summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commite2de64d6f1beb9e492daf5b886e19933c1fa41dd (patch)
tree9047cf9e6b5c43878d5bf82660adae77ceee097a
downloadtdemultimedia-e2de64d6f1beb9e492daf5b886e19933c1fa41dd.tar.gz
tdemultimedia-e2de64d6f1beb9e492daf5b886e19933c1fa41dd.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdemultimedia@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
-rw-r--r--AUTHORS3
-rw-r--r--COPYING346
-rw-r--r--COPYING-DOCS397
-rw-r--r--INSTALL176
-rw-r--r--Mainpage.dox10
-rw-r--r--Makefile.am.in15
-rw-r--r--Makefile.cvs16
-rw-r--r--README60
-rw-r--r--akode_artsplugin/Makefile.am46
-rw-r--r--akode_artsplugin/akodeFFMPEGPlayObject.mcopclass7
-rw-r--r--akode_artsplugin/akodeFFMPEGPlayObject_impl.cpp31
-rw-r--r--akode_artsplugin/akodeFFMPEGPlayObject_impl.h34
-rw-r--r--akode_artsplugin/akodeMPCPlayObject.mcopclass7
-rw-r--r--akode_artsplugin/akodeMPCPlayObject_impl.cpp31
-rw-r--r--akode_artsplugin/akodeMPCPlayObject_impl.h34
-rw-r--r--akode_artsplugin/akodeMPEGPlayObject.mcopclass7
-rw-r--r--akode_artsplugin/akodeMPEGPlayObject_impl.cpp31
-rw-r--r--akode_artsplugin/akodeMPEGPlayObject_impl.h34
-rw-r--r--akode_artsplugin/akodePlayObject.mcopclass7
-rw-r--r--akode_artsplugin/akodePlayObject_impl.cpp487
-rw-r--r--akode_artsplugin/akodePlayObject_impl.h98
-rw-r--r--akode_artsplugin/akodeSpeexStreamPlayObject.mcopclass7
-rw-r--r--akode_artsplugin/akodeSpeexStreamPlayObject_impl.cpp49
-rw-r--r--akode_artsplugin/akodeSpeexStreamPlayObject_impl.h39
-rw-r--r--akode_artsplugin/akodeVorbisStreamPlayObject.mcopclass7
-rw-r--r--akode_artsplugin/akodeVorbisStreamPlayObject_impl.cpp49
-rw-r--r--akode_artsplugin/akodeVorbisStreamPlayObject_impl.h39
-rw-r--r--akode_artsplugin/akodeXiphPlayObject.mcopclass7
-rw-r--r--akode_artsplugin/akodeXiphPlayObject_impl.cpp31
-rw-r--r--akode_artsplugin/akodeXiphPlayObject_impl.h34
-rw-r--r--akode_artsplugin/akodearts.idl30
-rw-r--r--akode_artsplugin/arts_inputstream.h152
-rw-r--r--akode_artsplugin/configure.in.in16
-rw-r--r--arts/Makefile.am9
-rw-r--r--arts/builder/FullDuplexTest.arts139
-rw-r--r--arts/builder/Makefile.am39
-rw-r--r--arts/builder/TODO69
-rw-r--r--arts/builder/artsbuilder.desktop122
-rw-r--r--arts/builder/artsbuilderui.rc47
-rw-r--r--arts/builder/autorouter.cpp609
-rw-r--r--arts/builder/autorouter.h184
-rw-r--r--arts/builder/createtool.cpp339
-rw-r--r--arts/builder/createtool.h132
-rw-r--r--arts/builder/dirmanager.cpp96
-rw-r--r--arts/builder/dirmanager.h34
-rw-r--r--arts/builder/drawutils.cpp39
-rw-r--r--arts/builder/drawutils.h31
-rw-r--r--arts/builder/execdlg.cpp201
-rw-r--r--arts/builder/execdlg.h54
-rw-r--r--arts/builder/interfacedlg.cpp178
-rw-r--r--arts/builder/interfacedlg.h52
-rw-r--r--arts/builder/main.cpp958
-rw-r--r--arts/builder/main.h118
-rw-r--r--arts/builder/menumaker.cpp212
-rw-r--r--arts/builder/menumaker.h70
-rw-r--r--arts/builder/module.cpp439
-rw-r--r--arts/builder/module.h108
-rw-r--r--arts/builder/mwidget.cpp652
-rw-r--r--arts/builder/mwidget.h129
-rw-r--r--arts/builder/pics/Makefile.am24
-rw-r--r--arts/builder/pics/Synth_ADD.xpm305
-rw-r--r--arts/builder/pics/Synth_AMAN_PLAY.xpm316
-rw-r--r--arts/builder/pics/Synth_ATAN_SATURATE.xpm310
-rw-r--r--arts/builder/pics/Synth_AUTOPANNER.xpm292
-rw-r--r--arts/builder/pics/Synth_BUS_DOWNLINK.xpm323
-rw-r--r--arts/builder/pics/Synth_BUS_UPLINK.xpm323
-rw-r--r--arts/builder/pics/Synth_DEBUG.xpm319
-rw-r--r--arts/builder/pics/Synth_DIV.xpm1350
-rw-r--r--arts/builder/pics/Synth_EMPTY.xpm1119
-rw-r--r--arts/builder/pics/Synth_ENVELOPE_ADSR.xpm318
-rw-r--r--arts/builder/pics/Synth_FILEPLAY.xpm312
-rw-r--r--arts/builder/pics/Synth_MIDI_DEBUG.xpm323
-rw-r--r--arts/builder/pics/Synth_MIDI_ROUTER.xpm323
-rw-r--r--arts/builder/pics/Synth_MOOG_VCF.xpm305
-rw-r--r--arts/builder/pics/Synth_MUL.xpm309
-rw-r--r--arts/builder/pics/Synth_MULTI_ADD.xpm1510
-rw-r--r--arts/builder/pics/Synth_PLAY.xpm316
-rw-r--r--arts/builder/pics/Synth_PLAY_AKAI.xpm317
-rw-r--r--arts/builder/pics/Synth_PLAY_AKAIS.xpm317
-rw-r--r--arts/builder/pics/Synth_PLAY_WAV.xpm317
-rw-r--r--arts/builder/pics/Synth_PSCALE.xpm299
-rw-r--r--arts/builder/pics/Synth_RC.xpm323
-rw-r--r--arts/builder/pics/Synth_SEQUENCE.xpm319
-rw-r--r--arts/builder/pics/Synth_SEQUENCE_FREQ.pngbin0 -> 3545 bytes
-rw-r--r--arts/builder/pics/Synth_SHELVE_CUTOFF.xpm247
-rw-r--r--arts/builder/pics/Synth_WAVE_SAW.xpm323
-rw-r--r--arts/builder/pics/Synth_WAVE_SIN.xpm320
-rw-r--r--arts/builder/pics/Synth_WAVE_SQUARE.xpm323
-rw-r--r--arts/builder/pics/Synth_WAVE_TRI.xpm305
-rw-r--r--arts/builder/pics/Synth_XFADE.xpm313
-rw-r--r--arts/builder/pics/cr16-action-artsbuilderexecute.pngbin0 -> 133 bytes
-rw-r--r--arts/builder/pics/cr22-action-artsbuilderexecute.pngbin0 -> 184 bytes
-rw-r--r--arts/builder/pics/hi16-app-artsbuilder.pngbin0 -> 898 bytes
-rw-r--r--arts/builder/pics/hi16-app-artscontrol.pngbin0 -> 859 bytes
-rw-r--r--arts/builder/pics/hisc-app-artsbuilder.svgzbin0 -> 1971 bytes
-rw-r--r--arts/builder/pics/hisc-app-artscontrol.svgzbin0 -> 4421 bytes
-rw-r--r--arts/builder/portposdlg.cpp258
-rw-r--r--arts/builder/portposdlg.h51
-rw-r--r--arts/builder/propertypanel.cpp404
-rw-r--r--arts/builder/propertypanel.h78
-rw-r--r--arts/builder/propertypanelbase.ui362
-rw-r--r--arts/builder/qttableview.cpp2274
-rw-r--r--arts/builder/qttableview.h251
-rw-r--r--arts/builder/retrievedlg.cpp137
-rw-r--r--arts/builder/retrievedlg.h45
-rw-r--r--arts/builder/scomponent.cpp108
-rw-r--r--arts/builder/scomponent.h87
-rw-r--r--arts/builder/session.cpp80
-rw-r--r--arts/builder/session.h38
-rw-r--r--arts/builder/structure.cpp462
-rw-r--r--arts/builder/structure.h92
-rw-r--r--arts/builder/structureport.cpp287
-rw-r--r--arts/builder/structureport.h66
-rw-r--r--arts/builder/x-artsbuilder.desktop60
-rw-r--r--arts/configure.in.in60
-rw-r--r--arts/examples/Makefile.am73
-rw-r--r--arts/examples/README256
-rw-r--r--arts/examples/TODO2
-rwxr-xr-xarts/examples/checknames.sh9
-rw-r--r--arts/examples/effect_delay.arts310
-rw-r--r--arts/examples/effect_delay_alone.arts310
-rw-r--r--arts/examples/effect_flanger_alone.arts285
-rw-r--r--arts/examples/example_adsr.arts137
-rw-r--r--arts/examples/example_atan_saturate.arts81
-rw-r--r--arts/examples/example_autopanner.arts117
-rw-r--r--arts/examples/example_brickwall.arts98
-rw-r--r--arts/examples/example_bus.arts44
-rw-r--r--arts/examples/example_capture_wav.arts50
-rw-r--r--arts/examples/example_cdelay.arts64
-rw-r--r--arts/examples/example_cflanger.arts127
-rw-r--r--arts/examples/example_data.arts39
-rw-r--r--arts/examples/example_delay.arts65
-rw-r--r--arts/examples/example_dtmf1.arts155
-rw-r--r--arts/examples/example_equalizer.arts84
-rw-r--r--arts/examples/example_fm.arts102
-rw-r--r--arts/examples/example_freeverb.arts99
-rw-r--r--arts/examples/example_moog.arts96
-rw-r--r--arts/examples/example_multi_add.arts83
-rw-r--r--arts/examples/example_noise.arts39
-rw-r--r--arts/examples/example_pitchshift.arts69
-rw-r--r--arts/examples/example_play_wave.arts57
-rw-r--r--arts/examples/example_pscale.arts112
-rw-r--r--arts/examples/example_pulse.arts66
-rw-r--r--arts/examples/example_rc.arts63
-rw-r--r--arts/examples/example_record.arts75
-rw-r--r--arts/examples/example_sequence.arts85
-rw-r--r--arts/examples/example_shelve_cutoff.arts61
-rw-r--r--arts/examples/example_sine.arts58
-rw-r--r--arts/examples/example_softsaw.arts61
-rw-r--r--arts/examples/example_square.arts61
-rw-r--r--arts/examples/example_stereobeep.arts91
-rw-r--r--arts/examples/example_tremolo.arts118
-rw-r--r--arts/examples/example_tri.arts61
-rw-r--r--arts/examples/example_xfade.arts120
-rw-r--r--arts/examples/instrument_arts_all.arts-map14
-rw-r--r--arts/examples/instrument_chirpdrum.arts260
-rw-r--r--arts/examples/instrument_deepdrum.arts255
-rw-r--r--arts/examples/instrument_flexible_slide.arts537
-rw-r--r--arts/examples/instrument_flexible_slide_GUI.arts481
-rw-r--r--arts/examples/instrument_fm_horn.arts301
-rw-r--r--arts/examples/instrument_full_square.arts422
-rw-r--r--arts/examples/instrument_hihat.arts235
-rw-r--r--arts/examples/instrument_moog_vcf_tune.arts341
-rw-r--r--arts/examples/instrument_moog_vcf_tune_GUI.arts331
-rw-r--r--arts/examples/instrument_neworgan.arts532
-rw-r--r--arts/examples/instrument_nokind.arts503
-rw-r--r--arts/examples/instrument_organ2.arts532
-rw-r--r--arts/examples/instrument_simple_sin.arts190
-rw-r--r--arts/examples/instrument_simple_square.arts190
-rw-r--r--arts/examples/instrument_simple_tri.arts304
-rw-r--r--arts/examples/instrument_slide.arts464
-rw-r--r--arts/examples/instrument_slide1.arts467
-rw-r--r--arts/examples/instrument_square.arts465
-rw-r--r--arts/examples/instrument_tri.arts348
-rw-r--r--arts/examples/mixer_element_eq.arts567
-rw-r--r--arts/examples/mixer_element_eqfx.arts740
-rw-r--r--arts/examples/mixer_element_simple.arts235
-rw-r--r--arts/examples/template_Empty_Structure.arts1
-rw-r--r--arts/examples/template_Instrument.arts103
-rw-r--r--arts/examples/template_Instrument_GUI.arts91
-rw-r--r--arts/examples/template_Mixer_Element.arts176
-rw-r--r--arts/gui/Makefile.am7
-rw-r--r--arts/gui/common/GenericGuiFactory.mcopclass3
-rw-r--r--arts/gui/common/Makefile.am35
-rw-r--r--arts/gui/common/artsgui.idl396
-rw-r--r--arts/gui/common/genericguifactory_impl.cc56
-rw-r--r--arts/gui/kde/Makefile.am38
-rw-r--r--arts/gui/kde/artstestgui.cpp124
-rw-r--r--arts/gui/kde/dbtest.cpp42
-rw-r--r--arts/gui/kde/dbtest.h16
-rw-r--r--arts/gui/kde/dbvolcalc.h82
-rw-r--r--arts/gui/kde/kartswidget.cpp89
-rw-r--r--arts/gui/kde/kartswidget.h92
-rw-r--r--arts/gui/kde/kbutton_impl.cpp135
-rw-r--r--arts/gui/kde/kbutton_impl.h76
-rw-r--r--arts/gui/kde/kcombobox_impl.cpp105
-rw-r--r--arts/gui/kde/kcombobox_impl.h74
-rw-r--r--arts/gui/kde/kfader.cpp31
-rw-r--r--arts/gui/kde/kfader.h46
-rw-r--r--arts/gui/kde/kfader_impl.cpp206
-rw-r--r--arts/gui/kde/kfader_impl.h92
-rw-r--r--arts/gui/kde/kframe_impl.cpp98
-rw-r--r--arts/gui/kde/kframe_impl.h55
-rw-r--r--arts/gui/kde/kgraph.cpp266
-rw-r--r--arts/gui/kde/kgraph.h64
-rw-r--r--arts/gui/kde/kgraph_impl.cpp92
-rw-r--r--arts/gui/kde/kgraph_impl.h65
-rw-r--r--arts/gui/kde/kgraphline_impl.cpp124
-rw-r--r--arts/gui/kde/kgraphline_impl.h61
-rw-r--r--arts/gui/kde/khbox_impl.cpp51
-rw-r--r--arts/gui/kde/khbox_impl.h48
-rw-r--r--arts/gui/kde/klabel_impl.cpp104
-rw-r--r--arts/gui/kde/klabel_impl.h90
-rw-r--r--arts/gui/kde/klayoutbox_impl.cpp122
-rw-r--r--arts/gui/kde/klayoutbox_impl.h103
-rw-r--r--arts/gui/kde/klevelmeter_firebars.cpp125
-rw-r--r--arts/gui/kde/klevelmeter_firebars.h58
-rw-r--r--arts/gui/kde/klevelmeter_impl.cpp131
-rw-r--r--arts/gui/kde/klevelmeter_impl.h70
-rw-r--r--arts/gui/kde/klevelmeter_linebars.cpp109
-rw-r--r--arts/gui/kde/klevelmeter_linebars.h46
-rw-r--r--arts/gui/kde/klevelmeter_normalbars.cpp81
-rw-r--r--arts/gui/kde/klevelmeter_normalbars.h75
-rw-r--r--arts/gui/kde/klevelmeter_private.h55
-rw-r--r--arts/gui/kde/klevelmeter_small.cpp59
-rw-r--r--arts/gui/kde/klevelmeter_small.h39
-rw-r--r--arts/gui/kde/klevelmeter_template.h70
-rw-r--r--arts/gui/kde/klineedit_impl.cpp75
-rw-r--r--arts/gui/kde/klineedit_impl.h63
-rw-r--r--arts/gui/kde/kpopupbox_impl.cpp134
-rw-r--r--arts/gui/kde/kpopupbox_impl.h70
-rw-r--r--arts/gui/kde/kpopupbox_private.h216
-rw-r--r--arts/gui/kde/kpoti.cpp780
-rw-r--r--arts/gui/kde/kpoti.h136
-rw-r--r--arts/gui/kde/kpoti_impl.cpp203
-rw-r--r--arts/gui/kde/kpoti_impl.h90
-rw-r--r--arts/gui/kde/kspinbox_impl.cpp108
-rw-r--r--arts/gui/kde/kspinbox_impl.h76
-rw-r--r--arts/gui/kde/ktickmarks_impl.cpp179
-rw-r--r--arts/gui/kde/ktickmarks_impl.h76
-rw-r--r--arts/gui/kde/kvbox_impl.cpp51
-rw-r--r--arts/gui/kde/kvbox_impl.h48
-rw-r--r--arts/gui/kde/kvolumefader_impl.cpp243
-rw-r--r--arts/gui/kde/kvolumefader_impl.h97
-rw-r--r--arts/gui/kde/kwidget_impl.cpp181
-rw-r--r--arts/gui/kde/kwidget_impl.h88
-rw-r--r--arts/gui/kde/kwidgetrepo.cpp89
-rw-r--r--arts/gui/kde/kwidgetrepo.h56
-rw-r--r--arts/gui/kde/mcopclass/Button.mcopclass5
-rw-r--r--arts/gui/kde/mcopclass/Fader.mcopclass5
-rw-r--r--arts/gui/kde/mcopclass/Graph.mcopclass5
-rw-r--r--arts/gui/kde/mcopclass/GraphLine.mcopclass5
-rw-r--r--arts/gui/kde/mcopclass/HBox.mcopclass5
-rw-r--r--arts/gui/kde/mcopclass/Label.mcopclass5
-rw-r--r--arts/gui/kde/mcopclass/LayoutBox.mcopclass5
-rw-r--r--arts/gui/kde/mcopclass/LevelMeter.mcopclass5
-rw-r--r--arts/gui/kde/mcopclass/LineEdit.mcopclass5
-rw-r--r--arts/gui/kde/mcopclass/Makefile.am5
-rw-r--r--arts/gui/kde/mcopclass/PopupBox.mcopclass5
-rw-r--r--arts/gui/kde/mcopclass/Poti.mcopclass5
-rw-r--r--arts/gui/kde/mcopclass/SpinBox.mcopclass5
-rw-r--r--arts/gui/kde/mcopclass/VBox.mcopclass5
-rw-r--r--arts/gui/kde/mcopclass/Widget.mcopclass5
-rw-r--r--arts/midi/Makefile.am53
-rw-r--r--arts/midi/README.midi261
-rw-r--r--arts/midi/alsamidigateway_impl.cc243
-rw-r--r--arts/midi/alsamidiport_impl.cc232
-rw-r--r--arts/midi/alsamidiport_impl.h85
-rw-r--r--arts/midi/artsmidi.idl379
-rw-r--r--arts/midi/audiomiditimer_impl.cc116
-rw-r--r--arts/midi/audiosync_impl.cc203
-rw-r--r--arts/midi/audiosync_impl.h79
-rw-r--r--arts/midi/audiotimer.cc98
-rw-r--r--arts/midi/audiotimer.h73
-rw-r--r--arts/midi/mcopclass/Makefile.am2
-rw-r--r--arts/midi/mcopclass/MidiManager.mcopclass3
-rw-r--r--arts/midi/midiclient_impl.cc274
-rw-r--r--arts/midi/midiclient_impl.h80
-rw-r--r--arts/midi/midimanager_impl.cc156
-rw-r--r--arts/midi/midimanager_impl.h67
-rw-r--r--arts/midi/midimanagerport_impl.cc71
-rw-r--r--arts/midi/midimanagerport_impl.h46
-rw-r--r--arts/midi/midimsg.c177
-rw-r--r--arts/midi/midimsg.h44
-rw-r--r--arts/midi/midisend.cc375
-rw-r--r--arts/midi/midisend.h106
-rw-r--r--arts/midi/midisyncgroup_impl.cc125
-rw-r--r--arts/midi/midisyncgroup_impl.h67
-rw-r--r--arts/midi/midisynctest.cc137
-rw-r--r--arts/midi/miditest_impl.cc57
-rw-r--r--arts/midi/miditimercommon.cc72
-rw-r--r--arts/midi/miditimercommon.h59
-rw-r--r--arts/midi/rawmidiport_impl.cc306
-rw-r--r--arts/midi/systemmiditimer_impl.cc105
-rw-r--r--arts/midi/timestampmath.cc112
-rw-r--r--arts/midi/timestampmath.h62
-rw-r--r--arts/modules/Makefile.am60
-rw-r--r--arts/modules/README.environments304
-rw-r--r--arts/modules/README.modules28
-rw-r--r--arts/modules/README.subdirs18
-rw-r--r--arts/modules/TODO58
-rw-r--r--arts/modules/artsmodules.idl186
-rw-r--r--arts/modules/common/Makefile.am61
-rw-r--r--arts/modules/common/artsmodulescommon.idl167
-rw-r--r--arts/modules/common/effectrackslot_impl.cc116
-rw-r--r--arts/modules/common/env_container_impl.cc136
-rw-r--r--arts/modules/common/env_context_impl.cc72
-rw-r--r--arts/modules/common/env_effectrackitem_impl.cc400
-rw-r--r--arts/modules/common/env_instrumentitem_impl.cc113
-rw-r--r--arts/modules/common/env_item_impl.cc48
-rw-r--r--arts/modules/common/env_item_impl.h28
-rw-r--r--arts/modules/common/env_mixeritem_impl.cc368
-rw-r--r--arts/modules/common/mcopclass/Container.mcopclass3
-rw-r--r--arts/modules/common/mcopclass/EffectRackGuiFactory.mcopclass4
-rw-r--r--arts/modules/common/mcopclass/EffectRackItem.mcopclass3
-rw-r--r--arts/modules/common/mcopclass/InstrumentItem.mcopclass3
-rw-r--r--arts/modules/common/mcopclass/InstrumentItemGuiFactory.mcopclass4
-rw-r--r--arts/modules/common/mcopclass/MixerGuiFactory.mcopclass4
-rw-r--r--arts/modules/common/mcopclass/MixerItem.mcopclass3
-rw-r--r--arts/modules/effects/Makefile.am70
-rw-r--r--arts/modules/effects/artsmoduleseffects.idl147
-rw-r--r--arts/modules/effects/effect_wavecapture_impl.cc63
-rw-r--r--arts/modules/effects/fivebandmonocomplexeq_impl.cc208
-rw-r--r--arts/modules/effects/freeverb/Makefile.am5
-rw-r--r--arts/modules/effects/freeverb/allpass.cpp36
-rw-r--r--arts/modules/effects/freeverb/allpass.hpp48
-rw-r--r--arts/modules/effects/freeverb/comb.cpp48
-rw-r--r--arts/modules/effects/freeverb/comb.hpp55
-rw-r--r--arts/modules/effects/freeverb/denormals.h15
-rw-r--r--arts/modules/effects/freeverb/readme.txt67
-rw-r--r--arts/modules/effects/freeverb/revmodel.cpp257
-rw-r--r--arts/modules/effects/freeverb/revmodel.hpp87
-rw-r--r--arts/modules/effects/freeverb/tuning.h60
-rw-r--r--arts/modules/effects/freeverbguifactory_impl.cc107
-rw-r--r--arts/modules/effects/kstereovolumecontrolgui_impl.cpp132
-rw-r--r--arts/modules/effects/kstereovolumecontrolgui_impl.h97
-rw-r--r--arts/modules/effects/mcopclass/Effect_WAVECAPTURE.mcopclass4
-rw-r--r--arts/modules/effects/mcopclass/FiveBandMonoComplexEQ.mcopclass6
-rw-r--r--arts/modules/effects/mcopclass/FiveBandMonoComplexEQGuiFactory.mcopclass4
-rw-r--r--arts/modules/effects/mcopclass/FreeverbGuiFactory.mcopclass4
-rw-r--r--arts/modules/effects/mcopclass/MonoToStereo.mcopclass4
-rw-r--r--arts/modules/effects/mcopclass/StereoBalance.mcopclass4
-rw-r--r--arts/modules/effects/mcopclass/StereoBalanceGuiFactory.mcopclass4
-rw-r--r--arts/modules/effects/mcopclass/StereoCompressorGuiFactory.mcopclass4
-rw-r--r--arts/modules/effects/mcopclass/StereoFirEqualizerGuiFactory.mcopclass4
-rw-r--r--arts/modules/effects/mcopclass/StereoToMono.mcopclass4
-rw-r--r--arts/modules/effects/mcopclass/StereoVolumeControlGui.mcopclass5
-rw-r--r--arts/modules/effects/mcopclass/StereoVolumeControlGuiFactory.mcopclass6
-rw-r--r--arts/modules/effects/mcopclass/Synth_FREEVERB.mcopclass7
-rw-r--r--arts/modules/effects/mcopclass/Synth_STEREO_COMPRESSOR.mcopclass6
-rw-r--r--arts/modules/effects/mcopclass/Synth_STEREO_FIR_EQUALIZER.mcopclass4
-rw-r--r--arts/modules/effects/mcopclass/Synth_STEREO_PITCH_SHIFT.mcopclass4
-rw-r--r--arts/modules/effects/mcopclass/Synth_STEREO_PITCH_SHIFT_FFT.mcopclass4
-rw-r--r--arts/modules/effects/mcopclass/Synth_VOICE_REMOVAL.mcopclass7
-rw-r--r--arts/modules/effects/mcopclass/VoiceRemovalGuiFactory.mcopclass4
-rw-r--r--arts/modules/effects/monostereoconversion_impl.cc160
-rw-r--r--arts/modules/effects/stereocompressorguifactory_impl.cc114
-rw-r--r--arts/modules/effects/stereovolumecontrolguifactory_impl.cpp45
-rw-r--r--arts/modules/effects/synth_freeverb_impl.cc84
-rw-r--r--arts/modules/effects/synth_stereo_compressor_impl.cc135
-rw-r--r--arts/modules/effects/synth_stereo_fir_equalizer_impl.cc221
-rw-r--r--arts/modules/effects/synth_stereo_pitch_shift_fft_impl.cc63
-rw-r--r--arts/modules/effects/synth_stereo_pitch_shift_impl.cc51
-rw-r--r--arts/modules/effects/synth_voice_removal_impl.cc108
-rw-r--r--arts/modules/effects/voiceremovalguifactory_impl.cc76
-rw-r--r--arts/modules/mixers/Makefile.am60
-rw-r--r--arts/modules/mixers/artsmodulesmixers.idl81
-rw-r--r--arts/modules/mixers/littlestereomixerchannel_impl.cc134
-rw-r--r--arts/modules/mixers/mcopclass/LittleStereoMixerChannel.mcopclass4
-rw-r--r--arts/modules/mixers/mcopclass/LittleStereoMixerChannelGuiFactory.mcopclass4
-rw-r--r--arts/modules/mixers/mcopclass/MonoSimpleMixerChannel.mcopclass3
-rw-r--r--arts/modules/mixers/mcopclass/MonoSimpleMixerChannelGuiFactory.mcopclass4
-rw-r--r--arts/modules/mixers/mcopclass/SimpleMixerChannel.mcopclass3
-rw-r--r--arts/modules/mixers/mcopclass/SimpleMixerChannelGuiFactory.mcopclass4
-rw-r--r--arts/modules/mixers/monosimplemixerchannel_impl.cc106
-rw-r--r--arts/modules/mixers/monosimplemixerchannelguifactory_impl.cc96
-rw-r--r--arts/modules/mixers/simplemixerchannel_impl.cc132
-rw-r--r--arts/modules/mixers/simplemixerchannelguifactory_impl.cc86
-rw-r--r--arts/modules/synth/Makefile.am64
-rw-r--r--arts/modules/synth/artsmodulessynth.idl301
-rw-r--r--arts/modules/synth/c_filter_stuff.c984
-rw-r--r--arts/modules/synth/c_filter_stuff.h246
-rw-r--r--arts/modules/synth/mcopclass/Synth_ATAN_SATURATE.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_AUTOPANNER.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_BRICKWALL_LIMITER.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_CAPTURE_WAV.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_CDELAY.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_COMPRESSOR.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_DATA.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_DEBUG.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_DELAY.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_DIV.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_ENVELOPE_ADSR.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_FM_SOURCE.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_FX_CFLANGER.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_MIDI_DEBUG.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_MIDI_TEST.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_MOOG_VCF.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_NIL.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_NOISE.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_OSC.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_PITCH_SHIFT.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_PITCH_SHIFT_FFT.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_PLAY_PAT.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_PSCALE.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_RC.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_SEQUENCE.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_SEQUENCE_FREQ.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_SHELVE_CUTOFF.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_STD_EQUALIZER.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_TREMOLO.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_WAVE_PULSE.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_WAVE_SOFTSAW.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_WAVE_SQUARE.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_WAVE_TRI.mcopclass4
-rw-r--r--arts/modules/synth/mcopclass/Synth_XFADE.mcopclass4
-rw-r--r--arts/modules/synth/objectcache_impl.cc73
-rw-r--r--arts/modules/synth/synth_atan_saturate_impl.cc54
-rw-r--r--arts/modules/synth/synth_autopanner_impl.cc50
-rw-r--r--arts/modules/synth/synth_brickwall_limiter_impl.cc51
-rw-r--r--arts/modules/synth/synth_capture_wav_impl.cc169
-rw-r--r--arts/modules/synth/synth_cdelay_impl.cc125
-rw-r--r--arts/modules/synth/synth_compressor_impl.cc139
-rw-r--r--arts/modules/synth/synth_data_impl.cc50
-rw-r--r--arts/modules/synth/synth_debug_impl.cc60
-rw-r--r--arts/modules/synth/synth_delay_impl.cc138
-rw-r--r--arts/modules/synth/synth_div_impl.cc45
-rw-r--r--arts/modules/synth/synth_envelope_adsr_impl.cc121
-rw-r--r--arts/modules/synth/synth_fm_source_impl.cc61
-rw-r--r--arts/modules/synth/synth_fx_cflanger_impl.cc96
-rw-r--r--arts/modules/synth/synth_midi_debug_impl.cc88
-rw-r--r--arts/modules/synth/synth_midi_test_impl.cc692
-rw-r--r--arts/modules/synth/synth_moog_vcf_impl.cc91
-rw-r--r--arts/modules/synth/synth_nil_impl.cc36
-rw-r--r--arts/modules/synth/synth_noise_impl.cc63
-rw-r--r--arts/modules/synth/synth_osc_impl.cc253
-rw-r--r--arts/modules/synth/synth_pitch_shift_fft_impl.cc431
-rw-r--r--arts/modules/synth/synth_pitch_shift_impl.cc196
-rw-r--r--arts/modules/synth/synth_play_pat_impl.cc529
-rw-r--r--arts/modules/synth/synth_pscale_impl.cc53
-rw-r--r--arts/modules/synth/synth_rc_impl.cc110
-rw-r--r--arts/modules/synth/synth_sequence_freq_impl.cc131
-rw-r--r--arts/modules/synth/synth_sequence_impl.cc132
-rw-r--r--arts/modules/synth/synth_shelve_cutoff_impl.cc71
-rw-r--r--arts/modules/synth/synth_std_equalizer_impl.cc207
-rw-r--r--arts/modules/synth/synth_tremolo_impl.cc51
-rw-r--r--arts/modules/synth/synth_wave_pulse_impl.cc52
-rw-r--r--arts/modules/synth/synth_wave_softsaw_impl.cc50
-rw-r--r--arts/modules/synth/synth_wave_square_impl.cc39
-rw-r--r--arts/modules/synth/synth_wave_tri_impl.cc39
-rw-r--r--arts/modules/synth/synth_xfade_impl.cc45
-rw-r--r--arts/runtime/ArtsBuilderLoader.mcopclass4
-rw-r--r--arts/runtime/LocalFactory.mcopclass3
-rw-r--r--arts/runtime/Makefile.am38
-rw-r--r--arts/runtime/StructureBuilder.mcopclass3
-rw-r--r--arts/runtime/StructureDesc.mcopclass3
-rw-r--r--arts/runtime/artsbuilder.idl228
-rw-r--r--arts/runtime/artsbuilderloader_impl.cc285
-rw-r--r--arts/runtime/compatibility.cc56
-rw-r--r--arts/runtime/compatibility.h36
-rw-r--r--arts/runtime/localfactory_impl.cc15
-rw-r--r--arts/runtime/moduleinfo.cc106
-rw-r--r--arts/runtime/moduleinfo.h33
-rw-r--r--arts/runtime/sequenceutils.cc188
-rw-r--r--arts/runtime/sequenceutils.h41
-rw-r--r--arts/runtime/structurebuilder_impl.cc347
-rw-r--r--arts/runtime/structures_impl.cc1421
-rw-r--r--arts/tools/Makefile.am82
-rw-r--r--arts/tools/artsactions.cpp191
-rw-r--r--arts/tools/artsactions.h117
-rw-r--r--arts/tools/artscontrol.desktop136
-rw-r--r--arts/tools/artscontrol.rc17
-rw-r--r--arts/tools/artscontrolapplet.cpp163
-rw-r--r--arts/tools/artscontrolapplet.desktop109
-rw-r--r--arts/tools/artscontrolapplet.h132
-rw-r--r--arts/tools/artscontrolapplet_private.h122
-rw-r--r--arts/tools/artsmidimanagerview.rc9
-rw-r--r--arts/tools/audiomanager.cpp198
-rw-r--r--arts/tools/audiomanager.h79
-rw-r--r--arts/tools/choosebusdlg.cpp184
-rw-r--r--arts/tools/choosebusdlg.h49
-rw-r--r--arts/tools/environmentview.cpp171
-rw-r--r--arts/tools/environmentview.h57
-rw-r--r--arts/tools/fftscopeview.cpp163
-rw-r--r--arts/tools/fftscopeview.h81
-rw-r--r--arts/tools/levelmeters.cpp239
-rw-r--r--arts/tools/levelmeters.h168
-rw-r--r--arts/tools/main.cpp211
-rw-r--r--arts/tools/main.h110
-rw-r--r--arts/tools/mediatypesview.cpp76
-rw-r--r--arts/tools/mediatypesview.h36
-rw-r--r--arts/tools/midiinstdlg.cpp179
-rw-r--r--arts/tools/midiinstdlg.h38
-rw-r--r--arts/tools/midimanagerdlg.ui151
-rw-r--r--arts/tools/midimanagerview.cpp260
-rw-r--r--arts/tools/midimanagerview.h61
-rw-r--r--arts/tools/midimanagerwidget.ui135
-rw-r--r--arts/tools/midiportdlg.cpp111
-rw-r--r--arts/tools/midiportdlg.h42
-rw-r--r--arts/tools/pics/Makefile.am2
-rw-r--r--arts/tools/pics/cr128-action-artsaudiomanager.pngbin0 -> 8472 bytes
-rw-r--r--arts/tools/pics/cr128-action-artsenvironment.pngbin0 -> 12033 bytes
-rw-r--r--arts/tools/pics/cr128-action-artsfftscope.pngbin0 -> 3077 bytes
-rw-r--r--arts/tools/pics/cr128-action-artsmediatypes.pngbin0 -> 6904 bytes
-rw-r--r--arts/tools/pics/cr128-action-artsmidimanager.pngbin0 -> 16951 bytes
-rw-r--r--arts/tools/pics/cr16-action-artsaudiomanager.pngbin0 -> 815 bytes
-rw-r--r--arts/tools/pics/cr16-action-artsenvironment.pngbin0 -> 1008 bytes
-rw-r--r--arts/tools/pics/cr16-action-artsfftscope.pngbin0 -> 790 bytes
-rw-r--r--arts/tools/pics/cr16-action-artsmediatypes.pngbin0 -> 795 bytes
-rw-r--r--arts/tools/pics/cr16-action-artsmidimanager.pngbin0 -> 854 bytes
-rw-r--r--arts/tools/pics/cr22-action-artsaudiomanager.pngbin0 -> 1145 bytes
-rw-r--r--arts/tools/pics/cr22-action-artsenvironment.pngbin0 -> 1465 bytes
-rw-r--r--arts/tools/pics/cr22-action-artsfftscope.pngbin0 -> 1193 bytes
-rw-r--r--arts/tools/pics/cr22-action-artsmediatypes.pngbin0 -> 1082 bytes
-rw-r--r--arts/tools/pics/cr22-action-artsmidimanager.pngbin0 -> 1061 bytes
-rw-r--r--arts/tools/pics/cr32-action-artsaudiomanager.pngbin0 -> 1742 bytes
-rw-r--r--arts/tools/pics/cr32-action-artsenvironment.pngbin0 -> 2415 bytes
-rw-r--r--arts/tools/pics/cr32-action-artsfftscope.pngbin0 -> 1494 bytes
-rw-r--r--arts/tools/pics/cr32-action-artsmediatypes.pngbin0 -> 1597 bytes
-rw-r--r--arts/tools/pics/cr32-action-artsmidimanager.pngbin0 -> 2688 bytes
-rw-r--r--arts/tools/pics/cr48-action-artsaudiomanager.pngbin0 -> 2918 bytes
-rw-r--r--arts/tools/pics/cr48-action-artsenvironment.pngbin0 -> 4131 bytes
-rw-r--r--arts/tools/pics/cr48-action-artsfftscope.pngbin0 -> 1578 bytes
-rw-r--r--arts/tools/pics/cr48-action-artsmediatypes.pngbin0 -> 2396 bytes
-rw-r--r--arts/tools/pics/cr48-action-artsmidimanager.pngbin0 -> 4681 bytes
-rw-r--r--arts/tools/pics/cr64-action-artsaudiomanager.pngbin0 -> 4165 bytes
-rw-r--r--arts/tools/pics/cr64-action-artsenvironment.pngbin0 -> 5671 bytes
-rw-r--r--arts/tools/pics/cr64-action-artsfftscope.pngbin0 -> 2003 bytes
-rw-r--r--arts/tools/pics/cr64-action-artsmediatypes.pngbin0 -> 3245 bytes
-rw-r--r--arts/tools/pics/cr64-action-artsmidimanager.pngbin0 -> 6910 bytes
-rw-r--r--arts/tools/pics/crsc-action-artsaudiomanager.svgzbin0 -> 2150 bytes
-rw-r--r--arts/tools/pics/crsc-action-artsenvironment.svgzbin0 -> 2019 bytes
-rw-r--r--arts/tools/pics/crsc-action-artsfftscope.svgzbin0 -> 1203 bytes
-rw-r--r--arts/tools/pics/crsc-action-artsmediatypes.svgzbin0 -> 3323 bytes
-rw-r--r--arts/tools/pics/crsc-action-artsmidimanager.svgzbin0 -> 1746 bytes
-rw-r--r--arts/tools/pics/hi128-app-artscontrol.pngbin0 -> 7551 bytes
-rw-r--r--arts/tools/pics/hi16-app-artscontrol.pngbin0 -> 845 bytes
-rw-r--r--arts/tools/pics/hi22-app-artscontrol.pngbin0 -> 1172 bytes
-rw-r--r--arts/tools/pics/hi32-app-artscontrol.pngbin0 -> 1745 bytes
-rw-r--r--arts/tools/pics/hi48-app-artscontrol.pngbin0 -> 2642 bytes
-rw-r--r--arts/tools/pics/hi64-app-artscontrol.pngbin0 -> 3540 bytes
-rw-r--r--arts/tools/pics/hisc-app-artscontrol.svgzbin0 -> 2410 bytes
-rw-r--r--arts/tools/statusview.cpp95
-rw-r--r--arts/tools/statusview.h52
-rw-r--r--arts/tools/templateview.cpp39
-rw-r--r--arts/tools/templateview.h39
-rw-r--r--audiofile_artsplugin/Makefile.am23
-rw-r--r--audiofile_artsplugin/README9
-rw-r--r--audiofile_artsplugin/audiofilePlayObject.mcopclass7
-rw-r--r--audiofile_artsplugin/audiofilePlayObjectI.cpp343
-rw-r--r--audiofile_artsplugin/audiofilePlayObjectI.h95
-rw-r--r--audiofile_artsplugin/audiofilearts.idl12
-rw-r--r--audiofile_artsplugin/configure.in.bot9
-rw-r--r--audiofile_artsplugin/configure.in.in52
-rw-r--r--configure.in.bot36
-rw-r--r--configure.in.in557
-rw-r--r--doc/Makefile.am5
-rw-r--r--doc/artsbuilder/Makefile.am5
-rw-r--r--doc/artsbuilder/apis.docbook357
-rw-r--r--doc/artsbuilder/arts-structure.pngbin0 -> 8955 bytes
-rw-r--r--doc/artsbuilder/artsbuilder.docbook864
-rw-r--r--doc/artsbuilder/detail.docbook1765
-rw-r--r--doc/artsbuilder/digitalaudio.docbook14
-rw-r--r--doc/artsbuilder/faq.docbook1112
-rw-r--r--doc/artsbuilder/future.docbook414
-rw-r--r--doc/artsbuilder/glossary.docbook164
-rw-r--r--doc/artsbuilder/gui.docbook28
-rw-r--r--doc/artsbuilder/helping.docbook246
-rw-r--r--doc/artsbuilder/images/Doc_MODUL.pngbin0 -> 2377 bytes
-rw-r--r--doc/artsbuilder/images/Gui_AUDIO_MANAGER.pngbin0 -> 313 bytes
-rw-r--r--doc/artsbuilder/images/Gui_INSTRUMENT_MAPPER.pngbin0 -> 306 bytes
-rw-r--r--doc/artsbuilder/images/Gui_LABEL.pngbin0 -> 1771 bytes
-rw-r--r--doc/artsbuilder/images/Gui_MIXER.pngbin0 -> 1729 bytes
-rw-r--r--doc/artsbuilder/images/Gui_PANEL.pngbin0 -> 1706 bytes
-rw-r--r--doc/artsbuilder/images/Gui_POTI.pngbin0 -> 1803 bytes
-rw-r--r--doc/artsbuilder/images/Gui_SLIDER.pngbin0 -> 1934 bytes
-rw-r--r--doc/artsbuilder/images/Gui_SUBPANEL.pngbin0 -> 1839 bytes
-rw-r--r--doc/artsbuilder/images/Gui_WINDOW.pngbin0 -> 1513 bytes
-rw-r--r--doc/artsbuilder/images/Interface_MIDI_NOTE.pngbin0 -> 1018 bytes
-rw-r--r--doc/artsbuilder/images/Makefile.am4
-rw-r--r--doc/artsbuilder/images/Synth_ADD.pngbin0 -> 1129 bytes
-rw-r--r--doc/artsbuilder/images/Synth_ATAN_SATURATE.pngbin0 -> 1266 bytes
-rw-r--r--doc/artsbuilder/images/Synth_BUS_DOWNLINK.pngbin0 -> 1577 bytes
-rw-r--r--doc/artsbuilder/images/Synth_BUS_UPLINK.pngbin0 -> 1503 bytes
-rw-r--r--doc/artsbuilder/images/Synth_CDELAY.pngbin0 -> 1011 bytes
-rw-r--r--doc/artsbuilder/images/Synth_COMPRESSOR.pngbin0 -> 680 bytes
-rw-r--r--doc/artsbuilder/images/Synth_DEBUG.pngbin0 -> 1271 bytes
-rw-r--r--doc/artsbuilder/images/Synth_DELAY.pngbin0 -> 538 bytes
-rw-r--r--doc/artsbuilder/images/Synth_DIV.pngbin0 -> 2851 bytes
-rw-r--r--doc/artsbuilder/images/Synth_ENVELOPE_ADSR.pngbin0 -> 1514 bytes
-rw-r--r--doc/artsbuilder/images/Synth_FILEPLAY.pngbin0 -> 1384 bytes
-rw-r--r--doc/artsbuilder/images/Synth_FM_SOURCE.pngbin0 -> 1033 bytes
-rw-r--r--doc/artsbuilder/images/Synth_FREQUENCY.pngbin0 -> 954 bytes
-rw-r--r--doc/artsbuilder/images/Synth_MIDI_DEBUG.pngbin0 -> 1349 bytes
-rw-r--r--doc/artsbuilder/images/Synth_MIDI_ROUTER.pngbin0 -> 1058 bytes
-rw-r--r--doc/artsbuilder/images/Synth_MUL.pngbin0 -> 1237 bytes
-rw-r--r--doc/artsbuilder/images/Synth_NIL.pngbin0 -> 812 bytes
-rw-r--r--doc/artsbuilder/images/Synth_PLAY.pngbin0 -> 1467 bytes
-rw-r--r--doc/artsbuilder/images/Synth_PLAY_AKAI.pngbin0 -> 459 bytes
-rw-r--r--doc/artsbuilder/images/Synth_PLAY_AKAIS.pngbin0 -> 577 bytes
-rw-r--r--doc/artsbuilder/images/Synth_PLAY_WAV.pngbin0 -> 678 bytes
-rw-r--r--doc/artsbuilder/images/Synth_PSCALE.pngbin0 -> 1479 bytes
-rw-r--r--doc/artsbuilder/images/Synth_RC.pngbin0 -> 1000 bytes
-rw-r--r--doc/artsbuilder/images/Synth_SEQUENCE.pngbin0 -> 1511 bytes
-rw-r--r--doc/artsbuilder/images/Synth_SEQUENCE_FREQ.pngbin0 -> 2194 bytes
-rw-r--r--doc/artsbuilder/images/Synth_SHELVE_CUTOFF.pngbin0 -> 1005 bytes
-rw-r--r--doc/artsbuilder/images/Synth_STD_EQUALIZER.pngbin0 -> 598 bytes
-rw-r--r--doc/artsbuilder/images/Synth_STRUCT_KILL.pngbin0 -> 874 bytes
-rw-r--r--doc/artsbuilder/images/Synth_WAVE_SIN.pngbin0 -> 1199 bytes
-rw-r--r--doc/artsbuilder/images/Synth_WAVE_SQUARE.pngbin0 -> 1446 bytes
-rw-r--r--doc/artsbuilder/images/Synth_WAVE_TRI.pngbin0 -> 1194 bytes
-rw-r--r--doc/artsbuilder/images/Synth_XFADE.pngbin0 -> 1423 bytes
-rw-r--r--doc/artsbuilder/images/schema1.pngbin0 -> 1129 bytes
-rw-r--r--doc/artsbuilder/images/schema2.pngbin0 -> 1373 bytes
-rw-r--r--doc/artsbuilder/images/schema3.pngbin0 -> 976 bytes
-rw-r--r--doc/artsbuilder/images/schema4.pngbin0 -> 881 bytes
-rw-r--r--doc/artsbuilder/index.docbook393
-rw-r--r--doc/artsbuilder/mcop.docbook2274
-rw-r--r--doc/artsbuilder/midi.docbook474
-rw-r--r--doc/artsbuilder/midiintro.docbook14
-rw-r--r--doc/artsbuilder/modules.docbook1336
-rw-r--r--doc/artsbuilder/porting.docbook64
-rw-r--r--doc/artsbuilder/references.docbook56
-rw-r--r--doc/artsbuilder/tools.docbook735
-rw-r--r--doc/juk/Makefile.am2
-rw-r--r--doc/juk/history-playlist.pngbin0 -> 2986 bytes
-rw-r--r--doc/juk/index.docbook1665
-rw-r--r--doc/juk/juk-adv-search.pngbin0 -> 20682 bytes
-rw-r--r--doc/juk/juk-file-renamer.pngbin0 -> 19560 bytes
-rw-r--r--doc/juk/juk-main.pngbin0 -> 62743 bytes
-rw-r--r--doc/juk/juk-tag-guesser.pngbin0 -> 26021 bytes
-rw-r--r--doc/juk/normal-playlist.pngbin0 -> 2112 bytes
-rw-r--r--doc/juk/search-playlist.pngbin0 -> 2637 bytes
-rw-r--r--doc/juk/toolbar.pngbin0 -> 10896 bytes
-rw-r--r--doc/kaboodle/Makefile.am4
-rw-r--r--doc/kaboodle/index.docbook78
-rw-r--r--doc/kaudiocreator/Makefile.am2
-rw-r--r--doc/kaudiocreator/cdconfiguration.pngbin0 -> 37323 bytes
-rw-r--r--doc/kaudiocreator/cddbconfigurationlookup.pngbin0 -> 50801 bytes
-rw-r--r--doc/kaudiocreator/cddbconfigurationsubmit.pngbin0 -> 55890 bytes
-rw-r--r--doc/kaudiocreator/cdinserted.pngbin0 -> 26622 bytes
-rw-r--r--doc/kaudiocreator/confirmartistcarryover.pngbin0 -> 26277 bytes
-rw-r--r--doc/kaudiocreator/encoderconfiguration.pngbin0 -> 69151 bytes
-rw-r--r--doc/kaudiocreator/encodernotfound.pngbin0 -> 37557 bytes
-rw-r--r--doc/kaudiocreator/entersong1.pngbin0 -> 28716 bytes
-rw-r--r--doc/kaudiocreator/generalconfiguration.pngbin0 -> 80803 bytes
-rw-r--r--doc/kaudiocreator/index.docbook1083
-rw-r--r--doc/kaudiocreator/jobcontrol.pngbin0 -> 39905 bytes
-rw-r--r--doc/kaudiocreator/jobshavestarted.pngbin0 -> 21841 bytes
-rw-r--r--doc/kaudiocreator/kaudiocreatormainwindow800.pngbin0 -> 26800 bytes
-rw-r--r--doc/kaudiocreator/lameconfiguration.pngbin0 -> 31241 bytes
-rw-r--r--doc/kaudiocreator/readytorip.pngbin0 -> 26096 bytes
-rw-r--r--doc/kaudiocreator/ripperconfiguration.pngbin0 -> 45479 bytes
-rw-r--r--doc/kaudiocreator/rippingandencoding.pngbin0 -> 43194 bytes
-rw-r--r--doc/kaudiocreator/rippingandencoding2.pngbin0 -> 43997 bytes
-rw-r--r--doc/kaudiocreator/setalbumcategory.pngbin0 -> 23626 bytes
-rw-r--r--doc/kaudiocreator/startalbumeditor.pngbin0 -> 23827 bytes
-rw-r--r--doc/kioslave/Makefile.am4
-rw-r--r--doc/kioslave/audiocd.docbook208
-rw-r--r--doc/kmid/Makefile.am4
-rw-r--r--doc/kmid/index.docbook1338
-rw-r--r--doc/kmix/Makefile.am4
-rw-r--r--doc/kmix/index.docbook503
-rw-r--r--doc/kmix/kmix-channel-playback.pngbin0 -> 2985 bytes
-rw-r--r--doc/kmix/kmix-channel-record.pngbin0 -> 2666 bytes
-rw-r--r--doc/kmix/kmix-channel-switches.pngbin0 -> 12103 bytes
-rw-r--r--doc/kmix/kmix-window.pngbin0 -> 24728 bytes
-rw-r--r--doc/krec/Makefile.am4
-rw-r--r--doc/krec/index.docbook639
-rw-r--r--doc/krec/krec-configuration.pngbin0 -> 43759 bytes
-rw-r--r--doc/krec/krec-hicolor.pngbin0 -> 13508 bytes
-rw-r--r--doc/krec/krec-keramik.pngbin0 -> 26028 bytes
-rw-r--r--doc/krec/krec-new_file_properties.pngbin0 -> 14401 bytes
-rw-r--r--doc/kscd/Makefile.am4
-rw-r--r--doc/kscd/index.docbook932
-rw-r--r--doc/kscd/kscd.pngbin0 -> 20705 bytes
-rw-r--r--doc/kscd/kscd11.pngbin0 -> 3129 bytes
-rw-r--r--doc/kscd/kscd12.pngbin0 -> 63131 bytes
-rw-r--r--doc/kscd/kscd13.pngbin0 -> 41329 bytes
-rw-r--r--doc/kscd/kscd14.pngbin0 -> 46009 bytes
-rw-r--r--doc/kscd/kscd16.pngbin0 -> 36771 bytes
-rw-r--r--doc/kscd/kscd18.pngbin0 -> 1828 bytes
-rw-r--r--doc/kscd/kscd19.pngbin0 -> 4562 bytes
-rw-r--r--doc/kscd/kscd2.pngbin0 -> 5694 bytes
-rw-r--r--doc/kscd/kscd3.pngbin0 -> 4030 bytes
-rw-r--r--doc/kscd/kscd5.pngbin0 -> 1841 bytes
-rw-r--r--doc/kscd/kscd6.pngbin0 -> 1408 bytes
-rw-r--r--doc/kscd/kscd9.pngbin0 -> 1928 bytes
-rw-r--r--doc/kscd/kscdannounc.pngbin0 -> 3963 bytes
-rw-r--r--doc/noatun/Makefile.am2
-rw-r--r--doc/noatun/index.docbook488
-rw-r--r--juk/HACKING147
-rw-r--r--juk/Makefile.am113
-rw-r--r--juk/TODO17
-rw-r--r--juk/actioncollection.cpp41
-rw-r--r--juk/actioncollection.h45
-rw-r--r--juk/advancedsearchdialog.cpp175
-rw-r--r--juk/advancedsearchdialog.h69
-rw-r--r--juk/akodeplayer.cpp170
-rw-r--r--juk/akodeplayer.h66
-rw-r--r--juk/artsplayer.cpp295
-rw-r--r--juk/artsplayer.h95
-rw-r--r--juk/cache.cpp323
-rw-r--r--juk/cache.h67
-rw-r--r--juk/categoryreaderinterface.cpp67
-rw-r--r--juk/categoryreaderinterface.h122
-rw-r--r--juk/collectionlist.cpp511
-rw-r--r--juk/collectionlist.h197
-rw-r--r--juk/configure.in.bot40
-rw-r--r--juk/configure.in.in102
-rw-r--r--juk/coverdialog.cpp168
-rw-r--r--juk/coverdialog.h41
-rw-r--r--juk/coverdialogbase.ui210
-rw-r--r--juk/covericonview.cpp48
-rw-r--r--juk/covericonview.h62
-rw-r--r--juk/coverinfo.cpp283
-rw-r--r--juk/coverinfo.h67
-rw-r--r--juk/covermanager.cpp577
-rw-r--r--juk/covermanager.h262
-rw-r--r--juk/cr22-action-juk_dock.pngbin0 -> 1268 bytes
-rw-r--r--juk/deletedialog.cpp121
-rw-r--r--juk/deletedialog.h64
-rw-r--r--juk/deletedialogbase.ui143
-rw-r--r--juk/directorylist.cpp101
-rw-r--r--juk/directorylist.h59
-rw-r--r--juk/directorylistbase.ui112
-rw-r--r--juk/dynamicplaylist.cpp194
-rw-r--r--juk/dynamicplaylist.h110
-rw-r--r--juk/exampleoptions.cpp83
-rw-r--r--juk/exampleoptions.h63
-rw-r--r--juk/exampleoptionsbase.ui285
-rw-r--r--juk/filehandle.cpp298
-rw-r--r--juk/filehandle.h82
-rw-r--r--juk/filehandleproperties.h94
-rw-r--r--juk/filerenamer.cpp1047
-rw-r--r--juk/filerenamer.h543
-rw-r--r--juk/filerenamerbase.ui379
-rw-r--r--juk/filerenamerconfigdlg.cpp43
-rw-r--r--juk/filerenamerconfigdlg.h38
-rw-r--r--juk/filerenameroptions.cpp157
-rw-r--r--juk/filerenameroptions.h79
-rw-r--r--juk/filerenameroptionsbase.ui425
-rw-r--r--juk/folderplaylist.cpp81
-rw-r--r--juk/folderplaylist.h44
-rw-r--r--juk/gstreamerplayer.cpp346
-rw-r--r--juk/gstreamerplayer.h81
-rw-r--r--juk/hi128-app-juk.pngbin0 -> 17935 bytes
-rw-r--r--juk/hi16-app-juk.pngbin0 -> 870 bytes
-rw-r--r--juk/hi32-app-juk.pngbin0 -> 2599 bytes
-rw-r--r--juk/hi48-app-juk.pngbin0 -> 4557 bytes
-rw-r--r--juk/hi64-app-juk.pngbin0 -> 6982 bytes
-rw-r--r--juk/historyplaylist.cpp160
-rw-r--r--juk/historyplaylist.h72
-rw-r--r--juk/juk.cpp478
-rw-r--r--juk/juk.desktop72
-rw-r--r--juk/juk.h100
-rw-r--r--juk/jukIface.h108
-rw-r--r--juk/jukservicemenu.desktop63
-rw-r--r--juk/jukui-rtl.rc109
-rw-r--r--juk/jukui.rc109
-rw-r--r--juk/k3bexporter.cpp298
-rw-r--r--juk/k3bexporter.h94
-rw-r--r--juk/keydialog.cpp204
-rw-r--r--juk/keydialog.h85
-rw-r--r--juk/ktrm.cpp606
-rw-r--r--juk/ktrm.h197
-rw-r--r--juk/main.cpp98
-rw-r--r--juk/mediafiles.cpp158
-rw-r--r--juk/mediafiles.h89
-rw-r--r--juk/musicbrainzquery.cpp120
-rw-r--r--juk/musicbrainzquery.h43
-rw-r--r--juk/nowplaying.cpp368
-rw-r--r--juk/nowplaying.h177
-rw-r--r--juk/pics/Makefile.am2
-rw-r--r--juk/pics/playing.pngbin0 -> 307 bytes
-rw-r--r--juk/pics/splash.pngbin0 -> 55736 bytes
-rw-r--r--juk/pics/yahoo_credit.pngbin0 -> 521 bytes
-rw-r--r--juk/player.h50
-rw-r--r--juk/playermanager.cpp689
-rw-r--r--juk/playermanager.h117
-rw-r--r--juk/playlist.cpp2361
-rw-r--r--juk/playlist.h789
-rw-r--r--juk/playlistbox.cpp790
-rw-r--r--juk/playlistbox.h189
-rw-r--r--juk/playlistcollection.cpp929
-rw-r--r--juk/playlistcollection.h270
-rw-r--r--juk/playlistexporter.h49
-rw-r--r--juk/playlistinterface.cpp80
-rw-r--r--juk/playlistinterface.h102
-rw-r--r--juk/playlistitem.cpp464
-rw-r--r--juk/playlistitem.h213
-rw-r--r--juk/playlistsearch.cpp328
-rw-r--r--juk/playlistsearch.h150
-rw-r--r--juk/playlistsplitter.cpp237
-rw-r--r--juk/playlistsplitter.h88
-rw-r--r--juk/searchplaylist.cpp115
-rw-r--r--juk/searchplaylist.h48
-rw-r--r--juk/searchwidget.cpp292
-rw-r--r--juk/searchwidget.h111
-rw-r--r--juk/slideraction.cpp357
-rw-r--r--juk/slideraction.h97
-rw-r--r--juk/sortedstringlist.cpp180
-rw-r--r--juk/sortedstringlist.h59
-rw-r--r--juk/splashscreen.cpp104
-rw-r--r--juk/splashscreen.h52
-rw-r--r--juk/statuslabel.cpp205
-rw-r--r--juk/statuslabel.h64
-rw-r--r--juk/stringhash.h312
-rw-r--r--juk/stringshare.cpp73
-rw-r--r--juk/stringshare.h37
-rw-r--r--juk/systemtray.cpp639
-rw-r--r--juk/systemtray.h133
-rw-r--r--juk/tag.cpp279
-rw-r--r--juk/tag.h95
-rw-r--r--juk/tageditor.cpp800
-rw-r--r--juk/tageditor.h118
-rw-r--r--juk/tagguesser.cpp218
-rw-r--r--juk/tagguesser.h74
-rw-r--r--juk/tagguesserconfigdlg.cpp122
-rw-r--r--juk/tagguesserconfigdlg.h39
-rw-r--r--juk/tagguesserconfigdlgwidget.ui159
-rw-r--r--juk/tagguessertest.cpp128
-rw-r--r--juk/tagrenameroptions.cpp158
-rw-r--r--juk/tagrenameroptions.h176
-rw-r--r--juk/tagtransactionmanager.cpp214
-rw-r--r--juk/tagtransactionmanager.h213
-rw-r--r--juk/trackpickerdialog.cpp101
-rw-r--r--juk/trackpickerdialog.h51
-rw-r--r--juk/trackpickerdialogbase.ui179
-rw-r--r--juk/tracksequenceiterator.cpp310
-rw-r--r--juk/tracksequenceiterator.h232
-rw-r--r--juk/tracksequencemanager.cpp183
-rw-r--r--juk/tracksequencemanager.h192
-rw-r--r--juk/treeviewitemplaylist.cpp93
-rw-r--r--juk/treeviewitemplaylist.h43
-rw-r--r--juk/upcomingplaylist.cpp277
-rw-r--r--juk/upcomingplaylist.h213
-rw-r--r--juk/viewmode.cpp425
-rw-r--r--juk/viewmode.h159
-rw-r--r--juk/webimagefetcher.cpp224
-rw-r--r--juk/webimagefetcher.h93
-rw-r--r--juk/webimagefetcherdialog.cpp235
-rw-r--r--juk/webimagefetcherdialog.h90
-rw-r--r--kaboodle/AUTHORS6
-rw-r--r--kaboodle/Makefile.am45
-rw-r--r--kaboodle/actions/Makefile.am2
-rw-r--r--kaboodle/actions/cr16-action-kaboodleloop.pngbin0 -> 641 bytes
-rw-r--r--kaboodle/actions/cr22-action-kaboodleloop.pngbin0 -> 266 bytes
-rw-r--r--kaboodle/conf.cpp59
-rw-r--r--kaboodle/conf.h52
-rw-r--r--kaboodle/configure.in.in4
-rw-r--r--kaboodle/controls.cpp138
-rw-r--r--kaboodle/controls.h97
-rw-r--r--kaboodle/engine.cpp207
-rw-r--r--kaboodle/engine.h100
-rw-r--r--kaboodle/kaboodle.desktop83
-rw-r--r--kaboodle/kaboodle_component.desktop72
-rw-r--r--kaboodle/kaboodle_factory.cpp83
-rw-r--r--kaboodle/kaboodle_factory.h55
-rw-r--r--kaboodle/kaboodleapp.cpp53
-rw-r--r--kaboodle/kaboodleapp.h45
-rw-r--r--kaboodle/kaboodleengine.desktop67
-rw-r--r--kaboodle/kaboodlepartui.rc26
-rw-r--r--kaboodle/kaboodleui.rc17
-rw-r--r--kaboodle/main.cpp59
-rw-r--r--kaboodle/pics/Makefile.am1
-rw-r--r--kaboodle/pics/hi128-app-kaboodle.pngbin0 -> 15814 bytes
-rw-r--r--kaboodle/pics/hi16-app-kaboodle.pngbin0 -> 897 bytes
-rw-r--r--kaboodle/pics/hi22-app-kaboodle.pngbin0 -> 1319 bytes
-rw-r--r--kaboodle/pics/hi32-app-kaboodle.pngbin0 -> 2070 bytes
-rw-r--r--kaboodle/pics/hi48-app-kaboodle.pngbin0 -> 4411 bytes
-rw-r--r--kaboodle/pics/hi64-app-kaboodle.pngbin0 -> 6498 bytes
-rw-r--r--kaboodle/player.cpp268
-rw-r--r--kaboodle/player.h116
-rw-r--r--kaboodle/userinterface.cpp182
-rw-r--r--kaboodle/userinterface.h72
-rw-r--r--kaboodle/view.cpp323
-rw-r--r--kaboodle/view.h104
-rw-r--r--kappfinder-data/Makefile.am29
-rw-r--r--kappfinder-data/ams.desktop55
-rw-r--r--kappfinder-data/amsynth.desktop46
-rw-r--r--kappfinder-data/ardour.desktop57
-rw-r--r--kappfinder-data/djplay.desktop62
-rw-r--r--kappfinder-data/ecamegapedal.desktop25
-rw-r--r--kappfinder-data/freebirth.desktop61
-rw-r--r--kappfinder-data/freqtweak.desktop60
-rw-r--r--kappfinder-data/galan.desktop57
-rw-r--r--kappfinder-data/hydrogen.desktop65
-rw-r--r--kappfinder-data/jack-rack.desktop39
-rw-r--r--kappfinder-data/jamin.desktop57
-rw-r--r--kappfinder-data/kde-multimedia-music.directory89
-rw-r--r--kappfinder-data/kde-multimedia-music.menu20
-rw-r--r--kappfinder-data/meterbridge.desktop64
-rw-r--r--kappfinder-data/mixxx.desktop59
-rw-r--r--kappfinder-data/muse.desktop60
-rw-r--r--kappfinder-data/qjackctl.desktop60
-rw-r--r--kappfinder-data/qsynth.desktop62
-rw-r--r--kappfinder-data/rezound.desktop68
-rw-r--r--kappfinder-data/vkeybd.desktop62
-rw-r--r--kappfinder-data/zynaddsubfx.desktop53
-rw-r--r--kaudiocreator/Makefile.am44
-rw-r--r--kaudiocreator/TODO19
-rw-r--r--kaudiocreator/audiocd_extract.desktop54
-rw-r--r--kaudiocreator/cdconfig.ui61
-rw-r--r--kaudiocreator/configure.in.in0
-rw-r--r--kaudiocreator/encodefile.ui345
-rw-r--r--kaudiocreator/encodefileimp.cpp82
-rw-r--r--kaudiocreator/encodefileimp.h57
-rw-r--r--kaudiocreator/encoder.cpp331
-rw-r--r--kaudiocreator/encoder.h68
-rw-r--r--kaudiocreator/encoder_prefs.kcfgc7
-rw-r--r--kaudiocreator/encoder_prefs_addons.h8
-rw-r--r--kaudiocreator/encoderconfig.ui293
-rw-r--r--kaudiocreator/encoderconfig.ui.h25
-rw-r--r--kaudiocreator/encoderconfigimp.cpp289
-rw-r--r--kaudiocreator/encoderconfigimp.h57
-rw-r--r--kaudiocreator/encoderedit.ui98
-rw-r--r--kaudiocreator/encoderoutput.ui103
-rw-r--r--kaudiocreator/eventsrc528
-rw-r--r--kaudiocreator/general.ui285
-rw-r--r--kaudiocreator/general.ui.h19
-rw-r--r--kaudiocreator/hi16-app-kaudiocreator.pngbin0 -> 971 bytes
-rw-r--r--kaudiocreator/hi32-app-kaudiocreator.pngbin0 -> 2596 bytes
-rw-r--r--kaudiocreator/icons/Makefile.am4
-rw-r--r--kaudiocreator/icons/check.pngbin0 -> 130 bytes
-rw-r--r--kaudiocreator/infodialog.ui297
-rw-r--r--kaudiocreator/job.cpp58
-rw-r--r--kaudiocreator/job.h78
-rw-r--r--kaudiocreator/jobque.ui132
-rw-r--r--kaudiocreator/jobqueimp.cpp310
-rw-r--r--kaudiocreator/jobqueimp.h80
-rw-r--r--kaudiocreator/kaudiocreator-libkcddb.upd6
-rw-r--r--kaudiocreator/kaudiocreator-meta.upd3
-rw-r--r--kaudiocreator/kaudiocreator.cpp305
-rw-r--r--kaudiocreator/kaudiocreator.desktop137
-rw-r--r--kaudiocreator/kaudiocreator.h93
-rw-r--r--kaudiocreator/kaudiocreator.kcfg107
-rw-r--r--kaudiocreator/kaudiocreator_encoders.kcfg28
-rw-r--r--kaudiocreator/kaudiocreator_workman.h27
-rw-r--r--kaudiocreator/kaudiocreatorui.rc41
-rw-r--r--kaudiocreator/lo16-app-kaudiocreator.pngbin0 -> 313 bytes
-rw-r--r--kaudiocreator/lo32-app-kaudiocreator.pngbin0 -> 539 bytes
-rw-r--r--kaudiocreator/main.cpp56
-rw-r--r--kaudiocreator/prefs.kcfgc7
-rw-r--r--kaudiocreator/ripconfig.ui191
-rw-r--r--kaudiocreator/ripper.cpp263
-rw-r--r--kaudiocreator/ripper.h68
-rw-r--r--kaudiocreator/tracks.ui225
-rw-r--r--kaudiocreator/tracksimp.cpp578
-rw-r--r--kaudiocreator/tracksimp.h136
-rwxr-xr-xkaudiocreator/upgrade-kaudiocreator-metadata.sh77
-rw-r--r--kaudiocreator/wizard.ui410
-rw-r--r--kaudiocreator/wizard.ui.h110
-rw-r--r--kdemultimedia.lsm11
-rw-r--r--kfile-plugins/Makefile.am22
-rw-r--r--kfile-plugins/RETURNED_ITEMS86
-rw-r--r--kfile-plugins/au/Makefile.am22
-rw-r--r--kfile-plugins/au/kfile_au.cpp172
-rw-r--r--kfile-plugins/au/kfile_au.desktop67
-rw-r--r--kfile-plugins/au/kfile_au.h37
-rw-r--r--kfile-plugins/avi/Makefile.am22
-rw-r--r--kfile-plugins/avi/kfile_avi.cpp540
-rw-r--r--kfile-plugins/avi/kfile_avi.desktop68
-rw-r--r--kfile-plugins/avi/kfile_avi.h90
-rw-r--r--kfile-plugins/flac/Makefile.am22
-rw-r--r--kfile-plugins/flac/configure.in.in1
-rw-r--r--kfile-plugins/flac/kfile_flac.cpp282
-rw-r--r--kfile-plugins/flac/kfile_flac.desktop62
-rw-r--r--kfile-plugins/flac/kfile_flac.h50
-rw-r--r--kfile-plugins/m3u/Makefile.am22
-rw-r--r--kfile-plugins/m3u/kfile_m3u.cpp87
-rw-r--r--kfile-plugins/m3u/kfile_m3u.desktop71
-rw-r--r--kfile-plugins/m3u/kfile_m3u.h39
-rw-r--r--kfile-plugins/mp3/Makefile.am22
-rw-r--r--kfile-plugins/mp3/configure.in.in1
-rw-r--r--kfile-plugins/mp3/kfile_mp3.cpp307
-rw-r--r--kfile-plugins/mp3/kfile_mp3.desktop70
-rw-r--r--kfile-plugins/mp3/kfile_mp3.h41
-rw-r--r--kfile-plugins/mpc/Makefile.am22
-rw-r--r--kfile-plugins/mpc/configure.in.in1
-rw-r--r--kfile-plugins/mpc/kfile_mpc.cpp253
-rw-r--r--kfile-plugins/mpc/kfile_mpc.desktop56
-rw-r--r--kfile-plugins/mpc/kfile_mpc.h48
-rw-r--r--kfile-plugins/mpeg/Makefile.am22
-rw-r--r--kfile-plugins/mpeg/kfile_mpeg.cpp582
-rw-r--r--kfile-plugins/mpeg/kfile_mpeg.desktop55
-rw-r--r--kfile-plugins/mpeg/kfile_mpeg.h70
-rw-r--r--kfile-plugins/ogg/Makefile.am22
-rw-r--r--kfile-plugins/ogg/configure.in.in1
-rw-r--r--kfile-plugins/ogg/kfile_ogg.cpp357
-rw-r--r--kfile-plugins/ogg/kfile_ogg.desktop68
-rw-r--r--kfile-plugins/ogg/kfile_ogg.h45
-rw-r--r--kfile-plugins/ogg/vcedit.c331
-rw-r--r--kfile-plugins/ogg/vcedit.h56
-rw-r--r--kfile-plugins/sid/Makefile.am22
-rw-r--r--kfile-plugins/sid/kfile_sid.cpp227
-rw-r--r--kfile-plugins/sid/kfile_sid.desktop60
-rw-r--r--kfile-plugins/sid/kfile_sid.h42
-rw-r--r--kfile-plugins/theora/Makefile.am22
-rw-r--r--kfile-plugins/theora/configure.in.bot7
-rw-r--r--kfile-plugins/theora/configure.in.in26
-rw-r--r--kfile-plugins/theora/kfile_theora.cpp322
-rw-r--r--kfile-plugins/theora/kfile_theora.desktop60
-rw-r--r--kfile-plugins/theora/kfile_theora.h42
-rw-r--r--kfile-plugins/wav/Makefile.am22
-rw-r--r--kfile-plugins/wav/kfile_wav.cpp173
-rw-r--r--kfile-plugins/wav/kfile_wav.desktop67
-rw-r--r--kfile-plugins/wav/kfile_wav.h37
-rw-r--r--kioslave/Makefile.am5
-rw-r--r--kioslave/audiocd/HACKING25
-rw-r--r--kioslave/audiocd/Makefile.am33
-rw-r--r--kioslave/audiocd/audiocd.cpp1159
-rw-r--r--kioslave/audiocd/audiocd.h126
-rw-r--r--kioslave/audiocd/audiocd.protocol16
-rw-r--r--kioslave/audiocd/audiocd.upd4
-rw-r--r--kioslave/audiocd/configure.in.bot0
-rw-r--r--kioslave/audiocd/configure.in.in46
-rw-r--r--kioslave/audiocd/kcmaudiocd/Makefile.am17
-rw-r--r--kioslave/audiocd/kcmaudiocd/audiocd.desktop182
-rw-r--r--kioslave/audiocd/kcmaudiocd/audiocdconfig.ui628
-rw-r--r--kioslave/audiocd/kcmaudiocd/kcmaudiocd.cpp267
-rw-r--r--kioslave/audiocd/kcmaudiocd/kcmaudiocd.h65
-rw-r--r--kioslave/audiocd/plugins/Makefile.am21
-rw-r--r--kioslave/audiocd/plugins/audiocdencoder.cpp95
-rw-r--r--kioslave/audiocd/plugins/audiocdencoder.h145
-rw-r--r--kioslave/audiocd/plugins/flac/Makefile.am13
-rw-r--r--kioslave/audiocd/plugins/flac/encoderflac.cpp197
-rw-r--r--kioslave/audiocd/plugins/flac/encoderflac.h61
-rw-r--r--kioslave/audiocd/plugins/lame/Makefile.am18
-rw-r--r--kioslave/audiocd/plugins/lame/audiocd_lame_encoder.kcfg138
-rw-r--r--kioslave/audiocd/plugins/lame/audiocd_lame_encoder.kcfgc4
-rw-r--r--kioslave/audiocd/plugins/lame/collectingprocess.cpp134
-rw-r--r--kioslave/audiocd/plugins/lame/collectingprocess.h72
-rw-r--r--kioslave/audiocd/plugins/lame/encoderlame.cpp366
-rw-r--r--kioslave/audiocd/plugins/lame/encoderlame.h67
-rw-r--r--kioslave/audiocd/plugins/lame/encoderlameconfig.ui930
-rw-r--r--kioslave/audiocd/plugins/vorbis/Makefile.am18
-rw-r--r--kioslave/audiocd/plugins/vorbis/audiocd_vorbis_encoder.kcfg84
-rw-r--r--kioslave/audiocd/plugins/vorbis/audiocd_vorbis_encoder.kcfgc4
-rw-r--r--kioslave/audiocd/plugins/vorbis/encodervorbis.cpp336
-rw-r--r--kioslave/audiocd/plugins/vorbis/encodervorbis.h68
-rw-r--r--kioslave/audiocd/plugins/vorbis/encodervorbisconfig.ui425
-rw-r--r--kioslave/audiocd/plugins/wav/Makefile.am15
-rw-r--r--kioslave/audiocd/plugins/wav/encodercda.cpp67
-rw-r--r--kioslave/audiocd/plugins/wav/encodercda.h61
-rw-r--r--kioslave/audiocd/plugins/wav/encoderwav.cpp85
-rw-r--r--kioslave/audiocd/plugins/wav/encoderwav.h56
-rwxr-xr-xkioslave/audiocd/upgrade-metadata.sh28
-rw-r--r--kmid/ChangeLog104
-rw-r--r--kmid/Makefile.am74
-rw-r--r--kmid/PEOPLE4
-rw-r--r--kmid/README123
-rw-r--r--kmid/audiomidi.desktop3
-rw-r--r--kmid/button1.xpm22
-rw-r--r--kmid/button2.xpm22
-rw-r--r--kmid/channel.cpp233
-rw-r--r--kmid/channel.h104
-rw-r--r--kmid/channel3d.cpp149
-rw-r--r--kmid/channel3d.h52
-rw-r--r--kmid/channel4d.cpp232
-rw-r--r--kmid/channel4d.h58
-rw-r--r--kmid/channelcfgdlg.cpp39
-rw-r--r--kmid/channelcfgdlg.h51
-rw-r--r--kmid/channelview.cpp165
-rw-r--r--kmid/channelview.h76
-rw-r--r--kmid/collectdlg.cpp307
-rw-r--r--kmid/collectdlg.h83
-rw-r--r--kmid/configure.in.in5
-rw-r--r--kmid/drums.o3bin0 -> 7680 bytes
-rw-r--r--kmid/drums.sbbin0 -> 6656 bytes
-rw-r--r--kmid/examples/DiesIrae.karbin0 -> 45515 bytes
-rw-r--r--kmid/examples/Guantanamera.karbin0 -> 37539 bytes
-rw-r--r--kmid/examples/Makefile.am6
-rw-r--r--kmid/examples/MariaDeLasMercedes.karbin0 -> 29440 bytes
-rw-r--r--kmid/examples/OFortuna.karbin0 -> 51276 bytes
-rw-r--r--kmid/hi16-app-kmid.pngbin0 -> 870 bytes
-rw-r--r--kmid/hi32-app-kmid.pngbin0 -> 2425 bytes
-rw-r--r--kmid/hi48-app-kmid.pngbin0 -> 4307 bytes
-rw-r--r--kmid/history.txt567
-rw-r--r--kmid/instrfilter.awk2
-rw-r--r--kmid/instrname.cpp132
-rw-r--r--kmid/instrname.h6
-rw-r--r--kmid/instrname.i18n133
-rw-r--r--kmid/kdisptext.cpp640
-rw-r--r--kmid/kdisptext.h125
-rw-r--r--kmid/keyboard.xpm50
-rw-r--r--kmid/klcdnumber.cpp339
-rw-r--r--kmid/klcdnumber.h153
-rw-r--r--kmid/kmid.desktop88
-rw-r--r--kmid/kmid.lsm22
-rw-r--r--kmid/kmid.spec62
-rw-r--r--kmid/kmidIface.h46
-rw-r--r--kmid/kmid_part.cpp144
-rw-r--r--kmid/kmid_part.h63
-rw-r--r--kmid/kmid_partui.rc17
-rw-r--r--kmid/kmidbutton.h32
-rw-r--r--kmid/kmidclient.cpp1606
-rw-r--r--kmid/kmidclient.h240
-rw-r--r--kmid/kmidframe.cpp728
-rw-r--r--kmid/kmidframe.h133
-rw-r--r--kmid/kmidui.rc44
-rw-r--r--kmid/ktrianglebutton.cpp164
-rw-r--r--kmid/ktrianglebutton.h73
-rw-r--r--kmid/main.cpp101
-rw-r--r--kmid/maps/Makefile.am5
-rw-r--r--kmid/maps/YamahaPSR500.map293
-rw-r--r--kmid/maps/YamahaPSS790.map299
-rw-r--r--kmid/maps/YamahaQY10.map305
-rw-r--r--kmid/maps/gm.map290
-rw-r--r--kmid/midicfgdlg.cpp138
-rw-r--r--kmid/midicfgdlg.h60
-rw-r--r--kmid/pics/Makefile.am3
-rw-r--r--kmid/pics/cr16-action-piano.pngbin0 -> 248 bytes
-rw-r--r--kmid/pics/cr16-action-volume.pngbin0 -> 180 bytes
-rw-r--r--kmid/pics/cr22-action-piano.pngbin0 -> 272 bytes
-rw-r--r--kmid/pics/cr22-action-volume.pngbin0 -> 377 bytes
-rw-r--r--kmid/pics/cr32-action-piano.pngbin0 -> 361 bytes
-rw-r--r--kmid/pics/cr32-action-volume.pngbin0 -> 447 bytes
-rw-r--r--kmid/qslidertime.cpp120
-rw-r--r--kmid/qslidertime.h56
-rw-r--r--kmid/randomlist.cpp103
-rw-r--r--kmid/randomlist.h34
-rw-r--r--kmid/rhythmview.cpp108
-rw-r--r--kmid/rhythmview.h54
-rw-r--r--kmid/slman.cpp320
-rw-r--r--kmid/slman.h74
-rw-r--r--kmid/songlist.cpp247
-rw-r--r--kmid/songlist.h84
-rw-r--r--kmid/std.o3bin0 -> 7680 bytes
-rw-r--r--kmid/std.sbbin0 -> 6656 bytes
-rw-r--r--kmid/version.h48
-rw-r--r--kmid/x-karaoke.desktop61
-rw-r--r--kmix/AUTHORS11
-rw-r--r--kmix/ChangeLog10
-rw-r--r--kmix/KMixApp.cpp71
-rw-r--r--kmix/KMixApp.h26
-rw-r--r--kmix/Makefile.am63
-rw-r--r--kmix/TODO47
-rw-r--r--kmix/TestCases36
-rw-r--r--kmix/colorwidget.ui276
-rw-r--r--kmix/configure.in.in0
-rw-r--r--kmix/dialogselectmaster.cpp197
-rw-r--r--kmix/dialogselectmaster.h43
-rw-r--r--kmix/dialogviewconfiguration.cpp102
-rw-r--r--kmix/dialogviewconfiguration.h30
-rw-r--r--kmix/doc/ControlNames.txt84
-rw-r--r--kmix/kledbutton.cpp79
-rw-r--r--kmix/kledbutton.h53
-rw-r--r--kmix/kmix-platforms.cpp149
-rw-r--r--kmix/kmix.cpp648
-rw-r--r--kmix/kmix.desktop92
-rw-r--r--kmix/kmix.h135
-rw-r--r--kmix/kmixapplet.cpp566
-rw-r--r--kmix/kmixapplet.desktop110
-rw-r--r--kmix/kmixapplet.h131
-rw-r--r--kmix/kmixctrl.cpp90
-rw-r--r--kmix/kmixctrl_restore.desktop70
-rw-r--r--kmix/kmixdockwidget.cpp391
-rw-r--r--kmix/kmixdockwidget.h82
-rw-r--r--kmix/kmixerwidget.cpp266
-rw-r--r--kmix/kmixerwidget.h118
-rw-r--r--kmix/kmixprefdlg.cpp132
-rw-r--r--kmix/kmixprefdlg.h67
-rw-r--r--kmix/kmixtoolbox.cpp215
-rw-r--r--kmix/kmixtoolbox.h28
-rw-r--r--kmix/kmixui.rc21
-rw-r--r--kmix/ksmallslider.cpp516
-rw-r--r--kmix/ksmallslider.h119
-rw-r--r--kmix/main.cpp69
-rw-r--r--kmix/mdwenum.cpp206
-rw-r--r--kmix/mdwenum.h77
-rw-r--r--kmix/mdwslider.cpp974
-rw-r--r--kmix/mdwslider.h128
-rw-r--r--kmix/mdwswitch.cpp231
-rw-r--r--kmix/mdwswitch.h88
-rw-r--r--kmix/mixdevice.cpp221
-rw-r--r--kmix/mixdevice.h114
-rw-r--r--kmix/mixdevicewidget.cpp120
-rw-r--r--kmix/mixdevicewidget.h115
-rw-r--r--kmix/mixer.cpp753
-rw-r--r--kmix/mixer.h174
-rw-r--r--kmix/mixerIface.h135
-rw-r--r--kmix/mixer_alsa.h53
-rw-r--r--kmix/mixer_alsa9.cpp828
-rw-r--r--kmix/mixer_backend.cpp147
-rw-r--r--kmix/mixer_backend.h104
-rw-r--r--kmix/mixer_hpux.cpp257
-rw-r--r--kmix/mixer_hpux.h36
-rw-r--r--kmix/mixer_irix.cpp133
-rw-r--r--kmix/mixer_irix.h28
-rw-r--r--kmix/mixer_none.cpp78
-rw-r--r--kmix/mixer_none.h22
-rw-r--r--kmix/mixer_oss.cpp330
-rw-r--r--kmix/mixer_oss.h33
-rw-r--r--kmix/mixer_oss4.cpp670
-rw-r--r--kmix/mixer_oss4.h42
-rw-r--r--kmix/mixer_sun.cpp473
-rw-r--r--kmix/mixer_sun.h52
-rw-r--r--kmix/mixertoolbox.cpp226
-rw-r--r--kmix/mixertoolbox.h22
-rw-r--r--kmix/mixset.cpp71
-rw-r--r--kmix/mixset.h21
-rw-r--r--kmix/pics/Listener.pngbin0 -> 1914 bytes
-rw-r--r--kmix/pics/Makefile.am14
-rw-r--r--kmix/pics/SpeakerFrontLeft.pngbin0 -> 1491 bytes
-rw-r--r--kmix/pics/SpeakerFrontRight.pngbin0 -> 1602 bytes
-rw-r--r--kmix/pics/SpeakerRearLeft.pngbin0 -> 1613 bytes
-rw-r--r--kmix/pics/SpeakerRearRight.pngbin0 -> 1663 bytes
-rw-r--r--kmix/pics/hi128-app-kmix.pngbin0 -> 12886 bytes
-rw-r--r--kmix/pics/hi16-app-kmix.pngbin0 -> 1043 bytes
-rw-r--r--kmix/pics/hi32-app-kmix.pngbin0 -> 2207 bytes
-rw-r--r--kmix/pics/hi48-app-kmix.pngbin0 -> 3703 bytes
-rw-r--r--kmix/pics/hi64-app-kmix.pngbin0 -> 5084 bytes
-rw-r--r--kmix/pics/kmixdocked.pngbin0 -> 1236 bytes
-rw-r--r--kmix/pics/kmixdocked_error.pngbin0 -> 1234 bytes
-rw-r--r--kmix/pics/kmixdocked_mute.pngbin0 -> 654 bytes
-rw-r--r--kmix/pics/mix_ac97.pngbin0 -> 517 bytes
-rw-r--r--kmix/pics/mix_audio.pngbin0 -> 323 bytes
-rw-r--r--kmix/pics/mix_bass.pngbin0 -> 334 bytes
-rw-r--r--kmix/pics/mix_cd.pngbin0 -> 489 bytes
-rw-r--r--kmix/pics/mix_digital.pngbin0 -> 206 bytes
-rw-r--r--kmix/pics/mix_ext.pngbin0 -> 331 bytes
-rw-r--r--kmix/pics/mix_headphone.pngbin0 -> 302 bytes
-rw-r--r--kmix/pics/mix_microphone.pngbin0 -> 350 bytes
-rw-r--r--kmix/pics/mix_midi.pngbin0 -> 337 bytes
-rw-r--r--kmix/pics/mix_recmon.pngbin0 -> 344 bytes
-rw-r--r--kmix/pics/mix_record.pngbin0 -> 983 bytes
-rw-r--r--kmix/pics/mix_surround.pngbin0 -> 265 bytes
-rw-r--r--kmix/pics/mix_toslink.pngbin0 -> 366 bytes
-rw-r--r--kmix/pics/mix_treble.pngbin0 -> 350 bytes
-rw-r--r--kmix/pics/mix_unknown.pngbin0 -> 340 bytes
-rw-r--r--kmix/pics/mix_video.pngbin0 -> 164 bytes
-rw-r--r--kmix/pics/mix_volume.pngbin0 -> 323 bytes
-rw-r--r--kmix/resource.h30
-rw-r--r--kmix/restore_kmix_volumes.desktop58
-rw-r--r--kmix/version.h21
-rw-r--r--kmix/verticaltext.cpp57
-rw-r--r--kmix/verticaltext.h19
-rw-r--r--kmix/viewapplet.cpp242
-rw-r--r--kmix/viewapplet.h47
-rw-r--r--kmix/viewbase.cpp187
-rw-r--r--kmix/viewbase.h121
-rw-r--r--kmix/viewdockareapopup.cpp206
-rw-r--r--kmix/viewdockareapopup.h57
-rw-r--r--kmix/viewgrid.cpp212
-rw-r--r--kmix/viewgrid.h42
-rw-r--r--kmix/viewinput.cpp50
-rw-r--r--kmix/viewinput.h19
-rw-r--r--kmix/viewoutput.cpp50
-rw-r--r--kmix/viewoutput.h19
-rw-r--r--kmix/viewsliders.cpp140
-rw-r--r--kmix/viewsliders.h33
-rw-r--r--kmix/viewsurround.cpp270
-rw-r--r--kmix/viewsurround.h42
-rw-r--r--kmix/viewswitches.cpp189
-rw-r--r--kmix/viewswitches.h36
-rw-r--r--kmix/volume.cpp266
-rw-r--r--kmix/volume.h80
-rw-r--r--krec/Makefile.am95
-rw-r--r--krec/configure.in.in3
-rw-r--r--krec/globals.h36
-rw-r--r--krec/hi128-app-krec.pngbin0 -> 9085 bytes
-rw-r--r--krec/hi16-app-krec.pngbin0 -> 538 bytes
-rw-r--r--krec/hi22-app-krec.pngbin0 -> 536 bytes
-rw-r--r--krec/hi32-app-krec.pngbin0 -> 1711 bytes
-rw-r--r--krec/hi48-app-krec.pngbin0 -> 3138 bytes
-rw-r--r--krec/hi64-app-krec.pngbin0 -> 4137 bytes
-rw-r--r--krec/kcm_krec.desktop108
-rw-r--r--krec/kcm_krec_files.desktop127
-rw-r--r--krec/krec.desktop80
-rw-r--r--krec/krec_exportitem.desktop61
-rw-r--r--krec/krec_exportwave.desktop57
-rw-r--r--krec/krecconfig_files.cpp85
-rw-r--r--krec/krecconfig_files.h52
-rw-r--r--krec/krecconfig_fileswidget.cpp147
-rw-r--r--krec/krecconfig_fileswidget.h68
-rw-r--r--krec/krecconfigure.cpp192
-rw-r--r--krec/krecconfigure.h67
-rw-r--r--krec/krecexport_template.cpp89
-rw-r--r--krec/krecexport_template.h99
-rw-r--r--krec/krecexport_wave.cpp135
-rw-r--r--krec/krecexport_wave.h43
-rw-r--r--krec/krecfile.cpp457
-rw-r--r--krec/krecfile.h201
-rw-r--r--krec/krecfileview.cpp89
-rw-r--r--krec/krecfileview.h56
-rw-r--r--krec/krecfileviewhelpers.cpp268
-rw-r--r--krec/krecfileviewhelpers.h110
-rw-r--r--krec/krecfilewidgets.cpp313
-rw-r--r--krec/krecfilewidgets.h110
-rw-r--r--krec/krecglobal.cpp130
-rw-r--r--krec/krecglobal.h97
-rw-r--r--krec/krecnewproperties.cpp102
-rw-r--r--krec/krecnewproperties.h60
-rw-r--r--krec/krecord.cpp407
-rw-r--r--krec/krecord.h60
-rw-r--r--krec/krecord_private.h148
-rw-r--r--krec/krecui.rc57
-rw-r--r--krec/main.cpp68
-rw-r--r--krec/mp3_export/Makefile.am22
-rw-r--r--krec/mp3_export/krec_exportmp3.desktop59
-rw-r--r--krec/mp3_export/krecexport_mp3.cpp234
-rw-r--r--krec/mp3_export/krecexport_mp3.h52
-rw-r--r--krec/ogg_export/Makefile.am22
-rw-r--r--krec/ogg_export/krec_exportogg.desktop60
-rw-r--r--krec/ogg_export/krecexport_ogg.cpp254
-rw-r--r--krec/ogg_export/krecexport_ogg.h63
-rw-r--r--krec/pics/Makefile.am2
-rw-r--r--krec/pics/cr16-action-krec_record.pngbin0 -> 525 bytes
-rw-r--r--krec/pics/cr22-action-krec_record.pngbin0 -> 834 bytes
-rw-r--r--krec/pics/cr32-action-krec_record.pngbin0 -> 1170 bytes
-rw-r--r--krec/pics/cr32-mime-krec_fileempty.pngbin0 -> 1379 bytes
-rw-r--r--krec/pics/cr32-mime-krec_fileplay.pngbin0 -> 2036 bytes
-rw-r--r--krec/pics/cr32-mime-krec_filerec.pngbin0 -> 1733 bytes
-rw-r--r--krec/tips44
-rw-r--r--kscd/CDQuery.txt21
-rw-r--r--kscd/ChangeLog266
-rw-r--r--kscd/Makefile.am83
-rw-r--r--kscd/README59
-rw-r--r--kscd/TODO9
-rw-r--r--kscd/audiocd_play.desktop55
-rw-r--r--kscd/bitmaps/AUTHORS37
-rw-r--r--kscd/bitmaps/CompactDisc.xpm68
-rw-r--r--kscd/bitmaps/Image3.gifbin0 -> 1043 bytes
-rw-r--r--kscd/bitmaps/Image3.tifbin0 -> 2442 bytes
-rw-r--r--kscd/bitmaps/Image3.xpmbin0 -> 6878 bytes
-rw-r--r--kscd/bitmaps/Image4.gifbin0 -> 156 bytes
-rw-r--r--kscd/bitmaps/Image4.xpm31
-rw-r--r--kscd/bitmaps/Makefile.am2
-rw-r--r--kscd/bitmaps/cd3d.xpm62
-rw-r--r--kscd/bitmaps/cdsmallpause.xpm34
-rw-r--r--kscd/bitmaps/cdsmallplay.xpm33
-rw-r--r--kscd/bitmaps/cdsmallstop.xpm34
-rw-r--r--kscd/bitmaps/db.xbm8
-rw-r--r--kscd/bitmaps/db.xpmbin0 -> 7342 bytes
-rw-r--r--kscd/bitmaps/eject.xbm7
-rw-r--r--kscd/bitmaps/ff.xbm7
-rw-r--r--kscd/bitmaps/info.xbm6
-rw-r--r--kscd/bitmaps/kscdlogo.xpm137
-rw-r--r--kscd/bitmaps/lock.xbm7
-rw-r--r--kscd/bitmaps/logo.xbm28
-rw-r--r--kscd/bitmaps/magic.xbm7
-rw-r--r--kscd/bitmaps/nexttrk.xbm7
-rw-r--r--kscd/bitmaps/options.xbm7
-rw-r--r--kscd/bitmaps/playpaus.xbm10
-rw-r--r--kscd/bitmaps/poweroff.xbm7
-rw-r--r--kscd/bitmaps/prevtrk.xbm7
-rw-r--r--kscd/bitmaps/repeat.xbm7
-rw-r--r--kscd/bitmaps/rew.xbm7
-rw-r--r--kscd/bitmaps/shuffle.xbm7
-rw-r--r--kscd/bitmaps/stop.xbm7
-rw-r--r--kscd/bwlednum.cpp591
-rw-r--r--kscd/bwlednum.h93
-rw-r--r--kscd/cddaslave.c532
-rw-r--r--kscd/cddbdlg.cpp198
-rw-r--r--kscd/cddbdlg.h46
-rw-r--r--kscd/configWidget.cpp143
-rw-r--r--kscd/configWidget.h50
-rw-r--r--kscd/configWidgetUI.ui462
-rw-r--r--kscd/configure.in.in68
-rw-r--r--kscd/cr22-action-cdsmall.pngbin0 -> 1417 bytes
-rw-r--r--kscd/docking.cpp158
-rw-r--r--kscd/docking.h75
-rw-r--r--kscd/hi128-app-kscd.pngbin0 -> 18304 bytes
-rw-r--r--kscd/hi16-app-kscd.pngbin0 -> 820 bytes
-rw-r--r--kscd/hi32-app-kscd.pngbin0 -> 2297 bytes
-rw-r--r--kscd/hi48-app-kscd.pngbin0 -> 4682 bytes
-rw-r--r--kscd/hi64-app-kscd.pngbin0 -> 6912 bytes
-rw-r--r--kscd/kcompactdisc.cpp486
-rw-r--r--kscd/kcompactdisc.h303
-rwxr-xr-xkscd/kscd-script782
-rw-r--r--kscd/kscd.cpp1677
-rw-r--r--kscd/kscd.desktop85
-rw-r--r--kscd/kscd.h258
-rw-r--r--kscd/kscd.kcfg107
-rw-r--r--kscd/kscd.lsm15
-rw-r--r--kscd/kscd.profile.xml59
-rw-r--r--kscd/kscdmagic/Makefile.am40
-rw-r--r--kscd/kscdmagic/README146
-rw-r--r--kscd/kscdmagic/core.cpp410
-rw-r--r--kscd/kscdmagic/logo.h50
-rw-r--r--kscd/kscdmagic/magicconf.h23
-rw-r--r--kscd/kscdmagic/main.cpp300
-rw-r--r--kscd/kscdmagic/polygon.h106
-rw-r--r--kscd/kscdmagic/sound.cpp252
-rw-r--r--kscd/kscdmagic/symbol.h1028
-rw-r--r--kscd/kscdmagic/syna.h180
-rw-r--r--kscd/kscdmagic/version.h1
-rw-r--r--kscd/kscdmagic/xlib.c781
-rw-r--r--kscd/kscdmagic/xlib.h139
-rw-r--r--kscd/kscdmagic/xlibwrap.cpp209
-rw-r--r--kscd/ledlamp.cpp103
-rw-r--r--kscd/ledlamp.h63
-rw-r--r--kscd/libwm/Makefile.am23
-rw-r--r--kscd/libwm/PLAT_IMPL_STATUS48
-rw-r--r--kscd/libwm/README20
-rw-r--r--kscd/libwm/audio/Makefile.am5
-rw-r--r--kscd/libwm/audio/audio.c29
-rw-r--r--kscd/libwm/audio/audio.h21
-rw-r--r--kscd/libwm/audio/audio_alsa.c334
-rw-r--r--kscd/libwm/audio/audio_arts.c141
-rw-r--r--kscd/libwm/audio/audio_sun.c525
-rw-r--r--kscd/libwm/buildindex.c221
-rw-r--r--kscd/libwm/cdda.c458
-rw-r--r--kscd/libwm/cddaslave.c572
-rw-r--r--kscd/libwm/cddb.c588
-rw-r--r--kscd/libwm/cdinfo.c890
-rw-r--r--kscd/libwm/cdrom.c899
-rw-r--r--kscd/libwm/cdtext.c428
-rw-r--r--kscd/libwm/configure.in.in72
-rw-r--r--kscd/libwm/database.c1543
-rw-r--r--kscd/libwm/drv_sony.c166
-rw-r--r--kscd/libwm/drv_toshiba.c149
-rw-r--r--kscd/libwm/include/wm_cdda.h203
-rw-r--r--kscd/libwm/include/wm_cddb.h36
-rw-r--r--kscd/libwm/include/wm_cdinfo.h76
-rw-r--r--kscd/libwm/include/wm_cdrom.h114
-rw-r--r--kscd/libwm/include/wm_cdtext.h105
-rw-r--r--kscd/libwm/include/wm_config.h377
-rw-r--r--kscd/libwm/include/wm_database.h42
-rw-r--r--kscd/libwm/include/wm_helpers.h109
-rw-r--r--kscd/libwm/include/wm_index.h36
-rw-r--r--kscd/libwm/include/wm_platform.h51
-rw-r--r--kscd/libwm/include/wm_scsi.h50
-rw-r--r--kscd/libwm/include/wm_struct.h198
-rw-r--r--kscd/libwm/include/wm_version.h35
-rw-r--r--kscd/libwm/include/workman.h52
-rw-r--r--kscd/libwm/include/workman_defs.h30
-rw-r--r--kscd/libwm/index.c383
-rw-r--r--kscd/libwm/plat_aix.c489
-rw-r--r--kscd/libwm/plat_bsd386.c510
-rw-r--r--kscd/libwm/plat_freebsd.c560
-rw-r--r--kscd/libwm/plat_hpux.c358
-rw-r--r--kscd/libwm/plat_irix.c474
-rw-r--r--kscd/libwm/plat_linux.c803
-rw-r--r--kscd/libwm/plat_linux_audio.c489
-rw-r--r--kscd/libwm/plat_linux_cdda.c253
-rw-r--r--kscd/libwm/plat_news.c442
-rw-r--r--kscd/libwm/plat_openbsd.c545
-rw-r--r--kscd/libwm/plat_osf1.c674
-rw-r--r--kscd/libwm/plat_scor5.c426
-rw-r--r--kscd/libwm/plat_sun.c972
-rw-r--r--kscd/libwm/plat_sun_audio.c493
-rw-r--r--kscd/libwm/plat_sun_cdda.c380
-rw-r--r--kscd/libwm/plat_svr4.c482
-rw-r--r--kscd/libwm/plat_template.c295
-rw-r--r--kscd/libwm/plat_ultrix.c663
-rw-r--r--kscd/libwm/scsi.c667
-rw-r--r--kscd/libwm/wm_helpers.c238
-rw-r--r--kscd/panel.ui468
-rw-r--r--kscd/prefs.kcfgc5
-rw-r--r--kscd/version.h1
-rwxr-xr-xkscd/workman2cddb.pl86
-rw-r--r--kscd/xmcd.desktop75
-rw-r--r--libkcddb/Makefile.am33
-rw-r--r--libkcddb/TODO12
-rw-r--r--libkcddb/asynccddbplookup.cpp352
-rw-r--r--libkcddb/asynccddbplookup.h94
-rw-r--r--libkcddb/asynchttplookup.cpp159
-rw-r--r--libkcddb/asynchttplookup.h62
-rw-r--r--libkcddb/asynchttpsubmit.cpp55
-rw-r--r--libkcddb/asynchttpsubmit.h44
-rw-r--r--libkcddb/asyncsmtpsubmit.cpp57
-rw-r--r--libkcddb/asyncsmtpsubmit.h46
-rw-r--r--libkcddb/cache.cpp131
-rw-r--r--libkcddb/cache.h57
-rw-r--r--libkcddb/categories.cpp50
-rw-r--r--libkcddb/categories.h42
-rw-r--r--libkcddb/cddb.cpp227
-rw-r--r--libkcddb/cddb.h92
-rw-r--r--libkcddb/cddbplookup.cpp113
-rw-r--r--libkcddb/cddbplookup.h56
-rw-r--r--libkcddb/cdinfo.cpp339
-rw-r--r--libkcddb/cdinfo.h146
-rw-r--r--libkcddb/cdinfodialogbase.ui429
-rw-r--r--libkcddb/cdinfodialogbase.ui.h248
-rw-r--r--libkcddb/cdinfoencodingwidget.cpp70
-rw-r--r--libkcddb/cdinfoencodingwidget.h45
-rw-r--r--libkcddb/cdinfoencodingwidgetbase.ui70
-rw-r--r--libkcddb/client.cpp315
-rw-r--r--libkcddb/client.h104
-rw-r--r--libkcddb/config.cpp56
-rw-r--r--libkcddb/config.h45
-rw-r--r--libkcddb/configbase.kcfgc4
-rw-r--r--libkcddb/genres.cpp123
-rw-r--r--libkcddb/genres.h43
-rw-r--r--libkcddb/httplookup.cpp186
-rw-r--r--libkcddb/httplookup.h82
-rw-r--r--libkcddb/httpsubmit.cpp60
-rw-r--r--libkcddb/httpsubmit.h41
-rw-r--r--libkcddb/kcmcddb/Makefile.am23
-rw-r--r--libkcddb/kcmcddb/cddbconfigwidget.cpp108
-rw-r--r--libkcddb/kcmcddb/cddbconfigwidget.h43
-rw-r--r--libkcddb/kcmcddb/cddbconfigwidgetbase.ui594
-rw-r--r--libkcddb/kcmcddb/kcmcddb-emailsettings.upd6
-rw-r--r--libkcddb/kcmcddb/kcmcddb.cpp132
-rw-r--r--libkcddb/kcmcddb/kcmcddb.h55
-rw-r--r--libkcddb/kcmcddb/libkcddb.desktop187
-rw-r--r--libkcddb/libkcddb.kcfg66
-rw-r--r--libkcddb/lookup.cpp87
-rw-r--r--libkcddb/lookup.h65
-rw-r--r--libkcddb/sites.cpp119
-rw-r--r--libkcddb/sites.h53
-rw-r--r--libkcddb/smtpsubmit.cpp60
-rw-r--r--libkcddb/smtpsubmit.h43
-rw-r--r--libkcddb/submit.cpp101
-rw-r--r--libkcddb/submit.h65
-rw-r--r--libkcddb/synccddbplookup.cpp222
-rw-r--r--libkcddb/synccddbplookup.h51
-rw-r--r--libkcddb/synchttplookup.cpp131
-rw-r--r--libkcddb/synchttplookup.h50
-rw-r--r--libkcddb/synchttpsubmit.cpp46
-rw-r--r--libkcddb/synchttpsubmit.h37
-rw-r--r--libkcddb/syncsmtpsubmit.cpp46
-rw-r--r--libkcddb/syncsmtpsubmit.h38
-rw-r--r--libkcddb/test/Makefile.am48
-rw-r--r--libkcddb/test/asynccddblookuptest.cpp113
-rw-r--r--libkcddb/test/asynccddblookuptest.h27
-rw-r--r--libkcddb/test/asynchttplookuptest.cpp111
-rw-r--r--libkcddb/test/asynchttplookuptest.h27
-rw-r--r--libkcddb/test/asynchttpsubmittest.cpp80
-rw-r--r--libkcddb/test/asynchttpsubmittest.h25
-rw-r--r--libkcddb/test/asyncsmtpsubmittest.cpp81
-rw-r--r--libkcddb/test/asyncsmtpsubmittest.h25
-rw-r--r--libkcddb/test/sitestest.cpp47
-rw-r--r--libkcddb/test/synccddblookuptest.cpp84
-rw-r--r--libkcddb/test/synchttplookuptest.cpp84
-rw-r--r--libkcddb/test/synchttpsubmittest.cpp79
-rw-r--r--libkcddb/test/syncsmtpsubmittest.cpp81
-rw-r--r--libkcddb/test/utf8test.cpp50
-rw-r--r--mpeglib/CHANGES24
-rw-r--r--mpeglib/COPYING498
-rw-r--r--mpeglib/INSTALL23
-rw-r--r--mpeglib/Makefile.am9
-rw-r--r--mpeglib/PROBLEMS20
-rw-r--r--mpeglib/README51
-rw-r--r--mpeglib/README-WIN32.TXT16
-rw-r--r--mpeglib/TODO2
-rw-r--r--mpeglib/configure.in.in241
-rw-r--r--mpeglib/example/Makefile.am3
-rw-r--r--mpeglib/example/cddaplay/Makefile.am33
-rw-r--r--mpeglib/example/cddaplay/cddaplay.cpp75
-rw-r--r--mpeglib/example/mpgplay/Makefile.am32
-rw-r--r--mpeglib/example/mpgplay/mpgplay.cpp74
-rw-r--r--mpeglib/example/splay/Makefile.am41
-rw-r--r--mpeglib/example/splay/mp3framing.cpp274
-rw-r--r--mpeglib/example/splay/splay.cpp64
-rw-r--r--mpeglib/example/tplay/Makefile.am32
-rw-r--r--mpeglib/example/tplay/tplay.cpp66
-rw-r--r--mpeglib/example/yaf/Makefile.am15
-rw-r--r--mpeglib/example/yaf/README4
-rw-r--r--mpeglib/example/yaf/yafcdda/Makefile.am35
-rw-r--r--mpeglib/example/yaf/yafcdda/cdda_control.cpp184
-rw-r--r--mpeglib/example/yaf/yafcore/Makefile.am44
-rw-r--r--mpeglib/example/yaf/yafcore/PROTOCOL.v01243
-rw-r--r--mpeglib/example/yaf/yafcore/README27
-rw-r--r--mpeglib/example/yaf/yafcore/buffer.cpp156
-rw-r--r--mpeglib/example/yaf/yafcore/buffer.h64
-rw-r--r--mpeglib/example/yaf/yafcore/commandLine.cpp86
-rw-r--r--mpeglib/example/yaf/yafcore/commandLine.h60
-rw-r--r--mpeglib/example/yaf/yafcore/commandTable.cpp256
-rw-r--r--mpeglib/example/yaf/yafcore/commandTable.h82
-rw-r--r--mpeglib/example/yaf/yafcore/commandTableYAF.cpp36
-rw-r--r--mpeglib/example/yaf/yafcore/commandTableYAF.h36
-rw-r--r--mpeglib/example/yaf/yafcore/inputDecoder.cpp237
-rw-r--r--mpeglib/example/yaf/yafcore/inputDecoder.h80
-rw-r--r--mpeglib/example/yaf/yafcore/inputInterface.cpp195
-rw-r--r--mpeglib/example/yaf/yafcore/inputInterface.h102
-rw-r--r--mpeglib/example/yaf/yafcore/lineStack.cpp95
-rw-r--r--mpeglib/example/yaf/yafcore/lineStack.h55
-rw-r--r--mpeglib/example/yaf/yafcore/multiReader.cpp206
-rw-r--r--mpeglib/example/yaf/yafcore/multiReader.h88
-rw-r--r--mpeglib/example/yaf/yafcore/outputDecoder.cpp144
-rw-r--r--mpeglib/example/yaf/yafcore/outputDecoder.h52
-rw-r--r--mpeglib/example/yaf/yafcore/outputInterface.cpp85
-rw-r--r--mpeglib/example/yaf/yafcore/outputInterface.h60
-rw-r--r--mpeglib/example/yaf/yafcore/parser.cpp152
-rw-r--r--mpeglib/example/yaf/yafcore/parser.h55
-rw-r--r--mpeglib/example/yaf/yafcore/runtimeTableYAF.cpp34
-rw-r--r--mpeglib/example/yaf/yafcore/runtimeTableYAF.h35
-rw-r--r--mpeglib/example/yaf/yafcore/yafCommand.defs89
-rw-r--r--mpeglib/example/yaf/yafcore/yafRuntime.defs67
-rw-r--r--mpeglib/example/yaf/yafcore/yaf_control.cpp81
-rw-r--r--mpeglib/example/yaf/yafcore/yaf_control.h26
-rw-r--r--mpeglib/example/yaf/yafmpgplay/Makefile.am34
-rw-r--r--mpeglib/example/yaf/yafmpgplay/mpg_control.cpp197
-rw-r--r--mpeglib/example/yaf/yafmpgplay/stresstest24
-rw-r--r--mpeglib/example/yaf/yafsplay/Makefile.am35
-rw-r--r--mpeglib/example/yaf/yafsplay/splay_control.cpp161
-rw-r--r--mpeglib/example/yaf/yafsplay/stresstest23
-rw-r--r--mpeglib/example/yaf/yaftplay/Makefile.am32
-rw-r--r--mpeglib/example/yaf/yaftplay/tplay_control.cpp135
-rw-r--r--mpeglib/example/yaf/yafvorbis/Makefile.am33
-rw-r--r--mpeglib/example/yaf/yafvorbis/vorbis_control.cpp150
-rw-r--r--mpeglib/example/yaf/yafxplayer/Makefile.am37
-rw-r--r--mpeglib/example/yaf/yafxplayer/README30
-rw-r--r--mpeglib/example/yaf/yafxplayer/commandTableXPlayer.cpp35
-rw-r--r--mpeglib/example/yaf/yafxplayer/commandTableXPlayer.h36
-rw-r--r--mpeglib/example/yaf/yafxplayer/inputDecoderXPlayer.cpp239
-rw-r--r--mpeglib/example/yaf/yafxplayer/inputDecoderXPlayer.h117
-rw-r--r--mpeglib/example/yaf/yafxplayer/inputDecoderYAF.cpp220
-rw-r--r--mpeglib/example/yaf/yafxplayer/inputDecoderYAF.h51
-rw-r--r--mpeglib/example/yaf/yafxplayer/runtimeTableXPlayer.cpp37
-rw-r--r--mpeglib/example/yaf/yafxplayer/runtimeTableXPlayer.h40
-rw-r--r--mpeglib/example/yaf/yafxplayer/xplayerCommand.defs84
-rw-r--r--mpeglib/example/yaf/yafxplayer/xplayerRuntime.defs123
-rw-r--r--mpeglib/example/yaf/yafxplayer/xplayer_control.cpp44
-rw-r--r--mpeglib/example/yaf/yafxplayer/xplayer_control.h25
-rw-r--r--mpeglib/example/yaf/yafxplayer/yafOutputStream.cpp307
-rw-r--r--mpeglib/example/yaf/yafxplayer/yafOutputStream.h140
-rw-r--r--mpeglib/example/yaf/yafyuv/Makefile.am37
-rw-r--r--mpeglib/example/yaf/yafyuv/yuv_control.cpp207
-rw-r--r--mpeglib/lib/Makefile.am36
-rw-r--r--mpeglib/lib/README80
-rw-r--r--mpeglib/lib/decoder/Makefile.am39
-rw-r--r--mpeglib/lib/decoder/cddaPlugin.cpp150
-rw-r--r--mpeglib/lib/decoder/cddaPlugin.h49
-rw-r--r--mpeglib/lib/decoder/command.cpp85
-rw-r--r--mpeglib/lib/decoder/command.h52
-rw-r--r--mpeglib/lib/decoder/commandPipe.cpp163
-rw-r--r--mpeglib/lib/decoder/commandPipe.h78
-rw-r--r--mpeglib/lib/decoder/decoderPlugin.cpp428
-rw-r--r--mpeglib/lib/decoder/decoderPlugin.h201
-rw-r--r--mpeglib/lib/decoder/mpegPlugin.cpp160
-rw-r--r--mpeglib/lib/decoder/mpegPlugin.h47
-rw-r--r--mpeglib/lib/decoder/mpgPlugin.cpp260
-rw-r--r--mpeglib/lib/decoder/mpgPlugin.h62
-rw-r--r--mpeglib/lib/decoder/nukePlugin.cpp62
-rw-r--r--mpeglib/lib/decoder/nukePlugin.h34
-rw-r--r--mpeglib/lib/decoder/splayPlugin.cpp308
-rw-r--r--mpeglib/lib/decoder/splayPlugin.h67
-rw-r--r--mpeglib/lib/decoder/tplayPlugin.cpp264
-rw-r--r--mpeglib/lib/decoder/tplayPlugin.h47
-rw-r--r--mpeglib/lib/decoder/vorbisPlugin.cpp305
-rw-r--r--mpeglib/lib/decoder/vorbisPlugin.h87
-rw-r--r--mpeglib/lib/dummy.cpp6
-rw-r--r--mpeglib/lib/frame/IOFrameQueue.cpp60
-rw-r--r--mpeglib/lib/frame/IOFrameQueue.h60
-rw-r--r--mpeglib/lib/frame/Makefile.am33
-rw-r--r--mpeglib/lib/frame/README68
-rw-r--r--mpeglib/lib/frame/audioFrame.cpp111
-rw-r--r--mpeglib/lib/frame/audioFrame.h83
-rw-r--r--mpeglib/lib/frame/audioFrameQueue.cpp339
-rw-r--r--mpeglib/lib/frame/audioFrameQueue.h75
-rw-r--r--mpeglib/lib/frame/floatFrame.cpp50
-rw-r--r--mpeglib/lib/frame/floatFrame.h46
-rw-r--r--mpeglib/lib/frame/frame.cpp73
-rw-r--r--mpeglib/lib/frame/frame.h101
-rw-r--r--mpeglib/lib/frame/frameQueue.cpp101
-rw-r--r--mpeglib/lib/frame/frameQueue.h43
-rw-r--r--mpeglib/lib/frame/framer.cpp241
-rw-r--r--mpeglib/lib/frame/framer.h182
-rw-r--r--mpeglib/lib/frame/pcmFrame.cpp140
-rw-r--r--mpeglib/lib/frame/pcmFrame.h45
-rw-r--r--mpeglib/lib/frame/rawDataBuffer.cpp21
-rw-r--r--mpeglib/lib/frame/rawDataBuffer.h48
-rw-r--r--mpeglib/lib/frame/rawFrame.cpp90
-rw-r--r--mpeglib/lib/frame/rawFrame.h75
-rw-r--r--mpeglib/lib/input/Makefile.am37
-rw-r--r--mpeglib/lib/input/README15
-rw-r--r--mpeglib/lib/input/bufferInputStream.cpp288
-rw-r--r--mpeglib/lib/input/bufferInputStream.h88
-rw-r--r--mpeglib/lib/input/cddaInputStream.cpp225
-rw-r--r--mpeglib/lib/input/cddaInputStream.h85
-rw-r--r--mpeglib/lib/input/cdigrap.cpp100
-rw-r--r--mpeglib/lib/input/cdromAccess.cpp54
-rw-r--r--mpeglib/lib/input/cdromAccess_Empty.cpp47
-rw-r--r--mpeglib/lib/input/cdromAccess_Linux.cpp124
-rw-r--r--mpeglib/lib/input/cdromInputStream.cpp309
-rw-r--r--mpeglib/lib/input/cdromInputStream.h88
-rw-r--r--mpeglib/lib/input/cdromRawAccess.cpp113
-rw-r--r--mpeglib/lib/input/cdromRawAccess.h67
-rw-r--r--mpeglib/lib/input/cdromToc.cpp234
-rw-r--r--mpeglib/lib/input/cdromToc.h65
-rw-r--r--mpeglib/lib/input/fileAccessWrapper.cpp65
-rw-r--r--mpeglib/lib/input/fileAccessWrapper.h41
-rw-r--r--mpeglib/lib/input/fileInputStream.cpp148
-rw-r--r--mpeglib/lib/input/fileInputStream.h46
-rw-r--r--mpeglib/lib/input/httpInputStream.cpp327
-rw-r--r--mpeglib/lib/input/httpInputStream.h84
-rw-r--r--mpeglib/lib/input/inputDetector.cpp192
-rw-r--r--mpeglib/lib/input/inputDetector.h56
-rw-r--r--mpeglib/lib/input/inputPlugin.cpp92
-rw-r--r--mpeglib/lib/input/inputPlugin.h45
-rw-r--r--mpeglib/lib/input/inputStream.cpp135
-rw-r--r--mpeglib/lib/input/inputStream.h79
-rw-r--r--mpeglib/lib/input/simpleRingBuffer.cpp420
-rw-r--r--mpeglib/lib/input/simpleRingBuffer.h136
-rw-r--r--mpeglib/lib/input/threadSafeInputStream.cpp137
-rw-r--r--mpeglib/lib/input/threadSafeInputStream.h56
-rw-r--r--mpeglib/lib/mpegplay/COPYRIGHT145
-rw-r--r--mpeglib/lib/mpegplay/Makefile.am48
-rw-r--r--mpeglib/lib/mpegplay/README26
-rw-r--r--mpeglib/lib/mpegplay/configure.in.in1
-rw-r--r--mpeglib/lib/mpegplay/copyFunctions.cpp330
-rw-r--r--mpeglib/lib/mpegplay/copyFunctions.h95
-rw-r--r--mpeglib/lib/mpegplay/copyFunctions_asm.cpp90
-rw-r--r--mpeglib/lib/mpegplay/copyFunctions_asm.h61
-rw-r--r--mpeglib/lib/mpegplay/copyFunctions_mmx.cpp313
-rw-r--r--mpeglib/lib/mpegplay/copyFunctions_mmx.h68
-rw-r--r--mpeglib/lib/mpegplay/decoderClass.cpp893
-rw-r--r--mpeglib/lib/mpegplay/decoderClass.h96
-rw-r--r--mpeglib/lib/mpegplay/decoderTables.cpp594
-rw-r--r--mpeglib/lib/mpegplay/decoderTables.h110
-rw-r--r--mpeglib/lib/mpegplay/globals.cpp41
-rw-r--r--mpeglib/lib/mpegplay/gop.cpp170
-rw-r--r--mpeglib/lib/mpegplay/gop.h62
-rw-r--r--mpeglib/lib/mpegplay/jrevdct.cpp1690
-rw-r--r--mpeglib/lib/mpegplay/jrevdct.h57
-rw-r--r--mpeglib/lib/mpegplay/macroBlock.cpp1152
-rw-r--r--mpeglib/lib/mpegplay/macroBlock.h97
-rw-r--r--mpeglib/lib/mpegplay/mainMpegPlay.cpp179
-rw-r--r--mpeglib/lib/mpegplay/mmxidct.cpp27
-rw-r--r--mpeglib/lib/mpegplay/mmxidct.h22
-rw-r--r--mpeglib/lib/mpegplay/mmxidct_asm.S738
-rw-r--r--mpeglib/lib/mpegplay/motionVector.cpp130
-rw-r--r--mpeglib/lib/mpegplay/motionVector.h40
-rw-r--r--mpeglib/lib/mpegplay/mpegExtension.cpp258
-rw-r--r--mpeglib/lib/mpegplay/mpegExtension.h49
-rw-r--r--mpeglib/lib/mpegplay/mpegSystemHeader.cpp786
-rw-r--r--mpeglib/lib/mpegplay/mpegSystemHeader.h484
-rw-r--r--mpeglib/lib/mpegplay/mpegSystemStream.cpp235
-rw-r--r--mpeglib/lib/mpegplay/mpegSystemStream.h53
-rw-r--r--mpeglib/lib/mpegplay/mpegVideoBitWindow.cpp258
-rw-r--r--mpeglib/lib/mpegplay/mpegVideoBitWindow.h108
-rw-r--r--mpeglib/lib/mpegplay/mpegVideoHeader.cpp293
-rw-r--r--mpeglib/lib/mpegplay/mpegVideoHeader.h83
-rw-r--r--mpeglib/lib/mpegplay/mpegVideoLength.cpp424
-rw-r--r--mpeglib/lib/mpegplay/mpegVideoLength.h93
-rw-r--r--mpeglib/lib/mpegplay/mpegVideoStream.cpp224
-rw-r--r--mpeglib/lib/mpegplay/mpegVideoStream.h110
-rw-r--r--mpeglib/lib/mpegplay/pesSystemStream.cpp498
-rw-r--r--mpeglib/lib/mpegplay/pesSystemStream.h57
-rw-r--r--mpeglib/lib/mpegplay/picture.cpp149
-rw-r--r--mpeglib/lib/mpegplay/picture.h82
-rw-r--r--mpeglib/lib/mpegplay/proto.h36
-rw-r--r--mpeglib/lib/mpegplay/psSystemStream.cpp163
-rw-r--r--mpeglib/lib/mpegplay/psSystemStream.h57
-rw-r--r--mpeglib/lib/mpegplay/recon.cpp735
-rw-r--r--mpeglib/lib/mpegplay/recon.h55
-rw-r--r--mpeglib/lib/mpegplay/slice.cpp73
-rw-r--r--mpeglib/lib/mpegplay/slice.h48
-rw-r--r--mpeglib/lib/mpegplay/startCodes.h63
-rw-r--r--mpeglib/lib/mpegplay/tsSystemStream.cpp377
-rw-r--r--mpeglib/lib/mpegplay/tsSystemStream.h68
-rw-r--r--mpeglib/lib/mpegplay/videoDecoder.cpp476
-rw-r--r--mpeglib/lib/mpegplay/videoDecoder.h118
-rw-r--r--mpeglib/lib/mpgplayer/Makefile.am31
-rw-r--r--mpeglib/lib/mpgplayer/mpegStreamPlayer.cpp431
-rw-r--r--mpeglib/lib/mpgplayer/mpegStreamPlayer.h90
-rw-r--r--mpeglib/lib/oggvorbis/Makefile.am32
-rw-r--r--mpeglib/lib/oggvorbis/oggFrame.cpp29
-rw-r--r--mpeglib/lib/oggvorbis/oggFrame.h50
-rw-r--r--mpeglib/lib/oggvorbis/ovFramer.cpp128
-rw-r--r--mpeglib/lib/oggvorbis/ovFramer.h69
-rw-r--r--mpeglib/lib/oggvorbis/vorbisDecoder.cpp135
-rw-r--r--mpeglib/lib/oggvorbis/vorbisDecoder.h53
-rw-r--r--mpeglib/lib/oggvorbis/vorbisInfo.cpp150
-rw-r--r--mpeglib/lib/oggvorbis/vorbisInfo.h76
-rw-r--r--mpeglib/lib/output/Makefile.am52
-rw-r--r--mpeglib/lib/output/artsOutputStream.cpp205
-rw-r--r--mpeglib/lib/output/artsOutputStream.h123
-rw-r--r--mpeglib/lib/output/audioData.cpp100
-rw-r--r--mpeglib/lib/output/audioData.h71
-rw-r--r--mpeglib/lib/output/audioDataArray.cpp138
-rw-r--r--mpeglib/lib/output/audioDataArray.h55
-rw-r--r--mpeglib/lib/output/audioTime.cpp154
-rw-r--r--mpeglib/lib/output/audioTime.h68
-rw-r--r--mpeglib/lib/output/avSyncer.cpp386
-rw-r--r--mpeglib/lib/output/avSyncer.h96
-rw-r--r--mpeglib/lib/output/dspX11OutputStream.cpp236
-rw-r--r--mpeglib/lib/output/dspX11OutputStream.h89
-rw-r--r--mpeglib/lib/output/outPlugin.cpp71
-rw-r--r--mpeglib/lib/output/outPlugin.h44
-rw-r--r--mpeglib/lib/output/outputStream.cpp238
-rw-r--r--mpeglib/lib/output/outputStream.h151
-rw-r--r--mpeglib/lib/output/performance.cpp50
-rw-r--r--mpeglib/lib/output/performance.h34
-rw-r--r--mpeglib/lib/output/pluginInfo.cpp65
-rw-r--r--mpeglib/lib/output/pluginInfo.h48
-rw-r--r--mpeglib/lib/output/threadSafeOutputStream.cpp153
-rw-r--r--mpeglib/lib/output/threadSafeOutputStream.h67
-rw-r--r--mpeglib/lib/output/windowOut.cpp64
-rw-r--r--mpeglib/lib/output/windowOut.h53
-rw-r--r--mpeglib/lib/output/yuvDumper.cpp81
-rw-r--r--mpeglib/lib/output/yuvDumper.h51
-rw-r--r--mpeglib/lib/splay/Makefile.am53
-rw-r--r--mpeglib/lib/splay/attribute.h33
-rw-r--r--mpeglib/lib/splay/common.h60
-rw-r--r--mpeglib/lib/splay/dct.h33
-rw-r--r--mpeglib/lib/splay/dct36_12.cpp288
-rw-r--r--mpeglib/lib/splay/dct64.cpp202
-rw-r--r--mpeglib/lib/splay/dct64_down.cpp215
-rw-r--r--mpeglib/lib/splay/dump.cpp157
-rw-r--r--mpeglib/lib/splay/dump.h36
-rw-r--r--mpeglib/lib/splay/dxHead.cpp129
-rw-r--r--mpeglib/lib/splay/dxHead.h66
-rw-r--r--mpeglib/lib/splay/huffmanlookup.cpp120
-rw-r--r--mpeglib/lib/splay/huffmanlookup.h57
-rw-r--r--mpeglib/lib/splay/huffmantable.cpp584
-rw-r--r--mpeglib/lib/splay/mpeg2tables.h432
-rw-r--r--mpeglib/lib/splay/mpegAudioBitWindow.cpp41
-rw-r--r--mpeglib/lib/splay/mpegAudioBitWindow.h141
-rw-r--r--mpeglib/lib/splay/mpegAudioFrame.cpp200
-rw-r--r--mpeglib/lib/splay/mpegAudioFrame.h66
-rw-r--r--mpeglib/lib/splay/mpegAudioHeader.cpp268
-rw-r--r--mpeglib/lib/splay/mpegAudioHeader.h101
-rw-r--r--mpeglib/lib/splay/mpegAudioInfo.cpp262
-rw-r--r--mpeglib/lib/splay/mpegAudioInfo.h95
-rw-r--r--mpeglib/lib/splay/mpegAudioStream.cpp43
-rw-r--r--mpeglib/lib/splay/mpegAudioStream.h139
-rw-r--r--mpeglib/lib/splay/mpeglayer1.cpp109
-rw-r--r--mpeglib/lib/splay/mpeglayer2.cpp449
-rw-r--r--mpeglib/lib/splay/mpeglayer3.cpp1761
-rw-r--r--mpeglib/lib/splay/mpegsound.h248
-rw-r--r--mpeglib/lib/splay/mpegtable.cpp35
-rw-r--r--mpeglib/lib/splay/mpegtoraw.cpp127
-rw-r--r--mpeglib/lib/splay/op.h96
-rw-r--r--mpeglib/lib/splay/sigsev.c29
-rw-r--r--mpeglib/lib/splay/splayDecoder.cpp73
-rw-r--r--mpeglib/lib/splay/splayDecoder.h70
-rw-r--r--mpeglib/lib/splay/synth_Down.cpp231
-rw-r--r--mpeglib/lib/splay/synth_Std.cpp330
-rw-r--r--mpeglib/lib/splay/synth_filter.cpp147
-rw-r--r--mpeglib/lib/splay/synthesis.cpp73
-rw-r--r--mpeglib/lib/splay/synthesis.h95
-rw-r--r--mpeglib/lib/splay/window.cpp70
-rw-r--r--mpeglib/lib/tplay/CHANGES154
-rw-r--r--mpeglib/lib/tplay/Makefile.am15
-rw-r--r--mpeglib/lib/tplay/README95
-rw-r--r--mpeglib/lib/tplay/au.cpp100
-rw-r--r--mpeglib/lib/tplay/tplayfunctions.cpp84
-rw-r--r--mpeglib/lib/tplay/tplayfunctions.h128
-rw-r--r--mpeglib/lib/tplay/wav.cpp91
-rw-r--r--mpeglib/lib/util/Makefile.am61
-rw-r--r--mpeglib/lib/util/abstract/Makefile.am14
-rw-r--r--mpeglib/lib/util/abstract/abs_thread.h123
-rw-r--r--mpeglib/lib/util/abstract/abs_thread_sdl.cpp89
-rw-r--r--mpeglib/lib/util/abstract/threadQueue.cpp108
-rw-r--r--mpeglib/lib/util/abstract/threadQueue.h74
-rw-r--r--mpeglib/lib/util/audio/Makefile.am18
-rw-r--r--mpeglib/lib/util/audio/audioIO.cpp49
-rw-r--r--mpeglib/lib/util/audio/audioIO.h80
-rw-r--r--mpeglib/lib/util/audio/audioIO_AIX.cpp533
-rw-r--r--mpeglib/lib/util/audio/audioIO_BeOS.cpp227
-rw-r--r--mpeglib/lib/util/audio/audioIO_HPUX.cpp190
-rw-r--r--mpeglib/lib/util/audio/audioIO_IRIX.cpp157
-rw-r--r--mpeglib/lib/util/audio/audioIO_Linux.cpp220
-rw-r--r--mpeglib/lib/util/audio/audioIO_SDL.cpp164
-rw-r--r--mpeglib/lib/util/audio/audioIO_SunOS.cpp167
-rw-r--r--mpeglib/lib/util/audio/dspWrapper.cpp193
-rw-r--r--mpeglib/lib/util/audio/dspWrapper.h75
-rw-r--r--mpeglib/lib/util/dynBuffer.cpp166
-rw-r--r--mpeglib/lib/util/dynBuffer.h63
-rw-r--r--mpeglib/lib/util/file/Makefile.am25
-rw-r--r--mpeglib/lib/util/file/fileAccess.cpp95
-rw-r--r--mpeglib/lib/util/file/fileAccess.h46
-rw-r--r--mpeglib/lib/util/mmx/Makefile.am46
-rw-r--r--mpeglib/lib/util/mmx/attribute.h33
-rw-r--r--mpeglib/lib/util/mmx/cpu_accel.c162
-rw-r--r--mpeglib/lib/util/mmx/mm_accel.h58
-rw-r--r--mpeglib/lib/util/mmx/mmx.c73
-rw-r--r--mpeglib/lib/util/mmx/mmx.h26
-rw-r--r--mpeglib/lib/util/mmx/mmx_asm.h258
-rw-r--r--mpeglib/lib/util/render/Makefile.am59
-rw-r--r--mpeglib/lib/util/render/dither/Makefile.am40
-rw-r--r--mpeglib/lib/util/render/dither/colorTable8Bit.cpp147
-rw-r--r--mpeglib/lib/util/render/dither/colorTable8Bit.h57
-rw-r--r--mpeglib/lib/util/render/dither/colorTableHighBit.cpp248
-rw-r--r--mpeglib/lib/util/render/dither/colorTableHighBit.h73
-rw-r--r--mpeglib/lib/util/render/dither/dither16Bit.cpp300
-rw-r--r--mpeglib/lib/util/render/dither/dither16Bit.h55
-rw-r--r--mpeglib/lib/util/render/dither/dither32Bit.cpp253
-rw-r--r--mpeglib/lib/util/render/dither/dither32Bit.h55
-rw-r--r--mpeglib/lib/util/render/dither/dither32mmx.cpp272
-rw-r--r--mpeglib/lib/util/render/dither/dither8Bit.cpp306
-rw-r--r--mpeglib/lib/util/render/dither/dither8Bit.h63
-rw-r--r--mpeglib/lib/util/render/dither/ditherDef.h100
-rw-r--r--mpeglib/lib/util/render/dither/ditherMMX.h38
-rw-r--r--mpeglib/lib/util/render/dither/ditherRGB.cpp230
-rw-r--r--mpeglib/lib/util/render/dither/ditherRGB.h45
-rw-r--r--mpeglib/lib/util/render/dither/ditherRGB_flipped.cpp82
-rw-r--r--mpeglib/lib/util/render/dither/ditherRGB_flipped.h34
-rw-r--r--mpeglib/lib/util/render/dither/ditherWrapper.cpp246
-rw-r--r--mpeglib/lib/util/render/dither/ditherWrapper.h80
-rw-r--r--mpeglib/lib/util/render/dither/ditherer_mmx16.cpp256
-rw-r--r--mpeglib/lib/util/render/dither2YUV/Makefile.am22
-rw-r--r--mpeglib/lib/util/render/dither2YUV/README13
-rw-r--r--mpeglib/lib/util/render/dither2YUV/dither2YUV.cpp124
-rw-r--r--mpeglib/lib/util/render/dither2YUV/dither2YUV.h64
-rw-r--r--mpeglib/lib/util/render/dither2YUV/rgb2yuv16.cpp916
-rw-r--r--mpeglib/lib/util/render/dither2YUV/rgb2yuv16.h74
-rw-r--r--mpeglib/lib/util/render/dither2YUV/rgb2yuv32.cpp1143
-rw-r--r--mpeglib/lib/util/render/dither2YUV/rgb2yuv32.h93
-rw-r--r--mpeglib/lib/util/render/dither2YUV/rgb2yuvdefs.h74
-rw-r--r--mpeglib/lib/util/render/imageBase.cpp76
-rw-r--r--mpeglib/lib/util/render/imageBase.h140
-rw-r--r--mpeglib/lib/util/render/pictureArray.cpp101
-rw-r--r--mpeglib/lib/util/render/pictureArray.h76
-rw-r--r--mpeglib/lib/util/render/renderMachine.cpp205
-rw-r--r--mpeglib/lib/util/render/renderMachine.h90
-rw-r--r--mpeglib/lib/util/render/sdl/Makefile.am43
-rw-r--r--mpeglib/lib/util/render/sdl/imageDeskSDL.cpp110
-rw-r--r--mpeglib/lib/util/render/sdl/imageDeskSDL.h65
-rw-r--r--mpeglib/lib/util/render/sdl/sdlSurface.cpp219
-rw-r--r--mpeglib/lib/util/render/sdl/sdlSurface.h78
-rw-r--r--mpeglib/lib/util/render/surface.cpp117
-rw-r--r--mpeglib/lib/util/render/surface.h55
-rw-r--r--mpeglib/lib/util/render/x11/Makefile.am48
-rw-r--r--mpeglib/lib/util/render/x11/imageDGAFull.cpp289
-rw-r--r--mpeglib/lib/util/render/x11/imageDGAFull.h131
-rw-r--r--mpeglib/lib/util/render/x11/imageDeskX11.cpp439
-rw-r--r--mpeglib/lib/util/render/x11/imageDeskX11.h85
-rw-r--r--mpeglib/lib/util/render/x11/imageXVDesk.cpp405
-rw-r--r--mpeglib/lib/util/render/x11/imageXVDesk.h88
-rw-r--r--mpeglib/lib/util/render/x11/initDisplay.cpp255
-rw-r--r--mpeglib/lib/util/render/x11/initDisplay.h34
-rw-r--r--mpeglib/lib/util/render/x11/x11Surface.cpp389
-rw-r--r--mpeglib/lib/util/render/x11/x11Surface.h79
-rw-r--r--mpeglib/lib/util/render/x11/xinit.h99
-rw-r--r--mpeglib/lib/util/render/yuvPicture.cpp253
-rw-r--r--mpeglib/lib/util/render/yuvPicture.h110
-rw-r--r--mpeglib/lib/util/syncClock.cpp59
-rw-r--r--mpeglib/lib/util/syncClock.h61
-rw-r--r--mpeglib/lib/util/syncClockMPEG.cpp221
-rw-r--r--mpeglib/lib/util/syncClockMPEG.h77
-rw-r--r--mpeglib/lib/util/timeStamp.cpp273
-rw-r--r--mpeglib/lib/util/timeStamp.h92
-rw-r--r--mpeglib/lib/util/timeStampArray.cpp178
-rw-r--r--mpeglib/lib/util/timeStampArray.h85
-rw-r--r--mpeglib/lib/util/timeWrapper.cpp77
-rw-r--r--mpeglib/lib/util/timeWrapper.h44
-rw-r--r--mpeglib/lib/yuv/Makefile.am28
-rw-r--r--mpeglib/lib/yuv/yuvPlugin.cpp158
-rw-r--r--mpeglib/lib/yuv/yuvPlugin.h47
-rw-r--r--mpeglib_artsplug/CDDAPlayObject.mcopclass7
-rw-r--r--mpeglib_artsplug/MP3PlayObject.mcopclass8
-rw-r--r--mpeglib_artsplug/MPGPlayObject.mcopclass8
-rw-r--r--mpeglib_artsplug/Makefile.am106
-rw-r--r--mpeglib_artsplug/NULLPlayObject.mcopclass7
-rw-r--r--mpeglib_artsplug/OGGPlayObject.mcopclass8
-rw-r--r--mpeglib_artsplug/README78
-rw-r--r--mpeglib_artsplug/SplayPlayObject.mcopclass8
-rw-r--r--mpeglib_artsplug/TODO19
-rw-r--r--mpeglib_artsplug/VCDPlayObject.mcopclass7
-rw-r--r--mpeglib_artsplug/WAVPlayObject.mcopclass7
-rw-r--r--mpeglib_artsplug/cddaPlayObject_impl.cpp48
-rw-r--r--mpeglib_artsplug/cddaPlayObject_impl.h43
-rw-r--r--mpeglib_artsplug/configure.in.in3
-rw-r--r--mpeglib_artsplug/decoderBaseObject.idl58
-rw-r--r--mpeglib_artsplug/decoderBaseObject_impl.cpp620
-rw-r--r--mpeglib_artsplug/decoderBaseObject_impl.h121
-rw-r--r--mpeglib_artsplug/doemacs29
-rw-r--r--mpeglib_artsplug/mp3PlayObject_impl.cpp45
-rw-r--r--mpeglib_artsplug/mp3PlayObject_impl.h43
-rw-r--r--mpeglib_artsplug/mpeglibartsplay.cpp276
-rw-r--r--mpeglib_artsplug/mpgPlayObject_impl.cpp53
-rw-r--r--mpeglib_artsplug/mpgPlayObject_impl.h48
-rw-r--r--mpeglib_artsplug/nullPlayObject_impl.cpp120
-rw-r--r--mpeglib_artsplug/nullPlayObject_impl.h75
-rw-r--r--mpeglib_artsplug/oggPlayObject_impl.cpp43
-rw-r--r--mpeglib_artsplug/oggPlayObject_impl.h47
-rw-r--r--mpeglib_artsplug/splayPlayObject.idl15
-rw-r--r--mpeglib_artsplug/splayPlayObject_impl.cpp500
-rw-r--r--mpeglib_artsplug/splayPlayObject_impl.h112
-rw-r--r--mpeglib_artsplug/vcdPlayObject_impl.cpp47
-rw-r--r--mpeglib_artsplug/vcdPlayObject_impl.h44
-rw-r--r--mpeglib_artsplug/wavPlayObject_impl.cpp39
-rw-r--r--mpeglib_artsplug/wavPlayObject_impl.h44
-rw-r--r--mpg123_artsplugin/Makefile.am25
-rwxr-xr-xmpg123_artsplugin/compare_to_original.sh3
-rw-r--r--mpg123_artsplugin/configure.in.in92
-rw-r--r--mpg123_artsplugin/distexclude24
-rw-r--r--mpg123_artsplugin/dxhead.c250
-rw-r--r--mpg123_artsplugin/dxhead.h77
-rw-r--r--mpg123_artsplugin/mpg123/Makefile.am34
-rw-r--r--mpg123_artsplugin/mpg123/README42
-rw-r--r--mpg123_artsplugin/mpg123/audio.c298
-rw-r--r--mpg123_artsplugin/mpg123/audio.h106
-rw-r--r--mpg123_artsplugin/mpg123/buffer.c245
-rw-r--r--mpg123_artsplugin/mpg123/buffer.h13
-rw-r--r--mpg123_artsplugin/mpg123/common.c921
-rw-r--r--mpg123_artsplugin/mpg123/common.h19
-rw-r--r--mpg123_artsplugin/mpg123/dct36_3dnow.s499
-rw-r--r--mpg123_artsplugin/mpg123/dct64.c168
-rw-r--r--mpg123_artsplugin/mpg123/dct64_3dnow.s706
-rw-r--r--mpg123_artsplugin/mpg123/dct64_MMX.s836
-rw-r--r--mpg123_artsplugin/mpg123/dct64_i386.c329
-rw-r--r--mpg123_artsplugin/mpg123/dct64_i486-a.s831
-rw-r--r--mpg123_artsplugin/mpg123/dct64_i486-b.c140
-rw-r--r--mpg123_artsplugin/mpg123/dct64_i486.c322
-rw-r--r--mpg123_artsplugin/mpg123/decode.c223
-rw-r--r--mpg123_artsplugin/mpg123/decode_2to1.c233
-rw-r--r--mpg123_artsplugin/mpg123/decode_3dnow.s279
-rw-r--r--mpg123_artsplugin/mpg123/decode_4to1.c240
-rw-r--r--mpg123_artsplugin/mpg123/decode_MMX.s108
-rw-r--r--mpg123_artsplugin/mpg123/decode_i386.c257
-rw-r--r--mpg123_artsplugin/mpg123/decode_i486.c250
-rw-r--r--mpg123_artsplugin/mpg123/decode_i586.s323
-rw-r--r--mpg123_artsplugin/mpg123/decode_ntom.c290
-rw-r--r--mpg123_artsplugin/mpg123/genre.h263
-rw-r--r--mpg123_artsplugin/mpg123/getbits.c116
-rw-r--r--mpg123_artsplugin/mpg123/getbits.h33
-rw-r--r--mpg123_artsplugin/mpg123/httpget.c481
-rw-r--r--mpg123_artsplugin/mpg123/huffman.h332
-rw-r--r--mpg123_artsplugin/mpg123/l2tables.h154
-rw-r--r--mpg123_artsplugin/mpg123/layer1.c157
-rw-r--r--mpg123_artsplugin/mpg123/layer2.c318
-rw-r--r--mpg123_artsplugin/mpg123/layer3.c1880
-rw-r--r--mpg123_artsplugin/mpg123/mpg123.h465
-rw-r--r--mpg123_artsplugin/mpg123/readers.c618
-rw-r--r--mpg123_artsplugin/mpg123/tabinit.c139
-rw-r--r--mpg123_artsplugin/mpg123/tabinit_MMX.s160
-rw-r--r--mpg123_artsplugin/mpg123/vbrhead.c79
-rw-r--r--mpg123_artsplugin/mpg123/xfermem.c275
-rw-r--r--mpg123_artsplugin/mpg123/xfermem.h60
-rw-r--r--mpg123_artsplugin/mpg123PlayObject.mcopclass6
-rw-r--r--mpg123_artsplugin/mpg123PlayObject_impl.cpp675
-rw-r--r--mpg123_artsplugin/mpg123PlayObject_impl.h111
-rw-r--r--mpg123_artsplugin/mpg123arts.idl12
-rw-r--r--noatun/CHANGES4
-rw-r--r--noatun/COPYING26
-rw-r--r--noatun/FILES13
-rw-r--r--noatun/Makefile.am29
-rw-r--r--noatun/TODO15
-rw-r--r--noatun/app/Makefile.am11
-rw-r--r--noatun/app/main.cpp47
-rw-r--r--noatun/configure.in.in15
-rw-r--r--noatun/hi128-app-noatun.pngbin0 -> 17465 bytes
-rw-r--r--noatun/hi16-app-noatun.pngbin0 -> 964 bytes
-rw-r--r--noatun/hi22-app-noatun.pngbin0 -> 1633 bytes
-rw-r--r--noatun/hi32-app-noatun.pngbin0 -> 2767 bytes
-rw-r--r--noatun/hi48-app-noatun.pngbin0 -> 5527 bytes
-rw-r--r--noatun/hi64-app-noatun.pngbin0 -> 8005 bytes
-rw-r--r--noatun/library/Makefile.am57
-rw-r--r--noatun/library/app.cpp530
-rw-r--r--noatun/library/cmodule.cpp131
-rw-r--r--noatun/library/cmodule.h43
-rw-r--r--noatun/library/controls.cpp114
-rw-r--r--noatun/library/conversion.cpp149
-rw-r--r--noatun/library/downloader.cpp236
-rw-r--r--noatun/library/effects.cpp285
-rw-r--r--noatun/library/effectview.cpp284
-rw-r--r--noatun/library/effectview.h83
-rw-r--r--noatun/library/engine.cpp589
-rw-r--r--noatun/library/equalizer.cpp339
-rw-r--r--noatun/library/equalizerview.cpp325
-rw-r--r--noatun/library/equalizerview.h91
-rw-r--r--noatun/library/equalizerwidget.ui337
-rw-r--r--noatun/library/gentable.cpp1037
-rw-r--r--noatun/library/globalvideo.h26
-rw-r--r--noatun/library/ksaver.cpp184
-rw-r--r--noatun/library/ksaver.h102
-rw-r--r--noatun/library/mimetypetree.cpp64
-rw-r--r--noatun/library/mimetypetree.h35
-rw-r--r--noatun/library/noatun/Makefile.am10
-rw-r--r--noatun/library/noatun/app.h271
-rw-r--r--noatun/library/noatun/controls.h79
-rw-r--r--noatun/library/noatun/conversion.h117
-rw-r--r--noatun/library/noatun/downloader.h120
-rw-r--r--noatun/library/noatun/effects.h186
-rw-r--r--noatun/library/noatun/engine.h120
-rw-r--r--noatun/library/noatun/equalizer.h238
-rw-r--r--noatun/library/noatun/mocs.cpp37
-rw-r--r--noatun/library/noatun/player.h270
-rw-r--r--noatun/library/noatun/playlist.h531
-rw-r--r--noatun/library/noatun/playlistsaver.h102
-rw-r--r--noatun/library/noatun/plugin.h467
-rw-r--r--noatun/library/noatun/pluginloader.h99
-rw-r--r--noatun/library/noatun/pref.h91
-rw-r--r--noatun/library/noatun/scrollinglabel.h78
-rw-r--r--noatun/library/noatun/stdaction.h204
-rw-r--r--noatun/library/noatun/stereobuttonaction.h31
-rw-r--r--noatun/library/noatun/vequalizer.h455
-rw-r--r--noatun/library/noatun/video.h60
-rw-r--r--noatun/library/noatunarts/Equalizer.mcopclass4
-rw-r--r--noatun/library/noatunarts/EqualizerSSE.mcopclass4
-rw-r--r--noatun/library/noatunarts/Equalizer_impl.cpp472
-rw-r--r--noatun/library/noatunarts/FFTScope.mcopclass4
-rw-r--r--noatun/library/noatunarts/FFTScopeStereo.mcopclass4
-rw-r--r--noatun/library/noatunarts/FFTScopes.cpp422
-rw-r--r--noatun/library/noatunarts/Listener.mcopclass4
-rw-r--r--noatun/library/noatunarts/Makefile.am33
-rw-r--r--noatun/library/noatunarts/RawScope.mcopclass4
-rw-r--r--noatun/library/noatunarts/RawScopeStereo.mcopclass4
-rw-r--r--noatun/library/noatunarts/Session.mcopclass4
-rw-r--r--noatun/library/noatunarts/Session_impl.cpp82
-rw-r--r--noatun/library/noatunarts/StereoEffectStack.mcopclass4
-rw-r--r--noatun/library/noatunarts/StereoEffectStack_impl.cpp253
-rw-r--r--noatun/library/noatunarts/StereoVolumeControl.mcopclass4
-rw-r--r--noatun/library/noatunarts/StereoVolumeControlSSE.mcopclass4
-rw-r--r--noatun/library/noatunarts/StereoVolumeControl_impl.cpp181
-rw-r--r--noatun/library/noatunarts/fft.c384
-rw-r--r--noatun/library/noatunarts/fft.h88
-rw-r--r--noatun/library/noatunarts/noatunarts.idl91
-rw-r--r--noatun/library/noatunlistview.h5
-rw-r--r--noatun/library/noatunstdaction.cpp362
-rw-r--r--noatun/library/noatuntags/Makefile.am14
-rw-r--r--noatun/library/noatuntags/tags.cpp229
-rw-r--r--noatun/library/noatuntags/tags.h35
-rw-r--r--noatun/library/noatuntags/tagsgetter.h56
-rw-r--r--noatun/library/noatunui.cpp12
-rw-r--r--noatun/library/player.cpp379
-rw-r--r--noatun/library/playlist.cpp442
-rw-r--r--noatun/library/playlistsaver.cpp771
-rw-r--r--noatun/library/plugin.cpp578
-rw-r--r--noatun/library/plugin_deps.h44
-rw-r--r--noatun/library/pluginloader.cpp366
-rw-r--r--noatun/library/pluginmodule.cpp406
-rw-r--r--noatun/library/pluginmodule.h100
-rw-r--r--noatun/library/pref.cpp95
-rw-r--r--noatun/library/scrollinglabel.cpp196
-rw-r--r--noatun/library/spline.cpp194
-rw-r--r--noatun/library/spline.h74
-rw-r--r--noatun/library/stereobuttonaction.cpp47
-rw-r--r--noatun/library/titleproxy.cpp376
-rw-r--r--noatun/library/titleproxy.h129
-rw-r--r--noatun/library/vequalizer.cpp936
-rw-r--r--noatun/library/video.cpp162
-rw-r--r--noatun/modules/Makefile.am4
-rw-r--r--noatun/modules/artseffects/ExtraStereo.mcopclass5
-rw-r--r--noatun/modules/artseffects/ExtraStereoGuiFactory.mcopclass5
-rw-r--r--noatun/modules/artseffects/Makefile.am25
-rw-r--r--noatun/modules/artseffects/RawWriter.mcopclass4
-rw-r--r--noatun/modules/artseffects/VoiceRemoval.mcopclass5
-rw-r--r--noatun/modules/artseffects/artseffects.idl17
-rw-r--r--noatun/modules/artseffects/effect.cpp1
-rw-r--r--noatun/modules/artseffects/extrastereo_impl.cc153
-rw-r--r--noatun/modules/artseffects/extrastereogui_impl.cc28
-rw-r--r--noatun/modules/artseffects/extrastereogui_impl.h26
-rw-r--r--noatun/modules/dcopiface/Makefile.am16
-rw-r--r--noatun/modules/dcopiface/dcopiface.cpp250
-rw-r--r--noatun/modules/dcopiface/dcopiface.h104
-rw-r--r--noatun/modules/dcopiface/dcopiface.plugin137
-rw-r--r--noatun/modules/excellent/Makefile.am18
-rw-r--r--noatun/modules/excellent/excellent.plugin120
-rw-r--r--noatun/modules/excellent/excellentui.rc45
-rw-r--r--noatun/modules/excellent/noatunui.cpp32
-rw-r--r--noatun/modules/excellent/userinterface.cpp394
-rw-r--r--noatun/modules/excellent/userinterface.h101
-rw-r--r--noatun/modules/htmlexport/Makefile.am16
-rw-r--r--noatun/modules/htmlexport/TODO3
-rw-r--r--noatun/modules/htmlexport/htmlexport.cpp308
-rw-r--r--noatun/modules/htmlexport/htmlexport.h89
-rw-r--r--noatun/modules/htmlexport/htmlexport.plugin124
-rw-r--r--noatun/modules/infrared/Makefile.am16
-rw-r--r--noatun/modules/infrared/README3
-rw-r--r--noatun/modules/infrared/infrared.cpp120
-rw-r--r--noatun/modules/infrared/infrared.h30
-rw-r--r--noatun/modules/infrared/infrared.plugin120
-rw-r--r--noatun/modules/infrared/irprefs.cpp311
-rw-r--r--noatun/modules/infrared/irprefs.h62
-rw-r--r--noatun/modules/infrared/lirc.cpp173
-rw-r--r--noatun/modules/infrared/lirc.h75
-rw-r--r--noatun/modules/kaiman/Makefile.am23
-rw-r--r--noatun/modules/kaiman/README3
-rw-r--r--noatun/modules/kaiman/SKIN-SPECS518
-rw-r--r--noatun/modules/kaiman/kaiman.plugin132
-rw-r--r--noatun/modules/kaiman/kaimanui.rc45
-rw-r--r--noatun/modules/kaiman/noatunui.cpp9
-rw-r--r--noatun/modules/kaiman/pref.cpp122
-rw-r--r--noatun/modules/kaiman/pref.h48
-rw-r--r--noatun/modules/kaiman/skins/Makefile.am3
-rw-r--r--noatun/modules/kaiman/skins/car-preset/Makefile.am10
-rw-r--r--noatun/modules/kaiman/skins/car-preset/README22
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_exit.pngbin0 -> 674 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_iconify.pngbin0 -> 585 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_list.pngbin0 -> 762 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_next.pngbin0 -> 1055 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_p1.pngbin0 -> 691 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_p2.pngbin0 -> 679 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_p3.pngbin0 -> 689 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_p4.pngbin0 -> 641 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_p5.pngbin0 -> 668 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_p6.pngbin0 -> 741 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_play.pngbin0 -> 623 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_prev.pngbin0 -> 1006 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_sml.pngbin0 -> 454 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_stop.pngbin0 -> 603 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_voldn.pngbin0 -> 923 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_volup.pngbin0 -> 1026 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/digbig.pngbin0 -> 359 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/digmed.pngbin0 -> 318 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/letters.pngbin0 -> 1240 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/main.pngbin0 -> 7191 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/monoster.pngbin0 -> 169 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/posbar.pngbin0 -> 519 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/random.pngbin0 -> 178 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/repeat.pngbin0 -> 172 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/skindata71
-rw-r--r--noatun/modules/kaiman/skins/car-preset/status.pngbin0 -> 133 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/volume.pngbin0 -> 664 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/Makefile.am13
-rw-r--r--noatun/modules/kaiman/skins/circle/README22
-rw-r--r--noatun/modules/kaiman/skins/circle/back.pngbin0 -> 20249 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/back_mask.pngbin0 -> 1519 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/back_sm.pngbin0 -> 2166 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/back_sm_mask.pngbin0 -> 217 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/bar_pos.pngbin0 -> 1188 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/bar_vol.pngbin0 -> 1156 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_exit.pngbin0 -> 2326 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_iconify.pngbin0 -> 1899 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_list.pngbin0 -> 2525 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_mode.pngbin0 -> 2026 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_next.pngbin0 -> 2527 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_play.pngbin0 -> 3081 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_pref.pngbin0 -> 2610 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_prev.pngbin0 -> 2411 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_repeat.pngbin0 -> 2488 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_shuffle.pngbin0 -> 2504 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_sm_exit.pngbin0 -> 680 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_sm_iconify.pngbin0 -> 627 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_sm_mode.pngbin0 -> 852 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_sm_next.pngbin0 -> 759 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_sm_play.pngbin0 -> 1004 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_sm_prev.pngbin0 -> 776 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_sm_stop.pngbin0 -> 1022 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_stop.pngbin0 -> 3186 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/dig.pngbin0 -> 370 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/digsml.pngbin0 -> 201 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/letters.pngbin0 -> 1260 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/skindata58
-rw-r--r--noatun/modules/kaiman/skins/circle/skindata_alt44
-rw-r--r--noatun/modules/kaiman/skins/circle/status.pngbin0 -> 121 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/Makefile.am11
-rw-r--r--noatun/modules/kaiman/skins/k9/README24
-rw-r--r--noatun/modules/kaiman/skins/k9/conf.jpgbin0 -> 771 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/conf.pngbin0 -> 2436 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/eject.jpgbin0 -> 6065 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/icon.jpgbin0 -> 569 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/icon.pngbin0 -> 1427 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/kill.jpgbin0 -> 536 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/kill.pngbin0 -> 1221 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/knine-nfont.jpgbin0 -> 1747 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/knine-nfont.pngbin0 -> 1226 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/knine-nfont2.jpgbin0 -> 1721 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/knine-nfont2.pngbin0 -> 1320 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/knine-normal2.jpgbin0 -> 17471 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/knine-normal2.pngbin0 -> 72057 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/knine-vfont.jpgbin0 -> 730 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/knine-vfont.pngbin0 -> 351 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/long2.jpgbin0 -> 146555 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/mask.pngbin0 -> 3865 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/newtext.jpgbin0 -> 3196 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/newtext.pngbin0 -> 3135 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/next.jpgbin0 -> 6840 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/pause.jpgbin0 -> 7683 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/play.jpgbin0 -> 8850 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/pos_item.jpgbin0 -> 166210 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/repeat.jpgbin0 -> 756 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/repeat.pngbin0 -> 1299 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/reverse.jpgbin0 -> 6904 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/shuffle.jpgbin0 -> 719 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/shuffle.pngbin0 -> 1268 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/skindata73
-rw-r--r--noatun/modules/kaiman/skins/k9/small-k.jpgbin0 -> 516 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/small-k.pngbin0 -> 1009 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/square.jpgbin0 -> 720 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/square.pngbin0 -> 1894 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/status.jpgbin0 -> 461 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/status.pngbin0 -> 246 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/stop.jpgbin0 -> 7743 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/trans-pos.pngbin0 -> 99 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/trans-slide.pngbin0 -> 105 bytes
-rw-r--r--noatun/modules/kaiman/style.cpp1504
-rw-r--r--noatun/modules/kaiman/style.h356
-rw-r--r--noatun/modules/kaiman/userinterface.cpp562
-rw-r--r--noatun/modules/kaiman/userinterface.h85
-rw-r--r--noatun/modules/keyz/Makefile.am14
-rw-r--r--noatun/modules/keyz/keyz.cpp189
-rw-r--r--noatun/modules/keyz/keyz.h48
-rw-r--r--noatun/modules/keyz/keyz.plugin69
-rw-r--r--noatun/modules/kjofol-skin/ChangeLog111
-rw-r--r--noatun/modules/kjofol-skin/Makefile.am41
-rw-r--r--noatun/modules/kjofol-skin/helpers.cpp64
-rw-r--r--noatun/modules/kjofol-skin/kjbackground.cpp29
-rw-r--r--noatun/modules/kjofol-skin/kjbackground.h21
-rw-r--r--noatun/modules/kjofol-skin/kjbutton.cpp301
-rw-r--r--noatun/modules/kjofol-skin/kjbutton.h34
-rw-r--r--noatun/modules/kjofol-skin/kjequalizer.cpp129
-rw-r--r--noatun/modules/kjofol-skin/kjequalizer.h39
-rw-r--r--noatun/modules/kjofol-skin/kjfont.cpp290
-rw-r--r--noatun/modules/kjofol-skin/kjfont.h50
-rw-r--r--noatun/modules/kjofol-skin/kjguisettingswidget.ui465
-rw-r--r--noatun/modules/kjofol-skin/kjloader.cpp832
-rw-r--r--noatun/modules/kjofol-skin/kjloader.h129
-rw-r--r--noatun/modules/kjofol-skin/kjofolui.plugin64
-rw-r--r--noatun/modules/kjofol-skin/kjprefs.cpp658
-rw-r--r--noatun/modules/kjofol-skin/kjprefs.h96
-rw-r--r--noatun/modules/kjofol-skin/kjseeker.cpp210
-rw-r--r--noatun/modules/kjofol-skin/kjseeker.h37
-rw-r--r--noatun/modules/kjofol-skin/kjskinselectorwidget.ui227
-rw-r--r--noatun/modules/kjofol-skin/kjsliders.cpp336
-rw-r--r--noatun/modules/kjofol-skin/kjsliders.h88
-rw-r--r--noatun/modules/kjofol-skin/kjtextdisplay.cpp650
-rw-r--r--noatun/modules/kjofol-skin/kjtextdisplay.h139
-rw-r--r--noatun/modules/kjofol-skin/kjvis.cpp538
-rw-r--r--noatun/modules/kjofol-skin/kjvis.h102
-rw-r--r--noatun/modules/kjofol-skin/kjwidget.cpp70
-rw-r--r--noatun/modules/kjofol-skin/kjwidget.h53
-rw-r--r--noatun/modules/kjofol-skin/noatunui.cpp9
-rw-r--r--noatun/modules/kjofol-skin/parser.cpp132
-rw-r--r--noatun/modules/kjofol-skin/parser.h49
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/HexoBronx.rc77
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/Makefile.am7
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/README.txt79
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/active.pngbin0 -> 113726 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/eckig_font.pngbin0 -> 272 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/font.pngbin0 -> 1299 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/inactive.pngbin0 -> 109729 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/mask.pngbin0 -> 3416 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/pitch.pngbin0 -> 22516 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/splash.pngbin0 -> 94261 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/time_font.pngbin0 -> 395 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/volume.pngbin0 -> 24291 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/volume_pitch_font.pngbin0 -> 355 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/Makefile.am3
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/Makefile.am11
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/kjofol.dck62
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/kjofol.pl39
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/kjofol.rc150
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/kjofol.wsh63
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sg.pngbin0 -> 29510 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sg_num.pngbin0 -> 1497 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sg_seek.bmpbin0 -> 218334 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sg_seek.pngbin0 -> 8170 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sg_text.pngbin0 -> 2093 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgdock.pngbin0 -> 5552 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgdock2.pngbin0 -> 5455 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgdocksk.pngbin0 -> 1236 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgdockvp.pngbin0 -> 1234 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgeq.pngbin0 -> 1236 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgpitch.pngbin0 -> 7552 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgpitchp.pngbin0 -> 5469 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgplist.pngbin0 -> 25049 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgplist2.pngbin0 -> 24538 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgpres1.pngbin0 -> 29852 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgpres2.pngbin0 -> 29568 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgpres3.pngbin0 -> 29962 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgvol.pngbin0 -> 17818 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgvolnum.pngbin0 -> 1290 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgvolpos.pngbin0 -> 4589 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgwshad.pngbin0 -> 7219 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgwshad2.pngbin0 -> 7270 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgwshdsk.pngbin0 -> 710 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgwshvol.pngbin0 -> 519 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgwshvp.pngbin0 -> 709 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/Makefile.am8
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/p_eq.pngbin0 -> 131 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/p_main.pngbin0 -> 53187 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/p_mainback.pngbin0 -> 56216 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/p_numbers.pngbin0 -> 189 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/p_playback.pngbin0 -> 30106 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/p_playlist.pngbin0 -> 29702 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/p_propos.pngbin0 -> 3363 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/p_text.pngbin0 -> 477 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/p_volbar.pngbin0 -> 49460 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/p_volpos.pngbin0 -> 2325 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/phong.dck26
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/phong.rc104
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/phong.wsh26
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/phong_readme.txt62
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/Makefile.am8
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/i_base.pngbin0 -> 65392 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/i_base2.pngbin0 -> 70854 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/i_eq.pngbin0 -> 165 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/i_font.pngbin0 -> 308 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/i_pl.pngbin0 -> 66544 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/i_pl2.pngbin0 -> 66435 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/i_pro.pngbin0 -> 676 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/i_text.pngbin0 -> 379 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/i_vol.pngbin0 -> 13273 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/i_volpos.pngbin0 -> 2550 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.dck26
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.rc105
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.wsh26
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric_readme.txt19
-rw-r--r--noatun/modules/making_plugins23
-rw-r--r--noatun/modules/marquis/Makefile.am15
-rw-r--r--noatun/modules/marquis/marquis.cpp186
-rw-r--r--noatun/modules/marquis/marquis.h52
-rw-r--r--noatun/modules/marquis/marquis.plugin83
-rw-r--r--noatun/modules/marquis/plugin.cpp9
-rw-r--r--noatun/modules/metatag/HANDLED_ITEMS21
-rw-r--r--noatun/modules/metatag/Makefile.am15
-rw-r--r--noatun/modules/metatag/edit.cpp312
-rw-r--r--noatun/modules/metatag/edit.h55
-rw-r--r--noatun/modules/metatag/metatag.cpp124
-rw-r--r--noatun/modules/metatag/metatag.h30
-rw-r--r--noatun/modules/metatag/metatag.plugin125
-rw-r--r--noatun/modules/monoscope/Makefile.am16
-rw-r--r--noatun/modules/monoscope/monoscope.cpp112
-rw-r--r--noatun/modules/monoscope/monoscope.h34
-rw-r--r--noatun/modules/monoscope/monoscope.plugin99
-rw-r--r--noatun/modules/net/Makefile.am14
-rw-r--r--noatun/modules/net/net.cpp57
-rw-r--r--noatun/modules/net/net.h31
-rw-r--r--noatun/modules/net/net.plugin136
-rw-r--r--noatun/modules/noatunui/Makefile.am16
-rw-r--r--noatun/modules/noatunui/noatunui.cpp9
-rw-r--r--noatun/modules/noatunui/noatunui.plugin120
-rw-r--r--noatun/modules/noatunui/userinterface.cpp315
-rw-r--r--noatun/modules/noatunui/userinterface.h72
-rw-r--r--noatun/modules/simple/Makefile.am16
-rw-r--r--noatun/modules/simple/back.xpm21
-rw-r--r--noatun/modules/simple/forward.xpm21
-rw-r--r--noatun/modules/simple/noatunui.cpp17
-rw-r--r--noatun/modules/simple/pause.xpm21
-rw-r--r--noatun/modules/simple/play.xpm21
-rw-r--r--noatun/modules/simple/playlist.xpm21
-rw-r--r--noatun/modules/simple/propertiesdialog.ui348
-rw-r--r--noatun/modules/simple/propertiesdialog.ui.h58
-rw-r--r--noatun/modules/simple/simple.plugin126
-rw-r--r--noatun/modules/simple/simpleui.rc37
-rw-r--r--noatun/modules/simple/stop.xpm21
-rw-r--r--noatun/modules/simple/userinterface.cpp379
-rw-r--r--noatun/modules/simple/userinterface.h80
-rw-r--r--noatun/modules/simple/volume.xpm21
-rw-r--r--noatun/modules/splitplaylist/LICENSE124
-rw-r--r--noatun/modules/splitplaylist/Makefile.am17
-rw-r--r--noatun/modules/splitplaylist/find.cpp63
-rw-r--r--noatun/modules/splitplaylist/find.h33
-rw-r--r--noatun/modules/splitplaylist/playlist.cpp281
-rw-r--r--noatun/modules/splitplaylist/playlist.h98
-rw-r--r--noatun/modules/splitplaylist/splitplaylist.cpp13
-rw-r--r--noatun/modules/splitplaylist/splitplaylist.plugin118
-rw-r--r--noatun/modules/splitplaylist/splui.rc34
-rw-r--r--noatun/modules/splitplaylist/view.cpp1009
-rw-r--r--noatun/modules/splitplaylist/view.h165
-rw-r--r--noatun/modules/systray/Makefile.am16
-rw-r--r--noatun/modules/systray/cmodule.cpp192
-rw-r--r--noatun/modules/systray/cmodule.h55
-rw-r--r--noatun/modules/systray/kitsystemtray.cpp131
-rw-r--r--noatun/modules/systray/kitsystemtray.h54
-rw-r--r--noatun/modules/systray/noatunui.cpp9
-rw-r--r--noatun/modules/systray/systray.cpp467
-rw-r--r--noatun/modules/systray/systray.h80
-rw-r--r--noatun/modules/systray/systray.plugin100
-rw-r--r--noatun/modules/systray/systrayui.rc20
-rw-r--r--noatun/modules/systray/yhconfig.kcfg81
-rw-r--r--noatun/modules/systray/yhconfig.kcfgc7
-rw-r--r--noatun/modules/systray/yhconfigwidget.ui333
-rw-r--r--noatun/modules/voiceprint/Makefile.am14
-rw-r--r--noatun/modules/voiceprint/prefs.cpp67
-rw-r--r--noatun/modules/voiceprint/prefs.h22
-rw-r--r--noatun/modules/voiceprint/voiceprint.cpp126
-rw-r--r--noatun/modules/voiceprint/voiceprint.h33
-rw-r--r--noatun/modules/voiceprint/voiceprint.plugin92
-rw-r--r--noatun/modules/winskin/Makefile.am51
-rw-r--r--noatun/modules/winskin/fileInfo.cpp50
-rw-r--r--noatun/modules/winskin/fileInfo.h21
-rw-r--r--noatun/modules/winskin/guiSpectrumAnalyser.cpp224
-rw-r--r--noatun/modules/winskin/guiSpectrumAnalyser.h66
-rw-r--r--noatun/modules/winskin/mimetypes/Makefile.am2
-rw-r--r--noatun/modules/winskin/mimetypes/interface/Makefile.am6
-rw-r--r--noatun/modules/winskin/mimetypes/interface/x-winamp-skin.desktop58
-rw-r--r--noatun/modules/winskin/plugin.cpp13
-rw-r--r--noatun/modules/winskin/skinMap.h38
-rw-r--r--noatun/modules/winskin/skins/Makefile.am11
-rw-r--r--noatun/modules/winskin/skins/winamp/BALANCE.BMPbin0 -> 9654 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/CBUTTONS.BMPbin0 -> 5974 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/FONT.BMPbin0 -> 12622 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/MAIN.BMPbin0 -> 96102 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/MONOSTER.BMPbin0 -> 2518 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/Makefile.am8
-rw-r--r--noatun/modules/winskin/skins/winamp/NUMS_EX.BMPbin0 -> 2482 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/PLAYPAUS.BMPbin0 -> 1474 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/POSBAR.BMPbin0 -> 4158 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/SHUFREP.BMPbin0 -> 23514 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/SPEC.BMPbin0 -> 2166 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/TEXT.BMPbin0 -> 3886 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/TITLEBAR.BMPbin0 -> 89838 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/VISCOLOR.TXT24
-rw-r--r--noatun/modules/winskin/skins/winamp/VOLUME.BMPbin0 -> 11548 bytes
-rw-r--r--noatun/modules/winskin/vis/Makefile.am39
-rw-r--r--noatun/modules/winskin/vis/WinSkinFFT.mcopclass5
-rw-r--r--noatun/modules/winskin/vis/realFFT.cpp156
-rw-r--r--noatun/modules/winskin/vis/realFFT.h69
-rw-r--r--noatun/modules/winskin/vis/realFFTFilter.cpp88
-rw-r--r--noatun/modules/winskin/vis/realFFTFilter.h49
-rw-r--r--noatun/modules/winskin/vis/visQueue.cpp43
-rw-r--r--noatun/modules/winskin/vis/visQueue.h32
-rw-r--r--noatun/modules/winskin/vis/winSkinFFT_impl.cpp148
-rw-r--r--noatun/modules/winskin/vis/winSkinFFT_impl.h62
-rw-r--r--noatun/modules/winskin/vis/winskinvis.idl12
-rw-r--r--noatun/modules/winskin/waBalanceSlider.cpp56
-rw-r--r--noatun/modules/winskin/waBalanceSlider.h42
-rw-r--r--noatun/modules/winskin/waButton.cpp101
-rw-r--r--noatun/modules/winskin/waButton.h62
-rw-r--r--noatun/modules/winskin/waClutterbar.cpp11
-rw-r--r--noatun/modules/winskin/waClutterbar.h18
-rw-r--r--noatun/modules/winskin/waColor.cpp73
-rw-r--r--noatun/modules/winskin/waColor.h26
-rw-r--r--noatun/modules/winskin/waDigit.cpp89
-rw-r--r--noatun/modules/winskin/waDigit.h48
-rw-r--r--noatun/modules/winskin/waIndicator.cpp34
-rw-r--r--noatun/modules/winskin/waIndicator.h41
-rw-r--r--noatun/modules/winskin/waInfo.cpp173
-rw-r--r--noatun/modules/winskin/waInfo.h53
-rw-r--r--noatun/modules/winskin/waJumpSlider.cpp78
-rw-r--r--noatun/modules/winskin/waJumpSlider.h52
-rw-r--r--noatun/modules/winskin/waLabel.cpp65
-rw-r--r--noatun/modules/winskin/waLabel.h39
-rw-r--r--noatun/modules/winskin/waMain.cpp13
-rw-r--r--noatun/modules/winskin/waMain.h16
-rw-r--r--noatun/modules/winskin/waRegion.cpp126
-rw-r--r--noatun/modules/winskin/waRegion.h26
-rw-r--r--noatun/modules/winskin/waShadeMapping.h148
-rw-r--r--noatun/modules/winskin/waSkin.cpp800
-rw-r--r--noatun/modules/winskin/waSkin.h177
-rw-r--r--noatun/modules/winskin/waSkinManager.cpp127
-rw-r--r--noatun/modules/winskin/waSkinManager.h39
-rw-r--r--noatun/modules/winskin/waSkinMapping.h148
-rw-r--r--noatun/modules/winskin/waSkinModel.cpp458
-rw-r--r--noatun/modules/winskin/waSkinModel.h64
-rw-r--r--noatun/modules/winskin/waSkins.h161
-rw-r--r--noatun/modules/winskin/waSlider.cpp209
-rw-r--r--noatun/modules/winskin/waSlider.h82
-rw-r--r--noatun/modules/winskin/waStatus.cpp42
-rw-r--r--noatun/modules/winskin/waStatus.h40
-rw-r--r--noatun/modules/winskin/waTitleBar.cpp79
-rw-r--r--noatun/modules/winskin/waTitleBar.h51
-rw-r--r--noatun/modules/winskin/waVolumeSlider.cpp51
-rw-r--r--noatun/modules/winskin/waVolumeSlider.h41
-rw-r--r--noatun/modules/winskin/waWidget.cpp58
-rw-r--r--noatun/modules/winskin/waWidget.h31
-rw-r--r--noatun/modules/winskin/winSkinConfig.cpp174
-rw-r--r--noatun/modules/winskin/winSkinConfig.h35
-rw-r--r--noatun/modules/winskin/winSkinVis.cpp107
-rw-r--r--noatun/modules/winskin/winSkinVis.h54
-rw-r--r--noatun/modules/winskin/winskin.plugin122
-rw-r--r--noatun/noatun.api169
-rw-r--r--noatun/noatun.desktop83
-rw-r--r--noatun/noatun.upd5
-rw-r--r--noatun/noatun20update.cpp41
-rw-r--r--noatun/pics/Makefile.am2
-rw-r--r--noatun/pics/cr128-action-equalizer.pngbin0 -> 6419 bytes
-rw-r--r--noatun/pics/cr128-action-playlist.pngbin0 -> 8465 bytes
-rw-r--r--noatun/pics/cr16-action-effect.pngbin0 -> 964 bytes
-rw-r--r--noatun/pics/cr16-action-equalizer.pngbin0 -> 746 bytes
-rw-r--r--noatun/pics/cr16-action-noatunback.pngbin0 -> 140 bytes
-rw-r--r--noatun/pics/cr16-action-noatunforward.pngbin0 -> 139 bytes
-rw-r--r--noatun/pics/cr16-action-noatunpause.pngbin0 -> 116 bytes
-rw-r--r--noatun/pics/cr16-action-noatunplay.pngbin0 -> 133 bytes
-rw-r--r--noatun/pics/cr16-action-noatunplaylist.pngbin0 -> 145 bytes
-rw-r--r--noatun/pics/cr16-action-noatunstop.pngbin0 -> 117 bytes
-rw-r--r--noatun/pics/cr16-action-playlist.pngbin0 -> 836 bytes
-rw-r--r--noatun/pics/cr22-action-equalizer.pngbin0 -> 1070 bytes
-rw-r--r--noatun/pics/cr22-action-noatunback.pngbin0 -> 209 bytes
-rw-r--r--noatun/pics/cr22-action-noatunfback.pngbin0 -> 189 bytes
-rw-r--r--noatun/pics/cr22-action-noatunfforward.pngbin0 -> 188 bytes
-rw-r--r--noatun/pics/cr22-action-noatunforward.pngbin0 -> 210 bytes
-rw-r--r--noatun/pics/cr22-action-noatunloopnone.pngbin0 -> 233 bytes
-rw-r--r--noatun/pics/cr22-action-noatunloopplaylist.pngbin0 -> 270 bytes
-rw-r--r--noatun/pics/cr22-action-noatunlooprandom.pngbin0 -> 244 bytes
-rw-r--r--noatun/pics/cr22-action-noatunloopsong.pngbin0 -> 266 bytes
-rw-r--r--noatun/pics/cr22-action-noatunpause.pngbin0 -> 201 bytes
-rw-r--r--noatun/pics/cr22-action-noatunplay.pngbin0 -> 184 bytes
-rw-r--r--noatun/pics/cr22-action-noatunplaylist.pngbin0 -> 234 bytes
-rw-r--r--noatun/pics/cr22-action-noatunstop.pngbin0 -> 140 bytes
-rw-r--r--noatun/pics/cr22-action-noatuntiny.pngbin0 -> 191 bytes
-rw-r--r--noatun/pics/cr22-action-playlist.pngbin0 -> 1066 bytes
-rw-r--r--noatun/pics/cr32-action-effect.pngbin0 -> 1937 bytes
-rw-r--r--noatun/pics/cr32-action-equalizer.pngbin0 -> 1742 bytes
-rw-r--r--noatun/pics/cr32-action-playlist.pngbin0 -> 1673 bytes
-rw-r--r--noatun/pics/cr48-action-effect.pngbin0 -> 3073 bytes
-rw-r--r--noatun/pics/cr48-action-equalizer.pngbin0 -> 2582 bytes
-rw-r--r--noatun/pics/cr48-action-playlist.pngbin0 -> 2672 bytes
-rw-r--r--noatun/pics/cr64-action-equalizer.pngbin0 -> 3478 bytes
-rw-r--r--noatun/pics/cr64-action-playlist.pngbin0 -> 3462 bytes
-rw-r--r--noatun/preset.dance8
-rw-r--r--noatun/preset.eclecticguitar9
-rw-r--r--noatun/preset.jazz8
-rw-r--r--noatun/preset.metal8
-rw-r--r--noatun/preset.trance8
-rw-r--r--noatun/preset.zero9
-rw-r--r--oggvorbis_artsplugin/Makefile.am23
-rw-r--r--oggvorbis_artsplugin/configure.in.in5
-rw-r--r--oggvorbis_artsplugin/oggPlayObject.mcopclass6
-rw-r--r--oggvorbis_artsplugin/oggPlayObject_impl.cpp313
-rw-r--r--oggvorbis_artsplugin/oggPlayObject_impl.h62
-rw-r--r--oggvorbis_artsplugin/oggarts.idl12
-rw-r--r--xine_artsplugin/Makefile.am25
-rw-r--r--xine_artsplugin/audio_fifo_out.c407
-rw-r--r--xine_artsplugin/audio_fifo_out.h44
-rw-r--r--xine_artsplugin/configure.in.in257
-rw-r--r--xine_artsplugin/tools/Makefile.am1
-rw-r--r--xine_artsplugin/tools/thumbnail/Makefile.am24
-rw-r--r--xine_artsplugin/tools/thumbnail/sprocket-large.pngbin0 -> 1033 bytes
-rw-r--r--xine_artsplugin/tools/thumbnail/sprocket-medium.pngbin0 -> 649 bytes
-rw-r--r--xine_artsplugin/tools/thumbnail/sprocket-small.pngbin0 -> 357 bytes
-rw-r--r--xine_artsplugin/tools/thumbnail/videocreator.cpp376
-rw-r--r--xine_artsplugin/tools/thumbnail/videocreator.h40
-rw-r--r--xine_artsplugin/tools/thumbnail/videoscaler.cpp258
-rw-r--r--xine_artsplugin/tools/thumbnail/videoscaler.h24
-rw-r--r--xine_artsplugin/tools/thumbnail/videothumbnail.desktop71
-rw-r--r--xine_artsplugin/xineAudioPlayObject.mcopclass7
-rw-r--r--xine_artsplugin/xinePlayObject.idl13
-rw-r--r--xine_artsplugin/xinePlayObject.mcopclass7
-rw-r--r--xine_artsplugin/xinePlayObject_impl.cpp784
-rw-r--r--xine_artsplugin/xinePlayObject_impl.h158
-rw-r--r--xine_artsplugin/xineVideoPlayObject.mcopclass7
2602 files changed, 328241 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 00000000..74eb758b
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,3 @@
+
+Look in the subdirs to get info about the authors.
+
diff --git a/COPYING b/COPYING
new file mode 100644
index 00000000..5ff207a8
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,346 @@
+NOTE! The GPL below is copyrighted by the Free Software Foundation, but
+the instance of code that it refers to (the kde programs) are copyrighted
+by the authors who actually wrote it.
+
+---------------------------------------------------------------------------
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/COPYING-DOCS b/COPYING-DOCS
new file mode 100644
index 00000000..4a0fe1c8
--- /dev/null
+++ b/COPYING-DOCS
@@ -0,0 +1,397 @@
+ GNU Free Documentation License
+ Version 1.2, November 2002
+
+
+ Copyright (C) 2000,2001,2002 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+0. PREAMBLE
+
+The purpose of this License is to make a manual, textbook, or other
+functional and useful document "free" in the sense of freedom: to
+assure everyone the effective freedom to copy and redistribute it,
+with or without modifying it, either commercially or noncommercially.
+Secondarily, this License preserves for the author and publisher a way
+to get credit for their work, while not being considered responsible
+for modifications made by others.
+
+This License is a kind of "copyleft", which means that derivative
+works of the document must themselves be free in the same sense. It
+complements the GNU General Public License, which is a copyleft
+license designed for free software.
+
+We have designed this License in order to use it for manuals for free
+software, because free software needs free documentation: a free
+program should come with manuals providing the same freedoms that the
+software does. But this License is not limited to software manuals;
+it can be used for any textual work, regardless of subject matter or
+whether it is published as a printed book. We recommend this License
+principally for works whose purpose is instruction or reference.
+
+
+1. APPLICABILITY AND DEFINITIONS
+
+This License applies to any manual or other work, in any medium, that
+contains a notice placed by the copyright holder saying it can be
+distributed under the terms of this License. Such a notice grants a
+world-wide, royalty-free license, unlimited in duration, to use that
+work under the conditions stated herein. The "Document", below,
+refers to any such manual or work. Any member of the public is a
+licensee, and is addressed as "you". You accept the license if you
+copy, modify or distribute the work in a way requiring permission
+under copyright law.
+
+A "Modified Version" of the Document means any work containing the
+Document or a portion of it, either copied verbatim, or with
+modifications and/or translated into another language.
+
+A "Secondary Section" is a named appendix or a front-matter section of
+the Document that deals exclusively with the relationship of the
+publishers or authors of the Document to the Document's overall subject
+(or to related matters) and contains nothing that could fall directly
+within that overall subject. (Thus, if the Document is in part a
+textbook of mathematics, a Secondary Section may not explain any
+mathematics.) The relationship could be a matter of historical
+connection with the subject or with related matters, or of legal,
+commercial, philosophical, ethical or political position regarding
+them.
+
+The "Invariant Sections" are certain Secondary Sections whose titles
+are designated, as being those of Invariant Sections, in the notice
+that says that the Document is released under this License. If a
+section does not fit the above definition of Secondary then it is not
+allowed to be designated as Invariant. The Document may contain zero
+Invariant Sections. If the Document does not identify any Invariant
+Sections then there are none.
+
+The "Cover Texts" are certain short passages of text that are listed,
+as Front-Cover Texts or Back-Cover Texts, in the notice that says that
+the Document is released under this License. A Front-Cover Text may
+be at most 5 words, and a Back-Cover Text may be at most 25 words.
+
+A "Transparent" copy of the Document means a machine-readable copy,
+represented in a format whose specification is available to the
+general public, that is suitable for revising the document
+straightforwardly with generic text editors or (for images composed of
+pixels) generic paint programs or (for drawings) some widely available
+drawing editor, and that is suitable for input to text formatters or
+for automatic translation to a variety of formats suitable for input
+to text formatters. A copy made in an otherwise Transparent file
+format whose markup, or absence of markup, has been arranged to thwart
+or discourage subsequent modification by readers is not Transparent.
+An image format is not Transparent if used for any substantial amount
+of text. A copy that is not "Transparent" is called "Opaque".
+
+Examples of suitable formats for Transparent copies include plain
+ASCII without markup, Texinfo input format, LaTeX input format, SGML
+or XML using a publicly available DTD, and standard-conforming simple
+HTML, PostScript or PDF designed for human modification. Examples of
+transparent image formats include PNG, XCF and JPG. Opaque formats
+include proprietary formats that can be read and edited only by
+proprietary word processors, SGML or XML for which the DTD and/or
+processing tools are not generally available, and the
+machine-generated HTML, PostScript or PDF produced by some word
+processors for output purposes only.
+
+The "Title Page" means, for a printed book, the title page itself,
+plus such following pages as are needed to hold, legibly, the material
+this License requires to appear in the title page. For works in
+formats which do not have any title page as such, "Title Page" means
+the text near the most prominent appearance of the work's title,
+preceding the beginning of the body of the text.
+
+A section "Entitled XYZ" means a named subunit of the Document whose
+title either is precisely XYZ or contains XYZ in parentheses following
+text that translates XYZ in another language. (Here XYZ stands for a
+specific section name mentioned below, such as "Acknowledgements",
+"Dedications", "Endorsements", or "History".) To "Preserve the Title"
+of such a section when you modify the Document means that it remains a
+section "Entitled XYZ" according to this definition.
+
+The Document may include Warranty Disclaimers next to the notice which
+states that this License applies to the Document. These Warranty
+Disclaimers are considered to be included by reference in this
+License, but only as regards disclaiming warranties: any other
+implication that these Warranty Disclaimers may have is void and has
+no effect on the meaning of this License.
+
+
+2. VERBATIM COPYING
+
+You may copy and distribute the Document in any medium, either
+commercially or noncommercially, provided that this License, the
+copyright notices, and the license notice saying this License applies
+to the Document are reproduced in all copies, and that you add no other
+conditions whatsoever to those of this License. You may not use
+technical measures to obstruct or control the reading or further
+copying of the copies you make or distribute. However, you may accept
+compensation in exchange for copies. If you distribute a large enough
+number of copies you must also follow the conditions in section 3.
+
+You may also lend copies, under the same conditions stated above, and
+you may publicly display copies.
+
+
+3. COPYING IN QUANTITY
+
+If you publish printed copies (or copies in media that commonly have
+printed covers) of the Document, numbering more than 100, and the
+Document's license notice requires Cover Texts, you must enclose the
+copies in covers that carry, clearly and legibly, all these Cover
+Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
+the back cover. Both covers must also clearly and legibly identify
+you as the publisher of these copies. The front cover must present
+the full title with all words of the title equally prominent and
+visible. You may add other material on the covers in addition.
+Copying with changes limited to the covers, as long as they preserve
+the title of the Document and satisfy these conditions, can be treated
+as verbatim copying in other respects.
+
+If the required texts for either cover are too voluminous to fit
+legibly, you should put the first ones listed (as many as fit
+reasonably) on the actual cover, and continue the rest onto adjacent
+pages.
+
+If you publish or distribute Opaque copies of the Document numbering
+more than 100, you must either include a machine-readable Transparent
+copy along with each Opaque copy, or state in or with each Opaque copy
+a computer-network location from which the general network-using
+public has access to download using public-standard network protocols
+a complete Transparent copy of the Document, free of added material.
+If you use the latter option, you must take reasonably prudent steps,
+when you begin distribution of Opaque copies in quantity, to ensure
+that this Transparent copy will remain thus accessible at the stated
+location until at least one year after the last time you distribute an
+Opaque copy (directly or through your agents or retailers) of that
+edition to the public.
+
+It is requested, but not required, that you contact the authors of the
+Document well before redistributing any large number of copies, to give
+them a chance to provide you with an updated version of the Document.
+
+
+4. MODIFICATIONS
+
+You may copy and distribute a Modified Version of the Document under
+the conditions of sections 2 and 3 above, provided that you release
+the Modified Version under precisely this License, with the Modified
+Version filling the role of the Document, thus licensing distribution
+and modification of the Modified Version to whoever possesses a copy
+of it. In addition, you must do these things in the Modified Version:
+
+A. Use in the Title Page (and on the covers, if any) a title distinct
+ from that of the Document, and from those of previous versions
+ (which should, if there were any, be listed in the History section
+ of the Document). You may use the same title as a previous version
+ if the original publisher of that version gives permission.
+B. List on the Title Page, as authors, one or more persons or entities
+ responsible for authorship of the modifications in the Modified
+ Version, together with at least five of the principal authors of the
+ Document (all of its principal authors, if it has fewer than five),
+ unless they release you from this requirement.
+C. State on the Title page the name of the publisher of the
+ Modified Version, as the publisher.
+D. Preserve all the copyright notices of the Document.
+E. Add an appropriate copyright notice for your modifications
+ adjacent to the other copyright notices.
+F. Include, immediately after the copyright notices, a license notice
+ giving the public permission to use the Modified Version under the
+ terms of this License, in the form shown in the Addendum below.
+G. Preserve in that license notice the full lists of Invariant Sections
+ and required Cover Texts given in the Document's license notice.
+H. Include an unaltered copy of this License.
+I. Preserve the section Entitled "History", Preserve its Title, and add
+ to it an item stating at least the title, year, new authors, and
+ publisher of the Modified Version as given on the Title Page. If
+ there is no section Entitled "History" in the Document, create one
+ stating the title, year, authors, and publisher of the Document as
+ given on its Title Page, then add an item describing the Modified
+ Version as stated in the previous sentence.
+J. Preserve the network location, if any, given in the Document for
+ public access to a Transparent copy of the Document, and likewise
+ the network locations given in the Document for previous versions
+ it was based on. These may be placed in the "History" section.
+ You may omit a network location for a work that was published at
+ least four years before the Document itself, or if the original
+ publisher of the version it refers to gives permission.
+K. For any section Entitled "Acknowledgements" or "Dedications",
+ Preserve the Title of the section, and preserve in the section all
+ the substance and tone of each of the contributor acknowledgements
+ and/or dedications given therein.
+L. Preserve all the Invariant Sections of the Document,
+ unaltered in their text and in their titles. Section numbers
+ or the equivalent are not considered part of the section titles.
+M. Delete any section Entitled "Endorsements". Such a section
+ may not be included in the Modified Version.
+N. Do not retitle any existing section to be Entitled "Endorsements"
+ or to conflict in title with any Invariant Section.
+O. Preserve any Warranty Disclaimers.
+
+If the Modified Version includes new front-matter sections or
+appendices that qualify as Secondary Sections and contain no material
+copied from the Document, you may at your option designate some or all
+of these sections as invariant. To do this, add their titles to the
+list of Invariant Sections in the Modified Version's license notice.
+These titles must be distinct from any other section titles.
+
+You may add a section Entitled "Endorsements", provided it contains
+nothing but endorsements of your Modified Version by various
+parties--for example, statements of peer review or that the text has
+been approved by an organization as the authoritative definition of a
+standard.
+
+You may add a passage of up to five words as a Front-Cover Text, and a
+passage of up to 25 words as a Back-Cover Text, to the end of the list
+of Cover Texts in the Modified Version. Only one passage of
+Front-Cover Text and one of Back-Cover Text may be added by (or
+through arrangements made by) any one entity. If the Document already
+includes a cover text for the same cover, previously added by you or
+by arrangement made by the same entity you are acting on behalf of,
+you may not add another; but you may replace the old one, on explicit
+permission from the previous publisher that added the old one.
+
+The author(s) and publisher(s) of the Document do not by this License
+give permission to use their names for publicity for or to assert or
+imply endorsement of any Modified Version.
+
+
+5. COMBINING DOCUMENTS
+
+You may combine the Document with other documents released under this
+License, under the terms defined in section 4 above for modified
+versions, provided that you include in the combination all of the
+Invariant Sections of all of the original documents, unmodified, and
+list them all as Invariant Sections of your combined work in its
+license notice, and that you preserve all their Warranty Disclaimers.
+
+The combined work need only contain one copy of this License, and
+multiple identical Invariant Sections may be replaced with a single
+copy. If there are multiple Invariant Sections with the same name but
+different contents, make the title of each such section unique by
+adding at the end of it, in parentheses, the name of the original
+author or publisher of that section if known, or else a unique number.
+Make the same adjustment to the section titles in the list of
+Invariant Sections in the license notice of the combined work.
+
+In the combination, you must combine any sections Entitled "History"
+in the various original documents, forming one section Entitled
+"History"; likewise combine any sections Entitled "Acknowledgements",
+and any sections Entitled "Dedications". You must delete all sections
+Entitled "Endorsements".
+
+
+6. COLLECTIONS OF DOCUMENTS
+
+You may make a collection consisting of the Document and other documents
+released under this License, and replace the individual copies of this
+License in the various documents with a single copy that is included in
+the collection, provided that you follow the rules of this License for
+verbatim copying of each of the documents in all other respects.
+
+You may extract a single document from such a collection, and distribute
+it individually under this License, provided you insert a copy of this
+License into the extracted document, and follow this License in all
+other respects regarding verbatim copying of that document.
+
+
+7. AGGREGATION WITH INDEPENDENT WORKS
+
+A compilation of the Document or its derivatives with other separate
+and independent documents or works, in or on a volume of a storage or
+distribution medium, is called an "aggregate" if the copyright
+resulting from the compilation is not used to limit the legal rights
+of the compilation's users beyond what the individual works permit.
+When the Document is included in an aggregate, this License does not
+apply to the other works in the aggregate which are not themselves
+derivative works of the Document.
+
+If the Cover Text requirement of section 3 is applicable to these
+copies of the Document, then if the Document is less than one half of
+the entire aggregate, the Document's Cover Texts may be placed on
+covers that bracket the Document within the aggregate, or the
+electronic equivalent of covers if the Document is in electronic form.
+Otherwise they must appear on printed covers that bracket the whole
+aggregate.
+
+
+8. TRANSLATION
+
+Translation is considered a kind of modification, so you may
+distribute translations of the Document under the terms of section 4.
+Replacing Invariant Sections with translations requires special
+permission from their copyright holders, but you may include
+translations of some or all Invariant Sections in addition to the
+original versions of these Invariant Sections. You may include a
+translation of this License, and all the license notices in the
+Document, and any Warranty Disclaimers, provided that you also include
+the original English version of this License and the original versions
+of those notices and disclaimers. In case of a disagreement between
+the translation and the original version of this License or a notice
+or disclaimer, the original version will prevail.
+
+If a section in the Document is Entitled "Acknowledgements",
+"Dedications", or "History", the requirement (section 4) to Preserve
+its Title (section 1) will typically require changing the actual
+title.
+
+
+9. TERMINATION
+
+You may not copy, modify, sublicense, or distribute the Document except
+as expressly provided for under this License. Any other attempt to
+copy, modify, sublicense or distribute the Document is void, and will
+automatically terminate your rights under this License. However,
+parties who have received copies, or rights, from you under this
+License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+
+10. FUTURE REVISIONS OF THIS LICENSE
+
+The Free Software Foundation may publish new, revised versions
+of the GNU Free Documentation License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns. See
+http://www.gnu.org/copyleft/.
+
+Each version of the License is given a distinguishing version number.
+If the Document specifies that a particular numbered version of this
+License "or any later version" applies to it, you have the option of
+following the terms and conditions either of that specified version or
+of any later version that has been published (not as a draft) by the
+Free Software Foundation. If the Document does not specify a version
+number of this License, you may choose any version ever published (not
+as a draft) by the Free Software Foundation.
+
+
+ADDENDUM: How to use this License for your documents
+
+To use this License in a document you have written, include a copy of
+the License in the document and put the following copyright and
+license notices just after the title page:
+
+ Copyright (c) YEAR YOUR NAME.
+ Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.2
+ or any later version published by the Free Software Foundation;
+ with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
+ A copy of the license is included in the section entitled "GNU
+ Free Documentation License".
+
+If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
+replace the "with...Texts." line with this:
+
+ with the Invariant Sections being LIST THEIR TITLES, with the
+ Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
+
+If you have Invariant Sections without Cover Texts, or some other
+combination of the three, merge those two alternatives to suit the
+situation.
+
+If your document contains nontrivial examples of program code, we
+recommend releasing these examples in parallel under your choice of
+free software license, such as the GNU General Public License,
+to permit their use in free software.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 00000000..f8bad0c1
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,176 @@
+Basic Installation
+==================
+
+ These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+ The file `configure.in' is used to create `configure' by a program
+called `autoconf'. You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes a while. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. You can give `configure'
+initial values for variables by setting them in the environment. Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+ CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+ env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory. After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+ By default, `make install' will install the package's files in
+`/usr/local/kde/bin', `/usr/local/kde/lib', etc. You can specify an
+installation prefix other than `/usr/local/kde' by giving `configure'
+the option `--prefix=PATH'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on. Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+ CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+ If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+ Use and save the results of the tests in FILE instead of
+ `./config.cache'. Set FILE to `/dev/null' to disable caching, for
+ debugging `configure'.
+
+`--help'
+ Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made.
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`--version'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
+
diff --git a/Mainpage.dox b/Mainpage.dox
new file mode 100644
index 00000000..cf5e977e
--- /dev/null
+++ b/Mainpage.dox
@@ -0,0 +1,10 @@
+/** @mainpage KDE Multimedia
+*
+* KDE Multimedia collects multimedia-related applications. That is,
+* any application with an audio or video orientation. Most of the
+* applications are audio oriented anyway. They are:
+*
+* - kscd, a CD player
+* - libkcddb, a library for dealing with CDDB queries
+*
+*/
diff --git a/Makefile.am.in b/Makefile.am.in
new file mode 100644
index 00000000..7ce22f0b
--- /dev/null
+++ b/Makefile.am.in
@@ -0,0 +1,15 @@
+## kdebase/Makefile.am
+## (C) 1997 Stephan Kulow
+
+AUTOMAKE_OPTIONS = foreign 1.6.1
+DISTCLEANFILES = inst-apps
+
+COMPILE_AFTER_arts = noatun krec akode_artsplugin
+COMPILE_AFTER_mpeglib = mpeglib_artsplug
+COMPILE_AFTER_libkcddb = kaudiocreator kioslave kscd
+COMPILE_AFTER_akode = juk
+
+MAINTAINERCLEANFILES = subdirs configure.in acinclude.m4 SUBDIRS
+
+include admin/deps.am
+
diff --git a/Makefile.cvs b/Makefile.cvs
new file mode 100644
index 00000000..b4752bd8
--- /dev/null
+++ b/Makefile.cvs
@@ -0,0 +1,16 @@
+
+all:
+ @echo "This Makefile is only for the CVS repository"
+ @echo "This will be deleted before making the distribution"
+ @echo ""
+ @if test ! -d admin; then \
+ echo "Please recheckout this module!" ;\
+ echo "for cvs: use checkout once and after that update again" ;\
+ echo "for cvsup: checkout kde-common from cvsup and" ;\
+ echo " link kde-common/admin to ./admin" ;\
+ exit 1 ;\
+ fi
+ $(MAKE) -f admin/Makefile.common cvs
+
+.SILENT:
+
diff --git a/README b/README
new file mode 100644
index 00000000..4e8b3ce7
--- /dev/null
+++ b/README
@@ -0,0 +1,60 @@
+In this file:
+
+* What it is
+* Common Mistakes
+* Debugging
+* More Info
+
+What it is
+----------
+
+* noatun: a multimedia player for sound and movies, very extensible due to
+ it's plugin interface
+* aktion: a player specialiced on movies, needs xanim
+* kaudiocreator: CD ripper and audio encoder frontend.
+* kaboodle: light media player
+* kmid: A standalone and embeddable midi player, includes a karaoke-mode
+* kmidi: midi player, can use sound patch files and create a WAV file
+* kmix: the audio mixer as a standalone program and Kicker applet
+* kscd: A CD player with an interface to the internet CDDB database
+* krec: A recording frontend using aRts
+
+The following are libraries and plugins that are building the core
+infrastructure of above applications:
+
+* arts: a versatily multimedia system the consists of various little
+ building blocks that you (or an application) can combine in
+ almost arbitrary ways to create sound and video processing pipes
+* kfile-plugins: provide meta information about sound files
+* mpeglib: a library for MPEG 1 (layer I, II and III) encoded files
+* mpeglib_artsplug: wrapped mpeglib to make it aRts-aware
+* mpg123_artsplugin: a trimmed down mpg123 made aRts-compatible
+* oggvorbis_artsplugin: support for ogg-vorbis (not included) in aRts
+* xine_artsplugin: ??
+* libkcddb: a library for retrieving and sending cddb information
+
+Common Mistakes
+---------------
+
+If configure claims Qt cannot be found, have a look at http://www.trolltech.com
+to get a copy of latest Qt 3.3.x version.
+
+Debugging
+---------
+
+You can use --enable-debug with the configure script, if you want to have
+debug code in your KDE apps and libs. If debugging is not a concern,
+--disable-debug will explicitly avoid compiling additional debugging
+functions.
+
+More Info
+---------
+
+Have a look at the individual subdirectories, if you want to know, what
+versions of apps are included.
+
+Please direct any bug reports to our bug list by visiting
+http://bugs.kde.org.
+
+General KDE discussions should go to the KDE mailing list (kde@kde.org).
+
diff --git a/akode_artsplugin/Makefile.am b/akode_artsplugin/Makefile.am
new file mode 100644
index 00000000..230a6a01
--- /dev/null
+++ b/akode_artsplugin/Makefile.am
@@ -0,0 +1,46 @@
+INCLUDES= $(akode_includes) -I$(kde_includes)/arts $(all_includes)
+
+noinst_HEADERS = akodePlayObject_impl.h
+
+lib_LTLIBRARIES = libarts_akode.la
+
+libarts_akode_la_SOURCES = akodearts.cc akodePlayObject_impl.cpp \
+ akodeMPCPlayObject_impl.cpp akodeMPEGPlayObject_impl.cpp \
+ akodeFFMPEGPlayObject_impl.cpp akodeXiphPlayObject_impl.cpp \
+ akodeVorbisStreamPlayObject_impl.cpp akodeSpeexStreamPlayObject_impl.cpp
+libarts_akode_la_LDFLAGS = $(all_libraries) -module -no-undefined
+libarts_akode_la_LIBADD = $(akode_libs) -lkmedia2_idl -lsoundserver_idl -lartsflow
+libarts_akode_la_METASOURCES = AUTO
+libarts_akode_la_COMPILE_FIRST = akodearts.h
+
+$(srcdir)/akodePlayObject_impl.cpp: akodearts.h
+akodearts.mcopclass: akodearts.h
+akodearts.mcoptype: akodearts.h
+akodearts.cc akodearts.h: $(srcdir)/akodearts.idl
+ $(MCOPIDL) -t $(INCLUDES) $(srcdir)/akodearts.idl
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = akodearts.mcoptype akodearts.mcopclass
+
+if include_akode_mpeg
+AKODE_MPEG=akodeMPEGPlayObject.mcopclass
+endif
+
+if include_akode_mpc
+AKODE_MPC=akodeMPCPlayObject.mcopclass
+endif
+
+if include_akode_xiph
+AKODE_XIPH=akodeXiphPlayObject.mcopclass akodeVorbisStreamPlayObject.mcopclass akodeSpeexStreamPlayObject.mcopclass
+endif
+
+if include_akode_ffmpeg
+AKODE_FFMPEG=akodeFFMPEGPlayObject.mcopclass
+endif
+
+mcopclassdir = $(libdir)/mcop
+mcopclass_DATA = akodePlayObject.mcopclass \
+ $(AKODE_MPC) $(AKODE_MPEG) $(AKODE_XIPH) $(AKODE_FFMPEG)
+
+CLEANFILES=akodearts.h akodearts.cc akodearts.mcopclass akodearts.mcoptype
+
diff --git a/akode_artsplugin/akodeFFMPEGPlayObject.mcopclass b/akode_artsplugin/akodeFFMPEGPlayObject.mcopclass
new file mode 100644
index 00000000..09f24118
--- /dev/null
+++ b/akode_artsplugin/akodeFFMPEGPlayObject.mcopclass
@@ -0,0 +1,7 @@
+Interface=akodeFFMPEGPlayObject,Arts::PlayObject,Arts::StreamPlayObject,Arts::PitchablePlayObject,Arts::SynthModule,Arts::Object
+Library=libarts_akode.la
+Language=C++
+Author=Allan Sandfeld Jensen <kde@carewolf.com>
+Extension=wma,ra,m4a,ram,aac,ac3,ogg
+MimeType=audio/mp4,audio/vnd.rn-realaudio,audio/x-pn-realaudio,audio/x-ms-wma,application/vnd.ms-asf,audio/ac3,audio/aac,audio/mpeg,audio/x-mp3,audio/x-mp2,audio/x-mp1,audio/vorbis,audio/x-vorbis,application/ogg,video/x-ms-asf
+Preference=5
diff --git a/akode_artsplugin/akodeFFMPEGPlayObject_impl.cpp b/akode_artsplugin/akodeFFMPEGPlayObject_impl.cpp
new file mode 100644
index 00000000..803dff73
--- /dev/null
+++ b/akode_artsplugin/akodeFFMPEGPlayObject_impl.cpp
@@ -0,0 +1,31 @@
+/* akodeFFMPEGPlayObject
+
+ Copyright (C) 2005 Allan Sandfeld Jensen <kde@carewolf.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "akodeFFMPEGPlayObject_impl.h"
+
+akodeFFMPEGPlayObject_impl::akodeFFMPEGPlayObject_impl()
+ : akodeFFMPEGPlayObject_skel(), akodePlayObject_impl("ffmpeg")
+{
+}
+
+REGISTER_IMPLEMENTATION(akodeFFMPEGPlayObject_impl);
+
+
+
diff --git a/akode_artsplugin/akodeFFMPEGPlayObject_impl.h b/akode_artsplugin/akodeFFMPEGPlayObject_impl.h
new file mode 100644
index 00000000..65f04296
--- /dev/null
+++ b/akode_artsplugin/akodeFFMPEGPlayObject_impl.h
@@ -0,0 +1,34 @@
+/* akodeFFMPEGPlayObject
+
+ Copyright (C) 2005 Allan Sandfeld Jensen <kde@carewolf.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _AKODEFFMPEGPLAYOBJECT_IMPL_H
+#define _AKODEFFMPEGPLAYOBJECT_IMPL_H
+
+#include "akodePlayObject_impl.h"
+#include "akodearts.h"
+
+class akodeFFMPEGPlayObject_impl
+ : virtual public akodeFFMPEGPlayObject_skel, public akodePlayObject_impl
+{
+public:
+ akodeFFMPEGPlayObject_impl();
+};
+
+#endif
diff --git a/akode_artsplugin/akodeMPCPlayObject.mcopclass b/akode_artsplugin/akodeMPCPlayObject.mcopclass
new file mode 100644
index 00000000..2cd1fe31
--- /dev/null
+++ b/akode_artsplugin/akodeMPCPlayObject.mcopclass
@@ -0,0 +1,7 @@
+Interface=akodeMPCPlayObject,Arts::PlayObject,Arts::PitchablePlayObject,Arts::SynthModule,Arts::Object
+Library=libarts_akode.la
+Language=C++
+Author=Allan Sandfeld Jensen <kde@carewolf.com>
+Extension=mpc,mpp,mp+
+MimeType=audio/x-musepack
+Preference=4
diff --git a/akode_artsplugin/akodeMPCPlayObject_impl.cpp b/akode_artsplugin/akodeMPCPlayObject_impl.cpp
new file mode 100644
index 00000000..8de09236
--- /dev/null
+++ b/akode_artsplugin/akodeMPCPlayObject_impl.cpp
@@ -0,0 +1,31 @@
+/* akodeMPCPlayObject
+
+ Copyright (C) 2004 Allan Sandfeld Jensen <kde@carewolf.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "akodeMPCPlayObject_impl.h"
+
+akodeMPCPlayObject_impl::akodeMPCPlayObject_impl()
+ : akodeMPCPlayObject_skel(), akodePlayObject_impl("mpc")
+{
+}
+
+REGISTER_IMPLEMENTATION(akodeMPCPlayObject_impl);
+
+
+
diff --git a/akode_artsplugin/akodeMPCPlayObject_impl.h b/akode_artsplugin/akodeMPCPlayObject_impl.h
new file mode 100644
index 00000000..59a9eef9
--- /dev/null
+++ b/akode_artsplugin/akodeMPCPlayObject_impl.h
@@ -0,0 +1,34 @@
+/* akodeMPCPlayObject
+
+ Copyright (C) 2004 Allan Sandfeld Jensen <kde@carewolf.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _AKODEMPCPLAYOBJECT_IMPL_H
+#define _AKODEMPCPLAYOBJECT_IMPL_H
+
+#include "akodePlayObject_impl.h"
+#include "akodearts.h"
+
+class akodeMPCPlayObject_impl
+ : virtual public akodeMPCPlayObject_skel, public akodePlayObject_impl
+{
+public:
+ akodeMPCPlayObject_impl();
+};
+
+#endif
diff --git a/akode_artsplugin/akodeMPEGPlayObject.mcopclass b/akode_artsplugin/akodeMPEGPlayObject.mcopclass
new file mode 100644
index 00000000..4e326b5f
--- /dev/null
+++ b/akode_artsplugin/akodeMPEGPlayObject.mcopclass
@@ -0,0 +1,7 @@
+Interface=akodeMPEGPlayObject,Arts::PlayObject,Arts::StreamPlayObject,Arts::PitchablePlayObject,Arts::SynthModule,Arts::Object
+Library=libarts_akode.la
+Language=C++
+Author=Allan Sandfeld Jensen <kde@carewolf.com>
+Extension=mp1,mp2,mp3
+MimeType=audio/mpeg,audio/x-mp3,audio/x-mp2,audio/x-mp1
+Preference=7
diff --git a/akode_artsplugin/akodeMPEGPlayObject_impl.cpp b/akode_artsplugin/akodeMPEGPlayObject_impl.cpp
new file mode 100644
index 00000000..1516ffe5
--- /dev/null
+++ b/akode_artsplugin/akodeMPEGPlayObject_impl.cpp
@@ -0,0 +1,31 @@
+/* akodeMPEGPlayObject
+
+ Copyright (C) 2004 Allan Sandfeld Jensen <kde@carewolf.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "akodeMPEGPlayObject_impl.h"
+
+akodeMPEGPlayObject_impl::akodeMPEGPlayObject_impl()
+ : akodeMPEGPlayObject_skel(), akodePlayObject_impl("mpeg")
+{
+}
+
+REGISTER_IMPLEMENTATION(akodeMPEGPlayObject_impl);
+
+
+
diff --git a/akode_artsplugin/akodeMPEGPlayObject_impl.h b/akode_artsplugin/akodeMPEGPlayObject_impl.h
new file mode 100644
index 00000000..4b8744d9
--- /dev/null
+++ b/akode_artsplugin/akodeMPEGPlayObject_impl.h
@@ -0,0 +1,34 @@
+/* akodeMPEGPlayObject
+
+ Copyright (C) 2004 Allan Sandfeld Jensen <kde@carewolf.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _AKODEMPEGPLAYOBJECT_IMPL_H
+#define _AKODEMPEGPLAYOBJECT_IMPL_H
+
+#include "akodePlayObject_impl.h"
+#include "akodearts.h"
+
+class akodeMPEGPlayObject_impl
+ : virtual public akodeMPEGPlayObject_skel, public akodePlayObject_impl
+{
+public:
+ akodeMPEGPlayObject_impl();
+};
+
+#endif
diff --git a/akode_artsplugin/akodePlayObject.mcopclass b/akode_artsplugin/akodePlayObject.mcopclass
new file mode 100644
index 00000000..28674e8f
--- /dev/null
+++ b/akode_artsplugin/akodePlayObject.mcopclass
@@ -0,0 +1,7 @@
+Interface=akodePlayObject,Arts::PlayObject,Arts::PitchablePlayObject,Arts::SynthModule,Arts::Object
+Library=libarts_akode.la
+Language=C++
+Author=Allan Sandfeld Jensen <kde@carewolf.com>
+Extension=wav
+MimeType=audio/x-wav
+Preference=3
diff --git a/akode_artsplugin/akodePlayObject_impl.cpp b/akode_artsplugin/akodePlayObject_impl.cpp
new file mode 100644
index 00000000..78708e13
--- /dev/null
+++ b/akode_artsplugin/akodePlayObject_impl.cpp
@@ -0,0 +1,487 @@
+/* akodePlayObject
+
+ Copyright (C) 2003-2005 Allan Sandfeld Jensen <kde@carewolf.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+//#define AKODEARTS_SINGLETHREADED
+//#define AKODEARTS_SRCRESAMPLING
+#define AKODEARTS_FRAMEBUFFER 32
+// The instream-buffer must be smaller than 64kbytes (1<<16)
+#define AKODEARTS_INSTREAMBUFFER (1<<14)
+
+#include <math.h>
+
+#include <soundserver.h>
+#include <audiosubsys.h>
+#include <debug.h>
+
+#include "config.h"
+
+#include <akode/audioframe.h>
+#include <akode/localfile.h>
+#include <akode/mmapfile.h>
+#include <akode/decoder.h>
+#include <akode/resampler.h>
+#include <akode/fast_resampler.h>
+#include <akode/wav_decoder.h>
+
+#include "arts_inputstream.h"
+
+#ifndef AKODEARTS_SINGLETHREADED
+ #include <akode/buffered_decoder.h>
+#endif
+
+#ifdef AKODEARTS_SRCRESAMPLING
+#define AKODEARTS_RESAMPLER "src"
+#else
+#define AKODEARTS_RESAMPLER "fast"
+#endif
+
+
+#include "akodePlayObject_impl.h"
+
+using namespace Arts;
+using namespace aKode;
+
+akodePlayObject_impl::akodePlayObject_impl(const string &plugin)
+ : source(0)
+ , frameDecoder(0)
+ , decoder(0)
+ , bufferedDecoder(0)
+ , resampler(0)
+ , buffer(0)
+ , inBuffer(0)
+ , buf_pos(0)
+ , mState(posIdle)
+ , mSpeed(1.0)
+ , m_packetQueue(0)
+ , m_bytebuffer(0)
+ , m_fading(false)
+ , decoderPlugin(plugin)
+ , resamplerPlugin(AKODEARTS_RESAMPLER)
+{
+ m_packetQueue = new queue<DataPacket<mcopbyte>*>;
+ if(!resamplerPlugin.isLoaded())
+ resamplerPlugin.load("fast");
+}
+
+akodePlayObject_impl::~akodePlayObject_impl()
+{
+ delete m_packetQueue;
+
+ unload();
+}
+
+bool akodePlayObject_impl::loadPlugin(const string &plugin)
+{
+ return decoderPlugin.load(plugin);
+}
+
+bool akodePlayObject_impl::streamMedia(Arts::InputStream inputstream)
+{
+ arts_debug("akode: opening input-stream");
+ m_bytebuffer = new aKode::ByteBuffer(AKODEARTS_INSTREAMBUFFER);
+ instream = inputstream;
+
+ Arts::StreamPlayObject self = Arts::StreamPlayObject::_from_base(_copy());
+ connect(instream, "outdata", self, "indata");
+
+ source = new Arts_InputStream(instream, m_bytebuffer);
+ return loadSource();
+}
+
+bool akodePlayObject_impl::loadMedia(const string &filename)
+{
+ arts_debug("akode: opening %s", filename.c_str());
+ source = new MMapFile(filename.c_str());
+ if (!source->openRO()) {
+ delete source;
+ source = new LocalFile(filename.c_str());
+ if (!source->openRO()) {
+ delete source;
+ source = 0;
+ return false;
+ }
+ }
+ source->close();
+ return loadSource();
+}
+
+bool akodePlayObject_impl::loadSource()
+{
+ if (!decoderPlugin.isLoaded()) {
+ return false;
+ }
+ frameDecoder = decoderPlugin.openDecoder(source);
+
+ if (!frameDecoder) {
+ delete source;
+ source = 0;
+ arts_warning("akode: Could not open frame-decoder");
+ return false;
+ }
+
+#ifndef AKODEARTS_SINGLETHREADED
+ bufferedDecoder = new BufferedDecoder();
+ bufferedDecoder->setBufferSize(AKODEARTS_FRAMEBUFFER);
+ bufferedDecoder->openDecoder(frameDecoder);
+ decoder = bufferedDecoder;
+#else
+ decoder = frameDecoder;
+#endif
+
+ return true;
+}
+
+string akodePlayObject_impl::description()
+{
+ return "akodePlayObject";
+}
+
+Arts::InputStream akodePlayObject_impl::inputStream() {
+ return instream;
+}
+
+string akodePlayObject_impl::mediaName()
+{
+ if (source)
+ return source->filename;
+ else
+ return string();
+}
+
+poCapabilities akodePlayObject_impl::capabilities()
+{
+ return (poCapabilities)(capPause | capSeek);
+}
+
+poState akodePlayObject_impl::state()
+{
+ return mState;
+}
+
+void akodePlayObject_impl::play()
+{
+ arts_debug("akode: play");
+
+ if (!decoder) {
+ arts_warning("akode: No media loaded");
+ return;
+ }
+
+ if (mState == posIdle) {
+ mState = posPlaying;
+
+ if (!inBuffer) inBuffer = new AudioFrame;
+ if (!buffer) buffer = inBuffer;
+
+
+ buf_pos = 0;
+ } else
+ mState = posPlaying;
+}
+
+void akodePlayObject_impl::pause()
+{
+ arts_debug("akode: pause");
+ mState = posPaused;
+}
+
+void akodePlayObject_impl::halt()
+{
+ arts_debug("akode: halt");
+ if (mState == posIdle) return;
+ mState = posIdle;
+ unload();
+}
+
+void akodePlayObject_impl::unload()
+{
+ arts_debug("akode: unload");
+ if (m_bytebuffer) m_bytebuffer->release();
+#ifndef AKODEARTS_SINGLETHREADED
+ if (bufferedDecoder) {
+ bufferedDecoder->stop();
+ bufferedDecoder->closeDecoder();
+ delete bufferedDecoder;
+ bufferedDecoder = 0;
+ }
+#endif
+ delete frameDecoder;
+ frameDecoder = 0;
+ decoder = 0;
+ if (buffer != inBuffer)
+ delete inBuffer;
+ delete buffer;
+ inBuffer = buffer = 0;
+ buf_pos = 0;
+
+ delete resampler;
+ resampler = 0;
+ delete source;
+ source = 0;
+#ifndef AKODEARTS_SINGLETHREADED
+ delete m_bytebuffer;
+ m_bytebuffer = 0;
+#endif
+}
+
+poTime akodePlayObject_impl::currentTime()
+{
+ poTime time;
+
+ long pos;
+ if (decoder) {
+ pos = decoder->position(); // decoder time
+ if (pos < 0 ) pos = 0;
+ else
+ if (samplingRate > 0 && buffer)
+ {
+ float lpos = (float)(buf_pos-buffer->length) / (float)samplingRate; // local time
+ pos += (long)(lpos*1000.0);
+ }
+ }
+ else
+ pos = 0;
+
+ time.seconds = pos / 1000 ;
+ time.ms = pos % 1000;
+
+ return time;
+}
+
+poTime akodePlayObject_impl::overallTime()
+{
+ poTime time;
+
+ long len;
+ if (decoder) {
+ len = decoder->length();
+ if (len < 0 ) len = 0;
+ }
+ else
+ len = 0;
+
+ time.seconds = len / 1000;
+ time.ms = len % 1000;
+
+ return time;
+}
+
+void akodePlayObject_impl::seek(const poTime &time)
+{
+ arts_debug("akode: seek");
+ if (!decoder) {
+ arts_warning("akode: No media loaded");
+ return;
+ }
+ long akode_pos = time.seconds*1000+time.ms;
+
+ if (decoder->seek(akode_pos) && buffer) {
+ buffer->length = 0; // force re-read
+ buf_pos = 0;
+ }
+}
+
+bool akodePlayObject_impl::readFrame()
+{
+ arts_debug("akode: readFrame");
+
+ if (!inBuffer || !decoder) return false;
+ if (m_bytebuffer) processQueue();
+ if(!decoder->readFrame(inBuffer)) {
+ if (decoder->eof()) {
+ arts_debug("akode: eof");
+ halt();
+ } else
+ if (decoder->error()) {
+ arts_debug("akode: error");
+ halt();
+ } else
+ buffer->length=0;
+ return false;
+ }
+
+ // Invalid frame from broken plugin
+ if (inBuffer->sample_rate == 0) return false;
+
+ if (samplingRate != inBuffer->sample_rate || mSpeed != 1.0) {
+ //arts_debug("akode: resampling to %d/%d", inBuffer->sample_rate, samplingRate);
+ if ( !buffer || buffer==inBuffer ) buffer = new AudioFrame;
+ if ( !resampler)
+ resampler = resamplerPlugin.openResampler();
+
+ resampler->setSampleRate(samplingRate);
+ resampler->setSpeed(mSpeed);
+
+ resampler->doFrame(inBuffer, buffer);
+ } else {
+ if ( buffer !=inBuffer) delete buffer;
+ buffer = inBuffer;
+ }
+
+ buf_pos = 0;
+ return true;
+}
+
+bool akodePlayObject_impl::eof()
+{
+ if (!decoder || !buffer) return true;
+ else
+ return buf_pos>=buffer->length && decoder->eof();
+}
+
+// GCC's lack of good template support means this is easyist done using DEFINE
+#define SEND_BLOCK(T) \
+ T* data = (T*)buffer->data[0]; \
+ j = i; bp = buf_pos; \
+ while (bp < buffer->length && j<count) { \
+ left[j] = data[bp]*scale; \
+ j++; bp++; \
+ } \
+ if (buffer->channels > 1) \
+ data = (T*)buffer->data[1]; \
+ j = i; bp = buf_pos; \
+ while (bp < buffer->length && j<count) { \
+ right[j] = data[bp]*scale; \
+ j++; bp++; \
+ }
+
+
+void akodePlayObject_impl::calculateBlock(unsigned long cnt)
+{
+ long count = (long)cnt;
+// arts_debug("akode: calculateBlock");
+ long i = 0, j, bp;
+
+ if (!decoder) {
+ arts_warning("akode: No media loaded");
+ goto fill_out;
+ }
+
+ if (!buffer)
+ // Not playing yet
+ goto fill_out;
+
+ while ((mState == posPlaying || m_fading) && i<count) {
+ if (buf_pos >= buffer->length) {
+ buf_pos = 0;
+ if (!readFrame()) {
+ break;
+ }
+ }
+ if (buffer->channels > 2 || buffer->sample_width > 24 || buffer->sample_width == 0) {
+ arts_warning("akode: Incompatible media");
+ halt();
+ break;
+ }
+
+ signed char width = buffer->sample_width;
+ float scale = (float)(1<<(width-1));
+ if (width >= 0) {
+ scale = 1/scale;
+ if (width <= 8) {
+ SEND_BLOCK(int8_t);
+ i=j;
+ buf_pos=bp;
+ }
+ else if (width <= 16) {
+ SEND_BLOCK(int16_t);
+ i=j;
+ buf_pos=bp;
+ }
+ else {
+ SEND_BLOCK(int32_t);
+ i=j;
+ buf_pos=bp;
+ }
+ }
+ else {
+ scale = 1.0;
+ SEND_BLOCK(float);
+ i=j;
+ buf_pos=bp;
+ }
+ }
+
+ // fill-out the rest with silence
+fill_out:
+ for (; i < count; i++) {
+ left[i] = right[i] = 0;
+ }
+
+}
+
+void akodePlayObject_impl::streamInit()
+{
+ arts_debug("akode: streamInit");
+}
+
+void akodePlayObject_impl::streamStart()
+{
+ arts_debug("akode: streamStart");
+#ifndef AKODEARTS_SINGLETHREADED
+ bufferedDecoder->start();
+#endif
+}
+
+void akodePlayObject_impl::streamEnd()
+{
+ arts_debug("akode: streamEnd");
+ mState = posIdle;
+ if (decoder) unload();
+}
+
+void akodePlayObject_impl::speed(float newValue)
+{
+ mSpeed = newValue;
+}
+
+float akodePlayObject_impl::speed()
+{
+ return mSpeed;
+}
+
+void akodePlayObject_impl::process_indata(DataPacket<mcopbyte> *inpacket) {
+ arts_debug("akode: process_indata");
+ m_packetQueue->push(inpacket);
+ if (!m_bytebuffer) return;
+ processQueue();
+}
+
+void akodePlayObject_impl::processQueue()
+{
+ while (!m_packetQueue->empty()) {
+ long freespace = m_bytebuffer->space();
+
+ DataPacket<mcopbyte> *inpacket = m_packetQueue->front();
+ if (!inpacket) return;
+
+ if (freespace >= inpacket->size) {
+ if (m_bytebuffer->write((char*)inpacket->contents, inpacket->size, false)) {
+ m_packetQueue->pop();
+ inpacket->processed();
+ }
+ } else
+ return;
+ }
+ if (instream.eof()) m_bytebuffer->close();
+}
+
+
+REGISTER_IMPLEMENTATION(akodePlayObject_impl);
diff --git a/akode_artsplugin/akodePlayObject_impl.h b/akode_artsplugin/akodePlayObject_impl.h
new file mode 100644
index 00000000..e603681b
--- /dev/null
+++ b/akode_artsplugin/akodePlayObject_impl.h
@@ -0,0 +1,98 @@
+/* akodePlayObject
+
+ Copyright (C) 2004 Allan Sandfeld Jensen <kde@carewolf.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _AKODEPLAYOBJECT_IMPL_H
+#define _AKODEPLAYOBJECT_IMPL_H
+
+#include <akode/resampler.h>
+#include <akode/audioframe.h>
+#include <akode/decoder.h>
+#include <akode/resampler.h>
+
+#include <stdsynthmodule.h>
+#include <kmedia2.h>
+#include <queue>
+#include "akodearts.h"
+
+using std::string;
+using std::queue;
+
+namespace aKode {
+ class BufferedDecoder;
+ class File;
+ class ByteBuffer;
+}
+
+class akodePlayObject_impl
+ : virtual public akodePlayObject_skel
+ , virtual public Arts::StdSynthModule
+{
+public:
+ akodePlayObject_impl(const string &plugin = "wav");
+ ~akodePlayObject_impl();
+
+ virtual bool loadMedia(const string &);
+ virtual bool streamMedia(Arts::InputStream instream);
+ virtual bool loadSource();
+ string description();
+ string mediaName();
+ Arts::InputStream inputStream();
+ Arts::poCapabilities capabilities();
+ Arts::poState state();
+ void play();
+ void pause();
+ void halt();
+ void seek(const Arts::poTime &);
+ Arts::poTime currentTime();
+ Arts::poTime overallTime();
+
+ void streamInit();
+ void streamStart();
+ void calculateBlock(unsigned long samples);
+ void streamEnd();
+
+ void speed(float newValue);
+ float speed();
+
+ void process_indata(Arts::DataPacket<Arts::mcopbyte>*);
+ void processQueue();
+
+protected:
+ bool loadPlugin(const string &plugin);
+ bool readFrame();
+ bool eof();
+ void unload();
+ Arts::InputStream instream;
+ aKode::File *source;
+ aKode::Decoder *frameDecoder, *decoder;
+ aKode::BufferedDecoder *bufferedDecoder;
+ aKode::Resampler *resampler;
+ aKode::AudioFrame *buffer, *inBuffer;
+ int buf_pos;
+ Arts::poState mState;
+ float mSpeed;
+ queue<Arts::DataPacket<Arts::mcopbyte>*> *m_packetQueue;
+ aKode::ByteBuffer *m_bytebuffer;
+ bool m_fading;
+ aKode::DecoderPluginHandler decoderPlugin;
+ aKode::ResamplerPluginHandler resamplerPlugin;
+};
+
+#endif
diff --git a/akode_artsplugin/akodeSpeexStreamPlayObject.mcopclass b/akode_artsplugin/akodeSpeexStreamPlayObject.mcopclass
new file mode 100644
index 00000000..06cfdf5b
--- /dev/null
+++ b/akode_artsplugin/akodeSpeexStreamPlayObject.mcopclass
@@ -0,0 +1,7 @@
+Interface=akodeSpeexStreamPlayObject,Arts::StreamPlayObject,Arts::PitchablePlayObject,Arts::SynthModule,Arts::Object
+Library=libarts_akode.la
+Language=C++
+Author=Allan Sandfeld Jensen <kde@carewolf.com>
+Extension=
+MimeType=audio/x-speex
+Preference=6
diff --git a/akode_artsplugin/akodeSpeexStreamPlayObject_impl.cpp b/akode_artsplugin/akodeSpeexStreamPlayObject_impl.cpp
new file mode 100644
index 00000000..9bba35d7
--- /dev/null
+++ b/akode_artsplugin/akodeSpeexStreamPlayObject_impl.cpp
@@ -0,0 +1,49 @@
+/* akodeSpeexStreamPlayObject
+
+ Copyright (C) 2004 Allan Sandfeld Jensen <kde@carewolf.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "akodeSpeexStreamPlayObject_impl.h"
+
+#include <akode/decoder.h>
+#include <akode/buffered_decoder.h>
+
+akodeSpeexStreamPlayObject_impl::akodeSpeexStreamPlayObject_impl()
+ : akodeSpeexStreamPlayObject_skel(), akodePlayObject_impl("xiph")
+{
+ speex_plugin = (aKode::DecoderPlugin*)decoderPlugin.loadPlugin("speex_decoder");
+}
+
+
+bool akodeSpeexStreamPlayObject_impl::loadSource()
+{
+ frameDecoder = speex_plugin->openDecoder(source);
+#ifndef SINGLETHREADED_AKODE_PLUGIN
+ bufferedDecoder = new aKode::BufferedDecoder();
+ bufferedDecoder->openDecoder(frameDecoder);
+ decoder = bufferedDecoder;
+#else
+ decoder = frameDecoder;
+#endif
+ return true;
+}
+
+REGISTER_IMPLEMENTATION(akodeSpeexStreamPlayObject_impl);
+
+
+
diff --git a/akode_artsplugin/akodeSpeexStreamPlayObject_impl.h b/akode_artsplugin/akodeSpeexStreamPlayObject_impl.h
new file mode 100644
index 00000000..b033938d
--- /dev/null
+++ b/akode_artsplugin/akodeSpeexStreamPlayObject_impl.h
@@ -0,0 +1,39 @@
+/* akodeSpeexStreamPlayObject
+
+ Copyright (C) 2004 Allan Sandfeld Jensen <kde@carewolf.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _AKODESPEEXSTREAMPLAYOBJECT_IMPL_H
+#define _AKODESPEEXSTREAMPLAYOBJECT_IMPL_H
+
+#include "akodePlayObject_impl.h"
+#include "akodearts.h"
+
+#include <akode/decoder.h>
+
+class akodeSpeexStreamPlayObject_impl
+ : virtual public akodeSpeexStreamPlayObject_skel, public akodePlayObject_impl
+{
+public:
+ akodeSpeexStreamPlayObject_impl();
+ bool loadSource();
+protected:
+ aKode::DecoderPlugin *speex_plugin;
+};
+
+#endif
diff --git a/akode_artsplugin/akodeVorbisStreamPlayObject.mcopclass b/akode_artsplugin/akodeVorbisStreamPlayObject.mcopclass
new file mode 100644
index 00000000..810bf319
--- /dev/null
+++ b/akode_artsplugin/akodeVorbisStreamPlayObject.mcopclass
@@ -0,0 +1,7 @@
+Interface=akodeVorbisStreamPlayObject,Arts::StreamPlayObject,Arts::PitchablePlayObject,Arts::SynthModule,Arts::Object
+Library=libarts_akode.la
+Language=C++
+Author=Allan Sandfeld Jensen <kde@carewolf.com>
+Extension=
+MimeType=audio/x-vorbis,audio/vorbis,application/ogg
+Preference=6
diff --git a/akode_artsplugin/akodeVorbisStreamPlayObject_impl.cpp b/akode_artsplugin/akodeVorbisStreamPlayObject_impl.cpp
new file mode 100644
index 00000000..5fcc6690
--- /dev/null
+++ b/akode_artsplugin/akodeVorbisStreamPlayObject_impl.cpp
@@ -0,0 +1,49 @@
+/* akodeVorbisStreamPlayObject
+
+ Copyright (C) 2004 Allan Sandfeld Jensen <kde@carewolf.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "akodeVorbisStreamPlayObject_impl.h"
+
+#include <akode/decoder.h>
+#include <akode/buffered_decoder.h>
+
+akodeVorbisStreamPlayObject_impl::akodeVorbisStreamPlayObject_impl()
+ : akodeVorbisStreamPlayObject_skel(), akodePlayObject_impl("xiph")
+{
+ vorbis_plugin = (aKode::DecoderPlugin*)decoderPlugin.loadPlugin("vorbis_decoder");
+}
+
+
+bool akodeVorbisStreamPlayObject_impl::loadSource()
+{
+ frameDecoder = vorbis_plugin->openDecoder(source);
+#ifndef SINGLETHREADED_AKODE_PLUGIN
+ bufferedDecoder = new aKode::BufferedDecoder();
+ bufferedDecoder->openDecoder(frameDecoder);
+ decoder = bufferedDecoder;
+#else
+ decoder = frameDecoder;
+#endif
+ return true;
+}
+
+REGISTER_IMPLEMENTATION(akodeVorbisStreamPlayObject_impl);
+
+
+
diff --git a/akode_artsplugin/akodeVorbisStreamPlayObject_impl.h b/akode_artsplugin/akodeVorbisStreamPlayObject_impl.h
new file mode 100644
index 00000000..d2ad76e5
--- /dev/null
+++ b/akode_artsplugin/akodeVorbisStreamPlayObject_impl.h
@@ -0,0 +1,39 @@
+/* akodeVorbisStreamPlayObject
+
+ Copyright (C) 2004 Allan Sandfeld Jensen <kde@carewolf.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _AKODEVORBISSTREAMPLAYOBJECT_IMPL_H
+#define _AKODEVORBISSTREAMPLAYOBJECT_IMPL_H
+
+#include "akodePlayObject_impl.h"
+#include "akodearts.h"
+
+#include <akode/decoder.h>
+
+class akodeVorbisStreamPlayObject_impl
+ : virtual public akodeVorbisStreamPlayObject_skel, public akodePlayObject_impl
+{
+public:
+ akodeVorbisStreamPlayObject_impl();
+ bool loadSource();
+protected:
+ aKode::DecoderPlugin *vorbis_plugin;
+};
+
+#endif
diff --git a/akode_artsplugin/akodeXiphPlayObject.mcopclass b/akode_artsplugin/akodeXiphPlayObject.mcopclass
new file mode 100644
index 00000000..3927f766
--- /dev/null
+++ b/akode_artsplugin/akodeXiphPlayObject.mcopclass
@@ -0,0 +1,7 @@
+Interface=akodeXiphPlayObject,Arts::PlayObject,Arts::PitchablePlayObject,Arts::SynthModule,Arts::Object
+Library=libarts_akode.la
+Language=C++
+Author=Allan Sandfeld Jensen <kde@carewolf.com>
+Extension=flac,ogg,spx
+MimeType=audio/x-flac,audio/x-oggflac,audio/x-speex,audio/x-vorbis,audio/vorbis,application/ogg
+Preference=6
diff --git a/akode_artsplugin/akodeXiphPlayObject_impl.cpp b/akode_artsplugin/akodeXiphPlayObject_impl.cpp
new file mode 100644
index 00000000..86f59ffe
--- /dev/null
+++ b/akode_artsplugin/akodeXiphPlayObject_impl.cpp
@@ -0,0 +1,31 @@
+/* akodeXiphPlayObject
+
+ Copyright (C) 2004 Allan Sandfeld Jensen <kde@carewolf.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "akodeXiphPlayObject_impl.h"
+
+akodeXiphPlayObject_impl::akodeXiphPlayObject_impl()
+ : akodeXiphPlayObject_skel(), akodePlayObject_impl("xiph")
+{
+}
+
+REGISTER_IMPLEMENTATION(akodeXiphPlayObject_impl);
+
+
+
diff --git a/akode_artsplugin/akodeXiphPlayObject_impl.h b/akode_artsplugin/akodeXiphPlayObject_impl.h
new file mode 100644
index 00000000..3d9b9e7a
--- /dev/null
+++ b/akode_artsplugin/akodeXiphPlayObject_impl.h
@@ -0,0 +1,34 @@
+/* akodeXiphPlayObject
+
+ Copyright (C) 2004 Allan Sandfeld Jensen <kde@carewolf.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _AKODEXIPHPLAYOBJECT_IMPL_H
+#define _AKODEXIPHPLAYOBJECT_IMPL_H
+
+#include "akodePlayObject_impl.h"
+#include "akodearts.h"
+
+class akodeXiphPlayObject_impl
+ : virtual public akodeXiphPlayObject_skel, public akodePlayObject_impl
+{
+public:
+ akodeXiphPlayObject_impl();
+};
+
+#endif
diff --git a/akode_artsplugin/akodearts.idl b/akode_artsplugin/akodearts.idl
new file mode 100644
index 00000000..a2686288
--- /dev/null
+++ b/akode_artsplugin/akodearts.idl
@@ -0,0 +1,30 @@
+#include <kmedia2.idl>
+#include <soundserver.idl>
+
+interface akodePlayObject : Arts::StreamPlayObject, Arts::SynthModule, Arts::PitchablePlayObject
+{
+ async in byte stream indata;
+
+ out audio stream left, right;
+};
+
+interface akodeMPCPlayObject : akodePlayObject
+{};
+
+interface akodeMPEGPlayObject : akodePlayObject
+{};
+
+interface akodeFFMPEGPlayObject : akodePlayObject
+{};
+
+interface akodeXiphPlayObject : akodePlayObject
+{};
+
+interface akodeFAADPlayObject : akodePlayObject
+{};
+
+interface akodeVorbisStreamPlayObject : akodePlayObject
+{};
+
+interface akodeSpeexStreamPlayObject : akodePlayObject
+{};
diff --git a/akode_artsplugin/arts_inputstream.h b/akode_artsplugin/arts_inputstream.h
new file mode 100644
index 00000000..71f8797d
--- /dev/null
+++ b/akode_artsplugin/arts_inputstream.h
@@ -0,0 +1,152 @@
+/* aKode: aRts InputStream
+
+ Copyright (C) 2004 Allan Sandfeld Jensen <kde@carewolf.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _AKODE_ARTS_INPUTSTREAM_H
+#define _AKODE_ARTS_INPUTSTREAM_H
+
+extern "C" {
+#include <stdio.h>
+#include <fcntl.h>
+}
+
+#include <arts/dispatcher.h>
+
+#include <akode/file.h>
+#include <akode/bytebuffer.h>
+
+class Arts_InputStream : public aKode::File
+{
+protected:
+ // m_instream is mutable because Arts::InputStream has not marked const functions
+ mutable Arts::InputStream m_instream;
+ aKode::ByteBuffer *m_buffer;
+ bool m_open, m_seekable;
+ long m_pos;
+ long m_len;
+public:
+ Arts_InputStream(Arts::InputStream inputstream, aKode::ByteBuffer *buffer)
+ : File("arts_inputstream"),
+ m_instream(inputstream), m_buffer(buffer),
+ m_open(false), m_seekable(false),
+ m_pos(-1), m_len(-1)
+ {
+ m_instream.streamInit();
+ };
+ virtual ~Arts_InputStream() {
+ }
+ bool openRO() {
+ m_open = true;
+ m_pos = 0;
+// Arts::Dispatcher::lock();
+ m_len = m_instream.size();
+ m_seekable = m_instream.seekOk();
+ m_instream.streamStart();
+// Arts::Dispatcher::unlock();
+ return true;
+ }
+ void close() {
+ m_open = false;
+ m_instream.streamEnd();
+ }
+ long read(char* ptr, long num) {
+ if (!m_open) return -1;
+ if (num<=0) return 0;
+ long n = m_buffer->read(ptr,num,true);
+ m_pos += n;
+ return n;
+ }
+ long write(const char*, long) {
+ return -1;
+ }
+ bool seek(long to, int whence) {
+ if(!m_open || !seekable()) return false;
+
+ arts_debug("akode: InputStream seeking");
+
+ long newpos = 0;
+ switch (whence) {
+ case SEEK_SET:
+ newpos = to;
+ break;
+ case SEEK_CUR:
+ newpos = m_pos + to;
+ break;
+ case SEEK_END:
+ if (m_len < 0) return false;
+ newpos = m_len + to;
+ break;
+ default:
+ return false;
+ }
+
+ long s = m_instream.seek(newpos);
+ if (s >= 0) {
+ m_pos = s;
+ m_buffer->flush();
+ return true;
+ }
+ else
+ return false;
+ }
+
+ long position() const {
+ if(!m_open) return -1;
+ return m_pos;
+ }
+
+ long length() const {
+ if(!m_open) return -1;
+ return m_len;
+ }
+
+ bool seekable() const {
+ return m_seekable;
+ }
+
+ bool readable() const {
+ return true;
+ }
+
+ bool writeable() const {
+ return false;
+ }
+ // Arts-call needs to be protected since
+ // the member-functions here are called by another thread
+ bool eof() const {
+ if(!m_open) return true;
+ bool res = false;
+ if (m_buffer->empty()) {
+ Arts::Dispatcher::lock();
+ res = m_instream.eof();
+ Arts::Dispatcher::unlock();
+ }
+ return res;
+ }
+
+ bool error() const {
+ return (!m_open);
+ }
+
+ void fadvise() {
+ return;
+ }
+};
+
+#endif
diff --git a/akode_artsplugin/configure.in.in b/akode_artsplugin/configure.in.in
new file mode 100644
index 00000000..5456ac39
--- /dev/null
+++ b/akode_artsplugin/configure.in.in
@@ -0,0 +1,16 @@
+if test x$build_arts != xyes || test x$have_akode != xyes
+then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE akode_artsplugin"
+else
+ KDE_CHECK_LIB(akode_mpeg_decoder, mpeg_decoder, have_akode_mpeg=yes, have_akode_mpeg=no, -lakode)
+ KDE_CHECK_LIB(akode_mpc_decoder, mpc_decoder, have_akode_mpc=yes, have_akode_mpc=no, -lakode)
+ KDE_CHECK_LIB(akode_xiph_decoder, xiph_decoder, have_akode_xiph=yes, have_akode_xiph=no, -lakode)
+ KDE_CHECK_LIB(akode_ffmpeg_decoder, ffmpeg_decoder, have_akode_ffmpeg=yes, have_akode_ffmpeg=no, -lakode)
+fi
+
+# This must always be called to handle the --without-akode case, as autoconf
+# will check these conditionals values it seems.
+AM_CONDITIONAL(include_akode_mpeg, test x$have_akode_mpeg = xyes)
+AM_CONDITIONAL(include_akode_mpc, test x$have_akode_mpc = xyes)
+AM_CONDITIONAL(include_akode_xiph, test x$have_akode_xiph = xyes)
+AM_CONDITIONAL(include_akode_ffmpeg, test x$have_akode_ffmpeg = xyes)
diff --git a/arts/Makefile.am b/arts/Makefile.am
new file mode 100644
index 00000000..f98d9c01
--- /dev/null
+++ b/arts/Makefile.am
@@ -0,0 +1,9 @@
+if arts_within_KDE
+ARTS_BUILD_KDE=builder tools
+endif
+
+SUBDIRS = runtime midi gui modules examples $(ARTS_BUILD_KDE)
+DIST_SUBDIRS = runtime midi gui modules examples builder tools
+
+messages:
+ $(XGETTEXT) modules/*/*.cpp modules/*/*.cc gui/*/*.cpp gui/*/*.cc -o $(podir)/artsmodules.pot
diff --git a/arts/builder/FullDuplexTest.arts b/arts/builder/FullDuplexTest.arts
new file mode 100644
index 00000000..bcd6f753
--- /dev/null
+++ b/arts/builder/FullDuplexTest.arts
@@ -0,0 +1,139 @@
+name=FullDuplexTest
+module=Arts::Synth_BUS_DOWNLINK
+{
+ id=0
+ x=3
+ y=0
+ port=busname
+ {
+ id=1
+ string_data=in_soundcard
+ }
+ port=left
+ {
+ id=2
+ connect_to=9
+ }
+ port=right
+ {
+ id=3
+ connect_to=13
+ }
+}
+module=Arts::Synth_BUS_UPLINK
+{
+ id=4
+ x=3
+ y=8
+ port=busname
+ {
+ id=5
+ string_data=out_soundcard
+ }
+ port=left
+ {
+ id=6
+ connect_to=11
+ }
+ port=right
+ {
+ id=7
+ connect_to=15
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=8
+ x=3
+ y=5
+ port=invalue1
+ {
+ id=9
+ connect_to=2
+ }
+ port=invalue2
+ {
+ id=10
+ connect_to=25
+ }
+ port=outvalue
+ {
+ id=11
+ connect_to=6
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=12
+ x=6
+ y=5
+ port=invalue1
+ {
+ id=13
+ connect_to=3
+ }
+ port=invalue2
+ {
+ id=14
+ connect_to=25
+ }
+ port=outvalue
+ {
+ id=15
+ connect_to=7
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=16
+ x=0
+ y=0
+ port=frequency
+ {
+ id=17
+ audio_data=100.00000
+ }
+ port=pos
+ {
+ id=18
+ connect_to=20
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=19
+ x=1
+ y=1
+ port=pos
+ {
+ id=20
+ connect_to=18
+ }
+ port=outvalue
+ {
+ id=21
+ connect_to=23
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=22
+ x=2
+ y=2
+ port=invalue1
+ {
+ id=23
+ connect_to=21
+ }
+ port=invalue2
+ {
+ id=24
+ audio_data=12.00000
+ }
+ port=outvalue
+ {
+ id=25
+ connect_to=10
+ connect_to=14
+ }
+}
diff --git a/arts/builder/Makefile.am b/arts/builder/Makefile.am
new file mode 100644
index 00000000..3f5ce2b3
--- /dev/null
+++ b/arts/builder/Makefile.am
@@ -0,0 +1,39 @@
+SUBDIRS = pics
+# set the include path for X, qt, KDE, mico and the synthesizer class defs
+INCLUDES= -I$(top_srcdir)/arts/runtime -I$(top_builddir)/arts/runtime -I$(kde_includes)/arts $(all_includes)
+
+xdg_apps_DATA = artsbuilder.desktop
+
+kdemime_DATA = x-artsbuilder.desktop
+kdemimedir = $(kde_mimedir)/application
+
+bin_PROGRAMS = artsbuilder
+
+artsbuilder_SOURCES = autorouter.cpp main.cpp propertypanelbase.ui \
+ module.cpp propertypanel.cpp structure.cpp \
+ retrievedlg.cpp createtool.cpp structureport.cpp \
+ drawutils.cpp scomponent.cpp portposdlg.cpp \
+ menumaker.cpp session.cpp dirmanager.cpp mwidget.cpp \
+ execdlg.cpp qttableview.cpp interfacedlg.cpp
+artsbuilder_COMPILE_FIRST = ../runtime/artsbuilder.h
+
+noinst_HEADERS = qttableview.h
+
+METASOURCES = AUTO
+
+artsbuilder_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+# the libraries to link against. Be aware of the order. First the libraries,
+# that depend on the following ones.
+artsbuilder_LDADD = $(LIB_KFILE) -lqtmcop $(LIB_X11) $(LIBPTHREAD) $(top_builddir)/arts/runtime/libartsbuilder.la -lsoundserver_idl -lartskde
+
+rcdir = $(kde_datadir)/artsbuilder
+rc_DATA = artsbuilderui.rc
+
+# make messages.po. Move this one to ../po/ and "make merge" in po
+# the -x is for skipping messages already translated in kdelibs
+messages: rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/artsbuilder.pot
+
+propertypanel.h: propertypanelbase.h
+
diff --git a/arts/builder/TODO b/arts/builder/TODO
new file mode 100644
index 00000000..139f22b2
--- /dev/null
+++ b/arts/builder/TODO
@@ -0,0 +1,69 @@
+Bugs:
+====
+- docked properties can be removed, but not fetched back
+
+Wishlist for the artsbuilder app:
+================================
+- implement File/New and provide templates
+- implement cut/copy/paste
+- implement undo
+- implement some preferences (e.g. zoom scale)
+
+- make application more KDE-like:
+ - icons in menus
+ - keyboard accelerators
+ - application icon(s)
+ - status bar
+ - session management
+ - tooltips and help
+ - kpart?
+
+- avoid crashing when loading a bad .arts file
+ (i.e. file open: wrong structure parameters like x/y pos)
+
+- ability to print a module view
+
+- don't allow execution of a structure with ports (like an instrument) directly
+
+- editing of structures while they are running (*)
+- wysiwyg editing of visual structures (like qt designer/delphi), requires (*)
+
+- provide a property dialog like
+ +----------+--------+
+ | property | value |
+ +----------+--------+
+ | caption | hello |
+ | width | 20 |
+ | height | 100 |
+ | x | 30 |
+ | y | 30 |
+ +----------+--------+
+ this should be very useful in editing objects with lots of properties
+ (widgets)
+
+- make _changed signals visible so you can connect the value_changed of a
+ poti to the frequency of a filter
+
+- make it possible to edit parent property of widgets (i.e. to insert one
+ widget into another)
+
+- add structure property dialog with structure meta information, at least
+ * (class) name
+ * (real world) name with i18n (?)
+ * implemented interfaces
+ * ports (maybe Details >>, tabbed notebook) - like already implemented
+ * author
+ * (category for menues)
+ * copyright
+ * license
+ * whether to publish or not (?)
+ * template (?)
+
+Additions "outside" the app code:
+================================
+- create standalone command line tool to load modules that does not
+ depend on KDE
+
+- write a tutorial in the arts manual
+
+- create more bitmaps/icons for all modules
diff --git a/arts/builder/artsbuilder.desktop b/arts/builder/artsbuilder.desktop
new file mode 100644
index 00000000..9d99c6d7
--- /dev/null
+++ b/arts/builder/artsbuilder.desktop
@@ -0,0 +1,122 @@
+# KDE Config File
+[Desktop Entry]
+Name=aRts Builder
+Name[af]=Arts Builder
+Name[ar]=باني Arts
+Name[bn]=আর্ট্‌স্ বিল্ডার
+Name[br]=arts Builder
+Name[ca]=Constructor aRts
+Name[cs]=Konstruktér aRts
+Name[cy]=Adeiladwr aRts
+Name[da]=aRts-bygger
+Name[de]=aRts-Builder
+Name[eo]=aRts-Sonkreilo
+Name[et]=aRtsi ehitaja
+Name[fa]=سازندۀ aRts
+Name[fi]=aRts-rakentaja
+Name[fr]=aRtsBuilder
+Name[gl]=Construtor aRts
+Name[hi]=एआरटीएस बिल्डर
+Name[hu]=aRts-modellező
+Name[is]=aRts-smiður
+Name[it]=Costruttore per aRts
+Name[ja]=aRts ビルダー
+Name[kk]=aRts құрастырғышы
+Name[km]=កម្មវិធី​បង្កើត aRts
+Name[ko]=aRts 빌더
+Name[lt]=aRts komponuoklis
+Name[lv]=aRts Būvētājs
+Name[mk]=Градител на aRts
+Name[nb]=aRts-bygger
+Name[nds]=aRts-Builder
+Name[ne]=aRts निर्माता
+Name[nn]=aRts-byggjar
+Name[pa]=aRts ਨਿਰਮਾਤਾ
+Name[pl]=Arts Builder
+Name[pt]=Construtor do aRts
+Name[pt_BR]=Construtor Arts
+Name[ru]=artsbuilder
+Name[se]=aRts-huksejeaddji
+Name[sl]=Graditelj aRts
+Name[sr]=Градитељ Rts-а
+Name[sr@Latn]=Graditelj Rts-a
+Name[sv]=Arts-byggare
+Name[ta]=aRts உருவாக்கி
+Name[tg]=Созандаи aRts
+Name[tr]=aRts
+Name[ven]=Mufhati wa aRts
+Name[xh]=Umakhi we aRts
+Name[zh_CN]=aRts 构建程序
+Name[zu]=Umakhi We aRts
+GenericName=Audio Filter Designer
+GenericName[af]=Audio Filter Ontwerper
+GenericName[bg]=Аудио дизайнер
+GenericName[bn]=অডিও ফিল্টার ডিসাইনার
+GenericName[br]=Ergrafer sil klevet
+GenericName[bs]=Dizajner audio filtera
+GenericName[ca]=Dissenyador de filtres àudio
+GenericName[cs]=Návrhář zvukových filtrů
+GenericName[cy]=Dylunydd Hidl Sain
+GenericName[da]=Audiofilter-designer
+GenericName[de]=Audiofilter-Designer
+GenericName[el]=Σχεδιαστής φίλτρων ήχου
+GenericName[eo]=Aŭdfiltrilo-desegnilo
+GenericName[es]=Diseñador de filtros de audio
+GenericName[et]=Audiofiltrite disainer
+GenericName[eu]=Audio iragazki diseinatzailea
+GenericName[fa]=طراح پالایۀ صوتی
+GenericName[fi]=Äänisuodattimien suunnitteluohjelma
+GenericName[fr]=Concepteur de filtre audio
+GenericName[ga]=Dearthóir Scagaire Fuaime
+GenericName[gl]=Deseñador de Filtros de Son
+GenericName[he]=מעצב מסנני שמע
+GenericName[hi]=ध्वनि फ़िल्टर डिजाइनर
+GenericName[hr]=Dizajner audio filtara
+GenericName[hu]=Hangszűrő-tervező
+GenericName[is]=Hljóðsíuhönnuður
+GenericName[it]=Disegnatore di filtro audio
+GenericName[ja]=オーディオフィルタデザイナー
+GenericName[kk]=Аудио сүзгі құрастырғышы
+GenericName[km]=កម្មវិធី​រចនាតម្រង​អូ​ឌីយូ​
+GenericName[ko]=오디오 필터 디자이너
+GenericName[lt]=Audio filtrų kūrimo priemonė
+GenericName[mk]=Изработувач на филтри за звук
+GenericName[ms]=Pereka Penapis Audio
+GenericName[nb]=Lydfilterdesigner
+GenericName[nds]=Klangfilter-Maker
+GenericName[ne]=अडियो फिल्टर डिजाइनर
+GenericName[nl]=Audiofilterontwerper
+GenericName[nn]=Utforming av lydfilter
+GenericName[pa]=ਆਡੀਓ ਫਿਲਟਰ ਡਿਜ਼ਾਈਨਰ
+GenericName[pl]=Projektant filtru audio
+GenericName[pt]=Editor de Filtros de Áudio
+GenericName[pt_BR]=Criador de Filtro de Áudio
+GenericName[ro]=Proiectare filtre audio
+GenericName[ru]=Дизайнер звуковых фильтров
+GenericName[se]=Jietnasilli hápmejeaddji
+GenericName[sk]=Návrh audio filtrov
+GenericName[sl]=Snovalnik avdio filtrov
+GenericName[sr]=Дизајнер аудио филтера
+GenericName[sr@Latn]=Dizajner audio filtera
+GenericName[sv]=Ljudfilterdesigner
+GenericName[ta]=கேட்பொலி வடிகட்டி படைப்பாளர்
+GenericName[tg]=Тарроҳи Полоягари Садо
+GenericName[th]=เครื่องมือออกแบบฟิลเตอร์ของเสียง
+GenericName[tr]=Ses Filtresi Tasarlayıcı
+GenericName[uk]=Дизайн аудіофільтрів
+GenericName[ven]=Muvhati wa filithara ino pfala
+GenericName[xh]=Umyili Wecebo lokucoca ulwelo Wesandi
+GenericName[zh_CN]=音频滤波器设计程序
+GenericName[zh_HK]=音訊過濾器設計師
+GenericName[zh_TW]=音效過濾器設計師
+GenericName[zu]=Umdwebi Wehluzo Lokuzwakalayo
+Exec=artsbuilder -caption "%c"
+Icon=artsbuilder
+Path=
+Type=Application
+Terminal=false
+MimeType=application/x-artsbuilder;
+DocPath=artsbuilder/index.html
+X-DCOP-ServiceType=Multi
+OnlyShowIn=KDE;
+Categories=Qt;KDE;AudioVideo;X-KDE-More;
diff --git a/arts/builder/artsbuilderui.rc b/arts/builder/artsbuilderui.rc
new file mode 100644
index 00000000..807d511f
--- /dev/null
+++ b/arts/builder/artsbuilderui.rc
@@ -0,0 +1,47 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="Artsbuilder" version="3">
+<MenuBar>
+ <Menu name="file"><text>&amp;File</text>
+ <Action name="file_open_example" append="new_merge"/>
+ <Action name="file_execute_structure"/>
+ <Action name="file_rename_structure"/>
+ <!-- currently unused
+ <Action name="file_open_session" append="new_merge"/>
+ <Action name="file_publish_structure"/>
+ <Action name="file_retrieve_from_server"/>
+ -->
+ </Menu>
+ <Menu name="edit"><text>&amp;Edit</text>
+ <Action name="edit_delete"/>
+ </Menu>
+ <Menu name="view"><text>&amp;View</text>
+ <Action name="view_properties"/>
+ <Separator/>
+ <Action name="view_200"/>
+ <Action name="view_150"/>
+ <Action name="view_100"/>
+ <Action name="view_50"/>
+ </Menu>
+ <Action name="modulesmenu"/>
+ <!--
+ <Menu name="modules"><text>&amp;Modules</text>
+ <Action name="modulesmenu"/>
+ <Separator/>
+ </Menu>
+ -->
+ <Menu name="ports"><text>&amp;Ports</text>
+ <Action name="ports_create_in_audio_signal"/>
+ <Action name="ports_create_out_audio_signal"/>
+ <Action name="ports_create_in_string_property"/>
+ <Action name="ports_create_in_audio_property"/>
+ <Separator/>
+ <Action name="ports_implement_interface"/>
+ <Separator/>
+ <Action name="ports_change_positions"/>
+ </Menu>
+</MenuBar>
+<ToolBar name="mainToolBar" ><text>Main Toolbar</text>
+ <Separator />
+ <Action name="file_execute_structure"/>
+</ToolBar>
+</kpartgui>
diff --git a/arts/builder/autorouter.cpp b/arts/builder/autorouter.cpp
new file mode 100644
index 00000000..bc69a7ee
--- /dev/null
+++ b/arts/builder/autorouter.cpp
@@ -0,0 +1,609 @@
+ /*
+
+ Copyright (C) 1998 Stefan Westerfeld <stefan@space.twc.de>,
+ 2002 Hans Meine <hans_meine@gmx.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "autorouter.h"
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <arts/debug.h>
+
+#include <kdebug.h>
+
+#ifndef HAVE_LIBPTHREAD
+#define pthread_create(a, b, c, d) ;
+#define pthread_join(a, b) ;
+#define pthread_mutex_lock(a) ;
+#define pthread_mutex_unlock(a) ;
+#define pthread_mutex_init(a, b) ;
+#define pthread_attr_init(a) ;
+#endif
+
+using namespace std;
+
+const int COST_INIT = 200000;
+const int PENALTY_OTHERCONNECTION = 100;
+const int PENALTY_CORNER = 100;
+const int IDLE_SLEEP_MILLISECONDS = 40;
+
+void *startroute(void *where)
+{
+ ((AutoRouter *)where)->thread_command_loop();
+ // just to prevent the compiler warning
+ return 0;
+}
+
+bool ARCommand::isDestructive()
+{
+ return false;
+}
+
+AutoRouter::AutoRouter(int width, int height)
+ : width(width), height(height)
+{
+#ifdef HAVE_LIBPTHREAD
+ pthread_attr_t attrs;
+
+ pthread_attr_init(&attrs);
+ pthread_mutex_init(&mutex_sync, 0);
+ pthread_mutex_init(&mutex_queue, 0);
+#endif
+
+ // allocate memory for fields..
+ field = new Field*[width];
+ completeField = new Field*[width];
+
+ for(int i = 0; i < width; i++)
+ {
+ field[i] = new Field[height];
+ completeField[i] = new Field[height];
+ }
+
+ // ..and clear the freshly allocated memory
+ thread_clear();
+ thread_sync();
+
+ newOwner = 1;
+
+ // init those two arrays - they should be constant somehow..
+ directionMask[DIR_DOWN] = ~(left | right);
+ directionMask[DIR_UP] = ~(left | right);
+ directionMask[DIR_LEFT] = ~(up | down);
+ directionMask[DIR_RIGHT] = ~(up | down);
+
+ ownerIndex[DIR_DOWN] = OWNER_UD;
+ ownerIndex[DIR_UP] = OWNER_UD;
+ ownerIndex[DIR_LEFT] = OWNER_LR;
+ ownerIndex[DIR_RIGHT] = OWNER_LR;
+
+ initPseudoRandom();
+
+ thread_terminate_now = false; // not yet
+#ifdef HAVE_LIBPTHREAD
+ // setup thread and run thread_command_loop() in it
+ pthread_create(&route_thread, &attrs, startroute, this);
+#endif
+
+ arts_debug("AR UP...");
+}
+
+void AutoRouter::initPseudoRandom()
+{
+ // init pseudoRandomDir array with random directions
+
+ int remainingDirs[4];
+ for(int i = 0; i < PRSIZE; i++)
+ {
+ if((i & 3) == 0)
+ {
+ int j = 0;
+ for(int dir = DIR_UP; dir <= DIR_RIGHT; dir++)
+ remainingDirs[j++] = dir;
+ }
+
+ int rnd;
+ do rnd = rand()&3;
+ while(remainingDirs[rnd] == DIR_NONE);
+
+ pseudoRandomDir[i] = remainingDirs[rnd];
+
+ remainingDirs[rnd] = DIR_NONE;
+ }
+ nextPR = 0;
+}
+
+AutoRouter::~AutoRouter()
+{
+ // tell the thread to shut down
+ pthread_mutex_lock(&mutex_queue);
+ thread_terminate_now = true;
+ pthread_mutex_unlock(&mutex_queue);
+
+ // terminate thread
+ void *rc;
+ pthread_join(route_thread, &rc);
+
+ // clean up
+ for(int i = 0; i < width; i++)
+ {
+ delete[] field[i];
+ delete[] completeField[i];
+ }
+ delete[] completeField;
+ delete[] field;
+
+ arts_debug("AR DOWN...");
+}
+
+void AutoRouter::enqueue(ARCommand *command)
+{
+#ifdef HAVE_LIBPTHREAD
+ // threaded execution, locking the queue
+ pthread_mutex_lock(&mutex_queue);
+
+ if(command->isDestructive())
+ {
+ // ok, then we can kill the whole list, since this will clear
+ // the whole results anyway
+
+ command_queue.setAutoDelete(true);
+ command_queue.clear();
+ command_queue.setAutoDelete(false);
+ }
+ command_queue.append(command);
+
+ pthread_mutex_unlock(&mutex_queue);
+#else
+ // immediate execution
+ command->execute(this);
+ delete command;
+#endif
+}
+
+void AutoRouter::thread_command_loop()
+{
+ while(1)
+ {
+ ARCommand *command = 0;
+
+ // pop one command from queue if possible..
+ pthread_mutex_lock(&mutex_queue);
+ if(!command_queue.isEmpty())
+ {
+ command = command_queue.first();
+ command_queue.remove(unsigned(0));
+ }
+ else
+ {
+ if (thread_terminate_now) {
+ pthread_mutex_unlock(&mutex_queue);
+ return;
+ }
+ }
+ pthread_mutex_unlock(&mutex_queue);
+
+ // ..and execute command if we got one
+ if(command)
+ {
+ command->execute(this);
+ delete command;
+ }
+ else // no more commands->wait for some milliseconds
+ usleep(1000 * IDLE_SLEEP_MILLISECONDS);
+ // TODO: use pthread_cond_wait here instead of half-busy-waiting?
+ }
+}
+
+// ------------------------- getXXX() handling -------------------------
+
+long AutoRouter::get(int x, int y)
+{
+ assert(x >= 0 && x < width);
+ assert(y >= 0 && y < height);
+
+ pthread_mutex_lock(&mutex_sync);
+ long result = completeField[x][y].data;
+ pthread_mutex_unlock(&mutex_sync);
+
+ return(result);
+}
+
+void AutoRouter::getowners(int x, int y, long& ud_owner, long& lr_owner)
+{
+ assert(x >= 0 && x < width);
+ assert(y >= 0 && y < height);
+
+ pthread_mutex_lock(&mutex_sync);
+ ud_owner = completeField[x][y].owner[OWNER_UD];
+ lr_owner = completeField[x][y].owner[OWNER_LR];
+ pthread_mutex_unlock(&mutex_sync);
+}
+
+// ------------------------- sync() handling -------------------------
+
+void AutoRouter::sync()
+{
+ enqueue(new ARSyncCommand());
+}
+
+void ARSyncCommand::execute(AutoRouter *router)
+{
+ router->thread_sync();
+}
+
+void AutoRouter::thread_sync()
+{
+ int i;
+ pthread_mutex_lock(&mutex_sync);
+
+ for(i = 0; i < width; i++)
+ memcpy(completeField[i], field[i], sizeof(Field)*height);
+ _needRedraw = true;
+
+ pthread_mutex_unlock(&mutex_sync);
+}
+
+bool AutoRouter::needRedraw()
+{
+ bool result;
+
+ pthread_mutex_lock(&mutex_sync);
+ result = _needRedraw;
+ _needRedraw = false;
+#ifdef AR_DEBUG
+ if(result) arts_debug("NEED REDRAW NOW!");
+#endif
+ pthread_mutex_unlock(&mutex_sync);
+
+ return result;
+}
+
+// ------------------------- clear() handling -------------------------
+
+void AutoRouter::clear()
+{
+ enqueue(new ARClearCommand());
+}
+
+bool ARClearCommand::isDestructive()
+{
+ return true;
+}
+
+void ARClearCommand::execute(AutoRouter *router)
+{
+ router->thread_clear();
+}
+
+void AutoRouter::thread_clear()
+{
+ arts_debug("clear()ing now...");
+ int x, y;
+
+ for(x = 0; x < width; x++)
+ for(y = 0; y < height; y++)
+ {
+ field[x][y].data = none;
+ field[x][y].penalty = 0;
+ field[x][y].owner[0] = -1;
+ field[x][y].owner[1] = -1;
+ }
+}
+
+// ------------------------- set() command handling -------------------------
+
+void AutoRouter::set(int x1, int y1, int x2, int y2, long lt)
+{
+ enqueue(new ARSetCommand(x1, y1, x2, y2, lt));
+}
+
+ARSetCommand::ARSetCommand(int x1, int y1, int x2, int y2, long lt)
+ : _x1(x1), _y1(y1), _x2(x2), _y2(y2), _lt(lt)
+{
+}
+
+void ARSetCommand::execute(AutoRouter *router)
+{
+ router->thread_set(_x1, _y1, _x2, _y2, _lt);
+}
+
+void AutoRouter::thread_set(int x1, int y1, int x2, int y2, long lt)
+{
+ for(int x = x1; x <= x2; x++)
+ {
+ for(int y = y1; y <= y2; y++)
+ {
+ assert(x >= 0 && x < width);
+ assert(y >= 0 && y < height);
+
+ if(lt & solid)
+ {
+ if((y - 1) >= 0)
+ field[x][y - 1].penalty += 5;
+
+ if((y - 2) >= 0)
+ field[x][y - 2].penalty += 2;
+
+ if((y + 1) < height)
+ field[x][y + 1].penalty += 5;
+
+ if((y + 2) < height)
+ field[x][y + 2].penalty += 2;
+ }
+
+ field[x][y].data = lt;
+ field[x][y].owner[0] = 0;
+ field[x][y].owner[1] = 0; // don't pass
+ }
+ }
+}
+
+
+long AutoRouter::connect(int x1, int y1, int x2, int y2, long owner)
+{
+ if(owner == 0)
+ owner = newOwner++;
+
+ enqueue(new ARConnectCommand(x1, y1, x2, y2, owner));
+
+ return owner;
+}
+
+ARConnectCommand::ARConnectCommand(int x1, int y1, int x2, int y2, long owner)
+{
+ _x1 = x1; _y1 = y1; _x2 = x2; _y2 = y2; _owner = owner;
+}
+
+void ARConnectCommand::execute(AutoRouter *router)
+{
+ router->thread_connect(_x1, _y1, _x2, _y2, _owner);
+}
+
+void AutoRouter::thread_connect(int x1, int y1, int x2, int y2, long owner)
+{
+ currentOwner = owner;
+
+#ifdef AR_DEBUG
+ arts_debug("-field[x1][y1].owner[0..1] = %ld, %ld",field[x1][y1].owner[0],
+ field[x1][y1].owner[1]);
+ arts_debug("-field[x2][y2].owner[0..1] = %ld, %ld", field[x2][y2].owner[0],
+ field[x2][y2].owner[1]);
+#endif
+
+ // clear data(source) & data(dest) first and restore later, since
+ // they might be solid
+ long sourceFieldData = field[x1][y1].data; field[x1][y1].data = none;
+ long destFieldData = field[x2][y2].data; field[x2][y2].data = none;
+
+ for(int x = 0; x < width; x++)
+ for(int y = 0; y < height; y++)
+ field[x][y].minCost = COST_INIT;
+
+#ifdef AR_DEBUG
+ arts_debug("autorouter: trying to connect %d, %d with %d, %d (owner %ld)",
+ x1, y1, x2, y2, owner);
+#endif
+ nextPR = 0;
+
+ bestGoalPath.cost = COST_INIT;
+
+ int activelist = 0;
+ numQueuedPaths = 0;
+
+ PathInfo path;
+ path.x1 = x1;
+ path.x2 = x2;
+ path.y1 = y1;
+ path.y2 = y2;
+ path.cost = 0;
+ path.depth = 0;
+ queuePath(path);
+
+ while(numQueuedPaths)
+ {
+ while(!pathlist[activelist].size())
+ activelist++;
+ PathQueue& activePathList =
+ pathlist[activelist];
+
+ assert(activePathList.size());
+ examinePath(activePathList.first());
+ activePathList.pop_front();
+ numQueuedPaths--;
+ }
+
+ field[x1][y1].data = sourceFieldData;
+ field[x2][y2].data = destFieldData;
+
+ if(bestGoalPath.cost != COST_INIT)
+ {
+ //arts_debug("bestGoalPath.history for this connection is %s", bestGoalPath.history.data());
+ //arts_debug("minCost for that was %d", gms);
+
+ const char *walk = bestGoalPath.history.ascii();
+
+ int x = x1;
+ int y = y1;
+
+ while(*walk)
+ {
+ field[x][y].owner[ownerIndex[*walk]] = currentOwner;
+ switch(*walk)
+ {
+ case DIR_DOWN:
+ field[x][y++].data |= down;
+ field[x][y].data |= up;
+ break;
+ case DIR_UP:
+ field[x][y--].data |= up;
+ field[x][y].data |= down;
+ break;
+ case DIR_LEFT:
+ field[x--][y].data |= left;
+ field[x][y].data |= right;
+ break;
+ case DIR_RIGHT:
+ field[x++][y].data |= right;
+ field[x][y].data |= left;
+ break;
+ }
+ field[x][y].owner[ownerIndex[*walk]] = currentOwner;
+ walk++;
+ }
+ }
+ else
+ {
+#ifdef AR_DEBUG
+ arts_debug("!! sorry, this connection is impossible !!");
+#endif
+ }
+}
+
+void AutoRouter::queuePath(const PathInfo &path)
+{
+ PathInfo newPath = path;
+
+ int targetlist = newPath.cost/5;
+ if(targetlist > 1023)
+ targetlist = 1023;
+
+ pathlist[targetlist].append(newPath);
+ qHeapSort(pathlist[targetlist]);
+
+ numQueuedPaths++;
+}
+
+void AutoRouter::examinePath(const PathInfo &path)
+{
+ const char *walk = path.history.ascii();
+
+// check if we can go here:
+
+ if(path.x1 < 0 || path.x1 >= width)
+ return;
+ if(path.y1 < 0 || path.y1 >= width)
+ return;
+
+ int currentFieldCost = path.cost;
+
+ if(path.depth > 0)
+ {
+ // going over a field where already connections are is bad
+ if(field[path.x1][path.y1].data != 0)
+ currentFieldCost += PENALTY_OTHERCONNECTION;
+
+ if(directionMask[walk[path.depth - 1]] & field[path.x1][path.y1].data)
+ {
+ // someone already uses that field... we can continue
+ // only if the connection has the same sourceport
+
+ long fieldowner = field[path.x1][path.y1].owner[ownerIndex[walk[path.depth - 1]]];
+
+ if(fieldowner != -1) // used?
+ {
+ if(fieldowner != currentOwner)
+ return;
+
+ // oops, the connections are from the same owner -> no
+ // penalty needed!
+ currentFieldCost -= PENALTY_OTHERCONNECTION;
+ }
+ }
+ }
+
+ // add cityblock distance to costs
+ currentFieldCost += abs(path.x1 - path.x2) + abs(path.y1 - path.y2);
+
+ // add field penalty to costs
+ currentFieldCost += field[path.x1][path.y1].penalty;
+
+ // add corner penalty to costs if path had corner here
+ if(path.depth > 2)
+ if(walk[path.depth - 2] != walk[path.depth - 1])
+ currentFieldCost += PENALTY_CORNER;
+
+ if(currentFieldCost > bestGoalPath.cost)
+ return;
+
+ // better path found?
+ if(currentFieldCost < field[path.x1][path.y1].minCost)
+ {
+ field[path.x1][path.y1].minCost = currentFieldCost;
+
+ // check if we are where we wanted to be:
+ if(path.x1 == path.x2 && path.y1 == path.y2) {
+ // goal! success! :-)
+
+ if(path.cost < bestGoalPath.cost)
+ {
+ bestGoalPath = path; // best solution until now
+ bestGoalPath.cost = currentFieldCost;
+ }
+
+ return;
+ }
+
+ // not at the goal yet, search next place to go; take some
+ // pseudo random direction order (this method improves search
+ // speed)
+
+ PathInfo newPath = path;
+ newPath.depth++;
+ newPath.cost = currentFieldCost;
+
+ for(int i = 0; i < 4; i++)
+ {
+ if(nextPR >= PRSIZE)
+ nextPR = 0;
+
+ switch(pseudoRandomDir[nextPR++])
+ {
+ case DIR_LEFT:
+ newPath.x1--;
+ newPath.history = path.history + (char)DIR_LEFT;
+ queuePath(newPath);
+ newPath.x1++;
+ break;
+
+ case DIR_RIGHT:
+ newPath.x1++;
+ newPath.history = path.history + (char)DIR_RIGHT;
+ queuePath(newPath);
+ newPath.x1--;
+ break;
+
+ case DIR_UP:
+ newPath.y1--;
+ newPath.history = path.history + (char)DIR_UP;
+ queuePath(newPath);
+ newPath.y1++;
+ break;
+
+ case DIR_DOWN:
+ newPath.y1++;
+ newPath.history = path.history + (char)DIR_DOWN;
+ queuePath(newPath);
+ newPath.y1--;
+ break;
+ }
+ }
+ }
+}
diff --git a/arts/builder/autorouter.h b/arts/builder/autorouter.h
new file mode 100644
index 00000000..df3780a0
--- /dev/null
+++ b/arts/builder/autorouter.h
@@ -0,0 +1,184 @@
+/*
+
+ Copyright (C) 1998 Stefan Westerfeld <stefan@space.twc.de>,
+ 2002 Hans Meine <hans_meine@gmx.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef __AUTOROUTER_H_
+#define __AUTOROUTER_H_
+
+// If you get into trouble with threading (random crashes), you can configure
+// things with --disable-threading, which should fix everything by not using
+// threads
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_LIBPTHREAD
+#include <pthread.h>
+#endif
+
+#include <qptrlist.h>
+#include <qvaluelist.h>
+
+class PathInfo
+{
+public:
+ int x1, x2, y1, y2, cost, depth;
+ QString history;
+ int operator<(const PathInfo& x) const { return cost < x.cost; }
+ int operator==(const PathInfo& x) const { return cost == x.cost; }
+};
+
+typedef QValueList<PathInfo> PathQueue;
+
+class ARCommand;
+
+/**
+ * The AutoRouter uses threads, commands are passed as objects via
+ * AutoRouter::enqueue() to the routing thread.
+ */
+class AutoRouter
+{
+public:
+ enum { none=0,up=1,down=2,left=4,right=8,head=16,tail=32,solid=64 };
+ enum Direction { DIR_NONE=0, DIR_UP, DIR_DOWN, DIR_LEFT, DIR_RIGHT };
+
+protected:
+ int width, height;
+
+ enum OwnerType { OWNER_UD=0, OWNER_LR=1 };
+ OwnerType ownerIndex[DIR_RIGHT + 1]; // index is of type Direction
+ long directionMask[DIR_RIGHT + 1]; // index is of type Direction
+
+ struct Field
+ {
+ long data;
+ long minCost;
+ long penalty;
+ long owner[2];
+ } **field, **completeField;
+
+ long newOwner; // next free owner ID
+ long currentOwner;
+
+ bool _needRedraw;
+
+ PathInfo bestGoalPath;
+ PathQueue pathlist[1024];
+ int numQueuedPaths;
+
+ // pseudo random table for fast "random" decisions
+ enum { PRSIZE = 16 };
+ long pseudoRandomDir[PRSIZE];
+ int nextPR;
+ void initPseudoRandom();
+
+/****** thread stuff *****/
+#ifdef HAVE_LIBPTHREAD
+ pthread_mutex_t mutex_sync;
+ pthread_mutex_t mutex_queue;
+
+ pthread_t route_thread;
+#endif
+ QPtrList<ARCommand> command_queue;
+
+ bool thread_terminate_now;
+/*************************/
+
+ void queuePath(const PathInfo &path);
+ void examinePath(const PathInfo &path);
+
+public:
+ AutoRouter(int width, int height);
+ ~AutoRouter();
+
+ // queries _needRedraw flag and deletes it
+ // (assuming that the client is smart and redraws when getting true ;)
+ bool needRedraw();
+
+ long get(int x, int y);
+ void getowners(int x, int y, long& ud_owner, long& lr_owner);
+
+ void enqueue(ARCommand *command);
+
+ // marks the entire field as unused
+ void clear();
+ // sets the
+ void set(int x1, int y1, int x2, int y2, long lt);
+ long connect(int x1, int y1, int x2, int y2, long owner);
+ //
+ void sync();
+
+ // the following functions are not for direct use; they're used
+ // for threading only
+ void thread_clear();
+ void thread_set(int x1, int y1, int x2, int y2, long lt);
+ void thread_connect(int x1, int y1, int x2, int y2, long owner);
+ void thread_sync();
+
+ void thread_command_loop();
+};
+
+/**
+ * The ARCommand classes are used to communicate with the routing
+ * thread, see AutoRouter::enqueue()
+ */
+class ARCommand
+{
+public:
+ virtual void execute(AutoRouter *autorouter) = 0;
+ // if a command is destructive (default: false), the command queue
+ // will be emptied before queuing this one, assuming it'll destroy
+ // results of all other commands anyway.
+ virtual bool isDestructive();
+};
+
+class ARClearCommand :public ARCommand
+{
+public:
+ void execute(AutoRouter *autorouter);
+ bool isDestructive();
+};
+
+class ARSyncCommand :public ARCommand
+{
+public:
+ void execute(AutoRouter *autorouter);
+};
+
+class ARConnectCommand :public ARCommand
+{
+ int _x1, _y1, _x2, _y2;
+ long _owner;
+public:
+ ARConnectCommand(int x1, int y1, int x2, int y2, long owner);
+ void execute(AutoRouter *autorouter);
+};
+
+class ARSetCommand :public ARCommand
+{
+private:
+ int _x1, _y1, _x2, _y2;
+ long _lt;
+public:
+ ARSetCommand(int x1, int y1, int x2, int y2, long lt);
+ void execute(AutoRouter *autorouter);
+};
+
+#endif
diff --git a/arts/builder/createtool.cpp b/arts/builder/createtool.cpp
new file mode 100644
index 00000000..a96edc6d
--- /dev/null
+++ b/arts/builder/createtool.cpp
@@ -0,0 +1,339 @@
+ /*
+
+ Copyright (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "createtool.h"
+
+#include "mwidget.h"
+#include "structureport.h"
+
+//#include <arts/debug.h>
+#include <qpalette.h>
+#include <qpainter.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+
+MWidgetTool::MWidgetTool(ModuleWidget *mwidget)
+{
+ this->mwidget = mwidget;
+}
+
+MWidgetTool::~MWidgetTool()
+{
+}
+
+CreateTool::CreateTool(ModuleWidget *mwidget)
+ : MWidgetTool(mwidget)
+{
+ oldCursor = mwidget->cursor();
+ mwidget->setCursor(Qt::crossCursor);
+
+ oldMouseTracking = mwidget->hasMouseTracking();
+ mwidget->setMouseTracking(true);
+
+ width = -1;
+ height = -1;
+ componentRect = QRect(0, 0, 0, 0);
+}
+
+CreateTool::~CreateTool()
+{
+ mwidget->repaint(componentRect);
+ mwidget->setCursor(oldCursor);
+ mwidget->setMouseTracking(oldMouseTracking);
+}
+
+void CreateTool::mousePressEvent(QMouseEvent *e)
+{
+ int x = mwidget->findCol(e->x());
+ int y = mwidget->findRow(e->y());
+
+ if(x < 0 || y < 0) return;
+
+ if( e->button() == Qt::LeftButton )
+ performCreate(x, y);
+
+ // well, perhaps make this an option
+ mwidget->leaveTool(this);
+}
+
+void CreateTool::mouseMoveEvent(QMouseEvent *e)
+{
+ if(width == -1) estimateSize();
+
+ int x = mwidget->findCol(e->x());
+ int y = mwidget->findRow(e->y());
+
+ if(x < 0 || y < 0) return;
+
+ int cellx, celly, cellx2, celly2;
+
+ bool posok = mwidget->colXPos(x, &cellx) && mwidget->rowYPos(y, &celly)
+ && mwidget->colXPos(x + width, &cellx2) && mwidget->rowYPos(y + height, &celly2);
+ if(!posok) return;
+
+ mwidget->repaint(componentRect);
+ componentRect = QRect(cellx, celly, cellx2 - cellx, celly2 - celly);
+
+ QPainter *p = new QPainter;
+
+ p->begin(mwidget);
+ p->save();
+ p->drawRect(componentRect);
+ p->restore();
+ p->end();
+ delete p;
+}
+
+void CreateTool::mouseReleaseEvent(QMouseEvent *e)
+{
+}
+
+// module creation
+
+void CreateModuleTool::estimateSize()
+{
+// yes, this is a it ugly: we create here the new module for a microsecond,
+// just to see how big it is, to be able to paint an accurate preview
+//
+// We delete it again, and if the user finally really creates the module
+// we recreate it.
+
+ mwidget->unselectAll();
+
+ Module *newModule = mwidget->theStructure()->createModule(minfo);
+ width = newModule->width();
+ height = newModule->height();
+ newModule->setSelected(true);
+
+ mwidget->theStructure()->deleteSelected();
+}
+
+void CreateModuleTool::performCreate(int x, int y)
+{
+ mwidget->unselectAll();
+ Module *newModule = mwidget->theStructure()->createModule(minfo);
+ newModule->setSelected(true);
+ newModule->move(x, y);
+
+ if(!mwidget->insertModule(newModule))
+ mwidget->theStructure()->deleteSelected();
+}
+
+CreateModuleTool::CreateModuleTool(ModuleWidget *mwidget,
+ const Arts::ModuleInfo& minfo) : CreateTool(mwidget)
+{
+ this->minfo = minfo;
+}
+
+CreateModuleTool::~CreateModuleTool()
+{
+}
+
+// interface creation
+
+void CreateInterfaceTool::estimateSize()
+{
+ vector<Arts::PortType>::iterator pi;
+
+ int input_width = 0;
+ int output_width = 0;
+
+ for(pi = minfo.ports.begin(); pi != minfo.ports.end(); ++pi)
+ {
+ if(pi->direction == Arts::input)
+ input_width++;
+
+ if(pi->direction == Arts::output)
+ output_width++;
+ }
+ width = max(input_width, output_width);
+ height = 5;
+}
+
+void CreateInterfaceTool::performCreate(int x, int y)
+{
+ vector<std::string>::iterator pni;
+ vector<Arts::PortType>::iterator pi;
+
+ mwidget->theStructure()->addInheritedInterface(minfo.name.c_str());
+
+ int input_width = 0;
+ int output_width = 0;
+
+ for(pi = minfo.ports.begin(), pni = minfo.portnames.begin();
+ pi != minfo.ports.end(); ++pi, pni++)
+ {
+ StructurePort *port = 0;
+ /*
+ * watch out: input ports (as in ports that accept data) are on the lower
+ * side of the structure, as the data flows out trough them ;)
+ */
+ if(pi->direction == Arts::input)
+ {
+ port = mwidget->insertPort(*pi, x + input_width, y + 4);
+ input_width++;
+ }
+ else if(pi->direction == Arts::output)
+ {
+ port = mwidget->insertPort(*pi, x + output_width, y);
+ output_width++;
+ }
+
+ assert(port);
+ port->rename(pni->c_str());
+ port->inheritedInterface(minfo.name.c_str());
+ }
+}
+
+CreateInterfaceTool::CreateInterfaceTool(ModuleWidget *mwidget,
+ const Arts::ModuleInfo& minfo) : CreateTool(mwidget)
+{
+ this->minfo = minfo;
+
+ vector<Arts::PortType>::iterator pi;
+ for(pi = this->minfo.ports.begin(); pi != this->minfo.ports.end(); ++pi)
+ {
+ /* reverse since we're inside the interface, not outside */
+ if(pi->direction == Arts::input)
+ pi->direction = Arts::output;
+ else if(pi->direction == Arts::output)
+ pi->direction = Arts::input;
+ }
+}
+
+CreateInterfaceTool::~CreateInterfaceTool()
+{
+}
+
+// port creation
+
+void CreatePortTool::estimateSize()
+{
+ width = height = 1;
+}
+
+void CreatePortTool::performCreate(int x, int y)
+{
+ mwidget->insertPort(type, x, y);
+}
+
+CreatePortTool::CreatePortTool(ModuleWidget *mwidget,
+ const Arts::PortType& type) : CreateTool(mwidget)
+{
+ this->type = type;
+}
+
+MoveComponentsTool::MoveComponentsTool(ModuleWidget *widget, QMouseEvent *e)
+ : MWidgetTool(widget),
+ lastPos(e->pos())
+{
+}
+
+void MoveComponentsTool::mousePressEvent(QMouseEvent *e)
+{
+}
+
+void MoveComponentsTool::mouseMoveEvent(QMouseEvent *e)
+{
+ int dx = (e->x() - lastPos.x())/mwidget->cellsize;
+ int dy = (e->y() - lastPos.y())/mwidget->cellsize;
+
+ if(dx == 0 && dy == 0)
+ return;
+
+ std::list<StructureComponent *>::iterator i;
+ std::list<StructureComponent *> *ComponentList = mwidget->structure->getComponentList();
+
+ for(i = ComponentList->begin();i != ComponentList->end();++i)
+ {
+ StructureComponent *c = *i;
+ if(c->selected() && !mwidget->hasSpace(c, c->x() + dx, c->y() + dy, true))
+ return;
+ }
+
+ mwidget->beginUpdate();
+ for(i = ComponentList->begin();i != ComponentList->end();++i)
+ {
+ StructureComponent *c = *i;
+ if(c->selected())
+ c->move(c->x() + dx, c->y() + dy);
+ }
+ mwidget->endUpdate();
+
+ lastPos.setX(lastPos.x() + dx*mwidget->cellsize);
+ lastPos.setY(lastPos.y() + dy*mwidget->cellsize);
+ mwidget->reRoute();
+}
+
+void MoveComponentsTool::mouseReleaseEvent(QMouseEvent *e)
+{
+ mwidget->leaveTool(this, true);
+}
+
+ConnectPortsTool::ConnectPortsTool(ModuleWidget *widget, ModulePort *connectingPort)
+ : MWidgetTool(widget),
+ connectingPort(connectingPort)
+{
+ firstPos = mwidget->portPos(connectingPort) + connectingPort->clickrect.center();
+}
+
+void ConnectPortsTool::mousePressEvent(QMouseEvent *e)
+{
+}
+
+void ConnectPortsTool::mouseMoveEvent(QMouseEvent *e)
+{
+ QPainter painter(mwidget);
+ painter.setPen(Qt::white);
+ mwidget->repaint(QRect(firstPos, lastPos).normalize());
+ painter.drawLine(firstPos, e->pos());
+ lastPos = e->pos();
+}
+
+void ConnectPortsTool::mouseReleaseEvent(QMouseEvent *e)
+{
+ StructureComponent *component;
+ ModulePort *otherPort;
+ mwidget->findAt(e->x(), e->y(), component, otherPort);
+ if(otherPort && (otherPort!= connectingPort))
+ {
+ // user is trying to close a connection
+ if(connectingPort->direction == otherPort->direction)
+ {
+ KMessageBox::sorry(mwidget,
+ i18n("You can only connect an IN-port with an OUT-port,\n"
+ "not two ports with the same direction."));
+ }
+ else
+ {
+ if(connectingPort->direction == ModulePort::in)
+ {
+ if(otherPort->PortDesc.connectTo(connectingPort->PortDesc))
+ mwidget->reRoute();
+ } else {
+ if(connectingPort->PortDesc.connectTo(otherPort->PortDesc))
+ mwidget->reRoute();
+ }
+ }
+ }
+ mwidget->repaint(QRect(firstPos, lastPos).normalize());
+ mwidget->leaveTool(this);
+}
diff --git a/arts/builder/createtool.h b/arts/builder/createtool.h
new file mode 100644
index 00000000..3796e018
--- /dev/null
+++ b/arts/builder/createtool.h
@@ -0,0 +1,132 @@
+ /*
+
+ Copyright (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef _CREATETOOL_H
+#define _CREATETOOL_H
+
+#include <qwidget.h>
+#include <qcursor.h>
+
+#include "structure.h"
+#include "module.h"
+#include "scomponent.h"
+
+class ModulePort;
+class ModuleWidget;
+
+class MWidgetTool
+{
+protected:
+ ModuleWidget *mwidget;
+
+public:
+ MWidgetTool(ModuleWidget *mwidget);
+ virtual ~MWidgetTool();
+
+ virtual void mousePressEvent(QMouseEvent *e) = 0;
+ virtual void mouseMoveEvent(QMouseEvent *e) = 0;
+ virtual void mouseReleaseEvent(QMouseEvent *e) = 0;
+};
+
+class CreateTool: public MWidgetTool
+{
+protected:
+ QCursor oldCursor;
+ bool oldMouseTracking;
+ QRect componentRect;
+ int width, height;
+
+public:
+ CreateTool(ModuleWidget *mwidget);
+ virtual ~CreateTool();
+
+ virtual void estimateSize() = 0;
+ virtual void performCreate(int x, int y) = 0;
+
+ void mousePressEvent(QMouseEvent *e);
+ void mouseMoveEvent(QMouseEvent *e);
+ void mouseReleaseEvent(QMouseEvent *e);
+};
+
+class CreateModuleTool: public CreateTool
+{
+protected:
+ Arts::ModuleInfo minfo;
+
+public:
+ CreateModuleTool(ModuleWidget *mwidget, const Arts::ModuleInfo& minfo);
+ ~CreateModuleTool();
+
+ void estimateSize();
+ void performCreate(int x, int y);
+};
+
+class CreateInterfaceTool: public CreateTool
+{
+protected:
+ Arts::ModuleInfo minfo;
+
+public:
+ CreateInterfaceTool(ModuleWidget *mwidget, const Arts::ModuleInfo& minfo);
+ ~CreateInterfaceTool();
+
+ void estimateSize();
+ void performCreate(int x, int y);
+};
+
+class CreatePortTool: public CreateTool
+{
+protected:
+ Arts::PortType type;
+
+public:
+ CreatePortTool(ModuleWidget *widget, const Arts::PortType& type);
+
+ void estimateSize();
+ void performCreate(int x, int y);
+};
+
+class MoveComponentsTool: public MWidgetTool
+{
+ QPoint lastPos;
+
+public:
+ MoveComponentsTool(ModuleWidget *widget, QMouseEvent *e);
+
+ void mousePressEvent(QMouseEvent *e);
+ void mouseMoveEvent(QMouseEvent *e);
+ void mouseReleaseEvent(QMouseEvent *e);
+};
+
+class ConnectPortsTool: public MWidgetTool
+{
+ ModulePort *connectingPort;
+ QPoint firstPos, lastPos;
+
+public:
+ ConnectPortsTool(ModuleWidget *widget, ModulePort *connectingPort);
+
+ void mousePressEvent(QMouseEvent *e);
+ void mouseMoveEvent(QMouseEvent *e);
+ void mouseReleaseEvent(QMouseEvent *e);
+};
+
+#endif
diff --git a/arts/builder/dirmanager.cpp b/arts/builder/dirmanager.cpp
new file mode 100644
index 00000000..80a2e0ff
--- /dev/null
+++ b/arts/builder/dirmanager.cpp
@@ -0,0 +1,96 @@
+ /*
+
+ Copyright (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <qfile.h>
+
+#include <kstandarddirs.h>
+#include <kmessagebox.h>
+#include <kapplication.h>
+#include <klocale.h>
+
+#include "dirmanager.h"
+
+using namespace std;
+
+const char *DirManager::mapDir()
+{
+ static char *d = 0;
+
+ if(!d) d = directory("/maps",i18n("instrument map files"));
+ return d;
+}
+
+const char *DirManager::sessionDir()
+{
+ static char *d = 0;
+
+ if(!d) d = directory("/sessions",
+ i18n("sessions (save files of the positions of all sliders/buttons)"));
+
+ return d;
+}
+
+const char *DirManager::structureDir()
+{
+ static char *d = 0;
+
+ if(!d) d = directory("/structures",i18n("structures (signal flow graphs)"));
+ return d;
+}
+
+const char *DirManager::baseDir()
+{
+ static char *d = 0;
+
+ if(!d) d = directory("",i18n("all aRts files/folders"));
+ return d;
+}
+
+char *DirManager::directory(const char *subdir, const QString &desc)
+{
+ const char *home = getenv("HOME");
+
+ if(home == 0) return strdup("");
+
+ QCString dirname = QCString(home) + "/arts" + subdir;
+
+ struct stat buf;
+ if(stat(dirname.data(), &buf) == -1)
+ {
+ QString message;
+ QString dir = QFile::decodeName(dirname);
+ message = i18n("You need the folder %1.\n"
+ "It will be used to store %2.\nShould I create it now?")
+ .arg(dir).arg(desc);
+
+ if(KMessageBox::questionYesNo(0,message,i18n("aRts Folder Missing"),i18n("Create Folder"),i18n("Do Not Create"))
+ == KMessageBox::Yes)
+ {
+ KStandardDirs::makeDir(dir);
+ }
+ }
+
+ return strdup(dirname.data());
+}
diff --git a/arts/builder/dirmanager.h b/arts/builder/dirmanager.h
new file mode 100644
index 00000000..6ae070de
--- /dev/null
+++ b/arts/builder/dirmanager.h
@@ -0,0 +1,34 @@
+ /*
+
+ Copyright (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef _DIRMANAGER_H_
+#define _DIRMANAGER_H_
+
+class DirManager {
+protected:
+ static char *directory(const char *subdir, const QString& desc);
+public:
+ static const char *mapDir();
+ static const char *sessionDir();
+ static const char *structureDir();
+ static const char *baseDir();
+};
+#endif
diff --git a/arts/builder/drawutils.cpp b/arts/builder/drawutils.cpp
new file mode 100644
index 00000000..144a8016
--- /dev/null
+++ b/arts/builder/drawutils.cpp
@@ -0,0 +1,39 @@
+ /*
+
+ Copyright (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "drawutils.h"
+
+QString DrawUtils::cropText(QPainter *p, QString text, int maxlen, int& textwidth)
+{
+ QString label = text;
+
+ while(p->fontMetrics().width(label) > maxlen && label.length() > 0) {
+ int i = label.find('_');
+
+ if(i != -1)
+ label = label.mid(i+1);
+ else
+ label = label.left(label.length() - 1);
+ }
+
+ textwidth = p->fontMetrics().width(label);
+ return label;
+}
diff --git a/arts/builder/drawutils.h b/arts/builder/drawutils.h
new file mode 100644
index 00000000..3b1a6282
--- /dev/null
+++ b/arts/builder/drawutils.h
@@ -0,0 +1,31 @@
+ /*
+
+ Copyright (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef __DRAWUTILS_H__
+#define __DRAWUTILS_H__
+
+#include <qpainter.h>
+
+class DrawUtils {
+public:
+ static QString cropText(QPainter *p, QString text, int maxlen, int& textwidth);
+};
+#endif
diff --git a/arts/builder/execdlg.cpp b/arts/builder/execdlg.cpp
new file mode 100644
index 00000000..2186d1f5
--- /dev/null
+++ b/arts/builder/execdlg.cpp
@@ -0,0 +1,201 @@
+ /*
+
+ Copyright (C) 1998 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include <qfile.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qbuttongroup.h>
+#include <qradiobutton.h>
+#include <qlineedit.h>
+
+#include <kbuttonbox.h>
+#include <kfiledialog.h>
+#include <kseparator.h>
+#include <klocale.h>
+#include <kapplication.h>
+#include <kstdguiitem.h>
+
+#include <unistd.h>
+#include "execdlg.h"
+#include "dirmanager.h"
+#include <arts/debug.h>
+#include <qpushbutton.h>
+
+#ifndef KDE_USE_FINAL
+static void min_size(QWidget *w) {
+ w->setMinimumSize(w->sizeHint());
+}
+#endif
+
+ExecDlg::ExecDlg(QWidget *parent, ExecutableStructure *structure)
+ :QDialog(parent,"X")
+/*, TRUE)*/
+{
+ this->structure = structure;
+
+ setCaption(i18n("aRts Module Execution"));
+
+ mainlayout = new QVBoxLayout(this);
+
+// caption label: "Synthesis running..."
+
+ mainlayout->addSpacing(5);
+ QLabel *captionlabel = new QLabel(this);
+ QFont labelfont(captionlabel->font());
+ labelfont.setPointSize(labelfont.pointSize()*3/2);
+ captionlabel->setFont(labelfont);
+ captionlabel->setText(QString(" ")+i18n("Synthesis running...")+QString(" "));
+ captionlabel->setAlignment(AlignCenter);
+ min_size(captionlabel);
+ mainlayout->addWidget(captionlabel);
+
+ cpuusagelabel = new QLabel(this);
+ cpuusagelabel->setText(i18n("CPU usage: unknown"));
+
+ cpuusagetimer = new QTimer( this );
+ connect( cpuusagetimer, SIGNAL(timeout()),
+ this, SLOT(updateCpuUsage()) );
+ connect( cpuusagetimer, SIGNAL(timeout()),
+ this, SLOT(guiServerTick()) );
+ cpuusagetimer->start( 2000, false );
+
+ min_size(cpuusagelabel);
+ mainlayout->addWidget(cpuusagelabel);
+
+// ruler above the sliderlayout
+
+ mainlayout->addSpacing(5);
+ KSeparator* sep = new KSeparator( KSeparator::HLine, this);
+ mainlayout->addWidget(sep);
+ mainlayout->addSpacing(5);
+
+// sliders, controlpanels
+
+ sliderlayout = new QVBoxLayout;
+ mainlayout->addLayout(sliderlayout);
+
+#if 0 /* PORT */
+ this->GUIServer = GUIServer;
+ GUIServer->setGlobalParent(this);
+ GUIServer->setGlobalLayout(sliderlayout);
+#endif
+
+// hruler below the sliderlayout
+
+ mainlayout->addSpacing(5);
+ sep = new KSeparator( KSeparator::HLine, this);
+ mainlayout->addWidget(sep);
+ mainlayout->addSpacing(5);
+
+// buttons
+
+ QHBoxLayout *buttonlayout = new QHBoxLayout;
+ mainlayout->addSpacing(5);
+ mainlayout->addLayout(buttonlayout);
+ mainlayout->addSpacing(5);
+
+ buttonlayout->addSpacing(5);
+ KButtonBox *bbox = new KButtonBox(this);
+
+ bbox->addButton(KStdGuiItem::help(), this, SLOT( help() ));
+
+ bbox->addStretch(1);
+
+ QButton *savebutton = bbox->addButton(KStdGuiItem::saveAs());
+ connect( savebutton, SIGNAL( clicked() ), SLOT(saveSession() ) );
+
+ QButton *okbutton = bbox->addButton(KStdGuiItem::ok());
+ connect( okbutton, SIGNAL( clicked() ), SLOT(accept() ) );
+
+ bbox->layout();
+ //min_size(bbox);
+
+ buttonlayout->addWidget(bbox);
+ buttonlayout->addSpacing(5);
+
+// mainlayout->freeze();
+}
+
+void ExecDlg::start()
+{
+ mainlayout->freeze();
+}
+
+void ExecDlg::guiServerTick()
+{
+#if 0 /* TODO:PORT */
+ GUIServer->tick();
+#endif
+}
+
+void ExecDlg::updateCpuUsage()
+{
+#if 0 /* TODO:PORT */
+ char cpuusage[100];
+
+ ArtsCorba::Status s = Synthesizer->getStatus();
+ if(s.halted)
+ {
+ cpuusagetimer->stop();
+ accept();
+ PortableKDE::KMsgSorry(this,i18n("Your synthesis has been interrupted due to excessive CPU load."));
+ /*
+ KMsgBox::message(this,i18n("Error"),
+ i18n("Your synthesis has been interrupted due to excessive CPU load."),
+ KMsgBox::STOP);
+ */
+ // warning: this is invalid after accept();
+ return;
+ }
+ sprintf(cpuusage,"%s%3.2f%%",
+ (const char *)i18n("CPU usage: "),s.cpu_usage*100);
+
+ cpuusagelabel->setText(cpuusage);
+
+ if(!structure->isExecuting()) accept();
+ // warning: this is invalid after accept();
+#endif
+}
+
+void ExecDlg::done( int r )
+{
+ structure->stopExecute();
+ QDialog::done(r);
+ emit ready();
+}
+
+void ExecDlg::saveSession()
+{
+ chdir(DirManager::sessionDir());
+
+ QString filename = KFileDialog::getSaveFileName(0,"*.arts-session",this);
+ if(!filename.isEmpty())
+ {
+ arts_debug("save... %s",filename.local8Bit().data());
+ structure->saveSession(QFile::encodeName(filename));
+ }
+}
+
+void ExecDlg::help()
+{
+ KApplication::kApplication()->invokeHelp("", "karts");
+}
+#include "execdlg.moc"
diff --git a/arts/builder/execdlg.h b/arts/builder/execdlg.h
new file mode 100644
index 00000000..47db3562
--- /dev/null
+++ b/arts/builder/execdlg.h
@@ -0,0 +1,54 @@
+ /*
+
+ Copyright (C) 1998 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef __EXECDLG_H_
+#define __EXECDLG_H_
+
+#include "structure.h"
+#include <qdialog.h>
+#include <qtimer.h>
+#include <qlabel.h>
+#include <qscrollbar.h>
+#include <qlayout.h>
+
+class ExecDlg :public QDialog {
+ Q_OBJECT
+public:
+ QTimer *cpuusagetimer;
+ QLabel *cpuusagelabel;
+ QVBoxLayout *mainlayout,*sliderlayout;
+ ExecutableStructure *structure;
+
+ void start();
+ void done(int r);
+ ExecDlg(QWidget *parent, ExecutableStructure *structure);
+
+protected slots:
+ void updateCpuUsage();
+ void guiServerTick();
+ void saveSession();
+ void help();
+
+signals:
+ void ready();
+};
+
+#endif
diff --git a/arts/builder/interfacedlg.cpp b/arts/builder/interfacedlg.cpp
new file mode 100644
index 00000000..b0cc5970
--- /dev/null
+++ b/arts/builder/interfacedlg.cpp
@@ -0,0 +1,178 @@
+ /*
+
+ Copyright (C) 1998-2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "interfacedlg.h"
+#include "structureport.h"
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qlistbox.h>
+#include <kbuttonbox.h>
+#include <qbuttongroup.h>
+#include <qradiobutton.h>
+#include <kapplication.h>
+#include <kiconloader.h>
+#include <kseparator.h>
+#include <klocale.h>
+#include <qlineedit.h>
+#include <stdio.h>
+#include <arts/debug.h>
+#include <arts/core.h>
+#include <arts/dispatcher.h>
+#include <qpushbutton.h>
+#include <kstdguiitem.h>
+
+using namespace std;
+
+InterfaceDlg::InterfaceDlg(QWidget *parent) :QDialog(parent,"Props", TRUE)
+{
+ setCaption(i18n("aRts: Structureport View"));
+
+ QVBoxLayout *mainlayout = new QVBoxLayout(this);
+ //QHBoxLayout *contentslayout = new QHBoxLayout;
+
+// object type
+/*
+ mainlayout->addSpacing(5);
+ QLabel *objectlabel = new QLabel(this);
+ QFont labelfont(objectlabel->font());
+ labelfont.setPointSize(labelfont.pointSize()*3/2);
+ objectlabel->setFont(labelfont);
+ objectlabel->setText(QString(" ")+i18n("Object type: ")+QString(port->owner->name())+QString(" "));
+ objectlabel->setAlignment(AlignCenter);
+ min_size(objectlabel);
+ mainlayout->addWidget(objectlabel);
+*/
+
+// port description
+
+/*
+ mainlayout->addSpacing(5);
+ QLabel *portlabel = new QLabel(this);
+ labelfont.setPointSize(labelfont.pointSize()*4/5);
+ portlabel->setFont(labelfont);
+ portlabel->setText(i18n("Port description: ")+ port->description);
+ min_size(portlabel);
+ portlabel->setAlignment(AlignCenter);
+ mainlayout->addWidget(portlabel);
+
+ int labelwidth = imax(portlabel->sizeHint().width(),objectlabel->sizeHint().width());
+
+ portlabel->setMinimumWidth(labelwidth);
+ objectlabel->setMinimumWidth(labelwidth);
+
+// hruler
+
+ mainlayout->addSpacing(5);
+ KSeparator *ruler = new KSeparator( KSeparator::HLine, this);
+ mainlayout->addWidget(ruler);
+ mainlayout->addSpacing(5);
+ mainlayout->addLayout(contentslayout);
+*/
+// list
+
+ listbox = new QListBox(this);
+
+ update();
+
+ listbox->setMinimumSize(340,400);
+ mainlayout->addWidget(listbox);
+ connect( listbox, SIGNAL( doubleClicked ( QListBoxItem *)), this,
+ SLOT(accept()));
+// hruler
+
+ mainlayout->addSpacing(5);
+ KSeparator *ruler2 = new KSeparator( KSeparator::HLine, this);
+ mainlayout->addWidget(ruler2);
+
+// buttons
+
+ QHBoxLayout *buttonlayout = new QHBoxLayout;
+ mainlayout->addSpacing(5);
+ mainlayout->addLayout(buttonlayout);
+ mainlayout->addSpacing(5);
+
+ buttonlayout->addSpacing(5);
+ KButtonBox *bbox = new KButtonBox(this);
+
+ bbox->addButton(KStdGuiItem::help(), this, SLOT( help() ));
+ bbox->addStretch(1);
+
+ QButton *okbutton = bbox->addButton(KStdGuiItem::ok());
+ connect( okbutton, SIGNAL( clicked() ), SLOT(accept() ) );
+
+ QButton *cancelbutton = bbox->addButton(KStdGuiItem::cancel());
+ connect( cancelbutton, SIGNAL( clicked() ), SLOT(reject() ) );
+
+ bbox->layout();
+ //min_size(bbox);
+
+ buttonlayout->addWidget(bbox);
+ buttonlayout->addSpacing(5);
+
+ //mainlayout->activate();
+ mainlayout->freeze();
+}
+
+string InterfaceDlg::interfaceName()
+{
+ if(listbox->currentItem() != -1)
+ {
+ string s = listbox->text(listbox->currentItem()).local8Bit().data();
+ string::iterator j = s.begin();
+ while(*j == ' ') j++;
+ return string(j, s.end());
+ }
+ return "";
+}
+
+void InterfaceDlg::raise()
+{
+}
+
+void InterfaceDlg::lower()
+{
+}
+
+void InterfaceDlg::rename()
+{
+}
+
+void InterfaceDlg::update(const string& interface, const string& indent)
+{
+ listbox->insertItem((indent + interface).c_str());
+
+ vector<string> *children = Arts::Dispatcher::the()->interfaceRepo().queryChildren(interface);
+ for (vector<string>::iterator ci = children->begin(); ci != children->end(); ++ci)
+ update(ci->c_str(), indent+" ");
+ delete children;
+}
+
+void InterfaceDlg::update()
+{
+ update("Arts::Object", "");
+}
+
+void InterfaceDlg::help()
+{
+ KApplication::kApplication()->invokeHelp("", "karts");
+}
+
+#include "interfacedlg.moc"
diff --git a/arts/builder/interfacedlg.h b/arts/builder/interfacedlg.h
new file mode 100644
index 00000000..1cfd6a60
--- /dev/null
+++ b/arts/builder/interfacedlg.h
@@ -0,0 +1,52 @@
+ /*
+
+ Copyright (C) 1998-2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef __INTERFACEDLG_H_
+#define __INTERFACEDLG_H_
+
+#include "structure.h"
+#include "structureport.h"
+#include <qdialog.h>
+#include <qlistbox.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <vector>
+
+class InterfaceDlg :public QDialog {
+ Q_OBJECT
+protected:
+ QListBox *listbox;
+
+public:
+ InterfaceDlg(QWidget *parent);
+
+ std::string interfaceName();
+ void update(const std::string& interface, const std::string& indent);
+ void update();
+
+public slots:
+ void raise();
+ void lower();
+ void rename();
+ void help();
+};
+
+#endif
diff --git a/arts/builder/main.cpp b/arts/builder/main.cpp
new file mode 100644
index 00000000..9c6d4277
--- /dev/null
+++ b/arts/builder/main.cpp
@@ -0,0 +1,958 @@
+/*
+
+ Copyright (C) 1998 - 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "main.h"
+
+#include "structure.h"
+#include "structureport.h"
+#include "menumaker.h"
+#include "session.h"
+#include "dirmanager.h"
+#include "moduleinfo.h"
+#include "qiomanager.h"
+#include "artsversion.h"
+#include "propertypanel.h"
+#include "module.h"
+#include "autorouter.h"
+#include "portposdlg.h"
+#include "interfacedlg.h"
+#include "execdlg.h"
+#include "retrievedlg.h"
+
+#include "config.h"
+
+#include <kdebug.h>
+#include <arts/debug.h>
+#include <kaction.h>
+#include <kstdaction.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+#include <kinputdialog.h>
+#include <kstdaccel.h>
+#include <kfiledialog.h>
+#include <ksavefile.h>
+#include <kmessagebox.h>
+#include <kaboutdata.h>
+#include <kstandarddirs.h>
+#include <kartsserver.h>
+#include <qfile.h>
+#include <qpopupmenu.h>
+
+#include <soundserver.h>
+
+#include <list>
+#include <iostream>
+
+#include <unistd.h>
+#include <string.h> // strerror always here?
+//#include <errno.h>
+
+
+using namespace std;
+
+/*************************************************************/
+
+class ArtsBuilderApp :public KApplication
+{
+protected:
+ ArtsBuilderWindow *mainWindow;
+
+public:
+ ArtsBuilderApp();
+ ArtsBuilderApp(QString filename);
+ void start();
+ void end();
+};
+
+/*************************************************************/
+
+ArtsBuilderWindow::ArtsBuilderWindow(const char *name)
+ : KDockMainWindow(0, name),
+ mainDock(0),
+ modulewidget(0),
+ propertyDock(0),
+ propertyPanel(0),
+ menumaker(0),
+ structure(0),
+ execDlg(0)
+{
+#if 0 /* PORT */
+ ModuleBroker = Synthesizer->moduleBroker();
+ assert(ModuleBroker);
+
+//---- publish my widgets on the server ----
+
+ GUIServer = new GUIServer_impl(ModuleBroker, Synthesizer);
+ arts_debug("GUIServer:\n%s", ArtsOrb->object_to_string(GUIServer));
+ GUIServer->incRef();
+
+//---- trigger autoloading of all structures that are present in my dirs ----
+
+ list<string> datadirs = PortableKDE::globalDirs("data");
+ list<string>::iterator it;
+
+ for(it = datadirs.begin(); it != datadirs.end(); ++it)
+ {
+ string common = *it;
+ common += "artsbuilder";
+ if(chdir(common.c_str()) == 0)
+ ModuleBroker->addPublishingPath(common.c_str());
+ }
+ /*
+ string common = (const char *)PortableKDE::globalDir("data");
+ common += "/artsbuilder";
+ arts_debug("%s", common.c_str());
+ if(chdir(common.c_str()) == 0)
+ ModuleBroker->addPublishingPath(common.c_str());
+ */
+
+ // just make sure that the mapsDir exists
+ (void)DirManager::mapDir();
+
+ if(chdir(DirManager::structureDir()) == 0) // retry
+ {
+ ModuleBroker->addPublishingPath(DirManager::structureDir());
+ Synthesizer->addArtsDirectory(DirManager::baseDir());
+
+ }
+#endif
+
+ arts_debug("PORT: structure");
+ structure = new Structure();
+ arts_debug("PORT: structure ok");
+ //ModuleList = structure->getModuleList();
+
+ mainDock = createDockWidget("mainDockWidget", 0, 0, "main_dock_widget");
+
+ arts_debug("PORT: modulewidget");
+ modulewidget = new ModuleWidget(structure, mainDock, "mwidget");
+ mainDock->setWidget(modulewidget);
+ connect(modulewidget, SIGNAL(modified(bool)), SLOT(setModified(bool)));
+ arts_debug("PORT: modulewidget ok");
+
+ // allow others to dock to the 4 sides
+ mainDock->setDockSite(KDockWidget::DockCorner);
+ // forbit docking abilities of module widget itself
+ mainDock->setEnableDocking(KDockWidget::DockNone);
+
+ setView(mainDock);
+ setMainDockWidget(mainDock);
+
+ propertyDock = createDockWidget("propertyDock", 0, 0, i18n("Port Properties"));
+
+ propertyPanel = new PropertyPanel(propertyDock, "ppanel");
+
+ propertyDock->setWidget(propertyPanel);
+ propertyDock->manualDock(mainDock, // dock target
+ KDockWidget::DockBottom, // dock site
+ 80); // relation target/this (in percent)
+
+ // selection
+ connect(modulewidget, SIGNAL(portSelected(ModulePort *)),
+ propertyPanel, SLOT (setSelectedPort(ModulePort *)));
+ connect(propertyPanel, SIGNAL(portSelected(ModulePort *)),
+ modulewidget, SLOT (selectPort(ModulePort *)));
+ connect(modulewidget, SIGNAL(componentSelected(StructureComponent *)),
+ propertyPanel, SLOT (setSelectedComponent(StructureComponent *)));
+
+ // connection
+ connect(propertyPanel, SIGNAL(startConnection(ModulePort *)),
+ modulewidget, SLOT (startConnection(ModulePort *)));
+
+ // port properties changed
+ connect(propertyPanel, SIGNAL(portPropertiesChanged(ModulePort *)),
+ modulewidget, SLOT (portPropertiesChanged(ModulePort *)));
+
+ arts_debug("PORT: setcanvas");
+ structure->setCanvas(modulewidget);
+ arts_debug("PORT: setcanvas ok");
+
+ mbroker_updateCount = 0;
+
+ arts_debug("PORT: menumaker");
+ menumaker = new MenuMaker(new KActionMenu(i18n("Modules"), actionCollection(), "modulesmenu"));
+ //menumaker->addCategory("&Gui", "Gui_");
+ menumaker->addCategory(i18n("&Synthesis"), "Arts::Synth_");
+ menumaker->addCategory(i18n("&Synthesis/&Arithmetic + Mixing"), "Arts::Synth_ADD$");
+ menumaker->addCategory(i18n("&Synthesis/&Arithmetic + Mixing"), "Arts::Synth_AUTOPANNER$");
+ menumaker->addCategory(i18n("&Synthesis/&Arithmetic + Mixing"), "Arts::Synth_MUL$");
+ menumaker->addCategory(i18n("&Synthesis/&Arithmetic + Mixing"), "Arts::Synth_DIV$");
+ menumaker->addCategory(i18n("&Synthesis/&Arithmetic + Mixing"), "Arts::Synth_MULTI_ADD$");
+ menumaker->addCategory(i18n("&Synthesis/&Arithmetic + Mixing"), "Arts::Synth_XFADE$");
+ menumaker->addCategory(i18n("&Synthesis/&Busses"), "Arts::Synth_BUS_");
+ menumaker->addCategory(i18n("&Synthesis/&Delays"), "Arts::Synth_DELAY$");
+ menumaker->addCategory(i18n("&Synthesis/&Delays"), "Arts::Synth_CDELAY$");
+ menumaker->addCategory(i18n("&Synthesis/&Envelopes"), "Arts::Synth_PSCALE$");
+ menumaker->addCategory(i18n("&Synthesis/&Envelopes"), "Arts::Synth_ENVELOPE_");
+ menumaker->addCategory(i18n("&Synthesis/Effe&cts"), "Arts::Synth_FREEVERB$");
+ menumaker->addCategory(i18n("&Synthesis/Effe&cts"), "Arts::Synth_FX_");
+ menumaker->addCategory(i18n("&Synthesis/Effe&cts"), "Arts::Synth_PITCH_SHIFT$");
+ menumaker->addCategory(i18n("&Synthesis/Effe&cts"), "Arts::Synth_TREMOLO$");
+ menumaker->addCategory(i18n("&Synthesis/&Filters"), "Arts::Synth_ATAN_SATURATE$");
+ menumaker->addCategory(i18n("&Synthesis/&Filters"), "Arts::Synth_BRICKWALL_LIMITER$");
+ menumaker->addCategory(i18n("&Synthesis/&Filters"), "Arts::Synth_MOOG_VCF");
+ menumaker->addCategory(i18n("&Synthesis/&Filters"), "Arts::Synth_SHELVE_CUTOFF$");
+ menumaker->addCategory(i18n("&Synthesis/&Filters"), "Arts::Synth_RC$");
+ menumaker->addCategory(i18n("&Synthesis/&Filters"), "Arts::Synth_STD_EQUALIZER$");
+ menumaker->addCategory(i18n("&Synthesis/&Midi + Sequencing"), "Arts::Synth_MIDI");
+ menumaker->addCategory(i18n("&Synthesis/&Midi + Sequencing"), "Arts::Interface_MIDI");
+ menumaker->addCategory(i18n("&Synthesis/&Midi + Sequencing"), "Arts::Synth_SEQUENCE$");
+ menumaker->addCategory(i18n("&Synthesis/&Midi + Sequencing"), "Arts::Synth_SEQUENCE_FREQ$");
+ menumaker->addCategory(i18n("&Synthesis/&Midi + Sequencing"), "Arts::Synth_STRUCT_KILL$");
+ menumaker->addCategory(i18n("&Synthesis/Sam&ples "), "Arts::Synth_PLAY_");
+ menumaker->addCategory(i18n("&Synthesis/&Sound IO"), "Arts::Synth_AMAN_");
+ menumaker->addCategory(i18n("&Synthesis/&Sound IO"), "Arts::Synth_CAPTURE_WAV$");
+ menumaker->addCategory(i18n("&Synthesis/&Sound IO"), "Arts::Synth_PLAY$");
+ menumaker->addCategory(i18n("&Synthesis/&Sound IO"), "Arts::Synth_RECORD$");
+ menumaker->addCategory(i18n("&Synthesis/&Sound IO"), "Arts::Synth_FULL_DUPLEX_");
+ menumaker->addCategory(i18n("&Synthesis/&Sound IO"), "Arts::Synth_FILEPLAY");
+ menumaker->addCategory(i18n("&Synthesis/&Tests"), "Arts::Synth_NIL$");
+ menumaker->addCategory(i18n("&Synthesis/&Tests"), "Arts::Synth_DEBUG$");
+ menumaker->addCategory(i18n("&Synthesis/&Tests"), "Arts::Synth_DATA$");
+ menumaker->addCategory(i18n("&Synthesis/&Tests"), "Arts::Synth_MIDI_DEBUG$");
+ menumaker->addCategory(i18n("&Synthesis/&Oscillation && Modulation"), "Arts::Synth_FREQUENCY$");
+ menumaker->addCategory(i18n("&Synthesis/&Oscillation && Modulation"), "Arts::Synth_FM_SOURCE$");
+ menumaker->addCategory(i18n("&Synthesis/&Oscillation && Modulation"), "Arts::Synth_OSC$");
+ menumaker->addCategory(i18n("&Synthesis/&WaveForms"), "Arts::Synth_WAVE_");
+ menumaker->addCategory(i18n("&Synthesis/&WaveForms"), "Arts::Synth_NOISE$");
+ menumaker->addCategory(i18n("&Synthesis/&Internal"), "Arts::Synth_PARAM_");
+
+ menumaker->addCategory(i18n("&Examples"), "example_");
+ menumaker->addCategory(i18n("&Instruments"), "instrument_");
+ menumaker->addCategory(i18n("&Mixer-Elements"), "mixer_element_");
+ menumaker->addCategory(i18n("&Templates"), "template_");
+ menumaker->addCategory(i18n("&Other"), "*");
+ arts_debug("PORT: menumaker ok");
+
+/*
+ m_modules->insertItem(i18n("&Gui"), m_modules_gui);
+ m_modules->insertItem(i18n("&Synthesis"), m_modules_synth);
+ m_modules->insertItem(i18n("&Instruments"), m_modules_instruments);
+ m_modules->insertItem(i18n("&Other"), m_modules_other);
+ */
+
+#if 000
+ connect(menubar, SIGNAL(highlighted(int)), this, SLOT(activateMenu(int)));
+ connect(m_view, SIGNAL(activated(int)), modulewidget, SLOT(setZoom(int)));
+ connect(m_ports, SIGNAL(activated(int)), this, SLOT(addPort(int)));
+ connect(m_file_new, SIGNAL(activated(int)), this, SLOT(fileNew(int)));
+
+ //connect(m_modules, SIGNAL(activated(int)), this, SLOT(addModule(int)));
+ /*
+ connect(m_modules_synth, SIGNAL(activated(int)), this, SLOT(addModule(int)));
+ connect(m_modules_gui, SIGNAL(activated(int)), this, SLOT(addModule(int)));
+ connect(m_modules_instruments, SIGNAL(activated(int)), this, SLOT(addModule(int)));
+ connect(m_modules_other, SIGNAL(activated(int)), this, SLOT(addModule(int)));
+ */
+ connect(kapp, SIGNAL(lastWindowClosed()), this , SLOT(quit()));
+
+ // update the modules menu once for the start
+#endif
+
+ arts_debug("PORT: activatemenu");
+ connect(menumaker, SIGNAL(activated(const char *)), this, SLOT(addModule(const char *)));
+ fillModuleMenu();
+ arts_debug("PORT: activatemenu ok");
+ setupActions();
+
+ createGUI();
+
+ // connect to aboutToShow to correctly show state of dockwidget there:
+ QPopupMenu *viewmenu = (QPopupMenu*)factory()->container("view", this);
+ if (viewmenu)
+ connect(viewmenu, SIGNAL(aboutToShow()), this, SLOT(viewMenuAboutToShow()));
+ else
+ arts_debug("view menu not found!");
+
+ m_filename = QString::null;
+ setModified(false);
+
+ installEventFilter(propertyPanel);
+}
+
+void ArtsBuilderWindow::setupActions()
+{
+ // File menu
+ KStdAction::openNew(this, SLOT(fileNew()), actionCollection());
+
+ (void)new KAction(i18n("Open Session..."), 0, this, SLOT(openSession()),
+ actionCollection(), "file_open_session");
+ KStdAction::open(this, SLOT(open()), actionCollection());
+ (void)new KAction(i18n("Open E&xample..."), Qt::CTRL + Qt::Key_X, this, SLOT(openExample()),
+ actionCollection(), "file_open_example");
+ KStdAction::save(this, SLOT(save()), actionCollection());
+ KStdAction::saveAs(this, SLOT(saveAs()), actionCollection());
+ (void)new KAction(i18n("&Retrieve From Server..."), Qt::CTRL + Qt::Key_R, this, SLOT(retrieve()),
+ actionCollection(), "file_retrieve_from_server");
+ (void)new KAction(i18n("&Execute Structure"), "artsbuilderexecute", Qt::CTRL + Qt::Key_E, this, SLOT(execute()),
+ actionCollection(), "file_execute_structure");
+ (void)new KAction(i18n("&Rename Structure..."), Qt::CTRL + Qt::Key_R, this, SLOT(rename()),
+ actionCollection(), "file_rename_structure");
+ (void)new KAction(i18n("&Publish Structure"), Qt::CTRL + Qt::Key_P, this, SLOT(publish()),
+ actionCollection(), "file_publish_structure");
+ KStdAction::quit(this, SLOT(close()), actionCollection());
+
+ // Edit menu
+ (void)new KAction(i18n("&Delete"), Qt::Key_Delete, modulewidget, SLOT(delModule()),
+ actionCollection(), "edit_delete");
+ KStdAction::selectAll(modulewidget, SLOT(selectAll()), actionCollection());
+
+ // View menu
+ viewPropertiesAction= new KToggleAction(i18n("&Property Panel"), 0,
+ propertyDock, SLOT(changeHideShowState()),
+ actionCollection(), "view_properties");
+ (void)new KAction(i18n("200%"), 0, this, SLOT(viewAt200()),
+ actionCollection(), "view_200");
+ (void)new KAction(i18n("150%"), 0, this, SLOT(viewAt150()),
+ actionCollection(), "view_150");
+ (void)new KAction(i18n("100%"), 0, this, SLOT(viewAt100()),
+ actionCollection(), "view_100");
+ (void)new KAction(i18n("50%"), 0, this, SLOT(viewAt50()),
+ actionCollection(), "view_50");
+
+ // Ports menu
+ (void)new KAction(i18n("Create IN Audio Signal"), 0, this, SLOT(createInAudioSignal()),
+ actionCollection(), "ports_create_in_audio_signal");
+ (void)new KAction(i18n("Create OUT Audio Signal"), 0, this, SLOT(createOutAudioSignal()),
+ actionCollection(), "ports_create_out_audio_signal");
+ (void)new KAction(i18n("Create IN String Property"), 0, this, SLOT(createInStringProperty()),
+ actionCollection(), "ports_create_in_string_property");
+ (void)new KAction(i18n("Create IN Audio Property"), 0, this, SLOT(createInAudioProperty()),
+ actionCollection(), "ports_create_in_audio_property");
+ (void)new KAction(i18n("Implement Interface..."), 0, this, SLOT(addInterface()),
+ actionCollection(), "ports_implement_interface");
+ (void)new KAction(i18n("Change Positions/Names..."), 0, this, SLOT(changePortPositions()),
+ actionCollection(), "ports_change_positions");
+}
+
+void ArtsBuilderWindow::fillModuleMenu()
+{
+ long updateCount = 3; /* PORT: automatic update of menues missing */
+
+ if(updateCount != mbroker_updateCount)
+ {
+ mbroker_updateCount = updateCount;
+ //---- query all available objects ----
+ Arts::TraderQuery query;
+ query.supports("Buildable", "true");
+ vector<Arts::TraderOffer> *offers = query.query();
+
+ menumaker->clear();
+ //m_file_new->clear();
+
+ vector<Arts::TraderOffer>::iterator i;
+ long n = 1; /* TODO:PORT: is this necessary? I think not */
+ for(i = offers->begin(); i != offers->end(); ++i)
+ {
+ Arts::TraderOffer& offer = *i;
+ string name = offer.interfaceName();
+ menumaker->addItem(name.c_str(),n++);
+
+ /* PORT: templates missing
+ if(strncmp(name, "template_", strlen("template_")) == 0)
+ {
+ char *xname = strdup(&name[strlen("template_")]);
+ int x;
+ for(x = 0;xname[x] != 0; x++)
+ if(xname[x] == '_') xname[x] = ' ';
+
+ m_file_new->insertItem(xname, i);
+ }
+ */
+ }
+ delete offers;
+ }
+#if 0
+ if(0) /*item == modules_menu_item) PORT!!! */
+ {
+ long updateCount = ModuleBroker->updateCount();
+
+ // if the contents of the ModukeBroker changed, update our "modules"-Menu
+ if(updateCount != mbroker_updateCount)
+ {
+ mbroker_updateCount = updateCount;
+ //---- query all available objects ----
+ ArtsCorba::StringSeq_var Modules = ModuleBroker->publishedModules();
+ assert(Modules);
+
+ menumaker->clear();
+ m_file_new->clear();
+
+ unsigned long i;
+ for(i = 0; i < Modules->length();i++)
+ {
+ const char *name = (*Modules)[i];
+ menumaker->addItem(name, i);
+
+ if(strncmp(name, "template_", strlen("template_")) == 0)
+ {
+ char *xname = strdup(&name[strlen("template_")]);
+ int x;
+ for(x = 0;xname[x] != 0; x++)
+ if(xname[x] == '_') xname[x] = ' ';
+
+ m_file_new->insertItem(xname, i);
+ }
+ }
+ }
+ }
+#endif
+}
+
+void ArtsBuilderWindow::quit()
+{
+ if(execDlg) return;
+ arts_debug(">> ArtsBuilderWindow::quit() called");
+ kapp->quit();
+ arts_debug("<< leaving ArtsBuilderWindow::quit()");
+}
+
+ArtsBuilderWindow::~ArtsBuilderWindow()
+{
+ delete structure;
+}
+
+void ArtsBuilderWindow::viewMenuAboutToShow()
+{
+ viewPropertiesAction->setChecked(propertyDock->isVisible());
+}
+
+void ArtsBuilderWindow::publish()
+{
+ checkName();
+ structure->publish();
+ KMessageBox::information(this,
+ i18n("The structure has been published as: '%1' on the server.").arg( structure->name().c_str() ));
+}
+
+QString ArtsBuilderWindow::getOpenFilename(const char *pattern, const char *initialDir)
+{
+ arts_debug(">>>>> getOpenFilename");
+ QString filename = KFileDialog::getOpenFileName(initialDir, pattern, this);
+ arts_debug(">>>>> opendlg closed");
+ if(!filename.isEmpty())
+ {
+ arts_debug("open... %s", filename.local8Bit().data());
+
+ // check that the file is ok:
+
+ FILE *infile = fopen(QFile::encodeName(filename), "r");
+
+ if(infile)
+ {
+ fclose(infile);
+ return(filename);
+ }
+ }
+ return QString("");
+}
+
+void ArtsBuilderWindow::fileNew()
+{
+ if(!promptToSave())
+ return;
+
+ propertyPanel->setSelectedComponent(0);
+ structure->clear();
+ modulewidget->reInit();
+ m_filename = QString::null;
+ setModified(false);
+}
+
+void ArtsBuilderWindow::open()
+{
+ if(!promptToSave())
+ return;
+
+ open(getOpenFilename("*.arts", DirManager::structureDir()));
+}
+
+void ArtsBuilderWindow::open(QString filename)
+{
+ if(!promptToSave())
+ return;
+
+ if(!filename.isEmpty())
+ {
+ structure->load(QFile::encodeName(filename));
+ modulewidget->reInit();
+ if(!structure->valid())
+ {
+ KMessageBox::sorry(this,
+ i18n("The structure could not be loaded correctly. Maybe some of\n"
+ "the modules used in the file are not available in this\n"
+ "version of aRts."),
+ i18n("Arts Warning"));
+ }
+ m_filename = filename;
+ setModified(false);
+ setCaption(m_filename);
+ }
+}
+
+void ArtsBuilderWindow::openSession()
+{
+ if(!promptToSave())
+ return;
+
+ QString filename = getOpenFilename("*.arts-session", DirManager::sessionDir());
+
+ if(!filename.isEmpty())
+ {
+ Session *session = new Session();
+ session->loadSession(QFile::encodeName(filename));
+
+ assert(!execDlg);
+ execDlg = new ExecDlg(0, session);
+ assert(execDlg);
+
+ // this will create the widgets that will eventually get into the
+ // execdlg
+ session->startExecute();
+
+ execDlg->start();
+ execDlg->show();
+
+ connect(execDlg, SIGNAL(ready()), this, SLOT(endexecute()));
+
+ hide();
+ // m_filename = filename; FIXME: DOESN'T THIS BELONG HERE?
+ setModified(false);
+ }
+}
+
+void ArtsBuilderWindow::openExample()
+{
+ if(!promptToSave())
+ return;
+
+ QString dir = locate("data", "artsbuilder/examples/");
+ if(!dir)
+ KMessageBox::sorry(
+ this,
+ i18n("Unable to find the examples folder.\nUsing the current folder instead."),
+ i18n("aRts Warning"));
+
+ open(getOpenFilename("*.arts", QFile::encodeName(dir)));
+}
+
+void ArtsBuilderWindow::saveAs()
+{
+ checkName();
+ string defaultname = string(structure->name()) + string(".arts");
+
+ chdir(DirManager::structureDir());
+ KFileDialog *dlg = new KFileDialog(0, "*.arts", this, 0, true /*,false TODO: acceptURLs */);
+
+ dlg->setSelection(defaultname.c_str());
+ dlg->setCaption(i18n("Save As"));
+
+ QString filename;
+ if(dlg->exec() == QDialog::Accepted)
+ filename = dlg->selectedFile();
+
+ delete dlg;
+ // QString filename = KFileDialog::getSaveFileName(0, "*.arts", this);
+ // filename.detach();
+
+ if(!filename.isEmpty())
+ save(filename);
+}
+
+bool ArtsBuilderWindow::save(QString filename)
+{
+ arts_debug("trying to save structure as '%s'", filename.local8Bit().data());
+
+ KSaveFile file(filename);
+
+ if(file.status()) {
+ KMessageBox::sorry(this,
+ i18n("The file '%1' could not be opened for writing: %2")
+ .arg(filename).arg(strerror(file.status())),
+ i18n("aRts Warning"));
+ return false;
+ }
+
+ structure->saveInto(file.fstream());
+
+ if(!file.close()) {
+ KMessageBox::sorry(this,
+ i18n("Saving to file '%1' could not be finished correctly: %2")
+ .arg(filename).arg(strerror(file.status())),
+ i18n("aRts Warning"));
+ return false;
+ }
+
+ // tell the server to rescan for structures
+ Arts::SoundServerV2 server = KArtsServer().server();
+ if(!server.isNull()) server.checkNewObjects();
+
+ m_filename = filename;
+ setModified(false);
+ return true;
+}
+
+void ArtsBuilderWindow::save()
+{
+ if(m_filename.isEmpty())
+ saveAs();
+ else
+ save(m_filename);
+}
+
+void ArtsBuilderWindow::checkName()
+{
+ if(strncmp(structure->name().c_str(), "template_", strlen("template_")) == 0)
+ rename();
+}
+
+void ArtsBuilderWindow::rename()
+{
+ bool ok;
+
+ QString name = KInputDialog::getText( i18n( "Rename Structure" ),
+ i18n( "Enter structure name:" ), structure->name().c_str(), &ok, this );
+ if (ok)
+ {
+ arts_debug("rename OK...");
+ structure->rename(name.local8Bit());
+ }
+
+ setModified(true);
+}
+
+void ArtsBuilderWindow::retrieve()
+{
+ if(!promptToSave())
+ return;
+
+ RetrieveDlg rd(0);
+
+ if(rd.exec())
+ {
+ QString result = rd.result();
+ if(!result.isEmpty())
+ {
+ structure->retrieve(result.local8Bit());
+ modulewidget->reInit();
+ }
+ }
+ // maybe set m_filename to null or sth. here?
+ setModified(true);
+}
+
+void ArtsBuilderWindow::execute()
+{
+ assert(structure);
+ assert(!execDlg);
+ execDlg = new ExecDlg(0, structure);
+ assert(execDlg);
+
+ // this will create the widgets that will eventually get into the
+ // execdlg
+ if(structure->startExecute())
+ {
+ execDlg->start();
+ execDlg->show();
+
+ connect(execDlg, SIGNAL(ready()), this, SLOT(endexecute()));
+
+ hide();
+ }
+ else
+ {
+ delete execDlg;
+ execDlg = 0;
+
+ KMessageBox::sorry(this,
+ i18n("Could not execute your structure. Make sure that the\n"
+ "sound server (artsd) is running.\n"), i18n("aRts Warning"));
+ }
+}
+
+void ArtsBuilderWindow::endexecute()
+{
+ show();
+ assert(execDlg);
+ delete execDlg;
+ // will be done by the execDlg itself now
+ //structure->stopExecute();
+
+ execDlg = 0;
+}
+
+void ArtsBuilderWindow::oldFileNewWhatTheHellDoesItDo(int what)
+{
+ if(!promptToSave())
+ return;
+
+ const char *name = menumaker->findID(what);
+ assert(name);
+ structure->retrieve(name);
+ modulewidget->reInit();
+ setModified(false);
+}
+
+void ArtsBuilderWindow::createInAudioSignal()
+{
+ // data that goes into the structure
+ modulewidget->addPort(Arts::PortType(Arts::output, "float", Arts::conn_stream, false));
+ setModified(true);
+}
+
+void ArtsBuilderWindow::createOutAudioSignal()
+{
+ // data that goes out of the structure
+ modulewidget->addPort(Arts::PortType(Arts::input, "float", Arts::conn_stream, false));
+ setModified(true);
+}
+
+void ArtsBuilderWindow::createInStringProperty()
+{
+ // data that goes into the structure
+ modulewidget->addPort(Arts::PortType(Arts::output, "string", Arts::conn_property, false));
+ setModified(true);
+}
+
+void ArtsBuilderWindow::createInAudioProperty()
+{
+ // data that goes into the structure
+ modulewidget->addPort(Arts::PortType(Arts::output, "float", Arts::conn_property, false));
+ setModified(true);
+}
+
+void ArtsBuilderWindow::changePortPositions()
+{
+ PortPosDlg *ppd = new PortPosDlg(this, structure);
+ ppd->exec();
+ setModified(true);
+ // XXX: delete ppd?
+}
+
+void ArtsBuilderWindow::addInterface()
+{
+ InterfaceDlg *ifd = new InterfaceDlg(this);
+ ifd->exec();
+
+ Arts::ModuleInfo minfo = makeModuleInfo(ifd->interfaceName());
+ if(!minfo.name.empty())
+ modulewidget->addInterface(minfo);
+
+ delete ifd;
+}
+
+void ArtsBuilderWindow::viewAt50()
+{
+ modulewidget->setZoom(50);
+}
+
+void ArtsBuilderWindow::viewAt100()
+{
+ modulewidget->setZoom(100);
+}
+
+void ArtsBuilderWindow::viewAt150()
+{
+ modulewidget->setZoom(150);
+}
+
+void ArtsBuilderWindow::viewAt200()
+{
+ modulewidget->setZoom(200);
+}
+
+void ArtsBuilderWindow::addModule(const char *name)
+{
+ arts_return_if_fail (name != 0);
+
+ arts_debug("addModule(%s)", name);
+ Arts::ModuleInfo minfo = makeModuleInfo(name);
+
+ if(!minfo.name.empty())
+ modulewidget->addModule(minfo);
+#if 0
+ const char *name = menumaker->findID(module);
+ assert(name);
+
+ arts_debug("selected (%s) (module=%d)", name, module);
+
+ ArtsCorba::ModuleBroker_var ModuleBroker = Synthesizer->moduleBroker();
+ ArtsCorba::ModuleInfo_var minfo = ModuleBroker->lookupModule(name);
+
+
+ if(minfo)
+ {
+ modulewidget->addModule(minfo);
+
+/*
+ Module *m = structure->createModule(minfo);
+ modulewidget->addModule(m);
+*/
+ }
+#endif
+ setModified(true);
+}
+
+bool ArtsBuilderWindow::isModified()
+{
+ return modified;
+}
+
+void ArtsBuilderWindow::setModified(bool m)
+{
+ modified = m;
+ setCaption(m_filename, modified);
+ actionCollection()->action(KStdAction::stdName(KStdAction::Save))->setEnabled(modified);
+}
+
+bool ArtsBuilderWindow::queryClose()
+{
+ return promptToSave();
+}
+
+bool ArtsBuilderWindow::promptToSave()
+{
+ bool result;
+ int query;
+
+ if(!isModified())
+ return true;
+
+ query = KMessageBox::warningYesNoCancel(this,
+ i18n("The current structure has been modified.\nWould you like to save it?"), QString::null, KStdGuiItem::save(), KStdGuiItem::discard());
+
+ result = false;
+ switch(query)
+ {
+ case KMessageBox::Yes:
+ save();
+ result = !modified;
+ break;
+ case KMessageBox::No:
+ result = true;
+ setModified(false);
+ break;
+ case KMessageBox::Cancel:
+ break;
+ }
+ return result;
+}
+
+/*************************************************************/
+
+ArtsBuilderApp::ArtsBuilderApp()
+{
+ start();
+}
+
+ArtsBuilderApp::ArtsBuilderApp(QString filename)
+{
+ start();
+ if(QFile::exists(filename))
+ {
+ mainWindow->open(filename);
+ } else {
+ KMessageBox::sorry(0,
+ i18n("The specified file '%1' does not exist.").arg(filename),
+ i18n("aRts Warning"));
+ }
+}
+
+void ArtsBuilderApp::start()
+{
+ arts_debug("PORT: mainWindow");
+ mainWindow = new ArtsBuilderWindow("main");
+
+ arts_debug("PORT: mainWindow ok");
+ mainWindow->resize(680, 500);
+ arts_debug("PORT: mainWindow show");
+ mainWindow->show();
+ arts_debug("PORT: mainWindow show ok");
+
+#if 0 /* PORT */
+ ArtsCorba::ModuleBroker_var ModuleBroker = theSynthesizer->moduleBroker();
+ assert(ModuleBroker);
+#endif
+
+ setTopWidget(mainWindow);
+}
+
+void ArtsBuilderApp::end()
+{
+ delete mainWindow;
+}
+
+/*************************************************************/
+
+static KCmdLineOptions options[] =
+{
+ { "+[file]", I18N_NOOP("Optional .arts file to be loaded"), 0 },
+ KCmdLineLastOption
+};
+
+#ifdef COMMON_BINARY
+int artsbuilder_main(int argc, char **argv)
+#else
+int main(int argc, char **argv)
+#endif
+{
+ KAboutData aboutData("artsbuilder",
+ I18N_NOOP("artsbuilder"),
+ ARTS_VERSION,
+ I18N_NOOP("aRts synthesizer designer"),
+ KAboutData::License_GPL,
+ "(C) 1998-2001, Stefan Westerfeld",
+ I18N_NOOP("The analog real-time synthesizer graphical design tool."),
+ "http://www.arts-project.org/",
+ "submit@bugs.kde.org");
+
+ aboutData.addAuthor("Stefan Westerfeld", I18N_NOOP("Author"), "stefan@twc.de");
+ aboutData.addCredit("Waldo Bastian", 0, "bastian@kde.org");
+ aboutData.addCredit("Jens Hahn", 0, "Jens.Hahn@t-online.de");
+ aboutData.addCredit("Martin Lorenz", 0, "lorenz@ch.tum.de");
+ aboutData.addCredit("Hans Meine", 0, "hans_meine@gmx.net");
+ aboutData.addCredit("Jeff Tranter", 0, "tranter@pobox.com");
+
+ KCmdLineArgs::init(argc, argv, &aboutData);
+ KCmdLineArgs::addCmdLineOptions(options);
+ Arts::QIOManager iomanager;
+ Arts::Dispatcher dispatcher(&iomanager);
+
+ Arts::ObjectManager::the()->provideCapability("kdegui");
+
+ // check for one optional filename argument
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ if(args->count() > 1) {
+ args->usage("");
+ }
+ if(args->count() > 0)
+ {
+ ArtsBuilderApp Application(QFile::decodeName(args->arg(0)));
+ args->clear();
+ return Application.exec();
+ } else {
+ ArtsBuilderApp Application;
+ args->clear();
+ return Application.exec();
+ }
+}
+#include "main.moc"
diff --git a/arts/builder/main.h b/arts/builder/main.h
new file mode 100644
index 00000000..f7babc3a
--- /dev/null
+++ b/arts/builder/main.h
@@ -0,0 +1,118 @@
+ /*
+
+ Copyright (C) 1998-1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef __MAIN_H__
+#define __MAIN_H__
+
+#include "mwidget.h"
+
+#include <kdockwidget.h>
+#include <kapplication.h>
+#include <kfiledialog.h>
+#include <ksimpleconfig.h>
+
+#include <list>
+
+class PropertyPanel;
+class Structure;
+class MenuMaker;
+class ExecDlg;
+class KToggleAction;
+
+class ArtsBuilderWindow: public KDockMainWindow
+{
+ Q_OBJECT
+
+protected:
+ KDockWidget* mainDock;
+ ModuleWidget *modulewidget;
+
+ KDockWidget* propertyDock;
+ PropertyPanel *propertyPanel;
+ KToggleAction *viewPropertiesAction;
+
+ MenuMaker *menumaker;
+
+ long mbroker_updateCount;
+ Structure *structure;
+
+ ExecDlg *execDlg;
+
+ QString m_filename;
+ bool modified;
+
+protected:
+ void setupActions();
+
+public:
+ ArtsBuilderWindow(const char *name);
+ ~ArtsBuilderWindow();
+
+ void clear();
+ void checkName();
+ QString getOpenFilename(const char *pattern, const char *initialDir = 0);
+ bool isModified();
+ bool promptToSave();
+ bool queryClose();
+
+public slots:
+ void fillModuleMenu();
+ void viewMenuAboutToShow();
+
+ /* ports menu */
+ void createInAudioSignal();
+ void createOutAudioSignal();
+ void createInStringProperty();
+ void createInAudioProperty();
+ void addInterface();
+
+ /* view menu */
+ void viewAt50();
+ void viewAt100();
+ void viewAt150();
+ void viewAt200();
+
+ /* file menu */
+ void fileNew();
+ void open();
+ void openSession();
+ void openExample();
+ void save();
+ void saveAs();
+ void quit();
+
+ void open(QString filename);
+ bool save(QString filename);
+ void setModified(bool m = true);
+
+ void execute();
+ void endexecute();
+ void rename();
+ void retrieve();
+ void publish();
+
+ void addModule(const char *module);
+ void changePortPositions();
+
+ void oldFileNewWhatTheHellDoesItDo(int what);
+};
+
+#endif
diff --git a/arts/builder/menumaker.cpp b/arts/builder/menumaker.cpp
new file mode 100644
index 00000000..54e32e2b
--- /dev/null
+++ b/arts/builder/menumaker.cpp
@@ -0,0 +1,212 @@
+#include "menumaker.h"
+#include <stdio.h>
+#include <assert.h>
+#include <arts/debug.h>
+
+using namespace std;
+
+MenuEntry::MenuEntry(MenuMaker *menumaker, KActionMenu *parent, const char *text)
+ : menumaker(menumaker), text(text)
+{
+ action = new KAction(QString::fromLocal8Bit(text));
+ parent->insert(action);
+ connect(action, SIGNAL(activated()), this, SLOT(activated()));
+}
+
+void MenuEntry::activated()
+{
+ menumaker->menuActivated(text);
+}
+
+MenuCategory::MenuCategory(const QString& name, const char *prefix, KActionMenu *menu)
+{
+ _menu = menu;
+ _name = name;
+ _catchall = (strcmp(prefix,"*") == 0);
+ addPrefix(prefix);
+}
+
+QString MenuCategory::name()
+{
+ return _name;
+}
+
+bool MenuCategory::catchall()
+{
+ return _catchall;
+}
+
+void MenuCategory::addPrefix(const char *prefix)
+{
+ prefixList.push_back(prefix);
+}
+
+bool MenuCategory::matches(const char *item)
+{
+ string pattern = string(item)+"$";
+ size_t patternlen = strlen(pattern.c_str());
+
+ list<string>::iterator i;
+
+ for(i=prefixList.begin();i != prefixList.end(); ++i)
+ {
+ const char *prefix = (*i).c_str();
+
+ if(patternlen >= strlen(prefix))
+ {
+ if(strncmp(prefix,pattern.c_str(),strlen(prefix)) == 0) return true;
+ }
+ }
+ return false;
+}
+
+KActionMenu *MenuCategory::menu()
+{
+ return _menu;
+}
+
+MenuMaker::MenuMaker(KActionMenu *root)
+{
+ categories.push_back(new MenuCategory("","",root));
+}
+
+// Add more specific categories later, more general categories first.
+//
+// for instance use
+// addCategory("&Synthesis", "Synth_");
+// addCategory("&Synthesis/&Waveforms", "Synth_WAVE");
+
+void MenuMaker::addCategory(const QString& name, const char *prefix)
+{
+ MenuCategory *mc = 0,*pc = 0;
+
+ mc = lookupCategoryByName(name);
+ if(mc)
+ {
+ mc->addPrefix(prefix);
+ return; // already exists
+ }
+
+ pc = lookupCategoryByName(basename(name));
+ if(pc)
+ {
+ KActionMenu *newMenu = new KActionMenu(catname(name));
+ pc->menu()->insert(newMenu);
+ /* 000 */
+ /*connect(newMenu,SIGNAL(activated(int)),this,SLOT(menuactivated(int)));
+ pc->menu()->insertItem(catname(name).c_str(), newMenu, CAT_MAGIC_ID);*/
+ arts_debug("inserting a menu called '%s' in the parent menu '%s'",
+ catname(name).local8Bit().data(),pc->name().local8Bit().data());
+ categories.push_back(new MenuCategory(name,prefix,newMenu));
+ }
+ else
+ {
+ arts_debug("Parent category '%s' for '%s' is missing.",
+ basename(name).local8Bit().data(),name.local8Bit().data());
+ }
+}
+
+MenuCategory *MenuMaker::lookupCategoryByName(const QString& name)
+{
+ MenuCategory *mc = 0;
+ list<MenuCategory *>::iterator i;
+ for(i=categories.begin();i != categories.end();++i)
+ {
+ if((*i)->name() == name) mc = (*i);
+ }
+ return mc;
+}
+
+void MenuMaker::addItem(const char *name, int index)
+{
+ MenuCategory *mc = 0;
+
+ list<MenuCategory *>::iterator i;
+ for(i=categories.begin();i != categories.end();++i)
+ if((*i)->matches(name)) mc = (*i);
+ assert(mc); // root category should always match
+
+ if(mc->name().isEmpty())
+ {
+ // if we hit the root category, it may be better to move the thing
+ // into the catchall category (looks cleaner)
+ for(i=categories.begin();i != categories.end();++i)
+ if((*i)->catchall()) mc = (*i);
+ }
+ //mc->menu()->insertItem(name,index); 000
+ //mc->menu()->insert(new KAction(name)); // index??
+ new MenuEntry(this, mc->menu(), name);
+ /*
+ KAction *action = new KAction(QString(name));
+ mc->menu()->insert(action);
+ */
+ //action->plug(mc->menu());
+ //mc->menu()->insert(new KAction(name)); // index??
+}
+
+QString MenuMaker::basename(const QString& name)
+{
+ QString result = "";
+
+ int i = name.findRev('/');
+ if(i != -1)
+ result = name.left(i);
+
+ arts_debug("basename(%s) => %s",name.local8Bit().data(),
+ result.local8Bit().data());
+ return result;
+}
+
+QString MenuMaker::catname(const QString& name)
+{
+ int i = name.findRev('/');
+ if(i >= 0)
+ return name.mid(i+1);
+
+ return name;
+}
+
+void MenuMaker::clear()
+{
+ list<MenuCategory *>::iterator i;
+ for(i=categories.begin();i != categories.end();++i)
+ {
+ /* 000
+ KActionMenu *m = (*i)->menu();
+ unsigned int k;
+
+ k = 0;
+ while(k<m->count())
+ {
+ if(m->idAt(k) != CAT_MAGIC_ID)
+ {
+ m->removeItemAt(k);
+ k = 0;
+ }
+ else
+ {
+ k++;
+ }
+ }
+ */
+ }
+}
+
+const char *MenuMaker::findID(int id)
+{
+ /* 000 ?
+ list<MenuCategory *>::iterator i;
+ for(i=categories.begin();i != categories.end();i++)
+ {
+ const char *name = (*i)->menu()->text(id);
+ if(name) return(name);
+ } */
+ return 0;
+}
+
+void MenuMaker::menuActivated(const char *text)
+{
+ emit activated(text);
+}
+
+#include "menumaker.moc"
diff --git a/arts/builder/menumaker.h b/arts/builder/menumaker.h
new file mode 100644
index 00000000..5b999dc5
--- /dev/null
+++ b/arts/builder/menumaker.h
@@ -0,0 +1,70 @@
+#ifndef __MENUMAKER_H__
+#define __MENUMAKER_H__
+
+#include <kaction.h>
+#include <qobject.h>
+#include <string>
+#include <list>
+
+class MenuMaker;
+
+class MenuEntry : public QObject
+{
+ Q_OBJECT
+
+protected:
+ MenuMaker *menumaker;
+ KAction *action;
+ QCString text;
+
+public:
+ MenuEntry(MenuMaker *menumaker, KActionMenu *parent, const char *text);
+
+public slots:
+ void activated();
+};
+
+class MenuCategory
+{
+protected:
+ KActionMenu *_menu;
+ QString _name;
+ std::list<std::string> prefixList;
+ bool _catchall;
+
+public:
+ MenuCategory(const QString& name, const char *prefix, KActionMenu *menu);
+
+ void addPrefix(const char *prefix);
+ QString name();
+ KActionMenu *menu();
+
+ bool catchall();
+ bool matches(const char *item);
+};
+
+class MenuMaker :public QObject
+{
+ Q_OBJECT
+
+ std::list<MenuCategory *> categories;
+public:
+ enum { CAT_MAGIC_ID = 10000 };
+
+ MenuMaker(KActionMenu *root);
+
+ void addCategory(const QString& name, const char *prefix);
+ MenuCategory *lookupCategoryByName(const QString& name);
+ void addItem(const char *name, int i);
+ QString basename(const QString& name);
+ QString catname(const QString& name);
+
+ void clear();
+
+ const char *findID(int id);
+
+ void menuActivated(const char *text);
+signals:
+ void activated(const char *text);
+};
+#endif
diff --git a/arts/builder/module.cpp b/arts/builder/module.cpp
new file mode 100644
index 00000000..250ee3a2
--- /dev/null
+++ b/arts/builder/module.cpp
@@ -0,0 +1,439 @@
+ /*
+
+ Copyright (C) 1998-1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "module.h"
+#include "drawutils.h"
+#include <stdio.h>
+#include <arts/debug.h>
+
+#include <qpalette.h>
+#include <qdrawutil.h>
+#include <kiconloader.h>
+#include <algorithm>
+
+using namespace std;
+
+ModulePort::ModulePort(StructureComponent *owner, const string& description,
+ int drawsegment, Direction direction, Arts::PortDesc PortDesc)
+{
+ selected = false;
+
+ this->owner = owner; // whats the syntax for that again?
+ this->drawsegment = drawsegment;
+ this->direction = direction;
+ this->PortDesc = PortDesc;
+ this->description = description.c_str();
+
+ pdID = PortDesc.ID();
+
+ isinitarg = (PortDesc.type().connType == Arts::conn_property);
+ if(isinitarg) arts_debug("port %s is an init arg", (const char *)this->description.latin1());
+ arts_debug("port %s created", (const char *)this->description.latin1());
+ conntype = none;
+ route_owner = 0;
+}
+
+bool ModulePort::down()
+{
+ return (PortDesc.isConnected() || PortDesc.hasValue() || selected);
+}
+
+QColor ModulePort::color(bool isInterface)
+{
+ if(selected) return QColor(255, 165, 0);
+
+ if(PortDesc.hasValue())
+ {
+ if(isinitarg) return QColor(180, 180, 180);
+ return QColor(100, 100, 255);
+ }
+
+ if(isinitarg) return QColor(128, 128, 128);
+
+ if(isInterface) return QColor(100, 100, 100);
+ return QColor(43, 43, 168);
+}
+
+Module::Module(Arts::ModuleDesc module, Arts::StructureDesc structuredesc,
+ StructureCanvas *canvas) : StructureComponent(canvas)
+{
+ StructureDesc = structuredesc;
+ ModuleDesc = module;
+
+ _x = ModuleDesc.x();
+ _y = ModuleDesc.y();
+ initModule();
+}
+
+Module::Module(const Arts::ModuleInfo& minfo, Arts::StructureDesc structuredesc,
+ StructureCanvas *canvas) : StructureComponent(canvas)
+{
+ StructureDesc = structuredesc;
+ ModuleDesc = StructureDesc.createModuleDesc(minfo);
+
+ initModule();
+}
+
+void Module::initModule()
+{
+ QString iconname;
+ KIconLoader iconloader;
+
+ _selected = false;
+ _visible = false;
+ _height = 1;
+ _name = ModuleDesc.name().c_str();
+ _pixmap = NULL;
+
+// test if pixmap available
+
+ iconname = _name + ".xpm";
+
+ _pixmap = new QPixmap(iconloader.loadIcon(iconname, KIcon::User));
+ if(!_pixmap->height())
+ {
+ iconname = _name + ".png";
+ delete _pixmap;
+ _pixmap = new QPixmap( iconloader.loadIcon( iconname, KIcon::User ) );
+ if( !_pixmap->height() )
+ {
+ delete _pixmap;
+ _pixmap = 0;
+ }
+ }
+// try again without Arts:: - prefix
+
+ if(iconname.startsWith("Arts::"))
+ {
+ iconname = iconname.mid(6);
+
+ _pixmap = new QPixmap(iconloader.loadIcon(iconname, KIcon::User));
+ if(!_pixmap->height())
+ {
+ iconname.replace( iconname.length() - 4, 3, "png" );
+ delete _pixmap;
+ _pixmap = new QPixmap(iconloader.loadIcon(iconname, KIcon::User));
+ if( !_pixmap->height() )
+ {
+ delete _pixmap;
+ _pixmap = 0;
+ }
+ }
+ }
+/*
+ FILE *test = fopen(QFile::encodeName(iconname), "r");
+ if(test)
+ {
+ pixmap = new QPixmap(iconname);
+ fclose(test);
+ }
+*/
+
+// create lists with inports & outports for this module
+// and bind them to it ...
+
+ arts_debug("Getting ports...");
+ vector<Arts::PortDesc >*ports = ModuleDesc.ports();
+ unsigned long portpos;
+ long indraw = 1, outdraw = 2;
+
+ for(portpos = 0; portpos < ports->size(); portpos++)
+ {
+ Arts::PortDesc pd = (*ports)[portpos];
+ ModulePort *p;
+
+ arts_debug("CREATING %s", pd.name().c_str());
+ switch(pd.type().direction)
+ {
+ case Arts::input:
+ p = new ModulePort(this, pd.name(), indraw++,
+ ModulePort::in, pd);
+ inports.push_back(p);
+ break;
+ case Arts::output:
+ p = new ModulePort(this, pd.name(), outdraw++,
+ ModulePort::out, pd);
+ outports.push_back(p);
+ break;
+ default:
+ assert(false); // shouldn't happen!
+ }
+ }
+
+ delete ports;
+
+ _width = 1 + max(inports.size(), outports.size() + 1);
+
+ mdID = ModuleDesc.ID();
+ isInterface = ModuleDesc.isInterface();
+}
+
+Module::~Module()
+{
+ arts_debug("hide...");
+ hide();
+ arts_debug("sdfmd...");
+ list<ModulePort *>::iterator i;
+
+ for(i = inports.begin(); i != inports.end(); ++i) delete *i;
+ inports.clear();
+
+ for(i = outports.begin(); i != outports.end(); ++i) delete *i;
+ outports.clear();
+
+ StructureDesc.freeModuleDesc(ModuleDesc);
+ arts_debug("ok...");
+ delete _pixmap;
+}
+
+bool Module::moveInternal(int x, int y)
+{
+ return ModuleDesc.moveTo(x, y);
+}
+
+int Module::width() const
+{
+ return _width;
+}
+
+int Module::height() const
+{
+ return _height;
+}
+
+StructureComponent::ComponentType Module::type()
+{
+ return ctModule;
+}
+
+bool Module::drawNeedsBackground(int segment)
+{
+ return (segment == 0);
+}
+
+void Module::drawSegment(QPainter *p, int cellsize, int segment)
+{
+ int border = cellsize / 10; // for the logo
+ int ltop = (cellsize - border)/2;
+ int lbot = (cellsize + border)/2;
+
+ QColor mcolor(43, 43, 168);
+ QColor mcolorlight(164, 176, 242);
+
+ if(isInterface)
+ {
+ mcolor = QColor(100, 100, 100);
+ mcolorlight = QColor(160, 160, 160);
+ }
+ QColorGroup g( Qt::white, Qt::blue, mcolorlight, mcolor.dark(), mcolor,
+ Qt::black, Qt::black );
+ QBrush fill( mcolor );
+ QPen textpen(QColor(255, 255, 180), 1);
+
+ if(segment == 0)
+ {
+ qDrawShadePanel(p, border, border, cellsize - 2*border + 1, cellsize - 2*border + 1,
+ g, false, 1, &fill);
+ p->fillRect(cellsize - border - 1, ltop, cellsize, lbot - ltop + 1, fill);
+ p->setPen(g.light());
+ p->drawLine(cellsize - border, ltop - 1, cellsize, ltop - 1);
+ p->setPen(g.dark());
+ p->drawLine(cellsize - border, lbot + 1, cellsize, lbot + 1);
+ if(_pixmap)
+ {
+ int destsize = (cellsize - 4*border);
+ float sx = (float)destsize/(float)_pixmap->width();
+ float sy = (float)destsize/(float)_pixmap->height();
+
+ QWMatrix matrix;
+ matrix.scale(sx, sy);
+ QPixmap pmscaled = _pixmap->xForm(matrix);
+ p->drawPixmap(border*2, border*2, pmscaled);
+ }
+ return;
+ }
+
+ p->fillRect(0, 0, cellsize, cellsize, fill);
+
+ /*
+ * take care of the bevel lines around the module
+ */
+
+ p->setPen(g.light());
+ p->drawLine(0, 0, cellsize - 1, 0);
+ if(segment < 2)
+ p->drawLine(0, 0, 0, cellsize - 1);
+
+ p->setPen(g.dark());
+ p->drawLine(cellsize - 1, cellsize - 1, 0, cellsize - 1);
+ if(segment == 0 || segment == width() - 1)
+ p->drawLine(cellsize - 1, cellsize - 1, cellsize - 1, 0);
+
+ /*
+ * now draw the ports
+ */
+ int direction;
+
+ for(direction = 0;direction < 2; direction++)
+ {
+ ModulePort *port = findPort(segment, direction);
+
+ if(port)
+ {
+ int border = cellsize/7;
+ int textwidth;
+ QString label = DrawUtils::cropText(p, port->description,
+ cellsize/2, textwidth);
+
+ QBrush pbrush(port->color(isInterface));
+
+ port->clickrect = QRect(border, direction * cellsize/2 + border,
+ cellsize/2 - 2*border, cellsize/2 - 2*border);
+ qDrawShadePanel(p, port->clickrect, g, port->down(), 2, &pbrush);
+
+#if 0
+ QBrush fillport(fill);
+ if(port->isinitarg)
+ {
+ fillport = QColor(128, 128, 128);
+ }
+
+ if(port->selected)
+ {
+ QBrush fillorange(QColor(255, 165, 0));
+ qDrawShadePanel(p, port->clickrect, g, true, 2, &fillorange);
+ }
+ else
+ {
+ if(port->PortDesc->isConnected())
+ {
+ qDrawShadePanel(p, port->clickrect, g, true, 2, &fillport);
+ }
+ else if(port->PortDesc->hasValue())
+ {
+ QBrush fillp(QColor(100, 100, 255));
+ if(port->isinitarg)
+ {
+ fillp = QColor(180, 180, 180);
+ }
+ qDrawShadePanel(p, port->clickrect, g, true, 2, &fillp);
+ }
+ else // not connected and no value();
+ qDrawShadePanel(p, port->clickrect, g, false, 2, &fillport);
+ }
+#endif
+
+ p->setPen(textpen);
+ p->drawText((cellsize - border)/2,
+ (1 + direction) * (cellsize/2) - border, label);
+ }
+ }
+
+ /*
+ * if it was the rightmost part of the module, it has the module name
+ * and the connection to the logo as well
+ */
+
+ if(segment == 1)
+ {
+ // object type label
+ int textwidth;
+ QString label = DrawUtils::cropText(p, _name, cellsize - 4, textwidth);
+
+ p->setPen(textpen);
+ p->fillRect(1, cellsize - 16, textwidth + 7, 15, QBrush(g.dark()));
+ p->drawText(4, cellsize - 5, label);
+
+ // logo connection
+ p->setPen(mcolor);
+ p->drawLine(0, ltop, 0, lbot);
+ }
+
+ /*
+ * when selected, draw a line of white dots around the module
+ */
+
+ if(selected())
+ {
+ QPen pen(Qt::white, 1, Qt::DotLine);
+
+ p->setPen(pen);
+ p->drawLine(0, 0, cellsize - 1, 0);
+ p->drawLine(0, cellsize - 1, cellsize - 1, cellsize - 1);
+ if(segment == 1)
+ p->drawLine(0, 0, 0, cellsize - 1);
+ if(segment == _width - 1)
+ p->drawLine(cellsize - 1, 0, cellsize - 1, cellsize - 1);
+ }
+}
+
+ModulePort *Module::findPort(int xoffset, int direction)
+{
+ list<ModulePort *>*ports;
+ list<ModulePort *>::iterator i;
+
+ long n;
+
+ if(direction == 0) ports = &inports; else ports = &outports;
+
+ i = ports->begin();
+ n = xoffset - 1 - direction;
+
+ if(n < (long)ports->size() && n >= 0)
+ {
+ while(n > 0) { n--; i++; }
+ return (*i);
+ }
+ return(NULL);
+}
+
+ModulePort *Module::portAt(int segment, int x, int y)
+{
+ for(int direction = 0; direction < 2; direction++)
+ {
+ ModulePort *port = findPort(segment, direction);
+ if(port)
+ {
+ QPoint clickpoint(x, y);
+ if(port->clickrect.contains(clickpoint)) return port;
+ }
+ }
+ return 0;
+}
+
+void Module::dumpPorts(list<ModulePort *>& ports)
+{
+ list<ModulePort *>::iterator i;
+ for(i = inports.begin(); i != inports.end(); ++i) ports.push_back(*i);
+ for(i = outports.begin(); i != outports.end(); ++i) ports.push_back(*i);
+}
+
+QPixmap *Module::pixmap()
+{
+ return _pixmap;
+}
+
+QString Module::name()
+{
+ return _name;
+}
+
+// vim: sw=4 ts=4 noet
diff --git a/arts/builder/module.h b/arts/builder/module.h
new file mode 100644
index 00000000..ff19d44b
--- /dev/null
+++ b/arts/builder/module.h
@@ -0,0 +1,108 @@
+ /*
+
+ Copyright (C) 1998 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef __MODULE_H_
+#define __MODULE_H_
+
+#include <list>
+
+#include <qpixmap.h>
+#include <qstring.h>
+#include <qrect.h>
+#include <ksimpleconfig.h>
+
+#include "artsbuilder.h"
+#include "scomponent.h"
+
+class ModulePort
+{
+ //ModulePort *connection;
+
+public:
+ Arts::PortDesc PortDesc;
+ long pdID;
+
+ enum ConnType {none, source, dest, value, conf};
+ enum Direction {in, out};
+
+ bool selected;
+ StructureComponent *owner;
+
+ QString description;
+ QRect clickrect;
+ ConnType conntype;
+ Direction direction;
+ int drawsegment;
+ bool isinitarg;
+
+ long route_owner;
+
+ ModulePort( StructureComponent *owner, const std::string& description,
+ int drawsegment, Direction direction, Arts::PortDesc PortDesc);
+
+ bool down();
+ QColor color(bool isInterface);
+};
+
+class Module :public StructureComponent
+{
+protected:
+ Arts::StructureDesc StructureDesc;
+ Arts::ModuleDesc ModuleDesc;
+
+ QPixmap *_pixmap;
+ QString _name;
+
+ int _width, _height;
+ bool moveInternal(int x, int y);
+
+ void initModule();
+
+public:
+ Module( Arts::ModuleDesc moduledesc, Arts::StructureDesc structuredesc,
+ StructureCanvas *canvas);
+ Module( const Arts::ModuleInfo& minfo, Arts::StructureDesc structuredesc,
+ StructureCanvas *canvas);
+ virtual ~Module();
+
+ ModulePort *findPort(int xoffset, int direction);
+
+ bool isInterface;
+
+ long mdID;
+ std::list<ModulePort *> inports, outports;
+
+// StructureComponent interface
+
+ int width() const;
+ int height() const;
+ ComponentType type();
+
+ ModulePort *portAt(int segment, int x, int y);
+ void dumpPorts(std::list<ModulePort *>& ports);
+
+ bool drawNeedsBackground(int segment);
+ void drawSegment(QPainter *dest, int cellsize, int segment);
+ QPixmap *pixmap();
+ QString name();
+ };
+
+#endif
diff --git a/arts/builder/mwidget.cpp b/arts/builder/mwidget.cpp
new file mode 100644
index 00000000..b6c3d841
--- /dev/null
+++ b/arts/builder/mwidget.cpp
@@ -0,0 +1,652 @@
+#include "mwidget.h"
+#include "autorouter.h"
+
+#include <arts/debug.h>
+
+//#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include <qtimer.h>
+#include <qpainter.h>
+#include <qevent.h>
+
+#include "createtool.h"
+
+Structure *ModuleWidget::theStructure()
+{
+ return structure;
+}
+
+void ModuleWidget::addInterface ( const Arts::ModuleInfo& minfo )
+{
+ delete activeTool;
+ activeTool = new CreateInterfaceTool(this, minfo);
+}
+
+void ModuleWidget::addModule ( const Arts::ModuleInfo& minfo )
+{
+ delete activeTool;
+ activeTool = new CreateModuleTool(this, minfo);
+}
+
+void ModuleWidget::addPort ( const Arts::PortType& type )
+{
+ delete activeTool;
+ activeTool = new CreatePortTool(this, type);
+}
+
+StructurePort *ModuleWidget::insertPort( const Arts::PortType& type, int x, int y )
+{
+ StructurePort *port = structure->createStructurePort(type);
+ unselectAll();
+ port->move(x, y);
+ port->setSelected(true);
+ port->show();
+
+ return port;
+}
+
+void ModuleWidget::leaveTool(MWidgetTool *tool, bool wasModified)
+{
+ assert(tool == activeTool);
+ delete tool;
+ activeTool = 0;
+ if(wasModified)
+ emit modified(wasModified);
+}
+
+QPoint ModuleWidget::componentPos(const StructureComponent *component) const
+{
+ int cellx = 0, celly = 0;
+ colXPos(component->x(), &cellx);
+ rowYPos(component->y(), &celly);
+
+ return QPoint(cellx, celly);
+}
+
+QPoint ModuleWidget::portPos(const ModulePort *port) const
+{
+ int cellx = 0, celly = 0;
+ colXPos(port->owner->x() + port->drawsegment, &cellx);
+ rowYPos(port->owner->y(), &celly);
+
+ return QPoint(cellx, celly);
+}
+
+bool ModuleWidget::insertModule( Module *newModule )
+{
+ if(hasSpace(newModule, newModule->x(), newModule->y(), true))
+ {
+ newModule->show();
+ reRoute();
+ return true;
+ }
+ return false;
+}
+
+void ModuleWidget::findAt(int windowX, int windowY,
+ StructureComponent *&component, ModulePort *&port)
+{
+ int x = findCol(windowX);
+ int y = findRow(windowY);
+
+ component = structure->componentAt(x, y, false);
+
+ if(component)
+ {
+ int cellx = 0, celly = 0;
+ colXPos(x, &cellx);
+ rowYPos(y, &celly);
+
+ port = component->portAt(x - (component->x()),
+ windowX - cellx, windowY - celly);
+ }
+ else
+ port = 0L;
+}
+
+void ModuleWidget::selectComponent( StructureComponent *component, bool onlyThis )
+{
+ beginUpdate();
+ if(onlyThis)
+ unselectAll();
+
+ if(!(component->selected()))
+ {
+ component->setSelected(true);
+ emit componentSelected(component);
+ } else
+ if(!onlyThis)
+ {
+ component->setSelected(false);
+ emit componentSelected(0L);
+ }
+ endUpdate();
+}
+
+void ModuleWidget::mousePressEvent( QMouseEvent *e )
+{
+ if(activeTool)
+ {
+ activeTool->mousePressEvent(e);
+ return;
+ }
+
+ if( e->button() == LeftButton )
+ {
+ StructureComponent *component;
+ ModulePort *port;
+ findAt(e->x(), e->y(), component, port);
+
+ if(component)
+ {
+ if(port)
+ {
+ // user clicked in port
+ selectPort(port);
+
+ delete activeTool;
+ activeTool = new ConnectPortsTool(this, port);
+ }
+ else
+ {
+ // user clicked in component
+ activeTool = new MoveComponentsTool(this, e);
+
+ // maintain selected group when pressing the shift or control button
+ selectComponent(component, !((e->state() & ControlButton)
+ || (e->state() & ShiftButton)));
+ }
+ }
+ else
+ {
+ // unselect all if user clicks on background (without shift)
+ if(!(e->state() & ShiftButton))
+ {
+ beginUpdate();
+ unselectAll();
+ endUpdate();
+ }
+ }
+ }
+}
+
+void ModuleWidget::mouseMoveEvent( QMouseEvent *e )
+{
+ if(activeTool)
+ {
+ activeTool->mouseMoveEvent(e);
+ return;
+ }
+}
+
+void ModuleWidget::mouseReleaseEvent( QMouseEvent *e )
+{
+ if(activeTool)
+ {
+ activeTool->mouseReleaseEvent(e);
+ return;
+ }
+}
+
+// may be called with port == 0
+void ModuleWidget::selectPort( ModulePort *port, bool newMode )
+{
+ beginUpdate();
+
+ if(selectedPort && (selectedPort!= port))
+ {
+ // unselect previous
+ selectedPort->selected = false;
+ selectedPort->owner->redraw();
+ if(selectedPort->owner->selected())
+ emit componentSelected(selectedPort->owner);
+ else
+ emit portSelected(0L);
+ selectedPort = 0L;
+ }
+
+ if(port)
+ {
+ port->selected = newMode;
+ selectedPort = port;
+ selectComponent(selectedPort->owner);
+ selectedPort->owner->redraw();
+ }
+ emit portSelected(port); // FIXME: should be "portSelectionChanged"
+
+ endUpdate();
+}
+
+void ModuleWidget::startConnection( ModulePort *port )
+{
+ delete activeTool;
+ activeTool = new ConnectPortsTool(this, port);
+}
+
+void ModuleWidget::portPropertiesChanged( ModulePort *port )
+{
+ reRoute();
+}
+
+bool ModuleWidget::hasSpace(StructureComponent *c, int destx, int desty,
+ bool ignore_selected)
+{
+ if((destx < 0) || (desty < 0))
+ return false;
+ if((destx + c->width() > numCols()) || (desty + c->height() > numRows()))
+ return false;
+
+ for(int ddx = 0; ddx < c->width(); ddx++)
+ {
+ for(int ddy = 0; ddy < c->height(); ddy++)
+ {
+ if(structure->componentAt(destx + ddx, desty + ddy, ignore_selected))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void ModuleWidget::paintCellBackground(QPainter *p, int y, int x)
+{
+ QColor bgcolor;
+
+ if((y & 1) == 1)
+ bgcolor = QColor(168, 168, 168);
+ else
+ bgcolor = QColor(146, 168, 146);
+
+ p->fillRect(0, 0, cellsize, cellsize, QBrush(bgcolor));
+
+ p->setPen(bgcolor.dark(115));
+ p->drawLine(0, 0, 0, cellsize - 1);
+ p->drawLine(0, 0, cellsize - 1, 0);
+
+ if(x == (numCols() - 1))
+ p->drawLine(cellsize - 1, 0, cellsize - 1, cellsize - 1);
+ if(y == (numRows() - 1))
+ p->drawLine(0, cellsize - 1, cellsize - 1, cellsize - 1);
+}
+
+void ModuleWidget::unselectAll()
+{
+ setSelectAll(false);
+}
+
+void ModuleWidget::setSelectAll(bool newstate)
+{
+ std::list<StructureComponent *>::iterator module;
+
+ for(module = structure->getComponentList()->begin();
+ module != structure->getComponentList()->end(); module++)
+ {
+ (*module)->setSelected(newstate);
+ }
+}
+
+void ModuleWidget::beginUpdate()
+{
+ updateDepth++;
+}
+
+void ModuleWidget::endUpdate()
+{
+ if(!--updateDepth)
+ {
+ std::list<QRect>::iterator i;
+
+ for(i = UpdateList.begin(); i != UpdateList.end(); i++)
+ {
+ redrawCells(*i);
+ }
+
+ UpdateList.clear();
+ }
+}
+
+void ModuleWidget::redrawRect(int x, int y, int width, int height)
+{
+ QRect r = QRect(x, y, width, height);
+
+ if(!updateDepth)
+ {
+ redrawCells(r);
+ }
+ else
+ {
+ UpdateList.push_back(r);
+ }
+}
+
+void ModuleWidget::redrawCells(QRect &r)
+{
+ int x, y;
+
+ for(x = r.left(); x <= r.right(); x++)
+ {
+ for(y = r.top(); y <= r.bottom(); y++)
+ {
+ updateCell(y, x, false);
+ }
+ }
+}
+
+
+void ModuleWidget::reRoute()
+{
+// clear everything
+ autorouter->clear();
+
+// add structure components (external ports/modules) to the router, so that
+// cables won't be drawn over them
+
+ std::list<StructureComponent *>::iterator c;
+ std::list<ModulePort *> portlist;
+
+ for(c = structure->getComponentList()->begin();
+ c != structure->getComponentList()->end(); c++)
+ {
+ StructureComponent *sc = *c;
+ autorouter->set(sc->x()*2, sc->y()*2,
+ (sc->x() + sc->width())*2 - 1,
+ (sc->y() + sc->height())*2 - 1, AutoRouter::solid);
+
+ sc->dumpPorts(portlist);
+ }
+
+ std::list<ModulePort *>::iterator pi;
+
+// build a map with all input ports to find corresponding ports of connections
+
+ std::map<long, ModulePort *> portmap;
+
+ for(pi = portlist.begin(); pi != portlist.end(); ++pi)
+ {
+ ModulePort *port = *pi;
+
+ if(port->direction == ModulePort::in) portmap[port->pdID] = port;
+ }
+
+// add connections to the router
+
+ /*
+ * assign colors after the following algorithm:
+ *
+ * - initialize assuming that each port has its own color
+ * - if two ports are connected, they must have the same color, that
+ * is, all references to the two colors must be made the same
+ *
+ * these colors are not used for drawing, but for making lines of
+ * different groups of ports not collide in the autorouter (ownership)
+ */
+ std::map<ModulePort *, long> color;
+ vector<long> owner(portlist.size());
+
+ long maxcolor = 0;
+ for(pi = portlist.begin(); pi != portlist.end(); ++pi)
+ color[*pi] = maxcolor++;
+
+ for(pi = portlist.begin(); pi != portlist.end(); ++pi)
+ {
+ ModulePort *src = *pi;
+ unsigned long c;
+ if(src->direction == ModulePort::out && src->PortDesc.isConnected())
+ {
+ vector<Arts::PortDesc> *conn = src->PortDesc.connections();
+
+ for(c = 0; c < conn->size(); c++)
+ {
+ ModulePort *dest = portmap[(*conn)[c].ID()];
+ long color_src = color[src];
+ long color_dest = color[dest];
+
+ if(color_src != color_dest)
+ {
+ std::list<ModulePort *>::iterator pi2;
+
+ for(pi2 = portlist.begin(); pi2 != portlist.end(); pi2++)
+ {
+ ModulePort *p = *pi2;
+
+ if(color[p] == color_dest) color[p] = color_src;
+ }
+ }
+ }
+ }
+ }
+
+ for(pi = portlist.begin(); pi != portlist.end(); ++pi)
+ {
+ ModulePort *p = *pi;
+
+ if(p->direction == ModulePort::out && p->PortDesc.isConnected())
+ {
+ ModulePort *src = p, *dest;
+ long& route_owner = owner[color[src]];
+ unsigned long c;
+
+ vector<Arts::PortDesc> *conn = p->PortDesc.connections();
+
+ for(c = 0; c < conn->size(); c++)
+ {
+ dest = portmap[(*conn)[c].ID()];
+ if(dest) // otherwise something bad has happend?
+ {
+/*
+ arts_debug("autoroute add connection port %s.%s to %s.%s",
+ src->owner->type.local8Bit().data(),src->description.local8Bit().data(),
+ dest->owner->type.local8Bit().data(),dest->description.local8Bit().data());
+*/
+
+ int x1 = (src->owner->x() + src->drawsegment)*2;
+ int y1 = src->owner->y()*2 + 1;
+
+ int x2 = (dest->owner->x() + dest->drawsegment)*2;
+ int y2 = dest->owner->y()*2;
+
+ route_owner = autorouter->connect(x1, y1, x2, y2, route_owner);
+ }
+ }
+
+ delete conn;
+ }
+ }
+
+ autorouter->sync();
+}
+
+void ModuleWidget::redrawAll()
+{
+// redraw everything
+ QRect updaterect(0, 0, cols, rows);
+ redrawCells(updaterect);
+}
+
+void ModuleWidget::paintConnection(QPainter *p, int x, int y, int arx, int ary)
+{
+ long linetype = autorouter->get(arx, ary);
+ long ud_owner = -1, lr_owner = -1, lr_break = 0, ud_break = 0;
+
+ autorouter->getowners(arx, ary, ud_owner, lr_owner);
+
+ p->setPen(QColor(255, 255, 255));
+
+/*
+ if(linetype == AutoRouter::none)
+ {
+ p->drawPoint(x + cellsize/4, y + cellsize/4);
+ }
+ if(linetype & AutoRouter::solid)
+ {
+ QBrush whitefill(QColor(255, 255, 255));
+
+ p->fillRect(x + cellsize/6, y + cellsize/6, cellsize/6, cellsize/6, whitefill);
+ }
+*/
+ x += cellsize/4;
+ y += cellsize/4;
+
+ // both used?
+ if(ud_owner != -1 && lr_owner != -1)
+ {
+ // and not of the same owner?
+ if(ud_owner != lr_owner)
+ {
+ // then we'll have to paint one of them broken
+ if(ud_owner > lr_owner)
+ lr_break = cellsize/8;
+ else
+ ud_break = cellsize/8;
+ }
+ }
+
+ if(linetype & AutoRouter::left)
+ p->drawLine(x - cellsize/4, y, x - lr_break, y);
+ if(linetype & AutoRouter::right)
+ p->drawLine(x + cellsize/4, y, x + lr_break, y);
+ if(linetype & AutoRouter::up)
+ p->drawLine(x, y - cellsize/4, x, y - ud_break);
+ if(linetype & AutoRouter::down)
+ p->drawLine(x, y + cellsize/4, x, y + ud_break);
+}
+
+void ModuleWidget::paintConnections(QPainter *p, int y, int x)
+{
+ // paints connections in the given 2x2-autorouter-block being a 1x1 block to the user
+ for(int dx = 0; dx < 2; dx++)
+ for(int dy = 0; dy < 2; dy++)
+ paintConnection(p, (cellsize*dx)/2, (cellsize*dy)/2, x*2 + dx, y*2 + dy);
+}
+
+void ModuleWidget::paintCell(QPainter *p, int y, int x)
+{
+#if 0 /* PORT */
+ if(theArtsBuilderApp->eventStackDepth() > 1)
+ {
+ // FIXME: set some redraw flag or something like that
+ return;
+ }
+#endif
+
+ std::list<StructureComponent *>::iterator c;
+ for(c = structure->getComponentList()->begin();
+ c != structure->getComponentList()->end(); c++)
+ {
+ StructureComponent *mwc = *c;
+ if(y == mwc->y() && mwc->visible())
+ {
+ int xoffset = x - mwc->x();
+
+ if(xoffset >= 0 && xoffset < mwc->width())
+ {
+ if(mwc->drawNeedsBackground(xoffset))
+ paintCellBackground(p, y, x);
+
+ mwc->drawSegment(p, cellsize, xoffset);
+ paintConnections(p, y, x);
+ return;
+ }
+ }
+ }
+ paintCellBackground(p, y, x);
+ paintConnections(p, y, x);
+}
+
+// ---------------------------------------------------------------------------
+// public part of modulewidget
+// ---------------------------------------------------------------------------
+
+void ModuleWidget::setZoom(int zoom)
+{
+ cellsize = (int)(50.0 * (float)zoom/100);
+
+ setCellHeight(cellsize);
+ setCellWidth(cellsize);
+ updateTableSize();
+ resize(cellsize*cols, cellsize*rows);
+}
+
+void ModuleWidget::selectAll()
+{
+ setSelectAll(true);
+}
+
+void ModuleWidget::reInit()
+{
+ emit componentSelected(0);
+ selectedPort = 0L;
+
+ delete activeTool;
+ activeTool = 0L;
+
+ reRoute();
+}
+
+void ModuleWidget::delModule()
+{
+ int numSelected = structure->countSelected();
+
+ if(!numSelected) return;
+
+ if(KMessageBox::warningContinueCancel(0,
+ i18n("Delete %n selected module, port or connection? (No undo possible.)",
+ "Delete %n selected modules, ports and connections? (No undo possible.)",
+ numSelected), QString::null, i18n("&Delete")) == KMessageBox::Continue)
+ {
+ selectPort(0L);
+ emit componentSelected(0);
+ structure->deleteSelected();
+ reRoute();
+ }
+}
+
+void ModuleWidget::autoRedrawRouter()
+{
+ if(autorouter->needRedraw()) redrawAll();
+}
+
+ModuleWidget::ModuleWidget(Structure *structure, QWidget *parent, const char *name, WFlags f)
+ : QtTableView( parent, name, f),
+ updateDepth( 0 ),
+ activeTool( 0L ),
+ structure( structure ),
+ selectedPort( 0L )
+{
+ arts_debug("PORT: mw; getmodulelist");
+ this->ModuleList = structure->getModuleList();
+ arts_debug("PORT: mw; cols&rows");
+
+ cols = 24;
+ rows = 32;
+
+ setNumCols(cols);
+ setNumRows(rows);
+ setTableFlags(Tbl_autoScrollBars);
+ setZoom(100);
+
+ setFocusPolicy( NoFocus );
+
+ arts_debug("PORT: mw; bgmode");
+ setBackgroundMode(NoBackground);
+
+ arts_debug("PORT: mw; new ar %d,%d", cols, rows);
+ autorouter = new AutoRouter(cols*2, rows*2);
+
+ arts_debug("PORT: mw; new ar ok - qtimer");
+ QTimer *timer = new QTimer( this );
+ connect( timer, SIGNAL(timeout()),
+ this, SLOT(autoRedrawRouter()) );
+
+ arts_debug("PORT: mw; tstart");
+ timer->start( 100, FALSE ); // 100 ms reoccurring check
+}
+
+ModuleWidget::~ModuleWidget()
+{
+ delete autorouter;
+}
+
+#include "mwidget.moc"
diff --git a/arts/builder/mwidget.h b/arts/builder/mwidget.h
new file mode 100644
index 00000000..e6716e3e
--- /dev/null
+++ b/arts/builder/mwidget.h
@@ -0,0 +1,129 @@
+ /*
+
+ Copyright (C) 1999 Stefan Westerfeld <stefan@space.twc.de>,
+ Hans Meine <hans_meine@gmx.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef MWIDGET_H
+#define MWIDGET_H
+
+#include "structure.h"
+#include "module.h"
+#include "scomponent.h"
+
+#include "qttableview.h"
+
+class AutoRouter;
+class MWidgetTool;
+
+class ModuleWidget :public QtTableView, public StructureCanvas
+{
+ Q_OBJECT
+ friend class MWidgetTool;
+ friend class CreateTool;
+ friend class CreateInterfaceTool;
+ friend class CreateModuleTool;
+ friend class CreatePortTool;
+ friend class MoveComponentsTool;
+ friend class ConnectPortsTool;
+
+protected:
+ int cellsize, cols, rows;
+ int updateDepth;
+
+ MWidgetTool *activeTool;
+ AutoRouter *autorouter;
+ Structure *structure;
+
+// for connecting ports
+
+ ModulePort *selectedPort;
+
+ std::list<Module *> *ModuleList;
+ std::list<QRect> UpdateList;
+
+ void paintCell(QPainter *p, int y, int x);
+ void paintCellBackground(QPainter *p, int y, int x);
+ void paintConnection(QPainter *p, int x, int y, int arx, int ary);
+ void paintConnections(QPainter *p, int y, int x);
+ void mousePressEvent( QMouseEvent *e );
+ void mouseMoveEvent( QMouseEvent *e );
+ void mouseReleaseEvent( QMouseEvent *e );
+
+ bool hasSpace(StructureComponent *c, int destx, int desty,
+ bool ignore_selected = false);
+
+ void reRoute();
+ void setSelectAll(bool newstate);
+
+ void beginUpdate();
+ void redrawCells(QRect &r);
+ void redrawAll();
+ void endUpdate();
+
+// for StructureComponents:
+
+ void redrawRect(int x, int y, int width, int height);
+
+// for CreateTool:
+
+ bool insertModule(Module *newModule);
+ StructurePort *insertPort(const Arts::PortType& type, int x, int y);
+
+// for this and ConnectTool:
+
+ void findAt(int windowX, int windowY,
+ StructureComponent *&component,
+ ModulePort *&port);
+
+signals:
+ void portSelected( ModulePort * );
+ void componentSelected( StructureComponent * );
+ void modified(bool); // the bool mod. flag is always true for easier connects
+
+public:
+ Structure *theStructure();
+
+ void addModule(const Arts::ModuleInfo& minfo);
+ void addInterface(const Arts::ModuleInfo& minfo);
+ void addPort(const Arts::PortType& type);
+ void reInit();
+ void leaveTool(MWidgetTool *tool, bool wasModified= false);
+
+ QPoint componentPos(const StructureComponent *component) const;
+ QPoint portPos(const ModulePort *port) const;
+
+ ModuleWidget( Structure *structure, QWidget *parent = 0,
+ const char *name = 0, WFlags f = 0);
+
+ ~ModuleWidget();
+
+public slots:
+ void autoRedrawRouter();
+ void setZoom(int zoom);
+ void delModule();
+ void selectAll();
+ void unselectAll();
+
+ void selectPort( ModulePort *port, bool newMode = true );
+ void selectComponent( StructureComponent *component, bool onlyThis = true );
+ void startConnection( ModulePort * );
+ void portPropertiesChanged( ModulePort *port );
+};
+
+#endif // MWIDGET_H
diff --git a/arts/builder/pics/Makefile.am b/arts/builder/pics/Makefile.am
new file mode 100644
index 00000000..26e29f8f
--- /dev/null
+++ b/arts/builder/pics/Makefile.am
@@ -0,0 +1,24 @@
+# this 10 paths are KDE specific. Use them:
+# kde_htmldir Where your docs should go to. (contains lang subdirs)
+# kde_appsdir Where your application file (.kdelnk) should go to.
+# kde_icondir Where your icon should go to.
+# kde_minidir Where your mini icon should go to.
+# kde_datadir Where you install application data. (Use a subdir)
+# kde_locale Where translation files should go to.(contains lang subdirs)
+# kde_cgidir Where cgi-bin executables should go to.
+# kde_confdir Where config files should go to.
+# kde_mimedir Where mimetypes should go to.
+# kde_toolbardir Where general toolbar icons should go to.
+# kde_wallpaperdir Where general wallpapers should go to.
+
+pix_DATA = Synth_ADD.xpm Synth_ATAN_SATURATE.xpm Synth_BUS_DOWNLINK.xpm \
+ Synth_BUS_UPLINK.xpm Synth_DEBUG.xpm Synth_ENVELOPE_ADSR.xpm \
+ Synth_FILEPLAY.xpm Synth_MIDI_DEBUG.xpm Synth_MUL.xpm Synth_PLAY.xpm \
+ Synth_PLAY_WAV.xpm Synth_PSCALE.xpm Synth_SEQUENCE.xpm Synth_WAVE_SAW.xpm \
+ Synth_WAVE_SIN.xpm Synth_WAVE_SQUARE.xpm Synth_WAVE_TRI.xpm Synth_XFADE.xpm \
+ Synth_MOOG_VCF.xpm Synth_RC.xpm Synth_SHELVE_CUTOFF.xpm Synth_MULTI_ADD.xpm \
+ Synth_AMAN_PLAY.xpm Synth_SEQUENCE_FREQ.png Synth_DIV.xpm
+
+pixdir = $(kde_datadir)/artsbuilder/pics/
+
+KDE_ICON = artsbuilder action-artsbuilderexecute
diff --git a/arts/builder/pics/Synth_ADD.xpm b/arts/builder/pics/Synth_ADD.xpm
new file mode 100644
index 00000000..d3ff5f82
--- /dev/null
+++ b/arts/builder/pics/Synth_ADD.xpm
@@ -0,0 +1,305 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"64 64 234 2",
+/* colors */
+" c #535B83",
+" . c #34427F",
+" X c #DBDDE4",
+" o c #303C7B",
+" O c #2E3A79",
+" + c #4A517A",
+" @ c #2C3877",
+" # c #4D589E",
+" $ c #2C356D",
+" % c #354283",
+" & c #B8BAC8",
+" * c #334081",
+" = c #1E2869",
+" - c #283476",
+" ; c #3F4A7C",
+" : c #475591",
+" > c #37437E",
+" , c #232E71",
+" < c #202A6E",
+" 1 c #43518D",
+" 2 c #1F2A6D",
+" 3 c #1E286C",
+" 4 c #1D286B",
+" 5 c #2B367C",
+" 6 c #46517C",
+" 7 c #4D5D9A",
+" 8 c #1A235E",
+" 9 c #394783",
+" 0 c #485795",
+" q c #27316E",
+" w c #465593",
+" e c #525A81",
+" r c #515A80",
+" t c #34417E",
+" y c #323F7C",
+" u c #212B68",
+" i c #606BB3",
+" p c #2E3978",
+" a c #252F6F",
+" s c #344181",
+" d c #323F7F",
+" f c #303D7D",
+" g c #2F3B7C",
+" h c #2D397A",
+" j c #3D498D",
+" k c #293576",
+" l c #283375",
+" z c #232D70",
+" x c #202B6D",
+" c c #53649F",
+" v c #1E296B",
+" b c #B2B5C4",
+" n c #1D276A",
+" m c #2D377D",
+" M c #2B377B",
+" N c #4E5E9A",
+" B c #2A357A",
+" V c #293379",
+" C c #283378",
+" Z c #979AB0",
+" A c #4B5A97",
+" S c #495895",
+" D c #485894",
+" F c #475693",
+" G c #455491",
+" H c #43528F",
+" J c #33407C",
+" K c #C5C8D3",
+" L c #30396F",
+" P c #3F4B81",
+" I c #717591",
+" U c #313E7D",
+" Y c #202A69",
+" T c #D7D9E1",
+" R c #D6D7E0",
+" E c #2C3878",
+" W c #273273",
+" Q c #48529A",
+" ! c #253071",
+" ~ c #6A6F8D",
+" ^ c #232E6F",
+" / c #3A4475",
+" ( c #434E95",
+" ) c #B6B8C7",
+" _ c #35417A",
+" ` c #212C6D",
+" ' c #202A6C",
+" ] c #232D65",
+" [ c #1F2A6B",
+" { c #374072",
+" } c #2C387B",
+" | c #1C2668",
+". c #9A9DB2",
+".. c #3B468D",
+".X c #29336E",
+".o c #384580",
+".O c #232E72",
+".+ c #465591",
+".@ c #333E85",
+".# c #212C70",
+".$ c #384276",
+".% c #202A6F",
+".& c #323F7A",
+".* c #2F3A81",
+".= c #1E286D",
+".- c #777C96",
+".; c #404F8B",
+".: c #3E4D89",
+".> c #3D4B88",
+"., c #A9ACBD",
+".< c #6671BA",
+".1 c #313D7C",
+".2 c #4D547D",
+".3 c #D9DAE2",
+".4 c #2F3B7A",
+".5 c #D6D8DF",
+".6 c #2D3978",
+".7 c #D5D6DE",
+".8 c #2C3777",
+".9 c #2B3776",
+".0 c #2B3576",
+".q c #283373",
+".w c None",
+".e c #242F6F",
+".r c #444F95",
+".t c #222D6D",
+".y c #EEEFF3",
+".u c #B4B7C4",
+".i c #404B91",
+".p c #1D2768",
+".a c #2B3779",
+".s c #3C478D",
+".d c #333B6D",
+".f c #2A3578",
+".g c #293577",
+".h c #7E839C",
+".j c #36427D",
+".k c #333D84",
+".l c #303B81",
+".z c #1F296D",
+".x c #42508C",
+".c c #1E296C",
+".v c #51629E",
+".b c #2E397F",
+".n c #51609E",
+".m c #1D276B",
+".M c #404E8A",
+".N c #2A357B",
+".B c #4C5C99",
+".V c #29337A",
+".C c #4A5A97",
+".Z c #364280",
+".A c #9094AA",
+".S c #303C7A",
+".D c #1D2664",
+".F c #5D68B0",
+".G c #293473",
+".H c #273271",
+".J c #5762AA",
+".K c #334080",
+".L c #323E7F",
+".P c #45538B",
+".I c #303C7D",
+".U c #2B3678",
+".Y c #1B2465",
+".T c #2A3677",
+".R c #3C4982",
+".E c #273274",
+".W c #384288",
+".Q c #263273",
+".! c #263073",
+".~ c #253072",
+".^ c #171F57",
+"./ c #222C6F",
+".( c #B5B8C7",
+".) c #313C81",
+"._ c #1E286B",
+".` c #2D387D",
+".' c #343E70",
+".] c #1C2669",
+".[ c #4F5F9B",
+".{ c #2C367C",
+".} c #293479",
+".| c #2C3772",
+"X c #979BB0",
+"X. c #4B5B97",
+"XX c #273277",
+"Xo c #4A5996",
+"XO c #9499AD",
+"X+ c #253075",
+"X@ c #475793",
+"X# c #222C72",
+"X$ c #445390",
+"X% c #1F2A6F",
+"X& c #42518E",
+"X* c #787E98",
+"X= c #727692",
+"X- c #2B3574",
+"X; c #555FA7",
+"X: c #232D6C",
+"X> c #2F3B7B",
+"X, c #515BA3",
+"X< c #4B527C",
+"X1 c #293575",
+"X2 c #3A4589",
+"X3 c #4A559C",
+"X4 c #263172",
+"X5 c #465198",
+"X6 c #B7B9C8",
+"X7 c #424D94",
+"X8 c #1F296B",
+"X9 c #2D397C",
+"X0 c #1D2769",
+"Xq c #2A3579",
+"Xw c #454E79",
+"Xe c #3B4883",
+"Xr c #273376",
+"Xt c #38438A",
+"Xy c #273176",
+"Xu c #364188",
+"Xi c #9498AC",
+"Xp c #3C477A",
+"Xa c #242F73",
+"Xs c #475692",
+"Xd c #343F86",
+"Xf c #36427E",
+"Xg c #323D84",
+"Xh c #212B70",
+"Xj c #44528F",
+"Xk c #43528E",
+"Xl c #1F296E",
+"Xz c #323E7A",
+"Xx c #1E296D",
+"Xc c #41508C",
+/* pixels */
+".w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.!XX C V CXyX+XaX# < v.p.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.|.1.WXuXg.* m 5.VXXX+X+.OX#.#Xh.%.z 8 8.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.>.r.i.sXtXuXg.* m 5.VXXX+X+.OX#.#Xh.%X%XlXl._ 8.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w > # # Q.r.i.sXtXdXg.* m.N CXXX+Xa.OX#.#Xh.%X%XlXx.=.= 3.] 8.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.wX;.JX, # Q (.i..XtXdXg.b m.N CXXX+Xa.OX#XhXh.%XlXlXx.= 3 3 3.m |.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.w.w.w.w.w.w.w A i.FX;X, # Q (.i...WXdXg.b 5.}Xy.! , ,X#XhXhXh.%XlXlXx.= 3 3 4.m.m.] 8.w.w.w.w.w.w.w.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.w.w.w.w.w.w.<.<.F.JX;X,X3X5X7 j..Xu.@.).` 5 C.! ! a z.#XhXh.%.%XlXlXx.= 3 3.m.m.m n n |.w.w.w.w.w.w.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.w.w.w.w.P.<.< i.F.JX; #X3X5.i jXtXu.k.l m.f.|.' {Xp $ u '.%XlX%Xl.z.= 3 3 3.m.m n n n n |.^.w.w.w.w.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.w.w.wXo.<.<.< i.JX;X, # Q (.i.sXtXuXg.* }.0 { ~.hXw q u <XlXlXlXx.= 3 3 4.m.m n n nX0.] |.^.w.w.w.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.w.w A.<.<.< i.F.JX;X,X3X5X7.i...WXdXg.b.U.0 ;.-.A., $ u 2.zXlXlXx.= 3 3 4.m.m n n nX0.].] |.^.w.w.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.wXo.<.<.< i.F.JX;X, # Q.r.i.sXtXu.k.l.b.U.0X<XO b.7 ~.d ] '.zXl.zXx 3 3 4.m.m n n nX0.].].] |.Y.^.w.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.P.<.<.< i.F.J.JX, #X3X5X7.i...WXdXg.*.`.f.0.2X .u.7 I.d ].z.cXl.z.= 3 3 3._.m.m n n nX0X0.] | |.Y.^.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.<.<.< i.F.F.JX;X,X3 Q.r.i.sXtXu.@.).b m.g.G.2. &.3X=.d ].z.cXl.zXxXx 3.c.c.c._._._._._ nX0X0 | |.Y.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.<.<.< i.F.J.JX;X, # Q.rX7.i...WXdXg.l.` 5Xr.G.2. &.3X=.d ].z.z < < < < < < < < < < ' '.zX8._._._X0.p.Y.w.w.w.w.w.w",
+".w.w.w.w.w A.< i i.F.J.JX;X, # QX5X7.i.sXtXu.k.).b m.N l.qX<. &.3X=.d ] x <.#.#.#.#.#.#././.# ` ` ` ` x ' 'X8X8 v = 8.w.w.w.w.w",
+".w.w.w.w.w i.F.F.J.JX;X,X, # QX5X7.i.sX2XuXdXg.*.` 5.}.E.HX<. X6.3 I.d ] `./ , , , , , , , , , , z z./.t./ ` ` ' ' [ =.w.w.w.w.w",
+".w.w.w.wX;.F.J.JX;X;X, #X3 QX5X7.i.sX2.WXdXg.l.b.{.N V.!.HX<X ).3X=.d q , ,XaXaXaXa.!.!.!.~.~.~ , , ,.e , ^ ^.t ` ' ' =.w.w.w.w",
+".w.w.w >.JX;X;X;X,X, #X3 Q.rX7.i.s...WXd.k.).b m.N.} C.~.HX<X X6.3X=.d q ,.~.!.!.!.!.!.!.E.E.E.Q.!.!X4.~ ! !.e.e ^.t ` `.D.w.w.w",
+".w.w.w #X,X,X, # #X3 QX5.rX7.i.sX2.WXd.k.).b.` 5 B CXX.!.HX<X X6.3X=.'.X.!.!XrXrXrXrXrXrXr - - l l.q.E.E WX4X4 ! a.e ^X: Y.w.w.w",
+".w.w.w # # #X3X3 QX5.rX7.i.i.sX2.WXd.k.).*.`.{ B VXXXX.!.qX<X ) TX=.'.X.E l.g.g.f.f.f.f.f.f.g.g k k -X1.q.q.q WX4 ! !.e ^.w.w.w",
+".w.w.> Q Q QX5X5 (X7.i.i.s..XtXuXd.k.).*.`.{ B V CXXXX.Q.GX<X X6.3X= { $X1.gXq.U.a.a.a.a.a.a.a.U.U.U.U.T.0X1X1.q.q W.H ! a ].w.w",
+".w.w.r.r ( (X7.i.i.i.s..Xt.WXuXdXg.).b.`.{.N V C C C C.E.GX<X ).3X= { $.T.U } } }X9X9X9X9X9 h h h.6 E E.U.9.U.0X1.G.q WX4 a.w.w",
+".w.|.i.i.i.i j j.s..Xt.WXuXd.kXg.l.b.`.{ B V C C C C.} l.G.2X X6.3X= { $ E }X9X9X9X9 g g g g g gX>X> O h.6.6 E.8.0X1X1.G.q.H ].w",
+".w.1.s.s......XtXt.WXuXd.@Xg.).*.b m 5 B V C C C V.}XqX1X-.2X ).3X= / L hX9 g.I.I.I.I f f f.I.I.I.I g gX> O O.6 E.8.0X1.G.q ].w",
+".w.WXtXtXt.WXuXuXuXd.kXg.).l.b.`.{.N B V C C C V.}Xq B -.9.2X ).3.- / LX> g f.L.L.L.L.L d.L.L.L.L U U U f.IX>.4 O.6 @.9.0X-.H.w",
+".wXuXuXdXdXd.@.kXgXg.l.*.b.` m 5.N.} CXXXX C C.}Xq B B.0 @.2X ).3.- / L g f.L d * * * * * s * *.K.K d d.L U.1.IX>.4 O.6 @.0X-.w",
+".!XgXgXgXgXg.).l.*.b.b.` m 5.N.} V CXXXXXX C.}Xq B B M.0 p.2X ).3.- /.' U.L * s s % % % % % % % % s s s.K d d U.1 o.4 O.6 @.9.G",
+"XX.*.*.*.b.b.` m }.U.U.f.gXr l.E.!.~.!.!.Q.E lX1 -.0.0.9.| r. & XX*Xw {Xz y t.K . . . . . . . . t t t J J yXzXz U.1.S.S.4 p @X-",
+" C m m m m 5 5.f.0.0.0.0.G.G.q.H.H.H.H.q.G.G.GX-.9 @ p.| L e., & X.h +.$Xz _.jXf > > > > > > > > > >.j.j.j _ _.&Xz y.1 o.S.4 O.9",
+" V 5 5.N.N.} C.| { ;X<.2.2.2X<X<X<X<X<X<X<X<.2.2.2.2.2 r e.h K.7.y.,.- ~.2 r e e e e e e e r r r r.2 + /.'Xz y UXz.S.4 @",
+" C.V.V C CXy.!.' .-XOX . . . . X X X X X X X X X X X . ., K.y.y.y R &.,. X X ZX XO ZXOXOXOXOXiXiXiXiXiXi.h.h.2 {Xz J y y U.S O",
+"XyXXXXXXXX.! ! { ~.A b.u & & &X6 )X6X6 )X6 )X6 ) ) ) ) & &.7.y.y.y X.7 KX6.(.u.u.(.u.u.u.u b b b b b b b b.,X /.$ t t t y.1.4",
+"X+X+X+X+X+ , aXp.h.,.7.7.3.3.3.3.3.3.3 T.3.3.3.3.3.3.3 X X.y.y.y.y.y.y X T T.5.5.5.5 T T T.5.5.5.5.7.7.7.7 K.u ~ /.$XfXf . t y.S",
+"XaX+X+XaXa , z $Xw ~ IX=X=X= IX=X=X=X=X=X=X=X=.-.-.-X*.h., R X.y K. .AX*X*X*X*X*X*X*X*X*X*X*X*X*X*.-.-.- I ~ +.$ _.o.Z.Z . J.1",
+"X#.O.O.O.OX#.# u q $.d.d.d.d.d.d.d.d.'.' { { { / / / /Xw +.- &.7.y. ~ 6 6Xw 6 6 6 6 6 6 6 6 6 6 6 6XwXwXwXwXp >.o 9.o.o.Z t y",
+" <X#X#X#X#XhXh ' u u ] ] ] ] ] ] q q.X.X $ $ $ L L L.' {.$ ~., K X.A XwXpXp ; ; P P P P P P P P P P P ; ; ;Xp.o 9 9 9 9.o.Z . J",
+" v.#.#.#XhXhXh.% < 2 '.z.z.z x ` , ,.!.EX1.T E hX> g UXzXz.2. X6 TX* 6XpXe.>.:.;.;Xc.xX&XkXkXkXk 1 1.xXc.;.:.:.>.>.>.>Xe 9.o.Z J",
+".pXhXhXhXhXh.%XlXl.z.z.c.c.z <./ ,.~.! l.g.U }X9 g f.L y _ rX .( TX* 6Xp.>.MXcX& H HX$ G G G G G G GX$ HX&.x.;.M.:.:.>Xe 9 9.o t",
+".w.%.%.%.%.%.%X%XlXlXlXlXl <.# ,Xa.!Xr.gXq }X9 g f.L * t.j eX .u.5X*Xw ;.:Xc H G w w w 0 0 0 0 0X@ F F.+ GX$Xk.x.;.:.:.>Xe 9.o.w",
+".w.zX%X%XlXlXlXlXlXl.z.z.z <.# ,Xa.!Xr.g.U }X9.I.L d s.KXf e Z.u.5X* 6 ;.;X& G w w 0 0 S S.C.CXo S S 0 F.+ GX$Xk.x.;.:.>.> 9.o.w",
+".w 8XlXlXlXlXl.zXxXxXx.=Xx <.# ,Xa.!Xr.f.a }X9.I.L * s . > eX .(.5X* 6 P.; H w w 0 0 S.C.C A A A A.CXo D F.+ GXj 1Xc.:.:.>XeXz.w",
+".w 8XlXxXxXxXx.=.=.= 3 3Xx <.# ,Xa.!Xr.f.aX9X9.I.L * % . > XO.u.5X* 6 PXc H w 0 0.C.CX..B.B.B.B.B.B AXo D F.+X$Xk.x.;.:.>Xe _.w",
+".w.w._.=.=.=.= 3 3 3 3 3 3 <.# ,.!.!Xr.f.aX9 g.I.L * % . > Z.u TX* 6 P.xX$ w 0 S.C.B.B 7 7 N N N 7.B AXo DXs GXj 1.;.:.>Xe.w.w",
+".w.w 8.= 3 3 3 3 3 3 4 3.c <.# ,.!.!Xr.f.aX9 g f.L * % . > XO.u TX* 6 PX& G 0 S.CX..B 7 N.[.[.[.[ N 7.B A SX@.+X$Xk.;.:.: _.w.w",
+".w.w.w 3 3 3 3 3 4 4.m._.c <./ ,.!.EXr.f.aX9 g f d * % . > XO.u TX* 6 PXk G 0 S.C.B 7 N.[.n.n.n.n.[ N 7X.XoX@Xs GXk.x.M.:.w.w.w",
+".w.w.w.] 3 4.m.m.m.m.m.m.c <./ ,.~.E -.f.aX9 g f.L s % . > eXO b.5X* 6 PXk G 0.C A.B 7.[.n.v c c.v.n.[ N.B.C DXs GXk.x.M.>.w.w.w",
+".w.w.w 8.m.m.m.m.m.m n.m._ <.# ,.~.E -.g.a h g.I.L * % . > eXO b.5X* 6 PXk G 0.C A.B N.[.n c c c c.v.[ N.B A DXs GXj.x.M.o.w.w.w",
+".w.w.w.w |.m.m n n n n n._ < ` ,.~.Q l.g.U h g.I.L * % . > eXi b.5X* 6 PXk G 0Xo A.B N.[.n c c c c.v.[ N.B A DXs GXk.x.>.w.w.w.w",
+".w.w.w.w.w.] n n n n n n._ ' ` z ,.! l k.U hX>.I.L.K % t > eXi b.5X* 6 P 1 GX@ S A.B N.[.n.v c c c.n.[ NX.Xo DXsX$Xk.M.w.w.w.w.w",
+".w.w.w.w.w 8 n n n nX0 n._ ' ` z ,.!.q k.U.6X>.I U.K s t > rXi b.7X* 6 P 1 G F S.C.B 7 N.[.n.v.v.n.[.[.BX.XoX@.+X$Xk.R.w.w.w.w.w",
+".w.w.w.w.w.w | nX0X0.]X0._.z `./ ,X4.E -.U E O g U d s t.j rXi b.7.- 6 P.xX$ F 0Xo A.B 7 N.[.[.[.[.[ 7X. A DXs.+Xj.M.w.w.w.w.w.w",
+".w.w.w.w.w.w.w |.].].]X0 nX8 x.t.e.~.EX1.T E h g U d s J.j rXi b.7.-Xw ;Xc H.+ F DXo A.B 7 N N N N.BX. A SX@ :X$.x.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.^ |.].].]X0._ './ , ! W.q.0.U.6X> f.L.K J.j rXi b.7.-Xw ;.;X& G.+ F DXo AX..B.B.BX.X. A SX@Xs G 1Xp.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.w.^ | | |X0._ ' ` ^ !X4.qX1.9.6 O.I U d y _.2.h., K IXw ;.:.xX$ G.+ F D SXo.C A AXoXo DX@Xs G 1Xp.w.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.w.w.^.Y | |._X8 ` ^.eX4.qX1.U E OX>.1 dXz _ +.hX .u ~XwXp.:.;XkX$ G.+XsX@X@ D D D DX@Xs : G 1 /.w.w.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.w.w.w.^.Y |X0X8 '.t.e ! W.q.0.8.6.4.I UXz.& /.2 ~ +Xp.o.>.M.xXkXjX$ G.+XsXsXsXsXs.+.+X$ 1Xp.w.w.w.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.w.w.w.w.^.Y.p v ' ` ^ aX4.qX1.0 E OX>.1 UXz.' { / /.$ > 9.>.:.;.x 1XkXjX$ G G G GX$X$Xj.xXp.w.w.w.w.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.w.w.w.w.w.w.Y = [ '.t.e ! W.GX1.8.6.4 o.1 yXzXz.$.$ _.o 9.>.:.:.;Xc.x 1XkXkXkXjXkXkXk.M.w.w.w.w.w.w.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.w.w.w.w.w.w.w 8 = ' ` ^ !.H.qX1.0 @ O.4.S.1 y J tXf.o 9 9.>.>.:.:.:.;.;.;.x.x.x.x.M.R.w.w.w.w.w.w.w.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w = `X:.e ! W.GX1.9.6 O.S o U y tXf.Z.o 9XeXe.>.>.:.:.:.:.M.M.M.>.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.D Y ^ aX4.q.G.0 @.6.4.SXz y t ..Z.o.o 9 9Xe.>.>.>.>.:.:.>.o.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w ] a.H.qX-.0 @ p.4.S U y t ..Z.Z.o 9 9 9XeXeXe _.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w ] ].HX-.9 @ O.4.S.1 y J t ..Z.o.o.oXz _.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w",
+".w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.GX-.9 @ O.4.S.1 y J J t.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w"
+};
diff --git a/arts/builder/pics/Synth_AMAN_PLAY.xpm b/arts/builder/pics/Synth_AMAN_PLAY.xpm
new file mode 100644
index 00000000..3ad46f6e
--- /dev/null
+++ b/arts/builder/pics/Synth_AMAN_PLAY.xpm
@@ -0,0 +1,316 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"64 64 245 2",
+/* colors */
+" c #B1B1B1",
+" . c #BFBFC2",
+" X c #ADADAD",
+" o c #A9AAB3",
+" O c #ABABAB",
+" + c #323E7D",
+" @ c #5F6792",
+" # c #71789D",
+" $ c #2F3A7A",
+" % c #3E4C8C",
+" & c #A5A5A5",
+" * c #3A4488",
+" = c #273272",
+" - c #8D93B5",
+" ; c #78787C",
+" : c #253070",
+" > c #616B97",
+" , c #344082",
+" < c #232E6E",
+" 1 c #313C7F",
+" 2 c #B0B5CA",
+" 3 c #1F286A",
+" 4 c #959595",
+" 5 c #1C2667",
+" 6 c #5F6BAC",
+" 7 c #696A6D",
+" 8 c #374288",
+" 9 c #8D8D8D",
+" 0 c #36417D",
+" q c #6F79AB",
+" w c #43518D",
+" e c #1F2A6D",
+" r c #1E286C",
+" t c #414F8B",
+" y c #2D3774",
+" u c #1A235E",
+" i c #9599AF",
+" p c #D1D2D6",
+" a c #64666B",
+" s c #475394",
+" d c #777777",
+" f c #767DA1",
+" g c #536299",
+" h c #2F3B79",
+" j c #8E8F94",
+" k c #2A3574",
+" l c #5D68A6",
+" z c #4B5891",
+" x c #50598C",
+" c c #273171",
+" v c #575D7F",
+" b c #222D6C",
+" n c #212B6B",
+" m c #303B7D",
+" M c #404B90",
+" N c #1D2767",
+" B c #3C478C",
+" V c #4C5A95",
+" C c #38447E",
+" Z c #38427E",
+" A c #343E7A",
+" S c #858CAF",
+" D c #42508B",
+" F c #2E397E",
+" G c #1D276A",
+" H c #404E89",
+" J c #1C2769",
+" K c #303A76",
+" L c #2C377C",
+" P c #3E4C87",
+" I c #C7C7C8",
+" U c #AFB0B7",
+" Y c #E4E5EB",
+" T c #29326F",
+" R c #384481",
+" E c #DCDDE3",
+" W c #303C79",
+" Q c #3E4C8A",
+" ! c #3D4A89",
+" ~ c #414B83",
+" ^ c #3A4886",
+" / c #686971",
+" ( c #7B84A8",
+" ) c #515C8C",
+" _ c #656F99",
+" ` c #7880A5",
+" ' c #4A578F",
+" ] c #424C91",
+" [ c #202A69",
+" { c #3E488D",
+" } c #2C3878",
+" | c #1B2464",
+". c #797A7E",
+".. c #3A477F",
+".X c #9D9D9E",
+".o c #39457E",
+".O c #47558F",
+".+ c #D0D0D0",
+".@ c #212C6D",
+".# c #1E286A",
+".$ c #CACACA",
+".% c #283277",
+".& c #3B4783",
+".* c #253074",
+".= c #485793",
+".- c #BEBEBE",
+".; c #222C71",
+".: c #44538F",
+".> c #BCBCBC",
+"., c #6974B0",
+".< c #BABABA",
+".1 c #1E286D",
+".2 c #3E4D89",
+".3 c #848AA6",
+".4 c #5F6585",
+".5 c #4F599D",
+".6 c #B4B4B4",
+".7 c #B2B2B2",
+".8 c #394784",
+".9 c #ABADB5",
+".0 c #34417F",
+".q c #ACACAC",
+".w c #87878B",
+".e c #58649F",
+".r c #AAAAAA",
+".t c #414D8F",
+".y c #2D3978",
+".u c #6F6F70",
+".i c #57608A",
+".p c #A0A0A0",
+".a c #666871",
+".s c #283373",
+".d c None",
+".f c #CFD0D8",
+".g c #364184",
+".h c #465197",
+".j c #242F6F",
+".k c #BBBCC1",
+".l c #9A9A9A",
+".z c #313D7F",
+".x c #989898",
+".c c #A8A8AB",
+".v c #4D5480",
+".b c #52609C",
+".n c #A3A4A6",
+".m c #4E5E98",
+".M c #2B3579",
+".N c #6B739A",
+".B c #293577",
+".V c #293377",
+".C c #273175",
+".Z c #495893",
+".A c #394680",
+".S c #475691",
+".D c #717482",
+".F c #E0E1E5",
+".G c #42508C",
+".H c #DADBDF",
+".J c #1D276B",
+".K c #979DBB",
+".L c #8088AB",
+".P c #2C3673",
+".I c #5C6281",
+".U c #242F75",
+".Y c #3C457C",
+".T c #33407D",
+".R c #747474",
+".E c #41508E",
+".W c #B8B9BA",
+".Q c #646FAD",
+".! c #2E3A78",
+".~ c #808083",
+".^ c #2C3876",
+"./ c #2B3675",
+".( c #293473",
+".) c #384485",
+"._ c #242E6E",
+".` c #45518B",
+".' c #424F88",
+".] c #465082",
+".[ c #414D87",
+".{ c #2D387A",
+".} c #9FA2AB",
+".| c #2B3678",
+"X c #384288",
+"X. c #171F57",
+"XX c #343E84",
+"Xo c #DEDEE2",
+"XO c #353F7B",
+"X+ c #2F3A7F",
+"X@ c #1C2669",
+"X# c #2F3975",
+"X$ c #3E4B87",
+"X% c #D7D8DB",
+"X& c #C4C4C5",
+"X* c #4A5996",
+"X= c #36437F",
+"X- c #48548A",
+"X; c #202A70",
+"X: c #F1F1F1",
+"X> c #1F2A6F",
+"X, c #515BA0",
+"X< c #B4B4B5",
+"X1 c #3B4987",
+"X2 c #4C579B",
+"X3 c #7D7D7F",
+"X4 c #B2B2B3",
+"X5 c #3E4880",
+"X6 c #384584",
+"X7 c #495598",
+"X8 c #364382",
+"X9 c #5661A8",
+"X0 c #434F92",
+"Xq c #374079",
+"Xw c #53597A",
+"Xe c #A5A7B0",
+"Xr c #3E498D",
+"Xt c #5966A4",
+"Xy c #1E296A",
+"Xu c #1D2769",
+"Xi c #C9C9C9",
+"Xp c #929293",
+"Xa c #3B458D",
+"Xs c #3D4A85",
+"Xd c #4C5C97",
+"Xf c #E6E7EC",
+"Xg c #D4D5D7",
+"Xh c #C3C3C3",
+"Xj c #C1C1C1",
+"Xk c #232D72",
+"Xl c #465491",
+"Xz c #455490",
+"Xx c #212B70",
+"Xc c #34407C",
+"Xv c #43528E",
+"Xb c #CCCDCF",
+"Xn c #1F296E",
+"Xm c #323E7A",
+"XM c #1E296D",
+"XN c #525EA0",
+"XB c #313C79",
+"XV c #B7B7B7",
+"XC c #676A79",
+"XZ c #2A3472",
+"XA c #7C7C7D",
+/* pixels */
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d c.%.V.V.%.C.U.U.;XnXy N.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.P +X 8XXX+ L L.V.%.U.UXk.;.;XxX;Xn | u.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.dX1X0 M BX 8XXX+ L L.V.%.U.UXk.;.;XxX>X>XnXn r u.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d ZX,.5X7.h MXaX XXXXX+ L L.V.%.U.UXk.;.;XxX>X>XnXM.1.1 rX@ u.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.dX9X9X,.5X7X0 MXaX XXXXX+ L.M.V.%.U.UXk.;XxXxX>XnXnXM.1 r r r.J 5.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.dXd 6 6X9X,X2.hX0 MXa 8XXXXX+ L.M.V.C.*.UXk.;XxXxX>XnXnXM.1 r r.J.J.JX@ u.d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.,., 6X9X9X,X2.h ] {Xa 8XX 1 F L.M.V.s = c :.;XxX;X>XnXnXM.1 r r.J.J.J G G 5.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.`.,.,.Q 6X9XN.5X7.h ] {Xa 8XX 1 F L m 0XOXOXB K.s.;XnX>XnXn.1 r r r.J.J G G G G 5X..d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d z.,.,., 6X9X9X,X2.hX0 MXaX 8XXX+ m Z x # f `.N @ ~ c.@XnXnXM.1 r 3 [ G G G G GXuX@ 5X..d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d V.,.,.,.Q 6X9X9X,X2.hX0 MXa 8XX 1X+ Z.' (.fX% E .Xe @ y.@XnXnXM r e._ T b rXu G GXuX@X@ 5X..d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d z.,.,.,.Q 6X9X9X,X2X7.h M {Xa 8XX , R.i ( U E.f I o j.4 y.@XnXnXM 3._Xq ).Y T [XuXu JX@X@ 5 5X..d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.`.,.,.,.Q 6X9X9XN.5X2.hX0 MXaX XXXX.&X-.L.f EXf.$ X j. Xw.P nXnXn.1 [ T ) - @Xq T.#XuXuXuX@ 5 5 5X..d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.,.,.,.Q 6 6X9XNX,X2.hX0 M {Xa 8.gX$ @ S 2 EXo.FXh &.w.RXwX# T.@ e r.@._.Y @ @ @X5 T n.# GXuXu 5 5 5.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.,.,.,.Q 6X9X9X9X,X2X7.h ] MXaX .gX$ ' S.f E YXo.H.>.X.~ / v KXBXm.(Xx.@.@.PXq @ i @XqXZ e 3 r.#.#XuXu |.d.d.d.d.d.d",
+".d.d.d.d.dXd.,.Q 6 6X9X9XNX,X2X7.hX0 MXaX *.[ > S 2X%Xb ..$XgXV.l.~ / vXqX- _X-Xm.( < c y.] @ > _.] c.@ e e 3 3Xy.# u.d.d.d.d.d",
+".d.d.d.d.d 6 6 6X9X9X9XNX,X2X7.hX0 M B *X H z S.fX%.H.W.xX4Xb.6.l.~ / v.Y >.K fX- A : : :X#.Y.N.9 @ K._.@.@.@ n nXyXu.d.d.d.d.d",
+".d.d.d.dX9 6X9X9X9XNX,.5X2X7 sX0.tXrX1 !.G > S 2.H IX4.p 9.qXbX4.l.~ /Xw A.v _ # ( x.y = :XZXq _.9 _XqXZ < < < <.@ n nXu.d.d.d.d",
+".d.d.d ZX9X9X9XNX,X9 lXtXt.e.b.b V V z z ) -.f.H.H.6 9.wX3 &.$.7.l.~ /.v yXq ~ # 2.N 0.(.C.sXB @.9.NX5 K c :.j.j < <.@.@ |.d.d.d",
+".d.d.dX,X,X,X,.5XN q.K.K.K.K.K.K.K - - -.K 2 EXbX<.xX3X3XA.n.$ .x.~ /Xw K A.o.N 2 f.[XB.s./.y.].N.N.N.]X# c c : :.j < b [.d.d.d",
+".d.d.d.5.5X2X2X7XN -X% EXfXfXfXfXfXf YXfXfXf Y.W 9.~.R dXA.pXi .x.~ /XwXm AXO _ 2.L ).&.|.|.BXO ~.N.9 _XqXZ.s = c : :.j <.d.d.d",
+".d.dX1X7X7.h.h s.b - E YXf Y.FXoXo E.H.H EXoXoXVXp.wXA.~.w &Xi .x.~ /Xw AXm $X- # `.L ) +.y.|XBX5 @ i @ Z.P k.(.s = c : : N.d.d",
+".d.dX0.hX0X0 ] ].m.KXfXf YXoX%XgXg p.+.+.+Xg.HXV 4 9.w.w 9.q.$ .x.~ /XwXOXm.{ Z.] f 2.N.& $.{ K.o.i (.i.o.^.|.B k.(.s = c.j.d.d",
+".d.P M M M M {Xr V -Xf.FXoXg.+Xb.$Xi I IXiXgXo p IXj.-XjXj.$.+.6.x.~ /Xw 0 W.{XO ~ _.K _X5XB m W.o @ i > ~Xm } }././ k.(.s c [.d",
+".d + BXaXaXaXa *Xl -XfXoXgXbX&XhXj.-.-.-Xj p YX:X:X:X:X:X:XfX%XV.x.~ /Xw Z h m WX5 @.L @ ~Xm mXB.. _ 2 #.].o h.y } }./ k.(.s [.d",
+".dX X X X 8 8.).` S YXoXg.$XjXj.-.-.>.-.-.+.F YXfXfXfXfXfXoXgXV 4X3 /Xw.oXB.z +X5 >.K.N.' Z +.TX= x # # # xXc h.!.y }.^./ k =.d",
+".d 8 8XXXXXXXX ,.[ S Y E.+XiXj.-.-.<.<.<.-XbXo.H.H.HX%X%X%Xg p.6 4X3 /Xw.o +.z.TXs.N 2 fX- ~ , ,.0X5X- # 2.NX5Xm $ $.!.y.^./ k.d",
+" cXXXXXXXXXX 1 +X$ S Y.H.+ I.-.>.<.WXV.W.<XiXgX& X.c O X.-Xb 4X3 / v.&.T ,.0.8 ) f f f ).&X8X8...' _ i _ ~Xm + W $.!.y.^./XZ",
+".%X+X+X+X+X+ F mXs.L Y.HXbX&.<.<.WXVXVXV.<X&.+.r.wX3 dXA.~ &X& X 4X3 / v.&.0X8X8.).[ x f 2 #.' RX8X=.[ @.3 @.[ 0 + +Xm W h.y.y k",
+".V L L L L L L.{.&.L YX%.$Xj.WXVXVX<.6XV.WXhXb &X3 d.u.RXA.pXh.q 4X3 / vXs.0X8.).)XsX-.N.K.N.` R.).A.' @.L @.' 0.0.T + + W h.!.^",
+".V L L L.M.M.M.| R (.FX% I.-XVXVX<.6X4X<XVXj.$.p d.u a.u.R.lXj O 4X3 / vXsX8.).).).&.` > S >X-.8 ^.&.' @.3 >.`X=.0.0.T +Xm W h.^",
+".%.V.V.V.V.V.%.B Z (.FXg I.-X<.6X4.7.7.6XVXj.$.p d.u a.u.R.lXj.rXpX3.a v.[ R ^ ^X1.&.` > S _.OXsX1.&.` @.L >.`.AX8X8.0.T + + W.!",
+".C.%.%.%.%.C.C.s 0 (.FXgX&.>.6X4.7 X4X<.-Xi.p d.u a.u.R.lXj.rXpX3.a v.[.8X1X1 !XsX- > S _ 'X$ !XsX- >.3 >X-.AX6X8X8.0.T +Xm h",
+".U.U.U.U.U.*.U = A `.F pXj.<.7 .7.6.- I.X.R.u a 7.R.lXj.rXpX3.aXw.[.8 ! ! QX$.O > S _ z.[ Q PX- >.3 >X-.&.8X6X8X=.0.0 + W",
+".U.U.U.U.U.U.U :Xm ` E pXj.W X4.- I.X.R.u a 7.R.xXj.rXpX3 /Xw P.& Q % %.2 ' _ S _ z H.t.[ ' > S _X-Xs ^.8X6X8X=.0XcXm",
+".;XkXkXkXkXkXk :XB f E pXjXV X X .7.- I.nX3 d.u dXA.pXj.rXp. .a v.'X1 % %.t.` ) #.K # z D.E H ' _ S _ 'XsX1 ^.8X6X8X=.0 +",
+"Xn.;.;.;.;.;.;.j K f E p.-XV X X X X .7.-Xi.c.w.~XA.~.w &Xj.rXp. .a.I.` P %.t.E z g ( 2 f z wXv D z _ S _ z PX$X1 ^.8 RX8.0Xc",
+"Xy.;.;.;XxXxXx bX# # E.+.-XV X X X.q.q X Xj.+ I.>.WXV.W.<XjXi XXp. .a.IX-.2.E.E.O > ( ( ( >.ZXl.:.O ) #.K.NX- P QX$X1 ^.8 RX=Xc",
+" NXxXxXxXxXxX; nXZ.N EXb.-X< X.q.q O O X X&X%.FX:X:X:X:X:Xo.+ Xp. .a.I.O H.E w V ` 2 ( g VXlXlXl V g ( 2.NX-.[ Q Q !X1.8.A RXc",
+".dX;X>X>X>X>X>.@ y # EXb.>.6.q.q O O.r X XjXg.H.F.F.FXoXoXgXb Xp. .a.I.O DX0.:.m f.K f g z.=.Z V > ( ( ( @.: t t Q PXs ^.8 R.d",
+".dXnX>X>XnXnXn.@ K ` EXb.<X4.q O O.r.r.q Xj.+.+.+.+.+.+.+Xb.$ XXp. .a.I '.GXl.O g # -.N g.ZX*.Z g ` 2 ( g z.: w.G t.2X$X1.8.A.d",
+".d |XnXnXnXnXn b K f E.$XV X & & & & &.c O.<.$.W.c &.p.n &X<X&.rXp. .a.4 '.:Xl z g `.K f g zX* V g f.K f g 'Xz.: w t Q PXs ^Xm.d",
+".d uXnXMXMXMXM nX# f.H I .r.p.p.p.p.p.n &X<Xh.pX3 d.u dXA.X.- &Xp. .a.4 '.SXl.m.e.L 2 ( g VXd V.e # -.N.m.OXlXzXv.G t QX$ ^Xq.d",
+".d.d r.1.1.1.1 [ y.N.kXe j.w.w.~.~.~.~.w 9.n.W.lXA.R 7.u d.x.- & 9. .a.4 ' z V _.L -.K q.b.m.m.m.e `.K # g.O.SXl.: w t.2X$.&.d.d",
+".d.d u.1 r r r [.P.4.} j.D.u / / / / /.u d j 4 ;.u a 7.u 4.> & 9. .a.4 z V.m ( 2.K q.e.m.m.m g >.L 2 ` g z.=Xl.: w t.2 PXq.d.d",
+".d.d.d r r r rXy._.Y.i vXwXwXwXwXwXwXwXw v.D j j 9X3.u.u.u 4.< & 9 ;.a.I z.m g.L 2 - l g.m.b.e.N.L.L.L _.m.Z.=.SXzXv.G H P.d.d.d",
+".d.d.dX@ r.J.J G r._ T TXZ.P yX# K KXmXq.Y v.D.w & 9 d.R.u 4.< & 9 ;.a.I '.m @.L 2.L.e.m.b.bXt.L 2.L > gXdX*.Z.SXzXv.G HXs.d.d.d",
+".d.d.d u.J.J.J.J G G 3 3 [ n b._ : =XZX#XO.] v.D.w 9 9.~ ;.l.< & 9 ; a.4 ) > ( S.K q.b.m.b g l.L 2.L.e.mXdX*.Z.SXzXv.G H...d.d.d",
+".d.d.d.d 5.J.J G G G G G r e.@.j : =.s.B } 0X5 v.D.w.nXp.~.p.> & 9 ; a.4 g ` 2 - q.e.m.m.b.e _ S 2 (.eXdXdX*.Z.SXzXv.G P.d.d.d.d",
+".d.d.d.d.dX@ G G G G G G r e.@.; :.*.s.B.| W 0.] v.D.w j.x &XV.p 9 ; a.4 ) _.L q.e.b.m.b.e.N (.L.L _ gXdXdX*.Z.SXz w H.d.d.d.d.d",
+".d.d.d.d.d u G G G GXu G r e.@ < :.*.s.B.|.y $ Z ~.I.D j X .X 9 ; a.4 z g.e.mXdXd.m g > (.K ( >.e.mXdXdX*.=Xl.: w...d.d.d.d.d",
+".d.d.d.d.d.d 5 GXuXu JXu.# e.@ < : c.C.B.| }.!Xm.o.v.I.D j.X .X.w d a.4 ' ' z.=X* V.m > ` ` ( _ g.mXdXdX*.Z.S.O.: H.d.d.d.d.d.d",
+".d.d.d.d.d.d.d 5X@X@X@Xu G 3.@ <.j :.C k.B }.{ $ +...].I.D j X.l.w d a.I ' wXl.=.Z z g f.K ` > g.mXdXdX*.Z.=.SXz D.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.dX. 5X@X@X@Xu r e.@.j : =.(.B.|.y $ mXc...v.I.D.w.~ d / a.IX- DXlXl.=.=Xd > # > gXdXdXdX*.Z.=.SXz w.Y.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.dX. 5 5 5Xu.# e.@ < : c.s k.|.{ $ m +.0X5.] vXC.a a a a vX- D.:XlXl.S.= V.mXd VX*X*X*.Z.=.SXl w.Y.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.dX. 5 5 5.# 3.@ <.j c.s k.| }.! $ + + 0X5.v v v v v v x.` D w.:XlXl.S.S.S.Z.=.Z.Z.=.S.SXz w.Y.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.dX. 5 5Xu 3 n <.j : =.(.B }.y $ m +.T.0.A ~ ~ ~.[.'.' Q t.G w.:XzXlXl.S.S.S.S.SXl.OXz w.Y.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.dX. 5XuXy n.@ < : c.s k./ }.! $ + +.0.0 0 C.A.&.&XsX$ Q t.G wXv.:.:XzXzXzXzXz.:.: D.Y.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d |.#Xy n <.j : =.(./ }.y $ W +.T.0X8X8X6.8 ^X1X$ Q Q t t.G w wXvXvXvXv w w H.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d uXu n.@ < : c.s k./ }.! $Xm +.T.0X8X8X6.8 ^X1 ! P.2 Q t t t.G.G.G.G H...d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.dXu.@ b.j : =.( k.^.y.! W + +.T.0X=X8X6.8 ^X1XsX$ P Q.2.2 H H H P.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d | [ < : c.s.(./.^.y h WXm +.T.0X=X8 R.8.8 ^X1XsX$X$ P PXs...d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d N.j c.s k./.^.y h W + +.0.0X=X8 R.A.8.8 ^ ^.&Xq.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d [ [ = k./.y.! h WXm +Xc.0.0X= R R.AXmXq.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.dXZ k.^.^.! h WXm +XcXcXc.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d"
+};
diff --git a/arts/builder/pics/Synth_ATAN_SATURATE.xpm b/arts/builder/pics/Synth_ATAN_SATURATE.xpm
new file mode 100644
index 00000000..153b5bfd
--- /dev/null
+++ b/arts/builder/pics/Synth_ATAN_SATURATE.xpm
@@ -0,0 +1,310 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"64 64 239 2",
+/* colors */
+" c #485896",
+" . c #364281",
+" X c #33407E",
+" o c #327F37",
+" O c #313E7C",
+" + c #31694A",
+" @ c #2E3A79",
+" # c #8088AF",
+" $ c #4F5AA0",
+" % c #1D2865",
+" & c #273272",
+" * c #253070",
+" = c #455296",
+" - c #838CB5",
+" ; c #2E3A7C",
+" : c #1E2869",
+" > c #2A3678",
+" , c #283276",
+" < c #273275",
+" 1 c #3A4781",
+" 2 c #475591",
+" 3 c #323E83",
+" 4 c #45538F",
+" 5 c #747AA6",
+" 6 c #212C6F",
+" 7 c #5363A0",
+" 8 c #42518C",
+" 9 c #1E286C",
+" 0 c #2D387E",
+" q c #2B367C",
+" w c #29347A",
+" e c #1A235E",
+" r c #384582",
+" t c #475794",
+" y c #232E74",
+" u c #465593",
+" i c #357943",
+" p c #35417F",
+" a c #233F56",
+" s c #656FB8",
+" d c #20345D",
+" f c #4D599D",
+" g c #2B3775",
+" h c #394786",
+" j c #768C9A",
+" k c #848DB5",
+" l c #3A478A",
+" z c #232D70",
+" x c #222D6F",
+" c c #313B81",
+" v c #1E296B",
+" b c #316D45",
+" n c #2E397E",
+" m c #405089",
+" M c #414E8A",
+" N c #2D397D",
+" B c #1D276A",
+" V c #2D3C73",
+" C c #2A357A",
+" Z c #4C5C98",
+" A c #293379",
+" S c #283378",
+" D c #4B5A97",
+" F c #4A5A96",
+" G c #2A3F66",
+" H c #263176",
+" J c #485894",
+" K c #242F74",
+" L c #475693",
+" P c #232D73",
+" I c #455491",
+" U c #33427C",
+" Y c #202B70",
+" T c #2D3876",
+" R c #3C4A88",
+" E c #3B4887",
+" W c #2B3674",
+" Q c #374483",
+" ! c #222C6B",
+" ~ c #2F3C7B",
+" ^ c #3D4C8C",
+" / c #1C2665",
+" ( c #4B569D",
+" ) c #28396A",
+" _ c #48529A",
+" ` c #747CA4",
+" ' c #434E95",
+" ] c #212C6D",
+" [ c #1C2668",
+" { c #4E5D99",
+" } c #293478",
+" | c #2B5452",
+". c #273276",
+".. c #374289",
+".X c #253074",
+".o c #485793",
+".O c #222C71",
+".+ c #212C70",
+".@ c #44538F",
+".# c #737AA6",
+".$ c #202A6F",
+".% c #43518E",
+".& c #1F2A6E",
+".* c #2F3A81",
+".= c #1D286C",
+".- c #3D4B88",
+".; c #37843C",
+".: c #435191",
+".> c #333F7E",
+"., c #414F8F",
+".< c #303D7B",
+".1 c #515DA2",
+".2 c #1E2B66",
+".3 c #2D3978",
+".4 c #2D5B50",
+".5 c #2C3777",
+".6 c #3B4989",
+".7 c #1B2563",
+".8 c #293574",
+".9 c #3A8F38",
+".0 c #294C56",
+".q c None",
+".w c #263171",
+".e c #242F6F",
+".r c #314B6B",
+".t c #404B91",
+".y c #1F296A",
+".u c #1C2767",
+".i c #2B3779",
+".p c #1B2566",
+".a c #3A458B",
+".s c #283376",
+".d c #384389",
+".f c #364187",
+".g c #353F86",
+".h c #26346A",
+".j c #757BA7",
+".k c #44528E",
+".l c #7379A5",
+".z c #1E296C",
+".x c #1D276B",
+".c c #404E8A",
+".v c #2C377D",
+".b c #4E5E9B",
+".n c #3E4C88",
+".m c #2A357B",
+".M c #283379",
+".N c #263177",
+".B c #28465B",
+".V c #475694",
+".C c #7684A1",
+".Z c #455492",
+".A c #445291",
+".S c #323E7C",
+".D c #439F36",
+".F c #404E8D",
+".G c #303C7A",
+".H c #3F4E8C",
+".J c #1F2A66",
+".K c #5F6AB2",
+".L c #2A3674",
+".P c #3A992D",
+".I c #283472",
+".U c #5560A8",
+".Y c #838CB4",
+".T c #327243",
+".R c #2D3F70",
+".E c #2D6148",
+".W c #49549C",
+".Q c #242E71",
+".! c #232E70",
+".~ c #171F57",
+".^ c #34457A",
+"./ c #348434",
+".( c #323C82",
+".) c #202A6D",
+"._ c #1F2A6C",
+".` c #424F8B",
+".' c #51619D",
+".] c #1D286A",
+".[ c #1C2669",
+".{ c #4F5F9B",
+".} c #2B367B",
+".| c #4B5B97",
+"X c #6E79C0",
+"X. c #283278",
+"XX c #273277",
+"Xo c #399332",
+"XO c #495995",
+"X+ c #253075",
+"X@ c #384581",
+"X# c #6A75BC",
+"X$ c #465592",
+"X% c #222C72",
+"X& c #455391",
+"X* c #212C71",
+"X= c #33417C",
+"X- c #202A70",
+"X; c #333F7C",
+"X: c #1D286D",
+"X> c #3F4D8B",
+"X, c #5A65AC",
+"X< c #364382",
+"X1 c #5761A9",
+"X2 c #344180",
+"X3 c #222D6B",
+"X4 c #323D7E",
+"X5 c #2C3978",
+"X6 c #3C478B",
+"X7 c #7D85AD",
+"X8 c #394588",
+"X9 c #388B37",
+"X0 c #767FA6",
+"Xq c #333F82",
+"Xw c #488654",
+"Xe c #424D94",
+"Xr c #1F296B",
+"Xt c #203062",
+"Xy c #3E4990",
+"Xu c #1D2769",
+"Xi c #2C377B",
+"Xp c #293578",
+"Xa c #293378",
+"Xs c #263175",
+"Xd c #495894",
+"Xf c #252F74",
+"Xg c #242F73",
+"Xh c #475692",
+"Xj c #343F86",
+"Xk c #222D71",
+"Xl c #747BA7",
+"Xz c #212B70",
+"Xx c #43528E",
+"Xc c #1F296E",
+"Xv c #1E296D",
+"Xb c #3E4C89",
+"Xn c #4D5C9B",
+"Xm c #3C4A87",
+"XM c #3A4885",
+/* pixels */
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.w ,Xa AX..N K y.O.$ v.u.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q W.S...f 3.* 0 q wXXX+ K PX%X* YX-Xc.7 e.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q E '.tX6.d.f 3.* 0.Q w.jXl K PX%X* Y.$.$XcXc 9 e.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q p $ $ _ '.tX6.d.g.(.* 0.Q.M.jXl y PX%X* Y.$.$XcXv 9 9 9.[ e.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.qX1X1.1 f _ '.tX6..Xj.(.*.v.Q.M.jXl y PX%Xz Y.$.&XcXv 9 9 9.=.x [.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q D.KX,.U.1 ( _ 'Xy.a..Xj.(.*.v.Q.M.jXl y PX%Xz Y.$XcXcXv 9 9 9.].x.x.[ e.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q s s.KX,.U $ ( =XeXy.a.fXj c n.v.mX..NXf y P.OXzX-.$XcXcXv 9 9 9.x.x.x B B.p.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.`X# s.KX,X1.1 $.W =.tXy.a.f 3 c 0 q wX. HXf PX%X*Xz.$.$XcXc 9 9 9.=.x.x B B B B [.~.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.qXdX X# s.KX,.U.1 ( _ '.tX6.d.f 3.* 0 q zXX.j.# PX%X*Xz.$.&XcXv 9 9 9.].x.x B B BXu.[ [.~.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q {X X# s.KX,X1.U $.W =XeXy.a..Xj.(.*.v.m zXXXl.# PX%Xz Y.$.&XcXv 9 9 9.].x.x B B BXu.[.[ [.~.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.qXdX X# s s.KX,.U.1 ( _ '.tXy.a.f 3 c n q w z.NXl.# P.OXz.$.$XcXcXv 9 9.].x.x B B BXuXu.[.[ [.p.~.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.`X X# s s.KX,X1.1 $.W =XeXyX6...g 3.* 0 q w 6 HXl.#X%X*Xz.$.&XcXc 9 9 9 9 9.x.x B B BXuXu.[ [ [.p.~.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.qX#X# s s.KX,X1.1 $ ( _ '.tXy.a.fXj c n.v.m.MXXX+ K PX%X* Y.$.&XcXcXvXv 9.z.z.z 9 9 9 9.] BXuXu [ [.p.q.q.q.q.q.q.q",
+".q.q.q.q.q.q s s s.K.KX,X1.U $ ( _ 'XeXy.a...g 3.* 0 q wX. HXfXg P.OXz Y.$.$.$.$.$.$.$.$.$.).).).)._._Xr 9.].]XuXu.p.q.q.q.q.q.q",
+".q.q.q.q.q D s.K.KX,X,X1.1 $ (.W =XeXyX6.d.f 3 c n.v.m AXX ] K.#.l.O.+.+.+.+.+.+.+.+ 6 6 6 6 6 ] ] ] ].).).)Xr.y.y : e.q.q.q.q.q",
+".q.q.q.q.q.K.KX,X,X1.U.1 $ (.W =Xe.tX6.a.fXj.(.* 0 q wX. H.) y.#.l PXkXkXk z z.Q.Q.Q.Q.Q.Q.Q.!.! z z x x ] ].)._.y._ :.q.q.q.q.q",
+".q.q.q.qX1X,X,X1.U.U.1 $ ( _ =Xe.tXy.a...g 3 c 0 q.m AXX H.) y.#.#XgXgXgXgXgXgXgXfXf.X.XXg.Q.e.Q.eX3.eX3X3X3X3X3.J.J.J %.q.q.q.q",
+".q.q.q pX1.U.U.1.1 $ (.W _ 'Xe.tXy.a...g 3 c n.v.} wX. HX+ ]Xf 5Xl.X.X.X.XXsXsXsXsXsXs.X.X.w.w.e.e.e.h.h.h.hXtXtXtXtXt.J e.q.q.q",
+".q.q.q $.1.1 $ $ (.W _ = 'XeXyX6.a...g 3.(.* 0 q w SXX. H HXsXsXsXsXs. . . . < < < &.w.h ) ) G a.B.0.0.0 | | | |.0 a d.J.q.q.q",
+".q.q.q $ f ( (.W _ = 'Xe.tXyX6.a...g 3.(.* 0 q w AXXXXXXXX. . . XXXX S S } } }.s.s.I.I ) G.B.0.4.E b.T i o o./ o o b.E aXt.q.q.q",
+".q.q E _ _ _ = = 'Xe.tXyXy.a.d.f.g 3.(.* 0 q C AX.XXXXXX S x S.jX0 }XpXp }Xp.8.I ) G.B |.4 +.T.T.T o./././././ o o b.E.B.hXt.q.q",
+".q.q ' ' ' 'Xe.t.tXyXyX6.a...fXj 3 c.* 0 q.m A S S S S S }.! wX0X0 > > > > g g ) G | + iX9.P.PXoX9Xo.PXoXoX9././ o.T.E.B.hX3.q.q",
+".q W.t.t.tXyXyXyX6.a.a...f.g 3.( c n 0 q C A S SXaXa } w w.e CX0X0 >.i g g G.B | +.T.;X9.9X9.; i b + +.E.E.4.4 | |.0.B ).h *.J.q",
+".q.SX6X6X6.a.a.a.d...f.gXj 3 c.* 0.v q w A S SXa A w C C C *.iX0X0.iX5 V.R.0 + iXo.P.P.P.9.; b +.4.r.r.r G G.R ) ) ) ).I.I &.J.q",
+".q...d.d.....f.f.fXj 3 3 c.* n 0 q.} w AX. SXa A w C C C.}XiXiXiXiX5 V G.4 +.;.9.9X9.;.T + |.B.r.R.R.R.R V V V V V W W.L.8.8.I.q",
+".q.f.f.gXjXjXj 3 3.( c.* n 0.v q.m w SXXXX SXa w C C C.}XiXi N ;.3 V.r.4 i.9.D.P.9 i +.r.r.R.< O O.S.SX4X4.<.< ~ @ @ @.3 T W.8.q",
+".w 3 3.(.(.( c c.*.* n 0.v q.m w AX.XXXXXX S } C C C.}Xi N * ; `.C.r.4 i.9.9.9 i + |.r.r.^X=X= pX2 pX2 X.>.>.>.S O.< ~ @.3 T g.I",
+" ,.*.*.*.*.* n 0 0.v q q.m w AX.XX H. XXXX S w C C.}Xi N ;.w @.C j +.;Xo.P.9 i.4.r.^X= pX<X< Q QX<X<X< . .X2X2 X.> O.<.G @.3 T.L",
+"Xa 0 0 0.v.v.v q q.m w w.MX.XX H HX+ HXX S } w C.}Xi N N ;.h.R jXw iXo.9.; +.4.r.^ UX< Q Q hX8 Q Q Q Q QX<X< .X2 p.>.S O.G @ @ g",
+" A q.Q.Q.Q.Q.m w z z z 6XX H ].).) ] H. x.!.e *XiXi.w.w.w.h.rXw.DXo.D.; +.r.^.^ Q Q h E E E E h h h h h Q Q QX< p p X.S O.G @ T",
+"X. w w.M.M.MX.X.XXXX.N HX+Xf K y yXfXs. S w C.iXiXi ;.3.R.r.4 iXo.9.; +.r.rX@ r h h.6.6.6.6.6.6.6 E E E h h Q QX< .X2 X.S O.G @",
+".NXX.j.j.j.j.N H.jXlXlXl KXg.#.#.# 5Xs. .jX0X0X0Xi ;X0 ` jXw.;Xo.D.;Xw j.CX7 h E.Y.Y.Y - ^ ^.Y.Y.Y.Y R.6.Y.Y # # r Q #X7X7X7.< @",
+" KX+XlXlXlXlXfXf.#.#.#.# P P.l.l.#XlXsXXX0X0X0X0Xi T ` j jXwXo.9.9 + j jX7X7 l ^ - - k k.H.H k k k kXbXb.Y.Y.Y.Y h r # #X7X7.S.G",
+" y K K y y y y P P P PX%X%.O.O PXg.XXsXX } >.i.i @ V.r.4.;.9.D.9 i.r.^X@ E.6 ^ ^ ^.H.H.F.F.F.F.F.F.H.HX>Xb.- R EXM h r Q . p X.<",
+".O P P P P P PX%X%X%.OX*X*Xz.+XkXg.XXs SXpXp.i g.R.r.4 i.9.9.9Xw j.r V T @.< ^.H.S.SX;X;.,.,X;X;X;X;.F.H O O.<.GXm E T T g W p.S",
+".$X%X%X%X%X%.OX*X*XzXzXz Y Y.+XkXg.X. S } > g.R.r.4.;Xo.D.9 i j.CX@ @.6 ^.H.H.,.,.,.:.:.A.A.A.A.:.%.%.,.F.HX>Xb.-XmXMXM r . pX;",
+" vX*X*X*XzXzXzXzXz Y.$.$.$.$.+XkXgXgXs <.8 ) G.0 + i.9.9.9 +.4 jX0 h @ ^.H.F.,.,.:.A.AX&.Z.Z.ZX&X&X&.AXx.% 8.HX>Xb.- EXM r r .X;",
+".u Y Y Y Y YX-.$.$.$.$.&.&.$.+ z.Q.w &.I.I G.0 +.9Xo.P.9 i.4.^.C #.6.<.H.F.,.,.:.AX&.Z u u u u u uX$.ZX&.A.% 8.`X>Xb.- EXM rX@X=",
+".qX-.$.$.$.$XcXcXvXv 9 9 9Xr.)X3.e.h.h G.0.4.T.;XoX9.; +.r.r.^ r E.6 ^.H.,.,.:X&.Z u u.V t t t t t.V LX$ I.@.% 8 MX>.nXmXM rX@.q",
+".qXc.$.$.& 9XvXvXv.z v.].].y !X3.h G.B.4.T.;.P.P.P.; +.r.^ p h E ^ ^.H.,.,.:X&.Z u.V t F FXO XO t.VX$ I.@.% 8.cXb.-XmXMX@.q",
+".q.7Xc.z.y.J.2.2.2XtXtXt d d a a.0.4 b iX9X9Xo.; i.4.r.r U Q h.Y - ^.S.,.:.A.Z u.V F D D D D D FXO J LX$ I.k 8.`X>.nXmXM.G.q",
+".q eX:.].2Xt d d d d a a.0.4.E.T oX9.P.P.PXo.; +.4.r U p QX8 E.Y -.H.S.,.AX& u.V F F.| Z Z Z Z Z Z D F J LX$ 4Xx 8.cXbXmXMX=.q",
+".q.qXu.2 d a |.E.E.E.E.E b.T o././X9X9.; i +.4.B.R.RX= . Q h.6.Y k.HX;.:.A.Z u t F ZXnXn.b.b.b.bXn Z D F tXh I.k 8.`Xb.-XM.q.q",
+".q.q e d.0 b./XoXo.P.PXo./Xo.P.PXo./ o +.4.B.R V.<.>X2 QX8 E.6 - k.FX;.:X& u.V F.|XnXn.b.{.{.{.{.bXn Z DXO.oX$.@.%.`Xb.n.^.q.q",
+".q.q.q d.0.E o o./././ o.T.T.T b.E |.0.B G ) V @X4.>X< QX8 E.6 ^.H.F.,.A.Z u t D ZXn.b.{.'.'.'.'.{.b {.|XO.oXh IXx 8.c.n.q.q.q",
+".q.q.q d a | b.T.T.T b.E.E |.0.B G ) ).I.8X5 ; ~X4X2X< Q h E.6 ^.H.F.,.A.Z u t F D Z.b.{.'.' 7 7.'.'.{.b Z FXdXh IXx 8.cXm.q.q.q",
+".q.q.q.~ d d a a a a a a d d dXt.h.h.w.I gX5 ;.<X4Xq . QX8 E.6.Y k.FX;.A.Z u t F D Z.b.{.' 7 7 7 7.'.{.b Z FXdXh I.k 8.c 1.q.q.q",
+".q.q.q.q.7 % % % % % % /.u.y._ x.Q.w.sXp.i @ ;.<X4Xq . Q Q h.6.Y k.FX;.AX& u tXO D Z.b.{.' 7 7 7 7.'.{.b Z FXdXh IXx 8.n.q.q.q.q",
+".q.q.q.q.q.p.u.u /.u.u [Xu.y.) 6.! &.sXp.i @ ;.<X4X2 .X< Q h.6.Y k.FX;.:X& u t D Z.b.{.'.' 7 7 7.'.{ { Z FXdXh 4Xx.c.q.q.q.q.q",
+".q.q.q.q.q e B B B BXu B 9._ ] z.Q & <Xp.iX5 ;.<X4X2 .X< Q h E.Y k.HX;.%X&X$.VXO F ZXn.b.{.'.'.'.'.{.{ Z.|XO.oX$ 4Xx 1.q.q.q.q.q",
+".q.q.q.q.q.q.p BXuXuXuXu.]._ ] x.Q.w <.s >X5 @ ~X4.>X2X< Q h E RXb.H.F.%.A.Z L tXO D ZXn.b.{.{.{.{.{ { Z FXdXh 2.k m.q.q.q.q.q.q",
+".q.q.q.q.q.q.q [.[.[.[Xu BXr.) x.e.w <.8 >X5 @ ;X4.>X2 . Q h E.6XbX>.H.,XxX&X$.V J F D Z {.b.b.b { Z Z FXO.o 2 4 8.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.~ [.[.[.[Xu 9.) ].! * &.8Xp.i.3 ~ OX4X2 .X< Q h.Y.YXb O.F.%.A IX$ L J F D.| Z Z Z Z.| FXO.oXh I 8 1.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.~ [ [ [Xu.].) ].!.Q.w <.8.5.3 @.< O.>X2X< Q h.Y.Y.- O.H 8.%.@ IX$ L tXOXO F F F FXOXd.oXh I 8.^.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.~.p [ [.]Xr ] x.e.w &.8 >X5 @ ~ O.>X2 . Q Q #.Y R.<X>.H 8.%.@ IX$Xh.o.oXdXdXdXd.oXh 2 I 8.^.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.~.p [Xu.y ] x.e * &.8Xp.5.3 ~.<.S XX2X< Q #.Y E.GXbX>.` 8.%.k 4 IX$XhXhXhXhXhX$ 2 4 8.^.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.~.pXu.y._ ].! *.w.I.8 gX5 @ ~ O.> p pX< r hXMXm.-XbX> M 8 8Xx.k.@ I I I I 4 4.k 8 1.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.p :._ ] x.e.w &.8.L.5.3 @.< O.> p . Q r h EXm.-XbX>.c.` 8 8.%XxXx.kXxXxXx m.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q e :._ ].e *.w.I.8 g.5 @ ~.<.S XX2 # # r TXM E.-.nXbX>.c.`.` 8 8 8 8.c 1.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q : ]X3.e.w &.I.8 g.3 @.G O.S XX7 # Q TXMXM EXm.-.nXbXbXb.c.c.c.n.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.7.y.e *.w.I.8 W T.3 @.G O.SX7X7 . g r rXMXMXmXmXm.-.n.nXm 1.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.J.e &.I.8 W T.3 @.G OX7X7 p W . r r rXMXMXMXM.^.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.J.J.I.8 g T @ @.G.<.S X p p .X@X@X@.GX=.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.I.L g T @ @.G.<.SX;X;X=.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q"
+};
diff --git a/arts/builder/pics/Synth_AUTOPANNER.xpm b/arts/builder/pics/Synth_AUTOPANNER.xpm
new file mode 100644
index 00000000..89506f3b
--- /dev/null
+++ b/arts/builder/pics/Synth_AUTOPANNER.xpm
@@ -0,0 +1,292 @@
+/* XPM */
+static char * Synth_AUTOPANNER_xpm[] = {
+"64 64 225 2",
+" c None",
+". c #161E54",
+"+ c #B2821C",
+"@ c #465284",
+"# c #2E3A74",
+"$ c #D2B624",
+"% c #8A662C",
+"& c #32465C",
+"* c #5E6AB4",
+"= c #CE9E14",
+"- c #222E64",
+"; c #4E5E94",
+"> c #42528C",
+", c #364684",
+"' c #36324C",
+") c #424A7C",
+"! c #CE8A14",
+"~ c #2A3674",
+"{ c #32427C",
+"] c #FACE14",
+"^ c #F6A204",
+"/ c #4E568C",
+"( c #2A2E5C",
+"_ c #1A2664",
+": c #3E4E84",
+"< c #CA9614",
+"[ c #363A64",
+"} c #F6B60C",
+"| c #3A468C",
+"1 c #D2AA1C",
+"2 c #4A5A94",
+"3 c #22265C",
+"4 c #FAC20C",
+"5 c #AA7E24",
+"6 c #5A62AC",
+"7 c #4A529C",
+"8 c #2E3A7C",
+"9 c #2A3274",
+"0 c #AE922C",
+"a c #222E74",
+"b c #3E467C",
+"c c #424A94",
+"d c #324284",
+"e c #FEDA14",
+"f c #FABE0C",
+"g c #222A6C",
+"h c #52526C",
+"i c #6A76BC",
+"j c #DEA214",
+"k c #525EA4",
+"l c #F6B20C",
+"m c #4E569C",
+"n c #2A325C",
+"o c #D69A14",
+"p c #AE8A2C",
+"q c #46568C",
+"r c #DE920C",
+"s c #FEB604",
+"t c #222A54",
+"u c #BA8224",
+"v c #926E34",
+"w c #D2A61C",
+"x c #CE9214",
+"y c #D29614",
+"z c #B27A24",
+"A c #2E3E7C",
+"B c #222A64",
+"C c #B2861C",
+"D c #4A567C",
+"E c #6672BC",
+"F c #D69E14",
+"G c #26326C",
+"H c #46529C",
+"I c #3A4A7C",
+"J c #2A367C",
+"K c #3A4274",
+"L c #FED614",
+"M c #FEAA04",
+"N c #3A4A8C",
+"O c #D2B224",
+"P c #424E8C",
+"Q c #2A326C",
+"R c #162254",
+"S c #323E74",
+"T c #926A34",
+"U c #3E4674",
+"V c #666EB4",
+"W c #D2A21C",
+"X c #262E64",
+"Y c #4E629C",
+"Z c #46528C",
+"` c #424E7C",
+" . c #FED214",
+".. c #F6A604",
+"+. c #4E5A8C",
+"@. c #1E2A6C",
+"#. c #363E84",
+"$. c #F6BA0C",
+"%. c #DAAA1C",
+"&. c #FECA0C",
+"*. c #4A568C",
+"=. c #263274",
+"-. c #3E4A7C",
+";. c #3A428C",
+">. c #FEE21C",
+",. c #5662A4",
+"'. c #4E5A9C",
+"). c #262A54",
+"!. c #B27E24",
+"~. c #464E8C",
+"{. c #B6821C",
+"]. c #4A527C",
+"^. c #323A6C",
+"/. c #8E6A2C",
+"(. c #626EB4",
+"_. c #D29E14",
+":. c #3A467C",
+"<. c #32366C",
+"[. c #CE8E14",
+"}. c #2E366C",
+"|. c #364274",
+"1. c #FECE0C",
+"2. c #2A2E6C",
+"3. c #1E2664",
+"4. c #CE9A14",
+"5. c #3A3E64",
+"6. c #D2AE1C",
+"7. c #FEC60C",
+"8. c #5A66AC",
+"9. c #BA961C",
+"0. c #4E567C",
+"a. c #3A4674",
+"b. c #FEAE04",
+"c. c #DE9614",
+"d. c #6E7ABC",
+"e. c #DAA61C",
+"f. c #3A364C",
+"g. c #FEDE14",
+"h. c #FEBA04",
+"i. c #D29A14",
+"j. c #D69214",
+"k. c #FEB204",
+"l. c #1A2264",
+"m. c #363E74",
+"n. c #4A569C",
+"o. c #8E6624",
+"p. c #AE7E1C",
+"q. c #323A84",
+"r. c #364284",
+"s. c #2E325C",
+"t. c #323E7C",
+"u. c #3E4A8C",
+"v. c #1A225C",
+"w. c #42466C",
+"x. c #464E7C",
+"y. c #525A94",
+"z. c #525AA4",
+"A. c #B67E1C",
+"B. c #626AB4",
+"C. c #FAA204",
+"D. c #CE9614",
+"E. c #FAB60C",
+"F. c #3E468C",
+"G. c #FEC20C",
+"H. c #262E74",
+"I. c #FEBE0C",
+"J. c #565EA4",
+"K. c #966E34",
+"L. c #D29214",
+"M. c #262A64",
+"N. c #B6861C",
+"O. c #2E367C",
+"P. c #2E326C",
+"Q. c #52629C",
+"R. c #FAA604",
+"S. c #FABA0C",
+"T. c #D28E14",
+"U. c #5E66AC",
+"V. c #222E6C",
+"W. c #4E5E9C",
+"X. c #425294",
+"Y. c #4E5694",
+"Z. c #2A2E64",
+"`. c #3E4E8C",
+" + c #4A5A9C",
+".+ c #2E3A84",
+"++ c #2A327C",
+"@+ c #3E4684",
+"#+ c #222A74",
+"$+ c #2A3264",
+"%+ c #465694",
+"&+ c #222A5C",
+"*+ c #4A5684",
+"=+ c #3A4A84",
+"-+ c #3A427C",
+";+ c #424E94",
+">+ c #262E6C",
+",+ c #465294",
+"'+ c #4A5694",
+")+ c #26327C",
+"!+ c #3E4A84",
+"~+ c #5662AC",
+"{+ c #4E5AA4",
+"]+ c #262A5C",
+"^+ c #464E94",
+"/+ c #B68224",
+"(+ c #4A5284",
+"_+ c #323A74",
+":+ c #D29E1C",
+"<+ c #3A4684",
+"[+ c #2E3674",
+"}+ c #36427C",
+"|+ c #FECE14",
+"1+ c #1E266C",
+"2+ c #D2AE24",
+"3+ c #4E5684",
+"4+ c #6E7AC4",
+"5+ c #FEDE1C",
+"6+ c #FEBA0C",
+"7+ c #D29A1C",
+"8+ c #36428C",
+"9+ c #323E84",
+"0+ c #3E4A94",
+"a+ c #464E84",
+"b+ c #B67E24",
+" =.9 9 ++++=.H.a #+g @.3. ",
+" &+_+r.r.q..+8 J )+H.=.H.a a g a l.. . . ",
+" <.) 0+F.;.r.9+.+O.O.J )+=.H.#+a @.@.@.1+l.l.. . ",
+" _+'.n.%+;+c | 8+#.q..+8 J )+H.a a a #+a a #+@.@._ l.. . . . ",
+" y.J.k m 7 ;+0+| 8+d q.O.J J J ++=.H.a #+@.@.@.@.#+@.@.1+1+l.. . ",
+" Y.B.8.~+z.m H ;+c F.8+#.9+.+O.++=.=.H.a V.a #+#+#+1+@.@.1+@.1+@.l.. . . ",
+" (.(.* 6 J.{+n.^+c F.| 8+9+q..+O.++)+)+H.H.#+V.@.@.#+1+@.@.1+@.1+@.1+_ l.. . ",
+" P i E B.U.~+k {+n.H 0+N ;.#.q.A 8 J J 9 =.=.a a #+a #+@.#+1+1+@.1+@.1+1+1+1+l.. . . ",
+" K i E E * 8.~+z.m H ^+c F.| r.q..+8 J )+)+H.a H.a #+V.#+@.@.@.1+@.1+1+_ 1+1+_ 1+l.. . . ",
+" / d.E E * 8.6 k {+n.,+;+0+| 8+#.9+O.J ++++H.)+H.a g #+@.@.@.@.@.@.1+@.@.@.1+@.@.1+@.l.. . . ",
+" K E i V V * 8.k z.n.7 ;+0+F.;.8+9+9+.+O.~ ++H.H.a a V.#+a #+@.@.1+#+1+_ 3 @._ 1+@._ 1+_ l.. . . ",
+" x.d.i E V * 8.J.k '.n.H ;+u.| r.9+9+8 O.J ++=.=.=.H.a @.#+@.@.1+1+@._ 3.% < 5 3._ 1+1+@.1+1+l.. . . ",
+" V E V (.U.U.~+J.{+n.,+^+c 0+| 8+9+q.8 8 J J )+a a a a a @.@.#+@.#+@.@.@.t C $.9.& @._ 1+_ 1+_ l.. . ",
+" E E E (.* 8.~+k {+n.7 ^+`.u.;.8+#.q..+.+J J =.H.H.X Z.B @.M.B @.@.3.@.M.3.3 ' j 4 9.&+@.@.@.1+1+@.l.. . ",
+" ) V B.B.U.8.J.k z.m 7 ^+c F.| ;.r.#.q.O.J ++++=.=.>+p 1 w 1 W W W W _.W = = 4.F S.G.E.+ ]+@.a @.@.@.@._ l.. ",
+" B.* U.8.~+k k z.n.7 H ;+c N | r.#.9+8 8 J J =.)+a Z.1 L L . .1.&.&.&.7.7.G.G.G.G.I.G.l o.- g V.#+g g @.. . ",
+" Y.U.8.~+J.J.z.{+n.%+;+;+0+F.| ;.9+9+q.O.O.J ++=.H.H.X 1 L &.1 w w W W = _._._._.F f I.E.+ ]+a a V.a V.#+V.@._ . ",
+" #.6 ,.k z.k {+ +n.7 ^+;+0+0+| ;.9+#.8 8 8 J =.=.)+=.=.Q 1 .6.s.n X $+Z.$+$+$+$+X f.j S.+ ( G G =.=.>+a >+a g g v.. ",
+" ~.z.z.{+m n.7 H H ;+c 0+| ;.8+r.9+q..+J J J ++=.=.)+=.s.1 .w Z.9 =.=.9 =.)+=.=.n + l {.n 9 9 =.=.=.=.=.>+=.G V.g _ ",
+" {+n.n.m n.7 H ^+;+c 0+F.;.8+#.9+q..+O.O.++++=.9 ++=.++Q w .W }.=.~ ~ J J ~ J Q % D.p.n 9 ~ J 9 9 ~ 9 =.=.H.H.V.G v. ",
+" }.n.n.H H ,+;+;+0+0+F.;.| ;.#.q.A q.8 J J 9 ++)+=.=.=.~ P.w .W }.++J ~ ~ J ~ ~ ~ ~ }.}.~ J ~ ~ ~ ~ ~ ~ ~ ~ =.9 >+Q V._ ",
+" ^+;+;+;+c 0+c 0+F.| | r.#.9+9+9+.+8 J J 9 )+~ 9 )+++++9 }.w .w ~ # J J J O.O.8 8 [+# 8 8 8 8 8 8 J J ~ ~ ~ ~ ~ ~ =.- . ",
+" 3 0+c 0+u.F.N | | ;.r.8+#.9+9+q.O.J O.J ++~ )+)+9 ~ ~ J ~ }.w 1.W }.~ 8 8 8 8 8 8 A 8 8 8 8 8 8 8 # 8 8 O.~ ~ ~ ~ ~ 9 ~ G 3. ",
+" q.F.| F.| | ;.;.8+r.#.#.9+9+8 O.8 O.++9 ~ )+9 9 J )+J ++J <.w 1.w <.8 8 8 8 A q.t.q.9+A A 8 A A 8 A 8 8 8 8 8 J J ~ ~ Q X . ",
+" S ;.8+;.;.8+r.#.9+9+9+q.q.O.8 O.J J )+)+=.++J 9 J 9 J J 8 <.w &.W ^.8 A A q.A A A t.t.9+t.t.t.t.t.t.t.A t.8 _+8 [+~ J ~ 9 G ",
+" 8+9+#.9+9+#.9+9+9+q..+8 J 8 J ++J =.9 9 ++~ =.J J J 8 ~ J ^.W 1.W _+9+9+9+t.t.#.r.d #.}+r.#.9+d 9+9+t.t.A t.A 8 A 8 # ~ ~ 9 ",
+"H.9+9+q.9+q.q.q..+8 8 8 O.J J J )+++)+=.=.)+J ~ J ~ J 8 8 J ^.W 1.W ^.t.t.d r.9+t.#.d d d d }+r.}+}+d r.#.t.t.t.8 t.8 # # # # ~ ",
+"9 q..+.+.+8 8 8 O.J J J J ++++=.9 =.=.)+J ++9 )+J 8 J 8 8 q.^.W &.W m.9+d 9+d r., , r.r.;.<+, r.r.r.r.}+d }+r.}+{ t.t.t.8 # J ~ ",
+"=.O.8 O.J J J J O.++++J )+=.)+H.H.H.H.=.=.J ~ ++J 8 J 8 A 8 m.W &.W m.#.r.r., r.r.<+<+, , <+<+<+, <+, <+r.r.d #.t.t.t.A 8 _+# [+",
+"J J ++J J J ++J )+J =.$+~ Q s.Q Q $+Q $+}.Q }.}.}.}.^.^.^.m.[ w 7._.K , , , r.<+<+<+| <+<+<+| , <+<+<+, , , -+, }+r.t.t.t.A 8 # ",
+"9 J ++++++J ++9 =.)+Q 0 $ O O O O O 2+6.6.1 1 1 1 w w W W W e.4 7.= m.r.;.<+, , N =+N N N =+u.N N | <+<+<+, r., r.r.}+{ t.A 8 8 ",
+")+H.)+=.=.H.=.)+H.H.2.$ >.>.>.5+g.e e L L L L .|+ .|+1.1.&.&.7.7.:+K <+, , | u.| N u.u.u.u.N u.u.=+u.N =+N =+<+, , r.#.{ t.t.# ",
+"=.H.=.H.=.a H.H.H.a 2.0 O O O 6.2+2+2+1 1 1 w 1 w W w W W _.e.4 7.W K N N u.N N u.u.u.`.`.P `.`.`.`.: u.!+F.=+<+<+, , r.}+{ { t.",
+"a a a a a H.a a a H.g X X X X Z.Z.Z.$+P.P.}.^.<.^.^.^.S m.|.5.j G._.:.| @+N u.`.`.`.`.`.`.`.P `.P `.c `.u.: u.=+<+<+<+-+r.#.#.8 ",
+"a a a a a a a a V.a #+V.g #+#+V.=.H.=.=.++~ 8 J A 9+t.t.t.d |._.G.i.U N u.N u.u.`.P P ;+> > P ;+P > `.`.u.u.`.!+!+<+=+, , }+}+t.",
+"g #+#+#+#+#+V.a @.#+V.#+a V.V.a a =.=.J ~ J J 8 8 8 9+d r.r.K :+G.:+:.N N `.P > P X.X.> X.X.,+> X.P > > > : c `.N =+<+<+:.r.}+t.",
+"@.a V.a V.#+@.a @.@.@.@.@.@.#+V.a =.=.J ~ J 8 8 t.q.t.9+r.r.|.:+G.i.b `.`.`.`.X.> > ,+,+Z %+%+,+%+X.X.X.> > `.P u.N =+<+<+, , }+",
+"3.g #+@.#+g #+g #+#+#+@.@.#+V.#+H.H.=.=.J 8 8 8 8 t.9+r.r., -+_.G.:+-.`.P X.;+X.X.,+%+%+%+,+,+%+,+q ,+> ,+> > `.P : u.=+=+<+-+t.",
+" @.#+@.@.@.@.@.@.@.1+1+@.@.a a =.H.9 =.~ J 8 A A 9+{ r.r.<+K i.I.i.b `.`.> P ,+X.,+,+%+2 2 %+2 %+%+'+%+,+Z Z > P `.u.=+<+, <+ ",
+" @.@.@.@.#+1+@.@.@.#+#+1+#+g V.a =.~ J J 8 8 q.9+{ 9+d , , :._.G.7+) `.;+X.%+,+%+%+2 2 %+%+2 %+n.2 %+%+%+X.X.P P P : !+!+<+, ",
+" . 1+#+1+#+#+1+@.@.@.@.@.g a a H.H.=.9 J O.8 8 9+#.r.r.r.<+:.i.I.4.) > P X.%+%+%+%+%+ + + ++. +2 2 2 %+'+q %+Z > P `.`.!+<+S ",
+" v.@.@.@.@.1+@.1+1+1+1+1+@.g V.=.=.9 J ~ 8 8 t.t.{ r.r.| <+K i.I.i.) ;+,+Z %+%+2 + +2 W. +W.; '.'.2 2 2 %+q %+> > P !+!+a.X ",
+" l.@.1+@.1+@.@.@.@.@.@.@.#+a =.=.=.J J 8 8 8 9+t.r.r., N :.i.6+7+: > X.X.%+%+'+2 +*++.*+W.W.W.'.W.2 +%+%+,+X.P P `.=+<+ ",
+" v.1+@.1+@.1+1+1+1+1+@.#+V.V.a =.~ ~ # J 8 9+t.d d , ;.<+U 4.I.4.) > ,+%+%+n.2 2 *.T [.!.].; Y W. +2 2 '+%+q Z > > : `.n ",
+" l.@.1+@.@.@.@.@.1+@.@.#+H.a =.=.++# 8 8 A 9+r.r., <+| b i.h.i.) > ,+,+2 %+ ++.W.@ u ^ /+].Y Q.W.'.W.2 %+%+%+> P P : ",
+" _ _ @.1+1+_ _ _ 1+@.g V.V.=.=.++~ J 8 8 q.t.t.d r., =+a.D.h.o ) ` @ @ @ q *.*+*+(+h c.R.b+D W.Y ; 2 2 2 %+> X.> `.|. ",
+" . l.1+_ 1+1+1+@._ 1+g a V.a =.~ ~ ~ 8 A t.9+#.}+r.8+N b 7+h.k.o D.y L.L.L.x L.L.x y R.M C.u ].Y W.'.2 2 '+Z Z P P :. ",
+" 1+@.@.@.@.1+_ 1+@.@.@.H.H.=.=.)+J # 8 8 t.{ r., <+<+a.D.6+6+s s k.s k.k.k.b.b.b.M M M M ^ K.+.W.W.+.'+%+%+> > I ",
+" . 1+1+@.1+1+1+1+g @.V.G =.~ ~ J 8 8 t.9+{ r.r., <+:.5 D.y L.D.x L.L.L.L.x T.T.j.R.M ^ A.].; W. +2 2 q ,+Z : ",
+" _ 1+_ _ 1+@._ @.@.V.a a H.=.~ ~ [+8 8 t.9+r., ;.N <+b b -.` a+x.@ @ @ *+3+*+D h c.C.A.].k W.2 2 2 %+,+q > }. ",
+" l.1+@._ @.1+@.@.g V.=.=.=.~ J # 8 8 t.t.t., }+<+<+u.u.u.`.X.> Z '+%+%+ + +].A.^ A.].; Q.; W.2 '+%+Z > P ",
+" _ _ 1+_ 3.1+1+@.g V.H.=.9 ~ J 8 8 t.t.r.r., <+<+=+u.`.P P X.%+%+%+2 2 *+T ! z ].; W.2 W.2 2 2 q q > ",
+" . l.1+_ _ @.@.@.g V.H.=.~ ~ ~ 8 8 t.t.t.r.r., <+=+N !+`.P P X.,+%+'+%+%+@ @ *+'.; W.2 2 '+%+%+X.P a. ",
+" . _ 1+1+1+1+g V.a G =.=.~ J # 8 8 A 9+}+r.<+| =+!+`.`.`.> > q ,+%+%+'+2 2 +2 2 2 2 '+2 q q @ }. ",
+" . . 3.3.@.@.g #+V.=.=.~ J # 8 q.S t.{ d , ;.<+<+!+u.`.`.P > > %+q %+'+'+2 '+'+'+2 %+'+,+> a. ",
+" v._ _ 1+1+V.V.H.>+=.9 9 ~ # A A t.9+}+}+, , <+| : u.P `.P > Z ,+,+q q %+%+%+%+,+Z > > }. ",
+" . R @.1+@.V.a >+=.~ ~ J ~ 8 8 t.t.#.}+r., <+<+=+N `.`.P P > > X.X.Z q q q q %+Z > a. ",
+" l.@.g @.V.=.G =.=.~ O.[+8 t.t.{ { r.<+, | @+!+!+`.`.`.`.> > Z X.> X.> > > > ",
+" . @.@.g V.>+9 =.~ ~ # # 8 q.t.}+t.}+, , <+=+<+!+`.: c P `.> P P P P : :. ",
+" @.V.g a G G ~ ~ J J # A t.t.{ r.}+, , <+N <+!+!+: !+P !+`.P `.: ",
+" _ - a =.>+9 ~ [+8 # 8 A t.t.{ }+r.r.<+<+<+=+=+!+=+`.: : !+:. ",
+" @.G =.=.~ ~ ~ # # q.t.t.#.}+}+}+<+, <+<+=+!+<+}+ ",
+" &+- =.~ ~ O.# # q.A t.}+r.{ }+r.<+, S { ",
+" 9 ~ # # # _+8 A S #.}+}+ "};
diff --git a/arts/builder/pics/Synth_BUS_DOWNLINK.xpm b/arts/builder/pics/Synth_BUS_DOWNLINK.xpm
new file mode 100644
index 00000000..60596d77
--- /dev/null
+++ b/arts/builder/pics/Synth_BUS_DOWNLINK.xpm
@@ -0,0 +1,323 @@
+/* XPM */
+static char * BUS_DOWNLINK_xpm[] = {
+"64 64 256 2",
+" c None",
+". c #161E54",
+"+ c #828698",
+"@ c #35427C",
+"# c #8A4664",
+"$ c #F5C224",
+"% c #52629C",
+"& c #96162C",
+"* c #CA8419",
+"= c #5C4644",
+"- c #612244",
+"; c #43528C",
+"> c #28326C",
+", c #C6A254",
+"' c #3A3658",
+") c #E0E2E4",
+"! c #A0A2B4",
+"~ c #52424C",
+"{ c #303A73",
+"] c #8E6644",
+"^ c #3C4A84",
+"/ c #EC920C",
+"( c #BEBEC9",
+"_ c #525A7C",
+": c #6D7294",
+"< c #EEA20C",
+"[ c #ECB624",
+"} c #BEC2CC",
+"| c #B68234",
+"1 c #D70A14",
+"2 c #9E6624",
+"3 c #D1D2D6",
+"4 c #565A7C",
+"5 c #62668C",
+"6 c #343267",
+"7 c #3E4274",
+"8 c #BE9644",
+"9 c #741E3C",
+"0 c #B0B2C0",
+"a c #4E2C54",
+"b c #515677",
+"c c #F1F2F4",
+"d c #F5AA16",
+"e c #9396A5",
+"f c #2E3A7D",
+"g c #202A6D",
+"h c #273276",
+"i c #EA1214",
+"j c #A27A4C",
+"k c #364285",
+"l c #3E4A8E",
+"m c #F69604",
+"n c #DADADD",
+"o c #5662AB",
+"p c #455295",
+"q c #3C366A",
+"r c #4B5A97",
+"s c #EB060C",
+"t c #CD8A1D",
+"u c #6F4E37",
+"v c #464A7C",
+"w c #B8BAC4",
+"x c #7A5A4C",
+"y c #552E54",
+"z c #F6BA24",
+"A c #C7CAD3",
+"B c #565A8C",
+"C c #525AA1",
+"D c #868AA4",
+"E c #AE2C40",
+"F c #E9EAEC",
+"G c #F8A208",
+"H c #262E73",
+"I c #363A6B",
+"J c #7A7EA4",
+"K c #EA9A14",
+"L c #6672BC",
+"M c #BA923C",
+"N c #6A6E97",
+"O c #841E38",
+"P c #B6B6C5",
+"Q c #FAFAFC",
+"R c #FBAA04",
+"S c #363E82",
+"T c #B57E35",
+"U c #866284",
+"V c #AE0E1C",
+"W c #2E3264",
+"X c #A6AABC",
+"Y c #966634",
+"Z c #C29A4C",
+"` c #9A9EAD",
+" . c #222E71",
+".. c #2E367A",
+"+. c #3A4586",
+"@. c #424E8E",
+"#. c #5E6AB4",
+"$. c #4A5597",
+"%. c #3B3E6C",
+"&. c #EE0E14",
+"*. c #464E92",
+"=. c #1E266B",
+"-. c #7C82A4",
+";. c #D68611",
+">. c #682644",
+",. c #B98A41",
+"'. c #AE6E1C",
+"). c #D6D6DE",
+"!. c #722644",
+"~. c #B2B6C0",
+"{. c #323E7E",
+"]. c #DEDEE1",
+"^. c #5A62AC",
+"/. c #F70204",
+"(. c #8A5E34",
+"_. c #CECED5",
+":. c #FEA304",
+"<. c #3A4279",
+"[. c #664E44",
+"}. c #424A7C",
+"|. c #FEB204",
+"1. c #323666",
+"2. c #3E468A",
+"3. c #7A203A",
+"4. c #4E569B",
+"5. c #F6F6F7",
+"6. c #9A9AAC",
+"7. c #2A3678",
+"8. c #4E5E9C",
+"9. c #9E9EAC",
+"0. c #7A5234",
+"a. c #464E6C",
+"b. c #764E74",
+"c. c #6A5684",
+"d. c #66565C",
+"e. c #CE9224",
+"f. c #4A3E48",
+"g. c #A21224",
+"h. c #D29A28",
+"i. c #926E44",
+"j. c #965A7C",
+"k. c #DA8E14",
+"l. c #662A44",
+"m. c #E2060C",
+"n. c #BE0E1C",
+"o. c #5E6A9C",
+"p. c #747A9C",
+"q. c #866E54",
+"r. c #865A30",
+"s. c #BE7A1C",
+"t. c #926A4C",
+"u. c #A66E2C",
+"v. c #585E7C",
+"w. c #FA9E07",
+"x. c #6E7AC0",
+"y. c #EA1A24",
+"z. c #626AB4",
+"A. c #5D6284",
+"B. c #636EB4",
+"C. c #3E3E5C",
+"D. c #4A4668",
+"E. c #B28E5C",
+"F. c #654A39",
+"G. c #AE7C3C",
+"H. c #F29A07",
+"I. c #FEC214",
+"J. c #565EA4",
+"K. c #6A1E3C",
+"L. c #463A64",
+"M. c #8E6244",
+"N. c #F6B21C",
+"O. c #4A5281",
+"P. c #F2A214",
+"Q. c #FEBD14",
+"R. c #D2821C",
+"S. c #E6E6E9",
+"T. c #A6A6BC",
+"U. c #C6C6D2",
+"V. c #5E5674",
+"W. c #F20204",
+"X. c #EEEEF1",
+"Y. c #B61224",
+"Z. c #AEAEC4",
+"`. c #8285AC",
+" + c #B6721C",
+".+ c #FE0204",
+"++ c #C20E1C",
+"@+ c #FEB614",
+"#+ c #727694",
+"$+ c #A66A24",
+"%+ c #E6060C",
+"&+ c #19225C",
+"*+ c #EAA61C",
+"=+ c #656A91",
+"-+ c #A67E3C",
+";+ c #474E81",
+">+ c #F7BE1F",
+",+ c #CACED8",
+"'+ c #9A6A34",
+")+ c #525E8C",
+"!+ c #525EA3",
+"~+ c #868EAC",
+"{+ c #363E6D",
+"]+ c #2E3666",
+"^+ c #3A467C",
+"/+ c #BE7E2C",
+"(+ c #F29E10",
+"_+ c #921A34",
+":+ c #A2A6B5",
+"<+ c #323E74",
+"[+ c #3E4E84",
+"}+ c #B8863C",
+"|+ c #D2D6DC",
+"1+ c #414679",
+"2+ c #F2F6F4",
+"3+ c #F6AE14",
+"4+ c #969AAA",
+"5+ c #EE161C",
+"6+ c #364684",
+"7+ c #3E4E8C",
+"8+ c #DADEDE",
+"9+ c #5366A4",
+"0+ c #465694",
+"a+ c #3A3A64",
+"b+ c #CE8E1C",
+"c+ c #72523C",
+"d+ c #7E5E44",
+"e+ c #585E86",
+"f+ c #EAEEF0",
+"g+ c #F9A606",
+"h+ c #FDAE08",
+"i+ c #AF7224",
+"j+ c #F6060C",
+"k+ c #72527C",
+"l+ c #BA1224",
+"m+ c #662240",
+"n+ c #FAB614",
+"o+ c #46568C",
+"p+ c #2A366C",
+"q+ c #EE960C",
+"r+ c #C2C6CC",
+"s+ c #76223C",
+"t+ c #FA9A04",
+"u+ c #BABEC4",
+"v+ c #E69E14",
+"w+ c #6A76BC",
+"x+ c #FEFEFC",
+"y+ c #AAAEBC",
+"z+ c #C49E4C",
+"A+ c #7E86A4",
+"B+ c #5B66AC",
+"C+ c #D29624",
+"D+ c #DE0A0C",
+"E+ c #423E74",
+"F+ c #DE860C",
+"G+ c #925E2C",
+" &+h h h h h H .g g g =. ",
+" =.H ..{.S f f ..h H h H H .=.. . . . . ",
+" > <.1++.k k S f ....7.h h .g .g g =.=.&+. . . ",
+" W *.4.p *.l l k S f f f 7.h H .H H g .g g g g =.. . . . . ",
+" v !+C 4.p @.l 2.k k f ..7.7.7.h h . .g g .g g g =.=.&+&+&+. . ",
+" ;+B+B+J.!+4.$.*.2.2.k S S f ..h h h .H . . .g g =.g g g g g =.. . . . ",
+" J.o.#.B+!+C $.*.*.l 2.k S f f ..h h h .H g g g g g =.g g =.=.g =.=.&+&+. . ",
+" 7 L B.B.B+o !+4.$.p l 2.k S f S f 7.7.h h h . . .g g g g g =.g g =.=.=.g =.. . . . ",
+" 7 w+w+B.z.^.J.!+4.p *.l 2.6+k f f f ..h h H H H g .g g g =.g =.=.g g =.=.=.=.=.&+. . . ",
+" 4.x.L B.B.#.o !+4.4.*.7+l 2.k S S ..7.h h H h . . .g g g g g g g g =.=.=.=.=.=.=.g &+. . . ",
+" 7 : w+L z.B+B+o !+4.0+*.l 2.k k S {.f ....h H H . . . . .g g =.g =.g g g &+g =.=.=.=.=.&+. . . ",
+" @.x.w+L B.z.B+o C 4.4.*.7+l 2.k S S f ..7.h h h H H g g g g g g g =.g &+=.=.g =.g =.=.=.=.=.&+. . . ",
+" B+w+B.B.#.B+o !+C 0+p *.l 2.2.k {.f f f 7.7.h h .H .g g g g g g g g g g g =.&+g g g =.=.=.=.&+. . ",
+" B.B.B.B.z.B+o !+C 4.$.p l l 2.k S f f f 7.7.h . . . .g .g g g g g g g g g g . .g g g g g =.=.g . . . ",
+" 1+z.z.z.B+o o !+C r p *.*.l 2.k k S f ....h h h h H . .g . .g g g g .g .g g g .g g .g .g g g =.=.. . ",
+" #.#.B+o J.!+C 4.4.*.*.l l k k S S {.f 7.h h H h g . .g .g g . . . .g .g . . .g . .g .g g g =.g =.. . ",
+" v % B+o.`.~+D `.-.-.J J #+#+: : =+o.5 5 5 A.A.e+e+4 B 4 b b 4.b o+b o+_ o+b o+b o+_ O.b O.b b O.b b b a.g g =.. ",
+" S ^.J.C J F c c c c c c X.f+F X.F X.F F F S.S.) ].].].n n n n n ).).|+|+3 |+3 3 3 3 ,+3 3 3 3 ,+3 ,+,+,+y+{+g &+. . ",
+" }.J.C 4.J X.Q x+x+Q Q Q Q x+Q x+Q Q x+Q Q x+Q Q 5.5.c c c X.f+X.X.F F F F F F S.S.S.F F ) S.) S.) S.) ) r+}. . .g =. ",
+" $.4.$.$.N A |+3 3 3 3 3 _.,+,+A A A U.U.U.} r+} } ( ( u+w w w w w w ~.w ~.~.~.~.~.~.0 0 ~.~.~.0 ~.0 0 0 :+b > H =.. ",
+" W @.$.p p *.*.O.*.;+v 1+1+7 7 S %.I I I { 1.6 6 W ]+]+W W h W ]+W 6 1.]+]+1.1.1.]+W ]+1.]+]+]+]+W W 1.]+W > > > > . .. ",
+" *.*.*.*.l V.E.z+, z+z+z+Z Z 8 ,.d.D.j ,.,.,.,.,.}+}+| | x a+t.T | | | T T | T G.] I d+G.T T T T T T G.s.'+W > > > > g . ",
+" &++.l l l 2.v t.[ $ $ $ $ >+z z N.T M.-+*+3+3+3+3+d d *+g+* u u ;.K P.g+< < G G (+k.u F. +q+w.G G (+w.(+(+/ u W p+p+> > H &+ ",
+" f l 2.k 2.+.k v E.$ I.I.I.Q.Q.Q.n+3+C+t.$+v+|.|.h+h+h+h+g+G R.~ r.;.H.:.g+:.g+G G m ;.= u +t+:.w.:.:.:.w.m F+F.W h 7.> .. ",
+" > k 2.k k k S 7 q.[ >+Q.Q.Q.z n+n+3+v+Y ~ ;.3+h+R R R R R G H.G+f.$+H.:.:.:.g+:.G G t+2 = 0./ :.w.w.w.w.w.w.m '.f.]+f p+> H ",
+" S k S S S S S %.-+h.h.C+h.e.e.e.e.t (.' '+t t b+t t * t * * 2 ' r.s.* * * * R.* * * '.C.F.i+s.* * R.R.* * s. +f.]+f 7.]+> &+ ",
+"=...{.f S f f f I I I {+I I I ' ]+]+> > H W 1.]+1.1.I I { 1.6 6 1.1.I {+{+I {+I {+{+{+{+<+<+7 {+a+{+{+a+I {+a+I { f {.f f { p+g ",
+"h f f f f f ..;+T.P P ~.0 ~.0 0 0 0 0 0 0 0 y+Z.y+y+X ` B b.E Y.Y.E c.=+! X :+X :+T.X :+:+X :+:+:+:+:+:+:+:+:+4+_ {.{ <+f { p+..",
+"7...f ........e+) 2+c 5.c 5.2+c X.X.c X.X.X.F f+F F S.n N j.5+j+j+y.U -.|+) ].) ].].].].n ].].) 8+8+8+8+n n n A _ {.{.{.f { { 7.",
+"h h ..7.7.7.h B n c 5.5.5.2+5.c c c X.c X.X.X.F X.F S.A B # &././.i b.: _.) ) ].].].].8+].n ].n n n n n n n n u+a.{.{.{.{.{.{.{ ",
+"h h h h h h H 1+e ! ! ! 9.9.! 9.9.` ` ` ` ` 9.6.` ` ` + I >.s .+/.m.y v D 4+6.4+4+4+4+4+4+4+4+4+4+4+4+e e e e -.^+@ @ @ {.{.f { ",
+"h h h h h h h h H h h H h H H H H > > > > > 7.{ 7.{ { {.{ - W..+.+m.a {+<.@ ^+^+^+6+1+^+1+^+1+^+1+^+^+^+6+^+@ <.@ @ @ {.@ {.{.f ",
+"H H H H . . .h H .H H . . . . .H h h 7.7.7.f 7.f f {.f - %+.+.+m.a k ^ +.^ ^ l ^ 7+7+7+7+7+7+l ^ 7+[+^ ^ ^ 6+6+6+6+@ @ @ @ {.",
+" . .h .H H H . .g . . .g . .H h H h h 7.7.7.f f {.{.{ - W..+.+m.a 6++.7+l l 7+7+7+7+7+7+@.7+7+@.7+l l ^ ^ ^ +.+.6+k k {.{.f ",
+" .g . . . .g .H .g g .g g g .h h 7.h 7.f f f f f {.{ - %+/..+m.a +.^ 7+7+7+@.@.@.@.; ; @.; ; 7+; 7+@.7+l 7+^ 2.^ 6+6+@ @ {.",
+"g . . .g g H . .g g g g g . . . .h h 7.7.7.f f {.{.{.{.- %+.+/.m.a ^ ^ l 7+@.; 7+p p p p ; p ; p 7+; @.7+7+^ ^ ^ +.6+^+k @ {.",
+"g g g .g g g g g . .g g g . .h .h 7.7.f f f {.{.k {.{.- W..+/.m.a ^ 7+7+7+7+p ; ; p ; 0+p p ; 0+p ; @.; ; 7+^ [+^ +.+.+.6+@ ",
+"=.g g g .g g g g g g g g g g H .h h h 7.7.7.{.f {.{.k {.- %+.+.+m.a +.7+@.p p p p 0+0+0+p 0+0+0+; p ; p @.@.@.7+l ^ ^ ^ 6+<.{.",
+" . g g g g g g g g g g g g g .H h h h 7.f 7.f {.f { S {.- %+.+/.m.y ^ 7+l @.; 0+p p 0+r 0+0+$.r 0+0+0+o+p ; ; 7+7+[+^ ^ 6+^+ ",
+" g g g g g g g =.=.g g =.g . .h H 7.7...7.f {.f 1.9 { <+- W..+.+1 y ^ E+s+[+0+p 0+r r 0+r r r 0+r r 0+0+o+p @.; @.l ^ 2.@ > ",
+" . &+g =.=.g g g =.=.g g g g .h .h 7.f f f f f 6 n.K.1.m+%+.+.+D+y L.s+Y.}.0+0+0+0+r r r r r r r r 0+0+0+0+; @.; 7+[+^ ^ { ",
+" &+g g g g =.g g g g =.g g . . .h 7.h 7.f f {...6 1 1 K.& W..+.+W.O 3.D+++v 0+r r r r r r r 8.r r r r 0+o+0+; ; @.7+^ ^ @ H ",
+" . =.g g =.=.=.=.g =.=.g g H h H 7.7.7.7.f {.{ 6 g.W.%+W././..+/.W.W.%+_+@.0+0+r r 8.8.8.8.8.8.8.r r r 0+$.o+; ; ; @.^ ^ ",
+" &+g =.=.g g g g =.g g g . . .h h h 7.f f {.{.{.6 V /./..+.+.+/.W.%+_+}.p 0+r r 8.8.8.8.8.8.8.8.8.8.r $.0+0+; @.@.[+}.H ",
+" . g =.=.=.=.g =.=.=.g g .h h 7.7.7.f f {.{.k k 1.& %+/./..+W.1 3.1+o+r 0+r r r 8.% 8.% % 8.% 8.r 8.r o+0+o+; @.[+^ ",
+" =.=.g =.g g =.=.g g g . . .h h 7.f f f f {.{.k @ q 9 m././.1 >.1+; 0+0+r r 8.8.8.8.% % % % 8.8.8.r r $.0+p ; ; 1+p+ ",
+" . &+g =.&+g &+=.=.=.g . . .h h h 7.f {.f {.{.k k <.' 9 m.m.>.E+@.p 0+0+r r 8.8.8.% 9+9+9+9+% % 8.8.8.r o+p ; ; 7+^+ ",
+" &+=.=.g =.g g g g g g H h h h 7.7.f f {.{.k k k k <.q 3.s+7 7+; ; p r r r r 8.% % % % 9+% % 8.)+r r 0+0+o+; 7+I ",
+" . =.&+g &+=.=.=.g .g .h h 7.7.f f {.{.@ k k k +.+.<.1+l @.p o+p 0+0+r 8.8.8.% % % 9+% % % 8.r r r o+p ; [+ ",
+" &+=.g =.g =.=.g g g .> > p+7.7.7.f f {.{.@ 6++.+.^ ^ l 7+; ; p 0+0+r r r r 8.8.% % % % % 8.8.r r 0+0+; [+W ",
+" . &+=.=.=.=.g g g g . .h 7.7.f f { {.k {.6+6+6+^ ^ 7+l 7+; ; 0+0+0+r r 8.8.8.8.8.% 8.8.8.8.r r o+o+; ; ",
+" =.&+=.=.=.=.g . . .h > h p+7.f f {.{.@ k 6++.6+^ ^ 7+@.7+p ; p 0+0+r r r 8.8.8.8.8.8.r r r 0+; o+@ ",
+" . &+=.&+=.=.=.g . . .h 7.7.7.f f {.{.{.k k +.6+^ ^ l 7+7+p ; 0+o+0+0+r r 8.8.r r r r r o+o+$.; ; @ ",
+" . g g &+=.g g g .> > h p+7.f f f {.{.{.k 6++.+.^ 7+7+@.7+; ; p o+0+0+0+o+r r r r r o+r o+o+O.]+ ",
+" . &+&+=.g g g g .h h 7.7.f f { {.@ k k 6+6+6++.l [+7+7+; ; p o+o+o+r o+r r r o+r o+$.o+; @ ",
+" &+g &+=.g g .H .> > > p+f {.{.{.{.@ k 6++.^ +.^ ^ 7+7+; ; p p 0+; 0+o+o+o+r ; o+o+@.]+ ",
+" . &+=.g g . . .h 7.7.7.7.f f {.{.k k 6+k ^ ^ l l l l 7+@.; ; p ; ; ; ; ; o+p ; ; ^+ ",
+" =.g g g . .> > h p+7.7.f {.{.{.@ @ 6+k 6+^ ^ [+[+[+7+7+7+; ; ; p ; p ; ; @ ",
+" . =.g . . .> h 7.7.{ f f <+{.{.@ k k k +.+.^ l [+[+@.7+@.@.; ; ; @.[+1+ ",
+" g g g .h > p+7.p+7.f f {.{.{.@ ^+6+6+^ +.+.^ l ^ 7+[+[+7+7+[+@ ",
+" . g .> .h h 7.f { f {.{.{.{.@ 6+k 6+^ +.^ ^ ^ l ^ ^ ^ ^ ^+ ",
+" g > > p+p+p+f { f <+{.{.S S @ @ k ^++.+.+.^ ^ ]+ ",
+" . .> h 7.f f <+<+{.@ @ @ @ k 6+^+^+<+@ ",
+" > p+7.7.f { { f {.@ @ > "};
diff --git a/arts/builder/pics/Synth_BUS_UPLINK.xpm b/arts/builder/pics/Synth_BUS_UPLINK.xpm
new file mode 100644
index 00000000..3c4b45ca
--- /dev/null
+++ b/arts/builder/pics/Synth_BUS_UPLINK.xpm
@@ -0,0 +1,323 @@
+/* XPM */
+static char * BUS_UPLINK_xpm[] = {
+"64 64 256 2",
+" c None",
+". c #161E54",
+"+ c #727A8C",
+"@ c #8E162C",
+"# c #3A4A7C",
+"$ c #561A3C",
+"% c #2E3668",
+"& c #B5B6BC",
+"* c #52629C",
+"= c #6C4A34",
+"- c #C27A19",
+"; c #525274",
+"> c #3E364C",
+", c #3E1E4C",
+"' c #4A567C",
+") c #8E5664",
+"! c #35427C",
+"~ c #F29604",
+"{ c #1E2A64",
+"] c #3A4A86",
+"^ c #D00A14",
+"/ c #3A366C",
+"( c #D2D6D4",
+"_ c #2A2E5C",
+": c #8E92A4",
+"< c #DE860C",
+"[ c #4E3A3C",
+"} c #8A623C",
+"| c #464E8C",
+"1 c #DE1A24",
+"2 c #323E7C",
+"3 c #EE464C",
+"4 c #6E728C",
+"5 c #9D6A2C",
+"6 c #ED060C",
+"7 c #DA9A9C",
+"8 c #464A6C",
+"9 c #263273",
+"0 c #2B367C",
+"a c #C4C6CC",
+"b c #4E5E9A",
+"c c #F9A204",
+"d c #6C1E3C",
+"e c #6A564C",
+"f c #D28612",
+"g c #322A5C",
+"h c #364285",
+"i c #E6E6E7",
+"j c #82869C",
+"k c #3E426C",
+"l c #475695",
+"m c #202A6D",
+"n c #42528C",
+"o c #9A9EAC",
+"p c #383E71",
+"q c #EE8E0C",
+"r c #AA6A1C",
+"s c #F90204",
+"t c #232659",
+"u c #AE1224",
+"v c #5F1A3C",
+"w c #5662AB",
+"x c #424A94",
+"y c #2A3279",
+"z c #525A94",
+"A c #FC9A04",
+"B c #9A96A4",
+"C c #6E7AC0",
+"D c #323665",
+"E c #DEDEDD",
+"F c #2C3260",
+"G c #49527C",
+"H c #3E468B",
+"I c #4E569B",
+"J c #1D2664",
+"K c #BBBEC4",
+"L c #D18214",
+"M c #82562C",
+"N c #B37221",
+"O c #5E66AC",
+"P c #7B8294",
+"Q c #A21224",
+"R c #424A73",
+"S c #451E4C",
+"T c #3A426F",
+"U c #3E4E8B",
+"V c #969AA8",
+"W c #E58E0C",
+"X c #6A4634",
+"Y c #464E94",
+"Z c #636EB4",
+"` c #CECED4",
+" . c #4A2A54",
+".. c #3A428C",
+"+. c #484660",
+"@. c #262E72",
+"#. c #363E83",
+"$. c #70523C",
+"%. c #8E6234",
+"&. c #F69E04",
+"*. c #262A5C",
+"=. c #E3060C",
+"-. c #EF0A0C",
+";. c #464E77",
+">. c #323A84",
+",. c #DA8A0C",
+"'. c #FA0A0C",
+"). c #661A3C",
+"!. c #525E99",
+"~. c #2B326C",
+"{. c #7A7E94",
+"]. c #4E264C",
+"^. c #2E3A74",
+"/. c #BABAC4",
+"(. c #CA8214",
+"_. c #F69604",
+":. c #3E4A94",
+"<. c #373A67",
+"[. c #9696AA",
+"}. c #58423C",
+"|. c #FEAA04",
+"1. c #D28A0C",
+"2. c #EEEEED",
+"3. c #3F4672",
+"4. c #222E71",
+"5. c #F69204",
+"6. c #FE0204",
+"7. c #5E1E3D",
+"8. c #5A62AC",
+"9. c #72768C",
+"0. c #BE1E2C",
+"a. c #EE2E34",
+"b. c #C60A14",
+"c. c #821634",
+"d. c #7E3E54",
+"e. c #5E4A4C",
+"f. c #9B662C",
+"g. c #323254",
+"h. c #D6C6CC",
+"i. c #32265C",
+"j. c #7E5E3C",
+"k. c #423E5C",
+"l. c #8E8EA0",
+"m. c #A6A6B4",
+"n. c #626684",
+"o. c #423244",
+"p. c #961E34",
+"q. c #6A76BC",
+"r. c #4A264C",
+"s. c #625254",
+"t. c #EA9A9C",
+"u. c #582244",
+"v. c #764E34",
+"w. c #3E2E5C",
+"x. c #745640",
+"y. c #4E4A5C",
+"z. c #5E463C",
+"A. c #A46A28",
+"B. c #8A5E34",
+"C. c #D70A14",
+"D. c #3E3A64",
+"E. c #DADADC",
+"F. c #F20204",
+"G. c #B26E1C",
+"H. c #B60E1C",
+"I. c #E2DEE4",
+"J. c #865A2C",
+"K. c #E68A0C",
+"L. c #FEA204",
+"M. c #A61224",
+"N. c #4A224C",
+"O. c #EA8E0C",
+"P. c #19225B",
+"Q. c #56464C",
+"R. c #4E567C",
+"S. c #424E8C",
+"T. c #4E5A9A",
+"U. c #424E78",
+"V. c #3A467C",
+"W. c #36326C",
+"X. c #46325C",
+"Y. c #767E94",
+"Z. c #B6BAC3",
+"`. c #222E64",
+" + c #2E3A7D",
+".+ c #364684",
+"++ c #EAEAEC",
+"@+ c #4A5A94",
+"#+ c #EF9207",
+"$+ c #AE6E1E",
+"%+ c #F6060C",
+"&+ c #5266A4",
+"*+ c #9A9AAB",
+"=+ c #7E5A34",
+"-+ c #B67624",
+";+ c #5F6AB4",
+">+ c #475299",
+",+ c #6672BC",
+"'+ c #6E4E34",
+")+ c #36467C",
+"!+ c #F09A04",
+"~+ c #9296A4",
+"{+ c #6E768C",
+"]+ c #4A4E6C",
+"^+ c #868A9C",
+"/+ c #9EA2AC",
+"(+ c #FE9E04",
+"_+ c #BEC2C4",
+":+ c #9E1624",
+"<+ c #46224C",
+"[+ c #E6920C",
+"}+ c #D1D2D4",
+"|+ c #CA7A14",
+"1+ c #2A265C",
+"2+ c #4E5274",
+"3+ c #96622C",
+"4+ c #A26624",
+"5+ c #561E44",
+"6+ c #46528B",
+"7+ c #323A69",
+"8+ c #3A4686",
+"9+ c #262E64",
+"0+ c #5E4E54",
+"a+ c #96162C",
+"b+ c #D68214",
+"c+ c #52669C",
+"d+ c #F69A04",
+"e+ c #FE0604",
+"f+ c #5A66AC",
+"g+ c #D6CACC",
+"h+ c #F20604",
+"i+ c #E2E2E4",
+"j+ c #FEA604",
+"k+ c #A61624",
+"l+ c #EA920C",
+"m+ c #2A3674",
+"n+ c #47568C",
+"o+ c #2A2E6C",
+"p+ c #323E84",
+"q+ c #1E266C",
+"r+ c #535EA4",
+"s+ c #363A74",
+"t+ c #3E3E6C",
+"u+ c #1A2264",
+"v+ c #26327C",
+"w+ c #525AA4",
+"x+ c #3A427C",
+"y+ c #424E94",
+"z+ c #4E5AA4",
+"A+ c #767A92",
+"B+ c #3E4A77",
+"C+ c #C67A1C",
+"D+ c #222A62",
+"E+ c #3E4A87",
+"F+ c #D6D6DA",
+"G+ c #2E2E64",
+" u+y y y y 9 @.4.m m m J ",
+" t _ m+2 #. + +0 v+@.v+@.@.4.J . . . . . ",
+" F x+8+H ..h p+ +0 0 0 v+v+4.m 4.m m q+q+u+. . . ",
+" F | l >+y+:.H h p+>. + +0 v+@.4.@.@.m 4.m m m m q+. . . . . ",
+" x !.w+I >+Y :.H ..h >.0 0 0 0 v+v+4.4.m m 4.m m m q+q+u+u+u+. . ",
+" ;.O O r+r+I >+x :.8+h p+p+>.0 y v+v+4.@.@.4.4.m m q+m m m m m q+. . . . ",
+" !.;+;+f+r+z+I >+y+:.8+..p+>.>.0 y y v+@.4.m m m m m q+m { q+q+m J q+u+u+. . ",
+" k ,+Z Z O 8.r+z+>+>+x H ..#.>.p+ +0 0 v+v+v+4.@.4.m m m m m q+{ { q+q+q+{ q+. . . . ",
+" k q.q.Z ;+8.r+w+z+>+y+:.8+.+.. + + +0 y 9 @.@.4.m 4.m m m q+m q+q+m { q+q+q+J J u+. . . ",
+" I C ,+Z Z ;+w r+z+>+Y y+:.H h p+p+0 0 y y @.v+4.4.4.m m m m m m m m q+q+q+q+q+q+q+m u+. . . ",
+" k ,+q.,+;+O f+w w+z+l y+:.8+....p+p+ +0 0 y @.@.@.4.4.4.4.m m q+m q+m { { q+q+J q+J q+J u+. . . ",
+" | C q.,+Z ;+f+w w+z+>+Y y+:.8+..p+p+>.0 0 v+v+v+@.4.m m m m m m { q+{ q+q+q+{ { { q+J q+q+q+u+. . . ",
+" O q.Z Z ;+f+w r+z+I >+Y x H .+..p+>. + +0 m+v+v+4.@.4.m m m m m m { m { { q+q+q+m { m q+q+J J u+. . ",
+" Z Z Z Z O f+8.r+w+I l >+x :.8+..p+>.>. +0 0 v+@.4.@.4.m 4.m m m m m m m m m m m m m m { { m q+q+m . . . ",
+" 3.;+;+;+;+8.w r+z+I >+y+y+:.H ....p+>.0 0 y y 9 @.4.m m m m 4.m m m m m m m 4.4.4.m m m 4.m 4.m m { q+J . . ",
+" ;+;+;+O 8.w r+z+z+l Y y+:.H 8+h p+p+ + +0 0 @.i.v ).).$ u+4.m 4.4.4.4.4.4.4.4.4.4.4.4.4.m 4.m 4.m m 4.q+. . ",
+" x 8.8.w r+r+T.I I >+>+y+:.8+....p+p+>.0 0 0 v+@.5+C.6 6 C., 4.4.4.4.4.v+4.v+4.4.4.v+v+4.v+4.9 4.4.4.m 4.4.m J . ",
+" p 8.r+r+w+w+I I I >+Y y+x H H ..p+p+>. + +0 v+y @.v 6 e+6.=.S 4.v+v+v+v+4.v+4.v+v+9 v+9 9 4.9 4.9 9 @.9 4.4.4.P.. . ",
+" E+!.!.z+z+@+I >+y+y+:.H H .+..h p+>. +0 0 0 m+v+@.v 6 e+e+=.S 4.v+9 v+m+v+m+v+v+9 9 v+m+m+v+m+9 9 9 @.9 @.4.9 4.D+J ",
+" I I I I l >+>+>+y+x :.H .+..h p+>.>.0 0 y y v+v+@.7.6 6.6.=.S @.v+m+m+v+m+v+m+m+0 m+m+m+v+m+v+y 9 m+m+9 9 9 @.@.J . ",
+" F Y >+>+Y y+y+:.:.:.H H ..#.p+>.p+>. +0 0 v+9 y v+@.7.-.6.6.=.<+9 y v+m+m+0 0 0 0 0 0 m+ +m+^.m+0 m+9 m+m+~.9 9 9 @.`.. ",
+" Y y+y+y+y+x :.:.:.H .+..h h p+p+ + +0 0 y y y 9 y y $ 6 e+e+=.S 9 + +m+ + + + + + + + +0 +0 + +m+ +m+m+m+m+m+~.9 D+. ",
+" P.H x :.:.:.H H ...+..h p+p+>.>.0 0 0 0 v+m+v+y m+y y v 6 6.6.=.<+y 0 +0 + + +^. + + +2 +2 +^. +0 ^.0 m+m+m+m+m+~.9 @.P. ",
+" >.:.8+8+..H .+....#.p+p+p+>.>.0 +0 y y 0 v+y 0 v+m+y 7.6 6.6.=.<+m+ + +2 + +2 p+2 2 2 +2 +2 +2 2 + + + +m+^.m+m+~.`.. ",
+" ~.........#...h h p+p+>.>. + +0 0 0 y v+y m+9 m+0 0 y v 6 s 6.=.r.m+ + + +2 +2 2 p+2 p+2 2 2 p+2 2 2 2 2 + + + +^.^.m+~.9+ ",
+" p+h p+h p+p+p+p+>.>.>. +0 0 0 y 0 v+9 y 9 v+y y m+m+9 7.F.6.6.=.<+ +2 p+p+p+! p+p+2 p+! p+! p+! 2 p+2 2 2 2 2 ^. + +m+m+9 P. ",
+"t 0 p+>.>.p+>.>. + +0 0 +0 0 0 v+y v+9 v+y 0 0 v+y 0 m+5+6 s 6.=.r. +2 2 2 p+p+! ! ! h h h h ! h ! h h 2 2 2 2 2 +2 ^.^.^.m+D+",
+"v+ +>.>. + + +0 +0 0 0 v+0 v+v+v+9 9 9 m+9 9 9 _ <+m+y 7.6 6.6.=.r.^.m+r.2 ! ! h h h .+.+h .+h h h h ! h h ! p+2 2 +2 +^.m+m+",
+"y 0 0 0 0 0 0 0 0 0 0 0 v+v+9 @.@.@.@.v+0 0 0 0 g u <+W.v F.s 6.=.].W.u.Q ^..+.+.+.+h .+.+.+.+.+.+.+h .+! h ! h ! ! 2 2 ^.>.^.m+",
+"0 0 0 0 0 0 0 v+y v+v+v+9 v+@.v+v+v+v+9 9 9 m+y g ^ ^ 5+c.6 6.s F.).d ^ b.p h ...+.+] 8+8+8+8+8+8+.+.+.+.+.+.+! h 2 2 2 2 2 2 ^.",
+"y v+v+v+v+v+y v+y @.v+@.@.v+4.4.@.@.@.v+0 y 0 m+g H.F.=.F.s 6.6.F.6 F.F.Q #..+.+:..+8+:.] ] ] ] E+E+8+8+.+.+.+h .+h ! ! 2 2 +^.",
+"v+y y v+v+v+9 9 9 y @.~.9 @.@.@.@.9 9 ~.9 m+m+~.m+w.1 '.6.6.6.6.6.s '.0./ ! 8+8+8+H ] 8+8+8+] 8+8+8+] V.8+V.! )+h )+h 2 ! 2 2 +",
+"4.v+@.@.@.4.t+^+V V ~+[.[.~+~+~+~+~+: ~+: ~+~+: ~+*+t.a.%+s 6.6.s -.3 7 B ~+: : ~+: ~+~+~+: ~+: : l.: : l.: {.3.h .+.+! ! ! ! 2 ",
+"4.4.4.4.@.m R.F+++2.2.2.2.2.++++++++++i ++i i i i i+h.d.Q F.s s 6 p.) g+I.E E E E.E.E.E.E.E.E.E.E.E.E.F+E.F+K U.V.8+.+h h 2 2 +",
+"m @.@.4.@.m 2+F+2.2.2.2.2.2.++++++++++++++i ++i ++i _+t+ .Q F.F.@ X.; a E E E E E E E E.E E E E E E.E E.E.E./.;.V.H ] )+8+! ! 2 ",
+"m 4.4.m m m p l.o o *+*+*+o o *+*+o *+*+*+o V *+*+*+P 2 ^.w.Q a+<.h 3.^+o o V o o V V o V V V V V V ~+~+V B j B+] ] 8+8+)+..! 2 ",
+"q+m 4.4.m m m *.G+_ _ _ *.*._ _ G+_ 9+F g.D 7+D <.D.<.7+7+2 7+<.p T T T k 3.3.3.3.3.B+R R R R 3.R R R 3.3.8 V.B+E+E+] 8+.+8+)+! ",
+"m m m m 4.m o.$+f 1.1.f 1.,.1.1.- z.> 4+f f f 1.f f f f (.J.k.f.L f f f f f f L f A.+.} - f L f L L L f L $+3.E+E+U E+] ] )+..2 ",
+" . m m m m J [ f &.|.j+|.|.j+|.c b+M }.L L.L.j+j+L.j+L.&.O.3+Q.- d+&.L.L.(+L.(+&.#+A.0+f.O.A (+(+(+A (+A 5.A.+.B+E+E+E+H )+V. ",
+" m m m m q+m t X !+j+|.|.j+j+j+j+!+C+= '+W L.j+L.j+L.L.L.&.< $.'+- _.(+(+(+(+(+(+&.W j.e 5 #+(+(+(+(+(+(+A O.B.+.E+U E+] ! F ",
+" . u+m m m q+J [ [+&.&.&.&.&.&.&.&.L v.}.f d+&.d+d+d+_._.~ K.=+e.$+_._.d+_._._._._.q %.$.} l+_.5._.5.5.~ 5.#+f.y.S.U U B+] ^. ",
+" u+m m m q+{ t M $+G.G.G.G.G.$+G.r = g.M $+G.G.G.G.N N N G.B.k x.A.N N N N N -+N N f.]+s.f.N N -+N N N G.N 4++.B+U U E+E+! 9+ ",
+" . q+{ q+J t *.*.*._ _ _ _ _ g.F F 9 % 7+7+<.p <.t+p T T T x+3.3.R R 8 ;.8 ;.;.G ;.n+G G G G G G G G U.U.U.U 6+S.S.U E+# ",
+" u+q+q+J 3.m./.K /././././.Z./.Z.K K /./.K K /.Z.Z./.Z.Z.& /.& /.& & Z.& Z./.Z.& & & Z.& & & & & & & & & o G S.6+n U # 9+ ",
+" . q+J ]+}+++2.2.2.2.2.2.2.++++++++++++++++i i i i i i i i i+i+i i+i+I.E I.i+i+E E i+i+E E i+E E E E.E K ' n n S.S.E+ ",
+" q+{ J 8 a E E.E.E.E.E.E.E.E E.E.E.E.E.F+F+E.F+E.F+F+F+}+( ( F+}+( ( ( ( ( }+}+}+}+` }+}+}+` ` ` ` }+` & 2+n n n V.% ",
+" . u+J F n.4 4 4 4 4 4 4 4 4 9.{+4 4 9.9.9.9.9.9.9.+ A+9.A+A+Y.A+A+A+A+A+Y.Y.Y.Y.P Y.P P P Y.{.Y.A+A++ 4 U.n 6+n U )+ ",
+" J J J J J J t J { { { D+`.9+9 % % m+^.^.2 2 2 ! ! ! V.H E+E+E+S.S.n n+6+n+n+@+@+z b b b b b z @+@+n+n+n 6+n S.7+ ",
+" . u+J J J J J J m 4.@.9 @.9 m+^.^.^. +2 ! ! ..8+] ] E+E+S.n n 6+>+l l T.T.b * b * c+c+* * b b b @+n+l 6+n S. ",
+" P.{ q+q+q+q+q+m { m 4.4.9 9 m+0 0 +2 2 2 ! h 8+8+8+] U U U n n n+l @+@+b @+b * !.* * * * * z @+l @+l n U.F ",
+" . J { { J J q+m m 4.@.9 9 9 m+^. + +2 p+! h .+8+8+E+U S.y+n n l l l l @+@+b b b b b b b b @+@+l l n+n n ",
+" J J J q+q+m { m D+9 9 9 m+0 m+ +2 2 2 ! h h .+8+8+E+E+S.n n n >+l @+@+@+@+b b b b b b b @+T.l 6+l T ",
+" . . q+J J q+{ m m 4.@.9 m+m+m+0 +2 p+! ! .+8+] ] ] U :.y+n n l l l @+@+@+@+b b @+@+@+@+@+@+n+n n ! ",
+" . J J q+q+q+m 4.4.@.9 9 m+0 ^. +2 2 2 h h .+8+8+E+] U S.y+n n n l l l @+@+@+@+@+@+@+@+n+n+l n % ",
+" . . J J { q+m 4.@.9 9 m+0 ^. + +2 2 2 h h 8+.+H E+U U S.n >+n >+l l l l l @+@+l n+l >+n 6+! ",
+" . q+q+q+m D+4.4.@.9 9 m+0 m+ + +2 h ! h .+8+] ] ] U U S.S.n n n n l l n+l n+n+l n+n+n % ",
+" . P.J m m D+4.@.9 9 m+m+^. + +2 2 ! h .+h 8+] H E+E+U S.n n >+n n l 6+>+n+n n n n V. ",
+" q+J { 4.4.4.@.9 9 m+0 ^.2 +2 2 ! ! .+.+8+] ] U U U S.S.n n 6+n n n n n 6+T ",
+" . u+{ 4.`.@.9 m+m+m+m+^. +2 2 p+! ! h )+8+8+8+E+E+U E+S.U S.n n S.n U ] ",
+" { m 4.9 9 9 9 m+m+ + + + +2 2 #.)+..8+.+] 8+] E+U E+S.U U U U p ",
+" . J 4.4.9 9 m+m+^.^.2 2 2 ! #.! ! V.8+8+8+] ] E+] E+E+E+E+V. ",
+" D+@.9 9 m+m+^.m+^.2 2 2 ! ! h ! V.8+V.8+] E+# % ",
+" . D+9 y ^. +^.^.2 +2 2 ! h )+h V.! 2 p ",
+" 9 m+m+0 ^.2 2 2 2 2 2 % "};
diff --git a/arts/builder/pics/Synth_DEBUG.xpm b/arts/builder/pics/Synth_DEBUG.xpm
new file mode 100644
index 00000000..4a0a09af
--- /dev/null
+++ b/arts/builder/pics/Synth_DEBUG.xpm
@@ -0,0 +1,319 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"64 64 248 2",
+/* colors */
+" c #8B94BA",
+" . c #525A8C",
+" X c #374482",
+" o c #8992B8",
+" O c #3B457C",
+" + c #354280",
+" @ c #444E92",
+" # c #323E7D",
+" $ c #6E77A4",
+" % c #3F4E8D",
+" & c #2C3877",
+" * c #444C7E",
+" = c #283473",
+" - c #253070",
+" ; c #525985",
+" : c #242E6F",
+" > c #333E81",
+" , c #5F6995",
+" < c #1F2A6A",
+" 1 c #1E2869",
+" 2 c #949DBF",
+" 3 c #2D387B",
+" 4 c #7E88B0",
+" 5 c #2F3973",
+" 6 c #2B3679",
+" 7 c #575F8D",
+" 8 c #555D8B",
+" 9 c #40487D",
+" 0 c #4D568D",
+" q c #4F5785",
+" w c #44538E",
+" e c #555FA2",
+" r c #1F2A6D",
+" t c #1E286C",
+" y c #8E97BC",
+" u c #283279",
+" i c #253076",
+" p c #364380",
+" a c #8289B0",
+" s c #2F3B79",
+" d c #404B8D",
+" f c #2E3978",
+" g c #4F599F",
+" h c #565F92",
+" j c #404C83",
+" k c #2F386F",
+" l c #1A2361",
+" z c #384585",
+" x c #364383",
+" c c #303D7D",
+" v c #2F3B7C",
+" b c #2E3B7B",
+" n c #1D2767",
+" m c #505E99",
+" M c #283375",
+" N c #384388",
+" B c #374187",
+" V c #4E598D",
+" C c #384174",
+" Z c #42508B",
+" A c #1E296B",
+" S c #1D276A",
+" D c #6C739D",
+" F c #2A357A",
+" G c #7983AD",
+" H c #3A4883",
+" J c #263176",
+" K c #676F98",
+" L c #28326E",
+" P c #242F74",
+" I c #9EA5C4",
+" U c #232D73",
+" Y c #42508E",
+" T c #41508D",
+" R c #3D4C89",
+" E c #2D3876",
+" W c #374483",
+" Q c #273270",
+" ! c #38417A",
+" ~ c #313E7D",
+" ^ c #5D6591",
+" / c #5C6390",
+" ( c #323B74",
+" ) c #2A3676",
+" _ c #354084",
+" ` c #232E6F",
+" ' c #757CA5",
+" ] c #1F2A6B",
+" [ c #323D77",
+" { c #1E286A",
+" } c #1C2668",
+" | c #9299BE",
+". c #293478",
+".. c #273276",
+".X c #3E467C",
+".o c #4C588D",
+".O c #222C71",
+".+ c #3A4278",
+".@ c #212C70",
+".# c #202A6F",
+".$ c #1F2A6E",
+".% c #1E286D",
+".& c #363E74",
+".* c #1D286C",
+".= c #353E73",
+".- c #3E4D89",
+".; c #3D4B88",
+".: c #7E85AA",
+".> c #2B3573",
+"., c #7C83A8",
+".< c #394784",
+".1 c #384583",
+".2 c #646C97",
+".3 c #3A447B",
+".4 c #636A96",
+".5 c #868FB5",
+".6 c #6872B2",
+".7 c #212B69",
+".8 c #3F498D",
+".9 c #7981A8",
+".0 c None",
+".q c #5B66A5",
+".w c #212B6C",
+".e c #1F296A",
+".r c #414E88",
+".t c #1D2768",
+".y c #2C377A",
+".u c #1C2567",
+".i c #3B458C",
+".p c #3D4A84",
+".a c #293577",
+".s c #3C4883",
+".d c #2C3670",
+".f c #4B5A95",
+".g c #646E9D",
+".h c #3A4681",
+".j c #394680",
+".k c #475691",
+".l c #172058",
+".z c #485292",
+".x c #323D83",
+".c c #5662A3",
+".v c #44528E",
+".b c #34407B",
+".n c #5D6696",
+".m c #303B81",
+".M c #1F296D",
+".N c #42508C",
+".B c #2E397F",
+".V c #414E8B",
+".C c #313C78",
+".Z c #1D276B",
+".A c #404E8A",
+".S c #3F4E89",
+".D c #596292",
+".F c #6D759F",
+".G c #2A357B",
+".H c #515F94",
+".J c #3D4A87",
+".K c #3B4885",
+".L c #485695",
+".P c #5F6A9B",
+".I c #35427F",
+".U c #242E6B",
+".Y c #707BA5",
+".T c #4C5485",
+".R c #232C6A",
+".E c #4A5283",
+".W c #303C7A",
+".Q c #202A67",
+".! c #343D74",
+".~ c #2D3877",
+".^ c #1D2664",
+"./ c #3C4A89",
+".( c #454E7E",
+".) c #2B3675",
+"._ c #68719D",
+".` c #293473",
+".' c #283272",
+".] c #485498",
+".[ c #39457C",
+".{ c #354282",
+".} c #334080",
+".| c #424E92",
+"X c #323C7F",
+"X. c #424F88",
+"XX c #6973A1",
+"Xo c #2A3477",
+"XO c #414A7D",
+"X+ c #4A5993",
+"X@ c #495792",
+"X# c #253072",
+"X$ c #43518C",
+"X% c #333F79",
+"X& c #6F78A0",
+"X* c #465085",
+"X= c #1C2669",
+"X- c #2C367C",
+"X; c #2F3975",
+"X: c #515E93",
+"X> c #2D3773",
+"X, c #5E69AD",
+"X< c #4F5C91",
+"X1 c #7882AC",
+"X2 c #29336F",
+"X3 c #232E73",
+"X4 c #888FB5",
+"X5 c #49548B",
+"X6 c #202A70",
+"X7 c #868DB3",
+"X8 c #333F7C",
+"X9 c #404F8C",
+"X0 c #3F4D8B",
+"Xq c #2F3B78",
+"Xw c #4D579C",
+"Xe c #3B4787",
+"Xr c #26316F",
+"Xt c #49568E",
+"Xy c #252F6E",
+"Xu c #344180",
+"Xi c #374279",
+"Xp c #57649F",
+"Xa c #4A5385",
+"Xs c #303D7C",
+"Xd c #828BB2",
+"Xf c #30397C",
+"Xg c #646EAF",
+"Xh c #2E397A",
+"Xj c #464F81",
+"Xk c #313A73",
+"Xl c #3C498B",
+"Xz c #343F83",
+"Xx c #6D77B1",
+"Xc c #222D6E",
+"Xv c #222B6E",
+"Xb c #1D2769",
+"Xn c #546195",
+"Xm c #263175",
+"XM c #757FA8",
+"XN c #455490",
+"XB c #202B6F",
+"XV c #43528E",
+"XC c #1F296E",
+"XZ c #323E7A",
+"XA c #1E296D",
+"XS c #505C9E",
+"XD c #3B4886",
+"XF c #2A3472",
+/* pixels */
+".0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0X#... u u i PX3.O.# A n.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.> # N B.x.m.B.G u u i P U.O.OX6X6.M l l.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0Xe @.|.i N B.x.m.B.G u u i P U.O.OX6.#.#XCXC t l.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 p g g.] @.|.i N B.x.m.B.G u u i P U.O.OX6.#.#XCXA.%.% tX= l.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.c.c e g.].|.8.i N _.x.BX-.G u.. i P U.O.OX6.#.$XCXA.% t t.*.Z }.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.0.0.0.0.0.0.0XwX,X,.c gXw.].|.8.i N _.x.BX-.G u J i P U.O.OX6.#XCXCXA.% t t.Z.Z.ZX= l.0.0.0.0.0.0.0.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.0.0.0.0.0.0.6.6X,X, e gXw.].|.8.i BXz.m.BX-.G u J i P U.O.@X6.#XCXCXA.% t t.Z.Z.Z S S.u.0.0.0.0.0.0.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.0.0.0.0X$.6.6XgX,.c e g.].].|.8.i B.x.m.BX- F u i iX3.O.O.@.#.#XC.M.% t t.*.Z.Z S S S S }.l.0.0.0.0.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.0.0.0X<Xx.6.6X,X,.c gXw.].|.8.i N B.x.m.B.G u u i PX3.O.O.@.#.$XCXA.% t t.Z.Z.Z S S SXbX= }.l.0.0.0.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.0.0X:Xx.6.6XgX,.q e g.].].|.8.i N _.x.BX-.G u.. i P U.O.OX6.#.$XCXA.% t t.Z.Z.Z S S SXbX=X= }.l.0.0.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.0X<Xx.6.6XgX,.c eXw.f.z @.8.i N _X Xh.yXo. .. J i P U.O.@XB.#XC.MXA t t.Z.Z.Z S S SXbXbX=X= }.u.l.0.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0X$Xx.6.6XgX,.q eXS m.PXn.f d.i BXz v.C E.) M J i iX3.OX6.#.#.$XC.M.% t t t t.Z.Z S S SXbXbX= } }.u.l.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.6.6.6XgX,.q.cXp.PXXX1.P.f.8 B.I ! 9Xa *.3.>X#X#X3.OXv.w.7.e t.%.MXAXA.Z.Z.Z.Z t t t t { SXbXb } }.u.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.6.6.6XgX,.q.c.qXg.5 | I $ 0.J x 9 8._ a.F /.& -X#.OXc.R.U L.7.M.M.#.#.#.M.M.M.M.M r.w r.M A t { {Xb.t.u.0.0.0.0.0.0",
+".0.0.0.0.0Xw.6XgX,X,.q.c eXS.g.5 |.gX5X5Xa . ^ ,.2.4.4.XXFXF.>.&XO C.=.U.w.wXv.w.w.UX2 LXr.w.w.w.w ] ] 1.Q n n.t.t l.0.0.0.0.0",
+".0.0.0.0.0X,X,X,X,.q eXSXwXw.nX7X7Xd.n.V h.Y.F K 7.T.D._X*X;.3X*.2X7 ,.(X2XcXc :XrX2.&Xj 9 !X2XcXcXc.w.7.7.R.7.Q.Q 1 n.0.0.0.0.0",
+".0.0.0.0.cX,X,.c.c e g g.].z.n oX1XX.n 0.P G ^ * * *.T 8 9X; OX5.F 2 / CX2 -.' E !Xj 8 K 8XO.d :.U.U L k CXO C k.U.Q.Q 1.0.0.0.0",
+".0.0.0 p.c.c e e g gXwXw @ d h $ h hXnXXX7 qX;.!.3 9.(.+X;.X ..9 I 7 kX2X# E.s.D.9X7 |._XaXkXr L.d C ;.2.,.4 ; k.7.7.w.^.0.0.0",
+".0.0.0 g e g g gXwXw.].] dX5.nX4.gX5 h.g._.F *.).&Xj.X CX>.'.&.T.2.:.( L Q M E.[.D aX7 , 9X%X>.+.E ; ^.2.F K.4.+ L.R.R <.0.0.0",
+".0.0.0 g gXwXw.].].] @.|.r hXXXd.n.J hXM._ /.+.`.& 7.XX> QX#.dXO ; K C L.' M E.b 7X4X7X7 7 !.3.[ 7.:X& K.2.4 D.9.E k L.UXc.0.0.0",
+".0.0Xe.].].].].].|.|.8.8X..g.gXXX5 X VX1 D / * ( 9 7.+X2.' M k 8 ; q k.'.`. .) [ .X7.:XM ..3 j . K.: ^Xj.(.( 7X&.( k LXyXy.Q.0.0",
+".0.0 @ @.|.|.|.|.8.8.i NX5XM.g h.p #Xa.:.F.n VXj.T 8.&.'.' M C D ; C.d.a 6.y.)X;.TX7XM._.T.[ V.FXM.: q.!.!.=.E D * k LXr -Xy.0.0",
+".0.>.|.|.8.8.8.8.i.i N _.s .X*.sX8XfXj.YX&.F.4.D.T * 5 M.'X2.+X&.EXk.d 6.y 3X;.+ 7X7.F.D.T j.D.9X& K * 5.=.+.E ^.X.d L.'.' Q.Q.0",
+".0 #.i.i.i.i.i.i N N _ >.}.hX8Xf 3.y.X.g.Y a GXM 0X%.>Xo.' k.( '.(.d.) 3 3 b [Xj.4 o K.TX5X*.2X4.F 7.+X;.=.(.T ; C.dXF.).` =.Q.0",
+".0 N N N N N B B B _.x.mXfXf 3.y 6. X; 9Xj . qXa !X>Xo. .> C.( / CX> & v b bXi /.FXd.n jX*X5._ y D q.+Xq.+ ;.TXj.=X>.>.).).>.'.0",
+".0 B B B _ _Xz.x.x.x.m.B.BX-X-X- F.. MXFX>.! [.C.). . F 5 *.(.(.! f b cXs.WXOX&XM 4 h.h jX5.F I KXj.+.CXO.2 q 9 ( E E f &.).>.0",
+"X#.x.x.x.x.x.m.m.B 3 3.y.G.G F F u..Xm.' Q.'XF.)Xo. F.yX>.&.!.!X; b c > cXZXjX&XXXXX5.j.pXa._ I ' 7Xa j 8.F q.+ (Xq f f f &.)XF",
+"...m.m.m.B.B.B.B.y 6 6 &. u u u.. J.. J.... u. F.y.y 3.~X; f f vX >Xz #.bXa.F.P.D j p.hXj K I a $.g.n D.9 qXi [Xs.W.W s f.~.>",
+". .BX-X-X-.y 3 s ! 9 O !.)..Xm i PX#X#.' M.. u F.y 3 3 3XhXh sXsX >Xz _.}.I.hX5X*.r.h W.h j ^Xd 4 GXM $.F._XjXiX% #XsXs.W s f.)",
+" u.G.G F F.yX8 j.D '.nX5.CX# PX3X#.'.`.> = M. 6.y.y 3.B v c c >Xz _.{ x.{ + X.h.<.1.<.<.< j ..Y.: X4X7.F.D j.b.bXu.} #XZ.W s E",
+" u u u... 3.s h.F a.n jX>X# -XF (XO OXi.> MXo 6 &.~ fXq.WXs ~ > _ x x z W W W.<.<.<XeXeXD.K.r V 7 , ,.n VX*.h p + +Xu.} # ~.W f",
+" i u i..Xo sX5 'Xd | ^ !XF :.> 9 7 ' , q ( M.a 6 E.b ! O !X8 #Xz x x z zXeXeXeXeXl././Xl././.p.p jX*X*X* j.s.1.1 X X +.I.} #Xs s",
+" P i P iXm M *.Y.9.:.E.dXr :.! 8 DX7 ^ *Xk.`X>X%XO . 8.DXa.s.I.{ x z z z z.<XD././XlX0X0 RX0.;.J.p.p j.J.K.K.<.<.<.1 X p.I.}X8.W",
+"X3 P P P U :.+.F.F.F.3 : :Xy 9 $X7 I ^.+ 5.>Xi .._ 4X7 o D ..s.{ W z z H.K.p.J RXl % % % % %X9X0X0X0 R R R R./XDXD.<.1 X p.IX8Xs",
+".O U U UXcXy 9 '.4 .Xk.wXc.U O.F.,X7 q 5XkXk.E D.F $ GX7.F hX..<.K.pX..o.o VX..-X0 % % %.A.S.S.S.SX0.AX0X0X0 R.;.JXD.<.1 X +.I #",
+".#.O.O.O.w L.(.:.D.X L r.wXc.+ DX& 'XOX2 (XO ,X4 '.P.YXd.F.n.o.rX5X<.P.9XM.YX: Z.V Y Y Y.AX.X.X..A.V T T TX9X0 R.;.J.K.<.1 X.IX8",
+" A.O.OX6.U.& ; '.EX>.U.M.wXyXO ' K / CX2 C 7 ^.2 8X*.nXMXX.PXnXtXn.gXM 4XM.HX$ Z.NX.X5 V.D.D.D.o Z Z Y T TX9X0 R.;XD.K.<.1 p.b",
+" nX6.#.#X2 q ^X&.X.U ]XA.wX2Xa.: ^.(Xk LXO., ^.T 9.[ 0 $XX.g.PXn $Xd | I GXn.vX$ Z.o ,X&.5.5 o.2.oX$XV YXV.NX9X0 R.JXD.<.h p.b",
+".0X6.M ]Xk ^ / 7Xk < t t.U ! 8.: ;.= k.d.(., ;.3 O.j V $.g.nX:Xt.g a y IXdXXXn.kX< h.gXM 4 o y.Y.n.o w wXNXV.N.VX0 R.J.K.< X.0",
+".0.M A.7.+ D 8XO L t t tX2.T.4.,.(X2 k.= ;.,.E [ !.sX< $.PX:X5.r.D 4 I G.PXnX<.P GX4 2 | y 2 a $X:.kXNXNXNXV.N.A.-.JXD.<.j.0",
+".0 l.e.7 k.T.+.d.7.Z t.eXk / ,.4.+ L.=.( ^.: q.b 9X5.n $ hX$.r.J hXd o |.YXn.nXp.Y .5 a.YXXX1 4.YXn.kXN.kXN.vX$X9X0 R.J.K [.0",
+".0 l {.7 L.! L.w {.Z 1.7.+ D ^ ;XkXr C ,X&.: . !X*.n.gXX V.p.s.s 8X7.5 oXXX:.P $.5 IXd $.PXn._XdX1.YXnX@.k.k.kXNXV.N.A.-.J.KX%.0",
+".0.0XbXb.e.R.e 1 S t 1.7.! 7.( CX2Xy.= / D.9.D j 8XX.P.DX* H.p j ^ X7 4.g.H.g G o IX1.PXn.H.g oXMXXXnX+X@.L.kXN.vX$.V.-.J.s.0.0",
+".0.0 l.Z S S S.Z t t.t.w kXj.! LXyXy.! 7 K.9.4 ..2.9.nX5.p.<.pXa K 2Xd.Y.PXnXX.5 y 2 $Xn.H.H.g yXM.P.H.fX+.LX@.k wXV.V.- RXi.0.0",
+".0.0.0.Z.Z.Z.Z.*.Z.ZXb 1.RX2.U.UXy - k.( / '.F K.2 ,Xa.s.1.< j 7X& yX1.g.PXn $ y y ._.HXnXn $ y.Y.P.H.f.f.fX@.kXNXV.N.A R.0.0.0",
+".0.0.0X=.*.Z.Z.Z.Z.ZX=X= 1.e.wXc -X#XF C q.F.9 a.2.T.h p X.<X*.F G $.n.nXpXM 2 a.P.HXn.P.Y y $.n.H m.f.fX+.kXNXV.N.A.p.0.0.0",
+".0.0.0 l.Z.Z.Z.Z.Z.ZX=X=Xb A.wXcX#X# Q.d.+Xj.T 8Xj O ! p X.<X5 ' GXd.gX<.HXnXM I yXd.gXn.P $X1.5XXXp m m.f.fX+.kXN.v.N.A.j.0.0.0",
+".0.0.0.0 }.Z.Z S S S S S t rXc `X#X#.' =X> (X% !.bX8Xu x X.h V G G G.nXtX+X<.Y I |XdXXXpXXXd 4 4.gXn m m.f.fX+.kXNXV.N.J.0.0.0.0",
+".0.0.0.0.0X= S S S S S S t.wXc `X#X#.' =.>X>X;.CXZ #Xu x X.hX*.P.P.PX<.vXtX< $ I 2 oX1 $X1 o 4XM.P m m m.f.fX@.kXNXV.S.0.0.0.0.0",
+".0.0.0.0.0 l S S S SXb S t rXc `X#X# M.a ) &Xh s c.} + x X.h j.o V V.vX$XtX<XX 2 2 | y y GXXXp m m m.f.fX@.kXNXV.h.0.0.0.0.0",
+".0.0.0.0.0.0.u SXbXbXbXb {.M.wXcX#X# MXo ) 6 f b c.}Xu x X.1.s.rX. Z Z.N wX@.P 4 o | y o.5.Y.PXn m.f.f.fX+.kXN w.r.0.0.0.0.0.0",
+".0.0.0.0.0.0.0 }X=X=X=Xb S A.wXc -X# M M ) &Xh v c.}Xu.{ x W.<.K.J RX0X9XVXNXn $ 4 | y .5 4XXXp m.f.f.fX+X@.kXN Z.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.l }X=X=X=Xb t.wXc `X#.' = ) 6 f b c #.} + x W zXD./ RX0 T.NXVX@Xn.P $ $.YXXXXXp m.f.f.fX+X@.kXNX$.[.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.0.l } } }Xb {.w.w `X#X# M ) & fXhXs ~.}Xu x W.<XD./ RX0X9X9 Y wXtX<XnXnXpXnXn m.f.f.fX+X@.kXNX$.3.0.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.0.0.l.u } } { A.wXc -X# = M ) & f bXs #.} + W z.<XD./ RX0X9.NX$ w.kXtX+X<.f.fX+X@X@X@.k.kXNX$.+.0.0.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.0.0.0.l.u }Xb.e.wXc :X#.' = ) & f sXs #.}Xu x W.<XDXD.; RX0X9.NXV.vXNXNXN.k.k.k.k.k.kXNXNX$.3.0.0.0.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.0.0.0.0.l.u.t A ].w ` -X# = ).) & f bXs #.} + x.1.<XD.J.; RX0.V.N.NX$XVXVXNXNXNXNXNXN w Z.[.0.0.0.0.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.0.0.0.0.0.0.u 1 ].wXc :X#.'.`.) & f b.W ~ #Xu + X.1.<XD.J.; RX0.AX9.NX$XVXVXV.vXVXVXV.r.0.0.0.0.0.0.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.0.0.0.0.0.0.0 l 1.wXc ` - Q =.`.) & f sXs #.}Xu + X.1.<.KXD.J R.-X0.A.V.V.N.N.N.N.S.h.0.0.0.0.0.0.0.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 1.wXc :Xr.'.`.).) f f.WXs #.}.I p X.1.<.KXD.J.J R.-.-.-.A.A.A.J.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.^.7 ` - Q.'.`.) & f s.WXZ #.}.I p X.1.<.<.KXD.J.J.J R R.p.j.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.QXy Q =.>.) & f s.W ~ #.}.I + X.1.h.<.<.K.K.sXi.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.Q.Q.'.>.).~ f s.WXsX8X8.I.I p p X.j [X%.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",
+".0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0XF.>.) E f s.WXs #X8.b.b.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0"
+};
diff --git a/arts/builder/pics/Synth_DIV.xpm b/arts/builder/pics/Synth_DIV.xpm
new file mode 100644
index 00000000..c424ce7a
--- /dev/null
+++ b/arts/builder/pics/Synth_DIV.xpm
@@ -0,0 +1,1350 @@
+/* XPM */
+static char * Synth_DIV_xpm[] = {
+"64 64 1283 2",
+" c None",
+". c #253073",
+"+ c #273276",
+"@ c #293378",
+"# c #283278",
+"$ c #263077",
+"% c #252F75",
+"& c #232E74",
+"* c #212B71",
+"= c #202A6E",
+"- c #1E296A",
+"; c #1C2667",
+"> c #2B3671",
+", c #313D7B",
+"' c #384288",
+") c #353F87",
+"! c #323D83",
+"~ c #2F3A80",
+"{ c #2D377D",
+"] c #2A357B",
+"^ c #29347A",
+"/ c #273177",
+"( c #253076",
+"_ c #232D72",
+": c #222C72",
+"< c #212C71",
+"[ c #202B70",
+"} c #202A70",
+"| c #1F296E",
+"1 c #1B2463",
+"2 c #3C4988",
+"3 c #434D95",
+"4 c #414C93",
+"5 c #3D488D",
+"6 c #202A6F",
+"7 c #1F2A6F",
+"8 c #1E286C",
+"9 c #37437E",
+"0 c #4F59A0",
+"a c #485399",
+"b c #3B458D",
+"c c #283379",
+"d c #242E74",
+"e c #1E296D",
+"f c #1E286D",
+"g c #1C2669",
+"h c #5862A8",
+"i c #555FA6",
+"j c #3F4B90",
+"k c #1F2A6E",
+"l c #1D276B",
+"m c #1C2668",
+"n c #4B5A97",
+"o c #616CB3",
+"p c #5C67AF",
+"q c #4B579B",
+"r c #2E397F",
+"s c #263177",
+"t c #253075",
+"u c #6872B4",
+"v c #343F84",
+"w c #222C71",
+"x c #212B70",
+"y c #1B2666",
+"z c #1B2366",
+"A c #45538B",
+"B c #445194",
+"C c #2B367B",
+"D c #263076",
+"E c #232E73",
+"F c #1D266B",
+"G c #1E266C",
+"H c #1A2264",
+"I c #1D276A",
+"J c #171F57",
+"K c #4A5996",
+"L c #273277",
+"M c #242F74",
+"N c #1A2664",
+"O c #1E296C",
+"P c #1D296B",
+"Q c #1E2A6C",
+"R c #354186",
+"S c #2C377B",
+"T c #202B6F",
+"U c #6672BC",
+"V c #666EB4",
+"W c #5E6AB4",
+"X c #5A66AC",
+"Y c #565EA4",
+"Z c #525EA4",
+"` c #4E5AA2",
+" . c #4A569C",
+".. c #465296",
+"+. c #424E94",
+"@. c #3E4A8E",
+"#. c #3E468C",
+"$. c #36428C",
+"%. c #363E84",
+"&. c #626EB4",
+"*. c #5E66AC",
+"=. c #5662AB",
+"-. c #525AA4",
+";. c #424A94",
+">. c #3A4A8C",
+",. c #3A468C",
+"'. c #323E84",
+"). c #273278",
+"!. c #4A529C",
+"~. c #464E93",
+"{. c #3A428C",
+"]. c #313983",
+"^. c #2D387E",
+"/. c #2A3479",
+"(. c #263176",
+"_. c #303A7B",
+":. c #353D7B",
+"<. c #303A7A",
+"[. c #252F74",
+"}. c #212A6F",
+"|. c #1F2B6F",
+"1. c #212A6E",
+"2. c #1E286A",
+"3. c #1D2769",
+"4. c #303B81",
+"5. c #2C367E",
+"6. c #293479",
+"7. c #232E72",
+"8. c #313C7C",
+"9. c #475088",
+"0. c #606998",
+"a. c #69709D",
+"b. c #606898",
+"c. c #485189",
+"d. c #303979",
+"e. c #212D72",
+"f. c #222E74",
+"g. c #222A6F",
+"h. c #1E2868",
+"i. c #1A235E",
+"j. c #5A62AC",
+"k. c #35418B",
+"l. c #2F3B80",
+"m. c #485088",
+"n. c #797FAA",
+"o. c #A3A7C3",
+"p. c #B3B6CD",
+"q. c #A3A8C3",
+"r. c #797EA8",
+"s. c #273176",
+"t. c #222B70",
+"u. c #212B6C",
+"v. c #20296C",
+"w. c #4E569C",
+"x. c #323A80",
+"y. c #29357A",
+"z. c #283178",
+"A. c #263075",
+"B. c #263171",
+"C. c #323E7A",
+"D. c #60699A",
+"E. c #A4A9C3",
+"F. c #D3D5E1",
+"G. c #E0E2EA",
+"H. c #D2D5E2",
+"I. c #A5A9C3",
+"J. c #626997",
+"K. c #313D7E",
+"L. c #242E73",
+"M. c #262E73",
+"N. c #262F73",
+"O. c #263273",
+"P. c #222E6C",
+"Q. c #223264",
+"R. c #222D6E",
+"S. c #4E5AA4",
+"T. c #4A5A9C",
+"U. c #303C81",
+"V. c #2D397E",
+"W. c #2C357D",
+"X. c #273378",
+"Y. c #263276",
+"Z. c #252F76",
+"`. c #373F7D",
+" + c #6C729E",
+".+ c #B4B7CE",
+"++ c #E0E2EB",
+"@+ c #EBECF2",
+"#+ c #B4B8CE",
+"$+ c #6C73A2",
+"%+ c #394278",
+"&+ c #243073",
+"*+ c #263277",
+"=+ c #263078",
+"-+ c #26327C",
+";+ c #252D72",
+">+ c #253071",
+",+ c #242F6E",
+"'+ c #232E6E",
+")+ c #46529C",
+"!+ c #425294",
+"~+ c #354182",
+"{+ c #2B357D",
+"]+ c #273178",
+"^+ c #263175",
+"/+ c #242F73",
+"(+ c #333E7D",
+"_+ c #636A9B",
+":+ c #A5A9C2",
+"<+ c #D3D5E2",
+"[+ c #646B9A",
+"}+ c #364181",
+"|+ c #26336C",
+"1+ c #283273",
+"2+ c #2A3674",
+"3+ c #26366C",
+"4+ c #2A3274",
+"5+ c #232E6C",
+"6+ c #212B69",
+"7+ c #464E94",
+"8+ c #2F3B81",
+"9+ c #2D3679",
+"0+ c #2B357C",
+"a+ c #273272",
+"b+ c #273274",
+"c+ c #273076",
+"d+ c #243074",
+"e+ c #253070",
+"f+ c #2D3779",
+"g+ c #4A538C",
+"h+ c #7C82AB",
+"i+ c #A5AAC5",
+"j+ c #B5B9CE",
+"k+ c #A5ABC5",
+"l+ c #7E84AC",
+"m+ c #4E568C",
+"n+ c #2E3A7A",
+"o+ c #2B3779",
+"p+ c #28337A",
+"q+ c #293574",
+"r+ c #2B377D",
+"s+ c #2A367C",
+"t+ c #2A327C",
+"u+ c #293474",
+"v+ c #273273",
+"w+ c #273271",
+"x+ c #3E4A94",
+"y+ c #323A84",
+"z+ c #2E3A7F",
+"A+ c #2A3679",
+"B+ c #2A3579",
+"C+ c #263470",
+"D+ c #263274",
+"E+ c #273377",
+"F+ c #273371",
+"G+ c #27346F",
+"H+ c #293475",
+"I+ c #394382",
+"J+ c #4F588C",
+"K+ c #666F9B",
+"L+ c #7279A3",
+"M+ c #686F9F",
+"N+ c #50598E",
+"O+ c #3C4681",
+"P+ c #2D3777",
+"Q+ c #2D3976",
+"R+ c #2E397B",
+"S+ c #2D3775",
+"T+ c #2B3873",
+"U+ c #2F3B77",
+"V+ c #30397E",
+"W+ c #2F3775",
+"X+ c #2A386E",
+"Y+ c #2E3A7D",
+"Z+ c #293579",
+"`+ c #293575",
+" @ c #293473",
+".@ c #263170",
+"+@ c #2A357C",
+"@@ c #2A3379",
+"#@ c #293278",
+"$@ c #273279",
+"%@ c #2A3378",
+"&@ c #283377",
+"*@ c #283378",
+"=@ c #2C3779",
+"-@ c #303B7E",
+";@ c #3A4482",
+">@ c #3D4781",
+",@ c #3D4683",
+"'@ c #323E80",
+")@ c #2D397A",
+"!@ c #2E3B7A",
+"~@ c #2E387D",
+"{@ c #2F3B7D",
+"]@ c #2E3C7A",
+"^@ c #2F387D",
+"/@ c #2F3B7F",
+"(@ c #313D7F",
+"_@ c #303E7E",
+":@ c #2F387C",
+"<@ c #2E367C",
+"[@ c #2C3877",
+"}@ c #2C3878",
+"|@ c #2C3777",
+"1@ c #2A3676",
+"2@ c #2B3571",
+"3@ c #2B3478",
+"4@ c #283477",
+"5@ c #2A3375",
+"6@ c #2A3474",
+"7@ c #2A3576",
+"8@ c #2B3676",
+"9@ c #2C3778",
+"0@ c #2C387A",
+"a@ c #2C397B",
+"b@ c #2E387B",
+"c@ c #2E3A7C",
+"d@ c #2E3B7B",
+"e@ c #2F3C7C",
+"f@ c #2F3D7C",
+"g@ c #303D7D",
+"h@ c #303D7E",
+"i@ c #313F7E",
+"j@ c #313E80",
+"k@ c #313D7D",
+"l@ c #313E7F",
+"m@ c #324081",
+"n@ c #323E81",
+"o@ c #2E3E7C",
+"p@ c #2D3A79",
+"q@ c #2D3978",
+"r@ c #283677",
+"s@ c #2A347C",
+"t@ c #263372",
+"u@ c #283479",
+"v@ c #283578",
+"w@ c #2B377B",
+"x@ c #2F3C7D",
+"y@ c #2F3C7E",
+"z@ c #303C7D",
+"A@ c #313E7E",
+"B@ c #323E7E",
+"C@ c #333F7F",
+"D@ c #323F7E",
+"E@ c #333F80",
+"F@ c #344081",
+"G@ c #333F7E",
+"H@ c #33417F",
+"I@ c #354183",
+"J@ c #34407E",
+"K@ c #2F3B7E",
+"L@ c #303980",
+"M@ c #2F3C7A",
+"N@ c #2A3279",
+"O@ c #293478",
+"P@ c #2B357A",
+"Q@ c #2B3579",
+"R@ c #2D397B",
+"S@ c #2E397C",
+"T@ c #2E3B7D",
+"U@ c #2F3B7B",
+"V@ c #2F3D7F",
+"W@ c #303D7F",
+"X@ c #313E7D",
+"Y@ c #313F81",
+"Z@ c #334082",
+"`@ c #334080",
+" # c #344183",
+".# c #354284",
+"+# c #35417E",
+"@# c #344284",
+"## c #364386",
+"$# c #364385",
+"%# c #364282",
+"&# c #354383",
+"*# c #344180",
+"=# c #2B3776",
+"-# c #2B3675",
+";# c #283370",
+"># c #29337C",
+",# c #273375",
+"'# c #293477",
+")# c #283474",
+"!# c #2C377C",
+"~# c #2C3875",
+"{# c #2E3A7E",
+"]# c #303C7F",
+"^# c #303D7C",
+"/# c #344181",
+"(# c #344281",
+"_# c #354280",
+":# c #344282",
+"<# c #34427F",
+"[# c #374280",
+"}# c #364482",
+"|# c #374487",
+"1# c #374582",
+"2# c #374586",
+"3# c #384485",
+"4# c #344080",
+"5# c #2D3876",
+"6# c #26327B",
+"7# c #273477",
+"8# c #2A3475",
+"9# c #2A3775",
+"0# c #2C3879",
+"a# c #2E3A78",
+"b# c #2F3B7C",
+"c# c #303C7B",
+"d# c #323E7F",
+"e# c #323F80",
+"f# c #333F81",
+"g# c #334180",
+"h# c #364283",
+"i# c #364382",
+"j# c #374385",
+"k# c #384584",
+"l# c #354482",
+"m# c #384588",
+"n# c #384587",
+"o# c #394585",
+"p# c #394587",
+"q# c #394686",
+"r# c #394687",
+"s# c #374483",
+"t# c #354382",
+"u# c #354282",
+"v# c #2B3775",
+"w# c #2A337C",
+"x# c #283570",
+"y# c #2D377C",
+"z# c #2B3679",
+"A# c #2D3A7C",
+"B# c #323F81",
+"C# c #344182",
+"D# c #334181",
+"E# c #344382",
+"F# c #354281",
+"G# c #364484",
+"H# c #364583",
+"I# c #374482",
+"J# c #384585",
+"K# c #374684",
+"L# c #374685",
+"M# c #374480",
+"N# c #3A4686",
+"O# c #394885",
+"P# c #3A4988",
+"Q# c #3A4A8A",
+"R# c #3A4989",
+"S# c #3A4786",
+"T# c #2F3B78",
+"U# c #2B3674",
+"V# c #343F80",
+"W# c #3B4384",
+"X# c #424D89",
+"Y# c #454F8A",
+"Z# c #454E89",
+"`# c #47518A",
+" $ c #47508A",
+".$ c #475190",
+"+$ c #495387",
+"@$ c #4B5691",
+"#$ c #4C5791",
+"$$ c #4B568C",
+"%$ c #4E5990",
+"&$ c #515B93",
+"*$ c #505B92",
+"=$ c #525D91",
+"-$ c #535E95",
+";$ c #535E94",
+">$ c #556094",
+",$ c #556095",
+"'$ c #556096",
+")$ c #566194",
+"!$ c #576296",
+"~$ c #566297",
+"{$ c #566294",
+"]$ c #586397",
+"^$ c #576496",
+"/$ c #576499",
+"($ c #576497",
+"_$ c #5A6497",
+":$ c #5A6599",
+"<$ c #5A659B",
+"[$ c #5A6598",
+"}$ c #586599",
+"|$ c #586498",
+"1$ c #576297",
+"2$ c #535E92",
+"3$ c #525D92",
+"4$ c #4F5992",
+"5$ c #44528C",
+"6$ c #3F4C85",
+"7$ c #384282",
+"8$ c #4D568F",
+"9$ c #656C9D",
+"0$ c #767DA9",
+"a$ c #7F85AE",
+"b$ c #8087AF",
+"c$ c #8086AF",
+"d$ c #8489B0",
+"e$ c #848AB0",
+"f$ c #848BB0",
+"g$ c #848BAE",
+"h$ c #858BB3",
+"i$ c #868DAF",
+"j$ c #888EB0",
+"k$ c #8B91B2",
+"l$ c #8B91B0",
+"m$ c #8B91B3",
+"n$ c #8D94B6",
+"o$ c #8B92B6",
+"p$ c #8C93B8",
+"q$ c #8B93B6",
+"r$ c #8D95B8",
+"s$ c #8D95B9",
+"t$ c #8D96B7",
+"u$ c #8D95B7",
+"v$ c #8F97BB",
+"w$ c #8D95B6",
+"x$ c #8E96BA",
+"y$ c #8D96B6",
+"z$ c #9197BC",
+"A$ c #9198B8",
+"B$ c #9097BA",
+"C$ c #9199BE",
+"D$ c #9198BE",
+"E$ c #9097BB",
+"F$ c #8F97BA",
+"G$ c #8E97B9",
+"H$ c #8B94B8",
+"I$ c #8991B7",
+"J$ c #8088B0",
+"K$ c #7079A5",
+"L$ c #596497",
+"M$ c #434D87",
+"N$ c #33407D",
+"O$ c #6972A1",
+"P$ c #9398BB",
+"Q$ c #ABAFC9",
+"R$ c #B7BAD0",
+"S$ c #B9BDD2",
+"T$ c #BBBED3",
+"U$ c #BBBED4",
+"V$ c #BBBFD3",
+"W$ c #BCBFD4",
+"X$ c #BDBFD6",
+"Y$ c #BCBFD5",
+"Z$ c #BEC1D5",
+"`$ c #BEC2D6",
+" % c #BEC2D5",
+".% c #BFC3D6",
+"+% c #BFC3D7",
+"@% c #C0C3D8",
+"#% c #C0C4D7",
+"$% c #C1C5D8",
+"%% c #C1C5D9",
+"&% c #C2C6D9",
+"*% c #C1C6D9",
+"=% c #C2C6D8",
+"-% c #C1C6D8",
+";% c #C3C7D9",
+">% c #C2C7D9",
+",% c #C1C7D9",
+"'% c #C2C7D8",
+")% c #C3C7DB",
+"!% c #C3C7DA",
+"~% c #BFC5D8",
+"{% c #BDC1D7",
+"]% c #B2B8D0",
+"^% c #9DA3C2",
+"/% c #757EA8",
+"(% c #3A4882",
+"_% c #2B357B",
+":% c #4B538C",
+"<% c #787FA8",
+"[% c #A6ABC7",
+"}% c #C0C3D6",
+"|% c #CBCEDE",
+"1% c #CED0DF",
+"2% c #CDD0DF",
+"3% c #CFD2E0",
+"4% c #D0D2E0",
+"5% c #D0D2E1",
+"6% c #D0D3E1",
+"7% c #D0D3E0",
+"8% c #D2D3E0",
+"9% c #D2D4E1",
+"0% c #D2D5E3",
+"a% c #D3D5E3",
+"b% c #D3D6E4",
+"c% c #D3D6E3",
+"d% c #D3D7E4",
+"e% c #D4D7E5",
+"f% c #D3D7E3",
+"g% c #D4D8E4",
+"h% c #D5D8E5",
+"i% c #D5D8E4",
+"j% c #D5D8E6",
+"k% c #D0D4E2",
+"l% c #C6CADC",
+"m% c #B0B5CD",
+"n% c #848CB3",
+"o% c #596599",
+"p% c #3D4A83",
+"q% c #35427E",
+"r% c #2A347A",
+"s% c #424B86",
+"t% c #696F9E",
+"u% c #9397BA",
+"v% c #AAAEC8",
+"w% c #B6B9CF",
+"x% c #B9BCD1",
+"y% c #BBBED2",
+"z% c #BBBFD2",
+"A% c #BCBFD6",
+"B% c #BDBFD5",
+"C% c #BDC0D6",
+"D% c #BDC1D5",
+"E% c #BFC2D8",
+"F% c #BFC3D8",
+"G% c #C0C4D8",
+"H% c #C2C7DB",
+"I% c #C3C8DB",
+"J% c #C3C9DB",
+"K% c #C2C5DA",
+"L% c #B4B9D0",
+"M% c #9EA4C3",
+"N% c #7680AC",
+"O% c #546195",
+"P% c #3D4B85",
+"Q% c #384583",
+"R% c #323F7C",
+"S% c #323D7D",
+"T% c #495189",
+"U% c #616999",
+"V% c #747AA4",
+"W% c #7C82AA",
+"X% c #7E85AC",
+"Y% c #7F85AC",
+"Z% c #8488AF",
+"`% c #848AAE",
+" & c #848BAD",
+".& c #858BAE",
+"+& c #858DB4",
+"@& c #868DB0",
+"#& c #868DB2",
+"$& c #8990B2",
+"%& c #8991B4",
+"&& c #8B92B8",
+"*& c #8D93B6",
+"=& c #8D94B7",
+"-& c #8D92B9",
+";& c #8D96BA",
+">& c #8F96B8",
+",& c #8D96B8",
+"'& c #9197BE",
+")& c #9197BB",
+"!& c #9199BB",
+"~& c #929ABD",
+"{& c #929CBE",
+"]& c #939DBE",
+"^& c #929ABC",
+"/& c #929BBE",
+"(& c #949DBE",
+"_& c #929ABE",
+":& c #8C95B8",
+"<& c #858EB5",
+"[& c #737EAA",
+"}& c #5D699D",
+"|& c #48538D",
+"1& c #3A4886",
+"2& c #394786",
+"3& c #2C3677",
+"4& c #363D7B",
+"5& c #3B4580",
+"6& c #3F4984",
+"7& c #3F4884",
+"8& c #414A84",
+"9& c #454E86",
+"0& c #455087",
+"a& c #49508B",
+"b& c #495190",
+"c& c #49538B",
+"d& c #495390",
+"e& c #4B558C",
+"f& c #505A92",
+"g& c #515A96",
+"h& c #515C8F",
+"i& c #525D96",
+"j& c #536096",
+"k& c #576095",
+"l& c #57649A",
+"m& c #59659A",
+"n& c #5A659C",
+"o& c #5B6799",
+"p& c #5C689D",
+"q& c #5B679D",
+"r& c #5B689A",
+"s& c #5E699F",
+"t& c #5F6A9F",
+"u& c #5F6C9F",
+"v& c #606E9D",
+"w& c #606D9D",
+"x& c #616CA3",
+"y& c #606C9D",
+"z& c #606CA4",
+"A& c #606DA0",
+"B& c #5F6B9E",
+"C& c #5E6A9D",
+"D& c #5A689D",
+"E& c #59669B",
+"F& c #556198",
+"G& c #4E5C94",
+"H& c #485690",
+"I& c #3E4B89",
+"J& c #3B4885",
+"K& c #384685",
+"L& c #2A3477",
+"M& c #2A3677",
+"N& c #283670",
+"O& c #303E80",
+"P& c #303C80",
+"Q& c #353F81",
+"R& c #374383",
+"S& c #384384",
+"T& c #3A4788",
+"U& c #3A4A89",
+"V& c #3C4A88",
+"W& c #3D4C8C",
+"X& c #3F4D8C",
+"Y& c #404F8E",
+"Z& c #42508D",
+"`& c #41508D",
+" * c #435190",
+".* c #425290",
+"+* c #43528C",
+"@* c #43548F",
+"#* c #445494",
+"$* c #44548C",
+"%* c #445293",
+"&* c #445391",
+"** c #435090",
+"=* c #42518E",
+"-* c #41508E",
+";* c #404E8C",
+">* c #3F4D8A",
+",* c #3A4885",
+"'* c #374581",
+")* c #232C71",
+"!* c #242F76",
+"~* c #252F73",
+"{* c #253176",
+"]* c #293673",
+"^* c #2A3678",
+"/* c #2E3C7C",
+"(* c #313E79",
+"_* c #313C81",
+":* c #324084",
+"<* c #344082",
+"[* c #374382",
+"}* c #374786",
+"|* c #3B478C",
+"1* c #3C4B8C",
+"2* c #3F4C8C",
+"3* c #3E4E8E",
+"4* c #404F90",
+"5* c #414F8C",
+"6* c #425090",
+"7* c #455391",
+"8* c #475694",
+"9* c #445493",
+"0* c #455394",
+"a* c #424F8C",
+"b* c #465494",
+"c* c #46588F",
+"d* c #45568F",
+"e* c #455491",
+"f* c #445390",
+"g* c #43528E",
+"h* c #35417C",
+"i* c #212D71",
+"j* c #222D71",
+"k* c #242E71",
+"l* c #253173",
+"m* c #283179",
+"n* c #273476",
+"o* c #2D397C",
+"p* c #303C77",
+"q* c #34407F",
+"r* c #374484",
+"s* c #384489",
+"t* c #3A478B",
+"u* c #3A4684",
+"v* c #3B4B8C",
+"w* c #404E90",
+"x* c #435290",
+"y* c #42538D",
+"z* c #455494",
+"A* c #455593",
+"B* c #44558D",
+"C* c #465690",
+"D* c #475793",
+"E* c #485495",
+"F* c #475594",
+"G* c #465592",
+"H* c #41508C",
+"I* c #3C4A89",
+"J* c #202C71",
+"K* c #222E72",
+"L* c #2D3979",
+"M* c #364082",
+"N* c #344484",
+"O* c #364483",
+"P* c #384688",
+"Q* c #3D498D",
+"R* c #3E4E8C",
+"S* c #435191",
+"T* c #435391",
+"U* c #465491",
+"V* c #485795",
+"W* c #475496",
+"X* c #485894",
+"Y* c #46568B",
+"Z* c #495A92",
+"`* c #495996",
+" = c #495992",
+".= c #414F8A",
+"+= c #3E4C88",
+"@= c #1F2B6E",
+"#= c #222C70",
+"$= c #252E73",
+"%= c #263279",
+"&= c #2B3777",
+"*= c #2D3879",
+"== c #2F3C78",
+"-= c #324080",
+";= c #364287",
+">= c #384481",
+",= c #3E4D8C",
+"'= c #3E4B8D",
+")= c #424F8F",
+"!= c #425292",
+"~= c #435291",
+"{= c #45528D",
+"]= c #465594",
+"^= c #43548C",
+"/= c #475991",
+"(= c #4A5A99",
+"_= c #485791",
+":= c #485593",
+"<= c #4B5B9A",
+"[= c #4C5B98",
+"}= c #4A5A95",
+"|= c #495894",
+"1= c #404E8A",
+"2= c #3E4D89",
+"3= c #394680",
+"4= c #1F2A6D",
+"5= c #1F286D",
+"6= c #232E70",
+"7= c #232D71",
+"8= c #253072",
+"9= c #263172",
+"0= c #293675",
+"a= c #2B3879",
+"b= c #313F83",
+"c= c #384888",
+"d= c #3B498C",
+"e= c #404E8D",
+"f= c #42508E",
+"g= c #435292",
+"h= c #455492",
+"i= c #465695",
+"j= c #475691",
+"k= c #48568F",
+"l= c #47578F",
+"m= c #495993",
+"n= c #48568C",
+"o= c #4C5C9C",
+"p= c #4C5D9C",
+"q= c #4D5E98",
+"r= c #333E78",
+"s= c #1E296E",
+"t= c #1E2A6D",
+"u= c #222D70",
+"v= c #253171",
+"w= c #283276",
+"x= c #283476",
+"y= c #2A3773",
+"z= c #323F7F",
+"A= c #384285",
+"B= c #384684",
+"C= c #3F4B87",
+"D= c #46518C",
+"E= c #4D598E",
+"F= c #495493",
+"G= c #43538C",
+"H= c #414E92",
+"I= c #405090",
+"J= c #44528F",
+"K= c #45558F",
+"L= c #4A5898",
+"M= c #485A92",
+"N= c #4A599C",
+"O= c #4A5891",
+"P= c #4B5A95",
+"Q= c #4B5B96",
+"R= c #4D5D97",
+"S= c #4E5F98",
+"T= c #505E99",
+"U= c #4F5E9D",
+"V= c #4C5D98",
+"W= c #485893",
+"X= c #455390",
+"Y= c #212B6F",
+"Z= c #232E6D",
+"`= c #253074",
+" - c #293674",
+".- c #2D387A",
+"+- c #344280",
+"@- c #46528D",
+"#- c #5B669F",
+"$- c #737DAC",
+"%- c #7B86B1",
+"&- c #747DAD",
+"*- c #495791",
+"=- c #414E8C",
+"-- c #445291",
+";- c #465593",
+">- c #46568E",
+",- c #485A91",
+"'- c #485897",
+")- c #4A578D",
+"!- c #4B5C95",
+"~- c #4B5A93",
+"{- c #4E5E99",
+"]- c #4E5E97",
+"^- c #4D5D98",
+"/- c #4F5F9C",
+"(- c #4C5C97",
+"_- c #475692",
+":- c #3A4883",
+"<- c #1E276C",
+"[- c #202B6E",
+"}- c #1F2B70",
+"|- c #242E70",
+"1- c #273370",
+"2- c #293573",
+"3- c #2C387C",
+"4- c #2C397A",
+"5- c #2D3A7A",
+"6- c #3C4886",
+"7- c #596597",
+"8- c #8990B4",
+"9- c #ADB3CE",
+"0- c #BDC1D6",
+"a- c #AEB4CE",
+"b- c #8A92B8",
+"c- c #606C9F",
+"d- c #46548F",
+"e- c #465694",
+"f- c #465691",
+"g- c #475697",
+"h- c #4A5895",
+"i- c #4A5B94",
+"j- c #4C5B97",
+"k- c #4C5A92",
+"l- c #4F5F9D",
+"m- c #4F609A",
+"n- c #52639E",
+"o- c #4D5E9C",
+"p- c #4A5991",
+"q- c #4F5A94",
+"r- c #1D286C",
+"s- c #1D276C",
+"t- c #212B6D",
+"u- c #212E6E",
+"v- c #232F73",
+"w- c #283470",
+"x- c #2A3575",
+"y- c #2C377A",
+"z- c #2B3876",
+"A- c #2E3B76",
+"B- c #434D8D",
+"C- c #707BA9",
+"D- c #ADB3CC",
+"E- c #D8DAE6",
+"F- c #E3E6EE",
+"G- c #D8DAE7",
+"H- c #B0B5CE",
+"I- c #7580AB",
+"J- c #445492",
+"K- c #465791",
+"L- c #495899",
+"M- c #47578E",
+"N- c #4B5995",
+"O- c #4C588F",
+"P- c #4B5A92",
+"Q- c #4F5E93",
+"R- c #50619D",
+"S- c #51629D",
+"T- c #4B5A8D",
+"U- c #4A5A9A",
+"V- c #4E5E94",
+"W- c #4A5994",
+"X- c #495693",
+"Y- c #455490",
+"Z- c #1C276A",
+"`- c #1F296D",
+" ; c #202B6B",
+".; c #242F72",
+"+; c #25326D",
+"@; c #2C3976",
+"#; c #434F89",
+"$; c #7982AC",
+"%; c #BCC0D6",
+"&; c #EDEFF3",
+"*; c #7D87B1",
+"=; c #4F5D97",
+"-; c #445491",
+";; c #475492",
+">; c #475695",
+",; c #495995",
+"'; c #485895",
+"); c #4F5F9B",
+"!; c #4F6098",
+"~; c #506099",
+"{; c #4C598E",
+"]; c #4B5C97",
+"^; c #4A5A9B",
+"/; c #4A5A94",
+"(; c #3E4B85",
+"_; c #1D2768",
+":; c #1F296C",
+"<; c #232F72",
+"[; c #25306D",
+"}; c #293576",
+"|; c #2A3774",
+"1; c #2D3977",
+"2; c #303B80",
+"3; c #424A85",
+"4; c #6F78A5",
+"5; c #ADB2CC",
+"6; c #ADB3CD",
+"7; c #757EAB",
+"8; c #4A5892",
+"9; c #43518F",
+"0; c #46558D",
+"a; c #4A5993",
+"b; c #4A5B95",
+"c; c #4D5A90",
+"d; c #4E5E9B",
+"e; c #4E5F9B",
+"f; c #4F5F9A",
+"g; c #4E5B8F",
+"h; c #4F609C",
+"i; c #1C2769",
+"j; c #20296D",
+"k; c #212C6F",
+"l; c #222C6D",
+"m; c #23306F",
+"n; c #263275",
+"o; c #293578",
+"p; c #384580",
+"q; c #586398",
+"r; c #8790B4",
+"s; c #8991B6",
+"t; c #5E6B9F",
+"u; c #44528A",
+"v; c #40508D",
+"w; c #43508F",
+"x; c #43528F",
+"y; c #475495",
+"z; c #4A5793",
+"A; c #4B5991",
+"B; c #495891",
+"C; c #4C5B95",
+"D; c #4D5B90",
+"E; c #4D5A8F",
+"F; c #4F5E9A",
+"G; c #4D5E96",
+"H; c #49588A",
+"I; c #4A5A8D",
+"J; c #46528C",
+"K; c #1D266A",
+"L; c #1B2667",
+"M; c #212C6D",
+"N; c #212C6C",
+"O; c #263174",
+"P; c #283374",
+"Q; c #293671",
+"R; c #33407F",
+"S; c #424C86",
+"T; c #737CA9",
+"U; c #7D87AF",
+"V; c #747EAB",
+"W; c #5D6A9D",
+"X; c #4A558E",
+"Y; c #3F4E8C",
+"Z; c #41518F",
+"`; c #43538F",
+" > c #475792",
+".> c #475791",
+"+> c #495998",
+"@> c #4B5998",
+"#> c #4C5C95",
+"$> c #4C5A93",
+"%> c #4D5D99",
+"&> c #4A568A",
+"*> c #45548F",
+"=> c #1D286B",
+"-> c #1E2968",
+";> c #202A6D",
+">> c #283371",
+",> c #27336F",
+"'> c #2F3C7B",
+")> c #323E7D",
+"!> c #334081",
+"~> c #4B5993",
+"{> c #495690",
+"]> c #414F86",
+"^> c #3F4C89",
+"/> c #404D8C",
+"(> c #425091",
+"_> c #44528D",
+":> c #45548D",
+"<> c #475690",
+"[> c #48578F",
+"}> c #495588",
+"|> c #4C5A98",
+"1> c #4A568B",
+"2> c #4C5A96",
+"3> c #1C2768",
+"4> c #1B2767",
+"5> c #1B2665",
+"6> c #1F2B6D",
+"7> c #222D6F",
+"8> c #232F6E",
+"9> c #273373",
+"0> c #2E3A7B",
+"a> c #3A4888",
+"b> c #3E4C8B",
+"c> c #3B4B87",
+"d> c #3B4A8A",
+"e> c #3D4B88",
+"f> c #3F4E8B",
+"g> c #404E8B",
+"h> c #42518C",
+"i> c #455493",
+"j> c #475795",
+"k> c #485793",
+"l> c #48558B",
+"m> c #4A5992",
+"n> c #485794",
+"o> c #4A5694",
+"p> c #1E266B",
+"q> c #1D2668",
+"r> c #1E2869",
+"s> c #1C2867",
+"t> c #1E2A69",
+"u> c #222E6D",
+"v> c #263173",
+"w> c #2D3A77",
+"x> c #394684",
+"y> c #3A4A84",
+"z> c #3E4A8C",
+"A> c #3B4B88",
+"B> c #3D4987",
+"C> c #3C4B89",
+"D> c #3D4C8B",
+"E> c #40508C",
+"F> c #44548E",
+"G> c #44538F",
+"H> c #485592",
+"I> c #485792",
+"J> c #46568C",
+"K> c #42528C",
+"L> c #1A2665",
+"M> c #1E276B",
+"N> c #222C6F",
+"O> c #263271",
+"P> c #2A3574",
+"Q> c #364684",
+"R> c #3C4884",
+"S> c #3B4987",
+"T> c #3D4A87",
+"U> c #3D4A89",
+"V> c #3E4D8B",
+"W> c #42508C",
+"X> c #44528B",
+"Y> c #44538E",
+"Z> c #46548E",
+"`> c #465493",
+" , c #475593",
+"., c #475490",
+"+, c #4A5794",
+"@, c #424E8C",
+"#, c #3C477A",
+"$, c #1A2467",
+"%, c #1D2969",
+"&, c #1C2868",
+"*, c #202C6C",
+"=, c #232F6F",
+"-, c #243075",
+";, c #2E3B7C",
+">, c #2F3A7E",
+",, c #333F7C",
+"', c #3E4A84",
+"), c #3B4A88",
+"!, c #3F4D88",
+"~, c #404F8C",
+"{, c #42508F",
+"], c #44518C",
+"^, c #445490",
+"/, c #485693",
+"(, c #465284",
+"_, c #1B2464",
+":, c #1C266A",
+"<, c #1E276D",
+"[, c #202A6C",
+"}, c #1F2B6A",
+"|, c #25316F",
+"1, c #2C367D",
+"2, c #2E3A74",
+"3, c #2F3B7A",
+"4, c #303F7C",
+"5, c #324283",
+"6, c #3B4889",
+"7, c #3D4A88",
+"8, c #404F8A",
+"9, c #44528E",
+"0, c #475794",
+"a, c #465294",
+"b, c #3A4475",
+"c, c #182059",
+"d, c #1A2462",
+"e, c #1D2869",
+"f, c #1F296A",
+"g, c #212A6D",
+"h, c #252F6F",
+"i, c #263272",
+"j, c #283275",
+"k, c #2A3675",
+"l, c #2E3A77",
+"m, c #3B4887",
+"n, c #3C4A87",
+"o, c #3E4C89",
+"p, c #3F4C88",
+"q, c #42528F",
+"r, c #45558C",
+"s, c #1B2565",
+"t, c #25306F",
+"u, c #283373",
+"v, c #2D3A78",
+"w, c #3C4987",
+"x, c #3E4B88",
+"y, c #3E4C8A",
+"z, c #3E4E89",
+"A, c #3F4E8A",
+"B, c #414E89",
+"C, c #42518D",
+"D, c #414F8B",
+"E, c #445190",
+"F, c #42528D",
+"G, c #42518B",
+"H, c #3C467F",
+"I, c #1C2664",
+"J, c #1F2A6B",
+"K, c #242F6F",
+"L, c #3F4D89",
+"M, c #3F4F8A",
+"N, c #40508B",
+"O, c #1D2765",
+"P, c #3B4988",
+"Q, c #3C4A86",
+"R, c #3C4B87",
+"S, c #404F8B",
+"T, c #3D4B87",
+"U, c #3D4C88",
+"V, c #3F4C87",
+" . + @ @ # $ % & * = - ; ",
+" > , ' ) ! ~ { ] ^ / ( % _ : < [ } | 1 1 ",
+" 2 3 4 5 ' ) ! ~ { ] ^ / ( % _ : < [ 6 7 | | 8 1 ",
+" 9 0 0 a 3 4 b ' ) ! ~ { ] c / ( d _ : < [ 6 7 | e f f 8 g 1 ",
+" h h i 0 a 3 j b ' ) ! ~ { ] c / ( d _ : * [ 6 k | e f 8 8 8 l m ",
+" n o p i 0 q a 3 j b ' ) ! r { ] c s t d _ : * [ 6 | | e f 8 8 l l l g 1 ",
+" u o p h i 0 q a 4 5 b ' v ! r { ] # s % d _ w x } 6 | | e f 8 8 l l y z m m ",
+" A u u o p h i 0 a B 4 5 b ' ! ~ r C ^ # D % E _ < x 6 7 | | f 8 8 8 l F G G H I m J ",
+" K u u o o p i 0 q a 3 j b ' ) ! ~ { ] ^ L D M E _ < x 6 k | e f 8 8 l y G G N G H g m J ",
+" n u u u o p h i 0 q a 3 j b ' v ! ~ { ] c / ( d _ : * [ 6 k | e f 8 O P Q G Q Q G Q H g m J ",
+" K u u u o p h i 0 0 a 3 4 5 b R v ~ r S ^ # s % d _ w x T 6 | | e 8 8 G G Q N G Q N G N H m ; J ",
+" A u u U V W X Y Z ` ...+.@.#.$.%.! ~ { C ^ L D % E _ < x 6 k | | f G O Q G G G N G G Q G G H m ; J ",
+" u u u &.*.X =.Z -. ...+.;.>.,.$.'.! r { ^ ).D M E w * < [ 6 k | e O G G Q Q Q Q G Q G N G N N m m ; ",
+" u u o &.W X =.Y ` .!.~.+.@.{.$.%.].^./.(.% E L _.:.<.[.}.|.6 k 1.Q Q Q Q Q Q Q Q Q Q Q Q Q G G 2.3.3.1 ",
+" n o o o *.X Y Z -. .!.+.;.#.#.{.$.4.5.6.(.d 7.8.9.0.a.b.c.d.e.< f.g.f.f.f.f.Q g.g.f.g.f.f.g.f.Q Q - - - h.i. ",
+" o p p p j.Y Z ` .!.!.+.;.>.,.k.l.^.6.L % d # m.n.o.p.q.r.c.s.f.t.f.f.f.f.f.f.f.f.f.f.g.g.f.g.f.g.u.u.v.- h. ",
+" h p h h i Z -.` w. .+.+.;.,.,.{.x.{ y.z.A.% B.C.D.E.F.G.H.I.J.K.L.M.N.f.M.M.O.O.O.M.O.O.O.P.Q.M.f.P.R.R.u.u.u.h. ",
+" 9 h i i i 0 S.T. .!.+.+.;.>.,.$.U.V.W.X.Y.( Z.d `. +.+++@+G.#+$+%+&+*+=+O.O.-+M.O.O.O.O.O.M.-+M.O.O.;+>+,+'+R.R.u.1 ",
+" 0 i 0 0 0 q !.)+!++.;.@.#.{.{.~+V.5.{+]+^+( Z./+(+_+:+<+++<+:+[+}+|+1+).-+O.2+-+-+3+-+O.3+4+3+O.O.O.>+B.>+>+,+'+5+6+ ",
+" 0 0 q q a a )+7++.;.@.,.{.$.%.8+9+0++ a+b+c+d+e+f+g+h+i+j+k+l+m+n+o+p+q+o+r+-+2+s+t+2+2+t+s+4+t+4+3+u+v+w+B.>+>+,+'+ ",
+" 2 a a a a B 3 +.x+;.,.{.,.$.'.y+z+A+B+*+C+D+E+F+G+H+I+J+K+L+M+N+O+P+Q+R+S+T+U+V+W+X+s+s+X+s+Y+2+2+Z+q+`+`+u+ @w+w+.@,+h. ",
+" 3 3 3 3 4 4 j j 5 b ' {.%.%.'.'.A++@@@#@*+$@%@&@*@6.=@-@;@>@,@'@)@!@~@{@]@^@/@(@_@:@Y+Y+<@Y+2+[@}@|@|@|@|@1@`+u+ @w+B.,+ ",
+" 2@4 4 j j 5 5 b b b ' ' ) '.'.y+<@3@A+4@5@6@`+7@8@9@0@a@b@c@d@{@e@f@g@h@_@i@j@k@l@m@n@{@o@Y+o@Y+p@q@p@q@q@q@|@1@1@`+ @w+w+6+ ",
+" , 5 b b b b b ' ' R ) v ! '.y+<@Y+r@$@s@t@u@y.v@A+w@a@0@{@x@y@z@A@A@B@C@D@E@F@D@G@H@I@J@K@o@L@]@M@M@K@M@!@p@q@q@|@1@`+u+ @6+ ",
+" ' ' ' ' ' ' ' ) v v ! ! ~ <@Y+<@s+s@N@@@O@P@Q@o+R@S@T@U@V@W@(@X@Y@Z@`@`@ #.#+#@###$#%#&#*#, X@X@X@X@X@X@, M@M@p@q@|@=#-#`+;# ",
+" ) ) ) ) ) v ! ! ! ~ ~ r { Y+s+t+s+>#,#'#)#A+!#~#R@{#]#^#j@j@C@H@/#/#(#_#:#.#<#[#}#|#1#2#3#4#D@*#*#D@D@D@X@, , M@!@p@q@5#-#`+ ",
+". ! ! ! ! ! ! ~ ~ ~ r { { C ] s+4+O.6#7#8#P@9#0#a#b#/@c#d#e#f#4#g#.#h#i#$#j#k#l#m#n#o#p#q#r#s#t#u#_#*#*#*#D@D@X@, M@M@p@q@5#v#;#",
+"+ ~ ~ ~ ~ r r r { { S C ] ^ @ -+-+-+4+w#x#r+y#z#A#W@]#g@B#C#D#E#F#G#H#I#J#K#L#M#N#O#P#Q#R#S#r#k#s#s#t#u#_#*#*#D@D@X@, M@T#q@5#U#",
+"@ { { { { { { C ] V#W#X#Y#Z#Z#`# $ $`#.$+$@$#$$$%$&$*$=$-$;$>$,$'$)$!$~${$]$^$/$($]$_$:$<$[$}$|$1$1$2$3$4$5$6$*#*#D@X@, M@T#q@v#",
+"@ ] ] ] ] ] ] ^ 7$8$9$0$a$b$c$d$d$e$f$g$h$i$j$k$l$m$n$o$p$q$r$r$s$t$u$v$w$x$y$z$A$B$C$C$D$D$z$E$F$G$H$I$J$K$L$M$_#*#N$D@, M@T#5#",
+"# ^ ^ c c c # { Z#O$P$Q$R$S$S$T$T$U$V$W$X$Y$Z$`$ %.%+%+%@%#%$%%%$%&%*%=%-%;%;%>%,%'%)%)%;%)%!%;%>%&%~%{%]%^%/%2$(%_#*#N$D@X@M@q@",
+"$ / / / / s s _%:%<%[%}%|%1%2%3%4%5%4%6%7%8%9%9%0%a%b%a%c%b%b%b%c%d%e%f%e%e%g%h%i%j%h%j%j%j%h%h%h%h%d%k%l%m%n%o%p%s#_#q%N$D@, T#",
+"% ( ( ( ( t % r%s%t%u%v%w%x%x%T$y%z%W$A%B%C%D%`$`$+%@%E%.%F%G%G%%%*%H%>%;%>%>%)%;%I%J%J%J%J%I%)%!%!%K%+%L%M%N%O%P%Q%s#_#q%N$R%M@",
+"& % % d d d d E S%T%U%V%W%X%Y%Z%`%e$ &.&+&@&#&$&%&&&*&&&=&-&;&>&,&'&)&D$D$!&~&{&]&^&/&(&]&(&{&{&/&_&v$:&<&[&}&|&1&2&Q%s#_#q%N$, ",
+"* _ _ _ _ _ _ _ _ 3&4&5&6&7&8&9&0&a&b&c&d&e&e&f&&$g&h&i&j&>$k&l&m&n&o&p&q&r&s&t&u&v&w&x&y&z&A&B&C&C&D&E&F&G&H&I&2 J&K&Q%s#_#q%R%",
+"= : : : : : w < < * x x [ }.e.L.O.O.4@L&2+M&N&O&d#P&d#Q&G#R&S&T&U&V&W&X&Y&Y&Z&`& *.*+*@*#*$*%*&*****=*-*-*;*>*I&I&2 ,*K&Q%'*q%R%",
+"- < < < * * x x x [ T 6 6 }.e.)*!*~*{*q+]*^*/*P&(*_*:*<*[*}*L#|*1*2*3*4*5*6*`&7*8*9*0*a*b*c*d*e*f*f*f*g*=*-*;*>*I&I&2 ,*Q%Q%_#h*",
+"; [ [ [ [ [ } 6 6 6 6 k T < i*j*k*~*l*m*n*M&o*c@c@p*q*:#r*s*t*K#u*v*X&w* *x* *y*z*A*B*C*D*E*F*G*G*G*e*f*x***Z&H*>*I&I*J&,*Q%'*h*",
+" } 6 6 6 6 6 7 k k | g.T = J*t.K*. O.v+u@1@L*c@O&O&M*N*O*P*|*>.Q*V&R*R*;*Z&S*T*U*V*W*X*Y*Z*`* =8*8*8*G*e*f*g*Z&.=>*+=2 J&Q%'* ",
+" | 7 7 k | | | Q G }.k @=6 6 #=7.$=%=v+4@&=&=*===-=N*;=P*>=u*u*>.,='=)=!=~={=]=^=/=(=_=X*:=<=[=}=|=|=8*G*e*f*g*H*1=2=+=J&,*3= ",
+" 1 | | | | | | e e 4=5=6 6 T j*6=7=7=8=9=^ 0=a=_.b=M*O*P*c=d=,.@.R*R*e=f=g=z*h=i=j=k=l=m=n=o=p=q=}=K |=8*G*e*f*=*H*>*+=2 J&r= ",
+" 1 | e e e e f f s=f t=e }.T = < u=7.v=O.w=x=y=c@z=N*A=B=C=D=E=F=G=H=I=6*J=]=K=L=M=N=O=P=Q=R=S=T=U=V=K W=8*G*X=g*H*1=I&2 J&r= ",
+" 8 f f f f 8 8 f Q G = e k = Y=j*Z=`=L ,# -&=.-b#+-O*@-#-$-%-&-u&*-=-.*--f*;->-,-'-)-!-~-{-S=]-^-/-(-K W=_-e*f*=*.=2=+=:- ",
+" 1 f 8 8 8 8 f f <-O 8 O f [-}-u=|-8=D+1-2-3-4-5-A@6-7-8-9-0-a-b-c-d- *--e-f-g-h-h-i-j-k-l-m-n-q=o-p-q-|=W=G*f*g*.=2=+=h* ",
+" 8 8 8 8 8 r-s-8 5=<-8 t=k t-u-v-t O.w-x-y-z-A-d#B-C-D-E-F-G-H-I-p-f=--J-K-L-M-N-O-P-q=Q-R-S-S-T-U-V-W-X-_-Y-g*H*1=+= ",
+" g 8 l l l s-l 8 Z-s-8 5=`- ;u=.;>++;4@A+0#@;n+(@#;$;%;F-&;F-0-*;=;=*x*-;;;>;,;';N-o=]-);!;~;{;!;];^;/;/;_-Y-g*H*1=(; ",
+" 1 l l l l l l <-s-l 8 _;:;= #=<;[;B.};M&|;1;2;l@3;4;5;E-F-G-6;7;8;f=9;z*0;j=*-a;b;c;d;e;f;g;{-h;P-^;/;/;_-Y-g*H*1=3= ",
+" m l l I l l m g i;8 8 j;k;l;'+m;n;o;2+2+n+e@X@p;q;r;D-0-a-s;t;u;v;w;x;9*y;_-z;A;B;C;D;E;F;G;H;}=I;e-J;_-Y-g*H*(; ",
+" g I I I I K;F l L;g - M;N;Z=O;P;)#Q;0#{#U@k@R;S;o%T;U;V;W;X;Y;Y&Z;`;f* >>;.>+>@>#>$>%>%>&>#>/;^;/;/;_-*>g*1= ",
+" 1 I I I I I m I l =>e ->;>6=8=>>,>};=@R@'>)>!>/#P%@-~>{>]>I*^>/>Y&(>_>:>:><>[>}>~>|>(-1>2>n W-/;/;e-G*X=g*3= ",
+" L;I 3.3.I K;3>4>5>8 6>7>j*8>8=9>'#L*1;0>z@z=F#K&a>I*b>c>d>e>f>g>h>S*--i>y;j=j>k>_=l>h-m>n>}=/;o>e-Y-g*.= ",
+" N N p>m q>K;I K;r>s>t>u>,+v>w=`+^*w>n+d#B@:#x>u*y>z>A>B>C>D>Y;E>H*F>G>F>G*H>I>k=';_-X*X*W-/;/;J>J>K> ",
+" J H G 5>L>M>L;3.<-:;6 N>7>>+O>P>7@)@n+b#z=F@Q>u*y>>.R>S>T>U>V>X&5*f=W>&*X>Y>Z>`>j= ,.,X-+,e-e-!+@,#, ",
+" J ; 5>$,F m i;%,&,*,7>=,d+-,u+1@M&;,>,,,~+u*,.y>',D>),V&e>e>!,/>~,H*{,],^,J-`;-;-;/,8* =J>J>(,#, ",
+" J ; _,L;; :,l <,[,},N>|,+ q+M&1,2,3,4,5,Q>{.u*u*',6,),C>7,e>+=g>8,E>H*W>h>x;f*9,0,J-o>a,K>b, ",
+" c,; d,; 3>e,f,[,g,7>h,i,j,k,=@l,e@D@*#t#Q%K&1&2 7,m,n,o,o,o,V>p,g>g>v;`&q,J=f*G=r,J>K>#, ",
+" J s,1 3>3.2. ;R.'+t,9=u,k,&=v,c#D@*#_#t#Q%K&1&2 w,1&n,x,y,>*z,A,B,C,C,D,x;C,E,F,G,H, ",
+" 1 I,3>2.J,u.R.K,9=9>P>&=!@M@X@D@*#_#s#Q%2&J&2 w,S>n,T>o,L,A,A,M,M,D,N,C,g*.= ",
+" i.O,f, ; ;R.K,w+9>x-|@p@M@, X@N$*#_#s#Q%K&,*2 P,Q,R,n,T>x,x,L,A,1=S,1=3= ",
+" h.u.5+,+.@w+ @`+=#q@p@M@, D@N$q%_#s#Q%K&,*J&2 T,n,7,U,U,+=+=V,(; ",
+" 1 6+'+,+B.w+u+-#5#q@T#M@, D@N$q%_#s#Q%Q%,*J&J&2 2 +=+=+=(;3= ",
+" h.,+w+ @`+-#5#q@T#M@X@D@N$q%_#'*Q%Q%Q%,*J&J&:-h* ",
+" 6+6+;#`+v#5#q@T#M@, R%N$q%q%_#'*'*3=r=r= ",
+" ;#U#v#5#q@T#M@, R%R%h*h* "};
diff --git a/arts/builder/pics/Synth_EMPTY.xpm b/arts/builder/pics/Synth_EMPTY.xpm
new file mode 100644
index 00000000..0fc95945
--- /dev/null
+++ b/arts/builder/pics/Synth_EMPTY.xpm
@@ -0,0 +1,1119 @@
+/* XPM */
+static char * Synth_EMPTY_xpm[] = {
+"64 64 1052 2",
+" c None",
+". c #253073",
+"+ c #273276",
+"@ c #293378",
+"# c #283278",
+"$ c #263077",
+"% c #252F75",
+"& c #232E74",
+"* c #212B71",
+"= c #202A6E",
+"- c #1E296A",
+"; c #1C2667",
+"> c #2B3671",
+", c #313D7B",
+"' c #384288",
+") c #353F87",
+"! c #323D83",
+"~ c #2F3A80",
+"{ c #2D377D",
+"] c #2A357B",
+"^ c #29347A",
+"/ c #273177",
+"( c #253076",
+"_ c #232D72",
+": c #222C72",
+"< c #212C71",
+"[ c #202B70",
+"} c #202A70",
+"| c #1F296E",
+"1 c #1B2463",
+"2 c #3C4988",
+"3 c #434D95",
+"4 c #414C93",
+"5 c #3D488D",
+"6 c #202A6F",
+"7 c #1F2A6F",
+"8 c #1E286C",
+"9 c #37437E",
+"0 c #4F59A0",
+"a c #485399",
+"b c #3B458D",
+"c c #283379",
+"d c #242E74",
+"e c #1E296D",
+"f c #1E286D",
+"g c #1C2669",
+"h c #5862A8",
+"i c #555FA6",
+"j c #3F4B90",
+"k c #1F2A6E",
+"l c #1D276B",
+"m c #1C2668",
+"n c #4B5A97",
+"o c #616CB3",
+"p c #5C67AF",
+"q c #4B579B",
+"r c #2E397F",
+"s c #263177",
+"t c #253075",
+"u c #6872B4",
+"v c #343F84",
+"w c #222C71",
+"x c #212B70",
+"y c #1B2666",
+"z c #1B2366",
+"A c #45538B",
+"B c #445194",
+"C c #2B367B",
+"D c #263076",
+"E c #232E73",
+"F c #1D266B",
+"G c #1E266C",
+"H c #1A2264",
+"I c #1D276A",
+"J c #171F57",
+"K c #4A5996",
+"L c #273277",
+"M c #242F74",
+"N c #1A2664",
+"O c #1E296C",
+"P c #1D296B",
+"Q c #1E2A6C",
+"R c #354186",
+"S c #2C377B",
+"T c #202B6F",
+"U c #6672BC",
+"V c #666EB4",
+"W c #5E6AB4",
+"X c #5A66AC",
+"Y c #565EA4",
+"Z c #525EA4",
+"` c #4E5AA2",
+" . c #4A569C",
+".. c #465296",
+"+. c #424E94",
+"@. c #3E4A8E",
+"#. c #3E468C",
+"$. c #36428C",
+"%. c #363E84",
+"&. c #626EB4",
+"*. c #5E66AC",
+"=. c #5662AB",
+"-. c #525AA4",
+";. c #424A94",
+">. c #3A4A8C",
+",. c #3A468C",
+"'. c #323E84",
+"). c #273278",
+"!. c #4A529C",
+"~. c #464E93",
+"{. c #3A428C",
+"]. c #313983",
+"^. c #2D387E",
+"/. c #2A3479",
+"(. c #263176",
+"_. c #222D72",
+":. c #212A6F",
+"<. c #1F2B6F",
+"[. c #212A6E",
+"}. c #1E286A",
+"|. c #1D2769",
+"1. c #303B81",
+"2. c #2C367E",
+"3. c #293479",
+"4. c #232E72",
+"5. c #222D71",
+"6. c #212D72",
+"7. c #222E74",
+"8. c #222A6F",
+"9. c #1E2868",
+"0. c #1A235E",
+"a. c #5A62AC",
+"b. c #35418B",
+"c. c #2F3B80",
+"d. c #232D73",
+"e. c #222D73",
+"f. c #222B70",
+"g. c #212B6C",
+"h. c #20296C",
+"i. c #4E569C",
+"j. c #323A80",
+"k. c #29357A",
+"l. c #283178",
+"m. c #263075",
+"n. c #263171",
+"o. c #232F70",
+"p. c #222E73",
+"q. c #233070",
+"r. c #24306D",
+"s. c #232E70",
+"t. c #242F70",
+"u. c #252E6F",
+"v. c #242E73",
+"w. c #262E73",
+"x. c #262F73",
+"y. c #263273",
+"z. c #222E6C",
+"A. c #223264",
+"B. c #222D6E",
+"C. c #4E5AA4",
+"D. c #4A5A9C",
+"E. c #303C81",
+"F. c #2D397E",
+"G. c #2C357D",
+"H. c #273378",
+"I. c #263276",
+"J. c #252F76",
+"K. c #253070",
+"L. c #242F73",
+"M. c #243071",
+"N. c #24306F",
+"O. c #243073",
+"P. c #253177",
+"Q. c #26326E",
+"R. c #263277",
+"S. c #263078",
+"T. c #26327C",
+"U. c #252D72",
+"V. c #253071",
+"W. c #242F6E",
+"X. c #232E6E",
+"Y. c #46529C",
+"Z. c #425294",
+"`. c #354182",
+" + c #2B357D",
+".+ c #273178",
+"++ c #263175",
+"@+ c #262F74",
+"#+ c #273073",
+"$+ c #263272",
+"%+ c #25306E",
+"&+ c #273173",
+"*+ c #26336C",
+"=+ c #283273",
+"-+ c #2A3674",
+";+ c #26366C",
+">+ c #2A3274",
+",+ c #232E6C",
+"'+ c #212B69",
+")+ c #464E94",
+"!+ c #2F3B81",
+"~+ c #2D3679",
+"{+ c #2B357C",
+"]+ c #273272",
+"^+ c #273274",
+"/+ c #273076",
+"(+ c #243074",
+"_+ c #283274",
+":+ c #263174",
+"<+ c #283374",
+"[+ c #293373",
+"}+ c #283575",
+"|+ c #293577",
+"1+ c #293376",
+"2+ c #293575",
+"3+ c #2B3779",
+"4+ c #28337A",
+"5+ c #293574",
+"6+ c #2B377D",
+"7+ c #2A367C",
+"8+ c #2A327C",
+"9+ c #293474",
+"0+ c #273273",
+"a+ c #273271",
+"b+ c #3E4A94",
+"c+ c #323A84",
+"d+ c #2E3A7F",
+"e+ c #2A3679",
+"f+ c #2A3579",
+"g+ c #263470",
+"h+ c #263274",
+"i+ c #273377",
+"j+ c #273371",
+"k+ c #27346F",
+"l+ c #293475",
+"m+ c #2A357A",
+"n+ c #2A3675",
+"o+ c #2C3878",
+"p+ c #2D3978",
+"q+ c #2D3777",
+"r+ c #2D3976",
+"s+ c #2E397B",
+"t+ c #2D3775",
+"u+ c #2B3873",
+"v+ c #2F3B77",
+"w+ c #30397E",
+"x+ c #2F3775",
+"y+ c #2A386E",
+"z+ c #2E3A7D",
+"A+ c #293579",
+"B+ c #293473",
+"C+ c #263170",
+"D+ c #2A357C",
+"E+ c #2A3379",
+"F+ c #293278",
+"G+ c #273279",
+"H+ c #2A3378",
+"I+ c #283377",
+"J+ c #283378",
+"K+ c #2C3779",
+"L+ c #2B3679",
+"M+ c #2B3877",
+"N+ c #2E397C",
+"O+ c #2D397C",
+"P+ c #2D397A",
+"Q+ c #2E3B7A",
+"R+ c #2E387D",
+"S+ c #2F3B7D",
+"T+ c #2E3C7A",
+"U+ c #2F387D",
+"V+ c #2F3B7F",
+"W+ c #313D7F",
+"X+ c #303E7E",
+"Y+ c #2F387C",
+"Z+ c #2E367C",
+"`+ c #2C3877",
+" @ c #2C3777",
+".@ c #2A3676",
+"+@ c #2B3571",
+"@@ c #2B3478",
+"#@ c #283477",
+"$@ c #2A3375",
+"%@ c #2A3474",
+"&@ c #2A3576",
+"*@ c #2B3676",
+"=@ c #2C3778",
+"-@ c #2C387A",
+";@ c #2C397B",
+">@ c #2E387B",
+",@ c #2E3A7C",
+"'@ c #2E3B7B",
+")@ c #2F3C7C",
+"!@ c #2F3D7C",
+"~@ c #303D7D",
+"{@ c #303D7E",
+"]@ c #313F7E",
+"^@ c #313E80",
+"/@ c #313D7D",
+"(@ c #313E7F",
+"_@ c #324081",
+":@ c #323E81",
+"<@ c #2E3E7C",
+"[@ c #2D3A79",
+"}@ c #283677",
+"|@ c #2A347C",
+"1@ c #263372",
+"2@ c #283479",
+"3@ c #283578",
+"4@ c #2B377B",
+"5@ c #2F3C7D",
+"6@ c #2F3C7E",
+"7@ c #303C7D",
+"8@ c #313E7E",
+"9@ c #323E7E",
+"0@ c #333F7F",
+"a@ c #323F7E",
+"b@ c #333F80",
+"c@ c #344081",
+"d@ c #333F7E",
+"e@ c #33417F",
+"f@ c #354183",
+"g@ c #34407E",
+"h@ c #2F3B7E",
+"i@ c #303980",
+"j@ c #2F3C7A",
+"k@ c #2A3279",
+"l@ c #293478",
+"m@ c #2B357A",
+"n@ c #2B3579",
+"o@ c #2D397B",
+"p@ c #2E3B7D",
+"q@ c #2F3B7B",
+"r@ c #2F3D7F",
+"s@ c #303D7F",
+"t@ c #313E7D",
+"u@ c #313F81",
+"v@ c #334082",
+"w@ c #334080",
+"x@ c #344183",
+"y@ c #354284",
+"z@ c #35417E",
+"A@ c #344284",
+"B@ c #364386",
+"C@ c #364385",
+"D@ c #364282",
+"E@ c #354383",
+"F@ c #344180",
+"G@ c #2B3776",
+"H@ c #2B3675",
+"I@ c #283370",
+"J@ c #29337C",
+"K@ c #273375",
+"L@ c #293477",
+"M@ c #283474",
+"N@ c #2C377C",
+"O@ c #2C3875",
+"P@ c #2E3A7E",
+"Q@ c #303C7F",
+"R@ c #303D7C",
+"S@ c #344181",
+"T@ c #344281",
+"U@ c #354280",
+"V@ c #344282",
+"W@ c #34427F",
+"X@ c #374280",
+"Y@ c #364482",
+"Z@ c #374487",
+"`@ c #374582",
+" # c #374586",
+".# c #384485",
+"+# c #344080",
+"@# c #2D3876",
+"## c #26327B",
+"$# c #273477",
+"%# c #2A3475",
+"&# c #2A3775",
+"*# c #2C3879",
+"=# c #2E3A78",
+"-# c #2F3B7C",
+";# c #303C7B",
+"># c #323E7F",
+",# c #323F80",
+"'# c #333F81",
+")# c #334180",
+"!# c #364283",
+"~# c #364382",
+"{# c #374385",
+"]# c #384584",
+"^# c #354482",
+"/# c #384588",
+"(# c #384587",
+"_# c #394585",
+":# c #394587",
+"<# c #394686",
+"[# c #394687",
+"}# c #374483",
+"|# c #354382",
+"1# c #354282",
+"2# c #2B3775",
+"3# c #2A337C",
+"4# c #283570",
+"5# c #2D377C",
+"6# c #2D3A7C",
+"7# c #323F81",
+"8# c #344182",
+"9# c #334181",
+"0# c #344382",
+"a# c #354281",
+"b# c #364484",
+"c# c #364583",
+"d# c #374482",
+"e# c #384585",
+"f# c #374684",
+"g# c #374685",
+"h# c #374480",
+"i# c #3A4686",
+"j# c #394885",
+"k# c #3A4988",
+"l# c #3A4A8A",
+"m# c #3A4989",
+"n# c #3A4786",
+"o# c #2F3B78",
+"p# c #2B3674",
+"q# c #283473",
+"r# c #2C387D",
+"s# c #2A3778",
+"t# c #2E3B7C",
+"u# c #313D80",
+"v# c #33407E",
+"w# c #364483",
+"x# c #394684",
+"y# c #374686",
+"z# c #374682",
+"A# c #3A4785",
+"B# c #394884",
+"C# c #394888",
+"D# c #3C4886",
+"E# c #3C4A8B",
+"F# c #3C4A87",
+"G# c #3A4887",
+"H# c #394685",
+"I# c #384685",
+"J# c #384583",
+"K# c #2A337B",
+"L# c #2E3976",
+"M# c #333D79",
+"N# c #313D77",
+"O# c #333E7B",
+"P# c #374380",
+"Q# c #364485",
+"R# c #374683",
+"S# c #374583",
+"T# c #3A4889",
+"U# c #364582",
+"V# c #384682",
+"W# c #3C498A",
+"X# c #3C4B85",
+"Y# c #3B4A87",
+"Z# c #3D4C8C",
+"`# c #3E4B8C",
+" $ c #3D4B8C",
+".$ c #3B4988",
+"+$ c #394786",
+"@$ c #33407D",
+"#$ c #27347B",
+"$$ c #2D3A7F",
+"%$ c #2F3A7B",
+"&$ c #364383",
+"*$ c #384684",
+"=$ c #3B4887",
+"-$ c #3A4885",
+";$ c #384883",
+">$ c #3D4A88",
+",$ c #3D4C8A",
+"'$ c #3C4B88",
+")$ c #394986",
+"!$ c #3C4A84",
+"~$ c #3F4C8C",
+"{$ c #3F4E8E",
+"]$ c #3E4E8A",
+"^$ c #3E4E8D",
+"/$ c #3E4D8B",
+"($ c #3D4B8A",
+"_$ c #3A4888",
+":$ c #3A4787",
+"<$ c #263279",
+"[$ c #283276",
+"}$ c #283478",
+"|$ c #2A3771",
+"1$ c #33407F",
+"2$ c #344385",
+"3$ c #344084",
+"4$ c #374585",
+"5$ c #384488",
+"6$ c #374587",
+"7$ c #394788",
+"8$ c #3B488C",
+"9$ c #394784",
+"0$ c #3B488A",
+"a$ c #3B498A",
+"b$ c #3B4B88",
+"c$ c #3F4D89",
+"d$ c #3E4D88",
+"e$ c #3F4F89",
+"f$ c #414E91",
+"g$ c #404F8E",
+"h$ c #404E8D",
+"i$ c #404D8C",
+"j$ c #3E4C8B",
+"k$ c #3C4A89",
+"l$ c #3A4886",
+"m$ c #35427E",
+"n$ c #283277",
+"o$ c #27327C",
+"p$ c #2C387B",
+"q$ c #364083",
+"r$ c #333C83",
+"s$ c #34407F",
+"t$ c #354385",
+"u$ c #394789",
+"v$ c #3C4A8C",
+"w$ c #3B4986",
+"x$ c #3E4B8A",
+"y$ c #3C4B89",
+"z$ c #3F4C88",
+"A$ c #404F8D",
+"B$ c #41518D",
+"C$ c #42518C",
+"D$ c #42508F",
+"E$ c #41518E",
+"F$ c #404F90",
+"G$ c #3F4E8C",
+"H$ c #323F7C",
+"I$ c #26346F",
+"J$ c #2F3B79",
+"K$ c #334084",
+"L$ c #384186",
+"M$ c #374687",
+"N$ c #3A4685",
+"O$ c #3C498C",
+"P$ c #3E4C88",
+"Q$ c #3F4E8B",
+"R$ c #41508E",
+"S$ c #425190",
+"T$ c #414E8A",
+"U$ c #43538D",
+"V$ c #42528E",
+"W$ c #435190",
+"X$ c #41508C",
+"Y$ c #404E8C",
+"Z$ c #3E4D8C",
+"`$ c #232D71",
+" % c #242E71",
+".% c #283077",
+"+% c #28327C",
+"@% c #28347C",
+"#% c #2A3678",
+"$% c #323E80",
+"%% c #323C84",
+"&% c #323F7B",
+"*% c #344384",
+"=% c #384483",
+"-% c #384889",
+";% c #3F4D8E",
+">% c #3E4C8D",
+",% c #3E4D8A",
+"'% c #414E90",
+")% c #425090",
+"!% c #425290",
+"~% c #44548E",
+"{% c #44538E",
+"]% c #455294",
+"^% c #43528E",
+"/% c #445295",
+"(% c #435391",
+"_% c #42518F",
+":% c #3F4D8A",
+"<% c #3E4B89",
+"[% c #3B4885",
+"}% c #2A3477",
+"|% c #2A3677",
+"1% c #283670",
+"2% c #303E80",
+"3% c #303C80",
+"4% c #353F81",
+"5% c #374383",
+"6% c #384384",
+"7% c #3A4788",
+"8% c #3A4A89",
+"9% c #3C4A88",
+"0% c #3F4D8C",
+"a% c #42508D",
+"b% c #41508D",
+"c% c #43528C",
+"d% c #43548F",
+"e% c #445494",
+"f% c #44548C",
+"g% c #445293",
+"h% c #445391",
+"i% c #435090",
+"j% c #42518E",
+"k% c #374581",
+"l% c #232C71",
+"m% c #242F76",
+"n% c #252F73",
+"o% c #253176",
+"p% c #293673",
+"q% c #2E3C7C",
+"r% c #313E79",
+"s% c #313C81",
+"t% c #324084",
+"u% c #344082",
+"v% c #374382",
+"w% c #374786",
+"x% c #3B478C",
+"y% c #3C4B8C",
+"z% c #3E4E8E",
+"A% c #414F8C",
+"B% c #455391",
+"C% c #475694",
+"D% c #445493",
+"E% c #455394",
+"F% c #424F8C",
+"G% c #465494",
+"H% c #46588F",
+"I% c #45568F",
+"J% c #455491",
+"K% c #445390",
+"L% c #35417C",
+"M% c #212D71",
+"N% c #253173",
+"O% c #283179",
+"P% c #273476",
+"Q% c #303C77",
+"R% c #374484",
+"S% c #384489",
+"T% c #3A478B",
+"U% c #3A4684",
+"V% c #3B4B8C",
+"W% c #404E90",
+"X% c #435290",
+"Y% c #42538D",
+"Z% c #455494",
+"`% c #455593",
+" & c #44558D",
+".& c #465690",
+"+& c #475793",
+"@& c #485495",
+"#& c #475594",
+"$& c #465592",
+"%& c #202C71",
+"&& c #222E72",
+"*& c #2D3979",
+"=& c #364082",
+"-& c #344484",
+";& c #384688",
+">& c #3D498D",
+",& c #3E4E8C",
+"'& c #435191",
+")& c #465491",
+"!& c #485795",
+"~& c #475496",
+"{& c #485894",
+"]& c #46568B",
+"^& c #495A92",
+"/& c #495996",
+"(& c #495992",
+"_& c #414F8A",
+":& c #1F2B6E",
+"<& c #222C70",
+"[& c #252E73",
+"}& c #2B3777",
+"|& c #2D3879",
+"1& c #2F3C78",
+"2& c #324080",
+"3& c #364287",
+"4& c #384481",
+"5& c #3E4B8D",
+"6& c #424F8F",
+"7& c #425292",
+"8& c #435291",
+"9& c #45528D",
+"0& c #465594",
+"a& c #43548C",
+"b& c #475991",
+"c& c #4A5A99",
+"d& c #485791",
+"e& c #485593",
+"f& c #4B5B9A",
+"g& c #4C5B98",
+"h& c #4A5A95",
+"i& c #495894",
+"j& c #404E8A",
+"k& c #3E4D89",
+"l& c #394680",
+"m& c #1F2A6D",
+"n& c #1F286D",
+"o& c #253072",
+"p& c #263172",
+"q& c #293675",
+"r& c #2B3879",
+"s& c #303A7B",
+"t& c #313F83",
+"u& c #384888",
+"v& c #3B498C",
+"w& c #42508E",
+"x& c #435292",
+"y& c #455492",
+"z& c #465695",
+"A& c #475691",
+"B& c #48568F",
+"C& c #47578F",
+"D& c #495993",
+"E& c #48568C",
+"F& c #4C5C9C",
+"G& c #4C5D9C",
+"H& c #4D5E98",
+"I& c #333E78",
+"J& c #1E296E",
+"K& c #1E2A6D",
+"L& c #222D70",
+"M& c #253171",
+"N& c #283476",
+"O& c #2A3773",
+"P& c #323F7F",
+"Q& c #384285",
+"R& c #3A4687",
+"S& c #3E4A84",
+"T& c #414E92",
+"U& c #405090",
+"V& c #44528F",
+"W& c #45558F",
+"X& c #4A5898",
+"Y& c #485A92",
+"Z& c #4A599C",
+"`& c #4A5891",
+" * c #4B5A95",
+".* c #4B5B96",
+"+* c #4D5D97",
+"@* c #4E5F98",
+"#* c #505E99",
+"$* c #4F5E9D",
+"%* c #4C5D98",
+"&* c #485893",
+"** c #455390",
+"=* c #212B6F",
+"-* c #232E6D",
+";* c #253074",
+">* c #293674",
+",* c #2D387A",
+"'* c #344280",
+")* c #3A478C",
+"!* c #3B4A8D",
+"~* c #414E8C",
+"{* c #445291",
+"]* c #465593",
+"^* c #46568E",
+"/* c #485A91",
+"(* c #485897",
+"_* c #4A578D",
+":* c #4B5C95",
+"<* c #4B5A93",
+"[* c #4E5E99",
+"}* c #4E5E97",
+"|* c #4D5D98",
+"1* c #4F5F9C",
+"2* c #4C5C97",
+"3* c #475692",
+"4* c #3A4883",
+"5* c #1E276C",
+"6* c #202B6E",
+"7* c #1F2B70",
+"8* c #242E70",
+"9* c #273370",
+"0* c #293573",
+"a* c #2C387C",
+"b* c #2C397A",
+"c* c #2D3A7A",
+"d* c #374386",
+"e* c #3C4885",
+"f* c #3C4A8A",
+"g* c #404F8C",
+"h* c #414F8F",
+"i* c #465694",
+"j* c #465691",
+"k* c #475697",
+"l* c #4A5895",
+"m* c #4A5B94",
+"n* c #4C5B97",
+"o* c #4C5A92",
+"p* c #4F5F9D",
+"q* c #4F609A",
+"r* c #52639E",
+"s* c #4D5E9C",
+"t* c #4A5991",
+"u* c #4F5A94",
+"v* c #1D286C",
+"w* c #1D276C",
+"x* c #212B6D",
+"y* c #212E6E",
+"z* c #232F73",
+"A* c #283470",
+"B* c #2A3575",
+"C* c #2C377A",
+"D* c #2B3876",
+"E* c #2E3B76",
+"F* c #354388",
+"G* c #384787",
+"H* c #404D8B",
+"I* c #445492",
+"J* c #465791",
+"K* c #495899",
+"L* c #47578E",
+"M* c #4B5995",
+"N* c #4C588F",
+"O* c #4B5A92",
+"P* c #4F5E93",
+"Q* c #50619D",
+"R* c #51629D",
+"S* c #4B5A8D",
+"T* c #4A5A9A",
+"U* c #4E5E94",
+"V* c #4A5994",
+"W* c #495693",
+"X* c #455490",
+"Y* c #1C276A",
+"Z* c #1F296D",
+"`* c #202B6B",
+" = c #242F72",
+".= c #25326D",
+"+= c #2C3976",
+"@= c #2E3A7A",
+"#= c #3E4C8C",
+"$= c #445491",
+"%= c #475492",
+"&= c #475695",
+"*= c #495995",
+"== c #485895",
+"-= c #4F5F9B",
+";= c #4F6098",
+">= c #506099",
+",= c #4C598E",
+"'= c #4B5C97",
+")= c #4A5A9B",
+"!= c #4A5A94",
+"~= c #3E4B85",
+"{= c #1D2768",
+"]= c #1F296C",
+"^= c #232F72",
+"/= c #25306D",
+"(= c #293576",
+"_= c #2A3774",
+":= c #2D3977",
+"<= c #303B80",
+"[= c #3D4B89",
+"}= c #43518F",
+"|= c #46558D",
+"1= c #495791",
+"2= c #4A5993",
+"3= c #4A5B95",
+"4= c #4D5A90",
+"5= c #4E5E9B",
+"6= c #4E5F9B",
+"7= c #4F5F9A",
+"8= c #4E5B8F",
+"9= c #4F609C",
+"0= c #1C2769",
+"a= c #20296D",
+"b= c #212C6F",
+"c= c #222C6D",
+"d= c #23306F",
+"e= c #263275",
+"f= c #293578",
+"g= c #364384",
+"h= c #40508D",
+"i= c #43508F",
+"j= c #43528F",
+"k= c #475495",
+"l= c #4A5793",
+"m= c #4B5991",
+"n= c #495891",
+"o= c #4C5B95",
+"p= c #4D5B90",
+"q= c #4D5A8F",
+"r= c #4F5E9A",
+"s= c #4D5E96",
+"t= c #49588A",
+"u= c #4A5A8D",
+"v= c #46528C",
+"w= c #1D266A",
+"x= c #1B2667",
+"y= c #212C6D",
+"z= c #212C6C",
+"A= c #293671",
+"B= c #3F4B89",
+"C= c #41518F",
+"D= c #43538F",
+"E= c #475792",
+"F= c #475791",
+"G= c #495998",
+"H= c #4B5998",
+"I= c #4C5C95",
+"J= c #4C5A93",
+"K= c #4D5D99",
+"L= c #4A568A",
+"M= c #45548F",
+"N= c #1D286B",
+"O= c #1E2968",
+"P= c #202A6D",
+"Q= c #283371",
+"R= c #27336F",
+"S= c #2F3C7B",
+"T= c #323E7D",
+"U= c #334081",
+"V= c #3C4A86",
+"W= c #3F4C89",
+"X= c #425091",
+"Y= c #44528D",
+"Z= c #45548D",
+"`= c #475690",
+" - c #48578F",
+".- c #495588",
+"+- c #4B5993",
+"@- c #4C5A98",
+"#- c #4A568B",
+"$- c #4C5A96",
+"%- c #1C2768",
+"&- c #1B2767",
+"*- c #1B2665",
+"=- c #1F2B6D",
+"-- c #222D6F",
+";- c #232F6E",
+">- c #273373",
+",- c #2E3A7B",
+"'- c #3B4B87",
+")- c #3B4A8A",
+"!- c #3D4B88",
+"~- c #404E8B",
+"{- c #455493",
+"]- c #475795",
+"^- c #485793",
+"/- c #48558B",
+"(- c #4A5992",
+"_- c #485794",
+":- c #4A5694",
+"<- c #1E266B",
+"[- c #1D2668",
+"}- c #1E2869",
+"|- c #1C2867",
+"1- c #1E2A69",
+"2- c #222E6D",
+"3- c #263173",
+"4- c #2D3A77",
+"5- c #3A4A84",
+"6- c #3E4A8C",
+"7- c #3D4987",
+"8- c #3D4C8B",
+"9- c #40508C",
+"0- c #44538F",
+"a- c #485592",
+"b- c #485792",
+"c- c #46568C",
+"d- c #42528C",
+"e- c #1A2665",
+"f- c #1E276B",
+"g- c #222C6F",
+"h- c #263271",
+"i- c #2A3574",
+"j- c #364684",
+"k- c #3C4884",
+"l- c #3B4987",
+"m- c #3D4A87",
+"n- c #3D4A89",
+"o- c #42508C",
+"p- c #44528B",
+"q- c #46548E",
+"r- c #465493",
+"s- c #475593",
+"t- c #475490",
+"u- c #4A5794",
+"v- c #424E8C",
+"w- c #3C477A",
+"x- c #1A2467",
+"y- c #1D2969",
+"z- c #1C2868",
+"A- c #202C6C",
+"B- c #232F6F",
+"C- c #243075",
+"D- c #2F3A7E",
+"E- c #333F7C",
+"F- c #3B4A88",
+"G- c #3F4D88",
+"H- c #44518C",
+"I- c #445490",
+"J- c #485693",
+"K- c #465284",
+"L- c #1B2464",
+"M- c #1C266A",
+"N- c #1E276D",
+"O- c #202A6C",
+"P- c #1F2B6A",
+"Q- c #25316F",
+"R- c #2C367D",
+"S- c #2E3A74",
+"T- c #2F3B7A",
+"U- c #303F7C",
+"V- c #324283",
+"W- c #3B4889",
+"X- c #404F8A",
+"Y- c #44528E",
+"Z- c #475794",
+"`- c #465294",
+" ; c #3A4475",
+".; c #182059",
+"+; c #1A2462",
+"@; c #1D2869",
+"#; c #1F296A",
+"$; c #212A6D",
+"%; c #252F6F",
+"&; c #283275",
+"*; c #2E3A77",
+"=; c #3E4C89",
+"-; c #42528F",
+";; c #43538C",
+">; c #45558C",
+",; c #1B2565",
+"'; c #25306F",
+"); c #283373",
+"!; c #2D3A78",
+"~; c #3C4987",
+"{; c #3E4B88",
+"]; c #3E4C8A",
+"^; c #3E4E89",
+"/; c #3F4E8A",
+"(; c #414E89",
+"_; c #42518D",
+":; c #414F8B",
+"<; c #445190",
+"[; c #42528D",
+"}; c #42518B",
+"|; c #3C467F",
+"1; c #1C2664",
+"2; c #1F2A6B",
+"3; c #242F6F",
+"4; c #3F4F8A",
+"5; c #40508B",
+"6; c #1D2765",
+"7; c #3C4B87",
+"8; c #404F8B",
+"9; c #3D4B87",
+"0; c #3D4C88",
+"a; c #3F4C87",
+" . + @ @ # $ % & * = - ; ",
+" > , ' ) ! ~ { ] ^ / ( % _ : < [ } | 1 1 ",
+" 2 3 4 5 ' ) ! ~ { ] ^ / ( % _ : < [ 6 7 | | 8 1 ",
+" 9 0 0 a 3 4 b ' ) ! ~ { ] c / ( d _ : < [ 6 7 | e f f 8 g 1 ",
+" h h i 0 a 3 j b ' ) ! ~ { ] c / ( d _ : * [ 6 k | e f 8 8 8 l m ",
+" n o p i 0 q a 3 j b ' ) ! r { ] c s t d _ : * [ 6 | | e f 8 8 l l l g 1 ",
+" u o p h i 0 q a 4 5 b ' v ! r { ] # s % d _ w x } 6 | | e f 8 8 l l y z m m ",
+" A u u o p h i 0 a B 4 5 b ' ! ~ r C ^ # D % E _ < x 6 7 | | f 8 8 8 l F G G H I m J ",
+" K u u o o p i 0 q a 3 j b ' ) ! ~ { ] ^ L D M E _ < x 6 k | e f 8 8 l y G G N G H g m J ",
+" n u u u o p h i 0 q a 3 j b ' v ! ~ { ] c / ( d _ : * [ 6 k | e f 8 O P Q G Q Q G Q H g m J ",
+" K u u u o p h i 0 0 a 3 4 5 b R v ~ r S ^ # s % d _ w x T 6 | | e 8 8 G G Q N G Q N G N H m ; J ",
+" A u u U V W X Y Z ` ...+.@.#.$.%.! ~ { C ^ L D % E _ < x 6 k | | f G O Q G G G N G G Q G G H m ; J ",
+" u u u &.*.X =.Z -. ...+.;.>.,.$.'.! r { ^ ).D M E w * < [ 6 k | e O G G Q Q Q Q G Q G N G N N m m ; ",
+" u u o &.W X =.Y ` .!.~.+.@.{.$.%.].^./.(.% E _.* < x 6 :.<.6 k [.Q Q Q Q Q Q Q Q Q Q Q Q Q G G }.|.|.1 ",
+" n o o o *.X Y Z -. .!.+.;.#.#.{.$.1.2.3.(.d 4._.< 5.:.< _.:.6.< 7.8.7.7.7.7.Q 8.8.7.8.7.7.8.7.Q Q - - - 9.0. ",
+" o p p p a.Y Z ` .!.!.+.;.>.,.b.c.^.3.L % d d.w e.f.w w w _.w 7.f.7.7.7.7.7.7.7.7.7.7.8.8.7.8.7.8.g.g.h.- 9. ",
+" h p h h i Z -.` i. .+.+.;.,.,.{.j.{ k.l.m.% n.o.p.q.r.s.p.t.u.7.v.w.x.7.w.w.y.y.y.w.y.y.y.z.A.w.7.z.B.B.g.g.g.9. ",
+" 9 h i i i 0 C.D. .!.+.+.;.>.,.$.E.F.G.H.I.( J.d E K.& L.M.N.O.P.Q.O.R.S.y.y.T.w.y.y.y.y.y.w.T.w.y.y.U.V.W.X.B.B.g.1 ",
+" 0 i 0 0 0 q !.Y.Z.+.;.@.#.{.{.`.F.2. +.+++( J.L.L.@+N.v.#+$+%+&+).*+=+).T.y.-+T.T.;+T.y.;+>+;+y.y.y.V.n.V.V.W.X.,+'+ ",
+" 0 0 q q a a Y.)++.;.@.,.{.$.%.!+~+{++ ]+^+/+(+K._+t :+<+[+}+|+1+2+3+4+5+3+6+T.-+7+8+-+-+8+7+>+8+>+;+9+0+a+n.V.V.W.X. ",
+" 2 a a a a B 3 +.b+;.,.{.,.$.'.c+d+e+f+R.g+h+i+j+k+l+m+n+-+o+S o+p+q+r+s+t+u+v+w+x+y+7+7+y+7+z+-+-+A+5+2+2+9+B+a+a+C+W.9. ",
+" 3 3 3 3 4 4 j j 5 b ' {.%.%.'.'.e+D+E+F+R.G+H+I+J+3.K+L+L+M+N+O+P+Q+R+S+T+U+V+W+X+Y+z+z+Z+z+-+`+o+ @ @ @ @.@2+9+B+a+n.W. ",
+" +@4 4 j j 5 5 b b b ' ' ) '.'.c+Z+@@e+#@$@%@2+&@*@=@-@;@>@,@'@S+)@!@~@{@X+]@^@/@(@_@:@S+<@z+<@z+[@p+[@p+p+p+ @.@.@2+B+a+a+'+ ",
+" , 5 b b b b b ' ' R ) v ! '.c+Z+z+}@G+|@1@2@k.3@e+4@;@-@S+5@6@7@8@8@9@0@a@b@c@a@d@e@f@g@h@<@i@T+j@j@h@j@Q+[@p+p+ @.@2+9+B+'+ ",
+" ' ' ' ' ' ' ' ) v v ! ! ~ Z+z+Z+7+|@k@E+l@m@n@3+o@N+p@q@r@s@W+t@u@v@w@w@x@y@z@A@B@C@D@E@F@, t@t@t@t@t@t@, j@j@[@p+ @G@H@2+I@ ",
+" ) ) ) ) ) v ! ! ! ~ ~ r { z+7+8+7+J@K@L@M@e+N@O@o@P@Q@R@^@^@0@e@S@S@T@U@V@y@W@X@Y@Z@`@ #.#+#a@F@F@a@a@a@t@, , j@Q+[@p+@#H@2+ ",
+". ! ! ! ! ! ! ~ ~ ~ r { { C ] 7+>+y.##$#%#m@&#*#=#-#V+;#>#,#'#+#)#y@!#~#C@{#]#^#/#(#_#:#<#[#}#|#1#U@F@F@F@a@a@t@, j@j@[@p+@#2#I@",
+"+ ~ ~ ~ ~ r r r { { S C ] ^ @ T.T.T.>+3#4#6+5#L+6#s@Q@~@7#8#9#0#a#b#c#d#e#f#g#h#i#j#k#l#m#n#[#]#}#}#|#1#U@F@F@a@a@t@, j@o#p+@#p#",
+"@ { { { { { { C ] ] ^ ^ c # L (.D D (.T.q#6+r#s#t#u#s@v#x@8#~#w#b#`@x#y#z#A#B#C#j#n#D#2 E#F#k#G#H#I#J#}#}#|#U@F@F@a@t@, j@o#p+2#",
+"@ ] ] ] ] ] ] ^ ^ c # L / (.D % % t (.y.K#p#L#M#N#O#P#9#y@V@Q#b# #R#S#T#U#[#V#W#X#Y#Z#Z#`# $W#.$G#+$+$I#J#J#}#|#U@F@@$a@, j@o#@#",
+"# ^ ^ c c c # # L / s D t % M d d % :+K@3##$p+$$%$a@8#8#&$Y@I#+$*$=$C#-$;$>$,$'$)$!$~${$]$^$/$($2 _$_$:$+$I#J#J#|#U@F@@$a@t@j@p+",
+"$ / / / / s s D D ( % % M v.E 7.w.<$[$}$|$L#[@o#Q@1$2$3$~#4$5$6$*$7$8$9$0$a$b$c$d${$e$f$g$h$i$j$($k$k$2 2 l$+$I#J#}#U@m$@$a@, o#",
+"% ( ( ( ( t % % M d d E _ _ _ 7.x.y.n$o$n@6+p$z+V+8#q$r$s$t$b#}#u$C#v$w$x$y$y$Z#z$A$B$C$D$E$F$G$/$j$j$($k$2 _$l$I#J#}#U@m$@$H$j@",
+"& % % d d d d E E _ _ _ : w _ d O.$ I$q#7+2#3+J$)@K$D@3$&$L$M$N$I#O$2 $ $P$Q$R$S$T$g$U$V$W$X$R$A$Y$Z$j$j$($k$2 l$+$J#}#U@m$@$, ",
+"* _ _ _ _ _ _ _ _ : w < < * `$ %O..%+%#@@%#%#%Q@$%%%&%K$*%~#=%-%a$O$P$;%>%,%'%)%!%~%{%]%^%/%(%_%R$R$R$Y$Z$:%j$<%2 [%I#J#}#U@m$H$",
+"= : : : : : w < < * x x [ :.6.v.y.y.#@}%-+|%1%2%>#3%>#4%b#5%6%7%8%9%Z#0%g$g$a%b%W$!%c%d%e%f%g%h%i%i%j%R$R$Y$:%<%<%2 -$I#J#k%m$H$",
+"- < < < * * x x x [ T 6 6 :.6.l%m%n%o%5+p%#%q%3%r%s%t%u%v%w%g#x%y%~$z%F$A%)%b%B%C%D%E%F%G%H%I%J%K%K%K%^%j%R$Y$:%<%<%2 -$J#J#U@L%",
+"; [ [ [ [ [ } 6 6 6 6 k T < M%5. %n%N%O%P%|%O+,@,@Q%s$V@R%S%T%f#U%V%0%W%W$X%W$Y%Z%`% &.&+&@&#&$&$&$&J%K%X%i%a%X$:%<%k$[%-$J#k%L%",
+" } 6 6 6 6 6 7 k k | 8.T = %&f.&&. y.0+2@.@*&,@2%2%=&-&w#;&x%>.>&9%,&,&Y$a%'&(%)&!&~&{&]&^&/&(&C%C%C%$&J%K%^%a%_&:%P$2 [%J#k% ",
+" | 7 7 k | | | Q G :.k :&6 6 <&4.[&<$0+#@}&}&|&1&2&-&3&;&4&U%U%>.Z$5&6&7&8&9&0&a&b&c&d&{&e&f&g&h&i&i&C%$&J%K%^%X$j&k&P$[%-$l& ",
+" 1 | | | | | | e e m&n&6 6 T 5.s.`$`$o&p&^ q&r&s&t&=&w#;&u&v&,.@.,&,&h$w&x&Z%y&z&A&B&C&D&E&F&G&H&h&K i&C%$&J%K%j%X$:%P$2 [%I& ",
+" 1 | e e e e f f J&f K&e :.T = < L&4.M&y.[$N&O&,@P&-&Q&*$R&:$S&@.,&T&U&)%V&0&W&X&Y&Z&`& *.*+*@*#*$*%*K &*C%$&**^%X$j&<%2 [%I& ",
+" 8 f f f f 8 8 f Q G = e k = =*5.-*;*L K@>*}&,*-#'*w#_$)*!*>.@.,&Z$~*!%{*K%]*^*/*(*_*:*<*[*@*}*|*1*2*K &*3*J%K%j%_&k&P$4* ",
+" 1 f 8 8 8 8 f f 5*O 8 O f 6*7*L&8*o&h+9*0*a*b*c*8@d*J#e*f*($j$/$g*h*W${*i*j*k*l*l*m*n*o*p*q*r*H&s*t*u*i&&*$&K%^%_&k&P$L% ",
+" 8 8 8 8 8 v*w*8 n&5*8 K&k x*y*z*t y.A*B*C*D*E*>#F*G*_$k$($Z$H*G$g*w&{*I*J*K*L*M*N*O*H&P*Q*R*R*S*T*U*V*W*3*X*^%X$j&P$ ",
+" g 8 l l l w*l 8 Y*w*8 n&Z*`*L& =V..=#@e+*#+=@=W+1$I#:$k$($Z$#=0%h$j%X%$=%=&=*===M*F&}*-=;=>=,=;='=)=!=!=3*X*^%X$j&~= ",
+" 1 l l l l l l 5*w*l 8 {=]== <&^=/=n.(=|%_=:=<=(@1$&$:$k$($Z$[=~$h$w&}=Z%|=A&1=2=3=4=5=6=7=8=[*9=O*)=!=!=3*X*^%X$j&l& ",
+" m l l I l l m g 0=8 8 a=b=c=X.d=e=f=-+-+@=)@t@v#g=+$2 ($j$y$Z#:%h=i=j=D%k=3*l=m=n=o=p=q=r=s=t=h&u=i*v=3*X*^%X$~= ",
+" g I I I I w=F l x=g - y=z=-*:+<+M@A=*#P@q@/@1$T@I#2 ($j$y$B=G$g$C=D=K%E=&=F=G=H=I=J=K=K=L=I=!=)=!=!=3*M=^%j& ",
+" 1 I I I I I m I l N=e O=P=s.o&Q=R=(=K+o@S=T=U=S@I#_$k$j$V=k$W=i$g$X=Y=Z=Z=`= -.-+-@-2*#-$-n V*!=!=i*$&**^%l& ",
+" x=I |.|.I w=%-&-*-8 =---5.;-o&>-L@*&:=,-7@P&a#I#_$k$j$'-)-!-Q$~-C$'&{*{-k=A&]-^-d&/-l*(-_-h&!=:-i*X*^%_& ",
+" N N <-m [-w=I w=}-|-1-2-W.3-[$2+#%4-@=>#9@V@x#U%5-6-b$7-y$8-G$9-X$~%0-~%$&a-b-B&==3*{&{&V*!=!=c-c-d- ",
+" J H G *-e-f-x=|.5*]=6 g---V.h-i-&@P+@=-#P&c@j-U%5->.k-l-m-n-/$0%A%w&o-h%p-{%q-r-A&s-t-W*u-i*i*Z.v-w- ",
+" J ; *-x-F m 0=y-z-A---B-(+C-9+.@|%t#D-E-`.U%,.5-S&8-F-9%!-!-G-i$g*X$D$H-I-I*D=$=$=J-C%(&c-c-K-w- ",
+" J ; L-x=; M-l N-O-P-g-Q-+ 5+|%R-S-T-U-V-j-{.U%U%S&W-F-y$>$!-P$~-X-9-X$o-C$j=K%Y-Z-I*:-`-d- ; ",
+" .;; +;; %-@;#;O-$;--%;$+&;n+K+*;)@a@F@|#J#I#l$2 >$=$F#=;=;=;/$z$~-~-h=b%-;V&K%;;>;c-d-w- ",
+" J ,;1 %-|.}.`*B.X.';p&);n+}&!;;#a@F@U@|#J#I#l$2 ~;l$F#{;];:%^;/;(;_;_;:;j=_;<;[;};|; ",
+" 1 1;%-}.2;g.B.3;p&>-i-}&Q+j@t@a@F@U@}#J#+$[%2 ~;l-F#m-=;c$/;/;4;4;:;5;_;^%_& ",
+" 0.6;#;`*`*B.3;a+>-B* @[@j@, t@@$F@U@}#J#I#-$2 .$V=7;F#m-{;{;c$/;j&8;j&l& ",
+" 9.g.,+W.C+a+B+2+G@p+[@j@, a@@$m$U@}#J#I#-$[%2 9;F#>$0;0;P$P$a;~= ",
+" 1 '+X.W.n.a+9+H@@#p+o#j@, a@@$m$U@}#J#J#-$[%[%2 2 P$P$P$~=l& ",
+" 9.W.a+B+2+H@@#p+o#j@t@a@@$m$U@k%J#J#J#-$[%[%4*L% ",
+" '+'+I@2+2#@#p+o#j@, H$@$m$m$U@k%k%l&I&I& ",
+" I@p#2#@#p+o#j@, H$H$L%L% "};
diff --git a/arts/builder/pics/Synth_ENVELOPE_ADSR.xpm b/arts/builder/pics/Synth_ENVELOPE_ADSR.xpm
new file mode 100644
index 00000000..4408f29d
--- /dev/null
+++ b/arts/builder/pics/Synth_ENVELOPE_ADSR.xpm
@@ -0,0 +1,318 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"64 64 247 2",
+/* colors */
+" c #54E523",
+" . c #364281",
+" X c #24306C",
+" o c #313E7C",
+" O c #303C7B",
+" + c #1E2A66",
+" @ c #2E3A79",
+" # c #505AA1",
+" $ c #1C2864",
+" % c #2C3877",
+" & c #4D589E",
+" * c #2A3675",
+" = c #293474",
+" - c #283473",
+" ; c #253070",
+" : c #334081",
+" > c #41607E",
+" , c #2E3A7C",
+" < c #1E2869",
+" 1 c #2B3679",
+" 2 c #2A3678",
+" 3 c #7C84AE",
+" 4 c #39448A",
+" 5 c #263074",
+" 6 c #232E71",
+" 7 c #303C81",
+" 8 c #202A6E",
+" 9 c #1E286C",
+" 0 c #414F8B",
+" q c #1D286B",
+" w c #2E387F",
+" e c #273278",
+" r c #1A235E",
+" t c #295252",
+" y c #384582",
+" u c #445391",
+" i c #313F7B",
+" p c #3E4D8B",
+" a c #3D4B8A",
+" s c #2D3977",
+" d c #3B4988",
+" f c #8C95BD",
+" g c #384585",
+" h c #55689E",
+" j c #858DB6",
+" k c #303D7D",
+" l c #1F2B69",
+" z c #2F3B7C",
+" x c #2B3778",
+" c c #45DA16",
+" v c #368640",
+" b c #295A44",
+" n c #333F83",
+" m c #313D81",
+" M c #43528C",
+" N c #1E296B",
+" B c #1D276A",
+" V c #3C9E35",
+" C c #2C3E72",
+" Z c #293379",
+" A c #2D6341",
+" S c #283378",
+" D c #263176",
+" F c #3A586F",
+" G c #252F75",
+" H c #242F74",
+" J c #41D015",
+" K c #232D73",
+" L c #3C4A88",
+" P c #4A5899",
+" I c #374483",
+" U c #354281",
+" Y c #33407F",
+" T c #409349",
+" R c #2E3A7A",
+" E c #1C2665",
+" W c #1B2464",
+" Q c #364285",
+" ! c #232E6F",
+" ~ c #323E81",
+" ^ c #434E95",
+" / c #212A6D",
+" ( c #303C7F",
+" ) c #3CBE16",
+" _ c #3E4A90",
+" ` c #2C387B",
+" ' c #1C2668",
+" ] c #1B2667",
+" [ c #293478",
+" { c #273276",
+" } c #394981",
+" | c #253074",
+". c #384580",
+".. c #263E61",
+".X c #37724D",
+".o c #37417F",
+".O c #222C71",
+".+ c #212C70",
+".@ c #202A6F",
+".# c #1F2A6E",
+".$ c #42518D",
+".% c #2E3A80",
+".& c #1E286D",
+".* c #1D286C",
+".= c #3E4D89",
+".- c #4E5D9C",
+".; c #3D4B88",
+".: c #3B4986",
+".> c #3A4785",
+"., c #384583",
+".< c #465594",
+".1 c #445192",
+".2 c #425190",
+".3 c #323F7D",
+".4 c #323D7D",
+".5 c #212B69",
+".6 c #2F3B7A",
+".7 c #4CD125",
+".8 c #3A4788",
+".9 c #4C5893",
+".0 c #273372",
+".q c None",
+".w c #263171",
+".e c #253666",
+".r c #222D6D",
+".t c #6E78A7",
+".y c #414D92",
+".u c #2F3B7D",
+".i c #1F296A",
+".p c #3F4B90",
+".a c #405287",
+".s c #2B3779",
+".d c #3B458C",
+".f c #283376",
+".g c #273375",
+".h c #374388",
+".j c #394680",
+".k c #364187",
+".l c #242F72",
+".z c #343F85",
+".x c #222B70",
+".c c #34427B",
+".v c #6670B6",
+".b c #1E296C",
+".n c #41508B",
+".m c #1D276B",
+".M c #404E8A",
+".N c #326747",
+".B c #3CAE22",
+".V c #3E4C88",
+".C c #2A357B",
+".Z c #3C4A86",
+".A c #44D219",
+".S c #465693",
+".D c #455492",
+".F c #33407D",
+".G c #42508F",
+".H c #404E8D",
+".J c #2B3A75",
+".K c #3C4889",
+".L c #2B3675",
+".P c #3A4887",
+".I c #384685",
+".U c #495699",
+".Y c #354282",
+".T c #25306F",
+".R c #36625B",
+".E c #43A739",
+".W c #5560A8",
+".Q c #344777",
+".! c #3DC215",
+".~ c #2D387A",
+".^ c #3B923B",
+"./ c #518F5E",
+".( c #384288",
+".) c #385274",
+"._ c #47529A",
+".` c #73A386",
+".' c #253072",
+".] c #475790",
+".[ c #232E70",
+".{ c #171F57",
+".} c #1F2A6C",
+".| c #6C75A7",
+"X c #424F8B",
+"X. c #2D387D",
+"XX c #1C2669",
+"Xo c #2C367C",
+"XO c #2B367B",
+"X+ c #283478",
+"X@ c #253075",
+"X# c #485794",
+"X$ c #45C023",
+"X% c #242E74",
+"X& c #232E73",
+"X* c #212C71",
+"X= c #34417D",
+"X- c #577192",
+"X; c #202A70",
+"X: c #1F2A6F",
+"X> c #3EB81C",
+"X, c #273370",
+"X< c #364582",
+"X1 c #364382",
+"X2 c #41CB18",
+"X3 c #5763A9",
+"X4 c #344180",
+"X5 c #303D7C",
+"X6 c #213260",
+"X7 c #2D3979",
+"X8 c #3D498C",
+"X9 c #3D478C",
+"X0 c #4D6295",
+"Xq c #2B3777",
+"Xw c #2A4962",
+"Xe c #293575",
+"Xr c #263172",
+"Xt c #42C71C",
+"Xy c #344183",
+"Xu c #737DA3",
+"Xi c #44548C",
+"Xp c #212B6D",
+"Xa c #4FDC22",
+"Xs c #1F296B",
+"Xd c #1E296A",
+"Xf c #2D397C",
+"Xg c #1D2769",
+"Xh c #1B2567",
+"Xj c #293578",
+"Xk c #4B5C96",
+"Xl c #4A5A95",
+"Xz c #273176",
+"Xx c #263175",
+"Xc c #465691",
+"Xv c #36427E",
+"Xb c #222D71",
+"Xn c #455490",
+"Xm c #212B70",
+"XM c #44528F",
+"XN c #33407B",
+"XB c #1F296E",
+"XV c #1E296D",
+"XC c #415782",
+"XZ c #2C377E",
+"XA c #4F5E9D",
+"XS c #5BF220",
+"XD c #3A8D3F",
+/* pixels */
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.qXr { [ Z e D HX&.O 8 N '.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.L.4.(.k n.%XZ.C Z eX@ H K.OX*X;X;XB W r.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.K ^.pX9 4.k n.%XZ.C Z eX@ H K.OX*X;.@X:XBXB 9 r.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.o # &._ ^.p.d 4.z m.%XZ.C S eX@X% K.OX*X;.@X:XBXV.&.& 9XX r.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.WX3 # &._ ^.p.d.(.z m.%XZ.C SXzX@X% K.OXmX;.@.#XBXV.& 9 9.*.m '.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q P.vX3.W # &._ ^ _.d.h.z m.%XZ.C S DX@X% K.OXmX;.@XBXBXV.& 9 9 q.m.mXX r.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.v.v.vX3.W # &._ ^ _.d.k.z 7 wXo.C e D GX% K.OXmX;.@XBXBXV.& 9 9.m.m.m B BXh.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.qX .v.v.vX3X3.W #._ ^.y _ 4.k n 7 wXo Z e D GX&XbX*Xm.@X:XBXB.& 9 9.*.m.m B B B B '.{.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.9.v.v.v.vX3.W # &._ ^.p.d 4.k ~.%X..C Z e D HX&XbX*Xm.@.#XBXV.& 9 9 q.m.m B B BXgXX '.{.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.9.v.v.v.vX3X3.W #.U._ ^ _.d.h.z m.%XZ.C SXzX@X% K.OXmX;.@.#XBXV.& 9 9 q.m.m B B BXgXXXX '.{.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.9.v.v.v.v.vX3.W # &._ ^.pX9 4.k n 7 wXo.C e D GX% K.OXm.@.@XBXBXV 9 9 q.m.m B B BXgXgXXXX ' E.{.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.qX .v.v.v.v.vX3.W # #.U._ ^ _.d.(.z ~.%X.Xo Z e D GX&XbX*Xm.@.#XBXB.& 9 9 9 9.m.m B B BXgXgXX ' ' E.{.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.v.v.v.v.vX3X3.W # &._ ^.p.d Q.z n 7 wXZ.C SXzX@ H K.OX*X;.@.#XBXBXVXV 9.b.b.b 9 9 9 9 < BXgXg ' ' E.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.v.v.v f fX3X3.W # &._ ^.pX8.8.o ~ m.%X.Xo Z e D GX& K.OXmX;.@.@ 8 8 8 8 8 8 8 8 8 8.}.}.bXs 9 < <XgXg W.q.q.q.q.q.q",
+".q.q.q.q.q P.v.v.v f f.W.W # &._ ^ ^X8.;.).R.Q z.~XZ.C Z { D HX& K.O.+.+.+.+.+.+.+.+.+.+.x.x.+XpXpXpXpXp.}.}Xs.iXd < r.q.q.q.q.q",
+".q.q.q.q.q.v.vX3X3X3.W # # &._._ ^.p dXC./.E.R C xXo Z e D GX%X& K KXbXbXbXbXb 6 6 6 6 6 6 6.[.[.[ ! !.r.rXpXpXp.}.} <.q.q.q.q.q",
+".q.q.q.q.WX3X3X3.W.W # # &._ ^ ^.pX9.>XC./.E.R C = Z ZXz D HX%X&X&X& H H H H H H H H.'.'.'.'.'.'.l.l.l.[.[ !.r.rXpXp.} <.q.q.q.q",
+".q.q.q.oX3.W.WX8 # f f.U._ ^ ^.pX9.d yXC./.7.X..X, SXz DX@X@ H H | | | | | 5 5 5 5 5 5 5XxXxXxXrXrXrXr.' ; ;.[ ! !.r.rXp $.q.q.q",
+".q.q.q # # # #.8 & f f._ ^.y _X9.d.( } F./.7 T.N...0 5Xz D DXxXxXxXxXx { { { {.f.f.f.f.f.f.fXe.g.g -.g.g.0XrXr ; ; ! !.r l.q.q.q",
+".q.q.q & & & &.(._ f j ^.p _.d 4.h Q.) F T .7.E bX,.w { { { { { { { S SX+ [ [XjXjXj 2 2 2 2XjXjXeXeXeXe - -.0.0Xr.w ; ! !.q.q.q",
+".q.q.K._._._._ n ^ j j _X9.d 4.kXy.j.R T.EXSXaX$.N.e.T { S S S SX+ [Xj 2 2 2 2 1.s.s.s.s.s.s.s x x xXq 2XeXeXe - -.0.w.w.T +.q.q",
+".q.q ^ ^ ^ ^ ^.y.p _X9.d 4.(.k.z.3.)./.7 XS .7 AX6 X { { [ [Xj 2 2 2.s.s.~ ` ` `XfXfXfXfXf.~.~.~X7.~ % xXqXqXeXe = -.0.w.T.q.q",
+".q.L.p.p.p _ _ _.d.d 4.(.k.z n m o.)./ XSXSXS V b...0.g 2 2.CXO.s ` `XfXfXf , , , z z z z z z R R R.~X7.~ % %.L *Xe -.0X,.5.q",
+".q.4X9.d.d.d.d.~ 4 3 3.z.z ~ 7.% @.) T XSXSXSXS.7.B bX, -XO.s ` ` `XfXf ,.u.u.u ( ( ( k k k k k kX5 z z z R @X7 % %.LXe = -.5.q",
+".q.( 4 4.(.h.k 1.k 3 3 ~ 7 7 wX. C.) T XSXSXSXS.7X$ A.e -XO `XfXf ,.u.u.u ( ( ( ~ ~ ~ ~ ~ ~ ~ ~ ~.3.3.4 kX5 z.6 @X7 %Xq.LXeX,.q",
+".q.k.k.z.z.z.z.f ~ 3 3.% wX.Xo `.QXw T Xa.7 A.eX,.s ` , ,.u ( ( ( ~ ~ ~ : : : : : : : : : : Y Y.3 o oX5 z R @X7 %.LXe.q",
+"Xr n n m m m 7.g.% 3 3X.XZXo 2 C.R v.E X$.^.^.^.BXa.^ b.. %X7.u.u ( m ~ ~ : : :XyXy.Y.Y.Y.Y.Y.Y.Y UX4X4 : Y.3.3 o O.6 @X7 %.L -",
+" {.%.%.%.%.% w wX.XZXoXo.C Z - C.X.7Xa XDXwXwXw.^XaX$.B b C s ( m ~ ~ : :Xy.Y Q Q Q Q I I I I IX1X1X1.Y UX4X4 Y.3 o o O.6 @ % *",
+" [XZXZXZXZXZXoXo.C.C.C Z S eX, C TXaXa TXwXwXw TXaXtX> A C C @.6 O i iXN.3XNX=X=XvXvXvXvXvX1 I g g., IX1X1 UX4 Y.3.3 o O.6 @.L",
+" Z.C.C.C.C.C.C.[ ZXuXu eXz DX,Xw T TXwXw.Q T.7XtX2 A.. C C C C C.Q.Q.Q.Q.Q.Q.Q.Q.Q.Q.Q. .I.I.>.I.I.,., IX1 UX4 Y.3 o O.6 %",
+" e Z Z S S S e.x eXuXu DX@ H.eXwXD .7X$.X C C C.R v.B.A V A A A A A.N.N.N.N.N.N.N.N.N.N.N.N F }.> d.P.8.>.I.,.,X1 .X4 Y.3 o O @",
+" D e e eXz D DXp DXuXu G H ;..XwXDXaX$.B bX, CX7.QXwXD c.AX2.!.!.!.!.!.! ) ) ) ) ) ) ) )X>.B vXC.Z L L d d.P.>.I., I .X4 Y.3 o.6",
+" HX@X@X@X@X@ G / HXuXuX&.[.e.R v.EXa.^ b.. - * ` CXwXD.A.A.A J J JX2X2X2X2X2X2X2X2X2X2X2XtXt T >XC a a a L d.P.>.I., I .Xv Y.3 O",
+"X& H HX%X%X%X%X&X& K KXb X.. v.7.7.A AX6 XXj 2 `.J.Q vX$.A c c c c c c c c c c c c c c c c.AXD F >.M p p a a L d.P.>., I .X4X= o",
+".O K K K K K KXbXb.O.OX*.r.. vXa.AXt A.eX,Xj `Xf @ i.R T T T T T T T T T T T T T T T TXD.B.A.E T.RXC.M.H p p a.; d.:.>., I .X= o",
+" 8.O.O.O.O.O.OXdX*.t.tXm l.. vXa.AXt A.eX,.s `Xf , k.Q.).) F F F F > > > > > > > > > > F T.AXtX$ T >.a.G.H.H p.=.; d.>.> yX1Xv.3",
+" NX*X*X*XmXmXm <Xm.t.t 8.eXwXD cXtX> b.eX,.sXf ,.u (.3.c.c.c. } } } }.Z.Z.a.a.a.a.aXC >.XX>XtXt T >XC.$.$.$.H p.=.; d.>., y .X=",
+" 'X;X;X;X;X;X;Xg.@.t.t.}.. t V cX>.B b X -.~Xf.u ( ~ :Xy Q g g.8.K aX8 p.H.G.G.1 u u.n.a.R.BX>.A T > >Xn.2XM.$ 0 p.=.;.:.> y. X=",
+".qX;.@.@.@.@.@Xg.#.t.t.e.R.^.B.A.^ b..X,Xe `Xf.u ( ~ :.Y Q g.8.8.K a p.H.H.G.1 u.D.<XM M F.N.^X2.E T./.]Xi u.$.$ 0.M.V.Z.>.,. .q",
+".qXBX:X:.#XBXBXBXBXB l.. vXtXtX2 AX6 XXe 2 ` ,.u ( ~ : Q Q g.8 dX8 p.H.H.G.1 u.D.<.<.<X#.a.).N )XtX$ T >Xi.D u.$X .M.=.;.Z.>. .q",
+".q WXBXBXBXBXBXBXVXV l.. v.AX2.! AX6 XX+.s ` , ( ~ :Xy Q g.I.8.K a p.H.G.2 u.D.<.<X#XlXlXiXC.N.BX>Xt T >XC.D.DXM.$ 0.M.V.Z.>XN.q",
+".q rXBXVXVXVXV '.&.tXu..XD c J.! b.e.TXj.sXf , ( ~ :Xy Q g.8.8 aX8.H.H.G u u.<.<X#XlXlXk.]Xi.R.BX>.A T > >XcXnXnXMX .M.=.;.:.c.q",
+".q.q 9.&.&.&.& ' 9.|Xu.. v c J.! b.e.T 2.sXf z ( ~ :.Y Q g.8.K a p.H.G.2 u.D.<X#XlXlXk.-Xl.9 F.N.^.!.E T./.]Xn.DXM.$ 0.=.;.>.q.q",
+".q.q r.& 9 9 9 ' 9.tXu.. v c J.! b.e.T 2.sXf z k ~ :.Y I g.8 d a p.H.G.2 u.<.<X#XlXk.-.-.-.-XiXC.X )XtX$./ >XiXcXM.$ 0.=.V.c.q.q",
+".q.q.q 9 9 9 9 ' +Xu./ v.B c.!.B b.e.T 2.sXf z k ~ :.Y I g.8 d a p.H.G u.D.<X#Xl PXk.-.-XAXA.9XC.X.BX>Xt T >XCXcXnXMX .M.V.q.q.q",
+".q.q.qXX.* q.m.m +.. vXt c c.!.B b.eX, 2.sXf z k ~ :.Y I.I.8 d a p.H.G u.D.<X#Xl PXk.-XAXAXAX0.].R VX>X2 T > >.]XnXMX .M.Z.q.q.q",
+".q.q.q r.m.m.m.m +.. v J J J.^ bX6 XX,Xj.s.~ z k ~ :.Y I g.8 d a p.H.GXM.D.<X#Xl P.-.-XAXA hXAX0 F.X.^.!.E T./.]XiXMX .M.j.q.q.q",
+".q.q.q.q '.m.m E $X-./ c J.! AX6.5.w.gXj x.~ z k ~ :.Y I g.8 d a p.H.GXM.D.<X#Xl PXk.-XAXA h h h.]XC.X )X>X$ T >XiXMX .V.q.q.q.q",
+".q.q.q.q.qXX B E $X-./ )X>.B bX6.5.'.gXe x.~ R k ~ :.YX1 g.> d a p.H.G.2 u.<X#XlXlXk.-XAXAXA h hXlXC.X.B.B.B T >.a M.M.q.q.q.q.q",
+".q.q.q.q.q r B B $X-./X$.B.B bX-.|XuXuXe x 3 3 3 3 : U 3 3 j j L p j j j j.S.< f f f f.-XA f f f j.].R.E.EX$.` >.a M }.q.q.q.q.q",
+".q.q.q.q.q.qXh B E.|X-././ bX6.|.tXuXuXeXq 3 3 3 3 YX4 3 3 j j L a j j j j.D.S f f f f.-.- f f f f.9 F./././.`XC.a.M.q.q.q.q.q.q",
+".q.q.q.q.q.q.q 'Xh ] $ r r $ +Xp !.'.gXe 2 %.~ z.4 YX4.Y I.I.8 d a p.H.GXM u.S.<X#XlXlXk.-.-.-.-XkXk.].aXCXC.aXi.n.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.{ 'XhXh r r E.i <.i.5.r -Xe.T.TX,.0.3 :.L % % @ d L O o.3.FXM.DXv .X< yXlXk.>.>.> }Xl.].c.c M MX .j.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.{ ' ' 'Xg <.}Xp ! ;Xr -XeXq.~ RX5 o YX4X1.,.I.P d a p.H.$XM u.D.S.SX#X#XlXlXlXlXlXlX#X#XcXn M.Q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.{ E ' ' <XsXp.r.[Xr.0XeXq % @ z o.3X4 U I.,.>.P L a p.H.$.$ u.DXc.SX#X#X#X#X#X#X#.SXcXn M.Q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.{ E 'Xg.iXp.r ! ;.0 -Xe %X7.6X5.3 YX4X1.,.I.> d.;.= p 0.$.$XMXn.DXc.S.S.S.SXcXcXnXn M.Q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.{ EXgXd.}Xp ! ;Xr -Xe.L % @ z o.3 Y UX1.,.I.P d.;.= p 0X .$XMXMXMXnXnXnXnXnXMXM.n.j.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q W <.}Xp.r !.w.0 = * %X7 R O o.3X4 . I.,.>.: d.;.=.M.M 0X .$.$XMXMXMXM M M.M.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q r <.}.r ! ;.w -Xe.L % @.6 o.3 YX4 . I.,.>.> d.;.V.=.M.M 0 0X X X X .M }.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q <Xp.r !.w.0 -XeXqX7 @ O o.3 YX4 . I.,.>.>.:.Z.;.V.=.=.=.M.M.M.V.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q $ l !.T.w.0 =.L %X7.6 O o.3 YXv . I y.,.>.>.Z.Z.;.;.V.V.Z.j.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q +.TX, -Xe.L % @.6 O o.3 YX4 .X1 y y.,.>.>.:.>.c.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.5.5X,Xe.L % @.6 O o.3X=X=Xv .. . . XN.c.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q",
+".q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q - *.L % @.6 O o o.3X=X=.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q"
+};
diff --git a/arts/builder/pics/Synth_FILEPLAY.xpm b/arts/builder/pics/Synth_FILEPLAY.xpm
new file mode 100644
index 00000000..76730eb0
--- /dev/null
+++ b/arts/builder/pics/Synth_FILEPLAY.xpm
@@ -0,0 +1,312 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"64 64 241 2",
+/* colors */
+" c #394684",
+" . c #FEFE82",
+" X c #FCF8EA",
+" o c #354280",
+" O c #664E4D",
+" + c #3C3352",
+" @ c #303C7B",
+" # c #2F3C7A",
+" $ c #2E3A79",
+" % c #282A5C",
+" & c #484C82",
+" * c #FFFFFA",
+" = c #2C3877",
+" - c #4D589E",
+" ; c #2A3675",
+" : c #FCFBF7",
+" > c #975F27",
+" , c #FFFEF0",
+" < c #384486",
+" 1 c #697095",
+" 2 c #222C6D",
+" 3 c #212C6C",
+" 4 c #303C7E",
+" 5 c #5A6390",
+" 6 c #FFE8B1",
+" 7 c #1C2667",
+" 8 c #EB9616",
+" 9 c #293477",
+" 0 c #FFFBE9",
+" q c #354086",
+" w c #475591",
+" e c #FEA50E",
+" r c #5363A0",
+" t c #1F2A6D",
+" y c #42518C",
+" u c #2F3A80",
+" i c #1E286C",
+" p c #F5DC68",
+" a c #50619D",
+" s c #2D387E",
+" d c #FFCD68",
+" f c #2B367C",
+" g c #1F2763",
+" h c #FFFEF6",
+" j c #283279",
+" k c #FEF2CA",
+" l c #FCBF3A",
+" z c #1A235E",
+" x c #384582",
+" c c #4C588F",
+" v c #232E74",
+" b c #DFE0E7",
+" n c #333F7D",
+" m c #F7E563",
+" M c #626DB5",
+" N c #3E4D8B",
+" B c #5C67AF",
+" V c #2A3574",
+" C c #FFF9C4",
+" Z c #273171",
+" A c #FFE799",
+" S c #354182",
+" D c #545FA7",
+" F c #FEDE8E",
+" G c #414E87",
+" H c #1D2767",
+" J c #3A458A",
+" K c #3A4680",
+" L c #323D82",
+" P c #E7CE8B",
+" I c #212B6E",
+" U c #1D276A",
+" Y c #FB9E0A",
+" T c #2C377C",
+" R c #353971",
+" E c #4C5C98",
+" W c #293379",
+" Q c #495895",
+" ! c #475693",
+" ~ c #232D73",
+" ^ c #222D72",
+" / c #FFA811",
+" ( c #455491",
+" ) c #212B71",
+" _ c #202B70",
+" ` c #43528F",
+" ' c #323E7B",
+" ] c #FEC548",
+" [ c #1D2663",
+" { c #2C3875",
+" } c #3A4886",
+" | c #FEFDB8",
+". c #374483",
+".. c #202A69",
+".X c #2F3A7B",
+".o c #1B2464",
+".O c #FDAD1F",
+".+ c #384287",
+".@ c #263072",
+".# c #FFFDE7",
+".$ c #253071",
+".% c #434E95",
+".& c #1E286A",
+".* c #9A8570",
+".= c #1C2668",
+".- c #4E5D99",
+".; c #485793",
+".: c #FCF487",
+".> c #202A6F",
+"., c #1F2A6E",
+".< c #C3822B",
+".1 c #1E286D",
+".2 c #1D286C",
+".3 c #2E3976",
+".4 c #3B4986",
+".5 c #FEF461",
+".6 c #FFCD55",
+".7 c #DC8715",
+".8 c #323F7D",
+".9 c #313D7C",
+".0 c #D89632",
+".q c #FEFD6E",
+".w c #FFFEFA",
+".e c #283373",
+".r c #273372",
+".t c None",
+".y c #39447D",
+".u c #FED678",
+".i c #242F6F",
+".p c #47548E",
+".a c #333F81",
+".s c #232D6E",
+".d c #414D92",
+".f c #FFD16F",
+".g c #1F296A",
+".h c #1E2769",
+".j c #2D397B",
+".k c #3E4A85",
+".l c #495893",
+".z c #563D40",
+".x c #242F72",
+".c c #61689A",
+".v c #232D71",
+".b c #FEB92F",
+".n c #212B6F",
+".m c #303B81",
+".M c #42508C",
+".N c #2E397F",
+".B c #1D276B",
+".V c #3E4C88",
+".C c #2A357B",
+".Z c #3D4A87",
+".A c #2C3673",
+".S c #263177",
+".D c #242F75",
+".F c #374481",
+".G c #787EA3",
+".H c #6872BB",
+".J c #B26E23",
+".K c #FFEFB7",
+".L c #232C6A",
+".P c #313C7B",
+".I c #FEFAD7",
+".U c #333973",
+".Y c #F69809",
+".T c #384685",
+".R c #564C67",
+".E c #5964AC",
+".W c #FFCC43",
+".Q c #404A90",
+".! c #FFFFFC",
+".~ c #FEB321",
+".^ c #3C468C",
+"./ c #D0B16E",
+".( c #283475",
+".) c #3B4781",
+"._ c #263273",
+".` c #2B336E",
+".' c #171F57",
+".] c #333E83",
+".[ c #45538E",
+".{ c #1E286B",
+".} c #1D286A",
+".| c #FFFFFF",
+"X c #1C2669",
+"X. c #4F5F9B",
+"XX c #4C5B98",
+"Xo c #3C4985",
+"XO c #C2791E",
+"X+ c #273277",
+"X@ c #483B52",
+"X# c #263076",
+"X$ c #253075",
+"X% c #465592",
+"X& c #7C5339",
+"X* c #222C72",
+"X= c #F3F3F3",
+"X- c #34417D",
+"X; c #343F7D",
+"X: c #202A70",
+"X> c #42518E",
+"X, c #404F8C",
+"X< c #3C4988",
+"X1 c #C8C7C2",
+"X2 c #364382",
+"X3 c #9E6D39",
+"X4 c #5761A9",
+"X5 c #252F6E",
+"X6 c #344180",
+"X7 c #3E4B8D",
+"X8 c #3C3C6A",
+"X9 c #2B3777",
+"X0 c #4A559C",
+"Xq c #FFD64F",
+"Xw c #475399",
+"Xe c #A8A9B6",
+"Xr c #FFDE66",
+"Xt c #FFFF73",
+"Xy c #1F296B",
+"Xu c #FDA20B",
+"Xi c #3E4990",
+"Xp c #1D2769",
+"Xa c #3F4C87",
+"Xs c #525F93",
+"Xd c #4B5A96",
+"Xf c #38438A",
+"Xg c #4A5A95",
+"Xh c #364188",
+"Xj c #FFE869",
+"Xk c #475692",
+"Xl c #44528F",
+"Xz c #34407C",
+"Xx c #292F64",
+"Xc c #FDB52F",
+"Xv c #43528E",
+"Xb c #1F296E",
+"Xn c #1E296D",
+"Xm c #999593",
+"XM c #404E8B",
+"XN c #2A357C",
+"XB c #7B6865",
+/* pixels */
+".t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.@X+ 9 W j.S.D v ).>.{ H.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.A.PXf q.] u s f W.SX$.D ~X* ) _X:Xb.o z.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.tX<.%.d.^Xf q.] u s f W.SX$.D ~X* ) _.>.>XbXb.{ z.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t o - -Xw.%.Q.^Xf q L u sXN j.SX$ v ~X* ) _.>.>XbXn.1.1 iX z.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.tX4X4 D -Xw.%.Q.^Xf q L u s.C j.SX$ v ~X* ) _.>.,XbXn.1 i i.2.B.=.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.t.t.t.t.t.t.tXX M BX4 D -Xw.%Xi.^Xh q L.N T.C j.SX$ v ~X* ) _.>XbXbXn.1 i i.B.B.BX z.t.t.t.t.t.t.t.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.t.t.t.t.t.t.H M B.E D -X0Xw.dXi JXh.].m.N f.C j.SX$ v ~X* )X:.>XbXbXn.1 i i.B.B.B U U 7.t.t.t.t.t.t.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.t.t.t.t.M.H.H M BX4 D -X0.%.dXi JXh.].m.N f W jX#X$ ~ ^ ) ).>.>XbXb.1 i i.2.B.B U U U U.=.'.t.t.t.t.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.t.t.t c.H.H M B.E D D -Xw.%.Q.^Xf q L u s f WX+X#.D ~ ^ ) ).>.,XbXn.1 i i.B.B.B U U UXpX 7.'.t.t.t.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.t.tXs.H.H.H M BX4 D -X0Xw.%Xi J.+.] L u T.C j.SX$ v ~X* ) _.>.,XbXn.1 i i.B.B.B U U UXpX X 7.'.t.t.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.t c.H.H.H M B.E D D -Xw.%.Q.^ J. .].m.N f.C j.SX$ v ~X* ).>.>XbXbXn i i.B.B.B U U UXpXpX X .= 7.'.t.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.M.H.H.H M B.EX4 D -X0XwX7.).RXBXBXBXBXB &.yXz.U.3.A V Z.i.s I.>Xb.1 i i i.{.B.B U U UXpXpX .=.= 7.'.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.H.H.H M B BX4 D -X0Xw.%Xo.y O.<.<.<.*XmXm.G.G.c 5Xs.p.k.y.P V.@.s IXn i i i i.{.{.{.{.& UXpXp.=.= 7.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.H.H M M B.EX4 D - -Xw.%.d.yX@ O.J.<.<./X1X1X1X1XeXeXeXeXe.G.G.G.G 1 1.c.c & R.`XxXx %XxXx...hXp.&XpXp.o.t.t.t.t.t.t",
+".t.t.t.t.tXX M M B B.EX4 D - -X0.%.dXiX8 OX&X3.<.0./.I X :X=X=X=X=X=X= b b bX1X1X1XeXeXe.G 1.R.RX@X@X@Xx...gXy.g.&.& z.t.t.t.t.t",
+".t.t.t.t.t M B B.EX4 D D - -X0Xw.%Xi JX@X& >.7.0.0 P 0 ,.! : : : : :X=X=X=X= b b b b bX1X1Xm.*X3X&X&X&X@.L.. I 3XyXy.h.t.t.t.t.t",
+".t.t.t.tX4 B.EX4 D D D -X0Xw.%.%.Q.^ <X@.JXO.Y.O l F h.w.|.|.|.|.|.|.|.|.|.|.|.|.|.|.| h , FXc.0XOXOXO.zXx.L.s.s 3 3 3.h.t.t.t.t",
+".t.t.t oX4X4 D D D - -X0Xw.%.%.Q.^.+X;.zXO.7.YXc d 6.w.!.|.|.|.|.|.|.|.|.|.|.|.|.|.|.| ,.# P.0.<.J.J.J.zXx.L.i.i.s.s 2 3 [.t.t.t",
+".t.t.t - D D - - -X0XwXw.%.dXi.^ JX; RX& 8.Y Y ] F k.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.| 0.I.f 8.<X3X3.JX@X5.i.@.$.i.i.s.s...t.t.t",
+".t.t.t - - -X0X0XwXw.%.%.dXi.^ JXh RX@X&.Y.YXu.6 6.I.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.I.K d 8.7.<.J > +.r.r.e.r.@.$.$.i.s.t.t.t",
+".t.tX<XwXwXwXw.%.%.%.dXi.^.^XfXh.]X8.z > Y YXu d k 0.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.| k 6.6 e Y.Y.JX& +.( V.(.(.e.r Z Z.i g.t.t",
+".t.t.%.%.%.%.d.d.QXi.^.^ JXfXh.].m +X&.J Y e.O.u.# ,.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.! * k F ] e Y.Y >.zXxX9X9X9 ; V V.e.r ZX5.t.t",
+".t.A.d.Q.QXiXiXi.^.^ JXfXh q.].m.NX@ >XO Y.O.b F h.w.|.|.|.|.|.|.|.|.|.|.|.|.|.|.| * h 6.f l / e YX&X@.` $ = = =X9 ; V.e.e Z...t",
+".t.P.^.^.^.^ J JXfXhXh q.] L.m.j.A.zXO.7 YXc.6 6 h.w.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.w h A.6.b / e 8X&X8.`.X $ $ $ = =X9 V V.e.L.t",
+".tXfXfXfXfXhXhXh q q.] L.m.m.N V.`.z.7.Y Y l.u.K.!.!.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.w , A ].b / 8.7 O R.U.9 @.X # $ = =X9 ; V.r.t",
+".t q q q q q.].] L L.m u.N s T.` +X&.Y.YXu.6 A k.!.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.| ,.# F l.~.O 8.< O.U.P.8.8.9 @.X $ $ = = ; V.t",
+".@.].] L L L.m.m u u.N s T f.CXxX@ > Y Y e d k.#.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.| 0.I.u.b.~.~.0.<.RXz n.a.a.8.8.9 @ # $ = = ;.e",
+"X+ u u u u.N.N.N s T f f.C j j +X&.J Y e.O.u.I 0.!.!.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.# C.u.b.~.~.<X3.R oX6 oX6X6X6.8.8.P # $ $ = V",
+" 9 s s s s T f f f.C.C W j.SX$ + >XOXu /Xc F.# , *.!.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.I 6.f.b.b.~.<XB.R. . X2X2 oX6X6 n.8.9 # $ $ {",
+" W f fXN.C.C.C W W j jX+.S.$.s.z.J.7Xu /.O.6 A 6.K k k.I.# 0 , h.w.w.!.!.|.|.|.!.! k AXr.b.bXcX3 OX8.T.T.T. . X2 oX6 n.8 ' # $ =",
+" j W W j j j j jX+.S.SX#X$.sXx.z.7.YXu e /.b ].6.u F A.K C.I.# 0 h h h.!.|.|.|.! *.K.:Xq.b.b.bX3.R.y } } }.T.T. X2 oX6 n.8.9 # $",
+".S.S.S.S.S.S.SX#X#X$X$X$.D.L %X& 8.YXu e /.~ l l ].6 d.f.u F F A A 6.K C k.I.#.#.# 6XjXq l l lXB.R.yX<X<.4 }.T x.F o o n.8.P $",
+".DX$X$X$X$X$X$X$.D v v ~ ~ % + > Y Y e e /.O.~.~.~.~.~.~.b.b.W.WXqXr.u.: A.K C C.I AXrXq.W l lXB &.) N.VX<X< } }.T x.F o oX-.8 #",
+" v.D.D v v v v ~ ~ ~ ~ ^ ) %.z > YXu e /.~ ].f.f d d d.6.6.6XqXqXqXrXrXrXj.:.:.: AXjXqXq.W l.0XB GXa N N N.VX<.4 }.T x.F o oX-.P",
+" ) ~ ~ ~ ~ ~ ~ ^ ^X*X* _.> + >XOXu e e.O.b.u k.K 6 6 6 A F.uXrXrXqXqXqXqXqXqXqXq mXqXqXqXq ]./XB GXMX,X, N N N.VX< } x.F oX- '",
+".>X*X*X*X*X*X* ) ) ) )Xy..X@.J.7 e e /Xc ].u.K A F A.K.K k.K 6 6 6 A A A.:XjXj mXqXqXqXqXq./.*.R.M.MX>X>X,X, N.V.VX< } x.F o n",
+".{ ) ) ) ) ) ) ) ) _.>.& g.z.7 8 e / / l d F 6 F d A k.#.! h , , , 0.#.I C A.:XjXqXqXqXqXq./.* 5Xl (Xl `X>.MX, N.V.V.4 } x oXz",
+" H _ _ _ _ _X:.>.>.>.> g %X& 8.Y e / / ] F F F.f ] F k.#.! * h h h h ,.#.I |.:Xj.5.5.5 m m./XB &X%X% ( ( ` `.MX, N.V.Z.4 x.FX-",
+".tX:.>.>.>.>.>.>.,.,Xb % +X& YXu e /.O.6 A F.f.6 l.u k.#.|.|.|.|.|.|.| * h C.:Xt.5.5.5.5 m.* 1 &.; ! !X% (XlX>.MXM N.V.Z } x.t",
+".tXb.>.>.,XbXbXbXbXbXb %.z > YXu /.O.~ d 6 F.6.6.W F.# ,.|.|.|.|.|.|.| * , | ..q.5.5.5 m p.* 5.p Q Q Q !X% (XlX>.MXM.V.ZXo .F.t",
+".t.oXbXbXbXbXbXbXnXn.2 %.z > Y e /.O.b.u k F.W ].6 A ,.w.|.|.|.|.|.|.| * , |Xt.q.5.5.q m p.* 5 cXdXd Q Q !X% (Xl yX, N.V.Z } '.t",
+".t zXbXnXnXnXn.1.1.1 i % +X& 8 8 /Xc ].u k F.W.6.u.K h.w.|.|.|.|.|.|.| ,.# |XtXt.q.q.q p./.*XsXg EXXXdXg.; !X% (Xv.MXM.V.ZXoXz.t",
+".t.t.{.1.1.1.1 i i i i [ %.z.7 8 /Xc.6 F k F ]Xr A C h.!.|.|.|.|.|.|.| ,.I | .Xt.q.q.q p./ 1X. E.-.- EXdXg.;Xk (Xl yX,.V.Z.).t.t",
+".t.t z.1 i i i i i i.B 7 g +.zX& >X3.*./X1 P P 6.K.I :.w.|.|.|.|.|.|.|.# C |XtXtXtXtXt P./ 1X..-X.X..- EXd Q.;X%XlX>X,.V.VXz.t.t",
+".t.t.t i i i i.2.B.B.BXpXp g % +X@.RXBXmXeX1X1 bX=X= : :.|.|.|.|.|.|.|.I |.:XtXtXtXtXt./Xm 1 a a a a.-.-XXXg.;Xk (Xv.MXM.V.t.t.t",
+".t.t.tX .2.B.B.B.B.B.BX .} H..XxXxXxX8X8 & 5 1 1.GXeXeX1X1 b bX= : :.w.I |.:XtXtXtXtXt./Xm 5 r r r aX..- EXg QXk (Xv.MXM.k.t.t.t",
+".t.t.t z.B.B.B.B.B.B U.B.{ t.n.v.$._.e.(X9.3 '.y & 5 1.GXeX1 b bX=X= : C A .XtXt . . .Xm 1Xs r r r a a.- EXd QXk (Xl.MXM K.t.t.t",
+".t.t.t.t.=.B.B U U U U U.{ t I.v.x._.(.( ; {.3.3.U.y & c 5 1 1.GXmXeXeX1 P P P.:.:.:.:Xm 1Xs r r r a a.- EXd QXk (Xv.MXa.t.t.t.t",
+".t.t.t.t.tX U U U U U U.{ t I.v.x._.( 9X9.j.X 4.8.a SX2.T.).k G c.c.GXmXm././ P P P PXm.cXs r r r aX..- EXg.lXk.[XvXM.t.t.t.t.t",
+".t.t.t.t.t z U U U UXp U.{ t I.s.x._.e 9X9 =.X @.8.a o S. x .).k.pXs 5 5 1.GXmXmXmXm 1Xs.- a a a aX. EXXXg.;X%.[Xv.).t.t.t.t.t",
+".t.t.t.t.t.t 7 UXpXpXpXp.&Xy I.s.x.@._ 9X9 = $ @.8.aX6X2. .T }X< N NX,X>Xl w cXs 5 5 1 5.-.- a aX.X..- EXd QXk w.[ G.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.=X X X Xp UXy I.s.i.$._.(X9 =.j.X.9.8X6 S. .T }X<.V NX,X>X>Xl.p.p.p c cXg E.-.-.-.- E EXdXg.; w.[ y.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.' 7X X X Xp.{ t 2.v.$._.( ;X9 $.X.9.8.a oX2.T }.4X< N NX,X> ` (X% !.;XgXdXX E E E EXXXdXg.;Xk ( y.y.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.t.' 7.=.=Xp.& t 3.s.$.@.e VX9 = $ @.8.aX6X2. .T }X<.V NX,.M `Xl (X% !.; QXgXgXdXdXgXg Q.;Xk ( y.y.t.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.t.t.' 7.=.=.&Xy I.s.i.@.e.(X9 = $.X.9.8X6 o. .T.T }X< N NX,.MX>Xl (X%Xk.;.; Q Q Q.l.;Xk w ( y.y.t.t.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.t.t.t.' 7.=Xp.g 3.s.i.$.r.( ; = $ # @.8X6X6X2. }.4.V.V NX,.MX>Xl ( (X%XkXkXkXkXkX% w.[ y.y.t.t.t.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.t.t.t.t.' 7Xp.&Xy 3.s.i.@.e VX9 = $.X.9.8X6 oX2 x.T }X<.V.V NXM.M yXvXlXl ( ( ( (.[.[.[ y.y.t.t.t.t.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.t.t.t.t.t.t.o.&Xy 3.s.i.$.r V ; = = $ @.8 nX6 o.F x.T }X<.V.V NXMX,.M yX>XvXvXlXvXvXv G.t.t.t.t.t.t.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.t.t.t.t.t.t.t z.h 3 2.s.$ Z.e VX9 = $ #.P.8 nX6 o.F x }.4.Z.V.V NXMX,X,.M.M.M.MXM.).t.t.t.t.t.t.t.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.h 3.s.i Z.r.e VX9 = $ #.9.8 n o o.F x }.4.Z.Z.V.V.V.VXMXMXMXa.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t [...s.i Z.e V ; = = $ # '.8 n o o.F x }Xo.Z.Z.Z.V.V.k K.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t gX5 Z.e V ; = $ $ #.9.8X- o o.F x x }Xo.)Xz.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t...L.r V ; = $ $ #.P.8X-X- o o.F x.F 'Xz.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t",
+".t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.e V { = $ $ #.P ' nXzX-.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t"
+};
diff --git a/arts/builder/pics/Synth_MIDI_DEBUG.xpm b/arts/builder/pics/Synth_MIDI_DEBUG.xpm
new file mode 100644
index 00000000..14dcd697
--- /dev/null
+++ b/arts/builder/pics/Synth_MIDI_DEBUG.xpm
@@ -0,0 +1,323 @@
+/* XPM */
+static char * MIDI_DEBUG_xpm[] = {
+"64 64 256 2",
+" c None",
+". c #161E54",
+"+ c #5A6A9C",
+"@ c #364684",
+"# c #4A5A8C",
+"$ c #8692B4",
+"% c #727EA4",
+"& c #26326C",
+"* c #46527C",
+"= c #5A5E8C",
+"- c #6A769C",
+"; c #323E6C",
+"> c #42467C",
+", c #526294",
+"' c #666A94",
+") c #7E8AAC",
+"! c #9AA6C4",
+"~ c #525A84",
+"{ c #4A527C",
+"] c #2E3A6C",
+"^ c #3A3E74",
+"/ c #7E829C",
+"( c #5A6694",
+"_ c #1E2A64",
+": c #929EBC",
+"< c #6A76BC",
+"[ c #3E4E84",
+"} c #62729C",
+"| c #424E7C",
+"1 c #66729C",
+"2 c #4A5294",
+"3 c #3A468C",
+"4 c #7276A4",
+"5 c #323E84",
+"6 c #525AA4",
+"7 c #5E6AAC",
+"8 c #929ABC",
+"9 c #7A86AC",
+"0 c #2E366C",
+"a c #868AA4",
+"b c #262E64",
+"c c #3E4674",
+"d c #7A7E9C",
+"e c #363E6C",
+"f c #525E8C",
+"g c #4A5684",
+"h c #5A66AC",
+"i c #626A8C",
+"j c #4E5A9C",
+"k c #8E9ABC",
+"l c #2A366C",
+"m c #5A62A4",
+"n c #6676A4",
+"o c #5662A4",
+"p c #2A3A7C",
+"q c #424E8C",
+"r c #6A72AC",
+"s c #8E92B4",
+"t c #465294",
+"u c #AAB2CC",
+"v c #62668C",
+"w c #222E64",
+"x c #1A2664",
+"y c #5A6284",
+"z c #424A8C",
+"A c #566694",
+"B c #666EB4",
+"C c #A2A6C4",
+"D c #7E82AC",
+"E c #6E729C",
+"F c #868AB4",
+"G c #4E5E94",
+"H c #8A96B4",
+"I c #7682A4",
+"J c #26327C",
+"K c #666E94",
+"L c #3A4284",
+"M c #9AA2C4",
+"N c #4A5694",
+"O c #727A9C",
+"P c #6E76A4",
+"Q c #3A4A7C",
+"R c #4A5A94",
+"S c #46568C",
+"T c #32427C",
+"U c #424A74",
+"V c #52629C",
+"W c #828EB4",
+"X c #525A94",
+"Y c #4A528C",
+"Z c #5A669C",
+"` c #222A74",
+" . c #4A4E7C",
+".. c #6672A4",
+"+. c #3A4A8C",
+"@. c #324284",
+"#. c #525E9C",
+"$. c #626EB4",
+"%. c #3E4A7C",
+"&. c #364274",
+"*. c #525E94",
+"=. c #4A568C",
+"-. c #626E94",
+";. c #323A7C",
+">. c #5A6294",
+",. c #868EB4",
+"'. c #727AAC",
+"). c #162254",
+"!. c #5E6E9C",
+"~. c #8A92B4",
+"{. c #767EA4",
+"]. c #2A326C",
+"^. c #42528C",
+"/. c #2E3E7C",
+"(. c #828AAC",
+"_. c #9EAACC",
+":. c #565A84",
+"<. c #323A6C",
+"[. c #3A4274",
+"}. c #222A64",
+"|. c #96A2C4",
+"1. c #464E7C",
+"2. c #5E6AB4",
+"3. c #7A82A4",
+"4. c #464E8C",
+"5. c #7E86AD",
+"6. c #666EA4",
+"7. c #4E5699",
+"8. c #424A84",
+"9. c #6E7AC0",
+"0. c #8A8EAC",
+"a. c #A2AACC",
+"b. c #7E86A4",
+"c. c #4E5E9C",
+"d. c #8E96BC",
+"e. c #465694",
+"f. c #6E7AA5",
+"g. c #6672BC",
+"h. c #1A2264",
+"i. c #2E367C",
+"j. c #262E74",
+"k. c #3E4684",
+"l. c #363E7C",
+"m. c #626A9C",
+"n. c #2A367C",
+"o. c #62669C",
+"p. c #222E74",
+"q. c #5E6A9A",
+"r. c #3A4682",
+"s. c #4E5A8B",
+"t. c #6E769C",
+"u. c #566293",
+"v. c #9EA6C6",
+"w. c #4E5284",
+"x. c #5E6693",
+"y. c #969EC4",
+"z. c #6A729B",
+"A. c #626AA4",
+"B. c #969ABC",
+"C. c #565E8A",
+"D. c #4E5684",
+"E. c #6A76A5",
+"F. c #2E3A7D",
+"G. c #1E266B",
+"H. c #5E628C",
+"I. c #6A6E94",
+"J. c #9EA2BC",
+"K. c #767AA4",
+"L. c #464A7C",
+"M. c #3E4A8E",
+"N. c #364285",
+"O. c #565EA1",
+"P. c #8A8EB5",
+"Q. c #1A225C",
+"R. c #3E4274",
+"S. c #8286AC",
+"T. c #A6AAC4",
+"U. c #3E468C",
+"V. c #363E84",
+"W. c #5E66AC",
+"X. c #2A327C",
+"Y. c #4E5A94",
+"Z. c #56629C",
+"`. c #4E528C",
+" + c #5E669C",
+".+ c #6A72A4",
+"++ c #565E94",
+"@+ c #4E568C",
+"#+ c #5E6294",
+"$+ c #767AAC",
+"%+ c #46528C",
+"&+ c #323E7C",
+"*+ c #626AB4",
+"=+ c #8692BC",
+"-+ c #727EAC",
+";+ c #263274",
+">+ c #465284",
+",+ c #5A5E94",
+"'+ c #323E74",
+")+ c #666A9C",
+"!+ c #7E8AB4",
+"~+ c #525A8C",
+"{+ c #4A5284",
+"]+ c #2E3A74",
+"^+ c #7E82A4",
+"/+ c #1E2A6C",
+"(+ c #929EC4",
+"_+ c #3E4E8C",
+":+ c #6272A4",
+"<+ c #424E84",
+"[+ c #4A529C",
+"}+ c #929AC4",
+"|+ c #7A86B4",
+"1+ c #2E3674",
+"2+ c #262E6C",
+"3+ c #3E467C",
+"4+ c #7A7EA4",
+"5+ c #363E74",
+"6+ c #626A94",
+"7+ c #4E5AA4",
+"8+ c #2A3674",
+"9+ c #5A62AC",
+"0+ c #5662AC",
+"a+ c #424E94",
+"b+ c #46529C",
+"c+ c #626694",
+"d+ c #222E6C",
+"e+ c #5A628C",
+"f+ c #424A94",
+"g+ c #56669C",
+"h+ c #6E72A4",
+"i+ c #8A96BC",
+"j+ c #7682AC",
+"k+ c #666E9C",
+"l+ c #3A428C",
+"m+ c #4A569C",
+"n+ c #727AA4",
+"o+ c #3A4A84",
+"p+ c #424A7C",
+"q+ c #525EA4",
+"r+ c #3E4A84",
+"s+ c #36427C",
+"t+ c #626E9C",
+"u+ c #323A84",
+"v+ c #868EBC",
+"w+ c #8A92BC",
+"x+ c #767EAC",
+"y+ c #2A3274",
+"z+ c #828AB4",
+"A+ c #565A8C",
+"B+ c #323A74",
+"C+ c #3A427C",
+"D+ c #222A6C",
+"E+ c #464E84",
+"F+ c #7A82AC",
+"G+ c #464E94",
+" ;+y+y+X.X.;+j.p.` D+/+G. ",
+" }.B+N.N.u+F.F.n.J j.;+j.p.p.D+d+Q.. . . ",
+" 0 8.M.U.l+N.5 F.i.i.n.J ;+j.` p./+/+/+G.h.h.. . ",
+" B+j 7.m+G+f+3 N.V.u+F.F.n.J j.p.p.p.` p.p.` /+/+x h.. . . . ",
+" X O.O.7.b+a+M.3 N.@.u+i.n.n.n.X.;+j.p.` /+/+/+/+` /+/+G.G.h.. . ",
+" @+*+h 9+7+m+[+a+f+3 l+V.5 F.i.X.J ;+j.p.p.p.` ` ` G./+/+G./+G./+h.. . . ",
+" 6.$.2.0+O.6 m+G+f+U.3 l+5 u+F.i.X.J J j.j.` p./+/+` G./+` G./+G./+G.x h.. . ",
+" E+< g.*+W.9+q+7+m+b+f+M.l+V.u+/.F.n.n.y+;+;+p.d+` p.` /+/+G.G./+G./+G.G.G.G.h.. . . ",
+" R.E.g.g.2.h 0+7+m+b+a+M.3 3 @.u+u+p n.J J j.p.j.p.` p.` /+/+` G./+G.G.x G.G.G.G.h.. . . ",
+" @+9.g.g.2.h 0+6 7.[+a+a+f+3 l+V.5 i.i.X.y+j.;+j.p.D+` /+/+/+/+/+/+G./+/+/+x /+/+G./+h.. . . ",
+" R.r < B $.2.0+q+j m+2 a+M.3 N.@.F.F.i.n.n.;+j.j.p.p.d+` p./+/+` G./+G.x G./+G.G./+x G.x h.. . . ",
+" <+9.< g.B W.9+O.#.O. +Z.X M.3 N.V.5 B+F.1+;+J ;+;+j.p./+/+/+` G.G./+G./+/+G.x G.x G.G./+G.G.h.. . . ",
+" B g.B *+2.h m m +.+4+o.7.z l+V.^ r+{+p+C+1+j.p.p.D+` D+D+/+/+/+` /+G.G./+x /+/+G./+G.G.G.x x h.. . ",
+" r g.g.$.2.9+o h r v+B.a.f.@+r+V.r+++I.S.P H.5+& j.d+d+d+2+].D+/+/+` /+/+/+/+` G.G./+/+/+/+/+G.G./+h.. . ",
+" p+B *+2.h 0+o 6 6 A.W w+}+6.Y Y .~+x.q.6+m.6+3+y+y+1+5+p+[.5+2+/+/+/+p.` p.y+;+;+p./+/+/+/+/+/+G.G.G.x . . ",
+" *+2.W.h 9+o 6 j R #+F F F +z ++n+.+)+= @+>.I.E+]+C+<+' ,.i 1.l ` p.p.p.8+5+>+p+s+y+p.p.p.d+d+d+w d+_ G.).. ",
+" 7.h h 0+V q+7+7.[+2 m P.x+h+#+7.o.F+x.1.L.| `.C.> ]+R.=.4 B.v e l 2+y+]+s+E+C.1 A+%.1+2+2+2+& 0 [.U [.] 2+_ x . ",
+" l.0+0+q+6 6 7.j m+G+q >.d.4 ++++++- ,.D.1+&+R.%.E+C+i.r.A+4+T.= ] ].;+]+3+#+4+a B.z.{+B+;+& ].[.:.K F+' D.<.D+}.. . ",
+" 4.6 6 6 7+7+[+b+t z 4. +v+t+=.++)+I.P E+1+5+ .> &.1+].'+g K (. .].;+;+F.C+u.z+F ~.c+%.5+1+[.w.~ H.' 4 z.' c & 2+D+Q. ",
+" 7+7.m+m+[+b+b+b+a+z >.z.z+>.z *.'.1 #+^ y+l.e+%.1+].j.0 L.:.I.[.& y+y+8+T C.0.,.P.u.C+s+r.u.b.n+1 K 6+E {.w.0 & w x ",
+" 0 m+m+b+b+G+a+f+M.M.8.)+I..+Y C+~+4+z.( | B+3+C.[.8+;+;+<.C.~ D.<.& 8+8+1+l.s.0.5.{.~ C+p+D.k+S.v 1.1.* e+f.{ 0 & 2+d+x ",
+" G+a+a+G+f+f+M.M.3 N.Y x+A.A+k.u+Y (.h+#+@+<+D.= 5+;+y+].e E ~ R.0 8+8+n.8+]+D.,.{.I.g r.s.P {.b.D.5+'+5+{ E 1.] ].& }.. ",
+" x z f+M.M.M.M.3 l+l+N.k.~+%+k.l.F.8.n+n+h+' >.D.p+1+;+;+].c t.{ ; 8+n.i.F.]+C+C.P.P >.Y p+e+F+n+z.| ] '+3+{ x.c l & ;+l & x ",
+" ;.U.3 3 3 3 l+l+l+V.;.&+k.&+;.F.1+3+)+4 (.F+$+@+s+1+8+].] .d 1.0 1+p F.p B+1.6+P.k+Y g %+m.P.t.e+[.]+5+| D.~ [.0 8+8+y+b . ",
+" &+l+l+l+N.N.N.N.@.5 5 F.;.F.i.n.n.]+%. .~+D.{+^ p y+n.l [.1.y [.8+1+F.F./.r.e+- (.( 8.E+{+1 d.z.s.C+]+[.~ D.E+5+1+1+8+8+y+& ",
+" N.5 5 5 5 V.5 5 u+u+i.i.i.n.n.y+y+y+y+]+'+&+;.8+y+8+n.1+U 1. .'+]+F./.&+;.%.n+4+5.C.r.<+=.E J.K 1.[.'+p+K ~ %.'+]+p ]+1+l ]. ",
+"j.5 5 5 u+u+u+F.F.F.F.F.n.X.n.n.J ;+;+j.y+y+8+1+8+n.n.F.]+5+'+5+]+F.&+5 /.;.E+f.E..+g @ %.%+z.M {.u.Y <+f t.~ [.B+B+F.]+F.p p l ",
+"y+u+u+u+F.p p i.n.8+1+n.y+8+;+;+y+;+J ;+;+;+;+n.n.n.8+n.i.]+]+F.;.&+5 5 &+s+>+z.m.u.8.s+r.>+K M z+t.1 ( z.4+D.&./.F././.]+]+1+8+",
+";+n.n.i.i.i.i.u+^ r+R.N.y+j.j.p.j.j.;+;+y+y+;+X.n.p F.p i.F.]+/.&+5 s+N.s+s+r+=.>+q r.r.r.[ #+(.(.F+x+n+E.1 >+&.'+&+;.&+;.F.F.1+",
+"n.i.X.n.n.n.5 8.u.K.x.Y ;.;+;+j.p.y+y+8+;+J X.8+F.i.n.F./.;./.5 5 N.@.N.@.N.s+r.r.@ r.@ r.8.f n+9 d.P.z+- >.<+s+s+l.T &+&+'+]+p ",
+"J X.J X.8+F.k.,+.+z+x.%.1+j.;+].B+p+R.C+0 8+n.8+8+]+]+;./.&+&+T @.N.N.L N.@ N.@ r.@ 3 r.3 o+<+@+C.q.q.x.s.E+r.N.s+N.s+T &+&+;.]+",
+";+J j.;+X.B+=.'.a B.x.C+y+` l 8.C.n+q.D.B+;+8+n.]+l.[.r.s+&+&+5 N.N.@ @ @ 3 o+3 +.M.M.M.o+M.o+[ <+| E+%+r+r.r.@ @ N.N.l.@.&+&+F.",
+"j.;+p.j.j.y+1.4 x+D {+0 ].j.B+C.h+a x.<+B+;+1+&+%.s.C.e+{+r+s+5 N.N.@ r.r.r.o+r++.M._+_++.M.r+r+r+[ r+[ o+r.r.r.r.@ @ s+s+T T &+",
+"p.j.j.j.p.2+C+t.t.t.R.d+p.2+> P F J.x.[.]+8+&.~+I.S.F P..+f r+s+N.r.l+r.r.r+M.+.M._+_+q q q q _+_+M.[ M.r+M.r++.o+r.@ N.N.V.l.F.",
+"` p.p.p.p.2+%.{.m.~+B+D+d+d+3+P F+0.D.1+<.B+{+z.P f.F+,.h+++%+r.o+r+^.@+@+@+>+[ _+_+_+_+_+_+M.q _+z q _+_+_+M.M.o+k.o+@ @ s+s+&+",
+"/+` d+` D+2+ .D = 3+& D+/+p.[.z.O {.p+l B+p+x.$ n+m.n+z+- x.=.<+t s.m.j+-+f.G 4.q q a+^.^.>+>+<+^.^.^.^.q q _+[ M.o+r.r.r.N.s+&+",
+"/+` d+/+2+5+A+{.{+1+d+/+D+2+8.K.k+e+^ l [.H.c+k+C.{+x.x+z.m., @+Z.t+j+d.!+x+, ^.q ^.4.%+s.( ( >.g q q ^._+q _+_+M.r+o+r.r.@ @ s+",
+"G./+/+` ].D.x.P 3+2+/+G.D+y+{+5.x.1.B+& %.3.v {+%.s+=.f...t+ +V P z+d.v.~.j+++%+^.^.# x.f.,.$ ~.t+# %+^.^.^.q q _+_+M.o+o+r.C+&+",
+" ` /+/+B+H.e+++B+}.D+G.d+[.C.D ~ '+B+0 { 3.C.R.r.r.@+f.6.Z *.=.t+(.d.v.z+E.*.=.Y.u.-.x+5.w+i+d.n+( # t t e.^.^._+[ M.o+r.@ r. ",
+" /+/+D+[.P C.p+2+/+G./+].Y 6+b.E+0 0 e ~ b.{ '+C+%.*.P q.*.Y q >.) d.v.F+q.u.Y.m.F+~.|.8 ~.d.y.) f.*.S S t %+4.q q [ M.r+r.@ ",
+" . G.}.0 `.C+0 D+/+x /+B+#+x.6+[.& <.1.v 5.D.T k.{+m t.u.%+<+[ ,+(.~.: f.Z.( Z f.w+,.!+-+- j+d.5.n+u.S S e.e.%+^.q _+_+r+r.'+ ",
+" Q./+/+& '+].D+G.x /+D+C+I.#+D.B+& [.i f.5.s.C+>+x.k+.+~+r+o+k.f ,.$ w+1 *.+ f.,._.W f.q.*.1 z+j+f.u.R e.e.S e.^.^.q r++.c b ",
+" Q.G.D+d+/+/+G.G./+w '+++E+[.y+2+5+H.z.3.u.p+f 1 t+u.%+r.r+[ x.s F 5.t+*.t+j+w+J.I q.g+*.} ~.x+..*.R N e.e.t ^.q q [ o+r. ",
+" h.G.x G.G.G./+/+G.D+1+1.5+& & ;+5+= K 4+6+~+)+4+x.=.k.r.r+Y 1 y.!+'.t+V ..=+d.|.P u.*.*.} i+-+q., R R N e.S t ^.^.q [ & ",
+" h.x /+x G.G./+G.}.d+].].2+d+2+] E+e+K.- 1 -. +Y Q r.@ <+C.f.8 j+t+ +Z.P d.i+i+k+*.V Z.P k % Z *.R R R e.e.e.^.q q [ ",
+" G./+x /+/+/+G.G.x G.D+/+D+d+;+8+[.@+t.3.(.)+D.3+N.N.r.<+- F+w+P Z.A g+j+|.s ) !.G u.q.% i+E.( G G c.R R e.^.^.^._+[. ",
+" . h.G.G.x x x G.G.x D+d+p.d+p.& l s+E+D.f <+r.s+s+N.r.=.{.9 W 1 *.*.*.-+_.d.!+:+V q.E.j+W n g+c.R j R R N t ^.q q r. ",
+" G.G.G.G.G.G.G.G./+/+/+d+;+;+y+8+0 B+l.&.&.T s+@ r.r.@+F+F+F+( N s.G n+u B.z+1 Z.n W !+5.t+V *.c.G R N e.S ^.^.Q ",
+" h.G./+/+x /+G.G.D+` D+2+;+;+8+8+8+]+;.;.&+&+N.@ r.>+m.q.q.s.^.N *.E.v.8 w+9 E.j+=+!+-+q.V c.c.j R R e.%+%+[ ",
+" )./+G.G./+x G./+/+d+p.;+j.;+8+8+]+F.F.&+T N.N.s+r.[ =.Y.7.^.4.S s.z.(+(+8 i+w+i+i+j+n + V G R R R e.t S ^.0 ",
+" . G.G.G./+G./+/+D+D+& ;+;+8+n.8+]+F.F.T T N.@ L o+r+^.q ^.q ^.R q.5.=+}+d.w+w+W f.7 , c.G c.R N S t ^.<+ ",
+" x x x x x G.G.d+p.p.j.;+;+8+i.p /.5 &+N.s+N.@ r.o+r+[ M.^.%+e.++E.9 8 i+i+W !+n g+*.G R R R R e.S ^. ",
+" . h.G.G.x /+G./+D+d+j.y+;+8+1+i.F.&+&+@.T N.N.r.3 U.[ M._+q ^.N , q.f.f.f.E...g+G Y.j R N e.=.^.4.c ",
+" . x x G.G./+/+d+p.j.;+8+8+8+p F.F./.l.s+@ r.r.o+o++._+q _+^.%+N Y.*.Z.+ g+#.Y.Y.R R e.R S e.>+l ",
+" . . x x /+/+d+d+d+j.;+8+1+]+]+;./.5 &+T N.N.r.U.r+_+q q ^.^.^.e.N Y.Y.R Y.R R e.R e.e.^.^.c ",
+" Q.G.G.G.G.D+d+p.;+;+;+8+8+F.F.;.&+s+N.@ r.r.o++.r+_+_+_+q t %+S %+S =.e.e.e.e.%+%+%+>+l ",
+" . . _ /+/+d+d+2+;+;+8+8+p F.F.F.&+&+@.@ N.r.+.r.M.r+_+_+q _+^.a+^.S %+%+S e.e.%+^.r. ",
+" h.G.D+D+d+;+;+;+y+8+1+]+/.&+&+V.s+s+@ N.@ o+o+_+M.<+_+q q ^.^.^.^.^.^.^.^.^. ",
+" . _ /+D+d+2+;+y+8+8+1+]+F./.&+5 s+s+L r.r.r.r++.[ _+_+q q q ^.^.a+q [ r. ",
+" G.d+D+;+& ;+;+8+8+F.]+&+&+&+5 s+@ N.r.r.o+k.r+r+[ _+_+_+_+q <+[ ",
+" x _ p.& ;+;+8+8+p 1+F./.'+&+&+s+N.@ N.@ r.o+3 r+r+r+M.r+r+r. ",
+" _ 2+y+;+y+1+p ]+B+/.&+&+T T s+@ r.@ r.o+r.o+r.&. ",
+" w w & 8+8+p F././.;.&+l.l.s+N.N.C+@ &+s+ ",
+" y+8+1+1+]+/.&+/.&+&+s+s+ "};
diff --git a/arts/builder/pics/Synth_MIDI_ROUTER.xpm b/arts/builder/pics/Synth_MIDI_ROUTER.xpm
new file mode 100644
index 00000000..43b8a59d
--- /dev/null
+++ b/arts/builder/pics/Synth_MIDI_ROUTER.xpm
@@ -0,0 +1,323 @@
+/* XPM */
+static char * Synth_MIDI_ROUTER_xpm[] = {
+"64 64 256 2",
+" c None",
+". c #161E54",
+"+ c #AE922C",
+"@ c #4A5A94",
+"# c #363E64",
+"$ c #DECA34",
+"% c #3E4E84",
+"& c #D2AE1C",
+"* c #222E64",
+"= c #A6721C",
+"- c #42425C",
+"; c #3A3A54",
+"> c #FEE61C",
+", c #626EB4",
+"' c #FEAE04",
+") c #3A4674",
+"! c #2E3664",
+"~ c #CE9214",
+"{ c #52669C",
+"] c #4A4E84",
+"^ c #E2DA4C",
+"/ c #2E3E7C",
+"( c #CABE5C",
+"_ c #2A2E5C",
+": c #CEA21C",
+"< c #565E94",
+"[ c #A68224",
+"} c #FACA14",
+"| c #3E427C",
+"1 c #CE8A14",
+"2 c #1A2664",
+"3 c #46568C",
+"4 c #D6AE24",
+"5 c #3E4E8C",
+"6 c #B28A24",
+"7 c #3E4A7C",
+"8 c #52629C",
+"9 c #FABE0C",
+"0 c #D29A14",
+"a c #4A5A9C",
+"b c #363E84",
+"c c #22265C",
+"d c #FEF62C",
+"e c #4A5694",
+"f c #2A3274",
+"g c #D6B624",
+"h c #464E8C",
+"i c #3A3E6C",
+"j c #222E74",
+"k c #323A74",
+"l c #2A367C",
+"m c #FAD214",
+"n c #DED23C",
+"o c #364684",
+"p c #2A3264",
+"q c #3E4A8C",
+"r c #D69E14",
+"s c #BE9A2C",
+"t c #323664",
+"u c #5E66AC",
+"v c #DEA614",
+"w c #B28224",
+"x c #525AA4",
+"y c #6A76BC",
+"z c #FAB60C",
+"A c #565EA4",
+"B c #B68A24",
+"C c #FAC60C",
+"D c #CE9E14",
+"E c #36427C",
+"F c #222A6C",
+"G c #DEBA24",
+"H c #323A64",
+"I c #BA9224",
+"J c #364274",
+"K c #26326C",
+"L c #FEEE24",
+"M c #2E3A74",
+"N c #4A528C",
+"O c #FEDA14",
+"P c #32427C",
+"Q c #DABE2C",
+"R c #D2AA1C",
+"S c #FACE14",
+"T c #3E467C",
+"U c #1E2A6C",
+"V c #42528C",
+"W c #464A84",
+"X c #FEFE34",
+"Y c #4A569C",
+"Z c #D6BA2C",
+"` c #46528C",
+" . c #E2D644",
+".. c #3A4A84",
+"+. c #2A326C",
+"@. c #36428C",
+"#. c #162254",
+"$. c #525A8C",
+"%. c #323E74",
+"&. c #C6B654",
+"*. c #262E64",
+"=. c #464A64",
+"-. c #2A3674",
+";. c #D69614",
+">. c #5A66AC",
+",. c #CEA61C",
+"'. c #1E266C",
+"). c #DEB21C",
+"!. c #424A7C",
+"~. c #FAC20C",
+"{. c #4E5E9C",
+"]. c #222A5C",
+"^. c #3A426C",
+"/. c #263274",
+"(. c #2E3A7C",
+"_. c #FAD61C",
+":. c #BA8224",
+"<. c #FABA0C",
+"[. c #5662A4",
+"}. c #D29E14",
+"|. c #363A64",
+"1. c #FEE21C",
+"2. c #DAC634",
+"3. c #3E468C",
+"4. c #DECE3C",
+"5. c #464E74",
+"6. c #D2B224",
+"7. c #3E466C",
+"8. c #FEEA24",
+"9. c #AA7E24",
+"0. c #AE8624",
+"a. c #4E5274",
+"b. c #6672BC",
+"c. c #5A629C",
+"d. c #D6B224",
+"e. c #AE8E2C",
+"f. c #5E6AB4",
+"g. c #6E7ABC",
+"h. c #B6962C",
+"i. c #4E5E94",
+"j. c #D29614",
+"k. c #AA8624",
+"l. c #CE8E14",
+"m. c #B28624",
+"n. c #B68E24",
+"o. c #C69A24",
+"p. c #3A4A7C",
+"q. c #525E9C",
+"r. c #FEF22C",
+"s. c #FEDE14",
+"t. c #DAC234",
+"u. c #E6A614",
+"v. c #FEB204",
+"w. c #FEFA2C",
+"x. c #1A2264",
+"y. c #2A2E6C",
+"z. c #323A84",
+"A. c #4A529C",
+"B. c #FEFE44",
+"C. c #46529C",
+"D. c #464E84",
+"E. c #5666A4",
+"F. c #323E7C",
+"G. c #FECA0C",
+"H. c #1E265C",
+"I. c #DAAE1C",
+"J. c #424E8C",
+"K. c #4E5AA1",
+"L. c #4E5692",
+"M. c #DAB624",
+"N. c #E2D244",
+"O. c #3A4686",
+"P. c #2E3262",
+"Q. c #424A94",
+"R. c #FEB604",
+"S. c #3A427C",
+"T. c #FECE0F",
+"U. c #42467C",
+"V. c #1A225C",
+"W. c #262A64",
+"X. c #FED614",
+"Y. c #FEBA04",
+"Z. c #5A62AC",
+"`. c #B28E24",
+" + c #B2922C",
+".+ c #666EB4",
+"++ c #D29214",
+"@+ c #D2A21C",
+"#+ c #FEBE0C",
+"$+ c #262E74",
+"%+ c #2E367C",
+"&+ c #FED214",
+"*+ c #DA9E14",
+"=+ c #FEC60C",
+"-+ c #4E569C",
+";+ c #DABA2C",
+">+ c #2E326C",
+",+ c #3A428C",
+"'+ c #363E74",
+")+ c #2E3674",
+"!+ c #D2A61C",
+"~+ c #FEC20C",
+"{+ c #DEC634",
+"]+ c #626AB4",
+"^+ c #BA962C",
+"/+ c #D28E14",
+"(+ c #B68624",
+"_+ c #BA8E24",
+":+ c #363E6C",
+"<+ c #DECA3C",
+"[+ c #D2AE24",
+"}+ c #222E6C",
+"|+ c #FEE624",
+"1+ c #3A467C",
+"2+ c #2E366C",
+"3+ c #5266A4",
+"4+ c #2A2E64",
+"5+ c #465694",
+"6+ c #3E4A84",
+"7+ c #D29A1C",
+"8+ c #2A327C",
+"9+ c #D6B62C",
+"0+ c #464E94",
+"a+ c #3A3E74",
+"b+ c #DED244",
+"c+ c #3E4A94",
+"d+ c #32366C",
+"e+ c #CE9E1C",
+"f+ c #364284",
+"g+ c #222A74",
+"h+ c #323A6C",
+"i+ c #4A5294",
+"j+ c #324284",
+"k+ c #3E4684",
+"l+ c #425294",
+"m+ c #FEFE3C",
+"n+ c #465294",
+"o+ c #E2D64C",
+"p+ c #3A4A8C",
+"q+ c #525A94",
+"r+ c #262E6C",
+"s+ c #424A84",
+"t+ c #222A64",
+"u+ c #3A4274",
+"v+ c #26327C",
+"w+ c #2E3A84",
+"x+ c #5662AC",
+"y+ c #D29E1C",
+"z+ c #363A6C",
+"A+ c #3E4674",
+"B+ c #6E7AC4",
+"C+ c #525EA4",
+"D+ c #FEDE1C",
+"E+ c #323E84",
+"F+ c #1E2664",
+"G+ c #424E94",
+" /.f f 8+8+/.$+j g+F U F+ ",
+" ].k f+f+z.w+(.l v+$+/.$+j j F j x.. . . ",
+" d+s+q 3.,+f+E+w+%+%+l v+/.$+g+j U U U '.x.x.. . ",
+" k K.-+Y G+c+3.@.b z.w+(.l v+$+j j j g+j j g+U U 2 x.. . . . ",
+" q+A C+Y A.0+c+O.@.j+z.%+l l l 8+/.$+j g+U U U U g+U U '.'.x.. . ",
+" L.]+>.x+x -+C.G+c+O.@.b E+w+%+8+/./.$+j }+j g+g+g+'.U U '.U '.U x.. . . ",
+" , , f.Z.C+K.Y 0+Q.3.3.@.E+z.w+%+8+v+v+$+$+g+}+U U g+'.U U '.U '.U '.2 x.. . ",
+" % y b.]+u x+A K.Y C.Q.c+@.b z./ (.l l f /./.j j g+j g+U g+'.'.U '.U '.'.'.'.x.. . . ",
+" | y b.b.f.>.x+x -+C.G+c+3.o @.z.w+(.l v+v+$+j $+j g+}+g+U U U '.U '.'.2 '.'.2 '.x.. . . ",
+" L.g.b.b.]+>.c.C+K.Y C.G+c+3.,+b E+%+l 8+8+$+v+$+j F g+U U U U U U '.U U U '.U U '.U x.. . . ",
+" | b.y .+, f.E.A x Y A.0+Q.O.@.f+E+E+w+%+-.8+$+$+j j }+g+j g+U U '.g+'.'.'.U 2 '.U 2 '.2 x.. . . ",
+" 5.g.y b..+f.>.A C+K.Y n+G+c+3.,+E+E+(.%+l 8+/././.$+j U g+U U '.'.U '.U U '.'.'.2 '.'.U '.'.x.. . . ",
+" .+b..+, u u Z.C+K.Y C.G+c+p+o @.E+z.(.(.l l v+j j j j j U U U g+U U '.'.U U U U '.U '.2 '.2 2 x.. . ",
+" b.b.b., f.>.x+C+K.Y A.0+G+c+,+,+b z.w+w+l l /.$+$+$+g+g+U g+g+g+U g+U U U U U U U U U U U U '.'.U x.. . ",
+" W .+]+]+u >.A C+x -+A.G+Q.3.3.,+@.b z.%+l /.y.K r+j $+r+j j j j j U j j j j U g+g+j g+j j g+j U U U U 2 x.. ",
+" f.f.u >.x+A C+K.Y A.C.G+c+c+O.@.b E+(.(.l ! h.& +_ }+j g+g+U }+g+j j j j j j j j j }+F F }+F j F g+F U . . ",
+" L.>.>.A c.[.< < q+e N ] D.U.T S.u+'+'+k d+P.s _.O m I *.r+j /.j /.$+j j j $+/././.r+/././.j /.}+j }+}+g+}+U 2 . ",
+" b Z.[.A @ ( o+o+o+N.N.4.n <+<+{+{+Q Q Z 9+g G _.D+O X.S 6 K /.$+$+j /././././.$+/././././.$+/.$+/.K $+j r+j F F V.. ",
+" h x K.x L.^ B.m+m+m+m+X X w.d r.L 8.|+> 1.1.D+O O O X.X.!+p /./.f /.f /.f /.-././.l /./.-./.-./././.K /.r+/.K }+F 2 ",
+" K.-+-+Y N &. .b+b+n 4.<+$ 2.t.Q Q Q ;+Z 9+4 M._.X.X.X.} 6 +.v+v+/./.v+-.-.l v+l -.f l -./.-./.f f -.f /./.$+$+}+K V. ",
+" ! Y 5+C.0+0+h ] D.W W !.| | | E '+k h+d+d+2+>+P.^+m X.X.I.; /.-.l l -.l l -.(.)+l (.-.l (.l M l l -.l -.-.-./.f r++.}+H. ",
+" 0+G+0+Q.G+c+c+c+3.O.o @.@.E+E+k w+w+l l v+f /.f P.n.I.G.G._+P.-.-.%+%+%+l %+l (.(.l (.(.l (.l M (.-.-.-.-.-.-.-.-./.* . ",
+" H.Q.c+Q.c+q 3.O.3.3.@.b b b z.w+w+l %+l f l l v+f v+>+P.I C } B ! M (.(.(.(.(.(.(./ (.(.F.(./ (.(.(.(.(.%+l -.-.-.-.f -.K H. ",
+" k O.3.3.,+,+O.@.@.b b E+E+/ w+(.%+%+l f v+/./.f l -.l l P.n.C C B ! l (.(.(.(.(.(.k / / (./ k / (./ (.(.(.M (.l l -.-.f * . ",
+" F.@.o ,+,+@.@.E+j+E+E+w+z.(.%+l l 8+8+f 8+8+l v+l l -.l l ! _+C ~.B ! / / / E+E+/ E+E+P E+E+E+F.F.F.F./ F.(.k M )+-.-.)+f K ",
+" @.b b j+j+b b E+z.z.z.w+%+l l l l /./.v+/.f f l f l l (.(.%+t o.C ~.B d+k E+F.E+f+b b E+b j+F.j+F.E+F.F./ F./ (./ (.M -.-.f ",
+"$+E+E+z.z.w+w+z.w+w+(.%+l %+l 8+/.8+v+f v+l l -.l l l l (.(.(.(.=.o.#+~.B |.E+f+F.j+j+E f+E f+f+f+E j+f+b F.E+F.(.F.(.M M M M -.",
+"f w+w+w+w+w+w+(.(.%+l l l l 8+f 8+/././.-././.8+-.l (.(.(.(.(./ z.H B 9 9 B # '+f+f+o o o f+o f+f+f+f+E j+E E f+P / F.F.(.M l -.",
+"8+%+%+%+%+%+l l l l l l v+f v+$+/.$+v+v+v+l l l l -.(.(.w+/ z.F.E+F.|.B <.9 r m.i S.,+o O.O.O.O.o O.o O.f+f+P b b F.F./ (.k M )+",
+"-.l l l l l 8+8+>+>+>++.>++.p p y.+.p p 2+P.2+2+d+k 2+%+F.E+/ j+f+j+F.- v #+~+z (+i ) | u+u+u+J a.5.| u+J J a+J J :+k F.F./ (.M ",
+"8+8+8+v+/.l /.K + g d.6.6.6.[+& & & R !+R !+!+!+!+!+k.M (.E+F.E+E f+'+w 9 #+~+#+<.0 0 0 j.j.j.j.v u.++++/+~ l.1 l.l.= %.F./ (.(.",
+"$+/./.f 8+$+v+y.g 1.1.s.s.O O O X.X.X.X.&+&+&+&+T.G.@+z+E+P E+j+f+f+u+}.#+~+#+#+Y.#+Y.R.Y.R.R.R.R.R.R.v.v.' ' ' ' ' l.i P F.F.M ",
+"$+$+j j /.$+/.r+e.6.d.[+[+[+R R R !+!+R !+@+@+@+!+@+[ '+E+j+f+j+o o J m.<.#+#+#+z 0 0 j.j.j.++++~ /+~ ~ l.l.l.1 1 /+w i f+P P F.",
+"j v+/.$+j j $+}+*.4+*.4+4+*.4+y.4++.>+p >+! M d+d+h+'+(.F.f+f+f+@.O.O.^.(+<.#+R.(+7.k+7 6+7 % !.!.7 !.A+!.A+A+A+7.^.!.f+E b b (.",
+"F g+j j $+g+j j j j }+j g+U j j $+/./.-.-.-.(.(./ (.E+E+E+P f+o o O.O.6+^.w 0 w 7.5 G+V G+J.V J.V J.V 5 J.5 6+p+q 6+..O.o E E F.",
+"U j j g+g+}+g+}+U g+U U }+g+U j K j v+/.l l l (.(.E+F.E f+f+O.O.O.p+p+..6+7 7 !.J.V V V ` n+l+l+l+V J.J.5 J.5 6+....O.o O.E E F.",
+"U F j }+U j g+g+F g+g+U U g+F g+$+$+/./.-.(.(.(.(.E+E+E+f+f+@.O.O.q q 5 q 5 5 l+l+l+n+5+5+V 5+` l+5+l+` V V V J.6+6+6+O.O.o o E ",
+"'.U g+U g+F U F U U U U F U j }+/./.f l l l l (./ F.F.f+f+o O.O.p+p+p+q J.J.l+G+` n+l+l+n+5+5+5+5+n+3 5+l+G+J.5 J.5 p+....1+S.F.",
+" U g+U U g+U U g+U '.U U U j F $+/./.f -.(.(.k / E+j+f+f+o o O.p+q 5 5 G+J.V l+5+3 5+5+e e 5+e @ 5+5+` 3 l+n+J.J.J.6+..O.o O. ",
+" U U g+U U U g+U g+g+U F g+U j j $+f -.l l w+w+z.F.E+j+@.o O.q q 5 5 l+V l+n+n+n+5+e @ a @ a @ 5+5+5+5+5+3 V V J.5 5 q 6+O.o ",
+" . 2 g+'.g+U U U U U '.'.F j }+/./.v+-.(.l (./ E+b E o S.O.O.O.q q 5 5 V l+5+5+e @ 5+5+a @ a K.@ @ a 5+5+5+5+V V J.% 5 6+O.%. ",
+" V.U '.U U '.'.'.'.'.U U U U $+j /./.8+-.(.(.k / P f+@.o o p+q q 5 J.l+n+3 5+5+5+5+a {.@ a @ i.{.K.@ @ e 5+` 5+V V J.6+6+) *. ",
+" 2 U '.U U 2 t+t+c t+W.*.*.4+*.p ! 2+2+M h+'+z+%.'+f+O.o p+p+q 5 J.l+l+l+5+5+a @ @ K.i.{.{.{.i.{.K.@ e @ 5+n+V J.J.5 ..O. ",
+" x.'.U '.'.t+[ @+,.: : D : D @+: e+D D }.e+7+D 7+9.E o O...q 5 5 G+J.` 5+5+a @ {.a {.{.q.{.{.{.{.i.{.a 5+e 3 ` l+V % % p ",
+" x.U U '.].,.T.T.T.T.G.T.G.=+=+=+=+~+=+=+~+~+~+0 u+O.O.p+q q 5 l+l+n+5+5+e @ i.{.{.{.{.8 8 8 8 {.i.K.@ e 5+3 V J.J.% ",
+" 2 '.'.U H.[ @+@+D D : y+D @+D y+}.e+0 0 }.0 0 9.a+o O...q J.J.J.V 5+n+5+a a a {.{.8 8 8 8 8 {.i.{.i.@ @ 5+l+l+V 5 J ",
+" . 2 '.'.'.H.].].].c ].*.*.p p +.! 2+M M h+%.%.J f+O.O.p+p+5 V G+V 5+5+5+e @ @ {.{.8 8 3+8 3+8 8 {.a @ @ e ` ` J.J.1+ ",
+" 2 2 2 2 '.2 '.'.U F F }+$+/.-.f -.%+k / F.E j+o o O...5 5 5 V l+` 5+5+@ K.{.{.{.8 3+8 3+8 8 {.i.i.@ e 3 5+V V p. ",
+" x.'.U '.'.'.'.U F g+}+}+$+-.-.M (.(.z.E+b f+f+O.O.O.q q J.J.l+` 5+5+@ @ {.{.{.{.8 [.E.8 8 {.{.a @ @ 3 n+` % ",
+" x.U '.'.U U '.'.U }+j j /./.-.l -.(./ / P f+f+o O.q p+5 5 V V l+5+5+5+@ @ {.{.8 8 8 8 8 {.{.@ @ @ 5+n+3 V ! ",
+" . U 2 2 U 2 U '.F }+j /././.l M M k E+F.f+f+o o O.q q q J.V ` 5+e 5+a @ {.{.{.8 {.8 8 8 {.{.@ e 5+` V J. ",
+" 2 '.'.2 U F+U F }+K j /.-.-.l %+(.(.E+P f+o O.O.O.5 5 5 l+l+V 5+e 5+@ @ a {.{.{.{.{.a i.@ @ @ 5+3 V ",
+" . V.'.F+2 '.U F F r+$+/.-.-.(.(./ F.F.P f+f+O...O.q q q J.l+V l+5+5+5+@ @ @ {.@ @ i.@ @ e 5+3 l+J.1+ ",
+" . 2 2 F+U '.U }+j /././.-.-.M (./ F.E+F.E o o ....% J.5 J.l+5+` 5+5+5+5+@ @ a @ a @ e @ 3 5+` ! ",
+" . . 2 2 '.'.F F r+/././.l )+M (.F.j+j+E S.O.O.O...5 5 J.J.V V 5+5+e 5+5+@ @ 5+e @ 5+e ` V ) ",
+" V.x.'.'.U }+}+j $+/.-.f l %+(.k F.b j+f+f+O.O.p+6+% 5 J.V n+` ` 5+5+3 5+5+e 5+n+` V V ! ",
+" . #.'.'.U F }+r+/.-.-.-.(.(.k / F.P f+f+O.O.O.3.6+% Q.% J.V V ` n+` ` 3 3 3 5+` V 1+ ",
+" 2 U F }+}+j $+/.-.-.-.M (.F.F.E+P E o O.......p+% Q.5 5 J.J.V V V l+V V V V ",
+" . F+F }+}+r+$+-.-.l M (.(.F.F.P b E f+O.O.......% 5 5 5 J.J.V J.J.J.% 1+ ",
+" U F }+j K /.f -.-.-.(.k F.F.P F.E E o O.O.6+q 6+% 5 5 5 5 J.5 % ",
+" H.}+}+r+/./.f )+M (.(./ F.P j+f+E f+o o O...k+6+6+6+6+% 6+O. ",
+" U }+K -.-.-.M M k (.F.F.P E E S.O.o O...O.....E ",
+" t+* /.f -.l M (.F.k E+E j+E E E f+O.%.P ",
+" -.-.M )+(./ k F.F.F.E E "};
diff --git a/arts/builder/pics/Synth_MOOG_VCF.xpm b/arts/builder/pics/Synth_MOOG_VCF.xpm
new file mode 100644
index 00000000..7f9662c3
--- /dev/null
+++ b/arts/builder/pics/Synth_MOOG_VCF.xpm
@@ -0,0 +1,305 @@
+/* XPM */
+static char * Synth_MOOG_xpm[] = {
+"64 64 238 2",
+" c None",
+". c #161E54",
+"+ c #4A962C",
+"@ c #3A5A6C",
+"# c #2E4254",
+"$ c #5ACA1C",
+"% c #3A4E74",
+"& c #6A7694",
+"* c #52AE24",
+"= c #3A427C",
+"- c #222E64",
+"; c #66E614",
+"> c #4E6E7C",
+", c #263664",
+"' c #464E8C",
+") c #4E5A9C",
+"! c #32466C",
+"~ c #7E86AC",
+"{ c #8692B4",
+"] c #5EDA14",
+"^ c #6EF614",
+"/ c #4E629C",
+"( c #2E3E74",
+"_ c #56BE1C",
+": c #3A4A74",
+"< c #2E3674",
+"[ c #3E4E84",
+"} c #767EA4",
+"| c #62D21C",
+"1 c #7EFE1C",
+"2 c #222E74",
+"3 c #46568C",
+"4 c #969ECC",
+"5 c #323E84",
+"6 c #1A2664",
+"7 c #6A76BC",
+"8 c #5AB62C",
+"9 c #72FE14",
+"0 c #5A62AC",
+"a c #4EA22C",
+"b c #5E6AB4",
+"c c #565EA4",
+"d c #868EB4",
+"e c #8E9ABC",
+"f c #3A467C",
+"g c #2A3674",
+"h c #4E5E94",
+"i c #5AC61C",
+"j c #3A4A8C",
+"k c #425684",
+"l c #7276A4",
+"m c #5AAE2C",
+"n c #5662A4",
+"o c #36427C",
+"p c #2E367C",
+"q c #425284",
+"r c #66D224",
+"s c #222A64",
+"t c #465A8C",
+"u c #2A3E64",
+"v c #62CA2C",
+"w c #3A5674",
+"x c #52B21C",
+"y c #223264",
+"z c #6AEE14",
+"A c #2A3A64",
+"B c #46528C",
+"C c #7E8AA4",
+"D c #8E92C4",
+"E c #62E214",
+"F c #76FA14",
+"G c #324274",
+"H c #2A3274",
+"I c #4E5694",
+"J c #3E4E74",
+"K c #36467C",
+"L c #5EC22C",
+"M c #324284",
+"N c #4EAA24",
+"O c #6672BC",
+"P c #3A468C",
+"Q c #32426C",
+"R c #162254",
+"S c #4E9E2C",
+"T c #6E76A4",
+"U c #6EE624",
+"V c #4E727C",
+"W c #2A3664",
+"X c #4E5AA4",
+"Y c #66DA24",
+"Z c #6EFA14",
+"` c #52669C",
+" . c #323E74",
+".. c #5AC21C",
+"+. c #3A4A84",
+"@. c #424E94",
+"#. c #7E82B4",
+"$. c #5ED614",
+"%. c #263274",
+"&. c #4A569C",
+"*. c #1E2A6C",
+"=. c #7AFE14",
+"-. c #666EB4",
+";. c #525EA4",
+">. c #424A94",
+",. c #2E3A7C",
+"'. c #4A5A9C",
+"). c #2E426C",
+"!. c #46529C",
+"~. c #7E8AB4",
+"{. c #4A9A2C",
+"]. c #5ECE1C",
+"^. c #365264",
+"/. c #6E7694",
+"(. c #56AE2C",
+"_. c #3A428C",
+":. c #262E6C",
+"<. c #364A74",
+"[. c #8A96BC",
+"}. c #62DE14",
+"|. c #5ABE2C",
+"1. c #2E3A6C",
+"2. c #424E8C",
+"3. c #7A82AC",
+"4. c #86FE1C",
+"5. c #4A5694",
+"6. c #9AA2CC",
+"7. c #1E2664",
+"8. c #6E7ABC",
+"9. c #3E626C",
+"0. c #56BA1C",
+"a. c #62CE24",
+"b. c #6AEA14",
+"c. c #5A66AC",
+"d. c #626EB4",
+"e. c #727AA4",
+"f. c #6EF214",
+"g. c #56AA2C",
+"h. c #4EA624",
+"i. c #5666A4",
+"j. c #8E96BC",
+"k. c #3E5274",
+"l. c #6EEA24",
+"m. c #526E8C",
+"n. c #5EC62C",
+"o. c #1A2264",
+"p. c #52728C",
+"q. c #62D624",
+"r. c #3A5274",
+"s. c #425294",
+"t. c #222A74",
+"u. c #56BA2C",
+"v. c #525A94",
+"w. c #8A92BC",
+"x. c #8A8EBC",
+"y. c #929AC4",
+"z. c #3E4684",
+"A. c #525E9C",
+"B. c #3E4A8C",
+"C. c #6AD224",
+"D. c #4A5A8C",
+"E. c #2E3E64",
+"F. c #56B224",
+"G. c #263264",
+"H. c #364284",
+"I. c #1A225C",
+"J. c #62D61C",
+"K. c #323A84",
+"L. c #5ECA1C",
+"M. c #36466C",
+"N. c #62DA14",
+"O. c #72F614",
+"P. c #52629C",
+"Q. c #5ABE1C",
+"R. c #82FE1C",
+"S. c #262E74",
+"T. c #9A9ECC",
+"U. c #363E84",
+"V. c #76FE14",
+"W. c #626AB4",
+"X. c #5EC61C",
+"Y. c #66E214",
+"Z. c #3E468C",
+"`. c #525AA4",
+" + c #72FA14",
+".+ c #3E4A84",
+"++ c #4A529C",
+"@+ c #828AB4",
+"#+ c #4E9A2C",
+"$+ c #66CE24",
+"%+ c #5E66AC",
+"&+ c #767AA4",
+"*+ c #52AE2C",
+"=+ c #3A4284",
+"-+ c #222E6C",
+";+ c #26366C",
+">+ c #464E94",
+",+ c #7E86B4",
+"'+ c #2E3E7C",
+")+ c #3A4A7C",
+"!+ c #3E4E8C",
+"~+ c #767EAC",
+"{+ c #465694",
+"]+ c #868EBC",
+"^+ c #8E9AC4",
+"/+ c #3A4684",
+"(+ c #2A367C",
+"_+ c #4E5E9C",
+":+ c #5662AC",
+"<+ c #42528C",
+"[+ c #66D22C",
+"}+ c #222A6C",
+"|+ c #52B224",
+"1+ c #2A3A6C",
+"2+ c #465294",
+"3+ c #7E8AAC",
+"4+ c #76FA1C",
+"5+ c #32427C",
+"6+ c #2A327C",
+"7+ c #4E569C",
+"8+ c #364684",
+"9+ c #2A366C",
+"0+ c #5266A4",
+"a+ c #323E7C",
+"b+ c #26327C",
+"c+ c #7AFE1C",
+"d+ c #2E3A84",
+"e+ c #6E769C",
+"f+ c #2E3A74",
+"g+ c #1E266C",
+"h+ c #6E7AC4",
+"i+ c #4EA62C",
+"j+ c #8E96C4",
+"k+ c #3E4A94",
+"l+ c #4A5A94",
+"m+ c #2E3E6C",
+"n+ c #26326C",
+"o+ c #36428C",
+" %.H H 6+6+%.S.2 t.}+*.7. ",
+" s f+H.H.K.d+,.(+b+S.%.S.2 2 }+2 o.. . . ",
+" < .+>.Z._.H.5 d+p p (+b+%.S.t.2 *.*.*.g+o.o.. . ",
+" 1.) 7+5.>+>.P o+U.K.d+,.(+b+S.2 2 2 t.2 2 t.*.*.6 o.. . . . ",
+" v.c ;.7+++@.k+P o+M K.p (+(+(+6+%.S.2 t.*.*.*.*.t.*.*.g+g+o.. . ",
+" I W.%+:+`.7+2+@.>.Z.o+U.5 d+p 6+%.%.S.2 -+2 t.t.t.g+*.*.g+*.g+*.o.. . . ",
+" d.d.b 0 c X &.>+>.Z.P o+5 K.d+p 6+b+b+S.S.t.-+*.*.t.g+*.*.g+*.g+*.g+6 o.. . ",
+" ' 7 O W.c.:+;.X &.!.k+j _.U.K.'+,.(+(+H %.%.2 2 t.2 t.*.t.g+g+*.g+*.g+g+g+g+o.. . . ",
+" = T O O W.c.n `.&.++>+>.Z.P H.K.d+,.(+b+b+S.2 S.2 t.-+t.*.*.*.g+*.g+g+6 g+g+6 g+o.. . . ",
+" I 8.O O W.c.0 ;.) &.2+@.B.P o+U.5 p (+6+6+S.b+S.2 }+t.*.*.*.*.*.*.g+*.*.*.g+*.*.g+*.o.. . . ",
+" = T 7 -.d.b c.c `.7+!.@.k+Z._.o+5 5 d+p g 6+S.S.2 2 -+t.2 t.*.*.g+t.g+g+g+*.6 g+*.6 g+6 o.. . . ",
+" ' 7 O O -.b c.c ;.X &.!.@.k+P H.5 5 ,.p (+6+%.%.%.S.2 *.t.*.*.g+g+*.g+*.*.g+g+g+6 g+g+*.g+g+o.. . . ",
+" O 7 -.d.%+c.:+;.X &.!.>+>.B.P o+5 K.,.,.(+(+b+2 2 2 2 2 *.*.*.t.*.*.g+g+*.*.*.*.g+*.g+6 g+6 6 o.. . ",
+" O O -.6.T.c.0 ;.X &.++>+!+B._.o+U.K.d+d+(+(+%.S.S.S.t.t.*.t.t.t.*.t.*.*.*.*.*.*.*.*.*.*.*.*.g+g+*.o.. . ",
+" .+-.-.W.4 4 c ;.`.7+!.>+>.Z.P _.H.U.K.p (+6+6+%.%.2 S.:.2 2 2 2 2 *.2 2 2 2 *.t.t.2 t.2 2 t.2 *.*.*.*.6 o.. ",
+" W.W.c.c.:+;.;.`.&.++!.@.>.j P H.U.5 ,.,.(+(+%.b+S.S.2 2 t.t.*.-+t.2 2 2 2 2 2 2 2 2 -+}+}+-+}+2 }+t.}+*.. . ",
+" I c.0 :+:+c X X 7+{+>+@.k+Z.P _.5 5 K.p p (+6+%.b+%.2 2 S.2 %.2 %.S.2 2 2 S.%.%.%.:.%.%.%.2 %.-+2 -+-+t.-+*.6 . ",
+" U.0 c :+Z.`.j+j+&.!.>+@.k+k+P _.5 U.,.,.,.(+%.%.%.S.2 %.S.%.%.S.S.2 %.%.%.%.%.S.%.%.%.%.%.S.%.S.%.n+S.2 :.2 }+}+I.. ",
+" ' ;.X ) /+&.D w.!.@.>.k+P _.o+o+5 K.d+(+(+(+6+b+%.%.S.%.%.%.%.%.H %.H %.H %.g %.%.(+%.%.;+%.;+%.%.%.n+%.:.%.n+-+}+6 ",
+" X 7+7+7+_.++{ ]+@.>.k+Z._.o+U.5 K.d+p p 6+6+%.H b+H %.%.%.%.%.b+b+%.%.9+;+).%.(+g H (+(+H (+6+H 6+;+H %.%.S.S.S.y I. ",
+" W ++!.!.>+U.>+]+@+k+Z._.P =+U.K.'+K.,.(+(+H 6+%.%.b+(+H 6+6+(+g g (+H , h.].u.W g ,.g (+,.g 1+(+g (+(+g g ;+%.%.S.H -+6 ",
+" >+s.>+@.@.!+k+Z.Z.P 8+=+U.5 5 a+d+,.(+(+H b+;+6+6+%.%.(+%.(+H (+(+g A * +9 ^ h.g (+,.,.(+,.p ,.,.< g g (+g g g g n+- . ",
+" 7.>.k+k+k+Z.Z.Z.Z._._.o+U.5 5 K.p (+p (+6+g 6+b+H (+(+%.(+H (+(+g (+, * ^ Z J.^ ].A f+,.a+,.'+,.f+,.,.,.< p < g g g H 9+n+7. ",
+" K.P Z.Z._.P d+8+,+,+U.5 5 5 d+p ,.p 6+6+;+b+%.H (+(+H (+(+,.,.(+,.A * ^ ^ g.# ].^ i+1+'+,.a+K.'+,.'+,.,.,.f+,.(+< g g H :.. ",
+" .o+o+_._.U.p o+,+,+5 K.K.p d+p (+(+H 6+6+6+(+%.6+(+(+(+(+(+,.(+A F.O.Z * u 1+i+^ ].1.a+a+a+a+5 a+a+a+'+'+,.f+,.p < g g H G. ",
+" U.H.U.M 5 5 H K.#.3.d+,.(+,.(+6+(+%.b+%.%.(+%.(+(+g p ,.p ,.,.u *+^ Z *+u '+a+E.$ f.i+).5 M U.5+5 5 a+5 a+a+a+f+'+,.f+g 9+H ",
+"%.5 5 K.5 5 d+%.,.~+3.,.p (+(+(+%.6+%.b+H %.(+g (+(+(+g ,.,.,.^.n. +O.*+m+5 5 M G h.f.L.).a+M H.o o M o 5 a+5 a+,.'+,.,.,.1+1+g ",
+"H d+d+d+p d+,.,.d+,.p (+(+6+H %.6+%.%.%.(+6+6+(+g (+,.,.,.1+u L +O.|+E.a+U.H.H.H.m+$ z a o /+H.H.H.H.H.o M o M 5+a+a+a+,.,.p g ",
+"H p p d+p p p (+6+6+6+(+b+b+%.S.S.S.S.%.%.;+6+(+,.g 1+1+A N | + +*+m+5 H.8+H.H.8+o h.z $ ).K 8+8+/+8+8+H.H.o U.a+a+a+a+K.f+f+< ",
+"6+(+(+6+(+(+6+-+(+} e.n+;+, n+;+;+;+;+n+;+9+9+9+;+A g.a.| 4+V. +*+m+5+8+8+H.8+8+/+8+Q $ b.a o P P /+/+/+/+8+H.K H.o a+ .a+a+,.1+",
+"6+6+(+b+b+(+b+}+b+~+& m C.C.r $+$+$+r r $+a.$+a.a.J.O.c+V.V.U L E.U.H.H.8+_./+8+P P f h.z L.! +.j P /+/+/+/+H.8+H.H.M 5+a+a+K.,.",
+"S.%.S.%.%.S.%.}+%.&+& C.4.4.4.R.R.R.1 1 1 1 =.=.=.c+V.l.U [+@ r.H.8+H.8+P +.P B.B..+/+! i b.a f B.+.j j +.j +./+8+8+o U.5+a+a+f+",
+"S.b+S.S.b+S.%.}+2 e.& g.r $+r C.r $+$+a.a.a.a.a.].v |.9.r.r.M M H.8+P 8+P j +.j !+!+B.)+a b.i M.B.!+!+B.+.Z.+./+/+/+8+o o 5+5+a+",
+"2 2 S.S.2 2 2 2 2 t.t.y y y - - - , , , W A 1+1+1.m+m+'+5+5 M 8+8+P /+j +.B.B.!+B.B.!+>.M.i ; a % 2.B.!+B.[ B.+./+8+8+H.o U.U.,.",
+"t.2 2 2 S.t.t.2 S.2 t.}+t.-+-+-+-+2 %.;+(+g (+,.,.K.a+5 H.H.H.o+/+P P P j j B.!+!+2.<+<+)+a ; ..<.2.!+2.B.B.!+B..+z.+.8+8+o o a+",
+"}+2 2 t.t.S.2 *.-+l e.*.-+t.*.2 S.%.H 6+6+(+,.(+,.'+5 a+M H.=+/+8++.B.B.B.!+!+!+s.s.@.<+<+% i ; S [ <+<+<+2.>.[ B.+./+/+f H.o a+",
+"g+}+-+-+*.t.}+g+}+T e.*.*.t.}+2 2 2 %.;+(+(+,.,.'+K.M a+H.8+8+P P j +.!+!+2.@.2.<+<+s.2+2+[ a ; ..: 2.s.<+<+!+[ B.B.j /+/+8+K o ",
+"*.*.t.*.t.*.*.g+*.l l *.*.*.2 -+S.%.b+6+g (+,.,.K.a+a+H.H.H._.+.+.j !+!+@.!+<+s.s.{+2+{+3 3 % ..E S q s.<+<+<+2.!+[ .++.+./+=+a+",
+" *.t.*.t.t.*.*.*.T l t.}+*.t.-+S.%.;+(+g (+,.,.'+5 5 M H.8+P P Z.B.B.!+s.@.<+2+s.{+{+{+5.5.q a E _ J {+2+B s.<+!+!+B.+./+8+/+ ",
+" *.*.*.*.*.*.g+t.}+*.g+*.}+-+2 2 S.H H p ,.,.'+5 a+H.H.8+8+8++.j B.!+!+2.<+2+s.{+{+{+{+'.t {+k...E S q {+s.<+' 2.2.!+.+.+/+K ",
+" . g+*.g+t.*.*.*.g+*.g+*.*.2 S.%.%.(+;+(+,.,.,.5 a+H.H.=+P P +.!+B.@.s.s.<+{+{+5.5.'.5.'.'.) t 8 E _ % 3 {+{+2+<+2.!+!+.+/+ . ",
+" I.*.*.*.*.*.6 g+l T *.g+t.}+-+S.S.%.(+(+(+,.a+a+M M H.8+/++.B.j !+!+2.<+2+{+5.{+'.t _+) _+l+l+> Y }.S k 5.{+k <+<+2..+.+f - ",
+" o.*.g+g+g+6 *.l T *.g+*.2 2 %.%.%.g ,.p ,.'+a+U.H.H.8+P j B.!+2.!+s.s.2+2+5.t '.'.l+_+_+_+_+m.v E 0.J {+5.2+<+2.2.!++.z. ",
+" o.g+*.*.*.g+g+e+l g+*.t.*.-+2 S.H (+g ,.,.K.5 5+M 8+_./+/+B.B.!+s.2.3 {+{+'.&.) '._+_+A.P._+h V J.N.S q {+{+2+<+<+[ [ G. ",
+" o.g+g+*.6 *.e+T g+*.}+t.:.%.%.H (+g ,.'+K.a+H.H.8+/+j +.B.!+2.s.s.2+{+{+l+l+h _+_+/ _+P.P.A.m.v }.0.J 3 {+{+<+2.2.2. ",
+" 6 *.*.6 *.g+g+6 *.*.*.-+2 2 b+;+H ,.,.,.'+5 a+H.H.8+/+j !+B.!+2.<+{+{+{+l+'.'._+_+_+P.P.P.P.P.V | ] {.q 3 <+<+<+[ = ",
+" . o.g+g+g+6 *.6 g+*.}+-+S.n+%.(+%.g ,.,.,.5 a+H.8+/+/+P !+!+<+@.<+<+{+{+l+) l+_+A.P.0+0+0+i.P.m.n.}.0.J {+2+B 2.[ z. ",
+" g+*.g+6 g+T l 6 g+*.*.-+2 %.%.(+(+< a+a+a+M H.H.H.P +..+>.!+<+s.{+{+&.t '.h _+/ / P.P.i.P.P._+> L.$.{.q 3 <+<+: ",
+" o.g+g+g+T l *.g+}+2 :.S.%.%.g ,.(+,.a+a+5+H.8+8+P j +.B.2.2.@.B {+5.l+'.l+_+A./ P.P.0+P.P._+h {.F.+ q s.B 2. ",
+" o.6 6 7.e+e+e+l *.*.l e.&+~+;+g 3.3.3.#.5 H.,+~ @+@+B.!+d { w.{ {+{+w.j.j.j._+A.y.^+y.^+_+h [.C C ~ <+3 <+W ",
+" o.g+g+T T e+l }+-+e.e.} &+b+g } 3.~ ~ a+o @+3+~ @+B.B.@+d ]+d 2+{+w.[.[.[._+_+e e e ^+_+) e [.[.w.B B 2. ",
+" 6 6 o.g+6 *.*.*.}+n+S.%.g (+,.1+,.'+5+U.H.H./+/+/+j 2.!+<+s.{+{+{+'.l+) '.h _+_+_+h h h ) l+3 5.3 q ",
+" . o.6 g+o.6 g+*.g+*.-+}+;+(+S.n+H H a+a+1+,.f+,..+j a+a+5+a+<+3 o H.K 8+'.) /+/+/+/+l+l+f f {+2+2.z. ",
+" . g+6 6 g+*.}+-+:.S.%.H %.g (+,.,.'+5 U.H.8+/++..+.+!+2.<+<+s.2+{+{+5.5.l+l+) ) l+l+5.{+{+3 q W ",
+" . I.7.7.*.*.*.-+:.%.g g < g f+a+a+5+a+H.8+H.8+j .+B.B.2.2.<+B B 3 {+{+{+t t l+5.{+{+B 2+<+f ",
+" . 6 6 7.g+-+}+2 :.%.H g p ,.,.'+a+M a+H.8+/+/+P .+[ !+<+<+<+s.2+3 3 5.5.5.{+{+3 3 {+q W ",
+" . o.*.7.*.-+2 :.%.%.;+(+g f+,.a+5+5+H.H.8+/++.j B..+B.2.<+<+s.B <+s.<+B {+{+B <+<+z. ",
+" o.*.}+}+-+-+%.%.g g ,.p f+K.a+U.o o =+8+8+z.+.B.!+!+2.!+2.<+s.B 2+<+<+<+<+' ",
+" R *.*.-+-+%.S.H H g ,.f+,.( a+M 5+o /+8+/++.+..+.+[ !+!+2.!+<+<+2.2.2.)+ ",
+" *.}+}+:.n+%.;+g g ,.f+K.'+a+a+H.H.H./+8+/+P +.B.[ B.[ 2.!+2.!+[ ",
+" 7.*.:.n+%.H %.g < f+K. .a+a+a+o H./+/+/++.+.z..+.++..+.+.+f ",
+" *.:.%.g g g g f+,.a+a+5+o 5+o o 8+/+/++.+.+..+o ",
+" s s ;+g g < f+,.,.'+a+a+H.o o o H./+U.G ",
+" H g g f+f+f+K.a+a+a+U.o "};
diff --git a/arts/builder/pics/Synth_MUL.xpm b/arts/builder/pics/Synth_MUL.xpm
new file mode 100644
index 00000000..9261785e
--- /dev/null
+++ b/arts/builder/pics/Synth_MUL.xpm
@@ -0,0 +1,309 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"64 64 238 2",
+/* colors */
+" c #A3A8C1",
+" . c #354280",
+" X c #4E5688",
+" o c #A0A4BE",
+" O c #2F3C7A",
+" + c #1B2463",
+" @ c #3A4888",
+" # c #3E4982",
+" $ c #293474",
+" % c #CDD0E0",
+" & c #626B98",
+" * c #38437C",
+" = c #232E6E",
+" - c #4A5487",
+" ; c #C1C4D4",
+" : c #1C2667",
+" > c #2F3973",
+" , c #3E4B85",
+" < c #2A3678",
+" 1 c #253073",
+" 2 c #35417C",
+" 3 c #202A6E",
+" 4 c #2F3A80",
+" 5 c #1E286C",
+" 6 c #455085",
+" 7 c #1C266A",
+" 8 c #AAAFC7",
+" 9 c #29347A",
+" 0 c #263077",
+" q c #253076",
+" w c #374581",
+" e c #475594",
+" r c #232E74",
+" t c #323F7C",
+" y c #313D7B",
+" u c #576393",
+" i c #3D4B8A",
+" p c #2B3775",
+" a c #BBC0D4",
+" s c #3A4787",
+" d c #4B579B",
+" f c #5C67AF",
+" g c #394786",
+" h c #263170",
+" j c #354382",
+" k c #242F6E",
+" l c #445194",
+" z c #6872B4",
+" x c #38447E",
+" c c #343F84",
+" v c #1D276A",
+" b c #2D377D",
+" n c #C0C3D5",
+" m c #434D82",
+" M c #283378",
+" N c #3A4883",
+" B c #273177",
+" V c #263176",
+" C c #525A8A",
+" Z c #252F75",
+" A c #242F74",
+" S c #455491",
+" D c #35427E",
+" F c #212B71",
+" G c #202B70",
+" H c #6C76AA",
+" J c #404E8C",
+" K c #616CB3",
+" L c #2D3876",
+" P c #555E90",
+" I c #2B3674",
+" U c #3A4886",
+" Y c #374483",
+" T c #A1A6C0",
+" R c #232E6C",
+" E c #313E7D",
+" W c #2D3A79",
+" Q c #3E4A8D",
+" ! c #2A3676",
+" ~ c #384487",
+" ^ c #273273",
+" / c #253071",
+" ( c #414C93",
+" ) c #43518B",
+" _ c #1E286A",
+" ` c #1C2668",
+" ' c #1B2667",
+" ] c #2B367A",
+" [ c #4B5B96",
+" { c #273276",
+" } c #242E73",
+" | c #222C71",
+". c #455390",
+".. c #212C70",
+".X c #202A6F",
+".o c #1F2A6E",
+".O c #1E286D",
+".+ c #3F4D8A",
+".@ c #2F3B77",
+".# c #3E4D89",
+".$ c #3E4B89",
+".% c #4C599A",
+".& c #384583",
+".* c #283370",
+".= c #3C467D",
+".- c #555FA6",
+".; c #424F90",
+".: c #212B69",
+".> c #3F4D8D",
+"., c #3E4D8C",
+".< c #333C74",
+".1 c #2D3978",
+".2 c #4F59A0",
+".3 c #6E759A",
+".4 c #2C3777",
+".5 c #2B3776",
+".6 c #485399",
+".7 c None",
+".8 c #263171",
+".9 c #DCDEE8",
+".0 c #212B6C",
+".q c #313B7F",
+".w c #9A9FBB",
+".e c #3F4B90",
+".r c #283376",
+".t c #4A5A94",
+".y c #263174",
+".u c #394680",
+".i c #354186",
+".p c #182059",
+".a c #232D71",
+".s c #45548F",
+".d c #323D83",
+".f c #7279A4",
+".g c #2E397F",
+".h c #C1C5D7",
+".j c #1D276B",
+".k c #404E8A",
+".l c #3E4C88",
+".z c #2A357B",
+".x c #29357A",
+".c c #9399B7",
+".v c #283379",
+".b c #3B4885",
+".n c #263177",
+".m c #475694",
+".M c #5862A8",
+".N c #777EA2",
+".B c #435290",
+".V c #33407D",
+".C c #435090",
+".Z c #41508E",
+".A c #3E4C8B",
+".S c #3C4A89",
+".D c #2B3675",
+".F c #293473",
+".G c #384685",
+".H c #8E95B5",
+".J c #273271",
+".K c #B7BBD0",
+".L c #CBCEDD",
+".P c #354282",
+".I c #1F2A69",
+".U c #1E2868",
+".Y c #3D488D",
+".T c #384288",
+".R c #4A5993",
+".E c #C6CADB",
+".W c #353F7B",
+".Q c #B0B5CC",
+".! c #AEB3CA",
+".~ c #414F8A",
+".^ c #1C2669",
+"./ c #2B367B",
+".( c #8087AA",
+".) c #293479",
+"._ c #283278",
+".` c #2B3571",
+".' c #273277",
+".] c #4A5996",
+".[ c #263076",
+".{ c #253075",
+".} c #7B81A5",
+".| c #242E74",
+"X c #232E73",
+"X. c #465592",
+"XX c #222C72",
+"Xo c #212C71",
+"XO c #445390",
+"X+ c #202A70",
+"X@ c #1F2A6F",
+"X# c #42518E",
+"X$ c #495181",
+"X% c #2F3B78",
+"X& c #3C4988",
+"X* c #D0D3E1",
+"X= c #A6ABC5",
+"X- c #4E5C93",
+"X; c #4C5A91",
+"X: c #344180",
+"X> c #323F7E",
+"X, c #222D6B",
+"X< c #2E3B7A",
+"X1 c #6C7399",
+"X2 c #293575",
+"X3 c #B3B8CE",
+"X4 c #222D6E",
+"X5 c #434D95",
+"X6 c #878EB0",
+"X7 c #20296C",
+"X8 c #5C6693",
+"X9 c #333E78",
+"X0 c #2F3B7E",
+"Xq c #1E296A",
+"Xw c #1D2769",
+"Xe c #2C377B",
+"Xr c #3B458D",
+"Xt c #293378",
+"Xy c #495894",
+"Xu c #485893",
+"Xi c #242F73",
+"Xp c #353F87",
+"Xa c #475692",
+"Xs c #232D72",
+"Xd c #222D71",
+"Xf c #455490",
+"Xg c #212B70",
+"Xh c #202B6F",
+"Xj c #43528E",
+"Xk c #C3C7DA",
+"Xl c #1F296E",
+"Xz c #42508D",
+"Xx c #1E296D",
+"Xc c #41508C",
+"Xv c #D4D6E4",
+"Xb c #4E5C9C",
+"Xn c #E6E7EF",
+"Xm c #3A4885",
+/* pixels */
+".7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7 1 {XtXt._ 0 Z r F 3Xq :.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7 I y.TXp.d 4 b.z 9 B q ZXsXXXo GX+Xl + +.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7X&X5 (.Y.TXp.d 4 b.z 9 B q ZXsXXXo G.XX@XlXl 5 +.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7 x.2.2.6X5 (Xr.TXp.d 4 b.z.v B q.|XsXXXo G.XX@XlXx.O.O 5.^ +.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.M.M.-.2.6X5.eXr.TXp.d 4 b.z.v B q.|XsXX F G.X.oXlXx.O 5 5 5.j `.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.7.7.7.7.7.7.7.% K f.-.2 d.6X5.eXr.TXp.d.g b.z.v.n.{.|XsXX F G.XXlXlXx.O 5 5.j.j.j.^ +.7.7.7.7.7.7.7.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.7.7.7.7.7.7 z K f.M.-.2 d.6 (.YXr.T c.d.g b.z._.n Z.|Xs |XgX+.XXlXlXx.O 5 5.j.j.j v v '.7.7.7.7.7.7.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.7.7.7.7 ) z z K f.M.-.2.6 l (.YXr.T.d 4.g./ 9._.[ ZX XsXoXg.XX@XlXl.O 5 5 5.j.j v v v v `.p.7.7.7.7.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.7.7.7X; z z K K f.-.2 d.6X5.eXr.TXp.d 4 b.z 9.'.[ AX XsXoXg.X.oXlXx.O 5 5.j.j.j v v vXw.^ :.p.7.7.7.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.7.7X- z z z K f.M.-.2 d.6X5.eXr.T c.d 4 b.z.v B q.|XsXX F G.X.oXlXx.O 5 5.j.j.j v v vXw.^.^ :.p.7.7.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.7X; z z z K f.M.-.2.2.6X5 (.YXr.i c 4.gXe 9._.n Z.|Xs |XgXh.XXlXlXx 5 5.j.j.j v v vXw.^.^.^ ` :.p.7.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7 ) z z z K f.M.-.-.%.6 l (.YXr.TXp.d 4 b./ 9.'.[ ZX XsXoXg.X.oXlXl.O 5 5 5 v.^.^ ` `.^ ` `.^ ` ` :.p.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7 z z z K f.M.M.-.MXb e.;.eXrXr.T c.d.g b.z.v B.{ AXsXXXo G.X.oXlXlXxXx 5 5.j _.U.U.UXwXw.^ `Xw ` ` :.7.7.7.7.7.7.7",
+".7.7.7.7.7.7 z z K K f.M.M.M &.} H &Xa.$ ~.T c.d 4 b./ 9._ V Z }Xs |Xg G.X.X 3 3 3 3X7Xq.: R.`.<.*.:.U.U v _ _XwXw +.7.7.7.7.7.7",
+".7.7.7.7.7.% K K K f.M.-.- H.(X=.w.H & 6.& c.d.d.g b.zXt.'.[ AX Xs |.................0.0.* *X$X8.=.*.:.IXqXqXqXqXq.U.p.7.7.7.7.7",
+".7.7.7.7.7 K f f f.M.-.2.- HX6X3.Q.Q.(X8 m.V.q.g b./ 9._ V Z.|X XsXsXdXdXd.a.a.aX4X4 R.`.= &.N.HX8.=.*X,.0.0.0.0X7Xq.U.7.7.7.7.7",
+".7.7.7.7.M f.M.M.-.-.2.2Xb H.cXk %Xv.Q.H P x O b ].zXt B.[ Z.| } } }XiXiXiXiXiXi.a k.` mX1 X3XkX6X8.< RX,X4X4X4.0.0.0.U.7.7.7.7",
+".7.7.7 x.M.-.-.-.2.2 d.6XyXb.f T aXv.h.!.} C.= LXt M B V.[.{ Z Z 1 1 1 1 1.y 1 / h >X$X1.H a.KX3.NX$ > k k.a / k =X4X4.0 +.7.7.7",
+".7.7.7.2.-.2.2.2 d d.6 l.C.C u.(X=X*XvXv 8X6 CX9 I { V B V V.y.y.y.y.y { { { ^.J > m.3X= n.9 nX= &.=.` h h /.8 / / k = R.:.7.7.7",
+".7.7.7.2.2 d d.6.6.6X5 (.e Q ) P.NX= aXv aX=.f -.<.J.y V V { { {.'.'.r.r { $.`.<X$.3.c ; ; ;.HX1X$ >.*.J.J $ ^.J.8 / / k =.7.7.7",
+".7.7X&.6.6.6.6 lX5X5 (.eXrXr ~.b P.( X*X*Xv .}X$ >.J { { {.r M MXt.)XtX2 $.<X$.3 8.h.9 ; 8X1 m.<.`.FX2X2X2X2 $.F.J.J h k.U.7.7",
+".7.7X5X5X5X5 ( (.e.e.YXr.T.T cX:.= X.f T.KX*.K .3X$.<.J ^.r M.x.x < ! ! I.<X$.N.c.h.h ;.c.3X$.< > p.5.4.4.4.4 !X2 $.F.J.8 k.7.7",
+".7 I ( (.e.e.Y.YXrXrXr.T.TXp.d.qX0.W X.} TX*X*X* o.NX$ >.FX2Xt.x.x <.4.D.< -.N.!Xk.9 ; 8.3X$.< L.1 W.1 W.1.1.1.4 ! !X2.F.J.J.:.7",
+".7 y.YXrXrXrXrXr.T.T.iXp c.d 4.gXe.1.WX$.3 o.KX*.K oX1X$.< I ! ].4 < >X9 X.N.wXk.h ;.c.3X$X9.@.1 O O OX0 OX< W.1.1.4 !X2 $.F.:.7",
+".7.T.T.T.T.T.T.TXp c c.d.d 4.g b.z.) I >X$.N TX*X* % o.NX$.< p ].4 L.W X.N.Q.E.9 nX=.3X$ * O O E E E E E E y O O W.1.4.5.DX2.*.7",
+".7XpXpXpXpXp c.d.d.d 4 4.g b b./ 9 M ^.J.<X$.3 T.KX*.K oX1X$.W.@.< * C.N.w.EXk n.c.3X$ *X9 EX>X>X:X:X>X>X> E y y OX< W.1 L.DX2.7",
+" 1.d.d.d.d.d.d 4 4 4.g b b./.z 9Xt._ V.y.J >X$.} TX*X* % o.f X * m C.}X3.E.9.hX=.3X$.=.VX>.PX:.P.P .X:X:X:X>X> E y O O W.1 L p.*",
+" { 4 4 4 4.g.g.g b bXe./.z 9Xt._ B V.[.y ^.J.<X$.3 .KX*.K o.NX8.3.( .EXk n.c.3 X.= 2X: j Y Y Y Y Y j.P .X:X:X>X> E y OX%.1 L I",
+"Xt b b b b b b./.z.z 9 9.v._.' V.[.[ V V { {.F >X$.} XvXvX*.K T.QXkX*.9 nX=.3 -.= . j Y.G.G.G.G.G.G.& Y Y j .X:X:X> E y OX%.1 p",
+"Xt.z.z.z.z.z.z 9 9.v._.' B V.[ Z Z.{ V { {.r $ I.< -.N.! ;Xv.L.h.LXv.L.E.c.3 X.= x Y.G s s s s g g g g.G.&.& Y j .X:.VX> y OX% L",
+"._ 9 9.v.v.v._._.' B.n.[.{ Z A.|.| Z.y {.r MXt ! p.< C.H.Q.9XnXnXn.9.E.Q.N X.= w.G g @.S.S.S.SX&X& @ @ s g.G.&.& j .X:.VX> E O.1",
+" 0 B B B B.n.n.[.[ q Z Z A }X X } Z.y { M.x.x ].D.@X$X6 8.9XnXnXnXnXkX=X1 6 # g s.S.S i i i i i i.S.SX&X& U g.G.& Y . D.VX> yX%",
+" Z q q q q.{ Z Z A.|.|X XsXsXsXs } 1.y.' M.x ] ].4 LX$.( 8.9XnXnXnXn n.w & m N @.S Q.A.A.,.,.,.A.A.A.A i.SX& @ U.G.& Y . D.V t O",
+" r Z Z.|.|.|.|X X XsXsXsXX | |Xs } 1.y.'Xt < <.4 >.=X8.w a.9.9XnXnXn.LX3.} P 6 ,.S.A.>.>.>.> J J J J.,.A.A i.SX& U g.& Y . D.V y",
+" FXsXsXsXsXsXsXsXsXX |XoXoXg..XdXi 1.y.r.).).4 p * C.( ;X*Xn.9X*.9XnXv %.w.f C.l.$ J.>.Z.Z.Z.Z.Z.Z.Z.Z J.,.+.A.$X&.b.G.& Y . D t",
+" 3XXXXXXXXXX |XoXo FXgXg G G..XdXi 1 { { !.1X9.=X8.( .LXk a TX6 oX3.hX*X3.w.3 C ).~.Z.Z.B.B.B.B.C.CX#.Z.Z J.+.$.$X&Xm.G.& w D t",
+"XqXoXoXo F FXgXgXg GXh.X.X.X..XdXi 1.y {.4.W -X1.H n.LXv.K.w.NX8.f.H.QXvX*.E.c.f C ) ).BXO S S SXOXOXOXjX#.Z J.+.$.$X&Xm.&.& . 2",
+" : G G G G GX+.X.X.X.X.o.o.X...aXi 1 ^.D * C.3.c.!.L nX3X6 & C m XX8.}.! a %.Q.c.3 P.s. . X. eX.X.X. SXO.B.CXzXc.+.$.S.bXm.& w 2",
+".7X+.X.X.X.X.XX@.o.oXlXlXl 3...aXs 1X2.W C.( T.E %Xv.Q.c & 6 #.&.u #X8X6.!Xv %Xk.c.f P.sXf.m.mXy.m.m.mX. SXOXjXz.~.+.lX&.b.& w.7",
+".7XlX@X@.oXlXlXlXlXlXlXlXl 3XhXd /.F * C.N .K % a.!.( & -.u w g N #X$X8.(X3.hX*.Q.c.3 PX;XuXu.].]XyXy.mX. SXOXjXc.k.#.l.bXm.u.7",
+".7 +XlXlXlXlXlXlXxXxXx.OXx 3Xh =.JX9 C.( .L %X*.!.HX8 m.u Y g @X&.S # mX8.w.K.9 %.h.c.f u.RXy.] [.].]Xy.mX. SXOX#Xc.+.lX&.bX9.7",
+".7 +XlXxXxXxXx.O.O.O 5 5 5XlX4 h.W C.NX=.K.L a 8.(X8 6 x w.G g.S.S i , , C.N.w.E.E.E 8.c.f uX-.t [ [ [.]Xu.mX.. XjXc.k.$X&.bX9.7",
+".7.7 5.O.O.O.O 5 5 5 5 5.jX7 k.< C.(X=.L.L %X=X6X8 # x . Y g @.S.A.A J J.sX8.(.Q n %Xk n.c.f u [ [.%.% [.]XuXa SXOX#.~.#.l N.7.7",
+".7.7 +.O 5 5 5 5 5 5 v.j.I R.< C.}X=.K.L.KX=.} u m 2 D j Y s.S i.A.> J.Z )X; &X6 .hXk.E 8.H.f uX- [ [ [ [XyXuX.XOXj.~.#.l 2.7.7",
+".7.7.7 5 5 5 5 5.j.j 7XwX, > CX6X= %.L.L .( P.= 2X>X: Y.G s.S i.,.>.Z.BXO. X; &X6.KXk %Xk a.H H uXb [Xb [.]XuXaXfXjXc.k.l.7.7.7",
+".7.7.7.^ 5.j.j.j.j.jXw.U > -.NX=.K.LX3 T.N C #X9 yX>.P Y.G s.S i.,.>.Z.BXO SXaX-X1.HX=XkXkXkX=.H.3 uX-Xb [.]XyXaXfXjXc.k ,.7.7.7",
+".7.7.7 +.j.j.j.j.j.j.UX,.=.f.w.E.E.E o.} C *X9 O EX:.P Y.G s.S i., J.Z.B S eXaXu u H.H aXk %.h.KX6 & u [ [ [XyXaXfXjXc.k.u.7.7.7",
+".7.7.7.7 `.j.j v v vXw.I.<X8.( 8 T.c.3 X *.@X% O EX:.P Y.G gX& i.A J.Z.B SX..mXuX; u.f.H 8Xk a.Q.( &X- [ [ [XyXaXfXjXc ,.7.7.7.7",
+".7.7.7.7.7.^ v v v vXw.U > X.3.c.(X1X$X9 L.1 WX0X>X:.P Y.G gX& i.A J.Z.CXOX..mXyXy.t u.f.H aX3 8.N uX- [ [.tXuXa.sXj.k.7.7.7.7.7",
+".7.7.7.7.7 + v v v v `Xw.: >.= XX$.=.< I.D.4 W y EX: . Y.G g @.S.A J.Z.CXOX..mXy.].]X- u.3X6.(.NX8X-X; [.].]XuX.. Xj.u.7.7.7.7.7",
+".7.7.7.7.7.7 ' vXwXw ` `.U.:X,.*.*.*.J $ !.1X< O EX>X: j.& g @.S.A.,.ZX#XO S.mXy.].].t [ u & & uX-X;.t [ [XyXaXfXj.~.7.7.7.7.7.7",
+".7.7.7.7.7.7.7 `.^.^.^ ` `.U.:.: R k.8 $X2.1 WX0 EX>X:.P Y.G sX& i.A J.ZXjXOX..mXy.].].tX- PX-X;X;.t.t [.tXuXa.s ).7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.p :.^.^.^Xw vXq.0X4 / ^ $ !.4.1 O EX>X: . Y.& gX&.S.A.,.ZX#.B SX..mXu.].].t.t.t.t [.] [.tXuXaXf ).=.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.7.p : ` `Xw _Xq.0X4 /.8 $X2.4.1X< y EX>X: j.&.G UX& i.+ J.Z.CXO SX..mXuXyXyXy.t.tXy.]XyXuXaXf ).=.7.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.7.7.p : ` ` _Xq.0 = /.8 ^X2.4.1 W O yX>X: . Y.& g @.S.A.+ JXzXjXO SX.XaXuXuXyXyXyXuXuXaXaXf ) *.7.7.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.7.7.7.p : `XwXq.0X4 k /.J $ !.4.1 O y EX>X: j.&.G UX&.$.$.+XcXzXjXO. SX.XaXaXaXaXaX.Xf.s ).=.7.7.7.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.7.7.7.7.p :XwXqX7.0 = /.8.FX2 !.1 W O yX>X: . j.&.G UX&.$.$.+.~XcX#XjXOXOXfXfXfXf.s. Xj ).=.7.7.7.7.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.7.7.7.7.7.7 +.UXq.0X4 k /.J $ !.4.1X< O EX>X: . Y.& g.bX&.$.$.+.kXcXcX#XjXjXjXjXjXjXj.~.7.7.7.7.7.7.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.7.7.7.7.7.7.7.p.U.0X4 = /.J.FX2 !.4 W O y E.VX: . Y.&.GXmX&.S.l.#.+.k.~.~XcXcXcXc.k.u.7.7.7.7.7.7.7.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.U.0 R k h.J.FX2.5.1 W O yX>.V D . Y.&.GXm.bX&.l.l.$.#.#.k.k.k ,.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7 +.: = k.8.J $.D L.1X% O yX>.V D . Y.&.&Xm.b.bX&X&.l.l.l ,.u.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.U k.J.FX2.D L.1X% O EX>.V D . w.&.&.&Xm.b.b N 2.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.:.:.*X2 p L.1X% O y t.V D D . w w.uX9X9.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7",
+".7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.* I p L.1X% O y t t 2 2.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7"
+};
diff --git a/arts/builder/pics/Synth_MULTI_ADD.xpm b/arts/builder/pics/Synth_MULTI_ADD.xpm
new file mode 100644
index 00000000..ec088b69
--- /dev/null
+++ b/arts/builder/pics/Synth_MULTI_ADD.xpm
@@ -0,0 +1,1510 @@
+/* XPM */
+static char * Synth_MULTI_ADD_xpm[] = {
+"64 64 1443 2",
+" c None",
+". c #253073",
+"+ c #273276",
+"@ c #293378",
+"# c #283278",
+"$ c #263077",
+"% c #252F75",
+"& c #232E74",
+"* c #212B71",
+"= c #202A6E",
+"- c #1E296A",
+"; c #1C2667",
+"> c #2B3671",
+", c #313D7B",
+"' c #384288",
+") c #353F87",
+"! c #323D83",
+"~ c #2F3A80",
+"{ c #2D377D",
+"] c #2A357B",
+"^ c #29347A",
+"/ c #273177",
+"( c #253076",
+"_ c #232D72",
+": c #222C72",
+"< c #212C71",
+"[ c #202B70",
+"} c #202A70",
+"| c #1F296E",
+"1 c #1B2463",
+"2 c #3C4988",
+"3 c #434D95",
+"4 c #414C93",
+"5 c #3D488D",
+"6 c #202A6F",
+"7 c #1F2A6F",
+"8 c #1E286C",
+"9 c #37437E",
+"0 c #4F59A0",
+"a c #485399",
+"b c #3B458D",
+"c c #283379",
+"d c #242E74",
+"e c #1E296D",
+"f c #1E286D",
+"g c #1C2669",
+"h c #5862A8",
+"i c #555FA6",
+"j c #3F4B90",
+"k c #1F2A6E",
+"l c #1D276B",
+"m c #1C2668",
+"n c #4B5A97",
+"o c #616CB3",
+"p c #5C67AF",
+"q c #4B579B",
+"r c #2E397F",
+"s c #263177",
+"t c #253075",
+"u c #6872B4",
+"v c #343F84",
+"w c #222C71",
+"x c #212B70",
+"y c #1B2666",
+"z c #1B2366",
+"A c #45538B",
+"B c #445194",
+"C c #2B367B",
+"D c #263076",
+"E c #232E73",
+"F c #1D266B",
+"G c #1E266C",
+"H c #1A2264",
+"I c #1D276A",
+"J c #171F57",
+"K c #4A5996",
+"L c #273277",
+"M c #242F74",
+"N c #1A2664",
+"O c #1E296C",
+"P c #1D296B",
+"Q c #1E2A6C",
+"R c #354186",
+"S c #2C377B",
+"T c #202B6F",
+"U c #6672BC",
+"V c #666EB4",
+"W c #5E6AB4",
+"X c #5A66AC",
+"Y c #565EA4",
+"Z c #525EA4",
+"` c #4E5AA2",
+" . c #4A569C",
+".. c #465296",
+"+. c #424E94",
+"@. c #3E4A8E",
+"#. c #3E468C",
+"$. c #36428C",
+"%. c #363E84",
+"&. c #626EB4",
+"*. c #5E66AC",
+"=. c #5662AB",
+"-. c #525AA4",
+";. c #4E5997",
+">. c #4D578F",
+",. c #4F5789",
+"'. c #4E528A",
+"). c #485381",
+"!. c #444D84",
+"~. c #414984",
+"{. c #3F477B",
+"]. c #3E457B",
+"^. c #3C4376",
+"/. c #3B4174",
+"(. c #383F70",
+"_. c #323A71",
+":. c #31386F",
+"<. c #32396C",
+"[. c #2F376C",
+"}. c #313769",
+"|. c #2F3669",
+"1. c #313868",
+"2. c #2C3469",
+"3. c #2C3368",
+"4. c #2D3566",
+"5. c #2C3267",
+"6. c #2D3465",
+"7. c #2D3464",
+"8. c #2E3263",
+"9. c #2A2F66",
+"0. c #2A3366",
+"a. c #2D3564",
+"b. c #343C60",
+"c. c #333761",
+"d. c #2C3565",
+"e. c #535C94",
+"f. c #676682",
+"g. c #7A7368",
+"h. c #857C5F",
+"i. c #867C58",
+"j. c #817559",
+"k. c #78705D",
+"l. c #766C59",
+"m. c #756B57",
+"n. c #766D52",
+"o. c #766C4D",
+"p. c #776C49",
+"q. c #73694A",
+"r. c #6B634D",
+"s. c #69614D",
+"t. c #6B624B",
+"u. c #6E6549",
+"v. c #716647",
+"w. c #736845",
+"x. c #716546",
+"y. c #68614A",
+"z. c #675F4B",
+"A. c #696149",
+"B. c #6E6347",
+"C. c #6F6545",
+"D. c #726843",
+"E. c #686048",
+"F. c #665F49",
+"G. c #6A6247",
+"H. c #796F3F",
+"I. c #85793A",
+"J. c #82793B",
+"K. c #615F4B",
+"L. c #363D60",
+"M. c #1E286A",
+"N. c #1D2769",
+"O. c #6A687F",
+"P. c #9F8E4A",
+"Q. c #CBAB28",
+"R. c #D6B21D",
+"S. c #DCB719",
+"T. c #D6B31C",
+"U. c #CDAC22",
+"V. c #C8A821",
+"W. c #CCAB1E",
+"X. c #CFAD1B",
+"Y. c #CEAC1A",
+"Z. c #D5B115",
+"`. c #D1AE17",
+" + c #C8A71C",
+".+ c #C4A41D",
+"++ c #CAA91B",
+"@+ c #CDAA18",
+"#+ c #CDAB19",
+"$+ c #D0AD17",
+"%+ c #D1AE18",
+"&+ c #C4A51B",
+"*+ c #D8B513",
+"=+ c #E3BF0E",
+"-+ c #E5C10C",
+";+ c #BBA423",
+">+ c #636151",
+",+ c #2F3568",
+"'+ c #222E74",
+")+ c #1E2868",
+"!+ c #1A235E",
+"~+ c #5A62AC",
+"{+ c #565A91",
+"]+ c #847B69",
+"^+ c #D1B023",
+"/+ c #F7CA05",
+"(+ c #FBCD02",
+"_+ c #F6C905",
+":+ c #EFC409",
+"<+ c #F1C607",
+"[+ c #F5C905",
+"}+ c #F1C507",
+"|+ c #FACC02",
+"1+ c #F4C805",
+"2+ c #EEC308",
+"3+ c #F5C804",
+"4+ c #F2C606",
+"5+ c #F8CB03",
+"6+ c #FACD02",
+"7+ c #FECF00",
+"8+ c #E5C10D",
+"9+ c #7F753F",
+"0+ c #353D69",
+"a+ c #222A6F",
+"b+ c #212B6C",
+"c+ c #20296C",
+"d+ c #4E569C",
+"e+ c #525987",
+"f+ c #8A7F5B",
+"g+ c #DFB918",
+"h+ c #FACC03",
+"i+ c #F8CB04",
+"j+ c #E2BB11",
+"k+ c #DDB713",
+"l+ c #E0B910",
+"m+ c #E3BB0E",
+"n+ c #E1B910",
+"o+ c #E9C00B",
+"p+ c #E5BD0D",
+"q+ c #DCB612",
+"r+ c #DBB612",
+"s+ c #E0B90F",
+"t+ c #E9C00A",
+"u+ c #E5BC0D",
+"v+ c #DBB512",
+"w+ c #DCB712",
+"x+ c #E2BB0E",
+"y+ c #EAC10A",
+"z+ c #F1C807",
+"A+ c #F5CC04",
+"B+ c #D1B517",
+"C+ c #716C45",
+"D+ c #34405B",
+"E+ c #262E73",
+"F+ c #222E6C",
+"G+ c #222D6E",
+"H+ c #4E5AA4",
+"I+ c #4A5A9C",
+"J+ c #4A529C",
+"K+ c #4D558B",
+"L+ c #7E7564",
+"M+ c #CEAE22",
+"N+ c #F6CA06",
+"O+ c #F2C608",
+"P+ c #DCB714",
+"Q+ c #B99C29",
+"R+ c #9D8936",
+"S+ c #92813A",
+"T+ c #958238",
+"U+ c #968238",
+"V+ c #9C8634",
+"W+ c #988435",
+"X+ c #917F38",
+"Y+ c #8D7C3B",
+"Z+ c #917F39",
+"`+ c #958236",
+" @ c #968335",
+".@ c #9C8733",
+"+@ c #998537",
+"@@ c #928037",
+"#@ c #8E7D3B",
+"$@ c #92803B",
+"%@ c #968239",
+"&@ c #978437",
+"*@ c #9D8833",
+"=@ c #998639",
+"-@ c #927E39",
+";@ c #8F7E3B",
+">@ c #948238",
+",@ c #A4902F",
+"'@ c #AF9A29",
+")@ c #B09C29",
+"!@ c #867B40",
+"~@ c #4D5165",
+"{@ c #293071",
+"]@ c #263273",
+"^@ c #252D72",
+"/@ c #253071",
+"(@ c #242F6E",
+"_@ c #232E6E",
+":@ c #46529C",
+"<@ c #425294",
+"[@ c #424A94",
+"}@ c #424D8A",
+"|@ c #605E73",
+"1@ c #9C8945",
+"2@ c #D2B01F",
+"3@ c #EDC30B",
+"4@ c #F2C707",
+"5@ c #E4BC0F",
+"6@ c #BB9E27",
+"7@ c #857643",
+"8@ c #5F5A56",
+"9@ c #474963",
+"0@ c #484863",
+"a@ c #464860",
+"b@ c #474860",
+"c@ c #434564",
+"d@ c #42455F",
+"e@ c #454661",
+"f@ c #474761",
+"g@ c #484B5F",
+"h@ c #47495C",
+"i@ c #494A60",
+"j@ c #444767",
+"k@ c #43485D",
+"l@ c #484A61",
+"m@ c #474966",
+"n@ c #484B68",
+"o@ c #484A60",
+"p@ c #4C4E61",
+"q@ c #43476A",
+"r@ c #474D5B",
+"s@ c #4A4D67",
+"t@ c #565759",
+"u@ c #525855",
+"v@ c #464964",
+"w@ c #2B3A69",
+"x@ c #263171",
+"y@ c #232E6C",
+"z@ c #212B69",
+"A@ c #464E94",
+"B@ c #3A468C",
+"C@ c #444984",
+"D@ c #63626C",
+"E@ c #958345",
+"F@ c #CAAA20",
+"G@ c #ECC20A",
+"H@ c #F0C508",
+"I@ c #BDA022",
+"J@ c #7A6F46",
+"K@ c #494963",
+"L@ c #2C366F",
+"M@ c #28326E",
+"N@ c #2B3472",
+"O@ c #273173",
+"P@ c #283272",
+"Q@ c #2B3572",
+"R@ c #2B3471",
+"S@ c #2B3773",
+"T@ c #2C3775",
+"U@ c #2C3574",
+"V@ c #2B3673",
+"W@ c #2D3877",
+"X@ c #2B3578",
+"Y@ c #2B3672",
+"Z@ c #2E3977",
+"`@ c #2E397B",
+" # c #2C3772",
+".# c #2C377A",
+"+# c #2D347A",
+"@# c #2F3971",
+"## c #2E3579",
+"$# c #2D387A",
+"%# c #2A3274",
+"&# c #2A327C",
+"*# c #26366C",
+"=# c #293474",
+"-# c #273273",
+";# c #273271",
+"># c #3E4A94",
+",# c #3A428C",
+"'# c #3E467C",
+")# c #5A576A",
+"!# c #8E7F44",
+"~# c #C3A521",
+"{# c #E9C00C",
+"]# c #FFD000",
+"^# c #BC9F23",
+"/# c #7B7048",
+"(# c #4A4C5E",
+"_# c #303A6A",
+":# c #293475",
+"<# c #2A357A",
+"[# c #2A3675",
+"}# c #2A3674",
+"|# c #2C3878",
+"1# c #2D3978",
+"2# c #2D3777",
+"3# c #2D3976",
+"4# c #2D3775",
+"5# c #2B3873",
+"6# c #2F3B77",
+"7# c #30397E",
+"8# c #2F3775",
+"9# c #2A386E",
+"0# c #2A367C",
+"a# c #2E3A7D",
+"b# c #293579",
+"c# c #293574",
+"d# c #293575",
+"e# c #293473",
+"f# c #263170",
+"g# c #323E84",
+"h# c #3A447E",
+"i# c #4E5064",
+"j# c #807349",
+"k# c #C0A123",
+"l# c #EAC10B",
+"m# c #F6C904",
+"n# c #E5BD0E",
+"o# c #BC9E25",
+"p# c #847643",
+"q# c #515160",
+"r# c #333B72",
+"s# c #2C3779",
+"t# c #2B3679",
+"u# c #2B3877",
+"v# c #2E397C",
+"w# c #2D397C",
+"x# c #2D397A",
+"y# c #2E3B7A",
+"z# c #2E387D",
+"A# c #2F3B7D",
+"B# c #2E3C7A",
+"C# c #2F387D",
+"D# c #2F3B7F",
+"E# c #313D7F",
+"F# c #303E7E",
+"G# c #2F387C",
+"H# c #2E367C",
+"I# c #2C3877",
+"J# c #2C3777",
+"K# c #2A3676",
+"L# c #2B3571",
+"M# c #323A84",
+"N# c #2E3676",
+"O# c #484C67",
+"P# c #7A6F49",
+"Q# c #BD9F23",
+"R# c #EEC309",
+"S# c #C8A81E",
+"T# c #90803E",
+"U# c #565661",
+"V# c #323D77",
+"W# c #2E387B",
+"X# c #2E3A7C",
+"Y# c #2E3B7B",
+"Z# c #2F3C7C",
+"`# c #2F3D7C",
+" $ c #303D7D",
+".$ c #303D7E",
+"+$ c #313F7E",
+"@$ c #313E80",
+"#$ c #313D7D",
+"$$ c #313E7F",
+"%$ c #324081",
+"&$ c #323E81",
+"*$ c #2E3E7C",
+"=$ c #2D3A79",
+"-$ c #283677",
+";$ c #283378",
+">$ c #494B69",
+",$ c #796F46",
+"'$ c #B89C27",
+")$ c #E3BB0F",
+"!$ c #CBAA1D",
+"~$ c #8B7D43",
+"{$ c #525363",
+"]$ c #353F79",
+"^$ c #2F3C7D",
+"/$ c #2F3C7E",
+"($ c #303C7D",
+"_$ c #313E7E",
+":$ c #323E7E",
+"<$ c #333F7F",
+"[$ c #323F7E",
+"}$ c #333F80",
+"|$ c #344081",
+"1$ c #333F7E",
+"2$ c #33417F",
+"3$ c #354183",
+"4$ c #34407E",
+"5$ c #2F3B7E",
+"6$ c #303980",
+"7$ c #2F3C7A",
+"8$ c #2A347C",
+"9$ c #2A3279",
+"0$ c #2F3676",
+"a$ c #484B66",
+"b$ c #7B6F4B",
+"c$ c #B59A29",
+"d$ c #DFB912",
+"e$ c #EDC30A",
+"f$ c #C8A820",
+"g$ c #8C7E43",
+"h$ c #5B5C64",
+"i$ c #3B4578",
+"j$ c #313E7D",
+"k$ c #313F81",
+"l$ c #334082",
+"m$ c #334080",
+"n$ c #344183",
+"o$ c #354284",
+"p$ c #35417E",
+"q$ c #344284",
+"r$ c #364386",
+"s$ c #364385",
+"t$ c #364282",
+"u$ c #354383",
+"v$ c #344180",
+"w$ c #2B3776",
+"x$ c #2B3675",
+"y$ c #283370",
+"z$ c #29337C",
+"A$ c #273375",
+"B$ c #293477",
+"C$ c #2E3870",
+"D$ c #454969",
+"E$ c #746B51",
+"F$ c #AE962C",
+"G$ c #E2BB10",
+"H$ c #EBC20B",
+"I$ c #D1AF1B",
+"J$ c #9C8A3D",
+"K$ c #636160",
+"L$ c #414976",
+"M$ c #344181",
+"N$ c #344281",
+"O$ c #354280",
+"P$ c #344282",
+"Q$ c #34427F",
+"R$ c #374280",
+"S$ c #364482",
+"T$ c #374487",
+"U$ c #374582",
+"V$ c #374586",
+"W$ c #384485",
+"X$ c #344080",
+"Y$ c #2D3876",
+"Z$ c #26327B",
+"`$ c #273477",
+" % c #2A3475",
+".% c #2B357A",
+"+% c #2E3A72",
+"@% c #43496B",
+"#% c #706951",
+"$% c #B2992D",
+"%% c #E0B912",
+"&% c #D7B418",
+"*% c #9E8B3D",
+"=% c #636161",
+"-% c #404A77",
+";% c #354283",
+">% c #364283",
+",% c #364382",
+"'% c #374385",
+")% c #384584",
+"!% c #354482",
+"~% c #384588",
+"{% c #384587",
+"]% c #394585",
+"^% c #394587",
+"/% c #394686",
+"(% c #394687",
+"_% c #374483",
+":% c #354382",
+"<% c #354282",
+"[% c #2B3775",
+"}% c #26327C",
+"|% c #2A337C",
+"1% c #283570",
+"2% c #2B377D",
+"3% c #2D377C",
+"4% c #2B3678",
+"5% c #43496E",
+"6% c #706B57",
+"7% c #AA9333",
+"8% c #DDB814",
+"9% c #F3C807",
+"0% c #F0C509",
+"a% c #D1B01C",
+"b% c #9E8D3D",
+"c% c #666561",
+"d% c #444D7A",
+"e% c #374681",
+"f% c #374482",
+"g% c #384585",
+"h% c #374684",
+"i% c #374685",
+"j% c #374480",
+"k% c #3A4686",
+"l% c #394885",
+"m% c #3A4988",
+"n% c #3A4A8A",
+"o% c #3A4989",
+"p% c #3A4786",
+"q% c #2F3B78",
+"r% c #2B3674",
+"s% c #263176",
+"t% c #283473",
+"u% c #2C387D",
+"v% c #2A3778",
+"w% c #2E3B7C",
+"x% c #444A73",
+"y% c #6A665B",
+"z% c #AB9433",
+"A% c #DBB716",
+"B% c #EFC40A",
+"C% c #D5B21B",
+"D% c #9E8D3F",
+"E% c #616266",
+"F% c #414B7E",
+"G% c #374682",
+"H% c #3A4785",
+"I% c #394884",
+"J% c #394888",
+"K% c #3C4886",
+"L% c #3C4A8B",
+"M% c #3C4A87",
+"N% c #3A4887",
+"O% c #394685",
+"P% c #384685",
+"Q% c #384583",
+"R% c #2A337B",
+"S% c #2E3976",
+"T% c #333D79",
+"U% c #313D77",
+"V% c #333E7A",
+"W% c #484F74",
+"X% c #6E6A5B",
+"Y% c #A99337",
+"Z% c #DCB815",
+"`% c #F3C707",
+" & c #F1C608",
+".& c #CEAE20",
+"+& c #7E7754",
+"@& c #485077",
+"#& c #3B4987",
+"$& c #364582",
+"%& c #384682",
+"&& c #3C498A",
+"*& c #3C4B85",
+"=& c #3B4A87",
+"-& c #3D4C8C",
+";& c #3E4B8C",
+">& c #3D4B8C",
+",& c #3B4988",
+"'& c #394786",
+")& c #33407D",
+"!& c #263174",
+"~& c #27347B",
+"{& c #2D3A7F",
+"]& c #2F3A7B",
+"^& c #364280",
+"/& c #4B5173",
+"(& c #807752",
+"_& c #BFA428",
+":& c #E6BE10",
+"<& c #F2C708",
+"[& c #D7B41A",
+"}& c #897E51",
+"|& c #4D557A",
+"1& c #3B4983",
+"2& c #384883",
+"3& c #3D4A88",
+"4& c #3D4C8A",
+"5& c #3C4B88",
+"6& c #394986",
+"7& c #3C4A84",
+"8& c #3F4C8C",
+"9& c #3F4E8E",
+"0& c #3E4E8A",
+"a& c #3E4E8D",
+"b& c #3E4D8B",
+"c& c #3D4B8A",
+"d& c #3A4888",
+"e& c #3A4787",
+"f& c #242E73",
+"g& c #263279",
+"h& c #283276",
+"i& c #283478",
+"j& c #2A3771",
+"k& c #303C7F",
+"l& c #33407F",
+"m& c #384681",
+"n& c #54576E",
+"o& c #8B7E4A",
+"p& c #C7A925",
+"q& c #E9C00E",
+"r& c #F0C609",
+"s& c #CDAD21",
+"t& c #7E7658",
+"u& c #4B5380",
+"v& c #394783",
+"w& c #3B488A",
+"x& c #3B498A",
+"y& c #3B4B88",
+"z& c #3F4D89",
+"A& c #3E4D88",
+"B& c #3F4F89",
+"C& c #414E91",
+"D& c #404F8E",
+"E& c #404E8D",
+"F& c #404D8C",
+"G& c #3E4C8B",
+"H& c #3C4A89",
+"I& c #3A4886",
+"J& c #35427E",
+"K& c #262F73",
+"L& c #283277",
+"M& c #27327C",
+"N& c #2B3579",
+"O& c #2C387B",
+"P& c #37437F",
+"Q& c #54566E",
+"R& c #807451",
+"S& c #BCA029",
+"T& c #BBA12E",
+"U& c #6D6B64",
+"V& c #455085",
+"W& c #3B4986",
+"X& c #3E4B8A",
+"Y& c #3C4B89",
+"Z& c #3F4C88",
+"`& c #404F8D",
+" * c #41518D",
+".* c #42518C",
+"+* c #42508F",
+"@* c #41518E",
+"#* c #404F90",
+"$* c #3F4E8C",
+"%* c #323F7C",
+"&* c #243073",
+"** c #26346F",
+"=* c #2B3779",
+"-* c #2F3B79",
+";* c #354078",
+">* c #55586D",
+",* c #867A4E",
+"'* c #BFA229",
+")* c #E6BE0F",
+"!* c #EEC40B",
+"~* c #E7BF0F",
+"{* c #C9AA23",
+"]* c #8D814B",
+"^* c #575C78",
+"/* c #3F4B85",
+"(* c #3E4C88",
+"_* c #3F4E8B",
+":* c #41508E",
+"<* c #425190",
+"[* c #414E8A",
+"}* c #43538D",
+"|* c #42528E",
+"1* c #435190",
+"2* c #41508C",
+"3* c #404E8C",
+"4* c #3E4D8C",
+"5* c #232D71",
+"6* c #242E71",
+"7* c #283077",
+"8* c #28327C",
+"9* c #283477",
+"0* c #28347C",
+"a* c #2A3678",
+"b* c #3B4478",
+"c* c #5B5B65",
+"d* c #8C7D49",
+"e* c #C6A821",
+"f* c #E9C00D",
+"g* c #F5C906",
+"h* c #E5BE10",
+"i* c #BFA329",
+"j* c #8D824E",
+"k* c #5F6170",
+"l* c #444E85",
+"m* c #3F4D8E",
+"n* c #3E4C8D",
+"o* c #3E4D8A",
+"p* c #414E90",
+"q* c #425090",
+"r* c #425290",
+"s* c #44548E",
+"t* c #44538E",
+"u* c #455294",
+"v* c #43528E",
+"w* c #445295",
+"x* c #435391",
+"y* c #42518F",
+"z* c #3F4D8A",
+"A* c #3E4B89",
+"B* c #3B4885",
+"C* c #212A6F",
+"D* c #212D72",
+"E* c #2A3477",
+"F* c #364068",
+"G* c #5F5F62",
+"H* c #958441",
+"I* c #CBAA20",
+"J* c #BDA22A",
+"K* c #837851",
+"L* c #595D72",
+"M* c #424F83",
+"N* c #3C4A88",
+"O* c #3F4D8C",
+"P* c #42508D",
+"Q* c #41508D",
+"R* c #43528C",
+"S* c #43548F",
+"T* c #445494",
+"U* c #44548C",
+"V* c #445293",
+"W* c #445391",
+"X* c #435090",
+"Y* c #42518E",
+"Z* c #3A4885",
+"`* c #374581",
+" = c #232C71",
+".= c #242F76",
+"+= c #252F73",
+"@= c #253176",
+"#= c #2D3970",
+"$= c #3E456C",
+"%= c #65635A",
+"&= c #9B893D",
+"*= c #CEAD1C",
+"== c #EEC30A",
+"-= c #E3BC11",
+";= c #B89E2D",
+">= c #7E7756",
+",= c #535972",
+"'= c #3F4A88",
+")= c #3C4B8C",
+"!= c #3E4E8E",
+"~= c #414F8C",
+"{= c #455391",
+"]= c #475694",
+"^= c #445493",
+"/= c #455394",
+"(= c #424F8C",
+"_= c #465494",
+":= c #46588F",
+"<= c #45568F",
+"[= c #455491",
+"}= c #445390",
+"|= c #35417C",
+"1= c #212D71",
+"2= c #222D71",
+"3= c #253173",
+"4= c #2E3575",
+"5= c #424866",
+"6= c #71694F",
+"7= c #A79133",
+"8= c #D6B218",
+"9= c #DBB616",
+"0= c #AD9634",
+"a= c #756F5A",
+"b= c #4E5379",
+"c= c #3B4889",
+"d= c #3A4684",
+"e= c #3B4B8C",
+"f= c #404E90",
+"g= c #435290",
+"h= c #42538D",
+"i= c #455494",
+"j= c #455593",
+"k= c #44558D",
+"l= c #465690",
+"m= c #475793",
+"n= c #485495",
+"o= c #475594",
+"p= c #465592",
+"q= c #202C71",
+"r= c #222B70",
+"s= c #222E72",
+"t= c #2B3570",
+"u= c #474961",
+"v= c #786E4B",
+"w= c #B59A28",
+"x= c #D9B517",
+"y= c #A79138",
+"z= c #6E6C5D",
+"A= c #495176",
+"B= c #384687",
+"C= c #3B478C",
+"D= c #3A4A8C",
+"E= c #3D498D",
+"F= c #3E4E8C",
+"G= c #435191",
+"H= c #465491",
+"I= c #485795",
+"J= c #475496",
+"K= c #485894",
+"L= c #46568B",
+"M= c #495A92",
+"N= c #495996",
+"O= c #495992",
+"P= c #414F8A",
+"Q= c #1F2B6E",
+"R= c #222C70",
+"S= c #232E72",
+"T= c #262F72",
+"U= c #464966",
+"V= c #7A6E46",
+"W= c #B99D26",
+"X= c #E3BC0F",
+"Y= c #F0C507",
+"Z= c #D5B218",
+"`= c #A49038",
+" - c #6C6A5F",
+".- c #474E7B",
+"+- c #384481",
+"@- c #3E4B8D",
+"#- c #424F8F",
+"$- c #425292",
+"%- c #435291",
+"&- c #45528D",
+"*- c #465594",
+"=- c #43548C",
+"-- c #475991",
+";- c #4A5A99",
+">- c #485791",
+",- c #485593",
+"'- c #4B5B9A",
+")- c #4C5B98",
+"!- c #4A5A95",
+"~- c #495894",
+"{- c #404E8A",
+"]- c #3E4D89",
+"^- c #394680",
+"/- c #1F2A6D",
+"(- c #1F286D",
+"_- c #26306E",
+":- c #424460",
+"<- c #786C45",
+"[- c #BC9F22",
+"}- c #E7BF0C",
+"|- c #C9A91E",
+"1- c #9A873B",
+"2- c #646362",
+"3- c #454B77",
+"4- c #384581",
+"5- c #384688",
+"6- c #384888",
+"7- c #3B498C",
+"8- c #42508E",
+"9- c #435292",
+"0- c #455492",
+"a- c #465695",
+"b- c #475691",
+"c- c #48568F",
+"d- c #47578F",
+"e- c #495993",
+"f- c #48568C",
+"g- c #4C5C9C",
+"h- c #4C5D9C",
+"i- c #4D5E98",
+"j- c #333E78",
+"k- c #1E296E",
+"l- c #1E2A6D",
+"m- c #2A3168",
+"n- c #494A5C",
+"o- c #7C6F42",
+"p- c #BEA021",
+"q- c #EBC10A",
+"r- c #F5C904",
+"s- c #BB9F25",
+"t- c #867940",
+"u- c #585862",
+"v- c #3E4877",
+"w- c #344483",
+"x- c #384285",
+"y- c #384684",
+"z- c #3A4687",
+"A- c #3E4A84",
+"B- c #414E92",
+"C- c #405090",
+"D- c #44528F",
+"E- c #45558F",
+"F- c #4A5898",
+"G- c #485A92",
+"H- c #4A599C",
+"I- c #4A5891",
+"J- c #4B5A95",
+"K- c #4B5B96",
+"L- c #4D5D97",
+"M- c #4E5F98",
+"N- c #505E99",
+"O- c #4F5E9D",
+"P- c #4C5D98",
+"Q- c #485893",
+"R- c #455390",
+"S- c #2E3566",
+"T- c #535054",
+"U- c #8E7C38",
+"V- c #C5A51D",
+"W- c #BDA023",
+"X- c #7C7146",
+"Y- c #4D4F63",
+"Z- c #363E74",
+"`- c #2F3B7C",
+" ; c #344280",
+".; c #364483",
+"+; c #3A478C",
+"@; c #3B4A8D",
+"#; c #414E8C",
+"$; c #445291",
+"%; c #465593",
+"&; c #46568E",
+"*; c #485A91",
+"=; c #485897",
+"-; c #4A578D",
+";; c #4B5C95",
+">; c #4B5A93",
+",; c #4E5E99",
+"'; c #4E5E97",
+"); c #4D5D98",
+"!; c #4F5F9C",
+"~; c #4C5C97",
+"{; c #475692",
+"]; c #3A4883",
+"^; c #1E276C",
+"/; c #2A3266",
+"(; c #545052",
+"_; c #968333",
+":; c #D5B114",
+"<; c #F3C705",
+"[; c #C5A51E",
+"}; c #87793D",
+"|; c #585759",
+"1; c #40466F",
+"2; c #3E466F",
+"3; c #434B72",
+"4; c #474E7A",
+"5; c #495077",
+"6; c #4A527B",
+"7; c #4A537F",
+"8; c #4D567E",
+"9; c #4D567F",
+"0; c #4D577F",
+"a; c #4C5782",
+"b; c #4D5785",
+"c; c #4D5788",
+"d; c #4E5889",
+"e; c #525E8A",
+"f; c #525E87",
+"g; c #535E8C",
+"h; c #56608B",
+"i; c #53608C",
+"j; c #536090",
+"k; c #535F8B",
+"l; c #556396",
+"m; c #526296",
+"n; c #52639D",
+"o; c #4D5E9C",
+"p; c #4A5991",
+"q; c #4F5A94",
+"r; c #1D286C",
+"s; c #1D276C",
+"t; c #232A69",
+"u; c #434459",
+"v; c #8E7D36",
+"w; c #D3B015",
+"x; c #D8B314",
+"y; c #BA9D24",
+"z; c #988535",
+"A; c #807445",
+"B; c #7E734A",
+"C; c #817545",
+"D; c #887B43",
+"E; c #8A7C48",
+"F; c #8B7F4D",
+"G; c #897E4F",
+"H; c #867C53",
+"I; c #867D54",
+"J; c #8C8151",
+"K; c #8E8351",
+"L; c #8D8152",
+"M; c #867E57",
+"N; c #817B5C",
+"O; c #7E7960",
+"P; c #807A62",
+"Q; c #837E60",
+"R; c #89835C",
+"S; c #8C8460",
+"T; c #8A835A",
+"U; c #878063",
+"V; c #817B64",
+"W; c #7B796A",
+"X; c #7B7B70",
+"Y; c #777771",
+"Z; c #687086",
+"`; c #5A6894",
+" > c #52629B",
+".> c #4B5A8D",
+"+> c #4A5A9A",
+"@> c #4E5E94",
+"#> c #4A5994",
+"$> c #495693",
+"%> c #455490",
+"&> c #1C276A",
+"*> c #282F66",
+"=> c #5A554E",
+"-> c #BE9F1F",
+";> c #F8CB02",
+">> c #EDC209",
+",> c #E7BF0B",
+"'> c #DEB812",
+")> c #D6B316",
+"!> c #D6B217",
+"~> c #DCB713",
+"{> c #E0BA11",
+"]> c #E0BA12",
+"^> c #DEB915",
+"/> c #D9B619",
+"(> c #D9B61A",
+"_> c #DFBA16",
+":> c #E2BC14",
+"<> c #DCB818",
+"[> c #D9B61B",
+"}> c #D3B220",
+"|> c #CEAF24",
+"1> c #CEAF25",
+"2> c #D3B321",
+"3> c #D8B61E",
+"4> c #D9B71E",
+"5> c #D3B323",
+"6> c #CBAD2A",
+"7> c #C2A834",
+"8> c #C1A834",
+"9> c #B9A33C",
+"0> c #9B9055",
+"a> c #70757C",
+"b> c #535E87",
+"c> c #4F6097",
+"d> c #4B5C97",
+"e> c #4A5A9B",
+"f> c #4A5A94",
+"g> c #3E4B85",
+"h> c #282F65",
+"i> c #59544F",
+"j> c #BA9C1F",
+"k> c #F2C605",
+"l> c #F7CA03",
+"m> c #F4C806",
+"n> c #F6CA04",
+"o> c #F7CA04",
+"p> c #F4C906",
+"q> c #F6CA05",
+"r> c #F3C808",
+"s> c #F0C60A",
+"t> c #F0C60B",
+"u> c #F4C808",
+"v> c #F7CB05",
+"w> c #F5C907",
+"x> c #F4C908",
+"y> c #F0C60C",
+"z> c #ECC30F",
+"A> c #ECC40F",
+"B> c #ECC410",
+"C> c #CCAF2C",
+"D> c #88815F",
+"E> c #5D678B",
+"F> c #50609A",
+"G> c #4B5A92",
+"H> c #212A66",
+"I> c #40415B",
+"J> c #7F703D",
+"K> c #B89A22",
+"L> c #CDAB18",
+"M> c #D2AF15",
+"N> c #D3B016",
+"O> c #D5B116",
+"P> c #D7B316",
+"Q> c #D6B216",
+"R> c #DDB815",
+"S> c #E3BD13",
+"T> c #E4BD12",
+"U> c #E8C010",
+"V> c #E4BE12",
+"W> c #E0BB16",
+"X> c #E0BA17",
+"Y> c #E5BE13",
+"Z> c #E9C110",
+"`> c #E8C012",
+" , c #E9C111",
+"., c #E6BF13",
+"+, c #E4BE15",
+"@, c #E8C111",
+"#, c #CCAE28",
+"$, c #888367",
+"%, c #5C6789",
+"&, c #4A5888",
+"*, c #4A5A8D",
+"=, c #465694",
+"-, c #46528C",
+";, c #1D266A",
+">, c #232C62",
+",, c #3E4058",
+"', c #59544E",
+"), c #6B6348",
+"!, c #746944",
+"~, c #776C48",
+"{, c #7A6F47",
+"], c #796F47",
+"^, c #7A7046",
+"/, c #7D724A",
+"(, c #83774A",
+"_, c #887B45",
+":, c #897C47",
+"<, c #8A7D48",
+"[, c #867B4C",
+"}, c #857B51",
+"|, c #8D8250",
+"1, c #95884B",
+"2, c #9A8B46",
+"3, c #A18F42",
+"4, c #9F8F45",
+"5, c #9C8D48",
+"6, c #9D8E49",
+"7, c #A29246",
+"8, c #A79543",
+"9, c #A89744",
+"0, c #A99744",
+"a, c #A69645",
+"b, c #AA9846",
+"c, c #B49F3E",
+"d, c #BDA536",
+"e, c #B8A13A",
+"f, c #9A8F56",
+"g, c #6E727C",
+"h, c #515B84",
+"i, c #4C5C94",
+"j, c #45548F",
+"k, c #222B68",
+"l, c #273068",
+"m, c #2C3361",
+"n, c #2E3565",
+"o, c #343B69",
+"p, c #383E68",
+"q, c #373F66",
+"r, c #38406D",
+"s, c #3B426F",
+"t, c #3F4670",
+"u, c #404870",
+"v, c #444B71",
+"w, c #434B76",
+"x, c #454D75",
+"y, c #46507B",
+"z, c #48527D",
+"A, c #4C557D",
+"B, c #50587D",
+"C, c #555B74",
+"D, c #575C75",
+"E, c #5C6074",
+"F, c #5A5E78",
+"G, c #5A607A",
+"H, c #5E637B",
+"I, c #606577",
+"J, c #636875",
+"K, c #616677",
+"L, c #646978",
+"M, c #676B76",
+"N, c #6F6F6B",
+"O, c #76756F",
+"P, c #747475",
+"Q, c #656C81",
+"R, c #535C83",
+"S, c #4D5A94",
+"T, c #1B2667",
+"U, c #1C2768",
+"V, c #1B2767",
+"W, c #1B2665",
+"X, c #1F2B6D",
+"Y, c #222D6F",
+"Z, c #232F6E",
+"`, c #253072",
+" ' c #273373",
+".' c #2D3979",
+"+' c #2D3977",
+"@' c #2E3A7B",
+"#' c #323F7F",
+"$' c #354281",
+"%' c #3C4C85",
+"&' c #3D4B88",
+"*' c #404D85",
+"=' c #414F89",
+"-' c #424F89",
+";' c #445289",
+">' c #45528F",
+",' c #46538E",
+"'' c #475591",
+")' c #495592",
+"!' c #4A588E",
+"~' c #4C5A90",
+"{' c #4F5C8C",
+"]' c #4F5B8B",
+"^' c #4B5788",
+"/' c #4A5894",
+"(' c #4A5992",
+"_' c #485794",
+":' c #4A5694",
+"<' c #1E266B",
+"[' c #1D2668",
+"}' c #1E2869",
+"|' c #1C2867",
+"1' c #1E2A69",
+"2' c #222E6D",
+"3' c #263173",
+"4' c #2D3A77",
+"5' c #2E3A7A",
+"6' c #323E7F",
+"7' c #394684",
+"8' c #3A4A84",
+"9' c #3E4A8C",
+"0' c #3D4987",
+"a' c #3D4C8B",
+"b' c #40508C",
+"c' c #44538F",
+"d' c #485592",
+"e' c #485792",
+"f' c #485895",
+"g' c #46568C",
+"h' c #42528C",
+"i' c #1A2665",
+"j' c #1E276B",
+"k' c #1F296C",
+"l' c #222C6F",
+"m' c #263271",
+"n' c #2A3574",
+"o' c #2A3576",
+"p' c #364684",
+"q' c #3C4884",
+"r' c #3D4A87",
+"s' c #3D4A89",
+"t' c #42508C",
+"u' c #44528B",
+"v' c #46548E",
+"w' c #465493",
+"x' c #475593",
+"y' c #475490",
+"z' c #4A5794",
+"A' c #424E8C",
+"B' c #3C477A",
+"C' c #1A2467",
+"D' c #1C2769",
+"E' c #1D2969",
+"F' c #1C2868",
+"G' c #202C6C",
+"H' c #232F6F",
+"I' c #243074",
+"J' c #243075",
+"K' c #2A3677",
+"L' c #2F3A7E",
+"M' c #333F7C",
+"N' c #354182",
+"O' c #3B4A88",
+"P' c #3F4D88",
+"Q' c #404F8C",
+"R' c #44518C",
+"S' c #445490",
+"T' c #445492",
+"U' c #43538F",
+"V' c #445491",
+"W' c #485693",
+"X' c #465284",
+"Y' c #1B2464",
+"Z' c #1C266A",
+"`' c #1E276D",
+" ) c #202A6C",
+".) c #1F2B6A",
+"+) c #25316F",
+"@) c #2C367D",
+"#) c #2E3A74",
+"$) c #2F3B7A",
+"%) c #303F7C",
+"&) c #324283",
+"*) c #404E8B",
+"=) c #404F8A",
+"-) c #43528F",
+";) c #44528E",
+">) c #475794",
+",) c #465294",
+"') c #3A4475",
+")) c #182059",
+"!) c #1A2462",
+"~) c #1D2869",
+"{) c #1F296A",
+"]) c #212A6D",
+"^) c #252F6F",
+"/) c #263272",
+"() c #283275",
+"_) c #2E3A77",
+":) c #3B4887",
+"<) c #3E4C89",
+"[) c #40508D",
+"}) c #42528F",
+"|) c #43538C",
+"1) c #45558C",
+"2) c #1B2565",
+"3) c #202B6B",
+"4) c #25306F",
+"5) c #263172",
+"6) c #283373",
+"7) c #2B3777",
+"8) c #2D3A78",
+"9) c #303C7B",
+"0) c #3C4987",
+"a) c #3E4B88",
+"b) c #3E4C8A",
+"c) c #3E4E89",
+"d) c #3F4E8A",
+"e) c #414E89",
+"f) c #42518D",
+"g) c #414F8B",
+"h) c #445190",
+"i) c #42528D",
+"j) c #42518B",
+"k) c #3C467F",
+"l) c #1C2664",
+"m) c #1F2A6B",
+"n) c #242F6F",
+"o) c #3F4F8A",
+"p) c #40508B",
+"q) c #1D2765",
+"r) c #2A3575",
+"s) c #3C4A86",
+"t) c #3C4B87",
+"u) c #404F8B",
+"v) c #3D4B87",
+"w) c #3D4C88",
+"x) c #3F4C87",
+" . + @ @ # $ % & * = - ; ",
+" > , ' ) ! ~ { ] ^ / ( % _ : < [ } | 1 1 ",
+" 2 3 4 5 ' ) ! ~ { ] ^ / ( % _ : < [ 6 7 | | 8 1 ",
+" 9 0 0 a 3 4 b ' ) ! ~ { ] c / ( d _ : < [ 6 7 | e f f 8 g 1 ",
+" h h i 0 a 3 j b ' ) ! ~ { ] c / ( d _ : * [ 6 k | e f 8 8 8 l m ",
+" n o p i 0 q a 3 j b ' ) ! r { ] c s t d _ : * [ 6 | | e f 8 8 l l l g 1 ",
+" u o p h i 0 q a 4 5 b ' v ! r { ] # s % d _ w x } 6 | | e f 8 8 l l y z m m ",
+" A u u o p h i 0 a B 4 5 b ' ! ~ r C ^ # D % E _ < x 6 7 | | f 8 8 8 l F G G H I m J ",
+" K u u o o p i 0 q a 3 j b ' ) ! ~ { ] ^ L D M E _ < x 6 k | e f 8 8 l y G G N G H g m J ",
+" n u u u o p h i 0 q a 3 j b ' v ! ~ { ] c / ( d _ : * [ 6 k | e f 8 O P Q G Q Q G Q H g m J ",
+" K u u u o p h i 0 0 a 3 4 5 b R v ~ r S ^ # s % d _ w x T 6 | | e 8 8 G G Q N G Q N G N H m ; J ",
+" A u u U V W X Y Z ` ...+.@.#.$.%.! ~ { C ^ L D % E _ < x 6 k | | f G O Q G G G N G G Q G G H m ; J ",
+" u u u &.*.X =.Z -.;.>.,.'.).!.~.{.].^./.(._.:.<.[.}.|.1.2.3.4.5.6.7.8.9.0.a.a.b.c.d.G N G N N m m ; ",
+" u u o &.W X =.Y ` e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.A.B.C.D.C.E.F.G.H.I.J.K.L.Q Q G G M.N.N.1 ",
+" n o o o *.X Y Z -.;.O.P.Q.R.S.T.U.V.W.X.Y.Z.`. +.+++@+#+Z.$+ +.+++@+#+Z.%+ +&+@+*+=+-+;+>+,+'+Q Q - - - )+!+ ",
+" o p p p ~+Y Z ` .{+]+^+/+/+(+_+:+:+<+[+}+|+1+2+2+}+3+}+|+1+2+2+}+[+}+|+1+2+2+4+5+6+7+8+9+0+a+'+a+b+b+c+- )+ ",
+" h p h h i Z -.` d+ .e+f+g+h+i+/+:+j+k+l+m+n+o+p+q+r+s+m+s+t+u+q+v+l+m+s+o+u+w+q+x+y+z+A+B+C+D+E+'+F+G+G+b+b+b+)+ ",
+" 9 h i i i 0 H+I+ .J++.K+L+M+N+/+O+P+Q+R+S+T+U+V+W+X+Y+Z+`+ @.@+@@@#@$@%@&@*@=@-@;@>@,@'@)@!@~@{@]@]@^@/@(@_@G+G+b+1 ",
+" 0 i 0 0 0 q J+:@<@+.[@}@|@1@2@3@4@5@6@7@8@9@0@a@b@c@d@e@f@g@h@i@j@k@l@m@n@o@p@q@q@r@s@t@u@v@w@]@]@]@/@x@/@/@(@_@y@z@ ",
+" 0 0 q q a a :@A@+.[@@.B@C@D@E@F@G@H@p+I@J@K@L@M@N@O@P@Q@R@S@T@U@V@W@X@Y@Z@`@^ #.#+# #@###$#%#&#%#*#=#-#;#x@/@/@(@_@ ",
+" 2 a a a a B 3 +.>#[@B@,#B@$.'#)#!#~#{#]#y+^#/#(#_#:#<#[#}#|#S |#1#2#3#`@4#5#6#7#8#9#0#0#9#0#a#}#}#b#c#d#d#=#e#;#;#f#(@)+ ",
+" 3 3 3 3 4 4 j j 5 b ' ,#%.%.g#h#i#j#k#l#m#n#o#p#q#r#s#t#t#u#v#w#x#y#z#A#B#C#D#E#F#G#a#a#H#a#}#I#|#J#J#J#J#K#d#=#e#;#x@(@ ",
+" L#4 4 j j 5 5 b b b ' ' ) g#g#M#H#N#O#P#Q#n#R#p+S#T#U#V#W#X#Y#A#Z#`# $.$F#+$@$#$$$%$&$A#*$a#*$a#=$1#=$1#1#1#J#K#K#d#e#;#;#z@ ",
+" , 5 b b b b b ' ' R ) v ! g#M#H#a#-$;$>$,$'$)$H@R#!$~${$]$^$/$($_$_$:$<$[$}$|$[$1$2$3$4$5$*$6$B#7$7$5$7$y#=$1#1#J#K#d#=#e#z@ ",
+" ' ' ' ' ' ' ' ) v v ! ! ~ H#a#H#0#8$9$0$a$b$c$d$4@e$f$g$h$i$E#j$k$l$m$m$n$o$p$q$r$s$t$u$v$, j$j$j$j$j$j$, 7$7$=$1#J#w$x$d#y$ ",
+" ) ) ) ) ) v ! ! ! ~ ~ r { a#0#&#0#z$A$B$C$D$E$F$G$|+H$I$J$K$L$2$M$M$N$O$P$o$Q$R$S$T$U$V$W$X$[$v$v$[$[$[$j$, , 7$y#=$1#Y$x$d# ",
+". ! ! ! ! ! ! ~ ~ ~ r { { C ] 0#%#]@Z$`$ %.%+%@%#%$%%%[+[+&%*%=%-%;%>%,%s$'%)%!%~%{%]%^%/%(%_%:%<%O$v$v$v$[$[$j$, 7$7$=$1#Y$[%y$",
+"+ ~ ~ ~ ~ r r r { { S C ] ^ @ }%}%}%%#|%1%2%3%4%5%6%7%8%9%0%a%b%c%d%e%f%g%h%i%j%k%l%m%n%o%p%(%)%_%_%:%<%O$v$v$[$[$j$, 7$q%1#Y$r%",
+"@ { { { { { { C ] ] ^ ^ c # L s%D D s%}%t%2%u%v%w%x%y%z%A%3@B%C%D%E%F%i%G%H%I%J%l%p%K%2 L%M%m%N%O%P%Q%_%_%:%O$v$v$[$j$, 7$q%1#[%",
+"@ ] ] ] ] ] ] ^ ^ c # L / s%D % % t s%]@R%r%S%T%U%V%W%X%Y%Z%`% &.&+&@&#&$&(%%&&&*&=&-&-&;&>&&&,&N%'&'&P%Q%Q%_%:%O$v$)&[$, 7$q%Y$",
+"# ^ ^ c c c # # L / s D t % M d d % !&A$|%~&1#{&]&[$^&/&(&_&:&<&[&}&|&1&2&3&4&5&6&7&8&9&0&a&b&c&2 d&d&e&'&P%Q%Q%:%O$v$)&[$j$7$1#",
+"$ / / / / s s D D ( % % M f&E '+E+g&h&i&j&S%=$q%k&l&m&n&o&p&q&r&s&t&u&v&w&x&y&z&A&9&B&C&D&E&F&G&c&H&H&2 2 I&'&P%Q%_%O$J&)&[$, q%",
+"% ( ( ( ( t % % M d d E _ _ _ '+K&]@L&M&N&2%O&a#D#P&Q&R&S&:& &3@T&U&V&W&X&Y&Y&-&Z&`& *.*+*@*#*$*b&G&G&c&H&2 d&I&P%Q%_%O$J&)&%*7$",
+"& % % d d d d E E _ _ _ : w _ d &*$ **t%0#[%=*-*;*>*,*'*)*!*~*{*]*^*/*>&>&(*_*:*<*[*D&}*|*1*2*:*`&3*4*G&G&c&H&2 I&'&Q%_%O$J&)&, ",
+"* _ _ _ _ _ _ _ _ : w < < * 5*6*&*7*8*9*0*a*a*b*c*d*e*f*g*h*i*j*k*l*(*m*n*o*p*q*r*s*t*u*v*w*x*y*:*:*:*3*4*z*G&A*2 B*P%Q%_%O$J&%*",
+"= : : : : : w < < * x x [ C*D*f&]@]@9*E*}#K#F*G*H*I*e$h+:&J*K*L*M*N*-&O*D&D&P*Q*1*r*R*S*T*U*V*W*X*X*Y*:*:*3*z*A*A*2 Z*P%Q%`*J&%*",
+"- < < < * * x x x [ T 6 6 C*D* =.=+=@=c##=$=%=&=*===O+-=;=>=,='=)=8&!=#*~=q*Q*{=]=^=/=(=_=:=<=[=}=}=}=v*Y*:*3*z*A*A*2 Z*Q%Q%O$|=",
+"; [ [ [ [ [ } 6 6 6 6 k T < 1=2=6*+=3=4=5=6=7=8=e$G@9=0=a=b=c=h%d=e=O*f=1*g=1*h=i=j=k=l=m=n=o=p=p=p=[=}=g=X*P*2*z*A*H&B*Z*Q%`*|=",
+" } 6 6 6 6 6 7 k k | a+T = q=r=s=. t=u=v=w=d$<+ &x=y=z=A=B=C=D=E=N*F=F=3*P*G=x*H=I=J=K=L=M=N=O=]=]=]=p=[=}=v*P*P=z*(*2 B*Q%`* ",
+" | 7 7 k | | | Q G C*k Q=6 6 R=S=T=U=V=W=X=Y=:+Z=`= -.-p%+-d=d=D=4*@-#-$-%-&-*-=---;->-K=,-'-)-!-~-~-]=p=[=}=v*2*{-]-(*B*Z*^- ",
+" 1 | | | | | | e e /-(-6 6 T 2=_-:-<-[-p+R#}-|-1-2-3-4-5-6-7-B@@.F=F=E&8-9-i=0-a-b-c-d-e-f-g-h-i-!-K ~-]=p=[=}=Y*2*z*(*2 B*j- ",
+" 1 | e e e e f f k-f l-e C*T m-n-o-p-q-r-p+s-t-u-v-w-x-y-z-e&A-@.F=B-C-q*D-*-E-F-G-H-I-J-K-L-M-N-O-P-K Q-]=p=R-v*2*{-A*2 B*j- ",
+" 8 f f f f 8 8 f Q G = e S-T-U-V-t+]#q-W-X-Y-Z-`- ;.;d&+;@;D=@.F=4*#;r*$;}=%;&;*;=;-;;;>;,;M-';);!;~;K Q-{;[=}=Y*P=]-(*]; ",
+" 1 f 8 8 8 8 f f ^;O 8 /;(;_;:;4+<;o+[;};|;1;2;2;3;4;5;6;7;8;9;0;a;b;c;d;e;f;g;h;h;i;j;k;l;m;n;i-o;p;q;~-Q-p=}=v*P=]-(*|= ",
+" 8 8 8 8 8 r;s;8 (-t;u;v;w;1+5+Y=x;y;z;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;W;X;Y;Z;`; >.>+>@>#>$>{;%>v*2*{-(* ",
+" g 8 l l l s;l 8 &>*>=>->1+;>5+1+>>,>'>)>!>~>{>]>j+^>/>(>_>:><>[>}>|>1>2>3>3>4>5>6>7>8>9>0>a>b>c>d>e>f>f>{;%>v*2*{-g> ",
+" 1 l l l l l l ^;s;h>i>j>k>l>l>5+1+m#[+m>1+n>(+o>(+/+p>g*/+(+q>/+r>s>t>u>v>w>v>x>y>z>A>B>C>D>E>F>G>e>f>f>{;%>v*2*{-^- ",
+" m l l I l l m g H>I>J>K>L>M>w;N>O>P>Q>Q>P+{>]>j+R>/>(>_>S>T>U>V>W>X>Y>Z>`> ,.,+,@,z>z>#,$,%,&,!-*,=,-,{;%>v*2*g> ",
+" g I I I I ;,F l >,,,',),C.!,~,{,],^,/,(,_,:,<,[,},H;|,1,2,3,4,5,6,7,8,9,0,a,b,c,d,e,f,g,h,i,f>e>f>f>{;j,v*{- ",
+" 1 I I I I I m I l k,l,m,n,1.o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,n #>f>f>=,p=R-v*^- ",
+" T,I N.N.I ;,U,V,W,8 X,Y,2=Z,`, 'B$.'+'@'($#'$'P%d&H&G&%'&'*'='-';'>',''')'!'~'{']'^'/'('_'!-f>:'=,%>v*P= ",
+" N N <'m [';,I ;,}'|'1'2'(@3'h&d#a*4'5'6':$P$7'd=8'9'y&0'Y&a'$*b'2*s*c's*p=d'e'c-f'{;K=K=#>f>f>g'g'h' ",
+" J H G W,i'j'T,N.^;k'6 l'Y,/@m'n'o'x#5'`-#'|$p'd=8'D=q'#&r's'b&O*~=8-t'W*u't*v'w'b-x'y'$>z'=,=,<@A'B' ",
+" J ; W,C'F m D'E'F'G'Y,H'I'J'=#K#K'w%L'M'N'd=B@8'A-a'O'N*&'&'P'F&Q'2*+*R'S'T'U'V'V'W']=O=g'g'X'B' ",
+" J ; Y'T,; Z'l `' ).)l'+)+ c#K'@)#)$)%)&)p',#d=d=A-c=O'Y&3&&'(**)=)b'2*t'.*-)}=;)>)T':',)h'') ",
+" )); !); U,~){) )])Y,^)/)()[#s#_)Z#[$v$:%Q%P%I&2 3&:)M%<)<)<)b&Z&*)*)[)Q*})D-}=|)1)g'h'B' ",
+" J 2)1 U,N.M.3)G+_@4)5)6)[#7)8)9)[$v$O$:%Q%P%I&2 0)I&M%a)b)z*c)d)e)f)f)g)-)f)h)i)j)k) ",
+" 1 l)U,M.m)b+G+n)5) 'n'7)y#7$j$[$v$O$_%Q%'&B*2 0)#&M%r'<)z&d)d)o)o)g)p)f)v*P= ",
+" !+q){)3)3)G+n);# 'r)J#=$7$, j$)&v$O$_%Q%P%Z*2 ,&s)t)M%r'a)a)z&d){-u){-^- ",
+" )+b+y@(@f#;#e#d#w$1#=$7$, [$)&J&O$_%Q%P%Z*B*2 v)M%3&w)w)(*(*x)g> ",
+" 1 z@_@(@x@;#=#x$Y$1#q%7$, [$)&J&O$_%Q%Q%Z*B*B*2 2 (*(*(*g>^- ",
+" )+(@;#e#d#x$Y$1#q%7$j$[$)&J&O$`*Q%Q%Q%Z*B*B*];|= ",
+" z@z@y$d#[%Y$1#q%7$, %*)&J&J&O$`*`*^-j-j- ",
+" y$r%[%Y$1#q%7$, %*%*|=|= "};
diff --git a/arts/builder/pics/Synth_PLAY.xpm b/arts/builder/pics/Synth_PLAY.xpm
new file mode 100644
index 00000000..3ad46f6e
--- /dev/null
+++ b/arts/builder/pics/Synth_PLAY.xpm
@@ -0,0 +1,316 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"64 64 245 2",
+/* colors */
+" c #B1B1B1",
+" . c #BFBFC2",
+" X c #ADADAD",
+" o c #A9AAB3",
+" O c #ABABAB",
+" + c #323E7D",
+" @ c #5F6792",
+" # c #71789D",
+" $ c #2F3A7A",
+" % c #3E4C8C",
+" & c #A5A5A5",
+" * c #3A4488",
+" = c #273272",
+" - c #8D93B5",
+" ; c #78787C",
+" : c #253070",
+" > c #616B97",
+" , c #344082",
+" < c #232E6E",
+" 1 c #313C7F",
+" 2 c #B0B5CA",
+" 3 c #1F286A",
+" 4 c #959595",
+" 5 c #1C2667",
+" 6 c #5F6BAC",
+" 7 c #696A6D",
+" 8 c #374288",
+" 9 c #8D8D8D",
+" 0 c #36417D",
+" q c #6F79AB",
+" w c #43518D",
+" e c #1F2A6D",
+" r c #1E286C",
+" t c #414F8B",
+" y c #2D3774",
+" u c #1A235E",
+" i c #9599AF",
+" p c #D1D2D6",
+" a c #64666B",
+" s c #475394",
+" d c #777777",
+" f c #767DA1",
+" g c #536299",
+" h c #2F3B79",
+" j c #8E8F94",
+" k c #2A3574",
+" l c #5D68A6",
+" z c #4B5891",
+" x c #50598C",
+" c c #273171",
+" v c #575D7F",
+" b c #222D6C",
+" n c #212B6B",
+" m c #303B7D",
+" M c #404B90",
+" N c #1D2767",
+" B c #3C478C",
+" V c #4C5A95",
+" C c #38447E",
+" Z c #38427E",
+" A c #343E7A",
+" S c #858CAF",
+" D c #42508B",
+" F c #2E397E",
+" G c #1D276A",
+" H c #404E89",
+" J c #1C2769",
+" K c #303A76",
+" L c #2C377C",
+" P c #3E4C87",
+" I c #C7C7C8",
+" U c #AFB0B7",
+" Y c #E4E5EB",
+" T c #29326F",
+" R c #384481",
+" E c #DCDDE3",
+" W c #303C79",
+" Q c #3E4C8A",
+" ! c #3D4A89",
+" ~ c #414B83",
+" ^ c #3A4886",
+" / c #686971",
+" ( c #7B84A8",
+" ) c #515C8C",
+" _ c #656F99",
+" ` c #7880A5",
+" ' c #4A578F",
+" ] c #424C91",
+" [ c #202A69",
+" { c #3E488D",
+" } c #2C3878",
+" | c #1B2464",
+". c #797A7E",
+".. c #3A477F",
+".X c #9D9D9E",
+".o c #39457E",
+".O c #47558F",
+".+ c #D0D0D0",
+".@ c #212C6D",
+".# c #1E286A",
+".$ c #CACACA",
+".% c #283277",
+".& c #3B4783",
+".* c #253074",
+".= c #485793",
+".- c #BEBEBE",
+".; c #222C71",
+".: c #44538F",
+".> c #BCBCBC",
+"., c #6974B0",
+".< c #BABABA",
+".1 c #1E286D",
+".2 c #3E4D89",
+".3 c #848AA6",
+".4 c #5F6585",
+".5 c #4F599D",
+".6 c #B4B4B4",
+".7 c #B2B2B2",
+".8 c #394784",
+".9 c #ABADB5",
+".0 c #34417F",
+".q c #ACACAC",
+".w c #87878B",
+".e c #58649F",
+".r c #AAAAAA",
+".t c #414D8F",
+".y c #2D3978",
+".u c #6F6F70",
+".i c #57608A",
+".p c #A0A0A0",
+".a c #666871",
+".s c #283373",
+".d c None",
+".f c #CFD0D8",
+".g c #364184",
+".h c #465197",
+".j c #242F6F",
+".k c #BBBCC1",
+".l c #9A9A9A",
+".z c #313D7F",
+".x c #989898",
+".c c #A8A8AB",
+".v c #4D5480",
+".b c #52609C",
+".n c #A3A4A6",
+".m c #4E5E98",
+".M c #2B3579",
+".N c #6B739A",
+".B c #293577",
+".V c #293377",
+".C c #273175",
+".Z c #495893",
+".A c #394680",
+".S c #475691",
+".D c #717482",
+".F c #E0E1E5",
+".G c #42508C",
+".H c #DADBDF",
+".J c #1D276B",
+".K c #979DBB",
+".L c #8088AB",
+".P c #2C3673",
+".I c #5C6281",
+".U c #242F75",
+".Y c #3C457C",
+".T c #33407D",
+".R c #747474",
+".E c #41508E",
+".W c #B8B9BA",
+".Q c #646FAD",
+".! c #2E3A78",
+".~ c #808083",
+".^ c #2C3876",
+"./ c #2B3675",
+".( c #293473",
+".) c #384485",
+"._ c #242E6E",
+".` c #45518B",
+".' c #424F88",
+".] c #465082",
+".[ c #414D87",
+".{ c #2D387A",
+".} c #9FA2AB",
+".| c #2B3678",
+"X c #384288",
+"X. c #171F57",
+"XX c #343E84",
+"Xo c #DEDEE2",
+"XO c #353F7B",
+"X+ c #2F3A7F",
+"X@ c #1C2669",
+"X# c #2F3975",
+"X$ c #3E4B87",
+"X% c #D7D8DB",
+"X& c #C4C4C5",
+"X* c #4A5996",
+"X= c #36437F",
+"X- c #48548A",
+"X; c #202A70",
+"X: c #F1F1F1",
+"X> c #1F2A6F",
+"X, c #515BA0",
+"X< c #B4B4B5",
+"X1 c #3B4987",
+"X2 c #4C579B",
+"X3 c #7D7D7F",
+"X4 c #B2B2B3",
+"X5 c #3E4880",
+"X6 c #384584",
+"X7 c #495598",
+"X8 c #364382",
+"X9 c #5661A8",
+"X0 c #434F92",
+"Xq c #374079",
+"Xw c #53597A",
+"Xe c #A5A7B0",
+"Xr c #3E498D",
+"Xt c #5966A4",
+"Xy c #1E296A",
+"Xu c #1D2769",
+"Xi c #C9C9C9",
+"Xp c #929293",
+"Xa c #3B458D",
+"Xs c #3D4A85",
+"Xd c #4C5C97",
+"Xf c #E6E7EC",
+"Xg c #D4D5D7",
+"Xh c #C3C3C3",
+"Xj c #C1C1C1",
+"Xk c #232D72",
+"Xl c #465491",
+"Xz c #455490",
+"Xx c #212B70",
+"Xc c #34407C",
+"Xv c #43528E",
+"Xb c #CCCDCF",
+"Xn c #1F296E",
+"Xm c #323E7A",
+"XM c #1E296D",
+"XN c #525EA0",
+"XB c #313C79",
+"XV c #B7B7B7",
+"XC c #676A79",
+"XZ c #2A3472",
+"XA c #7C7C7D",
+/* pixels */
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d c.%.V.V.%.C.U.U.;XnXy N.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.P +X 8XXX+ L L.V.%.U.UXk.;.;XxX;Xn | u.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.dX1X0 M BX 8XXX+ L L.V.%.U.UXk.;.;XxX>X>XnXn r u.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d ZX,.5X7.h MXaX XXXXX+ L L.V.%.U.UXk.;.;XxX>X>XnXM.1.1 rX@ u.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.dX9X9X,.5X7X0 MXaX XXXXX+ L.M.V.%.U.UXk.;XxXxX>XnXnXM.1 r r r.J 5.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.dXd 6 6X9X,X2.hX0 MXa 8XXXXX+ L.M.V.C.*.UXk.;XxXxX>XnXnXM.1 r r.J.J.JX@ u.d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.,., 6X9X9X,X2.h ] {Xa 8XX 1 F L.M.V.s = c :.;XxX;X>XnXnXM.1 r r.J.J.J G G 5.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.`.,.,.Q 6X9XN.5X7.h ] {Xa 8XX 1 F L m 0XOXOXB K.s.;XnX>XnXn.1 r r r.J.J G G G G 5X..d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d z.,.,., 6X9X9X,X2.hX0 MXaX 8XXX+ m Z x # f `.N @ ~ c.@XnXnXM.1 r 3 [ G G G G GXuX@ 5X..d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d V.,.,.,.Q 6X9X9X,X2.hX0 MXa 8XX 1X+ Z.' (.fX% E .Xe @ y.@XnXnXM r e._ T b rXu G GXuX@X@ 5X..d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d z.,.,.,.Q 6X9X9X,X2X7.h M {Xa 8XX , R.i ( U E.f I o j.4 y.@XnXnXM 3._Xq ).Y T [XuXu JX@X@ 5 5X..d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.`.,.,.,.Q 6X9X9XN.5X2.hX0 MXaX XXXX.&X-.L.f EXf.$ X j. Xw.P nXnXn.1 [ T ) - @Xq T.#XuXuXuX@ 5 5 5X..d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.,.,.,.Q 6 6X9XNX,X2.hX0 M {Xa 8.gX$ @ S 2 EXo.FXh &.w.RXwX# T.@ e r.@._.Y @ @ @X5 T n.# GXuXu 5 5 5.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.,.,.,.Q 6X9X9X9X,X2X7.h ] MXaX .gX$ ' S.f E YXo.H.>.X.~ / v KXBXm.(Xx.@.@.PXq @ i @XqXZ e 3 r.#.#XuXu |.d.d.d.d.d.d",
+".d.d.d.d.dXd.,.Q 6 6X9X9XNX,X2X7.hX0 MXaX *.[ > S 2X%Xb ..$XgXV.l.~ / vXqX- _X-Xm.( < c y.] @ > _.] c.@ e e 3 3Xy.# u.d.d.d.d.d",
+".d.d.d.d.d 6 6 6X9X9X9XNX,X2X7.hX0 M B *X H z S.fX%.H.W.xX4Xb.6.l.~ / v.Y >.K fX- A : : :X#.Y.N.9 @ K._.@.@.@ n nXyXu.d.d.d.d.d",
+".d.d.d.dX9 6X9X9X9XNX,.5X2X7 sX0.tXrX1 !.G > S 2.H IX4.p 9.qXbX4.l.~ /Xw A.v _ # ( x.y = :XZXq _.9 _XqXZ < < < <.@ n nXu.d.d.d.d",
+".d.d.d ZX9X9X9XNX,X9 lXtXt.e.b.b V V z z ) -.f.H.H.6 9.wX3 &.$.7.l.~ /.v yXq ~ # 2.N 0.(.C.sXB @.9.NX5 K c :.j.j < <.@.@ |.d.d.d",
+".d.d.dX,X,X,X,.5XN q.K.K.K.K.K.K.K - - -.K 2 EXbX<.xX3X3XA.n.$ .x.~ /Xw K A.o.N 2 f.[XB.s./.y.].N.N.N.]X# c c : :.j < b [.d.d.d",
+".d.d.d.5.5X2X2X7XN -X% EXfXfXfXfXfXf YXfXfXf Y.W 9.~.R dXA.pXi .x.~ /XwXm AXO _ 2.L ).&.|.|.BXO ~.N.9 _XqXZ.s = c : :.j <.d.d.d",
+".d.dX1X7X7.h.h s.b - E YXf Y.FXoXo E.H.H EXoXoXVXp.wXA.~.w &Xi .x.~ /Xw AXm $X- # `.L ) +.y.|XBX5 @ i @ Z.P k.(.s = c : : N.d.d",
+".d.dX0.hX0X0 ] ].m.KXfXf YXoX%XgXg p.+.+.+Xg.HXV 4 9.w.w 9.q.$ .x.~ /XwXOXm.{ Z.] f 2.N.& $.{ K.o.i (.i.o.^.|.B k.(.s = c.j.d.d",
+".d.P M M M M {Xr V -Xf.FXoXg.+Xb.$Xi I IXiXgXo p IXj.-XjXj.$.+.6.x.~ /Xw 0 W.{XO ~ _.K _X5XB m W.o @ i > ~Xm } }././ k.(.s c [.d",
+".d + BXaXaXaXa *Xl -XfXoXgXbX&XhXj.-.-.-Xj p YX:X:X:X:X:X:XfX%XV.x.~ /Xw Z h m WX5 @.L @ ~Xm mXB.. _ 2 #.].o h.y } }./ k.(.s [.d",
+".dX X X X 8 8.).` S YXoXg.$XjXj.-.-.>.-.-.+.F YXfXfXfXfXfXoXgXV 4X3 /Xw.oXB.z +X5 >.K.N.' Z +.TX= x # # # xXc h.!.y }.^./ k =.d",
+".d 8 8XXXXXXXX ,.[ S Y E.+XiXj.-.-.<.<.<.-XbXo.H.H.HX%X%X%Xg p.6 4X3 /Xw.o +.z.TXs.N 2 fX- ~ , ,.0X5X- # 2.NX5Xm $ $.!.y.^./ k.d",
+" cXXXXXXXXXX 1 +X$ S Y.H.+ I.-.>.<.WXV.W.<XiXgX& X.c O X.-Xb 4X3 / v.&.T ,.0.8 ) f f f ).&X8X8...' _ i _ ~Xm + W $.!.y.^./XZ",
+".%X+X+X+X+X+ F mXs.L Y.HXbX&.<.<.WXVXVXV.<X&.+.r.wX3 dXA.~ &X& X 4X3 / v.&.0X8X8.).[ x f 2 #.' RX8X=.[ @.3 @.[ 0 + +Xm W h.y.y k",
+".V L L L L L L.{.&.L YX%.$Xj.WXVXVX<.6XV.WXhXb &X3 d.u.RXA.pXh.q 4X3 / vXs.0X8.).)XsX-.N.K.N.` R.).A.' @.L @.' 0.0.T + + W h.!.^",
+".V L L L.M.M.M.| R (.FX% I.-XVXVX<.6X4X<XVXj.$.p d.u a.u.R.lXj O 4X3 / vXsX8.).).).&.` > S >X-.8 ^.&.' @.3 >.`X=.0.0.T +Xm W h.^",
+".%.V.V.V.V.V.%.B Z (.FXg I.-X<.6X4.7.7.6XVXj.$.p d.u a.u.R.lXj.rXpX3.a v.[ R ^ ^X1.&.` > S _.OXsX1.&.` @.L >.`.AX8X8.0.T + + W.!",
+".C.%.%.%.%.C.C.s 0 (.FXgX&.>.6X4.7 X4X<.-Xi.p d.u a.u.R.lXj.rXpX3.a v.[.8X1X1 !XsX- > S _ 'X$ !XsX- >.3 >X-.AX6X8X8.0.T +Xm h",
+".U.U.U.U.U.*.U = A `.F pXj.<.7 .7.6.- I.X.R.u a 7.R.lXj.rXpX3.aXw.[.8 ! ! QX$.O > S _ z.[ Q PX- >.3 >X-.&.8X6X8X=.0.0 + W",
+".U.U.U.U.U.U.U :Xm ` E pXj.W X4.- I.X.R.u a 7.R.xXj.rXpX3 /Xw P.& Q % %.2 ' _ S _ z H.t.[ ' > S _X-Xs ^.8X6X8X=.0XcXm",
+".;XkXkXkXkXkXk :XB f E pXjXV X X .7.- I.nX3 d.u dXA.pXj.rXp. .a v.'X1 % %.t.` ) #.K # z D.E H ' _ S _ 'XsX1 ^.8X6X8X=.0 +",
+"Xn.;.;.;.;.;.;.j K f E p.-XV X X X X .7.-Xi.c.w.~XA.~.w &Xj.rXp. .a.I.` P %.t.E z g ( 2 f z wXv D z _ S _ z PX$X1 ^.8 RX8.0Xc",
+"Xy.;.;.;XxXxXx bX# # E.+.-XV X X X.q.q X Xj.+ I.>.WXV.W.<XjXi XXp. .a.IX-.2.E.E.O > ( ( ( >.ZXl.:.O ) #.K.NX- P QX$X1 ^.8 RX=Xc",
+" NXxXxXxXxXxX; nXZ.N EXb.-X< X.q.q O O X X&X%.FX:X:X:X:X:Xo.+ Xp. .a.I.O H.E w V ` 2 ( g VXlXlXl V g ( 2.NX-.[ Q Q !X1.8.A RXc",
+".dX;X>X>X>X>X>.@ y # EXb.>.6.q.q O O.r X XjXg.H.F.F.FXoXoXgXb Xp. .a.I.O DX0.:.m f.K f g z.=.Z V > ( ( ( @.: t t Q PXs ^.8 R.d",
+".dXnX>X>XnXnXn.@ K ` EXb.<X4.q O O.r.r.q Xj.+.+.+.+.+.+.+Xb.$ XXp. .a.I '.GXl.O g # -.N g.ZX*.Z g ` 2 ( g z.: w.G t.2X$X1.8.A.d",
+".d |XnXnXnXnXn b K f E.$XV X & & & & &.c O.<.$.W.c &.p.n &X<X&.rXp. .a.4 '.:Xl z g `.K f g zX* V g f.K f g 'Xz.: w t Q PXs ^Xm.d",
+".d uXnXMXMXMXM nX# f.H I .r.p.p.p.p.p.n &X<Xh.pX3 d.u dXA.X.- &Xp. .a.4 '.SXl.m.e.L 2 ( g VXd V.e # -.N.m.OXlXzXv.G t QX$ ^Xq.d",
+".d.d r.1.1.1.1 [ y.N.kXe j.w.w.~.~.~.~.w 9.n.W.lXA.R 7.u d.x.- & 9. .a.4 ' z V _.L -.K q.b.m.m.m.e `.K # g.O.SXl.: w t.2X$.&.d.d",
+".d.d u.1 r r r [.P.4.} j.D.u / / / / /.u d j 4 ;.u a 7.u 4.> & 9. .a.4 z V.m ( 2.K q.e.m.m.m g >.L 2 ` g z.=Xl.: w t.2 PXq.d.d",
+".d.d.d r r r rXy._.Y.i vXwXwXwXwXwXwXwXw v.D j j 9X3.u.u.u 4.< & 9 ;.a.I z.m g.L 2 - l g.m.b.e.N.L.L.L _.m.Z.=.SXzXv.G H P.d.d.d",
+".d.d.dX@ r.J.J G r._ T TXZ.P yX# K KXmXq.Y v.D.w & 9 d.R.u 4.< & 9 ;.a.I '.m @.L 2.L.e.m.b.bXt.L 2.L > gXdX*.Z.SXzXv.G HXs.d.d.d",
+".d.d.d u.J.J.J.J G G 3 3 [ n b._ : =XZX#XO.] v.D.w 9 9.~ ;.l.< & 9 ; a.4 ) > ( S.K q.b.m.b g l.L 2.L.e.mXdX*.Z.SXzXv.G H...d.d.d",
+".d.d.d.d 5.J.J G G G G G r e.@.j : =.s.B } 0X5 v.D.w.nXp.~.p.> & 9 ; a.4 g ` 2 - q.e.m.m.b.e _ S 2 (.eXdXdX*.Z.SXzXv.G P.d.d.d.d",
+".d.d.d.d.dX@ G G G G G G r e.@.; :.*.s.B.| W 0.] v.D.w j.x &XV.p 9 ; a.4 ) _.L q.e.b.m.b.e.N (.L.L _ gXdXdX*.Z.SXz w H.d.d.d.d.d",
+".d.d.d.d.d u G G G GXu G r e.@ < :.*.s.B.|.y $ Z ~.I.D j X .X 9 ; a.4 z g.e.mXdXd.m g > (.K ( >.e.mXdXdX*.=Xl.: w...d.d.d.d.d",
+".d.d.d.d.d.d 5 GXuXu JXu.# e.@ < : c.C.B.| }.!Xm.o.v.I.D j.X .X.w d a.4 ' ' z.=X* V.m > ` ` ( _ g.mXdXdX*.Z.S.O.: H.d.d.d.d.d.d",
+".d.d.d.d.d.d.d 5X@X@X@Xu G 3.@ <.j :.C k.B }.{ $ +...].I.D j X.l.w d a.I ' wXl.=.Z z g f.K ` > g.mXdXdX*.Z.=.SXz D.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.dX. 5X@X@X@Xu r e.@.j : =.(.B.|.y $ mXc...v.I.D.w.~ d / a.IX- DXlXl.=.=Xd > # > gXdXdXdX*.Z.=.SXz w.Y.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.dX. 5 5 5Xu.# e.@ < : c.s k.|.{ $ m +.0X5.] vXC.a a a a vX- D.:XlXl.S.= V.mXd VX*X*X*.Z.=.SXl w.Y.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.dX. 5 5 5.# 3.@ <.j c.s k.| }.! $ + + 0X5.v v v v v v x.` D w.:XlXl.S.S.S.Z.=.Z.Z.=.S.SXz w.Y.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.dX. 5 5Xu 3 n <.j : =.(.B }.y $ m +.T.0.A ~ ~ ~.[.'.' Q t.G w.:XzXlXl.S.S.S.S.SXl.OXz w.Y.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.dX. 5XuXy n.@ < : c.s k./ }.! $ + +.0.0 0 C.A.&.&XsX$ Q t.G wXv.:.:XzXzXzXzXz.:.: D.Y.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d |.#Xy n <.j : =.(./ }.y $ W +.T.0X8X8X6.8 ^X1X$ Q Q t t.G w wXvXvXvXv w w H.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d uXu n.@ < : c.s k./ }.! $Xm +.T.0X8X8X6.8 ^X1 ! P.2 Q t t t.G.G.G.G H...d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.dXu.@ b.j : =.( k.^.y.! W + +.T.0X=X8X6.8 ^X1XsX$ P Q.2.2 H H H P.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d | [ < : c.s.(./.^.y h WXm +.T.0X=X8 R.8.8 ^X1XsX$X$ P PXs...d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d N.j c.s k./.^.y h W + +.0.0X=X8 R.A.8.8 ^ ^.&Xq.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d [ [ = k./.y.! h WXm +Xc.0.0X= R R.AXmXq.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d",
+".d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.dXZ k.^.^.! h WXm +XcXcXc.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d.d"
+};
diff --git a/arts/builder/pics/Synth_PLAY_AKAI.xpm b/arts/builder/pics/Synth_PLAY_AKAI.xpm
new file mode 100644
index 00000000..a7cedce2
--- /dev/null
+++ b/arts/builder/pics/Synth_PLAY_AKAI.xpm
@@ -0,0 +1,317 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"64 64 246 2",
+/* colors */
+" c #27326F",
+" . c #68D14E",
+" X c #303E7B",
+" o c #2E3A79",
+" O c #3B4889",
+" + c #2A3675",
+" @ c #293474",
+" # c #51FD0F",
+" $ c #273272",
+" % c #374285",
+" & c #4FFB0D",
+" * c #4EFB0C",
+" = c #44A73B",
+" - c #323E80",
+" ; c #434E94",
+" : c #202A6B",
+" > c #3E4A8F",
+" , c #1C2667",
+" < c #4BCA27",
+" 1 c #3D4B84",
+" 2 c #52F913",
+" 3 c #375074",
+" 4 c #45538F",
+" 5 c #1F2A6D",
+" 6 c #2F3A80",
+" 7 c #323D79",
+" 8 c #1E286C",
+" 9 c #414F8B",
+" 0 c #46ED07",
+" q c #46EB07",
+" w c #4CB935",
+" e c #3B4985",
+" r c #4C5999",
+" t c #4B5998",
+" y c #1A235E",
+" u c #3A5970",
+" i c #43DC0E",
+" p c #44CF19",
+" a c #384582",
+" s c #232E74",
+" d c #445191",
+" f c #323F7C",
+" g c #656FB8",
+" h c #6F76A4",
+" j c #408551",
+" k c #5CFE19",
+" l c #538967",
+" z c #56FE13",
+" x c #374384",
+" c c #46B332",
+" v c #303B7D",
+" b c #1D2767",
+" n c #2B3778",
+" m c #5BE22F",
+" M c #44E30B",
+" N c #4AF40A",
+" B c #42508B",
+" V c #1E296B",
+" C c #51629D",
+" Z c #48F208",
+" A c #1D276A",
+" S c #50609C",
+" D c #404E89",
+" F c #47EE07",
+" G c #2A357A",
+" H c #283378",
+" J c #29366F",
+" K c #263176",
+" L c #242F74",
+" P c #465492",
+" I c #222D72",
+" U c #202B70",
+" Y c #43508F",
+" T c #41508D",
+" R c #30496F",
+" E c #3E4C8A",
+" W c #52955B",
+" Q c #2D3876",
+" ! c #45EA08",
+" ~ c #45E808",
+" ^ c #5D68AF",
+" / c #3C4F7E",
+" ( c #4C569B",
+" ) c #394685",
+" _ c #4B845E",
+" ` c #4EEC14",
+" ' c #404E8F",
+" ] c #1E2867",
+" [ c #293475",
+" { c #283474",
+" } c #253071",
+" | c #4FF90E",
+". c #475E85",
+".. c #4A756A",
+".X c #4DF90C",
+".o c #4BF50A",
+".O c #4AF509",
+".+ c #1E286A",
+".@ c #49F108",
+".# c #1C2668",
+".$ c #4BD51E",
+".% c #4D5D98",
+".& c #293478",
+".* c #4C5B97",
+".= c #45D318",
+".- c #3B4783",
+".; c #273276",
+".: c #5B67A9",
+".> c #263075",
+"., c #485793",
+".< c #222C71",
+".1 c #44538F",
+".2 c #202A6F",
+".3 c #1F2A6E",
+".4 c #42518D",
+".5 c #1D286C",
+".6 c #699088",
+".7 c #45ED07",
+".8 c #3D4B88",
+".9 c #406B63",
+".0 c #4B5B99",
+".q c #34417F",
+".w c #505BA1",
+".e c #3C498A",
+".r c #6175A1",
+".t c #4EA645",
+".y c #45D814",
+".u c #3A5E6A",
+".i c None",
+".p c #242F6F",
+".a c #4A647D",
+".s c #4DF80B",
+".d c #232D6E",
+".f c #4CF80A",
+".g c #4CF60A",
+".h c #334077",
+".j c #556795",
+".k c #1E2969",
+".l c #2D397B",
+".z c #4E5E98",
+".x c #5DB950",
+".c c #38447F",
+".v c #242F72",
+".b c #34427B",
+".n c #43528D",
+".m c #1E296C",
+".M c #1D276B",
+".N c #60EB2E",
+".B c #404E8A",
+".V c #2C377D",
+".C c #47EE08",
+".Z c #5CF620",
+".A c #1D2661",
+".S c #4C5C99",
+".D c #485895",
+".F c #28346F",
+".G c #475694",
+".H c #364480",
+".J c #48598B",
+".K c #43954A",
+".L c #33407D",
+".P c #222C69",
+".I c #303C7A",
+".U c #808CAE",
+".Y c #2E3A78",
+".T c #2B3875",
+".R c #45E609",
+".E c #2B3675",
+".W c #53FD10",
+".Q c #44BF26",
+".! c #465296",
+".~ c #25306F",
+".^ c #757EA3",
+"./ c #496C71",
+".( c #42587E",
+".) c #2E3A7B",
+"._ c #2D387A",
+".` c #48E40F",
+".' c #3C468C",
+".] c #1B2465",
+".[ c #4A5B93",
+".{ c #46C821",
+".} c #273274",
+".| c #232E70",
+"X c #171F57",
+"X. c #46558F",
+"XX c #458D52",
+"Xo c #222C6F",
+"XO c #212C6E",
+"X+ c #52CE30",
+"X@ c #313C81",
+"X# c #43538C",
+"X$ c #6C9F7F",
+"X% c #48F108",
+"X& c #1C2669",
+"X* c #47EF07",
+"X= c #4F5F9B",
+"X- c #3F4D88",
+"X; c #4B5B97",
+"X: c #44DE0E",
+"X> c #273277",
+"X, c #495995",
+"X< c #253075",
+"X1 c #475793",
+"X2 c #384A77",
+"X3 c #465592",
+"X4 c #36417F",
+"X5 c #5663A5",
+"X6 c #212C71",
+"X7 c #4CE019",
+"X8 c #41765B",
+"X9 c #405282",
+"X0 c #41626F",
+"Xq c #45E708",
+"Xw c #394785",
+"Xe c #439D42",
+"Xr c #596D97",
+"Xt c #545FA6",
+"Xy c #334475",
+"Xu c #334275",
+"Xi c #427F55",
+"Xp c #394588",
+"Xa c #263172",
+"Xs c #38467D",
+"Xd c #354184",
+"Xf c #343F83",
+"Xg c #333F82",
+"Xh c #202B6C",
+"Xj c #49F408",
+"Xk c #1F296B",
+"Xl c #49F208",
+"Xz c #567E79",
+"Xx c #2F4074",
+"Xc c #51609C",
+"Xv c #1D2769",
+"Xb c #48F007",
+"Xn c #4A5A95",
+"Xm c #4C7D65",
+"XM c #475692",
+"XN c #232D72",
+"XB c #212B70",
+"XV c #1F296E",
+"XC c #1E296D",
+"XZ c #2D3A75",
+"XA c #48DD14",
+"XS c #3E517F",
+/* pixels */
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.iXa.; H H H K L s.<.2 V b.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.E fXpXdXf 6.V G HX>X< LXN.<X6 U.2XV.A y.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i O ; >.'XpXdXf 6.V G HX>X< LXN.<X6 U.2.2XVXV 8 y.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.iX4.w.w ( ; >.'XpXdX@ 6.V G HX>X< LXN.<X6 U.2.2XVXC 8 8 8X& y.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.iX5X5.w (.! ; >.'XpXfX@ 6.V G HX>X< LXN.<XB U.2.3XVXC 8 8 8.5.M.#.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i r ^ ^Xt.w (.! ; >.'XpXfX@ 6.V G H KX< LXN.<XB U.2XVXVXC 8 8 8.M.M.MX& y.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i g g ^.:Xt.w (.! ; >.' %XfX@ 6.V G H KX< LXN.<XB.2.2XVXVXC 8 8 8.M.M.M A A ,.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i B g g g ^X5Xt.w (.! ; >Xp %XfX@ 6.V G H.>X< s IX6XB.2.2XVXV 8 8 8.5.M.M A A A A.#X .i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.[ g g g ^.:Xt.w (.! ; >.'XpXd - 6.V G HX>.> L s IX6XB.2.3XVXC 8 8 8.M.M.M A A AXvX& ,X .i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.* g g g g ^X5Xt.w (.! ; >.'Xp a.-.q.V G HX>X< LXN.<XB U.2.3XVXC 8 8 8.M.M.M A A AXvX&X& ,X .i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.[ g g g g ^.:Xt.w (.! ; >.'Xp 1X0...( f.& H KX< LXN.<XB.2.2XVXVXC 8 8.M.M.M A A AXvX&X&X&.# ,X .i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i B g g g g ^.:XtXt.w C r ; >.' a / _.x..Xs nX>.>X< s IX6XB.2.3XVXV 8 8 8 8 8.M.M A A AXvXvX&.#.# ,X .i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i g g g g ^.:X5X5X5.r.6Xr P.'Xp.H 3 _.x...b [X>X< LXN.<X6 U.2.3XVXVXCXC 8.m.m.m 8 8 8 8.+ AXvXv.#.# ,.i.i.i.i.i.i.i",
+".i.i.i.i.i.i g g g g ^ ^.:.:.:X5.6X$.6., OXp.q 3XXX+Xm.b @ KX<.vXN.<XB U.2.2.2.2.2.2.2.2.2 5 5 5 : 5.mXk 8.+.+XvXv.].i.i.i.i.i.i",
+".i.i.i.i.i r g g ^ ^.:.: h.6.rXr.6 ..6X. OXdXs uXXX+Xm.b {.> L sXN.<XBXBXBXBXBXBXBXBXoXoXoXoXoXOXOXOXhXh : :XkXk.+.+ y.i.i.i.i.i",
+".i.i.i.i.i ^ ^ ^.:X5Xt.:.^X$.6XrX$ ..6.J.8Xd /.u.KX+Xm.h $X< L sXNXN.<.<.<XNXN.|.|.|.|.|.|.|.|.|.|.dXo.dXoXOXhXh : : ].i.i.i.i.i",
+".i.i.i.iX5 ^.U.UXtXt.w.:.6 .X$XrX$ . lX9 1 x.a.K.tX+Xm.h $ L L.v.v.v.v.v.v.v.v.v.v.v.v.v.v } }.v.v.v.v.p.|.|.d.dXOXh : ].i.i.i.i",
+".i.i.iX4X5Xt.U.U.w.w (X5.6 .X$XrX$ . W.( /.-XzX+X+ mXm.h $X< L LX<X<X<X<X<.>.>.>.>.>.>.>.}.}.}XaXaXaXa } } }.p.p.|.dXOXh.A.i.i.i",
+".i.i.i.w.w.w.w.w ( (.GXc.6 . .X$ . . W 3 /X9 l m m m _Xy J.>.>.>.>.>.>.}.} {.} [.;.;.;.;.; [ [ [ [ {.}.}.}XaXa } }.p.d.d :.i.i.i",
+".i.i.i.w ( ( ( (.!.! d.z.6 . ..N.N mXX 3 3.( l m m mXi RXx {.}.;X>X>.;.&.Y 7.Y n.&.&.&.&.&.&.&.& [ [ [ [ { {.} $Xa } }.p.d.i.i.i",
+".i.i O ( %.!.U.U ; ; d.zX$ ..N.N.N mXe.uX8 l.x.Z.Z ` =Xi.u R J H [ + Q.IX2X0 3.I n n n n n n n n n n n n + [ [ { { $Xa }.~.A.i.i",
+".i.i ; ;Xf ;.U.U > >XM.jX$.N.N k.Z.N.tX8.tX+ m 2 2 2X7 < j RXx n.Y f.h.bX0.K./.b Q.l.l.l.l.l.l.l.l._._ n n n n + [ @ { $Xa.~.i.i",
+".i.E > >X@ >.U.U.' '.j.6.x.N.Z k.Z.ZX+ =X+ m `.W.W | `X7 c j.u.h 3./.(X2./ _X0Xs.h.Y o.) v v v v.).) o.l o._ n n.E + @ @ $ $.P.i",
+".i f.'.'.).'.U.UXp BXz . m.Z k k k k.Z ` 2 2.W.W # #.X.g.$ w _ /.. W..XS./XmX0XsX2Xy.b X v v v v v v v v.) o o o n n.E + @ {.P.i",
+".iXpXpXpXpXp % % )X#Xz ..N k k k k k 2 2 2.W # # # &.X.gX7 < _.(Xm.t _./..Xm./XSX0X8 u.H.b f f - - - - v v v.).I o.Y Q.T.E @ $.i",
+".iXdXdXdXfXfXfXf 1. l m.Z k k k z z z z.W.W # # & &.X.gX7.$ WX0 _ w WXmXmXm./.(...t.. /.-.-.HXg.q.q - - - f X v.) o o.Y Q.E @.i",
+"XaXfXfX@.;X@.^.U.a l.x.N.Z k k z z z z.W.W # & &.X.X.g.o `X7 w W.t w W.. _ W...(Xm = _X0X0./.( a.q.H.q.q.q -.L f X.I.I o.Y Q.T.F",
+".; 6 6 6.} 6.^.UXz . m k k z z z z.W.W.W # # &.X.s.g.o.o.@.@X7.$.$ < W.a _ w _.( _ w.KX8XX.t.. /.H x xXd.H.q.q.q.L f X.I o.Y Q +",
+" H.V.V.V.v.V.^.U lX+ m z z z z.W.W.W # # & &.X.s.g.o.o NXl.@.`.`XAX7 w W.t <XX./.K.{ c = c wXX uX2XsXsXs.bXs.b.b.h f f X.I o.Y.T",
+" H G G.v.| G.^.U l m.Z z.W.W.W.W # # # & *.X.s.g.o.o N NXlXlX%X%.C qXA.$.=.= =X8 =.=.y.y.y.=Xe.9X0 u u uXSXS / 3 /XsXs f X.I o Q",
+" H H H H [XZ.u _.x.Z 2.W.W # # # # & *.X.s.s.g.o.o N NXl.@X%XbX* 0 q.`.`XAX:.Q =.QX:X:X:X: i.QXeXe.K.K.KXX _XXXXXi..X0Xs f X.I.Y",
+" KX>X>.^.^ R jX+X7 2.W # # # & & * *.X.s.g.g.o.o N NXlXlX%X%X*X* F F 0 0 q ~ MX7.`XqXq ~.R.RX:X7X7.= p.$.$ <.$ p.QX+X$.^.^ f X o",
+" LX<X<.^ h R jX+X7 2 | # & & * *.X.s.g.g.o.o N NXlXl.@X%XbX*X* F F 0 0 q q ~ MX7.`XqXq ~.R.R.`X7X7.= p.$.$ <.$ p.QX+X$.^.^.q f.I",
+" s L L L.~ R j <X7 | | & * *.X.X.f.g.o.o.O NXlXlXlXlX%X%X*X* F F 0 0 q q ~ ~ M.yX:.RXq ~.R.R i.y.=.= p p p.{.{ p.Q c j 3.b.q.L X",
+".<XNXNXN.| J.6X$.t ` `.X.X.s.f.g.o.o.O.O NXlXl ZX%X%XbX*X* F F 0 q ~.`XAX:X:.Q =.Q i iX: i i.QXeXe.K.K.K jXi j.K _X8.uXx.TX4.q f",
+".2.<.<.<.<XN.^.^Xi.$ `.f.g.g.o.o.O.O N NXlXlX%X%X%X%X*X* F F 0 0 ~.RXA.$.=.= =Xm =.=.=.y.= pXe././.a.a.(. . .(.(.(X9 1.- a.H.q f",
+" VX6X6X6XBXB h.^X8 <X7.o.o.o.O.O NXjXlXlXlX%X%XbX*X*X* F q !.`XAXA.y w W.t < W./ W.{ c = c w _. . X# B B D DX9 1 1 1 e.- a a.H.L",
+" b U U U U U h.^.9 w.$ N N.O N NXlXlXlXlX%X%XbXbX*X* F F ! ~XA.$.$ < W.a l w l.a l w.KXm W.tXz.[ 4X3 P d d Y.4 9 E E.8 e.- a.H.L",
+".i.2.2.2.2.2Xh } 3X8.tX7 `XjXlXlXl ZX%X%XbX*X*X* F F.7 qXA.= w W.t w WXz l WXz.a l.t l.aXzXz.j.[X3.GX1X3 P.1.n.4 9 EX- e e a a.i",
+".iXV.2.2.3XVXV.2 JXyXi.$X7XlXlXlX%X%XbXbX*X* F F F F ! ~.=.Q W.a l w W l l lXz.jXz.tXz.[.[.[XnX,.D.D.D.GX3 P.1.n B.BX-.8 e.-.c.i",
+".i.AXVXVXVXV h h.d JX8 <.$.CX*X%X* F !.R ~ q.7 0 0 q.RX: c.K... Xm.t lXzXz lXz.[.aXm.a.*.[.[Xn tX;X;X,.DX1X3 P.1.4 9 EX- e e.h.i",
+".i yXVXCXCXC h h : .9 w <.C.CXb F 0 MX: M q q 0 q q M.yXe.u.(X9./ WXz. Xz l.a.[.[. .[X; t.S.S.S.S.*X;Xn.DX1X3 4.n B.BX-.8 e.h.i",
+".i.i 8 8 8 8 h hXk.P RX8.tXA.`X* ~ M.{ =.Q.=X: ~ ~ ~.y.{.K uX9 D. ./.a. ./ WXz.[.[.[Xn.0.SX=X=X=X=.S.SX;Xn.,XM P.1.4 9X-.8.-.i.i",
+".i.i y 8 8 8 h h 8 b J RXi.$XAX* M.y =X8Xe.Q.=.R.R.R.=.Q j.(X- E.B BX#X#./ WXz.z.,.0.S.SX=X=X=X=X=X=.S.SX;.D.,X3.1.n 9X-X-.b.i.i",
+".i.i.i 8 8 8 8.5.M A.P JXi.{.$.`.y.=Xe.uX8Xi =XAXAXA = _.9 / e.e EX- BX.. Xz.j.[X,.0.SX=X= S C C C SX=.%X;Xn.,XM 4.n B.BX-.i.i.i",
+".i.i.iX&.5.M.M.M.M.M ] JX8 <.{.$.= p j R R 3XX.{.{.{ _.(XS e O E E ' Y dXM.[.[X, t.SX=X= S C C C C CX=X=.SXn.DXM 4.n B.B 1.i.i.i",
+".i.i.i y.M.M h h.M , ] JX8 w =XX =.{ j R RX2 _.{.{.{ _X9XsXw O E E T Y Y.1 4XM.D t.SX=X= C C C C C C SX=.SXn.DXM 4.n B.BXs.i.i.i",
+".i.i.i.i.#.M h h A , ] J.9 cXi.uXi c j R RXy _ < < <Xm 1.cXw O E E ' Y d PX3.DX, t.SX=X= C C CX5 C C SX=.SXn.DXM 4.n BX-.i.i.i.i",
+".i.i.i.i.iX& A A A A ].F.9XeX8 3Xi cX8 RXyXyX8.Q.Q <Xm 1.cXw O.e E ' T d dX3X1.DX;.SX=X= C C C C C CX=.z.*Xn.,XM 4.n D.i.i.i.i.i",
+".i.i.i.i.i y A A A A ].F.u.K.9 RX8 =.9XuXyXyX8 c.Q <Xm 1.cXw O.e E 9 T Y dX3.G.DX;.*.SX= S C C C C SX=.%X;Xn.,X3 4.nXs.i.i.i.i.i",
+".i.i.i.i.i.i , AXvXv ].P R.u 3Xx.9Xe.9.h.h 7.u j =.QXm 1.c ) O.e E E T Y d PX1.DX,X;.S.SX=X= S SX=X=.z.*Xn.DXMX..1 D.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.#X&X&.#.+.P J J.u.KX0.hXZ QXy.u j c.. 1.c )Xw O E E 9 T.1 dX3.G.DXnX;.S.%X=X=X=.z.%.*XnXn.,X3 4 B.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.iX ,X&X&.#Xv.k.P R.uX2XZ.E nXx u j =.. 1.H )Xw e.e E E T.4 d PX3X1.DXnX;X;.S.S.S.*X;XnXn.,XM 4.nXs.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.iX ,.#.#Xv.+ :.d.F.T @ @ @ nXZ RX8Xe./.-.H a ) e O.8 E 9 T Y.1 PX3X1.,.DXnXnXnXnXnXn.D.,XMX..nXs.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.iX ,.#.#.+XkXh.d.~ } $ [ n.TXx.u.9.(.c.q x )Xw O.8 E E 9.4.n.1 PX3XM.,.,.D.D.D.,.,XMX3 4.nXs.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.iX ,.#XvXkXh.d.p } $ { +.T.Y.h.b.b.q.q x a )Xw e.8 E E 9.4.n.1 4 PX3XMXMXMXMXMX3X. 4.nXs.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.iX ,Xv.+ :XO.| }Xa { [.E n.Y.Y.I f.q.q x a ) e e.8 E E 9 B.4.n.1.1 4 4 4 4 4 4.1 BXs.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.].+ :Xh.d.p } $ @ + n.Y o.I f.L.q.H a aXw e e.8 E E.B 9 B.4.n.n.n.n.n.n.n D.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i y ] :XO.d }Xa { @.E Q o.I X f.L.q.H a aXw e e.8X-X- E.B 9 9 B B B B DXs.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i ]Xh.d.p } $ @ +.T.Y o.I X f.L.q.H a aXw e e e.8X-X-X-X-.B.B.BX-.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.A :.d.~Xa $ @.E Q.Y o.I X f.L.q.H a a a.- e e e.8.8X-X- 1Xs.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.A.~ $ { @.E Q.Y o.I X f.q.qX4.H a a a.- e e.-.b.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.P.P $ @.T Q.Y o.I X f.L.q.q.H.H a.c.h.h.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.F +.T Q.Y o.I X f f.L.L.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i"
+};
diff --git a/arts/builder/pics/Synth_PLAY_AKAIS.xpm b/arts/builder/pics/Synth_PLAY_AKAIS.xpm
new file mode 100644
index 00000000..a7cedce2
--- /dev/null
+++ b/arts/builder/pics/Synth_PLAY_AKAIS.xpm
@@ -0,0 +1,317 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"64 64 246 2",
+/* colors */
+" c #27326F",
+" . c #68D14E",
+" X c #303E7B",
+" o c #2E3A79",
+" O c #3B4889",
+" + c #2A3675",
+" @ c #293474",
+" # c #51FD0F",
+" $ c #273272",
+" % c #374285",
+" & c #4FFB0D",
+" * c #4EFB0C",
+" = c #44A73B",
+" - c #323E80",
+" ; c #434E94",
+" : c #202A6B",
+" > c #3E4A8F",
+" , c #1C2667",
+" < c #4BCA27",
+" 1 c #3D4B84",
+" 2 c #52F913",
+" 3 c #375074",
+" 4 c #45538F",
+" 5 c #1F2A6D",
+" 6 c #2F3A80",
+" 7 c #323D79",
+" 8 c #1E286C",
+" 9 c #414F8B",
+" 0 c #46ED07",
+" q c #46EB07",
+" w c #4CB935",
+" e c #3B4985",
+" r c #4C5999",
+" t c #4B5998",
+" y c #1A235E",
+" u c #3A5970",
+" i c #43DC0E",
+" p c #44CF19",
+" a c #384582",
+" s c #232E74",
+" d c #445191",
+" f c #323F7C",
+" g c #656FB8",
+" h c #6F76A4",
+" j c #408551",
+" k c #5CFE19",
+" l c #538967",
+" z c #56FE13",
+" x c #374384",
+" c c #46B332",
+" v c #303B7D",
+" b c #1D2767",
+" n c #2B3778",
+" m c #5BE22F",
+" M c #44E30B",
+" N c #4AF40A",
+" B c #42508B",
+" V c #1E296B",
+" C c #51629D",
+" Z c #48F208",
+" A c #1D276A",
+" S c #50609C",
+" D c #404E89",
+" F c #47EE07",
+" G c #2A357A",
+" H c #283378",
+" J c #29366F",
+" K c #263176",
+" L c #242F74",
+" P c #465492",
+" I c #222D72",
+" U c #202B70",
+" Y c #43508F",
+" T c #41508D",
+" R c #30496F",
+" E c #3E4C8A",
+" W c #52955B",
+" Q c #2D3876",
+" ! c #45EA08",
+" ~ c #45E808",
+" ^ c #5D68AF",
+" / c #3C4F7E",
+" ( c #4C569B",
+" ) c #394685",
+" _ c #4B845E",
+" ` c #4EEC14",
+" ' c #404E8F",
+" ] c #1E2867",
+" [ c #293475",
+" { c #283474",
+" } c #253071",
+" | c #4FF90E",
+". c #475E85",
+".. c #4A756A",
+".X c #4DF90C",
+".o c #4BF50A",
+".O c #4AF509",
+".+ c #1E286A",
+".@ c #49F108",
+".# c #1C2668",
+".$ c #4BD51E",
+".% c #4D5D98",
+".& c #293478",
+".* c #4C5B97",
+".= c #45D318",
+".- c #3B4783",
+".; c #273276",
+".: c #5B67A9",
+".> c #263075",
+"., c #485793",
+".< c #222C71",
+".1 c #44538F",
+".2 c #202A6F",
+".3 c #1F2A6E",
+".4 c #42518D",
+".5 c #1D286C",
+".6 c #699088",
+".7 c #45ED07",
+".8 c #3D4B88",
+".9 c #406B63",
+".0 c #4B5B99",
+".q c #34417F",
+".w c #505BA1",
+".e c #3C498A",
+".r c #6175A1",
+".t c #4EA645",
+".y c #45D814",
+".u c #3A5E6A",
+".i c None",
+".p c #242F6F",
+".a c #4A647D",
+".s c #4DF80B",
+".d c #232D6E",
+".f c #4CF80A",
+".g c #4CF60A",
+".h c #334077",
+".j c #556795",
+".k c #1E2969",
+".l c #2D397B",
+".z c #4E5E98",
+".x c #5DB950",
+".c c #38447F",
+".v c #242F72",
+".b c #34427B",
+".n c #43528D",
+".m c #1E296C",
+".M c #1D276B",
+".N c #60EB2E",
+".B c #404E8A",
+".V c #2C377D",
+".C c #47EE08",
+".Z c #5CF620",
+".A c #1D2661",
+".S c #4C5C99",
+".D c #485895",
+".F c #28346F",
+".G c #475694",
+".H c #364480",
+".J c #48598B",
+".K c #43954A",
+".L c #33407D",
+".P c #222C69",
+".I c #303C7A",
+".U c #808CAE",
+".Y c #2E3A78",
+".T c #2B3875",
+".R c #45E609",
+".E c #2B3675",
+".W c #53FD10",
+".Q c #44BF26",
+".! c #465296",
+".~ c #25306F",
+".^ c #757EA3",
+"./ c #496C71",
+".( c #42587E",
+".) c #2E3A7B",
+"._ c #2D387A",
+".` c #48E40F",
+".' c #3C468C",
+".] c #1B2465",
+".[ c #4A5B93",
+".{ c #46C821",
+".} c #273274",
+".| c #232E70",
+"X c #171F57",
+"X. c #46558F",
+"XX c #458D52",
+"Xo c #222C6F",
+"XO c #212C6E",
+"X+ c #52CE30",
+"X@ c #313C81",
+"X# c #43538C",
+"X$ c #6C9F7F",
+"X% c #48F108",
+"X& c #1C2669",
+"X* c #47EF07",
+"X= c #4F5F9B",
+"X- c #3F4D88",
+"X; c #4B5B97",
+"X: c #44DE0E",
+"X> c #273277",
+"X, c #495995",
+"X< c #253075",
+"X1 c #475793",
+"X2 c #384A77",
+"X3 c #465592",
+"X4 c #36417F",
+"X5 c #5663A5",
+"X6 c #212C71",
+"X7 c #4CE019",
+"X8 c #41765B",
+"X9 c #405282",
+"X0 c #41626F",
+"Xq c #45E708",
+"Xw c #394785",
+"Xe c #439D42",
+"Xr c #596D97",
+"Xt c #545FA6",
+"Xy c #334475",
+"Xu c #334275",
+"Xi c #427F55",
+"Xp c #394588",
+"Xa c #263172",
+"Xs c #38467D",
+"Xd c #354184",
+"Xf c #343F83",
+"Xg c #333F82",
+"Xh c #202B6C",
+"Xj c #49F408",
+"Xk c #1F296B",
+"Xl c #49F208",
+"Xz c #567E79",
+"Xx c #2F4074",
+"Xc c #51609C",
+"Xv c #1D2769",
+"Xb c #48F007",
+"Xn c #4A5A95",
+"Xm c #4C7D65",
+"XM c #475692",
+"XN c #232D72",
+"XB c #212B70",
+"XV c #1F296E",
+"XC c #1E296D",
+"XZ c #2D3A75",
+"XA c #48DD14",
+"XS c #3E517F",
+/* pixels */
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.iXa.; H H H K L s.<.2 V b.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.E fXpXdXf 6.V G HX>X< LXN.<X6 U.2XV.A y.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i O ; >.'XpXdXf 6.V G HX>X< LXN.<X6 U.2.2XVXV 8 y.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.iX4.w.w ( ; >.'XpXdX@ 6.V G HX>X< LXN.<X6 U.2.2XVXC 8 8 8X& y.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.iX5X5.w (.! ; >.'XpXfX@ 6.V G HX>X< LXN.<XB U.2.3XVXC 8 8 8.5.M.#.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i r ^ ^Xt.w (.! ; >.'XpXfX@ 6.V G H KX< LXN.<XB U.2XVXVXC 8 8 8.M.M.MX& y.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i g g ^.:Xt.w (.! ; >.' %XfX@ 6.V G H KX< LXN.<XB.2.2XVXVXC 8 8 8.M.M.M A A ,.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i B g g g ^X5Xt.w (.! ; >Xp %XfX@ 6.V G H.>X< s IX6XB.2.2XVXV 8 8 8.5.M.M A A A A.#X .i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.[ g g g ^.:Xt.w (.! ; >.'XpXd - 6.V G HX>.> L s IX6XB.2.3XVXC 8 8 8.M.M.M A A AXvX& ,X .i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.* g g g g ^X5Xt.w (.! ; >.'Xp a.-.q.V G HX>X< LXN.<XB U.2.3XVXC 8 8 8.M.M.M A A AXvX&X& ,X .i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.[ g g g g ^.:Xt.w (.! ; >.'Xp 1X0...( f.& H KX< LXN.<XB.2.2XVXVXC 8 8.M.M.M A A AXvX&X&X&.# ,X .i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i B g g g g ^.:XtXt.w C r ; >.' a / _.x..Xs nX>.>X< s IX6XB.2.3XVXV 8 8 8 8 8.M.M A A AXvXvX&.#.# ,X .i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i g g g g ^.:X5X5X5.r.6Xr P.'Xp.H 3 _.x...b [X>X< LXN.<X6 U.2.3XVXVXCXC 8.m.m.m 8 8 8 8.+ AXvXv.#.# ,.i.i.i.i.i.i.i",
+".i.i.i.i.i.i g g g g ^ ^.:.:.:X5.6X$.6., OXp.q 3XXX+Xm.b @ KX<.vXN.<XB U.2.2.2.2.2.2.2.2.2 5 5 5 : 5.mXk 8.+.+XvXv.].i.i.i.i.i.i",
+".i.i.i.i.i r g g ^ ^.:.: h.6.rXr.6 ..6X. OXdXs uXXX+Xm.b {.> L sXN.<XBXBXBXBXBXBXBXBXoXoXoXoXoXOXOXOXhXh : :XkXk.+.+ y.i.i.i.i.i",
+".i.i.i.i.i ^ ^ ^.:X5Xt.:.^X$.6XrX$ ..6.J.8Xd /.u.KX+Xm.h $X< L sXNXN.<.<.<XNXN.|.|.|.|.|.|.|.|.|.|.dXo.dXoXOXhXh : : ].i.i.i.i.i",
+".i.i.i.iX5 ^.U.UXtXt.w.:.6 .X$XrX$ . lX9 1 x.a.K.tX+Xm.h $ L L.v.v.v.v.v.v.v.v.v.v.v.v.v.v } }.v.v.v.v.p.|.|.d.dXOXh : ].i.i.i.i",
+".i.i.iX4X5Xt.U.U.w.w (X5.6 .X$XrX$ . W.( /.-XzX+X+ mXm.h $X< L LX<X<X<X<X<.>.>.>.>.>.>.>.}.}.}XaXaXaXa } } }.p.p.|.dXOXh.A.i.i.i",
+".i.i.i.w.w.w.w.w ( (.GXc.6 . .X$ . . W 3 /X9 l m m m _Xy J.>.>.>.>.>.>.}.} {.} [.;.;.;.;.; [ [ [ [ {.}.}.}XaXa } }.p.d.d :.i.i.i",
+".i.i.i.w ( ( ( (.!.! d.z.6 . ..N.N mXX 3 3.( l m m mXi RXx {.}.;X>X>.;.&.Y 7.Y n.&.&.&.&.&.&.&.& [ [ [ [ { {.} $Xa } }.p.d.i.i.i",
+".i.i O ( %.!.U.U ; ; d.zX$ ..N.N.N mXe.uX8 l.x.Z.Z ` =Xi.u R J H [ + Q.IX2X0 3.I n n n n n n n n n n n n + [ [ { { $Xa }.~.A.i.i",
+".i.i ; ;Xf ;.U.U > >XM.jX$.N.N k.Z.N.tX8.tX+ m 2 2 2X7 < j RXx n.Y f.h.bX0.K./.b Q.l.l.l.l.l.l.l.l._._ n n n n + [ @ { $Xa.~.i.i",
+".i.E > >X@ >.U.U.' '.j.6.x.N.Z k.Z.ZX+ =X+ m `.W.W | `X7 c j.u.h 3./.(X2./ _X0Xs.h.Y o.) v v v v.).) o.l o._ n n.E + @ @ $ $.P.i",
+".i f.'.'.).'.U.UXp BXz . m.Z k k k k.Z ` 2 2.W.W # #.X.g.$ w _ /.. W..XS./XmX0XsX2Xy.b X v v v v v v v v.) o o o n n.E + @ {.P.i",
+".iXpXpXpXpXp % % )X#Xz ..N k k k k k 2 2 2.W # # # &.X.gX7 < _.(Xm.t _./..Xm./XSX0X8 u.H.b f f - - - - v v v.).I o.Y Q.T.E @ $.i",
+".iXdXdXdXfXfXfXf 1. l m.Z k k k z z z z.W.W # # & &.X.gX7.$ WX0 _ w WXmXmXm./.(...t.. /.-.-.HXg.q.q - - - f X v.) o o.Y Q.E @.i",
+"XaXfXfX@.;X@.^.U.a l.x.N.Z k k z z z z.W.W # & &.X.X.g.o `X7 w W.t w W.. _ W...(Xm = _X0X0./.( a.q.H.q.q.q -.L f X.I.I o.Y Q.T.F",
+".; 6 6 6.} 6.^.UXz . m k k z z z z.W.W.W # # &.X.s.g.o.o.@.@X7.$.$ < W.a _ w _.( _ w.KX8XX.t.. /.H x xXd.H.q.q.q.L f X.I o.Y Q +",
+" H.V.V.V.v.V.^.U lX+ m z z z z.W.W.W # # & &.X.s.g.o.o NXl.@.`.`XAX7 w W.t <XX./.K.{ c = c wXX uX2XsXsXs.bXs.b.b.h f f X.I o.Y.T",
+" H G G.v.| G.^.U l m.Z z.W.W.W.W # # # & *.X.s.g.o.o N NXlXlX%X%.C qXA.$.=.= =X8 =.=.y.y.y.=Xe.9X0 u u uXSXS / 3 /XsXs f X.I o Q",
+" H H H H [XZ.u _.x.Z 2.W.W # # # # & *.X.s.s.g.o.o N NXl.@X%XbX* 0 q.`.`XAX:.Q =.QX:X:X:X: i.QXeXe.K.K.KXX _XXXXXi..X0Xs f X.I.Y",
+" KX>X>.^.^ R jX+X7 2.W # # # & & * *.X.s.g.g.o.o N NXlXlX%X%X*X* F F 0 0 q ~ MX7.`XqXq ~.R.RX:X7X7.= p.$.$ <.$ p.QX+X$.^.^ f X o",
+" LX<X<.^ h R jX+X7 2 | # & & * *.X.s.g.g.o.o N NXlXl.@X%XbX*X* F F 0 0 q q ~ MX7.`XqXq ~.R.R.`X7X7.= p.$.$ <.$ p.QX+X$.^.^.q f.I",
+" s L L L.~ R j <X7 | | & * *.X.X.f.g.o.o.O NXlXlXlXlX%X%X*X* F F 0 0 q q ~ ~ M.yX:.RXq ~.R.R i.y.=.= p p p.{.{ p.Q c j 3.b.q.L X",
+".<XNXNXN.| J.6X$.t ` `.X.X.s.f.g.o.o.O.O NXlXl ZX%X%XbX*X* F F 0 q ~.`XAX:X:.Q =.Q i iX: i i.QXeXe.K.K.K jXi j.K _X8.uXx.TX4.q f",
+".2.<.<.<.<XN.^.^Xi.$ `.f.g.g.o.o.O.O N NXlXlX%X%X%X%X*X* F F 0 0 ~.RXA.$.=.= =Xm =.=.=.y.= pXe././.a.a.(. . .(.(.(X9 1.- a.H.q f",
+" VX6X6X6XBXB h.^X8 <X7.o.o.o.O.O NXjXlXlXlX%X%XbX*X*X* F q !.`XAXA.y w W.t < W./ W.{ c = c w _. . X# B B D DX9 1 1 1 e.- a a.H.L",
+" b U U U U U h.^.9 w.$ N N.O N NXlXlXlXlX%X%XbXbX*X* F F ! ~XA.$.$ < W.a l w l.a l w.KXm W.tXz.[ 4X3 P d d Y.4 9 E E.8 e.- a.H.L",
+".i.2.2.2.2.2Xh } 3X8.tX7 `XjXlXlXl ZX%X%XbX*X*X* F F.7 qXA.= w W.t w WXz l WXz.a l.t l.aXzXz.j.[X3.GX1X3 P.1.n.4 9 EX- e e a a.i",
+".iXV.2.2.3XVXV.2 JXyXi.$X7XlXlXlX%X%XbXbX*X* F F F F ! ~.=.Q W.a l w W l l lXz.jXz.tXz.[.[.[XnX,.D.D.D.GX3 P.1.n B.BX-.8 e.-.c.i",
+".i.AXVXVXVXV h h.d JX8 <.$.CX*X%X* F !.R ~ q.7 0 0 q.RX: c.K... Xm.t lXzXz lXz.[.aXm.a.*.[.[Xn tX;X;X,.DX1X3 P.1.4 9 EX- e e.h.i",
+".i yXVXCXCXC h h : .9 w <.C.CXb F 0 MX: M q q 0 q q M.yXe.u.(X9./ WXz. Xz l.a.[.[. .[X; t.S.S.S.S.*X;Xn.DX1X3 4.n B.BX-.8 e.h.i",
+".i.i 8 8 8 8 h hXk.P RX8.tXA.`X* ~ M.{ =.Q.=X: ~ ~ ~.y.{.K uX9 D. ./.a. ./ WXz.[.[.[Xn.0.SX=X=X=X=.S.SX;Xn.,XM P.1.4 9X-.8.-.i.i",
+".i.i y 8 8 8 h h 8 b J RXi.$XAX* M.y =X8Xe.Q.=.R.R.R.=.Q j.(X- E.B BX#X#./ WXz.z.,.0.S.SX=X=X=X=X=X=.S.SX;.D.,X3.1.n 9X-X-.b.i.i",
+".i.i.i 8 8 8 8.5.M A.P JXi.{.$.`.y.=Xe.uX8Xi =XAXAXA = _.9 / e.e EX- BX.. Xz.j.[X,.0.SX=X= S C C C SX=.%X;Xn.,XM 4.n B.BX-.i.i.i",
+".i.i.iX&.5.M.M.M.M.M ] JX8 <.{.$.= p j R R 3XX.{.{.{ _.(XS e O E E ' Y dXM.[.[X, t.SX=X= S C C C C CX=X=.SXn.DXM 4.n B.B 1.i.i.i",
+".i.i.i y.M.M h h.M , ] JX8 w =XX =.{ j R RX2 _.{.{.{ _X9XsXw O E E T Y Y.1 4XM.D t.SX=X= C C C C C C SX=.SXn.DXM 4.n B.BXs.i.i.i",
+".i.i.i.i.#.M h h A , ] J.9 cXi.uXi c j R RXy _ < < <Xm 1.cXw O E E ' Y d PX3.DX, t.SX=X= C C CX5 C C SX=.SXn.DXM 4.n BX-.i.i.i.i",
+".i.i.i.i.iX& A A A A ].F.9XeX8 3Xi cX8 RXyXyX8.Q.Q <Xm 1.cXw O.e E ' T d dX3X1.DX;.SX=X= C C C C C CX=.z.*Xn.,XM 4.n D.i.i.i.i.i",
+".i.i.i.i.i y A A A A ].F.u.K.9 RX8 =.9XuXyXyX8 c.Q <Xm 1.cXw O.e E 9 T Y dX3.G.DX;.*.SX= S C C C C SX=.%X;Xn.,X3 4.nXs.i.i.i.i.i",
+".i.i.i.i.i.i , AXvXv ].P R.u 3Xx.9Xe.9.h.h 7.u j =.QXm 1.c ) O.e E E T Y d PX1.DX,X;.S.SX=X= S SX=X=.z.*Xn.DXMX..1 D.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.#X&X&.#.+.P J J.u.KX0.hXZ QXy.u j c.. 1.c )Xw O E E 9 T.1 dX3.G.DXnX;.S.%X=X=X=.z.%.*XnXn.,X3 4 B.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.iX ,X&X&.#Xv.k.P R.uX2XZ.E nXx u j =.. 1.H )Xw e.e E E T.4 d PX3X1.DXnX;X;.S.S.S.*X;XnXn.,XM 4.nXs.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.iX ,.#.#Xv.+ :.d.F.T @ @ @ nXZ RX8Xe./.-.H a ) e O.8 E 9 T Y.1 PX3X1.,.DXnXnXnXnXnXn.D.,XMX..nXs.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.iX ,.#.#.+XkXh.d.~ } $ [ n.TXx.u.9.(.c.q x )Xw O.8 E E 9.4.n.1 PX3XM.,.,.D.D.D.,.,XMX3 4.nXs.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.iX ,.#XvXkXh.d.p } $ { +.T.Y.h.b.b.q.q x a )Xw e.8 E E 9.4.n.1 4 PX3XMXMXMXMXMX3X. 4.nXs.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.iX ,Xv.+ :XO.| }Xa { [.E n.Y.Y.I f.q.q x a ) e e.8 E E 9 B.4.n.1.1 4 4 4 4 4 4.1 BXs.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.].+ :Xh.d.p } $ @ + n.Y o.I f.L.q.H a aXw e e.8 E E.B 9 B.4.n.n.n.n.n.n.n D.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i y ] :XO.d }Xa { @.E Q o.I X f.L.q.H a aXw e e.8X-X- E.B 9 9 B B B B DXs.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i ]Xh.d.p } $ @ +.T.Y o.I X f.L.q.H a aXw e e e.8X-X-X-X-.B.B.BX-.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.A :.d.~Xa $ @.E Q.Y o.I X f.L.q.H a a a.- e e e.8.8X-X- 1Xs.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.A.~ $ { @.E Q.Y o.I X f.q.qX4.H a a a.- e e.-.b.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.P.P $ @.T Q.Y o.I X f.L.q.q.H.H a.c.h.h.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.F +.T Q.Y o.I X f f.L.L.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i"
+};
diff --git a/arts/builder/pics/Synth_PLAY_WAV.xpm b/arts/builder/pics/Synth_PLAY_WAV.xpm
new file mode 100644
index 00000000..a7cedce2
--- /dev/null
+++ b/arts/builder/pics/Synth_PLAY_WAV.xpm
@@ -0,0 +1,317 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"64 64 246 2",
+/* colors */
+" c #27326F",
+" . c #68D14E",
+" X c #303E7B",
+" o c #2E3A79",
+" O c #3B4889",
+" + c #2A3675",
+" @ c #293474",
+" # c #51FD0F",
+" $ c #273272",
+" % c #374285",
+" & c #4FFB0D",
+" * c #4EFB0C",
+" = c #44A73B",
+" - c #323E80",
+" ; c #434E94",
+" : c #202A6B",
+" > c #3E4A8F",
+" , c #1C2667",
+" < c #4BCA27",
+" 1 c #3D4B84",
+" 2 c #52F913",
+" 3 c #375074",
+" 4 c #45538F",
+" 5 c #1F2A6D",
+" 6 c #2F3A80",
+" 7 c #323D79",
+" 8 c #1E286C",
+" 9 c #414F8B",
+" 0 c #46ED07",
+" q c #46EB07",
+" w c #4CB935",
+" e c #3B4985",
+" r c #4C5999",
+" t c #4B5998",
+" y c #1A235E",
+" u c #3A5970",
+" i c #43DC0E",
+" p c #44CF19",
+" a c #384582",
+" s c #232E74",
+" d c #445191",
+" f c #323F7C",
+" g c #656FB8",
+" h c #6F76A4",
+" j c #408551",
+" k c #5CFE19",
+" l c #538967",
+" z c #56FE13",
+" x c #374384",
+" c c #46B332",
+" v c #303B7D",
+" b c #1D2767",
+" n c #2B3778",
+" m c #5BE22F",
+" M c #44E30B",
+" N c #4AF40A",
+" B c #42508B",
+" V c #1E296B",
+" C c #51629D",
+" Z c #48F208",
+" A c #1D276A",
+" S c #50609C",
+" D c #404E89",
+" F c #47EE07",
+" G c #2A357A",
+" H c #283378",
+" J c #29366F",
+" K c #263176",
+" L c #242F74",
+" P c #465492",
+" I c #222D72",
+" U c #202B70",
+" Y c #43508F",
+" T c #41508D",
+" R c #30496F",
+" E c #3E4C8A",
+" W c #52955B",
+" Q c #2D3876",
+" ! c #45EA08",
+" ~ c #45E808",
+" ^ c #5D68AF",
+" / c #3C4F7E",
+" ( c #4C569B",
+" ) c #394685",
+" _ c #4B845E",
+" ` c #4EEC14",
+" ' c #404E8F",
+" ] c #1E2867",
+" [ c #293475",
+" { c #283474",
+" } c #253071",
+" | c #4FF90E",
+". c #475E85",
+".. c #4A756A",
+".X c #4DF90C",
+".o c #4BF50A",
+".O c #4AF509",
+".+ c #1E286A",
+".@ c #49F108",
+".# c #1C2668",
+".$ c #4BD51E",
+".% c #4D5D98",
+".& c #293478",
+".* c #4C5B97",
+".= c #45D318",
+".- c #3B4783",
+".; c #273276",
+".: c #5B67A9",
+".> c #263075",
+"., c #485793",
+".< c #222C71",
+".1 c #44538F",
+".2 c #202A6F",
+".3 c #1F2A6E",
+".4 c #42518D",
+".5 c #1D286C",
+".6 c #699088",
+".7 c #45ED07",
+".8 c #3D4B88",
+".9 c #406B63",
+".0 c #4B5B99",
+".q c #34417F",
+".w c #505BA1",
+".e c #3C498A",
+".r c #6175A1",
+".t c #4EA645",
+".y c #45D814",
+".u c #3A5E6A",
+".i c None",
+".p c #242F6F",
+".a c #4A647D",
+".s c #4DF80B",
+".d c #232D6E",
+".f c #4CF80A",
+".g c #4CF60A",
+".h c #334077",
+".j c #556795",
+".k c #1E2969",
+".l c #2D397B",
+".z c #4E5E98",
+".x c #5DB950",
+".c c #38447F",
+".v c #242F72",
+".b c #34427B",
+".n c #43528D",
+".m c #1E296C",
+".M c #1D276B",
+".N c #60EB2E",
+".B c #404E8A",
+".V c #2C377D",
+".C c #47EE08",
+".Z c #5CF620",
+".A c #1D2661",
+".S c #4C5C99",
+".D c #485895",
+".F c #28346F",
+".G c #475694",
+".H c #364480",
+".J c #48598B",
+".K c #43954A",
+".L c #33407D",
+".P c #222C69",
+".I c #303C7A",
+".U c #808CAE",
+".Y c #2E3A78",
+".T c #2B3875",
+".R c #45E609",
+".E c #2B3675",
+".W c #53FD10",
+".Q c #44BF26",
+".! c #465296",
+".~ c #25306F",
+".^ c #757EA3",
+"./ c #496C71",
+".( c #42587E",
+".) c #2E3A7B",
+"._ c #2D387A",
+".` c #48E40F",
+".' c #3C468C",
+".] c #1B2465",
+".[ c #4A5B93",
+".{ c #46C821",
+".} c #273274",
+".| c #232E70",
+"X c #171F57",
+"X. c #46558F",
+"XX c #458D52",
+"Xo c #222C6F",
+"XO c #212C6E",
+"X+ c #52CE30",
+"X@ c #313C81",
+"X# c #43538C",
+"X$ c #6C9F7F",
+"X% c #48F108",
+"X& c #1C2669",
+"X* c #47EF07",
+"X= c #4F5F9B",
+"X- c #3F4D88",
+"X; c #4B5B97",
+"X: c #44DE0E",
+"X> c #273277",
+"X, c #495995",
+"X< c #253075",
+"X1 c #475793",
+"X2 c #384A77",
+"X3 c #465592",
+"X4 c #36417F",
+"X5 c #5663A5",
+"X6 c #212C71",
+"X7 c #4CE019",
+"X8 c #41765B",
+"X9 c #405282",
+"X0 c #41626F",
+"Xq c #45E708",
+"Xw c #394785",
+"Xe c #439D42",
+"Xr c #596D97",
+"Xt c #545FA6",
+"Xy c #334475",
+"Xu c #334275",
+"Xi c #427F55",
+"Xp c #394588",
+"Xa c #263172",
+"Xs c #38467D",
+"Xd c #354184",
+"Xf c #343F83",
+"Xg c #333F82",
+"Xh c #202B6C",
+"Xj c #49F408",
+"Xk c #1F296B",
+"Xl c #49F208",
+"Xz c #567E79",
+"Xx c #2F4074",
+"Xc c #51609C",
+"Xv c #1D2769",
+"Xb c #48F007",
+"Xn c #4A5A95",
+"Xm c #4C7D65",
+"XM c #475692",
+"XN c #232D72",
+"XB c #212B70",
+"XV c #1F296E",
+"XC c #1E296D",
+"XZ c #2D3A75",
+"XA c #48DD14",
+"XS c #3E517F",
+/* pixels */
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.iXa.; H H H K L s.<.2 V b.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.E fXpXdXf 6.V G HX>X< LXN.<X6 U.2XV.A y.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i O ; >.'XpXdXf 6.V G HX>X< LXN.<X6 U.2.2XVXV 8 y.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.iX4.w.w ( ; >.'XpXdX@ 6.V G HX>X< LXN.<X6 U.2.2XVXC 8 8 8X& y.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.iX5X5.w (.! ; >.'XpXfX@ 6.V G HX>X< LXN.<XB U.2.3XVXC 8 8 8.5.M.#.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i r ^ ^Xt.w (.! ; >.'XpXfX@ 6.V G H KX< LXN.<XB U.2XVXVXC 8 8 8.M.M.MX& y.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i g g ^.:Xt.w (.! ; >.' %XfX@ 6.V G H KX< LXN.<XB.2.2XVXVXC 8 8 8.M.M.M A A ,.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i B g g g ^X5Xt.w (.! ; >Xp %XfX@ 6.V G H.>X< s IX6XB.2.2XVXV 8 8 8.5.M.M A A A A.#X .i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.[ g g g ^.:Xt.w (.! ; >.'XpXd - 6.V G HX>.> L s IX6XB.2.3XVXC 8 8 8.M.M.M A A AXvX& ,X .i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.* g g g g ^X5Xt.w (.! ; >.'Xp a.-.q.V G HX>X< LXN.<XB U.2.3XVXC 8 8 8.M.M.M A A AXvX&X& ,X .i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.[ g g g g ^.:Xt.w (.! ; >.'Xp 1X0...( f.& H KX< LXN.<XB.2.2XVXVXC 8 8.M.M.M A A AXvX&X&X&.# ,X .i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i B g g g g ^.:XtXt.w C r ; >.' a / _.x..Xs nX>.>X< s IX6XB.2.3XVXV 8 8 8 8 8.M.M A A AXvXvX&.#.# ,X .i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i g g g g ^.:X5X5X5.r.6Xr P.'Xp.H 3 _.x...b [X>X< LXN.<X6 U.2.3XVXVXCXC 8.m.m.m 8 8 8 8.+ AXvXv.#.# ,.i.i.i.i.i.i.i",
+".i.i.i.i.i.i g g g g ^ ^.:.:.:X5.6X$.6., OXp.q 3XXX+Xm.b @ KX<.vXN.<XB U.2.2.2.2.2.2.2.2.2 5 5 5 : 5.mXk 8.+.+XvXv.].i.i.i.i.i.i",
+".i.i.i.i.i r g g ^ ^.:.: h.6.rXr.6 ..6X. OXdXs uXXX+Xm.b {.> L sXN.<XBXBXBXBXBXBXBXBXoXoXoXoXoXOXOXOXhXh : :XkXk.+.+ y.i.i.i.i.i",
+".i.i.i.i.i ^ ^ ^.:X5Xt.:.^X$.6XrX$ ..6.J.8Xd /.u.KX+Xm.h $X< L sXNXN.<.<.<XNXN.|.|.|.|.|.|.|.|.|.|.dXo.dXoXOXhXh : : ].i.i.i.i.i",
+".i.i.i.iX5 ^.U.UXtXt.w.:.6 .X$XrX$ . lX9 1 x.a.K.tX+Xm.h $ L L.v.v.v.v.v.v.v.v.v.v.v.v.v.v } }.v.v.v.v.p.|.|.d.dXOXh : ].i.i.i.i",
+".i.i.iX4X5Xt.U.U.w.w (X5.6 .X$XrX$ . W.( /.-XzX+X+ mXm.h $X< L LX<X<X<X<X<.>.>.>.>.>.>.>.}.}.}XaXaXaXa } } }.p.p.|.dXOXh.A.i.i.i",
+".i.i.i.w.w.w.w.w ( (.GXc.6 . .X$ . . W 3 /X9 l m m m _Xy J.>.>.>.>.>.>.}.} {.} [.;.;.;.;.; [ [ [ [ {.}.}.}XaXa } }.p.d.d :.i.i.i",
+".i.i.i.w ( ( ( (.!.! d.z.6 . ..N.N mXX 3 3.( l m m mXi RXx {.}.;X>X>.;.&.Y 7.Y n.&.&.&.&.&.&.&.& [ [ [ [ { {.} $Xa } }.p.d.i.i.i",
+".i.i O ( %.!.U.U ; ; d.zX$ ..N.N.N mXe.uX8 l.x.Z.Z ` =Xi.u R J H [ + Q.IX2X0 3.I n n n n n n n n n n n n + [ [ { { $Xa }.~.A.i.i",
+".i.i ; ;Xf ;.U.U > >XM.jX$.N.N k.Z.N.tX8.tX+ m 2 2 2X7 < j RXx n.Y f.h.bX0.K./.b Q.l.l.l.l.l.l.l.l._._ n n n n + [ @ { $Xa.~.i.i",
+".i.E > >X@ >.U.U.' '.j.6.x.N.Z k.Z.ZX+ =X+ m `.W.W | `X7 c j.u.h 3./.(X2./ _X0Xs.h.Y o.) v v v v.).) o.l o._ n n.E + @ @ $ $.P.i",
+".i f.'.'.).'.U.UXp BXz . m.Z k k k k.Z ` 2 2.W.W # #.X.g.$ w _ /.. W..XS./XmX0XsX2Xy.b X v v v v v v v v.) o o o n n.E + @ {.P.i",
+".iXpXpXpXpXp % % )X#Xz ..N k k k k k 2 2 2.W # # # &.X.gX7 < _.(Xm.t _./..Xm./XSX0X8 u.H.b f f - - - - v v v.).I o.Y Q.T.E @ $.i",
+".iXdXdXdXfXfXfXf 1. l m.Z k k k z z z z.W.W # # & &.X.gX7.$ WX0 _ w WXmXmXm./.(...t.. /.-.-.HXg.q.q - - - f X v.) o o.Y Q.E @.i",
+"XaXfXfX@.;X@.^.U.a l.x.N.Z k k z z z z.W.W # & &.X.X.g.o `X7 w W.t w W.. _ W...(Xm = _X0X0./.( a.q.H.q.q.q -.L f X.I.I o.Y Q.T.F",
+".; 6 6 6.} 6.^.UXz . m k k z z z z.W.W.W # # &.X.s.g.o.o.@.@X7.$.$ < W.a _ w _.( _ w.KX8XX.t.. /.H x xXd.H.q.q.q.L f X.I o.Y Q +",
+" H.V.V.V.v.V.^.U lX+ m z z z z.W.W.W # # & &.X.s.g.o.o NXl.@.`.`XAX7 w W.t <XX./.K.{ c = c wXX uX2XsXsXs.bXs.b.b.h f f X.I o.Y.T",
+" H G G.v.| G.^.U l m.Z z.W.W.W.W # # # & *.X.s.g.o.o N NXlXlX%X%.C qXA.$.=.= =X8 =.=.y.y.y.=Xe.9X0 u u uXSXS / 3 /XsXs f X.I o Q",
+" H H H H [XZ.u _.x.Z 2.W.W # # # # & *.X.s.s.g.o.o N NXl.@X%XbX* 0 q.`.`XAX:.Q =.QX:X:X:X: i.QXeXe.K.K.KXX _XXXXXi..X0Xs f X.I.Y",
+" KX>X>.^.^ R jX+X7 2.W # # # & & * *.X.s.g.g.o.o N NXlXlX%X%X*X* F F 0 0 q ~ MX7.`XqXq ~.R.RX:X7X7.= p.$.$ <.$ p.QX+X$.^.^ f X o",
+" LX<X<.^ h R jX+X7 2 | # & & * *.X.s.g.g.o.o N NXlXl.@X%XbX*X* F F 0 0 q q ~ MX7.`XqXq ~.R.R.`X7X7.= p.$.$ <.$ p.QX+X$.^.^.q f.I",
+" s L L L.~ R j <X7 | | & * *.X.X.f.g.o.o.O NXlXlXlXlX%X%X*X* F F 0 0 q q ~ ~ M.yX:.RXq ~.R.R i.y.=.= p p p.{.{ p.Q c j 3.b.q.L X",
+".<XNXNXN.| J.6X$.t ` `.X.X.s.f.g.o.o.O.O NXlXl ZX%X%XbX*X* F F 0 q ~.`XAX:X:.Q =.Q i iX: i i.QXeXe.K.K.K jXi j.K _X8.uXx.TX4.q f",
+".2.<.<.<.<XN.^.^Xi.$ `.f.g.g.o.o.O.O N NXlXlX%X%X%X%X*X* F F 0 0 ~.RXA.$.=.= =Xm =.=.=.y.= pXe././.a.a.(. . .(.(.(X9 1.- a.H.q f",
+" VX6X6X6XBXB h.^X8 <X7.o.o.o.O.O NXjXlXlXlX%X%XbX*X*X* F q !.`XAXA.y w W.t < W./ W.{ c = c w _. . X# B B D DX9 1 1 1 e.- a a.H.L",
+" b U U U U U h.^.9 w.$ N N.O N NXlXlXlXlX%X%XbXbX*X* F F ! ~XA.$.$ < W.a l w l.a l w.KXm W.tXz.[ 4X3 P d d Y.4 9 E E.8 e.- a.H.L",
+".i.2.2.2.2.2Xh } 3X8.tX7 `XjXlXlXl ZX%X%XbX*X*X* F F.7 qXA.= w W.t w WXz l WXz.a l.t l.aXzXz.j.[X3.GX1X3 P.1.n.4 9 EX- e e a a.i",
+".iXV.2.2.3XVXV.2 JXyXi.$X7XlXlXlX%X%XbXbX*X* F F F F ! ~.=.Q W.a l w W l l lXz.jXz.tXz.[.[.[XnX,.D.D.D.GX3 P.1.n B.BX-.8 e.-.c.i",
+".i.AXVXVXVXV h h.d JX8 <.$.CX*X%X* F !.R ~ q.7 0 0 q.RX: c.K... Xm.t lXzXz lXz.[.aXm.a.*.[.[Xn tX;X;X,.DX1X3 P.1.4 9 EX- e e.h.i",
+".i yXVXCXCXC h h : .9 w <.C.CXb F 0 MX: M q q 0 q q M.yXe.u.(X9./ WXz. Xz l.a.[.[. .[X; t.S.S.S.S.*X;Xn.DX1X3 4.n B.BX-.8 e.h.i",
+".i.i 8 8 8 8 h hXk.P RX8.tXA.`X* ~ M.{ =.Q.=X: ~ ~ ~.y.{.K uX9 D. ./.a. ./ WXz.[.[.[Xn.0.SX=X=X=X=.S.SX;Xn.,XM P.1.4 9X-.8.-.i.i",
+".i.i y 8 8 8 h h 8 b J RXi.$XAX* M.y =X8Xe.Q.=.R.R.R.=.Q j.(X- E.B BX#X#./ WXz.z.,.0.S.SX=X=X=X=X=X=.S.SX;.D.,X3.1.n 9X-X-.b.i.i",
+".i.i.i 8 8 8 8.5.M A.P JXi.{.$.`.y.=Xe.uX8Xi =XAXAXA = _.9 / e.e EX- BX.. Xz.j.[X,.0.SX=X= S C C C SX=.%X;Xn.,XM 4.n B.BX-.i.i.i",
+".i.i.iX&.5.M.M.M.M.M ] JX8 <.{.$.= p j R R 3XX.{.{.{ _.(XS e O E E ' Y dXM.[.[X, t.SX=X= S C C C C CX=X=.SXn.DXM 4.n B.B 1.i.i.i",
+".i.i.i y.M.M h h.M , ] JX8 w =XX =.{ j R RX2 _.{.{.{ _X9XsXw O E E T Y Y.1 4XM.D t.SX=X= C C C C C C SX=.SXn.DXM 4.n B.BXs.i.i.i",
+".i.i.i.i.#.M h h A , ] J.9 cXi.uXi c j R RXy _ < < <Xm 1.cXw O E E ' Y d PX3.DX, t.SX=X= C C CX5 C C SX=.SXn.DXM 4.n BX-.i.i.i.i",
+".i.i.i.i.iX& A A A A ].F.9XeX8 3Xi cX8 RXyXyX8.Q.Q <Xm 1.cXw O.e E ' T d dX3X1.DX;.SX=X= C C C C C CX=.z.*Xn.,XM 4.n D.i.i.i.i.i",
+".i.i.i.i.i y A A A A ].F.u.K.9 RX8 =.9XuXyXyX8 c.Q <Xm 1.cXw O.e E 9 T Y dX3.G.DX;.*.SX= S C C C C SX=.%X;Xn.,X3 4.nXs.i.i.i.i.i",
+".i.i.i.i.i.i , AXvXv ].P R.u 3Xx.9Xe.9.h.h 7.u j =.QXm 1.c ) O.e E E T Y d PX1.DX,X;.S.SX=X= S SX=X=.z.*Xn.DXMX..1 D.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.#X&X&.#.+.P J J.u.KX0.hXZ QXy.u j c.. 1.c )Xw O E E 9 T.1 dX3.G.DXnX;.S.%X=X=X=.z.%.*XnXn.,X3 4 B.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.iX ,X&X&.#Xv.k.P R.uX2XZ.E nXx u j =.. 1.H )Xw e.e E E T.4 d PX3X1.DXnX;X;.S.S.S.*X;XnXn.,XM 4.nXs.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.iX ,.#.#Xv.+ :.d.F.T @ @ @ nXZ RX8Xe./.-.H a ) e O.8 E 9 T Y.1 PX3X1.,.DXnXnXnXnXnXn.D.,XMX..nXs.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.iX ,.#.#.+XkXh.d.~ } $ [ n.TXx.u.9.(.c.q x )Xw O.8 E E 9.4.n.1 PX3XM.,.,.D.D.D.,.,XMX3 4.nXs.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.iX ,.#XvXkXh.d.p } $ { +.T.Y.h.b.b.q.q x a )Xw e.8 E E 9.4.n.1 4 PX3XMXMXMXMXMX3X. 4.nXs.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.iX ,Xv.+ :XO.| }Xa { [.E n.Y.Y.I f.q.q x a ) e e.8 E E 9 B.4.n.1.1 4 4 4 4 4 4.1 BXs.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.].+ :Xh.d.p } $ @ + n.Y o.I f.L.q.H a aXw e e.8 E E.B 9 B.4.n.n.n.n.n.n.n D.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i y ] :XO.d }Xa { @.E Q o.I X f.L.q.H a aXw e e.8X-X- E.B 9 9 B B B B DXs.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i ]Xh.d.p } $ @ +.T.Y o.I X f.L.q.H a aXw e e e.8X-X-X-X-.B.B.BX-.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.A :.d.~Xa $ @.E Q.Y o.I X f.L.q.H a a a.- e e e.8.8X-X- 1Xs.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.A.~ $ { @.E Q.Y o.I X f.q.qX4.H a a a.- e e.-.b.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.P.P $ @.T Q.Y o.I X f.L.q.q.H.H a.c.h.h.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i",
+".i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.F +.T Q.Y o.I X f f.L.L.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i.i"
+};
diff --git a/arts/builder/pics/Synth_PSCALE.xpm b/arts/builder/pics/Synth_PSCALE.xpm
new file mode 100644
index 00000000..b40b95d5
--- /dev/null
+++ b/arts/builder/pics/Synth_PSCALE.xpm
@@ -0,0 +1,299 @@
+/* XPM */
+static char * PSCALE_xpm[] = {
+"64 64 232 2",
+" c None",
+". c #161E54",
+"+ c #469224",
+"@ c #4A5A94",
+"# c #56CA0C",
+"$ c #2E3E7C",
+"% c #66E614",
+"& c #4EAE1C",
+"* c #428A34",
+"= c #3E4E84",
+"- c #5EDA0C",
+"; c #8692B4",
+"> c #222E64",
+", c #364684",
+"' c #66CE24",
+") c #6E769C",
+"! c #3A7244",
+"~ c #4AA21C",
+"{ c #6EF614",
+"] c #7E86AC",
+"^ c #52629C",
+"/ c #52BE0C",
+"( c #4A529C",
+"_ c #2A3674",
+": c #5AD214",
+"< c #7AFA1C",
+"[ c #469A24",
+"} c #363E84",
+"| c #46568C",
+"1 c #3E4684",
+"2 c #6EDA2C",
+"3 c #969ECC",
+"4 c #767EA4",
+"5 c #1A2664",
+"6 c #424E94",
+"7 c #525A94",
+"8 c #62E20C",
+"9 c #222E74",
+"0 c #5ECA1C",
+"a c #52B61C",
+"b c #8E9ABC",
+"c c #62BE34",
+"d c #66D624",
+"e c #6A76BC",
+"f c #4EAA1C",
+"g c #5A62AC",
+"h c #56C60C",
+"i c #323A84",
+"j c #5ED21C",
+"k c #6AEE14",
+"l c #3A4A7C",
+"m c #72FE14",
+"n c #868EB4",
+"o c #2E3A74",
+"p c #82FA24",
+"q c #4E9A34",
+"r c #36427C",
+"s c #222A64",
+"t c #569254",
+"u c #4E5E94",
+"v c #32427C",
+"w c #528E54",
+"x c #464E8C",
+"y c #66DA1C",
+"z c #8E92C4",
+"A c #5E6AB4",
+"B c #52A62C",
+"C c #3E4A84",
+"D c #2A3274",
+"E c #36428C",
+"F c #4A962C",
+"G c #428E34",
+"H c #62DE0C",
+"I c #767AA4",
+"J c #4EA61C",
+"K c #52669C",
+"L c #5AC21C",
+"M c #4A5694",
+"N c #5ED60C",
+"O c #425294",
+"P c #5ECE1C",
+"Q c #52BA0C",
+"R c #6672BC",
+"S c #162254",
+"T c #469234",
+"U c #4E5AA4",
+"V c #5ACE0C",
+"W c #323E84",
+"X c #66EA14",
+"Y c #4EB21C",
+"Z c #42528C",
+"` c #62DA1C",
+" . c #26326C",
+".. c #3A468C",
+"+. c #6ED22C",
+"@. c #727AA4",
+"#. c #76FA1C",
+"$. c #7E8AB4",
+"%. c #56C20C",
+"&. c #2E3674",
+"*. c #7AFE1C",
+"=. c #4A9E24",
+"-. c #7E82B4",
+";. c #1E2A6C",
+">. c #525EA4",
+",. c #66E21C",
+"'. c #263274",
+"). c #6ED62C",
+"!. c #3A427C",
+"~. c #666EB4",
+"{. c #3E4A94",
+"]. c #4E5694",
+"^. c #4A922C",
+"/. c #4E5A9C",
+"(. c #5ACA1C",
+"_. c #323E74",
+":. c #424E8C",
+"<. c #62DA0C",
+"[. c #8A96BC",
+"}. c #262E6C",
+"|. c #3A467C",
+"1. c #66D224",
+"2. c #7276A4",
+"3. c #4AA22C",
+"4. c #6EFA14",
+"5. c #5662AC",
+"6. c #5ED20C",
+"7. c #9AA2CC",
+"8. c #7A82AC",
+"9. c #56AA34",
+"0. c #6E7ABC",
+"a. c #4E9E2C",
+"b. c #5A66AC",
+"c. c #6AF214",
+"d. c #626EB4",
+"e. c #82FE1C",
+"f. c #8E96BC",
+"g. c #46528C",
+"h. c #6AE624",
+"i. c #56BE1C",
+"j. c #5AC61C",
+"k. c #428E3C",
+"l. c #52BA1C",
+"m. c #1A2264",
+"n. c #525AA4",
+"o. c #3A4A8C",
+"p. c #2E3A84",
+"q. c #222A74",
+"r. c #4EA62C",
+"s. c #56C21C",
+"t. c #468A2C",
+"u. c #8A92BC",
+"v. c #5662A4",
+"w. c #7EFA24",
+"x. c #4A9A2C",
+"y. c #1E2664",
+"z. c #66E214",
+"A. c #62CA24",
+"B. c #929AC4",
+"C. c #6AD624",
+"D. c #62D21C",
+"E. c #76FE14",
+"F. c #8A8EBC",
+"G. c #468E2C",
+"H. c #465294",
+"I. c #62CE24",
+"J. c #1A225C",
+"K. c #529E34",
+"L. c #5ACA0C",
+"M. c #52AE1C",
+"N. c #72F614",
+"O. c #72DA2C",
+"P. c #9A9ECC",
+"Q. c #464E94",
+"R. c #262E74",
+"S. c #56B61C",
+"T. c #626AB4",
+"U. c #3A428C",
+"V. c #6AEA14",
+"W. c #52B21C",
+"X. c #828AB4",
+"Y. c #7EFE1C",
+"Z. c #565EA4",
+"`. c #424A94",
+" + c #4EA22C",
+".+ c #5E66AC",
+"++ c #6EF214",
+"@+ c #56BA1C",
+"#+ c #46922C",
+"$+ c #4A5A9C",
+"%+ c #3E4E8C",
+"&+ c #222E6C",
+"*+ c #6E76A4",
+"=+ c #7E86B4",
+"-+ c #2A367C",
+";+ c #465694",
+">+ c #3E468C",
+",+ c #767EAC",
+"'+ c #62E214",
+")+ c #5ECA24",
+"!+ c #8E9AC4",
+"~+ c #56C614",
+"{+ c #3A4A84",
+"]+ c #868EBC",
+"^+ c #2E3A7C",
+"/+ c #364284",
+"(+ c #222A6C",
+"_+ c #4E5E9C",
+":+ c #324284",
+"<+ c #3E4A8C",
+"[+ c #2A327C",
+"}+ c #5266A4",
+"|+ c #4A569C",
+"1+ c #5ED614",
+"2+ c #2E367C",
+"3+ c #4A9E2C",
+"4+ c #26327C",
+"5+ c #3A4284",
+"6+ c #4E569C",
+"7+ c #323E7C",
+"8+ c #62DA14",
+"9+ c #3A4684",
+"0+ c #66D22C",
+"a+ c #5ED214",
+"b+ c #6E7AC4",
+"c+ c #82FE24",
+"d+ c #8E96C4",
+"e+ c #1E266C",
+"f+ c #6AD62C",
+"g+ c #62D224",
+"h+ c #76FE1C",
+"i+ c #46529C",
+" '.D D [+[+'.R.9 q.(+;.y. ",
+" s o /+/+i p.^+-+4+R.'.R.9 9 (+9 m.. . . ",
+" &.C `.>+U./+W p.2+2+-+4+'.R.q.9 ;.;.;.e+m.m.. . ",
+" o /.6+M Q.`...E } i p.^+-+4+R.9 9 9 q.9 9 q.;.;.5 m.. . . . ",
+" 7 Z.>.6+( 6 {...E :+i 2+-+-+-+[+'.R.9 q.;.;.;.;.q.;.;.e+e+m.. . ",
+" ].T..+5.n.6+H.6 `.>+E } W p.2+[+'.'.R.9 &+9 q.q.q.e+;.;.e+;.e+;.m.. . . ",
+" d.d.A g Z.U |+Q.`.>+..E W i p.2+[+4+4+R.R.q.&+;.;.q.e+;.;.e+;.e+;.e+5 m.. . ",
+" x e R T.b.5.>.U |+i+{.o.U.} i $ ^+-+-+D '.'.9 9 q.9 q.;.q.e+e+;.e+;.e+e+e+e+m.. . . ",
+" !.*+R R T.b.v.n.|+( Q.`.>+../+i p.^+-+4+4+R.9 R.9 q.&+q.;.;.;.e+;.e+e+5 e+e+5 e+m.. . . ",
+" ].0.R R T.b.g >./.|+H.6 <+..E } W 2+-+[+[+R.4+R.9 (+q.;.;.;.;.;.;.e+;.;.;.e+;.;.e+;.m.. . . ",
+" !.*+e ~.d.A b.Z.n.6+i+6 {.>+U.E W W p.2+_ [+R.R.9 9 &+q.9 q.;.;.e+q.e+e+e+;.5 e+;.5 e+5 m.. . . ",
+" x e R R ~.A b.Z.>.U |+i+6 {.../+W W ^+2+-+[+'.'.'.R.9 ;.q.;.;.e+e+;.e+;.;.e+e+e+5 e+e+;.e+e+m.. . . ",
+" R e ~.d..+b.5.>.U |+H.Q.`.<+..E W i ^+^+-+-+4+9 9 9 9 9 ;.;.;.q.;.;.e+e+;.;.;.;.e+;.e+5 e+5 5 m.. . ",
+" R R ~.7.P.b.g >.U |+( Q.%+<+U.E } i p.p.-+-+'.R.R.R.q.q.;.q.q.q.;.q.;.;.;.;.;.;.;.;.;.;.;.;.e+e+;.m.. . ",
+" C ~.~.T.3 3 Z.>.n.6+i+Q.`.>+..U./+} i 2+-+[+[+'.'.9 R.}.9 9 9 9 9 ;.9 9 9 9 ;.q.q.9 q.9 9 q.9 ;.;.;.;.5 m.. ",
+" T.T.b.b.5.>.>.n.|+( i+6 `.o.../+} W ^+^+-+-+'.4+R.R.9 9 q.q.;.&+q.9 9 9 9 9 9 9 9 9 &+(+(+&+(+9 (+q.(+;.. . ",
+" ].b.g 5.5.Z.U U 6+;+Q.6 {.>+..U.W W i 2+2+-+[+'.4+'.9 9 R.9 '.9 '.R.9 9 9 R.'.'.'.}.'.'.'.9 '.&+9 &+&+q.&+;.5 . ",
+" } g Z.^ >+n.d+d+|+i+Q.6 {.{...U.W W w t ^+-+'.'.'.R.9 '.R.'.'.R.R.9 '.'.'.'.'.R.'.'.'.'.'.R.'.R.'. .R.9 }.9 (+(+J.. ",
+" x >.n.U ..|+z u.i+6 `.{...U.E E } $ +.).-+-+[+4+'.'.R.'.'.'.'.'.D '.D '.D '._ '.'.-+'.'._ '._ '.'.'. .'.}.'. .&+(+5 ",
+" U 6+6+6+5+( ; ]+6 `.{.>+U.E } W p.i +.w.).B '.D 4+D '.'.'.'.4+4+'.'.4+_ _ -+4+-+_ D -+_ '._ '.D D _ D '.'.R.R.&+ .J. ",
+" &.( i+i+Q.} Q.]+X.{.>+U...5+} i W ^+^++.e.c+C.'.'.4+-+D [+[+_ _ -+-+_ -+-+_ ^+&.-+^+_ -+^+-+o -+-+_ -+_ _ _ '.D }.D &+5 ",
+" Q.O 6 6 6 %+{.>+>+.., U.} W W W ^+2+-+2 c+e.< 1.B '.'.-+'._ [+-+_ 2+2+2+-+2+-+^+^+-+^+^+-+^+-+o ^+_ _ _ _ _ _ _ _ .> . ",
+" y.`.`.{.{.>+>+>+>+U.U./+} W W i 2+p.c C.w.Y.Y.Y.Y.d -+'.-+[+-+_ -+^+^+^+^+^+^+^+^+$ ^+^+7+^+$ ^+^+^+^+^+2+-+_ _ _ _ D _ .y. ",
+" o ....>+U...p...=+=+} W W W ^+2+^+-++.e.e.w.C.d #.N.D. +-+o ^+-+^+^+-+^+^+^+^+^+^+i $ $ ^+$ i $ ^+$ ^+^+^+o ^+_ -+_ _ D > . ",
+" } 5+E U.E } -+:+=+=+W i i 2+p.2+-+_ 0+Y.Y.d [+4+1.h+h+D.-+-+^+^+^+$ ^+$ $ $ W 7+$ W 7+v 7+7+7+7+7+7+7+$ 7+^+i o &._ _ &.D . ",
+" } E } :+} W [+i -.8.p.^+-+^+-+[+D 4+' Y.*.A._ -+r.D.#.{ P ~ ^+^+^+^+$ 7+W W 7+W /+} } W } :+W :+7+W 7+7+$ 7+$ ^+$ ^+o _ _ D ",
+"'.W W i W W i D ^+,+8.p.2+-+-+-+4+D '.I.*.*.)+_ -+-+-+j m m P p.^+W i W 7+} :+r W v :+/+/+/+r /+/+r :+/+} 7+7+7+^+7+^+o o o o _ ",
+"'.^+p.p.2+p.^+^+p.^+2+-+-+[+D '.D '.[+1.#.1.B [+_ -+-+r.h.{ c.(.3+7+v W W :+/+/+, /+, , , /+, /+/+/+/+r :+r /+r v 7+7+7+^+o _ _ ",
+"[+2+2+p.2+-+-+-+[+[+[+-+4+4+4+'.R. +d N.D.-+_ -+2+^+^+^+p.,.{ { j.W :+/+/+/+/+/+9+5+5+, 9+9+9+9+, 9+, 9+/+/+:+} 7+7+7+$ ^+o o &.",
+"'.-+-+[+-+-+[+&+-+4 4 '.'.'.R.9 9 A.E.h+P '.-+2+-+-+2+p.W +0 k k j.x./+/+/+, 9+, , o.9+9+...., 9+9+.., , , !., /+/+7+7+7+7+^+^+",
+"D [+-+'.[+-+[+q.4+I I 4+R.'.R.R.9 )+h+h+)+[+D ^+o ^+^+^+i $ :+j.c.k j., , , , ..o.9+>+o.{+{+<+o.o.9+9+9+9+, /+, r /+r v 7+$ ^+o ",
+"R.'.R.4+'.R.'.(+4+@.@.R.9 R.9 9 R.0 m m )+_ -+-+-+^+^+i $ W W =.y k % L x.....o.<+o.o.<+<+<+o.<+C o.<+o.{+o.{+9+, , /+} v 7+7+^+",
+"R.4+R.R.4+9 '.(+'.@.@.R.q.9 9 9 9 P { P 3+-+_ ^+^+^+$ W 7+/+/+/+/+d X X i.C o.o.<+%+%+%+%+:.%+%+%+%+= <+C 1 {+9+9+, , r r v v 7+",
+"9 9 9 9 9 R.9 9 9 R.9 9 9 &+9 =.0 { 0 '._ _ 2+-+p.^+$ 7+/+W /+:+, =.%.z.z.i.F <+%+<+<+%+%+%+:.%+:.%+`.%+<+%+<+o.9+9+9+5+r } } ^+",
+"q.R.R.9 9 q.q.q.9 q.9 (+q.(+9 L { { L 4+-+-+^+^+^+i W 7+r :+/+/+, ....i.8 8 @+%+:.Z 6 6 Z Z 6 :.:.Z %+%+<+<+= <+C 9+{+, , r r 7+",
+"(+9 9 (+q.}.9 ;.;.@.*+9 q.q.;.s.{ { s.D _ -+-+-+$ $ 7+W :+/+9+9+....{+x.D.z.<.a F 6 Z Z O O g.Z O :.Z Z Z = `.= o.{+9+9+9+r r 7+",
+"e+;.9 &+;.q.(+;.(+@.*+;.;.q.9 j.{ { s.4+_ o ^+^+i W :+W /+/+, 9+{+o.o.%+o.j 8 H l.O H.H.H.;+;+H.;+O O Z O Z %+:.<+o.{+9+9+, , r ",
+";.q.q.;.q.;.;.e+;.2.2.;.;.;.q.` X L [ _ -+-+^+^+$ $ 7+/+/+, E o...<+<+%+:.F @+1+6.W.#+| ;+H.H.;+H.;+H.O g.Z Z %+:.= <+{+{+9+!.7+",
+" ;.q.;.q.q.(+e+;.@.@.q.(+[ d k ~+R.4+_ -+-+^+^+i W 7+/+/+9+, o.9+<+<+%+%+6 Z a <.- W.;+@ @ ;+@ ;+;+M ;+H.H.g.Z :.%+<+{+9+, 9+ ",
+" ;.;.q.;.;.;.;.;.e+e+;.;.l.k V.@+'.D 4+_ ^+^+i 7+7+/+:+/+, ..1 o.%+%+O O Z O F a a+V Y G ;+@ ;+M @ ;+;+| O Z :.:.:.%+C C 9+, ",
+" . y.;.;.e+q.e+;.;.e+e+;.Q k X @+'._ -+_ -+^+$ W W r , /+9+o.<+o.<+%+:.O H.O ;+;+a N 6.W.$+/.@ @ @ @ ;+M ;+;+g.Z :.%+%+C 9+_. ",
+" m.;.;.;.;.e+e+;.2.*+;.;.l.% X l.'.'.D -+^+^+i 7+v /+/+.., 9+o.<+%+:.O O Z ;+;+M G.M.V L.& G._+/./.@ $+@ ;+| ;+Z Z :.C C |.> ",
+" m.e+;.;.;.5 ;.2.*+e+e+j H l.+ '.-+_ _ ^+^+$ W W /+/+..o.{+<+%+%+:.:.H.H.;+;+@ @ $+& V V f _+u /.u @ @ ;+;+H.Z :.:.%+{+1 ",
+" m.;.e+e+e+e+e+) 2.! a+% l.&+9 R.'.-+-+o $ p.7+v :+, /+9+..%+<+%+O O | ;+;+@ $+_+_+G.M.h h f G _+$+$+$+M | ;+g.Z Z = = . ",
+" m.;.;.e+5 ;.) ) k.8 % W.}.'.'.'.-+-+2+7+i W } r 9+, {+9+<+<+%+Z O ;+H.;+;+@ @ _+_+^ & # L.f ^ u /.u @ ;+;+;+Z :.:.:. ",
+" e+5 e+e+e+e+e+e+G.8 H Y 9 R.'._ _ -+o ^+$ 7+v :+/+9+..o.%+%+:.:.Z H.;+;+@ $+_+_+_+_+G.i.h %.J t._+$+@ @ ;+Z Z Z = !. ",
+" . 5 ;.;.5 5 e+5 * '+8 Y &+R.'.D _ -+^+^+7+W } /+/+9+9+..<+<+:.6 Z ;+;+|+;+@ /.u _+^ ^ ^ l.%.%.J u /.@ @ M H.g.:.= 1 ",
+" e+5 5 e+e+*+*+* H H & &+'.'._ -+o -+7+^+7+v :+, /+{+{+%+%+%+Z O H.;+;+@ $+u _+^ ^ }+}+G.J / Q ~ x./.M ;+| Z Z l ",
+" m.e+e+e+2.2.* 8+<.~+(+&+R.'.'.o _ ^+i 7+r /+, /+..9+<+%+:.Z Z g.;+;+M $+$+_+_+>.^ K K ^ 3+%./ / @ @ ;+g.g.:. ",
+" m.;.5 5 *+) *+*+;.&+@.@.4 4 _ -+8.8.] 8.W :+=+X.X.X.o.<+n n ]+; ;+M [.[.f.[._+^ B.B.!+!+^ _+f.[.[.u.g.| Z &. ",
+" S e+e+*+) ) 2.;.(+@.@.I ,+D _ 8.4 ] ] 7+r ] $.X.X.<+<+n n ]+u.;+;+u.[.f.[.u _+b B.b d+_+_+f.f.[.[.g.Z :. ",
+" e+5 e+5 e+;.e+&+q.9 '.'._ _ _ ^+^+$ 7+W /+r , 9+{+o.<+:.Z Z Z H.M @ @ /.$+$+u _+_+u u u @ @ ;+g.| Z ",
+" . . e+5 m.y.e+(+;.;.;.(+_ -+ .'.D D 7+v o &.o ^+C o.7+7+7+v Z | v r , |./.@ 9+9+{+9+$+/.r !.;+Z x |. ",
+" J.5 e+5 5 ;.;.(+&+}.'.'._ -+_ ^+$ 7+W W , /+9+{+C {+<+:.:.O O H.;+;+;+@ @ @ /.@ @ @ @ @ ;+| g._ ",
+" . J.e+5 ;.;.(+9 &+'.'.D 2+_ o $ 7+v 7+/+, /+, <+C <+%+:.Z Z Z g.;+| ;+;+@ M @ ;+;+| ;+g.Z |. ",
+" . 5 e+y.(+(+&+R.}.'._ _ 2+o i ^+7+:+v /+, 9+9+o.= <+%+:.Z g.O g.H.;+| ;+;+| | | H.| Z &. ",
+" . S 5 ;.(+&+9 }. .'._ -+_ o ^+i 7+7+/+, , , ..C C <+`.%+Z Z Z g.H.g.| H.;+H.| Z Z |. ",
+" y.;.;.;.&+9 R.'._ _ ^+o o 7+7+r r r /+9+, {+o.= = :.:.%+Z :.Z Z O Z Z Z Z = ",
+" . ;.;.&+&+ .}.D '.-+o o $ ^+7+W r r 9+5+9+9+o.{+C %+%+%+:.:.:.Z :.Z :.C ",
+" ;.(+&+9 '. ._ _ _ ^+^+7+$ 7+v /+r , 9+, 9+1 o.C C :.%+%+= :.:.C ",
+" y.s &+R. .D D _ &.o i _.7+7+v r r 9+, , {+{+<+{+C C %+C C |. ",
+" s R. ._ D _ _ o ^+^+$ W 7+:+r !.9+9+9+9+9+{+l r ",
+" s > D _ _ o ^+o ^+7+7+7+:+r r r , 9+_.v ",
+" D _ &.^+o o 7+7+7+7+} r "};
diff --git a/arts/builder/pics/Synth_RC.xpm b/arts/builder/pics/Synth_RC.xpm
new file mode 100644
index 00000000..a906535e
--- /dev/null
+++ b/arts/builder/pics/Synth_RC.xpm
@@ -0,0 +1,323 @@
+/* XPM */
+static char * Synth_RC_xpm[] = {
+"64 64 256 2",
+" c None",
+". c #161E54",
+"+ c #42862C",
+"@ c #2E524C",
+"# c #366E34",
+"$ c #56BA24",
+"% c #263A54",
+"& c #4EA22C",
+"* c #4A529C",
+"= c #2A4654",
+"- c #768694",
+"; c #626E84",
+"> c #62D61C",
+", c #365E4C",
+"' c #1E2E54",
+") c #3A467C",
+"! c #323A84",
+"~ c #52629C",
+"{ c #46962C",
+"] c #467A4C",
+"^ c #425284",
+"/ c #2A3274",
+"( c #8A96BC",
+"_ c #3E4E84",
+": c #5ECA24",
+"< c #9AA2CC",
+"[ c #52AE2C",
+"} c #4A5A8C",
+"| c #727AA4",
+"1 c #66E21C",
+"2 c #4A765C",
+"3 c #66866C",
+"4 c #2A3A6C",
+"5 c #263664",
+"6 c #1A2664",
+"7 c #364674",
+"8 c #868EAC",
+"9 c #425E64",
+"0 c #3A468C",
+"a c #32427C",
+"b c #468E34",
+"c c #426E54",
+"d c #5EAA3C",
+"e c #222E6C",
+"f c #465A7C",
+"g c #5AC224",
+"h c #66DE1C",
+"i c #525A94",
+"j c #3A526C",
+"k c #52AA2C",
+"l c #2E4E51",
+"m c #425294",
+"n c #626EB4",
+"o c #5662AB",
+"p c #4A9E2C",
+"q c #1E266C",
+"r c #3E5E5C",
+"s c #5ED21C",
+"t c #7A82AC",
+"u c #3A427F",
+"v c #62C62C",
+"w c #3E7644",
+"x c #2A425A",
+"y c #56A634",
+"z c #2E465C",
+"A c #6AD624",
+"B c #3A4A84",
+"C c #567A5C",
+"D c #2A3674",
+"E c #969ECC",
+"F c #464E93",
+"G c #4A5A9C",
+"H c #2E3A7D",
+"I c #868EBC",
+"J c #263273",
+"K c #5AC61C",
+"L c #5E6AB4",
+"M c #4A8644",
+"N c #3A6E44",
+"O c #4EA62C",
+"P c #4A5694",
+"Q c #7E86B1",
+"R c #366640",
+"S c #4E963C",
+"T c #425680",
+"U c #6E7AC0",
+"V c #72EA24",
+"W c #424A94",
+"X c #4E8E44",
+"Y c #6E76A4",
+"Z c #325A4C",
+"` c #263A5C",
+" . c #2A4A4C",
+".. c #62DA1C",
+"+. c #263264",
+"@. c #323E7C",
+"#. c #4A824C",
+"$. c #8E9ABE",
+"%. c #424E94",
+"&. c #66CA2C",
+"*. c #56B62C",
+"=. c #6EE624",
+"-. c #222A64",
+";. c #426664",
+">. c #3A4A8C",
+",. c #36428C",
+"'. c #4A9236",
+"). c #42764C",
+"!. c #3E5A64",
+"~. c #5E66AC",
+"{. c #2A367C",
+"]. c #4A569C",
+"^. c #162254",
+"/. c #428A34",
+"(. c #32623C",
+"_. c #223254",
+":. c #4A9A30",
+"<. c #4A7A4C",
+"[. c #424E8C",
+"}. c #767EA9",
+"|. c #6AE61C",
+"1. c #5A6E74",
+"2. c #2E3E6C",
+"3. c #1E2A64",
+"4. c #364282",
+"5. c #262E73",
+"6. c #5EC228",
+"7. c #6EDE24",
+"8. c #525EA4",
+"9. c #626A84",
+"0. c #6A76BC",
+"a. c #567664",
+"b. c #6E8684",
+"c. c #42665C",
+"d. c #6A7299",
+"e. c #465E7C",
+"f. c #364E64",
+"g. c #8E92C4",
+"h. c #427E3C",
+"i. c #42724C",
+"j. c #3A5664",
+"k. c #529E38",
+"l. c #66D22C",
+"m. c #32564C",
+"n. c #3A723C",
+"o. c #5ABE27",
+"p. c #223E4C",
+"q. c #56B22C",
+"r. c #4E5E94",
+"s. c #465694",
+"t. c #6672BC",
+"u. c #5A66AC",
+"v. c #1E2A6C",
+"w. c #324A64",
+"x. c #4E5E9C",
+"y. c #3A7244",
+"z. c #7E8AB4",
+"A. c #2A3E5C",
+"B. c #5ECE24",
+"C. c #628A6C",
+"D. c #5A7E5C",
+"E. c #8A92BC",
+"F. c #768A8C",
+"G. c #8692B4",
+"H. c #2E3E7C",
+"I. c #4E9A34",
+"J. c #5A726C",
+"K. c #426264",
+"L. c #6ADA24",
+"M. c #66CE2C",
+"N. c #4A7E4C",
+"O. c #222E64",
+"P. c #5EC62C",
+"Q. c #46568C",
+"R. c #323E84",
+"S. c #1A2264",
+"T. c #36624C",
+"U. c #3E5674",
+"V. c #364684",
+"W. c #465A8C",
+"X. c #525AA4",
+"Y. c #3A428C",
+"Z. c #26327C",
+"`. c #223264",
+" + c #325254",
+".+ c #52A234",
+"++ c #66D624",
+"@+ c #3E4684",
+"#+ c #4A9634",
+"$+ c #46528C",
+"%+ c #8E96BE",
+"&+ c #56AE34",
+"*+ c #6AE222",
+"=+ c #2E3A73",
+"-+ c #1E2664",
+";+ c #4A8E3C",
+">+ c #466E5C",
+",+ c #62AA44",
+"'+ c #6ADE24",
+")+ c #3E5274",
+"!+ c #56AA34",
+"~+ c #465296",
+"{+ c #5A62AC",
+"]+ c #62D224",
+"^+ c #7E82B4",
+"/+ c #3E4A84",
+"(+ c #2E3674",
+"_+ c #4E5AA2",
+":+ c #5EC624",
+"<+ c #4E864C",
+"[+ c #3E6E4C",
+"}+ c #52A631",
+"|+ c #4E5694",
+"1+ c #365A54",
+"2+ c #2A3A64",
+"3+ c #2E4A50",
+"4+ c #46666C",
+"5+ c #3E4A8E",
+"6+ c #467654",
+"7+ c #1A225C",
+"8+ c #468A37",
+"9+ c #222A6F",
+"0+ c #2E4654",
+"a+ c #767AA4",
+"b+ c #3E468C",
+"c+ c #666EB4",
+"d+ c #9A9ECC",
+"e+ c #8A8EBC",
+"f+ c #626AB4",
+"g+ c #7276A4",
+"h+ c #66DA1C",
+"i+ c #5AB62C",
+"j+ c #2E367C",
+"k+ c #4E569C",
+"l+ c #36623C",
+"m+ c #565EA4",
+"n+ c #3E7244",
+"o+ c #828AB4",
+"p+ c #62CE24",
+"q+ c #363E84",
+"r+ c #4EA234",
+"s+ c #62D624",
+"t+ c #3A4684",
+"u+ c #42528C",
+"v+ c #2A327C",
+"w+ c #3E4E8C",
+"x+ c #4A5A94",
+"y+ c #26366C",
+"z+ c #36467C",
+"A+ c #868EB4",
+"B+ c #324284",
+"C+ c #468E3C",
+"D+ c #222E74",
+"E+ c #5AC22C",
+"F+ c #66DE24",
+"G+ c #52AA34",
+" J / / v+v+J 5.D+9+9+v.-+ ",
+" -.=+4.4.! H H {.Z.5.Z.5.D+D+9+D+S.. . . ",
+" (+/+W b+Y.4.R.H j+j+{.J Z.5.9+D+v.v.v.q S.S.. . ",
+" =+i k+P F W 0 ,.q+! H H {.Z.5.D+D+D+9+D+D+9+v.v.6 S.. . . . ",
+" i m+8.k+* %.5+0 ,.B+! j+{.{.{.v+Z.5.D+9+v.v.v.v.9+v.v.q q S.. . ",
+" |+f+~.o X.k+~+%.W b+,.q+R.H j+v+J J 5.D+D+D+9+9+9+q v.v.q v.q v.S.. . . ",
+" n n L {+8._+].F W b+0 ,.R.! H j+v+Z.J 5.5.9+D+v.v.9+q v.v.q v.q v.q 6 S.. . ",
+" [.0.t.f+u.o m+_+].~+W >.Y.q+! R.H {.{.v+Z.Z.D+D+9+D+9+v.v.q q v.q v.q q q q S.. . . ",
+" u Y t.t.L u.o _+].* %.W b+0 ,.! H H {.Z.J 5.D+5.D+9+D+9+v.v.v.q v.q q 6 q q 6 q S.. . . ",
+" |+U t.t.f+~.o 8.X.].~+%.5+0 ,.R.R.j+{.v+v+5.Z.5.D+9+9+v.v.9+v.v.v.q v.v.v.q v.v.q v.S.. . . ",
+" u d.0.c+n L u.m+_+k+* %.W 0 Y.,.R.H.! {.{.v+5.5.D+D+D+9+D+v.v.v.q v.q q q v.6 q v.6 q 6 S.. . . ",
+" [.U 0.t.c+L u.m+8._+].~+%.5+b+,.q+R.! {.j+/ J Z.Z.5.D+v.9+v.v.q q v.q v.v.q q q 6 q q v.q q S.. . . ",
+" c+t.c+n ~.u.o 8.X.].~+%.W >.0 ,.R.H H H {.{.J D+D+D+D+D+9+9+9+v.v.v.q q v.v.v.v.q v.q 6 q 6 6 S.. . ",
+" t.t.t.< d+u.o m+_+].* F %.5+Y.,.q+! ! H {.{.Z.5.5.5.9+9+v.9+v.v.9+v.v.v.v.v.v.v.v.v.v.v.v.v.q q v.S.. . ",
+" /+c+f+f+E E m+8.X.].* %.W b+b+Y.,.R.! j+{.v+v+J Z.D+5.5.D+D+D+D+D+9+D+D+D+D+v.9+9+D+9+D+D+9+D+v.v.v.v.6 S.. ",
+" f+f+~.u.{+m+8._+].* * %.W >.0 ,.R.R.H H {.{.J Z.D+D+D+v.9+v.9+D+9+D+D+D+D+D+D+D+D+D+D+9+9+D+9+D+9+9+9+v.. . ",
+" |+u.u.o o 8.X._+k+].%.%.W 0 0 Y.q+R.H j+j+{.v+J 5.+.@ R R 3+`.D+D+5.5.D+5.5.J J J 5.J J J e `.5.D+e e 9+e v.6 . ",
+" q+{+o 8.b+_+%+%+].* %.%.W >.0 ,.R.R.! H H {.Z.J D+3+[ ++]+B.]+:.5 D+J Z.J J Z.5.J J J J J 5.Z.5.J J 5.D+5.D+9+9+7+. ",
+" F i X.X.b+].g.E.m %.W 5+b+Y.Y.4.R.! ! {.{.{.v+J x v *+b 3+@ p |.[ 5 5./ Z.J D Z.Z.y+Z.J y+/ y+J J J J J 5.J J e 9+6 ",
+" _+k+k+].4.* E.I %.W 5+0 Y.,.q+R.! H {.j+v+/ J +.}+V '.+.J D+` k |./.J J D {.Z.D {.v+D D v+{./ v+/ y+/ J J 5.5.5.`.7+ ",
+" (+s.s.* F R.%.I o+W 0 Y.0 ,.R.! R.H H {.{.v+Z.J m.'+P.` J {.v+J = p+]+3+D D =+j+(+4 {.{.4 {.H D D {.D D D y+J J 5./ e 6 ",
+" F F %.%.%.%.W b+b+0 0 Y.q+q+R.R.H H j+{.v+J / J !+=.n+J Z./ {.D J /.|.+ y+j+{.H H j+H H j+H D H H (+{.D D D D D D J O.. ",
+" -+5+W W 5+b+0 b+0 Y.,.,.R.R.R.! j+j+{.{.v+{.J / A.A ++A./ {.{.{.j+D l F+6.4 H H H H.H H H.H H.H H H =+H j+(+(+D D D / D J -+ ",
+" ! b+0 0 Y.0 H 0 Q Q q+R.R.R.! j+H {.v+v+y+J {.J R *+k J {.D {.D H H y+E+..3+H H H H @.@.H H.! H.H H.H H =+H H D (+D D / O.. ",
+" @.,.,.Y.,.q+{.,.Q Q R.! H j+H j+{.{.v+v+v+v+/ J :.*+N D H H H H H H (+8+F+y.=+R.R.R.R.a @.@.@.@.@.@.@.H.H.H ! =+(+(+D (+/ +. ",
+" ,.q+q+,.q+q+v+! t t H H j+H {.v+{.v+J J Z.J v++.v h+0+D {.{.H H H R.2., h O 2.@.a R.@.q+q+a R.a R.R.@.@.@.@.H.H H.H =+D D / ",
+"5.R.R.! R.R.! v+! }.^+H {.{.{.{./ J Z.Z./ v+y+{.A.A p+2+H H H H H H.! H z s+:+2.q+4.4.B+4.4.4.4.4.4.B+4.@.@.@.@.H H.H =+=+4 4 D ",
+"/ H H ! j+H H H H H {.{.{.v+Z.Z.Z.Z./ v+y+{.v+/ m.h q.D {.H H H R.R.a R.2.: s+w.B+4.V.V.V.4.V.4.4.4.4.4.4.a 4.a a @.@.@.H H (+D ",
+"v+{.{.H {.{.{.{.v+v+v+{.Z./ J 5.5.5.5.Z.J {.{.J y.1 #+=+H H @.@.R.R.4.B+@.$ h+1+4.4.u V.0 t+0 V.V.t+V.V.4.4.4.q+@.@.@.@.! =+=+(+",
+"{.j+{.v+{.{.v+D+{.}.}.Z.J Z.5.J J J J J v+J D D '.*+R =+@.R.R.R.@.B+B+4.a 8+h w z+0 >.0 t+0 t+t+t+t+t+t+t+V.4.z+4.4.@.@.@.@.H H ",
+"Z.v+{.Z.v+{.v+9+Z.a+a+Z.5.J D+D+D+D+5.J v+Z.D D & F+Z H H.H.B+4.q+4.4.t+z+w h+C+z+t+@+>.B >.>.>.>.0 0 0 t+t+4.V.4.4.a a @.@.=+H ",
+"J Z.5.Z.Z.5.J 9+Z.| a+5.D+5.D+D+5.Z.J Z.y+D {.5 :+h+z H @.R.R.R.4.4.0 4.4.1+..&+z+>.>.5+5+5+5+/+5+B B B B >.B t+V.V.4.q+a @.@.=+",
+"5.Z.5.5.Z.D+J 9+J | | D+9+5.9+D+5.J J v+v+{.{.x ++p+A.! @.B+4.B+4.V.0 V.t+f.> : 7 5+w+w+w+w+w+w+w+w+w+5+B b+B t+t+t+V.4.a B+a @.",
+"D+D+D+D+D+5.D+D+D+D+5.D+D+9+5.9+D+5.J J {.D D , h [ =+R.4.q+B+V.V.0 B >.0 7 P.h+j._ 5+w+w+[.[.w+[.[.5+w+5+_ 5+B t+V.V.4.4.q+q+H ",
+"9+5.D+D+D+9+9+9+D+9+D+9+D+v.D+9+D+5.Z.J Z.D D '.F+N 2.R.B+4.4.0 V.0 t+>.>.z+#+h i._ [.[.u+m [.%.w+u+w+[.5+5+w+5+/+@+B ) V.4.a @.",
+"9+D+D+9+9+5.5.v.9+| g+D+v.9+9+D+J J J v+D {.y+o.h+ +H @.4.4.u 4.0 t+5+>.w+B [+..r+B [.m m u+~+u+~+[.u+u+u+[.W _ 5+B t+t+) 4.4.q+",
+"q 9+D+D+v.v.9+v.v.| Y 9+v.v.D+9+D+5.J D D D = ..: 2.R.R.4.V.V.0 >.B 5+5+5+5+j.s+B.j m u+~+s.s.u+s.~+u+u+u+u+w+_ 5+/+B t+t+) z+4.",
+"3.9+9+v.9+9+v.q 9+Y g+v.v.9+9+D+5.J J v+{.D h.1 8+=+@.B+V.,.0 V.t+>.5+w+[.[.B G+h+c u+s.Q.~+~+s.~+s.~+m m u+u+[.w+w+5+B B t+4.@.",
+" v.9+v.v.9+v.q 9+| Y 9+v.v.D+D+D+J J / Z.4 o... +H.q+B+4.V.0 >.5+B w+w+w+[.[.i.h+y [.s.W.W.P W.s.s.P Q.~+$+u+u+w+_ 5+B t+t+) ",
+" v.v.9+9+v.v.v.v.q 9+v.v.9+v.D+5.J {.D D , F+*.4 @.B+4.V.u t+t+>.w+5+u+m ~+u+U.: ]+r s.].].G P P x+s.s.s.m u+F [.w+_ /+B t+z+ ",
+" . q v.v.q v.q v.v.v.q v.9+9+D+5.5.J J 5 q...T.=+R.q+4.V.V.>.0 5+w+w+[.u+m m u+M h k.^ x+x+G G x+x+x+s.P s.s.$+u+[.w+_ B t+@. ",
+" S.v.v.v.9+q q v.Y Y v.v.v.D+e D+J Z.y+R 1 q.=+H.4.B+,.V.0 t+/+5+w+%.m m $+s.u+U.P.]+;.s.G x.r.i _+G x+W.s.s.Q.$+u+[.W /+) O. ",
+" S.q v.v.v.6 q g+g+q v.v.D+5.J J / ` o.> Z =+@.4.4.4.t+>.>.>.5+w+w+[.m ~+s.s.T #...!+T x+x.x.x.x+x.G x+s.s.m m [._ w+B @+ ",
+" S.v.q q q q v.Y Y v.q 9+9+e D+5.`.b 1 :.2+! R.@.B+4.V.t+B w+w+w+u+m ~+s.P x+].)+&+..N.W.x.x.~ x.G x+i P s.Q.$+u+u+_ [.+. ",
+" S.v.v.q q q Y d.9+v.v.D+5.`.e n...*.A.=+H R.4.4.,.V.0 t+5+5+[.u+u+$+s.s.W.G x+4+:+p+6+} x.~ ~ x.G r.x+P s.s.u+[.w+[. ",
+" q q 6 q 6 -+q v.6 q 9+9+e O.n.> g l =+H @.@.R.4.t+0 t+5+>.[.w+%.m s.~+s.P _+x+Q.>+p+: 6+} ~ x.~ x.G x+x+s.$+$+^ _ u ",
+" . q v.q v.q q q v.v.v.3.% { ..$ .J {.H H.@.B+a 4.t+t+/+>.5+[.u+m s.s.G W.G x.x.W.2 P.s X e.x+~ r.G x+x+s.m ~+F _ ) ",
+" 6 6 q S.6 d.d.6 6 _.R g s+p p.J D H 2.! R.@.4.V.V.B >.w+[.w+%.u+m s.s.x+G i x.x.x+4+&+]+i+2 f Q.} s.$+Q.$+u+^ /+ ",
+" S.q 6 3.J.C # & K B.$ (.+./ D D j+H.@.R.R.4.4.,.t+t+>.w+[.[.$+~+s.s.P G x.x.x.x.r.f <+K : g I.] c !.)+u+u+_ ",
+" 6 3.-++ p+d p+p+:.(.; d.| }.J H t }.t Q 4.4.Q o+z.o+/+5+A+G.I G.~+P ( ( ( ( x.~ $.$.G.- N.y &.v ,+v ;+[.$+5 ",
+" . 6 % C a.J.; 6 3.| | | }.J D t t t t @.4.Q Q o+o+B w+o+G.I E.u+s.( g.%+$.r.x.$.%+( E.W.Q.- b.3 3 !._ [. ",
+" 6 6 6 6 6 6 3.3.9+5.J J D D (+=+H @.@.a 4.t+t+t+5+>.[.w+u+u+s.$+s.W.x+x+G x.x.x.x.x.x.i x+Q.^ ^ ^ _ ",
+" . 7+-+-+S.6 v.9+3.-.-.9+y+{.D+J J J @.a D D 4 H /+>.! @.@.@.m $+4.4.4.t+x+x+B B B ) G x+V.V.s.$+$+@+ ",
+" . 6 6 q q q v.e D+5.J v+y+(+=+H @.@.R.4.V.u V.t+/+_ w+[.u+$+~+Q.s.P s.P x+x+x+x+x+x+x+P P Q.$+5 ",
+" . 7+-+-+v.q 9+e 5.J / y+{.=+=+H @.@.q+B+t+V.V.t+B w+5+w+u+%.u+Q.s.s.P s.P P W.P P s.$+$+u+) ",
+" . 6 6 q 3.v.e e 5.J v+/ {.H =+H @.a 4.4.V.t+0 b+B _ w+w+u+$+m $+s.s.s.s.s.P s.Q.Q.Q.u+5 ",
+" . 7+q v.9+e e 5.J D D D H =+H ! @.@.4.4.t+V.B 5+/+_ w+w+[.u+u+u+$+s.s.u+s.m ~+u+u+@+ ",
+" 6 -+v.9+D+`.J J / D (+H 2.H.@.a 4.4.z+t+t+t+/+5+5+[.[.w+[.[.u+u+u+$+u+$+u+[. ",
+" . -+v.9+e 5.J J y+{.{.H H.! @.a R.4.4.V.V.t+/+B _ w+w+[.[.[.[.u+[.u+[./+ ",
+" 3.e 9+e J / / D (+=+=+@.H @.4.4.V.u t+B t+@+B /+/+w+w+w+[.w+_ /+ ",
+" -+O.e e J / y+{.D 4 @.@.2.@.q+a 4.4.V.V.t+B /+B /+/+_ 5+/+) ",
+" -.5.J / / j+(+(+=+! H.@.4.4.4.4.t+t+t+t+@+B t+4. ",
+" O.O.J D 4 j+=+=+2.@.@.4.4.4.4.4.V.V.@.a ",
+" / {.4 H H H 2.=+@.@.a 4. "};
diff --git a/arts/builder/pics/Synth_SEQUENCE.xpm b/arts/builder/pics/Synth_SEQUENCE.xpm
new file mode 100644
index 00000000..23caeb2f
--- /dev/null
+++ b/arts/builder/pics/Synth_SEQUENCE.xpm
@@ -0,0 +1,319 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"64 64 248 2",
+/* colors */
+" c #354280",
+" . c #303C7B",
+" X c #3F4C8D",
+" o c #695B5D",
+" O c #313972",
+" + c #3A4688",
+" @ c #313468",
+" # c #263071",
+" $ c #354283",
+" % c #838594",
+" & c #334081",
+" * c #323E80",
+" = c #1E2869",
+" - c #F4BE22",
+" ; c #EEE45E",
+" : c #1C2667",
+" > c #293477",
+" , c #283276",
+" < c #8C93B7",
+" 1 c #4D588D",
+" 2 c #212C6F",
+" 3 c #1E286C",
+" 4 c #515B9E",
+" 5 c #4E599B",
+" 6 c #3C4986",
+" 7 c #1A235E",
+" 8 c #4B5598",
+" 9 c #374581",
+" 0 c #364380",
+" q c #4E4A66",
+" w c #DCD48E",
+" e c #47548A",
+" r c #6772B0",
+" t c #212B68",
+" y c #404D8D",
+" u c #7A7D91",
+" i c #2D3977",
+" p c #535D8F",
+" a c #3A4787",
+" s c #293373",
+" d c #FBE73F",
+" f c #273171",
+" g c #A2A7C2",
+" h c #252F6F",
+" j c #38427B",
+" k c #8C653C",
+" l c #4D5C96",
+" z c #7B83AC",
+" x c #252F72",
+" c c #38447E",
+" v c #5F6A97",
+" b c #CAC07F",
+" n c #737BA4",
+" m c #313B81",
+" M c #333E79",
+" N c #1F296C",
+" B c #42508B",
+" V c #3C406E",
+" C c #1E296B",
+" Z c #1D276A",
+" A c #1C2769",
+" S c #303A76",
+" D c #C99732",
+" F c #293379",
+" G c #2C3672",
+" H c #283378",
+" J c #273177",
+" K c #242F74",
+" L c #374480",
+" P c #232D73",
+" I c #747DA8",
+" U c #212B71",
+" Y c #202B70",
+" T c #1E296E",
+" R c #A27D47",
+" E c #465188",
+" W c #404E8C",
+" Q c #303C79",
+" ! c #576292",
+" ~ c #3E4C8A",
+" ^ c #474E7F",
+" / c #A7ACC6",
+" ( c #2B3674",
+" ) c #9B9182",
+" _ c #7B82A8",
+" ` c #283271",
+" ' c #252E6E",
+" ] c #303C7C",
+" [ c #868BAC",
+" { c #2B3677",
+" } c #1B2464",
+" | c #606BAB",
+". c #C1A851",
+".. c #9097B9",
+".X c #666F9D",
+".o c #283474",
+".O c #3C4781",
+".+ c #374486",
+".@ c #656A92",
+".# c #212C6D",
+".$ c #202A6C",
+".% c #384073",
+".& c #1E286A",
+".* c #6B75A5",
+".= c #2D387C",
+".- c #1C2668",
+".; c #947449",
+".: c #4A5995",
+".> c #3A4782",
+"., c #263275",
+".< c #666E96",
+".1 c #485793",
+".2 c #475592",
+".3 c #465591",
+".4 c #222C71",
+".5 c #202A6F",
+".6 c #5A4B52",
+".7 c #333F7B",
+".8 c #1F2A6E",
+".9 c #1E286D",
+".0 c #1D286C",
+".q c #434E84",
+".w c #3F498A",
+".e c #31386F",
+".r c #5C67AD",
+".t c #384583",
+".y c #3A447B",
+".u c #333F7E",
+".i c #2E3B79",
+".p c #333C74",
+".a c #434B7D",
+".s c #F7B618",
+".d c #3A4588",
+".f c None",
+".g c #263171",
+".h c #364184",
+".j c #252F70",
+".k c #242F6F",
+".l c #484664",
+".z c #212B6C",
+".x c #5D6693",
+".c c #303B7E",
+".v c #1F296A",
+".b c #2D397B",
+".n c #1D2768",
+".m c #404C87",
+".M c #989AAF",
+".N c #4E5B8E",
+".B c #252F73",
+".V c #29306D",
+".C c #45548F",
+".Z c #35407C",
+".A c #212B6F",
+".S c #44528E",
+".D c #2F3980",
+".F c #E9A317",
+".G c #1D276B",
+".H c #404E8A",
+".J c #3E4C88",
+".K c #5A515E",
+".L c #9BA1C2",
+".P c #33407D",
+".I c #323E7C",
+".U c #313E7B",
+".Y c #424264",
+".T c #F5D836",
+".R c #656FAE",
+".E c #1D2664",
+".W c #3C4A89",
+".Q c #2C3876",
+".! c #2B3675",
+".~ c #384685",
+".^ c #465296",
+"./ c #5863A1",
+".( c #434E93",
+".) c #323C7F",
+"._ c #303A7D",
+".` c #9C9FB2",
+".' c #8E8E97",
+".] c #3C4982",
+".[ c #374287",
+".{ c #364286",
+".} c #354085",
+".| c #232E70",
+"X c #171F57",
+"X. c #46558F",
+"XX c #43518C",
+"Xo c #1E286B",
+"XO c #1C2669",
+"X+ c #2B367B",
+"X@ c #6A554F",
+"X# c #263076",
+"X$ c #9096B3",
+"X% c #253075",
+"X& c #242E74",
+"X* c #232E73",
+"X= c #36417F",
+"X- c #212C71",
+"X; c #202A70",
+"X: c #43518F",
+"X> c #B6AD96",
+"X, c #8289AF",
+"X< c #3D4B89",
+"X1 c #414C83",
+"X2 c #2A3573",
+"X3 c #414979",
+"X4 c #283371",
+"X5 c #B8BCD0",
+"X6 c #BD8933",
+"X7 c #575B7E",
+"X8 c #5B6799",
+"X9 c #7F87AF",
+"X0 c #3C478B",
+"Xq c #7D85AD",
+"Xw c #283374",
+"Xe c #39467E",
+"Xr c #F8AF10",
+"Xt c #343F83",
+"Xy c #232D6F",
+"Xu c #222D6E",
+"Xi c #333D82",
+"Xp c #212B6D",
+"Xa c #303D7F",
+"Xs c #54609F",
+"Xd c #2E3B7D",
+"Xf c #775F51",
+"Xg c #424E8A",
+"Xh c #1D2769",
+"Xj c #515E9C",
+"Xk c #58556E",
+"Xl c #2C377B",
+"Xz c #2A3579",
+"Xx c #293578",
+"Xc c #40477E",
+"Xv c #757487",
+"Xb c #222D71",
+"Xn c #8890B4",
+"Xm c #455490",
+"XM c #878EB3",
+"XN c #6E78AB",
+"XB c #4D5684",
+"XV c #1F296E",
+"XC c #1E296D",
+"XZ c #41508C",
+"XA c #4A5281",
+"XS c #69719C",
+"XD c #3B4886",
+"XF c #5B6081",
+/* pixels */
+".f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f # , > F HX# KX&.4.8 C.n.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f G.I.[.}Xi.D.=X+ F JX% K P.4X- YX;XV } 7.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f 6.(.(X0.[.}Xi.D.=X+ F JX% K P.4X- Y.5.8XV TXo 7.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.fX= 4 5 8.(.(X0.[.}Xi.D.=X+ H JX%X& P.4X- Y.5.8XVXC.9.9 3XO 7.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f./.r 4 5 8.( XX0.[.} m.D.=X+ H JX%X& P.4 U Y.5.8XVXC.9 3 3.0.G.-.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.f.f.f.f.f.f.f 5 |.r./ 4 5.^.( XX0.[.} m.D.=X+ H JX%X& P.4 U Y.5XVXVXC.9 3 3.G.G.GXO 7.f.f.f.f.f.f.f.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.f.f.f.f.f.f r r.r.rXs 4 8.^.( XX0.[Xi._.bX+ F H JX%X& P.4 YX;.5XVXVXC.9 3 3.G.G.G Z Z :.f.f.f.f.f.f.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.f.f.f.f E r r.R.r.rXs 4 8.^.(X0.d.[.) M.i { H HX#X%X*XbX- Y.5.8XVXV.9 3 3.0.G.G Z Z Z Z.-X .f.f.f.f.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.f.f.f 1 r r.R.r.rXs 4 8.^.( XX0.[.}.%Xk V OXw JX# KX*XbX- Y.5.8XVXC.9 3 3.G.G.G Z Z ZXhXO :X .f.f.f.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.f.f l r r r.R.r./Xs 4 8.^.( XX0.{XtXk R o VX4 JX%X& P.4 U Y.5.8XVXC.9 3 3.G.G.G Z Z ZXhXOXO :X .f.f.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.f 1 r r r.R.R r.R |./Xs.2 XX0.d.h LXF. o.p.Q JX%X& P.4.A.8XVXoXo.GXo 3.G.G.G Z Z ZXh AXOXO.- :X .f.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f E r r r.R |XN < <XnX9XN l.w.d.[.h.]Xv. o O.bX#X%X*XbX- YXV.v.v.v Z N 3 3Xo.G.G Z Z ZXhXhXO.-.- :X .f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f r r r.R.R z < / g.L..X,.X.N.w.{.t e u bXk ( {X% K P.4X- x f O.%.e.V h N 3 3 3XoXoXoXo.& ZXhXh.-.- :.f.f.f.f.f.f.f",
+".f.f.f.f.f.f r r r | | z /X5X5X5X5 /.LXM I 1.h.m ! % bXk.! >X%X& P.4.5 `Xc ! _.x.q G.$.A.8.$.$.$.$.$ N.vXo.&.&Xh.n }.f.f.f.f.f.f",
+".f.f.f.f.f 5 r |.r |XN < /.. _ nXS I z z z 1Xt.q.< % )Xk ,., KX* PXy.#.p v n < n p.p.#Xu 2 2 2.#.#.#XpXp.$.$.v.v.v = 7.f.f.f.f.f",
+".f.f.f.f.f |.r.r.r.*.L /X5 I l.2X: l.XXNX9 1 *XAXS % )Xk.,.BX&X* PXu '.a [.. gX,XS.y.kXy.|.|.|.|XyXyXyXuXu.#Xp.z.$.$ =.f.f.f.f.f",
+".f.f.f.f./.r.r./Xs.*.L.L.L.X.2 y.w.wXg E E c.c.a u % ) ^., x x.|Xb f M p [.. / [.X.y.g x.B x x x x x x.k.|XyXyXu.#.z.$ =.f.f.f.f",
+".f.f.fX=.r./Xs 4 4.* /.L <X8.( XX0.d.[Xt.)._.=X3XvXv uX3 f.B x x x.p pXSXM.L /XM.X.y f #.,.,.,.,.g.g.g x # x.k.kXyXu.#.#.E.f.f.f",
+".f.f.f 4 4 4 4 5 8.X /.LXMXs XX0X0.[XtXi.c.).uXF ).'.'XF.y j.p G `X3XS nXqX$ g [XSXcXw., , >.oXwXwXw.,.,Xw.g.g #.j.kXyXu.$.f.f.f",
+".f.f.f 5 5 5 8.^.^.X /..X9XjX0.d.[.}Xi m._X=Xg _X5 gX$ _.X v.x.a OXBX$Xq nXM.LX,XSXc.! >XzXxXxXx > > >.o.oXwXw f.g #.j.kXy.f.f.f",
+".f.f 6 8 8.^.^.(.(X8 g..XM p.d.h.} m.c.Z.] E p [X5 /.`Xn _ I n.xXAXS.LXM _X$ /X$ _ ^.Q.Q ( (X2X2 (.!.! >.!.o.o.oXw f f.g h.E.f.f",
+".f.f.(.(.(.(.( X.w !.L.. < p L & m.c .X1 v.X.X.`X5X5 g.`..X$ <Xq n < /.. _.LX5.LXMXB Q Q.p O G G G (.Q { { { {.!.o.oXw f.g h.f.f",
+".f G.(.( X X XX0X0 1X9.. g.* e.O.I j.q.xXqXS p.'X> %X7XB ^XAXA p !.*XMXq I..X5 gXM.x.aXk o.K.6.6.6.6.Y @ @ G.Q.!.!.!X2 sX4 f t.f",
+".f.IX0X0X0X0X0.[.[Xg.X <X5.. n 1.Z 1 IXM gXSXA uX>Xk.e G s (.Q.Z E p.XXS n..X5 g < n.@Xv RX6X6X6X6 k kX@.l @.Q.Q i.Q.!.!.oXw t.f",
+".f.[.[.[.[.[.[.}.}X=.q vXMX,XqXS p.< _ n n.xXB uX>XF S.!.!.Q S Q.I cX1.q.q.@ <XM [.' ). D D.F.F.F DX6 kXf.l @ G i.Q.Q.Q.!X2X4.f",
+".f.}.}.}.}.}.}XiXi m.cX1X8 I <XnXMX,Xq v e 1XF uX>XFXlXlXl.=.bXd.b._ ].U M.qXS n _ ). - -.s.s.sXrXrXr DX6X@.Y.e.i.i.i i.Q.!X2.f",
+" #XiXiXi m m m m.D.=.= Q cXc ^ ^.a.aXc j S.aXFXv )X7XlXl.b.=Xd.cXd ].c.I.I j.qXF % ) - - -.s.s.sXrXrXr.F.F k.6.Y Q.i.i.i i.Q.!X4",
+" ,.D.D.D.D.D.D.D.=.=XlXzXz , s f # # # f., VXvXv )XAXl.bXd.c.cXaXa * & & & & XA %. - -.s.s.sXrXrXrXrXrXrX6 k.Y.U ].U Q.i i iX2",
+" >.=.=.=.=.=XlX+X+X+Xz H J.,.g x x # s , ,X3XvXvXvXk.bXd.cXaXa * * &Xt $ $ $ 0Xk.;. - -.s.s.sXrXrXrXrXr.FX6Xf.Y.P.I.I.I Q.i.i.Q",
+" FX+X+X+X+X+Xz F F H H J JX#X#X%.BX%X# , ,Xk )XvXF q.=.cXaXa * & & $ $ $.{.t.t q.;X6 - -.s.s.sXrXrXrXrXr.F RX@.Y .P.P.U Q.i.Q",
+" H F F H H H H H J JX% x.V.V @.V.V @.e G.!Xk )XvXk.lXd.cXa * & & $ $.{.+.+.~.t.K RX6 D D D.F.F.F.F.F.FX6X6Xf.l V $ 0 .P.P.I Q.i",
+"X# J J J J J JX#X# K.B.V V.l q q.l V.p O GXk. oX3 V.cXa * & & $ $.{.+.~ +XD.>.KX6 R R R RX6.F.F.F.F D RXf.K V j.t 9 0X=.P.P.U.i",
+" KX%X%X%X%X%X%X&X&.V @ q oXf.;.;.;.; o oXk.; bXv ^.y ].).u & $ $.{.+.~ + +.].O.KX6Xf.K.K.K oX@XfXfXfXf.K q V.y L.~.t 9 0 .P.I Q",
+"X& K KX&X&X&X& PX* @.K.;. . ; ; ; ; ;. ). b % E c.I.u.u $ $.{.+ + + +.WX1Xc oX6 oX3.a.mXcX3X3X3X3 qX3.O.] 6 6XD.~.t 9 0X=.P.U",
+".4 P P P P P P 2 '.Y.;. . .T d d d ; ; ;. b w.'.x 1.qX1X1.].~.~.~ + +.W.W.qX3 oX6 o.aX1.H.mX1X1X3.a.a.] 6.J ~X< 6XD.~.t 9 0.P.I",
+".8.4.4.4.4.4.4.z t.6. . .T d d d d d d ; ; w w.` I n.*.XX8.q L L.~ +.W.W X ^X3 oX6 o.qXgX:XZ.H.H.m.m.H.H W W.H.JX< 6XD.~.t L .7",
+" CX-X-X- U U Y.$ t.6. -.T d d d d d d ; ; w w /XMXMX,X,XM v E.O.~ a.W X X ^ q o R.K B.SX..N p p p.N 1.q.H.H W.H.JX< 6XD.t.t 0.Z",
+".n Y Y Y Y YX; t.VX@ D -.T.T d d d d d ; ; wX5X5.L.L.L /X5.. n e.>XD X X yX3XkXf.;Xk.S 1X8 nXnXnXnX, _ p B.HXZXZ.H.J.W 6.>.> L.P",
+".fX;.5.5.5.5.5.v t.Y.;X6.T.T.T.T.T.T.T ; w wX5 % p p.NXSXM <...< E 6 ~ W yX3.KXf kXk.C pXNXqXn n.< nX$XS.NXXXZXZ.H.H.J 6XD.t 9.f",
+".fXV.8.8.8XVXV 3.v @.6.; D -.T.T.T.T.T ; wX> /XF.p M.Z.q v <X5XnX8 E ~ WX:X3 oXfXfXk.2X8XMXMXM.< 1XS gX,.X 1XXXZXZ.H.J.J 6.> L.f",
+".f }XVXVXVXVXV 3.G t @.6 k RX6 D D. . bX>.`.`X7 SX=.t.m EXSX5Xn.X e ~.HXZ qXfXfX@X7.2 ! I I I v.NXS /Xn.*.N.SXXXXXZ.H.J 6XD.p.f",
+".f 7 TXCXCXCXC.9.9.n t @.Y.6Xf.; R R R )X$X$X$XA S 0 $XD c p.LXn I.N.H.HXZ q kXf.KXA.1 !.*.*.*X8.NXS /.. I.N.S.S.SXZ.H.J 6 6 M.f",
+".f.fXo.9.9.9.9 3 3 Z.n.V.% V.Y.Y.l qXk.@ _ [X$XA Q 0 $XD L p.L <Xq v 1.N.NXv RXvXv.x lXjX8X8.*.*XS [X5.. I p.C.S.SXXXZ.H.J.>.f.f",
+".f.f 7.9 3 3 3 3 3 3.G 3 3.8.AXy # s SXAXSXq..XA Q 0 $XD 9 p.L..X, I.* I z.M b ).' _.*X8 lX8.*Xq.. /X5.L z pX..S.SX:XZ.H.J j.f.f",
+".f.f.f 3 3 3 3.0.G.G.GXo 3.8 2Xb x f (X1XS zX$XA Q.u $.t c ! g..X9 z IX, <.` bX>.MX, IX8 lXjX8X8.<Xq /..Xn !X..CXm.SXZ.H.J.f.f.f",
+".f.f.fXO.0.G.G.G.G.G.G.G 3.$ 2.| x.g.oXcXS zX$XA S.I &X= c.x /..XqXqX9...LX5 wX5 /.. zX8 l l l l p.X <...L ! e.SXm.SXZ.H 6.f.f.f",
+".f.f.f 7.G.G.G.G.G.G Z.GXo.$ 2.| x.g sXcXSXq..XB.p M.ZXeXB n / n p p p v u ) b.' n.<X8X8 v v v ! pXS.L.... ! e.SXm.SXZ.HXe.f.f.f",
+".f.f.f.f.-.G.G Z Z Z Z ZXo.$.#.| x.g sXc.XXq..XB.p M jXB _X$ /.xX1.m B eXFXv. Xv p.N.NX8X,X9X9.X !XS g.L.. ! e.SXm.SXZ.m.f.f.f.f",
+".f.f.f.f.fXO Z Z Z Z Z ZXo.$.#Xy x #Xw j.x nX$.<XBXBXB.@Xq _XqXB.].J.H ^X7Xf R o 1.: lX8 IX9Xn _XSXq.LXn I pX..C.CXX.H.f.f.f.f.f",
+".f.f.f.f.f 7 Z Z Z ZXh ZXo.$.#Xy x xX4 M 1XSX$ [ [ [ [X,X,.* !.q.W.HXZX3 qX@ k.K.1.: lXj.X z....X$.... _.X.N.2.C.CXX.O.f.f.f.f.f",
+".f.f.f.f.f.f : ZXhXh AXh.& NXpXy x.gXw S.p.y ^ ^ ^ ^ ^XAXA.q.m.m ~ ~ WX3.KX@ k q.1.: lXjXjX8 v v v v v ! l.1.3.C.S.H.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.-XOXOXOXh Z.vXpXu.k x.,.!X2 ( G.Q S Q M.7.Z L.~.WX< ~.H V.6X@ k q.1.:.: l l l l l l.:.:.:.1.2.2.C B.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.fX :XOXOXOXhXo.$Xu.| #Xw.o.! i Q.I.PX= 0 L.t.~ a 6.W.J ~.lX@X@X@ q.2.1.:.: l l l l l l l.:.2.3XmXX.y.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.fX :.-.-Xh.&.$.#Xy x.gXw.o { i.i ].I.u $.t.~XD.WX<.J.l kX@.6X3Xm.1.1.:.:.:.:.:.:.:.:.1.2XmXX.y.f.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.f.fX :.-.-.&.vXpXy.k.gXw.o { i.i ..I.u & .t.t.~XD 6 6 q kX@.6X3Xm.3.2.1.1.:.:.:.1.1.2.2XmXX.y.f.f.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.f.f.fX :.-Xh.v.zXu.k # f.o.!.Q i.i ].I.u $.t.~ a.>.>.6 kX@.lX3.SXmXm.3.2.2.2.2.2.3.3.CXX.y.f.f.f.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.f.f.f.fX :.n.v.$.#Xy.j.gXw.o.! i.i ..I.u.u $.t.~.t.>.l k.6 VXcXX.S.S.SXmXmXmXm.C.C.S B.y.f.f.f.f.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.f.f.f.f.f.f } =.$.zXu.k # f.o.!.Q i.i ..I.u 0 9.t 9 c.YX@.l.y.]XZXZXXX:.S.S.S.SXXXX.H.f.f.f.f.f.f.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.f.f.f.f.f.f.f 7 =.$.#Xy.j fXwX2.!.Q.i.i.U.I.P 0 9 L L.%.Y VXe.].H.HXZXZXZXZXZXZ.H.O.f.f.f.f.f.f.f.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f =.#Xu.k.g f s.!.Q i.i Q.I.P.PX= 0 0 L 9.>.>.] 6.J.J.H.H.H.H.H.m.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.E.$Xy h.gX4.o.!.Q i.i Q.U.P.P 0 L 9.>.> 6 6 6.J.J.J 6Xe.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.E h fXwX2.!.Q i.i Q.I.P.PX= 0 L.t.>.t.>XD 6.> j.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f t tX4X2.! i.i.i Q.U.I.P.P 0 L 9 L.p M.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f",
+".f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.fX4X2.Q.Q.i.i Q.U.I.7.Z.P.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f"
+};
diff --git a/arts/builder/pics/Synth_SEQUENCE_FREQ.png b/arts/builder/pics/Synth_SEQUENCE_FREQ.png
new file mode 100644
index 00000000..ebd9d829
--- /dev/null
+++ b/arts/builder/pics/Synth_SEQUENCE_FREQ.png
Binary files differ
diff --git a/arts/builder/pics/Synth_SHELVE_CUTOFF.xpm b/arts/builder/pics/Synth_SHELVE_CUTOFF.xpm
new file mode 100644
index 00000000..bbee7585
--- /dev/null
+++ b/arts/builder/pics/Synth_SHELVE_CUTOFF.xpm
@@ -0,0 +1,247 @@
+/* XPM */
+static char * Synth_SHELF_CUTOFF_xpm[] = {
+"64 64 180 2",
+" c None",
+". c #161E54",
+"+ c #4A9A2C",
+"@ c #4A5A94",
+"# c #2E3E7C",
+"$ c #62CA2C",
+"% c #4A8644",
+"& c #3E4E84",
+"* c #66E614",
+"= c #52AE2C",
+"- c #222E64",
+"; c #364684",
+"> c #46725C",
+", c #8692B4",
+"' c #6E769C",
+") c #52629C",
+"! c #4A529C",
+"~ c #6EF614",
+"{ c #2A3674",
+"] c #7E86AC",
+"^ c #363E84",
+"/ c #62DA14",
+"( c #46568C",
+"_ c #3E4684",
+": c #969ECC",
+"< c #1A2664",
+"[ c #52A62C",
+"} c #424E94",
+"| c #767EA4",
+"1 c #525A94",
+"2 c #222E74",
+"3 c #62D224",
+"4 c #6AEE14",
+"5 c #8E9ABC",
+"6 c #5A62AC",
+"7 c #7EFE1C",
+"8 c #323A84",
+"9 c #56BE1C",
+"0 c #3A4A7C",
+"a c #6E7ABC",
+"b c #2E3A74",
+"c c #868EB4",
+"d c #36427C",
+"e c #222A64",
+"f c #5A9E44",
+"g c #4E5E94",
+"h c #32427C",
+"i c #464E8C",
+"j c #6EE624",
+"k c #5E6AB4",
+"l c #8E92C4",
+"m c #52669C",
+"n c #72FE14",
+"o c #3E4A84",
+"p c #2A3274",
+"q c #36428C",
+"r c #767AA4",
+"s c #4A5694",
+"t c #56A644",
+"u c #425294",
+"v c #66D224",
+"w c #6672BC",
+"x c #162254",
+"y c #4E5AA4",
+"z c #323E84",
+"A c #62CE24",
+"B c #42528C",
+"C c #6AEA14",
+"D c #56BA1C",
+"E c #26326C",
+"F c #3A468C",
+"G c #727AA4",
+"H c #2E3674",
+"I c #7E8AB4",
+"J c #62E214",
+"K c #1E2A6C",
+"L c #7E82B4",
+"M c #525EA4",
+"N c #263274",
+"O c #3A427C",
+"P c #666EB4",
+"Q c #7AFE14",
+"R c #3E4A94",
+"S c #4E5694",
+"T c #52A22C",
+"U c #4E5A9C",
+"V c #323E74",
+"W c #424E8C",
+"X c #262E6C",
+"Y c #3A467C",
+"Z c #8A96BC",
+"` c #7276A4",
+" . c #5662AC",
+".. c #9AA2CC",
+"+. c #1E2664",
+"@. c #7A82AC",
+"#. c #6AF214",
+"$. c #929AC4",
+"%. c #5A66AC",
+"&. c #86FE1C",
+"*. c #6EC644",
+"=. c #3A4A8C",
+"-. c #2E3A84",
+";. c #8A8EBC",
+">. c #222A74",
+",. c #56A24C",
+"'. c #626EB4",
+"). c #8E96BC",
+"!. c #5666A4",
+"~. c #56AA44",
+"{. c #465294",
+"]. c #66D62C",
+"^. c #1A2264",
+"/. c #4E9E2C",
+"(. c #4A5A9C",
+"_. c #62CA34",
+":. c #6A76BC",
+"<. c #3E4E8C",
+"[. c #6AE624",
+"}. c #56AE34",
+"|. c #222E6C",
+"1. c #8A92BC",
+"2. c #6E76A4",
+"3. c #5662A4",
+"4. c #72FA14",
+"5. c #2A367C",
+"6. c #7E86B4",
+"7. c #62DE14",
+"8. c #465694",
+"9. c #5EC22C",
+"0. c #5ED614",
+"a. c #52AA2C",
+"b. c #46528C",
+"c. c #6EEA24",
+"d. c #5ABA2C",
+"e. c #525AA4",
+"f. c #6AEA24",
+"g. c #1A225C",
+"h. c #5AA244",
+"i. c #9A9ECC",
+"j. c #464E94",
+"k. c #262E74",
+"l. c #82FE1C",
+"m. c #626AB4",
+"n. c #76FE14",
+"o. c #3A428C",
+"p. c #5AA644",
+"q. c #6AD224",
+"r. c #66CE24",
+"s. c #828AB4",
+"t. c #66E214",
+"u. c #565EA4",
+"v. c #424A94",
+"w. c #6EF214",
+"x. c #5E66AC",
+"y. c #3E468C",
+"z. c #767EAC",
+"A. c #8E9AC4",
+"B. c #3A4A84",
+"C. c #6E7AC4",
+"D. c #2E3A7C",
+"E. c #868EBC",
+"F. c #364284",
+"G. c #222A6C",
+"H. c #4E5E9C",
+"I. c #324284",
+"J. c #5266A4",
+"K. c #3E4A8C",
+"L. c #2A327C",
+"M. c #4A569C",
+"N. c #2E367C",
+"O. c #26327C",
+"P. c #3A4284",
+"Q. c #7AFE1C",
+"R. c #4E569C",
+"S. c #323E7C",
+"T. c #3A4684",
+"U. c #1E266C",
+"V. c #8E96C4",
+"W. c #46529C",
+" N p p L.L.N k.2 >.G.K +. ",
+" e b F.F.8 -.D.5.O.k.N k.2 2 G.2 ^.. . . ",
+" H o v.y.o.F.z -.N.N.5.O.N k.>.2 K K K U.^.^.. . ",
+" b U R.s j.v.F q ^ 8 -.D.5.O.k.2 2 2 >.2 2 >.K K < ^.. . . . ",
+" 1 u.M R.! } R F q I.8 N.5.5.5.L.N k.2 >.K K K K >.K K U.U.^.. . ",
+" S m.x. .e.R.{.} v.y.q ^ z -.N.L.N N k.2 |.2 >.>.>.U.K K U.K U.K ^.. . . ",
+" '.'.k 6 u.y M.j.v.y.F q z 8 -.N.L.O.O.k.k.>.|.K K >.U.K K U.K U.K U.< ^.. . ",
+" i :.w m.%. .M y M.W.R =.o.^ 8 # D.5.5.p N N 2 2 >.2 >.K >.U.U.K U.K U.U.U.U.^.. . . ",
+" O 2.w w m.%.3.e.M.! j.v.y.F F.8 -.D.5.O.O.k.2 k.2 >.|.>.K K K U.K U.U.< U.U.< U.^.. . . ",
+" S a w w m.%.6 M U M.{.} K.F q ^ z N.5.L.L.k.O.k.2 G.>.K K K K K K U.K K K U.K K U.K ^.. . . ",
+" O 2.:.P '.k %.u.e.R.W.} R y.o.q z z -.N.{ L.k.k.2 2 |.>.2 >.K K U.>.U.U.U.K < U.K < U.< ^.. . . ",
+" i :.w w P k %.u.M y M.W.} R F F.z z D.N.5.L.N N N k.2 K >.K K U.U.K U.K K U.U.U.< U.U.K U.U.^.. . . ",
+" w :.P '.x.%. .M y M.{.j.v.K.F q z 8 D.D.5.5.O.2 2 2 2 2 K K K >.K K U.U.K K K K U.K U.< U.< < ^.. . ",
+" w w P ..i.%.6 M y M.! j.<.K.o.q ^ 8 -.-.5.5.N k.k.k.>.>.K >.>.>.K >.K K K K K K K K K K K K U.U.K ^.. . ",
+" o P P m.: : u.M e.R.W.j.v.y.F o.F.^ 8 N.5.L.L.N N 2 k.X 2 2 2 2 2 K 2 2 2 2 K >.>.2 >.2 2 >.2 K K K K < ^.. ",
+" m.m.%.%. .M M e.M.! W.} v.=.F F.^ z D.D.5.5.N O.k.k.2 2 >.>.K |.>.2 2 2 2 2 2 2 2 2 |.G.G.|.G.2 G.>.G.K . . ",
+" S %.6 . .u.y y R.8.j.} R y.F o.z z 8 N.N.5.L.N O.N 2 2 k.2 N 2 N k.2 2 2 k.N N N X N N N 2 N |.2 |.|.>.|.K < . ",
+" ^ 6 u.) y.e.V.V.M.W.j.} R R F o.z ^ D.D.D.5.N N N k.2 N k.N N k.k.2 N N N N N k.N N N N N k.N k.N E k.2 X 2 G.G.g.. ",
+" i M e.y F M.l 1.W.} v.R F o.q q z 8 -.5.5.5.L.O.N N k.N N N N N p N p N p N { N N 5.N N { N { N N N E N X N E |.G.< ",
+" y R.R.R.P.! , E.} v.R y.o.q ^ z 8 -.N.N.L.L.N p O.p N N N N O.O.N N O.{ { 5.O.5.{ p 5.{ N { N p p { p N N k.k.|.E g. ",
+" H ! W.W.j.^ j.E.s.R y.o.F P.^ 8 # 8 D.5.5.p L.N N O.5.p L.L.{ { 5.5.{ 5.5.{ D.H 5.D.{ 5.D.5.b 5.5.{ 5.{ { { N p X p |.< ",
+" j.u } } } <.R y.y.F ; o.^ z z S.-.D.5.5.p O.{ L.L.N N 5.N { L.5.{ N.N.N.5.N.5.D.D.5.D.D.5.D.5.b D.{ { { { { { { { E - . ",
+" +.v.v.R R y.y.y.y.o.o.F.^ z z 8 N.5.N.5.L.{ O.N p 5.5.N 5.L.5.{ 5.D.D.D.D.D.D.D.D.# D.D.S.D.# D.D.D.D.D.N.5.{ { { { p { E +. ",
+" b F F y.o.F -.F 6.6.^ z z z -.N.D.N.L.p { O.p L.5.5.p 5.5.b D.5.D.D.5.D.D.D.D.D.D.8 # # D.# 8 # D.# D.D.D.b D.{ 5.{ { p - . ",
+" ^ P.q o.q ^ 5.I.6.6.z 8 8 N.-.N.5.5.O.O.N L.{ N L.5.5.5.5.5.D.D.D.# D.# # # z S.# z S.h S.S.S.S.S.S.S.# S.D.8 b H { { H p E ",
+" ^ q ^ I.^ z L.8 L @.-.D.5.D.5.L.5.N p p L.{ O.5.5.{ N.D.5.D.D.D.D.D.# S.z z S.z F.^ ^ z ^ I.z I.S.z S.S.# S.# D.# D.b { { p ",
+"N z z 8 z z 8 p D.z.@.-.N.5.5.5.N L.O.N N O.{ { 5.5.5.{ D.D.D.-.D.z 8 z S.^ I.d z h I.F.F.F.d F.F.d I.F.^ S.S.S.D.S.D.b b b b { ",
+"N D.-.-.N.-.D.D.-.D.N.5.5.L.p N L.N N O.5.p L.5.5.5.D.D.D.D.D.D.z S.h z z I.F.F.; F.; ; ; F.; F.F.F.F.d I.d F.d h S.S.S.D.b { { ",
+"L.N.N.-.N.5.5.5.L.L.p 5.O.O.N k.k.k.k.N O.{ p 5.b 5.D.D.-.# z S.z z I.d F.F.F.F.T.P.P.; T.T.T.T.; T.; T.F.F.I.^ S.S.S.# D.b b H ",
+"N 5.5.L.5.5.L.|.5.| z.N N N k.N N N N N { O.5.5.5.D.D.D.8 8 # z z F.F.F.F.F.; T.; ; =.T.T.F F ; T.T.F ; ; ; O ; F.F.S.S.S.S.D.D.",
+"p L.5.N L.5.L.>.O.r r > h.p.,.,.,.h.,.h.p.t p.t t t ~.~.~.~.~.}.~.~.,.; ; ; ; F =.T.y.=.B.B.K.=.=.T.T.T.T.; F.; d F.d h S.# D.b ",
+"k.N k.O.N k.N G.N G G f &.&.&.l.l.7 7 7 Q Q Q Q Q n.Q n.n.n.n n.n n n [.= F F =.K.=.=.K.K.K.=.K.o =.K.=.B.=.B.T.; ; F.^ h S.S.D.",
+"k.O.k.k.O.2 N G.N G G % v v v v v v 3 r.r.A A A A $ A A 3 j [.c.n n n 4.w.= B.=.K.<.<.<.<.W <.<.<.<.& K.o _ B.T.T.; ; d d h h S.",
+"2 2 2 2 2 k.2 2 2 2 2 2 G.G.G.G.X |.k.N N 5.b D.D.# # S.^ I.F.F.].[.C 4.n w.a.K.<.K.K.<.<.<.W <.W <.v.<.K.<.K.=.T.T.T.P.d ^ ^ D.",
+">.k.k.2 2 >.>.>.2 >.2 2 |.G.|.G.k.N N 5.{ 5.5.D.D.8 # S.^ F.; F.; F =.3 f.~ #.a.W B } } B B } W W B <.<.K.K.& K.o T.B.; ; d d S.",
+"G.2 2 G.>.k.2 K >.G 2.>.K 2 >.2 N k.p N L.5.b D.D.# z I.I.F.F.F F F F K.=.].w.4 = } B B u u b.B u W B B B & v.& =.B.T.T.T.d d S.",
+"U.K 2 |.K >.G.K G.G 2.G.K K |.K N N N { { 5.5.D.D.8 S.z I.F.; T.B.=.=.<.<.<.].#.4 a.{.{.{.8.8.{.8.u u B u B <.W K.=.B.T.T.; ; d ",
+"K >.>.K >.K K U.K ` G K K >.K 2 2 k.N O.5.5.D.D.S.z S.F.; ; q ; F K.<.K.W W u v 4 C a.( 8.{.{.8.{.8.{.u b.B B <.W & K.B.B.T.O S.",
+" K >.K >.>.G.U.K ` 2.U.K >.|.2 N N { N { D.D.D.# z h F.F.T.F B.=.K.<.<.u W u 8.3 4 * a.@ @ 8.@ 8.8.s 8.{.{.b.B W <.K.B.T.; T. ",
+" K K >.K K K K K K G.>.K K >.|.2 k.N { 5.5.D.D.z h ^ F.; ; T.F =.K.<.<.<.u {.u 8.9 C * [ 8.@ 8.s @ 8.8.( u B W W W <.o o T.; ",
+" . +.K K U.K >.U.K K U.K K >.2 N N L.{ { D.D.8 S.z S.F.F.; =.o <.K.} u B B 8.8.M.8.a.* * [ (.@ @ @ @ 8.s 8.8.b.B W <.<.o T.V ",
+" g.K K K K U.< K 2.2.K K G.|.X k.k.N 5.D.5.D.# z h F.F.F F T.=.=.<.B W u 8.{.8.@ (.@ [ * J [ H.U U @ (.@ 8.( 8.B B W o o Y - ",
+" ^.U.K K U.< U.` ` U.K K K 2 N N N p D.D.D.D.z I.F.F.T.=.F o K.K.} B u 8.8.8.8.(.g @ a.* J [ g U g @ @ 8.8.{.B W W <.B._ ",
+" ^.K U.U.K U.K ' ' K U.>.|.2 k.N p 5.{ D.D.S.S.S.F.; ; T.o K.<.<.B } b.8.s M.@ @ (.H.g [ 7.7.[ H.(.(.(.s ( 8.b.B B & & E ",
+" ^.K U.U.+.U.2.` K U.>.G.|.k.N N 5.{ D.D.z z I.F.F.; F =.K.<.<.B u {.{.@ @ (.(.H.H.H.) [ 7.7.[ g U g @ 8.8.8.B W W W ",
+" U.U.K K < K U.< U.K K |.2 N N { p 5.D.D.D.S.^ I.T.F T.B.K.W W } B 8.8.8.8.@ g (.H.H.) ) [ 7./ 9 /.(.@ @ 8.B B B & O ",
+" . ^.< U.U.U.U.< < U.G.|.|.N N { { 5.D.# S.S.^ h F.; F =.=.<.B } B 8.8.@ (.(.g H.) ) m m m d.7.J / D + @ s {.b.W & _ ",
+" U.U.U.< K 2.` K K K G.2 2 N N 5.{ b 8 D.S.I.F.; T.T.B.K.K.<.B u u {.8.8.@ U H.H.H.) ) J.J.A 7.7.7.0.}.8.( B B 0 ",
+" ^.U.^.U.' 2.U.U.K G.X 2 N N { 5.b # # S.S.F.F.; T.B.K.K.<.W b.8.8.@ M.@ (.H.) ) ) ) ) ) M $ A 3 0./ 8.b.b.W ",
+" < U.K U.' ` 2.` K |.` G r | 5.{ z.z.@.@.F.d 6.6.s.s.=.<.c c E.c {.8.Z Z ).Z H.H.A.A.: A.) g 5 Z *.7.b.( B H ",
+" . U.U.' ' ' ` K |.G G r r { H @.L ] @.z d s.I s.s.o K.c c , 1.{.8.Z Z ).Z H.H.$.).5 5 H.g ).5 1.Z b.B W ",
+" < < ^.U.+.K U.K |.N N N p { H { D.S.S.h d F.T.T.F <.<.<.W B B 8.s @ @ U (.g H.g H.H.g (.@ s ( b.( B ",
+" . ^.< < ^.+.U.G.+.K |.|.{ { N E E N z z { { b b T.=.S.S.S.h b.B F.F.; Y (.@ B.T.T.T.@ U ; Y 8.B i Y ",
+" . U.U.< < K K |.|.|.N N { { N.D.S.S.S.d F.; F B.o K.<.W W } 8.b.( 8.8.s @ @ @ @ @ @ @ s 8.( b.{ ",
+" . g.U.U.U.K G.2 2 N N { { D.b D.S.S.d F.; T.; B.=.<.<.B W u u b.8.8.8.8.s s @ @ 8.( ( b.B Y ",
+" . < < U.U.G.G.X k.N N { { D.D.# S.z S.F.F.T.T.o B.<.K.<.B B u b.( 8.s 8.8.8.( ( 8.b.B H ",
+" . g.K K G.K |.X N N { { b D.# S.h d d F.; ; B._ & & K.W B B u b.B ( ( b.{.{.b.B B Y ",
+" ^.K K |.|.N k.p N { 5.H D.# z S.I.F.P.T.F B.F K.<.& W W W B B u B u B B B B ",
+" . U.K G.X E N N { 5.H b D.S.S.h h F.F.T.B.B.B.K.o <.<.<.W W W B B B & Y ",
+" K |.|.2 E p p { b N.b D.S.S.h ^ ; F.F.T.T.B.B.o & v.<.& <.W & o ",
+" g.G.X |.E N N { N.b D.8 V h ^ d ; d T.T.T.B._ B.o o & o o _ ",
+" e E p p { { { b # 8 # V h ^ d F.P.T.T.B.B.B.B.d ",
+" - - { { { H b b # 8 h d d h ; d F.T.V V ",
+" { { { H b # 8 # S.d ^ h "};
diff --git a/arts/builder/pics/Synth_WAVE_SAW.xpm b/arts/builder/pics/Synth_WAVE_SAW.xpm
new file mode 100644
index 00000000..2f515dc4
--- /dev/null
+++ b/arts/builder/pics/Synth_WAVE_SAW.xpm
@@ -0,0 +1,323 @@
+/* XPM */
+static char * WAVE_SAW_xpm[] = {
+"64 64 256 2",
+" c None",
+". c #161E54",
+"+ c #329224",
+"@ c #2A563C",
+"# c #223A54",
+"$ c #36763C",
+"% c #264E44",
+"& c #3EC60C",
+"* c #4A568C",
+"= c #36AE14",
+"- c #323E6C",
+"; c #5E727C",
+"> c #3A4A74",
+", c #36664C",
+"' c #222E64",
+") c #46E20C",
+"! c #5A66AC",
+"~ c #3A566C",
+"{ c #828EB4",
+"] c #3A4A8C",
+"^ c #324274",
+"/ c #7A82A4",
+"( c #369E1C",
+"_ c #4A5E8C",
+": c #42D60C",
+"< c #6E769C",
+"[ c #263664",
+"} c #3ABA14",
+"| c #2E5E44",
+"1 c #32427C",
+"2 c #2A4254",
+"3 c #464E8C",
+"4 c #3ECE0C",
+"5 c #527A5C",
+"6 c #3A427C",
+"7 c #469244",
+"8 c #324A64",
+"9 c #222E74",
+"0 c #2A3A74",
+"a c #4A569C",
+"b c #46AE2C",
+"c c #42A22C",
+"d c #1A2664",
+"e c #32822C",
+"f c #3A526C",
+"g c #565EA4",
+"h c #727E9C",
+"i c #6672BC",
+"j c #52EE14",
+"k c #42528D",
+"l c #3A5E54",
+"m c #32961C",
+"n c #365664",
+"o c #465693",
+"p c #3AA622",
+"q c #4ACE23",
+"r c #3AB214",
+"s c #6A7A94",
+"t c #424A93",
+"u c #2A3678",
+"v c #4EBA34",
+"w c #365264",
+"x c #2A3A66",
+"y c #323A84",
+"z c #66728C",
+"A c #3A4E74",
+"B c #223269",
+"C c #4E5E9C",
+"D c #52D62C",
+"E c #365E54",
+"F c #364683",
+"G c #3A4683",
+"H c #324E64",
+"I c #222A6C",
+"J c #3A8634",
+"K c #3A8E34",
+"L c #2A5A44",
+"M c #46C61C",
+"N c #525A94",
+"O c #3E6E54",
+"P c #626EB4",
+"Q c #425679",
+"R c #3E9E2C",
+"S c #6E7AC0",
+"T c #2A465C",
+"U c #465290",
+"V c #2A3276",
+"W c #525AA4",
+"X c #32862C",
+"Y c #52D226",
+"Z c #26523C",
+"` c #4A5A94",
+" . c #3A6654",
+".. c #52E61C",
+"+. c #3A4674",
+"@. c #7E8AAC",
+"#. c #36A21C",
+"$. c #3EC214",
+"%. c #32624C",
+"&. c #36428C",
+"*. c #2E3E7C",
+"=. c #4A5A9C",
+"-. c #7A7EAC",
+";. c #5AF624",
+">. c #323E7E",
+",. c #4E629C",
+"'. c #3A468C",
+"). c #162254",
+"!. c #2E7E24",
+"~. c #42AA2C",
+"{. c #5E767C",
+"]. c #3A4A84",
+"^. c #626AB4",
+"/. c #3E4E8C",
+"(. c #324664",
+"_. c #767AA4",
+":. c #2A366B",
+"<. c #325E4B",
+"[. c #364282",
+"}. c #469A44",
+"|. c #364A74",
+"1. c #263274",
+"2. c #2E3A84",
+"3. c #4AB62C",
+"4. c #1E2A6C",
+"5. c #425284",
+"6. c #5662AB",
+"7. c #767EAC",
+"8. c #5AF21C",
+"9. c #4EEA14",
+"0. c #56DE24",
+"a. c #46DE0C",
+"b. c #4EC234",
+"c. c #42B624",
+"d. c #3A625C",
+"e. c #465A84",
+"f. c #263E56",
+"g. c #326A44",
+"h. c #5E6AB4",
+"i. c #7E86AC",
+"j. c #727AA3",
+"k. c #527E5C",
+"l. c #42963C",
+"m. c #52F21C",
+"n. c #369A24",
+"o. c #4ED224",
+"p. c #2E3E6C",
+"q. c #6A768C",
+"r. c #52DA24",
+"s. c #3E923C",
+"t. c #46CA1E",
+"u. c #3EAE24",
+"v. c #4AE60C",
+"w. c #3E5A6C",
+"x. c #3EBE14",
+"y. c #3ED20C",
+"z. c #6A76BC",
+"A. c #3A5A64",
+"B. c #3AB614",
+"C. c #424E8E",
+"D. c #36624C",
+"E. c #425A74",
+"F. c #367A3C",
+"G. c #42DA0C",
+"H. c #46B22C",
+"I. c #3A6A54",
+"J. c #2E564C",
+"K. c #42C61C",
+"L. c #3E527C",
+"M. c #3EA62C",
+"N. c #3E4E84",
+"O. c #46529C",
+"P. c #2A524C",
+"Q. c #1A2264",
+"R. c #364674",
+"S. c #2A4264",
+"T. c #3A428C",
+"U. c #222A74",
+"V. c #36922C",
+"W. c #2A4E4C",
+"X. c #4E5694",
+"Y. c #262E6C",
+"Z. c #3E4A8F",
+"`. c #3A9E24",
+" + c #4E5E94",
+".+ c #364A68",
+"++ c #2E3A7B",
+"@+ c #46A22C",
+"#+ c #1E266B",
+"$+ c #368232",
+"%+ c #767E9F",
+"&+ c #369624",
+"*+ c #4ECE24",
+"=+ c #2E3679",
+"-+ c #2E3A6C",
+";+ c #6A7294",
+">+ c #26326B",
+",+ c #525EA3",
+"'+ c #3E4684",
+")+ c #364E66",
+"!+ c #3E8E38",
+"~+ c #2E5A46",
+"{+ c #2E4660",
+"]+ c #368630",
+"^+ c #2A5244",
+"/+ c #56E624",
+"(+ c #828AB4",
+"_+ c #3AA224",
+":+ c #4E5AA3",
+"<+ c #363E83",
+"[+ c #1A225C",
+"}+ c #327E2C",
+"|+ c #627684",
+"1+ c #36466C",
+"2+ c #4A9A3C",
+"3+ c #4EB634",
+"4+ c #5A62AC",
+"5+ c #3E6264",
+"6+ c #567E64",
+"7+ c #4ACA1C",
+"8+ c #42AE2C",
+"9+ c #5E66AC",
+"0+ c #3E566C",
+"a+ c #868EB4",
+"b+ c #3EBA14",
+"c+ c #262E74",
+"d+ c #4E569C",
+"e+ c #3A5664",
+"f+ c #52BA34",
+"g+ c #3E4E74",
+"h+ c #56D62C",
+"i+ c #666EB4",
+"j+ c #42C214",
+"k+ c #52629C",
+"l+ c #3E468C",
+"m+ c #3E4A84",
+"n+ c #465284",
+"o+ c #366A44",
+"p+ c #56F21C",
+"q+ c #42D20C",
+"r+ c #3EB614",
+"s+ c #4A529C",
+"t+ c #2A5644",
+"u+ c #4A5694",
+"v+ c #323E74",
+"w+ c #5E7284",
+"x+ c #3A4A7C",
+"y+ c #222E6C",
+"z+ c #7A82AC",
+"A+ c #369E24",
+"B+ c #26366C",
+"C+ c #2E5E4C",
+"D+ c #324284",
+"E+ c #2A425C",
+"F+ c #464E94",
+"G+ c #42A234",
+" 1.V V V V 1.c+9 U.I 4.#+ ",
+" I ++[.[.y 2.2.u 1.c+1.c+9 9 I y+[+. . . ",
+" =+m+t '+T.[.>.2.=+c+u S %+c+U.9 4.4.4.#+Q.Q.. . ",
+" ++N d+u+F+t '.&.<+y 2.2.c+V 7.j.9 9 U.9 9 U.4.4.d Q.. . . . ",
+" N g g d+s+C.Z.'.&.D+y =+0 9 V _.7.c+9 U.4.4.4.4.U.4.4.#+#+Q.. . ",
+" X.^.9+6.:+d+O.F+t l+&.<+>.2.=+c+1.7.j.9 9 9 U.U.U.#+4.4.#+4.#+4.Q.. . . ",
+" i+P h.4+g :+a F+t l+'.&.>.y 2.u u 1.1.c+c+U.9 4.4.U.#+4.U.#+4.#+4.#+d Q.. . ",
+" 3 z.i ^.! 6.,+:+a O.Z.] T.<+y *.=+u V V c+1.9 y+U.9 U.4.4.#+#+4.#+4.#+#+#+#+Q.. . . ",
+" 6 < i i ^.! 6.W a O.F+t l+'.&.y 2.=+u 9 V S _.c+9 U.9 U.4.4.U.#+4.#+#+d #+#+#+#+Q.. . . ",
+" X.S i i h.! 6.,+:+a O.F+Z.'.&.<+>.2.2.u U.1._.j.9 I U.4.4.4.4.4.4.#+4.4.4.d 4.4.#+4.Q.. . . ",
+" 6 ;+z.i+i+h.! g W d+O.C.Z.'.T.&.>.y =+=+u U.1.j.j.9 y+U.9 4.4.U.#+4.#+d #+4.#+#+4.d #+d Q.. . . ",
+" 3 S z.i i+h.! g ,+:+a O.F+Z.l+&.<+>.2.2.u V 9 c+_.j.9 4.U.4.U.#+#+4.#+4.4.#+d #+d #+#+4.#+#+Q.. . . ",
+" i+i i+P 9+! 6.,+:+a O.F+Z.] '.&.>.2.2.=+u V 1.c+c+9 9 9 U.4.4.U.4.4.#+#+4.4.4.4.#+4.#+#+#+d d Q.. . ",
+" i i i P h.! 4+g W a O.t Z.'.T.&.<+y y u u u 1.1.1.9 U.U.4.4.U.4.U.U.4.U.4.U.4.U.4.4.4.4.4.4.#+#+4.Q.. . ",
+" m+i+^.^.9+! ,+,+:+d+s+U Z.l+'.&.>.<+>.2.u V V 1.U.c+_._.9 9 9 9 9 4.9 9 9 9 4.U.4.9 4.9 9 4.9 4.4.4.4.d Q.. ",
+" ^.^.9+! 6.,+,+:+d+O.C.m+N.l R.<+>.*.2.=+=+u V 1.4.9 j.j.U.U.4.9 U.9 9 9 9 9 9 9 c+9 c+U.I 9 I 9 I U.I 4.. . ",
+" X.! ! 6.g g W :+a s+C.Z.> d.2+E v+y 2.2.u u u c+c+U.c+j._.9 1.9 1.9 9 9 9 c+1.1.1.9 B 1.1.y+1.y+9 y+y+U.y+4.d . ",
+" <+4+6.,+W ,+:+:+a O.C.t f I.7 b.!+, T =+V u 1.1.1.1.9 1.j.7.1.9 c+9 1.1.1.1.1.c+1.1.1.1.1.c+1.c+1.>+c+9 Y.9 I I [+. ",
+" 3 W W :+d+a s+s+F+C.m+m+O v Y 8.*+H.C+x V V 1.V 1.c+c+1.c+1.1.1.V 1.1.1.V 1.B+1.1.u V 1.B+V B+1.1.1.1.1.Y.1.>+y+I d ",
+" :+d+d+a a s+O.C./.'+f O 2+Y 0./+0.*+K C+f.1.c+1.V 1.1.V 1.1.V 1.1.V V u u u V u u V u u V u V V V B+V 1.1.c+c+Y.B [+ ",
+" :.a o s+O.O.C.F+Z.] R.I.3+h+;./+D ..m.t.u.L >+1.9 1.u U.u _.7.u u u u u u u 0 u u 0 u u 0 u 0 u u u u u u B+1.1.c+V y+d ",
+" F+F+F+F+t Z.Z.'.6 )+I.l.D Y *+G+F.R 7+7+t.X L # 1.1.1.9 1.7.7.u u u u u u =+=+2.2.=+++2.=+++=+++++=+u u u u u u :.>+' . ",
+" d Z.Z.Z.Z.l+'.'.F R.I.3+Y 8.Y b g.E+g.u.M j K.p t+[ >+u 9 u 7.7.u 2.++2.2.2.2.++++*.2.++*.++*.++++++++++=+=+=+u u u V :.>+d ",
+" y l+'.'.'.'.&.<+.+ .s.o.Y o.K C+T B+E+L X K.K.K.X t+# >+Y.u z+z+2.0 u ++++++++++y ++*.>.2.*.y *.++*.++++++++0 u =+:.u V Y.. ",
+" <+T.&.&.&.[.1 ^ , 3.Y p+*+b C+:.>+1.B [ L p j+v.$.p t+[ :.u u 0 2.*.2.*.*.*.>.*.>.>.>.1 >.>.>.>.>.>.>.*.*.++++++=+=+u :.V >+ ",
+" D+<+<+<+y y (.%.s.o.o.*+K | 2 B+1.V c+>+# t+X $.$.x.e @ f.:.=+=+y y >.>.>.>.>.<+D+<+>.<+<+D+<+D+>.>.>.>.v+>.v+++*.++++=+:.V ",
+"c+>.>.2.*.++x %.H.*+m.q 8+| [ >+V 1.1.c+1.B B @ _+$.) x.#.Z x j./ *.y >.>.<+D+D+<+D+[.D+[.[.D+[.[.[.1 [.>.<+>.>.++*.++++++x 0 :.",
+"V 2.2.=+=+E+<.K q q 7+K | f.1.c+1.1.V 1.1.>+>+# t+e x.x.x.!.@ ; < ++*.>.>.D+[.[.F [.F F F [.G [.[.[.[.[.[.1 [.1 ^ >.v+*.++++=+u ",
+"V =+2.u V P.M.t.j t.~.L B >+1.c+c+1.1.1.V u u >+[ t+#.x.a.x.( k.w+v+>.&.[.&.[.[.'.&.6 F F F '.F F G F F [.[.[.<+>.>.>.v+y ++++=+",
+"u u c+I ' % M.c.K.X @ # >+y+U.4.U.4.1.1.9 9 U.y+1.f.Z !.x.} } 2+k.E+v+>.&.[.F F F '.] '.'.'.G '.G '.G G F 1 >.1 1 ^ >.>.v+v+++0 ",
+"V 1.V 1.>+W.`._+p t+B ' 9 9 1.9 c+c+1.1.1.u u u 0 0 [ t+#.x.G.} A+L ^ ^ F &.'.'.].'.'+m+].] ].] ].'.'.G G F ^ ^ v+v+1 >.>.>.++++",
+"1.V 7.< < ; t+^+k.; z ;+c+c+j.j.j.j.c+V 7.7.7.7.u u s ; 5 l.} b+} e 5 ; h i.G ] (+{ { (+Z.Z.a+a+{ { ].G %+|+6+5 J.{+-./ i.i.v+++",
+"9 c+7.j.< < B y+z ;+_.j.9 U._.j.j.7.1.V %+7.z+7.2.u z+s q.k.( b+G.} 8+k.q.%+G ] (+(+{ a+/.Z.(+(+(+(+].F s 5 @+c K t+h / i.i.[.v+",
+"9 1.c+c+c+U.9 9 9 c+U.9 U.9 9 U.c+c+c+1.V u u ++++*.*.++++2 L e } } r+e | 8 F G Z./.C./././.k C.C.m+> H ~+e r #.+ J.^ v+[.[.>.++",
+"U.9 U.9 9 U.y+U.9 9 U.9 U.4.9 9 9 1.1.u u u u 2.++2.>.*.>.v+S.k.b } : B.n.@ (.].>.*.>.>.k C.1 >.>.++1+<.m r 4 = m ~+x :.u =+[.>.",
+"4.U.9 U.9 c+U.9 4.4.4.4.y+U.U.9 1.9 1.1.u u ++u 2.>.>.<+D+1 v+{.k.e B.} } X %.8 x+Z.k U U k C.N.N.)+<.e r r r }+~+(.F F G F [.>.",
+"4.U.9 I 4.I I U.U.9 U.4.4.I I U.1.9 1.1.u u ++++++y 1 >.[.[.[.h q.| n.} : B.( <.> m+k k o o U m+.+%.n.r 4 r n.~+(.F G G G F F ^ ",
+"d y+4.4.U.9 4.I I 4.4.4.U.4.9 9 9 1.V u u =+++++>.>.>.[.D+G F / h 8 @ $+B.b+b+]+%.H N.k U C.L.w D.e r+B.r e | )+x+G m+x+G G 6 <+",
+" U.U.4.4.U.4.4.U.U.#+4.4.4.y+c+c+1.1.V u u 2.2.*.>.D+[.[.&.'.].G R.(.<.( B.y.B.`.%.> N.o N.A %.`.r 4 B.n.<..+x+C.m+Z.].G F G ",
+" 4.4.U.4.4.4.4.#+4.U.4.4.U.4.9 9 c+V u u ++++++>.>.<+&.[.G G '.] ].].H <.X b+r+} $+%.n L.e+D.$+r B.B.e <.w m+m+C./.N.m+m+G F ",
+" . 4.#+4.U.4.4.4.4.#+#+#+U.I c+1.1.1.u u 0 2.2.>.>.D+F F F ] (+(+Z.v+].> <.( r+q+b+`.g.f g.A+B.4 r n.D.> L.o k k C./.N.m+G v+ ",
+" [+#+U.#+4.4.4.#+#+4.4.4.4.9 9 9 1.u u u =+++>.*.D+<+&.T.'.G { { /.*.N.].)+%.$+} B.B.V.$ V.r+B.B.J , e+5.U U U k k C.Z.Z.+.' ",
+" ).4.#+#+#+4.4.#+#+4.4.4.y+9 9 c+1.u 2.++++>.>.D+D+F G ] '.(+(+t >.k k N.A D.`.B.q+& x.$.4 r+A+D.L.n+o o o o k k C.N.].'+ ",
+" d #+4.4.4.#+#+4.4.#+#+U.4.c+1.1.1.u u 0 ++2.>.<+[.[.F G Z.a+a+/.<+k k 5.k w D.]+B.x.& x.r+$+D.0+o ` ` ` ` U U C.k N.N.>+ ",
+" Q.#+#+4.4.#+#+d 4.#+I U.9 1.1.V u =+++>.>.1 >.D+[.F G ] Z././.C.k o k o k N.D.A+r+y.r+`. .Q o ` N ` o ` o o k C.C.C. ",
+" #+4.4.#+#+4.4.4.#+4.4.y+Y.9 1.u V u ++++++>.[.[.G '.] G /./.C.k U U o o Q * 0+ .}+p }+I.w.` + +C =.` o o k k 5.N.6 ",
+" . Q.#+#+#+#+#+#+d 4.4.4.c+1.1.B+u u ++*.2.>.D+D+[.[.] G (+{ C.>.k O.O.o o ` 5.Q l J d.e._ k+k+,. +C N ` o U U C.N.'+ ",
+" d 4.4.d #+d 4.4.#+U.y+9 9 1.1.u u u y >.>.<+[.F G G ] a+a+/.D+k o o a ` =.` o Q 5+E._ +k+,.,+C ` ` o o o k k > ",
+" . #+#+#+#+#+4.#+I y+c+9 1.1.u ++0 ++++>.>.[.[.F G ] (+a+C.>.C.U o o o ` N _ ` ` +k+k+k+,.,.C _ ` ` o U n+C. ",
+" d 4.4.4.#+d #+4.4.4.y+Y.1.V u u =+++>.*.[.[.F 6 ] G a+(+/.[.k o o o ` =.=.=.=._ C k+,.k+k+,+ +:+` * U o 5.:. ",
+" . #+d #+d d #+I U.9 c+1.1.V u u ++>.*.>.>.F &.'.'.Z.m+Z.C.C.k o o o ` N _ :+C C k+k+C +C ` N ` o k U C. ",
+" #+d #+#+#+#+4.4.y+y+1.1.B+u ++++++y >.[.D+F G G ].Z././.k k U U o =.=.:+_ C C C C +C ` ` ` o o o 5. ",
+" . Q.d #+d #+4.4.y+y+c+V u u u ++*.*.>.<+[.[.F G { { N.*.C.k k U U o o ` ` _ C =.:+` _ =.u+u+o U C.+. ",
+" ).#+d #+d #+I I 9 1.>+1.1.u u ++y *.>.1 F F F (+(+Z.>.C./.k o o o o a a u+` ` ` ` u+` ` o o 5.:. ",
+" . ).#+#+#+4.I I 9 1.:.u =+=+++y v+>.>.[.G [.(+(+m+*.C.C.C.k k o o o ` o ` ` ` ` o o o U 5.+. ",
+" . d d d 4.I y+Y.c+1.>+u :.++*.*.>.1 [.[.F (+(+].++Z././.C.k U U o U o o o o o o U U 5.:. ",
+" . [+#+4.4.4.y+Y.1.1.u u u ++y y >.1 [.[.F G m+].m+/./.C.C.k k k U U U U U U o Q k '+ ",
+" Q.#+I y+y+9 c+>+u :.=+=+++v+*.>.[.[.[.[.F ].] m+m+/.N./.C.k k k k k k k k C. ",
+" ).#+I y+Y.1.Y.1.u :.0 ++y v+>.>.[.i.i.6 ++].].m+/.N.C.C.C.C.k k k k N.'+ ",
+" 4.I y+Y.1.V B+u u 0 ++y *.1 1 i.(+F ++G ].'.m+m+m+N.N.N.C.C.C.m+ ",
+" d 4.y+B c+V u :.0 0 ++++>.[.z+i.[.u 6 G G G ].Z.m+m+m+m+m+> ",
+" I >+>+V V =+:.++++++*.i.i.[.++F G F G G G ].].^ ",
+" I ' >+=+=+-+++++v+y v+>.>.1 [.F G F >.[. ",
+" V :.=+:.++*.++>.v+<+^ [. "};
diff --git a/arts/builder/pics/Synth_WAVE_SIN.xpm b/arts/builder/pics/Synth_WAVE_SIN.xpm
new file mode 100644
index 00000000..66500375
--- /dev/null
+++ b/arts/builder/pics/Synth_WAVE_SIN.xpm
@@ -0,0 +1,320 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"64 64 249 2",
+/* colors */
+" c #6B798A",
+" . c #445292",
+" X c #323E7D",
+" o c #303C7B",
+" O c #3E4C8C",
+" + c #2E3A79",
+" @ c #60C225",
+" # c #2C3877",
+" $ c #2A3675",
+" % c #283473",
+" & c #384486",
+" * c #5B637A",
+" = c #364484",
+" - c #263071",
+" ; c #354283",
+" : c #404B70",
+" > c #334081",
+" , c #838CB5",
+" < c #303C7E",
+" 1 c #4FAD1E",
+" 2 c #818AB3",
+" 3 c #6EE32F",
+" 4 c #72DB33",
+" 5 c #1E2869",
+" 6 c #1C2667",
+" 7 c #66B02E",
+" 8 c #689044",
+" 9 c #374288",
+" 0 c #253073",
+" q c #232E71",
+" w c #343E85",
+" e c #222C70",
+" r c #4FBA17",
+" t c #202A6E",
+" y c #1F2A6D",
+" u c #1E286C",
+" i c #1D286B",
+" p c #2E387F",
+" a c #1A235E",
+" s c #384582",
+" d c #5E8347",
+" f c #435190",
+" g c #333F7D",
+" h c #2F3B79",
+" j c #3E4B8B",
+" k c #606BB3",
+" l c #2E3978",
+" z c #58744B",
+" x c #4E599E",
+" c c #3B4988",
+" v c #2B3775",
+" b c #5C67AF",
+" n c #394786",
+" m c #527245",
+" M c #3A487D",
+" N c #273171",
+" B c #333F80",
+" V c #323F7F",
+" C c #485384",
+" Z c #2F3B7C",
+" A c #2D397A",
+" S c #404C86",
+" D c #2A3577",
+" F c #7A81AB",
+" G c #495892",
+" H c #38447E",
+" J c #202B6D",
+" K c #313B81",
+" L c #1E296B",
+" P c #1D276A",
+" I c #1C2769",
+" U c #2C377C",
+" Y c #293379",
+" T c #283378",
+" R c #4A5A96",
+" E c #263176",
+" W c #242F74",
+" Q c #475693",
+" ! c #232D73",
+" ~ c #455491",
+" ^ c #394378",
+" / c #212B71",
+" ( c #202B70",
+" ) c #42508E",
+" _ c #323C7B",
+" ` c #404E8C",
+" ' c #58D01B",
+" ] c #3A4686",
+" [ c #53C616",
+" { c #364282",
+" } c #25306E",
+" | c #344080",
+". c #242E6D",
+".. c #222C6B",
+".X c #313E7D",
+".o c #424E91",
+".O c #303C7C",
+".+ c #202A69",
+".@ c #2C3878",
+".# c #1B2464",
+".$ c #5BBA21",
+".% c #354284",
+".& c #253071",
+".* c #242E70",
+".= c #334082",
+".- c #232E6F",
+".; c #232C6F",
+".: c #343F79",
+".> c #202A6C",
+"., c #1F2A6B",
+".< c #2C387B",
+".1 c #1C2668",
+".2 c #798881",
+".3 c #688459",
+".4 c #293478",
+".5 c #283477",
+".6 c #2C3571",
+".7 c #283277",
+".8 c #757CA8",
+".9 c #222C71",
+".0 c #212C70",
+".q c #202A6F",
+".w c #1E286D",
+".e c #505B9E",
+".r c #6FD334",
+".t c #475595",
+".y c #364381",
+".u c #509F26",
+".i c #2D3978",
+".p c #3A4788",
+".a c #273372",
+".s c None",
+".d c #344182",
+".f c #48548F",
+".g c #222D6D",
+".h c #2E3B7C",
+".j c #2D397B",
+".k c #3E498F",
+".l c #1D2768",
+".z c #2B3779",
+".x c #4B5374",
+".c c #2F3569",
+".v c #263174",
+".b c #486253",
+".n c #242F72",
+".m c #475691",
+".M c #313D82",
+".N c #43528D",
+".B c #1F296D",
+".V c #42508C",
+".C c #1E296C",
+".Z c #1D276B",
+".A c #404E8A",
+".S c #2C377D",
+".D c #4B5698",
+".F c #495896",
+".G c #6872BB",
+".H c #525C77",
+".J c #41508E",
+".K c #535EA3",
+".L c #303C7A",
+".P c #1D2664",
+".I c #3B4888",
+".U c #2A3474",
+".Y c #48546D",
+".T c #394686",
+".R c #5964AC",
+".E c #526B45",
+".W c #364083",
+".Q c #323E7F",
+".! c #747AA2",
+".~ c #838CB4",
+".^ c #2F3C7C",
+"./ c #2E3A7B",
+".( c #2B3678",
+".) c #3B468B",
+"._ c #283475",
+".` c #263273",
+".' c #47529A",
+".] c #4A6A4A",
+".[ c #242E71",
+".{ c #58B21F",
+".} c #171F57",
+".| c #7F8E88",
+"X c #212C6E",
+"X. c #2E3A7E",
+"XX c #1E286B",
+"Xo c #1C2669",
+"XO c #2B367B",
+"X+ c #2A347A",
+"X@ c #273277",
+"X# c #525B8A",
+"X$ c #253075",
+"X% c #242E74",
+"X& c #232E73",
+"X* c #222C72",
+"X= c #5761A6",
+"X- c #212C71",
+"X; c #445390",
+"X: c #51B41B",
+"X> c #1F2A6F",
+"X, c #434F8F",
+"X< c #313D7A",
+"X1 c #3E4D8A",
+"X2 c #529030",
+"X3 c #2B3574",
+"X4 c #384584",
+"X5 c #55BF18",
+"X6 c #364382",
+"X7 c #344180",
+"X8 c #3B4469",
+"X9 c #8089B0",
+"X0 c #547153",
+"Xq c #6C9946",
+"Xw c #283374",
+"Xe c #374586",
+"Xr c #364385",
+"Xt c #263172",
+"Xy c #464D6D",
+"Xu c #4A5988",
+"Xi c #303D7F",
+"Xp c #2F3B7E",
+"Xa c #1E296A",
+"Xs c #4F5579",
+"Xd c #2D397C",
+"Xf c #1D2769",
+"Xg c #587946",
+"Xh c #2B377A",
+"Xj c #2A3579",
+"Xk c #3D4A85",
+"Xl c #32396D",
+"Xz c #283377",
+"Xx c #273376",
+"Xc c #273176",
+"Xv c #677197",
+"Xb c #263175",
+"Xn c #252F74",
+"Xm c #7D819C",
+"XM c #465491",
+"XN c #222D71",
+"XB c #222B71",
+"XV c #212B70",
+"XC c #1F296E",
+"XZ c #1E296D",
+"XA c #3E4C89",
+"XS c #373E6B",
+"XD c #43527A",
+"XF c #3B4886",
+"XG c #2B3673",
+/* pixels */
+".s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.sXt.7Xz Y.7 E WX%XB t L.l.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.sXG _ 9 w.M K.SXO YX@X$ W !X*X- (.q.B.# a.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.sXF.o.o.k 9 w.M K.S.[ Y.8.8 W !X*X- (.qX>XCXCXX a.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s H x x.' ..k.) 9 w.M K.S.[ Y.8.8X% !X*X- (.qX>XCXZ.w.w uXo a.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.sX=.R.K x.'.o.k.) 9 w.M p.S.[ Y.8.8X% !X* / (.qXCXCXZ.w u u u.Z.1.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.s.s.s.s.s.s.s.D k bX=.K x.'.o.k.) 9 w.M p.S.[ Y.8.8X% !X* / (.qXCXCXZ.w u u i.Z.ZXo a.s.s.s.s.s.s.s.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.s.s.s.s.s.s.G.G k.RX= x x.'.o.k.) 9 w K p.SX+ Y EX$X% !.9XV.q.qXCXCXZ.w u u.Z.Z.Z P P 6.s.s.s.s.s.s.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.s.s.s.s.V.G.G k b.R.K x.'.'.o.k.) 9 w K pXOX+.7 EX$X&X*X-XV.qX>XC.B.w u u u.Z.Z P P P P.1.}.s.s.s.s.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.s.s.s G.G.G.G k.RX=.K x.'.o.k.) 9 w.M K pXO.-X@.8.8X&X*X-XV.qXCXCXZ.w u u i.Z.Z P P PXfXo 6.}.s.s.s.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.s.s R.G.G.G k b.R.K x.D.'.o.k.) 9 w.M p.SXO.-X@.8.8 !X* / (.qXCXCXZ.w u u i.Z.Z P P PXfXoXo 6.}.s.s.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.s G.G.G.G k k.RX=.K x.' ..o.k.) 9 w K p UX+.; E.8.8 !.9XV.q.qXC.BXZ u u i.Z.Z P P PXf IXoXo.1 6.}.s.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.V.G.G.G k k.RX=.K x.D.'.o.k.) 9 w.MX. pXOX+ e E.8.8X*X-XV.qXCXC.B.w u u uXX.Z.Z P P PXfXfXo.1.1 6.}.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.G.G.G k k bX=.K x.D.fX,.AXFX4.W w K p.SXO YX@X$ W !X*X- (.qXCXC.BXZXZ u.C.C.CXXXXXXXXXX PXfXf.1.1 6.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.G.G.G k k.RX=.K.eX#X#X#X# C C M H.M p pXOX+.7 EX$X% !.9XV (.q.q t t t t t t t J J J.> y.B.,XXXXXXXf.l.#.s.s.s.s.s.s",
+".s.s.s.s.s.D.G k k b.RX=.K.e .2.3.3.3.3.3X0.Y.: l UXO YX@ J W.8.!.9.0.0.0.0.0.0.0.0.0.0 e e.0X X X J J.>.>.,XaXa 5 a.s.s.s.s.s",
+".s.s.s.s.s k k b.R.RX=.KXv .2.r 4 4 4 4.r.r zX8XlXO Y.7 E JX%.8.! !XNXNXN q q q q q q q q q.-.-.-.-.-.gX X J J.,., 5.s.s.s.s.s",
+".s.s.s.sX= b.R.RX=.K.eX# .2Xq 4 4 4 4 4.r.rXq zXyXl.6Xc E.>X%.8.8X%.n.n.n.n.n.n 0 0 0 0 0.&.&.n.[.[.[.*.-.-.g.gX J., 5.s.s.s.s",
+".s.s.s H.RX=X=.K.e xX# *.3 4 4 4 4 4.r 3 3 3.r @ mX8Xl.vXn JXn.8.8 0 0 0 0.v.v.v.v.v.v.v.v.v.v.`XtXtXt.&.&.&.*.-.-.g.g...P.s.s.s",
+".s.s.s x.K.K x x.DX# *.3Xq 4Xq.3.3X0 z d d d 7 @X2.EX8.c NX$XbXbXbXbXbX@X@X@XxXxXxXxXxXxXx.5._._._Xw.v.v.`XtXt.&.&.-.-.g.+.s.s.s",
+".s.s.s x x x.'.'.f *.3.r 4 4.3.x.x M ^ ^ :X8X2 ' ' @.EXS.c.vXcX@X@X@XzXz.5.4.4 D.4.4 D D D D D D D D.5._._Xw.a.aXt -.&.-.-.s.s.s",
+".s.sXF.'.'.' .X,.H.3Xq 4 4.r.3Xy ^ X _ hXlX8 m @ ' 'X2.EX8. Xw.8.8.4.4 D DXjXj.z.z.z.z.z.z.z.z.z.z.z.( D D._._._ %.a N N }.P.s.s",
+".s.s.o ..o.o.k SX0.r 4 3 4.rX0 ^ _ K p p vXl.].{ ' ' '.{.].c.U.8.8XjXjXhXhXh.<.<.<.j.j.j.j.j A A A.@.@.@.z #.( D._.U %.aXt }.s.s",
+".sXG.o.k.k.k.I SX0.r 3 3 7.3.Y gXp p p U D.6X8.]X2 ' [.$.E.cXl.8 FXh.<.<.jXdXd./././.h Z Z Z Z Z./././ A.i.@.@ # $ $.U %.a N.+.s",
+".s _.k.).).) ]XD.3 4 3 3 8.Y ^X. p.SXOX+.7Xx.cXS.].{.$ ' mX8Xl.8 F UXdXdX..hXpXp < < <XiXiXi.O.O.O.O.^ Z Z./ +.i.@ # $ $.U %.+.s",
+".s 9 9 9 9 9 H.x.3 3.r.r zX8 l.S UXOX+ Y.7X@.6.cX8.EX2 @X2.E.bXl lXd.hXpXp <XiXi.Q.Q.Q.Q V.Q.Q.Q.Q.X.X.X.O.O Z h +.i # v $.U.a.s",
+".s w w w w w ^.H 8 3.r 7.bXl.(XOXO.4 TX@.7 T.7.4XlX8.E.$.$.{.EX8Xl ZXpXiXi.Q.Q V B > > > > | > > | | V V.Q.X.O.O Z./ +.i # $.U.s",
+"Xt.M.M.M < g.Y.3 7 3 7 zX8 v DX+ Y.7X@X@X@ T.4.4X3Xl.b mX2X5X2.3.2X<.O.Q B B.=.d.d ; ; ; ; ; ; ; ;.yX7X7 | V V X.O o h +.i # v %",
+".7 K K K l.:X0 @ @ ' dXyXl.4.7.7X@ EXcX@X@ T.4XjXj.zXSX8 m [X5 7.3 ^X<.=.=.d ;.%XrXrXr = = = = =X6X6X6 ;.yX7 | | V.XX<.L h l #XG",
+"Xz.S.S.S #XS.E @ ' 'XgX8.6X@X@ E EX$ EX@Xz.4.4X+XOXhXlX8 mX5X5 @.3X8.:.= ;.%XrXrXr & & & &.T &X4X4X4X4 =X6X6.y | g.:X< o.L h l v",
+" YXO.[.[. .c m ' ' '.E.c N E J J.> J EX@.g.-.-.& U U }.c.] 1X5 'XqXy ^ {.%XrXe &.T.T n.p.p.p.p n n n.TX4X4 sX6.y ^ ^ ^ gX<.L h #",
+".7 Y Y Y.UXS.E @.$.$.].c }X$ WX%X%XnXbX@Xz.4XjXh.<Xd lX<X8.EX2 [X2.E.Y M s &.T.p.I.I.I c c c c c c.IXF.p nX4 H :.Y.bXy ^X<.X.L l",
+" EX@.8.8.!Xm.E.{ 7 7.3Xv. X%.8.8.8.8XbX@.8.8 F FXdX. F FXm.2 m [.$.{.3Xm F 2.I c.~.~.~ , j j.~.~.~.~ c cX9X9Xm.|.]X2.2Xm F FX< h",
+" WX$.8.8.!.!X8.E.3.3 .! e !.!.!.8.8XbX@.8.8 F FXdX. F F.!Xm.EX:X:.$.3XmXm 2 c j , , , , O ` , , , ,X1 jX9X9.2.3X2.u.3XmXmX9 g.L",
+"X% W WX% !.[ N.c.c.c. e /.9.9 !X% 0XbX@.4XjXh UX.XpXi.Q.: ^.] 1X: [Xg.YXD c j O O ` ` ` ` ` ` ` ` ` `X1XkXD.] 1 1 r m : ^X7 gX<",
+"XB ! ! ! !X*.-. . ..X (X-XV.0XN.n 0XbXz.4Xj.<Xd.hXiXi B g ^Xy.3XqX5X2.E.bXSXk O X g g g ) ) g g g g.JX1XlX8.E 1 1 1.]XS.6 vX7 X",
+" tX*X*X*X*X*.9X-X- /XVXV ( (.0XN.n 0X@Xz DXh.<XdXpXi.Q.=.= { ^Xm.3.{.{.{ m.xXD `.J f f f f f f f f f.J `XD.Y m r 1.u.] ^ H.yX7 g",
+" LX-X-X- / /XVXVXV (.q.q.q.q.0XN.n 0X@.5 DXh.jX.XpXi B.= ;.% HXm.2 mX2X5X2.E.Y.A ` f . . ~ ~ ~ ~X;X;.V S.Y.EX2X5X2.EXy M H s.y g",
+".l ( ( ( ( (.q.q.q.q.qXCXC.q.0 q.n.vX@.4XjXhXd.h <.Q B.d.%Xr &X9Xm.Y m [X: 1X0 C.V . ~ Q Q.t.t Q QXM.V C.].u 1X5Xg.Y :XF s s.y g",
+".s.q.q.q.q.q.qX>XCXCXCXCXC t.0 q.n.vXx.4Xj.<XdXpXi.Q.= ;XrXe.T.p M.xXgX5X:X: zXs C ~.t.t.t.F.F.F Q Q CXs m 1X: r m.x MXFXF s s.s",
+".s.BX>X>XCXCXCXCXCXC.B.B.B t.0 q.n.vXx D.z.<./XpXi V.d.%Xr &.p cXkXD z.{.{X5Xg.H C Q Q.F.F.F.F.F.F Q C.x m r r r mXDXkXkXF ] s.s",
+".s.#XCXCXCXCXC.BXZXZXZ.wXZ t.0 q 0.vXx.4.z.<./ <.Q B.dXrXr.T.I.~X9 SX8 zX2.$X2 z.HXu.f.F R R R R G G.Y mX2 [ r 1.]XDXkXAXkXF.:.s",
+".s aXCXZXZXZXZ.w.w.w u uXZ t.0 q 0.vXx.4.z.j./ <.Q > ;Xr &.T.I.~.~ jXS.x z.{.{.{ z.HXu R R R R R GXuX0.u r [ r.u.bXDXkXAXkXF.:.s",
+".s.sXX.w.w.w.w u u u u u u t.0 q 0.vXx D.z.j.h <.Q > ;Xr & n.I.~ , O.: C.Y zX2.$X2 z.HX# G x.e.eX#X# z 1X: rX2.].Y S SX1XkXF.s.s",
+".s.s a.w u u u u u u i u.C t.0 q 0.vXx D.z.j ZXi.Q > ; = &.p c , , ` g ) C.H dX5.$.{ z.HX#.e.e.eX#.HXgX5.{.{ z.xXD.V.AX1XA ^.s.s",
+".s.s.s u u u u u i i.ZXX.C t e q 0.vXx D.z.j ZXi V > ; = &.p c j O ` ) f CXs zX: rX5X2 z *X#X#X# * zX2X5X2 z.Y C.V.V.V.AXA.s.s.s",
+".s.s.sXo u i.Z.Z.Z.Z.Z.Z.C J e q.&.v.5 D.z.j ZXi.Q | ; =.T.p c j ` ` ) f.N CX0.u r [X5.{ d * * * d.{.{X5 d.HXu.mX;.N.V.AXk.s.s.s",
+".s.s.s a.Z.Z.Z.Z.Z.Z P.ZXX J.0.-.&.v._ D.z A Z.O.Q > ; = &.p c.~ , ` g f.N.f.Y.]X2 r r.$X2 d d dX2.{.{ 1 z.HXuXM ~.N.V.A M.s.s.s",
+".s.s.s.s.1.Z.Z P P P P PXX JX .-.n.`._ D.z A Z.O.Q > ; =X4 n c.~ , ` g f . ~ C.x.].u 1X5X5X5X5X5X5X5 1.uX0Xu.f.m ~.N.V S.s.s.s.s",
+".s.s.s.s.sXo P P P P P PXX.>X .-.[Xt._ D.z A./.O.Q | ;X6X4 n c.~ , ` g fX; ~.N C.YX0 zXgX2X: 1 1X2Xg zX0.HXu.f.mX;.N.A.s.s.s.s.s",
+".s.s.s.s.s a P P P PXf PXX yX .-.[XtXw D.z.@./.O.X |.yX6X4 n.I.~ , ` g fX;XM Q Q GXuX#.H z.u.u.u z *X#X# G G.mXMX;.N M.s.s.s.s.s",
+".s.s.s.s.s.s 6 PXfXf IXfXX.B J.-.[Xt.v.5.(.@./.^.X VX7X6X4.TXF cX1 `.J fX; ~ Q Q.m.mXuXu.H.b.b.b.HX#Xu G G Q.mXM.N.A.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.1XoXoXoXf P., J.g.*.&.v._ D.@ A Z.X VX7 ; =X4.p c jX1 `.J fX;XM Q.F R R R GXuXuXu G R R R R G.mX;.V.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.} 6XoXoXoXfXX.>X .-.&.`._ D.z.i Z.O.Q |.yX6X4 n 2.~X1X<.J ) f ~XM Q G R R G G G G G R R R Q.m ~.V M.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.s.} 6.1.1XfXX.>X .-.&XtXw._ #.@./.O.X VX7X6 s.T 2.~XAX< `.J fX; ~XM Q Q.F R R R R R R G G.mXM.N ^.s.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.s.s.} 6.1.1XX., J.g.*Xt.a._.(.@ + Z.O V |.y =X4X9 2 cX<X1 `.V.NX; ~XM.m Q Q G G G G Q.m.m ~.N ^.s.s.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.s.s.s.} 6.1XfXa J.g.-.&.a._ D #.i h.O X |X7X6 sX9 2 c.LXAX1 `.V.NX;X; ~XM.m.m.m.m.mXMXMX;.V ^.s.s.s.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.s.s.s.s.} 6.lXa.,X .-.&Xt %._ $.@ + Z.O V |X7X6 s.TXF cXAXAX1 `.V.V.NX;X; ~ ~ ~ ~X;X;.N.V M.s.s.s.s.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.s.s.s.s.s.s.# 5., J.g.- -.a.U $ #.i./ o.X gX7.y s s nXF cXAXAX1 ` `.V.V.N.N.N.N.N.N.N.A.s.s.s.s.s.s.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.s.s.s.s.s.s.s a 5.,.g.-.& N %.U $ # + hX< X gX7X9X9 s lXF c cXAX1X1 ` ` `.V.V.V.V.A M.s.s.s.s.s.s.s.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s 5...g.- N.a % $ v.i +.L.O g gX9X9 s # nXFXFXkXkXAXAX1X1.A.A.A S.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.P.+.- }Xt.a.U $ #.i h.LX< g FX9.y v s s ]XFXFXkXkXkXAXAXk M.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.P } N %.U $ # l h.L.X FX9X7 v.y s s s ]XFXFXF ^.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.+.+.a.U v # l h.LX< g gX7X7.y.y s s.:.:.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s",
+".s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s %XG v # l h.LX< X g g g.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s"
+};
diff --git a/arts/builder/pics/Synth_WAVE_SQUARE.xpm b/arts/builder/pics/Synth_WAVE_SQUARE.xpm
new file mode 100644
index 00000000..4f5a1671
--- /dev/null
+++ b/arts/builder/pics/Synth_WAVE_SQUARE.xpm
@@ -0,0 +1,323 @@
+/* XPM */
+static char * WAVE_SQUARE_xpm[] = {
+"64 64 256 2",
+" c None",
+". c #161E54",
+"+ c #429224",
+"@ c #465A84",
+"# c #56CA0C",
+"$ c #2E3E64",
+"% c #66E614",
+"& c #3A4E74",
+"* c #3E821C",
+"= c #96E64C",
+"- c #222E64",
+"; c #7AD63C",
+"> c #4EAE1C",
+", c #5A6A9C",
+"' c #324674",
+") c #5EDA0C",
+"! c #565EA4",
+"~ c #464E8C",
+"{ c #263664",
+"] c #52BE0C",
+"^ c #6ACE2C",
+"/ c #6EF614",
+"( c #6A7694",
+"_ c #52629C",
+": c #92FE2C",
+"< c #4E5A8C",
+"[ c #7E86AC",
+"} c #2E3E7C",
+"| c #3E4684",
+"1 c #82FE24",
+"2 c #1A265C",
+"3 c #5EBE24",
+"4 c #4AA21C",
+"5 c #5AD20C",
+"6 c #AAFE44",
+"7 c #425684",
+"8 c #222E74",
+"9 c #465684",
+"0 c #2E3674",
+"a c #767AA4",
+"b c #3E4E8C",
+"c c #82DE44",
+"d c #7AFE1C",
+"e c #52BA0C",
+"f c #364684",
+"g c #56C60C",
+"h c #4E5A9C",
+"i c #363E84",
+"j c #1E266C",
+"k c #62CA24",
+"l c #6A7294",
+"m c #56BE1C",
+"n c #6AD62C",
+"o c #72FE14",
+"p c #469A14",
+"q c #4A5A94",
+"r c #56669C",
+"s c #9EFE3C",
+"t c #42527C",
+"u c #22325C",
+"v c #5E6AB4",
+"w c #6A76BC",
+"x c #222A64",
+"y c #465694",
+"z c #2E367C",
+"A c #52B61C",
+"B c #4A5E84",
+"C c #5ECA1C",
+"D c #2E3E6C",
+"E c #6AEE14",
+"F c #A2E65C",
+"G c #96D26C",
+"H c #3A467C",
+"I c #62E20C",
+"J c #46528C",
+"K c #2A3674",
+"L c #3E4A84",
+"M c #1E2A5C",
+"N c #4EAA1C",
+"O c #B6FE4C",
+"P c #2A3274",
+"Q c #7A82AC",
+"R c #8EE24C",
+"S c #4E5E9C",
+"T c #36427C",
+"U c #56C21C",
+"V c #569E34",
+"W c #5AB624",
+"X c #3E527C",
+"Y c #82DA41",
+"Z c #72D234",
+"` c #6EFA14",
+" . c #9AFE38",
+".. c #4E5E94",
+"+. c #828EB4",
+"@. c #324283",
+"#. c #42528F",
+"$. c #3A4A88",
+"%. c #469E1C",
+"&. c #4E569A",
+"*. c #36428C",
+"=. c #162254",
+"-. c #4A5A8B",
+";. c #9EE65C",
+">. c #4EB21C",
+",. c #5662AA",
+"'. c #2A3A6C",
+"). c #6AD22C",
+"!. c #727E9C",
+"~. c #8AFE2C",
+"{. c #1E2663",
+"]. c #5EC61C",
+"^. c #4AA61C",
+"/. c #66D224",
+"(. c #263274",
+"_. c #767E9C",
+":. c #1E2A6C",
+"<. c #62CE24",
+"[. c #72D634",
+"}. c #76FE1A",
+"|. c #5E66AC",
+"1. c #26326C",
+"2. c #626EB3",
+"3. c #2E3A7D",
+"4. c #52BA1C",
+"5. c #324274",
+"6. c #46529C",
+"7. c #3E4A94",
+"8. c #5ACE0C",
+"9. c #666EB4",
+"0. c #727AA4",
+"a. c #626EA4",
+"b. c #6676AC",
+"c. c #32425C",
+"d. c #5E6EA4",
+"e. c #7E8AAC",
+"f. c #6E7AC0",
+"g. c #A6EA64",
+"h. c #66EA14",
+"i. c #7ADA3C",
+"j. c #62DE0C",
+"k. c #52C20C",
+"l. c #5ED60C",
+"m. c #6AF214",
+"n. c #B2FE4C",
+"o. c #A6FE44",
+"p. c #96E254",
+"q. c #3E8624",
+"r. c #2E3A72",
+"s. c #5ECE24",
+"t. c #3A4A7A",
+"u. c #6672BC",
+"v. c #8ADE44",
+"w. c #5EC224",
+"x. c #5ABA24",
+"y. c #3E4E84",
+"z. c #9AE65C",
+"A. c #5AC61C",
+"B. c #4A9A24",
+"C. c #5A66AC",
+"D. c #1A2264",
+"E. c #767EAC",
+"F. c #3E4E7C",
+"G. c #9AE654",
+"H. c #262E6C",
+"I. c #364677",
+"J. c #2A366C",
+"K. c #72F614",
+"L. c #96FE34",
+"M. c #525A94",
+"N. c #323E7E",
+"O. c #AEFE4C",
+"P. c #424E8E",
+"Q. c #7EFE20",
+"R. c #3A4685",
+"S. c #525AA4",
+"T. c #5ABE22",
+"U. c #263264",
+"V. c #4A5696",
+"W. c #323E74",
+"X. c #66E214",
+"Y. c #BAFE54",
+"Z. c #525EA3",
+"`. c #3A427C",
+" + c #5AC21D",
+".+ c #1A225C",
+"++ c #5A62AC",
+"@+ c #6ED22E",
+"#+ c #62C624",
+"$+ c #222A6E",
+"%+ c #66CE25",
+"&+ c #323A84",
+"*+ c #828AB4",
+"=+ c #469224",
+"-+ c #5ACA0C",
+";+ c #52AE1C",
+">+ c #62DA0C",
+",+ c #86FE24",
+"'+ c #4EA21C",
+")+ c #5ED20C",
+"!+ c #262E74",
+"~+ c #86DE44",
+"{+ c #6ED62C",
+"]+ c #5A669C",
+"^+ c #A2FE3C",
+"/+ c #626AB4",
+"(+ c #56B61C",
+"_+ c #52AA1C",
+":+ c #7E82AC",
+"<+ c #92E24C",
+"[+ c #72FA14",
+"}+ c #525E94",
+"|+ c #868EB4",
+"1+ c #4A9E1C",
+"2+ c #3A428C",
+"3+ c #52B21C",
+"4+ c #8EFE2C",
+"5+ c #4EA61C",
+"6+ c #76D634",
+"7+ c #56BA1C",
+"8+ c #4A529C",
+"9+ c #424A94",
+"0+ c #6AEA14",
+"a+ c #7EDA3C",
+"b+ c #56C20C",
+"c+ c #6EF214",
+"d+ c #7A7EAC",
+"e+ c #465A8C",
+"f+ c #3E8224",
+"g+ c #222E6C",
+"h+ c #464E94",
+"i+ c #26366C",
+"j+ c #6EF61C",
+"k+ c #92FE34",
+"l+ c #4E5A94",
+"m+ c #3E468C",
+"n+ c #1A2664",
+"o+ c #46568C",
+"p+ c #4E5AA4",
+"q+ c #72FE1C",
+"r+ c #469A1C",
+"s+ c #4A5A9C",
+"t+ c #425284",
+"u+ c #223264",
+"v+ c #4A5E8C",
+"w+ c #5ECA24",
+"x+ c #2E3E74",
+"y+ c #62E214",
+"z+ c #465294",
+"A+ c #2A367C",
+"B+ c #3E4A8C",
+"C+ c #1E2A64",
+"D+ c #B6FE54",
+"E+ c #2A327C",
+"F+ c #364284",
+"G+ c #3E5284",
+" (.P P E+E+(.!+8 $+$+:.{. ",
+" x r.F+F+&+3.3.A+(.!+(.!+8 8 $+8 D.. . . ",
+" 0 L 9+m+2+F+N.3.z !+A+0.E.!+$+8 :.:.:.j D.D.. . ",
+" r.h &.V.h+9+R.*.i &+3.3.!+(.E.0.8 8 $+8 8 $+:.:.n+D.. . . . ",
+" M.! ! &.8+P.7.m+*.@.&+z 3.!+E+a E.!+8 $+:.:.:.:.$+:.:.j j D.. . ",
+" &./+|.++S.&.6.h+7.m+*.i i 3.A+8 (.E.0.8 8 8 $+$+$+j :.:.j :.j :.D.. . . ",
+" a.2.v ,.Z.p+V.h+9+m+m+@.i &+3.A+A+(.(.!+!+$+8 :.:.$+j :.:.j :.j :.j n+D.. . ",
+" ~ w u./+C.++Z.p+V.6.7.7.*.i &+} z z E+E+!+(.8 8 $+8 $+:.:.j j :.j :.j j j j D.. . . ",
+" `.w u.u./+C.,.S.V.6.h+7.m+m+*.&+&+A+A+8 (.0.0.!+8 $+8 $+:.:.:.j :.j j n+j j n+j D.. . . ",
+" &.a u.u./+C.,.Z.p+V.6.h+7.f *.i } &+3.A+$+E+a 0.8 $+$+:.:.$+:.:.:.j :.:.:.j :.:.j :.D.. . . ",
+" `.u.w 9.2.v C.! S.&.6.h+7.m+*.*.i &+A+A+A+$+(.a 0.8 8 $+8 :.:.:.j :.j j j :.n+j :.n+j n+D.. . . ",
+" ~ f.w u.9.v C.! Z.p+V.6.b 7.m+*.i } &+3.z E+8 !+0.0.8 :.$+:.:.j j :.j :.:.j j j n+j j :.j j D.. . . ",
+" 9.u.9.2.|.C.,.Z.p+V.6.h+7.7.f *.i &+3.A+A+E+(.!+!+8 8 8 $+$+$+:.:.:.j j :.:.:.:.j :.j n+j n+n+D.. . ",
+" b.b.a.u., , ++_ Z.s+o+9+B+R.F+i N.} 3.A+i+1.(.i+g+g+8 $+:.:.:.:.$+:.:.:.:.:.:.:.:.:.:.:.:.:.j j :.D.. . ",
+" | G g.F F p.G.p.R v.v.c Y a+; 6+[.@+).^ ^ %+k w+w+A.%+%+B.8 8 8 8 $+8 8 8 8 :.$+$+8 $+8 8 $+8 :.:.:.:.n+D.. ",
+" a.F Y.O O n.6 o.o.o.s .L.: 4+4+~.,+,+Q.Q.d d }.}.}.[+j+ +g+$+8 $+8 8 8 8 8 8 8 8 8 8 $+$+8 $+8 $+$+$+:.. . ",
+" &., G.O O.G.<+<+v.v.Y Y a+a+i.6+Z [.).)./.%+<.k w+A.s.j+j+U u+!+(.8 !+8 !+!+(.(.(.!+(.(.(.g+(.g+8 8 g+$+g+:.n+. ",
+" W.++}+p.O.= B l+-.V.#.t+P.L $.T ' 5.x+r.r.0 '.i+i+1.- u /.` +1.8 8 (.(.(.(.(.!+(.(.(.(.(.!+(.!+(.1.H.8 H.- - x . . ",
+" ~ S.M.R o.R J 6.z+h+9+B+$.`.2+i N.&+&+z A+E+E+(.(.(.(.U. +/ U i+E+E+(.E+(.(.A+(.(.i+(.(.i+(.i+(.(.(.(.(.- f+^.q.M 2 ",
+" h h -.R o.v.#.h+P.b 7.R.2+*.@.i &+3.3.A+A+(.(.E+E+(.P i+ +/ T.U.(.(.E+i+A+A+(.A+A+E+A+K E+A+E+P E+i+P (.U.^.8.4 u+.+ ",
+" 0 8+6.P.~+s Y L 7.m+m+m+2+*.i N.N.&+A+z E+A+(.(.(.E+(.$+i+k / m i+A+A+A+A+A+3.0 z '.A+A+'.A+'.A+K A+K K i+{ ^.8.4 U.g+2 ",
+" h+P.h+L c .Y $.m+m+R.*.*.N.i &+3.3.z A+K E+E+E+(.i+A+8 J.<.m. +'.K A+0 A+z A+3.3.z 3.3.z 3.z 3.3.0 A+K z U.^.8.4 U.- . ",
+" 2 9+7.9+| a+L.; H f *.i i i &+3.z A+A+A+(.(.(.(.E+E+(.A+g+J.k m.m '.3.3.3.3.r.3.3.} 3.3.} 3.} 3.r.3.3.3.K '.J.^.8.4 U.J.1.{. ",
+" &+B+m+R.t.i.: i.i *.i i i } 3.&+3.z A+(.E+E+A+E+A+A+A+A+!+i+k c+m r.A+3.3.3.3.3.3.3.N.N.&+N.&+} 3.} 3.3.3.&+{ ^.8.4 J.P H.. ",
+" &+F+*.*.T i.~.[.T N.i &+&+3.A+A+A+E+E+(.A+E+K E+A+K A+A+z '.m E m D 3.} } } N.N.N.N.N.@.N.N.N.N.N.N.N.N.3.W.'.^.8.4 { J.P U. ",
+" F+F+F+N.W.Z ~.[.x+&+3.3.A+A+A+E+E+E+(.E+(.(.E+K E+A+K 3.3.r.m m.m '.} N.N.N.N.N.@.N.N.N.N.@.i @.N.N.N.N.N.r.'.4 8.4 '.K J.P ",
+"!+N.&+N.3.r.{+,+).'.3.A+z A+A+A+A+(.E+(.(.E+i+A+A+A+A+A+3.u+r.#+E m r.N.N.i @.*.i F+F+F+F+F+@.T F+T @.@.@.T i $ 4 8.4 '.r.'.'.K ",
+"P &+3.&+&+'.@+1 @+E+A+A+A+A+E+(.!+(.(.(.(.A+E+E+K z 3.3.3.!+x+].E 7+5.i N.@.F+@.f F+f f f F+R.F+F+F+T T T N.@.D ^.# 4 r.3.3.z K ",
+"(.3.A+A+A+'.).Q./.1.A+A+E+(.(.(.(.(.(.E+(.A+i+A+A+A+3.3.N.(.x+].E 4.x+F+F+F+F+F+f F+F+R.f f R.f f R.f F+f F+@.D ^.# 4 $ r.3.r.0 ",
+"E+E+!+!+!+x %+Q.k - $+:.E+(.$+$+$+$+(.(.8 8 !+!+K 3.!+!+(.P x+#+E 7+5.F+*.F+R.f R.R.$.R.R.R.R.R.R.f R.R.`.I.`.5.4 -+4 x+N.W.3.3.",
+"E+E+E+(.A+1.%+Q.k 1.(.!+(.!+8 8 8 !+(.P (.P K 3.A+3.3.3.} N.x+7+E 7+5.f f f R.f $.R.R.B+$.$.$.$.B+R.$.$.R.f R.5.^.# 4 D N.N.r.3.",
+"(.(.E.E.0.l <.d {+l a 0.8 8 0.a 0.0.(.(.E.E.E.E.3.3.Q :+Q [ x+7+h.7+!.[ *+*+R.B+*++.+.*+B+B+|+|+*+|+$.R.*+*+e._.4 -+5+!.[ [ N.r.",
+"8 8 a a a l w+}.n l 0.a !+!+0.a a 0.!+E+_.E.d+d+3.3.[ [ Q [ 5.4.h.m Q e.*+*+7.$.*+*++.|+b B+*+|+*+|+b B++.*+*+_.4 -+_+!.[ [ @.N.",
+"!+(.8 8 8 C+w+}.w+g+8 8 8 8 $+$+8 !+(.(.A+A+i+3.3.} 3.N.@.@.5.(+% A 5.m+R.$.$.b b P.P.b b b #.b P.b b b B+L t.I.4 # 1+5.T @.i 3.",
+"$+8 !+8 !+u+C }.C x $+:.:.$+8 $+(.(.(.(.A+A+A+3.3.} 3.N.i @.5.w.% 7+'.} N.&+B+9+N.N.N.N.#.P.N.@.i i P.P.N.N.N.'.4 # %.J.0 J.T N.",
+":.$+8 $+$+x A.[+A.:.:.$+$+8 :.8 8 8 (.E+K 3.3.A+3.N.N.N.@.F+' T.% A D $.$.b b G+#.#.#.z+z+#.z+#.#.#.#.#.P.P.y.t.4 g 1+' I.R.T N.",
+":.:.8 8 :.g+A.[+A.C+8 :.:.$+8 8 !+!+(.(.E+K 3.3.3.N.@.@.F+F+I. +% A r.b b b P.b P.z+#.#.y y J z+#.#.#.#.#.P.y.t.4 g 1+I.f f I.T ",
+"j $+$+:.$+C+A.` U :.:.:.:.:.$+$+(.(.(.A+K A+3.3.N.N.i i *.`.5.3 I A D b b P.P.6.#.#.z+y z+y y y y y z+o+#.#.#.t.4 g %.I.R.R.`.N.",
+" :.$+:.:.n+U j+U {.$+j :.:.8 $+8 (.(.A+A+A+3.3.} N.@.F+f f T (+X.3+H b #.#.#.y y y y y y y y V.y y y o+o+J J t.4 g 1+I.R.f R. ",
+" :.:.$+:.C+ +/ m :.:.$+$+$+$+8 !+(.E+i+K 3.3.3.N.N.i @.*.R.I.A I A $.P.b #.#.#.z+y V.q V.s+q q q q y y z+z+#.F.4 g %.t.L R.I. ",
+" . j :.j n+m c+U C+:.j :.:.8 8 !+(.E+A+A+3.3.&+} @.F+F+F+f I.x.j.A D #.P.6.y y y y y q q s+q s+q q s+e+y y J F.1+g 1+t.L R.N. ",
+" D.:.j :.- m m.m n+j :.:.$+$+8 !+(.(.A+K A+3.} N.N.F+f R.R.I.T.y+3+x+P.#.#.6.y V.s+s+s+s+l+h l+h h q s+e+y o+F.1+b+%.H $.H - ",
+" n+:.j 2 m E m n+j j :.:.8 8 8 (.(.A+r.3.3.N.N.N.@.F+f R.I.x.j.3+x+#.z+y y s+y q v+S S S S S ..h l+h q V.y X 1+b+%.t.t.| ",
+" D.j :.n+e E e n+:.:.:.$+$+g+(.(.P K A+3.3.3.N.@.F+f R.$.I.x.j.3+W.#.#.y y y s+S h s+S S _ _ S S S q h e+V.F.%.k.p t.y.U. ",
+" D.:.{.4.0+7+n+n+j :.:.8 g+!+(.(.A+A+3.} N.N.N.@.*.F+R.t.>.) >.F.z+z+y y q q s+S S S S S _ _ S S q h q q t 1+b+p t.P. ",
+" n+j M + 4.+ n+j n+:.$+$+!+(.(.K P K 3.&+} N.F+F+f f $.I.3+>+;+L #.y z+q V.s+..S S _ _ r _ _ _ _ S v+q V.t 1+k.1+t.`. ",
+" . n+j M M 2 j :.j :.$+:.g+(.(.i+A+K 3.3.&+N.N.F+R.`.R.I.W ) A $ & t+y.7 t+7 9 @ e+e+-.v+v+v+e+-.o+9 7 t t.1+] p t.H ",
+" n+n+j n+j n+j n+j :.8 8 8 (.P A+A+K 3.} N.F+F+*.f f I.(+l.5 > _+> ;+N N N N N N N N N ^.^.5+5+'+4 1+1+4 ] ] p c. ",
+" D.:.:.:.j :.:.:.$+$+H.8 (.(.K 3.r.3.3.N.N.@.F+R.f t.W l.l.l.5 5 5 )+8.8.8.-+-+# # -+A.g g g g b+k.k.] ] ] 1+c. ",
+" D.j j j n+j n+{.:.g+$+!+(.P K K 3.3.N.} @.F+f f $.T V (+> N _+N N N _+N N N ^.5+5+5+^.4 4 4 1+4 1+p %.1+p * ",
+" . j :.j j :.j j g+g+8 (.(.E+K r.3.&+N.@.i F+F+R.f H t.t.& y.y.y.t+#.t+7 e+e+o+o+-.-.-.@ @ 9 9 9 F.F.F.t. ",
+" n+j n+n+n+j :.:.8 H.8 (.i+K A+0 } r.N.@.F+R.f $.$.$.b b #.J o+o+y s+V.q h S ..S ....h q q q o+V.o+t ",
+" . D.j j n+j j :.g+H.!+P (.K 3.K N.N.N.@.@.f `.R.*+*+B+} b P.#.y J V.V.q q S q S s+..S q V.V.o+J P.| ",
+" . n+n+j C+:.$+:.8 H.(.P K K 3.3.3.W.i T F+f f *+*+b N.P.#.P.#.y y y y q V.q q q q V.q e+y o+J J. ",
+" . =.n+j j $+g+8 8 (.(.P K K r.3.} N.i F+`.R.*+*+L } b #.#.#.#.y y y e+q V.q y e+y y o+z+#.H ",
+" . D.n+{.:.:.g+H.(.(.i+K z 3.x+&+N.@.@.f f *+*+R.r.B+L P.P.#.#.J z+z+y y y V.V.y z+z+t J. ",
+" . =.j C+$+:.g+!+(.P K K z r.3.W.N.N.T F+T R.B+$.b L b P.P.#.#.J #.o+J 7 #.J o+o+#.| ",
+" D.j :.g+$+g+!+(.i+K A+r.3.3.N.N.N.T R.f f | R.b L y.P.P.#.#.J #.#.z+J #.t+P. ",
+" . j g+g+g+!+!+P K K r.3.r.N.N.@.@.[ *+f r.R.$.L y.y.L y.b P.#.P.#.#.P.H ",
+" {.$+!+u+P (.K K A+r.3.N.N.N.N.[ [ f r.R.$.$.$.$.b b P.y.P.b y.L ",
+" 2 $+g+1.!+(.K K r.K r.} N.i [ [ F+'.`.R.R.L L L $.L L y.L | ",
+" C+1.P (.K K 3.3.r.W.} Q :+F+0 f `.R.f R.R.| $.T ",
+" x x P K K z r.N.W.N.N.@.i @.T `.f H W.W. ",
+" J.K K K 3.r.x+r.N.T T @. "};
diff --git a/arts/builder/pics/Synth_WAVE_TRI.xpm b/arts/builder/pics/Synth_WAVE_TRI.xpm
new file mode 100644
index 00000000..1ff5cf52
--- /dev/null
+++ b/arts/builder/pics/Synth_WAVE_TRI.xpm
@@ -0,0 +1,305 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"64 64 234 2",
+/* colors */
+" c #394684",
+" . c #3AA021",
+" X c #33407E",
+" o c #313E7C",
+" O c #43B123",
+" + c #505AA1",
+" @ c #1D2865",
+" # c #2C3877",
+" $ c #283473",
+" % c #273272",
+" & c #38912C",
+" * c #253070",
+" = c #37457B",
+" - c #232E6E",
+" ; c #263F53",
+" : c #314175",
+" > c #2D3A7B",
+" , c #2C387A",
+" < c #3E4D85",
+" 1 c #3C468D",
+" 2 c #283276",
+" 3 c #374288",
+" 4 c #49BC25",
+" 5 c #232E71",
+" 6 c #343E85",
+" 7 c #465590",
+" 8 c #757CA7",
+" 9 c #202A6E",
+" 0 c #2F3A80",
+" q c #1E286C",
+" w c #1D286B",
+" e c #50619D",
+" r c #2D387E",
+" t c #3F4D89",
+" y c #3A4784",
+" u c #253076",
+" i c #384582",
+" p c #5661A6",
+" a c #323F7C",
+" s c #303D7A",
+" d c #4E599E",
+" f c #3A4787",
+" g c #293573",
+" h c #242F6E",
+" j c #285640",
+" k c #323F7F",
+" l c #44508A",
+" z c #2F3B7C",
+" x c #2E3B7B",
+" c c #2C3979",
+" v c #1B2565",
+" b c #293576",
+" n c #394589",
+" m c #263173",
+" M c #252F72",
+" N c #242F71",
+" B c #5864A4",
+" V c #34427A",
+" C c #212B6E",
+" Z c #2F3B7F",
+" A c #1E296B",
+" S c #344C66",
+" D c #43963C",
+" F c #1D276A",
+" G c #1C2769",
+" H c #2C377C",
+" J c #2B357B",
+" K c #4C5C98",
+" L c #4A5A96",
+" P c #242F74",
+" I c #475693",
+" U c #222D72",
+" Y c #202B70",
+" T c #43528F",
+" R c #33407C",
+" E c #323E7B",
+" W c #41508D",
+" Q c #364482",
+" ! c #354281",
+" ~ c #2E3A7A",
+" ^ c #7F88AF",
+" / c #2C3B6E",
+" ( c #7B84AB",
+" ) c #263272",
+" _ c #364285",
+" ` c #475299",
+" ' c #28356A",
+" ] c #294357",
+" [ c #242E70",
+" { c #212C6D",
+" } c #1F2A6B",
+" | c #1D2869",
+". c #1C2668",
+".. c #495994",
+".X c #354087",
+".o c #242E73",
+".O c #232E72",
+".+ c #465591",
+".@ c #222C71",
+".# c #212C70",
+".$ c #202A6F",
+".% c #1F2A6E",
+".& c #42518D",
+".* c #1E286D",
+".= c #3D4B88",
+".- c #3B4986",
+".; c #294F49",
+".: c #33417E",
+".> c #626DB6",
+"., c #3FB01F",
+".< c #2D3978",
+".1 c #2C3777",
+".2 c #3D4C81",
+".3 c #2A3575",
+".4 c #1A2362",
+".5 c #283373",
+".6 c None",
+".7 c #263171",
+".8 c #222D6D",
+".9 c #365466",
+".0 c #323D80",
+".q c #202B6B",
+".w c #70799F",
+".e c #313E75",
+".r c #2D397B",
+".t c #1D2768",
+".y c #2B3779",
+".u c #3DAA20",
+".i c #293377",
+".p c #495893",
+".a c #182059",
+".s c #222D70",
+".d c #333D84",
+".f c #45548F",
+".g c #35427C",
+".h c #313B82",
+".j c #42508C",
+".k c #347B35",
+".l c #2E397F",
+".z c #1D276B",
+".x c #404E8A",
+".c c #4E5E9B",
+".v c #4C5C99",
+".b c #3C4A86",
+".n c #2C3873",
+".m c #4A5A97",
+".M c #7986A4",
+".N c #399729",
+".B c #3B7F3F",
+".V c #455492",
+".C c #6772BA",
+".Z c #313E7B",
+".A c #303C7A",
+".S c #202A67",
+".D c #21315E",
+".F c #2E3A78",
+".G c #41A42A",
+".H c #1F2D5C",
+".J c #1C2663",
+".K c #5D68B0",
+".L c #2B3675",
+".P c #334080",
+".I c #33486C",
+".U c #327C2F",
+".Y c #1E2B5E",
+".T c #2B396E",
+".R c #384488",
+".E c #253072",
+".W c #242E71",
+".Q c #212C6E",
+".! c #303C80",
+".~ c #202A6D",
+".^ c #42BA1D",
+"./ c #24365D",
+".( c #1E286B",
+".) c #414F8A",
+"._ c #1C2669",
+".` c #2B367B",
+".' c #4D5D99",
+".] c #1F354E",
+".[ c #293479",
+".{ c #4B5B97",
+".} c #273277",
+".| c #657096",
+"X c #2C5C40",
+"X. c #485794",
+"XX c #27336D",
+"Xo c #647C81",
+"XO c #242F6A",
+"X+ c #335B54",
+"X@ c #3F4D8B",
+"X# c #2F3B78",
+"X$ c #307433",
+"X% c #48C31F",
+"X& c #344180",
+"X* c #303D7C",
+"X= c #525DA4",
+"X- c #303B7C",
+"X; c #2B3777",
+"X: c #4A559C",
+"X> c #48CC18",
+"X, c #343F83",
+"X< c #222D6E",
+"X1 c #313D80",
+"X2 c #424D94",
+"X3 c #303D7F",
+"X4 c #404B92",
+"X5 c #1E296A",
+"X6 c #2D397C",
+"X7 c #3E4990",
+"X8 c #1D2769",
+"X9 c #327931",
+"X0 c #2A3579",
+"Xq c #283377",
+"Xw c #273376",
+"Xe c #263175",
+"Xr c #4DAE34",
+"Xt c #2B6634",
+"Xy c #475692",
+"Xu c #294950",
+"Xi c #378A2F",
+"Xp c #212B70",
+"Xa c #44528F",
+"Xs c #43528E",
+"Xd c #346B4A",
+"Xf c #1F296E",
+"Xg c #1E296D",
+"Xh c #2E3980",
+"Xj c #233A54",
+"Xk c #1D276C",
+"Xl c #2C377E",
+"Xz c #3E4C89",
+"Xx c #7E8CAA",
+"Xc c #495897",
+/* pixels */
+".6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.E 2.i.[Xq u P.o.@ 9 A.t.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.n E 3.X.d 0 r J.[.} u P.o U.@ Y.$Xf.4.4.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6 fX2X4 1.R.X.d 0 r.W.[ 8 8 P.o U.@ Y.$.$XfXf.(.4.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6 ! + d `X2X4 1.R.X.h 0 r.W.[ 8 8.o.o U.@ Y.$.$XfXg.*.* q._.4.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6 p pX= d `X2X7 1 3 6.h 0Xl.W.[ 8 8.o.o UXp Y.$.%XfXg.* q q q.z. .6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.6.6.6.6.6.6.6 K.K.K p +X: `X2X7 1 3 6.hXh H.W.[ 8 8.o.o UXp Y.$XfXfXg.* q q w.z.z._.4.6.6.6.6.6.6.6.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.6.6.6.6.6.6.C.>.K.K p +X: `X2X7 1 3 6.h.l H JXq.} P.o.o.@Xp.$.$XfXfXg.* q q.z.z.z F F. .6.6.6.6.6.6.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.6.6.6.6 l.C.C.>.K pX= dX: `X4X7 n 3.d.h.l.`.[Xq u P.o U.@Xp.$.$XfXf.* q q q.z.z F F F F. .a.6.6.6.6.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.6.6.6.p.C.C.>.K.K p + d `X2X7 1.R.X.d 0 r J.s.} 8 8.o U.@Xp.$.%XfXg.* q q w.z.z F F FX8._. .a.6.6.6.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.6.6 K.C.C.C.>.K pX= +X: `X2X7 1 3 6.h 0 H J.s u 8.w.sXp.$.$.$.%XfXg.* q q w.z.z F F FX8._._. .a.6.6.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.6.p.C.C.C.>.K.K p + d `X2X4 1 n.X.d.h.l HX0 C u.w.w.8 C 9.%XfXfXfXg q q w.z.z F F FX8 G._._. v.a.6.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6 l.C.C.C.>.K.K pX= dX: `X2X7 1 3.X.d 0 r.`.[.q [.|Xo.Y @X5XfXgXfXf.* q q q.(.z.z F F FX8X8._. . v.a.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.C.C.C.>.K.K pX= +X: `X2X4 1 n 3 6.h.l H.[.} *XOXj.;.].H @ qXgXfXfXgXg q q q q.(.(.(.(.( FX8X8. . v.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.C.C.>.>.K.K pX= + d ` `X4X7 1 3.X.d 0 r J 2.7.DXj.;XtXu.H @ qXf 9 9 9 9 9 9 9.~.~.~.~ } q A.(.(.(X8.t v.6.6.6.6.6.6",
+".6.6.6.6.6 K.>.>.K.K.K pX= + dX: `X2X7 1 n.X.d.h.l.`.[ %XO.] j DXr j.].Y.~ 9.#.#.#.#.Q.Q.s.s.Q.Q.Q.Q {.~.~.~ AX5X5 |.4.6.6.6.6.6",
+".6.6.6.6.6.K.K.K.K p pX= + dX: `X2X7 1 n 3 6.h 0 H.[ %.DXj j.UXr OX .].Y {.s 5 5 5 5 5 5 5 5 5 5.s.s.sX<.Q { {.q.q } |.6.6.6.6.6",
+".6.6.6.6 p.K.K p pX= + dX: ` `X2X4 1 n 3.X.d.h r J.5XXXj j.U., 4 4Xt.].D - 5.o.o M M.E.E.E.E.E M N N N [ 5 - -X< {.q.q |.6.6.6.6",
+".6.6.6 ! p p pX= + + dX: ` `X2X7 1 1 3.X.d.h rX0.5 'Xj j.U .X%X%X%XtXj.D [.E m m m m m m m m m m m m m.E * M [ [ -X<.8 {.J.6.6.6",
+".6.6.6 +X= + + d dX: ` `X2X4X7 1 n 3.X.d.h.l H $XXXjX .U OX%X>X>.^XtXj.D m mXwXqXqXqXqXqXqXqXwXwXw.5 m m % m m * * h -.8.q.6.6.6",
+".6.6.6 d dX:X:X: ` `X2X2X4X7 1 n 3.X.d.h.l.`.3 'Xj j.U.N.^X>X>X>.^XtXj.D.5Xw.i bX0X0X0X0X0 b b bXw $.5.5 % % ) % m.7 * h -.6.6.6",
+".6.6 f ` ` ` ` `X2X2X4X7 1 1 n 3.X.d.h.l H.3XX ;X .k O.^X%X>X>X>X%XtXj ' $ bX0.y.y.y.y.y.y.y.yX;X; g $ $ $ $.5.5.5 %.7.7 *.Y.6.6",
+".6.6X2X2X2X2X2X4X7X7 1 1 n 3.X 6.d.h.l ,.3 ' ]X .k.N O.NXi ..^ 4 4XtXj ' b.y , , ,.r.r.r.r.r ,X; g '././ 'XX.3 b b $.5 %.7 h.6.6",
+".6.nX4X4X7X7X7X7 1 1 n 3 3.X.d.h.h r H.3XX ]XdXi O O O.UX X9.N O 4XtXj 'X; ,X6 > > > z z z > >.n.T ].; ]./ ' g.L.L.L g $.5 %.S.6",
+".6 E 1 1 1 1 1 n.R 3.X.X 6.d.h 0 r.y g ' ]Xd &.G O &X9 j ;X XiXr 4Xt ; ' , > z ZX3X3X3X3 z x.n / ]X X$.;./ '.n # #.1.L.3 $.5.S.6",
+".6 3.R.R 3 3 3 3.X 6.d.d.h 0.l H J $XX ].B DX% 4 OX9.; ;./.;.U.N.,Xt ] ' > zX3X1X1X1.0X1X1X# / ]XdXi.NX ] '.<.F.F.< #X;.L.3 %.6",
+".6.X.X.X 6 6 6.d.d.h.h 0.l r.`.[.5 ' ]Xd D.G 4.NX9.;Xj./ 'Xu.U.N.^X$ ] /.AX3.0 k.P.P k o s / ]X &.N .X ].TX#.A z x.F.< #.L.3.6",
+".E.d.d.h.h.h.h.h 0 0.l r H J.[ %XX ;Xd &X% 4 OX9.;Xj 'XXXX ;X9XrX%.BX+.I o kX,X, !X,X& E.e ]XdXi.,.u .X ] / s.Z o.A.A.F.< #.L $",
+" 2 0 0 0 0Xh.l.l r H H.`.[ 2 %.DXjX Xi.G 4.NX9 jXj ' g g g ;.UXrX%.BX+.I k.P ! _ !X& a / ]X Xi.N., &X$Xu /.e a a k o.Z.AX#.F # g",
+".i r r rXl H H.` J JX0.[.}.7XOXj j.U O O.^.k.;Xj ' g ,X;.n ;X9XrX% DX+.IX& ! _ _ Q.: : ]X .U.u.u.,.k.; ].e E X XX& X a o.AX#.F.L",
+".[ J.W.W.W.W J.[.s.s C.q *.D.] j.U.N 4.G &X Xj.D $X; * hXOXu.UXrX% DX+.I ! Q _ !.: : ]X X9.N.,.G DXd S.I V.g ! ! !X& X a.Z.AX# #",
+"Xq.[.[.[.[.[XqXq.} u u [XOXj j.U.,.^X% DXdXu ' g.y , z.<.nXu.U.N.^ DX+.I Q Q ! V ]X X9.u.,.^ D.B.9 = = i i i Q !X& X a o.A.F",
+" u.} 8 8 8 8.} u 8.w.w.|Xj j DXr 4Xr.G.BXoXo.w 8 HX6 ( 8.wXoXi ..^.BXo.w ( ( Q.IXo.B D.G.u.GXrXoXo.w i i ^ ^ ^ ^ i Q ^ ^ ( (.ZX#",
+" P u 8 8 8 8 P.o 8.w.|Xo j.U 4 4 4Xr.BX+.w.w 8 8 H z ( 8.wXoXi ..^.kXo.w ( 8 V S.B D O O., DXoXo.MXx.=.= ^ ^ ^ ^ i ^ ^ ^ ( a.A",
+".o P P.o.o.o.o U.s.Y.] j.U &.u.N.UXdX+ ] g gX0.` z ZX3.A.eX+ &.G.^X$ S.I =.I SX .k &.u &.kXd.9.9 < <XzXzXz.=.=.- f i Q !X& X.Z",
+".@.o.o.o.o.o.o C.q.] jX$.u.u.uX9 jXu 'XXXqX0 ,X6 z.!X1 s.eX+ &Xr 4Xt ;./ ' ;X .k . . .X9X+.9.I : E a WX@.Z.Z s.A.b.-.F #.n.n.: a",
+" 9 U U U U U.@.~.S.]Xt.U.uXiX$ jXj./XX %.iX0 ,X6 ZX3.0 o :X+ &Xr 4X$ ; ] SX .k &.u &.kX+ S.2 <.).&.& T W WX@X@Xz.=.b y i QX& a",
+" A.@.@.@XpXpXp A @.]X$Xi .X$.;.].SXO mXwX0.y.r z ZX1.P o :X+ &Xr 4.k.;X X .k.u.u.u.kX+ S.2 tXaXa.V.VXa T.& W WX@Xz.=.- y i ! R",
+".t Y Y Y Y Y.$.( @.]XtXtXt.;.].H.q hXeXqX0 ,X6 zX3X1.P a :X+XiXrX% &Xt.kXi.N.u &.kX+ S =.)Xs.V.V I.+.V.V T T.& WX@Xz.=.- y i Q.:",
+".6.$.$.$.$.$.$X5 @.] j j.;.].Y.q 5.EXw.iX0 ,X6 zX3.0X, X VX+Xi .X%.u.N ..,.u.u.kX+ S.2.j.V IX.X.X. I I.+.VXaXs.&.xX@Xz.b.- i.6",
+".6Xf.$.$.%XfXf.( |.H.].].].Y.S {.O.EXq b.y , > ZX1 kX, X VXu.U .X>.^.,.,.u &.kX+ S.2 l.fX.XcXc LXc..X. I.+.VXaXs.j.xXz.=.b y i.6",
+".6.4XfXfXfXfXf.( F.J.Y.Y.J.t 9.s M mXqX0.y , >X3X1.P !.: VXu.U.GX>X>X>.^.u.kX+ S.2.j I...m.m.m.m.m.m LX. I.+.VXa.& WX@Xz.b.-.e.6",
+".6.4XfXgXgXgXgXk.z v.J.J @.(.$.s M mXqX0.y.r >X3X1.P !.: VXuX9.GX>.^.^.N.kX+ S.2.j.+...m.v.v.v.v.v K.m LX. I.+.fXs.j.xXz.b.- V.6",
+".6.6.(.*.*.*.*.z.z FX8X8 FXf.Q 5.E mXqX0.y.r zX3.0.P !.: VXuX9.GX>.^.u.kX+ S.2 l I...v.v.'.c.c.c.c.'.v.m LX.Xy.VXa.&.) t.= y.6.6",
+".6.6.4.* q q q q.z.z G.z wXf.Q 5.E mXqX0.y.r zX3.0.P !.: VXuX9.G.^.NX$X+ S =.j.f...m.v.c.c.c.c.c.c.c.' K.m..X..+XaXs.) tXz V.6.6",
+".6.6.6 q q q q q w w.z.( q 9.s 5.E mXqX0.y.r zX3 k.P !.: VXuX9 & ..kX+ S.2.).VX..m.v.'.c.c e e e e e.c.'.{ LX.Xy.fXs.j.xXz.6.6.6",
+".6.6.6._ q w.z.z.z.z.z.z q.~.s 5.E mXq b.y.r zX3 kX& !.: V ]XtX$X$X+ S = tXs IXc.m.v.c.c e e e e e e.c.c K L.pXy.fXs.j.x.b.6.6.6",
+".6.6.6.4.z.z.z.z.z.z F.z.(.~.Q 5.E mXw b.y.r zX- k.P !.g V ]X .BXo S / < T.VX.Xc.m.v.c.c e e e e e e e.c K L.pXy.fXa.j.x =.6.6.6",
+".6.6.6.6. .z.z F F F F F.(.~.Q 5 M mXw b.y.r zX- k.P !X&.g.IXuXoXo =.e.xXa.VX. L.m.v.c.c e e e B e e e.c K L.pXy.fXs.j <.6.6.6.6",
+".6.6.6.6.6._ F F F F F F.(.~.Q.s N mXw b.y.r xX- k.P ! ! ! : :.w.M.b E.&.V IX.Xc.m.v.c.c e e e e e e.c.' K L.pXy.fXs.x.6.6.6.6.6",
+".6.6.6.6.6.4 F F F FX8 F.( }.Q.s N m.5 b.y c >X- k.P ! ! Q.g V.M (.= a.&.V.+ I...m K.'.c e e e e e e.c.'.{ LX..+.fXs.2.6.6.6.6.6",
+".6.6.6.6.6.6. FX8X8 GX8.( q {.s N m m bX; , ~ z k kX& ! Q i i f.bXz W TXa.V IX. L.m.v.'.c.c e e.c.c.' K L.pXy 7Xa.).6.6.6.6.6.6",
+".6.6.6.6.6.6.6. ._._._X8 F A.~X< [.E m bX; #.r z o kX& ! Q i f.=XzX@ W T.V.+ IX. L.m K.'.c.c.c.'.' K L..Xy.+.f.j.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.a. ._._._X8.(.~.Q 5 * % $ b.y.< zX* k.P ! Q f ^ ^Xz.Z W.& T.V.+ IX. L.m.{ K K K K.{ L..X.Xy.f.& =.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.6.a. . . X8.(.~ { - M m.5 bX; , ~X- o kX& Q i ^ ^.=.ZX@ W TXa.V.+ IX... L L L L L L.pXyXy 7.& =.6.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.6.6.a v. . .( A { - [ m.5 bX; #.F z o kX& ! Q ^ ^.= sX@ W.&XsXa.V.+XyX.X..p.p.p.pX.Xy.+.f.& =.6.6.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.6.6.6.a v. X8X5.qX< [ * % $ b.1.<.AX- a XX& Q i ^ ^.-.AXzX@ W.&XsXa.f.V.+XyXyXyXyXy.+ 7.f.& =.6.6.6.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.6.6.6.6.a v.tX5.q { - * m.5 b.L #.F z o kX& ! Q i f.b.=XzX@.x.j.&XsXaXa.f.f.f.f.f.fXa.j =.6.6.6.6.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.6.6.6.6.6.6 v | }.qX< h.7 % $.L.1.< x.A o XX& ! Q i .-.b.=XzX@.x W.j.&XsXsXsXaXsXsXs.).6.6.6.6.6.6.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.6.6.6.6.6.6.6.4 |.q.8 - *.7.5 g.L #.F.A.Z a XX& ^ ^ i.F y.-.=XzXzX@.x.).).j.j.j.j.x.2.6.6.6.6.6.6.6.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6 | {.8 h.7 % $.3X;.<.F.A o a X ^ ^ Q # y.-.b.=XzXz t t.x.x.x <.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.J.q - *.7.5 $.L #.<X#.A.Z a ( ^ !.n i y.-.b.b.b.=XzXz.b =.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.Y h %.5.3.L #.FX#.A o ( (X&.n Q i i y.-.- y V.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.S.S %.3.L #.FX#.A.Z a X.:X& ! Q i i.e V.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6",
+".6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6 $ g.L #.FX#.A.Z a a R.:.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6.6"
+};
diff --git a/arts/builder/pics/Synth_XFADE.xpm b/arts/builder/pics/Synth_XFADE.xpm
new file mode 100644
index 00000000..296b9584
--- /dev/null
+++ b/arts/builder/pics/Synth_XFADE.xpm
@@ -0,0 +1,313 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"64 64 242 2",
+/* colors */
+" c #374482",
+" . c #3BA318",
+" X c #39477A",
+" o c #354280",
+" O c #395766",
+" + c #313E7C",
+" @ c #303C7B",
+" # c #2F3C7A",
+" $ c #33921A",
+" % c #338C1A",
+" & c #2C3877",
+" * c #1A2462",
+" = c #4D5D94",
+" - c #253070",
+" ; c #36A316",
+" : c #2E711F",
+" > c #46558D",
+" , c #838CB5",
+" < c #202A6B",
+" 1 c #1C2667",
+" 2 c #32801C",
+" 3 c #39B212",
+" 4 c #283276",
+" 5 c #384289",
+" 6 c #364287",
+" 7 c #242E72",
+" 8 c #283A62",
+" 9 c #232E71",
+" 0 c #343E85",
+" q c #333E84",
+" w c #747AA6",
+" e c #202A6E",
+" r c #424F8C",
+" t c #212D65",
+" y c #1E286C",
+" u c #2E387F",
+" i c #3FC111",
+" p c #1C266A",
+" a c #2B367C",
+" s c #314A5A",
+" d c #2A3771",
+" f c #1A235E",
+" g c #495996",
+" h c #32741F",
+" j c #475594",
+" k c #232E74",
+" l c #445391",
+" z c #333F7D",
+" x c #243261",
+" c c #27523C",
+" v c #313D7B",
+" b c #3B4988",
+" n c #374584",
+" m c #848DB5",
+" M c #376948",
+" N c #2E3B7B",
+" B c #2D397A",
+" V c #33871C",
+" C c #1B2565",
+" Z c #648088",
+" A c #2E5249",
+" S c #222D6F",
+" D c #323D82",
+" F c #55C733",
+" G c #1F296C",
+" H c #1D276A",
+" J c #404E89",
+" K c #1C2769",
+" L c #283378",
+" P c #263176",
+" I c #394682",
+" U c #3AA915",
+" Y c #242F74",
+" T c #475693",
+" R c #232D73",
+" E c #455491",
+" W c #25306B",
+" Q c #202B70",
+" ! c #43528F",
+" ~ c #33407C",
+" ^ c #5F7286",
+" / c #1E296E",
+" ( c #404E8C",
+" ) c #2E3A77",
+" _ c #2D3876",
+" ` c #3BBA0F",
+" ' c #1B2661",
+" ] c #3A4886",
+" [ c #374483",
+" { c #354281",
+" } c #232E6C",
+" | c #2E3A7A",
+". c #3B488A",
+".. c #7C84AC",
+".X c #283474",
+".o c #384487",
+".O c #475299",
+".+ c #38457D",
+".@ c #232E6F",
+".# c #323F77",
+".$ c #1E286A",
+".% c #1C2668",
+".& c #2B367A",
+".* c #3C468E",
+".= c #4B5996",
+".- c #273276",
+".; c #4B5796",
+".: c #629174",
+".> c #354087",
+"., c #767EA9",
+".< c #757CA8",
+".1 c #222C71",
+".2 c #212C70",
+".3 c #44538F",
+".4 c #737AA6",
+".5 c #202A6F",
+".6 c #1F2A6E",
+".7 c #2F3A81",
+".8 c #1E286D",
+".9 c #414F8C",
+".0 c #1D286C",
+".q c #606BB1",
+".w c #4D5B9B",
+".e c #3B4986",
+".r c #1F3150",
+".t c #364381",
+".y c #34417F",
+".u c #435191",
+".i c #555FA6",
+".p c #69E23B",
+".a c #3E4D8C",
+".s c #2A3575",
+".d c #394787",
+".f c #293574",
+".g c #384586",
+".h c None",
+".j c #263171",
+".k c #222D6D",
+".l c #434D94",
+".z c #3E498F",
+".x c #1D2768",
+".c c #346146",
+".v c #1B2566",
+".b c #487453",
+".n c #2A3578",
+".m c #3C4D79",
+".M c #263174",
+".N c #2D5049",
+".B c #59D62E",
+".V c #2F6C23",
+".C c #45548F",
+".Z c #202B6E",
+".A c #313B82",
+".S c #285A30",
+".D c #1E296C",
+".F c #2E397F",
+".G c #1D276B",
+".H c #3E4C88",
+".J c #2A357B",
+".K c #1F2D59",
+".L c #1C2860",
+".P c #213947",
+".I c #283379",
+".U c #3B4885",
+".Y c #3BAD17",
+".T c #28346F",
+".R c #6B76BE",
+".E c #435090",
+".W c #2C6623",
+".Q c #313C7B",
+".! c #1E2A65",
+".~ c #4D589D",
+".^ c #357B25",
+"./ c #2B3675",
+".( c #3A4687",
+".) c #384685",
+"._ c #485498",
+".` c #273271",
+".' c #48598E",
+".] c #273567",
+".[ c #334080",
+".{ c #313E7E",
+".} c #838CB4",
+".| c #39B811",
+"X c #273274",
+"X. c #364086",
+"XX c #171F57",
+"Xo c #222C6F",
+"XO c #1D286A",
+"X+ c #6E769F",
+"X@ c #23493E",
+"X# c #293479",
+"X$ c #283278",
+"X% c #253075",
+"X& c #2E7524",
+"X* c #5965A8",
+"X= c #465592",
+"X- c #222C72",
+"X; c #212C71",
+"X: c #42518E",
+"X> c #323F7B",
+"X, c #202B66",
+"X< c #2F3B78",
+"X1 c #3D4B89",
+"X2 c #394785",
+"X3 c #364382",
+"X4 c #344180",
+"X5 c #323F7E",
+"X6 c #303D7C",
+"X7 c #24503B",
+"X8 c #404B8F",
+"X9 c #8089B0",
+"X0 c #2D3979",
+"Xq c #40B517",
+"Xw c #2B3777",
+"Xe c #3C478B",
+"Xr c #2D3F65",
+"Xt c #2E7A20",
+"Xy c #3C526D",
+"Xu c #47568F",
+"Xi c #212B6D",
+"Xp c #369A17",
+"Xa c #43528B",
+"Xs c #2C377B",
+"Xd c #3E4C86",
+"Xf c #3A458C",
+"Xg c #2F7824",
+"Xh c #273176",
+"Xj c #485893",
+"Xk c #252F74",
+"Xl c #222D71",
+"Xz c #212B70",
+"Xx c #6570B6",
+"Xc c #1F296E",
+"Xv c #42508D",
+"Xb c #1E296D",
+"Xn c #2E3980",
+"Xm c #2C377E",
+"XM c #3E4C89",
+"XN c #213652",
+"XB c #1C2661",
+"XV c #2A3672",
+/* pixels */
+".h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.j 4 L.IX$ P Y k.1 e.D.x.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.hXV.Q 5.> q.7Xm a.IX$X% Y RX-X; Q.5Xc * f.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.e.lX8.* 5.> q.7Xm 7.I.< w Y RX-X; Q.5.5Xc / y f.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h o.~.~.O.lX8.* 5.> D.7Xm 7.I.< w k RX-X; Q.5.5XcXb.8.8 y.% f.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.iX*.i.~.O.lX8.* 5 0.AXnXm 7.I.< w k RX-X; Q.5.6XcXb.8 y y.0.G.%.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.h.h.h.h.h.h.h.w.qX*.i.i.~.O.l.zXf 5 0.AXnXm 7.I.< w k RX-X; Q.5XcXcXb.8 y y.G.G.G.% f.h.h.h.h.h.h.h.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.h.h.h.h.h.hXxXx.qX*.i.~.~.O.l.zXf 5 0.A.FXm.JX$ PXk k R.1Xz.5.5XcXcXb.8 y y.G.G.G H H.v.h.h.h.h.h.h.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.h.h.h.hXa.RXx.q.qX*.i.~.O.O.l.zXf 6 q.7 u aX#X$ PXk RXlX;Xz.5.5XcXc.8 y y.0.G.G H H H H.%XX.h.h.h.h.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.h.h.h.;.R.RXx.qX*.i.i.~.O.lX8.* 5.> D.7Xm aXlX$.<.4 RXlX;Xz.5.6XcXb.8 y y.G.G.G H H H H.% 1XX.h.h.h.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.h.h =.R.RXx.q.qX*.i.~.~.O.l.zXf 5 0.AXnXm.J.@Xh w.4 RX-X; Q.5.6XcXb.8 y y.G.G.G H H H H.%.% 1XX.h.h.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.h.;.R.RXxXx.qX*.i.i.~.O.lX8.*Xf 6 q.7.F a.JXo P w.4 R.1Xz.5.5XcXcXb y y.G.G.G H H H H K.%.%.%.vXX.h.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.hXa.R.RXxXx.qX*.i.i.~.~.O.l.z.* 5X. D.7Xm aX#Xo P w.4XlX;Xz.5.6XcXc.8 y y y y.G.G H H H H H.%.%.%.vXX.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.hXxXx.q.qX*.i.i.w.=._.l.lX8.*Xf 6 0.A.FXm.J.IXhX% Y RX-X; Q.5.6XcXcXbXb y.D.GXO C C C CXB C * 1.v.%.v.h.h.h.h.h.h.h",
+".h.h.h.h.h.hXxXx.q.qX*X*.i = =Xj.C.EX8.zXf 5X. D.7Xm aX#X$ PXk 7 R.1Xz Q.5.5 e e e e eXc G.!.L.L.L.L.L.L 'XB.x.%.x C.h.h.h.h.h.h",
+".h.h.h.h.h.;.qX+ Z.:.:.:.:.:.:.b.bXy.U.o 5X. q.A.FXm.J.I.-Xi Y.4.4.1.2.2.2.2.2.2.2.2.ZX,.!.rX@X@X7X7X7X@X@.r.!.x.$.$ f.h.h.h.h.h",
+".h.h.h.h.hX*X* Z.p.p.p.p.p.p.p F F.b X.tX. q D.7Xm aX#X$ PXi k.4.4 RXlXlXlXlXl 9 9Xl SX,.KX7Xp U.|.|.| ; $X@.!.! < <.x.h.h.h.h.h",
+".h.h.h.h.iX*.i ^.p.p.p.p.p.p.p.p.B.b MXyX> @.7 u a.J.IXh PXi k.4.4 7 Y Y Y Y Y Y 7 } tXNX7Xg.|.| `.|.| ; $X@ tX,XiXi <.x.h.h.h.h",
+".h.h.h oX*.i.w ^ F.p.p.p.p.p.p.p.p.B F MXr ) u a.JX#X$ PX%XiXk w wXkXkXkXk.M.MXk.j t xX7Xp.| i i ` `.| ; $X@ t <.@ S.kXiXB.h.h.h",
+".h.h.h.~.i.~.~.' ^.b.b.b.b.b.b F.B.B.B.^.cXrXV.X.I L.-Xh P P P P P P P.-.-.-X - WXNX7Xg.|.|.| V.S.S c cX@XN W } -.@.@.k <.h.h.h",
+".h.h.h.~.~.~._ j.3 r JXd.e X X.b F.B.B FXq.c.].TX$ P.-.-.-.-.-.-.-.- L L L 4.X W x c $ 3 i.|Xp c x x.].].] W.j.j.j.j -.@.@.h.h.h",
+".h.h.e.O.O.O.O.lX8X8Xe.(.o.t.y s M.^ F F F.^ AXN W - P.- L.@ L.,.,X#.n.n L.` WXN cXg 3 3 3Xg cXN.].T.T.T.T.T.X.X.X.`.j.j -.!.h.h",
+".h.h.l.l.l.l.l.lX8.z.*.*Xf 5 0 vXr.cXq F.BXq . c x W.-.- L.@X#.,.,.&.&.&.n.T x c $ 3 i 3Xp c 8.T & &X0 &XwXwXw.s.f.X.X.`.j -.h.h",
+".hXVX8X8X8.z.z.z.*XfXf 5 6X. D @ _Xr.c.^ iXqXq.^ cXN W.` L.@.n.,.,.&.&.f.TXN cXg.Y 3 3Xg cXN d _ B N | BX0X0 & &././.f.X.`.` t.h",
+".h.Q.*.*.*XfXfXf 5 5 6X. 0 D.A.FXmXV.] A .Xq iXqXp c x WX#.@.&.,.,XsXs.T.] c $ U i.Y $ c 8 d N NX6X6X6 N N | |X0 & &./.s.X.X t.h",
+".h 5 5 5 5 5 5 6.> 0 q D.A.7.FXm.J.X.TXN A.^XqXqXqXg cXN.T.f.&XsXsXw dXN cX& U U UXg c 8 _X<.{X5X5.{.{ +X6X6 N # |X0 &Xw./.f.`.h",
+".h.>.>.> 0 0 0 q D.A.7.7.FXmXm a.J L.- W x cXpXq i.Y $ c.].TXs B B d.] c % U i U $ cXr )X5.[.[.[.[.[.[X5X5 + +X6 N N |X0 &./.f.h",
+".j q q D.A.A.A.7.7Xn.FXmXm a.JX#.IX$ P - WXN cXg.Y.Y.YX& cXN dX+X+ 8 cX& ; ; UX& AXr.#X> {X3 { { { {X4X4.[.[X5X5 + @ # |X0 &./.T",
+" 4.7.7.7XnXn.F uXmXm a a.JX#.IX$Xh PXh PX W x c $.Y i U %X7 8 ^ ^ c V ; ` ; % AXr.# {X3 [ [ [ [X3X3X3 { {X4.[.[X5 + v #X< ) &XV",
+" LXmXmXmXmXmXm a a.J.JX#.IX$.- P PX% P.-.-.j WXN cX& U . .X& c.b.bX& ; ; ;X& AXr.#.y n.g.g.).g.).).) n [X3X3 {X4.yX5X5 + #X< )./",
+".I a 7 7 7 7.JX#Xl.@XoXoXh PXiXiXiXi P.-.@ S.k t x c V ; ` U ; . . U ` ; V AXr.# n.g.d.(.(.(.(.dX2X2.).) n [ [X3 o.yX5X5 + #X< _",
+"X$.I.I.I.I.IX$X$X$Xh P PX%Xk Y k kXk P.- LX#X#.X.TXN cXg.Y.Y 3 U U U UX& AXr.#.t.d b. b b b b b b b ].(X2.) n [X3 {X4X5X5 + # )",
+" PX$.<.<.<.< P P.< w w w Y 7.4.4.4 w P.-.,.,.,.,Xs.T ^.b .Xq ` ` ` U ..b ^.4.d b.}.}.} ,X1X1.}.}.}.}X1 b.}X9X9X9 [ X9...... vX<",
+" YX% w w w wXkXk.4.4.4.4 R R.4.4.4 w P.-.,.,.,.,Xs.T ^.b ..Y ` ` ` U ..b ^ w b. , , m m.a.a m m m mXMX1.}.}X9X9.) [X9X9....X> #",
+" k Y Y k k k k R R R RXlX-.1.1 R 7Xk P.-X#.&.&Xs B d 8 cXp ; ` ` ` UXp cXr ~X1X1.a.a.a ( ( ( ( ( ( (.a.aXMX1 b.e ]X2 [ .t.y ~ v",
+".1 R R R R R RXlXlX-.1X;X;Xz.2Xl YXk P L.n.&.&./ d 8X@.VXp ; ; U U ;Xp.VX@XN.+.e +X5 z zX:X: z z z z.9 ( + + v #.e.e ) _ _./.y +",
+" eX-X-X-X-X-.1X;X;X;XzXz Q Q.2Xl YXk.- L.n.&.&.T.]X@Xg % UXp $ . .Xp U $.^.N.# I (Xv.u.u.u.u ! !.u !X:Xv.9 (.aXMX1.e ]X2 I.t o ~",
+".DX;X;X;X;X;XzXzXz Q.5.5.5.5.2Xl YXk.- L L.X.TXNX@.S % % %.V c.b.b.V % % %.W.NXr IXM.u l E E E E l l l !X:Xv (.aXMX1.e ] I I.t ~",
+".x Q Q Q Q Q.5.5.5.5.5.6.6.5.2Xl Y.M.- 4.n.T xX@X& V ; %Xg.NXr ^ ^.NXt % ; %Xg A XXd l EX= j jX=X=X= E l ! !Xv.9.aXMX1.eX2 I.t ~",
+".h.5.5.5.5.5.5.5.6.6XcXcXc e.2Xl Y.MX .j WXNX@.S V V V.S.N 8.# ~ ~Xr.N.W % V V.S AXyXdXa TXjXjXj T T TX= E lX:Xv (XM.H.e.U I I.h",
+".hXc.5.5.6XcXcXcXcXcXcXcXc e.2 9 YXkX W xX@.V 2Xp VX&.NXr.#.) ] b.+.#.NXg VXp VX& A.m J T g g g g gXj TX= E lX:Xv (XMXd.eX2 I.h",
+".h *XcXcXcXcXcXcXbXbXb.8Xb e.2 9 7 } WXNX@.S 2 2 2.SX@ 8.# o.d.}.}.U dXr A.S V V 2.S AXyXa.C.=.=.=.= g g TX= E !Xv.9XM.H.e.U.#.h",
+".h f /XbXbXbXb.8.8.8 y yXb e.2Xl 7 t x.P.V 2 $ 2 :X@Xr.# [.g b.} ,.a + I X AX& 2Xp 2X& A.mXa.=.=.w.=.=.=Xj TX=.C !Xv (XMX1.e.#.h",
+".h.h y.8.8.8.8 y y y y y y e.Z < t.r.P.S 2 2 2.SX@ 8X< ~ n.d. .} m.a +.HXdXy A.S 2 2 2.S AXy >Xj.w.w.w.=.=Xj T E !Xv.9XMXd.U.h.h",
+".h.h f.8 y y y y y y.G y.DXcXiX,.K.P.W h %Xt.VX@.] _X4X3.g.( b , m ( zX: !Xd.m AX& 2 % 2.V A.m >.w.w.w.w.= gXjX=.3X:.9XM.H.#.h.h",
+".h.h.h y y y y.0 H.% C C C.!.!.r.P.S : h h.SX@XN d # { [.g.( bX1.a (X:.u lXa JXy A.S 2 2Xt.S AXy >.'Xj.'XuXu T T.C !Xv J.H.h.h.h",
+".h.h.h.%.0.G.G p.% C ' '.L.L.K.P.W : V :.W.P.] d +.[X3 [.).( bX1.a (X:.u EX= T J.m A.VXt % h.V A.mXa >XaXa >XjX=.C !Xv J.e.h.h.h",
+".h.h.h f.G.G.G.% CXX.P.P.P.PX@.S : : :.S.PXN.T _.{.[ { [.g.( b.} m ( z ! E j T.CXaXy A.S h h h.S A A s s sXy >.C.C !Xv J.+.h.h.h",
+".h.h.h.h.%.G.G C '.P.S.W.W : : h 2 :.W.P x.T | NX5.[ { [.).d b.} m ( z ! EX=Xj g.=Xa.m A.V h 2 h : :.V.V.W O >Xa.C !XvXd.h.h.h.h",
+".h.h.h.h.h.% H C '.P.W.W h h h : :.S.P.r WXV BX6X5.[ {X3.)X2 b.} m ( z.u lX= T g.=Xu >Xy A.S h h h h h.V.V O.'Xa.CX: J.h.h.h.h.h",
+".h.h.h.h.h f H C '.P.W h 2 2 2.V.W.P.K W./ & NX6.{.[ {X3.)X2 b.} m ( z ! lX= T g.=.=.w >.m s.W.V.^ h h.^.^.b >Xa.CX: I.h.h.h.h.h",
+".h.h.h.h.h.h.v 1XBXN c.c M.c.cX7.P.r t -.sX0 |X6.{.[X4X3 n.) ]X1XM.a.9X: l E TXj g.=.=Xj >Xy O O.b.b.b.b.bXy >Xa.3 J.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.v 1.! x 8 8 8 8 x t }.j.X.n & B N +X5X4 { [.).( bX1.a (Xv ! lX= T g.=.=.= =Xj.'.'.'.'.'.'.' >.C.3Xa.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.hXX.v.v.!.!.!.! tX, < }.j.X.sXwX0 NX6X5.[ {X3 nX2.}.}XM +.9X: ! EX= TXj.=.=.=Xj.'.'.'.'.' > > >.3Xa X.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.hXX 1.%.% H.$ <Xi.@ -.j.X.fXwX0 |X6 +.[X4X3 [.)X9.}X1 + (Xv ! l EX= TXj g.=.=.=.=.=.=XjXjX=.CXa X.h.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.h.hXX.v.%.%.$ GXi.@ -.jX .XXw & | N +X5.[ { [ nX9X9 b v.a (XvX: l EX= TXjXjXjXjXjXjXj TX=.CXa X.h.h.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.h.h.hXX.v.% H.$Xi S.@ -.`.X.s &X0 #X6X5.[X4X3 [X9X9.e #XM.a.9XvX: !.C EX= T T T TX=X=.C.CXa X.h.h.h.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.h.h.h.hXX.v.x.$ <Xi.@ -.j.X.f./ & | N +X5.y oX3 [.) ].eX1XM.a (XvXv ! !.3.C.C.C.C.C.C.3Xa X.h.h.h.h.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.h.h.h.h.h.h C.$ <Xi S.@.j.`.X./ &X0 N @ +X5.y { [X2.e.eX1XMXM (.9XvXvX: ! ! ! !X:X: J.h.h.h.h.h.h.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.h.h.h.h.h.h.h f.x <.k.@ -.j.X.f./ & | # vX5X5X4X9X9 [ ) ].eX1.HXMXM (.9.9XvXvXvXv J I.h.h.h.h.h.h.h.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.xXi.k.@.j.`.X.sXwX0 | # +X5X5..X9 _X2 ].e.eXd.HXMXMXM J J JXd.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.hXB <.@ -.j.`.X./ &X0X< # +X5.....t _ I IX2.U.e.eX1Xd.H.H.e.+.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.! -.`.X.f./ & )X< # +.....y./.t I I IX2.U.e.U.#.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h t t.`.f./ & )X< # vX> ~.y o.t.t I I.#.#.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h",
+".h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.TXV./ _ )X< # v + ~ ~ ~.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h"
+};
diff --git a/arts/builder/pics/cr16-action-artsbuilderexecute.png b/arts/builder/pics/cr16-action-artsbuilderexecute.png
new file mode 100644
index 00000000..abd8f18a
--- /dev/null
+++ b/arts/builder/pics/cr16-action-artsbuilderexecute.png
Binary files differ
diff --git a/arts/builder/pics/cr22-action-artsbuilderexecute.png b/arts/builder/pics/cr22-action-artsbuilderexecute.png
new file mode 100644
index 00000000..d4fbdae4
--- /dev/null
+++ b/arts/builder/pics/cr22-action-artsbuilderexecute.png
Binary files differ
diff --git a/arts/builder/pics/hi16-app-artsbuilder.png b/arts/builder/pics/hi16-app-artsbuilder.png
new file mode 100644
index 00000000..cc030d39
--- /dev/null
+++ b/arts/builder/pics/hi16-app-artsbuilder.png
Binary files differ
diff --git a/arts/builder/pics/hi16-app-artscontrol.png b/arts/builder/pics/hi16-app-artscontrol.png
new file mode 100644
index 00000000..1271ba0a
--- /dev/null
+++ b/arts/builder/pics/hi16-app-artscontrol.png
Binary files differ
diff --git a/arts/builder/pics/hisc-app-artsbuilder.svgz b/arts/builder/pics/hisc-app-artsbuilder.svgz
new file mode 100644
index 00000000..72c9647d
--- /dev/null
+++ b/arts/builder/pics/hisc-app-artsbuilder.svgz
Binary files differ
diff --git a/arts/builder/pics/hisc-app-artscontrol.svgz b/arts/builder/pics/hisc-app-artscontrol.svgz
new file mode 100644
index 00000000..98633799
--- /dev/null
+++ b/arts/builder/pics/hisc-app-artscontrol.svgz
Binary files differ
diff --git a/arts/builder/portposdlg.cpp b/arts/builder/portposdlg.cpp
new file mode 100644
index 00000000..240b3a13
--- /dev/null
+++ b/arts/builder/portposdlg.cpp
@@ -0,0 +1,258 @@
+ /*
+
+ Copyright (C) 1998 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "portposdlg.h"
+#include "structureport.h"
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qlistbox.h>
+#include <kbuttonbox.h>
+#include <klocale.h>
+#include <kinputdialog.h>
+#include <qbuttongroup.h>
+#include <qradiobutton.h>
+#include <kapplication.h>
+#include <kiconloader.h>
+#include <kseparator.h>
+#include <qlineedit.h>
+#include <stdio.h>
+#include <arts/debug.h>
+#include <qpushbutton.h>
+#include <kstdguiitem.h>
+
+using namespace std;
+
+PortPosDlg::PortPosDlg(QWidget *parent, Structure *structure) :QDialog(parent,"Props", TRUE)
+{
+ this->structure = structure;
+
+ setCaption(i18n("aRts: Structureport View"));
+
+ QVBoxLayout *mainlayout = new QVBoxLayout(this);
+ //QHBoxLayout *contentslayout = new QHBoxLayout;
+
+// object type
+/*
+ mainlayout->addSpacing(5);
+ QLabel *objectlabel = new QLabel(this);
+ QFont labelfont(objectlabel->font());
+ labelfont.setPointSize(labelfont.pointSize()*3/2);
+ objectlabel->setFont(labelfont);
+ objectlabel->setText(QString(" ")+i18n("Object type: ")+QString(port->owner->name())+QString(" "));
+ objectlabel->setAlignment(AlignCenter);
+ min_size(objectlabel);
+ mainlayout->addWidget(objectlabel);
+*/
+
+// port description
+
+/*
+ mainlayout->addSpacing(5);
+ QLabel *portlabel = new QLabel(this);
+ labelfont.setPointSize(labelfont.pointSize()*4/5);
+ portlabel->setFont(labelfont);
+ portlabel->setText(i18n("Port description: ")+ port->description);
+ min_size(portlabel);
+ portlabel->setAlignment(AlignCenter);
+ mainlayout->addWidget(portlabel);
+
+ int labelwidth = imax(portlabel->sizeHint().width(),objectlabel->sizeHint().width());
+
+ portlabel->setMinimumWidth(labelwidth);
+ objectlabel->setMinimumWidth(labelwidth);
+
+// hruler
+
+ mainlayout->addSpacing(5);
+ KSeparator *ruler = new KSeparator( KSeparator::HLine, this);
+ mainlayout->addWidget(ruler);
+ mainlayout->addSpacing(5);
+ mainlayout->addLayout(contentslayout);
+*/
+// list
+
+ listbox = new QListBox(this);
+
+ update();
+
+ listbox->setMinimumSize(100,200);
+ mainlayout->addWidget(listbox);
+// hruler
+
+ mainlayout->addSpacing(5);
+ KSeparator *ruler2 = new KSeparator( KSeparator::HLine, this);
+ mainlayout->addWidget(ruler2);
+
+// buttons
+
+ QHBoxLayout *buttonlayout = new QHBoxLayout;
+ mainlayout->addSpacing(5);
+ mainlayout->addLayout(buttonlayout);
+ mainlayout->addSpacing(5);
+
+ buttonlayout->addSpacing(5);
+ KButtonBox *bbox = new KButtonBox(this);
+
+ bbox->addButton(KStdGuiItem::help(), this, SLOT( help() ));
+ bbox->addStretch(1);
+
+ KIconLoader iconloader;
+ QButton *raise = bbox->addButton(i18n("&Raise"));
+ raise->setPixmap(iconloader.loadIcon("up", KIcon::Small));
+ connect( raise, SIGNAL( clicked() ), SLOT( raise() ));
+
+ QButton *lower = bbox->addButton(i18n("&Lower"));
+ lower->setPixmap(iconloader.loadIcon("down", KIcon::Small));
+ connect( lower, SIGNAL( clicked() ), SLOT( lower() ));
+
+ QButton *rename = bbox->addButton(i18n("R&ename..."));
+ connect( rename, SIGNAL( clicked() ), SLOT( rename() ));
+
+ QButton *okbutton = bbox->addButton(KStdGuiItem::ok());
+ connect( okbutton, SIGNAL( clicked() ), SLOT(accept() ) );
+
+/*
+ QButton *cancelbutton = bbox->addButton(i18n("Cancel"));
+ connect( cancelbutton, SIGNAL( clicked() ), SLOT(reject() ) );
+*/
+ bbox->layout();
+ //min_size(bbox);
+
+ buttonlayout->addWidget(bbox);
+ buttonlayout->addSpacing(5);
+
+ //mainlayout->activate();
+ mainlayout->freeze();
+}
+
+void PortPosDlg::raise()
+{
+ int i = listbox->currentItem();
+ arts_debug("selected %d",i);
+ if(i < 0) return;
+
+ StructurePort *port = listports[i];
+ assert(port);
+
+ // hmm ok this is ugly that the raise function calls lowerPosition
+ port->lowerPosition();
+ update();
+
+ unsigned long l;
+ for(l=0;l<listports.size();l++)
+ if(listports[l]->id() == port->id())
+ listbox->setCurrentItem(l);
+}
+
+void PortPosDlg::lower()
+{
+ int i = listbox->currentItem();
+ arts_debug("selected %d",i);
+ if(i < 0) return;
+ StructurePort *port = listports[i];
+ assert(port);
+
+ port->raisePosition();
+ update();
+
+ unsigned long l;
+ for(l=0;l<listports.size();l++)
+ if(listports[l]->id() == port->id())
+ listbox->setCurrentItem(l);
+}
+
+void PortPosDlg::rename()
+{
+ int i = listbox->currentItem();
+ arts_debug("selected %d",i);
+ if(i < 0) return;
+ StructurePort *port = listports[i];
+ assert(port);
+
+ bool ok;
+ QString name = KInputDialog::getText( i18n( "Rename Port" ),
+ i18n( "Enter port name:" ), port->name(), &ok, this );
+ if (ok)
+ {
+ arts_debug("rename OK...");
+ port->rename(name.local8Bit());
+ }
+ update();
+
+ unsigned long l;
+ for(l=0;l<listports.size();l++)
+ if(listports[l]->id() == port->id())
+ listbox->setCurrentItem(l);
+}
+void PortPosDlg::update()
+{
+ list<StructureComponent *> &cl = *structure->getComponentList();
+ list<StructureComponent *>::iterator ci;
+
+ listports.erase(listports.begin(), listports.end());
+ listbox->clear();
+
+ // first incoming ports, then outgoing (which are represented by
+ // the opposite directions inside the structure)
+ for(int direction = 0; direction < 2; direction++)
+ {
+ map<long, StructurePort *> pmap;
+ int finddirection = ModulePort::in;
+ int pcount = 0;
+
+ if(direction == 0) finddirection = ModulePort::out;
+
+ for(ci = cl.begin(); ci != cl.end(); ++ci)
+ {
+ StructureComponent *component = *ci;
+ if(component->type() == StructureComponent::ctPort)
+ {
+ StructurePort *port = (StructurePort *)component;
+ if(port->port()->direction == finddirection)
+ {
+ arts_debug("port %s position %ld",
+ port->name().local8Bit().data(), port->position());
+ pmap[port->position()] = port;
+ pcount++;
+ }
+ }
+ }
+ for(int i=0;i<pcount;i++)
+ {
+ StructurePort *port = pmap[i];
+ if (port)
+ {
+ assert(port);
+ listbox->insertItem(port->name(),listports.size());
+ listports.push_back(port);
+ arts_debug("listports.size() is now %d",listports.size());
+ }
+ }
+ }
+ listbox->repaint();
+}
+
+void PortPosDlg::help()
+{
+ KApplication::kApplication()->invokeHelp("", "karts");
+}
+
+#include "portposdlg.moc"
diff --git a/arts/builder/portposdlg.h b/arts/builder/portposdlg.h
new file mode 100644
index 00000000..e9c8f855
--- /dev/null
+++ b/arts/builder/portposdlg.h
@@ -0,0 +1,51 @@
+ /*
+
+ Copyright (C) 1998-1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef __PORTPOSDLG_H_
+#define __PORTPOSDLG_H_
+
+#include "structure.h"
+#include "structureport.h"
+#include <qdialog.h>
+#include <qlistbox.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <vector>
+
+class PortPosDlg :public QDialog {
+ Q_OBJECT
+protected:
+ Structure *structure;
+ QListBox *listbox;
+
+ std::vector<StructurePort *> listports;
+public:
+ PortPosDlg(QWidget *parent, Structure *structure);
+
+ void update();
+public slots:
+ void raise();
+ void lower();
+ void rename();
+ void help();
+};
+
+#endif
diff --git a/arts/builder/propertypanel.cpp b/arts/builder/propertypanel.cpp
new file mode 100644
index 00000000..39c84ac6
--- /dev/null
+++ b/arts/builder/propertypanel.cpp
@@ -0,0 +1,404 @@
+/*
+
+ Copyright (C) 2001 Hans Meine <hans_meine@gmx.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+#include "propertypanel.h"
+#include "propertypanel.moc"
+#include "module.h"
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qbuttongroup.h>
+#include <qradiobutton.h>
+#include <qcombobox.h>
+#include <qlistbox.h>
+#include <qlineedit.h>
+
+#include <arts/debug.h>
+
+PropertyPanel::PropertyPanel( QWidget* parent, const char* name, WFlags fl )
+ : PropertyPanelBase( parent, name, fl ),
+ component(0L),
+ port(0L)
+{
+ setTitleFont();
+ setTitleColors();
+ connect( kapp, SIGNAL( kdisplayFontChanged() ),
+ this, SLOT( setTitleFont() ));
+ connect( kapp, SIGNAL( kdisplayPaletteChanged() ),
+ this, SLOT( setTitleColors() ));
+ connect( portValueGroup, SIGNAL( clicked(int) ),
+ this, SLOT( pvModeChanged(int) ));
+ connect( constantValueEdit, SIGNAL( returnPressed() ),
+ this, SLOT( writePortProperties() ));
+ connect( constantValueComboBox, SIGNAL( activated(int) ),
+ this, SLOT( writePortProperties() ));
+ connect( portCombo, SIGNAL( activated(int) ),
+ this, SLOT( comboPortSelected(int) ));
+ connect( connectButton, SIGNAL( clicked() ),
+ this, SLOT( connectButtonClicked() ));
+
+ constantValueComboBox->hide();
+ setEnabled( false );
+ tipLabel->hide();
+}
+
+void PropertyPanel::setTitleFont()
+{
+ QFont titleFont = font();
+ titleFont.setPointSizeFloat( titleFont.pointSizeFloat()*1.5f );
+ moduleNameLabel->setFont( titleFont );
+ titleFont = font();
+ titleFont.setPointSizeFloat( titleFont.pointSizeFloat()*1.15f );
+ selectedLabel->setFont( titleFont );
+}
+
+QColorGroup PropertyPanel::highlightColorGroup( QColorGroup cg )
+{
+ cg.setColor( QColorGroup::Foreground, cg.highlightedText() );
+ cg.setColor( QColorGroup::Background, cg.highlight() );
+ return cg;
+}
+
+void PropertyPanel::setTitleColors()
+{
+ QPalette palette = titleFrame->palette();
+ palette.setActive( highlightColorGroup( palette.active() ) );
+ palette.setInactive( highlightColorGroup( palette.inactive() ) );
+ palette.setDisabled( highlightColorGroup( palette.disabled() ) );
+ titleFrame->setPalette( palette );
+}
+
+void PropertyPanel::resizeEvent ( QResizeEvent * )
+{
+ if(width()*3 < height()*4)
+ mainBoxLayout->setDirection( QBoxLayout::TopToBottom );
+ else
+ mainBoxLayout->setDirection( QBoxLayout::LeftToRight );
+}
+
+/*************************************************************/
+
+void PropertyPanel::setSelectedComponent( StructureComponent *component )
+{
+ //kdDebug() << QString("PropertyPanel::setSelectedComponent ") << component << endl;
+ if( this->component == component ) return;
+
+ this->component = component;
+ portCombo->clear();
+ modulePorts.clear();
+ if(!component)
+ setEnabled(false);
+ else
+ {
+ moduleNameLabel->setText( component->name() );
+ if(component->pixmap())
+ {
+ moduleIconLabel->setPixmap( *component->pixmap() );
+ moduleIconLabel->setMinimumSize( moduleIconLabel->sizeHint() );
+ moduleIconLabel->show();
+ }
+ else
+ moduleIconLabel->hide();
+
+ component->dumpPorts(modulePorts);
+
+ // fill combobox
+ for(std::list<ModulePort *>::iterator it = modulePorts.begin();
+ it != modulePorts.end(); it++)
+ {
+ QString portTitle = (*it)->description
+ + QString(" (%1)").arg( (*it)->PortDesc.type().direction == Arts::output ?
+ i18n("OUTPUT") : i18n("INPUT") );
+ portCombo->insertItem( portTitle );
+ }
+ //portCombo->setMinimumSize( portCombo->sizeHint() );
+ setEnabled(true);
+ }
+}
+
+void PropertyPanel::setSelectedPort( ModulePort *port )
+{
+ if( this->port == port ) return;
+
+// if( this->port ) writePortProperties( false ); // rereading not necessary here
+
+ this->port = port;
+ if(port)
+ {
+ setSelectedComponent( port->owner );
+ rereadPortProperties();
+ portValueGroup->setEnabled( true );
+ QString tipText = i18n("Tip: Just typing numbers or alphabetic characters starts entering constant values.");
+ tipLabel->setText( tipText );
+ tipLabel->show();
+
+ // select port in combobox - bah, should be easier ;-)
+ portCombo->setCurrentItem(
+ portCombo->listBox()->index(
+ portCombo->listBox()->findItem( port->description + " (" ) ) );
+ }
+ else
+ {
+ portValueGroup->setEnabled( false );
+ tipLabel->hide();
+ }
+}
+
+void PropertyPanel::pvModeChanged( int newMode )
+{
+ if(!port) return; // sanity check
+
+ switch( newMode )
+ {
+ case 0:
+ case 1:
+ writePortProperties();
+ break;
+ case 2:
+ ; // connection should already be established
+ }
+}
+
+void PropertyPanel::comboPortSelected( int number )
+{
+ if(!component) return;
+
+ std::list<ModulePort *>::iterator it;
+ for( it = modulePorts.begin();
+ (it != modulePorts.end()) && number > 0; it++, number--)
+ ;
+ setSelectedPort( *it );
+ emit portSelected( *it );
+}
+
+void PropertyPanel::connectButtonClicked()
+{
+ if(!port) return; // sanity check
+
+ pvConnectionButton->setChecked( true );
+ emit startConnection( port );
+}
+
+// this is a slot connected to changed() of the editing stuff
+void PropertyPanel::writePortProperties()
+{
+ writePortProperties( true );
+}
+
+void PropertyPanel::writePortProperties( bool reread )
+{
+ //kdDebug() << QString("PropertyPanel::writePortProperties") << endl;
+ if(!port) return; // sanity check
+
+ bool dirty = false;
+
+ if(!pvConnectionButton->isChecked())
+ {
+ if(port->PortDesc.isConnected())
+ {
+ port->PortDesc.disconnectAll();
+ dirty = true;
+ }
+ }
+
+ if(pvNotSetButton->isChecked() &&
+ (port->PortDesc.isConnected() || port->PortDesc.hasValue()))
+ {
+ port->PortDesc.hasValue(false);
+ dirty = true;
+ }
+
+ if(pvConstantButton->isChecked())
+ {
+ std::string type = port->PortDesc.type().dataType;
+ QString newvalue = constantValueEdit->text();
+
+ Arts::Any a;
+ a.type = type;
+ Arts::Buffer b;
+ if(type == "float")
+ b.writeFloat(newvalue.toFloat());
+ else if(type == "long")
+ b.writeLong(newvalue.toLong());
+ else if(type == "string")
+ b.writeString(newvalue.local8Bit().data());
+ else if(type == "boolean")
+ {
+ b.writeBool(newvalue.upper() == "TRUE" || newvalue.upper() == "T"
+ || newvalue == "1");
+ }
+ else if(isEnum(type))
+ {
+ b.writeLong(selectedEnumValue(type));
+ }
+
+ if(b.size() > 0)
+ {
+ b.read(a.value, b.size());
+ port->PortDesc.value(a);
+ dirty = true;
+ }
+ }
+
+ if( dirty )
+ emit portPropertiesChanged( port );
+
+ if( reread )
+ rereadPortProperties();
+}
+
+void PropertyPanel::rereadPortProperties()
+{
+ //kdDebug() << QString("PropertyPanel::rereadPortProperties") << endl;
+ if(!port) return; // sanity check
+
+ std::string dataType = port->PortDesc.type().dataType;
+ if(isEnum(dataType))
+ {
+ constantValueEdit->hide();
+ constantValueComboBox->show();
+
+ fillEnumChoices(dataType);
+ }
+ else
+ {
+ constantValueEdit->show();
+ constantValueComboBox->hide();
+ }
+
+ if( port->PortDesc.hasValue() )
+ {
+ pvConstantButton->setChecked( true );
+
+ QString constValue;
+
+ Arts::Any value = port->PortDesc.value();
+ Arts::Buffer b;
+ b.write(value.value);
+
+ if(isEnum(value.type))
+ {
+ long v = b.readLong();
+ constantValueComboBox->setCurrentItem(findEnumIndex(value.type,v));
+ }
+ else
+ {
+ if(value.type == "float")
+ constValue.sprintf("%2.4f", b.readFloat());
+ else if(value.type == "long")
+ constValue.sprintf("%ld", b.readLong());
+ else if(value.type == "string")
+ {
+ std::string s;
+ b.readString(s);
+ constValue = s.c_str();
+ }
+ else if(value.type == "boolean")
+ {
+ if(b.readBool())
+ constValue = "true";
+ else
+ constValue = "false";
+ }
+ else constValue = ("*unknown type* " + value.type).c_str();
+
+ constantValueEdit->setText( constValue );
+ }
+ }
+ else if( port->PortDesc.isConnected() )
+ pvConnectionButton->setChecked( true );
+ else
+ {
+ pvNotSetButton->setChecked( true );
+ constantValueEdit->clear();
+ }
+
+ pvConnectionButton->setEnabled( port->PortDesc.isConnected() );
+}
+
+bool PropertyPanel::eventFilter( QObject *o, QEvent *e )
+{
+ if( port && !constantValueEdit->hasFocus() && (e->type() == QEvent::KeyPress) ) {
+// kdDebug() << QString(" ..is KeyPress") << endl;
+ QString entered = static_cast<QKeyEvent *>(e)->text();
+ bool goodString = entered.length() > 0;
+
+// kdDebug() << QString("pressed '%1'").arg(entered) << endl;
+
+ for( unsigned int i = 0; i < entered.length(); i++)
+ goodString = goodString && entered[i].isLetterOrNumber();
+
+ if( goodString )
+ {
+ pvConstantButton->setChecked( true );
+ constantValueEdit->setText( entered );
+ constantValueEdit->setFocus();
+// kdDebug() << "keyPress used in propPanel" << endl;
+ return TRUE; // eat event
+ }
+ }
+// else
+// kdDebug() << "event type = " << e->type() << " != " << QEvent::KeyPress << endl;
+ return FALSE; // PropertyPanelBase::eventFilter( o, e );
+}
+
+bool PropertyPanel::isEnum(const std::string& type)
+{
+ Arts::InterfaceRepoV2 interfaceRepo = Arts::DynamicCast(Arts::Dispatcher::the()->interfaceRepo());
+ return (interfaceRepo.identifyType(type) == Arts::tiEnum);
+}
+
+void PropertyPanel::fillEnumChoices(const std::string& type)
+{
+ Arts::EnumDef edef =
+ Arts::Dispatcher::the()->interfaceRepo().queryEnum(type);
+
+ constantValueComboBox->clear();
+
+ std::vector<Arts::EnumComponent>::const_iterator eci;
+ for(eci = edef.contents.begin(); eci != edef.contents.end(); ++eci)
+ constantValueComboBox->insertItem(QString::fromUtf8(eci->name.c_str()));
+}
+
+long PropertyPanel::selectedEnumValue(const std::string& type)
+{
+ unsigned int i = (unsigned int)constantValueComboBox->currentItem();
+ Arts::EnumDef edef =
+ Arts::Dispatcher::the()->interfaceRepo().queryEnum(type);
+
+ if(i >= edef.contents.size()) i = 0;
+ if(edef.contents.size() == 0) return 0;
+ return edef.contents[i].value;
+}
+
+int PropertyPanel::findEnumIndex(const std::string& type, long value)
+{
+ Arts::EnumDef edef =
+ Arts::Dispatcher::the()->interfaceRepo().queryEnum(type);
+
+ for(unsigned int i = 0; i < edef.contents.size(); i++)
+ if(edef.contents[i].value == value)
+ return i;
+ return 0;
+}
diff --git a/arts/builder/propertypanel.h b/arts/builder/propertypanel.h
new file mode 100644
index 00000000..90e1b171
--- /dev/null
+++ b/arts/builder/propertypanel.h
@@ -0,0 +1,78 @@
+/*
+
+ Copyright (C) 2001 Hans Meine <hans_meine@gmx.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+#ifndef PROPERTYPANEL_H
+#define PROPERTYPANEL_H
+
+#include "propertypanelbase.h"
+#include "qpalette.h"
+
+#include <vector>
+#include <list>
+#include <string>
+
+class StructureComponent;
+class ModulePort;
+
+class PropertyPanel: public PropertyPanelBase
+{
+ Q_OBJECT
+
+public:
+ PropertyPanel( QWidget* parent = 0, const char* name = 0, WFlags fl = 0 );
+
+public slots:
+ void setSelectedComponent( StructureComponent *component );
+ void setSelectedPort( ModulePort *port );
+
+protected slots:
+ void setTitleFont();
+ void setTitleColors();
+
+ void pvModeChanged( int newMode );
+ void comboPortSelected( int number );
+ void connectButtonClicked();
+
+ void writePortProperties(); // default reread-> true
+ void writePortProperties( bool reread );
+ void rereadPortProperties();
+
+ bool eventFilter( QObject *, QEvent * );
+
+signals:
+ void startConnection( ModulePort * );
+ void portSelected( ModulePort * );
+ void portPropertiesChanged( ModulePort * ); // might be emitted a little bit too often (see writePortP..)
+
+protected:
+ enum ConnType { ctNone, ctValue, ctConnection };
+ void resizeEvent ( QResizeEvent * );
+
+ StructureComponent *component;
+ ModulePort *port;
+ std::list<ModulePort *> modulePorts;
+
+ QColorGroup highlightColorGroup( QColorGroup cg );
+ bool isEnum(const std::string& type);
+ void fillEnumChoices(const std::string& type);
+ long selectedEnumValue(const std::string& type);
+ int findEnumIndex(const std::string& type, long value);
+};
+
+#endif // PROPERTYPANEL_H
diff --git a/arts/builder/propertypanelbase.ui b/arts/builder/propertypanelbase.ui
new file mode 100644
index 00000000..c89abcc6
--- /dev/null
+++ b/arts/builder/propertypanelbase.ui
@@ -0,0 +1,362 @@
+<!DOCTYPE UI><UI version="3.0" stdsetdef="1">
+<class>PropertyPanelBase</class>
+<author>Hans Meine &lt;hans_meine@gmx.net&gt;</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>propertyPanelBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>628</width>
+ <height>484</height>
+ </rect>
+ </property>
+ <property name="layoutMargin" stdset="0">
+ </property>
+ <property name="layoutSpacing" stdset="0">
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>titleFrame</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <property name="layoutMargin" stdset="0">
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>8</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>selectedLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Properties of selected module/port:</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>spacerLayout</cstring>
+ </property>
+ <property name="layoutMargin" stdset="0">
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>mainBoxLayout</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout17</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout13</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>moduleIconLabel</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout16</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>moduleNameLabel</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout10</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>portComboLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Port:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>portCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>portCombo</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer28</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Preferred</enum>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>portValueGroup</cstring>
+ </property>
+ <property name="title">
+ <string>Port Value</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>pvNotSetButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Not set</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout9</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>pvConstantButton</cstring>
+ </property>
+ <property name="text">
+ <string>Constant &amp;value:</string>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>constantValueComboBox</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>constantValueEdit</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>connectionLayout</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>pvConnectionButton</cstring>
+ </property>
+ <property name="text">
+ <string>From connection</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer13</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>connectButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Connect</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>tipLabel</cstring>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter|AlignLeft</set>
+ </property>
+ <property name="wordwrap" stdset="0">
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>pvConstantButton</sender>
+ <signal>clicked()</signal>
+ <receiver>constantValueEdit</receiver>
+ <slot>setFocus()</slot>
+ </connection>
+ <connection>
+ <sender>pvConstantButton</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>constantValueEdit</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>pvConstantButton</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>constantValueComboBox</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<slots>
+ <slot access="protected">setTitleFont()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/arts/builder/qttableview.cpp b/arts/builder/qttableview.cpp
new file mode 100644
index 00000000..7f044d25
--- /dev/null
+++ b/arts/builder/qttableview.cpp
@@ -0,0 +1,2274 @@
+/**********************************************************************
+** $Id$
+**
+** Implementation of QtTableView class
+**
+** Created : 941115
+**
+** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
+**
+** This file contains a class moved out of the Qt GUI Toolkit API. It
+** may be used, distributed and modified without limitation.
+**
+**********************************************************************/
+
+#include "qttableview.h"
+#include "qttableview.moc"
+#ifndef QT_NO_QTTABLEVIEW
+#include "qscrollbar.h"
+#include "qpainter.h"
+#include "qdrawutil.h"
+#include <limits.h>
+
+enum ScrollBarDirtyFlags {
+ verGeometry = 0x01,
+ verSteps = 0x02,
+ verRange = 0x04,
+ verValue = 0x08,
+ horGeometry = 0x10,
+ horSteps = 0x20,
+ horRange = 0x40,
+ horValue = 0x80,
+ verMask = 0x0F,
+ horMask = 0xF0
+};
+
+
+#define HSBEXT horizontalScrollBar()->sizeHint().height()
+#define VSBEXT verticalScrollBar()->sizeHint().width()
+
+
+class QCornerSquare : public QWidget // internal class
+{
+public:
+ QCornerSquare( QWidget *, const char* = 0 );
+ void paintEvent( QPaintEvent * );
+};
+
+QCornerSquare::QCornerSquare( QWidget *parent, const char *name )
+ : QWidget( parent, name )
+{
+}
+
+void QCornerSquare::paintEvent( QPaintEvent * )
+{
+}
+
+
+// NOT REVISED
+/*!
+ \class QtTableView qttableview.h
+ \brief The QtTableView class provides an abstract base for tables.
+
+ \obsolete
+
+ A table view consists of a number of abstract cells organized in rows
+ and columns, and a visible part called a view. The cells are identified
+ with a row index and a column index. The top-left cell is in row 0,
+ column 0.
+
+ The behavior of the widget can be finely tuned using
+ setTableFlags(); a typical subclass will consist of little more than a
+ call to setTableFlags(), some table content manipulation and an
+ implementation of paintCell(). Subclasses that need cells with
+ variable width or height must reimplement cellHeight() and/or
+ cellWidth(). Use updateTableSize() to tell QtTableView when the
+ width or height has changed.
+
+ When you read this documentation, it is important to understand the
+ distinctions among the four pixel coordinate systems involved.
+
+ \list 1
+ \i The \e cell coordinates. (0,0) is the top-left corner of a cell.
+ Cell coordinates are used by functions such as paintCell().
+
+ \i The \e table coordinates. (0,0) is the top-left corner of the cell at
+ row 0 and column 0. These coordinates are absolute; that is, they are
+ independent of what part of the table is visible at the moment. They are
+ used by functions such as setXOffset() or maxYOffset().
+
+ \i The \e widget coordinates. (0,0) is the top-left corner of the widget,
+ \e including the frame. They are used by functions such as repaint().
+
+ \i The \e view coordinates. (0,0) is the top-left corner of the view, \e
+ excluding the frame. This is the least-used coordinate system; it is used by
+ functions such as viewWidth(). \endlist
+
+ It is rather unfortunate that we have to use four different
+ coordinate systems, but there was no alternative to provide a flexible and
+ powerful base class.
+
+ Note: The row,column indices are always given in that order,
+ i.e., first the vertical (row), then the horizontal (column). This is
+ the opposite order of all pixel operations, which take first the
+ horizontal (x) and then the vertical (y).
+
+ <img src=qtablevw-m.png> <img src=qtablevw-w.png>
+
+ \warning the functions setNumRows(), setNumCols(), setCellHeight(),
+ setCellWidth(), setTableFlags() and clearTableFlags() may cause
+ virtual functions such as cellWidth() and cellHeight() to be called,
+ even if autoUpdate() is FALSE. This may cause errors if relevant
+ state variables are not initialized.
+
+ \warning Experience has shown that use of this widget tends to cause
+ more bugs than expected and our analysis indicates that the widget's
+ very flexibility is the problem. If QScrollView or QListBox can
+ easily be made to do the job you need, we recommend subclassing
+ those widgets rather than QtTableView. In addition, QScrollView makes
+ it easy to have child widgets inside tables, which QtTableView
+ doesn't support at all.
+
+ \sa QScrollView
+ \link guibooks.html#fowler GUI Design Handbook: Table\endlink
+*/
+
+
+/*!
+ Constructs a table view. The \a parent, \a name and \f arguments
+ are passed to the QFrame constructor.
+
+ The \link setTableFlags() table flags\endlink are all cleared (set to 0).
+ Set \c Tbl_autoVScrollBar or \c Tbl_autoHScrollBar to get automatic scroll
+ bars and \c Tbl_clipCellPainting to get safe clipping.
+
+ The \link setCellHeight() cell height\endlink and \link setCellWidth()
+ cell width\endlink are set to 0.
+
+ Frame line shapes (QFrame::HLink and QFrame::VLine) are disallowed;
+ see QFrame::setFrameStyle().
+
+ Note that the \a f argument is \e not \link setTableFlags() table
+ flags \endlink but rather \link QWidget::QWidget() widget
+ flags. \endlink
+
+*/
+
+QtTableView::QtTableView( QWidget *parent, const char *name, WFlags f )
+ : QFrame( parent, name, f )
+{
+ nRows = nCols = 0; // zero rows/cols
+ xCellOffs = yCellOffs = 0; // zero offset
+ xCellDelta = yCellDelta = 0; // zero cell offset
+ xOffs = yOffs = 0; // zero total pixel offset
+ cellH = cellW = 0; // user defined cell size
+ tFlags = 0;
+ vScrollBar = hScrollBar = 0; // no scroll bars
+ cornerSquare = 0;
+ sbDirty = 0;
+ eraseInPaint = FALSE;
+ verSliding = FALSE;
+ verSnappingOff = FALSE;
+ horSliding = FALSE;
+ horSnappingOff = FALSE;
+ coveringCornerSquare = FALSE;
+ inSbUpdate = FALSE;
+}
+
+/*!
+ Destroys the table view.
+*/
+
+QtTableView::~QtTableView()
+{
+ delete vScrollBar;
+ delete hScrollBar;
+ delete cornerSquare;
+}
+
+
+/*!
+ \internal
+ Reimplements QWidget::setBackgroundColor() for binary compatibility.
+ \sa setPalette()
+*/
+
+void QtTableView::setBackgroundColor( const QColor &c )
+{
+ QWidget::setBackgroundColor( c );
+}
+
+/*!\reimp
+*/
+
+void QtTableView::setPalette( const QPalette &p )
+{
+ QWidget::setPalette( p );
+}
+
+/*!\reimp
+*/
+
+void QtTableView::show()
+{
+ showOrHideScrollBars();
+ QWidget::show();
+}
+
+
+/*!
+ \overload void QtTableView::repaint( bool erase )
+ Repaints the entire view.
+*/
+
+/*!
+ Repaints the table view directly by calling paintEvent() directly
+ unless updates are disabled.
+
+ Erases the view area \a (x,y,w,h) if \a erase is TRUE. Parameters \a
+ (x,y) are in \e widget coordinates.
+
+ If \a w is negative, it is replaced with <code>width() - x</code>.
+ If \a h is negative, it is replaced with <code>height() - y</code>.
+
+ Doing a repaint() usually is faster than doing an update(), but
+ calling update() many times in a row will generate a single paint
+ event.
+
+ At present, QtTableView is the only widget that reimplements \link
+ QWidget::repaint() repaint()\endlink. It does this because by
+ clearing and then repainting one cell at at time, it can make the
+ screen flicker less than it would otherwise. */
+
+void QtTableView::repaint( int x, int y, int w, int h, bool erase )
+{
+ if ( !isVisible() || testWState(WState_BlockUpdates) )
+ return;
+ if ( w < 0 )
+ w = width() - x;
+ if ( h < 0 )
+ h = height() - y;
+ QRect r( x, y, w, h );
+ if ( r.isEmpty() )
+ return; // nothing to do
+ QPaintEvent e( r );
+ if ( erase && backgroundMode() != NoBackground )
+ eraseInPaint = TRUE; // erase when painting
+ paintEvent( &e );
+ eraseInPaint = FALSE;
+}
+
+/*!
+ \overload void QtTableView::repaint( const QRect &r, bool erase )
+ Replaints rectangle \a r. If \a erase is TRUE draws the background
+ using the palette's background.
+*/
+
+
+/*!
+ \fn int QtTableView::numRows() const
+ Returns the number of rows in the table.
+ \sa numCols(), setNumRows()
+*/
+
+/*!
+ Sets the number of rows of the table to \a rows (must be non-negative).
+ Does not change topCell().
+
+ The table repaints itself automatically if autoUpdate() is set.
+
+ \sa numCols(), setNumCols(), numRows()
+*/
+
+void QtTableView::setNumRows( int rows )
+{
+ if ( rows < 0 ) {
+#if defined(QT_CHECK_RANGE)
+ qWarning( "QtTableView::setNumRows: (%s) Negative argument %d.",
+ name( "unnamed" ), rows );
+#endif
+ return;
+ }
+ if ( nRows == rows )
+ return;
+
+ if ( autoUpdate() && isVisible() ) {
+ int oldLastVisible = lastRowVisible();
+ int oldTopCell = topCell();
+ nRows = rows;
+ if ( autoUpdate() && isVisible() &&
+ ( oldLastVisible != lastRowVisible() || oldTopCell != topCell() ) )
+ repaint( oldTopCell != topCell() );
+ } else {
+ // Be more careful - if destructing, bad things might happen.
+ nRows = rows;
+ }
+ updateScrollBars( verRange );
+ updateFrameSize();
+}
+
+/*!
+ \fn int QtTableView::numCols() const
+ Returns the number of columns in the table.
+ \sa numRows(), setNumCols()
+*/
+
+/*!
+ Sets the number of columns of the table to \a cols (must be non-negative).
+ Does not change leftCell().
+
+ The table repaints itself automatically if autoUpdate() is set.
+
+ \sa numCols(), numRows(), setNumRows()
+*/
+
+void QtTableView::setNumCols( int cols )
+{
+ if ( cols < 0 ) {
+#if defined(QT_CHECK_RANGE)
+ qWarning( "QtTableView::setNumCols: (%s) Negative argument %d.",
+ name( "unnamed" ), cols );
+#endif
+ return;
+ }
+ if ( nCols == cols )
+ return;
+ int oldCols = nCols;
+ nCols = cols;
+ if ( autoUpdate() && isVisible() ) {
+ int maxCol = lastColVisible();
+ if ( maxCol >= oldCols || maxCol >= nCols )
+ repaint();
+ }
+ updateScrollBars( horRange );
+ updateFrameSize();
+}
+
+
+/*!
+ \fn int QtTableView::topCell() const
+ Returns the index of the first row in the table that is visible in
+ the view. The index of the first row is 0.
+ \sa leftCell(), setTopCell()
+*/
+
+/*!
+ Scrolls the table so that \a row becomes the top row.
+ The index of the very first row is 0.
+ \sa setYOffset(), setTopLeftCell(), setLeftCell()
+*/
+
+void QtTableView::setTopCell( int row )
+{
+ setTopLeftCell( row, -1 );
+ return;
+}
+
+/*!
+ \fn int QtTableView::leftCell() const
+ Returns the index of the first column in the table that is visible in
+ the view. The index of the very leftmost column is 0.
+ \sa topCell(), setLeftCell()
+*/
+
+/*!
+ Scrolls the table so that \a col becomes the leftmost
+ column. The index of the leftmost column is 0.
+ \sa setXOffset(), setTopLeftCell(), setTopCell()
+*/
+
+void QtTableView::setLeftCell( int col )
+{
+ setTopLeftCell( -1, col );
+ return;
+}
+
+/*!
+ Scrolls the table so that the cell at row \a row and colum \a
+ col becomes the top-left cell in the view. The cell at the extreme
+ top left of the table is at position (0,0).
+ \sa setLeftCell(), setTopCell(), setOffset()
+*/
+
+void QtTableView::setTopLeftCell( int row, int col )
+{
+ int newX = xOffs;
+ int newY = yOffs;
+
+ if ( col >= 0 ) {
+ if ( cellW ) {
+ newX = col*cellW;
+ if ( newX > maxXOffset() )
+ newX = maxXOffset();
+ } else {
+ newX = 0;
+ while ( col )
+ newX += cellWidth( --col ); // optimize using current! ###
+ }
+ }
+ if ( row >= 0 ) {
+ if ( cellH ) {
+ newY = row*cellH;
+ if ( newY > maxYOffset() )
+ newY = maxYOffset();
+ } else {
+ newY = 0;
+ while ( row )
+ newY += cellHeight( --row ); // optimize using current! ###
+ }
+ }
+ setOffset( newX, newY );
+}
+
+
+/*!
+ \fn int QtTableView::xOffset() const
+
+ Returns the x coordinate in \e table coordinates of the pixel that is
+ currently on the left edge of the view.
+
+ \sa setXOffset(), yOffset(), leftCell() */
+
+/*!
+ Scrolls the table so that \a x becomes the leftmost pixel in the view.
+ The \a x parameter is in \e table coordinates.
+
+ The interaction with \link setTableFlags() Tbl_snapToHGrid
+ \endlink is tricky.
+
+ \sa xOffset(), setYOffset(), setOffset(), setLeftCell()
+*/
+
+void QtTableView::setXOffset( int x )
+{
+ setOffset( x, yOffset() );
+}
+
+/*!
+ \fn int QtTableView::yOffset() const
+
+ Returns the y coordinate in \e table coordinates of the pixel that is
+ currently on the top edge of the view.
+
+ \sa setYOffset(), xOffset(), topCell()
+*/
+
+
+/*!
+ Scrolls the table so that \a y becomes the top pixel in the view.
+ The \a y parameter is in \e table coordinates.
+
+ The interaction with \link setTableFlags() Tbl_snapToVGrid
+ \endlink is tricky.
+
+ \sa yOffset(), setXOffset(), setOffset(), setTopCell()
+*/
+
+void QtTableView::setYOffset( int y )
+{
+ setOffset( xOffset(), y );
+}
+
+/*!
+ Scrolls the table so that \a (x,y) becomes the top-left pixel
+ in the view. Parameters \a (x,y) are in \e table coordinates.
+
+ The interaction with \link setTableFlags() Tbl_snapTo*Grid \endlink
+ is tricky. If \a updateScrBars is TRUE, the scroll bars are
+ updated.
+
+ \sa xOffset(), yOffset(), setXOffset(), setYOffset(), setTopLeftCell()
+*/
+
+void QtTableView::setOffset( int x, int y, bool updateScrBars )
+{
+ if ( (!testTableFlags(Tbl_snapToHGrid) || xCellDelta == 0) &&
+ (!testTableFlags(Tbl_snapToVGrid) || yCellDelta == 0) &&
+ (x == xOffs && y == yOffs) )
+ return;
+
+ if ( x < 0 )
+ x = 0;
+ if ( y < 0 )
+ y = 0;
+
+ if ( cellW ) {
+ if ( x > maxXOffset() )
+ x = maxXOffset();
+ xCellOffs = x / cellW;
+ if ( !testTableFlags(Tbl_snapToHGrid) ) {
+ xCellDelta = (short)(x % cellW);
+ } else {
+ x = xCellOffs*cellW;
+ xCellDelta = 0;
+ }
+ } else {
+ int xn=0, xcd=0, col = 0;
+ while ( col < nCols-1 && x >= xn+(xcd=cellWidth(col)) ) {
+ xn += xcd;
+ col++;
+ }
+ xCellOffs = col;
+ if ( testTableFlags(Tbl_snapToHGrid) ) {
+ xCellDelta = 0;
+ x = xn;
+ } else {
+ xCellDelta = (short)(x-xn);
+ }
+ }
+ if ( cellH ) {
+ if ( y > maxYOffset() )
+ y = maxYOffset();
+ yCellOffs = y / cellH;
+ if ( !testTableFlags(Tbl_snapToVGrid) ) {
+ yCellDelta = (short)(y % cellH);
+ } else {
+ y = yCellOffs*cellH;
+ yCellDelta = 0;
+ }
+ } else {
+ int yn=0, yrd=0, row=0;
+ while ( row < nRows-1 && y >= yn+(yrd=cellHeight(row)) ) {
+ yn += yrd;
+ row++;
+ }
+ yCellOffs = row;
+ if ( testTableFlags(Tbl_snapToVGrid) ) {
+ yCellDelta = 0;
+ y = yn;
+ } else {
+ yCellDelta = (short)(y-yn);
+ }
+ }
+ int dx = (x - xOffs);
+ int dy = (y - yOffs);
+ xOffs = x;
+ yOffs = y;
+ if ( autoUpdate() && isVisible() )
+ scroll( dx, dy );
+ if ( updateScrBars )
+ updateScrollBars( verValue | horValue );
+}
+
+
+/*!
+ \overload int QtTableView::cellWidth() const
+
+ Returns the column width in pixels. Returns 0 if the columns have
+ variable widths.
+
+ \sa setCellWidth(), cellHeight()
+*/
+
+/*!
+ Returns the width of column \a col in pixels.
+
+ This function is virtual and must be reimplemented by subclasses that
+ have variable cell widths. Note that if the total table width
+ changes, updateTableSize() must be called.
+
+ \sa setCellWidth(), cellHeight(), totalWidth(), updateTableSize()
+*/
+
+int QtTableView::cellWidth( int )
+{
+ return cellW;
+}
+
+
+/*!
+ Sets the width in pixels of the table cells to \a cellWidth.
+
+ Setting it to 0 means that the column width is variable. When
+ set to 0 (this is the default) QtTableView calls the virtual function
+ cellWidth() to get the width.
+
+ \sa cellWidth(), setCellHeight(), totalWidth(), numCols()
+*/
+
+void QtTableView::setCellWidth( int cellWidth )
+{
+ if ( cellW == cellWidth )
+ return;
+#if defined(QT_CHECK_RANGE)
+ if ( cellWidth < 0 || cellWidth > SHRT_MAX ) {
+ qWarning( "QtTableView::setCellWidth: (%s) Argument out of range (%d)",
+ name( "unnamed" ), cellWidth );
+ return;
+ }
+#endif
+ cellW = (short)cellWidth;
+
+ updateScrollBars( horSteps | horRange );
+ if ( autoUpdate() && isVisible() )
+ repaint();
+
+}
+
+/*!
+ \overload int QtTableView::cellHeight() const
+
+ Returns the row height, in pixels. Returns 0 if the rows have
+ variable heights.
+
+ \sa setCellHeight(), cellWidth()
+*/
+
+
+/*!
+ Returns the height of row \a row in pixels.
+
+ This function is virtual and must be reimplemented by subclasses that
+ have variable cell heights. Note that if the total table height
+ changes, updateTableSize() must be called.
+
+ \sa setCellHeight(), cellWidth(), totalHeight()
+*/
+
+int QtTableView::cellHeight( int )
+{
+ return cellH;
+}
+
+/*!
+ Sets the height in pixels of the table cells to \a cellHeight.
+
+ Setting it to 0 means that the row height is variable. When set
+ to 0 (this is the default), QtTableView calls the virtual function
+ cellHeight() to get the height.
+
+ \sa cellHeight(), setCellWidth(), totalHeight(), numRows()
+*/
+
+void QtTableView::setCellHeight( int cellHeight )
+{
+ if ( cellH == cellHeight )
+ return;
+#if defined(QT_CHECK_RANGE)
+ if ( cellHeight < 0 || cellHeight > SHRT_MAX ) {
+ qWarning( "QtTableView::setCellHeight: (%s) Argument out of range (%d)",
+ name( "unnamed" ), cellHeight );
+ return;
+ }
+#endif
+ cellH = (short)cellHeight;
+ if ( autoUpdate() && isVisible() )
+ repaint();
+ updateScrollBars( verSteps | verRange );
+}
+
+
+/*!
+ Returns the total width of the table in pixels.
+
+ This function is virtual and should be reimplemented by subclasses that
+ have variable cell widths and a non-trivial cellWidth() function, or a
+ large number of columns in the table.
+
+ The default implementation may be slow for very wide tables.
+
+ \sa cellWidth(), totalHeight() */
+
+int QtTableView::totalWidth()
+{
+ if ( cellW ) {
+ return cellW*nCols;
+ } else {
+ int tw = 0;
+ for( int i = 0 ; i < nCols ; i++ )
+ tw += cellWidth( i );
+ return tw;
+ }
+}
+
+/*!
+ Returns the total height of the table in pixels.
+
+ This function is virtual and should be reimplemented by subclasses that
+ have variable cell heights and a non-trivial cellHeight() function, or a
+ large number of rows in the table.
+
+ The default implementation may be slow for very tall tables.
+
+ \sa cellHeight(), totalWidth()
+*/
+
+int QtTableView::totalHeight()
+{
+ if ( cellH ) {
+ return cellH*nRows;
+ } else {
+ int th = 0;
+ for( int i = 0 ; i < nRows ; i++ )
+ th += cellHeight( i );
+ return th;
+ }
+}
+
+
+/*!
+ \fn uint QtTableView::tableFlags() const
+
+ Returns the union of the table flags that are currently set.
+
+ \sa setTableFlags(), clearTableFlags(), testTableFlags()
+*/
+
+/*!
+ \fn bool QtTableView::testTableFlags( uint f ) const
+
+ Returns TRUE if any of the table flags in \a f are currently set,
+ otherwise FALSE.
+
+ \sa setTableFlags(), clearTableFlags(), tableFlags()
+*/
+
+/*!
+ Sets the table flags to \a f.
+
+ If a flag setting changes the appearance of the table, the table is
+ repainted if - and only if - autoUpdate() is TRUE.
+
+ The table flags are mostly single bits, though there are some multibit
+ flags for convenience. Here is a complete list:
+
+ <dl compact>
+ <dt> Tbl_vScrollBar <dd> - The table has a vertical scroll bar.
+ <dt> Tbl_hScrollBar <dd> - The table has a horizontal scroll bar.
+ <dt> Tbl_autoVScrollBar <dd> - The table has a vertical scroll bar if
+ - and only if - the table is taller than the view.
+ <dt> Tbl_autoHScrollBar <dd> The table has a horizontal scroll bar if
+ - and only if - the table is wider than the view.
+ <dt> Tbl_autoScrollBars <dd> - The union of the previous two flags.
+ <dt> Tbl_clipCellPainting <dd> - The table uses QPainter::setClipRect() to
+ make sure that paintCell() will not draw outside the cell
+ boundaries.
+ <dt> Tbl_cutCellsV <dd> - The table will never show part of a
+ cell at the bottom of the table; if there is not space for all of
+ a cell, the space is left blank.
+ <dt> Tbl_cutCellsH <dd> - The table will never show part of a
+ cell at the right side of the table; if there is not space for all of
+ a cell, the space is left blank.
+ <dt> Tbl_cutCells <dd> - The union of the previous two flags.
+ <dt> Tbl_scrollLastHCell <dd> - When the user scrolls horizontally,
+ let him/her scroll the last cell left until it is at the left
+ edge of the view. If this flag is not set, the user can only scroll
+ to the point where the last cell is completely visible.
+ <dt> Tbl_scrollLastVCell <dd> - When the user scrolls vertically, let
+ him/her scroll the last cell up until it is at the top edge of
+ the view. If this flag is not set, the user can only scroll to the
+ point where the last cell is completely visible.
+ <dt> Tbl_scrollLastCell <dd> - The union of the previous two flags.
+ <dt> Tbl_smoothHScrolling <dd> - The table scrolls as smoothly as
+ possible when the user scrolls horizontally. When this flag is not
+ set, scrolling is done one cell at a time.
+ <dt> Tbl_smoothVScrolling <dd> - The table scrolls as smoothly as
+ possible when scrolling vertically. When this flag is not set,
+ scrolling is done one cell at a time.
+ <dt> Tbl_smoothScrolling <dd> - The union of the previous two flags.
+ <dt> Tbl_snapToHGrid <dd> - Except when the user is actually scrolling,
+ the leftmost column shown snaps to the leftmost edge of the view.
+ <dt> Tbl_snapToVGrid <dd> - Except when the user is actually
+ scrolling, the top row snaps to the top edge of the view.
+ <dt> Tbl_snapToGrid <dd> - The union of the previous two flags.
+ </dl>
+
+ You can specify more than one flag at a time using bitwise OR.
+
+ Example:
+ \code
+ setTableFlags( Tbl_smoothScrolling | Tbl_autoScrollBars );
+ \endcode
+
+ \warning The cutCells options (\c Tbl_cutCells, \c Tbl_cutCellsH and
+ Tbl_cutCellsV) may cause painting problems when scrollbars are
+ enabled. Do not combine cutCells and scrollbars.
+
+
+ \sa clearTableFlags(), testTableFlags(), tableFlags()
+*/
+
+void QtTableView::setTableFlags( uint f )
+{
+ f = (f ^ tFlags) & f; // clear flags already set
+ tFlags |= f;
+
+ bool updateOn = autoUpdate();
+ setAutoUpdate( FALSE );
+
+ uint repaintMask = Tbl_cutCellsV | Tbl_cutCellsH;
+
+ if ( f & Tbl_vScrollBar ) {
+ setVerScrollBar( TRUE );
+ }
+ if ( f & Tbl_hScrollBar ) {
+ setHorScrollBar( TRUE );
+ }
+ if ( f & Tbl_autoVScrollBar ) {
+ updateScrollBars( verRange );
+ }
+ if ( f & Tbl_autoHScrollBar ) {
+ updateScrollBars( horRange );
+ }
+ if ( f & Tbl_scrollLastHCell ) {
+ updateScrollBars( horRange );
+ }
+ if ( f & Tbl_scrollLastVCell ) {
+ updateScrollBars( verRange );
+ }
+ if ( f & Tbl_snapToHGrid ) {
+ updateScrollBars( horRange );
+ }
+ if ( f & Tbl_snapToVGrid ) {
+ updateScrollBars( verRange );
+ }
+ if ( f & Tbl_snapToGrid ) { // Note: checks for 2 flags
+ if ( (f & Tbl_snapToHGrid) != 0 && xCellDelta != 0 || //have to scroll?
+ (f & Tbl_snapToVGrid) != 0 && yCellDelta != 0 ) {
+ snapToGrid( (f & Tbl_snapToHGrid) != 0, // do snapping
+ (f & Tbl_snapToVGrid) != 0 );
+ repaintMask |= Tbl_snapToGrid; // repaint table
+ }
+ }
+
+ if ( updateOn ) {
+ setAutoUpdate( TRUE );
+ updateScrollBars();
+ if ( isVisible() && (f & repaintMask) )
+ repaint();
+ }
+
+}
+
+/*!
+ Clears the \link setTableFlags() table flags\endlink that are set
+ in \a f.
+
+ Example (clears a single flag):
+ \code
+ clearTableFlags( Tbl_snapToGrid );
+ \endcode
+
+ The default argument clears all flags.
+
+ \sa setTableFlags(), testTableFlags(), tableFlags()
+*/
+
+void QtTableView::clearTableFlags( uint f )
+{
+ f = (f ^ ~tFlags) & f; // clear flags that are already 0
+ tFlags &= ~f;
+
+ bool updateOn = autoUpdate();
+ setAutoUpdate( FALSE );
+
+ uint repaintMask = Tbl_cutCellsV | Tbl_cutCellsH;
+
+ if ( f & Tbl_vScrollBar ) {
+ setVerScrollBar( FALSE );
+ }
+ if ( f & Tbl_hScrollBar ) {
+ setHorScrollBar( FALSE );
+ }
+ if ( f & Tbl_scrollLastHCell ) {
+ int maxX = maxXOffset();
+ if ( xOffs > maxX ) {
+ setOffset( maxX, yOffs );
+ repaintMask |= Tbl_scrollLastHCell;
+ }
+ updateScrollBars( horRange );
+ }
+ if ( f & Tbl_scrollLastVCell ) {
+ int maxY = maxYOffset();
+ if ( yOffs > maxY ) {
+ setOffset( xOffs, maxY );
+ repaintMask |= Tbl_scrollLastVCell;
+ }
+ updateScrollBars( verRange );
+ }
+ if ( f & Tbl_smoothScrolling ) { // Note: checks for 2 flags
+ if ((f & Tbl_smoothHScrolling) != 0 && xCellDelta != 0 ||//must scroll?
+ (f & Tbl_smoothVScrolling) != 0 && yCellDelta != 0 ) {
+ snapToGrid( (f & Tbl_smoothHScrolling) != 0, // do snapping
+ (f & Tbl_smoothVScrolling) != 0 );
+ repaintMask |= Tbl_smoothScrolling; // repaint table
+ }
+ }
+ if ( f & Tbl_snapToHGrid ) {
+ updateScrollBars( horRange );
+ }
+ if ( f & Tbl_snapToVGrid ) {
+ updateScrollBars( verRange );
+ }
+ if ( updateOn ) {
+ setAutoUpdate( TRUE );
+ updateScrollBars(); // returns immediately if nothing to do
+ if ( isVisible() && (f & repaintMask) )
+ repaint();
+ }
+
+}
+
+
+/*!
+ \fn bool QtTableView::autoUpdate() const
+
+ Returns TRUE if the view updates itself automatically whenever it
+ is changed in some way.
+
+ \sa setAutoUpdate()
+*/
+
+/*!
+ Sets the auto-update option of the table view to \a enable.
+
+ If \a enable is TRUE (this is the default), the view updates itself
+ automatically whenever it has changed in some way (for example, when a
+ \link setTableFlags() flag\endlink is changed).
+
+ If \a enable is FALSE, the view does NOT repaint itself or update
+ its internal state variables when it is changed. This can be
+ useful to avoid flicker during large changes and is singularly
+ useless otherwise. Disable auto-update, do the changes, re-enable
+ auto-update and call repaint().
+
+ \warning Do not leave the view in this state for a long time
+ (i.e., between events). If, for example, the user interacts with the
+ view when auto-update is off, strange things can happen.
+
+ Setting auto-update to TRUE does not repaint the view; you must call
+ repaint() to do this.
+
+ \sa autoUpdate(), repaint()
+*/
+
+void QtTableView::setAutoUpdate( bool enable )
+{
+ if ( isUpdatesEnabled() == enable )
+ return;
+ setUpdatesEnabled( enable );
+ if ( enable ) {
+ showOrHideScrollBars();
+ updateScrollBars();
+ }
+}
+
+
+/*!
+ Repaints the cell at row \a row, column \a col if it is inside the view.
+
+ If \a erase is TRUE, the relevant part of the view is cleared to the
+ background color/pixmap before the contents are repainted.
+
+ \sa isVisible()
+*/
+
+void QtTableView::updateCell( int row, int col, bool erase )
+{
+ int xPos, yPos;
+ if ( !colXPos( col, &xPos ) )
+ return;
+ if ( !rowYPos( row, &yPos ) )
+ return;
+ QRect uR = QRect( xPos, yPos,
+ cellW ? cellW : cellWidth(col),
+ cellH ? cellH : cellHeight(row) );
+ repaint( uR.intersect(viewRect()), erase );
+}
+
+
+/*!
+ \fn QRect QtTableView::cellUpdateRect() const
+
+ This function should be called only from the paintCell() function in
+ subclasses. It returns the portion of a cell that actually needs to be
+ updated in \e cell coordinates. This is useful only for non-trivial
+ paintCell().
+
+*/
+
+/*!
+ Returns the rectangle that is the actual table, excluding any
+ frame, in \e widget coordinates.
+*/
+
+QRect QtTableView::viewRect() const
+{
+ return QRect( frameWidth(), frameWidth(), viewWidth(), viewHeight() );
+}
+
+
+/*!
+ Returns the index of the last (bottom) row in the view.
+ The index of the first row is 0.
+
+ If no rows are visible it returns -1. This can happen if the
+ view is too small for the first row and Tbl_cutCellsV is set.
+
+ \sa lastColVisible()
+*/
+
+int QtTableView::lastRowVisible() const
+{
+ int cellMaxY;
+ int row = findRawRow( maxViewY(), &cellMaxY );
+ if ( row == -1 || row >= nRows ) { // maxViewY() past end?
+ row = nRows - 1; // yes: return last row
+ } else {
+ if ( testTableFlags(Tbl_cutCellsV) && cellMaxY > maxViewY() ) {
+ if ( row == yCellOffs ) // cut by right margin?
+ return -1; // yes, nothing in the view
+ else
+ row = row - 1; // cut by margin, one back
+ }
+ }
+ return row;
+}
+
+/*!
+ Returns the index of the last (right) column in the view.
+ The index of the first column is 0.
+
+ If no columns are visible it returns -1. This can happen if the
+ view is too narrow for the first column and Tbl_cutCellsH is set.
+
+ \sa lastRowVisible()
+*/
+
+int QtTableView::lastColVisible() const
+{
+ int cellMaxX;
+ int col = findRawCol( maxViewX(), &cellMaxX );
+ if ( col == -1 || col >= nCols ) { // maxViewX() past end?
+ col = nCols - 1; // yes: return last col
+ } else {
+ if ( testTableFlags(Tbl_cutCellsH) && cellMaxX > maxViewX() ) {
+ if ( col == xCellOffs ) // cut by bottom margin?
+ return -1; // yes, nothing in the view
+ else
+ col = col - 1; // cell by margin, one back
+ }
+ }
+ return col;
+}
+
+/*!
+ Returns TRUE if \a row is at least partially visible.
+ \sa colIsVisible()
+*/
+
+bool QtTableView::rowIsVisible( int row ) const
+{
+ return rowYPos( row, 0 );
+}
+
+/*!
+ Returns TRUE if \a col is at least partially visible.
+ \sa rowIsVisible()
+*/
+
+bool QtTableView::colIsVisible( int col ) const
+{
+ return colXPos( col, 0 );
+}
+
+
+/*!
+ \internal
+ Called when both scroll bars are active at the same time. Covers the
+ bottom left corner between the two scroll bars with an empty widget.
+*/
+
+void QtTableView::coverCornerSquare( bool enable )
+{
+ coveringCornerSquare = enable;
+ if ( !cornerSquare && enable ) {
+ cornerSquare = new QCornerSquare( this );
+ Q_CHECK_PTR( cornerSquare );
+ cornerSquare->setGeometry( maxViewX() + frameWidth() + 1,
+ maxViewY() + frameWidth() + 1,
+ VSBEXT,
+ HSBEXT);
+ }
+ if ( autoUpdate() && cornerSquare ) {
+ if ( enable )
+ cornerSquare->show();
+ else
+ cornerSquare->hide();
+ }
+}
+
+
+/*!
+ \internal
+ Scroll the view to a position such that:
+
+ If \a horizontal is TRUE, the leftmost column shown fits snugly
+ with the left edge of the view.
+
+ If \a vertical is TRUE, the top row shown fits snugly with the top
+ of the view.
+
+ You can achieve the same effect automatically by setting any of the
+ \link setTableFlags() Tbl_snapTo*Grid \endlink table flags.
+*/
+
+void QtTableView::snapToGrid( bool horizontal, bool vertical )
+{
+ int newXCell = -1;
+ int newYCell = -1;
+ if ( horizontal && xCellDelta != 0 ) {
+ int w = cellW ? cellW : cellWidth( xCellOffs );
+ if ( xCellDelta >= w/2 )
+ newXCell = xCellOffs + 1;
+ else
+ newXCell = xCellOffs;
+ }
+ if ( vertical && yCellDelta != 0 ) {
+ int h = cellH ? cellH : cellHeight( yCellOffs );
+ if ( yCellDelta >= h/2 )
+ newYCell = yCellOffs + 1;
+ else
+ newYCell = yCellOffs;
+ }
+ setTopLeftCell( newYCell, newXCell ); //row,column
+}
+
+/*!
+ \internal
+ This internal slot is connected to the horizontal scroll bar's
+ QScrollBar::valueChanged() signal.
+
+ Moves the table horizontally to offset \a val without updating the
+ scroll bar.
+*/
+
+void QtTableView::horSbValue( int val )
+{
+ if ( horSliding ) {
+ horSliding = FALSE;
+ if ( horSnappingOff ) {
+ horSnappingOff = FALSE;
+ tFlags |= Tbl_snapToHGrid;
+ }
+ }
+ setOffset( val, yOffs, FALSE );
+}
+
+/*!
+ \internal
+ This internal slot is connected to the horizontal scroll bar's
+ QScrollBar::sliderMoved() signal.
+
+ Scrolls the table smoothly horizontally even if \c Tbl_snapToHGrid is set.
+*/
+
+void QtTableView::horSbSliding( int val )
+{
+ if ( testTableFlags(Tbl_snapToHGrid) &&
+ testTableFlags(Tbl_smoothHScrolling) ) {
+ tFlags &= ~Tbl_snapToHGrid; // turn off snapping while sliding
+ setOffset( val, yOffs, FALSE );
+ tFlags |= Tbl_snapToHGrid; // turn on snapping again
+ } else {
+ setOffset( val, yOffs, FALSE );
+ }
+}
+
+/*!
+ \internal
+ This internal slot is connected to the horizontal scroll bar's
+ QScrollBar::sliderReleased() signal.
+*/
+
+void QtTableView::horSbSlidingDone( )
+{
+ if ( testTableFlags(Tbl_snapToHGrid) &&
+ testTableFlags(Tbl_smoothHScrolling) )
+ snapToGrid( TRUE, FALSE );
+}
+
+/*!
+ \internal
+ This internal slot is connected to the vertical scroll bar's
+ QScrollBar::valueChanged() signal.
+
+ Moves the table vertically to offset \a val without updating the
+ scroll bar.
+*/
+
+void QtTableView::verSbValue( int val )
+{
+ if ( verSliding ) {
+ verSliding = FALSE;
+ if ( verSnappingOff ) {
+ verSnappingOff = FALSE;
+ tFlags |= Tbl_snapToVGrid;
+ }
+ }
+ setOffset( xOffs, val, FALSE );
+}
+
+/*!
+ \internal
+ This internal slot is connected to the vertical scroll bar's
+ QScrollBar::sliderMoved() signal.
+
+ Scrolls the table smoothly vertically even if \c Tbl_snapToVGrid is set.
+*/
+
+void QtTableView::verSbSliding( int val )
+{
+ if ( testTableFlags(Tbl_snapToVGrid) &&
+ testTableFlags(Tbl_smoothVScrolling) ) {
+ tFlags &= ~Tbl_snapToVGrid; // turn off snapping while sliding
+ setOffset( xOffs, val, FALSE );
+ tFlags |= Tbl_snapToVGrid; // turn on snapping again
+ } else {
+ setOffset( xOffs, val, FALSE );
+ }
+}
+
+/*!
+ \internal
+ This internal slot is connected to the vertical scroll bar's
+ QScrollBar::sliderReleased() signal.
+*/
+
+void QtTableView::verSbSlidingDone( )
+{
+ if ( testTableFlags(Tbl_snapToVGrid) &&
+ testTableFlags(Tbl_smoothVScrolling) )
+ snapToGrid( FALSE, TRUE );
+}
+
+
+/*!
+ This virtual function is called before painting of table cells
+ is started. It can be reimplemented by subclasses that want to
+ to set up the painter in a special way and that do not want to
+ do so for each cell.
+*/
+
+void QtTableView::setupPainter( QPainter * )
+{
+}
+
+/*!
+ \fn void QtTableView::paintCell( QPainter *p, int row, int col )
+
+ This pure virtual function is called to paint the single cell at \a
+ (row,col) using \a p, which is open when paintCell() is called and
+ must remain open.
+
+ The coordinate system is \link QPainter::translate() translated \endlink
+ so that the origin is at the top-left corner of the cell to be
+ painted, i.e. \e cell coordinates. Do not scale or shear the coordinate
+ system (or if you do, restore the transformation matrix before you
+ return).
+
+ The painter is not clipped by default and for maximum efficiency. For safety,
+ call setTableFlags(Tbl_clipCellPainting) to enable clipping.
+
+ \sa paintEvent(), setTableFlags() */
+
+
+/*!
+ Handles paint events, \a e, for the table view.
+
+ Calls paintCell() for the cells that needs to be repainted.
+*/
+
+void QtTableView::paintEvent( QPaintEvent *e )
+{
+ QRect updateR = e->rect(); // update rectangle
+ if ( sbDirty ) {
+ bool e = eraseInPaint;
+ updateScrollBars();
+ eraseInPaint = e;
+ }
+
+ QPainter paint( this );
+
+ if ( !contentsRect().contains( updateR, TRUE ) ) {// update frame ?
+ drawFrame( &paint );
+ if ( updateR.left() < frameWidth() ) //###
+ updateR.setLeft( frameWidth() );
+ if ( updateR.top() < frameWidth() )
+ updateR.setTop( frameWidth() );
+ }
+
+ int maxWX = maxViewX();
+ int maxWY = maxViewY();
+ if ( updateR.right() > maxWX )
+ updateR.setRight( maxWX );
+ if ( updateR.bottom() > maxWY )
+ updateR.setBottom( maxWY );
+
+ setupPainter( &paint ); // prepare for painting table
+
+ int firstRow = findRow( updateR.y() );
+ int firstCol = findCol( updateR.x() );
+ int xStart, yStart;
+ if ( !colXPos( firstCol, &xStart ) || !rowYPos( firstRow, &yStart ) ) {
+ paint.eraseRect( updateR ); // erase area outside cells but in view
+ return;
+ }
+ int maxX = updateR.right();
+ int maxY = updateR.bottom();
+ int row = firstRow;
+ int col;
+ int yPos = yStart;
+ int xPos = maxX+1; // in case the while() is empty
+ int nextX;
+ int nextY;
+ QRect winR = viewRect();
+ QRect cellR;
+ QRect cellUR;
+#ifndef QT_NO_TRANSFORMATIONS
+ QWMatrix matrix;
+#endif
+
+ while ( yPos <= maxY && row < nRows ) {
+ nextY = yPos + (cellH ? cellH : cellHeight( row ));
+ if ( testTableFlags( Tbl_cutCellsV ) && nextY > ( maxWY + 1 ) )
+ break;
+ col = firstCol;
+ xPos = xStart;
+ while ( xPos <= maxX && col < nCols ) {
+ nextX = xPos + (cellW ? cellW : cellWidth( col ));
+ if ( testTableFlags( Tbl_cutCellsH ) && nextX > ( maxWX + 1 ) )
+ break;
+
+ cellR.setRect( xPos, yPos, cellW ? cellW : cellWidth(col),
+ cellH ? cellH : cellHeight(row) );
+ cellUR = cellR.intersect( updateR );
+ if ( cellUR.isValid() ) {
+ cellUpdateR = cellUR;
+ cellUpdateR.moveBy( -xPos, -yPos ); // cell coordinates
+ if ( eraseInPaint )
+ paint.eraseRect( cellUR );
+
+#ifndef QT_NO_TRANSFORMATIONS
+ matrix.translate( xPos, yPos );
+ paint.setWorldMatrix( matrix );
+ if ( testTableFlags(Tbl_clipCellPainting) ||
+ frameWidth() > 0 && !winR.contains( cellR ) ) { //##arnt
+ paint.setClipRect( cellUR );
+ paintCell( &paint, row, col );
+ paint.setClipping( FALSE );
+ } else {
+ paintCell( &paint, row, col );
+ }
+ matrix.reset();
+ paint.setWorldMatrix( matrix );
+#else
+ paint.translate( xPos, yPos );
+ if ( testTableFlags(Tbl_clipCellPainting) ||
+ frameWidth() > 0 && !winR.contains( cellR ) ) { //##arnt
+ paint.setClipRect( cellUR );
+ paintCell( &paint, row, col );
+ paint.setClipping( FALSE );
+ } else {
+ paintCell( &paint, row, col );
+ }
+ paint.translate( -xPos, -yPos );
+#endif
+ }
+ col++;
+ xPos = nextX;
+ }
+ row++;
+ yPos = nextY;
+ }
+
+ // while painting we have to erase any areas in the view that
+ // are not covered by cells but are covered by the paint event
+ // rectangle these must be erased. We know that xPos is the last
+ // x pixel updated + 1 and that yPos is the last y pixel updated + 1.
+
+ // Note that this needs to be done regardless whether we do
+ // eraseInPaint or not. Reason: a subclass may implement
+ // flicker-freeness and encourage the use of repaint(FALSE).
+ // The subclass, however, cannot draw all pixels, just those
+ // inside the cells. So QtTableView is reponsible for all pixels
+ // outside the cells.
+
+ QRect viewR = viewRect();
+ const QColorGroup g = colorGroup();
+
+ if ( xPos <= maxX ) {
+ QRect r = viewR;
+ r.setLeft( xPos );
+ r.setBottom( yPos<maxY?yPos:maxY );
+ if ( inherits( "QMultiLineEdit" ) )
+ paint.fillRect( r.intersect( updateR ), g.base() );
+ else
+ paint.eraseRect( r.intersect( updateR ) );
+ }
+ if ( yPos <= maxY ) {
+ QRect r = viewR;
+ r.setTop( yPos );
+ if ( inherits( "QMultiLineEdit" ) )
+ paint.fillRect( r.intersect( updateR ), g.base() );
+ else
+ paint.eraseRect( r.intersect( updateR ) );
+ }
+}
+
+/*!\reimp
+*/
+void QtTableView::resizeEvent( QResizeEvent * )
+{
+ updateScrollBars( horValue | verValue | horSteps | horGeometry | horRange |
+ verSteps | verGeometry | verRange );
+ showOrHideScrollBars();
+ updateFrameSize();
+ int maxX = QMIN( xOffs, maxXOffset() ); // ### can be slow
+ int maxY = QMIN( yOffs, maxYOffset() );
+ setOffset( maxX, maxY );
+}
+
+
+/*!
+ Redraws all visible cells in the table view.
+*/
+
+void QtTableView::updateView()
+{
+ repaint( viewRect() );
+}
+
+/*!
+ Returns a pointer to the vertical scroll bar mainly so you can
+ connect() to its signals. Note that the scroll bar works in pixel
+ values; use findRow() to translate to cell numbers.
+*/
+
+QScrollBar *QtTableView::verticalScrollBar() const
+{
+ QtTableView *that = (QtTableView*)this; // semantic const
+ if ( !vScrollBar ) {
+ QScrollBar *sb = new QScrollBar( QScrollBar::Vertical, that );
+#ifndef QT_NO_CURSOR
+ sb->setCursor( arrowCursor );
+#endif
+ sb->resize( sb->sizeHint() ); // height is irrelevant
+ Q_CHECK_PTR(sb);
+ sb->setTracking( FALSE );
+ sb->setFocusPolicy( NoFocus );
+ connect( sb, SIGNAL(valueChanged(int)),
+ SLOT(verSbValue(int)));
+ connect( sb, SIGNAL(sliderMoved(int)),
+ SLOT(verSbSliding(int)));
+ connect( sb, SIGNAL(sliderReleased()),
+ SLOT(verSbSlidingDone()));
+ sb->hide();
+ that->vScrollBar = sb;
+ return sb;
+ }
+ return vScrollBar;
+}
+
+/*!
+ Returns a pointer to the horizontal scroll bar mainly so you can
+ connect() to its signals. Note that the scroll bar works in pixel
+ values; use findCol() to translate to cell numbers.
+*/
+
+QScrollBar *QtTableView::horizontalScrollBar() const
+{
+ QtTableView *that = (QtTableView*)this; // semantic const
+ if ( !hScrollBar ) {
+ QScrollBar *sb = new QScrollBar( QScrollBar::Horizontal, that );
+#ifndef QT_NO_CURSOR
+ sb->setCursor( arrowCursor );
+#endif
+ sb->resize( sb->sizeHint() ); // width is irrelevant
+ sb->setFocusPolicy( NoFocus );
+ Q_CHECK_PTR(sb);
+ sb->setTracking( FALSE );
+ connect( sb, SIGNAL(valueChanged(int)),
+ SLOT(horSbValue(int)));
+ connect( sb, SIGNAL(sliderMoved(int)),
+ SLOT(horSbSliding(int)));
+ connect( sb, SIGNAL(sliderReleased()),
+ SLOT(horSbSlidingDone()));
+ sb->hide();
+ that->hScrollBar = sb;
+ return sb;
+ }
+ return hScrollBar;
+}
+
+/*!
+ Enables or disables the horizontal scroll bar, as required by
+ setAutoUpdate() and the \link setTableFlags() table flags\endlink.
+*/
+
+void QtTableView::setHorScrollBar( bool on, bool update )
+{
+ if ( on ) {
+ tFlags |= Tbl_hScrollBar;
+ horizontalScrollBar(); // created
+ if ( update )
+ updateScrollBars( horMask | verMask );
+ else
+ sbDirty = sbDirty | (horMask | verMask);
+ if ( testTableFlags( Tbl_vScrollBar ) )
+ coverCornerSquare( TRUE );
+ if ( autoUpdate() )
+ sbDirty = sbDirty | horMask;
+ } else {
+ tFlags &= ~Tbl_hScrollBar;
+ if ( !hScrollBar )
+ return;
+ coverCornerSquare( FALSE );
+ bool hideScrollBar = autoUpdate() && hScrollBar->isVisible();
+ if ( hideScrollBar )
+ hScrollBar->hide();
+ if ( update )
+ updateScrollBars( verMask );
+ else
+ sbDirty = sbDirty | verMask;
+ if ( hideScrollBar && isVisible() )
+ repaint( hScrollBar->x(), hScrollBar->y(),
+ width() - hScrollBar->x(), hScrollBar->height() );
+ }
+ if ( update )
+ updateFrameSize();
+}
+
+
+/*!
+ Enables or disables the vertical scroll bar, as required by
+ setAutoUpdate() and the \link setTableFlags() table flags\endlink.
+*/
+
+void QtTableView::setVerScrollBar( bool on, bool update )
+{
+ if ( on ) {
+ tFlags |= Tbl_vScrollBar;
+ verticalScrollBar(); // created
+ if ( update )
+ updateScrollBars( verMask | horMask );
+ else
+ sbDirty = sbDirty | (horMask | verMask);
+ if ( testTableFlags( Tbl_hScrollBar ) )
+ coverCornerSquare( TRUE );
+ if ( autoUpdate() )
+ sbDirty = sbDirty | verMask;
+ } else {
+ tFlags &= ~Tbl_vScrollBar;
+ if ( !vScrollBar )
+ return;
+ coverCornerSquare( FALSE );
+ bool hideScrollBar = autoUpdate() && vScrollBar->isVisible();
+ if ( hideScrollBar )
+ vScrollBar->hide();
+ if ( update )
+ updateScrollBars( horMask );
+ else
+ sbDirty = sbDirty | horMask;
+ if ( hideScrollBar && isVisible() )
+ repaint( vScrollBar->x(), vScrollBar->y(),
+ vScrollBar->width(), height() - vScrollBar->y() );
+ }
+ if ( update )
+ updateFrameSize();
+}
+
+
+
+
+int QtTableView::findRawRow( int yPos, int *cellMaxY, int *cellMinY,
+ bool goOutsideView ) const
+{
+ int r = -1;
+ if ( nRows == 0 )
+ return r;
+ if ( goOutsideView || yPos >= minViewY() && yPos <= maxViewY() ) {
+ if ( yPos < minViewY() ) {
+#if defined(QT_CHECK_RANGE)
+ qWarning( "QtTableView::findRawRow: (%s) internal error: "
+ "yPos < minViewY() && goOutsideView "
+ "not supported. (%d,%d)",
+ name( "unnamed" ), yPos, yOffs );
+#endif
+ return -1;
+ }
+ if ( cellH ) { // uniform cell height
+ r = (yPos - minViewY() + yCellDelta)/cellH; // cell offs from top
+ if ( cellMaxY )
+ *cellMaxY = (r + 1)*cellH + minViewY() - yCellDelta - 1;
+ if ( cellMinY )
+ *cellMinY = r*cellH + minViewY() - yCellDelta;
+ r += yCellOffs; // absolute cell index
+ } else { // variable cell height
+ QtTableView *tw = (QtTableView *)this;
+ r = yCellOffs;
+ int h = minViewY() - yCellDelta; //##arnt3
+ int oldH = h;
+ Q_ASSERT( r < nRows );
+ while ( r < nRows ) {
+ oldH = h;
+ h += tw->cellHeight( r ); // Start of next cell
+ if ( yPos < h )
+ break;
+ r++;
+ }
+ if ( cellMaxY )
+ *cellMaxY = h - 1;
+ if ( cellMinY )
+ *cellMinY = oldH;
+ }
+ }
+ return r;
+
+}
+
+
+int QtTableView::findRawCol( int xPos, int *cellMaxX, int *cellMinX ,
+ bool goOutsideView ) const
+{
+ int c = -1;
+ if ( nCols == 0 )
+ return c;
+ if ( goOutsideView || xPos >= minViewX() && xPos <= maxViewX() ) {
+ if ( xPos < minViewX() ) {
+#if defined(QT_CHECK_RANGE)
+ qWarning( "QtTableView::findRawCol: (%s) internal error: "
+ "xPos < minViewX() && goOutsideView "
+ "not supported. (%d,%d)",
+ name( "unnamed" ), xPos, xOffs );
+#endif
+ return -1;
+ }
+ if ( cellW ) { // uniform cell width
+ c = (xPos - minViewX() + xCellDelta)/cellW; //cell offs from left
+ if ( cellMaxX )
+ *cellMaxX = (c + 1)*cellW + minViewX() - xCellDelta - 1;
+ if ( cellMinX )
+ *cellMinX = c*cellW + minViewX() - xCellDelta;
+ c += xCellOffs; // absolute cell index
+ } else { // variable cell width
+ QtTableView *tw = (QtTableView *)this;
+ c = xCellOffs;
+ int w = minViewX() - xCellDelta; //##arnt3
+ int oldW = w;
+ Q_ASSERT( c < nCols );
+ while ( c < nCols ) {
+ oldW = w;
+ w += tw->cellWidth( c ); // Start of next cell
+ if ( xPos < w )
+ break;
+ c++;
+ }
+ if ( cellMaxX )
+ *cellMaxX = w - 1;
+ if ( cellMinX )
+ *cellMinX = oldW;
+ }
+ }
+ return c;
+}
+
+
+/*!
+ Returns the index of the row at position \a yPos, where \a yPos is in
+ \e widget coordinates. Returns -1 if \a yPos is outside the valid
+ range.
+
+ \sa findCol(), rowYPos()
+*/
+
+int QtTableView::findRow( int yPos ) const
+{
+ int cellMaxY;
+ int row = findRawRow( yPos, &cellMaxY );
+ if ( testTableFlags(Tbl_cutCellsV) && cellMaxY > maxViewY() )
+ row = - 1; // cell cut by bottom margin
+ if ( row >= nRows )
+ row = -1;
+ return row;
+}
+
+
+/*!
+ Returns the index of the column at position \a xPos, where \a xPos is
+ in \e widget coordinates. Returns -1 if \a xPos is outside the valid
+ range.
+
+ \sa findRow(), colXPos()
+*/
+
+int QtTableView::findCol( int xPos ) const
+{
+ int cellMaxX;
+ int col = findRawCol( xPos, &cellMaxX );
+ if ( testTableFlags(Tbl_cutCellsH) && cellMaxX > maxViewX() )
+ col = - 1; // cell cut by right margin
+ if ( col >= nCols )
+ col = -1;
+ return col;
+}
+
+
+/*!
+ Computes the position in the widget of row \a row.
+
+ Returns TRUE and stores the result in \a *yPos (in \e widget
+ coordinates) if the row is visible. Returns FALSE and does not modify
+ \a *yPos if \a row is invisible or invalid.
+
+ \sa colXPos(), findRow()
+*/
+
+bool QtTableView::rowYPos( int row, int *yPos ) const
+{
+ int y;
+ if ( row >= yCellOffs ) {
+ if ( cellH ) {
+ int lastVisible = lastRowVisible();
+ if ( row > lastVisible || lastVisible == -1 )
+ return FALSE;
+ y = (row - yCellOffs)*cellH + minViewY() - yCellDelta;
+ } else {
+ //##arnt3
+ y = minViewY() - yCellDelta; // y of leftmost cell in view
+ int r = yCellOffs;
+ QtTableView *tw = (QtTableView *)this;
+ int maxY = maxViewY();
+ while ( r < row && y <= maxY )
+ y += tw->cellHeight( r++ );
+ if ( y > maxY )
+ return FALSE;
+
+ }
+ } else {
+ return FALSE;
+ }
+ if ( yPos )
+ *yPos = y;
+ return TRUE;
+}
+
+
+/*!
+ Computes the position in the widget of column \a col.
+
+ Returns TRUE and stores the result in \a *xPos (in \e widget
+ coordinates) if the column is visible. Returns FALSE and does not
+ modify \a *xPos if \a col is invisible or invalid.
+
+ \sa rowYPos(), findCol()
+*/
+
+bool QtTableView::colXPos( int col, int *xPos ) const
+{
+ int x;
+ if ( col >= xCellOffs ) {
+ if ( cellW ) {
+ int lastVisible = lastColVisible();
+ if ( col > lastVisible || lastVisible == -1 )
+ return FALSE;
+ x = (col - xCellOffs)*cellW + minViewX() - xCellDelta;
+ } else {
+ //##arnt3
+ x = minViewX() - xCellDelta; // x of uppermost cell in view
+ int c = xCellOffs;
+ QtTableView *tw = (QtTableView *)this;
+ int maxX = maxViewX();
+ while ( c < col && x <= maxX )
+ x += tw->cellWidth( c++ );
+ if ( x > maxX )
+ return FALSE;
+ }
+ } else {
+ return FALSE;
+ }
+ if ( xPos )
+ *xPos = x;
+ return TRUE;
+}
+
+
+/*!
+ Moves the visible area of the table right by \a xPixels and
+ down by \a yPixels pixels. Both may be negative.
+
+ \warning You might find that QScrollView offers a higher-level of
+ functionality than using QtTableView and this function.
+
+ This function is \e not the same as QWidget::scroll(); in particular,
+ the signs of \a xPixels and \a yPixels have the reverse semantics.
+
+ \sa setXOffset(), setYOffset(), setOffset(), setTopCell(),
+ setLeftCell()
+*/
+
+void QtTableView::scroll( int xPixels, int yPixels )
+{
+ QWidget::scroll( -xPixels, -yPixels, contentsRect() );
+}
+
+
+/*!
+ Returns the leftmost pixel of the table view in \e view
+ coordinates. This excludes the frame and any header.
+
+ \sa maxViewY(), viewWidth(), contentsRect()
+*/
+
+int QtTableView::minViewX() const
+{
+ return frameWidth();
+}
+
+
+/*!
+ Returns the top pixel of the table view in \e view
+ coordinates. This excludes the frame and any header.
+
+ \sa maxViewX(), viewHeight(), contentsRect()
+*/
+
+int QtTableView::minViewY() const
+{
+ return frameWidth();
+}
+
+
+/*!
+ Returns the rightmost pixel of the table view in \e view
+ coordinates. This excludes the frame and any scroll bar, but
+ includes blank pixels to the right of the visible table data.
+
+ \sa maxViewY(), viewWidth(), contentsRect()
+*/
+
+int QtTableView::maxViewX() const
+{
+ return width() - 1 - frameWidth()
+ - (tFlags & Tbl_vScrollBar ? VSBEXT
+ : 0);
+}
+
+
+/*!
+ Returns the bottom pixel of the table view in \e view
+ coordinates. This excludes the frame and any scroll bar, but
+ includes blank pixels below the visible table data.
+
+ \sa maxViewX(), viewHeight(), contentsRect()
+*/
+
+int QtTableView::maxViewY() const
+{
+ return height() - 1 - frameWidth()
+ - (tFlags & Tbl_hScrollBar ? HSBEXT
+ : 0);
+}
+
+
+/*!
+ Returns the width of the table view, as such, in \e view
+ coordinates. This does not include any header, scroll bar or frame,
+ but it does include background pixels to the right of the table data.
+
+ \sa minViewX() maxViewX(), viewHeight(), contentsRect() viewRect()
+*/
+
+int QtTableView::viewWidth() const
+{
+ return maxViewX() - minViewX() + 1;
+}
+
+
+/*!
+ Returns the height of the table view, as such, in \e view
+ coordinates. This does not include any header, scroll bar or frame,
+ but it does include background pixels below the table data.
+
+ \sa minViewY() maxViewY() viewWidth() contentsRect() viewRect()
+*/
+
+int QtTableView::viewHeight() const
+{
+ return maxViewY() - minViewY() + 1;
+}
+
+
+void QtTableView::doAutoScrollBars()
+{
+ int viewW = width() - frameWidth() - minViewX();
+ int viewH = height() - frameWidth() - minViewY();
+ bool vScrollOn = testTableFlags(Tbl_vScrollBar);
+ bool hScrollOn = testTableFlags(Tbl_hScrollBar);
+ int w = 0;
+ int h = 0;
+ int i;
+
+ if ( testTableFlags(Tbl_autoHScrollBar) ) {
+ if ( cellW ) {
+ w = cellW*nCols;
+ } else {
+ i = 0;
+ while ( i < nCols && w <= viewW )
+ w += cellWidth( i++ );
+ }
+ if ( w > viewW )
+ hScrollOn = TRUE;
+ else
+ hScrollOn = FALSE;
+ }
+
+ if ( testTableFlags(Tbl_autoVScrollBar) ) {
+ if ( cellH ) {
+ h = cellH*nRows;
+ } else {
+ i = 0;
+ while ( i < nRows && h <= viewH )
+ h += cellHeight( i++ );
+ }
+
+ if ( h > viewH )
+ vScrollOn = TRUE;
+ else
+ vScrollOn = FALSE;
+ }
+
+ if ( testTableFlags(Tbl_autoHScrollBar) && vScrollOn && !hScrollOn )
+ if ( w > viewW - VSBEXT )
+ hScrollOn = TRUE;
+
+ if ( testTableFlags(Tbl_autoVScrollBar) && hScrollOn && !vScrollOn )
+ if ( h > viewH - HSBEXT )
+ vScrollOn = TRUE;
+
+ setHorScrollBar( hScrollOn, FALSE );
+ setVerScrollBar( vScrollOn, FALSE );
+ updateFrameSize();
+}
+
+
+/*!
+ \fn void QtTableView::updateScrollBars()
+
+ Updates the scroll bars' contents and presence to match the table's
+ state. Generally, you should not need to call this.
+
+ \sa setTableFlags()
+*/
+
+/*!
+ Updates the scroll bars' contents and presence to match the table's
+ state \c or \a f.
+
+ \sa setTableFlags()
+*/
+
+void QtTableView::updateScrollBars( uint f )
+{
+ sbDirty = sbDirty | f;
+ if ( inSbUpdate )
+ return;
+ inSbUpdate = TRUE;
+
+ if ( testTableFlags(Tbl_autoHScrollBar) && (sbDirty & horRange) ||
+ testTableFlags(Tbl_autoVScrollBar) && (sbDirty & verRange) )
+ // if range change and auto
+ doAutoScrollBars(); // turn scroll bars on/off if needed
+
+ if ( !autoUpdate() ) {
+ inSbUpdate = FALSE;
+ return;
+ }
+ if ( yOffset() > 0 && testTableFlags( Tbl_autoVScrollBar ) &&
+ !testTableFlags( Tbl_vScrollBar ) ) {
+ setYOffset( 0 );
+ }
+ if ( xOffset() > 0 && testTableFlags( Tbl_autoHScrollBar ) &&
+ !testTableFlags( Tbl_hScrollBar ) ) {
+ setXOffset( 0 );
+ }
+ if ( !isVisible() ) {
+ inSbUpdate = FALSE;
+ return;
+ }
+
+ if ( testTableFlags(Tbl_hScrollBar) && (sbDirty & horMask) != 0 ) {
+ if ( sbDirty & horGeometry )
+ hScrollBar->setGeometry( 0,height() - HSBEXT,
+ viewWidth() + frameWidth()*2,
+ HSBEXT);
+
+ if ( sbDirty & horSteps ) {
+ if ( cellW )
+ hScrollBar->setSteps( QMIN(cellW,viewWidth()/2), viewWidth() );
+ else
+ hScrollBar->setSteps( 16, viewWidth() );
+ }
+
+ if ( sbDirty & horRange )
+ hScrollBar->setRange( 0, maxXOffset() );
+
+ if ( sbDirty & horValue )
+ hScrollBar->setValue( xOffs );
+
+ // show scrollbar only when it has a sane geometry
+ if ( !hScrollBar->isVisible() )
+ hScrollBar->show();
+ }
+
+ if ( testTableFlags(Tbl_vScrollBar) && (sbDirty & verMask) != 0 ) {
+ if ( sbDirty & verGeometry )
+ vScrollBar->setGeometry( width() - VSBEXT, 0,
+ VSBEXT,
+ viewHeight() + frameWidth()*2 );
+
+ if ( sbDirty & verSteps ) {
+ if ( cellH )
+ vScrollBar->setSteps( QMIN(cellH,viewHeight()/2), viewHeight() );
+ else
+ vScrollBar->setSteps( 16, viewHeight() ); // fttb! ###
+ }
+
+ if ( sbDirty & verRange )
+ vScrollBar->setRange( 0, maxYOffset() );
+
+ if ( sbDirty & verValue )
+ vScrollBar->setValue( yOffs );
+
+ // show scrollbar only when it has a sane geometry
+ if ( !vScrollBar->isVisible() )
+ vScrollBar->show();
+ }
+ if ( coveringCornerSquare &&
+ ( (sbDirty & verGeometry ) || (sbDirty & horGeometry)) )
+ cornerSquare->move( maxViewX() + frameWidth() + 1,
+ maxViewY() + frameWidth() + 1 );
+
+ sbDirty = 0;
+ inSbUpdate = FALSE;
+}
+
+
+void QtTableView::updateFrameSize()
+{
+ int rw = width() - ( testTableFlags(Tbl_vScrollBar) ?
+ VSBEXT : 0 );
+ int rh = height() - ( testTableFlags(Tbl_hScrollBar) ?
+ HSBEXT : 0 );
+ if ( rw < 0 )
+ rw = 0;
+ if ( rh < 0 )
+ rh = 0;
+
+ if ( autoUpdate() ) {
+ int fh = frameRect().height();
+ int fw = frameRect().width();
+ setFrameRect( QRect(0,0,rw,rh) );
+
+ if ( rw != fw )
+ update( QMIN(fw,rw) - frameWidth() - 2, 0, frameWidth()+4, rh );
+ if ( rh != fh )
+ update( 0, QMIN(fh,rh) - frameWidth() - 2, rw, frameWidth()+4 );
+ }
+}
+
+
+/*!
+ Returns the maximum horizontal offset within the table of the
+ view's left edge in \e table coordinates.
+
+ This is used mainly to set the horizontal scroll bar's range.
+
+ \sa maxColOffset(), maxYOffset(), totalWidth()
+*/
+
+int QtTableView::maxXOffset()
+{
+ int tw = totalWidth();
+ int maxOffs;
+ if ( testTableFlags(Tbl_scrollLastHCell) ) {
+ if ( nCols != 1)
+ maxOffs = tw - ( cellW ? cellW : cellWidth( nCols - 1 ) );
+ else
+ maxOffs = tw - viewWidth();
+ } else {
+ if ( testTableFlags(Tbl_snapToHGrid) ) {
+ if ( cellW ) {
+ maxOffs = tw - (viewWidth()/cellW)*cellW;
+ } else {
+ int goal = tw - viewWidth();
+ int pos = tw;
+ int nextCol = nCols - 1;
+ int nextCellWidth = cellWidth( nextCol );
+ while( nextCol > 0 && pos > goal + nextCellWidth ) {
+ pos -= nextCellWidth;
+ nextCellWidth = cellWidth( --nextCol );
+ }
+ if ( goal + nextCellWidth == pos )
+ maxOffs = goal;
+ else if ( goal < pos )
+ maxOffs = pos;
+ else
+ maxOffs = 0;
+ }
+ } else {
+ maxOffs = tw - viewWidth();
+ }
+ }
+ return maxOffs > 0 ? maxOffs : 0;
+}
+
+
+/*!
+ Returns the maximum vertical offset within the table of the
+ view's top edge in \e table coordinates.
+
+ This is used mainly to set the vertical scroll bar's range.
+
+ \sa maxRowOffset(), maxXOffset(), totalHeight()
+*/
+
+int QtTableView::maxYOffset()
+{
+ int th = totalHeight();
+ int maxOffs;
+ if ( testTableFlags(Tbl_scrollLastVCell) ) {
+ if ( nRows != 1)
+ maxOffs = th - ( cellH ? cellH : cellHeight( nRows - 1 ) );
+ else
+ maxOffs = th - viewHeight();
+ } else {
+ if ( testTableFlags(Tbl_snapToVGrid) ) {
+ if ( cellH ) {
+ maxOffs = th - (viewHeight()/cellH)*cellH;
+ } else {
+ int goal = th - viewHeight();
+ int pos = th;
+ int nextRow = nRows - 1;
+ int nextCellHeight = cellHeight( nextRow );
+ while( nextRow > 0 && pos > goal + nextCellHeight ) {
+ pos -= nextCellHeight;
+ nextCellHeight = cellHeight( --nextRow );
+ }
+ if ( goal + nextCellHeight == pos )
+ maxOffs = goal;
+ else if ( goal < pos )
+ maxOffs = pos;
+ else
+ maxOffs = 0;
+ }
+ } else {
+ maxOffs = th - viewHeight();
+ }
+ }
+ return maxOffs > 0 ? maxOffs : 0;
+}
+
+
+/*!
+ Returns the index of the last column, which may be at the left edge
+ of the view.
+
+ Depending on the \link setTableFlags() Tbl_scrollLastHCell\endlink flag,
+ this may or may not be the last column.
+
+ \sa maxXOffset(), maxRowOffset()
+*/
+
+int QtTableView::maxColOffset()
+{
+ int mx = maxXOffset();
+ if ( cellW )
+ return mx/cellW;
+ else {
+ int xcd=0, col=0;
+ while ( col < nCols && mx > (xcd=cellWidth(col)) ) {
+ mx -= xcd;
+ col++;
+ }
+ return col;
+ }
+}
+
+
+/*!
+ Returns the index of the last row, which may be at the top edge of
+ the view.
+
+ Depending on the \link setTableFlags() Tbl_scrollLastVCell\endlink flag,
+ this may or may not be the last row.
+
+ \sa maxYOffset(), maxColOffset()
+*/
+
+int QtTableView::maxRowOffset()
+{
+ int my = maxYOffset();
+ if ( cellH )
+ return my/cellH;
+ else {
+ int ycd=0, row=0;
+ while ( row < nRows && my > (ycd=cellHeight(row)) ) {
+ my -= ycd;
+ row++;
+ }
+ return row;
+ }
+}
+
+
+void QtTableView::showOrHideScrollBars()
+{
+ if ( !autoUpdate() )
+ return;
+ if ( vScrollBar ) {
+ if ( testTableFlags(Tbl_vScrollBar) ) {
+ if ( !vScrollBar->isVisible() )
+ sbDirty = sbDirty | verMask;
+ } else {
+ if ( vScrollBar->isVisible() )
+ vScrollBar->hide();
+ }
+ }
+ if ( hScrollBar ) {
+ if ( testTableFlags(Tbl_hScrollBar) ) {
+ if ( !hScrollBar->isVisible() )
+ sbDirty = sbDirty | horMask;
+ } else {
+ if ( hScrollBar->isVisible() )
+ hScrollBar->hide();
+ }
+ }
+ if ( cornerSquare ) {
+ if ( testTableFlags(Tbl_hScrollBar) &&
+ testTableFlags(Tbl_vScrollBar) ) {
+ if ( !cornerSquare->isVisible() )
+ cornerSquare->show();
+ } else {
+ if ( cornerSquare->isVisible() )
+ cornerSquare->hide();
+ }
+ }
+}
+
+
+/*!
+ Updates the scroll bars and internal state.
+
+ Call this function when the table view's total size is changed;
+ typically because the result of cellHeight() or cellWidth() have changed.
+
+ This function does not repaint the widget.
+*/
+
+void QtTableView::updateTableSize()
+{
+ bool updateOn = autoUpdate();
+ setAutoUpdate( FALSE );
+ int xofs = xOffset();
+ xOffs++; //so that setOffset will not return immediately
+ setOffset(xofs,yOffset(),FALSE); //to calculate internal state correctly
+ setAutoUpdate(updateOn);
+
+ updateScrollBars( horSteps | horRange |
+ verSteps | verRange );
+ showOrHideScrollBars();
+}
+
+
+#endif
diff --git a/arts/builder/qttableview.h b/arts/builder/qttableview.h
new file mode 100644
index 00000000..c5a540dd
--- /dev/null
+++ b/arts/builder/qttableview.h
@@ -0,0 +1,251 @@
+/**********************************************************************
+** $Id$
+**
+** Definition of QtTableView class
+**
+** Created : 941115
+**
+** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
+**
+** This file contains a class moved out of the Qt GUI Toolkit API. It
+** may be used, distributed and modified without limitation.
+**
+**********************************************************************/
+
+#ifndef QTTABLEVIEW_H
+#define QTTABLEVIEW_H
+
+#ifndef QT_H
+#include "qframe.h"
+#endif // QT_H
+
+#ifndef QT_NO_QTTABLEVIEW
+
+class QScrollBar;
+class QCornerSquare;
+
+
+class QtTableView : public QFrame
+{
+ Q_OBJECT
+public:
+ virtual void setBackgroundColor( const QColor & );
+ virtual void setPalette( const QPalette & );
+ void show();
+
+ void repaint( bool erase=TRUE );
+ void repaint( int x, int y, int w, int h, bool erase=TRUE );
+ void repaint( const QRect &, bool erase=TRUE );
+
+protected:
+ QtTableView( QWidget *parent=0, const char *name=0, WFlags f=0 );
+ ~QtTableView();
+
+ int numRows() const;
+ virtual void setNumRows( int );
+ int numCols() const;
+ virtual void setNumCols( int );
+
+ int topCell() const;
+ virtual void setTopCell( int row );
+ int leftCell() const;
+ virtual void setLeftCell( int col );
+ virtual void setTopLeftCell( int row, int col );
+
+ int xOffset() const;
+ virtual void setXOffset( int );
+ int yOffset() const;
+ virtual void setYOffset( int );
+ virtual void setOffset( int x, int y, bool updateScrBars = TRUE );
+
+ virtual int cellWidth( int col );
+ virtual int cellHeight( int row );
+ int cellWidth() const;
+ int cellHeight() const;
+ virtual void setCellWidth( int );
+ virtual void setCellHeight( int );
+
+ virtual int totalWidth();
+ virtual int totalHeight();
+
+ uint tableFlags() const;
+ bool testTableFlags( uint f ) const;
+ virtual void setTableFlags( uint f );
+ void clearTableFlags( uint f = ~0 );
+
+ bool autoUpdate() const;
+ virtual void setAutoUpdate( bool );
+
+ void updateCell( int row, int column, bool erase=TRUE );
+
+ QRect cellUpdateRect() const;
+ QRect viewRect() const;
+
+ int lastRowVisible() const;
+ int lastColVisible() const;
+
+ bool rowIsVisible( int row ) const;
+ bool colIsVisible( int col ) const;
+
+ QScrollBar *verticalScrollBar() const;
+ QScrollBar *horizontalScrollBar() const;
+
+private slots:
+ void horSbValue( int );
+ void horSbSliding( int );
+ void horSbSlidingDone();
+ void verSbValue( int );
+ void verSbSliding( int );
+ void verSbSlidingDone();
+
+protected:
+ virtual void paintCell( QPainter *, int row, int col ) = 0;
+ virtual void setupPainter( QPainter * );
+
+ void paintEvent( QPaintEvent * );
+ void resizeEvent( QResizeEvent * );
+
+ int findRow( int yPos ) const;
+ int findCol( int xPos ) const;
+
+ bool rowYPos( int row, int *yPos ) const;
+ bool colXPos( int col, int *xPos ) const;
+
+ int maxXOffset();
+ int maxYOffset();
+ int maxColOffset();
+ int maxRowOffset();
+
+ int minViewX() const;
+ int minViewY() const;
+ int maxViewX() const;
+ int maxViewY() const;
+ int viewWidth() const;
+ int viewHeight() const;
+
+ void scroll( int xPixels, int yPixels );
+ void updateScrollBars();
+ void updateTableSize();
+
+private:
+ void coverCornerSquare( bool );
+ void snapToGrid( bool horizontal, bool vertical );
+ virtual void setHorScrollBar( bool on, bool update = TRUE );
+ virtual void setVerScrollBar( bool on, bool update = TRUE );
+ void updateView();
+ int findRawRow( int yPos, int *cellMaxY, int *cellMinY = 0,
+ bool goOutsideView = FALSE ) const;
+ int findRawCol( int xPos, int *cellMaxX, int *cellMinX = 0,
+ bool goOutsideView = FALSE ) const;
+ int maxColsVisible() const;
+
+ void updateScrollBars( uint );
+ void updateFrameSize();
+
+ void doAutoScrollBars();
+ void showOrHideScrollBars();
+
+ int nRows;
+ int nCols;
+ int xOffs, yOffs;
+ int xCellOffs, yCellOffs;
+ short xCellDelta, yCellDelta;
+ short cellH, cellW;
+
+ uint eraseInPaint : 1;
+ uint verSliding : 1;
+ uint verSnappingOff : 1;
+ uint horSliding : 1;
+ uint horSnappingOff : 1;
+ uint coveringCornerSquare : 1;
+ uint sbDirty : 8;
+ uint inSbUpdate : 1;
+
+ uint tFlags;
+ QRect cellUpdateR;
+
+ QScrollBar *vScrollBar;
+ QScrollBar *hScrollBar;
+ QCornerSquare *cornerSquare;
+
+private: // Disabled copy constructor and operator=
+#if defined(Q_DISABLE_COPY)
+ QtTableView( const QtTableView & );
+ QtTableView &operator=( const QtTableView & );
+#endif
+};
+
+
+const uint Tbl_vScrollBar = 0x00000001;
+const uint Tbl_hScrollBar = 0x00000002;
+const uint Tbl_autoVScrollBar = 0x00000004;
+const uint Tbl_autoHScrollBar = 0x00000008;
+const uint Tbl_autoScrollBars = 0x0000000C;
+
+const uint Tbl_clipCellPainting = 0x00000100;
+const uint Tbl_cutCellsV = 0x00000200;
+const uint Tbl_cutCellsH = 0x00000400;
+const uint Tbl_cutCells = 0x00000600;
+
+const uint Tbl_scrollLastHCell = 0x00000800;
+const uint Tbl_scrollLastVCell = 0x00001000;
+const uint Tbl_scrollLastCell = 0x00001800;
+
+const uint Tbl_smoothHScrolling = 0x00002000;
+const uint Tbl_smoothVScrolling = 0x00004000;
+const uint Tbl_smoothScrolling = 0x00006000;
+
+const uint Tbl_snapToHGrid = 0x00008000;
+const uint Tbl_snapToVGrid = 0x00010000;
+const uint Tbl_snapToGrid = 0x00018000;
+
+
+inline int QtTableView::numRows() const
+{ return nRows; }
+
+inline int QtTableView::numCols() const
+{ return nCols; }
+
+inline int QtTableView::topCell() const
+{ return yCellOffs; }
+
+inline int QtTableView::leftCell() const
+{ return xCellOffs; }
+
+inline int QtTableView::xOffset() const
+{ return xOffs; }
+
+inline int QtTableView::yOffset() const
+{ return yOffs; }
+
+inline int QtTableView::cellHeight() const
+{ return cellH; }
+
+inline int QtTableView::cellWidth() const
+{ return cellW; }
+
+inline uint QtTableView::tableFlags() const
+{ return tFlags; }
+
+inline bool QtTableView::testTableFlags( uint f ) const
+{ return (tFlags & f) != 0; }
+
+inline QRect QtTableView::cellUpdateRect() const
+{ return cellUpdateR; }
+
+inline bool QtTableView::autoUpdate() const
+{ return isUpdatesEnabled(); }
+
+inline void QtTableView::repaint( bool erase )
+{ repaint( 0, 0, width(), height(), erase ); }
+
+inline void QtTableView::repaint( const QRect &r, bool erase )
+{ repaint( r.x(), r.y(), r.width(), r.height(), erase ); }
+
+inline void QtTableView::updateScrollBars()
+{ updateScrollBars( 0 ); }
+
+
+#endif // QT_NO_QTTABLEVIEW
+
+#endif // QTTABLEVIEW_H
diff --git a/arts/builder/retrievedlg.cpp b/arts/builder/retrievedlg.cpp
new file mode 100644
index 00000000..59a8079f
--- /dev/null
+++ b/arts/builder/retrievedlg.cpp
@@ -0,0 +1,137 @@
+ /*
+
+ Copyright (C) 1998 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "retrievedlg.h"
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qlistbox.h>
+#include <kbuttonbox.h>
+#include <qbuttongroup.h>
+#include <qradiobutton.h>
+#include <qlineedit.h>
+#include <kapplication.h>
+#include <kseparator.h>
+#include <klocale.h>
+#include <kstdguiitem.h>
+#include <stdio.h>
+#include <set>
+#include <arts/debug.h>
+#include <qpushbutton.h>
+
+static void min_size(QWidget *w) {
+ w->setMinimumSize(w->sizeHint());
+}
+
+RetrieveDlg::RetrieveDlg(QWidget *parent) :QDialog(parent,"X", TRUE)
+{
+ setCaption(i18n("Retrieve Structure From Server"));
+
+ QVBoxLayout *mainlayout = new QVBoxLayout(this);
+
+// caption label: "Synthesis running..."
+
+ mainlayout->addSpacing(5);
+ QLabel *captionlabel = new QLabel(this);
+ QFont labelfont(captionlabel->font());
+ labelfont.setPointSize(labelfont.pointSize()*3/2);
+ captionlabel->setFont(labelfont);
+ captionlabel->setText(QString(" ")+i18n("Published structures")+QString(" "));
+ captionlabel->setAlignment(AlignCenter);
+ min_size(captionlabel);
+ mainlayout->addWidget(captionlabel);
+
+// hruler
+
+ mainlayout->addSpacing(5);
+ KSeparator *ruler2 = new KSeparator( KSeparator::HLine, this);
+ mainlayout->addWidget(ruler2);
+ mainlayout->addSpacing(5);
+
+// listwidget
+
+ listbox = new QListBox(this);
+ listbox->setMinimumSize(300,200);
+
+ arts_debug("TODO:PORT:get available structures");
+#if 0
+ // sort the result:
+ vector<StructureDesc> *structures = Synthesizer->publishedStructures();
+ set<string> names;
+
+ unsigned long i;
+ for(i=0;i<structures->length();i++)
+ names.insert(structures[i]->Name());
+
+ set<string>::iterator ni;
+ for(ni=names.begin();ni!=names.end();++ni)
+ listbox->insertItem((*ni).c_str());
+#endif
+
+ mainlayout->addWidget(listbox);
+
+// hruler
+
+ mainlayout->addSpacing(5);
+ KSeparator *ruler = new KSeparator( KSeparator::HLine, this);
+ mainlayout->addWidget(ruler);
+ mainlayout->addSpacing(5);
+
+// buttons
+
+ QHBoxLayout *buttonlayout = new QHBoxLayout;
+ mainlayout->addSpacing(5);
+ mainlayout->addLayout(buttonlayout);
+ mainlayout->addSpacing(5);
+
+ buttonlayout->addSpacing(5);
+ KButtonBox *bbox = new KButtonBox(this);
+
+ bbox->addButton(KStdGuiItem::help(), this, SLOT( help() ));
+ bbox->addStretch(1);
+
+ QButton *cancelbutton = bbox->addButton(KStdGuiItem::cancel());
+ connect( cancelbutton, SIGNAL( clicked() ), SLOT(reject() ) );
+
+ QButton *okbutton = bbox->addButton(KStdGuiItem::ok());
+ connect( okbutton, SIGNAL( clicked() ), SLOT(accept() ) );
+
+ bbox->layout();
+
+ buttonlayout->addWidget(bbox);
+ buttonlayout->addSpacing(5);
+
+ mainlayout->freeze();
+}
+
+QString RetrieveDlg::result()
+{
+ if(listbox->currentItem() != -1)
+ {
+ return(listbox->text(listbox->currentItem()));
+ }
+ return QString::null;
+}
+
+void RetrieveDlg::help()
+{
+ KApplication::kApplication()->invokeHelp("", "karts");
+}
+#include "retrievedlg.moc"
diff --git a/arts/builder/retrievedlg.h b/arts/builder/retrievedlg.h
new file mode 100644
index 00000000..e37d3120
--- /dev/null
+++ b/arts/builder/retrievedlg.h
@@ -0,0 +1,45 @@
+ /*
+
+ Copyright (C) 1998 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef __RETRIEVEDLG_H_
+#define __RETRIEVEDLG_H_
+
+#include "structure.h"
+
+#include <qdialog.h>
+#include <qtimer.h>
+#include <qlabel.h>
+#include <qscrollbar.h>
+#include <qlineedit.h>
+#include <qlayout.h>
+#include <qlistbox.h>
+#include <string>
+
+class RetrieveDlg :public QDialog {
+ Q_OBJECT
+ QListBox *listbox;
+public:
+ RetrieveDlg(QWidget *parent);
+ QString result();
+public slots:
+ void help();
+};
+#endif
diff --git a/arts/builder/scomponent.cpp b/arts/builder/scomponent.cpp
new file mode 100644
index 00000000..e894abaa
--- /dev/null
+++ b/arts/builder/scomponent.cpp
@@ -0,0 +1,108 @@
+ /*
+
+ Copyright (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "scomponent.h"
+//#include <arts/debug.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <stdio.h>
+
+StructureComponent::StructureComponent(StructureCanvas *canvas)
+: _x(0), _y(0)
+{
+ this->canvas = canvas;
+}
+
+StructureComponent::~StructureComponent()
+{
+}
+
+void StructureComponent::hide()
+{
+ if(_visible)
+ {
+ _visible = false;
+ redraw();
+ }
+}
+
+void StructureComponent::show()
+{
+ if(!_visible)
+ {
+ _visible = true;
+ redraw();
+ }
+}
+
+bool StructureComponent::visible()
+{
+ return(_visible);
+}
+
+void StructureComponent::redraw()
+{
+ canvas->redrawRect(x(), y(), width(), height());
+}
+
+bool StructureComponent::move(int newx, int newy)
+{
+ bool success = moveInternal(newx, newy);
+
+ if(success)
+ {
+ hide();
+ redraw();
+
+ _x = newx;
+ _y = newy;
+
+ show();
+ redraw();
+ }
+
+ return success;
+}
+
+int StructureComponent::x() const
+{
+ return _x;
+}
+
+int StructureComponent::y() const
+{
+ return _y;
+}
+
+bool StructureComponent::selected()
+{
+ return _selected;
+}
+
+void StructureComponent::setSelected(bool newselection)
+{
+ if(newselection != _selected)
+ {
+ _selected = newselection;
+ redraw();
+ }
+}
+
diff --git a/arts/builder/scomponent.h b/arts/builder/scomponent.h
new file mode 100644
index 00000000..c5bdcbad
--- /dev/null
+++ b/arts/builder/scomponent.h
@@ -0,0 +1,87 @@
+ /*
+
+ Copyright (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef __SCOMPONENT_H__
+#define __SCOMPONENT_H__
+
+#include <qstring.h>
+#include <list>
+
+class ModulePort;
+class QPainter;
+class QPixmap;
+
+class StructureCanvas {
+public:
+ virtual void redrawRect(int x, int y, int width, int height) = 0;
+};
+
+class StructureComponent {
+protected:
+ StructureCanvas *canvas;
+
+ int _x, _y;
+ bool _selected, _visible;
+
+ virtual bool moveInternal(int x, int y) = 0;
+
+public:
+ StructureComponent(StructureCanvas *canvas);
+ virtual ~StructureComponent();
+
+ // type
+
+ enum ComponentType { ctModule, ctPort };
+ virtual ComponentType type() = 0;
+
+ // TODO: connection & autorouter
+
+ virtual ModulePort *portAt(int segment, int x, int y) = 0;
+ virtual void dumpPorts(std::list<ModulePort *>& ports) = 0;
+
+ // visibility
+ bool visible();
+ void show();
+ void hide();
+ void redraw();
+
+ // drawing
+ virtual bool drawNeedsBackground(int segment) = 0;
+ virtual void drawSegment(QPainter *dest, int cellsize, int segment) = 0;
+ virtual QPixmap *pixmap() = 0;
+ virtual QString name() = 0;
+
+ // space checking & positioning (dimensions in cells)
+ bool move(int x, int y);
+ int x() const;
+ int y() const;
+ virtual int width() const = 0;
+ virtual int height() const = 0;
+
+ // selection
+ bool selected();
+ void setSelected(bool newselection);
+
+ // creation and destruction are handled via standard
+ // constructors/destructors
+};
+
+#endif
diff --git a/arts/builder/session.cpp b/arts/builder/session.cpp
new file mode 100644
index 00000000..46efd7e1
--- /dev/null
+++ b/arts/builder/session.cpp
@@ -0,0 +1,80 @@
+ /*
+
+ Copyright (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "session.h"
+#include <stdio.h>
+#include <arts/debug.h>
+
+Session::Session()
+{
+ session = 0;
+}
+
+Session::~Session()
+{
+ if(session) delete session;
+ session = 0;
+}
+
+bool Session::startExecute()
+{
+ arts_debug("TODO:PORT: Session::startExecute()");
+#if 0
+ assert(session);
+ assert(!execID);
+
+ // just in case synthesis has been halted before,
+ // restart it and hope we'll have enough computing power now
+ Synthesizer->Reset();
+ execID = Synthesizer->restoreSession(*session,preferredservers);
+#endif
+ return true;
+}
+
+bool Session::loadSession(const char *filename)
+{
+#if 0
+ if(session) delete session;
+ session = 0;
+
+ FILE *infile = fopen(filename,"r");
+ if(!infile) return false;
+
+ session = new ArtsCorba::StringSeq;
+
+ char line[1024];
+ unsigned long i = 0;
+
+ while(fgets(line,1024,infile))
+ {
+ // cut eventual CR and/or LFs at the end of the line
+ while(strlen(line) && line[strlen(line)-1] < 14)
+ line[strlen(line)-1] = 0;
+
+ session->length(i+1);
+ (*session)[i++] = CORBA::string_dup(line);
+ }
+ fclose(infile);
+ return true;
+#endif
+ arts_debug("TODO:PORT:loadSession(%s)",filename);
+ return false;
+}
diff --git a/arts/builder/session.h b/arts/builder/session.h
new file mode 100644
index 00000000..bfa0c3d8
--- /dev/null
+++ b/arts/builder/session.h
@@ -0,0 +1,38 @@
+ /*
+
+ Copyright (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef __SESSION_H__
+#define __SESSION_H__
+
+#include "structure.h"
+
+class Session :public ExecutableStructure {
+ std::vector<std::string> *session;
+public:
+ Session();
+ ~Session();
+
+ bool startExecute();
+
+ bool loadSession(const char *filename);
+};
+
+#endif
diff --git a/arts/builder/structure.cpp b/arts/builder/structure.cpp
new file mode 100644
index 00000000..37950119
--- /dev/null
+++ b/arts/builder/structure.cpp
@@ -0,0 +1,462 @@
+ /*
+
+ Copyright (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "structure.h"
+#include "soundserver.h"
+#include "kartsserver.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <iostream>
+#include <fstream>
+#include <kdebug.h>
+#include <arts/debug.h>
+
+using namespace std;
+
+ExecutableStructure::ExecutableStructure()
+{
+}
+
+ExecutableStructure::~ExecutableStructure()
+{
+ // to make destructor virtual
+ // stop execution here?
+}
+
+void ExecutableStructure::stopExecute()
+{
+ arts_debug("TODO: PORT: freeStructure");
+}
+
+bool ExecutableStructure::isExecuting()
+{
+ arts_debug("TODO:PORT:isExecuting()");
+ return false;
+}
+
+bool ExecutableStructure::saveSession(const char *filename)
+{
+ arts_debug("TODO:PORT:saveSession()");
+#if 0
+ assert(execID);
+
+ ArtsCorba::StringSeq_var data;
+ arts_debug("saveSession");
+ if(Synthesizer->saveSession(execID,true,data))
+ {
+ arts_debug("ok");
+ FILE *file = fopen(filename,"w");
+ if(!file) return false;
+
+ unsigned long i;
+ for(i=0;i<data->length();i++) fprintf(file,"%s\n",(char *)(*data)[i]);
+ fclose(file);
+
+ return true;
+ }
+ arts_debug("failed");
+#endif
+ return false;
+}
+
+Structure::Structure() :ExecutableStructure()
+{
+ canvas = 0;
+}
+
+void Structure::setCanvas(StructureCanvas *canvas)
+{
+ this->canvas = canvas;
+}
+
+Structure::~Structure()
+{
+ clear();
+ arts_debug("~Structure (releasing structuredesc from structure)");
+}
+
+bool Structure::startExecute()
+{
+#if 0
+ assert(!execID);
+
+ arts_debug("PORT: TODO startExecute()");
+ // just in case synthesis has been halted before,
+ // restart it and hope we'll have enough computing power now
+ //Synthesizer->Reset();
+ //execID = Synthesizer->createStructure(StructureDesc,preferredservers);
+ assert(execID);
+#endif
+
+ /* connect to the sound server */
+ Arts::SimpleSoundServer server = KArtsServer().server();
+
+ if(server.isNull())
+ return false;
+
+ /* move a copy of the structure to the server, so that there will be
+ no latencies in querying what to connect to what */
+ vector<string> *savePtr = StructureDesc.saveToList();
+ Arts::StructureDesc remoteSD;
+ remoteSD = Arts::DynamicCast(server.createObject("Arts::StructureDesc"));
+ assert(!remoteSD.isNull());
+ remoteSD.loadFromList(*savePtr);
+ delete savePtr;
+
+ /* create a structure builder on the server */
+ Arts::StructureBuilder builder;
+ builder = Arts::DynamicCast(server.createObject("Arts::StructureBuilder"));
+
+ /* create a local factory - this will enable the builder to create gui qt
+ widgets (which need to reside within an qt application to work) */
+
+ Arts::LocalFactory factory;
+ builder.addFactory(factory);
+
+ /* create the structure on the server */
+ structure = Arts::DynamicCast(builder.createObject(remoteSD));
+
+ if (structure.isNull())
+ return false;
+
+ structure.start();
+
+ return true;
+}
+
+void Structure::stopExecute()
+{
+
+ // TODO:PORT: verify this code
+ structure = Arts::SynthModule::null();
+}
+
+void Structure::publish()
+{
+ arts_debug("PORT: TODO publish()");
+ //Synthesizer->publishStructureDesc(StructureDesc);
+}
+
+bool Structure::valid()
+{
+ return StructureDesc.valid();
+}
+
+string Structure::name()
+{
+ return StructureDesc.name();
+}
+
+void Structure::rename(const char *newname)
+{
+ StructureDesc.name(newname);
+}
+
+void Structure::addInheritedInterface(const char *iface)
+{
+ StructureDesc.addInheritedInterface(iface);
+}
+
+vector<string> *Structure::inheritedInterfaces()
+{
+ return StructureDesc.inheritedInterfaces();
+}
+
+void Structure::removeInheritedInterface(const char *iface)
+{
+ StructureDesc.removeInheritedInterface(iface);
+}
+
+void Structure::saveInto(FILE *file)
+{
+ vector<string> *list = StructureDesc.saveToList();
+
+ vector<string>::iterator i;
+
+ for(i = list->begin(); i != list->end(); i++)
+ fprintf(file, "%s\n", (*i).c_str());
+
+ delete list;
+}
+
+bool Structure::save(const char *filename)
+{
+ FILE *file = fopen(filename,"w");
+ if (!file)
+ return false;
+
+ saveInto(file);
+
+ fclose(file);
+
+ return true;
+}
+
+void Structure::clear()
+{
+ list<StructureComponent *>::iterator ci;
+
+ arts_debug("clc");
+/*
+ for(ci = ComponentList.begin(); ci != ComponentList.end(); ++ci)
+ delete (*ci);
+
+ ComponentList.erase(ComponentList.begin(), ComponentList.end());
+ ModuleList.erase(ModuleList.begin(), ModuleList.end());
+*/
+ for(ci = ComponentList.begin(); ci != ComponentList.end(); ++ci)
+ (*ci)->setSelected(true);
+ deleteSelected();
+
+ arts_debug("sdc");
+ // shouldn't do much, now that we deleted every single component of
+ // the structure, but we to it anyway, just to be sure.
+ StructureDesc.clear();
+}
+
+void Structure::retrieve(const char *pubname)
+{
+ arts_debug("PORT: TODO: retrieve");
+#if 0
+ arts_debug("retrieve... %s",pubname);
+ ArtsCorba::StructureDesc_var psd = Synthesizer->lookupStructureDesc(pubname);
+
+ arts_debug("psdlookup ok");
+ if(psd)
+ {
+ arts_debug("starting savetolist");
+ ArtsCorba::StringSeq_var strseq=psd->saveToList();
+ arts_debug("savetolist ok");
+ loadFromList(strseq);
+ arts_debug("loadfromlist ok");
+ }
+ arts_debug("retrieve... ok");
+#endif
+}
+
+void Structure::load(const char *filename)
+{
+ ifstream infile(filename);
+ string line;
+ vector<string> strseq;
+
+ while(getline(infile,line))
+ strseq.push_back(line);
+
+ loadFromList(strseq);
+#if 0
+ FILE *infile = fopen(filename,"r");
+ ArtsCorba::StringSeq_var strseq = new ArtsCorba::StringSeq;
+
+ char line[1024];
+ unsigned long i = 0;
+
+ while(fgets(line,1024,infile))
+ {
+ // cut eventual CR and/or LFs at the end of the line
+ while(strlen(line) && line[strlen(line)-1] < 14)
+ line[strlen(line)-1] = 0;
+
+ strseq->length(i+1);
+ (*strseq)[i++] = CORBA::string_dup(line);
+ }
+ fclose(infile);
+
+ arts_debug(">>loadfromlist...");
+ loadFromList(strseq);
+ arts_debug("<<loadfromlist...");
+#endif
+}
+
+void Structure::loadFromList(const vector<string>& strseq)
+{
+ assert(canvas);
+
+ arts_debug(">>clear");
+ clear();
+ arts_debug("<<clear");
+
+ StructureDesc.loadFromList(strseq);
+
+ vector<Arts::ModuleDesc> *modules = StructureDesc.modules();
+ vector<Arts::ModuleDesc>::iterator mi;
+
+ for(mi=modules->begin(); mi != modules->end(); ++mi)
+ {
+ Module *m = new Module(*mi,StructureDesc,canvas);
+
+ m->show();
+ ModuleList.push_back(m);
+ ComponentList.push_back(m);
+ }
+ delete modules;
+
+ vector<Arts::StructurePortDesc> *ports = StructureDesc.ports();
+ vector<Arts::StructurePortDesc>::iterator pi;
+
+ for(pi=ports->begin(); pi != ports->end(); ++pi)
+ {
+ StructurePort *p = new StructurePort(*pi,StructureDesc,canvas);
+
+ p->show();
+ ComponentList.push_back(p);
+ }
+ delete ports;
+}
+
+Module *Structure::createModule(const Arts::ModuleInfo& minfo)
+{
+ assert(canvas);
+ Module *m = new Module(minfo,StructureDesc,canvas);
+
+ ComponentList.push_back(m);
+ ModuleList.push_back(m);
+ return m;
+}
+
+StructurePort *Structure::createStructurePort(const Arts::PortType& type)
+{ // TODO:PORT: verify this code
+#if 0
+ arts_debug("TODO:PORT:createStructurePort");
+#endif
+// portname generation
+ unsigned long portindex = 1,l,baselen;;
+ char name[100];
+
+ string namebase;
+ if(type.direction == Arts::input) {
+ // this is a port where our structure puts its results
+ // so it is an input port, that is named out
+ namebase = "out"; baselen = 3;
+ } else {
+ // this is a port where our structure gets data from
+ // so it is an output port, that is named in
+ namebase = "in"; baselen = 2;
+ }
+
+ vector<Arts::StructurePortDesc> *sps = StructureDesc.ports();
+
+ for(l=0;l<sps->size();l++) {
+ string thisname = (*sps)[l].name();
+ if(strncmp(thisname.c_str(), namebase.c_str(), baselen) == 0 &&
+ strlen(thisname.c_str()) > baselen)
+ {
+ unsigned long index2 = atol(&thisname.c_str()[baselen]);
+ if(index2 >= portindex) portindex = index2+1;
+ }
+ }
+ delete sps;
+
+ sprintf(name,"%s%ld",namebase.c_str(),portindex);
+ arts_debug("new Portname: %s",name);
+ Arts::StructurePortDesc spd =
+ StructureDesc.createStructurePortDesc(type,name);
+
+ assert(canvas);
+ StructurePort *s = new StructurePort(spd,StructureDesc,canvas);
+ ComponentList.push_back(s);
+ return s;
+}
+
+list<Module *> *Structure::getModuleList()
+{
+ return(&ModuleList);
+}
+
+list<StructureComponent *> *Structure::getComponentList()
+{
+ return(&ComponentList);
+}
+
+long Structure::countSelected()
+{
+ list<StructureComponent *>::iterator ci;
+ long targets = 0;
+
+ for(ci=ComponentList.begin();ci!=ComponentList.end();++ci)
+ if((*ci)->selected()) targets++;
+
+ return targets;
+}
+
+void Structure::deleteSelected()
+{
+ arts_debug("deleteSelected...");
+
+ // remove module from the ModuleList
+ list<Module *>::iterator mi;
+ for(mi=ModuleList.begin();mi!=ModuleList.end();)
+ {
+ if((*mi)->selected())
+ {
+ mi = ModuleList.erase(mi);
+ }
+ else mi++;
+ }
+
+ // disconnect ports (it might be useful to get this right in the model
+ // instead of doing it in the view - however at it works without end
+ // user visible bugs like this)
+
+ list<StructureComponent *>::iterator ci;
+ list<ModulePort *> allPorts;
+
+ for(ci = ComponentList.begin(); ci!=ComponentList.end(); ++ci)
+ if((*ci)->selected())
+ (*ci)->dumpPorts(allPorts);
+
+ list<ModulePort *>::iterator pi;
+ for(pi = allPorts.begin(); pi != allPorts.end(); ++pi)
+ (*pi)->PortDesc.disconnectAll();
+
+ // and from the ComponentList (the future ;)
+
+ ci = ComponentList.begin();
+ while(ci!=ComponentList.end())
+ {
+ if((*ci)->selected())
+ {
+ delete (*ci);
+ ci = ComponentList.erase(ci);
+ }
+ else ci++;
+ }
+
+ arts_debug("deleteSelected ok.");
+}
+
+StructureComponent *Structure::componentAt(long x, long y, bool ignore_selected)
+{
+ list<StructureComponent *>::iterator ci;
+
+ for(ci=ComponentList.begin();ci!=ComponentList.end();++ci)
+ {
+ StructureComponent *c = *ci;
+
+ if(x >= c->x() && x < c->x()+c->width() &&
+ y >= c->y() && y < c->y()+c->height())
+ {
+ if((c->selected() && !ignore_selected) || !c->selected()) return c;
+ }
+ }
+
+ return 0;
+}
diff --git a/arts/builder/structure.h b/arts/builder/structure.h
new file mode 100644
index 00000000..7717f3ef
--- /dev/null
+++ b/arts/builder/structure.h
@@ -0,0 +1,92 @@
+ /*
+
+ Copyright (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef __STRUCTURE_H_
+#define __STRUCTURE_H_
+
+#include "artsbuilder.h"
+#include "module.h"
+#include "scomponent.h"
+#include "structureport.h"
+#include "artsflow.h"
+#include <vector>
+
+using namespace std;
+
+class ExecutableStructure
+{
+public:
+ ExecutableStructure();
+ virtual ~ExecutableStructure();
+
+ virtual bool startExecute() = 0;
+ virtual void stopExecute();
+ virtual bool isExecuting();
+ virtual bool saveSession(const char *filename);
+};
+
+class Structure :public ExecutableStructure
+{
+ Arts::StructureDesc StructureDesc;
+ Arts::SynthModule structure;
+ StructureCanvas *canvas;
+
+ std::list<Module *> ModuleList;
+ std::list<StructureComponent *> ComponentList;
+
+public:
+ Structure();
+ ~Structure();
+
+ void setCanvas(StructureCanvas *canvas);
+
+ bool startExecute();
+ void stopExecute();
+
+ bool valid();
+ void load(const char *filename);
+ void retrieve(const char *pubname);
+ void loadFromList(const std::vector<std::string>& strseq);
+ void saveInto(FILE *file);
+ bool save(const char *filename);
+ void clear();
+ void publish();
+
+ long countSelected();
+ void deleteSelected();
+
+ std::string name();
+ void rename(const char *newname);
+
+ void addInheritedInterface(const char *iface);
+ void removeInheritedInterface(const char *iface);
+ vector<std::string> *inheritedInterfaces();
+
+ Module *createModule(const Arts::ModuleInfo& minfo);
+ StructurePort *createStructurePort(const Arts::PortType& type);
+
+ StructureComponent *componentAt(long x, long y, bool ignore_selected);
+
+ std::list<Module *> *getModuleList();
+ std::list<StructureComponent *> *getComponentList();
+};
+
+#endif
diff --git a/arts/builder/structureport.cpp b/arts/builder/structureport.cpp
new file mode 100644
index 00000000..a645d60a
--- /dev/null
+++ b/arts/builder/structureport.cpp
@@ -0,0 +1,287 @@
+ /*
+
+ Copyright (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "structureport.h"
+#include "drawutils.h"
+#include <qpainter.h>
+#include <qpalette.h>
+#include <qdrawutil.h>
+#include <qbitmap.h>
+#include <assert.h>
+#include <stdio.h>
+#include <arts/debug.h>
+
+using namespace std;
+
+StructurePort::StructurePort(Arts::StructurePortDesc SPortDesc,
+ Arts::StructureDesc StructureDesc, StructureCanvas *canvas)
+ : StructureComponent(canvas)
+{
+ string portname = SPortDesc.name();
+ this->SPortDesc = SPortDesc;
+ this->StructureDesc = StructureDesc;
+ _x = SPortDesc.x();
+ _y = SPortDesc.y();
+ _selected = false;
+ _visible = false;
+
+ ModulePort::Direction dir;
+ if(SPortDesc.type().direction == Arts::input)
+ dir = ModulePort::in;
+ else
+ dir = ModulePort::out;
+
+ if(SPortDesc.type().connType == Arts::conn_property)
+ arts_debug("got property here....");
+ // owner, description, portnr
+ arts_debug(">>creating structureport: %s",portname.c_str());
+ _port = new ModulePort(this, portname.c_str(), 0, dir,this->SPortDesc);
+ arts_debug("<<");
+}
+
+StructurePort::~StructurePort()
+{
+ hide();
+ StructureDesc.freeStructurePortDesc(SPortDesc);
+ delete _port;
+}
+
+bool StructurePort::moveInternal(int x, int y)
+{
+ return SPortDesc.moveTo(x, y);
+}
+
+int StructurePort::width() const
+{
+ return 1;
+}
+
+int StructurePort::height() const
+{
+ return 1;
+}
+
+StructureComponent::ComponentType StructurePort::type()
+{
+ return ctPort;
+}
+
+bool StructurePort::drawNeedsBackground(int segment)
+{
+ assert(segment==0);
+ return true;
+}
+
+void StructurePort::drawSegment(QPainter *dest, int cellsize, int segment)
+{
+ QString pname = QString::fromLocal8Bit(SPortDesc.name().c_str());
+ assert(segment==0);
+
+ QPainter &p = *dest;
+
+ int direction = (_port->direction == ModulePort::out)?1:0;
+
+// FIXME: make these color defs available at one central place, they
+// are currently copied from main.cpp
+
+ QColor mcolor(43,43,168);
+ QColor mcolorlight(164,176,242);
+ QColorGroup g( Qt::white, Qt::blue, mcolorlight, mcolor.dark(), mcolor,
+ Qt::black, Qt::black );
+ QBrush fill( mcolor );
+ QPen textpen(QColor(255,255,180),1);
+ int border = cellsize/10;
+ int boxtop = (cellsize/2)*(1-direction);
+
+ qDrawShadePanel(&p,0,boxtop,cellsize,cellsize/2, g, false, 1, &fill);
+ p.setPen(textpen);
+
+ // Selection box
+ if(_selected)
+ {
+ QPen pen(Qt::white,1,Qt::DotLine);
+
+ p.setPen(pen);
+ p.drawRect(0,boxtop,cellsize,cellsize/2);
+ }
+ // ... doesn't look centered without the 2*border ?!?
+ int textwidth;
+ QString label=DrawUtils::cropText(&p, pname, cellsize-border*2, textwidth);
+ p.drawText(border,border+boxtop,cellsize-border-1,(cellsize/2-1)-2*border,
+ Qt::AlignCenter,label);
+
+ int arrowwidth = cellsize/4;
+
+ int i;
+ for(i=0;i<3;i++)
+ {
+ QBrush fbrush;
+ int delta = 0;
+ switch(i)
+ {
+ case 0: delta = 0;
+ fbrush = QBrush(g.light());
+ break;
+ case 1: delta = 2;
+ fbrush = QBrush(g.dark());
+ break;
+ case 2: delta = 1;
+ fbrush = fill;
+ break;
+ }
+
+/**********************
+- | |
+c | |
+- -+ +-
+d | |
+- \ /
+ \ /
+ \_/
+ |b| w |b|
+*********************/
+
+ int t = (cellsize/2-1)*direction; // top
+ int l = delta; // left
+ int w = arrowwidth-2; // arrow body width
+ int h = cellsize/2; // total arrow height
+
+ int b = (cellsize/2-arrowwidth)/2; // x border width
+/*
+ int c = cellsize/10;
+ int d = cellsize/6;
+*/
+ int c = cellsize/12;
+ int d = cellsize/5;
+
+ QPointArray a(9);
+ a.setPoint(0,b+l,t);
+ a.setPoint(1,b+l,t+c);
+ a.setPoint(2,l,t+c);
+ a.setPoint(3,l,t+c+d);
+ a.setPoint(4,b+w/2+l,t+h);
+ a.setPoint(5,b*2+w+l,t+c+d);
+ a.setPoint(6,b*2+w+l,t+c);
+ a.setPoint(7,b+w+l,t+c);
+ a.setPoint(8,b+w+l,t);
+ //a.setPoint(9,b+l,t);
+/*
+ a.setPoint(0,t,b+l);
+ a.setPoint(1,t+c,b+l);
+ a.setPoint(2,t+c,l);
+ a.setPoint(3,t+c+d,l);
+ a.setPoint(4,t+h,b+w/2+l);
+ a.setPoint(5,t+c+d,b*2+w+l);
+ a.setPoint(6,t+c,b*2+w+l);
+ a.setPoint(7,t+c,b+w+l);
+ a.setPoint(8,t,b+w+l);
+ a.setPoint(9,t,b+l);
+*/
+ p.setPen(Qt::NoPen);
+ p.setBrush(fbrush);
+ p.drawPolygon(a);
+ if(delta==1 && direction==0)
+ {
+ p.setPen(g.light());
+ p.drawLine(b+l,t,b+w+l,t);
+ }
+/*
+ p.fillRect((cellsize/2-arrowwidth)/2+delta,cellsize/2-1,
+ arrowwidth,cellsize/2,fbrush);
+*/
+ }
+ {
+ int border = cellsize/7;
+
+ QBrush pbrush(_port->color(false));
+
+ _port->clickrect = QRect(border,direction * cellsize/2 + border,
+ cellsize/2-2*border, cellsize/2-2*border);
+ qDrawShadePanel(&p, _port->clickrect, g, _port->down(), 2, &pbrush);
+ }
+}
+
+ModulePort *StructurePort::portAt(int segment, int x, int y)
+{
+ assert(segment == 0);
+
+ QPoint clickpoint(x,y);
+ if(_port->clickrect.contains(clickpoint)) return _port;
+ return 0;
+}
+
+void StructurePort::dumpPorts(list<ModulePort *>& ports)
+{
+ ports.push_back(_port);
+}
+
+QPixmap *StructurePort::pixmap()
+{
+ return 0;
+}
+
+QString StructurePort::name()
+{
+ return QString::fromLocal8Bit(SPortDesc.name().c_str());
+}
+
+void StructurePort::raisePosition()
+{
+ SPortDesc.raisePosition();
+}
+
+void StructurePort::lowerPosition()
+{
+ SPortDesc.lowerPosition();
+}
+
+void StructurePort::rename(const char *newname)
+{
+ SPortDesc.rename(newname);
+ canvas->redrawRect(_x,_y,1,1);
+}
+
+long StructurePort::id()
+{
+ return SPortDesc.ID();
+}
+
+long StructurePort::position()
+{
+ return SPortDesc.position();
+}
+
+ModulePort *StructurePort::port()
+{
+ return _port;
+}
+
+const char *StructurePort::inheritedInterface()
+{
+ static string ii;
+ ii = SPortDesc.inheritedInterface();
+ return ii.c_str();
+}
+
+void StructurePort::inheritedInterface(const char *iface)
+{
+ SPortDesc.inheritedInterface(iface);
+}
diff --git a/arts/builder/structureport.h b/arts/builder/structureport.h
new file mode 100644
index 00000000..3eac37a4
--- /dev/null
+++ b/arts/builder/structureport.h
@@ -0,0 +1,66 @@
+ /*
+
+ Copyright (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef STRUCTUREPORT_H
+#define STRUCTUREPORT_H
+
+#include "scomponent.h"
+#include "module.h"
+
+class StructurePort :public StructureComponent {
+protected:
+ Arts::StructurePortDesc SPortDesc;
+ Arts::StructureDesc StructureDesc;
+
+ ModulePort *_port;
+
+ bool moveInternal(int x, int y);
+
+public:
+ StructurePort( Arts::StructurePortDesc SPortDesc,
+ Arts::StructureDesc StructureDesc, StructureCanvas *canvas);
+ ~StructurePort();
+
+ void raisePosition();
+ void lowerPosition();
+ void rename(const char *newname);
+ long id();
+ long position();
+ ModulePort *port();
+
+ const char *inheritedInterface();
+ void inheritedInterface(const char *iface);
+
+ // StructureComponent Interface
+ int width() const;
+ int height() const;
+ ComponentType type();
+
+ ModulePort *portAt(int segment, int x, int y);
+ void dumpPorts(std::list<ModulePort *>& ports);
+
+ bool drawNeedsBackground(int segment);
+ void drawSegment(QPainter *dest, int cellsize, int segment);
+ QPixmap *pixmap();
+ QString name();
+};
+
+#endif // STRUCTUREPORT_H
diff --git a/arts/builder/x-artsbuilder.desktop b/arts/builder/x-artsbuilder.desktop
new file mode 100644
index 00000000..684125ae
--- /dev/null
+++ b/arts/builder/x-artsbuilder.desktop
@@ -0,0 +1,60 @@
+# KDE Config File
+[Desktop Entry]
+MimeType=application/x-artsbuilder
+Comment=Arts Builder
+Comment[ar]=أداة لبناء الأصوات باستخدام aRts
+Comment[bg]=Аудио дизайнер
+Comment[bn]=আর্ট্‌স্ বিল্ডার
+Comment[br]=Arload Arts Builder
+Comment[ca]=Constructor Arts
+Comment[cs]=Konstruktér aRts
+Comment[cy]=Adeiladwr Arts
+Comment[da]=aRts-bygger
+Comment[de]=Arts-Builder
+Comment[eo]=Arts-Kreilo
+Comment[es]=Arts Builder (diseñador del sintetizador aRts)
+Comment[et]=aRts'i generaator
+Comment[fa]=سازندۀ Arts
+Comment[fi]=Arts-rakentaja
+Comment[fr]=Créateur de synthétiseurs d'aRts
+Comment[ga]=Tógálaí Arts
+Comment[gl]=Constructor Arts
+Comment[he]=בנאי של Arts
+Comment[hi]=एआरटीएस बिल्डर
+Comment[hu]=aRts-alapú hangmodellező program
+Comment[id]=Builder Arts
+Comment[is]=Arts smiður
+Comment[it]=Costruttore per aRts
+Comment[ja]=aRts ビルダー
+Comment[kk]=Arts құрастырғышы
+Comment[km]=កម្មវិធី​បង្កើត Arts
+Comment[ko]=Arts 만들기
+Comment[lt]=Arts komponuoklis
+Comment[mk]=Градител на Arts
+Comment[mt]=Bennej Arts
+Comment[nb]=Arts-bygger
+Comment[nds]=Klangfiltern för aRts
+Comment[ne]=कला निर्माता
+Comment[nl]=Arts Bouwprogramma
+Comment[nn]=Arts-byggjar
+Comment[pa]=Arts ਨਿਰਮਾਤਾ
+Comment[pt]=Construtor do Arts
+Comment[pt_BR]=Construtor Arts
+Comment[ru]=Звуковой дизайнер aRts
+Comment[se]=Arts-huksejeaddji
+Comment[sl]=Graditelj Arts
+Comment[sr]=Градитељ Arts-а
+Comment[sr@Latn]=Graditelj Arts-a
+Comment[sv]=Arts-byggare
+Comment[ta]=கலைத்திறன் உருவாக்குபவர்
+Comment[tg]=Созандаи aRts
+Comment[tr]=Arts Oluşturucu
+Comment[ven]=Muiti wa zwavhutsila
+Comment[xh]=Umakhi wemizobo
+Comment[zh_CN]=Arts 构建程序
+Comment[zu]=Umakhi Wezamasiko
+Icon=arts
+Type=MimeType
+Patterns=*.arts;
+OnlyShowIn=KDE;
+X-KDE-AutoEmbed=false
diff --git a/arts/configure.in.in b/arts/configure.in.in
new file mode 100644
index 00000000..e76c7e40
--- /dev/null
+++ b/arts/configure.in.in
@@ -0,0 +1,60 @@
+
+if test "x$build_arts" = "xno"; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE arts"
+fi
+
+dnl Find aRts using artsc-config
+AC_DEFUN([AC_FIND_ARTS],
+[
+ AC_PATH_PROG(ARTSCCONFIG, artsc-config, no)
+ if test "x$ARTSCCONFIG" = "xno" ; then
+ AC_MSG_ERROR(Cannot find artsc-config - missing from path?)
+ fi
+ ARTSDIR=[`$ARTSCCONFIG --arts-prefix`]
+])
+
+KDE_CHECK_THREADING
+
+dnl Check if we are building as part of KDE.
+
+AC_MSG_CHECKING(if building standalone aRts snapshot without KDE)
+if test "$DCOPIDL" = ""; then
+ AC_MSG_RESULT(yes)
+
+ AC_FIND_ARTS
+
+ dnl find mcopidl
+ MCOPIDL="$ARTSDIR/bin/mcopidl"
+ AC_SUBST(MCOPIDL)
+
+ dnl fake KDE_RPATH, moc, uic
+ KDE_RPATH=""
+ MOC=true
+ UIC=true
+ AC_SUBST(KDE_RPATH)
+ AC_SUBST(MOC)
+ AC_SUBST(UIC)
+
+ dnl variables
+ arts_datadir="$ARTSDIR/share"
+ arts_includes="$ARTSDIR/include/arts"
+ arts_libraries="$ARTSDIR/lib"
+ ARTS_BUILD_KDE=""
+ ARTS_BUILD_KDE_GUI=""
+else
+ AC_MSG_RESULT(no)
+
+ dnl variables
+ arts_datadir="$kde_datadir"
+ arts_includes="$kde_includes/arts"
+ arts_libraries="$kde_libraries"
+
+ dnl conditional build some things
+ ARTS_BUILD_KDE="builder tools"
+ ARTS_BUILD_KDE_GUI="kde"
+fi
+
+AC_SUBST(arts_datadir)
+AC_SUBST(arts_includes)
+AC_SUBST(arts_libraries)
+AM_CONDITIONAL(arts_within_KDE, test -n "$ARTS_BUILD_KDE")
diff --git a/arts/examples/Makefile.am b/arts/examples/Makefile.am
new file mode 100644
index 00000000..b39302ed
--- /dev/null
+++ b/arts/examples/Makefile.am
@@ -0,0 +1,73 @@
+arts_examples_DATA = \
+README \
+example_adsr.arts \
+example_atan_saturate.arts \
+example_autopanner.arts \
+example_brickwall.arts \
+example_bus.arts \
+example_capture_wav.arts \
+example_cdelay.arts \
+example_cflanger.arts \
+example_data.arts \
+example_delay.arts \
+example_dtmf1.arts \
+example_equalizer.arts \
+example_fm.arts \
+example_freeverb.arts \
+example_moog.arts \
+example_multi_add.arts \
+example_noise.arts \
+example_pitchshift.arts \
+example_play_wave.arts \
+example_pscale.arts \
+example_pulse.arts \
+example_rc.arts \
+example_record.arts \
+example_sequence.arts \
+example_shelve_cutoff.arts \
+example_sine.arts \
+example_softsaw.arts \
+example_square.arts \
+example_stereobeep.arts \
+example_tremolo.arts \
+example_tri.arts \
+example_xfade.arts \
+instrument_full_square.arts \
+instrument_hihat.arts \
+instrument_neworgan.arts \
+instrument_nokind.arts \
+instrument_organ2.arts \
+instrument_simple_sin.arts \
+instrument_simple_square.arts \
+instrument_simple_tri.arts \
+instrument_slide.arts \
+instrument_slide1.arts \
+instrument_square.arts \
+instrument_tri.arts \
+template_Empty_Structure.arts \
+template_Instrument.arts \
+instrument_arts_all.arts-map \
+instrument_chirpdrum.arts \
+instrument_deepdrum.arts
+
+arts_examplesdir = $(arts_datadir)/artsbuilder/examples
+
+todo_DATA = \
+instrument_flexible_slide.arts \
+instrument_flexible_slide_GUI.arts \
+instrument_moog_vcf_tune.arts \
+instrument_moog_vcf_tune_GUI.arts \
+instrument_fm_horn.arts \
+effect_delay.arts \
+effect_delay_alone.arts \
+effect_flanger_alone.arts \
+mixer_element_simple.arts \
+mixer_element_eq.arts \
+mixer_element_eqfx.arts \
+template_Instrument_GUI.arts \
+template_Mixer_Element.arts
+
+tododir = $(arts_datadir)/artsbuilder/examples/todo
+
+
+EXTRA_DIST = $(arts_examples_DATA)
diff --git a/arts/examples/README b/arts/examples/README
new file mode 100644
index 00000000..2132bfc3
--- /dev/null
+++ b/arts/examples/README
@@ -0,0 +1,256 @@
+Example Arts Modules
+--------------------
+
+This directory contains example arts modules. The examples fall into
+several categories:
+
+1. Effects which can be used as reusable building blocks (named
+ effect_*.arts)
+
+2. Standalone examples illustrating how to use each of the built-in
+ arts modules (named example_*.arts). These typically send some
+ output to a sound card.
+
+3. Instruments built from lower level arts modules (named
+ instrument_*.arts). These following a standard convention for
+ input and output ports so they can be used by a (future) instrument
+ manager.
+
+4. Mixer elements used for creating mixers, including graphical
+ controls (named mixer_element_*.arts).
+
+5. Templates for creating new modules (names template_*.arts).
+
+6. Miscellaneous modules that don't fit into any of the above
+ categories.
+
+Detailed Description Of Each Module
+-----------------------------------
+
+Examples
+--------
+
+example_stereo_beep.arts
+
+Generates a 440Hz sine wave tone in the left channel and an 880Hz sine
+wave tone in the right channel, and sends it to the sound card
+output. This is referenced in the aRts documentation.
+
+example_sine.arts
+
+Generates a 440 Hz sine wave.
+
+example_pulse.arts
+
+Generates a 440 Hz pulse wave with a 20% duty cycle.
+
+example_softsaw.arts
+
+Generates a 440 Hz sawtooth wave.
+
+example_square.arts
+
+Generates a 440 Hz square wave.
+
+example_tri.arts
+
+Generates a 440 Hz triangle wave.
+
+example_noise.arts
+
+Generates white noise.
+
+example_dtmf1.arts
+
+Generates a dual tone by producing 697 and 1209 Hz sine waves, scaling
+them by 0.5, and adding them together. This is the DTMF tone for the
+digit "1" on a telephone keypad.
+
+example_atan_saturate.arts
+
+Runs a triangle wave through the atan saturate filter.
+
+example_autopanner.arts
+
+Uses an autopanner to pan a 400 Hz sine wave between the left and
+right speakers at a 2 Hz rate.
+
+example_brickwall.arts
+
+Scales a sine wave by a factor of 5 and then runs it through a
+brickwall limiter.
+
+example_bus.arts
+
+Downlinks from a bus called "Bus" and uplinks to the bus
+"out_soundcard" with the left and right channels reversed.
+
+example_cdelay.arts
+
+Downlinks from a bus called "Delay", uplinks the right channel through
+a 0.5 second cdelay, and the left channel unchanged. You can use
+artscontrol to connect the effect to a sound player and observe the
+results.
+
+example_delay.arts
+
+This is the same as example_cdelay but used the delay effect.
+
+example_capture_wav.arts
+
+This uses the Synth_CAPTURE_WAV to save a 400 Hz sine wave as a wav
+file. Run the module for a few seconds, and then examine the file
+created in /tmp. You can play the file with a player such as kaiman.
+
+example_data.arts
+
+This uses the Data module to generate a constant stream of the value
+"3" and sends it to a Debug module to periodically display it. It
+also contains a Nil module, illustrating how it can be used to
+do nothing at all.
+
+example_adsr.arts
+
+Shows how to create a simple instrument sound using the Envelope Adsr
+module, repetitively triggered by a square wave.
+
+example_fm.arts
+
+This uses the FM Source module to generate a 440 Hz sine
+wave which is frequency modulated at a 5 Hz rate.
+
+example_freeverb.arts
+
+This connects the Freeverb effect from a bus downlink to a bus
+outlink. You can use artscontrol to connect the effect to a sound
+player and observe the results.
+
+example_flanger.arts
+
+This implements a simple flanger effect (it doesn't appear
+to work yet, though).
+
+example_moog.arts
+
+This structure combines the two channels from a bus into
+one, passes it though the Moog VCF filter, and sends
+it out the out_soundcard bus.
+
+example_pitch_shift.arts
+
+This structure passes the left channel of sound card data through the
+Pitch Shift effect. Adjust the speed parameter to vary the effect.
+
+example_rc.arts
+
+This structure passes a white noise generator though an RC filter and
+out to the sound card. By viewing the FFT Scope display in artscontrol
+you can see how this varies from an unfiltered noise waveform.
+
+example_sequence.arts
+
+This demonstrates the Sequence module by playing a sequence of notes.
+
+example_shelve_cutoff.arts
+
+This structure passes a white noise generator though a Shelve Cutoff
+filter and out to the sound card. By viewing the FFT Scope display in
+artscontrol you can see how this varies from an unfiltered noise
+waveform.
+
+example_equalizer.arts
+
+This demonstrates the Std_Equalizer module. It boosts the low and high
+frequencies by 6 dB.
+
+example_tremolo.arts
+
+This demonstrates the Tremolo effect. It modulates the left and right
+channels using a 10 Hz tremolo.
+
+example_xfade.arts
+
+This example mixes 440 and 880 Hz sine waves using a cross fader.
+Adjust the value of the cross fader's percentage input from -1 to 1 to
+control the mixing of the two signals.
+
+example_pscale.arts
+
+This illustrates the Pscale module (I'm not sure if this is a
+meaningful example).
+
+example_play_wav.arts
+
+This illustrates the Play Wave module. You will need to
+enter the full path to a .wav file as the filename
+parameter.
+
+example_multi_add.arts
+
+This shows the Multi Add module which accepts any number of inputs. It
+sums three Data modules which produce inputs of 1, 2, and 3, and
+displays the result 6.
+
+Instruments
+-----------
+
+instrument_flexible_slide.arts
+
+instrument_flexible_slide_GUI.arts
+
+instrument_fm_horn.arts
+
+instrument_full_square.arts
+
+instrument_moog_vcf_tune.arts
+
+instrument_moog_vcf_tune_GUI.arts
+
+instrument_neworgan.arts
+
+instrument_nokind.arts
+
+instrument_organ2.arts
+
+instrument_simple_sin.arts
+
+instrument_simple_square.arts
+
+instrument_simple_tri.arts
+
+instrument_slide.arts
+
+instrument_slide1.arts
+
+instrument_square.arts
+
+instrument_tri.arts
+
+Effects
+-------
+
+effect_delay.arts
+
+effect_delay_alone.arts
+
+effect_flanger_alone.arts
+
+Templates
+---------
+
+template_Empty_Structure.arts
+
+template_Instrument.arts
+
+template_Instrument_GUI.arts
+
+template_Mixer_Element.arts
+
+Mixer Elements
+--------------
+
+mixer_element_simple.arts
+
+mixer_element_eq.arts
+
+mixer_element_eqfx.arts
diff --git a/arts/examples/TODO b/arts/examples/TODO
new file mode 100644
index 00000000..cc886aff
--- /dev/null
+++ b/arts/examples/TODO
@@ -0,0 +1,2 @@
+- port the remaining aRts 0.3 examples
+- test the instruments (need more MIDI infrastructure to be implemented)
diff --git a/arts/examples/checknames.sh b/arts/examples/checknames.sh
new file mode 100755
index 00000000..8806a9fd
--- /dev/null
+++ b/arts/examples/checknames.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+echo "This script searches for improperly named .arts structures:" >&2
+
+for i in *.arts
+do
+ grep ^name=$(echo $i|sed s/.arts//g) $i >/dev/null || echo $i
+done
+
+echo "... done" >&2
diff --git a/arts/examples/effect_delay.arts b/arts/examples/effect_delay.arts
new file mode 100644
index 00000000..0dfff7fb
--- /dev/null
+++ b/arts/examples/effect_delay.arts
@@ -0,0 +1,310 @@
+name=effect_delay
+module=Synth_RC
+{
+ id=60
+ x=0
+ y=8
+ port=invalue
+ {
+ id=61
+ connect_to=100
+ }
+ port=b
+ {
+ id=62
+ connect_to=83
+ }
+ port=f
+ {
+ id=63
+ connect_to=83
+ }
+ port=outvalue
+ {
+ id=64
+ connect_to=71
+ }
+}
+module=Synth_RC
+{
+ id=65
+ x=4
+ y=8
+ port=invalue
+ {
+ id=66
+ connect_to=116
+ }
+ port=b
+ {
+ id=67
+ connect_to=84
+ }
+ port=f
+ {
+ id=68
+ connect_to=84
+ }
+ port=outvalue
+ {
+ id=69
+ connect_to=75
+ }
+}
+module=Synth_CDELAY
+{
+ id=70
+ x=1
+ y=9
+ port=invalue
+ {
+ id=71
+ connect_to=64
+ }
+ port=time
+ {
+ id=72
+ data=0.2
+ }
+ port=outvalue
+ {
+ id=73
+ connect_to=90
+ }
+}
+module=Synth_CDELAY
+{
+ id=74
+ x=5
+ y=9
+ port=invalue
+ {
+ id=75
+ connect_to=69
+ }
+ port=time
+ {
+ id=76
+ data=0.3
+ }
+ port=outvalue
+ {
+ id=77
+ connect_to=106
+ }
+}
+module=Synth_MUL
+{
+ id=89
+ x=2
+ y=11
+ port=invalue1
+ {
+ id=90
+ connect_to=73
+ }
+ port=invalue2
+ {
+ id=91
+ connect_to=82
+ }
+ port=outvalue
+ {
+ id=92
+ connect_to=115
+ }
+}
+module=Synth_ADD
+{
+ id=97
+ x=0
+ y=3
+ port=invalue
+ {
+ id=98
+ connect_to=78
+ }
+ port=addit
+ {
+ id=99
+ connect_to=108
+ }
+ port=outvalue
+ {
+ id=100
+ connect_to=80
+ connect_to=61
+ }
+}
+module=Synth_MUL
+{
+ id=105
+ x=6
+ y=11
+ port=invalue1
+ {
+ id=106
+ connect_to=77
+ }
+ port=invalue2
+ {
+ id=107
+ connect_to=82
+ }
+ port=outvalue
+ {
+ id=108
+ connect_to=99
+ }
+}
+module=Synth_ADD
+{
+ id=113
+ x=4
+ y=3
+ port=invalue
+ {
+ id=114
+ connect_to=79
+ }
+ port=addit
+ {
+ id=115
+ connect_to=92
+ }
+ port=outvalue
+ {
+ id=116
+ connect_to=66
+ connect_to=81
+ }
+}
+structureport
+{
+ name=inleft
+ x=1
+ y=1
+ position=0
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=78
+ connect_to=98
+ }
+}
+structureport
+{
+ name=inright
+ x=5
+ y=1
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=79
+ connect_to=114
+ }
+}
+structureport
+{
+ name=outleft
+ x=2
+ y=5
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=80
+ connect_to=100
+ }
+}
+structureport
+{
+ name=outright
+ x=6
+ y=5
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=81
+ connect_to=116
+ }
+}
+structureport
+{
+ name=level
+ x=4
+ y=9
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=82
+ connect_to=91
+ connect_to=107
+ }
+}
+structureport
+{
+ name=filter1
+ x=2
+ y=6
+ position=3
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=83
+ connect_to=63
+ connect_to=62
+ }
+}
+structureport
+{
+ name=filter2
+ x=6
+ y=6
+ position=4
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=84
+ connect_to=68
+ connect_to=67
+ }
+}
diff --git a/arts/examples/effect_delay_alone.arts b/arts/examples/effect_delay_alone.arts
new file mode 100644
index 00000000..fbc29b7e
--- /dev/null
+++ b/arts/examples/effect_delay_alone.arts
@@ -0,0 +1,310 @@
+name=effect_delay_alone
+module=Synth_RC
+{
+ id=59
+ x=0
+ y=8
+ port=invalue
+ {
+ id=60
+ connect_to=84
+ }
+ port=b
+ {
+ id=61
+ connect_to=98
+ }
+ port=f
+ {
+ id=62
+ connect_to=98
+ }
+ port=outvalue
+ {
+ id=63
+ connect_to=70
+ }
+}
+module=Synth_RC
+{
+ id=64
+ x=4
+ y=8
+ port=invalue
+ {
+ id=65
+ connect_to=92
+ }
+ port=b
+ {
+ id=66
+ connect_to=99
+ }
+ port=f
+ {
+ id=67
+ connect_to=99
+ }
+ port=outvalue
+ {
+ id=68
+ connect_to=74
+ }
+}
+module=Synth_CDELAY
+{
+ id=69
+ x=1
+ y=9
+ port=invalue
+ {
+ id=70
+ connect_to=63
+ }
+ port=time
+ {
+ id=71
+ data=0.2
+ }
+ port=outvalue
+ {
+ id=72
+ connect_to=78
+ connect_to=95
+ }
+}
+module=Synth_CDELAY
+{
+ id=73
+ x=5
+ y=9
+ port=invalue
+ {
+ id=74
+ connect_to=68
+ }
+ port=time
+ {
+ id=75
+ data=0.3
+ }
+ port=outvalue
+ {
+ id=76
+ connect_to=86
+ connect_to=96
+ }
+}
+module=Synth_MUL
+{
+ id=77
+ x=2
+ y=11
+ port=invalue1
+ {
+ id=78
+ connect_to=72
+ }
+ port=invalue2
+ {
+ id=79
+ connect_to=97
+ }
+ port=outvalue
+ {
+ id=80
+ connect_to=91
+ }
+}
+module=Synth_ADD
+{
+ id=81
+ x=0
+ y=3
+ port=invalue
+ {
+ id=82
+ connect_to=93
+ }
+ port=addit
+ {
+ id=83
+ connect_to=88
+ }
+ port=outvalue
+ {
+ id=84
+ connect_to=60
+ }
+}
+module=Synth_MUL
+{
+ id=85
+ x=6
+ y=11
+ port=invalue1
+ {
+ id=86
+ connect_to=76
+ }
+ port=invalue2
+ {
+ id=87
+ connect_to=97
+ }
+ port=outvalue
+ {
+ id=88
+ connect_to=83
+ }
+}
+module=Synth_ADD
+{
+ id=89
+ x=4
+ y=3
+ port=invalue
+ {
+ id=90
+ connect_to=94
+ }
+ port=addit
+ {
+ id=91
+ connect_to=80
+ }
+ port=outvalue
+ {
+ id=92
+ connect_to=65
+ }
+}
+structureport
+{
+ name=inleft
+ x=1
+ y=1
+ position=0
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=93
+ connect_to=82
+ }
+}
+structureport
+{
+ name=inright
+ x=5
+ y=1
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=94
+ connect_to=90
+ }
+}
+structureport
+{
+ name=outleft
+ x=1
+ y=11
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=95
+ connect_to=72
+ }
+}
+structureport
+{
+ name=outright
+ x=9
+ y=11
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=96
+ connect_to=76
+ }
+}
+structureport
+{
+ name=level
+ x=4
+ y=9
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=97
+ connect_to=79
+ connect_to=87
+ }
+}
+structureport
+{
+ name=filter1
+ x=2
+ y=6
+ position=3
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=98
+ connect_to=61
+ connect_to=62
+ }
+}
+structureport
+{
+ name=filter2
+ x=6
+ y=6
+ position=4
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=99
+ connect_to=66
+ connect_to=67
+ }
+}
diff --git a/arts/examples/effect_flanger_alone.arts b/arts/examples/effect_flanger_alone.arts
new file mode 100644
index 00000000..c3dc1a63
--- /dev/null
+++ b/arts/examples/effect_flanger_alone.arts
@@ -0,0 +1,285 @@
+name=effect_flanger_alone
+module=Synth_FX_CFLANGER
+{
+ id=0
+ x=0
+ y=7
+ port=invalue
+ {
+ id=1
+ connect_to=18
+ }
+ port=lfo
+ {
+ id=2
+ connect_to=17
+ }
+ port=mintime
+ {
+ id=3
+ connect_to=23
+ }
+ port=maxtime
+ {
+ id=4
+ connect_to=24
+ }
+ port=outvalue
+ {
+ id=5
+ connect_to=30
+ }
+}
+module=Synth_FX_CFLANGER
+{
+ id=6
+ x=6
+ y=7
+ port=invalue
+ {
+ id=7
+ connect_to=19
+ }
+ port=lfo
+ {
+ id=8
+ connect_to=17
+ }
+ port=mintime
+ {
+ id=9
+ connect_to=23
+ }
+ port=maxtime
+ {
+ id=10
+ connect_to=24
+ }
+ port=outvalue
+ {
+ id=11
+ connect_to=39
+ }
+}
+module=Synth_FREQUENCY
+{
+ id=12
+ x=5
+ y=3
+ port=frequency
+ {
+ id=13
+ connect_to=22
+ }
+ port=pos
+ {
+ id=14
+ connect_to=16
+ }
+}
+module=Synth_WAVE_SIN
+{
+ id=15
+ x=6
+ y=4
+ port=pos
+ {
+ id=16
+ connect_to=14
+ }
+ port=outvalue
+ {
+ id=17
+ connect_to=2
+ connect_to=8
+ }
+}
+module=Synth_MUL
+{
+ id=29
+ x=2
+ y=9
+ port=invalue1
+ {
+ id=30
+ connect_to=5
+ }
+ port=invalue2
+ {
+ id=31
+ connect_to=41
+ }
+ port=outvalue
+ {
+ id=32
+ connect_to=20
+ }
+}
+module=Synth_MUL
+{
+ id=37
+ x=5
+ y=9
+ port=invalue1
+ {
+ id=38
+ connect_to=41
+ }
+ port=invalue2
+ {
+ id=39
+ connect_to=11
+ }
+ port=outvalue
+ {
+ id=40
+ connect_to=21
+ }
+}
+structureport
+{
+ name=inleft
+ x=1
+ y=6
+ position=0
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=18
+ connect_to=1
+ }
+}
+structureport
+{
+ name=inright
+ x=7
+ y=6
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=19
+ connect_to=7
+ }
+}
+structureport
+{
+ name=outleft
+ x=4
+ y=10
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=20
+ connect_to=32
+ }
+}
+structureport
+{
+ name=outright
+ x=7
+ y=10
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=21
+ connect_to=40
+ }
+}
+structureport
+{
+ name=frequency
+ x=6
+ y=2
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=22
+ connect_to=13
+ }
+}
+structureport
+{
+ name=mintime
+ x=6
+ y=0
+ position=3
+ type
+ {
+ direction=output
+ datatype=string
+ conntype=property
+ }
+ data
+ {
+ id=23
+ connect_to=3
+ connect_to=9
+ }
+}
+structureport
+{
+ name=maxtime
+ x=7
+ y=0
+ position=4
+ type
+ {
+ direction=output
+ datatype=string
+ conntype=property
+ }
+ data
+ {
+ id=24
+ connect_to=4
+ connect_to=10
+ }
+}
+structureport
+{
+ name=level
+ x=6
+ y=8
+ position=5
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=41
+ connect_to=38
+ connect_to=31
+ }
+}
diff --git a/arts/examples/example_adsr.arts b/arts/examples/example_adsr.arts
new file mode 100644
index 00000000..b6a2cdff
--- /dev/null
+++ b/arts/examples/example_adsr.arts
@@ -0,0 +1,137 @@
+name=example_adsr
+module=Arts::Synth_AMAN_PLAY
+{
+ id=0
+ x=1
+ y=7
+ port=title
+ {
+ id=1
+ string_data=Adsr
+ }
+ port=autoRestoreID
+ {
+ id=2
+ string_data=Demo2
+ }
+ port=left
+ {
+ id=3
+ connect_to=21
+ }
+ port=right
+ {
+ id=4
+ connect_to=21
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=5
+ x=5
+ y=3
+ port=pos
+ {
+ id=6
+ connect_to=10
+ }
+ port=outvalue
+ {
+ id=7
+ connect_to=16
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=8
+ x=5
+ y=1
+ port=frequency
+ {
+ id=9
+ audio_data=440.00000
+ }
+ port=pos
+ {
+ id=10
+ connect_to=6
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=11
+ x=1
+ y=1
+ port=frequency
+ {
+ id=12
+ audio_data=1.00000
+ }
+ port=pos
+ {
+ id=13
+ connect_to=24
+ }
+}
+module=Arts::Synth_ENVELOPE_ADSR
+{
+ id=14
+ x=2
+ y=5
+ port=active
+ {
+ id=15
+ connect_to=25
+ }
+ port=invalue
+ {
+ id=16
+ connect_to=7
+ }
+ port=attack
+ {
+ id=17
+ audio_data=0.10000
+ }
+ port=decay
+ {
+ id=18
+ audio_data=0.10000
+ }
+ port=sustain
+ {
+ id=19
+ audio_data=0.50000
+ }
+ port=release
+ {
+ id=20
+ audio_data=0.10000
+ }
+ port=outvalue
+ {
+ id=21
+ connect_to=3
+ connect_to=4
+ }
+ port=done
+ {
+ id=22
+ }
+}
+module=Arts::Synth_WAVE_SQUARE
+{
+ id=23
+ x=1
+ y=3
+ port=pos
+ {
+ id=24
+ connect_to=13
+ }
+ port=outvalue
+ {
+ id=25
+ connect_to=15
+ }
+}
diff --git a/arts/examples/example_atan_saturate.arts b/arts/examples/example_atan_saturate.arts
new file mode 100644
index 00000000..c693b38b
--- /dev/null
+++ b/arts/examples/example_atan_saturate.arts
@@ -0,0 +1,81 @@
+name=example_atan_saturate
+module=Arts::Synth_FREQUENCY
+{
+ id=76
+ x=1
+ y=1
+ port=frequency
+ {
+ id=77
+ audio_data=440.00000
+ }
+ port=pos
+ {
+ id=78
+ connect_to=89
+ }
+}
+module=Arts::Synth_AMAN_PLAY
+{
+ id=79
+ x=1
+ y=7
+ port=title
+ {
+ id=80
+ string_data=Atan Saturate
+ }
+ port=autoRestoreID
+ {
+ id=81
+ string_data=Atan Saturate
+ }
+ port=left
+ {
+ id=82
+ connect_to=87
+ }
+ port=right
+ {
+ id=83
+ connect_to=87
+ }
+}
+module=Arts::Synth_ATAN_SATURATE
+{
+ id=84
+ x=2
+ y=5
+ port=inscale
+ {
+ id=85
+ audio_data=1.00000
+ }
+ port=invalue
+ {
+ id=86
+ connect_to=90
+ }
+ port=outvalue
+ {
+ id=87
+ connect_to=82
+ connect_to=83
+ }
+}
+module=Arts::Synth_WAVE_TRI
+{
+ id=88
+ x=2
+ y=3
+ port=pos
+ {
+ id=89
+ connect_to=78
+ }
+ port=outvalue
+ {
+ id=90
+ connect_to=86
+ }
+}
diff --git a/arts/examples/example_autopanner.arts b/arts/examples/example_autopanner.arts
new file mode 100644
index 00000000..0a11a2e7
--- /dev/null
+++ b/arts/examples/example_autopanner.arts
@@ -0,0 +1,117 @@
+name=example_autopanner
+module=Arts::Synth_FREQUENCY
+{
+ id=0
+ x=1
+ y=1
+ port=frequency
+ {
+ id=1
+ audio_data=440.00000
+ }
+ port=pos
+ {
+ id=2
+ connect_to=4
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=3
+ x=1
+ y=3
+ port=pos
+ {
+ id=4
+ connect_to=2
+ }
+ port=outvalue
+ {
+ id=5
+ connect_to=12
+ }
+}
+module=Arts::Synth_AMAN_PLAY
+{
+ id=6
+ x=1
+ y=7
+ port=title
+ {
+ id=7
+ string_data=Autopanner
+ }
+ port=autoRestoreID
+ {
+ id=8
+ string_data=Autopanner
+ }
+ port=left
+ {
+ id=9
+ connect_to=14
+ }
+ port=right
+ {
+ id=10
+ connect_to=15
+ }
+}
+module=Arts::Synth_AUTOPANNER
+{
+ id=11
+ x=2
+ y=5
+ port=invalue
+ {
+ id=12
+ connect_to=5
+ }
+ port=inlfo
+ {
+ id=13
+ connect_to=18
+ }
+ port=outvalue1
+ {
+ id=14
+ connect_to=9
+ }
+ port=outvalue2
+ {
+ id=15
+ connect_to=10
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=16
+ x=5
+ y=3
+ port=pos
+ {
+ id=17
+ connect_to=21
+ }
+ port=outvalue
+ {
+ id=18
+ connect_to=13
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=19
+ x=5
+ y=1
+ port=frequency
+ {
+ id=20
+ audio_data=2.00000
+ }
+ port=pos
+ {
+ id=21
+ connect_to=17
+ }
+}
diff --git a/arts/examples/example_brickwall.arts b/arts/examples/example_brickwall.arts
new file mode 100644
index 00000000..546620e1
--- /dev/null
+++ b/arts/examples/example_brickwall.arts
@@ -0,0 +1,98 @@
+name=example_brickwall
+module=Arts::Synth_FREQUENCY
+{
+ id=0
+ x=1
+ y=1
+ port=frequency
+ {
+ id=1
+ audio_data=440.00000
+ }
+ port=pos
+ {
+ id=2
+ connect_to=13
+ connect_to=25
+ }
+}
+module=Arts::Synth_AMAN_PLAY
+{
+ id=3
+ x=1
+ y=9
+ port=title
+ {
+ id=4
+ string_data=Brickwall
+ }
+ port=autoRestoreID
+ {
+ id=5
+ string_data=Brickwall
+ }
+ port=left
+ {
+ id=6
+ connect_to=20
+ }
+ port=right
+ {
+ id=7
+ connect_to=20
+ }
+}
+module=Arts::Synth_BRICKWALL_LIMITER
+{
+ id=18
+ x=2
+ y=7
+ port=invalue
+ {
+ id=19
+ connect_to=34
+ }
+ port=outvalue
+ {
+ id=20
+ connect_to=6
+ connect_to=7
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=24
+ x=1
+ y=3
+ port=pos
+ {
+ id=25
+ connect_to=2
+ }
+ port=outvalue
+ {
+ id=26
+ connect_to=33
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=31
+ x=1
+ y=5
+ port=invalue1
+ {
+ id=32
+ audio_data=5.00000
+ }
+ port=invalue2
+ {
+ id=33
+ connect_to=26
+ }
+ port=outvalue
+ {
+ id=34
+ connect_to=19
+ }
+}
diff --git a/arts/examples/example_bus.arts b/arts/examples/example_bus.arts
new file mode 100644
index 00000000..2a43e84a
--- /dev/null
+++ b/arts/examples/example_bus.arts
@@ -0,0 +1,44 @@
+name=example_bus
+module=Arts::Synth_BUS_DOWNLINK
+{
+ id=0
+ x=1
+ y=1
+ port=busname
+ {
+ id=1
+ string_data=Bus
+ }
+ port=left
+ {
+ id=2
+ connect_to=7
+ }
+ port=right
+ {
+ id=3
+ connect_to=10
+ connect_to=6
+ }
+}
+module=Arts::Synth_BUS_UPLINK
+{
+ id=4
+ x=1
+ y=4
+ port=busname
+ {
+ id=5
+ string_data=out_soundcard
+ }
+ port=left
+ {
+ id=6
+ connect_to=3
+ }
+ port=right
+ {
+ id=7
+ connect_to=2
+ }
+}
diff --git a/arts/examples/example_capture_wav.arts b/arts/examples/example_capture_wav.arts
new file mode 100644
index 00000000..0638e64a
--- /dev/null
+++ b/arts/examples/example_capture_wav.arts
@@ -0,0 +1,50 @@
+name=example_capture_wav
+module=Arts::Synth_CAPTURE_WAV
+{
+ id=3
+ x=1
+ y=5
+ port=left
+ {
+ id=4
+ connect_to=11
+ }
+ port=right
+ {
+ id=5
+ connect_to=11
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=9
+ x=1
+ y=3
+ port=pos
+ {
+ id=10
+ connect_to=17
+ }
+ port=outvalue
+ {
+ id=11
+ connect_to=4
+ connect_to=5
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=15
+ x=1
+ y=1
+ port=frequency
+ {
+ id=16
+ audio_data=440.00000
+ }
+ port=pos
+ {
+ id=17
+ connect_to=10
+ }
+}
diff --git a/arts/examples/example_cdelay.arts b/arts/examples/example_cdelay.arts
new file mode 100644
index 00000000..211834b6
--- /dev/null
+++ b/arts/examples/example_cdelay.arts
@@ -0,0 +1,64 @@
+name=example_cdelay
+module=Arts::Synth_BUS_DOWNLINK
+{
+ id=0
+ x=1
+ y=1
+ port=busname
+ {
+ id=1
+ string_data=Delay
+ }
+ port=left
+ {
+ id=2
+ connect_to=6
+ }
+ port=right
+ {
+ id=3
+ connect_to=10
+ }
+}
+module=Arts::Synth_BUS_UPLINK
+{
+ id=4
+ x=1
+ y=5
+ port=busname
+ {
+ id=5
+ string_data=out_soundcard
+ }
+ port=left
+ {
+ id=6
+ connect_to=2
+ }
+ port=right
+ {
+ id=7
+ connect_to=11
+ }
+}
+module=Arts::Synth_CDELAY
+{
+ id=8
+ x=4
+ y=3
+ port=time
+ {
+ id=9
+ audio_data=0.50000
+ }
+ port=invalue
+ {
+ id=10
+ connect_to=3
+ }
+ port=outvalue
+ {
+ id=11
+ connect_to=7
+ }
+}
diff --git a/arts/examples/example_cflanger.arts b/arts/examples/example_cflanger.arts
new file mode 100644
index 00000000..7f8c2258
--- /dev/null
+++ b/arts/examples/example_cflanger.arts
@@ -0,0 +1,127 @@
+name=example_cflanger
+module=Arts::Synth_FX_CFLANGER
+{
+ id=11
+ x=1
+ y=5
+ port=mintime
+ {
+ id=12
+ audio_data=0.00100
+ }
+ port=maxtime
+ {
+ id=13
+ audio_data=0.00500
+ }
+ port=invalue
+ {
+ id=14
+ connect_to=29
+ }
+ port=lfo
+ {
+ id=15
+ connect_to=19
+ }
+ port=outvalue
+ {
+ id=16
+ connect_to=36
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=17
+ x=8
+ y=3
+ port=pos
+ {
+ id=18
+ connect_to=22
+ }
+ port=outvalue
+ {
+ id=19
+ connect_to=15
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=20
+ x=8
+ y=1
+ port=frequency
+ {
+ id=21
+ audio_data=1.00000
+ }
+ port=pos
+ {
+ id=22
+ connect_to=18
+ }
+}
+module=Arts::Synth_BUS_DOWNLINK
+{
+ id=27
+ x=1
+ y=1
+ port=busname
+ {
+ id=28
+ string_data=Flanger
+ }
+ port=left
+ {
+ id=29
+ connect_to=14
+ connect_to=37
+ }
+ port=right
+ {
+ id=30
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=35
+ x=5
+ y=7
+ port=invalue1
+ {
+ id=36
+ connect_to=16
+ }
+ port=invalue2
+ {
+ id=37
+ connect_to=29
+ }
+ port=outvalue
+ {
+ id=38
+ connect_to=9
+ connect_to=45
+ }
+}
+module=Arts::Synth_BUS_UPLINK
+{
+ id=43
+ x=1
+ y=9
+ port=busname
+ {
+ id=44
+ string_data=out_soundcard
+ }
+ port=left
+ {
+ id=45
+ connect_to=38
+ }
+ port=right
+ {
+ id=46
+ }
+}
diff --git a/arts/examples/example_data.arts b/arts/examples/example_data.arts
new file mode 100644
index 00000000..135c7778
--- /dev/null
+++ b/arts/examples/example_data.arts
@@ -0,0 +1,39 @@
+name=example_data
+module=Arts::Synth_DATA
+{
+ id=3
+ x=1
+ y=1
+ port=value
+ {
+ id=4
+ audio_data=3.00000
+ }
+ port=outvalue
+ {
+ id=5
+ connect_to=11
+ }
+}
+module=Arts::Synth_DEBUG
+{
+ id=9
+ x=1
+ y=3
+ port=comment
+ {
+ id=10
+ string_data=Data:
+ }
+ port=invalue
+ {
+ id=11
+ connect_to=5
+ }
+}
+module=Arts::Synth_NIL
+{
+ id=13
+ x=5
+ y=1
+}
diff --git a/arts/examples/example_delay.arts b/arts/examples/example_delay.arts
new file mode 100644
index 00000000..c7339a5c
--- /dev/null
+++ b/arts/examples/example_delay.arts
@@ -0,0 +1,65 @@
+name=example_delay
+module=Arts::Synth_BUS_DOWNLINK
+{
+ id=0
+ x=1
+ y=1
+ port=busname
+ {
+ id=1
+ string_data=Delay
+ }
+ port=left
+ {
+ id=2
+ connect_to=6
+ }
+ port=right
+ {
+ id=3
+ connect_to=10
+ connect_to=17
+ }
+}
+module=Arts::Synth_BUS_UPLINK
+{
+ id=4
+ x=1
+ y=5
+ port=busname
+ {
+ id=5
+ string_data=out_soundcard
+ }
+ port=left
+ {
+ id=6
+ connect_to=2
+ }
+ port=right
+ {
+ id=7
+ connect_to=19
+ }
+}
+module=Arts::Synth_DELAY
+{
+ id=16
+ x=4
+ y=3
+ port=invalue
+ {
+ id=17
+ connect_to=3
+ }
+ port=time
+ {
+ id=18
+ audio_data=0.50000
+ }
+ port=outvalue
+ {
+ id=19
+ connect_to=7
+ }
+}
diff --git a/arts/examples/example_dtmf1.arts b/arts/examples/example_dtmf1.arts
new file mode 100644
index 00000000..8adcb628
--- /dev/null
+++ b/arts/examples/example_dtmf1.arts
@@ -0,0 +1,155 @@
+name=example_dtmf1
+module=Arts::Synth_AMAN_PLAY
+{
+ id=0
+ x=2
+ y=8
+ port=title
+ {
+ id=1
+ string_data=Dtmf1
+ }
+ port=autoRestoreID
+ {
+ id=2
+ string_data=Dtmf1
+ }
+ port=left
+ {
+ id=3
+ connect_to=14
+ }
+ port=right
+ {
+ id=4
+ connect_to=14
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=5
+ x=1
+ y=2
+ port=pos
+ {
+ id=6
+ connect_to=10
+ }
+ port=outvalue
+ {
+ id=7
+ connect_to=23
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=8
+ x=1
+ y=0
+ port=frequency
+ {
+ id=9
+ audio_data=697.00000
+ }
+ port=pos
+ {
+ id=10
+ connect_to=6
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=11
+ x=3
+ y=6
+ port=invalue1
+ {
+ id=12
+ connect_to=24
+ }
+ port=invalue2
+ {
+ id=13
+ connect_to=28
+ }
+ port=outvalue
+ {
+ id=14
+ connect_to=3
+ connect_to=4
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=15
+ x=5
+ y=2
+ port=pos
+ {
+ id=16
+ connect_to=20
+ }
+ port=outvalue
+ {
+ id=17
+ connect_to=27
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=18
+ x=5
+ y=0
+ port=frequency
+ {
+ id=19
+ audio_data=1209.00000
+ }
+ port=pos
+ {
+ id=20
+ connect_to=16
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=21
+ x=1
+ y=4
+ port=invalue1
+ {
+ id=22
+ audio_data=0.50000
+ }
+ port=invalue2
+ {
+ id=23
+ connect_to=7
+ }
+ port=outvalue
+ {
+ id=24
+ connect_to=12
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=25
+ x=5
+ y=4
+ port=invalue1
+ {
+ id=26
+ audio_data=0.50000
+ }
+ port=invalue2
+ {
+ id=27
+ connect_to=17
+ }
+ port=outvalue
+ {
+ id=28
+ connect_to=13
+ }
+}
diff --git a/arts/examples/example_equalizer.arts b/arts/examples/example_equalizer.arts
new file mode 100644
index 00000000..fe89cea8
--- /dev/null
+++ b/arts/examples/example_equalizer.arts
@@ -0,0 +1,84 @@
+name=example_equalizer
+module=Arts::Synth_BUS_DOWNLINK
+{
+ id=0
+ x=1
+ y=1
+ port=busname
+ {
+ id=1
+ string_data=Equalizer
+ }
+ port=left
+ {
+ id=2
+ connect_to=14
+ }
+ port=right
+ {
+ id=3
+ }
+}
+module=Arts::Synth_BUS_UPLINK
+{
+ id=4
+ x=1
+ y=5
+ port=busname
+ {
+ id=5
+ string_data=out_soundcard
+ }
+ port=left
+ {
+ id=6
+ connect_to=15
+ }
+ port=right
+ {
+ id=7
+ connect_to=15
+ }
+}
+module=Arts::Synth_STD_EQUALIZER
+{
+ id=8
+ x=1
+ y=3
+ port=low
+ {
+ id=9
+ audio_data=6.00000
+ }
+ port=mid
+ {
+ id=10
+ audio_data=0.00000
+ }
+ port=high
+ {
+ id=11
+ audio_data=6.00000
+ }
+ port=frequency
+ {
+ id=12
+ audio_data=1000.00000
+ }
+ port=q
+ {
+ id=13
+ audio_data=1.00000
+ }
+ port=invalue
+ {
+ id=14
+ connect_to=2
+ }
+ port=outvalue
+ {
+ id=15
+ connect_to=6
+ connect_to=7
+ }
+}
diff --git a/arts/examples/example_fm.arts b/arts/examples/example_fm.arts
new file mode 100644
index 00000000..242519c0
--- /dev/null
+++ b/arts/examples/example_fm.arts
@@ -0,0 +1,102 @@
+name=example_fm
+module=Arts::Synth_AMAN_PLAY
+{
+ id=36
+ x=1
+ y=9
+ port=title
+ {
+ id=37
+ string_data=Demo1
+ }
+ port=autoRestoreID
+ {
+ id=38
+ string_data=Demo2
+ }
+ port=left
+ {
+ id=39
+ connect_to=43
+ }
+ port=right
+ {
+ id=40
+ connect_to=43
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=41
+ x=2
+ y=7
+ port=pos
+ {
+ id=42
+ connect_to=54
+ }
+ port=outvalue
+ {
+ id=43
+ connect_to=39
+ connect_to=40
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=44
+ x=1
+ y=3
+ port=pos
+ {
+ id=45
+ connect_to=49
+ }
+ port=outvalue
+ {
+ id=46
+ connect_to=52
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=47
+ x=1
+ y=1
+ port=frequency
+ {
+ id=48
+ audio_data=5.00000
+ }
+ port=pos
+ {
+ id=49
+ connect_to=45
+ }
+}
+module=Arts::Synth_FM_SOURCE
+{
+ id=50
+ x=1
+ y=5
+ port=frequency
+ {
+ id=51
+ audio_data=440.00000
+ }
+ port=modulator
+ {
+ id=52
+ connect_to=46
+ }
+ port=modlevel
+ {
+ id=53
+ audio_data=0.90000
+ }
+ port=pos
+ {
+ id=54
+ connect_to=42
+ }
+}
diff --git a/arts/examples/example_freeverb.arts b/arts/examples/example_freeverb.arts
new file mode 100644
index 00000000..b8d3a321
--- /dev/null
+++ b/arts/examples/example_freeverb.arts
@@ -0,0 +1,99 @@
+name=example_freeverb
+module=Arts::Synth_FREEVERB
+{
+ id=0
+ x=1
+ y=3
+ port=inleft
+ {
+ id=1
+ connect_to=13
+ }
+ port=inright
+ {
+ id=2
+ connect_to=14
+ }
+ port=outleft
+ {
+ id=3
+ connect_to=17
+ }
+ port=outright
+ {
+ id=4
+ connect_to=18
+ }
+ port=roomsize
+ {
+ id=5
+ audio_data=1.00000
+ }
+ port=damp
+ {
+ id=6
+ audio_data=0.20000
+ }
+ port=wet
+ {
+ id=7
+ audio_data=0.20000
+ }
+ port=dry
+ {
+ id=8
+ audio_data=0.20000
+ }
+ port=width
+ {
+ id=9
+ audio_data=1.00000
+ }
+ port=mode
+ {
+ id=10
+ audio_data=0.00000
+ }
+}
+module=Arts::Synth_BUS_DOWNLINK
+{
+ id=11
+ x=1
+ y=1
+ port=busname
+ {
+ id=12
+ string_data=Freeverb
+ }
+ port=left
+ {
+ id=13
+ connect_to=1
+ }
+ port=right
+ {
+ id=14
+ connect_to=2
+ }
+}
+module=Arts::Synth_BUS_UPLINK
+{
+ id=15
+ x=1
+ y=5
+ port=busname
+ {
+ id=16
+ string_data=out_soundcard
+ }
+ port=left
+ {
+ id=17
+ connect_to=3
+ }
+ port=right
+ {
+ id=18
+ connect_to=4
+ }
+}
diff --git a/arts/examples/example_moog.arts b/arts/examples/example_moog.arts
new file mode 100644
index 00000000..3a6a0586
--- /dev/null
+++ b/arts/examples/example_moog.arts
@@ -0,0 +1,96 @@
+name=example_moog
+module=Arts::Synth_MOOG_VCF
+{
+ id=17
+ x=3
+ y=4
+ port=frequency
+ {
+ id=18
+ audio_data=2000.00000
+ }
+ port=resonance
+ {
+ id=19
+ audio_data=3.00000
+ }
+ port=invalue
+ {
+ id=20
+ connect_to=47
+ }
+ port=outvalue
+ {
+ id=21
+ connect_to=36
+ connect_to=37
+ }
+}
+module=Arts::Synth_BUS_DOWNLINK
+{
+ id=26
+ x=3
+ y=0
+ port=busname
+ {
+ id=27
+ string_data=Moog VCF
+ }
+ port=left
+ {
+ id=28
+ connect_to=44
+ }
+ port=right
+ {
+ id=29
+ connect_to=45
+ }
+}
+module=Arts::Synth_BUS_UPLINK
+{
+ id=34
+ x=3
+ y=6
+ port=busname
+ {
+ id=35
+ string_data=out_soundcard
+ }
+ port=left
+ {
+ id=36
+ connect_to=21
+ }
+ port=right
+ {
+ id=37
+ connect_to=21
+ }
+}
+module=Arts::Synth_XFADE
+{
+ id=43
+ x=3
+ y=2
+ port=invalue1
+ {
+ id=44
+ connect_to=28
+ }
+ port=invalue2
+ {
+ id=45
+ connect_to=29
+ }
+ port=percentage
+ {
+ id=46
+ audio_data=0.00000
+ }
+ port=outvalue
+ {
+ id=47
+ connect_to=20
+ }
+}
diff --git a/arts/examples/example_multi_add.arts b/arts/examples/example_multi_add.arts
new file mode 100644
index 00000000..229033bc
--- /dev/null
+++ b/arts/examples/example_multi_add.arts
@@ -0,0 +1,83 @@
+name=example_multi_add
+module=Arts::Synth_MULTI_ADD
+{
+ id=3
+ x=2
+ y=3
+ port=invalue
+ {
+ id=4
+ connect_to=11
+ connect_to=17
+ connect_to=23
+ }
+ port=outvalue
+ {
+ id=5
+ connect_to=29
+ }
+}
+module=Arts::Synth_DATA
+{
+ id=9
+ x=1
+ y=1
+ port=value
+ {
+ id=10
+ audio_data=1.00000
+ }
+ port=outvalue
+ {
+ id=11
+ connect_to=4
+ }
+}
+module=Arts::Synth_DATA
+{
+ id=15
+ x=5
+ y=1
+ port=value
+ {
+ id=16
+ audio_data=2.00000
+ }
+ port=outvalue
+ {
+ id=17
+ connect_to=4
+ }
+}
+module=Arts::Synth_DATA
+{
+ id=21
+ x=9
+ y=1
+ port=value
+ {
+ id=22
+ audio_data=3.00000
+ }
+ port=outvalue
+ {
+ id=23
+ connect_to=4
+ }
+}
+module=Arts::Synth_DEBUG
+{
+ id=27
+ x=1
+ y=5
+ port=comment
+ {
+ id=28
+ string_data=Result
+ }
+ port=invalue
+ {
+ id=29
+ connect_to=5
+ }
+}
diff --git a/arts/examples/example_noise.arts b/arts/examples/example_noise.arts
new file mode 100644
index 00000000..60f6e2e3
--- /dev/null
+++ b/arts/examples/example_noise.arts
@@ -0,0 +1,39 @@
+name=example_noise
+module=Arts::Synth_AMAN_PLAY
+{
+ id=3
+ x=1
+ y=3
+ port=title
+ {
+ id=4
+ string_data=Noise
+ }
+ port=autoRestoreID
+ {
+ id=5
+ string_data=Noise
+ }
+ port=left
+ {
+ id=6
+ connect_to=15
+ }
+ port=right
+ {
+ id=7
+ connect_to=15
+ }
+}
+module=Arts::Synth_NOISE
+{
+ id=14
+ x=1
+ y=1
+ port=outvalue
+ {
+ id=15
+ connect_to=6
+ connect_to=7
+ }
+}
diff --git a/arts/examples/example_pitchshift.arts b/arts/examples/example_pitchshift.arts
new file mode 100644
index 00000000..5126e54b
--- /dev/null
+++ b/arts/examples/example_pitchshift.arts
@@ -0,0 +1,69 @@
+name=example_pitchshift
+module=Arts::Synth_BUS_DOWNLINK
+{
+ id=23
+ x=1
+ y=1
+ port=busname
+ {
+ id=24
+ string_data=Pitch Shift
+ }
+ port=left
+ {
+ id=25
+ connect_to=34
+ }
+ port=right
+ {
+ id=26
+ }
+}
+module=Arts::Synth_BUS_UPLINK
+{
+ id=27
+ x=1
+ y=5
+ port=busname
+ {
+ id=28
+ string_data=out_soundcard
+ }
+ port=left
+ {
+ id=29
+ connect_to=35
+ }
+ port=right
+ {
+ id=30
+ connect_to=35
+ }
+}
+module=Arts::Synth_PITCH_SHIFT
+{
+ id=31
+ x=1
+ y=3
+ port=speed
+ {
+ id=32
+ audio_data=0.75000
+ }
+ port=frequency
+ {
+ id=33
+ audio_data=10.00000
+ }
+ port=invalue
+ {
+ id=34
+ connect_to=25
+ }
+ port=outvalue
+ {
+ id=35
+ connect_to=29
+ connect_to=30
+ }
+}
diff --git a/arts/examples/example_play_wave.arts b/arts/examples/example_play_wave.arts
new file mode 100644
index 00000000..7a85423d
--- /dev/null
+++ b/arts/examples/example_play_wave.arts
@@ -0,0 +1,57 @@
+name=example_play_wave
+module=Arts::Synth_PLAY_WAV
+{
+ id=6
+ x=1
+ y=1
+ port=speed
+ {
+ id=7
+ audio_data=44100.00000
+ }
+ port=filename
+ {
+ id=8
+ string_data=/home/tranter/test.wav
+ }
+ port=finished
+ {
+ id=9
+ }
+ port=left
+ {
+ id=10
+ connect_to=20
+ }
+ port=right
+ {
+ id=11
+ connect_to=21
+ }
+}
+module=Arts::Synth_AMAN_PLAY
+{
+ id=17
+ x=1
+ y=3
+ port=title
+ {
+ id=18
+ string_data=Wave
+ }
+ port=autoRestoreID
+ {
+ id=19
+ string_data=Wave
+ }
+ port=left
+ {
+ id=20
+ connect_to=10
+ }
+ port=right
+ {
+ id=21
+ connect_to=11
+ }
+}
diff --git a/arts/examples/example_pscale.arts b/arts/examples/example_pscale.arts
new file mode 100644
index 00000000..6f364135
--- /dev/null
+++ b/arts/examples/example_pscale.arts
@@ -0,0 +1,112 @@
+name=example_pscale
+module=Arts::Synth_SEQUENCE
+{
+ id=10
+ x=1
+ y=1
+ port=speed
+ {
+ id=11
+ audio_data=0.50000
+ }
+ port=seq
+ {
+ id=12
+ string_data=C-4;D-4;E-4;F-4;G-4;A-4;B-4;C-5;
+ }
+ port=frequency
+ {
+ id=13
+ connect_to=21
+ }
+ port=pos
+ {
+ id=14
+ connect_to=34
+ }
+}
+module=Arts::Synth_AMAN_PLAY
+{
+ id=15
+ x=1
+ y=8
+ port=title
+ {
+ id=16
+ string_data=Pscale
+ }
+ port=autoRestoreID
+ {
+ id=17
+ string_data=Pscale
+ }
+ port=left
+ {
+ id=18
+ connect_to=35
+ }
+ port=right
+ {
+ id=19
+ connect_to=35
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=20
+ x=1
+ y=3
+ port=frequency
+ {
+ id=21
+ connect_to=13
+ }
+ port=pos
+ {
+ id=22
+ connect_to=24
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=23
+ x=1
+ y=5
+ port=pos
+ {
+ id=24
+ connect_to=22
+ }
+ port=outvalue
+ {
+ id=25
+ connect_to=33
+ }
+}
+module=Arts::Synth_PSCALE
+{
+ id=31
+ x=5
+ y=6
+ port=top
+ {
+ id=32
+ audio_data=0.50000
+ }
+ port=invalue
+ {
+ id=33
+ connect_to=25
+ }
+ port=pos
+ {
+ id=34
+ connect_to=14
+ }
+ port=outvalue
+ {
+ id=35
+ connect_to=18
+ connect_to=19
+ }
+}
diff --git a/arts/examples/example_pulse.arts b/arts/examples/example_pulse.arts
new file mode 100644
index 00000000..7da74675
--- /dev/null
+++ b/arts/examples/example_pulse.arts
@@ -0,0 +1,66 @@
+name=example_pulse
+module=Arts::Synth_FREQUENCY
+{
+ id=0
+ x=1
+ y=1
+ port=frequency
+ {
+ id=1
+ audio_data=440.00000
+ }
+ port=pos
+ {
+ id=2
+ connect_to=7
+ connect_to=23
+ }
+}
+module=Arts::Synth_AMAN_PLAY
+{
+ id=12
+ x=1
+ y=5
+ port=title
+ {
+ id=13
+ string_data=Pulse
+ }
+ port=autoRestoreID
+ {
+ id=14
+ string_data=Pulse
+ }
+ port=left
+ {
+ id=15
+ connect_to=24
+ }
+ port=right
+ {
+ id=16
+ connect_to=24
+ }
+}
+module=Arts::Synth_WAVE_PULSE
+{
+ id=21
+ x=2
+ y=3
+ port=dutycycle
+ {
+ id=22
+ audio_data=0.20000
+ }
+ port=pos
+ {
+ id=23
+ connect_to=2
+ }
+ port=outvalue
+ {
+ id=24
+ connect_to=15
+ connect_to=16
+ }
+}
diff --git a/arts/examples/example_rc.arts b/arts/examples/example_rc.arts
new file mode 100644
index 00000000..bef5a662
--- /dev/null
+++ b/arts/examples/example_rc.arts
@@ -0,0 +1,63 @@
+name=example_rc
+module=Arts::Synth_NOISE
+{
+ id=45
+ x=4
+ y=1
+ port=outvalue
+ {
+ id=46
+ connect_to=60
+ }
+}
+module=Arts::Synth_AMAN_PLAY
+{
+ id=47
+ x=2
+ y=5
+ port=title
+ {
+ id=48
+ }
+ port=autoRestoreID
+ {
+ id=49
+ }
+ port=left
+ {
+ id=50
+ connect_to=61
+ }
+ port=right
+ {
+ id=51
+ connect_to=61
+ }
+}
+module=Arts::Synth_RC
+{
+ id=57
+ x=3
+ y=3
+ port=b
+ {
+ id=58
+ audio_data=10.00000
+ }
+ port=f
+ {
+ id=59
+ audio_data=10.00000
+ }
+ port=invalue
+ {
+ id=60
+ connect_to=46
+ }
+ port=outvalue
+ {
+ id=61
+ connect_to=50
+ connect_to=51
+ }
+}
diff --git a/arts/examples/example_record.arts b/arts/examples/example_record.arts
new file mode 100644
index 00000000..62c365a9
--- /dev/null
+++ b/arts/examples/example_record.arts
@@ -0,0 +1,75 @@
+name=example_record
+module=Arts::Synth_DEBUG
+{
+ id=13
+ x=4
+ y=3
+ port=comment
+ {
+ id=14
+ string_data=Left
+ }
+ port=invalue
+ {
+ id=15
+ }
+}
+module=Arts::Synth_DEBUG
+{
+ id=19
+ x=8
+ y=3
+ port=comment
+ {
+ id=20
+ string_data=Right
+ }
+ port=invalue
+ {
+ id=21
+ }
+}
+module=Arts::Synth_AMAN_PLAY
+{
+ id=27
+ x=1
+ y=5
+ port=title
+ {
+ id=28
+ }
+ port=autoRestoreID
+ {
+ id=29
+ }
+ port=left
+ {
+ id=30
+ }
+ port=right
+ {
+ id=31
+ }
+}
+module=Arts::Synth_AMAN_RECORD
+{
+ id=56
+ x=1
+ y=1
+ port=title
+ {
+ id=57
+ }
+ port=autoRestoreID
+ {
+ id=58
+ }
+ port=left
+ {
+ id=59
+ }
+ port=right
+ {
+ id=60
+ }
+}
diff --git a/arts/examples/example_sequence.arts b/arts/examples/example_sequence.arts
new file mode 100644
index 00000000..2840657e
--- /dev/null
+++ b/arts/examples/example_sequence.arts
@@ -0,0 +1,85 @@
+name=example_sequence
+module=Arts::Synth_SEQUENCE
+{
+ id=0
+ x=3
+ y=1
+ port=speed
+ {
+ id=1
+ audio_data=0.50000
+ }
+ port=seq
+ {
+ id=2
+ string_data=C-4;D-4;E-4;F-4;G-4;A-4;B-4;C-5;
+ }
+ port=frequency
+ {
+ id=3
+ connect_to=11
+ }
+ port=pos
+ {
+ id=4
+ }
+}
+module=Arts::Synth_AMAN_PLAY
+{
+ id=5
+ x=2
+ y=7
+ port=title
+ {
+ id=6
+ string_data=Sequence
+ }
+ port=autoRestoreID
+ {
+ id=7
+ string_data=Sequence
+ }
+ port=left
+ {
+ id=8
+ connect_to=15
+ }
+ port=right
+ {
+ id=9
+ connect_to=15
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=10
+ x=4
+ y=3
+ port=frequency
+ {
+ id=11
+ connect_to=3
+ }
+ port=pos
+ {
+ id=12
+ connect_to=14
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=13
+ x=4
+ y=5
+ port=pos
+ {
+ id=14
+ connect_to=12
+ }
+ port=outvalue
+ {
+ id=15
+ connect_to=8
+ connect_to=9
+ }
+}
diff --git a/arts/examples/example_shelve_cutoff.arts b/arts/examples/example_shelve_cutoff.arts
new file mode 100644
index 00000000..788a6dd3
--- /dev/null
+++ b/arts/examples/example_shelve_cutoff.arts
@@ -0,0 +1,61 @@
+name=example_shelve_cutoff
+module=Arts::Synth_NOISE
+{
+ id=0
+ x=1
+ y=1
+ port=outvalue
+ {
+ id=1
+ connect_to=10
+ connect_to=17
+ }
+}
+module=Arts::Synth_AMAN_PLAY
+{
+ id=2
+ x=1
+ y=5
+ port=title
+ {
+ id=3
+ string_data=Shelve Cutoff
+ }
+ port=autoRestoreID
+ {
+ id=4
+ string_data=Shelve Cutoff
+ }
+ port=left
+ {
+ id=5
+ connect_to=19
+ }
+ port=right
+ {
+ id=6
+ connect_to=19
+ }
+}
+module=Arts::Synth_SHELVE_CUTOFF
+{
+ id=16
+ x=1
+ y=3
+ port=invalue
+ {
+ id=17
+ connect_to=1
+ }
+ port=frequency
+ {
+ id=18
+ audio_data=3000.00000
+ }
+ port=outvalue
+ {
+ id=19
+ connect_to=5
+ connect_to=6
+ }
+}
diff --git a/arts/examples/example_sine.arts b/arts/examples/example_sine.arts
new file mode 100644
index 00000000..5810a23a
--- /dev/null
+++ b/arts/examples/example_sine.arts
@@ -0,0 +1,58 @@
+name=example_sine
+module=Arts::Synth_FREQUENCY
+{
+ id=0
+ x=1
+ y=1
+ port=frequency
+ {
+ id=1
+ audio_data=440.00000
+ }
+ port=pos
+ {
+ id=2
+ connect_to=7
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=6
+ x=2
+ y=3
+ port=pos
+ {
+ id=7
+ connect_to=2
+ }
+ port=outvalue
+ {
+ id=8
+ connect_to=15
+ }
+}
+module=Arts::Synth_AMAN_PLAY
+{
+ id=12
+ x=1
+ y=5
+ port=title
+ {
+ id=13
+ string_data=Sine
+ }
+ port=autoRestoreID
+ {
+ id=14
+ string_data=Sine
+ }
+ port=left
+ {
+ id=15
+ connect_to=8
+ }
+ port=right
+ {
+ id=16
+ }
+}
diff --git a/arts/examples/example_softsaw.arts b/arts/examples/example_softsaw.arts
new file mode 100644
index 00000000..7ab5202c
--- /dev/null
+++ b/arts/examples/example_softsaw.arts
@@ -0,0 +1,61 @@
+name=example_softsaw
+module=Arts::Synth_FREQUENCY
+{
+ id=0
+ x=1
+ y=1
+ port=frequency
+ {
+ id=1
+ audio_data=440.00000
+ }
+ port=pos
+ {
+ id=2
+ connect_to=10
+ connect_to=16
+ }
+}
+module=Arts::Synth_AMAN_PLAY
+{
+ id=3
+ x=1
+ y=5
+ port=title
+ {
+ id=4
+ string_data=SoftSaw
+ }
+ port=autoRestoreID
+ {
+ id=5
+ string_data=SoftSaw
+ }
+ port=left
+ {
+ id=6
+ connect_to=17
+ }
+ port=right
+ {
+ id=7
+ connect_to=17
+ }
+}
+module=Arts::Synth_WAVE_SOFTSAW
+{
+ id=15
+ x=2
+ y=3
+ port=pos
+ {
+ id=16
+ connect_to=2
+ }
+ port=outvalue
+ {
+ id=17
+ connect_to=6
+ connect_to=7
+ }
+}
diff --git a/arts/examples/example_square.arts b/arts/examples/example_square.arts
new file mode 100644
index 00000000..1e0e1d5a
--- /dev/null
+++ b/arts/examples/example_square.arts
@@ -0,0 +1,61 @@
+name=example_square
+module=Arts::Synth_FREQUENCY
+{
+ id=0
+ x=1
+ y=1
+ port=frequency
+ {
+ id=1
+ audio_data=440.00000
+ }
+ port=pos
+ {
+ id=2
+ connect_to=10
+ connect_to=16
+ }
+}
+module=Arts::Synth_AMAN_PLAY
+{
+ id=3
+ x=1
+ y=5
+ port=title
+ {
+ id=4
+ string_data=Square
+ }
+ port=autoRestoreID
+ {
+ id=5
+ string_data=Square
+ }
+ port=left
+ {
+ id=6
+ connect_to=17
+ }
+ port=right
+ {
+ id=7
+ connect_to=17
+ }
+}
+module=Arts::Synth_WAVE_SQUARE
+{
+ id=15
+ x=2
+ y=3
+ port=pos
+ {
+ id=16
+ connect_to=2
+ }
+ port=outvalue
+ {
+ id=17
+ connect_to=6
+ connect_to=7
+ }
+}
diff --git a/arts/examples/example_stereobeep.arts b/arts/examples/example_stereobeep.arts
new file mode 100644
index 00000000..69c346e9
--- /dev/null
+++ b/arts/examples/example_stereobeep.arts
@@ -0,0 +1,91 @@
+name=example_stereobeep
+module=Arts::Synth_FREQUENCY
+{
+ id=29
+ x=1
+ y=1
+ port=frequency
+ {
+ id=30
+ audio_data=440.00000
+ }
+ port=pos
+ {
+ id=31
+ connect_to=36
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=32
+ x=5
+ y=1
+ port=frequency
+ {
+ id=33
+ audio_data=880.00000
+ }
+ port=pos
+ {
+ id=34
+ connect_to=39
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=35
+ x=2
+ y=3
+ port=pos
+ {
+ id=36
+ connect_to=31
+ }
+ port=outvalue
+ {
+ id=37
+ connect_to=44
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=38
+ x=6
+ y=3
+ port=pos
+ {
+ id=39
+ connect_to=34
+ }
+ port=outvalue
+ {
+ id=40
+ connect_to=45
+ }
+}
+module=Arts::Synth_AMAN_PLAY
+{
+ id=41
+ x=2
+ y=5
+ port=title
+ {
+ id=42
+ string_data=StereoBeep
+ }
+ port=autoRestoreID
+ {
+ id=43
+ string_data=StereoBeep
+ }
+ port=left
+ {
+ id=44
+ connect_to=37
+ }
+ port=right
+ {
+ id=45
+ connect_to=40
+ }
+}
diff --git a/arts/examples/example_tremolo.arts b/arts/examples/example_tremolo.arts
new file mode 100644
index 00000000..0a3130f3
--- /dev/null
+++ b/arts/examples/example_tremolo.arts
@@ -0,0 +1,118 @@
+name=example_tremolo
+module=Arts::Synth_BUS_DOWNLINK
+{
+ id=0
+ x=1
+ y=1
+ port=busname
+ {
+ id=1
+ string_data=Tremolo
+ }
+ port=left
+ {
+ id=2
+ connect_to=9
+ }
+ port=right
+ {
+ id=3
+ connect_to=13
+ }
+}
+module=Arts::Synth_BUS_UPLINK
+{
+ id=4
+ x=1
+ y=7
+ port=busname
+ {
+ id=5
+ string_data=out_soundcard
+ }
+ port=left
+ {
+ id=6
+ connect_to=11
+ }
+ port=right
+ {
+ id=7
+ connect_to=15
+ }
+}
+module=Arts::Synth_TREMOLO
+{
+ id=8
+ x=1
+ y=5
+ port=invalue
+ {
+ id=9
+ connect_to=2
+ }
+ port=inlfo
+ {
+ id=10
+ connect_to=21
+ }
+ port=outvalue
+ {
+ id=11
+ connect_to=6
+ }
+}
+module=Arts::Synth_TREMOLO
+{
+ id=12
+ x=5
+ y=5
+ port=invalue
+ {
+ id=13
+ connect_to=3
+ }
+ port=inlfo
+ {
+ id=14
+ connect_to=21
+ }
+ port=outvalue
+ {
+ id=15
+ connect_to=7
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=16
+ x=6
+ y=1
+ port=frequency
+ {
+ id=17
+ audio_data=5.00000
+ }
+ port=pos
+ {
+ id=18
+ connect_to=20
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=19
+ x=6
+ y=3
+ port=pos
+ {
+ id=20
+ connect_to=18
+ }
+ port=outvalue
+ {
+ id=21
+ connect_to=10
+ connect_to=14
+ }
+}
diff --git a/arts/examples/example_tri.arts b/arts/examples/example_tri.arts
new file mode 100644
index 00000000..7c9e5673
--- /dev/null
+++ b/arts/examples/example_tri.arts
@@ -0,0 +1,61 @@
+name=example_tri
+module=Arts::Synth_FREQUENCY
+{
+ id=0
+ x=1
+ y=1
+ port=frequency
+ {
+ id=1
+ audio_data=440.00000
+ }
+ port=pos
+ {
+ id=2
+ connect_to=10
+ connect_to=16
+ }
+}
+module=Arts::Synth_AMAN_PLAY
+{
+ id=3
+ x=1
+ y=5
+ port=title
+ {
+ id=4
+ string_data=Tri
+ }
+ port=autoRestoreID
+ {
+ id=5
+ string_data=Tri
+ }
+ port=left
+ {
+ id=6
+ connect_to=17
+ }
+ port=right
+ {
+ id=7
+ connect_to=17
+ }
+}
+module=Arts::Synth_WAVE_TRI
+{
+ id=15
+ x=2
+ y=3
+ port=pos
+ {
+ id=16
+ connect_to=2
+ }
+ port=outvalue
+ {
+ id=17
+ connect_to=6
+ connect_to=7
+ }
+}
diff --git a/arts/examples/example_xfade.arts b/arts/examples/example_xfade.arts
new file mode 100644
index 00000000..60ed2022
--- /dev/null
+++ b/arts/examples/example_xfade.arts
@@ -0,0 +1,120 @@
+name=example_xfade
+module=Arts::Synth_AMAN_PLAY
+{
+ id=10
+ x=1
+ y=7
+ port=title
+ {
+ id=11
+ string_data=Crossfade
+ }
+ port=autoRestoreID
+ {
+ id=12
+ string_data=Crossfade
+ }
+ port=left
+ {
+ id=13
+ connect_to=48
+ }
+ port=right
+ {
+ id=14
+ connect_to=48
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=15
+ x=1
+ y=3
+ port=pos
+ {
+ id=16
+ connect_to=20
+ }
+ port=outvalue
+ {
+ id=17
+ connect_to=33
+ connect_to=45
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=18
+ x=1
+ y=1
+ port=frequency
+ {
+ id=19
+ audio_data=440.00000
+ }
+ port=pos
+ {
+ id=20
+ connect_to=16
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=25
+ x=5
+ y=3
+ port=pos
+ {
+ id=26
+ connect_to=30
+ }
+ port=outvalue
+ {
+ id=27
+ connect_to=37
+ connect_to=46
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=28
+ x=5
+ y=1
+ port=frequency
+ {
+ id=29
+ audio_data=880.00000
+ }
+ port=pos
+ {
+ id=30
+ connect_to=26
+ }
+}
+module=Arts::Synth_XFADE
+{
+ id=44
+ x=3
+ y=5
+ port=invalue1
+ {
+ id=45
+ connect_to=17
+ }
+ port=invalue2
+ {
+ id=46
+ connect_to=27
+ }
+ port=percentage
+ {
+ id=47
+ audio_data=0.00000
+ }
+ port=outvalue
+ {
+ id=48
+ connect_to=13
+ connect_to=14
+ }
+}
diff --git a/arts/examples/instrument_arts_all.arts-map b/arts/examples/instrument_arts_all.arts-map
new file mode 100644
index 00000000..0508f8d8
--- /dev/null
+++ b/arts/examples/instrument_arts_all.arts-map
@@ -0,0 +1,14 @@
+ON program=0 DO structure=instrument_tri.arts
+ON program=1 DO structure=instrument_organ2.arts
+ON program=2 DO structure=instrument_slide1.arts
+ON program=3 DO structure=instrument_square.arts
+ON program=4 DO structure=instrument_neworgan.arts
+ON program=5 DO structure=instrument_nokind.arts
+ON program=6 DO structure=instrument_full_square.arts
+ON program=7 DO structure=instrument_simple_sin.arts
+ON program=8 DO structure=instrument_simple_square.arts
+ON program=9 DO structure=instrument_simple_tri.arts
+ON program=10 DO structure=instrument_slide.arts
+ON program=11 pitch=60 DO structure=instrument_deepdrum.arts
+ON program=11 pitch=61 DO structure=instrument_chirpdrum.arts
+ON program=11 pitch=62 DO structure=instrument_hihat.arts
diff --git a/arts/examples/instrument_chirpdrum.arts b/arts/examples/instrument_chirpdrum.arts
new file mode 100644
index 00000000..8b8dcff7
--- /dev/null
+++ b/arts/examples/instrument_chirpdrum.arts
@@ -0,0 +1,260 @@
+name=instrument_chirpdrum
+module=Arts::Synth_ENVELOPE_ADSR
+{
+ id=104
+ x=2
+ y=5
+ port=active
+ {
+ id=105
+ connect_to=130
+ }
+ port=invalue
+ {
+ id=106
+ connect_to=116
+ }
+ port=attack
+ {
+ id=107
+ audio_data=0.02000
+ }
+ port=decay
+ {
+ id=108
+ audio_data=0.01000
+ }
+ port=sustain
+ {
+ id=109
+ audio_data=0.00010
+ }
+ port=release
+ {
+ id=110
+ audio_data=0.00100
+ }
+ port=outvalue
+ {
+ id=111
+ connect_to=122
+ }
+ port=done
+ {
+ id=112
+ connect_to=133
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=113
+ x=2
+ y=3
+ port=invalue1
+ {
+ id=114
+ connect_to=118
+ }
+ port=invalue2
+ {
+ id=115
+ connect_to=129
+ }
+ port=outvalue
+ {
+ id=116
+ connect_to=106
+ }
+}
+module=Arts::Synth_NOISE
+{
+ id=117
+ x=1
+ y=2
+ port=outvalue
+ {
+ id=118
+ connect_to=114
+ }
+}
+module=Arts::Synth_MOOG_VCF
+{
+ id=119
+ x=1
+ y=6
+ port=frequency
+ {
+ id=120
+ audio_data=11000.00000
+ }
+ port=resonance
+ {
+ id=121
+ audio_data=1.90000
+ }
+ port=invalue
+ {
+ id=122
+ connect_to=111
+ }
+ port=outvalue
+ {
+ id=123
+ connect_to=126
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=124
+ x=1
+ y=7
+ port=invalue1
+ {
+ id=125
+ audio_data=15.00000
+ }
+ port=invalue2
+ {
+ id=126
+ connect_to=123
+ }
+ port=outvalue
+ {
+ id=127
+ connect_to=142
+ }
+}
+module=Arts::Synth_RC
+{
+ id=139
+ x=1
+ y=10
+ port=b
+ {
+ id=140
+ audio_data=5.00000
+ }
+ port=f
+ {
+ id=141
+ audio_data=5.00000
+ }
+ port=invalue
+ {
+ id=142
+ connect_to=127
+ }
+ port=outvalue
+ {
+ id=143
+ connect_to=132
+ connect_to=131
+ }
+}
+structureport
+{
+ name=frequency
+ x=1
+ y=0
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=128
+ }
+}
+structureport
+{
+ name=velocity
+ x=3
+ y=0
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=129
+ connect_to=115
+ }
+}
+structureport
+{
+ name=pressed
+ x=5
+ y=0
+ position=3
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=130
+ connect_to=105
+ }
+}
+structureport
+{
+ name=left
+ x=2
+ y=13
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=131
+ connect_to=143
+ }
+}
+structureport
+{
+ name=right
+ x=3
+ y=13
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=132
+ connect_to=143
+ }
+}
+structureport
+{
+ name=done
+ x=5
+ y=8
+ position=2
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=133
+ connect_to=112
+ }
+}
diff --git a/arts/examples/instrument_deepdrum.arts b/arts/examples/instrument_deepdrum.arts
new file mode 100644
index 00000000..f6b1ebef
--- /dev/null
+++ b/arts/examples/instrument_deepdrum.arts
@@ -0,0 +1,255 @@
+name=instrument_deepdrum
+module=Arts::Synth_ENVELOPE_ADSR
+{
+ id=144
+ x=2
+ y=5
+ port=active
+ {
+ id=145
+ connect_to=170
+ }
+ port=invalue
+ {
+ id=146
+ connect_to=156
+ }
+ port=attack
+ {
+ id=147
+ audio_data=0.02000
+ }
+ port=decay
+ {
+ id=148
+ audio_data=0.01000
+ }
+ port=sustain
+ {
+ id=149
+ audio_data=0.00100
+ }
+ port=release
+ {
+ id=150
+ audio_data=0.00100
+ }
+ port=outvalue
+ {
+ id=151
+ connect_to=162
+ }
+ port=done
+ {
+ id=152
+ connect_to=173
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=153
+ x=2
+ y=3
+ port=invalue1
+ {
+ id=154
+ connect_to=179
+ }
+ port=invalue2
+ {
+ id=155
+ connect_to=169
+ }
+ port=outvalue
+ {
+ id=156
+ connect_to=146
+ }
+}
+module=Arts::Synth_MOOG_VCF
+{
+ id=159
+ x=1
+ y=6
+ port=frequency
+ {
+ id=160
+ audio_data=400.00000
+ }
+ port=resonance
+ {
+ id=161
+ audio_data=2.90000
+ }
+ port=invalue
+ {
+ id=162
+ connect_to=151
+ }
+ port=outvalue
+ {
+ id=163
+ connect_to=166
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=164
+ x=1
+ y=7
+ port=invalue1
+ {
+ id=165
+ audio_data=150.00000
+ }
+ port=invalue2
+ {
+ id=166
+ connect_to=163
+ }
+ port=outvalue
+ {
+ id=167
+ connect_to=171
+ connect_to=172
+ }
+}
+module=Arts::Synth_WAVE_TRI
+{
+ id=177
+ x=1
+ y=2
+ port=pos
+ {
+ id=178
+ connect_to=185
+ }
+ port=outvalue
+ {
+ id=179
+ connect_to=154
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=183
+ x=0
+ y=1
+ port=frequency
+ {
+ id=184
+ audio_data=8000.00000
+ }
+ port=pos
+ {
+ id=185
+ connect_to=178
+ }
+}
+structureport
+{
+ name=frequency
+ x=1
+ y=0
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=168
+ }
+}
+structureport
+{
+ name=velocity
+ x=3
+ y=0
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=169
+ connect_to=155
+ }
+}
+structureport
+{
+ name=pressed
+ x=5
+ y=0
+ position=3
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=170
+ connect_to=145
+ }
+}
+structureport
+{
+ name=left
+ x=2
+ y=9
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=171
+ connect_to=167
+ }
+}
+structureport
+{
+ name=right
+ x=3
+ y=9
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=172
+ connect_to=167
+ }
+}
+structureport
+{
+ name=done
+ x=5
+ y=8
+ position=2
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=173
+ connect_to=152
+ }
+}
diff --git a/arts/examples/instrument_flexible_slide.arts b/arts/examples/instrument_flexible_slide.arts
new file mode 100644
index 00000000..b1ff28d6
--- /dev/null
+++ b/arts/examples/instrument_flexible_slide.arts
@@ -0,0 +1,537 @@
+name=instrument_flexible_slide
+module=Arts::Synth_FREQUENCY
+{
+ id=4
+ x=4
+ y=3
+ port=frequency
+ {
+ id=5
+ connect_to=79
+ }
+ port=pos
+ {
+ id=6
+ connect_to=32
+ }
+}
+module=Arts::Synth_ENVELOPE_ADSR
+{
+ id=9
+ x=13
+ y=4
+ port=active
+ {
+ id=10
+ connect_to=81
+ }
+ port=invalue
+ {
+ id=11
+ connect_to=29
+ }
+ port=attack
+ {
+ id=12
+ connect_to=78
+ }
+ port=decay
+ {
+ id=13
+ connect_to=76
+ }
+ port=sustain
+ {
+ id=14
+ audio_data=0.70000
+ }
+ port=release
+ {
+ id=15
+ audio_data=0.05000
+ }
+ port=outvalue
+ {
+ id=16
+ connect_to=40
+ }
+ port=done
+ {
+ id=17
+ connect_to=8
+ connect_to=84
+ }
+}
+module=Arts::Synth_ENVELOPE_ADSR
+{
+ id=22
+ x=12
+ y=2
+ port=active
+ {
+ id=23
+ connect_to=81
+ }
+ port=invalue
+ {
+ id=24
+ connect_to=80
+ }
+ port=attack
+ {
+ id=25
+ connect_to=78
+ }
+ port=decay
+ {
+ id=26
+ connect_to=75
+ }
+ port=sustain
+ {
+ id=27
+ audio_data=0.00010
+ }
+ port=release
+ {
+ id=28
+ audio_data=0.10000
+ }
+ port=outvalue
+ {
+ id=29
+ connect_to=11
+ connect_to=43
+ }
+ port=done
+ {
+ id=30
+ }
+}
+module=Arts::Synth_WAVE_TRI
+{
+ id=31
+ x=5
+ y=4
+ port=pos
+ {
+ id=32
+ connect_to=6
+ }
+ port=outvalue
+ {
+ id=33
+ connect_to=58
+ }
+}
+module=Arts::Synth_SHELVE_CUTOFF
+{
+ id=34
+ x=5
+ y=11
+ port=invalue
+ {
+ id=35
+ connect_to=60
+ }
+ port=frequency
+ {
+ id=36
+ connect_to=72
+ }
+ port=outvalue
+ {
+ id=37
+ connect_to=39
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=38
+ x=6
+ y=12
+ port=invalue1
+ {
+ id=39
+ connect_to=37
+ }
+ port=invalue2
+ {
+ id=40
+ connect_to=16
+ }
+ port=outvalue
+ {
+ id=41
+ connect_to=20
+ connect_to=21
+ connect_to=82
+ connect_to=83
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=42
+ x=11
+ y=6
+ port=invalue1
+ {
+ id=43
+ connect_to=29
+ }
+ port=invalue2
+ {
+ id=44
+ connect_to=74
+ }
+ port=outvalue
+ {
+ id=45
+ connect_to=71
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=46
+ x=1
+ y=3
+ port=invalue1
+ {
+ id=47
+ connect_to=79
+ }
+ port=invalue2
+ {
+ id=48
+ connect_to=73
+ }
+ port=outvalue
+ {
+ id=49
+ connect_to=51
+ connect_to=70
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=50
+ x=2
+ y=5
+ port=frequency
+ {
+ id=51
+ connect_to=49
+ }
+ port=pos
+ {
+ id=52
+ connect_to=54
+ }
+}
+module=Arts::Synth_WAVE_TRI
+{
+ id=53
+ x=3
+ y=6
+ port=pos
+ {
+ id=54
+ connect_to=52
+ }
+ port=outvalue
+ {
+ id=55
+ connect_to=57
+ }
+}
+module=Arts::Synth_XFADE
+{
+ id=56
+ x=4
+ y=7
+ port=invalue1
+ {
+ id=57
+ connect_to=55
+ }
+ port=invalue2
+ {
+ id=58
+ connect_to=33
+ }
+ port=percentage
+ {
+ id=59
+ connect_to=68
+ }
+ port=outvalue
+ {
+ id=60
+ connect_to=35
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=61
+ x=7
+ y=3
+ port=invalue1
+ {
+ id=62
+ connect_to=80
+ }
+ port=invalue2
+ {
+ id=63
+ audio_data=2.00000
+ }
+ port=outvalue
+ {
+ id=64
+ connect_to=66
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=65
+ x=8
+ y=4
+ port=invalue1
+ {
+ id=66
+ connect_to=64
+ }
+ port=invalue2
+ {
+ id=67
+ audio_data=-1.00000
+ }
+ port=outvalue
+ {
+ id=68
+ connect_to=59
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=69
+ x=7
+ y=9
+ port=invalue1
+ {
+ id=70
+ connect_to=49
+ }
+ port=invalue2
+ {
+ id=71
+ connect_to=45
+ }
+ port=outvalue
+ {
+ id=72
+ connect_to=36
+ }
+}
+structureport
+{
+ name=detune
+ x=1
+ y=0
+ position=0
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=73
+ connect_to=48
+ }
+}
+structureport
+{
+ name=cutoff
+ x=13
+ y=5
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=74
+ connect_to=44
+ }
+}
+structureport
+{
+ name=decay
+ x=16
+ y=0
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=75
+ connect_to=26
+ }
+}
+structureport
+{
+ name=decay2
+ x=19
+ y=0
+ position=3
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=76
+ connect_to=13
+ }
+}
+structureport
+{
+ name=attack
+ x=17
+ y=0
+ position=5
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=78
+ connect_to=12
+ connect_to=25
+ }
+}
+structureport
+{
+ name=frequency
+ x=3
+ y=0
+ position=6
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=79
+ connect_to=5
+ connect_to=47
+ }
+}
+structureport
+{
+ name=velocity
+ x=5
+ y=0
+ position=7
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=80
+ connect_to=62
+ connect_to=24
+ }
+}
+structureport
+{
+ name=pressed
+ x=7
+ y=0
+ position=8
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=81
+ connect_to=23
+ connect_to=10
+ }
+}
+structureport
+{
+ name=left
+ x=1
+ y=14
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=82
+ connect_to=41
+ }
+}
+structureport
+{
+ name=right
+ x=3
+ y=14
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=83
+ connect_to=41
+ }
+}
+structureport
+{
+ name=done
+ x=5
+ y=14
+ position=2
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=84
+ connect_to=17
+ }
+}
diff --git a/arts/examples/instrument_flexible_slide_GUI.arts b/arts/examples/instrument_flexible_slide_GUI.arts
new file mode 100644
index 00000000..3b0f0bca
--- /dev/null
+++ b/arts/examples/instrument_flexible_slide_GUI.arts
@@ -0,0 +1,481 @@
+name=instrument_flexible_slide_GUI
+module=Arts::Poti
+{
+ id=0
+ x=3
+ y=4
+ port=widgetID
+ {
+ id=1
+ }
+ port=parent
+ {
+ id=2
+ }
+ port=x
+ {
+ id=3
+ any_data=value:000000056c6f6e67000000000400000064
+ }
+ port=y
+ {
+ id=4
+ any_data=value:000000056c6f6e67000000000400000000
+ }
+ port=width
+ {
+ id=5
+ any_data=value:000000056c6f6e6700000000040000000a
+ }
+ port=height
+ {
+ id=6
+ any_data=value:000000056c6f6e6700000000040000000a
+ }
+ port=visible
+ {
+ id=7
+ any_data=value:00000008626f6f6c65616e000000000101
+ }
+ port=text
+ {
+ id=8
+ string_data=DETUNE
+ }
+ port=color
+ {
+ id=9
+ string_data=#ff8000
+ }
+ port=min
+ {
+ id=10
+ audio_data=2.00000
+ }
+ port=max
+ {
+ id=11
+ audio_data=2.03000
+ }
+ port=value
+ {
+ id=12
+ }
+}
+module=Arts::Widget
+{
+ id=13
+ x=1
+ y=2
+ port=widgetID
+ {
+ id=14
+ }
+ port=parent
+ {
+ id=15
+ }
+ port=x
+ {
+ id=16
+ any_data=value:000000056c6f6e67000000000400000000
+ }
+ port=y
+ {
+ id=17
+ any_data=value:000000056c6f6e67000000000400000000
+ }
+ port=width
+ {
+ id=18
+ any_data=value:000000056c6f6e67000000000400000032
+ }
+ port=height
+ {
+ id=19
+ any_data=value:000000056c6f6e67000000000400000032
+ }
+ port=visible
+ {
+ id=20
+ any_data=value:00000008626f6f6c65616e000000000101
+ }
+}
+module=Arts::Poti
+{
+ id=21
+ x=3
+ y=6
+ port=widgetID
+ {
+ id=22
+ }
+ port=parent
+ {
+ id=23
+ }
+ port=x
+ {
+ id=24
+ any_data=value:000000056c6f6e670000000004000000c8
+ }
+ port=y
+ {
+ id=25
+ any_data=value:000000056c6f6e67000000000400000000
+ }
+ port=width
+ {
+ id=26
+ }
+ port=height
+ {
+ id=27
+ }
+ port=visible
+ {
+ id=28
+ any_data=value:00000008626f6f6c65616e000000000101
+ }
+ port=text
+ {
+ id=29
+ string_data=CUTOFF
+ }
+ port=color
+ {
+ id=30
+ string_data=red
+ }
+ port=min
+ {
+ id=31
+ }
+ port=max
+ {
+ id=32
+ }
+ port=value
+ {
+ id=33
+ }
+}
+module=Arts::Poti
+{
+ id=34
+ x=3
+ y=10
+ port=widgetID
+ {
+ id=35
+ }
+ port=parent
+ {
+ id=36
+ }
+ port=x
+ {
+ id=37
+ any_data=value:000000056c6f6e67000000000400000190
+ }
+ port=y
+ {
+ id=38
+ any_data=value:000000056c6f6e67000000000400000000
+ }
+ port=width
+ {
+ id=39
+ }
+ port=height
+ {
+ id=40
+ }
+ port=visible
+ {
+ id=41
+ any_data=value:00000008626f6f6c65616e000000000101
+ }
+ port=text
+ {
+ id=42
+ string_data=DECAY
+ }
+ port=color
+ {
+ id=43
+ string_data=#00A000
+ }
+ port=min
+ {
+ id=44
+ audio_data=0.01000
+ }
+ port=max
+ {
+ id=45
+ audio_data=8.00000
+ }
+ port=value
+ {
+ id=46
+ }
+}
+module=Arts::Poti
+{
+ id=47
+ x=3
+ y=12
+ port=widgetID
+ {
+ id=48
+ }
+ port=parent
+ {
+ id=49
+ }
+ port=x
+ {
+ id=50
+ any_data=value:000000056c6f6e670000000004000001f4
+ }
+ port=y
+ {
+ id=51
+ any_data=value:000000056c6f6e67000000000400000000
+ }
+ port=width
+ {
+ id=52
+ }
+ port=height
+ {
+ id=53
+ }
+ port=visible
+ {
+ id=54
+ any_data=value:00000008626f6f6c65616e000000000101
+ }
+ port=text
+ {
+ id=55
+ string_data=DECAY2
+ }
+ port=color
+ {
+ id=56
+ string_data=darkgrey
+ }
+ port=min
+ {
+ id=57
+ audio_data=0.00010
+ }
+ port=max
+ {
+ id=58
+ audio_data=2.00000
+ }
+ port=value
+ {
+ id=59
+ }
+}
+module=Arts::Poti
+{
+ id=60
+ x=3
+ y=8
+ port=widgetID
+ {
+ id=61
+ }
+ port=parent
+ {
+ id=62
+ }
+ port=x
+ {
+ id=63
+ any_data=value:000000056c6f6e6700000000040000012c
+ }
+ port=y
+ {
+ id=64
+ any_data=value:000000056c6f6e67000000000400000000
+ }
+ port=width
+ {
+ id=65
+ }
+ port=height
+ {
+ id=66
+ }
+ port=visible
+ {
+ id=67
+ any_data=value:00000008626f6f6c65616e000000000101
+ }
+ port=text
+ {
+ id=68
+ string_data=ATTACK
+ }
+ port=color
+ {
+ id=69
+ string_data=#00A000
+ }
+ port=min
+ {
+ id=70
+ audio_data=0.01000
+ }
+ port=max
+ {
+ id=71
+ audio_data=0.90000
+ }
+ port=value
+ {
+ id=72
+ }
+}
+structureport
+{
+ name=parent
+ x=2
+ y=1
+ position=0
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=73
+ }
+}
+structureport
+{
+ name=x
+ x=3
+ y=1
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=property
+ }
+ data
+ {
+ id=74
+ }
+}
+structureport
+{
+ name=y
+ x=4
+ y=1
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=property
+ }
+ data
+ {
+ id=75
+ }
+}
+structureport
+{
+ name=detune
+ x=5
+ y=5
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=76
+ }
+}
+structureport
+{
+ name=cutoff
+ x=5
+ y=7
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=77
+ }
+}
+structureport
+{
+ name=decay
+ x=5
+ y=11
+ position=3
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=78
+ }
+}
+structureport
+{
+ name=decay2
+ x=5
+ y=13
+ position=4
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=79
+ }
+}
+structureport
+{
+ name=attack
+ x=5
+ y=9
+ position=2
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=80
+ }
+}
diff --git a/arts/examples/instrument_fm_horn.arts b/arts/examples/instrument_fm_horn.arts
new file mode 100644
index 00000000..698c7792
--- /dev/null
+++ b/arts/examples/instrument_fm_horn.arts
@@ -0,0 +1,301 @@
+name=instrument_fm_horn
+module=Arts::Synth_FM_SOURCE
+{
+ id=0
+ x=2
+ y=7
+ port=frequency
+ {
+ id=1
+ connect_to=47
+ }
+ port=modulator
+ {
+ id=2
+ connect_to=22
+ }
+ port=modlevel
+ {
+ id=3
+ audio_data=0.30000
+ }
+ port=pos
+ {
+ id=4
+ connect_to=6
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=5
+ x=3
+ y=8
+ port=pos
+ {
+ id=6
+ connect_to=4
+ }
+ port=outvalue
+ {
+ id=7
+ connect_to=16
+ connect_to=33
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=8
+ x=2
+ y=4
+ port=frequency
+ {
+ id=9
+ connect_to=45
+ }
+ port=pos
+ {
+ id=10
+ connect_to=12
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=11
+ x=3
+ y=5
+ port=pos
+ {
+ id=12
+ connect_to=10
+ }
+ port=outvalue
+ {
+ id=13
+ connect_to=19
+ }
+}
+module=Arts::Synth_CDELAY
+{
+ id=14
+ x=7
+ y=8
+ port=time
+ {
+ id=15
+ }
+ port=invalue
+ {
+ id=16
+ connect_to=7
+ }
+ port=outvalue
+ {
+ id=17
+ connect_to=20
+ }
+}
+module=Arts::Synth_XFADE
+{
+ id=18
+ x=7
+ y=6
+ port=invalue1
+ {
+ id=19
+ connect_to=13
+ }
+ port=invalue2
+ {
+ id=20
+ connect_to=17
+ }
+ port=percentage
+ {
+ id=21
+ audio_data=0.40000
+ }
+ port=outvalue
+ {
+ id=22
+ connect_to=2
+ }
+}
+module=Arts::Synth_ENVELOPE_ADSR
+{
+ id=31
+ x=3
+ y=11
+ port=active
+ {
+ id=32
+ connect_to=49
+ }
+ port=invalue
+ {
+ id=33
+ connect_to=7
+ }
+ port=attack
+ {
+ id=34
+ audio_data=0.10000
+ }
+ port=decay
+ {
+ id=35
+ audio_data=0.10000
+ }
+ port=sustain
+ {
+ id=36
+ audio_data=0.70000
+ }
+ port=release
+ {
+ id=37
+ audio_data=0.10000
+ }
+ port=outvalue
+ {
+ id=38
+ connect_to=29
+ connect_to=30
+ connect_to=50
+ connect_to=51
+ }
+ port=done
+ {
+ id=39
+ connect_to=41
+ connect_to=52
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=42
+ x=1
+ y=3
+ port=invalue1
+ {
+ id=43
+ connect_to=47
+ }
+ port=invalue2
+ {
+ id=44
+ audio_data=1.01000
+ }
+ port=outvalue
+ {
+ id=45
+ connect_to=9
+ }
+}
+structureport
+{
+ name=frequency
+ x=1
+ y=0
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=47
+ connect_to=43
+ connect_to=1
+ }
+}
+structureport
+{
+ name=velocity
+ x=3
+ y=0
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=48
+ }
+}
+structureport
+{
+ name=pressed
+ x=5
+ y=0
+ position=3
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=49
+ connect_to=32
+ }
+}
+structureport
+{
+ name=left
+ x=1
+ y=13
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=50
+ connect_to=38
+ }
+}
+structureport
+{
+ name=right
+ x=3
+ y=13
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=51
+ connect_to=38
+ }
+}
+structureport
+{
+ name=done
+ x=5
+ y=13
+ position=2
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=52
+ connect_to=39
+ }
+}
diff --git a/arts/examples/instrument_full_square.arts b/arts/examples/instrument_full_square.arts
new file mode 100644
index 00000000..2e562000
--- /dev/null
+++ b/arts/examples/instrument_full_square.arts
@@ -0,0 +1,422 @@
+name=instrument_full_square
+module=Arts::Synth_FREQUENCY
+{
+ id=0
+ x=1
+ y=2
+ port=frequency
+ {
+ id=1
+ connect_to=59
+ }
+ port=pos
+ {
+ id=2
+ connect_to=13
+ }
+}
+module=Arts::Synth_ENVELOPE_ADSR
+{
+ id=3
+ x=0
+ y=9
+ port=active
+ {
+ id=4
+ connect_to=61
+ }
+ port=invalue
+ {
+ id=5
+ connect_to=58
+ }
+ port=attack
+ {
+ id=6
+ audio_data=0.03000
+ }
+ port=decay
+ {
+ id=7
+ audio_data=0.10000
+ }
+ port=sustain
+ {
+ id=8
+ audio_data=0.60000
+ }
+ port=release
+ {
+ id=9
+ audio_data=0.30000
+ }
+ port=outvalue
+ {
+ id=10
+ connect_to=62
+ connect_to=63
+ }
+ port=done
+ {
+ id=11
+ connect_to=64
+ }
+}
+module=Arts::Synth_WAVE_SQUARE
+{
+ id=12
+ x=1
+ y=4
+ port=pos
+ {
+ id=13
+ connect_to=2
+ }
+ port=outvalue
+ {
+ id=14
+ connect_to=16
+ connect_to=21
+ connect_to=28
+ connect_to=52
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=15
+ x=6
+ y=6
+ port=invalue1
+ {
+ id=16
+ connect_to=14
+ }
+ port=invalue2
+ {
+ id=17
+ connect_to=38
+ }
+ port=outvalue
+ {
+ id=18
+ connect_to=24
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=19
+ x=15
+ y=6
+ port=invalue1
+ {
+ id=20
+ connect_to=26
+ }
+ port=invalue2
+ {
+ id=21
+ connect_to=14
+ }
+ port=outvalue
+ {
+ id=22
+ connect_to=48
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=23
+ x=10
+ y=6
+ port=invalue1
+ {
+ id=24
+ connect_to=18
+ }
+ port=invalue2
+ {
+ id=25
+ connect_to=42
+ }
+ port=outvalue
+ {
+ id=26
+ connect_to=20
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=27
+ x=6
+ y=2
+ port=invalue1
+ {
+ id=28
+ connect_to=14
+ }
+ port=invalue2
+ {
+ id=29
+ audio_data=2.00000
+ }
+ port=outvalue
+ {
+ id=30
+ connect_to=32
+ connect_to=36
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=31
+ x=10
+ y=2
+ port=invalue1
+ {
+ id=32
+ connect_to=30
+ }
+ port=invalue2
+ {
+ id=33
+ audio_data=16.00000
+ }
+ port=outvalue
+ {
+ id=34
+ connect_to=40
+ }
+}
+module=Arts::Synth_SHELVE_CUTOFF
+{
+ id=35
+ x=6
+ y=4
+ port=invalue
+ {
+ id=36
+ connect_to=30
+ }
+ port=frequency
+ {
+ id=37
+ audio_data=6000.00000
+ }
+ port=outvalue
+ {
+ id=38
+ connect_to=17
+ }
+}
+module=Arts::Synth_SHELVE_CUTOFF
+{
+ id=39
+ x=10
+ y=4
+ port=invalue
+ {
+ id=40
+ connect_to=34
+ }
+ port=frequency
+ {
+ id=41
+ audio_data=2000.00000
+ }
+ port=outvalue
+ {
+ id=42
+ connect_to=25
+ }
+}
+module=Arts::Synth_SHELVE_CUTOFF
+{
+ id=43
+ x=12
+ y=9
+ port=invalue
+ {
+ id=44
+ connect_to=54
+ }
+ port=frequency
+ {
+ id=45
+ audio_data=8000.00000
+ }
+ port=outvalue
+ {
+ id=46
+ connect_to=57
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=47
+ x=16
+ y=7
+ port=invalue1
+ {
+ id=48
+ connect_to=22
+ }
+ port=invalue2
+ {
+ id=49
+ audio_data=0.10000
+ }
+ port=outvalue
+ {
+ id=50
+ connect_to=53
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=51
+ x=16
+ y=9
+ port=invalue1
+ {
+ id=52
+ connect_to=14
+ }
+ port=invalue2
+ {
+ id=53
+ connect_to=50
+ }
+ port=outvalue
+ {
+ id=54
+ connect_to=44
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=55
+ x=8
+ y=9
+ port=invalue1
+ {
+ id=56
+ audio_data=0.20000
+ }
+ port=invalue2
+ {
+ id=57
+ connect_to=46
+ }
+ port=outvalue
+ {
+ id=58
+ connect_to=5
+ }
+}
+structureport
+{
+ name=frequency
+ x=2
+ y=0
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=59
+ connect_to=1
+ }
+}
+structureport
+{
+ name=velocity
+ x=4
+ y=0
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=60
+ }
+}
+structureport
+{
+ name=pressed
+ x=1
+ y=8
+ position=3
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=61
+ connect_to=4
+ }
+}
+structureport
+{
+ name=left
+ x=2
+ y=11
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=62
+ connect_to=10
+ }
+}
+structureport
+{
+ name=right
+ x=4
+ y=11
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=63
+ connect_to=10
+ }
+}
+structureport
+{
+ name=done
+ x=6
+ y=11
+ position=2
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=64
+ connect_to=11
+ }
+}
diff --git a/arts/examples/instrument_hihat.arts b/arts/examples/instrument_hihat.arts
new file mode 100644
index 00000000..68205093
--- /dev/null
+++ b/arts/examples/instrument_hihat.arts
@@ -0,0 +1,235 @@
+name=instrument_hihat
+module=Arts::Synth_ENVELOPE_ADSR
+{
+ id=0
+ x=2
+ y=5
+ port=active
+ {
+ id=1
+ connect_to=31
+ }
+ port=invalue
+ {
+ id=2
+ connect_to=12
+ }
+ port=attack
+ {
+ id=3
+ audio_data=0.00010
+ }
+ port=decay
+ {
+ id=4
+ audio_data=0.04000
+ }
+ port=sustain
+ {
+ id=5
+ audio_data=0.00010
+ }
+ port=release
+ {
+ id=6
+ audio_data=0.00100
+ }
+ port=outvalue
+ {
+ id=7
+ connect_to=18
+ connect_to=22
+ }
+ port=done
+ {
+ id=8
+ connect_to=34
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=9
+ x=2
+ y=3
+ port=invalue1
+ {
+ id=10
+ connect_to=14
+ }
+ port=invalue2
+ {
+ id=11
+ connect_to=30
+ }
+ port=outvalue
+ {
+ id=12
+ connect_to=2
+ }
+}
+module=Arts::Synth_NOISE
+{
+ id=13
+ x=1
+ y=2
+ port=outvalue
+ {
+ id=14
+ connect_to=10
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=20
+ x=1
+ y=7
+ port=invalue1
+ {
+ id=21
+ audio_data=2.00000
+ }
+ port=invalue2
+ {
+ id=22
+ connect_to=7
+ }
+ port=outvalue
+ {
+ id=23
+ connect_to=27
+ }
+}
+module=Arts::Synth_RC
+{
+ id=24
+ x=1
+ y=10
+ port=b
+ {
+ id=25
+ audio_data=2.00000
+ }
+ port=f
+ {
+ id=26
+ audio_data=4.00000
+ }
+ port=invalue
+ {
+ id=27
+ connect_to=23
+ }
+ port=outvalue
+ {
+ id=28
+ connect_to=32
+ connect_to=33
+ }
+}
+structureport
+{
+ name=frequency
+ x=1
+ y=0
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=29
+ }
+}
+structureport
+{
+ name=velocity
+ x=3
+ y=0
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=30
+ connect_to=11
+ }
+}
+structureport
+{
+ name=pressed
+ x=5
+ y=0
+ position=3
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=31
+ connect_to=1
+ }
+}
+structureport
+{
+ name=left
+ x=2
+ y=13
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=32
+ connect_to=28
+ }
+}
+structureport
+{
+ name=right
+ x=3
+ y=13
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=33
+ connect_to=28
+ }
+}
+structureport
+{
+ name=done
+ x=5
+ y=8
+ position=2
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=34
+ connect_to=8
+ }
+}
diff --git a/arts/examples/instrument_moog_vcf_tune.arts b/arts/examples/instrument_moog_vcf_tune.arts
new file mode 100644
index 00000000..51d057a7
--- /dev/null
+++ b/arts/examples/instrument_moog_vcf_tune.arts
@@ -0,0 +1,341 @@
+name=instrument_moog_vcf_tune
+module=Arts::Synth_FREQUENCY
+{
+ id=4
+ x=0
+ y=5
+ port=frequency
+ {
+ id=5
+ connect_to=48
+ }
+ port=pos
+ {
+ id=6
+ connect_to=23
+ }
+}
+module=Arts::Synth_ENVELOPE_ADSR
+{
+ id=7
+ x=5
+ y=13
+ port=active
+ {
+ id=8
+ connect_to=50
+ }
+ port=invalue
+ {
+ id=9
+ connect_to=43
+ }
+ port=attack
+ {
+ id=10
+ audio_data=0.03000
+ }
+ port=decay
+ {
+ id=11
+ audio_data=0.10000
+ }
+ port=sustain
+ {
+ id=12
+ audio_data=0.60000
+ }
+ port=release
+ {
+ id=13
+ audio_data=0.20000
+ }
+ port=outvalue
+ {
+ id=14
+ connect_to=20
+ connect_to=21
+ connect_to=51
+ connect_to=52
+ }
+ port=done
+ {
+ id=15
+ connect_to=17
+ connect_to=53
+ }
+}
+module=Arts::Synth_WAVE_TRI
+{
+ id=22
+ x=1
+ y=6
+ port=pos
+ {
+ id=23
+ connect_to=6
+ }
+ port=outvalue
+ {
+ id=24
+ connect_to=32
+ }
+}
+module=Arts::Synth_WAVE_TRI
+{
+ id=25
+ x=5
+ y=7
+ port=pos
+ {
+ id=26
+ connect_to=30
+ }
+ port=outvalue
+ {
+ id=27
+ connect_to=33
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=28
+ x=4
+ y=6
+ port=frequency
+ {
+ id=29
+ connect_to=38
+ }
+ port=pos
+ {
+ id=30
+ connect_to=26
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=31
+ x=3
+ y=9
+ port=invalue1
+ {
+ id=32
+ connect_to=24
+ }
+ port=invalue2
+ {
+ id=33
+ connect_to=27
+ }
+ port=outvalue
+ {
+ id=34
+ connect_to=42
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=35
+ x=3
+ y=5
+ port=invalue1
+ {
+ id=36
+ connect_to=48
+ }
+ port=invalue2
+ {
+ id=37
+ connect_to=47
+ }
+ port=outvalue
+ {
+ id=38
+ connect_to=29
+ }
+}
+module=Arts::Synth_MOOG_VCF
+{
+ id=39
+ x=7
+ y=11
+ port=frequency
+ {
+ id=40
+ }
+ port=resonance
+ {
+ id=41
+ }
+ port=invalue
+ {
+ id=42
+ connect_to=34
+ }
+ port=outvalue
+ {
+ id=43
+ connect_to=9
+ }
+}
+structureport
+{
+ name=cutoff
+ x=9
+ y=10
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=45
+ }
+}
+structureport
+{
+ name=resonance
+ x=10
+ y=10
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=46
+ }
+}
+structureport
+{
+ name=detune
+ x=5
+ y=4
+ position=3
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=47
+ connect_to=37
+ }
+}
+structureport
+{
+ name=frequency
+ x=1
+ y=0
+ position=4
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=48
+ connect_to=5
+ connect_to=36
+ }
+}
+structureport
+{
+ name=velocity
+ x=3
+ y=0
+ position=5
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=49
+ }
+}
+structureport
+{
+ name=pressed
+ x=5
+ y=0
+ position=6
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=50
+ connect_to=8
+ }
+}
+structureport
+{
+ name=left
+ x=1
+ y=15
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=51
+ connect_to=14
+ }
+}
+structureport
+{
+ name=right
+ x=3
+ y=15
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=52
+ connect_to=14
+ }
+}
+structureport
+{
+ name=done
+ x=5
+ y=15
+ position=2
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=53
+ connect_to=15
+ }
+}
diff --git a/arts/examples/instrument_moog_vcf_tune_GUI.arts b/arts/examples/instrument_moog_vcf_tune_GUI.arts
new file mode 100644
index 00000000..6db62f60
--- /dev/null
+++ b/arts/examples/instrument_moog_vcf_tune_GUI.arts
@@ -0,0 +1,331 @@
+name=instrument_moog_vcf_tune_GUI
+module=Arts::Widget
+{
+ id=0
+ x=1
+ y=2
+ port=widgetID
+ {
+ id=1
+ }
+ port=parent
+ {
+ id=2
+ }
+ port=x
+ {
+ id=3
+ any_data=value:000000056c6f6e67000000000400000000
+ }
+ port=y
+ {
+ id=4
+ any_data=value:000000056c6f6e67000000000400000000
+ }
+ port=width
+ {
+ id=5
+ any_data=value:000000056c6f6e6700000000040000000a
+ }
+ port=height
+ {
+ id=6
+ any_data=value:000000056c6f6e6700000000040000000a
+ }
+ port=visible
+ {
+ id=7
+ any_data=value:00000008626f6f6c65616e000000000101
+ }
+}
+module=Arts::Poti
+{
+ id=8
+ x=3
+ y=4
+ port=widgetID
+ {
+ id=9
+ }
+ port=parent
+ {
+ id=10
+ }
+ port=x
+ {
+ id=11
+ any_data=value:000000056c6f6e67000000000400000064
+ }
+ port=y
+ {
+ id=12
+ any_data=value:000000056c6f6e67000000000400000000
+ }
+ port=width
+ {
+ id=13
+ any_data=value:000000056c6f6e6700000000040000000a
+ }
+ port=height
+ {
+ id=14
+ any_data=value:000000056c6f6e6700000000040000000a
+ }
+ port=visible
+ {
+ id=15
+ any_data=value:00000008626f6f6c65616e000000000101
+ }
+ port=text
+ {
+ id=16
+ string_data= CUTOFF
+ }
+ port=color
+ {
+ id=17
+ string_data=red
+ }
+ port=min
+ {
+ id=18
+ audio_data=100.00000
+ }
+ port=max
+ {
+ id=19
+ audio_data=8000.00000
+ }
+ port=value
+ {
+ id=20
+ }
+}
+module=Arts::Poti
+{
+ id=21
+ x=3
+ y=7
+ port=widgetID
+ {
+ id=22
+ }
+ port=parent
+ {
+ id=23
+ }
+ port=x
+ {
+ id=24
+ any_data=value:000000056c6f6e670000000004000000c8
+ }
+ port=y
+ {
+ id=25
+ any_data=value:000000056c6f6e67000000000400000000
+ }
+ port=width
+ {
+ id=26
+ any_data=value:000000056c6f6e6700000000040000000a
+ }
+ port=height
+ {
+ id=27
+ any_data=value:000000056c6f6e6700000000040000000a
+ }
+ port=visible
+ {
+ id=28
+ any_data=value:00000008626f6f6c65616e000000000101
+ }
+ port=text
+ {
+ id=29
+ string_data= RESO
+ }
+ port=color
+ {
+ id=30
+ string_data=red
+ }
+ port=min
+ {
+ id=31
+ audio_data=0.00000
+ }
+ port=max
+ {
+ id=32
+ audio_data=3.95000
+ }
+ port=value
+ {
+ id=33
+ }
+}
+module=Arts::Poti
+{
+ id=34
+ x=3
+ y=10
+ port=widgetID
+ {
+ id=35
+ }
+ port=parent
+ {
+ id=36
+ }
+ port=x
+ {
+ id=37
+ any_data=value:000000056c6f6e6700000000040000012c
+ }
+ port=y
+ {
+ id=38
+ any_data=value:000000056c6f6e67000000000400000000
+ }
+ port=width
+ {
+ id=39
+ any_data=value:000000056c6f6e6700000000040000000a
+ }
+ port=height
+ {
+ id=40
+ any_data=value:000000056c6f6e6700000000040000000a
+ }
+ port=visible
+ {
+ id=41
+ any_data=value:00000008626f6f6c65616e000000000101
+ }
+ port=text
+ {
+ id=42
+ string_data=DETUNE
+ }
+ port=color
+ {
+ id=43
+ string_data=orange
+ }
+ port=min
+ {
+ id=44
+ audio_data=1.00000
+ }
+ port=max
+ {
+ id=45
+ audio_data=1.02000
+ }
+ port=value
+ {
+ id=46
+ }
+}
+structureport
+{
+ name=parent
+ x=2
+ y=1
+ position=0
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=47
+ }
+}
+structureport
+{
+ name=x
+ x=3
+ y=1
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=property
+ }
+ data
+ {
+ id=48
+ }
+}
+structureport
+{
+ name=y
+ x=4
+ y=1
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=property
+ }
+ data
+ {
+ id=49
+ }
+}
+structureport
+{
+ name=cutoff
+ x=5
+ y=5
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=50
+ }
+}
+structureport
+{
+ name=resonance
+ x=5
+ y=8
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=51
+ }
+}
+structureport
+{
+ name=detune
+ x=5
+ y=11
+ position=2
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=52
+ }
+}
diff --git a/arts/examples/instrument_neworgan.arts b/arts/examples/instrument_neworgan.arts
new file mode 100644
index 00000000..7bdbc316
--- /dev/null
+++ b/arts/examples/instrument_neworgan.arts
@@ -0,0 +1,532 @@
+name=instrument_neworgan
+module=Arts::Synth_FREQUENCY
+{
+ id=4
+ x=1
+ y=4
+ port=frequency
+ {
+ id=5
+ connect_to=93
+ }
+ port=pos
+ {
+ id=6
+ connect_to=23
+ }
+}
+module=Arts::Synth_ENVELOPE_ADSR
+{
+ id=7
+ x=3
+ y=14
+ port=active
+ {
+ id=8
+ connect_to=95
+ }
+ port=invalue
+ {
+ id=9
+ connect_to=70
+ }
+ port=attack
+ {
+ id=10
+ audio_data=0.03000
+ }
+ port=decay
+ {
+ id=11
+ audio_data=0.10000
+ }
+ port=sustain
+ {
+ id=12
+ audio_data=0.60000
+ }
+ port=release
+ {
+ id=13
+ audio_data=0.30000
+ }
+ port=outvalue
+ {
+ id=14
+ connect_to=20
+ connect_to=21
+ connect_to=90
+ connect_to=91
+ }
+ port=done
+ {
+ id=15
+ connect_to=17
+ connect_to=92
+ }
+}
+module=Arts::Synth_WAVE_SQUARE
+{
+ id=22
+ x=1
+ y=6
+ port=pos
+ {
+ id=23
+ connect_to=6
+ }
+ port=outvalue
+ {
+ id=24
+ connect_to=64
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=25
+ x=5
+ y=2
+ port=invalue1
+ {
+ id=26
+ connect_to=93
+ }
+ port=invalue2
+ {
+ id=27
+ audio_data=1.00000
+ }
+ port=outvalue
+ {
+ id=28
+ connect_to=47
+ }
+}
+module=Arts::Synth_WAVE_SQUARE
+{
+ id=29
+ x=5
+ y=6
+ port=pos
+ {
+ id=30
+ connect_to=48
+ }
+ port=outvalue
+ {
+ id=31
+ connect_to=65
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=32
+ x=9
+ y=2
+ port=invalue1
+ {
+ id=33
+ connect_to=93
+ }
+ port=invalue2
+ {
+ id=34
+ audio_data=8.00000
+ }
+ port=outvalue
+ {
+ id=35
+ connect_to=50
+ }
+}
+module=Arts::Synth_WAVE_SQUARE
+{
+ id=36
+ x=9
+ y=6
+ port=pos
+ {
+ id=37
+ connect_to=51
+ }
+ port=outvalue
+ {
+ id=38
+ connect_to=57
+ }
+}
+module=Arts::Synth_WAVE_SQUARE
+{
+ id=39
+ x=13
+ y=6
+ port=pos
+ {
+ id=40
+ connect_to=54
+ }
+ port=outvalue
+ {
+ id=41
+ connect_to=61
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=42
+ x=13
+ y=2
+ port=invalue1
+ {
+ id=43
+ connect_to=93
+ }
+ port=invalue2
+ {
+ id=44
+ audio_data=2.00000
+ }
+ port=outvalue
+ {
+ id=45
+ connect_to=53
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=46
+ x=5
+ y=4
+ port=frequency
+ {
+ id=47
+ connect_to=28
+ }
+ port=pos
+ {
+ id=48
+ connect_to=30
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=49
+ x=9
+ y=4
+ port=frequency
+ {
+ id=50
+ connect_to=35
+ }
+ port=pos
+ {
+ id=51
+ connect_to=37
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=52
+ x=13
+ y=4
+ port=frequency
+ {
+ id=53
+ connect_to=45
+ }
+ port=pos
+ {
+ id=54
+ connect_to=40
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=55
+ x=5
+ y=10
+ port=invalue1
+ {
+ id=56
+ connect_to=66
+ }
+ port=invalue2
+ {
+ id=57
+ connect_to=38
+ }
+ port=outvalue
+ {
+ id=58
+ connect_to=60
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=59
+ x=9
+ y=11
+ port=invalue1
+ {
+ id=60
+ connect_to=58
+ }
+ port=invalue2
+ {
+ id=61
+ connect_to=41
+ }
+ port=outvalue
+ {
+ id=62
+ connect_to=86
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=63
+ x=1
+ y=9
+ port=invalue1
+ {
+ id=64
+ connect_to=24
+ }
+ port=invalue2
+ {
+ id=65
+ connect_to=31
+ }
+ port=outvalue
+ {
+ id=66
+ connect_to=56
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=67
+ x=13
+ y=13
+ port=invalue1
+ {
+ id=68
+ connect_to=74
+ }
+ port=invalue2
+ {
+ id=69
+ audio_data=0.20000
+ }
+ port=outvalue
+ {
+ id=70
+ connect_to=9
+ }
+}
+module=Arts::Synth_SHELVE_CUTOFF
+{
+ id=71
+ x=13
+ y=11
+ port=invalue
+ {
+ id=72
+ connect_to=88
+ }
+ port=frequency
+ {
+ id=73
+ audio_data=5000.00000
+ }
+ port=outvalue
+ {
+ id=74
+ connect_to=68
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=75
+ x=17
+ y=2
+ port=invalue1
+ {
+ id=76
+ connect_to=93
+ }
+ port=invalue2
+ {
+ id=77
+ audio_data=4.00000
+ }
+ port=outvalue
+ {
+ id=78
+ connect_to=80
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=79
+ x=17
+ y=4
+ port=frequency
+ {
+ id=80
+ connect_to=78
+ }
+ port=pos
+ {
+ id=81
+ connect_to=83
+ }
+}
+module=Arts::Synth_WAVE_SQUARE
+{
+ id=82
+ x=17
+ y=6
+ port=pos
+ {
+ id=83
+ connect_to=81
+ }
+ port=outvalue
+ {
+ id=84
+ connect_to=87
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=85
+ x=17
+ y=9
+ port=invalue1
+ {
+ id=86
+ connect_to=62
+ }
+ port=invalue2
+ {
+ id=87
+ connect_to=84
+ }
+ port=outvalue
+ {
+ id=88
+ connect_to=72
+ }
+}
+structureport
+{
+ name=left
+ x=1
+ y=16
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=90
+ connect_to=14
+ }
+}
+structureport
+{
+ name=right
+ x=3
+ y=16
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=91
+ connect_to=14
+ }
+}
+structureport
+{
+ name=done
+ x=5
+ y=16
+ position=2
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=92
+ connect_to=15
+ }
+}
+structureport
+{
+ name=frequency
+ x=2
+ y=0
+ position=0
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=93
+ connect_to=5
+ connect_to=26
+ connect_to=33
+ connect_to=43
+ connect_to=76
+ }
+}
+structureport
+{
+ name=velocity
+ x=4
+ y=0
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=94
+ }
+}
+structureport
+{
+ name=pressed
+ x=6
+ y=0
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=95
+ connect_to=8
+ }
+}
diff --git a/arts/examples/instrument_nokind.arts b/arts/examples/instrument_nokind.arts
new file mode 100644
index 00000000..d0c78585
--- /dev/null
+++ b/arts/examples/instrument_nokind.arts
@@ -0,0 +1,503 @@
+name=instrument_nokind
+module=Arts::Synth_FREQUENCY
+{
+ id=0
+ x=1
+ y=2
+ port=frequency
+ {
+ id=1
+ connect_to=77
+ }
+ port=pos
+ {
+ id=2
+ connect_to=13
+ connect_to=64
+ }
+}
+module=Arts::Synth_ENVELOPE_ADSR
+{
+ id=3
+ x=3
+ y=9
+ port=active
+ {
+ id=4
+ connect_to=79
+ }
+ port=invalue
+ {
+ id=5
+ connect_to=73
+ }
+ port=attack
+ {
+ id=6
+ audio_data=0.03000
+ }
+ port=decay
+ {
+ id=7
+ audio_data=0.10000
+ }
+ port=sustain
+ {
+ id=8
+ audio_data=0.60000
+ }
+ port=release
+ {
+ id=9
+ audio_data=0.30000
+ }
+ port=outvalue
+ {
+ id=10
+ connect_to=74
+ connect_to=75
+ }
+ port=done
+ {
+ id=11
+ connect_to=76
+ }
+}
+module=Arts::Synth_WAVE_SQUARE
+{
+ id=12
+ x=1
+ y=4
+ port=pos
+ {
+ id=13
+ connect_to=2
+ }
+ port=outvalue
+ {
+ id=14
+ connect_to=16
+ connect_to=21
+ connect_to=28
+ connect_to=52
+ connect_to=56
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=15
+ x=6
+ y=6
+ port=invalue1
+ {
+ id=16
+ connect_to=14
+ }
+ port=invalue2
+ {
+ id=17
+ connect_to=38
+ }
+ port=outvalue
+ {
+ id=18
+ connect_to=24
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=19
+ x=14
+ y=6
+ port=invalue1
+ {
+ id=20
+ connect_to=26
+ }
+ port=invalue2
+ {
+ id=21
+ connect_to=14
+ }
+ port=outvalue
+ {
+ id=22
+ connect_to=48
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=23
+ x=10
+ y=6
+ port=invalue1
+ {
+ id=24
+ connect_to=18
+ }
+ port=invalue2
+ {
+ id=25
+ connect_to=42
+ }
+ port=outvalue
+ {
+ id=26
+ connect_to=20
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=27
+ x=6
+ y=2
+ port=invalue1
+ {
+ id=28
+ connect_to=14
+ }
+ port=invalue2
+ {
+ id=29
+ audio_data=2.00000
+ }
+ port=outvalue
+ {
+ id=30
+ connect_to=32
+ connect_to=36
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=31
+ x=10
+ y=2
+ port=invalue1
+ {
+ id=32
+ connect_to=30
+ }
+ port=invalue2
+ {
+ id=33
+ audio_data=16.00000
+ }
+ port=outvalue
+ {
+ id=34
+ connect_to=40
+ }
+}
+module=Arts::Synth_SHELVE_CUTOFF
+{
+ id=35
+ x=6
+ y=4
+ port=invalue
+ {
+ id=36
+ connect_to=30
+ }
+ port=frequency
+ {
+ id=37
+ audio_data=6000.00000
+ }
+ port=outvalue
+ {
+ id=38
+ connect_to=17
+ }
+}
+module=Arts::Synth_SHELVE_CUTOFF
+{
+ id=39
+ x=10
+ y=4
+ port=invalue
+ {
+ id=40
+ connect_to=34
+ }
+ port=frequency
+ {
+ id=41
+ audio_data=7000.00000
+ }
+ port=outvalue
+ {
+ id=42
+ connect_to=25
+ }
+}
+module=Arts::Synth_SHELVE_CUTOFF
+{
+ id=43
+ x=15
+ y=11
+ port=invalue
+ {
+ id=44
+ connect_to=54
+ }
+ port=frequency
+ {
+ id=45
+ audio_data=8000.00000
+ }
+ port=outvalue
+ {
+ id=46
+ connect_to=60
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=47
+ x=15
+ y=7
+ port=invalue1
+ {
+ id=48
+ connect_to=22
+ }
+ port=invalue2
+ {
+ id=49
+ audio_data=0.10000
+ }
+ port=outvalue
+ {
+ id=50
+ connect_to=53
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=51
+ x=15
+ y=9
+ port=invalue1
+ {
+ id=52
+ connect_to=14
+ }
+ port=invalue2
+ {
+ id=53
+ connect_to=50
+ }
+ port=outvalue
+ {
+ id=54
+ connect_to=44
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=55
+ x=11
+ y=13
+ port=invalue1
+ {
+ id=56
+ connect_to=14
+ }
+ port=invalue2
+ {
+ id=57
+ connect_to=62
+ }
+ port=outvalue
+ {
+ id=58
+ connect_to=68
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=59
+ x=15
+ y=13
+ port=invalue1
+ {
+ id=60
+ connect_to=46
+ }
+ port=invalue2
+ {
+ id=61
+ audio_data=0.50000
+ }
+ port=outvalue
+ {
+ id=62
+ connect_to=57
+ }
+}
+module=Arts::Synth_WAVE_TRI
+{
+ id=63
+ x=1
+ y=7
+ port=pos
+ {
+ id=64
+ connect_to=2
+ }
+ port=outvalue
+ {
+ id=65
+ connect_to=67
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=66
+ x=11
+ y=11
+ port=invalue1
+ {
+ id=67
+ connect_to=65
+ }
+ port=invalue2
+ {
+ id=68
+ connect_to=58
+ }
+ port=outvalue
+ {
+ id=69
+ connect_to=71
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=70
+ x=11
+ y=9
+ port=invalue1
+ {
+ id=71
+ connect_to=69
+ }
+ port=invalue2
+ {
+ id=72
+ audio_data=0.25000
+ }
+ port=outvalue
+ {
+ id=73
+ connect_to=5
+ }
+}
+structureport
+{
+ name=left
+ x=1
+ y=13
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=74
+ connect_to=10
+ }
+}
+structureport
+{
+ name=right
+ x=3
+ y=13
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=75
+ connect_to=10
+ }
+}
+structureport
+{
+ name=done
+ x=5
+ y=13
+ position=2
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=76
+ connect_to=11
+ }
+}
+structureport
+{
+ name=frequency
+ x=2
+ y=0
+ position=0
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=77
+ connect_to=1
+ }
+}
+structureport
+{
+ name=velocity
+ x=4
+ y=0
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=78
+ }
+}
+structureport
+{
+ name=pressed
+ x=6
+ y=0
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=79
+ connect_to=4
+ }
+}
diff --git a/arts/examples/instrument_organ2.arts b/arts/examples/instrument_organ2.arts
new file mode 100644
index 00000000..85fb1271
--- /dev/null
+++ b/arts/examples/instrument_organ2.arts
@@ -0,0 +1,532 @@
+name=instrument_organ2
+module=Arts::Synth_FREQUENCY
+{
+ id=4
+ x=1
+ y=4
+ port=frequency
+ {
+ id=5
+ connect_to=93
+ }
+ port=pos
+ {
+ id=6
+ connect_to=23
+ }
+}
+module=Arts::Synth_ENVELOPE_ADSR
+{
+ id=7
+ x=3
+ y=14
+ port=active
+ {
+ id=8
+ connect_to=95
+ }
+ port=invalue
+ {
+ id=9
+ connect_to=70
+ }
+ port=attack
+ {
+ id=10
+ audio_data=0.03000
+ }
+ port=decay
+ {
+ id=11
+ audio_data=0.10000
+ }
+ port=sustain
+ {
+ id=12
+ audio_data=0.60000
+ }
+ port=release
+ {
+ id=13
+ audio_data=0.30000
+ }
+ port=outvalue
+ {
+ id=14
+ connect_to=20
+ connect_to=21
+ connect_to=90
+ connect_to=91
+ }
+ port=done
+ {
+ id=15
+ connect_to=17
+ connect_to=92
+ }
+}
+module=Arts::Synth_WAVE_SQUARE
+{
+ id=22
+ x=1
+ y=6
+ port=pos
+ {
+ id=23
+ connect_to=6
+ }
+ port=outvalue
+ {
+ id=24
+ connect_to=64
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=25
+ x=5
+ y=2
+ port=invalue1
+ {
+ id=26
+ connect_to=93
+ }
+ port=invalue2
+ {
+ id=27
+ audio_data=1.00000
+ }
+ port=outvalue
+ {
+ id=28
+ connect_to=47
+ }
+}
+module=Arts::Synth_WAVE_SQUARE
+{
+ id=29
+ x=5
+ y=6
+ port=pos
+ {
+ id=30
+ connect_to=48
+ }
+ port=outvalue
+ {
+ id=31
+ connect_to=65
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=32
+ x=9
+ y=2
+ port=invalue1
+ {
+ id=33
+ connect_to=93
+ }
+ port=invalue2
+ {
+ id=34
+ audio_data=8.00000
+ }
+ port=outvalue
+ {
+ id=35
+ connect_to=50
+ }
+}
+module=Arts::Synth_WAVE_SQUARE
+{
+ id=36
+ x=9
+ y=6
+ port=pos
+ {
+ id=37
+ connect_to=51
+ }
+ port=outvalue
+ {
+ id=38
+ connect_to=57
+ }
+}
+module=Arts::Synth_WAVE_SQUARE
+{
+ id=39
+ x=13
+ y=6
+ port=pos
+ {
+ id=40
+ connect_to=54
+ }
+ port=outvalue
+ {
+ id=41
+ connect_to=61
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=42
+ x=13
+ y=2
+ port=invalue1
+ {
+ id=43
+ connect_to=93
+ }
+ port=invalue2
+ {
+ id=44
+ audio_data=2.00000
+ }
+ port=outvalue
+ {
+ id=45
+ connect_to=53
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=46
+ x=5
+ y=4
+ port=frequency
+ {
+ id=47
+ connect_to=28
+ }
+ port=pos
+ {
+ id=48
+ connect_to=30
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=49
+ x=9
+ y=4
+ port=frequency
+ {
+ id=50
+ connect_to=35
+ }
+ port=pos
+ {
+ id=51
+ connect_to=37
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=52
+ x=13
+ y=4
+ port=frequency
+ {
+ id=53
+ connect_to=45
+ }
+ port=pos
+ {
+ id=54
+ connect_to=40
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=55
+ x=5
+ y=10
+ port=invalue1
+ {
+ id=56
+ connect_to=66
+ }
+ port=invalue2
+ {
+ id=57
+ connect_to=38
+ }
+ port=outvalue
+ {
+ id=58
+ connect_to=60
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=59
+ x=9
+ y=11
+ port=invalue1
+ {
+ id=60
+ connect_to=58
+ }
+ port=invalue2
+ {
+ id=61
+ connect_to=41
+ }
+ port=outvalue
+ {
+ id=62
+ connect_to=86
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=63
+ x=1
+ y=9
+ port=invalue1
+ {
+ id=64
+ connect_to=24
+ }
+ port=invalue2
+ {
+ id=65
+ connect_to=31
+ }
+ port=outvalue
+ {
+ id=66
+ connect_to=56
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=67
+ x=13
+ y=13
+ port=invalue1
+ {
+ id=68
+ connect_to=74
+ }
+ port=invalue2
+ {
+ id=69
+ audio_data=0.20000
+ }
+ port=outvalue
+ {
+ id=70
+ connect_to=9
+ }
+}
+module=Arts::Synth_SHELVE_CUTOFF
+{
+ id=71
+ x=13
+ y=11
+ port=invalue
+ {
+ id=72
+ connect_to=88
+ }
+ port=frequency
+ {
+ id=73
+ audio_data=5000.00000
+ }
+ port=outvalue
+ {
+ id=74
+ connect_to=68
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=75
+ x=17
+ y=2
+ port=invalue1
+ {
+ id=76
+ connect_to=93
+ }
+ port=invalue2
+ {
+ id=77
+ audio_data=4.00000
+ }
+ port=outvalue
+ {
+ id=78
+ connect_to=80
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=79
+ x=17
+ y=4
+ port=frequency
+ {
+ id=80
+ connect_to=78
+ }
+ port=pos
+ {
+ id=81
+ connect_to=83
+ }
+}
+module=Arts::Synth_WAVE_SQUARE
+{
+ id=82
+ x=17
+ y=6
+ port=pos
+ {
+ id=83
+ connect_to=81
+ }
+ port=outvalue
+ {
+ id=84
+ connect_to=87
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=85
+ x=17
+ y=9
+ port=invalue1
+ {
+ id=86
+ connect_to=62
+ }
+ port=invalue2
+ {
+ id=87
+ connect_to=84
+ }
+ port=outvalue
+ {
+ id=88
+ connect_to=72
+ }
+}
+structureport
+{
+ name=left
+ x=1
+ y=16
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=90
+ connect_to=14
+ }
+}
+structureport
+{
+ name=right
+ x=3
+ y=16
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=91
+ connect_to=14
+ }
+}
+structureport
+{
+ name=done
+ x=5
+ y=16
+ position=2
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=92
+ connect_to=15
+ }
+}
+structureport
+{
+ name=frequency
+ x=1
+ y=0
+ position=0
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=93
+ connect_to=5
+ connect_to=26
+ connect_to=33
+ connect_to=43
+ connect_to=76
+ }
+}
+structureport
+{
+ name=velocity
+ x=3
+ y=0
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=94
+ }
+}
+structureport
+{
+ name=pressed
+ x=5
+ y=0
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=95
+ connect_to=8
+ }
+}
diff --git a/arts/examples/instrument_simple_sin.arts b/arts/examples/instrument_simple_sin.arts
new file mode 100644
index 00000000..e2725a8f
--- /dev/null
+++ b/arts/examples/instrument_simple_sin.arts
@@ -0,0 +1,190 @@
+name=instrument_simple_sin
+module=Arts::Synth_FREQUENCY
+{
+ id=4
+ x=1
+ y=2
+ port=frequency
+ {
+ id=5
+ connect_to=29
+ }
+ port=pos
+ {
+ id=6
+ connect_to=8
+ }
+}
+module=Arts::Synth_WAVE_SIN
+{
+ id=7
+ x=2
+ y=3
+ port=pos
+ {
+ id=8
+ connect_to=6
+ }
+ port=outvalue
+ {
+ id=9
+ connect_to=12
+ }
+}
+module=Arts::Synth_ENVELOPE_ADSR
+{
+ id=10
+ x=2
+ y=5
+ port=active
+ {
+ id=11
+ connect_to=31
+ }
+ port=invalue
+ {
+ id=12
+ connect_to=9
+ }
+ port=attack
+ {
+ id=13
+ audio_data=0.03000
+ }
+ port=decay
+ {
+ id=14
+ audio_data=0.10000
+ }
+ port=sustain
+ {
+ id=15
+ audio_data=0.60000
+ }
+ port=release
+ {
+ id=16
+ audio_data=0.20000
+ }
+ port=outvalue
+ {
+ id=17
+ connect_to=23
+ connect_to=24
+ connect_to=26
+ connect_to=27
+ }
+ port=done
+ {
+ id=18
+ connect_to=20
+ connect_to=28
+ }
+}
+structureport
+{
+ name=left
+ x=1
+ y=7
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=26
+ connect_to=17
+ }
+}
+structureport
+{
+ name=right
+ x=3
+ y=7
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=27
+ connect_to=17
+ }
+}
+structureport
+{
+ name=done
+ x=5
+ y=7
+ position=2
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=28
+ connect_to=18
+ }
+}
+structureport
+{
+ name=frequency
+ x=1
+ y=0
+ position=0
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=29
+ connect_to=5
+ }
+}
+structureport
+{
+ name=velocity
+ x=3
+ y=0
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=30
+ }
+}
+structureport
+{
+ name=pressed
+ x=5
+ y=0
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=31
+ connect_to=11
+ }
+}
diff --git a/arts/examples/instrument_simple_square.arts b/arts/examples/instrument_simple_square.arts
new file mode 100644
index 00000000..d82297e3
--- /dev/null
+++ b/arts/examples/instrument_simple_square.arts
@@ -0,0 +1,190 @@
+name=instrument_simple_square
+module=Arts::Synth_FREQUENCY
+{
+ id=4
+ x=1
+ y=2
+ port=frequency
+ {
+ id=5
+ connect_to=29
+ }
+ port=pos
+ {
+ id=6
+ connect_to=23
+ }
+}
+module=Arts::Synth_ENVELOPE_ADSR
+{
+ id=7
+ x=3
+ y=6
+ port=active
+ {
+ id=8
+ connect_to=31
+ }
+ port=invalue
+ {
+ id=9
+ connect_to=24
+ }
+ port=attack
+ {
+ id=10
+ audio_data=0.03000
+ }
+ port=decay
+ {
+ id=11
+ audio_data=0.10000
+ }
+ port=sustain
+ {
+ id=12
+ audio_data=0.60000
+ }
+ port=release
+ {
+ id=13
+ audio_data=0.20000
+ }
+ port=outvalue
+ {
+ id=14
+ connect_to=20
+ connect_to=21
+ connect_to=26
+ connect_to=27
+ }
+ port=done
+ {
+ id=15
+ connect_to=17
+ connect_to=28
+ }
+}
+module=Arts::Synth_WAVE_SQUARE
+{
+ id=22
+ x=1
+ y=4
+ port=pos
+ {
+ id=23
+ connect_to=6
+ }
+ port=outvalue
+ {
+ id=24
+ connect_to=9
+ }
+}
+structureport
+{
+ name=left
+ x=1
+ y=8
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=26
+ connect_to=14
+ }
+}
+structureport
+{
+ name=right
+ x=3
+ y=8
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=27
+ connect_to=14
+ }
+}
+structureport
+{
+ name=done
+ x=5
+ y=8
+ position=2
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=28
+ connect_to=15
+ }
+}
+structureport
+{
+ name=frequency
+ x=1
+ y=0
+ position=0
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=29
+ connect_to=5
+ }
+}
+structureport
+{
+ name=velocity
+ x=3
+ y=0
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=30
+ }
+}
+structureport
+{
+ name=pressed
+ x=5
+ y=0
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=31
+ connect_to=8
+ }
+}
diff --git a/arts/examples/instrument_simple_tri.arts b/arts/examples/instrument_simple_tri.arts
new file mode 100644
index 00000000..05453b8b
--- /dev/null
+++ b/arts/examples/instrument_simple_tri.arts
@@ -0,0 +1,304 @@
+name=instrument_simple_tri
+module=Arts::Synth_FREQUENCY
+{
+ id=0
+ x=0
+ y=2
+ port=frequency
+ {
+ id=1
+ connect_to=36
+ }
+ port=pos
+ {
+ id=2
+ connect_to=13
+ }
+}
+module=Arts::Synth_ENVELOPE_ADSR
+{
+ id=3
+ x=4
+ y=9
+ port=active
+ {
+ id=4
+ connect_to=38
+ }
+ port=invalue
+ {
+ id=5
+ connect_to=46
+ }
+ port=attack
+ {
+ id=6
+ audio_data=0.03000
+ }
+ port=decay
+ {
+ id=7
+ audio_data=0.10000
+ }
+ port=sustain
+ {
+ id=8
+ audio_data=0.60000
+ }
+ port=release
+ {
+ id=9
+ audio_data=0.20000
+ }
+ port=outvalue
+ {
+ id=10
+ connect_to=33
+ connect_to=34
+ }
+ port=done
+ {
+ id=11
+ connect_to=35
+ }
+}
+module=Arts::Synth_WAVE_TRI
+{
+ id=12
+ x=1
+ y=3
+ port=pos
+ {
+ id=13
+ connect_to=2
+ }
+ port=outvalue
+ {
+ id=14
+ connect_to=26
+ }
+}
+module=Arts::Synth_SHELVE_CUTOFF
+{
+ id=15
+ x=6
+ y=6
+ port=invalue
+ {
+ id=16
+ connect_to=28
+ }
+ port=frequency
+ {
+ id=17
+ audio_data=4000.00000
+ }
+ port=outvalue
+ {
+ id=18
+ connect_to=44
+ }
+}
+module=Arts::Synth_WAVE_TRI
+{
+ id=19
+ x=9
+ y=3
+ port=pos
+ {
+ id=20
+ connect_to=24
+ }
+ port=outvalue
+ {
+ id=21
+ connect_to=27
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=22
+ x=8
+ y=2
+ port=frequency
+ {
+ id=23
+ connect_to=32
+ }
+ port=pos
+ {
+ id=24
+ connect_to=20
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=25
+ x=5
+ y=5
+ port=invalue1
+ {
+ id=26
+ connect_to=14
+ }
+ port=invalue2
+ {
+ id=27
+ connect_to=21
+ }
+ port=outvalue
+ {
+ id=28
+ connect_to=16
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=29
+ x=7
+ y=1
+ port=invalue1
+ {
+ id=30
+ connect_to=36
+ }
+ port=invalue2
+ {
+ id=31
+ audio_data=1.01000
+ }
+ port=outvalue
+ {
+ id=32
+ connect_to=23
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=43
+ x=7
+ y=7
+ port=invalue1
+ {
+ id=44
+ connect_to=18
+ }
+ port=invalue2
+ {
+ id=45
+ audio_data=0.50000
+ }
+ port=outvalue
+ {
+ id=46
+ connect_to=5
+ }
+}
+structureport
+{
+ name=left
+ x=1
+ y=12
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=33
+ connect_to=10
+ }
+}
+structureport
+{
+ name=right
+ x=3
+ y=12
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=34
+ connect_to=10
+ }
+}
+structureport
+{
+ name=done
+ x=5
+ y=12
+ position=2
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=35
+ connect_to=11
+ }
+}
+structureport
+{
+ name=frequency
+ x=1
+ y=0
+ position=0
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=36
+ connect_to=1
+ connect_to=30
+ }
+}
+structureport
+{
+ name=velocity
+ x=3
+ y=0
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=37
+ }
+}
+structureport
+{
+ name=pressed
+ x=5
+ y=0
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=38
+ connect_to=4
+ }
+}
diff --git a/arts/examples/instrument_slide.arts b/arts/examples/instrument_slide.arts
new file mode 100644
index 00000000..cb2949b0
--- /dev/null
+++ b/arts/examples/instrument_slide.arts
@@ -0,0 +1,464 @@
+name=instrument_slide
+module=Arts::Synth_FREQUENCY
+{
+ id=0
+ x=4
+ y=3
+ port=frequency
+ {
+ id=1
+ connect_to=67
+ }
+ port=pos
+ {
+ id=2
+ connect_to=22
+ }
+}
+module=Arts::Synth_ENVELOPE_ADSR
+{
+ id=3
+ x=12
+ y=3
+ port=active
+ {
+ id=4
+ connect_to=69
+ }
+ port=invalue
+ {
+ id=5
+ connect_to=19
+ }
+ port=attack
+ {
+ id=6
+ audio_data=0.00200
+ }
+ port=decay
+ {
+ id=7
+ audio_data=0.05000
+ }
+ port=sustain
+ {
+ id=8
+ audio_data=0.70000
+ }
+ port=release
+ {
+ id=9
+ audio_data=0.05000
+ }
+ port=outvalue
+ {
+ id=10
+ connect_to=33
+ connect_to=60
+ }
+ port=done
+ {
+ id=11
+ connect_to=70
+ }
+}
+module=Arts::Synth_ENVELOPE_ADSR
+{
+ id=12
+ x=12
+ y=1
+ port=active
+ {
+ id=13
+ connect_to=69
+ }
+ port=invalue
+ {
+ id=14
+ connect_to=68
+ }
+ port=attack
+ {
+ id=15
+ audio_data=0.00200
+ }
+ port=decay
+ {
+ id=16
+ audio_data=2.00000
+ }
+ port=sustain
+ {
+ id=17
+ audio_data=0.00010
+ }
+ port=release
+ {
+ id=18
+ audio_data=0.10000
+ }
+ port=outvalue
+ {
+ id=19
+ connect_to=5
+ }
+ port=done
+ {
+ id=20
+ }
+}
+module=Arts::Synth_WAVE_TRI
+{
+ id=21
+ x=5
+ y=4
+ port=pos
+ {
+ id=22
+ connect_to=2
+ }
+ port=outvalue
+ {
+ id=23
+ connect_to=48
+ }
+}
+module=Arts::Synth_SHELVE_CUTOFF
+{
+ id=24
+ x=5
+ y=11
+ port=invalue
+ {
+ id=25
+ connect_to=50
+ }
+ port=frequency
+ {
+ id=26
+ connect_to=66
+ }
+ port=outvalue
+ {
+ id=27
+ connect_to=29
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=28
+ x=6
+ y=12
+ port=invalue1
+ {
+ id=29
+ connect_to=27
+ }
+ port=invalue2
+ {
+ id=30
+ connect_to=62
+ }
+ port=outvalue
+ {
+ id=31
+ connect_to=71
+ connect_to=72
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=32
+ x=11
+ y=5
+ port=invalue1
+ {
+ id=33
+ connect_to=10
+ }
+ port=invalue2
+ {
+ id=34
+ audio_data=2000.00000
+ }
+ port=outvalue
+ {
+ id=35
+ connect_to=65
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=36
+ x=1
+ y=3
+ port=invalue1
+ {
+ id=37
+ connect_to=67
+ }
+ port=invalue2
+ {
+ id=38
+ audio_data=2.01000
+ }
+ port=outvalue
+ {
+ id=39
+ connect_to=41
+ connect_to=64
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=40
+ x=2
+ y=5
+ port=frequency
+ {
+ id=41
+ connect_to=39
+ }
+ port=pos
+ {
+ id=42
+ connect_to=44
+ }
+}
+module=Arts::Synth_WAVE_TRI
+{
+ id=43
+ x=3
+ y=6
+ port=pos
+ {
+ id=44
+ connect_to=42
+ }
+ port=outvalue
+ {
+ id=45
+ connect_to=47
+ }
+}
+module=Arts::Synth_XFADE
+{
+ id=46
+ x=4
+ y=7
+ port=invalue1
+ {
+ id=47
+ connect_to=45
+ }
+ port=invalue2
+ {
+ id=48
+ connect_to=23
+ }
+ port=percentage
+ {
+ id=49
+ connect_to=58
+ }
+ port=outvalue
+ {
+ id=50
+ connect_to=25
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=51
+ x=7
+ y=3
+ port=invalue1
+ {
+ id=52
+ connect_to=68
+ }
+ port=invalue2
+ {
+ id=53
+ audio_data=2.00000
+ }
+ port=outvalue
+ {
+ id=54
+ connect_to=56
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=55
+ x=8
+ y=4
+ port=invalue1
+ {
+ id=56
+ connect_to=54
+ }
+ port=invalue2
+ {
+ id=57
+ audio_data=-1.00000
+ }
+ port=outvalue
+ {
+ id=58
+ connect_to=49
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=59
+ x=11
+ y=7
+ port=invalue1
+ {
+ id=60
+ connect_to=10
+ }
+ port=invalue2
+ {
+ id=61
+ audio_data=2.00000
+ }
+ port=outvalue
+ {
+ id=62
+ connect_to=30
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=63
+ x=7
+ y=9
+ port=invalue1
+ {
+ id=64
+ connect_to=39
+ }
+ port=invalue2
+ {
+ id=65
+ connect_to=35
+ }
+ port=outvalue
+ {
+ id=66
+ connect_to=26
+ }
+}
+structureport
+{
+ name=frequency
+ x=5
+ y=0
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=67
+ connect_to=1
+ connect_to=37
+ }
+}
+structureport
+{
+ name=velocity
+ x=6
+ y=0
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=68
+ connect_to=14
+ connect_to=52
+ }
+}
+structureport
+{
+ name=pressed
+ x=7
+ y=0
+ position=3
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=69
+ connect_to=4
+ connect_to=13
+ }
+}
+structureport
+{
+ name=done
+ x=15
+ y=14
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=70
+ connect_to=11
+ }
+}
+structureport
+{
+ name=left
+ x=6
+ y=14
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=71
+ connect_to=31
+ }
+}
+structureport
+{
+ name=right
+ x=8
+ y=14
+ position=2
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=72
+ connect_to=31
+ }
+}
diff --git a/arts/examples/instrument_slide1.arts b/arts/examples/instrument_slide1.arts
new file mode 100644
index 00000000..f540e3fa
--- /dev/null
+++ b/arts/examples/instrument_slide1.arts
@@ -0,0 +1,467 @@
+name=instrument_slide1
+module=Arts::Synth_FREQUENCY
+{
+ id=4
+ x=4
+ y=3
+ port=frequency
+ {
+ id=5
+ connect_to=81
+ }
+ port=pos
+ {
+ id=6
+ connect_to=32
+ }
+}
+module=Arts::Synth_ENVELOPE_ADSR
+{
+ id=9
+ x=12
+ y=3
+ port=active
+ {
+ id=10
+ connect_to=83
+ }
+ port=invalue
+ {
+ id=11
+ connect_to=29
+ }
+ port=attack
+ {
+ id=12
+ audio_data=0.00200
+ }
+ port=decay
+ {
+ id=13
+ audio_data=0.05000
+ }
+ port=sustain
+ {
+ id=14
+ audio_data=0.70000
+ }
+ port=release
+ {
+ id=15
+ audio_data=0.05000
+ }
+ port=outvalue
+ {
+ id=16
+ connect_to=43
+ connect_to=70
+ }
+ port=done
+ {
+ id=17
+ connect_to=8
+ connect_to=80
+ }
+}
+module=Arts::Synth_ENVELOPE_ADSR
+{
+ id=22
+ x=12
+ y=1
+ port=active
+ {
+ id=23
+ connect_to=83
+ }
+ port=invalue
+ {
+ id=24
+ connect_to=82
+ }
+ port=attack
+ {
+ id=25
+ audio_data=0.00200
+ }
+ port=decay
+ {
+ id=26
+ audio_data=2.00000
+ }
+ port=sustain
+ {
+ id=27
+ audio_data=0.00010
+ }
+ port=release
+ {
+ id=28
+ audio_data=0.10000
+ }
+ port=outvalue
+ {
+ id=29
+ connect_to=11
+ }
+ port=done
+ {
+ id=30
+ }
+}
+module=Arts::Synth_WAVE_TRI
+{
+ id=31
+ x=5
+ y=4
+ port=pos
+ {
+ id=32
+ connect_to=6
+ }
+ port=outvalue
+ {
+ id=33
+ connect_to=58
+ }
+}
+module=Arts::Synth_SHELVE_CUTOFF
+{
+ id=34
+ x=5
+ y=11
+ port=invalue
+ {
+ id=35
+ connect_to=60
+ }
+ port=frequency
+ {
+ id=36
+ connect_to=76
+ }
+ port=outvalue
+ {
+ id=37
+ connect_to=39
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=38
+ x=6
+ y=12
+ port=invalue1
+ {
+ id=39
+ connect_to=37
+ }
+ port=invalue2
+ {
+ id=40
+ connect_to=72
+ }
+ port=outvalue
+ {
+ id=41
+ connect_to=20
+ connect_to=21
+ connect_to=78
+ connect_to=79
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=42
+ x=11
+ y=5
+ port=invalue1
+ {
+ id=43
+ connect_to=16
+ }
+ port=invalue2
+ {
+ id=44
+ audio_data=2000.00000
+ }
+ port=outvalue
+ {
+ id=45
+ connect_to=75
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=46
+ x=1
+ y=3
+ port=invalue1
+ {
+ id=47
+ connect_to=81
+ }
+ port=invalue2
+ {
+ id=48
+ audio_data=2.01000
+ }
+ port=outvalue
+ {
+ id=49
+ connect_to=51
+ connect_to=74
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=50
+ x=2
+ y=5
+ port=frequency
+ {
+ id=51
+ connect_to=49
+ }
+ port=pos
+ {
+ id=52
+ connect_to=54
+ }
+}
+module=Arts::Synth_WAVE_TRI
+{
+ id=53
+ x=3
+ y=6
+ port=pos
+ {
+ id=54
+ connect_to=52
+ }
+ port=outvalue
+ {
+ id=55
+ connect_to=57
+ }
+}
+module=Arts::Synth_XFADE
+{
+ id=56
+ x=4
+ y=7
+ port=invalue1
+ {
+ id=57
+ connect_to=55
+ }
+ port=invalue2
+ {
+ id=58
+ connect_to=33
+ }
+ port=percentage
+ {
+ id=59
+ connect_to=68
+ }
+ port=outvalue
+ {
+ id=60
+ connect_to=35
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=61
+ x=7
+ y=3
+ port=invalue1
+ {
+ id=62
+ connect_to=82
+ }
+ port=invalue2
+ {
+ id=63
+ audio_data=2.00000
+ }
+ port=outvalue
+ {
+ id=64
+ connect_to=66
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=65
+ x=8
+ y=4
+ port=invalue1
+ {
+ id=66
+ connect_to=64
+ }
+ port=invalue2
+ {
+ id=67
+ audio_data=-1.00000
+ }
+ port=outvalue
+ {
+ id=68
+ connect_to=59
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=69
+ x=11
+ y=7
+ port=invalue1
+ {
+ id=70
+ connect_to=16
+ }
+ port=invalue2
+ {
+ id=71
+ audio_data=2.00000
+ }
+ port=outvalue
+ {
+ id=72
+ connect_to=40
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=73
+ x=7
+ y=9
+ port=invalue1
+ {
+ id=74
+ connect_to=49
+ }
+ port=invalue2
+ {
+ id=75
+ connect_to=45
+ }
+ port=outvalue
+ {
+ id=76
+ connect_to=36
+ }
+}
+structureport
+{
+ name=left
+ x=1
+ y=14
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=78
+ connect_to=41
+ }
+}
+structureport
+{
+ name=right
+ x=3
+ y=14
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=79
+ connect_to=41
+ }
+}
+structureport
+{
+ name=done
+ x=5
+ y=14
+ position=2
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=80
+ connect_to=17
+ }
+}
+structureport
+{
+ name=frequency
+ x=0
+ y=0
+ position=0
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=81
+ connect_to=47
+ connect_to=5
+ }
+}
+structureport
+{
+ name=velocity
+ x=2
+ y=0
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=82
+ connect_to=62
+ connect_to=24
+ }
+}
+structureport
+{
+ name=pressed
+ x=4
+ y=0
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=83
+ connect_to=23
+ connect_to=10
+ }
+}
diff --git a/arts/examples/instrument_square.arts b/arts/examples/instrument_square.arts
new file mode 100644
index 00000000..13d1e6ed
--- /dev/null
+++ b/arts/examples/instrument_square.arts
@@ -0,0 +1,465 @@
+name=instrument_square
+module=Arts::Synth_FREQUENCY
+{
+ id=0
+ x=1
+ y=2
+ port=frequency
+ {
+ id=1
+ connect_to=63
+ }
+ port=pos
+ {
+ id=2
+ connect_to=13
+ }
+}
+module=Arts::Synth_ENVELOPE_ADSR
+{
+ id=3
+ x=0
+ y=9
+ port=active
+ {
+ id=4
+ connect_to=65
+ }
+ port=invalue
+ {
+ id=5
+ connect_to=76
+ }
+ port=attack
+ {
+ id=6
+ audio_data=0.03000
+ }
+ port=decay
+ {
+ id=7
+ audio_data=0.10000
+ }
+ port=sustain
+ {
+ id=8
+ audio_data=0.60000
+ }
+ port=release
+ {
+ id=9
+ audio_data=0.30000
+ }
+ port=outvalue
+ {
+ id=10
+ connect_to=66
+ connect_to=67
+ }
+ port=done
+ {
+ id=11
+ connect_to=68
+ }
+}
+module=Arts::Synth_WAVE_SQUARE
+{
+ id=12
+ x=1
+ y=4
+ port=pos
+ {
+ id=13
+ connect_to=2
+ }
+ port=outvalue
+ {
+ id=14
+ connect_to=16
+ connect_to=21
+ connect_to=28
+ connect_to=52
+ connect_to=56
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=15
+ x=6
+ y=6
+ port=invalue1
+ {
+ id=16
+ connect_to=14
+ }
+ port=invalue2
+ {
+ id=17
+ connect_to=38
+ }
+ port=outvalue
+ {
+ id=18
+ connect_to=24
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=19
+ x=15
+ y=6
+ port=invalue1
+ {
+ id=20
+ connect_to=26
+ }
+ port=invalue2
+ {
+ id=21
+ connect_to=14
+ }
+ port=outvalue
+ {
+ id=22
+ connect_to=48
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=23
+ x=10
+ y=6
+ port=invalue1
+ {
+ id=24
+ connect_to=18
+ }
+ port=invalue2
+ {
+ id=25
+ connect_to=42
+ }
+ port=outvalue
+ {
+ id=26
+ connect_to=20
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=27
+ x=6
+ y=2
+ port=invalue1
+ {
+ id=28
+ connect_to=14
+ }
+ port=invalue2
+ {
+ id=29
+ audio_data=2.00000
+ }
+ port=outvalue
+ {
+ id=30
+ connect_to=32
+ connect_to=36
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=31
+ x=10
+ y=2
+ port=invalue1
+ {
+ id=32
+ connect_to=30
+ }
+ port=invalue2
+ {
+ id=33
+ audio_data=16.00000
+ }
+ port=outvalue
+ {
+ id=34
+ connect_to=40
+ }
+}
+module=Arts::Synth_SHELVE_CUTOFF
+{
+ id=35
+ x=6
+ y=4
+ port=invalue
+ {
+ id=36
+ connect_to=30
+ }
+ port=frequency
+ {
+ id=37
+ audio_data=6000.00000
+ }
+ port=outvalue
+ {
+ id=38
+ connect_to=17
+ }
+}
+module=Arts::Synth_SHELVE_CUTOFF
+{
+ id=39
+ x=10
+ y=4
+ port=invalue
+ {
+ id=40
+ connect_to=34
+ }
+ port=frequency
+ {
+ id=41
+ audio_data=2000.00000
+ }
+ port=outvalue
+ {
+ id=42
+ connect_to=25
+ }
+}
+module=Arts::Synth_SHELVE_CUTOFF
+{
+ id=43
+ x=12
+ y=9
+ port=invalue
+ {
+ id=44
+ connect_to=54
+ }
+ port=frequency
+ {
+ id=45
+ audio_data=8000.00000
+ }
+ port=outvalue
+ {
+ id=46
+ connect_to=60
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=47
+ x=16
+ y=7
+ port=invalue1
+ {
+ id=48
+ connect_to=22
+ }
+ port=invalue2
+ {
+ id=49
+ audio_data=0.10000
+ }
+ port=outvalue
+ {
+ id=50
+ connect_to=53
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=51
+ x=16
+ y=9
+ port=invalue1
+ {
+ id=52
+ connect_to=14
+ }
+ port=invalue2
+ {
+ id=53
+ connect_to=50
+ }
+ port=outvalue
+ {
+ id=54
+ connect_to=44
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=55
+ x=8
+ y=11
+ port=invalue1
+ {
+ id=56
+ connect_to=14
+ }
+ port=invalue2
+ {
+ id=57
+ connect_to=62
+ }
+ port=outvalue
+ {
+ id=58
+ connect_to=75
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=59
+ x=8
+ y=9
+ port=invalue1
+ {
+ id=60
+ connect_to=46
+ }
+ port=invalue2
+ {
+ id=61
+ audio_data=0.50000
+ }
+ port=outvalue
+ {
+ id=62
+ connect_to=57
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=73
+ x=0
+ y=7
+ port=invalue1
+ {
+ id=74
+ audio_data=0.30000
+ }
+ port=invalue2
+ {
+ id=75
+ connect_to=58
+ }
+ port=outvalue
+ {
+ id=76
+ connect_to=5
+ }
+}
+structureport
+{
+ name=frequency
+ x=2
+ y=0
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=63
+ connect_to=1
+ }
+}
+structureport
+{
+ name=velocity
+ x=4
+ y=0
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=64
+ }
+}
+structureport
+{
+ name=pressed
+ x=1
+ y=8
+ position=3
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=65
+ connect_to=4
+ }
+}
+structureport
+{
+ name=left
+ x=2
+ y=11
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=66
+ connect_to=10
+ }
+}
+structureport
+{
+ name=right
+ x=4
+ y=11
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=67
+ connect_to=10
+ }
+}
+structureport
+{
+ name=done
+ x=6
+ y=11
+ position=2
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=68
+ connect_to=11
+ }
+}
diff --git a/arts/examples/instrument_tri.arts b/arts/examples/instrument_tri.arts
new file mode 100644
index 00000000..2d5795a9
--- /dev/null
+++ b/arts/examples/instrument_tri.arts
@@ -0,0 +1,348 @@
+name=instrument_tri
+module=Arts::Synth_FREQUENCY
+{
+ id=0
+ x=1
+ y=2
+ port=frequency
+ {
+ id=1
+ connect_to=44
+ }
+ port=pos
+ {
+ id=2
+ connect_to=13
+ }
+}
+module=Arts::Synth_ENVELOPE_ADSR
+{
+ id=3
+ x=3
+ y=10
+ port=active
+ {
+ id=4
+ connect_to=46
+ }
+ port=invalue
+ {
+ id=5
+ connect_to=54
+ }
+ port=attack
+ {
+ id=6
+ audio_data=0.03000
+ }
+ port=decay
+ {
+ id=7
+ audio_data=0.10000
+ }
+ port=sustain
+ {
+ id=8
+ audio_data=0.60000
+ }
+ port=release
+ {
+ id=9
+ audio_data=0.20000
+ }
+ port=outvalue
+ {
+ id=10
+ connect_to=41
+ connect_to=42
+ }
+ port=done
+ {
+ id=11
+ connect_to=43
+ }
+}
+module=Arts::Synth_WAVE_TRI
+{
+ id=12
+ x=2
+ y=3
+ port=pos
+ {
+ id=13
+ connect_to=2
+ }
+ port=outvalue
+ {
+ id=14
+ connect_to=26
+ }
+}
+module=Arts::Synth_SHELVE_CUTOFF
+{
+ id=15
+ x=6
+ y=7
+ port=invalue
+ {
+ id=16
+ connect_to=28
+ }
+ port=frequency
+ {
+ id=17
+ connect_to=36
+ }
+ port=outvalue
+ {
+ id=18
+ connect_to=53
+ }
+}
+module=Arts::Synth_WAVE_TRI
+{
+ id=19
+ x=9
+ y=4
+ port=pos
+ {
+ id=20
+ connect_to=24
+ }
+ port=outvalue
+ {
+ id=21
+ connect_to=27
+ }
+}
+module=Arts::Synth_FREQUENCY
+{
+ id=22
+ x=8
+ y=3
+ port=frequency
+ {
+ id=23
+ connect_to=32
+ }
+ port=pos
+ {
+ id=24
+ connect_to=20
+ }
+}
+module=Arts::Synth_ADD
+{
+ id=25
+ x=6
+ y=5
+ port=invalue1
+ {
+ id=26
+ connect_to=14
+ }
+ port=invalue2
+ {
+ id=27
+ connect_to=21
+ }
+ port=outvalue
+ {
+ id=28
+ connect_to=16
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=29
+ x=7
+ y=1
+ port=invalue1
+ {
+ id=30
+ connect_to=44
+ }
+ port=invalue2
+ {
+ id=31
+ audio_data=1.01000
+ }
+ port=outvalue
+ {
+ id=32
+ connect_to=23
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=33
+ x=0
+ y=7
+ port=invalue1
+ {
+ id=34
+ connect_to=44
+ }
+ port=invalue2
+ {
+ id=35
+ connect_to=40
+ }
+ port=outvalue
+ {
+ id=36
+ connect_to=17
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=37
+ x=1
+ y=5
+ port=invalue1
+ {
+ id=38
+ audio_data=6.00000
+ }
+ port=invalue2
+ {
+ id=39
+ connect_to=45
+ }
+ port=outvalue
+ {
+ id=40
+ connect_to=35
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=51
+ x=6
+ y=8
+ port=invalue1
+ {
+ id=52
+ audio_data=0.50000
+ }
+ port=invalue2
+ {
+ id=53
+ connect_to=18
+ }
+ port=outvalue
+ {
+ id=54
+ connect_to=5
+ }
+}
+structureport
+{
+ name=left
+ x=2
+ y=12
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=41
+ connect_to=10
+ }
+}
+structureport
+{
+ name=right
+ x=4
+ y=12
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=42
+ connect_to=10
+ }
+}
+structureport
+{
+ name=done
+ x=6
+ y=12
+ position=2
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=43
+ connect_to=11
+ }
+}
+structureport
+{
+ name=frequency
+ x=2
+ y=0
+ position=0
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=44
+ connect_to=1
+ connect_to=30
+ connect_to=34
+ }
+}
+structureport
+{
+ name=velocity
+ x=4
+ y=0
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=45
+ connect_to=39
+ }
+}
+structureport
+{
+ name=pressed
+ x=6
+ y=0
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=46
+ connect_to=4
+ }
+}
diff --git a/arts/examples/mixer_element_eq.arts b/arts/examples/mixer_element_eq.arts
new file mode 100644
index 00000000..6123bb24
--- /dev/null
+++ b/arts/examples/mixer_element_eq.arts
@@ -0,0 +1,567 @@
+name=mixer_element_eq
+module=Arts::Widget
+{
+ id=0
+ x=6
+ y=1
+ port=widgetID
+ {
+ id=1
+ }
+ port=parent
+ {
+ id=2
+ }
+ port=x
+ {
+ id=3
+ }
+ port=y
+ {
+ id=4
+ }
+ port=width
+ {
+ id=5
+ }
+ port=height
+ {
+ id=6
+ }
+ port=visible
+ {
+ id=7
+ any_data=value:00000008626f6f6c65616e000000000101
+ }
+}
+module=Arts::Poti
+{
+ id=8
+ x=0
+ y=3
+ port=widgetID
+ {
+ id=9
+ }
+ port=x
+ {
+ id=11
+ }
+ port=y
+ {
+ id=12
+ }
+ port=width
+ {
+ id=13
+ }
+ port=height
+ {
+ id=14
+ }
+ port=visible
+ {
+ id=15
+ }
+ port=text
+ {
+ id=16
+ string_data=VOLUME
+ }
+ port=color
+ {
+ id=17
+ string_data=red
+ }
+ port=min
+ {
+ id=18
+ audio_data=0.00000
+ }
+ port=max
+ {
+ id=19
+ audio_data=1.00000
+ }
+ port=value
+ {
+ id=20
+ }
+}
+module=Arts::Synth_BUS_DOWNLINK
+{
+ id=21
+ x=8
+ y=4
+ port=busname
+ {
+ id=22
+ connect_to=119
+ }
+ port=left
+ {
+ id=23
+ connect_to=30
+ }
+ port=right
+ {
+ id=24
+ connect_to=34
+ }
+}
+module=Arts::Synth_BUS_UPLINK
+{
+ id=25
+ x=1
+ y=21
+ port=busname
+ {
+ id=26
+ connect_to=118
+ }
+ port=left
+ {
+ id=27
+ connect_to=44
+ }
+ port=right
+ {
+ id=28
+ connect_to=52
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=29
+ x=0
+ y=7
+ port=invalue1
+ {
+ id=30
+ connect_to=23
+ }
+ port=invalue2
+ {
+ id=31
+ }
+ port=outvalue
+ {
+ id=32
+ connect_to=43
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=33
+ x=6
+ y=7
+ port=invalue1
+ {
+ id=34
+ connect_to=24
+ }
+ port=invalue2
+ {
+ id=35
+ }
+ port=outvalue
+ {
+ id=36
+ connect_to=51
+ }
+}
+module=Arts::Synth_STD_EQUALIZER
+{
+ id=37
+ x=0
+ y=17
+ port=low
+ {
+ id=38
+ }
+ port=mid
+ {
+ id=39
+ }
+ port=high
+ {
+ id=40
+ }
+ port=frequency
+ {
+ id=41
+ }
+ port=q
+ {
+ id=42
+ }
+ port=invalue
+ {
+ id=43
+ connect_to=32
+ }
+ port=outvalue
+ {
+ id=44
+ connect_to=27
+ }
+}
+module=Arts::Synth_STD_EQUALIZER
+{
+ id=45
+ x=7
+ y=17
+ port=low
+ {
+ id=46
+ }
+ port=mid
+ {
+ id=47
+ }
+ port=high
+ {
+ id=48
+ }
+ port=frequency
+ {
+ id=49
+ }
+ port=q
+ {
+ id=50
+ }
+ port=invalue
+ {
+ id=51
+ connect_to=36
+ }
+ port=outvalue
+ {
+ id=52
+ connect_to=28
+ }
+}
+module=Arts::Poti
+{
+ id=53
+ x=14
+ y=9
+ port=widgetID
+ {
+ id=54
+ }
+ port=x
+ {
+ id=56
+ }
+ port=y
+ {
+ id=57
+ }
+ port=width
+ {
+ id=58
+ }
+ port=height
+ {
+ id=59
+ }
+ port=visible
+ {
+ id=60
+ }
+ port=text
+ {
+ id=61
+ string_data=MID
+ }
+ port=color
+ {
+ id=62
+ string_data=darkblue
+ }
+ port=min
+ {
+ id=63
+ audio_data=-24.00000
+ }
+ port=max
+ {
+ id=64
+ audio_data=24.00000
+ }
+ port=value
+ {
+ id=65
+ }
+}
+module=Arts::Poti
+{
+ id=66
+ x=14
+ y=11
+ port=widgetID
+ {
+ id=67
+ }
+ port=x
+ {
+ id=69
+ }
+ port=y
+ {
+ id=70
+ }
+ port=width
+ {
+ id=71
+ }
+ port=height
+ {
+ id=72
+ }
+ port=visible
+ {
+ id=73
+ }
+ port=text
+ {
+ id=74
+ string_data=HIGH
+ }
+ port=color
+ {
+ id=75
+ string_data=darkblue
+ }
+ port=min
+ {
+ id=76
+ audio_data=-24.00000
+ }
+ port=max
+ {
+ id=77
+ audio_data=24.00000
+ }
+ port=value
+ {
+ id=78
+ }
+}
+module=Arts::Poti
+{
+ id=79
+ x=14
+ y=7
+ port=widgetID
+ {
+ id=80
+ }
+ port=x
+ {
+ id=82
+ }
+ port=y
+ {
+ id=83
+ }
+ port=width
+ {
+ id=84
+ }
+ port=height
+ {
+ id=85
+ }
+ port=visible
+ {
+ id=86
+ }
+ port=text
+ {
+ id=87
+ string_data=LOW
+ }
+ port=color
+ {
+ id=88
+ string_data=darkblue
+ }
+ port=min
+ {
+ id=89
+ audio_data=-24.00000
+ }
+ port=max
+ {
+ id=90
+ audio_data=24.00000
+ }
+ port=value
+ {
+ id=91
+ }
+}
+module=Arts::Poti
+{
+ id=92
+ x=14
+ y=13
+ port=widgetID
+ {
+ id=93
+ }
+ port=x
+ {
+ id=95
+ }
+ port=y
+ {
+ id=96
+ }
+ port=width
+ {
+ id=97
+ }
+ port=height
+ {
+ id=98
+ }
+ port=visible
+ {
+ id=99
+ }
+ port=text
+ {
+ id=100
+ string_data=FREQUENCY
+ }
+ port=color
+ {
+ id=101
+ string_data=darkgreen
+ }
+ port=min
+ {
+ id=102
+ audio_data=1.00000
+ }
+ port=max
+ {
+ id=103
+ audio_data=5000.00000
+ }
+ port=value
+ {
+ id=104
+ }
+}
+module=Arts::Poti
+{
+ id=105
+ x=14
+ y=15
+ port=widgetID
+ {
+ id=106
+ }
+ port=x
+ {
+ id=108
+ }
+ port=y
+ {
+ id=109
+ }
+ port=width
+ {
+ id=110
+ }
+ port=height
+ {
+ id=111
+ }
+ port=visible
+ {
+ id=112
+ }
+ port=text
+ {
+ id=113
+ string_data=Q
+ }
+ port=color
+ {
+ id=114
+ string_data=darkgreen
+ }
+ port=min
+ {
+ id=115
+ audio_data=0.10000
+ }
+ port=max
+ {
+ id=116
+ audio_data=2.00000
+ }
+ port=value
+ {
+ id=117
+ }
+}
+structureport
+{
+ name=output
+ x=4
+ y=20
+ position=2
+ type
+ {
+ direction=output
+ datatype=string
+ conntype=property
+ }
+ data
+ {
+ id=118
+ connect_to=26
+ }
+}
+structureport
+{
+ name=input
+ x=13
+ y=0
+ position=1
+ type
+ {
+ direction=output
+ datatype=string
+ conntype=property
+ }
+ data
+ {
+ id=119
+ connect_to=22
+ }
+}
+structureport
+{
+ name=parent
+ x=7
+ y=0
+ position=0
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=120
+ }
+}
diff --git a/arts/examples/mixer_element_eqfx.arts b/arts/examples/mixer_element_eqfx.arts
new file mode 100644
index 00000000..b7248fcb
--- /dev/null
+++ b/arts/examples/mixer_element_eqfx.arts
@@ -0,0 +1,740 @@
+name=mixer_element_eqfx
+module=Gui_SUB_PANEL
+{
+ id=147
+ x=6
+ y=1
+ port=parent
+ {
+ id=148
+ connect_to=279
+ }
+ port=x
+ {
+ id=149
+ audio_data=0.00000
+ }
+ port=y
+ {
+ id=150
+ audio_data=0.00000
+ }
+ port=width
+ {
+ id=151
+ audio_data=6.50000
+ }
+ port=height
+ {
+ id=152
+ audio_data=32.00000
+ }
+ port=pixmap
+ {
+ id=153
+ string_data=easyblue
+ }
+ port=id
+ {
+ id=154
+ connect_to=156
+ connect_to=183
+ connect_to=206
+ connect_to=216
+ connect_to=226
+ connect_to=236
+ connect_to=246
+ connect_to=256
+ }
+}
+module=Gui_POTI
+{
+ id=155
+ x=0
+ y=3
+ port=parent
+ {
+ id=156
+ connect_to=154
+ }
+ port=x
+ {
+ id=157
+ audio_data=1.00000
+ }
+ port=y
+ {
+ id=158
+ audio_data=2.00000
+ }
+ port=color
+ {
+ id=159
+ string_data=red
+ }
+ port=caption
+ {
+ id=160
+ string_data=VOLUME
+ }
+ port=min
+ {
+ id=161
+ audio_data=0.00000
+ }
+ port=max
+ {
+ id=162
+ audio_data=1.00000
+ }
+ port=initial
+ {
+ id=163
+ audio_data=1.00000
+ }
+ port=value
+ {
+ id=164
+ connect_to=176
+ connect_to=180
+ }
+}
+module=Synth_BUS_DOWNLINK
+{
+ id=165
+ x=8
+ y=4
+ port=clients
+ {
+ id=166
+ audio_data=1.00000
+ }
+ port=busname
+ {
+ id=167
+ connect_to=278
+ }
+ port=left
+ {
+ id=168
+ connect_to=175
+ }
+ port=right
+ {
+ id=169
+ connect_to=179
+ }
+}
+module=Synth_BUS_UPLINK
+{
+ id=170
+ x=1
+ y=21
+ port=left
+ {
+ id=171
+ connect_to=196
+ }
+ port=right
+ {
+ id=172
+ connect_to=204
+ }
+ port=busname
+ {
+ id=173
+ connect_to=277
+ }
+}
+module=Synth_MUL
+{
+ id=174
+ x=0
+ y=7
+ port=invalue
+ {
+ id=175
+ connect_to=168
+ }
+ port=faktor
+ {
+ id=176
+ connect_to=164
+ }
+ port=outvalue
+ {
+ id=177
+ connect_to=190
+ }
+}
+module=Synth_MUL
+{
+ id=178
+ x=6
+ y=7
+ port=invalue
+ {
+ id=179
+ connect_to=169
+ }
+ port=faktor
+ {
+ id=180
+ connect_to=164
+ }
+ port=outvalue
+ {
+ id=181
+ connect_to=198
+ }
+}
+module=Gui_LABEL
+{
+ id=182
+ x=13
+ y=4
+ port=parent
+ {
+ id=183
+ connect_to=154
+ }
+ port=x
+ {
+ id=184
+ audio_data=0.00000
+ }
+ port=y
+ {
+ id=185
+ audio_data=0.00000
+ }
+ port=color
+ {
+ id=186
+ string_data=#fff7a3
+ }
+ port=caption
+ {
+ id=187
+ connect_to=278
+ }
+ port=pixmap
+ {
+ id=188
+ string_data=easylabel
+ }
+}
+module=Synth_STD_EQUALIZER
+{
+ id=189
+ x=0
+ y=17
+ port=invalue
+ {
+ id=190
+ connect_to=177
+ }
+ port=low
+ {
+ id=191
+ connect_to=234
+ }
+ port=mid
+ {
+ id=192
+ connect_to=214
+ }
+ port=high
+ {
+ id=193
+ connect_to=224
+ }
+ port=frequency
+ {
+ id=194
+ connect_to=244
+ }
+ port=q
+ {
+ id=195
+ connect_to=254
+ }
+ port=outvalue
+ {
+ id=196
+ connect_to=171
+ connect_to=270
+ }
+}
+module=Synth_STD_EQUALIZER
+{
+ id=197
+ x=7
+ y=17
+ port=invalue
+ {
+ id=198
+ connect_to=181
+ }
+ port=low
+ {
+ id=199
+ connect_to=234
+ }
+ port=mid
+ {
+ id=200
+ connect_to=214
+ }
+ port=high
+ {
+ id=201
+ connect_to=224
+ }
+ port=frequency
+ {
+ id=202
+ connect_to=244
+ }
+ port=q
+ {
+ id=203
+ connect_to=254
+ }
+ port=outvalue
+ {
+ id=204
+ connect_to=172
+ connect_to=274
+ }
+}
+module=Gui_POTI
+{
+ id=205
+ x=14
+ y=9
+ port=parent
+ {
+ id=206
+ connect_to=154
+ }
+ port=x
+ {
+ id=207
+ audio_data=1.00000
+ }
+ port=y
+ {
+ id=208
+ audio_data=10.00000
+ }
+ port=color
+ {
+ id=209
+ string_data=darkblue
+ }
+ port=caption
+ {
+ id=210
+ string_data=MID
+ }
+ port=min
+ {
+ id=211
+ audio_data=-24.00000
+ }
+ port=max
+ {
+ id=212
+ audio_data=24.00000
+ }
+ port=initial
+ {
+ id=213
+ audio_data=0.00000
+ }
+ port=value
+ {
+ id=214
+ connect_to=192
+ connect_to=200
+ }
+}
+module=Gui_POTI
+{
+ id=215
+ x=14
+ y=11
+ port=parent
+ {
+ id=216
+ connect_to=154
+ }
+ port=x
+ {
+ id=217
+ audio_data=1.00000
+ }
+ port=y
+ {
+ id=218
+ audio_data=14.00000
+ }
+ port=color
+ {
+ id=219
+ string_data=darkblue
+ }
+ port=caption
+ {
+ id=220
+ string_data=HIGH
+ }
+ port=min
+ {
+ id=221
+ audio_data=-24.00000
+ }
+ port=max
+ {
+ id=222
+ audio_data=24.00000
+ }
+ port=initial
+ {
+ id=223
+ audio_data=0.00000
+ }
+ port=value
+ {
+ id=224
+ connect_to=193
+ connect_to=201
+ }
+}
+module=Gui_POTI
+{
+ id=225
+ x=14
+ y=7
+ port=parent
+ {
+ id=226
+ connect_to=154
+ }
+ port=x
+ {
+ id=227
+ audio_data=1.00000
+ }
+ port=y
+ {
+ id=228
+ audio_data=6.00000
+ }
+ port=color
+ {
+ id=229
+ string_data=darkblue
+ }
+ port=caption
+ {
+ id=230
+ string_data=LOW
+ }
+ port=min
+ {
+ id=231
+ audio_data=-24.00000
+ }
+ port=max
+ {
+ id=232
+ audio_data=24.00000
+ }
+ port=initial
+ {
+ id=233
+ audio_data=0.00000
+ }
+ port=value
+ {
+ id=234
+ connect_to=191
+ connect_to=199
+ }
+}
+module=Gui_POTI
+{
+ id=235
+ x=14
+ y=13
+ port=parent
+ {
+ id=236
+ connect_to=154
+ }
+ port=x
+ {
+ id=237
+ audio_data=1.00000
+ }
+ port=y
+ {
+ id=238
+ audio_data=18.00000
+ }
+ port=color
+ {
+ id=239
+ string_data=darkgreen
+ }
+ port=caption
+ {
+ id=240
+ string_data=FREQUENCY
+ }
+ port=min
+ {
+ id=241
+ audio_data=1.00000
+ }
+ port=max
+ {
+ id=242
+ audio_data=5000.00000
+ }
+ port=initial
+ {
+ id=243
+ audio_data=1000.00000
+ }
+ port=value
+ {
+ id=244
+ connect_to=194
+ connect_to=202
+ }
+}
+module=Gui_POTI
+{
+ id=245
+ x=14
+ y=15
+ port=parent
+ {
+ id=246
+ connect_to=154
+ }
+ port=x
+ {
+ id=247
+ audio_data=1.00000
+ }
+ port=y
+ {
+ id=248
+ audio_data=22.00000
+ }
+ port=color
+ {
+ id=249
+ string_data=darkgreen
+ }
+ port=caption
+ {
+ id=250
+ string_data=Q
+ }
+ port=min
+ {
+ id=251
+ audio_data=0.10000
+ }
+ port=max
+ {
+ id=252
+ audio_data=2.00000
+ }
+ port=initial
+ {
+ id=253
+ audio_data=1.00000
+ }
+ port=value
+ {
+ id=254
+ connect_to=195
+ connect_to=203
+ }
+}
+module=Gui_POTI
+{
+ id=255
+ x=14
+ y=17
+ port=parent
+ {
+ id=256
+ connect_to=154
+ }
+ port=x
+ {
+ id=257
+ audio_data=1.00000
+ }
+ port=y
+ {
+ id=258
+ audio_data=26.00000
+ }
+ port=color
+ {
+ id=259
+ string_data=grey
+ }
+ port=caption
+ {
+ id=260
+ string_data=EFFECT
+ }
+ port=min
+ {
+ id=261
+ audio_data=0.00000
+ }
+ port=max
+ {
+ id=262
+ audio_data=1.00000
+ }
+ port=initial
+ {
+ id=263
+ audio_data=0.00000
+ }
+ port=value
+ {
+ id=264
+ connect_to=271
+ connect_to=275
+ }
+}
+module=Synth_BUS_UPLINK
+{
+ id=265
+ x=12
+ y=22
+ port=left
+ {
+ id=266
+ connect_to=272
+ }
+ port=right
+ {
+ id=267
+ connect_to=276
+ }
+ port=busname
+ {
+ id=268
+ string_data=effect
+ }
+}
+module=Synth_MUL
+{
+ id=269
+ x=9
+ y=20
+ port=invalue
+ {
+ id=270
+ connect_to=196
+ }
+ port=faktor
+ {
+ id=271
+ connect_to=264
+ }
+ port=outvalue
+ {
+ id=272
+ connect_to=266
+ }
+}
+module=Synth_MUL
+{
+ id=273
+ x=12
+ y=20
+ port=invalue
+ {
+ id=274
+ connect_to=204
+ }
+ port=faktor
+ {
+ id=275
+ connect_to=264
+ }
+ port=outvalue
+ {
+ id=276
+ connect_to=267
+ }
+}
+structureport
+{
+ name=output
+ x=4
+ y=20
+ position=2
+ type
+ {
+ direction=output
+ datatype=string
+ conntype=property
+ }
+ data
+ {
+ id=277
+ connect_to=173
+ }
+}
+structureport
+{
+ name=input
+ x=13
+ y=0
+ position=1
+ type
+ {
+ direction=output
+ datatype=string
+ conntype=property
+ }
+ data
+ {
+ id=278
+ connect_to=167
+ connect_to=187
+ }
+}
+structureport
+{
+ name=parent
+ x=7
+ y=0
+ position=0
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=279
+ connect_to=148
+ }
+}
diff --git a/arts/examples/mixer_element_simple.arts b/arts/examples/mixer_element_simple.arts
new file mode 100644
index 00000000..01246090
--- /dev/null
+++ b/arts/examples/mixer_element_simple.arts
@@ -0,0 +1,235 @@
+name=mixer_element_simple
+module=Arts::Widget
+{
+ id=0
+ x=6
+ y=1
+ port=widgetID
+ {
+ id=1
+ }
+ port=parent
+ {
+ id=2
+ }
+ port=x
+ {
+ id=3
+ any_data=value:000000056c6f6e67000000000400000000
+ }
+ port=y
+ {
+ id=4
+ any_data=value:000000056c6f6e67000000000400000000
+ }
+ port=width
+ {
+ id=5
+ any_data=value:000000056c6f6e6700000000040000000a
+ }
+ port=height
+ {
+ id=6
+ any_data=value:000000056c6f6e6700000000040000000a
+ }
+ port=visible
+ {
+ id=7
+ any_data=value:00000008626f6f6c65616e000000000101
+ }
+}
+module=Arts::Poti
+{
+ id=8
+ x=0
+ y=3
+ port=widgetID
+ {
+ id=9
+ }
+ port=parent
+ {
+ id=10
+ }
+ port=x
+ {
+ id=11
+ any_data=value:000000056c6f6e67000000000400000064
+ }
+ port=y
+ {
+ id=12
+ any_data=value:000000056c6f6e67000000000400000000
+ }
+ port=width
+ {
+ id=13
+ any_data=value:000000056c6f6e67000000000400000032
+ }
+ port=height
+ {
+ id=14
+ any_data=value:000000056c6f6e67000000000400000032
+ }
+ port=visible
+ {
+ id=15
+ any_data=value:00000008626f6f6c65616e000000000101
+ }
+ port=text
+ {
+ id=16
+ string_data=VOLUME
+ }
+ port=color
+ {
+ id=17
+ string_data=red
+ }
+ port=min
+ {
+ id=18
+ audio_data=0.00000
+ }
+ port=max
+ {
+ id=19
+ audio_data=1.00000
+ }
+ port=value
+ {
+ id=20
+ audio_data=0.50000
+ }
+}
+module=Arts::Synth_BUS_DOWNLINK
+{
+ id=21
+ x=8
+ y=4
+ port=busname
+ {
+ id=22
+ }
+ port=left
+ {
+ id=23
+ connect_to=30
+ }
+ port=right
+ {
+ id=24
+ connect_to=34
+ }
+}
+module=Arts::Synth_BUS_UPLINK
+{
+ id=25
+ x=1
+ y=10
+ port=busname
+ {
+ id=26
+ }
+ port=left
+ {
+ id=27
+ connect_to=32
+ }
+ port=right
+ {
+ id=28
+ connect_to=36
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=29
+ x=0
+ y=7
+ port=invalue1
+ {
+ id=30
+ connect_to=23
+ }
+ port=invalue2
+ {
+ id=31
+ }
+ port=outvalue
+ {
+ id=32
+ connect_to=27
+ }
+}
+module=Arts::Synth_MUL
+{
+ id=33
+ x=3
+ y=7
+ port=invalue1
+ {
+ id=34
+ connect_to=24
+ }
+ port=invalue2
+ {
+ id=35
+ }
+ port=outvalue
+ {
+ id=36
+ connect_to=28
+ }
+}
+structureport
+{
+ name=output
+ x=13
+ y=10
+ position=2
+ type
+ {
+ direction=input
+ datatype=string
+ conntype=property
+ }
+ data
+ {
+ id=37
+ }
+}
+structureport
+{
+ name=input
+ x=13
+ y=0
+ position=1
+ type
+ {
+ direction=output
+ datatype=string
+ conntype=property
+ }
+ data
+ {
+ id=38
+ }
+}
+structureport
+{
+ name=parent
+ x=7
+ y=0
+ position=0
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=39
+ }
+}
diff --git a/arts/examples/template_Empty_Structure.arts b/arts/examples/template_Empty_Structure.arts
new file mode 100644
index 00000000..c8df90c3
--- /dev/null
+++ b/arts/examples/template_Empty_Structure.arts
@@ -0,0 +1 @@
+name=template_Empty_Structure
diff --git a/arts/examples/template_Instrument.arts b/arts/examples/template_Instrument.arts
new file mode 100644
index 00000000..e555e424
--- /dev/null
+++ b/arts/examples/template_Instrument.arts
@@ -0,0 +1,103 @@
+name=template_Instrument
+structureport
+{
+ name=frequency
+ x=1
+ y=0
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=11
+ }
+}
+structureport
+{
+ name=velocity
+ x=3
+ y=0
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=12
+ }
+}
+structureport
+{
+ name=pressed
+ x=5
+ y=0
+ position=3
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=13
+ }
+}
+structureport
+{
+ name=left
+ x=1
+ y=8
+ position=0
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=14
+ }
+}
+structureport
+{
+ name=right
+ x=3
+ y=8
+ position=1
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=15
+ }
+}
+structureport
+{
+ name=done
+ x=5
+ y=8
+ position=2
+ type
+ {
+ direction=input
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=16
+ }
+}
diff --git a/arts/examples/template_Instrument_GUI.arts b/arts/examples/template_Instrument_GUI.arts
new file mode 100644
index 00000000..67db385e
--- /dev/null
+++ b/arts/examples/template_Instrument_GUI.arts
@@ -0,0 +1,91 @@
+name=template_Instrument_GUI
+module=Arts::Widget
+{
+ id=0
+ x=1
+ y=2
+ port=widgetID
+ {
+ id=1
+ }
+ port=parent
+ {
+ id=2
+ }
+ port=x
+ {
+ id=3
+ any_data=value:000000056c6f6e67000000000400000000
+ }
+ port=y
+ {
+ id=4
+ any_data=value:000000056c6f6e67000000000400000000
+ }
+ port=width
+ {
+ id=5
+ any_data=value:000000056c6f6e6700000000040000000a
+ }
+ port=height
+ {
+ id=6
+ any_data=value:000000056c6f6e6700000000040000000a
+ }
+ port=visible
+ {
+ id=7
+ any_data=value:00000008626f6f6c65616e000000000101
+ }
+}
+structureport
+{
+ name=parent
+ x=2
+ y=1
+ position=0
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=8
+ }
+}
+structureport
+{
+ name=x
+ x=3
+ y=1
+ position=1
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=property
+ }
+ data
+ {
+ id=9
+ }
+}
+structureport
+{
+ name=y
+ x=4
+ y=1
+ position=2
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=property
+ }
+ data
+ {
+ id=10
+ }
+}
diff --git a/arts/examples/template_Mixer_Element.arts b/arts/examples/template_Mixer_Element.arts
new file mode 100644
index 00000000..40c3e63c
--- /dev/null
+++ b/arts/examples/template_Mixer_Element.arts
@@ -0,0 +1,176 @@
+name=template_Mixer_Element
+module=Gui_SUB_PANEL
+{
+ id=0
+ x=0
+ y=1
+ port=parent
+ {
+ id=1
+ connect_to=26
+ }
+ port=x
+ {
+ id=2
+ audio_data=0.00000
+ }
+ port=y
+ {
+ id=3
+ audio_data=0.00000
+ }
+ port=width
+ {
+ id=4
+ audio_data=6.50000
+ }
+ port=height
+ {
+ id=5
+ audio_data=7.50000
+ }
+ port=pixmap
+ {
+ id=6
+ string_data=easyblue
+ }
+ port=id
+ {
+ id=7
+ connect_to=18
+ }
+}
+module=Synth_BUS_DOWNLINK
+{
+ id=8
+ x=2
+ y=4
+ port=clients
+ {
+ id=9
+ audio_data=1.00000
+ }
+ port=busname
+ {
+ id=10
+ connect_to=25
+ }
+ port=left
+ {
+ id=11
+ }
+ port=right
+ {
+ id=12
+ }
+}
+module=Synth_BUS_UPLINK
+{
+ id=13
+ x=3
+ y=8
+ port=left
+ {
+ id=14
+ }
+ port=right
+ {
+ id=15
+ }
+ port=busname
+ {
+ id=16
+ connect_to=24
+ }
+}
+module=Gui_LABEL
+{
+ id=17
+ x=6
+ y=4
+ port=parent
+ {
+ id=18
+ connect_to=7
+ }
+ port=x
+ {
+ id=19
+ audio_data=0.00000
+ }
+ port=y
+ {
+ id=20
+ audio_data=0.00000
+ }
+ port=color
+ {
+ id=21
+ string_data=#fff7a3
+ }
+ port=caption
+ {
+ id=22
+ connect_to=25
+ }
+ port=pixmap
+ {
+ id=23
+ string_data=easylabel
+ }
+}
+structureport
+{
+ name=output
+ x=6
+ y=7
+ position=2
+ type
+ {
+ direction=output
+ datatype=string
+ conntype=property
+ }
+ data
+ {
+ id=24
+ connect_to=16
+ }
+}
+structureport
+{
+ name=input
+ x=7
+ y=0
+ position=1
+ type
+ {
+ direction=output
+ datatype=string
+ conntype=property
+ }
+ data
+ {
+ id=25
+ connect_to=10
+ connect_to=22
+ }
+}
+structureport
+{
+ name=parent
+ x=1
+ y=0
+ position=0
+ type
+ {
+ direction=output
+ datatype=audio
+ conntype=stream
+ }
+ data
+ {
+ id=26
+ connect_to=1
+ }
+}
diff --git a/arts/gui/Makefile.am b/arts/gui/Makefile.am
new file mode 100644
index 00000000..380e6a8c
--- /dev/null
+++ b/arts/gui/Makefile.am
@@ -0,0 +1,7 @@
+if arts_within_KDE
+ARTS_BUILD_KDE_GUI=kde
+endif
+
+SUBDIRS = common $(ARTS_BUILD_KDE_GUI)
+
+DIST_SUBDIRS = common kde
diff --git a/arts/gui/common/GenericGuiFactory.mcopclass b/arts/gui/common/GenericGuiFactory.mcopclass
new file mode 100644
index 00000000..001dd5ff
--- /dev/null
+++ b/arts/gui/common/GenericGuiFactory.mcopclass
@@ -0,0 +1,3 @@
+Interface=Arts::GenericGuiFactory,Arts::GuiFactory,Arts::Object
+Language=C++
+Library=libartsgui.la
diff --git a/arts/gui/common/Makefile.am b/arts/gui/common/Makefile.am
new file mode 100644
index 00000000..9d82449f
--- /dev/null
+++ b/arts/gui/common/Makefile.am
@@ -0,0 +1,35 @@
+lib_LTLIBRARIES = libartsgui_idl.la libartsgui.la
+
+INCLUDES= -I$(arts_includes) -I$(top_builddir)/arts/runtime $(all_includes)
+
+libartsgui_idl_la_SOURCES = artsgui.cc
+libartsgui_idl_la_LIBADD = -lmcop $(LIBDL)
+libartsgui_idl_la_LDFLAGS = $(all_libraries) -L$(arts_libraries) \
+ -no-undefined
+
+libartsgui_la_SOURCES = genericguifactory_impl.cc
+libartsgui_la_LIBADD = libartsgui_idl.la
+libartsgui_la_LDFLAGS = $(all_libraries) -no-undefined
+libartsgui_la_COMPILE_FIRST = artsgui.h
+
+artsgui.cc artsgui.h: $(srcdir)/artsgui.idl $(MCOPIDL)
+ $(MCOPIDL) -t -I$(includedir)/arts $(srcdir)/artsgui.idl
+
+artsgui.mcoptype: artsgui.h
+artsgui.mcopclass: artsgui.h
+
+DISTCLEANFILES = artsgui.cc artsgui.h \
+ artsgui.mcoptype artsgui.mcopclass
+
+####### install idl files
+
+artsincludedir = $(includedir)/arts
+artsinclude_HEADERS = artsgui.h artsgui.idl
+
+mcopclassdir = $(libdir)/mcop/Arts
+mcopclass_DATA = GenericGuiFactory.mcopclass
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = artsgui.mcoptype artsgui.mcopclass
+
+artsgui.lo: artsgui.h
diff --git a/arts/gui/common/artsgui.idl b/arts/gui/common/artsgui.idl
new file mode 100644
index 00000000..a96b83b7
--- /dev/null
+++ b/arts/gui/common/artsgui.idl
@@ -0,0 +1,396 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+ 2001, 2002 Matthias Kretz <kretz@kde.org>
+ 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+/*
+ * DISCLAIMER: The interfaces in artsgui.idl (and the derived .cc/.h files)
+ * DO NOT GUARANTEE BINARY COMPATIBILITY YET.
+ *
+ * They are intended for developers. You shouldn't expect that applications in
+ * binary form will be fully compatibile with further releases of these
+ * interfaces.
+ */
+
+module Arts {
+ // MayGrow = 1,
+ // ExpMask = 2,
+ // MayShrink = 4
+ enum SizePolicy {
+ spFixed = 0,
+ spMinimum = 1,
+ spMaximum = 4,
+ spPreferred = 5,
+ spMinimumExpanding = 3,
+ spExpanding = 7,
+ spIgnored = 2
+ };
+
+ interface Widget {
+ readonly attribute long widgetID;
+
+ attribute Widget parent;
+ attribute long x,y,width,height;
+ attribute boolean visible;
+ attribute SizePolicy hSizePolicy;
+ attribute SizePolicy vSizePolicy;
+
+ void show();
+ void hide();
+ };
+
+ enum Shape {
+ NoFrame = 0,
+ Box = 0x0001,
+ Panel = 0x0002,
+ WinPanel = 0x0003,
+ HLine = 0x0004,
+ VLine = 0x0005,
+ StyledPanel = 0x0006,
+ PopupPanel = 0x0007,
+ MenuBarPanel = 0x0008,
+ ToolBarPanel = 0x0009,
+ LineEditPanel = 0x000a,
+ TabWidgetPanel = 0x000b,
+ MShape = 0x000f
+ };
+
+ enum Shadow {
+ Plain = 0x0010,
+ Raised = 0x0020,
+ Sunken = 0x0030,
+ MShadow = 0x00f0
+ };
+
+ interface Frame : Widget {
+ void constructor( Widget parent );
+
+ attribute long margin;
+ attribute long linewidth;
+ attribute long midlinewidth;
+
+ attribute long framestyle;
+ attribute Shape frameshape;
+ attribute Shadow frameshadow;
+ };
+
+ /**
+ Some alignmentflags used by various widgets.
+ Taken from Qt. (akrille)
+ */
+ enum Align {
+ AlignAuto=0,
+ AlignLeft=1,
+ AlignRight=2,
+ AlignHCenter=4,
+ AlignJustify=8,
+ AlignTop=16,
+ AlignBottom=32,
+ AlignVCenter=64,
+ AlignCenter=68
+ };
+
+ /**
+ Directions. From Qt. (akrille)
+ */
+ enum Direction { LeftToRight, RightToLeft, TopToBottom, BottomToTop };
+
+ /** The LayoutBox. - Arrange your widgets vertical or horizontal.
+
+ Usage is quite simple: Add the widgets you have in the right order
+ to the layoutbox by calling addWidget().
+ Thats it, no ._addChild or .parent with this widget.
+
+ For more information see QBoxLayout.
+ (akrille)
+ */
+ interface LayoutBox : Frame {
+ /// Sets the direction of the widgets. Can be changed on-the-fly.
+ attribute Direction direction;
+ /// Adds a widget with the stretch-factor and the alignment.
+ void addWidget( Widget widget, long stretch, long align );
+ void addWidget( Widget widget, long stretch );
+ void addWidget( Widget widget );
+ /// Inserts the Widget at the given position
+ void insertWidget( long position, Widget widget, long stretch, long align );
+ void insertWidget( long position, Widget widget, long stretch );
+ void insertWidget( long position, Widget widget );
+ /// Adds a stretch with stretch-factor.
+ void addStretch( long stretch );
+ void addStretch();
+ /// Adds a Spacer the given width or height according your direction.
+ void addSpace( long space );
+ /** [From QBoxLayout]
+ Limits the perpendicular dimension of the box (e.g. height if the
+ box is LeftToRight) to a minimum of size. Other constraints may
+ increase the limit.
+ */
+ void addStrut( long size );
+ /// Adds a separator (a horizontal/vertical line)
+ void addSeparator(long stretch, long align);
+ void addSeparator(long stretch);
+ void addSeparator();
+ /// Adds a line with width and space left/right (top/bottom)
+ void addLine(long width, long space, long stretch, long align);
+ void addLine(long width, long space, long stretch);
+ void addLine(long width, long space);
+ /// The spacing between all widgets.
+ attribute long spacing;
+ /// The margin at the outsideborder.
+ attribute long layoutmargin;
+ };
+
+ /** IMHO (akrille) this should be removed and everywhere replaced with the LayoutBox... */
+ interface HBox : Frame {
+ attribute long spacing;
+ };
+
+ interface VBox : Frame {
+ attribute long spacing;
+ };
+
+ /** The PopupBox. - It can hide the widgets inside or show them as an own window.
+
+ Usage is quite simple: Create the Widget you want to be hidable inside
+ a container like LayoutBox or a normal widget. Then create the PopupBox
+ and call <Name_of_PopupBox>.widget( <Name_of_your_Container> ).
+ Thats it, no ._addChild or .parent with this widget.
+ (akrille)
+ */
+ interface PopupBox : Frame {
+ /// The direction of the PopupBox.
+ attribute Direction direction;
+ /// The name of the box, this gets shown inside the titlebar if its not inside but an own toplevel-widget.
+ attribute string name;
+ /// Sets the widget that is shown/hidden.
+ attribute Widget widget;
+ };
+
+ interface Button : Widget {
+ void constructor( Widget parent );
+ void constructor( string text, Widget parent );
+
+ attribute string text;
+ attribute boolean toggle;
+ readonly attribute boolean pressed;
+ readonly attribute boolean clicked; //clicked( true ) is emitted whenever the button
+ //is pressed an released with the mouse cursor
+ //still above the button
+ };
+
+ interface Poti : Frame {
+ attribute string caption, color;
+ attribute float min, max, value;
+ attribute float logarithmic;
+ attribute long range;
+ };
+
+ interface Fader : Widget {
+ void constructor( Widget parent );
+
+ attribute string caption, color;
+ attribute float min, max, value;
+ attribute float logarithmic;
+ };
+
+ interface LineEdit : Widget {
+ void constructor( Widget parent );
+
+ attribute string caption;
+ attribute string text;
+ };
+
+ interface SpinBox : Widget {
+ void constructor( Widget parent );
+
+ attribute string caption;
+ attribute long min, max, value;
+ };
+
+ interface ComboBox : Widget {
+ void constructor( Widget parent );
+
+ attribute string caption;
+ attribute sequence<string> choices;
+ attribute string value;
+ };
+
+ interface Graph : Widget {
+ void constructor( Widget parent );
+
+ attribute string caption;
+ attribute float minx, maxx, miny, maxy;
+ };
+
+ struct GraphPoint {
+ float x, y;
+ };
+ interface GraphLine {
+ attribute Graph graph;
+ attribute boolean editable;
+ attribute string color;
+ attribute sequence<GraphPoint> points;
+ };
+
+ enum TextBottom { South, North, West, East };
+
+ interface Label : Frame {
+ /// The text to show.
+ attribute string text;
+ /// The alignment of the text. See enum Align
+ attribute long align;
+ /// Fontsize [pixel]
+ /*writeonly*/ attribute long fontsize;
+ /// Fontfamily
+ /*writeonly*/ attribute string fontfamily;
+ /// Direction of the text in normal L-R-mode. Is used to rotate the text accordingly.
+ attribute TextBottom bottom;
+ };
+
+/// Some Notes:
+// - The calculation of the peak is very easy, but it can be changed without changing all styles..
+
+ /** * Styles for the LevelMeter. *
+ In Detail:
+ - lmNormalBars: <count> colored Bars.
+ - lmFireBars: One Bar moving up and down.
+ - lmLineBars: One Bar moving up and down, color depends on the invalue.
+ substyle:
+ - 0: various colors
+ - 1: one color (blue) with clipping (red). The old aRtsControl style.
+ - lmLEDs: <count> but not more than [height|width]/15 LEDs. (not yet implemented)
+ So minimum size of the LED's is 15 pixel.
+ substyle (first tree belong together others are to be or'ed):
+ - 1: Flat
+ - 2: Raised
+ - 3: Sunken
+ - 4: Circular (otherwise Rectangular)
+ - 8: SingleColor (otherwise in colors green/yellow/red)
+ - lmAnalog: An old-style analog VU-Meter. (not yet implemented)
+ - lmSmall: One Bar with the color depending on the invalue.
+ */
+ enum LevelMeterStyle { lmNormalBars, lmFireBars, lmLineBars, lmLEDs, lmAnalog, lmSmall };
+
+ /**
+ One LevelMeter
+ */
+ interface LevelMeter : Frame {
+ /**
+ The Style of the LevelMeter.
+ */
+ attribute LevelMeterStyle style;
+ /**
+ A substyle. Is different on every style;-)
+ */
+ attribute long substyle;
+ /**
+ How many Bars/LEDs it should have. If the size is smaller than
+ this count it will have only that many Bars. So set Count to something
+ very high for smooth vu-meters.
+ Note: not every Style will honor this.
+ Note2: Perhaps this could be integrated into substyle.
+ */
+ attribute long count;
+ /**
+ Sets the peakfalloff. Set to 0 to deactivate peaks.
+ Is used for weighting the average.
+ Peak is evaluated: ( peakfalloff * oldpeak + invalue ) / ( peakfalloff + 1 )
+ */
+ attribute long peakfalloff;
+ /**
+ Minimum and minimum dB for the LevelMeter.
+ Maximum should always be 0dB.
+ */
+ attribute float mindB, maxdB;
+ /**
+ The incomming signal...
+ This attribute always returns 0.0, altough it processes all its input.
+ */
+ /*writeonly*/ attribute float invalue;
+ /**
+ The Direction of the levelmeter from null-value to highest value.
+ */
+ attribute Direction direction;
+ };
+
+ /// Some positions.
+ enum Position { posLeft=0x0001, posRight=0x0002, posTop=0x0004, posBottom=0x0008 };
+
+ /// Tickmarks for LevelMeter / Fader / etc.
+ interface Tickmarks : Frame {
+ void constructor( float min, float max, Direction dir, long pos );
+ /// Minimum/Maximum shown value.
+ attribute float min, max;
+ /// The minimal stepsize for numbers, and substep for smaller marks.
+ attribute float minstep, substep;
+ /// Direction from min to max.
+ attribute Direction direction;
+ /**
+ * The position of the levelmeter/fader/etc which gets the marks.
+ * Only Left/Right are needed. As if direction is Up/Down...
+ * For stereotickmarks set this to posLeft|posRight.
+ */
+ attribute long position;
+ };
+
+ /// A Fader specially for volumes
+ // Maybe something can be ported to the normal Fader.
+ interface VolumeFader : Frame {
+ void constructor( float dbmin, float dbmax, Direction dir );
+ /// Minimum/Maximum values in dB
+ attribute float dbmin, dbmax;
+ /// Direction from min to max
+ attribute Direction direction;
+ /**
+ *The actual volume (calling this value only changes the gui and not the
+ * underlying/connected objects ie. doesn't send a change notification.)
+ */
+ attribute float volume;
+ /// The actual volume in dB (no change notification)
+ attribute float dbvolume;
+ };
+
+ /**
+ * A gui factory is an object that can create a Widget which provides a
+ * user interface for a running object (for instance, you might have an
+ * effect running somewhere, and a gui factory
+ *
+ * TODO: do we need another argument (or other arguments) to specify
+ * style or other parameters?
+ */
+ interface GuiFactory {
+ Widget createGui(object runningObject);
+ };
+
+ /**
+ * Convenience gui factory, which knows how and whether to
+ *
+ * - build a gui out of hints?
+ * - create an artsbuilder built gui?
+ * - create a gui composed of other widgets?
+ * - create a completely custom gui?
+ */
+ interface GenericGuiFactory : GuiFactory {
+ };
+};
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/common/genericguifactory_impl.cc b/arts/gui/common/genericguifactory_impl.cc
new file mode 100644
index 00000000..8a91b54f
--- /dev/null
+++ b/arts/gui/common/genericguifactory_impl.cc
@@ -0,0 +1,56 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsgui.h"
+#include "debug.h"
+
+using namespace Arts;
+using namespace std;
+
+namespace Arts {
+
+class GenericGuiFactory_impl : public GenericGuiFactory_skel {
+public:
+ Widget createGui(Object runningObject);
+};
+REGISTER_IMPLEMENTATION(GenericGuiFactory_impl);
+
+}
+
+Widget GenericGuiFactory_impl::createGui(Object runningObject)
+{
+ Arts::Widget result = Arts::Widget::null();
+ arts_return_val_if_fail(!runningObject.isNull(), result);
+
+ TraderQuery query;
+ query.supports("Interface","Arts::GuiFactory");
+ query.supports("CanCreate",runningObject._interfaceName());
+
+ vector<TraderOffer> *queryResults = query.query();
+ if(queryResults->size())
+ {
+ Arts::GuiFactory factory = SubClass((*queryResults)[0].interfaceName());
+ result = factory.createGui(runningObject);
+ }
+ delete queryResults;
+ return result;
+}
diff --git a/arts/gui/kde/Makefile.am b/arts/gui/kde/Makefile.am
new file mode 100644
index 00000000..5ebb3d0b
--- /dev/null
+++ b/arts/gui/kde/Makefile.am
@@ -0,0 +1,38 @@
+SUBDIRS = mcopclass
+
+lib_LTLIBRARIES = libartsgui_kde.la
+
+check_PROGRAMS = artstestgui dbtest
+
+INCLUDES= -I$(top_builddir)/arts/runtime -I$(top_builddir)/arts/gui/common -I$(arts_includes) $(all_includes)
+
+libartsgui_kde_la_SOURCES = kwidget_impl.cpp kbutton_impl.cpp \
+ kpoti_impl.cpp kpoti.cpp kwidgetrepo.cpp kartswidget.cpp \
+ klineedit_impl.cpp khbox_impl.cpp kvbox_impl.cpp kspinbox_impl.cpp \
+ kcombobox_impl.cpp kfader.cpp kfader_impl.cpp kgraph_impl.cpp kgraph.cpp \
+ kgraphline_impl.cpp kframe_impl.cpp klayoutbox_impl.cpp kpopupbox_impl.cpp \
+ klevelmeter_impl.cpp klevelmeter_small.cpp klevelmeter_linebars.cpp \
+ klevelmeter_normalbars.cpp klevelmeter_firebars.cpp \
+ ktickmarks_impl.cpp kvolumefader_impl.cpp klabel_impl.cpp
+
+libartsgui_kde_la_LIBADD = $(LIB_KIO) -lkdecore -lkdeui \
+ $(top_builddir)/arts/gui/common/libartsgui_idl.la
+libartsgui_kde_la_LDFLAGS = $(all_libraries) -no-undefined
+libartsgui_kde_la_COMPILE_FIRST = ../common/artsgui.h
+
+artstestgui_SOURCES = artstestgui.cpp
+artstestgui_LDADD = -lqtmcop -lkdecore -lartsflow \
+ $(top_builddir)/arts/gui/common/libartsgui_idl.la
+artstestgui_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+dbtest_SOURCES = dbtest.cpp
+dbtest_LDADD = -lqtmcop -lkdecore -lartsflow \
+ $(top_builddir)/arts/gui/kde/libartsgui_kde.la
+dbtest_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+libartsgui_kde_la_METASOURCES = AUTO
+
+####### install header files
+
+artsincludedir = $(includedir)/arts
+artsinclude_HEADERS = kartswidget.h kframe_impl.h kwidget_impl.h kwidgetrepo.h klayoutbox_impl.h
diff --git a/arts/gui/kde/artstestgui.cpp b/arts/gui/kde/artstestgui.cpp
new file mode 100644
index 00000000..92f13163
--- /dev/null
+++ b/arts/gui/kde/artstestgui.cpp
@@ -0,0 +1,124 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsgui.h"
+#include <connect.h>
+#include <qiomanager.h>
+#include <qpushbutton.h>
+#include <kapplication.h>
+#include <objectmanager.h>
+
+using namespace Arts;
+
+int main(int argc, char **argv)
+{
+ QIOManager iomanager;
+ Dispatcher dispatcher(&iomanager);
+ KApplication application(argc, argv, "testgui");
+
+ ObjectManager::the()->provideCapability("kdegui");
+
+ Widget w;
+ w.width(500); w.height(350); w.show();
+
+ Button b;
+ b.parent(w);
+ b.x(10);
+ b.y(10);
+ b.width(100);
+ b.height(20);
+ b.text("Hello World");
+ b.show();
+
+ Button b2;
+ b2.parent(w);
+ b2.x(10);
+ b2.y(30);
+ b2.width(100);
+ b2.height(20);
+ b2.text("More World");
+ b2.show();
+
+ Poti p;
+ p.parent(w);
+ p.x(150);
+ p.y(10);
+ p.caption("delay (ms)");
+ p.color("red");
+ p.min(10);
+ p.max(100);
+ p.value(90);
+ p.show();
+
+ Poti q;
+ q.parent(w);
+ q.x(250);
+ q.y(10);
+ q.caption("delay (ms)");
+ q.color("blue");
+ q.min(10);
+ q.max(100);
+ q.value(90);
+ q.show();
+
+ Graph g;
+ g.parent(w);
+ g.x(50);
+ g.y(70);
+ g.width(400);
+ g.height(230);
+ g.caption("a graph");
+ g.minx(0.0);
+ g.maxx(1.0);
+ g.miny(0.0);
+ g.maxy(1.0);
+ g.show();
+
+ GraphLine gline;
+ gline.graph(g);
+ std::vector<GraphPoint> points;
+ points.push_back(GraphPoint(0, 1.0));
+ points.push_back(GraphPoint(0.5, 1.0));
+ points.push_back(GraphPoint(0.501, 0.0));
+ points.push_back(GraphPoint(1.0, 0.0));
+ gline.points(points);
+ gline.color("red");
+ gline.editable(true);
+
+ /* moves q if p is moved */
+ connect(p,"value_changed",q,"value");
+
+/*
+ connect(q,"value_changed",p,"value");
+
+ With the current assumptions that change notifications make, it is not
+ possible to connect p to q and q to p.
+
+ The reason is that the notification will only be delivered some time
+ in the future (due to the requirement that they are asynchronous, i.e.
+ do not cause the sender to block) - but this means that two different
+ change notifications (i.e. changed to 1.0, changed to 2.0) may move
+ in a cycle between the two objects p and q, if p and q are cross
+ connected. So don't cross connect ;).
+*/
+ return application.exec();
+}
diff --git a/arts/gui/kde/dbtest.cpp b/arts/gui/kde/dbtest.cpp
new file mode 100644
index 00000000..164b3630
--- /dev/null
+++ b/arts/gui/kde/dbtest.cpp
@@ -0,0 +1,42 @@
+
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kaboutdata.h>
+#include <klocale.h>
+#include <kstdaction.h>
+#include <kactioncollection.h>
+#include <kdebug.h>
+
+#include <dbtest.h>
+#include <dbtest.moc>
+
+dBTestWidget::dBTestWidget( QWidget* p, const char* n ) : QWidget( p,n ), dB2VolCalc( -24,6 ) {
+kdDebug() << k_funcinfo << endl;
+ ( void* ) KStdAction::quit( this, SLOT( close() ), new KActionCollection( this ) );
+
+ for ( float i=0; i<=1; i+=0.25 )
+ kdDebug() << i << " : " << amptodb( i ) << "dB" <<endl;
+
+ for ( int i=-24; i<=0; i++ )
+ kdDebug() << i <<"db : " << dbtoamp( i ) << endl;
+}
+dBTestWidget::~dBTestWidget() {
+kdDebug() << k_funcinfo << endl;
+}
+
+int main( int argc, char* argv[] ) {
+
+ KAboutData aboutData( "dbtest", I18N_NOOP( "dBTest" ),
+ "0.1", "", KAboutData::License_GPL,
+ "(c) 2002, Arnold Krille" );
+ aboutData.addAuthor( "Arnold Krille", I18N_NOOP( "Creator" ), "arnold@arnoldarts.de");
+ KCmdLineArgs::init( argc, argv, &aboutData );
+
+ KApplication app;
+
+ dBTestWidget* w = new dBTestWidget( 0 );
+ w->show();
+ app.setMainWidget( w );
+
+ return app.exec();
+}
diff --git a/arts/gui/kde/dbtest.h b/arts/gui/kde/dbtest.h
new file mode 100644
index 00000000..e7d996b0
--- /dev/null
+++ b/arts/gui/kde/dbtest.h
@@ -0,0 +1,16 @@
+
+#ifndef DBTESTWIDGET_H
+#define DBTESTWIDGET_H
+
+#include <qwidget.h>
+
+#include "dbvolcalc.h"
+
+class dBTestWidget : public QWidget, public dB2VolCalc {
+ Q_OBJECT
+public:
+ dBTestWidget( QWidget*, const char* =0 );
+ ~dBTestWidget();
+};
+
+#endif
diff --git a/arts/gui/kde/dbvolcalc.h b/arts/gui/kde/dbvolcalc.h
new file mode 100644
index 00000000..8d99bf59
--- /dev/null
+++ b/arts/gui/kde/dbvolcalc.h
@@ -0,0 +1,82 @@
+/*
+ Copyright ( C ) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation;
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef ARTS_DB2VOL_CALC_H
+#define ARTS_DB2VOL_CALC_H
+
+#include <math.h>
+
+class dB2VolCalc {
+private:
+ float _base;
+public:
+ dB2VolCalc( float _dbmin, float _dbmax )
+ : _base( 6/log10( double(2) ) ) // Depends on what you measure: intensity(10), pressure(20), old artscontrol(6/lg2 (near 20))
+ , dbmax( _dbmax )
+ , dbmin( _dbmin )
+ {}
+
+ float dbmax, dbmin;
+ /**
+ Logarithmic/decimal valuation:
+ p = ampfactor ( linear )
+ db = dezibel ( logarithmic )
+ p = 10^( d/10 )
+ db = 10*lg( p )
+
+ artscontrol was using
+ db = 6/ln( 2 ) * ln( p )
+ which would be
+ db = 6/lg( 2 ) * lg( p )
+ */
+ float amptodb( float p ) {
+ float db = _base*log10( p );
+ if ( db < dbmin ) db = dbmin;
+ if ( db > dbmax ) db = dbmax;
+ return db;
+ }
+ float dbtoamp( float db ) {
+ float amp = pow( 10, db/_base );
+ if ( amp <= pow( 10, dbmin/_base ) ) amp = 0;
+ return amp;
+ }
+ /// With ndb = normalized dB (between 0 and 1)
+ float amptondb( float p ) {
+ return dbtondb( amptodb( p ) ); //- dbmin ) / ( dbmax - dbmin );
+ }
+ float ndbtoamp( float ndb ) {
+ return dbtoamp( ndb * ( dbmax - dbmin ) + dbmin );
+ }
+ /// Normalizes a dezibel value.
+ float dbtondb( float db ) {
+ return ( db - dbmin )/( dbmax - dbmin );
+ }
+ /// Normalizes a volume to a logarithmic value between 0 and 1.
+ float dbtovol( float db ) {
+ return ( db -dbmin )/( 0-dbmin );
+ }
+ /// Unnormalizes a dezibel value.
+ float ndbtodb( float ndb ) {
+ return ( ndb * ( dbmax-dbmin ) +dbmin );
+ }
+};
+
+#endif
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kartswidget.cpp b/arts/gui/kde/kartswidget.cpp
new file mode 100644
index 00000000..78f3bc3f
--- /dev/null
+++ b/arts/gui/kde/kartswidget.cpp
@@ -0,0 +1,89 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ stefan@space.twc.de
+ 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "kartswidget.h"
+#include "kwidgetrepo.h"
+#include "debug.h"
+#include "qlayout.h"
+
+class KArtsWidgetPrivate {
+public:
+ QHBoxLayout *layout;
+};
+
+KArtsWidget::KArtsWidget( QWidget* parent, const char* name )
+ :QWidget( parent, name ), _content( Arts::Widget::null() )
+{
+ d = new KArtsWidgetPrivate;
+ d->layout = new QHBoxLayout(this);
+}
+
+KArtsWidget::KArtsWidget( Arts::Widget content, QWidget* parent, const char* name )
+ :QWidget( parent, name ), _content( Arts::Widget::null())
+{
+ d = new KArtsWidgetPrivate;
+ d->layout = new QHBoxLayout(this);
+ setContent(content);
+}
+
+KArtsWidget::KArtsWidget( Arts::Widget content, QWidget* parent, const char* name, WFlags wflags )
+ :QWidget( parent, name, wflags ), _content( Arts::Widget::null() )
+{
+ d = new KArtsWidgetPrivate;
+ d->layout = new QHBoxLayout( this );
+ setContent( content );
+}
+
+KArtsWidget::KArtsWidget(QWidget* parent, const char* name, WFlags wflags )
+ :QWidget(parent, name, wflags ), _content(Arts::Widget::null())
+{
+ d = new KArtsWidgetPrivate;
+ d->layout = new QHBoxLayout(this);
+}
+
+KArtsWidget::~KArtsWidget()
+{
+ QWidget *contentAsWidget
+ = KWidgetRepo::the()->lookupQWidget(_content.widgetID());
+ contentAsWidget->reparent(0,QPoint(0,0),_content.visible());
+ delete d;
+ d = 0;
+}
+
+/* TODO: change content (reparent old widget away, reparent new widget here) */
+void KArtsWidget::setContent(Arts::Widget content)
+{
+ arts_return_if_fail(!content.isNull());
+ QWidget *contentAsWidget
+ = KWidgetRepo::the()->lookupQWidget(content.widgetID());
+ arts_return_if_fail(contentAsWidget != 0);
+
+ _content = content;
+ contentAsWidget->reparent(this,QPoint(0,0),content.visible());
+ d->layout->addWidget(contentAsWidget);
+}
+
+Arts::Widget KArtsWidget::content()
+{
+ return _content;
+}
diff --git a/arts/gui/kde/kartswidget.h b/arts/gui/kde/kartswidget.h
new file mode 100644
index 00000000..049e4657
--- /dev/null
+++ b/arts/gui/kde/kartswidget.h
@@ -0,0 +1,92 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ stefan@space.twc.de
+ 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_GUI_KARTSWIDGET_H
+#define ARTS_GUI_KARTSWIDGET_H
+
+#include <qwidget.h>
+#include "artsgui.h"
+#include <kdelibs_export.h>
+class KArtsWidgetPrivate;
+
+/**
+ * KArtsWidget provides a simple way to treat Arts::Widget classes like
+ * native Qt widgets. Suppose you use Qt, and want to put an Arts::Widget
+ * type into a layout, you can do so using this code
+ *
+ * <pre>
+ * Arts::Widget widget = ...get widget from somewhere...;
+ * KArtsWidget *w = new KArtsWidget(widget, this);
+ * layout->addWidget(w);
+ * </pre>
+ *
+ * In line 2 of the code, the "this" is the parent widget (which is usually
+ * this in Qt code).
+ *
+ * The KArtsWidget class keeps a reference to the content widget, so the
+ * content widget will not be freed until the KArtsWidget gets destroyed.
+ */
+class KDE_EXPORT KArtsWidget : public QWidget {
+private:
+ KArtsWidgetPrivate *d;
+
+protected:
+ Arts::Widget _content;
+
+public:
+ /**
+ * creates a new KArtsWidget
+ */
+ KArtsWidget( QWidget* parent, const char* name );
+
+ /**
+ * creates a new KArtsWidget and sets the content to an Arts::Widget
+ */
+ KArtsWidget( Arts::Widget content, QWidget* parent, const char* name );
+
+ /**
+ * creates a new KArtsWidget with WidgetFlags and content
+ *
+ * BCI: should replace the above in the next major release. ( akrille )
+ */
+ KArtsWidget( Arts::Widget, QWidget* =0, const char* =0, WFlags =0 );
+ // same without the content
+ KArtsWidget( QWidget* =0, const char* =0, WFlags =0 );
+
+ /**
+ * destructor
+ */
+ ~KArtsWidget();
+
+ /**
+ * sets the content to a new Arts::Widget
+ */
+ void setContent(Arts::Widget content);
+
+ /**
+ * gets the content widget
+ */
+ Arts::Widget content();
+};
+
+#endif /* ARTS_GUI_KARTSWIDGET_H*/
diff --git a/arts/gui/kde/kbutton_impl.cpp b/arts/gui/kde/kbutton_impl.cpp
new file mode 100644
index 00000000..eca30fcd
--- /dev/null
+++ b/arts/gui/kde/kbutton_impl.cpp
@@ -0,0 +1,135 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+ 2001, 2002 Matthias Kretz
+ kretz@kde.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "kbutton_impl.h"
+#include "kbutton_impl.moc"
+
+using namespace Arts;
+using namespace std;
+
+KButtonMapper::KButtonMapper( KButton_impl *_impl, QPushButton *but )
+ : QObject( but, "KButtonMapper" )
+ , impl( _impl )
+ , button( but )
+{
+ connect( but, SIGNAL( pressed() ), this, SLOT( pressed() ) );
+ connect( but, SIGNAL( released() ), this, SLOT( released() ) );
+ connect( but, SIGNAL( toggled( bool ) ), this, SLOT( toggled( bool ) ) );
+ connect( but, SIGNAL( clicked() ), this, SLOT( clicked() ) );
+}
+
+void KButtonMapper::pressed()
+{
+ if( ! button->isToggleButton() )
+ impl->changeState(true);
+}
+
+void KButtonMapper::released()
+{
+ if( ! button->isToggleButton() )
+ impl->changeState(false);
+}
+
+void KButtonMapper::toggled( bool b )
+{
+ if( button->isToggleButton() )
+ impl->changeState( b );
+}
+
+void KButtonMapper::clicked()
+{
+ impl->emitClicked();
+}
+
+KButton_impl::KButton_impl( QPushButton * widget )
+ : KWidget_impl( widget ? widget : new QPushButton( 0 ) )
+ , _clicked( false )
+{
+ _qpushbutton = static_cast<QPushButton*>( _qwidget );
+ ( void )new KButtonMapper( this, _qpushbutton );
+}
+
+void KButton_impl::constructor( Widget p )
+{
+ parent( p );
+}
+
+void KButton_impl::constructor( const string & t, Widget p )
+{
+ parent( p );
+ text( t );
+}
+
+void KButton_impl::emitClicked()
+{
+ _clicked = true;
+ clicked_changed( true );
+}
+
+string KButton_impl::text()
+{
+ return _qpushbutton->text().utf8().data();
+}
+
+void KButton_impl::text(const string& newText)
+{
+ _qpushbutton->setText(QString::fromUtf8(newText.c_str()));
+}
+
+bool KButton_impl::toggle()
+{
+ return _qpushbutton->isToggleButton();
+}
+
+void KButton_impl::toggle(bool newToggle)
+{
+ _qpushbutton->setToggleButton(newToggle);
+}
+
+bool KButton_impl::pressed()
+{
+ if( _qpushbutton->isToggleButton() )
+ return _qpushbutton->isOn();
+ else
+ return _qpushbutton->isDown();
+}
+
+bool KButton_impl::clicked()
+{
+ if( _clicked )
+ {
+ _clicked = false;
+ return true;
+ }
+ return false;
+}
+
+void KButton_impl::changeState(bool newState)
+{
+ pressed_changed(newState);
+}
+
+REGISTER_IMPLEMENTATION(KButton_impl);
+
+// vim:sw=4:ts=4
diff --git a/arts/gui/kde/kbutton_impl.h b/arts/gui/kde/kbutton_impl.h
new file mode 100644
index 00000000..2c3c7163
--- /dev/null
+++ b/arts/gui/kde/kbutton_impl.h
@@ -0,0 +1,76 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+ 2001, 2002 Matthias Kretz
+ kretz@kde.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_GUI_KBUTTON_IMPL_H
+#define ARTS_GUI_KBUTTON_IMPL_H
+#include "kwidget_impl.h"
+#include <qpushbutton.h>
+
+
+namespace Arts {
+class KButton_impl;
+class KButtonMapper : public QObject {
+ Q_OBJECT
+ KButton_impl *impl;
+ QPushButton * button;
+public:
+ KButtonMapper(KButton_impl *impl, QPushButton *but);
+protected slots:
+ void pressed();
+ void released();
+ void toggled( bool );
+ void clicked();
+};
+
+class KButton_impl : virtual public Arts::Button_skel,
+ public Arts::KWidget_impl
+{
+private:
+ bool _clicked;
+
+protected:
+ QPushButton * _qpushbutton;
+
+public:
+ KButton_impl( QPushButton * w = 0 );
+ void constructor( Widget parent );
+ void constructor( const std::string &, Widget );
+
+ void emitClicked();
+
+ std::string text();
+ void text(const std::string& newCaption);
+
+ bool toggle();
+ void toggle(bool);
+
+ bool pressed();
+ bool clicked();
+ void changeState(bool);
+};
+
+}
+#endif //ARTS_GUI_KBUTTON_IMPL_H
+
+// vim:sw=4:ts=4
diff --git a/arts/gui/kde/kcombobox_impl.cpp b/arts/gui/kde/kcombobox_impl.cpp
new file mode 100644
index 00000000..dfa4e637
--- /dev/null
+++ b/arts/gui/kde/kcombobox_impl.cpp
@@ -0,0 +1,105 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+ 2001, 2002 Matthias Kretz
+ kretz@kde.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "kcombobox_impl.h"
+#include "kcombobox_impl.moc"
+#include "anyref.h"
+#include "stdio.h"
+
+using namespace Arts;
+using namespace std;
+
+ComboBoxIntMapper::ComboBoxIntMapper(KComboBox_impl *impl, KComboBox *co)
+ : QObject( co, "map Qt signal to aRts" )
+ ,impl(impl)
+{
+ connect(co, SIGNAL(activated(const QString &)), this, SLOT(activated(const QString &)));
+}
+
+void ComboBoxIntMapper::activated(const QString & newValue)
+{
+ impl->value(string(newValue.utf8().data()));
+}
+
+KComboBox_impl::KComboBox_impl( KComboBox * widget )
+ : KWidget_impl( widget ? widget : new KComboBox )
+{
+ _kcombobox = static_cast<KComboBox*>( _qwidget );
+ ( void )new ComboBoxIntMapper( this, _kcombobox );
+}
+
+string KComboBox_impl::caption()
+{
+ return m_caption.utf8().data();
+}
+
+void KComboBox_impl::caption(const string& newCaption)
+{
+ m_caption = QString::fromUtf8(newCaption.c_str());
+ // FIXME: do something with the caption here
+}
+
+vector<string> * KComboBox_impl::choices()
+{
+ return new vector<string>(m_choices);
+}
+
+void KComboBox_impl::choices(const vector<string> & newChoices)
+{
+ if(newChoices != m_choices)
+ {
+ m_choices = newChoices;
+ _kcombobox->clear();
+ for(vector<string>::const_iterator it = m_choices.begin(); it != m_choices.end(); ++it)
+ {
+ _kcombobox->insertItem(QString::fromUtf8(it->c_str()));
+ }
+ if(visible())
+ choices_changed(newChoices);
+ }
+}
+
+string KComboBox_impl::value()
+{
+ return m_value.utf8().data();
+}
+
+void KComboBox_impl::value(const string & newValue)
+{
+ if(newValue != m_value.utf8().data())
+ {
+ m_value = QString::fromUtf8(newValue.c_str());
+ for(unsigned int i = 0; i < m_choices.size(); ++i)
+ {
+ if(m_choices[i] == newValue)
+ _kcombobox->setCurrentItem(i);
+ }
+ if(visible())
+ value_changed(newValue);
+ }
+}
+
+REGISTER_IMPLEMENTATION(KComboBox_impl);
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kcombobox_impl.h b/arts/gui/kde/kcombobox_impl.h
new file mode 100644
index 00000000..83222e3b
--- /dev/null
+++ b/arts/gui/kde/kcombobox_impl.h
@@ -0,0 +1,74 @@
+ /*
+
+ Copyright (C) 2000,2001 Stefan Westerfeld
+ stefan@space.twc.de
+ 2001, 2002 Matthias Kretz
+ kretz@kde.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_GUI_KCOMBOBOX_IMPL_H
+#define ARTS_GUI_KCOMBOBOX_IMPL_H
+#include "kwidget_impl.h"
+#include <kcombobox.h>
+#include <qobject.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+
+namespace Arts {
+
+class KComboBox_impl;
+class ComboBoxIntMapper :public QObject {
+ Q_OBJECT
+ KComboBox_impl *impl;
+public:
+ ComboBoxIntMapper(KComboBox_impl *impl, KComboBox *co);
+public slots:
+ void activated(const QString &);
+};
+
+class KComboBox_impl : virtual public Arts::ComboBox_skel,
+ public Arts::KWidget_impl
+{
+protected:
+ KComboBox * _kcombobox;
+ QString m_caption;
+ QString m_value;
+ std::vector<std::string> m_choices;
+
+ void applyValue();
+
+public:
+ KComboBox_impl( KComboBox * w = 0 );
+ void constructor( Widget p ) { parent( p ); }
+
+ std::string caption();
+ void caption(const std::string & newCaption);
+
+ std::vector<std::string> * choices();
+ void choices(const std::vector<std::string> & newChoices);
+
+ std::string value();
+ void value(const std::string & newValue);
+};
+
+}
+#endif /* ARTS_GUI_KCOMBOBOX_IMPL_H */
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kfader.cpp b/arts/gui/kde/kfader.cpp
new file mode 100644
index 00000000..2978dd0c
--- /dev/null
+++ b/arts/gui/kde/kfader.cpp
@@ -0,0 +1,31 @@
+#include "kfader.h"
+#include "kfader.moc"
+
+void KFader::init()
+{
+}
+
+KFader::KFader( QWidget * parent, const char * name )
+ : QSlider( Qt::Vertical, parent, name )
+{
+ init();
+}
+
+KFader::KFader( int minValue, int maxValue, int pageStep, int value, QWidget * parent, const char * name )
+ : QSlider( minValue, maxValue, pageStep, value, Qt::Vertical, parent, name )
+{
+ init();
+}
+
+KFader::~KFader()
+{
+}
+
+void KFader::setColor( QColor & )
+{
+}
+
+void KFader::setRange( int minValue, int maxValue )
+{
+ QRangeControl::setRange( minValue, maxValue );
+}
diff --git a/arts/gui/kde/kfader.h b/arts/gui/kde/kfader.h
new file mode 100644
index 00000000..fcf53f21
--- /dev/null
+++ b/arts/gui/kde/kfader.h
@@ -0,0 +1,46 @@
+ /*
+
+ Copyright (C) 2001 Matthias Kretz
+ kretz@kde.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef _KFADER_H
+#define _KFADER_H
+
+#include <qslider.h>
+
+class KFader : public QSlider
+{
+ Q_OBJECT
+ protected:
+ void init();
+ public:
+ KFader( QWidget * parent = 0, const char * name = 0 );
+ KFader( int minValue, int maxValue, int pageStep, int value, QWidget * parent = 0, const char * name = 0 );
+
+ virtual ~KFader();
+
+ void setColor( QColor & );
+
+ virtual void setRange( int, int );
+};
+
+#endif /* KFADER_H */
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kfader_impl.cpp b/arts/gui/kde/kfader_impl.cpp
new file mode 100644
index 00000000..3304f3d3
--- /dev/null
+++ b/arts/gui/kde/kfader_impl.cpp
@@ -0,0 +1,206 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+ 2001, 2002 Matthias Kretz
+ kretz@kde.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "kfader_impl.h"
+#include "kfader_impl.moc"
+#include "anyref.h"
+#include "stdio.h"
+
+#include <math.h>
+
+using namespace Arts;
+using namespace std;
+
+FaderIntMapper::FaderIntMapper(KFader_impl *impl, KFader *kp) :impl(impl)
+{
+ connect(kp, SIGNAL(valueChanged(int)), this, SLOT(valueChanged(int)));
+}
+
+void FaderIntMapper::valueChanged(int pos)
+{
+ impl->valueChanged(pos);
+}
+
+KFader_impl::KFader_impl( KFader * widget )
+ : KWidget_impl( widget ? widget : new KFader( 0, 100, 1, 0 ) )
+{
+ _min = 0; _max = 1; _value = 0;
+ _factor = 1;
+ _logarithmic = 0;
+ _range = 100;
+
+ _kfader = static_cast<KFader*>( _qwidget );
+ _kfader->setMinimumWidth( 40 );
+ _kfader->setMinimumHeight( 100 );
+ new FaderIntMapper( this, _kfader );
+}
+
+string KFader_impl::caption()
+{
+ return _caption.utf8().data();
+}
+
+void KFader_impl::caption(const string& newText)
+{
+ _caption = QString::fromUtf8(newText.c_str());
+ _kfader->setName(_caption.utf8().data());
+}
+
+string KFader_impl::color()
+{
+ return _color;
+}
+
+void KFader_impl::color(const string& newColor)
+{
+ _color = newColor;
+ if(strlen(_color.c_str()))
+ {
+ QColor qc(_color.c_str());
+ _kfader->setColor(qc);
+ }
+}
+
+float KFader_impl::min()
+{
+ return _min;
+}
+
+void KFader_impl::min(float newMin)
+{
+ if(_min != newMin)
+ {
+ _min = newMin;
+ applyValue();
+ }
+}
+
+float KFader_impl::max()
+{
+ return _max;
+}
+
+void KFader_impl::max(float newMax)
+{
+ if(_max != newMax)
+ {
+ _max = newMax;
+ applyValue();
+ }
+}
+
+float KFader_impl::value()
+{
+ //float ret = ( _max + _min - float(_kfader->value()) ) / _factor;
+ //if(_logarithmic > 0)
+ //ret = convertFromLog(ret);
+ float ret = _value;
+ if(ret < _min)
+ ret = _min;
+ else if(ret > _max)
+ ret = _max;
+ return ret;
+}
+
+void KFader_impl::value(float newValue)
+{
+ if(newValue != _value)
+ {
+ _value = newValue;
+ applyValue();
+ if(visible())
+ value_changed(value());
+ }
+}
+
+long KFader_impl::range()
+{
+ return _range;
+}
+
+void KFader_impl::range(long newRange)
+{
+ if(_range != newRange)
+ {
+ _range = newRange;
+ applyValue();
+ }
+}
+
+void KFader_impl::valueChanged(int newvalue)
+{
+ _value = float(newvalue) / _factor;
+ if(_logarithmic > 0)
+ _value = convertFromLog(_value);
+ _value = _max + _min - _value;
+ if(visible())
+ value_changed(value());
+}
+
+float KFader_impl::convertToLog(float val)
+{
+ return log(val) / log(_logarithmic);
+}
+
+float KFader_impl::convertFromLog(float val)
+{
+ return pow(_logarithmic, val);
+}
+
+void KFader_impl::applyValue()
+{
+ double dmin = _min;
+ double dmax = _max;
+ double dvalue = _value;
+ if(_logarithmic > 0)
+ {
+ dmin = convertToLog(_min);
+ dmax = convertToLog(_max);
+ dvalue = convertToLog(_value);
+ }
+ _factor = _range / (dmax - dmin);
+ int imin = int(_factor * dmin);
+ int imax = int(_factor * dmax);
+ int ivalue = int(_factor * (dmax + dmin - dvalue));
+ _kfader->setRange(imin, imax);
+ _kfader->setValue(ivalue);
+}
+
+void KFader_impl::logarithmic(float newLogarithmic)
+{
+ if(_logarithmic != newLogarithmic)
+ {
+ _logarithmic = newLogarithmic;
+ applyValue();
+ }
+}
+
+float KFader_impl::logarithmic()
+{
+ return _logarithmic;
+}
+
+REGISTER_IMPLEMENTATION(KFader_impl);
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kfader_impl.h b/arts/gui/kde/kfader_impl.h
new file mode 100644
index 00000000..55419cff
--- /dev/null
+++ b/arts/gui/kde/kfader_impl.h
@@ -0,0 +1,92 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+ 2001, 2002 Matthias Kretz
+ kretz@kde.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_GUI_KFADER_IMPL_H
+#define ARTS_GUI_KFADER_IMPL_H
+#include "kwidget_impl.h"
+#include "kfader.h"
+
+#include <qobject.h>
+#include <qstring.h>
+
+
+namespace Arts {
+
+class KFader_impl;
+
+class FaderIntMapper :public QObject {
+ Q_OBJECT
+ KFader_impl *impl;
+public:
+ FaderIntMapper(KFader_impl *impl, KFader *kp);
+public slots:
+ void valueChanged(int x);
+};
+
+class KFader_impl : virtual public Arts::Fader_skel,
+ public Arts::KWidget_impl
+{
+protected:
+ KFader * _kfader;
+ QString _caption;
+ std::string _color;
+ float _min, _max, _value;
+ float _factor;
+ float _logarithmic;
+ long _range;
+
+ float convertToLog(float);
+ float convertFromLog(float);
+ void applyValue();
+
+public:
+ KFader_impl( KFader * w = 0 );
+ void constructor( Widget p ) { parent( p ); }
+
+ std::string caption();
+ void caption(const std::string& newText);
+ std::string color();
+ void color(const std::string& newColor);
+
+ float logarithmic();
+ void logarithmic(float);
+
+ float min();
+ void min(float newMin);
+ float max();
+ void max(float newMax);
+ float value();
+ void value(float newValue);
+
+ long range();
+ void range(long newRange);
+
+ /* from qt */
+ void valueChanged(int newValue);
+};
+
+}
+#endif /* ARTS_GUI_KFADER_IMPL_H */
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kframe_impl.cpp b/arts/gui/kde/kframe_impl.cpp
new file mode 100644
index 00000000..a2fc6d8c
--- /dev/null
+++ b/arts/gui/kde/kframe_impl.cpp
@@ -0,0 +1,98 @@
+/*
+ Copyright (C) 2002 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+// $Id$
+
+#include "kframe_impl.h"
+#include <qframe.h>
+#include <debug.h>
+#include <stdio.h>
+
+using namespace Arts;
+using namespace std;
+
+KFrame_impl::KFrame_impl( QFrame * widget )
+ : KWidget_impl( widget ? widget : new QFrame )
+{
+ _qframe = static_cast<QFrame*>( _qwidget );
+ assert( _qframe );
+}
+
+long KFrame_impl::margin()
+{
+ return _qframe->margin();
+}
+
+void KFrame_impl::margin( long m )
+{
+ _qframe->setMargin( m );
+}
+
+long KFrame_impl::linewidth()
+{
+ return _qframe->lineWidth();
+}
+
+void KFrame_impl::linewidth( long lw )
+{
+ _qframe->setLineWidth( lw );
+}
+
+long KFrame_impl::midlinewidth()
+{
+ return _qframe->midLineWidth();
+}
+
+void KFrame_impl::midlinewidth( long mlw )
+{
+ _qframe->setMidLineWidth( mlw );
+}
+
+long KFrame_impl::framestyle()
+{
+ return _qframe->frameStyle();
+}
+
+void KFrame_impl::framestyle( long fs )
+{
+ _qframe->setFrameStyle( fs );
+}
+
+Shape KFrame_impl::frameshape()
+{
+ return ( Shape )_qframe->frameShape();
+}
+
+void KFrame_impl::frameshape( Shape fs )
+{
+ _qframe->setFrameShape( ( QFrame::Shape )fs );
+}
+
+Shadow KFrame_impl::frameshadow()
+{
+ return ( Shadow )_qframe->frameShadow();
+}
+
+void KFrame_impl::frameshadow( Shadow fs )
+{
+ _qframe->setFrameShadow( ( QFrame::Shadow )fs );
+}
+
+REGISTER_IMPLEMENTATION(KFrame_impl);
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kframe_impl.h b/arts/gui/kde/kframe_impl.h
new file mode 100644
index 00000000..3b4bcb69
--- /dev/null
+++ b/arts/gui/kde/kframe_impl.h
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 2002 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+// $Id$
+
+#ifndef KFRAME_IMPL
+#define KFRAME_IMPL
+#include "kwidget_impl.h"
+#include <kdelibs_export.h>
+class QFrame;
+
+namespace Arts {
+
+class KDE_EXPORT KFrame_impl : virtual public Arts::Frame_skel,
+ public Arts::KWidget_impl
+{
+ protected:
+ QFrame * _qframe;
+
+ public:
+ KFrame_impl( QFrame * widget = 0 );
+ inline void constructor( Widget p ) { parent( p ); }
+
+ long margin();
+ void margin( long m );
+ long linewidth();
+ void linewidth( long lw );
+ long midlinewidth();
+ void midlinewidth( long mlw );
+ long framestyle();
+ void framestyle( long fs );
+ Shape frameshape();
+ void frameshape( Shape fs );
+ Shadow frameshadow();
+ void frameshadow( Shadow fs );
+}; //class
+} //namespace
+
+// vim: sw=4 ts=4
+#endif
diff --git a/arts/gui/kde/kgraph.cpp b/arts/gui/kde/kgraph.cpp
new file mode 100644
index 00000000..e7797692
--- /dev/null
+++ b/arts/gui/kde/kgraph.cpp
@@ -0,0 +1,266 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "kgraph.h"
+#include "kgraph.moc"
+#include "kgraphline_impl.h"
+#include "qpainter.h"
+
+#include <cstdlib>
+#include <math.h>
+
+using namespace Arts;
+using namespace std;
+
+KGraph::KGraph( QWidget * parent, const char * name )
+ : QWidget( parent, name )
+{
+ setBackgroundColor(white);
+
+ selectedIndex = -1;
+ minx = miny = 0.0;
+ maxx = maxy = 1.0;
+}
+
+KGraph::~KGraph()
+{
+}
+
+void KGraph::addLine(Arts::KGraphLine_impl *line)
+{
+ lines.push_back(line);
+}
+
+void KGraph::redrawLine(Arts::KGraphLine_impl * /*line*/)
+{
+ repaint();
+}
+
+void KGraph::removeLine(Arts::KGraphLine_impl *line)
+{
+ if(line == selectedLine)
+ {
+ selectedLine = 0;
+ selectedIndex = -1;
+ }
+ lines.remove(line);
+}
+
+inline QPoint KGraph::g2qPoint(const GraphPoint &gp)
+{
+ return QPoint(int(((gp.x - minx)/(maxx-minx)) * (width()-1)),
+ int((1.0 - (gp.y - miny)/(maxy-miny)) * (height()-1)));
+}
+
+inline GraphPoint KGraph::q2gPoint(const QPoint &qp)
+{
+ return GraphPoint((float(qp.x())/float(width()-1)) * (maxx-minx) + minx,
+ (1.0 - (float(qp.y())/float(height()-1))) * (maxy-miny) + miny);
+}
+
+void KGraph::paintEvent( QPaintEvent *e )
+{
+ QPainter painter(this);
+ painter.setClipRect(e->rect());
+
+ std::list<KGraphLine_impl *>::iterator li;
+ for(li = lines.begin(); li != lines.end(); li++)
+ {
+ KGraphLine_impl *gline = *li;
+
+ vector<GraphPoint>::iterator pi;
+ QPoint lastp;
+ bool first = true;
+
+ painter.setPen(gline->_color.c_str());
+
+ for(pi = gline->_points.begin(); pi != gline->_points.end(); pi++)
+ {
+ QPoint p = g2qPoint(*pi);
+
+ if(!first)
+ painter.drawLine(lastp,p);
+
+ if(gline->_editable)
+ painter.drawEllipse(p.x()-3,p.y()-3,7,7);
+
+ lastp = p;
+ first = false;
+ }
+ }
+}
+
+void KGraph::mousePressEvent(QMouseEvent *e)
+{
+ if(e->button() == LeftButton || e->button() == RightButton)
+ {
+ std::list<KGraphLine_impl *>::iterator li;
+ for(li = lines.begin(); li != lines.end(); li++)
+ {
+ KGraphLine_impl *gline = *li;
+
+ vector<GraphPoint>::iterator pi;
+ int index = 0;
+ for(pi = gline->_points.begin(); pi != gline->_points.end(); pi++, index++)
+ {
+ QPoint p = g2qPoint(*pi);
+
+ int dx = e->x() - p.x();
+ int dy = e->y() - p.y();
+
+ if(::sqrt(double(dx*dx + dy*dy)) < 5.0)
+ {
+ selectedIndex = index;
+ selectedLine = gline;
+ selectedPoint = *pi;
+ }
+ }
+ }
+ }
+
+ if(selectedIndex >= 0)
+ {
+ // erase point
+ if(e->button() == RightButton)
+ {
+ if(selectedIndex != 0 && selectedIndex != (( int )( selectedLine->_points.size() )-1))
+ {
+ vector<GraphPoint> points;
+
+ for(int i=0;i<( int )selectedLine->_points.size();i++)
+ {
+ if(selectedIndex != i)
+ points.push_back(selectedLine->_points[i]);
+ }
+
+ selectedLine->points(points);
+ }
+
+ selectedLine = 0;
+ selectedIndex = -1;
+ }
+ }
+ else if(e->button() == LeftButton)
+ {
+ // try to insert a point
+ std::list<KGraphLine_impl *>::iterator li;
+ for(li = lines.begin(); li != lines.end(); li++)
+ {
+ KGraphLine_impl *gline = *li;
+
+ QPoint lastp;
+ bool first = true;
+
+ vector<GraphPoint>::iterator pi;
+ int index = 0;
+ for(pi = gline->_points.begin(); pi != gline->_points.end(); pi++, index++)
+ {
+ QPoint p = g2qPoint(*pi);
+
+ if(!first && (e->x() > lastp.x()+2) && (e->x() < p.x()-2))
+ {
+ float pos = float(e->x()-lastp.x())/float(p.x()-lastp.x());
+ int y = (int)((1.0-pos) * lastp.y() + pos * p.y());
+
+ if(abs(y-e->y()) < 5)
+ {
+ GraphPoint gp = q2gPoint(QPoint(e->x(),y));
+ vector<GraphPoint> newPoints;
+
+ for(int i=0;i<( int )gline->_points.size();i++)
+ {
+ if(index == i)
+ newPoints.push_back(gp);
+ newPoints.push_back(gline->_points[i]);
+ }
+ gline->points(newPoints);
+
+ selectedLine = gline;
+ selectedIndex = index;
+ selectedPoint = gp;
+
+ return;
+ }
+ }
+ lastp = p;
+ first = false;
+ }
+ }
+ }
+}
+
+void KGraph::mouseMoveEvent(QMouseEvent *e)
+{
+ QPoint pos = e->pos();
+
+ if(pos.x() < 0) pos.setX(0);
+ if(pos.y() < 0) pos.setY(0);
+ if(pos.x() >= width()) pos.setX(width()-1);
+ if(pos.y() >= height()) pos.setY(height()-1);
+
+ if(selectedIndex >= 0)
+ {
+ vector<GraphPoint> points(selectedLine->_points);
+
+
+ if((( int )points.size() <= selectedIndex)
+ || (fabs(selectedPoint.x-points[selectedIndex].x) > 0.000001)
+ || (fabs(selectedPoint.y-points[selectedIndex].y) > 0.000001))
+ {
+ selectedLine = 0;
+ selectedIndex = -1;
+ return; // line was modified from somewhere else, meanwhile
+ }
+
+ // I am not sure whether we always want to constrain it that way
+ GraphPoint gp = q2gPoint(pos);
+ selectedPoint.y = gp.y;
+
+ if(selectedIndex != 0 && selectedIndex != (( int )( points.size() )-1))
+ {
+ float pixelsize = (maxx-minx)/float(width()-1);
+
+ if(selectedIndex > 0 && points[selectedIndex-1].x > gp.x)
+ {
+ selectedPoint.x = points[selectedIndex-1].x+pixelsize;
+ }
+ else if(selectedIndex < (( int )( points.size() )-1) && points[selectedIndex+1].x < gp.x)
+ {
+ selectedPoint.x = points[selectedIndex+1].x-pixelsize;
+ }
+ else
+ {
+ selectedPoint.x = gp.x;
+ }
+ }
+ points[selectedIndex] = selectedPoint;
+ selectedLine->points(points);
+ }
+}
+
+void KGraph::mouseReleaseEvent(QMouseEvent *)
+{
+ selectedIndex = -1;
+ selectedLine = 0;
+}
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kgraph.h b/arts/gui/kde/kgraph.h
new file mode 100644
index 00000000..51e64d70
--- /dev/null
+++ b/arts/gui/kde/kgraph.h
@@ -0,0 +1,64 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef _KGRAPH_H
+#define _KGRAPH_H
+
+#include <qwidget.h>
+#include <qpoint.h>
+#include "artsgui.h"
+#include <list>
+
+namespace Arts {
+
+class KGraphLine_impl;
+class KGraph : public QWidget
+{
+Q_OBJECT
+
+protected:
+ std::list<KGraphLine_impl *> lines;
+ float minx, maxx, miny, maxy;
+
+ KGraphLine_impl *selectedLine;
+ GraphPoint selectedPoint;
+ int selectedIndex; // -1 if nothing is selected
+
+ inline GraphPoint q2gPoint(const QPoint &qp);
+ inline QPoint g2qPoint(const GraphPoint &gp);
+public:
+ KGraph( QWidget * parent = 0, const char * name = 0 );
+ virtual ~KGraph();
+
+ void addLine(Arts::KGraphLine_impl *line);
+ void redrawLine(Arts::KGraphLine_impl *line);
+ void removeLine(Arts::KGraphLine_impl *line);
+
+ void mousePressEvent(QMouseEvent *me);
+ void mouseMoveEvent(QMouseEvent *me);
+ void mouseReleaseEvent(QMouseEvent *me);
+
+ void paintEvent(QPaintEvent *e);
+};
+}
+
+#endif /* KGRAPH_H */
diff --git a/arts/gui/kde/kgraph_impl.cpp b/arts/gui/kde/kgraph_impl.cpp
new file mode 100644
index 00000000..f629e680
--- /dev/null
+++ b/arts/gui/kde/kgraph_impl.cpp
@@ -0,0 +1,92 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ stefan@space.twc.de
+ 2002 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "kgraph_impl.h"
+#include "anyref.h"
+#include "stdio.h"
+
+using namespace Arts;
+using namespace std;
+
+KGraph_impl::KGraph_impl( KGraph * widget )
+ : KWidget_impl( widget ? widget : new KGraph )
+{
+ _minx = 0.0; _maxx = 1.0; _miny = 0.0; _maxy = 1.0;
+ _kgraph = static_cast<KGraph*>( _qwidget );
+ _kgraph->setFixedSize( 300, 200 );
+}
+
+string KGraph_impl::caption()
+{
+ return _caption.utf8().data();
+}
+
+void KGraph_impl::caption(const string& newCaption)
+{
+ _caption = QString::fromUtf8(newCaption.c_str());
+ // FIXME: do something with the caption here
+}
+
+float KGraph_impl::minx()
+{
+ return _minx;
+}
+
+void KGraph_impl::minx(float newMin)
+{
+ _minx = newMin;
+}
+
+float KGraph_impl::maxx()
+{
+ return _maxx;
+}
+
+void KGraph_impl::maxx(float newMax)
+{
+ _maxx = newMax;
+}
+
+float KGraph_impl::miny()
+{
+ return _miny;
+}
+
+void KGraph_impl::miny(float newMin)
+{
+ _miny = newMin;
+}
+
+float KGraph_impl::maxy()
+{
+ return _maxy;
+}
+
+void KGraph_impl::maxy(float newMax)
+{
+ _maxy = newMax;
+}
+
+namespace Arts { REGISTER_IMPLEMENTATION(KGraph_impl); }
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kgraph_impl.h b/arts/gui/kde/kgraph_impl.h
new file mode 100644
index 00000000..b576d65a
--- /dev/null
+++ b/arts/gui/kde/kgraph_impl.h
@@ -0,0 +1,65 @@
+ /*
+
+ Copyright (C) 2000,2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_GUI_KGRAPH_IMPL_H
+#define ARTS_GUI_KGRAPH_IMPL_H
+#include "kwidget_impl.h"
+#include "kgraph.h"
+
+#include <qobject.h>
+#include <qstring.h>
+
+
+namespace Arts {
+
+class KGraph_impl;
+
+class KGraph_impl : virtual public Arts::Graph_skel,
+ public Arts::KWidget_impl
+{
+protected:
+ QString _caption;
+ float _minx, _miny, _maxx, _maxy;
+
+ KGraph * _kgraph;
+
+public:
+ KGraph_impl( KGraph * w = 0 );
+ void constructor( Widget p ) { parent( p ); }
+
+ std::string caption();
+ void caption(const std::string& newCaption);
+
+ float minx();
+ void minx(float newMin);
+ float maxx();
+ void maxx(float newMax);
+ float miny();
+ void miny(float newMin);
+ float maxy();
+ void maxy(float newMax);
+};
+
+}
+#endif /* ARTS_GUI_KGRAPH_IMPL_H */
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kgraphline_impl.cpp b/arts/gui/kde/kgraphline_impl.cpp
new file mode 100644
index 00000000..5a48c000
--- /dev/null
+++ b/arts/gui/kde/kgraphline_impl.cpp
@@ -0,0 +1,124 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "kgraphline_impl.h"
+#include "kwidgetrepo.h"
+#include "kgraph.h"
+
+using namespace Arts;
+using namespace std;
+
+KGraphLine_impl::KGraphLine_impl()
+{
+ _graphID = -1;
+}
+
+KGraphLine_impl::~KGraphLine_impl()
+{
+ KGraph *kgraph = getKGraph();
+ if(kgraph)
+ kgraph->removeLine(this);
+}
+
+Graph KGraphLine_impl::graph()
+{
+ return Arts::DynamicCast(KWidgetRepo::the()->lookupWidget(_graphID));
+}
+
+void KGraphLine_impl::graph(Graph newGraph)
+{
+ KGraph *kgraph = getKGraph();
+ if(kgraph)
+ kgraph->removeLine(this);
+
+ _graphID = newGraph.widgetID();
+
+ kgraph = getKGraph();
+ if(kgraph)
+ kgraph->addLine(this);
+}
+
+bool KGraphLine_impl::editable()
+{
+ return _editable;
+}
+
+void KGraphLine_impl::editable(bool newEditable)
+{
+ _editable = newEditable;
+
+ KGraph *kgraph = getKGraph();
+ if(kgraph)
+ kgraph->redrawLine(this);
+}
+
+string KGraphLine_impl::color()
+{
+ return _color;
+}
+
+void KGraphLine_impl::color(const std::string& newColor)
+{
+ _color = newColor;
+
+ KGraph *kgraph = getKGraph();
+ if(kgraph)
+ kgraph->redrawLine(this);
+}
+
+vector<GraphPoint> *KGraphLine_impl::points()
+{
+ return new vector<GraphPoint>(_points);
+}
+
+void KGraphLine_impl::points(const vector<GraphPoint>& newPoints)
+{
+ _points = newPoints;
+
+ KGraph *kgraph = getKGraph();
+ if(kgraph)
+ kgraph->redrawLine(this);
+
+ // emitting a change notification is a bit tricky because no real
+ // Arts::AnyRef support is there for sequence<Arts::GraphPoint>
+ Arts::Any any;
+ Arts::Buffer buffer;
+
+ any.type = "*Arts::GraphPoint";
+ writeTypeSeq(buffer,_points);
+ buffer.read(any.value,buffer.size());
+
+ _emit_changed("points_changed",any);
+}
+
+KGraph *KGraphLine_impl::getKGraph()
+{
+ QWidget *widget = KWidgetRepo::the()->lookupQWidget(_graphID);
+ if(!widget)
+ return 0;
+
+ return dynamic_cast<KGraph *>(widget);
+}
+
+namespace Arts {
+ REGISTER_IMPLEMENTATION(KGraphLine_impl);
+}
diff --git a/arts/gui/kde/kgraphline_impl.h b/arts/gui/kde/kgraphline_impl.h
new file mode 100644
index 00000000..546cff90
--- /dev/null
+++ b/arts/gui/kde/kgraphline_impl.h
@@ -0,0 +1,61 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef _KGRAPHLINE_IMPL_H
+#define _KGRAPHLINE_IMPL_H
+
+#include "artsgui.h"
+
+namespace Arts {
+
+class KGraph;
+class KGraphLine_impl : virtual public GraphLine_skel {
+protected:
+ friend class KGraph; // for efficiency
+
+ long _graphID;
+ bool _editable;
+ std::string _color;
+ std::vector<GraphPoint> _points;
+
+ KGraph *getKGraph();
+
+public:
+ KGraphLine_impl();
+ ~KGraphLine_impl();
+
+ Graph graph();
+ void graph(Graph newGraph);
+
+ bool editable();
+ void editable(bool newEditable);
+
+ std::string color();
+ void color(const std::string& newColor);
+
+ std::vector<GraphPoint> *points();
+ void points(const std::vector<GraphPoint>& newPoints);
+};
+
+}
+
+#endif /* _KGRAPHLINE_IMPL_H */
diff --git a/arts/gui/kde/khbox_impl.cpp b/arts/gui/kde/khbox_impl.cpp
new file mode 100644
index 00000000..90ed9a3b
--- /dev/null
+++ b/arts/gui/kde/khbox_impl.cpp
@@ -0,0 +1,51 @@
+ /*
+
+ Copyright (C) 2000,2001 Stefan Westerfeld
+ stefan@space.twc.de
+ 2002 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "khbox_impl.h"
+#include <qhbox.h>
+
+using namespace Arts;
+
+KHBox_impl::KHBox_impl( QHBox * widget )
+ : KFrame_impl( widget ? widget : new QHBox )
+ , _spacing( 5 )
+{
+ _qhbox = static_cast<QHBox*>( _qwidget );
+ _qhbox->setSpacing( _spacing );
+ _qhbox->setMargin( 5 );
+}
+
+long KHBox_impl::spacing()
+{
+ return _spacing;
+}
+
+void KHBox_impl::spacing( long s )
+{
+ _spacing = s;
+ _qhbox->setSpacing( s );
+}
+
+REGISTER_IMPLEMENTATION(KHBox_impl);
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/khbox_impl.h b/arts/gui/kde/khbox_impl.h
new file mode 100644
index 00000000..70bade71
--- /dev/null
+++ b/arts/gui/kde/khbox_impl.h
@@ -0,0 +1,48 @@
+ /*
+
+ Copyright (C) 2000,2001 Stefan Westerfeld
+ stefan@space.twc.de
+ 2002 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "kframe_impl.h"
+
+class QHBox;
+
+namespace Arts {
+
+class KHBox_impl : virtual public Arts::HBox_skel,
+ public Arts::KFrame_impl
+{
+private:
+ long _spacing;
+
+protected:
+ QHBox * _qhbox;
+
+public:
+ KHBox_impl( QHBox * w = 0 );
+
+ long spacing();
+ void spacing( long );
+};
+
+}
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/klabel_impl.cpp b/arts/gui/kde/klabel_impl.cpp
new file mode 100644
index 00000000..f53de2e7
--- /dev/null
+++ b/arts/gui/kde/klabel_impl.cpp
@@ -0,0 +1,104 @@
+/*
+ Copyright ( C ) 2002 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or ( at your option ) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#include "klabel_impl.h"
+#include "klabel_impl.moc"
+
+#include <kdebug.h>
+#include <qfont.h>
+
+using namespace Arts;
+using namespace std;
+
+KLabel_impl::KLabel_impl( QFrame *widget ) : KFrame_impl( widget ? widget : new RotateLabel( 0 ) ) {
+ _label = static_cast<RotateLabel*>( _qwidget );
+}
+
+string KLabel_impl::text() {
+ return _label->title().utf8().data();
+}
+
+void KLabel_impl::text( const string& newtext ) {
+ _label->title( QString::fromUtf8( newtext.c_str() ) );
+}
+
+long KLabel_impl::align() { return _label->align(); }
+void KLabel_impl::align( long n ) { _label->align( n ); }
+
+void KLabel_impl::fontsize( long n ) { _label->fontsize( n ); }
+void KLabel_impl::fontfamily( const std::string& n ) { _label->fontfamily( n.c_str() ); }
+
+Arts::TextBottom KLabel_impl::bottom() { return _label->bottom(); }
+void KLabel_impl::bottom( Arts::TextBottom n ) { _label->bottom( n ); }
+
+REGISTER_IMPLEMENTATION( KLabel_impl );
+
+RotateLabel::RotateLabel( QWidget* p, const char* n ) : QFrame( p,n ) {
+ _bottom = Arts::South;
+ _align = Arts::AlignCenter;
+}
+void RotateLabel::paintEvent( QPaintEvent* ) {
+ QPainter p( this );
+ if ( _bottom == Arts::East ) {
+ p.rotate( 270 );
+ p.drawText( QRect( 0,0, -height(), width() ), _align, _title );
+ } else if ( _bottom == Arts::West ) {
+ p.rotate( 90 );
+ p.drawText( QRect( 0,0, height(), -width() ), _align, _title );
+ } else if ( _bottom == Arts::North ) {
+ p.rotate( 180 );
+ p.drawText( QRect( 0,0, -width(), -height() ), _align, _title );
+ } else {
+ p.drawText( QRect( 0,0, width(), height() ), _align, _title );
+ }
+}
+
+void RotateLabel::fontfamily( QString n ) {
+ QFont font = this->font();
+ font.setFamily( n );
+ this->setFont( font );
+}
+void RotateLabel::fontsize( int n ) {
+ QFont font = this->font();
+ font.setPixelSize( n );
+ this->setFont( font );
+}
+
+void RotateLabel::title( QString n ) {
+ _title = n;
+ QSize size = this->fontMetrics().size( SingleLine, _title );
+ if ( _bottom == Arts::East || _bottom == Arts::West )
+ this->setMinimumSize( size.height(), size.width() );
+ else
+ this->setMinimumSize( size );
+}
+
+void RotateLabel::align( long n ) {
+ _align=n;
+ repaint();
+}
+
+void RotateLabel::bottom( Arts::TextBottom bottom ) {
+ _bottom = bottom;
+ title( _title );
+ repaint();
+}
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/klabel_impl.h b/arts/gui/kde/klabel_impl.h
new file mode 100644
index 00000000..62b505a3
--- /dev/null
+++ b/arts/gui/kde/klabel_impl.h
@@ -0,0 +1,90 @@
+ /*
+
+ Copyright ( C ) 2002 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or ( at your option ) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_KLABEL_IMPL
+#define ARTS_KLABEL_IMPL
+
+#include <kframe_impl.h>
+#include <qframe.h>
+#include <qpainter.h>
+#include <qfontmetrics.h>
+#include <qfont.h>
+
+#include <artsgui.h>
+#include <kdelibs_export.h>
+
+class RotateLabel;
+
+namespace Arts {
+
+class KDE_EXPORT KLabel_impl : virtual public Arts::Label_skel,
+ public Arts::KFrame_impl
+{
+protected:
+ RotateLabel* _label;
+public:
+ KLabel_impl( QFrame* w=0 );
+ void constructor( Widget p ) { parent( p ); }
+
+ std::string text();
+ void text( const std::string& newtext );
+
+ long align();
+ void align( long );
+
+ long fontsize() { return -1; }
+ void fontsize( long );
+
+ std::string fontfamily() { return ""; }
+ void fontfamily( const std::string& );
+
+ Arts::TextBottom bottom();
+ void bottom( Arts::TextBottom );
+
+}; // class
+
+} // namespace
+
+class RotateLabel : public QFrame {
+ Q_OBJECT
+public:
+ RotateLabel( QWidget*, const char* =0 );
+ void paintEvent( QPaintEvent* );
+
+ void fontfamily( QString );
+ void fontsize( int );
+ void title( QString );
+ QString title() { return _title; }
+
+ long align() { return _align; }
+ void align( long );
+
+ Arts::TextBottom bottom() { return _bottom; }
+ void bottom( Arts::TextBottom );
+private:
+ long _align;
+ Arts::TextBottom _bottom;
+ QString _title;
+};
+
+#endif
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/klayoutbox_impl.cpp b/arts/gui/kde/klayoutbox_impl.cpp
new file mode 100644
index 00000000..e83ea5c7
--- /dev/null
+++ b/arts/gui/kde/klayoutbox_impl.cpp
@@ -0,0 +1,122 @@
+/*
+ Copyright ( C ) 2002, 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or ( at your option ) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#include "klayoutbox_impl.h"
+
+#include <qframe.h>
+#include <qlayout.h>
+#include <kdebug.h>
+#include <qpainter.h>
+#include <qstyle.h>
+#include <qpen.h>
+#include "kwidgetrepo.h"
+
+using namespace Arts;
+using namespace std;
+
+KLayoutBox_impl::KLayoutBox_impl( QFrame *widget ) : KFrame_impl( widget ? widget :new QFrame( 0 ) )
+{
+ _qframe = static_cast<QFrame*>( _qwidget );
+ _layout = new QBoxLayout( _qframe, QBoxLayout::LeftToRight );
+}
+KLayoutBox_impl::~KLayoutBox_impl() {
+}
+
+void KLayoutBox_impl::addWidget( Arts::Widget widget, long stretch, long align ) {
+ widget.parent( self() );
+ this->_addChild( widget, "layoutbox_item" );
+ QWidget * tmp = KWidgetRepo::the()->lookupQWidget( widget.widgetID() );
+ _layout->addWidget( tmp, stretch, align );
+}
+
+void KLayoutBox_impl::insertWidget( long index, Arts::Widget widget, long stretch, long align ) {
+ widget.parent( self() );
+ this->_addChild( widget, "layoutbox_item" );
+ QWidget * tmp = KWidgetRepo::the()->lookupQWidget( widget.widgetID() );
+ _layout->insertWidget( index, tmp, stretch, align );
+}
+
+void KLayoutBox_impl::addStretch( long n ) { _layout->addStretch( n ); }
+void KLayoutBox_impl::addSpace( long n ) { _layout->addSpacing( n ); }
+void KLayoutBox_impl::addStrut( long n ) { _layout->addStrut( n ); }
+void KLayoutBox_impl::addSeparator( long stretch, long align ) {
+ _layout->addWidget( new KLayoutBox_Separator( _qframe ), stretch, align );
+}
+void KLayoutBox_impl::addLine( long width, long space, long stretch, long align ) {
+ _layout->addWidget( new KLayoutBox_Line( width, space, _qframe ), stretch, align );
+}
+
+long KLayoutBox_impl::spacing() { return _layout->spacing(); }
+void KLayoutBox_impl::spacing( long n ) { _layout->setSpacing( n ); }
+
+long KLayoutBox_impl::layoutmargin() { return _layout->margin(); }
+void KLayoutBox_impl::layoutmargin( long n ) { _layout->setMargin( n ); this->margin( n ); }
+
+Direction KLayoutBox_impl::direction() { return Arts::Direction( _layout->direction() ); }
+void KLayoutBox_impl::direction( Direction d ) { _layout->setDirection( QBoxLayout::Direction( d ) ); }
+
+REGISTER_IMPLEMENTATION( KLayoutBox_impl );
+
+KLayoutBox_Separator::KLayoutBox_Separator( QWidget* p, const char* n ) : QWidget( p,n ) {
+//kdDebug() << k_funcinfo << endl;
+}
+
+void KLayoutBox_Separator::resizeEvent( QResizeEvent* ) { kdDebug() << k_funcinfo << size() << endl; }
+
+void KLayoutBox_Separator::paintEvent( QPaintEvent* ) {
+//kdDebug() << k_funcinfo << size() << endl;
+ QPainter p( this );
+ QStyle::SFlags flags = QStyle::Style_Default;
+ if ( width() < height() ) flags |= QStyle::Style_Horizontal;
+ style().drawPrimitive( QStyle::PE_Splitter, &p, rect(), colorGroup(), flags );
+}
+
+QSize KLayoutBox_Separator::minimumSizeHint() const {
+ int wh = style().pixelMetric( QStyle::PM_SplitterWidth, this );
+ return QSize( wh, wh );
+}
+
+KLayoutBox_Line::KLayoutBox_Line( int width, int space, QWidget* p, const char* n )
+ : QWidget( p,n )
+ , _width( width )
+ , _space( space )
+{
+//kdDebug() << k_funcinfo << size() << endl;
+}
+
+
+void KLayoutBox_Line::paintEvent( QPaintEvent* ) {
+//kdDebug() << k_funcinfo << size() << endl;
+ QPainter p( this );
+ p.setPen( QPen( colorGroup().foreground(), _width ) );
+ if ( width() > height() ) p.drawLine( 0, height()/2, width(), height()/2 );
+ else p.drawLine( width()/2, 0, width()/2, height() );
+}
+
+QSize KLayoutBox_Line::minimumSizeHint() const {
+//kdDebug() << k_funcinfo << size() << endl;
+ int wh = _width + 2* _space;
+ return QSize( wh, wh );
+}
+
+#include <klayoutbox_impl.moc>
+
+// vim: sw=4 ts=4
+
diff --git a/arts/gui/kde/klayoutbox_impl.h b/arts/gui/kde/klayoutbox_impl.h
new file mode 100644
index 00000000..fbb88630
--- /dev/null
+++ b/arts/gui/kde/klayoutbox_impl.h
@@ -0,0 +1,103 @@
+ /*
+
+ Copyright ( C ) 2002, 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or ( at your option ) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_KLAYOUTBOX_IMPL_H
+#define ARTS_KLAYOUTBOX_IMPL_H
+
+#include "kframe_impl.h"
+
+#include <artsgui.h>
+#include <kdelibs_export.h>
+
+class KArtsWidget;
+class QBoxLayout;
+
+namespace Arts {
+
+class KDE_EXPORT KLayoutBox_impl : virtual public Arts::LayoutBox_skel,
+ public Arts::KFrame_impl
+{
+protected:
+ QFrame* _qframe;
+ QBoxLayout* _layout;
+
+ LayoutBox self() { return LayoutBox::_from_base( _copy() ); }
+public:
+ KLayoutBox_impl( QFrame* w=0 );
+ ~KLayoutBox_impl();
+
+ Direction direction();
+ void direction( Direction );
+
+ void addWidget( Arts::Widget, long, long );
+ void addWidget( Arts::Widget w, long n ) { addWidget( w,n,0 ); }
+ void addWidget( Arts::Widget w ) { addWidget( w,0,0 ); }
+
+ void insertWidget( long, Arts::Widget, long, long );
+ void insertWidget( long i, Arts::Widget w, long n ) { insertWidget( i,w,n,0 ); }
+ void insertWidget( long i, Arts::Widget w ) {insertWidget( i,w,0,0 ); }
+
+ void addStretch( long );
+ void addStretch() { addStretch( 0 ); }
+
+ void addSpace( long );
+ void addStrut( long );
+
+ void addSeparator( long, long );
+ void addSeparator( long ) { addSeparator( 0, 0 ); }
+ void addSeparator() { addSeparator( 0, 0 ); }
+
+ void addLine( long, long, long, long );
+ void addLine( long width, long space, long stretch ) { addLine( width, space, stretch, 0 ); }
+ void addLine( long width, long space ) { addLine( width, space, 0, 0 ); }
+
+ long spacing();
+ void spacing( long );
+
+ long layoutmargin();
+ void layoutmargin( long );
+}; // class
+
+} // namespace
+
+class KDE_EXPORT KLayoutBox_Separator : public QWidget {
+ Q_OBJECT
+public:
+ KLayoutBox_Separator( QWidget*, const char* =0 );
+ void resizeEvent( QResizeEvent* );
+ void paintEvent( QPaintEvent* );
+ QSize minimumSizeHint() const;
+};
+
+class KLayoutBox_Line : public QWidget {
+ Q_OBJECT
+private:
+ int _width, _space;
+public:
+ KLayoutBox_Line( int, int, QWidget*, const char* =0 );
+ void paintEvent( QPaintEvent* );
+ QSize minimumSizeHint() const;
+};
+
+#endif
+
+// vim: sw=4 ts=4
+
diff --git a/arts/gui/kde/klevelmeter_firebars.cpp b/arts/gui/kde/klevelmeter_firebars.cpp
new file mode 100644
index 00000000..916c6b53
--- /dev/null
+++ b/arts/gui/kde/klevelmeter_firebars.cpp
@@ -0,0 +1,125 @@
+/*
+ Copyright ( C ) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation;
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#include "klevelmeter_firebars.h"
+
+#include <kdebug.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+
+KLevelMeter_FireBars_private::KLevelMeter_FireBars_private( KLevelMeter_FireBars* p, const char* n ) : QWidget( p,n ) {
+ _parent = p;
+ _pixmap = new QPixmap( 0,0 );
+// setPaletteBackgroundColor( QColor( 0,0,255 ) );
+}
+void KLevelMeter_FireBars_private::paintEvent( QPaintEvent* ) {
+ QPainter p;
+ if ( _pixmap->size() != this->size() ) {
+ _pixmap->resize( this->size() );
+ p.begin( _pixmap );
+ _pixmap->fill( paletteBackgroundColor() );
+ if ( dir==Arts::BottomToTop || dir==Arts::TopToBottom ) {
+kdDebug() << k_funcinfo << dir << endl;
+ //if ( dir==Arts::BottomToTop ) p.translate( 0, rect().bottom() );
+ for ( int i=this->height(); i>0; i-- ) {
+ p.setPen( _parent->color( 1-float( i )/this->height() ) );
+ //if ( dir==Arts::BottomToTop ) i *= -1;
+ p.drawLine( 0, i, this->width(), i );
+ }
+ } else {
+ if ( dir==Arts::RightToLeft ) p.translate( 0, rect().right() );
+ for ( int i=this->width(); i>0; i-- ) {
+ p.setPen( _parent->color( float( i )/this->width() ) );
+ if ( dir==Arts::RightToLeft ) i *= -1;
+ p.drawLine( i, 0, i, this->height() );
+ }
+ }
+ p.end();
+ }
+ p.begin( this );
+ p.translate( 0,0 );
+ p.drawPixmap( QPoint( 0,0 ), *_pixmap );
+ p.end();
+}
+
+KLevelMeter_FireBars::KLevelMeter_FireBars( Arts::KLevelMeter_impl* impl, QWidget* parent, long substyle, long count, Arts::Direction dir, float _dbmin, float _dbmax ) : KLevelMeter_Template( impl, parent, substyle, count, dir, _dbmin, _dbmax ) {
+//kdDebug()<<"KLevelMeter_FireBars::KLevelMeter_FireBars( Arts::KLevelMeter_impl* "<<impl<<", QWidget* "<<parent<<", long "<<substyle<<", long "<<count<<", float "<<_dbmin<<", float "<<_dbmax<<" )"<<endl;
+ this->setMinimumSize( 5, 5 );
+ _bar = new KLevelMeter_FireBars_private( this, 0 );
+ _peakwidget = new QWidget( this );
+ _peakwidget->resize( size() );
+ _peakwidget->setPaletteBackgroundColor( color( 1 ) );
+ _peakwidget->hide();
+}
+
+void KLevelMeter_FireBars::invalue( float n, float p ) {
+//kdDebug()<<"KLevelMeter_FireBars::invalue( float n )"<<endl;
+ if ( _peakwidget->size() != this->size() ) _peakwidget->setGeometry( 0,0, size().width(), size().height() );
+ _value = amptondb( n );
+ _peak = amptondb( p );
+ if ( _peak > 1 )_peakwidget->show(); else _peakwidget->hide();
+ _bar->dir = _dir;
+ switch ( _dir ) {
+ default:
+ case Arts::BottomToTop:
+ _bar->setGeometry( 0, int( this->height()-_value*this->height() ), this->width(), this->height() );
+ break;
+ case Arts::TopToBottom:
+ _bar->setGeometry( 0, -int( this->height()-_value*this->height() ), this->width(), this->height() );
+ break;
+ case Arts::LeftToRight:
+ _bar->setGeometry( -int( this->width()-_value*this->width() ), 0, this->width(), this->height() );
+ break;
+ case Arts::RightToLeft:
+ _bar->setGeometry( int( this->width()-_value*this->width() ), 0, this->width(), this->height() );
+ break;
+ }
+ repaint();
+}
+
+void KLevelMeter_FireBars::paintEvent( QPaintEvent* ) {
+ QPainter p( this );
+ //p.setPen( NoPen );
+ p.setPen( QColor( 0,0,255 ) );
+ // PeakBar
+ if ( _peak > 1.0/1000 ) {
+ if ( _dir == Arts::BottomToTop || _dir == Arts::TopToBottom ) {
+ if ( _dir == Arts::BottomToTop ) p.translate( 0, rect().bottom() );
+ int h = int( this->height()*_peak );
+ if ( _dir==Arts::BottomToTop ) h *= -1;
+ p.drawLine( 0, h, this->width(), h );
+ } else {
+ if ( _dir==Arts::RightToLeft ) p.translate( 0, rect().right() );
+ int w = int( this->width()* _peak );
+ if ( _dir==Arts::RightToLeft ) w *= -1;
+ p.drawLine( w, 0, w, this->height() );
+ }
+ }
+}
+
+/**
+ Planned feature: a little Tooltip showing the actual value of the volume in deziBel and perhaps as linear scaleFactor
+*/
+void KLevelMeter_FireBars::mouseMoveEvent( QMouseEvent* /*qme*/ ) {
+//kdDebug()<<"KLevelMeter_FireBars::mouseMoveEvent(QMouseEvent* "<<qme<<" )"<<endl;
+}
+
+#include <klevelmeter_firebars.moc>
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/klevelmeter_firebars.h b/arts/gui/kde/klevelmeter_firebars.h
new file mode 100644
index 00000000..49ffa75a
--- /dev/null
+++ b/arts/gui/kde/klevelmeter_firebars.h
@@ -0,0 +1,58 @@
+/*
+ Copyright ( C ) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation;
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef ARTS_KLEVELMETER_FIREBARS_H
+#define ARTS_KLEVELMETER_FIREBARS_H
+
+#include "klevelmeter_template.h"
+
+class QPixmap;
+class KLevelMeter_FireBars;
+
+class KLevelMeter_FireBars_private : public QWidget {
+ Q_OBJECT
+public:
+ KLevelMeter_FireBars_private( KLevelMeter_FireBars*, const char* );
+ void paintEvent( QPaintEvent* );
+
+ Arts::Direction dir;
+private:
+ KLevelMeter_FireBars* _parent;
+ QPixmap *_pixmap;
+};
+
+class KLevelMeter_FireBars : public KLevelMeter_Template {
+ Q_OBJECT
+public:
+ KLevelMeter_FireBars( Arts::KLevelMeter_impl*, QWidget* =0, long substyle=0, long count=0, Arts::Direction =Arts::BottomToTop, float _dbmin=-24, float _dbmax=6 );
+
+ void invalue( float, float =0 );
+
+ void paintEvent( QPaintEvent* );
+
+ void mouseMoveEvent( QMouseEvent* );
+private:
+ float _value, _peak;
+ KLevelMeter_FireBars_private *_bar;
+ QWidget* _peakwidget;
+};
+
+#endif
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/klevelmeter_impl.cpp b/arts/gui/kde/klevelmeter_impl.cpp
new file mode 100644
index 00000000..ff04edde
--- /dev/null
+++ b/arts/gui/kde/klevelmeter_impl.cpp
@@ -0,0 +1,131 @@
+/*
+ Copyright ( C ) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation;
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+
+#include "klevelmeter_impl.h"
+
+#include <math.h>
+
+#include <qframe.h>
+#include <kdebug.h>
+#include <qlayout.h>
+#include <kartswidget.h>
+
+#include "klevelmeter_private.h"
+#include "klevelmeter_private.moc"
+#include "klevelmeter_template.h"
+#include "klevelmeter_template.moc"
+
+#include "klevelmeter_small.h"
+#include "klevelmeter_linebars.h"
+#include "klevelmeter_firebars.h"
+#include "klevelmeter_normalbars.h"
+
+using namespace Arts;
+using namespace std;
+
+KLevelMeter_Private::KLevelMeter_Private( KLevelMeter_impl* impl, QFrame* frame, LevelMeterStyle defstyle, QObject* parent, const char* name )
+ : QObject( parent, name )
+ , _impl( impl )
+ , _frame( frame )
+ , _levelmeter( 0 )
+ , _style( defstyle )
+ , _substyle( 0 )
+ , _count( 20 )
+ , _direction( Arts::BottomToTop )
+ , _peak( 20 ), _peakvalue( 0.0 )
+ , _dbmin( -36 ), _dbmax( 0 )
+{
+ _layout = new QBoxLayout( _frame, QBoxLayout::LeftToRight );
+}
+
+void KLevelMeter_Private::createWidget() {
+ if ( _levelmeter != 0 ) { _levelmeter->hide(); delete _levelmeter; _levelmeter=0; }
+ switch ( _style ) {
+ case lmNormalBars:
+ _levelmeter = new KLevelMeter_NormalBars( _impl, _frame, _substyle, _count, _direction, _dbmin, _dbmax );
+ break;
+ case lmFireBars:
+ _levelmeter = new KLevelMeter_FireBars( _impl, _frame, _substyle, _count, _direction, _dbmin, _dbmax );
+ break;
+ default:
+ case lmLineBars:
+ _levelmeter = new KLevelMeter_LineBars( _impl, _frame, _substyle, _count, _direction, _dbmin, _dbmax );
+ break;
+ case lmSmall:
+ _levelmeter = new KLevelMeter_Small( _impl, _frame, _substyle, _count, _direction, _dbmin, _dbmax );
+ break;
+ }
+ _layout->addWidget( _levelmeter );
+ _levelmeter->show();
+ _levelmeter->setMinimumSize( 10,10 );
+}
+
+KLevelMeter_impl::KLevelMeter_impl( QFrame* w ) : Arts::KFrame_impl( w ? w : new QFrame( 0 ) ) {
+//kdDebug()<<"KLevelMeter_impl::KLevelMeter_impl( QFrame* "<<w<<" )"<<endl;
+ p = new KLevelMeter_Private( this, _qframe, lmLineBars );
+ p->createWidget();
+}
+
+LevelMeterStyle KLevelMeter_impl::style() { return p->_style; }
+
+void KLevelMeter_impl::style( LevelMeterStyle style ) {
+ if ( p->_style!=style )
+ {
+ p->_style = style;
+ p->createWidget();
+ }
+}
+
+long KLevelMeter_impl::substyle() { return p->_substyle; }
+void KLevelMeter_impl::substyle( long n ) { p->_substyle = n; p->_levelmeter->substyle( n ); }
+
+long KLevelMeter_impl::count() { return p->_levelmeter->count(); }
+void KLevelMeter_impl::count( long n ) { p->_levelmeter->count( n ); p->_count = n; }
+
+long KLevelMeter_impl::peakfalloff() { return p->_peak; }
+void KLevelMeter_impl::peakfalloff( long n ) { p->_peak = n; }
+
+float KLevelMeter_impl::mindB() { return p->_levelmeter->dbmin; }
+void KLevelMeter_impl::mindB( float n ) { p->_levelmeter->dbmin = n; p->_dbmin = n; }
+float KLevelMeter_impl::maxdB() { return p->_levelmeter->dbmax; }
+void KLevelMeter_impl::maxdB( float n ) { p->_levelmeter->dbmax = n; p->_dbmax = n; }
+
+float KLevelMeter_impl::invalue() { return 0.0; }
+void KLevelMeter_impl::invalue( float n ) {
+//kdDebug()<<"KLevelMeter_impl::invalue( float n="<<n<<" )"<<endl;
+ if ( p->_peak ) {
+ p->_peakvalue = ( p->_peak*p->_peakvalue + n ) / ( p->_peak + 1 );
+ if ( p->_peakvalue < n ) p->_peakvalue = n;
+ }
+ else p->_peakvalue = 0.0;
+ p->_levelmeter->invalue( n, p->_peakvalue );
+}
+
+Arts::Direction KLevelMeter_impl::direction() { return p->_direction; }
+void KLevelMeter_impl::direction( Arts::Direction n ) {
+ p->_direction = n;
+ p->_levelmeter->direction( n );
+}
+
+REGISTER_IMPLEMENTATION( KLevelMeter_impl );
+
+// vim: sw=4 ts=4
+
diff --git a/arts/gui/kde/klevelmeter_impl.h b/arts/gui/kde/klevelmeter_impl.h
new file mode 100644
index 00000000..53dc5fe2
--- /dev/null
+++ b/arts/gui/kde/klevelmeter_impl.h
@@ -0,0 +1,70 @@
+/*
+ Copyright ( C ) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation;
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef ARTS_KLEVELMETER_IMPL_H
+#define ARTS_KLEVELMETER_IMPL_H
+
+#include "artsgui.h"
+
+#include "kframe_impl.h"
+
+class QFrame;
+
+class KLevelMeter_Private;
+
+namespace Arts { // namespace Arts
+
+class KLevelMeter_impl : virtual public Arts::LevelMeter_skel,
+ virtual public Arts::KFrame_impl
+{
+private:
+ KLevelMeter_Private *p;
+public:
+ KLevelMeter_impl( QFrame* =0 );
+
+ LevelMeterStyle style();
+ void style( LevelMeterStyle );
+
+ long substyle();
+ void substyle( long );
+
+ long count();
+ void count( long );
+
+ long peakfalloff();
+ void peakfalloff( long );
+
+ float mindB();
+ void mindB( float );
+ float maxdB();
+ void maxdB( float );
+
+ float invalue();
+ void invalue( float );
+
+ Arts::Direction direction();
+ void direction( Arts::Direction );
+}; //class KLevelMeter_impl
+
+} // namespace Arts
+
+#endif
+// vim: sw=4 ts=4
+
diff --git a/arts/gui/kde/klevelmeter_linebars.cpp b/arts/gui/kde/klevelmeter_linebars.cpp
new file mode 100644
index 00000000..108c5a94
--- /dev/null
+++ b/arts/gui/kde/klevelmeter_linebars.cpp
@@ -0,0 +1,109 @@
+/*
+ Copyright ( C ) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation;
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#include "klevelmeter_linebars.h"
+
+#include <kdebug.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+
+KLevelMeter_LineBars::KLevelMeter_LineBars( Arts::KLevelMeter_impl* impl, QWidget* parent, long substyle, long count, Arts::Direction dir, float _dbmin, float _dbmax ) : KLevelMeter_Template( impl, parent, substyle, count, dir, _dbmin, _dbmax )
+ , _value( 0.0 )
+ , _peak( 0.0 )
+{
+//kdDebug()<<"KLevelMeter_LineBars::KLevelMeter_LineBars( Arts::KLevelMeter_impl* "<<impl<<", QWidget* "<<parent<<", long "<<substyle<<", long "<<count<<", Arts::Direction "<<dir<<", float "<<_dbmin<<", float "<<_dbmax<<" )"<<endl;
+ this->setMinimumSize( 5, 5 );
+ this->substyle( substyle );
+ _stdcolors = colorGroup();
+ setBackgroundMode( Qt::NoBackground );
+}
+
+void KLevelMeter_LineBars::invalue( float n, float p ) {
+ _value = amptondb( n );
+ _peak = amptondb( p );
+ repaint();
+}
+
+void KLevelMeter_LineBars::substyle( long n ) {
+ //kdDebug() << k_funcinfo << n << endl;
+ _substyle = n;
+}
+long KLevelMeter_LineBars::substyle() { return _substyle; }
+
+void KLevelMeter_LineBars::paintEvent( QPaintEvent* ) {
+ QPixmap pm( size() );
+ QPainter p( &pm );
+
+ switch ( _dir ) {
+ case Arts::BottomToTop:
+ break;
+ case Arts::TopToBottom:
+ p.rotate( 180.0 );
+ p.translate( -width() + 1, -height() + 1 );
+ break;
+ case Arts::LeftToRight:
+ p.rotate( 90.0 );
+ p.translate( 0, -width() + 1 );
+ break;
+ case Arts::RightToLeft:
+ p.rotate( 270.0 );
+ p.translate( -height() + 1, 0 );
+ break;
+ }
+
+ if ( _substyle & 1 )
+ p.setBrush( ( _peak<1 )?QColor( 0,0,255 ):QColor( 255,0,0 ) );
+ else
+ p.setBrush( ( _peak<1 )?color( _value ):QColor( 255,0,0 ) );
+
+ QColor bgcolor = ( _substyle & 2 ) ? p.brush().color().dark() : _stdcolors.background();
+ pm.fill( bgcolor );
+
+ p.setPen( NoPen );
+
+ QSize s = size();
+ if ( Arts::LeftToRight == _dir || Arts::RightToLeft == _dir )
+ s.transpose();
+
+ // Value
+ int h = int( s.height() * _value );
+ int top = s.height() - h;
+ int w = s.width();
+ p.drawRect( 0, top, w, h );
+ // PeakBar
+ if ( _peak > 1.0/1000 && _peak <= 1.0 ) {
+ p.setPen( QColor( 255-bgcolor.red(), 255-bgcolor.green(), 255-bgcolor.blue() ) );
+ top = int( s.height() * ( 1 - _peak ) );
+ p.drawLine( 0, top, w, top );
+ }
+
+ bitBlt( this, 0, 0, &pm, 0, 0, pm.width(), pm.height(), CopyROP, true );
+}
+
+/**
+ Planned feature: a little Tooltip showing the actual value of the volume in deziBel and perhaps as linear scaleFactor
+*/
+void KLevelMeter_LineBars::mouseMoveEvent( QMouseEvent* /*qme*/ ) {
+//kdDebug()<<"KLevelMeter_LineBars::mouseMoveEvent(QMouseEvent* "<<qme<<" )"<<endl;
+//kdDebug()<<"qme.y()="<<this->height()-qme->y()<<" db="<<db<<" dbtoamp(db)="<<dbtoamp( db )<<endl;
+}
+
+#include <klevelmeter_linebars.moc>
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/klevelmeter_linebars.h b/arts/gui/kde/klevelmeter_linebars.h
new file mode 100644
index 00000000..f512b6d1
--- /dev/null
+++ b/arts/gui/kde/klevelmeter_linebars.h
@@ -0,0 +1,46 @@
+/*
+ Copyright ( C ) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation;
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef ARTS_KLEVELMETER_LINEBARS_H
+#define ARTS_KLEVELMETER_LINEBARS_H
+
+#include "klevelmeter_template.h"
+
+class KLevelMeter_LineBars : public KLevelMeter_Template {
+ Q_OBJECT
+public:
+ KLevelMeter_LineBars( Arts::KLevelMeter_impl*, QWidget* =0, long substyle=0, long count=0, Arts::Direction =Arts::BottomToTop, float _dbmin=-24, float _dbmax=6 );
+
+ void invalue( float, float =0 );
+
+ void substyle( long );
+ long substyle();
+
+ void paintEvent( QPaintEvent* );
+
+ void mouseMoveEvent( QMouseEvent* );
+private:
+ float _value, _peak;
+ long _substyle;
+ QColorGroup _stdcolors;
+};
+
+#endif
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/klevelmeter_normalbars.cpp b/arts/gui/kde/klevelmeter_normalbars.cpp
new file mode 100644
index 00000000..a078eccf
--- /dev/null
+++ b/arts/gui/kde/klevelmeter_normalbars.cpp
@@ -0,0 +1,81 @@
+/*
+ Copyright ( C ) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation;
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#include "klevelmeter_normalbars.h"
+
+#include <kdebug.h>
+#include <qpainter.h>
+#include <qlayout.h>
+
+KLevelMeter_NormalBars::KLevelMeter_NormalBars( Arts::KLevelMeter_impl* impl, QWidget* parent, long substyle, long count, Arts::Direction dir, float _dbmin, float _dbmax ) : KLevelMeter_Template( impl, parent, substyle, count, dir, _dbmin, _dbmax ) {
+//kdDebug()<<"KLevelMeter_NormalBars::KLevelMeter_NormalBars( Arts::KLevelMeter_impl* "<<impl<<", QWidget* "<<parent<<", long "<<substyle<<", long "<<count<<", float "<<_dbmin<<", float "<<_dbmax<<" )"<<endl;
+ _layout = new QBoxLayout( this, QBoxLayout::BottomToTop );
+}
+
+void KLevelMeter_NormalBars::invalue( float n, float p ) {
+ //kdDebug()<<"KLevelMeter_NormalBars::invalue( float "<<n<<", float "<<p<<" )"<<endl;
+ _peak = amptondb( p );
+ _value = amptondb( n );
+ if ( _dir != Arts::Direction( _layout->direction() ) ) _layout->setDirection( QBoxLayout::Direction( _dir ) );
+ for ( uint i=0; i<bars.count(); i++ ) bars.at( i )->setValue( _value );
+}
+
+long KLevelMeter_NormalBars::count() { return _count; }
+void KLevelMeter_NormalBars::count( long n ) {
+//kdDebug()<<"KLevelMeter_NormalBars::count( long "<<n<<" )"<<endl;
+ if ( n != _count && n>0 ) {
+ _count = n;
+ resizeEvent();
+ }
+}
+
+void KLevelMeter_NormalBars::resizeEvent( QResizeEvent* ) {
+ //kdDebug()<<"KLevelMeter_NormalBars::resizeEvent( QResizeEvent* )"<<endl;
+uint barscount = _count;
+ //kdDebug()<<"[1] barscount="<<barscount<<" bars.count()="<<bars.count()<<endl;
+ if ( _dir==Arts::BottomToTop || _dir==Arts::TopToBottom ) {
+ if ( unsigned( this->height() ) < barscount ) barscount = this->height();
+ } else {
+ if ( unsigned( this->width() ) < barscount ) barscount = this->width();
+ }
+ if ( barscount != bars.count() ) {
+ //kdDebug()<<"[2] barscount="<<barscount<<" bars.count()="<<bars.count()<<endl;
+ while ( bars.count() > 0 ) {
+ bars.last()->hide();
+ delete bars.last();
+ bars.removeLast();
+ //kdDebug()<<"[2.5] barscount="<<barscount<<" bars.count()="<<bars.count()<<endl;
+ }
+ //kdDebug()<<"[3] barscount="<<barscount<<" bars.count()="<<bars.count()<<endl;
+ uint i=0;
+ while ( bars.count()<barscount ) {
+ Bar* tmp = new Bar( i*1.0/barscount, ( i+1 )*1.0/barscount, color( i*1.0/barscount ), this );
+ tmp->show();
+ _layout->addWidget( tmp );
+ bars.append( tmp );
+ i++;
+ //kdDebug()<<"[3."<<i<<"] barscount="<<barscount<<" bars.count()="<<bars.count()<<endl;
+ }
+ //kdDebug()<<"[4] barscount="<<barscount<<" bars.count()="<<bars.count()<<endl;
+ }
+}
+
+#include <klevelmeter_normalbars.moc>
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/klevelmeter_normalbars.h b/arts/gui/kde/klevelmeter_normalbars.h
new file mode 100644
index 00000000..6e319365
--- /dev/null
+++ b/arts/gui/kde/klevelmeter_normalbars.h
@@ -0,0 +1,75 @@
+/*
+ Copyright ( C ) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation;
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef ARTS_KLEVELMETER_NORMALBARS_H
+#define ARTS_KLEVELMETER_NORMALBARS_H
+
+#include "klevelmeter_template.h"
+
+#include <qptrlist.h>
+
+class Bar;
+class QBoxLayout;
+
+class KLevelMeter_NormalBars : public KLevelMeter_Template {
+ Q_OBJECT
+public:
+ KLevelMeter_NormalBars( Arts::KLevelMeter_impl*, QWidget* =0, long substyle=0, long count=25, Arts::Direction =Arts::BottomToTop, float _dbmin=-24, float _dbmax=6 );
+
+ void invalue( float, float =0 );
+
+ void count( long );
+ long count();
+
+ void resizeEvent( QResizeEvent* =0 );
+private:
+ float _value, _peak;
+ QPtrList <Bar> bars;
+// long _count;
+ QBoxLayout *_layout;
+};
+
+class Bar : public QWidget {
+ Q_OBJECT
+private:
+ float _min, _max;
+ QColor _color;
+ bool _on;
+public:
+ Bar( float min, float max, QColor color, QWidget* parent ) : QWidget( parent ), _min( min ), _max( max ), _color( color ), _on( false ) {
+ setBackgroundColor( _color.dark() );
+ }
+ void setValue( float n ) {
+ if ( n>_min /*&& n<_max*/ ) on( true );
+ else on( false );
+ }
+ void on( bool n ) {
+ if ( _on != n )
+ {
+ _on = n;
+ if ( _on ) setBackgroundColor( _color );
+ else setBackgroundColor( _color.dark() );
+ }
+ }
+ void setValues( float min, float max, QColor color ) { _min = min; _max = max; _color = color; on( _on ); }
+};
+
+#endif
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/klevelmeter_private.h b/arts/gui/kde/klevelmeter_private.h
new file mode 100644
index 00000000..c1826c63
--- /dev/null
+++ b/arts/gui/kde/klevelmeter_private.h
@@ -0,0 +1,55 @@
+/*
+ Copyright ( C ) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation;
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef ARTS_KLEVELMETER_PRIVATE_H
+#define ARTS_KLEVELMETER_PRIVATE_H
+
+#include <qwidget.h>
+
+#include "klevelmeter_impl.h"
+
+class KLevelMeter_Template;
+class QBoxLayout;
+class KArtsWidget;
+
+class KLevelMeter_Private : public QObject {
+ Q_OBJECT
+public:
+ Arts::KLevelMeter_impl* _impl;
+ QFrame* _frame;
+ QBoxLayout *_layout;
+
+ KLevelMeter_Template *_levelmeter;
+
+ Arts::LevelMeterStyle _style;
+ long _substyle;
+ long _count;
+ Arts::Direction _direction;
+ long _peak;
+ float _peakvalue;
+ float _dbmin, _dbmax;
+
+ KLevelMeter_Private( Arts::KLevelMeter_impl* impl, QFrame* frame, Arts::LevelMeterStyle defstyle, QObject* =0, const char* =0 );
+
+ void createWidget();
+};
+
+#endif
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/klevelmeter_small.cpp b/arts/gui/kde/klevelmeter_small.cpp
new file mode 100644
index 00000000..94ba0df0
--- /dev/null
+++ b/arts/gui/kde/klevelmeter_small.cpp
@@ -0,0 +1,59 @@
+/*
+ Copyright ( C ) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation;
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#include "klevelmeter_small.h"
+
+#include <kdebug.h>
+#include <qpainter.h>
+
+KLevelMeter_Small::KLevelMeter_Small( Arts::KLevelMeter_impl* impl, QWidget* parent, long substyle, long count, Arts::Direction dir, float _dbmin, float _dbmax ) : KLevelMeter_Template( impl, parent, substyle, count, dir, _dbmin, _dbmax ) {
+//kdDebug()<<"KLevelMeter_Small::KLevelMeter_Small( Arts::KLevelMeter_impl* "<<impl<<", QWidget* "<<parent<<", long "<<substyle<<", long "<<count<<", float "<<_dbmin<<", float "<<_dbmax<<" )"<<endl;
+}
+
+void KLevelMeter_Small::invalue( float n, float p ) {
+//kdDebug()<<"KLevelMeter_Small::invalue( float "<<n<<", float "<<p<<" )"<<endl;
+ _peak = amptondb( p );
+ if ( n ) this->setBackgroundColor( color( amptondb( n ) ) );
+ else this->setBackgroundColor( QColor( 0,255,0 ).dark() );
+}
+
+void KLevelMeter_Small::paintEvent( QPaintEvent* /*qpe*/ ) {
+//kdDebug()<<"KLevelMeter_Small::paintEvent( QPaintEvent* "<<qpe<<" )"<<endl;
+ if ( _peak && _peak <= 1.0 ) {
+ QPainter p( this );
+ //p.setPen( QColor( 0,0,0 ) );
+ QColor bgcolor = this->paletteBackgroundColor();
+ p.setPen( QColor( 255-bgcolor.red(), 255-bgcolor.green(), 255-bgcolor.blue() ) );
+ if ( _dir==Arts::BottomToTop || _dir==Arts::TopToBottom ) {
+ if ( _dir==Arts::BottomToTop ) p.translate( 0, rect().bottom() );
+ int h = int( this->height() * _peak );
+ if ( _dir==Arts::BottomToTop ) h *= -1;
+ p.drawLine( 0, h, this->width(), h );
+ } else {
+ if ( _dir==Arts::RightToLeft ) p.translate( 0, rect().right() );
+ int w = int( this->width() * _peak );
+ if ( _dir==Arts::RightToLeft ) w *= -1;
+ p.drawLine( w, 0, w, this->height() );
+ }
+ }
+}
+
+#include <klevelmeter_small.moc>
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/klevelmeter_small.h b/arts/gui/kde/klevelmeter_small.h
new file mode 100644
index 00000000..837c2bf6
--- /dev/null
+++ b/arts/gui/kde/klevelmeter_small.h
@@ -0,0 +1,39 @@
+/*
+ Copyright ( C ) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation;
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef ARTS_KLEVELMETER_SMALL_H
+#define ARTS_KLEVELMETER_SMALL_H
+
+#include "klevelmeter_template.h"
+
+class KLevelMeter_Small : public KLevelMeter_Template {
+ Q_OBJECT
+public:
+ KLevelMeter_Small( Arts::KLevelMeter_impl*, QWidget* =0, long substyle=0, long count=0, Arts::Direction =Arts::BottomToTop, float _dbmin=-24, float _dbmax=6 );
+
+ void invalue( float, float =0 );
+
+ void paintEvent( QPaintEvent* );
+private:
+ float _peak;
+};
+
+#endif
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/klevelmeter_template.h b/arts/gui/kde/klevelmeter_template.h
new file mode 100644
index 00000000..f1557d28
--- /dev/null
+++ b/arts/gui/kde/klevelmeter_template.h
@@ -0,0 +1,70 @@
+/*
+ Copyright ( C ) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation;
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef ARTS_KLEVELMETER_TEMPLATE_H
+#define ARTS_KLEVELMETER_TEMPLATE_H
+
+#include <qwidget.h>
+
+#include <math.h>
+
+#include "dbvolcalc.h"
+
+#include "klevelmeter_impl.h"
+
+class KLevelMeter_Template : public QWidget, public dB2VolCalc {
+ Q_OBJECT
+public:
+ Arts::KLevelMeter_impl* _impl;
+
+ KLevelMeter_Template( Arts::KLevelMeter_impl* impl, QWidget* p, long /*substyle*/, long count, Arts::Direction dir, float _dbmin, float _dbmax )
+ : QWidget( p )
+ , dB2VolCalc( _dbmin, _dbmax )
+ , _impl( impl )
+ , _count( count )
+ , nilline( 3/4.0 )
+ , _dir( dir )
+ {}
+
+ virtual void invalue( float, float =0 ) =0;
+
+ virtual void substyle( long ) {}
+ virtual long substyle() { return 0; }
+
+ virtual void count( long ) {}
+ virtual long count() { return 0; }
+ long _count;
+
+ void direction( Arts::Direction dir ) { _dir = dir; }
+ Arts::Direction direction() { return _dir; }
+
+ float nilline;
+ /// Gives the colors between green and red
+ QColor color( float n ) {
+ return QColor( int( ( n<=nilline )?255*( 1/nilline )*n:255 ),
+ int( ( n<=1 && n>nilline )?255-255*( 1/nilline )*( n-nilline ):( ( n>1 )?0:255 ) ),
+ 0 );
+ }
+protected:
+ Arts::Direction _dir;
+};
+
+#endif
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/klineedit_impl.cpp b/arts/gui/kde/klineedit_impl.cpp
new file mode 100644
index 00000000..473ace81
--- /dev/null
+++ b/arts/gui/kde/klineedit_impl.cpp
@@ -0,0 +1,75 @@
+/*
+ Copyright (C) 2000 Stefan Westerfeld <stefan@space.twc.de>
+ 2001 Charles Samuels <charles@kde.org>
+ 2002 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#include "klineedit_impl.h"
+#include "klineedit_impl.moc"
+
+using namespace Arts;
+using namespace std;
+
+KLineEditStringMapper::KLineEditStringMapper(KLineEdit_impl *impl, KLineEdit *ke)
+ :impl(impl)
+{
+ connect(ke, SIGNAL(textChanged(const QString&)),
+ this, SLOT(textChanged(const QString&)));
+}
+
+void KLineEditStringMapper::textChanged(const QString& newText)
+{
+ impl->textChanged(newText.utf8().data());
+}
+
+KLineEdit_impl::KLineEdit_impl( KLineEdit * widget )
+ : KWidget_impl( widget ? widget : new KLineEdit )
+{
+ _klineedit = static_cast<KLineEdit*>( _qwidget );
+ ( void )new KLineEditStringMapper( this, _klineedit );
+}
+
+string KLineEdit_impl::text()
+{
+ return _klineedit->text().utf8().data();
+}
+
+void KLineEdit_impl::text(const string& newText)
+{
+ _klineedit->setText(QString::fromUtf8(newText.c_str()));
+}
+
+void KLineEdit_impl::textChanged(const string& newText)
+{
+ text_changed(newText);
+}
+
+string KLineEdit_impl::caption()
+{
+ return ""; // FIXME
+}
+
+void KLineEdit_impl::caption(const string& /*newCaption*/)
+{
+ // FIXME
+}
+
+REGISTER_IMPLEMENTATION(KLineEdit_impl);
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/klineedit_impl.h b/arts/gui/kde/klineedit_impl.h
new file mode 100644
index 00000000..a079a2dc
--- /dev/null
+++ b/arts/gui/kde/klineedit_impl.h
@@ -0,0 +1,63 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+ 2002 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef klineedit_impl_h
+#define klineedit_impl_h
+
+#include "kwidget_impl.h"
+#include <klineedit.h>
+
+
+namespace Arts {
+
+class KLineEdit_impl : virtual public Arts::LineEdit_skel,
+ public Arts::KWidget_impl
+{
+protected:
+ KLineEdit * _klineedit;
+
+public:
+ KLineEdit_impl( KLineEdit * w = 0 );
+ void constructor( Widget p ) { parent( p ); }
+
+ std::string caption();
+ void caption(const std::string& newCaption);
+ std::string text();
+ void text(const std::string& newText);
+ void textChanged(const std::string& newText);
+};
+
+class KLineEditStringMapper :public QObject {
+ Q_OBJECT
+ KLineEdit_impl *impl;
+public:
+ KLineEditStringMapper(KLineEdit_impl *impl, KLineEdit *ed);
+public slots:
+ void textChanged(const QString& newText);
+};
+
+}
+
+#endif
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kpopupbox_impl.cpp b/arts/gui/kde/kpopupbox_impl.cpp
new file mode 100644
index 00000000..03651ea2
--- /dev/null
+++ b/arts/gui/kde/kpopupbox_impl.cpp
@@ -0,0 +1,134 @@
+ /*
+
+ Copyright ( C ) 2002, 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or ( at your option ) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "kpopupbox_impl.h"
+#include "kpopupbox_private.h"
+
+#include <qlayout.h>
+
+using namespace Arts;
+
+KPopupBox_impl::KPopupBox_impl( KPopupBox_widget *w ) : KFrame_impl( w ? w : new KPopupBox_widget )
+{
+ self().framestyle( Box ); self().margin( 1 ); self().linewidth( 1 );
+ self().vSizePolicy( spFixed ); self().hSizePolicy( spFixed );
+
+ if( !w ) w = static_cast<KPopupBox_widget *>( _qframe );
+
+ _widget = w;
+// _mapper = new KPopupBoxEventMapper( _widget, this );
+}
+KPopupBox_impl::~KPopupBox_impl() {
+}
+
+Direction KPopupBox_impl::direction() { return _widget->direction(); }
+void KPopupBox_impl::direction( Direction n ) { _widget->direction( n ); }
+
+void KPopupBox_impl::widget( Arts::Widget widget ) {
+ widget.parent( self() );
+ this->_addChild( widget, "PopupBox_child" );
+ _widget->setWidget( widget );
+}
+Arts::Widget KPopupBox_impl::widget() { return _widget->getWidget(); }
+
+std::string KPopupBox_impl::name() { return _name; }
+void KPopupBox_impl::name( const std::string& n ) { _name = ""; _name = n; _widget->name( n ); }
+
+// Following the private class:
+
+KPopupBox_widget::KPopupBox_widget( QWidget *parent, const char* name ) : QFrame( parent,name )
+{
+ this->setFrameShape( QFrame::Box );
+ this->setMargin( 1 ); this->setLineWidth( 1 );
+
+ _titlebar = new QFrame( this );
+ _titlebarlayout = new QBoxLayout( _titlebar, QBoxLayout::BottomToTop );
+ _titlebarlayout->setAutoAdd( true );
+
+ _showbutton = new ShowButton( _titlebar );
+ connect( _showbutton, SIGNAL( toggled( bool ) ), this, SLOT( hide( bool ) ) );
+ _drag = new HandleDrag( _titlebar );
+ connect( _drag, SIGNAL( clicked() ), _showbutton, SLOT( toggle() ) );
+ _ownbutton = new OwnButton( _titlebar );
+ connect( _ownbutton, SIGNAL( toggled( bool ) ), this, SLOT( own( bool ) ) );
+
+ _artswidget = new OwnWidget( _showbutton, this );
+
+ _layout = new QBoxLayout( this, QBoxLayout::LeftToRight );
+ _layout->addWidget( _titlebar , -1 );
+ _layout->addWidget( _artswidget, 20 );
+ _layout->addStretch( 0 );
+}
+KPopupBox_widget::~KPopupBox_widget() {
+}
+
+Arts::Direction KPopupBox_widget::direction() {
+ return Arts::Direction( _layout->direction() );
+}
+
+void KPopupBox_widget::direction( Arts::Direction n ) {
+ _layout->setDirection( QBoxLayout::Direction( n ) );
+ _showbutton->direction( QBoxLayout::Direction( n ) );
+ switch( n ) {
+ case LeftToRight:
+ case RightToLeft:
+ _titlebarlayout->setDirection( QBoxLayout::BottomToTop );
+ _drag->setMinimumHeight( 30 );
+ _drag->setMinimumWidth( 0 );
+ break;
+ case TopToBottom:
+ case BottomToTop:
+ _titlebarlayout->setDirection( QBoxLayout::RightToLeft );
+ _drag->setMinimumHeight( 0 );
+ _drag->setMinimumWidth( 30 );
+ }
+}
+
+void KPopupBox_widget::setWidget( Arts::Widget widget ) { _artswidget->setContent( widget ); }
+Arts::Widget KPopupBox_widget::getWidget() { return _artswidget->content(); }
+
+void KPopupBox_widget::hide( bool n ) {
+ if( n )
+ _artswidget->hide();
+ else
+ _artswidget->show();
+}
+
+void KPopupBox_widget::own( bool n ) {
+ if ( n )
+ _artswidget->reparent( 0, _artswidget->mapToGlobal( _artswidget->pos() ), !( _artswidget->isHidden() ) );
+ else
+ {
+ _artswidget->reparent( this, QPoint( 0,0 ), !( _artswidget->isHidden() ) );
+ _layout->insertWidget( 1, _artswidget, 20 );
+ }
+}
+
+void KPopupBox_widget::name( std::string n ) {
+ _artswidget->setCaption( n.c_str() );
+}
+
+REGISTER_IMPLEMENTATION( KPopupBox_impl );
+
+#include "kpopupbox_private.moc"
+
+// vim: sw=4 ts=4
+
diff --git a/arts/gui/kde/kpopupbox_impl.h b/arts/gui/kde/kpopupbox_impl.h
new file mode 100644
index 00000000..e3c8c250
--- /dev/null
+++ b/arts/gui/kde/kpopupbox_impl.h
@@ -0,0 +1,70 @@
+ /*
+
+ Copyright ( C ) 2002, 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or ( at your option ) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef KPOPUPBOX_IMPL_H
+#define KPOPUPBOX_IMPL_H
+
+#include <kframe_impl.h>
+#include <artsgui.h>
+
+class QBoxLayout;
+class KPopupBox_widget;
+//class KPopupBoxEventMapper; // The EventMapper isn't needed at present, but perhaps in the future...
+
+namespace Arts {
+
+/// The PopupBox
+
+class KPopupBox_impl : virtual public Arts::PopupBox_skel, public Arts::KFrame_impl
+{
+public:
+ /// selfreference like 'this'
+ PopupBox self() { return PopupBox::_from_base( _copy() ); }
+ /// Constructor
+ KPopupBox_impl( KPopupBox_widget *w=0 );
+ ~KPopupBox_impl();
+
+ /// The name of the widget
+ std::string name();
+ void name( const std::string & );
+
+ /// The direction. LeftToRight (true) or TopToBottom (false). May get other values in the future.
+ Direction direction();
+ void direction( Direction );
+
+ /// Set and Get the Widget.
+ void widget( Arts::Widget );
+ Arts::Widget widget();
+
+private:
+ std::string _name;
+ bool _lefttoright;
+ KPopupBox_widget *_widget;
+// KPopupBoxEventMapper *_mapper;
+
+}; // class KPopupBox
+
+} // namespace Arts
+
+#endif
+
+// vim: sw=4 ts=4
+
diff --git a/arts/gui/kde/kpopupbox_private.h b/arts/gui/kde/kpopupbox_private.h
new file mode 100644
index 00000000..b7e383d1
--- /dev/null
+++ b/arts/gui/kde/kpopupbox_private.h
@@ -0,0 +1,216 @@
+ /*
+
+ Copyright ( C ) 2002, 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or ( at your option ) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef KPOPUPBOX_PRIVATE_H
+#define KPOPUPBOX_PRIVATE_H
+
+
+class HandleDrag;
+class ShowButton;
+class OwnButton;
+class KArtsWidget;
+class OwnWidget;
+class QBoxLayout;
+
+#include <qframe.h>
+
+class KPopupBox_widget : public QFrame
+{
+ Q_OBJECT
+public:
+ KPopupBox_widget( QWidget* =0, const char* =0);
+ ~KPopupBox_widget();
+
+ Arts::Direction direction();
+ void direction( Arts::Direction );
+
+ void setWidget( Arts::Widget widget );
+ Arts::Widget getWidget();
+
+ void name( std::string );
+private slots:
+ void hide( bool );
+ void own( bool );
+private:
+ QBoxLayout *_layout;
+
+ QFrame *_titlebar;
+ QBoxLayout *_titlebarlayout;
+ HandleDrag *_drag;
+ ShowButton *_showbutton;
+ OwnButton *_ownbutton;
+
+ OwnWidget *_artswidget;
+};
+
+// See kpopupbox_impl.h - The eventmapper isn't needed at present, but perhaps in the future...
+
+/*class KPopupBoxEventMapper : public QObject {
+ Q_OBJECT
+public:
+ KPopupBoxEventMapper( KPopupBox_widget *widget, Arts::KPopupBox_impl *impl )
+ : QObject( widget,"" ), _widget( widget ), _impl( impl )
+ {}
+private:
+ KPopupBox_widget *_widget;
+ Arts::KPopupBox_impl *_impl;
+};*/
+
+#include <qpainter.h>
+#include <qstyle.h>
+
+class HandleDrag : public QWidget {
+ Q_OBJECT
+public:
+ HandleDrag( QWidget *parent, const char* name=0 ) : QWidget( parent,name ) {}
+ void paintEvent( QPaintEvent * ) {
+ QPainter p( this );
+ QStyle::SFlags flags = QStyle::Style_Default;
+ if( width() < height() ) flags |= QStyle::Style_Horizontal;
+ style().drawPrimitive( QStyle::PE_DockWindowHandle, &p, rect(), colorGroup(), flags );
+ }
+signals:
+ void clicked();
+protected:
+ virtual void mouseDoubleClickEvent( QMouseEvent * ) {
+ emit clicked();
+ }
+};
+
+static const char* const close_xpm[] = { "5 5 2 1", "# c black", ". c None", "#...#", ".#.#.", "..#..", ".#.#.", "#...#"};
+static const char* const up_xpm[] = { "5 5 2 1", "# c black", ". c None", ".....", "..#..", ".###.", "#####", "....."};
+static const char* const down_xpm[] = { "5 5 2 1", "# c black", ". c None", ".....", "#####", ".###.", "..#..", "....."};
+static const char* const left_xpm[] = { "5 5 2 1", "# c black", ". c None", "...#.", "..##.", ".###.", "..##.", "...#."};
+static const char* const right_xpm[] = { "5 5 2 1", "# c black", ". c None", ".#...", ".##..", ".###.", ".##..", ".#..."};
+static const char* const inside_xpm[] = { "5 5 2 1", "# c black", ". c None", "#####", "#...#", "#...#", "#...#", "#####"};
+static const char* const own_xpm[] = { "5 5 2 1", "# c black", ". c None", "###..", "#.###", "###.#", ".#..#", ".####"};
+
+#include <qpushbutton.h>
+#include <qlayout.h>
+
+class ShowButton : public QPushButton {
+ Q_OBJECT
+private:
+ QBoxLayout::Direction _dir;
+ QPixmap _pmleft, _pmright, _pmup, _pmdown;
+public:
+ ShowButton( QWidget *parent, const char* name=0 ) : QPushButton( parent,name ), _dir( QBoxLayout::LeftToRight )
+ {
+ connect( this, SIGNAL( toggled( bool ) ), this, SLOT( owntoggle( bool ) ) );
+ setToggleButton( true );
+ _pmleft = QPixmap( const_cast<const char**>( left_xpm ) );
+ _pmright = QPixmap( const_cast<const char**>( right_xpm ) );
+ _pmup = QPixmap( const_cast<const char**>( up_xpm ) );
+ _pmdown = QPixmap( const_cast<const char**>( down_xpm ) );
+ setPixmap( _pmright );
+ }
+
+ void direction( QBoxLayout::Direction n ) { _dir=n; }
+public slots:
+ void owntoggle( bool b ) {
+ switch( _dir )
+ {
+ case QBoxLayout::BottomToTop:
+ if( b ) setPixmap( _pmdown );
+ else setPixmap( _pmup );
+ break;
+ case QBoxLayout::TopToBottom:
+ if( b ) setPixmap( _pmup );
+ else setPixmap( _pmdown );
+ break;
+ case QBoxLayout::LeftToRight:
+ if( b ) setPixmap( _pmright );
+ else setPixmap( _pmleft );
+ break;
+ case QBoxLayout::RightToLeft:
+ if( b ) setPixmap( _pmleft );
+ else setPixmap( _pmright );
+ break;
+ }
+ }
+public:
+ QSize minimumSizeHint() const {
+ int wh = style().pixelMetric( QStyle::PM_DockWindowHandleExtent, this );
+ return QSize( wh, wh );
+ }
+ QSizePolicy sizePolicy() const { return QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); }
+ QSize minimumSize() const { return minimumSizeHint(); }
+ QSize sizeHint() const { return minimumSize(); }
+
+ void drawButton( QPainter * p )
+ {
+ p->fillRect( 0,0, width(), height(), QBrush( colorGroup().brush( QColorGroup::Background ) ) );
+ p->drawPixmap( ( width() - pixmap()->width() ) / 2, ( height() - pixmap()->height() ) / 2, *pixmap() );
+ }
+};
+
+class OwnButton : public QPushButton {
+ Q_OBJECT
+private:
+ QPixmap _pmown, _pminside;
+public:
+ OwnButton( QWidget *parent, const char* name=0 ) : QPushButton( parent,name )
+ {
+ connect( this, SIGNAL( toggled( bool ) ), this, SLOT( toggle( bool ) ) );
+ setToggleButton( true );
+ _pmown = QPixmap( const_cast<const char**>( own_xpm ) );
+ _pminside = QPixmap( const_cast<const char**>( inside_xpm ) );
+ setPixmap( _pmown );
+ }
+
+public slots:
+ void toggle( bool b ) {
+ if( b ) setPixmap( _pminside );
+ else setPixmap( _pmown );
+ }
+public:
+ QSize minimumSizeHint() const {
+ int wh = style().pixelMetric( QStyle::PM_DockWindowHandleExtent, this );
+ return QSize( wh, wh );
+ }
+ QSizePolicy sizePolicy() const { return QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); }
+ QSize minimumSize() const { return minimumSizeHint(); }
+ QSize sizeHint() const { return minimumSize(); }
+
+ void drawButton( QPainter * p )
+ {
+ p->fillRect( 0,0, width(), height(), QBrush( colorGroup().brush( QColorGroup::Background ) ) );
+ p->drawPixmap( ( width() - pixmap()->width() ) / 2, ( height() - pixmap()->height() ) / 2, *pixmap() );
+ }
+};
+
+#include <kartswidget.h>
+
+class OwnWidget : public KArtsWidget
+{
+ Q_OBJECT
+ ShowButton *_b;
+public:
+ OwnWidget( ShowButton* b, QWidget* p, const char* n=0, WFlags f=0 ) : KArtsWidget( p,n,f ) { _b = b; }
+ ~OwnWidget() {}
+public slots:
+ void closeEvent( QCloseEvent * ) { _b->toggle(); }
+};
+
+#endif
+
+// vim: sw=4 ts=4
+
diff --git a/arts/gui/kde/kpoti.cpp b/arts/gui/kde/kpoti.cpp
new file mode 100644
index 00000000..e12c1bf5
--- /dev/null
+++ b/arts/gui/kde/kpoti.cpp
@@ -0,0 +1,780 @@
+/***************************************************************************
+ kpoti.cpp - potentiometer widget
+ -------------------
+ begin : Wed Apr 28 23:05:05 MEST 1999
+
+ copyright : (C) 1999 by Martin Lorenz
+ email : lorenz@ch.tum.de
+ (C) 2002-2003 Matthias Kretz <kretz@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "kpoti.h"
+#include "kpoti.moc"
+
+#include <qbitmap.h>
+#include <qpainter.h>
+#include <qcolor.h>
+#include <qdrawutil.h>
+#include <qtimer.h>
+#include <qkeycode.h>
+#include <qpen.h>
+#include <qstring.h>
+#include <qstyle.h>
+
+#include <math.h>
+
+#include <kpixmap.h>
+#include <kpixmapeffect.h>
+#include <kglobal.h>
+#include <kdebug.h>
+#include <kapplication.h>
+
+#define PI 3.1415926
+static const int thresholdTime = 500;
+static const int repeatTime = 100;
+static const float maxAngle = PI*135/180; // 140 degrees to both sides
+static const float tickLength = 3;
+
+struct KPoti::KPotiPrivate
+{
+ KPotiPrivate()
+ : bgDirty( false )
+ , potiDirty( false )
+ {}
+
+ bool bgDirty;
+ KPixmap bgdb;
+ KPixmap bgPixmap( const QColorGroup & colorGroup )
+ {
+ if( bgDirty || bgdb.isNull() )
+ {
+ bgdb.resize( buttonRect.size() );
+ QPainter dbp( &bgdb );
+ dbp.setPen( Qt::NoPen );
+ QRect drawRect = bgdb.rect();
+
+ // create mask
+ QBitmap mask( bgdb.size(), true );
+ QPainter maskpainter( &mask );
+ maskpainter.setPen( Qt::NoPen );
+ maskpainter.setBrush( Qt::color1 );
+ maskpainter.drawEllipse( drawRect );
+ maskpainter.end();
+ bgdb.setMask( mask );
+
+ // inset shadow
+ KPixmap gradient( bgdb.size() );
+ KPixmapEffect::gradient( gradient, colorGroup.light(), colorGroup.dark(), KPixmapEffect::DiagonalGradient );
+ dbp.setBrush( QBrush( colorGroup.button(), gradient ) );
+ dbp.drawEllipse( drawRect );
+
+ potiRect.setSize( drawRect.size() * 0.9 );
+ if( potiRect.width() + 6 > drawRect.width() )
+ {
+ potiRect.setWidth( drawRect.width() - 6 );
+ potiRect.setHeight( drawRect.height() - 6 );
+ }
+ potiRect.moveCenter( center );
+
+ bgDirty = false;
+ }
+ return bgdb;
+ }
+
+ QColor potiColor;
+ bool potiDirty;
+ KPixmap potidb;
+ KPixmap potiPixmap()
+ {
+ if( ( potiDirty || potidb.isNull() ) && ! potiRect.size().isEmpty() )
+ {
+ potidb.resize( potiRect.size() );
+ QPainter dbp( &potidb );
+ dbp.setPen( Qt::NoPen );
+ QRect drawRect( potidb.rect() );
+
+ // create mask
+ QBitmap mask( potidb.size(), true );
+ QPainter maskpainter( &mask );
+ maskpainter.setPen( Qt::NoPen );
+ maskpainter.setBrush( Qt::color1 );
+ maskpainter.drawEllipse( drawRect );
+ maskpainter.end();
+ potidb.setMask( mask );
+
+ KPixmap gradient( potidb.size() );
+ KPixmapEffect::gradient( gradient, potiColor.dark( 130 ), potiColor.light( 130 ), KPixmapEffect::DiagonalGradient );
+ dbp.setBrush( QBrush( potiColor, gradient ) );
+ dbp.drawEllipse( drawRect );
+
+ potiDirty = false;
+ }
+ return potidb;
+ }
+
+ QRect buttonRect;
+ QRect potiRect;
+ QRect labelRect;
+ QString label;
+ QPoint center;
+};
+
+QSizePolicy KPoti::sizePolicy() const
+{
+ return QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );
+}
+
+QSize KPoti::sizeHint() const
+{
+ return minimumSizeHint();
+}
+
+QSize KPoti::minimumSizeHint() const
+{
+ int width = 40;
+ int height = 40;
+ if( m_bLabel )
+ {
+ QFontMetrics metrics( font() );
+ d->labelRect = metrics.boundingRect( d->label );
+ d->labelRect.setHeight( metrics.lineSpacing() );
+ width = KMAX( width, d->labelRect.width() + frameRect().width() - contentsRect().width() );
+ height += metrics.lineSpacing();
+ }
+ //kdDebug() << k_funcinfo << "return " << width << "x" << height << endl;
+ return QSize( width, height );
+}
+
+QString KPoti::text() const
+{
+ return d->label;
+}
+
+void KPoti::setText( const QString & text )
+{
+ d->label = text;
+ setMinimumSize( minimumSizeHint() );
+ updateGeometry();
+}
+
+/**
+ Constructs a poti.
+
+ The \e parent and \e name arguments are sent to the QWidget constructor.
+*/
+KPoti::KPoti( QWidget *parent, const char *name )
+ : QFrame( parent, name, WResizeNoErase | WRepaintNoErase )
+ , d( 0 )
+{
+ init();
+}
+
+
+
+/**
+ Constructs a poti.
+
+ \arg \e minValue is the minimum slider value.
+ \arg \e maxValue is the maximum slider value.
+ \arg \e step is the page step value.
+ \arg \e value is the initial value.
+
+ The \e parent and \e name arguments are sent to the QWidget constructor.
+*/
+
+KPoti::KPoti( int minValue, int maxValue, int step,
+ int value, QWidget *parent, const char *name )
+ : QFrame( parent, name, WResizeNoErase | WRepaintNoErase )
+ , QRangeControl( minValue, maxValue, 1, step, value )
+ , d( 0 )
+{
+ init(value);
+}
+
+KPoti::~KPoti()
+{
+ delete d;
+ d = 0;
+}
+
+void KPoti::init(int value)
+{
+ d = new KPotiPrivate;
+ font().setPointSize( 8 );
+ d->potiColor.setNamedColor( "red" );
+
+ timer = 0;
+ potiVal = value;
+ potiPos = positionFromValue(value);
+ clickOffset = 0;
+ state = Idle;
+ track = TRUE;
+ ticks = TRUE;
+ m_bLabel = true;
+ tickInt = 0;
+
+ setFocusPolicy( TabFocus );
+ initTicks();
+}
+
+
+/**
+ Does what's needed when someone changes the tickmark status
+*/
+
+void KPoti::initTicks()
+{
+ QRect available = contentsRect();
+ if( m_bLabel )
+ available.rTop() += d->labelRect.height();
+ d->center = available.center();
+ // make the width and height equal
+ if( available.width() > available.height() )
+ available.setWidth( available.height() );
+ else if( available.height() > available.width() )
+ available.setHeight( available.width() );
+ available.moveCenter( d->center );
+ d->buttonRect = available;
+
+ buttonRadius = available.width() / 2.0;
+ if( ticks )
+ {
+ buttonRadius -= tickLength;
+ int tickSpace = static_cast<int>( tickLength );
+ d->buttonRect.rTop() += tickSpace;
+ d->buttonRect.rLeft() += tickSpace;
+ d->buttonRect.rRight() -= tickSpace;
+ d->buttonRect.rBottom() -= tickSpace;
+ }
+
+ d->potiDirty = true;
+ d->bgDirty = true;
+}
+
+
+/**
+ Enables slider tracking if \e enable is TRUE, or disables tracking
+ if \e enable is FALSE.
+
+ If tracking is enabled (default), the slider emits the
+ valueChanged() signal whenever the slider is being dragged. If
+ tracking is disabled, the slider emits the valueChanged() signal
+ when the user releases the mouse button (unless the value happens to
+ be the same as before).
+
+ \sa tracking()
+*/
+
+void KPoti::setTracking( bool enable )
+{
+ track = enable;
+}
+
+
+/**
+ \fn bool KPoti::tracking() const
+ Returns TRUE if tracking is enabled, or FALSE if tracking is disabled.
+
+ Tracking is initially enabled.
+
+ \sa setTracking()
+*/
+
+
+/**
+ \fn void KPoti::valueChanged( int value )
+ This signal is emitted when the slider value is changed, with the
+ new slider value as an argument.
+*/
+
+/**
+ \fn void KPoti::sliderPressed()
+ This signal is emitted when the user presses the slider with the mouse.
+*/
+
+/**
+ \fn void KPoti::sliderMoved( int value )
+ This signal is emitted when the slider is dragged, with the
+ new slider value as an argument.
+*/
+
+/**
+ \fn void KPoti::sliderReleased()
+ This signal is emitted when the user releases the slider with the mouse.
+*/
+
+/**
+ Calculates slider position corresponding to value \a v. Does not perform
+ rounding.
+*/
+
+float KPoti::positionFromValue( int v ) const
+{
+
+ int range = maxValue() - minValue();
+ return ( (v - minValue() ) *2* maxAngle) / range - maxAngle;
+}
+
+
+/**
+ Calculates value corresponding to poti position \a p. Performs rounding.
+*/
+
+int KPoti::valueFromPosition( float p ) const
+{
+ int range = maxValue() - minValue();
+ return (int) (minValue() + ((p+maxAngle)*range)/(2*maxAngle));
+}
+
+/*!
+ Implements the virtual QRangeControl function.
+*/
+
+void KPoti::rangeChange()
+{
+ float newPos = positionFromValue( value() );
+ if ( newPos != potiPos ) {
+ reallyMovePoti( newPos );
+ }
+}
+
+void KPoti::paletteChange( const QPalette & )
+{
+ d->bgDirty = true;
+ d->potiDirty = true;
+}
+
+/*!
+ Changes the value (slot)
+*/
+
+void KPoti::valueChange()
+{
+ if ( potiVal != value() ) {
+ float newPos = positionFromValue( value() );
+ potiVal = value();
+ reallyMovePoti( newPos );
+ }
+ emit valueChanged(value());
+}
+
+
+/*!
+ Handles resize events for the poti.
+*/
+
+void KPoti::resizeEvent( QResizeEvent * )
+{
+ rangeChange();
+ initTicks();
+}
+
+void KPoti::setLabel(bool s)
+{
+ m_bLabel = s;
+ initTicks();
+}
+
+/**
+ Sets the color of the button
+ */
+void KPoti::setColor( const QColor &c )
+{
+ d->potiColor = c;
+ d->potiDirty = true;
+ repaint();
+}
+
+
+void KPoti::paintPoti( QPainter * p )
+{
+ if( isVisible() )
+ {
+ KPixmap db = d->potiPixmap();
+ if( db.isNull() )
+ return;
+
+ QPainter p2( &db );
+ p2.translate( db.rect().center().x(), db.rect().center().y() );
+ p2.rotate( potiPos * 180.0 / PI );
+ QRect pointer( db.width() / -20, db.width() / -2, db.width() / 10, db.width() / 2 );
+ QBrush buttonbrush( colorGroup().button() );
+ qDrawShadePanel( &p2, pointer, colorGroup(), true, 1, &buttonbrush );
+ p2.end();
+
+ p->drawPixmap( d->potiRect, db );
+ }
+}
+
+/*!
+ Performs the actual moving of the slider.
+*/
+
+void KPoti::reallyMovePoti( float newPos )
+{
+ QPainter p;
+ p.begin( this );
+ p.setPen(NoPen);
+ potiPos = newPos;
+ paintPoti(&p);
+ p.end();
+}
+
+
+
+
+/**
+ Handles paint events for the slider.
+*/
+
+void KPoti::drawContents( QPainter * p )
+{
+ QPixmap doublebuffer( contentsRect().size() );
+ doublebuffer.fill( colorGroup().background() );
+ QPainter dbp( &doublebuffer );
+ if( m_bLabel )
+ {
+ dbp.setFont( font() );
+ QFontMetrics metrics = dbp.fontMetrics();
+ dbp.drawText( contentsRect().x() - metrics.leftBearing( d->label[ 0 ] ) + ( contentsRect().width() - d->labelRect.width() ) / 2, metrics.height(), d->label );
+ }
+
+ int interval = tickInt;
+ if( interval <= 0 )
+ interval = 12;
+ if( ticks )
+ drawTicks( &dbp, buttonRadius, tickLength, interval );
+
+ dbp.drawPixmap( d->buttonRect, d->bgPixmap( colorGroup() ) );
+
+ if( hasFocus() )
+ style().drawPrimitive( QStyle::PE_FocusRect, &dbp, d->buttonRect, colorGroup() );
+
+ paintPoti( &dbp );
+ dbp.end();
+ p->drawPixmap( contentsRect(), doublebuffer );
+}
+
+
+/*!
+ Handles mouse press events for the slider.
+*/
+
+void KPoti::mousePressEvent( QMouseEvent *e )
+{
+ resetState();
+
+ if ( e->button() == MidButton ) {
+ double pos = atan2( double(e->pos().x()-d->center.x()),
+ double(- e->pos().y() + d->center.y()) );
+ movePoti( pos );
+ return;
+ }
+ if ( e->button() != LeftButton )
+ return;
+
+
+ int dx=e->pos().x()-d->center.x(), dy=e->pos().y()-d->center.y();
+
+ if ( dx*dx+dy*dy < buttonRadius*buttonRadius ) {
+ state = Dragging;
+ clickOffset = potiVal + (e->pos().y() ) ;
+ emit potiPressed();
+ } else if ( e->pos().x() < width()/2 ) {
+ state = TimingDown;
+ subtractPage();
+ if ( !timer )
+ timer = new QTimer( this );
+ connect( timer, SIGNAL(timeout()), SLOT(repeatTimeout()) );
+ timer->start( thresholdTime, TRUE );
+ } else {
+ state = TimingUp;
+ addPage();
+ if ( !timer )
+ timer = new QTimer( this );
+ connect( timer, SIGNAL(timeout()), SLOT(repeatTimeout()) );
+ timer->start( thresholdTime, TRUE );
+ }
+}
+
+/*!
+ Handles mouse move events for the slider.
+*/
+void KPoti::mouseMoveEvent( QMouseEvent *e )
+{
+
+ if ( (e->state() & MidButton) ) { // middle button wins
+ double pos = atan2( double(e->pos().x()-d->center.x()),
+ double(- e->pos().y()+d->center.y()) );
+ movePoti( pos );
+ return;
+ }
+ if ( !(e->state() & LeftButton) )
+ return; // left mouse button is up
+ if ( state != Dragging )
+ return;
+
+
+ movePoti( positionFromValue(- e->pos().y() + clickOffset ));
+}
+
+
+/*!
+ Handles mouse release events for the slider.
+*/
+
+void KPoti::mouseReleaseEvent( QMouseEvent *e )
+{
+ if ( !(e->button() & LeftButton) )
+ return;
+ resetState();
+}
+
+void KPoti::focusInEvent( QFocusEvent * e )
+{
+ //setFrameStyle( Raised | Box );
+ //setLineWidth( 1 );
+ QFrame::focusInEvent( e );
+}
+
+void KPoti::focusOutEvent( QFocusEvent * e )
+{
+ //setFrameStyle( NoFrame );
+ //setLineWidth( 0 );
+ QFrame::focusOutEvent( e );
+}
+
+
+void KPoti::enterEvent( QEvent * )
+{
+ emit mouseEntered( potiVal );
+}
+
+
+/*!
+ Moves the left (or top) edge of the slider to position
+ \a pos. Performs snapping.
+*/
+
+void KPoti::movePoti( float pos )
+{
+ float newPos = QMIN( maxAngle, QMAX( -maxAngle, pos ) );
+ int newVal = valueFromPosition( newPos );
+ if ( potiVal != newVal ) {
+ potiVal = newVal;
+ emit potiMoved( potiVal );
+ }
+ if ( tracking() && potiVal != value() ) {
+ directSetValue( potiVal );
+ emit valueChanged( potiVal );
+ }
+
+
+ if ( potiPos != newPos )
+ reallyMovePoti( newPos );
+}
+
+
+/*!
+ Resets all state information and stops my timer.
+*/
+
+void KPoti::resetState()
+{
+ if ( timer ) {
+ timer->stop();
+ timer->disconnect();
+ }
+ switch ( state ) {
+ case TimingUp:
+ case TimingDown:
+ break;
+ case Dragging: {
+ setValue( valueFromPosition( potiPos ) );
+ emit potiReleased();
+ break;
+ }
+ case Idle:
+ break;
+ default:
+ kdWarning() << "KPoti: in wrong state" << endl;
+ }
+ state = Idle;
+}
+
+
+/*!
+ Handles key press events for the slider.
+*/
+
+void KPoti::keyPressEvent( QKeyEvent *e )
+{
+
+ switch ( e->key() ) {
+ case Key_Left:
+ subtractLine();
+ break;
+ case Key_Right:
+ addLine();
+ break;
+ case Key_Up:
+ addLine();
+ break;
+ case Key_Down:
+ subtractLine();
+ break;
+ case Key_Prior:
+ subtractPage();
+ break;
+ case Key_Next:
+ addPage();
+ break;
+ case Key_Home:
+ setValue( minValue() );
+ break;
+ case Key_End:
+ setValue( maxValue() );
+ break;
+ default:
+ e->ignore();
+ return;
+ }
+ e->accept();
+}
+
+
+
+
+
+/*!
+ Makes QRangeControl::setValue() available as a slot.
+*/
+
+void KPoti::setValue( int value )
+{
+ QRangeControl::setValue( value );
+}
+
+
+/*!
+ Moves the slider one pageStep() upwards.
+*/
+
+void KPoti::addStep()
+{
+ addPage();
+}
+
+
+/*!
+ Moves the slider one pageStep() downwards.
+*/
+
+void KPoti::subtractStep()
+{
+ subtractPage();
+}
+
+
+/*!
+ Waits for autorepeat.
+*/
+
+void KPoti::repeatTimeout()
+{
+ Q_ASSERT( timer );
+ timer->disconnect();
+ if ( state == TimingDown )
+ connect( timer, SIGNAL(timeout()), SLOT(subtractStep()) );
+ else if ( state == TimingUp )
+ connect( timer, SIGNAL(timeout()), SLOT(addStep()) );
+ timer->start( repeatTime, FALSE );
+}
+
+
+
+
+/*!
+ Using \a p, draws tickmarks at a distance of \a dist from the edge
+ of the widget, using \a w pixels and \a i intervals.
+ */
+
+void KPoti::drawTicks( QPainter *p, double dist, double w, int i ) const
+{
+ p->setPen( colorGroup().foreground() );
+ double angle,s,c;
+ double x, y;
+ for (int v=0; v<=i; v++)
+ {
+ angle = -maxAngle+2*maxAngle*v/i;
+ s = sin( angle );
+ c = cos( angle );
+ x = d->center.x() - s * dist;
+ y = d->center.y() - c * dist;
+
+ p->drawLine( (int)x, (int)y, (int)(x - s * w), (int)(y - c * w) );
+ }
+}
+
+void KPoti::wheelEvent(QWheelEvent *e)
+{
+ setValue(value()+e->delta()/120*8);
+}
+
+
+/*!
+ Sets the way tickmarks are displayed by the slider. \a s can take
+ the following values:
+ <ul>
+ <li> \c NoMarks
+ <li> \c Above
+ <li> \c Left
+ <li> \c Below
+ <li> \c Right
+ <li> \c Both
+ </ul>
+ The initial value is \c NoMarks.
+ \sa tickmarks(), setTickInterval()
+*/
+
+void KPoti::setTickmarks( bool s )
+{
+ ticks = s;
+ initTicks();
+ update();
+}
+
+
+
+/*!
+ Sets the interval between tickmarks to \a i. This is a value interval,
+ not a pixel interval. If \a i is 0, the slider
+ will choose between lineStep() and pageStep(). The initial value of
+ tickInterval() is 0.
+ \sa tickInterval(), QRangeControl::lineStep(), QRangeControl::pageStep()
+*/
+
+void KPoti::setTickInterval( int i )
+{
+ tickInt = QMAX( 0, i );
+ update();
+}
+
+
+/*!
+ \fn int KPoti::tickInterval() const
+ Returns the interval between tickmarks. Returns 0 if the slider
+ chooses between pageStep() and lineStep().
+ \sa setTickInterval()
+*/
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kpoti.h b/arts/gui/kde/kpoti.h
new file mode 100644
index 00000000..a87b5162
--- /dev/null
+++ b/arts/gui/kde/kpoti.h
@@ -0,0 +1,136 @@
+/***************************************************************************
+ kpoti.h - Potentiometer Widget
+ -------------------
+ begin : Wed Apr 28 23:05:05 MEST 1999
+
+ copyright : (C) 1999 by Martin Lorenz
+ email : lorenz@ch.tum.de
+ (C) 2002 Matthias Kretz <kretz@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef _KPOTI_H
+#define _KPOTI_H
+
+#include <qframe.h>
+#include <qrangecontrol.h>
+
+
+class QTimer;
+struct QPotiData;
+
+
+class KPoti : public QFrame, public QRangeControl
+{
+ Q_OBJECT
+public:
+
+ KPoti( QWidget *parent=0, const char *name=0 );
+ KPoti( int minValue, int maxValue, int step, int value,
+ QWidget *parent=0, const char *name=0 );
+
+ ~KPoti();
+
+ void setTracking( bool enable );
+ bool tracking() const;
+
+ void setColor( const QColor & );
+
+ virtual void setTickmarks( bool );
+ virtual void setLabel( bool );
+ bool tickmarks() const { return ticks; }
+
+ virtual void setTickInterval( int );
+ int tickInterval() const { return tickInt; }
+
+ virtual QSizePolicy sizePolicy() const;
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+ QString text() const;
+
+public slots:
+ void setValue( int );
+ void addStep();
+ void subtractStep();
+ void setText( const QString & );
+
+signals:
+ void valueChanged( int value );
+ void potiPressed();
+ void potiMoved( int value );
+ void potiReleased();
+ void mouseEntered(int value);
+
+protected:
+ void resizeEvent( QResizeEvent * );
+ void drawContents( QPainter * );
+
+ void keyPressEvent( QKeyEvent * );
+
+ void mousePressEvent( QMouseEvent * );
+ void mouseReleaseEvent( QMouseEvent * );
+ void mouseMoveEvent( QMouseEvent * );
+ void enterEvent( QEvent *);
+
+ void focusInEvent( QFocusEvent *e );
+ void focusOutEvent( QFocusEvent *e );
+
+ void valueChange();
+ void rangeChange();
+
+ virtual void paletteChange( const QPalette & );
+
+ virtual void paintPoti( QPainter * );
+ void drawButton( QPainter *);
+ void drawTicks( QPainter *, double, double, int=1 ) const;
+
+ virtual void wheelEvent(QWheelEvent *e);
+private slots:
+ void repeatTimeout();
+
+private:
+ enum State { Idle, Dragging, TimingUp, TimingDown };
+
+ void init(int value=0);
+ float positionFromValue( int ) const;
+ int valueFromPosition( float ) const;
+ void movePoti( float );
+ void reallyMovePoti( float );
+ void resetState();
+ int potiRadius() const;
+ void initTicks();
+
+ QTimer *timer;
+ float potiPos;
+ int potiVal;
+ int clickOffset;
+ State state;
+ bool track;
+ bool ticks, m_bLabel;
+ int tickInt, space;
+ double buttonRadius;
+private: // Disabled copy constructor and operator=
+ // KPoti( const KPoti & ) {}
+ //KPoti &operator=( const KPoti & ) { return *this; }
+ struct KPotiPrivate;
+ KPotiPrivate * d;
+};
+
+inline bool KPoti::tracking() const
+{
+ return track;
+}
+
+
+#endif // _KPOTI_H
+
+
+
diff --git a/arts/gui/kde/kpoti_impl.cpp b/arts/gui/kde/kpoti_impl.cpp
new file mode 100644
index 00000000..10b0adb3
--- /dev/null
+++ b/arts/gui/kde/kpoti_impl.cpp
@@ -0,0 +1,203 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+ 2001, 2002 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "kpoti_impl.h"
+#include "kpoti_impl.moc"
+#include "anyref.h"
+#include "stdio.h"
+
+#include <math.h>
+
+using namespace Arts;
+using namespace std;
+
+PotiIntMapper::PotiIntMapper(KPoti_impl *impl, KPoti *kp)
+ : QObject( kp )
+ , impl( impl )
+{
+ connect(kp, SIGNAL(valueChanged(int)), this, SLOT(valueChanged(int)));
+}
+
+void PotiIntMapper::valueChanged(int pos)
+{
+ impl->valueChanged(pos);
+}
+
+KPoti_impl::KPoti_impl( KPoti * widget )
+ : KFrame_impl( widget ? widget : new KPoti( 0, 100, 1, 0 ) )
+{
+ _min = 0; _max = 1; _value = 0;
+ _factor = 1;
+ _logarithmic = 0;
+ _range = 100;
+
+ _kpoti = static_cast<KPoti*>( _qwidget );
+ ( void )new PotiIntMapper( this, _kpoti );
+}
+
+string KPoti_impl::caption()
+{
+ return _kpoti->text().utf8().data();
+}
+
+void KPoti_impl::caption(const string& newText)
+{
+ _caption = QString::fromUtf8( newText.c_str() );
+ _kpoti->setText( _caption );
+}
+
+string KPoti_impl::color()
+{
+ return _color;
+}
+
+void KPoti_impl::color(const string& newColor)
+{
+ _color = newColor;
+ if(strlen(_color.c_str()))
+ {
+ QColor qc(_color.c_str());
+ _kpoti->setColor(qc);
+ }
+}
+
+float KPoti_impl::min()
+{
+ return _min;
+}
+
+void KPoti_impl::min(float newMin)
+{
+ if(_min != newMin)
+ {
+ _min = newMin;
+ applyValue();
+ }
+}
+
+float KPoti_impl::max()
+{
+ return _max;
+}
+
+void KPoti_impl::max(float newMax)
+{
+ if(_max != newMax)
+ {
+ _max = newMax;
+ applyValue();
+ }
+}
+
+float KPoti_impl::value()
+{
+ float ret = float(_kpoti->value()) / _factor;
+ if(_logarithmic > 0)
+ ret = convertFromLog(ret);
+ if(ret < _min)
+ ret = _min;
+ else if(ret > _max)
+ ret = _max;
+ return ret;
+}
+
+void KPoti_impl::value(float newValue)
+{
+ if(newValue != _value)
+ {
+ _value = newValue;
+ applyValue();
+ if(visible())
+ value_changed(value());
+ }
+}
+
+long KPoti_impl::range()
+{
+ return _range;
+}
+
+void KPoti_impl::range(long newRange)
+{
+ if(_range != newRange)
+ {
+ _range = newRange;
+ applyValue();
+ }
+}
+
+void KPoti_impl::valueChanged(int newvalue)
+{
+ _value = (float)newvalue / _factor;
+ if(_logarithmic > 0)
+ _value = convertFromLog(_value);
+ if(visible())
+ value_changed(value());
+}
+
+float KPoti_impl::convertToLog(float val)
+{
+ return log(val) / log(_logarithmic);
+}
+
+float KPoti_impl::convertFromLog(float val)
+{
+ return pow(_logarithmic, val);
+}
+
+void KPoti_impl::applyValue()
+{
+ double dmin = _min;
+ double dmax = _max;
+ double dvalue = _value;
+ if(_logarithmic > 0)
+ {
+ dmin = convertToLog(_min);
+ dmax = convertToLog(_max);
+ dvalue = convertToLog(_value);
+ }
+ _factor = _range / (dmax - dmin);
+ int imin = int(_factor * dmin);
+ int imax = int(_factor * dmax);
+ int ivalue = int(_factor * dvalue);
+ _kpoti->setRange(imin, imax);
+ _kpoti->setValue(ivalue);
+}
+
+void KPoti_impl::logarithmic(float newLogarithmic)
+{
+ if(_logarithmic != newLogarithmic)
+ {
+ _logarithmic = newLogarithmic;
+ applyValue();
+ }
+}
+
+float KPoti_impl::logarithmic()
+{
+ return _logarithmic;
+}
+
+REGISTER_IMPLEMENTATION(KPoti_impl);
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kpoti_impl.h b/arts/gui/kde/kpoti_impl.h
new file mode 100644
index 00000000..94b03a6f
--- /dev/null
+++ b/arts/gui/kde/kpoti_impl.h
@@ -0,0 +1,90 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+ 2001, 2002 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_GUI_KPOTI_IMPL_H
+#define ARTS_GUI_KPOTI_IMPL_H
+#include "kframe_impl.h"
+#include "kpoti.h"
+
+#include <qobject.h>
+#include <qstring.h>
+
+
+namespace Arts {
+
+class KPoti_impl;
+
+class PotiIntMapper :public QObject {
+ Q_OBJECT
+ KPoti_impl *impl;
+public:
+ PotiIntMapper(KPoti_impl *impl, KPoti *kp);
+public slots:
+ void valueChanged(int x);
+};
+
+class KPoti_impl : virtual public Arts::Poti_skel,
+ public Arts::KFrame_impl
+{
+protected:
+ KPoti * _kpoti;
+ QString _caption;
+ std::string _color;
+ float _min, _max, _value;
+ float _factor;
+ float _logarithmic;
+ long _range;
+
+ float convertToLog(float);
+ float convertFromLog(float);
+ void applyValue();
+
+public:
+ KPoti_impl( KPoti * w = 0 );
+
+ std::string caption();
+ void caption(const std::string& newText);
+ std::string color();
+ void color(const std::string& newColor);
+
+ float logarithmic();
+ void logarithmic(float);
+
+ float min();
+ void min(float newMin);
+ float max();
+ void max(float newMax);
+ float value();
+ void value(float newValue);
+
+ long range();
+ void range(long newRange);
+
+ /* from qt */
+ void valueChanged(int newValue);
+};
+
+}
+#endif /* ARTS_GUI_KPOTI_IMPL_H */
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kspinbox_impl.cpp b/arts/gui/kde/kspinbox_impl.cpp
new file mode 100644
index 00000000..f97716c4
--- /dev/null
+++ b/arts/gui/kde/kspinbox_impl.cpp
@@ -0,0 +1,108 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+ 2002 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "kspinbox_impl.h"
+#include "kspinbox_impl.moc"
+#include "anyref.h"
+#include "stdio.h"
+
+using namespace Arts;
+using namespace std;
+
+SpinBoxIntMapper::SpinBoxIntMapper(KSpinBox_impl *impl, QSpinBox *sp)
+ :impl(impl)
+{
+ connect(sp, SIGNAL(valueChanged(int)), this, SLOT(valueChanged(int)));
+}
+
+void SpinBoxIntMapper::valueChanged(int pos)
+{
+ impl->valueChanged(pos);
+}
+
+KSpinBox_impl::KSpinBox_impl( QSpinBox * widget )
+ : KWidget_impl( widget ? widget : new QSpinBox )
+{
+ _min = 0; _max = 100; _value = 0;
+ _qspinbox = static_cast<QSpinBox*>( _qwidget );
+ _qspinbox->setRange( 0, 100 );
+ ( void )new SpinBoxIntMapper( this, _qspinbox );
+}
+
+string KSpinBox_impl::caption()
+{
+ return _caption.utf8().data();
+}
+
+void KSpinBox_impl::caption(const string& newCaption)
+{
+ _caption = QString::fromUtf8(newCaption.c_str());
+ // FIXME: do something with the caption here
+}
+
+long KSpinBox_impl::min()
+{
+ return _min;
+}
+
+void KSpinBox_impl::min(long newMin)
+{
+ _min = newMin;
+ _qspinbox->setMinValue(newMin);
+}
+
+long KSpinBox_impl::max()
+{
+ return _max;
+}
+
+void KSpinBox_impl::max(long newMax)
+{
+ _max = newMax;
+ _qspinbox->setMaxValue(newMax);
+}
+
+long KSpinBox_impl::value()
+{
+ return _value;
+}
+
+void KSpinBox_impl::value(long newValue)
+{
+ if(newValue != _value)
+ {
+ _value = newValue;
+ _qspinbox->setValue(newValue);
+ if(visible())
+ value_changed(newValue);
+ }
+}
+
+void KSpinBox_impl::valueChanged(int newvalue)
+{
+ value(newvalue);
+}
+
+REGISTER_IMPLEMENTATION(KSpinBox_impl);
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kspinbox_impl.h b/arts/gui/kde/kspinbox_impl.h
new file mode 100644
index 00000000..b0af8bae
--- /dev/null
+++ b/arts/gui/kde/kspinbox_impl.h
@@ -0,0 +1,76 @@
+ /*
+
+ Copyright (C) 2000,2001 Stefan Westerfeld
+ stefan@space.twc.de
+ 2002 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_GUI_KSPINBOX_IMPL_H
+#define ARTS_GUI_KSPINBOX_IMPL_H
+#include "kwidget_impl.h"
+
+#include <qspinbox.h>
+#include <qobject.h>
+#include <qstring.h>
+
+
+namespace Arts {
+
+class KSpinBox_impl;
+class SpinBoxIntMapper :public QObject {
+ Q_OBJECT
+ KSpinBox_impl *impl;
+public:
+ SpinBoxIntMapper(KSpinBox_impl *impl, QSpinBox *sp);
+public slots:
+ void valueChanged(int x);
+};
+
+class KSpinBox_impl : virtual public Arts::SpinBox_skel,
+ public Arts::KWidget_impl
+{
+protected:
+ QSpinBox * _qspinbox;
+ QString _caption;
+ long _min, _max, _value;
+
+ void applyValue();
+
+public:
+ KSpinBox_impl( QSpinBox * w = 0 );
+ void constructor( Widget p ) { parent( p ); }
+
+ std::string caption();
+ void caption(const std::string& newCaption);
+
+ long min();
+ void min(long newMin);
+ long max();
+ void max(long newMax);
+ long value();
+ void value(long newValue);
+
+ /* from qt */
+ void valueChanged(int newValue);
+};
+
+}
+#endif /* ARTS_GUI_KSPINBOX_IMPL_H */
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/ktickmarks_impl.cpp b/arts/gui/kde/ktickmarks_impl.cpp
new file mode 100644
index 00000000..0aa0a01e
--- /dev/null
+++ b/arts/gui/kde/ktickmarks_impl.cpp
@@ -0,0 +1,179 @@
+/*
+ Copyright ( C ) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation;
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#include "ktickmarks_impl.h"
+
+#include <kdebug.h>
+#include <qpainter.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+
+KTickmarks_impl::KTickmarks_impl( QFrame* w ) : Arts::KFrame_impl( w ? w : new KTickmarks_Widget( 0 ) )
+{
+ //kdDebug()<<"KTickmarks_impl::KTickmarks_impl( QFrame* w="<<w<<" )"<<endl;
+ _tmwidget = static_cast<KTickmarks_Widget*>( _qwidget );
+}
+
+float KTickmarks_impl::min() { return _tmwidget->dbmin; }
+void KTickmarks_impl::min( float n ) { _tmwidget->dbmin = n; }
+float KTickmarks_impl::max() { return _tmwidget->dbmax; }
+void KTickmarks_impl::max( float n ) { _tmwidget->dbmax = n; }
+float KTickmarks_impl::minstep() { return _tmwidget->minstep; }
+void KTickmarks_impl::minstep( float n ) { _tmwidget->minstep = n; }
+float KTickmarks_impl::substep() { return _tmwidget->substep; }
+void KTickmarks_impl::substep( float n ) { _tmwidget->substep = n; }
+
+Arts::Direction KTickmarks_impl::direction() { return _tmwidget->_dir; }
+void KTickmarks_impl::direction( Arts::Direction n ) { _tmwidget->_dir = n; }
+
+long KTickmarks_impl::position() { return _tmwidget->_pos; }
+void KTickmarks_impl::position( long n ) { _tmwidget->_pos = n; }
+
+void KTickmarks_impl::constructor( float min, float max, Arts::Direction dir, long pos ) {
+ this->min( min ); this->max( max ); direction( dir ); position( pos );
+}
+
+KTickmarks_Widget::KTickmarks_Widget( KTickmarks_impl* impl, QWidget* p, const char* n ) : QFrame( p,n ), dB2VolCalc( -24, 0 ), _impl( impl ), _pos( Arts::posLeft ), _dir( Arts::BottomToTop ), minstep( 1 ), substep( 0.5 ) {
+ setMinimumSize( 20,20 );
+}
+
+void KTickmarks_Widget::drawContents( QPainter* p ) {
+ //kdDebug()<<"KTickmarks::drawContents( QPainter* "<<p<<" )"<<endl;
+ bool left=false, right=false;
+ if ( _pos&Arts::posLeft ) left=true;
+ if ( _pos&Arts::posRight ) right=true;
+ // Setting the font
+ QFont font;
+ font.setPixelSize( 8 /*font.pixelSize()/2*/ ); // Maybe this could be adjusted...
+ p->setFont( font );
+ // Determining the size of the largest text (currently the text at the minimum-scale)
+ QFontMetrics fontmetric( font );
+ QRect fontrect = fontmetric.boundingRect( QString::number( dbmin ) );
+ // Calculating stepsizes
+ float _minstepcount = ( dbmax-dbmin )/minstep;
+ float _minstep = minstep; // this value gets changed
+ float _substepcount = ( dbmax-dbmin )/substep;
+ float _substep = substep; // this value gets changed
+ // Calculating minimum size of the widget
+ int _minsize;
+ // Shorcuts
+ int w,h;
+ QColor colornormal = colorGroup().foreground();
+ QColor colordiff = colorGroup().buttonText();
+
+ if ( _dir == Arts::BottomToTop || _dir == Arts::TopToBottom ) {
+ p->translate( contentsRect().left(), contentsRect().bottom() );
+ // Calculating stepsizes
+ for ( int i=1; _minstepcount*( fontrect.height()+4 ) > contentsRect().height(); i++ ) {
+ _minstepcount = ( dbmax-dbmin ) / minstep / i;
+ _minstep = minstep*i;
+ }
+ while ( _substepcount*2 > contentsRect().height() ) { _substepcount/=2; _substep*=2; }
+ // Calculating minimum size of the widget
+ _minsize=fontrect.width()+4;
+ if ( left ) _minsize+=6;
+ if ( right ) _minsize+=6;
+ setMinimumWidth( _minsize + frameWidth() + 2 );
+// setMaximumWidth( _minsize /*+6*/ );
+ w = contentsRect().width(); // Just a shortcut
+ h=0;
+ // Painting substep marks
+ p->setPen( QPen( colordiff, 1 ) );
+ for ( float i=dbmax; i>=dbmin; i-=_substep ) {
+ h = int( -contentsRect().height() * dbtondb( i ) );
+ if ( _dir==Arts::TopToBottom ) h = 1 - h;
+ if ( left ) p->drawLine( 0, h, 3, h );
+ if ( right ) p->drawLine( w-3, h, w, h );
+ }
+ // Painting step marks and texts
+ p->setPen( QPen( colornormal, 1 ) );
+ for ( float i=0; i>=dbmin; i-=_minstep ) {
+ h = int( -contentsRect().height() * dbtondb( i ) );
+ if ( _dir==Arts::TopToBottom ) h = 1 - h;
+ if ( left ) p->drawLine( 0, h, 6, h );
+ p->drawText( ( w - (left)*6 - (right)*6 - fontrect.width() )/2 + (left)*6
+ , h-fontrect.height()/2,
+ fontrect.width(), fontrect.height()+2,
+ Qt::AlignRight|Qt::AlignTop, QString::number( i ) );
+ if ( right ) p->drawLine( w-6, h, w, h );
+ }
+ for ( float i=_minstep; i<=dbmax; i+=_minstep ) {
+ h = int( -contentsRect().height() * dbtondb( i ) );
+ if ( _dir==Arts::TopToBottom ) h = 1 - h;
+ if ( left ) p->drawLine( 0, h, 6, h );
+ p->drawText( ( w - (left)*6 - (right)*6 - fontrect.width() )/2 + (left)*6
+ , h-fontrect.height()/2,
+ fontrect.width(), fontrect.height()+2,
+ Qt::AlignRight|Qt::AlignTop, QString::number( i ) );
+ if ( right ) p->drawLine( w-6, h, w, h );
+ }
+ } else {
+ //if ( _dir == Arts::LeftToRight || _dir == Arts::RightToLeft ) {
+ // Calculating stepsizes
+ for ( int i=1; _minstepcount*( fontrect.width()+4 ) > contentsRect().width(); i++ ) {
+ _minstepcount = ( dbmax-dbmin ) / minstep / i;
+ _minstep = minstep*i;
+ }
+ while ( _substepcount*2 > contentsRect().width() ) { _substepcount/=2; _substep*=2; }
+ // Calculating minimum size of the widget
+ _minsize=fontrect.height()+4;
+ if ( left ) _minsize+=6;
+ if ( right ) _minsize+=6;
+ setMinimumHeight( _minsize + frameWidth() + 2 );
+// setMaximumHeight( _minsize /*+6*/ );
+ w = 0; // Just a shortcut
+ h = frameWidth() + contentsRect().height();
+ // Painting substep marks
+ p->setPen( QPen( colordiff, 1 ) );
+ for ( float i=dbmax; i>=dbmin; i-=_substep ) {
+ w = this->frameWidth()+ int( contentsRect().width() * dbtondb( i ) );
+ if ( _dir==Arts::RightToLeft ) w = 1 - w;
+ if ( left ) p->drawLine( w, frameWidth(), w, frameWidth() + 3 );
+ if ( right ) p->drawLine( w, h-3, w, h );
+ }
+ // Painting step marks and texts
+ p->setPen( QPen( colornormal, 1 ) );
+ for ( float i=0; i>=dbmin; i-=_minstep ) {
+ w = int( contentsRect().width() * dbtondb( i ) );
+ if ( _dir==Arts::RightToLeft ) w = 1 - w;
+ if ( left ) p->drawLine( w, 0, w, 6 );
+ p->drawText( w - fontrect.width()/2
+ , ( h - (left)*6 - (right)*6 - fontrect.height() )/2 + (left)*6,
+ fontrect.width(), fontrect.height()+2,
+ Qt::AlignRight|Qt::AlignTop, QString::number( i ) );
+ if ( right ) p->drawLine( w, h-6, w, h );
+ }
+ for ( float i=_minstep; i<=dbmax; i+=_minstep ) {
+ h = int( -contentsRect().height() * dbtondb( i ) );
+ if ( _dir==Arts::RightToLeft ) w = 1 - w;
+ if ( left ) p->drawLine( w, 0, w, 6 );
+ p->drawText( w - fontrect.width()/2
+ , ( h - (left)*6 - (right)*6 - fontrect.height() )/2 + (left)*6,
+ fontrect.width(), fontrect.height()+2,
+ Qt::AlignRight|Qt::AlignTop, QString::number( i ) );
+ if ( right ) p->drawLine( w, h-6, w, h );
+ }
+ }
+}
+
+REGISTER_IMPLEMENTATION( KTickmarks_impl );
+
+#include "ktickmarks_impl.moc"
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/ktickmarks_impl.h b/arts/gui/kde/ktickmarks_impl.h
new file mode 100644
index 00000000..5974cf0f
--- /dev/null
+++ b/arts/gui/kde/ktickmarks_impl.h
@@ -0,0 +1,76 @@
+/*
+ Copyright ( C ) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation;
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef ARTS_KLEVELMETER_TICKMARKS_H
+#define ARTS_KLEVELMETER_TICKMARKS_H
+
+#include <qframe.h>
+#include <math.h>
+
+#include "artsgui.h"
+
+#include "kframe_impl.h"
+
+#include <dbvolcalc.h>
+
+class KTickmarks_Widget;
+
+class KTickmarks_impl : virtual public Arts::Tickmarks_skel,
+ virtual public Arts::KFrame_impl
+{
+public:
+ KTickmarks_impl( QFrame* =0 );
+
+ float min();
+ void min( float );
+ float max();
+ void max( float );
+
+ float minstep();
+ void minstep( float );
+ float substep();
+ void substep( float );
+
+ Arts::Direction direction();
+ void direction( Arts::Direction );
+
+ long position();
+ void position( long );
+
+ void constructor( float min, float max, Arts::Direction, long );
+private:
+ KTickmarks_Widget* _tmwidget;
+};
+
+class KTickmarks_Widget : public QFrame, public dB2VolCalc {
+ Q_OBJECT
+private:
+ KTickmarks_impl* _impl;
+public:
+ KTickmarks_Widget( KTickmarks_impl*, QWidget* =0, const char* =0 );
+ void drawContents( QPainter* );
+ long _pos;
+ Arts::Direction _dir;
+
+ float minstep, substep;
+};
+
+#endif
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kvbox_impl.cpp b/arts/gui/kde/kvbox_impl.cpp
new file mode 100644
index 00000000..0b737e78
--- /dev/null
+++ b/arts/gui/kde/kvbox_impl.cpp
@@ -0,0 +1,51 @@
+ /*
+
+ Copyright (C) 2000,2001 Stefan Westerfeld
+ stefan@space.twc.de
+ 2002 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "kvbox_impl.h"
+#include <qvbox.h>
+
+using namespace Arts;
+
+KVBox_impl::KVBox_impl( QVBox * widget )
+ : KFrame_impl( widget ? widget : new QVBox )
+ , _spacing( 5 )
+{
+ _qvbox = static_cast<QVBox*>( _qwidget );
+ _qvbox->setSpacing( _spacing );
+ _qvbox->setMargin( 5 );
+}
+
+long KVBox_impl::spacing()
+{
+ return _spacing;
+}
+
+void KVBox_impl::spacing( long s )
+{
+ _spacing = s;
+ _qvbox->setSpacing( s );
+}
+
+REGISTER_IMPLEMENTATION(KVBox_impl);
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kvbox_impl.h b/arts/gui/kde/kvbox_impl.h
new file mode 100644
index 00000000..9358d90e
--- /dev/null
+++ b/arts/gui/kde/kvbox_impl.h
@@ -0,0 +1,48 @@
+ /*
+
+ Copyright (C) 2000,2001 Stefan Westerfeld
+ stefan@space.twc.de
+ 2002 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "kframe_impl.h"
+
+class QVBox;
+
+namespace Arts {
+
+class KVBox_impl : virtual public Arts::VBox_skel,
+ public Arts::KFrame_impl
+{
+private:
+ long _spacing;
+
+protected:
+ QVBox * _qvbox;
+
+public:
+ KVBox_impl( QVBox * w = 0 );
+
+ long spacing();
+ void spacing( long );
+};
+
+}
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kvolumefader_impl.cpp b/arts/gui/kde/kvolumefader_impl.cpp
new file mode 100644
index 00000000..f3d09a0b
--- /dev/null
+++ b/arts/gui/kde/kvolumefader_impl.cpp
@@ -0,0 +1,243 @@
+/*
+ Copyright ( C ) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation;
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#include "kvolumefader_impl.h"
+
+#include <kdebug.h>
+#include <qpainter.h>
+#include <qstyle.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <kglobalsettings.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kaction.h>
+#include <kinputdialog.h>
+
+KVolumeFader_impl::KVolumeFader_impl( QFrame* w )
+ : Arts::KFrame_impl( w ? w : new KVolumeFader_Widget( 0 ) )
+ , dB2VolCalc( -36, 6 )
+ , _dir( Arts::BottomToTop )
+ , dbmin_inupdate( false )
+ , dbmax_inupdate( false )
+ , direction_inupdate( false )
+ , ignoreUpdates( 0 )
+{
+ //kdDebug()<<"KVolumeFader_impl::KVolumeFader_impl( QFrame* w="<<w<<" )"<<endl;
+ _vfwidget = static_cast<KVolumeFader_Widget*>( _qwidget );
+ _vfwidget->setImpl( this );
+}
+
+KVolumeFader_impl::~KVolumeFader_impl() {
+}
+
+float KVolumeFader_impl::dbmin() { return dB2VolCalc::dbmin; }
+void KVolumeFader_impl::dbmin( float n ) {
+ if ( n != dB2VolCalc::dbmin && !dbmin_inupdate ) {
+ dbmin_inupdate = true;
+ dB2VolCalc::dbmin = n;
+ dbmin_changed( dbmin() );
+ dbmin_inupdate = false;
+ }
+}
+float KVolumeFader_impl::dbmax() { return dB2VolCalc::dbmax; }
+void KVolumeFader_impl::dbmax( float n ) {
+ if ( n != dB2VolCalc::dbmax && !dbmax_inupdate ) {
+ dbmax_inupdate = true;
+ dB2VolCalc::dbmax = n;
+ dbmax_changed( dbmax() );
+ dbmax_inupdate = false;
+ }
+}
+
+Arts::Direction KVolumeFader_impl::direction() { return _dir; }
+void KVolumeFader_impl::direction( Arts::Direction n ) {
+ if ( n != _dir && !direction_inupdate ) {
+ direction_inupdate = true;
+ _dir = n;
+ _vfwidget->setDirection( _dir );
+// direction_changed( direction() );
+ direction_inupdate = false;
+ }
+}
+
+float KVolumeFader_impl::volume() { return _volume; }
+void KVolumeFader_impl::volume( float n ) {
+ //kdDebug() << k_funcinfo << n << " ignore: " << ignoreUpdates << endl;
+ if ( ignoreUpdates > 0 ) {
+ --ignoreUpdates;
+ return;
+ }
+ _volume = n;
+ _vfwidget->setValue( amptondb( _volume ) );
+}
+
+float KVolumeFader_impl::dbvolume() { return amptodb( _volume ); }
+void KVolumeFader_impl::dbvolume( float n ) {
+ //kdDebug() << k_funcinfo << n << endl;
+ normalizedvolume( dbtondb( n ) );
+}
+
+void KVolumeFader_impl::normalizedvolume( float n ) {
+ if ( ( ndbtodb( n ) > dbmin() ) && ( ndbtodb( n ) < dbmax() ) ) {
+ float amp = ndbtoamp( n );
+ if ( amp != _volume ) {
+ ++ignoreUpdates;
+ //kdDebug() << k_funcinfo << ignoreUpdates << endl;
+ _volume = amp;
+ _vfwidget->setValue( n );
+ volume_changed( _volume );
+ }
+ }
+}
+
+void KVolumeFader_impl::constructor( float dbmin, float dbmax, Arts::Direction dir ) {
+ this->dbmin( dbmin ); this->dbmax( dbmax ); direction( dir );
+}
+
+
+
+KVolumeFader_Widget::KVolumeFader_Widget( QWidget* p, const char* n )
+ : QFrame( p,n )
+ , _impl( 0 )
+ , _inupdate( false )
+ , _value( -1 )
+ , _dir( Arts::BottomToTop )
+ , _menu( new KPopupMenu( this ) )
+ , _aExactValue( new KAction( i18n( "Set Exact Value..." ), KShortcut(), this, SLOT( exactValue() ), this ) )
+{
+ //kdDebug() << k_funcinfo << endl;
+ setMinimumSize( 10,10 );
+ _aExactValue->plug( _menu );
+}
+
+void KVolumeFader_Widget::setImpl( KVolumeFader_impl* n ) { _impl = n; update(); }
+
+KVolumeFader_Widget::~KVolumeFader_Widget() {
+ //kdDebug() << k_funcinfo << endl;
+}
+
+void KVolumeFader_Widget::setDirection( Arts::Direction n ) { _dir = n; update(); }
+
+void KVolumeFader_Widget::setValue( float n ) {
+ //kdDebug() << k_funcinfo << n << endl;
+ if ( n != _value ) {
+ _value = n;
+ update();
+ }
+}
+
+QColor KVolumeFader_Widget::interpolate( QColor low, QColor high, float percent ) {
+ if ( percent<=0 ) return low; else
+ if ( percent>=1 ) return high; else
+ return QColor(
+ int( low.red() + ( high.red()-low.red() ) * percent ),
+ int( low.green() + ( high.green()-low.green() ) * percent ),
+ int( low.blue() + ( high.blue()-low.blue() ) * percent ) );
+}
+
+void KVolumeFader_Widget::drawContents( QPainter* p ){
+ if ( _dir==Arts::BottomToTop || _dir==Arts::BottomToTop ) {
+ float h;
+ if ( _dir==Arts::BottomToTop ) h = contentsRect().height() * ( 1 - _value );
+ else h = contentsRect().height() * _value;
+ for ( int i=int( h ); i<contentsRect().height(); i++ ) {
+ p->setPen( interpolate( colorGroup().light(), colorGroup().highlight(), float( i )/contentsRect().height() ) );
+ p->drawLine( contentsRect().left(), this->frameWidth() + i, contentsRect().right(), this->frameWidth() + i );
+ }
+ p->setPen( colorGroup().dark() );
+ p->drawLine( contentsRect().left(), this->frameWidth() + int( h ), contentsRect().right(), this->frameWidth() + int( h ) );
+ } else {
+ float w;
+ p->translate( this->width(),0 );
+ if ( _dir==Arts::LeftToRight ) w = - contentsRect().width() * ( 1 - _value );
+ else w = - contentsRect().width() * _value;
+ for ( int i=int( w ); i>=-contentsRect().width(); i-- ) {
+ p->setPen( interpolate( colorGroup().light(), colorGroup().highlight(), float( -i )/contentsRect().width() ) );
+ p->drawLine( this->frameWidth() + i, contentsRect().top(), this->frameWidth() + i, contentsRect().bottom() );
+ }
+ p->setPen( colorGroup().dark() );
+ p->drawLine( this->frameWidth() + int( w ), contentsRect().top(), this->frameWidth() + int( w ), contentsRect().bottom() );
+ }
+}
+
+void KVolumeFader_Widget::mousePressEvent( QMouseEvent* ){
+ //kdDebug() << k_funcinfo << endl;
+}
+
+void KVolumeFader_Widget::mouseReleaseEvent( QMouseEvent* qme ){
+ bool setValue = false;
+ if ( KGlobalSettings::mouseSettings().handed == 0 && qme->button() == Qt::LeftButton ) setValue=true;
+ if ( KGlobalSettings::mouseSettings().handed == 1 && qme->button() == Qt::RightButton ) setValue=true;
+ if ( setValue )
+ {
+ switch ( _dir ) {
+ default:
+ case Arts::BottomToTop:
+ if ( _impl ) _impl->normalizedvolume( 1 - float( qme->y() ) / contentsRect().height() );
+ break;
+ case Arts::TopToBottom:
+ if ( _impl ) _impl->normalizedvolume( float( qme->y() ) / contentsRect().height() );
+ break;
+ case Arts::LeftToRight:
+ if ( _impl ) _impl->normalizedvolume( float( qme->x() ) / contentsRect().width() );
+ break;
+ case Arts::RightToLeft:
+ if ( _impl ) _impl->normalizedvolume( 1 - float( qme->x() ) / contentsRect().width() );
+ break;
+ }
+ } else _menu->exec( qme->globalPos() );
+}
+
+void KVolumeFader_Widget::mouseMoveEvent( QMouseEvent* qme ){
+ switch ( _dir ) {
+ default:
+ case Arts::BottomToTop:
+ if ( _impl ) _impl->normalizedvolume( 1 - float( qme->y() ) / contentsRect().height() );
+ break;
+ case Arts::TopToBottom:
+ if ( _impl ) _impl->normalizedvolume( float( qme->y() ) / contentsRect().height() );
+ break;
+ case Arts::LeftToRight:
+ if ( _impl ) _impl->normalizedvolume( float( qme->x() ) / contentsRect().width() );
+ break;
+ case Arts::RightToLeft:
+ if ( _impl ) _impl->normalizedvolume( 1 - float( qme->x() ) / contentsRect().width() );
+ break;
+ }
+}
+
+void KVolumeFader_Widget::wheelEvent( QWheelEvent* qwe ){
+ //kdDebug() << k_funcinfo << endl;
+ if ( qwe->delta() < 0 ) { if ( _impl ) _impl->normalizedvolume( _impl->dbtondb( _impl->dbvolume() - 1 ) ); }
+ if ( qwe->delta() > 0 ) { if ( _impl ) _impl->normalizedvolume( _impl->dbtondb( _impl->dbvolume() + 1 ) ); }
+}
+
+void KVolumeFader_Widget::exactValue() {
+ //kdDebug() << k_funcinfo << endl;
+ bool ok=false;
+ double n = KInputDialog::getDouble( i18n( "Set Exact Volume Value" ), i18n( "Exact volume (dB):" ), _impl->dbvolume(), _impl->dbmin(), _impl->dbmax(), 1, &ok, this );
+ if ( ok ) _impl->dbvolume( n );
+}
+
+REGISTER_IMPLEMENTATION( KVolumeFader_impl );
+
+#include "kvolumefader_impl.moc"
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kvolumefader_impl.h b/arts/gui/kde/kvolumefader_impl.h
new file mode 100644
index 00000000..31ac438b
--- /dev/null
+++ b/arts/gui/kde/kvolumefader_impl.h
@@ -0,0 +1,97 @@
+/*
+ Copyright ( C ) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation;
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef ARTS_KVOLUMEFADER_TICKMARKS_H
+#define ARTS_KVOLUMEFADER_TICKMARKS_H
+
+#include <qframe.h>
+#include <math.h>
+
+#include "artsgui.h"
+
+#include "kframe_impl.h"
+
+#include <dbvolcalc.h>
+
+class KVolumeFader_Widget;
+
+class KVolumeFader_impl : virtual public Arts::VolumeFader_skel,
+ virtual public Arts::KFrame_impl,
+ public dB2VolCalc
+{
+public:
+ KVolumeFader_impl( QFrame* =0 );
+ ~KVolumeFader_impl();
+
+ float dbmin();
+ void dbmin( float );
+ float dbmax();
+ void dbmax( float );
+
+ Arts::Direction direction();
+ void direction( Arts::Direction );
+
+ float volume();
+ void volume( float );
+ float dbvolume();
+ void dbvolume( float );
+
+ void normalizedvolume( float );
+
+ void constructor( float min, float max, Arts::Direction dir );
+private:
+ KVolumeFader_Widget* _vfwidget;
+ Arts::Direction _dir;
+ bool dbmin_inupdate, dbmax_inupdate, direction_inupdate;
+ float _min, _max, _volume;
+ int ignoreUpdates;
+};
+
+class KPopupMenu;
+class KAction;
+
+class KVolumeFader_Widget : public QFrame {
+ Q_OBJECT
+private:
+ KVolumeFader_impl* _impl;
+ bool _inupdate;
+ float _value;
+ Arts::Direction _dir;
+ KPopupMenu *_menu;
+ KAction *_aExactValue;
+ QColor interpolate( QColor, QColor, float );
+public:
+ KVolumeFader_Widget( QWidget* =0, const char* =0 );
+ ~KVolumeFader_Widget();
+ void setImpl( KVolumeFader_impl* );
+ void setValue( float );
+ void setDirection( Arts::Direction );
+protected:
+ void drawContents( QPainter* );
+ void mousePressEvent( QMouseEvent* );
+ void mouseReleaseEvent( QMouseEvent* );
+ void mouseMoveEvent( QMouseEvent* );
+ void wheelEvent( QWheelEvent* );
+private slots:
+ void exactValue();
+};
+
+#endif
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kwidget_impl.cpp b/arts/gui/kde/kwidget_impl.cpp
new file mode 100644
index 00000000..261cf30b
--- /dev/null
+++ b/arts/gui/kde/kwidget_impl.cpp
@@ -0,0 +1,181 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+ 2002 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "kwidget_impl.h"
+#include "kwidgetrepo.h"
+#include "debug.h"
+#include <stdio.h>
+
+using namespace Arts;
+
+KWidget_impl::KWidget_impl( QWidget * widget )
+ : _qwidget( widget ? widget : new QWidget )
+{
+ _widgetID = KWidgetRepo::the()->add( this, _qwidget );
+
+ /*
+ * KWidgetGuard will protect us against deleting the widget if Qt already
+ * has done so (for instance if our widget was inside a panel, and
+ * the panel got deleted, our widget will be gone, too)
+ */
+ _guard = new KWidgetGuard(this);
+ QObject::connect(_qwidget, SIGNAL(destroyed()),
+ _guard, SLOT(widgetDestroyed()));
+}
+
+KWidget_impl::~KWidget_impl()
+{
+ if(_qwidget)
+ {
+ delete _qwidget;
+ arts_assert(_qwidget == 0); // should be true due to KWidgetGuard
+ }
+ delete _guard;
+}
+
+void KWidget_impl::widgetDestroyed()
+{
+ KWidgetRepo::the()->remove(_widgetID);
+ _widgetID = 0;
+ _qwidget = 0;
+}
+
+long KWidget_impl::widgetID()
+{
+ return _widgetID;
+}
+
+Widget KWidget_impl::parent()
+{
+ return KWidgetRepo::the()->lookupWidget(_parentID);
+}
+
+void KWidget_impl::parent(Arts::Widget newParent)
+{
+ if(!newParent.isNull())
+ {
+ _parentID = newParent.widgetID();
+
+ QWidget *qparent;
+ qparent = KWidgetRepo::the()->lookupQWidget(newParent.widgetID());
+ if( qparent != 0 )
+ {
+ QPoint pos(x(),y());
+ bool showIt = visible();
+ _qwidget->reparent(qparent, pos, showIt);
+ }
+ }
+ else
+ {
+ _parentID = 0;
+ }
+}
+
+long KWidget_impl::x()
+{
+ return _qwidget->x();
+}
+
+void KWidget_impl::x(long newX)
+{
+ _qwidget->move(newX,y());
+}
+
+long KWidget_impl::y()
+{
+ return _qwidget->y();
+}
+
+void KWidget_impl::y(long newY)
+{
+ _qwidget->move(x(),newY);
+}
+
+long KWidget_impl::width()
+{
+ return _qwidget->width();
+}
+
+void KWidget_impl::width(long newWidth)
+{
+ _qwidget->resize(newWidth,height());
+}
+
+long KWidget_impl::height()
+{
+ return _qwidget->height();
+}
+
+void KWidget_impl::height(long newHeight)
+{
+ _qwidget->resize(width(),newHeight);
+}
+
+bool KWidget_impl::visible()
+{
+ return _qwidget->isVisible();
+}
+
+void KWidget_impl::visible(bool newVisible)
+{
+ if(newVisible) show(); else hide();
+}
+
+SizePolicy KWidget_impl::hSizePolicy()
+{
+ return ( SizePolicy )_qwidget->sizePolicy().horData();
+}
+
+void KWidget_impl::hSizePolicy( SizePolicy p )
+{
+ QSizePolicy sp = _qwidget->sizePolicy();
+ sp.setHorData( ( QSizePolicy::SizeType )p );
+ _qwidget->setSizePolicy( sp );
+}
+
+SizePolicy KWidget_impl::vSizePolicy()
+{
+ return ( SizePolicy )_qwidget->sizePolicy().verData();
+}
+
+void KWidget_impl::vSizePolicy( SizePolicy p )
+{
+ QSizePolicy sp = _qwidget->sizePolicy();
+ sp.setVerData( ( QSizePolicy::SizeType )p );
+ _qwidget->setSizePolicy( sp );
+}
+
+void KWidget_impl::show()
+{
+ _qwidget->show();
+}
+
+void KWidget_impl::hide()
+{
+ _qwidget->hide();
+}
+
+REGISTER_IMPLEMENTATION(KWidget_impl);
+#include "kwidget_impl.moc"
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kwidget_impl.h b/arts/gui/kde/kwidget_impl.h
new file mode 100644
index 00000000..b94f1a8d
--- /dev/null
+++ b/arts/gui/kde/kwidget_impl.h
@@ -0,0 +1,88 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+ 2002 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_GUI_KWIDGET_IMPL_H
+#define ARTS_GUI_KWIDGET_IMPL_H
+#include "artsgui.h"
+#include <qwidget.h>
+#include <kwidgetrepo.h>
+#include <kdelibs_export.h>
+
+namespace Arts {
+
+class KWidgetGuard;
+class KDE_EXPORT KWidget_impl : virtual public Arts::Widget_skel {
+protected:
+ QWidget * _qwidget;
+ KWidgetGuard * _guard;
+ long _parentID;
+ long _widgetID;
+public:
+ KWidget_impl( QWidget * w = 0 );
+
+ ~KWidget_impl();
+
+ long widgetID();
+ Widget parent();
+ void parent(Arts::Widget);
+ long x();
+ void x(long newX);
+ long y();
+ void y(long newY);
+ long width();
+ void width(long newWidth);
+ long height();
+ void height(long newHeight);
+
+ bool visible();
+ void visible(bool newVisible);
+
+ SizePolicy hSizePolicy();
+ void hSizePolicy( SizePolicy );
+ SizePolicy vSizePolicy();
+ void vSizePolicy( SizePolicy );
+
+ void show();
+ void hide();
+
+ void widgetDestroyed();
+};
+
+class KWidgetGuard : public QObject {
+ Q_OBJECT
+protected:
+ KWidget_impl *impl;
+
+public:
+ KWidgetGuard(KWidget_impl *impl) : impl(impl) { }
+
+public slots:
+ void widgetDestroyed() {
+ impl->widgetDestroyed();
+ }
+};
+
+}
+#endif /* ARTS_GUI_KWIDGET_IMPL_H */
+
+// vim: sw=4 ts=4
diff --git a/arts/gui/kde/kwidgetrepo.cpp b/arts/gui/kde/kwidgetrepo.cpp
new file mode 100644
index 00000000..43dd122d
--- /dev/null
+++ b/arts/gui/kde/kwidgetrepo.cpp
@@ -0,0 +1,89 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "kwidgetrepo.h"
+#include "kwidget_impl.h"
+#include <startupmanager.h>
+
+using namespace Arts;
+
+class KWidgetRepoShutdown : public StartupClass {
+public:
+ void startup() {};
+ void shutdown()
+ {
+ KWidgetRepo::shutdown();
+ }
+};
+
+KWidgetRepo *KWidgetRepo::instance = 0;
+
+KWidgetRepo *KWidgetRepo::the()
+{
+ if(!instance)
+ instance = new KWidgetRepo();
+ return instance;
+}
+
+void KWidgetRepo::shutdown()
+{
+ if(instance)
+ {
+ delete instance;
+ instance = 0;
+ }
+}
+
+KWidgetRepo::KWidgetRepo()
+ : nextID(1)
+{
+}
+
+KWidgetRepo::~KWidgetRepo()
+{
+}
+
+
+long KWidgetRepo::add(KWidget_impl *widget, QWidget *qwidget)
+{
+ long ID = nextID++;
+ widgets[ID] = widget;
+ qwidgets[ID] = qwidget;
+ return ID;
+}
+
+QWidget *KWidgetRepo::lookupQWidget(long ID)
+{
+ return qwidgets[ID];
+}
+
+Widget KWidgetRepo::lookupWidget(long ID)
+{
+ if(qwidgets[ID]) /* check existence */
+ return Arts::Widget::_from_base(widgets[ID]->_copy());
+ return Arts::Widget::null();
+}
+
+void KWidgetRepo::remove(long ID)
+{
+ widgets.erase(ID);
+}
diff --git a/arts/gui/kde/kwidgetrepo.h b/arts/gui/kde/kwidgetrepo.h
new file mode 100644
index 00000000..e60cc011
--- /dev/null
+++ b/arts/gui/kde/kwidgetrepo.h
@@ -0,0 +1,56 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef KWIDGETREPO_H
+#define KWIDGETREPO_H
+
+#include <map>
+#include <qwidget.h>
+#include <weakreference.h>
+#include "artsgui.h"
+
+namespace Arts {
+ class KWidget_impl;
+}
+
+class KWidgetRepo {
+protected:
+ long nextID;
+ std::map<long, QWidget *> qwidgets;
+ std::map<long, Arts::KWidget_impl *> widgets;
+ static KWidgetRepo *instance;
+
+ KWidgetRepo();
+ ~KWidgetRepo();
+
+public:
+
+ long add(Arts::KWidget_impl *widget, QWidget *qwidget);
+ Arts::Widget lookupWidget(long ID);
+ QWidget *lookupQWidget(long ID);
+ void remove(long ID);
+
+ static KWidgetRepo *the();
+ static void shutdown();
+};
+
+#endif /* KWIDGETREPO_H */
diff --git a/arts/gui/kde/mcopclass/Button.mcopclass b/arts/gui/kde/mcopclass/Button.mcopclass
new file mode 100644
index 00000000..d5e06c30
--- /dev/null
+++ b/arts/gui/kde/mcopclass/Button.mcopclass
@@ -0,0 +1,5 @@
+Buildable=true
+Interface=Arts::Button,Arts::Widget,Arts::Object
+Language=C++
+Library=libartsgui_kde.la
+Requires=kdegui
diff --git a/arts/gui/kde/mcopclass/Fader.mcopclass b/arts/gui/kde/mcopclass/Fader.mcopclass
new file mode 100644
index 00000000..4071180d
--- /dev/null
+++ b/arts/gui/kde/mcopclass/Fader.mcopclass
@@ -0,0 +1,5 @@
+Buildable=true
+Interface=Arts::Fader,Arts::Widget,Arts::Object
+Language=C++
+Library=libartsgui_kde.la
+Requires=kdegui
diff --git a/arts/gui/kde/mcopclass/Graph.mcopclass b/arts/gui/kde/mcopclass/Graph.mcopclass
new file mode 100644
index 00000000..f1d98d3a
--- /dev/null
+++ b/arts/gui/kde/mcopclass/Graph.mcopclass
@@ -0,0 +1,5 @@
+Buildable=true
+Interface=Arts::Graph,Arts::Widget,Arts::Object
+Language=C++
+Library=libartsgui_kde.la
+Requires=kdegui
diff --git a/arts/gui/kde/mcopclass/GraphLine.mcopclass b/arts/gui/kde/mcopclass/GraphLine.mcopclass
new file mode 100644
index 00000000..57b48f2c
--- /dev/null
+++ b/arts/gui/kde/mcopclass/GraphLine.mcopclass
@@ -0,0 +1,5 @@
+Buildable=true
+Interface=Arts::GraphLine,Arts::Object
+Language=C++
+Library=libartsgui_kde.la
+Requires=kdegui
diff --git a/arts/gui/kde/mcopclass/HBox.mcopclass b/arts/gui/kde/mcopclass/HBox.mcopclass
new file mode 100644
index 00000000..fce0f113
--- /dev/null
+++ b/arts/gui/kde/mcopclass/HBox.mcopclass
@@ -0,0 +1,5 @@
+Buildable=true
+Interface=Arts::HBox,Arts::Frame,Arts::Widget,Arts::Object
+Language=C++
+Library=libartsgui_kde.la
+Requires=kdegui
diff --git a/arts/gui/kde/mcopclass/Label.mcopclass b/arts/gui/kde/mcopclass/Label.mcopclass
new file mode 100644
index 00000000..2bad3990
--- /dev/null
+++ b/arts/gui/kde/mcopclass/Label.mcopclass
@@ -0,0 +1,5 @@
+Buildable=true
+Interface=Arts::Label,Arts::Frame,Arts::Widget,Arts::Object
+Language=C++
+Library=libartsgui_kde.la
+Requires=kdegui
diff --git a/arts/gui/kde/mcopclass/LayoutBox.mcopclass b/arts/gui/kde/mcopclass/LayoutBox.mcopclass
new file mode 100644
index 00000000..48292c85
--- /dev/null
+++ b/arts/gui/kde/mcopclass/LayoutBox.mcopclass
@@ -0,0 +1,5 @@
+Buildable=true
+Interface=Arts::LayoutBox,Arts::Frame,Arts::Widget,Arts::Object
+Language=C++
+Library=libartsgui_kde.la
+Requires=kdegui
diff --git a/arts/gui/kde/mcopclass/LevelMeter.mcopclass b/arts/gui/kde/mcopclass/LevelMeter.mcopclass
new file mode 100644
index 00000000..77f804b9
--- /dev/null
+++ b/arts/gui/kde/mcopclass/LevelMeter.mcopclass
@@ -0,0 +1,5 @@
+Buildable=true
+Interface=Arts::LevelMeter,Arts::Frame,Arts::Widget,Arts::Object
+Language=C++
+Library=libartsgui_kde.la
+Requires=kdegui
diff --git a/arts/gui/kde/mcopclass/LineEdit.mcopclass b/arts/gui/kde/mcopclass/LineEdit.mcopclass
new file mode 100644
index 00000000..69b68703
--- /dev/null
+++ b/arts/gui/kde/mcopclass/LineEdit.mcopclass
@@ -0,0 +1,5 @@
+Buildable=true
+Interface=Arts::LineEdit,Arts::Widget,Arts::Object
+Language=C++
+Library=libartsgui_kde.la
+Requires=kdegui
diff --git a/arts/gui/kde/mcopclass/Makefile.am b/arts/gui/kde/mcopclass/Makefile.am
new file mode 100644
index 00000000..fac44b22
--- /dev/null
+++ b/arts/gui/kde/mcopclass/Makefile.am
@@ -0,0 +1,5 @@
+mcopclassdir = $(libdir)/mcop/Arts
+mcopclass_DATA = Widget.mcopclass Button.mcopclass Poti.mcopclass \
+ LineEdit.mcopclass HBox.mcopclass VBox.mcopclass SpinBox.mcopclass \
+ Fader.mcopclass GraphLine.mcopclass LayoutBox.mcopclass \
+ PopupBox.mcopclass LevelMeter.mcopclass Label.mcopclass
diff --git a/arts/gui/kde/mcopclass/PopupBox.mcopclass b/arts/gui/kde/mcopclass/PopupBox.mcopclass
new file mode 100644
index 00000000..5ac282d1
--- /dev/null
+++ b/arts/gui/kde/mcopclass/PopupBox.mcopclass
@@ -0,0 +1,5 @@
+Buildable=true
+Interface=Arts::PopupBox,Arts::Frame,Arts::Widget,Arts::Object
+Language=C++
+Library=libartsgui_kde.la
+Requires=kdegui
diff --git a/arts/gui/kde/mcopclass/Poti.mcopclass b/arts/gui/kde/mcopclass/Poti.mcopclass
new file mode 100644
index 00000000..cae9a5e9
--- /dev/null
+++ b/arts/gui/kde/mcopclass/Poti.mcopclass
@@ -0,0 +1,5 @@
+Buildable=true
+Interface=Arts::Poti,Arts::Frame,Arts::Widget,Arts::Object
+Language=C++
+Library=libartsgui_kde.la
+Requires=kdegui
diff --git a/arts/gui/kde/mcopclass/SpinBox.mcopclass b/arts/gui/kde/mcopclass/SpinBox.mcopclass
new file mode 100644
index 00000000..68cfc5eb
--- /dev/null
+++ b/arts/gui/kde/mcopclass/SpinBox.mcopclass
@@ -0,0 +1,5 @@
+Buildable=true
+Interface=Arts::SpinBox,Arts::Widget,Arts::Object
+Language=C++
+Library=libartsgui_kde.la
+Requires=kdegui
diff --git a/arts/gui/kde/mcopclass/VBox.mcopclass b/arts/gui/kde/mcopclass/VBox.mcopclass
new file mode 100644
index 00000000..b32c302e
--- /dev/null
+++ b/arts/gui/kde/mcopclass/VBox.mcopclass
@@ -0,0 +1,5 @@
+Buildable=true
+Interface=Arts::VBox,Arts::Frame,Arts::Widget,Arts::Object
+Language=C++
+Library=libartsgui_kde.la
+Requires=kdegui
diff --git a/arts/gui/kde/mcopclass/Widget.mcopclass b/arts/gui/kde/mcopclass/Widget.mcopclass
new file mode 100644
index 00000000..54a6e91f
--- /dev/null
+++ b/arts/gui/kde/mcopclass/Widget.mcopclass
@@ -0,0 +1,5 @@
+Buildable=true
+Interface=Arts::Widget,Arts::Object
+Language=C++
+Library=libartsgui_kde.la
+Requires=kdegui
diff --git a/arts/midi/Makefile.am b/arts/midi/Makefile.am
new file mode 100644
index 00000000..b5719649
--- /dev/null
+++ b/arts/midi/Makefile.am
@@ -0,0 +1,53 @@
+####### Various modules for artsmodules
+
+SUBDIRS = mcopclass
+INCLUDES= -I$(top_builddir)/arts/runtime -I$(srcdir)/freeverb -I$(arts_includes) $(all_includes)
+
+lib_LTLIBRARIES = libartsmidi_idl.la libartsmidi.la
+
+bin_PROGRAMS = midisend
+noinst_PROGRAMS = midisynctest
+
+midisend_SOURCES = midisend.cc midimsg.c
+midisend_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIBPTHREAD)
+midisend_LDADD = libartsmidi_idl.la
+midisend_COMPILE_FIRST = artsmidi.h
+
+midisynctest_SOURCES = midisynctest.cc
+midisynctest_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIBPTHREAD)
+midisynctest_LDADD = libartsmidi.la -lsoundserver_idl
+midisynctest_COMPILE_FIRST = artsmidi.h
+
+libartsmidi_idl_la_SOURCES = artsmidi.cc
+libartsmidi_idl_la_LIBADD = -lmcop -lartsflow
+libartsmidi_idl_la_LDFLAGS = $(all_libraries) -L$(arts_libraries) \
+ -no-undefined
+
+libartsmidi_la_SOURCES = midimanager_impl.cc midiclient_impl.cc \
+ miditest_impl.cc midimanagerport_impl.cc rawmidiport_impl.cc \
+ systemmiditimer_impl.cc audiomiditimer_impl.cc miditimercommon.cc \
+ audiosync_impl.cc audiotimer.cc alsamidigateway_impl.cc \
+ alsamidiport_impl.cc midisyncgroup_impl.cc timestampmath.cc
+libartsmidi_la_COMPILE_FIRST = artsmidi.h
+
+libartsmidi_la_LIBADD = libartsmidi_idl.la -lartsflow $(ARTS_LIBASOUND)
+libartsmidi_la_LDFLAGS = $(all_libraries) -L$(arts_libraries) \
+ -no-undefined
+
+artsmidi.mcopclass: artsmidi.h
+artsmidi.mcoptype: artsmidi.h
+artsmidi.cc artsmidi.h: $(srcdir)/artsmidi.idl $(MCOPIDL)
+ $(MCOPIDL) -t -I$(arts_includes) $(srcdir)/artsmidi.idl
+
+DISTCLEANFILES = artsmidi.cc artsmidi.h \
+ artsmidi.mcoptype artsmidi.mcopclass
+
+####### install idl files
+
+artsincludedir = $(includedir)/arts
+artsinclude_HEADERS = artsmidi.h artsmidi.idl
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = artsmidi.mcoptype artsmidi.mcopclass
+
+artsmidi.lo: artsmidi.h
diff --git a/arts/midi/README.midi b/arts/midi/README.midi
new file mode 100644
index 00000000..e341ca01
--- /dev/null
+++ b/arts/midi/README.midi
@@ -0,0 +1,261 @@
+Midi, Audio and Synchronization:
+================================
+
+1. Introduction
+2. The midi manager
+3. Midi synchronization
+4. Audio timestamping and synchronization
+5. Example code
+
+
+ 1. Introduction
+ ---------------
+
+Since aRts-1.0 (as shipped with KDE3.0), aRts provides a lot more
+infrastructure to deal with midi, audio, and their synchronization. The main
+goal is to provide a unified interface between sequencers (or other programs
+that require notes or audio tracks to be played at certain given time stamps)
+and underlying software/hardware that can play notes/audio tracks.
+
+Currently, there exist five distinct destinations that aRts supports, which
+can all be used at the same time or individually, that is:
+
+ * aRts synthetic midi instruments
+ * ALSA-0.5
+ * ALSA-0.9
+ * OSS
+ * other aRts modules (including but not limited to the playback/recording
+ of audio tracks)
+
+ 2. The midi manager
+ -------------------
+
+The midi manager is the basic component that connects between applications
+that supply/record midi data, and devices that process midi data. Devices
+might be both, virtual (as in software synthesis) or real (as in hardware
+devices).
+
+From the view of the midi manager, all event streams correspond to one midi
+client. So, a midi client might be an application (such as a sequencer) that
+provides events, or an ALSA hardware device that consumes events. If there
+are multiple event streams, they correspond to multiple clients. That is,
+if an application wishes to play three different midi tracks, one over ALSA,
+and two over two different synthetic instruments, it needs to register itself
+three times, with three different clients.
+
+The midi managers job is to connect midi clients (as in event streams). It
+maintains a list of connections that the user can modify with an application
+like artscontrol. Applications could also, if they wish so, modify this
+connection list.
+
+As a use case, we'll consider the following: you want to write a sequencer
+application that plays back two different tracks to two different devices.
+You want the user to be able to select these devices in a drop down box for
+each track.
+
+1) getting a list of choices:
+
+First, you will want to obtain a list of choices which the user could possibly
+connect your tracks to. You do so by reading the
+
+interface MidiManager { // SINGLETON: Arts_MidiManager
+ /**
+ * a list of clients
+ */
+ readonly attribute sequence<MidiClientInfo> clients;
+
+ //...
+};
+
+attribute. The three fields of each client that are interesting for you are
+
+struct MidiClientInfo {
+ long ID;
+
+ //...
+
+ MidiClientDirection direction;
+ MidiClientType type;
+ string title;
+};
+
+You would list those devices in the dropdown box that are of the appropriate
+direction, which is mcdRecord, as you would want a client that receives midi
+events (this might be confusing, but you look from the view of the client).
+
+Then, there is the type field, which tells you whether the client is a device-
+like thing (like a synthetic instrument), or another application (like another
+application currently recording a track). While it might not be an impossible
+setup that you send events between two applications, usually users will choose
+such clients that have mctDestination as type.
+
+Finally, you can list the titles in a drop down box, and keep the ID for making
+a connection later.
+
+2) registering clients:
+
+You will need to register one client for each track. Use
+
+ /**
+ * add a client
+ *
+ * this creates a new MidiManagerClient
+ */
+ MidiClient addClient(MidiClientDirection direction, MidiClientType type,
+ string title, string autoRestoreID);
+
+to do so.
+
+3) connecting:
+
+As you probably don't want your sequencer user to use artscontrol to setup
+connections between your tracks and the devices, you will need to connect
+your clients to the hardware devices for playing something.
+
+You can connect clients to their appropriate destinations using
+
+ /**
+ * connect two clients
+ */
+ void connect(long clientID, long destinationID);
+
+and
+
+ /**
+ * disconnect two clients
+ */
+ void disconnect(long clientID, long destinationID);
+
+Keep in mind that a client might be connected to more than one destination
+at the same time, so that you will need to disconnect the old destination
+before connecting the new one.
+
+4) playing events:
+
+You can now play events to the tracks, using each client's
+
+ MidiPort addOutputPort();
+
+function for getting a port where you can send events to. However, you will
+also need to ensure that the events will get synchronized as soon as you are
+playing back events to different devices. Read the next section for details
+on this.
+
+ 3. Midi synchronization
+ -----------------------
+
+As soon as you are writing a real sequencer, you might want to output to more
+than one midi device at a time. For instance, you might want to let some of
+your midi events be played by aRts synthesis, while others should be sent
+over the external midi port.
+
+To support this setup, a new interface called MidiSyncGroup has been added. To
+output midi events synchronized over more than one port, you proceed as follows:
+
+a) you obtain a reference to the midi manager object
+
+ MidiManager midiManager = DynamicCast(Reference("global:Arts_MidiManager"));
+ if(midiManager.isNull()) arts_fatal("midimanager is null");
+
+b) you create a midi synchronization group which will ensure that the
+ timestamps of your midi events will be synchronized
+
+ MidiSyncGroup syncGroup = midiManager.addSyncGroup();
+
+c) you add a client to the midi manager for each port you want to output
+ midi data over
+
+ MidiClient client = midiManager.addClient(mcdPlay, mctApplication, "midisynctest", "midisynctest");
+ MidiClient client2 = midiManager.addClient(mcdPlay, mctApplication, "midisynctest2", "midisynctest2");
+
+d) you insert the clients in the synchronization group
+
+ syncGroup.addClient(client);
+ syncGroup.addClient(client2);
+
+e) you create ports for each client as usual
+
+ MidiPort port = client.addOutputPort();
+ MidiPort port2 = client2.addOutputPort();
+
+f) at this point, you will need to ensure that the midi clients you created
+ are connected, you can either leave the user with artscontrol for doing
+ this, or use the clients and connect methods of the midiManager object
+ yourself (see use case discussed in previous section)
+
+g) you output events over the ports as usual
+
+ /* where t is a suitable TimeStamp */
+ MidiEvent e = MidiEvent(t,MidiCommand(mcsNoteOn|0, notes[np], 100));
+ port.processEvent(e);
+ port2.processEvent(e);
+
+ 4. Audio timestamping and synchronization
+ -----------------------------------------
+
+Audio in aRts is usually handled as structures consisting of small modules
+that do something. While this model allows you to describe anything you want
+to, from playing a sample to playing a synthetic sequence of notes with a
+synthetic instruments, it doesn't give you any notion of time. More so, if
+you build a large graph of objects, you might need quite some time for this,
+and you will want to have them all started at the same time.
+
+To solve this issue, an AudioSync interface has been introduced, that allows
+you to start() and stop() either synchronized at a specific point in time.
+
+Suppose you have two synthesis modules which together play back a sample.
+What can you do to start them at the same time?
+
+ Synth_PLAY_WAV wav = //... create on server
+ Synth_AMAN_PLAY sap //... create on server
+ AudioSync audioSync = //... create on server
+
+ wav.filename("/opt/kde3/share/sounds/pop.wav");
+ sap.title("midisynctest2");
+ sap.autoRestoreID("midisynctest2");
+ connect(wav,sap);
+
+ // this queues back start() to be called atomically later
+ audioSync.queueStart(wav);
+ audioSync.queueStart(sap);
+
+ // this line is a synchronized version of
+ // wav.start();
+ // sap.start();
+ audioSync.execute();
+
+You could also play them back at a specific time in the future and query the
+current time using the time and executeAt methods:
+
+interface AudioSync {
+ /**
+ * the current time
+ */
+ readonly attribute TimeStamp time;
+
+ //...
+
+ /**
+ * atomically executes all queued modifications to the flow system
+ * at a given time
+ */
+ void executeAt(TimeStamp timeStamp);
+};
+
+Finally, to get synchronized midi and audio, you can insert the AudioSync
+object into a midi synchronization group, then their timestamps will be
+synchronized to those of the midi channels.
+
+ 5. Example code
+ ---------------
+
+An example that illustrates most things discussed in this document is
+midisynctest.cc, which plays back two synchronized midi streams and samples.
+Note that you might want to change the source code, as it hardcodes the
+location of the .wav file.
+
+
+Questions and comments are welcome.
+
+Stefan Westerfeld
+stefan@space.twc.de
diff --git a/arts/midi/alsamidigateway_impl.cc b/arts/midi/alsamidigateway_impl.cc
new file mode 100644
index 00000000..91a0f118
--- /dev/null
+++ b/arts/midi/alsamidigateway_impl.cc
@@ -0,0 +1,243 @@
+ /*
+
+ Copyright (C) 2001-2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmidi.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/**
+ * compile real version if we have ALSA support, dummy version otherwise
+ */
+#if defined(HAVE_ARTS_LIBASOUND2) || defined(HAVE_ARTS_LIBASOUND)
+
+#ifdef HAVE_ALSA_ASOUNDLIB_H
+#include <alsa/asoundlib.h>
+#elif defined(HAVE_SYS_ASOUNDLIB_H)
+#include <sys/asoundlib.h>
+#endif
+
+#include "alsamidiport_impl.h"
+#include <arts/debug.h>
+#include <stdio.h>
+
+using namespace Arts;
+using namespace std;
+
+class AlsaMidiGateway_impl : virtual public AlsaMidiGateway_skel {
+protected:
+ snd_seq_t *seq;
+
+ struct PortEntry {
+ int alsaClient, alsaPort;
+ bool keep;
+
+ AlsaMidiPort port;
+ MidiClient client;
+ };
+ list<PortEntry> ports;
+
+#ifdef HAVE_ARTS_LIBASOUND2
+/* ALSA-0.9 specific code */
+ int alsaOpen() {
+ return snd_seq_open(&seq, "hw", SND_SEQ_OPEN_DUPLEX, 0);
+ }
+ bool alsaScan(MidiManager midiManager) {
+ snd_seq_client_info_t *cinfo;
+ snd_seq_port_info_t *pinfo;
+
+ snd_seq_client_info_alloca(&cinfo);
+ snd_seq_client_info_set_client(cinfo, -1);
+
+ while (snd_seq_query_next_client(seq, cinfo) >= 0) {
+ int client = snd_seq_client_info_get_client(cinfo);
+
+ snd_seq_port_info_alloca(&pinfo);
+ snd_seq_port_info_set_client(pinfo, client);
+
+ snd_seq_port_info_set_port(pinfo, -1);
+ while (snd_seq_query_next_port(seq, pinfo) >= 0) {
+ unsigned int cap;
+
+ cap = (SND_SEQ_PORT_CAP_SUBS_WRITE|SND_SEQ_PORT_CAP_WRITE);
+ if ((snd_seq_port_info_get_capability(pinfo) & cap) == cap) {
+ string name = snd_seq_port_info_get_name(pinfo);
+ int client = snd_seq_port_info_get_client(pinfo);
+ int port = snd_seq_port_info_get_port(pinfo);
+
+ createPort(midiManager, name, client, port);
+ }
+ }
+ }
+
+ return true;
+ }
+#else
+/* ALSA-0.5 specific code */
+ int alsaOpen() {
+ return snd_seq_open(&seq, SND_SEQ_OPEN);
+ }
+
+ bool alsaScan(MidiManager midiManager) {
+ snd_seq_system_info_t sysinfo;
+
+ int err = snd_seq_system_info(seq, &sysinfo);
+ if (err < 0)
+ {
+ arts_warning("snd_seq_systeminfo failed: %s", snd_strerror(err));
+ return false;
+ }
+
+ for(int client = 0; client < sysinfo.clients; client++)
+ {
+ snd_seq_client_info_t cinfo;
+ if (snd_seq_get_any_client_info(seq, client, &cinfo) == 0)
+ {
+ for(int port = 0; port < sysinfo.ports; port++)
+ {
+ snd_seq_port_info_t pinfo;
+ if(snd_seq_get_any_port_info(seq, client, port, &pinfo) == 0)
+ {
+ unsigned int cap;
+ cap = (SND_SEQ_PORT_CAP_SUBS_WRITE|SND_SEQ_PORT_CAP_WRITE);
+
+ if ((pinfo.capability & cap) == cap)
+ createPort(midiManager, pinfo.name, client, port);
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+#endif
+
+public:
+ AlsaMidiGateway_impl() : seq(0)
+ {
+ }
+
+ ~AlsaMidiGateway_impl()
+ {
+ if(seq)
+ snd_seq_close(seq);
+ }
+
+ void createPort(MidiManager midiManager, string name, int client, int port)
+ {
+ if(name != "aRts")
+ {
+ char nr[1024];
+
+ sprintf(nr, " (%3d:%-3d)", client, port);
+ name += nr;
+
+ list<PortEntry>::iterator pi = ports.begin();
+ while(pi != ports.end() && (pi->alsaClient != client || pi->alsaPort != port))
+ pi++;
+
+ if(pi != ports.end()) /* we already have this port */
+ pi->keep = true;
+ else /* we need to create it */
+ {
+ PortEntry pe;
+ pe.port = AlsaMidiPort::_from_base(
+ new AlsaMidiPort_impl(seq, client, port));
+
+ if(pe.port.open())
+ {
+ pe.client = midiManager.addClient(mcdRecord,
+ mctDestination,
+ name, name);
+ pe.client.addInputPort(pe.port);
+ pe.alsaClient = client;
+ pe.alsaPort = port;
+ pe.keep = true;
+
+ ports.push_back(pe);
+ }
+ }
+ }
+ }
+
+ bool rescan()
+ {
+ MidiManager midiManager = DynamicCast(Reference("global:Arts_MidiManager"));
+ if(midiManager.isNull())
+ {
+ arts_warning("AlsaMidiGateway: can't find MidiManager");
+ return false;
+ }
+
+ if(!seq)
+ {
+ int err = alsaOpen();
+ if (err < 0)
+ {
+ arts_warning("AlsaMidiGateway: could not open sequencer %s",
+ snd_strerror(err));
+ seq = 0;
+ return false;
+ }
+ }
+
+ list<PortEntry>::iterator pi;
+ for(pi = ports.begin(); pi != ports.end(); pi++)
+ pi->keep = false;
+
+ if(!alsaScan(midiManager))
+ return false;
+
+ /* erase those ports that are no longer needed */
+ pi = ports.begin();
+ while(pi != ports.end())
+ {
+ if(!pi->keep)
+ pi = ports.erase(pi);
+ else
+ pi++;
+ }
+
+ return true;
+ }
+};
+
+#else
+
+using namespace Arts;
+using namespace std;
+
+class AlsaMidiGateway_impl : virtual public AlsaMidiGateway_skel {
+public:
+ bool rescan()
+ {
+ /* dummy version: no ALSA support compiled in */
+ return false;
+ }
+};
+
+#endif
+
+namespace Arts {
+ REGISTER_IMPLEMENTATION(AlsaMidiGateway_impl);
+}
diff --git a/arts/midi/alsamidiport_impl.cc b/arts/midi/alsamidiport_impl.cc
new file mode 100644
index 00000000..e53d7fe7
--- /dev/null
+++ b/arts/midi/alsamidiport_impl.cc
@@ -0,0 +1,232 @@
+ /*
+
+ Copyright (C) 2001-2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "alsamidiport_impl.h"
+
+#if defined(HAVE_ARTS_LIBASOUND2) || defined(HAVE_ARTS_LIBASOUND)
+#include <arts/debug.h>
+
+#ifdef HAVE_ARTS_LIBASOUND
+#define snd_seq_queue_status_alloca(x) \
+ *x = (snd_seq_queue_status_t *)alloca(sizeof(snd_seq_queue_status_t))
+#define snd_seq_queue_status_get_tick_time(x) x->tick
+#define snd_seq_queue_status_get_real_time(x) (&(x->time))
+#endif
+
+using namespace std;
+using namespace Arts;
+
+AlsaMidiPort_impl::AlsaMidiPort_impl(snd_seq_t *seq, long client, long port)
+ : _client(client), _port(port), alsaSeq(seq)
+{
+ opened = false;
+}
+
+/* interface MidiPort */
+Arts::TimeStamp AlsaMidiPort_impl::time()
+{
+ snd_seq_queue_status_t *status;
+ snd_seq_queue_status_alloca(&status);
+
+ snd_seq_get_queue_status(alsaSeq, alsaQueue, status);
+ snd_seq_tick_time_t ttime = snd_seq_queue_status_get_tick_time(status);
+ const snd_seq_real_time_t *rtime =
+ snd_seq_queue_status_get_real_time(status);
+
+ return Arts::TimeStamp(rtime->tv_sec, rtime->tv_nsec / 1000);
+}
+
+Arts::TimeStamp AlsaMidiPort_impl::playTime()
+{
+ return time();
+}
+
+void AlsaMidiPort_impl::fillAlsaEvent(snd_seq_event_t *ev,
+ const MidiCommand& command)
+{
+ ev->source = alsaSourceAddr;
+ ev->dest = alsaDestAddr;
+
+ mcopbyte channel = command.status & mcsChannelMask;
+
+ switch(command.status & mcsCommandMask)
+ {
+ case mcsNoteOn:
+ snd_seq_ev_set_noteon(ev, channel, command.data1, command.data2);
+ break;
+ case mcsNoteOff:
+ snd_seq_ev_set_noteoff(ev, channel, command.data1, command.data2);
+ break;
+ case mcsProgram:
+ snd_seq_ev_set_pgmchange(ev, channel, command.data1);
+ break;
+ case mcsParameter:
+ snd_seq_ev_set_controller(ev, channel, command.data1, command.data2);
+ break;
+ default:
+ /* unhandled */
+ return;
+ }
+}
+
+void AlsaMidiPort_impl::sendAlsaEvent(snd_seq_event_t *ev)
+{
+ int ret = snd_seq_event_output(alsaSeq, ev);
+ if (ret < 0) {
+ arts_warning("AlsaMidiPort: error writing note %s\n",
+ snd_strerror(ret));
+ return;
+ }
+ flushAlsa();
+}
+
+void AlsaMidiPort_impl::processCommand(const MidiCommand& command)
+{
+ snd_seq_event_t ev;
+ snd_seq_ev_clear(&ev);
+
+ fillAlsaEvent(&ev, command);
+ sendAlsaEvent(&ev);
+}
+
+void AlsaMidiPort_impl::processEvent(const MidiEvent& event)
+{
+ snd_seq_event_t ev;
+ snd_seq_real_time_t time;
+
+ time.tv_sec = event.time.sec;
+ time.tv_nsec = event.time.usec * 1000;
+
+ snd_seq_ev_clear(&ev);
+ snd_seq_ev_schedule_real(&ev, alsaQueue, 0, &time);
+
+ fillAlsaEvent(&ev, event.command);
+ sendAlsaEvent(&ev);
+}
+
+/* interface AlsaMidiPort */
+void AlsaMidiPort_impl::client(long newClient)
+{
+ if(newClient != _client)
+ {
+ _client = newClient;
+
+ if(opened)
+ {
+ close();
+ open();
+ }
+
+ client_changed(newClient);
+ }
+}
+
+long AlsaMidiPort_impl::client()
+{
+ return _client;
+}
+
+void AlsaMidiPort_impl::port(long newPort)
+{
+ if(newPort != _port)
+ {
+ _port = newPort;
+
+ if(opened)
+ {
+ close();
+ open();
+ }
+
+ port_changed(newPort);
+ }
+}
+
+long AlsaMidiPort_impl::port()
+{
+ return _port;
+}
+
+bool AlsaMidiPort_impl::open()
+{
+ arts_return_val_if_fail(opened == false, false);
+
+ alsaQueue = snd_seq_alloc_queue(alsaSeq);
+ alsaClientId = snd_seq_client_id(alsaSeq);
+
+ alsaPort = snd_seq_create_simple_port(alsaSeq, "aRts",
+ SND_SEQ_PORT_CAP_WRITE |
+ SND_SEQ_PORT_CAP_SUBS_WRITE |
+ SND_SEQ_PORT_CAP_READ,
+ SND_SEQ_PORT_TYPE_MIDI_GENERIC);
+ if (alsaPort < 0) {
+ arts_warning("AlsaMidiPort: can't creating port %s\n",
+ snd_strerror(alsaPort));
+ return false;
+ }
+
+ alsaSourceAddr.client = alsaClientId;
+ alsaSourceAddr.port = alsaPort;
+
+ alsaDestAddr.client = _client;
+ alsaDestAddr.port = _port;
+
+ int ret;
+ ret = snd_seq_connect_to(alsaSeq, alsaPort,
+ alsaDestAddr.client,
+ alsaDestAddr.port);
+ if (ret < 0) {
+ arts_warning("AlsaMidiPort: error connecting port %s\n",
+ snd_strerror(ret));
+ /* FIXME: destroy port here */
+ return false;
+ }
+
+ snd_seq_start_queue(alsaSeq, alsaQueue, 0);
+ flushAlsa();
+
+ opened = true;
+ return true;
+}
+
+void AlsaMidiPort_impl::close()
+{
+ if(!opened)
+ return;
+
+ opened = false;
+}
+
+void AlsaMidiPort_impl::flushAlsa()
+{
+#ifdef HAVE_ARTS_LIBASOUND2
+ snd_seq_drain_output(alsaSeq);
+#else
+ int err;
+ while((err = snd_seq_flush_output(alsaSeq)) > 0)
+ {
+ arts_debug("alsa flush error %d\n",snd_strerror(err));
+ usleep(2000);
+ }
+#endif
+}
+#endif
diff --git a/arts/midi/alsamidiport_impl.h b/arts/midi/alsamidiport_impl.h
new file mode 100644
index 00000000..251f9d94
--- /dev/null
+++ b/arts/midi/alsamidiport_impl.h
@@ -0,0 +1,85 @@
+ /*
+
+ Copyright (C) 2001-2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_ALSAMIDIPORT_IMPL_H
+#define ARTS_ALSAMIDIPORT_IMPL_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/**
+ * compile real version if we have ALSA support, dummy version otherwise
+ */
+#if defined(HAVE_ARTS_LIBASOUND2) || defined(HAVE_ARTS_LIBASOUND)
+
+#ifdef HAVE_ALSA_ASOUNDLIB_H
+#include <alsa/asoundlib.h>
+#elif defined(HAVE_SYS_ASOUNDLIB_H)
+#include <sys/asoundlib.h>
+#endif
+
+#include "artsmidi.h"
+
+namespace Arts {
+
+class AlsaMidiPort_impl : virtual public AlsaMidiPort_skel {
+protected:
+ long _client, _port;
+ bool opened;
+
+ snd_seq_t *alsaSeq;
+
+ int alsaQueue;
+ int alsaClientId;
+ int alsaPort;
+
+ snd_seq_addr_t alsaSourceAddr;
+ snd_seq_addr_t alsaDestAddr;
+
+ void fillAlsaEvent(snd_seq_event_t *ev, const MidiCommand& command);
+ void sendAlsaEvent(snd_seq_event_t *ev);
+ void flushAlsa();
+
+public:
+ AlsaMidiPort_impl(snd_seq_t *seq, long client, long port);
+ void close();
+
+ /* interface MidiPort */
+ Arts::TimeStamp time();
+ Arts::TimeStamp playTime();
+ void processCommand(const MidiCommand& command);
+ void processEvent(const MidiEvent& event);
+
+ /* interface AlsaMidiPort */
+ void client(long newClient);
+ long client();
+
+ void port(long newPort);
+ long port();
+
+ bool open();
+};
+
+}
+#endif /* HAVE_ARTS_LIBASOUND2 */
+#endif /* ARTS_ALSAMIDIPORT_IMPL_H */
diff --git a/arts/midi/artsmidi.idl b/arts/midi/artsmidi.idl
new file mode 100644
index 00000000..63bf868b
--- /dev/null
+++ b/arts/midi/artsmidi.idl
@@ -0,0 +1,379 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+/*
+ * DISCLAIMER: The interfaces in artsmidi.idl (and the derived .cc/.h files)
+ * DO NOT GUARANTEE BINARY COMPATIBILITY YET.
+ *
+ * They are intended for developers. You shouldn't expect that applications in
+ * binary form will be fully compatibile with further releases of these
+ * interfaces.
+ */
+
+#include <artsflow.idl>
+
+module Arts {
+
+/* This is modelled somewhat after
+ - the AudioManager concept
+ - the aRts-0.3.4.1 MidiPort concept
+ - libkmid
+
+ It adds timing as new feature compared to older implementation, and also
+ tries to do the full set of midi operations.
+
+ It's current state is "experimental", and "binary compatibility not kept".
+ */
+
+/**
+ * an absolute timestamp
+ */
+struct TimeStamp {
+ long sec,usec;
+};
+
+/**
+ * different status of a midi command
+ */
+enum MidiCommandStatus {
+// Masks:
+ mcsCommandMask = 0xf0,
+ mcsChannelMask = 0x0f,
+
+// Commands:
+ mcsNoteOff = 0x80,
+ mcsNoteOn = 0x90,
+ mcsKeyPressure = 0xa0,
+ mcsParameter = 0xb0,
+ mcsProgram = 0xc0,
+ mcsChannelPressure = 0xd0,
+ mcsPitchWheel = 0xe0
+};
+
+/**
+ * the following are to be used once status is (mcsParameter|channel):
+ */
+enum MidiCommandParameter {
+ mcpSustain = 0x40,
+ mcpAllNotesOff = 0x7b
+};
+
+/**
+ * a midi command
+ */
+struct MidiCommand {
+ byte status;
+ byte data1;
+ byte data2;
+};
+
+/**
+ * a midi event
+ */
+
+struct MidiEvent {
+ TimeStamp time;
+ MidiCommand command;
+};
+
+/**
+ * a midi port
+ */
+interface MidiPort {
+ /**
+ * the current absolute time (since the existence of the midi device)
+ */
+ readonly attribute TimeStamp time;
+
+ /**
+ * the current play time
+ *
+ * Some midi devices, for instance synthetic audio devices, have a certain
+ * amount of internal buffering. This causes a time difference between
+ * where events are currently being rendered, which is the timestamp
+ * obtained by "time", and the events that the listener is hearing right
+ * now, which is this timestamp, the "playTime".
+ */
+ readonly attribute TimeStamp playTime;
+
+ /**
+ * processes a midi command
+ */
+ oneway void processCommand(MidiCommand command);
+
+ /**
+ * processes a midi event
+ */
+ oneway void processEvent(MidiEvent event);
+};
+
+enum MidiClientDirection { mcdPlay, mcdRecord };
+enum MidiClientType { mctDestination, mctApplication };
+
+/**
+ * information about a midi client
+ */
+struct MidiClientInfo {
+ long ID;
+ sequence<long> connections;
+
+ MidiClientDirection direction;
+ MidiClientType type;
+ string title, autoRestoreID;
+};
+
+/**
+ * a midi manager client
+ */
+interface MidiClient {
+ readonly attribute MidiClientInfo info;
+
+ /**
+ * you can change the title of your client on the fly - everything else
+ * (besides the actual assignment) is static
+ */
+ attribute string title;
+
+ /**
+ * creates a new port through which the client can receive data from
+ * the midi manager
+ */
+ void addInputPort(MidiPort port);
+
+ /**
+ * creates a new port through which the client can send data to the
+ * midi manager
+ */
+ MidiPort addOutputPort();
+
+ /**
+ * removes a port
+ */
+ void removePort(MidiPort port);
+};
+
+interface AudioSync;
+
+/**
+ * this synchronizes multiple midi clients - it also allows synchronization
+ * with audio events
+ */
+interface MidiSyncGroup {
+ /**
+ * adds a midi client to the synchronization group
+ *
+ * hint: during adding the client, the timestamps related to that
+ * client will jump
+ */
+ void addClient(MidiClient client);
+
+ /**
+ * deletes a midi client from the synchronization group
+ */
+ void removeClient(MidiClient client);
+
+ /**
+ * adds an AudioSync object to the synchronization group
+ *
+ * hint: during adding the AudioSync object, the timestamps related to
+ * that object might jump
+ */
+ void addAudioSync(AudioSync audioSync);
+
+ /**
+ * deletes an AudioSync object from the synchronization group
+ */
+ void removeAudioSync(AudioSync audioSync);
+};
+
+/**
+ * Some general notes to the understanding of the midi manager. The midi
+ * manager has the task to intelligently assign applications to destinations.
+ *
+ * It is important to understand what it actually does to understand the
+ * distinction first, which is expressed through the "MidiClientType" of
+ * each client.
+ *
+ * APPLICATIONS: An application is a user visible application, that produces
+ * or records midi data. It is important for the understanding of an
+ * application, that an application actually *wants* to be supplied with
+ * data, or wants to get its data played. Thus, adding an application to
+ * the midi manager is an implicit request: "go and find a place where to
+ * put the events to (or get the events from)".
+ *
+ * Examples for applications would be games or midi players.
+ *
+ * DESTINATIONS: A destination is a system service that plays or supplies
+ * midi data. The characteristic here is that a destination is something
+ * that is there if you need it.
+ *
+ * Examples for destinations might be might be a hardware device or an
+ * emulation of a hardware device (such as a virtual sampler).
+ *
+ * So the process is as follows:
+ * - destinations register themselves at the midi manager, and provide
+ * system services in that way
+ *
+ * - when the user starts an application (such as a midi player), the midi
+ * manager's task is to assign it to a suitable destination
+ *
+ * - the user can interact with the process by changing the way applications
+ * are assigned to destinations - the midi manager will try to learn
+ * what the user wants, and next time do a better job while assigning
+ *
+ * To actually record or play some data, you need to register a client first,
+ * and after that, you can add Input or Output "MidiPort"s to your client,
+ * so that you can actually send or receive events with them.
+ */
+interface MidiManager { // SINGLETON: Arts_MidiManager
+ /**
+ * a list of clients
+ */
+ readonly attribute sequence<MidiClientInfo> clients;
+
+ /**
+ * add a client
+ *
+ * this creates a new MidiManagerClient
+ */
+ MidiClient addClient(MidiClientDirection direction, MidiClientType type,
+ string title, string autoRestoreID);
+
+ /**
+ * connect two clients
+ */
+ void connect(long clientID, long destinationID);
+
+ /**
+ * disconnect two clients
+ */
+ void disconnect(long clientID, long destinationID);
+
+ /**
+ * add a synchronization group
+ *
+ * this creates a new MidiSyncGroup
+ */
+ MidiSyncGroup addSyncGroup();
+};
+
+interface MidiTest : MidiPort {
+};
+
+interface RawMidiPort : MidiPort {
+ attribute string device;
+ attribute boolean input, output;
+ attribute boolean running;
+ boolean open();
+};
+
+interface AlsaMidiGateway {
+ boolean rescan();
+};
+
+interface AlsaMidiPort : MidiPort {
+ attribute long client;
+ attribute long port;
+ boolean open();
+};
+
+/**
+ * EXPERIMENTAL interface for audio synchronization - this allows multiple
+ * objects to be started and stopped at a precisely defined timestamp
+ */
+interface AudioSync {
+ /**
+ * the current time
+ */
+ readonly attribute TimeStamp time;
+
+ /**
+ * the current play time
+ *
+ * Since aRts has internal buffering, there is a time difference between
+ * where events are currently being rendered, which is the timestamp
+ * obtained by "time", and the events that the listener is hearing right
+ * now, which is this timestamp, the "playTime".
+ */
+ readonly attribute TimeStamp playTime;
+
+ /**
+ * queues calling synthModule.start() later
+ *
+ * (will keep a reference on the module until executed)
+ */
+ void queueStart(SynthModule synthModule);
+
+ /**
+ * queues calling synthModule.stop() later
+ *
+ * (will keep a reference on the module until executed)
+ */
+ void queueStop(SynthModule synthModule);
+
+ /**
+ * atomically executes all queued modification to the flow system
+ */
+ void execute();
+
+ /**
+ * atomically executes all queued modifications to the flow system
+ * at a given time
+ */
+ void executeAt(TimeStamp timeStamp);
+};
+
+/**
+ * Midi Timer - can be used to provide timing for midi ports that have
+ * no "native" timing.
+ */
+interface MidiTimer
+{
+ /**
+ * the current time
+ */
+ readonly attribute TimeStamp time;
+
+ /**
+ * this will put the event into an event queue and send it to the port
+ * once the time for the event has been reached
+ */
+ oneway void queueEvent(MidiPort port, MidiEvent event);
+};
+
+/**
+ * Uses the system time (i.e. gettimeofday() and similar) to provide midi
+ * timing
+ */
+interface SystemMidiTimer : MidiTimer
+{
+};
+
+/**
+ * Uses the audio time (i.e. samples rendered to /dev/dsp) to provide midi
+ * timing
+ */
+interface AudioMidiTimer : MidiTimer
+{
+};
+
+};
diff --git a/arts/midi/audiomiditimer_impl.cc b/arts/midi/audiomiditimer_impl.cc
new file mode 100644
index 00000000..1e39e4f8
--- /dev/null
+++ b/arts/midi/audiomiditimer_impl.cc
@@ -0,0 +1,116 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmidi.h"
+#include "artsflow.h"
+#include "stdsynthmodule.h"
+#include "debug.h"
+#include "miditimercommon.h"
+#include "audiotimer.h"
+#include "flowsystem.h"
+
+using namespace std;
+
+namespace Arts {
+
+class AudioMidiTimerCommon : public MidiTimerCommon, public AudioTimerCallback
+{
+protected:
+ AudioMidiTimerCommon();
+ virtual ~AudioMidiTimerCommon();
+
+ AudioTimer *audioTimer;
+
+public:
+ // allocation: share one AudioMidiTimerCommon for everbody who needs one
+ static AudioMidiTimerCommon *subscribe();
+
+ TimeStamp time();
+ void updateTime();
+};
+
+}
+
+using namespace Arts;
+
+static AudioMidiTimerCommon *AudioMidiTimerCommon_the = 0;
+
+AudioMidiTimerCommon::AudioMidiTimerCommon()
+{
+ AudioMidiTimerCommon_the = this;
+
+ audioTimer = AudioTimer::subscribe();
+ audioTimer->addCallback(this);
+}
+
+AudioMidiTimerCommon::~AudioMidiTimerCommon()
+{
+ audioTimer->removeCallback(this);
+ audioTimer->unsubscribe();
+
+ AudioMidiTimerCommon_the = 0;
+}
+
+TimeStamp AudioMidiTimerCommon::time()
+{
+ return audioTimer->time();
+}
+
+void AudioMidiTimerCommon::updateTime()
+{
+ processQueue();
+}
+
+AudioMidiTimerCommon *AudioMidiTimerCommon::subscribe()
+{
+ if(!AudioMidiTimerCommon_the)
+ AudioMidiTimerCommon_the = new AudioMidiTimerCommon();
+ AudioMidiTimerCommon_the->refCount++;
+ return AudioMidiTimerCommon_the;
+}
+
+namespace Arts {
+
+class AudioMidiTimer_impl : public AudioMidiTimer_skel {
+protected:
+ AudioMidiTimerCommon *timer;
+public:
+ AudioMidiTimer_impl()
+ {
+ timer = AudioMidiTimerCommon::subscribe();
+ }
+ ~AudioMidiTimer_impl()
+ {
+ timer->unsubscribe();
+ }
+ TimeStamp time()
+ {
+ return timer->time();
+ }
+ void queueEvent(MidiPort port, const MidiEvent& event)
+ {
+ timer->queueEvent(port, event);
+ }
+};
+
+REGISTER_IMPLEMENTATION(AudioMidiTimer_impl);
+}
diff --git a/arts/midi/audiosync_impl.cc b/arts/midi/audiosync_impl.cc
new file mode 100644
index 00000000..fdba8231
--- /dev/null
+++ b/arts/midi/audiosync_impl.cc
@@ -0,0 +1,203 @@
+ /*
+
+ Copyright (C) 2001-2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "audiosync_impl.h"
+#include "midisyncgroup_impl.h"
+#include "audiotimer.h"
+#include "audiosubsys.h"
+#include "timestampmath.h"
+
+#undef AUDIO_DEBUG_DRIFT
+
+using namespace std;
+using namespace Arts;
+
+namespace Arts {
+ static list<AudioSync_impl *> audioSyncImplList;
+}
+
+void AudioSync_impl::AudioSyncEvent::execute()
+{
+ list<SynthModule>::iterator i;
+
+ for(i = startModules.begin(); i != startModules.end(); i++)
+ i->start();
+
+ for(i = stopModules.begin(); i != stopModules.end(); i++)
+ i->stop();
+}
+
+AudioSync_impl::AudioSync_impl()
+ : newEvent(new AudioSyncEvent), syncGroup(0)
+{
+ syncOffset = TimeStamp(0,0);
+
+ timer = AudioTimer::subscribe();
+ timer->addCallback(this);
+
+ audioSyncImplList.push_back(this);
+}
+
+AudioSync_impl::~AudioSync_impl()
+{
+ delete newEvent;
+
+ while(!events.empty())
+ {
+ delete events.front();
+ events.pop_front();
+ }
+
+ if(syncGroup)
+ {
+ syncGroup->audioSyncDied(this);
+ syncGroup = 0;
+ }
+ audioSyncImplList.remove(this);
+ timer->removeCallback(this);
+ timer->unsubscribe();
+}
+
+TimeStamp AudioSync_impl::time()
+{
+ if(syncGroup)
+ return syncGroup->time();
+ else
+ return audioTime();
+}
+
+TimeStamp AudioSync_impl::playTime()
+{
+ if(syncGroup)
+ return syncGroup->playTime();
+ else
+ return audioPlayTime();
+}
+
+TimeStamp AudioSync_impl::audioTime()
+{
+ return timer->time();
+}
+
+TimeStamp AudioSync_impl::audioPlayTime()
+{
+ double delay = AudioSubSystem::the()->outputDelay();
+
+ TimeStamp time = audioTime();
+ timeStampDec(time,timeStampFromDouble(delay));
+ return time;
+}
+
+TimeStamp AudioSync_impl::clientTime()
+{
+ TimeStamp time = audioTime();
+ timeStampDec(time, syncOffset);
+ return time;
+}
+
+void AudioSync_impl::queueStart(SynthModule synthModule)
+{
+ newEvent->startModules.push_back(synthModule);
+}
+
+void AudioSync_impl::queueStop(SynthModule synthModule)
+{
+ newEvent->stopModules.push_back(synthModule);
+}
+
+void AudioSync_impl::execute()
+{
+ newEvent->execute();
+ newEvent->startModules.clear();
+ newEvent->stopModules.clear();
+}
+
+void AudioSync_impl::executeAt(const TimeStamp& timeStamp)
+{
+ newEvent->time = timeStamp;
+ if(syncGroup)
+ timeStampInc(newEvent->time, syncOffset);
+
+ events.push_back(newEvent);
+
+ newEvent = new AudioSyncEvent;
+}
+
+void AudioSync_impl::updateTime()
+{
+ TimeStamp now = audioTime();
+ list<AudioSyncEvent*>::iterator i;
+
+ i = events.begin();
+ while(i != events.end())
+ {
+ AudioSyncEvent *event = *i;
+ TimeStamp& eventTime = event->time;
+
+ if( now.sec > eventTime.sec
+ || ((now.sec == eventTime.sec) && (now.usec > eventTime.usec)))
+ {
+ event->execute();
+ delete event;
+ i = events.erase(i);
+ }
+ else
+ {
+ i++;
+ }
+ }
+}
+
+void AudioSync_impl::setSyncGroup(MidiSyncGroup_impl *newSyncGroup)
+{
+ syncGroup = newSyncGroup;
+}
+
+void AudioSync_impl::synchronizeTo(const TimeStamp& time)
+{
+#ifdef AUDIO_DEBUG_DRIFT
+ TimeStamp drift = syncOffset; // debug drift
+#endif
+
+ syncOffset = audioPlayTime();
+ timeStampDec(syncOffset, time);
+
+#ifdef AUDIO_DEBUG_DRIFT
+ timeStampDec(drift, syncOffset); // debug drift
+ printf("SYNC DRIFT %30s %30s: %f\n",
+ "AudioSync", "AudioSync", timeStampToDouble(drift));
+#endif
+}
+
+AudioSync_impl *AudioSync_impl::find(AudioSync audioSync)
+{
+ list<AudioSync_impl *>::iterator i;
+
+ for(i = audioSyncImplList.begin(); i != audioSyncImplList.end(); i++)
+ {
+ if((*i)->_isEqual(audioSync._base()))
+ return (*i);
+ }
+ return 0;
+}
+
+namespace Arts { REGISTER_IMPLEMENTATION(AudioSync_impl); }
diff --git a/arts/midi/audiosync_impl.h b/arts/midi/audiosync_impl.h
new file mode 100644
index 00000000..835b8942
--- /dev/null
+++ b/arts/midi/audiosync_impl.h
@@ -0,0 +1,79 @@
+ /*
+
+ Copyright (C) 2001-2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef AUDIOSYNC_IMPL_H
+#define AUDIOSYNC_IMPL_H
+
+#include "artsmidi.h"
+#include "audiotimer.h"
+
+namespace Arts {
+
+class MidiSyncGroup_impl;
+class AudioSync_impl : virtual public AudioSync_skel,
+ virtual public AudioTimerCallback
+{
+ AudioTimer *timer;
+
+ struct AudioSyncEvent
+ {
+ TimeStamp time;
+ std::list<SynthModule> startModules;
+ std::list<SynthModule> stopModules;
+
+ void execute();
+ };
+ std::list<AudioSyncEvent *> events;
+ AudioSyncEvent *newEvent;
+ MidiSyncGroup_impl *syncGroup;
+ TimeStamp syncOffset;
+
+ TimeStamp audioTime();
+ TimeStamp audioPlayTime();
+
+public:
+ AudioSync_impl();
+ ~AudioSync_impl();
+
+ // public interface
+ TimeStamp time();
+ TimeStamp playTime();
+
+ void queueStart(SynthModule synthModule);
+ void queueStop(SynthModule synthModule);
+ void execute();
+ void executeAt(const TimeStamp& timeStamp);
+
+ // interface to AudioTimer
+ void updateTime();
+
+ // interface to MidiSyncGroup
+ static AudioSync_impl *find(AudioSync audioSync);
+
+ void synchronizeTo(const TimeStamp& time);
+ void setSyncGroup(MidiSyncGroup_impl *syncGroup);
+ TimeStamp clientTime();
+};
+
+}
+
+#endif /* AUDIOSYNC_IMPL_H */
diff --git a/arts/midi/audiotimer.cc b/arts/midi/audiotimer.cc
new file mode 100644
index 00000000..354f153b
--- /dev/null
+++ b/arts/midi/audiotimer.cc
@@ -0,0 +1,98 @@
+ /*
+
+ Copyright (C) 2001-2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmidi.h"
+#include "artsflow.h"
+#include "stdsynthmodule.h"
+#include "debug.h"
+#include "miditimercommon.h"
+#include "audiotimer.h"
+#include "flowsystem.h"
+
+using namespace std;
+using namespace Arts;
+
+static AudioTimer *AudioTimer_the = 0;
+
+AudioTimer::AudioTimer()
+{
+ AudioTimer_the = this;
+ samples = seconds = 0;
+}
+
+AudioTimer::~AudioTimer()
+{
+ AudioTimer_the = 0;
+}
+
+TimeStamp AudioTimer::time()
+{
+ return TimeStamp(seconds,
+ (long)((float)samples / samplingRateFloat * 1000000.0));
+}
+
+void AudioTimer::notify(const Notification &)
+{
+ list<AudioTimerCallback *>::iterator i;
+ for(i = callbacks.begin(); i != callbacks.end(); i++)
+ (*i)->updateTime();
+}
+
+void AudioTimer::calculateBlock(unsigned long s)
+{
+ samples += s;
+ while(samples > samplingRate)
+ {
+ samples -= samplingRate;
+ seconds++;
+ }
+ Notification n;
+ n.receiver = this;
+ n.ID = 0;
+ n.data = 0;
+ n.internal = 0;
+ NotificationManager::the()->send(n);
+}
+
+AudioTimer *AudioTimer::subscribe()
+{
+ if(!AudioTimer_the)
+ {
+ new AudioTimer();
+ AudioTimer_the->_node()->start();
+ }
+ else
+ {
+ AudioTimer_the->_copy();
+ }
+ return AudioTimer_the;
+}
+
+void AudioTimer::addCallback(AudioTimerCallback *callback)
+{
+ callbacks.push_back(callback);
+}
+
+void AudioTimer::removeCallback(AudioTimerCallback *callback)
+{
+ callbacks.remove(callback);
+}
diff --git a/arts/midi/audiotimer.h b/arts/midi/audiotimer.h
new file mode 100644
index 00000000..f0299cf3
--- /dev/null
+++ b/arts/midi/audiotimer.h
@@ -0,0 +1,73 @@
+ /*
+
+ Copyright (C) 2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+#ifndef AUDIOTIMER_H
+#define AUDIOTIMER_H
+
+#include "artsmidi.h"
+#include "artsflow.h"
+#include "stdsynthmodule.h"
+#include "miditimercommon.h"
+#include "flowsystem.h"
+
+namespace Arts {
+
+/**
+ * EXPERIMENTAL (this interface will probably not stay this way) way to handle
+ * audio based time stamps. Callback interface.
+ */
+class AudioTimerCallback {
+public:
+ virtual void updateTime() = 0;
+};
+
+/**
+ * EXPERIMENTAL (this interface will probably not stay this way) way to handle
+ * audio based time stamps. Timer class for audio timestamps.
+ */
+class AudioTimer : public SynthModule_skel, public StdSynthModule
+ /* we are a SynthModule to get the timing */
+{
+protected:
+ AudioTimer();
+ virtual ~AudioTimer();
+
+ std::list<AudioTimerCallback *> callbacks;
+
+ long samples; /* at most samplingRate, overflow goes in seconds */
+ long seconds;
+
+public:
+ // allocation: share one AudioTimer for everbody who needs one
+ static AudioTimer *subscribe();
+ void unsubscribe() { _release(); }
+
+ void notify(const Notification& n);
+ TimeStamp time();
+ void calculateBlock(unsigned long samples);
+
+ void addCallback(AudioTimerCallback *callback);
+ void removeCallback(AudioTimerCallback *callback);
+};
+
+}
+
+#endif
diff --git a/arts/midi/mcopclass/Makefile.am b/arts/midi/mcopclass/Makefile.am
new file mode 100644
index 00000000..5fbeafe8
--- /dev/null
+++ b/arts/midi/mcopclass/Makefile.am
@@ -0,0 +1,2 @@
+mcopclassdir = $(libdir)/mcop/Arts
+mcopclass_DATA = MidiManager.mcopclass
diff --git a/arts/midi/mcopclass/MidiManager.mcopclass b/arts/midi/mcopclass/MidiManager.mcopclass
new file mode 100644
index 00000000..a86af4a4
--- /dev/null
+++ b/arts/midi/mcopclass/MidiManager.mcopclass
@@ -0,0 +1,3 @@
+Interface=Arts::MidiManager,Arts::Object
+Language=C++
+Library=libartsmidi.la
diff --git a/arts/midi/midiclient_impl.cc b/arts/midi/midiclient_impl.cc
new file mode 100644
index 00000000..d57e7fd7
--- /dev/null
+++ b/arts/midi/midiclient_impl.cc
@@ -0,0 +1,274 @@
+ /*
+
+ Copyright (C) 2000-2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "midiclient_impl.h"
+#include "midimanager_impl.h"
+#include "midimanagerport_impl.h"
+#include "midisyncgroup_impl.h"
+#include "timestampmath.h"
+
+#undef DEBUG_SYNC_DRIFT
+
+using namespace Arts;
+using namespace std;
+
+MidiClient_impl::MidiClient_impl(const MidiClientInfo& info,
+ MidiManager_impl *manager) :_info(info), manager(manager), syncGroup(0)
+{
+}
+
+MidiClient_impl::~MidiClient_impl()
+{
+ while(!_info.connections.empty())
+ disconnect(manager->findClient(_info.connections[0]));
+
+ if(syncGroup)
+ {
+ syncGroup->clientDied(this);
+ syncGroup = 0;
+ }
+ manager->removeClient(this);
+}
+
+MidiClientInfo MidiClient_impl::info()
+{
+ return _info;
+}
+
+void MidiClient_impl::title(const string &newvalue)
+{
+ _info.title = newvalue;
+}
+
+string MidiClient_impl::title()
+{
+ return _info.title;
+}
+
+void MidiClient_impl::addInputPort(MidiPort port)
+{
+ assert(_info.direction == mcdRecord);
+
+ ports.push_back(port);
+
+ // FIXME: should we synchronize inputPorts at all
+ rebuildConnections();
+}
+
+MidiPort MidiClient_impl::addOutputPort()
+{
+ assert(_info.direction == mcdPlay);
+
+ MidiPort port = MidiPort::_from_base(new MidiManagerPort_impl(this));
+ ports.push_back(port);
+
+ rebuildConnections();
+ return port;
+}
+
+void MidiClient_impl::removePort(MidiPort port)
+{
+ list<MidiPort>::iterator i = ports.begin();
+ while(i != ports.end())
+ {
+ if (i->_isEqual(port))
+ i = ports.erase(i);
+ else
+ i++;
+ }
+
+ rebuildConnections();
+}
+
+void MidiClient_impl::rebuildConnections()
+{
+ _connections.clear();
+
+ vector<long>::iterator li;
+ for(li = _info.connections.begin(); li != _info.connections.end(); li++)
+ {
+ MidiClient_impl *other = manager->findClient(*li);
+ assert(other);
+
+ list<MidiPort>::iterator pi;
+ for(pi = other->ports.begin(); pi != other->ports.end(); pi++)
+ {
+ MidiClientConnection mcc;
+ mcc.offset = TimeStamp(0,0);
+ mcc.port = *pi;
+ _connections.push_back(mcc);
+ }
+ }
+ adjustSync();
+}
+
+list<MidiClientConnection> *MidiClient_impl::connections()
+{
+ return &_connections;
+}
+
+static void removeElement(vector<long>& vec, long el)
+{
+ vector<long> tmp;
+ vec.swap(tmp);
+ vector<long>::iterator i;
+ for(i = tmp.begin(); i != tmp.end(); i++)
+ if(*i != el) vec.push_back(*i);
+}
+
+void MidiClient_impl::connect(MidiClient_impl *dest)
+{
+ assert(_info.direction != dest->_info.direction);
+
+ disconnect(dest);
+
+ _info.connections.push_back(dest->ID());
+ dest->_info.connections.push_back(ID());
+
+ list<MidiPort>::iterator pi;
+
+ /* add the other clients ports to our connection list */
+ for(pi = dest->ports.begin(); pi != dest->ports.end(); pi++)
+ {
+ MidiClientConnection mcc;
+ mcc.offset = TimeStamp(0,0);
+ mcc.port = *pi;
+ _connections.push_back(mcc);
+ }
+
+ /* add our ports to the other clients connection list */
+ for(pi = ports.begin(); pi != ports.end(); pi++)
+ {
+ MidiClientConnection mcc;
+ mcc.offset = TimeStamp(0,0);
+ mcc.port = *pi;
+ dest->_connections.push_back(mcc);
+ }
+ adjustSync();
+}
+
+void MidiClient_impl::disconnect(MidiClient_impl *dest)
+{
+ assert(_info.direction != dest->_info.direction);
+
+ removeElement(_info.connections,dest->ID());
+ removeElement(dest->_info.connections,ID());
+
+ list<MidiPort>::iterator pi;
+
+ /* remove the other clients ports from our connection list */
+ for(pi = dest->ports.begin(); pi != dest->ports.end(); pi++)
+ {
+ list<MidiClientConnection>::iterator ci = _connections.begin();
+
+ while(ci != _connections.end())
+ {
+ if(ci->port._isEqual(*pi))
+ ci = _connections.erase(ci);
+ else
+ ci++;
+ }
+ }
+
+ /* remove our ports from the other clients connection list */
+ for(pi = ports.begin(); pi != ports.end(); pi++)
+ {
+ list<MidiClientConnection>::iterator ci = dest->_connections.begin();
+
+ while(ci != dest->_connections.end())
+ {
+ if(ci->port._isEqual(*pi))
+ ci = dest->_connections.erase(ci);
+ else
+ ci++;
+ }
+ }
+ adjustSync();
+}
+
+void MidiClient_impl::synchronizeTo(const TimeStamp& time)
+{
+ list<MidiClientConnection>::iterator i;
+
+ for(i = _connections.begin(); i != _connections.end(); i++)
+ {
+ MidiClientConnection& mcc = *i;
+
+#ifdef DEBUG_SYNC_DRIFT
+ TimeStamp drift = mcc.offset; // debug drift
+#endif
+
+ mcc.offset = mcc.port.playTime();
+ timeStampDec(mcc.offset, time);
+
+#ifdef DEBUG_SYNC_DRIFT
+ timeStampDec(drift,mcc.offset); // debug drift
+ printf("SYNC DRIFT %30s %30s: %f\n",
+ mcc.port._interfaceName().c_str(), _info.title.c_str(),
+ timeStampToDouble(drift));
+#endif
+ }
+}
+
+void MidiClient_impl::setSyncGroup(MidiSyncGroup_impl *newSyncGroup)
+{
+ syncGroup = newSyncGroup;
+}
+
+void MidiClient_impl::adjustSync()
+{
+ if(syncGroup)
+ syncGroup->clientChanged(this);
+ else
+ synchronizeTo(systemMidiTimer.time());
+}
+
+TimeStamp MidiClient_impl::time()
+{
+ if(syncGroup)
+ return syncGroup->time();
+ else
+ return clientTime();
+}
+
+TimeStamp MidiClient_impl::playTime()
+{
+ if(syncGroup)
+ return syncGroup->playTime();
+ else
+ return systemMidiTimer.time();
+}
+
+TimeStamp MidiClient_impl::clientTime()
+{
+ TimeStamp result = playTime();
+
+ list<MidiClientConnection>::iterator i;
+ for(i = _connections.begin(); i != _connections.end(); i++)
+ {
+ TimeStamp time = i->port.time();
+ timeStampDec(time, i->offset);
+ result = timeStampMax(result, time);
+ }
+
+ return result;
+}
diff --git a/arts/midi/midiclient_impl.h b/arts/midi/midiclient_impl.h
new file mode 100644
index 00000000..b1aeb204
--- /dev/null
+++ b/arts/midi/midiclient_impl.h
@@ -0,0 +1,80 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_MIDICLIENT_IMPL_H
+#define ARTS_MIDICLIENT_IMPL_H
+
+#include "artsmidi.h"
+#include "midimanager_impl.h"
+#include "midimanagerport_impl.h"
+
+namespace Arts {
+
+struct MidiClientConnection
+{
+ TimeStamp offset;
+ MidiPort port;
+};
+
+class MidiManager_impl;
+class MidiSyncGroup_impl;
+class MidiClient_impl : virtual public MidiClient_skel
+{
+protected:
+ SystemMidiTimer systemMidiTimer;
+ MidiClientInfo _info;
+ MidiManager_impl *manager;
+ MidiSyncGroup_impl *syncGroup;
+ std::list<MidiPort> ports;
+ std::list<MidiClientConnection> _connections;
+
+public:
+ MidiClient_impl(const MidiClientInfo& info, MidiManager_impl *manager);
+ ~MidiClient_impl();
+
+ // MCOP interface
+ MidiClientInfo info();
+ void title(const std::string &newvalue);
+ std::string title();
+ void addInputPort(MidiPort port);
+ MidiPort addOutputPort();
+ void removePort(MidiPort port);
+
+ // interface to MidiManager/Port
+ inline long ID() { return _info.ID; }
+ std::list<MidiClientConnection> *connections();
+ void connect(MidiClient_impl *dest);
+ void disconnect(MidiClient_impl *dest);
+ void rebuildConnections();
+ void adjustSync();
+
+ TimeStamp time();
+ TimeStamp playTime();
+
+ // interface to MidiSyncGroup
+ void synchronizeTo(const TimeStamp& time);
+ void setSyncGroup(MidiSyncGroup_impl *syncGroup);
+ TimeStamp clientTime();
+};
+
+}
+#endif /* ARTS_MIDICLIENT_IMPL_H */
diff --git a/arts/midi/midimanager_impl.cc b/arts/midi/midimanager_impl.cc
new file mode 100644
index 00000000..1ba07962
--- /dev/null
+++ b/arts/midi/midimanager_impl.cc
@@ -0,0 +1,156 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "midimanager_impl.h"
+#include "midiclient_impl.h"
+#include "midisyncgroup_impl.h"
+#include "debug.h"
+
+using namespace Arts;
+using namespace std;
+
+static int cleanReference(const string& reference)
+{
+ Object test = Reference("global:"+reference);
+ if(test.isNull())
+ {
+ Dispatcher::the()->globalComm().erase(reference);
+ return 1;
+ }
+ else
+ return 0;
+}
+
+MidiManager_impl::MidiManager_impl() : nextID(1)
+{
+ cleanReference("Arts_MidiManager");
+ if(!ObjectManager::the()->addGlobalReference(Object::_from_base(_copy()),
+ "Arts_MidiManager"))
+ {
+ arts_warning("can't register Arts::MidiManager");
+ }
+ else
+ {
+ arts_debug("Arts::MidiManager registered successfully.");
+ }
+ Dispatcher::the()->ioManager()->addTimer(1000, this);
+}
+
+MidiManager_impl::~MidiManager_impl()
+{
+ Dispatcher::the()->ioManager()->removeTimer(this);
+}
+
+vector<MidiClientInfo> *MidiManager_impl::clients()
+{
+ if(!alsaMidiGateway.isNull())
+ {
+ if(!alsaMidiGateway.rescan())
+ alsaMidiGateway = AlsaMidiGateway::null();
+ }
+
+ vector<MidiClientInfo> *result = new vector<MidiClientInfo>;
+
+ list<MidiClient_impl *>::iterator i;
+ for(i = _clients.begin(); i != _clients.end(); i++)
+ result->push_back((*i)->info());
+
+ return result;
+}
+
+MidiClient MidiManager_impl::addClient(MidiClientDirection direction,
+ MidiClientType type, const string& title, const string& autoRestoreID)
+{
+ MidiClientInfo info;
+
+ info.ID = nextID++;
+ info.direction = direction;
+ info.type = type;
+ info.title = title;
+ info.autoRestoreID = autoRestoreID;
+
+ MidiClient_impl *impl = new MidiClient_impl(info, this);
+ _clients.push_back(impl);
+ return MidiClient::_from_base(impl);
+}
+
+void MidiManager_impl::removeClient(MidiClient_impl *client)
+{
+ _clients.remove(client);
+}
+
+MidiClient_impl *MidiManager_impl::findClient(long clientID)
+{
+ list<MidiClient_impl *>::iterator i;
+
+ for(i = _clients.begin(); i != _clients.end(); i++)
+ {
+ if((*i)->ID() == clientID)
+ return (*i);
+ }
+ return 0;
+}
+
+void MidiManager_impl::connect(long clientID, long destinationID)
+{
+ MidiClient_impl *src = findClient(clientID);
+ MidiClient_impl *dest = findClient(destinationID);
+
+ arts_return_if_fail(src);
+ arts_return_if_fail(dest);
+ src->connect(dest);
+}
+
+void MidiManager_impl::disconnect(long clientID, long destinationID)
+{
+ MidiClient_impl *src = findClient(clientID);
+ MidiClient_impl *dest = findClient(destinationID);
+
+ arts_return_if_fail(src);
+ arts_return_if_fail(dest);
+ src->disconnect(dest);
+}
+
+MidiSyncGroup MidiManager_impl::addSyncGroup()
+{
+ MidiSyncGroup_impl *impl = new MidiSyncGroup_impl(this);
+ syncGroups.push_back(impl);
+ return MidiSyncGroup::_from_base(impl);
+}
+
+void MidiManager_impl::removeSyncGroup(MidiSyncGroup_impl *group)
+{
+ syncGroups.remove(group);
+}
+
+void MidiManager_impl::notifyTime()
+{
+ list<MidiClient_impl *>::iterator i;
+ for(i = _clients.begin(); i != _clients.end(); i++)
+ (*i)->adjustSync();
+
+ list<MidiSyncGroup_impl *>::iterator gi;
+ for(gi = syncGroups.begin(); gi != syncGroups.end(); gi++)
+ (*gi)->adjustSync();
+}
+
+namespace Arts { REGISTER_IMPLEMENTATION(MidiManager_impl); }
diff --git a/arts/midi/midimanager_impl.h b/arts/midi/midimanager_impl.h
new file mode 100644
index 00000000..0c9ec5df
--- /dev/null
+++ b/arts/midi/midimanager_impl.h
@@ -0,0 +1,67 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_MIDIMANAGER_IMPL_H
+#define ARTS_MIDIMANAGER_IMPL_H
+
+#include "artsmidi.h"
+
+namespace Arts {
+
+class MidiClient_impl;
+class MidiSyncGroup_impl;
+class MidiManager_impl : virtual public MidiManager_skel,
+ virtual public TimeNotify
+{
+protected:
+ std::list<MidiClient_impl *> _clients;
+ std::list<MidiSyncGroup_impl *> syncGroups;
+
+ long nextID;
+ AlsaMidiGateway alsaMidiGateway;
+
+ void notifyTime();
+
+public:
+ MidiManager_impl();
+ ~MidiManager_impl();
+
+ // public interface
+ std::vector<MidiClientInfo> *clients();
+
+ MidiClient addClient(MidiClientDirection direction, MidiClientType type,
+ const std::string& title, const std::string& autoRestoreID);
+
+ void connect(long clientID, long destinationID);
+ void disconnect(long clientID, long destinationID);
+ MidiSyncGroup addSyncGroup();
+
+ // interface to MidiClient_impl
+ void removeClient(MidiClient_impl *client);
+ MidiClient_impl *findClient(long clientID);
+
+ // interface to MidiSyncGroup_impl
+ void removeSyncGroup(MidiSyncGroup_impl *group);
+};
+
+}
+#endif /* ARTS_MIDIMANAGER_IMPL_H */
diff --git a/arts/midi/midimanagerport_impl.cc b/arts/midi/midimanagerport_impl.cc
new file mode 100644
index 00000000..a6973a51
--- /dev/null
+++ b/arts/midi/midimanagerport_impl.cc
@@ -0,0 +1,71 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "midimanagerport_impl.h"
+#include "midimanager_impl.h"
+#include "midiclient_impl.h"
+#include "timestampmath.h"
+
+#include <stdio.h>
+
+using namespace Arts;
+using namespace std;
+
+MidiManagerPort_impl::MidiManagerPort_impl(MidiClient_impl *client)
+ : client(client)
+{
+}
+
+TimeStamp MidiManagerPort_impl::time()
+{
+ return client->time();
+}
+
+TimeStamp MidiManagerPort_impl::playTime()
+{
+ return client->playTime();
+}
+
+
+void MidiManagerPort_impl::processCommand(const MidiCommand& command)
+{
+ list<MidiClientConnection> *connections = client->connections();
+ list<MidiClientConnection>::iterator i;
+
+ for(i = connections->begin(); i != connections->end(); i++)
+ i->port.processCommand(command);
+}
+
+void MidiManagerPort_impl::processEvent(const MidiEvent& event)
+{
+ list<MidiClientConnection> *connections = client->connections();
+ list<MidiClientConnection>::iterator i;
+
+ for(i = connections->begin(); i != connections->end(); i++)
+ {
+ /* relocate the event to the synchronized time */
+ TimeStamp time = event.time;
+ timeStampInc(time, i->offset);
+
+ i->port.processEvent(MidiEvent(time, event.command));
+ }
+}
diff --git a/arts/midi/midimanagerport_impl.h b/arts/midi/midimanagerport_impl.h
new file mode 100644
index 00000000..56998546
--- /dev/null
+++ b/arts/midi/midimanagerport_impl.h
@@ -0,0 +1,46 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef MIDIMANAGERPORT_IMPL_H
+#define MIDIMANAGERPORT_IMPL_H
+
+#include "artsmidi.h"
+
+namespace Arts {
+
+class MidiClient_impl;
+
+class MidiManagerPort_impl : public MidiPort_skel
+{
+ MidiClient_impl *client;
+ SystemMidiTimer timer;
+public:
+ MidiManagerPort_impl(MidiClient_impl *client);
+ TimeStamp time();
+ TimeStamp playTime();
+ void processCommand(const MidiCommand& command);
+ void processEvent(const MidiEvent& event);
+};
+
+}
+
+#endif /* MIDIMANAGERPORT_IMPL_H */
diff --git a/arts/midi/midimsg.c b/arts/midi/midimsg.c
new file mode 100644
index 00000000..7032fba8
--- /dev/null
+++ b/arts/midi/midimsg.c
@@ -0,0 +1,177 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "midimsg.h"
+
+#define STATUS_MASK 0x80
+#define MESSAGE_TYPE_MASK 0xf0
+#define CHANNEL_MASK 0x0f
+
+Byte midiReadByte(int fd)
+{
+ Byte current_byte;
+ /*
+ ** for now ignore all realtime-messages (0xF8 .. 0xFF), which may get
+ ** embedded *inside* every other message
+ */
+ do {
+ /*
+ ** read() is wrapped in a while() in order to handle interruption
+ ** by a signal. In the normal case, read() is only called once.
+ */
+ while (read(/* fd = */ fd, /* buf = */ (void *)(&current_byte),
+ /* count = */ 1) < 1) {}
+
+ } while(current_byte >= 0xf8);
+
+ return current_byte;
+}
+
+int midimsgGetMessageType(Byte *message)
+{
+ return (message[0] & MESSAGE_TYPE_MASK);
+}
+
+void midimsgSetMessageType(Byte *message_inout, int message_type)
+{
+ message_inout[0] = (message_inout[0] & ~MESSAGE_TYPE_MASK) | message_type;
+}
+
+int midimsgGetChannel(Byte *message)
+{
+ return (message[0] & CHANNEL_MASK);
+}
+
+void midimsgSetChannel(Byte *message_inout, int channel)
+{
+ message_inout[0] = (message_inout[0] & ~CHANNEL_MASK) | channel;
+}
+
+int midimsgGetPitch(Byte *message)
+{
+ return (message[1]);
+}
+
+void midimsgSetPitch(Byte *message_inout, int pitch)
+{
+ message_inout[1] = pitch;
+}
+
+int midimsgGetVelocity(Byte *message)
+{
+ return (message[2]);
+}
+
+void midimsgSetVelocity(Byte *message_inout, int velocity)
+{
+ message_inout[2] = velocity;
+}
+
+int midimsgGetParameterNumber(Byte *message)
+{
+ return (message[1]);
+}
+
+void midimsgSetParameterNumber(Byte *message_inout, int number)
+{
+ message_inout[1] = number;
+}
+
+int midimsgGetParameterValue(Byte *message)
+{
+ return (message[2]);
+}
+
+void midimsgSetParameterValue(Byte *message_inout, int value)
+{
+ message_inout[2] = value;
+}
+
+int midimsgGetPitchWheelValue(Byte *message)
+{
+ return (((int)(message[2]) << 7) + message[1]);
+}
+
+void midimsgRead(int fd, Byte *message_return)
+{
+ Byte current_byte;
+ static Byte last_status_byte;
+
+ current_byte = midiReadByte(fd);
+
+ if ((current_byte & STATUS_MASK) == 0)
+ {
+ /*
+ ** must be a running status, unless we're picking up mid-message
+ ** (which would be an unhandled error)
+ */
+
+ message_return[0] = last_status_byte;
+ }
+ else
+ {
+ message_return[0] = current_byte;
+ last_status_byte = current_byte;
+
+ current_byte = midiReadByte(fd);
+ }
+
+ switch (midimsgGetMessageType(/* message = */ message_return))
+ {
+ case MIDIMSG_NOTE_ON:
+ case MIDIMSG_NOTE_OFF:
+ case MIDIMSG_KEY_PRESSURE:
+ case MIDIMSG_PARAMETER:
+ case MIDIMSG_PITCH_WHEEL:
+
+ message_return[1] = current_byte;
+
+ current_byte = midiReadByte(fd);
+ message_return[2] = current_byte;
+
+ if ((midimsgGetMessageType(/* message = */ message_return) ==
+ MIDIMSG_NOTE_ON) && (midimsgGetVelocity(/* message = */
+ message_return) == 0))
+ {
+ /* note-on with velocity of zero is equivalent to note-off */
+
+ midimsgSetMessageType(/* message_inout = */ message_return,
+ /* message_type = */ MIDIMSG_NOTE_OFF);
+ }
+
+ return;
+
+ case MIDIMSG_PROGRAM:
+ case MIDIMSG_CHANNEL_PRESSURE:
+
+ message_return[1] = current_byte;
+
+ return;
+ }
+}
+
+void midimsgWrite(int fd, Byte *message)
+{
+ switch (midimsgGetMessageType(/* message = */ message))
+ {
+ case MIDIMSG_NOTE_ON:
+ case MIDIMSG_NOTE_OFF:
+ case MIDIMSG_KEY_PRESSURE:
+ case MIDIMSG_PARAMETER:
+ case MIDIMSG_PITCH_WHEEL:
+ write(fd, message, 3);
+ break;
+ case MIDIMSG_PROGRAM:
+ case MIDIMSG_CHANNEL_PRESSURE:
+ write(fd, message, 2);
+ break;
+ }
+}
+
diff --git a/arts/midi/midimsg.h b/arts/midi/midimsg.h
new file mode 100644
index 00000000..3bba90d7
--- /dev/null
+++ b/arts/midi/midimsg.h
@@ -0,0 +1,44 @@
+
+#ifndef MIDIMSG_H
+#define MIDIMSG_H
+
+typedef unsigned char Byte;
+
+#define MIDIMSG_NOTE_OFF 0x80
+#define MIDIMSG_NOTE_ON 0x90
+#define MIDIMSG_KEY_PRESSURE 0xa0
+#define MIDIMSG_PARAMETER 0xb0
+#define MIDIMSG_PROGRAM 0xc0
+#define MIDIMSG_CHANNEL_PRESSURE 0xd0
+#define MIDIMSG_PITCH_WHEEL 0xe0
+
+#ifndef True
+#define True 1
+#define False 0
+#endif
+
+int midimsgGetMessageType(Byte *message);
+void midimsgSetMessageType(Byte *message_inout, int message_type);
+
+int midimsgGetChannel(Byte *message);
+void midimsgSetChannel(Byte *message_inout, int channel);
+
+int midimsgGetPitch(Byte *message);
+void midimsgSetPitch(Byte *message_inout, int pitch);
+
+int midimsgGetVelocity(Byte *message);
+void midimsgSetVelocity(Byte *message_inout, int velocity);
+
+int midimsgGetParameterNumber(Byte *message);
+void midimsgSetParameterNumber(Byte *message_inout, int number);
+
+int midimsgGetParameterValue(Byte *message);
+void midimsgSetParameterValue(Byte *message_inout, int value);
+
+int midimsgGetPitchWheelValue(Byte *message);
+
+void midimsgRead(int fd, Byte *message_return);
+void midimsgWrite(int fd, Byte *message_return);
+
+#endif
+
diff --git a/arts/midi/midisend.cc b/arts/midi/midisend.cc
new file mode 100644
index 00000000..2c1878ef
--- /dev/null
+++ b/arts/midi/midisend.cc
@@ -0,0 +1,375 @@
+/*
+
+Copyright (C) 1999 Emmeran Seehuber
+ the_emmy@gmx.de
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+/*
+ Changes:
+ 16.09.1999 Emmeran "Emmy" Seehuber <the_emmy@gmx.de>
+ - Implementeted mapping of channels and pitches.
+ - Reworked option parsing, now using getopt().
+ Note: The parameters of the programms have changed !
+*/
+
+/*
+** This program was in original by David G. Slomin.
+** It was released to the Public Domain on 1/25/99.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "midisend.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+using namespace std;
+
+int input_fd = -1, test = 0, verbose = 0;
+char cFileName[1025];
+int optch;
+CMidiMap Map;
+
+void usage(char *prog)
+{
+ fprintf(stderr,"\n");
+ fprintf(stderr,"Usage: %s [ -f <mididevice> ] [ -m <mapfile> ] [ -v ] [ -t <loop> ]\n",prog);
+ fprintf(stderr," -f the mididevice to read the input from.\n");
+ fprintf(stderr," Default is /dev/midi. If you specify a dash, it is stdin\n");
+ fprintf(stderr," -m the mapfile to load.\n");
+ fprintf(stderr," -v verbose output.\n");
+ fprintf(stderr," -t test mode. Generates a testoutput on the midibus\n");
+ fprintf(stderr," -l long test mode. Generates a testoutput on the midibus\n");
+ exit(1);
+}
+
+void parseArgs(int argc, char** argv)
+{
+ // Setup default
+ strcpy(cFileName,"/dev/midi");
+
+ while((optch = getopt(argc,argv,"m:f:vtl")) > 0)
+ {
+ switch(optch)
+ {
+ case 'm': if( !Map.readMap(optarg) )
+ fprintf(stderr,"%s: can't read file %s!\n",argv[0],optarg);
+ break;
+ case 't': test = 1;
+ break;
+ case 'l': test = 2;
+ break;
+#ifdef VERSION
+ case 'v': verbose = 1; printf("MidiSend %s\n", VERSION );
+ break;
+#endif
+ case 'f': strncpy(cFileName,optarg,1024);
+ break;
+ default: usage(argv[0]);
+ break;
+ }
+ }
+}
+
+
+#ifdef COMMON_BINARY
+int midisend_main(int argc, char *argv[])
+#else
+int main(int argc, char *argv[])
+#endif
+{
+ Arts::Dispatcher dispatcher;
+ Arts::MidiManager manager = Arts::Reference("global:Arts_MidiManager");
+
+ if (manager.isNull())
+ {
+ fprintf(stderr, "%s trouble: No midimanager object found; please start "
+ "artsd.\n",argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ ** MIDI input initialization.
+ */
+
+ parseArgs(argc,argv);
+
+ string title = string("midisend (") + cFileName +")";
+ Arts::MidiClient client
+ = manager.addClient(Arts::mcdPlay,Arts::mctApplication,title,"midisend");
+ Arts::MidiPort port = client.addOutputPort();
+
+ if(test)
+ {
+ if( verbose )
+ printf("performing test ...\n");
+ unsigned long i,max=5000;
+ if(test==2) max = 20000;
+ for(i=0;i<max;i++)
+ {
+ port.processCommand(
+ Arts::MidiCommand(Arts::mcsNoteOn, 60+(i%12), 100));
+ port.processCommand(
+ Arts::MidiCommand(Arts::mcsNoteOff,60+(i%12), 0));
+ }
+ exit(0);
+ }
+
+ if( verbose )
+ printf("trying to open %s ...", cFileName );
+
+ input_fd = open(cFileName,O_RDONLY);
+
+ if(input_fd == -1)
+ {
+ fprintf(stderr,"\n%s trouble: can't open input device!\n",argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ else if( verbose )
+ printf(" ok!\n");
+
+
+ /*
+ ** Main loop.
+ */
+
+ if(verbose)
+ printf("beginning loop ...\n");
+
+ unsigned char msg[3];
+
+ while (1)
+ {
+ midimsgRead(input_fd, msg);
+ switch (midimsgGetMessageType(msg))
+ {
+ case MIDIMSG_NOTE_OFF:
+ Map.mapMsg(msg);
+ port.processCommand(
+ Arts::MidiCommand(Arts::mcsNoteOff|midimsgGetChannel(msg),
+ midimsgGetPitch(msg), midimsgGetVelocity(msg)));
+ if( verbose )
+ printf("NoteOff: Channel %d, Pitch %3d\n", midimsgGetChannel(msg),midimsgGetPitch(msg));
+ break;
+ case MIDIMSG_NOTE_ON:
+ Map.mapMsg(msg);
+ port.processCommand(
+ Arts::MidiCommand(Arts::mcsNoteOn|midimsgGetChannel(msg),
+ midimsgGetPitch(msg), midimsgGetVelocity(msg)));
+ if( verbose )
+ printf("NoteOn : Channel %d, Pitch %3d, Velocity %2d\n", midimsgGetChannel(msg),
+ midimsgGetPitch(msg),midimsgGetVelocity(msg));
+ break;
+ case MIDIMSG_PITCH_WHEEL:
+ Map.mapMsg(msg);
+ port.processCommand(
+ Arts::MidiCommand(Arts::mcsPitchWheel|midimsgGetChannel(msg),
+ midimsgGetPitch(msg), midimsgGetVelocity(msg)));
+ if( verbose )
+ printf("PitchWheel : Channel %d, LSB %3d MSB %3d\n", midimsgGetChannel(msg),
+ midimsgGetPitch(msg),midimsgGetVelocity(msg));
+ break;
+ }
+ }
+}
+
+//--------------------------------------------
+// The mapping stuff
+//--------------------------------------------
+
+bool CMidiMap::readMap(const char* pszFileName)
+{
+ if( verbose )
+ printf("reading mapfile %s ...\n", pszFileName);
+
+ FILE *file = fopen(pszFileName,"r");
+ if( !file )
+ return false;
+
+ char cBuffer[1024+1];
+ char* pszLine;
+ int nLine = 0;
+ while( (pszLine = fgets(cBuffer,1024,file)) ) {
+ nLine++;
+ parseLine(pszLine,pszFileName,nLine);
+ }
+ fclose(file);
+
+ return true;
+}
+
+bool CMidiMap::getNextWord(char*& pszLine, char*& pszWord)
+{
+ // First skip all leading blanks, etc.
+ bool bCont = true;
+ while(bCont) {
+ char cHelp = *pszLine;
+ switch( cHelp )
+ {
+ case 0: return false; // Out of line
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t': pszLine++; break; // Goto next char
+ default: bCont = false; break; // NonSpace character -> Word begins here.
+ }
+ }
+
+ // The word starts here
+ pszWord = pszLine;
+
+ // And now, goto the end of the word.
+ bCont = true;
+ while(bCont) {
+ char cHelp = *pszLine;
+ switch( cHelp )
+ {
+ case 0:
+ case ',':
+ case ';':
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t': *pszLine++ = 0; bCont = false; break; // Goto next char
+ default: pszLine++; break;
+ }
+ }
+
+ return true;
+}
+
+void CMidiMap::parseLine(char* pszLine, const char* pszConfigFile, int nConfigLine )
+{
+ char* pszWord = 0;
+ bool bOk = true;
+
+ // Get first word of the line
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ if( !bOk )
+ return;
+
+ // Skip comments
+ if( *pszWord == '#' )
+ return;
+
+ if( strcmp(pszWord,"PRC") == 0 ) {
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nOrigChannel = atol(pszWord);
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nStart = atol(pszWord);
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nEnd = atol(pszWord);
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nChannel = atol(pszWord);
+ if( bOk ) {
+ channelMaps[nOrigChannel].nChannel = nOrigChannel;
+ for( int i = nStart; i <= nEnd; i++ ) {
+ channelMaps[nOrigChannel].channelRemaps[i].nPitch = i;
+ channelMaps[nOrigChannel].channelRemaps[i].nChannel = nChannel;
+ }
+ }
+ else {
+ printf("midisend: (PRC) missing parameters at %s:%d\n", pszConfigFile,nConfigLine);
+ }
+ return;
+ }
+
+ if( strcmp(pszWord,"PRD") == 0 ) {
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nOrigChannel = atol(pszWord);
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nStart = atol(pszWord);
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nEnd = atol(pszWord);
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nPitchDiff = atol(pszWord);
+ if( bOk ) {
+ channelMaps[nOrigChannel].nChannel = nOrigChannel;
+ for( int i = nStart; i <= nEnd; i++ ) {
+ channelMaps[nOrigChannel].pitchRemaps[i].nPitch = i;
+ channelMaps[nOrigChannel].pitchRemaps[i].nToPitch = i + nPitchDiff;
+ }
+ }
+ else {
+ printf("midisend: (PRD) missing parameters at %s:%d\n", pszConfigFile,nConfigLine);
+ }
+ return;
+ }
+
+ if( strcmp(pszWord,"PTC") == 0 ) {
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nOrigChannel = atol(pszWord);
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nPitch = atol(pszWord);
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nToChannel = atol(pszWord);
+ if( bOk ) {
+ channelMaps[nOrigChannel].nChannel = nOrigChannel;
+ channelMaps[nOrigChannel].channelRemaps[nPitch].nPitch = nPitch;
+ channelMaps[nOrigChannel].channelRemaps[nPitch].nChannel = nToChannel;
+ }
+ else {
+ printf("midisend: (PTC) missing parameters at %s:%d\n", pszConfigFile,nConfigLine);
+ }
+ return;
+ }
+
+ if( strcmp(pszWord,"PTP") == 0 ) {
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nOrigChannel = atol(pszWord);
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nPitch = atol(pszWord);
+ bOk = bOk && getNextWord(pszLine,pszWord);
+ int nToPitch = atol(pszWord);
+ if( bOk ) {
+ channelMaps[nOrigChannel].nChannel = nOrigChannel;
+ channelMaps[nOrigChannel].pitchRemaps[nPitch].nPitch = nPitch;
+ channelMaps[nOrigChannel].pitchRemaps[nPitch].nToPitch = nToPitch;
+ }
+ else {
+ printf("midisend: (PTP) missing parameters at %s:%d\n", pszConfigFile,nConfigLine);
+ }
+ return;
+ }
+
+ printf("midisend: Unknown command at %s:%d\n", pszConfigFile,nConfigLine);
+}
+
+void CMidiMap::mapMsg(Byte* msg)
+{
+ // Get out the data for mapping
+ int nChannel = midimsgGetChannel(msg);
+ int nPitch = midimsgGetPitch(msg);
+
+ // Is there something to map for this channel ?
+ if( channelMaps.find(nChannel) != channelMaps.end() ) {
+ // => Yes, than do it.
+
+ // C/P => C
+ if( channelMaps[nChannel].channelRemaps.find(nPitch) != channelMaps[nChannel].channelRemaps.end() )
+ midimsgSetChannel(msg,channelMaps[nChannel].channelRemaps[nPitch].nChannel);
+
+ // C/P => P
+ if( channelMaps[nChannel].pitchRemaps.find(nPitch) != channelMaps[nChannel].pitchRemaps.end() ) {
+ midimsgSetPitch(msg,channelMaps[nChannel].pitchRemaps[nPitch].nToPitch);
+ }
+ }
+}
diff --git a/arts/midi/midisend.h b/arts/midi/midisend.h
new file mode 100644
index 00000000..817ebfa2
--- /dev/null
+++ b/arts/midi/midisend.h
@@ -0,0 +1,106 @@
+/*
+
+Copyright (C) 1999 Emmeran Seehuber
+ the_emmy@gmx.de
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+#ifndef MIDISEND_H
+#define MIDISEND_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "artsmidi.h"
+#include <vector>
+#include <iostream>
+#include <string>
+
+extern "C"
+{
+#include "midimsg.h"
+}
+
+/*
+ This class does the mapping of the
+ channels and pitches
+*/
+class CMidiMap {
+public:
+ /*
+ Reads in the mapfile pszFileName.
+ Returns TRUE, if successful.
+ */
+ bool readMap(const char* pszFileName);
+ /*
+ Maps the given message according to
+ the actual read configuration.
+ */
+ void mapMsg(Byte* msg);
+
+private:
+ /*
+ Parses a configuration line.
+ */
+ void parseLine(char* pszLine, const char* pszConfigFile, int nConfigLine );
+
+ /*
+ Gets the next word out of the line. throws
+ the exception CEOutOfLine, if there is no
+ more word in the line.
+
+ A word consists of all but \0, spacecharacter, ';' and
+ ','
+
+ pszLine is the pointer to the start of the
+ line. This function modifies the pointer.
+ pszWord is the pointer to the found word.
+ */
+ bool getNextWord(char*& pszLine, char*& pszWord);
+private:
+
+ /*
+ For each channel one instance of this structure
+ exists in the channelMaps. It holds the mapping information
+ for nChannel.
+ */
+ struct ChannelMaps {
+ int nChannel;
+
+ struct ChannelRemap {
+ int nPitch;
+ int nChannel;
+ };
+
+ struct PitchRemap {
+ int nPitch;
+ int nToPitch;
+ };
+
+ typedef std::map<int,ChannelRemap> ChannelRemapMap;
+ ChannelRemapMap channelRemaps;
+ typedef std::map<int,PitchRemap> PitchRemapMap;
+ PitchRemapMap pitchRemaps;
+ };
+ typedef std::map<int,ChannelMaps> ChannelMapsMap;
+ ChannelMapsMap channelMaps;
+};
+
+
+#endif
diff --git a/arts/midi/midisyncgroup_impl.cc b/arts/midi/midisyncgroup_impl.cc
new file mode 100644
index 00000000..57ebca2c
--- /dev/null
+++ b/arts/midi/midisyncgroup_impl.cc
@@ -0,0 +1,125 @@
+ /*
+
+ Copyright (C) 2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "midisyncgroup_impl.h"
+#include "audiosync_impl.h"
+#include "midiclient_impl.h"
+#include "midimanager_impl.h"
+#include "timestampmath.h"
+
+using namespace Arts;
+using namespace std;
+
+MidiSyncGroup_impl::MidiSyncGroup_impl(MidiManager_impl *manager)
+ : manager(manager)
+{
+}
+
+MidiSyncGroup_impl::~MidiSyncGroup_impl()
+{
+ /* tell clients we're dead */
+ list<MidiClient_impl *>::iterator i;
+ for(i = clients.begin(); i != clients.end(); i++)
+ (*i)->setSyncGroup(0);
+
+ list<AudioSync_impl *>::iterator ai;
+ for(ai = audioSyncs.begin(); ai != audioSyncs.end(); ai++)
+ (*ai)->setSyncGroup(0);
+
+ manager->removeSyncGroup(this);
+}
+
+void MidiSyncGroup_impl::addClient(MidiClient client)
+{
+ /* add client to list */
+ MidiClient_impl *impl = manager->findClient(client.info().ID);
+ impl->setSyncGroup(this);
+ clients.push_back(impl);
+
+ impl->synchronizeTo(masterTimer.time());
+}
+
+void MidiSyncGroup_impl::removeClient(MidiClient client)
+{
+ /* remove client from the list */
+ MidiClient_impl *impl = manager->findClient(client.info().ID);
+ impl->setSyncGroup(0);
+ clients.remove(impl);
+}
+
+void MidiSyncGroup_impl::addAudioSync(AudioSync audioSync)
+{
+ AudioSync_impl *impl = AudioSync_impl::find(audioSync);
+ impl->setSyncGroup(this);
+ audioSyncs.push_back(impl);
+
+ impl->synchronizeTo(masterTimer.time());
+}
+
+void MidiSyncGroup_impl::removeAudioSync(AudioSync audioSync)
+{
+ AudioSync_impl *impl = AudioSync_impl::find(audioSync);
+ impl->setSyncGroup(0);
+ audioSyncs.remove(impl);
+}
+
+void MidiSyncGroup_impl::clientChanged(MidiClient_impl *client)
+{
+ client->synchronizeTo(masterTimer.time());
+}
+
+void MidiSyncGroup_impl::clientDied(MidiClient_impl *client)
+{
+ clients.remove(client);
+}
+
+void MidiSyncGroup_impl::audioSyncDied(AudioSync_impl *audioSync)
+{
+ audioSyncs.remove(audioSync);
+}
+
+TimeStamp MidiSyncGroup_impl::time()
+{
+ TimeStamp result = masterTimer.time();
+
+ list<MidiClient_impl *>::iterator i;
+ for(i = clients.begin(); i != clients.end(); i++)
+ result = timeStampMax(result, (*i)->clientTime());
+
+ list<AudioSync_impl *>::iterator ai;
+ for(ai = audioSyncs.begin(); ai != audioSyncs.end(); ai++)
+ result = timeStampMax(result, (*ai)->clientTime());
+
+ return result;
+}
+
+TimeStamp MidiSyncGroup_impl::playTime()
+{
+ return masterTimer.time();
+}
+
+void MidiSyncGroup_impl::adjustSync()
+{
+ list<AudioSync_impl *>::iterator ai;
+ for(ai = audioSyncs.begin(); ai != audioSyncs.end(); ai++)
+ (*ai)->synchronizeTo(masterTimer.time());
+}
diff --git a/arts/midi/midisyncgroup_impl.h b/arts/midi/midisyncgroup_impl.h
new file mode 100644
index 00000000..2c2191ac
--- /dev/null
+++ b/arts/midi/midisyncgroup_impl.h
@@ -0,0 +1,67 @@
+ /*
+
+ Copyright (C) 2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_MIDISYNCGROUP_IMPL_H
+#define ARTS_MIDISYNCGROUP_IMPL_H
+
+#include "artsmidi.h"
+
+namespace Arts {
+
+class MidiClient_impl;
+class MidiManager_impl;
+class AudioSync_impl;
+
+class MidiSyncGroup_impl : virtual public MidiSyncGroup_skel {
+protected:
+ SystemMidiTimer masterTimer;
+
+ MidiManager_impl *manager;
+ std::list<MidiClient_impl *> clients;
+ std::list<AudioSync_impl *> audioSyncs;
+
+public:
+ MidiSyncGroup_impl(MidiManager_impl *manager);
+ ~MidiSyncGroup_impl();
+
+ // public interface
+ void addClient(MidiClient client);
+ void removeClient(MidiClient client);
+
+ void addAudioSync(AudioSync audioSync);
+ void removeAudioSync(AudioSync audioSync);
+
+ // interface to MidiClient (AudioSync)
+ void clientChanged(MidiClient_impl *client);
+ void clientDied(MidiClient_impl *client);
+ void audioSyncDied(AudioSync_impl *audioSync);
+
+ TimeStamp time();
+ TimeStamp playTime();
+
+ // interface to MidiManager
+ void adjustSync();
+};
+
+}
+
+#endif /* ARTS_MIDISYNCGROUP_IMPL_H */
diff --git a/arts/midi/midisynctest.cc b/arts/midi/midisynctest.cc
new file mode 100644
index 00000000..4faa5279
--- /dev/null
+++ b/arts/midi/midisynctest.cc
@@ -0,0 +1,137 @@
+ /*
+
+ Copyright (C) 2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "config.h"
+#include "artsmidi.h"
+#include "soundserver.h"
+#include "timestampmath.h"
+#include "debug.h"
+#include <stdio.h>
+#include <math.h>
+
+using namespace Arts;
+using namespace std;
+
+int main()
+{
+ Dispatcher dispatcher;
+
+ MidiManager midiManager = DynamicCast(Reference("global:Arts_MidiManager"));
+ if(midiManager.isNull())
+ arts_fatal("midimanager is null");
+
+ SoundServer soundServer = DynamicCast(Reference("global:Arts_SoundServer"));
+ if(soundServer.isNull())
+ arts_fatal("soundServer is null");
+
+ MidiSyncGroup syncGroup = midiManager.addSyncGroup();
+ MidiClient client = midiManager.addClient(mcdPlay, mctApplication, "midisynctest", "midisynctest");
+ syncGroup.addClient(client);
+
+ MidiPort port = client.addOutputPort();
+
+ MidiClient client2 = midiManager.addClient(mcdPlay, mctApplication, "midisynctest2", "midisynctest2");
+
+ syncGroup.addClient(client2);
+
+ MidiPort port2 = client2.addOutputPort();
+
+ /* setup audio synchronization */
+ AudioSync audioSync;
+ audioSync = DynamicCast(soundServer.createObject("Arts::AudioSync"));
+ if(audioSync.isNull())
+ arts_fatal("audioSync is null");
+
+ syncGroup.addAudioSync(audioSync);
+
+ const int C = 60;
+ const int D = 62;
+ const int E = 64;
+ const int F = 65, f = F - 12;
+ const int G = 67, g = G - 12;
+ const int A = 69, a = A - 12;
+ const int H = 71, h = H - 12;
+ int np = 0;
+ int notes[] = { C,E,G,E,a,C,E,C,f,a,C,a,g,h,D,h,0 };
+
+ printf("connect port1 and port2 to two different ports in the artscontrol midi manager,\n"
+ "hit return");
+ getchar();
+
+ TimeStamp t = port.time();
+ timeStampInc(t,TimeStamp(0,100000));
+ for(;;)
+ {
+ Synth_PLAY_WAV wav;
+ Synth_AMAN_PLAY sap;
+
+ MidiEvent e;
+
+ e = MidiEvent(t,MidiCommand(mcsNoteOn|0, notes[np], 100));
+
+ port.processEvent(e);
+ port2.processEvent(e);
+
+ if((np & 1) == 0)
+ {
+ /* setup wave player */
+ wav = DynamicCast(soundServer.createObject("Arts::Synth_PLAY_WAV"));
+ if(wav.isNull())
+ arts_fatal("can't create Arts::Synth_PLAY_WAV");
+
+ sap = DynamicCast(soundServer.createObject("Arts::Synth_AMAN_PLAY"));
+ if(sap.isNull())
+ arts_fatal("can't create Arts::Synth_AMAN_PLAY");
+
+ wav.filename("/opt/kde3/share/sounds/pop.wav");
+ sap.title("midisynctest2");
+ sap.autoRestoreID("midisynctest2");
+ connect(wav,sap);
+
+ audioSync.queueStart(wav);
+ audioSync.queueStart(sap);
+ audioSync.executeAt(t);
+ }
+
+ timeStampInc(t,TimeStamp(0,100000));
+
+ e = MidiEvent(t,MidiCommand(mcsNoteOff|0, notes[np], 100));
+
+ port.processEvent(e);
+ port2.processEvent(e);
+
+ if((np & 1) == 0)
+ {
+ audioSync.queueStop(wav);
+ audioSync.queueStop(sap);
+ audioSync.executeAt(t);
+ }
+
+ timeStampInc(t,TimeStamp(0,400000));
+
+ while(port.time().sec < (t.sec - 2))
+ usleep(100000);
+
+ np++;
+ if(notes[np] == 0) np = 0;
+ }
+}
diff --git a/arts/midi/miditest_impl.cc b/arts/midi/miditest_impl.cc
new file mode 100644
index 00000000..fe61e4c7
--- /dev/null
+++ b/arts/midi/miditest_impl.cc
@@ -0,0 +1,57 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "midimanager_impl.h"
+#include <stdio.h>
+
+namespace Arts {
+
+class MidiTest_impl : virtual public MidiTest_skel {
+public:
+ Arts::TimeStamp time()
+ {
+ return TimeStamp(0,0);
+ }
+ Arts::TimeStamp playTime()
+ {
+ return time();
+ }
+ void processCommand(const MidiCommand& command)
+ {
+ if((command.status & mcsCommandMask) == mcsNoteOn)
+ {
+ mcopbyte ch = command.status & mcsChannelMask;
+
+ printf("noteon ch = %d, note = %d, vel = %d\n",
+ ch,command.data1,command.data2);
+ }
+ }
+ void processEvent(const MidiEvent& event)
+ {
+ printf("At %ld.%06ld: ",event.time.sec,event.time.usec);
+ processCommand(event.command);
+ }
+};
+
+REGISTER_IMPLEMENTATION(MidiTest_impl);
+}
+
diff --git a/arts/midi/miditimercommon.cc b/arts/midi/miditimercommon.cc
new file mode 100644
index 00000000..205b72e5
--- /dev/null
+++ b/arts/midi/miditimercommon.cc
@@ -0,0 +1,72 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmidi.h"
+#include "debug.h"
+#include "miditimercommon.h"
+#include "math.h"
+
+#undef DEBUG_JITTER
+
+using namespace std;
+using namespace Arts;
+
+MidiTimerCommon::MidiTimerCommon() :refCount(0)
+{
+ refCount = 0;
+}
+
+MidiTimerCommon::~MidiTimerCommon()
+{
+ arts_assert(refCount == 0);
+}
+
+void MidiTimerCommon::processQueue()
+{
+ TimeStamp now = time();
+
+ list<TSNote>::iterator n = noteQueue.begin();
+ while(n != noteQueue.end())
+ {
+ TSNote& note = *n;
+ TimeStamp& noteTime = note.event.time;
+
+ if( now.sec > noteTime.sec
+ || ((now.sec == noteTime.sec) && (now.usec > noteTime.usec)))
+ {
+#ifdef DEBUG_JITTER
+ float jitter = (now.sec-noteTime.sec) * 1000.0;
+ jitter += (float)(now.usec-noteTime.usec) / 1000.0;
+ arts_debug("midi jitter: %f",jitter);
+#endif
+
+ note.port.processCommand(note.event.command);
+ n = noteQueue.erase(n);
+ }
+ else n++;
+ }
+}
+
+void MidiTimerCommon::queueEvent(MidiPort port,const MidiEvent& event)
+{
+ noteQueue.push_back(TSNote(port, event));
+}
diff --git a/arts/midi/miditimercommon.h b/arts/midi/miditimercommon.h
new file mode 100644
index 00000000..5f4bb250
--- /dev/null
+++ b/arts/midi/miditimercommon.h
@@ -0,0 +1,59 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_MIDI_MIDITIMERCOMMON_H
+#define ARTS_MIDI_MIDITIMERCOMMON_H
+
+#include "artsmidi.h"
+
+namespace Arts {
+
+class MidiTimerCommon {
+protected:
+ struct TSNote {
+ MidiPort port;
+ MidiEvent event;
+
+ TSNote() { /* for some stl impls */ }
+
+ TSNote(MidiPort port, const MidiEvent& event) :
+ port(port), event(event)
+ {
+ }
+ };
+ std::list<TSNote> noteQueue;
+
+ int refCount;
+ void processQueue();
+
+ MidiTimerCommon();
+ virtual ~MidiTimerCommon();
+
+public:
+ void unsubscribe() { if(--refCount == 0) delete this; }
+
+ void queueEvent(MidiPort port, const MidiEvent& event);
+ virtual TimeStamp time() = 0;
+};
+
+}
+#endif /* ARTS_MIDI_MIDITIMERCOMMON_H */
diff --git a/arts/midi/rawmidiport_impl.cc b/arts/midi/rawmidiport_impl.cc
new file mode 100644
index 00000000..0c9fd340
--- /dev/null
+++ b/arts/midi/rawmidiport_impl.cc
@@ -0,0 +1,306 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmidi.h"
+#include <iomanager.h>
+#include <debug.h>
+#include <fcntl.h>
+
+using namespace std;
+
+namespace Arts {
+
+class RawMidiPort_impl : virtual public RawMidiPort_skel,
+ virtual public IONotify
+{
+protected:
+ int fd;
+
+ string _device;
+ bool _input, _output;
+ bool _running;
+ mcopbyte laststatus;
+ queue<mcopbyte> inq;
+ MidiClient clientRecord, clientPlay;
+ MidiPort outputPort;
+ MidiManager manager;
+
+ RawMidiPort self() {
+ return RawMidiPort::_from_base(_copy());
+ }
+ SystemMidiTimer timer;
+
+public:
+ RawMidiPort_impl()
+ :_device("/dev/midi"), _input(true), _output(true), _running(false),
+ clientRecord(MidiClient::null()), clientPlay(MidiClient::null()),
+ outputPort(MidiPort::null()),
+ manager(Reference("global:Arts_MidiManager"))
+ {
+ }
+ Arts::TimeStamp time()
+ {
+ return timer.time();
+ }
+ Arts::TimeStamp playTime()
+ {
+ return timer.time();
+ }
+
+ // attribute string running;
+ bool running() { return _running; }
+ void running(bool newrunning) {
+ if(_running == newrunning) return;
+
+ if(newrunning)
+ open();
+ else
+ close();
+
+ running_changed(_running);
+ }
+
+ // attribute string device;
+ void device(const string& newdevice)
+ {
+ if(newdevice == _device) return;
+
+ if(_running)
+ {
+ close();
+ _device = newdevice;
+ open();
+ }
+ else _device = newdevice;
+
+ device_changed(newdevice);
+ }
+ string device() { return _device; }
+
+ // attribute boolean input;
+ void input(bool newinput)
+ {
+ if(newinput == _input) return;
+
+ if(_running)
+ {
+ close();
+ _input = newinput;
+ open();
+ }
+ else _input = newinput;
+
+ input_changed(newinput);
+ }
+ bool input() { return _input; }
+
+ // attribute boolean output;
+ void output(bool newoutput)
+ {
+ if(newoutput == _output) return;
+
+ if(_running)
+ {
+ close();
+ _output = newoutput;
+ open();
+ }
+ else _output = newoutput;
+
+ output_changed(newoutput);
+ }
+ bool output() { return _output; }
+
+ bool open() {
+ arts_return_val_if_fail(_running == false, true);
+ arts_return_val_if_fail(_output || _input, false);
+ arts_return_val_if_fail(manager.isNull() == false, false);
+ laststatus = 0;
+
+ int mode = O_NDELAY;
+ if(_input)
+ {
+ if(_output)
+ mode |= O_RDWR;
+ else
+ mode |= O_RDONLY;
+ }
+ else mode |= O_WRONLY;
+
+ fd = ::open(_device.c_str(),mode);
+ if(fd != -1)
+ {
+ IOManager *iom = Dispatcher::the()->ioManager();
+ if(_output)
+ iom->watchFD(fd,IOType::read,this);
+
+ string name = "OSS Midi Port ("+_device+")";
+ if(_input)
+ {
+ clientRecord =
+ manager.addClient(mcdRecord,mctDestination,name,name);
+ clientRecord.addInputPort(self());
+ }
+ if(_output)
+ {
+ clientPlay =
+ manager.addClient(mcdPlay,mctDestination,name,name);
+ outputPort = clientPlay.addOutputPort();
+ }
+
+ _running = true;
+ running_changed(true);
+ }
+ return _running;
+ }
+
+ void close()
+ {
+ arts_return_if_fail(_running == true);
+
+ if(_input)
+ {
+ clientRecord.removePort(self());
+ clientRecord = MidiClient::null();
+ }
+ if(_output)
+ {
+ clientPlay.removePort(outputPort);
+ clientPlay = MidiClient::null();
+ }
+
+ Dispatcher::the()->ioManager()->remove(this,IOType::all);
+ ::close(fd);
+ }
+
+ int midiMsgLen(mcopbyte status)
+ {
+ switch(status & mcsCommandMask)
+ {
+ case mcsNoteOn:
+ case mcsNoteOff:
+ case mcsKeyPressure:
+ case mcsParameter:
+ case mcsPitchWheel:
+ return 3;
+ break;
+ case mcsProgram:
+ case mcsChannelPressure:
+ return 2;
+ break;
+ }
+ return 0;
+ }
+ void notifyIO(int fd, int type)
+ {
+ arts_return_if_fail(_running);
+ assert(fd == this->fd);
+
+ // convert iomanager notification types to audiosubsys notification
+ if(type & IOType::read)
+ {
+ mcopbyte buffer[1024];
+ int count = read(fd,buffer,1024);
+ for(int i=0; i<count; i++)
+ {
+ /*
+ * for now ignore all realtime-messages (0xF8 .. 0xFF),
+ * which may get * embedded *inside* every other message
+ */
+ if(buffer[i] < 0xf8)
+ inq.push(buffer[i]);
+ }
+ }
+ processMidi();
+ }
+
+ void processMidi()
+ {
+ for(;;)
+ {
+ // if we get a status byte, this is our new status
+ if(!inq.empty())
+ {
+ if(inq.front() & 0x80)
+ {
+ laststatus = inq.front();
+ inq.pop();
+ }
+ }
+
+ // try to read a midi message with our current status
+ // (this supports running status as well as normal messages)
+ int len = midiMsgLen(laststatus);
+ if(len)
+ {
+ if(len == 2)
+ {
+ if(inq.empty()) return; // need more input
+
+ MidiCommand command;
+ command.status = laststatus;
+ command.data1 = inq.front(); inq.pop();
+ command.data2 = 0;
+ outputPort.processCommand(command);
+ }
+ else if(len == 3)
+ {
+ if(inq.size() < 2) return; // need more input
+
+ MidiCommand command;
+ command.status = laststatus;
+ command.data1 = inq.front(); inq.pop();
+ command.data2 = inq.front(); inq.pop();
+ outputPort.processCommand(command);
+ }
+ else
+ {
+ arts_assert(false);
+ }
+ }
+ else
+ {
+ if(inq.empty()) return; // need more input
+
+ /* we are somewhat out of sync it seems -> read something
+ * away and hope we'll find a status byte */
+ inq.pop();
+ }
+ }
+ }
+ void processCommand(const MidiCommand& command)
+ {
+ char message[3] = { command.status, command.data1, command.data2 };
+
+ int len = midiMsgLen(command.status);
+ if(midiMsgLen(command.status))
+ write(fd, message, len);
+ }
+ void processEvent(const MidiEvent& event)
+ {
+ timer.queueEvent(self(), event);
+ }
+};
+
+REGISTER_IMPLEMENTATION(RawMidiPort_impl);
+}
+
diff --git a/arts/midi/systemmiditimer_impl.cc b/arts/midi/systemmiditimer_impl.cc
new file mode 100644
index 00000000..bda27a8c
--- /dev/null
+++ b/arts/midi/systemmiditimer_impl.cc
@@ -0,0 +1,105 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmidi.h"
+#include "debug.h"
+#include "miditimercommon.h"
+
+using namespace std;
+using namespace Arts;
+
+namespace Arts {
+
+class SystemMidiTimerCommon : public MidiTimerCommon,
+ public TimeNotify {
+protected:
+ SystemMidiTimerCommon();
+ virtual ~SystemMidiTimerCommon();
+
+public:
+ // allocation: share one SystemMidiTimerCommon for everbody who needs one
+ static SystemMidiTimerCommon *subscribe();
+
+ void notifyTime();
+ TimeStamp time();
+};
+
+}
+
+static SystemMidiTimerCommon *SystemMidiTimerCommon_the = 0;
+
+SystemMidiTimerCommon::SystemMidiTimerCommon()
+{
+ SystemMidiTimerCommon_the = this;
+ Dispatcher::the()->ioManager()->addTimer(10, this);
+}
+
+SystemMidiTimerCommon::~SystemMidiTimerCommon()
+{
+ Dispatcher::the()->ioManager()->removeTimer(this);
+ SystemMidiTimerCommon_the = 0;
+}
+
+TimeStamp SystemMidiTimerCommon::time()
+{
+ timeval tv;
+ gettimeofday(&tv,0);
+ return TimeStamp(tv.tv_sec, tv.tv_usec);
+}
+
+void SystemMidiTimerCommon::notifyTime()
+{
+ processQueue();
+}
+
+SystemMidiTimerCommon *SystemMidiTimerCommon::subscribe()
+{
+ if(!SystemMidiTimerCommon_the) new SystemMidiTimerCommon();
+ SystemMidiTimerCommon_the->refCount++;
+ return SystemMidiTimerCommon_the;
+}
+
+class SystemMidiTimer_impl : public SystemMidiTimer_skel {
+protected:
+ SystemMidiTimerCommon *timer;
+public:
+ SystemMidiTimer_impl()
+ {
+ timer = SystemMidiTimerCommon::subscribe();
+ }
+ ~SystemMidiTimer_impl()
+ {
+ timer->unsubscribe();
+ }
+ TimeStamp time()
+ {
+ return timer->time();
+ }
+ void queueEvent(MidiPort port, const MidiEvent& event)
+ {
+ timer->queueEvent(port, event);
+ }
+};
+
+namespace Arts {
+ REGISTER_IMPLEMENTATION(SystemMidiTimer_impl);
+}
diff --git a/arts/midi/timestampmath.cc b/arts/midi/timestampmath.cc
new file mode 100644
index 00000000..ff33c280
--- /dev/null
+++ b/arts/midi/timestampmath.cc
@@ -0,0 +1,112 @@
+ /*
+
+ Copyright (C) 2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "timestampmath.h"
+#include <arts/debug.h>
+#include <stdio.h>
+
+using namespace std;
+
+namespace Arts {
+void timeStampInc(TimeStamp& t, const TimeStamp& delta)
+{
+ /* expect a normalized t, delta */
+ arts_return_if_fail(t.usec >= 0 && t.usec < 1000000);
+ arts_return_if_fail(delta.usec >= 0 && delta.usec < 1000000);
+
+ t.sec += delta.sec;
+ t.usec += delta.usec;
+
+ if (t.usec >= 1000000)
+ {
+ t.usec -= 1000000;
+ t.sec += 1;
+ }
+
+ arts_assert (t.usec >= 0 && t.usec < 1000000);
+}
+
+void timeStampDec(TimeStamp& t, const TimeStamp& delta)
+{
+ /* expect a normalized t, delta */
+ arts_return_if_fail(t.usec >= 0 && t.usec < 1000000);
+ arts_return_if_fail(delta.usec >= 0 && delta.usec < 1000000);
+
+ t.sec -= delta.sec;
+ t.usec -= delta.usec;
+
+ if(t.usec < 0)
+ {
+ t.usec += 1000000;
+ t.sec -= 1;
+ }
+
+ arts_assert(t.usec >= 0 && t.usec < 1000000);
+}
+
+string timeStampToString(const TimeStamp& t)
+{
+ arts_return_val_if_fail(t.usec >= 0 && t.usec < 1000000, "");
+
+ char buffer[1024];
+ if(t.sec < 0 && t.usec != 0)
+ {
+ sprintf(buffer, "-%d.%06d", -t.sec-1, 1000000-t.usec);
+ }
+ else
+ {
+ sprintf(buffer, "%d.%06d", t.sec, t.usec);
+ }
+ return buffer;
+}
+
+double timeStampToDouble(const TimeStamp& t)
+{
+ arts_return_val_if_fail(t.usec >= 0 && t.usec < 1000000, 0.0);
+
+ return double(t.sec) + double(t.usec)/1000000.0;
+}
+
+TimeStamp timeStampFromDouble(double d)
+{
+ TimeStamp t;
+
+ arts_return_val_if_fail(d >= 0, t);
+
+ t.sec = int(d);
+ d -= t.sec;
+ t.usec = int(d * 1000000.0);
+
+ return t;
+}
+
+TimeStamp timeStampMax(const TimeStamp& t1, const TimeStamp& t2)
+{
+ if(t1.sec > t2.sec)
+ return t1;
+ else if((t1.sec == t2.sec) && (t1.usec > t2.usec))
+ return t1;
+ else
+ return t2;
+}
+
+}
diff --git a/arts/midi/timestampmath.h b/arts/midi/timestampmath.h
new file mode 100644
index 00000000..934df716
--- /dev/null
+++ b/arts/midi/timestampmath.h
@@ -0,0 +1,62 @@
+ /*
+
+ Copyright (C) 2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_TIMESTAMPMATH_H
+#define ARTS_TIMESTAMPMATH_H
+
+#include "artsmidi.h"
+#include <kdelibs_export.h>
+namespace Arts {
+
+/**
+ * increments the timestamp by delta
+ */
+KDE_EXPORT void timeStampInc(TimeStamp& t, const TimeStamp& delta);
+
+/**
+ * decrements the timestamp by delta
+ */
+void timeStampDec(TimeStamp& t, const TimeStamp& delta);
+
+/**
+ * stringifies a timestamp
+ */
+std::string timeStampToString(const TimeStamp& t);
+
+/**
+ * converts a timestamp to a double of seconds
+ */
+double timeStampToDouble(const TimeStamp& t);
+
+/**
+ * converts a double of seconds to a timestamp
+ */
+TimeStamp timeStampFromDouble(double d);
+
+/**
+ * returns the maximum of two timestamps
+ */
+TimeStamp timeStampMax(const TimeStamp& t1, const TimeStamp& t2);
+
+}
+
+#endif /* ARTS_TIMESTAMPMATH_H */
diff --git a/arts/modules/Makefile.am b/arts/modules/Makefile.am
new file mode 100644
index 00000000..1f5fb09c
--- /dev/null
+++ b/arts/modules/Makefile.am
@@ -0,0 +1,60 @@
+####### Various modules for artsmodules
+
+SUBDIRS = synth common effects mixers
+INCLUDES= $(ARTSC_INCLUDE) \
+ -I$(top_srcdir)/arts/modules \
+ -I$(top_builddir)/arts/modules/synth \
+ -I$(top_builddir)/arts/modules/common \
+ -I$(top_builddir)/arts/modules/effects \
+ -I$(top_builddir)/arts/modules/mixers \
+ -I$(top_builddir)/arts/runtime \
+ -I$(top_builddir)/arts/midi \
+ -I$(top_srcdir)/arts/midi \
+ -I$(top_srcdir)/arts/gui/common -I$(top_builddir)/arts/gui/common \
+ -I$(arts_includes) $(all_includes)
+
+MCOPIDLINCLUDES = \
+ -I$(top_srcdir)/arts/midi \
+ -I$(top_srcdir)/arts/gui/common \
+ -I$(top_srcdir)/arts/modules \
+ -I$(top_srcdir)/arts/modules/synth \
+ -I$(top_srcdir)/arts/modules/common \
+ -I$(top_srcdir)/arts/modules/effects \
+ -I$(top_srcdir)/arts/modules/mixers \
+ -I$(arts_includes) $(all_includes)
+
+lib_LTLIBRARIES = libartsmodules.la
+
+libartsmodules_la_SOURCES = artsmodules.cc
+
+libartsmodules_la_LIBADD = \
+ $(top_builddir)/arts/runtime/libartsbuilder.la \
+ $(top_builddir)/arts/midi/libartsmidi_idl.la \
+ $(top_builddir)/arts/gui/common/libartsgui_idl.la \
+ $(top_builddir)/arts/modules/synth/libartsmodulessynth.la \
+ $(top_builddir)/arts/modules/common/libartsmodulescommon.la \
+ $(top_builddir)/arts/modules/effects/libartsmoduleseffects.la \
+ $(top_builddir)/arts/modules/mixers/libartsmodulesmixers.la \
+ -lartsflow -lmcop $(LIB_KDECORE) $(LIBDL)
+libartsmodules_la_LDFLAGS = $(all_libraries) -L$(arts_libraries) \
+ -no-undefined
+
+
+artsmodules.mcopclass: artsmodules.h
+artsmodules.mcoptype: artsmodules.h
+artsmodules.cc artsmodules.h: $(srcdir)/artsmodules.idl $(MCOPIDL)
+ $(MCOPIDL) -t $(MCOPIDLINCLUDES) $(srcdir)/artsmodules.idl
+
+DISTCLEANFILES = artsmodules.cc artsmodules.h \
+ artsmodules.mcoptype artsmodules.mcopclass
+
+####### install idl files
+
+artsincludedir = $(includedir)/arts
+artsinclude_HEADERS = artsmodules.h artsmodules.idl
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = artsmodules.mcoptype artsmodules.mcopclass
+
+artsmodules.lo: artsmodules.h ../midi/artsmidi.h ../gui/common/artsgui.h common/artsmodulescommon.h synth/artsmodulessynth.h effects/artsmoduleseffects.h mixers/artsmodulesmixers.h
+
diff --git a/arts/modules/README.environments b/arts/modules/README.environments
new file mode 100644
index 00000000..0ac64df5
--- /dev/null
+++ b/arts/modules/README.environments
@@ -0,0 +1,304 @@
+Environments:
+=============
+
+1. What is an Environment?
+2. How do the interfaces look?
+3. How do I create/use one?
+4. How do I implement items?
+
+/*
+Documentation TODO: add more details:
+ - Guis and aRts (probably seperate document), GuiFactory, GenericGuiFactory,...
+ - the dataDirectory (sections 2,3,4)
+ - how items react on changes of active (section 4)
+*/
+
+ 1. What is an Environment?
+ --------------------------
+
+Programs like sequencers often require complex structures with lots of
+parameters to configure running inside aRts, to create music. For instance
+for composing a song, you might require
+
+ - several effects
+ - several synthetic instruments
+ - several audio tracks
+ - a mixer
+
+While with artscontrol, the user can setup much of this himself manually, the
+problem is that this has to be done over and over again. That is, if he saves
+the song, the settings of his effects, instruments and the mixer will not be
+saved with it.
+
+The main idea of the new interfaces in Arts::Environment is that the sequencer
+can save the environment required to create a song along with the the song, so
+that the user will find himself surrounded by the same effects, instruments,...
+with the same settings again, once he loads the song again.
+
+So, conceptually, we can imagine the environment as a "room", where the user
+works in to create a song. He needs to install the things inside the room he
+needs. Initially, the room will be empty. Now, the user things: oh, I am going
+to need this nice 24 channel mixer. *plop* - it appears in the room. Now he
+thinks I need some sampler which can play my piano. *plop* - it appears in
+the room.
+
+Now he starts working, and adds the "items" he needs. Finally, if he stops
+working on the song, he can pack all what is in the environment in a little
+box, and whenever he starts working on the song again, he can start where he
+left off. He can even take the environment to a friend, and continue working
+on the song there.
+
+Note that there might be other tasks (such as creating a film, playing an
+mp3 with noatun,...) which will have similar requirements of saving the
+current state, so the concept of environments is not limited to songs.
+
+ 2. How do the interfaces look?
+ ------------------------------
+
+There are two main things in the Environment, that is
+
+Arts::Environment::Container this interface is where you put all your stuff
+ you need to create a song
+
+Arts::Environment::Item this is an item that works inside an
+ environment
+
+ 2.1 The Container interface:
+ ----------------------------
+
+Initially "Container"s are empty when created. If you create items, you need
+to tell the container about it.
+
+ void addItem(Item item);
+ Item createItem(string name);
+
+You can create the Item yourself and use addItem afterwards, or you can tell
+the container to create an Item (by name), and return it to you. Then it will
+automatically be put into the environment.
+
+You can list the items that are currently inside an environment with the
+attribute
+
+ readonly attribute sequence<Item> items;
+
+and remove them with
+
+ void removeItem(Item item);
+
+Finally, the more interesting aspect is that you can save the whole
+environment to a list of strings, and restore it from there.
+
+ sequence<string> saveToList();
+ void loadFromList(sequence<string> strlist);
+
+If you load it, all items will be created that were created in the environment
+you saved.
+
+
+There are two more special items. Here are two remaining problems to explain
+you the purpose of these (you can skip these explaination if you want get
+the basic idea first):
+
+ * the "sample data problem"
+
+Consider you have a song where you use these spectacular "boom" sound you just
+sampled. Now you save it to a list of strings, and take it to a friend. There
+are two things that could happen up to now:
+
+1. the "boom" sound doesn't get put into that list of strings, so when you play
+the song on your friends computer it might be missing
+
+2. the "boom" sound gets saved as string list - this would probably be really
+really inefficient for both, loading and saving
+
+So we introduce a
+
+ attribute string dataDirectory;
+
+where song specific data can be saved. The general idea is that items should
+access data from anywhere on your harddisc. However, if you execute a special
+operation (such as "pack the environment"), all data used in the environment
+should get copied into the dataDirectory, and the data should be used from
+there in the future.
+
+The details of this are not quite done yet.
+
+ * the "outside world object" problem
+
+Suppose you use an environment and some items use objects that are not items
+of the environment. A typical example might be objects which refer to an
+Arts::StereoEffectStack outside the environment. Such objects could be saved,
+but they need to know how to restore themselves to "the same" StereoEffectStack
+again (which will not be restored by the environment).
+
+The general idea to solve this is the context. In the context the user can
+name non-environment objects and say: "well, here is a StereoEffectStack my
+items will refer to, and it is named 'OutputEffectStack'". Upon serialization,
+the environment would not care about the OutputEffectStack, and each item
+which needs to refer to it would only save that it needs to put the items
+in something called 'OutputEffectStack' again.
+
+The same way, up on restore, items could lookup the 'OutputEffectStack' again
+and insert StereoEffects in there.
+
+The details of this are not quite done yet.
+
+ 2.2 The Item interface:
+ -----------------------
+
+Most functions in the Item interface are not too relevant for users. Upon
+insertion, the environment uses
+
+ void setContainer(Container container);
+
+to tell the Item in which environment it lives. It also uses setContainer(
+Container::null()) once the Item gets removed. Which container the Item
+is in can be seen in the
+
+ readonly attribute Container parent;
+
+Upon serialization, the container uses
+
+ sequence<string> saveToList();
+ void loadFromList(sequence<string> strlist);
+
+to save or restore the data. Finally, you can see if the item is currently
+inside a Container with the
+
+ readonly attribute boolean active;
+
+There is some trick here: the problem is that you will probably want to hold
+references to items (or parts of items) in some situations. But if you do so,
+and for instance display the item on the GUI, you will still display the item
+if it was removed from the environment. And you will (by reference counting)
+prevent it from disappearing.
+
+So... if you hold a reference, watch whether the item is still active, using
+
+ connect(item, "active_changed", ...)
+
+and do release the references you hold if it is not. I.e. if you have a mixer
+window on the screen, and the mixer gets inactive, close it. (See also: change
+notification documentation).
+
+ 3. How do I create/use one?
+ ---------------------------
+
+First of all, creation. Usually, you will hold your environments on the
+sound server. So creating will look like:
+
+ /* lookup sound server (use an existing one, if you have one) */
+ Arts::SoundServer server = Reference("global:Arts_SoundServer");
+ if(server.isNull())
+ /* error handling -> no sound server running */;
+
+ /* create the object on the server */
+ Arts::Environment::Container container =
+ Arts::DynamicCast(server.createObject("Arts::Environment::Container"));
+ if(container.isNull())
+ /* error handling -> environment container could not be created */;
+
+Good, now we have an environment. What to do now? We could add a mixer. This
+would work like
+
+ Arts::Environment::MixerItem mixer =
+ Arts::DynamicCast(container.createItem("Arts::Environment::MixerItem"));
+ if(mixer.isNull())
+ /* error handling -> no mixer */;
+
+Cool. A mixer. What do we do with that. Hm... setting the channel count would
+be nice, for instance.
+
+ mixer.channelCount(8); /* an eight channel mixer */
+
+And finally, we could display a GUI for it, using the KDE gui embedding thing.
+
+ Arts::GenericGuiFactory guiFactory;
+ Arts::Widget widget = guiFactory.createGui(mixer);
+ if(!widget.isNull())
+ {
+ KArtsWidget *kartswidget = new KArtsWidget(widget);
+ kartswidget->show();
+ }
+ else
+ {
+ /* error handling -> no gui available for this item */
+ }
+
+NOTE: for GenericGuiFactory to work, it needs to know what toolkit you are
+using. For KDE, you need to add the line
+
+ ObjectManager::the()->provideCapability("kdegui");
+
+somewhere in your application (only do this once).
+
+You can try using saveToList or loadFromList on the container, too. A classical
+piece of code which does this is, copied from artscontrol:
+
+void EnvironmentView::load() /* load from file DEFAULT_ENV_FILENAME */
+{
+ ifstream infile(DEFAULT_ENV_FILENAME);
+ string line;
+ vector<string> strseq;
+
+ while(getline(infile,line))
+ strseq.push_back(line);
+
+ defaultEnvironment().loadFromList(strseq); /* we'd use "container" here */
+}
+
+void EnvironmentView::save()
+{
+ vector<string> *strseq;
+ strseq = defaultEnvironment().saveToList(); /* we'd use "container" here */
+
+ ofstream outfile(DEFAULT_ENV_FILENAME);
+ for(vector<string>::iterator i = strseq->begin(); i != strseq->end(); i++)
+ outfile << *i << endl;
+ delete strseq;
+}
+
+/* remark about loading:
+Of course, after loading the environment, you might want to look at
+container.items, to be able to display the guis again. To find out if an
+item is a mixer, you can do Arts::Environment::MixerItem m =
+Arts::DynamicCast(item); - or if you want to get the type in a generic
+way, you can use string typename = item._interfaceName(); */
+
+Finally, if we're tired of using our mixer, we can remove it again, using
+
+ container.removeItem(mixer);
+
+ 4. How do I implement items?
+ ----------------------------
+
+Basically, you derive an interface from Arts::Environment::Item, like this:
+
+ // this code is in the .idl file:
+ interface MyItem : Arts::Environment::Item {
+ /* methods/attributes here */
+ };
+
+and your implementation from Arts::Environment::Item_impl, like this:
+
+ // this code is in the .cc file:
+ #include "artsmodules.h"
+ #include "env_item_impl.h"
+
+ class MyItem_impl : virtual public MyItem_skel,
+ virtual public Arts::Environment::Item_impl
+ {
+ public:
+ void loadFromList(const vector<string>& list)
+ {
+ /* you need to implement this... */
+ }
+ vector<string> *saveToList()
+ {
+ /* ... and that */
+ }
+ };
+ REGISTER_IMPLEMENTATION(MyItem_impl); /* register your implementation */
+
+If you want your item to have a Gui, implement a GuiFactory that can create
+a gui for the item.
diff --git a/arts/modules/README.modules b/arts/modules/README.modules
new file mode 100644
index 00000000..7113b3a9
--- /dev/null
+++ b/arts/modules/README.modules
@@ -0,0 +1,28 @@
+ How to add a new aRts module
+
+(This will eventually go into the aRts documentation).
+
+To add a new module "foo" to aRts:
+
+1. Add a new interface to artsmodules.idl defining the
+ module's input and output parameters.
+
+2. Implement the new module in a new source file foo_impl.cc
+
+3. Add foo_impl.cc to the list of libartsmodules_la_SOURCES in
+ Makefile.am.
+
+4. Create a new file mcopclass/foo.mcopclass
+
+5. Add foo.mcopclass to the list of mcopclass_DATA in
+ mcopclass/Makefile.am.
+
+6. Do a clean build and install and debug and test your new module.
+ (make clean && make && make install)
+ killall artsd ; artsd &
+
+7. Update the TODO file with the status of the new module.
+
+
+Jeff Tranter
+tranter@pobox.com
diff --git a/arts/modules/README.subdirs b/arts/modules/README.subdirs
new file mode 100644
index 00000000..c06193b9
--- /dev/null
+++ b/arts/modules/README.subdirs
@@ -0,0 +1,18 @@
+
+ Some info about the subdirs...
+
+In order of dependancy:
+
+- synth:
+ All Synth_* Modules.
+ Used to create Effects, mixers an synthesizers...
+
+ - common:
+ Just some basics like environment.
+
+- effects:
+ Implementations of effects.
+
+- mixers:
+ Implementations of mixerchannels.
+
diff --git a/arts/modules/TODO b/arts/modules/TODO
new file mode 100644
index 00000000..3aaf7a0f
--- /dev/null
+++ b/arts/modules/TODO
@@ -0,0 +1,58 @@
+Here is a complete list of modules that aRts-0.3.4.1 supported,
+with a status how far the port to the KDE2.0 aRts/MCOP version is.
+
+Porting modules is usually a ten-minute task, unless the underlying
+features are not yet provided by the new flow system or the general
+idea of how things need to be done changed.
+
+Synth_ADD done (kdelibs)
+Synth_AMAN_CAPTURE obsolete, see Synth_AMAN_RECORD, converter missing
+Synth_AMAN_INJECT obsolete, see Synth_AMAN_PLAY, ByteStreamToAudio
+Synth_ATAN_SATURATE done (kdemultimedia)
+Synth_AUTOPANNER done (kdemultimedia)
+Synth_BRICKWALL_LIMITER done (kdemultimedia)
+Synth_BUS_DOWNLINK done (kdelibs)
+Synth_BUS_UPLINK done (kdelibs)
+Synth_CDELAY done (kdemultimedia)
+Synth_DATA done (kdemultimedia)
+Synth_DEBUG done (kdemultimedia)
+Synth_DELAY done (kdemultimedia)
+Synth_ENVELOPE_ADSR done (kdemultimedia)
+Synth_FILEPLAY outfile name issue; called Synth_CAPTURE_WAV now
+Synth_FM_SOURCE done (kdemultimedia)
+Synth_FREQUENCY done (kdelibs)
+Synth_FULL_DUPLEX_PLAY obsolete, now done by Synth_PLAY (kdelibs)
+Synth_FULL_DUPLEX_REC obsolete, now done by Synth_REC (kdelibs)
+Synth_FX_CFLANGER done (kdemultimedia)
+Synth_MIDI_DEBUG done (kdemultimedia)
+Synth_MIDI_MAP_ROUTER todo, must fit in new midi architecture
+Synth_MIDI_ROUTER todo, must fit in new midi architecture
+Synth_MIDI_SOURCE obsolete
+Synth_MOOG_VCF done (kdemultimedia)
+Synth_MUL done (kdelibs)
+Synth_NIL done (kdemultimedia)
+Synth_PARAM_GET obsolete
+Synth_PARAM_SET obsolete
+Synth_PARAM_SGET obsolete
+Synth_PARAM_SSET obsolete
+Synth_PITCH_SHIFT done (kdemultimedia)
+Synth_PLAY done (kdelibs), currently no mono play ability
+Synth_PLAY_AKAI todo
+Synth_PLAY_AKAIS todo
+Synth_PLAY_PITCHED_WAV maybe obsolete (kdelibs version supports speed)
+Synth_PLAY_WAV done (kdelibs)
+Synth_PSCALE done (kdemultimedia)
+Synth_RC done (kdemultimedia)
+Synth_SEQUENCE done (kdemultimedia)
+Synth_SHELVE_CUTOFF done (kdemultimedia)
+Synth_STDIN obsolete
+Synth_STDOUT obsolete
+Synth_STD_EQUALIZER done (kdemultimedia)
+Synth_STRUCT_KILL obsolete
+Synth_TREMOLO done (kdemultimedia)
+Synth_WAVE_PULSE done (kdemultimedia, not in aRts 0.3.4.1)
+Synth_WAVE_SIN done (kdelibs)
+Synth_WAVE_SOFTSAW done (kdemultimedia)
+Synth_WAVE_SQUARE done (kdemultimedia)
+Synth_WAVE_TRI done (kdemultimedia)
+Synth_XFADE done (kdemultimedia)
diff --git a/arts/modules/artsmodules.idl b/arts/modules/artsmodules.idl
new file mode 100644
index 00000000..6a9bd116
--- /dev/null
+++ b/arts/modules/artsmodules.idl
@@ -0,0 +1,186 @@
+ /*
+
+ Copyright (C) 2000-2001 Stefan Westerfeld
+ stefan@space.twc.de
+ 2001-2003 Matthias Kretz
+ kretz@kde.org
+ 2002 Arnold Krille
+ arnold@arnoldarts.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+/*
+ * DISCLAIMER: The interfaces in artsmodules.idl (and the derived .cc/.h files)
+ * DO NOT GUARANTEE BINARY COMPATIBILITY YET.
+ *
+ * They are intended for developers. You shouldn't expect that applications in
+ * binary form will be fully compatibile with further releases of these
+ * interfaces.
+ */
+
+#include <artsflow.idl>
+#include <artsmidi.idl>
+#include <artsgui.idl>
+
+#include <artsmodulescommon.idl>
+#include <artsmodulessynth.idl>
+#include <artsmoduleseffects.idl>
+#include <artsmodulesmixers.idl>
+
+module Arts {
+
+// EXPERIMENTAL ENVIRONMENT CODE:
+//
+//// moved to common
+//
+//module Environment {
+// interface Context;
+// interface Item;
+// interface Container;
+// interface InstrumentItem : Item;
+// interface InstrumentItemGuiFactory : Arts::GuiFactory;
+// interface StereoEffectItem : Item;
+// interface MixerChannel : Arts::StereoEffect;
+// interface MixerItem : Item;
+// interface EffectRackItem : Item;
+//};
+
+//// moved to mixers
+//
+//interface SimpleMixerChannel : Environment::MixerChannel;
+//interface MonoSimpleMixerChannel : Environment::MixerChannel;
+//interface Synth_AUX_BUS : SynthModule; {
+
+//// moved to common
+//
+//// creates: Environment::MixerItem, SimpleMixerChannel
+//interface MixerGuiFactory : GuiFactory;
+//// creates: Environment::EffectRackItem
+//interface EffectRackGuiFactory : GuiFactory;
+
+//// moved to mixers
+//
+//// creates: MonoSimpleMixerChannel
+//interface MonoSimpleMixerChannelGuiFactory : GuiFactory;
+//// creates: SimpleMixerChannel
+//interface SimpleMixerChannelGuiFactory : GuiFactory;
+
+//// moved to common
+//
+//interface MixerItemGui;
+
+//// moved to synth
+//
+//// EXPERIMENTAL MIDI
+//interface ObjectCache;
+//interface MidiReleaseHelper : SynthModule;
+//// END EXPERIMENTAL MIDI
+
+//// moved to synth
+//
+//interface Synth_CAPTURE_WAV : SynthModule;
+//interface Synth_COMPRESSOR : SynthModule;
+
+//// moved to effects
+//
+//interface Synth_STEREO_COMPRESSOR : StereoEffect;
+//interface StereoCompressorGuiFactory : GuiFactory;
+
+//// moved to synth
+//
+//interface Synth_NIL : SynthModule;
+//interface Synth_DEBUG : SynthModule;
+//interface Synth_DATA : SynthModule;
+//interface Synth_ATAN_SATURATE : SynthModule;
+
+//// moved to synth
+//
+//interface Synth_BRICKWALL_LIMITER : SynthModule;
+//interface Synth_AUTOPANNER : SynthModule;
+//interface Synth_DELAY : SynthModule;
+//interface Synth_CDELAY : SynthModule;
+//interface Synth_FM_SOURCE : SynthModule;
+//interface Synth_TREMOLO : SynthModule;
+//interface Synth_FX_CFLANGER : SynthModule;
+//interface Synth_NOISE : SynthModule;
+//interface Synth_WAVE_TRI : SynthModule;
+//interface Synth_WAVE_SQUARE : SynthModule;
+//interface Synth_WAVE_PULSE : SynthModule;
+//interface Synth_WAVE_SOFTSAW : SynthModule;
+//interface Synth_ENVELOPE_ADSR : SynthModule;
+//interface Synth_SHELVE_CUTOFF : SynthModule;
+//interface Synth_XFADE : SynthModule;
+//interface Synth_MIDI_TEST : SynthModule, MidiPort;
+//interface Synth_MIDI_DEBUG : SynthModule, MidiPort;
+
+//// moved to effects
+//
+//interface Synth_FREEVERB : StereoEffect;
+//interface FreeverbGuiFactory : GuiFactory;
+
+//// moved to synth
+//
+//interface Synth_STD_EQUALIZER : SynthModule;
+//interface Synth_RC : SynthModule;
+//interface Synth_MOOG_VCF : SynthModule;
+//interface Synth_PSCALE : SynthModule;
+//interface Synth_SEQUENCE : SynthModule;
+//interface Synth_PITCH_SHIFT : SynthModule;
+//interface Synth_PITCH_SHIFT_FFT : SynthModule;
+
+//// moved to effects
+//
+//interface Synth_STEREO_PITCH_SHIFT : StereoEffect;
+//interface Synth_STEREO_PITCH_SHIFT_FFT : StereoEffect;
+//interface Effect_WAVECAPTURE : StereoEffect;
+
+//// moved to effects
+//
+//interface Synth_STEREO_FIR_EQUALIZER : StereoEffect;
+//interface StereoFirEqualizerGuiFactory : GuiFactory;
+
+//interface Synth_PLAY_PAT : SynthModule;
+
+//// moved to synth
+//
+//enum SynthOscWaveForm;
+//interface Synth_OSC : SynthModule;
+
+//// moved to common
+//
+//interface EffectRackSlot;
+//interface EffectRackItemGui;
+
+//// moved to effects
+//
+//interface Synth_VOICE_REMOVAL : StereoEffect;
+//interface VoiceRemovalGuiFactory : GuiFactory;
+
+/*----------------------------------------------------------------------------
+ * everything below this line is obsolete, but provided to help with porting
+ * old structures
+ */
+interface Interface_MIDI_NOTE : SynthModule {
+ out audio stream frequency,velocity,pressed;
+};
+
+interface Synth_STRUCT_KILL : SynthModule {
+ in audio stream ready;
+};
+
+};
diff --git a/arts/modules/common/Makefile.am b/arts/modules/common/Makefile.am
new file mode 100644
index 00000000..6742af36
--- /dev/null
+++ b/arts/modules/common/Makefile.am
@@ -0,0 +1,61 @@
+
+INCLUDES = \
+ -I$(top_builddir)/arts/modules/common \
+ -I$(top_builddir)/arts/modules/synth \
+ -I$(top_srcdir)/arts/modules/synth \
+ -I$(top_builddir)/arts/modules \
+ -I$(top_srcdir)/arts/modules \
+ -I$(top_builddir)/arts/gui/common \
+ -I$(top_srcdir)/arts/gui/common \
+ -I$(top_builddir)/arts/midi \
+ -I$(top_srcdir)/arts/midi \
+ -I$(arts_includes) \
+ $(all_includes)
+
+lib_LTLIBRARIES = libartsmodulescommon.la
+
+libartsmodulescommon_la_SOURCES = artsmodulescommon.cc \
+ effectrackslot_impl.cc env_container_impl.cc \
+ env_context_impl.cc env_effectrackitem_impl.cc \
+ env_instrumentitem_impl.cc env_item_impl.cc \
+ env_mixeritem_impl.cc
+libartsmodulescommon_la_COMPILE_FIRST = artsmodulescommon.h
+
+libartsmodulescommon_la_LIBADD = \
+ $(top_builddir)/arts/gui/common/libartsgui_idl.la \
+ $(top_builddir)/arts/midi/libartsmidi_idl.la \
+ $(top_builddir)/arts/modules/synth/libartsmodulessynth.la \
+ -lartsflow -lartsflow_idl -lmcop $(LIB_KDECORE)
+
+libartsmodulescommon_la_LDFLAGS = $(all_libraries) -L$(arts_libraries) -no-undefined
+
+artsmodulescommon.cc artsmodulescommon.h artsmodulescommon.mcoptype artsmodulescommon.mcopclass: $(srcdir)/artsmodulescommon.idl $(MCOPIDL)
+ $(MCOPIDL) -t $(INCLUDES) $(srcdir)/artsmodulescommon.idl
+
+DISTCLEANFILES= artsmodulescommon.cc artsmodulescommon.h artsmodulescommon.mcop*
+
+artsincludedir = $(includedir)/arts
+artsinclude_HEADERS = artsmodulescommon.h artsmodulescommon.idl
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = artsmodulescommon.mcoptype artsmodulescommon.mcopclass
+
+mcopclassdir = $(libdir)/mcop/Arts
+mcopclass_DATA = \
+ mcopclass/EffectRackGuiFactory.mcopclass mcopclass/MixerGuiFactory.mcopclass
+
+mcopclassenvdir = $(libdir)/mcop/Arts/Environment
+mcopclassenv_DATA= \
+ mcopclass/InstrumentItem.mcopclass mcopclass/Container.mcopclass \
+ mcopclass/MixerItem.mcopclass mcopclass/EffectRackItem.mcopclass \
+ mcopclass/InstrumentItemGuiFactory.mcopclass
+
+artsmodulescommon.lo: ../../gui/common/artsgui.h ../../midi/artsmidi.h ../synth/artsmodulessynth.h
+effectrackslot_impl.lo: ../../gui/common/artsgui.h ../../midi/artsmidi.h ../synth/artsmodulessynth.h
+env_container_impl.lo: ../../gui/common/artsgui.h ../../midi/artsmidi.h ../synth/artsmodulessynth.h
+env_context_impl.lo: ../../gui/common/artsgui.h ../../midi/artsmidi.h ../synth/artsmodulessynth.h
+env_effectrackitem_impl.lo: ../../gui/common/artsgui.h ../../midi/artsmidi.h ../synth/artsmodulessynth.h
+env_instrumentitem_impl.lo: ../../gui/common/artsgui.h ../../midi/artsmidi.h ../synth/artsmodulessynth.h
+env_item_impl.lo: ../../gui/common/artsgui.h ../../midi/artsmidi.h ../synth/artsmodulessynth.h
+env_mixeritem_impl.lo: ../../gui/common/artsgui.h ../../midi/artsmidi.h ../synth/artsmodulessynth.h
+
diff --git a/arts/modules/common/artsmodulescommon.idl b/arts/modules/common/artsmodulescommon.idl
new file mode 100644
index 00000000..299ca51d
--- /dev/null
+++ b/arts/modules/common/artsmodulescommon.idl
@@ -0,0 +1,167 @@
+/*
+
+ Copyright (C) 2000-2001 Stefan Westerfeld
+ stefan@space.twc.de
+ 2001-2003 Matthias Kretz
+ kretz@kde.org
+ 2002-2003 Arnold Krille
+ arnold@arnoldarts.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+/*
+* DISCLAIMER: The interfaces in artsmodules.idl (and the derived .cc/.h files)
+* DO NOT GUARANTEE BINARY COMPATIBILITY YET.
+*
+* They are intended for developers. You shouldn't expect that applications in
+* binary form will be fully compatibile with further releases of these
+* interfaces.
+*/
+
+#include <artsgui.idl>
+#include <artsflow.idl>
+#include <artsmidi.idl>
+
+#include <artsmodulessynth.idl>
+
+module Arts {
+
+module Environment {
+ interface Context {
+ void addEntry(string name, object obj);
+ string lookupEntry(object obj);
+ void removeEntry(object obj);
+ };
+
+ interface Item;
+
+ interface Container {
+ attribute string dataDirectory;
+ attribute Context context;
+ readonly attribute sequence<Item> items;
+
+ sequence<string> saveToList();
+ void loadFromList(sequence<string> strlist);
+
+ void addItem(Item item);
+ Item createItem(string name);
+ void removeItem(Item item);
+ };
+
+ interface Item {
+ /**
+ * true if item resides inside a container
+ */
+ readonly attribute boolean active;
+
+ /**
+ * the container the item lives in
+ */
+ readonly attribute Container parent;
+
+ /**
+ * called by the container to insert/remove item from/to the
+ * environment
+ */
+ void setContainer(Container container);
+
+ /**
+ * called by the container to save the item
+ */
+ sequence<string> saveToList();
+
+ /**
+ * called by the container to restore the item
+ */
+ void loadFromList(sequence<string> strlist);
+ };
+
+ interface InstrumentItem : Item {
+ readonly attribute Arts::MidiPort port;
+ attribute string filename;
+ attribute string busname;
+ };
+
+ interface InstrumentItemGuiFactory : Arts::GuiFactory {
+ };
+
+ interface StereoEffectItem : Item {
+ attribute Arts::SynthModule effect;
+ attribute Arts::StereoEffectStack stack;
+ };
+
+ interface MixerChannel : Arts::StereoEffect {
+ attribute string name;
+ };
+
+ interface MixerItem : Item {
+ readonly attribute sequence<MixerChannel> channels;
+ attribute long channelCount;
+ attribute string name;
+ attribute string type;
+ };
+
+ interface EffectRackItem : Item {
+ Arts::StereoEffect createEffect( string type, string name );
+ void delEffect( long pos );
+ void routeToMaster( long pos, boolean tomaster );
+ readonly attribute sequence<Arts::StereoEffect> effects;
+ readonly attribute long effectCount;
+ attribute string name;
+ };
+
+};
+
+interface MixerItemGui {
+ /*writeonly*/ attribute boolean active;
+ /*writeonly*/ attribute long channelCount;
+ /*writeonly*/ attribute string type;
+ // builds a MixerItemGui for a specific MixerItem (call this exactly once)
+ Widget initialize(Environment::MixerItem item);
+};
+
+interface EffectRackSlot;
+
+interface EffectRackItemGui {
+ void removeSlot( EffectRackSlot slot );
+ void routeToMaster( EffectRackSlot slot, boolean tomaster );
+
+ attribute boolean active;
+ attribute string type;
+ /*writeonly*/ attribute boolean addeffect;
+
+ // builds a EffectRackItemGui for a specific EffectRackItem (call this exactly once)
+ Widget initialize(Environment::EffectRackItem item);
+};
+
+interface EffectRackSlot {
+ void constructor( Widget parent, Widget effect, EffectRackItemGui effectrackgui );
+ /*writeonly*/ attribute boolean removeslot;
+ /*writeonly*/ attribute boolean tomaster;
+};
+
+// creates: Environment::MixerItem, SimpleMixerChannel
+interface MixerGuiFactory : GuiFactory {
+};
+
+// creates: Environment::EffectRackItem
+interface EffectRackGuiFactory : GuiFactory {
+};
+
+};
+
diff --git a/arts/modules/common/effectrackslot_impl.cc b/arts/modules/common/effectrackslot_impl.cc
new file mode 100644
index 00000000..3210e83e
--- /dev/null
+++ b/arts/modules/common/effectrackslot_impl.cc
@@ -0,0 +1,116 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+// $Id$
+
+#include "artsmodulescommon.h"
+#include <artsgui.h>
+#include <debug.h>
+
+namespace Arts {
+class EffectRackSlot_impl : virtual public EffectRackSlot_skel
+{
+ private:
+ HBox _hbox;
+ VBox _buttonbox; // Buttons
+ Button _removeButton;
+ Button _masterButton;
+ HBox _frame;
+ Widget _effect;
+ Frame _spacer;
+ EffectRackItemGui _effectrackgui; //XXX: need a WeakReference here?
+
+ EffectRackSlot self() { return EffectRackSlot::_from_base( _copy() ); }
+
+ public:
+ EffectRackSlot_impl()
+ {
+ }
+
+ void constructor( Widget parent, Widget effect, EffectRackItemGui effectrackgui )
+ {
+ _effectrackgui = effectrackgui;
+
+ _hbox.parent( parent );
+ _hbox.margin( 0 );
+ _hbox.spacing( 0 );
+ _hbox.framestyle( Sunken | Panel );
+ _hbox.linewidth( 1 );
+ _hbox.show();
+
+ _buttonbox.parent( _hbox );
+ _buttonbox.margin( 0 );
+ _buttonbox.spacing( 0 );
+ _buttonbox.show();
+
+ _removeButton.parent( _buttonbox );
+ _removeButton.text( "x" );
+ _removeButton.hSizePolicy( spFixed );
+ _removeButton.width( 20 );
+ _removeButton.height( 20 );
+ connect( _removeButton, "clicked_changed", self(), "removeslot" );
+ _removeButton.show();
+
+ _masterButton.parent( _buttonbox );
+ _masterButton.text( "MM" );
+ _masterButton.toggle( true );
+ _masterButton.hSizePolicy( spFixed );
+ _masterButton.width( 20 );
+ _masterButton.height( 20 );
+ connect( _masterButton, "pressed_changed", self(), "tomaster" );
+ _masterButton.show();
+
+ _frame.parent( _hbox );
+ _frame.margin( 5 );
+ _frame.spacing( 0 );
+ _frame.framestyle( Raised | Panel );
+ _frame.linewidth( 2 );
+ _frame.midlinewidth( 2 );
+ _frame.hSizePolicy( spExpanding );
+ _frame.show();
+
+ _effect = effect;
+ _effect.parent( _frame );
+ _effect.show();
+
+ _spacer.parent( _frame );
+ _spacer.hSizePolicy( spExpanding );
+ _spacer.show();
+ }
+
+ bool removeslot() { return false; } //unused
+ void removeslot( bool clicked )
+ {
+ if( ! _removeButton.clicked() || ! clicked )
+ return;
+
+ // I need to be removed...
+ _effectrackgui.removeSlot( self() );
+ // I should be deleted by now
+ }
+
+ bool tomaster() { return false; } //unused
+ void tomaster( bool toggled )
+ {
+ _effectrackgui.routeToMaster( self(), toggled );
+ }
+};
+REGISTER_IMPLEMENTATION( EffectRackSlot_impl );
+}
+
+// vim: sw=4 ts=4
diff --git a/arts/modules/common/env_container_impl.cc b/arts/modules/common/env_container_impl.cc
new file mode 100644
index 00000000..0a6f87d7
--- /dev/null
+++ b/arts/modules/common/env_container_impl.cc
@@ -0,0 +1,136 @@
+#include "artsmodulescommon.h"
+#include "../runtime/sequenceutils.h"
+#include <debug.h>
+
+using namespace std;
+
+namespace Arts {
+namespace Environment {
+
+class Container_impl : virtual public Container_skel {
+protected:
+ string _dataDirectory;
+ Context _context;
+ vector<Item> _items;
+
+ Container self() { return Container::_from_base(_copy()); }
+public:
+ ~Container_impl()
+ {
+ // tell items we're going to leave before actually going away
+ clear();
+ }
+ string dataDirectory()
+ {
+ return _dataDirectory;
+ }
+ void dataDirectory(const string& newDataDirectory)
+ {
+ if(newDataDirectory != _dataDirectory)
+ {
+ _dataDirectory = newDataDirectory;
+ dataDirectory_changed(newDataDirectory);
+ }
+ }
+ Context context()
+ {
+ return _context;
+ }
+ void context(Context newContext)
+ {
+ _context = newContext;
+ }
+ vector<Item> *items()
+ {
+ return new vector<Item>(_items);
+ }
+ vector<string> *saveToList()
+ {
+ vector<string> *result = new vector<string>;
+
+ vector<Item>::iterator ii;
+ for(ii=_items.begin(); ii != _items.end(); ii++)
+ {
+ sqprintf(result,"item=%s",ii->_interfaceName().c_str());
+
+ vector<string> *itemresult = ii->saveToList();
+ addSubStringSeq(result,itemresult);
+ delete itemresult;
+ }
+ return result;
+ }
+
+ void clear()
+ {
+ /* FIXME: performance ;) */
+ while(!_items.empty())
+ removeItem(_items.front());
+ }
+
+ void loadFromList(const vector<string>& strlist)
+ {
+ string cmd,param;
+ unsigned long i;
+
+ clear();
+
+ for(i=0;i<strlist.size();i++)
+ {
+ if(parse_line(strlist[i],cmd,param)) // otherwise: empty or comment
+ {
+ if(cmd == "item")
+ {
+ Item item = createItem(param);
+ vector<string> *itemlist = getSubStringSeq(&strlist,i);
+
+ if(!item.isNull())
+ item.loadFromList(*itemlist);
+ else
+ {
+ // error handling
+ assert(false);
+ }
+ delete itemlist;
+ }
+ }
+ }
+ }
+
+ vector<Item>::iterator findItem(Item item)
+ {
+ vector<Item>::iterator i;
+ for(i = _items.begin(); i != _items.end(); i++)
+ if(i->_isEqual(item)) return i;
+
+ return _items.end();
+ }
+
+ void addItem(Item item)
+ {
+ vector<Item>::iterator i = findItem(item);
+ arts_return_if_fail(i == _items.end());
+
+ _items.push_back(item);
+ item.setContainer(self());
+ }
+
+ Item createItem(const string& name)
+ {
+ Item item = SubClass(name);
+ addItem(item);
+ return item;
+ }
+
+ void removeItem(Item item)
+ {
+ vector<Item>::iterator i = findItem(item);
+ arts_return_if_fail(i != _items.end());
+
+ _items.erase(i);
+ item.setContainer(Container::null());
+ }
+};
+REGISTER_IMPLEMENTATION(Container_impl);
+}
+}
+
diff --git a/arts/modules/common/env_context_impl.cc b/arts/modules/common/env_context_impl.cc
new file mode 100644
index 00000000..a9b19a50
--- /dev/null
+++ b/arts/modules/common/env_context_impl.cc
@@ -0,0 +1,72 @@
+#include "artsmodulescommon.h"
+#include <debug.h>
+
+using namespace std;
+
+namespace Arts {
+namespace Environment {
+
+class Context_impl : virtual public Context_skel {
+protected:
+ struct ContextEntry {
+ ContextEntry(const string& name, Object object)
+ : name(name), object(object)
+ {
+ }
+ ContextEntry(const ContextEntry& entry)
+ : name(entry.name), object(entry.object)
+ {
+ }
+ string name;
+ Object object;
+ };
+ list<ContextEntry> entries;
+
+ list<ContextEntry>::iterator findEntry(const string& name)
+ {
+ list<ContextEntry>::iterator i = entries.begin();
+ for(i = entries.begin(); i != entries.end(); i++)
+ if(i->name == name) return i;
+
+ return entries.end();
+ }
+
+ list<ContextEntry>::iterator findEntry(Object object)
+ {
+ list<ContextEntry>::iterator i = entries.begin();
+ for(i = entries.begin(); i != entries.end(); i++)
+ if(object._isEqual(i->object)) return i;
+
+ return entries.end();
+ }
+
+
+public:
+ void addEntry(const string& name, Object object)
+ {
+ arts_return_if_fail(findEntry(name) != entries.end());
+ entries.push_back(ContextEntry(name, object));
+ }
+
+ string lookupEntry(Object object)
+ {
+ list<ContextEntry>::iterator i = findEntry(object);
+
+ if(i == entries.end())
+ return "";
+ else
+ return i->name;
+ }
+
+ void removeEntry(Object object)
+ {
+ list<ContextEntry>::iterator i = findEntry(object);
+
+ arts_return_if_fail(i != entries.end());
+ entries.erase(i);
+ }
+};
+REGISTER_IMPLEMENTATION(Context_impl);
+}
+}
+
diff --git a/arts/modules/common/env_effectrackitem_impl.cc b/arts/modules/common/env_effectrackitem_impl.cc
new file mode 100644
index 00000000..7872ce42
--- /dev/null
+++ b/arts/modules/common/env_effectrackitem_impl.cc
@@ -0,0 +1,400 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002-2003 Matthias Kretz <kretz@kde.org>
+ 2002 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+// $Id$
+
+#include "artsmodulescommon.h"
+#include <debug.h>
+#include "env_item_impl.h"
+#include <connect.h>
+#include <stdio.h>
+#include <vector>
+#include <map>
+
+
+// Wether you are able to edit the name of the effectrack with an ugly LineInput or not.
+//#define EFFECTRACK_NAME
+// We should implement something like a ConfigWidget or at least a KLineInputBox or something...
+
+namespace Arts {
+namespace Environment {
+
+class EffectRackItem_impl : virtual public EffectRackItem_skel,
+ virtual public Item_impl
+{
+protected:
+ std::string _name;
+ AudioManagerClient _amClient;
+
+ struct RackWiring {
+ RackWiring( const std::string & type, AudioManagerClient _amClient )
+ : routedtomaster( false )
+ , amClient( _amClient )
+ {
+ effect = SubClass( type );
+
+ connect( input, effect );
+ connect( effect, output );
+ }
+
+ inline void setName( const std::string & efname )
+ {
+ name = efname;
+ input.busname( efname );
+ if( ! routedtomaster )
+ {
+ output.title( efname );
+ output.autoRestoreID( efname );
+ }
+ }
+
+ inline void start()
+ {
+ input.start();
+ effect.start();
+ output.start();
+ }
+
+ inline void stop()
+ {
+ input.stop();
+ effect.stop();
+ output.stop();
+ }
+
+ inline void master( bool tomaster )
+ {
+ routedtomaster = tomaster;
+
+ output.stop();
+ output = tomaster ? Synth_AMAN_PLAY( amClient ) : Synth_AMAN_PLAY();
+ connect( effect, output );
+ if( ! tomaster )
+ {
+ output.title( name );
+ output.autoRestoreID( name );
+ }
+ output.start();
+ }
+
+ bool routedtomaster;
+ std::string name;
+ std::string effectName;
+ Synth_BUS_DOWNLINK input;
+ Arts::StereoEffect effect;
+ Synth_AMAN_PLAY output;
+ AudioManagerClient amClient;
+ };
+ std::vector<RackWiring> _wirings;
+
+public:
+ EffectRackItem_impl()
+ : _name( "effect rack" )
+ , _amClient( amPlay, _name + " Master", "effectrack_" + _name )
+ {
+ // TODO: check if there's another effect rack with the same name already - if so prefix with 2./3./4.
+ }
+
+ // readonly attribute sequence<Arts::StereoEffect> effects;
+ std::vector<Arts::StereoEffect> *effects()
+ {
+ std::vector<Arts::StereoEffect> * effects = new std::vector<Arts::StereoEffect>;
+ for( std::vector<RackWiring>::iterator it = _wirings.begin(); it != _wirings.end(); ++it )
+ effects->push_back( it->effect );
+ return effects;
+ }
+
+ // attribute long effectCount;
+ long effectCount() { return _wirings.size(); }
+
+ // attribute string name;
+ void name(const std::string& newName) {
+ if(newName != _name)
+ {
+ _name = newName;
+ _amClient.title( _name + " Master" );
+ _amClient.autoRestoreID( "effectrack_" + _name );
+ for( unsigned int i = 0; i < _wirings.size(); i++ )
+ _wirings[i].setName( effectName( i, _wirings[ i ].effectName ) );
+ name_changed( newName );
+ }
+ }
+ std::string name() { return _name; }
+
+ void loadFromList(const std::vector<std::string>& /*list*/)
+ {
+ }
+
+ std::vector<std::string> *saveToList()
+ {
+ std::vector<std::string> *result = new std::vector<std::string>;
+ return result;
+ }
+
+ std::string effectName( int n, const std::string & en )
+ {
+ char * efname = new char[ _name.length() + en.length() + 128 ];
+ sprintf( efname, "%s%02d (%s)", _name.c_str(), n, en.c_str() );
+ return efname;
+ }
+
+ Arts::StereoEffect createEffect( const std::string & type, const std::string & name )
+ {
+ RackWiring wiring( type, _amClient );
+ wiring.setName( effectName( _wirings.size() + 1, name ) );
+ wiring.start();
+ _wirings.push_back( wiring );
+ return wiring.effect;
+ }
+
+ void delEffect( long pos )
+ {
+ _wirings[ pos ].stop();
+ _wirings.erase( _wirings.begin() + pos );
+ for( unsigned int i = pos; i < _wirings.size(); ++i )
+ _wirings[ i ].setName( effectName( i, _wirings[ i ].effectName ) );
+ }
+
+ void routeToMaster( long pos, bool tomaster )
+ {
+ _wirings[ pos ].master( tomaster );
+ }
+};
+REGISTER_IMPLEMENTATION(EffectRackItem_impl);
+}
+
+using namespace Environment;
+
+typedef WeakReference<VBox> VBox_wref;
+
+class EffectRackItemGui_impl : virtual public EffectRackItemGui_skel {
+private:
+ bool _active;
+ long _effectCount;
+ std::string _type;
+ EffectRackItem _effectRack;
+
+ /* widgets */
+ VBox_wref _widget;
+ HBox hbox;
+ VBox effect_vbox;
+#ifdef EFFECTRACK_NAME
+ LineEdit name;
+#endif
+ ComboBox typebox;
+ Button addbutton;
+ GenericGuiFactory guiFactory;
+ std::vector<EffectRackSlot> _slots;
+ std::map<std::string, std::string> typeforname;
+ std::map<std::string, std::string> namefortype;
+
+public:
+ EffectRackItemGui self() { return EffectRackItemGui::_from_base(_copy()); }
+
+ void redoGui()
+ {
+ VBox vbox = _widget;
+ if(vbox.isNull())
+ arts_warning("update with vbox null");
+ if(_effectRack.isNull())
+ arts_warning("update with _effectRack null");
+ if(!_effectRack.isNull() && !vbox.isNull())
+ {
+ vbox.spacing( 0 );
+ vbox.margin( 10 );
+ hbox = HBox( vbox );
+ hbox.spacing( 5 );
+ hbox.margin( 0 );
+
+#ifdef EFFECTRACK_NAME
+ name = LineEdit();
+ name.caption("name");
+ name.text(_effectRack.name());
+ name.parent(hbox);
+ connect(name,"text_changed", _effectRack, "name");
+#endif
+
+ std::vector<std::string> choices;
+ TraderQuery query;
+ query.supports( "Interface", "Arts::StereoEffect" );
+ query.supports( "Features", "RackGUI" );
+ std::vector<TraderOffer> *queryResults = query.query();
+ for(std::vector<TraderOffer>::iterator it = queryResults->begin(); it != queryResults->end(); ++it)
+ {
+ std::vector<std::string> * names = it->getProperty( "Name" );
+ std::string name = names->empty() ? it->interfaceName() : names->front();
+ delete names;
+ choices.push_back( name );
+ typeforname[ name ] = it->interfaceName();
+ namefortype[ it->interfaceName() ] = name;
+ }
+ delete queryResults;
+ typebox = ComboBox();
+ typebox.choices(choices);
+ typebox.value(_type);
+ typebox.parent(hbox);
+ connect(typebox,"value_changed", self(), "type");
+
+ addbutton = Button( "add", hbox );
+ connect( addbutton, "clicked_changed", self(), "addeffect" );
+
+ effect_vbox = VBox( vbox );
+ effect_vbox.margin( 0 );
+ effect_vbox.spacing( 5 );
+ effect_vbox.show();
+
+ Frame spacer;
+ spacer.parent( effect_vbox );
+ spacer.vSizePolicy( spExpanding );
+ spacer.show();
+ effect_vbox._addChild( spacer, "spacer" );
+
+ _slots.clear();
+
+ // add Arts::StereoEffect widgets
+ std::vector<Arts::StereoEffect> * effects = _effectRack.effects();
+ for( std::vector<Arts::StereoEffect>::iterator it = effects->begin(); it != effects->end(); ++it )
+ createEffectGui( *it );
+ delete effects;
+ }
+ else
+ {
+ /* FIXME: maybe insert a "dead" label here */
+ if(!vbox.isNull())
+ vbox.show();
+ effect_vbox = VBox::null();
+ hbox = HBox::null();
+ // name = LineEdit::null();
+ typebox = ComboBox::null();
+ _slots.clear();
+ }
+ }
+
+ void createEffectGui( Arts::StereoEffect effect )
+ {
+ Widget w = guiFactory.createGui( effect );
+ if( ! w.isNull() )
+ {
+ // insert effect GUI into the "Rack"
+ EffectRackSlot slot( effect_vbox, w, self() );
+ _slots.push_back( slot );
+ }
+ }
+
+ void removeSlot( EffectRackSlot slot )
+ {
+ unsigned int i;
+ for( i = 0; i < _slots.size() && ! _slots[ i ]._isEqual( slot ) ; ++i );
+ if( i < _slots.size() )
+ {
+ _slots.erase( _slots.begin() + i );
+ _effectRack.delEffect( i );
+ }
+ else
+ arts_warning( "WARNING: Trying to remove an unknown slot" );
+ }
+
+ void routeToMaster( EffectRackSlot slot, bool tomaster )
+ {
+ unsigned int i;
+ for( i = 0; i < _slots.size() && ! _slots[ i ]._isEqual( slot ) ; ++i );
+ if( i < _slots.size() )
+ _effectRack.routeToMaster( i, tomaster );
+ else
+ arts_warning( "WARNING: Trying to route an unknown slot" );
+ }
+
+ bool active() { return _active; }
+ void active(bool newActive)
+ {
+ if(newActive != _active)
+ {
+ _active = newActive;
+ if(!newActive)
+ _effectRack = EffectRackItem::null();
+ redoGui();
+ }
+ }
+
+ std::string type()
+ {
+ return _type;
+ }
+ void type(const std::string& t)
+ {
+ _type = typeforname[ t ];
+ }
+
+ bool addeffect() { return false; } //unused
+ void addeffect( bool clicked )
+ {
+ if( ! addbutton.clicked() || ! clicked )
+ return;
+
+ Arts::StereoEffect effect = _effectRack.createEffect( _type, namefortype[ _type ] );
+ createEffectGui( effect );
+ }
+
+ Widget initialize(EffectRackItem item)
+ {
+ VBox vbox;
+ vbox._addChild(self(),"the_gui_updating_widget");
+
+ _widget = vbox;
+ _effectRack = item;
+ _active = item.active();
+ _type = "Arts::Synth_VOICE_REMOVAL";
+ _effectCount = item.effectCount();
+
+ if(!_effectRack.isNull())
+ {
+ connect(_effectRack, "active_changed", self(), "active");
+ }
+ redoGui();
+
+ return vbox;
+ }
+};
+
+REGISTER_IMPLEMENTATION(EffectRackItemGui_impl);
+
+class EffectRackGuiFactory_impl : virtual public EffectRackGuiFactory_skel
+{
+public:
+ Widget createGui(Object object)
+ {
+ arts_return_val_if_fail(!object.isNull(), Arts::Widget::null());
+
+ std::string iface = object._interfaceName();
+ arts_return_val_if_fail(iface == "Arts::Environment::EffectRackItem",
+ Arts::Widget::null());
+ if(iface == "Arts::Environment::EffectRackItem")
+ {
+ EffectRackItem effectRack = DynamicCast(object);
+ arts_return_val_if_fail(!effectRack.isNull(), Arts::Widget::null());
+
+ EffectRackItemGui gui;
+ return gui.initialize(effectRack);
+ }
+ return Arts::Widget::null();
+ }
+};
+REGISTER_IMPLEMENTATION(EffectRackGuiFactory_impl);
+}
+// vim:ts=4:sw=4
diff --git a/arts/modules/common/env_instrumentitem_impl.cc b/arts/modules/common/env_instrumentitem_impl.cc
new file mode 100644
index 00000000..17959ca0
--- /dev/null
+++ b/arts/modules/common/env_instrumentitem_impl.cc
@@ -0,0 +1,113 @@
+#include <vector>
+#include "artsmodulescommon.h"
+#include "debug.h"
+#include "env_item_impl.h"
+#include "connect.h"
+#include "../runtime/sequenceutils.h"
+
+namespace Arts {
+namespace Environment {
+
+class InstrumentItem_impl : virtual public InstrumentItem_skel,
+ virtual public Item_impl
+{
+protected:
+ Synth_MIDI_TEST instrument;
+ bool running;
+
+public:
+ InstrumentItem_impl() : running(false)
+ {
+ }
+ ~InstrumentItem_impl()
+ {
+ /* this will allow freeing the instrument */
+ if(running)
+ instrument.stop();
+ }
+ void filename(const std::string& newFilename)
+ {
+ if(newFilename != instrument.filename())
+ {
+ instrument.filename(newFilename);
+ filename_changed(newFilename);
+
+ if(!running) {
+ instrument.start();
+ running = true;
+ }
+ }
+ }
+ std::string filename()
+ {
+ return instrument.filename();
+ }
+ void busname(const std::string& newBusname)
+ {
+ if(newBusname != instrument.busname())
+ {
+ instrument.busname(newBusname);
+ busname_changed(newBusname);
+ }
+ }
+ std::string busname()
+ {
+ return instrument.busname();
+ }
+ MidiPort port()
+ {
+ return instrument;
+ }
+ void loadFromList(const std::vector<std::string>& list)
+ {
+ unsigned long i;
+ std::string cmd,param;
+ for(i=0;i<list.size();i++)
+ {
+ if(parse_line(list[i],cmd,param)) // otherwise: empty or comment
+ {
+ if(cmd == "filename") {
+ filename(param.c_str());
+ }
+ }
+ }
+ }
+ std::vector<std::string> *saveToList()
+ {
+ std::vector<std::string> *result = new std::vector<std::string>;
+ sqprintf(result,"filename=%s",filename().c_str());
+ return result;
+ }
+};
+REGISTER_IMPLEMENTATION(InstrumentItem_impl);
+
+class InstrumentItemGuiFactory_impl
+ : virtual public InstrumentItemGuiFactory_skel
+{
+public:
+ Widget createGui(Object object)
+ {
+ arts_return_val_if_fail(!object.isNull(), Arts::Widget::null());
+
+ InstrumentItem instrument = DynamicCast(object);
+ arts_return_val_if_fail(!instrument.isNull(), Arts::Widget::null());
+
+ Widget panel;
+ panel.width(150); panel.height(60); panel.show();
+
+ LineEdit edit;
+ edit.x(20); edit.y(10); edit.width(120); edit.height(40);
+ edit.text(instrument.filename());
+ edit.parent(panel);
+ edit.show();
+ connect(edit,"text_changed", instrument, "filename");
+ panel._addChild(edit,"editWidget");
+
+ return panel;
+ }
+};
+
+REGISTER_IMPLEMENTATION(InstrumentItemGuiFactory_impl);
+
+}
+}
diff --git a/arts/modules/common/env_item_impl.cc b/arts/modules/common/env_item_impl.cc
new file mode 100644
index 00000000..6bc960d8
--- /dev/null
+++ b/arts/modules/common/env_item_impl.cc
@@ -0,0 +1,48 @@
+#include "artsmodulescommon.h"
+#include "debug.h"
+#include "env_item_impl.h"
+
+using namespace Arts;
+using namespace std;
+
+Environment::Item_impl::Item_impl()
+ : _active(false)
+{
+}
+
+Environment::Item_impl::~Item_impl()
+{
+ // Items can't be deleted while they are still inside a Container
+ arts_assert(_active == false);
+}
+
+Environment::Container Environment::Item_impl::parent()
+{
+ Container p = _parent;
+ return p;
+}
+
+void Environment::Item_impl::setContainer(Environment::Container container)
+{
+ if(container.isNull()) // remove from container
+ {
+ arts_return_if_fail(_active == true);
+
+ _parent = container;
+ _active = false;
+ }
+ else // add to container
+ {
+ Container p = _parent;
+ arts_return_if_fail(p.isNull() && _active == false);
+
+ _parent = container;
+ _active = true;
+ }
+ active_changed(_active);
+}
+
+bool Environment::Item_impl::active()
+{
+ return _active;
+}
diff --git a/arts/modules/common/env_item_impl.h b/arts/modules/common/env_item_impl.h
new file mode 100644
index 00000000..dbdca1f9
--- /dev/null
+++ b/arts/modules/common/env_item_impl.h
@@ -0,0 +1,28 @@
+
+#ifndef env_item_impl_h
+#define env_item_impl_h
+
+#include "artsmodulescommon.h"
+#include "weakreference.h"
+
+namespace Arts {
+namespace Environment {
+typedef WeakReference<Container> Container_wref;
+class Item_impl : virtual public Item_skel {
+protected:
+ Container_wref _parent;
+ bool _active;
+
+public:
+ Item_impl();
+ ~Item_impl();
+
+ bool active();
+ Container parent();
+ void setContainer(Container container);
+};
+}
+}
+
+
+#endif
diff --git a/arts/modules/common/env_mixeritem_impl.cc b/arts/modules/common/env_mixeritem_impl.cc
new file mode 100644
index 00000000..632d8187
--- /dev/null
+++ b/arts/modules/common/env_mixeritem_impl.cc
@@ -0,0 +1,368 @@
+#include "artsmodulescommon.h"
+#include "debug.h"
+#include "env_item_impl.h"
+#include "connect.h"
+#include "../runtime/sequenceutils.h"
+#include <stdio.h>
+#include <kglobal.h>
+#include <klocale.h>
+
+#include <vector>
+
+namespace Arts {
+namespace Environment {
+
+class MixerItem_impl : virtual public MixerItem_skel,
+ virtual public Item_impl
+{
+protected:
+ std::vector<Synth_BUS_DOWNLINK> _inputs;
+ std::vector<MixerChannel> _channels;
+ std::vector<Synth_AMAN_PLAY> _outputs;
+ std::string _name;
+ std::string _type;
+ AudioManagerClient amClient;
+
+public:
+ MixerItem_impl()
+ : _name("mixer"), _type("Arts::SimpleMixerChannel"),
+ amClient(amPlay, "Mixer (mixer)","mixer_mixer")
+ {
+ }
+ // readonly attribute sequence<MixerChannel> channels;
+ std::vector<MixerChannel> *channels() {
+ return new std::vector<MixerChannel>(_channels);
+ }
+ // attribute long channelCount;
+ void channelCount(long newChannelCount)
+ {
+ if((unsigned long)newChannelCount != _channels.size())
+ {
+ while(_channels.size() < (unsigned long)newChannelCount) addChannel();
+ while(_channels.size() > (unsigned long)newChannelCount) delChannel();
+ channelCount_changed(newChannelCount);
+ }
+ }
+ long channelCount() { return _channels.size(); }
+ // attribute string name;
+ void name(const std::string& newName) {
+ if(newName != _name)
+ {
+ _name = newName;
+ amClient.title(i18n("Mixer (\"%1\")").arg(QString::fromUtf8(_name.c_str())).utf8().data());
+ amClient.autoRestoreID("mixer_"+_name);
+ for(unsigned int i = 0; i < _inputs.size(); i++)
+ _inputs[i].busname(channelName(i));
+ name_changed(newName);
+ }
+ }
+ std::string name() { return _name; }
+ // attribute string type;
+ void type(const std::string& newType) {
+ if(newType != _type)
+ {
+ _type = newType;
+ type_changed(newType);
+ }
+ }
+ std::string type() { return _type; }
+ void loadFromList(const std::vector<std::string>& /*list*/)
+ {
+ /*
+ unsigned long i;
+ std::string cmd,param;
+ for(i=0;i<list.size();i++)
+ {
+ if(parse_line(list[i],cmd,param)) // otherwise: empty or comment
+ {
+ if(cmd == "filename") {
+ filename(param.c_str());
+ }
+ }
+ }
+ */
+ }
+ std::vector<std::string> *saveToList()
+ {
+ std::vector<std::string> *result = new std::vector<std::string>;
+ /*
+ sqprintf(result,"filename=%s",filename().c_str());
+ */
+ return result;
+ }
+ std::string channelName(int n)
+ {
+ char chname[1024];
+ sprintf(chname, "%s%02d", _name.c_str(), n);
+ return chname;
+ }
+ void addChannel()
+ {
+ Synth_BUS_DOWNLINK input;
+ MixerChannel channel = SubClass(_type);
+ Synth_AMAN_PLAY output(amClient);
+
+ std::string chname = channelName(_channels.size()+1);
+ input.busname(chname);
+ channel.name(chname);
+
+ input.start();
+ channel.start();
+ output.start();
+
+ connect(input, channel);
+ connect(channel, output);
+
+ _inputs.push_back(input);
+ _channels.push_back(channel);
+ _outputs.push_back(output);
+ }
+
+ void delChannel()
+ {
+ unsigned long cc = _channels.size()-1;
+
+ _inputs.resize(cc);
+ _channels.resize(cc);
+ _outputs.resize(cc);
+ }
+};
+REGISTER_IMPLEMENTATION(MixerItem_impl);
+}
+
+using namespace Environment;
+
+typedef WeakReference<VBox> VBox_wref;
+
+class MixerItemGui_impl : virtual public MixerItemGui_skel {
+private:
+ bool _active;
+ long _channelCount;
+ std::string _type;
+ MixerItem _item;
+
+ /* widgets */
+ VBox_wref _widget;
+ HBox hbox, channel_hbox;
+ SpinBox spinbox;
+ LineEdit name;
+ ComboBox typebox;
+ GenericGuiFactory guiFactory;
+ std::vector<Widget> channelWidgets;
+
+public:
+ MixerItemGui self() { return MixerItemGui::_from_base(_copy()); }
+
+ void updateChannelGui()
+ {
+ if(channelWidgets.size() > (unsigned)_item.channelCount())
+ channelWidgets.resize(_item.channelCount());
+ else
+ {
+ std::vector<MixerChannel> *channels = _item.channels();
+ for(unsigned int i = channelWidgets.size(); i < channels->size(); ++i)
+ {
+ Widget w = guiFactory.createGui((*channels)[i]);
+ if(!w.isNull())
+ {
+ w.parent(channel_hbox);
+ w.show();
+ channelWidgets.push_back(w);
+ }
+ }
+ }
+ }
+
+ void redoGui()
+ {
+ VBox vbox = _widget;
+ if(vbox.isNull())
+ arts_warning("update with vbox null");
+ if(_item.isNull())
+ arts_warning("update with _item null");
+ if(!_item.isNull() && !vbox.isNull())
+ {
+ hbox = HBox();
+ hbox.parent(vbox);
+ hbox.show();
+
+ spinbox = SpinBox();
+ spinbox.caption(i18n("channels").utf8().data());
+ spinbox.min(0); spinbox.max(32);
+ spinbox.value(_item.channelCount());
+ spinbox.parent(hbox);
+ spinbox.show();
+ connect(spinbox,"value_changed", _item, "channelCount");
+
+ name = LineEdit();
+ name.caption(i18n("name").utf8().data());
+ name.text(_item.name());
+ name.parent(hbox);
+ name.show();
+ connect(name,"text_changed", _item, "name");
+
+ typebox = ComboBox();
+ typebox.caption(i18n("type").utf8().data());
+ std::vector<std::string> choices;
+ TraderQuery query;
+ query.supports("Interface", "Arts::Environment::MixerChannel");
+ std::vector<TraderOffer> *queryResults = query.query();
+ for(std::vector<TraderOffer>::iterator it = queryResults->begin(); it != queryResults->end(); ++it)
+ choices.push_back(it->interfaceName());
+ delete queryResults;
+ typebox.choices(choices);
+ typebox.value(_type);
+ typebox.parent(hbox);
+ typebox.show();
+ connect(typebox,"value_changed", _item, "type");
+
+ channel_hbox = HBox();
+ channel_hbox.parent(vbox);
+ channel_hbox.show();
+
+ channelWidgets.clear();
+ updateChannelGui();
+
+ vbox.show();
+ }
+ else
+ {
+ /* FIXME: maybe insert a "dead" label here */
+ if(!vbox.isNull())
+ vbox.show();
+ channel_hbox = HBox::null();
+ hbox = HBox::null();
+ spinbox = SpinBox::null();
+ name = LineEdit::null();
+ typebox = ComboBox::null();
+ channelWidgets.clear();
+ }
+ }
+
+ bool active() { return _active; }
+ void active(bool newActive)
+ {
+ if(newActive != _active)
+ {
+ _active = newActive;
+ if(!newActive)
+ _item = MixerItem::null();
+ redoGui();
+ }
+ }
+ long channelCount() { return _channelCount; }
+ void channelCount(long ch)
+ {
+ if(_channelCount != ch)
+ {
+ _channelCount = ch;
+ updateChannelGui();
+ }
+ }
+ std::string type() {
+ return _type;
+ }
+ void type(const std::string& t)
+ {
+ if(_type != t)
+ {
+ _type = t;
+ //redoGui();
+ }
+ }
+ Widget initialize(MixerItem item)
+ {
+ KGlobal::locale()->insertCatalogue( "artsmodules" );
+ VBox vbox;
+ vbox._addChild(self(),"the_gui_updating_widget");
+
+ _widget = vbox;
+ _item = item;
+ _active = item.active();
+ _type = item.type();
+ _channelCount = item.channelCount();
+
+ if(!_item.isNull())
+ {
+ connect(_item, "channelCount_changed", self(), "channelCount");
+ connect(_item, "type_changed", self(), "type");
+ connect(_item, "active_changed", self(), "active");
+ }
+ redoGui();
+
+ return vbox;
+ }
+};
+
+REGISTER_IMPLEMENTATION(MixerItemGui_impl);
+
+class MixerGuiFactory_impl : virtual public MixerGuiFactory_skel
+{
+public:
+ Widget createGui(Object object)
+ {
+ KGlobal::locale()->insertCatalogue( "artsmodules" );
+ arts_return_val_if_fail(!object.isNull(), Arts::Widget::null());
+
+ std::string iface = object._interfaceName();
+ arts_return_val_if_fail(iface == "Arts::Environment::MixerItem",
+ Arts::Widget::null());
+ if(iface == "Arts::Environment::MixerItem")
+ {
+ MixerItem mixerItem = DynamicCast(object);
+ arts_return_val_if_fail(!mixerItem.isNull(), Arts::Widget::null());
+
+#if 0
+ VBox vbox;
+ vbox.show();
+ vbox.width(330); vbox.height(500);
+
+ HBox hbox;
+ hbox.show();
+ hbox.width(330); hbox.height(50);
+ hbox.parent(vbox);
+ vbox._addChild(hbox,"hbox");
+
+ SpinBox spinbox;
+ spinbox.caption(i18n("channels").utf8().data());
+ spinbox.min(0); spinbox.max(32);
+ spinbox.value(mixerItem.channelCount());
+ spinbox.parent(hbox);
+ spinbox.show();
+ connect(spinbox,"value_changed", mixerItem, "channelCount");
+ hbox._addChild(spinbox,"channelsWidget");
+
+ LineEdit name;
+ name.caption(i18n("name").utf8().data());
+ name.caption(mixerItem.name());
+ name.parent(hbox);
+ name.show();
+ connect(name,"caption_changed", mixerItem, "name");
+ hbox._addChild(name,"nameWidget");
+
+ HBox channel_hbox;
+ channel_hbox.show();
+ channel_hbox.width(330); hbox.height(450);
+ channel_hbox.parent(vbox);
+ vbox._addChild(channel_hbox,"channel_hbox");
+
+ GenericGuiFactory gf;
+
+ std::vector<MixerChannel> *channels = mixerItem.channels();
+ std::vector<MixerChannel>::iterator i;
+ for(i = channels->begin(); i != channels->end(); i++)
+ {
+ Widget w = gf.createGui(*i);
+ w.parent(channel_hbox);
+ channel_hbox._addChild(w,"channel");
+ }
+#endif
+ MixerItemGui gui;
+ return gui.initialize(mixerItem);
+ }
+ return Arts::Widget::null();
+ }
+};
+REGISTER_IMPLEMENTATION(MixerGuiFactory_impl);
+}
+// vim:ts=4:sw=4
diff --git a/arts/modules/common/mcopclass/Container.mcopclass b/arts/modules/common/mcopclass/Container.mcopclass
new file mode 100644
index 00000000..174b8d8d
--- /dev/null
+++ b/arts/modules/common/mcopclass/Container.mcopclass
@@ -0,0 +1,3 @@
+Interface=Arts::Environment::Container,Arts::Object
+Language=C++
+Library=libartsmodulescommon.la
diff --git a/arts/modules/common/mcopclass/EffectRackGuiFactory.mcopclass b/arts/modules/common/mcopclass/EffectRackGuiFactory.mcopclass
new file mode 100644
index 00000000..7ee37168
--- /dev/null
+++ b/arts/modules/common/mcopclass/EffectRackGuiFactory.mcopclass
@@ -0,0 +1,4 @@
+Interface=Arts::EffectRackGuiFactory,Arts::GuiFactory,Arts::Object
+CanCreate=Arts::Environment::EffectRackItem
+Language=C++
+Library=libartsmodulescommon.la
diff --git a/arts/modules/common/mcopclass/EffectRackItem.mcopclass b/arts/modules/common/mcopclass/EffectRackItem.mcopclass
new file mode 100644
index 00000000..700f7b7f
--- /dev/null
+++ b/arts/modules/common/mcopclass/EffectRackItem.mcopclass
@@ -0,0 +1,3 @@
+Interface=Arts::Environment::EffectRackItem,Arts::Environment::Item,Arts::Object
+Language=C++
+Library=libartsmodulescommon.la
diff --git a/arts/modules/common/mcopclass/InstrumentItem.mcopclass b/arts/modules/common/mcopclass/InstrumentItem.mcopclass
new file mode 100644
index 00000000..8d525f8e
--- /dev/null
+++ b/arts/modules/common/mcopclass/InstrumentItem.mcopclass
@@ -0,0 +1,3 @@
+Interface=Arts::Environment::InstrumentItem,Arts::Environment::Item,Arts::Object
+Language=C++
+Library=libartsmodulescommon.la
diff --git a/arts/modules/common/mcopclass/InstrumentItemGuiFactory.mcopclass b/arts/modules/common/mcopclass/InstrumentItemGuiFactory.mcopclass
new file mode 100644
index 00000000..f4ee305b
--- /dev/null
+++ b/arts/modules/common/mcopclass/InstrumentItemGuiFactory.mcopclass
@@ -0,0 +1,4 @@
+Interface=Arts::Environment::InstrumentItemGuiFactory,Arts::GuiFactory,Arts::Object
+CanCreate=Arts::Environment::InstrumentItem
+Language=C++
+Library=libartsmodulescommon.la
diff --git a/arts/modules/common/mcopclass/MixerGuiFactory.mcopclass b/arts/modules/common/mcopclass/MixerGuiFactory.mcopclass
new file mode 100644
index 00000000..447e1d47
--- /dev/null
+++ b/arts/modules/common/mcopclass/MixerGuiFactory.mcopclass
@@ -0,0 +1,4 @@
+Interface=Arts::MixerGuiFactory,Arts::GuiFactory,Arts::Object
+CanCreate=Arts::Environment::MixerItem
+Language=C++
+Library=libartsmodulescommon.la
diff --git a/arts/modules/common/mcopclass/MixerItem.mcopclass b/arts/modules/common/mcopclass/MixerItem.mcopclass
new file mode 100644
index 00000000..3ebe4a01
--- /dev/null
+++ b/arts/modules/common/mcopclass/MixerItem.mcopclass
@@ -0,0 +1,3 @@
+Interface=Arts::Environment::MixerItem,Arts::Environment::Item,Arts::Object
+Language=C++
+Library=libartsmodulescommon.la
diff --git a/arts/modules/effects/Makefile.am b/arts/modules/effects/Makefile.am
new file mode 100644
index 00000000..d5d54aad
--- /dev/null
+++ b/arts/modules/effects/Makefile.am
@@ -0,0 +1,70 @@
+
+SUBDIRS = freeverb
+
+INCLUDES = \
+ -I$(top_builddir)/arts/modules/effects \
+ -I$(top_srcdir)/arts/modules/effects \
+ -I$(top_builddir)/arts/modules/synth \
+ -I$(top_srcdir)/arts/modules/synth \
+ -I$(top_builddir)/arts/modules/common \
+ -I$(top_srcdir)/arts/modules/common \
+ -I$(top_builddir)/arts/modules \
+ -I$(top_srcdir)/arts/modules \
+ -I$(top_builddir)/arts/gui/common \
+ -I$(top_srcdir)/arts/gui/common \
+ -I$(top_srcdir)/arts/gui/kde \
+ -I$(top_builddir)/arts/midi \
+ -I$(top_srcdir)/arts/midi \
+ -I$(arts_includes) \
+ $(all_includes)
+
+lib_LTLIBRARIES = libartsmoduleseffects.la
+
+libartsmoduleseffects_la_SOURCES = artsmoduleseffects.cc \
+ fivebandmonocomplexeq_impl.cc \
+ monostereoconversion_impl.cc \
+ synth_stereo_pitch_shift_impl.cc synth_stereo_pitch_shift_fft_impl.cc \
+ synth_voice_removal_impl.cc voiceremovalguifactory_impl.cc \
+ synth_stereo_compressor_impl.cc stereocompressorguifactory_impl.cc \
+ synth_stereo_fir_equalizer_impl.cc \
+ synth_freeverb_impl.cc freeverbguifactory_impl.cc \
+ effect_wavecapture_impl.cc \
+ kstereovolumecontrolgui_impl.cpp stereovolumecontrolguifactory_impl.cpp
+libartsmoduleseffects_la_COMPILE_FIRST = ../../gui/common/artsgui.h \
+ ../common/artsmodulescommon.h ../../midi/artsmidi.h ../synth/artsmodulessynth.h \
+ artsmoduleseffects.h
+libartsmoduleseffects_la_LIBADD = \
+ $(top_builddir)/arts/modules/effects/freeverb/libfreeverb.la \
+ $(top_builddir)/arts/gui/common/libartsgui_idl.la \
+ $(top_builddir)/arts/gui/kde/libartsgui_kde.la \
+ $(top_builddir)/arts/modules/common/libartsmodulescommon.la \
+ -lartsflow -lartsflow_idl -lmcop
+
+libartsmoduleseffects_la_LDFLAGS = $(all_libraries) -L$(arts_libraries) -no-undefined
+
+METASOURCES=AUTO
+
+artsmoduleseffects.cc artsmoduleseffects.h artsmoduleseffects.mcoptype artsmoduleseffects.mcopclass: $(srcdir)/artsmoduleseffects.idl $(MCOPIDL)
+ $(MCOPIDL) -t $(INCLUDES) $(srcdir)/artsmoduleseffects.idl
+
+DISTCLEANFILES= artsmoduleseffects.cc artsmoduleseffects.h artsmoduleseffects.mcop*
+
+artsincludedir = $(includedir)/arts
+artsinclude_HEADERS = artsmoduleseffects.h artsmoduleseffects.idl
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = artsmoduleseffects.mcoptype artsmoduleseffects.mcopclass
+
+mcopclassdir = $(libdir)/mcop/Arts
+mcopclass_DATA = \
+ mcopclass/FiveBandMonoComplexEQ.mcopclass mcopclass/FiveBandMonoComplexEQGuiFactory.mcopclass \
+ mcopclass/MonoToStereo.mcopclass mcopclass/StereoToMono.mcopclass \
+ mcopclass/StereoBalance.mcopclass mcopclass/StereoBalanceGuiFactory.mcopclass \
+ mcopclass/Synth_VOICE_REMOVAL.mcopclass mcopclass/VoiceRemovalGuiFactory.mcopclass \
+ mcopclass/Synth_STEREO_COMPRESSOR.mcopclass mcopclass/StereoCompressorGuiFactory.mcopclass \
+ mcopclass/Synth_STEREO_PITCH_SHIFT.mcopclass mcopclass/Synth_STEREO_PITCH_SHIFT_FFT.mcopclass \
+ mcopclass/Synth_STEREO_FIR_EQUALIZER.mcopclass mcopclass/StereoFirEqualizerGuiFactory.mcopclass \
+ mcopclass/Synth_FREEVERB.mcopclass mcopclass/FreeverbGuiFactory.mcopclass \
+ mcopclass/Effect_WAVECAPTURE.mcopclass \
+ mcopclass/StereoVolumeControlGui.mcopclass mcopclass/StereoVolumeControlGuiFactory.mcopclass
+
diff --git a/arts/modules/effects/artsmoduleseffects.idl b/arts/modules/effects/artsmoduleseffects.idl
new file mode 100644
index 00000000..73ecd142
--- /dev/null
+++ b/arts/modules/effects/artsmoduleseffects.idl
@@ -0,0 +1,147 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+/*
+ * DISCLAIMER: The interfaces in envmixer.idl (and the derived .cc/.h files)
+ * DO NOT GUARANTEE BINARY COMPATIBILITY YET.
+ *
+ * They are intended for developers. You shouldn't expect that applications in
+ * binary form will be fully compatibile with further releases of these
+ * interfaces.
+ */
+
+#include <artsflow.idl>
+#include <artsgui.idl>
+#include <artsmodulescommon.idl>
+
+module Arts {
+
+ interface StereoToMono : Arts::SynthModule {
+ attribute float pan; // From -1(left) to +1(right)
+ in audio stream inleft, inright;
+ out audio stream outmono;
+ };
+
+ interface MonoToStereo : Arts::SynthModule {
+ attribute float pan; // Same as StereoToMono
+ in audio stream inmono;
+ out audio stream outleft, outright;
+ };
+
+ interface StereoBalance : Arts::StereoEffect {
+ attribute float balance;
+ };
+ interface StereoBalanceGuiFactory : Arts::GuiFactory {};
+
+ interface FiveBandMonoComplexEQ : Arts::StereoEffect {
+ readonly attribute StereoToMono s2m;
+ attribute float lowfreq, lowq, lowgain;
+ attribute float mid1freq, mid1q, mid1gain;
+ attribute float mid2freq, mid2q, mid2gain;
+ attribute float mid3freq, mid3q, mid3gain;
+ attribute float highfreq, highq, highgain;
+ readonly attribute MonoToStereo m2s;
+ };
+ interface FiveBandMonoComplexEQGuiFactory : Arts::GuiFactory {};
+
+ interface Synth_VOICE_REMOVAL : StereoEffect {
+ attribute float position, frequency;
+ };
+
+ interface VoiceRemovalGuiFactory : GuiFactory {
+ };
+
+ interface Synth_STEREO_COMPRESSOR : StereoEffect {
+ attribute float attack, release, threshold, ratio, output;
+ attribute boolean thru;
+ };
+
+ interface StereoCompressorGuiFactory : GuiFactory {
+ };
+
+ interface Synth_STEREO_PITCH_SHIFT : StereoEffect {
+ attribute float speed, frequency;
+ };
+
+ interface Synth_STEREO_PITCH_SHIFT_FFT : StereoEffect {
+ attribute float speed, scaleFactor;
+ attribute long frameSize, oversample;
+ };
+
+ interface Synth_STEREO_FIR_EQUALIZER : StereoEffect {
+ attribute sequence<GraphPoint> frequencies;
+ attribute long taps;
+ };
+
+ interface StereoFirEqualizerGuiFactory : GuiFactory {
+ };
+
+ interface Synth_FREEVERB : StereoEffect {
+ attribute float roomsize, damp, wet, dry, width, mode;
+ };
+
+ interface FreeverbGuiFactory : GuiFactory {
+ };
+
+ interface Effect_WAVECAPTURE : StereoEffect {
+ attribute string filename;
+ };
+
+ interface StereoVolumeControlGui : LayoutBox {
+ /**
+ Creates a Gui for a StereoVolumeControl.
+ This should be the most often used function.
+ */
+ void constructor( Arts::StereoVolumeControl svc );
+ /*
+ The direction from min to max for all elements.
+ The elements will be order 90 degree clockwise to it.
+ */
+ //attribute Direction direction; // Is already in LayoutBox
+
+ /// The title of this volumecontrol
+ attribute string title;
+ /// the minimum and maximum value for levelmeter(only min, max is 0dB) and volumecontrol
+ attribute float dbmin, dbmax;
+
+ /**
+ The elements separate:
+ - the two levelmeter
+ - the volumefader
+ - the tickmarks for levelmeter and volumefader
+ - the Label showing the title of the VolumeControl
+ Use this only if you want to connect them to own devices.
+ */
+ readonly attribute LevelMeter left, right;
+ readonly attribute VolumeFader fader;
+ readonly attribute Tickmarks levelmetertickmarks, volumefadertickmarks;
+ readonly attribute Label label;
+
+ /**
+ Couples the two VolumeSlider
+ Is currently useless since StereoVolumeControl::scaleFactor is
+ only one value for both channels.
+ */
+ attribute boolean couple;
+
+ };
+
+ interface StereoVolumeControlGuiFactory : Arts::GuiFactory {};
+};
+
diff --git a/arts/modules/effects/effect_wavecapture_impl.cc b/arts/modules/effects/effect_wavecapture_impl.cc
new file mode 100644
index 00000000..e2b6c9a8
--- /dev/null
+++ b/arts/modules/effects/effect_wavecapture_impl.cc
@@ -0,0 +1,63 @@
+/*
+ Copyright (C) 2001 Matthias Kretz <kretz@kde.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/* $Id$ */
+
+#include "artsmoduleseffects.h"
+#include <stdsynthmodule.h>
+#include <flowsystem.h>
+
+using namespace std;
+namespace Arts {
+
+class Effect_WAVECAPTURE_impl : virtual public Effect_WAVECAPTURE_skel,
+ virtual public StdSynthModule
+{
+protected:
+ Synth_CAPTURE_WAV _capture;
+
+public:
+ void streamStart();
+ void streamEnd();
+ string filename() { return _capture.filename(); }
+ void filename( const string &newFilename ) { _capture.filename( newFilename ); }
+};
+
+void Effect_WAVECAPTURE_impl::streamStart()
+{
+ _capture.start();
+ _node()->virtualize("inleft",_capture._node(),"left");
+ _node()->virtualize("inright",_capture._node(),"right");
+ _node()->virtualize("outleft",_node(),"inleft");
+ _node()->virtualize("outright",_node(),"inright");
+}
+
+void Effect_WAVECAPTURE_impl::streamEnd()
+{
+ _node()->devirtualize("inleft",_capture._node(),"left");
+ _node()->devirtualize("inright",_capture._node(),"right");
+ _node()->devirtualize("outleft",_node(),"inleft");
+ _node()->devirtualize("outright",_node(),"inright");
+ _capture.stop();
+}
+
+REGISTER_IMPLEMENTATION(Effect_WAVECAPTURE_impl);
+
+}
+
+// vim:ts=4:sw=4
diff --git a/arts/modules/effects/fivebandmonocomplexeq_impl.cc b/arts/modules/effects/fivebandmonocomplexeq_impl.cc
new file mode 100644
index 00000000..30891b0a
--- /dev/null
+++ b/arts/modules/effects/fivebandmonocomplexeq_impl.cc
@@ -0,0 +1,208 @@
+/*
+
+ Copyright ( C ) 2002 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or ( at your option ) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include <artsflow.h>
+#include <flowsystem.h>
+#include <stdsynthmodule.h>
+#include <debug.h>
+#include <artsmoduleseffects.h>
+#include <connect.h>
+
+#include <kglobal.h>
+#include <klocale.h>
+
+namespace Arts {
+
+class FiveBandMonoComplexEQ_impl : virtual public FiveBandMonoComplexEQ_skel,
+ virtual public StdSynthModule
+{
+private:
+ Arts::StereoToMono _s2m;
+ Arts::MonoToStereo _m2s;
+ Arts::Synth_STD_EQUALIZER _low, _mid1, _mid2, _mid3, _high;
+public:
+ FiveBandMonoComplexEQ_impl() {}
+
+ Arts::StereoToMono s2m() { return _s2m; }
+ Arts::MonoToStereo m2s() { return _m2s; }
+
+ float lowfreq() { return _low.frequency(); }
+ void lowfreq( float n ) { _low.frequency( n ); };
+ float lowq() { return _low.q(); }
+ void lowq( float n ) { _low.q( n ); };
+ float lowgain() { return _low.low(); }
+ void lowgain( float n ) { _low.low( n ); };
+
+ float mid1freq() { return _mid1.frequency(); }
+ void mid1freq( float n ) { _mid1.frequency( n ); };
+ float mid1q() { return _mid1.q(); }
+ void mid1q( float n ) { _mid1.q( n ); };
+ float mid1gain() { return _mid1.mid(); }
+ void mid1gain( float n ) { _mid1.mid( n ); };
+
+ float mid2freq() { return _mid2.frequency(); }
+ void mid2freq( float n ) { _mid2.frequency( n ); };
+ float mid2q() { return _mid2.q(); }
+ void mid2q( float n ) { _mid2.q( n ); };
+ float mid2gain() { return _mid2.mid(); }
+ void mid2gain( float n ) { _mid2.mid( n ); };
+
+ float mid3freq() { return _mid3.frequency(); }
+ void mid3freq( float n ) { _mid3.frequency( n ); };
+ float mid3q() { return _mid3.q(); }
+ void mid3q( float n ) { _mid3.q( n ); };
+ float mid3gain() { return _mid3.mid(); }
+ void mid3gain( float n ) { _mid3.mid( n ); };
+
+ float highfreq() { return _high.frequency(); }
+ void highfreq( float n ) { _high.frequency( n ); };
+ float highq() { return _high.q(); }
+ void highq( float n ) { _high.q( n ); };
+ float highgain() { return _high.high(); }
+ void highgain( float n ) { _high.high( n ); };
+
+ void streamInit()
+ {
+ _s2m.start(); _low.start(); _mid1.start(); _mid2.start(); _mid3.start(); _high.start(); _m2s.start();
+
+ _node()->virtualize( "inleft", _s2m._node(), "inleft" );
+ _node()->virtualize( "inright", _s2m._node(), "inright" );
+ connect( _s2m, "outmono", _low, "invalue" );
+ connect( _low, "outvalue", _mid1, "invalue" );
+ connect( _mid1, "outvalue", _mid2, "invalue" );
+ connect( _mid2, "outvalue", _mid3, "invalue" );
+ connect( _mid3, "outvalue", _high, "invalue" );
+ connect( _high, "outvalue", _m2s, "inmono" );
+ _node()->virtualize( "outleft", _m2s._node(), "outleft" );
+ _node()->virtualize( "outright", _m2s._node(), "outright" );
+ }
+};
+REGISTER_IMPLEMENTATION( FiveBandMonoComplexEQ_impl );
+
+class FiveBandMonoComplexEQGuiFactory_impl : virtual public FiveBandMonoComplexEQGuiFactory_skel
+{
+public:
+ Arts::Widget createGui( Arts::Object object )
+ {
+ KGlobal::locale()->insertCatalogue( "artsmodules" );
+
+ arts_return_val_if_fail( !object.isNull(), Arts::Widget::null() );
+ FiveBandMonoComplexEQ ch = DynamicCast( object );
+ arts_return_val_if_fail( !ch.isNull(), Arts::Widget::null() );
+
+ Arts::LayoutBox hbox;
+ hbox.direction( Arts::LeftToRight );
+ hbox.layoutmargin( 5 ); hbox.spacing( 5 );
+
+ Arts::Poti lowgain; lowgain.caption( i18n( "Low Gain" ).utf8().data() );
+ lowgain.min( -24 ); lowgain.max( 24 );
+ lowgain.value( ch.lowgain() ); connect( lowgain, "value_changed", ch, "lowgain" );
+ hbox.addWidget( lowgain );
+ PopupBox low;
+ low.height( 100 ); low.direction( LeftToRight );
+ hbox.addWidget( low );
+ Arts::VBox lowbox; low.widget( lowbox );
+ Arts::Poti lowfreq; lowfreq.color( "grey" ); lowfreq.caption( i18n( "Low Freq" ).utf8().data() );
+ lowfreq.min( 20 ); lowfreq.max( 1000 );
+ lowfreq.value( ch.lowfreq() ); connect( lowfreq, "value_changed", ch, "lowfreq" );
+ lowfreq.parent( lowbox ); lowbox._addChild( lowfreq , "" );
+ Arts::Poti lowq; lowq.color( "grey" ); lowq.caption( i18n( "Low Q" ).utf8().data() );
+ lowq.min( 0.01 ); lowq.max( 10 );
+ lowq.value( ch.lowq() ); connect( lowq, "value_changed", ch, "lowq" );
+ lowq.parent( lowbox ); lowbox._addChild( lowq , "" );
+
+ Arts::Poti mid1gain; mid1gain.caption( i18n( "Mid1 Gain" ).utf8().data() );
+ mid1gain.min( -24 ); mid1gain.max( 24 );
+ mid1gain.value( ch.mid1gain() ); connect( mid1gain, "value_changed", ch, "mid1gain" );
+ hbox.addWidget( mid1gain );
+ PopupBox mid1;
+ mid1.height( 100 ); mid1.direction( LeftToRight );
+ hbox.addWidget( mid1 );
+ Arts::VBox mid1box; mid1.widget( mid1box );
+ Arts::Poti mid1freq; mid1freq.color( "grey" ); mid1freq.caption( i18n( "Mid1 Freq" ).utf8().data() );
+ mid1freq.min( 20 ); mid1freq.max( 5000 );
+ mid1freq.value( ch.mid1freq() ); connect( mid1freq, "value_changed", ch, "mid1freq" );
+ mid1freq.parent( mid1box ); mid1box._addChild( mid1freq , "" );
+ Arts::Poti mid1q; mid1q.color( "grey" ); mid1q.caption( i18n( "Mid1 Q" ).utf8().data() );
+ mid1q.min( 0.01 ); mid1q.max( 10 );
+ mid1q.value( ch.mid1q() ); connect( mid1q, "value_changed", ch, "mid1q" );
+ mid1q.parent( mid1box ); mid1box._addChild( mid1q , "" );
+
+ Arts::Poti mid2gain; mid2gain.caption( i18n( "Mid2 Gain" ).utf8().data() );
+ mid2gain.min( -24 ); mid2gain.max( 24 );
+ mid2gain.value( ch.mid2gain() ); connect( mid2gain, "value_changed", ch, "mid2gain" );
+ hbox.addWidget( mid2gain );
+ PopupBox mid2;
+ mid2.height( 100 ); mid2.direction( LeftToRight );
+ hbox.addWidget( mid2 );
+ Arts::VBox mid2box; mid2.widget( mid2box );
+ Arts::Poti mid2freq; mid2freq.color( "grey" ); mid2freq.caption( i18n( "Mid2 Freq" ).utf8().data() );
+ mid2freq.min( 20 ); mid2freq.max( 10000 );
+ mid2freq.value( ch.mid2freq() ); connect( mid2freq, "value_changed", ch, "mid2freq" );
+ mid2freq.parent( mid2box ); mid2box._addChild( mid2freq , "" );
+ Arts::Poti mid2q; mid2q.color( "grey" ); mid2q.caption( i18n( "Mid2 Q" ).utf8().data() );
+ mid2q.min( 0.01 ); mid2q.max( 10 );
+ mid2q.value( ch.mid2q() ); connect( mid2q, "value_changed", ch, "mid2q" );
+ mid2q.parent( mid2box ); mid2box._addChild( mid2q , "" );
+
+ Arts::Poti mid3gain; mid3gain.caption( i18n( "Mid3 Gain" ).utf8().data() );
+ mid3gain.min( -24 ); mid3gain.max( 24 );
+ mid3gain.value( ch.mid3gain() ); connect( mid3gain, "value_changed", ch, "mid3gain" );
+ hbox.addWidget( mid3gain );
+ PopupBox mid3;
+ mid3.height( 100 ); mid3.direction( LeftToRight );
+ hbox.addWidget( mid3 );
+ Arts::VBox mid3box; mid3.widget( mid3box );
+ Arts::Poti mid3freq; mid3freq.color( "grey" ); mid3freq.caption( i18n( "Mid3 Freq" ).utf8().data() );
+ mid3freq.min( 1000 ); mid3freq.max( 10000 );
+ mid3freq.value( ch.mid3freq() ); connect( mid3freq, "value_changed", ch, "mid3freq" );
+ mid3freq.parent( mid3box ); mid3box._addChild( mid3freq , "" );
+ Arts::Poti mid3q; mid3q.color( "grey" ); mid3q.caption( i18n( "Mid3 Q" ).utf8().data() );
+ mid3q.min( 0.01 ); mid3q.max( 10 );
+ mid3q.value( ch.mid3q() ); connect( mid3q, "value_changed", ch, "mid3q" );
+ mid3q.parent( mid3box ); mid3box._addChild( mid3q , "" );
+
+ Arts::Poti highgain; highgain.caption( i18n( "High Gain" ).utf8().data() );
+ highgain.min( -24 ); highgain.max( 24 );
+ highgain.value( ch.highgain() ); connect( highgain, "value_changed", ch, "highgain" );
+ hbox.addWidget( highgain );
+ PopupBox high;
+ high.height( 100 ); high.direction( LeftToRight );
+ hbox.addWidget( high );
+ Arts::VBox highbox; high.widget( highbox );
+ Arts::Poti highfreq; highfreq.color( "grey" ); highfreq.caption( i18n( "High Freq" ).utf8().data() );
+ highfreq.min( 5000 ); highfreq.max( 16000 );
+ highfreq.value( ch.highfreq() ); connect( highfreq, "value_changed", ch, "highfreq" );
+ highfreq.parent( highbox ); highbox._addChild( highfreq , "" );
+ Arts::Poti highq; highq.color( "grey" ); highq.caption( i18n( "High Q" ).utf8().data() );
+ highq.min( 0.01 ); highq.max( 10 );
+ highq.value( ch.highq() ); connect( highq, "value_changed", ch, "highq" );
+ highq.parent( highbox ); highbox._addChild( highq , "" );
+
+ hbox.addStretch( 100 );
+
+ return hbox;
+ }
+};
+REGISTER_IMPLEMENTATION( FiveBandMonoComplexEQGuiFactory_impl );
+
+}
+
diff --git a/arts/modules/effects/freeverb/Makefile.am b/arts/modules/effects/freeverb/Makefile.am
new file mode 100644
index 00000000..42d70f20
--- /dev/null
+++ b/arts/modules/effects/freeverb/Makefile.am
@@ -0,0 +1,5 @@
+
+noinst_LTLIBRARIES = libfreeverb.la
+
+libfreeverb_la_SOURCES = allpass.cpp comb.cpp revmodel.cpp
+
diff --git a/arts/modules/effects/freeverb/allpass.cpp b/arts/modules/effects/freeverb/allpass.cpp
new file mode 100644
index 00000000..ca4d8bc5
--- /dev/null
+++ b/arts/modules/effects/freeverb/allpass.cpp
@@ -0,0 +1,36 @@
+// Allpass filter implementation
+//
+// Written by Jezar at Dreampoint, June 2000
+// http://www.dreampoint.co.uk
+// This code is public domain
+
+#include "allpass.hpp"
+
+allpass::allpass()
+{
+ bufidx = 0;
+}
+
+void allpass::setbuffer(float *buf, int size)
+{
+ buffer = buf;
+ bufsize = size;
+}
+
+void allpass::mute()
+{
+ for (int i=0; i<bufsize; i++)
+ buffer[i]=0;
+}
+
+void allpass::setfeedback(float val)
+{
+ feedback = val;
+}
+
+float allpass::getfeedback()
+{
+ return feedback;
+}
+
+//ends
diff --git a/arts/modules/effects/freeverb/allpass.hpp b/arts/modules/effects/freeverb/allpass.hpp
new file mode 100644
index 00000000..853c7d41
--- /dev/null
+++ b/arts/modules/effects/freeverb/allpass.hpp
@@ -0,0 +1,48 @@
+// Allpass filter declaration
+//
+// Written by Jezar at Dreampoint, June 2000
+// http://www.dreampoint.co.uk
+// This code is public domain
+
+#ifndef _allpass_
+#define _allpass_
+#include "denormals.h"
+
+class allpass
+{
+public:
+ allpass();
+ void setbuffer(float *buf, int size);
+ inline float process(float inp);
+ void mute();
+ void setfeedback(float val);
+ float getfeedback();
+// private:
+ float feedback;
+ float *buffer;
+ int bufsize;
+ int bufidx;
+};
+
+
+// Big to inline - but crucial for speed
+
+inline float allpass::process(float input)
+{
+ float output;
+ float bufout;
+
+ bufout = buffer[bufidx];
+ undenormalise(bufout);
+
+ output = -input + bufout;
+ buffer[bufidx] = input + (bufout*feedback);
+
+ if(++bufidx>=bufsize) bufidx = 0;
+
+ return output;
+}
+
+#endif//_allpass
+
+//ends
diff --git a/arts/modules/effects/freeverb/comb.cpp b/arts/modules/effects/freeverb/comb.cpp
new file mode 100644
index 00000000..c05f5069
--- /dev/null
+++ b/arts/modules/effects/freeverb/comb.cpp
@@ -0,0 +1,48 @@
+// Comb filter implementation
+//
+// Written by Jezar at Dreampoint, June 2000
+// http://www.dreampoint.co.uk
+// This code is public domain
+
+#include "comb.hpp"
+
+comb::comb()
+{
+ filterstore = 0;
+ bufidx = 0;
+}
+
+void comb::setbuffer(float *buf, int size)
+{
+ buffer = buf;
+ bufsize = size;
+}
+
+void comb::mute()
+{
+ for (int i=0; i<bufsize; i++)
+ buffer[i]=0;
+}
+
+void comb::setdamp(float val)
+{
+ damp1 = val;
+ damp2 = 1-val;
+}
+
+float comb::getdamp()
+{
+ return damp1;
+}
+
+void comb::setfeedback(float val)
+{
+ feedback = val;
+}
+
+float comb::getfeedback()
+{
+ return feedback;
+}
+
+// ends
diff --git a/arts/modules/effects/freeverb/comb.hpp b/arts/modules/effects/freeverb/comb.hpp
new file mode 100644
index 00000000..4a73b615
--- /dev/null
+++ b/arts/modules/effects/freeverb/comb.hpp
@@ -0,0 +1,55 @@
+// Comb filter class declaration
+//
+// Written by Jezar at Dreampoint, June 2000
+// http://www.dreampoint.co.uk
+// This code is public domain
+
+#ifndef _comb_
+#define _comb_
+
+#include "denormals.h"
+
+class comb
+{
+public:
+ comb();
+ void setbuffer(float *buf, int size);
+ inline float process(float inp);
+ void mute();
+ void setdamp(float val);
+ float getdamp();
+ void setfeedback(float val);
+ float getfeedback();
+private:
+ float feedback;
+ float filterstore;
+ float damp1;
+ float damp2;
+ float *buffer;
+ int bufsize;
+ int bufidx;
+};
+
+
+// Big to inline - but crucial for speed
+
+inline float comb::process(float input)
+{
+ float output;
+
+ output = buffer[bufidx];
+ undenormalise(output);
+
+ filterstore = (output*damp2) + (filterstore*damp1);
+ undenormalise(filterstore);
+
+ buffer[bufidx] = input + (filterstore*feedback);
+
+ if(++bufidx>=bufsize) bufidx = 0;
+
+ return output;
+}
+
+#endif //_comb_
+
+//ends
diff --git a/arts/modules/effects/freeverb/denormals.h b/arts/modules/effects/freeverb/denormals.h
new file mode 100644
index 00000000..f8714127
--- /dev/null
+++ b/arts/modules/effects/freeverb/denormals.h
@@ -0,0 +1,15 @@
+// Macro for killing denormalled numbers
+//
+// Written by Jezar at Dreampoint, June 2000
+// http://www.dreampoint.co.uk
+// Based on IS_DENORMAL macro by Jon Watte
+// This code is public domain
+
+#ifndef _denormals_
+#define _denormals_
+
+#define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f
+
+#endif//_denormals_
+
+//ends
diff --git a/arts/modules/effects/freeverb/readme.txt b/arts/modules/effects/freeverb/readme.txt
new file mode 100644
index 00000000..36361f4b
--- /dev/null
+++ b/arts/modules/effects/freeverb/readme.txt
@@ -0,0 +1,67 @@
+Freeverb - Free, studio-quality reverb SOURCE CODE in the public domain
+-----------------------------------------------------------------------
+
+Written by Jezar at Dreampoint - http://www.dreampoint.co.uk
+
+
+Introduction
+------------
+
+Hello.
+
+I'll try to keep this "readme" reasonably small. There are few things in the world that I hate more than long "readme" files. Except "coding conventions" - but more on that later...
+
+In this zip file you will find two folders of C++ source code:
+
+"Components" - Contains files that should clean-compile ON ANY TYPE OF COMPUTER OR SYSTEM WHATSOEVER. It should not be necessary to make ANY changes to these files to get them to compile, except to make up for inadequacies of certain compilers. These files create three classes - a comb filter, an allpass filter, and a reverb model made up of a number of instances of the filters, with some features to control the filters at a macro level. You will need to link these classes into another program that interfaces with them. The files in the components drawer are completely independant, and can be built without dependancies on anything else. Because of the simple interface, it should be possible to interface these files to any system - VST, DirectX, anything - without changing them AT ALL.
+
+"FreeverbVST" - Contains a Steinberg VST implementation of this version of Freeverb, using the components in (surprise) the components folder. It was built on a PC but may compile properly for the Macintosh with no problems. I don't know - I don't have a Macintosh. If you've figured out how to compile the examples in the Steinberg VST Development Kit, then you should easilly figure out how to bring the files into a project and get it working in a few minutes. It should be very simple.
+
+Note that this version of Freeverb doesn't contain predelay, or any EQ. I thought that might make it difficult to understand the "reverb" part of the code. Once you figure out how Freeverb works, you should find it trivial to add such features with little CPU overhead.
+
+Also, the code in this version of Freeverb has been optimised. This has changed the sound *slightly*, but not significantly compared to how much processing power it saves.
+
+Finally, note that there is also a built copy of this version of Freeverb called "Freeverb3.dll" - this is a VST plugin for the PC. If you want a version for the Mac or anything else, then you'll need to build it yourself from the code.
+
+
+Technical Explanation
+---------------------
+
+Freeverb is a simple implementation of the standard Schroeder/Moorer reverb model. I guess the only reason why it sounds better than other reverbs, is simply because I spent a long while doing listening tests in order to create the values found in "tuning.h". It uses 8 comb filters on both the left and right channels), and you might possibly be able to get away with less if CPU power is a serious constraint for you. It then feeds the result of the reverb through 4 allpass filters on both the left and right channels. These "smooth" the sound. Adding more than four allpasses doesn't seem to add anything significant to the sound, and if you use less, the sound gets a bit "grainy". The filters on the right channel are slightly detuned compared to the left channel in order to create a stereo effect.
+
+Hopefully, you should find the code in the components drawer a model of brevity and clarity. Notice that I don't use any "coding conventions". Personally, I think that coding conventions suck. They are meant to make the code "clearer", but they inevitably do the complete opposite, making the code completely unfathomable. Anyone whose done Windows programming with its - frankly stupid - "Hungarian notation" will know exactly what I mean. Coding conventions typically promote issues that are irrelevant up to the status of appearing supremely important. It may have helped back people in the days when compilers where somewhat feeble in their type-safety, but not in the new millenium with advanced C++ compilers.
+
+Imagine if we rewrote the English language to conform to coding conventions. After all, The arguments should be just as valid for the English language as they are for a computer language. For example, we could put a lower-case "n" in front of every noun, a lower-case "p" in front of a persons name, a lower-case "v" in front of every verb, and a lower-case "a" in front of every adjective. Can you imagine what the English language would look like? All in the name of "clarity". It's just as stupid to do this for computer code as it would be to do it for the English language. I hope that the code for Freeverb in the components drawer demonstrates this, and helps start a movement back towards sanity in coding practices.
+
+
+Background
+----------
+
+Why is the Freeverb code now public domain? Simple. I only intended to create Freeverb to provide me and my friends with studio-quality reverb for free. I never intended to make any money out of it. However, I simply do not have the time to develop it any further. I'm working on a "concept album" at the moment, and I'll never finish it if I spend any more time programming.
+
+In any case, I make more far money as a contract programmer - making Mobile Internet products - than I ever could writing plugins, so it simply doesn't make financial sense for me to spend any more time on it.
+
+Rather than give Freeverb to any particular individual or organisation to profit from it, I've decided to give it away to the internet community at large, so that quality, FREE (or at the very least, low-cost) reverbs can be developed for all platforms.
+
+Feel free to use the source code for Freeverb in any of your own products, whether they are also available for free, or even if they are commercial - I really don't mind. You may do with the code whatever you wish. If you use it in a product (whether commercial or not), it would be very nice of you, if you were to send me a copy of your product - although I appreciate that this isn't always possible in all circumstances.
+
+HOWEVER, please don't bug me with questions about how to use this code. I gave away Freeverb because I don't have time to maintain it. That means I *certainly* don't have time to answer questions about the source code, so please don't email questions to me. I *will* ignore them. If you can't figure the code for Freeverb out - then find somebody who can. I hope that either way, you enjoy experimenting with it.
+
+
+Disclaimer
+----------
+
+This software and source code is given away for free, without any warranties of any kind. It has been given away to the internet community as a free gift, so please treat it in the same spirit.
+
+
+I hope this code is useful and interesting to you all!
+I hope you have lots of fun experimenting with it and make good products!
+
+Very best regards,
+Jezar.
+Technology Consultant
+Dreampoint Design and Engineering
+http://www.dreampoint.co.uk
+
+
+//ends
diff --git a/arts/modules/effects/freeverb/revmodel.cpp b/arts/modules/effects/freeverb/revmodel.cpp
new file mode 100644
index 00000000..23a766cc
--- /dev/null
+++ b/arts/modules/effects/freeverb/revmodel.cpp
@@ -0,0 +1,257 @@
+// Reverb model implementation
+//
+// Written by Jezar at Dreampoint, June 2000
+// http://www.dreampoint.co.uk
+// This code is public domain
+
+#include "revmodel.hpp"
+
+revmodel::revmodel()
+{
+ // Tie the components to their buffers
+ combL[0].setbuffer(bufcombL1,combtuningL1);
+ combR[0].setbuffer(bufcombR1,combtuningR1);
+ combL[1].setbuffer(bufcombL2,combtuningL2);
+ combR[1].setbuffer(bufcombR2,combtuningR2);
+ combL[2].setbuffer(bufcombL3,combtuningL3);
+ combR[2].setbuffer(bufcombR3,combtuningR3);
+ combL[3].setbuffer(bufcombL4,combtuningL4);
+ combR[3].setbuffer(bufcombR4,combtuningR4);
+ combL[4].setbuffer(bufcombL5,combtuningL5);
+ combR[4].setbuffer(bufcombR5,combtuningR5);
+ combL[5].setbuffer(bufcombL6,combtuningL6);
+ combR[5].setbuffer(bufcombR6,combtuningR6);
+ combL[6].setbuffer(bufcombL7,combtuningL7);
+ combR[6].setbuffer(bufcombR7,combtuningR7);
+ combL[7].setbuffer(bufcombL8,combtuningL8);
+ combR[7].setbuffer(bufcombR8,combtuningR8);
+ allpassL[0].setbuffer(bufallpassL1,allpasstuningL1);
+ allpassR[0].setbuffer(bufallpassR1,allpasstuningR1);
+ allpassL[1].setbuffer(bufallpassL2,allpasstuningL2);
+ allpassR[1].setbuffer(bufallpassR2,allpasstuningR2);
+ allpassL[2].setbuffer(bufallpassL3,allpasstuningL3);
+ allpassR[2].setbuffer(bufallpassR3,allpasstuningR3);
+ allpassL[3].setbuffer(bufallpassL4,allpasstuningL4);
+ allpassR[3].setbuffer(bufallpassR4,allpasstuningR4);
+
+ // Set default values
+ allpassL[0].setfeedback(0.5f);
+ allpassR[0].setfeedback(0.5f);
+ allpassL[1].setfeedback(0.5f);
+ allpassR[1].setfeedback(0.5f);
+ allpassL[2].setfeedback(0.5f);
+ allpassR[2].setfeedback(0.5f);
+ allpassL[3].setfeedback(0.5f);
+ allpassR[3].setfeedback(0.5f);
+ setwet(initialwet);
+ setroomsize(initialroom);
+ setdry(initialdry);
+ setdamp(initialdamp);
+ setwidth(initialwidth);
+ setmode(initialmode);
+
+ // Buffer will be full of rubbish - so we MUST mute them
+ mute();
+}
+
+void revmodel::mute()
+{
+ int i;
+
+ if (getmode() >= freezemode)
+ return;
+
+ for (i=0;i<numcombs;i++)
+ {
+ combL[i].mute();
+ combR[i].mute();
+ }
+ for (i=0;i<numallpasses;i++)
+ {
+ allpassL[i].mute();
+ allpassR[i].mute();
+ }
+}
+
+void revmodel::processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip)
+{
+ float outL,outR,input;
+
+ while(numsamples-- > 0)
+ {
+ int i;
+ outL = outR = 0;
+ input = (*inputL + *inputR) * gain;
+
+ // Accumulate comb filters in parallel
+ for(i=0; i<numcombs; i++)
+ {
+ outL += combL[i].process(input);
+ outR += combR[i].process(input);
+ }
+
+ // Feed through allpasses in series
+ for(i=0; i<numallpasses; i++)
+ {
+ outL = allpassL[i].process(outL);
+ outR = allpassR[i].process(outR);
+ }
+
+ // Calculate output REPLACING anything already there
+ *outputL = outL*wet1 + outR*wet2 + *inputL*dry;
+ *outputR = outR*wet1 + outL*wet2 + *inputR*dry;
+
+ // Increment sample pointers, allowing for interleave (if any)
+ inputL += skip;
+ inputR += skip;
+ outputL += skip;
+ outputR += skip;
+ }
+}
+
+void revmodel::processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip)
+{
+ float outL,outR,input;
+
+ while(numsamples-- > 0)
+ {
+ int i;
+
+ outL = outR = 0;
+ input = (*inputL + *inputR) * gain;
+
+ // Accumulate comb filters in parallel
+ for(i=0; i<numcombs; i++)
+ {
+ outL += combL[i].process(input);
+ outR += combR[i].process(input);
+ }
+
+ // Feed through allpasses in series
+ for(i=0; i<numallpasses; i++)
+ {
+ outL = allpassL[i].process(outL);
+ outR = allpassR[i].process(outR);
+ }
+
+ // Calculate output MIXING with anything already there
+ *outputL += outL*wet1 + outR*wet2 + *inputL*dry;
+ *outputR += outR*wet1 + outL*wet2 + *inputR*dry;
+
+ // Increment sample pointers, allowing for interleave (if any)
+ inputL += skip;
+ inputR += skip;
+ outputL += skip;
+ outputR += skip;
+ }
+}
+
+void revmodel::update()
+{
+// Recalculate internal values after parameter change
+
+ int i;
+
+ wet1 = wet*(width/2 + 0.5f);
+ wet2 = wet*((1-width)/2);
+
+ if (mode >= freezemode)
+ {
+ roomsize1 = 1;
+ damp1 = 0;
+ gain = muted;
+ }
+ else
+ {
+ roomsize1 = roomsize;
+ damp1 = damp;
+ gain = fixedgain;
+ }
+
+ for(i=0; i<numcombs; i++)
+ {
+ combL[i].setfeedback(roomsize1);
+ combR[i].setfeedback(roomsize1);
+ }
+
+ for(i=0; i<numcombs; i++)
+ {
+ combL[i].setdamp(damp1);
+ combR[i].setdamp(damp1);
+ }
+}
+
+// The following get/set functions are not inlined, because
+// speed is never an issue when calling them, and also
+// because as you develop the reverb model, you may
+// wish to take dynamic action when they are called.
+
+void revmodel::setroomsize(float value)
+{
+ roomsize = (value*scaleroom) + offsetroom;
+ update();
+}
+
+float revmodel::getroomsize()
+{
+ return (roomsize-offsetroom)/scaleroom;
+}
+
+void revmodel::setdamp(float value)
+{
+ damp = value*scaledamp;
+ update();
+}
+
+float revmodel::getdamp()
+{
+ return damp/scaledamp;
+}
+
+void revmodel::setwet(float value)
+{
+ wet = value*scalewet;
+ update();
+}
+
+float revmodel::getwet()
+{
+ return wet/scalewet;
+}
+
+void revmodel::setdry(float value)
+{
+ dry = value*scaledry;
+}
+
+float revmodel::getdry()
+{
+ return dry/scaledry;
+}
+
+void revmodel::setwidth(float value)
+{
+ width = value;
+ update();
+}
+
+float revmodel::getwidth()
+{
+ return width;
+}
+
+void revmodel::setmode(float value)
+{
+ mode = value;
+ update();
+}
+
+float revmodel::getmode()
+{
+ if (mode >= freezemode)
+ return 1;
+ else
+ return 0;
+}
+
+//ends
diff --git a/arts/modules/effects/freeverb/revmodel.hpp b/arts/modules/effects/freeverb/revmodel.hpp
new file mode 100644
index 00000000..ca6c89a0
--- /dev/null
+++ b/arts/modules/effects/freeverb/revmodel.hpp
@@ -0,0 +1,87 @@
+// Reverb model declaration
+//
+// Written by Jezar at Dreampoint, June 2000
+// http://www.dreampoint.co.uk
+// This code is public domain
+
+#ifndef _revmodel_
+#define _revmodel_
+
+#include "comb.hpp"
+#include "allpass.hpp"
+#include "tuning.h"
+
+class revmodel
+{
+public:
+ revmodel();
+ void mute();
+ void processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip);
+ void processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip);
+ void setroomsize(float value);
+ float getroomsize();
+ void setdamp(float value);
+ float getdamp();
+ void setwet(float value);
+ float getwet();
+ void setdry(float value);
+ float getdry();
+ void setwidth(float value);
+ float getwidth();
+ void setmode(float value);
+ float getmode();
+private:
+ void update();
+private:
+ float gain;
+ float roomsize,roomsize1;
+ float damp,damp1;
+ float wet,wet1,wet2;
+ float dry;
+ float width;
+ float mode;
+
+ // The following are all declared inline
+ // to remove the need for dynamic allocation
+ // with its subsequent error-checking messiness
+
+ // Comb filters
+ comb combL[numcombs];
+ comb combR[numcombs];
+
+ // Allpass filters
+ allpass allpassL[numallpasses];
+ allpass allpassR[numallpasses];
+
+ // Buffers for the combs
+ float bufcombL1[combtuningL1];
+ float bufcombR1[combtuningR1];
+ float bufcombL2[combtuningL2];
+ float bufcombR2[combtuningR2];
+ float bufcombL3[combtuningL3];
+ float bufcombR3[combtuningR3];
+ float bufcombL4[combtuningL4];
+ float bufcombR4[combtuningR4];
+ float bufcombL5[combtuningL5];
+ float bufcombR5[combtuningR5];
+ float bufcombL6[combtuningL6];
+ float bufcombR6[combtuningR6];
+ float bufcombL7[combtuningL7];
+ float bufcombR7[combtuningR7];
+ float bufcombL8[combtuningL8];
+ float bufcombR8[combtuningR8];
+
+ // Buffers for the allpasses
+ float bufallpassL1[allpasstuningL1];
+ float bufallpassR1[allpasstuningR1];
+ float bufallpassL2[allpasstuningL2];
+ float bufallpassR2[allpasstuningR2];
+ float bufallpassL3[allpasstuningL3];
+ float bufallpassR3[allpasstuningR3];
+ float bufallpassL4[allpasstuningL4];
+ float bufallpassR4[allpasstuningR4];
+};
+
+#endif//_revmodel_
+
+//ends
diff --git a/arts/modules/effects/freeverb/tuning.h b/arts/modules/effects/freeverb/tuning.h
new file mode 100644
index 00000000..baaa9ce0
--- /dev/null
+++ b/arts/modules/effects/freeverb/tuning.h
@@ -0,0 +1,60 @@
+// Reverb model tuning values
+//
+// Written by Jezar at Dreampoint, June 2000
+// http://www.dreampoint.co.uk
+// This code is public domain
+
+#ifndef _tuning_
+#define _tuning_
+
+const int numcombs = 8;
+const int numallpasses = 4;
+const float muted = 0;
+const float fixedgain = 0.015f;
+const float scalewet = 3;
+const float scaledry = 2;
+const float scaledamp = 0.4f;
+const float scaleroom = 0.28f;
+const float offsetroom = 0.7f;
+const float initialroom = 0.5f;
+const float initialdamp = 0.5f;
+const float initialwet = 1/scalewet;
+const float initialdry = 0;
+const float initialwidth = 1;
+const float initialmode = 0;
+const float freezemode = 0.5f;
+const int stereospread = 23;
+
+// These values assume 44.1KHz sample rate
+// they will probably be OK for 48KHz sample rate
+// but would need scaling for 96KHz (or other) sample rates.
+// The values were obtained by listening tests.
+const int combtuningL1 = 1116;
+const int combtuningR1 = 1116+stereospread;
+const int combtuningL2 = 1188;
+const int combtuningR2 = 1188+stereospread;
+const int combtuningL3 = 1277;
+const int combtuningR3 = 1277+stereospread;
+const int combtuningL4 = 1356;
+const int combtuningR4 = 1356+stereospread;
+const int combtuningL5 = 1422;
+const int combtuningR5 = 1422+stereospread;
+const int combtuningL6 = 1491;
+const int combtuningR6 = 1491+stereospread;
+const int combtuningL7 = 1557;
+const int combtuningR7 = 1557+stereospread;
+const int combtuningL8 = 1617;
+const int combtuningR8 = 1617+stereospread;
+const int allpasstuningL1 = 556;
+const int allpasstuningR1 = 556+stereospread;
+const int allpasstuningL2 = 441;
+const int allpasstuningR2 = 441+stereospread;
+const int allpasstuningL3 = 341;
+const int allpasstuningR3 = 341+stereospread;
+const int allpasstuningL4 = 225;
+const int allpasstuningR4 = 225+stereospread;
+
+#endif//_tuning_
+
+//ends
+
diff --git a/arts/modules/effects/freeverbguifactory_impl.cc b/arts/modules/effects/freeverbguifactory_impl.cc
new file mode 100644
index 00000000..d24f04df
--- /dev/null
+++ b/arts/modules/effects/freeverbguifactory_impl.cc
@@ -0,0 +1,107 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmoduleseffects.h"
+#include "debug.h"
+#include "connect.h"
+
+#include <kglobal.h>
+#include <klocale.h>
+
+using namespace std;
+using namespace Arts;
+
+namespace Arts {
+
+class FreeverbGuiFactory_impl : public FreeverbGuiFactory_skel
+{
+public:
+ Widget createGui(Object freeverb);
+};
+
+REGISTER_IMPLEMENTATION(FreeverbGuiFactory_impl);
+
+}
+
+Widget FreeverbGuiFactory_impl::createGui(Object object)
+{
+ KGlobal::locale()->insertCatalogue( "artsmodules" );
+ arts_return_val_if_fail(!object.isNull(), Arts::Widget::null());
+
+ Synth_FREEVERB freeverb = DynamicCast(object);
+ arts_return_val_if_fail(!freeverb.isNull(), Arts::Widget::null());
+
+ HBox hbox;
+ hbox.width(330); hbox.height(80); hbox.show();
+
+ Poti roomsize;
+ roomsize.x(20); roomsize.y(10); roomsize.caption(i18n("roomsize").utf8().data());
+ roomsize.color("red"); roomsize.min(0); roomsize.max(1);
+ roomsize.value(freeverb.roomsize());
+ roomsize.range(100);
+ roomsize.parent(hbox);
+ roomsize.show();
+ connect(roomsize,"value_changed", freeverb, "roomsize");
+ hbox._addChild(roomsize,"roomsizeWidget");
+
+ Poti damp;
+ damp.x(80); damp.y(10); damp.caption(i18n("damp").utf8().data());
+ damp.color("red"); damp.min(0); damp.max(1);
+ damp.value(freeverb.damp());
+ damp.range(100);
+ damp.parent(hbox);
+ damp.show();
+ connect(damp,"value_changed", freeverb, "damp");
+ hbox._addChild(damp,"dampWidget");
+
+ Poti wet;
+ wet.x(140); wet.y(10); wet.caption(i18n("wet").utf8().data());
+ wet.color("red"); wet.min(0); wet.max(1);
+ wet.value(freeverb.wet());
+ wet.range(100);
+ wet.parent(hbox);
+ wet.show();
+ connect(wet,"value_changed", freeverb, "wet");
+ hbox._addChild(wet,"wetWidget");
+
+ Poti dry;
+ dry.x(200); dry.y(10); dry.caption(i18n("dry").utf8().data());
+ dry.color("red"); dry.min(0); dry.max(1);
+ dry.value(freeverb.dry());
+ dry.range(100);
+ dry.parent(hbox);
+ dry.show();
+ connect(dry,"value_changed", freeverb, "dry");
+ hbox._addChild(dry,"dryWidget");
+
+ Poti width;
+ width.x(260); width.y(10); width.caption(i18n("width").utf8().data());
+ width.color("red"); width.min(0); width.max(1);
+ width.value(freeverb.width());
+ width.range(100);
+ width.parent(hbox);
+ width.show();
+ connect(width,"value_changed", freeverb, "width");
+ hbox._addChild(width,"widthWidget");
+
+ return hbox;
+}
diff --git a/arts/modules/effects/kstereovolumecontrolgui_impl.cpp b/arts/modules/effects/kstereovolumecontrolgui_impl.cpp
new file mode 100644
index 00000000..1d0f7785
--- /dev/null
+++ b/arts/modules/effects/kstereovolumecontrolgui_impl.cpp
@@ -0,0 +1,132 @@
+/*
+
+ Copyright ( C ) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation;
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "kstereovolumecontrolgui_impl.h"
+
+#include <qframe.h>
+#include <kdebug.h>
+#include <iostream>
+
+using namespace Arts;
+
+KStereoVolumeControlGui_impl::KStereoVolumeControlGui_impl( QFrame* w ) : KLayoutBox_impl( w ? w : new QFrame( 0 ) )
+{
+ //kdDebug()<<"KStereoVolumeControlGui_impl::KStereoVolumeControlGui_impl( QFrame* "<<w<<" )"<<endl;
+ _mapper = new KStereoVolumeControlGui_EventMapper( this, _qframe );
+ this->addWidget( _label, -100 );
+ _label.bottom( Arts::East );
+ _label.text( "Volume" );
+ this->addLine( 1, 0, -100 );
+ this->addWidget( _left, 20 );
+ this->addWidget( _tickmarks, -100 );
+ this->addWidget( _right, 20 );
+ this->addLine( 1, 0, -100 );
+ this->addWidget( _volumefader, 20 );
+ this->addWidget( _fadertickmarks, -100 );
+ _fadertickmarks.position( posLeft );
+ _tickmarks.position( posLeft|posRight );
+ this->dbmin( -36 );
+ this->dbmax( 6 );
+ _left.framestyle( Arts::Raised|Arts::Panel ); _left.linewidth( 4 );
+ _right.framestyle( Arts::Raised|Arts::Panel ); _right.linewidth( 4 );
+ this->layoutmargin( 1 ); this->linewidth( 1 ); this->framestyle( Arts::Panel|Arts::Raised );
+}
+
+void KStereoVolumeControlGui_impl::constructor( Arts::StereoVolumeControl svc ) {
+ //kdDebug() << k_funcinfo << endl;
+ _svc = svc;
+ connect( svc, "currentVolumeLeft_changed", _left, "invalue" );
+ connect( svc, "currentVolumeRight_changed", _right, "invalue" );
+
+ connect( svc, "scaleFactor_changed", _volumefader, "volume" );
+ connect( _volumefader, "volume_changed", svc, "scaleFactor" );
+
+ _volumefader.volume( svc.scaleFactor() );
+
+ _mapper->_timer->start( 100 );
+}
+
+Arts::Direction KStereoVolumeControlGui_impl::direction() { return _dir; }
+void KStereoVolumeControlGui_impl::direction( Arts::Direction n ) {
+kdDebug() << k_funcinfo << n << endl;
+ _dir = n;
+ Arts::KLayoutBox_impl::direction( _dir );
+ switch ( _dir ) {
+ case Arts::LeftToRight:
+ case Arts::RightToLeft:
+ allWidgets( BottomToTop );
+ _label.bottom( Arts::East );
+ break;
+ case Arts::TopToBottom:
+ allWidgets( LeftToRight );
+ _label.bottom( Arts::South );
+ break;
+ case Arts::BottomToTop:
+ allWidgets( RightToLeft );
+ _label.bottom( Arts::South );
+ break;
+ default: break;
+ }
+}
+
+void KStereoVolumeControlGui_impl::allWidgets( Arts::Direction n ) {
+ _left.direction( n );
+ _right.direction( n );
+ _volumefader.direction( n );
+ _tickmarks.direction( n );
+ _fadertickmarks.direction( n );
+}
+
+void KStereoVolumeControlGui_impl::title( const std::string& n ) { _label.text( n ); }
+std::string KStereoVolumeControlGui_impl::title() { return _label.text(); }
+
+float KStereoVolumeControlGui_impl::dbmin() { return _dbmin; }
+void KStereoVolumeControlGui_impl::dbmin( float n ) {
+ //kdDebug() << k_funcinfo << n << endl;
+ _dbmin = n;
+ _left.mindB( _dbmin );
+ _right.mindB( _dbmin );
+ _tickmarks.min( _dbmin );
+ _volumefader.dbmin( _dbmin );
+ _fadertickmarks.min( _dbmin );
+}
+
+float KStereoVolumeControlGui_impl::dbmax() { return _dbmax; }
+void KStereoVolumeControlGui_impl::dbmax( float n ) {
+ //kdDebug() << k_funcinfo << n << endl;
+ _dbmax = n;
+ _left.maxdB( 0 );
+ _right.maxdB( 0 );
+ _tickmarks.max( 0 );
+ _volumefader.dbmax( _dbmax );
+ _fadertickmarks.max( _dbmax );
+}
+
+void KStereoVolumeControlGui_impl::updateValues() {
+ _left.invalue( _svc.currentVolumeLeft() );
+ _right.invalue( _svc.currentVolumeRight() );
+}
+
+REGISTER_IMPLEMENTATION( KStereoVolumeControlGui_impl );
+
+// vim: sw=4 ts=4
+#include "kstereovolumecontrolgui_impl.moc"
+
diff --git a/arts/modules/effects/kstereovolumecontrolgui_impl.h b/arts/modules/effects/kstereovolumecontrolgui_impl.h
new file mode 100644
index 00000000..abaec6ac
--- /dev/null
+++ b/arts/modules/effects/kstereovolumecontrolgui_impl.h
@@ -0,0 +1,97 @@
+/*
+
+ Copyright ( C ) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation;
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_STEREOVOLUMECONTROL_GUI_H
+#define ARTS_STEREOVOLUMECONTROL_GUI_H
+
+#include <artsmoduleseffects.h>
+
+#include <klayoutbox_impl.h>
+
+#include <kdebug.h>
+
+class KStereoVolumeControlGui_EventMapper;
+
+namespace Arts { // namespace Arts
+
+class KStereoVolumeControlGui_impl : virtual public Arts::StereoVolumeControlGui_skel,
+ virtual public Arts::KLayoutBox_impl
+{
+protected:
+ Arts::StereoVolumeControl _svc;
+ Arts::LevelMeter _left, _right;
+ Arts::Tickmarks _tickmarks;
+ Arts::Tickmarks _fadertickmarks;
+ Arts::VolumeFader _volumefader;
+ Arts::Label _label;
+ KStereoVolumeControlGui_EventMapper* _mapper;
+ float _dbmin, _dbmax;
+ Arts::Direction _dir;
+public:
+ KStereoVolumeControlGui_impl( QFrame* =0 );
+
+ void constructor( Arts::StereoVolumeControl );
+
+ Arts::Direction direction();
+ void direction( Arts::Direction );
+ std::string title();
+ void title( const std::string& );
+ float dbmin();
+ void dbmin( float );
+ float dbmax();
+ void dbmax( float );
+
+ Arts::LevelMeter left() { return _left; }
+ Arts::LevelMeter right() { return _right; }
+ Arts::VolumeFader fader() { return _volumefader; }
+ Arts::Tickmarks levelmetertickmarks() { return _tickmarks; }
+ Arts::Tickmarks volumefadertickmarks() { return _fadertickmarks; }
+ Arts::Label label() { return _label; }
+
+ void couple( bool ) {}
+ bool couple() { return true; }
+
+ void updateValues();
+private:
+ void allWidgets( Arts::Direction );
+}; // class StereoVolumeControlGui
+
+} // namespace Arts
+
+#include <qobject.h>
+#include <qtimer.h>
+
+class KStereoVolumeControlGui_EventMapper : public QObject {
+ Q_OBJECT
+public:
+ QTimer* _timer;
+ Arts::KStereoVolumeControlGui_impl* _impl;
+public:
+ KStereoVolumeControlGui_EventMapper( Arts::KStereoVolumeControlGui_impl* impl, QObject* parent, const char* name=0 ) : QObject( parent,name ), _impl( impl ) {
+ _timer = new QTimer( this );
+ connect( _timer, SIGNAL( timeout() ), this, SLOT( slotTimerSignal() ) );
+ }
+public slots:
+ void slotTimerSignal() { _impl->updateValues(); }
+};
+
+#endif
+// vim: sw=4 ts=4
diff --git a/arts/modules/effects/mcopclass/Effect_WAVECAPTURE.mcopclass b/arts/modules/effects/mcopclass/Effect_WAVECAPTURE.mcopclass
new file mode 100644
index 00000000..7eae5f24
--- /dev/null
+++ b/arts/modules/effects/mcopclass/Effect_WAVECAPTURE.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Effect_WAVECAPTURE,Arts::StereoEffect,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmoduleseffects.la
diff --git a/arts/modules/effects/mcopclass/FiveBandMonoComplexEQ.mcopclass b/arts/modules/effects/mcopclass/FiveBandMonoComplexEQ.mcopclass
new file mode 100644
index 00000000..015054ad
--- /dev/null
+++ b/arts/modules/effects/mcopclass/FiveBandMonoComplexEQ.mcopclass
@@ -0,0 +1,6 @@
+Buildable=true
+Interface=Arts::FiveBandMonoComplexEQ,Arts::StereoEffect,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmoduleseffects.la
+Features=RackGUI
+Name="Five Band Equalizer (Mono)"
diff --git a/arts/modules/effects/mcopclass/FiveBandMonoComplexEQGuiFactory.mcopclass b/arts/modules/effects/mcopclass/FiveBandMonoComplexEQGuiFactory.mcopclass
new file mode 100644
index 00000000..8ecb41a7
--- /dev/null
+++ b/arts/modules/effects/mcopclass/FiveBandMonoComplexEQGuiFactory.mcopclass
@@ -0,0 +1,4 @@
+Interface=Arts::FiveBandMonoComplexEQGuiFactory,Arts::GuiFactory,Arts::Object
+CanCreate=Arts::FiveBandMonoComplexEQ
+Language=C++
+Library=libartsmoduleseffects.la
diff --git a/arts/modules/effects/mcopclass/FreeverbGuiFactory.mcopclass b/arts/modules/effects/mcopclass/FreeverbGuiFactory.mcopclass
new file mode 100644
index 00000000..4c4d4e93
--- /dev/null
+++ b/arts/modules/effects/mcopclass/FreeverbGuiFactory.mcopclass
@@ -0,0 +1,4 @@
+Interface=Arts::FreeverbGuiFactory,Arts::GuiFactory,Arts::Object
+CanCreate=Arts::Synth_FREEVERB
+Language=C++
+Library=libartsmoduleseffects.la
diff --git a/arts/modules/effects/mcopclass/MonoToStereo.mcopclass b/arts/modules/effects/mcopclass/MonoToStereo.mcopclass
new file mode 100644
index 00000000..775b00e2
--- /dev/null
+++ b/arts/modules/effects/mcopclass/MonoToStereo.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::MonoToStereo,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmoduleseffects.la
diff --git a/arts/modules/effects/mcopclass/StereoBalance.mcopclass b/arts/modules/effects/mcopclass/StereoBalance.mcopclass
new file mode 100644
index 00000000..1a59e1f8
--- /dev/null
+++ b/arts/modules/effects/mcopclass/StereoBalance.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::StereoBalance,Arts::StereoEffect,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmoduleseffects.la
diff --git a/arts/modules/effects/mcopclass/StereoBalanceGuiFactory.mcopclass b/arts/modules/effects/mcopclass/StereoBalanceGuiFactory.mcopclass
new file mode 100644
index 00000000..a539a91e
--- /dev/null
+++ b/arts/modules/effects/mcopclass/StereoBalanceGuiFactory.mcopclass
@@ -0,0 +1,4 @@
+Interface=Arts::StereoBalanceGuiFactory,Arts::GuiFactory,Arts::Object
+CanCreate=Arts::StereoBalance
+Language=C++
+Library=libartsmoduleseffects.la
diff --git a/arts/modules/effects/mcopclass/StereoCompressorGuiFactory.mcopclass b/arts/modules/effects/mcopclass/StereoCompressorGuiFactory.mcopclass
new file mode 100644
index 00000000..f392adb7
--- /dev/null
+++ b/arts/modules/effects/mcopclass/StereoCompressorGuiFactory.mcopclass
@@ -0,0 +1,4 @@
+Interface=Arts::StereoCompressorGuiFactory,Arts::GuiFactory,Arts::Object
+CanCreate=Arts::Synth_STEREO_COMPRESSOR
+Language=C++
+Library=libartsmoduleseffects.la
diff --git a/arts/modules/effects/mcopclass/StereoFirEqualizerGuiFactory.mcopclass b/arts/modules/effects/mcopclass/StereoFirEqualizerGuiFactory.mcopclass
new file mode 100644
index 00000000..d8f3c71d
--- /dev/null
+++ b/arts/modules/effects/mcopclass/StereoFirEqualizerGuiFactory.mcopclass
@@ -0,0 +1,4 @@
+Interface=Arts::StereoFirEqualizerGuiFactory,Arts::GuiFactory,Arts::Object
+CanCreate=Arts::Synth_STEREO_FIR_EQUALIZER
+Language=C++
+Library=libartsmoduleseffects.la
diff --git a/arts/modules/effects/mcopclass/StereoToMono.mcopclass b/arts/modules/effects/mcopclass/StereoToMono.mcopclass
new file mode 100644
index 00000000..8208e6f4
--- /dev/null
+++ b/arts/modules/effects/mcopclass/StereoToMono.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::StereoToMono,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmoduleseffects.la
diff --git a/arts/modules/effects/mcopclass/StereoVolumeControlGui.mcopclass b/arts/modules/effects/mcopclass/StereoVolumeControlGui.mcopclass
new file mode 100644
index 00000000..f1db522e
--- /dev/null
+++ b/arts/modules/effects/mcopclass/StereoVolumeControlGui.mcopclass
@@ -0,0 +1,5 @@
+Buildable=true
+Interface=Arts::StereoVolumeControlGui,Arts::LayoutBox,Arts::Frame,Arts::Widget,Arts::Object
+Language=C++
+Library=libartsmoduleseffects.la
+Requires=kdegui
diff --git a/arts/modules/effects/mcopclass/StereoVolumeControlGuiFactory.mcopclass b/arts/modules/effects/mcopclass/StereoVolumeControlGuiFactory.mcopclass
new file mode 100644
index 00000000..8668ddbc
--- /dev/null
+++ b/arts/modules/effects/mcopclass/StereoVolumeControlGuiFactory.mcopclass
@@ -0,0 +1,6 @@
+Buildable=true
+Interface=Arts::StereoVolumeControlGuiFactory,Arts::GuiFactory,Arts::Object
+CanCreate=Arts::StereoVolumeControl
+Language=C++
+Library=libartsmoduleseffects.la
+Requires=kdegui
diff --git a/arts/modules/effects/mcopclass/Synth_FREEVERB.mcopclass b/arts/modules/effects/mcopclass/Synth_FREEVERB.mcopclass
new file mode 100644
index 00000000..def8bf1d
--- /dev/null
+++ b/arts/modules/effects/mcopclass/Synth_FREEVERB.mcopclass
@@ -0,0 +1,7 @@
+Buildable=true
+Interface=Arts::Synth_FREEVERB,Arts::StereoEffect,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmoduleseffects.la
+Features=RackGUI
+Name="Freeverb"
+Use=directly \ No newline at end of file
diff --git a/arts/modules/effects/mcopclass/Synth_STEREO_COMPRESSOR.mcopclass b/arts/modules/effects/mcopclass/Synth_STEREO_COMPRESSOR.mcopclass
new file mode 100644
index 00000000..357215a1
--- /dev/null
+++ b/arts/modules/effects/mcopclass/Synth_STEREO_COMPRESSOR.mcopclass
@@ -0,0 +1,6 @@
+Buildable=true
+Interface=Arts::Synth_STEREO_COMPRESSOR,Arts::StereoEffect,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmoduleseffects.la
+Features=RackGUI
+Name="Stereo Compressor"
diff --git a/arts/modules/effects/mcopclass/Synth_STEREO_FIR_EQUALIZER.mcopclass b/arts/modules/effects/mcopclass/Synth_STEREO_FIR_EQUALIZER.mcopclass
new file mode 100644
index 00000000..c127e6f0
--- /dev/null
+++ b/arts/modules/effects/mcopclass/Synth_STEREO_FIR_EQUALIZER.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_STEREO_FIR_EQUALIZER,Arts::StereoEffect,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmoduleseffects.la
diff --git a/arts/modules/effects/mcopclass/Synth_STEREO_PITCH_SHIFT.mcopclass b/arts/modules/effects/mcopclass/Synth_STEREO_PITCH_SHIFT.mcopclass
new file mode 100644
index 00000000..97eff8dd
--- /dev/null
+++ b/arts/modules/effects/mcopclass/Synth_STEREO_PITCH_SHIFT.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_STEREO_PITCH_SHIFT,Arts::StereoEffect,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmoduleseffects.la
diff --git a/arts/modules/effects/mcopclass/Synth_STEREO_PITCH_SHIFT_FFT.mcopclass b/arts/modules/effects/mcopclass/Synth_STEREO_PITCH_SHIFT_FFT.mcopclass
new file mode 100644
index 00000000..a1669725
--- /dev/null
+++ b/arts/modules/effects/mcopclass/Synth_STEREO_PITCH_SHIFT_FFT.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_STEREO_PITCH_SHIFT_FFT,Arts::StereoEffect,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmoduleseffects.la
diff --git a/arts/modules/effects/mcopclass/Synth_VOICE_REMOVAL.mcopclass b/arts/modules/effects/mcopclass/Synth_VOICE_REMOVAL.mcopclass
new file mode 100644
index 00000000..411747c5
--- /dev/null
+++ b/arts/modules/effects/mcopclass/Synth_VOICE_REMOVAL.mcopclass
@@ -0,0 +1,7 @@
+Buildable=true
+Interface=Arts::Synth_VOICE_REMOVAL,Arts::StereoEffect,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmoduleseffects.la
+Features=RackGUI
+Name="Voice Removal"
+Use=directly \ No newline at end of file
diff --git a/arts/modules/effects/mcopclass/VoiceRemovalGuiFactory.mcopclass b/arts/modules/effects/mcopclass/VoiceRemovalGuiFactory.mcopclass
new file mode 100644
index 00000000..95368698
--- /dev/null
+++ b/arts/modules/effects/mcopclass/VoiceRemovalGuiFactory.mcopclass
@@ -0,0 +1,4 @@
+Interface=Arts::VoiceRemovalGuiFactory,Arts::GuiFactory,Arts::Object
+CanCreate=Arts::Synth_VOICE_REMOVAL
+Language=C++
+Library=libartsmoduleseffects.la
diff --git a/arts/modules/effects/monostereoconversion_impl.cc b/arts/modules/effects/monostereoconversion_impl.cc
new file mode 100644
index 00000000..4d408e68
--- /dev/null
+++ b/arts/modules/effects/monostereoconversion_impl.cc
@@ -0,0 +1,160 @@
+/*
+
+ Copyright ( C ) 2002 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or ( at your option ) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include <artsflow.h>
+#include <flowsystem.h>
+#include <stdsynthmodule.h>
+#include <debug.h>
+#include <artsmoduleseffects.h>
+#include <connect.h>
+
+#include <kglobal.h>
+#include <klocale.h>
+
+namespace Arts {
+
+class MonoToStereo_impl : virtual public MonoToStereo_skel,
+ virtual public StdSynthModule
+{
+protected:
+ float _pan, _pLeft, _pRight;
+public:
+ MonoToStereo_impl()
+ {
+ pan( 0 );
+ }
+
+ float pan() { return _pan; }
+ void pan( float pan )
+ {
+ if( pan < -1 ) pan = -1;
+ if( pan > 1 ) pan = 1;
+ _pan = pan;
+ _pLeft = _pRight = 1;
+ if( _pan < 0 )
+ _pRight = 1 + _pan;
+ else
+ _pLeft = 1 - _pan;
+ }
+
+ void calculateBlock( unsigned long samples )
+ {
+ for( unsigned int i=0; i<samples; i++ )
+ {
+ outleft[ i ] = inmono[ i ] * _pLeft;
+ outright[ i ] = inmono[ i ] * _pRight;
+ }
+ }
+};
+REGISTER_IMPLEMENTATION( MonoToStereo_impl );
+
+class StereoToMono_impl : virtual public StereoToMono_skel,
+ virtual public StdSynthModule
+{
+protected:
+ float _pan, _pLeft, _pRight;
+public:
+ StereoToMono_impl()
+ {
+ pan( 0 );
+ }
+
+ float pan() { return _pan; }
+ void pan( float pan )
+ {
+ if( pan < -1 ) pan = -1;
+ if( pan > 1 ) pan = 1;
+ _pan = pan;
+ _pLeft = _pRight = 1;
+ if( _pan < 0 )
+ _pRight = 1 + _pan;
+ else
+ _pLeft = 1 - _pan;
+ }
+
+ void calculateBlock( unsigned long samples )
+ {
+ for( unsigned int i=0; i<samples; i++ )
+ outmono[ i ] = ( inleft[ i ] * _pLeft + inright[ i ] * _pRight ) / ( _pLeft + _pRight );
+ }
+};
+REGISTER_IMPLEMENTATION( StereoToMono_impl );
+
+class StereoBalance_impl : virtual public StereoBalance_skel,
+ virtual public StdSynthModule
+{
+protected:
+ float _balance, _left, _right;
+public:
+ StereoBalance_impl() : _balance( 0 ), _left( 1 ), _right( 1 ) { }
+
+ float balance() { return _balance; }
+ void balance( float n )
+ {
+//arts_debug( "StereoBalance::balance( float %f )", n );
+ if( n>1 ) n=1;
+ if( n<-1 ) n=-1;
+ _balance = n;
+ _right = _left = 1;
+ if( _balance < 0 )
+ _right = 1 + _balance;
+ else
+ _left = 1 - _balance;
+ }
+
+ void calculateBlock( unsigned long samples )
+ {
+//arts_debug( "StereoBalance::calculateBlock( unsigned int %i )", samples );
+ for( unsigned long i=0; i<samples; i++ )
+ {
+ // outleft[ i ] = inleft[ i ];
+ // outright[ i ] = inright[ i ];
+ outleft[ i ] = inleft[ i ] * _left;
+ outright[ i ] = inright[ i ] * _right;
+ }
+ }
+};
+REGISTER_IMPLEMENTATION( StereoBalance_impl );
+
+class StereoBalanceGuiFactory_impl : virtual public StereoBalanceGuiFactory_skel
+{
+public:
+ Widget createGui( Object object )
+ {
+ KGlobal::locale()->insertCatalogue( "artsmodules" );
+
+ arts_return_val_if_fail( !object.isNull(), Arts::Widget::null() );
+ StereoBalance ch= DynamicCast( object );
+ arts_return_val_if_fail( !ch.isNull(), Arts::Widget::null() );
+
+ Poti bal;
+ bal.caption( i18n( "Balance" ).utf8().data() );
+ bal.min( -1 ); bal.max( 1 );
+ bal.value( ch.balance() );
+ connect( bal, "value_changed", ch, "balance" );
+
+ return bal;
+ }
+};
+REGISTER_IMPLEMENTATION( StereoBalanceGuiFactory_impl );
+
+}
+
diff --git a/arts/modules/effects/stereocompressorguifactory_impl.cc b/arts/modules/effects/stereocompressorguifactory_impl.cc
new file mode 100644
index 00000000..b2f18a64
--- /dev/null
+++ b/arts/modules/effects/stereocompressorguifactory_impl.cc
@@ -0,0 +1,114 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#include <kglobal.h>
+#include <klocale.h>
+
+#include "artsmoduleseffects.h"
+#include "connect.h"
+#include "debug.h"
+
+using namespace Arts;
+
+namespace Arts {
+
+class StereoCompressorGuiFactory_impl : virtual public StereoCompressorGuiFactory_skel
+{
+public:
+ Widget createGui( Object object )
+ {
+ KGlobal::locale()->insertCatalogue( "artsmodules" );
+
+ arts_return_val_if_fail(!object.isNull(), Arts::Widget::null() );
+
+ Synth_STEREO_COMPRESSOR comp = DynamicCast(object);
+ arts_return_val_if_fail(!comp.isNull(), Arts::Widget::null());
+
+ Poti attack;
+ attack.caption(i18n("attack").utf8().data());
+ attack.color("blue");
+ attack.min(0.1); attack.max(250);
+ attack.value( comp.attack() );
+ attack.range(250);
+ connect( attack, "value_changed", comp, "attack" );
+
+ Poti release;
+ release.caption(i18n("release").utf8().data());
+ release.color("blue");
+ release.min(0.1); release.max(250);
+ release.value( comp.release() );
+ release.range(250);
+ connect( release, "value_changed", comp, "release" );
+
+ Poti threshold;
+ threshold.caption(i18n("thresh.").utf8().data());
+ threshold.min(0.00001); threshold.max(1);
+ threshold.value( comp.threshold() );
+ threshold.logarithmic( 2.0 );
+ threshold.range(200);
+ connect( threshold, "value_changed", comp, "threshold" );
+
+ Poti ratio;
+ ratio.caption(i18n("ratio").utf8().data());
+ ratio.min(0); ratio.max(1);
+ ratio.value( comp.ratio() );
+ ratio.range(200);
+ connect( ratio, "value_changed", comp, "ratio" );
+
+ Poti output;
+ output.caption(i18n("output").utf8().data());
+ output.min(0.1); output.max(10.0);
+ output.value( comp.output() );
+ output.logarithmic( 2.0 );
+ output.range(200);
+ connect( output, "value_changed", comp, "output" );
+
+ Button bon;
+ bon.text(i18n("Bypass").utf8().data());
+ bon.toggle( true );
+ connect( bon, "pressed_changed", comp, "thru" );
+
+ LayoutBox hbox;
+ hbox.direction( LeftToRight ); hbox.layoutmargin( 5 ); hbox.spacing( 5 );
+ PopupBox timesbox;
+ timesbox.name( "Timings" ); timesbox.direction( LeftToRight );
+ LayoutBox times;
+ times.direction( LeftToRight ); times.spacing( 5 );
+
+ hbox.addWidget( timesbox );
+ times.addSpace( 5 );
+ times.addWidget( attack );
+ times.addWidget( release );
+ times.addSpace( 5 );
+ timesbox.widget( times );
+ hbox.addWidget( threshold );
+ hbox.addWidget( ratio );
+ hbox.addWidget( output );
+ hbox.addWidget( bon );
+ hbox.addStretch( 10 );
+
+ return hbox;
+ }
+};
+
+// vim:sw=4:ts=4
+
+REGISTER_IMPLEMENTATION(StereoCompressorGuiFactory_impl);
+
+}
diff --git a/arts/modules/effects/stereovolumecontrolguifactory_impl.cpp b/arts/modules/effects/stereovolumecontrolguifactory_impl.cpp
new file mode 100644
index 00000000..fb2163f1
--- /dev/null
+++ b/arts/modules/effects/stereovolumecontrolguifactory_impl.cpp
@@ -0,0 +1,45 @@
+/*
+
+ Copyright ( C ) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation;
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include <artsmoduleseffects.h>
+#include <connect.h>
+#include <debug.h>
+
+namespace Arts {
+
+class StereoVolumeControlGuiFactory_impl : virtual public StereoVolumeControlGuiFactory_skel
+{
+public:
+ Widget createGui( Object object )
+ {
+ arts_return_val_if_fail( !object.isNull(), Arts::Widget::null() );
+ StereoVolumeControl svc = DynamicCast( object );
+ arts_return_val_if_fail( !svc.isNull(), Arts::Widget::null() );
+
+ return StereoVolumeControlGui( svc );
+ }
+};
+
+REGISTER_IMPLEMENTATION( StereoVolumeControlGuiFactory_impl );
+
+}
+// vim: sw=4 ts=4
+
diff --git a/arts/modules/effects/synth_freeverb_impl.cc b/arts/modules/effects/synth_freeverb_impl.cc
new file mode 100644
index 00000000..0f68902f
--- /dev/null
+++ b/arts/modules/effects/synth_freeverb_impl.cc
@@ -0,0 +1,84 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "freeverb/revmodel.hpp"
+#include "artsmoduleseffects.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+class Synth_FREEVERB_impl : virtual public Synth_FREEVERB_skel,
+ virtual public StdSynthModule
+{
+ revmodel *model;
+public:
+ float roomsize() { return model->getroomsize(); }
+ void roomsize(float newval) { return model->setroomsize(newval); }
+
+ float damp() { return model->getdamp(); }
+ void damp(float newval) { return model->setdamp(newval); }
+
+ float wet() { return model->getwet(); }
+ void wet(float newval) { return model->setwet(newval); }
+
+ float dry() { return model->getdry(); }
+ void dry(float newval) { return model->setdry(newval); }
+
+ float width() { return model->getwidth(); }
+ void width(float newval) { return model->setwidth(newval); }
+
+ float mode() { return model->getmode(); }
+ void mode(float newval) { return model->setmode(newval); }
+
+ void streamInit()
+ {
+ /* prevent full buffers to be carried over stop-start sequence */
+ model->mute();
+ }
+
+ void calculateBlock(unsigned long samples)
+ {
+ model->processreplace(inleft, inright, outleft, outright, samples,1);
+ // don't add the original signal - that's what the "dry" argument is for
+ //for(unsigned long i = 0;i < samples; i++)
+ //{
+ //outleft[i] += inleft[i];
+ //outright[i] += inright[i];
+ //}
+ }
+ Synth_FREEVERB_impl()
+ {
+ // "revmodel" object size is far too big, vtable
+ // can't handle it
+ model=new revmodel;
+ // set dry to 1 so it at first sounds like it always did before
+ // ok, since scaledry = 2 calling dry( 0.5 ) has the desired
+ // effect
+ dry( 0.5f );
+ }
+ ~Synth_FREEVERB_impl()
+ {
+ delete model;
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_FREEVERB_impl);
diff --git a/arts/modules/effects/synth_stereo_compressor_impl.cc b/arts/modules/effects/synth_stereo_compressor_impl.cc
new file mode 100644
index 00000000..db3463a9
--- /dev/null
+++ b/arts/modules/effects/synth_stereo_compressor_impl.cc
@@ -0,0 +1,135 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#include "artsmoduleseffects.h"
+#include <stdsynthmodule.h>
+#include <flowsystem.h>
+#include <debug.h>
+
+using namespace Arts;
+
+namespace Arts {
+
+class Synth_STEREO_COMPRESSOR_impl : virtual public Synth_STEREO_COMPRESSOR_skel,
+ virtual public StdSynthModule
+{
+public:
+ Synth_STEREO_COMPRESSOR_impl()
+ {
+ attack( 10 );
+ release( 10 );
+ threshold( 1 );
+ ratio( 0.8 );
+ output( 1 );
+ _thru = false;
+ _run = false;
+ }
+
+ void streamStart()
+ {
+ _run = true;
+ compleft.start();
+ compright.start();
+ if(!_thru)
+ connectComp(true);
+ else
+ connectThru(true);
+ }
+
+ void streamEnd()
+ {
+ _run = false;
+ connectComp(false);
+ connectThru(false);
+ compleft.stop();
+ compright.stop();
+ }
+
+ float attack() { return compleft.attack(); };
+ void attack( float f ) { compleft.attack(f); compright.attack(f); }
+ float release() { return compleft.release(); };
+ void release( float f ) { compleft.release(f); compright.release(f); }
+ float threshold() { return compleft.threshold(); };
+ void threshold( float f ) { compleft.threshold(f); compright.threshold(f); }
+ float ratio() { return compleft.ratio(); };
+ void ratio( float f ) { compleft.ratio(f); compright.ratio(f); }
+ float output() { return compleft.output(); };
+ void output( float f ) { compleft.output(f); compright.output(f); }
+
+ bool thru() { return _thru; }
+ void thru( bool f )
+ {
+ if( _thru != f )
+ {
+ if(!_thru)
+ connectComp(false);
+ else
+ connectThru(false);
+ _thru = f;
+ if(!_thru)
+ connectComp(true);
+ else
+ connectThru(true);
+ }
+ }
+
+private:
+ Synth_COMPRESSOR compleft, compright;
+ bool _thru;
+ bool _run;
+
+ void connectComp( bool _connect )
+ {
+ if(_connect)
+ {
+ _node()->virtualize("inleft",compleft._node(),"invalue");
+ _node()->virtualize("inright",compright._node(),"invalue");
+ _node()->virtualize("outleft",compleft._node(),"outvalue");
+ _node()->virtualize("outright",compright._node(),"outvalue");
+ }
+ else
+ {
+ _node()->devirtualize("inleft",compleft._node(),"invalue");
+ _node()->devirtualize("inright",compright._node(),"invalue");
+ _node()->devirtualize("outleft",compleft._node(),"outvalue");
+ _node()->devirtualize("outright",compright._node(),"outvalue");
+ }
+ }
+
+ void connectThru( bool _connect )
+ {
+ if(_connect)
+ {
+ _node()->virtualize("inleft",_node(),"outleft");
+ _node()->virtualize("inright",_node(),"outright");
+ }
+ else
+ {
+ _node()->devirtualize("inleft",_node(),"outleft");
+ _node()->devirtualize("inright",_node(),"outright");
+ }
+ }
+
+};
+
+// vim:sw=4:ts=4
+
+REGISTER_IMPLEMENTATION(Synth_STEREO_COMPRESSOR_impl);
+
+}
diff --git a/arts/modules/effects/synth_stereo_fir_equalizer_impl.cc b/arts/modules/effects/synth_stereo_fir_equalizer_impl.cc
new file mode 100644
index 00000000..8125104a
--- /dev/null
+++ b/arts/modules/effects/synth_stereo_fir_equalizer_impl.cc
@@ -0,0 +1,221 @@
+/*
+
+ Copyright (C) 2001-2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include <math.h>
+
+#include <arts/debug.h>
+#include <arts/fft.h>
+#include <arts/stdsynthmodule.h>
+#include <arts/connect.h>
+#include "artsmoduleseffects.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <kglobal.h>
+#include <klocale.h>
+
+using namespace std;
+using namespace Arts;
+
+static inline bool odd(int x) { return ((x & 1) == 1); }
+
+/* returns a blackman window: x is supposed to be in the interval [0..1] */
+static inline float blackmanWindow(float x)
+{
+ if(x < 0) return 0;
+ if(x > 1) return 0;
+ return 0.42-0.5*cos(M_PI*x*2)+0.08*cos(4*M_PI*x);
+}
+
+void firapprox(double *filter, int filtersize, vector<GraphPoint>& points)
+{
+ assert((filtersize >= 3) && odd(filtersize));
+
+ int fft_size = 8;
+ while(fft_size/2 < filtersize)
+ fft_size *= 2;
+
+ vector<GraphPoint>::iterator pi = points.begin();
+ float lfreq=-2, lval=1.0, rfreq=-1, rval=1.0;
+
+ float *re = (float*) malloc(fft_size * sizeof(float));
+ for(int i=0;i<fft_size/2;i++)
+ {
+ float freq = float(i)/float(fft_size/2);
+
+ while(freq > rfreq && pi != points.end())
+ {
+ lfreq = rfreq; rfreq = pi->x;
+ lval = rval; rval = pi->y;
+ pi++;
+ }
+ float pos = (freq-lfreq)/(rfreq-lfreq);
+ float val = lval*(1.0-pos) + rval*pos;
+
+ //printf("%f %f\n",freq,val);
+ re[i] = re[fft_size-1-i] = val;
+ }
+
+ float *filter_re = (float*) malloc(fft_size * sizeof(float));
+ float *filter_im = (float*) malloc(fft_size * sizeof(float));
+ arts_fft_float (fft_size, 1, re, 0, filter_re, filter_im);
+
+ for(int i=0;i<filtersize;i++)
+ {
+ filter[i] = filter_re[(i+fft_size-filtersize/2) & (fft_size-1)]
+ * blackmanWindow(float(i+1)/float(filtersize+1));
+ }
+ free(re);
+ free(filter_re);
+ free(filter_im);
+}
+
+namespace Arts {
+
+class Synth_STEREO_FIR_EQUALIZER_impl
+ : virtual public Synth_STEREO_FIR_EQUALIZER_skel,
+ virtual public StdSynthModule
+{
+ vector<GraphPoint> _frequencies;
+ long _taps;
+ unsigned long bpos;
+ double filter[256];
+ float lbuffer[256];
+ float rbuffer[256];
+
+public:
+ Synth_STEREO_FIR_EQUALIZER_impl()
+ {
+ _frequencies.push_back(GraphPoint(0.0,1.0));
+ _frequencies.push_back(GraphPoint(1.0,1.0));
+ _taps = 3;
+ for(bpos = 0; bpos < 256; bpos++)
+ lbuffer[bpos] = rbuffer[bpos] = 0.0;
+
+ calcFilter();
+ }
+ vector<GraphPoint> *frequencies() {
+ return new vector<GraphPoint>(_frequencies);
+ }
+ void frequencies(const vector<GraphPoint>& newFrequencies)
+ {
+ _frequencies = newFrequencies;
+
+ calcFilter();
+ }
+ long taps()
+ {
+ return _taps;
+ }
+ void taps(long newTaps)
+ {
+ arts_return_if_fail(newTaps >= 3 && newTaps <= 255);
+
+ if(!odd(newTaps))
+ newTaps++;
+ _taps = newTaps;
+
+ calcFilter();
+ }
+ void calcFilter()
+ {
+ firapprox(filter, _taps, _frequencies);
+ }
+ void calculateBlock(unsigned long samples)
+ {
+ for(unsigned i=0;i<samples;i++)
+ {
+ double lval = 0.0;
+ double rval = 0.0;
+ lbuffer[bpos & 255] = inleft[i];
+ rbuffer[bpos & 255] = inright[i];
+
+ for(int j=0;j<_taps;j++)
+ {
+ lval += lbuffer[(bpos-j) & 255] * filter[j];
+ rval += rbuffer[(bpos-j) & 255] * filter[j];
+ }
+ outleft[i] = lval;
+ outright[i] = rval;
+ bpos++;
+ }
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_STEREO_FIR_EQUALIZER_impl);
+
+class StereoFirEqualizerGuiFactory_impl : public StereoFirEqualizerGuiFactory_skel
+{
+public:
+ Widget createGui(Object equalizer);
+};
+
+REGISTER_IMPLEMENTATION(StereoFirEqualizerGuiFactory_impl);
+
+}
+
+Widget StereoFirEqualizerGuiFactory_impl::createGui(Object object)
+{
+ KGlobal::locale()->insertCatalogue( "artsmodules" );
+ arts_return_val_if_fail(!object.isNull(), Arts::Widget::null());
+
+ Synth_STEREO_FIR_EQUALIZER equalizer = DynamicCast(object);
+ arts_return_val_if_fail(!equalizer.isNull(), Arts::Widget::null());
+
+ VBox vbox;
+ vbox.show();
+
+ Graph g;
+ g.parent(vbox);
+ g.width(400);
+ g.height(230);
+ g.caption(i18n("a graph").utf8().data());
+ g.minx(0.0);
+ g.maxx(1.0);
+ g.miny(0.0);
+ g.maxy(1.0);
+ g.show();
+
+ GraphLine gline;
+ gline.graph(g);
+ vector<GraphPoint> *points = equalizer.frequencies();
+ gline.points(*points);
+ delete points;
+ gline.color("red");
+ gline.editable(true);
+
+ connect(gline,"points_changed", equalizer, "frequencies");
+ g._addChild(gline,"gline");
+
+ SpinBox spinbox;
+ spinbox.caption(i18n("channels").utf8().data());
+ spinbox.min(3); spinbox.max(255);
+ spinbox.value(equalizer.taps());
+ spinbox.parent(vbox);
+ spinbox.show();
+ connect(spinbox,"value_changed", equalizer, "taps");
+ vbox._addChild(spinbox,"spinbox");
+ vbox._addChild(g,"g");
+
+ return vbox;
+}
diff --git a/arts/modules/effects/synth_stereo_pitch_shift_fft_impl.cc b/arts/modules/effects/synth_stereo_pitch_shift_fft_impl.cc
new file mode 100644
index 00000000..390fd04e
--- /dev/null
+++ b/arts/modules/effects/synth_stereo_pitch_shift_fft_impl.cc
@@ -0,0 +1,63 @@
+#include "flowsystem.h"
+#include "artsmoduleseffects.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+class Synth_STEREO_PITCH_SHIFT_FFT_impl : virtual public Synth_STEREO_PITCH_SHIFT_FFT_skel,
+ virtual public StdSynthModule
+{
+protected:
+ Synth_PITCH_SHIFT_FFT leftPitchShift, rightPitchShift;
+
+public:
+ float speed() { return leftPitchShift.speed(); }
+ void speed(float newSpeed)
+ {
+ leftPitchShift.speed(newSpeed);
+ rightPitchShift.speed(newSpeed);
+ }
+
+ float scaleFactor() { return leftPitchShift.scaleFactor(); }
+ void scaleFactor(float newScaleFactor)
+ {
+ leftPitchShift.scaleFactor(newScaleFactor);
+ rightPitchShift.scaleFactor(newScaleFactor);
+ }
+
+ long frameSize() { return leftPitchShift.frameSize(); }
+ void frameSize(long newFrameSize)
+ {
+ leftPitchShift.frameSize(newFrameSize);
+ rightPitchShift.frameSize(newFrameSize);
+ }
+
+ long oversample() { return leftPitchShift.oversample(); }
+ void oversample(long newOversample)
+ {
+ leftPitchShift.oversample(newOversample);
+ rightPitchShift.oversample(newOversample);
+ }
+
+ void streamStart()
+ {
+ leftPitchShift.start();
+ rightPitchShift.start();
+ _node()->virtualize("inleft",leftPitchShift._node(),"inStream");
+ _node()->virtualize("outleft",leftPitchShift._node(),"outStream");
+ _node()->virtualize("inright",rightPitchShift._node(),"inStream");
+ _node()->virtualize("outright",rightPitchShift._node(),"outStream");
+ }
+
+ void streamEnd()
+ {
+ _node()->devirtualize("inleft",leftPitchShift._node(),"inStream");
+ _node()->devirtualize("outleft",leftPitchShift._node(),"outStream");
+ _node()->devirtualize("inright",rightPitchShift._node(),"inStream");
+ _node()->devirtualize("outright",rightPitchShift._node(),"outStream");
+ leftPitchShift.stop();
+ rightPitchShift.stop();
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_STEREO_PITCH_SHIFT_FFT_impl);
diff --git a/arts/modules/effects/synth_stereo_pitch_shift_impl.cc b/arts/modules/effects/synth_stereo_pitch_shift_impl.cc
new file mode 100644
index 00000000..fbf7bb07
--- /dev/null
+++ b/arts/modules/effects/synth_stereo_pitch_shift_impl.cc
@@ -0,0 +1,51 @@
+#include "flowsystem.h"
+#include "artsmoduleseffects.h"
+#include "stdsynthmodule.h"
+
+#include <artsmodulessynth.h>
+
+using namespace Arts;
+
+class Synth_STEREO_PITCH_SHIFT_impl : virtual public Synth_STEREO_PITCH_SHIFT_skel,
+ virtual public StdSynthModule
+{
+protected:
+ Synth_PITCH_SHIFT leftPitchShift, rightPitchShift;
+
+public:
+ float speed() { return leftPitchShift.speed(); }
+ void speed(float newSpeed)
+ {
+ leftPitchShift.speed(newSpeed);
+ rightPitchShift.speed(newSpeed);
+ }
+
+ float frequency() { return leftPitchShift.frequency(); }
+ void frequency(float newFrequency)
+ {
+ leftPitchShift.frequency(newFrequency);
+ rightPitchShift.frequency(newFrequency);
+ }
+
+ void streamStart()
+ {
+ leftPitchShift.start();
+ rightPitchShift.start();
+ _node()->virtualize("inleft",leftPitchShift._node(),"invalue");
+ _node()->virtualize("outleft",leftPitchShift._node(),"outvalue");
+ _node()->virtualize("inright",rightPitchShift._node(),"invalue");
+ _node()->virtualize("outright",rightPitchShift._node(),"outvalue");
+ }
+
+ void streamEnd()
+ {
+ _node()->devirtualize("inleft",leftPitchShift._node(),"invalue");
+ _node()->devirtualize("outleft",leftPitchShift._node(),"outvalue");
+ _node()->devirtualize("inright",rightPitchShift._node(),"invalue");
+ _node()->devirtualize("outright",rightPitchShift._node(),"outvalue");
+ leftPitchShift.stop();
+ rightPitchShift.stop();
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_STEREO_PITCH_SHIFT_impl);
diff --git a/arts/modules/effects/synth_voice_removal_impl.cc b/arts/modules/effects/synth_voice_removal_impl.cc
new file mode 100644
index 00000000..8aee1218
--- /dev/null
+++ b/arts/modules/effects/synth_voice_removal_impl.cc
@@ -0,0 +1,108 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002-2003 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+// $Id$
+
+#include "artsmoduleseffects.h"
+#include <stdsynthmodule.h>
+#include <c_filter_stuff.h>
+#include <algorithm>
+
+using namespace Arts;
+using namespace std;
+
+class Synth_VOICE_REMOVAL_impl : virtual public Synth_VOICE_REMOVAL_skel,
+ virtual public StdSynthModule
+{
+protected:
+ float _position, _frequency;
+ filter fleft, fright;
+
+public:
+ Synth_VOICE_REMOVAL_impl()
+ : _position( 0 )
+ , _frequency( 200 )
+ {
+ }
+
+ float position() { return _position; }
+ void position(float newPosition)
+ {
+ if(newPosition != _position)
+ {
+ _position = newPosition;
+ position_changed(newPosition);
+ }
+ }
+
+ float frequency() { return _frequency; }
+ void frequency(float newFrequency)
+ {
+ if(newFrequency != _frequency)
+ {
+ _frequency = newFrequency;
+ // the shelve-lowpass-filter is extremely sensitive to frequencies which
+ // are out of it's range (it produces NaN's then) so we'll be careful ;)
+ if(_frequency > 22000.0) _frequency = 22000.0;
+ if(_frequency < 1.0) _frequency = 1.0;
+ frequency_changed(_frequency);
+ }
+ }
+
+ void streamInit()
+ {
+ initfilter(&fleft);
+ initfilter(&fright);
+ }
+
+ void calculateBlock(unsigned long samples)
+ {
+ setfilter_shelvelowpass(&fleft,_frequency,80.0);
+ setfilter_shelvelowpass(&fright,_frequency,80.0);
+
+ unsigned long i;
+ for (i=0; i<samples; i++)
+ {
+ fleft.x = inleft[i];// * min(float(1), (1 - _position));
+ fleft.y = fleft.cx * fleft.x + fleft.cx1 * fleft.x1 + fleft.cx2
+ * fleft.x2 + fleft.cy1 * fleft.y1 + fleft.cy2 * fleft.y2;
+ fleft.x2 = fleft.x1;
+ fleft.x1 = fleft.x;
+ fleft.y2 = fleft.y1;
+ fleft.y1 = fleft.y;
+ float highleft = inleft[i] - 0.95 * fleft.y;
+
+ fright.x = inright[i];// * min(float(1), (1 + _position));
+ fright.y = fright.cx * fright.x + fright.cx1 * fright.x1 + fright.cx2
+ * fright.x2 + fright.cy1 * fright.y1 + fright.cy2 * fright.y2;
+ fright.x2 = fright.x1;
+ fright.x1 = fright.x;
+ fright.y2 = fright.y1;
+ fright.y1 = fright.y;
+ float highright = inright[i] - 0.95 * fright.y;
+
+ outleft[i] = (inleft[i] - highright);// / min(float(1), (1 - _position));
+ outright[i] = (inright[i] - highleft);// / min(float(1), (1 + _position));
+ }
+ }
+
+};
+
+REGISTER_IMPLEMENTATION(Synth_VOICE_REMOVAL_impl);
+
+// vim: sw=4 ts=4
diff --git a/arts/modules/effects/voiceremovalguifactory_impl.cc b/arts/modules/effects/voiceremovalguifactory_impl.cc
new file mode 100644
index 00000000..af666477
--- /dev/null
+++ b/arts/modules/effects/voiceremovalguifactory_impl.cc
@@ -0,0 +1,76 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+// $Id$
+
+#include "artsmoduleseffects.h"
+#include <debug.h>
+#include <connect.h>
+#include <iostream>
+
+using namespace std;
+using namespace Arts;
+
+namespace Arts {
+
+class VoiceRemovalGuiFactory_impl : public VoiceRemovalGuiFactory_skel
+{
+ public:
+ Widget createGui( Object );
+};
+
+REGISTER_IMPLEMENTATION( VoiceRemovalGuiFactory_impl );
+
+}
+
+Widget VoiceRemovalGuiFactory_impl::createGui( Object object )
+{
+ arts_return_val_if_fail( ! object.isNull(), Arts::Widget::null() );
+
+ Synth_VOICE_REMOVAL voiceremoval = DynamicCast( object );
+ arts_return_val_if_fail( ! voiceremoval.isNull(), Arts::Widget::null() );
+
+ HBox hbox;
+ hbox.width( 140 ); hbox.height( 80 );// hbox.show();
+
+ Poti position;
+ position.x( 20 ); position.y( 10 ); position.caption( "position" );
+ position.color( "grey" ); position.min( -1 ); position.max( 1 );
+ position.value( voiceremoval.position() );
+ position.range( 100 );
+ position.parent( hbox );
+ position.show();
+ connect( position, "value_changed", voiceremoval, "position" );
+ hbox._addChild( position, "positionWidget" );
+
+ Poti freq;
+ freq.x( 80 ); freq.y( 10 ); freq.caption( "freq" );
+ freq.color( "red" ); freq.min( 1 ); freq.max( 10000 );
+ freq.value( voiceremoval.frequency() );
+ freq.range( 400 );
+ freq.logarithmic( 2.0 );
+ freq.parent( hbox );
+ freq.show();
+ connect( freq, "value_changed", voiceremoval, "frequency" );
+ hbox._addChild( freq, "freqWidget" );
+
+
+ return hbox;
+}
+
+// vim: ts=4 sw=4
diff --git a/arts/modules/mixers/Makefile.am b/arts/modules/mixers/Makefile.am
new file mode 100644
index 00000000..b504bfba
--- /dev/null
+++ b/arts/modules/mixers/Makefile.am
@@ -0,0 +1,60 @@
+
+INCLUDES = \
+ -I$(top_builddir)/arts/modules \
+ -I$(top_srcdir)/arts/modules \
+ -I$(top_builddir)/arts/modules/common \
+ -I$(top_srcdir)/arts/modules/common \
+ -I$(top_builddir)/arts/modules/synth \
+ -I$(top_srcdir)/arts/modules/synth \
+ -I$(top_builddir)/arts/modules/effects \
+ -I$(top_builddir)/arts/gui/common \
+ -I$(top_srcdir)/arts/gui/common \
+ -I$(top_builddir)/arts/midi \
+ -I$(top_srcdir)/arts/midi \
+ -I$(arts_includes) \
+ $(all_includes)
+
+MCOPINCLUDES = \
+ -I$(top_srcdir)/arts/modules \
+ -I$(top_srcdir)/arts/modules/common \
+ -I$(top_srcdir)/arts/modules/synth \
+ -I$(top_srcdir)/arts/gui/common \
+ -I$(top_srcdir)/arts/midi \
+ -I$(arts_includes) \
+ $(all_includes)
+
+lib_LTLIBRARIES = libartsmodulesmixers.la
+
+libartsmodulesmixers_la_SOURCES = artsmodulesmixers.cc \
+ monosimplemixerchannel_impl.cc monosimplemixerchannelguifactory_impl.cc \
+ simplemixerchannel_impl.cc simplemixerchannelguifactory_impl.cc \
+ littlestereomixerchannel_impl.cc
+libartsmodulesmixers_la_COMPILE_FIRST = ../synth/artsmodulessynth.h \
+ ../../midi/artsmidi.h ../common/artsmodulescommon.h ../../gui/common/artsgui.h \
+ artsmodulesmixers.h
+libartsmodulesmixers_la_LIBADD = \
+ $(top_builddir)/arts/gui/common/libartsgui_idl.la \
+ $(top_builddir)/arts/modules/common/libartsmodulescommon.la \
+ $(top_builddir)/arts/modules/effects/libartsmoduleseffects.la \
+ -lartsflow -lartsflow_idl -lmcop
+
+libartsmodulesmixers_la_LDFLAGS = $(all_libraries) -L$(arts_libraries) -no-undefined
+
+artsmodulesmixers.cc artsmodulesmixers.h artsmodulesmixers.mcoptype artsmodulesmixers.mcopclass: $(srcdir)/artsmodulesmixers.idl $(MCOPIDL)
+ $(MCOPIDL) -t $(MCOPINCLUDES) $(srcdir)/artsmodulesmixers.idl
+
+DISTCLEANFILES= artsmodulesmixers.cc artsmodulesmixers.h artsmodulesmixers.mcop*
+
+artsincludedir = $(includedir)/arts
+artsinclude_HEADERS = artsmodulesmixers.h artsmodulesmixers.idl
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = artsmodulesmixers.mcoptype artsmodulesmixers.mcopclass
+
+mcopclassdir = $(libdir)/mcop/Arts
+mcopclass_DATA = \
+ mcopclass/MonoSimpleMixerChannel.mcopclass mcopclass/MonoSimpleMixerChannelGuiFactory.mcopclass \
+ mcopclass/SimpleMixerChannel.mcopclass mcopclass/SimpleMixerChannelGuiFactory.mcopclass \
+ mcopclass/LittleStereoMixerChannel.mcopclass mcopclass/LittleStereoMixerChannelGuiFactory.mcopclass
+
+littlestereomixerchannel_impl.lo: ../effects/artsmoduleseffects.h
diff --git a/arts/modules/mixers/artsmodulesmixers.idl b/arts/modules/mixers/artsmodulesmixers.idl
new file mode 100644
index 00000000..f2eb6de8
--- /dev/null
+++ b/arts/modules/mixers/artsmodulesmixers.idl
@@ -0,0 +1,81 @@
+/*
+
+ Copyright (C) 2000-2001 Stefan Westerfeld
+ stefan@space.twc.de
+ 2001-2003 Matthias Kretz
+ kretz@kde.org
+ 2002-2003 Arnold Krille
+ arnold@arnoldarts.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+/*
+* DISCLAIMER: The interfaces in artsmodules.idl (and the derived .cc/.h files)
+* DO NOT GUARANTEE BINARY COMPATIBILITY YET.
+*
+* They are intended for developers. You shouldn't expect that applications in
+* binary form will be fully compatibile with further releases of these
+* interfaces.
+*/
+
+#include <artsflow.idl>
+
+#include <artsmodulessynth.idl>
+#include <artsmodulescommon.idl>
+
+module Arts {
+
+interface SimpleMixerChannel : Environment::MixerChannel {
+ readonly attribute Synth_STD_EQUALIZER equalizerLeft, equalizerRight;
+ readonly attribute StereoEffectStack insertEffects;
+ attribute float gainLeft, gainRight;
+ attribute float pan;
+ attribute float volumeLeft, volumeRight;
+};
+
+// creates: SimpleMixerChannel
+interface SimpleMixerChannelGuiFactory : GuiFactory {
+};
+
+interface MonoSimpleMixerChannel : Environment::MixerChannel {
+ readonly attribute Synth_STD_EQUALIZER equalizer;
+ readonly attribute StereoEffectStack insertEffects;
+ attribute float gain;
+ attribute float pan;
+ attribute float volume;
+};
+
+// creates: MonoSimpleMixerChannel
+interface MonoSimpleMixerChannelGuiFactory : GuiFactory {
+};
+
+interface Synth_AUX_BUS : SynthModule {
+ attribute float level;
+ attribute long channel;
+ in audio stream invalue;
+};
+
+interface LittleStereoMixerChannel : Environment::MixerChannel {
+ attribute float volume;
+ attribute float balance;
+};
+interface LittleStereoMixerChannelGuiFactory : GuiFactory {
+};
+
+};
+
diff --git a/arts/modules/mixers/littlestereomixerchannel_impl.cc b/arts/modules/mixers/littlestereomixerchannel_impl.cc
new file mode 100644
index 00000000..ec521e6e
--- /dev/null
+++ b/arts/modules/mixers/littlestereomixerchannel_impl.cc
@@ -0,0 +1,134 @@
+ /*
+
+ Copyright ( C ) 2002-2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmodulesmixers.h"
+#include "artsmoduleseffects.h"
+
+#include <flowsystem.h>
+#include <stdsynthmodule.h>
+#include <connect.h>
+#include <debug.h>
+#include "artsgui.h"
+
+#include <klocale.h>
+
+using namespace Arts;
+
+namespace Arts {
+
+class LittleStereoMixerChannel_impl : virtual public LittleStereoMixerChannel_skel,
+ virtual public StdSynthModule
+{
+protected:
+ std::string _name;
+ Arts::StereoBalance _balance;
+ Arts::StereoVolumeControl _volume;
+public:
+ LittleStereoMixerChannel_impl()
+ {
+ arts_debug( "LittleStereo startup" );
+ if( _balance.isNull() ) arts_debug( "\n\nCouldn't create StereoBalance!!!!\n\n" );
+ if( _volume.isNull() ) arts_debug( "\n\nCouldn't create StereoVolumeControl!!!!\n\n" );
+ _balance.balance( 0 );
+ _volume.scaleFactor( 1 );
+ arts_debug( "startup ok\n" );
+ }
+
+ std::string name() { return _name; }
+ void name( const std::string& n ) { arts_debug( "Name = %s", n.c_str() ); _name = n; }
+
+ //Arts::StereoBalance balance() { return _balance; }
+
+ //Arts::StereoVolumeControl volume() { return _volume; }
+
+ float balance() { return _balance.balance(); }
+ void balance( float n ) { _balance.balance( n ); }
+
+ float volume() { return _volume.scaleFactor(); }
+ void volume( float n ) { _volume.scaleFactor( n ); }
+
+ void streamInit()
+ {
+ arts_debug( "LittleStereo::streamInit()" );
+ if( _balance.isNull() ) arts_warning( "Couldn't create StereoBalance!!!!\n" );
+ if( _volume.isNull() ) arts_warning( "Couldn't create StereoVolumeControl!!!!\n" );
+
+ arts_debug( "LittleStereo::streamInit() starts" );
+ _balance.start();
+ _volume.start();
+
+ arts_debug( "LittleStereo::streamInit() first connects" );
+ _node()->virtualize( "inleft", _balance._node(), "inleft" );
+ _node()->virtualize( "inright", _balance._node(), "inright" );
+ arts_debug( "LittleStereo::streamInit() middle connects" );
+ connect( _balance, "outleft", _volume, "inleft" );
+ connect( _balance, "outright", _volume, "inright" );
+ arts_debug( "LittleStereo::streamInit() last connects" );
+ _node()->virtualize( "outleft", _volume._node(), "outleft" );
+ _node()->virtualize( "outright", _volume._node(), "outright" );
+ arts_debug( "LittleStereo::streamInit() finished.\nbye" );
+ _balance.balance( 0 );
+ _volume.scaleFactor( 1 );
+ }
+
+ void streamEnd()
+ {
+ _balance.stop();
+ _volume.stop();
+ }
+
+};
+REGISTER_IMPLEMENTATION( LittleStereoMixerChannel_impl );
+
+class LittleStereoMixerChannelGuiFactory_impl : virtual public LittleStereoMixerChannelGuiFactory_skel
+{
+public:
+ Widget createGui( Object object )
+ {
+ arts_return_val_if_fail(!object.isNull(), Arts::Widget::null());
+ LittleStereoMixerChannel ch= DynamicCast(object);
+ arts_return_val_if_fail(!ch.isNull(), Arts::Widget::null());
+
+ Arts::LayoutBox vbox;
+ vbox.direction( Arts::TopToBottom );
+
+ Poti pan;
+ pan.caption( i18n( "pan" ).utf8().data() );
+ pan.color( "grey" ); pan.min( -1.0 ); pan.max( 1.0 );
+ pan.value( ch.balance() );
+ connect( pan, "value_changed", ch, "balance" );
+ vbox.addWidget( pan );
+
+ Fader volume;
+ volume.caption( i18n( "volume" ).utf8().data() );
+ volume.color( "red" ); volume.min( 0.01 ); volume.max( 2 );
+ //volume.logarithmic( 2 );
+ volume.value( ch.volume() );
+ connect( volume, "value_changed", ch, "volume" );
+ vbox.addWidget( volume );
+
+ return vbox;
+ }
+};
+REGISTER_IMPLEMENTATION( LittleStereoMixerChannelGuiFactory_impl );
+
+}
+
diff --git a/arts/modules/mixers/mcopclass/LittleStereoMixerChannel.mcopclass b/arts/modules/mixers/mcopclass/LittleStereoMixerChannel.mcopclass
new file mode 100644
index 00000000..eba4b12e
--- /dev/null
+++ b/arts/modules/mixers/mcopclass/LittleStereoMixerChannel.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::LittleStereoMixerChannel,Arts::Environment::MixerChannel,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulesmixers.la
diff --git a/arts/modules/mixers/mcopclass/LittleStereoMixerChannelGuiFactory.mcopclass b/arts/modules/mixers/mcopclass/LittleStereoMixerChannelGuiFactory.mcopclass
new file mode 100644
index 00000000..b6024a6b
--- /dev/null
+++ b/arts/modules/mixers/mcopclass/LittleStereoMixerChannelGuiFactory.mcopclass
@@ -0,0 +1,4 @@
+Interface=Arts::LittleStereoMixerChannelGuiFactory,Arts::GuiFactory,Arts::Object
+CanCreate=Arts::LittleStereoMixerChannel
+Language=C++
+Library=libartsmodulesmixers.la
diff --git a/arts/modules/mixers/mcopclass/MonoSimpleMixerChannel.mcopclass b/arts/modules/mixers/mcopclass/MonoSimpleMixerChannel.mcopclass
new file mode 100644
index 00000000..4ed15c42
--- /dev/null
+++ b/arts/modules/mixers/mcopclass/MonoSimpleMixerChannel.mcopclass
@@ -0,0 +1,3 @@
+Interface=Arts::MonoSimpleMixerChannel,Arts::Environment::MixerChannel,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulesmixers.la
diff --git a/arts/modules/mixers/mcopclass/MonoSimpleMixerChannelGuiFactory.mcopclass b/arts/modules/mixers/mcopclass/MonoSimpleMixerChannelGuiFactory.mcopclass
new file mode 100644
index 00000000..41c2b719
--- /dev/null
+++ b/arts/modules/mixers/mcopclass/MonoSimpleMixerChannelGuiFactory.mcopclass
@@ -0,0 +1,4 @@
+Interface=Arts::MonoSimpleMixerChannelGuiFactory,Arts::GuiFactory,Arts::Object
+CanCreate=Arts::MonoSimpleMixerChannel
+Language=C++
+Library=libartsmodulesmixers.la
diff --git a/arts/modules/mixers/mcopclass/SimpleMixerChannel.mcopclass b/arts/modules/mixers/mcopclass/SimpleMixerChannel.mcopclass
new file mode 100644
index 00000000..4b8dbb87
--- /dev/null
+++ b/arts/modules/mixers/mcopclass/SimpleMixerChannel.mcopclass
@@ -0,0 +1,3 @@
+Interface=Arts::SimpleMixerChannel,Arts::Environment::MixerChannel,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulesmixers.la
diff --git a/arts/modules/mixers/mcopclass/SimpleMixerChannelGuiFactory.mcopclass b/arts/modules/mixers/mcopclass/SimpleMixerChannelGuiFactory.mcopclass
new file mode 100644
index 00000000..f4b38c3e
--- /dev/null
+++ b/arts/modules/mixers/mcopclass/SimpleMixerChannelGuiFactory.mcopclass
@@ -0,0 +1,4 @@
+Interface=Arts::SimpleMixerChannelGuiFactory,Arts::GuiFactory,Arts::Object
+CanCreate=Arts::SimpleMixerChannel
+Language=C++
+Library=libartsmodulesmixers.la
diff --git a/arts/modules/mixers/monosimplemixerchannel_impl.cc b/arts/modules/mixers/monosimplemixerchannel_impl.cc
new file mode 100644
index 00000000..04bad0ce
--- /dev/null
+++ b/arts/modules/mixers/monosimplemixerchannel_impl.cc
@@ -0,0 +1,106 @@
+#include "artsmodulesmixers.h"
+#include "flowsystem.h"
+#include "stdsynthmodule.h"
+#include "connect.h"
+
+namespace Arts {
+class MonoSimpleMixerChannel_impl : virtual public MonoSimpleMixerChannel_skel,
+ virtual public StdSynthModule
+{
+protected:
+ Synth_STD_EQUALIZER _equalizer;
+ StereoEffectStack _insertEffects;
+ Synth_MUL mulGain;
+ Synth_MUL mulVolumeLeft, mulVolumeRight;
+ float _gain, _pan, _volume, pLeft, pRight;
+ std::string _name;
+public:
+ MonoSimpleMixerChannel_impl()
+ : _gain(1.0), _pan(0), _volume(1.0), pLeft(1), pRight(1)
+ {
+ setValue(mulVolumeLeft,"invalue2",_volume*pLeft);
+ setValue(mulVolumeRight,"invalue2",_volume*pRight);
+ setValue(mulGain,"invalue2",_gain);
+ }
+
+ Synth_STD_EQUALIZER equalizer() { return _equalizer; }
+ StereoEffectStack insertEffects() { return _insertEffects; }
+
+ float gain() { return _gain; }
+ void gain(float g)
+ {
+ if(g != _gain) {
+ _gain = g;
+ setValue(mulGain,"invalue2",g);
+ gain_changed(g);
+ }
+ }
+
+ float volume() { return _volume; }
+ void volume(float v)
+ {
+ if(v != _volume) {
+ _volume = v;
+ setValue(mulVolumeLeft,"invalue2",v*pLeft);
+ setValue(mulVolumeRight,"invalue2",v*pRight);
+ volume_changed(v);
+ }
+ }
+
+ float pan() { return _pan; }
+ void pan(float p)
+ {
+ if(p != _pan)
+ {
+ _pan = p;
+ pLeft = 1.0;
+ pRight = 1.0;
+ if(p > 0)
+ pLeft = 1-p;
+ else
+ pRight = 1+p;
+ setValue(mulVolumeLeft,"invalue2",_volume*pLeft);
+ setValue(mulVolumeRight,"invalue2",_volume*pRight);
+ pan_changed(p);
+ }
+ }
+
+ std::string name() { return _name; }
+ void name(const std::string& newName)
+ {
+ if(_name != newName) {
+ _name = newName;
+ name_changed(newName);
+ }
+ }
+
+ void streamInit()
+ {
+ _equalizer.start();
+ mulVolumeLeft.start();
+ mulVolumeRight.start();
+ mulGain.start();
+ //_insertEffects.start();
+
+ _node()->virtualize("inleft",mulGain._node(),"invalue1");
+ connect(mulGain,"outvalue",_equalizer,"invalue");
+ //connect(_equalizer,"outvalue",_insertEffects,"inleft");
+ //connect(_insertEffects,"outleft",mulVolume,"invalue1");
+ connect(_equalizer,"outvalue",mulVolumeLeft,"invalue1");
+ connect(_equalizer,"outvalue",mulVolumeRight,"invalue1");
+ _node()->virtualize("outleft",mulVolumeLeft._node(),"outvalue");
+ _node()->virtualize("outright",mulVolumeRight._node(),"outvalue");
+
+ }
+ void streamEnd()
+ {
+ _equalizer.stop();
+ //_insertEffects.stop();
+ mulVolumeLeft.stop();
+ mulVolumeRight.stop();
+ mulGain.stop();
+ }
+};
+REGISTER_IMPLEMENTATION(MonoSimpleMixerChannel_impl);
+}
+
diff --git a/arts/modules/mixers/monosimplemixerchannelguifactory_impl.cc b/arts/modules/mixers/monosimplemixerchannelguifactory_impl.cc
new file mode 100644
index 00000000..d97f1aa4
--- /dev/null
+++ b/arts/modules/mixers/monosimplemixerchannelguifactory_impl.cc
@@ -0,0 +1,96 @@
+#include "artsmodulesmixers.h"
+#include "debug.h"
+#include "connect.h"
+
+#include <kglobal.h>
+#include <klocale.h>
+
+namespace Arts {
+
+ class MonoSimpleMixerChannelGuiFactory_impl : virtual public MonoSimpleMixerChannelGuiFactory_skel
+ {
+ public:
+ Widget createGui(Object object)
+ {
+ KGlobal::locale()->insertCatalogue( "artsmodules" );
+ arts_return_val_if_fail(!object.isNull(), Arts::Widget::null());
+ MonoSimpleMixerChannel ch= DynamicCast(object);
+ arts_return_val_if_fail(!ch.isNull(), Arts::Widget::null());
+
+ Arts::LayoutBox vbox;
+ vbox.direction( Arts::TopToBottom );
+
+ Poti gain;
+ gain.caption(i18n("gain").utf8().data());
+ gain.color("red"); gain.min(0.01); gain.max(4);
+ gain.value(ch.gain());
+ connect(gain,"value_changed", ch, "gain");
+ vbox.addWidget( gain );
+
+ Arts::PopupBox eqbox;
+ eqbox.name( i18n( "EQ" ).utf8().data() );
+ eqbox.direction( Arts::TopToBottom );
+ vbox.addWidget( eqbox );
+
+ Arts::LayoutBox eq;
+ eq.direction( Arts::TopToBottom );
+ eqbox.widget( eq );
+
+ Poti high;
+ high.caption(i18n("volume","high").utf8().data());
+ high.color("blue"); high.min(-12); high.max(12);
+ high.value(ch.equalizer().high());
+ connect(high,"value_changed", ch.equalizer(), "high");
+ eq.addWidget( high );
+
+ Poti mid;
+ mid.caption(i18n("volume","mid").utf8().data());
+ mid.color("blue"); mid.min(-12); mid.max(12);
+ mid.value(ch.equalizer().mid());
+ connect(mid,"value_changed", ch.equalizer(), "mid");
+ eq.addWidget( mid );
+
+ Poti low;
+ low.caption(i18n("volume","low").utf8().data());
+ low.color("blue"); low.min(-12); low.max(12);
+ low.value(ch.equalizer().low());
+ connect(low,"value_changed", ch.equalizer(), "low");
+ eq.addWidget( low );
+
+ Poti frequency;
+ frequency.caption(i18n("frequency").utf8().data());
+ frequency.color("darkgreen"); frequency.min(20); frequency.max(10000);
+ frequency.value(ch.equalizer().frequency());
+ frequency.logarithmic(2.0);
+ connect(frequency,"value_changed", ch.equalizer(), "frequency");
+ eq.addWidget( frequency );
+
+ Poti q;
+ q.caption(i18n( "q" ).utf8().data());
+ q.color("darkgreen"); q.min(0.01); q.max(10);
+ q.value(ch.equalizer().q());
+ q.logarithmic(2.0);
+ connect(q,"value_changed", ch.equalizer(), "q");
+ eq.addWidget( q );
+
+ Poti pan;
+ pan.caption(i18n("pan").utf8().data());
+ pan.color("grey"); pan.min(-1.0); pan.max(1.0);
+ pan.value(ch.pan());
+ connect(pan,"value_changed",ch,"pan");
+ vbox.addWidget( pan );
+
+ Fader volume;
+ volume.caption(i18n("volume").utf8().data());
+ volume.color("red"); volume.min(0.01); volume.max(4);
+ volume.value(ch.volume());
+ connect(volume,"value_changed", ch, "volume");
+ vbox.addWidget( volume );
+
+ return vbox;
+ }
+ };
+ REGISTER_IMPLEMENTATION(MonoSimpleMixerChannelGuiFactory_impl);
+}
+
+// vim:ts=4:sw=4
diff --git a/arts/modules/mixers/simplemixerchannel_impl.cc b/arts/modules/mixers/simplemixerchannel_impl.cc
new file mode 100644
index 00000000..0c00768a
--- /dev/null
+++ b/arts/modules/mixers/simplemixerchannel_impl.cc
@@ -0,0 +1,132 @@
+#include "artsmodulesmixers.h"
+#include "flowsystem.h"
+#include "stdsynthmodule.h"
+#include "connect.h"
+
+namespace Arts {
+class SimpleMixerChannel_impl : virtual public SimpleMixerChannel_skel,
+ virtual public StdSynthModule
+{
+protected:
+ Synth_STD_EQUALIZER _equalizerLeft, _equalizerRight;
+ StereoEffectStack _insertEffects;
+ Synth_MUL mulGainLeft, mulGainRight;
+ Synth_MUL mulVolumeLeft, mulVolumeRight;
+ float _gainLeft, _gainRight, _pan, _volumeLeft, _volumeRight, pLeft, pRight;
+ std::string _name;
+public:
+ SimpleMixerChannel_impl()
+ : _gainLeft(1.0), _gainRight(1.0), _pan(0), _volumeLeft(1.0), _volumeRight(1.0), pLeft(1), pRight(1)
+ {
+ setValue(mulVolumeLeft,"invalue2",_volumeLeft*pLeft);
+ setValue(mulVolumeRight,"invalue2",_volumeRight*pRight);
+ setValue(mulGainLeft,"invalue2",_gainLeft);
+ setValue(mulGainRight,"invalue2",_gainRight);
+ }
+
+ Synth_STD_EQUALIZER equalizerLeft() { return _equalizerLeft; }
+ Synth_STD_EQUALIZER equalizerRight() { return _equalizerRight; }
+ StereoEffectStack insertEffects() { return _insertEffects; }
+
+ float gainLeft() { return _gainLeft; }
+ void gainLeft(float g)
+ {
+ if(g != _gainLeft) {
+ _gainLeft = g;
+ setValue(mulGainLeft,"invalue2",g);
+ gainLeft_changed(g);
+ }
+ }
+
+ float gainRight() { return _gainRight; }
+ void gainRight(float g)
+ {
+ if(g != _gainRight) {
+ _gainRight = g;
+ setValue(mulGainRight,"invalue2",g);
+ gainRight_changed(g);
+ }
+ }
+
+ float volumeLeft() { return _volumeLeft; }
+ void volumeLeft(float v)
+ {
+ if(v != _volumeLeft) {
+ _volumeLeft = v;
+ setValue(mulVolumeLeft,"invalue2",v*pLeft);
+ volumeLeft_changed(v);
+ }
+ }
+
+ float volumeRight() { return _volumeRight; }
+ void volumeRight(float v)
+ {
+ if(v != _volumeRight) {
+ _volumeRight = v;
+ setValue(mulVolumeRight,"invalue2",v*pRight);
+ volumeRight_changed(v);
+ }
+ }
+
+ float pan() { return _pan; }
+ void pan(float p)
+ {
+ if(p != _pan)
+ {
+ _pan = p;
+ pLeft = 1.0;
+ pRight = 1.0;
+ if(p > 0)
+ pLeft = 1-p;
+ else
+ pRight = 1+p;
+ setValue(mulVolumeLeft,"invalue2",_volumeLeft*pLeft);
+ setValue(mulVolumeRight,"invalue2",_volumeRight*pRight);
+ pan_changed(p);
+ }
+ }
+
+ std::string name() { return _name; }
+ void name(const std::string& newName)
+ {
+ if(_name != newName) {
+ _name = newName;
+ name_changed(newName);
+ }
+ }
+
+ void streamInit()
+ {
+ _equalizerLeft.start();
+ _equalizerRight.start();
+ _insertEffects.start();
+ mulVolumeLeft.start();
+ mulVolumeRight.start();
+ mulGainLeft.start();
+ mulGainRight.start();
+
+ _node()->virtualize("inleft",mulGainLeft._node(),"invalue1");
+ _node()->virtualize("inright",mulGainRight._node(),"invalue1");
+ connect(mulGainLeft,"outvalue",_equalizerLeft,"invalue");
+ connect(mulGainRight,"outvalue",_equalizerRight,"invalue");
+ connect(_equalizerLeft,"outvalue",_insertEffects,"inleft");
+ connect(_equalizerRight,"outvalue",_insertEffects,"inright");
+ connect(_insertEffects,"outleft",mulVolumeLeft,"invalue1");
+ connect(_insertEffects,"outright",mulVolumeRight,"invalue1");
+ _node()->virtualize("outleft",mulVolumeLeft._node(),"outvalue");
+ _node()->virtualize("outright",mulVolumeRight._node(),"outvalue");
+ }
+ void streamEnd()
+ {
+ _equalizerLeft.stop();
+ _equalizerRight.stop();
+ _insertEffects.stop();
+ mulVolumeLeft.stop();
+ mulVolumeRight.stop();
+ mulGainLeft.stop();
+ mulGainRight.stop();
+ }
+};
+REGISTER_IMPLEMENTATION(SimpleMixerChannel_impl);
+}
+
diff --git a/arts/modules/mixers/simplemixerchannelguifactory_impl.cc b/arts/modules/mixers/simplemixerchannelguifactory_impl.cc
new file mode 100644
index 00000000..0f585eb1
--- /dev/null
+++ b/arts/modules/mixers/simplemixerchannelguifactory_impl.cc
@@ -0,0 +1,86 @@
+#include "artsmodulesmixers.h"
+#include "debug.h"
+#include "connect.h"
+
+#include <kglobal.h>
+#include <klocale.h>
+
+namespace Arts {
+ class SimpleMixerChannelGuiFactory_impl : virtual public SimpleMixerChannelGuiFactory_skel {
+ public:
+ Widget createGui(Object object)
+ {
+ KGlobal::locale()->insertCatalogue( "artsmodules" );
+ arts_return_val_if_fail(!object.isNull(), Arts::Widget::null());
+ SimpleMixerChannel ch = DynamicCast(object);
+ arts_return_val_if_fail(!ch.isNull(), Arts::Widget::null());
+
+ Arts::LayoutBox vbox;
+ vbox.direction( Arts::TopToBottom );
+
+ Poti high;
+ high.caption(i18n("volume","high").utf8().data());
+ high.color("blue"); high.min(-12); high.max(12);
+ high.value(ch.equalizerLeft().high());
+ connect(high,"value_changed", ch.equalizerLeft(), "high");
+ connect(high,"value_changed", ch.equalizerRight(), "high");
+ vbox.addWidget( high );
+
+ Poti mid;
+ mid.caption(i18n("volume","mid").utf8().data());
+ mid.color("blue"); mid.min(-12); mid.max(12);
+ mid.value(ch.equalizerLeft().mid());
+ connect(mid,"value_changed", ch.equalizerLeft(), "mid");
+ connect(mid,"value_changed", ch.equalizerRight(), "mid");
+ vbox.addWidget( mid );
+
+ Poti low;
+ low.caption(i18n("volume","low").utf8().data());
+ low.color("blue"); low.min(-12); low.max(12);
+ low.value(ch.equalizerLeft().low());
+ connect(low,"value_changed", ch.equalizerLeft(), "low");
+ connect(low,"value_changed", ch.equalizerRight(), "low");
+ vbox.addWidget( low );
+
+ Poti frequency;
+ frequency.caption(i18n("frequency").utf8().data());
+ frequency.color("darkgreen"); frequency.min(200); frequency.max(10000);
+ frequency.value(ch.equalizerLeft().frequency());
+ frequency.logarithmic(2.0);
+ connect(frequency,"value_changed", ch.equalizerLeft(), "frequency");
+ connect(frequency,"value_changed", ch.equalizerRight(), "frequency");
+ vbox.addWidget( frequency );
+
+ Poti q;
+ q.caption(i18n( "q" ).utf8().data());
+ q.color("darkgreen"); q.min(0.01); q.max(10);
+ q.value(ch.equalizerLeft().q());
+ q.logarithmic(2.0);
+ connect(q,"value_changed", ch.equalizerLeft(), "q");
+ connect(q,"value_changed", ch.equalizerRight(), "q");
+ vbox.addWidget( q );
+
+ Poti pan;
+ pan.caption(i18n("pan").utf8().data());
+ pan.color("grey"); pan.min(-1.0); pan.max(1.0);
+ pan.value(ch.pan());
+ connect(pan,"value_changed",ch,"pan");
+ vbox.addWidget( pan );
+
+ Fader volume;
+ volume.caption(i18n("volume").utf8().data());
+ volume.color("red"); volume.min(0.01); volume.max(4);
+ volume.value(ch.volumeLeft());
+ connect(volume,"value_changed", ch, "volumeLeft");
+ connect(volume,"value_changed", ch, "volumeRight");
+ vbox.addWidget( volume );
+
+ return vbox;
+ }
+ };
+ REGISTER_IMPLEMENTATION(SimpleMixerChannelGuiFactory_impl);
+}
+
+
+// vim:ts=4:sw=4
+
diff --git a/arts/modules/synth/Makefile.am b/arts/modules/synth/Makefile.am
new file mode 100644
index 00000000..3995f402
--- /dev/null
+++ b/arts/modules/synth/Makefile.am
@@ -0,0 +1,64 @@
+
+INCLUDES = \
+ -I$(top_builddir)/arts/modules \
+ -I$(top_srcdir)/arts/modules \
+ -I$(top_builddir)/arts/gui/common \
+ -I$(top_builddir)/arts/midi \
+ -I$(top_builddir)/arts/runtime \
+ -I$(arts_includes) \
+ $(ARTSC_INCLUDE) $(all_includes)
+
+MCOP_INCLUDES= \
+ -I$(top_srcdir)/arts/gui/common \
+ -I$(top_srcdir)/arts/midi \
+ -I$(arts_includes)
+
+lib_LTLIBRARIES = libartsmodulessynth.la
+
+libartsmodulessynth_la_SOURCES = artsmodulessynth.cc \
+ synth_xfade_impl.cc synth_autopanner_impl.cc synth_delay_impl.cc synth_cdelay_impl.cc \
+ synth_envelope_adsr_impl.cc synth_pscale_impl.cc \
+ synth_tremolo_impl.cc synth_fx_cflanger_impl.cc synth_compressor_impl.cc \
+ synth_pitch_shift_impl.cc synth_pitch_shift_fft_impl.cc c_filter_stuff.c synth_shelve_cutoff_impl.cc synth_brickwall_limiter_impl.cc synth_std_equalizer_impl.cc synth_rc_impl.cc synth_moog_vcf_impl.cc synth_atan_saturate_impl.cc \
+ synth_fm_source_impl.cc \
+ synth_wave_tri_impl.cc synth_noise_impl.cc synth_wave_softsaw_impl.cc synth_wave_square_impl.cc synth_wave_pulse_impl.cc synth_osc_impl.cc synth_play_pat_impl.cc \
+ synth_capture_wav_impl.cc \
+ synth_midi_test_impl.cc synth_sequence_impl.cc synth_sequence_freq_impl.cc \
+ synth_midi_debug_impl.cc objectcache_impl.cc synth_nil_impl.cc synth_debug_impl.cc synth_data_impl.cc \
+ synth_div_impl.cc
+libartsmodulessynth_la_COMPILE_FIRST = artsmodulessynth.h ../../midi/artsmidi.h
+
+libartsmodulessynth_la_LIBADD = \
+ $(top_builddir)/arts/runtime/libartsbuilder.la \
+ $(top_builddir)/arts/midi/libartsmidi_idl.la \
+ -lartsflow -lartsflow_idl -lmcop
+
+#libartsmodulessynth_la_LIBADD = $(top_builddir)/arts/gui/common/libartsgui_idl.la $(top_builddir)/arts/midi/libartsmidi_idl.la -lartsflow -lartsflow_idl -lmcop $(LIBDL) $(LIB_KDEUI) $(LIB_KDECORE)
+libartsmodulessynth_la_LDFLAGS = $(all_libraries) -L$(arts_libraries) -no-undefined
+
+artsmodulessynth.cc artsmodulessynth.h artsmodulessynth.mcoptype artsmodulessynth.mcopclass: $(srcdir)/artsmodulessynth.idl $(MCOPIDL)
+ $(MCOPIDL) -t $(MCOP_INCLUDES) $(srcdir)/artsmodulessynth.idl
+
+DISTCLEANFILES= artsmodulessynth.cc artsmodulessynth.h artsmodulessynth.mcop*
+
+artsincludedir = $(includedir)/arts
+artsinclude_HEADERS = artsmodulessynth.h artsmodulessynth.idl
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = artsmodulessynth.mcoptype artsmodulessynth.mcopclass
+
+mcopclassdir = $(libdir)/mcop/Arts
+mcopclass_DATA = \
+ mcopclass/Synth_XFADE.mcopclass mcopclass/Synth_AUTOPANNER.mcopclass mcopclass/Synth_DELAY.mcopclass mcopclass/Synth_CDELAY.mcopclass \
+ mcopclass/Synth_ENVELOPE_ADSR.mcopclass mcopclass/Synth_PSCALE.mcopclass \
+ mcopclass/Synth_TREMOLO.mcopclass mcopclass/Synth_FX_CFLANGER.mcopclass mcopclass/Synth_COMPRESSOR.mcopclass \
+ mcopclass/Synth_PITCH_SHIFT.mcopclass mcopclass/Synth_PITCH_SHIFT_FFT.mcopclass mcopclass/Synth_SHELVE_CUTOFF.mcopclass mcopclass/Synth_BRICKWALL_LIMITER.mcopclass mcopclass/Synth_STD_EQUALIZER.mcopclass mcopclass/Synth_RC.mcopclass mcopclass/Synth_MOOG_VCF.mcopclass mcopclass/Synth_ATAN_SATURATE.mcopclass \
+ mcopclass/Synth_FM_SOURCE.mcopclass \
+ mcopclass/Synth_WAVE_TRI.mcopclass mcopclass/Synth_NOISE.mcopclass mcopclass/Synth_WAVE_SOFTSAW.mcopclass mcopclass/Synth_WAVE_SQUARE.mcopclass mcopclass/Synth_WAVE_PULSE.mcopclass mcopclass/Synth_OSC.mcopclass mcopclass/Synth_PLAY_PAT.mcopclass \
+ mcopclass/Synth_CAPTURE_WAV.mcopclass mcopclass/Synth_DIV.mcopclass \
+ mcopclass/Synth_MIDI_TEST.mcopclass mcopclass/Synth_SEQUENCE.mcopclass \
+ mcopclass/Synth_SEQUENCE_FREQ.mcopclass \
+ mcopclass/Synth_MIDI_DEBUG.mcopclass mcopclass/Synth_DATA.mcopclass mcopclass/Synth_DEBUG.mcopclass mcopclass/Synth_NIL.mcopclass
+
+synth_midi_test_impl.lo: ../../runtime/artsbuilder.h
+
diff --git a/arts/modules/synth/artsmodulessynth.idl b/arts/modules/synth/artsmodulessynth.idl
new file mode 100644
index 00000000..5e80f3e4
--- /dev/null
+++ b/arts/modules/synth/artsmodulessynth.idl
@@ -0,0 +1,301 @@
+/*
+
+ Copyright (C) 2000-2001 Stefan Westerfeld
+ stefan@space.twc.de
+ 2001-2003 Matthias Kretz
+ kretz@kde.org
+ 2002-2003 Arnold Krille
+ arnold@arnoldarts.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+/*
+* DISCLAIMER: The interfaces in artsmodules.idl (and the derived .cc/.h files)
+* DO NOT GUARANTEE BINARY COMPATIBILITY YET.
+*
+* They are intended for developers. You shouldn't expect that applications in
+* binary form will be fully compatibile with further releases of these
+* interfaces.
+*/
+
+#include <artsflow.idl>
+#include <artsmidi.idl>
+
+module Arts {
+
+// Arithmetic & Mixing
+
+/**
+ * Divides two audio streams
+ */
+interface Synth_DIV : SynthModule {
+ in audio stream invalue1,invalue2;
+ out audio stream outvalue;
+ default invalue1, invalue2;
+};
+
+interface Synth_XFADE : SynthModule {
+ in audio stream invalue1,invalue2,percentage;
+ out audio stream outvalue;
+};
+
+interface Synth_AUTOPANNER : SynthModule {
+ in audio stream invalue, inlfo;
+ out audio stream outvalue1, outvalue2;
+};
+
+// Delays
+
+interface Synth_DELAY : SynthModule {
+ attribute float maxdelay;
+ in audio stream invalue, time;
+ out audio stream outvalue;
+};
+
+interface Synth_CDELAY : SynthModule {
+ attribute float time;
+ in audio stream invalue;
+ out audio stream outvalue;
+};
+
+// Envelopes
+
+interface Synth_ENVELOPE_ADSR : SynthModule {
+ in audio stream active,invalue,attack,decay,sustain,release;
+ out audio stream outvalue,done;
+};
+
+interface Synth_PSCALE : SynthModule {
+ attribute float top;
+ in audio stream invalue, pos;
+ out audio stream outvalue;
+};
+
+// Effects
+
+interface Synth_TREMOLO : SynthModule {
+ in audio stream invalue, inlfo;
+ out audio stream outvalue;
+};
+
+interface Synth_FX_CFLANGER : SynthModule {
+ attribute float mintime, maxtime;
+ in audio stream invalue, lfo;
+ out audio stream outvalue;
+};
+
+interface Synth_COMPRESSOR : SynthModule {
+ attribute float attack, release, threshold, ratio, output;
+ in audio stream invalue;
+ out audio stream outvalue;
+};
+
+// Filters
+
+interface Synth_PITCH_SHIFT : SynthModule {
+ attribute float speed, frequency;
+ in audio stream invalue;
+ out audio stream outvalue;
+};
+
+interface Synth_PITCH_SHIFT_FFT : SynthModule {
+ attribute float speed, scaleFactor;
+ attribute long frameSize, oversample;
+ in audio stream inStream;
+ out audio stream outStream;
+};
+
+interface Synth_SHELVE_CUTOFF : SynthModule {
+ in audio stream invalue,frequency;
+ out audio stream outvalue;
+};
+
+interface Synth_BRICKWALL_LIMITER : SynthModule {
+ in audio stream invalue;
+ out audio stream outvalue;
+};
+
+interface Synth_STD_EQUALIZER : SynthModule {
+ attribute float low, mid, high, frequency, q;
+ in audio stream invalue;
+ out audio stream outvalue;
+};
+
+interface Synth_RC : SynthModule {
+ attribute float b, f;
+ in audio stream invalue;
+ out audio stream outvalue;
+};
+
+interface Synth_MOOG_VCF : SynthModule {
+ attribute float frequency, resonance;
+ in audio stream invalue;
+ out audio stream outvalue;
+};
+
+interface Synth_ATAN_SATURATE : SynthModule {
+ attribute float inscale;
+ in audio stream invalue;
+ out audio stream outvalue;
+};
+
+// Midi + Sequencing
+
+interface Synth_MIDI_TEST : SynthModule, MidiPort {
+ attribute string filename;
+ attribute string busname;
+};
+
+interface Synth_SEQUENCE : SynthModule {
+ attribute float speed;
+ attribute string seq;
+ out audio stream frequency, pos;
+};
+
+interface Synth_SEQUENCE_FREQ : SynthModule {
+ attribute float speed;
+ attribute string seq;
+ out audio stream frequency, pos;
+};
+
+// Oscillation & Modulation
+
+interface Synth_FM_SOURCE : SynthModule {
+ in audio stream frequency, modulator, modlevel;
+ out audio stream pos;
+};
+
+// Waveforms
+
+interface Synth_WAVE_TRI : SynthModule {
+ in audio stream pos;
+ out audio stream outvalue;
+};
+
+interface Synth_NOISE : SynthModule {
+ out audio stream outvalue;
+};
+
+interface Synth_WAVE_SQUARE : SynthModule {
+ in audio stream pos;
+ out audio stream outvalue;
+};
+
+interface Synth_WAVE_SOFTSAW : SynthModule {
+ in audio stream pos;
+ out audio stream outvalue;
+};
+
+interface Synth_WAVE_PULSE : SynthModule {
+ attribute float dutycycle;
+ in audio stream pos;
+ out audio stream outvalue;
+};
+
+enum SynthOscWaveForm {
+ soWaveSine,
+ soWaveTriangle,
+ soWaveSawRise,
+ soWaveSawFall,
+ soWavePeakRise,
+ soWavePeakFall,
+ soWaveMoogSaw,
+ soWaveSquare,
+ soWavePulseSaw
+};
+
+interface Synth_OSC : SynthModule {
+ /* streams */
+ in audio stream infrequency, modulation, inpwm, insync;
+ out audio stream outvalue, outsync;
+
+ attribute SynthOscWaveForm waveForm;
+
+ /* FM */
+ attribute boolean fmExponential;
+ attribute float fmStrength;
+ attribute float fmSelfStrength;
+
+ /* phase, frequency, fineTune */
+ attribute float phase;
+ attribute float frequency;
+ attribute long fineTune;
+
+ /* pulse width */
+ attribute float pulseWidth;
+ attribute float pulseModStrength;
+};
+
+interface Synth_PLAY_PAT : SynthModule {
+ attribute string filename;
+ in audio stream frequency;
+ out audio stream outvalue;
+};
+
+// Others
+
+/**
+ * this interface currently has probably a problem - usually, if you are
+ * using such a module, you would expect that you can specify the filename
+ * with it - BUT, if you allow this, then any instrument definition file
+ * (.arts) and similar might overwrite every file the user can access, which
+ * might not be what you want, so I currently save it to a file in
+ * /tmp/mcop-<username>/<filename>.wav (which might be unlucky since the user
+ * might not have too much space there)
+ */
+interface Synth_CAPTURE_WAV : SynthModule {
+ attribute string filename;
+ default in audio stream left, right;
+};
+
+// Tests
+
+interface Synth_NIL : SynthModule {
+};
+
+interface Synth_DEBUG : SynthModule {
+ attribute string comment;
+ in audio stream invalue;
+};
+
+interface Synth_DATA : SynthModule {
+ attribute float value;
+ out audio stream outvalue;
+};
+
+interface Synth_MIDI_DEBUG : SynthModule, MidiPort {
+};
+
+// EXPERIMENTAL MIDI
+interface ObjectCache {
+ void put(object obj, string name);
+ object get(string name);
+};
+
+interface MidiReleaseHelper : SynthModule {
+ attribute SynthModule voice;
+ attribute string name;
+ attribute ObjectCache cache;
+
+ boolean terminate();
+ in audio stream done;
+};
+// END EXPERIMENTAL MIDI
+
+};
+
diff --git a/arts/modules/synth/c_filter_stuff.c b/arts/modules/synth/c_filter_stuff.c
new file mode 100644
index 00000000..29b0d4bb
--- /dev/null
+++ b/arts/modules/synth/c_filter_stuff.c
@@ -0,0 +1,984 @@
+ /*
+
+ Copyright (C) 1998 Juhana Sadeharju
+ kouhia at nic.funet.fi
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "c_filter_stuff.h"
+#include <math.h>
+
+
+/*-- double tan(),pow(),atan2(),sqrt(),asin(); --*/
+
+#define C_MIN16 -32768
+#define C_MAX16 32767
+
+#define SR 44100
+#define PI M_PI
+
+/*
+ * Presence and Shelve filters as given in
+ * James A. Moorer
+ * The manifold joys of conformal mapping:
+ * applications to digital filtering in the studio
+ * JAES, Vol. 31, No. 11, 1983 November
+ */
+
+/*#define SPN MINDOUBLE*/
+#define SPN 0.00001
+
+double bw2angle(a,bw)
+double a,bw;
+{
+ double T,d,sn,cs,mag,delta,theta,tmp,a2,a4,asnd;
+
+ T = tan(2.0*PI*bw);
+ a2 = a*a;
+ a4 = a2*a2;
+ d = 2.0*a2*T;
+ sn = (1.0 + a4)*T;
+ cs = (1.0 - a4);
+ mag = sqrt(sn*sn + cs*cs);
+ d /= mag;
+ delta = atan2(sn,cs);
+ asnd = asin(d);
+ theta = 0.5*(PI - asnd - delta);
+ tmp = 0.5*(asnd-delta);
+ if ((tmp > 0.0) && (tmp < theta)) theta = tmp;
+ return(theta/(2.0*PI));
+}
+
+void presence(cf,boost,bw,a0,a1,a2,b1,b2)
+double cf,boost,bw,*a0,*a1,*a2,*b1,*b2;
+{
+ double a,A,F,xfmbw,C,tmp,alphan,alphad,b0,recipb0,asq,F2,a2plus1,ma2plus1;
+
+ a = tan(PI*(cf-0.25));
+ asq = a*a;
+ A = pow(10.0,boost/20.0);
+ if ((boost < 6.0) && (boost > -6.0)) F = sqrt(A);
+ else if (A > 1.0) F = A/sqrt(2.0);
+ else F = A*sqrt(2.0);
+ xfmbw = bw2angle(a,bw);
+
+ C = 1.0/tan(2.0*PI*xfmbw);
+ F2 = F*F;
+ tmp = A*A - F2;
+ if (fabs(tmp) <= SPN) alphad = C;
+ else alphad = sqrt(C*C*(F2-1.0)/tmp);
+ alphan = A*alphad;
+
+ a2plus1 = 1.0 + asq;
+ ma2plus1 = 1.0 - asq;
+ *a0 = a2plus1 + alphan*ma2plus1;
+ *a1 = 4.0*a;
+ *a2 = a2plus1 - alphan*ma2plus1;
+
+ b0 = a2plus1 + alphad*ma2plus1;
+ *b2 = a2plus1 - alphad*ma2plus1;
+
+ recipb0 = 1.0/b0;
+ *a0 *= recipb0;
+ *a1 *= recipb0;
+ *a2 *= recipb0;
+ *b1 = *a1;
+ *b2 *= recipb0;
+}
+
+void shelve(cf,boost,a0,a1,a2,b1,b2)
+double cf,boost,*a0,*a1,*a2,*b1,*b2;
+{
+ double a,A,F,tmp,b0,recipb0,asq,F2,gamma2,siggam2,gam2p1;
+ double gamman,gammad,ta0,ta1,ta2,tb0,tb1,tb2,aa1,ab1;
+
+ a = tan(PI*(cf-0.25));
+ asq = a*a;
+ A = pow(10.0,boost/20.0);
+ if ((boost < 6.0) && (boost > -6.0)) F = sqrt(A);
+ else if (A > 1.0) F = A/sqrt(2.0);
+ else F = A*sqrt(2.0);
+
+ F2 = F*F;
+ tmp = A*A - F2;
+ if (fabs(tmp) <= SPN) gammad = 1.0;
+ else gammad = pow((F2-1.0)/tmp,0.25);
+ gamman = sqrt(A)*gammad;
+
+ gamma2 = gamman*gamman;
+ gam2p1 = 1.0 + gamma2;
+ siggam2 = 2.0*sqrt(2.0)/2.0*gamman;
+ ta0 = gam2p1 + siggam2;
+ ta1 = -2.0*(1.0 - gamma2);
+ ta2 = gam2p1 - siggam2;
+
+ gamma2 = gammad*gammad;
+ gam2p1 = 1.0 + gamma2;
+ siggam2 = 2.0*sqrt(2.0)/2.0*gammad;
+ tb0 = gam2p1 + siggam2;
+ tb1 = -2.0*(1.0 - gamma2);
+ tb2 = gam2p1 - siggam2;
+
+ aa1 = a*ta1;
+ *a0 = ta0 + aa1 + asq*ta2;
+ *a1 = 2.0*a*(ta0+ta2)+(1.0+asq)*ta1;
+ *a2 = asq*ta0 + aa1 + ta2;
+
+ ab1 = a*tb1;
+ b0 = tb0 + ab1 + asq*tb2;
+ *b1 = 2.0*a*(tb0+tb2)+(1.0+asq)*tb1;
+ *b2 = asq*tb0 + ab1 + tb2;
+
+ recipb0 = 1.0/b0;
+ *a0 *= recipb0;
+ *a1 *= recipb0;
+ *a2 *= recipb0;
+ *b1 *= recipb0;
+ *b2 *= recipb0;
+}
+
+void initfilter(filter *f)
+{
+ f->x1 = 0.0;
+ f->x2 = 0.0;
+ f->y1 = 0.0;
+ f->y2 = 0.0;
+ f->y = 0.0;
+}
+
+void setfilter_presence(f,freq,boost,bw)
+filter *f;
+double freq,boost,bw;
+{
+ presence(freq/(double)SR,boost,bw/(double)SR,
+ &f->cx,&f->cx1,&f->cx2,&f->cy1,&f->cy2);
+ f->cy1 = -f->cy1;
+ f->cy2 = -f->cy2;
+}
+
+void setfilter_shelve(filter *f, double freq, double boost)
+{
+ shelve(freq/(double)SR,boost,
+ &f->cx,&f->cx1,&f->cx2,&f->cy1,&f->cy2);
+ f->cy1 = -f->cy1;
+ f->cy2 = -f->cy2;
+}
+
+void setfilter_shelvelowpass(filter *f, double freq, double boost)
+{
+ double gain;
+
+ gain = pow(10.0,boost/20.0);
+ shelve(freq/(double)SR,boost,
+ &f->cx,&f->cx1,&f->cx2,&f->cy1,&f->cy2);
+ f->cx /= gain;
+ f->cx1 /= gain;
+ f->cx2 /= gain;
+ f->cy1 = -f->cy1;
+ f->cy2 = -f->cy2;
+}
+
+/*
+ * As in ''An introduction to digital filter theory'' by Julius O. Smith
+ * and in Moore's book; I use the normalized version in Moore's book.
+ */
+void setfilter_2polebp(f,freq,R)
+filter *f;
+double freq,R;
+{
+ double theta;
+
+ theta = 2.0*PI*freq/(double)SR;
+ f->cx = 1.0-R;
+ f->cx1 = 0.0;
+ f->cx2 = -(1.0-R)*R;
+ f->cy1 = 2.0*R*cos(theta);
+ f->cy2 = -R*R;
+}
+
+/*
+ * As in
+ * Stanley A. White
+ * Design of a digital biquadratic peaking or notch filter
+ * for digital audio equalization
+ * JAES, Vol. 34, No. 6, 1986 June
+ */
+void setfilter_peaknotch(f,freq,M,bw)
+filter *f;
+double freq,M,bw;
+{
+ double w0,om,ta,d, p=0.0 /* prevents compiler warning */;
+
+ w0 = 2.0*PI*freq;
+ if ((1.0/sqrt(2.0) < M) && (M < sqrt(2.0))) {
+ fprintf(stderr,"peaknotch filter: 1/sqrt(2) < M < sqrt(2)\n");
+ exit(-1);
+ }
+ if (M <= 1.0/sqrt(2.0)) p = sqrt(1.0-2.0*M*M);
+ if (sqrt(2.0) <= M) p = sqrt(M*M-2.0);
+ om = 2.0*PI*bw;
+ ta = tan(om/((double)SR*2.0));
+ d = p+ta;
+ f->cx = (p+M*ta)/d;
+ f->cx1 = -2.0*p*cos(w0/(double)SR)/d;
+ f->cx2 = (p-M*ta)/d;
+ f->cy1 = 2.0*p*cos(w0/(double)SR)/d;
+ f->cy2 = -(p-ta)/d;
+}
+
+/*
+ * Some JAES's article on ladder filter.
+ * freq (Hz), gdb (dB), bw (Hz)
+ */
+void setfilter_peaknotch2(f,freq,gdb,bw)
+filter *f;
+double freq,gdb,bw;
+{
+ double k,w,bwr,abw,gain;
+
+ k = pow(10.0,gdb/20.0);
+ w = 2.0*PI*freq/(double)SR;
+ bwr = 2.0*PI*bw/(double)SR;
+ abw = (1.0-tan(bwr/2.0))/(1.0+tan(bwr/2.0));
+ gain = 0.5*(1.0+k+abw-k*abw);
+ f->cx = 1.0*gain;
+ f->cx1 = gain*(-2.0*cos(w)*(1.0+abw))/(1.0+k+abw-k*abw);
+ f->cx2 = gain*(abw+k*abw+1.0-k)/(abw-k*abw+1.0+k);
+ f->cy1 = 2.0*cos(w)/(1.0+tan(bwr/2.0));
+ f->cy2 = -abw;
+}
+
+double applyfilter(f,x)
+filter *f;
+double x;
+{
+ f->x = x;
+ f->y = f->cx * f->x + f->cx1 * f->x1 + f->cx2 * f->x2
+ + f->cy1 * f->y1 + f->cy2 * f->y2;
+ f->x2 = f->x1;
+ f->x1 = f->x;
+ f->y2 = f->y1;
+ f->y1 = f->y;
+ return(f->y);
+}
+
+/*
+ * aRts doesn't need the functions below this line
+ */
+
+#if 0
+int saturate16(x)
+double x;
+{
+ if (x > 32765.0) {
+ return(32765);
+ } else if (x < -32765.0) {
+ return(-32765);
+ } else return((int)x);
+}
+
+void initdelay(d,n)
+delay *d;
+int n;
+{
+ int i;
+
+ d->len = n;
+ d->wloc = n-1;
+ d->rloc = 0;
+ d->buf = (double *)malloc(n*sizeof(double));
+ for(i = 0; i < n; i++) d->buf[i] = 0.0;
+}
+
+double readdelay(d)
+delay *d;
+{
+ double y;
+
+ y = d->buf[d->rloc];
+ d->rloc++;
+ if (d->rloc == d->len) d->rloc = 0;
+ return(y);
+}
+
+void writedelay(d,x)
+delay *d;
+double x;
+{
+ d->buf[d->wloc] = x;
+ d->wloc++;
+ if (d->wloc == d->len) d->wloc = 0;
+}
+
+void initringbufferd(rb,n)
+ringbufferd *rb;
+int n;
+{
+ int i;
+
+ rb->len = n;
+ rb->wloc = n-1;
+ rb->buf = (double *)malloc(n*sizeof(double));
+ for(i = 0; i < n; i++) rb->buf[i] = 0.0;
+}
+
+double readringbufferd(rb,n)
+ringbufferd *rb;
+int n;
+{
+ int i;
+
+ if (n >= rb->len) return(0.0);
+ i = rb->wloc - n;
+ if (i < 0) i += rb->len;
+ return(rb->buf[i]);
+}
+
+void writeringbufferd(rb,x)
+ringbufferd *rb;
+double x;
+{
+ rb->buf[rb->wloc] = x;
+ rb->wloc++;
+ if (rb->wloc == rb->len) rb->wloc = 0;
+}
+
+void initringbufferi(rb,n)
+ringbufferi *rb;
+int n;
+{
+ int i;
+
+ rb->len = n;
+ rb->wloc = n-1;
+ rb->buf = (int *)malloc(n*sizeof(int));
+ for(i = 0; i < n; i++) rb->buf[i] = 0;
+}
+
+int readringbufferi(rb,n)
+ringbufferi *rb;
+int n;
+{
+ int i;
+
+ if (n >= rb->len) return(0);
+ i = rb->wloc - n;
+ if (i < 0) i += rb->len;
+ return(rb->buf[i]);
+}
+
+void writeringbufferi(rb,x)
+ringbufferi *rb;
+int x;
+{
+ rb->buf[rb->wloc] = x;
+ rb->wloc++;
+ if (rb->wloc == rb->len) rb->wloc = 0;
+}
+
+unsigned char buffc[BUFFSIZE];
+int buffi[BUFFSIZE];
+/* int buffs[C_MAXCHANNELS][BUFFSIZE]; */
+int **buffs;
+
+
+int makenodes(n)
+int n;
+{
+ int *p;
+ int i;
+
+ p = (int *)malloc(n*sizeof(int *));
+ for(i = 0; i < n; i++) p[i] = (int)(int *)0;
+ return((int)p);
+}
+
+int makeints(n)
+int n;
+{
+ int *p;
+ int i;
+
+ p = (int *)malloc(n*sizeof(int));
+ for(i = 0; i < n; i++) p[i] = 0;
+ return((int)p);
+}
+
+/*
+
+constant memory size:
+ (i) one big malloc
+ (ii) many mallocs, upper limit in doing mallocs
+
+
+
+ */
+
+
+
+/* new routines:
+ *
+ * readbufb(n) -- read n bytes (8 bits) from stream
+ * readbufs(n) -- read n shorts (16 bits) from stream
+ * readbufi(n) -- read n ints (32 bits) from stream
+ * readbuff(n) -- read n floats (32 bits) from stream
+ *
+ * bufb2bufs() -- convert byte buffer to short buffer
+ * bufb2bufi() -- convert byte buffer to int buffer
+ * bufb2buff() -- convert byte buffer to float buffer
+ * bufs2bufb() -- convert short buffer to byte buffer
+ * bufi2bufb() -- convert int buffer to byte buffer
+ * buff2bufb() -- convert float buffer to byte buffer
+ *
+ * copychannelb() -- copy one channel from buffer to buffer
+ * copychannels() -- copy one channel from buffer to buffer
+ * copychanneli() -- copy one channel from buffer to buffer
+ * copychannelf() -- copy one channel from buffer to buffer
+ *
+ * multichannel buffers:
+ * buf[sample][channel]
+ * buf[channel][sample]
+ *
+ * multi to uni buffer
+ *
+ * reading and writing:
+ * uni buffer to sample[channel]
+ * multi buffer to sample[channel]
+ *
+ */
+/*
+int newfreadbufs(buf,n,p)
+short **buf;
+int n;
+ty_audiofile *p;
+{
+ if (n*p->afsc > BUFFSIZE) {
+ fprintf(stderr,"freadbufi: reading too many samples\n");
+ exit(-1);
+ }
+ l = readbufs(tmpbufs,n*p->afsc);
+ m = uni2multis(tmpbufs,l,p->afsc,buf);
+ return(m);
+}
+
+int newfreadbufi(buf,n,p)
+int **buf;
+int n;
+ty_audiofile *p;
+{
+ if (n*p->afsc > BUFFSIZE) {
+ fprintf(stderr,"freadbufi: reading too many samples\n");
+ exit(-1);
+ }
+ l = readbufi(tmpbufi,n*p->afsc);
+ m = uni2multii(tmpbufi,l,p->afsc,buf);
+ return(m);
+}
+
+int newfreadbuff(buf,n,p)
+float **buf;
+int n;
+ty_audiofile *p;
+{
+ if (n*p->afsc > BUFFSIZE) {
+ fprintf(stderr,"freadbufi: reading too many samples\n");
+ exit(-1);
+ }
+ l = readbuf(tmpbuff,n*p->afsc);
+ m = uni2multif(tmpbuff,l,p->afsc,buf);
+ return(m);
+}
+
+
+int newfreadbuf(buf,p)
+ty_buffer *buf;
+ty_audiofile *p;
+{
+
+}
+
+*/
+
+/*
+ * freadbuf() reads next n samples from the file; one sample may have
+ * several channels.
+ * Return value is the number of the samples read.
+ */
+
+int freadbuf(buf,n,p)
+int **buf;
+int n;
+ty_audiofile *p;
+{
+ int h,i,j,k,l,s;
+ unsigned int us;
+
+ if (n > BUFFSIZE) {
+ fprintf(stderr,"freadbuf reading too many samples\n");
+ exit(-1);
+ }
+ if (p->afstype == C_INTTYPE) {
+ h = 0;
+ for(j = 0; j < p->afsc; j++) {
+ l = fread(buffi,sizeof(int),n,p->affp);
+ for(i = 0; i < l; i += p->afsc) {
+ for(k = 0; k < p->afsc; k++) buf[k][h] = buffi[i+k];
+ h++;
+ }
+ }
+ } else if (p->afstype == C_FLOATTYPE) {
+ h = 0;
+ for(j = 0; j < p->afsc; j++) {
+ l = fread((float *)buffi,sizeof(float),n,p->affp);
+ for(i = 0; i < l; i += p->afsc) {
+ for(k = 0; k < p->afsc; k++) buf[k][h] = buffi[i+k];
+ h++;
+ }
+ }
+ } else {
+ h = 0;
+ for(j = 0; j < 2*p->afsc; j++) {
+ l = fread(buffc,sizeof(unsigned char),n,p->affp);
+ for(i = 0; i < l; i += 2*p->afsc) {
+ for(k = 0; k < p->afsc; k++) {
+ if (p->afstype == C_CDASBTYPE)
+ us = buffc[i+1+2*k] + (buffc[i+2*k]<<8);
+ else
+ us = buffc[i+2*k] + (buffc[i+1+2*k]<<8);
+ us = us<<16;
+ s = ((signed int)us)>>16;
+ buf[k][h] = s;
+ }
+ h++;
+ }
+ }
+ }
+ return(h);
+}
+
+
+int fwritebuf(buf,n,p)
+int **buf;
+int n;
+ty_audiofile *p;
+{
+ int h,i,j,k,l,s;
+ unsigned int us1,us2;
+
+ if (p->afstype == C_INTTYPE) {
+ h = 0;
+ for(i = 0; i < n; i++) {
+ for(k = 0; k < p->afsc; k++) {
+ buffi[h] = buf[k][i];
+ h++;
+ }
+ if (h == BUFFSIZE) {
+ l = fwrite(buffi,sizeof(int),h,p->affp);
+ if (l != h) {
+ fprintf(stderr,"fwritebuf() error\n");
+ exit(-1);
+ }
+ h = 0;
+ }
+ }
+ l = fwrite(buffi,sizeof(int),h,p->affp);
+ if (l != h) {
+ fprintf(stderr,"fwritebuf() error\n");
+ exit(-1);
+ }
+ } else {
+ h = 0;
+ for(i = 0; i < n; i++) {
+ for(k = 0; k < p->afsc; k++) {
+ s = buf[k][i];
+ if (s > C_MAX16) s = C_MAX16;
+ else if (s < C_MIN16) s = C_MIN16;
+ us1 = ((unsigned int)s)&0x000000ff;
+ us2 = (((unsigned int)s)&0x0000ff00)>>8;
+ if (p->afstype == C_CDASBTYPE) {
+ buffc[h] = (unsigned char)us2;
+ h++;
+ buffc[h] = (unsigned char)us1;
+ h++;
+ } else {
+ buffc[h] = (unsigned char)us1;
+ h++;
+ buffc[h] = (unsigned char)us2;
+ h++;
+ }
+ }
+ if (h == BUFFSIZE) {
+ l = fwrite(buffc,sizeof(unsigned char),h,p->affp);
+ if (l != h) {
+ fprintf(stderr,"fwritebuf() error\n");
+ exit(-1);
+ }
+ h = 0;
+ }
+ }
+ l = fwrite(buffc,sizeof(unsigned char),h,p->affp);
+ if (l != h) {
+ fprintf(stderr,"fwritebuf() error\n");
+ exit(-1);
+ }
+ }
+ return(n);
+}
+
+
+ty_audiofile *initaf(afm,afn,aft)
+ty_afmethod *afm;
+ty_afname *afn;
+ty_aftype *aft;
+{
+ ty_audiofile *p;
+ int i,j,k,n,s;
+ unsigned int us;
+ FILE *fp;
+
+ p = (ty_audiofile *)malloc(sizeof(ty_audiofile));
+ p->afmethod = afm->method;
+ p->afname = afn->filename;
+ p->affd = afn->fd;
+ p->afsr = aft->sr;
+ p->afsc = aft->sc;
+ p->afstype = aft->stype;
+ p->buflen = afm->buflen;
+
+ switch(p->afmethod) {
+ case C_FLOWOUTMETHOD:
+ if (p->affd == STDOUT_FILENO) {
+ fp = stdout;
+ p->afname = "stdout";
+ } else {
+ if ((fp = fopen(p->afname,"w")) == (FILE *)NULL) {
+ fprintf(stderr,"could not open file %s\n",p->afname);
+ exit(-1);
+ }
+ }
+ p->affp = fp;
+ p->buflen = BUFFSIZE;
+ p->buf = (int **)malloc(p->afsc*sizeof(int *));
+ for(i = 0; i < p->afsc; i++)
+ p->buf[i] = (int *)malloc(p->buflen*sizeof(int));
+ p->bloc = 0;
+ break;
+ case C_RBMETHOD:
+ if (p->affd == STDIN_FILENO) {
+ fp = stdin;
+ p->afname = "stdin";
+ } else {
+ if ((fp = fopen(p->afname,"r")) == (FILE *)NULL) {
+ fprintf(stderr,"could not open file %s\n",p->afname);
+ exit(-1);
+ }
+ }
+ p->affp = fp;
+ p->buf = (int **)malloc(p->afsc*sizeof(int *));
+ for(i = 0; i < p->afsc; i++)
+ p->buf[i] = (int *)malloc(p->buflen*sizeof(int));
+ n = freadbuf(p->buf,MINBUFFSIZE,p);
+ if (n != MINBUFFSIZE) {
+ fprintf(stderr,"could not read file %s\n",p->afname);
+ fprintf(stderr,"%i\n",n);
+ exit(-1);
+ }
+ p->bloc = 0;
+ p->eloc = n-1;
+ p->rbbtime = 0;
+ p->rbetime = n-1;
+ break;
+ case C_AIMROMETHOD:
+ p->buf = (int **)malloc(p->afsc*sizeof(int *));
+ if ((fp = fopen(p->afname,"r")) == (FILE *)NULL) {
+ fprintf(stderr,"could not open file %s\n",p->afname);
+ exit(-1);
+ }
+ (void)fseek(fp,(long)0,SEEK_END);
+ p->buflen = ftell(fp)/p->afsc;
+ fclose(fp);
+ switch(p->afstype) {
+ case C_CDATYPE:
+ p->buflen /= 2;
+ break;
+ case C_CDASBTYPE:
+ p->buflen /= 2;
+ break;
+ case C_INTTYPE:
+ p->buflen /= sizeof(int);
+ break;
+ }
+ for(i = 0; i < p->afsc; i++)
+ p->buf[i] = (int *)malloc(p->buflen*sizeof(int));
+
+ if ((fp = fopen(p->afname,"r")) == (FILE *)NULL) {
+ fprintf(stderr,"could not open file %s\n",p->afname);
+ exit(-1);
+ }
+ p->affp = fp;
+ j = 0;
+ while ((n = freadbuf(buffs,BUFFSIZE,p)) != 0) {
+ for(i = 0; i < n; i++,j++) {
+ for(k = 0; k < p->afsc; k++) p->buf[k][j] = buffs[k][i];
+ }
+ }
+ fclose(fp);
+ break;
+ }
+ return(p);
+}
+
+
+void bye()
+{
+ ty_audiofile *p;
+ int i,l;
+
+ for(i = 0; i < C_MAXAUDIOFILES; i++) {
+ p = gaf[i];
+ if (p != (ty_audiofile *)0) {
+ switch(p->afmethod) {
+ case C_FLOWOUTMETHOD:
+ l = fwritebuf(p->buf,p->bloc,p);
+ if (l != p->bloc) {
+ fprintf(stderr,"could not write to %s\n",p->afname);
+ exit(-1);
+ }
+ fclose(p->affp);
+ break;
+ case C_RBMETHOD:
+ fclose(p->affp);
+ break;
+ }
+ }
+ }
+}
+
+
+ty_sample *makesample(sc)
+int sc;
+{
+ ty_sample *p;
+
+ p = (ty_sample *)malloc(sizeof(ty_sample));
+ p->sc = sc;
+ return(p);
+}
+
+
+int readsample(p,n,s)
+ty_audiofile *p;
+int n;
+ty_sample *s;
+{
+ int i,j,k,dt,l;
+ FILE *fp;
+ ty_sample *out;
+
+ /*
+ out = makesample(p->afsc);
+ / * out->time = n; * /
+ */
+
+ out = s;
+
+ switch(p->afmethod) {
+ case C_RBMETHOD:
+ for(;;) {
+ if ((p->rbbtime <= n) && (n <= p->rbetime)) {
+ dt = n - p->rbbtime;
+ l = p->bloc + dt;
+ if (l >= p->buflen) l -= p->buflen;
+ for(i = 0; i < p->afsc; i++) out->buf[i] = p->buf[i][l];
+ return(TRUE);
+ } else {
+ if (n < p->rbbtime) {
+ fprintf(stderr,"n = %i\n",n);
+ fprintf(stderr,"ring buffer has dropped this sample already\n");
+ exit(-1);
+ }
+ l = freadbuf(buffs,BUFFSIZE,p);
+ if (l == 0) return(FALSE);
+ for(i = 0; i < l; i++) {
+ p->eloc++;
+ if (p->eloc >= p->buflen) p->eloc -= p->buflen;
+ p->rbetime++;
+ if (p->eloc == p->bloc) {
+ p->bloc++;
+ if (p->bloc >= p->buflen) p->bloc -= p->buflen;
+ p->rbbtime++;
+ }
+ for(j = 0; j < p->afsc; j++) {
+ p->buf[j][p->eloc] = buffs[j][i];
+ }
+ }
+ }
+ }
+ break;
+ case C_AIMROMETHOD:
+ if ((n < 0) || (n >= p->buflen)) return(FALSE);
+ for(i = 0; i < p->afsc; i++) out->buf[i] = p->buf[i][n];
+ return(TRUE);
+ break;
+ }
+
+}
+
+
+int writesample(p,n,s)
+ty_audiofile *p;
+int n;
+ty_sample *s;
+{
+ int i,j,k,dt,l;
+ FILE *fp;
+ ty_sample *out;
+
+ switch(p->afmethod) {
+ case C_FLOWOUTMETHOD:
+ for(i = 0; i < p->afsc; i++) p->buf[i][p->bloc] = s->buf[i];
+ p->bloc++;
+ if (p->bloc == p->buflen) {
+ p->bloc = 0;
+ l = fwritebuf(p->buf,p->buflen,p);
+ if (l != p->buflen) {
+ fprintf(stderr,"could not write to %s\n",p->afname);
+ exit(-1);
+ }
+ }
+ break;
+ case C_AIMRWMETHOD:
+ if ((n < 0) || (n >= p->buflen)) return(FALSE);
+ for(i = 0; i < p->afsc; i++) p->buf[i][n] = s->buf[i];
+ break;
+ }
+ return(TRUE);
+}
+
+ty_afmethod *afmethod_flowout()
+{
+ ty_afmethod *p;
+
+ p = (ty_afmethod *)malloc(sizeof(ty_afmethod));
+ p->method = C_FLOWOUTMETHOD;
+ return(p);
+}
+
+ty_afmethod *afmethod_rb(n)
+int n;
+{
+ ty_afmethod *p;
+
+ if (n <= BUFFSIZE) {
+ fprintf(stderr,"RB buffer size should be greater than BUFFSIZE\n");
+ exit(-1);
+ }
+ p = (ty_afmethod *)malloc(sizeof(ty_afmethod));
+ p->method = C_RBMETHOD;
+ p->buflen = n;
+ return(p);
+}
+
+ty_afmethod *afmethod_aimro()
+{
+ ty_afmethod *p;
+
+ p = (ty_afmethod *)malloc(sizeof(ty_afmethod));
+ p->method = C_AIMROMETHOD;
+ return(p);
+}
+
+ty_afname *afname(s)
+char *s;
+{
+ ty_afname *p;
+
+ p = (ty_afname *)malloc(sizeof(ty_afname));
+ p->filename = strdup(s);
+ p->fd = -1;
+ return(p);
+}
+
+/* stdin and stdout could have their own read and write routines
+ * but this could be a second solution
+ */
+ty_afname *afname_stdin()
+{
+ ty_afname *p;
+
+ p = (ty_afname *)malloc(sizeof(ty_afname));
+ p->filename = (char *)0;
+ p->fd = STDIN_FILENO;
+ return(p);
+}
+
+ty_afname *afname_stdout()
+{
+ ty_afname *p;
+
+ p = (ty_afname *)malloc(sizeof(ty_afname));
+ p->filename = (char *)0;
+ p->fd = STDOUT_FILENO;
+ return(p);
+}
+
+ty_aftype *aftype(sr,sc,stype)
+int sr,sc,stype;
+{
+ ty_aftype *p;
+
+ p = (ty_aftype *)malloc(sizeof(ty_aftype));
+ p->sr = sr;
+ p->sc = sc;
+ p->stype = stype;
+ return(p);
+}
+
+ty_aftype *aftype_defstereo()
+{
+ return(aftype(44100,2,C_CDATYPE));
+}
+
+
+ty_audiofile *initaf_aimdefstereo(filename)
+char *filename;
+{
+ return(initaf(afmethod_aimro(),afname(filename),aftype_defstereo()));
+}
+
+
+ty_audiofile *initaf_stdin()
+{
+ return(initaf(afmethod_rb(C_RBBUFSIZE),afname_stdin(),aftype_defstereo()));
+}
+
+void init()
+{
+ int i;
+
+ for(i = 0; i < C_MAXAUDIOFILES; i++) {
+ gaf[i] = (ty_audiofile *)0;
+ }
+
+ buffs = (int **)malloc(C_MAXCHANNELS*sizeof(int *));
+ for(i = 0; i < C_MAXCHANNELS; i++)
+ buffs[i] = (int *)malloc(BUFFSIZE*sizeof(int));
+
+}
+
+
+#endif
diff --git a/arts/modules/synth/c_filter_stuff.h b/arts/modules/synth/c_filter_stuff.h
new file mode 100644
index 00000000..ca7ef385
--- /dev/null
+++ b/arts/modules/synth/c_filter_stuff.h
@@ -0,0 +1,246 @@
+ /*
+
+ Copyright (C) 1998 Juhana Sadeharju
+ kouhia at nic.funet.fi
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef C_FILTER_STUFF_H
+#define C_FILTER_STUFF_H
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ double cx,cx1,cx2,cy1,cy2;
+ double x,x1,x2,y,y1,y2;
+} filter;
+
+void presence();
+void shelve();
+void initfilter(filter *f);
+void setfilter_presence();
+void setfilter_shelve(filter *f, double freq, double boost);
+void setfilter_shelvelowpass(filter *f, double freq, double boost);
+void setfilter_2polebp();
+void setfilter_peaknotch();
+void setfilter_peaknotch2();
+double applyfilter();
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+ * aRts doesn't need the flow stuff that's in music_orig.c - just the filters
+ */
+#if 0
+
+#define STRBUFSIZE 200
+#define TRUE 1
+#define FALSE 0
+
+/* must be divisible by 6 and 8
+ * max 2 items (ints or bytes) per sample value, 3 or 4 channels */
+#define MINBUFFSIZE 2*3*4
+#define BUFFSIZE 512*MINBUFFSIZE
+
+#define C_RBBUFSIZE 10*44100
+#define C_MAXCHANNELS 4
+
+/*
+ * afmethod = 0, ring buffer
+ * 1, swap ro bufs
+ * 2, swap rw bufs
+ * 3, all in memory ro
+ * 4, all in memory rw
+ * afname = filename for the audio file;
+ * in case of multipart file, the filenames are <filename>.aa, etc.
+ * affd = file descriptor number, if it is preset to be STDIN_FILENO or
+ * STDOUT_FILENO, then the filename has no effect, otherwise affd
+ * is set at the init time if afmethod == 0
+ * afsr = samplerate
+ * afsc = samplechannels
+ * afstype = 0, 16 bit (standard CDA format)
+ * 1, direct copy of int variable
+ * noofbufs = number of swap buffers
+ * buflen = length of swap buffers
+ * realbuflen = length of swap buffers with respect to the data;
+ * different from buflen only if content is load from
+ * the end of audiofile
+ * btime = time of the first sample in buffers
+ * etime = time of the last sample in buffers
+ *
+ * **buf and ***bufs since one array is for one channel
+ */
+
+typedef struct {
+ int afmethod;
+ char *afname;
+ FILE *affp;
+ int affd;
+ int afsr;
+ int afsc;
+ int afstype;
+ int buflen;
+ /* ring buffer
+ * int buflen;
+ */
+ int **buf;
+ int bloc;
+ int eloc;
+ int rbbtime;
+ int rbetime;
+ /* swap buffers
+ * int buflen;
+ */
+ int ***bufs;
+ int noofbufs;
+ int *realbuflen;
+ int *btime;
+ int *etime;
+ int bufupdatemethod;
+ /* all in memory
+ * int buflen;
+ * int *buf;
+ */
+ /* buffer updating method info */
+ int *modifiedbuf;
+ int *bufpri;
+ int npri;
+ int cpri;
+} ty_audiofile;
+
+/*
+ * Priority entries are numbered 0,1,2,... no two same number
+ * in two buffers. The buffer which will be swapped is the buffer
+ * with highest priority (i.e. nobufs-1). When a buffer is swapped,
+ * the priority is set to 1 and priorities of all other buffers are
+ * lowered down by one.
+ * When a sample is read, the priorities are set for each Nth read.
+ */
+
+typedef struct {
+ int method;
+ int noofbufs;
+ int buflen;
+} ty_afmethod;
+
+#define C_FLOWOUTMETHOD 0
+#define C_RBMETHOD 1
+#define C_SWAPROMETHOD 2
+#define C_SWAPRWMETHOD 3
+#define C_AIMROMETHOD 4
+#define C_AIMRWMETHOD 5
+
+typedef struct {
+ char *filename;
+ int fd;
+} ty_afname;
+
+typedef struct {
+ int sr;
+ int sc;
+ int stype;
+} ty_aftype;
+
+#define C_CDATYPE 0
+#define C_CDASBTYPE 1 /* swap bytes */
+#define C_INTTYPE 2
+#define C_FLOATTYPE 3
+
+typedef struct {
+ int sc;
+ int time;
+ int buf[C_MAXCHANNELS];
+} ty_sample;
+
+#define C_MAXAUDIOFILES 20
+
+typedef struct {
+ int len;
+ int rloc,wloc;
+ double *buf;
+} delay;
+
+typedef struct {
+ int len;
+ int wloc;
+ double *buf;
+} ringbufferd;
+
+typedef struct {
+ int len;
+ int wloc;
+ int *buf;
+} ringbufferi;
+
+typedef struct {
+ int n;
+ double gain;
+ filter f;
+} rbreaddev;
+
+
+ty_audiofile *gaf[C_MAXAUDIOFILES];
+
+int makenodes();
+int makeints();
+/*
+int freadbuf();
+int fwritebuf();
+*/
+ty_audiofile *initaf();
+void bye();
+ty_sample *makesample();
+int readsample();
+int writesample();
+ty_afmethod *afmethod_flowout();
+ty_afmethod *afmethod_rb();
+ty_afmethod *afmethod_aimro();
+ty_afname *afname();
+ty_afname *afname_stdin();
+ty_afname *afname_stdout();
+ty_aftype *aftype();
+ty_aftype *aftype_defstereo();
+ty_audiofile *initaf_aimdefstereo();
+ty_audiofile *initaf_stdin();
+void init();
+int saturate16();
+void initdelay();
+double readdelay();
+void writedelay();
+void initringbufferd();
+double readringbufferd();
+void writeringbufferd();
+void initringbufferi();
+int readringbufferi();
+void writeringbufferi();
+#endif
+
+#endif // C_FILTER_STUFF_H
+
diff --git a/arts/modules/synth/mcopclass/Synth_ATAN_SATURATE.mcopclass b/arts/modules/synth/mcopclass/Synth_ATAN_SATURATE.mcopclass
new file mode 100644
index 00000000..d714659a
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_ATAN_SATURATE.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_ATAN_SATURATE,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_AUTOPANNER.mcopclass b/arts/modules/synth/mcopclass/Synth_AUTOPANNER.mcopclass
new file mode 100644
index 00000000..a4af2832
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_AUTOPANNER.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_AUTOPANNER,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_BRICKWALL_LIMITER.mcopclass b/arts/modules/synth/mcopclass/Synth_BRICKWALL_LIMITER.mcopclass
new file mode 100644
index 00000000..a1204038
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_BRICKWALL_LIMITER.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_BRICKWALL_LIMITER,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_CAPTURE_WAV.mcopclass b/arts/modules/synth/mcopclass/Synth_CAPTURE_WAV.mcopclass
new file mode 100644
index 00000000..b2e6a6aa
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_CAPTURE_WAV.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_CAPTURE_WAV,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_CDELAY.mcopclass b/arts/modules/synth/mcopclass/Synth_CDELAY.mcopclass
new file mode 100644
index 00000000..da344bb5
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_CDELAY.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_CDELAY,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_COMPRESSOR.mcopclass b/arts/modules/synth/mcopclass/Synth_COMPRESSOR.mcopclass
new file mode 100644
index 00000000..7611908a
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_COMPRESSOR.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_COMPRESSOR,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_DATA.mcopclass b/arts/modules/synth/mcopclass/Synth_DATA.mcopclass
new file mode 100644
index 00000000..bbff2ac1
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_DATA.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_DATA,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_DEBUG.mcopclass b/arts/modules/synth/mcopclass/Synth_DEBUG.mcopclass
new file mode 100644
index 00000000..52615982
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_DEBUG.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_DEBUG,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_DELAY.mcopclass b/arts/modules/synth/mcopclass/Synth_DELAY.mcopclass
new file mode 100644
index 00000000..0651df87
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_DELAY.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_DELAY,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_DIV.mcopclass b/arts/modules/synth/mcopclass/Synth_DIV.mcopclass
new file mode 100644
index 00000000..0a2a3eec
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_DIV.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_DIV,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_ENVELOPE_ADSR.mcopclass b/arts/modules/synth/mcopclass/Synth_ENVELOPE_ADSR.mcopclass
new file mode 100644
index 00000000..eab45052
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_ENVELOPE_ADSR.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_ENVELOPE_ADSR,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_FM_SOURCE.mcopclass b/arts/modules/synth/mcopclass/Synth_FM_SOURCE.mcopclass
new file mode 100644
index 00000000..49fce727
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_FM_SOURCE.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_FM_SOURCE,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_FX_CFLANGER.mcopclass b/arts/modules/synth/mcopclass/Synth_FX_CFLANGER.mcopclass
new file mode 100644
index 00000000..cf7519c8
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_FX_CFLANGER.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_FX_CFLANGER,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_MIDI_DEBUG.mcopclass b/arts/modules/synth/mcopclass/Synth_MIDI_DEBUG.mcopclass
new file mode 100644
index 00000000..b9f03597
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_MIDI_DEBUG.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_MIDI_DEBUG,Arts::MidiPort,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_MIDI_TEST.mcopclass b/arts/modules/synth/mcopclass/Synth_MIDI_TEST.mcopclass
new file mode 100644
index 00000000..80ea8661
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_MIDI_TEST.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_MIDI_TEST,Arts::MidiPort,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_MOOG_VCF.mcopclass b/arts/modules/synth/mcopclass/Synth_MOOG_VCF.mcopclass
new file mode 100644
index 00000000..92e572b5
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_MOOG_VCF.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_MOOG_VCF,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_NIL.mcopclass b/arts/modules/synth/mcopclass/Synth_NIL.mcopclass
new file mode 100644
index 00000000..f11a7fb6
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_NIL.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_NIL,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_NOISE.mcopclass b/arts/modules/synth/mcopclass/Synth_NOISE.mcopclass
new file mode 100644
index 00000000..bf37079d
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_NOISE.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_NOISE,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_OSC.mcopclass b/arts/modules/synth/mcopclass/Synth_OSC.mcopclass
new file mode 100644
index 00000000..5cd19123
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_OSC.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_OSC,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_PITCH_SHIFT.mcopclass b/arts/modules/synth/mcopclass/Synth_PITCH_SHIFT.mcopclass
new file mode 100644
index 00000000..962b4a75
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_PITCH_SHIFT.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_PITCH_SHIFT,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_PITCH_SHIFT_FFT.mcopclass b/arts/modules/synth/mcopclass/Synth_PITCH_SHIFT_FFT.mcopclass
new file mode 100644
index 00000000..d37b3190
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_PITCH_SHIFT_FFT.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_PITCH_SHIFT_FFT,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_PLAY_PAT.mcopclass b/arts/modules/synth/mcopclass/Synth_PLAY_PAT.mcopclass
new file mode 100644
index 00000000..578f6222
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_PLAY_PAT.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_PLAY_PAT,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_PSCALE.mcopclass b/arts/modules/synth/mcopclass/Synth_PSCALE.mcopclass
new file mode 100644
index 00000000..52d076b3
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_PSCALE.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_PSCALE,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_RC.mcopclass b/arts/modules/synth/mcopclass/Synth_RC.mcopclass
new file mode 100644
index 00000000..5d40d363
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_RC.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_RC,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_SEQUENCE.mcopclass b/arts/modules/synth/mcopclass/Synth_SEQUENCE.mcopclass
new file mode 100644
index 00000000..0eef9733
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_SEQUENCE.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_SEQUENCE,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_SEQUENCE_FREQ.mcopclass b/arts/modules/synth/mcopclass/Synth_SEQUENCE_FREQ.mcopclass
new file mode 100644
index 00000000..efa69000
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_SEQUENCE_FREQ.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_SEQUENCE_FREQ,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_SHELVE_CUTOFF.mcopclass b/arts/modules/synth/mcopclass/Synth_SHELVE_CUTOFF.mcopclass
new file mode 100644
index 00000000..1d25a6e8
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_SHELVE_CUTOFF.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_SHELVE_CUTOFF,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_STD_EQUALIZER.mcopclass b/arts/modules/synth/mcopclass/Synth_STD_EQUALIZER.mcopclass
new file mode 100644
index 00000000..8b2b65a6
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_STD_EQUALIZER.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_STD_EQUALIZER,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_TREMOLO.mcopclass b/arts/modules/synth/mcopclass/Synth_TREMOLO.mcopclass
new file mode 100644
index 00000000..a937293d
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_TREMOLO.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_TREMOLO,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_WAVE_PULSE.mcopclass b/arts/modules/synth/mcopclass/Synth_WAVE_PULSE.mcopclass
new file mode 100644
index 00000000..8ae14ccf
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_WAVE_PULSE.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_WAVE_PULSE,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_WAVE_SOFTSAW.mcopclass b/arts/modules/synth/mcopclass/Synth_WAVE_SOFTSAW.mcopclass
new file mode 100644
index 00000000..116680bf
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_WAVE_SOFTSAW.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_WAVE_SOFTSAW,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_WAVE_SQUARE.mcopclass b/arts/modules/synth/mcopclass/Synth_WAVE_SQUARE.mcopclass
new file mode 100644
index 00000000..5033f281
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_WAVE_SQUARE.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_WAVE_SQUARE,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_WAVE_TRI.mcopclass b/arts/modules/synth/mcopclass/Synth_WAVE_TRI.mcopclass
new file mode 100644
index 00000000..a5cbebdc
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_WAVE_TRI.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_WAVE_TRI,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/mcopclass/Synth_XFADE.mcopclass b/arts/modules/synth/mcopclass/Synth_XFADE.mcopclass
new file mode 100644
index 00000000..0b1166a8
--- /dev/null
+++ b/arts/modules/synth/mcopclass/Synth_XFADE.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=Arts::Synth_XFADE,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartsmodulessynth.la
diff --git a/arts/modules/synth/objectcache_impl.cc b/arts/modules/synth/objectcache_impl.cc
new file mode 100644
index 00000000..d58163a6
--- /dev/null
+++ b/arts/modules/synth/objectcache_impl.cc
@@ -0,0 +1,73 @@
+/*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmodulessynth.h"
+#include <iostream>
+
+using namespace Arts;
+using namespace std;
+
+namespace Arts {
+
+
+class ObjectCache_impl : public ObjectCache_skel {
+protected:
+ typedef map<string, list<Object> *> ObjectCacheMap;
+ ObjectCacheMap objects;
+
+public:
+ ~ObjectCache_impl()
+ {
+ ObjectCacheMap::iterator i;
+ for(i=objects.begin(); i != objects.end(); i++)
+ {
+ cout << "ObjectCache: deleting remaining " <<
+ i->first << " objects" << endl;
+ delete i->second;
+ }
+ }
+
+ void put(Object obj, const string& name)
+ {
+ list<Object> *l = objects[name];
+
+ if(l == 0) objects[name] = l = new list<Object>;
+ l->push_back(obj);
+ }
+
+ Object get(const string& name)
+ {
+ list<Object> *l = objects[name];
+ if(l && !l->empty())
+ {
+ Object result = l->front();
+ l->pop_front();
+
+ return result;
+ }
+ return Object::null();
+ }
+};
+
+REGISTER_IMPLEMENTATION(ObjectCache_impl);
+}
+
diff --git a/arts/modules/synth/synth_atan_saturate_impl.cc b/arts/modules/synth/synth_atan_saturate_impl.cc
new file mode 100644
index 00000000..d9220d26
--- /dev/null
+++ b/arts/modules/synth/synth_atan_saturate_impl.cc
@@ -0,0 +1,54 @@
+ /*
+
+ Copyright (C) 2000 Jeff Tranter
+ tranter@pobox.com
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+// This can be used to get the input signal to the normalized range
+// between -1 and 1 that Synth_PLAY can process. The louder the input
+// signal, the more the signal is distorted by this module. For very
+// small input signals, the output signal is about the input signal
+// (no change).
+
+class Synth_ATAN_SATURATE_impl : virtual public Synth_ATAN_SATURATE_skel,
+ virtual public StdSynthModule
+{
+protected:
+ float _inscale;
+
+public:
+ float inscale() { return _inscale; }
+
+ void inscale(float newInscale) { _inscale = newInscale; }
+
+ void calculateBlock(unsigned long samples)
+ {
+ for (unsigned long i=0; i<samples; i++)
+ outvalue[i] = atan(invalue[i]*_inscale)/(M_PI/2.0);
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_ATAN_SATURATE_impl);
diff --git a/arts/modules/synth/synth_autopanner_impl.cc b/arts/modules/synth/synth_autopanner_impl.cc
new file mode 100644
index 00000000..9f49f0b1
--- /dev/null
+++ b/arts/modules/synth/synth_autopanner_impl.cc
@@ -0,0 +1,50 @@
+/*
+
+ Copyright (C) 2000 Jeff Tranter
+ tranter@pobox.com
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+// An Autopanner is used to automatically pan the input signal between
+// the left and the right output. This makes mixes more lively. A
+// standard application would be a guitar or lead sound. Connect a
+// LFO, a sine or saw wave for example to "inlfo" and select a
+// frequency between 0.1 and 5Hz for a traditional effect or even more
+// for Special FX.
+
+class Synth_AUTOPANNER_impl : virtual public Synth_AUTOPANNER_skel,
+ virtual public StdSynthModule
+{
+public:
+ void calculateBlock(unsigned long samples)
+ {
+ unsigned long i;
+ for(i=0; i<samples; i++)
+ {
+ outvalue1[i] = invalue[i] * (1.0 - (inlfo[i] + 1.0) / 2.0);
+ outvalue2[i] = invalue[i] * (inlfo[i] + 1.0) / 2.0;
+ }
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_AUTOPANNER_impl);
diff --git a/arts/modules/synth/synth_brickwall_limiter_impl.cc b/arts/modules/synth/synth_brickwall_limiter_impl.cc
new file mode 100644
index 00000000..107fe47a
--- /dev/null
+++ b/arts/modules/synth/synth_brickwall_limiter_impl.cc
@@ -0,0 +1,51 @@
+/*
+
+ Copyright (C) 2000 Jeff Tranter
+ tranter@pobox.com
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+// A brickwall limiter is used to protect equipment (and your ears..)
+// from peaks that exceed the dynamic range of your system. It doesn't
+// sound good but it's better than digital distortion.
+
+class Synth_BRICKWALL_LIMITER_impl : virtual public Synth_BRICKWALL_LIMITER_skel,
+ virtual public StdSynthModule
+{
+public:
+ void calculateBlock(unsigned long samples)
+ {
+ unsigned long i;
+ for(i=0; i<samples; i++)
+ {
+ if (invalue[i] > 1.0)
+ outvalue[i] = 1.0;
+ else if (invalue[i] < -1.0)
+ outvalue[i] = -1.0;
+ else
+ outvalue[i] = invalue[i];
+ }
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_BRICKWALL_LIMITER_impl);
diff --git a/arts/modules/synth/synth_capture_wav_impl.cc b/arts/modules/synth/synth_capture_wav_impl.cc
new file mode 100644
index 00000000..15416761
--- /dev/null
+++ b/arts/modules/synth/synth_capture_wav_impl.cc
@@ -0,0 +1,169 @@
+ /*
+
+ Copyright (C) 2000, 2001 Stefan Westerfeld
+ stefan@space.twc.de, Matthias Kretz <kretz@kde.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmodulessynth.h"
+#include "convert.h"
+#include "mcoputils.h"
+#include "stdsynthmodule.h"
+#include "debug.h"
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+
+using namespace std;
+namespace Arts {
+
+class Synth_CAPTURE_WAV_impl :virtual public Synth_CAPTURE_WAV_skel,
+ virtual public StdSynthModule
+{
+ bool audioinit, scaleerr, running;
+ int audiofd, byteorder, v,datalen,channels;
+
+ unsigned char *outblock;
+ unsigned long maxsamples;
+
+ string _filename;
+
+/****
+
+ WAV writing code and header taken from kwave. Many thanks go to
+ Martin Wilz who has written this ;)
+
+ ****/
+
+ struct wavheader
+ {
+ char riffid[4];
+ long filelength;
+ char wavid[4];
+ char fmtid[4];
+ long fmtlength;
+ short int mode;
+ short int channels;
+ long rate;
+ long AvgBytesPerSec;
+ short int BlockAlign;
+ short int bitspersample;
+ } header;
+
+public:
+ Synth_CAPTURE_WAV_impl();
+ void streamInit();
+ void calculateBlock(unsigned long samples);
+ void streamEnd();
+ string filename() { return _filename; }
+ void filename( const string &newFilename );
+};
+
+Synth_CAPTURE_WAV_impl::Synth_CAPTURE_WAV_impl()
+ : running(false), _filename( "capture" )
+{
+}
+
+void Synth_CAPTURE_WAV_impl::streamInit()
+{
+ /*
+ * we use createFilePath to prevent the usual symlink security issues
+ * in /tmp - add .wav manually as createFilePath substitutes . with _
+ */
+ string filename = MCOPUtils::createFilePath(_filename)+ ".wav";
+ audiofd = open(filename.c_str(),O_WRONLY|O_CREAT|O_TRUNC,0644);
+
+/* write header */
+
+ int rate=44100;
+ int bit=16;
+
+ channels = 2; /* hardcoded here - make it a parameter? */
+
+ arts_info("capturing output to %s", filename.c_str());
+ datalen=0;
+
+ strncpy (header.riffid,"RIFF",4);
+ strncpy (header.wavid,"WAVE",4);
+ strncpy (header.fmtid,"fmt ",4);
+ header.fmtlength=16;
+ header.filelength=sizeof(struct wavheader);
+ header.mode=1;
+ header.channels=channels;
+ header.rate=rate;
+ header.AvgBytesPerSec=rate*bit/8;
+ header.BlockAlign=channels*bit/8;
+ header.bitspersample=bit;
+
+ write(audiofd,&header,sizeof (struct wavheader));
+ write(audiofd,"data",4);
+ write(audiofd,&datalen,4);
+
+ maxsamples = 0;
+ outblock = 0;
+ v = 0;
+ running = true;
+}
+
+void Synth_CAPTURE_WAV_impl::calculateBlock(unsigned long samples)
+{
+ if(samples > maxsamples)
+ {
+ maxsamples = samples;
+ outblock = (unsigned char *)realloc(outblock, maxsamples * 4);
+ // 2 channels, 16 bit
+ }
+
+ if(channels == 1)
+ convert_mono_float_16le(samples,left,outblock);
+
+ if(channels == 2)
+ convert_stereo_2float_i16le(samples,left,right,outblock);
+
+ write(audiofd,outblock,samples*channels*2);
+ datalen += samples*channels*2;
+}
+
+void Synth_CAPTURE_WAV_impl::streamEnd()
+{
+/* rewrite header which now contains the correct size of the file */
+ lseek(audiofd,0,SEEK_SET);
+ header.filelength=sizeof(struct wavheader)+datalen;
+ write(audiofd,&header,sizeof (struct wavheader));
+ write(audiofd,"data",4);
+ write(audiofd,&datalen,4);
+
+ close(audiofd);
+
+ running = false;
+}
+void Synth_CAPTURE_WAV_impl::filename( const string &newFilename )
+{
+ if(_filename != newFilename) {
+ _filename = newFilename;
+ if(running)
+ {
+ streamEnd();
+ streamInit();
+ }
+ filename_changed(newFilename);
+ }
+}
+
+REGISTER_IMPLEMENTATION(Synth_CAPTURE_WAV_impl);
+
+}
diff --git a/arts/modules/synth/synth_cdelay_impl.cc b/arts/modules/synth/synth_cdelay_impl.cc
new file mode 100644
index 00000000..afac3144
--- /dev/null
+++ b/arts/modules/synth/synth_cdelay_impl.cc
@@ -0,0 +1,125 @@
+/*
+
+ Copyright (C) 2000 Jeff Tranter
+ tranter@pobox.com
+ 2001 Matthias Kretz
+ kretz@kde.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+#include <math.h>
+#include <cstring>
+
+using namespace Arts;
+
+// This delays the input signal for an amount of time. The time
+// specification can be any number greater or equal zero.
+// The delay is constant during the calculation, that means it
+// can't be modified. This saves computing time as no interpolation is
+// done, and is useful for recursive structures. Actually it can be
+// modified, but without interpolation it won't sound too good. See
+// the description for Synth_DELAY.
+
+class Synth_CDELAY_impl : virtual public Synth_CDELAY_skel,
+ virtual public StdSynthModule
+{
+protected:
+ unsigned long _buffersize;
+ unsigned long _bitmask;
+ float *_buffer; // holds the data to be delayed (variable size)
+ float _delaytime;
+ unsigned int _readpos;
+ unsigned int _writepos;
+
+public:
+ Synth_CDELAY_impl() : _buffersize( 0 ), _bitmask( 0 ), _buffer( 0 ), _delaytime( 0 ), _readpos( 0 ), _writepos( 0 )
+ {
+ }
+
+ ~Synth_CDELAY_impl()
+ {
+ delete[] _buffer;
+ }
+
+ float time() { return _delaytime; }
+
+ void time(float newTime)
+ {
+ _delaytime = newTime;
+ double n = ceil( log( double(_delaytime * samplingRateFloat )) / log( 2. ) );
+ unsigned long newbuffersize = (unsigned long)( pow( 2, n ) );
+ unsigned long newbitmask = newbuffersize - 1;
+ if( newbuffersize != _buffersize )
+ {
+ float *newbuffer = new float[newbuffersize];
+ if( newbuffersize > _buffersize ) {
+ for( unsigned long i = 0; i < _buffersize; i++ ) {
+ newbuffer[i] = _buffer[_writepos];
+ _writepos++;
+ _writepos &= newbitmask;
+ }
+ for( unsigned long i = _buffersize; i < newbuffersize; i++ )
+ newbuffer[i] = 0;
+ } else {
+ _writepos -= newbuffersize;
+ _writepos &= newbitmask;
+ for( unsigned long i = 0; i < newbuffersize; i++ ) {
+ newbuffer[i] = _buffer[_writepos];
+ _writepos++;
+ _writepos &= newbitmask;
+ }
+ }
+ _buffer = newbuffer;
+ _buffersize = newbuffersize;
+ _bitmask = newbitmask;
+ }
+ _readpos = (unsigned long)rint( _writepos - _delaytime * samplingRateFloat ) & _bitmask;
+ time_changed( _delaytime );
+ }
+
+ void streamInit()
+ {
+ // initialize buffer to all zeroes
+ if( _buffer )
+ for( unsigned long i = 0; i < _buffersize; i++ )
+ _buffer[i] = 0.0;
+ }
+
+ void calculateBlock(unsigned long samples)
+ {
+ if( ! _buffer ) {
+ memcpy( outvalue, invalue, sizeof( float ) * samples );
+ return;
+ }
+ for( unsigned long i = 0; i < samples; i++ ) {
+ _buffer[_writepos] = invalue[i];
+ outvalue[i] = _buffer[_readpos];
+ _readpos++;
+ _readpos &= _bitmask;
+ _writepos++;
+ _writepos &= _bitmask;
+ }
+ }
+};
+
+// vim:sw=4:ts=4
+
+REGISTER_IMPLEMENTATION(Synth_CDELAY_impl);
diff --git a/arts/modules/synth/synth_compressor_impl.cc b/arts/modules/synth/synth_compressor_impl.cc
new file mode 100644
index 00000000..54c7648e
--- /dev/null
+++ b/arts/modules/synth/synth_compressor_impl.cc
@@ -0,0 +1,139 @@
+/*
+
+ Copyright (C) 2001 Matthias Kretz <kretz@kde.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+#include "debug.h"
+
+#include <math.h>
+#include <string.h>
+
+#ifndef LN2
+# define LN2 0.69314718
+#endif
+
+#ifndef MAX
+# define MAX(a,b) (((a) > (b) ? (a) : (b)))
+#endif
+
+using namespace std;
+namespace Arts {
+
+class Synth_COMPRESSOR_impl : virtual public Synth_COMPRESSOR_skel,
+ virtual public StdSynthModule
+{
+protected:
+ float _attack, _release, _threshold, _ratiominus1, _output;
+ float _attackfactor, _releasefactor;
+ float _volume;
+ float _compfactor;
+ bool _autooutput;
+
+public:
+ float attack() { return _attack; }
+ float release() { return _release; }
+ float threshold() { return _threshold; }
+ float ratio() { return _ratiominus1 + 1.0; }
+ float output() { return _output; }
+
+ Synth_COMPRESSOR_impl()
+ : _threshold( 1 )
+ , _ratiominus1( -0.2 )
+ , _output( 0 )
+ , _autooutput( true )
+ {
+ newCompFactor();
+ attack( 10 );
+ release( 10 );
+ }
+
+ void newCompFactor()
+ {
+ _compfactor = _output / pow( _threshold, _ratiominus1 );
+ }
+
+ void streamInit()
+ {
+ _volume = 0;
+ }
+
+ void calculateBlock(unsigned long samples)
+ {
+ for( unsigned long i = 0; i < samples; i++ ) {
+ float delta = fabs( invalue[i] ) - _volume;
+ if( delta > 0.0 )
+ _volume += _attackfactor * delta;
+ else
+ _volume += _releasefactor * delta;
+
+ if( _volume > _threshold )
+ // compress
+ // this is what it does:
+ // UtodB(x) = 20 * log( x )
+ // dBtoU(x) = pow( 10, x / 20 )
+ // outvalue[i] = dBtoU( ( UtodB( volume ) - UtodB( threshold ) ) * ratio + UtodB( threshold ) ) / volume * output * invalue[ i ];
+ // showing that it's equal to the formula below
+ // is left as an exercise to the reader.
+ outvalue[i] = pow( _volume, _ratiominus1 ) * _compfactor * invalue[ i ];
+ else
+ outvalue[i] = invalue[i] * _output;
+ }
+ }
+
+ void attack( float newAttack )
+ { // in ms
+ _attack = newAttack;
+ // _attackfactor has to be <= 1, that's why we need the MAX here
+ _attackfactor = LN2 / MAX( _attack / 1000 * samplingRateFloat, LN2 );
+ attack_changed( newAttack );
+ }
+
+ void release( float newRelease )
+ { // in ms
+ _release = newRelease;
+ // _releasefactor has to be <= 1, that's why we need the MAX here
+ _releasefactor = LN2 / MAX( _release / 1000 * samplingRateFloat, LN2 );
+ release_changed( newRelease );
+ }
+
+ void threshold( float newThreshold )
+ { // in V not in dB
+ _threshold = newThreshold;
+ newCompFactor();
+ threshold_changed( newThreshold );
+ }
+
+ void ratio( float newRatio )
+ {
+ _ratiominus1 = newRatio - 1;
+ newCompFactor();
+ ratio_changed( newRatio );
+ }
+
+ void output( float newOutput )
+ { // in V not in dB
+ _output = newOutput;
+ newCompFactor();
+ output_changed( newOutput );
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_COMPRESSOR_impl);
+}
diff --git a/arts/modules/synth/synth_data_impl.cc b/arts/modules/synth/synth_data_impl.cc
new file mode 100644
index 00000000..7cd185d3
--- /dev/null
+++ b/arts/modules/synth/synth_data_impl.cc
@@ -0,0 +1,50 @@
+ /*
+
+ Copyright (C) 2000 Jeff Tranter
+ tranter@pobox.com
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include <stdio.h>
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+// This module outputs a constant stream of data corresponding to the
+// value given as it's parameter.
+
+class Synth_DATA_impl : virtual public Synth_DATA_skel,
+ virtual public StdSynthModule
+{
+protected:
+ float _value;
+
+public:
+ float value() { return _value; }
+
+ void value(float newValue) { _value = newValue; }
+
+ void calculateBlock(unsigned long samples)
+ {
+ for (unsigned long i=0; i<samples; i++)
+ outvalue[i] = _value;
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_DATA_impl);
diff --git a/arts/modules/synth/synth_debug_impl.cc b/arts/modules/synth/synth_debug_impl.cc
new file mode 100644
index 00000000..609290f7
--- /dev/null
+++ b/arts/modules/synth/synth_debug_impl.cc
@@ -0,0 +1,60 @@
+ /*
+
+ Copyright (C) 2000 Jeff Tranter
+ tranter@pobox.com
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include <stdio.h>
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace std;
+using namespace Arts;
+
+// You can use this for debugging. It will print out the value of the
+// signal at invalue in regular intervals (ca. 1 second), combined
+// with the comment you have specified. That way you can find out if
+// some signals stay in certain ranges, or if they are there at all.
+
+class Synth_DEBUG_impl : virtual public Synth_DEBUG_skel,
+ virtual public StdSynthModule
+{
+protected:
+ string _comment;
+ int i;
+
+public:
+ string comment() { return _comment; }
+
+ void comment(const string &newComment) { _comment = newComment; }
+
+ void streamInit() { i = 0; }
+
+ void calculateBlock(unsigned long samples)
+ {
+ for (unsigned long j=0; j<samples; j++)
+ {
+ i++;
+ if ((i % 65536) == 0)
+ printf("Synth_DEBUG: %s %f\n", _comment.c_str(), invalue[j]);
+ }
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_DEBUG_impl);
diff --git a/arts/modules/synth/synth_delay_impl.cc b/arts/modules/synth/synth_delay_impl.cc
new file mode 100644
index 00000000..f872b284
--- /dev/null
+++ b/arts/modules/synth/synth_delay_impl.cc
@@ -0,0 +1,138 @@
+/*
+
+ Copyright (C) 2000 Jeff Tranter
+ tranter@pobox.com
+ Stefan Westerfeld
+ stefan@space.twc.de
+ Jens Hahn
+ Jens.Hahn@t-online.de
+ 2001 Matthias Kretz
+ kretz@kde.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+#include <math.h>
+
+using namespace Arts;
+
+// This delays the input signal for an amount of time. The time
+// specification must be between 0 and 1 for a delay between 0 seconds
+// and 1 second.
+//
+// This kind of delay may not be used in feedback structures. This is
+// because it's a variable delay. You can modify it's length while it
+// is running, and even set it down to zero. But since in a feedback
+// structure the own output is needed to calculate the next samples, a
+// delay whose value could drop to zero during synthesis could lead to
+// a stall situation.
+//
+// Use CDELAYs in that setup, perhaps combine a small constant delay
+// (of 0.001 seconds) with a flexible delay.
+//
+// You can also combine a CDELAY and a DELAY to achieve a variable
+// length delay with a minimum value in a feedback loop. Just make
+// sure that you have a CDELAY involved.
+
+class Synth_DELAY_impl : virtual public Synth_DELAY_skel,
+ virtual public StdSynthModule
+{
+protected:
+ unsigned long _buffersize;
+ unsigned long _bitmask;
+ float * _buffer;
+ float _maxdelay;
+ unsigned int _writepos;
+
+public:
+ Synth_DELAY_impl() : _buffersize( 0 ), _bitmask( 0 ), _buffer( 0 ), _maxdelay( 0 ), _writepos( 0 )
+ {
+ maxdelay( 1 ); // take a one second buffer if nothing else is specified
+ }
+
+ ~Synth_DELAY_impl()
+ {
+ delete[] _buffer;
+ }
+
+ void streamInit()
+ {
+ // initialize buffer to all zeroes
+ for ( unsigned long i = 0; i < _buffersize; i++ )
+ _buffer[i] = 0.0;
+ }
+
+ void calculateBlock(unsigned long samples)
+ {
+ for( unsigned long i = 0; i <samples; i++ )
+ {
+ double int_pos;
+ double error = modf( time[i] * samplingRateFloat, &int_pos );
+ unsigned long readpos1 = ( _writepos - (unsigned long)(int_pos) ) & _bitmask;
+ unsigned long readpos2 = ( readpos1 - 1 ) & _bitmask; // Shouldn't this be +1? (mkretz)
+ // No, it's right this way:
+ // ( 1 - error ) needs to be multiplied with the second
+ // sample; error with the first
+ _buffer[_writepos] = invalue[i];
+ outvalue[i] = _buffer[readpos1] * ( 1 - error ) + _buffer[readpos2] * error;
+ _writepos++;
+ _writepos &= _bitmask;
+ }
+ }
+
+ float maxdelay() { return _maxdelay; }
+
+ void maxdelay(float newmaxdelay)
+ {
+ if( newmaxdelay <= 0 )
+ return;
+ _maxdelay = newmaxdelay;
+ double n = ceil( log( double(_maxdelay * samplingRateFloat) ) / log( 2. ) );
+ unsigned long newbuffersize = (unsigned long)( pow( 2, n ) );
+ unsigned long newbitmask = newbuffersize - 1;
+ if( newbuffersize != _buffersize )
+ {
+ float *newbuffer = new float[newbuffersize];
+ if( newbuffersize > _buffersize ) {
+ for( unsigned long i = 0; i < _buffersize; i++ ) {
+ newbuffer[i] = _buffer[_writepos];
+ _writepos++;
+ _writepos &= newbitmask;
+ }
+ for( unsigned long i = _buffersize; i < newbuffersize; i++ )
+ newbuffer[i] = 0;
+ } else {
+ _writepos -= newbuffersize;
+ _writepos &= newbitmask;
+ for( unsigned long i = 0; i < newbuffersize; i++ ) {
+ newbuffer[i] = _buffer[_writepos];
+ _writepos++;
+ _writepos &= newbitmask;
+ }
+ }
+ _buffer = newbuffer;
+ _buffersize = newbuffersize;
+ _bitmask = newbitmask;
+ }
+ maxdelay_changed( _maxdelay );
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_DELAY_impl);
diff --git a/arts/modules/synth/synth_div_impl.cc b/arts/modules/synth/synth_div_impl.cc
new file mode 100644
index 00000000..8e087ff5
--- /dev/null
+++ b/arts/modules/synth/synth_div_impl.cc
@@ -0,0 +1,45 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+ 2004 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+namespace Arts {
+
+class Synth_DIV_impl :public Synth_DIV_skel, public StdSynthModule
+{
+public:
+ void calculateBlock(unsigned long samples)
+ {
+ unsigned long i;
+
+ for(i = 0;i < samples; i++)
+ outvalue[i] = invalue1[i] / invalue2[i];
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_DIV_impl);
+
+}
diff --git a/arts/modules/synth/synth_envelope_adsr_impl.cc b/arts/modules/synth/synth_envelope_adsr_impl.cc
new file mode 100644
index 00000000..51ddf76c
--- /dev/null
+++ b/arts/modules/synth/synth_envelope_adsr_impl.cc
@@ -0,0 +1,121 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmodulessynth.h"
+#include "debug.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+class Synth_ENVELOPE_ADSR_impl : virtual public Synth_ENVELOPE_ADSR_skel,
+ virtual public StdSynthModule
+{
+protected:
+ enum { NOOUT, ATTACK, SUSTAIN, DECAY, RELEASE } currentphase;
+ float level,increment,decrement;
+public:
+ void streamInit()
+ {
+ currentphase = NOOUT;
+ level = 0;
+ }
+ void calculateBlock(unsigned long samples);
+};
+
+void Synth_ENVELOPE_ADSR_impl::calculateBlock(unsigned long samples)
+{
+ /* FIXME:
+ * should be rewritten as generic envelope, would certainly
+ * be faster & more flexible
+ */
+ unsigned long i;
+
+ for(i=0;i<samples;i++)
+ {
+ done[i] = 0;
+ if(active[i] < 0.5)
+ {
+ if(currentphase == NOOUT)
+ {
+ level = 0;
+ done[i] = 1;
+ }
+ else
+ {
+ if(currentphase != RELEASE) {
+ artsdebug("ADSR: entering release phase\n");
+ currentphase = RELEASE;
+ decrement = level / (release[i] * samplingRateFloat);
+ }
+ level -= decrement;
+ if(level <= 0)
+ {
+ level = 0;
+ currentphase = NOOUT;
+ }
+ }
+ }
+ else
+ {
+ switch(currentphase)
+ {
+ //quickly kill the note that is still there (channel busy ;)
+ case RELEASE:
+ level -= 1/200;
+ if(level <= 0) {
+ currentphase = NOOUT;
+ level = 0;
+ }
+ break;
+ case NOOUT:
+ artsdebug("ADSR: entering attack\n");
+ increment = 1 / (attack[i] * samplingRateFloat);
+ currentphase = ATTACK;
+ break;
+ case ATTACK:
+ level += increment;
+ if (level >= 1)
+ {
+ level = 1;
+ currentphase = DECAY;
+ decrement = (1-sustain[i]) /
+ (decay[i] * samplingRateFloat);
+ }
+ break;
+ case DECAY:
+ level -= decrement;
+ if (level <= sustain[i])
+ {
+ level = sustain[i];
+ currentphase = SUSTAIN;
+ }
+ break;
+ case SUSTAIN:
+ level = sustain[i];
+ break;
+ }
+ }
+ outvalue[i] = invalue[i] * level;
+ }
+}
+
+REGISTER_IMPLEMENTATION(Synth_ENVELOPE_ADSR_impl);
diff --git a/arts/modules/synth/synth_fm_source_impl.cc b/arts/modules/synth/synth_fm_source_impl.cc
new file mode 100644
index 00000000..a5b74b08
--- /dev/null
+++ b/arts/modules/synth/synth_fm_source_impl.cc
@@ -0,0 +1,61 @@
+ /*
+
+ Copyright (C) 2000 Jeff Tranter
+ tranter@pobox.com
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+// This is used for frequency modulation. Put your frequency to the
+// frequency input and put another signal on the modulator input. Then
+// set modlevel to something, say 0.3. The frequency will be modulated
+// with modulator then. Just try it. Works nice when you put a
+// feedback in there, that means take a combination of the delayed
+// output signal from the Synth_FM_SOURCE (you need to put it to some
+// oscillator as it only takes the role of Synth_FREQUENCY) and some
+// other signal to get good results. Works nicely in combination with
+// Synth_WAVE_SIN oscillators.
+
+class Synth_FM_SOURCE_impl : virtual public Synth_FM_SOURCE_skel,
+ virtual public StdSynthModule
+{
+protected:
+ static const int SAMPLINGRATE = 44100;
+ float posn;
+
+public:
+ void streamInit() { posn = 0; }
+
+ void calculateBlock(unsigned long samples)
+ {
+ for (unsigned long i=0; i<samples; i++)
+ {
+ float pinc = frequency[i] / (float) SAMPLINGRATE;
+ posn += pinc;
+ if (posn > 1)
+ posn -= 1;
+ pos[i] = posn + modulator[i] * modlevel[i];
+ }
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_FM_SOURCE_impl);
diff --git a/arts/modules/synth/synth_fx_cflanger_impl.cc b/arts/modules/synth/synth_fx_cflanger_impl.cc
new file mode 100644
index 00000000..16910958
--- /dev/null
+++ b/arts/modules/synth/synth_fx_cflanger_impl.cc
@@ -0,0 +1,96 @@
+/*
+
+ Copyright (C) 2000 Jeff Tranter
+ tranter@pobox.com
+ Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include <math.h>
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+class Synth_FX_CFLANGER_impl : virtual public Synth_FX_CFLANGER_skel,
+ virtual public StdSynthModule
+{
+protected:
+ float _mintime;
+ float _maxtime;
+ enum { SAMPLINGRATE = 44100, MAXDELAY = 44100 };
+ float *dbuffer;
+ unsigned long dbpos;
+ float center;
+ float range;
+
+public:
+ Synth_FX_CFLANGER_impl()
+ {
+ dbuffer=new float[MAXDELAY];
+ }
+ ~Synth_FX_CFLANGER_impl()
+ {
+ delete [] dbuffer;
+ }
+
+ float mintime() { return _mintime; }
+
+ void mintime(float newMintime) { _mintime = newMintime; }
+
+ float maxtime() { return _maxtime; }
+
+ void maxtime(float newMaxtime) { _maxtime = newMaxtime; }
+
+ void streamInit()
+ {
+ center = (_maxtime + _mintime) / 2;
+ range = _maxtime - center;
+ for (int i=0; i<MAXDELAY; i++)
+ dbuffer[i] = 0;
+ dbpos = 0;
+ }
+
+ void calculateBlock(unsigned long samples)
+ {
+ unsigned long i;
+ float delay, floor_delay;
+ long start_pos, end_pos;
+ float start_val, end_val;
+
+ for(i=0; i<samples; i++)
+ {
+ dbuffer[dbpos] = invalue[i];
+ // Delaytime i.e. = 35ms + (+/- LFO[-1 bis 1] * 15ms) / 1000 * 44100
+ delay = ((center + (lfo[i] * range)) / 1000.0) * (float) SAMPLINGRATE;
+ floor_delay = floor(delay);
+ start_pos = dbpos - (long)(floor_delay);
+ end_pos = start_pos-1;
+ if (start_pos < 0) start_pos += MAXDELAY; // wrapping exception
+ if (end_pos < 0) end_pos += MAXDELAY;
+ start_val = dbuffer[start_pos];
+ end_val = dbuffer[end_pos];
+ outvalue[i] = start_val + ((delay - floor_delay) * (end_val - start_val));
+ dbpos++;
+ if (dbpos == MAXDELAY) dbpos = 0;
+ }
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_FX_CFLANGER_impl);
diff --git a/arts/modules/synth/synth_midi_debug_impl.cc b/arts/modules/synth/synth_midi_debug_impl.cc
new file mode 100644
index 00000000..628b910b
--- /dev/null
+++ b/arts/modules/synth/synth_midi_debug_impl.cc
@@ -0,0 +1,88 @@
+/*
+
+ Copyright (C) 2000 Jeff Tranter
+ tranter@pobox.com
+
+ (C) 1998 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include <stdio.h>
+#include "debug.h"
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+class Synth_MIDI_DEBUG_impl : virtual public Synth_MIDI_DEBUG_skel,
+ virtual public StdSynthModule
+{
+ SystemMidiTimer timer;
+ MidiClient client;
+public:
+ Synth_MIDI_DEBUG self() { return Synth_MIDI_DEBUG::_from_base(_copy()); }
+
+ void streamInit()
+ {
+ printf("MIDI_DEBUG: streamInit\n");
+ MidiManager manager = Reference("global:Arts_MidiManager");
+ if(!manager.isNull())
+ {
+ client = manager.addClient(mcdRecord,mctDestination,"midi debug",
+ "Arts::Synth_MIDI_DEBUG");
+ client.addInputPort(self());
+ }
+ else
+ arts_warning("Synth_MIDI_DEBUG: no midi manager found "
+ "- not registered");
+ }
+
+ void processEvent(const MidiEvent& event)
+ {
+ printf("MIDI_DEBUG: scheduling event at %ld.%ld\n",
+ event.time.sec, event.time.usec);
+ timer.queueEvent(self(),event);
+ }
+ void processCommand(const MidiCommand& command)
+ {
+ mcopbyte channel = command.status & mcsChannelMask;
+ switch(command.status & mcsCommandMask)
+ {
+ case mcsNoteOn: printf("MIDI_DEBUG: note on channel %d, "
+ "note %d, velocity %d\n", channel,
+ command.data1, command.data2);
+ break;
+ case mcsNoteOff: printf("MIDI_DEBUG: note off channel %d, "
+ "note %d, velocity %d\n", channel,
+ command.data1, command.data2);
+ break;
+ }
+ }
+
+ TimeStamp time()
+ {
+ return timer.time();
+ }
+
+ TimeStamp playTime()
+ {
+ return timer.time();
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_MIDI_DEBUG_impl);
diff --git a/arts/modules/synth/synth_midi_test_impl.cc b/arts/modules/synth/synth_midi_test_impl.cc
new file mode 100644
index 00000000..91714cac
--- /dev/null
+++ b/arts/modules/synth/synth_midi_test_impl.cc
@@ -0,0 +1,692 @@
+#include "artsmodulessynth.h"
+#include "artsbuilder.h"
+#include "stdsynthmodule.h"
+#include "objectmanager.h"
+#include "connect.h"
+#include "flowsystem.h"
+#include "debug.h"
+#include "dynamicrequest.h"
+#include "audiosubsys.h"
+#include <fstream>
+#include <math.h>
+#include <stdlib.h>
+
+using namespace Arts;
+using namespace std;
+
+/*-------- instrument mapping ---------*/
+
+class InstrumentMap {
+protected:
+ struct InstrumentData;
+ class Tokenizer;
+ list<InstrumentData> instruments;
+ string directory;
+ void loadLine(const string& line);
+
+public:
+ struct InstrumentParam;
+
+ void loadFromList(const string& filename, const vector<string>& list);
+ StructureDesc getInstrument(mcopbyte channel, mcopbyte note,
+ mcopbyte velocity, mcopbyte program,
+ vector<InstrumentParam>*& params);
+};
+
+struct InstrumentMap::InstrumentParam
+{
+ string param;
+ Any value;
+
+ InstrumentParam()
+ {
+ }
+
+ InstrumentParam(const InstrumentParam& src)
+ : param(src.param), value(src.value)
+ {
+ }
+
+ InstrumentParam(const string& param, const string& strValue)
+ : param(param)
+ {
+ /* put the string into the any */
+ value.type = "string";
+
+ Buffer b;
+ b.writeString(strValue);
+ b.read(value.value, b.size());
+ }
+};
+
+struct InstrumentMap::InstrumentData
+{
+ struct Range
+ {
+ int minValue, maxValue;
+ Range() : minValue(0), maxValue(0)
+ {
+ }
+ Range(int minValue, int maxValue)
+ : minValue(minValue), maxValue(maxValue)
+ {
+ }
+ bool match(int value)
+ {
+ return (value >= minValue) && (value <= maxValue);
+ }
+ };
+ Range channel, pitch, program, velocity;
+ vector<InstrumentParam> params;
+ StructureDesc instrument;
+};
+
+class InstrumentMap::Tokenizer {
+protected:
+ bool haveToken, haveNextToken;
+ string token, nextToken, input;
+ string::iterator ii;
+public:
+ Tokenizer(const string& line)
+ : haveToken(false), haveNextToken(false),
+ input(line+"\n"), ii(input.begin())
+ {
+ /* adding a \n ensures that we will definitely find the last token */
+ }
+ string getToken()
+ {
+ if(!haveMore())
+ return "";
+
+ if(haveNextToken)
+ {
+ string t = token;
+ haveNextToken = false;
+ token = nextToken;
+ return t;
+ }
+ else
+ {
+ haveToken = false;
+ return token;
+ }
+ }
+ bool haveMore()
+ {
+ if(haveToken)
+ return true;
+
+ token = "";
+ while(ii != input.end() && !haveToken)
+ {
+ const char& c = *ii++;
+
+ if(c == ' ' || c == '\t' || c == '\n')
+ {
+ if(!token.empty()) haveToken = true;
+ }
+ else if(c == '=') /* || c == '-' || c == '+')*/
+ {
+ if(!token.empty())
+ {
+ haveNextToken = true;
+ nextToken = c;
+ }
+ else
+ {
+ token = c;
+ }
+ haveToken = true;
+ }
+ else
+ {
+ token += c;
+ }
+ }
+ return haveToken;
+ }
+};
+
+void InstrumentMap::loadLine(const string& line)
+{
+ Tokenizer t(line);
+ InstrumentData id;
+ /* default: no filtering */
+ id.channel = InstrumentData::Range(0,15);
+ id.pitch = id.program = id.velocity = InstrumentData::Range(0,127);
+
+ string s[3];
+ int i = 0;
+ bool seenDo = false;
+ bool loadOk = false;
+
+ if(t.getToken() != "ON")
+ {
+ arts_warning("error in arts-map: lines must start with ON (did start with %s)\n", t.getToken().c_str());
+ return;
+ }
+
+ while(t.haveMore())
+ {
+ const string& token = t.getToken();
+
+ if(token == "DO")
+ seenDo = true;
+ else
+ {
+ s[i] = token;
+ if(i == 2) /* evaluate */
+ {
+ if(s[1] != "=")
+ {
+ arts_warning("error in arts-map: no = operator\n");
+ return;
+ }
+
+ if(seenDo)
+ {
+ if(s[0] == "structure")
+ {
+ string filename = s[2];
+
+ /* if it's no absolute path, its relative to the map */
+ if(!filename.empty() && filename[0] != '/')
+ filename = directory + "/" + s[2];
+
+ ifstream infile(filename.c_str());
+ string line;
+ vector<string> strseq;
+
+ while(getline(infile,line))
+ strseq.push_back(line);
+
+ id.instrument.loadFromList(strseq);
+ if(id.instrument.name() != "unknown")
+ {
+ loadOk = true;
+ }
+ else
+ {
+ arts_warning("mapped instrument: "
+ "can't load structure %s",s[2].c_str());
+ }
+ }
+ else
+ {
+ /* TODO: handle different datatypes */
+ id.params.push_back(InstrumentParam(s[0], s[2]));
+ }
+ }
+ else
+ {
+ InstrumentData::Range range;
+ range.minValue = atoi(s[2].c_str());
+ range.maxValue = range.minValue;
+ int i = s[2].find("-",0);
+ if(i != 0)
+ {
+ range.minValue = atoi(s[2].substr(0,i).c_str());
+ range.maxValue =
+ atoi(s[2].substr(i+1,s[2].size()-(i+1)).c_str());
+ }
+ if(s[0] == "pitch") id.pitch = range;
+ if(s[0] == "channel") id.channel = range;
+ if(s[0] == "program") id.program = range;
+ if(s[0] == "velocity") id.velocity = range;
+ }
+ i = 0;
+ }
+ else i++;
+ }
+ }
+ if(loadOk) instruments.push_back(id);
+}
+
+void InstrumentMap::loadFromList(const string& filename,
+ const vector<string>& list)
+{
+ int r = filename.rfind('/');
+ if(r > 0)
+ directory = filename.substr(0,r);
+ else
+ directory = "";
+
+ vector<string>::const_iterator i;
+ instruments.clear();
+ for(i = list.begin(); i != list.end(); i++) loadLine(*i);
+}
+
+StructureDesc InstrumentMap::getInstrument(mcopbyte channel, mcopbyte note,
+ mcopbyte velocity, mcopbyte program,
+ vector<InstrumentParam>*& params)
+{
+ list<InstrumentData>::iterator i;
+ for(i = instruments.begin(); i != instruments.end(); i++)
+ {
+ InstrumentData &id = *i;
+
+ if(id.channel.match(channel) && id.pitch.match(note) &&
+ id.velocity.match(velocity) && id.program.match(program))
+ {
+ params = &id.params;
+ return id.instrument;
+ }
+ }
+
+ return StructureDesc::null();
+}
+
+
+/*-------instrument mapping end -------*/
+
+static SynthModule get_AMAN_PLAY(Object structure)
+{
+ Object resultObj = structure._getChild("play");
+ assert(!resultObj.isNull());
+
+ SynthModule result = DynamicCast(resultObj);
+ assert(!result.isNull());
+
+ return result;
+}
+
+struct TSNote {
+ MidiPort port;
+ MidiEvent event;
+ TSNote(MidiPort port, const MidiEvent& event) :
+ port(port), event(event)
+ {
+ }
+};
+
+class AutoMidiRelease : public TimeNotify {
+public:
+ vector<MidiReleaseHelper> impls;
+ AutoMidiRelease()
+ {
+ Dispatcher::the()->ioManager()->addTimer(10, this);
+ }
+ virtual ~AutoMidiRelease()
+ {
+ Dispatcher::the()->ioManager()->removeTimer(this);
+ }
+ void notifyTime()
+ {
+ vector<MidiReleaseHelper>::iterator i = impls.begin();
+ while(i != impls.end())
+ {
+ if(i->terminate())
+ {
+ MidiReleaseHelper& helper = *i;
+
+ arts_debug("one voice terminated");
+ // put the MidiReleaseHelper and the voice into the ObjectCache
+ // (instead of simply freeing it)
+ ObjectCache cache = helper.cache();
+ SynthModule voice = helper.voice();
+ get_AMAN_PLAY(voice).stop();
+ voice.stop();
+ cache.put(voice,helper.name());
+ impls.erase(i);
+ return;
+ } else i++;
+ }
+ }
+} *autoMidiRelease;
+
+// cache startup & shutdown
+static class AutoMidiReleaseStart :public StartupClass
+{
+public:
+ void startup() { autoMidiRelease = new AutoMidiRelease(); }
+ void shutdown() { delete autoMidiRelease; }
+} autoMidiReleaseStart;
+
+class MidiReleaseHelper_impl : virtual public MidiReleaseHelper_skel,
+ virtual public StdSynthModule
+{
+protected:
+ bool _terminate;
+ SynthModule _voice;
+ ObjectCache _cache;
+ string _name;
+
+public:
+ MidiReleaseHelper_impl()
+ {
+ autoMidiRelease->impls.push_back(MidiReleaseHelper::_from_base(_copy()));
+ }
+ ~MidiReleaseHelper_impl() {
+ artsdebug("MidiReleaseHelper: one voice is gone now\n");
+ }
+
+
+ SynthModule voice() { return _voice; }
+ void voice(SynthModule voice) { _voice = voice; }
+
+ ObjectCache cache() { return _cache; }
+ void cache(ObjectCache cache) { _cache = cache; }
+
+ string name() { return _name; }
+ void name(const string& name) { _name = name; }
+
+ bool terminate() { return _terminate; }
+ void streamStart() { _terminate = false; }
+
+ void calculateBlock(unsigned long /*samples*/)
+ {
+ if(done[0] > 0.5)
+ _terminate = true;
+ }
+};
+REGISTER_IMPLEMENTATION(MidiReleaseHelper_impl);
+
+class Synth_MIDI_TEST_impl : virtual public Synth_MIDI_TEST_skel,
+ virtual public StdSynthModule {
+protected:
+ struct ChannelData {
+ SynthModule voice[128];
+ string name[128];
+ float pitchShiftValue;
+ mcopbyte program;
+ ChannelData() {
+ // initialize all voices with NULL objects (no lazy create)
+ for(int i = 0; i < 128; i++) voice[i] = SynthModule::null();
+
+ pitchShiftValue = 0.0;
+ program = 0;
+ }
+ } *channelData; /* data for all 16 midi channels */
+
+ bool useMap;
+ InstrumentMap map;
+ StructureDesc instrument;
+ StructureBuilder builder;
+ AudioManagerClient amClient;
+ ObjectCache cache;
+ MidiClient client;
+ MidiTimer timer;
+
+ string _filename;
+ string _busname;
+ string _title;
+public:
+ Synth_MIDI_TEST self() { return Synth_MIDI_TEST::_from_base(_copy()); }
+
+ Synth_MIDI_TEST_impl();
+ ~Synth_MIDI_TEST_impl();
+
+ void filename(const string& newname);
+ string filename()
+ {
+ return _filename;
+ }
+ void busname(const string& newname);
+ string busname()
+ {
+ return _busname;
+ }
+ string title()
+ {
+ return _title;
+ }
+ void noteOn(mcopbyte channel, mcopbyte note, mcopbyte velocity);
+ void noteOff(mcopbyte channel, mcopbyte note);
+ void pitchWheel(mcopbyte channel, mcopbyte lsb, mcopbyte msb);
+
+ float getFrequency(mcopbyte note,mcopbyte channel);
+
+ void streamStart();
+ void streamEnd();
+
+ TimeStamp time()
+ {
+ return timer.time();
+ }
+ TimeStamp playTime()
+ {
+ /*
+ * what the user currently hears is exactly latencySec before our
+ * port timeStamp (as this is the size of the audio buffer)
+ */
+ double latencySec = AudioSubSystem::the()->outputDelay();
+ TimeStamp t = time();
+
+ int sec = int(latencySec);
+ t.sec -= sec;
+ latencySec -= double(sec);
+ t.usec -= int(latencySec * 1000000.0);
+
+ if (t.usec < 0)
+ {
+ t.usec += 1000000;
+ t.sec -= 1;
+ }
+
+ arts_assert(t.usec >= 0 && t.usec < 1000000);
+ return t;
+ }
+ void processEvent(const MidiEvent& event)
+ {
+ timer.queueEvent(self(),event);
+ }
+ void processCommand(const MidiCommand& command)
+ {
+ mcopbyte channel = command.status & mcsChannelMask;
+
+ switch(command.status & mcsCommandMask)
+ {
+ case mcsNoteOn: noteOn(channel,command.data1,command.data2);
+ return;
+ case mcsNoteOff: noteOff(channel,command.data1);
+ return;
+ case mcsPitchWheel: pitchWheel(channel,command.data1,command.data2);
+ return;
+ case mcsProgram: channelData[channel].program = command.data1;
+ return;
+ case mcsParameter:
+ if(command.data1 == mcpAllNotesOff && command.data2 == 0)
+ for(mcopbyte note=0; note<128; note++)
+ noteOff(channel,note);
+ return;
+ }
+ }
+};
+REGISTER_IMPLEMENTATION(Synth_MIDI_TEST_impl);
+
+
+void Synth_MIDI_TEST_impl::busname(const string& newname)
+{
+ // TODO:
+ _busname = newname;
+}
+
+void Synth_MIDI_TEST_impl::filename(const string& newname)
+{
+ ifstream infile(newname.c_str());
+ string line;
+ vector<string> strseq;
+
+ while(getline(infile,line))
+ strseq.push_back(line);
+
+ _filename = newname;
+
+/* search extension */
+ string::const_reverse_iterator i;
+ string extension;
+ bool extensionok = false;
+
+ for(i = newname.rbegin(); i != newname.rend() && !extensionok; i++)
+ {
+ if(*i == '.')
+ extensionok = true;
+ else
+ extension.insert(extension.begin(), (char)tolower(*i));
+ }
+
+ if(extensionok && extension == "arts")
+ {
+ instrument.loadFromList(strseq);
+ _title = "aRts Instrument ("+instrument.name()+")";
+ useMap = false;
+ }
+ else if(extensionok && extension == "arts-map")
+ {
+ map.loadFromList(newname, strseq);
+ _title = "aRts Instrument (mapped)";
+ useMap = true;
+ }
+
+ if(!client.isNull())
+ client.title(title());
+ amClient.title(title());
+}
+
+Synth_MIDI_TEST_impl::Synth_MIDI_TEST_impl()
+ : amClient(amPlay, "aRts Instrument","Synth_MIDI_TEST")
+{
+ useMap = false;
+ client = MidiClient::null();
+ timer = SubClass("Arts::AudioMidiTimer");
+ channelData = new ChannelData[16];
+}
+
+Synth_MIDI_TEST_impl::~Synth_MIDI_TEST_impl()
+{
+ delete[] channelData;
+}
+
+void Synth_MIDI_TEST_impl::streamStart()
+{
+ // register with the midi manager
+ MidiManager manager = Reference("global:Arts_MidiManager");
+ if(!manager.isNull())
+ {
+ client = manager.addClient(mcdRecord,mctDestination,title(),
+ "Arts::Synth_MIDI_TEST");
+ client.addInputPort(self());
+ }
+ else
+ arts_warning("Synth_MIDI_TEST: no midi manager found - not registered");
+}
+
+void Synth_MIDI_TEST_impl::streamEnd()
+{
+ client = MidiClient::null();
+}
+
+void Synth_MIDI_TEST_impl::noteOn(mcopbyte channel, mcopbyte note,
+ mcopbyte velocity)
+{
+ if(velocity == 0)
+ {
+ noteOff(channel,note);
+ return;
+ }
+ if(!channelData[channel].voice[note].isNull())
+ {
+ noteOff(channel,note);
+ arts_info("Synth_MIDI_TEST: duplicate noteOn (mixed channels?)");
+ }
+
+ vector<InstrumentMap::InstrumentParam> *params = 0;
+ if(useMap)
+ {
+ mcopbyte program = channelData[channel].program;
+ StructureDesc sd = map.getInstrument(channel,note,velocity,program,params);
+ if(sd.isNull()) return;
+ instrument = sd;
+ }
+
+ Object structureObject = cache.get(instrument.name());
+ if(structureObject.isNull())
+ {
+ arts_debug("creating new structure");
+ structureObject = builder.createObject(instrument);
+
+ SynthModule play;
+ // TODO: allow changing busname!
+ if(!_busname.empty())
+ {
+ Synth_BUS_UPLINK b;
+ b.busname(_busname);
+ play = b;
+ }
+ else
+ {
+ Synth_AMAN_PLAY a(amClient);
+ play = a;
+ }
+ structureObject._addChild(play,"play");
+ connect(structureObject,"left",play,"left");
+ connect(structureObject,"right",play,"right");
+ }
+ else
+ {
+ arts_debug("used cached structure");
+ }
+
+ SynthModule structure = DynamicCast(structureObject);
+ assert(!structure.isNull());
+
+ if(params)
+ {
+ vector<InstrumentMap::InstrumentParam>::iterator pi;
+ for(pi = params->begin(); pi != params->end(); pi++)
+ {
+ DynamicRequest req(structure);
+
+ req.method("_set_"+pi->param).param(pi->value).invoke();
+ }
+ }
+ setValue(structure,"frequency",getFrequency(note,channel));
+ setValue(structure,"velocity",(float)velocity/127.0);
+ setValue(structure,"pressed",1.0);
+
+ get_AMAN_PLAY(structure).start();
+ structure.start();
+
+ channelData[channel].voice[note] = structure;
+ channelData[channel].name[note] = instrument.name();
+}
+
+void Synth_MIDI_TEST_impl::noteOff(mcopbyte channel, mcopbyte note)
+{
+ if(!channelData[channel].voice[note].isNull())
+ {
+ setValue(channelData[channel].voice[note],"pressed",0.0);
+
+ MidiReleaseHelper h;
+
+ h.voice(channelData[channel].voice[note]);
+ h.cache(cache);
+ h.name(channelData[channel].name[note]);
+
+ connect(channelData[channel].voice[note],"done",h,"done");
+ h.start();
+ assert(!h.terminate());
+ channelData[channel].voice[note] = SynthModule::null();
+ }
+}
+
+float Synth_MIDI_TEST_impl::getFrequency(mcopbyte note, mcopbyte channel)
+{
+ /* 2 semitones pitchshift */
+ return 261.63 * pow(2,((float)(note)+(channelData[channel].pitchShiftValue*2.0))/12.0)/32.0;
+}
+
+void Synth_MIDI_TEST_impl::pitchWheel(mcopbyte channel,
+ mcopbyte lsb, mcopbyte msb)
+{
+ mcopbyte note;
+
+ channelData[channel].pitchShiftValue =
+ (float)((lsb + msb*128) - (0x40*128))/8192.0;
+
+ for(note = 0; note < 128; note++)
+ {
+ if(!channelData[channel].voice[note].isNull())
+ setValue(channelData[channel].voice[note],"frequency",getFrequency(note,channel));
+ }
+}
diff --git a/arts/modules/synth/synth_moog_vcf_impl.cc b/arts/modules/synth/synth_moog_vcf_impl.cc
new file mode 100644
index 00000000..f20c8491
--- /dev/null
+++ b/arts/modules/synth/synth_moog_vcf_impl.cc
@@ -0,0 +1,91 @@
+/*
+
+ Copyright (C) 2000 Jeff Tranter
+ tranter@pobox.com
+
+ (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+/*
+Try this. It's a really nice 4pole. Very Moog like.
+
+in[x] and out[x] are member variables, init to 0.0
+the controls:
+
+fc = cutoff, nearly linear [0,1] -> [0, fs/2]
+res = resonance [0, 4] -> [no resonance, self-oscillation]
+*/
+
+class Synth_MOOG_VCF_impl : virtual public Synth_MOOG_VCF_skel,
+ virtual public StdSynthModule
+{
+protected:
+ float _frequency, _resonance;
+ double freqcorrect;
+ double in1, in2, in3, in4;
+ double out1, out2, out3, out4;
+
+public:
+ float frequency() { return _frequency; }
+ void frequency(float newFrequency) { _frequency = newFrequency; }
+
+ float resonance() { return _resonance; }
+ void resonance(float newResonance) { _resonance = newResonance; }
+
+ void streamInit()
+ {
+ in1 = in2 = in3 = in4 = out1 = out2 = out3 = out4 = 0.0;
+ }
+
+ void calculateBlock(unsigned long samples)
+ {
+ freqcorrect = 1.16 / (double)(samplingRate / 2);
+
+ for (unsigned int i=0; i < samples; i++)
+ {
+ double input = invalue[i];
+ double fc = _frequency;
+ double res = _resonance;
+ double f = fc * freqcorrect;
+ double fb = res * (1.0 - 0.15 * f * f);
+
+ input -= out4 * fb;
+ input *= 0.35013 * (f * f) * (f * f);
+
+ out1 = input + 0.3 * in1 + (1 - f) * out1; // Pole 1
+ in1 = input;
+ out2 = out1 + 0.3 * in2 + (1 - f) * out2; // Pole 2
+ in2 = out1;
+ out3 = out2 + 0.3 * in3 + (1 - f) * out3; // Pole 3
+ in3 = out2;
+ out4 = out3 + 0.3 * in4 + (1 - f) * out4; // Pole 4
+ in4 = out3;
+
+ outvalue[i] = out4;
+ }
+ }
+
+};
+
+REGISTER_IMPLEMENTATION(Synth_MOOG_VCF_impl);
diff --git a/arts/modules/synth/synth_nil_impl.cc b/arts/modules/synth/synth_nil_impl.cc
new file mode 100644
index 00000000..ad927b85
--- /dev/null
+++ b/arts/modules/synth/synth_nil_impl.cc
@@ -0,0 +1,36 @@
+ /*
+
+ Copyright (C) 2000 Jeff Tranter
+ tranter@pobox.com
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+// This module does nothing. It is only used for test purposes.
+class Synth_NIL_impl : virtual public Synth_NIL_skel,
+ virtual public StdSynthModule
+{
+public:
+ void calculateBlock(unsigned long /*samples*/) { }
+};
+
+REGISTER_IMPLEMENTATION(Synth_NIL_impl);
diff --git a/arts/modules/synth/synth_noise_impl.cc b/arts/modules/synth/synth_noise_impl.cc
new file mode 100644
index 00000000..77c41082
--- /dev/null
+++ b/arts/modules/synth/synth_noise_impl.cc
@@ -0,0 +1,63 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include <stdlib.h>
+
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+namespace Arts {
+
+#define NOISE_SIZE 8192
+
+class Synth_NOISE_impl : virtual public Synth_NOISE_skel,
+ virtual public StdSynthModule
+{
+ static float noise[NOISE_SIZE];
+ static bool noiseInit;
+ unsigned long pos;
+public:
+ Synth_NOISE_impl()
+ {
+ if(!noiseInit)
+ {
+ for(unsigned long i=0;i<NOISE_SIZE;i++)
+ noise[i] = ((float)rand()/(float)RAND_MAX)*2.0-1.0;
+ noiseInit = true;
+ }
+ }
+ void calculateBlock(unsigned long samples)
+ {
+ unsigned long i;
+ pos = rand();
+ for(i=0;i<samples;i++) outvalue[i] = noise[pos++ & (NOISE_SIZE-1)];
+ }
+};
+
+float Synth_NOISE_impl::noise[8192];
+bool Synth_NOISE_impl::noiseInit = false;
+
+REGISTER_IMPLEMENTATION(Synth_NOISE_impl);
+
+}
diff --git a/arts/modules/synth/synth_osc_impl.cc b/arts/modules/synth/synth_osc_impl.cc
new file mode 100644
index 00000000..227f92cf
--- /dev/null
+++ b/arts/modules/synth/synth_osc_impl.cc
@@ -0,0 +1,253 @@
+ /*
+
+ Copyright (C) 2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmodulessynth.h"
+#include "debug.h"
+#include "stdsynthmodule.h"
+#include <gsl/gsloscillator.h>
+#include <gsl/gslsignal.h>
+#include <string.h>
+
+#include <math.h>
+
+using namespace Arts;
+
+namespace Arts {
+
+static double arts_gsl_window_osc(double x)
+{
+ const double FILTER_H = 22000.0;
+ const double FILTER_L = 19000.0;
+ double f = 22050.0 * fabs(x), fact;
+
+ if(f > FILTER_H)
+ fact = 0.0;
+ else if (f < FILTER_L)
+ fact = 1.0;
+ else
+ fact = cos(M_PI/2.0*((FILTER_L-f)/(FILTER_H-FILTER_L)));
+
+ return fact;
+}
+
+class Synth_OSC_impl :public Synth_OSC_skel, public StdSynthModule
+{
+private:
+ GslOscConfig cfg;
+ GslOscData osc;
+ SynthOscWaveForm _waveForm;
+
+ bool infrequency_connected;
+ bool modulation_connected;
+ bool insync_connected;
+ bool outvalue_connected;
+ bool outsync_connected;
+
+ void updateConnected()
+ {
+ infrequency_connected = inputConnectionCount("infrequency");
+ modulation_connected = inputConnectionCount("modulation");
+ insync_connected = inputConnectionCount("insync");
+ outvalue_connected = outputConnectionCount("outvalue");
+ outsync_connected = outputConnectionCount("outsync");
+ }
+public:
+ Synth_OSC_impl() {
+ _waveForm = soWaveTriangle;
+
+ memset(&cfg, 0, sizeof(GslOscConfig));
+ memset(&osc, 0, sizeof(GslOscData));
+
+ cfg.table = 0;
+ cfg.exponential_fm = 0;
+ cfg.fm_strength = 0;
+ cfg.self_fm_strength = 0;
+ cfg.cfreq = 440;
+ cfg.fine_tune = 0;
+ cfg.pulse_width = 0.5;
+ cfg.pulse_mod_strength = 0;
+
+ waveForm(soWaveSine);
+ };
+ void apply()
+ {
+ gsl_osc_config(&osc, &cfg);
+ }
+ void streamInit()
+ {
+ updateConnected();
+ }
+ void calculateBlock(unsigned long samples)
+ {
+ if(connectionCountChanged())
+ updateConnected();
+
+ arts_debug("gop tab%p samples%ld f%p m%p is%p ov%p os%p\n",
+ cfg.table, samples, infrequency_connected?infrequency:0,
+ modulation_connected?modulation:0,
+ insync_connected?insync:0,
+ outvalue_connected?outvalue:0,
+ outsync_connected?outsync:0);
+
+ gsl_osc_process(&osc, samples, infrequency_connected?infrequency:0,
+ modulation_connected?modulation:0,
+ insync_connected?insync:0,
+ outvalue_connected?outvalue:0,
+ outsync_connected?outsync:0);
+ }
+ SynthOscWaveForm waveForm()
+ {
+ return _waveForm;
+ }
+ void waveForm(SynthOscWaveForm wf)
+ {
+ if(wf != _waveForm)
+ {
+ if(cfg.table)
+ gsl_osc_table_free(cfg.table);
+
+ float freqs[100];
+ int n_freqs = 0;
+
+ freqs[n_freqs] = 20;
+ while (freqs[n_freqs] < 22000)
+ {
+ freqs[n_freqs + 1] = freqs[n_freqs] * M_SQRT2;
+ n_freqs++;
+ }
+ arts_debug("Synth_OSC::waveForm: n_freqs = %d", n_freqs);
+ cfg.table = gsl_osc_table_create(samplingRateFloat, GslOscWaveForm(wf + 1), arts_gsl_window_osc, n_freqs, freqs);
+ _waveForm = wf;
+ apply();
+ waveForm_changed(wf);
+ }
+ }
+ bool fmExponential()
+ {
+ return cfg.exponential_fm;
+ }
+ void fmExponential(bool newFm)
+ {
+ bool oldFm = fmExponential();
+
+ if(newFm != oldFm)
+ {
+ cfg.exponential_fm = newFm;
+ apply();
+ fmExponential_changed(newFm);
+ }
+ }
+ float fmStrength()
+ {
+ return cfg.fm_strength;
+ }
+ void fmStrength(float f)
+ {
+ if(cfg.fm_strength != f)
+ {
+ cfg.fm_strength = f;
+ apply();
+ fmStrength_changed(f);
+ }
+ }
+ float fmSelfStrength()
+ {
+ return cfg.self_fm_strength;
+ }
+ void fmSelfStrength(float f)
+ {
+ if(cfg.self_fm_strength != f)
+ {
+ cfg.self_fm_strength = f;
+ apply();
+ fmSelfStrength_changed(f);
+ }
+ }
+ float phase()
+ {
+ return cfg.phase;
+ }
+ void phase(float p)
+ {
+ if(cfg.phase != p)
+ {
+ cfg.phase = p;
+ apply();
+ phase_changed(p);
+ }
+ }
+ float frequency()
+ {
+ return cfg.cfreq;
+ }
+ void frequency(float f)
+ {
+ if(cfg.cfreq != f)
+ {
+ cfg.cfreq = f;
+ apply();
+ frequency_changed(f);
+ }
+ }
+ long fineTune()
+ {
+ return cfg.fine_tune;
+ }
+ void fineTune(long f)
+ {
+ if(cfg.fine_tune != f)
+ {
+ cfg.fine_tune = f;
+ apply();
+ fineTune_changed(f);
+ }
+ }
+ float pulseWidth()
+ {
+ return cfg.pulse_width;
+ }
+ void pulseWidth(float pw)
+ {
+ if(cfg.pulse_width != pw)
+ {
+ cfg.pulse_width = pw;
+ apply();
+ pulseWidth_changed(pw);
+ }
+ }
+ float pulseModStrength()
+ {
+ return cfg.pulse_mod_strength;
+ }
+ void pulseModStrength(float pms)
+ {
+ if(cfg.pulse_mod_strength != pms)
+ {
+ cfg.pulse_mod_strength = pms;
+ apply();
+ pulseModStrength_changed(pms);
+ }
+ }
+};
+REGISTER_IMPLEMENTATION(Synth_OSC_impl);
+
+}
diff --git a/arts/modules/synth/synth_pitch_shift_fft_impl.cc b/arts/modules/synth/synth_pitch_shift_fft_impl.cc
new file mode 100644
index 00000000..f356e1b8
--- /dev/null
+++ b/arts/modules/synth/synth_pitch_shift_fft_impl.cc
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2002 Michael Zuercher
+ * mzuerche@iastate.edu
+ *
+ * Based on an algorithm by Stephan M. Sprenger, http://www.dspdimension.com
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+
+
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+#include <stdio.h> //debug only
+#include <arts/fft.h>
+#include <string.h>
+#include <math.h>
+
+#define MAX(a,b) (((a) > (b) ? (a) : (b)))
+#define MIN(a,b) (((a) < (b) ? (a) : (b)))
+
+using namespace Arts;
+
+class Synth_PITCH_SHIFT_FFT_impl : virtual public Synth_PITCH_SHIFT_FFT_skel,
+ virtual public StdSynthModule
+{
+ private:
+ struct fftBin
+ {
+ float magnitude;
+ float frequency;
+ float phase;
+ };
+
+ bool addPi;
+
+ /* the attributes (gui changeable) */
+ /* these can happen on the fly */
+ float _scaleFactor, _speed;
+ /* these require calling setStreamOpts() */
+ unsigned int _frameSize, _oversample;
+
+ /* the buffers */
+ float *inBuffer, *outBuffer; /* circular buffers (float) */
+ float *windowedData; /* windowed and unrolled buffer (float) */
+ fftBin *analysisBuf, *synthesisBuf; /* workspaces (fftBin) */
+ float *real, *imag; /* place for the FFT to output */
+ float *windowCoeffient;
+ float *scratch; /* used to store imag IFFT results that we don't need */
+ float *phaseDiff;
+
+ /* variables to keep us in the right place of the buffers */
+ unsigned long bufferOffset;
+ /* stream not yet ready to go until we have prerolled this many windows */
+ unsigned int initStepsRemaining;
+
+ /* some commonly used variables */
+ unsigned long stepSize;
+ double expectedPhaseDiff;
+ double freqPerBin;
+
+ /* Helper functions */
+ void inWindow(float windowedData[], const float *inBuffer, const unsigned int basePopPoint);
+ void analysis(fftBin analysisBuf[], const float real[]);
+ void pitchScale(fftBin synthesisBuf[], const fftBin analysisBuf[]);
+ void synthesis(float windowedData[], fftBin synthesisBuf[]);
+ void outWindow(float *outBuffer, const unsigned int basePushPoint, const float windowedData[]);
+
+
+ public:
+ /* functions for the plugin interface */
+ float speed() { return _speed; }
+ void speed(float newSpeed) { _speed = newSpeed; }
+
+ float scaleFactor() { return _scaleFactor; }
+ void scaleFactor(float newScaleFactor) { _scaleFactor = newScaleFactor; }
+
+ long frameSize() { return (long)_frameSize; }
+ void frameSize(long newFrameSize)
+ {
+ setStreamOpts(newFrameSize, _oversample);
+ }
+
+ long oversample() { return (long)_oversample; }
+ void oversample(long newOversample)
+ {
+ setStreamOpts(_frameSize, newOversample);
+ }
+
+ /* gets called by arts when it needs more data */
+ void calculateBlock(unsigned long samples);
+
+ void streamInit()
+ {
+ inBuffer = outBuffer = NULL;
+ analysisBuf = synthesisBuf = NULL;
+ real = imag = NULL;
+ windowedData = NULL;
+ windowCoeffient = NULL;
+ scratch = NULL;
+ phaseDiff = NULL;
+
+ /* setup default stream parameters */
+ _speed = 1.0;
+ _scaleFactor = 0.9;
+ setStreamOpts(4096,2);
+
+ addPi = false;
+ }
+
+ void streamEnd()
+ {
+ /* clean up buffers */
+ delete [] inBuffer;
+ delete [] outBuffer;
+ delete [] windowedData;
+ delete [] analysisBuf;
+ delete [] synthesisBuf;
+ delete [] real;
+ delete [] imag;
+ delete [] windowCoeffient;
+ delete [] scratch;
+ delete [] phaseDiff;
+ }
+
+ void setStreamOpts(unsigned int frameSize, unsigned int oversample)
+ {
+ /* clear any buffers left around */
+ delete [] inBuffer;
+ delete [] outBuffer;
+ delete [] windowedData;
+ delete [] analysisBuf;
+ delete [] synthesisBuf;
+ delete [] real;
+ delete [] imag;
+ delete [] windowCoeffient;
+ delete [] scratch;
+ delete [] phaseDiff;
+
+ _frameSize = frameSize;
+ _oversample = oversample;
+
+ /* create the buffers */
+ inBuffer = new float[_frameSize];
+ outBuffer = new float[_frameSize];
+ windowedData = new float[_frameSize];
+ analysisBuf = new fftBin[_frameSize];
+ synthesisBuf = new fftBin[_frameSize];
+ real = new float[_frameSize];
+ imag = new float[_frameSize];
+ windowCoeffient = new float[_frameSize];
+ scratch = new float[_frameSize];
+ phaseDiff = new float[_oversample];
+
+
+ /* set up the windowing coeffients */
+ for(unsigned int sample=0; sample < _frameSize; sample++)
+ {
+ windowCoeffient[sample] = -0.5*cos(2.0*M_PI*(double)sample/(double)_frameSize)+0.5;
+ }
+
+ /* we should start at the beginning of the buffers */
+ bufferOffset = 0;
+
+ /* stream not yet ready to go until we have prerolled this many windows */
+ initStepsRemaining = _oversample;
+
+ /* calculate some commonly used variables */
+ stepSize = _frameSize / _oversample;
+ expectedPhaseDiff = 2*M_PI*(double)stepSize/(double)_frameSize;
+ freqPerBin = samplingRate/(double)_frameSize;
+
+ for(unsigned int bin=0; bin < _oversample; bin++)
+ {
+ phaseDiff[bin] = bin*expectedPhaseDiff;
+ }
+
+ memset(outBuffer, 0 ,stepSize * sizeof(float)); /* clear the first part of the output accumulator */
+ memset(analysisBuf, 0 ,_frameSize * sizeof(fftBin));
+ memset(synthesisBuf, 0 ,_frameSize * sizeof(fftBin));
+ }
+};
+
+void Synth_PITCH_SHIFT_FFT_impl::calculateBlock(unsigned long samples)
+{
+ unsigned long samplesRemaining = samples;
+
+ /* pointers to the arts streams */
+ float *inData = inStream;
+ float *outData = outStream;
+
+ while(samplesRemaining > 0)
+ {
+ /* either fill the next window, or take all we have */
+ int samplesThisPass = MIN(samplesRemaining,stepSize - (bufferOffset % stepSize));
+
+ /* copy the incoming data into the buffer */
+ memcpy(inBuffer + bufferOffset, inData, samplesThisPass * sizeof(float));
+ /* set inData to data we haven't already taken */
+ inData += samplesThisPass;
+
+ if((bufferOffset+samplesThisPass) % stepSize)
+ {
+ /* if we still have only a partial window (input is still in the
+ * middle of a window), we can't run it yet, but we have leftover
+ * output we can use */
+ }
+ else
+ {
+ /* round down the the nearest stepSize, and this window is full */
+
+ if(initStepsRemaining > 0) /* we need to have enough old data for a full block too */
+ {
+ initStepsRemaining--; /* one less step to fill before we can start for real */
+ }
+ else
+ {
+ unsigned int stepOffset = (bufferOffset + samplesThisPass) - stepSize;
+ /* now we have a complete block (not still filling at init) to add the
+ * new complete window on to */
+
+ /* ############################ prepare one stepSize ########################### */
+
+ inWindow(windowedData,inBuffer,stepOffset);
+ analysis(analysisBuf,windowedData);
+ pitchScale(synthesisBuf,analysisBuf);
+ synthesis(windowedData,synthesisBuf);
+ outWindow(outBuffer,bufferOffset,windowedData);
+
+ /* ############################################################################# */
+ }
+ }
+
+ memcpy(outData, outBuffer + bufferOffset, samplesThisPass * sizeof(float));
+ outData += samplesThisPass;
+ memset(outBuffer + bufferOffset, 0 ,samplesThisPass * sizeof(float)); /* clear the output space that we have used */
+ bufferOffset += samplesThisPass;
+ bufferOffset %= _frameSize; /* wrap if needed before the next frame starts */
+ samplesRemaining -= samplesThisPass;
+ }
+}
+
+void Synth_PITCH_SHIFT_FFT_impl::inWindow(float windowedData[], const float *inBuffer, const unsigned int basePopPoint)
+{
+ unsigned int sample;
+ for(sample=0; sample < _frameSize-basePopPoint; sample++)
+ {
+ /* window the data and unroll the buffers */
+ windowedData[sample] = inBuffer[basePopPoint + sample] * windowCoeffient[sample];
+ }
+ for(; sample < _frameSize; sample++)
+ {
+ /* window the data and unroll the buffers */
+ windowedData[sample] = inBuffer[(basePopPoint + sample) - _frameSize] * windowCoeffient[sample];
+ }
+}
+
+void Synth_PITCH_SHIFT_FFT_impl::analysis(fftBin analysisBuf[], const float windowedData[])
+{
+ float lastPhase;
+ float phaseDrift;
+
+ /* do forward FFT */
+ /* const_cast because arts_fft_float is silly */
+ arts_fft_float(_frameSize, 0, const_cast<float *>(windowedData), NULL, real, imag);
+
+ /* the actual analysis loop */
+ for(unsigned int bin=0; bin < _frameSize/2; bin++)
+ {
+ lastPhase = analysisBuf[bin].phase;
+
+ /* compute magnitude and phase */
+ analysisBuf[bin].magnitude = 2.0 * sqrt(real[bin]*real[bin] + imag[bin]*imag[bin]);
+ analysisBuf[bin].phase = atan2(imag[bin],real[bin]);
+
+
+ /* compute phase difference and subtract expected phase difference */
+ phaseDrift = (analysisBuf[bin].phase - lastPhase) - float(phaseDiff[bin % _oversample]);
+
+ /* we now need to map it into the +/- Pi interval */
+ while(phaseDrift < -M_PI)
+ phaseDrift += 2*M_PI;
+ while(phaseDrift > M_PI)
+ phaseDrift -= 2*M_PI;
+
+ /* compute true frequency */
+ analysisBuf[bin].frequency = (bin + ((phaseDrift * _oversample) / (2*M_PI)))*freqPerBin;
+ //analysisBuf[bin].frequency = (bin + (phaseDrift/(2*M_PI)))*freqPerBin;
+ }
+
+}
+
+void Synth_PITCH_SHIFT_FFT_impl::pitchScale(fftBin synthesisBuf[], const fftBin analysisBuf[])
+{
+ unsigned int sourceBin;
+ for(unsigned int destBin=0; destBin < _frameSize/2; destBin++)
+ {
+ sourceBin = (unsigned int)floor(destBin/_scaleFactor);
+ if(sourceBin < _frameSize/2)
+ {
+ /* new bin overrides existing if magnitude is higher */
+ //if(analysisBuf[sourceBin].magnitude > synthesisBuf[destBin].magnitude)
+ //{
+ synthesisBuf[destBin].magnitude = analysisBuf[sourceBin].magnitude;
+ synthesisBuf[destBin].frequency = analysisBuf[sourceBin].frequency * _scaleFactor;
+ //}
+#if 0
+ /* fill empty bins with nearest neighbor */
+ if((synthesisBuf[destBin].frequency == 0.0) && (destBin > 0))
+ {
+ cerr << "Empty bins\n";
+ synthesisBuf[destBin].frequency = synthesisBuf[destBin-1].frequency;
+ synthesisBuf[destBin].magnitude = synthesisBuf[destBin-1].magnitude;
+ }
+#endif
+ }
+ else
+ {
+ synthesisBuf[destBin].magnitude = 0;
+ }
+ }
+#if 0
+ for(unsigned int destBin=0; destBin < _frameSize/2; destBin++)
+ {
+ synthesisBuf[destBin].magnitude = analysisBuf[destBin].magnitude;
+ synthesisBuf[destBin].frequency = analysisBuf[destBin].frequency;
+ }
+#endif
+}
+
+void Synth_PITCH_SHIFT_FFT_impl::synthesis(float windowedData[], fftBin synthesisBuf[])
+{
+ double phaseDrift;
+
+#if 0
+ double test;
+ if(addPi == true)
+ test = -M_PI;
+ else
+ test = 0;
+#endif
+
+ for(unsigned int bin=0;bin < _frameSize/2; bin++)
+ {
+ /* deviation of this bin's phase from one exactly at the true bin frequency */
+ //phaseDrift = (((synthesisBuf[bin].frequency - bin*freqPerBin)/ freqPerBin)*(2*M_PI))/_oversample;
+ phaseDrift = (synthesisBuf[bin].frequency / freqPerBin - bin)*(2*M_PI)/_oversample;
+ //phaseDrift = 0;
+
+
+ /* calculate the real and imag data */
+ real[bin] = synthesisBuf[bin].magnitude * cos(synthesisBuf[bin].phase);
+ imag[bin] = synthesisBuf[bin].magnitude * sin(synthesisBuf[bin].phase);
+
+ /* accumulate current phase for this wave */
+ synthesisBuf[bin].phase += (phaseDrift + phaseDiff[bin % _oversample]);
+ //synthesisBuf[bin].phase += (phaseDrift + phaseDiff[bin % _oversample] + test);
+
+ /* keep it so that -M_PI < phase < M_PI */
+ while(synthesisBuf[bin].phase > M_PI)
+ synthesisBuf[bin].phase -= 2*M_PI;
+ while(synthesisBuf[bin].phase <= -M_PI)
+ synthesisBuf[bin].phase += 2*M_PI;
+
+
+#if 0
+ //this needs to happen so that that 'strongest wave' picking in pitchScale works
+ //but this isn't really the right place to do it
+ synthesisBuf[bin].magnitude = 0;
+ synthesisBuf[bin].frequency = 0;
+#endif
+
+ }
+
+ /* zero the conjugate numbers */
+ for(unsigned int i = _frameSize/2; i < _frameSize; i++)
+ {
+ real[i] = 0.0;
+ imag[i] = 0.0;
+ }
+
+#if 0
+ if(addPi == false)
+ addPi = true;
+ else
+ addPi = false;
+#endif
+
+ /* do the inverse transform */
+ arts_fft_float(_frameSize, 1, real, imag, windowedData, scratch);
+}
+
+void Synth_PITCH_SHIFT_FFT_impl::outWindow(float *outBuffer, const unsigned int basePushPoint, const float windowedData[])
+{
+ unsigned int sample;
+
+ for(sample=0; sample < _frameSize - basePushPoint; sample++)
+ {
+ /* window the data and accumulate it back into the circular buffer */
+ outBuffer[sample+basePushPoint] += 2.0 * windowCoeffient[sample] * windowedData[sample]/(_oversample);
+ }
+ for(; sample < _frameSize; sample++)
+ {
+ /* window the data and accumulate it back into the circular buffer */
+ outBuffer[(sample+basePushPoint) - _frameSize] += 2.0 * windowCoeffient[sample] * windowedData[sample]/(_oversample);
+ }
+}
+
+
+REGISTER_IMPLEMENTATION(Synth_PITCH_SHIFT_FFT_impl);
diff --git a/arts/modules/synth/synth_pitch_shift_impl.cc b/arts/modules/synth/synth_pitch_shift_impl.cc
new file mode 100644
index 00000000..273d9fd7
--- /dev/null
+++ b/arts/modules/synth/synth_pitch_shift_impl.cc
@@ -0,0 +1,196 @@
+/*
+
+ Copyright (C) 2000 Jeff Tranter
+ tranter@pobox.com
+
+ (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include <math.h>
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+class Synth_PITCH_SHIFT_impl : virtual public Synth_PITCH_SHIFT_skel,
+ virtual public StdSynthModule
+{
+protected:
+ float _speed, _frequency;
+
+ enum { MAXDELAY = 44100 };
+ float *dbuffer;
+ float lfopos, b1pos, b2pos, b1inc, b2inc;
+ bool b1reset, b2reset, initialized;
+ int dbpos;
+
+public:
+ Synth_PITCH_SHIFT_impl() : _speed(1.0), _frequency(5.0)
+ {
+ }
+
+ float speed() { return _speed; }
+ void speed(float newSpeed) { _speed = newSpeed; }
+
+ float frequency() { return _frequency; }
+ void frequency(float newFrequency) { _frequency = newFrequency; }
+
+
+ void streamInit()
+ {
+ dbuffer = new float[MAXDELAY];
+ for (dbpos=0; dbpos<MAXDELAY; dbpos++)
+ dbuffer[dbpos] = 0;
+
+ dbpos = 0;
+ initialized = false;
+ lfopos = 0;
+ }
+ void streamEnd()
+ {
+ delete[] dbuffer;
+ }
+
+ void calculateBlock(unsigned long samples)
+ {
+ float *outend = outvalue + samples;
+ float fsr = (float)samplingRate;
+ float pi2 = 2*M_PI;
+ float lfo, b1value, b2value;
+ float lfoposinc = _frequency / fsr;
+
+ if (!initialized)
+ {
+ if (_speed <= 1.0) {
+ b1pos = b2pos = 0.0;
+ b1inc = b2inc = 1.0 - _speed;
+ } else {
+ /* not yet sure what would be a nice initialization here? */
+ b1pos = b2pos = 0.0;
+ b1inc = b2inc = 0.0;
+ }
+ initialized = true;
+ }
+
+ while (outvalue < outend)
+ {
+ /*
+ * fill delay buffer with the input signal
+ */
+ dbuffer[dbpos] = *invalue++;
+
+ lfopos += lfoposinc;
+ lfopos -= floor(lfopos);
+
+ if (lfopos < 0.25) {
+ b1reset = b2reset = false;
+ }
+
+ /*
+ * _speed < 1.0 (downpitching)
+ *
+ * start with current sample and increase delay slowly
+ *
+ * _speed > 1.0 (uppitching)
+ *
+ * start with a sample from long ago and slowly decrease delay
+ */
+ if (!b1reset && lfopos > 0.25) {
+ if (_speed <= 1.0) {
+ b1pos = 0;
+ b1inc = 1 - _speed;
+ } else {
+ b1inc = 1 - _speed;
+ b1pos = 10 + ((-b1inc) * (1 / lfoposinc));
+ /* 10+ are not strictly necessary */
+ }
+ b1reset = true;
+ }
+
+ if (!b2reset && lfopos > 0.75) {
+ if (_speed <= 1.0) {
+ b2pos = 0;
+ b2inc = 1 - _speed;
+ } else{
+ b2inc = 1 - _speed;
+ b2pos = 10 + ((-b2inc) * (1/lfoposinc));
+ /* 10+ are not strictly necessary */
+ }
+ b2reset = true;
+ }
+
+ b1pos += b1inc;
+ b2pos += b2inc;
+
+ int position, position1;
+ double error,int_pos;
+
+ /*
+ * Interpolate value from buffer position 1
+ */
+ error = modf(b1pos, &int_pos);
+
+ position = dbpos - (int)int_pos;
+ if (position < 0)
+ position += MAXDELAY;
+ position1 = position - 1;
+ if (position1 < 0)
+ position1 += MAXDELAY;
+
+ b1value = dbuffer[position] * (1 - error) + dbuffer[position1] * error;
+
+ /*
+ * Interpolate value from buffer position 2
+ */
+ error = modf(b2pos,&int_pos);
+
+ position = dbpos - (int)int_pos;
+ if (position < 0)
+ position += MAXDELAY;
+ position1 = position-1;
+ if ( position1 < 0)
+ position1 += MAXDELAY;
+
+ b2value = dbuffer[position]*(1-error) + dbuffer[position1]*error;
+
+ /*
+ * Calculate output signal from these two buffers
+ */
+
+ lfo = (sin(pi2 * lfopos) + 1) / 2;
+
+ /* position sin lfo variable
+ *------------------------------------------------------------------
+ * lfo value: 0.25 1 1 => buffer 2 is used
+ * 0.75 -1 0 => buffer 1 is used
+ */
+
+ *outvalue++ = b1value * (1.0 - lfo) + b2value * lfo;
+
+ /*
+ * increment delay buffer position
+ */
+ dbpos++;
+ if (dbpos == MAXDELAY)
+ dbpos = 0;
+ }
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_PITCH_SHIFT_impl);
diff --git a/arts/modules/synth/synth_play_pat_impl.cc b/arts/modules/synth/synth_play_pat_impl.cc
new file mode 100644
index 00000000..4913e991
--- /dev/null
+++ b/arts/modules/synth/synth_play_pat_impl.cc
@@ -0,0 +1,529 @@
+# /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmodulessynth.h"
+#include <sys/stat.h>
+#include <stdsynthmodule.h>
+#include <unistd.h>
+#include <math.h>
+#include <debug.h>
+#include <cache.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+using namespace std;
+
+namespace Arts {
+
+namespace PatchLoader {
+ typedef unsigned char byte;
+ typedef unsigned short int word;
+ typedef unsigned int dword;
+ typedef char sbyte;
+ typedef short int sword;
+ typedef int sdword;
+
+ static int pos = 0;
+ static int apos = 0;
+
+ inline void xRead(FILE *file, int len, void *data)
+ {
+ // printf("(0x%2x) - 0x%02x ... reading %d bytes\n",apos,pos,len);
+ pos += len;
+ apos += len;
+ if(fread(data, len, 1, file) != 1)
+ fprintf(stdout, "short read\n");
+ }
+
+ inline void skip(FILE *file, int len)
+ {
+ // printf("(0x%2x) - 0x%02x ... skipping %d bytes\n",apos,pos,len);
+ pos += len;
+ apos += len;
+ while(len > 0)
+ {
+ char junk;
+ if(fread(&junk, 1, 1, file) != 1)
+ fprintf(stdout, "short read\n");
+ len--;
+ }
+ }
+
+
+ inline void readBytes(FILE *file, unsigned char *bytes, int len)
+ {
+ xRead(file, len, bytes);
+ }
+
+ inline void readString(FILE *file, char *str, int len)
+ {
+ xRead(file, len, str);
+ }
+
+ /* readXXX with sizeof(xxx) == 1 */
+ inline void readByte(FILE *file, byte& b)
+ {
+ xRead(file, 1, &b);
+ }
+
+ /* readXXX with sizeof(xxx) == 2 */
+ inline void readWord(FILE *file, word& w)
+ {
+ byte h, l;
+ xRead(file, 1, &l);
+ xRead(file, 1, &h);
+ w = (h << 8) + l;
+ }
+
+ inline void readSWord(FILE *file, sword& sw)
+ {
+ word w;
+ readWord(file, w);
+ sw = (sword)w;
+ }
+
+ /* readXXX with sizeof(xxx) == 4 */
+ inline void readDWord(FILE *file, dword& dw)
+ {
+ byte h, l, hh, hl;
+ xRead(file, 1, &l);
+ xRead(file, 1, &h);
+ xRead(file, 1, &hl);
+ xRead(file, 1, &hh);
+ dw = (hh << 24) + (hl << 16) + (h << 8) + l;
+ }
+
+ struct PatHeader {
+ char id[12]; /* ID='GF1PATCH110' */
+ char manufacturer_id[10]; /* Manufacturer ID */
+ char description[60]; /* Description of the contained Instruments
+ or copyright of manufacturer. */
+ byte instruments; /* Number of instruments in this patch */
+ byte voices; /* Number of voices for sample */
+ byte channels; /* Number of output channels
+ (1=mono,2=stereo) */
+ word waveforms; /* Number of waveforms */
+ word mastervolume; /* Master volume for all samples */
+ dword size; /* Size of the following data */
+ char reserved[36]; /* reserved */
+
+ PatHeader(FILE *file)
+ {
+ readString(file, id, 12);
+ readString(file, manufacturer_id, 10);
+ readString(file, description, 60);
+ /* skip(file, 2);*/
+
+ readByte(file, instruments);
+ readByte(file, voices);
+ readByte(file, channels);
+
+ readWord(file, waveforms);
+ readWord(file, mastervolume);
+ readDWord(file, size);
+
+ readString(file, reserved, 36);
+ }
+ };
+
+ struct PatInstrument {
+ word number;
+ char name[16];
+ dword size; /* Size of the whole instrument in bytes. */
+ byte layers;
+ char reserved[40];
+
+ /* layer? */
+ word layerUnknown;
+ dword layerSize;
+ byte sampleCount; /* number of samples in this layer (?) */
+ char layerReserved[40];
+
+ PatInstrument(FILE *file)
+ {
+ readWord(file, number);
+ readString(file, name, 16);
+ readDWord(file, size);
+ readByte(file, layers);
+ readString(file, reserved, 40);
+
+ /* layer: (?) */
+ readWord(file, layerUnknown);
+ readDWord(file, layerSize);
+ readByte(file, sampleCount);
+ readString(file, reserved, 40);
+ }
+ };
+
+ struct PatPatch {
+ char filename[7]; /* Wave file name */
+ byte fractions; /* Fractions */
+ dword wavesize; /* Wave size.
+ Size of the wave digital data */
+ dword loopStart;
+ dword loopEnd;
+ word sampleRate;
+ dword minFreq;
+ dword maxFreq;
+ dword origFreq;
+ sword fineTune;
+ byte balance;
+ byte filterRate[6];
+ byte filterOffset[6];
+ byte tremoloSweep;
+ byte tremoloRate;
+ byte tremoloDepth;
+ byte vibratoSweep;
+ byte vibratoRate;
+ byte vibratoDepth;
+ byte waveFormat;
+ sword freqScale;
+ word freqScaleFactor;
+ char reserved[36];
+
+ PatPatch(FILE *file)
+ {
+ readString(file, filename, 7);
+ readByte(file, fractions);
+ readDWord(file, wavesize);
+ readDWord(file, loopStart);
+ readDWord(file, loopEnd);
+ readWord(file, sampleRate);
+ readDWord(file, minFreq);
+ readDWord(file, maxFreq);
+ readDWord(file, origFreq);
+ readSWord(file, fineTune);
+ readByte(file, balance);
+ readBytes(file, filterRate, 6);
+ readBytes(file, filterOffset, 6);
+ readByte(file, tremoloSweep);
+ readByte(file, tremoloRate);
+ readByte(file, tremoloDepth);
+ readByte(file, vibratoSweep);
+ readByte(file, vibratoRate);
+ readByte(file, vibratoDepth);
+ readByte(file, waveFormat);
+ readSWord(file, freqScale);
+ readWord(file, freqScaleFactor);
+ readString(file, reserved, 36);
+ }
+ };
+}
+
+class CachedPat : public CachedObject
+{
+protected:
+ struct stat oldstat;
+ string filename;
+ bool initOk;
+ long dataSize;
+
+ CachedPat(Cache *cache, const string& filename);
+ ~CachedPat();
+
+public:
+
+ struct Data {
+ PatchLoader::PatPatch patch;
+ mcopbyte *rawdata;
+ Data(FILE *file)
+ : patch(file)
+ {
+ rawdata = new mcopbyte[patch.wavesize];
+ fread(rawdata, 1, patch.wavesize, file);
+
+ // sign conversion only for 16bit!
+ if(patch.waveFormat & (1 << 1))
+ {
+ for(unsigned int i = 1; i < patch.wavesize; i+=2)
+ rawdata[i] ^= 0x80;
+ }
+
+ // unfold ping-pong loops
+ if(patch.waveFormat & (1 << 3))
+ {
+ int looplen = patch.loopEnd - patch.loopStart;
+ arts_assert(looplen > 0);
+
+ mcopbyte *newdata = new mcopbyte[patch.wavesize + looplen];
+
+ // copy head
+ memcpy(&newdata[0], &rawdata[0], patch.loopStart + looplen);
+
+ // 16 bit unfolding only:
+ for(int i=0; i<looplen; i+=2)
+ {
+ newdata[patch.loopStart+looplen+i] =
+ newdata[patch.loopStart+looplen-i-2];
+ newdata[patch.loopStart+looplen+i+1] =
+ newdata[patch.loopStart+looplen-i-1];
+ }
+
+ // copy tail:
+ memcpy(&newdata[patch.loopStart+2*looplen],
+ &rawdata[patch.loopStart+looplen],
+ patch.wavesize - patch.loopEnd);
+
+ delete[] rawdata;
+ rawdata = newdata;
+
+ patch.wavesize += looplen;
+ patch.loopEnd += looplen;
+ patch.waveFormat &= ~(1 << 3);
+ }
+ }
+ ~Data()
+ {
+ delete[] rawdata;
+ }
+ };
+
+ list<Data*> dList;
+
+ static CachedPat *load(Cache *cache, const string& filename);
+ /**
+ * validity test for the cache - returns false if the object is having
+ * reflecting the correct contents anymore (e.g. if the file on the
+ * disk has changed), and there is no point in keeping it in the cache any
+ * longer
+ */
+ bool isValid();
+ /**
+ * memory usage for the cache
+ */
+ int memoryUsage();
+};
+
+CachedPat *CachedPat::load(Cache *cache, const string& filename)
+{
+ CachedPat *pat;
+
+ pat = (CachedPat *)cache->get(string("CachedPat:")+filename);
+ if(!pat) {
+ pat = new CachedPat(cache, filename);
+
+ if(!pat->initOk) // loading failed
+ {
+ pat->decRef();
+ return 0;
+ }
+ }
+
+ return pat;
+}
+
+bool CachedPat::isValid()
+{
+ if(!initOk)
+ return false;
+
+ struct stat newstat;
+
+ lstat(filename.c_str(),&newstat);
+ return(newstat.st_mtime == oldstat.st_mtime);
+}
+
+int CachedPat::memoryUsage()
+{
+ return dataSize;
+}
+
+CachedPat::CachedPat(Cache *cache, const string& filename)
+ : CachedObject(cache), filename(filename), initOk(false), dataSize(0)
+{
+ setKey(string("CachedPat:")+filename);
+
+ if(lstat(filename.c_str(),&oldstat) == -1)
+ {
+ arts_info("CachedPat: Can't stat file '%s'", filename.c_str());
+ return;
+ }
+
+ FILE *patfile = fopen(filename.c_str(), "r");
+ if(patfile)
+ {
+ //PatchLoader::PatHeader header(patfile);
+ PatchLoader::PatInstrument ins(patfile);
+
+ for(int i=0;i<ins.sampleCount;i++)
+ {
+ Data *data = new Data(patfile);
+ dList.push_back(data);
+ dataSize += data->patch.wavesize;
+ }
+ fclose(patfile);
+
+ arts_debug("loaded pat %s",filename.c_str());
+ arts_debug(" %d patches, datasize total is %d bytes",
+ ins.sampleCount, dataSize);
+
+ initOk = true;
+ }
+}
+
+CachedPat::~CachedPat()
+{
+ while(!dList.empty())
+ {
+ delete dList.front();
+ dList.pop_front();
+ }
+}
+
+class Synth_PLAY_PAT_impl : virtual public Synth_PLAY_PAT_skel,
+ virtual public StdSynthModule
+{
+protected:
+ string _filename;
+ CachedPat *pat;
+ CachedPat::Data *selected;
+ float fpos;
+
+public:
+ Synth_PLAY_PAT_impl()
+ {
+ pat = 0;
+ selected = 0;
+ }
+
+ void unload()
+ {
+ if(pat)
+ {
+ pat->decRef();
+ pat = 0;
+ }
+ }
+
+ ~Synth_PLAY_PAT_impl()
+ {
+ unload();
+ }
+
+ void streamStart()
+ {
+ fpos = 0.0;
+ selected = 0;
+ }
+
+ void calculateBlock(unsigned long samples)
+ {
+ /* the normal offset is 60
+ 60 = 12*5
+ so we scale with 2^5 = 32
+ */
+ float freq = frequency[0]; /* * 32.0 / 2.0; * why /2.0? */
+ int ifreq = (int)(freq * 1024.0);
+ if(!selected && pat)
+ {
+ int bestdiff = 20000 * 1024;
+
+ list<CachedPat::Data*>::iterator i;
+ for(i = pat->dList.begin(); i != pat->dList.end(); i++)
+ {
+ int diff = ::abs(double(ifreq - (*i)->patch.origFreq));
+ if(diff < bestdiff)
+ {
+ selected = *i;
+ bestdiff = diff;
+ }
+ }
+
+ /* drums */
+ if(selected && selected->patch.freqScaleFactor == 0)
+ ifreq = selected->patch.origFreq;
+ }
+ if(selected)
+ {
+ const PatchLoader::PatPatch& patch = selected->patch;
+
+ /*
+ * thats just a *guess*, I have no idea how tuning actually
+ * should work
+ */
+#if 0
+ float tonetune = float(pat.fineTune)/float(pat.freqScaleFactor);
+ float tuning = pow(2.0, tonetune/12.0);
+ freq *= tuning;
+#endif
+ //printf("tonetune: %f\n",tonetune);
+ //printf("tuning: %f\n",tuning);
+
+ float step = double(patch.sampleRate)/samplingRateFloat *
+ float(ifreq) / float(patch.origFreq);
+ for(unsigned int i = 0; i < samples; i++)
+ {
+ // ASSUME 16bit signed, native byte order
+ int ifpos = int(fpos)*2;
+
+ // looped? bidi? backw?
+ if((patch.waveFormat & ((1 << 2) + (1 << 3) + (1 << 4)))
+ == (1 << 2))
+ {
+ while(ifpos >= signed(patch.loopEnd))
+ {
+ ifpos -= (patch.loopEnd - patch.loopStart);
+ fpos -= (patch.loopEnd - patch.loopStart)/2;
+ }
+ }
+
+ short int *x = (short int *)&selected->rawdata[ifpos];
+ float sample = (ifpos >= 0 && ifpos < signed(patch.wavesize))?
+ x[0]/32768.0:0.0;
+ float sample1 = ((ifpos+2) >= 0 && (ifpos+2) < signed(patch.wavesize))?
+ x[1]/32768.0:0.0;
+ float error = fpos - (int)fpos;
+ outvalue[i] = sample * (1.0-error) + sample1 * error;
+
+ fpos += step;
+ }
+ }
+ else
+ {
+ for(unsigned int i = 0; i < samples; i++)
+ outvalue[i] = 0.0;
+ }
+ }
+
+ void clearDList()
+ {
+ selected = 0;
+
+ }
+
+ string filename() { return _filename; }
+ void filename(const string& newFile)
+ {
+ if(newFile == _filename) return;
+
+ unload();
+ pat = CachedPat::load(Cache::the(), newFile);
+
+ _filename = newFile;
+ filename_changed(newFile);
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_PLAY_PAT_impl);
+
+}
diff --git a/arts/modules/synth/synth_pscale_impl.cc b/arts/modules/synth/synth_pscale_impl.cc
new file mode 100644
index 00000000..9cf972db
--- /dev/null
+++ b/arts/modules/synth/synth_pscale_impl.cc
@@ -0,0 +1,53 @@
+/*
+
+ Copyright (C) 2000 Jeff Tranter
+ tranter@pobox.com
+
+ (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+class Synth_PSCALE_impl : virtual public Synth_PSCALE_skel,
+ virtual public StdSynthModule
+{
+protected:
+ float _top;
+
+public:
+ float top() { return _top; }
+ void top(float newTop) { _top = newTop; }
+
+ void calculateBlock(unsigned long samples)
+ {
+ for (unsigned int i=0; i < samples; i++)
+ {
+ if (pos[i] >= _top)
+ outvalue[i] = invalue[i] * (1 - pos[i])/(1 - _top);
+ else
+ outvalue[i] = invalue[i] * pos[i] / _top;
+ }
+ }
+
+};
+
+REGISTER_IMPLEMENTATION(Synth_PSCALE_impl);
diff --git a/arts/modules/synth/synth_rc_impl.cc b/arts/modules/synth/synth_rc_impl.cc
new file mode 100644
index 00000000..0c62c4c6
--- /dev/null
+++ b/arts/modules/synth/synth_rc_impl.cc
@@ -0,0 +1,110 @@
+/*
+
+ Copyright (C) 2000 Jeff Tranter
+ tranter@pobox.com
+
+ (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include <math.h>
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+class Synth_RC_impl : virtual public Synth_RC_skel,
+ virtual public StdSynthModule
+{
+protected:
+ float _b, _f;
+ float B, dB;
+ float F, dF, oF, oU, U, Fsoll, Bsoll;
+ float oldvalue;
+
+public:
+ float b() { return _b; }
+ void b(float newB) { _b = newB; }
+
+ float f() { return _f; }
+ void f(float newF) { _f = newF; }
+
+ void streamInit()
+ {
+ oldvalue = 0;
+ B = 0;
+ F = 0; oF = 0;
+ U = 0; oU = 0;
+ }
+
+ void calculateBlock(unsigned long samples)
+ {
+ unsigned long i, hits;
+ const float zero_lower = -0.00000001;
+ const float zero_upper = 0.00000001;
+
+ if (zero_lower < invalue[0] && invalue[0] < zero_upper)
+ {
+ /* for comments see equalizer.cc/Synth_STD_EQUALIZER implementation */
+
+ /*
+ * This implementation differs from the implementation there,
+ * because it is done as a kind of powersafing. If no input is
+ * found, then no output is generated.
+ */
+ if (zero_lower < oldvalue && oldvalue < zero_upper)
+ {
+ oldvalue = 0.0;
+ B = 0.0;
+ F = 0.0; oF = 0.0;
+ U = 0.0; oU = 0.0;
+ hits = 0;
+ for (i=0; i<samples; i++)
+ {
+ if (zero_lower < invalue[i] && invalue[i] < zero_upper)
+ {
+ // try to zero out the whole block
+ outvalue[i] = 0.0;
+ hits++;
+ }
+ }
+ if (hits == samples) return;
+ }
+ }
+
+ for (i=0; i<samples; i++)
+ {
+ B = B + (invalue[i] - oldvalue); /* input into RC */
+ oldvalue = invalue[i];
+
+ Bsoll = U - oU;
+ oU = U;
+ dB = (Bsoll - B) / _b;
+ B += dB;
+ U -= dB;
+ Fsoll = U;
+ dF = (Fsoll - F) / _f;
+ F += dF; /* Energie dF wird ins Feld uebertragen */
+ U -= dF;
+ outvalue[i] = (F - oF) * (_b + _f);
+ oF = F;
+ }
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_RC_impl);
diff --git a/arts/modules/synth/synth_sequence_freq_impl.cc b/arts/modules/synth/synth_sequence_freq_impl.cc
new file mode 100644
index 00000000..64ecd512
--- /dev/null
+++ b/arts/modules/synth/synth_sequence_freq_impl.cc
@@ -0,0 +1,131 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000 Jeff Tranter <tranter@pobox.com>
+ 2003 Matthias Kretz <kretz@kde.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+#include <debug.h>
+
+using namespace std;
+using namespace Arts;
+
+class Synth_SEQUENCE_FREQ_impl : virtual public Synth_SEQUENCE_FREQ_skel,
+ virtual public StdSynthModule
+{
+protected:
+ float _speed;
+ string _seq;
+ long posn, delay;
+ float *fsequence;
+ float *slen;
+
+public:
+ Synth_SEQUENCE_FREQ_impl()
+ : _speed( 1 )
+ , fsequence( 0 )
+ , slen( 0 )
+ {
+ }
+
+ ~Synth_SEQUENCE_FREQ_impl()
+ {
+ delete[] fsequence;
+ delete[] slen;
+ }
+
+ float speed() { return _speed; }
+ void speed(float newSpeed) { _speed = newSpeed; }
+
+ string seq() { return _seq; }
+ void seq(const string &newSeq)
+ {
+ _seq = newSeq;
+ parseSeqString();
+ }
+
+ void parseSeqString()
+ {
+ delete[] fsequence;
+ delete[] slen;
+
+ long bufferlen = _seq.length();
+ fsequence = new float[ bufferlen ];
+ slen = new float[ bufferlen ];
+
+ int i = 0;
+ int oldpos = 0;
+ int pos = _seq.find_first_of( ",;", 0 );
+ arts_debug( "tokenizer: parse %s", _seq.c_str() );
+ while( pos > 0 )
+ {
+ string token = _seq.substr( oldpos, pos - oldpos );
+ arts_debug( "tokenizer: pos = %d, oldpos = %d, token = %s", pos, oldpos, token.c_str() );
+ handleToken( token, i++ );
+ oldpos = pos + 1;
+ pos = _seq.find_first_of( ",;", oldpos );
+ }
+ string token = _seq.substr( oldpos, _seq.length() - oldpos );
+ arts_debug( "tokenizer: pos = %d, oldpos = %d, token = %s", pos, oldpos, token.c_str() );
+ handleToken( token, i++ );
+ fsequence[ i ] = -1.0;
+ }
+
+ void handleToken( const string & token, int i )
+ {
+ int colon = token.find( ':' );
+ if( colon > -1 )
+ {
+ slen[ i ] = atof( &token.c_str()[ colon + 1 ] );
+ fsequence[ i ] = atof( token.substr( 0, colon ).c_str() );
+ }
+ else
+ {
+ slen[ i ] = 1;
+ fsequence[ i ] = atof( token.c_str() );
+ }
+ }
+
+ void streamInit()
+ {
+ delay = 0;
+ posn = 0;
+ }
+
+ void calculateBlock(unsigned long samples)
+ {
+ for (unsigned int i=0; i < samples; i++)
+ {
+ delay++;
+ if (delay > _speed * samplingRate * slen[posn])
+ {
+ posn++;
+ if (fsequence[posn] == -1.0)
+ posn = 0;
+ delay = 0;
+ }
+ pos[i] = (int)delay / (_speed * samplingRate * slen[posn]);
+ frequency[i] = fsequence[posn];
+ }
+ }
+
+};
+
+REGISTER_IMPLEMENTATION(Synth_SEQUENCE_FREQ_impl);
+// vim: sw=4 ts=4 noet
diff --git a/arts/modules/synth/synth_sequence_impl.cc b/arts/modules/synth/synth_sequence_impl.cc
new file mode 100644
index 00000000..9c2bead4
--- /dev/null
+++ b/arts/modules/synth/synth_sequence_impl.cc
@@ -0,0 +1,132 @@
+ /*
+
+ Copyright (C) 2000 Jeff Tranter
+ tranter@pobox.com
+
+ (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ (C) 2003 Matthias Kretz
+ kretz@kde.org
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace std;
+using namespace Arts;
+
+class Synth_SEQUENCE_impl : virtual public Synth_SEQUENCE_skel,
+ virtual public StdSynthModule
+{
+protected:
+ float _speed;
+ string _seq;
+ long posn, delay;
+ float *fsequence;
+ float *slen;
+
+public:
+ Synth_SEQUENCE_impl()
+ : _speed( 1 )
+ , fsequence( 0 )
+ , slen( 0 )
+ {
+ }
+
+ ~Synth_SEQUENCE_impl()
+ {
+ delete [] fsequence;
+ delete [] slen;
+ }
+
+ float speed() { return _speed; }
+ void speed(float newSpeed) { _speed = newSpeed; }
+
+ string seq() { return _seq; }
+ void seq(const string &newSeq) { _seq = newSeq; }
+
+ void streamInit()
+ {
+ char notea[][4]= {"C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#-", "B-", "\0"};
+ char noteb[][3]= {"C-", "Db", "D-", "Eb", "E-", "F-", "Gb", "G-", "Ab", "A-", "Bb", "B-", "\0"};
+ float freq[] = {261.7,277.2,293.7,311.2,329.7,349.3,370.0,392.0,415.3,440.0, 466.2, 493.9, 0 };
+ float zhoch[] = {1,2,4,8,16,32,64,128,256};
+ int s = 0, i, oktave;
+ char *nptr;
+ float f;
+ char buffer[1024];
+
+ strncpy(buffer, _seq.c_str(), 1023);
+ buffer[ 1023 ] = '\0';
+ long bufferlen = strlen(buffer);
+ delete[] fsequence;
+ delete[] slen;
+ fsequence = new float[ bufferlen ];
+ slen = new float[ bufferlen ];
+ nptr = strtok(buffer, ",;");
+ while (nptr)
+ {
+ if (nptr[3] == ':')
+ slen[s] = atof(&nptr[4]);
+ else
+ slen[s] = 1;
+ fprintf(stderr," <%d> %s\n", s, nptr);
+ oktave = atol(&nptr[2]);
+ nptr[2] = 0;
+ f = 0;
+ for (i=0; notea[i][0]; i++)
+ if (strcmp(nptr,notea[i]) == 0)
+ f = freq[i];
+ for (i=0; noteb[i][0]; i++)
+ if (strcmp(nptr, noteb[i]) == 0)
+ f = freq[i];
+ f *= zhoch[oktave] / zhoch[4];
+ fsequence[s++] = f;
+ fprintf(stderr, ">%2.2f\n", f);
+ nptr = strtok(NULL,",;");
+ }
+ fsequence[s] = 0;
+ delay = 0;
+ posn = 0;
+ }
+
+ void calculateBlock(unsigned long samples)
+ {
+ for (unsigned int i=0; i < samples; i++)
+ {
+ delay++;
+ if (delay > _speed * samplingRate * slen[posn])
+ {
+ posn++;
+ if (fsequence[posn] == 0)
+ posn = 0;
+ delay = 0;
+ }
+ pos[i] = (int)delay / (_speed * samplingRate * slen[posn]);
+ frequency[i] = fsequence[posn];
+ }
+ }
+
+};
+
+REGISTER_IMPLEMENTATION(Synth_SEQUENCE_impl);
+// vim: sw=4 ts=4 noet
diff --git a/arts/modules/synth/synth_shelve_cutoff_impl.cc b/arts/modules/synth/synth_shelve_cutoff_impl.cc
new file mode 100644
index 00000000..888480e3
--- /dev/null
+++ b/arts/modules/synth/synth_shelve_cutoff_impl.cc
@@ -0,0 +1,71 @@
+/*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+#include "c_filter_stuff.h"
+
+using namespace Arts;
+
+class Synth_SHELVE_CUTOFF_impl : virtual public Synth_SHELVE_CUTOFF_skel,
+ virtual public StdSynthModule
+{
+protected:
+ filter f;
+public:
+ void streamInit();
+ void calculateBlock(unsigned long samples);
+};
+
+REGISTER_IMPLEMENTATION(Synth_SHELVE_CUTOFF_impl);
+
+void Synth_SHELVE_CUTOFF_impl::streamInit()
+{
+ initfilter(&f);
+}
+
+void Synth_SHELVE_CUTOFF_impl::calculateBlock(unsigned long samples)
+{
+ float filterfrequency = frequency[0];
+
+ // the shelve-lowpass-filter is extremely sensitive to frequencies which
+ // are out of it's range (it produces NaN's then) so we'll be careful ;)
+ if(filterfrequency > 22000.0) filterfrequency = 22000.0;
+ if(filterfrequency < 1.0) filterfrequency = 1.0;
+ setfilter_shelvelowpass(&f,filterfrequency,80.0);
+
+ unsigned long i;
+
+ for(i=0;i<samples;i++)
+ {
+ // better paste applyfilter here instead of:
+ // *outsig++ = 0.95 * applyfilter(&f,*insig++);
+ f.x = invalue[i];
+ f.y = f.cx * f.x + f.cx1 * f.x1 + f.cx2 * f.x2
+ + f.cy1 * f.y1 + f.cy2 * f.y2;
+ f.x2 = f.x1;
+ f.x1 = f.x;
+ f.y2 = f.y1;
+ f.y1 = f.y;
+ outvalue[i] = 0.95 * f.y;
+ }
+}
diff --git a/arts/modules/synth/synth_std_equalizer_impl.cc b/arts/modules/synth/synth_std_equalizer_impl.cc
new file mode 100644
index 00000000..3500503b
--- /dev/null
+++ b/arts/modules/synth/synth_std_equalizer_impl.cc
@@ -0,0 +1,207 @@
+/*
+
+ Copyright (C) 2000 Jeff Tranter
+ tranter@pobox.com
+
+ (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ (C) 1999 Martin Lorenz
+ lorenz@ch.tum.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include <math.h>
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+class Synth_STD_EQUALIZER_impl : virtual public Synth_STD_EQUALIZER_skel,
+ virtual public StdSynthModule
+{
+protected:
+ float _low, _mid, _high, _frequency, _q;
+ float tlow, tmid, thigh, tfrequency;
+ float a1, a2, b0, b1, b2, x_0, x_1, x_2, y_1, y_2;
+ unsigned long all;
+
+public:
+ float low() { return _low; }
+ void low(float newLow)
+ {
+ if(newLow != _low)
+ {
+ _low = newLow;
+ calcParameters();
+ high_changed(newLow);
+ }
+ }
+
+
+ float mid() { return _mid; }
+ void mid(float newMid)
+ {
+ if(newMid != _mid)
+ {
+ _mid = newMid;
+ calcParameters();
+ mid_changed(newMid);
+ }
+ }
+
+ float high() { return _high; }
+ void high(float newHigh)
+ {
+ if(newHigh != _high)
+ {
+ _high = newHigh;
+ calcParameters();
+ high_changed(newHigh);
+ }
+ }
+
+
+ float frequency() { return _frequency; }
+ void frequency(float newFrequency)
+ {
+ if(newFrequency != _frequency)
+ {
+ _frequency = newFrequency;
+ calcParameters();
+ frequency_changed(newFrequency);
+ }
+ }
+
+ float q() { return _q; }
+ void q(float newQ)
+ {
+ if(newQ != _q)
+ {
+ _q = newQ;
+ calcParameters();
+ q_changed(newQ);
+ }
+ }
+
+ Synth_STD_EQUALIZER_impl() {
+ _low = _mid = _high = 0; _q = 0.5;
+ _frequency = 300;
+ }
+
+ void calcParameters()
+ {
+ /*
+
+ * _low, _mid, _high are in dB, transform them to tlow, tmid,
+ * thigh using:
+ * -6dB => 0.5 ; 0dB => 1 ; 6dB = 2.0 ; ...
+ */
+
+ tlow = exp(_low * 0.115524530093324); // exp(p[LOW]*ln(2)/6)
+ tmid = exp(_mid * 0.115524530093324);
+ thigh = exp(_high * 0.115524530093324);
+
+ // _frequency is given in Hz, we need the w-value (and do clipping if
+ // it exceeds SR/2)
+ const float SAMPLING_RATE = 44100.0;
+ tfrequency = _frequency;
+ if (tfrequency > SAMPLING_RATE / 2.01)
+ tfrequency = SAMPLING_RATE / 2.01;
+ float w = 2 * M_PI * tfrequency / SAMPLING_RATE;
+
+ // Calculations:
+ float t = 1/tan(w/2);
+ float tq = t/_q;
+ float t2 = t*t;
+
+ float a0 = 1+tq+t2;
+ float a0r = 1/a0;
+
+ // and now the real filter values:
+ a1 = (2 - 2 * t2) * a0r;
+ a2 = (1 - tq + t2) * a0r;
+ b0 = (tlow + tmid * tq + thigh * t2) * a0r;
+ b1 = (2 * tlow -2 * thigh * t2) * a0r;
+ b2 = (tlow - tmid * tq + thigh * t2) * a0r;
+
+ // TODO: try if we need that here, or if we can change filter
+ // coefficients without setting the state to 0
+ x_0 = x_1 = x_2 = y_1 = y_2 = 0.0;
+ all = 0;
+ }
+
+ void streamInit()
+ {
+ calcParameters();
+ }
+
+ void calculateBlock(unsigned long samples)
+ {
+ all += samples;
+
+ if (all > 1024)
+ {
+ /* The _problem_: (observed on a PII-350)
+ *
+ * I am not quite sure what happens here, but it seems to be like that:
+ *
+ * If an ordinary signal (a mp3 for instance) is sent through the
+ * equalizer, and then no more input is given (zeros as input),
+ * the y_1 and y_2 values oscillate for some time, coming closer and
+ * close to zero.
+ *
+ * But before the reach zero, they reach the smallest negative number
+ * (or smallest positive, or whatever), and stay there
+ * (because 0.005*smallest_negative will remain smallest_negative).
+ *
+ * Since then, the CPU usage for all operations on these floats
+ * increases, (since handling of smallest_negative seems to be a rare
+ * case).
+ *
+ * The _fix_:
+ *
+ * We observe the value of y_1. If it's very close to zero (may be as
+ * well smallest_positive/smallest_negative), we set it to zero,
+ * together with y_2. This shouldn't significantly influence
+ * correctness of the filter, but effectively solves the problem.
+ *
+ * If you don't believe me, try without this fix and tell me what
+ * happens on your computer.
+ */
+ const float zero_lower =-0.00000001;
+ const float zero_upper = 0.00000001;
+ all = 0;
+
+ if(zero_lower < y_1 && y_1 < zero_upper)
+ y_1 = y_2 = 0.0;
+ }
+
+ unsigned long i;
+ float tmp;
+ for (i=0; i<samples; i++)
+ {
+ x_0 = invalue[i];
+ tmp = x_0 * b0 + x_1 * b1 + x_2 * b2 - y_1 * a1 - y_2 * a2;
+ x_2 = x_1; x_1 = x_0; y_2 = y_1; y_1 = tmp;
+ outvalue[i] = tmp;
+ }
+ }
+
+};
+
+REGISTER_IMPLEMENTATION(Synth_STD_EQUALIZER_impl);
diff --git a/arts/modules/synth/synth_tremolo_impl.cc b/arts/modules/synth/synth_tremolo_impl.cc
new file mode 100644
index 00000000..2c703c52
--- /dev/null
+++ b/arts/modules/synth/synth_tremolo_impl.cc
@@ -0,0 +1,51 @@
+/*
+
+ Copyright (C) 2000 Jeff Tranter
+ tranter@pobox.com
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include <math.h>
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+// The tremolo module modulates the amplitude according to a LFO-Wave.
+// Traditionally you would use a sine wave but why limit yourself?
+// What you get is a very intense effect that cuts through most
+// arrangements because of its high dynamic range. The tremolo effect
+// is still one of guitarists favorite effects although it's not as
+// popular as in the 1960's.
+
+class Synth_TREMOLO_impl : virtual public Synth_TREMOLO_skel,
+ virtual public StdSynthModule
+{
+public:
+ void calculateBlock(unsigned long samples)
+ {
+ unsigned long i;
+ for(i=0; i<samples; i++)
+ {
+ // simply modulate the amplitude
+ outvalue[i] = invalue[i] * fabs(inlfo[i]);
+ }
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_TREMOLO_impl);
diff --git a/arts/modules/synth/synth_wave_pulse_impl.cc b/arts/modules/synth/synth_wave_pulse_impl.cc
new file mode 100644
index 00000000..52a2c35e
--- /dev/null
+++ b/arts/modules/synth/synth_wave_pulse_impl.cc
@@ -0,0 +1,52 @@
+ /*
+
+ Copyright (C) 2000 Jeff Tranter
+ tranter@pobox.com
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+// A square wave with a programmable duty cycle. The duty cycle should
+// be in the range 0..1.
+
+class Synth_WAVE_PULSE_impl : virtual public Synth_WAVE_PULSE_skel,
+ virtual public StdSynthModule
+{
+protected:
+ float _dutycycle;
+
+public:
+ float dutycycle() { return _dutycycle; }
+
+ void dutycycle(float newDutycycle) { _dutycycle = newDutycycle; }
+
+ void calculateBlock(unsigned long samples)
+ {
+ unsigned long i;
+ for(i=0;i<samples;i++)
+ {
+ outvalue[i] = (pos[i] < _dutycycle) ? 1.0 : -1.0;
+ }
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_WAVE_PULSE_impl);
diff --git a/arts/modules/synth/synth_wave_softsaw_impl.cc b/arts/modules/synth/synth_wave_softsaw_impl.cc
new file mode 100644
index 00000000..3818830f
--- /dev/null
+++ b/arts/modules/synth/synth_wave_softsaw_impl.cc
@@ -0,0 +1,50 @@
+ /*
+
+ Copyright (C) 2000 Jeff Tranter
+ tranter@pobox.com
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include <math.h>
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+namespace Arts {
+
+// This is a sawtooth that has it's trough rounded off using a cosine
+// wave.
+
+class Synth_WAVE_SOFTSAW_impl : virtual public Synth_WAVE_SOFTSAW_skel,
+ virtual public StdSynthModule
+{
+public:
+ void calculateBlock(unsigned long cycles)
+ {
+ for(unsigned long i=0; i<cycles; i++)
+ if ((pos[i] < 0.1) || (pos[i] > 0.9))
+ outvalue[i] = 1 - pos[i] * 2;
+ else
+ outvalue[i] = cos(pos[i]*2*M_PI);
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_WAVE_SOFTSAW_impl);
+
+}
diff --git a/arts/modules/synth/synth_wave_square_impl.cc b/arts/modules/synth/synth_wave_square_impl.cc
new file mode 100644
index 00000000..73e97daa
--- /dev/null
+++ b/arts/modules/synth/synth_wave_square_impl.cc
@@ -0,0 +1,39 @@
+ /*
+
+ Copyright (C) 2000 Jeff Tranter
+ tranter@pobox.com
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+class Synth_WAVE_SQUARE_impl : virtual public Synth_WAVE_SQUARE_skel,
+ virtual public StdSynthModule
+{
+public:
+ void calculateBlock(unsigned long samples)
+ {
+ unsigned long i;
+ for(i=0;i<samples;i++) outvalue[i] = (pos[i] < 0.5) ? 1.0 : -1.0;
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_WAVE_SQUARE_impl);
diff --git a/arts/modules/synth/synth_wave_tri_impl.cc b/arts/modules/synth/synth_wave_tri_impl.cc
new file mode 100644
index 00000000..c6ef34ed
--- /dev/null
+++ b/arts/modules/synth/synth_wave_tri_impl.cc
@@ -0,0 +1,39 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+class Synth_WAVE_TRI_impl : virtual public Synth_WAVE_TRI_skel,
+ virtual public StdSynthModule
+{
+public:
+ void calculateBlock(unsigned long samples)
+ {
+ unsigned long i;
+ for(i=0;i<samples;i++) outvalue[i] = (pos[i] - 0.5) * 2.0;
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_WAVE_TRI_impl);
diff --git a/arts/modules/synth/synth_xfade_impl.cc b/arts/modules/synth/synth_xfade_impl.cc
new file mode 100644
index 00000000..acf473ca
--- /dev/null
+++ b/arts/modules/synth/synth_xfade_impl.cc
@@ -0,0 +1,45 @@
+/*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsmodulessynth.h"
+#include "stdsynthmodule.h"
+
+using namespace Arts;
+
+class Synth_XFADE_impl : virtual public Synth_XFADE_skel,
+ virtual public StdSynthModule
+{
+public:
+ void calculateBlock(unsigned long samples)
+ {
+ unsigned long i;
+
+ for(i=0;i<samples;i++)
+ {
+ float p = (percentage[i]+1)/2;
+
+ outvalue[i] = invalue1[i]*p + invalue2[i]*(1-p);
+ }
+ }
+};
+
+REGISTER_IMPLEMENTATION(Synth_XFADE_impl);
diff --git a/arts/runtime/ArtsBuilderLoader.mcopclass b/arts/runtime/ArtsBuilderLoader.mcopclass
new file mode 100644
index 00000000..cceb4765
--- /dev/null
+++ b/arts/runtime/ArtsBuilderLoader.mcopclass
@@ -0,0 +1,4 @@
+Interface=Arts::ArtsBuilderLoader,Arts::Loader,Arts::Object
+LoadLanguage=aRts
+Language=C++
+Library=libartsbuilder.la
diff --git a/arts/runtime/LocalFactory.mcopclass b/arts/runtime/LocalFactory.mcopclass
new file mode 100644
index 00000000..e35f778b
--- /dev/null
+++ b/arts/runtime/LocalFactory.mcopclass
@@ -0,0 +1,3 @@
+Interface=Arts::LocalFactory,Arts::ObjectFactory,Arts::Object
+Language=C++
+Library=libartsbuilder.la
diff --git a/arts/runtime/Makefile.am b/arts/runtime/Makefile.am
new file mode 100644
index 00000000..b10abe7a
--- /dev/null
+++ b/arts/runtime/Makefile.am
@@ -0,0 +1,38 @@
+####### Runtime part of artsbuilder: this part will get dynamically loaded
+# into the aRts server (or other apps) to create structures which are designed
+# in artsbuilder. So to speak it's the "loader for the .arts language".
+
+AM_CXXFLAGS = -DEXAMPLES_DIR='"$(arts_datadir)/artsbuilder/examples"'
+INCLUDES= -I$(arts_includes) $(all_includes)
+
+lib_LTLIBRARIES = libartsbuilder.la
+
+libartsbuilder_la_SOURCES = artsbuilder.cc sequenceutils.cc \
+ structurebuilder_impl.cc structures_impl.cc moduleinfo.cc \
+ compatibility.cc localfactory_impl.cc artsbuilderloader_impl.cc
+
+libartsbuilder_la_LIBADD = -lmcop -lartsflow $(LIBDL)
+libartsbuilder_la_COMPILE_FIRST = artsbuilder.h
+libartsbuilder_la_LDFLAGS = $(all_libraries) -L$(arts_libraries) \
+ -no-undefined
+
+artsbuilder.lo: artsbuilder.h
+artsbuilder.mcopclass: artsbuilder.h
+artsbuilder.mcoptype: artsbuilder.h
+artsbuilder.h artsbuilder.cc: $(srcdir)/artsbuilder.idl $(MCOPIDL)
+ $(MCOPIDL) -t -I$(arts_includes) $(srcdir)/artsbuilder.idl
+
+DISTCLEANFILES = artsbuilder.cc artsbuilder.h \
+ artsbuilder.mcoptype artsbuilder.mcopclass
+
+####### install idl files
+
+artsincludedir = $(includedir)/arts
+artsinclude_HEADERS = artsbuilder.h artsbuilder.idl
+
+mcopclassdir = $(libdir)/mcop/Arts
+mcopclass_DATA = StructureBuilder.mcopclass StructureDesc.mcopclass \
+ LocalFactory.mcopclass ArtsBuilderLoader.mcopclass
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = artsbuilder.mcoptype artsbuilder.mcopclass
diff --git a/arts/runtime/StructureBuilder.mcopclass b/arts/runtime/StructureBuilder.mcopclass
new file mode 100644
index 00000000..5ea71358
--- /dev/null
+++ b/arts/runtime/StructureBuilder.mcopclass
@@ -0,0 +1,3 @@
+Interface=Arts::StructureBuilder,Arts::Object
+Language=C++
+Library=libartsbuilder.la
diff --git a/arts/runtime/StructureDesc.mcopclass b/arts/runtime/StructureDesc.mcopclass
new file mode 100644
index 00000000..3a7039e0
--- /dev/null
+++ b/arts/runtime/StructureDesc.mcopclass
@@ -0,0 +1,3 @@
+Interface=Arts::StructureDesc,Arts::Object
+Language=C++
+Library=libartsbuilder.la
diff --git a/arts/runtime/artsbuilder.idl b/arts/runtime/artsbuilder.idl
new file mode 100644
index 00000000..42ff393a
--- /dev/null
+++ b/arts/runtime/artsbuilder.idl
@@ -0,0 +1,228 @@
+/*
+ * DISCLAIMER: The interfaces in artsbuilder.idl (and the derived .cc/.h files)
+ * DO NOT GUARANTEE BINARY COMPATIBILITY YET.
+ *
+ * They are intended for developers. You shouldn't expect that applications in
+ * binary form will be fully compatibile with further releases of these
+ * interfaces.
+ */
+
+#include <core.idl>
+#include <artsflow.idl>
+module Arts {
+ /*
+ * incoming or outgoing port?
+ */
+ enum PortDirection {input, output};
+
+ /**
+ * ConnType (maybe obsolete)
+ *
+ * ConnType: (connection type) this is wether this value is used
+ *
+ * - once (such as a filename of a waveplugin) -> property
+ * this implies that the allowed connection is only value
+ *
+ * - event based (such as midi events) -> event
+ * when events arrive, they are processed, when no events arrive,
+ * don't care
+ *
+ * - stream based (such as audio streams) -> stream
+ * every calculation of the module consumes/creates a sample
+ * that means: no data = no calculation possible
+ *
+ * WARNING: This is part of the artsbuilder dynamic programming interface
+ * as the MCOP port isn't there yet, this stuff may change
+ */
+ enum PortConnType { conn_stream, conn_event, conn_property};
+
+ /**
+ * PortType (maybe obsolete)
+ *
+ * isMultiPort specifies if the port can take multiple incoming
+ * connections or not. This is only relevant/allowed for input ports,
+ * the output of all output ports may be connected to any amount of
+ * receivers.
+ *
+ * Ports which can take multiple connections are handled differently
+ * internally. (Also, artsbuilder needs to know whether to allow multi-
+ * connections or not).
+ *
+ * WARNING: This is part of the artsbuilder dynamic programming interface
+ * as the MCOP port isn't there yet, this stuff may change
+ */
+ struct PortType {
+ PortDirection direction;
+ string dataType;
+ PortConnType connType;
+ boolean isMultiPort;
+ };
+
+ struct ModuleInfo {
+ string name;
+
+ //--- internal information:
+ // ports, first the input ports, then the output ports
+ sequence<PortType> ports;
+ sequence<string> portnames;
+ boolean isInterface;
+ boolean isStructure;
+ };
+
+ interface PortDesc;
+ interface ModuleDesc;
+ interface StructureDesc;
+ interface StructurePortDesc;
+
+ interface PortDesc {
+ // internal:
+ void constructor(ModuleDesc parent, string name, PortType type);
+
+ // ID is guaranteed to be unique in the structure the port belongs to
+ readonly attribute long ID;
+ readonly attribute ModuleDesc parent;
+
+ // Name is guaranteed to be unique for each module (no two in/out-
+ // ports with the same name allowed)
+ readonly attribute string name;
+ readonly attribute PortType type;
+
+ /*
+ * - for input channels, one of those must be true (only event
+ * channels may remain unconnected),
+ * - for output channels, only isConnected may be set
+ *
+ * only one of them may be set, not both
+ */
+ readonly attribute boolean isConnected;
+ attribute boolean hasValue; // set to false is only allowed writing
+
+ // connections, used when isConnected is true
+ readonly attribute sequence<PortDesc> connections;
+
+ // to be used as const value when hasValue is true
+ attribute float floatValue;
+ attribute string stringValue;
+
+ // the value as "any"
+ attribute Any value;
+
+ boolean connectTo(PortDesc port);
+ void disconnectFrom(PortDesc port);
+ void disconnectAll();
+
+ sequence<string> saveToList();
+ void loadFromList(sequence<string> list);
+
+ // never call this by hand, it will only be used by the module:
+ void internalConnectInput(PortDesc port);
+ void internalReConnect(sequence<PortDesc> allports);
+
+ readonly attribute long internalOldID;
+ };
+
+ interface ModuleDesc {
+ // internal
+ void constructor(StructureDesc parent, ModuleInfo info);
+
+ // ID is guaranteed to be unique in the structure the module belongs to
+ readonly attribute long ID;
+ readonly attribute StructureDesc parent;
+
+ readonly attribute string name;
+ readonly attribute sequence<PortDesc> ports;
+ readonly attribute long x, y, width, height;
+ readonly attribute boolean isInterface, isStructure;
+
+ boolean moveTo(long x, long y); // returns true when successful
+
+ PortDesc findPort(string name);
+ sequence<string> saveToList();
+ void loadFromList(sequence<string> list);
+ };
+
+ interface StructureDesc {
+ // internal:
+ long obtainID(); // only to be used as module or port
+
+ // public:
+ readonly attribute boolean valid;
+ attribute string name;
+ readonly attribute sequence<ModuleDesc> modules;
+ readonly attribute sequence<StructurePortDesc> ports;
+ readonly attribute sequence<string> inheritedInterfaces;
+
+ sequence<string> saveToList();
+ void loadFromList(sequence<string> list);
+
+ /**
+ * deletes all components in the structure
+ */
+ void clear();
+
+ ModuleDesc createModuleDesc(ModuleInfo info);
+ void freeModuleDesc(ModuleDesc moduledesc);
+
+ /**
+ * publishing (HMM)
+ */
+ readonly attribute ModuleInfo externalInterface;
+
+ // External Interface Ports:
+ StructurePortDesc createStructurePortDesc(PortType type,
+ string name);
+ void freeStructurePortDesc(StructurePortDesc portdesc);
+ void moveStructurePortDesc(StructurePortDesc portdesc,
+ long newposition);
+
+ // you will need to add the structure ports yourself
+ void addInheritedInterface(string iface);
+
+ // this will remove the associated structure ports
+ void removeInheritedInterface(string iface);
+ };
+
+ interface StructurePortDesc : PortDesc {
+ // internal
+ void constructor(StructureDesc parent, string name, PortType type);
+
+ // Position: how the port is positioned when the structure is used
+ // as module - 0 is leftmost, higher numbers are more right
+ readonly attribute long x, y, position;
+ readonly attribute StructureDesc parentStructure;
+
+ // if the port is associated with an inherited interface of the
+ // parent structure, then it should be setup here
+ attribute string inheritedInterface;
+
+ boolean moveTo(long x, long y); // returns true when successful
+
+ void lowerPosition(); // will move the port more left
+ void raisePosition(); // will move the port more right
+ void rename(string newname);
+
+ // only used by the structure to reorder the ports
+ void internalSetPosition(long position);
+ };
+
+ interface ObjectFactory {
+ object createObject(string name);
+ };
+
+ interface LocalFactory : ObjectFactory {
+ };
+
+ interface StructureBuilder {
+ void addFactory(ObjectFactory factory);
+ object createObject(StructureDesc structure);
+ ModuleDef createTypeInfo(StructureDesc structure);
+ };
+
+ interface ArtsBuilderLoader : Loader {
+ };
+
+ interface Structure : SynthModule {
+ void run();
+ void halt();
+ };
+};
diff --git a/arts/runtime/artsbuilderloader_impl.cc b/arts/runtime/artsbuilderloader_impl.cc
new file mode 100644
index 00000000..415106ef
--- /dev/null
+++ b/arts/runtime/artsbuilderloader_impl.cc
@@ -0,0 +1,285 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsbuilder.h"
+#include "debug.h"
+#include <stdlib.h>
+#include <fstream>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <set>
+#include <cstring>
+
+
+using namespace Arts;
+using namespace std;
+
+namespace Arts {
+
+class ArtsBuilderLoader_impl : virtual public ArtsBuilderLoader_skel {
+protected:
+ set<string> sourceDirs;
+
+ string lastDataVersion;
+ vector<TraderEntry> _traderEntries;
+ vector<ModuleDef> _modules;
+
+public:
+ Object loadObject(Arts::TraderOffer offer)
+ {
+ StructureDesc structureDesc;
+
+ vector<string> strseq;
+
+ // load file
+ vector<string> *filenames = offer.getProperty("File");
+ if(filenames->size() == 1)
+ {
+ string& filename = filenames->front();
+ arts_info("ArtsBuilderLoader: filename = %s", filename.c_str());
+
+ ifstream infile(filename.c_str());
+ string line;
+ while(getline(infile,line)) strseq.push_back(line);
+ }
+ delete filenames;
+
+ structureDesc.loadFromList(strseq);
+ if(structureDesc.name() != offer.interfaceName())
+ {
+ arts_warning("failed (name = %s).",structureDesc.name().c_str());
+ return Object::null();
+ }
+
+ StructureBuilder builder;
+ builder.addFactory(LocalFactory());
+
+ return builder.createObject(structureDesc);
+ }
+
+ vector<string> *listFiles(const string& pathname, const char *extension)
+ {
+ vector<string> *result = new vector<string>();
+
+ unsigned long extlen = strlen(extension);
+ DIR *dir = opendir(pathname.c_str());
+ if(dir != 0)
+ {
+ struct dirent *de;
+ while((de = readdir(dir)) != 0)
+ {
+ if(strlen(de->d_name) > extlen &&
+ strncmp(&de->d_name[strlen(de->d_name)-extlen],
+ extension,extlen) == 0)
+ result->push_back(de->d_name);
+ }
+ closedir(dir);
+ }
+ return result;
+ }
+
+ void collectInterfaces(const InterfaceDef& interface,
+ map<string, bool>& implemented)
+ {
+ if(!implemented[interface.name])
+ {
+ implemented[interface.name] = true;
+
+ vector<string>::const_iterator ii;
+ for(ii = interface.inheritedInterfaces.begin();
+ ii != interface.inheritedInterfaces.end(); ii++)
+ {
+ InterfaceDef id;
+ id = Dispatcher::the()->interfaceRepo().queryInterface(*ii);
+ collectInterfaces(id, implemented);
+ }
+ }
+ }
+
+ string getInterfacesList(const InterfaceDef& interface)
+ {
+ map<string, bool> implemented;
+ map<string, bool>::iterator ii;
+ string result;
+
+ collectInterfaces(interface, implemented);
+
+ for(ii = implemented.begin(); ii != implemented.end(); ii++)
+ result += ii->first + ",";
+ result += "Arts::Object";
+ return result;
+ }
+
+ void scanArtsFile(const string& filename)
+ {
+ StructureDesc structureDesc;
+ vector<string> strseq;
+
+ // load file
+ {
+ ifstream infile(filename.c_str());
+ string line;
+ int inmodule = 0;
+
+ while(getline(infile,line))
+ {
+ /*
+ * TODO - maybe there is a cleaner way?
+ *
+ * the following six lines are a bit hackish code to skip
+ * the module sections of the structures
+ *
+ * the problem with the module sections is this:
+ * we can't be sure that every module is known to the type
+ * system before we registered them with the type system,
+ * but as this code should be able to initially register .arts
+ * files with the type system, we can't rely that it has been
+ * done already (if we could, what would be the point of
+ * running this?)
+ */
+ if(strncmp(line.c_str(), "module=", 7) == 0)
+ inmodule = 1;
+
+ if(strncmp(line.c_str(), "{", 1) == 0 && inmodule == 1)
+ inmodule = 2;
+
+ if(strncmp(line.c_str(), "}", 1) == 0 && inmodule == 2)
+ inmodule = 0;
+
+ if(inmodule == 0)
+ strseq.push_back(line);
+ }
+ }
+
+ structureDesc.loadFromList(strseq);
+ string name = structureDesc.name();
+
+
+ arts_debug("%s [%s]\n",filename.c_str(),name.c_str());
+
+ /* add to _modules */
+ StructureBuilder builder;
+ ModuleDef md = builder.createTypeInfo(structureDesc);
+ _modules.push_back(md);
+
+ arts_assert(md.moduleName == name);
+ arts_assert(!md.interfaces.empty());
+
+ const InterfaceDef& id = md.interfaces.front();
+
+ /* add to _traderEntries */
+
+ TraderEntry entry;
+ entry.interfaceName = name;
+ entry.lines.push_back("Buildable=true");
+ entry.lines.push_back("Interface="+getInterfacesList(id));
+ entry.lines.push_back("Language=aRts");
+ entry.lines.push_back("File="+filename);
+
+ _traderEntries.push_back(entry);
+ /*
+ * TODO: more entries like
+ * Author="Stefan Westerfeld <stefan@space.twc.de>"
+ * URL="http://www.arts-project.org"
+ * License=...
+ */
+ }
+
+ void rescan()
+ {
+ lastDataVersion = dataVersion();
+
+ _traderEntries.clear();
+ _modules.clear();
+
+ set<string>::iterator si;
+ for(si = sourceDirs.begin(); si != sourceDirs.end(); si++)
+ {
+ vector<string> *files = listFiles(*si, ".arts");
+ vector<string>::iterator i;
+ for(i = files->begin(); i != files->end(); i++)
+ scanArtsFile(*si + "/" +*i);
+ delete files;
+ }
+ }
+
+ string dataVersion()
+ {
+ /*
+ * change this string if you change the loading algorithm to force
+ * rescanning even with the same data
+ */
+ string result = "ArtsBuilderLoader:1.1:";
+
+ bool first = true;
+
+ set<string>::iterator i;
+ for(i = sourceDirs.begin(); i != sourceDirs.end(); i++)
+ {
+ const string& filename = *i;
+
+ if(!first) result += ",";
+ first = false;
+
+ struct stat st;
+ if( stat(filename.c_str(), &st) == 0 )
+ {
+ char mtime[32];
+ sprintf(mtime,"[%ld]",st.st_mtime);
+ result += filename + mtime;
+ }
+ else
+ result += filename + "[-1]";
+ }
+ return result;
+ }
+
+ vector<TraderEntry> *traderEntries()
+ {
+ if(dataVersion() != lastDataVersion)
+ rescan();
+
+ return new vector<TraderEntry>(_traderEntries);
+ }
+
+ vector<ModuleDef> *modules()
+ {
+ if(dataVersion() != lastDataVersion)
+ rescan();
+
+ return new vector<ModuleDef>(_modules);
+ }
+
+ ArtsBuilderLoader_impl()
+ {
+ sourceDirs.insert(EXAMPLES_DIR);
+
+ const char *home = getenv("HOME");
+ if(home) sourceDirs.insert(home+string("/arts/structures"));
+ }
+};
+
+REGISTER_IMPLEMENTATION(ArtsBuilderLoader_impl);
+}
diff --git a/arts/runtime/compatibility.cc b/arts/runtime/compatibility.cc
new file mode 100644
index 00000000..190d471b
--- /dev/null
+++ b/arts/runtime/compatibility.cc
@@ -0,0 +1,56 @@
+ /*
+
+ Copyright (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "compatibility.h"
+#include "debug.h"
+#include <iostream>
+
+#undef DEBUG_COMPATIBILITY
+
+using namespace std;
+
+string Arts::OldFormatTranslator::newModuleName(const string& module)
+{
+#ifdef DEBUG_COMPATIBILITY
+ arts_debug("COMPAT: %s", module.c_str());
+#endif
+
+ if(module.substr(0,10) == "Interface_") return "Arts::"+module;
+ if(module.substr(0,6) == "Synth_") return "Arts::"+module;
+ return module;
+}
+
+string Arts::OldFormatTranslator::newPortName(const string& module, const string& port)
+{
+#ifdef DEBUG_COMPATIBILITY
+ arts_debug("COMPAT: %s.%s", module.c_str(), port.c_str());
+#endif
+
+ if(module == "Arts::Synth_MUL") {
+ if(port == "invalue") return "invalue1";
+ if(port == "faktor") return "invalue2";
+ }
+ if(module == "Arts::Synth_ADD") {
+ if(port == "invalue") return "invalue1";
+ if(port == "addit") return "invalue2";
+ }
+ return port;
+}
diff --git a/arts/runtime/compatibility.h b/arts/runtime/compatibility.h
new file mode 100644
index 00000000..b95b0266
--- /dev/null
+++ b/arts/runtime/compatibility.h
@@ -0,0 +1,36 @@
+ /*
+
+ Copyright (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_COMPATIBILITY_H
+#define ARTS_COMPATIBILITY_H
+
+#include <string>
+
+namespace Arts {
+ class OldFormatTranslator {
+ public:
+ static std::string newModuleName(const std::string& module);
+ static std::string newPortName(const std::string& module,
+ const std::string& port);
+ };
+}
+
+#endif /* ARTS_COMPATIBILITY_H */
diff --git a/arts/runtime/localfactory_impl.cc b/arts/runtime/localfactory_impl.cc
new file mode 100644
index 00000000..bf55271c
--- /dev/null
+++ b/arts/runtime/localfactory_impl.cc
@@ -0,0 +1,15 @@
+#include "artsbuilder.h"
+
+using namespace Arts;
+using namespace std;
+
+class LocalFactory_impl : virtual public LocalFactory_skel {
+public:
+ Object createObject(const string& name)
+ {
+ return SubClass(name);
+ }
+};
+
+REGISTER_IMPLEMENTATION(LocalFactory_impl);
+
diff --git a/arts/runtime/moduleinfo.cc b/arts/runtime/moduleinfo.cc
new file mode 100644
index 00000000..28a44310
--- /dev/null
+++ b/arts/runtime/moduleinfo.cc
@@ -0,0 +1,106 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Permission is also granted to link this program with the Qt
+ library, treating Qt like a library that normally accompanies the
+ operating system kernel, whether or not that is in fact the case.
+
+ */
+
+#include "moduleinfo.h"
+
+using namespace std;
+
+static void gatherPorts(Arts::InterfaceDef& idef, Arts::ModuleInfo& minfo,
+ map<string, bool>& done)
+{
+ Arts::InterfaceRepo interfaceRepo=Arts::Dispatcher::the()->interfaceRepo();
+
+ vector<string>::iterator ii = idef.inheritedInterfaces.begin();
+ while(ii != idef.inheritedInterfaces.end())
+ {
+ Arts::InterfaceDef inherited = interfaceRepo.queryInterface(*ii++);
+ gatherPorts(inherited,minfo,done);
+ }
+
+ if(idef.name == "Arts::Object" || idef.name == "Arts::SynthModule")
+ {
+ // don't gather members of these basic interfaces as ports
+ return;
+ }
+ vector<Arts::AttributeDef>::iterator i;
+ for(i=idef.attributes.begin(); i != idef.attributes.end(); i++)
+ {
+ // 1 = direction, 10000 = connectiontype
+ long complete = 0;
+ Arts::PortType ptype;
+
+ if(i->flags & Arts::streamIn)
+ {
+ ptype.direction = Arts::input;
+ complete += 1;
+ }
+ else if(i->flags & Arts::streamOut)
+ {
+ ptype.direction = Arts::output;
+ complete += 1;
+ }
+
+ ptype.dataType = i->type;
+
+ if(i->flags & Arts::attributeStream)
+ {
+ ptype.connType = Arts::conn_stream;
+ complete += 10000;
+ }
+ else if(i->flags & Arts::attributeAttribute)
+ {
+ ptype.connType = Arts::conn_property;
+ complete += 10000;
+ }
+
+ ptype.isMultiPort = (i->flags & Arts::streamMulti);
+
+ if(complete == 10001 && !done[i->name])
+ {
+ minfo.portnames.push_back(i->name);
+ minfo.ports.push_back(ptype);
+ done[i->name] = true;
+ }
+ }
+}
+
+Arts::ModuleInfo makeModuleInfo(const string& name)
+{
+ Arts::InterfaceRepo interfaceRepo=Arts::Dispatcher::the()->interfaceRepo();
+ Arts::InterfaceDef idef = interfaceRepo.queryInterface(name);
+ Arts::ModuleInfo minfo;
+
+ if(!idef.name.empty())
+ {
+ map<string,bool> done;
+ minfo.name = name;
+ minfo.isStructure = false;
+ minfo.isInterface = false;
+
+ gatherPorts(idef,minfo,done);
+ }
+ return minfo;
+}
+
diff --git a/arts/runtime/moduleinfo.h b/arts/runtime/moduleinfo.h
new file mode 100644
index 00000000..1067f342
--- /dev/null
+++ b/arts/runtime/moduleinfo.h
@@ -0,0 +1,33 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Permission is also granted to link this program with the Qt
+ library, treating Qt like a library that normally accompanies the
+ operating system kernel, whether or not that is in fact the case.
+
+ */
+
+#ifndef MODULEINFO_H
+#define MODULEINFO_H
+
+#include "artsbuilder.h"
+#include <kdelibs_export.h>
+KDE_EXPORT Arts::ModuleInfo makeModuleInfo(const std::string& name);
+
+#endif /* MODULEINFO_H */
diff --git a/arts/runtime/sequenceutils.cc b/arts/runtime/sequenceutils.cc
new file mode 100644
index 00000000..9634f40b
--- /dev/null
+++ b/arts/runtime/sequenceutils.cc
@@ -0,0 +1,188 @@
+ /*
+
+ Copyright (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "sequenceutils.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <config.h>
+
+using namespace std;
+
+#if 0
+void sqprintf(ArtsCorba::StringSeq *list, const char *fmt, ...)
+{
+ char p[1024];
+ va_list ap;
+ va_start(ap, fmt);
+ (void) vsnprintf(p, 1024, fmt, ap);
+ va_end(ap);
+
+ unsigned long len = list->length();
+ list->length(len+1);
+ (*list)[len] = CORBA::string_dup(p);
+}
+#endif
+
+void sqprintf(vector<string> *list, const char *fmt, ...)
+{
+ char p[1024];
+ va_list ap;
+ va_start(ap, fmt);
+ (void) vsnprintf(p, 1024, fmt, ap);
+ va_end(ap);
+
+ list->push_back(p);
+}
+
+int parse_line(const char *in, char *& cmd, char *& param)
+{
+ int i,cmdlen=0,paramlen=0;
+ static char static_cmd[1000], static_param[1000];
+
+ cmd = static_cmd;
+ param = static_param;
+ i = 0;
+
+ while(in[i] == ' ' || in[i] == '\t') i++;
+
+ if(in[i] == 0) return(0);
+
+ while(in[i] != '=' && in[i] != 0) cmd[cmdlen++] = in[i++];
+ if(in[i] != 0) i++;
+ while(in[i] != 0) param[paramlen++] = in[i++];
+
+ cmd[cmdlen] = 0;
+ param[paramlen] = 0;
+
+ if(paramlen) return(2);
+ if(cmdlen) return(1);
+ return(0);
+}
+
+int parse_line(const string& in, string& cmd, string& param)
+{
+ char *ccmd, *cparam;
+ int result = parse_line(in.c_str(),ccmd,cparam);
+ param = cparam;
+ cmd = ccmd;
+ return result;
+}
+
+#if 0
+void addSubStringSeq(ArtsCorba::StringSeq *target, ArtsCorba::StringSeq *source)
+{
+ unsigned long i;
+
+ sqprintf(target,"{");
+ for(i=0;i<source->length();i++)
+ {
+ unsigned long len = target->length();
+ target->length(len+1);
+ string srcstring = string(" ") + string((*source)[i]);
+ (*target)[len] = CORBA::string_dup(srcstring.c_str());
+ }
+ sqprintf(target,"}");
+}
+#endif
+
+void addSubStringSeq(vector<string> *target, const vector<string> *source)
+{
+ sqprintf(target,"{");
+
+ vector<string>::const_iterator i;
+ for(i=source->begin();i != source->end();i++)
+ target->push_back(" " + *i);
+
+ sqprintf(target,"}");
+}
+
+#if 0
+void appendStringSeq(ArtsCorba::StringSeq *target, ArtsCorba::StringSeq *source)
+{
+ unsigned long i;
+
+ for(i=0;i<source->length();i++)
+ {
+ unsigned long len = target->length();
+ target->length(len+1);
+ (*target)[len] = CORBA::string_dup((*source)[i]);
+ }
+}
+#endif
+
+void appendStringSeq(vector<string> *target, const vector<string> *source)
+{
+ vector<string>::const_iterator i;
+ for(i=source->begin();i != source->end();i++)
+ target->push_back(*i);
+}
+
+#if 0
+ArtsCorba::StringSeq *getSubStringSeq(const ArtsCorba::StringSeq *seq,unsigned long& i)
+{
+ ArtsCorba::StringSeq *result = new ArtsCorba::StringSeq;
+ char empty[1] = {0};
+ char *cmd = empty,*param;
+
+
+ while(strcmp(cmd,"{") && i<seq->length())
+ parse_line((*seq)[i++],cmd,param);
+
+ int brackets = 1;
+
+ while(i<seq->length())
+ {
+ parse_line((*seq)[i],cmd,param);
+ if(strcmp(cmd,"{") == 0) brackets++;
+ if(strcmp(cmd,"}") == 0) brackets--;
+ if(brackets == 0) return(result);
+
+ unsigned long len = result->length();
+ result->length(len+1);
+ (*result)[len] = CORBA::string_dup((*seq)[i]);
+ i++;
+ }
+ return(result);
+}
+#endif
+
+vector<string> *getSubStringSeq(const vector<string> *seq,unsigned long& i)
+{
+ vector<string> *result = new vector<string>;
+ string cmd = "",param;
+
+ while(cmd != "{" && i<seq->size())
+ parse_line((*seq)[i++],cmd,param);
+
+ int brackets = 1;
+
+ while(i<seq->size())
+ {
+ parse_line((*seq)[i],cmd,param);
+ if(cmd == "{") brackets++;
+ if(cmd == "}") brackets--;
+ if(brackets == 0) return(result);
+
+ result->push_back((*seq)[i]);
+ i++;
+ }
+ return result;
+}
diff --git a/arts/runtime/sequenceutils.h b/arts/runtime/sequenceutils.h
new file mode 100644
index 00000000..5925bff3
--- /dev/null
+++ b/arts/runtime/sequenceutils.h
@@ -0,0 +1,41 @@
+ /*
+
+ Copyright (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_SEQUENCEUTILS_H
+#define ARTS_SEQUENCEUTILS_H
+
+#include <vector>
+#include <string>
+#include <kdelibs_export.h>
+
+KDE_EXPORT int parse_line(const char *in, char *& cmd, char *& param);
+
+KDE_EXPORT void sqprintf(std::vector<std::string> *list, const char *fmt, ...)
+#ifdef __GNUC__
+ __attribute__ (( format (printf, 2, 3)))
+#endif
+;
+KDE_EXPORT void addSubStringSeq(std::vector<std::string> *target, const std::vector<std::string> *source);
+KDE_EXPORT void appendStringSeq(std::vector<std::string> *target, const std::vector<std::string> *source);
+KDE_EXPORT int parse_line(const std::string& in, std::string& cmd, std::string& param);
+KDE_EXPORT std::vector<std::string> *getSubStringSeq(const std::vector<std::string> *seq,unsigned long& i);
+
+#endif /* ARTS_SEQUENCEUTILS_H */
diff --git a/arts/runtime/structurebuilder_impl.cc b/arts/runtime/structurebuilder_impl.cc
new file mode 100644
index 00000000..63dd6927
--- /dev/null
+++ b/arts/runtime/structurebuilder_impl.cc
@@ -0,0 +1,347 @@
+ /*
+
+ Copyright (C) 2000,2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsbuilder.h"
+#include "artsflow.h"
+#include "connect.h"
+#include "debug.h"
+#include "flowsystem.h"
+#include "stdsynthmodule.h"
+#include "dynamicrequest.h"
+#include "dynamicskeleton.h"
+#include "startupmanager.h"
+#include <list>
+
+//#define STRUCTBUILDER_DEBUG 1
+
+using namespace Arts;
+using namespace std;
+
+class StructureBuilder_impl : virtual public StructureBuilder_skel {
+protected:
+ list<ObjectFactory> factories;
+public:
+ void addFactory(ObjectFactory factory);
+ Object createObject(StructureDesc structure);
+ ModuleDef createTypeInfo(StructureDesc structure);
+};
+
+REGISTER_IMPLEMENTATION(StructureBuilder_impl);
+
+typedef DynamicSkeleton<SynthModule_skel> SynthModule_dskel;
+
+class Structure_impl : virtual public SynthModule_dskel,
+ virtual public StdSynthModule {
+protected:
+ list<Object> structureObjects;
+
+ struct ForwardMethod {
+ string method;
+ Object destObject;
+ string destMethod;
+ };
+
+ list<ForwardMethod> forwardMethods;
+
+public:
+ Structure_impl(StructureDesc structure, list<ObjectFactory>& factories);
+ void streamInit();
+ void streamEnd();
+
+ void process(long methodID, Buffer *request, Buffer *result);
+};
+
+void StructureBuilder_impl::addFactory(ObjectFactory factory)
+{
+ factories.push_back(factory);
+}
+
+ModuleDef StructureBuilder_impl::createTypeInfo(StructureDesc structure)
+{
+ ModuleDef md;
+ InterfaceDef id;
+
+/* convert structure to InterfaceDef id */
+ md.moduleName = id.name = structure.name();
+ id.inheritedInterfaces.push_back("Arts::SynthModule");
+
+ vector<string> *otherInterfaces = structure.inheritedInterfaces();
+ vector<string>::iterator ii;
+ for(ii = otherInterfaces->begin(); ii != otherInterfaces->end(); ii++)
+ id.inheritedInterfaces.push_back(*ii);
+ delete otherInterfaces;
+
+ vector<StructurePortDesc> *ports = structure.ports();
+ vector<StructurePortDesc>::iterator pi;
+ for(pi = ports->begin(); pi != ports->end(); pi++)
+ {
+ const Arts::PortType& type = pi->type();
+
+ // if we inherited the port from a parent interface, we don't need to
+ // list it in our interface description
+ if(pi->inheritedInterface().empty())
+ {
+ AttributeDef ad;
+ ad.name = pi->name();
+
+ // This is a little tricky, as input ports (which are bringing data
+ // from outside into the structure) are saved as output ports (and
+ // output ports as input ports).
+ ad.flags = AttributeType(
+ ((type.direction == input)?streamOut:streamIn) |
+ ((type.connType == conn_stream)?attributeStream:attributeAttribute)
+ );
+ ad.type = type.dataType;
+
+ id.attributes.push_back(ad);
+ }
+ }
+ delete ports;
+
+ md.interfaces.push_back(id);
+
+ return md;
+}
+
+namespace Arts {
+static class StructureBuilderCleanUp : public StartupClass {
+public:
+ vector<long> types;
+ void startup() { };
+ void shutdown() {
+ vector<long>::iterator i;
+ for(i = types.begin(); i != types.end(); i++)
+ Dispatcher::the()->interfaceRepo().removeModule(*i);
+ types.clear();
+ }
+ virtual ~StructureBuilderCleanUp() {}
+} structureBuilderCleanUp;
+}
+
+Object StructureBuilder_impl::createObject(StructureDesc structure)
+{
+ ModuleDef md = createTypeInfo(structure);
+
+ // FIXME: find some faster way of ensuring type consistency than creating
+ // the thing from scratch every time
+ structureBuilderCleanUp.types.push_back(Dispatcher::the()->interfaceRepo().insertModule(md));
+ Object obj = Object::_from_base(new Structure_impl(structure, factories));
+ return obj;
+}
+
+Structure_impl::Structure_impl(StructureDesc structureDesc,
+ list<ObjectFactory>& factories)
+ : SynthModule_dskel(structureDesc.name())
+{
+ map<long, Object> moduleMap;
+ vector<ModuleDesc> *modules = structureDesc.modules();
+ vector<ModuleDesc>::iterator mi;
+
+ // create each object
+ for(mi = modules->begin(); mi != modules->end(); mi++)
+ {
+ ModuleDesc& md = *mi;
+
+#ifdef STRUCTBUILDER_DEBUG
+ cout << "create " << md.name() << endl;
+#endif
+ Object o = Object::null(); //SubClass(md.name());
+
+ Object_skel *skel = 0;
+ skel = ObjectManager::the()->create(md.name());
+ if(skel) o = Object::_from_base(skel);
+
+#ifdef STRUCTBUILDER_DEBUG
+ if(o.isNull()) cout << "no local creator for " << md.name() << endl;
+#endif
+ list<ObjectFactory>::iterator fi = factories.begin();
+ while(o.isNull() && fi != factories.end())
+ {
+ o = fi->createObject(md.name());
+ fi++;
+ }
+
+#ifdef STRUCTBUILDER_DEBUG
+ if(o.isNull()) cout << "no remote creator for " << md.name() << endl;
+#endif
+ assert(!o.isNull());
+ moduleMap[md.ID()] = o;
+ structureObjects.push_back(o);
+ }
+
+ // connect objects and set values
+ for(mi = modules->begin(); mi != modules->end(); mi++)
+ {
+ Object& object = moduleMap[mi->ID()];
+
+ vector<PortDesc> *ports = mi->ports();
+ vector<PortDesc>::iterator pi;
+
+ for(pi = ports->begin(); pi != ports->end(); pi++)
+ {
+ PortDesc& pd = *pi;
+ const Arts::PortType& ptype = pd.type();
+
+ if(pd.hasValue())
+ {
+ // set values
+#ifdef STRUCTBUILDER_DEBUG
+ cout << "value " << mi->name() << "." << pi->name() << endl;
+#endif
+
+ if(ptype.connType == conn_property)
+ {
+ DynamicRequest req(object);
+ req.method("_set_"+pi->name());
+ req.param(pd.value());
+
+ bool requestOk = req.invoke();
+ arts_assert(requestOk);
+ }
+ else
+ {
+ if(ptype.dataType == "float")
+ setValue(object,pi->name(),pd.floatValue());
+ else
+ arts_warning("unexpected property type %s",
+ ptype.dataType.c_str());
+ //setStringValue(object,pd.stringValue());
+ }
+ }
+ else if(pd.isConnected() && ptype.direction == output)
+ {
+ // create connections
+
+ vector<PortDesc> *connections = pd.connections();
+ vector<PortDesc>::iterator ci;
+
+ for(ci = connections->begin(); ci != connections->end(); ci++)
+ {
+ if(!ci->parent().isNull()) // structureport otherwise
+ {
+ Object& dest = moduleMap[ci->parent().ID()];
+#ifdef STRUCTBUILDER_DEBUG
+ cout << "connect " << mi->name() << "." << pi->name()
+ << " to " << ci->parent().name()
+ << "." << ci->name() << endl;
+#endif
+ connect(object,pd.name(),dest,ci->name());
+ }
+ }
+ delete connections;
+ }
+ }
+ delete ports;
+ }
+ delete modules;
+
+ // create ports (should be done via dynamic impl class...)
+
+ vector<StructurePortDesc> *ports = structureDesc.ports();
+ vector<StructurePortDesc>::iterator pi;
+
+ for(pi = ports->begin(); pi != ports->end(); pi++)
+ {
+ Arts::StructurePortDesc& pd = *pi;
+ if(pd.isConnected())
+ {
+ // create connections
+
+ vector<PortDesc> *connections = pd.connections();
+ vector<PortDesc>::iterator ci;
+
+ for(ci = connections->begin(); ci != connections->end(); ci++)
+ {
+ Object& dest = moduleMap[ci->parent().ID()];
+#ifdef STRUCTBUILDER_DEBUG
+ cout << "virtualize " << pi->name()
+ << " to " << ci->parent().name() << "." << ci->name()
+ << endl;
+#endif
+
+ _node()->virtualize(pd.name(),dest._node(),ci->name());
+
+ if(pd.type().connType == conn_property)
+ {
+ ForwardMethod fm;
+ fm.method = "_set_"+pd.name();
+ fm.destObject = dest;
+ fm.destMethod = "_set_"+ci->name();
+ forwardMethods.push_back(fm);
+ }
+ }
+ delete connections;
+ }
+ }
+ delete ports;
+}
+
+void Structure_impl::streamInit()
+{
+ list<Object>::iterator i;
+
+#ifdef STRUCTBUILDER_DEBUG
+ cout << "vstructure: got streamInit()" << endl;
+#endif
+
+ for(i=structureObjects.begin(); i != structureObjects.end(); i++)
+ {
+ if(i->_base()->_isCompatibleWith("Arts::SynthModule"))
+ i->_node()->start();
+ }
+}
+
+void Structure_impl::streamEnd()
+{
+ list<Object>::iterator i;
+
+#ifdef STRUCTBUILDER_DEBUG
+ cout << "vstructure: got streamEnd()" << endl;
+#endif
+
+ for(i=structureObjects.begin(); i != structureObjects.end(); i++)
+ if(i->_base()->_isCompatibleWith("Arts::SynthModule"))
+ i->_node()->stop();
+}
+
+void Structure_impl::process(long methodID, Buffer *request, Buffer *result)
+{
+ const MethodDef& methodDef = getMethodDef(methodID);
+
+ arts_debug("Structure_impl: got method, method ID=%ld name='%s'",
+ methodID, methodDef.name.c_str());
+
+ list<ForwardMethod>::iterator fi;
+ for(fi = forwardMethods.begin(); fi != forwardMethods.end(); fi++)
+ {
+ if(fi->method == methodDef.name)
+ {
+ Any a;
+ a.type = methodDef.signature[0].type;
+
+ while(request->remaining() > 0)
+ a.value.push_back(request->readByte());
+
+ DynamicRequest(fi->destObject).method(fi->destMethod).param(a).invoke();
+ }
+ }
+}
diff --git a/arts/runtime/structures_impl.cc b/arts/runtime/structures_impl.cc
new file mode 100644
index 00000000..7c5a5e05
--- /dev/null
+++ b/arts/runtime/structures_impl.cc
@@ -0,0 +1,1421 @@
+#include "artsbuilder.h"
+#include "weakreference.h"
+#include "moduleinfo.h"
+#include "compatibility.h"
+#include "sequenceutils.h"
+#include <stdio.h>
+#include <iostream>
+
+using namespace std;
+using namespace Arts;
+
+typedef WeakReference<PortDesc> PortDesc_wref;
+typedef WeakReference<ModuleDesc> ModuleDesc_wref;
+typedef WeakReference<StructureDesc> StructureDesc_wref;
+
+class PortDesc_impl :virtual public Arts::PortDesc_skel {
+protected:
+ string _name;
+ PortType _type;
+ vector<PortDesc_wref> _connections;
+ ModuleDesc_wref _parent;
+ bool _isConnected;
+ bool _hasValue;
+ Any _value;
+ long _ID;
+ long _oldID;
+ list<long> oldConnections;
+
+ void removeNullConnections();
+
+public:
+ ~PortDesc_impl();
+
+ inline PortDesc self() { return PortDesc::_from_base(_copy()); }
+ void constructor(ModuleDesc parent, const string& name, const PortType& type);
+
+ void disconnectAll();
+ long ID();
+ ModuleDesc parent();
+ string name();
+ PortType type();
+ bool isConnected();
+ bool hasValue();
+ void hasValue(bool newvalue);
+ vector<PortDesc> *connections();
+ float floatValue();
+ void floatValue( float _new_value );
+
+ string stringValue();
+ void stringValue( const string& _new_value );
+
+ Any value();
+ void value( const Any& _new_value );
+
+ bool connectTo( PortDesc port );
+ void internalConnectInput( PortDesc port );
+ void disconnectFrom( PortDesc port );
+
+ void loadFromList(const vector<string>& list);
+ vector<string> *saveToList();
+
+ void internalReConnect( const vector<PortDesc>& allports );
+ long internalOldID();
+};
+
+class ModuleDesc_impl : virtual public ModuleDesc_skel {
+private:
+ long _ID;
+ StructureDesc_wref _parent;
+ string _name;
+ long _x, _y;
+ vector<PortDesc> _ports;
+ long collectPorts( const Arts::ModuleInfo& info );
+
+ bool _isInterface, _isStructure;
+
+ inline ModuleDesc self() { return ModuleDesc::_from_base(_copy()); }
+
+public:
+ long ID();
+ StructureDesc parent();
+ string name();
+ vector<PortDesc> *ports();
+ long height();
+ long width();
+ long x();
+ long y();
+ bool moveTo( long x, long y );
+ void constructor( StructureDesc parent, const ModuleInfo& info );
+
+ void loadFromList(const vector<string>& list);
+ vector<string> *saveToList();
+
+ ~ModuleDesc_impl();
+
+ bool isInterface();
+ bool isStructure();
+ Arts::PortDesc findPort(const string& name);
+};
+
+class StructureDesc_impl : virtual public Arts::StructureDesc_skel {
+protected:
+ bool _valid;
+ vector<ModuleDesc> _modules;
+ vector<StructurePortDesc> _ports; /* only structure ports which are part of the interface */
+ vector<string> _inheritedInterfaces;
+ long nextID;
+ ModuleInfo _externalInterface;
+
+ inline StructureDesc self() { return StructureDesc::_from_base(_copy()); }
+public:
+ string name();
+ void name(const string& newName);
+
+ vector<string> *saveToList();
+ void loadFromList(const vector<string>& list);
+ vector<ModuleDesc> *modules();
+ vector<StructurePortDesc> *ports();
+ vector<string> *inheritedInterfaces();
+
+ void clear();
+ long obtainID();
+ long width();
+ long height();
+ bool valid();
+
+ ModuleDesc createModuleDesc( const ModuleInfo& info );
+ ModuleDesc createModuleDesc( const string& name );
+ void freeModuleDesc(ModuleDesc moduledesc);
+
+ // external interface
+ StructurePortDesc createStructurePortDesc(const PortType& type, const string& name);
+ void freeStructurePortDesc(StructurePortDesc portdesc);
+ void moveStructurePortDesc(StructurePortDesc portdesc, long newposition);
+
+ ModuleInfo externalInterface();
+
+ void addInheritedInterface(const string& iface);
+ void removeInheritedInterface(const string& iface);
+
+ StructureDesc_impl();
+ ~StructureDesc_impl();
+};
+
+class StructurePortDesc_impl :
+ virtual public PortDesc_impl,
+ virtual public StructurePortDesc_skel
+{
+protected:
+ StructureDesc_wref _parentStructure;
+ long _x, _y, _position;
+ string _inheritedInterface;
+
+ inline StructurePortDesc self() {
+ return StructurePortDesc::_from_base(_copy());
+ }
+public:
+ void constructor(StructureDesc parent, const string& name,
+ const PortType& type);
+ ~StructurePortDesc_impl();
+
+ long x();
+ long y();
+ long position();
+ void lowerPosition();
+ void raisePosition();
+ void rename(const string& newname);
+ string inheritedInterface();
+ void inheritedInterface(const string& iface);
+
+ void internalSetPosition(long position);
+ StructureDesc parentStructure();
+ bool moveTo( long X, long Y );
+
+ void loadFromList(const vector<string>& list);
+ vector<string> *saveToList();
+};
+
+REGISTER_IMPLEMENTATION(PortDesc_impl);
+REGISTER_IMPLEMENTATION(ModuleDesc_impl);
+REGISTER_IMPLEMENTATION(StructureDesc_impl);
+REGISTER_IMPLEMENTATION(StructurePortDesc_impl);
+
+/*
+#include "structures.h"
+*/
+#include "debug.h"
+#include <vector>
+#include <algorithm>
+
+#define dname(dir) ((dir)==Arts::input?"input":"output")
+#define pstat \
+ printf("port name %s, direction %s, id %d\n",_Name.c_str(),dname(_Type.direction),_ID);
+
+void PortDesc_impl::constructor(ModuleDesc parent, const string& name,
+ const PortType& type)
+{
+#if 0
+ if(parent)
+ {
+ char * pname = parent->Name();
+ describe("PortDesc."+string(pname)+string(".")+name);
+ }
+ else
+ {
+ describe("PortDesc.Structure."+name);
+ }
+#endif
+ _name = name;
+ _type = type;
+ _parent = parent;
+ _isConnected = false;
+ _hasValue = false;
+ _value.type = _type.dataType;
+
+ if(!parent.isNull())
+ {
+ StructureDesc sd = parent.parent();
+ _ID = sd.obtainID();
+ }
+ // else: assume that some smart object which derives from us will set the ID accordingly
+ // -> for instance StructurePortDesc_impl does so
+}
+
+#if 0 /* PORT */
+void PortDesc_impl::cleanUp()
+{
+ disconnectAll();
+ delete _Connections;
+}
+#endif
+
+/*
+ * This is new and related to weak references, it purges all null references from _connections
+ */
+void PortDesc_impl::removeNullConnections()
+{
+ vector<PortDesc_wref>::iterator i = _connections.begin();
+
+ while(i != _connections.end())
+ {
+ PortDesc pd = *i;
+ if(pd.isNull())
+ {
+ _connections.erase(i);
+ i = _connections.begin();
+ printf("removeNullConnections() removed something (shouldn't happen)\n");
+ }
+ else i++;
+ }
+
+ _isConnected = !_connections.empty();
+}
+
+void PortDesc_impl::disconnectAll()
+{
+ // disconnect all connected ports
+ while(!_connections.empty())
+ {
+ PortDesc pd = _connections.front();
+
+ if(pd.isNull()) // weak references can automatically disappear
+ _connections.erase(_connections.begin());
+ else
+ pd.disconnectFrom(self());
+ }
+}
+
+PortDesc_impl::~PortDesc_impl()
+{
+}
+
+// Implementation for interface PortDesc
+long PortDesc_impl::ID()
+{
+ return _ID;
+}
+
+ModuleDesc PortDesc_impl::parent()
+{
+ return _parent;
+}
+
+string PortDesc_impl::name()
+{
+ return _name;
+}
+
+PortType PortDesc_impl::type()
+{
+ return _type;
+}
+
+bool PortDesc_impl::isConnected()
+{
+ if(_isConnected) removeNullConnections();
+ return _isConnected;
+}
+
+bool PortDesc_impl::hasValue()
+{
+ return _hasValue;
+}
+
+void PortDesc_impl::hasValue(bool newvalue)
+{
+ if(_hasValue != newvalue)
+ {
+ assert(newvalue == false);
+ _hasValue = newvalue;
+ }
+}
+
+vector<PortDesc> *PortDesc_impl::connections()
+{
+ vector<PortDesc_wref>::iterator i;
+ vector<PortDesc> *result = new vector<PortDesc>;
+
+ for(i = _connections.begin(); i != _connections.end(); i++)
+ {
+ PortDesc pd = *i;
+ if(!pd.isNull()) result->push_back(pd);
+ }
+ return result;
+}
+
+float PortDesc_impl::floatValue()
+{
+ assert(_hasValue);
+ assert(_type.dataType == "float");
+
+ Buffer b;
+ b.write(_value.value);
+ return b.readFloat();
+}
+
+void PortDesc_impl::floatValue( float _new_value )
+{
+ assert(!_isConnected);
+ assert(_type.direction == Arts::input);
+ assert(_type.dataType == "float");
+ assert(_value.type == "float");
+
+ Buffer b;
+ b.writeFloat(_new_value);
+ b.read(_value.value, b.size());
+ _hasValue = true;
+}
+
+string PortDesc_impl::stringValue()
+{
+ assert(_hasValue);
+ assert(_type.dataType == "string");
+ assert(_value.type == "string");
+
+ string result;
+ Buffer b;
+ b.write(_value.value);
+ b.readString(result);
+ return result;
+}
+
+void PortDesc_impl::stringValue( const string& _new_value )
+{
+ assert(!_isConnected); // shouldn't happen, but check anyway
+ assert(_type.direction == Arts::input);
+ assert(_type.dataType == "string");
+
+ Buffer b;
+ b.writeString(_new_value);
+ b.read(_value.value, b.size());
+ _hasValue = true;
+}
+
+Any PortDesc_impl::value()
+{
+ assert(_hasValue);
+ return _value;
+}
+
+void PortDesc_impl::value( const Any& _new_value )
+{
+ _value = _new_value;
+ _hasValue = true;
+}
+
+bool PortDesc_impl::connectTo( PortDesc port )
+{
+ removeNullConnections();
+
+ // check if we are already connected to that port:
+
+ unsigned long i;
+ for(i=0;i<_connections.size();i++)
+ {
+ PortDesc pd = _connections[i];
+ if(pd.ID() == port.ID()) return true;
+ }
+
+ const PortType& rType = port.type();
+
+ // only stream or event channels may be connected
+ if( _type.connType != rType.connType )
+ return false;
+
+ // TODO: eventually check conditions when it is legal to connect property
+ // ports, and when it is insane (_Type.connType == Arts::property)
+ //
+ // for incoming structure ports, for instance, it is perfectly allright
+
+ // only same data type connections allowed
+ if( _type.dataType != rType.dataType )
+ return false;
+
+ // only opposite directions
+ if( _type.direction == rType.direction )
+ return false;
+
+ // always first connect the input port to the output port and
+ // then the other direction
+
+ if( _type.direction == Arts::input )
+ {
+ if(!_isConnected || _type.isMultiPort)
+ {
+ assert(_connections.empty() || _type.isMultiPort);
+ _connections.push_back(port);
+
+ port.internalConnectInput(self());
+
+ _isConnected = true;
+ _hasValue = false;
+ return true;
+ }
+ }
+ if( _type.direction == Arts::output )
+ return port.connectTo(self());
+
+ return false;
+}
+
+void PortDesc_impl::internalConnectInput( PortDesc port )
+{
+ _connections.push_back(port);
+ _isConnected = true;
+}
+
+void PortDesc_impl::disconnectFrom( PortDesc port )
+{
+ removeNullConnections();
+
+ unsigned long found = 0;
+
+ artsdebug("port %ld disconnecting from port %ld\n",ID(),port.ID());
+
+ vector<PortDesc_wref>::iterator i = _connections.begin();
+ while(!found && i != _connections.end())
+ {
+ Arts::PortDesc other = *i;
+ if(!other.isNull() && other.ID() == port.ID())
+ {
+ _connections.erase(i);
+ i = _connections.begin();
+ found++;
+ }
+ else i++;
+ }
+
+ _isConnected = !_connections.empty();
+
+ ModuleDesc parent = _parent;
+ if(parent.isNull())
+ artsdebug("_Parent = <some structure>, isConnected = %d\n",_isConnected);
+ else
+ artsdebug("_Parent = %s, isConnected = %d\n",parent.name().c_str(),_isConnected);
+
+ if(found)
+ port.disconnectFrom(self());
+}
+
+// Implementation for interface ModuleDesc
+long ModuleDesc_impl::ID()
+{
+ return _ID;
+}
+
+StructureDesc ModuleDesc_impl::parent()
+{
+ return _parent;
+}
+
+string ModuleDesc_impl::name()
+{
+ return _name;
+}
+
+vector<PortDesc> *ModuleDesc_impl::ports()
+{
+ return new vector<PortDesc>(_ports);
+}
+
+long ModuleDesc_impl::x()
+{
+ return _x;
+}
+
+long ModuleDesc_impl::y()
+{
+ return _y;
+}
+
+long ModuleDesc_impl::width()
+{
+ assert(false);
+ return 0;
+}
+
+long ModuleDesc_impl::height()
+{
+ assert(false);
+ return 0;
+}
+
+
+bool ModuleDesc_impl::moveTo( long x, long y )
+{
+ // FIXME: collision checking!
+ _x = x;
+ _y = y;
+
+ return(true);
+}
+
+
+// Implementation for interface StructureDesc
+long StructureDesc_impl::width()
+{
+ assert(false);
+ return 0;
+}
+
+long StructureDesc_impl::height()
+{
+ assert(false);
+ return 0;
+}
+
+/*
+ * Query the module for it's paramenters
+ */
+
+void ModuleDesc_impl::constructor( StructureDesc parent,
+ const Arts::ModuleInfo& info )
+{
+ _name = info.name;
+ _x = -1; // no position assigned
+ _y = -1;
+ _ID = parent.obtainID();
+ _parent = parent;
+ _isInterface = info.isInterface;
+ _isStructure = info.isStructure;
+
+ collectPorts(info);
+}
+
+ModuleDesc_impl::~ModuleDesc_impl()
+{
+}
+
+bool ModuleDesc_impl::isInterface()
+{
+ return _isInterface;
+}
+
+bool ModuleDesc_impl::isStructure()
+{
+ return _isStructure;
+}
+
+
+PortDesc ModuleDesc_impl::findPort(const string& name)
+{
+ vector<PortDesc>::iterator p;
+
+ for(p = _ports.begin(); p != _ports.end(); p++)
+ {
+ if(name == p->name()) return *p;
+ }
+
+ return PortDesc::null();
+}
+
+long ModuleDesc_impl::collectPorts( const Arts::ModuleInfo& info )
+{
+ vector<PortType>::const_iterator i;
+ vector<string>::const_iterator ni = info.portnames.begin();
+ long portcount = 0;
+
+ for(i=info.ports.begin(); i != info.ports.end(); i++)
+ {
+ const PortType& porttype = *i;
+ const string& portname = *ni++;
+
+ artsdebug("#%d: %s\n",portcount,portname.c_str());
+
+ PortDesc pd(self(),portname,porttype);
+ _ports.push_back(pd);
+ portcount++;
+ }
+ return(portcount);
+}
+
+ModuleDesc StructureDesc_impl::createModuleDesc( const ModuleInfo& info )
+{
+ Arts::ModuleDesc result = createModuleDesc(info.name);
+
+ assert(!result.isNull());
+ return result;
+}
+
+ModuleDesc StructureDesc_impl::createModuleDesc( const string& name )
+{
+ /* FIXME: need new comment
+ * to create a representation of a specified synth module, we
+ *
+ * - create an instance of this synth module by contacting the
+ * module server and telling him to do so (result is a C++ class)
+ *
+ * - create an instance of a ModuleDesc, and tell it to query the
+ * module for it's parameters (result is a CORBA object)
+ *
+ * - destroy the synth module (C++ class) again and return a reference
+ * to the CORBA object
+ */
+/*
+ ModuleServer<SynthModule> *MS_SynthModule;
+ MS_SynthModule = (ModuleServer<SynthModule> *)SynthModule::get_MS();
+
+ SynthModule *m = (SynthModule *)MS_SynthModule->getModule(name);
+*/
+#if 0
+ Arts::ModuleInfo_var info = ModuleBroker->lookupModule(name);
+ if(!info) return 0;
+#endif
+ const Arts::ModuleInfo& info = makeModuleInfo(name);
+ Arts::ModuleDesc moduledesc = ModuleDesc(self(),info);
+ _modules.push_back(moduledesc);
+ return moduledesc;
+}
+
+void StructureDesc_impl::freeModuleDesc(ModuleDesc moduledesc)
+{
+ vector<ModuleDesc>::iterator i;
+
+ for(i=_modules.begin();i != _modules.end(); i++)
+ {
+ Arts::ModuleDesc current = *i;
+
+ if(current.ID() == moduledesc.ID())
+ {
+ _modules.erase(i); // will get freed automagically
+ return;
+ }
+ }
+}
+
+vector<ModuleDesc> *StructureDesc_impl::modules()
+{
+ vector<ModuleDesc> *retval = new vector<ModuleDesc>(_modules);
+ return(retval);
+}
+
+void StructureDesc_impl::addInheritedInterface(const string& iface)
+{
+ _inheritedInterfaces.push_back(iface);
+}
+
+void StructureDesc_impl::removeInheritedInterface(const string& iface)
+{
+ vector<string> newII;
+ vector<string>::iterator ii;
+
+ for(ii = _inheritedInterfaces.begin(); ii != _inheritedInterfaces.end(); ii++)
+ if(*ii != iface)
+ newII.push_back(*ii);
+
+ _inheritedInterfaces = newII;
+}
+
+vector<string> *StructureDesc_impl::inheritedInterfaces()
+{
+ return new vector<string>(_inheritedInterfaces);
+}
+
+StructureDesc_impl::StructureDesc_impl()
+{
+ arts_debug("PORT: created structuredesc_impl");
+ nextID = 0;
+ _valid = true;
+ _externalInterface.name = "unknown"; // FIXME
+ _externalInterface.isStructure = true;
+ _externalInterface.isInterface = false;
+}
+
+StructureDesc_impl::~StructureDesc_impl()
+{
+ artsdebug("StructureDesc released...\n");
+}
+
+long StructureDesc_impl::obtainID()
+{
+ return(nextID++);
+}
+
+bool StructureDesc_impl::valid()
+{
+ return(_valid);
+}
+
+void StructureDesc_impl::clear()
+{
+ _modules.clear();
+ _ports.clear();
+ _inheritedInterfaces.clear();
+ _valid = true;
+}
+
+// "file" management
+
+vector<string> *PortDesc_impl::saveToList()
+{
+ vector<string> *list = new vector<string>;
+
+ sqprintf(list,"id=%ld",_ID);
+ if(_hasValue)
+ {
+ if(_type.dataType == "string")
+ {
+ sqprintf(list,"string_data=%s",stringValue().c_str());
+ }
+ else if(_type.dataType == "float")
+ {
+ sqprintf(list,"audio_data=%2.5f",floatValue());
+ }
+ else
+ {
+ Buffer b;
+ _value.writeType(b);
+ sqprintf(list,"any_data=%s",b.toString("value").c_str());
+ }
+ }
+
+ if(_isConnected)
+ {
+ vector<PortDesc_wref>::iterator i;
+
+ for(i=_connections.begin();i != _connections.end(); i++)
+ {
+ Arts::PortDesc port = *i;
+ if(!port.isNull()) sqprintf(list,"connect_to=%ld",port.ID());
+ }
+ }
+ return list;
+}
+
+vector<string> *ModuleDesc_impl::saveToList()
+{
+ vector<string> *list = new vector<string>;
+ vector<PortDesc>::iterator i;
+
+ sqprintf(list,"id=%ld",_ID);
+ sqprintf(list,"x=%ld",_x);
+ sqprintf(list,"y=%ld",_y);
+ for(i=_ports.begin();i != _ports.end();i++)
+ {
+ PortDesc pd = *i;
+ sqprintf(list,"port=%s",pd.name().c_str());
+
+ vector<string> *portlist = pd.saveToList();
+ addSubStringSeq(list,portlist);
+ delete portlist;
+ }
+ return list;
+}
+
+vector<string> *StructureDesc_impl::saveToList()
+{
+ vector<string> *list = new vector<string>;
+ vector<ModuleDesc>::iterator mi;
+ vector<StructurePortDesc>::iterator pi;
+ vector<string>::iterator ii;
+
+ sqprintf(list,"name=%s",_externalInterface.name.c_str());
+ for(mi=_modules.begin();mi != _modules.end();mi++)
+ {
+ ModuleDesc md = *mi;
+ sqprintf(list,"module=%s",md.name().c_str());
+
+ vector<string> *modulelist = md.saveToList();
+ addSubStringSeq(list,modulelist);
+ delete modulelist;
+ }
+ for(pi=_ports.begin(); pi!=_ports.end(); pi++)
+ {
+ Arts::StructurePortDesc spd = *pi;
+ sqprintf(list,"structureport");
+
+ vector<string> *portlist= spd.saveToList();
+ addSubStringSeq(list,portlist);
+ delete portlist;
+ }
+ for(ii=_inheritedInterfaces.begin(); ii != _inheritedInterfaces.end(); ii++)
+ sqprintf(list,"interface=%s",ii->c_str());
+
+ return list;
+}
+
+void PortDesc_impl::internalReConnect( const vector<PortDesc>& allports )
+{
+ vector<PortDesc>::const_iterator i;
+
+ for(i=allports.begin(); i != allports.end(); i++)
+ {
+ PortDesc pd = (*i);
+ long oid = pd.internalOldID();
+
+ if(find(oldConnections.begin(),oldConnections.end(),oid)
+ != oldConnections.end())
+ {
+ connectTo(pd);
+ }
+ }
+}
+
+long PortDesc_impl::internalOldID()
+{
+ return _oldID;
+}
+
+void PortDesc_impl::loadFromList(const vector<string>& list)
+{
+ unsigned long i;
+ string cmd,param;
+ for(i=0;i<list.size();i++)
+ {
+ if(parse_line(list[i],cmd,param)) // otherwise: empty or comment
+ {
+ if(cmd == "audio_data") {
+ floatValue(atof(param.c_str()));
+ } else if(cmd == "string_data") {
+ stringValue(param);
+ } else if(cmd == "any_data") {
+ Buffer b;
+ if(b.fromString(param,"value"))
+ {
+ Any any;
+ any.readType(b);
+ if(!b.readError() && !b.remaining())
+ value(any);
+ }
+ } else if(cmd == "id") {
+ _oldID = atol(param.c_str());
+ } else if(cmd == "connect_to") {
+ oldConnections.push_back(atol(param.c_str()));
+ }
+ }
+ }
+}
+
+void ModuleDesc_impl::loadFromList(const vector<string>& list)
+{
+ artsdebug("mlist-----------\n");
+ unsigned long i;
+ string cmd, param;
+ for(i=0;i<list.size();i++)
+ {
+ if(parse_line(list[i],cmd,param)) // otherwise: empty or comment
+ {
+ artsdebug("MD: load-> cmd was %s\n",cmd.c_str());
+ if(cmd == "port")
+ {
+ string portName =
+ OldFormatTranslator::newPortName(_name,param);
+ PortDesc pd = PortDesc::null();
+ vector<PortDesc>::iterator pi;
+
+ for(pi=_ports.begin(); pi != _ports.end(); pi++)
+ {
+ artsdebug("pdi = %s, portName = %s\n",pi->name().c_str(),
+ portName.c_str());
+ if(pi->name() == portName) pd = *pi;
+ }
+ assert(!pd.isNull());
+
+ vector<string> *plist = getSubStringSeq(&list,i);
+ pd.loadFromList(*plist);
+ delete plist;
+ } else if(cmd == "x") {
+ _x = atol(param.c_str());
+ artsdebug("X set to %ld (param was %s)\n",_x,param.c_str());
+ } else if(cmd == "y") {
+ _y = atol(param.c_str());
+ artsdebug("Y set to %ld (param was %s)\n",_y,param.c_str());
+ }
+ }
+ }
+ artsdebug("-----------mlist\n");
+}
+
+void StructureDesc_impl::loadFromList(const vector<string>& list)
+{
+ string cmd,param;
+ unsigned long i;
+ vector<PortDesc> allports;
+
+ clear();
+ _externalInterface.name = (const char *)"unknown";
+
+ artsdebug("loadFromList; listlen = %ld\n",list.size());
+ for(i=0;i<list.size();i++)
+ {
+ if(parse_line(list[i],cmd,param)) // otherwise: empty or comment
+ {
+ artsdebug("SD: load-> cmd was %s\n",cmd.c_str());
+ if(cmd == "module")
+ {
+ string newName = OldFormatTranslator::newModuleName(param);
+ ModuleDesc md = createModuleDesc(newName);
+
+ vector<string> *mlist = getSubStringSeq(&list,i);
+
+ if(!md.isNull())
+ {
+ md.loadFromList(*mlist);
+
+ // PORT: order changed
+ vector<PortDesc> *pd = md.ports();
+ vector<PortDesc>::iterator pi;
+ for(pi = pd->begin(); pi != pd->end();pi++)
+ allports.push_back(*pi);
+
+ delete pd;
+ }
+ else
+ {
+ // module couldn't be found
+ _valid = false;
+ }
+ delete mlist;
+ }
+ else if(cmd == "name")
+ {
+ _externalInterface.name = param;
+ }
+ else if(cmd == "interface")
+ {
+ _inheritedInterfaces.push_back(param);
+ }
+ else if(cmd == "structureport")
+ {
+ // just to have valid values to pass to the new (to be loaded)
+ // port:
+ PortType type;
+ type.direction = Arts::input;
+ type.dataType = "float";
+ type.connType = Arts::conn_stream;
+ type.isMultiPort = false;
+
+ StructurePortDesc spd =
+ createStructurePortDesc(type,"unknown");
+
+ vector<string> *splist = getSubStringSeq(&list,i);
+ spd.loadFromList(*splist);
+ delete splist;
+
+ // yes; this is a port as well
+ allports.push_back(spd);
+ }
+ }
+ }
+
+ for(i=0;i<allports.size();i++)
+ allports[i].internalReConnect(allports);
+}
+
+void StructureDesc_impl::name(const string& name)
+{
+ _externalInterface.name = name;
+}
+
+string StructureDesc_impl::name()
+{
+ return _externalInterface.name;
+}
+
+long extint_pscore(StructurePortDesc p)
+{
+ long result = p.position(); //p->X()*1000+p->Y();
+ if(p.type().direction == Arts::input) result += 5000000;
+
+ return result;
+}
+
+bool extint_port_compare(StructurePortDesc p1, StructurePortDesc p2)
+{
+ long p1s = extint_pscore(p1);
+ long p2s = extint_pscore(p2);
+
+ artsdebug("compare; [%s] = %d ; [%s] = %d\n", p1.name().c_str(),p1s,
+ p2.name().c_str(),p2s);
+ return (p1s < p2s);
+// return -1;
+ //if(p1s == p2s) return 0;
+ //return 1;
+}
+
+ModuleInfo StructureDesc_impl::externalInterface()
+{
+ ModuleInfo result = _externalInterface;
+ vector<StructurePortDesc> sorted_ports = _ports;
+ vector<StructurePortDesc>::iterator p;
+ unsigned long l;
+/* PORT:
+ for(l=0;l<_Ports->length();l++) sorted_ports.push_back((*_Ports)[l]);
+*/
+ sort(sorted_ports.begin(),sorted_ports.end(),extint_port_compare);
+
+ l = 0;
+ for(p=sorted_ports.begin();p != sorted_ports.end();p++)
+ {
+ string pname = p->name();
+ PortType ptype = p->type();
+
+ if(ptype.direction == Arts::input)
+ ptype.direction = Arts::output;
+ else
+ ptype.direction = Arts::input;
+
+ artsdebug("externalInterface; sorted ports: %d => %s\n",l,pname.c_str());
+ result.ports.push_back(ptype);
+ result.portnames.push_back(pname);
+ l++;
+ }
+ return result;
+}
+
+vector<StructurePortDesc> *StructureDesc_impl::ports()
+{
+ return new vector<StructurePortDesc>(_ports);
+}
+
+StructurePortDesc StructureDesc_impl::createStructurePortDesc(
+ const Arts::PortType& type, const string& name)
+{
+ artsdebug("creating new port %s\n",name.c_str());
+ StructurePortDesc port(self(), name, type);
+ _ports.push_back(port);
+
+ // set the Position (put it at the end of the ports)
+ unsigned long i, count = 0;
+ for(i=0;i<_ports.size();i++)
+ {
+ if(_ports[i].type().direction == type.direction) count++;
+ }
+ assert(count > 0); // we just inserted one ;)
+ port.internalSetPosition(count-1);
+ return port;
+}
+
+void StructureDesc_impl::freeStructurePortDesc(StructurePortDesc portdesc)
+{
+ vector<StructurePortDesc>::iterator i;
+
+ for(i=_ports.begin(); i != _ports.end(); i++)
+ {
+ if(i->ID() == portdesc.ID())
+ {
+ _ports.erase(i);
+ return;
+ }
+ }
+}
+
+void StructureDesc_impl::moveStructurePortDesc(StructurePortDesc
+ portdesc, long newposition)
+{
+ const Arts::PortType& type = portdesc.type();
+
+ unsigned long i;
+ long count = 0;
+ for(i=0;i<_ports.size();i++)
+ {
+ if(_ports[i].type().direction == type.direction) count++;
+ }
+
+ if(newposition < 0) newposition = 0;
+ if(newposition > count-1) newposition = count-1;
+
+ if(newposition == portdesc.position()) return;
+
+ int delta, lower, upper;
+
+ if(newposition > portdesc.position())
+ {
+ // if the port gets a higher position, move all ports that
+ // are between it's current position and its new position down one
+ lower = portdesc.position();
+ upper = newposition;
+ delta = -1;
+ }
+ else
+ {
+ // if the port gets a lower position, move all ports that
+ // are between it's current position and its new position up one
+ lower = newposition;
+ upper = portdesc.position();
+ delta = 1;
+ }
+
+ for(i=0;i<_ports.size();i++)
+ {
+ StructurePortDesc pd = _ports[i];
+
+ if(pd.type().direction == type.direction)
+ {
+ if(pd.ID() != portdesc.ID() &&
+ pd.position() >= lower && pd.position() <= upper)
+ {
+ pd.internalSetPosition(pd.position()+delta);
+ }
+ }
+
+ }
+ portdesc.internalSetPosition(newposition);
+}
+
+void StructurePortDesc_impl::constructor(StructureDesc parent,
+ const string& name, const PortType& type)
+{
+ PortDesc_impl::constructor(ModuleDesc::null(),name,type);
+ _parentStructure = parent;
+ _ID = parent.obtainID();
+ _x = 0;
+ _y = 0;
+ _position = 0;
+}
+
+StructurePortDesc_impl::~StructurePortDesc_impl()
+{
+ // this destructor is required to make some compilers (egcs-1.1.2) compile
+}
+
+long StructurePortDesc_impl::x()
+{
+ return _x;
+}
+
+long StructurePortDesc_impl::y()
+{
+ return _y;
+}
+
+long StructurePortDesc_impl::position()
+{
+ return _position;
+}
+
+void StructurePortDesc_impl::lowerPosition()
+{
+ StructureDesc parent = _parentStructure; // weak reference
+
+ if(!parent.isNull())
+ parent.moveStructurePortDesc(self(), _position-1);
+}
+
+void StructurePortDesc_impl::raisePosition()
+{
+ StructureDesc parent = _parentStructure; // weak reference
+
+ if(!parent.isNull())
+ parent.moveStructurePortDesc(self(), _position+1);
+}
+
+void StructurePortDesc_impl::rename(const string& newname)
+{
+ _name = newname;
+}
+
+void StructurePortDesc_impl::inheritedInterface(const string& iface)
+{
+ _inheritedInterface = iface;
+}
+
+string StructurePortDesc_impl::inheritedInterface()
+{
+ return _inheritedInterface;
+}
+
+// only used by the structure to reorder the ports
+void StructurePortDesc_impl::internalSetPosition(long position)
+{
+ _position = position;
+}
+
+StructureDesc StructurePortDesc_impl::parentStructure()
+{
+ return _parentStructure;
+}
+
+bool StructurePortDesc_impl::moveTo( long X, long Y )
+{
+ // FIXME: check space
+ _x = X;
+ _y = Y;
+
+ return true;
+}
+
+/*
+ override load & save behaviour this kind of port requires that we save the type
+ of the port as well, that means all of the porttype:
+
+ enum PortDirection {input, output};
+ enum PortDataType {audio_data, string_data};
+ enum PortConnType {stream, event, property};
+ struct PortType {
+ PortDirection direction;
+ PortDataType dataType;
+ PortConnType connType;
+ };
+
+ so when saved, it will look like that:
+
+ {
+ name=fasel
+ x=4
+ y=2
+ type
+ {
+ direction=input/output
+ datatype=audio/string
+ conntype=stream/event/property
+ }
+ data
+ {
+ [original port saves here]
+ }
+ }
+*/
+
+PortType loadTypeFromList(const vector<string>& list)
+{
+ unsigned long i,loadstate = 0;
+ string cmd,param;
+ Arts::PortType result;
+
+ for(i=0;i<list.size();i++)
+ {
+ if(parse_line(list[i],cmd,param)) // otherwise: empty or comment
+ {
+ artsdebug("PortType: load-> cmd was %s\n",cmd.c_str());
+ if(cmd == "direction")
+ {
+ if(param == "input") {
+ result.direction = Arts::input;
+ }
+ else if(param == "output") {
+ result.direction = Arts::output;
+ }
+ else assert(false);
+
+ loadstate += 1;
+ } else if(cmd == "datatype") {
+ if(param == "audio") {
+ result.dataType = "float";
+ }
+ else if(param == "string") {
+ result.dataType = "string";
+ }
+ else assert(false);
+
+ loadstate += 100;
+ } else if(cmd == "conntype") {
+ if(param == "stream") {
+ result.connType = Arts::conn_stream;
+ }
+ else if(param == "event") {
+ result.connType = Arts::conn_event;
+ }
+ else if(param == "property") {
+ result.connType = Arts::conn_property;
+ artsdebug("got property stuff\n");
+ }
+ else assert(false);
+
+ loadstate += 10000;
+ }
+ }
+ }
+ assert(loadstate == 10101); // should see every member exactly once
+ result.isMultiPort = false;
+ return result;
+}
+
+void StructurePortDesc_impl::loadFromList(const vector<string>& list)
+{
+ artsdebug("structureportlist-----------\n");
+ unsigned long i;
+ string cmd,param;
+ vector<string> *typelist = 0, *datalist = 0;
+ bool haveType = false, haveData = false;
+ // need both to do restore, type first
+
+ for(i=0;i<list.size();i++)
+ {
+ if(parse_line(list[i],cmd,param)) // otherwise: empty or comment
+ {
+ artsdebug("StructurePortDesc: load-> cmd was %s\n",cmd.c_str());
+ if(cmd == "type")
+ {
+ assert(!haveType); // only allowed once
+ haveType = true;
+ typelist = getSubStringSeq(&list,i);
+ } else if(cmd == "data") {
+ assert(!haveData); // only allowed once
+ haveData = true;
+ datalist = getSubStringSeq(&list,i);
+ } else if(cmd == "x") {
+ _x = atol(param.c_str());
+ artsdebug("X set to %ld (param was %s)\n",_x,param.c_str());
+ } else if(cmd == "y") {
+ _y = atol(param.c_str());
+ artsdebug("Y set to %ld (param was %s)\n",_y,param.c_str());
+ } else if(cmd == "position") {
+ _position = atol(param.c_str());
+ artsdebug("Position set to %ld (param was %s)\n",_position,
+ param.c_str());
+ } else if(cmd == "name") {
+ _name = param;
+ artsdebug("Name set to %s\n",_name.c_str());
+ } else if(cmd == "interface") {
+ _inheritedInterface = param;
+ artsdebug("Interface set to %s\n",_inheritedInterface.c_str());
+ }
+ }
+ }
+ assert(haveType && haveData);
+
+ _type = loadTypeFromList(*typelist);
+
+ if(_type.connType == Arts::conn_property) artsdebug("have property here\n");
+ PortDesc_impl::loadFromList(*datalist);
+
+ delete typelist;
+ delete datalist;
+ artsdebug("-----------structureportlist\n");
+}
+
+vector<string> *saveTypeToList(const PortType& type)
+{
+ vector<string> *list = new vector<string>;
+
+ switch(type.direction)
+ {
+ case Arts::input: sqprintf(list,"direction=input");
+ break;
+ case Arts::output: sqprintf(list,"direction=output");
+ break;
+ default: assert(false); // should never happen!
+ }
+ if(type.dataType == "float")
+ {
+ sqprintf(list,"datatype=audio");
+ }
+ else if(type.dataType == "string")
+ {
+ sqprintf(list,"datatype=string");
+ }
+ else
+ {
+ assert(false); // should never happen!
+ }
+ switch(type.connType)
+ {
+ case Arts::conn_stream: sqprintf(list,"conntype=stream");
+ break;
+ case Arts::conn_event: sqprintf(list,"conntype=event");
+ break;
+ case Arts::conn_property: sqprintf(list,"conntype=property");
+ break;
+ default: assert(false); // should never happen!
+ }
+
+ return list;
+}
+
+vector<string> *StructurePortDesc_impl::saveToList()
+{
+ vector<string> *list = new vector<string>;
+ sqprintf(list,"name=%s",_name.c_str());
+ sqprintf(list,"x=%ld",_x);
+ sqprintf(list,"y=%ld",_y);
+ sqprintf(list,"position=%ld",_position);
+
+ if(!_inheritedInterface.empty())
+ sqprintf(list, "interface=%s",_inheritedInterface.c_str());
+
+ sqprintf(list,"type");
+
+ vector<string> *typelist = saveTypeToList(_type);
+ addSubStringSeq(list,typelist);
+ delete typelist;
+
+ sqprintf(list,"data");
+
+ vector<string> *portlist = PortDesc_impl::saveToList();
+ addSubStringSeq(list,portlist);
+ delete portlist;
+
+ return list;
+}
diff --git a/arts/tools/Makefile.am b/arts/tools/Makefile.am
new file mode 100644
index 00000000..e503d54a
--- /dev/null
+++ b/arts/tools/Makefile.am
@@ -0,0 +1,82 @@
+
+SUBDIRS= . pics
+
+INCLUDES = \
+ -I$(top_srcdir)/arts/gui/kde \
+ -I$(top_builddir)/arts/gui/common \
+ -I$(top_builddir)/arts/modules \
+ -I$(top_builddir)/arts/modules/common \
+ -I$(top_builddir)/arts/modules/synth \
+ -I$(top_builddir)/arts/modules/mixers \
+ -I$(top_builddir)/arts/modules/effects \
+ -I$(top_builddir)/arts/midi \
+ -I$(kde_includes)/arts \
+ $(all_includes)
+
+
+lib_LTLIBRARIES = libartscontrolsupport.la libartscontrolapplet.la
+
+libartscontrolapplet_la_SOURCES = \
+ artscontrolapplet.cpp
+
+libartscontrolsupport_la_SOURCES = \
+ templateview.cpp artsactions.cpp \
+ audiomanager.cpp choosebusdlg.cpp \
+ midimanagerview.cpp midimanagerdlg.ui midimanagerwidget.ui \
+ midiportdlg.cpp midiinstdlg.cpp environmentview.cpp \
+ fftscopeview.cpp mediatypesview.cpp statusview.cpp
+
+METASOURCES = AUTO
+
+SOMAJOR = 1
+SOMINOR = 0
+SOSUBMINOR = 0
+
+libartscontrolapplet_la_LDFLAGS = $(all_libraries) -version-info $(SOMAJOR):$(SOMINOR):$(SOSUBMINOR) -no-undefined -module
+libartscontrolsupport_la_LDFLAGS = $(all_libraries) -version-info $(SOMAJOR):$(SOMINOR):$(SOSUBMINOR) -no-undefined
+
+libartscontrolapplet_la_LIBADD = \
+ -lartsflow -lartsflow_idl -lmcop -lqtmcop $(LIBDL) -lsoundserver_idl -lartskde \
+ $(LIB_KDEUI) ./libartscontrolsupport.la
+
+# Arnolds version with dynamic-linking for testing.
+#
+#libartscontrolsupport_la_LIBADD = \
+# -lartsflow -lartsflow_idl -lmcop -lqtmcop $(LIBDL) -lsoundserver_idl -lartskde \
+# $(top_builddir)/arts/gui/common/libartsgui_idl.la \
+# -L$(top_builddir)/arts/gui/kde -lartsgui_kde \
+# -L$(top_builddir)/arts/modules -lartsmodules \
+# $(LIB_KDEUI)
+
+libartscontrolsupport_la_LIBADD = \
+ -lartsflow -lartsflow_idl -lmcop -lqtmcop $(LIBDL) -lsoundserver_idl -lartskde \
+ $(top_builddir)/arts/gui/common/libartsgui_idl.la \
+ $(top_builddir)/arts/gui/kde/libartsgui_kde.la \
+ $(top_builddir)/arts/modules/libartsmodules.la \
+ $(LIB_KDEUI)
+
+bin_PROGRAMS = artscontrol
+artscontrol_SOURCES = main.cpp levelmeters.cpp
+artscontrol_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+artscontrol_LDADD = libartscontrolsupport.la \
+ $(LIB_KDEUI) $(LIB_KSYCOCA) $(LIB_KFILE) \
+ -lqtmcop -lartsflow_idl -lsoundserver_idl -lartskde
+
+xdg_apps_DATA = artscontrol.desktop
+
+lnkdir = $(kde_datadir)/kicker/applets
+lnk_DATA = artscontrolapplet.desktop
+
+rcdir = $(kde_datadir)/artscontrol
+rc_DATA = artscontrol.rc artsmidimanagerview.rc
+
+messages: rc.cpp
+ $(XGETTEXT) *.h *.cpp -o $(podir)/artscontrol.pot
+
+artsactions.lo: ../modules/artsmodules.h ../midi/artsmidi.h ../gui/common/artsgui.h ../modules/common/artsmodulescommon.h ../modules/synth/artsmodulessynth.h ../modules/effects/artsmoduleseffects.h ../modules/mixers/artsmodulesmixers.h
+artscontrolapplet.lo: ../gui/common/artsgui.h ../modules/effects/artsmoduleseffects.h ../modules/common/artsmodulescommon.h ../midi/artsmidi.h ../modules/synth/artsmodulessynth.h ../modules/artsmodules.h ../modules/mixers/artsmodulesmixers.h
+environmentview.lo: ../modules/artsmodules.h ../midi/artsmidi.h ../gui/common/artsgui.h ../modules/common/artsmodulescommon.h ../modules/synth/artsmodulessynth.h ../modules/effects/artsmoduleseffects.h ../modules/mixers/artsmodulesmixers.h
+fftscopeview.lo: ../modules/artsmodules.h ../midi/artsmidi.h ../gui/common/artsgui.h ../modules/common/artsmodulescommon.h ../modules/synth/artsmodulessynth.h ../modules/effects/artsmoduleseffects.h ../modules/mixers/artsmodulesmixers.h
+main.o: ../gui/common/artsgui.h ../modules/artsmodules.h ../midi/artsmidi.h ../modules/common/artsmodulescommon.h ../modules/synth/artsmodulessynth.h ../modules/effects/artsmoduleseffects.h ../modules/mixers/artsmodulesmixers.h
+midimanagerview.lo: ../midi/artsmidi.h ../modules/artsmodules.h ../gui/common/artsgui.h ../modules/common/artsmodulescommon.h ../modules/synth/artsmodulessynth.h ../modules/effects/artsmoduleseffects.h ../modules/mixers/artsmodulesmixers.h
+
diff --git a/arts/tools/artsactions.cpp b/arts/tools/artsactions.cpp
new file mode 100644
index 00000000..b75de027
--- /dev/null
+++ b/arts/tools/artsactions.cpp
@@ -0,0 +1,191 @@
+/*
+
+ Copyright (C) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsactions.h"
+
+#include <kaction.h>
+#include <kactioncollection.h>
+#include <kpopupmenu.h>
+#include <klocale.h>
+#include <kglobal.h>
+//#include <kdebug.h>
+
+#include <kartsserver.h>
+
+#include "fftscopeview.h"
+#include "audiomanager.h"
+#include "statusview.h"
+#include "midimanagerview.h"
+#include "environmentview.h"
+#include "mediatypesview.h"
+
+ArtsActions::ArtsActions( KArtsServer* server, KActionCollection* col, QWidget* parent, const char* name )
+ : QObject( parent,name )
+ , _kartsserver( server )
+ , _actioncollection( col )
+ , _a_sv( 0 ), _a_am( 0 ), _a_asv( 0 ), _a_mmv( 0 ), _a_ev( 0 ), _a_mtv( 0 )
+// , _a_morebars( 0 ), _a_lessbars( 0 )
+ , _a_style_normal( 0 ), _a_style_fire( 0 ), _a_style_line( 0 ), _a_style_led( 0 ), _a_style_analog( 0 ), _a_style_small( 0 )
+ , _stylemenu( 0 )
+ , _sv( 0 ), _am( 0 ), _asv( 0 ), _mmv( 0 ), _ev( 0 ), _mtv( 0 )
+{
+ //kdDebug()<<k_funcinfo<<endl;
+ KGlobal::locale()->insertCatalogue( "artscontrol" );
+ if ( !_kartsserver ) _kartsserver = new KArtsServer( this );
+}
+
+ArtsActions::~ArtsActions() {
+ //kdDebug()<<k_funcinfo<<endl;
+ if ( _sv ) viewScopeView();
+ if ( _am ) viewAudioManager();
+ if ( _asv ) viewArtsStatusView();
+ if ( _mmv ) viewMidiManagerView();
+ if ( _ev ) viewEnvironmentView();
+ if ( _mtv ) viewMediaTypesView();
+}
+
+KAction* ArtsActions::actionScopeView() {
+ if ( !_a_sv ) _a_sv = new KAction( i18n( "&FFT Scope" ), "artsfftscope", KShortcut(), this, SLOT( viewScopeView() ), _actioncollection, "artssupport_view_scopeview" );
+ return _a_sv;
+}
+KAction* ArtsActions::actionAudioManager() {
+ if ( !_a_am ) _a_am = new KAction( i18n( "&Audio Manager" ), "artsaudiomanager", KShortcut(), this, SLOT( viewAudioManager() ), _actioncollection, "artssupport_view_audiomanager" );
+ return _a_am;
+}
+KAction* ArtsActions::actionArtsStatusView() {
+ if ( !_a_asv ) _a_asv = new KAction( i18n( "aRts &Status" ), "artscontrol", KShortcut(), this, SLOT( viewArtsStatusView() ), _actioncollection, "artssupport_view_artsstatus" );
+ return _a_asv;
+}
+KAction* ArtsActions::actionMidiManagerView() {
+ if ( !_a_mmv ) _a_mmv = new KAction( i18n( "&MIDI Manager" ), "artsmidimanager", KShortcut(), this, SLOT( viewMidiManagerView() ), _actioncollection, "artssupport_view_midimanager" );
+ return _a_mmv;
+}
+KAction* ArtsActions::actionEnvironmentView() {
+ if ( !_a_ev ) _a_ev = new KAction( i18n( "&Environment" ), "artsenvironment", KShortcut(), this, SLOT( viewEnvironmentView() ), _actioncollection, "artssupport_view_environment" );
+ return _a_ev;
+}
+KAction* ArtsActions::actionMediaTypesView() {
+ if ( !_a_mtv ) _a_mtv = new KAction( i18n( "Available Media &Types" ), "artsmediatypes", KShortcut(), this, SLOT( viewMediaTypesView() ), _actioncollection, "artssupport_view_mediatypes" );
+ return _a_mtv;
+}
+
+KAction* ArtsActions::actionStyleNormal() {
+ if ( !_a_style_normal ) _a_style_normal = new KAction( i18n( "Style: NormalBars" ), "", KShortcut(), this, SLOT( _p_style_normal() ), _actioncollection, "artssupport_style_normal" );
+ return _a_style_normal;
+}
+KAction* ArtsActions::actionStyleFire() {
+ if ( !_a_style_fire ) _a_style_fire = new KAction( i18n( "Style: FireBars" ), "", KShortcut(), this, SLOT( _p_style_fire() ), _actioncollection, "artssupport_style_fire" );
+ return _a_style_fire;
+}
+KAction* ArtsActions::actionStyleLine() {
+ if ( !_a_style_line ) _a_style_line = new KAction( i18n( "Style: LineBars" ), "", KShortcut(), this, SLOT( _p_style_line() ), _actioncollection, "artssupport_style_line" );
+ return _a_style_line;
+}
+KAction* ArtsActions::actionStyleLED() {
+ if ( !_a_style_led ) _a_style_led = new KAction( i18n( "Style: LEDs" ), "", KShortcut(), this, SLOT( _p_style_led() ), _actioncollection, "artssupport_style_led" );
+ return _a_style_led;
+}
+KAction* ArtsActions::actionStyleAnalog() {
+ if ( !_a_style_analog ) _a_style_analog = new KAction( i18n( "Style: Analog" ), "", KShortcut(), this, SLOT( _p_style_analog() ), _actioncollection, "artssupport_style_analog" );
+ return _a_style_analog;
+}
+KAction* ArtsActions::actionStyleSmall() {
+ if ( !_a_style_small ) _a_style_small = new KAction( i18n( "Style: Small" ), "", KShortcut(), this, SLOT( _p_style_small() ), _actioncollection, "artssupport_style_small" );
+ return _a_style_small;
+}
+KPopupMenu* ArtsActions::stylemenu() {
+ if ( !_stylemenu ) {
+ _stylemenu = new KPopupMenu();
+ KAction *tmp;
+ tmp = actionStyleNormal(); tmp->plug( _stylemenu );
+ tmp = actionStyleFire(); tmp->plug( _stylemenu );
+ tmp = actionStyleLine(); tmp->plug( _stylemenu );
+ tmp = actionStyleLED(); tmp->plug( _stylemenu );
+ tmp = actionStyleAnalog(); tmp->plug( _stylemenu );
+ tmp = actionStyleSmall(); tmp->plug( _stylemenu );
+ }
+ return _stylemenu;
+}
+
+KAction* ArtsActions::actionMoreBars( const QObject* receiver, const char* slot, KActionCollection *actioncollection ) {
+ static KAction *_a_morebars = new KAction( i18n( "More Bars in VU-Meters" ), "up", KShortcut(), receiver, slot, actioncollection, "artssupport_morebars" );
+ return _a_morebars;
+}
+KAction* ArtsActions::actionLessBars( const QObject* receiver, const char* slot, KActionCollection *actioncollection ) {
+ static KAction *_a_lessbars = new KAction( i18n( "Less Bars in VU-Meters" ), "down", KShortcut(), receiver, slot, actioncollection, "artssupport_lessbars" );
+ return _a_lessbars;
+}
+
+void ArtsActions::viewScopeView() {
+ if ( !_sv ) {
+ _sv = new FFTScopeView( _kartsserver->server() );
+ connect( _sv, SIGNAL( closed() ), this, SLOT( viewScopeView() ) );
+ } else {
+ delete _sv;
+ _sv = 0;
+ }
+}
+void ArtsActions::viewAudioManager() {
+ if ( !_am ) {
+ _am = new Gui_AUDIO_MANAGER();
+ connect( _am, SIGNAL( closed() ), this, SLOT( viewAudioManager() ) );
+ } else {
+ delete _am;
+ _am = 0;
+ }
+}
+void ArtsActions::viewArtsStatusView() {
+ if ( !_asv ) {
+ _asv = new ArtsStatusView( _kartsserver->server() );
+ connect( _asv, SIGNAL( closed() ), this, SLOT( viewArtsStatusView() ) );
+ } else {
+ delete _asv;
+ _asv = 0;
+ }
+}
+void ArtsActions::viewMidiManagerView() {
+ if ( !_mmv ) {
+ _mmv = new MidiManagerView();
+ connect( _mmv, SIGNAL( closed() ), this, SLOT( viewMidiManagerView() ) );
+ } else {
+ delete _mmv;
+ _mmv = 0;
+ }
+}
+void ArtsActions::viewEnvironmentView() {
+ if ( !_ev ) {
+ _ev = new EnvironmentView( defaultEnvironment() );
+ connect( _ev, SIGNAL( closed() ), this, SLOT( viewEnvironmentView() ) );
+ } else {
+ delete _ev;
+ _ev = 0;
+ }
+}
+void ArtsActions::viewMediaTypesView() {
+ if ( !_mtv ) {
+ _mtv = new MediaTypesView();
+ connect( _mtv, SIGNAL( closed() ), this, SLOT( viewMediaTypesView() ) );
+ } else {
+ delete _mtv;
+ _mtv = 0;
+ }
+}
+
+#include "artsactions.moc"
diff --git a/arts/tools/artsactions.h b/arts/tools/artsactions.h
new file mode 100644
index 00000000..c87945c2
--- /dev/null
+++ b/arts/tools/artsactions.h
@@ -0,0 +1,117 @@
+/*
+
+ Copyright (C) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_ACTIONS_H
+#define ARTS_ACTIONS_H
+
+#include <qwidget.h>
+#include <kdelibs_export.h>
+class KArtsServer;
+class KAction;
+class KActionCollection;
+class KPopupMenu;
+class FFTScopeView;
+class Gui_AUDIO_MANAGER;
+class ArtsStatusView;
+class MidiManagerView;
+class EnvironmentView;
+class MediaTypesView;
+
+class KDE_EXPORT ArtsActions : public QObject
+{
+ Q_OBJECT
+public:
+ /**
+ Constructs a ArtsActions-object.
+
+ Use the KActions you get from it to obtain a unique Style for all menus.
+
+ It also provides an easy way to have FFT-ScopeView, Audiomanager and other things available.
+
+ @param artsserver a pointer to a existing KArtsServer. If 0 a new is created.
+ @param actioncollection the KActionCollection all the actions should belong to. Names of the actions are then: artssupport_*
+ @param qwidget the parent QWidget
+ @param name the name of the object
+ */
+ ArtsActions( KArtsServer* artsserver, KActionCollection* actioncollection, QWidget* qwidget, const char* name=0 );
+ /** Destructor */
+ ~ArtsActions();
+
+ /** Returns an Action for showing the ScopeView. Unless otherwise connected it also toggles a ScopeView. */
+ KAction* actionScopeView();
+ /** Returns an Action for showing the Audiomanager. Unless otherwise connected it also toggles a Audiomanager. */
+ KAction* actionAudioManager();
+ /** Returns an Action for showing the StatusView. Unless otherwise connected it also toggles a StatusView. */
+ KAction* actionArtsStatusView();
+ /** Returns an Action for showing the MidiManager. Unless otherwise connected it also toggles a MidiManager. */
+ KAction* actionMidiManagerView();
+ /** Returns an Action for showing the EnvironmentView. Unless otherwise connected it also toggles a EnvironmentView. */
+ KAction* actionEnvironmentView();
+ /** Returns an Action for showing the MediaTypesView. Unless otherwise connected it also toggles a MediaTypesView. */
+ KAction* actionMediaTypesView();
+
+ KAction* actionStyleNormal();
+ KAction* actionStyleFire();
+ KAction* actionStyleLine();
+ KAction* actionStyleLED();
+ KAction* actionStyleAnalog();
+ KAction* actionStyleSmall();
+ KPopupMenu* stylemenu();
+
+ static KAction* actionMoreBars( const QObject*, const char*, KActionCollection* );
+ static KAction* actionLessBars( const QObject*, const char*, KActionCollection* );
+
+public slots:
+ void viewScopeView();
+ void viewAudioManager();
+ void viewArtsStatusView();
+ void viewMidiManagerView();
+ void viewEnvironmentView();
+ void viewMediaTypesView();
+private slots:
+ void _p_style_normal() { emit styleNormal(); }
+ void _p_style_fire() { emit styleFire(); }
+ void _p_style_line() { emit styleLine(); }
+ void _p_style_led() { emit styleLED(); }
+ void _p_style_analog() { emit styleAnalog(); }
+ void _p_style_small() { emit styleSmall(); }
+signals:
+ void styleNormal();
+ void styleFire();
+ void styleLine();
+ void styleLED();
+ void styleAnalog();
+ void styleSmall();
+private:
+ KArtsServer* _kartsserver;
+ KActionCollection* _actioncollection;
+ KAction *_a_sv, *_a_am, *_a_asv, *_a_mmv, *_a_ev, *_a_mtv;
+ //KAction *_a_morebars, *_a_lessbars;
+ KAction *_a_style_normal, *_a_style_fire, *_a_style_line, *_a_style_led, *_a_style_analog, *_a_style_small;
+ KPopupMenu* _stylemenu;
+ FFTScopeView *_sv;
+ Gui_AUDIO_MANAGER *_am;
+ ArtsStatusView *_asv;
+ MidiManagerView *_mmv;
+ EnvironmentView *_ev;
+ MediaTypesView *_mtv;
+};
+
+#endif
diff --git a/arts/tools/artscontrol.desktop b/arts/tools/artscontrol.desktop
new file mode 100644
index 00000000..4c7068d8
--- /dev/null
+++ b/arts/tools/artscontrol.desktop
@@ -0,0 +1,136 @@
+[Desktop Entry]
+Name=aRts Control Tool
+Name[af]=Arts Kontrole Program
+Name[ar]=أداة aRts للتحكم
+Name[bn]=আর্ট্‌স্ নিয়ন্ত্রণ টুল
+Name[br]=Ostilh renadur aRts
+Name[bs]=Alat za podešavanje aRts-a
+Name[ca]=Eina de control de aRts
+Name[cs]=Ovládání programu aRts
+Name[cy]=Erfyn Rheoli aRts
+Name[da]=aRts-kontrolværktøj
+Name[de]=aRts-Steuerung
+Name[el]=Εργαλείο ελέγχου aRts
+Name[eo]=Sonservostirilo
+Name[et]=aRts'i juhtimine
+Name[eu]=aRts-en kontrol tresna
+Name[fa]=ابزار کنترل aRts
+Name[fi]=aRts-asetustyökalu
+Name[fr]=aRtsControl
+Name[ga]=Uirlis Rialaithe aRts
+Name[gl]=Ferramenta de Control de aRts
+Name[hi]=एआरटीएस नियंत्रक औज़ार
+Name[hr]=Podešavanje aRts-a
+Name[hu]=aRts vezérlőprogram
+Name[is]=Stjórnborð aRts
+Name[it]=Strumento di controllo di aRts
+Name[ja]=aRts コントロールツール
+Name[kk]=aRts басқару құралы
+Name[km]=ឧបករណ៍​បញ្ជា aRts
+Name[ko]=aRts 설정 도구
+Name[lt]=aRts valdymo įrankis
+Name[lv]=aRts Vadības Rīks
+Name[mk]=Контролна алатка на aRts
+Name[nb]=aRts-kontrollverktøy
+Name[nds]=aRts-Kuntrullwarktüüch
+Name[ne]=aRts नियन्त्रण उपकरण
+Name[nl]=aRts bedieningshulpprogramma
+Name[nn]=aRts-kontrollverktøy
+Name[pa]=aRts ਕੰਟਰੋਲ ਸੰਦ
+Name[pl]=Sterowanie aRts
+Name[pt]=Ferramenta de Controlo do aRts
+Name[pt_BR]=Ferramenta de Controle do aRts
+Name[ro]=Utilitar control aRts
+Name[ru]=artscontrol
+Name[se]=aRts-stivrenreaidu
+Name[sk]=Ovládací nástroj aRts
+Name[sl]=Orodje za nadzor aRts
+Name[sr]=Контролни алат aRts-а
+Name[sr@Latn]=Kontrolni alat aRts-a
+Name[sv]=Arts-kontrollverktyg
+Name[ta]=aRts கட்டுப்பாட்டுக் கருவி
+Name[tg]=aRts Асбоби Идоракунӣ
+Name[th]=เครื่องมือควบคุม aRts
+Name[tr]=aRts Denetim Aracı
+Name[uk]=Керування aRts
+Name[uz]=aRts boshqaruv vositasi
+Name[uz@cyrillic]=aRts бошқарув воситаси
+Name[ven]=Tshishumiswa tshau langula aRTs
+Name[xh]=Ulawulo Lwemizobo
+Name[zh_CN]=aRts 控制工具
+Name[zh_HK]=aRts 控制工具
+Name[zh_TW]=aRts 控制工具
+Name[zu]=Ithuluazi Lokuphatha le aRts
+GenericName=Sound Server Control
+GenericName[af]=Klank Bediener Kontrole
+GenericName[bg]=Контрол на аудио сървъра
+GenericName[bn]=সাউণ্ড সার্ভার নিয়ন্ত্রণ
+GenericName[br]=Kefluniañ ar Servijer Son
+GenericName[bs]=Kontrola sound servera
+GenericName[ca]=Control del servidor de so
+GenericName[cs]=Ovládání zvukového serveru
+GenericName[cy]=Rheoli Gweinydd Sain
+GenericName[da]=Lydserverkontrol
+GenericName[de]=Soundserver-Steuerung
+GenericName[el]=Έλεγχος εξυπηρετητή ήχου
+GenericName[eo]=Agordo de la sonservo
+GenericName[es]=Control del servidor de sonido
+GenericName[et]=Heliserveri seadistamine
+GenericName[eu]=Soinu zerbitzariaren kontrola
+GenericName[fa]=کنترل کارساز صدا
+GenericName[fi]=Äänipalvelimen hallinta
+GenericName[fr]=Contrôle du serveur de son
+GenericName[ga]=Rialú Freastalaí Fuaime
+GenericName[gl]=Control do Servidor de Son
+GenericName[he]=שליטה בשרת הצליל
+GenericName[hi]=ध्वनि सर्वर नियंत्रक
+GenericName[hr]=Kontrola zvučnog poslužitelja
+GenericName[hu]=Hangszolgáltatás-vezérlő
+GenericName[is]=Stillingar hljóðmiðlarans
+GenericName[it]=Controllo del server sonoro
+GenericName[ja]=サウンドサーバのコントロール
+GenericName[kk]=Дыбыс серверін басқару
+GenericName[km]=ឧបករណ៍​បញ្ជា​ម៉ាស៊ីន​បម្រើ​សំឡេង
+GenericName[ko]=소리 서버 설정
+GenericName[lt]=Garsų serverio valdymas
+GenericName[mk]=Контрола на серверот за звук
+GenericName[ms]=Kawalan Pelayan Bunyi
+GenericName[nb]=Lydtjenerkontroll
+GenericName[nds]=Klangserverstüern
+GenericName[ne]=ध्वनि सर्भर नियन्त्रण
+GenericName[nl]=Geluidsserverbediening
+GenericName[nn]=Lydtenarstyring
+GenericName[pa]=ਸਾਊਂਡ ਸਰਵਰ ਕੰਟਰੋਲ
+GenericName[pl]=Sterowanie serwerem dźwięku
+GenericName[pt]=Controlo do Servidor de Som
+GenericName[pt_BR]=Controle do servidor de som
+GenericName[ro]=Control server de sunet
+GenericName[ru]=Управление звуковым сервером
+GenericName[se]=Jietnabálvástivren
+GenericName[sk]=Nastavenie zvukového servera
+GenericName[sl]=Nadzor zvočnega strežnika
+GenericName[sr]=Контрола звучног сервера
+GenericName[sr@Latn]=Kontrola zvučnog servera
+GenericName[sv]=Inställning av ljudserver
+GenericName[ta]=ஒலி சேவையக கட்டுப்பாடு
+GenericName[tg]=Идоракунии Хидматрасони Овоз
+GenericName[th]=ควบคุมเซิร์ฟเวอร์เสียง
+GenericName[tr]=Ses Sunucu Yöneticisi
+GenericName[uk]=Керування сервером звуку
+GenericName[uz]=Tovush serverini boshqarish
+GenericName[uz@cyrillic]=Товуш серверини бошқариш
+GenericName[ven]=Ndangulo ya siva ya mubvumo
+GenericName[wa]=Contrôle do sierveu di sons
+GenericName[xh]=Ulawulo Lomncedisi Wesandi
+GenericName[zh_CN]=声音服务器控制
+GenericName[zh_HK]=聲音伺服器控制器
+GenericName[zh_TW]=聲音伺服器控制器
+GenericName[zu]=Ukulawila Lomlekeleli Womsindo
+Exec=artscontrol -caption "%c"
+Icon=artscontrol
+Type=Application
+Terminal=false
+X-DCOP-ServiceType=Multi
+DocPath=artsbuilder/index.html
+OnlyShowIn=KDE;
+Categories=Qt;KDE;AudioVideo;X-KDE-More;
diff --git a/arts/tools/artscontrol.rc b/arts/tools/artscontrol.rc
new file mode 100644
index 00000000..5c1c0368
--- /dev/null
+++ b/arts/tools/artscontrol.rc
@@ -0,0 +1,17 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="artscontrol" version="3">
+<MenuBar>
+ <Menu name="view"><text>&amp;View</text>
+ <Action name="artssupport_view_scopeview"/>
+ <Action name="artssupport_view_audiomanager"/>
+ <Action name="artssupport_view_artsstatus"/>
+ <Action name="artssupport_view_midimanager"/>
+ <Action name="artssupport_view_environment"/>
+ <Action name="artssupport_view_mediatypes"/>
+ <Action name="view_freeverb"/>
+ <Action name="old_volume_display"/>
+ <Separator />
+ <Action name="quit_artscontrol"/>
+ </Menu>
+</MenuBar>
+</kpartgui>
diff --git a/arts/tools/artscontrolapplet.cpp b/arts/tools/artscontrolapplet.cpp
new file mode 100644
index 00000000..a178c2d3
--- /dev/null
+++ b/arts/tools/artscontrolapplet.cpp
@@ -0,0 +1,163 @@
+/***************************************************************************
+ artscontrolapplet.cpp - description
+ -------------------
+ begin : Don Jan 30 20:42:53 CET 2003
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 <kglobal.h>
+#include <klocale.h>
+#include <kconfig.h>
+#include <kapplication.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+#include <qcursor.h>
+#include <qtimer.h>
+
+#include "artscontrolapplet.h"
+#include "artscontrolapplet_private.h"
+
+extern "C"
+{
+ KDE_EXPORT KPanelApplet* init( QWidget *parent, const QString configFile)
+ {
+ KGlobal::locale()->insertCatalogue("artscontrol");
+ return new ArtsControlApplet(configFile, KPanelApplet::Normal,
+ KPanelApplet::About /*| KPanelApplet::Help | KPanelApplet::Preferences*/,
+ parent, "artscontrolapplet");
+ }
+}
+
+ArtsControlApplet::ArtsControlApplet(const QString& configFile, Type type, int actions, QWidget *parent, const char *name) : KPanelApplet(configFile, type, actions, parent, name)
+{
+ //kdDebug()<<"ArtsControlApplet::ArtsControlApplet( const QString& "<<configFile<<", Type "<<type<<", int "<<actions<<", QWidget* "<<parent<<", const char* "<<name<<" )"<<endl;
+ // Get the current application configuration handle
+ ksConfig = config();
+
+ p = new ArtsControlAppletPrivate( this );
+ if( !p->barts ) KMessageBox::information(0, i18n("Something with the ArtsServer went wrong. You probably need to restart aRts and then reload this applet."));
+ setCustomMenu(p->menu);
+
+ p->layout = new QBoxLayout( this, QBoxLayout::LeftToRight );
+ p->layout->setSpacing( 2 );
+
+ if( p->barts ) {
+ p->vu = Arts::StereoVolumeControlGui( p->volume );
+ p->vu.label().fontsize( 8 );
+ p->vuw = new KArtsWidget( p->vu, this );
+ p->vuw->setMinimumSize( 16,16 );
+ p->layout->addWidget( p->vuw );
+ }
+
+ p->layout->activate();
+
+ QTimer::singleShot( 100, this, SLOT( supdatelayout() ) );
+
+kdDebug()<<"ArtsControlApplet::ArtsControlApplet() finished."<<endl;
+}
+
+ArtsControlApplet::~ArtsControlApplet() {
+kdDebug()<<k_funcinfo<<endl;
+}
+
+#include <kaboutdata.h>
+#include <kaboutapplication.h>
+
+void ArtsControlApplet::about() {
+ KAboutData about( "artscontrolapplet", I18N_NOOP( "aRts Control Applet" ), "0.5",
+ I18N_NOOP( "A kickerapplet to control aRts." ),
+ KAboutData::License_GPL, I18N_NOOP( "(c) 2003 by Arnold Krille" ) );
+ about.addAuthor( "Arnold Krille", I18N_NOOP( "Author of the Applet" ), "arnold@arnoldarts.de" );
+ about.addCredit( "Stefan Westerfeld", I18N_NOOP( "Thanks for creating aRts!" ) );
+ KAboutApplication a( &about, this );
+ a.exec();
+}
+
+void ArtsControlApplet::help() {
+kdDebug()<<k_funcinfo<<endl;
+}
+
+void ArtsControlApplet::preferences() {
+kdDebug()<<k_funcinfo<<endl;
+}
+
+int ArtsControlApplet::widthForHeight( int h ) const {
+kdDebug()<<"ArtsControlApplet::widthForHeight( int "<<h<<" )"<<endl;
+ return p->layout->sizeHint().width();
+}
+
+int ArtsControlApplet::heightForWidth( int w ) const {
+kdDebug()<<"ArtsControlApplet::heightForWidth( int "<<w<<" )"<<endl;
+ return p->layout->sizeHint().height();
+}
+
+void ArtsControlApplet::resizeEvent( QResizeEvent * /*_Event*/ ) {
+ kdDebug()<<"ArtsControlApplet::resizeEvent( QResizeEvent * )"<<endl;
+}
+
+void ArtsControlApplet::mousePressEvent( QMouseEvent* ev ) {
+ //kdDebug()<<"ArtsControlApplet::mousePressEvent( QMouseEvent* "<<ev<<" )"<<endl;
+ if ( Qt::RightButton == ev->button() /*|| Qt::LeftButton == ev->button()*/ )
+ p->menu->exec( QCursor::pos() );
+}
+
+void ArtsControlApplet::positionChange( Position ) {
+ kdDebug() << k_funcinfo << endl;
+ resetLayout();
+}
+
+void ArtsControlApplet::resetLayout() {
+kdDebug()<<k_funcinfo<<" position()="<<position()<<endl;
+ switch ( position() )
+ {
+ case pTop:
+ case pBottom:
+ p->layout->setDirection( QBoxLayout::LeftToRight );
+ if ( p->barts ) p->vu.direction( Arts::LeftToRight );
+ break;
+ case pRight:
+ case pLeft:
+ p->layout->setDirection( QBoxLayout::TopToBottom );
+ if ( p->barts ) p->vu.direction( Arts::TopToBottom );
+ break;
+ default: break;
+ }
+}
+
+void ArtsControlAppletPrivate::SVinline() {
+kdDebug() << k_funcinfo << endl;
+ if ( !svinline ) {
+ svinline = new FFTScopeView( arts->server(), _parent );
+ svinline->setMargin( 2 ); svinline->setLineWidth( 2 ); svinline->setFrameStyle( QFrame::Panel|QFrame::Sunken );
+ connect( svinline, SIGNAL( closed() ), this, SLOT( SVinline() ) );
+ layout->addWidget( svinline );
+ } else {
+ delete svinline;
+ svinline = 0;
+ }
+ _parent->supdatelayout();
+}
+
+void ArtsControlAppletPrivate::moreBars() { vu.left().count( vu.left().count()+10 ); vu.right().count( vu.right().count()+10 ); }
+void ArtsControlAppletPrivate::lessBars() { vu.left().count( vu.left().count()-10 ); vu.right().count( vu.right().count()-10 ); }
+
+void ArtsControlAppletPrivate::styleNormalBars() { vu.left().style( Arts::lmNormalBars ); vu.right().style( Arts::lmNormalBars ); _parent->supdatelayout(); }
+void ArtsControlAppletPrivate::styleFireBars() { vu.left().style( Arts::lmFireBars ); vu.right().style( Arts::lmFireBars ); _parent->supdatelayout(); }
+void ArtsControlAppletPrivate::styleLineBars() { vu.left().style( Arts::lmLineBars ); vu.right().style( Arts::lmLineBars ); _parent->supdatelayout(); }
+void ArtsControlAppletPrivate::styleLEDs() { vu.left().style( Arts::lmLEDs ); vu.right().style( Arts::lmLEDs ); _parent->supdatelayout(); }
+void ArtsControlAppletPrivate::styleAnalog() { vu.left().style( Arts::lmAnalog ); vu.right().style( Arts::lmAnalog ); _parent->supdatelayout(); }
+void ArtsControlAppletPrivate::styleSmall() { vu.left().style( Arts::lmSmall ); vu.right().style( Arts::lmSmall ); _parent->supdatelayout(); }
+
+// vim: sw=4 ts=4
+#include "artscontrolapplet.moc"
+#include "artscontrolapplet_private.moc"
diff --git a/arts/tools/artscontrolapplet.desktop b/arts/tools/artscontrolapplet.desktop
new file mode 100644
index 00000000..d43517f7
--- /dev/null
+++ b/arts/tools/artscontrolapplet.desktop
@@ -0,0 +1,109 @@
+[Desktop Entry]
+Type=Plugin
+Comment=Control the aRts sound server
+Comment[ar]=تحكّم بخادم الصوت aRts
+Comment[bg]=Управление на аудио сървъра aRts
+Comment[bn]=আর্ট্‌স্ সাউন্ড সার্ভার নিয়ন্ত্রণ করুন
+Comment[br]=A ren servijer klevet aRts
+Comment[bs]=Kontrolišite aRts server zvuka
+Comment[ca]=Control del servidor de so aRts
+Comment[cs]=Ovládání zvukového serveru aRts
+Comment[da]=Kontrollér aRts-lydserveren
+Comment[de]=Kontrolle über den aRts-Soundserver
+Comment[el]=Διαχείριση του εξυπηρετητή ήχου aRts
+Comment[eo]=Stiru la aRts-sonservilon
+Comment[es]=Controla el servidor de sonido aRts
+Comment[et]=aRts heliserveri juhtimine
+Comment[eu]=Kontrolatu aRts soinu-zerbitzaria
+Comment[fa]=کنترل کارساز صدای aRts
+Comment[fi]=Hallitse aRts-äänipalvelinta
+Comment[fr]=Contrôlez le serveur de son aRts
+Comment[ga]=Rialaigh freastalaí fuaime aRts
+Comment[gl]=Controla o servidor de son aRts
+Comment[he]=שליטה בשרת הצליל של aRts
+Comment[hu]=Az aRts hangszolgáltatás kezelése
+Comment[is]=Stjórnaðu aRts hljóðþjóninum
+Comment[it]=Controlla il server sonoro aRts
+Comment[ja]=aRts サウンドサーバを制御
+Comment[kk]=aRts дыбыс серверін басқару
+Comment[km]=បញ្ជា​ម៉ាស៊ីន​បម្រើ​សំឡេង aRts
+Comment[ko]=aRts 소리 서버를 조정합니다
+Comment[lt]=Valdyti aRts garsų serverį
+Comment[mk]=Го контролира звучниот сервер aRts
+Comment[nb]=Styr lydtjeneren aRts
+Comment[nds]=aRts-Klangserverkuntrull
+Comment[ne]=aRts ध्वनि सर्भर नियन्त्रण गर्नुहोस्
+Comment[nl]=Bedien de aRts geluidsserver
+Comment[nn]=Styr aRts-lydtenaren
+Comment[pl]=Sterowanie serwerem dźwięku aRts
+Comment[pt]=Controlar o servidor de som aRts
+Comment[pt_BR]=Controlar o servidor de som aRTs
+Comment[ru]=Управление звуковым сервером aRts
+Comment[sk]=Ovládanie zvukového servera aRts
+Comment[sl]=Nadzorujte zvočni strežnik aRts
+Comment[sr]=Контролише aRts звучни сервер
+Comment[sr@Latn]=Kontroliše aRts zvučni server
+Comment[sv]=Styr ljudservern aRts
+Comment[th]=ควบคุมเซิร์ฟเวอร์เสียง aRTs
+Comment[tr]=Arts ses sunucusunu konrtol edin
+Comment[uk]=Керує звуковим сервером aRts
+Comment[zh_CN]=控制 aRts 声音服务器
+Comment[zh_HK]=控制 aRts 聲音伺服器
+Comment[zh_TW]=控制 aRts 聲音伺服器
+Name=aRts Control
+Name[ar]=التحكم بـ aRts
+Name[bn]=আর্ট্‌স নিয়ন্ত্রণ
+Name[br]=Renadur aRts
+Name[bs]=aRts kontrola
+Name[ca]=Control d'aRts
+Name[cs]=Ovládání programu aRts
+Name[cy]=Rheoli aRts
+Name[da]=aRts-kontrol
+Name[de]=aRts-Steuerung
+Name[el]=Έλεγχος aRts
+Name[eo]=aRts-stirilo
+Name[es]=Control de aRts
+Name[et]=aRtsi juhtimine
+Name[eu]=aRts-en kontrola
+Name[fa]=کنترل aRts
+Name[fi]=aRts-hallinta
+Name[ga]=Rialú aRts
+Name[gl]=Control do aRts
+Name[hi]=एआरटीएस नियंत्रण
+Name[hu]=aRts vezérlő
+Name[is]=Stjórnborð aRts
+Name[it]=Controllo di aRts
+Name[ja]=aRts コントロール
+Name[kk]=aRts басқару құралы
+Name[km]=ឧបករណ៍​បញ្ជា aRts
+Name[ko]=aRts 설정
+Name[lt]=aRts valdymas
+Name[mk]=Контрола на aRts
+Name[nb]=aRts-kontrollverktøy
+Name[nds]=aRts-Kuntrull
+Name[ne]=aRts नियन्त्रण
+Name[nl]=aRts bediening
+Name[nn]=aRts-kontroll
+Name[pa]=aRts ਕੰਟਰੋਲ
+Name[pl]=Sterowanie aRts
+Name[pt]=Controlo do aRts
+Name[pt_BR]=Controle do aRts
+Name[ro]=Control aRts
+Name[ru]=Утилита управления aRts
+Name[sk]=Ovládanie aRts
+Name[sl]=Nadzor aRts
+Name[sr]=Контрола aRts-а
+Name[sr@Latn]=Kontrola aRts-a
+Name[sv]=Arts-kontroll
+Name[ta]=aRts கட்டுப்பாடு
+Name[tg]=aRts Идоракунӣ
+Name[tr]=aRts Kontrol
+Name[uk]=Керування aRts
+Name[uz]=aRts boshqaruvi
+Name[uz@cyrillic]=aRts бошқаруви
+Name[zh_CN]=aRts 控制
+Name[zh_HK]=aRts 控制器
+Name[zh_TW]=aRts 控制
+X-KDE-Library=libartscontrolapplet
+Icon=artscontrol
+OnlyShowIn=KDE;
diff --git a/arts/tools/artscontrolapplet.h b/arts/tools/artscontrolapplet.h
new file mode 100644
index 00000000..09ff15fe
--- /dev/null
+++ b/arts/tools/artscontrolapplet.h
@@ -0,0 +1,132 @@
+/***************************************************************************
+ artscontrolapplet.h - description
+ -------------------
+ begin : Don Jan 30 20:42:53 CET 2003
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 ARTSCONTROLAPPLET_H
+#define ARTSCONTROLAPPLET_H
+
+#ifdef HAVE_CONFIG_H
+ #include <config.h>
+#endif
+
+#include <kpanelapplet.h>
+#include <qstring.h>
+#include <kconfig.h>
+
+class ArtsControlAppletPrivate;
+
+class ArtsControlApplet : public KPanelApplet
+{
+ Q_OBJECT
+public:
+ /**
+ * Construct a @ref KPanelApplet just like any other widget.
+ *
+ * @param configFile The configFile handed over in the factory function.
+ * @param Type The applet @ref type().
+ * @param actions Standard RMB menu actions supported by the applet (see @ref action() ).
+ * @param parent The pointer to the parent widget handed over in the factory function.
+ * @param name A Qt object name for your applet.
+ **/
+ ArtsControlApplet(const QString& configFile, Type t = Normal, int = 0,
+ QWidget * = 0, const char * = 0);
+ /** destructor */
+ ~ArtsControlApplet();
+ /**
+ * Retrieve a suggested width for a given height.
+ *
+ * Every applet should reimplement this function.
+ *
+ * Depending on the panel orientation the height (horizontal panel) or the
+ * width (vertical panel) of the applets is fixed.
+ * The exact values of the fixed size component depend on the panel size.
+ *
+ * On a horizontal panel the applet height is fixed, the panel will
+ * call @ref widthForHeight(int height) with @p height
+ * equal to 'the fixed applet height'
+ * when laying out the applets.
+ *
+ * The applet can now choose the other size component (width)
+ * based on the given height.
+ *
+ * The width you return is granted.
+ **/
+ int widthForHeight( int ) const;
+ /**
+ * @return A suggested height for a given width.
+ *
+ * Every applet should reimplement this function.
+ *
+ * Depending on the panel orientation the height (horizontal panel) or the
+ * width (vertical panel) of the applets is fixed.
+ * The exact values of the fixed size component depend on the panel size.
+ *
+ * On a vertical panel the applet width is fixed, the panel will
+ * call @ref heightForWidth(int width) with @p width
+ * equal to 'the fixed applet width'
+ * when laying out the applets.
+ *
+ * The applet can now choose the other size component (height)
+ * based on the given width.
+ *
+ * The height you return is granted.
+ **/
+ int heightForWidth( int ) const;
+ /**
+ * Is called when the user selects "About" from the applets RMB menu.
+ * Reimplement this function to launch a about dialog.
+ *
+ * Note that this is called only when your applet supports the About action.
+ * See @ref Action and @ref KPanelApplet().
+ **/
+ void about();
+ /**
+ * Is called when the user selects "Help" from the applets RMB menu.
+ * Reimplement this function to launch a manual or help page.
+ *
+ * Note that this is called only when your applet supports the Help action.
+ * See @ref Action and @ref KPanelApplet().
+ **/
+ void help();
+ /**
+ * Is called when the user selects "Preferences" from the applets RMB menu.
+ * Reimplement this function to launch a preferences dialog or kcontrol module.
+ *
+ * Note that this is called only when your applet supports the preferences action.
+ * See @ref Action and @ref KPanelApplet().
+ **/
+ void preferences();
+
+protected:
+
+ void resizeEvent( QResizeEvent* );
+
+ virtual void mousePressEvent( QMouseEvent* );
+
+ void positionChange( Position );
+
+public slots:
+ void supdatelayout() { emit updateLayout(); }
+
+protected slots:
+ void resetLayout();
+
+private:
+ KConfig *ksConfig;
+ ArtsControlAppletPrivate *p;
+};
+
+#endif
diff --git a/arts/tools/artscontrolapplet_private.h b/arts/tools/artscontrolapplet_private.h
new file mode 100644
index 00000000..044a156b
--- /dev/null
+++ b/arts/tools/artscontrolapplet_private.h
@@ -0,0 +1,122 @@
+/***************************************************************************
+ artscontrolapplet_private.h - description
+ -------------------
+ begin : Don Jan 30 2003
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 ARTSCONTROLAPPLET_PRIVATE_H
+#define ARTSCONTROLAPPLET_PRIVATE_H
+
+#include "artscontrolapplet.h"
+
+#include <qobject.h>
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qslider.h>
+#include <qpushbutton.h>
+#include <kpopupmenu.h>
+#include <kaction.h>
+#include <qfont.h>
+
+#include <kartsserver.h>
+#include <kartsdispatcher.h>
+#include <kartsfloatwatch.h>
+#include <kartswidget.h>
+#include <artsflow.h>
+#include <connect.h>
+#include <artsmoduleseffects.h>
+#include <kdelibs_export.h>
+
+#include "artsactions.h"
+
+#include "fftscopeview.h"
+
+class VolumeSlider;
+
+class KDE_EXPORT ArtsControlAppletPrivate : public QObject {
+ Q_OBJECT
+private:
+ ArtsControlApplet *_parent;
+ ArtsActions* _artsactions;
+public:
+ QBoxLayout *layout;
+ KArtsServer *arts;
+ KArtsDispatcher *dispatcher;
+ Arts::StereoVolumeControl volume;
+ bool barts, bInUpdate;
+ FFTScopeView *svinline;
+
+ KArtsWidget *vuw;
+ Arts::StereoVolumeControlGui vu;
+
+ KPopupMenu *menu;
+ KAction *_showSV, *_showSVinline, *_showAM, *_showArtsStatus, *_showMidiManager, *_showEnvironment, *_showMediaTypes, *_moreBars, *_lessBars;
+ KAction *_styleNormalBars, *_styleFireBars, *_styleLineBars, *_styleLEDs, *_styleAnalog, *_styleSmall;
+
+ ArtsControlAppletPrivate( ArtsControlApplet *parent )
+ : QObject(parent)
+ , _parent( parent )
+ , barts( false )
+ , bInUpdate( false )
+ {
+ arts = new KArtsServer( 0 );
+ dispatcher = new KArtsDispatcher( 0 );
+ if( ! arts->server().isNull() ) barts = true;
+ if( barts ) volume = arts->server().outVolume();
+ svinline=0;
+
+ _artsactions = new ArtsActions( arts, 0, parent );
+
+ menu = new KPopupMenu( 0 );
+ _showSV = _artsactions->actionScopeView();
+ _showSV->plug( menu );
+ _showSVinline = new KAction( i18n( "Toggle &Inline FFT Scope" ), "artscontrol", KShortcut(), this, SLOT( SVinline() ), this );
+ _showSVinline->plug( menu );
+ _showAM = _artsactions->actionAudioManager();
+ _showAM->plug( menu );
+ _showArtsStatus = _artsactions->actionArtsStatusView();
+ _showArtsStatus->plug( menu );
+ _showMidiManager = _artsactions->actionMidiManagerView();
+ _showMidiManager->plug( menu );
+ _showEnvironment = _artsactions->actionEnvironmentView();
+ _showEnvironment->plug( menu );
+ _showMediaTypes = _artsactions->actionMediaTypesView();
+ _showMediaTypes->plug( menu );
+ menu->insertSeparator();
+ menu->insertItem( i18n( "VU-Style" ), _artsactions->stylemenu() );
+ connect( _artsactions, SIGNAL( styleNormal() ), this, SLOT( styleNormalBars() ) );
+ connect( _artsactions, SIGNAL( styleFire() ), this, SLOT( styleFireBars() ) );
+ connect( _artsactions, SIGNAL( styleLine() ), this, SLOT( styleLineBars() ) );
+ connect( _artsactions, SIGNAL( styleLED() ), this, SLOT( styleLEDs() ) );
+ connect( _artsactions, SIGNAL( styleAnalog() ), this, SLOT( styleAnalog() ) );
+ connect( _artsactions, SIGNAL( styleSmall() ), this, SLOT( styleSmall() ) );
+ }
+ ~ArtsControlAppletPrivate() {
+ if ( svinline ) SVinline();
+ }
+public slots:
+ void SVinline(); // ScopeView inline
+ void moreBars();
+ void lessBars();
+ void styleNormalBars();
+ void styleFireBars();
+ void styleLineBars();
+ void styleLEDs();
+ void styleAnalog();
+ void styleSmall();
+};
+
+// vim: sw=4 ts=4
+#endif
diff --git a/arts/tools/artsmidimanagerview.rc b/arts/tools/artsmidimanagerview.rc
new file mode 100644
index 00000000..6bd743e6
--- /dev/null
+++ b/arts/tools/artsmidimanagerview.rc
@@ -0,0 +1,9 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="artsmidimanager.rc" version="1">
+<MenuBar>
+ <Menu name="add"><text>&amp;Add</text>
+ <Action name="add_oss_midi_port"/>
+ <Action name="add_arts_midi_output"/>
+ </Menu>
+</MenuBar>
+</kpartgui>
diff --git a/arts/tools/audiomanager.cpp b/arts/tools/audiomanager.cpp
new file mode 100644
index 00000000..d250e37f
--- /dev/null
+++ b/arts/tools/audiomanager.cpp
@@ -0,0 +1,198 @@
+/*
+
+ Copyright (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+ 2003 Arnold Krille
+ arnold@arnoldarts.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "audiomanager.h"
+#include "choosebusdlg.h"
+
+#include <qtimer.h>
+#include <qlayout.h>
+
+#include <klocale.h>
+#include <klistview.h>
+#include <kiconloader.h>
+
+using namespace std;
+
+/*
+ * as this is an 1:1 port of an old arts-0.3.4.1 artsbuilable visual widget,
+ * you'll see some porting artefacts, and it's not elegance itself ;)
+ */
+Gui_AUDIO_MANAGER::Gui_AUDIO_MANAGER( QWidget* parent, const char* name ) : Template_ArtsView( parent,name )
+{
+ this->setCaption( i18n( "Audio Manager" ) );
+ this->setIcon( MainBarIcon( "artsaudiomanager", 32 ) );
+ //printf("constructor\n");
+ ParentWidget = 0;
+ listview = 0;
+ inDialog = false;
+ proxy = new GuiAudioManagerProxy(this);
+
+ AudioManager = Arts::Reference("global:Arts_AudioManager");
+ changes = AudioManager.changes()-1;
+ setParent(this,0);
+ tick();
+ show();
+
+ QTimer *updatetimer = new QTimer(this);
+ updatetimer->start(500);
+ QObject::connect(updatetimer,SIGNAL(timeout()),this,SLOT(tick()));
+}
+
+Gui_AUDIO_MANAGER::~Gui_AUDIO_MANAGER()
+{
+ if(listview) delete listview;
+ delete proxy;
+}
+
+#if 0
+void Gui_AUDIO_MANAGER::widgetDestroyed(QWidget *widget)
+{
+ assert(widget == listview);
+ listview = 0;
+}
+#endif
+
+void Gui_AUDIO_MANAGER::setParent(QWidget *parent, QBoxLayout * /*layout*/)
+{
+/************************************************************************
+ * From Gui_INSTRUMENT_MAPPER:
+ *
+ * I am still not sure wether this kind of putting yourself into a parent
+ * widget (with own layout etc.) is a good idea (there may not even be
+ * a singe call to setParent, because there is no parent).
+ *
+ * But the "how to write aRts widgets"-stuff will need some experiments,
+ * so lets try that method...
+ *
+ * ----
+ *
+ * But perhaps here (since only one widget Listview is used) something
+ * else would be appropriate. Check that. FIXME
+ ************************************************************************/
+
+ QVBoxLayout *mainlayout = new QVBoxLayout(parent);
+ /*QHBoxLayout *contentslayout = new QHBoxLayout;*/
+
+// list
+
+ listview = new KListView(parent);
+
+ listview->addColumn(i18n("Title"),175);
+ listview->addColumn(i18n("Type"),50);
+ listview->addColumn(i18n("Bus"),75);
+
+ listview->setMinimumSize(300,100);
+
+ QObject::connect(listview,SIGNAL(executed(QListViewItem *)),proxy,
+ SLOT(edit(QListViewItem *)));
+
+ mainlayout->addWidget(listview);
+
+ mainlayout->activate();
+ //mainlayout->freeze();
+ ParentWidget = parent;
+}
+
+void Gui_AUDIO_MANAGER::tick()
+{
+ unsigned long newChanges = AudioManager.changes();
+ if(inDialog) return;
+ if(changes == newChanges) return;
+
+ changes = newChanges;
+
+ listview->clear();
+ vector<Arts::AudioManagerInfo> *acs = AudioManager.clients();
+
+ unsigned long c;
+ for(c=0;c<acs->size();c++)
+ {
+ //char status[2][10] = {"init","running"};
+ QString description = QString::fromUtf8( (*acs)[c].title.c_str() );
+ QString type;
+ if((*acs)[c].direction == Arts::amPlay)
+ type = i18n("play");
+ else
+ type = i18n("record");
+ QString destination = QString::fromUtf8( (*acs)[c].destination.c_str() );
+ long ID = (*acs)[c].ID;
+
+ (void)new AudioManagerItem(listview, description, type, destination, ID);
+ }
+ delete acs;
+ //Widget->show();
+}
+
+void Gui_AUDIO_MANAGER::edit(QListViewItem *item)
+{
+ AudioManagerItem *ai = (AudioManagerItem *)item;
+ ChooseBusDlg *cd = new ChooseBusDlg(0);
+ assert(cd);
+
+ inDialog = true;
+ int accept = cd->exec();
+ inDialog = false;
+
+ if( accept == QDialog::Accepted )
+ {
+ QString result = cd->result();
+ if(!result.isNull())
+ {
+ //lukas: I hope UTF-8 is OK here...
+ AudioManager.setDestination(ai->ID(),result.utf8().data());
+ // refresh:
+ changes = 0;
+ tick();
+ }
+ }
+ delete cd;
+}
+
+GuiAudioManagerProxy::GuiAudioManagerProxy(Gui_AUDIO_MANAGER *gim)
+{
+ this->gim = gim;
+}
+
+void GuiAudioManagerProxy::edit(QListViewItem *item)
+{
+ gim->edit(item);
+}
+
+AudioManagerItem::AudioManagerItem(QListView *parent, QString a,
+ QString b, QString c, long ID) :QListViewItem(parent,a,b,c)
+{
+ _ID = ID;
+}
+
+long AudioManagerItem::ID()
+{
+ return _ID;
+}
+
+AudioManagerItem::~AudioManagerItem()
+{
+ //
+}
+#include "audiomanager.moc"
+
+// vim: sw=4 ts=4
diff --git a/arts/tools/audiomanager.h b/arts/tools/audiomanager.h
new file mode 100644
index 00000000..202c454c
--- /dev/null
+++ b/arts/tools/audiomanager.h
@@ -0,0 +1,79 @@
+/*
+
+ Copyright (C) 1999 Stefan Westerfeld
+ stefan@space.twc.de
+ 2003 Arnold Krille
+ arnold@arnoldarts.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef _AUDIOMANAGER_H_
+#define _AUDIOMANAGER_H_
+
+#include <artsflow.h>
+#include <qwidget.h>
+#include <qlistview.h>
+
+#include "templateview.h"
+
+class KListView;
+class GuiAudioManagerProxy;
+class QBoxLayout;
+
+class Gui_AUDIO_MANAGER : public Template_ArtsView
+{
+ Q_OBJECT
+protected:
+ QWidget *ParentWidget;
+ KListView *listview;
+ GuiAudioManagerProxy *proxy;
+ Arts::AudioManager AudioManager;
+
+ bool inDialog;
+ unsigned long changes;
+ int x,y;
+public:
+ Gui_AUDIO_MANAGER( QWidget* =0, const char* =0 );
+ ~Gui_AUDIO_MANAGER();
+
+ void setParent(QWidget *Parent,QBoxLayout *layout);
+
+ void edit(QListViewItem *item);
+public slots:
+ void tick();
+};
+
+class GuiAudioManagerProxy :public QObject {
+ Q_OBJECT
+ Gui_AUDIO_MANAGER *gim;
+public:
+ GuiAudioManagerProxy(Gui_AUDIO_MANAGER *gim);
+public slots:
+ void edit(QListViewItem *item);
+};
+
+class AudioManagerItem : public QListViewItem {
+ long _ID;
+public:
+ AudioManagerItem(QListView *parent, QString a, QString b,
+ QString c, long ID);
+ ~AudioManagerItem();
+
+ long ID();
+};
+
+#endif
diff --git a/arts/tools/choosebusdlg.cpp b/arts/tools/choosebusdlg.cpp
new file mode 100644
index 00000000..77e439ee
--- /dev/null
+++ b/arts/tools/choosebusdlg.cpp
@@ -0,0 +1,184 @@
+/*
+
+ Copyright (C) 1998 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "choosebusdlg.h"
+
+#include <artsflow.h>
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qlistbox.h>
+#include <qpushbutton.h>
+
+#include <kbuttonbox.h>
+#include <kseparator.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kstdguiitem.h>
+
+using namespace std;
+
+static void min_size(QWidget *w) {
+ w->setMinimumSize(w->sizeHint());
+}
+
+ChooseBusDlg::ChooseBusDlg(QWidget *parent)
+ : KDialog(parent,"X", TRUE)
+ , _newbusitemindex( -1 )
+{
+ setCaption(i18n("Choose Bus"));
+
+ QVBoxLayout *mainlayout = new QVBoxLayout(this);
+
+// caption label: "Synthesis running..."
+
+ mainlayout->addSpacing(5);
+ QLabel *captionlabel = new QLabel(this);
+ QFont labelfont(captionlabel->font());
+ labelfont.setPointSize(labelfont.pointSize()*3/2);
+ captionlabel->setFont(labelfont);
+ captionlabel->setText(QString(" ")+i18n("Available busses:")+QString(" "));
+ captionlabel->setAlignment(AlignCenter);
+ min_size(captionlabel);
+ mainlayout->addWidget(captionlabel);
+
+// hruler
+
+ mainlayout->addSpacing(5);
+ KSeparator *ruler2 = new KSeparator( KSeparator::HLine, this);
+ mainlayout->addWidget(ruler2);
+
+ mainlayout->addSpacing(5);
+
+// listwidget
+
+
+ listbox = new QListBox(this);
+ listbox->setMinimumSize(300,200);
+
+ Arts::AudioManager aman = Arts::Reference("global:Arts_AudioManager");
+
+ if(!aman.isNull())
+ {
+ vector<string> *destinations = aman.destinations();
+ unsigned long i;
+ for(i=0;i<destinations->size();i++)
+ listbox->insertItem((*destinations)[i].c_str());
+ delete destinations;
+ }
+ if( listbox->count() > 0 )
+ listbox->setCurrentItem( 0 );
+
+ mainlayout->addWidget(listbox);
+
+// hruler
+
+ mainlayout->addSpacing(5);
+ KSeparator *ruler = new KSeparator( KSeparator::HLine, this);
+ mainlayout->addWidget(ruler);
+ mainlayout->addSpacing(5);
+
+// new bus lineedit
+
+ QBoxLayout * layout2 = new QHBoxLayout( mainlayout );
+ //mainlayout->addLayout( layout2 );
+ QLabel * newbuslabel = new QLabel( i18n( "New bus:" ), this );
+ layout2->addWidget( newbuslabel );
+ lineedit = new KLineEdit( this );
+ connect( lineedit, SIGNAL( textChanged( const QString & ) ), SLOT( textChanged( const QString & ) ) );
+ layout2->addWidget( lineedit );
+
+// hruler
+
+ mainlayout->addSpacing(5);
+ KSeparator *ruler3 = new KSeparator( KSeparator::HLine, this);
+ mainlayout->addWidget(ruler3);
+
+ mainlayout->addSpacing(5);
+
+// buttons
+
+ mainlayout->addSpacing(5);
+ QHBoxLayout *buttonlayout = new QHBoxLayout( mainlayout );
+ //mainlayout->addLayout(buttonlayout);
+ mainlayout->addSpacing(5);
+
+ buttonlayout->addSpacing(5);
+ KButtonBox *bbox = new KButtonBox(this);
+
+ bbox->addButton(KStdGuiItem::help(), this, SLOT( help() ));
+ bbox->addStretch(1);
+
+ QPushButton * okbutton = bbox->addButton(KStdGuiItem::ok());
+ okbutton->setDefault( true );
+ connect( okbutton, SIGNAL( clicked() ), SLOT(accept() ) );
+
+ QButton *cancelbutton = bbox->addButton(KStdGuiItem::cancel());
+ connect( cancelbutton, SIGNAL( clicked() ), SLOT(reject() ) );
+
+ bbox->layout();
+
+ buttonlayout->addWidget(bbox);
+ buttonlayout->addSpacing(5);
+
+ mainlayout->freeze();
+}
+
+QString ChooseBusDlg::result()
+{
+ if(listbox->currentItem() != -1)
+ {
+ return(listbox->text(listbox->currentItem()));
+ }
+ return(0);
+}
+
+void ChooseBusDlg::help()
+{
+ KApplication::kApplication()->invokeHelp("", "artsbuilder");
+}
+
+void ChooseBusDlg::textChanged( const QString & busname )
+{
+ if( ! busname.isEmpty() )
+ {
+ if( _newbusitemindex > -1 )
+ listbox->changeItem( busname, _newbusitemindex );
+ else
+ {
+ _newbusitemindex = listbox->count();
+ listbox->insertItem( busname, _newbusitemindex );
+ listbox->setCurrentItem( _newbusitemindex );
+ }
+ }
+ else
+ {
+ listbox->removeItem( _newbusitemindex );
+ listbox->setCurrentItem( _newbusitemindex - 1 );
+ _newbusitemindex = -1;
+ }
+}
+
+#include "choosebusdlg.moc"
+
+// vim: sw=4 ts=4
diff --git a/arts/tools/choosebusdlg.h b/arts/tools/choosebusdlg.h
new file mode 100644
index 00000000..40c48603
--- /dev/null
+++ b/arts/tools/choosebusdlg.h
@@ -0,0 +1,49 @@
+/*
+
+ Copyright (C) 1998 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef __CHOOSEBUSDLG_H_
+#define __CHOOSEBUSDLG_H_
+
+#include <kdialog.h>
+
+class QListBox;
+class KLineEdit;
+class QPushButton;
+
+class ChooseBusDlg :public KDialog {
+ Q_OBJECT
+ QListBox *listbox;
+ KLineEdit * lineedit;
+
+ int _newbusitemindex;
+public:
+ ChooseBusDlg(QWidget *parent);
+
+public slots:
+ QString result();
+ void help();
+
+protected slots:
+ void textChanged( const QString & );
+};
+#endif
+
+// vim: sw=4 ts=4
diff --git a/arts/tools/environmentview.cpp b/arts/tools/environmentview.cpp
new file mode 100644
index 00000000..e2074b5f
--- /dev/null
+++ b/arts/tools/environmentview.cpp
@@ -0,0 +1,171 @@
+/*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ <stefan@space.twc.de>
+ 2003 Matthias Kretz <kretz@kde.org>
+ 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "environmentview.h"
+#include <qdir.h>
+#include <qfile.h>
+#include <qpushbutton.h>
+
+#include <klistbox.h>
+#include <kartswidget.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <stdio.h>
+#include <fstream>
+#include <vector>
+
+#include <qlayout.h>
+
+#define DEFAULT_ENV_FILENAME "~/default.arts-env"
+
+using namespace Arts;
+using Environment::Container;
+using Environment::Item;
+
+class ItemView : public QListBoxText {
+public:
+ Item item;
+ KArtsWidget *widget;
+ ItemView(QListBox *listBox, Item item)
+ : QListBoxText(listBox), item(item), widget(0)
+ {
+ }
+ ~ItemView()
+ {
+ delete widget;
+ widget = 0;
+ printf("~ItemView()\n");
+ }
+ QString text() const {
+ return QString::fromLatin1(item._interfaceName().c_str());
+ }
+};
+
+EnvironmentView::EnvironmentView( Container container, QWidget* parent, const char* name ) : Template_ArtsView( parent,name ), container(container)
+{
+ this->setCaption( i18n( "Environment" ) );
+ this->setIcon( MainBarIcon( "artsenvironment", 32 ) );
+ QVBoxLayout* _layout = new QVBoxLayout( this );
+ _layout->setAutoAdd( true );
+ defaultEnvFileName = DEFAULT_ENV_FILENAME;
+ defaultEnvFileName.replace('~', QDir::homeDirPath());
+ listBox = new KListBox(this);
+ update();
+ connect(listBox,SIGNAL(executed(QListBoxItem*)),
+ this,SLOT(view(QListBoxItem*)));
+
+ QPushButton *mixerButton = new QPushButton(i18n("Add Mixer"), this);
+ connect(mixerButton, SIGNAL(clicked()), this, SLOT(addMixer()));
+
+ QPushButton *effectRackButton = new QPushButton(i18n("Add Effect Rack"), this);
+ connect(effectRackButton, SIGNAL(clicked()), this, SLOT(addEffectRack()));
+
+ QPushButton *delButton = new QPushButton(i18n("Delete Item"), this);
+ connect(delButton, SIGNAL(clicked()), this, SLOT(delItem()));
+
+ QPushButton *loadButton = new
+ QPushButton(i18n("Load %1").arg(DEFAULT_ENV_FILENAME), this);
+ connect(loadButton, SIGNAL(clicked()), this, SLOT(load()));
+
+ QPushButton *saveButton = new
+ QPushButton(i18n("Save %1").arg(DEFAULT_ENV_FILENAME), this);
+ connect(saveButton, SIGNAL(clicked()), this, SLOT(save()));
+ show();
+}
+
+void EnvironmentView::view(QListBoxItem *i)
+{
+ ItemView *iv = static_cast<ItemView*>(i);
+
+ if(!iv->widget)
+ {
+ GenericGuiFactory gf;
+ Widget w = gf.createGui(iv->item);
+ if(!w.isNull())
+ {
+ iv->widget = new KArtsWidget(w);
+ }
+ else
+ {
+ printf("no gui for %s\n",iv->text().ascii());
+ }
+ }
+ if(iv->widget)
+ iv->widget->show();
+}
+
+void EnvironmentView::addMixer()
+{
+ container.createItem("Arts::Environment::MixerItem");
+ update();
+}
+
+void EnvironmentView::addEffectRack()
+{
+ container.createItem("Arts::Environment::EffectRackItem");
+ update();
+}
+
+void EnvironmentView::delItem()
+{
+ int i = listBox->currentItem();
+ if(i < 0) return; /* nothing selected */
+
+ ItemView *iv = static_cast<ItemView*>(listBox->item(i));
+ container.removeItem(iv->item);
+ update();
+}
+
+void EnvironmentView::update()
+{
+ listBox->clear();
+
+ std::vector<Item> *items = container.items();
+ for(std::vector<Item>::iterator i = items->begin(); i != items->end(); i++)
+ (void)new ItemView(listBox, *i);
+ delete items;
+}
+
+void EnvironmentView::load()
+{
+ std::ifstream infile(QFile::encodeName(defaultEnvFileName).data());
+ std::string line;
+ std::vector<std::string> strseq;
+
+ while(getline(infile,line))
+ strseq.push_back(line);
+
+ defaultEnvironment().loadFromList(strseq);
+}
+
+void EnvironmentView::save()
+{
+ std::vector<std::string> *strseq;
+ strseq = defaultEnvironment().saveToList();
+
+ std::ofstream outfile(QFile::encodeName(defaultEnvFileName).data());
+ for(std::vector<std::string>::iterator i = strseq->begin(); i != strseq->end(); i++)
+ outfile << ( *i ) << std::endl;
+ delete strseq;
+}
+#include "environmentview.moc"
diff --git a/arts/tools/environmentview.h b/arts/tools/environmentview.h
new file mode 100644
index 00000000..535fcc98
--- /dev/null
+++ b/arts/tools/environmentview.h
@@ -0,0 +1,57 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ <stefan@space.twc.de>
+ 2003 Matthias Kretz <kretz@kde.org>
+ 2003 Arnold Krille <arnold@arnoldarts.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_ENVIRONMENT_VIEW_H
+#define ARTS_ENVIRONMENT_VIEW_H
+
+#include "artsmodules.h"
+
+#include "templateview.h"
+
+class QListBoxItem;
+class KListBox;
+
+class EnvironmentView : public Template_ArtsView {
+ Q_OBJECT
+protected:
+ Arts::Environment::Container container;
+ KListBox *listBox;
+ QString defaultEnvFileName;
+
+public:
+ EnvironmentView( Arts::Environment::Container container, QWidget* =0, const char* =0 );
+
+public slots:
+ void view(QListBoxItem *i);
+ void addMixer();
+ void addEffectRack();
+ void delItem();
+ void update();
+ void load();
+ void save();
+
+};
+
+Arts::Environment::Container defaultEnvironment();
+
+#endif /* ARTS_ENVIRONMENT_VIEW_H */
diff --git a/arts/tools/fftscopeview.cpp b/arts/tools/fftscopeview.cpp
new file mode 100644
index 00000000..cb4c2acc
--- /dev/null
+++ b/arts/tools/fftscopeview.cpp
@@ -0,0 +1,163 @@
+/*
+
+ Copyright ( C ) 2000-2001 Stefan Westerfeld
+ <stefan@space.twc.de>
+ 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ ( at your option ) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "fftscopeview.h"
+
+#include <qlayout.h>
+#include <qcursor.h>
+#include <qtimer.h>
+#include <kaction.h>
+#include <kpopupmenu.h>
+#include <kartswidget.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kiconloader.h>
+
+#include "artsactions.h"
+
+using namespace std;
+using namespace Arts;
+
+FFTScopeView::FFTScopeView( SimpleSoundServer server, QWidget* parent )
+ : Template_ArtsView( parent )
+ , server( server )
+ , scopeData( 0 )
+{
+kdDebug()<<k_funcinfo<<endl;
+ this->setCaption( i18n( "FFT Scope View" ) );
+ this->setIcon( MainBarIcon( "artsfftscope", 32 ) );
+ /*
+ create a stereo fft scope on the server and push it into the
+ effect chain
+ */
+ {
+ scopefx = DynamicCast( server.createObject( "Arts::StereoFFTScope" ) );
+ assert( !scopefx.isNull() );
+ scopefx.start();
+
+ // put it into the effect chain
+ effectID = server.outstack().insertBottom( scopefx,"FFT Scope" );
+ }
+
+ updateScopeData();
+ QBoxLayout * l = new QHBoxLayout( this );
+ l->setAutoAdd( TRUE );
+
+ for ( unsigned int i=0;i<scopeData->size();i++ )
+ {
+ LevelMeter tmp;
+ tmp.count( 20 );
+ scopeScales.push_back( tmp );
+ scopeDraw.push_back( 0.0 );
+ KArtsWidget *w = new KArtsWidget( tmp, this );
+ aw.push_back( w );
+ }
+
+ l->activate();
+ show();
+
+ updatetimer = new QTimer( this );
+ updatetimer->start( 100 );
+ connect( updatetimer,SIGNAL( timeout() ),this,SLOT( updateScope() ) );
+
+ _artsactions = new ArtsActions( 0, 0, this );
+ _moreBars = ArtsActions::actionMoreBars( this, SLOT( moreBars() ), 0 );
+ _lessBars = ArtsActions::actionLessBars( this, SLOT( lessBars() ), 0 );
+ _menu = new KPopupMenu( 0 );
+ _moreBars->plug( _menu ); _lessBars->plug( _menu );
+ _substyle = new KAction( i18n( "Substyle" ), "", KShortcut(), this, SLOT( substyle() ), this );
+ _substyle->plug( _menu );
+ _menu->insertItem( i18n("VU-Style"), _artsactions->stylemenu() );
+
+ connect( _artsactions, SIGNAL( styleNormal() ), this, SLOT( styleNormalBars() ) );
+ connect( _artsactions, SIGNAL( styleFire() ), this, SLOT( styleFireBars() ) );
+ connect( _artsactions, SIGNAL( styleLine() ), this, SLOT( styleLineBars() ) );
+ connect( _artsactions, SIGNAL( styleLED() ), this, SLOT( styleLEDs() ) );
+ connect( _artsactions, SIGNAL( styleAnalog() ), this, SLOT( styleAnalog() ) );
+ connect( _artsactions, SIGNAL( styleSmall() ), this, SLOT( styleSmall() ) );
+}
+
+FFTScopeView::~FFTScopeView() {
+kdDebug()<<"FFTScopeView::~FFTScopeView()"<<endl;
+ updatetimer->stop();
+ for ( int i=int( aw.size() )-1; i>=0; i-- ) { scopeScales[ i ].hide(); delete aw[ i ]; aw.pop_back(); scopeScales.pop_back(); }
+ server.outstack().remove( effectID );
+kdDebug()<<"FFTScopeView is gone..."<<endl;
+}
+
+void FFTScopeView::updateScopeData() {
+ if ( scopeData ) delete scopeData;
+ scopeData = scopefx.scope();
+}
+
+void FFTScopeView::updateScope() {
+ updateScopeData();
+
+ for ( unsigned int i=0;i<scopeData->size();i++ )
+ {
+// scopeDraw[ i ] /= 1.25;
+// if ( ( *scopeData )[ i ] > scopeDraw[ i ] ) scopeDraw[ i ] = ( *scopeData )[ i ];
+ scopeDraw[ i ] = ( *scopeData )[ i ];
+ scopeScales[ i ].invalue( scopeDraw[ i ] );
+ }
+}
+
+void FFTScopeView::mousePressEvent( QMouseEvent* ev ) {
+ if ( Qt::RightButton == ev->button() /*|| Qt::LeftButton == ev->button()*/ )
+ _menu->exec( QCursor::pos() );
+}
+
+void FFTScopeView::moreBars() {
+ int bars = scopeScales[ 0 ].count() + 10;
+ for ( unsigned int i=0;i<scopeData->size();i++ )
+ scopeScales[ i ].count( bars );
+}
+
+void FFTScopeView::lessBars() {
+ int bars = scopeScales[ 0 ].count() - 10;
+ for ( unsigned int i=0;i<scopeData->size();i++ )
+ scopeScales[ i ].count( bars );
+}
+
+void FFTScopeView::setStyle( Arts::LevelMeterStyle style ) {
+ for ( uint i=0; i<scopeScales.size(); i++ )
+ scopeScales[ i ].style( style );
+}
+
+void FFTScopeView::styleNormalBars() { setStyle( Arts::lmNormalBars ); }
+void FFTScopeView::styleFireBars() { setStyle( Arts::lmFireBars ); }
+void FFTScopeView::styleLineBars() { setStyle( Arts::lmLineBars ); }
+void FFTScopeView::styleLEDs() { setStyle( Arts::lmLEDs ); }
+void FFTScopeView::styleAnalog() { setStyle( Arts::lmAnalog ); }
+void FFTScopeView::styleSmall() { setStyle( Arts::lmSmall ); }
+
+#include <kinputdialog.h>
+
+void FFTScopeView::substyle() {
+ int _substyle = KInputDialog::getInteger( i18n("Substyle"), i18n("Please enter substyle:"), 0, 0, 10, 1, 0, this );
+ for ( unsigned int i=0; i<scopeData->size(); i++ )
+ scopeScales[ i ].substyle( _substyle );
+}
+
+#include "fftscopeview.moc"
+// vim: sw=4 ts=4
+
diff --git a/arts/tools/fftscopeview.h b/arts/tools/fftscopeview.h
new file mode 100644
index 00000000..d01b6c3c
--- /dev/null
+++ b/arts/tools/fftscopeview.h
@@ -0,0 +1,81 @@
+/*
+
+ Copyright ( C ) 2000-2001 Stefan Westerfeld
+ <stefan@space.twc.de>
+ 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ ( at your option ) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTSCONTROLLIB_FFTSCOPEVIEW_H
+#define ARTSCONTROLLIB_FFTSCOPEVIEW_H
+
+#include <artsmodules.h>
+#include <flowsystem.h>
+#include <kartsserver.h>
+
+#include "templateview.h"
+
+#include <artsgui.h>
+#include <kdelibs_export.h>
+
+class QTimer;
+class KPopupMenu;
+class KAction;
+class KArtsWidget;
+class ArtsActions;
+
+class KDE_EXPORT FFTScopeView : public Template_ArtsView {
+ Q_OBJECT
+protected:
+ Arts::StereoFFTScope scopefx;
+ Arts::SimpleSoundServer server;
+ long effectID;
+
+ std::vector<float> *scopeData;
+ std::vector<float> scopeDraw;
+ std::vector<Arts::LevelMeter> scopeScales;
+ std::vector<KArtsWidget*> aw;
+ QTimer *updatetimer;
+
+ ArtsActions* _artsactions;
+ KPopupMenu *_menu, *_stylemenu;
+ KAction *_moreBars, *_lessBars;
+ KAction *_styleNormalBars, *_styleFireBars, *_styleLineBars, *_styleLEDs, *_styleAnalog, *_styleSmall;
+ KAction *_substyle;
+
+ void mousePressEvent( QMouseEvent* );
+public:
+ void updateScopeData();
+ FFTScopeView( Arts::SimpleSoundServer server, QWidget* =0 );
+ ~FFTScopeView();
+
+public slots:
+ void updateScope();
+ void moreBars();
+ void lessBars();
+ void setStyle( Arts::LevelMeterStyle style );
+ void styleNormalBars();
+ void styleFireBars();
+ void styleLineBars();
+ void styleLEDs();
+ void styleAnalog();
+ void styleSmall();
+ void substyle();
+};
+
+// vim: sw=4 ts=4
+#endif
diff --git a/arts/tools/levelmeters.cpp b/arts/tools/levelmeters.cpp
new file mode 100644
index 00000000..3b2ca1ac
--- /dev/null
+++ b/arts/tools/levelmeters.cpp
@@ -0,0 +1,239 @@
+#include <qlayout.h>
+#include <qpainter.h>
+#include <qfontmetrics.h>
+#include <qptrlist.h>
+#include <kled.h>
+#include "levelmeters.h"
+
+const int PeakBar::peakMillis=1500;
+
+PeakBar::PeakBar(QWidget *parent)
+ : ACLevelMeter(parent)
+ , maxValue( 0.0f )
+ , minValue( 0.0f )
+{
+ clipped = false;
+ displayMinPeak= false;
+ horizontalMode= false;
+ currentValue= 0.0f;
+
+ lastValues.setAutoDelete( TRUE );
+
+ setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
+ setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred));
+ setBackgroundMode(NoBackground);
+ setMinimumSize(frameWidth()+7, 70);
+}
+
+void PeakBar::frameChanged() {
+ setMinimumSize(frameWidth()+7, 70);
+ QFrame::frameChanged();
+}
+
+QSize PeakBar::sizeHint() const {
+ return QSize(13, 250);
+}
+
+void PeakBar::checkMinMax() {
+ int mustRecheck= 0; // bool
+ Observation *o;
+
+ while ((o= lastValues.first()) && o->time.elapsed() > peakMillis) {
+ lastValues.removeFirst();
+ mustRecheck= 1;
+ }
+
+ if (mustRecheck) {
+ maxValue= 0.f;
+ minValue= 1.f;
+ clipped = false;
+ for (QPtrListIterator<Observation> it(lastValues); it.current(); ++it) {
+ float value= it.current()->value;
+ if (value>maxValue)
+ maxValue= value;
+ if (value<minValue)
+ minValue= value;
+ if (value > 1.f)
+ clipped = true;
+ }
+ }
+}
+
+void PeakBar::drawContents(QPainter *p)
+{
+ QRect size= contentsRect();
+
+ checkMinMax();
+
+ p->setBrush(clipped ? darkRed : darkBlue);
+ p->setPen(NoPen);
+ p->drawRect(size);
+
+ QRect bar= size;
+ p->setBrush(clipped ? red : blue);
+ if (horizontalMode) {
+ bar.setWidth((int)(bar.width()*currentValue));
+ } else {
+ int newHeight= (int)(bar.height()*currentValue);
+ bar.moveBy(0, bar.height()-newHeight);
+ bar.setHeight(newHeight);
+ }
+ p->drawRect(bar);
+
+ int y;
+ // TODO: if (horizontalMode)
+ if (displayMinPeak) {
+ y= frameWidth()+size.height()-((int)(size.height()*minValue));
+ p->setPen(white);
+ p->drawLine(frameWidth(), y, frameWidth()+size.width()-1, y);
+ }
+ y= frameWidth()+size.height()-((int)(size.height()*maxValue));
+ p->setPen(white);
+ p->drawLine(frameWidth(), y, frameWidth()+size.width()-1, y);
+}
+
+void PeakBar::setValue(float f) {
+ if (f > 1.f)
+ clipped = true;
+
+ currentValue= f;
+ if (f>=maxValue)
+ maxValue= f;
+ if (displayMinPeak && (f<=minValue))
+ minValue= f;
+
+ lastValues.append(new Observation(f));
+
+ repaint();
+}
+
+// -------------------------------------------------------------
+
+PeakLevelMeters::PeakLevelMeters(QWidget *parent):
+ StereoLevelMeter(parent), left(this), right(this), scaleView(this)
+{
+ QBoxLayout *layout= new QHBoxLayout(this);
+ layout->addWidget(&left);
+ // layout->setStretchFactor(&left, 0);
+ layout->addWidget(&right);
+ // layout->setStretchFactor(&right, 0);
+ layout->addWidget(&scaleView);
+ // layout->setStretchFactor(&scaleView, 0);
+ left.setLineWidth(2);
+ right.setLineWidth(2);
+ scaleView.setScaleMargins(right.frameWidth());
+ setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred));
+
+ setDbRange(36);
+}
+
+void PeakLevelMeters::setDbRange(int db) {
+ dbRange= db;
+ scaleView.setDbRange(db);
+}
+
+void PeakLevelMeters::setValues(float leftVal, float rightVal) {
+ float f= 1.0f+levelToDB(leftVal)/dbRange;
+ if (f<0.f) f= 0.f;
+ left.setValue(f);
+
+ f= 1.0f+levelToDB(rightVal)/dbRange;
+ if (f<0.f) f= 0.f;
+ right.setValue(f);
+}
+
+ScaleView::ScaleView(QWidget *parent): QFrame(parent) {
+ font.setPixelSize(10);
+ setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
+}
+
+void ScaleView::setDbRange(int db) {
+ dbRange= db;
+ repaint();
+}
+
+QSize ScaleView::sizeHint() const {
+ return QSize(QFontMetrics(font).width("-88")+8, 250);
+}
+
+void ScaleView::drawContents(QPainter *p) {
+ QRect size= contentsRect();
+ size.rTop()+= upperMargin;
+ size.rBottom()-= lowerMargin;
+
+ QFrame::drawContents(p); // clear background
+
+ int step=3;
+ while (dbRange/step*20>size.height())
+ step*=2;
+
+ /* float offset= ((float)size.height()*step)/dbRange, pos=0.f;
+ p->setPen(black);
+ p->drawLine(0, (, size.width()*3/5, y);*/
+
+ p->setPen(black);
+ p->setFont(font);
+ QString numStr;
+ for (int i=0; i<=dbRange; i++) {
+ int y= size.top()+(size.height()-1)*i/dbRange;
+ if (i%step==0) {
+ p->drawLine(0, y, 4, y);
+ numStr.setNum(-i);
+ p->drawText(8, y+5, numStr);
+ } else
+ p->drawLine(0, y, 2, y);
+ }
+}
+
+// -------------------------------------------------------------
+
+LedMeter::LedMeter(QWidget *parent, bool blueState) : ACLevelMeter(parent) {
+ setBackgroundColor(black);
+ QBoxLayout * l = new QVBoxLayout( this );
+ l->setAutoAdd(TRUE);
+ for(int i=0;i<12;i++) {
+ QColor c;
+ if(blueState)
+ c = blue;
+ else {
+ c = red;
+ if(i>=2) c = yellow;
+ if(i>=5) c = green;
+ }
+
+ // put each led in its own frame, since it seems to be broken
+ QFrame *lframe = new QFrame(this);
+ QBoxLayout *lfl = new QVBoxLayout( lframe );
+ lfl->setAutoAdd(TRUE);
+ leds[i] =
+ new KLed(c,KLed::Off, KLed::Sunken, KLed::Circular,lframe);
+ }
+}
+
+void LedMeter::setValue(float f)
+{
+ //printf("value %f\n",f);
+ for(int i=11;i>=0;i--)
+ {
+ if(f > 0.06) leds[i]->setState(KLed::On);
+ else leds[i]->setState(KLed::Off);
+ f /= 1.25;
+ }
+}
+
+// -------------------------------------------------------------
+
+StereoLedMeters::StereoLedMeters(QWidget *parent)
+ : StereoLevelMeter(parent), left(this), right(this)
+{
+ QBoxLayout *layout= new QHBoxLayout(this);
+ layout->addWidget(&left);
+ layout->addWidget(&right);
+}
+
+void StereoLedMeters::setValues(float leftVal, float rightVal) {
+ left.setValue(leftVal);
+ right.setValue(rightVal);
+}
+
+#include "levelmeters.moc"
diff --git a/arts/tools/levelmeters.h b/arts/tools/levelmeters.h
new file mode 100644
index 00000000..42ef0fae
--- /dev/null
+++ b/arts/tools/levelmeters.h
@@ -0,0 +1,168 @@
+ /*
+
+ Copyright (C) 2000 Hans Meine
+ <hans_meine@gmx.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_TOOLS_LEVELMETERS_H
+#define ARTS_TOOLS_LEVELMETERS_H
+
+#include <qobject.h>
+#include <qframe.h>
+#include <qdatetime.h>
+#include <qptrlist.h>
+#include <math.h>
+
+// helper functions
+const float LEVEL_MIN= 1.f/(1<<20); // minimal positive sample for 20 bit resolution
+inline float levelToDB(float level) {
+ if (level<LEVEL_MIN) level=LEVEL_MIN; // prevent from div by 0
+ return (6.f/log(2.f))*log(level);
+}
+
+inline float DBToLevel(float db) {
+ return exp(db/(log(2.f)/6.f));
+}
+
+/**
+ * Base class for a single volume / value bar.
+ */
+class ACLevelMeter : public QFrame {
+ Q_OBJECT
+public:
+ ACLevelMeter(QWidget *parent): QFrame(parent) {}
+public slots:
+ virtual void setValue(float f) = 0;
+};
+
+/**
+ * Base class for a pair of volume / value bars.
+ */
+class StereoLevelMeter : public QFrame {
+ Q_OBJECT
+public:
+ StereoLevelMeter(QWidget *parent): QFrame(parent) {}
+public slots:
+ virtual void setValues(float left, float right) = 0;
+};
+
+/**
+ * Special LevelMeter which remembers min and max of the last [peakMillis]
+ * milliseconds and displays a full bar with optional max/min markers.
+ */
+class PeakBar : public ACLevelMeter {
+ Q_OBJECT
+ bool clipped;
+
+protected:
+ static const int peakMillis; // how long do the peaks stay at their max?
+
+ class Observation {
+ public:
+ QTime time;
+ float value;
+ Observation(float aValue): value(aValue) { time.start(); }
+ };
+ QPtrList<Observation> lastValues;
+
+ float currentValue, maxValue, minValue;
+ void checkMinMax();
+
+ bool displayMinPeak;
+ bool horizontalMode;
+
+ void frameChanged();
+
+public:
+ PeakBar(QWidget *parent);
+
+ QSize sizeHint() const;
+
+ void drawContents(QPainter *p);
+ virtual void setValue(float f);
+};
+
+/**
+ * Special class which draws the Db scale with ticks, numbers and so on.
+ */
+class ScaleView : public QFrame {
+ Q_OBJECT
+protected:
+ QFont font;
+ int dbRange;
+ int upperMargin, lowerMargin;
+public:
+ ScaleView(QWidget *parent);
+ void setDbRange(int db);
+ void setScaleMargins(int margins) { upperMargin= margins; lowerMargin=margins; }
+ QSize sizeHint() const;
+ void drawContents(QPainter *p);
+};
+
+/**
+ * Reusable class which displays two volume bars (left/right) with a Db scale
+ * and clip indicators. Supports / will have a context menu with some display
+ * options like Db range, whether minimal values are also shown and others.
+ */
+class PeakLevelMeters : public StereoLevelMeter {
+ Q_OBJECT
+protected:
+ int dbRange;
+ PeakBar left, right;
+ ScaleView scaleView;
+
+public:
+ PeakLevelMeters(QWidget *parent);
+
+public slots:
+ void setValues(float leftVal, float rightVal);
+ void setDbRange(int db);
+};
+
+class KLed;
+
+/**
+ * Simple LevelMeter implementation with 12 KLeds.
+ * Shows them in green/yellow/red combi if not blueState set, in which
+ * case it's all blue. (Original artscontrol widget by stw.)
+ */
+class LedMeter : public ACLevelMeter {
+ Q_OBJECT
+protected:
+ KLed *leds[12];
+
+public:
+ LedMeter(QWidget *parent, bool blueState = false);
+ void setValue(float f);
+};
+
+/**
+ * A simple pair of LedMeters.
+ */
+class StereoLedMeters : public StereoLevelMeter {
+ Q_OBJECT
+protected:
+ LedMeter left, right;
+
+public:
+ StereoLedMeters(QWidget *parent);
+public slots:
+ void setValues(float left, float right);
+};
+
+#endif /* ARTS_TOOLS_LEVELMETERS_H */
diff --git a/arts/tools/main.cpp b/arts/tools/main.cpp
new file mode 100644
index 00000000..5961dfcd
--- /dev/null
+++ b/arts/tools/main.cpp
@@ -0,0 +1,211 @@
+/*
+
+ Copyright (C) 2000-2001 Stefan Westerfeld
+ <stefan@space.twc.de>
+ 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "main.h"
+#include "environmentview.h"
+#include "main.moc"
+#include "levelmeters.h"
+#include "midimanagerview.h"
+#include "audiomanager.h"
+#include "fftscopeview.h"
+#include "mediatypesview.h"
+#include "statusview.h"
+
+#include <objectmanager.h>
+#include <debug.h>
+#include <artsversion.h>
+#include <kartsfloatwatch.h>
+
+#include <qlabel.h>
+#include <qslider.h>
+#include <qpushbutton.h>
+#include <kaction.h>
+#include <kled.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+#include <kartsserver.h>
+
+#include <kstdaction.h>
+
+#include "kartswidget.h"
+
+using namespace std;
+using namespace Arts;
+
+void FreeVerbView::closeEvent(QCloseEvent *e) {
+ e->accept();
+ emit closed();
+}
+
+FreeVerbView::FreeVerbView(SimpleSoundServer server) : server(server) {
+ freeverb = DynamicCast(server.createObject("Arts::Synth_FREEVERB"));
+ arts_assert(!freeverb.isNull());
+ freeverb.start();
+
+ // put it into the effect chain
+ effectID = server.outstack().insertTop(freeverb,"FreeVerb");
+
+ GenericGuiFactory factory;
+ Widget gui = factory.createGui(freeverb);
+
+ QBoxLayout * l = new QHBoxLayout( this );
+ if(!gui.isNull())
+ l->add(new KArtsWidget(gui, this));
+ else
+ l->add(new QLabel(i18n("No GUI found for this effect."), this));
+ l->freeze();
+ show();
+}
+
+FreeVerbView::~FreeVerbView() {
+ // remove effect
+ server.outstack().remove(effectID);
+}
+
+
+VControl::VControl( KArtsServer* artsserver, QWidget *parent) : QFrame(parent)
+ , freeVerbView(0)
+ , server( artsserver )
+{
+
+ connect( server, SIGNAL( restartedServer() ), this, SLOT( initaRtsConnections() ) );
+/* if(server.isNull())
+ {
+ KMessageBox::error( 0, i18n("Connection to the soundserver failed - make sure that artsd is really running and that your kdelibs version is not older than kdemultimedia."));
+ exit(1);
+ }*/
+ boxLayout = new QHBoxLayout( this );
+
+ // 0 => 4.0
+ // 200 => 2.0
+ // 400 => 1.0
+ // 600 => 0.5
+ // 800 => 0.25
+ // 1000 => 0.125
+ // 1200 => 0.0 (forced)
+
+ svcguiw = new KArtsWidget( this );
+ boxLayout->addWidget( svcguiw );
+
+ boxLayout->activate();
+ show();
+
+ initaRtsConnections();
+}
+VControl::~VControl() {
+ if ( freeVerbView ) showFreeVerbView();
+}
+
+void VControl::useOldVolumeBar(int old) {
+/* delete stereoMeter;
+ if (old) {
+ stereoMeter= new StereoLedMeters(this);
+ } else {
+ stereoMeter= new PeakLevelMeters(this);
+ }
+ boxLayout->insertWidget(0, stereoMeter);
+ stereoMeter->show();*/
+ if ( old ) {
+ svcgui.left().substyle( 3 );
+ svcgui.right().substyle( 3 );
+ } else {
+ svcgui.left().substyle( 2 );
+ svcgui.right().substyle( 2 );
+ }
+}
+
+void VControl::showFreeVerbView() {
+ if(!freeVerbView) {
+ freeVerbView = new FreeVerbView(server->server());
+ connect(freeVerbView,SIGNAL(closed()),this,SLOT(showFreeVerbView()));
+ } else {
+ delete freeVerbView;
+ freeVerbView = 0;
+ }
+}
+
+void VControl::initaRtsConnections() {
+kdDebug() << k_funcinfo << endl;
+ svcgui = Arts::StereoVolumeControlGui::null();
+ svcgui = Arts::StereoVolumeControlGui( server->server().outVolume() );
+ svcgui.title( i18n( "aRts Master Volume" ).utf8().data() );
+ svcguiw->setContent( Arts::Widget() );
+ svcguiw->setContent( svcgui );
+ useOldVolumeBar( false );
+kdDebug() << k_funcinfo << "done." << endl;
+}
+
+void MainWindow::toggleVolumeBar() {
+ vc->useOldVolumeBar(showOldVolumeDisplay->isChecked());
+}
+
+MainWindow::MainWindow() : KMainWindow(0), kartsserver( new KArtsServer( this ) ) {
+kdDebug() << k_funcinfo << endl;
+ connect( kartsserver, SIGNAL( restartedServer() ), this, SLOT( serverRestarted() ) );
+
+ vc = new VControl( kartsserver, this );
+ setCentralWidget( vc );
+ artsactions = new ArtsActions( kartsserver, actionCollection(), this );
+
+ ( void ) artsactions->actionScopeView();
+ ( void ) artsactions->actionAudioManager();
+ ( void ) artsactions->actionArtsStatusView();
+ ( void ) artsactions->actionMidiManagerView();
+ ( void ) artsactions->actionEnvironmentView();
+ ( void ) artsactions->actionMediaTypesView();
+ ( void ) new KAction( i18n("Toggle Free&Verb"), 0, vc, SLOT( showFreeVerbView() ), actionCollection(), "view_freeverb" );
+ showOldVolumeDisplay=
+ new KToggleAction( i18n( "Old aRts-Control-Style for VU-Meter" /*"&LED-Style Volume Display"*/ ), 0, this,
+ SLOT( toggleVolumeBar() ), actionCollection(), "old_volume_display" );
+ ( void ) KStdAction::quit( this, SLOT( close() ), actionCollection(), "quit_artscontrol" );
+
+ createGUI("artscontrol.rc");
+ resize(20,300);
+ show();
+}
+
+void MainWindow::serverRestarted() {
+kdDebug() << k_funcinfo << endl;
+ KMessageBox::sorry( this, "aRts had to restart!" );
+}
+
+int main(int argc, char **argv) {
+ KAboutData aboutData( "artscontrol", I18N_NOOP("aRts control"),
+ ARTS_VERSION, I18N_NOOP("Control tool for the aRts server"),
+ KAboutData::License_GPL, I18N_NOOP("(c) 2000 Stefan Westerfeld\n(c) 2003 Arnold Krille") );
+ aboutData.addAuthor( "Stefan Westerfeld", I18N_NOOP( "Author and aRts maintainer" ), "stefan@space.twc.de" );
+ aboutData.addAuthor( "Arnold Krille", I18N_NOOP( "Some improvements" ), "arnold@arnoldarts.de" );
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+
+ KApplication app(argc, argv);
+
+ // setup mcop communication
+ QIOManager qiomanager;
+ Dispatcher dispatcher(&qiomanager);
+
+ ObjectManager::the()->provideCapability("kdegui");
+
+ app.setMainWidget(new MainWindow);
+ app.setName("artsbuilder");
+ return app.exec();
+}
diff --git a/arts/tools/main.h b/arts/tools/main.h
new file mode 100644
index 00000000..818e3e89
--- /dev/null
+++ b/arts/tools/main.h
@@ -0,0 +1,110 @@
+/*
+
+ Copyright (C) 2000-2001 Stefan Westerfeld
+ <stefan@space.twc.de>
+ 2003 Arnold Krille <arnold@arnoldarts.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef MAIN_H
+#define MAIN_H
+#include <qiomanager.h>
+#include <dispatcher.h>
+#include <qmessagebox.h>
+#include <kapplication.h>
+#include <qframe.h>
+#include <kmainwindow.h>
+#include <kcmdlineargs.h>
+#include <kaboutdata.h>
+#include <klocale.h>
+#include <kstdaction.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <artsflow.h>
+#include <soundserver.h>
+#include <qobject.h>
+#include <kartswidget.h>
+#include <flowsystem.h>
+#include <artsmodules.h>
+#include <kartsserver.h>
+
+class LevelMeter;
+class PeakBar;
+class PeakLevelMeter;
+class VScale;
+
+class FreeVerbView : public QWidget {
+ Q_OBJECT
+protected:
+ Arts::Synth_FREEVERB freeverb;
+ Arts::SimpleSoundServer server;
+ long effectID;
+
+ void closeEvent(QCloseEvent *e);
+public:
+ FreeVerbView(Arts::SimpleSoundServer server);
+ ~FreeVerbView();
+signals:
+ void closed();
+};
+
+class KArtsWidget;
+
+class VControl : public QFrame {
+ Q_OBJECT
+protected:
+ class StereoLevelMeter *stereoMeter;
+ FreeVerbView *freeVerbView;
+ Arts::StereoVolumeControl svc;
+ Arts::StereoVolumeControlGui svcgui;
+ KArtsWidget *svcguiw;
+ KArtsServer *server;
+
+ QBoxLayout *boxLayout;
+
+public:
+ VControl( KArtsServer*, QWidget *parent);
+ ~VControl();
+
+public slots:
+ void useOldVolumeBar(int old);
+ void showFreeVerbView();
+private slots:
+ void initaRtsConnections();
+};
+
+#include "artsactions.h"
+
+class MainWindow : public KMainWindow {
+ Q_OBJECT
+protected:
+ VControl *vc;
+ KToggleAction *showOldVolumeDisplay;
+ ArtsActions* artsactions;
+ KArtsServer *kartsserver;
+
+public slots:
+ void toggleVolumeBar();
+
+ void serverRestarted();
+public:
+ MainWindow();
+};
+
+//Arts::Environment::Container defaultEnvironment();
+
+#endif /* MAIN_H */
diff --git a/arts/tools/mediatypesview.cpp b/arts/tools/mediatypesview.cpp
new file mode 100644
index 00000000..b54d2dd1
--- /dev/null
+++ b/arts/tools/mediatypesview.cpp
@@ -0,0 +1,76 @@
+/*
+
+ Copyright (C) 2000-2001 Stefan Westerfeld
+ <stefan@space.twc.de>
+ 2003 Arnold Krille
+ <arnold@arnoldarts.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include <qlayout.h>
+#include <klistview.h>
+#include <klocale.h>
+#include <kartsserver.h>
+#include <map>
+#include <kiconloader.h>
+
+#include "mediatypesview.h"
+
+using namespace std;
+using namespace Arts;
+
+MediaTypesView::MediaTypesView( QWidget* parent, const char* name ) : Template_ArtsView( parent,name )
+{
+ this->setCaption( i18n( "Available Media Types" ) );
+ this->setIcon( MainBarIcon( "artsmediatypes", 32 ) );
+ QBoxLayout *l= new QHBoxLayout(this);
+ l->setAutoAdd(true);
+
+ KListView *listView = new KListView(this);
+ listView->addColumn(i18n("Media Type"));
+
+ Arts::TraderQuery q;
+ std::vector<Arts::TraderOffer> *results = q.query();
+ std::map<std::string, bool> done;
+ QString str;
+
+ for(std::vector<Arts::TraderOffer>::iterator i = results->begin(); i != results->end(); i++)
+ {
+ std::vector<string> *ext = (*i).getProperty("Extension");
+
+ for(vector<string>::iterator it = ext->begin(); it != ext->end(); it++)
+ {
+ if(!(*it).length() || done[*it])
+ continue;
+
+ done[*it] = true;
+ (void) new QListViewItem(listView, (*it).c_str());
+ }
+ delete ext;
+ }
+ delete results;
+
+ l->activate();
+ show();
+ setBaseSize(300,200);
+}
+
+MediaTypesView::~MediaTypesView()
+{
+}
+
+#include "mediatypesview.moc"
diff --git a/arts/tools/mediatypesview.h b/arts/tools/mediatypesview.h
new file mode 100644
index 00000000..a55e44a3
--- /dev/null
+++ b/arts/tools/mediatypesview.h
@@ -0,0 +1,36 @@
+/*
+
+ Copyright (C) 2000-2001 Stefan Westerfeld
+ <stefan@space.twc.de>
+ 2003 Arnold Krille
+ <arnold@arnoldarts.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef MEDIATYPESVIEW_H
+#define MEDIATYPESVIEW_H
+
+#include "templateview.h"
+
+class MediaTypesView : public Template_ArtsView {
+ Q_OBJECT
+public:
+ MediaTypesView( QWidget* =0, const char* =0 );
+ ~MediaTypesView();
+};
+
+#endif
diff --git a/arts/tools/midiinstdlg.cpp b/arts/tools/midiinstdlg.cpp
new file mode 100644
index 00000000..ef98f299
--- /dev/null
+++ b/arts/tools/midiinstdlg.cpp
@@ -0,0 +1,179 @@
+/*
+
+ Copyright (C) 1998 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "midiinstdlg.h"
+#include <klocale.h>
+#include <kstandarddirs.h>
+
+#include <kapplication.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qfile.h>
+#include <qdir.h>
+#include <kbuttonbox.h>
+#include <kseparator.h>
+#include <kdebug.h>
+#include <qbutton.h>
+#include <qpushbutton.h>
+#include <kstdguiitem.h>
+
+static QStringList getArtsPath()
+{
+ QStringList artsPath;
+ QString dir = locate("data", "artsbuilder/examples/");
+ artsPath += dir;
+ QString home = QDir::homeDirPath() + "/arts/structures/";
+ artsPath += home;
+ return artsPath;
+}
+
+static QStringList listFiles(QString directory, QString extension)
+{
+ QStringList result;
+ QStringList artsPath = getArtsPath();
+
+ QStringList::Iterator it;
+ for ( it = artsPath.begin(); it != artsPath.end(); it++ ) {
+ QString pathname = *it + "/" + directory;
+ QDir dir(pathname, extension);
+ if (dir.exists()) {
+ //kdDebug() << "found dir " << dir.absPath() << endl;
+ result += dir.entryList();
+ }
+ }
+
+ return result;
+}
+
+MidiInstDlg::MidiInstDlg(QWidget *parent)
+ :QDialog(parent,"instrument",TRUE)
+{
+ QVBoxLayout *mainlayout = new QVBoxLayout(this);
+
+// caption label: title
+
+ mainlayout->addSpacing(5);
+ QLabel *captionlabel = new QLabel(this);
+ QFont labelfont(captionlabel->font());
+ labelfont.setPointSize(labelfont.pointSize()*3/2);
+ captionlabel->setFont(labelfont);
+ captionlabel->setText(QString(" ")+i18n("Instrument")+QString(" "));
+ captionlabel->setAlignment(AlignCenter);
+ //min_size(captionlabel);
+ mainlayout->addWidget(captionlabel);
+
+// hruler
+
+ mainlayout->addSpacing(5);
+ KSeparator *ruler2 = new KSeparator( KSeparator::HLine, this);
+ mainlayout->addWidget(ruler2);
+ mainlayout->addSpacing(5);
+
+// combobox
+
+ box = new QComboBox(this);
+
+ QStringList instruments = listFiles(".","*.arts");
+ QStringList::Iterator it;
+ for ( it = instruments.begin(); it != instruments.end(); it++ ) {
+ QString modname = *it;
+ QString prefix = QString::fromLatin1("instrument_");
+ if (modname.length() > 5)
+ modname.truncate(modname.length()-5); // kill .arts extension
+ if ( (modname.startsWith(prefix)) && (!modname.contains("_GUI")) )
+ box->insertItem(modname.mid(prefix.length()));
+ //kdDebug() << "inserted instrument: " << modname.mid(prefix.length()) << endl;
+ }
+
+
+ QStringList maps = listFiles(".","*.arts-map");
+
+ for ( it = maps.begin(); it != maps.end(); it++ ) {
+ QString modname = *it;
+ QString prefix = QString::fromLatin1("instrument_");
+ if (modname.length() > 9)
+ modname.truncate(modname.length()-9); // kill .arts-map extension
+ if (modname.startsWith(prefix))
+ box->insertItem(modname.mid(prefix.length()));
+ //kdDebug() << "inserted map: " << modname.mid(prefix.length()) << endl;
+ }
+
+ mainlayout->addWidget(box);
+
+// hruler
+
+ mainlayout->addSpacing(5);
+ KSeparator *ruler = new KSeparator( KSeparator::HLine, this);
+ mainlayout->addWidget(ruler);
+ mainlayout->addSpacing(5);
+
+// buttons
+
+ QHBoxLayout *buttonlayout = new QHBoxLayout;
+ mainlayout->addSpacing(5);
+ mainlayout->addLayout(buttonlayout);
+ mainlayout->addSpacing(5);
+
+ buttonlayout->addSpacing(5);
+ KButtonBox *bbox = new KButtonBox(this);
+
+ bbox->addButton(KStdGuiItem::help(), this, SLOT( help() ));
+ bbox->addStretch(1);
+
+ QButton *okbutton = bbox->addButton(KStdGuiItem::ok());
+ connect( okbutton, SIGNAL( clicked() ), SLOT(accept() ) );
+
+ bbox->layout();
+
+ buttonlayout->addWidget(bbox);
+ buttonlayout->addSpacing(5);
+
+ mainlayout->freeze();
+}
+
+QCString MidiInstDlg::filename()
+{
+ QStringList artsPath = getArtsPath();
+ QString instrument = box->currentText();
+
+ QStringList::Iterator it;
+
+ for ( it = artsPath.begin(); it != artsPath.end(); it++ ) {
+ QString pathname = *it + QString::fromLatin1("/instrument_") + instrument + QString::fromLatin1(".arts");
+ QFileInfo fi(pathname);
+ if (fi.exists() && fi.isReadable())
+ return QFile::encodeName(pathname);
+
+ pathname = *it + QString::fromLatin1("/instrument_") + instrument + QString::fromLatin1(".arts-map");
+ fi.setFile(pathname);
+ if (fi.exists() && fi.isReadable())
+ return QFile::encodeName(pathname);
+ }
+
+ return "";
+}
+
+void MidiInstDlg::help()
+{
+ KApplication::kApplication()->invokeHelp("", "artsbuilder");
+}
+
+#include "midiinstdlg.moc"
diff --git a/arts/tools/midiinstdlg.h b/arts/tools/midiinstdlg.h
new file mode 100644
index 00000000..95d7b677
--- /dev/null
+++ b/arts/tools/midiinstdlg.h
@@ -0,0 +1,38 @@
+/*
+
+ Copyright (C) 1998 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_TOOLS_MIDIINSTDLG_H
+#define ARTS_TOOLS_MIDIINSTDLG_H
+
+#include <qdialog.h>
+#include <qcombobox.h>
+#include <qcstring.h>
+
+class MidiInstDlg :public QDialog {
+ Q_OBJECT
+ QComboBox *box;
+public:
+ MidiInstDlg(QWidget *parent);
+ QCString filename();
+public slots:
+ void help();
+};
+#endif
diff --git a/arts/tools/midimanagerdlg.ui b/arts/tools/midimanagerdlg.ui
new file mode 100644
index 00000000..78941e4c
--- /dev/null
+++ b/arts/tools/midimanagerdlg.ui
@@ -0,0 +1,151 @@
+<!DOCTYPE UI><UI version="3.0" stdsetdef="1">
+<class>MidiManagerDlg</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>MidiManagerDlg</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>462</width>
+ <height>266</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>MIDI Manager</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>MIDI inputs:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>TextLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>MIDI outputs:</string>
+ </property>
+ </widget>
+ <widget class="QListBox" row="1" column="0">
+ <property name="name">
+ <cstring>inputsListBox</cstring>
+ </property>
+ </widget>
+ <widget class="QListBox" row="1" column="2">
+ <property name="name">
+ <cstring>outputsListBox</cstring>
+ </property>
+ </widget>
+ <widget class="QFrame" row="2" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>Frame5</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <property name="layoutMargin" stdset="0">
+ </property>
+ <property name="layoutSpacing" stdset="0">
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QPushButton" row="0" column="0">
+ <property name="name">
+ <cstring>AddButton_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Add...</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="0" column="1">
+ <property name="name">
+ <cstring>RemoveButton_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="0" column="2">
+ <property name="name">
+ <cstring>connectButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Connect</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="0" column="3">
+ <property name="name">
+ <cstring>disconnectButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Disconnect</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QFrame" row="1" column="1">
+ <property name="name">
+ <cstring>connectionFrame</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/arts/tools/midimanagerview.cpp b/arts/tools/midimanagerview.cpp
new file mode 100644
index 00000000..764f4ee5
--- /dev/null
+++ b/arts/tools/midimanagerview.cpp
@@ -0,0 +1,260 @@
+/*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Permission is also granted to link this program with the Qt
+ library, treating Qt like a library that normally accompanies the
+ operating system kernel, whether or not that is in fact the case.
+
+ */
+
+#include "midimanagerview.h"
+#define protected public
+#include "midimanagerwidget.h"
+#undef protected
+#include <qpushbutton.h>
+#include <qlistbox.h>
+#include <qlayout.h>
+#include <qpainter.h>
+#include <stdio.h>
+#include <kaction.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <soundserver.h>
+#include "artsmidi.h"
+#include "artsmodules.h"
+#include "midiportdlg.h"
+#include "midiinstdlg.h"
+
+#include <kstdaction.h>
+
+using namespace Arts;
+using namespace std;
+
+/* quick hack as long as the sound server doesn't support environments */
+Arts::Environment::Container defaultEnvironment()
+{
+ Arts::SimpleSoundServer server = Reference("global:Arts_SimpleSoundServer");
+ Arts::Environment::Container d =
+ Arts::DynamicCast(server._getChild("defaultEnvironment"));
+
+ if(d.isNull())
+ {
+ d = Arts::DynamicCast(
+ server.createObject("Arts::Environment::Container"));
+ server._addChild(d,"defaultEnvironment");
+ }
+ return d;
+}
+
+class MidiManagerItem : public QListBoxText {
+public:
+ MidiClientInfo info;
+
+ MidiManagerItem(const MidiClientInfo &info) :info(info)
+ {
+ }
+ QString text () const {
+ return QString::fromUtf8(info.title.c_str());
+ }
+
+};
+
+class ConnectionWidget : public QWidget {
+public:
+ MidiManagerView *v;
+ ConnectionWidget(MidiManagerView *v, QWidget *parent) : QWidget(parent), v(v)
+ {
+ }
+ void paintEvent(QPaintEvent * /*event*/)
+ {
+ QPainter painter;
+ painter.begin(this);
+// painter.fillRect(event->rect(),white);
+
+ unsigned int in;
+ for(in = 0; in < v->widget->inputsListBox->count(); in++)
+ {
+ vector<long>::iterator conn;
+ MidiManagerItem *item = (MidiManagerItem *)v->widget->inputsListBox->item(in);
+ for(conn = item->info.connections.begin();
+ conn != item->info.connections.end(); conn++)
+ {
+ MidiManagerItem *outitem = v->itemMap[*conn];
+ QRect r1 = v->widget->inputsListBox->itemRect(item);
+ QRect r2 = v->widget->outputsListBox->itemRect(outitem);
+
+ if(r1.height() > 0 && r2.height() > 0)
+ {
+ painter.drawLine(0,(r1.top()+r1.bottom()) / 2,
+ width(), (r2.top()+r2.bottom())/2);
+ }
+ }
+ }
+ painter.end();
+ }
+};
+
+MidiManagerView::MidiManagerView()
+ : manager(Reference("global:Arts_MidiManager"))
+{
+ QTimer *updatetimer = new QTimer(this);
+ updatetimer->start(5000);
+ connect(updatetimer,SIGNAL(timeout()),this,SLOT(updateLists()));
+
+ widget = new MidiManagerWidget(this);
+ setCentralWidget(widget);
+ setCaption(i18n("MIDI Manager"));
+ setIcon( MainBarIcon( "artsfftscope", 32 ) );
+
+ (void)new KAction(i18n("&System MIDI Port (OSS)"), 0, this, SLOT(addOSSMidiPort()),
+ actionCollection(), "add_oss_midi_port");
+ (void)new KAction(i18n("&aRts Synthesis MIDI Output"), 0, this,
+ SLOT(addArtsMidiOutput()), actionCollection(), "add_arts_midi_output");
+
+ (void) KStdAction::quit( this, SLOT(close()), actionCollection());
+ connect(widget->connectButton,SIGNAL(clicked()), this, SLOT(slotConnect()));
+ connect(widget->disconnectButton,SIGNAL(clicked()), this, SLOT(slotDisconnect()));
+
+ connectionWidget = new ConnectionWidget(this, widget->connectionFrame);
+ connectionWidget->setMinimumSize(60,10);
+ widget->connectionFrameLayout->addWidget( connectionWidget, 0, 0 );
+
+ updateLists();
+ createGUI( "artsmidimanagerview.rc");
+ show();
+ setCaption(i18n("MIDI Manager"));
+ setIcon( MainBarIcon( "artsfftscope", 32 ) );
+}
+
+void MidiManagerView::closeEvent(QCloseEvent *e)
+{
+ e->accept();
+ emit closed();
+}
+
+void MidiManagerView::updateLists()
+{
+ vector<MidiClientInfo> *clients = manager.clients();
+ vector<MidiClientInfo>::iterator i;
+
+// keep selection over updating
+ MidiManagerItem *src =
+ (MidiManagerItem *)widget->inputsListBox->item(widget->inputsListBox->currentItem());
+ MidiManagerItem *dest =
+ (MidiManagerItem *)widget->outputsListBox->item(widget->outputsListBox->currentItem());
+
+ long srcID = src?src->info.ID:0;
+ long destID = dest?dest->info.ID:0;
+
+// clear everything, and rebuild
+ itemMap.clear();
+ widget->inputsListBox->clear();
+ widget->outputsListBox->clear();
+ for(i = clients->begin(); i != clients->end(); i++)
+ {
+ QListBox *box;
+ if(i->direction == mcdPlay)
+ box = widget->inputsListBox;
+ else
+ box = widget->outputsListBox;
+
+ MidiManagerItem *item = new MidiManagerItem(*i);
+ itemMap[item->info.ID] = item;
+ box->insertItem(item);
+ }
+ delete clients;
+
+// restore selection
+ if(srcID && itemMap[srcID])
+ widget->inputsListBox->setSelected(itemMap[srcID],true);
+ if(destID && itemMap[destID])
+ widget->outputsListBox->setSelected(itemMap[destID],true);
+ connectionWidget->repaint();
+}
+
+void MidiManagerView::slotConnect()
+{
+ MidiManagerItem *src =
+ (MidiManagerItem *)widget->inputsListBox->item(widget->inputsListBox->currentItem());
+ MidiManagerItem *dest =
+ (MidiManagerItem *)widget->outputsListBox->item(widget->outputsListBox->currentItem());
+ if(src && dest)
+ {
+ manager.connect(src->info.ID,dest->info.ID);
+ updateLists();
+ }
+}
+
+void MidiManagerView::slotDisconnect()
+{
+ MidiManagerItem *src =
+ (MidiManagerItem *)widget->inputsListBox->item(widget->inputsListBox->currentItem());
+ MidiManagerItem *dest =
+ (MidiManagerItem *)widget->outputsListBox->item(widget->outputsListBox->currentItem());
+ if(src && dest)
+ {
+ manager.disconnect(src->info.ID,dest->info.ID);
+ updateLists();
+ }
+}
+
+void MidiManagerView::addOSSMidiPort()
+{
+ //lukas: no i18n here, QDialog's CTOR doesn't know about QString
+ //lukas: can't use that with const char *, i18n()'ed in the dialog itself
+ MidiPortDlg *dlg = new MidiPortDlg(0,"/dev/midi","OSS Midi Port");
+
+ if(dlg->exec())
+ {
+ SoundServer s = Reference("global:Arts_SoundServer");
+ if(!s.isNull())
+ {
+ RawMidiPort p = DynamicCast(s.createObject("Arts::RawMidiPort"));
+ p.device(dlg->device());
+ if(p.open())
+ p._addChild(p,"avoid_delete");
+ }
+ }
+ delete dlg;
+}
+
+void MidiManagerView::addArtsMidiOutput()
+{
+ MidiInstDlg *dlg = new MidiInstDlg(0);
+
+ if(dlg->exec())
+ {
+ SoundServer s = Reference("global:Arts_SoundServer");
+ if(!s.isNull())
+ {
+ /*
+ Synth_MIDI_TEST t
+ = DynamicCast(s.createObject("Arts::Synth_MIDI_TEST"));
+ t.filename(dlg->filename().data());
+ t.start();
+ t._addChild(t,"avoid_delete");
+ */
+ Environment::InstrumentItem item = DynamicCast(
+ defaultEnvironment().createItem("Arts::Environment::InstrumentItem"));
+ if(!item.isNull())
+ item.filename(dlg->filename().data());
+ }
+ }
+}
+#include "midimanagerview.moc"
diff --git a/arts/tools/midimanagerview.h b/arts/tools/midimanagerview.h
new file mode 100644
index 00000000..7ab9d7ff
--- /dev/null
+++ b/arts/tools/midimanagerview.h
@@ -0,0 +1,61 @@
+/*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Permission is also granted to link this program with the Qt
+ library, treating Qt like a library that normally accompanies the
+ operating system kernel, whether or not that is in fact the case.
+
+ */
+
+#ifndef ARTS_TOOLS_MIDIMANAGERVIEW_H
+#define ARTS_TOOLS_MIDIMANAGERVIEW_H
+
+#include <kmainwindow.h>
+#include "artsmidi.h"
+#include <map>
+
+class ConnectionWidget;
+class MidiManagerItem;
+class MidiManagerWidget;
+
+class MidiManagerView : public KMainWindow {
+ Q_OBJECT
+protected:
+ friend class ConnectionWidget;
+ Arts::MidiManager manager;
+ MidiManagerWidget *widget;
+ ConnectionWidget *connectionWidget;
+ std::map<long, MidiManagerItem *> itemMap;
+
+public:
+ MidiManagerView();
+
+ void closeEvent(QCloseEvent *e);
+public slots:
+ void updateLists();
+ void slotConnect();
+ void slotDisconnect();
+ void addOSSMidiPort();
+ void addArtsMidiOutput();
+
+signals:
+ void closed();
+};
+
+#endif
diff --git a/arts/tools/midimanagerwidget.ui b/arts/tools/midimanagerwidget.ui
new file mode 100644
index 00000000..115626d0
--- /dev/null
+++ b/arts/tools/midimanagerwidget.ui
@@ -0,0 +1,135 @@
+<!DOCTYPE UI><UI version="3.0" stdsetdef="1">
+<class>MidiManagerWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>MidiManagerWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>454</width>
+ <height>266</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>MIDI Manager</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>MIDI inputs:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>TextLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>MIDI outputs:</string>
+ </property>
+ </widget>
+ <widget class="QListBox" row="1" column="0">
+ <property name="name">
+ <cstring>inputsListBox</cstring>
+ </property>
+ </widget>
+ <widget class="QListBox" row="1" column="2">
+ <property name="name">
+ <cstring>outputsListBox</cstring>
+ </property>
+ </widget>
+ <widget class="QFrame" row="2" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>Frame5</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <property name="layoutMargin" stdset="0">
+ </property>
+ <property name="layoutSpacing" stdset="0">
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QPushButton" row="0" column="2">
+ <property name="name">
+ <cstring>connectButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Connect</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="0" column="3">
+ <property name="name">
+ <cstring>disconnectButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Disconnect</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QFrame" row="1" column="1">
+ <property name="name">
+ <cstring>connectionFrame</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/arts/tools/midiportdlg.cpp b/arts/tools/midiportdlg.cpp
new file mode 100644
index 00000000..c7e61e03
--- /dev/null
+++ b/arts/tools/midiportdlg.cpp
@@ -0,0 +1,111 @@
+/*
+
+ Copyright (C) 1998 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "midiportdlg.h"
+#include <klocale.h>
+
+#include <kapplication.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <kseparator.h>
+#include <kbuttonbox.h>
+#include <qlineedit.h>
+#include <qbutton.h>
+#include <qpushbutton.h>
+#include <kstdguiitem.h>
+
+MidiPortDlg::MidiPortDlg(QWidget *parent, const char *oldname, const char *title) :QDialog(parent,title,TRUE)
+{
+ QVBoxLayout *mainlayout = new QVBoxLayout(this);
+
+// caption label: title
+
+ mainlayout->addSpacing(5);
+ QLabel *captionlabel = new QLabel(this);
+ QFont labelfont(captionlabel->font());
+ labelfont.setPointSize(labelfont.pointSize()*3/2);
+ captionlabel->setFont(labelfont);
+ captionlabel->setText(i18n("OSS MIDI Port"));
+ captionlabel->setAlignment(AlignCenter);
+ //min_size(captionlabel);
+ mainlayout->addWidget(captionlabel);
+
+// hruler
+
+ mainlayout->addSpacing(5);
+ KSeparator *ruler2 = new KSeparator( KSeparator::HLine, this);
+ mainlayout->addWidget(ruler2);
+ mainlayout->addSpacing(5);
+
+// editwidget
+
+ edit = new QLineEdit(this);
+ edit->setText(oldname);
+ //min_size(edit);
+
+ mainlayout->addWidget(edit);
+
+// hruler
+
+ mainlayout->addSpacing(5);
+ KSeparator *ruler = new KSeparator( KSeparator::HLine, this);
+ mainlayout->addWidget(ruler);
+ mainlayout->addSpacing(5);
+
+// buttons
+
+ QHBoxLayout *buttonlayout = new QHBoxLayout;
+ mainlayout->addSpacing(5);
+ mainlayout->addLayout(buttonlayout);
+ mainlayout->addSpacing(5);
+
+ buttonlayout->addSpacing(5);
+ KButtonBox *bbox = new KButtonBox(this);
+
+ QPushButton *helpbutton = bbox->addButton(KStdGuiItem::help(), this, SLOT( help() ));
+ bbox->addStretch(1);
+ helpbutton->setAutoDefault( true );
+ helpbutton->setDefault( true );
+
+ QPushButton *okbutton = bbox->addButton(KStdGuiItem::ok());
+ connect( okbutton, SIGNAL( clicked() ), SLOT(accept() ) );
+ okbutton->setAutoDefault( true );
+ okbutton->setDefault( true );
+
+ bbox->layout();
+
+ buttonlayout->addWidget(bbox);
+ buttonlayout->addSpacing(5);
+
+ mainlayout->freeze();
+}
+
+const char *MidiPortDlg::device()
+{
+ return edit->text().ascii();
+}
+
+void MidiPortDlg::help()
+{
+ KApplication::kApplication()->invokeHelp("", "artsbuilder");
+}
+
+#include "midiportdlg.moc"
diff --git a/arts/tools/midiportdlg.h b/arts/tools/midiportdlg.h
new file mode 100644
index 00000000..4664bebc
--- /dev/null
+++ b/arts/tools/midiportdlg.h
@@ -0,0 +1,42 @@
+/*
+
+ Copyright (C) 1998 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_TOOLS_MIDIPORTDLG_H
+#define ARTS_TOOLS_MIDIPORTDLG_H
+
+#include <qdialog.h>
+#include <qtimer.h>
+#include <qlabel.h>
+#include <qscrollbar.h>
+#include <qlineedit.h>
+#include <qlayout.h>
+
+class MidiPortDlg :public QDialog {
+ Q_OBJECT
+ QLineEdit *edit;
+public:
+ MidiPortDlg(QWidget *parent, const char *device, const char *title);
+ const char *device();
+
+public slots:
+ void help();
+};
+#endif
diff --git a/arts/tools/pics/Makefile.am b/arts/tools/pics/Makefile.am
new file mode 100644
index 00000000..779ff362
--- /dev/null
+++ b/arts/tools/pics/Makefile.am
@@ -0,0 +1,2 @@
+KDE_ICON = AUTO
+
diff --git a/arts/tools/pics/cr128-action-artsaudiomanager.png b/arts/tools/pics/cr128-action-artsaudiomanager.png
new file mode 100644
index 00000000..d659b4b5
--- /dev/null
+++ b/arts/tools/pics/cr128-action-artsaudiomanager.png
Binary files differ
diff --git a/arts/tools/pics/cr128-action-artsenvironment.png b/arts/tools/pics/cr128-action-artsenvironment.png
new file mode 100644
index 00000000..5075c8cc
--- /dev/null
+++ b/arts/tools/pics/cr128-action-artsenvironment.png
Binary files differ
diff --git a/arts/tools/pics/cr128-action-artsfftscope.png b/arts/tools/pics/cr128-action-artsfftscope.png
new file mode 100644
index 00000000..0e1b512b
--- /dev/null
+++ b/arts/tools/pics/cr128-action-artsfftscope.png
Binary files differ
diff --git a/arts/tools/pics/cr128-action-artsmediatypes.png b/arts/tools/pics/cr128-action-artsmediatypes.png
new file mode 100644
index 00000000..877bf20a
--- /dev/null
+++ b/arts/tools/pics/cr128-action-artsmediatypes.png
Binary files differ
diff --git a/arts/tools/pics/cr128-action-artsmidimanager.png b/arts/tools/pics/cr128-action-artsmidimanager.png
new file mode 100644
index 00000000..ab5b672a
--- /dev/null
+++ b/arts/tools/pics/cr128-action-artsmidimanager.png
Binary files differ
diff --git a/arts/tools/pics/cr16-action-artsaudiomanager.png b/arts/tools/pics/cr16-action-artsaudiomanager.png
new file mode 100644
index 00000000..43e287cd
--- /dev/null
+++ b/arts/tools/pics/cr16-action-artsaudiomanager.png
Binary files differ
diff --git a/arts/tools/pics/cr16-action-artsenvironment.png b/arts/tools/pics/cr16-action-artsenvironment.png
new file mode 100644
index 00000000..62b04e42
--- /dev/null
+++ b/arts/tools/pics/cr16-action-artsenvironment.png
Binary files differ
diff --git a/arts/tools/pics/cr16-action-artsfftscope.png b/arts/tools/pics/cr16-action-artsfftscope.png
new file mode 100644
index 00000000..4ef12da5
--- /dev/null
+++ b/arts/tools/pics/cr16-action-artsfftscope.png
Binary files differ
diff --git a/arts/tools/pics/cr16-action-artsmediatypes.png b/arts/tools/pics/cr16-action-artsmediatypes.png
new file mode 100644
index 00000000..592e300b
--- /dev/null
+++ b/arts/tools/pics/cr16-action-artsmediatypes.png
Binary files differ
diff --git a/arts/tools/pics/cr16-action-artsmidimanager.png b/arts/tools/pics/cr16-action-artsmidimanager.png
new file mode 100644
index 00000000..44948a1e
--- /dev/null
+++ b/arts/tools/pics/cr16-action-artsmidimanager.png
Binary files differ
diff --git a/arts/tools/pics/cr22-action-artsaudiomanager.png b/arts/tools/pics/cr22-action-artsaudiomanager.png
new file mode 100644
index 00000000..9a103e73
--- /dev/null
+++ b/arts/tools/pics/cr22-action-artsaudiomanager.png
Binary files differ
diff --git a/arts/tools/pics/cr22-action-artsenvironment.png b/arts/tools/pics/cr22-action-artsenvironment.png
new file mode 100644
index 00000000..3e4c3a04
--- /dev/null
+++ b/arts/tools/pics/cr22-action-artsenvironment.png
Binary files differ
diff --git a/arts/tools/pics/cr22-action-artsfftscope.png b/arts/tools/pics/cr22-action-artsfftscope.png
new file mode 100644
index 00000000..1104ab76
--- /dev/null
+++ b/arts/tools/pics/cr22-action-artsfftscope.png
Binary files differ
diff --git a/arts/tools/pics/cr22-action-artsmediatypes.png b/arts/tools/pics/cr22-action-artsmediatypes.png
new file mode 100644
index 00000000..271de38e
--- /dev/null
+++ b/arts/tools/pics/cr22-action-artsmediatypes.png
Binary files differ
diff --git a/arts/tools/pics/cr22-action-artsmidimanager.png b/arts/tools/pics/cr22-action-artsmidimanager.png
new file mode 100644
index 00000000..2d21c16a
--- /dev/null
+++ b/arts/tools/pics/cr22-action-artsmidimanager.png
Binary files differ
diff --git a/arts/tools/pics/cr32-action-artsaudiomanager.png b/arts/tools/pics/cr32-action-artsaudiomanager.png
new file mode 100644
index 00000000..1ce95887
--- /dev/null
+++ b/arts/tools/pics/cr32-action-artsaudiomanager.png
Binary files differ
diff --git a/arts/tools/pics/cr32-action-artsenvironment.png b/arts/tools/pics/cr32-action-artsenvironment.png
new file mode 100644
index 00000000..78b9936c
--- /dev/null
+++ b/arts/tools/pics/cr32-action-artsenvironment.png
Binary files differ
diff --git a/arts/tools/pics/cr32-action-artsfftscope.png b/arts/tools/pics/cr32-action-artsfftscope.png
new file mode 100644
index 00000000..a70320f8
--- /dev/null
+++ b/arts/tools/pics/cr32-action-artsfftscope.png
Binary files differ
diff --git a/arts/tools/pics/cr32-action-artsmediatypes.png b/arts/tools/pics/cr32-action-artsmediatypes.png
new file mode 100644
index 00000000..8f44fe1a
--- /dev/null
+++ b/arts/tools/pics/cr32-action-artsmediatypes.png
Binary files differ
diff --git a/arts/tools/pics/cr32-action-artsmidimanager.png b/arts/tools/pics/cr32-action-artsmidimanager.png
new file mode 100644
index 00000000..90d6c565
--- /dev/null
+++ b/arts/tools/pics/cr32-action-artsmidimanager.png
Binary files differ
diff --git a/arts/tools/pics/cr48-action-artsaudiomanager.png b/arts/tools/pics/cr48-action-artsaudiomanager.png
new file mode 100644
index 00000000..048a567e
--- /dev/null
+++ b/arts/tools/pics/cr48-action-artsaudiomanager.png
Binary files differ
diff --git a/arts/tools/pics/cr48-action-artsenvironment.png b/arts/tools/pics/cr48-action-artsenvironment.png
new file mode 100644
index 00000000..f12b27da
--- /dev/null
+++ b/arts/tools/pics/cr48-action-artsenvironment.png
Binary files differ
diff --git a/arts/tools/pics/cr48-action-artsfftscope.png b/arts/tools/pics/cr48-action-artsfftscope.png
new file mode 100644
index 00000000..c5d832af
--- /dev/null
+++ b/arts/tools/pics/cr48-action-artsfftscope.png
Binary files differ
diff --git a/arts/tools/pics/cr48-action-artsmediatypes.png b/arts/tools/pics/cr48-action-artsmediatypes.png
new file mode 100644
index 00000000..7f297365
--- /dev/null
+++ b/arts/tools/pics/cr48-action-artsmediatypes.png
Binary files differ
diff --git a/arts/tools/pics/cr48-action-artsmidimanager.png b/arts/tools/pics/cr48-action-artsmidimanager.png
new file mode 100644
index 00000000..cdec56ba
--- /dev/null
+++ b/arts/tools/pics/cr48-action-artsmidimanager.png
Binary files differ
diff --git a/arts/tools/pics/cr64-action-artsaudiomanager.png b/arts/tools/pics/cr64-action-artsaudiomanager.png
new file mode 100644
index 00000000..088969ac
--- /dev/null
+++ b/arts/tools/pics/cr64-action-artsaudiomanager.png
Binary files differ
diff --git a/arts/tools/pics/cr64-action-artsenvironment.png b/arts/tools/pics/cr64-action-artsenvironment.png
new file mode 100644
index 00000000..13a79a9b
--- /dev/null
+++ b/arts/tools/pics/cr64-action-artsenvironment.png
Binary files differ
diff --git a/arts/tools/pics/cr64-action-artsfftscope.png b/arts/tools/pics/cr64-action-artsfftscope.png
new file mode 100644
index 00000000..53627e6f
--- /dev/null
+++ b/arts/tools/pics/cr64-action-artsfftscope.png
Binary files differ
diff --git a/arts/tools/pics/cr64-action-artsmediatypes.png b/arts/tools/pics/cr64-action-artsmediatypes.png
new file mode 100644
index 00000000..b5969d0f
--- /dev/null
+++ b/arts/tools/pics/cr64-action-artsmediatypes.png
Binary files differ
diff --git a/arts/tools/pics/cr64-action-artsmidimanager.png b/arts/tools/pics/cr64-action-artsmidimanager.png
new file mode 100644
index 00000000..e363de1e
--- /dev/null
+++ b/arts/tools/pics/cr64-action-artsmidimanager.png
Binary files differ
diff --git a/arts/tools/pics/crsc-action-artsaudiomanager.svgz b/arts/tools/pics/crsc-action-artsaudiomanager.svgz
new file mode 100644
index 00000000..fa7c0105
--- /dev/null
+++ b/arts/tools/pics/crsc-action-artsaudiomanager.svgz
Binary files differ
diff --git a/arts/tools/pics/crsc-action-artsenvironment.svgz b/arts/tools/pics/crsc-action-artsenvironment.svgz
new file mode 100644
index 00000000..7fa3fc30
--- /dev/null
+++ b/arts/tools/pics/crsc-action-artsenvironment.svgz
Binary files differ
diff --git a/arts/tools/pics/crsc-action-artsfftscope.svgz b/arts/tools/pics/crsc-action-artsfftscope.svgz
new file mode 100644
index 00000000..e0938861
--- /dev/null
+++ b/arts/tools/pics/crsc-action-artsfftscope.svgz
Binary files differ
diff --git a/arts/tools/pics/crsc-action-artsmediatypes.svgz b/arts/tools/pics/crsc-action-artsmediatypes.svgz
new file mode 100644
index 00000000..46fbdd30
--- /dev/null
+++ b/arts/tools/pics/crsc-action-artsmediatypes.svgz
Binary files differ
diff --git a/arts/tools/pics/crsc-action-artsmidimanager.svgz b/arts/tools/pics/crsc-action-artsmidimanager.svgz
new file mode 100644
index 00000000..9ca209dc
--- /dev/null
+++ b/arts/tools/pics/crsc-action-artsmidimanager.svgz
Binary files differ
diff --git a/arts/tools/pics/hi128-app-artscontrol.png b/arts/tools/pics/hi128-app-artscontrol.png
new file mode 100644
index 00000000..c6586076
--- /dev/null
+++ b/arts/tools/pics/hi128-app-artscontrol.png
Binary files differ
diff --git a/arts/tools/pics/hi16-app-artscontrol.png b/arts/tools/pics/hi16-app-artscontrol.png
new file mode 100644
index 00000000..b39d4682
--- /dev/null
+++ b/arts/tools/pics/hi16-app-artscontrol.png
Binary files differ
diff --git a/arts/tools/pics/hi22-app-artscontrol.png b/arts/tools/pics/hi22-app-artscontrol.png
new file mode 100644
index 00000000..dcb1d88c
--- /dev/null
+++ b/arts/tools/pics/hi22-app-artscontrol.png
Binary files differ
diff --git a/arts/tools/pics/hi32-app-artscontrol.png b/arts/tools/pics/hi32-app-artscontrol.png
new file mode 100644
index 00000000..98525ed1
--- /dev/null
+++ b/arts/tools/pics/hi32-app-artscontrol.png
Binary files differ
diff --git a/arts/tools/pics/hi48-app-artscontrol.png b/arts/tools/pics/hi48-app-artscontrol.png
new file mode 100644
index 00000000..14549fbf
--- /dev/null
+++ b/arts/tools/pics/hi48-app-artscontrol.png
Binary files differ
diff --git a/arts/tools/pics/hi64-app-artscontrol.png b/arts/tools/pics/hi64-app-artscontrol.png
new file mode 100644
index 00000000..f9064f92
--- /dev/null
+++ b/arts/tools/pics/hi64-app-artscontrol.png
Binary files differ
diff --git a/arts/tools/pics/hisc-app-artscontrol.svgz b/arts/tools/pics/hisc-app-artscontrol.svgz
new file mode 100644
index 00000000..7763a33a
--- /dev/null
+++ b/arts/tools/pics/hisc-app-artscontrol.svgz
Binary files differ
diff --git a/arts/tools/statusview.cpp b/arts/tools/statusview.cpp
new file mode 100644
index 00000000..7606b3ef
--- /dev/null
+++ b/arts/tools/statusview.cpp
@@ -0,0 +1,95 @@
+/*
+
+ Copyright (C) 2000-2001 Stefan Westerfeld
+ <stefan@space.twc.de>
+ 2003 Arnold Krille
+ <arnold@arnoldarts.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include <qlayout.h>
+#include <klocale.h>
+#include <qpushbutton.h>
+#include <qwidget.h>
+#include <qtimer.h>
+#include <qlabel.h>
+
+#include "statusview.h"
+
+using namespace std;
+using namespace Arts;
+
+ArtsStatusView::ArtsStatusView(Arts::SoundServer a_server, QWidget* parent, const char* name )
+ : Template_ArtsView( parent,name )
+ , server(a_server)
+{
+ this->setCaption( i18n( "aRts Status" ) );
+ QBoxLayout *l= new QVBoxLayout(this);
+ //l->setAutoAdd(TRUE);
+
+ RealtimeStatus rs= server.realtimeStatus();
+ l->addWidget(new QLabel(rs==rtRealtime?
+ i18n("Artsd is running with realtime scheduling."):
+ rs==rtNoSupport?
+ i18n("Your system does not support realtime scheduling."):
+ rs==rtNoWrapper?
+ i18n("Artsd is not configured for realtime scheduling\n "
+ "or was manually started without artswrapper."):
+ i18n("Artsd should run with realtime scheduling,\n but it "
+ "does not (Is artswrapper suid root?)."),
+ this, "realtimeLabel"));
+ l->addSpacing(10);
+
+ suspendLabel= new QLabel(i18n("Determining suspend status..."),
+ this, "suspendLabel");
+ l->addWidget(suspendLabel);
+ l->addSpacing(6);
+ l->setMargin(6);
+
+ suspendButton= new QPushButton(this, "suspendButton");
+ suspendButton->setText(i18n("&Suspend Now"));
+ l->addWidget(suspendButton);
+ connect(suspendButton, SIGNAL(clicked()), this, SLOT(suspendButtonClicked()));
+
+ artsPollStatusTimer= new QTimer(this);
+ connect(artsPollStatusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
+ artsPollStatusTimer->start(1000);
+ //l->activate();
+ show();
+}
+
+void ArtsStatusView::suspendButtonClicked()
+{
+ (void)server.suspend(); // TODO: error msg if suspend not possible?
+}
+
+void ArtsStatusView::updateStatus()
+{
+ long seconds= server.secondsUntilSuspend();
+ if (seconds<0)
+ suspendLabel->setText(i18n("The aRts sound daemon will not autosuspend right\n"
+ "now since there are active modules."));
+ else if (seconds==0)
+ suspendLabel->setText(i18n("The aRts sound daemon is suspended. Legacy\n "
+ "applications can use the sound card now."));
+ else
+ suspendLabel->setText(i18n("Autosuspend will happen in %1 seconds.").
+ arg(seconds));
+ suspendButton->setEnabled(seconds>0);
+}
+
+#include "statusview.moc"
diff --git a/arts/tools/statusview.h b/arts/tools/statusview.h
new file mode 100644
index 00000000..6ded00af
--- /dev/null
+++ b/arts/tools/statusview.h
@@ -0,0 +1,52 @@
+/*
+
+ Copyright (C) 2000-2001 Stefan Westerfeld
+ <stefan@space.twc.de>
+ 2003 Arnold Krille
+ <arnold@arnoldarts.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef STATUSVIEW_H
+#define STATUSVIEW_H
+
+#include <qwidget.h>
+#include <arts/soundserver.h>
+
+#include "templateview.h"
+
+class QButton;
+class QTimer;
+class QLabel;
+
+class ArtsStatusView : public Template_ArtsView {
+ Q_OBJECT
+public:
+ ArtsStatusView(Arts::SoundServer server, QWidget* =0, const char* =0 );
+
+public slots:
+ void updateStatus();
+ void suspendButtonClicked();
+
+protected:
+ QTimer *artsPollStatusTimer;
+ Arts::SoundServer server;
+ QLabel *suspendLabel;
+ QButton *suspendButton;
+};
+
+#endif
diff --git a/arts/tools/templateview.cpp b/arts/tools/templateview.cpp
new file mode 100644
index 00000000..ecf7a0e8
--- /dev/null
+++ b/arts/tools/templateview.cpp
@@ -0,0 +1,39 @@
+/*
+
+ Copyright (C) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "templateview.h"
+
+//#include <kdebug.h>
+
+Template_ArtsView::Template_ArtsView( QWidget* parent, const char* name ) : QFrame( parent,name ){
+ //kdDebug()<<k_funcinfo<<endl;
+}
+
+Template_ArtsView::~Template_ArtsView() {
+ //kdDebug()<<k_funcinfo<<endl;
+}
+
+void Template_ArtsView::closeEvent( QCloseEvent *e ) {
+ //kdDebug()<<k_funcinfo<<endl;
+ e->accept();
+ emit closed();
+}
+
+#include "templateview.moc"
diff --git a/arts/tools/templateview.h b/arts/tools/templateview.h
new file mode 100644
index 00000000..e101aa85
--- /dev/null
+++ b/arts/tools/templateview.h
@@ -0,0 +1,39 @@
+/*
+
+ Copyright (C) 2003 Arnold Krille <arnold@arnoldarts.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTS_TEMPLATEVIEW_H
+#define ARTS_TEMPLATEVIEW_H
+
+//#include <artsflow.h>
+#include <qframe.h>
+
+class Template_ArtsView : public QFrame
+{
+ Q_OBJECT
+public:
+ Template_ArtsView( QWidget* =0, const char* =0 );
+ ~Template_ArtsView();
+
+ void closeEvent( QCloseEvent* );
+signals:
+ void closed();
+};
+
+#endif
diff --git a/audiofile_artsplugin/Makefile.am b/audiofile_artsplugin/Makefile.am
new file mode 100644
index 00000000..c5209797
--- /dev/null
+++ b/audiofile_artsplugin/Makefile.am
@@ -0,0 +1,23 @@
+INCLUDES= -I$(kde_includes)/arts $(all_includes)
+
+noinst_HEADERS = audiofilePlayObjectI.h
+
+lib_LTLIBRARIES = libarts_audiofile.la
+libarts_audiofile_la_COMPILE_FIRST = audiofilearts.h
+libarts_audiofile_la_SOURCES = audiofilearts.cc audiofilePlayObjectI.cpp
+libarts_audiofile_la_LDFLAGS = $(all_libraries) -module -no-undefined
+libarts_audiofile_la_LIBADD = -lkmedia2_idl -lsoundserver_idl -lartsflow -laudiofile
+libarts_audiofile_la_METASOURCES = AUTO
+
+audiofilearts.mcopclass: audiofilearts.h
+audiofilearts.mcoptype: audiofilearts.h
+audiofilearts.cc audiofilearts.h: $(srcdir)/audiofilearts.idl $(MCOPIDL)
+ $(MCOPIDL) -t -I$(kde_includes)/arts $(srcdir)/audiofilearts.idl
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = audiofilearts.mcoptype audiofilearts.mcopclass
+
+mcopclassdir = $(libdir)/mcop/Arts
+mcopclass_DATA = audiofilePlayObject.mcopclass
+
+CLEANFILES=audiofilearts.h audiofilearts.cc audiofilearts.mcopclass audiofilearts.mcoptype
diff --git a/audiofile_artsplugin/README b/audiofile_artsplugin/README
new file mode 100644
index 00000000..8c25f5bf
--- /dev/null
+++ b/audiofile_artsplugin/README
@@ -0,0 +1,9 @@
+audiofilePlayObject by Neil Stevens <neil@qualityassistant.com>
+
+inspired by Alex Zepeda <zipzippy@sonic.net>
+
+based on the 7-line IRC explanation by Stefan Westerfeld <stefan@space.twc.de>
+
+uses audiofile by Michael Pruett <michael@68k.org>
+
+fiddled with by Rik Hemsley <rik@kde.org>
diff --git a/audiofile_artsplugin/audiofilePlayObject.mcopclass b/audiofile_artsplugin/audiofilePlayObject.mcopclass
new file mode 100644
index 00000000..f317002d
--- /dev/null
+++ b/audiofile_artsplugin/audiofilePlayObject.mcopclass
@@ -0,0 +1,7 @@
+Interface=Arts::audiofilePlayObject,Arts::PitchablePlayObject,Arts::PlayObject,Arts::SynthModule,Arts::Object
+Library=libarts_audiofile.la
+Language=C++
+Author=Neil Stevens <neil@qualityassistant.com>
+Extension=wav,au,aiff,snd
+MimeType=audio/x-wav,audio/basic,audio/x-aiff
+Preference=5
diff --git a/audiofile_artsplugin/audiofilePlayObjectI.cpp b/audiofile_artsplugin/audiofilePlayObjectI.cpp
new file mode 100644
index 00000000..377a9d25
--- /dev/null
+++ b/audiofile_artsplugin/audiofilePlayObjectI.cpp
@@ -0,0 +1,343 @@
+// audiofilePlayObject
+//
+// Copyright (C) 2001 Neil Stevens <neil@qualityassistant.com>
+// Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#include "audiofilePlayObjectI.h"
+
+#include <resample.h>
+#include <soundserver.h>
+#include <convert.h>
+#include <debug.h>
+#include <math.h>
+#include <stdlib.h>
+
+namespace Arts
+{
+ class AudioFilePlayObjectRefiller : public Refiller
+ {
+ public:
+
+ AudioFilePlayObjectRefiller()
+ : fileHandle_(AF_NULL_FILEHANDLE),
+ frameSize_(0)
+ {
+ }
+
+ void setFileHandle(AFfilehandle fileHandle)
+ {
+ fileHandle_ = fileHandle;
+ }
+
+ void setFrameSize(unsigned int frameSize)
+ {
+ frameSize_ = frameSize;
+ }
+
+
+ unsigned long read(unsigned char * buffer, unsigned long len)
+ {
+ if (AF_NULL_FILEHANDLE == fileHandle_)
+ return 0;
+
+ int framesRead =
+ afReadFrames
+ (fileHandle_, AF_DEFAULT_TRACK, (void *)buffer, len / frameSize_);
+
+ if (-1 == framesRead)
+ return 0;
+
+ return framesRead * frameSize_;
+ }
+
+ private:
+
+ AFfilehandle fileHandle_;
+ unsigned int frameSize_;
+ };
+}
+
+static inline AFframecount timeToFrame(poTime time, float samplingRate)
+{
+ float seconds = time.seconds;
+ // ignoring ms
+ return (int)(floor(seconds * samplingRate));
+}
+
+static inline poTime frameToTime(AFframecount frame, float samplingRate)
+{
+ float seconds = (float)frame / samplingRate;
+ poTime time;
+ time.seconds = (long int)(floor(seconds));
+ time.ms = (long int)(floor(seconds * 1000.0) - (time.seconds * 1000.0));
+ return time;
+}
+
+audiofilePlayObjectI::audiofilePlayObjectI()
+ : audiofilePlayObject_skel()
+ , StdSynthModule()
+ , fh(AF_NULL_FILEHANDLE)
+ , channels(0)
+ , frameSize(0)
+ , sampleWidth(0)
+ , samplingRate(0)
+ , myState(posIdle)
+ , _speed(1.0)
+ , resampler(0)
+{
+ refiller = new AudioFilePlayObjectRefiller;
+ resampler = new Arts::Resampler(refiller);
+}
+
+audiofilePlayObjectI::~audiofilePlayObjectI()
+{
+ delete refiller;
+ refiller = 0;
+
+ delete resampler;
+ resampler = 0;
+
+ if (sanityCheck())
+ {
+ afCloseFile(fh);
+ fh = AF_NULL_FILEHANDLE;
+ }
+}
+
+bool audiofilePlayObjectI::loadMedia(const string &filename)
+{
+ if (sanityCheck())
+ {
+ afCloseFile(fh);
+ fh = AF_NULL_FILEHANDLE;
+ refiller->setFileHandle(fh);
+ }
+
+ fh = afOpenFile(filename.c_str(), "r", 0);
+
+ if (!sanityCheck())
+ {
+ this->filename = "";
+ return false;
+ }
+
+ this->filename = filename;
+ afSetVirtualByteOrder(fh, AF_DEFAULT_TRACK, AF_BYTEORDER_LITTLEENDIAN);
+
+ int sampleFormat;
+
+ channels = afGetChannels(fh, AF_DEFAULT_TRACK);
+
+ afGetSampleFormat(fh, AF_DEFAULT_TRACK, &sampleFormat, &sampleWidth);
+
+ samplingRate = afGetRate(fh, AF_DEFAULT_TRACK);
+ frameSize = sampleWidth / 8 * channels;
+
+ arts_debug("loading wav: %s", filename.c_str());
+ arts_debug(" frame size: %d", frameSize);
+
+ resampler->setChannels(channels);
+ resampler->setBits(sampleWidth);
+ resampler->setEndianness(Arts::Resampler::littleEndian);
+
+ refiller->setFileHandle(fh);
+ refiller->setFrameSize(frameSize);
+
+ arts_debug(" channels: %d", channels);
+ arts_debug(" bits: %d",sampleWidth);
+
+ myState = posIdle;
+ return true;
+}
+
+string audiofilePlayObjectI::description()
+{
+ return "audiofilePlayObject";
+}
+
+string audiofilePlayObjectI::mediaName()
+{
+ return filename;
+}
+
+poCapabilities audiofilePlayObjectI::capabilities()
+{
+ return static_cast<poCapabilities>(capSeek | capPause);
+}
+
+poState audiofilePlayObjectI::state()
+{
+ if(!sanityCheck())
+ return posIdle;
+ else
+ return myState;
+}
+
+void audiofilePlayObjectI::play()
+{
+ myState = posPlaying;
+}
+
+void audiofilePlayObjectI::pause()
+{
+ myState = posPaused;
+}
+
+void audiofilePlayObjectI::halt()
+{
+ afSeekFrame(fh, AF_DEFAULT_TRACK, 0);
+ myState = posIdle;
+}
+
+poTime audiofilePlayObjectI::currentTime()
+{
+ if (!sanityCheck())
+ return poTime(0,0,0,"samples");
+
+ AFfileoffset offset = afTellFrame(fh, AF_DEFAULT_TRACK);
+
+ float timesec = offset / samplingRate;
+ float timems = (timesec - floor(timesec)) * 1000.0;
+
+ return poTime
+ (
+ int(timesec),
+ int(timems),
+ offset,
+ "samples"
+ );
+}
+
+poTime audiofilePlayObjectI::overallTime()
+{
+ if (!sanityCheck())
+ return poTime(0, 0, 0, "samples");
+
+ AFfileoffset offset = afGetTrackBytes(fh, AF_DEFAULT_TRACK) / frameSize;
+
+ float timesec = offset / (float)samplingRate;
+ float timems = (timesec - floor(timesec)) * 1000.0;
+
+ return poTime(int(timesec), int(timems), offset, "samples");
+}
+
+void audiofilePlayObjectI::seek(const poTime &time)
+{
+ if (!sanityCheck())
+ {
+ return;
+ }
+
+ float fnewsamples = -1;
+
+ if (time.seconds != -1 && time.ms != -1)
+ {
+ float flnewtime = (float)time.seconds+((float)time.ms/1000.0);
+ fnewsamples = flnewtime * samplingRate;
+ }
+ else if (time.custom >= 0 && time.customUnit == "samples")
+ {
+ fnewsamples = time.custom;
+ }
+
+ // Avoid going past end of file.
+
+ AFfileoffset eof = afGetTrackBytes(fh, AF_DEFAULT_TRACK) / frameSize;
+
+ if (fnewsamples > (float)eof)
+ fnewsamples = (float)eof;
+
+ // Avoid going past beginning of file.
+
+ if (fnewsamples < 0)
+ fnewsamples = 0.0;
+
+ afSeekFrame(fh, AF_DEFAULT_TRACK, (unsigned long)fnewsamples);
+}
+
+void audiofilePlayObjectI::calculateBlock(unsigned long count)
+{
+ if (myState == posPlaying)
+ {
+ double speed = samplingRate / samplingRateFloat;
+
+ resampler->setStep(speed * _speed);
+ resampler->run(left, right, count);
+
+ if (resampler->underrun())
+ {
+ myState = posIdle;
+ }
+ }
+ else
+ {
+ for (unsigned long i = 0; i < count; i++)
+ {
+ left[i] = right[i] = 0;
+ }
+ }
+
+}
+
+AutoSuspendState audiofilePlayObjectI::autoSuspend()
+{
+ return asNoSuspend;
+}
+
+float audiofilePlayObjectI::speed()
+{
+ return _speed;
+}
+
+void audiofilePlayObjectI::speed(float newSpeed)
+{
+ if(newSpeed != _speed)
+ {
+ _speed = newSpeed;
+ speed_changed(_speed);
+ }
+}
+
+void audiofilePlayObjectI::start()
+{
+}
+
+void audiofilePlayObjectI::stop()
+{
+}
+
+void audiofilePlayObjectI::streamInit()
+{
+}
+
+void audiofilePlayObjectI::streamStart()
+{
+}
+
+void audiofilePlayObjectI::streamEnd()
+{
+}
+
+REGISTER_IMPLEMENTATION(audiofilePlayObjectI);
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/audiofile_artsplugin/audiofilePlayObjectI.h b/audiofile_artsplugin/audiofilePlayObjectI.h
new file mode 100644
index 00000000..3d0246a3
--- /dev/null
+++ b/audiofile_artsplugin/audiofilePlayObjectI.h
@@ -0,0 +1,95 @@
+// audiofilePlayObject
+//
+// Copyright (C) 2001 Neil Stevens <neil@qualityassistant.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifndef AUDIOFILEPLAYOBJECTI_H
+#define AUDIOFILEPLAYOBJECTI_H
+
+#include "audiofilearts.h"
+#include <stdsynthmodule.h>
+
+#include <string>
+
+namespace Arts
+{
+ class Resampler;
+ class AudioFilePlayObjectRefiller;
+}
+
+extern "C"
+{
+#include <audiofile.h>
+}
+
+using namespace Arts;
+using namespace std; // violation of Kyoto protocol against namespace pollution
+
+class audiofilePlayObjectI : public audiofilePlayObject_skel, public StdSynthModule
+{
+public:
+ audiofilePlayObjectI();
+ ~audiofilePlayObjectI();
+
+ bool loadMedia(const string &);
+ string description();
+ string mediaName();
+ poCapabilities capabilities();
+ poState state();
+ void play();
+ void pause();
+ void halt();
+ poTime currentTime();
+ poTime overallTime();
+ void seek(const poTime &);
+ void calculateBlock(unsigned long samples);
+
+ // functions demanded by the linker
+ AutoSuspendState autoSuspend();
+ void start();
+ void stop();
+ void streamInit();
+ void streamStart();
+ void streamEnd();
+
+ // PitchablePlayObject:
+ float speed();
+ void speed(float newSpeed);
+
+private:
+ bool sanityCheck(void) { return fh != AF_NULL_FILEHANDLE; };
+
+ AFfilehandle fh;
+
+ int channels, frameSize, sampleWidth;
+ float samplingRate;
+
+ poState myState;
+ std::string filename;
+
+ float _speed;
+ Arts::Resampler * resampler;
+ AudioFilePlayObjectRefiller * refiller;
+};
+
+#endif
diff --git a/audiofile_artsplugin/audiofilearts.idl b/audiofile_artsplugin/audiofilearts.idl
new file mode 100644
index 00000000..6532c0e7
--- /dev/null
+++ b/audiofile_artsplugin/audiofilearts.idl
@@ -0,0 +1,12 @@
+#include <kmedia2.idl>
+#include <soundserver.idl>
+
+module Arts
+{
+
+interface audiofilePlayObject : PlayObject, SynthModule, PitchablePlayObject
+{
+ out audio stream left, right;
+};
+
+};
diff --git a/audiofile_artsplugin/configure.in.bot b/audiofile_artsplugin/configure.in.bot
new file mode 100644
index 00000000..1ac1ffa1
--- /dev/null
+++ b/audiofile_artsplugin/configure.in.bot
@@ -0,0 +1,9 @@
+if test "x$with_audiofile" = xcheck && test "x$arts_audiolib_found" = xno; then
+ echo ""
+ echo "You're missing libaudiofile. aRts won't be able to load or play"
+ echo "any samples without it, so please install it."
+ echo "Have a look at http://oss.sgi.com/projects/audiofile/ or find a"
+ echo "binary package for your platform."
+ echo ""
+ all_tests=bad
+fi
diff --git a/audiofile_artsplugin/configure.in.in b/audiofile_artsplugin/configure.in.in
new file mode 100644
index 00000000..367e64d3
--- /dev/null
+++ b/audiofile_artsplugin/configure.in.in
@@ -0,0 +1,52 @@
+if test "x$build_arts" = "xno"; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE audiofile_artsplugin"
+fi
+
+dnl libaudiofile is used for loading wave files
+AC_DEFUN([AC_CHECK_LIBAUDIOFILE],
+[
+ ac_ldflags_save="$LDFLAGS"
+ ac_CPPFLAGS_save="$CPPFLAGS"
+ LDFLAGS="$all_libraries $LDFLAGS"
+ CPPFLAGS="$CPPFLAGS $all_includes"
+ arts_audiolib_found=no
+ dnl WAV reading
+ AC_LANG_SAVE
+ AC_LANG_C
+ kde_has_audio_lib=no
+ AC_CHECK_HEADER(audiofile.h,
+ [
+ kde_has_audio_lib=yes
+ ])
+ if test "x$kde_has_audio_lib" = "xyes"; then
+ KDE_CHECK_LIB(audiofile,afOpenFile,[
+ dnl LDFLAGS in case it's in KDEDIR/lib
+ LIBAUDIOFILE="$LDFLAGS -laudiofile"
+ AC_DEFINE(HAVE_LIBAUDIOFILE, 1,
+ [Define if you have libaudiofile (required for playing wavs with aRts)])
+ arts_audiolib_found=yes
+ ])
+ fi
+ AC_SUBST(LIBAUDIOFILE)
+ AC_LANG_RESTORE
+ CPPFLAGS="$ac_CPPFLAGS_save"
+ LDFLAGS="$ac_ldflags_save"
+])
+
+AC_ARG_WITH(audiofile,
+ [AC_HELP_STRING(--with-audiofile,
+ [enable support for audiofile @<:@default=check@:>@])],
+ [], with_audiofile=check)
+
+arts_audiolib_found=no
+if test "x$with_audiofile" != xno; then
+ AC_CHECK_LIBAUDIOFILE
+
+ if test "x$with_audiofile" != xcheck && test "x$arts_audiolib_found" != xyes; then
+ AC_MSG_ERROR([--with-audiofile was given, but test for audiofile failed])
+ fi
+fi
+
+if test x$arts_audiolib_found = xno; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE audiofile_artsplugin"
+fi
diff --git a/configure.in.bot b/configure.in.bot
new file mode 100644
index 00000000..9a79138a
--- /dev/null
+++ b/configure.in.bot
@@ -0,0 +1,36 @@
+dnl put here things which have to be done as very last part of configure
+
+if test "x$with_vorbis" = xcheck && test "x$have_oggvorbis" = xno; then
+ echo ""
+ echo "Ogg Vorbis support was not found."
+ echo "a KFile-plugin for displaying Ogg Vorbis Information"
+ echo "has been disabled from compilation."
+ echo "audiocd:/ will be built without Vorbis support."
+ all_tests=bad
+fi
+
+if test "x$with_taglib" = xcheck && test "x$have_taglib" = xno; then
+ echo ""
+ echo "You're missing TagLib. Without TagLib KDE will not support ID3"
+ echo "tags in mp3 files. You can find taglib either in KDE's Subversion"
+ echo "repository under trunk/kdesupport/taglib or at"
+ echo "http://ktown.kde.org/~wheeler/taglib"
+ all_tests=bad
+fi
+
+if test "x$with_lame" = xcheck && test "x$have_lame" = xno; then
+ echo ""
+ echo "lame not found, MP3 support will be built into audiocd:/, but"
+ echo "if the library isn't present at runtime it will be disabled."
+ echo "See http://lame.sourceforge.net/"
+ all_tests=bad
+fi
+
+if test "x$with_akode" = xcheck && test "x$have_akode" = xno; then
+ echo ""
+ echo "aKode was not found. Without it the aKode aRts-plugin will not be"
+ echo "installed, and aRts will be unable to play many music formats."
+ echo "You can find aKode in KDE's Subversion repository under"
+ echo "trunk/kdesupport/akode."
+ all_tests=bad
+fi
diff --git a/configure.in.in b/configure.in.in
new file mode 100644
index 00000000..e63d3beb
--- /dev/null
+++ b/configure.in.in
@@ -0,0 +1,557 @@
+#MIN_CONFIG
+
+dnl Checks for header files.
+AC_HEADER_DIRENT
+AC_HEADER_STDC
+AC_CHECK_HEADERS(fcntl.h sys/time.h unistd.h features.h alloca.h linux/awe_voice.h awe_voice.h /usr/src/sys/i386/isa/sound/awe_voice.h /usr/src/sys/gnu/i386/isa/sound/awe_voice.h linux/ucdrom.h sys/stdtypes.h sys/filio.h sys/audioio.h Alib.h sys/sem.h string.h getopt.h machine/endian.h sys/awe_voice.h)
+
+dnl First, setup HAVE_LINUX_CDROM_H.
+dnl This gets cached, so later we can selectively
+dnl disable applications by using _CHECK_HEADER
+AC_CHECK_HEADERS(linux/cdrom.h)
+
+AC_CHECK_FUNCS(usleep snprintf)
+
+AC_MSG_CHECKING(machine architecture)
+AC_SUBST(ARCH_TYPE)
+ARCH_TYPE=`uname -m`
+if test `uname -s` = "FreeBSD"
+then
+ HW_MODEL=`sysctl -n hw.model`
+
+ case "$HW_MODEL" in
+ "Pentium II/Pentium II Xeon/Celeron")
+ ARCH_TYPE="i686"
+ ;;
+ esac
+fi;
+AC_MSG_RESULT($ARCH_TYPE)
+
+case "$ARCH_TYPE" in
+i?86)
+ AC_MSG_CHECKING(for a binutils new enough to support MMX)
+ as_ver=`echo|as -v 2>&1|grep ".*version.*"`
+ if test -z "$as_ver"
+ then
+ MMX_SUPPORT="no"
+ else
+ as_ver=`echo $as_ver |sed -e "s/.*version //;s/ .*//"`
+ as_major=`echo $as_ver |cut -d. -f1`
+ if test $as_major -gt 2
+ then
+ MMX_SUPPORT="yes"
+ else
+ if test $as_major -lt 2
+ then
+ MMX_SUPPORT="no"
+ else
+ as_minor=`echo $as_ver |cut -d. -f2`
+ if test $as_minor -ge 10
+ then
+ MMX_SUPPORT="yes"
+ else
+ MMX_SUPPORT="no"
+ fi
+ fi
+ fi
+ fi
+ AC_MSG_RESULT($MMX_SUPPORT)
+ ;;
+*)
+ dnl No MMX support on non-x86 architecture toolchains
+ MMX_SUPPORT="no"
+ ;;
+esac
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_HEADER_TIME
+CXXFLAGS="$CXXFLAGS $KDE_DEFAULT_CXXFLAGS"
+
+dnl check if the assembler supports SSE instructions
+AC_MSG_CHECKING([for x86 SSE instructions])
+AC_CACHE_VAL(ac_cv_x86_sse,
+[
+AC_TRY_COMPILE(,
+[
+#if defined(__GNUC__) && defined(__i386__)
+__asm__("movups %xmm0, (%esp)");
+#else
+#error Not gcc on x86
+#endif
+],
+ac_cv_x86_sse=yes,
+ac_cv_x86_sse=no)
+])
+AC_MSG_RESULT($ac_cv_x86_sse)
+if test "x$ac_cv_x86_sse" = "xyes"; then
+ AC_DEFINE(HAVE_X86_SSE,1,
+ [Define if your assembler supports x86 SSE instructions])
+fi
+
+AC_DEFUN([KDE_CHECK_OGGVORBIS],
+[
+ have_oggvorbis=yes
+
+ KDE_CHECK_LIB(ogg, ogg_page_version,
+ [:], [have_oggvorbis=no])
+
+ KDE_CHECK_HEADER(vorbis/vorbisfile.h,
+ [:], [have_oggvorbis=no])
+
+ KDE_CHECK_LIB(vorbis, vorbis_info_init,
+ [:], [have_oggvorbis=no])
+
+ KDE_CHECK_LIB(vorbisfile, ov_open,
+ [:], [have_oggvorbis=no], -lvorbis -logg)
+
+ KDE_CHECK_LIB(vorbisenc, vorbis_encode_init,
+ [:], [have_oggvorbis=no], -lvorbis -logg)
+
+ KDE_CHECK_LIB(vorbis, vorbis_bitrate_addblock,
+ [have_vorbis_value=2], [have_vorbis_value=1])
+
+ if test "x$have_oggvorbis" = xyes; then
+ # for akode/plugins/xiph_decoder/ kioslave/audiocd/plugins/ and krec/ogg_export/
+ VORBIS_LIBS="-lvorbis -logg"
+ VORBISFILE_LIBS="-lvorbisfile"
+ VORBISENC_LIBS="-lvorbisenc"
+
+ # for akode/plugins/xiph_decoder/
+ AC_DEFINE_UNQUOTED(HAVE_OGG_VORBIS, 1, [Define if you have ogg/vorbis installed])
+
+ # for kioslave/audiocd/plugins/ and krec/ogg_export/
+ AC_DEFINE_UNQUOTED(HAVE_VORBIS, $have_vorbis_value, [Define if you ogg/vorbis installed])
+
+ # for mpeglib/
+ OGG_VORBISLIBS="-lvorbisfile -lvorbis -logg"
+ AC_DEFINE(OGG_VORBIS, 1, [Define if you have ogg/vorbis installed])
+ fi
+
+ AC_SUBST(VORBIS_LIBS)
+ AC_SUBST(VORBISFILE_LIBS)
+ AC_SUBST(VORBISENC_LIBS)
+])
+
+AC_DEFUN([KDE_CHECK_OSSAUDIO],
+[
+ have_ossaudio=no
+
+ AC_CHECK_HEADERS([soundcard.h sys/soundcard.h],
+ [have_ossaudio=yes])
+
+ AC_CHECK_LIB(ossaudio, _oss_ioctl,
+ [:], [have_ossaudio=no])
+
+ if test "x$have_ossaudio" = xyes; then
+ # for kmix/ and akode/
+ LIBOSSAUDIO="-lossaudio"
+ fi
+
+ AC_SUBST(LIBOSSAUDIO)
+])
+
+AC_DEFUN([KDE_CHECK_ALSA],
+[
+ have_alsa=no
+
+ AC_CHECK_HEADERS([sys/asoundlib.h alsa/asoundlib.h],
+ [have_alsa=yes])
+
+ AC_CHECK_LIB(asound, snd_seq_create_simple_port,
+ [:], [have_alsa=no])
+
+ AC_LANG_SAVE
+ AC_LANG_C
+ if test "x$have_alsa" = xyes; then
+ AC_TRY_COMPILE([
+ #include "confdefs.h"
+ #ifdef HAVE_SYS_ASOUNDLIB_H
+ #include <sys/asoundlib.h>
+ #endif
+ #ifdef HAVE_ALSA_ASOUNDLIB_H
+ #include <alsa/asoundlib.h>
+ #endif
+ ],[
+ #if (SND_LIB_MAJOR == 0) && (SND_LIB_MINOR == 5)
+ /* we have ALSA 0.5.x */
+ #else
+ #error not ALSA 0.5.x
+ #endif
+ ],
+ have_alsa_0_5=yes)
+
+ AC_TRY_COMPILE([
+ #include "confdefs.h"
+ #ifdef HAVE_SYS_ASOUNDLIB_H
+ #include <sys/asoundlib.h>
+ #endif
+ #ifdef HAVE_ALSA_ASOUNDLIB_H
+ #include <alsa/asoundlib.h>
+ #endif
+ ],[
+ #if (SND_LIB_MAJOR == 0) && (SND_LIB_MINOR == 9)
+ /* we have ALSA 0.9.x */
+ #else
+ #error not ALSA 0.9.x
+ #endif
+ ],
+ have_alsa_0_9=yes)
+
+ AC_TRY_COMPILE([
+ #include "confdefs.h"
+ #ifdef HAVE_SYS_ASOUNDLIB_H
+ #include <sys/asoundlib.h>
+ #endif
+ #ifdef HAVE_ALSA_ASOUNDLIB_H
+ #include <alsa/asoundlib.h>
+ #endif
+ ],[
+ #if (SND_LIB_MAJOR == 1)
+ /* we have ALSA 1.x */
+ #else
+ #error not ALSA 1.x
+ #endif
+ ],
+ have_alsa_1=yes)
+ fi
+ AC_LANG_RESTORE
+
+ if test "x$have_alsa_0_9" = xyes || test "x$have_alsa_1" = xyes; then
+ # for kmix/ and akode/
+ LIBASOUND="-lasound"
+ AC_DEFINE(HAVE_LIBASOUND2, 1, [Define if you have libasound.so.2 (required for ALSA 0.9.x/1.x support)])
+
+ # for arts/
+ ARTS_LIBASOUND="-lasound"
+ AC_DEFINE(HAVE_ARTS_LIBASOUND2, 1, [Define if you have libasound.so.2 (required for ALSA 0.9.x/1.x support)])
+ fi
+
+ if test "x$have_alsa_0_5" = xyes; then
+ # for arts/
+ ARTS_LIBASOUND="-lasound"
+ AC_DEFINE(HAVE_ARTS_LIBASOUND, 1, [Define if you have libasound.so.1 (required for ALSA 0.5.x support)])
+ fi
+
+ AC_SUBST(LIBASOUND)
+ AC_SUBST(ARTS_LIBASOUND)
+])
+
+AC_DEFUN([KDE_CHECK_CDPARANOIA],
+[
+ have_cdparanoia=yes
+
+ if test -z "$CDPARANOIA"; then
+ KDE_FIND_PATH(cdparanoia, CDPARANOIA, [/bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin])
+ fi
+
+ if test -z "$CDPARANOIA"; then
+ have_cdparanoia=no
+ fi
+
+ KDE_CHECK_HEADER(cdda_interface.h,
+ [:], [have_cdparanoia=no])
+
+ # Older versions of FreeBSD's cdparanoia lack cdrom_drive.cdda_device_name
+ AC_MSG_CHECKING([for cdrom_drive.cdda_device_name in cdda_interface.h])
+ kde_save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $all_includes"
+ AC_LANG_SAVE
+ AC_LANG_C
+ AC_TRY_COMPILE([
+ #include <cdda_interface.h>
+ ],
+ [
+ struct cdrom_drive device;
+ device.cdda_device_name = 0;
+ ],
+ [
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ have_cdparanoia=no
+ ])
+ AC_LANG_RESTORE
+ CPPFLAGS=$kde_save_CPPFLAGS
+
+ KDE_CHECK_LIB(cdda_paranoia, paranoia_init,
+ [:], [have_cdparanoia=no], [-lcdda_interface -lm])
+
+ # because of the horrible hack we need shared cdparanoia
+ for ext in so sl la a; do
+ AC_FIND_FILE(libcdda_paranoia.$ext, $kde_libraries /usr/lib /usr/local/lib,
+ para_libdir)
+ if test -r $para_libdir/libcdda_paranoia.$ext; then
+ if test $ext = a && ls "$para_libdir" | grep "^libcdda_paranoia.so.*" 2>&1 >/dev/null; then
+ ext=so
+ fi
+ break
+ fi
+ done
+ if test "$ext" = la; then
+ grep "^library_names='.*[a-z].*'" $para_libdir/libcdda_paranoia.$ext 2>&1 > /dev/null || have_cdparanoia_only_static=yes
+ fi
+ if test "$ext" = a; then
+ have_cdparanoia_only_static=yes
+ fi
+
+ if test "x$have_cdparanoia" = xyes; then
+ # for kioslave/audiocd/
+ CDPARANOIA_LIBS="-lcdda_paranoia -lcdda_interface -lm"
+
+ # for mpeglib/
+ CDDALIBS="-lcdda_paranoia -lcdda_interface -lm"
+ AC_DEFINE(CDDA_PARANOIA, 1, [Define if you have cdparanoia installed])
+ fi
+
+ if test "x$have_cdparanoia_only_static" = xyes; then
+ # for kioslave/audiocd/
+ AC_DEFINE_UNQUOTED(CDPARANOIA_STATIC, 1, [Define if you only have a static cdparanoia])
+ fi
+
+ AC_SUBST(CDPARANOIA_LIBS)
+])
+
+AC_DEFUN([KDE_CHECK_LAME],
+[
+ have_lame=yes
+
+ KDE_CHECK_HEADER(lame/lame.h,
+ [:], [have_lame=no])
+
+ KDE_CHECK_LIB(mp3lame, lame_init,
+ [:], [have_lame=no], [-lm])
+
+ if test "x$have_lame" = xyes; then
+ # for krec/mp3_export/ (kioslave/audiocd/ uses the lame binary at runtime)
+ LAME_LIBS="-lmp3lame -lm"
+ fi
+
+ AC_SUBST(LAME_LIBS)
+])
+
+AC_DEFUN([KDE_CHECK_TAGLIB],
+[
+ AC_PATH_PROG(TAGLIB_CONFIG, taglib-config, [no], [$PATH:$prefix/bin])
+
+ if test "x$TAGLIB_CONFIG" != xno; then
+ AC_DEFINE(HAVE_TAGLIB, 1, [define if you have TagLib])
+ taglib_includes=`$TAGLIB_CONFIG --cflags`
+ taglib_libs=`$TAGLIB_CONFIG --libs`
+ have_taglib=yes
+ else
+ taglib_includes=""
+ taglib_libs=""
+ have_taglib=no
+ fi
+
+ KDE_CHECK_HEADER(taglib/mpcfile.h, have_taglib_mpc=yes, have_taglib_mpc=no)
+
+ AC_SUBST(taglib_includes)
+ AC_SUBST(taglib_libs)
+])
+
+AC_DEFUN([KDE_CHECK_AKODE],
+[
+ AC_PATH_PROG(AKODE_CONFIG, akode-config, [no], [$PATH:$prefix/bin])
+
+ if test "x$AKODE_CONFIG" != xno; then
+ AC_DEFINE(HAVE_AKODE, 1, [define if you have aKodelib installed])
+ akode_includes=`$AKODE_CONFIG --cflags`
+ akode_libs=`$AKODE_CONFIG --libs`
+ have_akode=yes
+ else
+ akode_includes=""
+ akode_libs=""
+ have_akode=no
+ fi
+
+ AC_SUBST(akode_includes)
+ AC_SUBST(akode_libs)
+])
+
+AC_DEFUN([KDE_CHECK_MPEGLIB_ARTS],
+[
+ MPEGLIB_ARTS_MAJOR_VERSION=0
+ MPEGLIB_ARTS_MINOR_VERSION=3
+ MPEGLIB_ARTS_MICRO_VERSION=0
+
+ MPEGLIB_ARTS_VERSION=$MPEGLIB_ARTS_MAJOR_VERSION.$MPEGLIB_ARTS_MINOR_VERSION.$MPEGLIB_ARTS_MICRO_VERSION
+
+ AC_SUBST(MPEGLIB_ARTS_MAJOR_VERSION)
+ AC_SUBST(MPEGLIB_ARTS_MINOR_VERSION)
+ AC_SUBST(MPEGLIB_ARTS_MICRO_VERSION)
+ AC_SUBST(MPEGLIB_ARTS_VERSION)
+
+ dnl build search PATH
+ artsc_config_test_path=$prefix/bin:$exec_prefix/bin:$KDEDIR/bin:$PATH
+ AC_PATH_PROG(kde_artsplug_compiles,artsc-config,no,$artsc_config_test_path)
+
+ dnl if we found it here set variable
+ if test x$kde_artsplug_compiles != xno; then
+ ARTSCCONFIG=$kde_artsplug_compiles;
+ kde_artsplug_compiles=yes
+ fi
+
+ dnl this is needed for a standalone mpeglib
+ dnl it should compile without KDE installed
+ dnl but if we find arts we need the include
+ dnl path for it.
+
+ if test x$kde_artsplug_compiles = xyes; then
+ ARTSC_INCLUDE=`${ARTSCCONFIG} --cflags`
+ AC_MSG_RESULT([arts includes... $ARTSC_INCLUDE])
+ AC_SUBST(ARTSC_INCLUDE)
+ ARTSC_LIBS=`${ARTSCCONFIG} --libs`
+ AC_MSG_RESULT([arts libraries... $ARTSC_LIBS])
+ AC_SUBST(ARTSC_LIBS)
+ AC_SUBST(LIBDL)
+ fi
+
+ if test x$kde_mpeglib_compiles = xno; then
+ AC_MSG_RESULT([** mpeglib disabled we disable artsplug **])
+ kde_artsplug_compiles=no;
+ fi
+
+ if test x$kde_artsplug_compiles = xno; then
+ AC_MSG_RESULT([** DO NOT COMPILE mpeglib_artsplug **])
+ DO_NOT_COMPILE="$DO_NOT_COMPILE mpeglib_artsplug"
+ fi
+])dnl KDE_CHECK_MPEGLIB_ARTS
+
+AC_TYPE_SIGNAL
+
+AC_ARG_WITH(vorbis,
+ [AC_HELP_STRING(--with-vorbis,
+ [enable support for Ogg Vorbis @<:@default=check@:>@])],
+ [], with_vorbis=check)
+
+have_oggvorbis=no
+if test "x$with_vorbis" != xno; then
+ KDE_CHECK_OGGVORBIS
+
+ if test "x$with_vorbis" != xcheck && test "x$have_oggvorbis" != xyes; then
+ AC_MSG_ERROR([--with-vorbis was given, but test for Ogg Vorbis failed])
+ fi
+fi
+
+# for krec/ogg_export
+AM_CONDITIONAL(compile_ogg_export, test "x$have_oggvorbis" = xyes)
+
+if test "x$kde_mpeglib_compiles" = xyes -o "x$have_oggvorbis" = xno; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE oggvorbis_artsplugin"
+fi
+
+AC_ARG_WITH(ossaudio,
+ [AC_HELP_STRING(--with-ossaudio,
+ [enable support for OpenBSD ossaudio @<:@default=check@:>@])],
+ [], with_ossaudio=check)
+
+if test "x$with_ossaudio" != xno; then
+ KDE_CHECK_OSSAUDIO
+
+ if test "x$with_ossaudio" != xcheck && test "x$have_ossaudio" != xyes; then
+ AC_MSG_ERROR([--with-ossaudio was given, but test for ossaudio failed])
+ fi
+fi
+
+AC_ARG_WITH(alsa,
+ [AC_HELP_STRING(--with-alsa,
+ [enable support for ALSA @<:@default=check@:>@])],
+ [], with_alsa=check)
+
+have_alsa=no
+if test "x$with_alsa" != xno; then
+ KDE_CHECK_ALSA
+
+ if test "x$with_alsa" != xcheck && test "x$have_alsa" != xyes; then
+ AC_MSG_ERROR([--with-alsa was given, but test for ALSA failed])
+ fi
+fi
+
+AC_ARG_WITH(cdparanoia,
+ [AC_HELP_STRING(--with-cdparanoia,
+ [enable support for CD ripping thorugh cdparanoia @<:@default=check@:>@])],
+ [], with_cdparanoia=check)
+
+have_cdparanoia=no
+if test "x$with_cdparanoia" != xno; then
+ KDE_CHECK_CDPARANOIA
+
+ if test "x$with_cdparanoia" != xcheck && test "x$have_cdparanoia" != xyes; then
+ AC_MSG_ERROR([--with-cdparanoia was given, but test for cdparanoia failed])
+ fi
+fi
+
+# for kioslave/audiocd/
+AM_CONDITIONAL(include_kioslave_audiocd, test "x$have_cdparanoia" = xyes)
+AM_CONDITIONAL(include_kcm_audiocd, test "x$have_cdparanoia" = xyes)
+if test "x$have_cdparanoia" != xyes; then
+ # kaudiocreator depends on kio_audiocd
+ DO_NOT_COMPILE="$DO_NOT_COMPILE kaudiocreator"
+fi
+
+AC_ARG_WITH(lame,
+ [AC_HELP_STRING(--with-lame,
+ [enable support for MP3 encoding thorugh lame @<:@default=check@:>@])],
+ [], with_lame=check)
+
+have_lame=no
+if test "x$with_lame" != xno; then
+ KDE_CHECK_LAME
+
+ if test "x$with_lame" != xcheck && test "x$have_lame" != xyes; then
+ AC_MSG_ERROR([--with-lame was given, but test for lame failed])
+ fi
+fi
+
+# for krec/mp3_export
+AM_CONDITIONAL(compile_mp3_export, test "x$have_lame" = xyes)
+
+AC_ARG_WITH(taglib,
+ [AC_HELP_STRING(--with-taglib,
+ [enable support for TagLib @<:@default=check@:>@])],
+ [], with_taglib=check)
+
+have_taglib=no
+have_taglib_mpc=no
+if test "x$with_taglib" != xno; then
+ KDE_CHECK_TAGLIB
+
+ if test "x$with_taglib" != xcheck && test "x$have_taglib" != xyes; then
+ AC_MSG_ERROR([--with-taglib was given, but test for TagLib failed])
+ fi
+fi
+
+AC_ARG_WITH(akode,
+ [AC_HELP_STRING([--with-akode],
+ [enable the aKode decoder @<:@default=check@:>@])],
+ [], with_akode=check)
+
+have_akode=no
+if test "x$with_akode" != xno; then
+ KDE_CHECK_AKODE
+
+ if test "x$with_akode" != xcheck && test "x$have_akode" != xyes; then
+ AC_MSG_ERROR([--with-akode was given, but test for aKode failed])
+ fi
+fi
+
+artsc_config_test_path=$prefix/bin:$exec_prefix/bin:$KDEDIR/bin:$PATH
+AC_PATH_PROG(ARTSCCONFIG, artsc-config, no, $artsc_config_test_path)
+
+if test "x$build_arts" = "xyes" && test "x$ARTSCCONFIG" != "xno" ; then
+ LIB_ARTS="-lartskde"
+ ARTS_PREFIX=[`$ARTSCCONFIG --arts-prefix`]
+ ARTS_CFLAGS="-I$ARTS_PREFIX/include/arts"
+ AC_DEFINE(HAVE_ARTS, 1, [have arts support in juk])
+else
+ build_arts="no"
+ LIB_ARTS=""
+ ARTS_CFLAGS=""
+ AC_DEFINE(HAVE_ARTS, 0, [no arts support in juk])
+fi
+
+AC_SUBST(LIB_ARTS)
+AC_SUBST(ARTS_CFLAGS)
+
+KDE_CHECK_MPEGLIB_ARTS
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644
index 00000000..6812bd2d
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,5 @@
+
+KDE_LANG = en
+KDE_DOCS = AUTO
+SUBDIRS = $(AUTODIRS)
+
diff --git a/doc/artsbuilder/Makefile.am b/doc/artsbuilder/Makefile.am
new file mode 100644
index 00000000..c0ba5446
--- /dev/null
+++ b/doc/artsbuilder/Makefile.am
@@ -0,0 +1,5 @@
+
+SUBDIRS = $(AUTODIRS)
+KDE_LANG = en
+KDE_DOCS = AUTO
+
diff --git a/doc/artsbuilder/apis.docbook b/doc/artsbuilder/apis.docbook
new file mode 100644
index 00000000..65de23be
--- /dev/null
+++ b/doc/artsbuilder/apis.docbook
@@ -0,0 +1,357 @@
+<!-- <?xml version="1.0" ?>
+<!DOCTYPE chapter PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd">
+To validate or process this file as a standalone document, uncomment
+this prolog. Be sure to comment it out again when you are done -->
+
+<chapter id="arts-apis">
+<title>&arts; Application Programming Interfaces</title>
+
+<sect1 id="api-overview">
+<title>Overview</title>
+<para>
+aRts is not only a piece of software, it also provides a variety of APIs
+for a variety of purposes. In this section, I will try to describe the "big
+picture", a brief glance what those APIs are supposed to do, and how they
+interact.
+</para>
+
+<para>
+There is one important distinction to make: most of the APIs are <emphasis>
+language and location independent</emphasis> because they are specified as
+<emphasis>mcopidl</emphasis>.
+That is, you can basically use the services they offer from any language,
+implement them in any language, and you will not have to care whether you
+are talking to local or remote objects. Here is a list of these first:
+</para>
+
+
+<variablelist>
+<varlistentry>
+<term>core.idl</term>
+ <listitem><para>
+ Basic definitions that form the core of the MCOP functionality, such as
+ the protocol itself, definitions of the object, the trader, the flow
+ system and so on.
+ </para></listitem>
+
+</varlistentry>
+
+<varlistentry>
+<term>artsflow.idl</term>
+
+ <listitem><para>
+ These contain the flow system you will use for connecting audio streams, the
+ definition of <emphasis>Arts::SynthModule</emphasis> which is the base for
+ any interface that has streams, and finally a few useful audio objects
+ </para></listitem>
+
+</varlistentry>
+
+<varlistentry>
+<term>kmedia2.idl</term>
+
+
+ <listitem><para>
+ Here, an object that can play a media, <emphasis>Arts::PlayObject</emphasis>
+ gets defined. Media players such as the KDE media player noatun will be able
+ to play any media for which a PlayObject can be found. So it makes sense to
+ implement PlayObjects for various formats (such as mp3, mpg video, midi, wav,
+ ...) on that base, and there are a lot already.
+ </para></listitem>
+
+</varlistentry>
+
+<varlistentry>
+<term>soundserver.idl</term>
+
+ <listitem><para>
+ Here, an interface for the system wide sound server artsd is defined. The
+ interface is called <emphasis>Arts::SoundServer</emphasis>, which implements
+ functionality like accepting streams from the network, playing samples,
+ creating custom other aRts objects and so on. Network transparency is
+ implied due to the use of MCOP (as for everything else here).
+ </para></listitem>
+
+</varlistentry>
+
+<varlistentry>
+<term>artsbuilder.idl</term>
+ <listitem><para>
+ This module defines basic flow graph functionality, that is, combining
+ simpler objects to more complex ones, by defining a graph of them. It defines
+ the basic interface <emphasis>Arts::StructureDesc</emphasis>,
+ <emphasis>Arts::ModuleDesc</emphasis> and <emphasis>Arts::PortDesc</emphasis>
+ which contain a description of a structure, module, and port. There is also
+ a way to get a "living network of objects" out of these connection and value
+ descriptions, using a factory.
+ </para></listitem>
+
+</varlistentry>
+
+<varlistentry>
+<term>artsmidi.idl</term>
+
+ <listitem><para>
+ This module defines basic midi functionality, like objects that produce
+ midi events, what is a midi event, an <emphasis>Arts::MidiManager</emphasis>
+ to connect the producers and consumers of midi events, and so on. As always
+ network transparency implied.
+ </para></listitem>
+
+</varlistentry>
+
+<varlistentry>
+<term>artsmodules.idl</term>
+ <listitem><para>
+ Here are various additional filters, oscillators, effects, delays and
+ so on, everything required for real useful signal processing, and to
+ build complex instruments and effects out of these basic building blocks.
+ </para></listitem>
+
+</varlistentry>
+
+<varlistentry>
+<term>artsgui.idl</term>
+
+ <listitem><para>
+ This cares about visual objects. It defines the basic type <emphasis>
+ Arts::Widget</emphasis> from which all GUI modules derive. This will produce
+ toolkit independency, and ... visual GUI editing, and serializable GUIs.
+ Also, as the GUI elements have normal attributes, their values can be
+ straight forward connected to some signal processing modules. (I.e. the
+ value of a slider to the cutoff of a filter). As always: network transparent.
+ </para></listitem>
+
+</varlistentry>
+
+</variablelist>
+<para>
+Where possible, aRts itself is implemented using IDL. On the other hand, there
+are some <emphasis>language specific</emphasis> APIs, using either plain C++ or
+plain C. It is usually wise to use IDL interfaces where possible, and the
+other APIs where necessary. Here is a list of language specific APIs:
+</para>
+
+<variablelist>
+
+<varlistentry>
+<term>KNotify, KAudioPlayer (included in libkdecore)</term>
+
+ <listitem><para>
+ These are convenience KDE APIs for the simple and common common case, where
+ you just want to play a sample. The APIs are plain C++, Qt/KDE optimized,
+ and as easy as it can get.
+ </para></listitem>
+
+</varlistentry>
+
+<varlistentry>
+<term>libartsc</term>
+ <listitem><para>
+ Plain C interface for the sound server. Very useful for porting legacy
+ applications.
+ </para></listitem>
+
+</varlistentry>
+
+<varlistentry>
+<term>libmcop</term>
+
+ <listitem><para>
+ Here all magic for MCOP happens. The library contains the basic things you
+ need to know for writing a simple MCOP application, the dispatcher, timers,
+ iomanagement, but also the internals to make the MCOP protocol itself work.
+ </para></listitem>
+
+</varlistentry>
+
+<varlistentry>
+<term>libartsflow</term>
+ <listitem><para>
+ Besides the implementation of artsflow.idl, some useful utilities like
+ sampling rate conversion.
+ </para></listitem>
+
+</varlistentry>
+
+<varlistentry>
+<term>libqiomanager</term>
+
+ <listitem><para>
+ Integration of MCOP into the Qt event loop, when you write Qt applications
+ using MCOP.
+ </para></listitem>
+
+</varlistentry>
+
+</variablelist>
+
+
+
+</sect1>
+<sect1 id="knotify">
+<title>knotify</title>
+<para>
+Not yet written
+</para>
+</sect1>
+
+<sect1 id="kaudioplayer">
+<title>kaudioplayer</title>
+<para>
+Not yet written
+</para>
+</sect1>
+
+<sect1 id="libkmid">
+<title>libkmid</title>
+<para>
+Not yet written
+</para>
+</sect1>
+
+<sect1 id="kmedia2">
+<title>kmedia2</title>
+<para>
+Not yet written
+</para>
+</sect1>
+
+<sect1 id="soundserver">
+<title>sound server</title>
+<para>
+Not yet written
+</para>
+</sect1>
+
+<sect1 id="artsflow">
+<title>artsflow</title>
+<para>
+Not yet written
+</para>
+</sect1>
+
+<sect1 id="capi">
+<title>C <acronym>API</acronym></title>
+
+<sect2 id="capiintro">
+<title>Introduction</title>
+
+<para> The &arts; C <acronym>API</acronym> was designed to make it easy to
+writing and port plain C applications to the &arts; sound server. It provides
+streaming functionality (sending sample streams to
+<application>artsd</application>), either blocking or non-blocking. For most
+applications you simply remove the few system calls that deal with your audio
+device and replace them with the appropriate &arts; calls.</para>
+
+<para>I did two ports as a proof of concept: <application>mpg123</application>
+and <application>quake</application>. You can get the patches from <ulink
+url="http://space.twc.de/~stefan/kde/download/artsc-patches.tar.gz">here</ulink>.
+Feel free to submit your own patches to the maintainer of &arts; or of
+multimedia software packages so that they can integrate &arts; support into
+their code.</para>
+
+</sect2>
+
+<sect2 id="capiwalkthru">
+<title>Quick Walkthrough</title>
+
+<para>Sending audio to the sound server with the <acronym>API</acronym> is very
+simple:</para>
+<procedure>
+<step><para>include the header file using <userinput>#include
+&lt;artsc.h&gt;</userinput></para></step>
+<step><para>initialize the <acronym>API</acronym> with
+<function>arts_init()</function></para></step>
+<step><para>create a stream with
+<function>arts_play_stream()</function></para></step>
+<step><para>configure specific parameters with
+<function>arts_stream_set()</function></para></step>
+<step><para>write sampling data to the stream with
+<function>arts_write()</function></para></step>
+<step><para>close the stream with
+<function>arts_close_stream()</function></para></step>
+<step><para>free the <acronym>API</acronym> with
+<function>arts_free()</function></para></step>
+</procedure>
+
+<para>Here is a small example program that illustrates this:</para>
+
+<programlisting>
+#include &lt;stdio.h&gt;
+#include &lt;artsc.h&gt;
+int main()
+{
+ arts_stream_t stream;
+ char buffer[8192];
+ int bytes;
+ int errorcode;
+
+ errorcode = arts_init();
+ if (errorcode &lt; 0)
+ {
+ fprintf(stderr, "arts_init error: %s\n", arts_error_text(errorcode));
+ return 1;
+ }
+
+ stream = arts_play_stream(44100, 16, 2, "artsctest");
+
+ while((bytes = fread(buffer, 1, 8192, stdin)) &gt; 0)
+ {
+ errorcode = arts_write(stream, buffer, bytes);
+ if(errorcode &lt; 0)
+ {
+ fprintf(stderr, "arts_write error: %s\n", arts_error_text(errorcode));
+ return 1;
+ }
+ }
+
+ arts_close_stream(stream);
+ arts_free();
+
+ return 0;
+}
+</programlisting>
+</sect2>
+
+<sect2 id="capiartscconfig">
+<title>Compiling and Linking: <application>artsc-config</application></title>
+
+<para>To easily compile and link programs using the &arts; C
+<acronym>API</acronym>, the <application>artsc-config</application> utility is
+provided which knows which libraries you need to link and where the includes
+are. It is called using</para>
+
+<screen>
+<userinput><command>artsc-config</command> <option>--libs</option></userinput>
+</screen>
+
+<para>to find out the libraries and </para>
+
+<screen>
+<userinput><command>artsc-config</command> <option>--cflags</option></userinput>
+</screen>
+
+<para>to find out additional C compiler flags. The example above could have been
+
+compiled using the command line:</para>
+
+<screen>
+<userinput><command>cc</command> <option>-o artsctest artsctest.c `artsc-config --cflags` `artsc-config --libs`</option></userinput>
+
+<userinput><command>cc</command> <option>-o artsctest</option> <option>artsctest.c</option> <option>`artsc-config --cflags`</option> <option>`artsc-config --libs`</option></userinput>
+</screen>
+
+</sect2>
+
+<sect2 id="c-api-reference">
+<title>Library Reference</title>
+
+<para>
+[TODO: generate the documentation for artsc.h using kdoc]
+</para>
+
+</sect2>
+
+</sect1>
+</chapter>
diff --git a/doc/artsbuilder/arts-structure.png b/doc/artsbuilder/arts-structure.png
new file mode 100644
index 00000000..8f6f3131
--- /dev/null
+++ b/doc/artsbuilder/arts-structure.png
Binary files differ
diff --git a/doc/artsbuilder/artsbuilder.docbook b/doc/artsbuilder/artsbuilder.docbook
new file mode 100644
index 00000000..b5f4f68c
--- /dev/null
+++ b/doc/artsbuilder/artsbuilder.docbook
@@ -0,0 +1,864 @@
+<chapter id="artsbuilder">
+<title>&arts-builder;</title>
+
+<sect1 id="overview">
+<title>Overview</title>
+
+<para>
+First of all, when trying to run &arts-builder; , you should also be
+running the sound server (&artsd;). Usually, when you use &kde; 2.1,
+this should already be the case. If not, you can configure the automatic
+sound server startup in &kcontrol; under
+<menuchoice><guilabel>Sound</guilabel><guilabel>Sound
+Server</guilabel></menuchoice>.
+</para>
+
+<para>
+When you are running &arts;, it always runs small modules. &arts-builder;
+is a tool to create new structures of small connected modules. You
+simply click the modules inside the grid. To do so, choose them from the
+<guimenu>Modules</guimenu> menu, and then click somewhere in the
+green-gray plane.
+</para>
+
+<para>
+Modules usually have ports (where usually audio signals are flowing in
+or out). To connect two ports, click on the first, which causes it to
+turn orange, and then click on the second. You can only connect an input
+port (on the upper side of a module) with an output port (on the lower
+side of a module). If you want to assign a fixed value to a port (or
+disconnect it), do so by double clicking on the port.
+</para>
+
+</sect1>
+
+<sect1 id="artsbuilder-tutorial">
+<title>Tutorial</title>
+
+<sect2 id="step-1">
+<title>Step 1</title>
+
+<para>
+Start &arts-builder;.
+</para>
+
+<para>
+You need a Synth&lowbar;AMAN&lowbar;PLAY-module to hear the output you
+are creating. So create a Synth&lowbar;AMAN&lowbar;PLAY-module by
+selecting <menuchoice><guimenu>Modules</guimenu>
+<guisubmenu>Synthesis</guisubmenu> <guisubmenu>SoundIO</guisubmenu>
+<guisubmenu>Synth&lowbar;AMAN&lowbar;PLAY</guisubmenu></menuchoice> and
+clicking on the empty module space. Put it below the fifth line or so,
+because we'll add some stuff above.
+</para>
+
+<para>
+The module will have a parameter <parameter>title</parameter> (leftmost
+port), and <parameter>autoRestoreID</parameter> (besides the leftmost
+port) for finding it. To fill these out, doubleclick on these ports,
+select constant value and type <userinput>tutorial</userinput> in the
+edit box. Click <guibutton>OK</guibutton> to apply.
+</para>
+
+<para>
+Select <menuchoice><guimenu>File</guimenu><guimenuitem>Execute
+structure</guimenuitem> </menuchoice>. You will hear absolutely
+nothing. The play module needs some input yet... ;) If you have listened
+to the silence for a while, click <guibutton>OK</guibutton> and go to
+Step 2
+</para>
+</sect2>
+
+<sect2 id="step-2">
+<title>Step 2</title>
+
+<para>Create a Synth&lowbar;WAVE&lowbar;SIN module (from <menuchoice>
+<guimenu>Modules</guimenu> <guimenuitem>Synthesis</guimenuitem>
+<guimenuitem>Waveforms</guimenuitem></menuchoice>)
+and put it above the Synth&lowbar;AMAN&lowbar;PLAY module. (Leave one line
+space in between).
+</para>
+
+<para>
+As you see, it produces some output, but requires a
+<guilabel>pos</guilabel> as input. First lets put the output to the
+speakers. Click on the <guilabel>out</guilabel> port of the
+Synth&lowbar;WAVE&lowbar;SIN and then on the <guilabel>left</guilabel>
+port of Synth&lowbar;AMAN&lowbar;PLAY. Voila, you have connected two
+modules.
+</para>
+
+<para>
+All oscillators in &arts; don't require a frequency as input, but a
+position in the wave. The position should be between 0 and 1, which maps
+for a standard Synth&lowbar;WAVE&lowbar;SIN object to the range
+0..2*pi. To generate oscillating values from a frequency, a
+Synth&lowbar;FREQUENCY modules is used.
+</para>
+
+<para>
+Create a Synth&lowbar;FREQUENCY module (from <menuchoice>
+<guimenu>Modules</guimenu> <guimenu>Synthesis</guimenu>
+<guimenu>Oscillation &amp; Modulation</guimenu> </menuchoice>) and
+connect it's <quote>pos</quote> output to the <quote>pos</quote> input
+of your Synth&lowbar;WAVE&lowbar;SIN. Specify the frequency port of the
+FREQUENCY generator as constant value 440.
+</para>
+
+
+<para>
+Select <menuchoice><guimenu>File</guimenu><guimenuitem>Execute
+structure</guimenuitem></menuchoice>. You will hear a sinus wave at 440
+Hz on one of your speakers. If you have listened to it for a while,
+click <guibutton>OK</guibutton> and go to Step 3.
+</para>
+
+</sect2>
+
+<sect2 id="step-3">
+<title>Step 3</title>
+
+<para>
+Ok, it would be nicer if you would hear the sin wave on both speakers.
+Connect the right port of Synth&lowbar;PLAY to the outvalue of the
+Synth&lowbar;WAVE&lowbar;SIN as well.
+</para>
+
+<para>Create a Synth&lowbar;SEQUENCE object (from
+<menuchoice><guimenu>Modules</guimenu>
+<guisubmenu>Synthesis</guisubmenu><guisubmenu>Midi &amp;
+Sequencing</guisubmenu></menuchoice>). It should be at the top of the
+screen. If you need more room you can move the other modules by
+selecting them (to select multiple modules use &Shift;), and dragging
+them around.
+</para>
+
+<para>
+Now connect the frequency output of Synth&lowbar;SEQUENCE to the
+frequency input of the Synth&lowbar;FREQUENCY module. Then specify the
+sequence speed as constant value 0.13 (the speed is the leftmost port).
+</para>
+
+<para>
+Now go to the rightmost port (sequence) of Synth&lowbar;SEQUENCE and
+type in as constant value <userinput>A-3;C-4;E-4;C-4;</userinput> this
+specifies a sequence. More to that in the Module Reference.
+</para>
+
+<note>
+<para>Synth&lowbar;SEQUENCE really <emphasis>needs</emphasis> a sequence
+and the speed. Without that you'll perhaps get core dumps.
+</para>
+</note>
+
+<para>
+Select <menuchoice><guimenu>File</guimenu><guimenuitem>Execute
+Structure</guimenuitem></menuchoice>. You will hear a nice sequence
+playing. If you have enjoyed the feeling, click
+<guibutton>OK</guibutton> and go to Step 4.
+</para>
+</sect2>
+
+<sect2 id="step-4">
+<title>Step 4</title>
+
+<para>Create a Synth&lowbar;PSCALE module (from
+<menuchoice><guimenu>Modules</guimenu>
+<guisubmenu>Synthesis</guisubmenu> <guisubmenu>Envelopes</guisubmenu>
+</menuchoice>). Disconnect the outvalue of the SIN wave by doubleclicking it
+and choosing <guilabel>not connected</guilabel>. Connect
+</para>
+
+<orderedlist><listitem>
+<para>The SIN outvalue to the PSCALE invalue</para>
+</listitem>
+<listitem>
+<para>The PSCALE outvalue to the AMAN_PLAY left</para>
+</listitem>
+<listitem>
+<para>The PSCALE outvalue to the AMAN_PLAY right</para>
+</listitem>
+<listitem>
+<para>The SEQUENCE pos to the PSCALE pos</para>
+</listitem>
+</orderedlist>
+
+<para>
+Finally, set the PSCALE top to some value, for instance 0.1.
+</para>
+
+<para>
+How that works now: The Synth&lowbar;SEQUENCE gives additional
+information about the position of the note it is playing right now,
+while 0 means just started and 1 means finished. The Synth&lowbar;PSCALE
+module will scale the audio stream that is directed through it from a
+volume 0 (silent) to 1 (original loudness) back to 0 (silent). According
+to the position. The position where the peak should occur can be given
+as pos. 0.1 means that after 10&percnt; of the note has been played, the
+volume has reached its maximum, and starts decaying afterwards.
+</para>
+
+
+<para>Select <menuchoice><guimenu>File</guimenu><guimenuitem>Execute
+Structure</guimenuitem></menuchoice>. You will hear a nice sequence
+playing. If you have enjoyed the feeling, click
+<guibutton>OK</guibutton> and go to Step 5.
+</para>
+
+</sect2>
+
+<sect2 id="step-5-starting-to-beam-data-around">
+<title>Step 5: Starting to beam data around ;)</title>
+
+<para>Start another &arts-builder;</para>
+
+<para>
+Put a Synth&lowbar;AMAN&lowbar;PLAY into it, configure it to a sane
+name. Put a Synth&lowbar;BUS&lowbar;DOWNLINK into it and:</para>
+
+<orderedlist>
+<listitem>
+<para>
+Set Synth&lowbar;BUS&lowbar;DOWNLINK bus to audio (that is just a name,
+call it fred if you like)
+</para>
+</listitem>
+<listitem>
+<para>
+Connect Synth&lowbar;BUS&lowbar;DOWNLINK left to
+Synth&lowbar;AMAN&lowbar;PLAY left
+</para>
+</listitem>
+<listitem>
+<para>
+Connect Synth&lowbar;BUS&lowbar;DOWNLINK right to
+Synth&lowbar;AMAN&lowbar;PLAY right
+</para>
+</listitem>
+</orderedlist>
+
+<para>
+Start executing the structure. As expected, you hear nothing, ... not
+yet.
+</para>
+
+<para>
+Go back to the structure with the Synth&lowbar;WAVE&lowbar;SIN stuff and
+replace the Synth&lowbar;AMAN&lowbar;PLAY module by an
+Synth&lowbar;BUS&lowbar;UPLINK, and configure the name to audio (or fred
+if you like). Deleting modules works with selecting them and choosing
+<menuchoice><guimenu>Edit</guimenu>
+<guimenuitem>delete</guimenuitem></menuchoice> from the menu (or
+pressing the <keycap>Del</keycap> key).
+</para>
+
+<para>
+Hit <menuchoice><guimenu>File</guimenu> <guilabel>Execute
+structure</guilabel></menuchoice>. You will hear the sequence with
+scaled notes, transported over the bus.
+</para>
+
+<para>
+If you want to find out why something like this can actually be useful,
+click <guibutton>OK</guibutton> (in the &arts-builder; that is executing
+the Synth&lowbar;SEQUENCE stuff, you can leave the other one running)
+and go to Step 6.
+</para>
+</sect2>
+
+<sect2 id="step-6-beaming-for-advanced-users">
+<title>Step 6: Beaming for advanced users</title>
+
+<para>
+Choose <menuchoice><guimenu>File</guimenu><guimenuitem>Rename</guimenuitem>
+</menuchoice> structure from the menu of the artsbuilder which
+contains the Synth&lowbar;SEQUENCE stuff, and call it tutorial. Hit
+<guibutton>OK</guibutton>.
+</para>
+
+<para>
+Choose <menuchoice><guimenu>File</guimenu> <guimenuitem>Save</guimenuitem>
+</menuchoice>
+</para>
+
+<para>
+Start yet another &arts-builder; and choose
+<menuchoice><guimenu>File</guimenu><guimenuitem>Load</guimenuitem>
+</menuchoice>, and load the tutorial again.
+</para>
+
+<para>
+Now you can select
+<menuchoice><guimenu>File</guimenu><guimenuitem>Execute
+structure</guimenuitem> </menuchoice>in both &arts-builder;s having that
+structure. You'll now hear two times the same thing. Depending on the
+time when you start it it will sound more or less nice.
+</para>
+
+<para>
+Another thing that is good to do at this point in time is: start
+&noatun;, and play some <literal role="extension">mp3</literal>. Start
+&artscontrol;. Go to
+<menuchoice><guimenu>View</guimenu><guimenuitem>View audio
+manager</guimenuitem></menuchoice>. What you will see is &noatun; and
+your <quote>tutorial</quote> playback structure playing something. The
+nice thing you can do is this: doubleclick on &noatun;. You'll now get a
+list of available busses. And see? You can assign &noatun; to send it's
+output via the audio bus your tutorial playback structure provides.
+</para>
+</sect2>
+
+<sect2 id="step-7-midi-synthesis">
+<title>Step 7: Midi synthesis</title>
+
+<para>
+Finally, now you should be able to turn your sin wave into an real
+instrument. This only makes sense if you have something handy that could
+send &MIDI; events to &arts;. I'll describe here how you can use some
+external keyboard, but a midibus aware sequence like &brahms; will work
+as well.
+</para>
+
+<para>
+First of all, clean up on your desktop until you only have one
+&arts-builder; with the sine wave structure running (not executing).
+Then, three times go to <menuchoice><guimenu>Ports</guimenu>
+<guisubmenu>Create IN audio signal</guisubmenu></menuchoice>, and three
+times to <menuchoice><guimenu>Ports</guimenu> <guisubmenu>Create OUT
+audio signal</guisubmenu></menuchoice>. Place the ports somewhere.
+</para>
+
+<para>
+Finally, go to <menuchoice><guimenu>Ports</guimenu> <guilabel>Change
+positions and names</guilabel></menuchoice> and call the ports
+frequency, velocity, pressed, left, right, done.
+</para>
+
+<para>
+Finally, you can delete the Synth&lowbar;SEQUENCE module, and rather
+connect connect the frequency input port of the structure to the
+Synth&lowbar;FREQUENCY frequency port. Hm. But what do do about
+pos?</para> <para>We don't have this, because with no algorithm in the
+world, you can predict when the user will release the note he just
+pressed on the midi keyboard. So we rather have a pressed parameter
+instead that just indicates wether the user still holds down the
+key. (pressed = 1: key still hold down, pressed = 0: key
+released)
+</para>
+
+<para>
+That means the Synth&lowbar;PSCALE object also must be replaced
+now. Plug in a Synth&lowbar;ENVELOPE&lowbar;ADSR instead (from
+<menuchoice><guimenu>Modules</guimenu>
+<guisubmenu>Synthesis</guisubmenu> <guisubmenu>Envelopes</guisubmenu>
+</menuchoice>). Connect:
+</para>
+
+<orderedlist>
+<listitem>
+<para>The pressed structure input to the ADSR active</para>
+</listitem>
+<listitem>
+<para>The SIN outvalue to the ADSR invalue</para>
+</listitem>
+<listitem>
+<para>The ADSR outvalue to the left structure output</para>
+</listitem><listitem>
+<para>The ADSR outvalue to the right structure output</para>
+</listitem>
+</orderedlist>
+
+<para>
+Set the parameters attack to 0.1, decay to 0.2, sustain to 0.7, release
+to 0.1.
+</para>
+
+<para>
+Another thing we need to think of is that the instrument structure
+somehow should know when it is ready playing and then be cleaned up,
+because otherwise it would be never stopped even if the note has been
+released. Fortunately, the ADSR envelope knows when the will be nothing
+to hear anymore, since it anyway scales the signal to zero at some point
+after the note has been released.
+</para>
+
+<para>
+This is indicated by setting the done output to 1. So connect this to
+the done output of the structure. The structure will be removed as soon
+as done goes up to 1.
+</para>
+
+<para>
+Rename your structure to instrument_tutorial (from <menuchoice><guimenu>
+File</guimenu> <guimenuitem>Rename
+structure</guimenuitem></menuchoice>. Then, save it using save as (the
+default name offered should be instrument_tutorial
+now).</para><para>Start artscontrol, and go to
+<menuchoice><guimenu>View</guimenu><guimenuitem>Midi
+Manager</guimenuitem></menuchoice>, and choose
+<menuchoice><guimenu>Add</guimenu><guimenuitem>aRts Synthesis Midi
+Output</guimenuitem></menuchoice>. Finally, you should be able to
+select your instrument (tutorial) here.
+</para>
+
+<para>
+Open a terminal and type
+<userinput><command>midisend</command></userinput>. You'll see that
+<command>midisend</command> and the instrument are listed now in the
+&arts; &MIDI; manager. After selecting both and hitting
+<guibutton>connect</guibutton>, we're finally done. Take your keyboard
+and start playing (of course it should be connected to your computer).
+</para>
+</sect2>
+
+<sect2 id="suggestions">
+<title>Suggestions</title>
+
+<para>
+You now should be able to work with &arts;. Here are a few tips what you
+could try to improve with your structures now:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+Try using other things than a SIN wave. When you plug in a TRI wave, you
+will most likely think the sound is not too nice. But try appending a
+SHELVE&lowbar;CUTOFF filter right after the TRI wave to cut the
+frequenciesabove a certain frequency (try something like 1000 Hz, or
+even better two times the input frequency or input frequency+200Hz or
+something like that).
+</para>
+</listitem>
+<listitem>
+<para>
+Try using more than one oscillator. Synth&lowbar;XFADE can be used to
+cross fade (mix) two signals, Synth&lowbar;ADD to add them.
+</para>
+</listitem>
+<listitem>
+<para>
+Try setting the frequencies of the oscillators to not exactly the same
+value, that gives nice oscillations.
+</para>
+</listitem>
+<listitem>
+<para>
+Experiment with more than one envelope.
+</para>
+</listitem>
+<listitem>
+<para>
+Try synthesizing instruments with different output left and right.
+</para>
+</listitem>
+<listitem>
+<para>
+Try postprocessing the signal after it comes out the bus downlink. You
+could for instance mix a delayed version of the signal to the original
+to get an echo effect.
+</para>
+</listitem>
+<listitem>
+<para>
+Try using the velocity setting (its the strength with which the note has
+been pressed, you could also say volume). The special effect is always
+when this not only modifies the volume of the resulting signal, but as
+well the sound of the instrument (for instance the cutoff frequency).
+</para>
+</listitem>
+<listitem>
+<para>...</para>
+</listitem>
+</itemizedlist>
+
+<para>
+If you have created something great, please consider providing it for
+the &arts; web page. Or for inclusion into the next release.
+</para>
+</sect2>
+
+</sect1>
+
+<sect1 id="artsbuilder-examples">
+<title>Examples</title>
+
+<para>
+&arts-builder; comes with several examples, which can be opened through
+<menuchoice><guimenu>File</guimenu><guimenuitem>Open
+Example...</guimenuitem> </menuchoice>. Some of them are in the
+folder, some of them (which for some reason don't work with the
+current release) are left in the todo folder.
+</para>
+<para>
+The examples fall into several categories:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+Standalone examples illustrating how to use each of the built-in
+arts modules (named <filename>example_*.arts</filename>). These
+typically send some output to a sound card.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Instruments built from lower level arts modules (named
+<filename>instrument_*.arts</filename>). These following a standard
+convention for input and output ports so they can be used by the &MIDI;
+manager in &artscontrol;.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Templates for creating new modules (names
+<filename>template_*.arts</filename>).
+</para>
+</listitem>
+
+<listitem>
+<para>
+Effects which can be used as reusable building blocks (named
+<filename>effect_*.arts</filename>) [ all in todo ]
+</para>
+</listitem>
+
+<listitem>
+<para>
+Mixer elements used for creating mixers, including graphical
+controls (named <filename>mixer_element_*.arts</filename>). [ all in todo ]
+</para>
+</listitem>
+
+<listitem>
+<para>
+Miscellaneous modules that don't fit into any of the above categories.
+</para>
+</listitem>
+</itemizedlist>
+
+<variablelist>
+<title>Detailed Description Of Each Module:</title>
+<varlistentry>
+<term><filename>example_stereo_beep.arts</filename></term>
+<listitem>
+<para>
+Generates a 440Hz sine wave tone in the left channel and an 880Hz sine
+wave tone in the right channel, and sends it to the sound card
+output. This is referenced in the &arts; documentation.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_sine.arts</filename></term>
+<listitem>
+<para>
+Generates a 440 Hz sine wave.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_pulse.arts</filename></term>
+<listitem>
+<para>
+Generates a 440 Hz pulse wave with a 20% duty cycle.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_softsaw.arts</filename></term>
+<listitem>
+<para>
+Generates a 440 Hz sawtooth wave.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_square.arts</filename></term>
+<listitem>
+<para>
+Generates a 440 Hz square wave.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_tri.arts</filename></term>
+<listitem>
+<para>
+Generates a 440 Hz triangle wave.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_noise.arts</filename></term>
+<listitem>
+<para>
+Generates white noise.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_dtmf1.arts</filename></term>
+<listitem>
+<para>
+Generates a dual tone by producing 697 and 1209 Hz sine waves, scaling
+them by 0.5, and adding them together. This is the DTMF tone for the
+digit "1" on a telephone keypad.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_atan_saturate.arts</filename></term>
+<listitem>
+<para>
+Runs a triangle wave through the atan saturate filter.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_autopanner.arts</filename></term>
+<listitem>
+<para>
+Uses an autopanner to pan a 400 Hz sine wave between the left and right
+speakers at a 2 Hz rate.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_brickwall.arts</filename></term>
+<listitem>
+<para>
+Scales a sine wave by a factor of 5 and then runs it through a brickwall
+limiter.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_bus.arts</filename></term>
+<listitem>
+<para>
+Downlinks from a bus called <quote>Bus</quote> and uplinks to the bus
+<quote>out_soundcard</quote> with the left and right channels reversed.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_cdelay.arts</filename></term>
+<listitem>
+<para>
+Downlinks from a bus called <quote>Delay</quote>, uplinks the right
+channel through a 0.5 second cdelay, and the left channel unchanged. You
+can use &artscontrol; to connect the effect to a sound player and
+observe the results.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_delay.arts</filename></term>
+<listitem>
+<para>
+This is the same as <filename>example_cdelay.arts</filename> but used
+the delay effect.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_capture_wav.arts</filename></term>
+<listitem>
+<para>
+This uses the Synth_CAPTURE_WAV to save a 400 Hz sine wave as a wav
+file. Run the module for a few seconds, and then examine the file
+created in <filename class="directory">/tmp</filename>. You can play the
+file with a player such as <application>kaiman</application>.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_data.arts</filename></term>
+<listitem>
+<para>
+This uses the Data module to generate a constant stream of the value
+<quote>3</quote> and sends it to a Debug module to periodically display
+it. It also contains a Nil module, illustrating how it can be used to do
+nothing at all.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_adsr.arts</filename></term>
+<listitem>
+<para>
+Shows how to create a simple instrument sound using the Envelope Adsr
+module, repetitively triggered by a square wave.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_fm.arts</filename></term>
+<listitem>
+<para>
+This uses the FM Source module to generate a 440 Hz sine wave which is
+frequency modulated at a 5 Hz rate.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_freeverb.arts</filename></term>
+<listitem>
+<para>
+This connects the Freeverb effect from a bus downlink to a bus
+outlink. You can use artscontrol to connect the effect to a sound player
+and observe the results.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_flanger.arts</filename></term>
+<listitem>
+<para>
+This implements a simple flanger effect (it doesn't appear to work yet,
+though).
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_moog.arts</filename></term>
+<listitem>
+<para>
+This structure combines the two channels from a bus into one, passes it
+though the Moog VCF filter, and sends it out the out_soundcard bus.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_pitch_shift.arts</filename></term>
+<listitem>
+<para>
+This structure passes the left channel of sound card data through the
+Pitch Shift effect. Adjust the speed parameter to vary the effect.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_rc.arts</filename></term>
+<listitem>
+<para>
+This structure passes a white noise generator though an RC filter and
+out to the sound card. By viewing the FFT Scope display in artscontrol
+you can see how this varies from an unfiltered noise waveform.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_sequence.arts</filename></term>
+<listitem>
+<para>
+This demonstrates the Sequence module by playing a sequence of notes.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_shelve_cutoff.arts</filename></term>
+<listitem>
+<para>
+This structure passes a white noise generator though a Shelve Cutoff
+filter and out to the sound card. By viewing the FFT Scope display in
+artscontrol you can see how this varies from an unfiltered noise
+waveform.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_equalizer.arts</filename></term>
+<listitem>
+<para>
+This demonstrates the Std_Equalizer module. It boosts the low and high
+frequencies by 6 dB.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_tremolo.arts</filename></term>
+<listitem>
+<para>
+This demonstrates the Tremolo effect. It modulates the left and right
+channels using a 10 Hz tremolo.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_xfade.arts</filename></term>
+<listitem>
+<para>
+This example mixes 440 and 880 Hz sine waves using a cross fader.
+Adjust the value of the cross fader's percentage input from -1 to 1 to
+control the mixing of the two signals.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_pscale.arts</filename></term>
+<listitem>
+<para>
+This illustrates the Pscale module (I'm not sure if this is a
+meaningful example).
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><filename>example_play_wav.arts</filename></term>
+<listitem>
+<para>
+This illustrates the Play Wave module. You will need to
+enter the full path to a <literal role="extension">.wav</literal> file
+as the filename parameter.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>example_multi_add.arts</term>
+<listitem>
+<para>
+This shows the Multi Add module which accepts any number of inputs. It
+sums three Data modules which produce inputs of 1, 2, and 3, and
+displays the result 6.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+</sect1>
+</chapter>
diff --git a/doc/artsbuilder/detail.docbook b/doc/artsbuilder/detail.docbook
new file mode 100644
index 00000000..c7ed7319
--- /dev/null
+++ b/doc/artsbuilder/detail.docbook
@@ -0,0 +1,1765 @@
+<!-- <?xml version="1.0" ?>
+<!DOCTYPE chapter PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd">
+To validate or process this file as a standalone document, uncomment
+this prolog. Be sure to comment it out again when you are done -->
+
+<chapter id="arts-in-detail">
+<title>&arts; in Detail</title>
+
+<sect1 id="architecture">
+<title>Architecture</title>
+
+<mediaobject>
+<imageobject>
+<imagedata fileref="arts-structure.png" format="PNG"/>
+</imageobject>
+<textobject><phrase>The &arts; structure.</phrase></textobject>
+</mediaobject>
+</sect1>
+
+<sect1 id="modules-ports">
+<title>Modules &amp; Ports</title>
+
+<para>
+The idea of &arts; is, that synthesis can be done using small modules,
+which only do one thing, and then recombine them in complex
+structures. The small modules normally have inputs, where they can get
+some signals or parameters, and outputs, where they produce some
+signals.
+</para>
+
+<para>
+One module (Synth&lowbar;ADD) for instance just takes the two signals at
+it's input and adds them together. The result is available as output
+signal. The places where modules provide their input/output signals are
+called ports.
+</para>
+
+</sect1>
+
+<sect1 id="structures">
+<title>Structures</title>
+
+<para>
+A structure is a combination of connected modules, some of which may
+have parameters coded directly to their input ports, others which may be
+connected, and others, which are not connected at all.
+</para>
+
+<para>
+What you can do with &arts-builder; is describing structures. You
+describe, which modules you want to be connected with which other
+modules. When you are done, you can save that structure description to a
+file, or tell &arts; to create such a structure you described (Execute).
+</para>
+
+<para>
+Then you'll probably hear some sound, if you did everything the right
+way.
+</para>
+</sect1>
+
+<!-- TODO
+
+<sect1 id="streams">
+<title>Streams</title>
+<para>
+</para>
+</sect1>
+
+-->
+
+<sect1 id="latency">
+<title>Latency</title>
+
+<sect2 id="what-islatency">
+<title>What Is Latency?</title>
+
+<para>
+Suppose you have an application called <quote>mousepling</quote>(that
+should make a <quote>pling</quote> sound if you click on a button. The
+latency is the time between your finger clicking the mouse button and
+you hearing the pling. The latency in this setup composes itself out of
+certain latencies, that have different causes.
+</para>
+
+</sect2>
+
+<sect2 id="latenbcy-simple">
+<title>Latency in Simple Applications</title>
+
+<para>
+In this simple application, latency occurs at these places:
+</para>
+
+<itemizedlist>
+
+<listitem>
+<para>
+The time until the kernel has notified the X11 server that a mouse
+button was pressed.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The time until the X11 server has notified your application that a mouse
+button was pressed.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The time until the mousepling application has decided that this button
+is worth playing a pling.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The time it takes the mousepling application to tell the soundserver
+that it should play a pling.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The time it takes for the pling (which the soundserver starts mixing to
+the other output at once) to go through the buffered data, until it
+really reaches the position where the soundcard plays.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The time it takes the pling sound from the speakers to reach your ear.
+</para>
+</listitem>
+</itemizedlist>
+
+<para>
+The first three items are latencies external to &arts;. They are
+interesting, but beyond the scope of this document. Nevertheless be
+aware that they exist, so that even if you have optimized everything
+else to really low values, you may not necessarily get exactly the
+result you calculated.
+</para>
+
+<para>
+Telling the server to play something involves usually one single &MCOP;
+call. There are benchmarks which confirm that, on the same host with
+unix domain sockets, telling the server to play something can be done
+about 9000 times in one second with the current implementation. I expect
+that most of this is kernel overhead, switching from one application to
+another. Of course this value changes with the exact type of the
+parameters. If you transfer a whole image with one call, it will be
+slower than if you transfer only one long value. For the returncode the
+same is true. However for ordinary strings (such as the filename of the
+<literal role="extension">wav</literal> file to play) this shouldn't be
+a problem.
+</para>
+
+<para>
+That means, we can approximate this time with 1/9000 sec, that is below
+0.15 ms. We'll see that this is not relevant.
+</para>
+
+<para>
+Next is the time between the server starting playing and the soundcard
+getting something. The server needs to do buffering, so that when other
+applications are running, such as your X11 server or
+<quote>mousepling</quote> application no dropouts are heard. The way
+this is done under &Linux; is that there are a number fragments of a
+size. The server will refill fragments, and the soundcard will play
+fragments.
+</para>
+
+<para>
+So suppose there are three fragments. The server refills the first, the
+soundcard starts playing it. The server refills the second. The server
+refills the third. The server is done, other applications can do
+something now.
+</para>
+
+<para>
+As the soundcard has played the first fragment, it starts playing the
+second and the server starts refilling the first. And so on.
+</para>
+
+<para>
+The maximum latency you get with all that is (number of fragments)*(size
+of each fragment)/(samplingrate * (size of each sample)). Suppose we
+assume 44kHz stereo, and 7 fragments a 1024 bytes (the current aRts
+defaults), we get 40 ms.
+</para>
+
+<para>
+These values can be tuned according to your needs. However, the
+<acronym>CPU</acronym> usage increases with smaller latencies, as the
+sound server needs to refill the buffers more often, and in smaller
+parts. It is also mostly impossible to reach better values without
+giving the soundserver realtime priority, as otherwise you'll often get
+drop-outs.
+</para>
+
+<para>
+However, it is realistic to do something like 3 fragments with 256 bytes
+each, which would make this value 4.4 ms. With 4.4ms delay the idle
+<acronym>CPU</acronym> usage of &arts; would be about 7.5%. With 40ms delay, it would be
+about 3% (of a PII-350, and this value may depend on your soundcard,
+kernel version and others).
+</para>
+
+<para>
+Then there is the time it takes the pling sound to get from the speakers
+to your ear. Suppose your distance from the speakers is 2 meters. Sound
+travels at a speed of 330 meters per second. So we can approximate this
+time with 6 ms.
+</para>
+
+</sect2>
+
+<sect2 id="latency-streaming">
+<title>Latency in Streaming Applications</title>
+
+<para>
+Streaming applications are those that produce their sound themselves.
+Assume a game, which outputs a constant stream of samples, and should
+now be adapted to replay things via &arts;. To have an example: when I
+press a key, the figure which I am playing jumps, and a boing sound is
+played.
+</para>
+
+<para>
+First of all, you need to know how &arts; does streaming. Its very
+similar to the I/O with the soundcard. The game sends some packets with
+samples to the sound server. Lets say three packets. As soon as the
+sound server is done with the first packet, it sends a confirmation back
+to the game that this packet is done.
+</para>
+
+<para>
+The game creates another packet of sound and sends it to the server.
+Meanwhile the server starts consuming the second sound packet, and so
+on. The latency here looks similar like in the simple case:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+The time until the kernel has notified the X11 server that a key was
+pressed.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The time until the X11 server has notified the game that a key was
+pressed.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The time until the game has decided that this key is worth playing a
+boing.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The time until the packet of sound in which the game has started putting
+the boing sound is reaching the sound server.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The time it takes for the boing (which the soundserver starts mixing to
+the other output at once) to go through the buffered data, until it
+really reaches the position where the soundcard plays.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The time it takes the boing sound from the speakers to
+reach your ear.
+</para>
+</listitem>
+
+</itemizedlist>
+
+<para>
+The external latencies, as above, are beyond the scope of this document.
+</para>
+
+<para>
+Obviously, the streaming latency depends on the time it takes all
+packets that are used for streaming to be played once. So it is (number
+of packets)*(size of each packet)/(samplingrate * (size of each sample))
+</para>
+
+<para>
+As you see that is the same formula as applies for the
+fragments. However for games, it makes no sense to do such small delays
+as above. I'd say a realistic configuration for games would be 2048
+bytes per packet, use 3 packets. The resulting latency would be 35ms.
+</para>
+
+<para>
+This is based on the following: assume that the game renders 25 frames
+per second (for the display). It is probably safe to assume that you
+won't notice a difference of sound output of one frame. Thus 1/25 second
+delay for streaming is acceptable, which in turn means 40ms would be
+okay.
+</para>
+
+<para>
+Most people will also not run their games with realtime priority, and
+the danger of drop-outs in the sound is not to be neglected. Streaming
+with 3 packets a 256 bytes is possible (I tried that) - but causes a lot
+of <acronym>CPU</acronym> usage for streaming.
+</para>
+
+<para>
+For server side latencies, you can calculate these exactly as above.
+</para>
+
+</sect2>
+
+<sect2 id="cpu-usage">
+<title>Some <acronym>CPU</acronym> usage considerations</title>
+
+<para>
+There are a lot of factors which influence _<acronym>CPU</acronym> usage
+in a complex scenario, with some streaming applications and some others,
+some plugins on the server etc. To name a few:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+Raw <acronym>CPU</acronym> usage by the calculations necessary.
+</para>
+</listitem>
+
+<listitem>
+<para>
+&arts; internal scheduling overhead - how &arts; decides when which
+module should calculate what.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Integer to float conversion overhead.
+</para>
+</listitem>
+
+<listitem>
+<para>
+&MCOP;0 protocol overhead.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Kernel: process/context switching.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Kernel: communication overhead
+</para>
+</listitem>
+</itemizedlist>
+
+<para>
+For raw <acronym>CPU</acronym> usage for calculations, if you play two
+streams, simultaneously you need to do additions. If you apply a filter,
+some calculations are involved. To have a simplified example, adding two
+streams involves maybe four <acronym>CPU</acronym> cycles per addition,
+on a 350Mhz processor, this is 44100*2*4/350000000 = 0.1%
+<acronym>CPU</acronym> usage.
+</para>
+
+<para>
+&arts; internal scheduling: &arts; needs to decide which plugin when
+calculates what. This takes time. Take a profiler if you are interested
+in that. Generally what can be said is: the less realtime you do
+(&ie;. the larger blocks can be calculated at a time) the less
+scheduling overhead you have. Above calculating blocks of 128 samples at
+a time (thus using fragment sizes of 512 bytes) the scheduling overhead
+is probably not worth thinking about it.
+</para>
+
+<para>
+Integer to float conversion overhead: &arts; uses floats internally as
+data format. These are easy to handle and on recent processors not
+slower than integer operations. However, if there are clients which play
+data which is not float (like a game that should do its sound output via
+&arts;), it needs to be converted. The same applies if you want to
+replay the sounds on your soundcard. The soundcard wants integers, so
+you need to convert.
+</para>
+
+<para>
+Here are numbers for a Celeron, approx. ticks per sample, with -O2 +egcs
+2.91.66 (taken by Eugene Smith <email>hamster@null.ru</email>). This is
+of course highly processor dependant:
+</para>
+
+<programlisting>
+convert_mono_8_float: 14
+convert_stereo_i8_2float: 28
+convert_mono_16le_float: 40
+interpolate_mono_16le_float: 200
+convert_stereo_i16le_2float: 80
+convert_mono_float_16le: 80
+</programlisting>
+
+<para>
+So that means 1% <acronym>CPU</acronym> usage for conversion and 5% for
+interpolation on this 350 MHz processor.
+</para>
+
+<para>
+&MCOP; protocol overheadL &MCOP; does, as a rule of thumb, 9000
+invocations per second. Much of this is not &MCOP;s fault, but relates
+to the two kernel causes named below. However, this gives a base to do
+calculations what the cost of streaming is.
+</para>
+
+<para>
+Each data packet transferred through streaming can be considered one
+&MCOP; invocation. Of course large packets are slower than 9000
+packets/s, but its about the idea.
+</para>
+
+<para>
+Suppose you use packet sizes of 1024 bytes. Thus, to transfer a stream
+with 44kHz stereo, you need to transfer 44100*4/1024 = 172 packets per
+second. Suppose you could with 100% cpu usage transfer 9000 packets,
+then you get (172*100)/9000 = 2% <acronym>CPU</acronym> usage due to
+streaming with 1024 byte packets.
+</para>
+
+<para>
+That are approximations. However, they show, that you would be much
+better off (if you can afford it for the latency), to use for instance
+packets of 4096 bytes. We can make a compact formula here, by
+calculating the packet size which causes 100% <acronym>CPU</acronym> usage as
+44100*4/9000 = 19.6 samples, and thus getting the quick formula:
+</para>
+
+<para>
+streaming <acronym>CPU</acronym> usage in percent = 1960/(your packet size)
+</para>
+
+<para>
+which gives us 0.5% <acronym>CPU</acronym> usage when streaming with 4096 byte packets.
+</para>
+
+<para>
+Kernel process/context switching: this is part of the &MCOP; protocol
+overhead. Switching between two processes takes time. There is new
+memory mapping, the caches are invalid, whatever else (if there is a
+kernel expert reading this - let me know what exactly are the causes).
+This means: it takes time.
+</para>
+
+<para>
+I am not sure how many context switches &Linux; can do per second, but
+that number isn't infinite. Thus, of the &MCOP; protocol overhead I
+suppose quite a bit is due to context switching. In the beginning of
+&MCOP;, I did tests to use the same communication inside one process,
+and it was much faster (four times as fast or so).
+</para>
+
+<para>
+Kernel: communication overhead: This is part of the &MCOP; protocol
+overhead. Transferring data between processes is currently done via
+sockets. This is convenient, as the usual select() methods can be used
+to determine when a message has arrived. It can also be combined with
+other I/O sources as audio I/O, X11 server or whatever else easily.
+</para>
+
+<para>
+However, those read and write calls cost certainly processor cycles. For
+small invocations (such as transferring one midi event) this is probably
+not so bad, for large invocations (such as transferring one video frame
+with several megabytes) this is clearly a problem.
+</para>
+
+<para>
+Adding the usage of shared memory to &MCOP; where appropriate is
+probably the best solution. However it should be done transparent to the
+application programmer.
+</para>
+
+<para>
+Take a profiler or do other tests to find out how much exactly
+current audio streaming is impacted by the not using sharedmem. However,
+its not bad, as audio streaming (replaying mp3) can be done with 6%
+total <acronym>CPU</acronym> usage for &artsd; and
+<application>artscat</application> (and 5% for the mp3
+decoder). However, this includes all things from the necessary
+calculations up do the socket overhead, thus I'd say in this setup you
+could perhaps save 1% by using sharedmem.
+</para>
+
+</sect2>
+
+<sect2 id="hard-numbers">
+<title>Some Hard Numbers</title>
+
+<para>
+These are done with the current development snapshot. I also wanted to
+try out the real hard cases, so this is not what everyday applications
+should use.
+</para>
+
+<para>
+I wrote an application called streamsound which sends streaming data to
+&arts;. Here it is running with realtime priority (without problems),
+and one small serverside (volume-scaling and clipping) plugin:
+</para>
+
+<programlisting>
+ 4974 stefan 20 0 2360 2360 1784 S 0 17.7 1.8 0:21 artsd
+ 5016 stefan 20 0 2208 2208 1684 S 0 7.2 1.7 0:02 streamsound
+ 5002 stefan 20 0 2208 2208 1684 S 0 6.8 1.7 0:07 streamsound
+ 4997 stefan 20 0 2208 2208 1684 S 0 6.6 1.7 0:07 streamsound
+</programlisting>
+
+<para>
+Each of them is streaming with 3 fragments a 1024 bytes (18 ms). There
+are three such clients running simultaneously. I know that that does
+look a bit too much, but as I said: take a profiler and find out what
+costs time, and if you like, improve it.
+</para>
+
+<para>
+However, I don't think using streaming like that is realistic or makes
+sense. To take it even more to the extreme, I tried what would be the
+lowest latency possible. Result: you can do streaming without
+interruptions with one client application, if you take 2 fragments of
+128 bytes between aRts and the soundcard, and between the client
+application and aRts. This means that you have a total maximum latency
+of 128*4/44100*4 = 3 ms, where 1.5 ms is generated due to soundcard I/O
+and 1.5 ms is generated through communication with &arts;. Both
+applications need to run realtimed.
+</para>
+
+<para>
+But: this costs an enormous amount of
+<acronym>CPU</acronym>. This example cost you about 45% of my
+P-II/350. I also starts to click if you start top, move windows on your
+X11 display or do disk I/O. All these are kernel issues. The problem is
+that scheduling two or more applications with realtime priority cost you
+an enormous amount of effort, too, even more if the communicate, notify
+each other &etc;.
+</para>
+
+<para>
+Finally, a more real life example. This is &arts; with artsd and one
+artscat (one streaming client) running 16 fragments a 4096 bytes:
+</para>
+
+<programlisting>
+ 5548 stefan 12 0 2364 2364 1752 R 0 4.9 1.8 0:03 artsd
+ 5554 stefan 3 0 752 752 572 R 0 0.7 0.5 0:00 top
+ 5550 stefan 2 0 2280 2280 1696 S 0 0.5 1.7 0:00 artscat
+</programlisting>
+
+</sect2>
+</sect1>
+
+<!-- TODO
+
+<sect1 id="dynamic-instantiation">
+<title>Dynamic Instantiation</title>
+<para>
+</para>
+</sect1>
+
+-->
+
+<sect1 id="busses">
+<title>Busses</title>
+
+<para>
+Busses are dynamically built connections that transfer audio. Basically,
+there are some uplinks and some downlinks. All signals from the uplinks
+are added and send to the downlinks.
+</para>
+
+<para>
+Busses as currently implemented operate in stereo, so you can only
+transfer stereo data over busses. If you want mono data, well, transfer
+it only over one channel and set the other to zero or whatever. What
+you need to to, is to create one or more Synth&lowbar;BUS&lowbar;UPLINK
+objects and tell them a bus name, to which they should talk (&eg;
+<quote>audio</quote> or <quote>drums</quote>). Simply throw the data in
+there.
+</para>
+
+<para>
+Then, you'll need to create one or more Synth&lowbar;BUS&lowbar;DOWNLINK
+objects, and tell them the bus name (<quote>audio</quote> or
+<quote>drums</quote> ... if it matches, the data will get through), and
+the mixed data will come out again.
+</para>
+
+<para>
+The uplinks and downlinks can reside in different structures, you can
+even have different &arts-builder;s running and start an uplink in one
+and receive the data from the other with a downlink.
+</para>
+
+<para>
+What is nice about busses is, that they are fully dynamic. Clients can
+plug in and out on the fly. There should be no clicking or noise as this
+happens.
+</para>
+
+<para>
+Of course, you should not plug out a client playing a signal, since it
+will probably not be a zero level when plugged out the bus, and then it
+will click.
+</para>
+</sect1>
+
+<!-- TODO
+<sect1 id="network-ransparency">
+<title>Network Transparency</title>
+<para>
+</para>
+</sect1>
+
+<sect1 id="security">
+<title>Security</title>
+<para>
+</para>
+</sect1>
+
+
+<sect1 id="effects">
+<title>Effects and Effect Stacks</title>
+<para>
+</para>
+</sect1>
+
+-->
+<sect1 id="trader">
+<title>Trader</title>
+
+<para>
+&arts;/&MCOP; heavily relies on splitting up things into small
+components. This makes things very flexible, as you can extend the
+system easily by adding new components, which implement new effects,
+fileformats, oscillators, gui elements, ... As almost everything is a
+component, almost everything can be extended easily, without changing
+existing sources. New components can be simply loaded dynamically to
+enhance already existing applications.
+</para>
+
+<para>
+However, to make this work, two things are required:
+</para>
+
+<itemizedlist>
+
+<listitem>
+<para>
+Components must advertise themselves - they must describe what great
+things they offer, so that applications will be able to use them.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Applications must actively look for components that they could use,
+instead of using always the same thing for some task.
+</para>
+</listitem>
+
+</itemizedlist>
+
+<para>
+The combination of this: components which say <quote>here I am, I am
+cool, use me</quote>, and applications (or if you like, other
+components) which go out and look which component they could use to get
+a thing done, is called trading.
+</para>
+
+<para>
+In &arts;, components describe themselves by specifying values that they
+<quote>support</quote> for properties. A typical property for a
+file-loading component could be the extension of the files that it can
+process. Typical values could be <literal
+role="extension">wav</literal>, <literal role="extension">aiff</literal>
+or <literal role="extension">mp3</literal>.
+</para>
+
+<para>
+In fact, every component may choose to offer many different values for
+one property. So one single component could offer reading both, <literal
+role="extension">wav</literal> and <literal
+role="extension">aiff</literal> files, by specifying that it supports
+these values for the property <quote>Extension</quote>.
+</para>
+
+<para>
+To do so, a component has to place a <literal
+role="extension">.mcopclass</literal> file at an appropriate place,
+containing the properties it supports, for our example, this could look
+like this (and would be installed in
+<filename><replaceable>componentdir</replaceable>/Arts/WavPlayObject.mcopclass</filename>):
+</para>
+
+<programlisting>
+Interface=Arts::WavPlayObject,Arts::PlayObject,Arts::SynthModule,Arts::Object
+Author="Stefan Westerfeld &lt;stefan@space.twc.de&gt;"
+URL="http://www.arts-project.org"
+Extension=wav,aiff
+MimeType=audio/x-wav,audio/x-aiff
+</programlisting>
+
+<para>
+It is important that the filename of the <literal
+role="extension">.mcopclass</literal>-file also says what the interface
+of the component is called like. The trader doesn't look at the contents
+at all, if the file (like here) is called
+<filename>Arts/WavPlayObject.mcopclass</filename>, the component
+interface is called <interfacename>Arts::WavPlayObject</interfacename>
+(modules map to folders).
+</para>
+
+<para>
+To look for components, there are two interfaces (which are defined in
+<filename>core.idl</filename>, so you have them in every application),
+called <interfacename>Arts::TraderQuery</interfacename> and
+<interfacename>Arts::TraderOffer</interfacename>. You to go on a
+<quote>shopping tour</quote> for components like this:
+</para>
+
+<orderedlist>
+<listitem>
+<para>
+Create a query object:
+</para>
+<programlisting>
+ Arts::TraderQuery query;
+</programlisting>
+</listitem>
+
+<listitem>
+<para>
+Specify what you want. As you saw above, components describe themselves
+using properties, for which they offer certain values. So specifying
+what you want is done by selecting components that support a certain
+value for a property. This is done using the supports method of a
+TraderQuery:
+</para>
+
+<programlisting>
+ query.supports("Interface","Arts::PlayObject");
+ query.supports("Extension","wav");
+</programlisting>
+</listitem>
+
+<listitem>
+<para>
+Finally, perform the query using the query method. Then, you'll
+(hopefully) get some offers:
+</para>
+
+<programlisting>
+ vector&lt;Arts::TraderOffer&gt; *offers = query.query();
+</programlisting>
+</listitem>
+
+<listitem>
+<para>
+Now you can examine what you found. Important is the interfaceName
+method of TraderOffer, which will tell you the name of the component,
+that matched the query. You can also find out further properties by
+getProperty. The following code will simply iterate through all
+components, print their interface names (which could be used for
+creation), and delete the results of the query again:
+</para>
+<programlisting>
+ vector&lt;Arts::TraderOffer&gt;::iterator i;
+ for(i = offers-&gt;begin(); i != offers-&gt;end(); i++)
+ cout &lt;&lt; i-&gt;interfaceName() &lt;&lt; endl;
+ delete offers;
+</programlisting>
+</listitem>
+</orderedlist>
+
+<para>
+For this kind of trading service to be useful, it is important to
+somehow agree on what kinds of properties components should usually
+define. It is essential that more or less all components in a certain
+area use the same set of properties to describe themselves (and the same
+set of values where applicable), so that applications (or other
+components) will be able to find them.
+</para>
+
+<para>
+Author (type string, optional): This can be used to ultimately let the
+world know that you wrote something. You can write anything you like in
+here, e-mail address is of course helpful.
+</para>
+
+<para>
+Buildable (type boolean, recommended): This indicates whether the
+component is usable with <acronym>RAD</acronym> tools (such as
+&arts-builder;) which use components by assigning properties and
+connecting ports. It is recommended to set this value to true for
+almost any signal processing component (such as filters, effects,
+oscillators, ...), and for all other things which can be used in
+<acronym>RAD</acronym> like fashion, but not for internal stuff like for
+instance <interfacename>Arts::InterfaceRepo</interfacename>.
+</para>
+
+<para>
+Extension (type string, used where relevant): Everything dealing with
+files should consider using this. You should put the lowercase version
+of the file extension without the <quote>.</quote> here, so something
+like <userinput>wav</userinput> should be fine.
+</para>
+
+<para>
+Interface (type string, required): This should include the full list of
+(useful) interfaces your components supports, probably including
+<interfacename>Arts::Object</interfacename> and if applicable
+<interfacename>Arts::SynthModule</interfacename>.
+</para>
+
+<para>
+Language (type string, recommended): If you want your component to be
+dynamically loaded, you need to specify the language here. Currently,
+the only allowed value is <userinput>C++</userinput>, which means the
+component was written using the normal C++ <acronym>API</acronym>. If
+you do so, you'll also need to set the <quote>Library</quote> property
+below.
+</para>
+
+<para>
+Library (type string, used where relevant): Components written in C++
+can be dynamically loaded. To do so, you have to compile them into a
+dynamically loadable libtool (<literal role="extension">.la</literal>)
+module. Here, you can specify the name of the <literal
+role="extension">.la</literal>-File that contains your component.
+Remember to use REGISTER_IMPLEMENTATION (as always).
+</para>
+
+<para>
+MimeType (type string, used where relevant): Everything dealing with
+files should consider using this. You should put the lowercase version
+of the standard mimetype here, for instance
+<userinput>audio/x-wav</userinput>.
+</para>
+
+<para>
+&URL; (type string, optional): If you like to let people know where they
+can find a new version of the component (or a homepage or anything), you
+can do it here. This should be standard &HTTP; or &FTP; &URL;.
+</para>
+
+</sect1>
+
+<!-- TODO
+<sect1 id="midi-synthesis">
+<title><acronym>MIDI</acronym> Synthesis</title>
+<para>
+</para>
+</sect1>
+
+<sect1 id="instruments">
+<title>Instruments</title>
+<para>
+</para>
+</sect1>
+
+<sect1 id="session-management">
+<title>Session Management</title>
+<para>
+</para>
+</sect1>
+
+<sect1 id="full-duplex">
+<title>Full duplex Audio</title>
+<para>
+</para>
+</sect1>
+-->
+
+<sect1 id="namespaces">
+<title>Namespaces in &arts;</title>
+
+<sect2 id="namespaces-intro">
+<title>Introduction</title>
+
+<para>
+Each namespace declaration corresponds to a <quote>module</quote>
+declaration in the &MCOP; &IDL;.
+</para>
+
+<programlisting>
+// mcop idl
+
+module M {
+ interface A
+ {
+ }
+};
+
+interface B;
+</programlisting>
+
+<para>
+In this case, the generated C++ code for the &IDL; snippet would look
+like this:
+</para>
+
+<programlisting>
+// C++ header
+
+namespace M {
+ /* declaration of A_base/A_skel/A_stub and similar */
+ class A { // Smartwrapped reference class
+ /* [...] */
+ };
+}
+
+/* declaration of B_base/B_skel/B_stub and similar */
+class B {
+ /* [...] */
+};
+</programlisting>
+
+<para>
+So when referring the classes from the above example in your C++ code,
+you would have to write <classname>M::A</classname>, but only
+B. However, you can of course use <quote>using M</quote> somewhere -
+like with any namespace in C++.
+</para>
+
+</sect2>
+
+<sect2 id="namespaces-how">
+<title>How &arts; uses namespaces</title>
+
+<para>
+There is one global namespace called <quote>Arts</quote>, which all
+programs and libraries that belong to &arts; itself use to put their
+declarations in. This means, that when writing C++ code that depends on
+&arts;, you normally have to prefix every class you use with
+<classname>Arts::</classname>, like this:
+</para>
+
+<programlisting>
+int main(int argc, char **argv)
+{
+ Arts::Dispatcher dispatcher;
+ Arts::SimpleSoundServer server(Arts::Reference("global:Arts_SimpleSoundServer"));
+
+ server.play("/var/foo/somefile.wav");
+</programlisting>
+
+<para>
+The other alternative is to write a using once, like this:
+</para>
+
+<programlisting>
+using namespace Arts;
+
+int main(int argc, char **argv)
+{
+ Dispatcher dispatcher;
+ SimpleSoundServer server(Reference("global:Arts_SimpleSoundServer"));
+
+ server.play("/var/foo/somefile.wav");
+ [...]
+</programlisting>
+
+<para>
+In &IDL; files, you don't exactly have a choice. If you are writing code
+that belongs to &arts; itself, you'll have to put it into module &arts;.
+</para>
+
+<programlisting>
+// IDL File for aRts code:
+#include &lt;artsflow.idl&gt;
+module Arts { // put it into the Arts namespace
+ interface Synth_TWEAK : SynthModule
+ {
+ in audio stream invalue;
+ out audio stream outvalue;
+ attribute float tweakFactor;
+ };
+};
+</programlisting>
+
+<para>
+If you write code that doesn't belong to &arts; itself, you should not
+put it into the <quote>Arts</quote> namespace. However, you can make an
+own namespace if you like. In any case, you'll have to prefix classes
+you use from &arts;.
+</para>
+
+<programlisting>
+// IDL File for code which doesn't belong to aRts:
+#include &lt;artsflow.idl&gt;
+
+// either write without module declaration, then the generated classes will
+// not use a namespace:
+interface Synth_TWEAK2 : Arts::SynthModule
+{
+ in audio stream invalue;
+ out audio stream outvalue;
+ attribute float tweakFactor;
+};
+
+// however, you can also choose your own namespace, if you like, so if you
+// write an application "PowerRadio", you could for instance do it like this:
+module PowerRadio {
+ struct Station {
+ string name;
+ float frequency;
+ };
+
+ interface Tuner : Arts::SynthModule {
+ attribute Station station; // no need to prefix Station, same module
+ out audio stream left, right;
+ };
+};
+</programlisting>
+
+</sect2>
+
+<sect2 id="namespaces-implementation">
+<title>Internals: How the Implementation Works</title>
+
+<para>
+Often, in interfaces, casts, method signatures and similar, &MCOP; needs
+to refer to names of types or interfaces. These are represented as
+string in the common &MCOP; datastructures, while the namespace is
+always fully represented in the C++ style. This means the strings would
+contain <quote>M::A</quote> and <quote>B</quote>, following the example
+above.
+</para>
+
+<para>
+Note this even applies if inside the &IDL; text the namespace qualifiers
+were not given, since the context made clear which namespace the
+interface <interfacename>A</interfacename> was meant to be used in.
+</para>
+
+</sect2>
+</sect1>
+
+<sect1 id="threads">
+<title>Threads in &arts;</title>
+
+<sect2 id="threads-basics">
+<title>Basics</title>
+
+<para>
+Using threads isn't possible on all platforms. This is why &arts; was
+originally written without using threading at all. For almost all
+problems, for each threaded solution to the problem, there is a
+non-threaded solution that does the same.
+</para>
+
+<para>
+For instance, instead of putting audio output in a separate thread, and
+make it blocking, &arts; uses non-blocking audio output, and figures out
+when to write the next chunk of data using
+<function>select()</function>.
+</para>
+
+<para>
+However, &arts; (in very recent versions) at least provides support for
+people who do want to implement their objects using threads. For
+instance, if you already have code for an <literal
+role="extension">mp3</literal> player, and the code expects the <literal
+role="extension">mp3</literal> decoder to run in a separate thread, it's
+usually the easiest thing to do to keep this design.
+</para>
+
+<para>
+The &arts;/&MCOP; implementation is built along sharing state between
+separate objects in obvious and non-obvious ways. A small list of shared
+state includes:
+</para>
+
+<itemizedlist>
+<listitem><para>
+The Dispatcher object which does &MCOP; communication.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The Reference counting (Smartwrappers).
+</para>
+</listitem>
+
+<listitem>
+<para>
+The IOManager which does timer and fd watches.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The ObjectManager which creates objects and dynamically loads plugins.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The FlowSystem which calls calculateBlock in the appropriate situations.
+</para>
+</listitem>
+</itemizedlist>
+
+<para>
+All of the above objects don't expect to be used concurrently (&ie;
+called from separate threads at the same time). Generally there are two
+ways of solving this:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+Require the caller of any functions on this objects to
+acquire a lock before using them.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Making these objects really threadsafe and/or create
+per-thread instances of them.
+</para>
+</listitem>
+</itemizedlist>
+
+<para>
+&arts; follows the first approach: you will need a lock whenever you talk to
+any of these objects. The second approach is harder to do. A hack which
+tries to achieve this is available at
+<ulink url="http://space.twc.de/~stefan/kde/download/arts-mt.tar.gz">
+http://space.twc.de/~stefan/kde/download/arts-mt.tar.gz</ulink>, but for
+the current point in time, a minimalistic approach will probably work
+better, and cause less problems with existing applications.
+</para>
+
+</sect2>
+<sect2 id="threads-locking">
+<title>When/how to acquire the lock?</title>
+
+<para>
+You can get/release the lock with the two functions:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+<ulink
+url="http://space.twc.de/~stefan/kde/arts-mcop-doc/arts-reference/headers/Arts__Dispatcher.html#lock"><function>Arts::Dispatcher::lock()</function></ulink>
+</para>
+</listitem>
+<listitem>
+<para>
+<ulink
+url="http://space.twc.de/~stefan/kde/arts-mcop-doc/arts-reference/headers/Arts__Dispatcher.html#unlock"><function>Arts::Dispatcher::unlock()</function></ulink>
+</para>
+</listitem>
+</itemizedlist>
+
+<para>
+Generally, you don't need to acquire the lock (and you shouldn't try to
+do so), if it is already held. A list of conditions when this is the
+case is:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+You receive a callback from the IOManager (timer or fd).
+</para>
+</listitem>
+
+<listitem>
+<para>
+You get call due to some &MCOP; request.
+</para>
+</listitem>
+
+<listitem>
+<para>
+You are called from the NotificationManager.
+</para>
+</listitem>
+
+<listitem>
+<para>
+You are called from the FlowSystem (calculateBlock)
+</para>
+</listitem>
+</itemizedlist>
+
+<para>
+There are also some exceptions of functions. which you can only call in
+the main thread, and for that reason you will never need a lock to call
+them:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+Constructor/destructor of Dispatcher/IOManager.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<methodname>Dispatcher::run()</methodname> /
+<methodname>IOManager::run()</methodname>
+</para>
+</listitem>
+
+<listitem>
+<para><methodname>IOManager::processOneEvent()</methodname></para>
+</listitem>
+</itemizedlist>
+
+<para>
+But that is it. For everything else that is somehow related to &arts;,
+you will need to get the lock, and release it again when
+done. Always. Here is a simple example:
+</para>
+
+<programlisting>
+class SuspendTimeThread : Arts::Thread {
+public:
+ void run() {
+ /*
+ * you need this lock because:
+ * - constructing a reference needs a lock (as global: will go to
+ * the object manager, which might in turn need the GlobalComm
+ * object to look up where to connect to)
+ * - assigning a smartwrapper needs a lock
+ * - constructing an object from reference needs a lock (because it
+ * might need to connect a server)
+ */
+ Arts::Dispatcher::lock();
+ Arts::SoundServer server = Arts::Reference("global:Arts_SoundServer");
+ Arts::Dispatcher::unlock();
+
+ for(;;) { /*
+ * you need a lock here, because
+ * - dereferencing a smartwrapper needs a lock (because it might
+ * do lazy creation)
+ * - doing an MCOP invocation needs a lock
+ */
+ Arts::Dispatcher::lock();
+ long seconds = server.secondsUntilSuspend();
+ Arts::Dispatcher::unlock();
+
+ printf("seconds until suspend = %d",seconds);
+ sleep(1);
+ }
+ }
+}
+</programlisting>
+
+
+</sect2>
+
+<sect2 id="threads-classes">
+<title>Threading related classes</title>
+
+<para>
+The following threading related classes are currently available:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+<ulink
+url="http://www.arts-project.org/doc/headers/Arts__Thread.html"><classname>
+Arts::Thread</classname></ulink> - which encapsulates a thread.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<ulink url="http://www.arts-project.org/doc/headers/Arts__Mutex.html">
+<classname>Arts::Mutex</classname></ulink> - which encapsulates a mutex.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<ulink
+url="http://www.arts-project.org/doc/headers/Arts__ThreadCondition.html">
+<classname>Arts::ThreadCondition</classname></ulink> - which provides
+support to wake up threads which are waiting for a certain condition to
+become true.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<ulink
+url="http://www.arts-project.org/doc/headers/Arts__SystemThreads.html"><classname>Arts::SystemThreads</classname></ulink>
+- which encapsulates the operating system threading layer (which offers
+a few helpful functions to application programmers).
+</para>
+</listitem>
+</itemizedlist>
+
+<para>
+See the links for documentation.
+</para>
+
+</sect2>
+</sect1>
+
+<sect1 id="references-errors">
+<title>References and Error Handling</title>
+
+<para>
+&MCOP; references are one of the most central concepts in &MCOP;
+programming. This section will try to describe how exactly references
+are used, and will especially also try to cover cases of failure (server
+crashes).
+</para>
+
+<sect2 id="references-properties">
+<title>Basic properties of references</title>
+
+<itemizedlist>
+<listitem>
+<para>
+An &MCOP; reference is not an object, but a reference to an object: Even
+though the following declaration
+
+<programlisting>
+ Arts::Synth_PLAY p;
+</programlisting>
+
+looks like a definition of an object, it only declares a reference to an
+object. As C++ programmer, you might also think of it as Synth_PLAY *, a
+kind of pointer to a Synth_PLAY object. This especially means, that p
+can be the same thing as a NULL pointer.
+</para>
+</listitem>
+
+<listitem>
+<para>
+You can create a NULL reference by assigning it explicitly
+</para>
+<programlisting>
+ Arts::Synth_PLAY p = Arts::Synth_PLAY::null();
+</programlisting>
+</listitem>
+
+<listitem>
+<para>
+Invoking things on a NULL reference leads to a core dump
+</para>
+<programlisting>
+ Arts::Synth_PLAY p = Arts::Synth_PLAY::null();
+ string s = p.toString();
+</programlisting>
+<para>
+will lead to a core dump. Comparing this to a pointer, it is essentially
+the same as
+<programlisting>
+ QWindow* w = 0;
+ w-&gt;show();
+</programlisting>
+which every C++ programmer would know to avoid.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Uninitialized objects try to lazy-create themselves upon first use
+</para>
+
+<programlisting>
+ Arts::Synth_PLAY p;
+ string s = p.toString();
+</programlisting>
+<para>
+is something different than dereferencing a NULL pointer. You didn't tell
+the object at all what it is, and now you try to use it. The guess here
+is that you want to have a new local instance of a Arts::Synth_PLAY
+object. Of course you might have wanted something else (like creating the
+object somewhere else, or using an existing remote object). However, it
+is a convenient short cut to creating objects. Lazy creation will not work
+once you assigned something else (like a null reference).
+</para>
+
+<para>
+The equivalent C++ terms would be
+<programlisting>
+ QWidget* w;
+ w-&gt;show();
+</programlisting>
+
+which obviously in C++ just plain segfaults. So this is different here.
+This lazy creation is tricky especially as not necessarily an implementation
+exists for your interface.
+</para>
+
+<para>
+For instance, consider an abstract thing like a
+Arts::PlayObject. There are certainly concrete PlayObjects like those for
+playing mp3s or wavs, but
+
+<programlisting>
+ Arts::PlayObject po;
+ po.play();
+</programlisting>
+
+will certainly fail. The problem is that although lazy creation kicks
+in, and tries to create a PlayObject, it fails, because there are only
+things like Arts::WavPlayObject and similar. Thus, use lazy creation
+only when you are sure that an implementation exists.
+</para>
+</listitem>
+
+<listitem>
+<para>
+References may point to the same object
+</para>
+
+<programlisting>
+ Arts::SimpleSoundServer s = Arts::Reference("global:Arts_SimpleSoundServer");
+ Arts::SimpleSoundServer s2 = s;
+</programlisting>
+
+<para>
+creates two references referring to the same object. It doesn't copy any
+value, and doesn't create two objects.
+</para>
+</listitem>
+
+<listitem>
+<para>
+All objects are reference counted So once an object isn't referred any
+longer by any references, it gets deleted. There is no way to
+explicitly delete an object, however, you can use something like this
+<programlisting>
+ Arts::Synth_PLAY p;
+ p.start();
+ [...]
+ p = Arts::Synth_PLAY::null();
+</programlisting>
+to make the Synth_PLAY object go away in the end. Especially, it should never
+be necessary to use new and delete in conjunction with references.
+</para>
+</listitem>
+</itemizedlist>
+
+</sect2>
+
+<sect2 id="references-failure">
+<title>The case of failure</title>
+
+<para>
+As references can point to remote objects, the servers containing these
+objects can crash. What happens then?
+</para>
+
+<itemizedlist>
+
+<listitem>
+<para>
+A crash doesn't change whether a reference is a null reference. This
+means that if <function>foo.isNull()</function> was
+<returnvalue>true</returnvalue> before a server crash then it is also
+<returnvalue>true</returnvalue> after a server crash (which is
+clear). It also means that if <function>foo.isNull()</function> was
+<returnvalue>false</returnvalue> before a server crash (foo referred to
+an object) then it is also <returnvalue>false</returnvalue> after the
+server crash.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Invoking methods on a valid reference stays safe
+Suppose the server containing the object calc crashed. Still calling things
+like
+<programlisting>
+ int k = calc.subtract(i,j)
+</programlisting>
+are safe. Obviously subtract has to return something here, which it
+can't because the remote object no longer exists. In this case (k == 0)
+would be true. Generally, operations try to return something
+<quote>neutral</quote> as result, such as 0.0, a null reference for
+objects or empty strings, when the object no longer exists.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Checking <function>error()</function> reveals whether something worked.
+</para>
+
+<para>
+In the above case,
+<programlisting>
+ int k = calc.subtract(i,j)
+ if(k.error()) {
+ printf("k is not i-j!\n");
+ }
+</programlisting>
+would print out <computeroutput>k is not i-j</computeroutput> whenever
+the remote invocation didn't work. Otherwise <varname>k</varname> is
+really the result of the subtract operation as performed by the remote
+object (no server crash). However, for methods doing things like
+deleting a file, you can't know for sure whether it really happened. Of
+course it happened if <function>.error()</function> is
+<returnvalue>false</returnvalue>. However, if
+<function>.error()</function> is <returnvalue>true</returnvalue>, there
+are two possibilities:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+The file got deleted, and the server crashed just after deleting it, but
+before transferring the result.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The server crashed before being able to delete the file.
+</para>
+</listitem>
+</itemizedlist>
+</listitem>
+
+<listitem>
+<para>
+Using nested invocations is dangerous in crash resistant programs
+</para>
+
+<para>
+Using something like
+<programlisting>
+ window.titlebar().setTitle("foo");
+</programlisting>
+is not a good idea. Suppose you know that window contains a valid Window
+reference. Suppose you know that <function>window.titlebar()</function>
+will return a Titlebar reference because the Window object is
+implemented properly. However, still the above statement isn't safe.
+</para>
+
+<para>
+What could happen is that the server containing the Window object has
+crashed. Then, regardless of how good the Window implementation is, you
+will get a null reference as result of the window.titlebar()
+operation. And then of course invoking setTitle on that null reference
+will lead to a crash as well.
+</para>
+
+<para>
+So a safe variant of this would be
+<programlisting>
+ Titlebar titlebar = window.titlebar();
+ if(!window.error())
+ titlebar.setTitle("foo");
+</programlisting>
+add the appropriate error handling if you like. If you don't trust the
+Window implementation, you might as well use
+<programlisting>
+ Titlebar titlebar = window.titlebar();
+ if(!titlebar.isNull())
+ titlebar.setTitle("foo");
+</programlisting>
+which are both safe.
+</para>
+</listitem>
+</itemizedlist>
+
+<para>
+There are other conditions of failure, such as network disconnection
+(suppose you remove the cable between your server and client while your
+application runs). However their effect is the same like a server crash.
+</para>
+
+<para>
+Overall, it is of course a consideration of policy how strictly you try
+to trap communication errors throughout your application. You might
+follow the <quote>if the server crashes, we need to debug the server
+until it never crashes again</quote> method, which would mean you need
+not bother about all these problems.
+</para>
+
+</sect2>
+
+<sect2 id="references-internals">
+<title>Internals: Distributed Reference Counting</title>
+
+<para>
+An object, to exist, must be owned by someone. If it isn't, it will
+cease to exist (more or less) immediately. Internally, ownership is
+indicated by calling <function>_copy()</function>, which increments an
+reference count, and given back by calling
+<function>_release()</function>. As soon as the reference count drops to
+zero, a delete will be done.
+</para>
+
+<para>
+As a variation of the theme, remote usage is indicated by
+<function>_useRemote()</function>, and dissolved by
+<function>_releaseRemote()</function>. These functions lead a list which
+server has invoked them (and thus owns the object). This is used in case
+this server disconnects (&ie; crash, network failure), to remove the
+references that are still on the objects. This is done in
+<function>_disconnectRemote()</function>.
+</para>
+
+<para>
+Now there is one problem. Consider a return value. Usually, the return
+value object will not be owned by the calling function any longer. It
+will however also not be owned by the caller, until the message holding
+the object is received. So there is a time of
+<quote>ownershipless</quote> objects.
+</para>
+
+<para>
+Now, when sending an object, one can be reasonable sure that as soon as
+it is received, it will be owned by somebody again, unless, again, the
+receiver dies. However this means that special care needs to be taken
+about object at least while sending, probably also while receiving, so
+that it doesn't die at once.
+</para>
+
+<para>
+The way &MCOP; does this is by <quote>tagging</quote> objects that are
+in process of being copied across the wire. Before such a copy is
+started, <function>_copyRemote</function> is called. This prevents the
+object from being freed for a while (5 seconds). Once the receiver calls
+<function>_useRemote()</function>, the tag is removed again. So all
+objects that are send over wire are tagged before transfer.
+</para>
+
+<para>
+If the receiver receives an object which is on his server, of course he
+will not <function>_useRemote()</function> it. For this special case,
+<function>_cancelCopyRemote()</function> exists to remove the tag
+manually. Other than that, there is also timer based tag removal, if
+tagging was done, but the receiver didn't really get the object (due to
+crash, network failure). This is done by the
+<classname>ReferenceClean</classname> class.
+</para>
+
+</sect2>
+
+</sect1>
+
+<sect1 id="detail-gui-elements">
+<title>&GUI; Elements</title>
+
+<para>
+&GUI; elements are currently in the experimental state. However, this
+section will describe what is supposed to happen here, so if you are a
+developer, you will be able to understand how &arts; will deal with
+&GUI;s in the future. There is some code there already, too.
+</para>
+
+<para>
+&GUI; elements should be used to allow synthesis structures to interact
+with the user. In the simplest case, the user should be able to modify
+some parameters of a structure directly (such as a gain factor which is
+used before the final play module).
+</para>
+
+<para>
+In more complex settings, one could imagine the user modifying
+parameters of groups of structures and/or not yet running structures,
+such as modifying the <acronym>ADSR</acronym> envelope of the currently
+active &MIDI; instrument. Another thing would be setting the filename of
+some sample based instrument.
+</para>
+
+<para>
+On the other hand, the user could like to monitor what the synthesizer
+is doing. There could be oscilloscopes, spectrum analyzers, volume
+meters and <quote>experiments</quote> that figure out the frequency
+transfer curve of some given filter module.
+</para>
+
+<para>
+Finally, the &GUI; elements should be able to control the whole
+structure of what is running inside &arts; and how. The user should be
+able to assign instruments to midi channels, start new effect
+processors, configure his main mixer desk (which is built of &arts;
+structures itself) to have one channel more and use another strategy for
+its equalizers.
+</para>
+
+<para>
+You see - the <acronym>GUI</acronym> elements should bring all
+possibilities of the virtual studio &arts; should simulate to the
+user. Of course, they should also gracefully interact with midi inputs
+(such as sliders should move if they get &MIDI; inputs which also change
+just that parameter), and probably even generate events themselves, to
+allow the user interaction to be recorded via sequencer.
+</para>
+
+<para>
+Technically, the idea is to have an &IDL; base class for all widgets
+(<classname>Arts::Widget</classname>), and derive a number of commonly
+used widgets from there (like <classname>Arts::Poti</classname>,
+<classname>Arts::Panel</classname>, <classname>Arts::Window</classname>,
+...).
+</para>
+
+<para>
+Then, one can implement these widgets using a toolkit, for instance &Qt;
+or Gtk. Finally, effects should build their &GUI;s out of existing
+widgets. For instance, a freeverb effect could build it's &GUI; out of
+five <classname>Arts::Poti</classname> thingies and an
+<classname>Arts::Window</classname>. So IF there is a &Qt;
+implementation for these base widgets, the effect will be able to
+display itself using &Qt;. If there is Gtk implementation, it will also
+work for Gtk (and more or less look/work the same).
+</para>
+
+<para>
+Finally, as we're using &IDL; here, &arts-builder; (or other tools) will
+be able to plug &GUI;s together visually, or autogenerate &GUI;s given
+hints for parameters, only based on the interfaces. It should be
+relatively straight forward to write a <quote>create &GUI; from
+description</quote> class, which takes a &GUI; description (containing
+the various parameters and widgets), and creates a living &GUI; object
+out of it.
+</para>
+
+<para>
+Based on &IDL; and the &arts;/&MCOP; component model, it should be easy
+to extend the possible objects which can be used for the &GUI; just as
+easy as it is to add a plugin implementing a new filter to &arts;.
+</para>
+
+</sect1>
+
+</chapter>
diff --git a/doc/artsbuilder/digitalaudio.docbook b/doc/artsbuilder/digitalaudio.docbook
new file mode 100644
index 00000000..99d24968
--- /dev/null
+++ b/doc/artsbuilder/digitalaudio.docbook
@@ -0,0 +1,14 @@
+<!-- <?xml version="1.0" ?>
+<!DOCTYPE appendix PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd">
+To validate or process this file as a standalone document, uncomment
+this prolog. Be sure to comment it out again when you are done -->
+
+<appendix id="intro-digital-audio">
+<title>Introduction to Digital Audio</title>
+
+<para>digital sampling, filters, effects, &etc;</para>
+
+</appendix>
+
+
+
diff --git a/doc/artsbuilder/faq.docbook b/doc/artsbuilder/faq.docbook
new file mode 100644
index 00000000..c5b111ec
--- /dev/null
+++ b/doc/artsbuilder/faq.docbook
@@ -0,0 +1,1112 @@
+<!-- <?xml version="1.0" ?>
+<!DOCTYPE chapter PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd">
+To validate or process this file as a standalone document, uncomment
+this prolog. Be sure to comment it out again when you are done -->
+<chapter id="faq">
+<title>Questions and answers</title>
+
+<para>
+This section answers some frequently asked questions about &arts;.
+</para>
+
+<qandaset id="faq-general">
+<title>General Questions</title>
+
+<qandaentry>
+<question>
+<para>
+Does &kde; support my sound card for audio output?
+</para>
+</question>
+
+<answer>
+<para>
+&kde; uses &arts; to play sound, and &arts; uses the &Linux; kernel
+sound drivers, either <acronym>OSS</acronym> or <acronym>ALSA</acronym>
+(using <acronym>OSS</acronym> emulation). If your sound card is
+supported by either <acronym>ALSA</acronym> or <acronym>OSS</acronym>
+and properly configured (&ie; any other &Linux; application can output
+sound), it will work. There are however some problems with some specific
+hardware, please read the <link linkend="faq-hardware-specific">section
+for hardware specific problems</link> if you're having problems with artsd
+on your machine.
+</para>
+<para>
+Meanwhile also support for various other platforms has been added. Here is
+a complete list of how the most recent version of &arts; can play sound. If
+you have an unsupported platform, please consider porting &arts; to your
+platform.
+</para>
+
+<informaltable>
+<tgroup cols="2">
+<thead>
+<row>
+<entry>&arts; audio I/O method</entry>
+<entry>Comment</entry>
+</row>
+</thead>
+
+<tbody>
+<row>
+<entry>paud</entry>
+<entry>Support for AIX Personal Audio Device</entry>
+</row>
+
+<row>
+<entry>alsa</entry>
+<entry>Linux ALSA-0.5 and ALSA-0.9 drivers</entry>
+</row>
+
+<row>
+<entry>libaudioio</entry>
+<entry>Support for generic LibAudioIO library which works on Solaris</entry>
+</row>
+
+<row>
+<entry>nas</entry>
+<entry>NAS sound server, useful for X Terminals with NAS support</entry>
+</row>
+
+<row>
+<entry>null</entry>
+<entry>Null audio device, discards sound silently</entry>
+</row>
+
+<row>
+<entry>oss</entry>
+<entry>OSS (Open Sound System) support (works on Linux, various BSDs and
+ other platforms with OSS drivers installed)</entry>
+</row>
+
+<row>
+<entry>toss</entry>
+<entry>Threaded OSS support, which works better in some cases where the
+ standard OSS support doesn't work well</entry>
+</row>
+
+<row>
+<entry>sgi</entry>
+<entry>SGI Direct Media support for IRIX</entry>
+</row>
+
+<row>
+<entry>sun</entry>
+<entry>Solaris support</entry>
+</row>
+
+</tbody>
+</tgroup>
+</informaltable>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>
+I can't play <literal role="extension">wav</literal> files with &artsd;!
+</para>
+</question>
+
+<answer>
+<para>
+Check that &artsd; is linked to <filename>libaudiofile</filename>
+(<userinput><command>ldd</command>
+<parameter>artsd</parameter></userinput>). If it isn't, download
+kdesupport, recompile everything, and it will work.
+</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>
+I hear sound when logged in as <systemitem
+class="username">root</systemitem> but no other users have sound!
+</para>
+</question>
+
+<answer>
+<para>
+The permissions of the file <filename
+class="devicefile">/dev/dsp</filename> affect which users will have
+sound. To allow everyone to use it, do this:
+</para>
+
+<procedure>
+<step>
+<para>
+Log in as <systemitem class="username">root</systemitem>.
+</para>
+</step>
+
+<step>
+<para>
+Open a &konqueror; window.
+</para>
+</step>
+
+<step>
+<para>
+Go into the <filename class="directory">/dev</filename> folder.
+</para>
+</step>
+
+<step>
+<para>
+Click on the file <filename>dsp</filename> with the
+<mousebutton>right</mousebutton> mouse button, and choose properties.
+</para>
+</step>
+
+<step>
+<para>
+Click on the <guilabel>Permissions</guilabel> tab.
+</para>
+</step>
+
+<step>
+<para>
+Check the <guilabel>Read</guilabel> and <guilabel>Write</guilabel> check
+boxes in all sections.
+</para>
+</step>
+
+<step>
+<para>
+Click on <guibutton>OK</guibutton>.
+</para>
+</step>
+</procedure>
+
+<para>
+You can achieve the same effect in a terminal window using the command
+<userinput><command>chmod</command> <option>666</option>
+<parameter>/dev/dsp</parameter></userinput>.
+</para>
+
+<para>
+For restricting access to sound to specific users, you can use group
+permissions. On some &Linux; distributions, for instance Debian/Potato,
+<filename class="devicefile">/dev/dsp</filename> is already owned by a
+group called <systemitem class="groupname">audio</systemitem>, so all
+you need to do is add the users to this group.
+</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>
+This helps for &artsd;, but what about &kmix;, &kmid;, &kscd;,&etc;?
+</para>
+</question>
+<answer>
+
+<para>
+There are various other devices which provide functionality accessed by
+multimedia applications. You can treat them in the same way, either by
+making them accessible for everyone, or using groups to control
+access. Here is a list, which may still be incomplete (also if there are
+various devices in a form like <filename
+class="devicefile">midi0</filename>, <filename
+class="devicefile">midi1</filename>, ..., then only the 0-version is
+listed here):
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+<filename class="devicefile">/dev/admmidi0</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/adsp0</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/amidi0</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/amixer0</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/audio</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/audio0</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/cdrom</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/dmfm0</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/dmmidi0</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/dsp</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/dsp0</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/midi0</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/midi0</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/midi00</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/midi00</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/mixer</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/mixer0</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/mpu401data</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/mpu401stat</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/music</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/rmidi0</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/rtc</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/sequencer</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/smpte0</filename>
+</para>
+</listitem>
+<listitem>
+<para>
+<filename class="devicefile">/dev/sndstat</filename>
+</para>
+</listitem>
+</itemizedlist>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>What can I do if artsd doesn't start or crashes while running?</para>
+</question>
+
+<answer>
+<para>
+First of all: try using the default settings in &kcontrol; (or if you
+are starting manually, don't give additional options besides maybe
+<userinput><option>-F</option><parameter>10</parameter>
+<option>-S</option><parameter>4096</parameter></userinput> for
+latency). Especially <emphasis>full duplex is likely to break</emphasis>
+with various drivers, so try disabling it.
+</para>
+
+<para>
+A good way to figure out why &artsd; doesn't start (or crashes while
+running) is to start it manually. Open a &konsole; window, and do:
+</para>
+
+<screen width="40"><prompt>%</prompt> <userinput><command>artsd</command> <option>-F</option><parameter>10</parameter> <option>-S</option><parameter>4096</parameter></userinput></screen>
+
+<para>
+You can also add the <option>-l0</option> option, which will print more
+information about what is happening, like this:
+</para>
+<screen width="40"><prompt>%</prompt> <userinput><command>artsd</command> <option>-l0</option> <option>-F</option><parameter>10</parameter> <option>-S</option><parameter>4096</parameter></userinput></screen>
+
+<para>
+Doing so, you will probably get some useful information why it didn't
+start. Or, if it crashes when doing this-and-that, you can do
+this-and-that, and see <quote>how</quote> it crashes. If you want to
+report a bug, producing a backtrace with <command>gdb</command> and/or
+an <command>strace</command> may help finding the problem.
+</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>Can I relocate &artsd; (move compiled files to another
+folder)?</para>
+</question>
+
+<answer>
+<para>
+You can't relocate &arts; perfectly. The problem is that &artswrapper;
+has the location of &artsd; compiled in due to security reasons. You can
+however use the <filename>.mcoprc</filename> file
+(TraderPath/ExtensionPath entries) to at least make a relocated &artsd;
+find it's components. See the <link linkend="the-mcoprc-file">chapter
+about the <filename>.mcoprc</filename> file</link> for details on how to
+do this.
+</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>Can I compile &arts; with gcc-3.0?</para>
+</question>
+
+<answer>
+<para>
+Short answer: no, &arts; will not work if you compile it with gcc-3.0.
+</para>
+
+<para>
+Long answer: In the official release, there are two gcc-3.0 bugs which affect
+&arts;. The first, gcc-3.0 bug c++/2733 is relatively harmless (and has to do
+with problems with the asm statement). It breaks compilation of convert.cc. It
+has been fixed in the gcc-3.0 CVS, and will no longer be a problem with
+gcc-3.0.1 and higher. A workaround has also been added to the CVS version
+of KDE/aRts.
+</para>
+<para>
+The second gcc-3.0 bug, c++/3145 (which is generation of wrong code for some
+cases of multiple virtual inheritance) is critical. Applications like &artsd;
+will simply crash on startup when compiled with gcc-3.0. Even if some progress
+has been made in the gcc-3.0 branch at time of this writing, still &artsd;
+crashes quite often, unpredictably.
+</para>
+</answer>
+</qandaentry>
+<qandaentry>
+<question>
+<para>What applications run under &arts;?</para>
+</question>
+<answer>
+
+<para>
+Obviously, all of the applications included with &kde; are
+&arts;-aware. This includes:
+</para>
+
+<itemizedlist>
+<listitem><para>&noatun;</para></listitem>
+<listitem><para>&arts-builder;</para></listitem>
+<listitem><para>&aktion;</para></listitem>
+<listitem><para>&kmid;</para></listitem>
+<listitem><para>&kmidi;</para></listitem>
+<listitem><para>&kmix;</para></listitem>
+<listitem><para>&kscd;</para></listitem>
+<listitem><para>&kde; games such as &kpoker; and
+&ktuberling;</para></listitem>
+</itemizedlist>
+
+<para>
+Some &kde; applications that are not yet included in the &kde; release
+(&eg; in kdenonbeta) also support &arts;, including:
+</para>
+
+<itemizedlist>
+<listitem><para>&brahms;</para></listitem>
+<listitem><para><application>Kaboodle</application></para></listitem>
+<listitem><para><application>Kdao</application></para></listitem>
+</itemizedlist>
+
+<para>
+The following non-&kde; applications are known to work with &arts;:
+</para>
+
+<itemizedlist>
+<listitem><para><application>xmms</application> (with &arts;
+plug-in)</para></listitem>
+<listitem><para>Real Networks <application>RealPlayer</application> 8.0
+(works with &artsdsp;; native &arts; support is being
+considered)</para></listitem>
+</itemizedlist>
+
+<para>
+The following applications are known <emphasis>not</emphasis> to work
+with &arts;:
+</para>
+
+<itemizedlist>
+<listitem><para>none</para></listitem>
+</itemizedlist>
+
+<para>
+See also the answers to the questions in the section on
+<link linkend="faq-non-arts">non-&arts; applications</link>.
+</para>
+
+<para>
+This section is incomplete -- if you have more information on supported
+and unsupported applications, please send them to the author so they can
+be included here.
+</para>
+</answer>
+</qandaentry>
+
+</qandaset>
+
+<qandaset id="faq-non-arts">
+<title>Non-&arts; Applications</title>
+
+<qandaentry>
+<question>
+<para>
+As soon as &kde; is running, no other application can access my sound device!
+</para>
+</question>
+<answer>
+<para>
+Since the &arts; sound server used by &kde; is running, it is using the
+sound device. If the server is idle for 60 seconds, it will
+auto-suspend and release it automatically.
+</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>
+You said it suspends after 60 seconds, it doesn't for me!
+</para>
+</question>
+<answer>
+<para>
+If you start artsd from the KDE control panel, the default is to suspend
+after 60 seconds. If you start artsd from the command line you need to
+use the -s option to specify the autosuspend time, otherwise it will
+default to disabling the autosuspend feature.
+</para>
+<para>
+Currently it doesn't suspend when using full duplex. Turn full duplex
+off from the &kcontrol; and it will suspend. Disabling full duplex is
+generally a good idea anyway if you only use &arts; for playing audio
+and not recording.
+</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>
+How can I run old, non-&arts; applications?
+</para>
+</question>
+
+<answer>
+<para>
+Run them using the &artsdsp;. For instance, if you normally would run:
+</para>
+
+<screen><prompt>&percnt;</prompt> <userinput><command>mpg123</command> <option>foo.mp3</option></userinput></screen>
+
+<para>instead use:</para>
+
+<screen><prompt>&percnt;</prompt> <userinput><command>artsdsp</command> <option>mpg123 foo.mp3</option></userinput></screen>
+
+<para>
+This will redirect the sound output to &arts;. This method doesn't
+require changes to the applications. It is something of an ugly hack
+however, and does not yet fully support all features of the sound card
+device, so some applications may not work.
+</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>
+I can't run &artsdsp; with any application, it always crashes!
+</para>
+</question>
+<answer>
+<para>
+You need a recent version of the glibc library; &artsdsp; will not work
+reliably on some older &Linux; distributions. For instance, on Debian
+2.1 (which is glibc 2.0 based) it doesn't work, while on Debian 2.2
+(which is glibc 2.1.3 based), it does.
+</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>
+Are there theoretical limitations with some applications that will
+prevent them from ever working with &artsdsp;?
+</para>
+</question>
+<answer>
+<para>
+No. Using &artsdsp; can result in slightly more latency and
+<acronym>CPU</acronym> usage that using the &arts;
+<acronym>API</acronym>s directly. Other than that, any application that
+doesn't work should be considered a bug in &artsdsp;. The technique used
+by &artsdsp; should, if implemented properly, allow
+<emphasis>every</emphasis> application to work with it (including large
+applications like <application>Quake</application> 3).
+</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>
+What can I do if an application doesn't work with &artsdsp;?
+</para>
+</question>
+<answer>
+<para>
+You can wait for &artsd; to suspend or use the command
+<userinput><command>artsshell</command>
+<option>suspend</option></userinput> to ask the server to suspend
+itself. You will only be able to suspend the server if no &arts;
+applications are currently using it, and no &arts; applications will be
+able to run when the server is suspended.
+</para>
+
+<para>
+If the server is busy, a crude but effective way to get rid of it is:
+</para>
+
+
+<screen><prompt>&percnt;</prompt> <userinput><command>killall</command> <option>artsd</option> ; <command>killall</command> <option>artswrapper</option></userinput>
+<lineannotation>Now start your own application.</lineannotation>
+<prompt>&percnt;</prompt> <userinput><command>kcminit</command> <option>arts</option></userinput>
+</screen>
+
+<para>
+Any currently running &arts; applications may crash, however, once you
+kill the server.
+</para>
+</answer>
+</qandaentry>
+<qandaentry>
+<question>
+<para>
+What about applications written for &kde; 1.x?
+</para>
+</question>
+<answer>
+<para>
+If you are running &kde; 1.x applications, which output sound via the
+&kde; 1 audio server, you will need to run
+<application>kaudioserver</application> to make it work. You can start
+<application>kaudioserver</application> in the same way than other
+non-&arts;-applications:
+</para>
+
+<screen>
+<prompt>&percnt;</prompt> <userinput><command>artsdsp</command> <option>kaudioserver</option></userinput>
+</screen>
+
+<para>
+You will need to have installed kaudioserver (from the same source where
+you got your &kde; 1.x applications from) - it belongs to &kde; 1.x, not
+&kde; 2.
+</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>
+What about applications using the enlightened sound daemon,
+<acronym>ESD</acronym>?
+</para>
+</question>
+<answer>
+<para>
+The issue is similar than with
+<application>kaudioserver</application>. Such applications will need a
+running esd server. You can start <command>esd</command> via &artsdsp;,
+and every <acronym>ESD</acronym> aware application should work fine,
+like this:
+</para>
+<screen>
+<prompt>&percnt;</prompt> <userinput><command>artsdsp</command> <option>esd</option></userinput>
+</screen>
+<para>
+Newer versions of aRts (>= 1.2.0) also can also use the enlightened sound
+daemon instead of directly accessing the soundcard. On the command line, you
+can use the -a option, such as
+</para>
+<screen>
+<prompt>&percnt;</prompt> <userinput><command>artsd</command> <option>-a esd</option></userinput>
+</screen>
+<para>
+to get EsounD support, whereas in KDE, you can use kcontrol to configure artsd
+to use esd via Sound -&gt; Sound Server -&gt; Sound I/O.
+</para>
+</answer>
+</qandaentry>
+
+</qandaset>
+
+<qandaset id="faq-latency">
+<title>Latency</title>
+
+<qandaentry>
+<question>
+<para>
+I sometimes hear short pauses when listening to music, is this a bug?
+</para>
+</question>
+<answer>
+<para>
+This is most likely not a bug, but caused by the fact that the &Linux;
+kernel is not very good at real-time scheduling. There are situations
+where &arts; will not be able to keep up with playback. You can,
+however, enable real-time rights (via &kcontrol;), and use a large
+latency setting (like <guilabel>250ms</guilabel> or <guilabel>don't
+care</guilabel>), which should improve the situation.
+</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>
+What's the effect of the response time setting?
+</para>
+</question>
+<answer>
+<para>
+The help text for this setting in the &kcontrol; can be misleading. A
+lower value means that &arts; will take less time to respond to external
+events (&ie;. the time that it takes between closing a window and
+hearing a sound played by &artsd;). It will also use more
+<acronym>CPU</acronym> resources, and be more likely to cause
+dropouts.</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>
+Is there anything else I can do to prevent pauses?
+</para>
+</question>
+<answer>
+<para>
+For users of <acronym>IDE</acronym> drives, you can use the
+<command>hdparm</command> command to put your <acronym>IDE</acronym>
+drive in <acronym>DMA</acronym> mode. A word of warning: this does not
+work on all hardware, and can result in having to do a hard reset or in
+rare cases, data loss. Read the documentation for the
+<command>hdparm</command> command for more details. I have successfully
+used the following command:
+</para>
+
+<screen>
+<prompt>&percnt;</prompt> <userinput><command>hdparm</command> <option>-c1</option> <option>-d1</option> <option>-k1</option> <option>-K1</option> <parameter>/dev/hda</parameter></userinput>
+</screen>
+
+<para>
+You need to run this after every boot, so you might want to place it in
+a system startup script (how to do this distribution specific, on Debian
+&Linux; it is usually put in <filename>/etc/rc.boot</filename>).
+</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>
+Realtime priority doesn't seem to have any effect for me?
+</para>
+</question>
+<answer>
+<para>
+Verify that artswrapper is really installed suid <systemitem class="username">root</systemitem>, like it is supposed to
+be. A lot of distributions (SuSE7.x for instance) don't do this. You can verify
+this using: ls -l $(which artswrapper). Good:
+<screen>
+<prompt>&percnt;</prompt> <userinput><command>ls</command> <option>-l</option> <parameter>$(which artswrapper)</parameter></userinput>
+-rwsr-xr-x 1 root root 4556 Sep 24 18:05 /opt/kde2/bin/artswrapper
+</screen>
+Bad:
+<screen>
+<prompt>&percnt;</prompt> <userinput><command>ls</command> <option>-l</option> <parameter>$(which artswrapper)</parameter></userinput>
+-rwxr-xr-x 1 root root 4556 Sep 24 18:05 /opt/kde2/bin/artswrapper
+</screen>
+If you are not having the s, you can get it using:
+<screen>
+<prompt>&percnt;</prompt> <userinput><command>chown</command> <option>root</option> <parameter>$(which artswrapper)</parameter></userinput>
+<prompt>&percnt;</prompt> <userinput><command>chmod</command> <option>4755</option> <parameter>$(which artswrapper)</parameter></userinput>
+</screen>
+</para>
+
+<para>If you make &artswrapper; SUID <systemitem
+class="username">root</systemitem>, it will likely improve the quality
+of your audio playback by reducing gaps in the music. However, it
+also increases the risk that a bug in the code or a malicious user can
+crash or otherwise harm your machine. In addition, on multi-user
+machines, prioritizing high-quality audio may result in deteriorated
+performance for the users who are trying to make
+<quote>productive</quote> use of the machine.</para>
+
+</answer>
+</qandaentry>
+
+
+<qandaentry>
+<question>
+<para>
+Why is &artsd; taking so much <acronym>CPU</acronym> time?
+</para>
+</question>
+<answer>
+<para>
+Check your response time settings. However, the current version is not
+yet really optimized. This will improve, and until then no real
+prediction can be made how fast &artsd; can or can't be.
+</para>
+</answer>
+</qandaentry>
+</qandaset>
+
+<qandaset id="faq-network">
+<title>Network Transparency</title>
+
+<qandaentry>
+<question>
+<para>
+What do I need for network transparency?
+</para>
+</question>
+<answer>
+<para>
+Enable it in the &kcontrol; <guilabel>Sound Server</guilabel> settings
+(<guilabel>enable X11 server for security information</guilabel> and
+<guilabel>network transparency</guilabel>). Then copy your
+<filename>.mcoprc</filename> to all machines you plan to use network
+transparency from. Log in again. Make sure that the hosts that interact
+know each other by name (&ie; they have resolvable names or are in
+<filename>/etc/hosts</filename>).
+</para>
+
+<para>
+This should be all you need to do. However, if it still doesn't work
+here are some additional details. The &arts; sound server process,
+&artsd;, should only run on one host, the one with the sound card where
+the sound should be played. It can be started automatically on login by
+&kde; (if you configure that in &kcontrol;), or manually using something
+like:
+</para>
+
+<screen>
+<prompt>&percnt;</prompt> <userinput><command>artsd</command> <option>-n</option> <option>-F</option> <parameter>5</parameter> <option>-S</option> <parameter>8192</parameter></userinput>
+</screen>
+
+<para>
+The <option>-n</option> parameter is for network transparency, while the
+others configure latency.
+</para>
+
+<para>
+Your <filename>.mcoprc</filename> file should have this entry:
+</para>
+
+<screen>
+<userinput>GlobalComm=Arts::X11GlobalComm</userinput>
+</screen>
+
+<para>
+on all machines involved, in order for network transparency to work,
+This is what is enabled by the <guilabel>X11 server for security
+information</guilabel> control panel setting.
+</para>
+
+<para>
+Finally, in any &kde; version in the 2.0.x series, there is a bug which
+applies if you don't have a domain name set. Clients of &artsd; try to
+find where to connect to via the <systemitem
+class="systemname"><replaceable>hostname</replaceable>.<replaceable>domainname</replaceable></systemitem>
+combination. If your domain name is empty, it will try to connect to
+<systemitem
+class="systemname"><replaceable>hostname</replaceable></systemitem>. (note
+the extra dot). Adding an entry like this to
+<filename>/etc/hosts</filename> (&ie; <userinput>orion.</userinput> if
+your hostname is <systemitem class="systemname">orion</systemitem>)
+works around the problem.
+</para>
+</answer>
+</qandaentry>
+
+
+<qandaentry>
+<question>
+<para>
+How do I debug network transparency if it doesn't work?
+</para>
+</question>
+<answer>
+<para>
+Assuming you have the &kde; source code, go to <filename
+class="directory">kdelibs/arts/examples</filename>, and run
+<userinput><command>make</command> <option>check</option></userinput> to
+compile some programs, including
+<application>referenceinfo</application>. Then run
+</para>
+
+<screen>
+<prompt>&percnt;</prompt> <userinput><command>./referenceinfo</command> <option>global:Arts&lowbar;SimpleSoundServer</option></userinput>
+</screen>
+
+<para>
+The output will indicate the host name and port being used by
+&arts;. For example, <computeroutput>tcp:orion:1698</computeroutput>
+would mean that any client trying to use network transparency should
+know how to reach host <systemitem
+class="systemname">orion</systemitem>.
+</para>
+</answer>
+</qandaentry>
+
+</qandaset>
+
+<qandaset id="faq-hardware-specific">
+<title>Hardware specific questions</title>
+
+<qandaentry>
+<question>
+<para>
+What hardware artsd doesn't work well with?
+</para>
+</question>
+<answer>
+<para>
+It seems that there are a few linux drivers which don't work well with aRts in
+some kernel versions. Please read this list before reporting a bug. If you
+find that some information in this list is incomplete, please don't hesitate
+to let us know.
+
+<informaltable>
+<tgroup cols="4">
+<thead>
+<row>
+<entry>Linux Driver / Soundcard</entry>
+<entry>Fails under</entry>
+<entry>Works under</entry>
+<entry>Remarks</entry>
+</row>
+</thead>
+
+<tbody>
+<row>
+<entry>i810 driver (Intel 810 + AC97 Audio)</entry>
+<entry>2.4.9</entry>
+<entry>2.4.18, 2.2.20, commercial oss driver, alsa-0.5.12a with OSS emulation</entry>
+<entry>driver causes cpu overload (see below)</entry>
+</row>
+
+<row>
+<entry>maestro 3/4 chipset</entry>
+<entry>2.4.9</entry>
+<entry>?</entry>
+<entry>driver sometimes causes cpu overload (see below)</entry>
+</row>
+
+<row>
+<entry>aureal8820, aureal8830 drivers from sourceforge</entry>
+<entry>2.4.17</entry>
+<entry>?</entry>
+<entry>driver triggers assertion / causes cpu overload (see below)</entry>
+</row>
+
+<row>
+<entry>OSS Commercial 3.9.4g with Aureal Vortex</entry>
+<entry>?</entry>
+<entry>?</entry>
+<entry>system lockup</entry>
+</row>
+
+<row>
+<entry>ymfpci</entry>
+<entry>2.4.0, 2.4.12</entry>
+<entry>2.4.17</entry>
+<entry>driver triggers assertion (see below)</entry>
+</row>
+
+
+
+</tbody>
+</tgroup>
+</informaltable>
+</para>
+</answer>
+</qandaentry>
+
+
+
+<qandaentry>
+<question>
+<para>
+Why are there hardware specific problems and how do I see them?
+</para>
+</question>
+<answer>
+<para>
+The usual problem is that the driver doesn't supply aRts with enough or accurate
+enough information on when to write sound data. Most OSS drivers do supply
+correct information, but not all.
+</para>
+<para>
+You might notice that some other applications (like xmms) may not need this
+data, and thus work correctly even with your hardware. However, &arts; needs
+this data, so artsd might fail. This is still a bug in the driver, and not
+in &arts;.
+</para>
+<para>
+There are two kinds of behavior that artsd exposes on being run on an incorrect
+driver. Either, it continously tries to feed new data, but never really
+succeeds, which eventually leads to consuming all CPU power and reporting
+<emphasis>cpu overload</emphasis> and exiting. The other problem is that artsd
+might get supplied with wrong information how much to write. Artsd will then
+<emphasis>stop with an assertion</emphasis> like:
+<screen>
+artsd: audiosubsys.cc:458: void Arts::AudioSubSystem::handleIO(int):
+Assertion `len == can_write' failed.
+Aborted
+</screen>
+</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>
+What is wrong in the driver if I get the cpu overload problem?
+</para>
+</question>
+<answer>
+<para>
+Usually, artsd uses select() to find out when to write new data. Then, it
+uses an ioctl(...GETOSPACE...) to find out how much data to write. Finally,
+it writes this data.
+</para>
+<para>
+A problem occurs if artsd is woken up either always or if there are minimal
+amounts of data to write. The OSS documentation specifies that select() only
+wakes up a process if there is at least one fragment to write. However, if
+artsd is woken up if there isn't data to write, or very little, for instance
+one sample, then it will keep writing little pieces of audio data, which can
+be very costly, and eventually overload the cpu.
+</para>
+<para>
+To fix this, the driver should wake up artsd only if there is a full fragment
+to write.
+</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>
+What is wrong in the driver if I get the assertion?
+</para>
+</question>
+<answer>
+<para>
+Usually, artsd uses select() to find out when to write new data. Then, it
+uses an ioctl(...GETOSPACE...) to find out how much data to write. Finally,
+it writes this data.
+</para>
+<para>
+If artsd can't write as much data as indicated by the ioctl, it will fail in
+the assertion. To fix this, the driver should supply the correct amount of
+free space.
+</para>
+</answer>
+</qandaentry>
+</qandaset>
+
+<qandaset id="faq-other">
+<title>Other Issues</title>
+
+<qandaentry>
+<question>
+<para>
+I can't use &arts-builder;. It crashes when executing a module!
+</para>
+</question>
+<answer>
+<para>
+The most likely cause is that you are using old structures or modules
+which aren't supported with the &kde; 2 version. Unfortunately the
+documentation which is on the web refers to &arts;-0.3.4.1 which is
+quite outdated. The most often reported crash is: that performing an
+execute structure in &arts-builder; results in the error message
+<errorname>[artsd] Synth_PLAY: audio subsystem is already
+used.</errorname>
+</para>
+
+<para>
+You should use a Synth_AMAN_PLAY instead of a Synth_PLAY module and the
+problem will go away. Also see the &arts-builder; help file (hit
+<keycap>F1</keycap> in &arts-builder;).
+</para>
+
+<para>
+Recent versions of &arts-builder; (&kde; 2.1 beta 1 and later) come with
+a set of examples which you can use.
+</para>
+</answer>
+</qandaentry>
+
+</qandaset>
+
+</chapter>
diff --git a/doc/artsbuilder/future.docbook b/doc/artsbuilder/future.docbook
new file mode 100644
index 00000000..529cc103
--- /dev/null
+++ b/doc/artsbuilder/future.docbook
@@ -0,0 +1,414 @@
+<!-- <?xml version="1.0" ?>
+<!DOCTYPE chapter PUBLIC "-//KDE//DTD DocBook XML V4.1.2-Based Variant
+V1.1//EN" "dtd/kdex.dtd">
+To validate or process this file as a standalone document, uncomment
+this prolog. Be sure to comment it out again when you are done -->
+
+<chapter id="future-work">
+<title>Future Work</title>
+
+<para>
+This section describes some of the &arts; work that is in progress.
+Development progresses quickly, so this information may be out of date.
+You should check the TODO list file and the <link
+linkend="mailing-lists">mailing list</link> archives to see what new
+functionality is planned. Feel free to get involved in new design and
+implementation.
+</para>
+
+<para>
+This is a draft document which tries to give you an overview how new
+technologies will be integrated in &arts;. Namely, it does cover the
+following:
+</para>
+
+<itemizedlist>
+<listitem><para>How interfaces work.</para></listitem>
+<listitem><para>Codecs - decoding of mp3 or wav streams in a form that
+they can be used as data.</para></listitem>
+<listitem><para>Video.</para></listitem>
+<listitem><para>Threading.</para></listitem>
+<listitem><para>Synchronization.</para></listitem>
+<listitem><para>Dynamic expansion/masquerading.</para></listitem>
+<listitem><para>Dynamic composition.</para></listitem>
+<listitem><para>&GUI;</para></listitem>
+<listitem><para>&MIDI;</para></listitem>
+</itemizedlist>
+
+<para>
+This is work in progress. However, it should be the base if you want to
+see new technology in &arts;. It should give you a general idea how
+these problems will be addressed. However, feel free to correct anything
+you see here.
+</para>
+
+<para>
+Things that will be use &arts; technology (so please, coordinate your
+efforts):
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+<application>KPhone</application> (voice over <acronym>IP</acronym>)
+</para>
+</listitem>
+
+<listitem>
+<para>
+&noatun; (video / audio player)
+</para>
+</listitem>
+
+<listitem>
+<para>
+&artscontrol; (sound server control program, for scopes)
+</para>
+</listitem>
+
+<listitem>
+<para>
+<application>Brahms</application> (music sequencer)
+</para>
+</listitem>
+
+<listitem>
+<para><application>Kaiman</application> (&kde;2 media player - kmedia2 compliant)
+</para>
+</listitem>
+
+<listitem>
+<para>
+<application>mpglib</application>/<application>kmpg</application>
+(<acronym>mpg</acronym> audio and video playing technology)
+</para>
+</listitem>
+
+<listitem>
+<para>
+<application>SDL</application> (direct media layer for games not
+yet started but maybe nice)
+</para>
+</listitem>
+
+<listitem>
+<para>
+<application>electric ears</application> (author contacted me - status
+unknown)
+</para>
+</listitem>
+</itemizedlist>
+
+<sect1 id="interfaces-how">
+<title>How Interfaces Work</title>
+
+<!-- I think this is now obsolete and documented elsewhere ? -->
+
+<para>
+&MCOP; interfaces are the base of the &arts; concept. They are the
+network transparent equivalent to C++ classes. Whenever possible you
+should orient your design towards interfaces. Interfaces consist of four
+parts:
+</para>
+
+<itemizedlist>
+<listitem><para>Synchronous streams</para></listitem>
+<listitem><para>Asynchronous streams</para></listitem>
+<listitem><para>Methods</para></listitem>
+<listitem><para>Attributes</para></listitem>
+</itemizedlist>
+
+<para>
+These can be mixed in any way you like. New technologies should be
+defined in terms of interfaces. Read the sections about asynchronous
+streams and synchronous streams, as well as the KMedia2 interfaces,
+which are a good example how such things work
+</para>
+
+<para>
+Interfaces are specified in <literal role="extension">.idl</literal>
+code and run through the <command>mcopidl</command> compiler. You
+derive the
+<classname><replaceable>Interfacename</replaceable>_impl</classname>
+class to implement them, and use
+<function>REGISTER_IMPLEMENTATION(Interfacename_impl)</function> to
+insert your object implementations into the &MCOP; object system.
+</para>
+
+</sect1>
+
+<sect1 id="codecs">
+<title>Codecs - Data Decoding</title>
+
+<para>
+The kmedia2 interfaces allow you to ignore that wav files, mp3s and
+whatever consist of data streams. Instead, you only implement methods to
+play them.
+</para>
+
+<para>
+Thus, you can write a wave loading routine in a way that you can play
+wave files (as PlayObject), but nobody else can use your code.
+</para>
+
+<para>
+Asynchronous streams would be the alternative. You define an interface
+which allows you to pass data blocks in, and get data blocks out. This
+looks like that in &MCOP;:
+</para>
+
+<programlisting>
+interface Codec {
+ in async byte stream indata;
+ out async byte stream outdata;
+};
+</programlisting>
+
+
+<para>
+Of course codecs could also provide attributes to emit additional data,
+such as format information.
+</para>
+
+<programlisting>
+interface ByteAudioCodec {
+ in async byte stream indata;
+ out async byte stream outdata;
+ readonly attribute samplingRate, bits, channels;
+};
+</programlisting>
+
+<para>
+This <interfacename>ByteAudioCodec</interfacename> for instance could be
+connected to a <interfacename>ByteStreamToAudio</interfacename> object,
+to make real float audio.
+</para>
+
+<para>
+Of course, other Codec types could involve directly emitting video data,
+such as
+</para>
+
+<programlisting>
+interface VideoCodec {
+ in async byte stream indata;
+ out video stream outdata; /* note: video streams do not exist yet */
+};
+</programlisting>
+
+<para>
+Most likely, a codec concept should be employed rather than the
+<quote>you know how to play and I don't</quote> way for instance
+<interfacename>WavPlayObject</interfacename> currently uses. However,
+somebody needs to sit down and do some experiments before an
+<acronym>API</acronym> can be finalized.
+</para>
+
+</sect1>
+
+<sect1 id="video">
+<title>Video</title>
+
+<para>
+My idea is to provide video as asynchronous streams of some native
+&MCOP; data type which contains images. This data type is to be created
+yet. Doing so, plugins which deal with video images could be connected
+the same way audio plugins can be connected.
+</para>
+
+<para>
+There are a few things that are important not to leave out, namely:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+There are <acronym>RGB</acronym> and <acronym>YUV</acronym> colorspaces.
+</para>
+</listitem>
+<listitem>
+<para>
+The format should be somehow tagged to the stream.
+</para>
+</listitem>
+<listitem>
+<para>
+Synchronization is important.
+</para>
+</listitem>
+</itemizedlist>
+
+<para>
+My idea is to leave it possible to reimplement the
+<classname>VideoFrame</classname> class so that it can store stuff in a
+shared memory segment. Doing so, even video streaming between different
+processes would be possible without too much pain.
+</para>
+
+<para>
+However, the standard situation for video is that things are in the same
+process, from the decoding to the rendering.
+</para>
+
+<para>
+I have done a prototypic video streaming implementation, which you can
+download <ulink
+url="http://space.twc.de/~stefan/kde/download/video-quickdraw.tar.gz">here
+</ulink>. This would need to be integrated into &MCOP; after some
+experiments.
+</para>
+
+<para>
+A rendering component should be provided that supports XMITSHM (with
+<acronym>RGB</acronym> and <acronym>YUV</acronym>), Martin Vogt told me
+he is working on such a thing.
+</para>
+
+</sect1>
+
+<sect1 id="threading">
+<title>Threading</title>
+
+<para>
+Currently, &MCOP; is all single threaded. Maybe for video we will no
+longer be able to get around threading. Ok. There are a few things that
+should be treated carefully:
+</para>
+
+
+<itemizedlist>
+<listitem><para>
+SmartWrappers - they are not threadsafe due to non-safe reference
+counting and similar.
+</para>
+</listitem>
+<listitem>
+<para>
+Dispatcher / I/O - also not threadsafe.
+</para>
+</listitem>
+</itemizedlist>
+
+<para>
+However, what I could imagine is to make selected modules threadsafe,
+for both, synchronous and asynchronous streaming. That way - with a
+thread aware flow system, you could schedule the signal flow over two or
+more processors. This would also help audio a lot on multiprocessor
+things.
+</para>
+
+<para>
+How it would work:
+</para>
+
+
+<itemizedlist>
+<listitem>
+<para>The Flow System decides which modules should calculate what - that
+is:
+</para>
+ <itemizedlist>
+ <listitem><para>video frames (with process_indata method)</para></listitem>
+ <listitem><para>synchronous audio streams
+ (calculateBlock)</para></listitem>
+ <listitem><para>other asynchronous streams, mainly byte
+ streams</para></listitem>
+ </itemizedlist>
+</listitem>
+<listitem>
+<para>
+Modules can calculate these things in own threads. For audio, it makes
+sense to reuse threads (&eg; render on four threads for four processors,
+no matter if 100 modules are running). For video and byte decompression,
+it may be more confortable to have a blocking implementation in an own
+thread, which is synchronized against the rest of &MCOP; by the flow
+system.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Modules may not use &MCOP; functionality (such as remote invocations)
+during threaded operation.
+</para>
+</listitem>
+</itemizedlist>
+
+</sect1>
+
+<sect1 id="synchronization">
+<title>Synchronization</title>
+
+<para>
+Video and &MIDI; (and audio) may require synchronization. Basically, that
+is timestamping. The idea I have is to attach timestamps to asynchronous
+streams, by adding one timestamp to each packet. If you send two video
+frames, simply make it two packets (they are large anyway), so that you
+can have two different time stamps.
+</para>
+
+<para>
+Audio should implicitely have time stamps, as it is synchronous.
+</para>
+
+</sect1>
+
+<sect1 id="dynamic-composition">
+<title>Dynamic Composition</title>
+
+<para>
+It should be possible to say: An effect FX is composed out of these
+simpler modules. FX should look like a normal &MCOP; module (see
+masquerading), but in fact consist of other modules.
+</para>
+
+<para>
+This is required for &arts-builder;.
+</para>
+
+</sect1>
+
+<sect1 id="gui">
+<title>&GUI;</title>
+
+<para>
+All &GUI; components will be &MCOP; modules. They should have attributes
+like size, label, color, ... . A <acronym>RAD</acronym> builder
+(&arts-builder;) should be able to compose them visually.
+</para>
+
+<para>
+The &GUI; should be saveable by saving the attributes.
+</para>
+
+</sect1>
+
+<sect1 id="midi-stuff">
+<title>&MIDI;</title>
+
+<para>
+The &MIDI; stuff will be implemented as asynchronous streams. There are
+two options, one is using normal &MCOP; structures to define the types
+and the other is to introduce yet another custom types.
+</para>
+
+<para>
+I think normal structures may be enough, that is something like:
+</para>
+
+<programlisting>
+struct MidiEvent {
+ byte b1,b2,b3;
+ sequence&lt;byte&gt; sysex;
+}
+</programlisting>
+
+<para>
+Asynchronous streams should support custom stream types.
+</para>
+
+</sect1>
+
+</chapter>
+
+
diff --git a/doc/artsbuilder/glossary.docbook b/doc/artsbuilder/glossary.docbook
new file mode 100644
index 00000000..12fd2dac
--- /dev/null
+++ b/doc/artsbuilder/glossary.docbook
@@ -0,0 +1,164 @@
+<!-- <?xml version="1.0" ?>
+<!DOCTYPE glossary PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd">
+To validate or process this file as a standalone document, uncomment
+this prolog. Be sure to comment it out again when you are done -->
+
+<glossary id="glossary">
+
+<glossentry id="gloss-alsa">
+<glossterm><acronym>ALSA</acronym></glossterm>
+<glossdef>
+<para>
+Advanced &Linux; Sound Architecture; a &Linux; sound card driver; not
+currently included with the standard kernel source code.
+</para>
+</glossdef>
+</glossentry>
+
+<glossentry id="gloss-arts">
+<glossterm>&arts;</glossterm>
+<glossdef>
+<para>
+Analog Real-Time Synthesizer; the name of the multimedia
+architecture/library/toolkit used by the &kde; project (note
+capitalization)
+</para>
+</glossdef>
+</glossentry>
+
+<glossentry id="gloss-bsd">
+<glossterm><acronym>BSD</acronym></glossterm>
+<glossdef>
+<para>
+Berkeley Software Distribution; here refers to any of several free
+&UNIX;-compatible operating systems derived from <acronym>BSD</acronym>
+&UNIX;.
+</para>
+</glossdef>
+</glossentry>
+
+<glossentry id="gloss-corba">
+<glossterm><acronym>CORBA</acronym></glossterm>
+<glossdef>
+<para>
+Common Object Request Broker Architecture; a standard for implementing
+object-oriented remote execution.
+</para>
+</glossdef>
+</glossentry>
+
+<glossentry id="gloss-cvs">
+<glossterm><acronym>CVS</acronym></glossterm>
+<glossdef>
+<para>
+Concurrent Versions System; a software configuration management system
+used by many software projects including &kde; and &arts;.
+</para>
+</glossdef>
+</glossentry>
+
+<glossentry id="glos-fft">
+<glossterm><acronym>FFT</acronym></glossterm>
+<glossdef>
+<para>
+Fast Fourier Transform; an algorithm for converting data from the time
+to frequency domain; often used in signal processing.
+</para>
+</glossdef>
+</glossentry>
+
+<glossentry id="gloss-full-duplex">
+<glossterm>Full Duplex</glossterm>
+<glossdef>
+<para>
+The ability of a sound card to simultaneously record and play audio.
+</para>
+</glossdef>
+</glossentry>
+
+<glossentry id="gloss-gpl">
+<glossterm><acronym>GPL</acronym></glossterm>
+<glossdef>
+<para>
+<acronym>GNU</acronym> General Public License; a software license
+created by the Free Software Foundation defining the terms for releasing
+free software.
+</para>
+</glossdef>
+</glossentry>
+
+<glossentry id="gloss-gui">
+<glossterm>&GUI;</glossterm>
+<glossdef>
+<para>
+Graphical User Interface
+</para>
+</glossdef>
+</glossentry>
+
+<glossentry id="gloss-idl">
+<glossterm><acronym>IDL</acronym></glossterm>
+<glossdef>
+<para>
+Interface Definition Language; a programming language independent format
+for specifying interfaces (methods and data).
+</para>
+</glossdef>
+</glossentry>
+
+<glossentry id="gloss-kde">
+<glossterm>&kde;</glossterm>
+<glossdef>
+<para>
+K Desktop Environment; a project to develop a free graphical desktop
+environment for &UNIX; compatible systems.
+</para>
+</glossdef>
+</glossentry>
+
+<glossentry id="gloss-lgpl">
+<glossterm><acronym>LGPL</acronym></glossterm>
+<glossdef>
+<para>
+<acronym>GNU</acronym> Lesser General Public License; a software license
+created by the Free Software Foundation defining the terms for releasing
+free software; less restrictive than the <acronym>GPL</acronym> and
+often used for software libraries.
+</para>
+</glossdef>
+</glossentry>
+
+<glossentry id="gloss-mcop">
+<glossterm>&MCOP;</glossterm>
+<glossdef>
+<para>
+Multimedia COmmunication Protocol; the protocol used for communication
+between &arts; software modules; similar to <acronym>CORBA</acronym> but
+simpler and optimized for multimedia.
+</para>
+</glossdef>
+</glossentry>
+
+<glossentry id="gloss-midi">
+<glossterm>&MIDI;</glossterm>
+<glossdef>
+<para>
+Musical Instrument Digital Interface; a standard protocol for
+communication between electronic musical instruments; often also used to
+refer to a file format for storing &MIDI; commands.
+</para>
+</glossdef>
+</glossentry>
+
+<glossentry id="gloss-oss">
+<glossterm><acronym>OSS</acronym></glossterm>
+<glossdef>
+<para>
+Open Sound System; the sound drivers included with the &Linux; kernel
+(sometimes called <acronym>OSS</acronym>/Free) or a commercial version
+sold by 4Front Technologies.
+</para>
+</glossdef>
+</glossentry>
+
+</glossary>
diff --git a/doc/artsbuilder/gui.docbook b/doc/artsbuilder/gui.docbook
new file mode 100644
index 00000000..d420bf8a
--- /dev/null
+++ b/doc/artsbuilder/gui.docbook
@@ -0,0 +1,28 @@
+<!-- <?xml version="1.0" ?>
+<!DOCTYPE chapter PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd">
+To validate or process this file as a standalone document, uncomment
+this prolog. Be sure to comment it out again when you are done -->
+
+<!--
+<chapter id="gui-elements">
+<title>&GUI; Elements</title>
+
+<sect1 id="gui-introduction">
+<title>Introduction</title>
+<para>
+</para>
+</sect1>
+
+<sect1 id="parents">
+<title>Parents</title>
+<para>
+</para>
+</sect1>
+
+<sect1 id="mixers">
+<title>Mixers</title>
+<para>
+</para>
+</sect1>
+</chapter>
+-->
diff --git a/doc/artsbuilder/helping.docbook b/doc/artsbuilder/helping.docbook
new file mode 100644
index 00000000..72b2ff2b
--- /dev/null
+++ b/doc/artsbuilder/helping.docbook
@@ -0,0 +1,246 @@
+<!-- <?xml version="1.0" ?>
+<!DOCTYPE chapter PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd">
+To validate or process this file as a standalone document, uncomment
+this prolog. Be sure to comment it out again when you are done -->
+
+<chapter id="contributing">
+<title>Contributing to &arts;</title>
+
+<sect1 id="how-to-help">
+<title>How You Can Help</title>
+
+<para>
+The &arts; project can use help from developers to make existing
+multimedia applications &arts;-aware, write new multimedia applications,
+and enhance the capabilities of &arts;. However, you don't have to be a
+developer to contribute. We can also use help from testers to submit bug
+reports, translators to translate the application text and documentation
+into other languages, artists to design bitmaps (especially for
+<application>artsbuilder</application> modules), musicians to create
+sample &arts; modules, and writers to write or proofread documentation.
+</para>
+</sect1>
+
+<sect1 id="mailing-lists">
+<title>Mailing Lists</title>
+
+<para>
+Most development discussions on &arts; take place on two mailing
+lists. This is the place to discuss new feature and implementation ideas
+or ask for help with problems.
+</para>
+
+<para>
+The &kde; Multimedia mailing list is for general &kde; multimedia issues
+including &arts; as well as the multimedia applications like &noatun;
+and &aktion;. You can subscribe from the web page at
+<ulink url="http://www.kde.org/mailinglists.html">
+http://www.kde.org/mailinglists.html</ulink> or send an email with the
+subject set to <userinput>subscribe
+<replaceable>your-email-address</replaceable></userinput> to
+<email>kde-multimedia-request@kde.org</email>. The list is also archived
+at <ulink url="http://lists.kde.org"> http://lists.kde.org</ulink>.
+</para>
+
+<para>
+The &arts; mailing list is for issues specific to &arts;, including
+non-&kde; use of &arts;. To subscribe, send an email containing the
+message body <userinput>subscribe
+<replaceable>your-email-address</replaceable></userinput> to
+<email>arts-request@space.twc.de</email>. The list is archived at
+<ulink url="http://space.twc.de/~stefan/arts-archive">
+http://space.twc.de/~stefan/arts-archive</ulink>.
+</para>
+
+</sect1>
+
+<sect1 id="coding-standards">
+<title>Coding Standards</title>
+
+<para>
+For getting a consistent reading through all the sources, it is
+important to keep the coding style the same, all over the &arts;
+source. Please, even if you just write a module, try to write/format
+your source accordingly, as it will make it easier for different people
+to maintain the source tree, and easier to copy pieces of from one
+source to another.
+</para>
+
+<variablelist>
+<varlistentry>
+<term>Naming of member functions</term>
+<listitem>
+<para>
+&Qt;/&Java; style. That means capitalization on word breaks, and first
+letter always without capitalization; no underscores.
+</para>
+<para>This means for instance:</para>
+
+<programlisting> createStructureDesc()
+ updateWidget();
+ start(); </programlisting>
+
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>Class members</term>
+<listitem>
+<para>
+Class members are not capitalized, such as menubar or button.
+</para>
+
+<para>
+When there are accessing functions, the standard should be the &MCOP;
+way, that is, when having an long member <function>foo</function>, which
+shouldn't be visible directly, you create:
+</para>
+
+<programlisting> foo(long new_value);
+ long foo(); </programlisting>
+
+<para>
+functions to get and set the value. In that case, the real value of
+<function>foo</function> should be stored in
+<returnvalue>&lowbar;foo</returnvalue>.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>Class names</term>
+<listitem>
+<para>
+All classes should be wordwise capitalized, that means
+<classname>ModuleView</classname>,
+<classname>SynthModule</classname>. All classes that belong to the
+libraries should use the &arts; namespace, like
+<classname>Arts::Soundserver</classname>.
+</para>
+<para>
+The implementations of &MCOP; classes should get called
+<classname>Class&lowbar;impl</classname>, such as
+<classname>SoundServer&lowbar;impl</classname>.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>Parameters</term>
+<listitem>
+<para>
+Parameters are always uncapitalized.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>Local variables</term>
+<listitem>
+<para>
+Local variables are always uncapitalized, and may have names like
+<varname>i</varname>, <varname>p</varname>, <varname>x</varname>, &etc;
+where appropriate.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>Tab width (Shift width)</term>
+<listitem>
+<para>
+One tab is as long as 4 spaces.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>Spaces in expressions</term>
+<listitem>
+<para>
+You normally don't need to use spaces in expressions. You can however use
+them between operator and their operands. However, if you put a space before
+an operator (i.e. +), you also need to put a space after the operator. The only
+exception to this are list-like expressions (with ,), where you should only put
+a space after the ",", but not before. It's okay to omit the space here, too.
+</para>
+<para>
+The following examples demonstrate good use of spaces:
+</para>
+<programlisting>
+{
+ int a,b;
+ int c, d, e;
+ int f = 4;
+
+ a=b=c=d+e+f;
+ a = b = c = d + e + f;
+
+ if(a == 4) {
+ a = b = c = (d+e)/2;
+ }
+
+ while(b&lt;3)
+ c--;
+
+ arts_debug("%d\n", c);
+}
+</programlisting>
+<para>
+The following examples demonstrate how <emphasis>not</emphasis> to use spaces.
+For function calls, after if, while, for, switch and so on, no space is being
+written.
+</para>
+<programlisting>
+{
+ // BAD: if you write a list, write spaces only after the ","
+ int a , b , c , d , e , f;
+
+ // BAD: non-symmetric use of spaces for = operator
+ a= 5;
+
+ // BAD: if is considered a function, and isn't followed by a space
+ if (a == 5) {
+ }
+
+ // BAD: don't write a space after while
+ while (a--)
+ b++;
+
+ // BAD: functions names are not followed by a space
+ arts_debug ("%d\n", c);
+
+ // BAD: neither are member names
+ Arts::Object o = Arts::Object::null ();
+}
+</programlisting>
+</listitem>
+</varlistentry>
+
+
+<varlistentry>
+<term>Naming of source files</term>
+<listitem>
+<para>
+Source files should have no capitalization in the name. They should have
+the name of the class when they implement a single class. Their
+extension is <literal role="extension">.cc</literal> if they refer to
+&Qt;/&GUI; independent code, and <literal
+role="extension">.cpp</literal> if they refer to &Qt;/&GUI; dependant
+code. Implementation files for interfaces should be called
+<filename><replaceable>foo</replaceable>_impl</filename>, if Foo was the
+name of the interface.
+</para>
+
+<para>
+&IDL; files should be called in a descriptive way for the collection of
+interfaces they contain, also all lower case. Especially it is not good
+to call an &IDL; file like the class itself, as the .mcopclass trader
+and type info entries will collide, then.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+</sect1>
+
+</chapter>
diff --git a/doc/artsbuilder/images/Doc_MODUL.png b/doc/artsbuilder/images/Doc_MODUL.png
new file mode 100644
index 00000000..fe131e74
--- /dev/null
+++ b/doc/artsbuilder/images/Doc_MODUL.png
Binary files differ
diff --git a/doc/artsbuilder/images/Gui_AUDIO_MANAGER.png b/doc/artsbuilder/images/Gui_AUDIO_MANAGER.png
new file mode 100644
index 00000000..5cc92920
--- /dev/null
+++ b/doc/artsbuilder/images/Gui_AUDIO_MANAGER.png
Binary files differ
diff --git a/doc/artsbuilder/images/Gui_INSTRUMENT_MAPPER.png b/doc/artsbuilder/images/Gui_INSTRUMENT_MAPPER.png
new file mode 100644
index 00000000..19231daa
--- /dev/null
+++ b/doc/artsbuilder/images/Gui_INSTRUMENT_MAPPER.png
Binary files differ
diff --git a/doc/artsbuilder/images/Gui_LABEL.png b/doc/artsbuilder/images/Gui_LABEL.png
new file mode 100644
index 00000000..9ba2ce80
--- /dev/null
+++ b/doc/artsbuilder/images/Gui_LABEL.png
Binary files differ
diff --git a/doc/artsbuilder/images/Gui_MIXER.png b/doc/artsbuilder/images/Gui_MIXER.png
new file mode 100644
index 00000000..f6a036a7
--- /dev/null
+++ b/doc/artsbuilder/images/Gui_MIXER.png
Binary files differ
diff --git a/doc/artsbuilder/images/Gui_PANEL.png b/doc/artsbuilder/images/Gui_PANEL.png
new file mode 100644
index 00000000..c6ce0888
--- /dev/null
+++ b/doc/artsbuilder/images/Gui_PANEL.png
Binary files differ
diff --git a/doc/artsbuilder/images/Gui_POTI.png b/doc/artsbuilder/images/Gui_POTI.png
new file mode 100644
index 00000000..b40bf431
--- /dev/null
+++ b/doc/artsbuilder/images/Gui_POTI.png
Binary files differ
diff --git a/doc/artsbuilder/images/Gui_SLIDER.png b/doc/artsbuilder/images/Gui_SLIDER.png
new file mode 100644
index 00000000..7e9c83e7
--- /dev/null
+++ b/doc/artsbuilder/images/Gui_SLIDER.png
Binary files differ
diff --git a/doc/artsbuilder/images/Gui_SUBPANEL.png b/doc/artsbuilder/images/Gui_SUBPANEL.png
new file mode 100644
index 00000000..34bc5a77
--- /dev/null
+++ b/doc/artsbuilder/images/Gui_SUBPANEL.png
Binary files differ
diff --git a/doc/artsbuilder/images/Gui_WINDOW.png b/doc/artsbuilder/images/Gui_WINDOW.png
new file mode 100644
index 00000000..677ada48
--- /dev/null
+++ b/doc/artsbuilder/images/Gui_WINDOW.png
Binary files differ
diff --git a/doc/artsbuilder/images/Interface_MIDI_NOTE.png b/doc/artsbuilder/images/Interface_MIDI_NOTE.png
new file mode 100644
index 00000000..bc2d5ad0
--- /dev/null
+++ b/doc/artsbuilder/images/Interface_MIDI_NOTE.png
Binary files differ
diff --git a/doc/artsbuilder/images/Makefile.am b/doc/artsbuilder/images/Makefile.am
new file mode 100644
index 00000000..1b7d1e0f
--- /dev/null
+++ b/doc/artsbuilder/images/Makefile.am
@@ -0,0 +1,4 @@
+
+KDE_LANG = en
+KDE_DOCS = artsbuilder/images
+
diff --git a/doc/artsbuilder/images/Synth_ADD.png b/doc/artsbuilder/images/Synth_ADD.png
new file mode 100644
index 00000000..e06e47a0
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_ADD.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_ATAN_SATURATE.png b/doc/artsbuilder/images/Synth_ATAN_SATURATE.png
new file mode 100644
index 00000000..c8bea2a0
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_ATAN_SATURATE.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_BUS_DOWNLINK.png b/doc/artsbuilder/images/Synth_BUS_DOWNLINK.png
new file mode 100644
index 00000000..a2ad7c93
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_BUS_DOWNLINK.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_BUS_UPLINK.png b/doc/artsbuilder/images/Synth_BUS_UPLINK.png
new file mode 100644
index 00000000..3253ce69
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_BUS_UPLINK.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_CDELAY.png b/doc/artsbuilder/images/Synth_CDELAY.png
new file mode 100644
index 00000000..e9df7981
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_CDELAY.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_COMPRESSOR.png b/doc/artsbuilder/images/Synth_COMPRESSOR.png
new file mode 100644
index 00000000..2452a237
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_COMPRESSOR.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_DEBUG.png b/doc/artsbuilder/images/Synth_DEBUG.png
new file mode 100644
index 00000000..9c03a732
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_DEBUG.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_DELAY.png b/doc/artsbuilder/images/Synth_DELAY.png
new file mode 100644
index 00000000..dba8d715
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_DELAY.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_DIV.png b/doc/artsbuilder/images/Synth_DIV.png
new file mode 100644
index 00000000..5b811cdd
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_DIV.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_ENVELOPE_ADSR.png b/doc/artsbuilder/images/Synth_ENVELOPE_ADSR.png
new file mode 100644
index 00000000..31bda8ca
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_ENVELOPE_ADSR.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_FILEPLAY.png b/doc/artsbuilder/images/Synth_FILEPLAY.png
new file mode 100644
index 00000000..c68dec67
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_FILEPLAY.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_FM_SOURCE.png b/doc/artsbuilder/images/Synth_FM_SOURCE.png
new file mode 100644
index 00000000..a0da9390
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_FM_SOURCE.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_FREQUENCY.png b/doc/artsbuilder/images/Synth_FREQUENCY.png
new file mode 100644
index 00000000..4e038f69
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_FREQUENCY.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_MIDI_DEBUG.png b/doc/artsbuilder/images/Synth_MIDI_DEBUG.png
new file mode 100644
index 00000000..30c18efa
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_MIDI_DEBUG.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_MIDI_ROUTER.png b/doc/artsbuilder/images/Synth_MIDI_ROUTER.png
new file mode 100644
index 00000000..6115b6b4
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_MIDI_ROUTER.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_MUL.png b/doc/artsbuilder/images/Synth_MUL.png
new file mode 100644
index 00000000..0c98e4c8
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_MUL.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_NIL.png b/doc/artsbuilder/images/Synth_NIL.png
new file mode 100644
index 00000000..997ef1e0
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_NIL.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_PLAY.png b/doc/artsbuilder/images/Synth_PLAY.png
new file mode 100644
index 00000000..7f318b78
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_PLAY.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_PLAY_AKAI.png b/doc/artsbuilder/images/Synth_PLAY_AKAI.png
new file mode 100644
index 00000000..6e5c7988
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_PLAY_AKAI.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_PLAY_AKAIS.png b/doc/artsbuilder/images/Synth_PLAY_AKAIS.png
new file mode 100644
index 00000000..9b8a95cd
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_PLAY_AKAIS.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_PLAY_WAV.png b/doc/artsbuilder/images/Synth_PLAY_WAV.png
new file mode 100644
index 00000000..61d714ea
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_PLAY_WAV.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_PSCALE.png b/doc/artsbuilder/images/Synth_PSCALE.png
new file mode 100644
index 00000000..56e645b9
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_PSCALE.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_RC.png b/doc/artsbuilder/images/Synth_RC.png
new file mode 100644
index 00000000..b86b1dfb
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_RC.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_SEQUENCE.png b/doc/artsbuilder/images/Synth_SEQUENCE.png
new file mode 100644
index 00000000..25594c28
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_SEQUENCE.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_SEQUENCE_FREQ.png b/doc/artsbuilder/images/Synth_SEQUENCE_FREQ.png
new file mode 100644
index 00000000..07126bf7
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_SEQUENCE_FREQ.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_SHELVE_CUTOFF.png b/doc/artsbuilder/images/Synth_SHELVE_CUTOFF.png
new file mode 100644
index 00000000..9a97e853
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_SHELVE_CUTOFF.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_STD_EQUALIZER.png b/doc/artsbuilder/images/Synth_STD_EQUALIZER.png
new file mode 100644
index 00000000..7a3955f2
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_STD_EQUALIZER.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_STRUCT_KILL.png b/doc/artsbuilder/images/Synth_STRUCT_KILL.png
new file mode 100644
index 00000000..6c4d7927
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_STRUCT_KILL.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_WAVE_SIN.png b/doc/artsbuilder/images/Synth_WAVE_SIN.png
new file mode 100644
index 00000000..6d26eab6
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_WAVE_SIN.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_WAVE_SQUARE.png b/doc/artsbuilder/images/Synth_WAVE_SQUARE.png
new file mode 100644
index 00000000..ba99e85c
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_WAVE_SQUARE.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_WAVE_TRI.png b/doc/artsbuilder/images/Synth_WAVE_TRI.png
new file mode 100644
index 00000000..62083f40
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_WAVE_TRI.png
Binary files differ
diff --git a/doc/artsbuilder/images/Synth_XFADE.png b/doc/artsbuilder/images/Synth_XFADE.png
new file mode 100644
index 00000000..90498689
--- /dev/null
+++ b/doc/artsbuilder/images/Synth_XFADE.png
Binary files differ
diff --git a/doc/artsbuilder/images/schema1.png b/doc/artsbuilder/images/schema1.png
new file mode 100644
index 00000000..8bd236d0
--- /dev/null
+++ b/doc/artsbuilder/images/schema1.png
Binary files differ
diff --git a/doc/artsbuilder/images/schema2.png b/doc/artsbuilder/images/schema2.png
new file mode 100644
index 00000000..2d70af0b
--- /dev/null
+++ b/doc/artsbuilder/images/schema2.png
Binary files differ
diff --git a/doc/artsbuilder/images/schema3.png b/doc/artsbuilder/images/schema3.png
new file mode 100644
index 00000000..9e1adf41
--- /dev/null
+++ b/doc/artsbuilder/images/schema3.png
Binary files differ
diff --git a/doc/artsbuilder/images/schema4.png b/doc/artsbuilder/images/schema4.png
new file mode 100644
index 00000000..834ac2ed
--- /dev/null
+++ b/doc/artsbuilder/images/schema4.png
Binary files differ
diff --git a/doc/artsbuilder/index.docbook b/doc/artsbuilder/index.docbook
new file mode 100644
index 00000000..ba6649a1
--- /dev/null
+++ b/doc/artsbuilder/index.docbook
@@ -0,0 +1,393 @@
+<?xml version="1.0" ?>
+<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
+ <!ENTITY kappname "&arts;" >
+ <!ENTITY tools SYSTEM "tools.docbook">
+ <!ENTITY artsbuilder-doc SYSTEM "artsbuilder.docbook">
+ <!ENTITY detail SYSTEM "detail.docbook">
+ <!ENTITY arts-midi SYSTEM "midi.docbook">
+ <!ENTITY gui SYSTEM "gui.docbook">
+ <!ENTITY mcop-ref SYSTEM "mcop.docbook">
+ <!ENTITY arts-mcop SYSTEM "mcop.docbook">
+ <!ENTITY apis SYSTEM "apis.docbook">
+ <!ENTITY modules SYSTEM "modules.docbook">
+ <!ENTITY porting SYSTEM "porting.docbook">
+ <!ENTITY helping SYSTEM "helping.docbook">
+ <!ENTITY future SYSTEM "future.docbook">
+ <!ENTITY references SYSTEM "references.docbook">
+ <!ENTITY arts-faq SYSTEM "faq.docbook">
+ <!ENTITY arts-glossary SYSTEM "glossary.docbook">
+ <!ENTITY digitalaudio SYSTEM "digitalaudio.docbook">
+ <!ENTITY midiintro SYSTEM "midiintro.docbook">
+ <!ENTITY MCOP "<acronym>MCOP</acronym>">
+ <!ENTITY DCOP "<acronym>DCOP</acronym>">
+ <!ENTITY MIDI "<acronym>MIDI</acronym>">
+ <!ENTITY mcopidl "<application>mcopidl</application>">
+ <!ENTITY IDL "<acronym>IDL</acronym>">
+ <!ENTITY % English "INCLUDE" > <!-- change language only here -->
+ <!ENTITY % addindex "IGNORE">
+]>
+
+<book lang="&language;">
+<bookinfo>
+<title>The &arts; Handbook</title>
+<authorgroup>
+
+<author>
+<firstname>Stefan</firstname>
+<surname>Westerfeld</surname>
+<affiliation>
+<address><email>stefan@space.twc.de</email></address>
+</affiliation>
+</author>
+
+<author>
+<firstname>Jeff</firstname>
+<surname>Tranter</surname>
+<affiliation>
+<address><email>tranter@kde.org</email></address>
+</affiliation>
+</author>
+
+<!-- TRANS:ROLES_OF_TRANSLATORS -->
+</authorgroup>
+
+<copyright>
+<year>1999-2001</year>
+<holder>Stefan Westerfeld &amp; Jeff Tranter</holder>
+</copyright>
+<legalnotice>&FDLNotice;</legalnotice>
+
+<date>2001-06-10</date>
+<releaseinfo>1.00.09</releaseinfo>
+
+<abstract><para>This handbook describes &arts;, the Analog Real-time
+Synthesizer.</para>
+
+</abstract>
+
+<keywordset>
+<keyword>aRts</keyword>
+<keyword>artsbuilder</keyword>
+<keyword>synthesizer</keyword>
+<keyword>multimedia</keyword>
+<keyword>structure</keyword>
+<keyword>music</keyword>
+<keyword>sound</keyword>
+<keyword>KDE</keyword>
+</keywordset>
+</bookinfo>
+
+<chapter id="introduction">
+<title>Introduction</title>
+
+<sect1 id="what-is-arts">
+<title>What is &arts;?</title>
+
+<para>The Analog Real-Time Synthesizer, or &arts;, is a modular system
+for synthesizing sound and music on a digital computer. Using small
+building blocks called modules, the user can easily build complex audio
+processing tools. Modules typically provide functions such as sound
+waveform generators, filters, audio effects, mixing, and playback of
+digital audio in different file formats.</para>
+
+<para>The &artsd; sound server mixes audio from several sources in real
+time, allowing multiple sound applications to transparently share access
+to sound hardware.</para>
+
+<para>Using &MCOP;, the Multimedia Communication Protocol, multimedia
+applications can be network transparent, authenticated for security, and
+cross-platform using interfaces defined in a language-independent way
+using &IDL;. Support is also provided for non &arts;-aware legacy
+applications. As a core component of the &kde; 2 desktop environment,
+&arts; provides the basis for the &kde; multimedia architecture, and
+will in future support more media types including video. Like &kde;,
+&arts; runs on a number of operating systems, including &Linux; and BSD
+variants. It can also be used independently of &kde;.</para>
+
+</sect1>
+
+<sect1 id="using-this-manual">
+<title>Using This Manual</title>
+
+<para>This manual is intended to provide comprehensive documentation on
+&arts; for users at different skill levels. Depending on whether you are
+a casual user of multimedia applications that make use of &arts; or a
+multimedia application developer, you may want to take different paths
+through the manual.</para>
+
+<para>It is suggested that you first read the <link
+linkend="installation">Downloading and Building &arts;</link> chapter if
+you need to get &arts; initially installed and running. If you already
+have a working system, likely bundled with your operating system
+distribution, you may choose to skip this section.</para>
+
+<para>You should then read the sections in the <link
+linkend="arts-tools">&arts; Tools</link> chapter, especially &artsd;,
+artscontrol;, &artsshell;, and &artsdsp;. This will help you make the
+most effective use of &arts;.</para>
+
+<para>If you are interested in going further with &arts;, read the
+chapter on <link linkend="artsbuilder">&arts-builder;</link> and go
+through the tutorial. This should give you an appreciation of the
+powerful capabilities of &arts; and the provided modules that can be
+used without the need to be a programmer.</para>
+
+<para>If you want to know more about the internals of &arts;, either to
+develop multimedia applications or extend &arts; itself, read some or
+all of the chapter <link linkend="arts-in-detail">&arts; in
+Detail</link>. This should give you an understanding of all of the
+concepts that are prerequisites to &arts; software development.</para>
+
+<para>If you are interested specifically in the <acronym>MIDI</acronym>
+capabilities of &arts;, you should read the chapter on <link
+linkend="midi">&MIDI;</link>.</para>
+
+<!-- TODO
+<para>To learn more about the &arts; graphical elements, either as an advanced
+user of artsbuilder or to create new elements, read the section on <link
+linkend="gui-elements"><acronym>GUI</acronym> Elements</link>.</para>
+-->
+
+<para>If you want to develop &arts;-aware multimedia applications, the
+<link linkend="arts-apis">&arts; Application Programming
+Interfaces</link> chapter covers the different <acronym>API</acronym>s
+in detail.</para>
+
+<para>If you want to extend &arts; by creating new modules, read the
+<link linkend="arts-modules">&arts; Modules</link> chapter.</para>
+
+<para>If you are modifying an existing application to run under &arts;,
+read the chapter on <link linkend="porting">Porting Applications to
+&arts;</link>.</para>
+
+<para>You you can find out how to help contribute to the &arts; project
+in the <link linkend="contributing">Contributing to &arts;</link>
+chapter, read about upcoming &arts; development in the chapter on <link
+linkend="future-work">Future Work</link>, and find links to more
+information in the <link linkend="references">References</link>
+section.</para>
+
+<para>We have also rounded out the manual with some additional material,
+including <link linkend="faq">answers to frequently asked
+questions</link>, a <link linkend="contributors">list of
+contributors</link>, the details on &arts; <link
+linkend="copyright-and-licenses">copyright and licensing</link>, and
+some background material on <link linkend="intro-digital-audio">digital
+audio</link> and <link
+linkend="midi-introduction">&MIDI;</link>. A <link
+linkend="glossary">glossary</link> of terms is also included.</para>
+
+<note>
+<para>
+This manual is still very much a work in progress. You are welcome to
+contribute by writing portions of it, but if you wish to do so, contact
+Jeff Tranter <email>tranter@kde.org</email> or Stefan Westerfeld
+<email>stefan@space.twc.de</email> first to avoid duplication of effort.
+</para>
+</note>
+
+</sect1>
+
+<sect1 id="history">
+<title>History</title>
+
+<para>
+In late 1997 Stefan Westerfeld started working on a real-time, modular
+system for sound synthesis. The code initially ran on a PowerPC system
+running &AIX;. This first implementation was quite simple but supported
+a full-featured flow system that was able to do such things as play MP3
+files and pipe audio streams through effects modules.
+</para>
+
+
+<para>The next step was to implement a &GUI; so that modules could be
+manipulated graphically. Stefan had had some good experience using
+&kde;, so that was chosen as the &GUI; toolkit, (knowing that it might
+be necessary to do a GNOME/Gtk+ version as well) and this later led to
+using &Linux; as the main development platform. Originally named
+<application>ksynth</application>, the project was renamed &arts; and
+the pace of development accelerated. The project at this stage was quite
+complete, with a <acronym>CORBA</acronym>-based protocol, dozens of
+modules, a graphical module editing tool, C and C++
+<acronym>API</acronym>s, documentation, utilities, and a mailing list
+and web site with a small group of developers. The project had come a
+long way after only a little more than a year of development.</para>
+
+<para>As the &kde; team started planning for &kde; 2.0, it became clear
+that &kde; needed a more powerful infrastructure for sound and other
+streaming media. It was decided to adapt &arts;, as it was a good step
+in this direction with a proven architecture. Much new development
+effort went into this new version of &arts;, most notably the
+replacement of the <acronym>CORBA</acronym> code with an entirely new
+subsystem, &MCOP;, optimized for multimedia. Version 0.4 of &arts; was
+included in the &kde; 2.0 release.</para>
+
+<para>Work continues on &arts;, improving performance and adding new
+functionality. It should be noted that even though &arts; is now a core
+component of &kde;, it can be used without &kde;, and is also being used
+for applications that go beyond traditional multimedia. The project has
+attracted some interest from the GNOME team, opening up the possibility
+that it may someday become the standard multimedia architecture for
+&UNIX; desktop systems.</para>
+
+</sect1>
+
+</chapter>
+
+&tools;
+&artsbuilder-doc;
+&detail;
+&arts-midi;
+&gui;
+&mcop-ref;
+&apis;
+&modules;
+&porting;
+&helping;
+&future;
+&references;
+&arts-faq;
+
+<chapter id="copyright-and-licenses">
+
+<title>&arts; Copyright and Licensing</title>
+
+<para>&arts; software copyright 1998-2001 Stefan Westerfeld
+<email>stefan@space.twc.de</email></para>
+
+<para><anchor id="contributors" />
+Documentation copyright 1999-2001
+Stefan Westerfeld <email>stefan@space.twc.de</email> and
+Jeff Tranter <email>tranter@kde.org</email>.
+</para>
+<!-- TRANS:CREDIT_FOR_TRANSLATORS -->
+
+&underFDL;
+
+<para>
+All libraries that are in &arts; are licensed under the terms of the
+<acronym>GNU</acronym> Lesser General Public license. The vast majority of the
+&arts; code is in the libraries, including the whole of <acronym>MCOP</acronym>
+and ArtsFlow. This allows the libraries to be used for non-free/non-open source
+applications if desired.
+</para>
+
+<para>There are a few programs (such as <application>artsd</application>), that
+are released under the terms of the <acronym>GNU</acronym> General Public
+License. As there have been different opinions on whether or not linking
+<acronym>GPL</acronym> programs with &Qt; is legal, I also added an explicit
+notice which allows that, in addition to the <acronym>GPL</acronym>: permission
+is also granted to link this program with the &Qt; library, treating &Qt; like a
+library that normally accompanies the operating system kernel, whether or not
+that is in fact the case.</para>
+
+</chapter>
+
+<appendix id="installation">
+<title>Installing &arts;</title>
+
+<para>
+In order to use &arts; you obviously need to have it installed and running on
+your system. There are two approaches for doing this, which are described in the
+next sections.
+</para>
+
+<sect1 id="binary-install">
+<title>Installing a Precompiled Binary Release</title>
+
+<para>
+The quickest and easiest way to get &arts; up and running is to install
+precompiled binary packages for your system. Most recent &Linux; distributions
+include &kde;, and if it is &kde; 2.0 or later it will include &arts;. If &kde;
+is not included on your installation media it may be available as a download
+from your operating system vendor. Alternatively it may be available from third
+parties. Make sure that you use packages that are compatible with your operating
+system version.
+</para>
+
+<para>
+A basic install of &kde; will include the sound server, allowing most
+applications to play sound. If you want the full set of multimedia tools and
+applications you will likely need to install additional optional packages.
+</para>
+
+<para>
+The disadvantage of using precompiled binaries is that they may not be the most
+recent version of &arts;. This is particularly likely if they are provided on
+&CD-ROM;, as the pace of development of &arts; and &kde; is such that &CD-ROM;
+media cannot usually keep pace. You may also find that, if you have one of the
+less common architectures or operating system distributions, precompiled binary
+packages may not be available and you will need to use the second method.
+</para>
+
+</sect1>
+
+<sect1 id="source-install">
+<title>Building From Source</title>
+
+<para>
+While time consuming, the most flexible way to build &arts; is to compile it
+yourself from source code. This ensures you have a version compiled optimally
+for your system configuration and allows you to build the most recent version.
+</para>
+
+<para>
+You have two choices here -- you can either install the most recent stable
+version included with &kde; or you can get the most recent (but possibly
+unstable) version directly from the &kde; project <acronym>CVS</acronym>
+repository. Most users who aren't developing for &arts; should use the stable
+version. You can download it from <ulink
+url="ftp://ftp.kde.org">ftp://ftp.kde.org</ulink> or one of the many mirror
+sites. If you are actively developing for &arts; you probably want to use the
+<acronym>CVS</acronym> version. If you want to use aRts without KDE, you can
+download a standalone development snapshot from
+<ulink url="http://space.twc.de/~stefan/kde/arts-snapshot-doc.html">
+http://space.twc.de/~stefan/kde/arts-snapshot-doc.html</ulink>.
+</para>
+
+<para>
+Note that if you are building from <acronym>CVS</acronym>, some components
+of &arts; (&ie; the basic core components including the sound server) are found
+in the <acronym>CVS</acronym> module kdelibs, while additional components (&eg;
+<application>artsbuilder</application>) are included in the. This may change in
+the future. You may also find a version in the kmusic module; this is the old
+(pre-&kde; 2.0) version which is now obsolete.
+</para>
+
+<para>
+The requirements for building &arts; are essentially the same as for building
+&kde;. The configure scripts should detect your system configuration and
+indicate if any required components are missing. Make sure that you have a
+working sound driver on your system (either the <acronym>OSS</acronym>/Free
+driver in the kernel, <acronym>OSS</acronym> driver from 4Front
+Technologies, or
+<acronym>ALSA</acronym> driver with <acronym>OSS</acronym> emulation).
+</para>
+
+<para>More information on downloading and installing &kde; (including &arts;)
+can be found in the <ulink
+url="http://www.kde.org/documentation/faq/index.html">&kde;
+&FAQ;</ulink>.</para>
+
+</sect1>
+
+</appendix>
+
+&digitalaudio;
+&midiintro;
+&arts-glossary;
+
+</book>
+<!--
+Local Variables:
+mode: sgml
+sgml-omittag:nil
+sgml-shorttag:t
+sgml-namecase-general:t
+sgml-general-insert-case:lower
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:0
+sgml-indent-data:nil
+End:
+-->
diff --git a/doc/artsbuilder/mcop.docbook b/doc/artsbuilder/mcop.docbook
new file mode 100644
index 00000000..86aa03b5
--- /dev/null
+++ b/doc/artsbuilder/mcop.docbook
@@ -0,0 +1,2274 @@
+<!-- <?xml version="1.0" ?>
+<!DOCTYPE chapter PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd">
+To validate or process this file as a standalone document, uncomment
+this prolog. Be sure to comment it out again when you are done -->
+
+<chapter id="mcop">
+<title>&MCOP;: Object Model and Streaming</title>
+
+<sect1 id="mcop-overview">
+
+<title>Overview</title>
+
+<para>
+&MCOP; is the standard &arts; uses for:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+Communication between objects.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Network transparency.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Describing object interfaces.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Language independancy.
+</para>
+</listitem>
+</itemizedlist>
+
+<para>
+One major aspect of &MCOP; is the <emphasis>interface description
+language</emphasis>, &IDL;, in which many of the &arts; interfaces and
+<acronym>API</acronym>s are defined in a language independent way.
+</para>
+
+<para>
+To use &IDL; interfaces from C++, is compiled by the &IDL;
+compiler into C++ code. When you implement an interface, you derive from
+the skeleton class the &IDL; compiler has generated. When you use an
+interface, you do so using a wrapper. This way, &MCOP; can use a
+protocol if the object you are talking to is not local - you get network
+transparency.
+</para>
+
+<para>
+This chapter is supposed to describe the basic features of the object
+model that results from the use of &MCOP;, the protocol, how do use
+&MCOP; in C++ (language binding), and so on.
+</para>
+
+</sect1>
+
+<sect1 id="interfaces">
+
+<title>Interfaces and &IDL;</title>
+
+<para>
+Many of the services provided by &arts;, such as modules and the sound
+server, are defined in terms of <acronym>interfaces</acronym>.
+Interfaces are specified in a programming language independent format:
+&IDL;.
+</para>
+
+<para>
+This allows many of the implementation details such as the format of
+multimedia data streams, network transparency, and programming language
+dependencies, to be hidden from the specification for the interface. A
+tool, &mcopidl;, translates the interface
+definition into a specific programming language (currently only C++ is
+supported).
+</para>
+
+<para>
+The tool generates a skeleton class with all of the boilerplate code and
+base functionality. You derive from that class to implement the features
+you want.
+</para>
+
+<para>
+The &IDL; used by &arts; is similar to that used by
+<acronym>CORBA</acronym> and <acronym>DCOM</acronym>.
+</para>
+
+<para>
+&IDL; files can contain:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+C-style #include directives for other &IDL; files.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Definitions of enumerated and struct types, as in C/C++.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Definitions of interfaces.
+</para>
+</listitem>
+</itemizedlist>
+
+<para>
+In &IDL;, interfaces are defined much like a C++ class or C struct,
+albeit with some restrictions. Like C++, interfaces can subclass other
+interfaces using inheritance. Interface definitions can include three
+things: streams, attributes, and methods.
+</para>
+
+<sect2 id="streams">
+
+<title>Streams</title>
+
+<para>
+Streams define multimedia data, one of the most important components of
+a module. Streams are defined in the following format:
+</para>
+
+<para>
+[ async ] in|out [ multi ] <replaceable>type</replaceable> stream <replaceable>name</replaceable> [ , <replaceable>name</replaceable> ] ;
+</para>
+
+<para>
+Streams have a defined direction in reference to the module, as
+indicated by the required qualifiers in or out. The type argument
+defines the type of data, which can be any of the types described later
+for attributes (not all are currently supported). Many modules use the
+stream type audio, which is an alias for float since that is the
+internal data format used for audio stream. Multiple streams of the same
+type can defined in the same definition uisng comma separated names.
+</para>
+
+<para>
+Streams are by default synchronous, which means they are continuous
+flows of data at a constant rate, such as <acronym>PCM</acronym>
+audio. The async qualifier specifies an asynchronous stream, which is
+used for non-continuous data flows. The most common example of an async
+stream is &MIDI; messages.
+</para>
+
+<para>
+The multi keyword, only valid for input streams, indicates that the
+interface supports a variable number of inputs. This is useful for
+implementing devices such as mixers that can accept any number of input
+streams.
+</para>
+
+</sect2>
+<sect2 id="attributes">
+
+<title>Attributes</title>
+
+<para>
+Attributes are data associated with an instance of an interface. They
+are declared like member variables in C++, and can can use any of the
+primitive types boolean, byte, long, string, or float. You can also use
+user-defined struct or enum types as well as variable sized sequences
+using the syntax sequence&lt;type&gt;. Attributes can optionally be
+marked readonly.
+</para>
+
+</sect2>
+<sect2 id="methods">
+
+<title>Methods</title>
+
+<para>
+As in C++, methods can be defined in interfaces. The method parameters
+are restricted to the same types as attributes. The keyword oneway
+indicates a method which returns immediately and is executed
+asynchronously.
+</para>
+
+</sect2>
+
+<sect2 id="standardinterfaces">
+
+<title>Standard Interfaces</title>
+
+<para>
+Several standard module interfaces are already defined for you in
+&arts;, such as <interfacename>StereoEffect</interfacename>, and
+<interfacename>SimpleSoundServer</interfacename>.
+</para>
+
+</sect2>
+
+<sect2 id="example">
+<title>Example</title>
+
+<para>
+A simple example of a module taken from &arts; is the constant delay
+module, found in the file
+<filename>kdemultimedia/arts/modules/artsmodules.idl</filename>. The
+interface definition is listed below.
+</para>
+
+<programlisting>
+interface Synth_CDELAY : SynthModule {
+ attribute float time;
+ in audio stream invalue;
+ out audio stream outvalue;
+};
+</programlisting>
+
+<para>
+This modules inherits from
+<interfacename>SynthModule</interfacename>. That interface, defined in
+<filename>artsflow.idl</filename>, defines the standard methods
+implemented in all music synthesizer modules.
+</para>
+
+<para>
+The CDELAY effect delays a stereo audio stream by the time value
+specified as a floating point parameter. The interface definition has an
+attribute of type float to store the delay value. It defines two input
+audio streams and two output audio streams (typical of stereo
+effects). No methods are required other than those it inherits.
+</para>
+
+</sect2>
+
+</sect1>
+
+<sect1 id="more-about-streams">
+<title>More About Streams</title>
+
+<para>
+This section covers some additional topics related to streams.
+</para>
+
+<sect2 id="stream-types">
+<title>Stream Types</title>
+
+<para>
+There are various requirements for how a module can do streaming. To
+illustrate this, consider these examples:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+Scaling a signal by a factor of two.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Performing sample frequency conversion.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Decompressing a run-length encoded signal.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Reading &MIDI; events from <filename
+class="devicefile">/dev/midi00</filename> and inserting them into a
+stream.
+</para>
+</listitem>
+</itemizedlist>
+
+<para>
+The first case is the simplest: upon receiving 200 samples of input the
+module produces 200 samples of output. It only produces output when it
+gets input.
+</para>
+
+<para>
+The second case produces different numbers of output samples when given
+200 input samples. It depends what conversion is performed, but the
+number is known in advance.
+</para>
+
+<para>
+The third case is even worse. From the outset you cannot even guess how
+much data 200 input bytes will generate (probably a lot more than 200
+bytes, but...).
+</para>
+
+<para>
+The last case is a module which becomes active by itself, and sometimes
+produces data.
+</para>
+
+<para>
+In &arts;s-0.3.4, only streams of the first type were handled, and most
+things worked nicely. This is probably what you need most when writing
+modules that process audio. The problem with the other, more complex
+types of streaming, is that they are hard to program, and that you don't
+need the features most of the time. That is why we do this with two
+different stream types: synchronous and asynchronous.
+</para>
+
+<para>
+Synchronous streams have these characteristics:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+Modules must be able to calculate data of any length, given enough
+input.
+</para>
+</listitem>
+
+<listitem>
+<para>
+All streams have the same sampling rate.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The <function>calculateBlock()</function> function will be called when
+enough data is available, and the module can rely on the pointers
+pointing to data.
+</para>
+</listitem>
+
+<listitem>
+<para>
+There is no allocation and deallocation to be done.
+</para>
+</listitem>
+</itemizedlist>
+
+<para>
+Asynchronous streams, on the other hand, have this behavior:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+Modules may produce data sometimes, or with varying sampling rate, or
+only if they have input from some filed descriptor. They are not bound by
+the rule <quote>must be able to satisfy requests of any size</quote>.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Asynchronous streams of a module may have entirely different sampling
+rates.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Outgoing streams: there are explicit functions to allocate packets, to
+send packets - and an optional polling mechanism that will tell you when
+you should create some more data.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Incoming streams: you get a call when you receive a new packet - you
+have to say when you are through with processing all data of that
+packet, which must not happen at once (you can say that anytime later,
+and if everybody has processed a packet, it will be freed/reused)
+</para>
+</listitem>
+</itemizedlist>
+
+<para>
+When you declare streams, you use the keyword <quote>async</quote> to
+indicate you want to make an asynchronous stream. So, for instance,
+assume you want to convert an asynchronous stream of bytes into a
+synchronous stream of samples. Your interface could look like this:
+</para>
+
+<programlisting>
+interface ByteStreamToAudio : SynthModule {
+ async in byte stream indata; // the asynchronous input sample stream
+
+ out audio stream left,right; // the synchronous output sample streams
+};
+</programlisting>
+
+</sect2>
+
+<sect2 id="async-streams">
+<title>Using Asynchronous Streams</title>
+
+<para>
+Suppose you decided to write a module to produce sound
+asynchronously. Its interface could look like this:
+</para>
+
+<programlisting>
+interface SomeModule : SynthModule
+{
+ async out byte stream outdata;
+};
+</programlisting>
+
+<para>
+How do you send the data? The first method is called <quote>push
+delivery</quote>. With asynchronous streams you send the data as
+packets. That means you send individual packets with bytes as in the
+above example. The actual process is: allocate a packet, fill it, send
+it.
+</para>
+
+<para>
+Here it is in terms of code. First we allocate a packet:
+</para>
+
+<programlisting>
+DataPacket&lt;mcopbyte&gt; *packet = outdata.allocPacket(100);
+</programlisting>
+
+<para>
+The we fill it:
+</para>
+
+<programlisting>
+// cast so that fgets is happy that it has a (char *) pointer
+char *data = (char *)packet-&gt;contents;
+
+// as you can see, you can shrink the packet size after allocation
+// if you like
+if(fgets(data,100,stdin))
+ packet-&gt;size = strlen(data);
+else
+ packet-&gt;size = 0;
+</programlisting>
+
+<para>
+Now we send it:
+</para>
+
+<programlisting>
+packet-&gt;send();
+</programlisting>
+
+<para>
+This is quite simple, but if we want to send packets exactly as fast as
+the receiver can process them, we need another approach, the <quote>pull
+delivery</quote> method. You ask to send packets as fast as the receiver
+is ready to process them. You start with a certain amount of packets you
+send. As the receiver processes one packet after another, you start
+refilling them with fresh data, and send them again.
+</para>
+
+<para>
+You start that by calling setPull. For example:
+</para>
+
+<programlisting>
+outdata.setPull(8, 1024);
+</programlisting>
+
+<para>
+This means that you want to send packets over outdata. You want to start
+sending 8 packets at once, and as the receiver processes some of them,
+you want to refill them.
+</para>
+
+<para>
+Then, you need to implement a method which fills the packets, which could
+look like this:
+</para>
+
+<programlisting>
+void request_outdata(DataPacket&lt;mcopbyte&gt; *packet)
+{
+ packet-&gt;size = 1024; // shouldn't be more than 1024
+ for(int i = 0;i &lt; 1024; i++)
+ packet-&gt;contents[i] = (mcopbyte)'A';
+ packet-&gt;send();
+}
+</programlisting>
+
+<para>
+Thats it. When you don't have any data any more, you can start sending
+packets with zero size, which will stop the pulling.
+</para>
+
+<para>
+Note that it is essential to give the method the exact name
+<methodname>request_<replaceable>streamname</replaceable></methodname>.
+</para>
+
+<para>
+We just discussed sending data. Receiving data is much much
+simpler. Suppose you have a simple ToLower filter, which simply converts
+all letters in lowercase:
+</para>
+
+<programlisting>
+interface ToLower {
+ async in byte stream indata;
+ async out byte stream outdata;
+};
+</programlisting>
+
+<para>
+This is really simple to implement; here is the whole implementation:
+</para>
+
+<programlisting>
+class ToLower_impl : public ToLower_skel {
+public:
+ void process_indata(DataPacket&lt;mcopbyte&gt; *inpacket)
+ {
+ DataPacket&lt;mcopbyte&gt; *outpacket = outdata.allocPacket(inpacket-&gt;size);
+
+ // convert to lowercase letters
+ char *instring = (char *)inpacket-&gt;contents;
+ char *outstring = (char *)outpacket-&gt;contents;
+
+ for(int i=0;i&lt;inpacket-&gt;size;i++)
+ outstring[i] = tolower(instring[i]);
+
+ inpacket-&gt;processed();
+ outpacket-&gt;send();
+ }
+};
+
+REGISTER_IMPLEMENTATION(ToLower_impl);
+</programlisting>
+
+<para>
+Again, it is essential to name the method
+<methodname>process_<replaceable>streamname</replaceable></methodname>.
+</para>
+
+<para>
+As you see, for each arriving packet you get a call for a function (the
+<function>process_indata</function> call in our case). You need to call
+the <methodname>processed()</methodname> method of a packet to indicate
+you have processed it.
+</para>
+
+<para>
+Here is an implementation tip: if processing takes longer (&ie; if you
+need to wait for soundcard output or something like that), don't call
+processed immediately, but store the whole data packet and call
+processed only as soon as you really processed that packet. That way,
+senders have a chance to know how long it really takes to do your work.
+</para>
+
+<para>
+As synchronization isn't so nice with asynchronous streams, you should
+use synchronous streams wherever possible, and asynchronous streams only
+when necessary.
+</para>
+
+</sect2>
+
+<sect2 id="default-streams">
+<title>Default Streams</title>
+
+<para>
+Suppose you have 2 objects, for example an AudioProducer and an
+AudioConsumer. The AudioProducer has an output stream and AudioConsumer
+has an input one. Each time you want to connect them, you will use those
+2 streams. The first use of defaulting is to enable you to make the
+connection without specifying the ports in that case.
+</para>
+
+<para>
+Now suppose the teo objects above can handle stereo, and each have a
+<quote>left</quote> and <quote>right</quote> port. You'd still like to
+connect them as easily as before. But how can the connecting system
+know which output port to connect to which input port? It has no way to
+correctly map the streams. Defaulting is then used to specify several
+streams, with an order. Thus, when you connect an object with 2 default
+output streams to another one with 2 default input streams, you don't
+have to specify the ports, and the mapping will be done correctly.
+</para>
+
+<para>
+Of course, this is not limited to stereo. Any number of streams can be
+made default if needed, and the connect function will check that the
+number of defaults for 2 object match (in the required direction) if you
+don't specify the ports to use.
+</para>
+
+<para>
+The syntax is as follows: in the &IDL;, you can use the default keyword
+in the stream declaration, or on a single line. For example:
+</para>
+
+<programlisting>
+interface TwoToOneMixer {
+ default in audio stream input1, input2;
+ out audio stream output;
+};
+</programlisting>
+
+<para>
+In this example, the object will expect its two input ports to be
+connected by default. The order is the one specified on the default
+line, so an object like this one:
+</para>
+
+<programlisting>
+interface DualNoiseGenerator {
+ out audio stream bzzt, couic;
+ default couic, bzzt;
+};
+</programlisting>
+
+<para>
+Will make connections from <quote>couic</quote> to
+<quote>input1</quote>, and <quote>bzzt</quote> to <quote>input2</quote>
+automatically. Note that since there is only one output for the mixer,
+it will be made default in this case (see below). The syntax used in the
+noise generator is useful to declare a different order than the
+declaration, or selecting only a few ports as default. The directions of
+the ports on this line will be looked up by &mcopidl;, so don't specify
+them. You can even mix input and output ports in such a line, only the
+order is important.
+</para>
+
+<para>
+There are some rules that are followed when using inheritance:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+If a default list is specified in the &IDL;, then use
+it. Parent ports can be put in this list as well, whether they were
+default in the parent or not.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Otherwise, inherit parent's defaults. Ordering is parent1 default1,
+parent1 default2..., parent2 default1... If there is a common ancestor
+using 2 parent branches, a <quote>virtual public</quote>-like merging is
+done at that default's first occurrence in the list.
+</para>
+</listitem>
+
+<listitem>
+<para>
+If there is still no default and a single stream in a
+direction, use it as default for that direction.
+</para>
+</listitem>
+</itemizedlist>
+
+</sect2>
+
+</sect1>
+<sect1 id="attribute-change-notify">
+<title>Attribute change notifications</title>
+
+<!-- TODO: This should be embedded better into the context - I mean: the
+ context should be written ;-). -->
+
+<para>
+Attribute change notifications are a way to know when an attribute
+changed. They are a bit comparable with &Qt;'s or Gtk's signals and
+slots. For instance, if you have a &GUI; element, a slider, which
+configures a number between 0 and 100, you will usually have an object
+that does something with that number (for instance, it might be
+controlling the volume of some audio signal). So you would like that
+whenever the slider is moved, the object which scales the volume gets
+notified. A connection between a sender and a receiver.
+</para>
+
+<para>
+&MCOP; deals with that by being able to providing notifications when
+attributes change. Whatever is declared as <quote>attribute</quote> in
+the &IDL;, can emit such change notifications, and should do so,
+whenever it is modified. Whatever is declared as
+<quote>attribute</quote> can also receive such change notifications. So
+for instance if you had two &IDL; interfaces, like these:
+</para>
+
+<programlisting>
+ interface Slider {
+ attribute long min,max;
+ attribute long position;
+ };
+ interface VolumeControl : Arts::StereoEffect {
+ attribute long volume; // 0..100
+ };
+</programlisting>
+
+<para>
+You can connect them using change notifications. It works using the
+normal flowsystem connect operation. In this case, the C++ code to
+connect two objects would look like this:
+</para>
+
+<programlisting>
+#include &lt;connect.h&gt;
+using namespace Arts;
+[...]
+connect(slider,"position_changed",volumeControl,"volume");
+</programlisting>
+
+<para>
+As you see, each attribute offers two different streams, one for sending
+the change notifications, called
+<function><replaceable>attributename</replaceable>_changed</function>,
+
+<!-- TODO - how do I markup code that is an example - you wouldn't write
+ attributename in the source, but the name of some attribute
+
+ LW: I'm guessing
+ here, because I know how to markup QT code, but your stuff is different.
+ Hopefully this will give you inspiration, and we can work out later the fine
+ tuning if I have it wrong. The line above in the code sample, if it were qt
+ stuff, I would mark up this way (linebreaks for clarity of markup only, yes I
+ know it's incorrect!):
+
+ <function>connect(<classname>slider</classname>,
+ <function><replaceable>position</replaceable>_changed</function>,
+ <classname>volumeControl</classname>,
+ <function>volume</function>);</function>
+
+ You can use <function><replaceable>attributename</function> and even
+ <function><replaceable>attributename</replaceable>_changed</function>.
+
+ If I have the above totally wrong (which is entirely possible!) Some other
+ elements you might find handy:
+
+ <varname>, <type>, <returnvalue>, <constant>, <methodname>
+ There's also a markup guide at http://madmax.atconnex.net/kde/ that might
+ help, although unfortunately the programming section is still incomplete. -->
+
+ and one for receiving change notifications, called
+<function>attributename</function>.
+</para>
+
+<para>
+It is important to know that change notifications and asynchronous
+streams are compatible. They are also network transparent. So you can
+connect a change notification of a float attribute of a &GUI; widget has
+to an asynchronous stream of a synthesis module running on another
+computer. This of course also implies that change notifications are
+<emphasis>not synchronous</emphasis>, this means, that after you have
+sent the change notification, it may take some time until it really gets
+received.
+</para>
+
+<sect2 id="sending-change-notifications">
+
+<title>Sending change notifications</title>
+
+<para>
+When implementing objects that have attributes, you need to send change
+notifications whereever an attribute changes. The code for doing this
+looks like this:
+</para>
+
+<programlisting>
+ void KPoti_impl::value(float newValue)
+ {
+ if(newValue != _value)
+ {
+ _value = newValue;
+ value_changed(newValue); // &lt;- send change notification
+ }
+ }
+</programlisting>
+
+<para>
+It is strongly recommended to use code like this for all objects you
+implement, so that change notifications can be used by other people. You
+should however void sending notifications too often, so if you are doing
+signal processing, it is probably the best if you keep track when you
+sent your last notification, so that you don't send one with every
+sample you process.
+</para>
+
+</sect2>
+
+<sect2 id="change-notifications-apps">
+<title>Applications for change notifications</title>
+
+<para>
+It will be especially useful to use change notifications in conjunction
+with scopes (things that visualize audio data for instance), gui
+elements, control widgets, and monitoring. Code using this is in
+<filename class="directory">kdelibs/arts/tests</filename>, and in the
+experimental artsgui implementation, which you can find under <filename
+class="directory">kdemultimedia/arts/gui</filename>.
+</para>
+
+<!-- TODO: can I markup links into the source code - if yes, how? -->
+
+<!-- LW: Linking into the source is problematic - we can't assume people are
+reading this on a machine with the sources available, or that they aren't
+reading it from a website. We're working on it! -->
+
+</sect2>
+</sect1>
+
+<sect1 id="the-mcoprc-file">
+
+<title>The <literal role="extension">.mcoprc</literal> file</title>
+
+<para>
+The <literal role="extension">.mcoprc</literal> file (in each user's
+home folder) can be used to configure &MCOP; in some ways. Currently,
+the following is possible:
+</para>
+
+<variablelist>
+
+<varlistentry>
+<term>GlobalComm</term>
+<listitem>
+<para>
+The name of an interface to be used for global communication. Global
+communication is used to find other objects and obtain the secret
+cookie. Multiple &MCOP; clients/servers that should be able to talk to
+each other need to have a GlobalComm object which is able to share
+information between them. Currently, the possible values are
+<quote>Arts::TmpGlobalComm</quote> to communicate via <filename
+class="directory">/tmp/mcop-<replaceable>username</replaceable></filename>
+folder (which will only work on the local computer) and
+<quote>Arts::X11GlobalComm</quote> to communicate via the root window
+properties on the X11 server.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>TraderPath</term>
+
+<listitem>
+<para>
+Specifies where to look for trader information. You can list more than
+one folder here, and separate them with commas, like
+</para>
+</listitem>
+
+</varlistentry>
+
+<varlistentry>
+<term>ExtensionPath</term>
+
+<listitem>
+<para>
+Specifies from which folders extensions (in the form of shared
+libraries) are loaded. Multiple values can be specified comma separated.
+</para>
+</listitem>
+
+</varlistentry>
+</variablelist>
+
+<para>
+An example which uses all of the above is:
+</para>
+
+<programlisting>
+# $HOME/.mcoprc file
+GlobalComm=Arts::X11GlobalComm
+
+# if you are a developer, it might be handy to add a folder in your home
+# to the trader/extension path to be able to add components without
+# installing them
+TraderPath="/opt/kde2/lib/mcop","/home/joe/mcopdevel/mcop"
+ExtensionPath="/opt/kde2/lib","/home/joe/mcopdevel/lib"
+</programlisting>
+
+</sect1>
+
+<sect1 id="mcop-for-corba-users">
+<title>&MCOP; for <acronym>CORBA</acronym> Users</title>
+
+<para>
+If you have used <acronym>CORBA</acronym> before, you will see that
+&MCOP; is much the same thing. In fact, &arts; prior to version 0.4 used
+<acronym>CORBA</acronym>.
+</para>
+
+<para>
+The basic idea of <acronym>CORBA</acronym> is the same: you implement
+objects (components). By using the &MCOP; features, your objects are not
+only available as normal classes from the same process (via standard C++
+techniques) - they also are available to remote servers
+transparently. For this to work, the first thing you need to do is to
+specify the interface of your objects in an &IDL; file - just like
+<acronym>CORBA</acronym> &IDL;. There are only a few differences.
+</para>
+
+<sect2 id="corba-missing">
+<title><acronym>CORBA</acronym> Features That Are Missing In
+&MCOP;</title>
+
+<para>
+In &MCOP; there are no <quote>in</quote> and <quote>out</quote>
+parameters on method invocations. Parameters are always incoming, the
+return code is always outgoing, which means that the interface:
+</para>
+
+<programlisting>
+// CORBA idl
+interface Account {
+ void deposit( in long amount );
+ void withdraw( in long amount );
+ long balance();
+};
+</programlisting>
+
+<para>
+is written as
+</para>
+
+<programlisting>
+// MCOP idl
+interface Account {
+ void deposit( long amount );
+ void withdraw( long amount );
+ long balance();
+};
+</programlisting>
+
+<para>
+in &MCOP;.
+</para>
+
+<para>
+There is no exception support. &MCOP; doesn't have exceptions - it uses
+something else for error handling.
+</para>
+
+<para>
+There are no union types and no typedefs. I don't know if that is a real
+weakness, something one would desperately need to survive.
+</para>
+
+<para>
+There is no support for passing interfaces or object references
+</para>
+
+</sect2>
+
+<sect2 id="corba-different">
+<title><acronym>CORBA</acronym> Features That Are Different In
+&MCOP;</title>
+
+<para>
+You declare sequences as
+<quote>sequence<replaceable>type</replaceable></quote> in &MCOP;. There
+is no need for a typedef. For example, instead of:
+</para>
+
+<programlisting>
+// CORBA idl
+struct Line {
+ long x1,y1,x2,y2;
+};
+typedef sequence&lt;Line&gt; LineSeq;
+interface Plotter {
+ void draw(in LineSeq lines);
+};
+</programlisting>
+
+<para>
+you would write
+</para>
+
+<programlisting>
+// MCOP idl
+struct Line {
+ long x1,y1,x2,y2;
+};
+interface Plotter {
+ void draw(sequence&lt;Line&gt; lines);
+};
+</programlisting>
+
+</sect2>
+
+<sect2 id="no-in-corba">
+<title>&MCOP; Features That Are Not In <acronym>CORBA</acronym></title>
+
+<para>
+You can declare streams, which will then be evaluated by the &arts;
+framework. Streams are declared in a similar manner to attributes. For
+example:
+</para>
+
+<programlisting>
+// MCOP idl
+interface Synth_ADD : SynthModule {
+ in audio stream signal1,signal2;
+ out audio stream outvalue;
+};
+</programlisting>
+
+<para>
+This says that your object will accept two incoming synchronous audio
+streams called signal1 and signal2. Synchronous means that these are
+streams that deliver x samples per second (or other time), so that the
+scheduler will guarantee to always provide you a balanced amount of
+input data (&eg; 200 samples of signal1 are there and 200 samples
+signal2 are there). You guarantee that if your object is called with
+those 200 samples signal1 + signal2, it is able to produce exactly 200
+samples to outvalue.
+</para>
+
+</sect2>
+
+<sect2 id="mcop-binding">
+<title>The &MCOP; C++ Language Binding</title>
+
+<para>
+This differs from <acronym>CORBA</acronym> mostly:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+Strings use the C++ <acronym>STL</acronym> <classname>string</classname>
+class. When stored in sequences, they are stored <quote>plain</quote>,
+that means they are considered to be a primitive type. Thus, they need
+copying.
+</para>
+</listitem>
+
+<listitem>
+<para>
+longs are plain long's (expected to be 32 bit).
+</para>
+</listitem>
+
+<listitem>
+<para>
+Sequences use the C++ <acronym>STL</acronym>
+<classname>vector</classname> class.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Structures are all derived from the &MCOP; class
+<classname>Type</classname>, and generated by the &MCOP; &IDL;
+compiler. When stored in sequences, they are not stored
+<quote>plain</quote> , but as pointers, as otherwise, too much copying
+would occur.
+</para>
+</listitem>
+</itemizedlist>
+</sect2>
+
+<sect2 id="implementing-objects">
+<title>Implementing &MCOP; Objects</title>
+
+<para>
+After having them passed through the &IDL; compiler, you need to derive
+from the <classname>_skel</classname> class. For instance, consider you
+have defined your interface like this:
+</para>
+
+<programlisting>
+// MCOP idl: hello.idl
+interface Hello {
+ void hello(string s);
+ string concat(string s1, string s2);
+ long sum2(long a, long b);
+};
+</programlisting>
+
+<para>
+You pass that through the &IDL; compiler by calling
+<userinput><command>mcopidl</command>
+<parameter>hello.idl</parameter></userinput>, which will in turn generate
+<filename>hello.cc</filename> and <filename>hello.h</filename>. To
+implement it, you need to define a C++-class that inherits the skeleton:
+</para>
+
+<programlisting>
+// C++ header file - include hello.h somewhere
+class Hello_impl : virtual public Hello_skel {
+public:
+ void hello(const string&amp; s);
+ string concat(const string&amp; s1, const string&amp; s2);
+ long sum2(long a, long b);
+};
+</programlisting>
+
+<para>
+Finally, you need to implement the methods as normal C++
+</para>
+
+<programlisting>
+// C++ implementation file
+
+// as you see string's are passed as const string references
+void Hello_impl::hello(const string&amp; s)
+{
+ printf("Hello '%s'!\n",s.c_str());
+}
+
+// when they are a returncode they are passed as "normal" strings
+string Hello_impl::concat(const string&amp; s1, const string&amp; s2)
+{
+ return s1+s2;
+}
+
+long Hello_impl::sum2(long a, long b)
+{
+ return a+b;
+}
+</programlisting>
+
+<para>
+Once you do that, you have an object which can communicate using &MCOP;.
+Just create one (using the normal C++ facilities to create an object):
+</para>
+
+<programlisting>
+ Hello_impl server;
+</programlisting>
+
+<para>
+And as soon as you give somebody the reference
+</para>
+
+<programlisting>
+ string reference = server._toString();
+ printf("%s\n",reference.c_str());
+</programlisting>
+
+<para>
+and go to the &MCOP; idle loop
+</para>
+
+<programlisting>
+Dispatcher::the()-&gt;run();
+</programlisting>
+
+<para>
+People can access the thing using
+</para>
+
+<programlisting>
+// this code can run anywhere - not necessarily in the same process
+// (it may also run on a different computer/architecture)
+
+ Hello *h = Hello::_fromString([the object reference printed above]);
+</programlisting>
+
+<para>
+and invoke methods:
+</para>
+
+<programlisting>
+ if(h)
+ h-&gt;hello("test");
+ else
+ printf("Access failed?\n");
+</programlisting>
+
+</sect2>
+</sect1>
+
+<sect1 id="mcop-security">
+<title>&MCOP; Security Considerations</title>
+
+<para>
+Since &MCOP; servers will listen on a <acronym>TCP</acronym> port,
+potentially everybody (if you are on the Internet) may try to connect
+&MCOP; services. Thus, it is important to authenticate clients. &MCOP;
+uses the md5-auth protocol.
+</para>
+
+<para>
+The md5-auth protocol does the following to ensure that only selected
+(trusted) clients may connect to a server:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+It assumes you can give every client a secret cookie.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Every time a client connects, it verifies that this client knows that
+secret cookie, without actually transferring it (not even in a form that
+somebody listening to the network traffic could find it out).
+</para>
+</listitem>
+
+</itemizedlist>
+
+<para>
+To give each client the secret cookie, &MCOP; will (normally) put it in
+the <filename class="directory">mcop</filename> folder (under
+<filename
+class="directory">/tmp/mcop-<envar>USER</envar>/secret-cookie</filename>). Of
+course, you can copy it to other computers. However, if you do so, use a
+secure transfer mechanism, such as <command>scp</command> (from
+<application>ssh</application>).
+</para>
+
+<para>
+The authentication of clients uses the following steps:
+</para>
+
+<procedure>
+<step>
+<para>
+[SERVER] generate a new (random) cookie R
+</para>
+</step>
+
+<step>
+<para>
+[SERVER] send it to the client
+</para>
+</step>
+
+<step>
+<para>
+[CLIENT] read the "secret cookie" S from a file
+</para>
+</step>
+
+<step>
+<para>
+[CLIENT] mangle the cookies R and S to a mangled cookie M using the MD5
+algorithm
+</para>
+</step>
+
+<step>
+<para>
+[CLIENT] send M to the server
+</para>
+</step>
+
+<step>
+<para>
+[SERVER] verify that mangling R and S gives just the
+same thing as the cookie M received from the client. If yes,
+authentication is successful.
+</para>
+</step>
+
+</procedure>
+
+<para>
+This algorithm should be secure, given that
+</para>
+
+<orderedlist>
+<listitem>
+<para>
+The secret cookies and random cookies are <quote>random enough</quote>
+ and
+</para>
+</listitem>
+
+<listitem>
+<para>
+The MD5 hashing algorithm doesn't allow to find out the
+<quote>original text</quote>, that is the secret cookie S and the random
+cookie R (which is known, anyway), from the mangled cookie M.
+</para>
+</listitem>
+</orderedlist>
+
+<para>
+The &MCOP; protocol will start every new connection with an
+authentication process. Basically, it looks like this:
+</para>
+
+<procedure>
+
+<step>
+<para>
+Server sends a ServerHello message, which describes
+the known authentication protocols.
+</para>
+</step>
+
+<step>
+<para>
+Client sends a ClientHello message, which includes authentication info.
+</para>
+</step>
+
+<step>
+<para>
+Server sends an AuthAccept message.
+</para>
+</step>
+</procedure>
+
+<para>
+To see that the security actually works, we should look at how messages
+are processed on unauthenticated connections:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+Before the authentication succeeds, the server will not receive other
+messages from the connection. Instead, if the server for instance
+expects a <quote>ClientHello</quote> message, and gets an mcopInvocation
+message, it will drop the connection.
+</para>
+</listitem>
+
+<listitem>
+<para>
+If the client doesn't send a valid &MCOP; message at all (no &MCOP;
+magic in the message header) in the authentication phase, but something
+else, the connection is dropped.
+</para>
+</listitem>
+
+<listitem>
+<para>
+If the client tries to send a very very large message (&gt; 4096 bytes
+in the authentication phase, the message size is truncated to 0 bytes,
+which will cause that it isn't accepted for authentication) This is to
+prevent unauthenticated clients from sending &eg; 100 megabytes of
+message, which would be received and could cause the server to run out
+of memory.
+</para>
+</listitem>
+
+<listitem>
+<para>
+If the client sends a corrupt ClientHello message (one, for which
+demarshalling fails), the connection is dropped.
+</para>
+</listitem>
+
+<listitem>
+<para>
+If the client send nothing at all, then a timeout should occur (to be
+implemented).
+</para>
+</listitem>
+</itemizedlist>
+
+</sect1>
+
+<sect1 id="mcop-protocol">
+<title>&MCOP; Protocol Specification</title>
+
+<sect2 id="mcop-protocol-intro">
+<title>Introduction</title>
+
+<para>
+It has conceptual similarities to <acronym>CORBA</acronym>, but it is
+intended to extend it in all ways that are required for real time
+multimedia operations.
+</para>
+
+<para>
+It provides a multimedia object model, which can be used for both:
+communication between components in one address space (one process), and
+between components that are in different threads, processes or on
+different hosts.
+</para>
+
+<para>
+All in all, it will be designed for extremely high performance (so
+everything shall be optimized to be blazingly fast), suitable for very
+communicative multimedia applications. For instance streaming videos
+around is one of the applications of &MCOP;, where most
+<acronym>CORBA</acronym> implementations would go down to their knees.
+</para>
+
+<para>
+The interface definitions can handle the following natively:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+Continuous streams of data (such as audio data).
+</para>
+</listitem>
+
+<listitem>
+<para>
+Event streams of data (such as &MIDI; events).
+</para>
+</listitem>
+
+<listitem>
+<para>
+Real reference counting.
+</para>
+</listitem>
+</itemizedlist>
+
+<para>
+and the most important <acronym>CORBA</acronym> gimmicks, like
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+Synchronous method invocations.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Asynchronous method invocations.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Constructing user defined data types.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Multiple inheritance.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Passing object references.
+</para>
+</listitem>
+</itemizedlist>
+
+</sect2>
+
+<sect2 id="mcop-protocol-marshalling">
+<title>The &MCOP; Message Marshalling</title>
+
+<para>
+Design goals/ideas:
+</para>
+
+<itemizedlist>
+
+<listitem>
+<para>
+Marshalling should be easy to implement.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Demarshalling requires the receiver to know what type he wants to
+demarshall.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The receiver is expected to use every information - so skipping is only
+in the protocol to a degree that:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+If you know you are going to receive a block of bytes, you don't need to
+look at each byte for an end marker.
+</para>
+</listitem>
+
+<listitem>
+<para>
+If you know you are going to receive a string, you don't need to read it
+until the zero byte to find out it's length while demarshalling, however,
+</para>
+</listitem>
+
+<listitem>
+<para>
+If you know you are going to receive a sequence of strings, you need to
+look at the length of each of them to find the end of the sequence, as
+strings have variable length. But if you use the strings for something
+useful, you'll need to do that anyway, so this is no loss.
+</para>
+</listitem>
+</itemizedlist>
+
+</listitem>
+
+<listitem>
+<para>
+As little overhead as possible.
+</para>
+</listitem>
+</itemizedlist>
+
+<!-- TODO: Make this a table -->
+
+<para>
+Marshalling of the different types is show in the table below:
+</para>
+
+<informaltable>
+<tgroup cols="3">
+<thead>
+<row>
+<entry>Type</entry>
+<entry>Marshalling Process</entry>
+<entry>Result</entry>
+</row>
+</thead>
+
+<tbody>
+<row>
+<entry><type>void</type></entry>
+<entry><type>void</type> types are marshalled by omitting them, so
+nothing is written to the stream for them.</entry>
+<entry></entry>
+</row>
+
+<row>
+<entry><type>long</type></entry>
+<entry>is marshalled as four bytes, the most significant byte first,
+so the number 10001025 (which is 0x989a81) would be marshalled
+as:</entry>
+<entry><literal>0x00 0x98 0x9a 0x81</literal></entry>
+</row>
+
+<row>
+<entry><type>enums</type></entry>
+<entry><para>are marshalled like <type>long</type>s</para></entry>
+<entry></entry>
+</row>
+
+<row>
+<entry><type>byte</type></entry>
+<entry><para>is marshalled as a single byte, so the byte 0x42 would be
+marshalled as:</para></entry>
+<entry><literal>0x42</literal></entry>
+</row>
+
+<row>
+<entry><type>string</type></entry>
+<entry><para>is marshalled as a <type>long</type>, containing the length
+of the following string, and then the sequence of characters strings
+must end with one zero byte (which is included in the length
+counting).</para>
+<important>
+<para>include the trailing 0 byte in length counting!</para>
+</important>
+<para><quote>hello</quote> would be marshalled as:</para></entry>
+<entry><literal>0x00 0x00 0x00 0x06 0x68 0x65 0x6c 0x6c 0x6f 0x00</literal></entry>
+</row>
+
+<row>
+<entry><type>boolean</type></entry>
+<entry><para>is marshalled as a byte, containing 0 if
+<returnvalue>false</returnvalue> or 1 if
+<returnvalue>true</returnvalue>, so the boolean value
+<returnvalue>true</returnvalue> is marshalled as:</para></entry>
+<entry><literal>0x01</literal></entry>
+</row>
+
+<row>
+<entry><type>float</type></entry>
+<entry><para>is marshalled after the four byte IEEE754 representation -
+detailed docs how IEEE works are here: <ulink
+url="http://twister.ou.edu/workshop.docs/common-tools/numerical_comp_guide/ncg_math.doc.html">http://twister.ou.edu/workshop.docs/common-tools/numerical_comp_guide/ncg_math.doc.html</ulink>
+and here: <ulink
+url="http://java.sun.com/docs/books/vmspec/2nd-edition/html/Overview.doc.html">http://java.sun.com/docs/books/vmspec/2nd-edition/html/Overview.doc.html</ulink>.
+So, the value 2.15 would be marshalled as:</para></entry>
+<entry><literal>0x9a 0x99 0x09 0x40</literal></entry>
+</row>
+
+<row>
+<entry><type>struct</type></entry>
+<entry><para>A structure is marshalled by marshalling it's
+contents. There are no additional prefixes or suffixes required, so the
+structure
+</para>
+<programlisting>
+struct test {
+ string name; // which is "hello"
+ long value; // which is 10001025 (0x989a81)
+};
+</programlisting>
+<para>would be marshalled as</para></entry>
+<entry>
+<literallayout>
+0x00 0x00 0x00 0x06 0x68 0x65 0x6c 0x6c
+0x6f 0x00 0x00 0x98 0x9a 0x81
+</literallayout></entry>
+</row>
+
+<row>
+<entry><type>sequence</type></entry>
+<entry><para>a sequence is marshalled by listing the number of elements
+that follow, and then marshalling the elements one by one.</para>
+<para>So a sequence of 3 longs a, with a[0] = 0x12345678, a[1] = 0x01
+and a[2] = 0x42 would be marshalled as:</para></entry>
+<entry>
+<literallayout>
+0x00 0x00 0x00 0x03 0x12 0x34 0x56 0x78
+0x00 0x00 0x00 0x01 0x00 0x00 0x00 0x42
+</literallayout>
+</entry>
+</row>
+</tbody>
+</tgroup>
+</informaltable>
+
+<para>
+If you need to refer to a type, all primitive types are referred by the
+names given above. Structures and enums get own names (like
+Header). Sequences are referred as *<replaceable>normal
+type</replaceable>, so that a sequence of longs is <quote>*long</quote>
+and a sequence of Header struct's is <quote>*Header</quote>.
+</para>
+
+</sect2>
+
+<sect2 id="mcop-protocol-messages">
+<title>Messages</title>
+
+<para>
+The &MCOP; message header format is defined as defined by this
+structure:
+</para>
+
+<programlisting>
+struct Header {
+ long magic; // the value 0x4d434f50, which is marshalled as MCOP
+ long messageLength;
+ long messageType;
+};
+</programlisting>
+
+<para>
+The possible messageTypes are currently
+</para>
+
+<programlisting>
+ mcopServerHello = 1
+ mcopClientHello = 2
+ mcopAuthAccept = 3
+ mcopInvocation = 4
+ mcopReturn = 5
+ mcopOnewayInvocation = 6
+</programlisting>
+
+<para>
+A few notes about the &MCOP; messaging:
+</para>
+
+
+<itemizedlist>
+<listitem>
+<para>
+Every message starts with a Header.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Some messages types should be dropped by the server, as long as the
+authentication is not complete.
+</para>
+</listitem>
+
+<listitem>
+<para>
+After receiving the header, the protocol (connection) handling can
+receive the message completely, without looking at the contents.
+</para>
+</listitem>
+</itemizedlist>
+
+<para>
+The messageLength in the header is of course in some cases redundant,
+which means that this approach is not minimal regarding the number of
+bytes.
+</para>
+
+<para>
+However, it leads to an easy (and fast) implementation of non-blocking
+messaging processing. With the help of the header, the messages can be
+received by protocol handling classes in the background (non-blocking),
+if there are many connections to the server, all of them can be served
+parallel. You don't need to look at the message content, to receive the
+message (and to determine when you are done), just at the header, so the
+code for that is pretty easy.
+</para>
+
+<para>
+Once a message is there, it can be demarshalled and processed in one
+single pass, without caring about cases where not all data may have been
+received (because the messageLength guarantees that everything is
+there).
+</para>
+
+</sect2>
+
+<sect2 id="mcop-protocol-invocations">
+<title>Invocations</title>
+
+<para>
+To call a remote method, you need to send the following structure in the
+body of an &MCOP; message with the messageType = 1 (mcopInvocation):
+</para>
+
+<programlisting>
+struct Invocation {
+ long objectID;
+ long methodID;
+ long requestID;
+};
+</programlisting>
+
+<para>
+after that, you send the parameters as structure, &eg; if you invoke the
+method string concat(string s1, string s2), you send a structure like
+</para>
+
+<programlisting>
+struct InvocationBody {
+ string s1;
+ string s2;
+};
+</programlisting>
+
+
+<para>
+if the method was declared to be oneway - that means asynchronous
+without return code - then that was it. Otherwise, you'll receive as
+answer the message with messageType = 2 (mcopReturn)
+</para>
+
+<programlisting>
+struct ReturnCode {
+ long requestID;
+ &lt;resulttype&gt; result;
+};
+</programlisting>
+
+
+<para>
+where &lt;resulttype&gt; is the type of the result. As void types are
+omitted in marshalling, you can also only write the requestID if you
+return from a void method.
+</para>
+
+<para>
+So our string concat(string s1, string s2) would lead to a returncode
+like
+</para>
+
+<programlisting>
+struct ReturnCode {
+ long requestID;
+ string result;
+};
+</programlisting>
+
+</sect2>
+
+<sect2 id="mcop-protocol-inspecting">
+<title>Inspecting Interfaces</title>
+
+<para>
+To do invocations, you need to know the methods an object supports. To
+do so, the methodID 0, 1, 2 and 3 are hardwired to certain
+functionalities. That is
+</para>
+
+<programlisting>
+long _lookupMethod(MethodDef methodDef); // methodID always 0
+string _interfaceName(); // methodID always 1
+InterfaceDef _queryInterface(string name); // methodID always 2
+TypeDef _queryType(string name); // methodID always 3
+</programlisting>
+
+<para>
+to read that, you of course need also
+</para>
+
+<programlisting>
+struct MethodDef {
+ string methodName;
+ string type;
+ long flags; // set to 0 for now (will be required for streaming)
+ sequence&lt;ParamDef&gt; signature;
+};
+
+struct ParamDef {
+ string name;
+ long typeCode;
+};
+</programlisting>
+
+<para>
+the parameters field contains type components which specify the types of
+the parameters. The type of the returncode is specified in the
+MethodDef's type field.
+</para>
+
+<para>
+Strictly speaking, only the methods
+<methodname>_lookupMethod()</methodname> and
+<methodname>_interfaceName()</methodname> differ from object to object,
+while the <methodname>_queryInterface()</methodname> and
+<methodname>_queryType()</methodname> are always the same.
+</para>
+
+<para>
+What are those methodIDs? If you do an &MCOP; invocation, you are
+expected to pass a number for the method you are calling. The reason for
+that is, that numbers can be processed much faster than strings when
+executing an &MCOP; request.
+</para>
+
+<para>
+So how do you get those numbers? If you know the signature of the
+method, that is a MethodDef that describes the method, (which contains
+name, type, parameter names, parameter types and such), you can pass
+that to _lookupMethod of the object where you wish to call a method. As
+_lookupMethod is hardwired to methodID 0, you should encounter no
+problems doing so.
+</para>
+
+<para>
+On the other hand, if you don't know the method signature, you can find
+which methods are supported by using _interfaceName, _queryInterface and
+_queryType.
+</para>
+</sect2>
+
+<sect2 id="mcop-protocol-typedefs">
+<title>Type Definitions</title>
+
+<para>
+User defined datatypes are described using the
+<structname>TypeDef</structname> structure:
+</para>
+
+<programlisting>
+struct TypeComponent {
+ string type;
+ string name;
+};
+
+struct TypeDef {
+ string name;
+
+ sequence&lt;TypeComponent&gt; contents;
+};
+</programlisting>
+
+</sect2>
+</sect1>
+
+<sect1 id="why-not-dcop">
+<title>Why &arts; Doesn't Use &DCOP;</title>
+
+<para>
+Since &kde; dropped <acronym>CORBA</acronym> completely, and is using
+&DCOP; everywhere instead, naturally the question arises why &arts;
+isn't doing so. After all, &DCOP; support is in
+<classname>KApplication</classname>, is well-maintained, supposed to
+integrate greatly with libICE, and whatever else.
+</para>
+
+<para>
+Since there will be (potentially) a lot of people asking whether having
+&MCOP; besides &DCOP; is really necessary, here is the answer. Please
+don't get me wrong, I am not trying to say <quote>&DCOP; is
+bad</quote>. I am just trying to say <quote>&DCOP; isn't the right
+solution for &arts;</quote> (while it is a nice solution for other
+things).
+</para>
+
+<para>
+First, you need to understand what exactly &DCOP; was written
+for. Created in two days during the &kde;-TWO meeting, it was intended
+to be as simple as possible, a really <quote>lightweight</quote>
+communication protocol. Especially the implementation left away
+everything that could involve complexity, for instance a full blown
+concept how data types shall be marshalled.
+</para>
+
+<para>
+Even although &DCOP; doesn't care about certain things (like: how do I
+send a string in a network-transparent manner?) - this needs to be
+done. So, everything that &DCOP; doesn't do, is left to &Qt; in the
+&kde; apps that use &DCOP; today. This is mostly type management (using
+the &Qt; serialization operator).
+</para>
+
+<para>
+So &DCOP; is a minimal protocol which perfectly enables &kde;
+applications to send simple messages like <quote>open a window pointing
+to http://www.kde.org</quote> or <quote>your configuration data has
+changed</quote>. However, inside &arts; the focus lies on other things.
+</para>
+
+<para>
+The idea is, that little plugins in &arts; will talk involving such data
+structures as <quote>midi events</quote> and <quote>songposition
+pointers</quote> and <quote>flow graphs</quote>.
+</para>
+
+<para>
+These are complex data types, which must be sent between different
+objects, and be passed as streams, or parameters. &MCOP; supplies a type
+concept, to define complex data types out of simpler ones (similar to
+structs or arrays in C++). &DCOP; doesn't care about types at all, so
+this problem would be left to the programmer - like: writing C++ classes
+for the types, and make sure they can serialize properly (for instance:
+support the &Qt; streaming operator).
+</para>
+
+<para>
+But that way, they would be inaccessible to everything but direct C++
+coding. Specifically, you could not design a scripting language, that
+would know all types plugins may ever expose, as they are not self
+describing.
+</para>
+
+<para>
+Much the same argument is valid for interfaces as well. &DCOP; objects
+don't expose their relationships, inheritance hierarchies, etc. - if you
+were to write an object browser which shows you <quote>what attributes
+has this object got</quote>, you'd fail.
+</para>
+
+
+<para>
+While Matthias told me that you have a special function
+<quote>functions</quote> on each object that tells you about the methods
+that an object supports, this leaves out things like attributes
+(properties), streams and inheritance relations.
+</para>
+
+<para>
+This seriously breaks applications like &arts-builder;. But remember:
+&DCOP; was not so much intended to be an object model (as &Qt; already
+has one with <application>moc</application> and similar), nor to be
+something like <acronym>CORBA</acronym>, but to supply inter-application
+communication.
+</para>
+
+<para>
+Why &MCOP; even exists is: it should work fine with streams between
+objects. &arts; makes heavily use of small plugins, which interconnect
+themselves with streams. The <acronym>CORBA</acronym> version of &arts;
+had to introduce a very annoying split between <quote>the SynthModule
+objects</quote>, which were the internal work modules that did do the
+streaming, and <quote>the <acronym>CORBA</acronym> interface</quote>,
+which was something external.
+</para>
+
+<para>
+Much code cared about making interaction between <quote>the SynthModule
+objects</quote> and <quote>the <acronym>CORBA</acronym>
+interface</quote> look natural, but it didn't, because
+<acronym>CORBA</acronym> knew nothing at all about streams. &MCOP;
+does. Look at the code (something like
+<filename>simplesoundserver_impl.cc</filename>). Way better! Streams
+can be declared in the interface of modules, and implemented in a
+natural looking way.
+</para>
+
+<para>
+One can't deny it. One of the reasons why I wrote &MCOP; was speed. Here
+are some arguments why &MCOP; will definitely be faster than &DCOP;
+(even without giving figures).
+</para>
+
+
+<para>
+An invocation in &MCOP; will have a six-<quote>long</quote>-header. That
+is:
+</para>
+
+<itemizedlist>
+<listitem><para>magic <quote>MCOP</quote></para></listitem>
+<listitem><para>message type (invocation)</para></listitem>
+<listitem><para>size of the request in bytes</para></listitem>
+<listitem><para>request ID</para></listitem>
+<listitem><para>target object ID</para></listitem>
+<listitem><para>target method ID</para></listitem>
+</itemizedlist>
+
+<para>
+After that, the parameters follow. Note that the demarshalling of this
+is extremely fast. You can use table lookups to find the object and the
+method demarshalling function, which means that complexity is O(1) [ it
+will take the same amount of time, no matter how many objects are alive,
+or how many functions are there ].
+</para>
+
+<para>
+Comparing this to &DCOP;, you'll see, that there are at least
+</para>
+
+<itemizedlist>
+<listitem><para>a string for the target object - something like
+<quote>myCalculator</quote></para></listitem>
+<listitem><para>a string like <quote>addNumber(int,int)</quote> to
+specify the method</para></listitem>
+<listitem><para>several more protocol info added by libICE, and other
+DCOP specifics I don't know</para></listitem>
+</itemizedlist>
+
+<para>
+These are much more painful to demarshall, as you'll need to parse the
+string, search for the function, &etc;.
+</para>
+
+<para>
+In &DCOP;, all requests are running through a server
+(<application>DCOPServer</application>). That means, the process of a
+synchronous invocation looks like this:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+Client process sends invocation.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<application>DCOPserver</application> (man-in-the-middle) receives
+invocation and looks where it needs to go, and sends it to the
+<quote>real</quote> server.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Server process receives invocation, performs request and sends result.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<application>DCOPserver</application> (man-in-the-middle) receives
+result and ... sends it to the client.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Client decodes reply.
+</para>
+</listitem>
+</itemizedlist>
+
+<para>
+In &MCOP;, the same invocation looks like this:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+Client process sends invocation.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Server process receives invocation, performs request and sends result.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Client decodes reply.
+</para>
+</listitem>
+</itemizedlist>
+
+<para>
+Say both were implemented correctly, &MCOP;s peer-to-peer strategy
+should be faster by a factor of two, than &DCOP;s man-in-the-middle
+strategy. Note however that there were of course reasons to choose the
+&DCOP; strategy, which is namely: if you have 20 applications running,
+and each app is talking to each app, you need 20 connections in &DCOP;,
+and 200 with &MCOP;. However in the multimedia case, this is not
+supposed to be the usual setting.
+</para>
+
+<para>
+I tried to compare &MCOP; and &DCOP;, doing an invocation like adding
+two numbers. I modified testdcop to achieve this. However, the test may
+not have been precise on the &DCOP; side. I invoked the method in the
+same process that did the call for &DCOP;, and I didn't know how to get
+rid of one debugging message, so I used output redirection.
+</para>
+
+<para>
+The test only used one object and one function, expect &DCOP;s results
+to decrease with more objects and functions, while &MCOP;s results
+should stay the same. Also, the <application>dcopserver</application>
+process wasn't connected to other applications, it might be that if many
+applications are connected, the routing performance decreases.
+</para>
+
+<para>
+The result I got was that while &DCOP; got slightly more than 2000
+invocations per second, &MCOP; got slightly more than 8000 invocations
+per second. That makes a factor of 4. I know that &MCOP; isn't tuned to
+the maximum possible, yet. (Comparison: <acronym>CORBA</acronym>, as
+implemented with mico, does something between 1000 and 1500 invocations
+per second).
+</para>
+
+<para>
+If you want <quote>harder</quote> data, consider writing some small
+benchmark app for &DCOP; and send it to me.
+</para>
+
+<para>
+<acronym>CORBA</acronym> had the nice feature that you could use objects
+you implemented once, as <quote>separate server process</quote>, or as
+<quote>library</quote>. You could use the same code to do so, and
+<acronym>CORBA</acronym> would transparently decide what to do. With
+&DCOP;, that is not really intended, and as far as I know not really
+possible.
+</para>
+
+<para>
+&MCOP; on the other hand should support that from the beginning. So you
+can run an effect inside &artsd;. But if you are a wave editor, you can
+choose to run the same effect inside your process space as well.
+</para>
+
+<para>
+While &DCOP; is mostly a way to communicate between apps, &MCOP; is also
+a way to communicate inside apps. Especially for multimedia streaming,
+this is important (as you can run multiple &MCOP; objects parallely, to
+solve a multimedia task in your application).
+</para>
+
+<para>
+Although &MCOP; does not currently do so, the possibilities are open to
+implement quality of service features. Something like <quote>that &MIDI; event
+is really really important, compared to this invocation</quote>. Or something
+like <quote>needs to be there in time</quote>.
+</para>
+
+<para>
+On the other hand, stream transfer can be integrated in the &MCOP;
+protocol nicely, and combined with <acronym>QoS</acronym> stuff. Given
+that the protocol may be changed, &MCOP; stream transfer should not
+really get slower than conventional <acronym>TCP</acronym> streaming,
+but: it will be easier and more consistent to use.
+</para>
+
+<para>
+There is no need to base a middleware for multimedia on &Qt;. Deciding
+so, and using all that nice &Qt;-streaming and stuff, will easily lead
+to the middleware becoming a &Qt;-only (or rather &kde;-only) thing. I
+mean: as soon as I'll see the GNOMEs using &DCOP;, too, or something like
+that, I am certainly proven wrong.
+</para>
+
+<para>
+While I do know that &DCOP; basically doesn't know about the data types
+it sends, so that you could use &DCOP; without using &Qt;, look at how
+it is used in daily &kde; usage: people send types like
+<classname>QString</classname>, <classname>QRect</classname>,
+<classname>QPixmap</classname>, <classname>QCString</classname>, ...,
+around. These use &Qt;-serialization. So if somebody choose to support
+&DCOP; in a GNOME program, he would either have to claim to use
+<classname>QString</classname>,... types (although he doesn't do so),
+and emulate the way &Qt; does the streaming, or he would send other
+string, pixmap and rect types around, and thus not be interoperable.
+</para>
+
+<para>
+Well, whatever. &arts; was always intended to work with or without
+&kde;, with or without &Qt;, with or without X11, and maybe even with or
+without &Linux; (and I have even no problems with people who port it to
+a popular non-free operating systems).
+</para>
+
+<para>
+It is my position that non-&GUI;-components should be written
+non-&GUI;-dependant, to make sharing those among wider amounts of
+developers (and users) possible.
+</para>
+
+<para>
+I see that using two <acronym>IPC</acronym> protocols may cause
+inconveniences. Even more, if they are both non-standard. However, for
+the reasons given above, switching to &DCOP; is no option. If there is
+significant interest to find a way to unite the two, okay, we can
+try. We could even try to make &MCOP; speak <acronym>IIOP</acronym>,
+then we'd have a <acronym>CORBA</acronym> <acronym>ORB</acronym> ;).
+</para>
+
+<para>
+I talked with Matthias Ettrich a bit about the future of the two
+protocols, and we found lots of ways how things could go on. For
+instance, &MCOP; could handle the message communication in &DCOP;, thus
+bringing the protocols a bit closer together.
+</para>
+
+<para>
+So some possible solutions would be:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+Write an &MCOP; - &DCOP; gateway (which should be possible, and would
+make interoperation possible) - note: there is an experimental
+prototype, if you like to work on that.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Integrate everything &DCOP; users expect into &MCOP;, and try to only do
+&MCOP; - one could add an <quote>man-in-the-middle-option</quote> to
+&MCOP;, too ;)
+</para>
+</listitem>
+
+<listitem>
+<para>
+Base &DCOP; on &MCOP; instead of libICE, and slowly start integrating
+things closer together.
+</para>
+</listitem>
+</itemizedlist>
+
+<para>
+However, it may not be the worst possibility to use each protocol for
+everything it was intended for (there are some big differences in the
+design goals), and don't try to merge them into one.
+</para>
+
+</sect1>
+</chapter>
diff --git a/doc/artsbuilder/midi.docbook b/doc/artsbuilder/midi.docbook
new file mode 100644
index 00000000..611b457c
--- /dev/null
+++ b/doc/artsbuilder/midi.docbook
@@ -0,0 +1,474 @@
+<!-- <?xml version="1.0" ?>
+<!DOCTYPE chapter PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd">
+To validate or process this file as a standalone document, uncomment
+this prolog. Be sure to comment it out again when you are done -->
+
+<chapter id="midi">
+<title>&MIDI;</title>
+
+<sect1 id="midi-overview">
+<title>Overview</title>
+
+<!-- what-to-say-here: aRts has three roles
+ * moving midi events around between applications
+ * abstracting the hardware
+ * synthesizer -->
+
+<para>
+The &MIDI; support in &arts; can do a number of things. First of all, it
+allows <emphasis>communication</emphasis> between different pieces of
+software that produce or consume &MIDI; events. If you for instance have
+a sequencer and a sampler that are both &arts; aware, &arts; can send
+the &MIDI; events from the sequencer to the sampler.
+</para>
+
+<para>
+On the other hand, &arts; can also help applications to
+<emphasis>interact with the hardware</emphasis>. If a piece of software
+(for instance the sampler) works together with &arts;, it will be able
+to receive the &MIDI; events from an external &MIDI; keyboard as well.
+</para>
+
+<para>
+Finally, &arts; makes a great <emphasis>modular
+synthesizer</emphasis>. It is designed to do exactly this. So you can
+build instruments out of small modules using artsbuilder, and then use
+these instruments to compose or play music. Synthesis does not
+necessarily mean pure synthesis, there are modules you can use to play
+samples. So &arts; can be a sampler, synthesizer, and so on, and being
+fully modular, it is very easy to extend, very easy to experiment with,
+powerful and flexible.
+</para>
+</sect1>
+
+<sect1 id="midi-manager">
+<title>The &MIDI; Manager</title>
+<!-- what-to-say-here:
+ * how to use artscontrol - view midimanager
+ * what does autorestore do? (not yet implemented - so not yet documented) -->
+
+<para>
+The central component in &arts; that keeps track which applications are
+connected and how midi events should be passed between them is the midi
+manager. To see or influence what it does, start artscontrol. Then,
+choose <menuchoice><guilabel>View</guilabel><guilabel>View Midi
+Manager</guilabel> </menuchoice> from the menu.
+</para>
+
+<para>
+On the left side, you will see <guilabel>Midi Inputs</guilabel>. There,
+all objects that produce &MIDI; events, such as an external &MIDI; port
+which sends data from a connected &MIDI; keyboard, a sequencer which
+plays a song and so on will be listed. On the right side, you will see
+<guilabel>Midi Outputs</guilabel>. There, all things that consume &MIDI;
+events, such as a simulated sampler (as software), or the external
+&MIDI; port where your hardware sampler outside your computer is
+connected will be listed. New applications, such as sequencers and so on
+will register themselves, so the list will be changing over time.
+</para>
+
+<para>
+You can connect inputs and outputs if you mark the input on the left
+side and the output on the right side, and choose
+<guilabel>Connect</guilabel> with the button
+below. <guilabel>Disconnect</guilabel> works the same. You will see what
+is connected as small lines between the inputs and outputs, in the
+middle of the window. Note that you can connect one sender to more than
+one receiver (and the other way round).
+</para>
+
+<para>
+Programs (like the Brahms sequencer) will add themselves when they start
+and be removed from the list when they are terminated. But you can also
+add new things in the <guilabel>Add</guilabel> menu:
+</para>
+
+<variablelist>
+<varlistentry>
+<term><guimenuitem>System Midi Port (OSS)</guimenuitem></term>
+<listitem>
+<para>
+This will create a new &arts; object that talks to an external midi
+port.
+</para>
+
+<para>
+As external midi ports can do both, send and receive data, choosing this
+option will add a midi input and a midi output. Under &Linux;, you
+should either have an <acronym>OSS</acronym> (or
+<acronym>OSS</acronym>/Free, the thing that comes with your &Linux;
+kernel) or an <acronym>ALSA</acronym> driver for your soundcard
+installed, to make it work. It will ask for the name of the
+device. Usually, this is <filename
+class="devicefile">/dev/midi</filename> or <filename
+class="devicefile">/dev/midi00</filename>.
+</para>
+
+<para>
+However, if you have more than one &MIDI; device or a &MIDI; loopback
+driver installed, there might be more choices. To see information about
+your midi ports, start the &kcontrolcenter;, and choose
+<menuchoice><guilabel>Information</guilabel>
+<guilabel>Sound</guilabel></menuchoice>.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>aRts Synthesis Midi Output</guimenuitem></term>
+<listitem>
+<para>
+This will add a new &MIDI; output with an &arts; synthesis
+instrument. If you choose the menu item, a dialog will pop up, and allow
+you to choose an instrument. You can create new instruments using
+artsbuilder. All <literal role="extension">.arts</literal> files with a
+name that starts with <filename>instrument_</filename> will appear here.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+</sect1>
+
+<sect1 id="brahms">
+<title>Using &arts; &amp; Brahms</title>
+
+<para>
+Actually, getting started is quite easy. You need a &kde; 2.1-aware
+version of &brahms;, which can be found in the <literal>kmusic</literal>
+<acronym>CVS</acronym> module. There is also information on how to get
+&brahms; on the <ulink url="http://www.arts-project.org/">aRts
+Homepage</ulink> in the Download section.
+</para>
+
+<para>
+When you start it, it will show up in the &MIDI; manager. If you want to
+do synthesis, simply add a synthesis &MIDI; instrument via
+<menuchoice><guilabel>Add</guilabel><guilabel>aRts Synthesis Midi
+Output</guilabel></menuchoice>.
+</para>
+
+<para>
+Choose an instrument (for instance <guilabel>organ2</guilabel>). Connect
+them using the <guilabel>Connect</guilabel> button. Finally, you can
+start composing in &brahms;, and the output will be synthesized with
+&arts;.
+</para>
+
+<para>
+It is usually a good idea to have the &artscontrol; window open, and see
+that the volume is not too loud (quality gets bad when the bars hit the
+upper limit). Now you can start working on a new &arts; demosong, and if
+you are done, you can get it published on aRts-project.org ;-).
+</para>
+
+<!-- TODO: how to do more than one instrument in Brahms (hm, not implemented
+ yet, not documented yet), how to use samples, mapping and so on. These
+ things need to be implemented, too. -->
+
+</sect1>
+
+<sect1 id="midisend">
+<title>midisend</title>
+
+<para>
+<command>midisend</command> is a small application that will allow you
+to send &MIDI; events from
+the shell. It will register as client like all other applications. The most
+simple way to use it is to do
+
+<screen><prompt>&percnt;</prompt> <userinput><command>midisend</command> <option>-f</option> <parameter><replaceable>/dev/midi00</replaceable></parameter></userinput> </screen>
+
+which will achieve about the same as adding a system &MIDI; port in
+&artscontrol;. (Not quite, because <command>midisend</command> only sends events). The difference is that it is
+easy for instance to start <command>midisend</command> on different computers (and like that,
+use network transparency).
+</para>
+
+<para>
+It is also possible to make <command>midisend</command> send data from
+<filename class="devicefile">stdin</filename>, which you can use to pipe
+data from non-&arts;-aware applications to &arts;, like this:
+
+<screen><prompt>&percnt;</prompt> <userinput><command><replaceable>applicationwhichproducesmidieventsonstdout</replaceable></command> | <command>midisend</command> <option>-f</option> <option><replaceable>-</replaceable></option></userinput></screen>
+<!-- TODO: document all options -->
+</para>
+
+</sect1>
+
+<sect1 id="midi-creating-instruments">
+<title>Creating Instruments</title>
+
+<para>
+The way &arts; does midi synthesis is this: you have a structures which
+has some input ports, where it gets the frequency, velocity (volume) and
+a parameter which indicates whether the note is still pressed. The
+structure should now synthesize exactly that note with that volume, and
+react on the pressed parameter (where pressed = 1 means the user still
+holds down that key and pressed = 0 means the user has released that
+key).
+</para>
+
+<para>
+When &MIDI; events arrive, &arts; will create new structures for the
+notes as needed, give them the parameters, and clean them up once they
+are done.
+</para>
+
+<para>
+To create and use such a structure, you should do the following:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+To get started, the most convenient way is to open
+<filename>template_Instrument.arts</filename> in &arts-builder;.
+</para>
+
+<para>
+This can be achieved by using
+<menuchoice><guimenu>File</guimenu><guimenuitem>Open
+Example...</guimenuitem></menuchoice> and choosing
+<guimenuitem>template_Instrument</guimenuitem> in the file
+selector. This will give you an empty structure with the required
+parameters, which you only need to <quote>fill out</quote>.
+</para>
+</listitem>
+
+<listitem>
+<para>
+To process the pressed parameter, it is convenient to use
+Synth&lowbar;ENVELOPE&lowbar;ADSR, or, in case of playing some drum wav,
+just play it anyway, and ignore the pressed parameter.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The structure should indicate when it is no longer needed on the
+<quote>done</quote> output. If done is <returnvalue>1</returnvalue>,
+&arts; assumes that it can delete the structure. Conveniently, the ADSR
+envelope provides a parameter when it is done, so you just need to
+connect this to the done output of the structure.
+</para>
+</listitem>
+
+<listitem>
+<para>
+You should rename your structure to some name starting with
+<filename>instrument_</filename>, like
+<filename>instrument_piano.arts</filename> - you should save the file
+under the same name, in your <filename
+class="directory">$<envar>HOME</envar>/arts/structures</filename>
+folder (which is where artsbuilder wants to save files normally).
+</para>
+</listitem>
+
+<listitem>
+<para>
+Finally, once you saved it, you will be able to use it with &artscontrol;
+in the &MIDI; manager <!-- todo link to midimanager -->.</para>
+</listitem>
+
+<listitem>
+<para>
+Oh, and of course your structure should play the audio data it generates
+to the left and right output of the structure, which will then be played
+via audio manager (you can see that in &artscontrol;), so that you
+finally can hear it (or postprocess it with effects).
+</para>
+</listitem>
+</itemizedlist>
+
+<para>
+A good way to learn how to do instruments is to open an existing
+instrument via <menuchoice><guilabel>File</guilabel><guilabel>Open
+Example</guilabel> </menuchoice> and see how it works ;)
+</para>
+</sect1>
+
+<sect1 id="mapped-instruments">
+<title>Mapped Instruments</title>
+
+<para>
+Mapped instruments are instruments, that behave differently depending on
+the pitch, the program, the channel or the velocity. You could for
+instance build a piano of 5 octaves, using one sample for each octave
+(pitchshifting it accordingly). That sounds a whole lot better than only
+using one sample.
+</para>
+
+<para>
+You could also build a drum map, that plays one specific drum sample per
+key.
+</para>
+
+<para>
+Finally, it is very useful if you put quite some different sounds into
+one mapped instrument on different programs. That way, you can use your
+sequencer, external keyboard or other &MIDI; source to switch between
+the sounds without having to tweak &arts; as you work.
+</para>
+
+<para>
+A good example for this is the instrument <filename>arts_all</filename>,
+which just puts together all instruments that come with &arts; in one
+map. That way, you just need to setup once in &artscontrol; to use this
+<quote>instrument</quote>, and then, you can compose a whole song in a
+sequencer without ever bothering about &arts;. Need another sound?
+Simply change the program in the sequencer, and &arts; will give you
+another sound.
+</para>
+
+<para>
+Creating such maps is pretty straightforward. You just need to create a
+textfile, and write rules which look like this:
+</para>
+
+<programlisting>
+ON <replaceable>[ conditions ...]</replaceable> DO structure=<replaceable>somestructure</replaceable>.arts
+</programlisting>
+
+<para>
+The conditions could be one or more than one of the following:
+</para>
+
+<variablelist>
+
+<varlistentry>
+<term><option>pitch</option></term>
+
+<listitem>
+<para>
+The pitch that is being played. You would use this if you want to split
+your instrument depending on the pitch. In our initial examples, a piano
+which uses different samples for different octaves would use this as
+condition. You can specify a single pitch, like
+<userinput><option>pitch</option>=<parameter>62</parameter></userinput>
+or a range of pitches, like
+<userinput><option>pitch</option>=<parameter>60</parameter>-<parameter>72</parameter></userinput>.
+The possible pitches are between <parameter>0</parameter> and
+<parameter>127</parameter>.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>program</option></term>
+<listitem>
+<para>
+The program that is active on the channel that the note is being sent
+on. Usually, sequencers let you choose the <quote>instrument</quote> via
+the program setting. Single programs or ranges are allowed, that is
+<userinput><option>program</option>=<parameter>3</parameter></userinput>
+or
+<userinput><option>program</option>=<parameter>3</parameter>-<parameter>6</parameter></userinput>.
+The possible programs are between <parameter>0</parameter> and
+<parameter>127</parameter>.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>channel</option></term>
+<listitem>
+<para>
+The channel that that the note is being sent on. Single channels or
+ranges are allowed, that is
+<userinput><option>channel</option>=<parameter>0</parameter></userinput>
+or
+<userinput><option>channel</option>=<parameter>0</parameter>-<parameter>8</parameter></userinput>.
+The possible channels are between <parameter>0</parameter> and
+<parameter>15</parameter>.
+</para>
+</listitem>
+
+</varlistentry>
+<varlistentry>
+<term><option>velocity</option></term>
+<listitem>
+<para>
+The velocity (volume) that that the note has. Single velocities (who
+would use that?) or ranges are allowed, that is
+<userinput><option>velocity</option>=<parameter>127</parameter></userinput>
+or
+<userinput><option>velocity</option>=<parameter>64</parameter>-<parameter>127</parameter></userinput>.
+The possible velocities are between <parameter>0</parameter> and
+<parameter>127</parameter>.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+<para>
+A complete example for a map would be (this is taken from the current
+<filename>instrument_arts_all.arts-map</filename>):
+</para>
+
+<programlisting>
+ON program=0 DO structure=instrument_tri.arts
+ON program=1 DO structure=instrument_organ2.arts
+ON program=2 DO structure=instrument_slide1.arts
+ON program=3 DO structure=instrument_square.arts
+ON program=4 DO structure=instrument_neworgan.arts
+ON program=5 DO structure=instrument_nokind.arts
+ON program=6 DO structure=instrument_full_square.arts
+ON program=7 DO structure=instrument_simple_sin.arts
+ON program=8 DO structure=instrument_simple_square.arts
+ON program=9 DO structure=instrument_simple_tri.arts
+ON program=10 DO structure=instrument_slide.arts
+ON program=11 pitch=60 DO structure=instrument_deepdrum.arts
+ON program=11 pitch=61 DO structure=instrument_chirpdrum.arts
+</programlisting>
+
+<para>
+As you see, the structure is chosen depending on the program. On
+program 11, you see a <quote>drum map</quote> (with two entries), which
+would play a <quote>deepdrum</quote> on C-5 (pitch=60), and a
+<quote>chirpdrum</quote> on C#5 (pitch=61).
+</para>
+
+<para>
+To make map files automatically appear in &artscontrol; as choice for
+the instrument, they have to be called
+<filename>instrument_<replaceable>something</replaceable>.arts-map</filename>
+and reside either in your Home Folder, under <filename
+class="directory">$<envar>HOME</envar>/arts/structures</filename>, or in the
+&kde; folder under <filename
+class="directory">$<envar>KDEDIR</envar>/usr/local/kde/share/apps/artsbuilder/examples</filename>. Structures
+that are used by the map can either be given with an absolute path, or
+relative to the folder the map file resides in.
+</para>
+
+<para>
+Extending the arts_all map or even making a complete general &MIDI; map
+for &arts; is a good idea for making &arts; easier to use
+out-of-the-box. Please consider contributing interesting instruments
+you make, so that they can be included in further version of &arts;.
+</para>
+</sect1>
+
+<!-- TODO: Maybe helpful
+ * using an external keyboard
+ * loopback midi device
+
+<sect1 id="quick-start">
+<title>Quick Start</title>
+<para>
+</para>
+</sect1>
+<sect1 id="internal-details">
+<title>More Internal Details</title>
+<para>
+</para>
+</sect1>
+
+<sect1 id="other-considerations">
+<title>Other Considerations</title>
+<para>
+</para>
+</sect1>
+-->
+
+</chapter>
diff --git a/doc/artsbuilder/midiintro.docbook b/doc/artsbuilder/midiintro.docbook
new file mode 100644
index 00000000..18d6fc93
--- /dev/null
+++ b/doc/artsbuilder/midiintro.docbook
@@ -0,0 +1,14 @@
+<!-- <?xml version="1.0" ?>
+<!DOCTYPE appendix PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd">
+To validate or process this file as a standalone document, uncomment
+this prolog. Be sure to comment it out again when you are done -->
+
+<appendix id="midi-introduction">
+
+<title>Introduction to <acronym>MIDI</acronym></title>
+
+<para>
+Not yet written
+</para>
+
+</appendix>
diff --git a/doc/artsbuilder/modules.docbook b/doc/artsbuilder/modules.docbook
new file mode 100644
index 00000000..d0da50d8
--- /dev/null
+++ b/doc/artsbuilder/modules.docbook
@@ -0,0 +1,1336 @@
+<!-- <?xml version="1.0" ?>
+<!DOCTYPE chapter PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd">
+To validate or process this file as a standalone document, uncomment
+this prolog. Be sure to comment it out again when you are done -->
+
+<chapter id="arts-modules">
+<title>&arts; modules</title>
+
+ <sect1 id="modules-introduction">
+<title>Introduction</title>
+
+<para>
+This chapter describes all of the standard &arts; modules. One of the
+most powerful features of &arts;, modules can be connected together into
+structures to implement new functions such as effects and instruments.
+</para>
+
+<para>
+Modules are broken down into two categories. Synthesis modules are used
+for implementing the <quote>plumbing</quote> that manipulates multimedia
+data streams to implement new effects, instruments, mixers, and
+applications. Visual modules allow you to provide a graphical user
+interface to control the sound structures that are built up with the
+synthesis modules.
+</para>
+
+</sect1>
+
+<sect1 id="synth-modules-reference">
+<title>Synthesis Modules Reference</title>
+<para>
+</para>
+
+<sect2 id="mcat-synth-arithmetic-mixing">
+<title>Arithmetic + Mixing</title>
+
+<para>
+</para>
+
+<sect3 id="mref-synth-add-sect">
+<title>Synth&lowbar;ADD</title>
+<anchor id="mref-synth-add" />
+
+<mediaobject>
+<imageobject>
+<imagedata fileref="images/Synth_ADD.png" format="PNG"/></imageobject>
+<textobject><phrase>Synth&lowbar;ADD</phrase></textobject>
+</mediaobject>
+
+<para>
+This adds two signals.
+</para>
+
+</sect3>
+
+<sect3 id="mref-synth-mul-sect">
+<title>Synth&lowbar;MUL</title>
+<anchor id="mref-synth-mul"/>
+
+<mediaobject>
+<imageobject>
+<imagedata fileref="images/Synth_MUL.png" format="PNG"/></imageobject>
+<textobject><phrase>Synth&lowbar;MUL</phrase></textobject>
+</mediaobject>
+
+<para>
+This multiplies a signal by a factor. You can use this to scale signals
+down (0 &lt; factor &lt; 1) or up (factor &gt; 1) or invert signals
+(factor &lt; 0). Note that the factor may be a signal and don't has to
+be constant (&eg; envelope or real signal).
+</para>
+
+</sect3>
+
+<sect3 id="mref-synth-div-sect">
+<title>Synth&lowbar;DIV</title>
+<anchor id="mref-synth-div"/>
+
+<mediaobject>
+<imageobject>
+<imagedata fileref="images/Synth_DIV.png" format="PNG"/></imageobject>
+<textobject><phrase>Synth&lowbar;DIV</phrase></textobject>
+</mediaobject>
+
+<para>
+This divides a signal by a factor. You can use this to do divide one signal
+by another one. Or set invalue1 to 1 and you will get the
+reciprocal of the invalue2 as outvalue. Take care that invalue2 never
+reaches 0 or you will get problems with divisions by zero.
+</para>
+
+</sect3>
+
+<sect3 id="mref-synth-multi-add-sect">
+<title>Synth&lowbar;MULTI&lowbar;ADD</title>
+<anchor id="mref-synth-multi-add" />
+
+<mediaobject>
+<imageobject>
+<imagedata fileref="images/Synth_MULTI_ADD.png"
+ format="PNG"/></imageobject>
+<textobject><phrase>Synth&lowbar;MULTI&lowbar;ADD</phrase></textobject>
+</mediaobject>
+
+<para>
+This adds an arbitrary number of signals. If you need to sum up the
+waveforms produces by four different oscillators, you for instance can
+connect all their outputs to one Synth&lowbar;MULTI&lowbar;ADD
+module. This is more efficient than using three Synth&lowbar;ADD
+modules.
+</para>
+
+</sect3>
+
+<sect3 id="mref-synth-xfade-sect">
+<title>Synth&lowbar;XFADE</title>
+<anchor id="mref-synth-xfade" />
+
+<mediaobject>
+<imageobject><imagedata fileref="images/Synth_XFADE.png" format="PNG"/>
+</imageobject>
+<textobject><phrase>Synth&lowbar;XFADE</phrase></textobject>
+</mediaobject>
+
+<para>
+This crossfades two signals. If the percentage input is -1, only the
+left signal is heard, if it is 1, only the right signal is heard. When
+it is 0, both signals a heard with the same volume.
+</para>
+
+<para>
+This allows you to ensure that your signal stays in a well defined
+range. If you had two signals that were between -1 and 1 before
+crossfading, they will be in the same range after crossfading.
+</para>
+</sect3>
+
+<sect3 id="mref-synth-autopanner-sect">
+<title>Synth&lowbar;AUTOPANNER</title>
+<anchor id="mref-synth-autopanner" />
+
+<para>
+The opposite of a crossfader. This takes a mono signal and splits it
+into a stereo signal: It is used to automatically pan the input signal
+between the left and the right output. This makes mixes more lively. A
+standard application would be a guitar or lead sound.
+</para>
+
+<para>
+Connect a <acronym>LFO</acronym>, a sine or saw wave for example to
+inlfo. and select a frequency between 0.1 and 5Hz for a traditional
+effect or even more for Special <acronym>FX</acronym>.
+</para>
+
+</sect3>
+
+</sect2>
+
+<sect2 id="mcat-synth-busses">
+<title>Busses</title>
+
+<sect3 id="mref-synth-bus-uplink-sect">
+<title>Synth&lowbar;BUS&lowbar;UPLINK</title>
+<anchor id="mref-synth-bus-uplink" />
+
+<mediaobject>
+<imageobject><imagedata fileref="images/Synth_BUS_UPLINK.png"
+ format="PNG"/>
+</imageobject>
+<textobject><phrase>Synth&lowbar;BUS&lowbar;UPLINK</phrase></textobject>
+</mediaobject>
+
+<para>
+An uplink to a bus. Give signals to left and right, and the name of the
+bus where the data should go on the <quote>bus</quote> port. The
+combined signal from all uplinks with this name will appear on every
+downlink on that <quote>bus</quote>.
+</para>
+</sect3>
+
+<sect3 id="mref-synth-bus-downlink-sect">
+<title>Synth&lowbar;BUS&lowbar;DOWNLINK</title>
+<anchor id="mref-synth-bus-downlink" />
+
+<mediaobject>
+<imageobject>
+<imagedata fileref="images/Synth_BUS_DOWNLINK.png"
+ format="PNG"/></imageobject>
+<textobject><phrase>Synth&lowbar;BUS&lowbar;DOWNLINK</phrase></textobject>
+</mediaobject>
+
+<para>
+Gets (the sum of) all data that is put to a certain bus (with the name
+you specify at the <quote>bus</quote> port).
+</para>
+</sect3>
+
+</sect2>
+
+<!-- TODO AFTER KDE2.1: move freeverb into delays, and rename category to
+ Delays &amp; reverbs -->
+
+<sect2 id="mcat-synth-delays">
+<title>Delays</title>
+
+<para>
+</para>
+
+<sect3 id="mref-synth-delay-sect">
+<title>Synth&lowbar;DELAY</title>
+<anchor id="mref-synth-delay" />
+
+<mediaobject>
+<imageobject><imagedata fileref="images/Synth_DELAY.png"
+ format="PNG"/></imageobject></mediaobject>
+
+<para>
+This delays the input signal for an amount of time. The time
+specification must be between 0 and maxdelay for a delay between 0 and
+maxdelay seconds.
+</para>
+
+<para>
+This kind of delay <emphasis>may not be used</emphasis> in feedback
+structures. This is because it's a variable delay. You can modify it's
+length while it is running, and even set it down to zero. But since in a
+feedback structure the own output is needed to calculate the next
+samples, a delay whose value could drop to zero during synthesis could
+lead to a stall situation.
+</para>
+
+<para>
+Use CDELAYs in that setup, perhaps combine a small constant delay (of
+0.001 seconds) with a flexible delay.
+</para>
+
+<para>
+You can also combine a CDELAY and a DELAY to achieve a variable length
+delay with a minimum value in a feedback loop. Just make sure that you
+have a CDELAY involved.
+</para>
+
+</sect3>
+
+<sect3 id="mref-synth-cdelay-sect">
+<title>Synth&lowbar;CDELAY</title>
+<anchor id="mref-synth-cdelay" />
+
+<mediaobject>
+<imageobject><imagedata fileref="images/Synth_CDELAY.png"
+format="PNG"/></imageobject>
+<textobject><phrase>Synth&lowbar;CDELAY</phrase></textobject>
+</mediaobject>
+
+<para>
+This delays the input signal for an amount of time. The time
+specification must be greater than 0 for a delay of 0 seconds or more.
+The delay is constant during the calculation, that means it
+can't be modified.
+</para>
+
+<para>
+This saves computing time as no interpolation is done, and is useful for
+recursive structures. See description above (Synth&lowbar;DELAY).
+</para>
+
+</sect3>
+
+</sect2>
+
+<sect2 id="mcat-synth-envelopes">
+<title>Envelopes</title>
+
+<para>
+</para>
+
+<sect3 id="mref-synth-envelope-adsr-sect">
+<title>Synth&lowbar;ENVELOPE&lowbar;ADSR</title>
+<anchor id="mref-synth-envelope-adsr" />
+
+<mediaobject>
+<imageobject><imagedata fileref="images/Synth_ENVELOPE_ADSR.png"
+ format="PNG"/></imageobject>
+<textobject><phrase>Synth&lowbar;ENVELOPE&lowbar;ADSR</phrase></textobject>
+</mediaobject>
+
+<para>
+This is a classic <acronym>ADSR</acronym> envelope which means you
+specify:
+</para>
+
+<variablelist>
+<varlistentry>
+<term>active</term>
+<listitem>
+<para>
+Whether the note is being pressed right now by the user.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>invalue</term>
+<listitem>
+<para>
+The input signal.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>attack</term>
+<listitem>
+<para>
+The time that should pass between the user presses the note and the signal
+reaching it's maximum amplitude (in seconds).
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>decay</term>
+<listitem>
+<para>
+The time that should pass between the the signal reaching it's maximum
+amplitude and the signal going back to some constant level (in seconds).
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>sustain</term>
+<listitem>
+<para>
+The constant level the signal is held at afterwards, until the user
+releases the note.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>release</term>
+<listitem>
+<para>
+The time that should pass after the user has released the note until the
+signal is scaled down to zero (in seconds).
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+<para>
+You'll get the scaled signal at outvalue. If the <acronym>ASDR</acronym>
+envelope is finished, it will set done to 1. You can use this to provide
+the <quote>done</quote> output of an instrument (which will make the
+instrument structure be deleted by the &MIDI; router object once the
+release phase is over).
+</para>
+
+</sect3>
+
+<sect3 id="mref-synth-pscale-sect">
+<title>Synth&lowbar;PSCALE</title>
+<anchor id="mref-synth-pscale" />
+
+<mediaobject>
+<imageobject><imagedata fileref="images/Synth_PSCALE.png"
+format="PNG"/></imageobject>
+<textobject><phrase>Synth&lowbar;PSCALE</phrase></textobject>
+</mediaobject>
+
+<para>
+The Synth&lowbar;PSCALE module will scale the audio stream that is
+directed through it from a volume 0 (silent) to 1 (original loudness)
+back to 0 (silent). According to the position (get the position from
+Synth&lowbar;SEQUENCE). The position where the peak should occur can be
+given as pos.
+</para>
+
+<para>
+Example: Setting top to 0.1 means that after 10&percnt; of the note has
+been played, the volume has reached its maximum, and starts decaying
+afterwards.
+</para>
+</sect3>
+
+</sect2>
+
+<sect2 id="mcat-synth-effects">
+<title>Effects</title>
+
+<sect3 id="mref-synth-freeverb-sect">
+<title>Synth&lowbar;FREEVERB</title>
+<anchor id="mref-synth-freeverb" />
+
+<mediaobject>
+<imageobject><imagedata fileref="images/Synth_FREEVERB.png"
+format="PNG"/></imageobject>
+<textobject><phrase>Synth&lowbar;FREEVERB</phrase></textobject>
+</mediaobject>
+
+<para>
+This is a reverb effect. In the current implementation, it is thought to
+pass a stereo signal through the reverb, and it will -add- it's reverb
+effect to the signal.
+</para>
+
+<note>
+<para>
+This means that it can be used inside an StereoEffectStack as well.
+</para>
+</note>
+
+<para>
+The input signal should be connected to (inleft, inright), the output
+signal will be (outleft, outright).
+</para>
+
+<para>
+The parameters which you can configure are:
+</para>
+
+<variablelist>
+<varlistentry>
+<term>roomsize</term>
+<listitem>
+<para>
+The size of the room which the reverb simulates (range: 0..1, where 1 is
+the largest possible room).
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>damp</term>
+<listitem>
+<para>
+This specifies a filter which will make the simulated room absorb high
+frequencies (range 0..1, where 1 means absorb high frequencies quite
+aggressive).
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>wet</term>
+<listitem>
+<para>
+The amount of reverb-signal (that is, the amount of the signal that
+should be modified by the filters, resulting in a <quote>wet</quote>,
+that is <quote>reverb sound</quote>.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>dry</term>
+<listitem>
+<para>
+The amount of pure signal passed through, resulting in an echo (or
+combined delay) rather than reverb effect (range: 0..1).
+</para>
+<!-- TODO: do some measurements to show that this documentation -is- correct,
+I am not sure if it is echo, or really pure (non-delayed), or multiple delay
+or whatever -->
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>width</term>
+<listitem>
+<para>
+The amount of stereo-magic the reverb algorithm adds to the reverb
+effect, making the reverb sound wider in the stereo panorama (range:
+0..1).
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>mode</term>
+<listitem>
+<para>
+[ TODO: I think if mode is 1, the reverb holds the current image of the
+sound, whereas 0 is normal operation ]
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+</sect3>
+
+<sect3 id="mref-synth-tremolo-sect">
+<title>Synth&lowbar;TREMOLO</title>
+<anchor id="mref-synth-tremolo" />
+
+<mediaobject><imageobject><imagedata fileref="images/Synth_TREMOLO.png"
+format="PNG" /></imageobject>
+<textobject><phrase>Synth&lowbar;TREMOLO</phrase></textobject>
+</mediaobject>
+
+<para>
+The tremolo module modulates the amplitude according to a
+<acronym>LFO</acronym>-Wave. Traditionally you would use a sine wave
+but why limit yourself? What you get is a very intense effect that cuts
+through most arrangements because of its high dynamic range. The
+tremolo effect is still one of guitarists favorite effects although
+it's not as popular as in the 1960's.
+</para>
+
+<para>
+[ TODO: currently this is implemented as invalue + abs(inlfo) - maybe it
+would make more sense to implement it as invalue * (1+inlfo*depth),
+where depth would be a parameter between 0..1 - decide this after &kde;2.1
+; if you have a comment, send a mail to the &arts; list ;). ]
+</para>
+
+</sect3>
+<sect3 id="mref-synth-fx-cflanger-sect">
+<title>Synth&lowbar;FX&lowbar;CFLANGER</title>
+<anchor id="mref-synth-fx-cflanger" />
+
+<mediaobject><imageobject><imagedata
+fileref="images/Synth_FX_CFLANGER.png" format="PNG" /></imageobject>
+<textobject><phrase>Synth&lowbar;FX&lowbar;CFLANGER</phrase></textobject>
+</mediaobject>
+
+<para>
+A flanger is a time-varying delay effect. To make development of complex
+flanger effects simpler, this module is provided, which contains the
+core of a one-channel flanger.
+</para>
+
+<para>It has the following ports:</para>
+
+<variablelist>
+<varlistentry>
+<term>invalue</term>
+<listitem>
+<para>
+The signal which you want to process.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>lfo</term>
+<listitem>
+<para>
+Preferably a sine wave which modulates the delay time inside the
+flanger (-1 .. 1).
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>mintime</term>
+<listitem>
+<para>
+The minimum value for the delay inside the flanger in milliseconds.
+Suggested values: try something like 1 ms. Please use values &lt; 1000
+ms.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>maxtime</term>
+<listitem>
+<para>
+The minimum value for the delay inside the flanger in milliseconds.
+Suggested values: try something like 5 ms. Please use values &lt; 1000
+ms.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>outvalue</term>
+<listitem>
+<para>
+The output signal. It is important that you mix that with the
+original (unflanged) signal to get the desired effect.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+<tip>
+<para>
+You can use this as a basis for a chorus effect.
+</para>
+</tip>
+
+</sect3>
+
+</sect2>
+
+<sect2 id="mcat-synth-filters">
+<title>Filters</title>
+
+<sect3 id="mref-synth-pitch-shift-sect">
+<title>Synth&lowbar;PITCH&lowbar;SHIFT</title>
+<anchor id="mref-synth-pitch-shift" />
+
+<mediaobject><imageobject><imagedata
+fileref="images/Synth_PITCH_SHIFT.png" format="PNG"/></imageobject>
+<textobject><phrase>Synth&lowbar;PITCH&lowbar;SHIFT</phrase></textobject>
+</mediaobject>
+
+<para>
+This pitch shifting effect changes the frequency of the input signal
+without affecting the speed. An application for this is for instance
+changing the pitch of your voice while you record (and replay) it in
+realtime.
+</para>
+
+<para>
+The <emphasis>speed</emphasis> parameter is the relative speed with
+which the signal will be replayed. So a speed of two would make it sound
+twice as high (&ie; an input frequency of 440 Hz would result in an
+output frequency of 880 Hz).
+</para>
+
+<para>
+The <emphasis>frequency</emphasis> parameter is used internally to
+switch between different grains of the signal. It is tunable, and
+depending on your choice, the pitch shifting will sound more or less
+realistic for your use case. A good value to start with is something
+like 5 or 10.
+</para>
+
+</sect3>
+
+<sect3 id="mref-synth-shelve-cutoff-sect">
+<title>Synth&lowbar;SHELVE&lowbar;CUTOFF</title>
+<anchor id="mref-synth-shelve-cutoff" />
+
+<mediaobject><imageobject><imagedata
+fileref="images/Synth_SHELVE_CUTOFF.png" format="PNG"/></imageobject>
+<textobject><phrase>Synth&lowbar;SHELVE&lowbar;CUTOFF</phrase></textobject>
+</mediaobject>
+
+<para>
+Filters out all frequencies over the cutoff frequency.
+</para>
+
+</sect3>
+
+<sect3 id="mref-synth-brickwall-limiter-sect">
+<title>Synth&lowbar;BRICKWALL&lowbar;LIMITER</title>
+<anchor id="mref-synth-brickwall-limiter" />
+
+<mediaobject><imageobject><imagedata
+fileref="images/Synth_BRICKWALL_LIMITER.png"
+ format="PNG"/></imageobject>
+<textobject><phrase>Synth&lowbar;BRICKWALL&lowbar;LIMITER</phrase></textobject>
+</mediaobject>
+
+<para>
+This modules clips a signal to make it fit into the range of [-1;1]. It
+doesn't do anything to prevent the distortion that happens when clipping
+loud signals. You can use this as effect (for instance to create a
+slightly clipped sine wave). However, it's probably a good idea to run
+the signal through a lowpass filter afterwards if you do so, to make it
+sound less aggressive.
+</para>
+</sect3>
+
+<sect3 id="mref-synth-std-equalizer-sect">
+<title>Synth&lowbar;STD&lowbar;EQUALIZER</title>
+<anchor id="mref-synth-std-equalizer" />
+
+<mediaobject><imageobject><imagedata
+fileref="images/Synth_STD_EQUALIZER.png" format="PNG" /></imageobject>
+<textobject><phrase>Synth&lowbar;STD&lowbar;EQUALIZER</phrase></textobject>
+</mediaobject>
+
+<para>
+This is a nice parametric equalizer building block. It's parameters are:
+</para>
+
+<variablelist>
+<varlistentry>
+<term>invalue, outvalue</term>
+<listitem>
+<para>
+The signal that gets filtered by the equalizer.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>low</term>
+<listitem>
+<para>
+How low frequencies should be changed. The value is in dB, while 0 means
+don't change low frequencies, -6 would mean take them out by 6dB, and +6
+mean boost them by 6dB.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>mid</term>
+<listitem>
+<para>
+How middle frequencies should be changed by the equalizer in dB (see
+low).
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>high</term>
+<listitem>
+<para>
+How high frequencies should be changed by the equalizer in dB (see low).
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>frequency</term>
+<listitem>
+<para>
+This is the center frequency of the equalizer in Hz, the mid frequencies
+are around that spectrum, the low and high frequencies below and above.
+Note that the frequency may not be higher than half the sampling rate,
+usually that is 22050 Hz, and not lower than 1 Hz.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>q</term>
+<listitem>
+<para>
+This influences how broad the mid spectrum is. It must be be a positive
+number &gt; 0. A value of one is reasonable, higher values of q mean a
+narrower spectrum of middle frequencies. Lower values than one mean a
+broader spectrum.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+</sect3>
+
+<sect3 id="mref-synth-rc-sect">
+<title>Synth&lowbar;RC</title>
+<anchor id="mref-synth-rc" />
+
+<mediaobject><imageobject><imagedata fileref="images/Synth_RC.png"
+format="PNG"/></imageobject>
+<textobject><phrase>Synth&lowbar;RC</phrase></textobject>
+</mediaobject>
+
+<para>
+A damped resonator filter filtering all frequencies around some peak
+value. There is no useful way of specifying middle frequency (that
+won't be cut), since the input are two strange constants f and b. The
+code is very old, from the first days of the synthesizer, and will
+probably replaced by a new filter which will have a frequency and a
+resonance value as parameters.
+</para>
+
+<para>
+Try something like b=5, f=5 or b=10, f=10 or b=15, f=15 though.
+</para>
+
+</sect3>
+
+<sect3 id="mref-synth-moog-vcf-sect">
+<title>Synth&lowbar;MOOG&lowbar;VCF</title>
+<anchor id="mref-synth-moog-vcf" />
+
+<mediaobject><imageobject><imagedata fileref="images/Synth_MOOG_VCF.png"
+format="PNG" /></imageobject>
+<textobject><phrase>Synth&lowbar;MOOG&lowbar;VCF</phrase></textobject>
+</mediaobject>
+
+<para>
+Filters out all frequencies over the cutoff frequency (it's a 24db 4pole
+filter, which filters -24db per octave above the cutoff frequency), but
+offers an additional parameter for tuning the filter resonance, while 0
+means no resonance and 4 means self oscillation.
+</para>
+
+</sect3>
+
+</sect2>
+
+<sect2 id="mcat-synth-midi-sequencing">
+<title>Midi + Sequencing</title>
+
+<sect3 id="mref-synth-midi-test-sect">
+<title>Synth&lowbar;MIDI&lowbar;TEST</title>
+<anchor id="mref-synth-midi-test" />
+
+<mediaobject><imageobject><imagedata fileref="images/Synth_MIDI_TEST.png"
+format="PNG" /></imageobject>
+<textobject><phrase>Synth&lowbar;MIDI&lowbar;TEST</phrase></textobject>
+</mediaobject>
+
+<para>
+This modules loads an instrument structure from a file, and registers
+itself as midi output with the &arts; &MIDI; manager. Notes sent to this
+output will result in instrument voices being created.
+</para>
+
+<note>
+<para>
+You can setup something like this more convenient in &artscontrol; than
+manually in &arts-builder;.
+</para>
+</note>
+
+</sect3>
+
+<sect3 id="mref-synth-sequence-sect">
+<title>Synth&lowbar;SEQUENCE</title>
+<anchor id="mref-synth-sequence" />
+
+<mediaobject><imageobject><imagedata fileref="images/Synth_SEQUENCE.png"
+format="PNG" /></imageobject></mediaobject>
+
+<para>
+Will play a sequence of notes over and over again. The notes are given
+in tracker notation, and are separated by semicolons. An example is
+<literal>A-3;C-4;E-4;C-4;</literal>. The speed is given as seconds per
+note, so if you want to get 120 bpm, you will probably specify 0.5
+seconds/note, as 60 seconds/0.5 seconds per note=120 bpm.
+</para>
+
+<para>
+You can give each note an length relative to the speed by using a colon
+after the note and then then
+length. <literal>A-3:2;C-4:0.5;D-4:0.5;E-4;</literal> demonstrates
+this. As you see, midi composing programs tend to offer more comfort ;)
+</para>
+
+<para>
+The Synth&lowbar;SEQUENCE gives additional information about the
+position of the note it is playing right now, while 0 means just started
+and 1 means finished. This information you can use with
+Synth&lowbar;PSCALE (see below).
+</para>
+</sect3>
+
+<sect3 id="mref-synth-sequence-freq-sect">
+<title>Synth&lowbar;SEQUENCE&lowbar;FREQ</title>
+<anchor id="mref-synth-sequence-freq" />
+
+<mediaobject><imageobject><imagedata fileref="images/Synth_SEQUENCE_FREQ.png"
+format="PNG" /></imageobject></mediaobject>
+
+<para>
+This module works just like Synth&lowbar;SEQUENCE with the only difference that
+you don't write notenames but frequencies.
+</para>
+
+</sect3>
+
+</sect2>
+
+<sect2 id="mcat-synth-samples">
+<title>Samples</title>
+
+<sect3 id="mref-synth-play-wav-sect">
+<title>Synth&lowbar;PLAY&lowbar;WAV</title>
+<anchor id="mref-synth-play-wav" />
+
+<mediaobject>
+<imageobject><imagedata fileref="images/Synth_PLAY_WAV.png"
+format="PNG"/></imageobject>
+<textobject><phrase>Synth&lowbar;PLAY&lowbar;WAV</phrase></textobject>
+</mediaobject>
+
+<para>
+This will play a <literal role="extension">wav</literal> file. It will
+only be present if you have libaudiofile on your computer. The wave file
+will start as soon as the module gets created.
+</para>
+
+<para>
+It will stop as soon as it's over, then finished will be set to 1. The
+speed parameter can be used to replay the file faster or slower, where
+1.0 is the normal (recorded) speed.
+</para>
+<!-- TODO: KDE2.2: check that this really works together in instruments with
+the done parameter things ;) -->
+</sect3>
+
+</sect2>
+
+<sect2 id="mcat-synth-soundio">
+<title>Sound IO</title>
+
+<sect3 id="mref-synth-play-sect">
+<title>Synth&lowbar;PLAY</title>
+<anchor id="mref-synth-play" />
+
+<mediaobject>
+<imageobject><imagedata fileref="images/Synth_PLAY.png"
+format="PNG"/></imageobject>
+<textobject><phrase>Synth&lowbar;PLAY</phrase></textobject>
+</mediaobject>
+
+<important>
+<para>
+You will normally not need this module, unless you are writing
+standalone applications. Inside &artsd;, there normally is already a
+Synth&lowbar;PLAY module, and creating another one will not work.
+</para>
+</important>
+
+<para>
+The Synth&lowbar;PLAY-module will output your audio signal to the
+soundcard. The left and right channels should contain the
+<emphasis>normalized</emphasis> input for the channels. If your input
+is not between -1 and 1, you get clipping.
+</para>
+
+<para>
+As already mentioned, there may only be one Synth&lowbar;PLAY module
+used, as this one directly accesses your soundcard. Use busses if you
+want to mix more than one audio stream together before playing. Use the
+Synth&lowbar;AMAN&lowbar;PLAY module to get something like an output
+inside &artsd;.
+</para>
+
+<para>
+Note that Synth&lowbar;PLAY also does the timing of the whole
+structure. This means: no Synth&lowbar;PLAY = no source for timing = no
+sound. So you absolutely need (exactly) one Synth&lowbar;PLAY object.
+</para>
+
+</sect3>
+
+<sect3 id="mref-synth-record-sect">
+<title>Synth&lowbar;RECORD</title>
+<anchor id="mref-synth-record" />
+
+<mediaobject><imageobject><imagedata fileref="images/Synth_RECORD.png"
+format="PNG"/></imageobject>
+<textobject><phrase>Synth&lowbar;RECORD</phrase></textobject>
+</mediaobject>
+
+<important>
+<para>You will normally not need this module, unless you are writing
+standalone applications. Inside artsd, there normally is already a
+Synth&lowbar;RECORD module, and creating another one will not work.
+</para>
+</important>
+
+<para>
+The Synth&lowbar;RECORD-module will record a signal from the soundcard.
+The left and right channels will contain the input for the channels
+(between -1 and 1).
+</para>
+
+<para>
+As already mentioned, there may only be one Synth&lowbar;RECORD module
+used, as this one directly accesses your soundcard. Use busses if you
+want to use the recorded audio stream in more than one place. Use the
+Synth&lowbar;AMAN&lowbar;RECORD module to get something like an input
+inside artsd. For this to work, &artsd; must run <emphasis>with full
+duplex enabled </emphasis>.
+</para>
+</sect3>
+
+<sect3 id="mref-synth-aman-play-sect">
+<title>Synth&lowbar;AMAN&lowbar;PLAY</title>
+<anchor id="mref-synth-aman-play" />
+
+<mediaobject><imageobject><imagedata fileref="images/Synth_AMAN_PLAY.png"
+format="PNG"/></imageobject>
+<textobject><phrase>Synth&lowbar;AMAN&lowbar;PLAY</phrase></textobject>
+</mediaobject>
+
+<para>
+The Synth&lowbar;AMAN&lowbar;PLAY-module will output your audio signal.
+It is nice (but not necessary) if you output a normalized signal
+(between -1 and 1).
+</para>
+
+<para>
+This module will use the audio manager to assign where the signal will
+be played. The audio manager can be controlled through &artscontrol;. To
+make it more intuitive to use, it is good to give the signal you play a
+name. This can be achieved through setting
+<emphasis>title</emphasis>. Another feature of the audio manager is to
+be able to remember where you played a signal the last time. To do so it
+needs to be able to distinguish signals. That is why you should assign
+something unique to <emphasis>autoRestoreID</emphasis>, too.
+</para>
+</sect3>
+
+<sect3 id="mref-synth-aman-record-sect">
+<title>Synth&lowbar;AMAN&lowbar;RECORD</title>
+<anchor id="mref-synth-aman-record" />
+
+<mediaobject><imageobject><imagedata
+fileref="images/Synth_AMAN_RECORD.png" format="PNG"/></imageobject>
+<textobject><phrase>Synth&lowbar;AMAN&lowbar;RECORD</phrase></textobject>
+</mediaobject>
+
+<para>
+The Synth&lowbar;AMAN&lowbar;RECORD-module will record an audio signal
+from an external source (&ie;. line in/microphone) within &artsd;. The
+output will be a normalized signal (between -1 and 1).
+</para>
+
+<para>
+This module will use the audio manager to assign where the signal will
+be played. The audio manager can be controlled through artscontrol. To
+make it more intuitive to use, it is good to give the signal you record
+a name. This can be achieved through setting
+<emphasis>title</emphasis>. Another feature of the audio manager is to
+be able to remember where you recorded a signal the last time. To do so
+it needs to be able to distinguish signals. That is why you should
+assign something unique to <emphasis>autoRestoreID</emphasis>, too.
+</para>
+</sect3>
+
+<sect3 id="mref-synth-capture-sect">
+<title>Synth&lowbar;CAPTURE</title>
+<anchor id="mref-synth-capture" />
+
+<mediaobject><imageobject><imagedata fileref="images/Synth_CAPTURE.png"
+format="PNG" /></imageobject>
+<textobject><phrase>Synth&lowbar;CAPTURE</phrase></textobject>
+</mediaobject>
+
+<para>
+The Synth&lowbar;CAPTURE-module will write an audio signal to a wave
+file on your hard disc. The file will always be called
+<filename>/tmp/mcop-<replaceable>usename</replaceable>/capture.wav</filename>
+</para>
+</sect3>
+
+</sect2>
+
+<sect2 id="mcat-synth-tests">
+<title>Tests</title>
+
+<sect3 id="mref-synth-nil-sect">
+<title>Synth&lowbar;NIL</title>
+<anchor id="mref-synth-nil" />
+
+<mediaobject><imageobject><imagedata fileref="images/Synth_NIL.png"
+format="PNG" /></imageobject>
+<textobject><phrase>Synth&lowbar;NIL</phrase></textobject>
+</mediaobject>
+
+<para>
+This just does nothing. It is only useful for test situations.
+</para>
+
+</sect3>
+
+<sect3 id="mref-synth-debug-sect">
+<title>Synth&lowbar;DEBUG</title>
+<anchor id="mref-synth-debug" />
+
+<mediaobject><imageobject><imagedata fileref="images/Synth_DEBUG.png"
+format="PNG" /></imageobject>
+<textobject><phrase>Synth&lowbar;DEBUG</phrase></textobject>
+</mediaobject>
+
+<para>
+You can use this for debugging. It will print out the value of the
+signal at invalue in regular intervals (ca. 1 second), combined with the
+comment you have specified. That way you can find out if some signals
+stay in certain ranges, or if they are there at all.
+</para>
+</sect3>
+
+<sect3 id="mref-synth-midi-debug-sect">
+<title>Synth&lowbar;MIDI&lowbar;DEBUG</title>
+<anchor id="mref-synth-midi-debug" />
+
+<mediaobject><imageobject><imagedata fileref="images/Synth_MIDI_DEBUG.png"
+format="PNG" /></imageobject>
+<textobject><phrase>Synth&lowbar;MIDI&lowbar;DEBUG</phrase></textobject>
+</mediaobject>
+
+<para>
+You can use this to debug how your &MIDI; events are actually arriving
+in &arts;.
+</para>
+
+<para>
+When a MIDI&lowbar;DEBUG is running &artsserver; will print out a lines
+like:
+</para>
+
+<screen><computeroutput>201 100753.837585 on 0 42 127</computeroutput></screen>
+
+<screen><computeroutput>202 101323.128355 off 0 42</computeroutput></screen>
+
+<para>
+While the first line would be telling you that 100753ms (that is 100
+seconds) after the MIDI&lowbar;DEBUG started, a &MIDI; on event arrived
+on channel 0. This midi on event had the velocity (volume) of 127, the
+loudest possible. The next line shows the midi release event. [ TODO:
+this does not work currently, make it work, and do it via &MIDI; manager
+].
+</para>
+</sect3>
+
+<sect3 id="mref-synth-data-sect">
+<title>Synth&lowbar;DATA</title>
+<anchor id="mref-synth-data" />
+
+<mediaobject><imageobject><imagedata fileref="images/Synth_DATA.png"
+format="PNG" /></imageobject>
+<textobject><phrase>Synth&lowbar;DATA</phrase></textobject>
+</mediaobject>
+
+<para>
+This creates a signal with a constant number.
+</para>
+<!-- TODO: this doesn't really belong in test, does it? -->
+</sect3>
+</sect2>
+
+<sect2 id="mcat-synth-osc-mod">
+<title>Oscillation &amp; Modulation</title>
+
+<sect3 id="mref-synth-frequency-sect">
+<title>Synth&lowbar;FREQUENCY</title>
+<anchor id="mref-synth-frequency" />
+
+<mediaobject><imageobject><imagedata fileref="images/Synth_FREQUENCY.png"
+format="PNG" /></imageobject>
+<textobject><phrase>Synth&lowbar;FREQUENCY</phrase></textobject>
+</mediaobject>
+
+<para>
+All oscillators in &arts; don't require a frequency as input, but a
+position in the wave. The position should be between 0 and 1, which maps
+for a standard Synth&lowbar;WAVE&lowbar;SIN object to the range
+0..2*pi. To generate oscillating values from a frequency, a
+Synth&lowbar;FREQUENCY modules is used.
+</para>
+</sect3>
+
+<sect3 id="mref-synth-fm-source-sect">
+<title>Synth&lowbar;FM&lowbar;SOURCE</title>
+<anchor id="mref-synth-fm-source" />
+
+<mediaobject><imageobject><imagedata fileref="images/Synth_FM_SOURCE.png"
+format="PNG" /></imageobject>
+<textobject><phrase>Synth&lowbar;FM&lowbar;SOURCE</phrase></textobject>
+</mediaobject>
+
+<para>
+This is used for frequency modulation. Put your frequency to the
+frequency input and put another signal on the modulator input. Then set
+modlevel to something, say 0.3. The frequency will be modulated with
+modulator then. Just try it. Works nice when you put a feedback in
+there, that means take a combination of the delayed output signal from
+the Synth&lowbar;FM&lowbar;SOURCE (you need to put it to some oscillator
+as it only takes the role of Synth&lowbar;FREQUENCY) and some other
+signal to get good results.
+</para>
+
+<para>
+Works nicely in combination with Synth&lowbar;WAVE&lowbar;SIN
+oscillators.
+</para>
+</sect3>
+
+</sect2>
+
+<sect2 id="mcat-synth-waveforms">
+<title>Wave Forms</title>
+
+<sect3 id="mref-synth-wave-sin-sect">
+<title>Synth&lowbar;WAVE&lowbar;SIN</title>
+<anchor id="mref-synth-wave-sin" />
+
+<mediaobject><imageobject><imagedata fileref="images/Synth_WAVE_SIN.png"
+format="PNG" /></imageobject>
+<textobject><phrase>Synth&lowbar;WAVE&lowbar;SIN</phrase></textobject>
+</mediaobject>
+
+<para>
+Sinus oscillator. Put a pos signal from Synth&lowbar;FREQUENCY or
+Synth&lowbar;FM&lowbar;SOURCE at the input. And get a sinus wave as
+output. The pos signal specifies the position in the wave, the range
+0..1 is mapped to 0..2*pi internally.
+</para>
+
+</sect3>
+
+<sect3 id="mref-synth-wave-tri-sect">
+<title>Synth&lowbar;WAVE&lowbar;TRI</title>
+<anchor id="mref-synth-wave-tri" />
+
+<mediaobject><imageobject><imagedata fileref="images/Synth_WAVE_TRI.png"
+format="PNG" /></imageobject>
+<textobject><phrase>Synth&lowbar;WAVE&lowbar;TRI</phrase></textobject>
+</mediaobject>
+
+<para>
+Triangle oscillator. Put a pos signal from Synth&lowbar;FREQUENCY or
+Synth&lowbar;FM&lowbar;SOURCE at the input. And get a triangle wave as
+output. The pos signal specifies the position in the wave, the range
+0..1 is mapped to 0..2*pi internally. Be careful. The input signal
+<emphasis>must</emphasis> be in the range 0..1 for the output signal to
+produce good results.
+</para>
+</sect3>
+
+<sect3 id="mref-synth-noise-sect">
+<title>Synth&lowbar;NOISE</title>
+<anchor id="mref-synth-noise" />
+
+<mediaobject><imageobject><imagedata fileref="images/Synth_NOISE.png"
+format="PNG" /></imageobject>
+<textobject><phrase>Synth&lowbar;NOISE</phrase></textobject>
+</mediaobject>
+
+<para>
+Noise generator. This generates a random signal between -1 and 1.
+</para>
+
+</sect3>
+
+<sect3 id="mref-synth-wave-square-sect">
+<title>Synth&lowbar;WAVE&lowbar;SQUARE</title>
+<anchor id="mref-synth-wave-square" />
+
+<mediaobject><imageobject><imagedata
+fileref="images/Synth_WAVE_SQUARE.png" format="PNG" /></imageobject>
+<textobject><phrase>Synth&lowbar;WAVE&lowbar;SQUARE</phrase></textobject>
+</mediaobject>
+
+<para>
+Square oscillator. Put a pos signal from Synth&lowbar;FREQUENCY or
+Synth&lowbar;FM&lowbar;SOURCE at the input. And get a square wave as
+output. The pos signal specifies the position in the wave, the range
+0..1 is mapped to 0..2*pi internally. Be careful. The input signal
+<emphasis>must</emphasis> be in the range 0..1 for the output signal to
+produce good results.
+</para>
+</sect3>
+
+<sect3 id="mref-synth-wave-softsaw-sect">
+<title>Synth&lowbar;WAVE&lowbar;SOFTSAW</title>
+<anchor id="mref-synth-wave-softsaw" />
+
+<mediaobject><imageobject><imagedata
+fileref="images/Synth_WAVE_SOFTSAW.png" format="PNG" /></imageobject>
+<textobject><phrase>Synth&lowbar;WAVE&lowbar;SOFTSAW</phrase></textobject>
+</mediaobject>
+
+<para>
+Softened saw wave, similar in look like the Synth&lowbar;WAVE&lowbar;TRI
+oscillator. Put a pos signal from Synth&lowbar;FREQUENCY or
+Synth&lowbar;FM&lowbar;SOURCE at the input. You'll get a softened saw
+wave as output. The pos signal specifies the position in the wave, the
+range 0..1 is mapped to 0..2*pi internally. Be careful. The input signal
+<emphasis>must</emphasis> be in the range 0..1 for the output signal to
+produce good results.
+</para>
+</sect3>
+
+<sect3 id="mref-synth-wave-pulse-sect">
+<title>Synth&lowbar;WAVE&lowbar;PULSE</title>
+<anchor id="mref-synth-wave-pulse" />
+
+<mediaobject><imageobject><imagedata fileref="images/Synth_WAVE_PULSE.png"
+format="PNG" /></imageobject>
+<textobject><phrase>Synth&lowbar;WAVE&lowbar;PULSE</phrase></textobject>
+</mediaobject>
+
+<para>
+Pulse oscillator - this module is similar in spirit like the rectangular
+oscillator (Synth_WAVE_RECT), but it provides a configurable up/down
+ratio, through the <emphasis>dutycycle</emphasis> parameter. Put a pos
+signal from Synth&lowbar;FREQUENCY or Synth&lowbar;FM&lowbar;SOURCE at
+the input. Get a pulse wave as output. The pos signal specifies the
+position in the wave, the range 0..1 is mapped to 0..2*pi internally. Be
+careful. The input signal <emphasis>must</emphasis> be in the range 0..1
+for the output signal to produce good results.
+</para>
+</sect3>
+</sect2>
+<sect2 id="mcat-synth-misc">
+<title>Miscellaneous</title>
+
+<sect3 id="mref-synth-compressor-sect">
+<title>Synth&lowbar;COMPRESSOR</title>
+<anchor id="mref-synth-compressor" />
+
+<mediaobject>
+<imageobject><imagedata fileref="images/Synth_COMPRESSOR.png"
+ format="PNG"/></imageobject></mediaobject>
+
+<para>
+This module reduces the dynamic range of the signal. For example
+compressors are useful in compensating for the wide variations in
+loudness of somebody talking into a microphone.
+</para>
+
+<para>
+As soon as the input level exceeds a certain level (the threshold)
+the signal gets compressed. It simply multiplies everything above
+the threshold with the ratio, which should be a number between 0 and
+1. Finally the whole signal is multiplied by the output factor.
+</para>
+
+<para>
+The attack and release arguments delay the start and end of the
+compression. Use this if you, for example, still want to hear the
+loud beginning of a basedrum. The argument is in milliseconds and an
+attack or release of 0ms is possible but may result in a slight noise.
+</para>
+
+</sect3>
+</sect2>
+</sect1>
+
+<sect1 id="visual-modules-reference">
+<title>Visual Modules Reference</title>
+
+<para>
+TODO when visual modules are more "finished".
+</para>
+</sect1>
+
+</chapter>
diff --git a/doc/artsbuilder/porting.docbook b/doc/artsbuilder/porting.docbook
new file mode 100644
index 00000000..f039904e
--- /dev/null
+++ b/doc/artsbuilder/porting.docbook
@@ -0,0 +1,64 @@
+<!-- <?xml version="1.0" ?>
+<!DOCTYPE chapter PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd">
+To validate or process this file as a standalone document, uncomment
+this prolog. Be sure to comment it out again when you are done -->
+
+<chapter id="porting">
+<title>Porting Applications to &arts;</title>
+
+<sect1 id="using-artsdsp">
+<title>Using &artsdsp;</title>
+
+<para>
+The &artsdsp; utility, <link linkend="artsdsp">described
+previously</link>, allows most legacy sound applications that talk to
+the audio devices directly, to work properly under &arts;. Applications
+written to use the Enlightenment Sound Daemon
+(<application>esd</application>) will also work in most cases by running
+<application>esd</application> under &artsdsp;.
+</para>
+
+<para>
+This makes a good short term solution to porting existing applications
+to &kde;. However, it does not allow the application to directly take
+advantage of all of the power of &arts;, such as using modules and
+multimedia streams other than digital audio. If the application goes
+beyond simple playing of sound files, it usually makes sense to add
+native support for &arts; to the application.
+</para>
+
+<para>
+Using &arts; also means that application does not have to do as much
+work -- it can leverage the functions in &arts; to handle issues like
+codecs for different media formats and control of the sound hardware.
+</para>
+
+</sect1>
+
+<sect1 id="adding-native-arts-support">
+<title>Adding Native &arts; support</title>
+
+<para>
+When using &arts;, you have a number of different <link
+linkend="arts-apis"><acronym>API</acronym>s</link> to choose from. The
+decision of which to use depends on a number of factors, including what
+type of streaming media is used (sound, &MIDI;, &CD; audio, &etc;), the
+<acronym>API</acronym> features required, and whether it is written in
+C++. In most cases the choice should be relatively obvious based on the
+required features.
+</para>
+
+<para>
+For cross-platform portability, applications that need to run on
+environments other than &kde; cannot rely on &arts; being present. Using
+the plug-ins paradigm is a good way to support different multimedia
+environments. Making the plug-in <acronym>API</acronym> open and
+documented (especially for closed source applications) also has the
+advantage of allowing someone other than the application developer to
+implement an &arts; plug-in.
+</para>
+
+</sect1>
+
+</chapter>
+
diff --git a/doc/artsbuilder/references.docbook b/doc/artsbuilder/references.docbook
new file mode 100644
index 00000000..4978f723
--- /dev/null
+++ b/doc/artsbuilder/references.docbook
@@ -0,0 +1,56 @@
+<!-- <?xml version="1.0" ?>
+<!DOCTYPE chapter PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd">
+To validate or process this file as a standalone document, uncomment
+this prolog. Be sure to comment it out again when you are done -->
+
+<chapter id="references">
+<title>References</title>
+
+<variablelist>
+
+<varlistentry>
+<term><ulink
+url="http://multimedia.kde.org">http://multimedia.kde.org</ulink></term>
+<listitem>
+<para>
+This is the primary web site for &kde;-related multimedia information.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><ulink
+url="http://www.arts-project.org">http://www.arts-project.org</ulink></term>
+<listitem>
+<para>
+This is the home page for the &arts; project.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>&kde; 2.0 Development</term>
+<listitem>
+<para>
+Chapter 14 of this published book covers multimedia, including
+&arts;. It is available in print or on-line with annotations at <ulink
+url="http://www.andamooka.org/">http://www.andamooka.org</ulink>.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<ulink
+url="http://sound.condorow.net">http://sound.condorow.net</ulink></term>
+<listitem>
+<para>
+This site has a comprehensive listing of sound and &MIDI; applications
+for &Linux;.
+</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+</chapter>
diff --git a/doc/artsbuilder/tools.docbook b/doc/artsbuilder/tools.docbook
new file mode 100644
index 00000000..417951df
--- /dev/null
+++ b/doc/artsbuilder/tools.docbook
@@ -0,0 +1,735 @@
+<!--
+<?xml version="1.0" ?>
+<!DOCTYPE chapter PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd">
+
+To validate or process this file as a standalone document, uncomment
+this prolog. Be sure to comment it out again when you are done -->
+
+<chapter id="arts-tools">
+<title>&arts; Tools</title>
+
+<para>
+Included with &arts; is a number of utilities for controlling and
+configuring its behavior. You need to have some familiarity with most of
+these tools in order to use &arts; effectively. This section describes
+each of the utilities and their command options.
+</para>
+
+<sect1 id="kde-control-center">
+<title>&kcontrol;</title>
+
+<para>
+When running &arts; under &kde;, the &kcontrolcenter; provides a group
+of control panel settings under the <guilabel>Sound</guilabel>
+category. Some of these settings are used by &arts;. You can also
+associate sounds with various window manager and &kde; events using the
+<menuchoice><guilabel>Look &amp; Feel</guilabel><guilabel>System
+Notifications</guilabel></menuchoice> panel. See the &kcontrol; manual
+for information on using the panel settings.
+</para>
+
+</sect1>
+
+<sect1 id="artsd">
+<title>&artsd;</title>
+
+<para>
+Access to the sound hardware resources is controlled by &artsd;, the
+&arts; daemon. This allows different applications to simultaneously send
+requests to the server, where they can be mixed together and
+played. Without a centralized sound server a single application using a
+sound device would prevent other applications from using it.
+</para>
+
+<para>
+To use &arts; there should be one and only one copy of &artsd;
+running. It is typically run when &kde; starts up if it is enabled in
+the &kcontrol; <guilabel>Sound Server</guilabel> panel.
+</para>
+
+<para>The program accepts the following arguments:</para>
+
+<!-- LW: FIX THIS -->
+
+<cmdsynopsis>
+<command>artsd</command>
+<group choice="opt">
+<option>-n</option>
+<option>-p</option>
+<option>-N</option>
+<option>-W <replaceable>n</replaceable></option>
+
+</group>
+<group choice="opt">
+<option>-a <replaceable>audiomethod</replaceable></option>
+<option>-r <replaceable>sampling rate</replaceable></option>
+<option>-b <replaceable>bits</replaceable></option>
+<option>-d</option>
+<option>-D <replaceable>devicename</replaceable></option>
+<option>-F <replaceable>fragments</replaceable></option>
+<option>-S <replaceable>size</replaceable></option>
+<option>-s <replaceable>seconds</replaceable></option>
+<option>-m <replaceable>appName</replaceable></option>
+</group>
+<group choice="opt">
+<option>-h</option>
+<option>-A</option>
+<option>-v</option>
+<option>-l <replaceable>level</replaceable></option>
+</group>
+</cmdsynopsis>
+
+<variablelist><varlistentry>
+<term><option>-r <replaceable>sampling rate</replaceable></option></term>
+<listitem>
+<para>Set sampling rate to use.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-h</option></term>
+<listitem>
+<para>Display command usage.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-n</option></term>
+<listitem>
+<para>Enable network transparency.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-p <replaceable>port</replaceable></option>
+</term>
+<listitem>
+<para>Set <acronym>TCP</acronym> port to use (implies
+<option>-n</option>).</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-u</option></term>
+<listitem>
+<para>Public, no authentication (dangerous).</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-d</option></term>
+<listitem>
+<para>Enable full duplex operation.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><option>-D <replaceable>device name</replaceable></option></term>
+<listitem>
+<para>Specify audio device (usually <filename>/dev/dsp</filename>).</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-F <replaceable>fragments</replaceable></option></term>
+<listitem>
+<para>Set number of fragments.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-S <replaceable>size</replaceable></option></term>
+<listitem>
+<para>Set fragment size, in bytes.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-s <replaceable>seconds</replaceable></option></term>
+<listitem>
+<para>Set server auto-suspend time, in seconds. A value of zero
+disables auto-suspend.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-m <replaceable>appName</replaceable></option></term>
+<listitem>
+<para>Specify the name of an application to be used to display error,
+warning, and informational messages. If you are running KDE you can
+use the <application>artsmessage</application> utility for this.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-N</option></term>
+<listitem>
+<para>
+Increase the size of network buffers to a value suitable for running over
+a 10 mbps LAN. This is equivalent to using the -w 5 option (see below).
+</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><option>-w <replaceable>n</replaceable></option></term>
+<listitem>
+<para>
+When running <application>artsd</application> over a network connection
+to another host you typically want to use a larger buffer size to
+avoid dropouts. ARts provides applications with a suggested minimum
+buffer size. Without this option the default size is based on the
+fragment size * fragment count. Using this option you can increase
+the size from the default by a factor of <replaceable>n</replaceable>.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-l <replaceable>level</replaceable></option></term>
+<listitem>
+<para>Set information level - 3 (quiet), 2 (warnings), 1 (info), 0
+(debug).</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-v</option></term>
+<listitem>
+<para>Display version level.</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+<para>
+In most cases simply running &artsd; will suffice.
+</para>
+</sect1>
+
+<sect1 id="artswrapper">
+<title>&artswrapper;</title>
+
+<para>
+To provide good real-time response, &artsd; is usually run as a
+real-time process (on platforms where real-time priorities are
+supported). This requires <systemitem class="username">root</systemitem>
+permissions, so to minimize the security implications, &artsd; can be
+started using the small wrapper program &artswrapper; which simply sets
+real-time priority (running as <systemitem
+class="username">root</systemitem>) and then executes &artsd; as a
+non-<systemitem class="username">root</systemitem> user.
+</para>
+
+<para>If you make artswrapper SUID <systemitem
+class="username">root</systemitem>, it will likely improve the quality
+of your audio playback by reducing gaps in the music. However, it
+also increases the risk that a bug in the code or a malicious user can
+crash or otherwise harm your machine. In addition, on multi-user
+machines, prioritizing high-quality audio may result in deteriorated
+performance for the users who are trying to make
+<quote>productive</quote> use of the machine.</para>
+
+</sect1>
+
+<sect1 id="artsshell">
+<title>&artsshell;</title>
+
+<para>
+The &artsshell; command is intended as a utility to perform
+miscellaneous functions related to the sound server. It is expected that
+the utility will be extended with new commands in the future (see the
+comments in the source code for some ideas).
+</para>
+
+<para>
+The command accepts the following format:
+</para>
+
+<!-- LW: FIX THIS -->
+
+<cmdsynopsis>
+<command>artsshell</command>
+<group>
+<arg>suspend</arg><arg>status</arg>
+<arg>terminate</arg>
+<arg>autosuspend <replaceable>secs</replaceable></arg>
+<arg>networkbuffers <replaceable>n</replaceable></arg>
+<arg>volume [<replaceable>volume</replaceable>]</arg>
+<arg>stereoeffect <replaceable>options</replaceable></arg>
+</group>
+<group>
+<option>-h</option>
+<option>-q</option>
+</group>
+</cmdsynopsis>
+
+<para>artsshell [options] <replaceable>command</replaceable> [<replaceable>command-options</replaceable>] </para>
+
+<para>
+The following options are supported:
+</para>
+
+<variablelist>
+
+<varlistentry>
+<term><option>-q</option></term>
+<listitem>
+<para>Suppress all output.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-h</option></term>
+<listitem>
+<para>Display command usage.</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+<para>The following commands are supported:</para>
+
+<variablelist>
+
+<varlistentry>
+<term><option>suspend</option></term>
+<listitem>
+<para>
+Suspend the sound server.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>status</option></term>
+<listitem>
+<para>Display sound server status information.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>terminate</option></term>
+<listitem>
+<para>
+Terminate the sound server. This may confuse and/or crash any
+applications that are currently using it.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>autosuspend</option> <parameter>seconds</parameter></term>
+<listitem>
+<para>
+Set the autosuspend time to the specified number of seconds. The sound
+server will suspend itself if idle for that period of time. A value of
+zero disables auto-suspend.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>networkbuffers</option> <parameter>n</parameter></term>
+<listitem>
+<para>
+Set the size of the network buffers to be a factor of
+<parameter>n</parameter> times the default size.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>volume</option> [<replaceable>volume</replaceable>]</term>
+<listitem>
+<para>
+Sets volume scaling for sound server audio output. The
+<replaceable>volume</replaceable> argument is a floating point
+value. With no argument the current volume is displayed.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>stereoeffect list</option></term>
+<listitem>
+<para>List all of the available stereo effect modules.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>stereoeffect insert [top|bottom]</option> <replaceable>name</replaceable></term>
+<listitem>
+<para>Insert a stereo effect into the stereo effect stack. Returns
+an identifier that can be used for later removing it. It can be
+installed at the top or the bottom (the default).</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>stereoeffect remove</option> <replaceable>id</replaceable></term>
+<listitem>
+<para>Removes the stereo effect with identifier
+<replaceable>id</replaceable> from the effects stack.</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+</sect1>
+
+<sect1 id="artsplay">
+<title><application>artsplay</application></title>
+
+<para>The <application>artsplay</application> command is a simple utility to
+play a sound file. It accepts a single argument corresponding to the name of a
+sound file which is sent to the sound server to be played. The sound
+file can be any common sound file type such as <literal
+role="extension">wav</literal> or <literal
+role="extension">au</literal>. This utility is good for testing that the
+sound server is working. By running two commands in parallel or in rapid
+succession you can demonstrate how the sound servers mixes more than one
+sound source.</para>
+
+</sect1>
+
+<sect1 id="artsdsp">
+<title><application>artsdsp</application></title>
+
+<para>
+The sound server only supports applications that are &arts;-aware. Many
+legacy applications want to access the sound device directly. The
+&artsdsp; command provides an interim solution that
+allows most of these applications to run unchanged.
+</para>
+
+<para>
+When an application is run under &artsdsp; all accesses to the <filename
+class="devicefile">/dev/dsp</filename> audio device are intercepted and
+mapped into &arts; <acronym>API</acronym> calls. While the device
+emulation is not perfect, most applications work this way, albeit with
+some degradation in performance and latency.
+</para>
+
+<para>The &artsdsp; command follows the format:
+</para>
+
+<!-- LW: FIX THIS -->
+<para>
+artsdsp [<replaceable>options</replaceable>] <replaceable>application arguments</replaceable>
+</para>
+
+<para>
+The following options are recognized:
+</para>
+
+<variablelist>
+
+<varlistentry>
+<term><option>-h</option>, <option>--help</option></term>
+<listitem>
+<para>Show brief help.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><option>-n</option> <option>--name</option> = <replaceable>name</replaceable></term>
+<listitem>
+<para>Use <replaceable>name</replaceable> to identify player to <command>artsd</command>.</para>
+
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-m</option> <option>--mmap</option></term>
+<listitem>
+<para>Emulate memory mapping (&eg; for <application>Quake</application>).</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-v</option> <option>--verbose</option></term>
+<listitem>
+<para>Show parameters.</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+<para>
+A typical invocation is:
+</para>
+
+<para>
+<userinput><command>artsdsp</command> <option>-v</option> <option>-m</option> <parameter>realplay <replaceable>song.mp3</replaceable></parameter></userinput>
+</para>
+
+<para>
+Some applications work better with the <option>--mmap</option>
+option. Not all features of the sound device are fully emulated, but
+most applications should work. If you find one that does not, submit a
+detailed bug report and the developers may be able to fix it. Again,
+remember this is an interim solution and something of an ugly hack; the
+best solution is to add native &arts; support to the applications. If
+your favorite sound application does not have &arts; support, ask the
+developer to provide it.
+</para>
+
+</sect1>
+
+<sect1 id="artscat">
+<title><application>artscat</application></title>
+
+<para>
+This is a simple utility to send raw audio data to the sound server.
+You need to specify the data format (sampling rate, sample size, and
+number of channels). This is probably not a utility that you will use
+often, but it can be handy for testing purposes. The command syntax is:
+</para>
+
+<!-- LW: FIX THIS -->
+
+<para>
+artscat [ <replaceable>options</replaceable> ] [ <replaceable>filename</replaceable> ]
+</para>
+
+<para>
+If no file name is specified, it reads standard input. The following
+options are supported:
+</para>
+
+<variablelist>
+<varlistentry>
+<term><option>-r</option> <parameter>sampling
+rate</parameter></term>
+<listitem>
+<para>
+Set the sampling rate to use.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-b</option> <parameter>bits</parameter></term>
+<listitem>
+<para>
+Set sample size to use (8 or 16).
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-c</option> <parameter>channels</parameter></term>
+<listitem>
+<para>
+Set number of channels (1 or 2).
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-h</option></term>
+<listitem>
+<para>
+Display command usage and exit.
+</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+</sect1>
+
+<sect1 id="artscontrol">
+<title>&artscontrol;</title>
+
+<para>
+This is a graphical utility for performing a number of tasks related to
+the sound server. The default window displays two volume level
+indicators and a slider to control overall output volume. From the
+<guimenu>View</guimenu> menu you can select other functions:
+</para>
+
+<variablelist>
+
+<varlistentry>
+<term><guimenuitem>FFT Scope</guimenuitem></term>
+<listitem>
+<para>
+Opens a window which shows a real-time spectrum analyzer style display.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Audio Manager</guimenuitem></term>
+<listitem>
+<para>
+Displays active sound sources and allows you to connect them to any of
+the available busses.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>aRts Status</guimenuitem></term>
+<listitem>
+<para>
+Shows if the sound server is running and if scheduling is
+real-time. Indicates when server will autosuspend and allows you to
+suspend it immediately.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Midi Manager</guimenuitem></term>
+<listitem>
+<para>
+Shows active &MIDI; inputs and outputs and allows you to make connections
+[TODO: Does this work yet? Need more detail].
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>FreeVerb</guimenuitem></term>
+<listitem>
+<para>
+Connects a FreeVerb reverb effect to the stack of &arts; output effects
+and allows you to control the effect parameters graphically.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Leds-like volume display</guimenuitem></term>
+<listitem>
+<para>
+Changes the volume indicators in the main window to use a colored
+<acronym>LED</acronym> display format instead of progress bars.
+</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+</sect1>
+
+<sect1 id="artsc-config">
+<title><application>artsc-config</application></title>
+
+<para>
+This is a utility to assist developers using the &arts; C
+<acronym>API</acronym>. It outputs the appropriate compiler and linker
+options needed when compiling and linking code with &arts;. It is
+intended to be used within make files to assist in portability. The
+command accepts three options:
+</para>
+
+<variablelist>
+<varlistentry>
+<term><option>--cflags</option></term>
+<listitem>
+<para>
+Displays the compiler flags needed when compiling with the &arts; C
+<acronym>API</acronym>.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--libs</option></term>
+<listitem>
+<para>
+Displays the linker flags needed when linking with the &arts; C
+<acronym>API</acronym>.
+</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><acronym>--version</acronym></term>
+<listitem>
+<para>
+Displays the version of the <command>artsc-config</command> command.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+<para>Typical output from the command is shown below:</para>
+
+<screen width="40">
+<prompt>%</prompt> <userinput><command>artsc-config</command> <option>--cflags</option></userinput>
+<computeroutput>-I/usr/local/kde2/include/artsc</computeroutput>
+<prompt>%</prompt> <userinput><command>artsc-config</command> <option>--libs</option></userinput>
+<computeroutput>-L/usr/local/kde2/lib -ldl -lartsc -DPIC -fPIC -lpthread</computeroutput>
+<prompt>%</prompt> <userinput><command>artsc-config</command> <option>--version</option></userinput>
+<computeroutput>0.9.5</computeroutput>
+</screen>
+
+<para>
+You could use this utility in a make file using a rule such as:
+</para>
+
+<programlisting>
+artsc: artsc.c
+ gcc `artsc-config --cflags` -o artsc artsc.c `artsc-config --libs`
+</programlisting>
+
+</sect1>
+
+<sect1 id="mcopidl">
+<title>&mcopidl;</title>
+
+<para>
+The &mcopidl; command is the &IDL; file compiler for &MCOP;, the
+Multimedia Communication Protocol used by &arts;. Interfaces in &arts;
+are defined in &IDL;, a language independent Interface Definition
+Language. The &mcopidl; utility accepts an &IDL; file as input and
+generates C++ header and source files for a class implementing the
+interface. The command accepts the following syntax:
+</para>
+
+<!-- LW: FIX THIS -->
+
+<para>mcopidl [ <replaceable>options</replaceable> ] <replaceable>filename</replaceable>
+</para>
+
+<para>The valid options are:</para>
+<variablelist>
+<varlistentry>
+<term><option>-I</option> <parameter>directory</parameter></term>
+<listitem>
+<para>
+Search in <parameter>directory</parameter> for includes.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-e</option> <parameter>name</parameter></term>
+<listitem>
+<para>
+Exclude a struct, interface, or enum type <parameter>name</parameter>
+from code generation.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-t</option></term>
+<listitem>
+<para>
+Also create <literal role="extension">.mcoptype</literal>/<literal
+role="extension">.mcopclass</literal> files containing type information
+for the &IDL; file.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+<para>
+More information about &MCOP; and &IDL; is covered in the section <link
+linkend="interfaces">Interfaces and &IDL;</link>.
+</para>
+
+</sect1>
+
+</chapter>
diff --git a/doc/juk/Makefile.am b/doc/juk/Makefile.am
new file mode 100644
index 00000000..171f575c
--- /dev/null
+++ b/doc/juk/Makefile.am
@@ -0,0 +1,2 @@
+KDE_LANG = en
+KDE_DOCS = AUTO
diff --git a/doc/juk/history-playlist.png b/doc/juk/history-playlist.png
new file mode 100644
index 00000000..c80ee792
--- /dev/null
+++ b/doc/juk/history-playlist.png
Binary files differ
diff --git a/doc/juk/index.docbook b/doc/juk/index.docbook
new file mode 100644
index 00000000..28763a3d
--- /dev/null
+++ b/doc/juk/index.docbook
@@ -0,0 +1,1665 @@
+<?xml version="1.0" ?>
+<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
+ <!ENTITY package "kdemultimedia">
+ <!ENTITY kappname "&juk;">
+ <!ENTITY juk "<application>JuK</application>">
+ <!ENTITY % addindex "IGNORE">
+ <!ENTITY % English "INCLUDE">
+]>
+
+<book lang="&language;">
+
+<bookinfo>
+<title>The &juk; Handbook</title>
+
+<authorgroup>
+<author>&Lauri.Watts; &Lauri.Watts.mail;</author>
+<author>
+<firstname>Michael</firstname>
+<surname>Pyne</surname>
+<affiliation>
+<address><email>michael.pyne@kdemail.net</email></address>
+</affiliation>
+</author>
+
+<othercredit role="developer">
+<firstname>Scott</firstname>
+<surname>Wheeler</surname>
+<affiliation>
+<address>&Scott.Wheeler.mail;</address>
+</affiliation>
+</othercredit>
+
+<!-- TRANS:ROLES_OF_TRANSLATORS -->
+
+</authorgroup>
+
+<copyright>
+<year>2001</year>
+<year>2002</year>
+<year>2004</year>
+<holder>&Scott.Wheeler;</holder>
+</copyright>
+<legalnotice>&FDLNotice;</legalnotice>
+
+<date>2004-05-06</date>
+<releaseinfo>2.1</releaseinfo>
+
+<abstract>
+<para>
+&juk; is a jukebox, tagger and music collection manager.
+</para>
+</abstract>
+
+<keywordset>
+<keyword>KDE</keyword>
+<keyword>kdemultimedia</keyword>
+<keyword>audio</keyword>
+<keyword>tagger</keyword>
+<keyword>player</keyword>
+<keyword>jukebox</keyword>
+<keyword>JuK</keyword>
+</keywordset>
+
+</bookinfo>
+
+<chapter id="introduction">
+<title>Introduction</title>
+
+<para>
+&juk; is, well, a jukebox. As is typical with many jukebox
+applications, &juk; allows you to edit the <quote>tags</quote> of your
+audio files, and manage your collection and playlists.
+</para>
+</chapter>
+
+<chapter id="using-juk">
+<title>Using &juk;</title>
+
+<para>
+<screenshot>
+<screeninfo>Here's a screenshot of &juk;</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="juk-main.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Screenshot of &juk; in action.</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<para>&juk; maintains a list of all files that it knows about. This
+is called the <guilabel>Collection List</guilabel>. The collection
+list is specific to &juk; and is not shared with other
+applications.</para>
+
+<para>Independent of the Collection List, are playlists. You can have
+as many playlists as you want. You can use &juk; created playlists
+with other media players (such as &noatun; or
+<application>xmms</application>) and you can manage playlists created
+in those applications from within &juk;.</para>
+
+<para>You can add files to the Collection List individually, using
+<menuchoice><guimenu>File</guimenu><guimenuitem>Open
+File...</guimenuitem></menuchoice> and selecting them from a standard
+&kde; file dialog. You can add entire folders using
+<menuchoice><guimenu>File</guimenu><guimenuitem>Open
+Folder...</guimenuitem></menuchoice>. Folders added this way
+will be rescanned every time you start &juk;
+<!-- ask scott:
+ Asked 2004-04-27 (mpyne), Choosing Reload from the Collection
+ List context menu will result in a rescan. Also, a KDirWatch
+ watches the folders, but is apparently not recursive. -->
+. You can force the folders to be rescanned by right-clicking on the <guilabel>Collection List</guilabel>
+icon, and selecting <guimenuitem>Reload</guimenuitem>.</para>
+
+<para>Adding a song to a playlist will automatically add its file to
+the Collection List, but adding a file to the Collection List won't
+automatically add the song to any playlists.</para>
+
+<para>You can quickly create a playlist from your entire Collection
+List, by &RMB; clicking on the <guilabel>Collection List</guilabel>
+icon, and choosing <guimenuitem>Duplicate</guimenuitem>. The
+resulting playlist is a normal playlist, and editing it will not
+affect the <guilabel>Collection List</guilabel>.</para>
+
+<para>You can add playlist files created outside &juk; individually by
+selecting them with
+<menuchoice><guimenu>File</guimenu><guimenuitem>Open
+File...</guimenuitem></menuchoice>. Any playlist files found in
+folders you add with
+<menuchoice><guimenu>File</guimenu><guimenuitem>Open
+Folder...</guimenuitem></menuchoice> will also be added
+automatically.</para>
+
+<para>You can create a new playlist by choosing
+<menuchoice><guimenu>File</guimenu><guimenuitem>New</guimenuitem></menuchoice>
+or the <guiicon>New</guiicon> icon on the toolbar. You will be
+prompted for a name, and then an icon for that playlist will appear in
+the playlist pane. You can now drag and drop files from the
+Collection List, or from other playlists, to your playlist. Use the <guiicon>Save</guiicon>
+icon or
+<menuchoice><guimenu>File</guimenu><guimenuitem>Save</guimenuitem></menuchoice> to
+save the playlist at any time.</para>
+
+<sect1 id="collection-list-gui">
+<title>The Song List</title>
+
+<para>When you are viewing the Collection List, the main pane contains
+all the files that &juk; knows about. When you are viewing a
+playlist, only the songs that are in that playlist are shown. In
+either case, the appearance and behavior of the list is the
+same.</para>
+
+<para>Each song takes one row in the display. There is a column for
+each metadata field that &juk; tracks. These columns correspond to
+the fields available to edit in the tag editor.</para>
+
+<para>You can reorder the list at any time by &LMB; clicking on the
+column header. This will first sort the files in ascending order
+based on the contents of that column. If you &LMB; click again on the
+same header, the files will be re-sorted in descending order.</para>
+
+<para>The columns are initially sized wide enough to show the longest
+entry available. You can resize the columns by placing your mouse
+cursor on the divider between two columns. When the cursor changes
+from a pointer, &LMB; click and drag in the direction you want to
+resize the columns.</para>
+
+<para>You can reorder the columns by &LMB; clicking on a header and
+dragging the header to the left or right. You cannot drag past the
+edge of the window when doing this however, so you may need to scroll
+a little to the left or right, and repeat dragging the header, until
+you have placed it in your preferred position.</para>
+
+<para>You can hide or unhide columns by &RMB; clicking on a column header,
+and clicking on the name of the column to change.</para>
+
+<para>&LMB; double clicking on a file will play it with the built-in
+player. If another song was already playing, it will stop, and the
+new song will play.</para>
+
+<para>&RMB; clicking on a file offers you several options:</para>
+
+<variablelist>
+<varlistentry>
+<term><guimenuitem>Play Next</guimenuitem></term>
+<listitem>
+<para>This will start playing the file as soon as the current
+song is over. If no song is playing, the file will be played when
+you next hit the <guiicon>Play</guiicon> button. If you have already
+chosen the Play Next option on a different file, then this file will
+override that selection.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Cut</guimenuitem></term>
+<term><guimenuitem>Copy</guimenuitem></term>
+<term><guimenuitem>Paste</guimenuitem></term>
+<listitem>
+<para>...</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Clear</guimenuitem></term>
+<listitem>
+<para>If you are viewing the Collection List, choosing
+<guimenuitem>Clear</guimenuitem> will remove the file from the list,
+and will also remove all corresponding entries for this song from all
+playlists. You should note that if this file is in a folder that
+&juk; scans on startup, it will be readded to the Collection List the
+next time you start up &juk; but it won't be automatically added to
+any playlists.</para>
+<para>If you are viewing a playlist, <guimenuitem>Clear</guimenuitem>
+will simply remove the song from the playlist.</para>
+
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Edit</guimenuitem></term>
+<listitem>
+<para>Will allow you to edit the currently highlighted song, in the
+column you clicked in. For example, if you do not have the tag editor
+visible, and you are busy creating a playlist, but you notice a
+mis-spelling in an artist name, you can edit it directly with this
+menu item.</para>
+<para>Changes made in this manner are always saved immediately as soon
+as you click elsewhere and are finished editing.</para>
+<para>This menu item will be disabled if &juk; detects that the track you
+have selected is read-only.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Refresh Items</guimenuitem></term>
+<listitem>
+<para>This will reload the tag information of the selected files, in
+case the files have been changed while &juk; was running.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Remove From Disk</guimenuitem></term>
+<listitem>
+<para>This will remove the file from the Collection List, remove all
+entries for the song in all playlists, and delete the file from your
+disk. You cannot undo this, although you will be asked to confirm
+your choice. Use this with caution.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guisubmenu>Guess Tag Information</guisubmenu></term>
+<listitem>
+<para>This will make &juk; try to guess information such as the
+Artist and Title of a song. &juk; employs different methods of guessing:
+ <variablelist>
+
+ <varlistentry><term><guimenuitem>From File Name</guimenuitem></term>
+ <listitem><para>&juk; will try to guess the tags of the song based on its filename.
+ For example, a song name such as <filename>Roxette - You've Got the Look.mp3</filename>
+ would guess Roxette for the artist and You've Got the Look as the title. You
+ can adjust the way &juk; guesses for tags by selecting <menuchoice><guimenu>Settings
+ </guimenu><guimenuitem>Tag Guesser...</guimenuitem></menuchoice>, which will open the
+ <link linkend="juk-tag-guesser-configuration">Tag Guesser dialog</link>. &juk; will not
+ replace tags that it did not guess from the file name.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry><term><guimenuitem>From Internet</guimenuitem></term>
+ <listitem><para>&juk; will try to guess the tags of the song by using the
+ MusicBrainz program. You must have MusicBrainz installed for this command to work.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Rename File</guimenuitem></term>
+<listitem>
+<para>This will rename the selected files to conform to a given format. You must
+choose the way you want the files renamed first by selecting <menuchoice>
+<guimenu>Settings</guimenu><guimenuitem>File Renamer...</guimenuitem></menuchoice>.
+The resulting name of each file is based on its metadata tags. For example,
+the Ogg Vorbis song <quote>The Theme (Metroid)</quote> by Stemage could result in
+<filename>/usr/share/music/Stemage/The Theme (Metroid).ogg</filename>.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Create Playlist From Selected Items</guimenuitem></term>
+<listitem>
+<para>This allows you to quickly create a playlist from songs in your Collection List.
+This function will prompt you for a name for the new playlist, and will then insert all of
+the songs that are selected into the new playlist.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Add Selected Items to K3b Project</guimenuitem></term>
+<listitem>
+<para>This allows you to quickly create a K3b &CD;-burning project from your selected songs. &juk; will ask you if you would like an Audio &CD; project or a Data &CD; project, unless K3b already has a project open.</para>
+
+<para>If K3b is not already running, &juk; will start it up for you. After that, &juk; will add your selected files to the current K3b project. You can then save the project in K3b for burning later, or burn the &CD; right away.
+</para>
+
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+</sect1>
+
+<sect1 id="juk-playlists">
+<title>&juk; Playlists</title>
+<para>A playlist is simply a collection of songs grouped by some category. For example, you
+may have a playlist of songs that you listen to while coding, while trying to sleep, or even
+when you need a laugh. &juk; supports several different types of playlists.
+</para>
+
+<variablelist>
+
+<varlistentry>
+<term id="juk-normal-playlists">Normal playlists</term>
+<listitem><para><inlinemediaobject><imageobject><imagedata format="PNG" fileref="normal-playlist.png"/></imageobject></inlinemediaobject>
+This is the most common kind of playlist. It is a playlist composed of files, just
+like the Collection List.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term id="juk-history-playlists">The history playlist</term>
+<listitem><para><inlinemediaobject><imageobject><imagedata format="PNG" fileref="history-playlist.png"/></imageobject></inlinemediaobject>
+If you enable this playlist (by enabling <menuchoice><guimenu>View</guimenu><guimenuitem>Show History</guimenuitem></menuchoice>,
+this playlist will record every song that &juk; plays. The playlist will have an extra column, <guilabel>Time</guilabel>, which
+records the exact time and date the song played. The playlist doesn't start tracking the history until it is enabled, however.
+</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term id="juk-search-playlists">Search playlists</term>
+<listitem><para><inlinemediaobject><imageobject><imagedata format="PNG" fileref="search-playlist.png"/></imageobject></inlinemediaobject>
+This is a playlist which is based off of a search. You can create a playlist like this by clicking <menuchoice>
+<guimenu>File</guimenu><guisubmenu>New</guisubmenu><guimenuitem>Search Playlist</guimenuitem></menuchoice>, or by clicking on the
+<link linkend="juk-advanced-search-dialog">Advanced Search</link>
+ button on the <link linkend="search-bar">Search Bar</link>.</para>
+<para>After creating this playlist, it will keep track of which songs in the Collection List match your criteria, and automatically update itself accordingly whenever the
+Collection List changes.</para></listitem>
+</varlistentry>
+
+</variablelist>
+
+<para>Playlists are organized in the <guilabel>Playlist pane</guilabel>, which is the vertical bar at the left. In this pane is an icon
+for every playlist you have. There are different view mode for this pane, which can be selected from the
+<guimenu>View</guimenu><guisubmenu>View Modes</guisubmenu> menu.
+
+<variablelist>
+
+<varlistentry>
+<term id="juk-viewmode-default">Default View mode</term>
+<listitem>
+<para>
+This is the default view mode. In this mode, all the playlists are shown as large icons, one above the other in the view mode.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term id="juk-viewmode-compact">Compact View mode</term>
+<listitem>
+<para>
+This mode is similar to the Normal Viewmode, with the exception that the playlists are represented with horizontal bars with small icons instead
+of with square boxes.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term id="juk-viewmode-tree">Tree View mode</term>
+<listitem>
+<para>This mode is the most powerful. This mode is just like the Compact viewmode, except that the Collection List is now the root of a tree of virtual
+playlists. The Collection List has three children nodes, Artist, Album, and Genre. Each of these node has children representing all of the entries
+from that specific category. For example, if your Collection List contains music from 4 different artists, you would have 4 entries under the artist
+node.</para>
+<para>One nifty feature of the tree view mode is something called drag-and-drop retagging. Simply select some files in the track list, and drag them
+onto one of the artist, album, or genre nodes under Collection List. The songs will automatically be retagged to match the item you dropped the
+tracks on. For example, if you drag a group of tracks onto a Genre called "Rock", all of the tracks will be retagged will a Genre tag of Rock.
+</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+</para>
+
+</sect1>
+
+<sect1 id="juk-tagger">
+<title>The &juk; Tag Editor</title>
+
+<para>For many file formats, it is practical to use the filename to
+describe the contents of the file: <quote><filename>Report for the
+board - June 2003.doc</filename></quote> for example, may be all the
+information you need in order to find that file again. Trying to
+capture all the useful information about a particular song however,
+could lead to filenames like this: <quote><filename>Type O Negative -
+The Glorious Liberation Of The Peoples Technocratic Republic Of
+Vinnland By The Combined Forces Of The United Territories Of
+Europa.mp3</filename></quote> or <quote><filename>Various
+Artists_15_The Smithsonian Collection of Classic Jazz Volume II_Jimmie
+Lunceford &amp; His Orchestra - Organ Grinder's
+Swing.mp3</filename></quote>. These are neither very practical to
+use, nor do they contain all of the useful information that you might
+have collected about the song. Adding the album, and track number,
+for example, to the first would make it even longer and more
+unmanageable, while still not telling you at a glance the year it was
+released, or what style of music it is, if you're not familiar with
+the artist.</para>
+
+<para>The solution then, is to store this kind of metadata inside the
+files themselves. Mp3 and ogg files can also contain small snippets of
+text which you can use to describe the content of the file. There are
+several formats, but &juk; hides the details of the differences
+between them, and provides a standard way to edit a standard subset of
+well known tags for all your audio files.</para>
+
+<para>&juk;'s full featured tag editor allows you to edit the tags in
+both mp3 and ogg files. You can edit single files or multiple files,
+and you can select a mix of mp3 and ogg files to edit. The only
+requirement is that you have write access to the files themselves; you
+cannot edit the tags of a file that is mounted from a &CD-ROM; for
+example.</para>
+
+<sect2 id="tagging-a-single-file">
+<title>Editing the Tags in a Single File</title>
+
+<para>To edit the tag in a single file, select it in either the
+collection list or any entries it has in any playlist. If the tag
+editor is not visible, enable it by choosing
+<menuchoice><guimenu>View</guimenu><guimenuitem>Show Tag
+Editor</guimenuitem></menuchoice>. The tag editor displays in the
+bottom of the list view.</para>
+
+<para>Simply type into any of the editable fields to change the
+information. When you are done, &LMB; click back in the list, and you
+will be prompted to save your changes.</para>
+
+<para>You may find that the tag editor remains disabled when you've
+clicked on a file. This happens when &juk; has detected that the track
+is read-only.</para>
+
+<variablelist>
+<title>Tag Editor Fields</title>
+<varlistentry>
+<term><guilabel>Artist Name:</guilabel></term>
+<listitem>
+<para>The name of the Artist(s) who released the song.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Track name:</guilabel></term>
+<listitem>
+<para>The name of the song.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Album name:</guilabel></term>
+<listitem>
+<para>The name of the album the song was released on.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Genre:</guilabel></term>
+<listitem>
+<para>The <quote>Style</quote> of the music. &juk; provides a list
+corresponding roughly to the informal id3 standard, but you are free
+to type your own entries in this list.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>File name:</guilabel></term>
+<listitem>
+<para>The file name of the actual file on disk. You can edit this
+directly, and when you save, the file will be renamed.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Track:</guilabel></term>
+<listitem>
+<para>The position of the track on the original recording.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Year:</guilabel></term>
+<listitem>
+<para>The year the song was released.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Length:</guilabel></term>
+<listitem>
+<para>This is not editable, it is simply shown for information.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Bitrate:</guilabel></term>
+<listitem>
+<para>This is not editable, it is simply shown for information.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Comment:</guilabel></term>
+<listitem>
+<para>You can add your own free text comment here, with additional
+notes &etc;</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+<para>You can explicitly and immediately save your changes at any time
+using the
+<menuchoice><guimenu>Tagger</guimenu><guimenuitem>Save</guimenuitem></menuchoice>
+menu entry or by pressing
+<keycombo action="simul">&Ctrl;<keycap>T</keycap></keycombo>.</para>
+
+</sect2>
+
+<sect2 id="tagging-multiple-files">
+<title>Editing the Tags in Multiple Files</title>
+
+<para>You can select multiple files in the list view, and edit one or
+more fields in the tags for all files at once.</para>
+
+<para>Use <keycap>Shift</keycap> and the &LMB; to select a contiguous
+list of files, and &Ctrl; and &LMB; to select individual
+non-contiguous files.</para>
+
+<para>If the tag editor is not visible, you can enable it by choosing
+<menuchoice><guimenu>View</guimenu><guimenuitem>Show Tag
+Editor</guimenuitem></menuchoice>. The tag editor displays in the
+bottom of the list view.</para>
+
+<para>The tag editor behaves slightly differently when you have
+selected multiple files.</para>
+
+<para>Each field in the tag editor will now show an
+<guilabel>Enable</guilabel> check box next to it. Any field that has
+exactly the same contents for all the files you selected, displays
+that content, and is enabled for editing, with the
+<guilabel>Enable</guilabel> check box checked.</para>
+
+<!-- put screeny here -->
+
+<para>Any field that does not have matching contents in all selected
+files is not initially editable, and does not display any contents at
+all.</para>
+
+<para>To change the content of any field, check the
+<guilabel>Enable</guilabel> check box if it is not already checked, and
+edit the field as you normally would.</para>
+
+<para>When you are done, &LMB; click back in the list view and you
+will be prompted to save your changes. The prompt dialog will show
+you a list of the affected files, so you have a chance to check that
+you are indeed altering the files you intended to.</para>
+
+<para>You can explicitly and immediately save your changes at any time
+using the
+<menuchoice><guimenu>Tagger</guimenu><guimenuitem>Save</guimenuitem></menuchoice>
+menu entry or by pressing
+<keycombo action="simul">&Ctrl;<keycap>T</keycap></keycombo>.</para>
+
+</sect2>
+
+</sect1>
+
+<sect1 id="juk-rename-dialog">
+<title>The Rename File dialog</title>
+<para>
+<screenshot>
+<screeninfo>The Rename File dialog</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="juk-file-renamer.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Screenshot of the Rename File dialog.</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<para>The File Renamer Configuration dialog box is used to configure the
+Rename File action, which renames a song's based on the information contained
+within its metadata tags. First the tags are altered according to the different
+tokens you can alter, and then the tokens are used to generate the filename
+according to the <guilabel>Filename scheme</guilabel>.
+</para>
+
+<para>The most important part of the dialog is the <guilabel>Filename
+scheme</guilabel> section. You can type a file name scheme here which &juk;
+will use to rename the files. The way it works is that some characters are
+special.
+</para>
+
+<para><variablelist>
+<varlistentry>
+<term>%t</term>
+<listitem><para>This will be replaced with the Title token upon evaluation.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>%a</term>
+<listitem><para>This will be replaced with the Artist token upon evaluation.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>%A</term>
+<listitem><para>This will be replaced with the Album token upon evaluation.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>%T</term>
+<listitem><para>This will be replaced with the Track token upon evaluation.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>%c</term>
+<listitem><para>This will be replaced with the Comment token upon evaluation.</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+</para>
+
+<para>Every token can contain %s, which is replaced with the actual tag,
+and any text you want, including slashes (/). If a token has a slash, then
+that will indicate a folder separator. Of course, it would be possible to
+simply type folder separators in the <guilabel>Filename scheme</guilabel>
+line.</para>
+
+<para>Using the tokens, however, allows us to completely ignore tags that are empty.
+If you check the <guilabel>Need value</guilabel> check box, then the token will be
+ignored if the corresponding tag is empty. For example, you could use this to separate
+files with comments from those without by placing something such as
+<replaceable>has-comment/%s</replaceable> in the <guilabel>Comment token</guilabel>
+editor.</para>
+
+<para>You can test your filename scheme by using the <guilabel>Current filename</guilabel> editor
+at the bottom of the dialog. Type in a filename of a music file, and the <guilabel>New
+filename</guilabel> area will display would &juk; would rename the file as given the current
+settings.
+</para>
+</sect1>
+
+<sect1 id="juk-tag-guesser-configuration">
+<title>The Tag Guesser Configuration dialog</title>
+<para>
+<screenshot>
+<screeninfo>The Tag Guesser Configuration dialog</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="juk-tag-guesser.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Screenshot of the Tag Guesser Configuration dialog.</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<para>The Tag Guesser Configuration dialog is used to configure the
+Guess from Filename command.</para>
+
+<para>In the dialog you will see a list on the left of different filename scheme patterns.
+&juk; includes an extensive set of defaults patterns to match most common filenaming styles.
+If you'd like to add a new scheme, click on the <guibutton>Add</guibutton> button, and type
+in your scheme and click on <guibutton>OK</guibutton>. You may use the same percent
+tokens as defined in the <link linkend="juk-rename-dialog">Rename Dialog Configuration</link>.
+</para>
+
+<para>&juk; will try the schemes you have listed one at a time, starting at the top of the list.
+The first scheme which results in a match will be the scheme used to guess the song's tags.
+Some songs may match more than one scheme. You can make sure that the correct scheme matches
+first by selecting the scheme in the list box and then using the arrow buttons to move it to the
+top of the list.
+</para>
+
+<para>You can also edit or remove a scheme from the list. Just select the scheme in the list,
+and use the <guibutton>Modify</guibutton> button to change the scheme, or the
+<guibutton>Remove</guibutton> button to remove the scheme from the list.
+</para>
+</sect1>
+
+<sect1 id="juk-advanced-search-dialog">
+<title>The Advanced Search dialog</title>
+<para>
+<screenshot>
+<screeninfo>The Advanced Search dialog</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="juk-adv-search.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Screenshot of the Advanced Search Dialog.</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<para>The Advanced Search dialog is used to create <link linkend="juk-search-playlists">Search
+Playlists</link>. It allows you create a fine-grained search among the different tags of
+your song collection.</para>
+
+<para>At the top of the dialog, you can type in the name of your search playlist. Then, you
+can define your search criteria in the <guilabel>Search Criteria</guilabel> group.
+</para>
+
+<para>The top of the <guilabel>Search Criteria</guilabel> group has two radio buttons,
+<guilabel>Match any of the following</guilabel> and <guilabel>Match all of the
+following</guilabel>. If you select <guilabel>Match any of the following</guilabel>, then
+a match by any of the conditions you define will include the song in the playlist. Otherwise,
+every condition you define must match in order to include the song in the playlist.
+</para>
+
+<para>Below the radio buttons are the condition definitions. You can add more conditions
+by using the <guibutton>More</guibutton> button, and remove conditions by using the
+<guibutton>Fewer</guibutton> button. Any conditions you leave blank are ignored, so you do
+not have to use <guibutton>Fewer</guibutton> to eliminate empty conditions.
+</para>
+
+<para>Every condition definition has three parts: The tag chooser list on the left, the
+matching style list on the right, and the search text in the middle. The tag chooser
+lets &juk; know what tag you want to search for the text in. If you choose the special
+tag "&lt;All Visible&gt;", then any tag that you can see in the Collection List listing
+is fair game to match the search text.
+</para>
+
+<para>The match style list lets &juk; know which search method to use.
+The search methods you can use are as follows:
+<variablelist>
+<varlistentry>
+<term>Normal Matching</term>
+<listitem><para>This is the default matching style. It searches for the given
+text anywhere in the chosen tag, ignoring case differences. For example a
+search for mode in the Artist tag would match Depeche Mode.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term>Case Sensitive</term>
+<listitem><para>This search is just like Normal Matching, except that
+the search must match the exact case of the text.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term>Pattern Matching</term>
+<listitem><para>This is the most powerful search method. The search
+text you type in will define a regular expression used to search within
+the tag. Regular expressions are beyond the scope of this documentation, but
+the application &kregexpeditor; can help you form a regular expression.
+&juk; uses the &Qt; regular expression style.</para></listitem>
+</varlistentry>
+
+</variablelist>
+
+</para>
+
+<para>Simply choose the conditions you want to include in your search, and
+click <guibutton>OK</guibutton> to create your search playlist!
+</para>
+</sect1>
+
+<sect1 id="juk-cover-manager">
+
+<title>The &juk; Cover Manager</title>
+
+<para>&juk; 2.3 (part of &kde; 3.5) includes improved cover management code which introduces some new possibilities for users compared with &juk; 2.2 (which was shipped with &kde; 3.4). It also can change the workflow slightly for you if you are used to the way covers were handled in &juk; 2.2. So first, let's review how things used to be.</para>
+
+<sect2 id="covers-in-juk-2.2">
+<title>How Covers Worked in &juk; 2.2</title>
+
+<para>In &juk; 2.2, the cover for a track was strictly tied to its <guilabel>Artist</guilabel> and <guilabel>Album</guilabel> information. Although this proved useful enough, and had a few advantages, it wasn't a great way to organize the covers. If you wanted to use a cover for a different track, you either had to rename the tags in the track, or you had to duplicate the cover, wasting hard disk space. And if your track had no <guilabel>Artist</guilabel> or <guilabel>Album</guilabel> information, &juk; would prevent you from setting a cover since it had no information to go by. It worked, but it could be better.</para>
+
+</sect2>
+
+<sect2 id="covers-in-juk-2.3">
+
+<title>How Covers work in &juk; 2.3</title>
+
+<para>In &juk; 2.3, the code was redesigned to add a core component responsible for dealing with cover art. Instead of looking on disk for a picture file with a specific name like &juk; 2.2, the Cover Manager in &juk; 2.3 associates every cover with an identification tag, and then uses the tag with your music. It's still not perfect, but it works, and it can save you time while allowing you to do more.</para>
+
+<sect3 id="examples-adding-covers">
+
+<title>Examples of adding covers</title>
+
+<para>So just as an example, let's say you wanted to set a cover for tracks you just ripped off of your &CD;. We'll use <quote>Alabama - Greatest Hits III</quote> for the sake of discussion. In &juk; 2.2, you could simply select any one of those tracks, and import a cover from the Internet by right-clicking on that track, and using the <menuchoice><guimenu>Tagger</guimenu> <guisubmenu>Cover Manager</guisubmenu> <guimenuitem>Get Cover From Internet</guimenuitem></menuchoice> command. As a side effect of the way &juk; worked, the cover would then be immediately applied to <emphasis>all</emphasis> of the <quote>Alabama - Greatest Hits III</quote> tracks, <emphasis>whether you wanted that or not</emphasis></para>
+
+<para>In &juk; 2.3, the procedure is exactly the same, with one exception: You should select all of the tracks you want to apply the cover to first. So you would select all the <quote>Alabama - Greatest Hits III</quote> tracks before using the <menuchoice><guimenuitem>Get Cover From Internet</guimenuitem></menuchoice> command. Or if you only wanted to set cover art to half of the tracks for some reason, you'd only select half the tracks before running the <menuchoice><guimenuitem>Get Cover From Internet</guimenuitem></menuchoice> command. Don't worry about duplicating covers, either: &juk; is smart enough to re-use the same image, so you won't get 14 duplicate <literal role="extension">.png</literal> images cluttering your hard drive.</para>
+</sect3>
+
+<sect3 id="reusing-old-covers">
+
+<title>Reusing Old Covers</title>
+
+<para>But what happens if you forgot to select all the tracks you wanted to tag? You could select them and repeat the process, but that would leave a duplicate cover on your hard drive because &juk; cannot quickly tell that the cover you've found is the same as one you already have. But that's alright, because you can tell &juk; to use the cover from another track.</para>
+
+<para>There are two ways of doing this:</para>
+
+<para>1. Open the <guilabel>Cover Manager</guilabel> dialog using the <guimenu>Tagger</guimenu> menu (<menuchoice><guimenu>Tagger Cover</guimenu> <guisubmenu>Manager</guisubmenu> <guimenuitem>Show Cover Manager</guimenuitem></menuchoice>). The Cover Manager will display a list of all the covers &juk; knows about on the right, and after they have loaded you can quickly pare the list down using the search line at the top, or by using the list of Artists on the left. Once you see the cover you want to use, you can drag-and-drop the cover onto a track to apply it. It should happen nearly instantaneously since &juk; is reusing the same cover (and you'll see the cover while you're dragging it as well). Unfortunately, it can take awhile to load the covers in the first place, and the Cover Manager isn't really useful for much else besides.</para>
+
+<para>2. I prefer to use this method because it's rather easy. All you do is double-click on the track that has the cover you want, in order to start it playing. This will cause its cover to show up in the <interface>Now Playing bar</interface>, and you can drag-and-drop the cover to the track you want to change exactly as you would for the Cover Manager.</para>
+
+</sect3>
+
+<sect3 id="dragging-covers">
+
+<title>Dragging covers to more than one track at once</title>
+
+<para>Also note that you can use drag-and-drop to quickly apply covers to more than one track. Just select the tracks you want to apply a cover to, and drag the cover onto any one of the selected tracks.</para>
+</sect3>
+
+<sect3 id="old-covers">
+
+<title>What happens to my old covers?</title>
+
+<para>You may be wondering what &juk; will do if you already have covers from &juk; 2.2. What happens is that &juk; will automatically convert the old covers and merge them into the cover management system.</para>
+
+<para>Because this is a time consuming process, it does not happen all at once. Instead, the old cover is only converted when the cover needs to be shown on screen. As the conversion process is happening, &juk; will recognize what tracks would have shown the cover being converted, and will automatically apply the new cover to those tracks. The end result is that there should be no visible changes: &juk; will keep the same cover on your tracks that they've always had, except that now you can immediately take advantage of the new cover management features.</para>
+
+</sect3>
+<sect3 id="removing-covers">
+
+<title>Removing Covers</title>
+
+<para>Another side effect is that you can now remove a cover from a track without simultaneously removing it from all other tracks with the same <guilabel>Artist</guilabel> and <guilabel>Album</guilabel>.</para>
+
+<para>In &juk; 2.3, the Remove Cover command now only removes the covers from the selected tracks.
+</para>
+
+</sect3>
+<sect3 id="suggested-use">
+
+<title>Suggested Uses:</title>
+
+<para>1. You can now apply the same cover to tracks with Albums that have <quote>Disc 1</quote>, <quote>Disc 2</quote>, etc, which you couldn't do in &juk; 2.2 without duplicating the cover.</para>
+
+<para>
+2. Applying a generic cover to tracks if you simply must have a cover on every track, or if you have music that wasn't released as an album but fits a genre well. You could make yourself a cover for that type of music and apply it to the songs in question.</para>
+
+</sect3>
+</sect2>
+</sect1>
+
+</chapter>
+
+<chapter id="toolbar-reference">
+<title>The &juk; Toolbar</title>
+
+<sect1 id="main-toolbar">
+<title>The Main Toolbar</title>
+
+<screenshot>
+<screeninfo>The &juk; toolbar.</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata format="PNG" fileref="toolbar.png" />
+</imageobject>
+<textobject>
+<phrase>The &juk; toolbar.</phrase>
+</textobject>
+<caption><para>The &juk; toolbar.</para></caption>
+</mediaobject>
+</screenshot>
+
+<para>From left to right in the screenshot above, the icons on the
+default toolbar are:</para>
+
+<variablelist>
+<varlistentry>
+<term><guiicon>New</guiicon></term>
+<listitem><para>Create a new playlist. If you hold down the button, a
+menu will pop up allowing you to select the different kinds of playlists
+to create.
+</para>
+ <variablelist>
+ <varlistentry><term><guimenuitem>Empty Playlist...</guimenuitem></term>
+ <listitem><para>This prompts you for a playlist name, and then inserts it into the
+ Playlist view. The playlist starts out completely empty.</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term><guimenuitem>Playlist From Folder...</guimenuitem></term>
+ <listitem><para>This prompts you for a folder to open, and then creates a playlist
+ containing the music within the folder and any sub-folders. The name of the created
+ playlist is the same as the name of the selected folder.</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term><guimenuitem>Search Playlist...</guimenuitem></term>
+ <listitem><para>This brings up the Advanced Search Dialog, allowing you to create a
+ <quote>virtual playlist</quote>. Any songs in your Collection List that match the search
+ criteria that you specify in the Advanced Search Dialog will be added to the new playlist.
+ As your Collection List changes, the new playlist will as well. For example, if you create a playlist
+ of all of your Depeche Mode songs, and then add another Depeche Mode song to your Collection List,
+ it will show up in the Depeche Mode playlist with no special action required on your part.
+ </para></listitem>
+ </varlistentry>
+
+ </variablelist>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guiicon>Open</guiicon></term>
+<listitem><para>Add a file to the collection list (if it's active) or
+to the currently selected playlist. Adding a file to a playlist will
+add it to the collection list automatically, but not vice
+versa.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guiicon>Save</guiicon></term>
+<listitem><para>Save the currently selected playlist. To save a tag
+you have edited, either select another item, or press <keycombo
+action="simul">&Ctrl;<keycap>T</keycap></keycombo>
+instead.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guiicon>Cut</guiicon></term>
+<listitem><para>If a playlist or song is selected, cut (remove) it from the
+list. If the tag editor is active, this works like cut in any editor,
+removing the selected text, but keeping a copy on the
+clipboard.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guiicon>Copy</guiicon></term>
+<listitem><para>If the tag editor is active, this works like copy in
+any editor, placing a copy of the selected text on the
+clipboard.</para>
+<para>If you use copy on a song in the collection list, the url is
+placed on the clipboard, so you can paste it. For example, you could
+paste the url into a text editor, &konqueror;, or another
+playlist.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guiicon>Paste</guiicon></term>
+<listitem><para>If you previously either cut or copied a url from the
+collection list, you can paste the url back into a new playlist. You
+could also paste a url you have copied from &konqueror; or any other
+application. If you are operating in the tag editor, paste will paste
+any text currently on the clipboard into the selected
+field.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guiicon>Show Search Bar</guiicon></term>
+<listitem><para>Show or hide <link linkend="search-bar">the search
+bar</link>.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guiicon>Show Tag Editor</guiicon></term>
+<listitem><para>Show or hide <link linkend="tagging-a-single-file">the
+tag editor</link>.</para></listitem>
+</varlistentry>
+
+<varlistentry id="play-toolbar">
+<term>Play controls</term>
+<listitem>
+<para>These work like any standard media player you may have come
+across. The controls are <guiicon>Play</guiicon>,
+<guiicon>Pause</guiicon>, <guiicon>Stop</guiicon>, <guiicon>Skip to
+the previous song</guiicon> and <guiicon>Skip to the next
+song</guiicon>.</para>
+<para>There is also a tracking bar, showing how far along (relatively)
+in the current song you are. You can drag this slider with the mouse
+in order to skip forwards or backwards within a track.</para>
+<para>Finally there is a volume slider. As you may expect, this
+raises and lowers the volume. <quote>Loud</quote> is on the right,
+and <quote>Quiet</quote> is on the left.</para></listitem>
+</varlistentry>
+</variablelist>
+
+</sect1>
+
+<sect1 id="search-bar">
+<title>The Search bar</title>
+
+<para>The search bar allows you to quickly search for a song in the
+collection list or a playlist.</para>
+
+<para>Simply typing text into the search bar will reduce the visible
+list of songs to those which contain that text in any visible
+column. Pressing <keycap>Enter</keycap> will start playing the top match in the playlist view.</para>
+
+<para>Searching begins instantly when text is entered into the search
+field. Searching is incremental, that is, as you type each character
+into the text field, the search is narrowed further. This is useful
+to find a song where you only remember part of a name, for
+instance.</para>
+
+<para>If you would like to make a more fine-grained search, you can click
+the Advanced Search button to the right of the search bar, which will allow
+you to create a virtual playlist. If you would like to cancel the search,
+you can simply click on the Clear button to the left of the search bar.</para>
+
+</sect1>
+
+</chapter>
+
+<chapter id="menu-and-command-reference">
+<title>Menu and Command Reference</title>
+
+<sect1 id="menus">
+<title>Menus</title>
+
+<sect2 id="menu-file">
+<title><guimenu>File</guimenu> Menu</title>
+
+<variablelist>
+<varlistentry>
+<term><menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;<keycap>N</keycap></keycombo>
+</shortcut>
+<guimenu>File</guimenu><guisubmenu>New</guisubmenu><guimenuitem>Empty Playlist...</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>Create a new playlist</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;<keycap>D</keycap></keycombo>
+</shortcut>
+<guimenu>File</guimenu><guisubmenu>New</guisubmenu><guimenuitem>Playlist From
+Folder...</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>Creates a new playlist, containing all music files
+in a folder and any sub-folders. Any music within playlists files that
+&juk; recognizes will also be added.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;<keycap>F</keycap></keycombo>
+</shortcut>
+<guimenu>File</guimenu><guisubmenu>New</guisubmenu><guimenuitem>Search Playlist...</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>Creates a new <link linkend="juk-search-playlists">search playlist</link>.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<shortcut>
+<keycombo action="simul">&Ctrl;<keycap>O</keycap></keycombo>
+</shortcut>
+<guimenu>File</guimenu>
+<guimenuitem>Open...</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>Select a file (or files) to add to the collection list. If you select
+a playlist file, every file in the playlist will be added.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>File</guimenu><guimenuitem>Add Folder...</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>Select a folder (or folders) to add to the
+collection list. These folders will also be rescanned whenever
+&juk; is started or
+<menuchoice><guimenu>File</guimenu><guimenuitem>Reload</guimenuitem>
+</menuchoice> is chosen.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>File</guimenu>
+<guimenuitem>Rename...</guimenuitem></menuchoice>
+</term>
+<listitem><para>Rename a playlist.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>File</guimenu><guimenuitem>Duplicate...</guimenuitem></menuchoice>
+</term>
+<listitem><para>Create a duplicate of the selected playlist, and
+prompt for a new name.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>File</guimenu><guimenuitem>Reload</guimenuitem></menuchoice>
+</term>
+<listitem><para>Reloads the tag information on every file in the selected
+playlist.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice><guimenu>File</guimenu><guimenuitem>Remove</guimenuitem></menuchoice></term>
+<listitem><para>Remove the selected playlist.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;<keycap>S</keycap></keycombo>
+</shortcut>
+<guimenu>File</guimenu><guimenuitem>Save</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>Save the selected playlist.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>File</guimenu><guimenuitem>Save As...</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>Save the selected playlist, with a different name.</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+</sect2>
+
+<sect2 id="menu-edit">
+<title><guimenu>Edit</guimenu> Menu</title>
+
+<variablelist>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Edit</guimenu><guimenuitem>Clear</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>Removes the selected songs from the playlist.</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+</sect2>
+
+<sect2 id="menu-view">
+<title><guimenu>View</guimenu> Menu</title>
+
+<variablelist>
+<varlistentry>
+<term><menuchoice>
+<guimenu>View</guimenu><guimenuitem>Show Search Bar</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This is a toggle action that sets whether or not the
+<link linkend="search-bar">Search Bar</link> is shown.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>View</guimenu><guimenuitem>Show Tag Editor</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This is a toggle action that sets whether or not the
+<interface>Tag Editor</interface> is shown.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>View</guimenu><guimenuitem>Show History</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This is a toggle action that sets whether or not the
+<link linkend="juk-history-playlists">History Playlist</link> is shown.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>View</guimenu><guisubmenu>View Modes</guisubmenu><guimenuitem>Default</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>Switches to <link linkend="juk-viewmode-default">Default View mode</link>.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>View</guimenu><guisubmenu>View Modes</guisubmenu><guimenuitem>Compact</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>Switches to <link linkend="juk-viewmode-compact">Compact View mode</link>.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>View</guimenu><guisubmenu>View Modes</guisubmenu><guimenuitem>Tree</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>Switches to <link linkend="juk-viewmode-tree">Tree View mode</link>.
+</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+</sect2>
+
+<sect2 id="menu-player">
+<title><guimenu>Player</guimenu> Menu</title>
+
+<variablelist>
+<varlistentry>
+<term><menuchoice>
+<guimenu>Player</guimenu><guimenuitem>Random Play</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This is a toggle option which controls the Random Play setting.
+If Random Play is enabled, then &juk; will randomly select a random song from the
+current playlist when the currently playing song is over.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Player</guimenu><guimenuitem>Loop Playlist</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This is a toggle option which controls the Loop Playlist setting.
+If Loop Playlist is enabled, then &juk; will start playing from the beginning when
+it has finished playing every song in the current playlist.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Player</guimenu><guimenuitem>Play</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This command starts playing the currently selected song, or resumes
+playback of the song if it was paused.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Player</guimenu><guimenuitem>Pause</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This command pauses the currently playing song. Use the
+Play command to restart playback.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Player</guimenu><guimenuitem>Stop</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This command stops the playback of the currently playing song.
+You cannot resume playback from its current position after that.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Player</guimenu><guimenuitem>Previous Track</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This command plays the song that was playing before
+the currently playing song.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Player</guimenu><guimenuitem>Next Track</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This command skips to the next song to play in the
+playlist.</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+</sect2>
+
+<sect2 id="menu-tagger">
+<title><guimenu>Tagger</guimenu> Menu</title>
+
+<variablelist>
+
+<varlistentry>
+<term><menuchoice>
+<shortcut><keycombo action="simul"><keycap>Ctrl</keycap><keycap>T</keycap></keycombo></shortcut>
+<guimenu>Tagger</guimenu><guimenuitem>Save</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This command saves any changes to the tags that you are
+editing. Normally, changes are not saved until you deselect the file you
+are editing.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Tagger</guimenu><guimenuitem>Delete</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This command deletes the currently selected files from
+the Collection List and any playlists containing it, and then deletes
+the selected file from the disk.
+<!-- God this is a dumb place to put this particular command --></para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<shortcut><keycombo action="simul"><keycap>Ctrl</keycap><keycap>F</keycap></keycombo></shortcut>
+<guimenu>Tagger</guimenu><guisubmenu>Guess Tag
+Information</guisubmenu><guimenuitem>From Filename</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This command tries to guess the tags of the selected files
+by scanning the filename. You can configure the patterns used for guessing
+by selecting <menuchoice><guimenu>Settings</guimenu>
+<guimenuitem>Tag Guesser...</guimenuitem></menuchoice>, which opens the
+<link linkend="juk-tag-guesser-configuration">Tag Guesser Configuration dialog</link>.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<shortcut><keycombo action="simul"><keycap>Ctrl</keycap><keycap>I</keycap></keycombo></shortcut>
+<guimenu>Tagger</guimenu><guisubmenu>Guess Tag
+Information</guisubmenu><guimenuitem>From Internet</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This command tries to guess the tags of the selected files
+by using the <application>trm</application> provided with
+<ulink url="http://www.musicbrainz.org/">MusicBrainz</ulink>.</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+</sect2>
+
+<sect2 id="menu-settings">
+<title><guimenu>Settings</guimenu> Menu</title>
+
+<variablelist>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu><guisubmenu>Toolbars</guisubmenu>
+<guimenuitem>Show Main Toolbar</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This command shows or hide the <link linkend="main-toolbar">Main
+Toolbar</link>.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu><guisubmenu>Toolbars</guisubmenu>
+<guimenuitem>Show Play Toolbar</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This command shows or hide the <link linkend="play-toolbar">Play
+Toolbar</link>.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Show Splash Screen on Startup</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This is a toggle option. If enabled, &juk; will display
+an informational screen upon startup as it loads your music collection.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Dock in System Tray</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This is a toggle option. If enabled, &juk; will display an
+icon in your system tray. You can use the system
+tray icon to tell if &juk; is playing, and control playback.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Stay in System Tray on Close</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This is a toggle option. If enabled, &juk; will remain
+running if you close the main window. The Dock in System Tray option must
+also be enabled. To quit &juk;, use the <menuchoice><guimenu>File</guimenu>
+<guimenuitem>Quit</guimenuitem></menuchoice> command from the main window, or
+the <guimenuitem>Quit</guimenuitem> command from the system tray's context
+menu.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Popup Track Announcement</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This is a toggle option. If enabled, &juk; will display
+an indicator whenever a song starts playing, with information on the artist and
+title, and with buttons allowing you to quickly switch to a different song. The
+Dock in System Tray option must also be enabled.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Tag Guesser...</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This command brings up the <link linkend="juk-tag-guesser-configuration">Tag Guesser Configuration
+dialog box</link>, where you can alter the patterns used to guess tag information
+from filenames.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>File Renamer...</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This command brings up the <link linkend="juk-rename-dialog">File Renamer Configuration
+dialog box</link>, where you can alter the way &juk; renames files for you.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Configure Shortcuts...</guimenuitem>
+</menuchoice>
+</term>
+<listitem><para>This brings up the standard &kde; dialog box where you can configure
+keyboard shortcuts for &juk;. Some reasonable defaults are included as well, including
+Multimedia keys for people who have multimedia keyboards.</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+</sect2>
+
+</sect1>
+
+<sect1 id="keybindings">
+<title>Keybinding Reference</title>
+
+<!--
+ctrl-a select all
+ctrl-c copy
+ctrl-r rename file
+ctrl-i guess tag entries from internet
+ctrl-f guess tag entries based on filename
+ctrl-f new search playlist
+ctrl-n new empty playlist
+ctrl-d new playlist from folder
+ctrl-o open file (add to the collection or a playlist)
+ctrl-q quit
+ctrl-s save
+ctrl-t save edited tag information
+ctrl-v paste
+ctrl-x cut
+f1 Show manual
+shift-f1 what's this help
+
+-->
+<informaltable>
+<tgroup cols="2">
+<thead>
+<row>
+<entry>Key Combination</entry>
+<entry>Action</entry>
+</row>
+</thead>
+<tbody>
+<row>
+<entry><keycombo
+action="simul">&Ctrl;<keycap>A</keycap></keycombo></entry>
+<entry>Select all</entry>
+</row>
+<row>
+<entry><keycombo action="simul">&Ctrl;<keycap>C</keycap></keycombo></entry>
+<entry>Copy</entry>
+</row>
+<row>
+<entry><keycombo
+action="simul">&Ctrl;<keycap>R</keycap></keycombo></entry>
+<entry>Rename file</entry>
+</row>
+<row>
+<entry><keycombo
+action="simul">&Ctrl;<keycap>I</keycap></keycombo></entry>
+<entry>Guess tags from the Internet.</entry>
+</row>
+<row>
+<entry><keycombo
+action="simul">&Ctrl;<keycap>G</keycap></keycombo></entry>
+<entry>Guess tags from the filename.</entry>
+</row>
+<row>
+<entry><keycombo
+action="simul">&Ctrl;<keycap>F</keycap></keycombo></entry>
+<entry>New <link linkend="juk-search-playlists">search playlist</link>.</entry>
+</row>
+<row>
+<entry><keycombo action="simul">&Ctrl;<keycap>G</keycap></keycombo></entry>
+<entry>Guess tag entries based on filename</entry>
+</row>
+<row>
+<entry><keycombo
+action="simul">&Ctrl;<keycap>N</keycap></keycombo></entry>
+<entry>New empty Playlist</entry>
+</row>
+<row>
+<entry><keycombo
+action="simul">&Ctrl;<keycap>D</keycap></keycombo></entry>
+<entry>New playlist from folder.</entry>
+</row>
+<row>
+<entry><keycombo
+action="simul">&Ctrl;<keycap>T</keycap></keycombo></entry>
+<entry>Save changes to edited tags.</entry>
+</row>
+</tbody>
+</tgroup>
+</informaltable>
+
+</sect1>
+</chapter>
+
+<chapter id="credits-and-licenses">
+<title>Credits and Licenses</title>
+
+<para>&juk; Copyright &copy; 2002, 2003, 2004 &Scott.Wheeler;.</para>
+
+<para>&juk; is developed and maintained by &Scott.Wheeler;
+&Scott.Wheeler.mail;.</para>
+
+<para>Many thanks to the following people who have contributed to &juk;:</para>
+
+<itemizedlist>
+<listitem><para>&Daniel.Molkentin; &Daniel.Molkentin.mail; for system tray docking, <quote>inline</quote> tag
+editing, bug fixes, evangelism, moral support.</para>
+</listitem>
+<listitem><para>Tim Jansen <email>tim@tjansen.de</email> for
+the <application>GStreamer</application> port</para>
+</listitem>
+
+<listitem><para>Stefan Asserh&auml;ll <email>stefan.asserhall@telia.com</email>
+for global shortcut support.</para>
+</listitem>
+
+<listitem><para>Stephen Douglas <email>stephen_douglas@yahoo.com</email>
+for track announcement popups.</para>
+</listitem>
+
+<listitem><para>&Frerich.Raabe; &Frerich.Raabe.mail;
+for automagical track data guessing, and bugfixes.</para>
+</listitem>
+
+<listitem><para>Zack Rusin <email>zack@kde.org</email>
+for more automagical things, including MusicBrainz support.</para>
+</listitem>
+
+<listitem><para>Adam Treat <email>manyoso@yahoo.com</email>
+for co-conspiring in MusicBrainz wizardry.</para>
+</listitem>
+
+<listitem><para>Matthias Kretz <email>kretz@kde.org</email>
+for being the friendly neighborhood &arts; guru.</para>
+</listitem>
+
+<listitem><para>Maks Orlovich <email>maksim@kde.org</email>
+for making &juk; friendlier to people with terabytes of music.</para>
+</listitem>
+
+<listitem><para>Antonio Larrosa Jimenez <email>larrosa@kde.org</email>
+for the &DCOP; interface.</para>
+</listitem>
+
+</itemizedlist>
+
+<para>Documentation Copyright &copy; 2003, &Lauri.Watts;, and copyright
+&copy; 2004 Michael Pyne.</para>
+
+<!-- TRANS:CREDIT_FOR_TRANSLATORS -->
+
+&underFDL;
+&underGPL;
+
+</chapter>
+
+&documentation.index;
+
+</book>
+<!--
+Local Variables:
+mode: sgml
+sgml-minimize-attributes:nil
+sgml-general-insert-case:lower
+sgml-indent-step:0
+sgml-indent-data:nil
+End:
+-->
diff --git a/doc/juk/juk-adv-search.png b/doc/juk/juk-adv-search.png
new file mode 100644
index 00000000..240ffa10
--- /dev/null
+++ b/doc/juk/juk-adv-search.png
Binary files differ
diff --git a/doc/juk/juk-file-renamer.png b/doc/juk/juk-file-renamer.png
new file mode 100644
index 00000000..bc2bd702
--- /dev/null
+++ b/doc/juk/juk-file-renamer.png
Binary files differ
diff --git a/doc/juk/juk-main.png b/doc/juk/juk-main.png
new file mode 100644
index 00000000..b4bb08eb
--- /dev/null
+++ b/doc/juk/juk-main.png
Binary files differ
diff --git a/doc/juk/juk-tag-guesser.png b/doc/juk/juk-tag-guesser.png
new file mode 100644
index 00000000..aa0a81ee
--- /dev/null
+++ b/doc/juk/juk-tag-guesser.png
Binary files differ
diff --git a/doc/juk/normal-playlist.png b/doc/juk/normal-playlist.png
new file mode 100644
index 00000000..21725a72
--- /dev/null
+++ b/doc/juk/normal-playlist.png
Binary files differ
diff --git a/doc/juk/search-playlist.png b/doc/juk/search-playlist.png
new file mode 100644
index 00000000..e9453eae
--- /dev/null
+++ b/doc/juk/search-playlist.png
Binary files differ
diff --git a/doc/juk/toolbar.png b/doc/juk/toolbar.png
new file mode 100644
index 00000000..cb656b23
--- /dev/null
+++ b/doc/juk/toolbar.png
Binary files differ
diff --git a/doc/kaboodle/Makefile.am b/doc/kaboodle/Makefile.am
new file mode 100644
index 00000000..085981d9
--- /dev/null
+++ b/doc/kaboodle/Makefile.am
@@ -0,0 +1,4 @@
+
+KDE_LANG = en
+KDE_DOCS = AUTO
+
diff --git a/doc/kaboodle/index.docbook b/doc/kaboodle/index.docbook
new file mode 100644
index 00000000..6df20400
--- /dev/null
+++ b/doc/kaboodle/index.docbook
@@ -0,0 +1,78 @@
+<?xml version="1.0" ?>
+<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
+ <!ENTITY kappname "<application>kaboodle</application>">
+ <!ENTITY package "kdemultimedia">
+ <!ENTITY % addindex "IGNORE">
+ <!ENTITY % English "INCLUDE">
+]>
+
+<book lang="&language;">
+
+<bookinfo>
+<title>The &kappname; Handbook</title>
+
+<authorgroup>
+<author>
+<firstname></firstname>
+<othername></othername>
+<surname></surname>
+<affiliation>
+<address><email></email></address>
+</affiliation>
+</author>
+<!-- TRANS:ROLES_OF_TRANSLATORS -->
+</authorgroup>
+
+<legalnotice>&FDLNotice;</legalnotice>
+
+<!-- Date and version information of the documentation
+Don't forget to include this last date and this last revision number, we
+need them for translation coordination !
+Please respect the format of the date (YYYY-MM-DD) and of the version
+(Major.minor.lesser), it could be used by automation scripts -->
+
+<date>2000-09-02</date>
+<releaseinfo>0.00.00</releaseinfo>
+
+<!-- Abstract about this handbook -->
+
+<abstract>
+<para>
+&kaboodle; is a one file quick media player for &kde;
+</para>
+</abstract>
+
+
+<keywordset>
+<keyword>KDE</keyword>
+<keyword>Kapp</keyword>
+</keywordset>
+
+</bookinfo>
+
+<chapter id="introduction"> <title>Introduction</title> <para>Sorry, but
+the documentation for &kappname; was not finished when &kde; was installed on
+this computer.</para> <para>If you need help, please check <ulink
+url="http://www.kde.org">The &kde; Website</ulink> for updates, or by
+submitting your question to <ulink url="mailto:kde@kde.org">The
+&kde; User Mailing list</ulink>.</para> <para><emphasis>The &kde;
+Team</emphasis></para>
+
+&underFDL;
+
+</chapter>
+
+&documentation.index;
+</book>
+
+<!--
+Local Variables:
+mode: sgml
+sgml-minimize-attributes:nil
+sgml-general-insert-case:lower
+sgml-indent-step:0
+sgml-indent-data:nil
+End:
+
+// vim:ts=2:sw=2:tw=78:noet
+-->
diff --git a/doc/kaudiocreator/Makefile.am b/doc/kaudiocreator/Makefile.am
new file mode 100644
index 00000000..31d09cd4
--- /dev/null
+++ b/doc/kaudiocreator/Makefile.am
@@ -0,0 +1,2 @@
+KDE_DOCS = kaudiocreator
+KDE_LANG = en
diff --git a/doc/kaudiocreator/cdconfiguration.png b/doc/kaudiocreator/cdconfiguration.png
new file mode 100644
index 00000000..9ef499a9
--- /dev/null
+++ b/doc/kaudiocreator/cdconfiguration.png
Binary files differ
diff --git a/doc/kaudiocreator/cddbconfigurationlookup.png b/doc/kaudiocreator/cddbconfigurationlookup.png
new file mode 100644
index 00000000..38d2e56c
--- /dev/null
+++ b/doc/kaudiocreator/cddbconfigurationlookup.png
Binary files differ
diff --git a/doc/kaudiocreator/cddbconfigurationsubmit.png b/doc/kaudiocreator/cddbconfigurationsubmit.png
new file mode 100644
index 00000000..0b12bde3
--- /dev/null
+++ b/doc/kaudiocreator/cddbconfigurationsubmit.png
Binary files differ
diff --git a/doc/kaudiocreator/cdinserted.png b/doc/kaudiocreator/cdinserted.png
new file mode 100644
index 00000000..c6d5120b
--- /dev/null
+++ b/doc/kaudiocreator/cdinserted.png
Binary files differ
diff --git a/doc/kaudiocreator/confirmartistcarryover.png b/doc/kaudiocreator/confirmartistcarryover.png
new file mode 100644
index 00000000..84eb1922
--- /dev/null
+++ b/doc/kaudiocreator/confirmartistcarryover.png
Binary files differ
diff --git a/doc/kaudiocreator/encoderconfiguration.png b/doc/kaudiocreator/encoderconfiguration.png
new file mode 100644
index 00000000..cfb685ab
--- /dev/null
+++ b/doc/kaudiocreator/encoderconfiguration.png
Binary files differ
diff --git a/doc/kaudiocreator/encodernotfound.png b/doc/kaudiocreator/encodernotfound.png
new file mode 100644
index 00000000..6714ea9e
--- /dev/null
+++ b/doc/kaudiocreator/encodernotfound.png
Binary files differ
diff --git a/doc/kaudiocreator/entersong1.png b/doc/kaudiocreator/entersong1.png
new file mode 100644
index 00000000..f92f21b8
--- /dev/null
+++ b/doc/kaudiocreator/entersong1.png
Binary files differ
diff --git a/doc/kaudiocreator/generalconfiguration.png b/doc/kaudiocreator/generalconfiguration.png
new file mode 100644
index 00000000..8d6c6b36
--- /dev/null
+++ b/doc/kaudiocreator/generalconfiguration.png
Binary files differ
diff --git a/doc/kaudiocreator/index.docbook b/doc/kaudiocreator/index.docbook
new file mode 100644
index 00000000..8cfe57e5
--- /dev/null
+++ b/doc/kaudiocreator/index.docbook
@@ -0,0 +1,1083 @@
+<?xml version="1.0" ?>
+<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
+ <!ENTITY kaudiocreator "<application>kaudiocreator 1.12</application>">
+ <!ENTITY kappname "&kaudiocreator;"><!-- Do *not* replace kappname-->
+ <!ENTITY package "kdemultimedia"><!-- kdebase, kdeadmin, etc -->
+ <!ENTITY % addindex "IGNORE">
+ <!ENTITY % English "INCLUDE"><!-- change language only here -->
+]>
+
+<book lang="&language;">
+
+<bookinfo>
+<title>The &kaudiocreator; Handbook</title>
+
+<authorgroup>
+<author>
+<surname>alan</surname>
+<affiliation>
+<address><email>sorry@no.mail</email>no mail, sorry.</address>
+</affiliation>
+</author>
+<!-- TRANS:ROLES_OF_TRANSLATORS -->
+</authorgroup>
+
+<copyright>
+<year>1999</year>
+<year>2007</year>
+<holder>alan</holder>
+</copyright>
+<legalnotice>&FDLNotice;</legalnotice>
+
+<!-- Date and version information of the documentation
+Don't forget to include this last date and this last revision number, we
+need them for translation coordination !
+Please respect the format of the date (YYYY-MM-DD) and of the version
+(V.MM.LL), it could be used by automation scripts.
+Do NOT change these in the translation. -->
+
+<date>2007-01-15</date>
+<releaseinfo>1.13</releaseinfo>
+
+<abstract>
+<para>
+&kaudiocreator; is an audio &CD; ripper for &kde;.
+</para>
+</abstract>
+
+<keywordset>
+<keyword>KDE</keyword>
+<keyword>kaudiocreator</keyword>
+<keyword>CD</keyword>
+<keyword>ripper</keyword>
+</keywordset>
+
+</bookinfo>
+
+<chapter id="introduction">
+<title>Introduction</title>
+
+<para>&kaudiocreator; is an audio &CD; ripper for &kde;. With it you can easily rip your audio &CD;s to mp3 or ogg files or other formats, depending on whatever encoder you have installed on your system.</para>
+
+<para>&kaudiocreator; is a frontend to cdparanoia (vor ripping the &CD; data to wav files on your hard disk) and many encoders which encode (compress) these wav files. Currently lame for mp3, oggenc for oggs and flac (lossless compression) are supported out of the box (you may still need to install these encoder packages from your distro). But you can add more encoders with the program (if you have them installed).</para>
+
+<para>In general ripping an audio &CD; is a 2 step process:
+ <itemizedlist>
+ <listitem><para>first the &CD; tracks/songs are ripped to the computer into wav files (lossless, uncompressed) and then</para></listitem>
+ <listitem><para>in a second step these wav files (usually one per song) are compressed into mp3 or ogg format or other formats like the lossless flac.</para></listitem>
+ </itemizedlist>
+</para>
+
+<para>For the 1st step, the ripping of the &CD;, &kaudiocreator; uses cdparanoia. Nearly every distro has a pre-compiled package, so install it, if you haven't done so already.</para>
+
+<para>For the 2nd step you need lame (for mp3), oggvorbis (for ogg) or other encoders to be installed. All these programs are usually provided for whatever linux distribution you may have, so it is most likely not necessary to compile anything yourself. You just might need to install any of these packages. You only have to install the encoder package you want to need. If you for instance don't need flac, there is no need to install it.</para>
+
+<para>What encoder/file format to use? The <link linkend="what_encoder">What encoder</link> chapter gives you a small introduction about encoders, audio quality and compression factors.</para>
+
+<para>One word to copy protected audio &CD;s: As already said, &kaudiocreator; uses cdparanoia to rip the audio data from the &CD;. This program is not designed to crack any copy protection. So unless your &CD;/DVD-player firmware circumvents the protection, you will fail to rip protected audio &CD;s. In any case &kaudiocreator; itself cannot handle/work around any protection mechanism. Your bad, just don't buy copy protected audio &CD;s and the market power will dry out this idiocy!</para>
+
+<para>Hey, still reading? Even if you just punched the help menu entry after opening the program the first time, not having a clue how to proceed from this somewhat empty screen (especially when no audio &CD; is already in the drive when starting the program) and the unusual short menu?</para>
+
+<para>Don't worry, this handbook will tell you how to rip a &CD; with this program. Beside the usual explanation of the programs commands/settings, there is a special section with a <link linkend="Example">full walkthrough example</link>. At first you will learn how to set the general settings. This includes your &CD; drive (device ID) the folder for temporary files, the main destination folder, in which a subfolder named according your choice from the &CD; parametes will be created for each &CD;, the encoder to use (like lame for mp3s or oggvorbis for oggs) and among other settings whether you want to use freedb to fetch the data for your &CD; from the Internet or (paranoia, not cdparanoia :-) ) whether you want to enter all data manually.</para>
+<para>While the setup has only to be done once (you can however tweak it as often as you like until you find a suitable setup for yourself), you will then learn the daily business of ripping a &CD; to the harddrive. It is (hopefully) then when you will understand and learn to like the lean approach of the interface you may have stumbled over at first.</para>
+
+<para>Last remark, &kaudiocreator; is a very flexible program which can invoke many encoders. Therefore this handbook (currently) does not cover every possible command and setting. Use it as a starting point to explore the program yourself if you feel the need for more than what is covered here. Usually more information about the programs/encoders (app-name) which are called by &kaudiocreator; can be obtained by opening a console and typing <command>man app-name</command>, <command>app-name -help</command>, <command>app-name --help</command> or <command>app-name -h</command>.</para>
+
+<para>And now: Have fun...</para>
+</chapter>
+
+<chapter id="using-kaudiocreator">
+<title>Using &kaudiocreator;</title>
+
+<para>
+What are we talking about? Well you read this, so you have most likely started the program already. It should look somehow like this:
+</para>
+<para>
+
+<screenshot>
+<screeninfo>Here's a screenshot of &kaudiocreator;</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="kaudiocreatormainwindow800.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Screenshot &kaudiocreator; main window</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+<para>
+The main window after starting the program.
+</para>
+
+<sect1 id="kaudiocreator-features">
+<title>A short list of &kaudiocreator; features</title>
+
+<itemizedlist>
+ <listitem><para>can encode to many formats - depends on what programs/encoders you have installed (mp3, ogg, flac &etc;)</para></listitem>
+ <listitem><para>can fetch &CD; information from freedb via the Internet or you can work completely off line, entering everything manually</para></listitem>
+ <listitem><para>can add tags to the created files - depends on what format you encode to</para></listitem>
+ <listitem><para>set folder names and song names according free adjustable combinations of &CD; information</para></listitem>
+</itemizedlist>
+
+</sect1>
+</chapter>
+
+<chapter id="commands">
+<title>Command Reference</title>
+
+<para>This is just a brief list of the commands of the main window. See the <link linkend="Example">full walkthrough example</link> for more details how to use this program and screenshots with descriptions about the program configuration tabs.</para>
+
+<sect1 id="kaudiocreator-mainwindow">
+<title>The main &kaudiocreator; window</title>
+
+<sect2>
+<title>The <guimenu>File</guimenu> Menu</title>
+<para>
+<variablelist>
+<varlistentry>
+<term><menuchoice>
+<guimenu>File</guimenu>
+<guimenuitem>Eject CD</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Opens the drive bay and ejects the &CD;.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>File</guimenu>
+<guimenuitem>CDDB Lookup</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Starts the CDDB lookup of the cd data according your settings from the <guilabel>Lookup</guilabel> tab on the <guilabel>CDDB</guilabel> page. (Can be local or online, in the later case you have to connect to the Internet first.)</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>File</guimenu>
+<guimenuitem>Edit Album...</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Opens the Album Editor window.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>File</guimenu>
+<guimenuitem>Select All Tracks</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Selects all tracks for processing (ripping and, if set up, encoding).</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>File</guimenu>
+<guimenuitem>Deselect All Tracks</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Deselects all tracks for processing.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>File</guimenu>
+<guimenuitem>Rip Selection</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Starts the ripping and, if an encoder is selected on the <guilabel>Encoder Configuration</guilabel> tab in the settings dialog, the encoding.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<shortcut>
+<keycombo action="simul">&Ctrl;<keycap>S</keycap></keycombo>
+</shortcut>
+<guimenu>File</guimenu>
+<guisubmenu>Rip Selection</guisubmenu>
+</menuchoice></term>
+<listitem><para><action>Opens a sub-menu with a list of the available encoders, so you can rip a selection with another encoder than the standard one. Note, that the encoder should have been configured on the <guilabel>Encoder Configuration</guilabel> tab in the settings dialog.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>File</guimenu>
+<guimenuitem>Remove Completed Jobs</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Remove the completed jobs from the <guilabel>Jobs</guilabel> window.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>File</guimenu>
+<guimenuitem>Encode File...</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Opens a file browser so you can select an already ripped file to encode rather then rip and encode a complete &CD;.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<shortcut>
+<keycombo action="simul">&Ctrl;<keycap>Q</keycap></keycombo>
+</shortcut>
+<guimenu>File</guimenu>
+<guimenuitem>Quit</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Quits &kaudiocreator;.</action></para></listitem>
+</varlistentry>
+</variablelist>
+</para>
+
+</sect2>
+
+<sect2>
+<title>The <guimenu>Settings</guimenu> Menu</title>
+<para>
+<variablelist>
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Hide/Show Toolbar</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Toggles the toolbar.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Hide/Show Statusbar</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Toggles the statusbar.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Configure Shortcuts...</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Opens the configuration window to set shortcuts (key codes) to menu commands.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Configure Toolbars...</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Opens the configuration window to configure the toolbar.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Configure Notifications...</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Opens the configuration window to configure the type of notification (like logs, beeps) for program events (like finished &CD; ripping).</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Configure &kaudiocreator;...</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Opens &kaudiocreator; main configuration dialog with several tabs (like Encoder, for the encoder configuration). You have to go here before you will be able to succesfully use this program!</action></para></listitem>
+</varlistentry>
+</variablelist>
+</para>
+
+</sect2>
+
+<sect2>
+<title>The <guimenu>Help</guimenu> Menu</title>
+
+&help.menu.documentation;
+
+</sect2>
+
+</sect1>
+</chapter>
+
+<chapter id="what_encoder">
+<title>What encoder/file format to use - about audio quality, encoders and compression rates</title>
+
+<para>What encoder to use?</para>
+
+<para>If you don't want to loose any audio information you need a lossless audio format. Beside flac there are others like shorten or monkey, which may be available for your distro. The down side is that the compression rates will be low and hardly any commercial device can play these files.</para>
+
+<para>Between the audio formats which do lose information due to data compression according their psychoacustic model, mp3 and ogg are the most common. ogg may today have a slight advantage over mp3 in audio quality on lower bit rates (up to 128 kbps), but above that rate the differences become less important, as both encoders produce very good audio quality.</para>
+
+<para>In short:</para>
+
+<para>flac
+ <itemizedlist>
+ <listitem><para>+ lossless compression</para></listitem>
+ <listitem><para>+ free</para></listitem>
+ <listitem><para>+ very good audio quality (lossless)</para></listitem>
+ <listitem><para>- low compression rate (probably only around 2.x)</para></listitem>
+ <listitem><para>- hardware player support not existing (afaik)</para></listitem>
+ </itemizedlist>
+</para>
+
+<para>ogg
+ <itemizedlist>
+ <listitem><para>- loss of audio information (amount depends on final bit rate)</para></listitem>
+ <listitem><para>+ free</para></listitem>
+ <listitem><para>+ good to very good (high bit rates) audio quality</para></listitem>
+ <listitem><para>+ high compression rate (depending on resulting audio quality)</para></listitem>
+ <listitem><para>- poor hardware player support</para></listitem>
+ </itemizedlist>
+</para>
+
+<para>mp3
+ <itemizedlist>
+ <listitem><para>- loss of audio information (amount depends on final bit rate)</para></listitem>
+ <listitem><para>o 'not so' free, lame comes for free with most distros but there are license issues</para></listitem>
+ <listitem><para>+ good to very good (high bit rates) audio quality, though slightly lower at lower bit rates compared to ogg</para></listitem>
+ <listitem><para>+ high compression rate (depending on resulting audio quality)</para></listitem>
+ <listitem><para>+ good hardware player support</para></listitem>
+ </itemizedlist>
+</para>
+
+<para>As mentioned before, mp3 and ogg both are audio formats with which you will lose sound information in the end. You cannot revert back to the original once you have the mp3/ogg files. You can create wav files of these and even burn them back onto a &CD; to play them on a &CD;-Player (while DVD-players usually can play both, wav and mp3, mostly not ogg) but the wav files generated from a mp3 or ogg will not be as good as the original. In reality, depending on your HiFi system and the compression rate you chose when creating the mp3s/oggs, you might not even hear a difference. However, if you do not want to lose any sound information but still want to compress your wave files, you should take a look into flac, shorten or monkey. But these encoders will not compress much better than the factor of 2.x .</para>
+
+<para>Until you are not the absolute enthusiast you will most likely decide between ogg and mp3. Go for</para>
+
+<para>ogg - if you want to listening to the music only via your computer or a computer based music server and/or you already have one of the few ogg playing players.</para>
+
+<para>mp3 - if you want to listen to your music on other devices as well, like standard DVD players, portable players (USB stick, flash card, HD) or special car stereos. Today, early 2005, most of these devices only support the mp3 format.</para>
+
+<para>Qualitywise both audio formats should be just fine, depending the compression rate you choose. For mp3 (and most likely ogg too) a bit rate of 128 kbps results in a compression rate of 11 compared to the original wav file which is stored on the &CD;. That might be good during travel but is not sufficient for a good home HiFi stereo system. 192 kbps is a good compromise even for you HiFi system in your living room. This would give you a compression rate of 7.3 .</para>
+
+<para>Already years ago experts were not able to differentiate a 256 kbps mp3 from the &CD; original and that was in a blind test with a HiFi system which you might never be able to affort. :-) That still will reduce the storage space of the original wav by the factor of 5.5 , quite nice. On most modern systems you can as well use variable bit rate. Here the encoder varies the bit rate depending the complexity of the music according to its psychoacoustic model (wow :-) ). The lame setting '--preset extreme' generates a file with a VBR (variable bit rate in opposite to CBR, constant bit rate) of 224 to 256 kbps, depending the complexity of the music. This will compress the original by a factor up to 6.3 and will play on nearly everything currently on the market.</para>
+
+<para>Unless you have an extremly expensive equipment (we are talking of thousands of Euros or US Dollars) and an extremly well trained ear, you will most likely not hear a difference to the maximum possible CBR rate of 320 kbps, just beleave me. Even that would still result in a compression rate of 4.4 . By the way, the setting '--preset extreme' will infact use 320 kbps for complex music parts while compressing less complex music much higher to achieve the average bit rate of 224 to 256 kbps.</para>
+
+<para>A short list of audio quality parameters and resulting compression rates for lame (mp3s) (check lame --help for more):
+ <itemizedlist>
+ <listitem><para>'--preset extreme' = 224 to 256 kbps VBR, compression rate up to 6.3 . Uses up to 320 kbps for complex music parts and way less for less complex parts of the song. Good enough for high quality home HiFi systems. First choice!</para></listitem>
+ <listitem><para>'--preset extreme -b 256 = 256 kbps CBR, compression rate around 5.5 . Uses always 256 kbps for complex and not so complex music parts of the song. Some older players need CBRs. Good enough for high quality home HiFi systems.</para></listitem>
+ <listitem><para>'-h' = 128 kbps CBR joint stereo, compression rate about 11 . 128 kbps for all parts of the song. Good enough for kids music, portable players and, well, car stereos. Not good enough for better home Hifi systems, though.</para></listitem>
+ </itemizedlist>
+</para>
+
+</chapter>
+
+
+<chapter id="parameters">
+<title>&CD; info parameters</title>
+
+<para>The &CD; information you enter or the program fetches automatically from freedb via the Internet, is available as a list of parameters within the program which can be parsed to/used with &kaudiocreator; and the encorder programs in order to create folder and file names and or generate tags in the song files.</para>
+
+<para>When used with encoder programs, often you have to use a combination of program switches from the encoder program and these parameters here. The manual or help of the encoders will tell you the switches for these programs.</para>
+
+<para>E. g. to add the &CD; title to a tag when encoding mp3s with lame you have to add the -tt switch from lame followed by the title parameter from &kaudiocreator;. The result looks then like '... -tt {title} ...' .</para>
+
+<para>This is a summary of the parameters from &kaudiocreator; you can use (= button name in the wizard):</para>
+<itemizedlist>
+ <listitem><para>%{albumartist} - album artist (Artist)</para></listitem>
+ <listitem><para>%{albumtitle} - album title (Album)</para></listitem>
+ <listitem><para>%{artist} - song artist (Track Artist)</para></listitem>
+ <listitem><para>%{albumcomment} - album comment (Comment)</para></listitem>
+ <listitem><para>%{comment} - song comment (Track Comment)</para></listitem>
+ <listitem><para>%{extension} - file extension like mp3 or ogg (Extension)</para></listitem>
+ <listitem><para>%{genre} - music genre (Genre)</para></listitem>
+ <listitem><para>%{title} - song title of current track (Track Title)</para></listitem>
+ <listitem><para>%{number} - number of current track (Track Number)</para></listitem>
+ <listitem><para>%{~} - standard linux shortcut for the users home folder (Home Folder)</para></listitem>
+</itemizedlist>
+
+</chapter>
+
+
+<chapter id="freedb">
+<title>freedb - what's that?</title>
+
+<para>
+You are not alone with your wish to listen to your music independently of your audio &CD;. People all over the world are ripping their &CD;s and converting it into a different format. Everybody makes the same experience: The most time consuming part in this process is the input of the &CD; data. So some smart people had the idea, that it would be much more efficient, if only one person would do this for every &CD; and the rest could just fetch the &CD; data and save the typing work.</para>
+
+<para>That's what freedb is, a free database where audio &CD; rippers can input/upload the infos of a &CD; so that everybody else can download this information rather than typing it into the computer each by themselfs. Note, we are only talking text here: &CD; title, artist, song names &etc;, not the audio file itself.</para>
+
+<para>So is it legal and save to use?</para>
+
+<para>Yes, it should be legal, but I am not a lawyer. However, nobody can prevent you from issuing the &CD; information which is openly available on the &CD; anyhow. And better, without the the &CD; the info is more or less pointless, so no damage can be done to the artists or the audio companies. You can obtain all the information anyhow by strolling through a &CD; shop, physically or online.</para>
+
+<para>Save to use? Well, for retrieving the &CD; information, you do not need to reveal any personal data yourself. There are freedb mirrors in many countries and you can even download the database to work off-line. (Be warned, now in early 2005 it is already a 370 MB tar.bz2 file!). You are asked to setup a mail address to fetch data, but this can be a faked one. I would actually recommend this, to ensure that your mail address is not accidentally be slipped to public and you are ending up getting lot's of spam mail. The initiator of the service claim they will not track or store your data (so why asking for it anyhow??) and then, many mirrors are in the hands of trusted institutions.</para>
+
+<para>In &kaudiocreator; you have to actively check a box to enable smtp (sending of an address) and you can specify a different (faked) address to use with freedb other than the one the system is set up with. The way the configuration is designed, it seems, that the email address is not used when retrieving data from the server. (But I am not the programmer, haven't checked the code.)</para>
+
+<para>However, to be able to give feedback the initiators of freedb ask that in case you submit data (about a new, not yet listed &CD; or a correction of an existing entry) you should do so with a valid email address. It's a great service, so respect this wish in case you want to and have something to contribute! There shouldn't be anything to worry beside the fact that your mail address might fall into the wrong hands and you receive (more) spam. (You may just not want to use your 'best' address). Please read carefully the corresponding FAQ and How-To's at freedb.org if you think you have a &CD; which data has not yet been submitted to the database.</para>
+
+<para>There are mirrors of the master database in many countries. When you set up &kaudiocreator; to use freedb, please use the mirror nearest to you. This saves bandwith on the net and balances the load between the servers.</para>
+
+<para>Visit freedb.org to get an up-to-date list of mirrors for online retrieval of &CD; information every time you rip a &CD; or to download the full database or (usually monthly created) updates of the database.</para>
+
+<para>Oh, how does it work?</para>
+
+<para>Well due to some magic a (not so) unique ID is automatically generated from the &CD; data when it is entered/present in the drive. That might be the number of tracks and their length and/or other things. See freedb.org for more information. Funny enough but unfortunately, the created ID does not seem to be unique for each &CD;. So sometimes a &CD; has to be sorted into a wrong category, out of the 11 defined by freedb, because there is already a &CD; with the same key in the database for the correct category. For backward compatibility they don't want to change the key generation. However, that should be only exceptional. :-) Check freedb.org for the categories before submitting. For instance the category rock holds pop and rap &CD;s as well, as there does not exist a category pop or rap.</para>
+
+</chapter>
+
+
+<chapter id="faq">
+<title>Questions and Answers</title>
+
+&reporting.bugs;
+&updating.documentation;
+
+<qandaset id="faqlist">
+<qandaentry>
+<question>
+<para>What ripper (program) is used to extract the songs from the &CD; into wav files on my computer?</para>
+</question>
+<answer>
+<para>Well, I haven't scanned the source but rumor has it, that cdparanoia is used for that. That has always been the best choice for ripping audio &CD;s. Note, that you have to install the cdparanoia package from your distro, if it isn't already.</para>
+<para>Meanwhile cdda2wav has cought up and there might be areas, like mixed mode &CD;s, where it has surpassed cdparanoia. For simple &CD; ripping cdda2wav uses now the same library as cdparanoia. However, this info is of not much use for you as you cannot change the &CD; ripper in &kaudiocreator;. In case you have problems ripping a &CD; and you want to try cdda2wav you have to use another program like grip, a ripper using gtk and which let's you define the ripper program you want to use.</para>
+</answer>
+</qandaentry>
+<qandaentry>
+<question>
+<para>What encoders/file formats are supported? Can I create mp3 or ogg files?</para>
+</question>
+<answer>
+<para>You can choose the encoder and therefore the file format you want to turn the ripped wav files into. On first opening you will find pre-defined entries for lame (mp3s), oggenc (oggs) and flac (a lossless compression audio format) on the encoder setup tab. But you can add other encoders on that tab as well.</para>
+<para>Note, that you have to install the encoder package from your distro for the encoder you want to use first, before you can use the encoder. (That's required even for the pre-defined encoders!). Take a look at the <link linkend="what_encoder">What encoder</link> chapter for more information about encoders.</para>
+<para>After installation of an encoder you can get more information about its switches by typing something like <command>lame -help</command> or <command>lame --help</command> in a console (&eg; konsole). While 'lame' has to be replaced by the name of the encoder you installed.</para>
+</answer>
+</qandaentry>
+<qandaentry>
+<question>
+<para>There isn't much working here, is it?</para>
+</question>
+<answer>
+<para>Ha, a thousand possible reasons for that. Check the &CD;/DVD drive device ID setting, access/read permission and whether you have installed cdparanoia and the audio encoder of your choise (lame for mp3s, oggenc for oggs, flac &etc;). Try invoking both programs from a command line in a terminal. That might give you a hint what's wrong.</para>
+<para>Oh, by any chance, you don't try to rip a copy protected audio &CD;, do you? Check the cover of the &CD;. This won't work! Your bad, buy unprotected audio &CD;s next time.</para>
+<para>There is a chapter with a <link linkend="Example">full walkthrough example</link>. Even if you want to use different settings it might give you a hint how to proceed.</para>
+</answer>
+</qandaentry>
+</qandaset>
+</chapter>
+
+
+<chapter id="Example">
+
+<title>Example from a first start over the basic setup to the first rip of a &CD;</title>
+
+<para>Read the complete handbook, understood everthing and somehow still unsure what to do? Well, no problem, follow this step by step example and you will get enough information to adjust just those areas which will the program make do exactly what you want.</para>
+
+<para>In this example we will start the program for the first time, set everything up and rip our first &CD;. We will use lame '--preset extreme' to create mp3s with a VBR (Variable Bit Rate) of 224 to 256 kbps in average and ID3V1 tags.</para>
+
+<para>You need to have cdparanoia and lame installed on your system. Both programs should be available as a pre-compiled package for your linux distribution. However, you might need to ask a search engine to find a suitable package of lame, as sometimes it is considered to be not totally free. In any case you might not need to compile from source which, of course, you always can.</para>
+
+<para>The structure how we organize our music on the hard disk is:
+ <itemizedlist>
+ <listitem><para>main dir name for all &CD;s : /usr/share/cd</para></listitem>
+ <listitem><para>in that for each &CD; a subdir named : cdartist - cdtitle - year</para></listitem>
+ <listitem><para>in the subdir the song names : track# - tracktitle</para></listitem>
+ </itemizedlist>
+</para>
+<para>In summary : /usr/share/cd/cdartist - cdtitle - year/track# - tracktitle</para>
+<para>Please make sure, that the folder <filename>/usr/share/cd</filename> exists and that you have write permission on it (&eg; try to copy something to it with konqueror).</para>
+
+<para>The example is divided into 2 sections:
+ <itemizedlist>
+ <listitem><para>the first covering the basic setup of &kaudiocreator;, something you might only do once and</para></listitem>
+ <listitem><para>the second describing the process of actually ripping a &CD;, which you will have to repeat for every &CD; you rip.</para></listitem>
+ </itemizedlist>
+</para>
+
+<para>Here we go...</para>
+
+<sect1 id="Basic-Setup">
+ <title>E 1 Basic Setup</title>
+
+ <para>Here we set the encoder to use to compress the music (lame) and so the audio file format (mp3) as well as the resulting audio quality (--preset extreme => VBR 224 to 256 kbps). You define where the files will go and what structure will be used to store them. In set the program to use freedb to get the &CD; data via Internet as we are lazy.</para>
+
+ <para>While you can always change any of these settings, you will most likely don't change them every time you rip a &CD; once you found a convenient setup.</para>
+
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <menuchoice>
+ <guimenuitem>E 1.1 Step 01: Setting the device ID for your &CD;/DVD drive/writer</guimenuitem>
+ </menuchoice>
+ </term>
+ <listitem>
+ <para>
+ <action>On the main window:</action>
+ </para>
+ <para>
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="kaudiocreatormainwindow800.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Screenshot &kaudiocreator; main window</phrase>
+ </textobject>
+ </mediaobject>
+ </screenshot>
+ </para>
+ <para>set <guilabel>Device:</guilabel> to <filename>/dev/cdrom</filename></para>
+ <para>The device ID is parsed to the ripper program so it can read the &CD; data/songs.</para>
+ <para>Often /dev/cdrom is a symlink to the actual device ID from your drive. If that does not work or you have more than one &CD;/DVD drive, you can give the exact device ID. If you can access your drive with other programs, take a look into /etc/fstab and try the device entries for &CD-ROM; and or DVD-drives listed there.</para>
+ <para>Nothing in there, no luck?</para>
+ <para>Running kernel 2.6:</para>
+ <para>If you happen to have one of the common &ATAPI; (or IDE) drives, with kernel 2.6 the device ID will range from /dev/hda to /dev/hdd. The master on channel 2 is a good starting point: that would be /dev/hdc. If your hard disks are all S-ATA, /dev/hda will probably do the trick. Native SCSI drives will start from /dev/sda unless you have S-ATA hard disks, which come first in the list, so depending on the number of disks the &CD;/DVD drive will start at /dev/sdb or /dev/sdc .</para>
+ <para>Running kernel 2.4:</para>
+ <para>&CD;/DVD drives are accessed via SCSI, so the devices start at /dev/sda .</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <menuchoice>
+ <guimenuitem>E 1.2 Step 02: General Configuration Tab</guimenuitem>
+ </menuchoice>
+ </term>
+ <listitem>
+ <para>
+ <action>Settings -> Configure KAudioCreator... -> General</action>
+ </para>
+ <para>
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="generalconfiguration.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Screenshot &kaudiocreator; General Configuration Tab</phrase>
+ </textobject>
+ </mediaobject>
+ </screenshot>
+ </para>
+ <para>
+ The <guilabel>General Configuration</guilabel> tab. Not much to do here. You could define some additional formating though. For our example, just enter/leave everything as shown in the screenshot.
+ </para>
+ <para>Just check <guilabel>Prompt if information is not complete</guilabel>, so you will get informed when some infos for generating tags are not available.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <menuchoice>
+ <guimenuitem>E 1.3 Step 03: CD Configuration Tab</guimenuitem>
+ </menuchoice>
+ </term>
+ <listitem>
+ <para>
+ <action>Settings -> Configure KAudioCreator... -> &CD;</action>
+ </para>
+ <para>
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="cdconfiguration.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Screenshot &kaudiocreator; &CD; Configuration Tab</phrase>
+ </textobject>
+ </mediaobject>
+ </screenshot>
+ </para>
+ <para>The <guilabel>&CD; Configuration</guilabel> tab. Just check both entries. We are lazy and want that the &CD; data is fetched automatically via the Internet from a freedb server.</para>
+ <para>Note, you need to be online when ripping the &CD; to be able to access the freedb server.</para>
+ <para>The second box is convenient, as if a &CD; entry is found, the &CD; will be ripped imidiately. That's low risk, because if you are later unhappy with the database entry and the resulting director/file names or tag entries, you can easily change them. The folder and file names with konqueror->rename and the id3 tags with kid3, the &kde; tagger.</para>
+ <para>Further note appart from this example: If you want to enter everything manually uncheck the boxes and freedb will not be used. You can use freedb locally in off-line mode if you download the database before. However, this will be over 370 MB so think twice if it's worth it. To use freedb in off-line mode, you need to check the boxes here, at least the first one. The mode is set on the following tab.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <menuchoice>
+ <guimenuitem>E 1.4 Step 04: CDDB Configuration Lookup Tab</guimenuitem>
+ </menuchoice>
+ </term>
+ <listitem>
+ <para>
+ <action>Settings -> Configure KAudioCreator... -> CDDB -> Lookup tab</action>
+ </para>
+ <para>
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="cddbconfigurationlookup.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Screenshot &kaudiocreator; CDDB Configuration Lookup Tab</phrase>
+ </textobject>
+ </mediaobject>
+ </screenshot>
+ </para>
+ <para>CDDB, that's freedb for us. The settings used for the data lookup, for retrieving the &CD; information from the freedb server via the Internet. How does the system know what data to get? Read <link linkend="freedb">the section about freedb</link>. Just set everything as in the screenshot.</para>
+ <para>Short explanation:</para>
+ <para>Mode: We haven't downloaded the database, so we want to work remote with the Internet server only.</para>
+ <para>CDDB Server: PLEASE (yes, I am shouting!) go to freedb.org and look up a mirror near you. This way the load is balanced between the mirror servers. However, as a first try, this setting should work, but PLEASE...</para>
+ <para>Port and Transport: There are 2 combinations, which even the mirrors should understand.</para>
+ <itemizedlist>
+ <listitem><para>Port=80 and Transport=&HTTP;</para></listitem>
+ <listitem><para>Port=8880 and Transport=CDDB</para></listitem>
+ </itemizedlist>
+ <para>Both work equally regarding the server, but as many firewalls blocking Port 8880 by default, you may have better luck with Port 80, as the &HTTP; port is usually open for browsing the Internet.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <menuchoice>
+ <guimenuitem>E 1.5 Step 05: CDDB Configuration Submit Tab</guimenuitem>
+ </menuchoice>
+ </term>
+ <listitem>
+ <para>
+ <action>Settings -> Configure KAudioCreator... -> CDDB -> Submit tab</action>
+ </para>
+ <para>
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="cddbconfigurationsubmit.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Screenshot &kaudiocreator; CDDB Configuration Submit Tab</phrase>
+ </textobject>
+ </mediaobject>
+ </screenshot>
+ </para>
+ <para>In this example we don't want to submit any &CD; data to the freedb server. Uncheck the first box and that should it be.</para>
+ <para>Note beside this example: If one day you find a &CD; which is not yet known by the freedb server or you find an error in a &CD; entry, you can submit the new/updated data. Check the first box at the top of this tab and then either use your mail address known to your system which &kaudiocreator; has already detected or check the lower radio button and enter a new mail address. Please read <link linkend="freedb">the section about freedb</link> first. You are asked to provide a mail address when submitting new or updated entries to the server and you should read the information on freedb.org about revision counts when sending updated information in case you found an error.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <menuchoice>
+ <guimenuitem>E 1.6 Step 06: Ripper Configuration Tab</guimenuitem>
+ </menuchoice>
+ </term>
+ <listitem>
+ <para>
+ <action>Settings -> Configure &kaudiocreator;... -> Ripper</action>
+ </para>
+ <para>
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="ripperconfiguration.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Screenshot &kaudiocreator; Ripper Configuration Tab</phrase>
+ </textobject>
+ </mediaobject>
+ </screenshot>
+ </para>
+ <para>Just take the settings from the screenshot. (Make sure the folder <filename>/tmp</filename> exists on your system!)</para>
+ <para><guilabel>Beep after each rip is done</guilabel> : check this to get an audible feedback from the progress.</para>
+ <para><guilabel>Number of tracks to rip at a time</guilabel> : sorry, cannot think of a reason to do more than one with one drive? Reading and encoding parallel? Just don't know, try for yourself.</para>
+ <para><guilabel>Auto-eject &CD; after last track is ripped</guilabel> : yeah well, so you know it's done. Do what you like.</para>
+ <para><guilabel>Default Temporary Directory</guilabel> : If you check this, you can/must specify the path/folder where the wav files are ripped into. This might be handy if you only have space on another drive. If not checked the files will be created in your home folder. Remember this path if you want do something with the wav files themselfs.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <menuchoice>
+ <guimenuitem>E 1.7 Step 07: Encoder Configuration Tab</guimenuitem>
+ </menuchoice>
+ </term>
+ <listitem>
+ <para>
+ <action>Settings -> Configure KAudioCreator... -> Encoder</action>
+ </para>
+ <para>
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="encoderconfiguration.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Screenshot &kaudiocreator; Encoder Configuration Tab</phrase>
+ </textobject>
+ </mediaobject>
+ </screenshot>
+ </para>
+ <para>Finally the encoder configuration. We will use lame. Here we have a little bit more to do:</para>
+ <para>Click on <guilabel>Lame</guilabel></para>
+ <para>Click on the <guibutton>Configure...</guibutton> button</para>
+ <para>
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="lameconfiguration.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Screenshot &kaudiocreator; Lame Configuration</phrase>
+ </textobject>
+ </mediaobject>
+ </screenshot>
+ </para>
+ <para>Uff, enter everything as in the screenshot. The command entry is a bit long, so you may have to scroll a bit to the right.</para>
+ <para>The settings are more or less self explaining. There is a space between '--preset' and 'extreme' .</para>
+ <para>'--preset extreme' defines the audio quality of the mp3s. As mentioned before, this is a setting for very good audio quality, sufficient even for very good HiFi Stereo systems. The resulting bit rate is about 224 to 256 kbps in average. The achieved compression factor is 6.3 . A good balance between audio quality and space consumption. Storage space/memory is cheap these days, isn't it?</para>
+ <para>Note the '--id3v1-only' entry to limit the tag creation to V1/V1.1 tags. Delete it if you want id3v2 tags as well. kid3 is a very good id3 tagger for &kde; in case you want to manipulate tags later. %f and %o should be the internal variables for the input file (incl. the full path) and the output file name (incl. the full path).</para>
+ <para>Click <guibutton>OK</guibutton> to close and apply the settings.</para>
+ <para><guilabel>Encoded File Location</guilabel> - Back on the <guilabel>Encoder Configuration</guilabel> tab, we define here the destination folder including the folder name for the files from the ripped &CD;. These are variables which will be filled with the corresponding &CD; data everytime you rip a &CD;. The &CD; data can be entered manually or fetched via Internet (CDDB). As already configured in this example, we will use freedb and fetch the data automatically. Play with the Wizard, but in the end for this example the entry should be:</para>
+ <para>'/usr/share/cd/%{albumartist} - %{albumtitle} - %{year}/%{number} - %{title}.%{extension}'</para>
+ <para>Set the rest as in the screenshot. A few comments for future use outside this example:</para>
+ <para><guilabel>'Number of wav files to encode at a time</guilabel>: Why would one want to encode more than one wav file at the time, multi-processor machines? Well, these will come soon, so if you already have one when you read this, give it a try.</para>
+ <para><guilabel>Encoder Priority</guilabel>: for the fanatics, :-) , play with it, if you feel for it. Higher is faster I guess!</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>That's about it, we have just finished the basic setup of KAudioCreator. This configuration is used as the default for every &CD; rip. If you have choosen your music organization structure wise you will never have to change it again. ;-)</para>
+
+</sect1>
+
+<sect1 id="Ripping-a-CD">
+ <title>E 2 Ripping a &CD;</title>
+
+ <para>Ready to rumble! This section will describe the process of ripping a &CD; onto the harddrive. We will create mp3 files. The folder structure, the way the music is organised on the hard disk, was described and setup in the previous section. So connect to the Internet now, as we are lazy and want to fetch the &CD; data from freedb! The process described in this section has to be repeated for ever &CD; you want to rip.</para>
+ <para>As we use a &CD;, which is not already listed in freedb, the screenshots might look a bit different as the ones you will encounter for a &CD; which could successfully fetched from freedb. Therefore you will learn to enter the &CD; data manually rather than fetching it from the Internet but comments will be made to point out the differences.</para>
+ <para>Don't try too hard to look for the &CD; we use in this example. It's a 'special' handbook-writer edition. ;-) Sadly these days, you never know...</para>
+ <para>Here we go...</para>
+
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <menuchoice>
+ <guimenuitem>E 2.1 Step 01: Ripping a CD.</guimenuitem>
+ </menuchoice>
+ </term>
+ <listitem>
+ <para>
+ <action>The main window with an audio &CD; in the drive:</action>
+ </para>
+ <para>
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="cdinserted.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Screenshot &kaudiocreator; main window - cd inserted</phrase>
+ </textobject>
+ </mediaobject>
+ </screenshot>
+ </para>
+ <para>If you haven't done so far, insert the &CD; into the drive which device ID you specified here on this main window.</para>
+ <para>After a short moment the tracks and their length should be listed on the window, as in the screenshot. That is, if the &CD; could be identified as an audio &CD; and read by cdparanoia.</para>
+ <para>If that's not happening, check the <link linkend="faq">Questions and Answers</link> section for help or go back to the previous section covering the basic setup.</para>
+ <para>Attention: if the &CD; could successfully fetched from freedb, all song titles will already be filled out and above the track list, the artist and album name will be shown.</para>
+ <para>In case that there are more than one entry in the database which matches the 'unique' &CD; key for your &CD;, a popup window will appear and you can select the database entry which you think fits best for your &CD;.</para>
+ <para>If you have not checked the box for automatically querying freedb (<guilabel>&CD; Configuration</guilabel> page in the settings dialog), you can trigger this manually with the first button from the button bar. If you don't have a local database but fetching from the Internet you need to be connected to the net.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <menuchoice>
+ <guimenuitem>E 1.2 Step 02: The Album Editor - Enter the album data and the first song title</guimenuitem>
+ </menuchoice>
+ </term>
+ <listitem>
+ <para>
+ <action>Double click anywhere in the first, blue 'highlighted' row (track 1):</action>
+ </para>
+ <para>The Album Editor window pops up.</para>
+ <para>
+ <screenshot>
+ <mediaobject>
+ <imageobject><imagedata fileref="startalbumeditor.png" format="PNG"/></imageobject>
+ <textobject><phrase>Screenshot &kaudiocreator; The Album Editor</phrase></textobject>
+ </mediaobject>
+ </screenshot>
+ </para>
+ <para>Enter the album data for your &CD; according the pattern in the next screenshot. The song title in the Track 1 row of the Current Track box, the rest of the &CD; data in the Album box.</para>
+ <para>No need to enter the artist in the Current Track box, unless of course, you have a sampler with different artists for each song.</para>
+ <para>The upper comment field can be used for individual comments for each song, where as the Album comment field can be used for comments which should be the same across all songs.</para>
+ <para>Attention: of course, when you have successfully fetched the data from freedb the Album Editor is not empty but already holds the data as shown in the following screenshot. However, it is always a good idea to open the Album Editor once to check whether you are happy not only with the artist and album name (e.g. all words start with capital letters or not...) but to check if there are any unwanted comments.</para>
+ <para>
+ <screenshot>
+ <mediaobject>
+ <imageobject><imagedata fileref="entersong1.png" format="PNG"/></imageobject>
+ <textobject><phrase>Screenshot &kaudiocreator; enter title for song 1 and album data</phrase></textobject>
+ </mediaobject>
+ </screenshot>
+ </para>
+ <para>Note: In this example we are using only id3v1 tags. This limits the length of the fields to 30 characters for the artist, album and song title and only 28 characters for the comment field. If you want to create id3v2 tags, you can use unlimited field length, well, at least what &kaudiocreator; allows here. Haven't checked. You set this option on the encoder configuration tab, take a look into the Basic Setup section for this.</para>
+ <para>You can change the tag information of the created files later at any time with a tag editor. Kid3 is a good one for mp3 files with which you can create id3v2 tags, too.</para>
+ <para>Tags may not be supported by all encoder/file formats. Check the encoder help for more information about this.</para>
+
+ <para>After clicking <guibutton>OK</guibutton> a confirmation message pops up:</para>
+ <para>
+ <screenshot>
+ <mediaobject>
+ <imageobject><imagedata fileref="confirmartistcarryover.png" format="PNG"/></imageobject>
+ <textobject><phrase>Screenshot &kaudiocreator; Confirm album data change carry over</phrase></textobject>
+ </mediaobject>
+ </screenshot>
+ </para>
+ <para>We changed the general album data and so a message pops up, asking whether we want to take over this change for the other songs. Yep, we do. Will save us some work. Click Yes.</para>
+ <para>Attention: this popup window will not appear, if you for instance change the album name in the Album box. However, the change will be valid for the other songs as well.</para>
+
+ <para>And another pop-up window:</para>
+ <para>
+ <screenshot>
+ <mediaobject>
+ <imageobject><imagedata fileref="setalbumcategory.png" format="PNG"/></imageobject>
+ <textobject><phrase>Screenshot &kaudiocreator; Set album category</phrase></textobject>
+ </mediaobject>
+ </screenshot>
+ </para>
+ <para>We are asked to select a music category for the album. Don't mix this up with the genre for the id3 tags. These are the 11 categories which are defined by freedb to pre-sort the &CD; IDs. As our &CD; is not in any database (how could it the way we use the program), we have to choose one so in case we would submit the &CD; data, it would be sorted correctly into the freedb database.</para>
+ <para>Just choose one of the entries which seems to match the style of the &CD; you rip. It's not important for this example, we don't submit the &CD; data anyways. For more info go to freedb.org and read about the categories. Pop or Rap for instance are listed under Rock.</para>
+ <para>After hitting OK, you come back to the main window again. Just double click on the row of the second track.</para>
+ <para>Again the Album Editor window pops up and you can see, that the Album box is already filled out with the data we entered for the first track. So just enter the field entries in the Current Track box. The song title for sure and other information if you want.</para>
+ <para>Click into the next row on the main screen and proceed this way until you entered the track data for all tracks on the &CD;.</para>
+ <para>Attention: this popup will not show up, if you have fetched the data from freedb. It was already sorted into one of these categories in the database.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <menuchoice>
+ <guimenuitem>E 2.3 Step 03: Start ripping</guimenuitem>
+ </menuchoice>
+ </term>
+ <listitem>
+ <para>
+ <action>Hit the <guibutton>Rip Selection</guibutton> button as shown on the screenshot</action>
+ </para>
+ <para>
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="readytorip.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Screenshot &kaudiocreator; Start the ripping process</phrase>
+ </textobject>
+ </mediaobject>
+ </screenshot>
+ </para>
+ <para>It's the 3rd button on the toolbar. Note, that the artist and album is shown above the track list.</para>
+ <para>Further note, that there is a check sign in front of every track row. Only the tracks which are marked that way will be processed. A single click on the track row will toggle the track selection. Ensure, that all tracks are selected or use the button <guibutton>Select All Tracks</guibutton> at the bottom of the screen (not shown on screenshot) to select all tracks at once.</para>
+
+ <para>
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="jobshavestarted.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Screenshot &kaudiocreator; Message jobs have started</phrase>
+ </textobject>
+ </mediaobject>
+ </screenshot>
+ </para>
+ <para>Once you have started the ripping process by clicking on the <guibutton>Rip Selection</guibutton> button, this confirmation window pops up. Hit OK and then we will take a look at the jobs window to follow what's going on.</para>
+
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <menuchoice>
+ <guimenuitem>E 2.4 Step 04: Following the progress on the jobs window</guimenuitem>
+ </menuchoice>
+ </term>
+ <listitem>
+ <para>
+ <action>Hit the <guilabel>Jobs</guilabel> tab to switch to the jobs window. It's on the upper left of the screen, right under the toolbar with the buttons.</action>
+ </para>
+ <para>
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="jobcontrol.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Screenshot &kaudiocreator; Job control window</phrase>
+ </textobject>
+ </mediaobject>
+ </screenshot>
+ </para>
+ <para>That's the job control window. The first track is just ripped to the hard disk. A program status is shown on the lower left screen in the status row.</para>
+
+ <para>
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="encodernotfound.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Screenshot &kaudiocreator; Encoder not found</phrase>
+ </textobject>
+ </mediaobject>
+ </screenshot>
+ </para>
+ <para>Aarrrrgh! Sh*t ! Suddenly this. RTFM (read the f***ing manual)! I wrote it! Why, I am incapable! That's what you'll see, when you try to encode with/invoke an encoder which you haven't installed yet. Tracks that could not successfully encoded will get marked with a red 'x' on the job list, too.</para>
+ <para>By the way, see how I cheated? The path for the tmp file and the destination path is not what we set up here in the example. Shame over me, but you have checked that you have write access to both, the folder for the temporary files and the destination folder, haven't you? ;-)</para>
+ <para>Let's do that again...</para>
+
+ <para>
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="rippingandencoding.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Screenshot &kaudiocreator; Ripping and encoding</phrase>
+ </textobject>
+ </mediaobject>
+ </screenshot>
+ </para>
+ <para>Hm, now it becomes clear, why the error occured just after the rip of the first song was finished.</para>
+ <para>See that? Job one, ripping the first track, is gone (finished) but we have a new entry: Job 4 which is the encoding of track 1 to mp3. So ripping and encoding is done parallel. See the status message on the bottom of the window, it's enhanced now.</para>
+ <para>I could not find a configuration setting to suppress this behavior. The only thing that seems to be configurable is the number of parallel rips and encodings. Wow, do even more jobs parallel? I don't know, what this means on lower end machines? The AMD64 used here didn't seem to have a problem ripping and encoding a track (two different though) at once.</para>
+
+ <para>
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="rippingandencoding2.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Screenshot &kaudiocreator; Ripping and encoding 2</phrase>
+ </textobject>
+ </mediaobject>
+ </screenshot>
+ </para>
+ <para>Well, the cycling goes on. Now we are ripping the last track while encoding another one and having one in the encoder queue. After the last track is encoded, a small pop up message is shown on the upper left of the window for a short time.</para>
+ <para>That's it, ready for the next &CD;. Enjoy listening to your music...</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+</sect1>
+
+</chapter>
+
+
+<chapter id="credits">
+
+<title>Credits and License</title>
+
+<para>
+&kaudiocreator;
+</para>
+<para>
+Program copyright 2003-2007 Benjamin Meyer <email>ben+kaudiocreator@meyerhome.net</email>
+</para>
+<para>
+Contributors:
+<itemizedlist>
+ <listitem><para>A one man show so far!<email>nope@no.mail</email></para></listitem>
+</itemizedlist>
+</para>
+
+<para>
+Documentation copyright 2005-2007 alan <email>sorry@no.mail</email>
+</para>
+<para>
+As a bad example: sorry no mail. I cannot maintain this handbook. It is thought as a first starter for a handbook for kaudiocreator. Whoever may think of something useful to add or change, may do so. Well, at least after contacting the author first, I assume. And if you read this, he left this section in and therefore agrees to this proposal.
+</para>
+
+<!-- TRANS:CREDIT_FOR_TRANSLATORS -->
+
+&underFDL; <!-- FDL: do not remove -->
+&underGPL; <!-- GPL License -->
+
+</chapter>
+
+<appendix id="installation">
+<title>Installation</title>
+
+<sect1 id="getting-kaudiocreator">
+<title>How to obtain &kaudiocreator;</title>
+
+&install.intro.documentation;
+
+</sect1>
+
+<sect1 id="requirements">
+<title>Requirements</title>
+
+<para>In order to successfully use &kaudiocreator;, you need &kde; 3.x. and cdparanoia. If you want to create compressed audio files like mp3s or oggs, you need the encoder of your choice.</para>
+
+<para>
+&kaudiocreator; is today maintained within the &kde; CVS as part of the kdemultimedia package. If you are familier with cvs you can do an anonymous checkout from there. However, most distros come with a pre-compiled package, especially because &kaudiocreator; is part of a main &kde; package. Install the kdemultimedia package from your distro. If you read this, you have most likely found it already...
+</para>
+
+</sect1>
+
+<sect1 id="compilation">
+<title>Compilation and Installation</title>
+
+<para>Funny, if you managed to checkout the source from the &kde; SVN you won't need this info here but...</para>
+
+&install.compile.documentation;
+
+</sect1>
+
+<sect1 id="configuration">
+<title>Configuration</title>
+
+<para>Well, this section is most likely intended for people who build from source and not the user using a pre-compiled package from a distro.</para>
+
+<para>Sorry, there is currently no help available for configuration options before compiling the source.</para>
+
+<para>If you look for help to configure the program to your needs once it is running, then the following is for you:</para>
+
+<para>Being a program that can invoke many different other programs there is a lot to configure. Best read the <link linkend="Example">full walkthrough example</link> to introduce yourself with the configuration options. It sports screenshots of the different configuration tabs and provides comments even for options not used in the example.</para>
+
+</sect1>
+
+</appendix>
+
+&documentation.index;
+</book>
+
+<!--
+Local Variables:
+mode: sgml
+sgml-minimize-attributes:nil
+sgml-general-insert-case:lower
+sgml-indent-step:0
+sgml-indent-data:nil
+End:
+
+vim:tabstop=2:shiftwidth=2:expandtab
+-->
+
diff --git a/doc/kaudiocreator/jobcontrol.png b/doc/kaudiocreator/jobcontrol.png
new file mode 100644
index 00000000..de89ac26
--- /dev/null
+++ b/doc/kaudiocreator/jobcontrol.png
Binary files differ
diff --git a/doc/kaudiocreator/jobshavestarted.png b/doc/kaudiocreator/jobshavestarted.png
new file mode 100644
index 00000000..a4d306ab
--- /dev/null
+++ b/doc/kaudiocreator/jobshavestarted.png
Binary files differ
diff --git a/doc/kaudiocreator/kaudiocreatormainwindow800.png b/doc/kaudiocreator/kaudiocreatormainwindow800.png
new file mode 100644
index 00000000..46751313
--- /dev/null
+++ b/doc/kaudiocreator/kaudiocreatormainwindow800.png
Binary files differ
diff --git a/doc/kaudiocreator/lameconfiguration.png b/doc/kaudiocreator/lameconfiguration.png
new file mode 100644
index 00000000..8fbc0710
--- /dev/null
+++ b/doc/kaudiocreator/lameconfiguration.png
Binary files differ
diff --git a/doc/kaudiocreator/readytorip.png b/doc/kaudiocreator/readytorip.png
new file mode 100644
index 00000000..2ebead2e
--- /dev/null
+++ b/doc/kaudiocreator/readytorip.png
Binary files differ
diff --git a/doc/kaudiocreator/ripperconfiguration.png b/doc/kaudiocreator/ripperconfiguration.png
new file mode 100644
index 00000000..1269eb47
--- /dev/null
+++ b/doc/kaudiocreator/ripperconfiguration.png
Binary files differ
diff --git a/doc/kaudiocreator/rippingandencoding.png b/doc/kaudiocreator/rippingandencoding.png
new file mode 100644
index 00000000..d15703a5
--- /dev/null
+++ b/doc/kaudiocreator/rippingandencoding.png
Binary files differ
diff --git a/doc/kaudiocreator/rippingandencoding2.png b/doc/kaudiocreator/rippingandencoding2.png
new file mode 100644
index 00000000..dcd3a42a
--- /dev/null
+++ b/doc/kaudiocreator/rippingandencoding2.png
Binary files differ
diff --git a/doc/kaudiocreator/setalbumcategory.png b/doc/kaudiocreator/setalbumcategory.png
new file mode 100644
index 00000000..8562dade
--- /dev/null
+++ b/doc/kaudiocreator/setalbumcategory.png
Binary files differ
diff --git a/doc/kaudiocreator/startalbumeditor.png b/doc/kaudiocreator/startalbumeditor.png
new file mode 100644
index 00000000..e1052ddc
--- /dev/null
+++ b/doc/kaudiocreator/startalbumeditor.png
Binary files differ
diff --git a/doc/kioslave/Makefile.am b/doc/kioslave/Makefile.am
new file mode 100644
index 00000000..085981d9
--- /dev/null
+++ b/doc/kioslave/Makefile.am
@@ -0,0 +1,4 @@
+
+KDE_LANG = en
+KDE_DOCS = AUTO
+
diff --git a/doc/kioslave/audiocd.docbook b/doc/kioslave/audiocd.docbook
new file mode 100644
index 00000000..982e1f23
--- /dev/null
+++ b/doc/kioslave/audiocd.docbook
@@ -0,0 +1,208 @@
+<article lang="&language;" id="audiocd">
+<title>audiocd</title>
+<articleinfo>
+<authorgroup>
+<author>&Rik.Hemsley; &Rik.Hemsley.mail;</author>
+<author><personname><firstname>Benjamin</firstname><surname>Meyer</surname></personname></author>
+<!-- TRANS:ROLES_OF_TRANSLATORS -->
+</authorgroup>
+
+<date>2004-09-16</date>
+<releaseinfo>2.30.00</releaseinfo>
+
+</articleinfo>
+
+<para>Allows treating audio <acronym>CD</acronym>s like a
+<quote>real</quote> filesystem, where tracks are represented as files
+and, when copied from the folder, are digitally extracted from the
+<acronym>CD</acronym>. This ensures a perfect copy of the audio
+data.</para>
+
+<para>To see how this slave works, insert an audio <acronym>CD</acronym>
+in your &CD-ROM; drive and type <userinput>audiocd:/</userinput> into
+&konqueror;. Within a few seconds you should see a list of tracks and
+some folders.</para>
+
+<para>Audio <acronym>CD</acronym>s don't really have folders, but
+the audiocd slave provides them as a convenience. If you look inside
+these folders you will see that they all contain the same number of
+tracks. If you are connected to the Internet, some folders will have
+the actual track titles shown as the filenames.</para>
+
+<para>The reason that these separate folders exist are so that you
+can choose in which format you would like to listen to (or copy) the
+tracks on the <acronym>CD</acronym>.</para>
+
+<para>If you drag a track from the <filename class="directory">Ogg
+Vorbis</filename> folder and drop it on another &konqueror; window
+open at your home folder, you should see a progress window showing
+you that the track is being extracted from the <acronym>CD</acronym> and
+saved to a file. Note that Ogg Vorbis is a compressed format, so the
+file in your home folder will appear a great deal smaller than it
+would have been if you had copied the raw data.</para>
+
+<para>The mechanism behind this is quite simple. When the audiocd slave
+is asked to retrieve a track from the <filename class="directory">Ogg
+Vorbis</filename> folder, it starts extracting the digital audio data
+from the <acronym>CD</acronym>. As it sends the data over to the file in
+your home folder, it simultaneously encodes it in Ogg Vorbis format
+(<acronym>CD</acronym> audio is in an uncompressed format to start
+with).</para>
+
+<para>You could also try dragging a file ending in <literal
+role="extension">.wav</literal> and dropping it on the &kde; Media
+Player, &noatun;. In this case, the procedure that happens behind the
+scenes is similar, except that instead of encoding the audio data in Ogg
+Vorbis format, it is put through a very simple conversion, from raw
+binary data (which the <literal role="extension">.cda</literal> files in
+the toplevel folder represent) to <quote>RIFF WAV</quote> format, a
+non-compressed format that most media players understand.</para>
+
+<para>&noatun; should quite happily play the <literal
+role="extension">.wav</literal> file, but if it has trouble, you may
+consider using the <option>paranoia_level</option> option, explained
+below.</para>
+
+<variablelist>
+<title>Options</title>
+
+<varlistentry>
+<term><option>device</option></term>
+<listitem>
+<para>Set the path to the audio <acronym>CD</acronym> device, &eg;
+<userinput>audiocd:/<option>?device</option>=<parameter>/dev/sdc</parameter></userinput>.
+Normally, the slave will try to find a <acronym>CD</acronym> drive with
+an audio <acronym>CD</acronym> inserted, but if it fails or you have
+more than one <acronym>CD</acronym> drive, you may want to try this
+option. Note that the configuration dialog allows you to set a default
+value for this option.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>fileNameTemplate</option></term>
+<listitem>
+<para>Set the file name template, &eg;
+<userinput>audiocd:/<option>?fileNameTemplate</option>=<parameter>Track %{number}</parameter></userinput>. Note that the configuration dialog allows you to set a default value for this option. A warning that if you set it to an empty string no files will show up.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>albumNameTemplate</option></term>
+<listitem>
+<para>Set the album name template, &eg;
+<userinput>audiocd:/<option>?albumNameTemplate</option>=<parameter>%{albumartist} %{albumtitle}</parameter></userinput>. Note that the configuration dialog allows you to set a default value for this option.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>niceLevel</option></term>
+<listitem>
+<para>Sets the process nice level for encoders, &eg;
+<userinput>audiocd:/<option>?albumNameTemplate</option>=<parameter>niceLevel=10</parameter></userinput>. Note that the configuration dialog allows you to set a default value for this option.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>paranoia_level</option></term>
+<listitem>
+<para>Set the amount of error detection and correction used when
+extracting data.</para>
+
+<variablelist>
+<varlistentry>
+<term>Level 0</term>
+<listitem>
+<para>No detection or correction. Only useful if you have a perfect
+<acronym>CD</acronym> drive (unlikely).</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>Level 1</term>
+<listitem>
+<para>Enable basic error checking and correction.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>Level 2</term>
+<listitem>
+<para>Default. Specifies that only a perfect extraction will be
+accepted.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+<para>Note that there is a disadvantage to level 2. Extraction can be
+very slow, so real-time digital playback may not work properly. If you
+have a good quality <acronym>CD</acronym> drive (note that more
+expensive does not necessarily mean better quality) then you probably
+won't experience very slow extraction, but a poor drive may take days
+(!) to extract the audio from one <acronym>CD</acronym>.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>cddbChoice</option></term>
+<listitem>
+
+<para>Specify which Internet <acronym>CD</acronym> Database entry to use. Audio
+<acronym>CD</acronym>s don't have track names, but the Internet
+<acronym>CD</acronym> Database is a clever system which uses a special
+unique identifier generated from the number and length of tracks on each
+<acronym>CD</acronym> to cross-reference a track listing. Track listings
+are contributed by the Internet community and made available to
+all. Occasionally there will be multiple entries. You can specify which one to use.</para>
+
+<para>You can submit your own track listings using &kscd;, the &kde;
+<acronym>CD</acronym> player.</para>
+
+<para>By default audiocd tries to pick the best one.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+<variablelist>
+<title>Examples</title>
+<varlistentry>
+<term><userinput>audiocd:/?device=/dev/scd0&amp;paranoia_level=0&amp;cddbChoice=0</userinput></term>
+<listitem>
+<para>Gives a listing of the tracks on the audio <acronym>CD</acronym>
+inserted in <filename class="devicefile">/dev/scd0</filename>, which on
+&Linux; specifies the first <acronym>SCSI</acronym> &CD-ROM; device. If
+you copy tracks from the <acronym>CD</acronym>, digital extraction will
+be performed without error correction or detection. The
+<acronym>CDDB</acronym> Database entry 0 will be used.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+<qandaset>
+<title>Frequently Asked Question</title>
+<qandaentry>
+<question>
+<para>I get <errorname>The file or folder / does not
+exist</errorname>. How do I fix that? I have an audio
+<acronym>CD</acronym> in my drive!</para>
+</question>
+
+<answer>
+<para>Try running <userinput><command>cdparanoia</command>
+<option>-vsQ</option></userinput> as yourself (not <systemitem
+class="username">root</systemitem>). Do you see a track list? If not,
+make sure you have permission to access the <acronym>CD</acronym>
+device. If you're using <acronym>SCSI</acronym> emulation (possible if
+you have an <acronym>IDE</acronym> <acronym>CD</acronym> writer) then
+make sure you check that you have read and write permissions on the
+generic <acronym>SCSI</acronym> device, which is probably <filename
+class="devicefile">/dev/sg0</filename>, <filename
+class="devicefile">/dev/sg1</filename>, &etc;. If it still doesn't work,
+try typing <userinput>audiocd:/?device=/dev/sg0</userinput> (or similar)
+to tell kio_audiocd which device your &CD-ROM; is.</para>
+</answer>
+</qandaentry>
+</qandaset>
+
+
+</article>
diff --git a/doc/kmid/Makefile.am b/doc/kmid/Makefile.am
new file mode 100644
index 00000000..085981d9
--- /dev/null
+++ b/doc/kmid/Makefile.am
@@ -0,0 +1,4 @@
+
+KDE_LANG = en
+KDE_DOCS = AUTO
+
diff --git a/doc/kmid/index.docbook b/doc/kmid/index.docbook
new file mode 100644
index 00000000..6670dd54
--- /dev/null
+++ b/doc/kmid/index.docbook
@@ -0,0 +1,1338 @@
+<?xml version="1.0" ?>
+<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
+ <!ENTITY kappname "&kmid;">
+ <!ENTITY package "kdemultimedia">
+ <!ENTITY % English "INCLUDE" > <!-- change language only here -->
+ <!ENTITY % addindex "IGNORE">
+]>
+
+<book lang="&language;">
+
+<bookinfo>
+<title>The &kmid; Handbook</title>
+<authorgroup>
+<author>
+<firstname>Antonio</firstname>
+<surname>Larrosa Jim&eacute;nez</surname>
+<affiliation>
+<address><email>larrosa@kde.org</email></address>
+</affiliation>
+</author>
+<!-- TRANS:ROLES_OF_TRANSLATORS -->
+</authorgroup>
+
+<copyright>
+<year>1999</year><year>2001</year>
+<holder>Antonio Larrosa Jim&eacute;nez</holder>
+</copyright>
+
+<date>2002-02-05</date>
+<releaseinfo>2.00.00</releaseinfo>
+
+<abstract>
+<para>
+&kmid; is a midi/karaoke multimedia player
+</para>
+</abstract>
+
+<keywordset>
+<keyword>KMid</keyword>
+<keyword>midi</keyword>
+<keyword>karaoke</keyword>
+<keyword>multimedia</keyword>
+<keyword>mid</keyword>
+<keyword>kar</keyword>
+<keyword>player</keyword>
+<keyword>music</keyword>
+<keyword>sound</keyword>
+<keyword>fm</keyword>
+<keyword>awe</keyword>
+<keyword>gus</keyword>
+</keywordset>
+</bookinfo>
+
+<chapter id="introduction">
+<title>Introduction</title>
+
+<para>
+&kmid; is &kde;'s midi and karaoke multimedia player. It features some
+features not found in any other &UNIX; midi player, such as realtime
+graphics and karaoke text highlighting among others.
+</para>
+
+<para>
+&kmid; has been reported to run on &Linux; and FreeBSD operating
+systems. It uses the <acronym>OSS</acronym> sound driver, so it should
+run on every system where &kde; and <acronym>OSS</acronym>
+compile. &kmid; also supports the &Linux; Ultrasound Project Driver ,
+which is required to get sound in <acronym>GUS</acronym> cards. I plan
+to support the <acronym>ALSA</acronym> driver as soon as it supports a
+sequencer device.
+</para>
+
+<para>
+&kmid; shows the lyrics in the screen changing its color at the same
+time the music is playing, so it is very easy to follow the tune of the
+songs.
+</para>
+
+<para>
+Hope you find &kmid; as fun to use as I found developing it.
+</para>
+
+<para>
+Antonio Larrosa Jim&eacute;nez <email>larrosa@kde.org</email>
+</para>
+
+<sect1 id="kmids-features">
+<title>&kmid;'s features</title>
+
+<para>
+These are some of &kmid;'s main features:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+It has a very <emphasis>friendly user interface</emphasis> to display karaoke
+text with <emphasis>realtime highlighting</emphasis> of lyrics.
+</para>
+</listitem>
+<listitem>
+<para>
+It features a graphical view of what is being played on each midi channel, by
+highlighting the keys pressed in (virtual) keyboards.
+</para>
+</listitem>
+<listitem>
+<para>
+The most powerful <emphasis>Midi Mapper</emphasis> that you will ever find in
+any operating system.
+</para>
+</listitem>
+<listitem>
+<para>
+<emphasis>Drag &amp; drop</emphasis> so you can drop in &kde; any midi file from a
+&konqueror; window.
+</para>
+</listitem>
+<listitem>
+<para>
+You can <emphasis>change the tempo</emphasis> of songs to play them slower or
+faster at your wish.
+</para>
+</listitem>
+<listitem>
+<para>
+It shows lights to follow the rhythm of the song.
+</para>
+</listitem>
+<listitem>
+<para>
+<emphasis>Customizable fonts</emphasis> for karaoke text to be displayed.
+</para>
+</listitem>
+<listitem>
+<para>
+Supports the two standards to introduce lyrics in midi files, that is, lyrics or
+text events (and guess which one a song uses automatically).
+</para>
+</listitem>
+<listitem>
+<para>
+Session Management. If a song is playing while you logout from &kde;, the next
+time you login, the same song will start playing.
+</para>
+</listitem>
+<listitem>
+<para>
+<emphasis>Adjustable volume</emphasis> in realtime.
+</para>
+</listitem>
+<listitem>
+<para>
+It can play broken midi files which make other players core dump!
+</para>
+</listitem>
+<listitem>
+<para>
+It can open <emphasis>gzipped midi/karaoke files</emphasis> just as any other
+file.
+</para>
+</listitem>
+<listitem>
+<para>
+Consumes approximately <emphasis>0.1&percnt;</emphasis> of my
+<acronym>CPU</acronym> (depends on the complexity of the song).
+</para>
+</listitem>
+<listitem>
+<para>
+Supports external midi synths, <acronym>AWE</acronym>, <acronym>FM</acronym> and
+<acronym>GUS</acronym> cards (for the latter you need the <acronym>LUP</acronym>
+driver and gusd installed).
+</para>
+</listitem>
+<listitem>
+<para>
+Runs on &Linux; and FreeBSD (maybe also other unices ...).
+</para>
+</listitem>
+</itemizedlist>
+
+</sect1>
+</chapter>
+
+<chapter id="general-usage">
+<title>General usage</title>
+
+<sect1 id="opening-songs">
+<title>Opening songs</title>
+
+<para>
+You can open a song several different ways.
+</para>
+
+<para>
+First, you can select <guimenuitem>Open...</guimenuitem> from the
+<guimenu>File</guimenu> menu, then you are presented with a standard
+open dialog, with which you can select the song you wish to open.
+</para>
+
+<para>
+You can drag a file from a &konqueror; window and drop it in the &kmid; window.
+You can also Drag &amp; Drop multiple songs at the same time.
+</para>
+
+<para>
+If you specify a song in the command line when running &kmid;, it will also be
+opened.
+</para>
+
+<para>
+And the final way is by selecting the song from the list of songs of the active
+collection.
+</para>
+
+</sect1>
+
+<sect1 id="playing-songs">
+<title>Playing songs</title>
+
+<para>
+To play a song, first open it, and then press on the
+<guiicon>Play</guiicon> button of the toolbar, choose the
+<guimenuitem>Play</guimenuitem> entry of the <guimenu>Song</guimenu>
+menu, or just press the <keycap>Space</keycap> key.
+</para>
+
+<para>
+Note that when you open a file using Drag &amp; Drop, &kmid; will start
+playing it automatically (if you drop more than one file, they will be
+added to a collection and they will be played sequentially).
+</para>
+
+<para>
+Once &kmid; is playing a song, you can move the time slider, by pressing
+with the &MMB; mouse button, to go to a specified position.
+</para>
+
+<para>
+If a song is playing too fast or too slow for you, you can press on the
+arrows at both sides of the tempo <acronym>LCD</acronym> and make it
+play faster or slower. To get back to the default tempo, just do a
+double click on the tempo <acronym>LCD</acronym>.
+</para>
+
+<para>
+The <keycap>Space</keycap> key is used for two things, when music is
+playing, and you press the <keycap>Space</keycap> key, it will act as
+when you press on the <guiicon>pause</guiicon> button or the
+<guimenuitem>Pause</guimenuitem> entry of the <guimenu>Song</guimenu>
+menu, that is, it will pause music. If you press the
+<keycap>Space</keycap> key when no music is being played, &kmid; will
+play it.
+</para>
+
+</sect1>
+
+<sect1 id="displaying-lyrics">
+<title>Displaying lyrics</title>
+
+<para>
+There are two methods to store lyrics in a song, by using
+<guimenuitem>Text events</guimenuitem> or <guimenuitem>Lyrics
+events</guimenuitem>, some songs use the first, some the second, some
+use both of them, and some don't include lyrics :-)
+</para>
+
+<para>
+&kmid; lets you choose which events to display, and even better, it has
+an option to automatically select the type of events that a song uses,
+so that you don't have to change the type manually . That way, if you
+activate the <guimenuitem>Automatic Text Chooser</guimenuitem> entry of
+the <guimenu>Settings</guimenu> menu, the karaoke text will be
+automatically selected, but you can still change them if you prefer to
+see the other type.
+</para>
+
+<para>
+To select which type to see, you can use the appropriate entries in the
+<guimenu>Settings</guimenu> menu, or just press the <keycap>1</keycap>
+and <keycap>2</keycap> keys of your keyboard to see the
+<guimenuitem>Text events</guimenuitem> or <guimenuitem>Lyrics
+events</guimenuitem> respectively.
+</para>
+
+</sect1>
+
+</chapter>
+
+<chapter id="collections">
+<title>Collections</title>
+
+<para>
+A collection is a list of midi files that you put in a set, and which
+are played one after another. This section will help you to use them,
+and will give you some useful tips to make a good use of them.
+</para>
+
+<sect1 id="creating-a-collection">
+<title>Creating a collection</title>
+
+<para>
+To create a collection, first open the <guilabel>Collections
+Manager</guilabel> dialog, by selecting the <guimenuitem>Organize
+...</guimenuitem> entry of the <guimenu>Collections</guimenu> menu .
+Then click on the <guibutton>New</guibutton> button, and enter the name
+you want the collection to have.
+</para>
+
+<para>
+You can also copy a complete collection by selecting it and then
+pressing the <guibutton>Copy</guibutton> button, which will ask you for
+the name of the new collection that will have initially the same songs
+as the selected collection.
+</para>
+
+<para>
+Once you have more than one collection, you can change the active
+collection from the <guilabel>Collections Manager</guilabel>, by
+selecting it.
+</para>
+
+</sect1>
+<sect1 id="the-temporary-collection">
+<title>The Temporary Collection</title>
+
+<para>
+The Temporary Collection is a collection that is used to hold songs you
+want to play but that you don't want to add to any collection.
+</para>
+
+<para>
+This collection is <emphasis>not saved</emphasis> on exit of the
+application, so keep it in mind when you add lots of songs to it.
+</para>
+
+<para>
+Keep on reading this section for a better understanding of the Temporary
+Collection.
+</para>
+
+</sect1>
+
+<sect1 id="adding-songs-to-a-collection">
+<title>Adding songs to a collection</title>
+<subtitle>How to use <guimenuitem>AutoAdd to a
+collection</guimenuitem></subtitle>
+
+<para>
+There are some different ways to add a song to a collection.
+</para>
+
+<para>
+First of all in each method, you must have selected the collection you
+want to add songs to in the <guilabel>Collections Manager</guilabel>.
+Then you can press on the <guibutton>Add</guibutton> button to add a
+song, there will appear an open file dialog so that you can choose which
+song to add.
+</para>
+
+<para>
+The other methods to add a song depend on the state of the
+<guimenuitem>AutoAdd to Collection</guimenuitem> option.
+</para>
+
+<para>
+If <guimenuitem>AutoAdd to Collection</guimenuitem> is enabled, when
+you open a song (using <menuchoice><guimenu>File</guimenu>
+<guimenuitem>Open...</guimenuitem></menuchoice> or Drag &amp; Drop) it
+(they) will be added to the active collection without user intervention.
+</para>
+
+<para>
+If <guimenuitem>AutoAdd to Collection</guimenuitem> is not enabled,
+when you open a song the Temporary Collection will be activated and
+cleared, and the opened songs will be added to it.
+</para>
+
+</sect1>
+
+<sect1 id="removing-songs-from-collections">
+<title>Removing songs from collections</title>
+
+<para>
+To delete a song from a collection, just open the <guilabel>Collection
+Manager</guilabel>, select the appropriate collection, and the song you
+wish to delete, and then click on the <guibutton>Remove</guibutton>
+button.
+</para>
+
+</sect1>
+
+<sect1 id="playing-order">
+<title>Playing in order or at random</title>
+
+<para>
+You can select the order in which songs will be played . When you select
+<guimenuitem>In order</guimenuitem> mode from the <guisubmenu>Play
+Order</guisubmenu> submenu of the <guimenu>Collections</guimenu> menu,
+songs will be played in the same order in which they were added to the
+collection.
+</para>
+
+<para>
+When you select <guimenuitem>Shuffle</guimenuitem> mode, &kmid; will
+generate a random variable with a discrete uniform distribution to
+really play randomly the songs in the collection . It will give values
+to that random variable while generating the list in which order the
+songs will be played (you surely want to play random songs, but don't
+want to play twice the same song, and you want to play the last played
+song when you press on the <guibutton>Previous Song</guibutton> button,
+don't you ? :-) ).
+</para>
+
+<para>
+The random list in which the collection will be played will be
+regenerated each time you add or remove a file in the active collection,
+and when you press on the <guimenuitem>Shuffle mode</guimenuitem> entry
+of the menu.
+</para>
+
+</sect1>
+
+<sect1 id="selecting-from-a-collection">
+<title>Selecting a song from a collection</title>
+
+<para>
+You can select a song to play in the <literal>Collection
+Manager</literal>, or by using the combo box over the karaoke text.
+</para>
+
+<para>
+You can also change to the next song by using the <literal>Next
+Song</literal> entry of the <literal>Song</literal> menu, the
+<literal>Next Song</literal> button of the toolbar, or pressing the
+<literal>right arrow</literal> key.
+</para>
+
+<para>
+To change to the previous song, use the <guimenuitem>Previous
+Song</guimenuitem> entry of the <guimenu>Song</guimenu> menu, the
+<guimenuitem>Previous Song</guimenuitem> button of the toolbar, or press
+the <keycap>left arrow</keycap> key of your keyboard.
+</para>
+
+</sect1>
+
+<sect1 id="deleting-a-collection">
+<title>Deleting a collection</title>
+
+<para>
+To delete a collection, simply open the <guilabel>Collection
+Manager</guilabel>, select the collection you want to delete, and click
+on <guibutton>Delete</guibutton>. Easy, it isn't? </para>
+
+<para>
+Please keep in mind that you cannot delete the Temporary Collection, but
+it doesn't matter as it is not saved when you quit &kmid;.
+</para>
+
+</sect1>
+
+</chapter>
+
+<chapter id="midi-maps">
+<title>Midi Maps</title>
+
+<sect1 id="what-is-a-midimap">
+<title>What is a midi map ?</title>
+
+<para>
+A Midi Map is something that maps midi events in other midi events.
+</para>
+
+<para>
+This is totally needed if a synthesizer doesn't understand the standard
+events (that is, if a synthesizer is not General Midi compliant), in
+this case, a midi map will translate General Midi events in the events
+that that synthesizer understands.
+</para>
+
+<para>
+For example you can make a midi map that converts all the
+<literal>Change patch to Bright Piano</literal> events, to
+<literal>Change patch to Trumpet</literal> events, and so when a song
+tries to play a piano, it will play a trumpet instead.
+</para>
+
+<para>
+This may sound odd, (why playing a trumpet when the song is made to play
+a piano?), but it is very useful. The <acronym>GM</acronym> standard
+specifies that when a midi keyboard receives an event to change patch to
+<literal>0</literal>, it will change the current patch to <literal>Grand
+Piano</literal>, but older synthesizer will change for example to a
+<literal>Electric Guitar</literal> when it receives a
+<literal>0</literal>. This old keyboard, needed to receive a
+<literal>3</literal> (for example) to change to a
+<literal>Piano</literal>. And here comes the midi map in action,
+changing all <literal>change patch to 0</literal> to <literal>change
+patch to 3</literal> and thus really playing the correct instrument when
+it has to.
+</para>
+
+</sect1>
+
+<sect1 id="do-i-need-a-midi-map">
+<title>Do I need a midi map ?</title>
+
+<para>
+In short, if you don't have an external synth, <emphasis>no</emphasis>!
+</para>
+
+<para>
+If you only have a sound card, midi maps are not needed because all the
+sound cards are <acronym>GM</acronym> compliant (this include AWE cards,
+<acronym>GUS</acronym> cards, <acronym>FM</acronym> devices and so on).
+</para>
+
+<para>
+If you are playing music through an external synthesizer, and it is not
+GM compliant, you will need to make a midi map for your midi keyboard .
+Although you will perhaps be a whole afternoon doing your map file and
+trying different values for all the options, you will be fully rewarded
+when you finish it, because then you will find all the hidden
+possibilities of your keyboard. For example, I have a low-cost Yamaha
+PSS-790, which is not <acronym>GM</acronym> compatible, and doesn't has
+as many instruments as a <acronym>GM</acronym> synthesizer, but with
+&kmid;'s midi mapper, it sounds even better than many soundcards
+(including AWE :-)), due to the sound quality found in external synths
+(even on non <acronym>GM</acronym> compliant ones).
+</para>
+
+</sect1>
+
+<sect1 id="creating-a-midi-map">
+<title>Creating a midi map</title>
+
+<para>
+There isn't any program to generate midi maps, so you will have to edit a file
+by hand (using your favorite text editor).
+</para>
+
+<para>
+A Midi map is a text file that keeps all the needed translations there will be
+made when playing music.
+</para>
+
+<para>
+It consist of four sections: <literal>PATCHMAP</literal>,
+<literal>KEYMAP</literal>, <literal>CHANNELMAP</literal> and
+<literal>OPTIONS</literal>.
+</para>
+
+<para>
+Each section must appear only once, except the <literal>KEYMAP</literal> section
+that can appear as many times as needed, provided that each appearance use a
+different TextID (continue reading for details).
+</para>
+
+<para>
+The general structure of a map file is:
+</para>
+
+<screen>DEFINE PATCHMAP
+...
+END
+
+DEFINE KEYMAP "Name of Keymap"
+...
+END
+
+DEFINE KEYMAP "Another Keymap"
+...
+END
+
+DEFINE CHANNELMAP
+...
+END
+
+OPTIONS
+...
+END
+</screen>
+
+<para>
+You can see that the <literal>DEFINE</literal> word is used to specify
+which section is going to be started (except for
+<literal>OPTIONS</literal>), and <literal>END</literal> is put at the
+end of each section. </para>
+
+<para>
+You can put comments by starting the line with a
+<literal>&num;</literal> character.
+</para>
+
+<para>
+Please, don't forget to send me your map file by email, so that future
+releases of &kmid; will include support for more non General Midi
+compliant keyboards.
+</para>
+
+<sect2 id="the-patchmap-section">
+<title>The <literal>PATCHMAP</literal> section</title>
+
+<para>
+This section is used to specify how patches are going to be mapped, from
+GM to your keyboard specs . The general usage is:
+</para>
+
+<screen>(Name of GM Patch name)=(<replaceable>N</replaceable>) [AllKeysTo M]
+</screen>
+
+<para>
+Where <replaceable>N</replaceable> is the number that you keyboard needs to
+receive to change the patch to the same that the GM standard does .
+</para>
+
+<para>
+Please note that the left side of the equal sign is ignored, so
+<acronym>GM</acronym> patches are supposed to be in order (from 0 to 127) , and
+so you are not allowed to change the order of the lines nor to omit any of the
+128 instruments.
+</para>
+
+<para>
+The optional <literal>AllKeysTo M</literal> is used to map all notes
+that use that instrument to the <literal>M</literal> key . For example,
+suppose that your midi keyboard doesn't have a Gun Shot sound (GM patch
+127) so you want to map it to a percussion drum (i.e. key 60), which
+sounds similar to a gun shot, then you can put in the 127th line of the
+<literal>PATCHMAP</literal> section:
+</para>
+
+<screen>Gunshot =100 AllKeysTo 60</screen>
+
+
+<para>
+So when a midi file tries to play a note with the patch 127 (gun shot), it will
+be mapped to the patch 100 (your keyboard's percussion patch) and play the note
+60 (independently of the key that was going to be played).
+</para>
+
+<para>
+Please note that when I use the expression <quote>Percussion patch</quote>, I
+mean the patch in which each key plays a different drum, cymbal, tom, maracas
+and so on, and not to a possible sound which some keyboards have and which plays
+a different tone of the same drum with each key.
+</para>
+
+</sect2>
+
+<sect2 id="The-keymap-section">
+<title>The <literal>KEYMAP</literal> section</title>
+
+<para>
+The <literal>KEYMAP</literal> section is used to specify how keys are
+going to be mapped, within a given channel or instrument . The usage is:
+</para>
+
+<screen>DEFINE KEYMAP "Name of Keymap"
+C 0 =0
+C#0 =1
+D 0 =2
+...
+END
+</screen>
+
+<para>
+As with the <literal>PATCHMAP</literal> section, it is very important
+the order of the lines, and that they are all there (the 128 keys).
+</para>
+
+<para>
+As you can define multiple keymaps for different channels and instruments,
+you must give a different name to each one in the first line.
+</para>
+
+<para>
+Keymaps are mainly used to map keys in the percussion channel . Have a
+look at the distributed maps to see some examples.
+</para>
+
+</sect2>
+
+<sect2 id="the-channelmap-section">
+<title>The <literal>CHANNELMAP</literal> section</title>
+
+<para>
+This section can be used to map some channels to different ones . For
+example, if you want to swap the first and second channels, you can
+easily do it within the <literal>CHANNELMAP</literal> section.
+</para>
+
+<para>
+However it is more useful for keyboards that need the percussion
+channel to be in a given channel (the GM standard use the channel 10,
+others use the channel 16 and others use channel 9).
+</para>
+
+<para>
+Note that midi devices use 16 channels, so the <literal>CHANNELMAP</literal>
+section, has 16 lines, from 0 to 15 , as this one:
+</para>
+
+<screen>(N) = (M) [Keymap "Name"] [ForcePatch x]
+</screen>
+
+<para>
+Where <literal>N</literal> is the channel which is mapped to the
+<literal>M</literal> channel . If the <literal>Keymap</literal> option
+is used, the Keymap with name <literal>Name</literal> will be used in
+this channel (this Keymap should be defined earlier in the map file !) .
+If the <literal>ForcePatch</literal> option is used, all events that try
+to change the patch which is used in this channel will be ignored, and
+patch <literal>x</literal> will be used instead.
+</para>
+
+<para>
+The <literal>ForcePatch</literal> option may be useful for example to
+always use the percussion patch on the percussion channel.
+</para>
+
+</sect2>
+
+<sect2 id="the-options-section">
+<title>The <literal>OPTIONS</literal> section</title>
+
+<para>
+The <literal>OPTIONS</literal> section has some general options that can
+be very useful:
+</para>
+
+<screen>OPTIONS
+PitchBenderRatio = r
+MapExpressionToVolumeEvents
+END
+</screen>
+
+<para>
+You can specify both options, only one, or none of them.
+</para>
+
+<para>
+The <literal>PitchBenderRatio r</literal> value, has the ratio by which
+pitch bender events will be multiplied . That is, when a midi file tries
+to send a pitch bender event with a <literal>n</literal> value, the real
+value that will be sent is <literal>n*(r/4096)</literal> (the
+<literal>4096</literal> value is for not having to put decimal points in
+the map file).
+</para>
+
+<para>
+This is used because the <acronym>GM</acronym> standard says that when a
+midi keyboard receives a Pitch Bender event with a 4096 data value, it
+should bend the note to a higher one , but some midi keyboards try to
+bend the initial note by two or more higher notes (even an octave
+higher!) when they receive a 4096 . This can be easily fixed by trying
+different values so that instead of sending a 4096, KMid sends the
+appropriate value.
+</para>
+
+<para>
+When the <literal>MapExpressionToVolumeEvents</literal> option is set in
+the map file, and a midi file try to send an expression event, KMid will
+send a volume event which is understood by more non-GM keyboards, and
+which has a similar effect . There are many midi files which use
+expression events to fade out at the end of a song, so if you feel that
+music should be heard softer and softer, you can turn on this option and
+see if this is what you need, because your midi synthesizer could be
+ignoring the expression events because it doesn't understand them.
+</para>
+
+</sect2>
+
+</sect1>
+
+<sect1 id="using-midimaps">
+<title>Using midi maps</title>
+
+<para>
+To use a midi map, simply open the <guilabel>Midi Setup</guilabel>
+dialog by selecting the <guimenuitem>Midi Setup ...</guimenuitem> entry
+of the <guimenu>Settings</guimenu> menu.
+</para>
+
+<para>
+Then click on <guilabel>Browse ...</guilabel>, select the map file within the open
+file dialog and enjoy the music ! :-)
+</para>
+</sect1>
+</chapter>
+
+<chapter id="advanced-features">
+<title>Advanced features</title>
+
+<sect1 id="the-channel-view">
+<title>The Channel View</title>
+
+<para>
+The Channel view is a window where you are shown a heap of keyboards (one for
+each midi channel). In these keyboards, the notes that are being played with
+each instrument are highlighted so that you can see what is each instrument
+playing.
+</para>
+
+<sect2 id="changing-instruments">
+<title>Changing instruments</title>
+
+<para>
+You can use the Channel View to change the instrument that each channel is
+playing. In each channel there is a combo box where you can select it. Once you
+change it, the green button next to it will change to red to indicate that this
+is not the default instrument.
+</para>
+
+<para>
+If you want to set again the default instrument, click on the red button, and it
+will be automatically set.
+</para>
+
+</sect2>
+
+<sect2 id="changing-the-look">
+<title>Changing the look mode</title>
+
+<para>
+The Channel View has two different ways (for now) to display the played notes,
+you can select them from the <guimenuitem>Channel View Options...</guimenuitem>
+item in the <guimenu>Settings</guimenu> menu.
+</para>
+
+<para>
+You can choose between a mode in which played keys are pressed, as if it were a
+normal piano (<guilabel>3D look</guilabel>), or a mode in which keys are also
+filled with red color, so that pressed keys are easily recognized (<guilabel>3D
+- filled</guilabel>). If you play the piano, or any other music instrument, you
+can use this view to learn to play a song by yourself. I've used this technique
+and it (along with a tempo reduction) is great to
+learn new compositions.
+</para>
+
+</sect2>
+
+</sect1>
+
+<sect1 id="the-tempo-lcd">
+<title>The Tempo <acronym>LCD</acronym></title>
+
+<para>
+This shows the tempo in which a song is played, that is, the velocity of the
+song. The higher this number is, the faster the song will play.
+</para>
+
+<para>
+You can also change the tempo of the song, so if a song plays too fast for you
+to follow the lyrics, you can make it play slower. To change the tempo, you can
+use the arrows that appear at each sides of the <acronym>LCD</acronym>.
+</para>
+
+<para>
+Once you have changed the tempo, you can get back the default one by doing a
+double click on the <acronym>LCD</acronym>.
+</para>
+
+</sect1>
+
+</chapter>
+
+<chapter id="key-bindings">
+<title>Key bindings</title>
+
+<informaltable>
+<tgroup cols="2">
+<thead>
+<row>
+<entry>Key</entry>
+<entry>Action</entry>
+</row>
+</thead>
+<tbody>
+<row>
+<entry><keycap>Space</keycap></entry>
+<entry>Play the loaded song, if it isn't playing, or pause it, if it's already
+playing.</entry>
+</row>
+<row>
+<entry><keycap>Backspace</keycap></entry>
+<entry>Stop playing</entry>
+</row>
+<row>
+<entry><keycap>Right Arrow</keycap></entry>
+<entry>Next song in current collection</entry>
+</row>
+<row>
+<entry><keycap>Left Arrow</keycap></entry>
+<entry>Previous song in current collection</entry>
+</row>
+<row>
+<entry><keycap>Up Arrow</keycap></entry>
+<entry>Scroll lyrics one line up</entry>
+</row>
+<row>
+<entry><keycap>Down Arrow</keycap></entry>
+<entry>Scroll lyrics one line down</entry>
+</row>
+<row>
+<entry><keycap>Page Up</keycap></entry>
+<entry>Scroll lyrics one page up</entry>
+</row>
+<row>
+<entry><keycap>Page Down</keycap></entry>
+<entry>Scroll lyrics one page down</entry>
+</row>
+<row>
+<entry><keycap>1</keycap></entry>
+<entry>Display text events</entry>
+</row>
+<row>
+<entry><keycap>2</keycap></entry>
+<entry>Display lyric events</entry>
+</row>
+<row>
+<entry><keycombo action="simul">&Ctrl;<keycap>O</keycap></keycombo></entry>
+<entry>Open a song</entry>
+</row>
+<row>
+<entry><keycombo action="simul">&Ctrl;<keycap>Q</keycap></keycombo></entry>
+<entry>Quit &kmid;</entry>
+</row>
+<row>
+<entry><keycap>F1</keycap></entry>
+<entry>Open this document</entry>
+</row>
+</tbody>
+</tgroup>
+</informaltable>
+
+</chapter>
+
+<chapter id="frequently-asked-questions">
+<title>Frequently Asked Questions (FAQ)</title>
+
+<qandaset>
+<qandaentry>
+<question>
+<para>
+What is exactly a midi file?
+</para>
+</question>
+
+<answer>
+<para>A Midi file is a file that contains the information on how to play
+a song, that is, it contains simply the notes, the rhythm,
+velocity,&etc; This implies that the same midi file, when played in two
+different devices, can produce very different results, as well as a
+given staff can be played very differently by two different musicians.
+</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>
+I can get better sound with a mp3/wav player, why should I use &kmid;?
+</para>
+</question>
+<answer>
+<para>
+Well, I cannot force anyone to use &kmid;, but a typical midi file
+occupies 50 Kb. while a typical mp3 file occupies 4 Mb. (and that is a
+1:80 compression ratio :-) . And with a good synthesizer device, you can
+get a comparable sound quality. Even more, with a midi file, you can
+change individual instruments, change the velocity of a song, &etc; so
+you have more overall control.
+</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>
+I can't get my AWE card to work with KMid, what can I do?
+</para>
+</question>
+<answer>
+<para>
+This can happen when you get a binary distribution (rpm, deb) of &kmid;. It
+happens because &kmid; was compiled without awe support. If it doesn't
+work, then you must download a source code distribution (for example, from
+<ulink url="http://www.arrakis.es/~rlarrosa/kmid.html">&kmid;'s homepage</ulink>)
+</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>
+I want to add a whole folder to a collection, but having to add the midi
+files one by one is not funny.
+</para>
+</question>
+
+<answer>
+<para>
+I agree, that's why &kmid; supports Drag &amp; Drop. Just open, in &konqueror;,
+the folder you want to add, select all the files, drag them and drop them in
+&kmid;.
+</para>
+<para>
+Be sure to set the <guimenuitem>AutoAdd to Collection</guimenuitem> option before, so that the
+files will be added to the current collection. If you don't do this, files will
+be added to the Temporary Collection.
+</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>
+I can't follow the lyrics, it's playing too fast!
+</para>
+</question>
+<answer>
+<para>
+You can press the left arrow of the tempo <acronym>LCD</acronym> to make it play
+slower. Remember that you can do a double click on the <acronym>LCD</acronym> to
+get the default tempo.
+</para>
+</answer>
+</qandaentry>
+</qandaset>
+
+</chapter>
+
+<chapter id="final-notes">
+<title>Final notes</title>
+
+<sect1 id="some-tips-and-tricks">
+<title>Some tips and tricks</title>
+
+<para>
+I will include some tips so that you can take fully advantage from all
+the features of &kmid;:
+</para>
+
+<variablelist>
+<varlistentry>
+<term>Opening files</term>
+<listitem>
+<para>I always keep a &kde; desktop with a &konqueror; window in my root
+midi folder, and &kmid; in this desktop (if playing a midi file) or
+sticky (if playing a karaoke file :-)). This way, when the active
+collection finishes, or I want to play some file, I just go to the
+konqueror; window, select the desired files and Drag &amp; Drop to the
+&kmid;'s window.
+</para>
+
+<para>
+Suppose that you want to play some midi files, but don't want to add
+them to any collection, well, just turn off the <guimenuitem>AutoAdd to
+Collection</guimenuitem> option in the <guimenu>Collections</guimenu>
+menu, and open the files, they will be added to the Temporary
+Collection.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>Another method to create a new Collection</term>
+<listitem>
+<para>
+Suppose that you have midi files <filename>A.mid</filename>,
+<filename>B.mid</filename> and <filename>C.mid</filename>. At first you
+only want to play the first midi file, so you unselect
+<guimenuitem>AutoAdd</guimenuitem> and open
+<filename>A.mid</filename>. You get then a Temporary Collection with
+only one midi file.
+</para>
+<para>
+Then you decide to play also B and C, and make a collection with all them, what
+do you do?
+</para>
+<para>
+Easy, select <guimenuitem>AutoAdd</guimenuitem> and open
+<filename>B.mid</filename> and <filename>C.mid</filename> (by any of the
+multiple methods), they will be automatically added to the Temporary
+Collection, that will then have <filename>A.mid</filename>,
+<filename>B.mid</filename> and <filename>C.mid</filename>. At this
+point, you can open the <guilabel>Organize Collections</guilabel>
+dialog, select the Temporary Collection, and click on the
+<literal>Copy</literal> button, enter the name of the new collection,
+and you are done . You already have a new collection, which holds the
+A,B and C midi files, and that is not deleted when you close &kmid;.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+</sect1>
+
+<sect1 id="hall-of-kmids-friends">
+<title>Hall of &kmid;'s friends</title>
+
+<para>
+These are some folks who have sent me midi files or a postcard, thanks
+to everyone! Hearing those songs and watching those postcards will keep
+me programming more and more on &kmid;.
+</para>
+
+<itemizedlist>
+<listitem>
+<para>Ola Sigurdson - <literal>Taking Care of Business</literal> (Bachman
+Turner Overdrive)</para>
+</listitem>
+<listitem>
+<para>EG Lim - A really very nice postcard from Penang.</para>
+</listitem>
+<listitem>
+<para>Guenther Starnberger - <literal>Amadeus</literal> (Falco) and
+<literal>Schrei Nach Liebe</literal> (Die Aerzte)</para>
+</listitem>
+<listitem>
+<para>Leandro Terr&eacute;s - <literal>All That She Wants</literal> and
+<literal>The Sign</literal> (Ace of Base)</para>
+</listitem>
+<listitem>
+<para>Nick Stoic - Two midi files</para>
+</listitem>
+</itemizedlist>
+
+</sect1>
+
+<sect1 id="about-the-author">
+<title>About the author</title>
+
+<para>
+&kmid; has been made by Antonio Larrosa Jim&eacute;nez, in M&aacute;laga
+(Spain). I am a student of Mathematics at the University of
+M&aacute;laga, currently I'm doing the third course, so I don't have
+much free time for my hobbies, but I always try to get some :-) . My
+hobbies include : Programming, collecting midi files, playing music and
+proving theorems ;-).
+</para>
+
+<para>
+If you want to know where to download midi/karaoke files, you have any
+question, a bug to report, an idea or a feature you'd like to see in
+&kmid; or just want to make me happy, feel free to send me an email to:
+<email>larrosa@kde.org</email> or <email>antlarr@arrakis.es</email>
+</para>
+
+<para>or write to me by snail-mail at:
+</para>
+
+<literallayout> Antonio Larrosa Jimenez
+Rio Arnoya 10 5B
+Malaga (Spain)
+</literallayout>
+
+<para>
+You will really make me happy if you send me a postcard from where you
+live, or a midi/karaoke file from a local music group of your country
+. Everyone who sends me a postcard or a midi file will have his/her name
+in the Hall of &kmid;'s friends of this file (provided they don't oppose
+to this). Please contact me before sending me any midi files because I
+may have it already.
+</para>
+
+<para>
+I'd like stress that &kmid; has been done completely on free time,
+without any monetary support from any company nor particular. So please
+keep in mind when you use it, that the only think that keep me working
+on this is getting some feedback from its users (a postcard, or just an
+email).
+</para>
+
+<para>
+I would like to thanks the following persons their help in developing &kmid;:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>
+Paul J. Leonard <email>P.J.Leonard@bath.ac.uk</email> - Support for AWE
+cards
+</para>
+</listitem>
+<listitem>
+<para>
+Sebestyen Zoltan <email>szoli@digo.inf.elte.hu</email>- FreeBSD port and
+AWE testing
+</para>
+</listitem>
+<listitem>
+<para>
+Christian Esken <email>esken@kde.org</email> - For organizing the KDE
+multimedia efforts
+</para>
+</listitem>
+<listitem>
+<para>
+Stephan Kulow <email>coolo@kde.org</email>- Configure scripts and help
+with <command>automake</command> and <command>CVS</command>
+</para>
+</listitem>
+<listitem>
+<para>
+Jaroslav Kysela - Help in doing the &Linux; Ultrasound Project driver
+support
+</para>
+</listitem>
+<listitem>
+<para>
+Takashi Iwai and Joseph H. Buehler - Fix for AWE cards pitch being
+too high
+</para>
+</listitem>
+<listitem>
+<para>
+Adrian Knoth - For giving me good news and many suggestions
+</para>
+</listitem>
+<listitem>
+<para>
+Kevin Street - Patch to support FreeBSD 3.0
+</para>
+</listitem>
+<listitem>
+<para>
+Thanks go also to Jose Luis Sanchez for his testing of GUS support,
+Ignacio Garcia for testing the AWE support, Hans Petter Bieker, Ola
+Sigurdson, Marc Diefenbruch, Peter Gritsch, Magnus Pfeffer, Urko Lusa,
+Peter-Paul Witta, Thorsten Westheider, Ulrich Cordes and everyone that
+sent me a patch, bug report or just an email to give me encouragement.
+</para>
+</listitem>
+<listitem>
+<para>
+And of course to all the fabulous musicians over the net that keep giving
+us those wonderful midi and karaoke files.
+</para>
+</listitem>
+</itemizedlist>
+
+<!-- TRANS:CREDIT_FOR_TRANSLATORS -->
+
+</sect1>
+
+<sect1 id="copyright-and-license">
+<title>Copyrights and License</title>
+
+<para>&kmid; is copyright Antonio Larrosa Jim&eacute;nez, 1999-2001</para>
+
+<para>Documentation is copyright Antonio Larrosa Jim&eacute;nez 1999,
+2001</para>
+
+&underFDL;
+&underGPL;
+
+</sect1>
+
+</chapter>
+
+<appendix id="installation">
+<title>Installation</title>
+
+<sect1 id="how-to-obtain-kmid">
+<title>How to obtain &kmid;</title>
+
+&install.intro.documentation;
+
+<para>
+Additionally, &kmid; can be found on its homepage, which is at <ulink
+url="http://www.arrakis.es/~rlarrosa/kmid.html">
+http://www.arrakis.es/~rlarrosa/kmid.html</ulink>. In the homepage, you can
+follow its development, see some information about it, some screenshots, a list
+of sites from where you can download more karaoke songs, &etc;
+</para>
+
+</sect1>
+
+<sect1 id="requirements">
+<title>Requirements</title>
+
+<para>&kmid; requires to work:</para>
+
+<itemizedlist>
+<listitem>
+<para>
+&kde;. Well, you probably already have this :-)
+</para>
+</listitem>
+<listitem>
+<para>
+A sound card. A good soundcard and/or external synthesizer are
+recommended, as the sound quality depends greatly in your soundcard,
+it's not the same to play the music using an FM device, than using an
+AWE card.
+</para>
+
+<para>
+If you don't have a soundcard, you can still compile &kmid; with
+<literal>MODE_DEMO_ONLYVISUAL</literal> defined and it will run as if
+you had one (but you'll get no music, of course :-( ).
+</para>
+</listitem>
+</itemizedlist>
+
+</sect1>
+
+<sect1 id="compilation-and-installation">
+<title>Compilation and Installation</title>
+
+&install.compile.documentation;
+
+<para>
+I've included some examples that are installed in <filename
+class="directory">$dollar;<envar>KDEDIR</envar>/share/apps/kmid</filename>
+</para>
+
+<para>
+If you run into any problem, don't hesitate to contact any of the &kde; mailing
+list, or send a report directly to me.
+</para>
+
+</sect1>
+
+</appendix>
+
+</book>
+<!--
+Local Variables:
+mode: sgml
+sgml-omittag: nil
+sgml-shorttag: t
+End:
+-->
diff --git a/doc/kmix/Makefile.am b/doc/kmix/Makefile.am
new file mode 100644
index 00000000..085981d9
--- /dev/null
+++ b/doc/kmix/Makefile.am
@@ -0,0 +1,4 @@
+
+KDE_LANG = en
+KDE_DOCS = AUTO
+
diff --git a/doc/kmix/index.docbook b/doc/kmix/index.docbook
new file mode 100644
index 00000000..53db80b9
--- /dev/null
+++ b/doc/kmix/index.docbook
@@ -0,0 +1,503 @@
+<?xml version="1.0" ?>
+<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
+ <!ENTITY kappname "&kmix;">
+ <!ENTITY package "kdemultimedia">
+ <!ENTITY % English "INCLUDE" > <!-- change language only here -->
+ <!ENTITY % addindex "IGNORE">
+]>
+
+<book lang="&language;">
+
+<bookinfo>
+<title>The &kmix; Handbook</title>
+
+<authorgroup>
+<author>
+<firstname>Matt</firstname>
+<surname>Johnston</surname>
+<affiliation>
+<address>&Matt.Johnston.mail;</address>
+</affiliation>
+</author>
+
+<othercredit role="developer">
+<firstname>Christian</firstname>
+<surname>Esken</surname>
+<affiliation><address><email>esken@kde.org</email></address></affiliation>
+<contrib>Developer</contrib>
+</othercredit>
+
+<othercredit role="developer">
+<firstname>Helio</firstname>
+<surname>Chissini de Castro</surname>
+<affiliation><address><email>helio@kde.org</email></address></affiliation>
+<contrib>Developer</contrib>
+</othercredit>
+
+
+<othercredit role="developer">
+<firstname>Stefan</firstname>
+<surname>Schimanski</surname>
+<affiliation><address><email>1Stein@gmx.de</email></address></affiliation>
+<contrib>Developer</contrib>
+</othercredit>
+
+<othercredit role="reviewer">
+<firstname>Lauri</firstname>
+<surname>Watts</surname>
+<affiliation><address>&Lauri.Watts.mail;</address></affiliation>
+<contrib>Reviewer</contrib>
+</othercredit>
+<!-- TRANS:ROLES_OF_TRANSLATORS -->
+</authorgroup>
+
+<copyright>
+<year>1996</year><year>2005</year>
+<holder>Christian Esken &amp; &Matt.Johnston;</holder>
+</copyright>
+
+<legalnotice>&FDLNotice;</legalnotice>
+
+<date>2007-01-05</date>
+<releaseinfo>2.6.1</releaseinfo>
+
+<abstract><para>&kmix; is an application to allow you to change the volume of
+your sound card.</para></abstract>
+
+<keywordset>
+<keyword>KDE</keyword>
+<keyword>KMix</keyword>
+<keyword>kdemultimedia</keyword>
+<keyword>sound</keyword>
+<keyword>volume</keyword>
+<keyword>mixer</keyword>
+</keywordset>
+
+</bookinfo>
+
+<chapter id="introduction">
+<title>Introduction</title>
+
+<para>&kmix; is &kde;'s soundcard mixer program. Though small, it is
+full-featured. The program should give controls for each of your
+soundcards.</para>
+
+<para>&kmix; supports several platforms and sound drivers:</para>
+
+<itemizedlist>
+<listitem><para> The ALSA soundcard driver.</para></listitem>
+<listitem><para> All Open Sound System platforms. Explicitly tested
+are &Linux;, FreeBSD, NetBSD and BSDI.</para></listitem>
+<listitem><para> &Solaris; based machines.</para></listitem>
+<listitem><para> &IRIX; based machines.</para></listitem>
+<listitem><para> &HP-UX; based machines.</para></listitem>
+</itemizedlist>
+
+<para>If you have both ALSA and Open Sound System drivers installed, &kmix; will use the ALSA driver.</para>
+
+</chapter>
+
+<chapter id="working-with-kmix">
+<title>Working with &kmix;</title>
+
+<sect1 id="basic-usage">
+<title>Basics</title>
+
+<para>&kmix; usage is straightforward. Every mixer control that your
+soundcard provides is represented by a volume slider. Mono controls
+have a single slider, stereo controls can have either one or two
+sliders, depending on your choice. Additionally there is a panning
+slider at the bottom of the &kmix; window. If you have more than one soundcard, a list will be displayed on the top of the window, where you can choose between your soundcards.
+
+<screenshot>
+<screeninfo>The &kmix; Main Window</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="kmix-window.png" format="PNG"/></imageobject>
+<textobject><phrase>The &kmix; Main Window</phrase></textobject>
+</mediaobject>
+</screenshot>
+
+</para>
+
+
+<para>The Window can have up to three sections with different soundcard controls:
+<guilabel>Output</guilabel>, <guilabel>Input</guilabel> and <guilabel>Switches</guilabel>.
+Those sections contain volume sliders, switches for enabling/disabling record or playback, and multiple-choice selectors.
+
+<simplelist>
+<member><guilabel>Output</guilabel>: This holds the controls that are most likely playback related, like the <guilabel>Master</guilabel> volume control.</member>
+<member><guilabel>Input</guilabel>: This holds all controls that are most likely record related, like <guilabel>Capture</guilabel>.</member>
+<member><guilabel>Switches</guilabel>: This holds all controls, that allows only to switch some functionality ON or OFF (like "Mic Boost (+20dB)"), and multiple-choice controls (like <guilabel>Mic Select</guilabel>: <guilabel>Mic1</guilabel> or <guilabel>Mic2</guilabel>).</member>
+</simplelist>
+</para>
+
+<para>Besides volumes controls, &kmix; also features LED's. The general coloring rule is:</para>
+<simplelist>
+<member>Green: A LED dealing with playback</member>
+<member>Red: A LED dealing with recording</member>
+<member>Yellow: A LED dealing with special soundcard capabilities</member>
+</simplelist>
+<para>These 3 colors come in two flavours: A lit LED means ON, a non-lit LED means OFF.</para>
+</sect1>
+
+<sect1 id="volume-sliders">
+<title>Volume controls</title>
+
+<para>The volume controls in section <guilabel>Output</guilabel> and <guilabel>Input</guilabel> consist of (top to bottom):
+<screenshot>
+<screeninfo>Volume control (<guilabel>Input</guilabel> Section)</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="kmix-channel-playback.png" format="PNG"/></imageobject>
+<textobject><phrase>Volume control (<guilabel>Input</guilabel> Section)</phrase></textobject>
+</mediaobject>
+</screenshot>
+
+<simplelist>
+<member>An icon, representing the function of the control.</member>
+<member>A volume value indicator (optional).</member>
+<member>A Green Mute LED, that allows you to mute a control (light goes off/gets dark) or unmute it again (light goes on/gets bright).</member>
+<member>A slider, for volume control (Hint: You can hide the label on the slider, for example if the mixer takes too much of your screen space).</member>
+<member>If a control supports recording, there will be a red Record LED. If the LED is lit (bright red), the control is selected for recording. If it is not lit (dark red), the control is NOT selected for recording.</member>
+</simplelist>
+
+<screenshot>
+<screeninfo>Volume control with Recording Switch (<guilabel>Output</guilabel> Section)</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="kmix-channel-record.png" format="PNG"/></imageobject>
+<textobject><phrase>Volume control with Recording Switch (<guilabel>Output</guilabel> Section)</phrase></textobject>
+</mediaobject>
+</screenshot>
+
+</para>
+
+
+<para>
+Most of these controls have a context menu, accessible by right clicking on the control or device icon. Several entries are possible in the context menu, but only those applicable are shown.
+</para>
+
+<variablelist>
+<varlistentry>
+<term><guimenuitem>Split Channels</guimenuitem></term>
+<listitem><para>Show either one or two sliders. This is only applicable to
+stereo devices. The right slider controls right side volume, and the left
+controls left side volume.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Muted</guimenuitem></term>
+<listitem><para>Mute or unmute the device</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Hide</guimenuitem></term>
+<listitem><para>If you are not interested in regulating this device you can hide it with this option. If you want to show it again, you can only do this by selecting the <guimenuitem>Channels</guimenuitem> option (see below)</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Configure Global Shortcuts...</guimenuitem></term>
+<listitem><para>You can control a device with your keyboard. Use this menu option to show the &kde; <guilabel>Configure Shortcuts</guilabel> dialog. Here you can define keys for increasing and decreasing volume and for muting the device. The keys are global and operate also when &kmix; is iconified or docked.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Channels</guimenuitem></term>
+<listitem><para>You will get a dialog box where you can configure per section (<guilabel>Output</guilabel>, <guilabel>Input</guilabel>, <guilabel>Switches</guilabel>), which channels you want to see.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Show/Hide Menubar</guimenuitem></term>
+<listitem><para>This option is not device specific, but affects the mixer window. You can hide and show the menubar with this option. You can also do this by pressing the shortcut (usually <keycombo action="simul">&Ctrl;<keycap>M</keycap></keycombo>)</para></listitem>
+</varlistentry>
+
+</variablelist>
+</sect1>
+
+<sect1 id="switches">
+<title>Switches and Multiple-Choice selectors</title>
+
+<para>The controls in the section <guilabel>Switches</guilabel> consist of a LED and a short label describing the function. The <guilabel>Switches</guilabel> section can also contain Multiple-Choice selectors. Please note that these controls are often very special and usually don't need to be changed by the average user. The context menu contains the <guimenuitem>Channels</guimenuitem> and <guimenuitem>Show/Hide Menubar</guimenuitem> entries already described.
+
+<screenshot>
+<screeninfo>Switches and Multiple-Choice selectors (<guilabel>Switches</guilabel> Section)</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="kmix-channel-switches.png" format="PNG"/></imageobject>
+<textobject><phrase>Switches and Multiple-Choice selectors (<guilabel>Switches</guilabel> Section)</phrase></textobject>
+</mediaobject>
+</screenshot>
+
+Please remember, red LED's are recording related, yellow LED's control special soundcard capabilities.
+
+The screenshot above shows from left to right an unlit <guilabel>IEC958 Output</guilabel> LED (yellow = special control), a lit <guilabel>Mix</guilabel> LED (red = Recording related), an unlit recording related LED, a lit special control and one multiple-choice selector (<guilabel>PCM Out Path &amp; Mute</guilabel>).
+
+If you are uncertain about the meaning of a control, please ask your soundcard driver supplier (for most current &Linux; distributions this is <ulink url="http://www.alsa-project.org">ALSA</ulink>).
+</para>
+
+</sect1>
+
+<sect1 id="panning-slider">
+<title>Panning slider</title>
+
+<para>With this slider you can control the volume distribution between left and
+right speaker. This slider is an overall regulator, which affects the Master Volume.
+The middle position is the default. Dragging
+the slider to the left lowers the volume of the right speaker, dragging it to
+the right vice versa. Of course, these might be swapped if your speakers aren't
+positioned correctly.</para>
+<para>For Surround Systems please be aware that the <guilabel>Master</guilabel> device often regulates only the Front Speakers.
+This is a limitation of your Soundcard driver.</para>
+<para>If your soundcard has no <guilabel>Master</guilabel> device, some other device might get picked by &kmix; - for most people this is the <guilabel>Wave</guilabel> (or <guilabel>PCM</guilabel>) control.</para>
+
+</sect1>
+
+<sect1 id="configuration-options">
+<title>Configuration options</title>
+
+<para>Use <menuchoice><guimenu>Settings</guimenu><guimenuitem>Configure &kmix;...</guimenuitem></menuchoice>
+from the menubar to choose preferences. These items are:</para>
+
+<variablelist>
+<varlistentry>
+<term><guilabel>Dock into panel</guilabel></term>
+<listitem><para>If checked, &kmix; will dock in the systray when pressing the window close button. If not checked, &kmix; will quit on pressing the window close button. Attention: After quitting you will not be able to control the volume if you have assigned keys for this.</para></listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Enable system tray volume control</guilabel></term>
+<listitem><para>If enabled, a left-clicking the &kmix; dock icon will show a popup window with a volume control for the preferred device (Hint: currently you cannot change this device - it is selected by &kmix; instead). If the option is disabled, the &kmix; Main Window will be shown on a left-click on the &kmix; dock icon.</para></listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Show tickmarks</guilabel></term>
+<listitem><para>Show lines to mark positions on the sliders.</para></listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Show labels</guilabel></term>
+<listitem><para>Display labels for each of the sound devices. Wether this item
+is checked or not, by holding the mouse over the icon for each device, you can see this information.</para></listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Restore volumes on login</guilabel></term>
+<listitem><para>Let &kde; restore the volumes when you Login: This restores your personal volume levels, stored when you last logged out. If your Operating System saves the volume levels, you might not need this option (but on a computer with multiple users it is still needed).</para></listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Volume Values:</guilabel></term>
+<listitem><para>Select if and how volume values are displayed: <guilabel>None</guilabel>, <guilabel>Absolute</guilabel> or <guilabel>Relative</guilabel>.</para></listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Slider Orientation</guilabel></term>
+<listitem><para>With this option you can turn the &kmix; main window content by 90 degrees. Sliders, texts and everything else (if applicable) is rotated. There are some exclusions to this rule, most notably the menubar, the mixer selector (if shown at all), the panning slider and the multiple-choice selectors.</para></listitem>
+</varlistentry>
+</variablelist>
+
+</sect1>
+
+
+</chapter>
+
+
+<chapter id="working-with-kmixapplet">
+<title>The &kmix; panel applet</title>
+
+<para>
+The &kmix; panel applet is an alternative interface to &kmix;.
+You can add it to the &kde; panel by selecting <guimenuitem>Add Applet to Panel...</guimenuitem> in the <guimenu>Panel Menu</guimenu> or context menu.
+Choose <guilabel>Sound Mixer</guilabel> and click the <guibutton>Add to Panel</guibutton> or double click <guilabel>Sound Mixer</guilabel>.
+</para>
+
+<para>
+You can work with the &kmix; applet as described for the <link linkend="working-with-kmix">main window</link> - including the context menu.
+Due to the limited space in the panel there are differences:
+
+<simplelist>
+<member>No main menu available.</member>
+<member>If you have multiple soundcards, you cannot change the selected mixer after the initial selection.</member>
+<member>No dock icon. If you want to use the dock item you must additionally start &kmix; with
+<menuchoice><guimenu>K-Menu</guimenu><guisubmenu>Multimedia</guisubmenu><guimenuitem>&kmix; Sound Mixer</guimenuitem></menuchoice>.</member>
+<member>Icons only available when panel is big enough.</member>
+<member>No device name labels available.</member>
+<member>Configuration is done via panel menu - you can configure colors and slider direction there.</member>
+<member>No automatic volume saving. If you want your volumes saved when you logout for later restauration, you must also start &kmix; from the K-Menu.</member>
+</simplelist>
+
+</para>
+</chapter>
+
+
+<chapter id="advanced-kmix">
+<title>Advanced &kmix; features</title>
+
+<warning><para>This chapter describes &kmix; functionality that is targeted at the experienced user. Most users will never have a need for this functionality, so you can safely skip this chapter</para></warning>
+
+<sect1 id="dcop-overview">
+<title>The &DCOP; Interface</title>
+
+<para>
+Sometimes you want to do specialized things.
+Things like controlling the mixer from another application or muting the master device every day at 10pm.
+&kmix; has a &DCOP; interface that allows you to achieve much with minimal work.
+You can start a shell and type <userinput><command>dcop kmix</command></userinput> to start exploring the &kmix; &DCOP; interface.
+The &kmix; specific interfaces are:</para>
+
+<variablelist>
+<varlistentry>
+<term><userinput><command>Mixer0</command></userinput></term>
+<listitem><para>Allows manipulating the first mixer. You can set volume levels, mute the device, change balance, retrieve the mixer name and much more. Type <userinput><command>dcop kmix Mixer0</command></userinput> if you want to explore all the features. There are more entries like <userinput><command>Mixer1</command></userinput> in case multiple soundcards are installed.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><userinput><command>kmix-mainwindow#1</command></userinput></term>
+<listitem><para>The &GUI; window can be controlled with this command. You can hide and show the window, resize it and much more. Type <userinput><command>dcop kmix kmix-mainwindow#1</command></userinput> if you want to explore all the features.</para></listitem>
+</varlistentry>
+</variablelist>
+
+</sect1>
+
+
+<sect1 id="dcop-examples">
+<title>&DCOP; Examples</title>
+
+<variablelist>
+<varlistentry>
+<term><userinput><command>dcop kmix kmix-mainwindow#1 hide</command></userinput></term>
+<listitem><para>Hides the &GUI; window. Use <userinput><command>dcop kmix kmix-mainwindow#1 show</command></userinput> or the dock icon to show it again.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><userinput><command>dcop kmix kmix-mainwindow#1 resize 1 1</command></userinput></term>
+<listitem><para>Resizes the &GUI; window to the smallest size possible. This is the size so that all sliders (and other &GUI; elements) will fit into the window.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><userinput><command>dcop kmix Mixer0 mixerName</command></userinput></term>
+<listitem><para>Tells the name of the first Mixer, for example <computeroutput>Sound Fusion CS46xx</computeroutput>.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><userinput><command>dcop kmix Mixer1 setVolume 0 10</command></userinput></term>
+<listitem><para>Sets the volume on the second mixer, device 0 to 10 percent. Device 0 is often the master device, but not always. If you want to quiet down the (first) master device on your second soundcard, you can use <userinput><command>dcop kmix Mixer1 setMasterVolume 0</command></userinput></para></listitem>
+</varlistentry>
+</variablelist>
+
+<para>You can directly execute these commands from a shell that you started from inside &kde;. If you need to execute dcop commands from somewhere else, for example from a crontab script, you need to define the <envar>DCOPSERVER</envar> environment variable (as seen in the first line of your <filename>~/.DCOPserver_hostname_:0</filename> file), for example:</para>
+
+<programlisting>
+#!/bin/sh
+DCOPSERVER=`cat /home/yourhome/.DCOPserver_yourhostname_:0 | grep local`
+export DCOPSERVER
+dcop kmix Mixer0 setMasterVolume 0
+</programlisting>
+</sect1>
+
+<sect1 id="tips-and-tricks">
+<title>Tips and Tricks</title>
+<sect2>
+<title>Using ALSA and OSS driver at the same time</title>
+<para>&kmix; on &Linux; can use either the ALSA driver or the OSS driver, not both. If you really need to use both drivers at the same time (a very rare situation), you can do it as follows: Quit &kmix; and add the following line to your <filename>kmixrc</filename> file in the global configuration section.</para>
+<programlisting>MultiDriver=true</programlisting>
+<para>Start &kmix; again. If you click <menuchoice><guimenu>Help</guimenu><guimenuitem>Hardware Information</guimenuitem></menuchoice> you should see <guilabel>Sound drivers used: ALSA0.9 + OSS</guilabel> and <guilabel>Experimental multiple-Driver mode activated</guilabel>.</para>
+
+<warning><para>You will probably see all of your mixers doubled.</para>
+<para>There is no support for this kind of configuration.</para></warning>
+</sect2>
+
+</sect1>
+
+</chapter>
+
+<chapter id="credits">
+<title>Credits and License</title>
+
+<para>Main developers</para>
+
+<itemizedlist>
+<listitem><para>Copyright 1996-2000 Christian Esken</para></listitem>
+<listitem><para>Copyright 2000-2003 Christian Esken &amp; Stefan Schimanski</para></listitem>
+<listitem><para>Copyright 2003-2005 Christian Esken &amp; Helio Chissini de Castro</para></listitem>
+</itemizedlist>
+
+<para>Contributors:</para>
+
+<itemizedlist>
+<listitem><para>Christian Esken <email>esken@kde.org</email></para></listitem>
+<listitem><para>Stefan Schimanski <email>1Stein@gmx.de</email></para></listitem>
+<listitem><para>Paul Kendall <email>paul@orion.co.nz</email> - &SGI;
+Port</para></listitem>
+<listitem><para>Sebestyen Zoltan <email>szoli@digo.inf.elte.hu</email> - FreeBSD
+Fixes</para></listitem>
+<listitem><para>Faraut Jean-Louis <email>jlf@essi.fr</email> - &Solaris;
+Fixes</para></listitem>
+<listitem><para>Nick Lopez <email>kimo_sabe@usa.net</email> - ALSA
+Port</para></listitem>
+<listitem><para>&Helge.Deller; <email>deller@gmx.de</email> - &HP;-UX
+Port</para></listitem>
+<listitem><para>Lennart Augustsson <email>augustss@cs.chalmers.se</email> - *BSD
+Fixes</para></listitem>
+</itemizedlist>
+
+<para>Documentation copyright 2000 &Matt.Johnston;
+&Matt.Johnston.mail;</para>
+
+<para>Updated 2003 to match &kmix; V1.91 by Christian Esken
+<email>esken@kde.org</email></para>
+
+<para>Updated 2005 to match &kmix; V2.2 by Christian Esken
+<email>esken@kde.org</email></para>
+
+<para>Updated 7/2005 to match &kmix; V2.6 by Christian Esken
+<email>esken@kde.org</email></para>
+
+<para>Based on documentation by Christian Esken
+<email>esken@kde.org</email></para>
+<!-- TRANS:CREDIT_FOR_TRANSLATORS -->
+
+&underFDL;
+&underGPL;
+
+</chapter>
+
+<appendix id="installation">
+<title>Installation</title>
+
+<sect1 id="getting-kmix">
+<title>How to obtain &kmix;</title>
+
+&install.intro.documentation;
+
+</sect1>
+
+<sect1 id="requirements">
+<title>Requirements</title>
+
+<para>Obviously, &kmix; is only of use if you have a soundcard.
+&kmix; supports several platforms and sound drivers:</para>
+
+<itemizedlist>
+<listitem><para> All Open Sound System platforms. Explicitly tested
+are &Linux;, FreeBSD, NetBSD and BSDI.</para></listitem>
+<listitem><para> &Solaris; based machines.</para></listitem>
+<listitem><para> &IRIX; based machines.</para></listitem>
+<listitem><para> The ALSA soundcard driver.</para></listitem>
+<listitem><para> &HP-UX; based machines.</para></listitem>
+</itemizedlist>
+
+</sect1>
+
+<sect1 id="compilation">
+<title>Compilation and Installation</title>
+
+&install.compile.documentation;
+
+</sect1>
+
+</appendix>
+
+</book>
+<!--
+Local Variables:
+mode: sgml
+sgml-omittag: nil
+sgml-shorttag: t
+End:
+-->
+
diff --git a/doc/kmix/kmix-channel-playback.png b/doc/kmix/kmix-channel-playback.png
new file mode 100644
index 00000000..11e65ae0
--- /dev/null
+++ b/doc/kmix/kmix-channel-playback.png
Binary files differ
diff --git a/doc/kmix/kmix-channel-record.png b/doc/kmix/kmix-channel-record.png
new file mode 100644
index 00000000..7d1d3b39
--- /dev/null
+++ b/doc/kmix/kmix-channel-record.png
Binary files differ
diff --git a/doc/kmix/kmix-channel-switches.png b/doc/kmix/kmix-channel-switches.png
new file mode 100644
index 00000000..497a12a5
--- /dev/null
+++ b/doc/kmix/kmix-channel-switches.png
Binary files differ
diff --git a/doc/kmix/kmix-window.png b/doc/kmix/kmix-window.png
new file mode 100644
index 00000000..ab473dd7
--- /dev/null
+++ b/doc/kmix/kmix-window.png
Binary files differ
diff --git a/doc/krec/Makefile.am b/doc/krec/Makefile.am
new file mode 100644
index 00000000..085981d9
--- /dev/null
+++ b/doc/krec/Makefile.am
@@ -0,0 +1,4 @@
+
+KDE_LANG = en
+KDE_DOCS = AUTO
+
diff --git a/doc/krec/index.docbook b/doc/krec/index.docbook
new file mode 100644
index 00000000..38665c91
--- /dev/null
+++ b/doc/krec/index.docbook
@@ -0,0 +1,639 @@
+<?xml version="1.0" ?>
+<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
+ <!ENTITY kappname "&krec;">
+ <!ENTITY package "kdemultimedia">
+ <!ENTITY % addindex "IGNORE">
+ <!ENTITY % English "INCLUDE">
+]>
+
+<book lang="&language;">
+<bookinfo>
+<title>The &krec; Handbook</title>
+
+<authorgroup>
+<author>
+<firstname>Arnold</firstname>
+<surname>Krille</surname>
+<affiliation>
+<address><email>arnold@arnoldarts.de</email></address>
+</affiliation>
+</author>
+</authorgroup>
+
+<!-- TRANS:ROLES_OF_TRANSLATORS -->
+
+<copyright>
+<year>2002</year>
+<year>2003</year>
+<year>2004</year>
+<holder>Arnold Krille</holder>
+</copyright>
+<legalnotice>&FDLNotice;</legalnotice>
+
+<date>2004-03-01</date>
+<releaseinfo>0.5.1</releaseinfo>
+
+<!--
+ vim: tw=80 et sw=2 ts=2
+-->
+<abstract>
+<para>
+&krec; is a recording application for &arts;. It can be used to record any
+sound coming into or out of the computer. Some effects for dynamics are
+implemented as well as the possibility to play out what is recorded.
+</para>
+</abstract>
+
+<keywordset>
+<keyword>KDE</keyword>
+<keyword>kdemultimedia</keyword>
+<keyword>KRec</keyword>
+<keyword>aRts</keyword>
+<keyword>recording</keyword>
+<keyword>frontend</keyword>
+</keywordset>
+
+</bookinfo>
+
+<!--
+ TODO: (ordered by priority)
+ - Explained:
+ - Mainwidget
+ - more on exports
+ + Quality settings
+ + possible more examples for the compressors
+ + Configuration
+ + Recording from line-in
+-->
+
+<chapter id="introduction">
+<title>Introduction</title>
+
+<sect1 id="why-krec">
+<title>Why I wrote &krec;</title>
+
+<para>
+After working with &arts; for some time I realized that there is no recording
+application for it except the command line tool <command>artsrec</command>.
+I had to record a radio play some friends of mine wanted me to mix and
+master and I wanted to use &Linux; for the recording. So I started
+writing &krec;.
+</para>
+</sect1>
+
+<sect1 id="what-krec-does">
+<title>What &krec; does</title>
+
+<para>
+&krec;'s function is quite simple. It connects to the &arts; server and records
+what is routed to it into files. These files are in a special &krec; format but
+it is possible to export to wave, ogg and mp3 files.
+</para>
+<para>
+But &krec; has much more functionality. You can do multiple recordings in one
+file even with overlaying functionality.
+</para>
+</sect1>
+
+<sect1 id="bugs_and_info">
+<title>Getting more info</title>
+&reporting.bugs;
+&updating.documentation;
+</sect1>
+
+</chapter>
+
+<chapter id="first_glance">
+<title>A first glance at &krec;</title>
+
+<!--<para>
+Let`s take a first glance at &krec; right after startup and I will try to give
+some explainations what the different items are.
+</para>-->
+
+<screenshot>
+<screeninfo>Here's a screenshot of &krec;</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="krec-keramik.png" format="PNG"/>
+</imageobject>
+<textobject>
+<phrase>Here is a screenshot of &krec; right after it started.</phrase>
+</textobject>
+</mediaobject>
+</screenshot>
+
+<sect1 id="first_glance_items">
+<title>The &krec;-mainwindow in detail</title>
+
+<itemizedlist>
+<listitem><para>At the top there is the <emphasis>menubar</emphasis> and two
+<emphasis>toolbars</emphasis>. The first toolbar contains some usefull items
+from the <guimenu>Files</guimenu>-menu, the second toolbar is shipped with
+important functions from the <guimenu>Play</guimenu>-menu.</para></listitem>
+<listitem><para>The middle has the important parts: On the left is the VU-Meter
+displaying the volume of the audiosignal currently recorded/played combined
+with a volumecontrol to adjust the level.</para></listitem>
+<listitem><para>The main part shows the file and consists of four parts. First
+at the top is the name of the file, the second shows the parts recorded in their
+chronological order and scaled length. It also allows to disable or delete
+parts via contextmenu. Below is the time line where you can see the current
+position and (by clicking) move to the position you want. The bottom of this
+block are two widgets showing the current position and the length in the
+timeformat you want.</para></listitem>
+<listitem><para>At the overall bottom there is another toolbar containing a
+compressor to edit the dynamics of your recording and a statusbar showing all
+kinds of messages.</para></listitem>
+</itemizedlist>
+
+<para>
+See <xref linkend="krec_explained" /> for more info.
+</para>
+
+</sect1>
+</chapter>
+
+<chapter id="recording_howtos">
+<title>Howto record</title>
+
+<para>
+This chapter contains some step-by-step tutorials which show you the way to go
+for some good recordings with &krec;.
+</para>
+
+<sect1 id="recording_from_music">
+<title>Recording from internal music</title>
+
+<orderedlist>
+<listitem><para>
+The first thing to do is a recording from our favourite &kde;
+audioplayer. So start &noatun;, &juk; <!--JuK--> or &kaboodle;. We could
+use other players but they have to use &arts; for their output, otherwise
+recording is a bit more complex and beyond the scope of this section. So please
+jump over your shadow and select a song to play in one of this three players
+(all are shipped with kdemultimedia where you got &krec;
+from).
+</para></listitem>
+<listitem><para>
+In &krec; select the <guimenuitem>Audio Manager</guimenuitem> from the
+<guimenu>Tools</guimenu>-menu. There you will see at least a line for &krec;::In
+and a line for &krec;::Out. The second column says what type the item is, either
+<emphasis>play</emphasis> or <emphasis>record</emphasis>. The last column says
+where the sound for this item comes from or goes to. Currently the &krec;::In
+item is (should be?) connected to <emphasis>in_soundcard</emphasis> which is the
+input channel of your soundcard but as we currently want to record from the
+player and the player plays to <emphasis>out_soundcard</emphasis>, we click on
+the &krec;::In item to switch it to another source. Select
+<emphasis>out_soundcard</emphasis> from the upcoming window and click
+<guibutton>Ok</guibutton>. To learn more about the audio manager see <xref
+linkend="audio_manager" />.
+</para></listitem>
+<listitem><para>
+Now the VU-Meter in &krec; should flicker up and down in a way corresponding to
+the music your hear (if you don't hear sound you shouldn't expect the VU to show
+something).
+</para></listitem>
+<listitem><para>
+Now open a new file either by clicking on the first item in the toolbar or by
+selecting <guimenuitem>New</guimenuitem> from the
+<guimenu>Files</guimenu>-menu. Accept the quality settings for now or see <xref
+linkend="quality" /> for more info.
+</para></listitem>
+<listitem><para>
+Select <guimenuitem>Record</guimenuitem> from the <guimenu>Play</guimenu>-menu
+or press the <keycap>R</keycap>-key. After you are finished select
+<guimenuitem>Stop</guimenuitem> from the same menu or use the
+<keycap>S</keycap>-key.
+</para></listitem>
+<listitem><para>Saving works the standard way, if you are interested in
+exporting see <xref linkend="exporting" />.</para></listitem>
+</orderedlist>
+
+<para>
+Thats it! Now you can hear your recording or export it (don`t forget to go back to the
+beginning).
+</para>
+
+</sect1>
+
+<sect1 id="recording_from_line_in">
+<title>Recording from Line-In or Mic-In</title>
+
+<para>
+Recording outside-sources is a bit more complicated as it involves a lot of
+different applications and hardware devices. I am assuming your hardware is
+installed correctly, the drivers are working as they should and you are able to
+control the hardware volumes via &kmix;. In &kmix; you can also select channels
+for recording which basicly means that their signal is sent to the
+analog-digital-converter (short ADC) and can be read by the driver and applications. This
+works differently on almost all soundcards and drivers so you have to try a bit
+before you can be sure...
+</para>
+<para>
+Second important thing is that &arts; has to run in full-duplex mode. That means
+that &arts; is reading from the soundcard and writing to it at the same time.
+You have to start &kcontrol; and edit the soundsystem settings (or press Alt+F2
+and enter <command>kcmshell arts</command>). On the second tab-page you have to
+make sure the checkbox for full-duplex is selected, clicking
+<guibutton>Apply</guibutton> restarts &arts; which means that you have to restart
+&krec; too.
+</para>
+<para>
+After these preparations the VU-Meter (see <xref linkend="vu-meter" /> for more
+info) of &krec; should flicker according to the
+audio-signal you want to record and which you have selected for recording in
+&kmix;. Adjusting the volume to the right values is very important for usable
+recordings. If the amplification inside the soundcard is to high you get digital
+crackles because the <glossterm>ADC</glossterm> can only create values between a
+minimum and a maximum and if the signal is to loud it gets digitally clipped
+which ruins the recording. On the other hand if the volume is to silent you get
+the noise and hiss from the audio-hardware to loud into your recording. So you
+have to choose a middle-way so the signal is not to loud and gets clipped but
+not to silent to get lost in the noise of the hardware. Its almost always better
+to leave some headroom.
+</para>
+<para>
+Now you can adjust the level a second time in &krec; which then is a software
+amplification. Here it is best to use the compressor to equalize the differences
+between silent and loud parts a bit. More info on compressor usage can be found
+in <xref linkend="compressor" />.
+</para>
+<para>
+The remaining steps are the same as in <xref linkend="recording_from_music" />
+from step four and following. So if you started with that section you should
+know it now.
+</para>
+
+</sect1>
+</chapter>
+
+<chapter id="krec_explained">
+<title>&krec; explained</title>
+
+<para>
+This chapter describes some parts and functions of &krec; in detail and gives
+some tips on usage. The items are sorted alphabeticly, not by importance.
+</para>
+
+<sect1 id="audio_manager">
+<title>The Audio Manager</title>
+<para>
+The audio manager is used to connect the outputs from different applications to
+existing or new busses. A bus is some kind of a virtual signal distributor.
+Every play- or record-item can connect to exactly one bus but multiple items can
+connect to a bus. Example: The output of &noatun; can connect to the main out
+<emphasis>or</emphasis> any other bus. But multiple &noatun;s can all connect to the main out.
+</para>
+
+<sect2 id="audio_manager_mw">
+<title>The main window of the Audio Manager</title>
+<para>
+It contains three columns:
+</para>
+<orderedlist>
+<listitem><para>The name of the item playing or recording sound.</para></listitem>
+<listitem><para>The type of the item either <emphasis>play</emphasis> or
+<emphasis>record</emphasis>.</para></listitem>
+<listitem><para>The bus the item is connected to.</para></listitem>
+</orderedlist>
+<para>
+Click on an item and a dialog for choosing the wanted bus pops up.
+</para>
+</sect2>
+<sect2 id="audio_manager_dialog">
+<title>The Busdialog</title>
+<para>
+The main part shows all currently existing busses. Select one to send your audio
+to it or get your audio from it. Below you can create new busses to connect your
+item to.
+</para>
+<tip><para>
+To record from an &arts;-aware-player and listening to what you actually record
+just create a new bus (<emphasis>test</emphasis> for example), connect your
+player to it (you wont hear anything now), connect &krec;::In to the new bus
+too and then turn on the <guimenuitem>Play Through</guimenuitem>.
+</para></tip>
+</sect2>
+
+</sect1>
+
+<sect1 id="compressor">
+<title>The Compressor</title>
+<para>
+If you are recording with a microphone you might notice that the level is
+sometimes almost clipped and sometimes very low especially
+singing or speeching voices. To correct this you can use the compressor. It
+simply reduces all sound that is over the given <emphasis>threshold</emphasis>
+by the factor given as <emphasis>ratio</emphasis>. Note that the threshold is
+logarithmic, a mid setting is already relativ low but thats very usable that
+way. Another note: ratio is at its highest turned to the left, the right end of
+the poti means no compression at all. As this reduces the loudness there is a
+<emphasis>output</emphasis> potentiometer to expand (or reduce) the sound.
+<emphasis>attack</emphasis> and <emphasis>release</emphasis> let you control the
+time after which the compressor reacts (the time going by after input first
+exceeds the threshold) and the time the compressor still reacts after sound is
+below the threshold.
+</para>
+<tip><para>Test it while you are speaking into your microphone with <guimenuitem>Play
+Through</guimenuitem> enabled and you will hear the difference between the
+plain and a compressed version.</para></tip>
+<sect2 id="compressor_tips">
+<title>Tips for compressor usage</title>
+<para>
+These are <emphasis>only</emphasis> tips. In the end the only thing that counts
+is how it sounds. So if it sounds as you want it, its probably the right
+setting. And don't hesitate to do some experiments.
+</para>
+<glosslist>
+<glossentry><glossterm>Normal speech</glossterm><glossdef><para>Most times a
+single voice speaking for radio or television is very heavily
+compressed. Because the main problem of speech is that the level is perhaps
+the right way at the beginning of the sentence but probably not at the
+end. Additionaly the wordendings are less loud than the start. That makes it impossible to use spoken
+words without compressing it. Examplesettings: Short attack, mid-time release,
+low threshold, very high ratio.</para></glossdef></glossentry>
+<glossentry><glossterm>Mastering 1: Limiting the
+level</glossterm><glossdef><para>To just limit peaks but not compress whole
+dynamics use a high threshold, a high ration, a short attack and a short-to-mid
+release. This protects your recording from some internal digital distortion and,
+with the treshold a bit lower, removes rare (and perhaps unwanted) peakes and
+gives more room for the actual recorded signal.</para></glossdef></glossentry>
+<glossentry><glossterm>Mastering 2: Doing real
+mastering</glossterm><glossdef><para>Doing real Mastering of music is difficult
+and depends totally on your hearing and the music that is to be mastered.
+Normally you will use fast attacks sou you get the level reduced fast enough at
+the bass drum beat. On the other hand you don't want the music to be pumping up
+and down just because of the bass drum beats so you select a longer release. The
+compression factor shouldn't be much. Ideally you would plug a limiter after the
+compressor to be free of clicks and clippings.</para></glossdef></glossentry>
+<glossentry><glossterm>Single Instruments</glossterm><glossdef><para>These
+settings depend on the instrument. While recording it is wise to use a
+limitersetting.</para></glossdef></glossentry>
+<glossentry><glossterm>Final tip</glossterm><glossdef><para>Use your ears and
+do some practicing. Anything is allowed if it sounds right!</para></glossdef></glossentry>
+<!--<glossentry><glossterm>Term</glossterm><glossdef><para>Definition</para></glossdef></glossentry>-->
+</glosslist>
+</sect2>
+</sect1>
+
+<sect1 id="configuration">
+<title>Configuration</title>
+<para>
+Two pages are available at the configuration. The first one is for general
+settings and explained in this section. The second is about the default quality
+settings and the same as described in <xref linkend="quality" />.
+</para>
+<screenshot>
+<screeninfo>General settings</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="krec-configuration.png" format="PNG"/>
+</imageobject>
+<textobject>
+<phrase>Editing general &krec; settings.</phrase>
+</textobject>
+</mediaobject>
+</screenshot>
+<para>
+The first part are settings controlling the way time and positions are
+displayed. The style "Plain samples" just shows the number of samples, the next
+one has optionally hours, minutes, seconds and samples. The third style is the
+same as the second except that it shows frames instead of the samples. The
+fourth style shows the size in megabyte and kilobyte and usefull for controlling
+diskspace. On the right side of the styles you have the opportunity to select
+the number of frames forming one second.
+</para>
+<para>
+The checkbox below makes the timedisplays be more verbose and showing the unit
+within.
+</para>
+<para>
+If you want to restore the tip of the day at startup you can do so with the next
+checkbox. The button below it brings back all the messages where you did select
+"Don't show this message again", mostly messages fom the export functions.
+</para>
+</sect1>
+
+<sect1 id="exporting">
+<title>Exporting</title>
+<blockquote>
+<attribution>An anonymous fan of &krec;</attribution>
+<para>Your app is very cool, I use it all my day but it really lacks exporting to
+wave/mp3/ogg!</para>
+</blockquote>
+<para>
+Here it is: the definitiv export functionality for &krec;. The available export
+formats vary on the libraries found at compiletime, all currently available ones
+are described in the following sections.
+</para>
+<para>
+Selecting the wanted exportplugin is done via the filename: You select
+<guimenuitem>Export File...</guimenuitem> from the <guimenu>Files</guimenu>,
+choose the filename for the exported file and its extention and the plugin is
+determined from your extention. The list of extentions in the dialog also shows
+which exportplugins are available.
+</para>
+<para>
+For understanding the general usage of export: Technically exporting works like
+playing. That means that you have to go to the position where you want to start
+the exporting before doing it. It also means that you can see the progress of
+the exportation from the current position marker moving forward. And it means
+that in the future its possible to export every selection you like just like
+playing only a selection.
+</para>
+<sect2 id="export_wave">
+<title>Exporting to Wave (*.wav)</title>
+<para>
+The simpliest exportplugin. It exports your &krec; file to
+a wave file with the quality settings you made for the whole file.
+</para>
+</sect2>
+<sect2 id="export_mp3">
+<title>Exporting to MP3 (*.mp3)</title>
+<para>
+Maybe the most-wanted export possibility. This one exports your &krec;-file into
+a mp3-file.
+</para>
+<important><para>
+The qualitysettings you set up in &kcontrol; section
+<quote>Sound &amp; Multimedia</quote> / <quote>Audio CDs</quote> are used in
+this version since &krec; also uses the same libraries as the audiocd:/-feature.
+</para></important>
+</sect2>
+<sect2 id="export_ogg">
+<title>Exporting to OGG (*.ogg)</title>
+<para>
+This one exports your &krec;-file into an ogg-file.
+</para>
+<important><para>
+The qualitysettings you set up in &kcontrol; section
+<quote>Sound &amp; Multimedia</quote> / <quote>Audio CDs</quote> are used in
+this version since &krec; also uses the same libraries as the audiocd:/-feature.
+</para></important>
+</sect2>
+</sect1>
+
+<sect1 id="play_thru">
+<title>Play through</title>
+<para>
+For those who want to hear what they are recording there is the very useful
+<guimenuitem>Play-Through</guimenuitem> option in the menu
+<guimenu>Play</guimenu>. I advise using it as much as possible especially if you
+do things like using the compressor or other effects and want to control what
+actually is recorded.
+</para>
+<caution><para>
+Be sure to not build a feedback loop while recording from
+<emphasis>out_soundcard</emphasis> and activating
+<guimenuitem>Play-Through</guimenuitem>. Such a loop is way to much for poor
+&arts; and it slows your system heavily down! You might kill &arts;...
+</para><para>
+The reason is that &arts; calculates a network for audio for every sample
+(acually blocks of samples) and if on sample is build via a loop from itself
+&arts; has to calculate more than is possible.
+</para></caution>
+</sect1>
+
+<sect1 id="quality">
+<title>Quality settings</title>
+<screenshot>
+<screeninfo>The properties for new files</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="krec-new_file_properties.png" format="PNG"/>
+</imageobject>
+<textobject>
+<phrase>This is the dialog for choosing the properties for new files.</phrase>
+</textobject>
+</mediaobject>
+</screenshot>
+<para>
+While creating a new &krec;-file this dialog is displayed and lets you choose
+some settings for the quality of the recordings. All of these settings have an
+impact on the size.
+</para>
+<para>
+The <emphasis>sampling rate</emphasis> is the rate which tells audiosystem how
+many samples to take during a second and is measure in Hertz (Hz) respectivly
+Kilohertz (kHz). The higher this rate the higher is the maximum recorded
+frequency. Since at least two samples are needed to rebuild a
+<quote>sinus</quote>-wave the maximum recording frequency is half of the
+sampling rate. The human ear is capable of hearing tones up to something between
+10kHz and 20kHz depending on the age, little children are possibly nearer to
+20kHz while normal adults have their maximum around 15kHz and elder people go
+down to 10kHz. But even without actually hearing the higher frequencies they
+still have an impact on what is heared and felt (corresponding keyword: psycho
+acoustics).
+</para>
+<para>
+The number of channels can be freely choosen depending on the task of the
+recording. If you are using a mono-microphone without applying a stereo effect
+you can safely choose <quote>Mono</quote> without the loss of data.
+</para>
+<para>
+The last part are the number of bits used for one sample, possible values are 8
+and 16 bits. The more bits the more steps are available for the range from
+minimum and maximum signal. 8 bits are one byte so this can also be referred to
+as one byte or two byte samples.
+</para>
+<para>
+The space needed for the recording can be calculated in a very simple way: Its
+the sampling rate multiplied by the number of channels multiplied by the number
+of bytes per sample multiplied by the number of seconds wanted to record.
+</para>
+<example><title>Calculating the size of one minute CD quality</title><para>
+For one minute (60 seconds) audio in CD quality (44100Hz, 16bits, stereo) the
+space needed is: 44100 * 2 * 2 * 60 = 1058400 Bytes = 10335.938 Kilobytes. That
+is around 10 MByte of data per minute.
+</para></example>
+<tip><para>
+Always use the best needed quality! Reducing the quality later on is always
+possible, but enhancing the quality is not possible since then more data as
+available is needed.
+</para></tip>
+<para>
+The last item above the button is a checkbox for using the entered values
+as defaults for every new file without showing this dialog again.
+</para>
+<para>
+As the same dialog is also available in the configuration to choose the standard
+settings, the "Use defaults..." checkbox is also accessible from there to get
+the dialog for every file back.
+</para>
+</sect1>
+
+<sect1 id="vu-meter">
+<title>VU-Meter</title>
+<para>
+As the compressor is probably not necessary for every task the vu-meter with its
+builtin volumecontrol is the most needed part of &krec; for recordings. It shows
+the actual level that is recorded to the file after the used effects and
+after the volume set with the control. If it is deep red most of the time
+the recording is probably clipped and doesn't sound nice. If it flickers around
+the bottom 2% its probably not much you will hear in your recording.
+</para>
+<tip><para>
+For good recordings the level should be between -12dB and 0dB most of the time.
+</para></tip>
+<tip><para>
+Use the compressor for editing the dynamics of your recordings. See <xref
+linkend="compressor" /> for more info.
+</para></tip>
+</sect1>
+
+</chapter>
+
+
+
+<chapter id="credits">
+<title>Credits and License</title>
+<para>
+&krec;
+</para>
+<para>
+Program copyright 2002-2003 Arnold Krille<email>arnold@arnoldarts.de</email>
+</para>
+<para>
+Documentation copyright 2002-2004 Arnold Krille <email>arnold@arnoldarts.de</email>
+</para>
+<!-- TRANS:CREDIT_FOR_TRANSLATORS -->
+&underFDL;
+&underGPL;
+</chapter>
+
+<appendix id="installation">
+<title>Installation</title>
+
+<sect1 id="getting-krec">
+<title>How to obtain &krec;</title>
+&install.intro.documentation;
+</sect1>
+
+<sect1 id="requirements">
+<title>Requirements</title>
+<para>
+In order to successfully use &krec; 0.5.1, you need &kde; 3.3.
+</para>
+<para>
+&krec; should be within your kdemultimedia package. As this package needs a
+running &kde; and &arts; too, everything should be fine.
+</para>
+</sect1>
+
+<sect1 id="compilation">
+<title>Compilation and Installation</title>
+&install.compile.documentation;
+</sect1>
+
+</appendix>
+
+&documentation.index;
+</book>
+
+<!--
+Local Variables:
+mode: sgml
+sgml-minimize-attributes:nil
+sgml-general-insert-case:lower
+End:
+-->
diff --git a/doc/krec/krec-configuration.png b/doc/krec/krec-configuration.png
new file mode 100644
index 00000000..2dabbdf0
--- /dev/null
+++ b/doc/krec/krec-configuration.png
Binary files differ
diff --git a/doc/krec/krec-hicolor.png b/doc/krec/krec-hicolor.png
new file mode 100644
index 00000000..d8b8fcbf
--- /dev/null
+++ b/doc/krec/krec-hicolor.png
Binary files differ
diff --git a/doc/krec/krec-keramik.png b/doc/krec/krec-keramik.png
new file mode 100644
index 00000000..0fa4b174
--- /dev/null
+++ b/doc/krec/krec-keramik.png
Binary files differ
diff --git a/doc/krec/krec-new_file_properties.png b/doc/krec/krec-new_file_properties.png
new file mode 100644
index 00000000..676a58b0
--- /dev/null
+++ b/doc/krec/krec-new_file_properties.png
Binary files differ
diff --git a/doc/kscd/Makefile.am b/doc/kscd/Makefile.am
new file mode 100644
index 00000000..085981d9
--- /dev/null
+++ b/doc/kscd/Makefile.am
@@ -0,0 +1,4 @@
+
+KDE_LANG = en
+KDE_DOCS = AUTO
+
diff --git a/doc/kscd/index.docbook b/doc/kscd/index.docbook
new file mode 100644
index 00000000..25c61904
--- /dev/null
+++ b/doc/kscd/index.docbook
@@ -0,0 +1,932 @@
+<?xml version="1.0" ?>
+<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
+ <!ENTITY kappname "&kscd;">
+ <!ENTITY package "kdemultimedia">
+ <!ENTITY % English "INCLUDE" > <!-- change language only here -->
+ <!ENTITY % addindex "IGNORE">
+]>
+
+<book lang="&language;">
+
+<bookinfo>
+<title>The &kscd; Handbook</title>
+
+<authorgroup>
+
+<author>
+<firstname>Mike</firstname>
+<surname>McBride</surname>
+<affiliation><address>&Mike.McBride.mail;</address></affiliation>
+</author>
+
+<author>
+<firstname>Jonathan</firstname>
+<surname>Singer</surname>
+<affiliation><address>&Jonathan.Singer.mail;</address></affiliation>
+</author>
+
+<author>
+<firstname>David</firstname>
+<surname>White</surname>
+<affiliation><address><email>a9403784@unet.univie.ac.at</email></address>
+</affiliation>
+</author>
+
+<othercredit role="developer">
+<firstname>Bernd</firstname>
+<othername>Johannes</othername>
+<surname>Wuebben</surname>
+<affiliation><address>&Bernd.Johannes.Wuebben.mail;</address></affiliation>
+<contrib>Developer</contrib>
+</othercredit>
+
+<othercredit role="developer">
+<firstname>Dirk</firstname>
+<surname>Forsterling</surname>
+<affiliation><address><email>milliByte@gmx.net</email></address></affiliation>
+<contrib>Developer</contrib>
+</othercredit>
+
+<othercredit role="developer">
+<firstname>Dirk</firstname>
+<surname>Foersterling</surname>
+<affiliation><address><email>milliByte@gmx.net</email></address></affiliation>
+<contrib>Developer</contrib>
+</othercredit>
+
+<othercredit role="reviewer">
+<firstname>Lauri</firstname>
+<surname>Watts</surname>
+<affiliation><address>&Lauri.Watts.mail;</address></affiliation>
+<contrib>Reviewer</contrib>
+</othercredit>
+<!-- TRANS:ROLES_OF_TRANSLATORS -->
+</authorgroup>
+
+<copyright>
+<year>2000</year><year>2004</year>
+<holder>J Singer</holder>
+
+</copyright>
+<copyright>
+
+<year>2005-2006</year>
+<holder>Mike McBride</holder>
+</copyright>
+<legalnotice>&FDLNotice;</legalnotice>
+
+
+<date>2005-12-22</date>
+<releaseinfo>1.5</releaseinfo>
+
+<abstract>
+<para>&kscd; is a small, fast, <abbrev>CDDB</abbrev> enabled audio
+<abbrev>&CD;</abbrev> player for &UNIX; platforms.</para>
+</abstract>
+
+<keywordset>
+<keyword>KDE</keyword>
+<keyword>kdemultimedia</keyword>
+<keyword>kscd</keyword>
+<keyword>music</keyword>
+<keyword>CD</keyword>
+<keyword>audio</keyword>
+</keywordset>
+
+</bookinfo>
+
+<chapter id="introduction">
+<title>Introduction</title>
+
+<para>&kscd; is a fast, <acronym>CDDB</acronym> enabled &CD; player for the &UNIX;
+platform. &kscd; stands for <quote>The KDE Project's small/simple &CD;
+player</quote>.</para>
+
+<para><emphasis>New</emphasis>: the <filename>workman2cddb.pl</filename> Perl
+script provided in order to facilitate transition for users of workman.</para>
+
+<para>I hope you will enjoy this &CD; player.</para>
+
+<para>&Bernd.Johannes.Wuebben;</para>
+
+<para>&Bernd.Johannes.Wuebben.mail;</para>
+
+<sect1 id="supported-platforms">
+<title>Supported Platforms</title>
+
+<para>&kscd; explicitly supports the following platforms:</para>
+
+<itemizedlist>
+<listitem><para>&Linux;</para></listitem>
+<listitem><para>FreeBSD</para></listitem>
+<listitem><para>NetBSD</para></listitem>
+<listitem><para>BSD386</para></listitem>
+<listitem><para>Sun</para></listitem>
+<listitem><para>&Solaris; (including <acronym>cdda</acronym>
+support)</para></listitem>
+<listitem><para>&HP-UX;</para></listitem>
+<listitem><para>&SGI; Irix (including <abbrev>cdda</abbrev>
+support)</para></listitem>
+<listitem><para>Sony NEWS</para></listitem>
+<listitem><para>OSF/1</para></listitem>
+<listitem><para>Ultrix</para></listitem>
+</itemizedlist>
+
+<para>and should compile on many others with few modifications.</para>
+
+</sect1>
+
+</chapter>
+
+<chapter id="onscreen-fundamentals">
+<title>Onscreen fundamentals</title>
+
+<sect1 id="basic-operation">
+<title>Basic Operation</title>
+
+<screenshot>
+<screeninfo>The &kscd; Interface</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="kscd.png" format="PNG"/></imageobject>
+<textobject><phrase>The &kscd; Interface</phrase></textobject>
+</mediaobject>
+</screenshot>
+
+<para>This is the main window of &kscd;. You should see something like this when
+you start &kscd;. The controls in this window are explained below, in no
+particular order.</para>
+
+<sect2 id="control-panel">
+<title>The Control Panel</title>
+
+<screenshot>
+<screeninfo>The Control Panel</screeninfo>
+<mediaobject>
+<imageobject><imagedata fileref="kscd2.png" format="PNG"/></imageobject>
+<textobject><phrase>The Control Panel</phrase></textobject>
+</mediaobject>
+</screenshot>
+
+<para> This is the main control panel for &kscd;. The function of these buttons
+should be familiar to anyone who has ever used a &CD; player.</para>
+
+<para>The uppermost button in the above diagram toggles between playing and
+pausing the &CD;. The left button in the second row stops playing the &CD;. The
+right button in the second row ejects the &CD;. The two buttons in the third row
+skip forward (right) or backward (left) to the beginning of the next or previous track.
+The left button in the bottom row toggles random play order on and off; the right button
+in the bottom row toggles looping, so that the &CD; will start
+playing again from the beginning when the end of the last audio track is
+reached.</para>
+</sect2>
+
+
+<sect2 id="status-display">
+<title>The Status Display</title>
+
+<screenshot>
+<screeninfo>The Status Display</screeninfo>
+<mediaobject>
+<imageobject><imagedata fileref="kscd3.png" format="PNG"/></imageobject>
+<textobject><phrase>The Status Display</phrase></textobject>
+</mediaobject>
+</screenshot>
+
+<para>This is the status display. Starting at the top, from right to left, is
+the main time display (see below for a discussion of the various possible time
+display modes), the status of the &CD-ROM; drive, the total play time of the
+audio &CD;, the current volume setting, and the current and maximum track
+numbers (curr./max.). The bottommost two lines of text display the artist and
+title of the &CD;, and then the title of the track, assuming that appropriate
+entries exist in the local or network <acronym>CDDB</acronym> (&CD; Data
+Base.)</para>
+
+<para>Click the time display to toggle between the possible main time display modes. By
+default, &kscd; displays the elapsed time of the current track, if the &CD; is
+playing, or either &ndash;&ndash;:&ndash;&ndash; or 00:00 if the &CD; is not playing.
+Clicking the display toggles in sequence between remaining track time, total
+elapsed time, and total remaining time.</para>
+</sect2>
+
+<sect2 id="configuration-button">
+<title>The <guibutton>Configuration</guibutton> button</title>
+
+<screenshot>
+<screeninfo>The Extras button</screeninfo>
+<mediaobject>
+<imageobject><imagedata fileref="kscd5.png" format="PNG"/></imageobject>
+<textobject><phrase>The Extras button</phrase></textobject>
+</mediaobject>
+</screenshot>
+
+<para>This button brings up a menu with a number of options. You can choose to open the &kscd;
+configuration panel or configure keyboard shortcuts. These allow you to
+configure &kscd; to work exactly to your tastes. See <link linkend="configuring-kscd">the configuring &kscd;</link> section, for details
+about configuring &kscd;</para>
+<para>The menu provides several tools to help you search for information about the artist on the
+Internet. You can find out about performance dates, purchase information, and
+other information by pressing this button and choosing the appropriate option in
+the popup menu that appears.</para>
+<para>This menu also allows you to open this help document, report bugs, learn more
+about &kscd; and &kde; and to quit &kscd;.</para>
+</sect2>
+
+<sect2 id="cddb-button">
+<title>The <guibutton>CDDB</guibutton> button</title>
+
+<screenshot>
+<screeninfo>The <acronym>CDDB</acronym> button</screeninfo>
+<mediaobject>
+<imageobject><imagedata fileref="kscd6.png" format="PNG"/></imageobject>
+<textobject><phrase>The <acronym>CDDB</acronym> button</phrase></textobject>
+</mediaobject>
+</screenshot>
+
+<para> This button opens the <acronym>CDDB</acronym> (Compact Disc Data Base) entry
+editor panel. </para>
+
+<para>The <acronym>CDDB</acronym> can identify your &CD; and often download a
+list of tracks for that &CD; or load it from the filesystem. See the <link
+linkend="cddb-editor">&CD; Database Editor</link> section for more details about
+using this tool.</para>
+</sect2>
+
+
+
+
+<sect2>
+<title>The Volume slider</title>
+
+<screenshot>
+<screeninfo>The Volume slider</screeninfo>
+<mediaobject>
+<imageobject><imagedata fileref="kscd9.png" format="PNG"/></imageobject>
+<textobject><phrase>The Volume slider</phrase></textobject>
+</mediaobject>
+</screenshot>
+
+<para> This slider controls the volume of the audio output
+of the &CD;. Moving it to the right makes the music louder, left makes it quieter. If you are playing your &CD; through your sound card,
+the sound card mixer will affect the playback volume as well.</para>
+</sect2>
+
+
+<sect2>
+<title>The track selector</title>
+
+<screenshot>
+<screeninfo>The track selector</screeninfo>
+<mediaobject>
+<imageobject><imagedata fileref="kscd11.png" format="PNG"/></imageobject>
+<textobject><phrase>The Track selector</phrase></textobject>
+</mediaobject>
+</screenshot>
+
+<para>This combo box shows you the current track number, the name of the track and the time (in minutes and seconds). You can use this drop down box to directly select
+any track on the &CD;. </para>
+</sect2>
+
+</sect1>
+</chapter>
+
+<chapter id="configuring-kscd">
+<title>Configuring &kscd;</title>
+<sect1 id="configuration-intro">
+<title>The primary configuration window</title>
+
+<para>You configure &kscd; by clicking on the <guibutton>Extras</guibutton> button. This will bring up a menu, select
+<guilabel>Configure &kscd;...</guilabel>. This will open a new window.</para>
+<para>The primary configuration window for &kscd; is divided into two
+major sections. </para>
+
+<itemizedlist>
+<listitem><para><link linkend="kscd-options-tab"><guilabel>CD
+Player</guilabel></link> to determine the look and behavior of &kscd;.</para></listitem>
+<listitem><para><guilabel>CDDB</guilabel> which is used to configure the CDDB lookup features of &kscd;.</para></listitem>
+</itemizedlist>
+
+<para>You can switch between these two sections using the icons on the left side of the dialog.</para>
+</sect1>
+
+<sect1 id="kscd-options-tab">
+<title>The <guilabel>CD Player</guilabel> dialog</title>
+
+<screenshot>
+<screeninfo>The <guilabel>CD Player</guilabel> dialog</screeninfo>
+<mediaobject>
+<imageobject><imagedata fileref="kscd12.png" format="PNG"/></imageobject>
+<textobject><phrase>The <guilabel>KSCD Configuration</guilabel> dialog</phrase></textobject>
+</mediaobject>
+</screenshot>
+
+<para>The <guilabel>LCD font:</guilabel> text box lists the currently selected font to display all information in the &kscd; status display. To change the font, click the <guibutton>Choose...</guibutton> button.</para>
+
+<para>The <guilabel>LCD color:</guilabel> and <guilabel>Background
+color:</guilabel> fields show the color selected for the foreground and
+background of the status display. Press the color bars to change
+these colors.</para>
+
+
+<!--
+<para>The <guilabel>Unix mail command</guilabel> field shows the
+command used to send new <acronym>CDDB</acronym> entries to the
+database. The default value is <userinput><command>mail</command>
+<option>-s <replaceable>%s</replaceable></option></userinput>. You
+will need to make sure this command will actually send mail to
+non-local systems if you want to submit <acronym>CDDB</acronym>
+entries, especially if you access the Internet through a dial-up
+connection. Check the documentation for your &Linux; distribution for
+details. Some Linux distributions that use sendmail to deliver email
+require only that you enter your mail host into the
+<option>&quot;Smart&quot; relay host</option> field in
+<filename>/etc/sendmail.cf</filename>. In addition, the
+<acronym>CDDB</acronym> site will want to be able to mail you back; it
+may therefore also be necessary to edit
+<filename>/etc/sendmail.cf</filename> to ensure that the return
+address on the submission is valid. Your mileage is likely to vary. If
+all else fails, use <link linkend="smtp-options">SMTP</link>
+instead.</para>
+
+<para>The <guilabel>WWW-Browser</guilabel> section lets you choose which web
+browser to use to access the web sites in the <link linkend="information-button">information button</link> menus. You can choose
+either &konqueror; or a custom browser with the radio buttons. By default, the
+<guilabel>Use Custom Browser</guilabel> field contains
+<userinput><command>kfmclient</command>
+<option><replaceable>openURL %s</replaceable></option></userinput>.</para>
+-->
+
+<para>Placing a mark in the checkbox labeled <guilabel>Show icon in system tray</guilabel> causes a &kscd; control to
+appear in the &kicker; panel.</para>
+
+<para>Placing a mark in the checkbox labeled <guilabel>Show track announcement</guilabel> causes a small information window to appear on
+top of the kicker window each time the &CD; track changes. This window will automatically disappear in 5 seconds.</para>
+
+<screenshot>
+<screeninfo>Sample track announcement</screeninfo>
+<mediaobject>
+<imageobject><imagedata fileref="kscdannounc.png" format="PNG"/></imageobject>
+<textobject><phrase>Sample track announcement</phrase></textobject>
+</mediaobject>
+</screenshot>
+
+
+<para>Set the <guilabel>Skip interval</guilabel> box to the desired number of
+seconds to move ahead or behind when the <guibutton>forward skip</guibutton>
+or <guibutton>reverse skip</guibutton> buttons in the Control Panel are pressed.</para>
+
+<para><guilabel>Autoplay when CD inserted</guilabel> causes &kscd; to start playing the &CD;
+when the tray is closed, with no need to press the
+<guibutton>Play</guibutton> button.</para>
+
+<para><guilabel>Eject CD when finished playing</guilabel> causes the
+&CD; to be automatically ejected when playback ends.</para>
+
+<para><guilabel>Stop playing CD on exit</guilabel> causes &CD; playback to stop
+when &kscd; is closed.</para>
+
+<para>The <guilabel>CD-ROM Device</guilabel> field contains the name
+of the &CD-ROM; device to be used to play audio &CD;s. The default
+value is <filename>/dev/cdrom</filename>. The permissions on this
+device must be set to allow opening this device read-only. Changing
+the permissions on this device file will in almost all cases require
+superuser privileges and can be done from the command line, or in the
+Super User Mode of &konqueror;.</para>
+
+<note><para>Before we discuss the options that follow, it is important to understand that there are two
+ways that personal computers can play a &CD;.</para>
+
+<para>The first method (which &kscd; refers to as direct digital playback), is performed by reading the digital data from the
+&CD; using Digital Audio Extraction (DAE). This data is sent to your machines CPU which converts the digital data to sound. This method requires
+a &CD; drive that is capable of DAE (most new drives are) and it requires some CPU processing time to generate the sounds you hear.</para>
+
+<para>The second method uses the internal circuitry available on most &CD; drives to read the data and generate the sounds you will hear
+without using your computers CPU. This data is transmitted by a dedicated cable directly to the sound card in your computer. This method requires
+less CPU proccessing time, but it does require that the dedicated cable be connected inside your computer. Not all computers have this connection.</para></note>
+
+<para>The checkbox labeled <guilabel>Use direct digital playback</guilabel> determines which method &kscd; uses to read the audio tracts. If there is a mark in the checkbox, the first method is used. If there is no mark in the checkbox, the second method is utilized.</para>
+
+<para>If you have selected direct digital playback, a dropdown box labeled <guilabel>Select audio backend:</guilabel> will let you select which
+sound backend the digital information should be sent to. The contents of the dropdown box will vary depending on your system.
+Most users should select <guilabel>arts</guilabel>. You can also select the device the audio backend uses by entering the device location in
+the text box labeled <guilabel>Select audio device:</guilabel>. A full discussion of audio devices and audio backends is beyond the scope of this manual.</para>
+
+<para>If a mark is placed in the checkbox labeled <guilabel>Allow encoding selection</guilabel>, you can select the text encoding for the results of a CDDB request.</para>
+<tip><para>The standard describes CDDB results as being strictly Latin 1. If you are not having problems with your CDDB information, leave this box unchecked.</para></tip>
+
+
+<para>The <guibutton>Help</guibutton> button opens the &kscd; help contents
+page. The <guibutton>Defaults</guibutton> button restores the default values of all entries in this dialog; <guibutton>OK</guibutton> saves the current settings and exits;
+<guibutton>Apply</guibutton> saves the current settings without exiting;
+<guibutton>Cancel</guibutton> exits without saving. </para>
+</sect1>
+
+<sect1 id="freedb-tab">
+<title>The <guilabel>freedb Lookup</guilabel> tab</title>
+<screenshot>
+<screeninfo>The <guilabel>freedb Lookup</guilabel> tab of the configuration dialog</screeninfo>
+<mediaobject>
+<imageobject> <imagedata fileref="kscd14.png" format="PNG"/> </imageobject>
+<textobject><phrase>The <guilabel>freedb Lookup</guilabel> tab of the configuration
+dialog</phrase></textobject>
+</mediaobject>
+</screenshot>
+
+<para> The <guilabel>freedb Lookup</guilabel> tab sets up the <acronym>CDDB</acronym> functions
+of &kscd;.</para>
+
+<para>The <guilabel>Mode</guilabel> option determines how <acronym>CDDB</acronym> lookups
+are performed. Setting <guilabel>Cache only</guilabel> means that only information already
+on your computer will be used. <guilabel>Cache and remote</guilabel> will look up
+information you do not already have while <guilabel>Remote only</guilabel> looks up
+every disc over the Internet.</para>
+
+
+<para>The <guilabel>CDDB Server:</guilabel> section determines which CDDB mirror site is used by &kscd; to
+get album information. You can enter a server name, port number and protocol using the text boxes and drop
+down boxes or you can click the <guibutton>Show Mirror List</guibutton> button. Clicking this button will
+open a new window with a list of CDDB mirrors and their locations. Simply select the server you want from
+the list and click <guibutton>OK</guibutton>.</para>
+
+<para>The section labeled <guilabel>Cache locations</guilabel> lets you determine where &kscd; saves CDDB information
+on your computer. To add a folder, enter the folder location in the text box at the top of the section and click
+<guibutton>Add</guibutton>. You can also select a
+folder by clicking on the blue file folder to the right of the text box. To delete a folder, click on the folder name
+once with the &LMB; and click <guibutton>Remove</guibutton>. You can change the order that &kscd; searches the folders by
+clicking on the folder name and clicking on the <guibutton>Move Up</guibutton> and <guibutton>Move Down</guibutton> buttons.</para>
+
+<para>The <guibutton>Help</guibutton> button opens the &kscd; help contents
+page. The <guibutton>Defaults</guibutton> button restores the default values of all entries in this dialog; <guibutton>OK</guibutton> saves the current settings and exits;
+<guibutton>Apply</guibutton> saves the current settings without exiting;
+<guibutton>Cancel</guibutton> exits without saving. </para>
+
+</sect1>
+
+<sect1 id="smtp-options">
+<title>The <guilabel>freedb Submit</guilabel> tab</title>
+
+<screenshot>
+<screeninfo>The freedb Submit tab</screeninfo>
+<mediaobject>
+<imageobject><imagedata fileref="kscd16.png" format="PNG"/></imageobject>
+</mediaobject>
+</screenshot>
+
+<para>The freedb Submit tab sets up connection to a mail
+server or a web server to submit new <acronym>CDDB</acronym> entries. This is useful
+if you do not have your own system configured as a server. </para>
+
+<para>If you plan to submit a CDDB entry, first you must decide between submitting between HTTP
+and submitting the entry as an email (via SMTP). </para>
+
+<tip><para>It is simpler to submit new entries using &HTTP;. Some firewalls block this traffic. If your
+firewall prevents you from sending new entries using &HTTP;, you can use SMTP.</para></tip>
+
+<para>First select either <guilabel>&HTTP;</guilabel> or <guilabel>SMTP (Email)</guilabel> to determine which
+protocol to use.</para>
+
+<sect2 id="http-options">
+<title>Using &HTTP; to send CDDB information</title>
+<para>You can enter a server name or port number in the text boxes provided. If
+you want to send this information to the international servers, you do not need to
+change anything.</para>
+</sect2>
+
+<sect2 id="smtp2-options">
+<title>Using SMTP (Email) to send CDDB information</title>
+<para>To prepare to submit information using email, enter your email address in the
+textbox labeled <guilabel>Reply-To:</guilabel>, your email server in the textbox
+labeled <guilabel>SMTP-server:</guilabel> and select the port number in the dropdown box
+labeled <guilabel>Port:</guilabel>.</para>
+<para>If you need to use a password to send email using the email server, place a mark in the
+checkbox labeled <guilabel>Server needs authentication</guilabel> and enter your
+username in the textbox labeled <guilabel>Username:</guilabel>.</para>
+
+<para>The <guibutton>Help</guibutton> button opens the &kscd; help contents
+page. The <guibutton>Defaults</guibutton> button restores the default values of all entries in this dialog; <guibutton>OK</guibutton> saves the current settings and exits;
+<guibutton>Apply</guibutton> saves the current settings without exiting;
+<guibutton>Cancel</guibutton> exits without saving. </para>
+</sect2>
+</sect1>
+
+
+</chapter>
+
+<chapter id="cddb-editor">
+<title>The <guilabel>CD Database Editor</guilabel></title>
+
+<screenshot>
+<screeninfo>The &CD; Database Editor</screeninfo>
+<mediaobject>
+<imageobject><imagedata fileref="kscd13.png" format="PNG"/></imageobject>
+<textobject><phrase>The &CD; Database Editor</phrase></textobject>
+</mediaobject>
+</screenshot>
+
+<para>The &CD; Database Editor allows you to modify, download, save, annotate,
+and upload <link linkend="cddb-support"><acronym>CDDB</acronym> (Compact Disc
+Data Base)</link> entries.</para>
+
+<para>If there is an entry in your local <acronym>CDDB</acronym> tree
+(see the <link linkend="freedb-tab">CDDB subsection</link> in the
+Configuration chapter) for the &CD; in your &CD-ROM; drive, or if the
+disc could be found in the freedb, you will see the name of the artist
+and the title of the &CD; in the <guilabel>Artist:</guilabel> and
+<guilabel>Title</guilabel> fields and a list of tracks with song
+titles in the <interface>Tracks</interface> selection box. Otherwise,
+you will see a list of tracks and play times without titles.</para>
+
+<para>You can make an annotation for the entire disc with the
+<guibutton>Comment</guibutton> button under the
+<guilabel>Title</guilabel> field, or for a selected track in the
+<guilabel>Tracks</guilabel> selection box with the adjacent
+<guibutton>Comment</guibutton> button. If you select a track in the
+<guilabel>Tracks</guilabel> selection box, the title, if present, will
+appear in the <guilabel>Title</guilabel> field below. You can type a
+title for the track in the box, or edit the entry to suit your
+needs. Press the <keycap>Return</keycap> key on your keyboard, and the
+text will appear in the proper line in the selection box.</para>
+
+<para>Once all tracks have been given titles and the
+<guilabel>Artist:</guilabel> and <guilabel>Title</guilabel> fields have
+been filled out, you can press the <guibutton>Upload</guibutton>
+button to send your submission by e-mail to freedb.</para>
+
+<para>You will be prompted to select a category for the
+submission. The <guilabel>Disc ID</guilabel> section displays the 32
+bit <acronym>ID</acronym> code used by freedb to identify a compact
+disc. Above the <acronym>ID</acronym> code is the category of the
+<guilabel>freedb</guilabel> entry. These categories correspond to the
+subfolders tree of the folder chosen in the <guilabel>freedb
+Base Folder:</guilabel> in the <link
+linkend="freedb-tab"><guilabel>freedb</guilabel> tab</link> of the &kscd;
+Configuration window.</para>
+
+<para>The <guilabel>Length:</guilabel> display shows the total play time of
+the &CD;.</para>
+
+<para>Press the <guibutton>Fetch Info</guibutton> button to download
+<acronym>CDDB</acronym> data.
+Press the <guibutton>OK</guibutton> button to save your changes locally.
+The <guibutton>Cancel</guibutton> button closes the &CD;
+Database Editor without saving.</para>
+
+</chapter>
+
+<chapter id="using-kscd-in-the-panel">
+<title>Using &kscd; in the &kde; Panel</title>
+
+<screenshot>
+<screeninfo>Using &kscd; in the &kde; Panel</screeninfo>
+<mediaobject>
+<imageobject><imagedata fileref="kscd18.png" format="PNG"/></imageobject>
+<textobject><phrase>Using &kscd; in the &kde; Panel</phrase></textobject>
+</mediaobject>
+</screenshot>
+
+<para>When the <guilabel>Show icon in system tray</guilabel> box is checked, a
+small &kscd; applet is also displayed in the &kicker;. Unlike the main &kscd; window, this applet is available on
+any desktop. <mousebutton>Right</mousebutton> click on the applet to pop up a
+menu to control &CD; playback. A <mousebutton>left</mousebutton> click on the
+applet hides the main &kscd; window. If the main window is hidden, a second
+<mousebutton>left</mousebutton> click on the applet restores it. </para>
+
+<note><para>To be precise, the applet is displayed in the <application>system
+tray</application> in the panel. If no applet appears when &kscd; is minimized,
+you may have removed the tray. To add it, <mousebutton>right</mousebutton> click
+on an empty spot on the panel and select <menuchoice><guisubmenu>Add</guisubmenu>
+<guisubmenu>Applet</guisubmenu> <guimenuitem>System Tray</guimenuitem>
+</menuchoice></para></note>
+
+</chapter>
+
+<chapter id="cddb-support">
+<title><acronym>CDDB</acronym> Support</title>
+
+<para>freedb is a distributed network database accessible
+over the Internet that contains information about most audio &CD;s in
+circulation. If you have Internet access, you will likely never have to manually
+enter track information for your &CD;s if you have this set up properly. See
+<link linkend="freedb-tab">The freedb Tab</link> subsection in the configuring
+&kscd; chapter for detailed instructions on how to configure this service, and
+the <link linkend="cddb-editor">The CD Database Editor</link> section for
+instructions on how to edit <acronym>CDDB</acronym> entries.</para>
+
+<para>Use of the <acronym>CDDB</acronym> is free. Submissions from users are
+encouraged.</para>
+
+<para>When preparing entries for the <acronym>CDDB</acronym>, please keep the
+following points in mind:</para>
+
+<itemizedlist>
+<listitem><para>Use <quote>standard</quote> latin characters in the entries. Some
+special characters are supported, but Cyrillic or Greek alphabet submissions,
+for example, cannot be accepted.</para></listitem>
+<listitem><para>Use only one <keysym>/</keysym> character in the
+<guilabel>Disc Artist / Title</guilabel> field in the &CD; Database
+Editor.</para>
+
+<para>For classical &CD;s, it is standard practice to put the composer's name in
+the Artist section (before the slash) and the performer's name in the Title
+section (after the slash).</para></listitem>
+<listitem><para>If you send an entry that already exists in the database, any
+additional information you provide may be added to the existing entry.</para>
+</listitem>
+</itemizedlist>
+
+<para>By default, &kscd; installs the standard <acronym>CDDB</acronym>
+categories in <filename class="directory">$KDEDIR/share/apps/kscd/cddb</filename>. You can create as
+many category subfolders as you like. However, when uploading, only the
+official <acronym>CDDB</acronym> categories are displayed. The default upload
+address is <email>freedb-submit@freedb.org</email>. For more information about
+<abbrev>freedb</abbrev> visit the <abbrev>freedb</abbrev> homepage.</para>
+
+<para>The local <acronym>CDDB</acronym> entry for a particular &CD; is stored in
+the file <filename><replaceable>category name</replaceable>/<replaceable>disc
+ID</replaceable></filename> under the <acronym>CDDB</acronym> Base
+Folder. These files can be edited with any text editor if you have nothing
+better to do with your spare time.</para>
+
+</chapter>
+
+<chapter id="troubleshooting">
+<title>Troubleshooting the CD player</title>
+
+<para>This section of the manual provides a step by step guide to troubleshooting your CD drive if the CD player will not play an audio &CD;</para>
+
+<sect1 id="ts-begin">
+<title>Begin troubleshooting</title>
+<para>To begin, place an audio CD in your CD drive. Close the CD drive door and press play on the &kscd; window. Watch the CD drive on your computer and select the link below that best describes the problem.</para>
+<para>When I pressed <guilabel>Play</guilabel>:</para>
+<itemizedlist>
+<listitem><para><link linkend="ts-errorbox">An error box appeared</link></para></listitem>
+<listitem><para><link linkend="ts-playing">No error box appeared</link></para></listitem>
+</itemizedlist>
+</sect1>
+<sect1 id="ts-playing">
+<title>I did not get an error box, but no sound is coming out of my speakers</title>
+<para>First, we will check to make sure the volume is turned up in &kscd;.</para>
+<para>Near the upper right corner of the &kscd; window, locate the volume slider. The volume slider looks like this:</para>
+<screenshot>
+<screeninfo>The Volume slider</screeninfo>
+<mediaobject>
+<imageobject><imagedata fileref="kscd9.png" format="PNG"/></imageobject>
+<textobject><phrase>The Volume slider</phrase></textobject>
+</mediaobject>
+</screenshot>
+<para>Click once on the vertical yellow bar and drag the bar all the way to the right end of the slider.</para>
+<para>If you can hear music now, the problem is solved. If you still can not hear the music, click
+<link linkend="ts-kmixlevels">here</link> to continue.</para>
+</sect1>
+<sect1 id="ts-kmixlevels">
+<title>Determining if the mixer volume levels are up</title>
+<para>The next step is to determine if &kmix; volume levels are appropriate.</para>
+<note><para>&kmix; is a sound mixer panel that is included in &kde;. This mixer allows you to adjust the relative volume levels of many sound components.</para></note>
+<para>To start &kmix;, select
+<menuchoice><guimenu>K-Button</guimenu><guimenuitem>Multimedia</guimenuitem>
+<guimenuitem>&kmix;</guimenuitem></menuchoice>.</para>
+<para>Once &kmix; has started, you will be presented with a new window with a number of volume sliders. Depending on the configuration, the sliders may have lables, or the labels may be hidden. If you do not see any labels for the sliders, you should make the labels visible before continuing. To make the labels visible, select
+<menuchoice><guimenu>Settings</guimenu><guimenuitem>Configure &kmix;...</guimenuitem></menuchoice> from the &kmix; window. This will open a small window with a few check boxes. To
+turn the labels on, place a mark in the checkbox labeld <guilabel>Show labels</guilabel> and click <guibutton>OK</guibutton>.</para>
+<para>Each slider controls the volume of a different audio device. There are two parts to each device that may need to be altered. If you look at a slider, there is a green
+light above each slider. If you click on this light you can toggle between bright green and dark green. If the light is dark green, that audio device is muted and will not produce any sound. If the light is light green, the device is not muted. Once you have verified that the device is not muted, you increase the volume of the device by draging the yellow bar up the slider. The volume of an audio device is decreased by dragging the yellow bar down the slider.</para>
+<note><para>Some sliders will have a red light as well. This light is not important for playback of compact discs so you can ignore them for now.</para></note>
+<para>Click on the tab labeled <guilabel>Output</guilabel>.</para>
+<para>Please make sure the sliders are not muted (bright green light) and that the yellow bar is all the way at the top of the slider for the following audio devices:</para>
+<itemizedlist>
+<listitem><para><guilabel>Master</guilabel></para></listitem>
+<listitem><para><guilabel>Master Mono</guilabel></para></listitem>
+<listitem><para><guilabel>PC Speaker</guilabel></para></listitem>
+<listitem><para><guilabel>PCM</guilabel></para></listitem>
+</itemizedlist>
+
+<para>If you still do not hear the &CD;, click on the tab labeled <guilabel>Input</guilabel></para>
+<para>Now make sure the slider labeled <guilabel>CD</guilabel> is not muted (bright green light) and that the yellow bar is all the way at the top of the slider.</para>
+<para>If you can hear the &CD; now, you can stop troubleshooting. If not, click <link linkend="ts-othersounds">here</link> to continue</para>
+</sect1>
+<sect1 id="ts-othersounds">
+<title>Determine if other sounds are audible on my computer</title>
+<para>In this section, we are going to test to see if other types of sounds are audible on your computer. We will do this by playing a sound over the speakers that is stored on your hard drive. </para>
+<para>First we must locate an appropriate test file. This will be done using the command line.</para>
+<para>Select <menuchoice><guimenu>K-Button</guimenu><guimenuitem>System</guimenuitem>
+<guimenuitem>&konsole;</guimenuitem></menuchoice>. A new window will appear with a command prompt. At the prompt type:</para>
+<para><screen><prompt>$</prompt> <userinput><command>locate -n1 KDE_Window_Open.wav</command></userinput></screen></para>
+<para>When you press return, there will be a pause, and a single line with a file location will be printed below your typed command.</para>
+<para><screen><prompt>$</prompt> <userinput><command>locate -n1 KDE_Window_Open.wav</command></userinput>
+/usr/local/kde/share/sounds/KDE_Window_Open.wav</screen></para>
+
+<para>Now you are going to ask &kde; to play this short sound file. Type the text <command>noatun</command> followed by a space. Then copy the full location of the file you just located with the previous command. For example: </para>
+<para><screen><prompt>$</prompt> <userinput><command>noatun /usr/local/kde/share/sounds/KDE_Window_Open.wav</command></userinput></screen></para>
+<para>If you heard a short sound, <link linkend="ts-othersoundsplay">click here</link>.</para>
+<para>If you did not hear a short sound, your sound system is not configured correctly. Click <link linkend="ts-noothersounds">here</link> to proceed.</para>
+</sect1>
+
+<sect1 id="ts-noothersounds">
+<title>The test sound failed to play</title>
+<para>So far, we have verified that the volume on &kscd; and all the mixer levels in &kmix; are set correctly. We have also sent a test sound and you could not hear the sound played. This suggests the trouble is not with &kscd; but rather with your sound configuration.</para>
+<note><para>Please make sure the &CD; is still playing in your CD drive.</para></note>
+<para>First, we need to make sure your speakers are plugged in and that the volume of your speakers is set appropriately. If you are using external speakers, please check the following:</para>
+<itemizedlist>
+<listitem><para>Please make sure the speakers are connected to your computer appropriatly (see your user manual if necessary).</para></listitem>
+<listitem><para>If your speakers require batteries, please replace the batteries with fresh batteries.</para></listitem>
+<listitem><para>If your external speakers plug into the wall, please make sure they are plugged in to the socket, the power cord is securly plugged into the back of the speakers and the wall outlet is working.</para></listitem>
+<listitem><para>If your speakers have a power button, please make sure the power is turned on.</para></listitem>
+<listitem><para>If your speakers have a volume knob, please make sure the volume is turned half way between off and maximum.</para></listitem>
+</itemizedlist>
+
+<para>If your speakers are part of you computers case, please check to see if your speakers have a volume knob. If they do make sure the volume is turned half way between off and maximum.</para>
+
+<para>If you have checked all of this, you probably need detailed help on getting the sound working on your computer. A full discussion of troubleshooting the sound system is beyond the scope of this manual and the user is referred to other internet sources. Some potential sources of information are:</para>
+
+<itemizedlist>
+<listitem><para><ulink url="http://www.tldp.org/HOWTO/Sound-HOWTO/index.html">Linux Sound HOWTO</ulink>.</para></listitem>
+<listitem><para>The website of your distribution will probably have a user forum for asking questions.</para></listitem>
+<listitem><para>Post a question to a Usenet newsgroup like comp.os.linux</para></listitem>
+<listitem><para>Use a search engine to locate others who have encountered similar problems as you.</para></listitem>
+</itemizedlist>
+</sect1>
+
+<sect1 id="ts-othersoundsplay">
+<title>The test sound played, but I can not hear the &CD;</title>
+<para>So far, we have verified that the volume on &kscd; and all the mixer levels in &kmix; are set correctly. We have also played a test sound and you were able to hear the sound played. This suggests the trouble is limited to &kscd; or the &CD;.</para>
+<para>Check to make sure the &CD; is playable. If this is a new &CD;, put it in another CD player (preferrably not located in a computer) and make sure the &CD; is playable in that device. If it is playable in another device, continue <link linkend="ts-ddpback">here</link>.</para>
+</sect1>
+
+<sect1 id="ts-ddpback">
+<title>Try using direct digital playback</title>
+<para>There are two
+ways that personal computers can play a &CD;.</para>
+
+<para>The first method (which &kscd; refers to as direct digital playback), is performed by reading the digital data from the
+&CD; using Digital Audio Extraction (DAE). This data is sent to your machines CPU which converts the digital data to sound. This method requires
+a &CD; drive that is capable of DAE (most new drives are) and it requires some CPU processing time to generate the sounds you hear.</para>
+
+<para>The second method uses the internal circuitry available on many &CD; drives to read the data and generate the sounds you will hear
+without using your computers CPU. This data is transmitted by a dedicated cable directly to the sound card in your computer. This method requires
+less CPU proccessing time, but it does require that the dedicated cable be connected inside your computer. Not all computers have this connection.</para>
+
+<para>&kscd; defaults to the second method of playback. The next step in troubleshooting your &CD; problems is to enable direct digital playback. To do this begin by clicking on the button labeled <guibutton>Stop</guibutton> on the &kscd; window. This will stop any attempt to play the &CD; for now.</para>
+
+<para>Now click on the button labeled <guibutton>Extras</guibutton>. This will open a small menu. Select <guilabel>Configure &kscd;...</guilabel>. This will open a new dialog box.</para>
+<para>Click the icon labeled <guilabel>CD Player</guilabel> on the left side of the dialog box.</para>
+<para>Place a mark in the checkbox labeled <guilabel>Use direct digital playback</guilabel>.</para>
+<para>Click <guibutton>OK</guibutton>. </para>
+<para>Now click <guibutton>Play</guibutton> in the &kscd; window and see if the &CD; begins to play correctly.</para>
+
+<para>If you still can not hear the music on the &CD;, your problem requires specific knowledge of your system and the problems you encounter. The reader is referred to many good internet resources for this information. Please consider finding help in one of the following ways:</para>
+
+<itemizedlist>
+<listitem><para>The website of your distribution will probably have a user forum for asking questions.</para></listitem>
+<listitem><para>Post a question to a Usenet newsgroup like comp.os.linux or an IRC channel for users of your operating system</para></listitem>
+<listitem><para>Use a search engine to locate others who have encountered similar problems as you.</para></listitem>
+</itemizedlist>
+
+</sect1>
+
+
+<sect1 id="ts-errorbox">
+<title>An error box appeared</title>
+<para>Probably the most common error boxes seen is this one:</para>
+<screenshot>
+<screeninfo>Error dialog</screeninfo>
+<mediaobject>
+<imageobject><imagedata fileref="kscd19.png" format ="PNG"/></imageobject>
+<textobject><phrase>Error dialog</phrase></textobject>
+</mediaobject>
+</screenshot>
+
+<para>Information about fixing this dialog box is available in the <link linkend="questions-and-answers">Questions and
+answers</link> section of this manual.</para>
+
+<para>If you recieve another error box, you should post the text from the error box on a forum for your distribution or enter this text in a search engine such
+as <ulink url="http://www.google.com">Google</ulink>.</para>
+</sect1>
+</chapter>
+
+<chapter id="questions-and-answers">
+<title>Questions and answers</title>
+
+<qandaset>
+<qandaentry>
+<question><para>I see this dialog when I start &kscd;. What's wrong?</para>
+<screenshot>
+<screeninfo>Error dialog</screeninfo>
+<mediaobject>
+<imageobject><imagedata fileref="kscd19.png" format ="PNG"/></imageobject>
+<textobject><phrase>Error dialog</phrase></textobject>
+</mediaobject>
+</screenshot>
+</question>
+<answer><para>This means that &kscd; couldn't open your &CD-ROM; drive. The name of
+the device in the <guilabel>&CD-ROM; Device</guilabel>cdrom field of the
+<guilabel>Kscd Configuration</guilabel> must actually refer to the block device
+associated with your &CD-ROM; drive. This will often be a hardlink to the
+appropriate <acronym>IDE</acronym> (<filename>/dev/hdx</filename>) or
+<acronym>SCSI</acronym> (<filename>/dev/sdx</filename>) device.</para>
+
+<para> The device file normally belongs to user root in group root, and does not
+allow normal users to open it for reading, writing, or execution directly. This
+has <emphasis>nothing</emphasis> to do with the <application>SUID</application>
+(Set User <acronym>ID</acronym> programs use the <function>setuid()</function>
+function in the standard Un*x library to assume the identity of another user)
+<command>mount</command> command, which has no problems with permissions; &kscd;
+must be able to get a read-only file descriptor referring to the &CD; device to
+control the &CD-ROM; drive and read raw data off the disk.</para>
+
+<para> If you have the root password, you can fix this quickly and easily.
+Become root and type <userinput><command>chmod</command> <option>a+r</option>
+<filename><replaceable>/dev/cdrom</replaceable></filename></userinput> to allow
+any user on your system to read from <filename>/dev/cdrom</filename>. If your
+&CD-ROM; device is called something else, change the permissions on that device
+with the same procedure. If you don't have the root password, ask your system
+administrator nicely to give you read permission for the &CD-ROM; device.</para>
+
+<para>See also the chapter on <link linkend="configuring-kscd">configuring KSCD
+</link></para></answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>I can not get the <acronym>CDDB</acronym> to work. Can I get any
+detailed information about what may be going wrong?</para></question>
+<answer><para>If you experience trouble with the <acronym>CDDB</acronym>
+functionality try to starting &kscd; from the command line with the
+<option>-d</option> switch and observe the debug output.</para></answer>
+</qandaentry>
+</qandaset>
+</chapter>
+
+<chapter id="credits-and-license">
+<title>Credits and licenses</title>
+
+<para>&kscd; Copyright 1997,1998 &Bernd.Johannes.Wuebben;
+&Bernd.Johannes.Wuebben.mail;</para>
+
+<para>&kscd; contains code from: </para>
+<itemizedlist>
+<listitem><para><application>workman</application> 1.4 beta 3 Copyright (c)
+Steven Grimm <email>koreth@hyperion.com</email></para></listitem>
+</itemizedlist>
+
+
+<para>Special thanks to Ti Kan and Steve Scherf, the inventors of the
+<acronym>CDDB</acronym> database concept. Visit <ulink
+url="http://www.cddb.com/">http://ww.cddb.com</ulink> for more information on
+<acronym>CDDB</acronym>.</para>
+
+<para>A very special thank you also to David White who wrote the original &kscd;
+help documention. Great Job David!</para>
+
+<para>Documentation updated for KDE 2.0, and copyright by &Jonathan.Singer;
+&Jonathan.Singer.mail;</para>
+
+<para>Documentation updated for KDE 3.4, and copyright by &Mike.McBride;
+&Mike.McBride.mail;</para>
+
+<!-- TRANS:CREDIT_FOR_TRANSLATORS -->
+
+&underFDL;
+&underGPL;
+
+</chapter>
+
+<appendix id="installation">
+<title>Installation</title>
+
+<sect1 id="how-to-obtain-kscd">
+<title>How to obtain &kscd;</title>
+
+&install.intro.documentation;
+
+</sect1>
+
+<sect1 id="requirements">
+<title>Requirements</title>
+
+<para>In order to successfully compile &kscd;, you need the latest versions of
+the KDE libraries as well as the &Qt; C++ library. All required libraries as
+well as &kscd; itself can be found at the &kde; &FTP; site, &kde-ftp;.</para>
+
+</sect1>
+
+<sect1 id="compilation-and-installation">
+<title>Compilation and Installation</title>
+
+&install.compile.documentation;
+
+<para>Please inform the current maintainer, Aaron Seigo, at
+&Aaron.J.Seigo.mail; of any modification you had to undertake in
+order to get &kscd; to compile on your platform.</para>
+
+</sect1>
+
+</appendix>
+
+&documentation.index;
+</book>
+<!--
+Local Variables:
+mode: sgml
+sgml-minimize-attributes: nil
+sgml-general-insert-case: lower
+End:
+-->
diff --git a/doc/kscd/kscd.png b/doc/kscd/kscd.png
new file mode 100644
index 00000000..027c324d
--- /dev/null
+++ b/doc/kscd/kscd.png
Binary files differ
diff --git a/doc/kscd/kscd11.png b/doc/kscd/kscd11.png
new file mode 100644
index 00000000..d1902fda
--- /dev/null
+++ b/doc/kscd/kscd11.png
Binary files differ
diff --git a/doc/kscd/kscd12.png b/doc/kscd/kscd12.png
new file mode 100644
index 00000000..68a9328b
--- /dev/null
+++ b/doc/kscd/kscd12.png
Binary files differ
diff --git a/doc/kscd/kscd13.png b/doc/kscd/kscd13.png
new file mode 100644
index 00000000..c957e1f9
--- /dev/null
+++ b/doc/kscd/kscd13.png
Binary files differ
diff --git a/doc/kscd/kscd14.png b/doc/kscd/kscd14.png
new file mode 100644
index 00000000..cbc30004
--- /dev/null
+++ b/doc/kscd/kscd14.png
Binary files differ
diff --git a/doc/kscd/kscd16.png b/doc/kscd/kscd16.png
new file mode 100644
index 00000000..fc66d57a
--- /dev/null
+++ b/doc/kscd/kscd16.png
Binary files differ
diff --git a/doc/kscd/kscd18.png b/doc/kscd/kscd18.png
new file mode 100644
index 00000000..cd33de29
--- /dev/null
+++ b/doc/kscd/kscd18.png
Binary files differ
diff --git a/doc/kscd/kscd19.png b/doc/kscd/kscd19.png
new file mode 100644
index 00000000..24b77794
--- /dev/null
+++ b/doc/kscd/kscd19.png
Binary files differ
diff --git a/doc/kscd/kscd2.png b/doc/kscd/kscd2.png
new file mode 100644
index 00000000..f9d724ab
--- /dev/null
+++ b/doc/kscd/kscd2.png
Binary files differ
diff --git a/doc/kscd/kscd3.png b/doc/kscd/kscd3.png
new file mode 100644
index 00000000..3b1dabba
--- /dev/null
+++ b/doc/kscd/kscd3.png
Binary files differ
diff --git a/doc/kscd/kscd5.png b/doc/kscd/kscd5.png
new file mode 100644
index 00000000..2e01ef69
--- /dev/null
+++ b/doc/kscd/kscd5.png
Binary files differ
diff --git a/doc/kscd/kscd6.png b/doc/kscd/kscd6.png
new file mode 100644
index 00000000..8c114f10
--- /dev/null
+++ b/doc/kscd/kscd6.png
Binary files differ
diff --git a/doc/kscd/kscd9.png b/doc/kscd/kscd9.png
new file mode 100644
index 00000000..4a199f35
--- /dev/null
+++ b/doc/kscd/kscd9.png
Binary files differ
diff --git a/doc/kscd/kscdannounc.png b/doc/kscd/kscdannounc.png
new file mode 100644
index 00000000..ff51f096
--- /dev/null
+++ b/doc/kscd/kscdannounc.png
Binary files differ
diff --git a/doc/noatun/Makefile.am b/doc/noatun/Makefile.am
new file mode 100644
index 00000000..171f575c
--- /dev/null
+++ b/doc/noatun/Makefile.am
@@ -0,0 +1,2 @@
+KDE_LANG = en
+KDE_DOCS = AUTO
diff --git a/doc/noatun/index.docbook b/doc/noatun/index.docbook
new file mode 100644
index 00000000..7a6bd2b7
--- /dev/null
+++ b/doc/noatun/index.docbook
@@ -0,0 +1,488 @@
+<?xml version="1.0" ?>
+<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
+ <!ENTITY kappname "&noatun;">
+ <!ENTITY package "kdemultimedia">
+ <!ENTITY % English "INCLUDE" > <!-- change language only here -->
+ <!ENTITY % addindex "IGNORE">
+]>
+
+<book lang="&language;">
+<bookinfo>
+<title>The &noatun; Handbook</title>
+
+<authorgroup>
+<author>
+<firstname>Charles</firstname>
+<surname>Samuels</surname>
+<affiliation>
+<address><email>charles@kde.org</email></address>
+</affiliation>
+</author>
+<!-- TRANS:ROLES_OF_TRANSLATORS -->
+</authorgroup>
+
+<copyright>
+<year>2001</year><year>2002</year>
+<holder>Charles Samuels</holder>
+</copyright>
+
+<legalnotice>&FDLNotice;</legalnotice>
+
+<date>2002-03-01</date>
+<releaseinfo>2.00.00</releaseinfo> <!-- Use App version here -->
+
+<abstract>
+<para>&noatun; is a fully-featured plugin-based media player for &kde;.</para>
+</abstract>
+
+<keywordset>
+<keyword>KDE</keyword>
+<keyword>Noatun</keyword>
+<keyword>kdemultimedia</keyword>
+<keyword>mp3</keyword>
+<keyword>music</keyword>
+<keyword>media</keyword>
+</keywordset>
+</bookinfo>
+
+<chapter id="introduction">
+<title>Introduction</title>
+
+<para>&noatun; is a fully-featured plugin-based media player for &kde;.</para>
+
+<sect1 id="features">
+<title>Features</title>
+
+<para>&noatun; is an elaborate front-end to &arts; &mdash; the Analog
+Real-Time Synthesizer. To add additional playobjects, go to <ulink
+url="http://noatun.kde.org/plugins/">
+http://noatun.kde.org/plugins.phtml</ulink>, or <ulink
+url="http://mpeglib.sf.net">http://mpeglib.sf.net</ulink>. By
+default &arts; supports MP3 and MPEG-1. Vorbis is also supported if the Vorbis
+libraries were available during the compilation of &kde;. </para>
+
+</sect1>
+</chapter>
+
+<chapter id="using-noatun">
+<title>Using &noatun;</title>
+
+<para>&noatun;, by default, starts with the Excellent user interface
+plugin. This plugin was chosen as it bears the most similarity to
+other &kde; applications.</para>
+
+<para>&noatun; is unique in that no two installations are the same,
+and there is no standard interface, although there is a default
+one. You're free to mix-and-match your selection of plugins, and
+customize &noatun; until it is your ideal media player!
+</para>
+
+<sect1 id="title-format">
+<title>Title Format</title>
+<para>
+The &noatun; Preferences Window has an odd <guilabel>Title Format</guilabel> text box. You can enter a format string to customize how titles appear.
+</para>
+
+<itemizedlist>
+<listitem><para>Any text appears normally, unless it is within a <literal>$( )</literal>.</para></listitem>
+<listitem><para>The text within <literal>$( )</literal> will read the &noatun; property
+for the given item, and replace the text with it.</para></listitem>
+<listitem><para>If, within the <literal>$( )</literal> are quotes, the text within the quotes
+will be displayed normally, but only if the property of the name exists.</para></listitem>
+<listitem><para>The quotes may be either at the beginning of the <literal>$( )</literal>, at the end of
+it, or at both the end or the beginning</para></listitem>
+</itemizedlist>
+
+<para>
+ For example, <literal>$(bitrate)</literal> is replaced by the bitrate of the file, as loaded
+ by the Metatag plugin. However, if you insert quotes into that field,
+ the text within the quotes will be displayed:
+<literal>$(bitrate"kbps")</literal> for
+ example will display the bitrate of the file, followed by the characters <literal>&quot;kbps&quot;</literal>. Neither will be displayed if the property <quote>bitrate</quote> does not exist.
+</para>
+</sect1>
+
+</chapter>
+
+<chapter id="using-noatun-plugins">
+<title>Using &noatun; Plugins</title>
+
+<para>You can select different plugins by going to the
+<guimenuitem>Settings</guimenuitem> menu, and selecting
+<guimenuitem>Configure &noatun;...</guimenuitem>. Move to the
+<guilabel>Plugins</guilabel> page by selecting the appropriate list item.
+Then you can enable enable plugins by selecting the checkbox near their name.
+&noatun; requires at least one User-Interface plugin, and requires exactly
+one Playlist plugin.
+</para>
+
+<sect1 id="milk-chocolate">
+<title>Milk Chocolate</title>
+
+<para>Milk Chocolate is a small, simple User Interface. The buttons
+behave mostly like a CD-player, and the <guiicon>eject</guiicon>
+button opens the playlist. The <guiicon>sheet with a cross</guiicon>
+button removes the current playlist item, but does not delete the file
+from disk, and the <guiicon>arrow</guiicon> button sets the looping
+mode. A menu is available by &RMB; clicking anywhere in the
+window.</para>
+
+</sect1>
+
+<sect1 id="young-hickory">
+
+<title>Young Hickory</title>
+
+<para>Young hickory is a plugin for the &kde; System Tray, the area near the
+clock, by default.</para>
+
+<para>&RMB; clicking on the icon will show a small menu, and &LMB; clicking
+will toggle the visibility of your &noatun; user-interface windows. Note that
+playlists, for example, are not considered user-interfaces.</para>
+
+</sect1>
+
+<sect1 id="html-exporter">
+<title><acronym>HTML</acronym> Playlist Export</title>
+
+<para>This plugin will place your playlist in a nice
+<acronym>HTML</acronym> table. Its preferences page will allow you to
+set colors, background image, and enable the Hover mode, for changing
+colors when the cursor is over a link.</para>
+
+<para>After setting options, the <guimenu>Actions</guimenu> menu's
+<guimenuitem>Export Playlist...</guimenuitem> will open a file dialog
+for you to select where to save the output. </para>
+
+</sect1>
+
+<sect1 id="kjofol-skin">
+<title><application>K-Jöfol</application> Skins</title>
+
+<para>The &noatun; <application>K-Jöfol</application> skin loader is a
+reimplementation of a &Windows; program under the same name.</para>
+
+<para>&noatun;'s implementation has a few limitations,
+unfortunately. For instance the skins must be uncompressed on disk in
+order to load them.</para>
+
+<para>To install a skin (in the &Windows; ZIP format) you can use
+the skin-installer that can be found in the preferences-dialog of
+&noatun;.</para>
+
+<para>Because some skins are not packaged correctly and the skin-installer
+can not guess everything you can still follow these commands if installation
+of a certain skin failed:</para>
+
+<screen><prompt>%</prompt> <userinput><command>cd</command> <filename class="directory">$KDEHOME/share/apps/noatun</filename></userinput>
+<prompt>%</prompt> <userinput><command>mkdir</command> <option>kjskins</option></userinput> (if needed)
+<prompt>%</prompt> <userinput><command>cd</command> <option>kjskins</option></userinput>
+<prompt>%</prompt> <userinput><command>mkdir</command> <option>new_skin</option> ; <command>cd</command> <replaceable>new_skin</replaceable></userinput>
+<prompt>%</prompt> <userinput><command>unzip</command> <replaceable>/path/to/new_skin.zip</replaceable></userinput></screen>
+
+<para>You can also make your own skins with the tutorial at <ulink
+url="http://www.angelfire.com/mo/nequiem/tutorial.html">http://www.angelfire.com/mo/nequiem/tutorial.html</ulink>.
+</para>
+
+</sect1>
+
+<sect1 id="splitplaylist">
+<title>The Split Playlist</title>
+
+<para> The Split Playlist had a simple, classic-style design. Double
+clicking on an entry will play it (as will selecting it and pressing
+<keycap>Enter</keycap>). You can drag files and
+&URL;s in as well. </para>
+
+<para>
+As of &kde; 3.0, the Split Playlist (<acronym>SPL</acronym>) stores its
+data in an &XML; format, but will automatically
+import the <acronym>m3u</acronym> list if the &XML; file
+does not exist. This means that you can write to the m3u file, and delete
+the &XML; file, to automatically generate playlists.
+</para>
+
+<para>
+The name Split Playlist is a bit of a misnomer, as the list is not actually split.
+It results from the original design (back in the early &noatun; days) actually
+having it split.
+</para>
+</sect1>
+
+<sect1 id="winampskin">
+<title>Winamp Skins</title>
+<para>
+If you're actually using the <trademark>Winamp</trademark> skin,
+it should seem familiar to you. Clicking on the timer will
+toggle it between count-down and count-up mode. Selecting
+the Scope region under it will enable and disable the scope. You
+can also double click on the titlebar to toggle Windowshade mode.
+<mousebutton>Right</mousebutton> clicking (or clicking on the top-left icon will show the
+standard &noatun; toolbar.
+</para>
+<para>
+You can install new skins by, in
+<filename class="directory">$KDEHOME/share/apps/noatun/skins/winamp</filename>,
+creating a folder for them, and then unzipping the skin in there. <trademark>Winamp</trademark>
+skin files with the extension <literal role="extension">.wsz</literal> can be treated
+as normal zip files. You may have to rename them first, however, to be
+able to unzip them.
+</para>
+</sect1>
+
+<sect1 id="metatag">
+<title>Metatag</title>
+<para>
+Metatag is a plugin that loads information about a file through the use
+of KFile, the same mechanism that provides &konqueror; with those tooltips
+when you hover a mouse over files. Aside from loading the information,
+it supports editing it via the <guimenu>Actions'</guimenu> menu subitem
+<guimenu>Tag Editing</guimenu>. It supports editing of <acronym>ID3</acronym>
+tags, as well as OggVorbis tags. It also reads the bitrate from files.
+</para>
+</sect1>
+
+<sect1 id="keyz">
+<title>Keyz</title>
+<para>
+Carsten Pfeiffer decided to break with the long lived &noatun; tradition
+of naming a plugin in the most inaccurate way possible, as proven by both
+Milk-Chocolate, Young Hickory, and countless others. What's the value
+in just converting an S to a Z? Sounds like something American-English speakers would do!
+</para>
+<para>
+However, just because the name is unoriginal doesn't mean this is any
+less of a plugin. Indeed, this one lets you assign keystrokes to some
+&noatun; actions. The real beauty is that these keystrokes work from
+anywhere, not just from &noatun;. So this may finally make those
+<quote>Multimedia Keyboards</quote> worthwhile.
+</para>
+</sect1>
+
+<sect1 id="ir-control">
+<title>Infrared Control</title>
+<para>
+If you have a remote control for your computer (such as those found
+on television cards with <trademark class="registered">Brooktree</trademark>
+tuners), and your infrared remote control is supported by
+<ulink url="http://www.lirc.org">LIRC</ulink>, this should work. Like Keyz,
+the name is unexciting, but the plugin allows you to assign actions to
+button presses.
+</para>
+<para>
+To assign an action to a keypress, load the plugin, go to the Infrared Control
+page in the &noatun; configuration window. Select the keypress in the
+list, and then choose the action to perform with the combo box below. If, in
+an action like Volume control, you want the action to be performed repeatedly,
+check the box and select the interval between actions.
+</para>
+<para>
+If you have a <acronym>TV</acronym> card, a convenient trick is to
+assign the <guibutton>Mute</guibutton> button to Pause, thereby allowing you to mute your
+<acronym>TV</acronym> display application while unpausing &noatun;,
+and vice-versa, particularly useful in the case of commercials.
+</para>
+</sect1>
+</chapter>
+
+<chapter id="questions-answers-and-tips">
+
+<title>Questions, Answers, and Tips</title>
+
+<qandaset id="faq">
+<title>Frequently-asked questions</title>
+<qandaentry>
+<question>
+<para>The music skips a lot when moving windows.</para>
+</question>
+<answer>
+<para>
+You can have &arts; buffer more as follows:
+</para>
+
+<itemizedlist>
+<listitem><para>Start &kcontrol;</para></listitem>
+<listitem><para>Move to the <guilabel>Sound</guilabel>
+group</para></listitem>
+<listitem><para>Move to the <guilabel>Sound
+Server</guilabel> section</para></listitem>
+<listitem><para>Increase the response time&mdash;384ms is
+usually sufficient
+for most computers.</para></listitem>
+</itemizedlist>
+
+<para>
+You may also consider running the soundserver with real-time priority
+if setting the response time doesn't help. Be aware that this can
+cause your system to lock-up.
+</para>
+
+</answer>
+</qandaentry>
+<qandaentry>
+<question>
+<para>I can't remove a playlist or user-interface from the plugins list.</para>
+</question>
+<answer>
+<para>
+Since &noatun; requires at least one user-interface loaded, and exactly
+one playlist, you have to add a new user-interface plugin before
+removing the old one. Adding a new playlist will automatically
+remove the old one.
+</para>
+</answer>
+</qandaentry>
+<qandaentry>
+<question>
+<para>Where can I get more plugins?</para>
+</question>
+<answer>
+<para>
+Third-party developers can submit their own plugins to the
+<ulink url="http://noatun.kde.org/plugins/">&noatun; web-site</ulink>, where they
+can be downloaded by you, the users.
+</para>
+</answer>
+</qandaentry>
+<qandaentry>
+<question>
+<para>How do I write a &noatun; plugin?</para>
+</question>
+<answer>
+<para>
+Documentation, an <acronym>API</acronym> reference, and example source code is
+available at the <ulink url="http://noatun.kde.org">&noatun; web-site</ulink>.
+Also, in the spirit of Open Source software the source code to
+&noatun; and all default plugins is available.
+</para>
+</answer>
+</qandaentry>
+</qandaset>
+</chapter>
+
+<chapter id="credits-and-licenses">
+<title>Credits and Licenses</title>
+
+<para>Program copyright 2000-2002 Charles Samuels
+<email>charles@kde.org</email></para>
+
+<para>Documentation copyright 2002 Charles Samuels
+<email>charles@kde.org</email></para>
+
+<para>&noatun; has been brought to you by the following people:</para>
+
+<itemizedlist>
+<listitem>
+<para>Charles Samuels <email>charles@kde.org</email></para>
+</listitem>
+<listitem>
+<para>Neil Stevens <email>multivac@fcmail.com</email></para>
+</listitem>
+<listitem>
+<para>Stefan Westerfeld <email>stefan@space.twc.de</email></para>
+</listitem>
+<listitem>
+<para>Martin Vogt <email>mvogt@rhrk.uni-kl.de</email></para>
+</listitem>
+<listitem>
+<para>Malte Starostik <email>malte.starostik@t-online.de</email></para>
+</listitem>
+<listitem>
+<para>Nikolas Zimmermann <email>wildfox@kde.org</email></para>
+</listitem>
+<listitem>
+<para>Stefan Schimanski <email>1Stein@gmx.de</email></para>
+</listitem>
+</itemizedlist>
+<!-- TRANS:CREDIT_FOR_TRANSLATORS -->
+
+&underFDL;
+&underBSDLicense;
+
+</chapter>
+
+<appendix id="installation">
+<title>Installation</title>
+
+<sect1 id="how-to-obtain-Noatun">
+<title>How to obtain &noatun;</title>
+
+&install.intro.documentation;
+
+</sect1>
+
+<sect1 id="requirements">
+<title>Requirements</title>
+
+<para>&noatun; requires at least a Pentium 200 with &Linux;, a PowerPC with
+&Linux; 2.4.1 or later, or several other platforms. Support for more platforms
+will be available in later versions.</para>
+
+<para>For a platform to be supported easily, it must have pthread support, and
+the <acronym>OSS</acronym> sound output system, however <acronym>ALSA</acronym>
+is supported under &Linux;.</para>
+
+</sect1>
+
+<sect1 id="compilation-and-installation">
+<title>Compilation and Installation</title>
+
+&install.compile.documentation;
+
+<para>Should you run into any problems, please report them to
+the author at <email>charles@kde.org</email>.</para>
+
+<para>If you have this documentation, you've probably already compiled
+&noatun;</para>
+
+</sect1>
+
+</appendix>
+
+<glossary id="glossary">
+<title>Glossary</title>
+
+<glossentry id="gloss-mc">
+<glossterm>Milk Chocolate</glossterm><glossdef>
+<para>
+ Milk Chocolate is a simple, minimalist user interface plugin
+</para></glossdef></glossentry>
+
+<glossentry id="gloss-arts">
+<glossterm>&arts;</glossterm><glossdef>
+<para>
+ &arts; is the Analog Real-time Synthesizer. A powerful
+ media framework used by &noatun;
+</para></glossdef></glossentry>
+<glossentry id="gloss-kj">
+<glossterm>K-Jöfol</glossterm><glossdef>
+<para>
+ This plugin loads skins originally used under a &Windows;
+ media player under the same name.
+</para></glossdef></glossentry>
+
+<glossentry id="gloss-keyz">
+<glossterm>Keyz</glossterm><glossdef>
+<para>
+ Keyz allows you to assign keystrokes to actions in &noatun;
+</para></glossdef></glossentry>
+<glossentry id="gloss-young-hickory">
+<glossterm>Young Hickory</glossterm><glossdef>
+<para>
+ Young Hickory is a system tray plugin.
+</para></glossdef></glossentry>
+<glossentry id="gloss-kaiman">
+<glossterm>Noatun</glossterm><glossdef>
+<para>
+ Kaiman is a plugin that loads skins from the media player
+ GQMPEG. Kaiman is also &noatun;'s predecessor, and was
+ distributed with &kde; for &kde; 2.0. When
+ &noatun; was introduced in &kde; 2.1, Kaiman's skin loader
+ became a &noatun; plugin.
+</para></glossdef></glossentry>
+
+</glossary>
+
+&documentation.index;
+</book>
+
diff --git a/juk/HACKING b/juk/HACKING
new file mode 100644
index 00000000..2fb4d392
--- /dev/null
+++ b/juk/HACKING
@@ -0,0 +1,147 @@
+Since I tend to be one of the more pedantic people on coding style I'll provide
+a bit of a reference here. Please take a few minutes to read this as it will
+save us both time when processing patches.
+
+================================================================================
+Indentation
+================================================================================
+
+The older files in JuK are indented using Qt-style. The first level was 4
+spaces, the second one tab, the third one tab followed by 4 spaces. I'm not
+particularly fond of this style anymore, but it used to be the Emacs default
+when using the KDE Emacs scripts.
+
+static void foo()
+{
+ if(bar()) // <-- 4 spaces
+ baz() // <-- 1 tab
+}
+
+Newer files simply use 4 spaces at all levels. In most cases the style of the
+file currently being worked in should be followed. So:
+
+static void foo()
+{
+ if(bar()) // <-- 4 spaces
+ baz() // <-- 8 spaces
+}
+
+================================================================================
+Braces
+================================================================================
+
+Braces opening classes, structs, namespaces and functions should be on their own
+line. Braces opening conditionals should be on the same line with one notable
+exception: if the conditional is split up onto several lines then the opening
+brace should be on its own line. i.e.
+
+class Foo
+{
+ // stuff
+};
+
+if(foo == bar) {
+ // stuff
+}
+
+while(foo == bar &&
+ baz == quux &&
+ flop == pop)
+{
+ // stuff
+}
+
+static void foo()
+{
+ // stuff
+}
+
+Other exceptions include inline functions that are just returning or setting a
+value. However multiple statements should not ever be combined onto one line:
+
+class Foo
+{
+public:
+ String value() const { return m_value; }
+};
+
+Also conditionals / loops that only contiain one line in their body (but where
+the conditional statement fits onto one line) should omit braces:
+
+if(foo == bar)
+ baz();
+
+But:
+
+if(baz == quux &&
+ ralf == spot)
+{
+ bar();
+}
+
+================================================================================
+Spaces
+================================================================================
+
+Spaces should not be used between the conditional / loop type and the
+conditional statement. They should also not be used after parenthesis. However
+the should be to mark of mathematical or comparative operators.
+
+if ( foo == bar )
+ ^ ^ ^
+
+The marked spaces should be ommitted to produce:
+
+if(foo == bar)
+
+================================================================================
+Header Organization
+================================================================================
+
+Member variables should always be private and prefixed with "m_". Accessors may
+be inline in the headers. The organization of the members in a class should be
+roughly as follows:
+
+public:
+public slots:
+protected:
+protected slots:
+signals:
+private: // member funtions
+private slots:
+private: // member variables
+
+If there are no private slots there is no need for two private sections, however
+private functions and private variables should be clearly separated.
+
+The implementations files -- .cpp files -- should follow (when possible) the
+same order of function declarations as the header files.
+
+Virtual functions should always be marked as such even in derrived classes where
+it is not strictly necessary.
+
+================================================================================
+Whitespace
+================================================================================
+
+Whitespace should be used liberally. When blocks of code are logically distinct
+I tend to put a blank line between them. This is difficult to explain
+systematically but after looking a bit at the current code organization this
+ideally will be somewhat clear.
+
+Also I tend to separate comments by blank lines on both sides.
+
+================================================================================
+Pointer and Reference Operators
+================================================================================
+
+This one is pretty simple. I prefer "Foo *f" to "Foo* f" in function signatures
+and declarations. The same goes for "Foo &f" over "Foo& f".
+
+================================================================================
+
+There are likely things missing here and I'll try to add them over time as I
+notice things that are often missed. Please let me know if specific points are
+ambiguous.
+
+Scott Wheeler <wheeler@kde.org>
diff --git a/juk/Makefile.am b/juk/Makefile.am
new file mode 100644
index 00000000..4fba4b1e
--- /dev/null
+++ b/juk/Makefile.am
@@ -0,0 +1,113 @@
+bin_PROGRAMS = juk
+check_PROGRAMS = tagguessertest
+
+juk_SOURCES = \
+ advancedsearchdialog.cpp \
+ actioncollection.cpp \
+ akodeplayer.cpp \
+ artsplayer.cpp \
+ cache.cpp \
+ categoryreaderinterface.cpp \
+ collectionlist.cpp \
+ coverdialog.cpp \
+ coverdialogbase.ui \
+ covericonview.cpp \
+ coverinfo.cpp \
+ covermanager.cpp \
+ deletedialog.cpp \
+ deletedialogbase.ui \
+ directorylist.cpp \
+ directorylistbase.ui \
+ dynamicplaylist.cpp \
+ exampleoptions.cpp \
+ exampleoptionsbase.ui \
+ folderplaylist.cpp \
+ filehandle.cpp \
+ filerenamer.cpp \
+ filerenamerbase.ui \
+ filerenameroptions.cpp \
+ filerenameroptionsbase.ui \
+ filerenamerconfigdlg.cpp \
+ gstreamerplayer.cpp \
+ webimagefetcher.cpp \
+ webimagefetcherdialog.cpp \
+ historyplaylist.cpp \
+ juk.cpp \
+ jukIface.skel \
+ k3bexporter.cpp \
+ keydialog.cpp \
+ main.cpp \
+ mediafiles.cpp \
+ musicbrainzquery.cpp \
+ nowplaying.cpp \
+ playermanager.cpp \
+ playlist.cpp \
+ playlistbox.cpp \
+ playlistcollection.cpp \
+ playlistinterface.cpp \
+ playlistitem.cpp \
+ playlistsearch.cpp \
+ playlistsplitter.cpp \
+ searchplaylist.cpp \
+ searchwidget.cpp \
+ slideraction.cpp \
+ sortedstringlist.cpp \
+ splashscreen.cpp \
+ statuslabel.cpp \
+ stringshare.cpp \
+ systemtray.cpp \
+ tag.cpp \
+ tageditor.cpp \
+ tagguesser.cpp \
+ tagguesserconfigdlg.cpp \
+ tagguesserconfigdlgwidget.ui \
+ tagrenameroptions.cpp \
+ tagtransactionmanager.cpp \
+ trackpickerdialog.cpp \
+ trackpickerdialogbase.ui \
+ tracksequenceiterator.cpp \
+ tracksequencemanager.cpp \
+ treeviewitemplaylist.cpp \
+ upcomingplaylist.cpp \
+ ktrm.cpp \
+ viewmode.cpp
+
+tagguessertest_SOURCES = tagguessertest.cpp tagguesser.cpp
+
+INCLUDES= $(all_includes) $(taglib_includes) $(akode_includes) $(GST_CFLAGS) $(ARTS_CFLAGS)
+
+##################################################
+# check to see if MusicBrainz is available
+##################################################
+if link_lib_MB
+mblibs = -ltunepimp
+endif
+##################################################
+
+juk_LDADD = -lm $(LDADD_GST) $(mblibs) $(LIB_KIO) $(taglib_libs) $(akode_libs) $(LIB_KHTML) $(LIB_ARTS)
+juk_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LDFLAGS_GST)
+
+KDE_CXXFLAGS = $(USE_EXCEPTIONS)
+
+tagguessertest_LDADD = $(LIB_KDECORE)
+tagguessertest_LDFLAGS = $(all_libraries)
+
+SUBDIRS = pics
+
+rcdir = $(kde_datadir)/juk
+rc_DATA = jukui.rc jukui-rtl.rc
+
+servicemenudir = $(kde_datadir)/konqueror/servicemenus
+servicemenu_DATA = jukservicemenu.desktop
+
+METASOURCES = AUTO
+KDE_ICON = AUTO
+POFILES = AUTO
+
+xdg_apps_DATA = juk.desktop
+
+messages: rc.cpp
+ $(EXTRACTRC) *.rc >> rc.cpp
+ $(XGETTEXT) *.rc *.cpp *.h -o $(podir)/juk.pot
+ -rm rc.cpp
+KDE_OPTIONS=nofinal
diff --git a/juk/TODO b/juk/TODO
new file mode 100644
index 00000000..f515343d
--- /dev/null
+++ b/juk/TODO
@@ -0,0 +1,17 @@
+Just a quick 3.4 checklist. I'll add things as they pop into my mind...
+
+( ) Make the upcoming playlist not suck, specifically I'd like to see it as a
+ real and usable replacement for the "Play Next" feature, which it
+ presently is not.
+
+( ) At least play with making the "Now Playing" bar resizable. Currently have
+ something sort of working putting it in a slider type of thing. Not sure
+ if I like it.
+
+( ) Fix the cover manager in lots of ways. I really don't like having the icon
+ in the listview and have played around with a few things to fix this. What
+ I'm currently thinking is making a "cover manager" as a view mode and when
+ the user clicks to set the cover switching to that mode. Or something like
+ that. Something's got to give.
+
+( ) Show a preview of the cover to be added before saving it.
diff --git a/juk/actioncollection.cpp b/juk/actioncollection.cpp
new file mode 100644
index 00000000..2ba64474
--- /dev/null
+++ b/juk/actioncollection.cpp
@@ -0,0 +1,41 @@
+/***************************************************************************
+ begin : Fri Feb 27 2004
+ copyright : (C) 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kactioncollection.h>
+#include <kdebug.h>
+
+#include "actioncollection.h"
+
+namespace ActionCollection
+{
+ KActionCollection *actions()
+ {
+ static KActionCollection *a =
+ new KActionCollection(static_cast<QWidget *>(0), "JuK Action Collection");
+ return a;
+ }
+
+ KAction *action(const char *key)
+ {
+#ifndef NO_DEBUG
+ KAction *a = actions()->action(key);
+ if(!a)
+ kdWarning(65432) << "KAction \"" << key << "\" is not defined yet." << endl;
+ return a;
+#else
+ return actions()->action(key);
+#endif
+ }
+}
diff --git a/juk/actioncollection.h b/juk/actioncollection.h
new file mode 100644
index 00000000..22eddd54
--- /dev/null
+++ b/juk/actioncollection.h
@@ -0,0 +1,45 @@
+/***************************************************************************
+ begin : Fri Feb 27 2004
+ copyright : (C) 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef JUK_ACTIONCOLLECTION_H
+#define JUK_ACTIONCOLLECTION_H
+
+class KActionCollection;
+class KAction;
+
+namespace ActionCollection
+{
+ /**
+ * The global action collection for JuK.
+ */
+ KActionCollection *actions();
+
+ /**
+ * Returns the action for the associated key from the global action
+ * collection.
+ */
+ KAction *action(const char *key);
+
+ /**
+ * Returns the action for the associated key but includes a cast to the
+ * type \a T. i.e. KSelectAction *a = action<KSelectAction>("chooser");
+ */
+ template <class T> T *action(const char *key)
+ {
+ return dynamic_cast<T *>(action(key));
+ }
+}
+
+#endif
diff --git a/juk/advancedsearchdialog.cpp b/juk/advancedsearchdialog.cpp
new file mode 100644
index 00000000..4b5f2dbb
--- /dev/null
+++ b/juk/advancedsearchdialog.cpp
@@ -0,0 +1,175 @@
+/***************************************************************************
+ begin : Thu Jul 31 00:31:51 2003
+ copyright : (C) 2003 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kcombobox.h>
+#include <klineedit.h>
+#include <kpushbutton.h>
+#include <klocale.h>
+
+#include <qradiobutton.h>
+#include <qvgroupbox.h>
+#include <qlabel.h>
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qlayout.h>
+#include <qhbuttongroup.h>
+
+#include "collectionlist.h"
+#include "advancedsearchdialog.h"
+#include "searchwidget.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+AdvancedSearchDialog::AdvancedSearchDialog(const QString &defaultName,
+ const PlaylistSearch &defaultSearch,
+ QWidget *parent,
+ const char *name) :
+ KDialogBase(parent, name, true, i18n("Create Search Playlist"), Ok|Cancel)
+{
+ makeVBoxMainWidget();
+
+ QHBox *box = new QHBox(mainWidget());
+ box->setSpacing(5);
+
+ new QLabel(i18n("Playlist name:"), box);
+ m_playlistNameLineEdit = new KLineEdit(defaultName, box);
+
+ QVGroupBox *criteriaGroupBox = new QVGroupBox(i18n("Search Criteria"), mainWidget());
+ static_cast<QHBox *>(mainWidget())->setStretchFactor(criteriaGroupBox, 1);
+
+ QHButtonGroup *group = new QHButtonGroup(criteriaGroupBox);
+ m_matchAnyButton = new QRadioButton(i18n("Match any of the following"), group);
+ m_matchAllButton = new QRadioButton(i18n("Match all of the following"), group);
+
+ m_criteria = new QVBox(criteriaGroupBox);
+
+ if(defaultSearch.isNull()) {
+ m_searchLines.append(new SearchLine(m_criteria));
+ m_searchLines.append(new SearchLine(m_criteria));
+ m_matchAnyButton->setChecked(true);
+ }
+ else {
+ PlaylistSearch::ComponentList components = defaultSearch.components();
+ for(PlaylistSearch::ComponentList::ConstIterator it = components.begin();
+ it != components.end();
+ ++it)
+ {
+ SearchLine *s = new SearchLine(m_criteria);
+ s->setSearchComponent(*it);
+ m_searchLines.append(s);
+ }
+ if(defaultSearch.searchMode() == PlaylistSearch::MatchAny)
+ m_matchAnyButton->setChecked(true);
+ else
+ m_matchAllButton->setChecked(true);
+ }
+
+ QWidget *buttons = new QWidget(criteriaGroupBox);
+ QBoxLayout *l = new QHBoxLayout(buttons, 0, 5);
+
+ KPushButton *clearButton = new KPushButton(KStdGuiItem::clear(), buttons);
+ connect(clearButton, SIGNAL(clicked()), SLOT(clear()));
+ l->addWidget(clearButton);
+
+ l->addStretch(1);
+
+ m_moreButton = new KPushButton(i18n("More"), buttons);
+ connect(m_moreButton, SIGNAL(clicked()), SLOT(more()));
+ l->addWidget(m_moreButton);
+
+ m_fewerButton = new KPushButton(i18n("Fewer"), buttons);
+ connect(m_fewerButton, SIGNAL(clicked()), SLOT(fewer()));
+ l->addWidget(m_fewerButton);
+
+ m_playlistNameLineEdit->setFocus();
+}
+
+AdvancedSearchDialog::~AdvancedSearchDialog()
+{
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public slots
+////////////////////////////////////////////////////////////////////////////////
+
+AdvancedSearchDialog::Result AdvancedSearchDialog::exec()
+{
+ Result r;
+ r.result = DialogCode(KDialogBase::exec());
+ r.search = m_search;
+ r.playlistName = m_playlistName;
+ return r;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected slots
+////////////////////////////////////////////////////////////////////////////////
+
+void AdvancedSearchDialog::accept()
+{
+ m_search.clearPlaylists();
+ m_search.clearComponents();
+
+ m_search.addPlaylist(CollectionList::instance());
+
+ QValueListConstIterator<SearchLine *> it = m_searchLines.begin();
+ for(; it != m_searchLines.end(); ++it)
+ m_search.addComponent((*it)->searchComponent());
+
+ PlaylistSearch::SearchMode m = PlaylistSearch::SearchMode(!m_matchAnyButton->isChecked());
+ m_search.setSearchMode(m);
+
+ m_playlistName = m_playlistNameLineEdit->text();
+
+ KDialogBase::accept();
+}
+
+void AdvancedSearchDialog::clear()
+{
+ QValueListConstIterator<SearchLine *> it = m_searchLines.begin();
+ for(; it != m_searchLines.end(); ++it)
+ (*it)->clear();
+}
+
+void AdvancedSearchDialog::more()
+{
+ SearchLine *searchLine = new SearchLine(m_criteria);
+ m_searchLines.append(searchLine);
+ searchLine->show();
+ updateButtons();
+}
+
+void AdvancedSearchDialog::fewer()
+{
+ SearchLine *searchLine = m_searchLines.last();
+ m_searchLines.remove(searchLine);
+ delete searchLine;
+ updateButtons();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private methods
+////////////////////////////////////////////////////////////////////////////////
+
+void AdvancedSearchDialog::updateButtons()
+{
+ m_moreButton->setEnabled(m_searchLines.count() < 16);
+ m_fewerButton->setEnabled(m_searchLines.count() > 1);
+}
+
+#include "advancedsearchdialog.moc"
diff --git a/juk/advancedsearchdialog.h b/juk/advancedsearchdialog.h
new file mode 100644
index 00000000..03280d19
--- /dev/null
+++ b/juk/advancedsearchdialog.h
@@ -0,0 +1,69 @@
+/***************************************************************************
+ begin : Thu Jul 31 00:31:51 2003
+ copyright : (C) 2003 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef ADVANCEDSEARCHDIALOG_H
+#define ADVANCEDSEARCHDIALOG_H
+
+#include <kdialogbase.h>
+
+class KLineEdit;
+class KPushButton;
+class QGroupBox;
+class QRadioButton;
+class SearchLine;
+
+class AdvancedSearchDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ struct Result
+ {
+ DialogCode result;
+ PlaylistSearch search;
+ QString playlistName;
+ };
+
+ AdvancedSearchDialog(const QString &defaultName,
+ const PlaylistSearch &defaultSearch = PlaylistSearch(),
+ QWidget *parent = 0,
+ const char *name = 0);
+
+ virtual ~AdvancedSearchDialog();
+
+public slots:
+ Result exec();
+
+protected slots:
+ virtual void accept();
+ virtual void clear();
+ virtual void more();
+ virtual void fewer();
+
+private:
+ void updateButtons();
+
+ QWidget *m_criteria;
+ PlaylistSearch m_search;
+ QString m_playlistName;
+ QValueList<SearchLine *> m_searchLines;
+ KLineEdit *m_playlistNameLineEdit;
+ QRadioButton *m_matchAnyButton;
+ QRadioButton *m_matchAllButton;
+ KPushButton *m_moreButton;
+ KPushButton *m_fewerButton;
+};
+
+#endif
diff --git a/juk/akodeplayer.cpp b/juk/akodeplayer.cpp
new file mode 100644
index 00000000..0d75d830
--- /dev/null
+++ b/juk/akodeplayer.cpp
@@ -0,0 +1,170 @@
+/***************************************************************************
+ copyright : (C) 2004 by Allan Sandfeld Jensen
+ email : kde@carewolf.com
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <config.h>
+
+#ifdef HAVE_AKODE
+
+#include <kdebug.h>
+
+#include <qfile.h>
+
+#include <akode/player.h>
+#include <akode/decoder.h>
+
+#include "akodeplayer.h"
+
+using namespace aKode;
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+aKodePlayer::aKodePlayer() : Player(),
+ m_player(0),
+ m_volume(1.0)
+{}
+
+aKodePlayer::~aKodePlayer()
+{
+ delete m_player;
+}
+
+void aKodePlayer::play(const FileHandle &file)
+{
+ kdDebug( 65432 ) << k_funcinfo << endl;
+
+ if (file.isNull()) { // null FileHandle file means unpause
+ if (paused())
+ m_player->resume();
+ else
+ stop();
+ return;
+ }
+
+ QString filename = file.absFilePath();
+
+ kdDebug( 65432 ) << "Opening: " << filename << endl;
+
+ if (m_player)
+ m_player->stop();
+ else {
+ m_player = new aKode::Player();
+ m_player->open("auto");
+ m_player->setVolume(m_volume);
+ }
+
+ if (m_player->load(filename.local8Bit().data()))
+ m_player->play();
+
+}
+
+void aKodePlayer::pause()
+{
+ if (m_player)
+ m_player->pause();
+}
+
+void aKodePlayer::stop()
+{
+ if (m_player) {
+ m_player->stop();
+ m_player->unload();
+ }
+}
+
+void aKodePlayer::setVolume(float volume)
+{
+ m_volume = volume;
+
+ if (m_player)
+ m_player->setVolume(m_volume);
+}
+
+float aKodePlayer::volume() const
+{
+ return m_volume;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+// m_player status functions
+/////////////////////////////////////////////////////////////////////////////////
+
+bool aKodePlayer::playing() const
+{
+ if (m_player && m_player->decoder() && m_player->state() == aKode::Player::Playing)
+ return !m_player->decoder()->eof();
+ else
+ return false;
+}
+
+bool aKodePlayer::paused() const
+{
+ return m_player && (m_player->state() == aKode::Player::Paused);
+}
+
+int aKodePlayer::totalTime() const
+{
+ if (m_player) {
+ Decoder *d = m_player->decoder();
+ if (d)
+ return d->length() / 1000;
+ }
+ return -1;
+}
+
+int aKodePlayer::currentTime() const
+{
+ if (m_player) {
+ Decoder *d = m_player->decoder();
+ if (d)
+ return d->position() / 1000;
+ }
+ return -1;
+}
+
+int aKodePlayer::position() const
+{
+ if (m_player) {
+ Decoder *d = m_player->decoder();
+ if (d && d->length())
+ return (d->position()*1000)/(d->length());
+ else
+ return -1;
+ }
+ else
+ return -1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+// m_player seek functions
+/////////////////////////////////////////////////////////////////////////////////
+
+void aKodePlayer::seek(int seekTime)
+{
+ // seek time in seconds?
+ if (m_player)
+ m_player->decoder()->seek(seekTime*1000);
+}
+
+void aKodePlayer::seekPosition(int position)
+{
+ // position unit is 1/1000th
+ if (m_player)
+ m_player->decoder()->seek((position * m_player->decoder()->length())/1000);
+}
+
+#include "akodeplayer.moc"
+
+#endif
diff --git a/juk/akodeplayer.h b/juk/akodeplayer.h
new file mode 100644
index 00000000..6b44f2fd
--- /dev/null
+++ b/juk/akodeplayer.h
@@ -0,0 +1,66 @@
+/***************************************************************************
+ copyright : (C) 2004 by Allan Sandfeld Jensen
+ email : kde@carewolf.com
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+
+#ifndef AKODEPLAYER_H
+#define AKODEPLAYER_H
+
+#include <config.h>
+
+#ifdef HAVE_AKODE
+
+#include <qstring.h>
+
+#include "player.h"
+#include <kdemacros.h>
+
+namespace aKode {
+ class File;
+ class Player;
+}
+
+class KDE_EXPORT aKodePlayer : public Player
+{
+ Q_OBJECT
+
+public:
+ aKodePlayer();
+ virtual ~aKodePlayer();
+
+ virtual void play(const FileHandle &file = FileHandle::null());
+
+ virtual void setVolume(float volume = 1.0);
+ virtual float volume() const;
+
+ virtual bool playing() const;
+ virtual bool paused() const;
+
+ virtual int totalTime() const;
+ virtual int currentTime() const;
+ virtual int position() const;
+
+ virtual void seek(int seekTime);
+ virtual void seekPosition(int position);
+
+public slots:
+ void pause();
+ void stop();
+
+private:
+ aKode::Player *m_player;
+ float m_volume;
+};
+
+#endif
+#endif
diff --git a/juk/artsplayer.cpp b/juk/artsplayer.cpp
new file mode 100644
index 00000000..410a6356
--- /dev/null
+++ b/juk/artsplayer.cpp
@@ -0,0 +1,295 @@
+/***************************************************************************
+ begin : Sun Feb 17 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+
+ copyright : (C) 2003 by Matthias Kretz
+ email : kretz@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "artsplayer.h"
+
+#if HAVE_ARTS
+
+#include <kdebug.h>
+#include <kconfig.h>
+#include <kstandarddirs.h>
+
+#include <qdir.h>
+
+#include <kartsserver.h>
+#include <kartsdispatcher.h>
+#include <kplayobject.h>
+#include <kplayobjectfactory.h>
+
+#include <cstdlib>
+#include <sys/wait.h>
+
+#include <kmessagebox.h>
+#include <kaudiomanagerplay.h>
+#include <klocale.h>
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+ArtsPlayer::ArtsPlayer() : Player(),
+ m_dispatcher(0),
+ m_server(0),
+ m_factory(0),
+ m_playobject(0),
+ m_amanPlay(0),
+ m_volumeControl(Arts::StereoVolumeControl::null()),
+ m_currentVolume(1.0)
+{
+ setupPlayer();
+}
+
+ArtsPlayer::~ArtsPlayer()
+{
+ delete m_playobject;
+ delete m_factory;
+ delete m_amanPlay;
+ delete m_server;
+ delete m_dispatcher;
+}
+
+void ArtsPlayer::play(const FileHandle &file)
+{
+ // kdDebug(65432) << k_funcinfo << endl;
+ // Make sure that the server still exists, if it doesn't a new one should
+ // be started automatically and the factory and amanPlay are created again.
+
+ if(!file.isNull())
+ m_currentURL.setPath(file.absFilePath());
+
+ if(m_server->server().isNull()) {
+ KMessageBox::error(0, i18n("Cannot find the aRts soundserver."));
+ return;
+ }
+
+ if(!m_playobject || !file.isNull()) {
+ stop();
+
+ delete m_playobject;
+ m_playobject = m_factory->createPlayObject(m_currentURL, false);
+
+ if(m_playobject->object().isNull())
+ connect(m_playobject, SIGNAL(playObjectCreated()), SLOT(playObjectCreated()));
+ else
+ playObjectCreated();
+ }
+
+ m_playobject->play();
+}
+
+void ArtsPlayer::pause()
+{
+ // kdDebug(65432) << k_funcinfo << endl;
+ if(m_playobject)
+ m_playobject->pause();
+}
+
+void ArtsPlayer::stop()
+{
+ // kdDebug(65432) << k_funcinfo << endl;
+ if(m_playobject) {
+ m_playobject->halt();
+ delete m_playobject;
+ m_playobject = 0;
+ }
+ if(!m_volumeControl.isNull()) {
+ m_volumeControl.stop();
+ m_volumeControl = Arts::StereoVolumeControl::null();
+ }
+}
+
+void ArtsPlayer::setVolume(float volume)
+{
+ // kdDebug( 65432 ) << k_funcinfo << endl;
+
+ m_currentVolume = volume;
+
+ if(serverRunning() && m_playobject && !m_playobject->isNull()) {
+ if(m_volumeControl.isNull())
+ setupVolumeControl();
+ if(!m_volumeControl.isNull()) {
+ m_volumeControl.scaleFactor(volume);
+ // kdDebug( 65432 ) << "set volume to " << volume << endl;
+ }
+ }
+}
+
+float ArtsPlayer::volume() const
+{
+ return m_currentVolume;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+// player status functions
+/////////////////////////////////////////////////////////////////////////////////
+
+bool ArtsPlayer::playing() const
+{
+ if(serverRunning() && m_playobject && m_playobject->state() == Arts::posPlaying)
+ return true;
+ else
+ return false;
+}
+
+bool ArtsPlayer::paused() const
+{
+ if(serverRunning() && m_playobject && m_playobject->state() == Arts::posPaused)
+ return true;
+ else
+ return false;
+}
+
+int ArtsPlayer::totalTime() const
+{
+ if(serverRunning() && m_playobject)
+ return m_playobject->overallTime().seconds;
+ else
+ return -1;
+}
+
+int ArtsPlayer::currentTime() const
+{
+ if(serverRunning() && m_playobject &&
+ (m_playobject->state() == Arts::posPlaying ||
+ m_playobject->state() == Arts::posPaused))
+ {
+ return m_playobject->currentTime().seconds;
+ }
+ else
+ return -1;
+}
+
+int ArtsPlayer::position() const
+{
+ if(serverRunning() && m_playobject && m_playobject->state() == Arts::posPlaying) {
+ long total = m_playobject->overallTime().seconds * 1000 + m_playobject->overallTime().ms;
+ long current = m_playobject->currentTime().seconds * 1000 + m_playobject->currentTime().ms;
+
+ // add .5 to make rounding happen properly
+
+ return int(double(current) * 1000 / total + .5);
+ }
+ else
+ return -1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+// player seek functions
+/////////////////////////////////////////////////////////////////////////////////
+
+void ArtsPlayer::seek(int seekTime)
+{
+ if(serverRunning() && m_playobject) {
+ Arts::poTime poSeekTime;
+ poSeekTime.custom = 0;
+ poSeekTime.ms = 0;
+ poSeekTime.seconds = seekTime;
+ m_playobject->object().seek(poSeekTime);
+ }
+}
+
+void ArtsPlayer::seekPosition(int position)
+{
+ if(serverRunning() && m_playobject) {
+ Arts::poTime poSeekTime;
+ long total = m_playobject->overallTime().seconds;
+ poSeekTime.custom = 0;
+ poSeekTime.ms = 0;
+ poSeekTime.seconds = long(double(total) * position / 1000 + .5);
+ m_playobject->object().seek(poSeekTime);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+// private
+/////////////////////////////////////////////////////////////////////////////////
+
+void ArtsPlayer::setupArtsObjects()
+{
+ // kdDebug( 65432 ) << k_funcinfo << endl;
+ delete m_factory;
+ delete m_amanPlay;
+ m_volumeControl = Arts::StereoVolumeControl::null();
+ m_factory = new KDE::PlayObjectFactory(m_server);
+ m_amanPlay = new KAudioManagerPlay(m_server);
+
+ if(m_amanPlay->isNull() || !m_factory) {
+ KMessageBox::error(0, i18n("Connecting/starting aRts soundserver failed. "
+ "Make sure that artsd is configured properly."));
+ exit(1);
+ }
+
+ m_amanPlay->setTitle(i18n("JuK"));
+ m_amanPlay->setAutoRestoreID("JuKAmanPlay");
+
+ m_factory->setAudioManagerPlay(m_amanPlay);
+}
+
+void ArtsPlayer::playObjectCreated()
+{
+ // kdDebug(65432) << k_funcinfo << endl;
+ setVolume(m_currentVolume);
+}
+
+void ArtsPlayer::setupPlayer()
+{
+ m_dispatcher = new KArtsDispatcher;
+ m_server = new KArtsServer;
+ setupArtsObjects();
+ connect(m_server, SIGNAL(restartedServer()), SLOT(setupArtsObjects()));
+}
+
+void ArtsPlayer::setupVolumeControl()
+{
+ // kdDebug( 65432 ) << k_funcinfo << endl;
+ m_volumeControl = Arts::DynamicCast(m_server->server().createObject("Arts::StereoVolumeControl"));
+ if(!m_volumeControl.isNull() && !m_playobject->isNull() && !m_playobject->object().isNull()) {
+ Arts::Synth_AMAN_PLAY ap = m_amanPlay->amanPlay();
+ Arts::PlayObject po = m_playobject->object();
+ ap.stop();
+ Arts::disconnect(po, "left" , ap, "left" );
+ Arts::disconnect(po, "right", ap, "right");
+
+ m_volumeControl.start();
+ ap.start();
+
+ Arts::connect(po, "left" , m_volumeControl, "inleft" );
+ Arts::connect(po, "right", m_volumeControl, "inright");
+ Arts::connect(m_volumeControl, "outleft" , ap, "left" );
+ Arts::connect(m_volumeControl, "outright", ap, "right");
+ // kdDebug( 65432 ) << "connected volume control" << endl;
+ }
+ else {
+ m_volumeControl = Arts::StereoVolumeControl::null();
+ kdDebug(65432) << "Could not initialize volume control!" << endl;
+ }
+}
+
+bool ArtsPlayer::serverRunning() const
+{
+ if(m_server)
+ return !(m_server->server().isNull());
+ else
+ return false;
+}
+
+#include "artsplayer.moc"
+
+#endif
+
+// vim: sw=4 ts=8 et
diff --git a/juk/artsplayer.h b/juk/artsplayer.h
new file mode 100644
index 00000000..2c1306e1
--- /dev/null
+++ b/juk/artsplayer.h
@@ -0,0 +1,95 @@
+/***************************************************************************
+ begin : Sun Feb 17 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+
+ copyright : (C) 2003 by Matthias Kretz
+ email : kretz@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef ARTSPLAYER_H
+#define ARTSPLAYER_H
+
+#include <config.h>
+
+#if HAVE_ARTS
+
+#include "player.h"
+
+#include <kurl.h>
+
+#include <qstring.h>
+#include <qobject.h>
+#include <artsflow.h>
+
+class KArtsDispatcher;
+class KArtsServer;
+class KAudioManagerPlay;
+
+namespace KDE {
+ class PlayObjectFactory;
+ class PlayObject;
+}
+
+class ArtsPlayer : public Player
+{
+ Q_OBJECT
+
+public:
+ ArtsPlayer();
+ virtual ~ArtsPlayer();
+
+ virtual void play(const FileHandle &file = FileHandle::null());
+ virtual void pause();
+ virtual void stop();
+
+ virtual void setVolume(float volume = 1.0);
+ virtual float volume() const;
+
+ virtual bool playing() const;
+ virtual bool paused() const;
+
+ virtual int totalTime() const;
+ virtual int currentTime() const;
+ virtual int position() const; // in this case not really the percent
+
+ virtual void seek(int seekTime);
+ virtual void seekPosition(int position);
+
+private slots:
+ void setupArtsObjects();
+ void playObjectCreated();
+
+private:
+ void setupPlayer();
+ void setupVolumeControl();
+ bool serverRunning() const;
+
+ KArtsDispatcher *m_dispatcher;
+ KArtsServer *m_server;
+ KDE::PlayObjectFactory *m_factory;
+ KDE::PlayObject *m_playobject;
+ KAudioManagerPlay *m_amanPlay;
+
+ // This is a pretty heavy module for the needs that JuK has, it would probably
+ // be good to use two Synth_MUL instead or the one from Noatun.
+
+ Arts::StereoVolumeControl m_volumeControl;
+
+ KURL m_currentURL;
+ float m_currentVolume;
+};
+
+#endif
+#endif
+
+// vim: sw=4 ts=8 et
diff --git a/juk/cache.cpp b/juk/cache.cpp
new file mode 100644
index 00000000..3f37ee4a
--- /dev/null
+++ b/juk/cache.cpp
@@ -0,0 +1,323 @@
+/***************************************************************************
+ begin : Sat Sep 7 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kstandarddirs.h>
+#include <kmessagebox.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kactionclasses.h>
+#include <kdebug.h>
+
+#include <qdir.h>
+#include <qbuffer.h>
+
+#include "cache.h"
+#include "tag.h"
+#include "searchplaylist.h"
+#include "historyplaylist.h"
+#include "upcomingplaylist.h"
+#include "folderplaylist.h"
+#include "playlistcollection.h"
+#include "actioncollection.h"
+
+using namespace ActionCollection;
+
+static const int playlistCacheVersion = 2;
+
+enum PlaylistType
+{
+ Normal = 0,
+ Search = 1,
+ History = 2,
+ Upcoming = 3,
+ Folder = 4
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+Cache *Cache::instance()
+{
+ static Cache cache;
+
+ // load() indirectly calls instance() so we have to protect against recursion.
+ static bool loaded = false;
+
+ if(!loaded) {
+ loaded = true;
+ cache.load();
+ }
+
+ return &cache;
+}
+
+void Cache::save()
+{
+ QString dirName = KGlobal::dirs()->saveLocation("appdata");
+ QString cacheFileName = dirName + "cache.new";
+
+ QFile f(cacheFileName);
+
+ if(!f.open(IO_WriteOnly))
+ return;
+
+ QByteArray data;
+ QDataStream s(data, IO_WriteOnly);
+
+ for(Iterator it = begin(); it != end(); ++it) {
+ s << (*it).absFilePath();
+ s << *it;
+ }
+
+ QDataStream fs(&f);
+
+ Q_INT32 checksum = qChecksum(data.data(), data.size());
+
+ fs << Q_INT32(m_currentVersion)
+ << checksum
+ << data;
+
+ f.close();
+
+ QDir(dirName).rename("cache.new", "cache");
+}
+
+void Cache::loadPlaylists(PlaylistCollection *collection) // static
+{
+ QString playlistsFile = KGlobal::dirs()->saveLocation("appdata") + "playlists";
+
+ QFile f(playlistsFile);
+
+ if(!f.open(IO_ReadOnly))
+ return;
+
+ QDataStream fs(&f);
+
+ Q_INT32 version;
+ fs >> version;
+
+ switch(version) {
+ case 1:
+ case 2:
+ {
+ // Our checksum is only for the values after the version and checksum so
+ // we want to get a byte array with just the checksummed data.
+
+ QByteArray data;
+ Q_UINT16 checksum;
+ fs >> checksum >> data;
+
+ if(checksum != qChecksum(data.data(), data.size()))
+ return;
+
+ // Create a new stream just based on the data.
+
+ QDataStream s(data, IO_ReadOnly);
+
+ while(!s.atEnd()) {
+
+ Q_INT32 playlistType;
+ s >> playlistType;
+
+ Playlist *playlist = 0;
+
+ switch(playlistType) {
+ case Search:
+ {
+ SearchPlaylist *p = new SearchPlaylist(collection);
+ s >> *p;
+ playlist = p;
+ break;
+ }
+ case History:
+ {
+ action<KToggleAction>("showHistory")->setChecked(true);
+ collection->setHistoryPlaylistEnabled(true);
+ s >> *collection->historyPlaylist();
+ playlist = collection->historyPlaylist();
+ break;
+ }
+ case Upcoming:
+ {
+ /*
+ collection->setUpcomingPlaylistEnabled(true);
+ Playlist *p = collection->upcomingPlaylist();
+ action<KToggleAction>("saveUpcomingTracks")->setChecked(true);
+ s >> *p;
+ playlist = p;
+ */
+ break;
+ }
+ case Folder:
+ {
+ FolderPlaylist *p = new FolderPlaylist(collection);
+ s >> *p;
+ playlist = p;
+ break;
+ }
+ default:
+ Playlist *p = new Playlist(collection, true);
+ s >> *p;
+ playlist = p;
+ break;
+ }
+ if(version == 2) {
+ Q_INT32 sortColumn;
+ s >> sortColumn;
+ if(playlist)
+ playlist->setSorting(sortColumn);
+ }
+ }
+ break;
+ }
+ default:
+ {
+ // Because the original version of the playlist cache did not contain a
+ // version number, we want to revert to the beginning of the file before
+ // reading the data.
+
+ f.reset();
+
+ while(!fs.atEnd()) {
+ Playlist *p = new Playlist(collection);
+ fs >> *p;
+ }
+ break;
+ }
+ }
+
+ f.close();
+}
+
+void Cache::savePlaylists(const PlaylistList &playlists)
+{
+ QString dirName = KGlobal::dirs()->saveLocation("appdata");
+ QString playlistsFile = dirName + "playlists.new";
+ QFile f(playlistsFile);
+
+ if(!f.open(IO_WriteOnly))
+ return;
+
+ QByteArray data;
+ QDataStream s(data, IO_WriteOnly);
+
+ for(PlaylistList::ConstIterator it = playlists.begin(); it != playlists.end(); ++it) {
+ if(*it) {
+ if(dynamic_cast<HistoryPlaylist *>(*it)) {
+ s << Q_INT32(History)
+ << *static_cast<HistoryPlaylist *>(*it);
+ }
+ else if(dynamic_cast<SearchPlaylist *>(*it)) {
+ s << Q_INT32(Search)
+ << *static_cast<SearchPlaylist *>(*it);
+ }
+ else if(dynamic_cast<UpcomingPlaylist *>(*it)) {
+ if(!action<KToggleAction>("saveUpcomingTracks")->isChecked())
+ continue;
+ s << Q_INT32(Upcoming)
+ << *static_cast<UpcomingPlaylist *>(*it);
+ }
+ else if(dynamic_cast<FolderPlaylist *>(*it)) {
+ s << Q_INT32(Folder)
+ << *static_cast<FolderPlaylist *>(*it);
+ }
+ else {
+ s << Q_INT32(Normal)
+ << *(*it);
+ }
+ s << Q_INT32((*it)->sortColumn());
+ }
+ }
+
+ QDataStream fs(&f);
+ fs << Q_INT32(playlistCacheVersion);
+ fs << qChecksum(data.data(), data.size());
+
+ fs << data;
+ f.close();
+
+ QDir(dirName).rename("playlists.new", "playlists");
+}
+
+bool Cache::cacheFileExists() // static
+{
+ return QFile::exists(KGlobal::dirs()->saveLocation("appdata") + "cache");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected methods
+////////////////////////////////////////////////////////////////////////////////
+
+Cache::Cache() : FileHandleHash()
+{
+
+}
+
+void Cache::load()
+{
+ QString cacheFileName = KGlobal::dirs()->saveLocation("appdata") + "cache";
+
+ QFile f(cacheFileName);
+
+ if(!f.open(IO_ReadOnly))
+ return;
+
+ CacheDataStream s(&f);
+
+ Q_INT32 version;
+ s >> version;
+
+ QBuffer buffer;
+
+ // Do the version specific stuff.
+
+ switch(version) {
+ case 1: {
+ s.setCacheVersion(1);
+
+ Q_INT32 checksum;
+ QByteArray data;
+ s >> checksum
+ >> data;
+
+ buffer.setBuffer(data);
+ buffer.open(IO_ReadOnly);
+ s.setDevice(&buffer);
+
+ if(checksum != qChecksum(data.data(), data.size())) {
+ KMessageBox::sorry(0, i18n("The music data cache has been corrupted. JuK "
+ "needs to rescan it now. This may take some time."));
+ return;
+ }
+ break;
+ }
+ default: {
+ s.device()->reset();
+ s.setCacheVersion(0);
+ break;
+ }
+ }
+
+ // Read the cached tags.
+
+ while(!s.atEnd()) {
+ QString fileName;
+ s >> fileName;
+ fileName.squeeze();
+
+ FileHandle f(fileName, s);
+ }
+}
diff --git a/juk/cache.h b/juk/cache.h
new file mode 100644
index 00000000..09f6906d
--- /dev/null
+++ b/juk/cache.h
@@ -0,0 +1,67 @@
+/***************************************************************************
+ begin : Sat Sep 7 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef CACHE_H
+#define CACHE_H
+
+
+#include "stringhash.h"
+
+class Tag;
+class Playlist;
+class PlaylistCollection;
+
+typedef QValueList<Playlist *> PlaylistList;
+
+class Cache : public FileHandleHash
+{
+public:
+ static Cache *instance();
+ void save();
+
+ static void loadPlaylists(PlaylistCollection *collection);
+ static void savePlaylists(const PlaylistList &playlists);
+
+ static bool cacheFileExists();
+
+protected:
+ Cache();
+ void load();
+
+private:
+ static const int m_currentVersion = 1;
+};
+
+/**
+ * A simple QDataStream subclass that has an extra field to indicate the cache
+ * version.
+ */
+
+class CacheDataStream : public QDataStream
+{
+public:
+ CacheDataStream(QIODevice *d) : QDataStream(d), m_cacheVersion(0) {}
+ CacheDataStream(QByteArray a, int mode) : QDataStream(a, mode), m_cacheVersion(0) {}
+
+ virtual ~CacheDataStream() {}
+
+ int cacheVersion() const { return m_cacheVersion; }
+ void setCacheVersion(int v) { m_cacheVersion = v; }
+
+private:
+ int m_cacheVersion;
+};
+
+#endif
diff --git a/juk/categoryreaderinterface.cpp b/juk/categoryreaderinterface.cpp
new file mode 100644
index 00000000..f9f73103
--- /dev/null
+++ b/juk/categoryreaderinterface.cpp
@@ -0,0 +1,67 @@
+/***************************************************************************
+ begin : Sun Oct 31 2004
+ copyright : (C) 2004 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <qstring.h>
+
+#include "filerenameroptions.h"
+#include "categoryreaderinterface.h"
+
+QString CategoryReaderInterface::value(const CategoryID &category) const
+{
+ QString value = categoryValue(category.category).stripWhiteSpace();
+
+ if(category.category == Track)
+ value = fixupTrack(value, category.categoryNumber).stripWhiteSpace();
+
+ if(value.isEmpty() && emptyAction(category) == TagRenamerOptions::UseReplacementValue)
+ value = emptyText(category);
+
+ return prefix(category) + value + suffix(category);
+}
+
+bool CategoryReaderInterface::isRequired(const CategoryID &category) const
+{
+ return emptyAction(category) != TagRenamerOptions::IgnoreEmptyTag;
+}
+
+bool CategoryReaderInterface::isEmpty(TagType category) const
+{
+ return categoryValue(category).isEmpty();
+}
+
+QString CategoryReaderInterface::fixupTrack(const QString &track, unsigned categoryNum) const
+{
+ QString str(track);
+ CategoryID trackId(Track, categoryNum);
+
+ if(track == "0") {
+ if(emptyAction(trackId) == TagRenamerOptions::UseReplacementValue)
+ str = emptyText(trackId);
+ else
+ return QString::null;
+ }
+
+ unsigned minimumWidth = trackWidth(categoryNum);
+
+ if(str.length() < minimumWidth) {
+ QString prefix;
+ prefix.fill('0', minimumWidth - str.length());
+ return prefix + str;
+ }
+
+ return str;
+}
+
+// vim: set et sw=4 ts=4:
diff --git a/juk/categoryreaderinterface.h b/juk/categoryreaderinterface.h
new file mode 100644
index 00000000..5d2dfd0f
--- /dev/null
+++ b/juk/categoryreaderinterface.h
@@ -0,0 +1,122 @@
+/***************************************************************************
+ begin : Sun Oct 31 2004
+ copyright : (C) 2004 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef JUK_CATEGORYREADERINTERFACE_H
+#define JUK_CATEGORYREADERINTERFACE_H
+
+#include "tagrenameroptions.h"
+
+enum TagType;
+class QString;
+
+template<class T> class QValueList;
+
+/**
+ * This class is used to map categories into values. You should implement the
+ * functionality in a subclass.
+ *
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ */
+class CategoryReaderInterface
+{
+public:
+ virtual ~CategoryReaderInterface() { }
+
+ /**
+ * Returns the textual representation of \p type, without any processing done
+ * on it. For example, track values shouldn't be expanded out to the minimum
+ * width from this function. No CategoryID is needed since the value is constant
+ * for a category.
+ *
+ * @param type, The category to retrieve the value of.
+ * @return textual representation of that category's value.
+ */
+ virtual QString categoryValue(TagType type) const = 0;
+
+ /**
+ * Returns the user-specified prefix string for \p category.
+ *
+ * @param category the category to retrieve the value for.
+ * @return user-specified prefix string for \p category.
+ */
+ virtual QString prefix(const CategoryID &category) const = 0;
+
+ /**
+ * Returns the user-specified suffix string for \p category.
+ *
+ * @param category the category to retrieve the value for.
+ * @return user-specified suffix string for \p category.
+ */
+ virtual QString suffix(const CategoryID &category) const = 0;
+
+ /**
+ * Returns the user-specified empty action for \p category.
+ *
+ * @param category the category to retrieve the value for.
+ * @return user-specified empty action for \p category.
+ */
+ virtual TagRenamerOptions::EmptyActions emptyAction(const CategoryID &category) const = 0;
+
+ /**
+ * Returns the user-specified empty text for \p category. This text might
+ * be used to replace an empty value.
+ *
+ * @param category the category to retrieve the value for.
+ * @return the user-specified empty text for \p category.
+ */
+ virtual QString emptyText(const CategoryID &category) const = 0;
+
+ /**
+ * @return the categories in the order the user has chosen. Categories may
+ * be repeated (which is why CategoryID has the categoryNumber value to
+ * disambiguate duplicates).
+ */
+ virtual QValueList<CategoryID> categoryOrder() const = 0;
+
+ /**
+ * @return track width for the Track item identified by categoryNum.
+ */
+ virtual int trackWidth(unsigned categoryNum) const = 0;
+
+ // You probably shouldn't reimplement this
+ virtual QString value(const CategoryID &category) const;
+
+ virtual QString separator() const = 0;
+
+ virtual QString musicFolder() const = 0;
+
+ /**
+ * @param index the zero-based index of the item just before the
+ * separator in question.
+ * @return true if a folder separator should be placed between the tags
+ * at index and index + 1.
+ */
+ virtual bool hasFolderSeparator(unsigned index) const = 0;
+
+ virtual bool isDisabled(const CategoryID &category) const = 0;
+
+ // You probably shouldn't reimplement this
+ virtual bool isRequired(const CategoryID &category) const;
+
+ // You probably shouldn't reimplement this
+ virtual bool isEmpty(TagType category) const;
+
+ // You probably shouldn't reimplement this
+ virtual QString fixupTrack(const QString &track, unsigned categoryNum) const;
+};
+
+#endif /* JUK_CATEGORYREADERINTERFACE_H */
+
+// vim: set et sw=4 ts=4:
diff --git a/juk/collectionlist.cpp b/juk/collectionlist.cpp
new file mode 100644
index 00000000..9540be85
--- /dev/null
+++ b/juk/collectionlist.cpp
@@ -0,0 +1,511 @@
+/***************************************************************************
+ begin : Fri Sep 13 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kurldrag.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+#include <kpopupmenu.h>
+#include <kiconloader.h>
+#include <kconfig.h>
+#include <kaction.h>
+#include <kurl.h>
+
+#include "collectionlist.h"
+#include "playlistcollection.h"
+#include "splashscreen.h"
+#include "stringshare.h"
+#include "cache.h"
+#include "actioncollection.h"
+#include "tag.h"
+#include "viewmode.h"
+#include "coverinfo.h"
+#include "covermanager.h"
+
+using namespace ActionCollection;
+
+////////////////////////////////////////////////////////////////////////////////
+// static methods
+////////////////////////////////////////////////////////////////////////////////
+
+CollectionList *CollectionList::m_list = 0;
+
+CollectionList *CollectionList::instance()
+{
+ return m_list;
+}
+
+void CollectionList::initialize(PlaylistCollection *collection)
+{
+ if(m_list)
+ return;
+
+ // We have to delay initilaization here because dynamic_cast or comparing to
+ // the collection instance won't work in the PlaylistBox::Item initialization
+ // won't work until the CollectionList is fully constructed.
+
+ m_list = new CollectionList(collection);
+ m_list->setName(i18n("Collection List"));
+
+ FileHandleHash::Iterator end = Cache::instance()->end();
+ for(FileHandleHash::Iterator it = Cache::instance()->begin(); it != end; ++it)
+ new CollectionListItem(*it);
+
+ SplashScreen::update();
+
+ // The CollectionList is created with sorting disabled for speed. Re-enable
+ // it here, and perform the sort.
+ KConfigGroup config(KGlobal::config(), "Playlists");
+
+ SortOrder order = Descending;
+ if(config.readBoolEntry("CollectionListSortAscending", true))
+ order = Ascending;
+
+ m_list->setSortOrder(order);
+ m_list->setSortColumn(config.readNumEntry("CollectionListSortColumn", 1));
+
+ m_list->sort();
+
+ collection->setupPlaylist(m_list, "folder_sound");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+PlaylistItem *CollectionList::createItem(const FileHandle &file, QListViewItem *, bool)
+{
+ // It's probably possible to optimize the line below away, but, well, right
+ // now it's more important to not load duplicate items.
+
+ if(m_itemsDict.find(file.absFilePath()))
+ return 0;
+
+ PlaylistItem *item = new CollectionListItem(file);
+
+ if(!item->isValid()) {
+ kdError() << "CollectionList::createItem() -- A valid tag was not created for \""
+ << file.absFilePath() << "\"" << endl;
+ delete item;
+ return 0;
+ }
+
+ setupItem(item);
+
+ return item;
+}
+
+void CollectionList::clearItems(const PlaylistItemList &items)
+{
+ for(PlaylistItemList::ConstIterator it = items.begin(); it != items.end(); ++it) {
+ Cache::instance()->remove((*it)->file());
+ clearItem(*it, false);
+ }
+
+ dataChanged();
+}
+
+void CollectionList::setupTreeViewEntries(ViewMode *viewMode) const
+{
+ TreeViewMode *treeViewMode = dynamic_cast<TreeViewMode *>(viewMode);
+ if(!treeViewMode) {
+ kdWarning(65432) << "Can't setup entries on a non-tree-view mode!\n";
+ return;
+ }
+
+ QValueList<int> columnList;
+ columnList << PlaylistItem::ArtistColumn;
+ columnList << PlaylistItem::GenreColumn;
+ columnList << PlaylistItem::AlbumColumn;
+
+ QStringList items;
+ for(QValueList<int>::Iterator colIt = columnList.begin(); colIt != columnList.end(); ++colIt) {
+ items.clear();
+ for(TagCountDictIterator it(*m_columnTags[*colIt]); it.current(); ++it)
+ items << it.currentKey();
+
+ treeViewMode->addItems(items, *colIt);
+ }
+}
+
+void CollectionList::slotNewItems(const KFileItemList &items)
+{
+ QStringList files;
+
+ for(KFileItemListIterator it(items); it.current(); ++it)
+ files.append((*it)->url().path());
+
+ addFiles(files);
+ update();
+}
+
+void CollectionList::slotRefreshItems(const KFileItemList &items)
+{
+ for(KFileItemListIterator it(items); it.current(); ++it) {
+ CollectionListItem *item = lookup((*it)->url().path());
+
+ if(item) {
+ item->refreshFromDisk();
+
+ // If the item is no longer on disk, remove it from the collection.
+
+ if(item->file().fileInfo().exists())
+ item->repaint();
+ else
+ clearItem(item);
+ }
+ }
+
+ update();
+}
+
+void CollectionList::slotDeleteItem(KFileItem *item)
+{
+ CollectionListItem *listItem = lookup(item->url().path());
+ if(listItem)
+ clearItem(listItem);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public slots
+////////////////////////////////////////////////////////////////////////////////
+
+void CollectionList::clear()
+{
+ int result = KMessageBox::warningContinueCancel(this,
+ i18n("Removing an item from the collection will also remove it from "
+ "all of your playlists. Are you sure you want to continue?\n\n"
+ "Note, however, that if the directory that these files are in is in "
+ "your \"scan on startup\" list, they will be readded on startup."));
+
+ if(result == KMessageBox::Continue) {
+ Playlist::clear();
+ emit signalCollectionChanged();
+ }
+}
+
+void CollectionList::slotCheckCache()
+{
+ PlaylistItemList invalidItems;
+
+ for(QDictIterator<CollectionListItem>it(m_itemsDict); it.current(); ++it) {
+ if(!it.current()->checkCurrent())
+ invalidItems.append(*it);
+ processEvents();
+ }
+
+ clearItems(invalidItems);
+}
+
+void CollectionList::slotRemoveItem(const QString &file)
+{
+ clearItem(m_itemsDict[file]);
+}
+
+void CollectionList::slotRefreshItem(const QString &file)
+{
+ m_itemsDict[file]->refresh();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected methods
+////////////////////////////////////////////////////////////////////////////////
+
+CollectionList::CollectionList(PlaylistCollection *collection) :
+ Playlist(collection, true),
+ m_itemsDict(5003),
+ m_columnTags(15, 0)
+{
+ new KAction(i18n("Show Playing"), KShortcut(), actions(), "showPlaying");
+
+ connect(action("showPlaying"), SIGNAL(activated()), this, SLOT(slotShowPlaying()));
+
+ connect(action<KToolBarPopupAction>("back")->popupMenu(), SIGNAL(aboutToShow()),
+ this, SLOT(slotPopulateBackMenu()));
+ connect(action<KToolBarPopupAction>("back")->popupMenu(), SIGNAL(activated(int)),
+ this, SLOT(slotPlayFromBackMenu(int)));
+ setSorting(-1); // Temporarily disable sorting to add items faster.
+
+ m_columnTags[PlaylistItem::ArtistColumn] = new TagCountDict(5001, false);
+ m_columnTags[PlaylistItem::ArtistColumn]->setAutoDelete(true);
+
+ m_columnTags[PlaylistItem::AlbumColumn] = new TagCountDict(5001, false);
+ m_columnTags[PlaylistItem::AlbumColumn]->setAutoDelete(true);
+
+ m_columnTags[PlaylistItem::GenreColumn] = new TagCountDict(5001, false);
+ m_columnTags[PlaylistItem::GenreColumn]->setAutoDelete(true);
+
+ polish();
+}
+
+CollectionList::~CollectionList()
+{
+ KConfigGroup config(KGlobal::config(), "Playlists");
+ config.writeEntry("CollectionListSortColumn", sortColumn());
+ config.writeEntry("CollectionListSortAscending", sortOrder() == Ascending);
+
+ // The CollectionListItems will try to remove themselves from the
+ // m_columnTags member, so we must make sure they're gone before we
+ // are.
+
+ clearItems(items());
+ for(TagCountDicts::Iterator it = m_columnTags.begin(); it != m_columnTags.end(); ++it)
+ delete *it;
+}
+
+void CollectionList::contentsDropEvent(QDropEvent *e)
+{
+ if(e->source() == this)
+ return; // Don't rearrange in the CollectionList.
+ else
+ Playlist::contentsDropEvent(e);
+}
+
+void CollectionList::contentsDragMoveEvent(QDragMoveEvent *e)
+{
+ if(canDecode(e) && e->source() != this)
+ e->accept(true);
+ else
+ e->accept(false);
+}
+
+QString CollectionList::addStringToDict(const QString &value, unsigned column)
+{
+ if(column > m_columnTags.count() || value.stripWhiteSpace().isEmpty())
+ return QString::null;
+
+ int *refCountPtr = m_columnTags[column]->find(value);
+ if(refCountPtr)
+ ++(*refCountPtr);
+ else {
+ m_columnTags[column]->insert(value, new int(1));
+ emit signalNewTag(value, column);
+ }
+
+ return value;
+}
+
+QStringList CollectionList::uniqueSet(UniqueSetType t) const
+{
+ int column;
+
+ switch(t)
+ {
+ case Artists:
+ column = PlaylistItem::ArtistColumn;
+ break;
+
+ case Albums:
+ column = PlaylistItem::AlbumColumn;
+ break;
+
+ case Genres:
+ column = PlaylistItem::GenreColumn;
+ break;
+
+ default:
+ return QStringList();
+ }
+
+ if((unsigned) column >= m_columnTags.count())
+ return QStringList();
+
+ TagCountDictIterator it(*m_columnTags[column]);
+ QStringList list;
+
+ for(; it.current(); ++it)
+ list += it.currentKey();
+
+ return list;
+}
+
+void CollectionList::removeStringFromDict(const QString &value, unsigned column)
+{
+ if(column > m_columnTags.count() || value.isEmpty())
+ return;
+
+ int *refCountPtr = m_columnTags[column]->find(value);
+ if(refCountPtr) {
+ --(*refCountPtr);
+ if(*refCountPtr == 0) {
+ emit signalRemovedTag(value, column);
+ m_columnTags[column]->remove(value);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CollectionListItem public methods
+////////////////////////////////////////////////////////////////////////////////
+
+void CollectionListItem::refresh()
+{
+ int offset = static_cast<Playlist *>(listView())->columnOffset();
+ int columns = lastColumn() + offset + 1;
+
+ data()->local8Bit.resize(columns);
+ data()->cachedWidths.resize(columns);
+
+ for(int i = offset; i < columns; i++) {
+ int id = i - offset;
+ if(id != TrackNumberColumn && id != LengthColumn) {
+ // All columns other than track num and length need local-encoded data for sorting
+
+ QCString lower = text(i).lower().local8Bit();
+
+ // For some columns, we may be able to share some strings
+
+ if((id == ArtistColumn) || (id == AlbumColumn) ||
+ (id == GenreColumn) || (id == YearColumn) ||
+ (id == CommentColumn))
+ {
+ lower = StringShare::tryShare(lower);
+
+ if(id != YearColumn && id != CommentColumn && data()->local8Bit[id] != lower) {
+ CollectionList::instance()->removeStringFromDict(data()->local8Bit[id], id);
+ CollectionList::instance()->addStringToDict(text(i), id);
+ }
+ }
+
+ data()->local8Bit[id] = lower;
+ }
+
+ int newWidth = width(listView()->fontMetrics(), listView(), i);
+ data()->cachedWidths[i] = newWidth;
+
+ if(newWidth != data()->cachedWidths[i])
+ playlist()->slotWeightDirty(i);
+ }
+
+ file().coverInfo()->setCover();
+
+ if(listView()->isVisible())
+ repaint();
+
+ for(PlaylistItemList::Iterator it = m_children.begin(); it != m_children.end(); ++it) {
+ (*it)->playlist()->update();
+ (*it)->playlist()->dataChanged();
+ if((*it)->listView()->isVisible())
+ (*it)->repaint();
+ }
+
+ CollectionList::instance()->dataChanged();
+ emit CollectionList::instance()->signalCollectionChanged();
+}
+
+PlaylistItem *CollectionListItem::itemForPlaylist(const Playlist *playlist)
+{
+ if(playlist == CollectionList::instance())
+ return this;
+
+ PlaylistItemList::ConstIterator it;
+ for(it = m_children.begin(); it != m_children.end(); ++it)
+ if((*it)->playlist() == playlist)
+ return *it;
+ return 0;
+}
+
+void CollectionListItem::updateCollectionDict(const QString &oldPath, const QString &newPath)
+{
+ CollectionList *collection = CollectionList::instance();
+
+ if(!collection)
+ return;
+
+ collection->removeFromDict(oldPath);
+ collection->addToDict(newPath, this);
+}
+
+void CollectionListItem::repaint() const
+{
+ QListViewItem::repaint();
+ for(PlaylistItemList::ConstIterator it = m_children.begin(); it != m_children.end(); ++it)
+ (*it)->repaint();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CollectionListItem protected methods
+////////////////////////////////////////////////////////////////////////////////
+
+CollectionListItem::CollectionListItem(const FileHandle &file) :
+ PlaylistItem(CollectionList::instance()),
+ m_shuttingDown(false)
+{
+ CollectionList *l = CollectionList::instance();
+ if(l) {
+ l->addToDict(file.absFilePath(), this);
+
+ data()->fileHandle = file;
+
+ if(file.tag()) {
+ refresh();
+ l->dataChanged();
+ // l->addWatched(m_path);
+ }
+ else
+ kdError() << "CollectionListItem::CollectionListItem() -- Tag() could not be created." << endl;
+ }
+ else
+ kdError(65432) << "CollectionListItems should not be created before "
+ << "CollectionList::initialize() has been called." << endl;
+
+ SplashScreen::increment();
+}
+
+CollectionListItem::~CollectionListItem()
+{
+ m_shuttingDown = true;
+
+ for(PlaylistItemList::ConstIterator it = m_children.begin();
+ it != m_children.end();
+ ++it)
+ {
+ (*it)->playlist()->clearItem(*it);
+ }
+
+ CollectionList *l = CollectionList::instance();
+ if(l) {
+ l->removeFromDict(file().absFilePath());
+ l->removeStringFromDict(file().tag()->album(), AlbumColumn);
+ l->removeStringFromDict(file().tag()->artist(), ArtistColumn);
+ l->removeStringFromDict(file().tag()->genre(), GenreColumn);
+ }
+}
+
+void CollectionListItem::addChildItem(PlaylistItem *child)
+{
+ m_children.append(child);
+}
+
+void CollectionListItem::removeChildItem(PlaylistItem *child)
+{
+ if(!m_shuttingDown)
+ m_children.remove(child);
+}
+
+bool CollectionListItem::checkCurrent()
+{
+ if(!file().fileInfo().exists() || !file().fileInfo().isFile())
+ return false;
+
+ if(!file().current()) {
+ file().refresh();
+ refresh();
+ }
+
+ return true;
+}
+
+#include "collectionlist.moc"
diff --git a/juk/collectionlist.h b/juk/collectionlist.h
new file mode 100644
index 00000000..c5cafca2
--- /dev/null
+++ b/juk/collectionlist.h
@@ -0,0 +1,197 @@
+/***************************************************************************
+ begin : Fri Sep 13 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef COLLECTIONLIST_H
+#define COLLECTIONLIST_H
+
+#include <kapplication.h>
+#include <kdirwatch.h>
+#include <kfileitem.h>
+
+#include <qdict.h>
+#include <qclipboard.h>
+#include <qvaluevector.h>
+
+#include "playlist.h"
+#include "playlistitem.h"
+#include "sortedstringlist.h"
+
+class CollectionListItem;
+class ViewMode;
+
+/**
+ * This type is for mapping QString track attributes like the album, artist
+ * and track to an integer count representing the number of outstanding items
+ * that hold the string.
+ */
+
+typedef QDict<int> TagCountDict;
+typedef QDictIterator<int> TagCountDictIterator;
+
+/**
+ * We then have an array of dicts, one for each column in the list view. We
+ * use pointers to TagCountDicts because QDict has a broken copy ctor, which
+ * doesn't copy the case sensitivity setting.
+ */
+
+typedef QValueVector<TagCountDict*> TagCountDicts;
+
+/**
+ * This is the "collection", or all of the music files that have been opened
+ * in any playlist and not explicitly removed from the collection.
+ *
+ * It is being implemented as a "semi-singleton" because I need universal access
+ * to just one instance. However, because the collection needs initialization
+ * parameters (that will not always be available when an instance is needed).
+ * Hence there will be the familiar singleton "instance()" method allong with an
+ * "initialize()" method.
+ */
+
+class CollectionList : public Playlist
+{
+ friend class CollectionListItem;
+
+ Q_OBJECT
+
+public:
+ /**
+ * A variety of unique value lists will be kept in the collection. This
+ * enum can be used as an index into those structures.
+ */
+ enum UniqueSetType { Artists = 0, Albums = 1, Genres = 2 };
+
+ static CollectionList *instance();
+ static void initialize(PlaylistCollection *collection);
+
+ /**
+ * Returns a unique set of values associated with the type specified.
+ */
+ QStringList uniqueSet(UniqueSetType t) const;
+
+ CollectionListItem *lookup(const QString &file) { return m_itemsDict.find(file); }
+
+ virtual PlaylistItem *createItem(const FileHandle &file,
+ QListViewItem * = 0,
+ bool = false);
+
+ void emitVisibleColumnsChanged() { emit signalVisibleColumnsChanged(); }
+
+ virtual void clearItems(const PlaylistItemList &items);
+
+ void setupTreeViewEntries(ViewMode *viewMode) const;
+
+ virtual bool canReload() const { return true; }
+
+public slots:
+ virtual void paste() { decode(kapp->clipboard()->data()); }
+ virtual void clear();
+ void slotCheckCache();
+
+ void slotRemoveItem(const QString &file);
+ void slotRefreshItem(const QString &file);
+
+ void slotNewItems(const KFileItemList &items);
+ void slotRefreshItems(const KFileItemList &items);
+ void slotDeleteItem(KFileItem *item);
+
+protected:
+ CollectionList(PlaylistCollection *collection);
+ virtual ~CollectionList();
+
+ virtual void contentsDropEvent(QDropEvent *e);
+ virtual void contentsDragMoveEvent(QDragMoveEvent *e);
+
+ // These methods are used by CollectionListItem, which is a friend class.
+
+ void addToDict(const QString &file, CollectionListItem *item) { m_itemsDict.replace(file, item); }
+ void removeFromDict(const QString &file) { m_itemsDict.remove(file); }
+
+ // These methods are also used by CollectionListItem, to manage the
+ // strings used in generating the unique sets and tree view mode playlists.
+
+ QString addStringToDict(const QString &value, unsigned column);
+ void removeStringFromDict(const QString &value, unsigned column);
+
+ void addWatched(const QString &file) { m_dirWatch->addFile(file); }
+ void removeWatched(const QString &file) { m_dirWatch->removeFile(file); }
+
+ virtual bool hasItem(const QString &file) const { return m_itemsDict.find(file); }
+
+signals:
+ void signalCollectionChanged();
+
+ /**
+ * This is emitted when the set of columns that is visible is changed.
+ *
+ * \see Playlist::hideColumn()
+ * \see Playlist::showColumn()
+ * \see Playlsit::isColumnVisible()
+ */
+ void signalVisibleColumnsChanged();
+ void signalNewTag(const QString &, unsigned);
+ void signalRemovedTag(const QString &, unsigned);
+
+private:
+ /**
+ * Just the size of the above enum to keep from hard coding it in several
+ * locations.
+ */
+ static const int m_uniqueSetCount = 3;
+
+ static CollectionList *m_list;
+ QDict<CollectionListItem> m_itemsDict;
+ KDirWatch *m_dirWatch;
+ TagCountDicts m_columnTags;
+};
+
+class CollectionListItem : public PlaylistItem
+{
+ friend class Playlist;
+ friend class CollectionList;
+ friend class PlaylistItem;
+
+ /**
+ * Needs access to the destructor, even though the destructor isn't used by QDict.
+ */
+ friend class QDict<CollectionListItem>;
+
+public:
+ virtual void refresh();
+ PlaylistItem *itemForPlaylist(const Playlist *playlist);
+ void updateCollectionDict(const QString &oldPath, const QString &newPath);
+ void repaint() const;
+ PlaylistItemList children() const { return m_children; }
+
+protected:
+ CollectionListItem(const FileHandle &file);
+ virtual ~CollectionListItem();
+
+ void addChildItem(PlaylistItem *child);
+ void removeChildItem(PlaylistItem *child);
+
+ /**
+ * Returns true if the item is now up to date (even if this required a refresh) or
+ * false if the item is invalid.
+ */
+ bool checkCurrent();
+
+ virtual CollectionListItem *collectionItem() { return this; }
+
+private:
+ bool m_shuttingDown;
+ PlaylistItemList m_children;
+};
+
+#endif
diff --git a/juk/configure.in.bot b/juk/configure.in.bot
new file mode 100644
index 00000000..f611be1c
--- /dev/null
+++ b/juk/configure.in.bot
@@ -0,0 +1,40 @@
+if test "x$with_taglib" = xcheck && test "x$have_taglib" != xyes; then
+ echo "**************************************************"
+ echo "*"
+ echo "* JuK will not be built without TagLib."
+ echo "* See the notice below for where to find TagLib."
+ echo "*"
+ echo "**************************************************"
+fi
+
+if test "x$with_gstreamer" = xcheck && test "x$have_gst" = xno; then
+ echo "**************************************************"
+ echo "*"
+ echo "* You do not seem to have GStreamer 0.8.x installed."
+ echo "* Without this aRts and/or aKode output will be used exclusively."
+ echo "*"
+ echo "* If you actually have gstreamer installed make sure you also have"
+ echo "* the gst-plugins collection installed. Both gstreamer and"
+ echo "* gst-plugins need to be 0.8.x (0.9 is not supported)"
+ echo "* http://gstreamer.freedesktop.org/modules/"
+ echo "*"
+ echo "* JuK supports GStreamer output but will also"
+ echo "* work with aRts and aKode."
+ echo "*"
+ echo "**************************************************"
+fi
+
+if test "x$with_musicbrainz" = xcheck && test "x$have_musicbrainz" = xno; then
+ echo "**************************************************"
+ echo "*"
+ echo "* You do not seem to have libmusicbrainz and"
+ echo "* libtunepimp. JuK will be compiled without"
+ echo "* MusicBrainz support and automatic song"
+ echo "* recognition will not be supported."
+ echo "* Please download libmusicbrainz 2.x and libtunepimp"
+ echo "* 0.3.x from:"
+ echo "* http://www.musicbrainz.org/products/client/download.html "
+ echo "* http://www.musicbrainz.org/products/tunepimp/download.html"
+ echo "*"
+ echo "**************************************************"
+fi
diff --git a/juk/configure.in.in b/juk/configure.in.in
new file mode 100644
index 00000000..9d357c67
--- /dev/null
+++ b/juk/configure.in.in
@@ -0,0 +1,102 @@
+#MIN_CONFIG(3)
+
+AM_INIT_AUTOMAKE(juk,1.0)
+
+AC_ARG_WITH(musicbrainz,
+ [AC_HELP_STRING(--with-musicbrainz,
+ [enable support for MusicBrainz @<:@default=check@:>@])],
+ [], with_musicbrainz=check)
+
+have_musicbrainz=no
+
+if test "x$with_musicbrainz" != xno; then
+ KDE_CHECK_HEADER(tunepimp/tp_c.h, have_musicbrainz=yes)
+
+ if test "x$with_musicbrainz" != xcheck && test "x$have_musicbrainz" != xyes; then
+ AC_MSG_ERROR([--with-musicbrainz was given, but test for MusicBrainz failed])
+ fi
+fi
+
+if test "x$have_musicbrainz" = xyes; then
+ v=1
+ KDE_CHECK_LIB(tunepimp, tp_SetFileNameEncoding,
+ [v=4])
+ case "$v" in
+ 4) KDE_CHECK_LIB(tunepimp, tp_SetTRMCollisionThreshold,
+ AC_DEFINE(HAVE_MUSICBRAINZ, 4, [have MusicBrainz 0.4.x]),
+ [AC_MSG_WARN([Tunepimp 0.5 detected - disabled.])
+ AC_DEFINE(HAVE_MUSICBRAINZ, 0, [have MusicBrainz 0.5.x - disabled])
+ ])
+ ;;
+ *) AC_DEFINE(HAVE_MUSICBRAINZ, 1, [have MusicBrainz]) ;;
+ esac
+else
+ AC_DEFINE(HAVE_MUSICBRAINZ, 0, [have MusicBrainz])
+fi
+
+AM_CONDITIONAL(link_lib_MB, test "x$have_musicbrainz" = xyes)
+
+AC_ARG_WITH(gstreamer,
+ [AC_HELP_STRING(--with-gstreamer,
+ [enable support for GStreamer @<:@default=check@:>@])],
+ [], with_gstreamer=check)
+
+have_gst=no
+
+if test "x$with_gstreamer" != xno; then
+ # pkg-config seems to have a bug where it masks needed -L entries when it
+ # shouldn't, so disable that.
+
+ PKG_CONFIG_ALLOW_SYSTEM_LIBS=1
+ export PKG_CONFIG_ALLOW_SYSTEM_LIBS
+
+ GST_MAJORMINOR=0.10
+ GST_REQ=0.10.0
+ GST_VERSION=10
+
+ PKG_CHECK_MODULES(GST, \
+ gstreamer-$GST_MAJORMINOR >= $GST_REQ, \
+ have_gst=yes, have_gst=no)
+
+ if test "x$have_gst" != xyes; then
+
+ GST_MAJORMINOR=0.8
+ GST_REQ=0.8.0
+ GST_VERSION=8
+
+ PKG_CHECK_MODULES(GST, \
+ gstreamer-$GST_MAJORMINOR >= $GST_REQ, \
+ have_gst=yes, have_gst=no)
+ fi
+
+ if test "x$with_gstreamer" != xcheck && test "x$have_gst" != xyes; then
+ AC_MSG_ERROR([--with-gstreamer was given, but test for GStreamer failed])
+ fi
+fi
+
+if test "x$have_gst" = "xno"; then
+ GST_CFLAGS=""
+ LDADD_GST=""
+ LDFLAGS_GST=""
+ AC_DEFINE(HAVE_GSTREAMER, 0, [have GStreamer])
+else
+ LDADD_GST=`$PKG_CONFIG --libs-only-l gstreamer-$GST_MAJORMINOR`
+ LDFLAGS_GST=`$PKG_CONFIG --libs-only-other gstreamer-$GST_MAJORMINOR`
+
+ # Append -L entries, since they are masked by --libs-only-l and
+ # --libs-only-other
+ LIBDIRS_GST=`$PKG_CONFIG --libs-only-L gstreamer-$GST_MAJORMINOR`
+ LDADD_GST="$LDADD_GST $LIBDIRS_GST"
+
+ AC_MSG_NOTICE([GStreamer version >= $GST_REQ found.])
+ AC_DEFINE(HAVE_GSTREAMER, 1, [have GStreamer])
+ AC_DEFINE_UNQUOTED(GSTREAMER_VERSION, $GST_VERSION, [GStreamer Version])
+fi
+
+AC_SUBST(GST_CFLAGS)
+AC_SUBST(LDADD_GST)
+AC_SUBST(LDFLAGS_GST)
+
+if test "x$have_taglib" != xyes || ( test "x$build_arts" = "xno" && test "x$have_gst" = "xno" && test "x$have_akode" = "xno") ; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE juk"
+fi
diff --git a/juk/coverdialog.cpp b/juk/coverdialog.cpp
new file mode 100644
index 00000000..cdb9853a
--- /dev/null
+++ b/juk/coverdialog.cpp
@@ -0,0 +1,168 @@
+/***************************************************************************
+ begin : Sun May 15 2005
+ copyright : (C) 2005 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <klistview.h>
+#include <kiconview.h>
+#include <kiconviewsearchline.h>
+#include <kiconloader.h>
+#include <kapplication.h>
+#include <kpopupmenu.h>
+#include <klocale.h>
+
+#include <qtimer.h>
+#include <qtoolbutton.h>
+
+#include "coverdialog.h"
+#include "covericonview.h"
+#include "covermanager.h"
+#include "collectionlist.h"
+
+using CoverUtility::CoverIconViewItem;
+
+class AllArtistsListViewItem : public KListViewItem
+{
+public:
+ AllArtistsListViewItem(QListView *parent) :
+ KListViewItem(parent, i18n("<All Artists>"))
+ {
+ }
+
+ int compare(QListViewItem *, int, bool) const
+ {
+ return -1; // Always be at the top.
+ }
+};
+
+class CaseInsensitiveItem : public KListViewItem
+{
+public:
+ CaseInsensitiveItem(QListView *parent, const QString &text) :
+ KListViewItem(parent, text)
+ {
+ }
+
+ int compare(QListViewItem *item, int column, bool ascending) const
+ {
+ Q_UNUSED(ascending);
+ return text(column).lower().localeAwareCompare(item->text(column).lower());
+ }
+};
+
+CoverDialog::CoverDialog(QWidget *parent) :
+ CoverDialogBase(parent, "juk_cover_dialog", WType_Dialog)
+{
+ m_covers->setResizeMode(QIconView::Adjust);
+ m_covers->setGridX(140);
+ m_covers->setGridY(150);
+
+ m_searchLine->setIconView(m_covers);
+ m_clearSearch->setIconSet(SmallIconSet("locationbar_erase"));
+}
+
+CoverDialog::~CoverDialog()
+{
+}
+
+void CoverDialog::show()
+{
+ m_artists->clear();
+ m_covers->clear();
+
+ QStringList artists = CollectionList::instance()->uniqueSet(CollectionList::Artists);
+
+ m_artists->setSorting(-1);
+ new AllArtistsListViewItem(m_artists);
+ for(QStringList::ConstIterator it = artists.begin(); it != artists.end(); ++it)
+ new CaseInsensitiveItem(m_artists, *it);
+
+ m_artists->setSorting(0);
+
+ QTimer::singleShot(0, this, SLOT(loadCovers()));
+ CoverDialogBase::show();
+}
+
+// Here we try to keep the GUI from freezing for too long while we load the
+// covers.
+void CoverDialog::loadCovers()
+{
+ QValueList<coverKey> keys = CoverManager::keys();
+ QValueList<coverKey>::ConstIterator it;
+ int i = 0;
+
+ for(it = keys.begin(); it != keys.end(); ++it) {
+ new CoverIconViewItem(*it, m_covers);
+
+ if(++i == 10) {
+ i = 0;
+ kapp->processEvents();
+ }
+ }
+}
+
+// TODO: Add a way to show cover art for tracks with no artist.
+void CoverDialog::slotArtistClicked(QListViewItem *item)
+{
+ m_covers->clear();
+
+ if(dynamic_cast<AllArtistsListViewItem *>(item)) {
+ // All artists.
+ loadCovers();
+ }
+ else {
+ QString artist = item->text(0).lower();
+ QValueList<coverKey> keys = CoverManager::keys();
+ QValueList<coverKey>::ConstIterator it;
+
+ for(it = keys.begin(); it != keys.end(); ++it) {
+ CoverDataPtr data = CoverManager::coverInfo(*it);
+ if(data->artist == artist)
+ new CoverIconViewItem(*it, m_covers);
+ }
+ }
+}
+
+void CoverDialog::slotContextRequested(QIconViewItem *item, const QPoint &pt)
+{
+ static KPopupMenu *menu = 0;
+
+ if(!item)
+ return;
+
+ if(!menu) {
+ menu = new KPopupMenu(this);
+ menu->insertItem(i18n("Remove Cover"), this, SLOT(removeSelectedCover()));
+ }
+
+ menu->popup(pt);
+}
+
+void CoverDialog::removeSelectedCover()
+{
+ CoverIconViewItem *coverItem = m_covers->currentItem();
+
+ if(!coverItem || !coverItem->isSelected()) {
+ kdWarning(65432) << "No item selected for removeSelectedCover.\n";
+ return;
+ }
+
+ if(!CoverManager::removeCover(coverItem->id()))
+ kdError(65432) << "Unable to remove selected cover: " << coverItem->id() << endl;
+ else
+ delete coverItem;
+}
+
+#include "coverdialog.moc"
+
+// vim: set et ts=4 sw=4:
diff --git a/juk/coverdialog.h b/juk/coverdialog.h
new file mode 100644
index 00000000..3ced0d75
--- /dev/null
+++ b/juk/coverdialog.h
@@ -0,0 +1,41 @@
+/***************************************************************************
+ begin : Sun May 15 2005
+ copyright : (C) 2005 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef JUK_COVERDIALOG_H
+#define JUK_COVERDIALOG_H
+
+#include "coverdialogbase.h"
+
+class CoverDialog : public CoverDialogBase
+{
+ Q_OBJECT
+public:
+ CoverDialog(QWidget *parent);
+ ~CoverDialog();
+
+ virtual void show();
+
+public slots:
+ void slotArtistClicked(QListViewItem *item);
+ void slotContextRequested(QIconViewItem *item, const QPoint &pt);
+
+private slots:
+ void loadCovers();
+ void removeSelectedCover();
+};
+
+#endif /* JUK_COVERDIALOG_H */
+
+// vim: set et ts=4 sw=4:
diff --git a/juk/coverdialogbase.ui b/juk/coverdialogbase.ui
new file mode 100644
index 00000000..180dc8b0
--- /dev/null
+++ b/juk/coverdialogbase.ui
@@ -0,0 +1,210 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>CoverDialogBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>CoverDialogBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>685</width>
+ <height>554</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Cover Manager</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Artist</string>
+ </property>
+ <property name="clickable">
+ <bool>false</bool>
+ </property>
+ <property name="resizable">
+ <bool>false</bool>
+ </property>
+ </column>
+ <item>
+ <property name="text">
+ <string>&lt;All&gt;</string>
+ </property>
+ <property name="pixmap">
+ <pixmap></pixmap>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_artists</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>164</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="resizeMode">
+ <enum>LastColumn</enum>
+ </property>
+ <property name="shadeSortColumn">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QToolButton">
+ <property name="name">
+ <cstring>m_clearSearch</cstring>
+ </property>
+ <property name="text">
+ <string>Clear Search</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Clear Search</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Clear the current cover search.</string>
+ </property>
+ </widget>
+ <widget class="KIconViewSearchLine">
+ <property name="name">
+ <cstring>m_searchLine</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="CoverIconView">
+ <property name="name">
+ <cstring>m_covers</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="gridX" stdset="0">
+ <number>140</number>
+ </property>
+ <property name="gridY" stdset="0">
+ <number>150</number>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+</widget>
+<customwidgets>
+ <customwidget>
+ <class>KIconViewSearchLine</class>
+ <header location="global">kiconviewsearchline.h</header>
+ <sizehint>
+ <width>100</width>
+ <height>-1</height>
+ </sizehint>
+ <container>0</container>
+ <sizepolicy>
+ <hordata>5</hordata>
+ <verdata>0</verdata>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ <pixmap>image0</pixmap>
+ <slot access="public" specifier="">clear()</slot>
+ </customwidget>
+ <customwidget>
+ <class>CoverIconView</class>
+ <header location="local">covericonview.h</header>
+ <sizehint>
+ <width>100</width>
+ <height>100</height>
+ </sizehint>
+ <container>1</container>
+ <sizepolicy>
+ <hordata>7</hordata>
+ <verdata>7</verdata>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ <pixmap>image0</pixmap>
+ <signal>contextMenuRequested(QIconViewItem *item, const QPoint &amp;pos)</signal>
+ <property type="Int">gridX</property>
+ <property type="Int">gridY</property>
+ </customwidget>
+</customwidgets>
+<images>
+ <image name="image0">
+ <data format="PNG" length="1002">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000003b149444154388dad945f4c5b551cc73fe7dc4b7b4bcba0762d45c43114323599ee6192609c51d883892ce083f1718b3ebb185f8dc91e972cf39d2d2a2f1af664b6f1e0fe3863a0718969700eb0c52142da0242a1bd6d696f7bcff101585203ceb8fd9ece39f99dcff9fe7edf939f88c562ec465f5f9fe609442c161362173c3e3eae7b7a7ac8e7f36432196cdbfe4f907c3e4f2291201e8fe338cec3737357e9e8e828aded1e229d650e1f2d51754b082110124c13a4dc5ea341eb9dc284c0558a853f3ce8cb0677ef500fde7d39d2596679e326597b8e9abb85d7a770ab16ab6983ec5a05b487a70e36f0f4e10afe408d6a558310980108478dba4a1e8233990c5d474b64ed39aa3a8fe5f3317fbf81dbd70bccfeb205947632fd74f6589c1c6ea2f70d03a58ba0c1f2c9bdc1b66de3b8256a6e11cbe7e3ee1d181b590124fe2693aeee08d223c82c3a2c24b7b874bec8f26288774f7bd054504aef0dde6e99c0eb83f9fb266323cb80a27fb0958141836044605a2ee5523393371cc646fee2da37195aa35d0c0c5b4859ac03d7e91712dcaac5adab3650a3ff9d08ef7dd8404bb48869e5d958b5b87dadc4c9a1464e9f0d0326df7ebd86bd2e310cb1bf62d384d59441f2d70a070e1c60e09489929b988681bdd9cc97170bcc4c65595f71f8e0e3301337fc24a7732467831875a47f289652b0be5e4151e6d07316c1b0c0340d8ab92023e76d66a6b2840e36d2fb7a13fee632475e6edc367ea98a90fb98b7dd6310ca0328a44761582e1bab41befabcc0ec940d28bc5e93b68e064cab84e1d9beaeb48934eac1f53b01c1b000fca496aa54b61a99fcde61662a4b4b4b23d1680be9d426173e4df3602a48ea411989a4fd590f52a8fd156b05ed9d350e3defe3cfdf4b4c7ce770ea7d3fb9f520afbe1620daeee5c26735d20b9b9cfb6811a754a439e4e5c5639a4caa1e5caf586bfc0197b78702005cb9b4cae4cd3267ce8638fe964bd72b393e39d74928d242617303a756a37f284447770dcdbffc6384a05a85de1306e9a52057c7527c7131c3c42d3f475eb2303c82d4fc3276d6811db37efeb148723082d9b08f79f97c1e5729109a9a28307cc622d2d6cdf52b2b24efe548dedb00142009862cfa879ee1a71f6cec928353511472fbf4389148b0b0e0c108081412458dfe21c9f11351e67e7358595468246d1d1e5e38a6e9e851bc39d84ab502a669331dafec0d8ec7e3e8cb06e1a881d727d1ae40180a434a8c9db129a54126ad48a7358c2b4c5352c8c374bcccdab2bb37d8719cba79fab8211f9df218e0582c261e95f8bfc04f1a1e8bc5c4dfe0a190172af6a9690000000049454e44ae426082</data>
+ </image>
+</images>
+<connections>
+ <connection>
+ <sender>m_artists</sender>
+ <signal>clicked(QListViewItem*)</signal>
+ <receiver>CoverDialogBase</receiver>
+ <slot>slotArtistClicked(QListViewItem*)</slot>
+ </connection>
+ <connection>
+ <sender>m_clearSearch</sender>
+ <signal>clicked()</signal>
+ <receiver>m_searchLine</receiver>
+ <slot>clear()</slot>
+ </connection>
+ <connection>
+ <sender>m_covers</sender>
+ <signal>contextMenuRequested(QIconViewItem*,const QPoint&amp;)</signal>
+ <receiver>CoverDialogBase</receiver>
+ <slot>slotContextRequested(QIconViewItem*,const QPoint&amp;)</slot>
+ </connection>
+</connections>
+<slots>
+ <slot>slotArtistClicked(QListViewItem *item)</slot>
+ <slot>slotContextRequested(QIconViewItem *, const QPoint &amp;pt)</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<forwards>
+ <forward>class QIconViewItem;</forward>
+</forwards>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>kiconviewsearchline.h</includehint>
+ <includehint>covericonview.h</includehint>
+</includehints>
+</UI>
diff --git a/juk/covericonview.cpp b/juk/covericonview.cpp
new file mode 100644
index 00000000..88defcab
--- /dev/null
+++ b/juk/covericonview.cpp
@@ -0,0 +1,48 @@
+/***************************************************************************
+ begin : Sat Jul 9 2005
+ copyright : (C) 2005 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "covericonview.h"
+#include "covermanager.h"
+
+using CoverUtility::CoverIconViewItem;
+
+CoverIconViewItem::CoverIconViewItem(coverKey id, QIconView *parent) :
+ KIconViewItem(parent), m_id(id)
+{
+ CoverDataPtr data = CoverManager::coverInfo(id);
+ setText(QString("%1 - %2").arg(data->artist, data->album));
+ setPixmap(data->thumbnail());
+}
+
+CoverIconView::CoverIconView(QWidget *parent, const char *name) : KIconView(parent, name)
+{
+ setResizeMode(Adjust);
+}
+
+CoverIconViewItem *CoverIconView::currentItem() const
+{
+ return static_cast<CoverIconViewItem *>(KIconView::currentItem());
+}
+
+QDragObject *CoverIconView::dragObject()
+{
+ CoverIconViewItem *item = currentItem();
+ if(item)
+ return new CoverDrag(item->id(), this);
+
+ return 0;
+}
+
+// vim: set et ts=4 sw=4:
diff --git a/juk/covericonview.h b/juk/covericonview.h
new file mode 100644
index 00000000..4126cb3f
--- /dev/null
+++ b/juk/covericonview.h
@@ -0,0 +1,62 @@
+/***************************************************************************
+ begin : Sat Jul 9 2005
+ copyright : (C) 2005 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef JUK_COVERICONVIEW_H
+#define JUK_COVERICONVIEW_H
+
+#include <kiconview.h>
+
+#include "covermanager.h"
+
+// The WebImageFetcher dialog also has a class named CoverIconViewItem and I
+// don't like the idea of naming it "CoverIVI" or something, so just namespace
+// it out. I would merge them except for webimagefetcher's dependence on KIO
+// and such.
+namespace CoverUtility
+{
+ class CoverIconViewItem : public KIconViewItem
+ {
+ public:
+ CoverIconViewItem(coverKey id, QIconView *parent);
+
+ coverKey id() const { return m_id; }
+
+ private:
+ coverKey m_id;
+ };
+}
+
+using CoverUtility::CoverIconViewItem;
+
+/**
+ * This class subclasses KIconView in order to provide cover drag-and-drop
+ * support.
+ *
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ */
+class CoverIconView : public KIconView
+{
+public:
+ CoverIconView(QWidget *parent, const char *name);
+
+ CoverIconViewItem *currentItem() const;
+
+protected:
+ virtual QDragObject *dragObject();
+};
+
+#endif /* JUK_COVERICONVIEW_H */
+
+// vim: set et ts=4 sw=4:
diff --git a/juk/coverinfo.cpp b/juk/coverinfo.cpp
new file mode 100644
index 00000000..243bc429
--- /dev/null
+++ b/juk/coverinfo.cpp
@@ -0,0 +1,283 @@
+/***************************************************************************
+ copyright : (C) 2004 Nathan Toone
+ : (C) 2005 Michael Pyne <michael.pyne@kdemail.net>
+ email : nathan@toonetown.com
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kglobal.h>
+#include <kapplication.h>
+#include <kstandarddirs.h>
+#include <kdebug.h>
+
+#include <qregexp.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qcursor.h>
+
+#include "collectionlist.h"
+#include "playlistsearch.h"
+#include "playlistitem.h"
+#include "coverinfo.h"
+#include "tag.h"
+
+struct CoverPopup : public QWidget
+{
+ CoverPopup(const QPixmap &image, const QPoint &p) :
+ QWidget(0, 0, WDestructiveClose | WX11BypassWM)
+ {
+ QHBoxLayout *layout = new QHBoxLayout(this);
+ QLabel *label = new QLabel(this);
+
+ layout->addWidget(label);
+ label->setFrameStyle(QFrame::Box | QFrame::Raised);
+ label->setLineWidth(1);
+ label->setPixmap(image);
+
+ setGeometry(p.x(), p.y(), label->width(), label->height());
+ show();
+ }
+ virtual void leaveEvent(QEvent *) { close(); }
+ virtual void mouseReleaseEvent(QMouseEvent *) { close(); }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+
+CoverInfo::CoverInfo(const FileHandle &file) :
+ m_file(file),
+ m_hasCover(false),
+ m_haveCheckedForCover(false),
+ m_coverKey(CoverManager::NoMatch),
+ m_needsConverting(false)
+{
+
+}
+
+bool CoverInfo::hasCover()
+{
+ if(m_haveCheckedForCover)
+ return m_hasCover;
+
+ m_haveCheckedForCover = true;
+
+ // Check for new-style covers. First let's determine what our coverKey is
+ // if it's not already set, as that's also tracked by the CoverManager.
+ if(m_coverKey == CoverManager::NoMatch)
+ m_coverKey = CoverManager::idForTrack(m_file.absFilePath());
+
+ // We were assigned a key, let's see if we already have a cover. Notice
+ // that due to the way the CoverManager is structured, we should have a
+ // cover if we have a cover key. If we don't then either there's a logic
+ // error, or the user has been mucking around where they shouldn't.
+ if(m_coverKey != CoverManager::NoMatch)
+ m_hasCover = CoverManager::hasCover(m_coverKey);
+
+ // We *still* don't have it? Check the old-style covers then.
+ if(!m_hasCover) {
+ m_hasCover = QFile(coverLocation(FullSize)).exists();
+
+ if(m_hasCover)
+ m_needsConverting = true;
+ }
+
+ return m_hasCover;
+}
+
+void CoverInfo::clearCover()
+{
+ m_hasCover = false;
+
+ // Yes, we have checked, and we don't have it. ;)
+ m_haveCheckedForCover = true;
+
+ m_needsConverting = false;
+
+ // We don't need to call removeCover because the CoverManager will
+ // automatically unlink the cover if we were the last track to use it.
+ CoverManager::setIdForTrack(m_file.absFilePath(), CoverManager::NoMatch);
+ m_coverKey = CoverManager::NoMatch;
+}
+
+void CoverInfo::setCover(const QImage &image)
+{
+ if(image.isNull())
+ return;
+
+ m_haveCheckedForCover = true;
+ m_needsConverting = false;
+ m_hasCover = true;
+
+ QPixmap cover;
+ cover.convertFromImage(image);
+
+ // If we use replaceCover we'll change the cover for every other track
+ // with the same coverKey, which we don't want since that case will be
+ // handled by Playlist. Instead just replace this track's cover.
+ m_coverKey = CoverManager::addCover(cover, m_file.tag()->artist(), m_file.tag()->album());
+ if(m_coverKey != CoverManager::NoMatch)
+ CoverManager::setIdForTrack(m_file.absFilePath(), m_coverKey);
+}
+
+void CoverInfo::setCoverId(coverKey id)
+{
+ m_coverKey = id;
+ m_haveCheckedForCover = true;
+ m_needsConverting = false;
+ m_hasCover = id != CoverManager::NoMatch;
+
+ // Inform CoverManager of the change.
+ CoverManager::setIdForTrack(m_file.absFilePath(), m_coverKey);
+}
+
+void CoverInfo::applyCoverToWholeAlbum(bool overwriteExistingCovers) const
+{
+ QString artist = m_file.tag()->artist();
+ QString album = m_file.tag()->album();
+ PlaylistSearch::ComponentList components;
+ ColumnList columns;
+
+ columns.append(PlaylistItem::ArtistColumn);
+ components.append(PlaylistSearch::Component(artist, false, columns, PlaylistSearch::Component::Exact));
+
+ columns.clear();
+ columns.append(PlaylistItem::AlbumColumn);
+ components.append(PlaylistSearch::Component(album, false, columns, PlaylistSearch::Component::Exact));
+
+ PlaylistList playlists;
+ playlists.append(CollectionList::instance());
+
+ PlaylistSearch search(playlists, components, PlaylistSearch::MatchAll);
+
+ // Search done, iterate through results.
+
+ PlaylistItemList results = search.matchedItems();
+ PlaylistItemList::ConstIterator it = results.constBegin();
+ for(; it != results.constEnd(); ++it) {
+
+ // Don't worry about files that somehow already have a tag,
+ // unless the coversion is forced.
+ if(!overwriteExistingCovers && !(*it)->file().coverInfo()->m_needsConverting)
+ continue;
+
+ kdDebug(65432) << "Setting cover for: " << *it << endl;
+ (*it)->file().coverInfo()->setCoverId(m_coverKey);
+ }
+}
+
+QPixmap CoverInfo::pixmap(CoverSize size) const
+{
+ if(m_needsConverting)
+ convertOldStyleCover();
+
+ if(m_coverKey == CoverManager::NoMatch)
+ return QPixmap();
+
+ if(size == Thumbnail)
+ return CoverManager::coverFromId(m_coverKey, CoverManager::Thumbnail);
+ else
+ return CoverManager::coverFromId(m_coverKey, CoverManager::FullSize);
+}
+
+void CoverInfo::popup() const
+{
+ QPixmap image = pixmap(FullSize);
+ QPoint mouse = QCursor::pos();
+ QRect desktop = KApplication::desktop()->screenGeometry(mouse);
+
+ int x = mouse.x();
+ int y = mouse.y();
+ int height = image.size().height() + 4;
+ int width = image.size().width() + 4;
+
+ // Detect the right direction to pop up (always towards the center of the
+ // screen), try to pop up with the mouse pointer 10 pixels into the image in
+ // both directions. If we're too close to the screen border for this margin,
+ // show it at the screen edge, accounting for the four pixels (two on each
+ // side) for the window border.
+
+ if(x - desktop.x() < desktop.width() / 2)
+ x = (x - desktop.x() < 10) ? desktop.x() : (x - 10);
+ else
+ x = (x - desktop.x() > desktop.width() - 10) ? desktop.width() - width +desktop.x() : (x - width + 10);
+
+ if(y - desktop.y() < desktop.height() / 2)
+ y = (y - desktop.y() < 10) ? desktop.y() : (y - 10);
+ else
+ y = (y - desktop.y() > desktop.height() - 10) ? desktop.height() - height + desktop.y() : (y - height + 10);
+
+ new CoverPopup(image, QPoint(x, y));
+}
+
+/**
+ * DEPRECATED
+ */
+QString CoverInfo::coverLocation(CoverSize size) const
+{
+ QString fileName(QFile::encodeName(m_file.tag()->artist() + " - " + m_file.tag()->album()));
+ QRegExp maskedFileNameChars("[ /?:\"]");
+
+ fileName.replace(maskedFileNameChars, "_");
+ fileName.append(".png");
+
+ QString dataDir = KGlobal::dirs()->saveLocation("appdata");
+ QString subDir;
+
+ switch (size) {
+ case FullSize:
+ subDir = "large/";
+ break;
+ default:
+ break;
+ }
+ QString fileLocation = dataDir + "covers/" + subDir + fileName.lower();
+
+ return fileLocation;
+}
+
+bool CoverInfo::convertOldStyleCover() const
+{
+ // Ah, old-style cover. Let's transfer it to the new system.
+ kdDebug() << "Found old style cover for " << m_file.absFilePath() << endl;
+
+ QString artist = m_file.tag()->artist();
+ QString album = m_file.tag()->album();
+ QString oldLocation = coverLocation(FullSize);
+ m_coverKey = CoverManager::addCover(oldLocation, artist, album);
+
+ m_needsConverting = false;
+
+ if(m_coverKey != CoverManager::NoMatch) {
+ CoverManager::setIdForTrack(m_file.absFilePath(), m_coverKey);
+
+ // Now let's also set the ID for the tracks matching the track and
+ // artist at this point so that the conversion is complete, otherwise
+ // we can't tell apart the "No cover on purpose" and "Has no cover yet"
+ // possibilities.
+
+ applyCoverToWholeAlbum();
+
+ // If we convert we need to remove the old cover otherwise we'll find
+ // it later if the user un-sets the new cover.
+ if(!QFile::remove(oldLocation))
+ kdError(65432) << "Unable to remove converted cover at " << oldLocation << endl;
+
+ return true;
+ }
+ else {
+ kdDebug() << "We were unable to replace the old style cover.\n";
+ return false;
+ }
+}
+
+// vim: set et sw=4 ts=8:
diff --git a/juk/coverinfo.h b/juk/coverinfo.h
new file mode 100644
index 00000000..1caae8d0
--- /dev/null
+++ b/juk/coverinfo.h
@@ -0,0 +1,67 @@
+/***************************************************************************
+ copyright : (C) 2004 Nathan Toone
+ email : nathan@toonetown.com
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef COVERINFO_H
+#define COVERINFO_H
+
+#include <qimage.h>
+
+#include "filehandle.h"
+#include "covermanager.h"
+
+class CoverInfo
+{
+ friend class FileHandle;
+
+public:
+ enum CoverSize { FullSize, Thumbnail };
+
+ CoverInfo(const FileHandle &file);
+
+ bool hasCover();
+
+ void clearCover();
+ void setCover(const QImage &image = QImage());
+
+ // Use this to assign to a specific cover id.
+ void setCoverId(coverKey id);
+
+ /**
+ * This function sets the cover identifier for all tracks that have the
+ * same Artist and Album as this track, to the cover identifier of this
+ * track.
+ *
+ * @param overwriteExistingCovers If set to true, this function will always
+ * apply the new cover to a track even if the track already had
+ * a different cover set.
+ */
+ void applyCoverToWholeAlbum(bool overwriteExistingCovers = false) const;
+
+ coverKey coverId() const { return m_coverKey; }
+
+ QPixmap pixmap(CoverSize size) const;
+ void popup() const;
+
+private:
+ QString coverLocation(CoverSize size) const;
+ bool convertOldStyleCover() const;
+
+ FileHandle m_file;
+ bool m_hasCover;
+ bool m_haveCheckedForCover;
+ mutable coverKey m_coverKey;
+ mutable bool m_needsConverting;
+};
+#endif
+
diff --git a/juk/covermanager.cpp b/juk/covermanager.cpp
new file mode 100644
index 00000000..1fe36cc4
--- /dev/null
+++ b/juk/covermanager.cpp
@@ -0,0 +1,577 @@
+/***************************************************************************
+ begin : Sun May 15 2005
+ copyright : (C) 2005 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <qpixmap.h>
+#include <qmap.h>
+#include <qstring.h>
+#include <qfile.h>
+#include <qimage.h>
+#include <qdir.h>
+#include <qdatastream.h>
+#include <qdict.h>
+#include <qcache.h>
+#include <qmime.h>
+#include <qbuffer.h>
+
+#include <kdebug.h>
+#include <kstaticdeleter.h>
+#include <kstandarddirs.h>
+#include <kglobal.h>
+
+#include "covermanager.h"
+
+// This is a dictionary to map the track path to their ID. Otherwise we'd have
+// to store this info with each CollectionListItem, which would break the cache
+// of users who upgrade, and would just generally be a big mess.
+typedef QDict<coverKey> TrackLookupMap;
+
+// This is responsible for making sure that the CoverManagerPrivate class
+// gets properly destructed on shutdown.
+static KStaticDeleter<CoverManagerPrivate> sd;
+
+const char *CoverDrag::mimetype = "application/x-juk-coverid";
+// Caches the QPixmaps for the covers so that the covers are not all kept in
+// memory for no reason.
+typedef QCache<QPixmap> CoverPixmapCache;
+
+CoverManagerPrivate *CoverManager::m_data = 0;
+
+// Used to save and load CoverData from a QDataStream
+QDataStream &operator<<(QDataStream &out, const CoverData &data);
+QDataStream &operator>>(QDataStream &in, CoverData &data);
+
+//
+// Implementation of CoverData struct
+//
+
+QPixmap CoverData::pixmap() const
+{
+ return CoverManager::coverFromData(*this, CoverManager::FullSize);
+}
+
+QPixmap CoverData::thumbnail() const
+{
+ return CoverManager::coverFromData(*this, CoverManager::Thumbnail);
+}
+
+/**
+ * This class is responsible for actually keeping track of the storage for the
+ * different covers and such. It holds the covers, and the map of path names
+ * to cover ids, and has a few utility methods to load and save the data.
+ *
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ * @see CoverManager
+ */
+class CoverManagerPrivate
+{
+public:
+
+ /// Maps coverKey id's to CoverDataPtrs
+ CoverDataMap covers;
+
+ /// Maps file names to coverKey id's.
+ TrackLookupMap tracks;
+
+ /// A cache of the cover representations. The key format is:
+ /// 'f' followed by the pathname for FullSize covers, and
+ /// 't' followed by the pathname for Thumbnail covers.
+ CoverPixmapCache pixmapCache;
+
+ CoverManagerPrivate() : tracks(1301), pixmapCache(2 * 1024 * 768)
+ {
+ loadCovers();
+ pixmapCache.setAutoDelete(true);
+ }
+
+ ~CoverManagerPrivate()
+ {
+ saveCovers();
+ }
+
+ /**
+ * Creates the data directory for the covers if it doesn't already exist.
+ * Must be in this class for loadCovers() and saveCovers().
+ */
+ void createDataDir() const;
+
+ /**
+ * Returns the next available unused coverKey that can be used for
+ * inserting new items.
+ *
+ * @return unused id that can be used for new CoverData
+ */
+ coverKey nextId() const;
+
+ void saveCovers() const;
+
+ private:
+ void loadCovers();
+
+ /**
+ * @return the full path and filename of the file storing the cover
+ * lookup map and the translations between pathnames and ids.
+ */
+ QString coverLocation() const;
+};
+
+//
+// Implementation of CoverManagerPrivate methods.
+//
+void CoverManagerPrivate::createDataDir() const
+{
+ QDir dir;
+ QString dirPath(QDir::cleanDirPath(coverLocation() + "/.."));
+ if(!dir.exists(dirPath))
+ KStandardDirs::makeDir(dirPath);
+}
+
+void CoverManagerPrivate::saveCovers() const
+{
+ kdDebug() << k_funcinfo << endl;
+
+ // Make sure the directory exists first.
+ createDataDir();
+
+ QFile file(coverLocation());
+
+ kdDebug() << "Opening covers db: " << coverLocation() << endl;
+
+ if(!file.open(IO_WriteOnly)) {
+ kdError() << "Unable to save covers to disk!\n";
+ return;
+ }
+
+ QDataStream out(&file);
+
+ // Write out the version and count
+ out << Q_UINT32(0) << Q_UINT32(covers.count());
+
+ // Write out the data
+ for(CoverDataMap::ConstIterator it = covers.begin(); it != covers.end(); ++it) {
+ out << Q_UINT32(it.key());
+ out << *it.data();
+ }
+
+ // Now write out the track mapping.
+ out << Q_UINT32(tracks.count());
+
+ QDictIterator<coverKey> trackMapIt(tracks);
+ while(trackMapIt.current()) {
+ out << trackMapIt.currentKey() << Q_UINT32(*trackMapIt.current());
+ ++trackMapIt;
+ }
+}
+
+void CoverManagerPrivate::loadCovers()
+{
+ kdDebug() << k_funcinfo << endl;
+
+ QFile file(coverLocation());
+
+ if(!file.open(IO_ReadOnly)) {
+ // Guess we don't have any covers yet.
+ return;
+ }
+
+ QDataStream in(&file);
+ Q_UINT32 count, version;
+
+ // First thing we'll read in will be the version.
+ // Only version 0 is defined for now.
+ in >> version;
+ if(version > 0) {
+ kdError() << "Cover database was created by a higher version of JuK,\n";
+ kdError() << "I don't know what to do with it.\n";
+
+ return;
+ }
+
+ // Read in the count next, then the data.
+ in >> count;
+ for(Q_UINT32 i = 0; i < count; ++i) {
+ // Read the id, and 3 QStrings for every 1 of the count.
+ Q_UINT32 id;
+ CoverDataPtr data(new CoverData);
+
+ in >> id;
+ in >> *data;
+ data->refCount = 0;
+
+ covers[(coverKey) id] = data;
+ }
+
+ in >> count;
+ for(Q_UINT32 i = 0; i < count; ++i) {
+ QString path;
+ Q_UINT32 id;
+
+ in >> path >> id;
+
+ // If we somehow already managed to load a cover id with this path,
+ // don't do so again. Possible due to a coding error during 3.5
+ // development.
+
+ if(!tracks.find(path)) {
+ ++covers[(coverKey) id]->refCount; // Another track using this.
+ tracks.insert(path, new coverKey(id));
+ }
+ }
+}
+
+QString CoverManagerPrivate::coverLocation() const
+{
+ return KGlobal::dirs()->saveLocation("appdata") + "coverdb/covers";
+}
+
+// XXX: This could probably use some improvement, I don't like the linear
+// search for ID idea.
+coverKey CoverManagerPrivate::nextId() const
+{
+ // Start from 1...
+ coverKey key = 1;
+
+ while(covers.contains(key))
+ ++key;
+
+ return key;
+}
+
+//
+// Implementation of CoverDrag
+//
+CoverDrag::CoverDrag(coverKey id, QWidget *src) : QDragObject(src, "coverDrag"),
+ m_id(id)
+{
+ QPixmap cover = CoverManager::coverFromId(id);
+ if(!cover.isNull())
+ setPixmap(cover);
+}
+
+const char *CoverDrag::format(int i) const
+{
+ if(i == 0)
+ return mimetype;
+ if(i == 1)
+ return "image/png";
+
+ return 0;
+}
+
+QByteArray CoverDrag::encodedData(const char *mimetype) const
+{
+ if(qstrcmp(CoverDrag::mimetype, mimetype) == 0) {
+ QByteArray data;
+ QDataStream ds(data, IO_WriteOnly);
+
+ ds << Q_UINT32(m_id);
+ return data;
+ }
+ else if(qstrcmp(mimetype, "image/png") == 0) {
+ QPixmap large = CoverManager::coverFromId(m_id, CoverManager::FullSize);
+ QImage img = large.convertToImage();
+ QByteArray data;
+ QBuffer buffer(data);
+
+ buffer.open(IO_WriteOnly);
+ img.save(&buffer, "PNG"); // Write in PNG format.
+
+ return data;
+ }
+
+ return QByteArray();
+}
+
+bool CoverDrag::canDecode(const QMimeSource *e)
+{
+ return e->provides(mimetype);
+}
+
+bool CoverDrag::decode(const QMimeSource *e, coverKey &id)
+{
+ if(!canDecode(e))
+ return false;
+
+ QByteArray data = e->encodedData(mimetype);
+ QDataStream ds(data, IO_ReadOnly);
+ Q_UINT32 i;
+
+ ds >> i;
+ id = (coverKey) i;
+
+ return true;
+}
+
+//
+// Implementation of CoverManager methods.
+//
+coverKey CoverManager::idFromMetadata(const QString &artist, const QString &album)
+{
+ // Search for the string, yay! It might make sense to use a cache here,
+ // if so it's not hard to add a QCache.
+ CoverDataMap::ConstIterator it = begin();
+ CoverDataMap::ConstIterator endIt = end();
+
+ for(; it != endIt; ++it) {
+ if(it.data()->album == album.lower() && it.data()->artist == artist.lower())
+ return it.key();
+ }
+
+ return NoMatch;
+}
+
+QPixmap CoverManager::coverFromId(coverKey id, Size size)
+{
+ CoverDataPtr info = coverInfo(id);
+
+ if(!info)
+ return QPixmap();
+
+ if(size == Thumbnail)
+ return info->thumbnail();
+
+ return info->pixmap();
+}
+
+QPixmap CoverManager::coverFromData(const CoverData &coverData, Size size)
+{
+ QString path = coverData.path;
+
+ // Prepend a tag to the path to separate in the cache between full size
+ // and thumbnail pixmaps. If we add a different kind of pixmap in the
+ // future we also need to add a tag letter for it.
+ if(size == FullSize)
+ path.prepend('f');
+ else
+ path.prepend('t');
+
+ // Check in cache for the pixmap.
+ QPixmap *pix = data()->pixmapCache[path];
+ if(pix) {
+ kdDebug(65432) << "Found pixmap in cover cache.\n";
+ return *pix;
+ }
+
+ // Not in cache, load it and add it.
+ pix = new QPixmap(coverData.path);
+ if(pix->isNull())
+ return QPixmap();
+
+ if(size == Thumbnail) {
+ // Convert to image for smoothScale()
+ QImage image = pix->convertToImage();
+ pix->convertFromImage(image.smoothScale(80, 80, QImage::ScaleMin));
+ }
+
+ QPixmap returnValue = *pix; // Save it early.
+ if(!data()->pixmapCache.insert(path, pix, pix->height() * pix->width()))
+ delete pix;
+
+ return returnValue;
+}
+
+coverKey CoverManager::addCover(const QPixmap &large, const QString &artist, const QString &album)
+{
+ kdDebug() << k_funcinfo << endl;
+
+ coverKey id = data()->nextId();
+ CoverDataPtr coverData(new CoverData);
+
+ if(large.isNull()) {
+ kdDebug() << "The pixmap you're trying to add is NULL!\n";
+ return NoMatch;
+ }
+
+ // Save it to file first!
+
+ QString ext = QString("/coverdb/coverID-%1.png").arg(id);
+ coverData->path = KGlobal::dirs()->saveLocation("appdata") + ext;
+
+ kdDebug() << "Saving pixmap to " << coverData->path << endl;
+ data()->createDataDir();
+
+ if(!large.save(coverData->path, "PNG")) {
+ kdError() << "Unable to save pixmap to " << coverData->path << endl;
+ return NoMatch;
+ }
+
+ coverData->artist = artist.lower();
+ coverData->album = album.lower();
+ coverData->refCount = 0;
+
+ data()->covers[id] = coverData;
+
+ // Make sure the new cover isn't inadvertently cached.
+ data()->pixmapCache.remove(QString("f%1").arg(coverData->path));
+ data()->pixmapCache.remove(QString("t%1").arg(coverData->path));
+
+ return id;
+}
+
+coverKey CoverManager::addCover(const QString &path, const QString &artist, const QString &album)
+{
+ return addCover(QPixmap(path), artist, album);
+}
+
+bool CoverManager::hasCover(coverKey id)
+{
+ return data()->covers.contains(id);
+}
+
+bool CoverManager::removeCover(coverKey id)
+{
+ if(!hasCover(id))
+ return false;
+
+ // Remove cover from cache.
+ CoverDataPtr coverData = coverInfo(id);
+ data()->pixmapCache.remove(QString("f%1").arg(coverData->path));
+ data()->pixmapCache.remove(QString("t%1").arg(coverData->path));
+
+ // Remove references to files that had that track ID.
+ QDictIterator<coverKey> it(data()->tracks);
+ for(; it.current(); ++it)
+ if(*it.current() == id)
+ data()->tracks.remove(it.currentKey());
+
+ // Remove covers from disk.
+ QFile::remove(coverData->path);
+
+ // Finally, forget that we ever knew about this cover.
+ data()->covers.remove(id);
+
+ return true;
+}
+
+bool CoverManager::replaceCover(coverKey id, const QPixmap &large)
+{
+ if(!hasCover(id))
+ return false;
+
+ CoverDataPtr coverData = coverInfo(id);
+
+ // Empty old pixmaps from cache.
+ data()->pixmapCache.remove(QString("%1%2").arg("t", coverData->path));
+ data()->pixmapCache.remove(QString("%1%2").arg("f", coverData->path));
+
+ large.save(coverData->path, "PNG");
+ return true;
+}
+
+CoverManagerPrivate *CoverManager::data()
+{
+ if(!m_data)
+ sd.setObject(m_data, new CoverManagerPrivate);
+
+ return m_data;
+}
+
+void CoverManager::saveCovers()
+{
+ data()->saveCovers();
+}
+
+void CoverManager::shutdown()
+{
+ sd.destructObject();
+}
+
+CoverDataMap::ConstIterator CoverManager::begin()
+{
+ return data()->covers.constBegin();
+}
+
+CoverDataMap::ConstIterator CoverManager::end()
+{
+ return data()->covers.constEnd();
+}
+
+QValueList<coverKey> CoverManager::keys()
+{
+ return data()->covers.keys();
+}
+
+void CoverManager::setIdForTrack(const QString &path, coverKey id)
+{
+ coverKey *oldId = data()->tracks.find(path);
+ if(oldId && (id == *oldId))
+ return; // We're already done.
+
+ if(oldId && *oldId != NoMatch) {
+ data()->covers[*oldId]->refCount--;
+ data()->tracks.remove(path);
+
+ if(data()->covers[*oldId]->refCount == 0) {
+ kdDebug(65432) << "Cover " << *oldId << " is unused, removing.\n";
+ removeCover(*oldId);
+ }
+ }
+
+ if(id != NoMatch) {
+ data()->covers[id]->refCount++;
+ data()->tracks.insert(path, new coverKey(id));
+ }
+}
+
+coverKey CoverManager::idForTrack(const QString &path)
+{
+ coverKey *coverPtr = data()->tracks.find(path);
+
+ if(!coverPtr)
+ return NoMatch;
+
+ return *coverPtr;
+}
+
+CoverDataPtr CoverManager::coverInfo(coverKey id)
+{
+ if(data()->covers.contains(id))
+ return data()->covers[id];
+
+ return CoverDataPtr(0);
+}
+
+/**
+ * Write @p data out to @p out.
+ *
+ * @param out the data stream to write @p data out to.
+ * @param data the CoverData to write out.
+ * @return the data stream that the data was written to.
+ */
+QDataStream &operator<<(QDataStream &out, const CoverData &data)
+{
+ out << data.artist;
+ out << data.album;
+ out << data.path;
+
+ return out;
+}
+
+/**
+ * Read @p data from @p in.
+ *
+ * @param in the data stream to read from.
+ * @param data the CoverData to read into.
+ * @return the data stream read from.
+ */
+QDataStream &operator>>(QDataStream &in, CoverData &data)
+{
+ in >> data.artist;
+ in >> data.album;
+ in >> data.path;
+
+ return in;
+}
+
+// vim: set et sw=4 ts=4:
diff --git a/juk/covermanager.h b/juk/covermanager.h
new file mode 100644
index 00000000..9a95d991
--- /dev/null
+++ b/juk/covermanager.h
@@ -0,0 +1,262 @@
+/***************************************************************************
+ begin : Sun May 15 2005
+ copyright : (C) 2005 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef JUK_COVERMANAGER_H
+#define JUK_COVERMANAGER_H
+
+#include <ksharedptr.h>
+
+#include <qmap.h>
+#include <qdragobject.h>
+
+class CoverManagerPrivate;
+class QString;
+class QPixmap;
+class QDataStream;
+
+/**
+ * This class holds the data on a cover. This includes the path to the cover
+ * representation on-disk, and the artist and album associated with the cover.
+ * Don't assume that the artist or album information is filled out, it is
+ * there to allow the CoverManager to try to automatically assign covers to
+ * new tracks.
+ *
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ * @see CoverManager
+ */
+class CoverData : public KShared
+{
+public:
+ QPixmap pixmap() const;
+ QPixmap thumbnail() const;
+
+ QString artist;
+ QString album;
+ QString path;
+
+ unsigned refCount; // Refers to number of tracks using this.
+};
+
+typedef KSharedPtr<CoverData> CoverDataPtr;
+typedef unsigned long coverKey; ///< Type of the id for a cover.
+typedef QMap<coverKey, CoverDataPtr> CoverDataMap;
+
+/**
+ * This class is used to drag covers in JuK. It adds a special mimetype that
+ * contains the cover ID used for this cover, and also supports an image/png
+ * mimetype for dragging to other applications.
+ *
+ * As of this writing the mimetype is application/x-juk-coverid
+ *
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ */
+class CoverDrag : public QDragObject
+{
+public:
+ CoverDrag(coverKey id, QWidget *src);
+
+ virtual const char *format(int i) const;
+ virtual QByteArray encodedData(const char *mimetype) const;
+
+ void setId(coverKey id) { m_id = id; }
+
+ /**
+ * Returns true if CoverDrag can decode the given mime source. Note that
+ * true is returned only if \p e contains a cover id, even though
+ * CoverDrag can convert it to an image.
+ */
+ static bool canDecode(const QMimeSource *e);
+ static bool decode(const QMimeSource *e, coverKey &id);
+
+ static const char* mimetype;
+
+private:
+ coverKey m_id;
+};
+
+/**
+ * This class holds all of the cover art, and manages looking it up by artist
+ * and/or album. This class is similar to a singleton class, but instead all
+ * of the methods are static. This way you can invoke methods like this:
+ * \code
+ * CoverManager::method()
+ * \endcode
+ * instead of using:
+ * \code
+ * CoverManager::instance()->method()
+ * \endcode
+ *
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ */
+class CoverManager
+{
+public:
+ /// The set of different sizes you can request a pixmap as.
+ typedef enum { Thumbnail, FullSize } Size;
+
+ /**
+ * Tries to match @p artist and @p album to a cover in the database.
+ *
+ * @param artist The artist to look for matching covers on.
+ * @param album The album to look for matching covers on.
+ * @return NoMatch if no match could be found, otherwise the id of the
+ * cover art that matches the given metadata.
+ */
+ static coverKey idFromMetadata(const QString &artist, const QString &album);
+
+ /**
+ * Returns the cover art for @p id.
+ *
+ * @param id The id of the cover.
+ * @param size The size to return it as. Note that FullSize doesn't
+ * necessarily mean the pixmap is large, so you may need to
+ * scale it up.
+ * @return QPixmap::null if there is no cover art for @p id, otherwise the
+ * cover art.
+ */
+ static QPixmap coverFromId(coverKey id, Size size = Thumbnail);
+
+ /**
+ * Returns the cover art for @p ptr. This function is intended for use
+ * by CoverData.
+ *
+ * @param ptr The CoverData to get the cover of. Note that it is a
+ * CoverData, not CoverDataPtr.
+ * @param size The size to return it as.
+ * @see CoverData
+ */
+ static QPixmap coverFromData(const CoverData &coverData, Size size = Thumbnail);
+
+ /**
+ * Returns the full suite of information known about the cover given by
+ * @p id.
+ *
+ * @param id the id of the cover to retrieve info on.
+ * @return 0 if there is no info on @p id, otherwise its information.
+ */
+ static CoverDataPtr coverInfo(coverKey id);
+
+ /**
+ * Adds @p large to the cover database, associating with it @p artist and
+ * @p album.
+ *
+ * @param large The full size cover (the thumbnail is automatically
+ * generated).
+ * @param artist The artist of the new cover.
+ * @param album The album of the new cover.
+ */
+ static coverKey addCover(const QPixmap &large, const QString &artist = "", const QString &album = "");
+
+ /**
+ * Adds the file pointed to by the local path @p path to the database,
+ * associating it with @p artist and @p album.
+ *
+ * @param path The absolute path to the fullsize cover art.
+ * @param artist The artist of the new cover.
+ * @param album The album of the new cover.
+ */
+ static coverKey addCover(const QString &path, const QString &artist = "", const QString &album = "");
+
+ /**
+ * Function to determine if @p id matches any covers in the database.
+ *
+ * @param id The id of the cover to search for.
+ * @return true if the database has a cover identified by @p id, false
+ * otherwise.
+ */
+ static bool hasCover(coverKey id);
+
+ /**
+ * Removes the cover identified by @p id.
+ *
+ * @param id the id of the cover to remove.
+ * @return true if the removal was successful, false if unsuccessful or if
+ * the cover didn't exist.
+ */
+ static bool removeCover(coverKey id);
+
+ /**
+ * Replaces the cover art for the cover identified by @p id with @p large.
+ * Any other metadata such as artist and album is unchanged.
+ *
+ * @param id The id of the cover to replace.
+ * @param large The full size cover art for the new cover.
+ */
+ static bool replaceCover(coverKey id, const QPixmap &large);
+
+ /**
+ * Saves the current CoverManager information to disk. Changes are not
+ * automatically written to disk due to speed issues, so you can
+ * periodically call this function while running to reduce the chance of
+ * lost data in the event of a crash.
+ */
+ static void saveCovers();
+
+ /**
+ * This is a hack, as we should be shut down automatically by
+ * KStaticDeleter, but JuK is crashing for me on shutdown before
+ * KStaticDeleter gets a chance to run, which is cramping my testing.
+ */
+ static void shutdown();
+
+ /**
+ * @return Iterator pointing to the first element in the cover database.
+ */
+ static CoverDataMap::ConstIterator begin();
+
+ /**
+ * @return Iterator pointing after the last element in the cover database.
+ */
+ static CoverDataMap::ConstIterator end();
+
+ /**
+ * @return A list of all of the id's listed in the database.
+ */
+ static QValueList<coverKey> keys();
+
+ /**
+ * Associates @p path with the cover identified by @id. No comparison of
+ * metadata is performed to enforce this matching.
+ *
+ * @param path The absolute file path to the track.
+ * @param id The identifier of the cover to use with @p path.
+ */
+ static void setIdForTrack(const QString &path, coverKey id);
+
+ /**
+ * Returns the identifier of the cover for the track at @p path.
+ *
+ * @param path The absolute file path to the track.
+ * @return NoMatch if @p path doesn't have a cover, otherwise the id of
+ * its cover.
+ */
+ static coverKey idForTrack(const QString &path);
+
+ /**
+ * This identifier is used to indicate that no cover was found in the
+ * database.
+ */
+ static const coverKey NoMatch = 0;
+
+ private:
+ static CoverManagerPrivate *m_data;
+
+ static CoverManagerPrivate *data();
+ static QPixmap createThumbnail(const QPixmap &base);
+};
+
+#endif /* JUK_COVERMANAGER_H */
+
+// vim: set et sw=4 ts=4:
diff --git a/juk/cr22-action-juk_dock.png b/juk/cr22-action-juk_dock.png
new file mode 100644
index 00000000..8d023aa6
--- /dev/null
+++ b/juk/cr22-action-juk_dock.png
Binary files differ
diff --git a/juk/deletedialog.cpp b/juk/deletedialog.cpp
new file mode 100644
index 00000000..cafd7849
--- /dev/null
+++ b/juk/deletedialog.cpp
@@ -0,0 +1,121 @@
+/***************************************************************************
+ begin : Tue Aug 31 21:59:58 EST 2004
+ copyright : (C) 2004 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kdialogbase.h>
+#include <kglobal.h>
+#include <kstdguiitem.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kconfig.h>
+
+#include <qstringlist.h>
+#include <qcheckbox.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qvbox.h>
+#include <qhbox.h>
+
+#include "deletedialog.h"
+
+//////////////////////////////////////////////////////////////////////////////
+// DeleteWidget implementation
+//////////////////////////////////////////////////////////////////////////////
+
+DeleteWidget::DeleteWidget(QWidget *parent, const char *name)
+ : DeleteDialogBase(parent, name)
+{
+ KConfigGroup messageGroup(KGlobal::config(), "FileRemover");
+
+ bool deleteInstead = messageGroup.readBoolEntry("deleteInsteadOfTrash", false);
+ slotShouldDelete(deleteInstead);
+ ddShouldDelete->setChecked(deleteInstead);
+}
+
+void DeleteWidget::setFiles(const QStringList &files)
+{
+ ddFileList->clear();
+ ddFileList->insertStringList(files);
+ ddNumFiles->setText(i18n("<b>1</b> file selected.", "<b>%n</b> files selected.", files.count()));
+}
+
+void DeleteWidget::slotShouldDelete(bool shouldDelete)
+{
+ if(shouldDelete) {
+ ddDeleteText->setText(i18n("<qt>These items will be <b>permanently "
+ "deleted</b> from your hard disk.</qt>"));
+ ddWarningIcon->setPixmap(KGlobal::iconLoader()->loadIcon("messagebox_warning",
+ KIcon::Desktop, KIcon::SizeLarge));
+ }
+ else {
+ ddDeleteText->setText(i18n("<qt>These items will be moved to the Trash Bin.</qt>"));
+ ddWarningIcon->setPixmap(KGlobal::iconLoader()->loadIcon("trashcan_full",
+ KIcon::Desktop, KIcon::SizeLarge));
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// DeleteDialog implementation
+//////////////////////////////////////////////////////////////////////////////
+
+DeleteDialog::DeleteDialog(QWidget *parent, const char *name) :
+ KDialogBase(Swallow, WStyle_DialogBorder, parent, name,
+ true /* modal */, i18n("About to delete selected files"),
+ Ok | Cancel, Cancel /* Default */, true /* separator */),
+ m_trashGuiItem(i18n("&Send to Trash"), "trashcan_full")
+{
+ m_widget = new DeleteWidget(this, "delete_dialog_widget");
+ setMainWidget(m_widget);
+
+ m_widget->setMinimumSize(400, 300);
+ setMinimumSize(410, 326);
+ adjustSize();
+
+ slotShouldDelete(shouldDelete());
+
+ connect(m_widget->ddShouldDelete, SIGNAL(toggled(bool)), SLOT(slotShouldDelete(bool)));
+}
+
+bool DeleteDialog::confirmDeleteList(const QStringList &condemnedFiles)
+{
+ m_widget->setFiles(condemnedFiles);
+
+ return exec() == QDialog::Accepted;
+}
+
+void DeleteDialog::setFiles(const QStringList &files)
+{
+ m_widget->setFiles(files);
+}
+
+void DeleteDialog::accept()
+{
+ KConfigGroup messageGroup(KGlobal::config(), "FileRemover");
+
+ // Save user's preference
+
+ messageGroup.writeEntry("deleteInsteadOfTrash", shouldDelete());
+ messageGroup.sync();
+
+ KDialogBase::accept();
+}
+
+void DeleteDialog::slotShouldDelete(bool shouldDelete)
+{
+ setButtonGuiItem(Ok, shouldDelete ? KStdGuiItem::del() : m_trashGuiItem);
+}
+
+#include "deletedialog.moc"
+
+// vim: set et ts=4 sw=4:
diff --git a/juk/deletedialog.h b/juk/deletedialog.h
new file mode 100644
index 00000000..9fd45838
--- /dev/null
+++ b/juk/deletedialog.h
@@ -0,0 +1,64 @@
+/***************************************************************************
+ begin : Tue Aug 31 21:54:20 EST 2004
+ copyright : (C) 2004 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef _DELETEDIALOG_H
+#define _DELETEDIALOG_H
+
+
+#include <qcheckbox.h>
+
+#include "deletedialogbase.h"
+
+class QStringList;
+class KListBox;
+class QLabel;
+class QWidgetStack;
+
+class DeleteWidget : public DeleteDialogBase
+{
+ Q_OBJECT
+
+public:
+ DeleteWidget(QWidget *parent = 0, const char *name = 0);
+
+ void setFiles(const QStringList &files);
+
+protected slots:
+ virtual void slotShouldDelete(bool shouldDelete);
+};
+
+class DeleteDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ DeleteDialog(QWidget *parent, const char *name = "delete_dialog");
+
+ bool confirmDeleteList(const QStringList &condemnedFiles);
+ void setFiles(const QStringList &files);
+ bool shouldDelete() const { return m_widget->ddShouldDelete->isChecked(); }
+
+protected slots:
+ virtual void accept();
+ void slotShouldDelete(bool shouldDelete);
+
+private:
+ DeleteWidget *m_widget;
+ KGuiItem m_trashGuiItem;
+};
+
+#endif
+
+// vim: set et ts=4 sw=4:
diff --git a/juk/deletedialogbase.ui b/juk/deletedialogbase.ui
new file mode 100644
index 00000000..f1054d79
--- /dev/null
+++ b/juk/deletedialogbase.ui
@@ -0,0 +1,143 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>DeleteDialogBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>DeleteDialogBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>542</width>
+ <height>374</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>420</width>
+ <height>320</height>
+ </size>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>ddWarningIcon</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Icon Placeholder, not in GUI</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Are you sure that you want to remove these items?</string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>ddDeleteText</cstring>
+ </property>
+ <property name="text">
+ <string>Deletion method placeholder, never shown to user.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignCenter</set>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="KListBox">
+ <property name="name">
+ <cstring>ddFileList</cstring>
+ </property>
+ <property name="selectionMode">
+ <enum>NoSelection</enum>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>List of files that are about to be deleted.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is the list of items that are about to be deleted.</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>ddNumFiles</cstring>
+ </property>
+ <property name="text">
+ <string>Placeholder for number of files, not in GUI</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>ddShouldDelete</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Delete files instead of moving them to the trash</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>If checked, files will be permanently removed instead of being placed in the Trash Bin</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;&lt;p&gt;If this box is checked, files will be &lt;b&gt;permanently removed&lt;/b&gt; instead of being placed in the Trash Bin.&lt;/p&gt;
+
+&lt;p&gt;&lt;em&gt;Use this option with caution&lt;/em&gt;: Most filesystems are unable to reliably undelete deleted files.&lt;/p&gt;&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>ddShouldDelete</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DeleteDialogBase</receiver>
+ <slot>slotShouldDelete(bool)</slot>
+ </connection>
+</connections>
+<slots>
+ <slot access="protected">slotShouldDelete(bool)</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistbox.h</includehint>
+</includehints>
+</UI>
diff --git a/juk/directorylist.cpp b/juk/directorylist.cpp
new file mode 100644
index 00000000..1e53cac8
--- /dev/null
+++ b/juk/directorylist.cpp
@@ -0,0 +1,101 @@
+/***************************************************************************
+ begin : Tue Feb 4 2003
+ copyright : (C) 2003 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kfiledialog.h>
+#include <klocale.h>
+#include <klistview.h>
+#include <kpushbutton.h>
+
+#include <qcheckbox.h>
+
+#include "directorylistbase.h"
+#include "directorylist.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+DirectoryList::DirectoryList(const QStringList &directories, bool importPlaylists,
+ QWidget *parent, const char *name) :
+ KDialogBase(parent, name, true, i18n("Folder List"), Ok | Cancel, Ok, true),
+ m_dirList(directories),
+ m_importPlaylists(importPlaylists)
+{
+ m_base = new DirectoryListBase(this);
+
+ setMainWidget(m_base);
+
+ m_base->directoryListView->setFullWidth(true);
+
+ connect(m_base->addDirectoryButton, SIGNAL(clicked()),
+ SLOT(slotAddDirectory()));
+ connect(m_base->removeDirectoryButton, SIGNAL(clicked()),
+ SLOT(slotRemoveDirectory()));
+
+ QStringList::ConstIterator it = directories.begin();
+ for(; it != directories.end(); ++it)
+ new KListViewItem(m_base->directoryListView, *it);
+
+ m_base->importPlaylistsCheckBox->setChecked(importPlaylists);
+
+ QSize sz = sizeHint();
+ setMinimumSize(kMax(350, sz.width()), kMax(250, sz.height()));
+ resize(sizeHint());
+}
+
+DirectoryList::~DirectoryList()
+{
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public slots
+////////////////////////////////////////////////////////////////////////////////
+
+DirectoryList::Result DirectoryList::exec()
+{
+ m_result.status = static_cast<DialogCode>(KDialogBase::exec());
+ m_result.addPlaylists = m_base->importPlaylistsCheckBox->isChecked();
+ return m_result;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private slots
+////////////////////////////////////////////////////////////////////////////////
+
+void DirectoryList::slotAddDirectory()
+{
+ QString dir = KFileDialog::getExistingDirectory();
+ if(!dir.isEmpty() && m_dirList.find(dir) == m_dirList.end()) {
+ m_dirList.append(dir);
+ new KListViewItem(m_base->directoryListView, dir);
+ m_result.addedDirs.append(dir);
+ }
+}
+
+void DirectoryList::slotRemoveDirectory()
+{
+ if(!m_base->directoryListView->selectedItem())
+ return;
+
+ QString dir = m_base->directoryListView->selectedItem()->text(0);
+ m_dirList.remove(dir);
+ m_result.removedDirs.append(dir);
+ delete m_base->directoryListView->selectedItem();
+}
+
+#include "directorylist.moc"
+
+// vim: ts=8
diff --git a/juk/directorylist.h b/juk/directorylist.h
new file mode 100644
index 00000000..fef730a2
--- /dev/null
+++ b/juk/directorylist.h
@@ -0,0 +1,59 @@
+/***************************************************************************
+ begin : Tue Feb 4 2003
+ copyright : (C) 2003 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef DIRECTORYLIST_H
+#define DIRECTORYLIST_H
+
+
+class DirectoryListBase;
+
+class DirectoryList : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ struct Result
+ {
+ QStringList addedDirs;
+ QStringList removedDirs;
+ DialogCode status;
+ bool addPlaylists;
+ };
+
+ DirectoryList(const QStringList &directories, bool importPlaylists,
+ QWidget *parent = 0, const char *name = 0);
+ virtual ~DirectoryList();
+
+public slots:
+ Result exec();
+
+signals:
+ void signalDirectoryAdded(const QString &directory);
+ void signalDirectoryRemoved(const QString &directory);
+
+private slots:
+ void slotAddDirectory();
+ void slotRemoveDirectory();
+
+private:
+ QStringList m_dirList;
+ bool m_importPlaylists;
+ DirectoryListBase *m_base;
+ Result m_result;
+};
+
+#endif
+
+// vim:ts=8
diff --git a/juk/directorylistbase.ui b/juk/directorylistbase.ui
new file mode 100644
index 00000000..643417d3
--- /dev/null
+++ b/juk/directorylistbase.ui
@@ -0,0 +1,112 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>DirectoryListBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>DirectoryListBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>458</width>
+ <height>229</height>
+ </rect>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Folders</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>directoryListView</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>rightColumnLayout</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>addDirectoryButton</cstring>
+ </property>
+ <property name="text">
+ <string>Add Folder...</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>removeDirectoryButton</cstring>
+ </property>
+ <property name="text">
+ <string>Remove Folder</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>informationLabel</cstring>
+ </property>
+ <property name="text">
+ <string>These folders will be scanned on startup for new files.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>rightColumnSpacer</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>importPlaylistsCheckBox</cstring>
+ </property>
+ <property name="text">
+ <string>Import playlists</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+</widget>
+<includes>
+ <include location="global" impldecl="in implementation">kdialog.h</include>
+</includes>
+<layoutdefaults spacing="6" margin="0"/>
+<layoutfunctions spacing="KDialog::spacingHint"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/juk/dynamicplaylist.cpp b/juk/dynamicplaylist.cpp
new file mode 100644
index 00000000..69d0a4ae
--- /dev/null
+++ b/juk/dynamicplaylist.cpp
@@ -0,0 +1,194 @@
+/***************************************************************************
+ begin : Mon May 5 2003
+ copyright : (C) 2003 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kdebug.h>
+
+#include "dynamicplaylist.h"
+#include "collectionlist.h"
+#include "playlistcollection.h"
+#include "tracksequencemanager.h"
+
+class PlaylistDirtyObserver : public PlaylistObserver
+{
+public:
+ PlaylistDirtyObserver(DynamicPlaylist *parent, Playlist *playlist) :
+ PlaylistObserver(playlist),
+ m_parent(parent)
+ {
+
+ }
+ virtual void updateData() { m_parent->slotSetDirty(); }
+ virtual void updateCurrent() {}
+
+private:
+ DynamicPlaylist *m_parent;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+DynamicPlaylist::DynamicPlaylist(const PlaylistList &playlists,
+ PlaylistCollection *collection,
+ const QString &name,
+ const QString &iconName,
+ bool setupPlaylist,
+ bool synchronizePlaying) :
+ Playlist(collection, true),
+ m_playlists(playlists),
+ m_dirty(true),
+ m_synchronizePlaying(synchronizePlaying)
+{
+ if(setupPlaylist)
+ collection->setupPlaylist(this, iconName);
+ setName(name);
+
+ setSorting(columns() + 1);
+
+ for(PlaylistList::ConstIterator it = playlists.begin(); it != playlists.end(); ++it)
+ m_observers.append(new PlaylistDirtyObserver(this, *it));
+
+ connect(CollectionList::instance(), SIGNAL(signalCollectionChanged()), this, SLOT(slotSetDirty()));
+}
+
+DynamicPlaylist::~DynamicPlaylist()
+{
+ lower();
+
+ for(QValueList<PlaylistObserver *>::ConstIterator it = m_observers.begin();
+ it != m_observers.end();
+ ++it)
+ {
+ delete *it;
+ }
+}
+
+void DynamicPlaylist::setPlaylists(const PlaylistList &playlists)
+{
+ m_playlists = playlists;
+ updateItems();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public slots
+////////////////////////////////////////////////////////////////////////////////
+
+void DynamicPlaylist::slotReload()
+{
+ for(PlaylistList::Iterator it = m_playlists.begin(); it != m_playlists.end(); ++it)
+ (*it)->slotReload();
+
+ checkUpdateItems();
+}
+
+void DynamicPlaylist::lower(QWidget *top)
+{
+ if(top == this)
+ return;
+
+ if(playing()) {
+ PlaylistList l;
+ l.append(this);
+ for(PlaylistList::Iterator it = m_playlists.begin();
+ it != m_playlists.end(); ++it)
+ {
+ (*it)->synchronizePlayingItems(l, true);
+ }
+ }
+
+ PlaylistItemList list = PlaylistItem::playingItems();
+ for(PlaylistItemList::Iterator it = list.begin(); it != list.end(); ++it) {
+ if((*it)->playlist() == this) {
+ list.remove(it);
+ break;
+ }
+ }
+
+ if(!list.isEmpty())
+ TrackSequenceManager::instance()->setCurrentPlaylist(list.front()->playlist());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected methods
+////////////////////////////////////////////////////////////////////////////////
+
+PlaylistItemList DynamicPlaylist::items()
+{
+ checkUpdateItems();
+ return Playlist::items();
+}
+
+void DynamicPlaylist::showEvent(QShowEvent *e)
+{
+ checkUpdateItems();
+ Playlist::showEvent(e);
+}
+
+void DynamicPlaylist::paintEvent(QPaintEvent *e)
+{
+ checkUpdateItems();
+ Playlist::paintEvent(e);
+}
+
+void DynamicPlaylist::updateItems()
+{
+ PlaylistItemList siblings;
+
+ for(PlaylistList::ConstIterator it = m_playlists.begin(); it != m_playlists.end(); ++it)
+ siblings += (*it)->items();
+
+
+ PlaylistItemList newSiblings = siblings;
+ if(m_siblings != newSiblings) {
+ m_siblings = newSiblings;
+ QTimer::singleShot(0, this, SLOT(slotUpdateItems()));
+ }
+}
+
+bool DynamicPlaylist::synchronizePlaying() const
+{
+ return m_synchronizePlaying;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private methods
+////////////////////////////////////////////////////////////////////////////////
+
+void DynamicPlaylist::checkUpdateItems()
+{
+ if(!m_dirty)
+ return;
+
+ updateItems();
+
+ m_dirty = false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private slots
+////////////////////////////////////////////////////////////////////////////////
+
+void DynamicPlaylist::slotUpdateItems()
+{
+ // This should be optimized to check to see which items are already in the
+ // list and just adding those and removing the ones that aren't.
+
+ clear();
+ createItems(m_siblings);
+ if(m_synchronizePlaying)
+ synchronizePlayingItems(m_playlists, true);
+}
+
+#include "dynamicplaylist.moc"
diff --git a/juk/dynamicplaylist.h b/juk/dynamicplaylist.h
new file mode 100644
index 00000000..3e6e2c4b
--- /dev/null
+++ b/juk/dynamicplaylist.h
@@ -0,0 +1,110 @@
+/***************************************************************************
+ begin : Mon May 5 2003
+ copyright : (C) 2003 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef DYNAMICPLAYLIST_H
+#define DYNAMICPLAYLIST_H
+
+#include "playlist.h"
+
+/**
+ * A Playlist that is a union of other playlists that is created dynamically.
+ */
+
+class DynamicPlaylist : public Playlist
+{
+ Q_OBJECT
+public:
+ /**
+ * Creates a dynamic playlist based on lists.
+ */
+ DynamicPlaylist(const PlaylistList &lists,
+ PlaylistCollection *collection,
+ const QString &name = QString::null,
+ const QString &iconName = "midi",
+ bool setupPlaylist = true,
+ bool synchronizePlaying = false);
+
+ virtual ~DynamicPlaylist();
+
+ virtual bool canReload() const { return false; }
+
+ void setPlaylists(const PlaylistList &playlists);
+
+public slots:
+ /**
+ * Reimplemented so that it will reload all of the playlists that are
+ * associated with the dynamic list.
+ */
+ virtual void slotReload();
+ void slotSetDirty() { m_dirty = true; }
+
+ /**
+ * This is called when lowering the widget from the widget stack so that
+ * it can synchronize the playing item with the one that playlist it was
+ * create from.
+ */
+ void lower(QWidget *top = 0);
+
+protected:
+ /**
+ * Returns true if this list's items need to be updated the next time it's
+ * shown.
+ */
+ bool dirty() const { return m_dirty; }
+
+ /**
+ * Return a list of the items in this playlist. For example in a search
+ * list this should return only the matched items. By default it returns
+ * all of the items in the playlists associated with this dynamic list.
+ */
+ virtual PlaylistItemList items();
+
+ /**
+ * Reimplemented from QWidget. Here it updates the list of items (when
+ * appropriate) as the widget is shown.
+ */
+ virtual void showEvent(QShowEvent *e);
+
+ virtual void paintEvent(QPaintEvent *e);
+
+ /**
+ * Updates the items (unconditionally). This should be reimplemented in
+ * subclasses to refresh the items in the dynamic list (i.e. running a
+ * search).
+ */
+ virtual void updateItems();
+
+ bool synchronizePlaying() const;
+
+private:
+ /**
+ * Checks to see if the current list of items is "dirty" and if so updates
+ * this dynamic playlist's items to be in sync with the lists that it is a
+ * wrapper around.
+ */
+ void checkUpdateItems();
+
+private slots:
+ void slotUpdateItems();
+
+private:
+ QValueList<PlaylistObserver *> m_observers;
+ PlaylistItemList m_siblings;
+ PlaylistList m_playlists;
+ bool m_dirty;
+ bool m_synchronizePlaying;
+};
+
+#endif
diff --git a/juk/exampleoptions.cpp b/juk/exampleoptions.cpp
new file mode 100644
index 00000000..68e2bc0f
--- /dev/null
+++ b/juk/exampleoptions.cpp
@@ -0,0 +1,83 @@
+/***************************************************************************
+ begin : Thu Oct 28 2004
+ copyright : (C) 2004 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kurlrequester.h>
+#include <klocale.h>
+
+#include <qradiobutton.h>
+#include <qlayout.h>
+
+#include "exampleoptions.h"
+
+ExampleOptions::ExampleOptions(QWidget *parent) :
+ ExampleOptionsBase(parent, "example options widget")
+{
+}
+
+void ExampleOptions::exampleSelectionChanged()
+{
+ if(m_fileTagsButton->isChecked())
+ emit fileChanged();
+ else
+ emit dataChanged();
+}
+
+void ExampleOptions::exampleDataChanged()
+{
+ emit dataChanged();
+}
+
+void ExampleOptions::exampleFileChanged()
+{
+ emit fileChanged();
+}
+
+ExampleOptionsDialog::ExampleOptionsDialog(QWidget *parent) :
+ QDialog(parent, "example options dialog")
+{
+ setCaption(i18n("JuK"));
+ QVBoxLayout *l = new QVBoxLayout(this);
+
+ m_options = new ExampleOptions(this);
+ l->addWidget(m_options);
+
+ // Forward signals
+
+ connect(m_options, SIGNAL(fileChanged()), SLOT(fileModeSelected()));
+ connect(m_options, SIGNAL(dataChanged()), SIGNAL(dataChanged()));
+ connect(m_options->m_exampleFile, SIGNAL(urlSelected(const QString &)),
+ this, SIGNAL(fileChanged(const QString &)));
+ connect(m_options->m_exampleFile, SIGNAL(returnPressed(const QString &)),
+ this, SIGNAL(fileChanged(const QString &)));
+}
+
+void ExampleOptionsDialog::hideEvent(QHideEvent *)
+{
+ emit signalHidden();
+}
+
+void ExampleOptionsDialog::showEvent(QShowEvent *)
+{
+ emit signalShown();
+}
+
+void ExampleOptionsDialog::fileModeSelected()
+{
+ emit fileChanged(m_options->m_exampleFile->url());
+}
+
+#include "exampleoptions.moc"
+
+// vim: set et sw=4 ts=4:
diff --git a/juk/exampleoptions.h b/juk/exampleoptions.h
new file mode 100644
index 00000000..26ee23c1
--- /dev/null
+++ b/juk/exampleoptions.h
@@ -0,0 +1,63 @@
+/***************************************************************************
+ begin : Thu Oct 28 2004
+ copyright : (C) 2004 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef JUK_EXAMPLEOPTIONS_H
+#define JUK_EXAMPLEOPTIONS_H
+
+#include <qdialog.h>
+#include "exampleoptionsbase.h"
+
+class ExampleOptions : public ExampleOptionsBase
+{
+ Q_OBJECT
+ public:
+ ExampleOptions(QWidget *parent);
+
+ protected slots:
+ virtual void exampleSelectionChanged();
+ virtual void exampleDataChanged();
+ virtual void exampleFileChanged();
+};
+
+// We're not using KDialog(Base) because this dialog won't have any push
+// buttons to close it. It's just a little floating dialog.
+class ExampleOptionsDialog : public QDialog
+{
+ Q_OBJECT
+ public:
+ ExampleOptionsDialog(QWidget *parent);
+
+ const ExampleOptions *widget() const { return m_options; }
+
+ protected:
+ virtual void hideEvent(QHideEvent *);
+ virtual void showEvent(QShowEvent *);
+
+ protected slots:
+ void fileModeSelected();
+
+ signals:
+ void fileChanged(const QString &);
+ void dataChanged();
+ void signalHidden();
+ void signalShown();
+
+ private:
+ ExampleOptions *m_options;
+};
+
+#endif /* JUK_EXAMPLEOPTIONS_H */
+
+// vim: set et sw=4 ts=4:
diff --git a/juk/exampleoptionsbase.ui b/juk/exampleoptionsbase.ui
new file mode 100644
index 00000000..a2280ef4
--- /dev/null
+++ b/juk/exampleoptionsbase.ui
@@ -0,0 +1,285 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>ExampleOptionsBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ExampleOptionsBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>308</width>
+ <height>334</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Example</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup3</cstring>
+ </property>
+ <property name="title">
+ <string>Example Tag Selection</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>m_fileTagsButton</cstring>
+ </property>
+ <property name="text">
+ <string>Get example tags from this file:</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>m_exampleFile</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="mode">
+ <number>24</number>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>m_manualTagsButton</cstring>
+ </property>
+ <property name="text">
+ <string>Enter example tags manually:</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>m_manualGroup</cstring>
+ </property>
+ <property name="title">
+ <string>Example Tags</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>m_exampleArtist</cstring>
+ </property>
+ <property name="text">
+ <string>Artist</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>m_exampleTitle</cstring>
+ </property>
+ <property name="text">
+ <string>Title</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>m_exampleAlbum</cstring>
+ </property>
+ <property name="text">
+ <string>Album</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Title:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Artist:</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="5" column="1">
+ <property name="name">
+ <cstring>m_exampleYear</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2006</number>
+ </property>
+ <property name="minValue">
+ <number>1901</number>
+ </property>
+ <property name="value">
+ <number>2004</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Album:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Genre:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Track number:</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="4" column="1">
+ <property name="name">
+ <cstring>m_exampleTrack</cstring>
+ </property>
+ <property name="value">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>m_exampleGenre</cstring>
+ </property>
+ <property name="text">
+ <string>Genre</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>Year:</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>m_manualTagsButton</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_manualGroup</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>m_fileTagsButton</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_exampleFile</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>m_fileTagsButton</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>ExampleOptionsBase</receiver>
+ <slot>exampleSelectionChanged()</slot>
+ </connection>
+ <connection>
+ <sender>m_manualTagsButton</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>ExampleOptionsBase</receiver>
+ <slot>exampleSelectionChanged()</slot>
+ </connection>
+ <connection>
+ <sender>m_exampleTitle</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>ExampleOptionsBase</receiver>
+ <slot>exampleDataChanged()</slot>
+ </connection>
+ <connection>
+ <sender>m_exampleArtist</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>ExampleOptionsBase</receiver>
+ <slot>exampleDataChanged()</slot>
+ </connection>
+ <connection>
+ <sender>m_exampleAlbum</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>ExampleOptionsBase</receiver>
+ <slot>exampleDataChanged()</slot>
+ </connection>
+ <connection>
+ <sender>m_exampleGenre</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>ExampleOptionsBase</receiver>
+ <slot>exampleDataChanged()</slot>
+ </connection>
+ <connection>
+ <sender>m_exampleTrack</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>ExampleOptionsBase</receiver>
+ <slot>exampleDataChanged()</slot>
+ </connection>
+ <connection>
+ <sender>m_exampleYear</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>ExampleOptionsBase</receiver>
+ <slot>exampleDataChanged()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>m_exampleFile</tabstop>
+ <tabstop>m_manualTagsButton</tabstop>
+ <tabstop>m_exampleTitle</tabstop>
+ <tabstop>m_exampleArtist</tabstop>
+ <tabstop>m_exampleAlbum</tabstop>
+ <tabstop>m_exampleGenre</tabstop>
+ <tabstop>m_exampleTrack</tabstop>
+ <tabstop>m_exampleYear</tabstop>
+</tabstops>
+<signals>
+ <signal>dataChanged()</signal>
+ <signal>fileChanged()</signal>
+</signals>
+<slots>
+ <slot access="protected">exampleSelectionChanged()</slot>
+ <slot access="protected">exampleDataChanged()</slot>
+ <slot access="protected">exampleFileChanged()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/juk/filehandle.cpp b/juk/filehandle.cpp
new file mode 100644
index 00000000..583cfc0d
--- /dev/null
+++ b/juk/filehandle.cpp
@@ -0,0 +1,298 @@
+/***************************************************************************
+ begin : Sun Feb 29 2004
+ copyright : (C) 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <limits.h>
+#include <stdlib.h>
+
+#include <kdebug.h>
+
+#include <qfileinfo.h>
+
+#include "filehandle.h"
+#include "filehandleproperties.h"
+#include "tag.h"
+#include "cache.h"
+#include "coverinfo.h"
+
+AddProperty(Title, tag()->title())
+AddProperty(Artist, tag()->artist())
+AddProperty(Album, tag()->album())
+AddProperty(Genre, tag()->genre())
+AddNumberProperty(Track, tag()->track())
+AddNumberProperty(Year, tag()->year())
+AddProperty(Comment, tag()->comment())
+AddNumberProperty(Seconds, tag()->seconds())
+AddNumberProperty(Bitrate, tag()->bitrate())
+AddProperty(Path, absFilePath())
+AddNumberProperty(Size, fileInfo().size())
+AddProperty(Extension, fileInfo().extension(false))
+
+static QString resolveSymLinks(const QFileInfo &file) // static
+{
+ char real[PATH_MAX];
+
+ if(file.exists() && realpath(QFile::encodeName(file.absFilePath()).data(), real))
+ return QFile::decodeName(real);
+ else
+ return file.filePath();
+}
+
+/**
+ * A simple reference counter -- pasted from TagLib.
+ */
+
+class RefCounter
+{
+public:
+ RefCounter() : refCount(1) {}
+ void ref() { refCount++; }
+ bool deref() { return ! --refCount ; }
+ int count() const { return refCount; }
+private:
+ uint refCount;
+};
+
+class FileHandle::FileHandlePrivate : public RefCounter
+{
+public:
+ FileHandlePrivate() :
+ tag(0),
+ coverInfo(0) {}
+
+ ~FileHandlePrivate()
+ {
+ delete tag;
+ delete coverInfo;
+ }
+
+ mutable Tag *tag;
+ mutable CoverInfo *coverInfo;
+ mutable QString absFilePath;
+ QFileInfo fileInfo;
+ QDateTime modificationTime;
+ QDateTime lastModified;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+FileHandle::FileHandle()
+{
+ static FileHandlePrivate nullPrivate;
+ d = &nullPrivate;
+ d->ref();
+}
+
+FileHandle::FileHandle(const FileHandle &f) :
+ d(f.d)
+{
+ if(!d) {
+ kdDebug(65432) << "The source FileHandle was not initialized." << endl;
+ d = null().d;
+ }
+ d->ref();
+}
+
+FileHandle::FileHandle(const QFileInfo &info, const QString &path) :
+ d(0)
+{
+ setup(info, path);
+}
+
+FileHandle::FileHandle(const QString &path) :
+ d(0)
+{
+ setup(QFileInfo(path), path);
+}
+
+FileHandle::FileHandle(const QString &path, CacheDataStream &s)
+{
+ d = new FileHandlePrivate;
+ d->fileInfo = QFileInfo(path);
+ d->absFilePath = path;
+ read(s);
+ Cache::instance()->insert(*this);
+}
+
+FileHandle::~FileHandle()
+{
+ if(d->deref())
+ delete d;
+}
+
+void FileHandle::refresh()
+{
+ d->fileInfo.refresh();
+ delete d->tag;
+ d->tag = new Tag(d->absFilePath);
+}
+
+void FileHandle::setFile(const QString &path)
+{
+ if(!d || isNull())
+ setup(QFileInfo(path), path);
+ else {
+ d->absFilePath = resolveSymLinks(path);
+ d->fileInfo.setFile(path);
+ d->tag->setFileName(d->absFilePath);
+ }
+}
+
+Tag *FileHandle::tag() const
+{
+ if(!d->tag)
+ d->tag = new Tag(d->absFilePath);
+
+ return d->tag;
+}
+
+CoverInfo *FileHandle::coverInfo() const
+{
+ if(!d->coverInfo)
+ d->coverInfo = new CoverInfo(*this);
+
+ return d->coverInfo;
+}
+
+QString FileHandle::absFilePath() const
+{
+ if(d->absFilePath.isNull())
+ d->absFilePath = resolveSymLinks(d->fileInfo.absFilePath());
+ return d->absFilePath;
+}
+
+const QFileInfo &FileHandle::fileInfo() const
+{
+ return d->fileInfo;
+}
+
+bool FileHandle::isNull() const
+{
+ return *this == null();
+}
+
+bool FileHandle::current() const
+{
+ return (d->modificationTime.isValid() &&
+ lastModified().isValid() &&
+ d->modificationTime >= lastModified());
+}
+
+const QDateTime &FileHandle::lastModified() const
+{
+ if(d->lastModified.isNull())
+ d->lastModified = d->fileInfo.lastModified();
+
+ return d->lastModified;
+}
+
+void FileHandle::read(CacheDataStream &s)
+{
+ switch(s.cacheVersion()) {
+ case 1:
+ default:
+ if(!d->tag)
+ d->tag = new Tag(d->absFilePath, true);
+
+ s >> *(d->tag);
+ s >> d->modificationTime;
+ break;
+ }
+}
+
+FileHandle &FileHandle::operator=(const FileHandle &f)
+{
+ if(&f == this)
+ return *this;
+
+ if(d->deref())
+ delete d;
+
+ d = f.d;
+ d->ref();
+
+ return *this;
+}
+
+bool FileHandle::operator==(const FileHandle &f) const
+{
+ return d == f.d;
+}
+
+bool FileHandle::operator!=(const FileHandle &f) const
+{
+ return d != f.d;
+}
+
+QStringList FileHandle::properties() // static
+{
+ return FileHandleProperties::properties();
+}
+
+QString FileHandle::property(const QString &name) const
+{
+ return FileHandleProperties::property(*this, name.latin1());
+}
+
+const FileHandle &FileHandle::null() // static
+{
+ static FileHandle f;
+ return f;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private methods
+////////////////////////////////////////////////////////////////////////////////
+
+void FileHandle::setup(const QFileInfo &info, const QString &path)
+{
+ if(d && !isNull())
+ return;
+
+ QString fileName = path.isNull() ? info.absFilePath() : path;
+
+ FileHandle cached = Cache::instance()->value(resolveSymLinks(fileName));
+
+ if(cached != null()) {
+ d = cached.d;
+ d->ref();
+ }
+ else {
+ d = new FileHandlePrivate;
+ d->fileInfo = info;
+ d->absFilePath = resolveSymLinks(fileName);
+ d->modificationTime = info.lastModified();
+ Cache::instance()->insert(*this);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// related functions
+////////////////////////////////////////////////////////////////////////////////
+
+QDataStream &operator<<(QDataStream &s, const FileHandle &f)
+{
+ s << *(f.tag())
+ << f.lastModified();
+
+ return s;
+}
+
+CacheDataStream &operator>>(CacheDataStream &s, FileHandle &f)
+{
+ f.read(s);
+ return s;
+}
diff --git a/juk/filehandle.h b/juk/filehandle.h
new file mode 100644
index 00000000..6c12a344
--- /dev/null
+++ b/juk/filehandle.h
@@ -0,0 +1,82 @@
+/***************************************************************************
+ begin : Sun Feb 29 2004
+ copyright : (C) 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef JUK_FILEHANDLE_H
+#define JUK_FILEHANDLE_H
+
+#include <qstringlist.h>
+
+class QFileInfo;
+class QDateTime;
+class QDataStream;
+class CoverInfo;
+
+class Tag;
+class CacheDataStream;
+
+/**
+ * An value based, explicitly shared wrapper around file related information
+ * used in JuK's playlists.
+ */
+
+class FileHandle
+{
+public:
+ FileHandle();
+ FileHandle(const FileHandle &f);
+ explicit FileHandle(const QFileInfo &info, const QString &path = QString::null);
+ explicit FileHandle(const QString &path);
+ FileHandle(const QString &path, CacheDataStream &s);
+ ~FileHandle();
+
+ /**
+ * Forces the FileHandle to reread its information from the disk.
+ */
+ void refresh();
+ void setFile(const QString &path);
+
+ Tag *tag() const;
+ CoverInfo *coverInfo() const;
+ QString absFilePath() const;
+ const QFileInfo &fileInfo() const;
+
+ bool isNull() const;
+ bool current() const;
+ const QDateTime &lastModified() const;
+
+ void read(CacheDataStream &s);
+
+ FileHandle &operator=(const FileHandle &f);
+ bool operator==(const FileHandle &f) const;
+ bool operator!=(const FileHandle &f) const;
+
+ static QStringList properties();
+ QString property(const QString &name) const;
+
+ static const FileHandle &null();
+
+private:
+ class FileHandlePrivate;
+ FileHandlePrivate *d;
+
+ void setup(const QFileInfo &info, const QString &path);
+};
+
+typedef QValueList<FileHandle> FileHandleList;
+
+QDataStream &operator<<(QDataStream &s, const FileHandle &f);
+CacheDataStream &operator>>(CacheDataStream &s, FileHandle &f);
+
+#endif
diff --git a/juk/filehandleproperties.h b/juk/filehandleproperties.h
new file mode 100644
index 00000000..b0b708bc
--- /dev/null
+++ b/juk/filehandleproperties.h
@@ -0,0 +1,94 @@
+/***************************************************************************
+ Copyright (C) 2004 by Scott Wheeler <wheeler@kde.org>
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef FILEHANDLEPROPERTIES_H
+#define FILEHANDLEPROPERTIES_H
+
+#include <qmap.h>
+
+/*
+ * These ugly macros make possible a property registration system that makes it
+ * easy to add properties to the FileHandle that can be accessed via the DCOP
+ * interface.
+ *
+ * Properties should actually be added to the filehandle.cpp file. This file
+ * is just here to separate out some of the macro-related ugliness.
+ */
+
+#define AddProperty(name, method) \
+ namespace FileHandleProperties \
+ { \
+ struct name##Property : public Property \
+ { \
+ virtual QString value(const FileHandle &f) const \
+ { \
+ return f.method; \
+ } \
+ static const int dummy; \
+ }; \
+ static name##Property name##Instance; \
+ const int name##Property::dummy = addToPropertyMap(#name, &name##Instance); \
+ }
+
+#define AddNumberProperty(name, method) \
+ namespace FileHandleProperties \
+ { \
+ struct name##Property : public Property \
+ { \
+ virtual QString value(const FileHandle &f) const \
+ { \
+ return QString::number(f.method); \
+ } \
+ static const int dummy; \
+ }; \
+ static name##Property name##Instance; \
+ const int name##Property::dummy = addToPropertyMap(#name, &name##Instance); \
+ }
+
+namespace FileHandleProperties
+{
+ struct Property
+ {
+ virtual QString value(const FileHandle &) const
+ {
+ return QString::null;
+ }
+ };
+
+ static QMap<QCString, const Property *> propertyMap;
+
+ static int addToPropertyMap(const QCString &name, Property *property)
+ {
+ propertyMap[name] = property;
+ return 0;
+ }
+
+ static QString property(const FileHandle &file, const QCString &key)
+ {
+ return propertyMap.contains(key) ? propertyMap[key]->value(file) : QString::null;
+ }
+
+ static QStringList properties()
+ {
+ static QStringList l;
+
+ if(l.isEmpty()) {
+ QMap<QCString, const Property *>::ConstIterator it = propertyMap.begin();
+ for(; it != propertyMap.end(); ++it)
+ l.append(QString(it.key()));
+ }
+ return l;
+ }
+}
+
+#endif
diff --git a/juk/filerenamer.cpp b/juk/filerenamer.cpp
new file mode 100644
index 00000000..ec45a268
--- /dev/null
+++ b/juk/filerenamer.cpp
@@ -0,0 +1,1047 @@
+/***************************************************************************
+ begin : Thu Oct 28 2004
+ copyright : (C) 2004 by Michael Pyne
+ : (c) 2003 Frerich Raabe <raabe@kde.org>
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <algorithm>
+
+#include <kdebug.h>
+#include <kcombobox.h>
+#include <kurl.h>
+#include <kurlrequester.h>
+#include <kiconloader.h>
+#include <knuminput.h>
+#include <kstandarddirs.h>
+#include <kio/netaccess.h>
+#include <kconfigbase.h>
+#include <kconfig.h>
+#include <kglobal.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <kpushbutton.h>
+#include <kapplication.h>
+#include <kmessagebox.h>
+#include <ksimpleconfig.h>
+
+#include <qfile.h>
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qscrollview.h>
+#include <qobjectlist.h>
+#include <qtimer.h>
+#include <qregexp.h>
+#include <qcheckbox.h>
+#include <qdir.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qsignalmapper.h>
+#include <qheader.h>
+
+#include "tag.h"
+#include "filehandle.h"
+#include "filerenamer.h"
+#include "exampleoptions.h"
+#include "playlistitem.h"
+#include "playlist.h"
+#include "coverinfo.h"
+
+class ConfirmationDialog : public KDialogBase
+{
+public:
+ ConfirmationDialog(const QMap<QString, QString> &files,
+ QWidget *parent = 0, const char *name = 0)
+ : KDialogBase(parent, name, true, i18n("Warning"), Ok | Cancel)
+ {
+ QVBox *vbox = makeVBoxMainWidget();
+ QHBox *hbox = new QHBox(vbox);
+
+ QLabel *l = new QLabel(hbox);
+ l->setPixmap(SmallIcon("messagebox_warning", 32));
+
+ l = new QLabel(i18n("You are about to rename the following files. "
+ "Are you sure you want to continue?"), hbox);
+ hbox->setStretchFactor(l, 1);
+
+ KListView *lv = new KListView(vbox);
+
+ lv->addColumn(i18n("Original Name"));
+ lv->addColumn(i18n("New Name"));
+
+ int lvHeight = 0;
+
+ QMap<QString, QString>::ConstIterator it = files.begin();
+ for(; it != files.end(); ++it) {
+ KListViewItem *i = it.key() != it.data()
+ ? new KListViewItem(lv, it.key(), it.data())
+ : new KListViewItem(lv, it.key(), i18n("No Change"));
+ lvHeight += i->height();
+ }
+
+ lvHeight += lv->horizontalScrollBar()->height() + lv->header()->height();
+ lv->setMinimumHeight(QMIN(lvHeight, 400));
+ resize(QMIN(width(), 500), QMIN(minimumHeight(), 400));
+ }
+};
+
+//
+// Implementation of ConfigCategoryReader
+//
+
+ConfigCategoryReader::ConfigCategoryReader() : CategoryReaderInterface(),
+ m_currentItem(0)
+{
+ KConfigGroup config(KGlobal::config(), "FileRenamer");
+
+ QValueList<int> categoryOrder = config.readIntListEntry("CategoryOrder");
+ unsigned categoryCount[NumTypes] = { 0 }; // Keep track of each category encountered.
+
+ // Set a default:
+
+ if(categoryOrder.isEmpty())
+ categoryOrder << Artist << Album << Title << Track;
+
+ QValueList<int>::ConstIterator catIt = categoryOrder.constBegin();
+ for(; catIt != categoryOrder.constEnd(); ++catIt)
+ {
+ unsigned catCount = categoryCount[*catIt]++;
+ TagType category = static_cast<TagType>(*catIt);
+ CategoryID catId(category, catCount);
+
+ m_options[catId] = TagRenamerOptions(catId);
+ m_categoryOrder << catId;
+ }
+
+ m_folderSeparators.resize(m_categoryOrder.count() - 1, false);
+
+ QValueList<int> checkedSeparators = config.readIntListEntry("CheckedDirSeparators");
+
+ QValueList<int>::ConstIterator it = checkedSeparators.constBegin();
+ for(; it != checkedSeparators.constEnd(); ++it) {
+ unsigned index = static_cast<unsigned>(*it);
+ if(index < m_folderSeparators.count())
+ m_folderSeparators[index] = true;
+ }
+
+ m_musicFolder = config.readPathEntry("MusicFolder", "${HOME}/music");
+ m_separator = config.readEntry("Separator", " - ");
+}
+
+QString ConfigCategoryReader::categoryValue(TagType type) const
+{
+ if(!m_currentItem)
+ return QString::null;
+
+ Tag *tag = m_currentItem->file().tag();
+
+ switch(type) {
+ case Track:
+ return QString::number(tag->track());
+
+ case Year:
+ return QString::number(tag->year());
+
+ case Title:
+ return tag->title();
+
+ case Artist:
+ return tag->artist();
+
+ case Album:
+ return tag->album();
+
+ case Genre:
+ return tag->genre();
+
+ default:
+ return QString::null;
+ }
+}
+
+QString ConfigCategoryReader::prefix(const CategoryID &category) const
+{
+ return m_options[category].prefix();
+}
+
+QString ConfigCategoryReader::suffix(const CategoryID &category) const
+{
+ return m_options[category].suffix();
+}
+
+TagRenamerOptions::EmptyActions ConfigCategoryReader::emptyAction(const CategoryID &category) const
+{
+ return m_options[category].emptyAction();
+}
+
+QString ConfigCategoryReader::emptyText(const CategoryID &category) const
+{
+ return m_options[category].emptyText();
+}
+
+QValueList<CategoryID> ConfigCategoryReader::categoryOrder() const
+{
+ return m_categoryOrder;
+}
+
+QString ConfigCategoryReader::separator() const
+{
+ return m_separator;
+}
+
+QString ConfigCategoryReader::musicFolder() const
+{
+ return m_musicFolder;
+}
+
+int ConfigCategoryReader::trackWidth(unsigned categoryNum) const
+{
+ return m_options[CategoryID(Track, categoryNum)].trackWidth();
+}
+
+bool ConfigCategoryReader::hasFolderSeparator(unsigned index) const
+{
+ if(index >= m_folderSeparators.count())
+ return false;
+ return m_folderSeparators[index];
+}
+
+bool ConfigCategoryReader::isDisabled(const CategoryID &category) const
+{
+ return m_options[category].disabled();
+}
+
+//
+// Implementation of FileRenamerWidget
+//
+
+FileRenamerWidget::FileRenamerWidget(QWidget *parent) :
+ FileRenamerBase(parent), CategoryReaderInterface(),
+ m_exampleFromFile(false)
+{
+ QLabel *temp = new QLabel(0);
+ m_exampleText->setPaletteBackgroundColor(temp->paletteBackgroundColor());
+ delete temp;
+
+ layout()->setMargin(0); // We'll be wrapped by KDialogBase
+
+ // This must be created before createTagRows() is called.
+
+ m_exampleDialog = new ExampleOptionsDialog(this);
+
+ createTagRows();
+ loadConfig();
+
+ // Add correct text to combo box.
+ m_category->clear();
+ for(unsigned i = StartTag; i < NumTypes; ++i) {
+ QString category = TagRenamerOptions::tagTypeText(static_cast<TagType>(i));
+ m_category->insertItem(category);
+ }
+
+ connect(m_exampleDialog, SIGNAL(signalShown()), SLOT(exampleDialogShown()));
+ connect(m_exampleDialog, SIGNAL(signalHidden()), SLOT(exampleDialogHidden()));
+ connect(m_exampleDialog, SIGNAL(dataChanged()), SLOT(dataSelected()));
+ connect(m_exampleDialog, SIGNAL(fileChanged(const QString &)),
+ this, SLOT(fileSelected(const QString &)));
+
+ exampleTextChanged();
+}
+
+void FileRenamerWidget::loadConfig()
+{
+ QValueList<int> checkedSeparators;
+ KConfigGroup config(KGlobal::config(), "FileRenamer");
+
+ for(unsigned i = 0; i < m_rows.count(); ++i)
+ m_rows[i].options = TagRenamerOptions(m_rows[i].category);
+
+ checkedSeparators = config.readIntListEntry("CheckedDirSeparators");
+
+ QValueList<int>::ConstIterator it = checkedSeparators.begin();
+ for(; it != checkedSeparators.end(); ++it) {
+ unsigned separator = static_cast<unsigned>(*it);
+ if(separator < m_folderSwitches.count())
+ m_folderSwitches[separator]->setChecked(true);
+ }
+
+ QString url = config.readPathEntry("MusicFolder", "${HOME}/music");
+ m_musicFolder->setURL(url);
+
+ m_separator->setCurrentText(config.readEntry("Separator", " - "));
+}
+
+void FileRenamerWidget::saveConfig()
+{
+ KConfigGroup config(KGlobal::config(), "FileRenamer");
+ QValueList<int> checkedSeparators;
+ QValueList<int> categoryOrder;
+
+ for(unsigned i = 0; i < m_rows.count(); ++i) {
+ unsigned rowId = idOfPosition(i); // Write out in GUI order, not m_rows order
+ m_rows[rowId].options.saveConfig(m_rows[rowId].category.categoryNumber);
+ categoryOrder += m_rows[rowId].category.category;
+ }
+
+ for(unsigned i = 0; i < m_folderSwitches.count(); ++i)
+ if(m_folderSwitches[i]->isChecked() == true)
+ checkedSeparators += i;
+
+ config.writeEntry("CheckedDirSeparators", checkedSeparators);
+ config.writeEntry("CategoryOrder", categoryOrder);
+ config.writePathEntry("MusicFolder", m_musicFolder->url());
+ config.writeEntry("Separator", m_separator->currentText());
+
+ config.sync();
+}
+
+FileRenamerWidget::~FileRenamerWidget()
+{
+}
+
+unsigned FileRenamerWidget::addRowCategory(TagType category)
+{
+ static QPixmap up = SmallIcon("up");
+ static QPixmap down = SmallIcon("down");
+
+ // Find number of categories already of this type.
+ unsigned categoryCount = 0;
+ for(unsigned i = 0; i < m_rows.count(); ++i)
+ if(m_rows[i].category.category == category)
+ ++categoryCount;
+
+ Row row;
+
+ row.category = CategoryID(category, categoryCount);
+ row.position = m_rows.count();
+ unsigned id = row.position;
+
+ QHBox *frame = new QHBox(m_mainFrame);
+ frame->setPaletteBackgroundColor(frame->paletteBackgroundColor().dark(110));
+
+ row.widget = frame;
+ frame->setFrameShape(QFrame::Box);
+ frame->setLineWidth(1);
+ frame->setMargin(3);
+
+ m_mainFrame->setStretchFactor(frame, 1);
+
+ QVBox *buttons = new QVBox(frame);
+ buttons->setFrameStyle(QFrame::Plain | QFrame::Box);
+ buttons->setLineWidth(1);
+
+ row.upButton = new KPushButton(buttons);
+ row.downButton = new KPushButton(buttons);
+
+ row.upButton->setPixmap(up);
+ row.downButton->setPixmap(down);
+ row.upButton->setFlat(true);
+ row.downButton->setFlat(true);
+
+ upMapper->connect(row.upButton, SIGNAL(clicked()), SLOT(map()));
+ upMapper->setMapping(row.upButton, id);
+ downMapper->connect(row.downButton, SIGNAL(clicked()), SLOT(map()));
+ downMapper->setMapping(row.downButton, id);
+
+ QString labelText = QString("<b>%1</b>").arg(TagRenamerOptions::tagTypeText(category));
+ QLabel *label = new QLabel(labelText, frame);
+ frame->setStretchFactor(label, 1);
+ label->setAlignment(AlignCenter);
+
+ QVBox *options = new QVBox(frame);
+ row.enableButton = new KPushButton(i18n("Remove"), options);
+ toggleMapper->connect(row.enableButton, SIGNAL(clicked()), SLOT(map()));
+ toggleMapper->setMapping(row.enableButton, id);
+
+ row.optionsButton = new KPushButton(i18n("Options"), options);
+ mapper->connect(row.optionsButton, SIGNAL(clicked()), SLOT(map()));
+ mapper->setMapping(row.optionsButton, id);
+
+ row.widget->show();
+ m_rows.append(row);
+
+ // Disable add button if there's too many rows.
+ if(m_rows.count() == MAX_CATEGORIES)
+ m_insertCategory->setEnabled(false);
+
+ return id;
+}
+
+void FileRenamerWidget::moveSignalMappings(unsigned oldId, unsigned newId)
+{
+ mapper->setMapping(m_rows[oldId].optionsButton, newId);
+ downMapper->setMapping(m_rows[oldId].downButton, newId);
+ upMapper->setMapping(m_rows[oldId].upButton, newId);
+ toggleMapper->setMapping(m_rows[oldId].enableButton, newId);
+}
+
+bool FileRenamerWidget::removeRow(unsigned id)
+{
+ if(id >= m_rows.count()) {
+ kdWarning(65432) << "Trying to remove row, but " << id << " is out-of-range.\n";
+ return false;
+ }
+
+ if(m_rows.count() == 1) {
+ kdError(65432) << "Can't remove last row of File Renamer.\n";
+ return false;
+ }
+
+ // Remove widget. Don't delete it since it appears QSignalMapper may still need it.
+ m_rows[id].widget->deleteLater();
+ m_rows[id].widget = 0;
+ m_rows[id].enableButton = 0;
+ m_rows[id].upButton = 0;
+ m_rows[id].optionsButton = 0;
+ m_rows[id].downButton = 0;
+
+ unsigned checkboxPosition = 0; // Remove first checkbox.
+
+ // If not the first row, remove the checkbox before it.
+ if(m_rows[id].position > 0)
+ checkboxPosition = m_rows[id].position - 1;
+
+ // The checkbox is contained within a layout widget, so the layout
+ // widget is the one the needs to die.
+ delete m_folderSwitches[checkboxPosition]->parent();
+ m_folderSwitches.erase(&m_folderSwitches[checkboxPosition]);
+
+ // Go through all the rows and if they have the same category and a
+ // higher categoryNumber, decrement the number. Also update the
+ // position identifier.
+ for(unsigned i = 0; i < m_rows.count(); ++i) {
+ if(i == id)
+ continue; // Don't mess with ourself.
+
+ if((m_rows[id].category.category == m_rows[i].category.category) &&
+ (m_rows[id].category.categoryNumber < m_rows[i].category.categoryNumber))
+ {
+ --m_rows[i].category.categoryNumber;
+ }
+
+ // Items are moving up.
+ if(m_rows[id].position < m_rows[i].position)
+ --m_rows[i].position;
+ }
+
+ // Every row after the one we delete will have a different identifier, since
+ // the identifier is simply its index into m_rows. So we need to re-do the
+ // signal mappings for the affected rows.
+ for(unsigned i = id + 1; i < m_rows.count(); ++i)
+ moveSignalMappings(i, i - 1);
+
+ m_rows.erase(&m_rows[id]);
+
+ // Make sure we update the buttons of affected rows.
+ m_rows[idOfPosition(0)].upButton->setEnabled(false);
+ m_rows[idOfPosition(m_rows.count() - 1)].downButton->setEnabled(false);
+
+ // We can insert another row now, make sure GUI is updated to match.
+ m_insertCategory->setEnabled(true);
+
+ QTimer::singleShot(0, this, SLOT(exampleTextChanged()));
+ return true;
+}
+
+void FileRenamerWidget::addFolderSeparatorCheckbox()
+{
+ QWidget *temp = new QWidget(m_mainFrame);
+ QHBoxLayout *l = new QHBoxLayout(temp);
+
+ QCheckBox *cb = new QCheckBox(i18n("Insert folder separator"), temp);
+ m_folderSwitches.append(cb);
+ l->addWidget(cb, 0, AlignCenter);
+ cb->setChecked(false);
+
+ connect(cb, SIGNAL(toggled(bool)),
+ SLOT(exampleTextChanged()));
+
+ temp->show();
+}
+
+void FileRenamerWidget::createTagRows()
+{
+ KConfigGroup config(KGlobal::config(), "FileRenamer");
+ QValueList<int> categoryOrder = config.readIntListEntry("CategoryOrder");
+
+ if(categoryOrder.isEmpty())
+ categoryOrder << Artist << Album << Artist << Title << Track;
+
+ // Setup arrays.
+ m_rows.reserve(categoryOrder.count());
+ m_folderSwitches.reserve(categoryOrder.count() - 1);
+
+ mapper = new QSignalMapper(this, "signal mapper");
+ toggleMapper = new QSignalMapper(this, "toggle mapper");
+ upMapper = new QSignalMapper(this, "up button mapper");
+ downMapper = new QSignalMapper(this, "down button mapper");
+
+ connect(mapper, SIGNAL(mapped(int)), SLOT(showCategoryOption(int)));
+ connect(toggleMapper, SIGNAL(mapped(int)), SLOT(slotRemoveRow(int)));
+ connect(upMapper, SIGNAL(mapped(int)), SLOT(moveItemUp(int)));
+ connect(downMapper, SIGNAL(mapped(int)), SLOT(moveItemDown(int)));
+
+ m_mainFrame = new QVBox(m_mainView->viewport());
+ m_mainFrame->setMargin(10);
+ m_mainFrame->setSpacing(5);
+
+ m_mainView->addChild(m_mainFrame);
+ m_mainView->setResizePolicy(QScrollView::AutoOneFit);
+
+ // OK, the deal with the categoryOrder variable is that we need to create
+ // the rows in the order that they were saved in (the order given by categoryOrder).
+ // The signal mappers operate according to the row identifier. To find the position of
+ // a row given the identifier, use m_rows[id].position. To find the id of a given
+ // position, use idOfPosition(position).
+
+ QValueList<int>::ConstIterator it = categoryOrder.constBegin();
+
+ for(; it != categoryOrder.constEnd(); ++it) {
+ if(*it < StartTag || *it >= NumTypes) {
+ kdError(65432) << "Invalid category encountered in file renamer configuration.\n";
+ continue;
+ }
+
+ if(m_rows.count() == MAX_CATEGORIES) {
+ kdError(65432) << "Maximum number of File Renamer tags reached, bailing.\n";
+ break;
+ }
+
+ TagType i = static_cast<TagType>(*it);
+
+ addRowCategory(i);
+
+ // Insert the directory separator checkbox if this isn't the last
+ // item.
+
+ QValueList<int>::ConstIterator dup(it);
+
+ // Check for last item
+ if(++dup != categoryOrder.constEnd())
+ addFolderSeparatorCheckbox();
+ }
+
+ m_rows.first().upButton->setEnabled(false);
+ m_rows.last().downButton->setEnabled(false);
+
+ // If we have maximum number of categories already, don't let the user
+ // add more.
+ if(m_rows.count() >= MAX_CATEGORIES)
+ m_insertCategory->setEnabled(false);
+}
+
+void FileRenamerWidget::exampleTextChanged()
+{
+ // Just use .mp3 as an example
+
+ if(m_exampleFromFile && (m_exampleFile.isEmpty() ||
+ !FileHandle(m_exampleFile).tag()->isValid()))
+ {
+ m_exampleText->setText(i18n("No file selected, or selected file has no tags."));
+ return;
+ }
+
+ m_exampleText->setText(FileRenamer::fileName(*this) + ".mp3");
+}
+
+QString FileRenamerWidget::fileCategoryValue(TagType category) const
+{
+ FileHandle file(m_exampleFile);
+ Tag *tag = file.tag();
+
+ switch(category) {
+ case Track:
+ return QString::number(tag->track());
+
+ case Year:
+ return QString::number(tag->year());
+
+ case Title:
+ return tag->title();
+
+ case Artist:
+ return tag->artist();
+
+ case Album:
+ return tag->album();
+
+ case Genre:
+ return tag->genre();
+
+ default:
+ return QString::null;
+ }
+}
+
+QString FileRenamerWidget::categoryValue(TagType category) const
+{
+ if(m_exampleFromFile)
+ return fileCategoryValue(category);
+
+ const ExampleOptions *example = m_exampleDialog->widget();
+
+ switch (category) {
+ case Track:
+ return example->m_exampleTrack->text();
+
+ case Year:
+ return example->m_exampleYear->text();
+
+ case Title:
+ return example->m_exampleTitle->text();
+
+ case Artist:
+ return example->m_exampleArtist->text();
+
+ case Album:
+ return example->m_exampleAlbum->text();
+
+ case Genre:
+ return example->m_exampleGenre->text();
+
+ default:
+ return QString::null;
+ }
+}
+
+QValueList<CategoryID> FileRenamerWidget::categoryOrder() const
+{
+ QValueList<CategoryID> list;
+
+ // Iterate in GUI row order.
+ for(unsigned i = 0; i < m_rows.count(); ++i) {
+ unsigned rowId = idOfPosition(i);
+
+ list += m_rows[rowId].category;
+ }
+
+ return list;
+}
+
+bool FileRenamerWidget::hasFolderSeparator(unsigned index) const
+{
+ if(index >= m_folderSwitches.count())
+ return false;
+ return m_folderSwitches[index]->isChecked();
+}
+
+void FileRenamerWidget::moveItem(unsigned id, MovementDirection direction)
+{
+ QWidget *l = m_rows[id].widget;
+ unsigned bottom = m_rows.count() - 1;
+ unsigned pos = m_rows[id].position;
+ unsigned newPos = (direction == MoveUp) ? pos - 1 : pos + 1;
+
+ // Item we're moving can't go further down after this.
+
+ if((pos == (bottom - 1) && direction == MoveDown) ||
+ (pos == bottom && direction == MoveUp))
+ {
+ unsigned idBottomRow = idOfPosition(bottom);
+ unsigned idAboveBottomRow = idOfPosition(bottom - 1);
+
+ m_rows[idBottomRow].downButton->setEnabled(true);
+ m_rows[idAboveBottomRow].downButton->setEnabled(false);
+ }
+
+ // We're moving the top item, do some button switching.
+
+ if((pos == 0 && direction == MoveDown) || (pos == 1 && direction == MoveUp)) {
+ unsigned idTopItem = idOfPosition(0);
+ unsigned idBelowTopItem = idOfPosition(1);
+
+ m_rows[idTopItem].upButton->setEnabled(true);
+ m_rows[idBelowTopItem].upButton->setEnabled(false);
+ }
+
+ // This is the item we're swapping with.
+
+ unsigned idSwitchWith = idOfPosition(newPos);
+ QWidget *w = m_rows[idSwitchWith].widget;
+
+ // Update the table of widget rows.
+
+ std::swap(m_rows[id].position, m_rows[idSwitchWith].position);
+
+ // Move the item two spaces above/below its previous position. It has to
+ // be 2 spaces because of the checkbox.
+
+ QBoxLayout *layout = dynamic_cast<QBoxLayout *>(m_mainFrame->layout());
+
+ layout->remove(l);
+ layout->insertWidget(2 * newPos, l);
+
+ // Move the top item two spaces in the opposite direction, for a similar
+ // reason.
+
+ layout->remove(w);
+ layout->insertWidget(2 * pos, w);
+ layout->invalidate();
+
+ QTimer::singleShot(0, this, SLOT(exampleTextChanged()));
+}
+
+unsigned FileRenamerWidget::idOfPosition(unsigned position) const
+{
+ if(position >= m_rows.count()) {
+ kdError(65432) << "Search for position " << position << " out-of-range.\n";
+ return static_cast<unsigned>(-1);
+ }
+
+ for(unsigned i = 0; i < m_rows.count(); ++i)
+ if(m_rows[i].position == position)
+ return i;
+
+ kdError(65432) << "Unable to find identifier for position " << position << endl;
+ return static_cast<unsigned>(-1);
+}
+
+unsigned FileRenamerWidget::findIdentifier(const CategoryID &category) const
+{
+ for(unsigned index = 0; index < m_rows.count(); ++index)
+ if(m_rows[index].category == category)
+ return index;
+
+ kdError(65432) << "Unable to find match for category " <<
+ TagRenamerOptions::tagTypeText(category.category) <<
+ ", number " << category.categoryNumber << endl;
+
+ return MAX_CATEGORIES;
+}
+
+void FileRenamerWidget::enableAllUpButtons()
+{
+ for(unsigned i = 0; i < m_rows.count(); ++i)
+ m_rows[i].upButton->setEnabled(true);
+}
+
+void FileRenamerWidget::enableAllDownButtons()
+{
+ for(unsigned i = 0; i < m_rows.count(); ++i)
+ m_rows[i].downButton->setEnabled(true);
+}
+
+void FileRenamerWidget::showCategoryOption(int id)
+{
+ TagOptionsDialog *dialog = new TagOptionsDialog(this, m_rows[id].options, m_rows[id].category.categoryNumber);
+
+ if(dialog->exec() == QDialog::Accepted) {
+ m_rows[id].options = dialog->options();
+ exampleTextChanged();
+ }
+
+ delete dialog;
+}
+
+void FileRenamerWidget::moveItemUp(int id)
+{
+ moveItem(static_cast<unsigned>(id), MoveUp);
+}
+
+void FileRenamerWidget::moveItemDown(int id)
+{
+ moveItem(static_cast<unsigned>(id), MoveDown);
+}
+
+void FileRenamerWidget::toggleExampleDialog()
+{
+ m_exampleDialog->setShown(!m_exampleDialog->isShown());
+}
+
+void FileRenamerWidget::insertCategory()
+{
+ TagType category = TagRenamerOptions::tagFromCategoryText(m_category->currentText());
+ if(category == Unknown) {
+ kdError(65432) << "Trying to add unknown category somehow.\n";
+ return;
+ }
+
+ // We need to enable the down button of the current bottom row since it
+ // can now move down.
+ unsigned idBottom = idOfPosition(m_rows.count() - 1);
+ m_rows[idBottom].downButton->setEnabled(true);
+
+ addFolderSeparatorCheckbox();
+
+ // Identifier of new row.
+ unsigned id = addRowCategory(category);
+
+ // Set its down button to be disabled.
+ m_rows[id].downButton->setEnabled(false);
+
+ m_mainFrame->layout()->invalidate();
+ m_mainView->update();
+
+ // Now update according to the code in loadConfig().
+ m_rows[id].options = TagRenamerOptions(m_rows[id].category);
+ exampleTextChanged();
+}
+
+void FileRenamerWidget::exampleDialogShown()
+{
+ m_showExample->setText(i18n("Hide Renamer Test Dialog"));
+}
+
+void FileRenamerWidget::exampleDialogHidden()
+{
+ m_showExample->setText(i18n("Show Renamer Test Dialog"));
+}
+
+void FileRenamerWidget::fileSelected(const QString &file)
+{
+ m_exampleFromFile = true;
+ m_exampleFile = file;
+ exampleTextChanged();
+}
+
+void FileRenamerWidget::dataSelected()
+{
+ m_exampleFromFile = false;
+ exampleTextChanged();
+}
+
+QString FileRenamerWidget::separator() const
+{
+ return m_separator->currentText();
+}
+
+QString FileRenamerWidget::musicFolder() const
+{
+ return m_musicFolder->url();
+}
+
+void FileRenamerWidget::slotRemoveRow(int id)
+{
+ // Remove the given identified row.
+ if(!removeRow(id))
+ kdError(65432) << "Unable to remove row " << id << endl;
+}
+
+//
+// Implementation of FileRenamer
+//
+
+FileRenamer::FileRenamer()
+{
+}
+
+void FileRenamer::rename(PlaylistItem *item)
+{
+ PlaylistItemList list;
+ list.append(item);
+
+ rename(list);
+}
+
+void FileRenamer::rename(const PlaylistItemList &items)
+{
+ ConfigCategoryReader reader;
+ QStringList errorFiles;
+ QMap<QString, QString> map;
+ QMap<QString, PlaylistItem *> itemMap;
+
+ for(PlaylistItemList::ConstIterator it = items.begin(); it != items.end(); ++it) {
+ reader.setPlaylistItem(*it);
+ QString oldFile = (*it)->file().absFilePath();
+ QString extension = (*it)->file().fileInfo().extension(false);
+ QString newFile = fileName(reader) + "." + extension;
+
+ if(oldFile != newFile) {
+ map[oldFile] = newFile;
+ itemMap[oldFile] = *it;
+ }
+ }
+
+ if(itemMap.isEmpty() || ConfirmationDialog(map).exec() != QDialog::Accepted)
+ return;
+
+ KApplication::setOverrideCursor(Qt::waitCursor);
+ for(QMap<QString, QString>::ConstIterator it = map.begin();
+ it != map.end(); ++it)
+ {
+ if(moveFile(it.key(), it.data())) {
+ itemMap[it.key()]->setFile(it.data());
+ itemMap[it.key()]->refresh();
+
+ setFolderIcon(it.data(), itemMap[it.key()]);
+ }
+ else
+ errorFiles << i18n("%1 to %2").arg(it.key()).arg(it.data());
+
+ processEvents();
+ }
+ KApplication::restoreOverrideCursor();
+
+ if(!errorFiles.isEmpty())
+ KMessageBox::errorList(0, i18n("The following rename operations failed:\n"), errorFiles);
+}
+
+bool FileRenamer::moveFile(const QString &src, const QString &dest)
+{
+ kdDebug(65432) << "Moving file " << src << " to " << dest << endl;
+
+ if(src == dest)
+ return false;
+
+ // Escape URL.
+ KURL srcURL = KURL::fromPathOrURL(src);
+ KURL dstURL = KURL::fromPathOrURL(dest);
+
+ // Clean it.
+ srcURL.cleanPath();
+ dstURL.cleanPath();
+
+ // Make sure it is valid.
+ if(!srcURL.isValid() || !dstURL.isValid())
+ return false;
+
+ // Get just the directory.
+ KURL dir = dstURL;
+ dir.setFileName(QString::null);
+
+ // Create the directory.
+ if(!KStandardDirs::exists(dir.path()))
+ if(!KStandardDirs::makeDir(dir.path())) {
+ kdError() << "Unable to create directory " << dir.path() << endl;
+ return false;
+ }
+
+ // Move the file.
+ return KIO::NetAccess::file_move(srcURL, dstURL);
+}
+
+void FileRenamer::setFolderIcon(const KURL &dst, const PlaylistItem *item)
+{
+ if(item->file().tag()->album().isEmpty() ||
+ !item->file().coverInfo()->hasCover())
+ {
+ return;
+ }
+
+ KURL dstURL = dst;
+ dstURL.cleanPath();
+
+ // Split path, and go through each path element. If a path element has
+ // the album information, set its folder icon.
+ QStringList elements = QStringList::split("/", dstURL.directory());
+ QString path;
+
+ for(QStringList::ConstIterator it = elements.begin(); it != elements.end(); ++it) {
+ path.append("/" + (*it));
+
+ kdDebug() << "Checking path: " << path << endl;
+ if((*it).find(item->file().tag()->album()) != -1 &&
+ !QFile::exists(path + "/.directory"))
+ {
+ // Seems to be a match, let's set the folder icon for the current
+ // path. First we should write out the file.
+
+ QPixmap thumb = item->file().coverInfo()->pixmap(CoverInfo::Thumbnail);
+ thumb.save(path + "/.juk-thumbnail.png", "PNG");
+
+ KSimpleConfig config(path + "/.directory");
+ config.setGroup("Desktop Entry");
+
+ if(!config.hasKey("Icon")) {
+ config.writeEntry("Icon", QString("%1/.juk-thumbnail.png").arg(path));
+ config.sync();
+ }
+
+ return;
+ }
+ }
+}
+
+/**
+ * Returns iterator pointing to the last item enabled in the given list with
+ * a non-empty value (or is required to be included).
+ */
+QValueList<CategoryID>::ConstIterator lastEnabledItem(const QValueList<CategoryID> &list,
+ const CategoryReaderInterface &interface)
+{
+ QValueList<CategoryID>::ConstIterator it = list.constBegin();
+ QValueList<CategoryID>::ConstIterator last = list.constEnd();
+
+ for(; it != list.constEnd(); ++it) {
+ if(interface.isRequired(*it) || (!interface.isDisabled(*it) &&
+ !interface.categoryValue((*it).category).isEmpty()))
+ {
+ last = it;
+ }
+ }
+
+ return last;
+}
+
+QString FileRenamer::fileName(const CategoryReaderInterface &interface)
+{
+ const QValueList<CategoryID> categoryOrder = interface.categoryOrder();
+ const QString separator = interface.separator();
+ const QString folder = interface.musicFolder();
+ QValueList<CategoryID>::ConstIterator lastEnabled;
+ unsigned i = 0;
+ QStringList list;
+ QChar dirSeparator = QChar(QDir::separator());
+
+ // Use lastEnabled to properly handle folder separators.
+ lastEnabled = lastEnabledItem(categoryOrder, interface);
+ bool pastLast = false; // Toggles to true once we've passed lastEnabled.
+
+ for(QValueList<CategoryID>::ConstIterator it = categoryOrder.begin();
+ it != categoryOrder.end();
+ ++it, ++i)
+ {
+ if(it == lastEnabled)
+ pastLast = true;
+
+ if(interface.isDisabled(*it))
+ continue;
+
+ QString value = interface.value(*it);
+
+ // The user can use the folder separator checkbox to add folders, so don't allow
+ // slashes that slip in to accidentally create new folders. Should we filter this
+ // back out when showing it in the GUI?
+ value.replace('/', "%2f");
+
+ if(!pastLast && interface.hasFolderSeparator(i))
+ value.append(dirSeparator);
+
+ if(interface.isRequired(*it) || !value.isEmpty())
+ list.append(value);
+ }
+
+ // Construct a single string representation, handling strings ending in
+ // '/' specially
+
+ QString result;
+
+ for(QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); /* Empty */) {
+ result += *it;
+
+ ++it; // Manually advance iterator to check for end-of-list.
+
+ // Add separator unless at a directory boundary
+ if(it != list.constEnd() &&
+ !(*it).startsWith(dirSeparator) && // Check beginning of next item.
+ !result.endsWith(dirSeparator))
+ {
+ result += separator;
+ }
+ }
+
+ return QString(folder + dirSeparator + result);
+}
+
+#include "filerenamer.moc"
+
+// vim: set et sw=4 ts=8:
diff --git a/juk/filerenamer.h b/juk/filerenamer.h
new file mode 100644
index 00000000..0097ed86
--- /dev/null
+++ b/juk/filerenamer.h
@@ -0,0 +1,543 @@
+/***************************************************************************
+ begin : Thu Oct 28 2004
+ copyright : (C) 2004 by Michael Pyne
+ : (C) 2003 Frerich Raabe <raabe@kde.org>
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef JUK_FILERENAMER_H
+#define JUK_FILERENAMER_H
+
+#include <qstring.h>
+#include <qvaluevector.h>
+#include <qmap.h>
+
+#include "filerenamerbase.h"
+#include "filerenameroptions.h"
+#include "categoryreaderinterface.h"
+#include "tagrenameroptions.h"
+#include "playlistitem.h"
+
+class ExampleOptionsDialog;
+class QCheckBox;
+class QLayout;
+class QLayoutItem;
+class QPushButton;
+class QVBox;
+class PlaylistItem;
+class QSignalMapper;
+
+// Used to decide what direction the FileRenamerWidget will move rows in.
+enum MovementDirection { MoveUp, MoveDown };
+
+/**
+ * This is used by FileRenamerWidget to store information about a particular
+ * tag type, including its position, the QFrame holding the information,
+ * the up, down, and enable buttons, and the user-selected renaming options.
+ */
+struct Row
+{
+ Row() : widget(0), upButton(0), downButton(0), enableButton(0) {}
+
+ QWidget *widget;
+
+ QPushButton *upButton, *downButton, *optionsButton, *enableButton;
+
+ TagRenamerOptions options;
+ CategoryID category; // Includes category and a disambiguation id.
+ unsigned position; ///< Position in the GUI (0 == top)
+ QString name;
+};
+
+/**
+ * A list of rows, each of which may have its own category options and other
+ * associated data. There is no relation between the order of rows in the vector and their
+ * GUI layout. Instead, each Row has a position member which indicates what GUI position it
+ * takes up. The index into the vector is known as the row identifier (which is unique but
+ * not necessarily constant).
+ */
+typedef QValueVector<Row> Rows;
+
+/**
+ * Holds a list directory separator checkboxes which separate a row. There
+ * should always be 1 less than the number of rows in the GUI.
+ *
+ * Used for ConfigCategoryReader.
+ */
+typedef QValueVector<QCheckBox *> DirSeparatorCheckBoxes;
+
+/**
+ * Associates a CategoryID combination with a set of options.
+ *
+ * Used for ConfigCategoryReader
+ */
+typedef QMap<CategoryID, TagRenamerOptions> CategoryOptionsMap;
+
+/**
+ * An implementation of CategoryReaderInterface that reads the user's settings
+ * from the global KConfig configuration object, and reads track information
+ * from whatever the given PlaylistItem is. You can assign different
+ * PlaylistItems in order to change the returned tag category information.
+ *
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ */
+class ConfigCategoryReader : public CategoryReaderInterface
+{
+public:
+ // ConfigCategoryReader specific members
+
+ ConfigCategoryReader();
+
+ const PlaylistItem *playlistItem() const { return m_currentItem; }
+ void setPlaylistItem(const PlaylistItem *item) { m_currentItem = item; }
+
+ // CategoryReaderInterface reimplementations
+
+ virtual QString categoryValue(TagType type) const;
+ virtual QString prefix(const CategoryID &category) const;
+ virtual QString suffix(const CategoryID &category) const;
+ virtual TagRenamerOptions::EmptyActions emptyAction(const CategoryID &category) const;
+ virtual QString emptyText(const CategoryID &category) const;
+ virtual QValueList<CategoryID> categoryOrder() const;
+ virtual QString separator() const;
+ virtual QString musicFolder() const;
+ virtual int trackWidth(unsigned categoryNum) const;
+ virtual bool hasFolderSeparator(unsigned index) const;
+ virtual bool isDisabled(const CategoryID &category) const;
+
+private:
+ const PlaylistItem *m_currentItem;
+ CategoryOptionsMap m_options;
+ QValueList<CategoryID> m_categoryOrder;
+ QString m_separator;
+ QString m_musicFolder;
+ QValueVector<bool> m_folderSeparators;
+};
+
+/**
+ * This class implements a dialog that allows the user to alter the behavior
+ * of the file renamer. It supports 6 different genre types at this point,
+ * and it shouldn't be too difficult to extend that in the future if needed.
+ * It allows the user to open an external dialog, which will let the user see
+ * an example of what their current options will look like, by either allowing
+ * the user to type in some sample information, or by loading a file and
+ * reading tags from there.
+ *
+ * It also implements the CategoryReaderInterface in order to implement the
+ * example filename functionality.
+ *
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ */
+class FileRenamerWidget : public FileRenamerBase, public CategoryReaderInterface
+{
+ Q_OBJECT
+
+public:
+ FileRenamerWidget(QWidget *parent);
+ ~FileRenamerWidget();
+
+ /// Maximum number of total categories the widget will allow.
+ static unsigned const MAX_CATEGORIES = 16;
+
+ /**
+ * This function saves all of the category options to the global KConfig
+ * object. You must call this manually, FileRenamerWidget doesn't call it
+ * automatically so that situations where the user hits "Cancel" work
+ * correctly.
+ */
+ void saveConfig();
+
+protected slots:
+ /**
+ * This function should be called whenever the example text may need to be
+ * changed. For example, when the user selects a different separator or
+ * changes the example text, this slot should be called.
+ */
+ virtual void exampleTextChanged();
+
+ /**
+ * This function shows the example dialog if it is hidden, and hides the
+ * example dialog if it is shown.
+ */
+ virtual void toggleExampleDialog();
+
+ /**
+ * This function inserts the currently selected category, so that the
+ * user can use duplicate tags in the file renamer.
+ */
+ virtual void insertCategory();
+
+private:
+ /**
+ * This function initializes the category options by loading the data from
+ * the global KConfig object. This is called automatically in the constructor.
+ */
+ void loadConfig();
+
+ /**
+ * This function adds a "Insert Folder separator" checkbox to the end of
+ * the current layout. The setting defaults to being unchecked.
+ */
+ void addFolderSeparatorCheckbox();
+
+ /**
+ * This function creates a row in the main view for category, appending it
+ * to the end. It handles connecting signals to the mapper and such as
+ * well.
+ *
+ * @param category Type of row to append.
+ * @return identifier of newly added row.
+ */
+ unsigned addRowCategory(TagType category);
+
+ /**
+ * Removes the given row, updating the other rows to have the correct
+ * number of categoryNumber.
+ *
+ * @param id The identifier of the row to remove.
+ * @return true if the delete succeeded, false otherwise.
+ */
+ bool removeRow(unsigned id);
+
+ /**
+ * Updates the mappings currently set for the row identified by oldId so
+ * that they emit newId instead. Does not actually delete the row given
+ * by oldId.
+ *
+ * @param oldId The identifier of the row to change mappings for.
+ * @param newId The identifier to use instead.
+ */
+ void moveSignalMappings(unsigned oldId, unsigned newId);
+
+ /**
+ * This function sets up the internal view by creating the checkboxes and
+ * the rows for each category.
+ */
+ void createTagRows();
+
+ /**
+ * Returns the value for \p category by retrieving the tag from m_exampleFile.
+ * If \p category is Track, then an appropriate fixup will be applied if needed
+ * to match the user's desired minimum width.
+ *
+ * @param category the category to retrieve the value for.
+ * @return the string representation of the value for \p category.
+ */
+ QString fileCategoryValue(TagType category) const;
+
+ /**
+ * Returns the value for \p category by reading the user entry for that
+ * category. If \p category is Track, then an appropriate fixup will be applied
+ * if needed to match the user's desired minimum width.
+ *
+ * @param category the category to retrieve the value for.
+ * @return the string representation of the value for \p category.
+ */
+ virtual QString categoryValue(TagType category) const;
+
+ /**
+ * Returns the user-specified prefix string for \p category.
+ *
+ * @param category the category to retrieve the value for.
+ * @return user-specified prefix string for \p category.
+ */
+ virtual QString prefix(const CategoryID &category) const
+ {
+ return m_rows[findIdentifier(category)].options.prefix();
+ }
+
+ /**
+ * Returns the user-specified suffix string for \p category.
+ *
+ * @param category the category to retrieve the value for.
+ * @return user-specified suffix string for \p category.
+ */
+ virtual QString suffix(const CategoryID &category) const
+ {
+ return m_rows[findIdentifier(category)].options.suffix();
+ }
+
+ /**
+ * Returns the user-specified empty action for \p category.
+ *
+ * @param category the category to retrieve the value for.
+ * @return user-specified empty action for \p category.
+ */
+ virtual TagRenamerOptions::EmptyActions emptyAction(const CategoryID &category) const
+ {
+ return m_rows[findIdentifier(category)].options.emptyAction();
+ }
+
+ /**
+ * Returns the user-specified empty text for \p category. This text might
+ * be used to replace an empty value.
+ *
+ * @param category the category to retrieve the value for.
+ * @return the user-specified empty text for \p category.
+ */
+ virtual QString emptyText(const CategoryID &category) const
+ {
+ return m_rows[findIdentifier(category)].options.emptyText();
+ }
+
+ /**
+ * @return list of CategoryIDs corresponding to the user-specified category order.
+ */
+ virtual QValueList<CategoryID> categoryOrder() const;
+
+ /**
+ * @return string that separates the tag values in the file name.
+ */
+ virtual QString separator() const;
+
+ /**
+ * @return local path to the music folder used to store renamed files.
+ */
+ virtual QString musicFolder() const;
+
+ /**
+ * @param categoryNum Zero-based number of category to get results for (if more than one).
+ * @return the minimum width of the track category.
+ */
+ virtual int trackWidth(unsigned categoryNum) const
+ {
+ CategoryID id(Track, categoryNum);
+ return m_rows[findIdentifier(id)].options.trackWidth();
+ }
+
+ /**
+ * @param index, the 0-based index for the folder boundary.
+ * @return true if there should be a folder separator between category
+ * index and index + 1, and false otherwise. Note that for purposes
+ * of this function, only categories that are required or non-empty
+ * should count.
+ */
+ virtual bool hasFolderSeparator(unsigned index) const;
+
+ /**
+ * @param category The category to get the status of.
+ * @return true if \p category is disabled by the user, and false otherwise.
+ */
+ virtual bool isDisabled(const CategoryID &category) const
+ {
+ return m_rows[findIdentifier(category)].options.disabled();
+ }
+
+ /**
+ * This moves the widget \p l in the direction given by \p direction, taking
+ * care to make sure that the checkboxes are not moved, and that they are
+ * enabled or disabled as appropriate for the new layout, and that the up and
+ * down buttons are also adjusted as necessary.
+ *
+ * @param id the identifier of the row to move
+ * @param direction the direction to move
+ */
+ void moveItem(unsigned id, MovementDirection direction);
+
+ /**
+ * This function actually performs the work of showing the options dialog for
+ * \p category.
+ *
+ * @param category the category to show the options dialog for.
+ */
+ void showCategoryOptions(TagType category);
+
+ /**
+ * This function enables or disables the widget in the row identified by \p id,
+ * controlled by \p enable. This function also makes sure that checkboxes are
+ * enabled or disabled as appropriate if they no longer make sense due to the
+ * adjacent category being enabled or disabled.
+ *
+ * @param id the identifier of the row to change. This is *not* the category to
+ * change.
+ * @param enable enables the category if true, disables if false.
+ */
+ void setCategoryEnabled(int id, bool enable);
+
+ /**
+ * This function enables all of the up buttons.
+ */
+ void enableAllUpButtons();
+
+ /**
+ * This function enables all of the down buttons.
+ */
+ void enableAllDownButtons();
+
+ /**
+ * This function returns the identifier of the row at \p position.
+ *
+ * @param position The position to find the identifier of.
+ * @return The unique id of the row at \p position.
+ */
+ unsigned idOfPosition(unsigned position) const;
+
+ /**
+ * This function returns the identifier of the row in the m_rows index that
+ * contains \p category and matches \p categoryNum.
+ *
+ * @param category the category to find.
+ * @return the identifier of the category, or MAX_CATEGORIES if it couldn't
+ * be found.
+ */
+ unsigned findIdentifier(const CategoryID &category) const;
+
+private slots:
+ /**
+ * This function reads the tags from \p file and ensures that the dialog will
+ * use those tags until a different file is selected or dataSelected() is
+ * called.
+ *
+ * @param file the path to the local file to read.
+ */
+ virtual void fileSelected(const QString &file);
+
+ /**
+ * This function reads the tags from the user-supplied examples and ensures
+ * that the dialog will use those tags until a file is selected using
+ * fileSelected().
+ */
+ virtual void dataSelected();
+
+ /**
+ * This function brings up a dialog that allows the user to edit the options
+ * for \p id.
+ *
+ * @param id the unique id to bring up the options for.
+ */
+ virtual void showCategoryOption(int id);
+
+ /**
+ * This function removes the row identified by id and updates the internal data to be
+ * consistent again, by forwarding the call to removeRow().
+ * This roundabout way is done due to QSignalMapper.
+ *
+ * @param id The unique id to update
+ */
+ virtual void slotRemoveRow(int id);
+
+ /**
+ * This function moves \p category up in the layout.
+ *
+ * @param id the unique id of the widget to move up.
+ */
+ virtual void moveItemUp(int id);
+
+ /**
+ * This function moves \p category down in the layout.
+ *
+ * @param id the unique id of the widget to move down.
+ */
+ virtual void moveItemDown(int id);
+
+ /**
+ * This slot should be called whenever the example input dialog is shown.
+ */
+ virtual void exampleDialogShown();
+
+ /**
+ * This slot should be called whever the example input dialog is hidden.
+ */
+ virtual void exampleDialogHidden();
+
+private:
+ /// This is the frame that holds all of the category widgets and checkboxes.
+ QVBox *m_mainFrame;
+
+ /**
+ * This is the meat of the widget, it holds the rows for the user configuration. It is
+ * initially created such that m_rows[0] is the top and row + 1 is the row just below.
+ * However, this is NOT NECESSARILY true, so don't rely on this. As soon as the user
+ * clicks an arrow to move a row then the order will be messed up. Use row.position to
+ * determine where the row is in the GUI.
+ *
+ * @see idOfPosition
+ * @see findIdentifier
+ */
+ Rows m_rows;
+
+ /**
+ * This holds an array of checkboxes that allow the user to insert folder
+ * separators in between categories.
+ */
+ DirSeparatorCheckBoxes m_folderSwitches;
+
+ ExampleOptionsDialog *m_exampleDialog;
+
+ /// This is true if we're reading example tags from m_exampleFile.
+ bool m_exampleFromFile;
+ QString m_exampleFile;
+
+ // Used to map signals from rows to the correct widget.
+ QSignalMapper *mapper;
+ QSignalMapper *toggleMapper;
+ QSignalMapper *upMapper;
+ QSignalMapper *downMapper;
+};
+
+/**
+ * This class contains the backend code to actually implement the file renaming. It performs
+ * the function of moving the files from one location to another, constructing the file name
+ * based off of the user's options (see ConfigCategoryReader) and of setting folder icons
+ * if appropriate.
+ *
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ */
+class FileRenamer
+{
+public:
+ FileRenamer();
+
+ /**
+ * Renames the filename on disk of the file represented by item according
+ * to the user configuration stored in KConfig.
+ *
+ * @param item The item to rename.
+ */
+ void rename(PlaylistItem *item);
+
+ /**
+ * Renames the filenames on disk of the files given in items according to
+ * the user configuration stored in KConfig.
+ *
+ * @param items The items to rename.
+ */
+ void rename(const PlaylistItemList &items);
+
+ /**
+ * Returns the file name that would be generated based on the options read from
+ * interface, which must implement CategoryReaderInterface. (A whole interface is used
+ * so that we can re-use the code to generate filenames from a in-memory GUI and from
+ * KConfig).
+ *
+ * @param interface object to read options/data from.
+ */
+ static QString fileName(const CategoryReaderInterface &interface);
+
+private:
+ /**
+ * Sets the folder icon for elements of the destination path for item (if
+ * there is not already a folder icon set, and if the folder's name has
+ * the album name.
+ */
+ void setFolderIcon(const KURL &dst, const PlaylistItem *item);
+
+ /**
+ * Attempts to rename the file from \a src to \a dest. Returns true if the
+ * operation succeeded.
+ */
+ bool moveFile(const QString &src, const QString &dest);
+};
+
+#endif /* JUK_FILERENAMER_H */
+
+// vim: set et sw=4 ts=8:
diff --git a/juk/filerenamerbase.ui b/juk/filerenamerbase.ui
new file mode 100644
index 00000000..28370971
--- /dev/null
+++ b/juk/filerenamerbase.ui
@@ -0,0 +1,379 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>FileRenamerBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>FileRenamerBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>489</width>
+ <height>579</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="caption">
+ <string>File Renamer Configuration</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KComboBox" row="1" column="1">
+ <item>
+ <property name="text">
+ <string> - </string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>_</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>-</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_separator</cstring>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1_6</cstring>
+ </property>
+ <property name="text">
+ <string>Music folder:</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="1">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>Album Tag</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Artist Tag</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Genre Tag</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Title Tag</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Track Tag</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Year Tag</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_category</cstring>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>m_insertCategory</cstring>
+ </property>
+ <property name="text">
+ <string>Insert Category</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="KURLRequester" row="0" column="1">
+ <property name="name">
+ <cstring>m_musicFolder</cstring>
+ </property>
+ <property name="url" stdset="0">
+ <string>/home/kde-cvs/music</string>
+ </property>
+ <property name="showLocalProtocol">
+ <bool>true</bool>
+ </property>
+ <property name="mode">
+ <number>26</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Add category:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_category</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel1_7</cstring>
+ </property>
+ <property name="text">
+ <string>Separator:</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="Line">
+ <property name="name">
+ <cstring>line1_2</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QScrollView">
+ <property name="name">
+ <cstring>m_mainView</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>1</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="Line">
+ <property name="name">
+ <cstring>line1</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Example</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>m_showExample</cstring>
+ </property>
+ <property name="text">
+ <string>Show Renamer Test Dialog</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer2_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>m_exampleText</cstring>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="acceptDrops">
+ <bool>false</bool>
+ </property>
+ <property name="lineWidth">
+ <number>2</number>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer7</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<customwidgets>
+ <customwidget>
+ <class>QScrollView</class>
+ <header location="global">qscrollview.h</header>
+ <sizehint>
+ <width>100</width>
+ <height>30</height>
+ </sizehint>
+ <container>1</container>
+ <sizepolicy>
+ <hordata>5</hordata>
+ <verdata>5</verdata>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ <pixmap>image0</pixmap>
+ </customwidget>
+</customwidgets>
+<images>
+ <image name="image0">
+ <data format="PNG" length="1002">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000003b149444154388dad945f4c5b551cc73fe7dc4b7b4bcba0762d45c43114323599ee6192609c51d883892ce083f1718b3ebb185f8dc91e972cf39d2d2a2f1af664b6f1e0fe3863a0718969700eb0c52142da0242a1bd6d696f7bcff101585203ceb8fd9ece39f99dcff9fe7edf939f88c562ec465f5f9fe609442c161362173c3e3eae7b7a7ac8e7f36432196cdbfe4f907c3e4f2291201e8fe338cec3737357e9e8e828aded1e229d650e1f2d51754b082110124c13a4dc5ea341eb9dc284c0558a853f3ce8cb0677ef500fde7d39d2596679e326597b8e9abb85d7a770ab16ab6983ec5a05b487a70e36f0f4e10afe408d6a558310980108478dba4a1e8233990c5d474b64ed39aa3a8fe5f3317fbf81dbd70bccfeb205947632fd74f6589c1c6ea2f70d03a58ba0c1f2c9bdc1b66de3b8256a6e11cbe7e3ee1d181b590124fe2693aeee08d223c82c3a2c24b7b874bec8f26288774f7bd054504aef0dde6e99c0eb83f9fb266323cb80a27fb0958141836044605a2ee5523393371cc646fee2da37195aa35d0c0c5b4859ac03d7e91712dcaac5adab3650a3ff9d08ef7dd8404bb48869e5d958b5b87dadc4c9a1464e9f0d0326df7ebd86bd2e310cb1bf62d384d59441f2d70a070e1c60e09489929b988681bdd9cc97170bcc4c65595f71f8e0e3301337fc24a7732467831875a47f289652b0be5e4151e6d07316c1b0c0340d8ab92023e76d66a6b2840e36d2fb7a13fee632475e6edc367ea98a90fb98b7dd6310ca0328a44761582e1bab41befabcc0ec940d28bc5e93b68e064cab84e1d9beaeb48934eac1f53b01c1b000fca496aa54b61a99fcde61662a4b4b4b23d1680be9d426173e4df3602a48ea411989a4fd590f52a8fd156b05ed9d350e3defe3cfdf4b4c7ce770ea7d3fb9f520afbe1620daeee5c26735d20b9b9cfb6811a754a439e4e5c5639a4caa1e5caf586bfc0197b78702005cb9b4cae4cd3267ce8638fe964bd72b393e39d74928d242617303a756a37f284447770dcdbffc6384a05a85de1306e9a52057c7527c7131c3c42d3f475eb2303c82d4fc3276d6811db37efeb148723082d9b08f79f97c1e5729109a9a28307cc622d2d6cdf52b2b24efe548dedb00142009862cfa879ee1a71f6cec928353511472fbf4389148b0b0e0c108081412458dfe21c9f11351e67e7358595468246d1d1e5e38a6e9e851bc39d84ab502a669331dafec0d8ec7e3e8cb06e1a881d727d1ae40180a434a8c9db129a54126ad48a7358c2b4c5352c8c374bcccdab2bb37d8719cba79fab8211f9df218e0582c261e95f8bfc04f1a1e8bc5c4dfe0a190172af6a9690000000049454e44ae426082</data>
+ </image>
+</images>
+<connections>
+ <connection>
+ <sender>m_separator</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>FileRenamerBase</receiver>
+ <slot>exampleTextChanged()</slot>
+ </connection>
+ <connection>
+ <sender>m_musicFolder</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>FileRenamerBase</receiver>
+ <slot>exampleTextChanged()</slot>
+ </connection>
+ <connection>
+ <sender>m_showExample</sender>
+ <signal>clicked()</signal>
+ <receiver>FileRenamerBase</receiver>
+ <slot>toggleExampleDialog()</slot>
+ </connection>
+ <connection>
+ <sender>m_insertCategory</sender>
+ <signal>clicked()</signal>
+ <receiver>FileRenamerBase</receiver>
+ <slot>insertCategory()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>m_musicFolder</tabstop>
+ <tabstop>m_separator</tabstop>
+ <tabstop>m_category</tabstop>
+ <tabstop>m_insertCategory</tabstop>
+ <tabstop>m_showExample</tabstop>
+</tabstops>
+<slots>
+ <slot access="protected">exampleTextChanged()</slot>
+ <slot access="protected">toggleExampleDialog()</slot>
+ <slot access="protected">insertCategory()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kcombobox.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>qscrollview.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/juk/filerenamerconfigdlg.cpp b/juk/filerenamerconfigdlg.cpp
new file mode 100644
index 00000000..74038dc6
--- /dev/null
+++ b/juk/filerenamerconfigdlg.cpp
@@ -0,0 +1,43 @@
+/***************************************************************************
+ begin : Mon Nov 01 2004
+ copyright : (C) 2004 by Michael Pyne
+ : (c) 2003 Frerich Raabe <raabe@kde.org>
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <klocale.h>
+
+#include "filerenamer.h"
+#include "filerenamerconfigdlg.h"
+
+FileRenamerConfigDlg::FileRenamerConfigDlg(QWidget *parent) :
+ KDialogBase(parent, "file renamer dialog", true,
+ i18n("File Renamer Options"), Ok | Cancel),
+ m_renamerWidget(new FileRenamerWidget(this))
+{
+ m_renamerWidget->setMinimumSize(400, 300);
+
+ setMainWidget(m_renamerWidget);
+}
+
+void FileRenamerConfigDlg::accept()
+{
+ // Make sure the config gets saved.
+
+ m_renamerWidget->saveConfig();
+
+ KDialogBase::accept();
+}
+
+#include "filerenamerconfigdlg.moc"
+
+// vim: set et sw=4 ts=4:
diff --git a/juk/filerenamerconfigdlg.h b/juk/filerenamerconfigdlg.h
new file mode 100644
index 00000000..0678f52b
--- /dev/null
+++ b/juk/filerenamerconfigdlg.h
@@ -0,0 +1,38 @@
+/***************************************************************************
+ begin : Mon Nov 01 2004
+ copyright : (C) 2004 by Michael Pyne
+ : (c) 2003 Frerich Raabe <raabe@kde.org>
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef JUK_FILERENAMERCONFIGDLG_H
+#define JUK_FILERENAMERCONFIGDLG_H
+
+
+class FileRenamerWidget;
+
+class FileRenamerConfigDlg : public KDialogBase
+{
+ Q_OBJECT
+ public:
+ FileRenamerConfigDlg(QWidget *parent);
+
+ protected slots:
+ virtual void accept();
+
+ private:
+ FileRenamerWidget *m_renamerWidget;
+};
+
+#endif // FILERENAMERCONFIGDLG_H
+
+// vim: set et ts=4 sw=4:
diff --git a/juk/filerenameroptions.cpp b/juk/filerenameroptions.cpp
new file mode 100644
index 00000000..2813be4b
--- /dev/null
+++ b/juk/filerenameroptions.cpp
@@ -0,0 +1,157 @@
+/***************************************************************************
+ begin : Thu Oct 28 2004
+ copyright : (C) 2004 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <knuminput.h>
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qradiobutton.h>
+#include <qlineedit.h>
+#include <qbuttongroup.h>
+
+#include "filerenameroptions.h"
+
+FileRenamerTagOptions::FileRenamerTagOptions(QWidget *parent,
+ const TagRenamerOptions &options) :
+ FileRenamerTagOptionsBase(parent), m_options(options)
+{
+ layout()->setSpacing(KDialog::spacingHint());
+ layout()->setMargin(0);
+
+ m_emptyTagGroup->layout()->setSpacing(KDialog::spacingHint());
+ m_trackGroup->layout()->setSpacing(KDialog::spacingHint());
+ m_emptyValueLayout->setSpacing(KDialog::spacingHint());
+ m_exampleLayout->setSpacing(KDialog::spacingHint());
+ m_spinLayout->setSpacing(KDialog::spacingHint());
+ m_widthLayout->setSpacing(KDialog::spacingHint());
+ m_tagLayout->setSpacing(KDialog::spacingHint());
+ m_tagFormatGroup->layout()->setSpacing(KDialog::spacingHint());
+
+ if(m_options.category() != Track)
+ m_trackGroup->hide();
+
+ QString tagText = m_options.tagTypeText();
+
+ setCaption(caption().arg(tagText));
+ m_tagFormatGroup->setTitle(m_tagFormatGroup->title().arg(tagText));
+ m_emptyTagGroup->setTitle(m_emptyTagGroup->title().arg(tagText));
+ m_description->setText(m_description->text().arg(tagText));
+ m_tagLabel->setText(m_tagLabel->text().arg(tagText));
+
+ m_prefixText->setText(options.prefix());
+ m_suffixText->setText(options.suffix());
+ if(options.emptyAction() == TagRenamerOptions::ForceEmptyInclude)
+ m_includeEmptyButton->setChecked(true);
+ else if(options.emptyAction() == TagRenamerOptions::UseReplacementValue)
+ m_useValueButton->setChecked(true);
+ m_emptyTagValue->setText(options.emptyText());
+ m_trackWidth->setValue(options.trackWidth());
+
+ slotBracketsChanged();
+ slotEmptyActionChanged();
+ slotTrackWidthChanged();
+}
+
+void FileRenamerTagOptions::slotBracketsChanged()
+{
+ QString tag = m_options.tagTypeText();
+
+ m_options.setPrefix(m_prefixText->text());
+ m_options.setSuffix(m_suffixText->text());
+
+ m_substitution->setText(m_options.prefix() + tag + m_options.suffix());
+}
+
+void FileRenamerTagOptions::slotTrackWidthChanged()
+{
+ unsigned width = m_trackWidth->value();
+
+ m_options.setTrackWidth(width);
+
+ QString singleDigitText = m_singleDigit->text();
+ singleDigitText.remove(" ->");
+ QString doubleDigitText = m_doubleDigit->text();
+ doubleDigitText.remove(" ->");
+
+ if(singleDigitText.length() < width) {
+ QString p;
+ p.fill('0', width - singleDigitText.length());
+ singleDigitText.prepend(p);
+ }
+
+ if(doubleDigitText.length() < width) {
+ QString p;
+ p.fill('0', width - doubleDigitText.length());
+ doubleDigitText.prepend(p);
+ }
+
+ m_singleDigitExample->setText(singleDigitText);
+ m_doubleDigitExample->setText(doubleDigitText);
+}
+
+void FileRenamerTagOptions::slotEmptyActionChanged()
+{
+ m_options.setEmptyText(m_emptyTagValue->text());
+
+ m_options.setEmptyAction(TagRenamerOptions::IgnoreEmptyTag);
+
+ if(m_useValueButton->isChecked())
+ m_options.setEmptyAction(TagRenamerOptions::UseReplacementValue);
+ else if(m_includeEmptyButton->isChecked())
+ m_options.setEmptyAction(TagRenamerOptions::ForceEmptyInclude);
+}
+
+TagOptionsDialog::TagOptionsDialog(QWidget *parent,
+ const TagRenamerOptions &options,
+ unsigned categoryNumber) :
+ KDialogBase(parent, 0, true, i18n("File Renamer"), Ok | Cancel),
+ m_options(options),
+ m_categoryNumber(categoryNumber)
+{
+ loadConfig();
+
+ m_widget = new FileRenamerTagOptions(this, m_options);
+ m_widget->setMinimumSize(400, 200);
+
+ setMainWidget(m_widget);
+}
+
+void TagOptionsDialog::accept()
+{
+ m_options = m_widget->options();
+
+ saveConfig();
+ KDialogBase::accept();
+}
+
+void TagOptionsDialog::loadConfig()
+{
+ // Our m_options may not have been loaded from KConfig, force that to
+ // happen.
+
+ CategoryID category(m_options.category(), m_categoryNumber);
+ m_options = TagRenamerOptions(category);
+}
+
+void TagOptionsDialog::saveConfig()
+{
+ m_options.saveConfig(m_categoryNumber);
+}
+
+#include "filerenameroptions.moc"
+
+// vim: set et ts=4 sw=4:
diff --git a/juk/filerenameroptions.h b/juk/filerenameroptions.h
new file mode 100644
index 00000000..0711fbf7
--- /dev/null
+++ b/juk/filerenameroptions.h
@@ -0,0 +1,79 @@
+/***************************************************************************
+ begin : Thu Oct 28 2004
+ copyright : (C) 2004 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef JUK_FILERENAMEROPTIONS_H
+#define JUK_FILERENAMEROPTIONS_H
+
+#include <kdialogbase.h>
+#include "filerenameroptionsbase.h"
+#include "tagrenameroptions.h"
+
+/**
+ * Base widget implementing the options for a particular tag type.
+ *
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ */
+class FileRenamerTagOptions : public FileRenamerTagOptionsBase
+{
+ Q_OBJECT
+
+ public:
+ FileRenamerTagOptions(QWidget *parent, const TagRenamerOptions &options);
+
+ const TagRenamerOptions &options() const { return m_options; }
+
+ protected slots:
+ virtual void slotBracketsChanged();
+ virtual void slotTrackWidthChanged();
+ virtual void slotEmptyActionChanged();
+
+ private:
+ TagRenamerOptions m_options;
+};
+
+/**
+ * This defines the dialog that actually gets the options from the user.
+ *
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ */
+class TagOptionsDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ TagOptionsDialog(QWidget *parent, const TagRenamerOptions &options, unsigned categoryNumber);
+
+ const TagRenamerOptions &options() const { return m_options; }
+
+ protected slots:
+ virtual void accept();
+
+ private:
+
+ // Private methods
+
+ void loadConfig(); // Loads m_options from KConfig
+ void saveConfig(); // Saves m_options to KConfig
+
+ // Private members
+
+ FileRenamerTagOptions *m_widget;
+ TagRenamerOptions m_options;
+ unsigned m_categoryNumber;
+};
+
+#endif /* JUK_FILERENAMEROPTIONS_H */
+
+// vim: set et ts=4 sw=4:
diff --git a/juk/filerenameroptionsbase.ui b/juk/filerenameroptionsbase.ui
new file mode 100644
index 00000000..1e40ce60
--- /dev/null
+++ b/juk/filerenameroptionsbase.ui
@@ -0,0 +1,425 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>FileRenamerTagOptionsBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>FileRenamerTagOptionsBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>568</width>
+ <height>377</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>%1 Options</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>m_tagFormatGroup</cstring>
+ </property>
+ <property name="title">
+ <string>%1 Format</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_description</cstring>
+ </property>
+ <property name="text">
+ <string>When using the file renamer your files will be renamed to the values that you have in your track's %1 tag, plus any additional text that you specify below.</string>
+ </property>
+ <property name="textFormat">
+ <enum>RichText</enum>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignCenter</set>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>m_tagLayout</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>m_prefixText</cstring>
+ </property>
+ <property name="alignment">
+ <set>AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_tagLabel</cstring>
+ </property>
+ <property name="text">
+ <string>%1</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>m_suffixText</cstring>
+ </property>
+ <property name="alignment">
+ <set>AlignLeft</set>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_substitution</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <italic>1</italic>
+ </font>
+ </property>
+ <property name="text">
+ <string>Substitution Example</string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>m_emptyTagGroup</cstring>
+ </property>
+ <property name="title">
+ <string>When the Track's %1 is Empty</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>m_includeEmptyButton</cstring>
+ </property>
+ <property name="text">
+ <string>Include in the &amp;filename anyways</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>m_ignoreTagButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Ignore this tag when renaming the file</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>m_emptyValueLayout</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>m_useValueButton</cstring>
+ </property>
+ <property name="text">
+ <string>Use &amp;this value:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>m_emptyTagValue</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Empty</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>m_trackGroup</cstring>
+ </property>
+ <property name="title">
+ <string>Track Width Options</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel10</cstring>
+ </property>
+ <property name="text">
+ <string>JuK can force the track used in a file name to have a minimum number of digits. You may want to do this for better sorting in file managers.</string>
+ </property>
+ <property name="textFormat">
+ <enum>RichText</enum>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignCenter</set>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>m_widthLayout</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>m_spinLayout</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Minimum track &amp;width:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_trackWidth</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>m_trackWidth</cstring>
+ </property>
+ <property name="specialValueText">
+ <string>None</string>
+ </property>
+ <property name="maxValue">
+ <number>6</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>m_exampleLayout</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>m_doubleDigitExample</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <italic>1</italic>
+ </font>
+ </property>
+ <property name="text">
+ <string>014</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>m_singleDigitExample</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <italic>1</italic>
+ </font>
+ </property>
+ <property name="text">
+ <string>003</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>m_singleDigit</cstring>
+ </property>
+ <property name="text">
+ <string>3 -&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>m_doubleDigit</cstring>
+ </property>
+ <property name="text">
+ <string>14 -&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>m_useValueButton</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_emptyTagValue</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>m_useValueButton</sender>
+ <signal>clicked()</signal>
+ <receiver>m_emptyTagValue</receiver>
+ <slot>setFocus()</slot>
+ </connection>
+ <connection>
+ <sender>m_trackWidth</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>FileRenamerTagOptionsBase</receiver>
+ <slot>slotTrackWidthChanged()</slot>
+ </connection>
+ <connection>
+ <sender>m_prefixText</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>FileRenamerTagOptionsBase</receiver>
+ <slot>slotBracketsChanged()</slot>
+ </connection>
+ <connection>
+ <sender>m_suffixText</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>FileRenamerTagOptionsBase</receiver>
+ <slot>slotBracketsChanged()</slot>
+ </connection>
+ <connection>
+ <sender>m_includeEmptyButton</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>FileRenamerTagOptionsBase</receiver>
+ <slot>slotEmptyActionChanged()</slot>
+ </connection>
+ <connection>
+ <sender>m_ignoreTagButton</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>FileRenamerTagOptionsBase</receiver>
+ <slot>slotEmptyActionChanged()</slot>
+ </connection>
+ <connection>
+ <sender>m_useValueButton</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>FileRenamerTagOptionsBase</receiver>
+ <slot>slotEmptyActionChanged()</slot>
+ </connection>
+ <connection>
+ <sender>m_emptyTagValue</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>FileRenamerTagOptionsBase</receiver>
+ <slot>slotEmptyActionChanged()</slot>
+ </connection>
+</connections>
+<slots>
+ <slot access="protected">slotBracketsChanged()</slot>
+ <slot access="protected">slotTrackWidthChanged()</slot>
+ <slot access="protected">slotEmptyActionChanged()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+</includehints>
+</UI>
diff --git a/juk/folderplaylist.cpp b/juk/folderplaylist.cpp
new file mode 100644
index 00000000..ecde8f77
--- /dev/null
+++ b/juk/folderplaylist.cpp
@@ -0,0 +1,81 @@
+/***************************************************************************
+ copyright : (C) 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "folderplaylist.h"
+#include "playlistcollection.h"
+
+#include <qtimer.h>
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+FolderPlaylist::FolderPlaylist(PlaylistCollection *collection, const QString &folder,
+ const QString &name) :
+ Playlist(collection, name, "folder"),
+ m_folder(folder)
+{
+ QTimer::singleShot(0, this, SLOT(slotReload()));
+}
+
+FolderPlaylist::~FolderPlaylist()
+{
+
+}
+
+QString FolderPlaylist::folder() const
+{
+ return m_folder;
+}
+
+void FolderPlaylist::setFolder(const QString &s)
+{
+ m_folder = s;
+ QTimer::singleShot(0, this, SLOT(slotReload()));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private slots
+////////////////////////////////////////////////////////////////////////////////
+
+void FolderPlaylist::slotReload()
+{
+ if(!m_folder.isNull())
+ addFiles(m_folder);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// helper functions
+////////////////////////////////////////////////////////////////////////////////
+
+QDataStream &operator<<(QDataStream &s, const FolderPlaylist &p)
+{
+ s << p.name()
+ << p.folder();
+ return s;
+}
+
+QDataStream &operator>>(QDataStream &s, FolderPlaylist &p)
+{
+ QString name;
+ QString folder;
+ s >> name
+ >> folder;
+
+ p.setFolder(folder);
+ p.setName(name);
+ return s;
+}
+
+#include "folderplaylist.moc"
diff --git a/juk/folderplaylist.h b/juk/folderplaylist.h
new file mode 100644
index 00000000..0635a131
--- /dev/null
+++ b/juk/folderplaylist.h
@@ -0,0 +1,44 @@
+/***************************************************************************
+ copyright : (C) 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef FOLDERPLAYLIST_H
+#define FOLDERPLAYLIST_H
+
+#include "playlist.h"
+
+class FolderPlaylist : public Playlist
+{
+ Q_OBJECT
+
+public:
+ FolderPlaylist(PlaylistCollection *collection, const QString &folder = QString::null,
+ const QString &name = QString::null);
+ virtual ~FolderPlaylist();
+
+ QString folder() const;
+ void setFolder(const QString &s);
+
+ virtual bool canReload() const { return true; }
+
+public slots:
+ virtual void slotReload();
+
+private:
+ QString m_folder;
+};
+
+QDataStream &operator<<(QDataStream &s, const FolderPlaylist &p);
+QDataStream &operator>>(QDataStream &s, FolderPlaylist &p);
+
+#endif
diff --git a/juk/gstreamerplayer.cpp b/juk/gstreamerplayer.cpp
new file mode 100644
index 00000000..c538d9ff
--- /dev/null
+++ b/juk/gstreamerplayer.cpp
@@ -0,0 +1,346 @@
+/***************************************************************************
+ copyright : (C) 2004 Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "gstreamerplayer.h"
+
+#if HAVE_GSTREAMER
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kglobal.h>
+#include <kdebug.h>
+
+#include <qfile.h>
+#include <qtimer.h>
+
+// Defined because recent versions of glib add support for having gcc check
+// whether the sentinel used on g_object_{set,get} is correct. Although 0
+// is a valid NULL pointer in C++, when used in a C function call g++ doesn't
+// know to turn it into a pointer so it leaves it as an int instead (which is
+// wrong for 64-bit arch). So, use the handy define below instead.
+
+#define JUK_GLIB_NULL static_cast<gpointer>(0)
+
+#if GSTREAMER_VERSION == 8
+
+/******************************************************************************/
+/******************************************************************************/
+/****************************** GSTREAMER 0.8 *******************************/
+/******************************************************************************/
+/******************************************************************************/
+
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+GStreamerPlayer::GStreamerPlayer() :
+ Player(),
+ m_pipeline(0),
+ m_source(0),
+ m_decoder(0),
+ m_volume(0),
+ m_sink(0)
+{
+ readConfig();
+ setupPipeline();
+}
+
+GStreamerPlayer::~GStreamerPlayer()
+{
+ stop();
+ gst_object_unref(GST_OBJECT(m_pipeline));
+}
+
+void GStreamerPlayer::play(const FileHandle &file)
+{
+ if(!file.isNull()) {
+ stop();
+ g_object_set(G_OBJECT(m_source), "location", file.absFilePath().local8Bit().data(), JUK_GLIB_NULL);
+ }
+
+ gst_element_set_state(m_pipeline, GST_STATE_PLAYING);
+}
+
+void GStreamerPlayer::pause()
+{
+ gst_element_set_state(m_pipeline, GST_STATE_PAUSED);
+}
+
+void GStreamerPlayer::stop()
+{
+ gst_element_set_state(m_pipeline, GST_STATE_NULL);
+}
+
+void GStreamerPlayer::setVolume(float volume)
+{
+ g_object_set(G_OBJECT(m_volume), "volume", volume, JUK_GLIB_NULL);
+}
+
+float GStreamerPlayer::volume() const
+{
+ gdouble value;
+ g_object_get(G_OBJECT(m_volume), "volume", &value, JUK_GLIB_NULL);
+ return (float) value;
+}
+
+bool GStreamerPlayer::playing() const
+{
+ return gst_element_get_state(m_pipeline) == GST_STATE_PLAYING;
+}
+
+bool GStreamerPlayer::paused() const
+{
+ return gst_element_get_state(m_pipeline) == GST_STATE_PAUSED;
+}
+
+int GStreamerPlayer::totalTime() const
+{
+ return time(GST_QUERY_TOTAL) / GST_SECOND;
+}
+
+int GStreamerPlayer::currentTime() const
+{
+ return time(GST_QUERY_POSITION) / GST_SECOND;
+}
+
+int GStreamerPlayer::position() const
+{
+ long long total = time(GST_QUERY_TOTAL);
+ long long current = time(GST_QUERY_POSITION);
+ return total > 0 ? int((double(current) / double(total)) * double(1000) + 0.5) : 0;
+}
+
+void GStreamerPlayer::seek(int seekTime)
+{
+ int type = (GST_FORMAT_TIME | GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH);
+ gst_element_seek(m_sink, GstSeekType(type), seekTime * GST_SECOND);
+}
+
+void GStreamerPlayer::seekPosition(int position)
+{
+ long long total = time(GST_QUERY_TOTAL);
+ if(total > 0)
+ seek(int(double(position) / double(1000) * double(totalTime()) + 0.5));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private methods
+////////////////////////////////////////////////////////////////////////////////
+
+void GStreamerPlayer::readConfig()
+{
+ KConfigGroup config(KGlobal::config(), "GStreamerPlayer");
+ m_sinkName = config.readEntry("SinkName", QString::null);
+}
+
+void GStreamerPlayer::setupPipeline()
+{
+ static bool initialized = false;
+ if(!initialized) {
+ int argc = kapp->argc();
+ char **argv = kapp->argv();
+ gst_init(&argc, &argv);
+ initialized = true;
+ }
+
+ m_pipeline = gst_thread_new("pipeline");
+ m_source = gst_element_factory_make("filesrc", "source");
+ m_decoder = gst_element_factory_make("spider", "decoder");
+ m_volume = gst_element_factory_make("volume", "volume");
+
+ if(!m_sinkName.isNull())
+ m_sink = gst_element_factory_make(m_sinkName.utf8().data(), "sink");
+ else {
+ m_sink = gst_element_factory_make("alsasink", "sink");
+ if(!m_sink)
+ m_sink = gst_element_factory_make("osssink", "sink");
+ }
+
+
+ gst_bin_add_many(GST_BIN(m_pipeline), m_source, m_decoder, m_volume, m_sink, 0);
+ gst_element_link_many(m_source, m_decoder, m_volume, m_sink, 0);
+}
+
+long long GStreamerPlayer::time(GstQueryType type) const
+{
+ gint64 ns = 0;
+ GstFormat format = GST_FORMAT_TIME;
+ gst_element_query(m_sink, type, &format, &ns);
+ return ns;
+}
+
+#else
+
+/******************************************************************************/
+/******************************************************************************/
+/****************************** GSTREAMER 0.10 ******************************/
+/******************************************************************************/
+/******************************************************************************/
+
+static GstBusSyncReply messageHandler(GstBus *, GstMessage *message, gpointer data)
+{
+ if(GST_MESSAGE_TYPE(message) == GST_MESSAGE_EOS) {
+ GStreamerPlayer *player = static_cast<GStreamerPlayer *>(data);
+ QTimer::singleShot(0, player, SLOT(stop()));
+ }
+
+ gst_message_unref(message);
+ return GST_BUS_DROP;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+GStreamerPlayer::GStreamerPlayer() :
+ Player(),
+ m_playbin(0)
+{
+ setupPipeline();
+}
+
+GStreamerPlayer::~GStreamerPlayer()
+{
+ stop();
+ gst_object_unref(GST_OBJECT(m_playbin));
+}
+
+void GStreamerPlayer::play(const FileHandle &file)
+{
+ if(!file.isNull()) {
+ stop();
+ gchar *uri = g_filename_to_uri(file.absFilePath().local8Bit().data(), NULL, NULL);
+ g_object_set(G_OBJECT(m_playbin), "uri", uri, JUK_GLIB_NULL);
+ }
+
+ gst_element_set_state(m_playbin, GST_STATE_PLAYING);
+}
+
+void GStreamerPlayer::pause()
+{
+ gst_element_set_state(m_playbin, GST_STATE_PAUSED);
+}
+
+void GStreamerPlayer::stop()
+{
+ gst_element_set_state(m_playbin, GST_STATE_NULL);
+}
+
+void GStreamerPlayer::setVolume(float volume)
+{
+ g_object_set(G_OBJECT(m_playbin), "volume", volume, JUK_GLIB_NULL);
+}
+
+float GStreamerPlayer::volume() const
+{
+ gdouble value;
+ g_object_get(G_OBJECT(m_playbin), "volume", &value, JUK_GLIB_NULL);
+ return (float) value;
+}
+
+bool GStreamerPlayer::playing() const
+{
+ return state() == GST_STATE_PLAYING;
+}
+
+bool GStreamerPlayer::paused() const
+{
+ return state() == GST_STATE_PAUSED;
+}
+
+int GStreamerPlayer::totalTime() const
+{
+ return time(TotalLength) / GST_SECOND;
+}
+
+int GStreamerPlayer::currentTime() const
+{
+ return time(CurrentPosition) / GST_SECOND;
+}
+
+int GStreamerPlayer::position() const
+{
+ long long total = time(TotalLength);
+ long long current = time(CurrentPosition);
+ return total > 0 ? int((double(current) / double(total)) * double(1000) + 0.5) : 0;
+}
+
+void GStreamerPlayer::seek(int seekTime)
+{
+ gst_element_seek(m_playbin, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
+ GST_SEEK_TYPE_SET, seekTime * GST_SECOND, GST_SEEK_TYPE_END, 0);
+}
+
+void GStreamerPlayer::seekPosition(int position)
+{
+ gint64 time = gint64((double(position) / double(1000) * double(totalTime())
+ + 0.5) * double(GST_SECOND));
+ gst_element_seek(m_playbin, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
+ GST_SEEK_TYPE_SET, time, GST_SEEK_TYPE_END, 0);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private methods
+////////////////////////////////////////////////////////////////////////////////
+
+void GStreamerPlayer::setupPipeline()
+{
+ static bool initialized = false;
+ if(!initialized) {
+ int argc = kapp->argc();
+ char **argv = kapp->argv();
+ gst_init(&argc, &argv);
+ initialized = true;
+ }
+
+ m_playbin = gst_element_factory_make("playbin", "playbin");
+ gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(m_playbin)), messageHandler, this);
+}
+
+long long GStreamerPlayer::time(TimeQuery type) const
+{
+ GstQuery *query = (type == CurrentPosition)
+ ? gst_query_new_position(GST_FORMAT_TIME)
+ : gst_query_new_duration(GST_FORMAT_TIME);
+
+ gint64 ns = 0;
+ GstFormat format;
+
+ if(gst_element_query(m_playbin, query))
+ {
+ if(type == CurrentPosition)
+ gst_query_parse_position(query, &format, &ns);
+ else
+ gst_query_parse_duration(query, &format, &ns);
+ }
+
+ gst_query_unref(query);
+
+ return ns;
+}
+
+GstState GStreamerPlayer::state() const
+{
+ GstState state;
+ gst_element_get_state(m_playbin, &state, NULL, GST_CLOCK_TIME_NONE);
+ return state;
+}
+
+#endif
+
+#include "gstreamerplayer.moc"
+#endif
+
+// vim: set et sw=4:
diff --git a/juk/gstreamerplayer.h b/juk/gstreamerplayer.h
new file mode 100644
index 00000000..1d15562b
--- /dev/null
+++ b/juk/gstreamerplayer.h
@@ -0,0 +1,81 @@
+/***************************************************************************
+ copyright : (C) 2004 Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+
+#ifndef GSTREAMERPLAYER_H
+#define GSTREAMERPLAYER_H
+
+#include "config.h"
+
+#if HAVE_GSTREAMER
+
+#include <gst/gst.h>
+#include <qstring.h>
+#include "player.h"
+
+class GStreamerPlayer : public Player
+{
+ Q_OBJECT
+
+public:
+ GStreamerPlayer();
+ virtual ~GStreamerPlayer();
+
+ virtual void play(const FileHandle &file = FileHandle::null());
+
+ virtual void setVolume(float volume = 1.0);
+ virtual float volume() const;
+
+ virtual bool playing() const;
+ virtual bool paused() const;
+
+ virtual int totalTime() const;
+ virtual int currentTime() const;
+ virtual int position() const; // in this case not really the percent
+
+ virtual void seek(int seekTime);
+ virtual void seekPosition(int position);
+
+ virtual void pause();
+public slots:
+ virtual void stop();
+
+private:
+ void setupPipeline();
+
+#if GSTREAMER_VERSION == 8
+
+ void readConfig();
+ long long time(GstQueryType type) const;
+
+ QString m_sinkName;
+ GstElement *m_pipeline;
+ GstElement *m_source;
+ GstElement *m_decoder;
+ GstElement *m_volume;
+ GstElement *m_sink;
+
+#else
+
+ enum TimeQuery { CurrentPosition, TotalLength };
+ long long time(TimeQuery type) const;
+
+ GstState state() const;
+ GstElement *m_playbin;
+
+#endif
+};
+
+#endif
+#endif
diff --git a/juk/hi128-app-juk.png b/juk/hi128-app-juk.png
new file mode 100644
index 00000000..5c41868e
--- /dev/null
+++ b/juk/hi128-app-juk.png
Binary files differ
diff --git a/juk/hi16-app-juk.png b/juk/hi16-app-juk.png
new file mode 100644
index 00000000..57933cde
--- /dev/null
+++ b/juk/hi16-app-juk.png
Binary files differ
diff --git a/juk/hi32-app-juk.png b/juk/hi32-app-juk.png
new file mode 100644
index 00000000..2b58a33d
--- /dev/null
+++ b/juk/hi32-app-juk.png
Binary files differ
diff --git a/juk/hi48-app-juk.png b/juk/hi48-app-juk.png
new file mode 100644
index 00000000..73a07612
--- /dev/null
+++ b/juk/hi48-app-juk.png
Binary files differ
diff --git a/juk/hi64-app-juk.png b/juk/hi64-app-juk.png
new file mode 100644
index 00000000..e258cd80
--- /dev/null
+++ b/juk/hi64-app-juk.png
Binary files differ
diff --git a/juk/historyplaylist.cpp b/juk/historyplaylist.cpp
new file mode 100644
index 00000000..6ebd4643
--- /dev/null
+++ b/juk/historyplaylist.cpp
@@ -0,0 +1,160 @@
+ /***************************************************************************
+ begin : Fri Aug 8 2003
+ copyright : (C) 2003 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <kdebug.h>
+
+#include "historyplaylist.h"
+#include "collectionlist.h"
+#include "playermanager.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// HistoryPlayList public members
+////////////////////////////////////////////////////////////////////////////////
+
+HistoryPlaylist::HistoryPlaylist(PlaylistCollection *collection) :
+ Playlist(collection, true), m_timer(0)
+{
+ setAllowDuplicates(true);
+ m_timer = new QTimer(this);
+
+ connect(PlayerManager::instance(), SIGNAL(signalPlay()), this, SLOT(slotAddPlaying()));
+ connect(m_timer, SIGNAL(timeout()), this, SLOT(slotCreateNewItem()));
+}
+
+HistoryPlaylist::~HistoryPlaylist()
+{
+
+}
+
+HistoryPlaylistItem *HistoryPlaylist::createItem(const FileHandle &file,
+ QListViewItem *after, bool emitChanged)
+{
+ if(!after)
+ after = lastItem();
+ return Playlist::createItem<HistoryPlaylistItem, CollectionListItem,
+ CollectionList>(file, after, emitChanged);
+}
+
+void HistoryPlaylist::createItems(const PlaylistItemList &siblings)
+{
+ Playlist::createItems<CollectionListItem, HistoryPlaylistItem, PlaylistItem>(siblings);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HistoryPlaylist protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void HistoryPlaylist::polish()
+{
+ addColumn(i18n("Time"));
+ Playlist::polish();
+ setSorting(-1);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private slots
+////////////////////////////////////////////////////////////////////////////////
+
+void HistoryPlaylist::slotAddPlaying()
+{
+ m_file = PlayerManager::instance()->playingFile();
+ m_timer->stop();
+ m_timer->start(delay(), true);
+}
+
+void HistoryPlaylist::slotCreateNewItem()
+{
+ PlayerManager *player = PlayerManager::instance();
+
+ if(player->playing() && m_file == player->playingFile()) {
+ createItem(m_file);
+ m_file = FileHandle::null();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HistoryPlaylistItem public members
+////////////////////////////////////////////////////////////////////////////////
+
+HistoryPlaylistItem::HistoryPlaylistItem(CollectionListItem *item, Playlist *parent, QListViewItem *after) :
+ PlaylistItem(item, parent, after),
+ m_dateTime(QDateTime::currentDateTime())
+{
+ setText(0, KGlobal::locale()->formatDateTime(m_dateTime));
+}
+
+HistoryPlaylistItem::HistoryPlaylistItem(CollectionListItem *item, Playlist *parent) :
+ PlaylistItem(item, parent),
+ m_dateTime(QDateTime::currentDateTime())
+{
+ setText(0, KGlobal::locale()->formatDateTime(m_dateTime));
+}
+
+HistoryPlaylistItem::~HistoryPlaylistItem()
+{
+
+}
+
+void HistoryPlaylistItem::setDateTime(const QDateTime &dt)
+{
+ m_dateTime = dt;
+ setText(0, KGlobal::locale()->formatDateTime(m_dateTime));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// helper functions
+////////////////////////////////////////////////////////////////////////////////
+
+QDataStream &operator<<(QDataStream &s, const HistoryPlaylist &p)
+{
+ PlaylistItemList l = const_cast<HistoryPlaylist *>(&p)->items();
+
+ s << Q_INT32(l.count());
+
+ for(PlaylistItemList::ConstIterator it = l.begin(); it != l.end(); ++it) {
+ const HistoryPlaylistItem *i = static_cast<HistoryPlaylistItem *>(*it);
+ s << i->file().absFilePath();
+ s << i->dateTime();
+ }
+
+ return s;
+}
+
+QDataStream &operator>>(QDataStream &s, HistoryPlaylist &p)
+{
+ Q_INT32 count;
+ s >> count;
+
+ HistoryPlaylistItem *after = 0;
+
+ QString fileName;
+ QDateTime dateTime;
+
+ for(int i = 0; i < count; i++) {
+ s >> fileName;
+ s >> dateTime;
+
+ after = p.createItem(FileHandle(fileName), after, false);
+ after->setDateTime(dateTime);
+ }
+
+ p.dataChanged();
+
+ return s;
+}
+
+#include "historyplaylist.moc"
diff --git a/juk/historyplaylist.h b/juk/historyplaylist.h
new file mode 100644
index 00000000..144ec11c
--- /dev/null
+++ b/juk/historyplaylist.h
@@ -0,0 +1,72 @@
+/***************************************************************************
+ begin : Fri Aug 8 2003
+ copyright : (C) 2003 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef HISTORYPLAYLIST_H
+#define HISTORYPLAYLIST_H
+
+
+#include "playlist.h"
+#include "playlistitem.h"
+
+class HistoryPlaylistItem : public PlaylistItem
+{
+public:
+ HistoryPlaylistItem(CollectionListItem *item, Playlist *parent, QListViewItem *after);
+ HistoryPlaylistItem(CollectionListItem *item, Playlist *parent);
+ virtual ~HistoryPlaylistItem();
+
+ QDateTime dateTime() const { return m_dateTime; }
+ void setDateTime(const QDateTime &dt);
+
+private:
+ QDateTime m_dateTime;
+};
+
+class HistoryPlaylist : public Playlist
+{
+ Q_OBJECT
+
+public:
+ HistoryPlaylist(PlaylistCollection *collection);
+ virtual ~HistoryPlaylist();
+
+ virtual HistoryPlaylistItem *createItem(const FileHandle &file, QListViewItem *after = 0,
+ bool emitChanged = true);
+ virtual void createItems(const PlaylistItemList &siblings);
+ virtual int columnOffset() const { return 1; }
+ virtual bool readOnly() const { return true; }
+
+ static int delay() { return 5000; }
+
+public slots:
+ void cut() {}
+ void clear() {}
+
+protected:
+ virtual void polish();
+
+private slots:
+ void slotAddPlaying();
+ void slotCreateNewItem();
+
+private:
+ FileHandle m_file;
+ QTimer *m_timer;
+};
+
+QDataStream &operator<<(QDataStream &s, const HistoryPlaylist &p);
+QDataStream &operator>>(QDataStream &s, HistoryPlaylist &p);
+
+#endif
diff --git a/juk/juk.cpp b/juk/juk.cpp
new file mode 100644
index 00000000..a3e0988c
--- /dev/null
+++ b/juk/juk.cpp
@@ -0,0 +1,478 @@
+/***************************************************************************
+ begin : Mon Feb 4 23:40:41 EST 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <config.h>
+
+#include <kcmdlineargs.h>
+#include <kstatusbar.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+
+
+#include "juk.h"
+#include "slideraction.h"
+#include "statuslabel.h"
+#include "splashscreen.h"
+#include "systemtray.h"
+#include "keydialog.h"
+#include "tagguesserconfigdlg.h"
+#include "filerenamerconfigdlg.h"
+#include "actioncollection.h"
+#include "cache.h"
+#include "playlistsplitter.h"
+#include "collectionlist.h"
+#include "covermanager.h"
+#include "tagtransactionmanager.h"
+
+using namespace ActionCollection;
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+JuK::JuK(QWidget *parent, const char *name) :
+ KMainWindow(parent, name, WDestructiveClose),
+ m_player(PlayerManager::instance()),
+ m_shuttingDown(false)
+{
+ // Expect segfaults if you change this order.
+
+ readSettings();
+
+ if(m_showSplash && !m_startDocked && Cache::cacheFileExists()) {
+ SplashScreen::instance()->show();
+ kapp->processEvents();
+ }
+
+ setupActions();
+ setupLayout();
+
+ if(QApplication::reverseLayout())
+ setupGUI(ToolBar | Save | Create, "jukui-rtl.rc");
+ else
+ setupGUI(ToolBar | Save | Create);
+
+ readConfig();
+ setupSystemTray();
+ setupGlobalAccels();
+ createDirs();
+
+ SplashScreen::finishedLoading();
+ QTimer::singleShot(0, CollectionList::instance(), SLOT(slotCheckCache()));
+ QTimer::singleShot(0, this, SLOT(slotProcessArgs()));
+
+ m_sliderAction->slotUpdateOrientation();
+}
+
+JuK::~JuK()
+{
+ kdDebug(65432) << k_funcinfo << endl;
+}
+
+KActionCollection *JuK::actionCollection() const
+{
+ return ActionCollection::actions();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void JuK::setupLayout()
+{
+ new TagTransactionManager(this);
+
+ m_splitter = new PlaylistSplitter(this, "playlistSplitter");
+ setCentralWidget(m_splitter);
+
+ m_statusLabel = new StatusLabel(m_splitter->playlist(), statusBar());
+ connect(CollectionList::instance(), SIGNAL(signalCollectionChanged()),
+ m_statusLabel, SLOT(updateData()));
+ statusBar()->addWidget(m_statusLabel, 1);
+ PlayerManager::instance()->setStatusLabel(m_statusLabel);
+
+ m_splitter->setFocus();
+ resize(750, 500);
+}
+
+void JuK::setupActions()
+{
+ ActionCollection::actions()->setWidget(this);
+
+ KStdAction::quit(this, SLOT(slotQuit()), actions());
+ KStdAction::undo(this, SLOT(slotUndo()), actions());
+ KStdAction::cut(kapp, SLOT(cut()), actions());
+ KStdAction::copy(kapp, SLOT(copy()), actions());
+ KStdAction::paste(kapp, SLOT(paste()), actions());
+ KStdAction::clear(kapp, SLOT(clear()), actions());
+ KStdAction::selectAll(kapp, SLOT(selectAll()), actions());
+
+ new KAction(i18n("Remove From Playlist"), "edit_remove", 0, kapp, SLOT(clear()), actions(), "removeFromPlaylist");
+
+ KActionMenu *actionMenu = new KActionMenu(i18n("&Random Play"), "roll", actions(), "actionMenu");
+ actionMenu->setDelayed(false);
+
+ KRadioAction *ka = new KRadioAction(i18n("&Disable Random Play"), "player_playlist", 0, actions(), "disableRandomPlay");
+ ka->setExclusiveGroup("randomPlayGroup");
+ actionMenu->insert(ka);
+
+ m_randomPlayAction = new KRadioAction(i18n("Use &Random Play"), "roll", 0, actions(), "randomPlay");
+ m_randomPlayAction->setExclusiveGroup("randomPlayGroup");
+ actionMenu->insert(m_randomPlayAction);
+
+ ka = new KRadioAction(i18n("Use &Album Random Play"), "roll", 0, actions(), "albumRandomPlay");
+ ka->setExclusiveGroup("randomPlayGroup");
+ connect(ka, SIGNAL(toggled(bool)), SLOT(slotCheckAlbumNextAction(bool)));
+ actionMenu->insert(ka);
+
+ new KAction(i18n("&Play"), "player_play", 0, m_player, SLOT(play()), actions(), "play");
+ new KAction(i18n("P&ause"), "player_pause", 0, m_player, SLOT(pause()), actions(), "pause");
+ new KAction(i18n("&Stop"), "player_stop", 0, m_player, SLOT(stop()), actions(), "stop");
+
+ new KToolBarPopupAction(i18n("previous track", "Previous"), "player_start", KShortcut(), m_player, SLOT(back()), actions(), "back");
+ new KAction(i18n("next track", "&Next"), "player_end", KShortcut(), m_player, SLOT(forward()), actions(), "forward");
+ new KToggleAction(i18n("&Loop Playlist"), 0, KShortcut(), actions(), "loopPlaylist");
+ KToggleAction *resizeColumnAction =
+ new KToggleAction(i18n("&Resize Playlist Columns Manually"),
+ KShortcut(), actions(), "resizeColumnsManually");
+ resizeColumnAction->setCheckedState(i18n("&Resize Column Headers Automatically"));
+
+ // the following are not visible by default
+
+ new KAction(i18n("Mute"), "mute", 0, m_player, SLOT(mute()), actions(), "mute");
+ new KAction(i18n("Volume Up"), "volumeUp", 0, m_player, SLOT(volumeUp()), actions(), "volumeUp");
+ new KAction(i18n("Volume Down"), "volumeDown", 0, m_player, SLOT(volumeDown()), actions(), "volumeDown");
+ new KAction(i18n("Play / Pause"), "playPause", 0, m_player, SLOT(playPause()), actions(), "playPause");
+ new KAction(i18n("Seek Forward"), "seekForward", 0, m_player, SLOT(seekForward()), actions(), "seekForward");
+ new KAction(i18n("Seek Back"), "seekBack", 0, m_player, SLOT(seekBack()), actions(), "seekBack");
+
+ //////////////////////////////////////////////////
+ // settings menu
+ //////////////////////////////////////////////////
+
+ m_toggleSplashAction =
+ new KToggleAction(i18n("Show Splash Screen on Startup"),
+ KShortcut(), actions(), "showSplashScreen");
+ m_toggleSplashAction->setCheckedState(i18n("Hide Splash Screen on Startup"));
+ m_toggleSystemTrayAction =
+ new KToggleAction(i18n("&Dock in System Tray"),
+ KShortcut(), actions(), "toggleSystemTray");
+ m_toggleDockOnCloseAction =
+ new KToggleAction(i18n("&Stay in System Tray on Close"),
+ KShortcut(), actions(), "dockOnClose");
+ m_togglePopupsAction =
+ new KToggleAction(i18n("Popup &Track Announcement"),
+ KShortcut(), this, 0, actions(), "togglePopups");
+ new KToggleAction(i18n("Save &Play Queue on Exit"),
+ KShortcut(), this, 0, actions(), "saveUpcomingTracks");
+
+ connect(m_toggleSystemTrayAction, SIGNAL(toggled(bool)),
+ this, SLOT(slotToggleSystemTray(bool)));
+
+
+ m_outputSelectAction = PlayerManager::playerSelectAction(actions());
+
+ if(m_outputSelectAction)
+ m_outputSelectAction->setCurrentItem(0);
+
+ new KAction(i18n("&Tag Guesser..."), 0, 0, this, SLOT(slotConfigureTagGuesser()),
+ actions(), "tagGuesserConfig");
+
+ new KAction(i18n("&File Renamer..."), 0, 0, this, SLOT(slotConfigureFileRenamer()),
+ actions(), "fileRenamerConfig");
+
+ KStdAction::keyBindings(this, SLOT(slotEditKeys()), actions());
+
+ //////////////////////////////////////////////////
+ // just in the toolbar
+ //////////////////////////////////////////////////
+
+ m_sliderAction = new SliderAction(i18n("Track Position"), actions(),
+ "trackPositionAction");
+}
+
+void JuK::setupSystemTray()
+{
+ if(m_toggleSystemTrayAction && m_toggleSystemTrayAction->isChecked()) {
+ m_systemTray = new SystemTray(this, "systemTray");
+ m_systemTray->show();
+
+ m_toggleDockOnCloseAction->setEnabled(true);
+ m_togglePopupsAction->setEnabled(true);
+
+ connect(m_systemTray, SIGNAL(quitSelected()), this, SLOT(slotAboutToQuit()));
+ }
+ else {
+ m_systemTray = 0;
+ m_toggleDockOnCloseAction->setEnabled(false);
+ m_togglePopupsAction->setEnabled(false);
+ }
+}
+
+void JuK::setupGlobalAccels()
+{
+ m_accel = new KGlobalAccel(this);
+
+ KeyDialog::insert(m_accel, "Play", i18n("Play"), action("play"), SLOT(activate()));
+ KeyDialog::insert(m_accel, "PlayPause", i18n("Play / Pause"), action("playPause"), SLOT(activate()));
+ KeyDialog::insert(m_accel, "Stop", i18n("Stop Playing"), action("stop"), SLOT(activate()));
+ KeyDialog::insert(m_accel, "Back", i18n("Back"), action("back"), SLOT(activate()));
+ KeyDialog::insert(m_accel, "Forward", i18n("Forward"), action("forward"), SLOT(activate()));
+ KeyDialog::insert(m_accel, "SeekBack", i18n("Seek Back"), action("seekBack"), SLOT(activate()));
+ KeyDialog::insert(m_accel, "SeekForward", i18n("Seek Forward"), action("seekForward"), SLOT(activate()));
+ KeyDialog::insert(m_accel, "VolumeUp", i18n("Volume Up"), action("volumeUp"), SLOT(activate()));
+ KeyDialog::insert(m_accel, "VolumeDown", i18n("Volume Down"), action("volumeDown"), SLOT(activate()));
+ KeyDialog::insert(m_accel, "Mute", i18n("Mute"), action("mute"), SLOT(activate()));
+ KeyDialog::insert(m_accel, "ShowHide", i18n("Show / Hide"), this, SLOT(slotShowHide()));
+ KeyDialog::insert(m_accel, "ForwardAlbum", i18n("Play Next Album"), action("forwardAlbum"), SLOT(activate()));
+
+ m_accel->setConfigGroup("Shortcuts");
+ m_accel->readSettings();
+ m_accel->updateConnections();
+}
+
+void JuK::slotProcessArgs()
+{
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ QStringList files;
+
+ for(int i = 0; i < args->count(); i++)
+ files.append(args->arg(i));
+
+ CollectionList::instance()->addFiles(files);
+}
+
+void JuK::createDirs()
+{
+ QDir dir(KGlobal::dirs()->saveLocation("data", kapp->instanceName() + '/'));
+ if (!dir.exists("covers", false))
+ dir.mkdir("covers", false);
+ dir.cd("covers");
+ if (!dir.exists("large", false))
+ dir.mkdir("large", false);
+}
+
+void JuK::keyPressEvent(QKeyEvent *e)
+{
+ if (e->key() >= Qt::Key_Back && e->key() <= Qt::Key_MediaLast)
+ e->accept();
+ KMainWindow::keyPressEvent(e);
+}
+
+/**
+ * These are settings that need to be know before setting up the GUI.
+ */
+
+void JuK::readSettings()
+{
+ KConfigGroup config(KGlobal::config(), "Settings");
+ m_showSplash = config.readBoolEntry("ShowSplashScreen", true);
+ m_startDocked = config.readBoolEntry("StartDocked", false);
+}
+
+void JuK::readConfig()
+{
+ // player settings
+
+ KConfigGroup playerConfig(KGlobal::config(), "Player");
+
+ if(m_sliderAction->volumeSlider()) {
+ int maxVolume = m_sliderAction->volumeSlider()->maxValue();
+ int volume = playerConfig.readNumEntry("Volume", maxVolume);
+ m_sliderAction->volumeSlider()->setVolume(volume);
+ }
+
+ // Default to no random play
+
+ ActionCollection::action<KToggleAction>("disableRandomPlay")->setChecked(true);
+
+ QString randomPlayMode = playerConfig.readEntry("RandomPlay", "Disabled");
+ if(randomPlayMode == "true" || randomPlayMode == "Normal")
+ m_randomPlayAction->setChecked(true);
+ else if(randomPlayMode == "AlbumRandomPlay")
+ ActionCollection::action<KToggleAction>("albumRandomPlay")->setChecked(true);
+
+ bool loopPlaylist = playerConfig.readBoolEntry("LoopPlaylist", false);
+ ActionCollection::action<KToggleAction>("loopPlaylist")->setChecked(loopPlaylist);
+
+ // general settings
+
+ KConfigGroup settingsConfig(KGlobal::config(), "Settings");
+
+ bool dockInSystemTray = settingsConfig.readBoolEntry("DockInSystemTray", true);
+ m_toggleSystemTrayAction->setChecked(dockInSystemTray);
+
+ bool dockOnClose = settingsConfig.readBoolEntry("DockOnClose", true);
+ m_toggleDockOnCloseAction->setChecked(dockOnClose);
+
+ bool showPopups = settingsConfig.readBoolEntry("TrackPopup", false);
+ m_togglePopupsAction->setChecked(showPopups);
+
+ if(m_outputSelectAction)
+ m_outputSelectAction->setCurrentItem(settingsConfig.readNumEntry("MediaSystem", 0));
+
+ m_toggleSplashAction->setChecked(m_showSplash);
+}
+
+void JuK::saveConfig()
+{
+ // player settings
+
+ KConfigGroup playerConfig(KGlobal::config(), "Player");
+
+ if (m_sliderAction->volumeSlider())
+ {
+ playerConfig.writeEntry("Volume", m_sliderAction->volumeSlider()->volume());
+ }
+
+ playerConfig.writeEntry("RandomPlay", m_randomPlayAction->isChecked());
+
+ KToggleAction *a = ActionCollection::action<KToggleAction>("loopPlaylist");
+ playerConfig.writeEntry("LoopPlaylist", a->isChecked());
+
+ a = ActionCollection::action<KToggleAction>("albumRandomPlay");
+ if(a->isChecked())
+ playerConfig.writeEntry("RandomPlay", "AlbumRandomPlay");
+ else if(m_randomPlayAction->isChecked())
+ playerConfig.writeEntry("RandomPlay", "Normal");
+ else
+ playerConfig.writeEntry("RandomPlay", "Disabled");
+
+ // general settings
+
+ KConfigGroup settingsConfig(KGlobal::config(), "Settings");
+ settingsConfig.writeEntry("ShowSplashScreen", m_toggleSplashAction->isChecked());
+ settingsConfig.writeEntry("StartDocked", m_startDocked);
+ settingsConfig.writeEntry("DockInSystemTray", m_toggleSystemTrayAction->isChecked());
+ settingsConfig.writeEntry("DockOnClose", m_toggleDockOnCloseAction->isChecked());
+ settingsConfig.writeEntry("TrackPopup", m_togglePopupsAction->isChecked());
+ if(m_outputSelectAction)
+ settingsConfig.writeEntry("MediaSystem", m_outputSelectAction->currentItem());
+
+ KGlobal::config()->sync();
+}
+
+bool JuK::queryExit()
+{
+ m_startDocked = !isVisible();
+
+ kdDebug(65432) << k_funcinfo << endl;
+
+ hide();
+
+ action("stop")->activate();
+ delete m_systemTray;
+ m_systemTray = 0;
+
+ CoverManager::shutdown();
+ Cache::instance()->save();
+ saveConfig();
+
+ delete m_splitter;
+ m_splitter = 0;
+ return true;
+}
+
+bool JuK::queryClose()
+{
+ kdDebug(65432) << k_funcinfo << endl;
+
+ if(!m_shuttingDown &&
+ !kapp->sessionSaving() &&
+ m_systemTray &&
+ m_toggleDockOnCloseAction->isChecked())
+ {
+ KMessageBox::information(this,
+ i18n("<qt>Closing the main window will keep JuK running in the system tray. "
+ "Use Quit from the File menu to quit the application.</qt>"),
+ i18n("Docking in System Tray"), "hideOnCloseInfo");
+ hide();
+ return false;
+ }
+ else
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private slot definitions
+////////////////////////////////////////////////////////////////////////////////
+
+void JuK::slotShowHide()
+{
+ setShown(!isShown());
+}
+
+void JuK::slotAboutToQuit()
+{
+ m_shuttingDown = true;
+}
+
+void JuK::slotQuit()
+{
+ kdDebug(65432) << k_funcinfo << endl;
+ m_shuttingDown = true;
+
+ kapp->quit();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// settings menu
+////////////////////////////////////////////////////////////////////////////////
+
+void JuK::slotToggleSystemTray(bool enabled)
+{
+ if(enabled && !m_systemTray)
+ setupSystemTray();
+ else if(!enabled && m_systemTray) {
+ delete m_systemTray;
+ m_systemTray = 0;
+ m_toggleDockOnCloseAction->setEnabled(false);
+ m_togglePopupsAction->setEnabled(false);
+ }
+}
+
+void JuK::slotEditKeys()
+{
+ KeyDialog::configure(m_accel, actions(), this);
+}
+
+void JuK::slotConfigureTagGuesser()
+{
+ TagGuesserConfigDlg(this).exec();
+}
+
+void JuK::slotConfigureFileRenamer()
+{
+ FileRenamerConfigDlg(this).exec();
+}
+
+void JuK::slotUndo()
+{
+ TagTransactionManager::instance()->undo();
+}
+
+void JuK::slotCheckAlbumNextAction(bool albumRandomEnabled)
+{
+ // If album random play is enabled, then enable the Play Next Album action
+ // unless we're not playing right now.
+
+ if(albumRandomEnabled && !m_player->playing())
+ albumRandomEnabled = false;
+
+ action("forwardAlbum")->setEnabled(albumRandomEnabled);
+}
+
+#include "juk.moc"
diff --git a/juk/juk.desktop b/juk/juk.desktop
new file mode 100644
index 00000000..b7734c31
--- /dev/null
+++ b/juk/juk.desktop
@@ -0,0 +1,72 @@
+# KDE Config File
+[Desktop Entry]
+Type=Application
+Exec=juk -caption "%c" %i %m
+Icon=juk
+DocPath=juk/index.html
+Comment=
+Terminal=false
+Name=JuK
+Name[bn]=জুক
+Name[hi]=ज्यूक
+Name[sv]=Juk
+Name[tr]=Juk
+GenericName=Music Player
+GenericName[ar]=مشغّل موسيقى
+GenericName[bg]=Плеър за музикални файлове
+GenericName[br]=C'hoarier ar sonerezh
+GenericName[bs]=Sviranje muzike
+GenericName[ca]=Reproductor musical
+GenericName[cs]=Přehrávač hudby
+GenericName[cy]=Chwaraewr Cerdd
+GenericName[da]=Musikafspiller
+GenericName[de]=Audio-Wiedergabe
+GenericName[el]=Αναπαραγωγέας μουσικής
+GenericName[eo]=Muzikludilo
+GenericName[es]=Reproductor de audio
+GenericName[et]=Muusika mängija
+GenericName[eu]=Musika erreproduzigailua
+GenericName[fa]=پخش‌کنندۀ موسیقی
+GenericName[fi]=Musiikkisoitin
+GenericName[fr]=Lecteur multimédia
+GenericName[ga]=Seinnteoir Ceoil
+GenericName[gl]=Reproductor de Música
+GenericName[he]=נגן מוזיקה
+GenericName[hi]=म्यूज़िक प्लेयर
+GenericName[hu]=Zenelejátszó
+GenericName[is]=Tónlistarforrit
+GenericName[it]=Lettore musicale
+GenericName[ja]=ミュージックプレーヤ
+GenericName[kk]=Музыка ойнатқышы
+GenericName[km]=កម្មវិធី​ចាក់​តន្ត្រី
+GenericName[ko]=음악 재생기
+GenericName[lt]=Muzikos grotuvas
+GenericName[mk]=Изведувач на музика
+GenericName[nb]=Musikkavspiller
+GenericName[nds]=Musikafspeler
+GenericName[ne]=सङ्गित प्लेयर
+GenericName[nl]=Muziekspeler
+GenericName[nn]=Musikkspelar
+GenericName[pa]=ਸੰਗੀਤ ਵਾਜਾ
+GenericName[pl]=Odtwarzacz muzyki
+GenericName[pt]=Leitor de Música
+GenericName[pt_BR]=Músicas
+GenericName[ro]=Program de redare muzică
+GenericName[ru]=Проигрыватель
+GenericName[sk]=Prehrávač hudby
+GenericName[sl]=Glasbeni predvajalnik
+GenericName[sr]=Музички плејер
+GenericName[sr@Latn]=Muzički plejer
+GenericName[sv]=Musikspelare
+GenericName[ta]=இசை இயக்கி
+GenericName[tg]=Бозингари Мусиқӣ
+GenericName[th]=โปรแกรมเล่นดนตรี
+GenericName[tr]=Müzik Çalar
+GenericName[uk]=Програвач музики
+GenericName[uz]=Musiqa pleyer
+GenericName[uz@cyrillic]=Мусиқа плейер
+GenericName[zh_CN]=音乐播放器
+GenericName[zh_HK]=音樂播放器
+GenericName[zh_TW]=音樂播放器
+MimeType=application/x-ogg;audio/mpeg;audio/mpegurl;audio/vorbis;audio/x-adpcm;audio/x-flac;audio/x-matroska;audio/x-mp2;audio/x-mp3;audio/x-mpegurl;audio/x-musepack;audio/x-oggflac;audio/x-speex;audio/x-vorbis;audio/x-wav;
+Categories=Qt;KDE;AudioVideo;
diff --git a/juk/juk.h b/juk/juk.h
new file mode 100644
index 00000000..41f3150a
--- /dev/null
+++ b/juk/juk.h
@@ -0,0 +1,100 @@
+/***************************************************************************
+ begin : Mon Feb 4 23:40:41 EST 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef JUK_H
+#define JUK_H
+
+#include <kmainwindow.h>
+
+#include "playermanager.h"
+
+class QTimer;
+class QListViewItem;
+
+class KToggleAction;
+class KSelectAction;
+class KToolBarPopupAction;
+class KGlobalAccel;
+
+class SliderAction;
+class StatusLabel;
+class SystemTray;
+class PlayerManager;
+class PlaylistSplitter;
+
+class JuK : public KMainWindow
+{
+ Q_OBJECT
+
+public:
+ JuK(QWidget* parent = 0, const char *name = 0);
+ virtual ~JuK();
+ virtual KActionCollection *actionCollection() const;
+
+private:
+ void setupLayout();
+ void setupActions();
+ void setupSystemTray();
+ void setupGlobalAccels();
+ void createDirs();
+
+ void keyPressEvent(QKeyEvent *);
+
+ /**
+ * readSettings() is separate from readConfig() in that it contains settings
+ * that need to be read before the GUI is setup.
+ */
+ void readSettings();
+ void readConfig();
+ void saveConfig();
+
+ virtual bool queryExit();
+ virtual bool queryClose();
+
+private slots:
+ void slotShowHide();
+ void slotAboutToQuit();
+ void slotQuit();
+ void slotToggleSystemTray(bool enabled);
+ void slotEditKeys();
+ void slotConfigureTagGuesser();
+ void slotConfigureFileRenamer();
+ void slotUndo();
+ void slotCheckAlbumNextAction(bool albumRandomEnabled);
+ void slotProcessArgs();
+
+private:
+ PlaylistSplitter *m_splitter;
+ StatusLabel *m_statusLabel;
+ SystemTray *m_systemTray;
+
+ SliderAction *m_sliderAction;
+ KToggleAction *m_randomPlayAction;
+ KToggleAction *m_toggleSystemTrayAction;
+ KToggleAction *m_toggleDockOnCloseAction;
+ KToggleAction *m_togglePopupsAction;
+ KToggleAction *m_toggleSplashAction;
+ KToggleAction *m_loopPlaylistAction;
+ KSelectAction *m_outputSelectAction;
+
+ PlayerManager *m_player;
+ KGlobalAccel *m_accel;
+
+ bool m_startDocked;
+ bool m_showSplash;
+ bool m_shuttingDown;
+};
+
+#endif
diff --git a/juk/jukIface.h b/juk/jukIface.h
new file mode 100644
index 00000000..e119b25e
--- /dev/null
+++ b/juk/jukIface.h
@@ -0,0 +1,108 @@
+#ifndef JUKIFACE_H
+#define JUKIFACE_H
+
+#include <dcopobject.h>
+#include <qstringlist.h>
+#include <qpixmap.h>
+
+class CollectionIface : public DCOPObject
+{
+ K_DCOP
+k_dcop:
+ void openFile(const QString &file) { open(file); }
+ void openFile(const QStringList &files) { open(files); }
+ void openFile(const QString &playlist, const QString &file) { open(playlist, file); }
+ void openFile(const QString &playlist, const QStringList &files) { open(playlist, files); }
+
+ virtual QStringList playlists() const = 0;
+ virtual void createPlaylist(const QString &) = 0;
+ virtual void remove() = 0;
+
+ virtual void removeTrack(const QString &playlist, const QString &file) { removeTrack(playlist, QStringList(file)); }
+ virtual void removeTrack(const QString &playlist, const QStringList &files) = 0;
+
+ virtual QString playlist() const = 0;
+ virtual QString playingPlaylist() const = 0;
+ virtual void setPlaylist(const QString &playlist) = 0;
+
+ virtual QStringList playlistTracks(const QString &playlist) const = 0;
+ virtual QString trackProperty(const QString &file, const QString &property) const = 0;
+
+ virtual QPixmap trackCover(const QString &file, const QString &size = "Small") const = 0;
+
+protected:
+ CollectionIface() : DCOPObject("Collection") {}
+ virtual void open(const QStringList &files) = 0;
+ virtual void open(const QString &playlist, const QStringList &files) = 0;
+};
+
+class PlayerIface : public DCOPObject
+{
+ K_DCOP
+k_dcop:
+ virtual bool playing() const = 0;
+ virtual bool paused() const = 0;
+ virtual float volume() const = 0;
+ virtual int status() const = 0;
+
+ virtual QStringList trackProperties() = 0;
+ virtual QString trackProperty(const QString &property) const = 0;
+ virtual QPixmap trackCover(const QString &size = "Small") const = 0;
+
+ virtual QString currentFile() const
+ {
+ return trackProperty("Path");
+ }
+
+ virtual void play() = 0;
+ virtual void play(const QString &file) = 0;
+ virtual void pause() = 0;
+ virtual void stop() = 0;
+ virtual void playPause() = 0;
+
+ virtual void back() = 0;
+ virtual void forward() = 0;
+ virtual void seekBack() = 0;
+ virtual void seekForward() = 0;
+
+ virtual void volumeUp() = 0;
+ virtual void volumeDown() = 0;
+ virtual void mute() = 0;
+ virtual void setVolume(float volume) = 0;
+ virtual void seek(int time) = 0;
+
+ virtual QString playingString() const = 0;
+ virtual int currentTime() const = 0;
+ virtual int totalTime() const = 0;
+
+ /**
+ * @return The current player mode.
+ * @see setRandomPlayMode()
+ */
+ virtual QString randomPlayMode() const = 0;
+
+ /**
+ * Sets the player mode to one of normal, random play, or album
+ * random play, depending on @p randomMode.
+ * "NoRandom" -> Normal
+ * "Random" -> Random
+ * "AlbumRandom" -> Album Random
+ */
+ virtual void setRandomPlayMode(const QString &randomMode) = 0;
+
+protected:
+ PlayerIface() : DCOPObject("Player") {}
+};
+
+class SearchIface : public DCOPObject
+{
+ K_DCOP
+k_dcop:
+ virtual QString searchText() const = 0;
+ virtual void setSearchText(const QString &text) = 0;
+
+protected:
+ SearchIface() : DCOPObject("Search") {}
+};
+
+#endif
diff --git a/juk/jukservicemenu.desktop b/juk/jukservicemenu.desktop
new file mode 100644
index 00000000..942a6a45
--- /dev/null
+++ b/juk/jukservicemenu.desktop
@@ -0,0 +1,63 @@
+[Desktop Entry]
+ServiceTypes=application/ogg,audio/vorbis,audio/x-mp3,audio/x-flac,audio/x-oggflac,audio/x-musepack
+Actions=addToCollection
+
+[Desktop Action addToCollection]
+Name=Add to JuK Collection
+Name[bn]=জুক সংকলনে যোগ করো
+Name[br]=Ouzhpennañ d'an dastumad JuK
+Name[bs]=Dodaj u JuK kolekciju
+Name[ca]=Afegeix a la col·lecció JuK
+Name[cs]=Přidat do JuK kolekce
+Name[cy]=Ychwanegu i Gasgliad JuK
+Name[da]=Tilføj til JuK-samling
+Name[de]=Zur JuK-Kollektion hinzufügen
+Name[el]=Προσθήκη στη συλλογή του JuK
+Name[eo]=Aldoni al JuK-kolekto
+Name[es]=Añadir a colección de JuK
+Name[et]=Lisa JuKi kollektsiooni
+Name[eu]=Gehitu Juk-en bildumara
+Name[fa]=افزودن به مجموعۀ JuK
+Name[fi]=Lisää JuKin kokoelmalistaan
+Name[fr]=Ajouter à la collection de JuK
+Name[ga]=Cuir le bailiúchán JuK
+Name[gl]=Engadir á colección de JuK
+Name[he]=הוסף לאוסף של Juk
+Name[hi]=ज्यूक संग्रह में जोड़ें
+Name[hu]=Hozzáadás egy JuK-válogatáshoz
+Name[is]=Bæta við JuK safnið
+Name[it]=Aggiungi alla collezione di JuK
+Name[ja]=JuK コレクションに追加
+Name[kk]=JuK жинағына қосу
+Name[km]=បន្ថែម​ទៅ​ការ​ប្រមូលផ្ដុំ JuK
+Name[ko]=JuK 모음집에 추가하기
+Name[lt]=Pridėti prie JuK kolekcijos
+Name[mk]=Додај во колекција на JuK
+Name[ms]=Tambah ke koleksi JuK
+Name[nb]=Legg til JuK-samling
+Name[nds]=Na de JuK-Sammeln tofögen
+Name[ne]=JuK सङ्कलनमा थप्नुहोस्
+Name[nl]=Toevoegen aan JuK-collectie
+Name[nn]=Legg til JuK-samlinga
+Name[pa]=JuK ਭੰਡਾਰ 'ਚ ਸ਼ਾਮਿਲ
+Name[pl]=Dodaj do kolekcji JuK
+Name[pt]=Adicionar à Colecção do JuK
+Name[pt_BR]=Adicionar à Coleção do Juk
+Name[ro]=Adaugă în colecţia JuK
+Name[ru]=Добавить в коллекцию JuK
+Name[sk]=Pridať do kolekcie JuK
+Name[sl]=Dodaj v zbirko JuK
+Name[sr]=Додај у JuK колекцију
+Name[sr@Latn]=Dodaj u JuK kolekciju
+Name[sv]=Lägg till i Juk-samlingslista
+Name[ta]=JuK திரட்டிக்கு சேர்
+Name[tg]=Иловакунӣ ба Маҷмӯаи JuK
+Name[th]=เพิ่มลงชุดสะสมของ JuK
+Name[tr]=JuK Koleksiyonuna Ekle
+Name[uk]=Додати до збірки JuK
+Name[uz]=JuK toʻplamiga qoʻshish
+Name[uz@cyrillic]=JuK тўпламига қўшиш
+Name[zh_CN]=添加到 JuK 收藏
+Name[zh_TW]=新增到 JuK 收藏清單
+Icon=juk
+Exec=dcop juk Collection "openFile(QStringList)" [ %U ]
diff --git a/juk/jukui-rtl.rc b/juk/jukui-rtl.rc
new file mode 100644
index 00000000..d8eb65ae
--- /dev/null
+++ b/juk/jukui-rtl.rc
@@ -0,0 +1,109 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="juk" version="8">
+<MenuBar>
+ <Menu name="file" noMerge="1"><text>&amp;File</text>
+ <Action name="file_new"/>
+ <Action name="file_open"/>
+ <Action name="openDirectory"/>
+
+ <Separator/>
+
+ <Action name="renamePlaylist"/>
+ <Action name="editSearch"/>
+ <Action name="duplicatePlaylist"/>
+ <Action name="reloadPlaylist"/>
+ <Action name="deleteItemPlaylist"/>
+
+ <Separator/>
+
+ <Action name="file_save"/>
+ <Action name="file_save_as"/>
+
+ <Separator/>
+
+ <Action name="file_quit"/>
+ </Menu>
+ <Menu name="view" noMerge="1"><text>&amp;View</text>
+ <Action name="showSearch"/>
+ <Action name="showEditor"/>
+ <Action name="showHistory"/>
+ <Action name="showUpcoming"/>
+ <Action name="showColumns"/>
+ <Action name="resizeColumnsManually"/>
+
+ <Separator/>
+
+ <Action name="viewModeMenu"/>
+ </Menu>
+ <Menu name="player"><text>&amp;Player</text>
+ <Action name="actionMenu"/>
+
+ <Action name="loopPlaylist"/>
+
+ <Separator/>
+
+ <Action name="play"/>
+ <Action name="pause"/>
+ <Action name="stop"/>
+ <Action name="forward"/>
+ <Action name="back"/>
+
+ <Separator/>
+
+ <Action name="forwardAlbum"/>
+ </Menu>
+ <Menu name="playlist"><text>&amp;Tagger</text>
+ <Action name="saveItem"/>
+ <Action name="removeItem"/>
+ <Action name="refresh"/>
+
+ <Separator/>
+
+ <Action name="guessTag"/>
+ <Action name="coverManager"/>
+ <Action name="renameFile"/>
+ </Menu>
+ <Menu name="settings"><text>&amp;Settings</text>
+ <Action name="showSplashScreen" append="show_merge"/>
+ <Action name="toggleSystemTray" append="show_merge"/>
+ <Action name="dockOnClose" append="show_merge"/>
+ <Action name="togglePopups" append="show_merge"/>
+ <!-- <Action name="saveUpcomingTracks" append="show_merge"/> -->
+ <Action name="tagGuesserConfig" append="save_merge"/>
+ <Action name="fileRenamerConfig" append="save_merge"/>
+ <Action name="outputSelect" append="save_merge"/>
+ </Menu>
+</MenuBar>
+
+<ToolBar name="mainToolBar" hidden="true" noMerge="1"><text>Main Toolbar</text>
+ <Action name="file_new"/>
+ <Action name="file_open"/>
+ <Action name="file_save"/>
+
+ <Separator lineSeparator="true"/>
+
+ <Action name="edit_cut"/>
+ <Action name="edit_copy"/>
+ <Action name="edit_paste"/>
+
+ <Separator lineSeparator="true"/>
+
+ <Action name="showSearch"/>
+ <Action name="showEditor"/>
+ <Action name="showNowPlaying"/>
+</ToolBar>
+
+<ToolBar name="playToolBar" noMerge="1"><text>Play Toolbar</text>
+
+ <Action name="trackPositionAction"/>
+ <Action name="forward"/>
+ <Action name="back"/>
+ <Separator lineSeparator="false"/>
+
+
+ <Action name="stop"/>
+ <Action name="pause"/>
+ <Action name="play"/>
+
+</ToolBar>
+</kpartgui>
diff --git a/juk/jukui.rc b/juk/jukui.rc
new file mode 100644
index 00000000..453739da
--- /dev/null
+++ b/juk/jukui.rc
@@ -0,0 +1,109 @@
+<!-- PLEASE UPDATE jukui-rtl.rc WHEN UPDATING THIS FILE -->
+
+<!DOCTYPE kpartgui>
+<kpartgui name="juk" version="8">
+<MenuBar>
+ <Menu name="file" noMerge="1"><text>&amp;File</text>
+ <Action name="file_new"/>
+ <Action name="file_open"/>
+ <Action name="openDirectory"/>
+
+ <Separator/>
+
+ <Action name="renamePlaylist"/>
+ <Action name="editSearch"/>
+ <Action name="duplicatePlaylist"/>
+ <Action name="reloadPlaylist"/>
+ <Action name="deleteItemPlaylist"/>
+
+ <Separator/>
+
+ <Action name="file_save"/>
+ <Action name="file_save_as"/>
+
+ <Separator/>
+
+ <Action name="file_quit"/>
+ </Menu>
+ <Menu name="view" noMerge="1"><text>&amp;View</text>
+ <Action name="showSearch"/>
+ <Action name="showEditor"/>
+ <Action name="showHistory"/>
+ <Action name="showUpcoming"/>
+ <Action name="showColumns"/>
+ <Action name="resizeColumnsManually"/>
+
+ <Separator/>
+
+ <Action name="viewModeMenu"/>
+ </Menu>
+ <Menu name="player"><text>&amp;Player</text>
+ <Action name="actionMenu"/>
+
+ <Action name="loopPlaylist"/>
+
+ <Separator/>
+
+ <Action name="play"/>
+ <Action name="pause"/>
+ <Action name="stop"/>
+ <Action name="forward"/>
+ <Action name="back"/>
+
+ <Separator/>
+
+ <Action name="forwardAlbum"/>
+ </Menu>
+ <Menu name="playlist"><text>&amp;Tagger</text>
+ <Action name="saveItem"/>
+ <Action name="removeItem"/>
+ <Action name="refresh"/>
+
+ <Separator/>
+
+ <Action name="guessTag"/>
+ <Action name="coverManager"/>
+ <Action name="renameFile"/>
+ </Menu>
+ <Menu name="settings"><text>&amp;Settings</text>
+ <Action name="showSplashScreen" append="show_merge"/>
+ <Action name="toggleSystemTray" append="show_merge"/>
+ <Action name="dockOnClose" append="show_merge"/>
+ <Action name="togglePopups" append="show_merge"/>
+ <!-- <Action name="saveUpcomingTracks" append="show_merge"/> -->
+ <Action name="tagGuesserConfig" append="save_merge"/>
+ <Action name="fileRenamerConfig" append="save_merge"/>
+ <Action name="outputSelect" append="save_merge"/>
+ </Menu>
+</MenuBar>
+
+<ToolBar name="mainToolBar" hidden="true" noMerge="1"><text>Main Toolbar</text>
+ <Action name="file_new"/>
+ <Action name="file_open"/>
+ <Action name="file_save"/>
+
+ <Separator lineSeparator="true"/>
+
+ <Action name="edit_cut"/>
+ <Action name="edit_copy"/>
+ <Action name="edit_paste"/>
+
+ <Separator lineSeparator="true"/>
+
+ <Action name="showSearch"/>
+ <Action name="showEditor"/>
+ <Action name="showNowPlaying"/>
+</ToolBar>
+
+<ToolBar name="playToolBar" noMerge="1"><text>Play Toolbar</text>
+ <Action name="play"/>
+ <Action name="pause"/>
+ <Action name="stop"/>
+
+ <Separator lineSeparator="false"/>
+
+ <Action name="back"/>
+ <Action name="forward"/>
+ <Action name="trackPositionAction"/>
+</ToolBar>
+</kpartgui>
diff --git a/juk/k3bexporter.cpp b/juk/k3bexporter.cpp
new file mode 100644
index 00000000..a08ddcfe
--- /dev/null
+++ b/juk/k3bexporter.cpp
@@ -0,0 +1,298 @@
+/***************************************************************************
+ begin : Mon May 31 2004
+ copyright : (C) 2004 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kprocess.h>
+#include <kmessagebox.h>
+#include <kurl.h>
+#include <klocale.h>
+#include <kaction.h>
+#include <kactioncollection.h>
+#include <kstandarddirs.h>
+#include <kiconloader.h>
+#include <kapplication.h>
+
+#include <qcstring.h>
+#include <qmap.h>
+
+#include <dcopref.h>
+#include <dcopclient.h>
+
+#include "k3bexporter.h"
+#include "playlistitem.h"
+#include "playlist.h"
+#include "playlistbox.h"
+#include "actioncollection.h"
+
+using ActionCollection::actions;
+
+// static member variable definition
+
+PlaylistAction *K3bExporter::m_action = 0;
+
+// Special KAction subclass used to automatically call a slot when activated,
+// depending on the visible playlist at the time. In other words, use *one*
+// instance of this action for many playlists.
+//
+// This is used to handle some actions in the Playlist context menu.
+class PlaylistAction : public KAction
+{
+ public:
+ PlaylistAction(const char *name,
+ const QString &userText,
+ const QIconSet &pix,
+ const char *slot,
+ const KShortcut &cut = 0) :
+ KAction(userText, pix, cut, 0 /* receiver */, 0 /* slot */, actions(), name),
+ m_slot(slot)
+ {
+ }
+
+ typedef QMap<const Playlist *, QObject *> PlaylistRecipientMap;
+
+ /**
+ * Defines a QObject to call (using the m_slot SLOT) when an action is
+ * emitted from a Playlist.
+ */
+ void addCallMapping(const Playlist *p, QObject *obj)
+ {
+ m_playlistRecipient[p] = obj;
+ }
+
+ protected slots:
+ void slotActivated()
+ {
+ kdDebug(65432) << k_funcinfo << endl;
+
+ // Determine current playlist, and call its slot.
+ Playlist *p = PlaylistCollection::instance()->visiblePlaylist();
+ if(!p)
+ return;
+
+ // Make sure we're supposed to notify someone about this playlist.
+ QObject *recipient = m_playlistRecipient[p];
+ if(!recipient)
+ return;
+
+ // Invoke the slot using some trickery.
+ // XXX: Use the QMetaObject to do this in Qt 4.
+ connect(this, SIGNAL(activated()), recipient, m_slot);
+ emit(activated());
+ disconnect(this, SIGNAL(activated()), recipient, m_slot);
+ }
+
+ private:
+ QCString m_slot;
+ PlaylistRecipientMap m_playlistRecipient;
+};
+
+K3bExporter::K3bExporter(Playlist *parent) : PlaylistExporter(parent), m_parent(parent)
+{
+}
+
+KAction *K3bExporter::action()
+{
+ if(!m_action && !KStandardDirs::findExe("k3b").isNull()) {
+ m_action = new PlaylistAction(
+ "export_to_k3b",
+ i18n("Add Selected Items to Audio or Data CD"),
+ SmallIconSet("k3b"),
+ SLOT(slotExport())
+ );
+
+ m_action->setShortcutConfigurable(false);
+ }
+
+ // Tell the action to let us know when it is activated when
+ // m_parent is the visible playlist. This allows us to reuse the
+ // action to avoid duplicate entries in KActionCollection.
+ if(m_action)
+ m_action->addCallMapping(m_parent, this);
+
+ return m_action;
+}
+
+void K3bExporter::exportPlaylistItems(const PlaylistItemList &items)
+{
+ if(items.empty())
+ return;
+
+ DCOPClient *client = DCOPClient::mainClient();
+ QCString appId, appObj;
+ QByteArray data;
+
+ if(!client->findObject("k3b-*", "K3bInterface", "", data, appId, appObj))
+ exportViaCmdLine(items);
+ else {
+ DCOPRef ref(appId, appObj);
+ exportViaDCOP(items, ref);
+ }
+}
+
+void K3bExporter::slotExport()
+{
+ if(m_parent)
+ exportPlaylistItems(m_parent->selectedItems());
+}
+
+void K3bExporter::exportViaCmdLine(const PlaylistItemList &items)
+{
+ K3bOpenMode mode = openMode();
+ QCString cmdOption;
+
+ switch(mode) {
+ case AudioCD:
+ cmdOption = "--audiocd";
+ break;
+
+ case DataCD:
+ cmdOption = "--datacd";
+ break;
+
+ case Abort:
+ return;
+ }
+
+ KProcess *process = new KProcess;
+
+ *process << "k3b";
+ *process << cmdOption;
+
+ PlaylistItemList::ConstIterator it;
+ for(it = items.begin(); it != items.end(); ++it)
+ *process << (*it)->file().absFilePath();
+
+ if(!process->start(KProcess::DontCare))
+ KMessageBox::error(m_parent, i18n("Unable to start K3b."));
+}
+
+void K3bExporter::exportViaDCOP(const PlaylistItemList &items, DCOPRef &ref)
+{
+ QValueList<DCOPRef> projectList;
+ DCOPReply projectListReply = ref.call("projects()");
+
+ if(!projectListReply.get<QValueList<DCOPRef> >(projectList, "QValueList<DCOPRef>")) {
+ DCOPErrorMessage();
+ return;
+ }
+
+ if(projectList.count() == 0 && !startNewK3bProject(ref))
+ return;
+
+ KURL::List urlList;
+ PlaylistItemList::ConstIterator it;
+
+ for(it = items.begin(); it != items.end(); ++it) {
+ KURL item;
+
+ item.setPath((*it)->file().absFilePath());
+ urlList.append(item);
+ }
+
+ if(!ref.send("addUrls(KURL::List)", DCOPArg(urlList, "KURL::List"))) {
+ DCOPErrorMessage();
+ return;
+ }
+}
+
+void K3bExporter::DCOPErrorMessage()
+{
+ KMessageBox::error(m_parent, i18n("There was a DCOP communication error with K3b."));
+}
+
+bool K3bExporter::startNewK3bProject(DCOPRef &ref)
+{
+ QCString request;
+ K3bOpenMode mode = openMode();
+
+ switch(mode) {
+ case AudioCD:
+ request = "createAudioCDProject()";
+ break;
+
+ case DataCD:
+ request = "createDataCDProject()";
+ break;
+
+ case Abort:
+ return false;
+ }
+
+ if(!ref.send(request)) {
+ DCOPErrorMessage();
+ return false;
+ }
+
+ return true;
+}
+
+K3bExporter::K3bOpenMode K3bExporter::openMode()
+{
+ int reply = KMessageBox::questionYesNoCancel(
+ m_parent,
+ i18n("Create an audio mode CD suitable for CD players, or a data "
+ "mode CD suitable for computers and other digital music "
+ "players?"),
+ i18n("Create K3b Project"),
+ i18n("Audio Mode"),
+ i18n("Data Mode")
+ );
+
+ switch(reply) {
+ case KMessageBox::Cancel:
+ return Abort;
+
+ case KMessageBox::No:
+ return DataCD;
+
+ case KMessageBox::Yes:
+ return AudioCD;
+ }
+
+ return Abort;
+}
+
+K3bPlaylistExporter::K3bPlaylistExporter(PlaylistBox *parent) : K3bExporter(0),
+ m_playlistBox(parent)
+{
+}
+
+KAction *K3bPlaylistExporter::action()
+{
+ if(!KStandardDirs::findExe("k3b").isNull()) {
+ return new KAction(
+ i18n("Add Playlist to Audio or Data CD"),
+ SmallIconSet("k3b"),
+ 0,
+ this,
+ SLOT(slotExport()),
+ actions(),
+ "export_playlist_to_k3b"
+ );
+ }
+
+ return 0;
+}
+
+void K3bPlaylistExporter::slotExport()
+{
+ if(m_playlistBox) {
+ setPlaylist(m_playlistBox->visiblePlaylist());
+ exportPlaylistItems(m_playlistBox->visiblePlaylist()->items());
+ }
+}
+
+#include "k3bexporter.moc"
+
+// vim: set et sw=4 ts=4:
diff --git a/juk/k3bexporter.h b/juk/k3bexporter.h
new file mode 100644
index 00000000..7f23fb0e
--- /dev/null
+++ b/juk/k3bexporter.h
@@ -0,0 +1,94 @@
+/***************************************************************************
+ begin : Mon May 31 2004
+ copyright : (C) 2004 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef K3BEXPORTER_H
+#define K3BEXPORTER_H
+
+#include "playlistexporter.h"
+#include "playlistitem.h"
+
+class QWidget;
+class DCOPRef;
+class PlaylistBox;
+class PlaylistAction;
+
+/**
+ * Class that will export the selected items of a playlist to K3b.
+ */
+class K3bExporter : public PlaylistExporter
+{
+ Q_OBJECT
+
+public:
+ K3bExporter(Playlist *parent = 0);
+
+ /**
+ * Returns a KAction that can be used to invoke the export.
+ *
+ * @return action used to start the export.
+ */
+ virtual KAction *action();
+
+ Playlist *playlist() const { return m_parent; }
+ void setPlaylist(Playlist *playlist) { m_parent = playlist; }
+
+protected:
+ void exportPlaylistItems(const PlaylistItemList &items);
+
+private slots:
+ void slotExport();
+
+private:
+ enum K3bOpenMode { AudioCD, DataCD, Abort };
+
+ // Private method declarations
+ void exportViaCmdLine(const PlaylistItemList &items);
+ void exportViaDCOP(const PlaylistItemList &items, DCOPRef &ref);
+ void DCOPErrorMessage();
+ bool startNewK3bProject(DCOPRef &ref);
+ K3bOpenMode openMode();
+
+ // Private member variable declarations
+ Playlist *m_parent;
+
+ // Static member used to avoid adding more than one action to KDE's
+ // action list.
+ static PlaylistAction *m_action;
+};
+
+/**
+ * Class to export EVERY item in a playlist to K3b. Used with the PlaylistBox
+ * class to implement context-menus there.
+ *
+ * @see PlaylistBox
+ */
+class K3bPlaylistExporter : public K3bExporter
+{
+ Q_OBJECT
+public:
+ K3bPlaylistExporter(PlaylistBox *parent = 0);
+
+ virtual KAction *action();
+
+private slots:
+ void slotExport();
+
+private:
+ PlaylistBox *m_playlistBox;
+};
+
+#endif /* K3BEXPORTER_H */
+
+// vim: set et ts=4 sw=4:
diff --git a/juk/keydialog.cpp b/juk/keydialog.cpp
new file mode 100644
index 00000000..2fe429c1
--- /dev/null
+++ b/juk/keydialog.cpp
@@ -0,0 +1,204 @@
+/***************************************************************************
+ begin : Tue Mar 11 19:00:00 CET 2003
+ copyright : (C) 2003 by Stefan Asserhall
+ email : stefan.asserhall@telia.com
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "keydialog.h"
+
+#include <kconfig.h>
+#include <klocale.h>
+
+#include <qradiobutton.h>
+#include <qvbox.h>
+#include <qwhatsthis.h>
+
+
+// Table of shortcut keys for each action, key group and three or four button modifier
+
+const KeyDialog::KeyInfo KeyDialog::keyInfo[] = {
+ { "PlayPause",
+ { { KShortcut::null(), KShortcut::null() },
+ { Qt::CTRL+Qt::ALT+Qt::Key_P, KKey::QtWIN+Qt::ALT+Qt::Key_P },
+ { Qt::Key_MediaPlay, Qt::Key_MediaPlay } } },
+ { "Stop",
+ { { KShortcut::null(), KShortcut::null() },
+ { Qt::CTRL+Qt::ALT+Qt::Key_S, KKey::QtWIN+Qt::ALT+Qt::Key_S },
+ { Qt::Key_MediaStop, Qt::Key_MediaStop } } },
+ { "Back",
+ { { KShortcut::null(), KShortcut::null() },
+ { Qt::CTRL+Qt::ALT+Qt::Key_Left, KKey::QtWIN+Qt::ALT+Qt::Key_Left },
+ { Qt::Key_MediaPrev, Qt::Key_MediaPrev } } },
+ { "Forward",
+ { { KShortcut::null(), KShortcut::null() },
+ { Qt::CTRL+Qt::ALT+Qt::Key_Right, KKey::QtWIN+Qt::ALT+Qt::Key_Right },
+ { Qt::Key_MediaNext, Qt::Key_MediaNext } } },
+ { "ForwardAlbum",
+ { { KShortcut::null(), KShortcut::null() },
+ { Qt::CTRL+Qt::ALT+Qt::Key_Up, KKey::QtWIN+Qt::ALT+Qt::Key_Up },
+ { Qt::CTRL+Qt::Key_MediaNext, Qt::CTRL+Qt::Key_MediaNext } } },
+ { "SeekBack",
+ { { KShortcut::null(), KShortcut::null() },
+ { Qt::CTRL+Qt::SHIFT+Qt::ALT+Qt::Key_Left, KKey::QtWIN+Qt::SHIFT+Qt::ALT+Qt::Key_Left },
+ { Qt::SHIFT+Qt::Key_MediaPrev, Qt::SHIFT+Qt::Key_MediaPrev } } },
+ { "SeekForward",
+ { { KShortcut::null(), KShortcut::null() },
+ { Qt::CTRL+Qt::SHIFT+Qt::ALT+Qt::Key_Right, KKey::QtWIN+Qt::SHIFT+Qt::ALT+Qt::Key_Right },
+ { Qt::SHIFT+Qt::Key_MediaNext, Qt::SHIFT+Qt::Key_MediaNext } } },
+ { "VolumeUp",
+ { { KShortcut::null(), KShortcut::null() },
+ { Qt::CTRL+Qt::ALT+Qt::SHIFT+Qt::Key_Up, KKey::QtWIN+Qt::ALT+Qt::SHIFT+Qt::Key_Up },
+ { Qt::Key_VolumeUp, Qt::Key_VolumeUp } } },
+ { "VolumeDown",
+ { { KShortcut::null(), KShortcut::null() },
+ { Qt::CTRL+Qt::ALT+Qt::SHIFT+Qt::Key_Down, KKey::QtWIN+Qt::ALT+Qt::SHIFT+Qt::Key_Down },
+ { Qt::Key_VolumeDown, Qt::Key_VolumeDown } } },
+ { "Mute",
+ { { KShortcut::null(), KShortcut::null() },
+ { Qt::CTRL+Qt::ALT+Qt::Key_M, KKey::QtWIN+Qt::ALT+Qt::Key_M },
+ { Qt::Key_VolumeMute, Qt::Key_VolumeMute } } },
+ { "ShowHide",
+ { { KShortcut::null(), KShortcut::null() },
+ { KShortcut::null(), KShortcut::null() },
+ { KShortcut::null(), KShortcut::null() } } }
+};
+
+const uint KeyDialog::keyInfoCount = sizeof(KeyDialog::keyInfo) / sizeof(KeyDialog::keyInfo[0]);
+
+KeyDialog::KeyDialog(KGlobalAccel *keys, KActionCollection *actionCollection,
+ QWidget *parent, const char *name)
+ : KDialogBase(parent, name, true, i18n("Configure Shortcuts"), Default | Ok | Cancel, Ok)
+{
+ // Read key group from configuration
+
+ int selectedButton;
+
+ KConfigGroup config(KGlobal::config(), "Shortcuts");
+ selectedButton = config.readNumEntry("GlobalKeys", StandardKeys);
+
+ // Create widgets for key chooser - widget stack used to replace key chooser
+
+ QVBox *vbox = new QVBox(this);
+ vbox->setSpacing(KDialog::spacingHint());
+ m_widgetStack = new QWidgetStack(vbox);
+
+ vbox->setStretchFactor(m_widgetStack, 1);
+
+ // Create buttons to select key group
+
+ m_group = new QHButtonGroup(i18n("Global Shortcuts"), vbox);
+ new QRadioButton(i18n("&No keys"), m_group);
+ new QRadioButton(i18n("&Standard keys"), m_group);
+ new QRadioButton(i18n("&Multimedia keys"), m_group);
+ connect(m_group, SIGNAL(clicked(int)), this, SLOT(slotKeys(int)));
+ QWhatsThis::add(m_group,
+ i18n("Here you can select the keys used as global shortcuts to control the player"));
+
+ // Create the key chooser
+
+ setMainWidget(vbox);
+ newDialog(keys, actionCollection, selectedButton);
+}
+
+KeyDialog::~KeyDialog()
+{
+
+}
+
+void KeyDialog::newDialog(KGlobalAccel *keys, KActionCollection *actionCollection,
+ int selectedButton)
+{
+ m_keys = keys;
+ m_actionCollection = actionCollection;
+
+ // Create key chooser and show it in the widget stack
+ m_pKeyChooser = new KKeyChooser(keys, this);
+ m_pKeyChooser->insert(actionCollection);
+ m_widgetStack->addWidget(m_pKeyChooser);
+ m_widgetStack->raiseWidget(m_pKeyChooser);
+ m_group->setButton(selectedButton);
+
+ connect(this, SIGNAL(defaultClicked()), this, SLOT(slotDefault()));
+}
+
+int KeyDialog::configure()
+{
+ // Show the dialog and save configuration if accepted
+
+ int retcode = exec();
+ if(retcode == Accepted) {
+
+ KConfigGroup config(KGlobal::config(), "Shortcuts");
+ config.writeEntry("GlobalKeys", m_group->id(m_group->selected()));
+ KGlobal::config()->sync();
+
+ m_pKeyChooser->save();
+ }
+ return retcode;
+}
+
+void KeyDialog::slotKeys(int group)
+{
+ bool fourModKeys = KGlobalAccel::useFourModifierKeys();
+
+ // Set modifier keys according to key group and modifier keys
+
+ for(uint i = 0; i < keyInfoCount; i++)
+ m_keys->setShortcut(keyInfo[i].action, keyInfo[i].shortcut[group][fourModKeys]);
+
+ // Create a new key chooser to show the keys, and delete the old one
+
+ QWidget *w = m_widgetStack->visibleWidget();
+ newDialog(m_keys, m_actionCollection, group);
+ m_widgetStack->removeWidget(w);
+ delete w;
+}
+
+void KeyDialog::slotDefault()
+{
+ // Select default keys - standard key group
+
+ m_group->setButton(StandardKeys);
+ m_pKeyChooser->allDefault();
+}
+
+int KeyDialog::configure(KGlobalAccel *keys, KActionCollection *actionCollection,
+ QWidget *parent)
+{
+ // Create and show dialog - update connections if accepted
+
+ int retcode = KeyDialog(keys, actionCollection, parent).configure();
+
+ if(retcode == Accepted)
+ keys->updateConnections();
+ return retcode;
+}
+
+void KeyDialog::insert(KGlobalAccel *keys, const QString &action, const QString &label,
+ const QObject *objSlot, const char *methodSlot)
+{
+ KShortcut def3 = KShortcut::null();
+ KShortcut def4 = KShortcut::null();
+
+ // Find and insert a standard key
+
+ for(uint i = 0; i < keyInfoCount; i++) {
+ if(keyInfo[i].action == action) {
+ def3 = keyInfo[i].shortcut[StandardKeys][0];
+ def4 = keyInfo[i].shortcut[StandardKeys][1];
+ break;
+ }
+ }
+ keys->insert(action, label, QString::null, def3, def4, objSlot, methodSlot);
+}
+
+#include "keydialog.moc"
diff --git a/juk/keydialog.h b/juk/keydialog.h
new file mode 100644
index 00000000..13d76d4f
--- /dev/null
+++ b/juk/keydialog.h
@@ -0,0 +1,85 @@
+/***************************************************************************
+ begin : Tue Mar 11 19:00:00 CET 2003
+ copyright : (C) 2003 by Stefan Asserhall
+ email : stefan.asserhall@telia.com
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef KEYDIALOG_H
+#define KEYDIALOG_H
+
+#include <kdeversion.h>
+#include <kglobalaccel.h>
+#include <kkeydialog.h>
+
+#include <qhbuttongroup.h>
+#include <qwidgetstack.h>
+
+class KeyDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Constructs a KeyDialog called @p name as a child of @p parent.
+ */
+ KeyDialog(KGlobalAccel *keys, KActionCollection *actionCollection, QWidget *parent = 0, const char* name = 0);
+
+ /**
+ * Destructor. Deletes all resources used by a KeyDialog object.
+ */
+ virtual ~KeyDialog();
+
+ /**
+ * This is a member function, provided to allow inserting both global
+ * accelerators and actions. It behaves essentially like the functions
+ * in KKeyDialog.
+ */
+ static int configure(KGlobalAccel *keys, KActionCollection *actionCollection, QWidget *parent = 0);
+
+ /**
+ * This is a member function, provided to create a global accelerator with
+ * standard keys. It behaves like the function in KGlobalAccel.
+ */
+ static void insert(KGlobalAccel *keys, const QString &action, const QString &label,
+ const QObject *objSlot, const char *methodSlot);
+
+private:
+
+ /**
+ * Groups of keys that can be selected in the dialog.
+ */
+ enum KeyGroup { NoKeys = 0, StandardKeys = 1, MultimediaKeys = 2 };
+
+ struct KeyInfo {
+ QString action;
+ KShortcut shortcut[3][2];
+ };
+
+ void newDialog(KGlobalAccel *keys, KActionCollection *actionCollection, int selectedButton = 0);
+ int configure();
+
+private slots:
+ void slotKeys(int group);
+ void slotDefault();
+
+private:
+ KActionCollection *m_actionCollection;
+ KGlobalAccel *m_keys;
+ KKeyChooser *m_pKeyChooser;
+ QHButtonGroup *m_group;
+ QWidgetStack *m_widgetStack;
+
+ static const KeyInfo keyInfo[];
+ static const uint keyInfoCount;
+};
+
+#endif // KEYDIALOG_H
diff --git a/juk/ktrm.cpp b/juk/ktrm.cpp
new file mode 100644
index 00000000..ae4a2e61
--- /dev/null
+++ b/juk/ktrm.cpp
@@ -0,0 +1,606 @@
+/***************************************************************************
+ copyright : (C) 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+ * USA *
+ ***************************************************************************/
+
+#include "ktrm.h"
+
+#if HAVE_MUSICBRAINZ
+
+#include <kapplication.h>
+#include <kresolver.h>
+#include <kprotocolmanager.h>
+#include <kurl.h>
+#include <kdebug.h>
+
+#include <qmutex.h>
+#include <qregexp.h>
+#include <qevent.h>
+#include <qobject.h>
+#include <qfile.h>
+
+#include <tunepimp/tp_c.h>
+#include <fixx11h.h>
+
+class KTRMLookup;
+
+extern "C"
+{
+#if HAVE_MUSICBRAINZ >= 4
+ static void TRMNotifyCallback(tunepimp_t pimp, void *data, TPCallbackEnum type, int fileId, TPFileStatus status);
+#else
+ static void TRMNotifyCallback(tunepimp_t pimp, void *data, TPCallbackEnum type, int fileId);
+#endif
+}
+
+/**
+ * This represents the main TunePimp instance and handles incoming requests.
+ */
+
+class KTRMRequestHandler
+{
+public:
+ static KTRMRequestHandler *instance()
+ {
+ static QMutex mutex;
+ mutex.lock();
+ static KTRMRequestHandler handler;
+ mutex.unlock();
+ return &handler;
+ }
+
+ int startLookup(KTRMLookup *lookup)
+ {
+ int id;
+
+ if(!m_fileMap.contains(lookup->file())) {
+#if HAVE_MUSICBRAINZ >= 4
+ id = tp_AddFile(m_pimp, QFile::encodeName(lookup->file()), 0);
+#else
+ id = tp_AddFile(m_pimp, QFile::encodeName(lookup->file()));
+#endif
+ m_fileMap.insert(lookup->file(), id);
+ }
+ else {
+ id = m_fileMap[lookup->file()];
+ tp_IdentifyAgain(m_pimp, id);
+ }
+ m_lookupMap[id] = lookup;
+ return id;
+ }
+
+ void endLookup(KTRMLookup *lookup)
+ {
+ tp_ReleaseTrack(m_pimp, tp_GetTrack(m_pimp, lookup->fileId()));
+ tp_Remove(m_pimp, lookup->fileId());
+ m_lookupMap.remove(lookup->fileId());
+ }
+
+ bool lookupMapContains(int fileId) const
+ {
+ m_lookupMapMutex.lock();
+ bool contains = m_lookupMap.contains(fileId);
+ m_lookupMapMutex.unlock();
+ return contains;
+ }
+
+ KTRMLookup *lookup(int fileId) const
+ {
+ m_lookupMapMutex.lock();
+ KTRMLookup *l = m_lookupMap[fileId];
+ m_lookupMapMutex.unlock();
+ return l;
+ }
+
+ void removeFromLookupMap(int fileId)
+ {
+ m_lookupMapMutex.lock();
+ m_lookupMap.remove(fileId);
+ m_lookupMapMutex.unlock();
+ }
+
+ const tunepimp_t &tunePimp() const
+ {
+ return m_pimp;
+ }
+
+protected:
+ KTRMRequestHandler()
+ {
+ m_pimp = tp_New("KTRM", "0.1");
+ //tp_SetDebug(m_pimp, true);
+ tp_SetTRMCollisionThreshold(m_pimp, 100);
+ tp_SetAutoSaveThreshold(m_pimp, -1);
+ tp_SetMoveFiles(m_pimp, false);
+ tp_SetRenameFiles(m_pimp, false);
+#if HAVE_MUSICBRAINZ >= 4
+ tp_SetFileNameEncoding(m_pimp, "UTF-8");
+#else
+ tp_SetUseUTF8(m_pimp, true);
+#endif
+ tp_SetNotifyCallback(m_pimp, TRMNotifyCallback, 0);
+
+ // Re-read proxy config.
+ KProtocolManager::reparseConfiguration();
+
+ if(KProtocolManager::useProxy()) {
+ // split code copied from kcm_kio.
+ QString noProxiesFor = KProtocolManager::noProxyFor();
+ QStringList noProxies = QStringList::split(QRegExp("[',''\t'' ']"), noProxiesFor);
+ bool useProxy = true;
+
+ // Host that libtunepimp will contact.
+ QString tunepimpHost = "www.musicbrainz.org";
+ QString tunepimpHostWithPort = "www.musicbrainz.org:80";
+
+ // Check what hosts are allowed to proceed without being proxied,
+ // or is using reversed proxy, what hosts must be proxied.
+ for(QStringList::ConstIterator it = noProxies.constBegin(); it != noProxies.constEnd(); ++it) {
+ QString normalizedHost = KNetwork::KResolver::normalizeDomain(*it);
+
+ if(normalizedHost == tunepimpHost ||
+ tunepimpHost.endsWith("." + normalizedHost))
+ {
+ useProxy = false;
+ break;
+ }
+
+ // KDE's proxy mechanism also supports exempting a specific
+ // host/port combo, check that also.
+ if(normalizedHost == tunepimpHostWithPort ||
+ tunepimpHostWithPort.endsWith("." + normalizedHost))
+ {
+ useProxy = false;
+ break;
+ }
+ }
+
+ // KDE supports a reverse proxy mechanism. Uh, yay.
+ if(KProtocolManager::useReverseProxy())
+ useProxy = !useProxy;
+
+ if(useProxy) {
+ KURL proxy = KProtocolManager::proxyFor("http");
+ QString proxyHost = proxy.host();
+
+ kdDebug(65432) << "Using proxy server " << proxyHost << " for www.musicbrainz.org.\n";
+ tp_SetProxy(m_pimp, proxyHost.latin1(), short(proxy.port()));
+ }
+ }
+ }
+
+ ~KTRMRequestHandler()
+ {
+ tp_Delete(m_pimp);
+ }
+
+private:
+ tunepimp_t m_pimp;
+ QMap<int, KTRMLookup *> m_lookupMap;
+ QMap<QString, int> m_fileMap;
+ mutable QMutex m_lookupMapMutex;
+};
+
+
+/**
+ * A custom event type used for signalling that a TRM lookup is finished.
+ */
+
+class KTRMEvent : public QCustomEvent
+{
+public:
+ enum Status {
+ Recognized,
+ Unrecognized,
+ Collision,
+ Error
+ };
+
+ KTRMEvent(int fileId, Status status) :
+ QCustomEvent(id),
+ m_fileId(fileId),
+ m_status(status) {}
+
+ int fileId() const
+ {
+ return m_fileId;
+ }
+
+ Status status() const
+ {
+ return m_status;
+ }
+
+ static const int id = User + 1984; // random, unique, event id
+
+private:
+ int m_fileId;
+ Status m_status;
+};
+
+/**
+ * A helper class to intercept KTRMQueryEvents and call recognized() (from the GUI
+ * thread) for the lookup.
+ */
+
+class KTRMEventHandler : public QObject
+{
+public:
+ static void send(int fileId, KTRMEvent::Status status)
+ {
+ KApplication::postEvent(instance(), new KTRMEvent(fileId, status));
+ }
+
+protected:
+ KTRMEventHandler() : QObject() {}
+
+ static KTRMEventHandler *instance()
+ {
+ static QMutex mutex;
+ mutex.lock();
+ static KTRMEventHandler handler;
+ mutex.unlock();
+ return &handler;
+ }
+
+ virtual void customEvent(QCustomEvent *event)
+ {
+ if(!event->type() == KTRMEvent::id)
+ return;
+
+ KTRMEvent *e = static_cast<KTRMEvent *>(event);
+
+ static QMutex mutex;
+ mutex.lock();
+
+ if(!KTRMRequestHandler::instance()->lookupMapContains(e->fileId())) {
+ mutex.unlock();
+ return;
+ }
+
+ KTRMLookup *lookup = KTRMRequestHandler::instance()->lookup(e->fileId());
+#if HAVE_MUSICBRAINZ >= 4
+ if ( e->status() != KTRMEvent::Unrecognized)
+#endif
+ KTRMRequestHandler::instance()->removeFromLookupMap(e->fileId());
+
+ mutex.unlock();
+
+ switch(e->status()) {
+ case KTRMEvent::Recognized:
+ lookup->recognized();
+ break;
+ case KTRMEvent::Unrecognized:
+ lookup->unrecognized();
+ break;
+ case KTRMEvent::Collision:
+ lookup->collision();
+ break;
+ case KTRMEvent::Error:
+ lookup->error();
+ break;
+ }
+ }
+};
+
+/**
+ * Callback fuction for TunePimp lookup events.
+ */
+#if HAVE_MUSICBRAINZ >= 4
+static void TRMNotifyCallback(tunepimp_t /*pimp*/, void* /*data*/, TPCallbackEnum type, int fileId, TPFileStatus status)
+#else
+static void TRMNotifyCallback(tunepimp_t pimp, void *data, TPCallbackEnum type, int fileId)
+#endif
+{
+ if(type != tpFileChanged)
+ return;
+
+#if HAVE_MUSICBRAINZ < 4
+ track_t track = tp_GetTrack(pimp, fileId);
+ TPFileStatus status = tr_GetStatus(track);
+#endif
+
+ switch(status) {
+ case eRecognized:
+ KTRMEventHandler::send(fileId, KTRMEvent::Recognized);
+ break;
+ case eUnrecognized:
+ KTRMEventHandler::send(fileId, KTRMEvent::Unrecognized);
+ break;
+ case eTRMCollision:
+#if HAVE_MUSICBRAINZ >= 4
+ case eUserSelection:
+#endif
+ KTRMEventHandler::send(fileId, KTRMEvent::Collision);
+ break;
+ case eError:
+ KTRMEventHandler::send(fileId, KTRMEvent::Error);
+ break;
+ default:
+ break;
+ }
+#if HAVE_MUSICBRAINZ < 4
+ tp_ReleaseTrack(pimp, track);
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// KTRMResult implementation
+////////////////////////////////////////////////////////////////////////////////
+
+class KTRMResult::KTRMResultPrivate
+{
+public:
+ KTRMResultPrivate() : track(0), year(0), relevance(0) {}
+ QString title;
+ QString artist;
+ QString album;
+ int track;
+ int year;
+ int relevance;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// KTRMResult public methods
+////////////////////////////////////////////////////////////////////////////////
+
+KTRMResult::KTRMResult()
+{
+ d = new KTRMResultPrivate;
+}
+
+KTRMResult::KTRMResult(const KTRMResult &result)
+{
+ d = new KTRMResultPrivate(*result.d);
+}
+
+KTRMResult::~KTRMResult()
+{
+ delete d;
+}
+
+QString KTRMResult::title() const
+{
+ return d->title;
+}
+
+QString KTRMResult::artist() const
+{
+ return d->artist;
+}
+
+QString KTRMResult::album() const
+{
+ return d->album;
+}
+
+int KTRMResult::track() const
+{
+ return d->track;
+}
+
+int KTRMResult::year() const
+{
+ return d->year;
+}
+
+bool KTRMResult::operator<(const KTRMResult &r) const
+{
+ return r.d->relevance < d->relevance;
+}
+
+bool KTRMResult::operator>(const KTRMResult &r) const
+{
+ return r.d->relevance > d->relevance;
+}
+
+bool KTRMResult::isEmpty() const
+{
+ return d->title.isEmpty() && d->artist.isEmpty() && d->album.isEmpty() &&
+ d->track == 0 && d->year == 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// KTRMLookup implementation
+////////////////////////////////////////////////////////////////////////////////
+
+class KTRMLookup::KTRMLookupPrivate
+{
+public:
+ KTRMLookupPrivate() :
+ fileId(-1) {}
+ QString file;
+ KTRMResultList results;
+ int fileId;
+ bool autoDelete;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// KTRMLookup public methods
+////////////////////////////////////////////////////////////////////////////////
+
+KTRMLookup::KTRMLookup(const QString &file, bool autoDelete)
+{
+ d = new KTRMLookupPrivate;
+ d->file = file;
+ d->autoDelete = autoDelete;
+ d->fileId = KTRMRequestHandler::instance()->startLookup(this);
+}
+
+KTRMLookup::~KTRMLookup()
+{
+ KTRMRequestHandler::instance()->endLookup(this);
+ delete d;
+}
+
+QString KTRMLookup::file() const
+{
+ return d->file;
+}
+
+int KTRMLookup::fileId() const
+{
+ return d->fileId;
+}
+
+void KTRMLookup::recognized()
+{
+ kdDebug() << k_funcinfo << d->file << endl;
+
+ d->results.clear();
+
+ metadata_t *metaData = md_New();
+ track_t track = tp_GetTrack(KTRMRequestHandler::instance()->tunePimp(), d->fileId);
+ tr_Lock(track);
+ tr_GetServerMetadata(track, metaData);
+
+ KTRMResult result;
+
+ result.d->title = QString::fromUtf8(metaData->track);
+ result.d->artist = QString::fromUtf8(metaData->artist);
+ result.d->album = QString::fromUtf8(metaData->album);
+ result.d->track = metaData->trackNum;
+ result.d->year = metaData->releaseYear;
+
+ d->results.append(result);
+
+ md_Delete(metaData);
+ tr_Unlock(track);
+ finished();
+}
+
+void KTRMLookup::unrecognized()
+{
+ kdDebug() << k_funcinfo << d->file << endl;
+#if HAVE_MUSICBRAINZ >= 4
+ char trm[255];
+ bool finish = false;
+ trm[0] = 0;
+ track_t track = tp_GetTrack(KTRMRequestHandler::instance()->tunePimp(), d->fileId);
+ tr_Lock(track);
+ tr_GetTRM(track, trm, 255);
+ if ( !trm[0] ) {
+ tr_SetStatus(track, ePending);
+ tp_Wake(KTRMRequestHandler::instance()->tunePimp(), track);
+ }
+ else
+ finish = true;
+ tr_Unlock(track);
+ tp_ReleaseTrack(KTRMRequestHandler::instance()->tunePimp(), track);
+ if ( !finish )
+ return;
+#endif
+ d->results.clear();
+ finished();
+}
+
+void KTRMLookup::collision()
+{
+ kdDebug() << k_funcinfo << d->file << endl;
+
+ track_t track = tp_GetTrack(KTRMRequestHandler::instance()->tunePimp(), d->fileId);
+
+ if(track <= 0) {
+ kdDebug() << "invalid track number" << endl;
+ return;
+ }
+
+ tr_Lock(track);
+ int resultCount = tr_GetNumResults(track);
+
+ if(resultCount > 0) {
+ TPResultType type;
+ result_t *results = new result_t[resultCount];
+ tr_GetResults(track, &type, results, &resultCount);
+
+ switch(type) {
+ case eNone:
+ kdDebug() << k_funcinfo << "eNone" << endl;
+ break;
+ case eArtistList:
+ kdDebug() << "eArtistList" << endl;
+ break;
+ case eAlbumList:
+ kdDebug() << "eAlbumList" << endl;
+ break;
+ case eTrackList:
+ {
+ kdDebug() << "eTrackList" << endl;
+ albumtrackresult_t **tracks = (albumtrackresult_t **) results;
+ d->results.clear();
+
+ for(int i = 0; i < resultCount; i++) {
+ KTRMResult result;
+
+ result.d->title = QString::fromUtf8(tracks[i]->name);
+#if HAVE_MUSICBRAINZ >= 4
+ result.d->artist = QString::fromUtf8(tracks[i]->artist.name);
+ result.d->album = QString::fromUtf8(tracks[i]->album.name);
+ result.d->year = tracks[i]->album.releaseYear;
+#else
+ result.d->artist = QString::fromUtf8(tracks[i]->artist->name);
+ result.d->album = QString::fromUtf8(tracks[i]->album->name);
+ result.d->year = tracks[i]->album->releaseYear;
+#endif
+ result.d->track = tracks[i]->trackNum;
+ result.d->relevance = tracks[i]->relevance;
+
+ d->results.append(result);
+ }
+ break;
+ }
+ case eMatchedTrack:
+ kdDebug() << k_funcinfo << "eMatchedTrack" << endl;
+ break;
+ }
+
+ delete [] results;
+ }
+
+ tr_Unlock(track);
+
+ finished();
+}
+
+void KTRMLookup::error()
+{
+ kdDebug() << k_funcinfo << d->file << endl;
+
+ d->results.clear();
+ finished();
+}
+
+KTRMResultList KTRMLookup::results() const
+{
+ return d->results;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// KTRMLookup protected methods
+////////////////////////////////////////////////////////////////////////////////
+
+void KTRMLookup::finished()
+{
+ if(d->autoDelete)
+ delete this;
+}
+
+#endif
+
+// vim: set et ts=8 sw=4:
diff --git a/juk/ktrm.h b/juk/ktrm.h
new file mode 100644
index 00000000..028a436b
--- /dev/null
+++ b/juk/ktrm.h
@@ -0,0 +1,197 @@
+/***************************************************************************
+ copyright : (C) 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+ * USA *
+ ***************************************************************************/
+
+/*
+ * At some point this will likely be a library class, as such it's been written
+ * as such and is LGPL'ed.
+ */
+
+#ifndef KTRM_H
+#define KTRM_H
+
+#include <config.h>
+
+#if HAVE_MUSICBRAINZ
+
+#include <qstring.h>
+#include <qvaluelist.h>
+#include <qmap.h>
+
+/**
+ * This represents a potential match for a TRM lookup. KTRMResultList is
+ * returned from KTRMLookup and will be sorted by relevance (better matches
+ * at the beginning of the list).
+ */
+
+class KTRMResult
+{
+ friend class KTRMLookup;
+
+public:
+ KTRMResult();
+ KTRMResult(const KTRMResult &result);
+ ~KTRMResult();
+
+ /**
+ * Returns the title of the track for the potential match.
+ */
+ QString title() const;
+
+ /**
+ * Returns the artist name of the track for the potential match.
+ */
+ QString artist() const;
+
+ /**
+ * Returns the album name of the track for the potential match.
+ */
+ QString album() const;
+
+ /**
+ * Returns the track number of the track for the potential match.
+ */
+ int track() const;
+
+ /**
+ * Returns the original release year of the track for the potential match.
+ */
+ int year() const;
+
+ /**
+ * Returns true if all of the values for the result are empty.
+ */
+ bool isEmpty() const;
+
+ /**
+ * Compares to \a r based on the relevance of the match. Better matches
+ * will be greater than less accurate matches.
+ */
+ bool operator<(const KTRMResult &r) const;
+
+ /**
+ * Compares to \a r based on the relevance of the match. Better matches
+ * will be greater than less accurate matches.
+ */
+ bool operator>(const KTRMResult &r) const;
+
+private:
+ class KTRMResultPrivate;
+ KTRMResultPrivate *d;
+};
+
+typedef QValueList<KTRMResult> KTRMResultList;
+
+/**
+ * An abstraction for libtunepimp's TRM based lookup and file recognition.
+ *
+ * A lookup is started when the object is created. One of the virtual methods
+ * -- recognized(), unrecognized(), collision() or error(). Those methods
+ * should be reimplemented in subclasses to specify what behavior should happen
+ * for each result.
+ *
+ * The lookups themselves happen in a background thread, but the return calls
+ * are guaranteed to run in the GUI thread.
+ */
+class KTRMLookup
+{
+public:
+ /**
+ * Creates and starts a lookup for \a file. If \a autoDelete is set to
+ * true the lookup will delete itself when it has finished.
+ */
+ KTRMLookup(const QString &file, bool autoDelete = false);
+
+ virtual ~KTRMLookup();
+
+ /**
+ * Returns the file name that was specified in the constructor.
+ */
+ QString file() const;
+
+ /**
+ * Returns the TunePimp file ID for the file. This is of no use to the
+ * public API.
+ *
+ * @internal
+ */
+ int fileId() const;
+
+ /**
+ * This method is called if the track was recognized by the TRM server.
+ * results() will return just one value. This may be reimplemented to
+ * provide specific behavion in the case of the track being recognized.
+ *
+ * \note The base class call should happen at the beginning of the subclass
+ * reimplementation; it populates the values of results().
+ */
+ virtual void recognized();
+
+ /**
+ * This method is called if the track was not recognized by the TRM server.
+ * results() will return an empty set. This may be reimplemented to provide
+ * specific behavion in the case of the track not being recognized.
+ */
+ virtual void unrecognized();
+
+ /**
+ * This method is called if there are multiple potential matches for the TRM
+ * value. results() will return a list of the potential matches, sorted by
+ * liklihood. This may be reimplemented to provide
+ * specific behavion in the case of the track not being recognized.
+ *
+ * \note The base class call should happen at the beginning of the subclass
+ * reimplementation; it populates the values of results().
+ */
+ virtual void collision();
+
+ /**
+ * This method is called if the track was not recognized by the TRM server.
+ * results() will return an empty set. This may be reimplemented to provide
+ * specific behavion in the case of the track not being recognized.
+ */
+ virtual void error();
+
+ /**
+ * Returns the list of matches found by the lookup. In the case that there
+ * was a TRM collision this list will contain multiple entries. In the case
+ * that it was recognized this will only contain one entry. Otherwise it
+ * will remain empty.
+ */
+ KTRMResultList results() const;
+
+protected:
+ /**
+ * This method is called when any of terminal states (recognized,
+ * unrecognized, collision or error) has been reached after the specifc
+ * method for the result has been called.
+ *
+ * This should be reimplemented in the case that there is some general
+ * processing to be done for all terminal states.
+ */
+ virtual void finished();
+
+private:
+ class KTRMLookupPrivate;
+ KTRMLookupPrivate *d;
+};
+
+#endif
+#endif
diff --git a/juk/main.cpp b/juk/main.cpp
new file mode 100644
index 00000000..7a0c53e3
--- /dev/null
+++ b/juk/main.cpp
@@ -0,0 +1,98 @@
+/***************************************************************************
+ begin : Mon Feb 4 23:40:41 EST 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ : (C) 2006 - 2007 by Michael Pyne
+ email : wheeler@kde.org
+ : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kuniqueapplication.h>
+#include <kcmdlineargs.h>
+#include <kaboutdata.h>
+#include <dcopclient.h>
+#include <kconfigbase.h>
+#include <kconfig.h>
+
+#include "juk.h"
+
+static const char description[] = I18N_NOOP("Jukebox and music manager for KDE");
+static const char scott[] = I18N_NOOP("Author, chief dork and keeper of the funk");
+static const char michael[] = I18N_NOOP("Assistant super-hero, fixer of many things");
+static const char daniel[] = I18N_NOOP("System tray docking, \"inline\" tag editing,\nbug fixes, evangelism, moral support");
+static const char tim[] = I18N_NOOP("GStreamer port");
+static const char stefan[] = I18N_NOOP("Global keybindings support");
+static const char stephen[] = I18N_NOOP("Track announcement popups");
+static const char frerich[] = I18N_NOOP("Automagic track data guessing, bugfixes");
+static const char zack[] = I18N_NOOP("More automagical things, now using MusicBrainz");
+static const char adam[] = I18N_NOOP("Co-conspirator in MusicBrainz wizardry");
+static const char matthias[] = I18N_NOOP("Friendly, neighborhood aRts guru");
+static const char maks[] = I18N_NOOP("Making JuK friendlier to people with\nterabytes of music");
+static const char antonio[] = I18N_NOOP("DCOP interface");
+static const char allan[] = I18N_NOOP("FLAC and MPC support");
+static const char nathan[] = I18N_NOOP("Album cover manager");
+static const char pascal[] = I18N_NOOP("Gimper of splash screen");
+
+static KCmdLineOptions options[] =
+{
+ { "+[file(s)]", I18N_NOOP("File(s) to open"), 0 },
+ KCmdLineLastOption
+};
+
+int main(int argc, char *argv[])
+{
+ KAboutData aboutData("juk", I18N_NOOP("JuK"),
+ "2.3.5", description, KAboutData::License_GPL,
+ "© 2002 - 2007, Scott Wheeler", 0,
+ "http://developer.kde.org/~wheeler/juk.html");
+
+ aboutData.addAuthor("Scott Wheeler", scott, "wheeler@kde.org");
+ aboutData.addAuthor("Michael Pyne", michael, "michael.pyne@kdemail.net");
+ aboutData.addCredit("Daniel Molkentin", daniel, "molkentin@kde.org");
+ aboutData.addCredit("Tim Jansen", tim, "tim@tjansen.de");
+ aboutData.addCredit("Stefan Asserhäll", stefan, "stefan.asserhall@telia.com");
+ aboutData.addCredit("Stephen Douglas", stephen, "stephen_douglas@yahoo.com");
+ aboutData.addCredit("Frerich Raabe", frerich, "raabe@kde.org");
+ aboutData.addCredit("Zack Rusin", zack, "zack@kde.org");
+ aboutData.addCredit("Adam Treat", adam, "manyoso@yahoo.com");
+ aboutData.addCredit("Matthias Kretz", matthias, "kretz@kde.org");
+ aboutData.addCredit("Maks Orlovich", maks, "maksim@kde.org");
+ aboutData.addCredit("Antonio Larrosa Jimenez", antonio, "larrosa@kde.org");
+ aboutData.addCredit("Allan Sandfeld Jensen", allan, "kde@carewolf.com");
+ aboutData.addCredit("Nathan Toone", nathan, "nathan@toonetown.com");
+ aboutData.addCredit("Pascal Klein", pascal, "4pascal@tpg.com.au");
+
+ KCmdLineArgs::init(argc, argv, &aboutData);
+ KCmdLineArgs::addCmdLineOptions(options);
+
+ KUniqueApplication::addCmdLineOptions();
+
+ KUniqueApplication a;
+
+ // Here we do some DCOP locking of sorts to prevent incoming DCOP calls
+ // before JuK has finished its initialization.
+
+ a.dcopClient()->suspend();
+ JuK *juk = new JuK;
+ a.dcopClient()->resume();
+
+ a.setMainWidget(juk);
+
+ bool startDocked;
+
+ KConfigGroup config(KGlobal::config(), "Settings");
+ startDocked = config.readBoolEntry("StartDocked", false);
+
+ if(!startDocked)
+ juk->show();
+
+ return a.exec();
+}
diff --git a/juk/mediafiles.cpp b/juk/mediafiles.cpp
new file mode 100644
index 00000000..b52c4be3
--- /dev/null
+++ b/juk/mediafiles.cpp
@@ -0,0 +1,158 @@
+/***************************************************************************
+ begin : Fri Sep 13 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kfiledialog.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kio/netaccess.h>
+
+#include "mediafiles.h"
+
+#include <taglib/tag.h>
+#if (TAGLIB_MAJOR_VERSION>1) || \
+ ((TAGLIB_MAJOR_VERSION==1) && (TAGLIB_MINOR_VERSION>=2))
+#define TAGLIB_1_2
+#endif
+#if (TAGLIB_MAJOR_VERSION>1) || \
+ ((TAGLIB_MAJOR_VERSION==1) && (TAGLIB_MINOR_VERSION>=3))
+#define TAGLIB_1_3
+#endif
+
+namespace MediaFiles {
+ QStringList mimeTypes();
+
+ static const char mp3Type[] = "audio/mpeg";
+ static const char oggType[] = "application/ogg";
+ static const char flacType[] = "audio/x-flac";
+ static const char mpcType[] = "audio/x-musepack";
+ static const char m3uType[] = "audio/mpegurl";
+
+ static const char vorbisType[] = "audio/x-vorbis";
+ static const char oggflacType[] = "audio/x-oggflac";
+
+ static const char playlistExtension[] = ".m3u";
+}
+
+QStringList MediaFiles::openDialog(QWidget *parent)
+{
+ KFileDialog dialog(QString::null, QString::null, parent, "filedialog", true);
+ dialog.setOperationMode(KFileDialog::Opening);
+
+ dialog.setCaption(i18n("Open"));
+ dialog.setMode(KFile::Files | KFile::LocalOnly);
+ // dialog.ops->clearHistory();
+ dialog.setMimeFilter(mimeTypes());
+
+ dialog.exec();
+
+ return convertURLsToLocal(dialog.selectedFiles());
+}
+
+QString MediaFiles::savePlaylistDialog(const QString &playlistName, QWidget *parent)
+{
+ QString fileName = KFileDialog::getSaveFileName(playlistName + playlistExtension,
+ QString("*").append(playlistExtension),
+ parent,
+ i18n("Playlists"));
+ if(!fileName.isEmpty() && !fileName.endsWith(playlistExtension))
+ fileName.append(playlistExtension);
+
+ return fileName;
+}
+
+bool MediaFiles::isMediaFile(const QString &fileName)
+{
+ KMimeType::Ptr result = KMimeType::findByPath(fileName, 0, true);
+
+ return result->is(mp3Type) || result->is(oggType) || result->is(flacType)
+#ifdef TAGLIB_1_3
+ || result->is(mpcType)
+#endif
+ ;
+}
+
+bool MediaFiles::isPlaylistFile(const QString &fileName)
+{
+ KMimeType::Ptr result = KMimeType::findByPath(fileName, 0, true);
+ return result->is(m3uType);
+}
+
+bool MediaFiles::isMP3(const QString &fileName)
+{
+ KMimeType::Ptr result = KMimeType::findByPath(fileName, 0, true);
+ return result->is(mp3Type);
+}
+
+bool MediaFiles::isOgg(const QString &fileName)
+{
+ KMimeType::Ptr result = KMimeType::findByPath(fileName, 0, true);
+ return result->is(oggType);
+}
+
+bool MediaFiles::isFLAC(const QString &fileName)
+{
+ KMimeType::Ptr result = KMimeType::findByPath(fileName, 0, true);
+ return result->is(flacType);
+}
+
+bool MediaFiles::isMPC(const QString &fileName)
+{
+ KMimeType::Ptr result = KMimeType::findByPath(fileName, 0, true);
+ return result->is(mpcType);
+}
+
+bool MediaFiles::isVorbis(const QString &fileName)
+{
+ KMimeType::Ptr result = KMimeType::findByPath(fileName, 0, false);
+ return result->is(vorbisType);
+}
+
+bool MediaFiles::isOggFLAC(const QString &fileName)
+{
+ KMimeType::Ptr result = KMimeType::findByPath(fileName, 0, false);
+ return result->is(oggflacType);
+}
+
+QStringList MediaFiles::mimeTypes()
+{
+ QStringList l;
+
+ l << mp3Type << oggType << flacType << m3uType << vorbisType
+#ifdef TAGLIB_1_2
+ << oggflacType
+#endif
+#ifdef TAGLIB_1_3
+ << mpcType
+#endif
+ ;
+ return l;
+}
+
+QStringList MediaFiles::convertURLsToLocal(const QStringList &urlList, QWidget *w)
+{
+ QStringList result;
+ KURL localUrl;
+
+ for(QStringList::ConstIterator it = urlList.constBegin(); it != urlList.constEnd(); ++it) {
+ localUrl = KIO::NetAccess::mostLocalURL(KURL::fromPathOrURL(*it), w);
+
+ if(!localUrl.isLocalFile())
+ kdDebug(65432) << localUrl << " is not a local file, skipping.\n";
+ else
+ result.append(localUrl.path());
+ }
+
+ return result;
+}
diff --git a/juk/mediafiles.h b/juk/mediafiles.h
new file mode 100644
index 00000000..e0cc8458
--- /dev/null
+++ b/juk/mediafiles.h
@@ -0,0 +1,89 @@
+/***************************************************************************
+ begin : Fri Sep 13 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef MEDIAFILES_H
+#define MEDIAFILES_H
+
+
+/**
+ * A namespace for file JuK's file related functions. The goal is to hide
+ * all specific knowledge of mimetypes and file extensions here.
+ */
+namespace MediaFiles
+{
+ /**
+ * Creates a JuK specific KFileDialog with the specified parent.
+ */
+ QStringList openDialog(QWidget *parent = 0);
+
+ /**
+ * Creates a JuK specific KFileDialog for saving a playlist with the name
+ * playlistName and the specified parent and returns the file name.
+ */
+ QString savePlaylistDialog(const QString &playlistName, QWidget *parent = 0);
+
+ /**
+ * Returns true if fileName is a supported media file.
+ */
+ bool isMediaFile(const QString &fileName);
+
+ /**
+ * Returns true if fileName is a supported playlist file.
+ */
+ bool isPlaylistFile(const QString &fileName);
+
+ /**
+ * Returns true if fileName is a mp3 file.
+ */
+ bool isMP3(const QString &fileName);
+
+ /**
+ * Returns true if fileName is a mpc (aka musepack) file.
+ */
+ bool isMPC(const QString &fileName);
+
+ /**
+ * Returns true if fileName is an Ogg file.
+ */
+ bool isOgg(const QString &fileName);
+
+ /**
+ * Returns true if fileName is a FLAC file.
+ */
+ bool isFLAC(const QString &fileName);
+
+ /**
+ * Returns true if fileName is an Ogg/Vorbis file.
+ */
+ bool isVorbis(const QString &fileName);
+
+ /**
+ * Returns true if fileName is an Ogg/FLAC file.
+ */
+ bool isOggFLAC(const QString &fileName);
+
+ /**
+ * Returns a list of absolute local filenames, mapped from \p urlList.
+ * Any URLs in urlList that aren't really local files will be stripped
+ * from the result (so result.size() may be < urlList.size()).
+ *
+ * @param urlList list of file names or URLs to convert.
+ * @param w KIO may need the widget to handle user interaction.
+ * @return list of all local files in urlList, converted to absolute paths.
+ */
+ QStringList convertURLsToLocal(const QStringList &urlList, QWidget *w = 0);
+}
+
+#endif
diff --git a/juk/musicbrainzquery.cpp b/juk/musicbrainzquery.cpp
new file mode 100644
index 00000000..84bb38d7
--- /dev/null
+++ b/juk/musicbrainzquery.cpp
@@ -0,0 +1,120 @@
+/***************************************************************************
+ begin : Tue Aug 3 2004
+ copyright : (C) 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "musicbrainzquery.h"
+
+#if HAVE_MUSICBRAINZ
+
+#include "trackpickerdialog.h"
+#include "tag.h"
+#include "collectionlist.h"
+#include "tagtransactionmanager.h"
+
+#include <kmainwindow.h>
+#include <kapplication.h>
+#include <kstatusbar.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <qfileinfo.h>
+
+MusicBrainzLookup::MusicBrainzLookup(const FileHandle &file) :
+ KTRMLookup(file.absFilePath()),
+ m_file(file)
+{
+ message(i18n("Querying MusicBrainz server..."));
+}
+
+void MusicBrainzLookup::recognized()
+{
+ KTRMLookup::recognized();
+ confirmation();
+ delete this;
+}
+
+void MusicBrainzLookup::unrecognized()
+{
+ KTRMLookup::unrecognized();
+ message(i18n("No matches found."));
+ delete this;
+}
+
+void MusicBrainzLookup::collision()
+{
+ KTRMLookup::collision();
+ confirmation();
+ delete this;
+}
+
+void MusicBrainzLookup::error()
+{
+ KTRMLookup::error();
+ message(i18n("Error connecting to MusicBrainz server."));
+ delete this;
+}
+
+void MusicBrainzLookup::message(const QString &s) const
+{
+ KMainWindow *w = static_cast<KMainWindow *>(kapp->mainWidget());
+ w->statusBar()->message(QString("%1 (%2)").arg(s).arg(m_file.fileInfo().fileName()), 3000);
+}
+
+void MusicBrainzLookup::confirmation()
+{
+ // Here we do a bit of queuing to make sure that we don't pop up multiple
+ // instances of the confirmation dialog at once.
+
+ static QValueList< QPair<FileHandle, KTRMResultList> > queue;
+
+ if(results().isEmpty())
+ return;
+
+ if(!queue.isEmpty()) {
+ queue.append(qMakePair(m_file, results()));
+ return;
+ }
+
+ queue.append(qMakePair(m_file, results()));
+
+ while(!queue.isEmpty()) {
+ QPair<FileHandle, KTRMResultList> item = queue.first();
+ FileHandle file = item.first;
+ KTRMResultList results = item.second;
+ TrackPickerDialog dialog(file.fileInfo().fileName(), results);
+
+ if(dialog.exec() == QDialog::Accepted && !dialog.result().isEmpty()) {
+ KTRMResult result = dialog.result();
+ Tag *tag = TagTransactionManager::duplicateTag(file.tag());
+
+ if(!result.title().isEmpty())
+ tag->setTitle(result.title());
+ if(!result.artist().isEmpty())
+ tag->setArtist(result.artist());
+ if(!result.album().isEmpty())
+ tag->setAlbum(result.album());
+ if(result.track() != 0)
+ tag->setTrack(result.track());
+ if(result.year() != 0)
+ tag->setYear(result.year());
+
+ PlaylistItem *item = CollectionList::instance()->lookup(file.absFilePath());
+ TagTransactionManager::instance()->changeTagOnItem(item, tag);
+ TagTransactionManager::instance()->commit();
+ }
+ queue.pop_front();
+ }
+}
+
+#endif
diff --git a/juk/musicbrainzquery.h b/juk/musicbrainzquery.h
new file mode 100644
index 00000000..6bf51278
--- /dev/null
+++ b/juk/musicbrainzquery.h
@@ -0,0 +1,43 @@
+/***************************************************************************
+ begin : Tue Aug 3 2004
+ copyright : (C) 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef MUSICBRAINZQUERY_H
+#define MUSICBRAINZQUERY_H
+
+#include <config.h>
+
+#if HAVE_MUSICBRAINZ
+
+#include "ktrm.h"
+#include "filehandle.h"
+
+class MusicBrainzLookup : public KTRMLookup
+{
+public:
+ MusicBrainzLookup(const FileHandle &file);
+ virtual void recognized();
+ virtual void unrecognized();
+ virtual void collision();
+ virtual void error();
+
+private:
+ void message(const QString &s) const;
+ void confirmation();
+
+ FileHandle m_file;
+};
+
+#endif
+#endif
diff --git a/juk/nowplaying.cpp b/juk/nowplaying.cpp
new file mode 100644
index 00000000..2336faac
--- /dev/null
+++ b/juk/nowplaying.cpp
@@ -0,0 +1,368 @@
+/***************************************************************************
+ begin : Tue Nov 9 2004
+ copyright : (C) 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kurldrag.h>
+#include <kio/netaccess.h>
+
+#include <qimage.h>
+#include <qlayout.h>
+#include <qevent.h>
+#include <qdragobject.h>
+#include <qimage.h>
+#include <qtimer.h>
+#include <qpoint.h>
+
+#include "nowplaying.h"
+#include "playlistcollection.h"
+#include "playermanager.h"
+#include "coverinfo.h"
+#include "covermanager.h"
+#include "tag.h"
+#include "playlistitem.h"
+#include "collectionlist.h"
+#include "historyplaylist.h"
+
+static const int imageSize = 64;
+
+struct Line : public QFrame
+{
+ Line(QWidget *parent) : QFrame(parent) { setFrameShape(VLine); }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// NowPlaying
+////////////////////////////////////////////////////////////////////////////////
+
+NowPlaying::NowPlaying(QWidget *parent, PlaylistCollection *collection, const char *name) :
+ QHBox(parent, name),
+ m_observer(this, collection),
+ m_collection(collection)
+{
+ // m_observer is set to watch the PlaylistCollection, also watch for
+ // changes that come from CollectionList.
+
+ CollectionList::instance()->addObserver(&m_observer);
+
+ layout()->setMargin(5);
+ layout()->setSpacing(3);
+ setFixedHeight(imageSize + 2 + layout()->margin() * 2);
+
+ setStretchFactor(new CoverItem(this), 0);
+ setStretchFactor(new TrackItem(this), 2);
+ setStretchFactor(new Line(this), 0);
+ setStretchFactor(new HistoryItem(this), 1);
+
+ connect(PlayerManager::instance(), SIGNAL(signalPlay()), this, SLOT(slotUpdate()));
+ connect(PlayerManager::instance(), SIGNAL(signalStop()), this, SLOT(slotUpdate()));
+
+ hide();
+}
+
+void NowPlaying::addItem(NowPlayingItem *item)
+{
+ m_items.append(item);
+}
+
+PlaylistCollection *NowPlaying::collection() const
+{
+ return m_collection;
+}
+
+void NowPlaying::slotUpdate()
+{
+ FileHandle file = PlayerManager::instance()->playingFile();
+
+ if(file.isNull()) {
+ hide();
+ return;
+ }
+ else
+ show();
+
+ for(QValueList<NowPlayingItem *>::Iterator it = m_items.begin();
+ it != m_items.end(); ++it)
+ {
+ (*it)->update(file);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CoverItem
+////////////////////////////////////////////////////////////////////////////////
+
+CoverItem::CoverItem(NowPlaying *parent) :
+ QLabel(parent, "CoverItem"),
+ NowPlayingItem(parent)
+{
+ setFixedHeight(parent->height() - parent->layout()->margin() * 2);
+ setFrameStyle(Box | Plain);
+ setLineWidth(1);
+ setMargin(1);
+ setAcceptDrops(true);
+}
+
+void CoverItem::update(const FileHandle &file)
+{
+ m_file = file;
+
+ if(file.coverInfo()->hasCover()) {
+ show();
+ QImage image = file.coverInfo()->pixmap(CoverInfo::Thumbnail).convertToImage();
+ setPixmap(image.smoothScale(imageSize, imageSize, QImage::ScaleMin));
+ }
+ else
+ hide();
+}
+
+void CoverItem::mouseReleaseEvent(QMouseEvent *event)
+{
+ if(m_dragging) {
+ m_dragging = false;
+ return;
+ }
+
+ if(event->x() >= 0 && event->y() >= 0 &&
+ event->x() < width() && event->y() < height() &&
+ event->button() == LeftButton &&
+ m_file.coverInfo()->hasCover())
+ {
+ m_file.coverInfo()->popup();
+ }
+
+ QLabel::mousePressEvent(event);
+}
+
+void CoverItem::mousePressEvent(QMouseEvent *e)
+{
+ m_dragging = false;
+ m_dragStart = e->globalPos();
+}
+
+void CoverItem::mouseMoveEvent(QMouseEvent *e)
+{
+ if(m_dragging)
+ return;
+
+ QPoint diff = m_dragStart - e->globalPos();
+ if(QABS(diff.x()) > 1 || QABS(diff.y()) > 1) {
+
+ // Start a drag.
+
+ m_dragging = true;
+
+ CoverDrag *drag = new CoverDrag(m_file.coverInfo()->coverId(), this);
+ drag->drag();
+ }
+}
+
+void CoverItem::dragEnterEvent(QDragEnterEvent *e)
+{
+ e->accept(QImageDrag::canDecode(e) || KURLDrag::canDecode(e) || CoverDrag::canDecode(e));
+}
+
+void CoverItem::dropEvent(QDropEvent *e)
+{
+ QImage image;
+ KURL::List urls;
+ coverKey key;
+
+ if(e->source() == this)
+ return;
+
+ if(QImageDrag::decode(e, image)) {
+ m_file.coverInfo()->setCover(image);
+ update(m_file);
+ }
+ else if(CoverDrag::decode(e, key)) {
+ m_file.coverInfo()->setCoverId(key);
+ update(m_file);
+ }
+ else if(KURLDrag::decode(e, urls)) {
+ QString fileName;
+
+ if(KIO::NetAccess::download(urls.front(), fileName, this)) {
+ if(image.load(fileName)) {
+ m_file.coverInfo()->setCover(image);
+ update(m_file);
+ }
+ else
+ kdError(65432) << "Unable to load image from " << urls.front() << endl;
+
+ KIO::NetAccess::removeTempFile(fileName);
+ }
+ else
+ kdError(65432) << "Unable to download " << urls.front() << endl;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TrackItem
+////////////////////////////////////////////////////////////////////////////////
+
+TrackItem::TrackItem(NowPlaying *parent) :
+ QWidget(parent, "TrackItem"),
+ NowPlayingItem(parent)
+{
+ setFixedHeight(parent->height() - parent->layout()->margin() * 2);
+ setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
+
+ QVBoxLayout *layout = new QVBoxLayout(this);
+
+ m_label = new LinkLabel(this);
+ m_label->setLinkUnderline(false);
+
+ layout->addStretch();
+ layout->addWidget(m_label);
+ layout->addStretch();
+
+ connect(m_label, SIGNAL(linkClicked(const QString &)), this,
+ SLOT(slotOpenLink(const QString &)));
+}
+
+void TrackItem::update(const FileHandle &file)
+{
+ m_file = file;
+ QTimer::singleShot(0, this, SLOT(slotUpdate()));
+}
+
+void TrackItem::slotOpenLink(const QString &link)
+{
+ PlaylistCollection *collection = NowPlayingItem::parent()->collection();
+
+ if(link == "artist")
+ collection->showMore(m_file.tag()->artist());
+ else if(link == "album")
+ collection->showMore(m_file.tag()->artist(), m_file.tag()->album());
+ else if(link == "clear")
+ collection->clearShowMore();
+
+ update(m_file);
+}
+
+void TrackItem::slotUpdate()
+{
+ QString title = QStyleSheet::escape(m_file.tag()->title());
+ QString artist = QStyleSheet::escape(m_file.tag()->artist());
+ QString album = QStyleSheet::escape(m_file.tag()->album());
+ QString separator = (artist.isNull() || album.isNull()) ? QString::null : QString(" - ");
+
+ // This block-o-nastiness makes the font smaller and smaller until it actually fits.
+
+ int size = 4;
+ QString format =
+ "<font size=\"+%1\"><b>%2</b></font>"
+ "<br />"
+ "<font size=\"+%3\"><b><a href=\"artist\">%4</a>%5<a href=\"album\">%6</a></b>";
+
+ if(NowPlayingItem::parent()->collection()->showMoreActive())
+ format.append(QString(" (<a href=\"clear\">%1</a>)").arg(i18n("back to playlist")));
+
+ format.append("</font>");
+
+ do {
+ m_label->setText(format.arg(size).arg(title).arg(size - 2)
+ .arg(artist).arg(separator).arg(album));
+ --size;
+ } while(m_label->heightForWidth(m_label->width()) > imageSize && size >= 0);
+
+ m_label->setFixedHeight(QMIN(imageSize, m_label->heightForWidth(m_label->width())));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HistoryItem
+////////////////////////////////////////////////////////////////////////////////
+
+HistoryItem::HistoryItem(NowPlaying *parent) :
+ LinkLabel(parent, "HistoryItem"),
+ NowPlayingItem(parent)
+{
+ setFixedHeight(parent->height() - parent->layout()->margin() * 2);
+ setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
+ setLinkUnderline(false);
+ setText(QString("<b>%1</b>").arg(i18n("History")));
+
+ m_timer = new QTimer(this);
+ connect(m_timer, SIGNAL(timeout()), this, SLOT(slotAddPlaying()));
+}
+
+void HistoryItem::update(const FileHandle &file)
+{
+ if(file.isNull() || (!m_history.isEmpty() && m_history.front().file == file))
+ return;
+
+ if(m_history.count() >= 10)
+ m_history.remove(m_history.fromLast());
+
+ QString format = "<br /><a href=\"%1\"><font size=\"-1\">%2</font></a>";
+ QString current = QString("<b>%1</b>").arg(i18n("History"));
+ QString previous;
+
+ for(QValueList<Item>::ConstIterator it = m_history.begin();
+ it != m_history.end(); ++it)
+ {
+ previous = current;
+ current.append(format.arg((*it).anchor).arg(QStyleSheet::escape((*it).file.tag()->title())));
+ setText(current);
+ if(heightForWidth(width()) > imageSize) {
+ setText(previous);
+ break;
+ }
+ }
+
+ m_file = file;
+ m_timer->stop();
+ m_timer->start(HistoryPlaylist::delay(), true);
+}
+
+void HistoryItem::openLink(const QString &link)
+{
+ for(QValueList<Item>::ConstIterator it = m_history.begin();
+ it != m_history.end(); ++it)
+ {
+ if((*it).anchor == link) {
+ if((*it).playlist) {
+ CollectionListItem *collectionItem =
+ CollectionList::instance()->lookup((*it).file.absFilePath());
+ PlaylistItem *item = collectionItem->itemForPlaylist((*it).playlist);
+ (*it).playlist->clearSelection();
+ (*it).playlist->setSelected(item, true);
+ (*it).playlist->ensureItemVisible(item);
+ NowPlayingItem::parent()->collection()->raise((*it).playlist);
+ }
+ break;
+ }
+ }
+}
+
+void HistoryItem::slotAddPlaying()
+{
+ // More or less copied from the HistoryPlaylist
+
+ PlayerManager *manager = PlayerManager::instance();
+
+ if(manager->playing() && manager->playingFile() == m_file) {
+ m_history.prepend(Item(KApplication::randomString(20),
+ m_file, Playlist::playingItem()->playlist()));
+ }
+
+ m_file = FileHandle::null();
+}
+
+#include "nowplaying.moc"
+
+// vim: set et sw=4 ts=8:
diff --git a/juk/nowplaying.h b/juk/nowplaying.h
new file mode 100644
index 00000000..208eabc4
--- /dev/null
+++ b/juk/nowplaying.h
@@ -0,0 +1,177 @@
+/***************************************************************************
+ begin : Tue Nov 9 2004
+ copyright : (C) 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef NOWPLAYING_H
+#define NOWPLAYING_H
+
+#include <kactivelabel.h>
+
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qguardedptr.h>
+
+#include "filehandle.h"
+#include "playlist.h"
+
+class QTimer;
+class QPoint;
+
+class NowPlayingItem;
+class PlaylistCollection;
+class Playlist;
+
+/**
+ * This is the widget that holds all of the other items and handles updating them
+ * when the playing item changes.
+ */
+
+class NowPlaying : public QHBox
+{
+ Q_OBJECT
+
+public:
+ NowPlaying(QWidget *parent, PlaylistCollection *collection,
+ const char *name = 0);
+ void addItem(NowPlayingItem *item);
+ PlaylistCollection *collection() const;
+
+private slots:
+ void slotUpdate();
+
+private:
+ struct Observer : public PlaylistObserver
+ {
+ Observer(NowPlaying *parent, PlaylistInterface *playlist) :
+ PlaylistObserver(playlist),
+ m_parent(parent) {}
+ virtual void updateCurrent() {}
+ virtual void updateData() { m_parent->slotUpdate(); }
+ NowPlaying *m_parent;
+ };
+ friend struct Observer;
+
+ Observer m_observer;
+ PlaylistCollection *m_collection;
+ QValueList<NowPlayingItem *> m_items;
+};
+
+/**
+ * Abstract base for the other NowPlaying items.
+ */
+
+class NowPlayingItem
+{
+public:
+ virtual void update(const FileHandle &file) = 0;
+ NowPlaying *parent() const { return m_parent; }
+protected:
+ NowPlayingItem(NowPlaying *parent) : m_parent(parent) { parent->addItem(this); }
+private:
+ NowPlaying *m_parent;
+};
+
+/**
+ * Displays the cover of the currently playing file if available, or hides
+ * itself if not.
+ */
+
+class CoverItem : public QLabel, public NowPlayingItem
+{
+public:
+ CoverItem(NowPlaying *parent);
+ virtual void update(const FileHandle &file);
+ virtual void mouseReleaseEvent(QMouseEvent *event);
+
+protected:
+ virtual void dragEnterEvent(QDragEnterEvent *e);
+ virtual void dropEvent(QDropEvent *e);
+
+ virtual void mousePressEvent(QMouseEvent *e);
+ virtual void mouseMoveEvent(QMouseEvent *e);
+
+private:
+ FileHandle m_file;
+ bool m_dragging;
+ QPoint m_dragStart;
+};
+
+/**
+ * A link label that doesn't automatically open Konqueror.
+ */
+
+class LinkLabel : public KActiveLabel
+{
+public:
+ LinkLabel(QWidget *parent, const char *name = 0) : KActiveLabel(parent, name) {}
+ virtual void openLink(const QString &) {}
+};
+
+/**
+ * Show the text information on the current track and provides links to the
+ * album and artist of the currently playing item.
+ */
+
+class TrackItem : public QWidget, public NowPlayingItem
+{
+ Q_OBJECT
+
+public:
+ TrackItem(NowPlaying *parent);
+ virtual void update(const FileHandle &file);
+
+private slots:
+ void slotOpenLink(const QString &link);
+ void slotUpdate();
+
+private:
+ FileHandle m_file;
+ LinkLabel *m_label;
+};
+
+/**
+ * Shows up to 10 items of history and links to those items.
+ */
+
+class HistoryItem : public LinkLabel, public NowPlayingItem
+{
+ Q_OBJECT
+
+public:
+ HistoryItem(NowPlaying *parent);
+ virtual void update(const FileHandle &file);
+ virtual void openLink(const QString &link);
+
+private slots:
+ void slotAddPlaying();
+
+private:
+ struct Item
+ {
+ Item() {}
+ Item(const QString &a, const FileHandle &f, Playlist *p)
+ : anchor(a), file(f), playlist(p) {}
+
+ QString anchor;
+ FileHandle file;
+ QGuardedPtr<Playlist> playlist;
+ };
+
+ QValueList<Item> m_history;
+ LinkLabel *m_label;
+ QTimer *m_timer;
+ FileHandle m_file;
+};
+
+#endif
diff --git a/juk/pics/Makefile.am b/juk/pics/Makefile.am
new file mode 100644
index 00000000..6dff1bce
--- /dev/null
+++ b/juk/pics/Makefile.am
@@ -0,0 +1,2 @@
+pics_DATA = playing.png splash.png yahoo_credit.png
+picsdir = $(kde_datadir)/juk/pics
diff --git a/juk/pics/playing.png b/juk/pics/playing.png
new file mode 100644
index 00000000..ba75b4d5
--- /dev/null
+++ b/juk/pics/playing.png
Binary files differ
diff --git a/juk/pics/splash.png b/juk/pics/splash.png
new file mode 100644
index 00000000..b61d7b59
--- /dev/null
+++ b/juk/pics/splash.png
Binary files differ
diff --git a/juk/pics/yahoo_credit.png b/juk/pics/yahoo_credit.png
new file mode 100644
index 00000000..cb64dbd7
--- /dev/null
+++ b/juk/pics/yahoo_credit.png
Binary files differ
diff --git a/juk/player.h b/juk/player.h
new file mode 100644
index 00000000..a3bd471a
--- /dev/null
+++ b/juk/player.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ begin : Sun Feb 17 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef PLAYER_H
+#define PLAYER_H
+
+#include <qobject.h>
+
+#include "filehandle.h"
+
+class Player : public QObject
+{
+public:
+ virtual ~Player() {}
+
+ virtual void play(const FileHandle &file = FileHandle::null()) = 0;
+ virtual void pause() = 0;
+ virtual void stop() = 0;
+
+ virtual void setVolume(float volume = 1.0) = 0;
+ virtual float volume() const = 0;
+
+ virtual bool playing() const = 0;
+ virtual bool paused() const = 0;
+
+ virtual int totalTime() const = 0;
+ virtual int currentTime() const = 0;
+ virtual int position() const = 0; // in this case not really the percent
+
+ virtual void seek(int seekTime) = 0;
+ virtual void seekPosition(int position) = 0;
+
+protected:
+ Player() : QObject() {}
+
+};
+
+#endif
diff --git a/juk/playermanager.cpp b/juk/playermanager.cpp
new file mode 100644
index 00000000..b60c7af5
--- /dev/null
+++ b/juk/playermanager.cpp
@@ -0,0 +1,689 @@
+/***************************************************************************
+ begin : Sat Feb 14 2004
+ copyright : (C) 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+/**
+ * Note to those who work here. The preprocessor variables HAVE_ARTS and HAVE_GSTREAMER
+ * are ::ALWAYS DEFINED::. You can't use #ifdef to see if they're present, you should just
+ * use #if.
+ *
+ * However, HAVE_AKODE is #define'd if present, and undefined if not present.
+ * - mpyne
+ */
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <qslider.h>
+#include <qtimer.h>
+
+#include <math.h>
+
+#include "artsplayer.h"
+#include "akodeplayer.h"
+#include "gstreamerplayer.h"
+#include "playermanager.h"
+#include "playlistinterface.h"
+#include "slideraction.h"
+#include "statuslabel.h"
+#include "actioncollection.h"
+#include "collectionlist.h"
+#include "coverinfo.h"
+#include "tag.h"
+
+#include "config.h"
+
+using namespace ActionCollection;
+
+enum PlayerManagerStatus { StatusStopped = -1, StatusPaused = 1, StatusPlaying = 2 };
+
+////////////////////////////////////////////////////////////////////////////////
+// helper functions
+////////////////////////////////////////////////////////////////////////////////
+
+enum SoundSystem { ArtsBackend = 0, GStreamerBackend = 1, AkodeBackend = 2 };
+
+static Player *createPlayer(int system = ArtsBackend)
+{
+
+ Player *p = 0;
+ switch(system) {
+#ifdef HAVE_AKODE
+ case AkodeBackend:
+ p = new aKodePlayer;
+ break;
+#endif
+#if HAVE_ARTS
+ case ArtsBackend:
+ p = new ArtsPlayer;
+ break;
+#endif
+#if HAVE_GSTREAMER
+ case GStreamerBackend:
+ p = new GStreamerPlayer;
+ break;
+#endif
+ default:
+#if HAVE_ARTS
+ p = new ArtsPlayer;
+#elif HAVE_GSTREAMER
+ p = new GStreamerPlayer;
+#else
+ p = new aKodePlayer;
+#endif
+ break;
+ }
+ return p;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+PlayerManager::PlayerManager() :
+ Player(),
+ m_sliderAction(0),
+ m_playlistInterface(0),
+ m_statusLabel(0),
+ m_player(0),
+ m_timer(0),
+ m_noSeek(false),
+ m_muted(false),
+ m_setup(false)
+{
+// This class is the first thing constructed during program startup, and
+// therefore has no access to the widgets needed by the setup() method.
+// Since the setup() method will be called indirectly by the player() method
+// later, just disable it here. -- mpyne
+// setup();
+}
+
+PlayerManager::~PlayerManager()
+{
+ delete m_player;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+PlayerManager *PlayerManager::instance() // static
+{
+ static PlayerManager manager;
+ return &manager;
+}
+
+bool PlayerManager::playing() const
+{
+ if(!player())
+ return false;
+
+ return player()->playing();
+}
+
+bool PlayerManager::paused() const
+{
+ if(!player())
+ return false;
+
+ return player()->paused();
+}
+
+float PlayerManager::volume() const
+{
+ if(!player())
+ return 0;
+
+ return player()->volume();
+}
+
+int PlayerManager::status() const
+{
+ if(!player())
+ return StatusStopped;
+
+ if(player()->paused())
+ return StatusPaused;
+
+ if(player()->playing())
+ return StatusPlaying;
+
+ return 0;
+}
+
+int PlayerManager::totalTime() const
+{
+ if(!player())
+ return 0;
+
+ return player()->totalTime();
+}
+
+int PlayerManager::currentTime() const
+{
+ if(!player())
+ return 0;
+
+ return player()->currentTime();
+}
+
+int PlayerManager::position() const
+{
+ if(!player())
+ return 0;
+
+ return player()->position();
+}
+
+QStringList PlayerManager::trackProperties()
+{
+ return FileHandle::properties();
+}
+
+QString PlayerManager::trackProperty(const QString &property) const
+{
+ if(!playing() && !paused())
+ return QString::null;
+
+ return m_file.property(property);
+}
+
+QPixmap PlayerManager::trackCover(const QString &size) const
+{
+ if(!playing() && !paused())
+ return QPixmap();
+
+ if(size.lower() == "small")
+ return m_file.coverInfo()->pixmap(CoverInfo::Thumbnail);
+ if(size.lower() == "large")
+ return m_file.coverInfo()->pixmap(CoverInfo::FullSize);
+
+ return QPixmap();
+}
+
+FileHandle PlayerManager::playingFile() const
+{
+ return m_file;
+}
+
+QString PlayerManager::playingString() const
+{
+ if(!playing())
+ return QString::null;
+
+ QString str = m_file.tag()->artist() + " - " + m_file.tag()->title();
+ if(m_file.tag()->artist().isEmpty())
+ str = m_file.tag()->title();
+
+ return str;
+}
+
+void PlayerManager::setPlaylistInterface(PlaylistInterface *interface)
+{
+ m_playlistInterface = interface;
+}
+
+void PlayerManager::setStatusLabel(StatusLabel *label)
+{
+ m_statusLabel = label;
+}
+
+KSelectAction *PlayerManager::playerSelectAction(QObject *parent) // static
+{
+ KSelectAction *action = 0;
+ action = new KSelectAction(i18n("&Output To"), 0, parent, "outputSelect");
+ QStringList l;
+
+#if HAVE_ARTS
+ l << i18n("aRts");
+#endif
+#if HAVE_GSTREAMER
+ l << i18n("GStreamer");
+#endif
+#ifdef HAVE_AKODE
+ l << i18n("aKode");
+#endif
+
+ if(l.isEmpty()) {
+ kdError(65432) << "Your JuK seems to have no output backend possibilities.\n";
+ l << i18n("aKode"); // Looks like akode is the default backend.
+ }
+
+ action->setItems(l);
+ return action;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public slots
+////////////////////////////////////////////////////////////////////////////////
+
+void PlayerManager::play(const FileHandle &file)
+{
+ if(!player() || !m_playlistInterface)
+ return;
+
+ if(file.isNull()) {
+ if(player()->paused())
+ player()->play();
+ else if(player()->playing()) {
+ if(m_sliderAction->trackPositionSlider())
+ m_sliderAction->trackPositionSlider()->setValue(0);
+ player()->seekPosition(0);
+ }
+ else {
+ m_playlistInterface->playNext();
+ m_file = m_playlistInterface->currentFile();
+
+ if(!m_file.isNull())
+ player()->play(m_file);
+ }
+ }
+ else {
+ m_file = file;
+ player()->play(file);
+ }
+
+ // Make sure that the player() actually starts before doing anything.
+
+ if(!player()->playing()) {
+ kdWarning(65432) << "Unable to play " << file.absFilePath() << endl;
+ stop();
+ return;
+ }
+
+ action("pause")->setEnabled(true);
+ action("stop")->setEnabled(true);
+ action("forward")->setEnabled(true);
+ if(action<KToggleAction>("albumRandomPlay")->isChecked())
+ action("forwardAlbum")->setEnabled(true);
+ action("back")->setEnabled(true);
+
+ if(m_sliderAction->trackPositionSlider())
+ m_sliderAction->trackPositionSlider()->setEnabled(true);
+
+ m_timer->start(m_pollInterval);
+
+ emit signalPlay();
+}
+
+void PlayerManager::play(const QString &file)
+{
+ CollectionListItem *item = CollectionList::instance()->lookup(file);
+ if(item) {
+ Playlist::setPlaying(item);
+ play(item->file());
+ }
+}
+
+void PlayerManager::play()
+{
+ play(FileHandle::null());
+}
+
+void PlayerManager::pause()
+{
+ if(!player())
+ return;
+
+ if(player()->paused()) {
+ play();
+ return;
+ }
+
+ m_timer->stop();
+ action("pause")->setEnabled(false);
+
+ player()->pause();
+
+ emit signalPause();
+}
+
+void PlayerManager::stop()
+{
+ if(!player() || !m_playlistInterface)
+ return;
+
+ m_timer->stop();
+
+ action("pause")->setEnabled(false);
+ action("stop")->setEnabled(false);
+ action("back")->setEnabled(false);
+ action("forward")->setEnabled(false);
+ action("forwardAlbum")->setEnabled(false);
+
+ if(m_sliderAction->trackPositionSlider()) {
+ m_sliderAction->trackPositionSlider()->setValue(0);
+ m_sliderAction->trackPositionSlider()->setEnabled(false);
+ }
+
+ player()->stop();
+ m_playlistInterface->stop();
+
+ m_file = FileHandle::null();
+
+ emit signalStop();
+}
+
+void PlayerManager::setVolume(float volume)
+{
+ if(!player())
+ return;
+
+ player()->setVolume(volume);
+}
+
+void PlayerManager::seek(int seekTime)
+{
+ if(!player())
+ return;
+
+ player()->seek(seekTime);
+}
+
+void PlayerManager::seekPosition(int position)
+{
+ if(!player())
+ return;
+
+ if(!player()->playing() || m_noSeek)
+ return;
+
+ slotUpdateTime(position);
+ player()->seekPosition(position);
+
+ if(m_sliderAction->trackPositionSlider())
+ m_sliderAction->trackPositionSlider()->setValue(position);
+}
+
+void PlayerManager::seekForward()
+{
+ seekPosition(kMin(SliderAction::maxPosition, position() + 10));
+}
+
+void PlayerManager::seekBack()
+{
+ seekPosition(kMax(SliderAction::minPosition, position() - 10));
+}
+
+void PlayerManager::playPause()
+{
+ playing() ? action("pause")->activate() : action("play")->activate();
+}
+
+void PlayerManager::forward()
+{
+ m_playlistInterface->playNext();
+ FileHandle file = m_playlistInterface->currentFile();
+
+ if(!file.isNull())
+ play(file);
+ else
+ stop();
+}
+
+void PlayerManager::back()
+{
+ m_playlistInterface->playPrevious();
+ FileHandle file = m_playlistInterface->currentFile();
+
+ if(!file.isNull())
+ play(file);
+ else
+ stop();
+}
+
+void PlayerManager::volumeUp()
+{
+ if(!player() || !m_sliderAction || !m_sliderAction->volumeSlider())
+ return;
+
+ int volume = m_sliderAction->volumeSlider()->volume() +
+ m_sliderAction->volumeSlider()->maxValue() / 25; // 4% up
+
+ slotSetVolume(volume);
+ m_sliderAction->volumeSlider()->setVolume(volume);
+}
+
+void PlayerManager::volumeDown()
+{
+ if(!player() || !m_sliderAction || !m_sliderAction->volumeSlider())
+ return;
+
+ int volume = m_sliderAction->volumeSlider()->value() -
+ m_sliderAction->volumeSlider()->maxValue() / 25; // 4% down
+
+ slotSetVolume(volume);
+ m_sliderAction->volumeSlider()->setVolume(volume);
+}
+
+void PlayerManager::mute()
+{
+ if(!player() || !m_sliderAction || !m_sliderAction->volumeSlider())
+ return;
+
+ slotSetVolume(m_muted ? m_sliderAction->volumeSlider()->value() : 0);
+ m_muted = !m_muted;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private slots
+////////////////////////////////////////////////////////////////////////////////
+
+void PlayerManager::slotPollPlay()
+{
+ if(!player() || !m_playlistInterface)
+ return;
+
+ m_noSeek = true;
+
+ if(!player()->playing()) {
+ m_timer->stop();
+
+ m_playlistInterface->playNext();
+ FileHandle nextFile = m_playlistInterface->currentFile();
+ if(!nextFile.isNull())
+ play(nextFile);
+ else
+ stop();
+ }
+ else if(!m_sliderAction->dragging()) {
+ if(m_sliderAction->trackPositionSlider())
+ m_sliderAction->trackPositionSlider()->setValue(player()->position());
+
+ if(m_statusLabel) {
+ m_statusLabel->setItemTotalTime(player()->totalTime());
+ m_statusLabel->setItemCurrentTime(player()->currentTime());
+ }
+ }
+
+ // This call is done because when the user adds the slider to the toolbar
+ // while playback is occuring the volume slider generally defaults to 0,
+ // and doesn't get updated to the correct volume. It might be better to
+ // have the SliderAction class fill in the correct volume, but I'm trying
+ // to avoid having it depend on PlayerManager since it may not be
+ // constructed in time during startup. -mpyne
+
+ if(!m_sliderAction->volumeDragging() && m_sliderAction->volumeSlider())
+ {
+ int maxV = m_sliderAction->volumeSlider()->maxValue();
+ float v = sqrt(sqrt(volume())); // Cancel out exponential scaling
+
+ m_sliderAction->volumeSlider()->blockSignals(true);
+ m_sliderAction->volumeSlider()->setVolume((int)((v) * maxV));
+ m_sliderAction->volumeSlider()->blockSignals(false);
+ }
+
+ // Ok, this is weird stuff, but it works pretty well. Ordinarily we don't
+ // need to check up on our playing time very often, but in the span of the
+ // last interval, we want to check a lot -- to figure out that we've hit the
+ // end of the song as soon as possible.
+
+ if(player()->playing() &&
+ player()->totalTime() > 0 &&
+ float(player()->totalTime() - player()->currentTime()) < m_pollInterval * 2)
+ {
+ m_timer->changeInterval(50);
+ }
+
+ m_noSeek = false;
+}
+
+void PlayerManager::slotSetOutput(const QString &system)
+{
+ stop();
+ setOutput(system);
+ setup();
+}
+
+void PlayerManager::setOutput(const QString &system)
+{
+ delete m_player;
+ if(system == i18n("aRts"))
+ m_player = createPlayer(ArtsBackend);
+ else if(system == i18n("GStreamer"))
+ m_player = createPlayer(GStreamerBackend);
+ else if(system == i18n("aKode"))
+ m_player = createPlayer(AkodeBackend);
+}
+
+void PlayerManager::slotSetVolume(int volume)
+{
+ float scaledVolume;
+
+ if(m_sliderAction->volumeSlider())
+ scaledVolume = float(volume) / m_sliderAction->volumeSlider()->maxValue();
+ else {
+ scaledVolume = float(volume) / 100.0; // Hopefully this is accurate
+ scaledVolume = kMin(1.0f, scaledVolume);
+ }
+
+ // Perform exponential scaling to counteract the fact that humans perceive
+ // volume changes logarithmically.
+
+ scaledVolume *= scaledVolume;
+ scaledVolume *= scaledVolume;
+ setVolume(scaledVolume); // scaledVolume ^ 4
+}
+
+void PlayerManager::slotUpdateTime(int position)
+{
+ if(!m_statusLabel)
+ return;
+
+ float positionFraction = float(position) / SliderAction::maxPosition;
+ float totalTime = float(player()->totalTime());
+ int seekTime = int(positionFraction * totalTime + 0.5); // "+0.5" for rounding
+
+ m_statusLabel->setItemCurrentTime(seekTime);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+Player *PlayerManager::player() const
+{
+ if(!m_player)
+ instance()->setup();
+
+ return m_player;
+}
+
+void PlayerManager::setup()
+{
+ // All of the actions required by this class should be listed here.
+
+ if(!action("pause") ||
+ !action("stop") ||
+ !action("back") ||
+ !action("forwardAlbum") ||
+ !action("forward") ||
+ !action("trackPositionAction"))
+ {
+ kdWarning(65432) << k_funcinfo << "Could not find all of the required actions." << endl;
+ return;
+ }
+
+ if(m_setup)
+ return;
+ m_setup = true;
+
+ // initialize action states
+
+ action("pause")->setEnabled(false);
+ action("stop")->setEnabled(false);
+ action("back")->setEnabled(false);
+ action("forward")->setEnabled(false);
+ action("forwardAlbum")->setEnabled(false);
+
+ // setup sliders
+
+ m_sliderAction = action<SliderAction>("trackPositionAction");
+
+ connect(m_sliderAction, SIGNAL(signalPositionChanged(int)),
+ this, SLOT(seekPosition(int)));
+ connect(m_sliderAction->trackPositionSlider(), SIGNAL(valueChanged(int)),
+ this, SLOT(slotUpdateTime(int)));
+ connect(m_sliderAction, SIGNAL(signalVolumeChanged(int)),
+ this, SLOT(slotSetVolume(int)));
+
+ // Call this method manually to avoid warnings.
+
+ KAction *outputAction = actions()->action("outputSelect");
+
+ if(outputAction) {
+ setOutput(static_cast<KSelectAction *>(outputAction)->currentText());
+ connect(outputAction, SIGNAL(activated(const QString &)), this, SLOT(slotSetOutput(const QString &)));
+ }
+ else
+ m_player = createPlayer();
+
+ float volume;
+
+ if(m_sliderAction->volumeSlider()) {
+ volume =
+ float(m_sliderAction->volumeSlider()->volume()) /
+ float(m_sliderAction->volumeSlider()->maxValue());
+ }
+ else
+ volume = 1; // Assume user wants full volume
+
+ m_player->setVolume(volume);
+
+ m_timer = new QTimer(this, "play timer");
+ connect(m_timer, SIGNAL(timeout()), this, SLOT(slotPollPlay()));
+}
+
+QString PlayerManager::randomPlayMode() const
+{
+ if(action<KToggleAction>("randomPlay")->isChecked())
+ return "Random";
+ if(action<KToggleAction>("albumRandomPlay")->isChecked())
+ return "AlbumRandom";
+ return "NoRandom";
+}
+
+void PlayerManager::setRandomPlayMode(const QString &randomMode)
+{
+ if(randomMode.lower() == "random")
+ action<KToggleAction>("randomPlay")->setChecked(true);
+ if(randomMode.lower() == "albumrandom")
+ action<KToggleAction>("albumRandomPlay")->setChecked(true);
+ if(randomMode.lower() == "norandom")
+ action<KToggleAction>("disableRandomPlay")->setChecked(true);
+}
+
+#include "playermanager.moc"
+
+// vim: set et ts=4 sw=4:
diff --git a/juk/playermanager.h b/juk/playermanager.h
new file mode 100644
index 00000000..8f1920b1
--- /dev/null
+++ b/juk/playermanager.h
@@ -0,0 +1,117 @@
+/***************************************************************************
+ begin : Sat Feb 14 2004
+ copyright : (C) 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef PLAYERMANAGER_H
+#define PLAYERMANAGER_H
+
+#include "player.h"
+#include "jukIface.h"
+
+class QTimer;
+class KSelectAction;
+class SliderAction;
+class StatusLabel;
+class PlaylistInterface;
+
+/**
+ * This class serves as a proxy to the Player interface and handles managing
+ * the actions from the top-level mainwindow.
+ */
+
+class PlayerManager : public Player, public PlayerIface
+{
+ Q_OBJECT
+
+protected:
+ PlayerManager();
+ virtual ~PlayerManager();
+
+public:
+ static PlayerManager *instance();
+
+ bool playing() const;
+ bool paused() const;
+ float volume() const;
+ int status() const;
+ int totalTime() const;
+ int currentTime() const;
+ int position() const;
+
+ QStringList trackProperties();
+ QString trackProperty(const QString &property) const;
+ QPixmap trackCover(const QString &size) const;
+
+ FileHandle playingFile() const;
+ QString playingString() const;
+
+ void setPlaylistInterface(PlaylistInterface *interface);
+ void setStatusLabel(StatusLabel *label);
+
+ QString randomPlayMode() const;
+
+ static KSelectAction *playerSelectAction(QObject *parent);
+
+public slots:
+
+ void play(const FileHandle &file);
+ void play(const QString &file);
+ void play();
+ void pause();
+ void stop();
+ void setVolume(float volume = 1.0);
+ void seek(int seekTime);
+ void seekPosition(int position);
+ void seekForward();
+ void seekBack();
+ void playPause();
+ void forward();
+ void back();
+ void volumeUp();
+ void volumeDown();
+ void mute();
+
+ void setRandomPlayMode(const QString &randomMode);
+
+signals:
+ void signalPlay();
+ void signalPause();
+ void signalStop();
+
+private:
+ Player *player() const;
+ void setup();
+ void setOutput(const QString &);
+
+private slots:
+ void slotPollPlay();
+ void slotUpdateTime(int position);
+ void slotSetOutput(const QString &);
+ void slotSetVolume(int volume);
+
+private:
+ FileHandle m_file;
+ SliderAction *m_sliderAction;
+ PlaylistInterface *m_playlistInterface;
+ StatusLabel *m_statusLabel;
+ Player *m_player;
+ QTimer *m_timer;
+ bool m_noSeek;
+ bool m_muted;
+ bool m_setup;
+
+ static const int m_pollInterval = 800;
+};
+
+#endif
diff --git a/juk/playlist.cpp b/juk/playlist.cpp
new file mode 100644
index 00000000..b090dca2
--- /dev/null
+++ b/juk/playlist.cpp
@@ -0,0 +1,2361 @@
+/***************************************************************************
+ begin : Sat Feb 16 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kconfig.h>
+#include <kmessagebox.h>
+#include <kurldrag.h>
+#include <kiconloader.h>
+#include <klineedit.h>
+#include <kaction.h>
+#include <kpopupmenu.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kinputdialog.h>
+#include <kfiledialog.h>
+#include <kglobalsettings.h>
+#include <kurl.h>
+#include <kio/netaccess.h>
+#include <kio/job.h>
+#include <dcopclient.h>
+
+#include <qheader.h>
+#include <qcursor.h>
+#include <qdir.h>
+#include <qeventloop.h>
+#include <qtooltip.h>
+#include <qwidgetstack.h>
+#include <qfile.h>
+#include <qhbox.h>
+
+#include <id3v1genres.h>
+
+#include <time.h>
+#include <math.h>
+#include <dirent.h>
+
+#include "playlist.h"
+#include "playlistitem.h"
+#include "playlistcollection.h"
+#include "playlistsearch.h"
+#include "mediafiles.h"
+#include "collectionlist.h"
+#include "filerenamer.h"
+#include "actioncollection.h"
+#include "tracksequencemanager.h"
+#include "juk.h"
+#include "tag.h"
+#include "k3bexporter.h"
+#include "upcomingplaylist.h"
+#include "deletedialog.h"
+#include "webimagefetcher.h"
+#include "coverinfo.h"
+#include "coverdialog.h"
+#include "tagtransactionmanager.h"
+#include "cache.h"
+
+using namespace ActionCollection;
+
+/**
+ * Just a shortcut of sorts.
+ */
+
+static bool manualResize()
+{
+ return action<KToggleAction>("resizeColumnsManually")->isChecked();
+}
+
+/**
+ * A tooltip specialized to show full filenames over the file name column.
+ */
+
+class PlaylistToolTip : public QToolTip
+{
+public:
+ PlaylistToolTip(QWidget *parent, Playlist *playlist) :
+ QToolTip(parent), m_playlist(playlist) {}
+
+ virtual void maybeTip(const QPoint &p)
+ {
+ PlaylistItem *item = static_cast<PlaylistItem *>(m_playlist->itemAt(p));
+
+ if(!item)
+ return;
+
+ QPoint contentsPosition = m_playlist->viewportToContents(p);
+
+ int column = m_playlist->header()->sectionAt(contentsPosition.x());
+
+ if(column == m_playlist->columnOffset() + PlaylistItem::FileNameColumn ||
+ item->cachedWidths()[column] > m_playlist->columnWidth(column) ||
+ (column == m_playlist->columnOffset() + PlaylistItem::CoverColumn &&
+ item->file().coverInfo()->hasCover()))
+ {
+ QRect r = m_playlist->itemRect(item);
+ int headerPosition = m_playlist->header()->sectionPos(column);
+ r.setLeft(headerPosition);
+ r.setRight(headerPosition + m_playlist->header()->sectionSize(column));
+
+ if(column == m_playlist->columnOffset() + PlaylistItem::FileNameColumn)
+ tip(r, item->file().absFilePath());
+ else if(column == m_playlist->columnOffset() + PlaylistItem::CoverColumn) {
+ QMimeSourceFactory *f = QMimeSourceFactory::defaultFactory();
+ f->setImage("coverThumb",
+ QImage(item->file().coverInfo()->pixmap(CoverInfo::Thumbnail).convertToImage()));
+ tip(r, "<center><img source=\"coverThumb\"/></center>");
+ }
+ else
+ tip(r, item->text(column));
+ }
+ }
+
+private:
+ Playlist *m_playlist;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Playlist::SharedSettings definition
+////////////////////////////////////////////////////////////////////////////////
+
+bool Playlist::m_visibleChanged = false;
+bool Playlist::m_shuttingDown = false;
+
+/**
+ * Shared settings between the playlists.
+ */
+
+class Playlist::SharedSettings
+{
+public:
+ static SharedSettings *instance();
+ /**
+ * Sets the default column order to that of Playlist @param p.
+ */
+ void setColumnOrder(const Playlist *l);
+ void toggleColumnVisible(int column);
+ void setInlineCompletionMode(KGlobalSettings::Completion mode);
+
+ /**
+ * Apply the settings.
+ */
+ void apply(Playlist *l) const;
+ void sync() { writeConfig(); }
+
+protected:
+ SharedSettings();
+ ~SharedSettings() {}
+
+private:
+ void writeConfig();
+
+ static SharedSettings *m_instance;
+ QValueList<int> m_columnOrder;
+ QValueVector<bool> m_columnsVisible;
+ KGlobalSettings::Completion m_inlineCompletion;
+};
+
+Playlist::SharedSettings *Playlist::SharedSettings::m_instance = 0;
+
+////////////////////////////////////////////////////////////////////////////////
+// Playlist::SharedSettings public members
+////////////////////////////////////////////////////////////////////////////////
+
+Playlist::SharedSettings *Playlist::SharedSettings::instance()
+{
+ static SharedSettings settings;
+ return &settings;
+}
+
+void Playlist::SharedSettings::setColumnOrder(const Playlist *l)
+{
+ if(!l)
+ return;
+
+ m_columnOrder.clear();
+
+ for(int i = l->columnOffset(); i < l->columns(); ++i)
+ m_columnOrder.append(l->header()->mapToIndex(i));
+
+ writeConfig();
+}
+
+void Playlist::SharedSettings::toggleColumnVisible(int column)
+{
+ if(column >= int(m_columnsVisible.size()))
+ m_columnsVisible.resize(column + 1, true);
+
+ m_columnsVisible[column] = !m_columnsVisible[column];
+
+ writeConfig();
+}
+
+void Playlist::SharedSettings::setInlineCompletionMode(KGlobalSettings::Completion mode)
+{
+ m_inlineCompletion = mode;
+ writeConfig();
+}
+
+
+void Playlist::SharedSettings::apply(Playlist *l) const
+{
+ if(!l)
+ return;
+
+ int offset = l->columnOffset();
+ int i = 0;
+ for(QValueListConstIterator<int> it = m_columnOrder.begin(); it != m_columnOrder.end(); ++it)
+ l->header()->moveSection(i++ + offset, *it + offset);
+
+ for(uint i = 0; i < m_columnsVisible.size(); i++) {
+ if(m_columnsVisible[i] && !l->isColumnVisible(i + offset))
+ l->showColumn(i + offset, false);
+ else if(!m_columnsVisible[i] && l->isColumnVisible(i + offset))
+ l->hideColumn(i + offset, false);
+ }
+
+ l->updateLeftColumn();
+ l->renameLineEdit()->setCompletionMode(m_inlineCompletion);
+ l->slotColumnResizeModeChanged();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Playlist::ShareSettings protected members
+////////////////////////////////////////////////////////////////////////////////
+
+Playlist::SharedSettings::SharedSettings()
+{
+ KConfigGroup config(KGlobal::config(), "PlaylistShared");
+
+ bool resizeColumnsManually = config.readBoolEntry("ResizeColumnsManually", false);
+ action<KToggleAction>("resizeColumnsManually")->setChecked(resizeColumnsManually);
+
+ // save column order
+ m_columnOrder = config.readIntListEntry("ColumnOrder");
+
+ QValueList<int> l = config.readIntListEntry("VisibleColumns");
+
+ if(l.isEmpty()) {
+
+ // Provide some default values for column visibility if none were
+ // read from the configuration file.
+
+ for(int i = 0; i <= PlaylistItem::lastColumn(); i++) {
+ switch(i) {
+ case PlaylistItem::BitrateColumn:
+ case PlaylistItem::CommentColumn:
+ case PlaylistItem::FileNameColumn:
+ case PlaylistItem::FullPathColumn:
+ m_columnsVisible.append(false);
+ break;
+ default:
+ m_columnsVisible.append(true);
+ }
+ }
+ }
+ else {
+ // Convert the int list into a bool list.
+
+ m_columnsVisible.resize(l.size(), true);
+ uint i = 0;
+ for(QValueList<int>::Iterator it = l.begin(); it != l.end(); ++it) {
+ if(! bool(*it))
+ m_columnsVisible[i] = bool(*it);
+ i++;
+ }
+ }
+
+ m_inlineCompletion = KGlobalSettings::Completion(
+ config.readNumEntry("InlineCompletionMode", KGlobalSettings::CompletionAuto));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Playlist::SharedSettings private members
+////////////////////////////////////////////////////////////////////////////////
+
+void Playlist::SharedSettings::writeConfig()
+{
+ KConfigGroup config(KGlobal::config(), "PlaylistShared");
+ config.writeEntry("ColumnOrder", m_columnOrder);
+
+ QValueList<int> l;
+ for(uint i = 0; i < m_columnsVisible.size(); i++)
+ l.append(int(m_columnsVisible[i]));
+
+ config.writeEntry("VisibleColumns", l);
+ config.writeEntry("InlineCompletionMode", m_inlineCompletion);
+
+ config.writeEntry("ResizeColumnsManually", manualResize());
+
+ KGlobal::config()->sync();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+PlaylistItemList Playlist::m_history;
+QMap<int, PlaylistItem *> Playlist::m_backMenuItems;
+int Playlist::m_leftColumn = 0;
+
+Playlist::Playlist(PlaylistCollection *collection, const QString &name,
+ const QString &iconName) :
+ KListView(collection->playlistStack(), name.latin1()),
+ m_collection(collection),
+ m_fetcher(new WebImageFetcher(this)),
+ m_selectedCount(0),
+ m_allowDuplicates(false),
+ m_polished(false),
+ m_applySharedSettings(true),
+ m_columnWidthModeChanged(false),
+ m_disableColumnWidthUpdates(true),
+ m_time(0),
+ m_widthsDirty(true),
+ m_searchEnabled(true),
+ m_lastSelected(0),
+ m_playlistName(name),
+ m_rmbMenu(0),
+ m_toolTip(0),
+ m_blockDataChanged(false)
+{
+ setup();
+ collection->setupPlaylist(this, iconName);
+}
+
+Playlist::Playlist(PlaylistCollection *collection, const PlaylistItemList &items,
+ const QString &name, const QString &iconName) :
+ KListView(collection->playlistStack(), name.latin1()),
+ m_collection(collection),
+ m_fetcher(new WebImageFetcher(this)),
+ m_selectedCount(0),
+ m_allowDuplicates(false),
+ m_polished(false),
+ m_applySharedSettings(true),
+ m_columnWidthModeChanged(false),
+ m_disableColumnWidthUpdates(true),
+ m_time(0),
+ m_widthsDirty(true),
+ m_searchEnabled(true),
+ m_lastSelected(0),
+ m_playlistName(name),
+ m_rmbMenu(0),
+ m_toolTip(0),
+ m_blockDataChanged(false)
+{
+ setup();
+ collection->setupPlaylist(this, iconName);
+ createItems(items);
+}
+
+Playlist::Playlist(PlaylistCollection *collection, const QFileInfo &playlistFile,
+ const QString &iconName) :
+ KListView(collection->playlistStack()),
+ m_collection(collection),
+ m_fetcher(new WebImageFetcher(this)),
+ m_selectedCount(0),
+ m_allowDuplicates(false),
+ m_polished(false),
+ m_applySharedSettings(true),
+ m_columnWidthModeChanged(false),
+ m_disableColumnWidthUpdates(true),
+ m_time(0),
+ m_widthsDirty(true),
+ m_searchEnabled(true),
+ m_lastSelected(0),
+ m_fileName(playlistFile.absFilePath()),
+ m_rmbMenu(0),
+ m_toolTip(0),
+ m_blockDataChanged(false)
+{
+ setup();
+ loadFile(m_fileName, playlistFile);
+ collection->setupPlaylist(this, iconName);
+}
+
+Playlist::Playlist(PlaylistCollection *collection, bool delaySetup) :
+ KListView(collection->playlistStack()),
+ m_collection(collection),
+ m_fetcher(new WebImageFetcher(this)),
+ m_selectedCount(0),
+ m_allowDuplicates(false),
+ m_polished(false),
+ m_applySharedSettings(true),
+ m_columnWidthModeChanged(false),
+ m_disableColumnWidthUpdates(true),
+ m_time(0),
+ m_widthsDirty(true),
+ m_searchEnabled(true),
+ m_lastSelected(0),
+ m_rmbMenu(0),
+ m_toolTip(0),
+ m_blockDataChanged(false)
+{
+ setup();
+
+ if(!delaySetup)
+ collection->setupPlaylist(this, "midi");
+}
+
+Playlist::~Playlist()
+{
+ // clearItem() will take care of removing the items from the history,
+ // so call clearItems() to make sure it happens.
+
+ clearItems(items());
+
+ delete m_toolTip;
+
+ // Select a different playlist if we're the selected one
+
+ if(isVisible() && this != CollectionList::instance())
+ m_collection->raise(CollectionList::instance());
+
+ if(!m_shuttingDown)
+ m_collection->removePlaylist(this);
+}
+
+QString Playlist::name() const
+{
+ if(m_playlistName.isNull())
+ return m_fileName.section(QDir::separator(), -1).section('.', 0, -2);
+ else
+ return m_playlistName;
+}
+
+FileHandle Playlist::currentFile() const
+{
+ return playingItem() ? playingItem()->file() : FileHandle::null();
+}
+
+int Playlist::time() const
+{
+ // Since this method gets a lot of traffic, let's optimize for such.
+
+ if(!m_addTime.isEmpty()) {
+ for(PlaylistItemList::ConstIterator it = m_addTime.begin();
+ it != m_addTime.end(); ++it)
+ {
+ if(*it)
+ m_time += (*it)->file().tag()->seconds();
+ }
+
+ m_addTime.clear();
+ }
+
+ if(!m_subtractTime.isEmpty()) {
+ for(PlaylistItemList::ConstIterator it = m_subtractTime.begin();
+ it != m_subtractTime.end(); ++it)
+ {
+ if(*it)
+ m_time -= (*it)->file().tag()->seconds();
+ }
+
+ m_subtractTime.clear();
+ }
+
+ return m_time;
+}
+
+void Playlist::playFirst()
+{
+ TrackSequenceManager::instance()->setNextItem(static_cast<PlaylistItem *>(
+ QListViewItemIterator(const_cast<Playlist *>(this), QListViewItemIterator::Visible).current()));
+ action("forward")->activate();
+}
+
+void Playlist::playNextAlbum()
+{
+ PlaylistItem *current = TrackSequenceManager::instance()->currentItem();
+ if(!current)
+ return; // No next album if we're not already playing.
+
+ QString currentAlbum = current->file().tag()->album();
+ current = TrackSequenceManager::instance()->nextItem();
+
+ while(current && current->file().tag()->album() == currentAlbum)
+ current = TrackSequenceManager::instance()->nextItem();
+
+ TrackSequenceManager::instance()->setNextItem(current);
+ action("forward")->activate();
+}
+
+void Playlist::playNext()
+{
+ TrackSequenceManager::instance()->setCurrentPlaylist(this);
+ setPlaying(TrackSequenceManager::instance()->nextItem());
+}
+
+void Playlist::stop()
+{
+ m_history.clear();
+ setPlaying(0);
+}
+
+void Playlist::playPrevious()
+{
+ if(!playingItem())
+ return;
+
+ bool random = action("randomPlay") && action<KToggleAction>("randomPlay")->isChecked();
+
+ PlaylistItem *previous = 0;
+
+ if(random && !m_history.isEmpty()) {
+ PlaylistItemList::Iterator last = m_history.fromLast();
+ previous = *last;
+ m_history.remove(last);
+ }
+ else {
+ m_history.clear();
+ previous = TrackSequenceManager::instance()->previousItem();
+ }
+
+ if(!previous)
+ previous = static_cast<PlaylistItem *>(playingItem()->itemAbove());
+
+ setPlaying(previous, false);
+}
+
+void Playlist::setName(const QString &n)
+{
+ m_collection->addNameToDict(n);
+ m_collection->removeNameFromDict(m_playlistName);
+
+ m_playlistName = n;
+ emit signalNameChanged(m_playlistName);
+}
+
+void Playlist::save()
+{
+ if(m_fileName.isEmpty())
+ return saveAs();
+
+ QFile file(m_fileName);
+
+ if(!file.open(IO_WriteOnly))
+ return KMessageBox::error(this, i18n("Could not save to file %1.").arg(m_fileName));
+
+ QTextStream stream(&file);
+
+ QStringList fileList = files();
+
+ for(QStringList::Iterator it = fileList.begin(); it != fileList.end(); ++it)
+ stream << *it << endl;
+
+ file.close();
+}
+
+void Playlist::saveAs()
+{
+ m_collection->removeFileFromDict(m_fileName);
+
+ m_fileName = MediaFiles::savePlaylistDialog(name(), this);
+
+ if(!m_fileName.isEmpty()) {
+ m_collection->addFileToDict(m_fileName);
+
+ // If there's no playlist name set, use the file name.
+ if(m_playlistName.isEmpty())
+ emit signalNameChanged(name());
+ save();
+ }
+}
+
+void Playlist::clearItem(PlaylistItem *item, bool emitChanged)
+{
+ emit signalAboutToRemove(item);
+ m_members.remove(item->file().absFilePath());
+ m_search.clearItem(item);
+
+ m_history.remove(item);
+ m_addTime.remove(item);
+ m_subtractTime.remove(item);
+
+ delete item;
+ if(emitChanged)
+ dataChanged();
+}
+
+void Playlist::clearItems(const PlaylistItemList &items)
+{
+ m_blockDataChanged = true;
+
+ for(PlaylistItemList::ConstIterator it = items.begin(); it != items.end(); ++it)
+ clearItem(*it, false);
+
+ m_blockDataChanged = false;
+
+ dataChanged();
+}
+
+PlaylistItem *Playlist::playingItem() // static
+{
+ return PlaylistItem::playingItems().isEmpty() ? 0 : PlaylistItem::playingItems().front();
+}
+
+QStringList Playlist::files() const
+{
+ QStringList list;
+
+ for(QListViewItemIterator it(const_cast<Playlist *>(this)); it.current(); ++it)
+ list.append(static_cast<PlaylistItem *>(*it)->file().absFilePath());
+
+ return list;
+}
+
+PlaylistItemList Playlist::items()
+{
+ return items(QListViewItemIterator::IteratorFlag(0));
+}
+
+PlaylistItemList Playlist::visibleItems()
+{
+ return items(QListViewItemIterator::Visible);
+}
+
+PlaylistItemList Playlist::selectedItems()
+{
+ PlaylistItemList list;
+
+ switch(m_selectedCount) {
+ case 0:
+ break;
+ // case 1:
+ // list.append(m_lastSelected);
+ // break;
+ default:
+ list = items(QListViewItemIterator::IteratorFlag(QListViewItemIterator::Selected |
+ QListViewItemIterator::Visible));
+ break;
+ }
+
+ return list;
+}
+
+PlaylistItem *Playlist::firstChild() const
+{
+ return static_cast<PlaylistItem *>(KListView::firstChild());
+}
+
+void Playlist::updateLeftColumn()
+{
+ int newLeftColumn = leftMostVisibleColumn();
+
+ if(m_leftColumn != newLeftColumn) {
+ updatePlaying();
+ m_leftColumn = newLeftColumn;
+ }
+}
+
+void Playlist::setItemsVisible(const PlaylistItemList &items, bool visible) // static
+{
+ m_visibleChanged = true;
+ for(PlaylistItemList::ConstIterator it = items.begin(); it != items.end(); ++it)
+ (*it)->setVisible(visible);
+}
+
+void Playlist::setSearch(const PlaylistSearch &s)
+{
+ m_search = s;
+
+ if(!m_searchEnabled)
+ return;
+
+ setItemsVisible(s.matchedItems(), true);
+ setItemsVisible(s.unmatchedItems(), false);
+
+ TrackSequenceManager::instance()->iterator()->playlistChanged();
+}
+
+void Playlist::setSearchEnabled(bool enabled)
+{
+ if(m_searchEnabled == enabled)
+ return;
+
+ m_searchEnabled = enabled;
+
+ if(enabled) {
+ setItemsVisible(m_search.matchedItems(), true);
+ setItemsVisible(m_search.unmatchedItems(), false);
+ }
+ else
+ setItemsVisible(items(), true);
+}
+
+void Playlist::markItemSelected(PlaylistItem *item, bool selected)
+{
+ if(selected && !item->isSelected()) {
+ m_selectedCount++;
+ m_lastSelected = item;
+ }
+ else if(!selected && item->isSelected())
+ m_selectedCount--;
+}
+
+void Playlist::synchronizePlayingItems(const PlaylistList &sources, bool setMaster)
+{
+ for(PlaylistList::ConstIterator it = sources.begin(); it != sources.end(); ++it) {
+ if((*it)->playing()) {
+ CollectionListItem *base = playingItem()->collectionItem();
+ for(QListViewItemIterator itemIt(this); itemIt.current(); ++itemIt) {
+ PlaylistItem *item = static_cast<PlaylistItem *>(itemIt.current());
+ if(base == item->collectionItem()) {
+ item->setPlaying(true, setMaster);
+ PlaylistItemList playing = PlaylistItem::playingItems();
+ TrackSequenceManager::instance()->setCurrent(item);
+ return;
+ }
+ }
+ return;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public slots
+////////////////////////////////////////////////////////////////////////////////
+
+void Playlist::copy()
+{
+ kapp->clipboard()->setData(dragObject(0), QClipboard::Clipboard);
+}
+
+void Playlist::paste()
+{
+ decode(kapp->clipboard()->data(), static_cast<PlaylistItem *>(currentItem()));
+}
+
+void Playlist::clear()
+{
+ PlaylistItemList l = selectedItems();
+ if(l.isEmpty())
+ l = items();
+
+ clearItems(l);
+}
+
+void Playlist::slotRefresh()
+{
+ PlaylistItemList l = selectedItems();
+ if(l.isEmpty())
+ l = visibleItems();
+
+ KApplication::setOverrideCursor(Qt::waitCursor);
+ for(PlaylistItemList::Iterator it = l.begin(); it != l.end(); ++it) {
+ (*it)->refreshFromDisk();
+
+ if(!(*it)->file().tag() || !(*it)->file().fileInfo().exists()) {
+ kdDebug(65432) << "Error while trying to refresh the tag. "
+ << "This file has probably been removed."
+ << endl;
+ clearItem((*it)->collectionItem());
+ }
+
+ processEvents();
+ }
+ KApplication::restoreOverrideCursor();
+}
+
+void Playlist::slotRenameFile()
+{
+ FileRenamer renamer;
+ PlaylistItemList items = selectedItems();
+
+ if(items.isEmpty())
+ return;
+
+ emit signalEnableDirWatch(false);
+
+ m_blockDataChanged = true;
+ renamer.rename(items);
+ m_blockDataChanged = false;
+ dataChanged();
+
+ emit signalEnableDirWatch(true);
+}
+
+void Playlist::slotViewCover()
+{
+ PlaylistItemList items = selectedItems();
+ if (items.isEmpty())
+ return;
+ for(PlaylistItemList::Iterator it = items.begin(); it != items.end(); ++it)
+ (*it)->file().coverInfo()->popup();
+}
+
+void Playlist::slotRemoveCover()
+{
+ PlaylistItemList items = selectedItems();
+ if(items.isEmpty())
+ return;
+ int button = KMessageBox::warningContinueCancel(this,
+ i18n("Are you sure you want to delete these covers?"),
+ QString::null,
+ i18n("&Delete Covers"));
+ if(button == KMessageBox::Continue)
+ refreshAlbums(items);
+}
+
+void Playlist::slotShowCoverManager()
+{
+ static CoverDialog *managerDialog = 0;
+
+ if(!managerDialog)
+ managerDialog = new CoverDialog(this);
+
+ managerDialog->show();
+}
+
+unsigned int Playlist::eligibleCoverItems(const PlaylistItemList &items)
+{
+ // This used to count the number of tracks with an artist and album, that
+ // is not strictly required anymore. This may prove useful in the future
+ // so I'm leaving it in for now, right now we just mark every item as
+ // eligible.
+
+ return items.count();
+}
+
+void Playlist::slotAddCover(bool retrieveLocal)
+{
+ PlaylistItemList items = selectedItems();
+
+ if(items.isEmpty())
+ return;
+
+ if(eligibleCoverItems(items) == 0) {
+ // No items in the list can be assigned a cover, inform the user and
+ // bail.
+
+ // KDE 4.0 Fix this string.
+ KMessageBox::sorry(this, i18n("None of the items you have selected can "
+ "be assigned a cover. A track must have both the Artist "
+ "and Album tags set to be assigned a cover."));
+
+ return;
+ }
+
+ QPixmap newCover;
+
+ if(retrieveLocal) {
+ KURL file = KFileDialog::getImageOpenURL(
+ ":homedir", this, i18n("Select Cover Image File"));
+ newCover = QPixmap(file.directory() + "/" + file.fileName());
+ }
+ else {
+ m_fetcher->setFile((*items.begin())->file());
+ m_fetcher->chooseCover();
+ return;
+ }
+
+ if(newCover.isNull())
+ return;
+
+ QString artist = items.front()->file().tag()->artist();
+ QString album = items.front()->file().tag()->album();
+
+ coverKey newId = CoverManager::addCover(newCover, artist, album);
+ refreshAlbums(items, newId);
+}
+
+// Called when image fetcher has added a new cover.
+void Playlist::slotCoverChanged(int coverId)
+{
+ kdDebug(65432) << "Refreshing information for newly changed covers.\n";
+ refreshAlbums(selectedItems(), coverId);
+}
+
+void Playlist::slotGuessTagInfo(TagGuesser::Type type)
+{
+ KApplication::setOverrideCursor(Qt::waitCursor);
+ PlaylistItemList items = selectedItems();
+ setDynamicListsFrozen(true);
+
+ m_blockDataChanged = true;
+
+ for(PlaylistItemList::Iterator it = items.begin(); it != items.end(); ++it) {
+ (*it)->guessTagInfo(type);
+ processEvents();
+ }
+
+ // MusicBrainz queries automatically commit at this point. What would
+ // be nice is having a signal emitted when the last query is completed.
+
+ if(type == TagGuesser::FileName)
+ TagTransactionManager::instance()->commit();
+
+ m_blockDataChanged = false;
+
+ dataChanged();
+ setDynamicListsFrozen(false);
+ KApplication::restoreOverrideCursor();
+}
+
+void Playlist::slotReload()
+{
+ QFileInfo fileInfo(m_fileName);
+ if(!fileInfo.exists() || !fileInfo.isFile() || !fileInfo.isReadable())
+ return;
+
+ clearItems(items());
+ loadFile(m_fileName, fileInfo);
+}
+
+void Playlist::slotWeightDirty(int column)
+{
+ if(column < 0) {
+ m_weightDirty.clear();
+ for(int i = 0; i < columns(); i++) {
+ if(isColumnVisible(i))
+ m_weightDirty.append(i);
+ }
+ return;
+ }
+
+ if(m_weightDirty.find(column) == m_weightDirty.end())
+ m_weightDirty.append(column);
+}
+
+void Playlist::slotShowPlaying()
+{
+ if(!playingItem())
+ return;
+
+ Playlist *l = playingItem()->playlist();
+
+ l->clearSelection();
+
+ // Raise the playlist before selecting the items otherwise the tag editor
+ // will not update when it gets the selectionChanged() notification
+ // because it will think the user is choosing a different playlist but not
+ // selecting a different item.
+
+ m_collection->raise(l);
+
+ l->setSelected(playingItem(), true);
+ l->setCurrentItem(playingItem());
+ l->ensureItemVisible(playingItem());
+}
+
+void Playlist::slotColumnResizeModeChanged()
+{
+ if(manualResize())
+ setHScrollBarMode(Auto);
+ else
+ setHScrollBarMode(AlwaysOff);
+
+ if(!manualResize())
+ slotUpdateColumnWidths();
+
+ SharedSettings::instance()->sync();
+}
+
+void Playlist::dataChanged()
+{
+ if(m_blockDataChanged)
+ return;
+ PlaylistInterface::dataChanged();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void Playlist::removeFromDisk(const PlaylistItemList &items)
+{
+ if(isVisible() && !items.isEmpty()) {
+
+ QStringList files;
+ for(PlaylistItemList::ConstIterator it = items.begin(); it != items.end(); ++it)
+ files.append((*it)->file().absFilePath());
+
+ DeleteDialog dialog(this);
+
+ m_blockDataChanged = true;
+
+ if(dialog.confirmDeleteList(files)) {
+ bool shouldDelete = dialog.shouldDelete();
+ QStringList errorFiles;
+
+ for(PlaylistItemList::ConstIterator it = items.begin(); it != items.end(); ++it) {
+ if(playingItem() == *it)
+ action("forward")->activate();
+
+ QString removePath = (*it)->file().absFilePath();
+ if((!shouldDelete && KIO::NetAccess::synchronousRun(KIO::trash(removePath), this)) ||
+ (shouldDelete && QFile::remove(removePath)))
+ {
+ CollectionList::instance()->clearItem((*it)->collectionItem());
+ }
+ else
+ errorFiles.append((*it)->file().absFilePath());
+ }
+
+ if(!errorFiles.isEmpty()) {
+ QString errorMsg = shouldDelete ?
+ i18n("Could not delete these files") :
+ i18n("Could not move these files to the Trash");
+ KMessageBox::errorList(this, errorMsg, errorFiles);
+ }
+ }
+
+ m_blockDataChanged = false;
+
+ dataChanged();
+ }
+}
+
+QDragObject *Playlist::dragObject(QWidget *parent)
+{
+ PlaylistItemList items = selectedItems();
+ KURL::List urls;
+ for(PlaylistItemList::Iterator it = items.begin(); it != items.end(); ++it) {
+ KURL url;
+ url.setPath((*it)->file().absFilePath());
+ urls.append(url);
+ }
+
+ KURLDrag *drag = new KURLDrag(urls, parent, "Playlist Items");
+ drag->setPixmap(BarIcon("sound"));
+
+ return drag;
+}
+
+void Playlist::contentsDragEnterEvent(QDragEnterEvent *e)
+{
+ KListView::contentsDragEnterEvent(e);
+
+ if(CoverDrag::canDecode(e)) {
+ setDropHighlighter(true);
+ setDropVisualizer(false);
+
+ e->accept();
+ return;
+ }
+
+ setDropHighlighter(false);
+ setDropVisualizer(true);
+
+ KURL::List urls;
+ if(!KURLDrag::decode(e, urls) || urls.isEmpty()) {
+ e->ignore();
+ return;
+ }
+
+ e->accept();
+ return;
+}
+
+bool Playlist::acceptDrag(QDropEvent *e) const
+{
+ return CoverDrag::canDecode(e) || KURLDrag::canDecode(e);
+}
+
+bool Playlist::canDecode(QMimeSource *s)
+{
+ KURL::List urls;
+
+ if(CoverDrag::canDecode(s))
+ return true;
+
+ return KURLDrag::decode(s, urls) && !urls.isEmpty();
+}
+
+void Playlist::decode(QMimeSource *s, PlaylistItem *item)
+{
+ KURL::List urls;
+
+ if(!KURLDrag::decode(s, urls) || urls.isEmpty())
+ return;
+
+ // handle dropped images
+
+ if(!MediaFiles::isMediaFile(urls.front().path())) {
+
+ QString file;
+
+ if(urls.front().isLocalFile())
+ file = urls.front().path();
+ else
+ KIO::NetAccess::download(urls.front(), file, 0);
+
+ KMimeType::Ptr mimeType = KMimeType::findByPath(file);
+
+ if(item && mimeType->name().startsWith("image/")) {
+ item->file().coverInfo()->setCover(QImage(file));
+ refreshAlbum(item->file().tag()->artist(),
+ item->file().tag()->album());
+ }
+
+ KIO::NetAccess::removeTempFile(file);
+ }
+
+ QStringList fileList;
+
+ for(KURL::List::Iterator it = urls.begin(); it != urls.end(); ++it)
+ fileList += MediaFiles::convertURLsToLocal((*it).path(), this);
+
+ addFiles(fileList, item);
+}
+
+bool Playlist::eventFilter(QObject *watched, QEvent *e)
+{
+ if(watched == header()) {
+ switch(e->type()) {
+ case QEvent::MouseMove:
+ {
+ if((static_cast<QMouseEvent *>(e)->state() & LeftButton) == LeftButton &&
+ !action<KToggleAction>("resizeColumnsManually")->isChecked())
+ {
+ m_columnWidthModeChanged = true;
+
+ action<KToggleAction>("resizeColumnsManually")->setChecked(true);
+ slotColumnResizeModeChanged();
+ }
+
+ break;
+ }
+ case QEvent::MouseButtonPress:
+ {
+ if(static_cast<QMouseEvent *>(e)->button() == RightButton)
+ m_headerMenu->popup(QCursor::pos());
+
+ break;
+ }
+ case QEvent::MouseButtonRelease:
+ {
+ if(m_columnWidthModeChanged) {
+ m_columnWidthModeChanged = false;
+ notifyUserColumnWidthModeChanged();
+ }
+
+ if(!manualResize() && m_widthsDirty)
+ QTimer::singleShot(0, this, SLOT(slotUpdateColumnWidths()));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return KListView::eventFilter(watched, e);
+}
+
+void Playlist::keyPressEvent(QKeyEvent *event)
+{
+ if(event->key() == Key_Up) {
+ QListViewItemIterator selected(this, QListViewItemIterator::IteratorFlag(
+ QListViewItemIterator::Selected |
+ QListViewItemIterator::Visible));
+ if(selected.current()) {
+ QListViewItemIterator visible(this, QListViewItemIterator::IteratorFlag(
+ QListViewItemIterator::Visible));
+ if(selected.current() == visible.current())
+ KApplication::postEvent(parent(), new FocusUpEvent);
+ }
+
+ }
+
+ KListView::keyPressEvent(event);
+}
+
+void Playlist::contentsDropEvent(QDropEvent *e)
+{
+ QPoint vp = contentsToViewport(e->pos());
+ PlaylistItem *item = static_cast<PlaylistItem *>(itemAt(vp));
+
+ // First see if we're dropping a cover, if so we can get it out of the
+ // way early.
+ if(item && CoverDrag::canDecode(e)) {
+ coverKey id;
+ CoverDrag::decode(e, id);
+
+ // If the item we dropped on is selected, apply cover to all selected
+ // items, otherwise just apply to the dropped item.
+
+ if(item->isSelected()) {
+ PlaylistItemList selItems = selectedItems();
+ for(PlaylistItemList::Iterator it = selItems.begin(); it != selItems.end(); ++it) {
+ (*it)->file().coverInfo()->setCoverId(id);
+ (*it)->refresh();
+ }
+ }
+ else {
+ item->file().coverInfo()->setCoverId(id);
+ item->refresh();
+ }
+
+ return;
+ }
+
+ // When dropping on the upper half of an item, insert before this item.
+ // This is what the user expects, and also allows the insertion at
+ // top of the list
+
+ if(!item)
+ item = static_cast<PlaylistItem *>(lastItem());
+ else if(vp.y() < item->itemPos() + item->height() / 2)
+ item = static_cast<PlaylistItem *>(item->itemAbove());
+
+ m_blockDataChanged = true;
+
+ if(e->source() == this) {
+
+ // Since we're trying to arrange things manually, turn off sorting.
+
+ setSorting(columns() + 1);
+
+ QPtrList<QListViewItem> items = KListView::selectedItems();
+
+ for(QPtrListIterator<QListViewItem> it(items); it.current(); ++it) {
+ if(!item) {
+
+ // Insert the item at the top of the list. This is a bit ugly,
+ // but I don't see another way.
+
+ takeItem(it.current());
+ insertItem(it.current());
+ }
+ else
+ it.current()->moveItem(item);
+
+ item = static_cast<PlaylistItem *>(it.current());
+ }
+ }
+ else
+ decode(e, item);
+
+ m_blockDataChanged = false;
+
+ dataChanged();
+ emit signalPlaylistItemsDropped(this);
+ KListView::contentsDropEvent(e);
+}
+
+void Playlist::contentsMouseDoubleClickEvent(QMouseEvent *e)
+{
+ // Filter out non left button double clicks, that way users don't have the
+ // weird experience of switching songs from a double right-click.
+
+ if(e->button() == LeftButton)
+ KListView::contentsMouseDoubleClickEvent(e);
+}
+
+void Playlist::showEvent(QShowEvent *e)
+{
+ if(m_applySharedSettings) {
+ SharedSettings::instance()->apply(this);
+ m_applySharedSettings = false;
+ }
+ KListView::showEvent(e);
+}
+
+void Playlist::applySharedSettings()
+{
+ m_applySharedSettings = true;
+}
+
+void Playlist::read(QDataStream &s)
+{
+ QString buffer;
+
+ s >> m_playlistName
+ >> m_fileName;
+
+ QStringList files;
+ s >> files;
+
+ QListViewItem *after = 0;
+
+ m_blockDataChanged = true;
+
+ for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it)
+ after = createItem(FileHandle(*it), after, false);
+
+ m_blockDataChanged = false;
+
+ dataChanged();
+ m_collection->setupPlaylist(this, "midi");
+}
+
+void Playlist::viewportPaintEvent(QPaintEvent *pe)
+{
+ // If there are columns that need to be updated, well, update them.
+
+ if(!m_weightDirty.isEmpty() && !manualResize())
+ {
+ calculateColumnWeights();
+ slotUpdateColumnWidths();
+ }
+
+ KListView::viewportPaintEvent(pe);
+}
+
+void Playlist::viewportResizeEvent(QResizeEvent *re)
+{
+ // If the width of the view has changed, manually update the column
+ // widths.
+
+ if(re->size().width() != re->oldSize().width() && !manualResize())
+ slotUpdateColumnWidths();
+
+ KListView::viewportResizeEvent(re);
+}
+
+void Playlist::insertItem(QListViewItem *item)
+{
+ // Because we're called from the PlaylistItem ctor, item may not be a
+ // PlaylistItem yet (it would be QListViewItem when being inserted. But,
+ // it will be a PlaylistItem by the time it matters, but be careful if
+ // you need to use the PlaylistItem from here.
+
+ m_addTime.append(static_cast<PlaylistItem *>(item));
+ KListView::insertItem(item);
+}
+
+void Playlist::takeItem(QListViewItem *item)
+{
+ // See the warning in Playlist::insertItem.
+
+ m_subtractTime.append(static_cast<PlaylistItem *>(item));
+ KListView::takeItem(item);
+}
+
+void Playlist::addColumn(const QString &label)
+{
+ slotWeightDirty(columns());
+ KListView::addColumn(label, 30);
+}
+
+PlaylistItem *Playlist::createItem(const FileHandle &file,
+ QListViewItem *after, bool emitChanged)
+{
+ return createItem<PlaylistItem, CollectionListItem, CollectionList>(file, after, emitChanged);
+}
+
+void Playlist::createItems(const PlaylistItemList &siblings, PlaylistItem *after)
+{
+ createItems<CollectionListItem, PlaylistItem, PlaylistItem>(siblings, after);
+}
+
+void Playlist::addFiles(const QStringList &files, PlaylistItem *after)
+{
+ if(!after)
+ after = static_cast<PlaylistItem *>(lastItem());
+
+ KApplication::setOverrideCursor(Qt::waitCursor);
+
+ m_blockDataChanged = true;
+
+ FileHandleList queue;
+
+ const QStringList::ConstIterator filesEnd = files.end();
+ for(QStringList::ConstIterator it = files.begin(); it != filesEnd; ++it)
+ addFile(*it, queue, true, &after);
+
+ addFileHelper(queue, &after, true);
+
+ m_blockDataChanged = false;
+
+ slotWeightDirty();
+ dataChanged();
+
+ KApplication::restoreOverrideCursor();
+}
+
+void Playlist::refreshAlbums(const PlaylistItemList &items, coverKey id)
+{
+ QValueList< QPair<QString, QString> > albums;
+ bool setAlbumCovers = items.count() == 1;
+
+ for(PlaylistItemList::ConstIterator it = items.begin(); it != items.end(); ++it) {
+ QString artist = (*it)->file().tag()->artist();
+ QString album = (*it)->file().tag()->album();
+
+ if(albums.find(qMakePair(artist, album)) == albums.end())
+ albums.append(qMakePair(artist, album));
+
+ (*it)->file().coverInfo()->setCoverId(id);
+ if(setAlbumCovers)
+ (*it)->file().coverInfo()->applyCoverToWholeAlbum(true);
+ }
+
+ for(QValueList< QPair<QString, QString> >::ConstIterator it = albums.begin();
+ it != albums.end(); ++it)
+ {
+ refreshAlbum((*it).first, (*it).second);
+ }
+}
+
+void Playlist::updatePlaying() const
+{
+ for(PlaylistItemList::ConstIterator it = PlaylistItem::playingItems().begin();
+ it != PlaylistItem::playingItems().end(); ++it)
+ {
+ (*it)->listView()->triggerUpdate();
+ }
+}
+
+void Playlist::refreshAlbum(const QString &artist, const QString &album)
+{
+ ColumnList columns;
+ columns.append(PlaylistItem::ArtistColumn);
+ PlaylistSearch::Component artistComponent(artist, false, columns,
+ PlaylistSearch::Component::Exact);
+
+ columns.clear();
+ columns.append(PlaylistItem::AlbumColumn);
+ PlaylistSearch::Component albumComponent(album, false, columns,
+ PlaylistSearch::Component::Exact);
+
+ PlaylistSearch::ComponentList components;
+ components.append(artist);
+ components.append(album);
+
+ PlaylistList playlists;
+ playlists.append(CollectionList::instance());
+
+ PlaylistSearch search(playlists, components);
+ PlaylistItemList matches = search.matchedItems();
+
+ for(PlaylistItemList::Iterator it = matches.begin(); it != matches.end(); ++it)
+ (*it)->refresh();
+}
+
+void Playlist::hideColumn(int c, bool updateSearch)
+{
+ m_headerMenu->setItemChecked(c, false);
+
+ if(!isColumnVisible(c))
+ return;
+
+ setColumnWidthMode(c, Manual);
+ setColumnWidth(c, 0);
+
+ // Moving the column to the end seems to prevent it from randomly
+ // popping up.
+
+ header()->moveSection(c, header()->count());
+ header()->setResizeEnabled(false, c);
+
+ if(c == m_leftColumn) {
+ updatePlaying();
+ m_leftColumn = leftMostVisibleColumn();
+ }
+
+ if(!manualResize()) {
+ slotUpdateColumnWidths();
+ triggerUpdate();
+ }
+
+ if(this != CollectionList::instance())
+ CollectionList::instance()->hideColumn(c, false);
+
+ if(updateSearch)
+ redisplaySearch();
+}
+
+void Playlist::showColumn(int c, bool updateSearch)
+{
+ m_headerMenu->setItemChecked(c, true);
+
+ if(isColumnVisible(c))
+ return;
+
+ // Just set the width to one to mark the column as visible -- we'll update
+ // the real size in the next call.
+
+ if(manualResize())
+ setColumnWidth(c, 35); // Make column at least slightly visible.
+ else
+ setColumnWidth(c, 1);
+
+ header()->setResizeEnabled(true, c);
+ header()->moveSection(c, c); // Approximate old position
+
+ if(c == leftMostVisibleColumn()) {
+ updatePlaying();
+ m_leftColumn = leftMostVisibleColumn();
+ }
+
+ if(!manualResize()) {
+ slotUpdateColumnWidths();
+ triggerUpdate();
+ }
+
+ if(this != CollectionList::instance())
+ CollectionList::instance()->showColumn(c, false);
+
+ if(updateSearch)
+ redisplaySearch();
+}
+
+bool Playlist::isColumnVisible(int c) const
+{
+ return columnWidth(c) != 0;
+}
+
+void Playlist::polish()
+{
+ KListView::polish();
+
+ if(m_polished)
+ return;
+
+ m_polished = true;
+
+ addColumn(i18n("Track Name"));
+ addColumn(i18n("Artist"));
+ addColumn(i18n("Album"));
+ addColumn(i18n("Cover"));
+ addColumn(i18n("Track"));
+ addColumn(i18n("Genre"));
+ addColumn(i18n("Year"));
+ addColumn(i18n("Length"));
+ addColumn(i18n("Bitrate"));
+ addColumn(i18n("Comment"));
+ addColumn(i18n("File Name"));
+ addColumn(i18n("File Name (full path)"));
+
+ setRenameable(PlaylistItem::TrackColumn, true);
+ setRenameable(PlaylistItem::ArtistColumn, true);
+ setRenameable(PlaylistItem::AlbumColumn, true);
+ setRenameable(PlaylistItem::TrackNumberColumn, true);
+ setRenameable(PlaylistItem::GenreColumn, true);
+ setRenameable(PlaylistItem::YearColumn, true);
+
+ setAllColumnsShowFocus(true);
+ setSelectionMode(QListView::Extended);
+ setShowSortIndicator(true);
+ setDropVisualizer(true);
+
+ m_columnFixedWidths.resize(columns(), 0);
+
+ //////////////////////////////////////////////////
+ // setup header RMB menu
+ //////////////////////////////////////////////////
+
+ m_columnVisibleAction = new KActionMenu(i18n("&Show Columns"), this, "showColumns");
+
+ m_headerMenu = m_columnVisibleAction->popupMenu();
+ m_headerMenu->insertTitle(i18n("Show"));
+ m_headerMenu->setCheckable(true);
+
+ for(int i = 0; i < header()->count(); ++i) {
+ if(i == PlaylistItem::FileNameColumn)
+ m_headerMenu->insertSeparator();
+ m_headerMenu->insertItem(header()->label(i), i);
+ m_headerMenu->setItemChecked(i, true);
+ adjustColumn(i);
+ }
+
+ connect(m_headerMenu, SIGNAL(activated(int)), this, SLOT(slotToggleColumnVisible(int)));
+
+ connect(this, SIGNAL(contextMenuRequested(QListViewItem *, const QPoint &, int)),
+ this, SLOT(slotShowRMBMenu(QListViewItem *, const QPoint &, int)));
+ connect(this, SIGNAL(itemRenamed(QListViewItem *, const QString &, int)),
+ this, SLOT(slotInlineEditDone(QListViewItem *, const QString &, int)));
+ connect(this, SIGNAL(doubleClicked(QListViewItem *)),
+ this, SLOT(slotPlayCurrent()));
+ connect(this, SIGNAL(returnPressed(QListViewItem *)),
+ this, SLOT(slotPlayCurrent()));
+
+ connect(header(), SIGNAL(sizeChange(int, int, int)),
+ this, SLOT(slotColumnSizeChanged(int, int, int)));
+
+ connect(renameLineEdit(), SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
+ this, SLOT(slotInlineCompletionModeChanged(KGlobalSettings::Completion)));
+
+ connect(action("resizeColumnsManually"), SIGNAL(activated()),
+ this, SLOT(slotColumnResizeModeChanged()));
+
+ if(action<KToggleAction>("resizeColumnsManually")->isChecked())
+ setHScrollBarMode(Auto);
+ else
+ setHScrollBarMode(AlwaysOff);
+
+ setAcceptDrops(true);
+ setDropVisualizer(true);
+
+ m_disableColumnWidthUpdates = false;
+
+ setShowToolTips(false);
+ m_toolTip = new PlaylistToolTip(viewport(), this);
+}
+
+void Playlist::setupItem(PlaylistItem *item)
+{
+ if(!m_search.isEmpty())
+ item->setVisible(m_search.checkItem(item));
+
+ if(childCount() <= 2 && !manualResize()) {
+ slotWeightDirty();
+ slotUpdateColumnWidths();
+ triggerUpdate();
+ }
+}
+
+void Playlist::setDynamicListsFrozen(bool frozen)
+{
+ m_collection->setDynamicListsFrozen(frozen);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected slots
+////////////////////////////////////////////////////////////////////////////////
+
+void Playlist::slotPopulateBackMenu() const
+{
+ if(!playingItem())
+ return;
+
+ KPopupMenu *menu = action<KToolBarPopupAction>("back")->popupMenu();
+ menu->clear();
+ m_backMenuItems.clear();
+
+ int count = 0;
+ PlaylistItemList::ConstIterator it = m_history.end();
+
+ while(it != m_history.begin() && count < 10) {
+ ++count;
+ --it;
+ int index = menu->insertItem((*it)->file().tag()->title());
+ m_backMenuItems[index] = *it;
+ }
+}
+
+void Playlist::slotPlayFromBackMenu(int number) const
+{
+ if(!m_backMenuItems.contains(number))
+ return;
+
+ TrackSequenceManager::instance()->setNextItem(m_backMenuItems[number]);
+ action("forward")->activate();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void Playlist::setup()
+{
+ setItemMargin(3);
+
+ connect(header(), SIGNAL(indexChange(int, int, int)), this, SLOT(slotColumnOrderChanged(int, int, int)));
+
+ connect(m_fetcher, SIGNAL(signalCoverChanged(int)), this, SLOT(slotCoverChanged(int)));
+
+ // Prevent list of selected items from changing while internet search is in
+ // progress.
+ connect(this, SIGNAL(selectionChanged()), m_fetcher, SLOT(abortSearch()));
+
+ setSorting(1);
+}
+
+void Playlist::loadFile(const QString &fileName, const QFileInfo &fileInfo)
+{
+ QFile file(fileName);
+ if(!file.open(IO_ReadOnly))
+ return;
+
+ QTextStream stream(&file);
+
+ // Turn off non-explicit sorting.
+
+ setSorting(PlaylistItem::lastColumn() + columnOffset() + 1);
+
+ PlaylistItem *after = 0;
+
+ m_disableColumnWidthUpdates = true;
+
+ m_blockDataChanged = true;
+
+ while(!stream.atEnd()) {
+ QString itemName = stream.readLine().stripWhiteSpace();
+
+ QFileInfo item(itemName);
+
+ if(item.isRelative())
+ item.setFile(QDir::cleanDirPath(fileInfo.dirPath(true) + "/" + itemName));
+
+ if(item.exists() && item.isFile() && item.isReadable() &&
+ MediaFiles::isMediaFile(item.fileName()))
+ {
+ if(after)
+ after = createItem(FileHandle(item, item.absFilePath()), after, false);
+ else
+ after = createItem(FileHandle(item, item.absFilePath()), 0, false);
+ }
+ }
+
+ m_blockDataChanged = false;
+
+ file.close();
+
+ dataChanged();
+
+ m_disableColumnWidthUpdates = false;
+}
+
+void Playlist::setPlaying(PlaylistItem *item, bool addToHistory)
+{
+ if(playingItem() == item)
+ return;
+
+ if(playingItem()) {
+ if(addToHistory) {
+ if(playingItem()->playlist() ==
+ playingItem()->playlist()->m_collection->upcomingPlaylist())
+ m_history.append(playingItem()->collectionItem());
+ else
+ m_history.append(playingItem());
+ }
+ playingItem()->setPlaying(false);
+ }
+
+ TrackSequenceManager::instance()->setCurrent(item);
+ QByteArray data;
+ kapp->dcopClient()->emitDCOPSignal("Player", "trackChanged()", data);
+
+ if(!item)
+ return;
+
+ item->setPlaying(true);
+
+ bool enableBack = !m_history.isEmpty();
+ action<KToolBarPopupAction>("back")->popupMenu()->setEnabled(enableBack);
+}
+
+bool Playlist::playing() const
+{
+ return playingItem() && this == playingItem()->playlist();
+}
+
+int Playlist::leftMostVisibleColumn() const
+{
+ int i = 0;
+ while(!isColumnVisible(header()->mapToSection(i)) && i < PlaylistItem::lastColumn())
+ i++;
+
+ return header()->mapToSection(i);
+}
+
+PlaylistItemList Playlist::items(QListViewItemIterator::IteratorFlag flags)
+{
+ PlaylistItemList list;
+
+ for(QListViewItemIterator it(this, flags); it.current(); ++it)
+ list.append(static_cast<PlaylistItem *>(it.current()));
+
+ return list;
+}
+
+void Playlist::calculateColumnWeights()
+{
+ if(m_disableColumnWidthUpdates)
+ return;
+
+ PlaylistItemList l = items();
+ QValueListConstIterator<int> columnIt;
+
+ QValueVector<double> averageWidth(columns(), 0);
+ double itemCount = l.size();
+
+ QValueVector<int> cachedWidth;
+
+ // Here we're not using a real average, but averaging the squares of the
+ // column widths and then using the square root of that value. This gives
+ // a nice weighting to the longer columns without doing something arbitrary
+ // like adding a fixed amount of padding.
+
+ for(PlaylistItemList::ConstIterator it = l.begin(); it != l.end(); ++it) {
+ cachedWidth = (*it)->cachedWidths();
+ for(columnIt = m_weightDirty.begin(); columnIt != m_weightDirty.end(); ++columnIt)
+ averageWidth[*columnIt] += pow(double(cachedWidth[*columnIt]), 2.0) / itemCount;
+ }
+
+ m_columnWeights.resize(columns(), -1);
+
+ for(columnIt = m_weightDirty.begin(); columnIt != m_weightDirty.end(); ++columnIt) {
+ m_columnWeights[*columnIt] = int(sqrt(averageWidth[*columnIt]) + 0.5);
+
+ // kdDebug(65432) << k_funcinfo << "m_columnWeights[" << *columnIt << "] == "
+ // << m_columnWeights[*columnIt] << endl;
+ }
+
+ m_weightDirty.clear();
+}
+
+void Playlist::addFile(const QString &file, FileHandleList &files, bool importPlaylists,
+ PlaylistItem **after)
+{
+ if(hasItem(file) && !m_allowDuplicates)
+ return;
+
+ processEvents();
+ addFileHelper(files, after);
+
+ // Our biggest thing that we're fighting during startup is too many stats
+ // of files. Make sure that we don't do one here if it's not needed.
+
+ FileHandle cached = Cache::instance()->value(file);
+
+ if(!cached.isNull()) {
+ cached.tag();
+ files.append(cached);
+ return;
+ }
+
+
+ const QFileInfo fileInfo = QDir::cleanDirPath(file);
+ if(!fileInfo.exists())
+ return;
+
+ if(fileInfo.isFile() && fileInfo.isReadable()) {
+ if(MediaFiles::isMediaFile(file)) {
+ FileHandle f(fileInfo, fileInfo.absFilePath());
+ f.tag();
+ files.append(f);
+ }
+ }
+
+ if(importPlaylists && MediaFiles::isPlaylistFile(file) &&
+ !m_collection->containsPlaylistFile(fileInfo.absFilePath()))
+ {
+ new Playlist(m_collection, fileInfo);
+ return;
+ }
+
+ if(fileInfo.isDir()) {
+
+ // Resorting to the POSIX API because QDir::listEntries() stats every
+ // file and blocks while it's doing so.
+
+ DIR *dir = ::opendir(QFile::encodeName(fileInfo.filePath()));
+
+ if(dir) {
+ struct dirent *dirEntry;
+
+ for(dirEntry = ::readdir(dir); dirEntry; dirEntry = ::readdir(dir)) {
+ if(strcmp(dirEntry->d_name, ".") != 0 && strcmp(dirEntry->d_name, "..") != 0) {
+
+ // We set importPlaylists to the value from the add directories
+ // dialog as we want to load all of the ones that the user has
+ // explicitly asked for, but not those that we find in lower
+ // directories.
+
+ addFile(fileInfo.filePath() + QDir::separator() + QFile::decodeName(dirEntry->d_name),
+ files, m_collection->importPlaylists(), after);
+ }
+ }
+ ::closedir(dir);
+ }
+ else {
+ kdWarning(65432) << "Unable to open directory "
+ << fileInfo.filePath()
+ << ", make sure it is readable.\n";
+ }
+ }
+}
+
+void Playlist::addFileHelper(FileHandleList &files, PlaylistItem **after, bool ignoreTimer)
+{
+ static QTime time = QTime::currentTime();
+
+ // Process new items every 10 seconds, when we've loaded 1000 items, or when
+ // it's been requested in the API.
+
+ if(ignoreTimer || time.elapsed() > 10000 ||
+ (files.count() >= 1000 && time.elapsed() > 1000))
+ {
+ time.restart();
+
+ const bool focus = hasFocus();
+ const bool visible = isVisible() && files.count() > 20;
+
+ if(visible)
+ m_collection->raiseDistraction();
+ const FileHandleList::ConstIterator filesEnd = files.end();
+ for(FileHandleList::ConstIterator it = files.begin(); it != filesEnd; ++it)
+ *after = createItem(*it, *after, false);
+ files.clear();
+
+ if(visible)
+ m_collection->lowerDistraction();
+
+ if(focus)
+ setFocus();
+
+ processEvents();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private slots
+////////////////////////////////////////////////////////////////////////////////
+
+void Playlist::slotUpdateColumnWidths()
+{
+ if(m_disableColumnWidthUpdates || manualResize())
+ return;
+
+ // Make sure that the column weights have been initialized before trying to
+ // update the columns.
+
+ QValueList<int> visibleColumns;
+ for(int i = 0; i < columns(); i++) {
+ if(isColumnVisible(i))
+ visibleColumns.append(i);
+ }
+
+ QValueListConstIterator<int> it;
+
+ if(count() == 0) {
+ for(it = visibleColumns.begin(); it != visibleColumns.end(); ++it)
+ setColumnWidth(*it, header()->fontMetrics().width(header()->label(*it)) + 10);
+
+ return;
+ }
+
+ if(m_columnWeights.isEmpty())
+ return;
+
+ // First build a list of minimum widths based on the strings in the listview
+ // header. We won't let the width of the column go below this width.
+
+ QValueVector<int> minimumWidth(columns(), 0);
+ int minimumWidthTotal = 0;
+
+ // Also build a list of either the minimum *or* the fixed width -- whichever is
+ // greater.
+
+ QValueVector<int> minimumFixedWidth(columns(), 0);
+ int minimumFixedWidthTotal = 0;
+
+ for(it = visibleColumns.begin(); it != visibleColumns.end(); ++it) {
+ int column = *it;
+ minimumWidth[column] = header()->fontMetrics().width(header()->label(column)) + 10;
+ minimumWidthTotal += minimumWidth[column];
+
+ minimumFixedWidth[column] = QMAX(minimumWidth[column], m_columnFixedWidths[column]);
+ minimumFixedWidthTotal += minimumFixedWidth[column];
+ }
+
+ // Make sure that the width won't get any smaller than this. We have to
+ // account for the scrollbar as well. Since this method is called from the
+ // resize event this will set a pretty hard lower bound on the size.
+
+ setMinimumWidth(minimumWidthTotal + verticalScrollBar()->width());
+
+ // If we've got enough room for the fixed widths (larger than the minimum
+ // widths) then instead use those for our "minimum widths".
+
+ if(minimumFixedWidthTotal < visibleWidth()) {
+ minimumWidth = minimumFixedWidth;
+ minimumWidthTotal = minimumFixedWidthTotal;
+ }
+
+ // We've got a list of columns "weights" based on some statistics gathered
+ // about the widths of the items in that column. We need to find the total
+ // useful weight to use as a divisor for each column's weight.
+
+ double totalWeight = 0;
+ for(it = visibleColumns.begin(); it != visibleColumns.end(); ++it)
+ totalWeight += m_columnWeights[*it];
+
+ // Computed a "weighted width" for each visible column. This would be the
+ // width if we didn't have to handle the cases of minimum and maximum widths.
+
+ QValueVector<int> weightedWidth(columns(), 0);
+ for(it = visibleColumns.begin(); it != visibleColumns.end(); ++it)
+ weightedWidth[*it] = int(double(m_columnWeights[*it]) / totalWeight * visibleWidth() + 0.5);
+
+ // The "extra" width for each column. This is the weighted width less the
+ // minimum width or zero if the minimum width is greater than the weighted
+ // width.
+
+ QValueVector<int> extraWidth(columns(), 0);
+
+ // This is used as an indicator if we have any columns where the weighted
+ // width is less than the minimum width. If this is false then we can
+ // just use the weighted width with no problems, otherwise we have to
+ // "readjust" the widths.
+
+ bool readjust = false;
+
+ // If we have columns where the weighted width is less than the minimum width
+ // we need to steal that space from somewhere. The amount that we need to
+ // steal is the "neededWidth".
+
+ int neededWidth = 0;
+
+ // While we're on the topic of stealing -- we have to have somewhere to steal
+ // from. availableWidth is the sum of the amount of space beyond the minimum
+ // width that each column has been allocated -- the sum of the values of
+ // extraWidth[].
+
+ int availableWidth = 0;
+
+ // Fill in the values discussed above.
+
+ for(it = visibleColumns.begin(); it != visibleColumns.end(); ++it) {
+ if(weightedWidth[*it] < minimumWidth[*it]) {
+ readjust = true;
+ extraWidth[*it] = 0;
+ neededWidth += minimumWidth[*it] - weightedWidth[*it];
+ }
+ else {
+ extraWidth[*it] = weightedWidth[*it] - minimumWidth[*it];
+ availableWidth += extraWidth[*it];
+ }
+ }
+
+ // The adjustmentRatio is the amount of the "extraWidth[]" that columns will
+ // actually be given.
+
+ double adjustmentRatio = (double(availableWidth) - double(neededWidth)) / double(availableWidth);
+
+ // This will be the sum of the total space that we actually use. Because of
+ // rounding error this won't be the exact available width.
+
+ int usedWidth = 0;
+
+ // Now set the actual column widths. If the weighted widths are all greater
+ // than the minimum widths, just use those, otherwise use the "reajusted
+ // weighted width".
+
+ for(it = visibleColumns.begin(); it != visibleColumns.end(); ++it) {
+ int width;
+ if(readjust) {
+ int adjustedExtraWidth = int(double(extraWidth[*it]) * adjustmentRatio + 0.5);
+ width = minimumWidth[*it] + adjustedExtraWidth;
+ }
+ else
+ width = weightedWidth[*it];
+
+ setColumnWidth(*it, width);
+ usedWidth += width;
+ }
+
+ // Fill the remaining gap for a clean fit into the available space.
+
+ int remainingWidth = visibleWidth() - usedWidth;
+ setColumnWidth(visibleColumns.back(), columnWidth(visibleColumns.back()) + remainingWidth);
+
+ m_widthsDirty = false;
+}
+
+void Playlist::slotAddToUpcoming()
+{
+ m_collection->setUpcomingPlaylistEnabled(true);
+ m_collection->upcomingPlaylist()->appendItems(selectedItems());
+}
+
+void Playlist::slotShowRMBMenu(QListViewItem *item, const QPoint &point, int column)
+{
+ if(!item)
+ return;
+
+ // Create the RMB menu on demand.
+
+ if(!m_rmbMenu) {
+
+ // A bit of a hack to get a pointer to the action collection.
+ // Probably more of these actions should be ported over to using KActions.
+
+ m_rmbMenu = new KPopupMenu(this);
+
+ m_rmbUpcomingID = m_rmbMenu->insertItem(SmallIcon("today"),
+ i18n("Add to Play Queue"), this, SLOT(slotAddToUpcoming()));
+ m_rmbMenu->insertSeparator();
+
+ if(!readOnly()) {
+ action("edit_cut")->plug(m_rmbMenu);
+ action("edit_copy")->plug(m_rmbMenu);
+ action("edit_paste")->plug(m_rmbMenu);
+ m_rmbMenu->insertSeparator();
+ action("removeFromPlaylist")->plug(m_rmbMenu);
+ }
+ else
+ action("edit_copy")->plug(m_rmbMenu);
+
+ m_rmbEditID = m_rmbMenu->insertItem(
+ i18n("Edit"), this, SLOT(slotRenameTag()));
+
+ action("refresh")->plug(m_rmbMenu);
+ action("removeItem")->plug(m_rmbMenu);
+
+ m_rmbMenu->insertSeparator();
+
+ action("guessTag")->plug(m_rmbMenu);
+ action("renameFile")->plug(m_rmbMenu);
+
+ action("coverManager")->plug(m_rmbMenu);
+
+ m_rmbMenu->insertSeparator();
+
+ m_rmbMenu->insertItem(
+ SmallIcon("folder_new"), i18n("Create Playlist From Selected Items..."), this, SLOT(slotCreateGroup()));
+
+ K3bExporter *exporter = new K3bExporter(this);
+ KAction *k3bAction = exporter->action();
+ if(k3bAction)
+ k3bAction->plug(m_rmbMenu);
+ }
+
+ // Ignore any columns added by subclasses.
+
+ column -= columnOffset();
+
+ bool showEdit =
+ (column == PlaylistItem::TrackColumn) ||
+ (column == PlaylistItem::ArtistColumn) ||
+ (column == PlaylistItem::AlbumColumn) ||
+ (column == PlaylistItem::TrackNumberColumn) ||
+ (column == PlaylistItem::GenreColumn) ||
+ (column == PlaylistItem::YearColumn);
+
+ if(showEdit)
+ m_rmbMenu->changeItem(m_rmbEditID,
+ i18n("Edit '%1'").arg(columnText(column + columnOffset())));
+
+ m_rmbMenu->setItemVisible(m_rmbEditID, showEdit);
+
+ // Disable edit menu if only one file is selected, and it's read-only
+
+ FileHandle file = static_cast<PlaylistItem*>(item)->file();
+
+ m_rmbMenu->setItemEnabled(m_rmbEditID, file.fileInfo().isWritable() ||
+ selectedItems().count() > 1);
+
+ action("viewCover")->setEnabled(file.coverInfo()->hasCover());
+ action("removeCover")->setEnabled(file.coverInfo()->hasCover());
+
+ m_rmbMenu->popup(point);
+ m_currentColumn = column + columnOffset();
+}
+
+void Playlist::slotRenameTag()
+{
+ // kdDebug(65432) << "Playlist::slotRenameTag()" << endl;
+
+ // setup completions and validators
+
+ CollectionList *list = CollectionList::instance();
+
+ KLineEdit *edit = renameLineEdit();
+
+ switch(m_currentColumn - columnOffset())
+ {
+ case PlaylistItem::ArtistColumn:
+ edit->completionObject()->setItems(list->uniqueSet(CollectionList::Artists));
+ break;
+ case PlaylistItem::AlbumColumn:
+ edit->completionObject()->setItems(list->uniqueSet(CollectionList::Albums));
+ break;
+ case PlaylistItem::GenreColumn:
+ {
+ QStringList genreList;
+ TagLib::StringList genres = TagLib::ID3v1::genreList();
+ for(TagLib::StringList::ConstIterator it = genres.begin(); it != genres.end(); ++it)
+ genreList.append(TStringToQString((*it)));
+ edit->completionObject()->setItems(genreList);
+ break;
+ }
+ default:
+ edit->completionObject()->clear();
+ break;
+ }
+
+ m_editText = currentItem()->text(m_currentColumn);
+
+ rename(currentItem(), m_currentColumn);
+}
+
+bool Playlist::editTag(PlaylistItem *item, const QString &text, int column)
+{
+ Tag *newTag = TagTransactionManager::duplicateTag(item->file().tag());
+
+ switch(column - columnOffset())
+ {
+ case PlaylistItem::TrackColumn:
+ newTag->setTitle(text);
+ break;
+ case PlaylistItem::ArtistColumn:
+ newTag->setArtist(text);
+ break;
+ case PlaylistItem::AlbumColumn:
+ newTag->setAlbum(text);
+ break;
+ case PlaylistItem::TrackNumberColumn:
+ {
+ bool ok;
+ int value = text.toInt(&ok);
+ if(ok)
+ newTag->setTrack(value);
+ break;
+ }
+ case PlaylistItem::GenreColumn:
+ newTag->setGenre(text);
+ break;
+ case PlaylistItem::YearColumn:
+ {
+ bool ok;
+ int value = text.toInt(&ok);
+ if(ok)
+ newTag->setYear(value);
+ break;
+ }
+ }
+
+ TagTransactionManager::instance()->changeTagOnItem(item, newTag);
+ return true;
+}
+
+void Playlist::slotInlineEditDone(QListViewItem *, const QString &, int column)
+{
+ QString text = renameLineEdit()->text();
+ bool changed = false;
+
+ PlaylistItemList l = selectedItems();
+
+ // See if any of the files have a tag different from the input.
+
+ for(PlaylistItemList::ConstIterator it = l.begin(); it != l.end() && !changed; ++it)
+ if((*it)->text(column - columnOffset()) != text)
+ changed = true;
+
+ if(!changed ||
+ (l.count() > 1 && KMessageBox::warningContinueCancel(
+ 0,
+ i18n("This will edit multiple files. Are you sure?"),
+ QString::null,
+ i18n("Edit"),
+ "DontWarnMultipleTags") == KMessageBox::Cancel))
+ {
+ return;
+ }
+
+ for(PlaylistItemList::ConstIterator it = l.begin(); it != l.end(); ++it)
+ editTag(*it, text, column);
+
+ TagTransactionManager::instance()->commit();
+
+ CollectionList::instance()->dataChanged();
+ dataChanged();
+ update();
+}
+
+void Playlist::slotColumnOrderChanged(int, int from, int to)
+{
+ if(from == 0 || to == 0) {
+ updatePlaying();
+ m_leftColumn = header()->mapToSection(0);
+ }
+
+ SharedSettings::instance()->setColumnOrder(this);
+}
+
+void Playlist::slotToggleColumnVisible(int column)
+{
+ if(!isColumnVisible(column)) {
+ int fileNameColumn = PlaylistItem::FileNameColumn + columnOffset();
+ int fullPathColumn = PlaylistItem::FullPathColumn + columnOffset();
+
+ if(column == fileNameColumn && isColumnVisible(fullPathColumn)) {
+ hideColumn(fullPathColumn, false);
+ SharedSettings::instance()->toggleColumnVisible(fullPathColumn);
+ }
+ if(column == fullPathColumn && isColumnVisible(fileNameColumn)) {
+ hideColumn(fileNameColumn, false);
+ SharedSettings::instance()->toggleColumnVisible(fileNameColumn);
+ }
+ }
+
+ if(isColumnVisible(column))
+ hideColumn(column);
+ else
+ showColumn(column);
+
+ SharedSettings::instance()->toggleColumnVisible(column - columnOffset());
+}
+
+void Playlist::slotCreateGroup()
+{
+ QString name = m_collection->playlistNameDialog(i18n("Create New Playlist"));
+
+ if(!name.isEmpty())
+ new Playlist(m_collection, selectedItems(), name);
+}
+
+void Playlist::notifyUserColumnWidthModeChanged()
+{
+ KMessageBox::information(this,
+ i18n("Manual column widths have been enabled. You can "
+ "switch back to automatic column sizes in the view "
+ "menu."),
+ i18n("Manual Column Widths Enabled"),
+ "ShowManualColumnWidthInformation");
+}
+
+void Playlist::slotColumnSizeChanged(int column, int, int newSize)
+{
+ m_widthsDirty = true;
+ m_columnFixedWidths[column] = newSize;
+}
+
+void Playlist::slotInlineCompletionModeChanged(KGlobalSettings::Completion mode)
+{
+ SharedSettings::instance()->setInlineCompletionMode(mode);
+}
+
+void Playlist::slotPlayCurrent()
+{
+ QListViewItemIterator it(this, QListViewItemIterator::Selected);
+ PlaylistItem *next = static_cast<PlaylistItem *>(it.current());
+ TrackSequenceManager::instance()->setNextItem(next);
+ action("forward")->activate();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// helper functions
+////////////////////////////////////////////////////////////////////////////////
+
+QDataStream &operator<<(QDataStream &s, const Playlist &p)
+{
+ s << p.name();
+ s << p.fileName();
+ s << p.files();
+
+ return s;
+}
+
+QDataStream &operator>>(QDataStream &s, Playlist &p)
+{
+ p.read(s);
+ return s;
+}
+
+bool processEvents()
+{
+ static QTime time = QTime::currentTime();
+
+ if(time.elapsed() > 100) {
+ time.restart();
+ kapp->processEvents();
+ return true;
+ }
+ return false;
+}
+
+#include "playlist.moc"
diff --git a/juk/playlist.h b/juk/playlist.h
new file mode 100644
index 00000000..26c11ff9
--- /dev/null
+++ b/juk/playlist.h
@@ -0,0 +1,789 @@
+/***************************************************************************
+ begin : Sat Feb 16 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef PLAYLIST_H
+#define PLAYLIST_H
+
+#include <klistview.h>
+#include <kurldrag.h>
+#include <kdebug.h>
+#include <kglobalsettings.h>
+
+#include <qvaluevector.h>
+#include <qfileinfo.h>
+
+#include "covermanager.h"
+#include "stringhash.h"
+#include "playlistsearch.h"
+#include "tagguesser.h"
+#include "playlistinterface.h"
+#include "playlistitem.h"
+
+class KPopupMenu;
+class KActionMenu;
+
+class QEvent;
+
+class PlaylistCollection;
+class WebImageFetcher;
+class PlaylistToolTip;
+class UpcomingPlaylist;
+
+typedef QValueList<PlaylistItem *> PlaylistItemList;
+
+class Playlist : public KListView, public PlaylistInterface
+{
+ Q_OBJECT
+
+public:
+
+ Playlist(PlaylistCollection *collection, const QString &name = QString::null,
+ const QString &iconName = "midi");
+ Playlist(PlaylistCollection *collection, const PlaylistItemList &items,
+ const QString &name = QString::null, const QString &iconName = "midi");
+ Playlist(PlaylistCollection *collection, const QFileInfo &playlistFile,
+ const QString &iconName = "midi");
+
+ /**
+ * This constructor should generally only be used either by the cache
+ * restoration methods or by subclasses that want to handle calls to
+ * PlaylistCollection::setupPlaylist() differently.
+ */
+ Playlist(PlaylistCollection *collection, bool delaySetup);
+
+ virtual ~Playlist();
+
+
+ // The following group of functions implement the PlaylistInterface API.
+
+ virtual QString name() const;
+ virtual FileHandle currentFile() const;
+ virtual int count() const { return childCount(); }
+ virtual int time() const;
+ virtual void playNext();
+ virtual void playPrevious();
+ virtual void stop();
+
+ /**
+ * Plays the top item of the playlist.
+ */
+ void playFirst();
+
+ /**
+ * Plays the next album in the playlist. Only useful when in album random
+ * play mode.
+ */
+ void playNextAlbum();
+
+ /**
+ * Saves the file to the currently set file name. If there is no filename
+ * currently set, the default behavior is to prompt the user for a file
+ * name.
+ */
+ virtual void save();
+
+ /**
+ * Standard "save as". Prompts the user for a location where to save the
+ * playlist to.
+ */
+ virtual void saveAs();
+
+ /**
+ * Removes \a item from the Playlist, but not from the disk. If
+ * \a emitChanged is true this will also notify relevant classes
+ * that the content of the list has changed.
+ *
+ * In some situations, for instance when removing items in a loop, it is
+ * preferable to delay this notification until after other operations have
+ * completed. In those cases set \a emitChanged to false and call the
+ * signal directly.
+ */
+ virtual void clearItem(PlaylistItem *item, bool emitChanged = true);
+
+ /**
+ * Remove \a items from the playlist and emit a signal indicating
+ * that the number of items in the list has changed.
+ */
+ virtual void clearItems(const PlaylistItemList &items);
+
+ /**
+ * Accessor function to return a pointer to the currently playing file.
+ *
+ * @return 0 if no file is playing, otherwise a pointer to the PlaylistItem
+ * of the track that is currently playing.
+ */
+ static PlaylistItem *playingItem();
+
+ /**
+ * All of the (media) files in the list.
+ */
+ QStringList files() const;
+
+ /**
+ * Returns a list of all of the items in the playlist.
+ */
+ virtual PlaylistItemList items();
+
+ /**
+ * Returns a list of all of the \e visible items in the playlist.
+ */
+ PlaylistItemList visibleItems();
+
+ /**
+ * Returns a list of the currently selected items.
+ */
+ PlaylistItemList selectedItems();
+
+ /**
+ * Returns properly casted first child item in list.
+ */
+ PlaylistItem *firstChild() const;
+
+ /**
+ * Allow duplicate files in the playlist.
+ */
+ void setAllowDuplicates(bool allow) { m_allowDuplicates = allow; }
+
+ /**
+ * This is being used as a mini-factory of sorts to make the construction
+ * of PlaylistItems virtual. In this case it allows for the creation of
+ * both PlaylistItems and CollectionListItems.
+ */
+ virtual PlaylistItem *createItem(const FileHandle &file,
+ QListViewItem *after = 0,
+ bool emitChanged = true);
+
+ /**
+ * This is implemented as a template method to allow subclasses to
+ * instantiate their PlaylistItem subclasses using the same method. Some
+ * of the types here are artificially templatized (i.e. CollectionListType and
+ * CollectionItemType) to avoid recursive includes, but in fact will always
+ * be the same.
+ */
+ template <class ItemType, class CollectionItemType, class CollectionListType>
+ ItemType *createItem(const FileHandle &file,
+ QListViewItem *after = 0,
+ bool emitChanged = true);
+
+ virtual void createItems(const PlaylistItemList &siblings, PlaylistItem *after = 0);
+
+ /**
+ * This handles adding files of various types -- music, playlist or directory
+ * files. Music files that are found will be added to this playlist. New
+ * playlist files that are found will result in new playlists being created.
+ *
+ * Note that this should not be used in the case of adding *only* playlist
+ * items since it has the overhead of checking to see if the file is a playlist
+ * or directory first.
+ */
+ virtual void addFiles(const QStringList &files, PlaylistItem *after = 0);
+
+ /**
+ * Returns the file name associated with this playlist (an m3u file) or
+ * QString::null if no such file exists.
+ */
+ QString fileName() const { return m_fileName; }
+
+ /**
+ * Sets the file name to be associated with this playlist; this file should
+ * have the "m3u" extension.
+ */
+ void setFileName(const QString &n) { m_fileName = n; }
+
+ /**
+ * Hides column \a c. If \a updateSearch is true then a signal that the
+ * visible columns have changed will be emitted and things like the search
+ * will be udated.
+ */
+ void hideColumn(int c, bool updateSearch = true);
+
+ /**
+ * Shows column \a c. If \a updateSearch is true then a signal that the
+ * visible columns have changed will be emitted and things like the search
+ * will be udated.
+ */
+ void showColumn(int c, bool updateSearch = true);
+ bool isColumnVisible(int c) const;
+
+ /**
+ * This sets a name for the playlist that is \e different from the file name.
+ */
+ void setName(const QString &n);
+
+ /**
+ * Returns the KActionMenu that allows this to be embedded in menus outside
+ * of the playlist.
+ */
+ KActionMenu *columnVisibleAction() const { return m_columnVisibleAction; }
+
+ /**
+ * Set item to be the playing item. If \a item is null then this will clear
+ * the playing indicator.
+ */
+ static void setPlaying(PlaylistItem *item, bool addToHistory = true);
+
+ /**
+ * Returns true if this playlist is currently playing.
+ */
+ bool playing() const;
+
+ /**
+ * This forces an update of the left most visible column, but does not save
+ * the settings for this.
+ */
+ void updateLeftColumn();
+
+ /**
+ * Returns the leftmost visible column of the listview.
+ */
+ int leftColumn() const { return m_leftColumn; }
+
+ /**
+ * Sets the items in the list to be either visible based on the value of
+ * visible. This is useful for search operations and such.
+ */
+ static void setItemsVisible(const PlaylistItemList &items, bool visible = true);
+
+ /**
+ * Returns the search associated with this list, or an empty search if one
+ * has not yet been set.
+ */
+ PlaylistSearch search() const { return m_search; }
+
+ /**
+ * Set the search associtated with this playlist.
+ */
+ void setSearch(const PlaylistSearch &s);
+
+ /**
+ * If the search is disabled then all items will be shown, not just those that
+ * match the current search.
+ */
+ void setSearchEnabled(bool searchEnabled);
+
+ /**
+ * Marks \a item as either selected or deselected based.
+ */
+ void markItemSelected(PlaylistItem *item, bool selected);
+
+ /**
+ * Subclasses of Playlist which add new columns will set this value to
+ * specify how many of those colums exist. This allows the Playlist
+ * class to do some internal calculations on the number and positions
+ * of columns.
+ */
+ virtual int columnOffset() const { return 0; }
+
+ /**
+ * Some subclasses of Playlist will be "read only" lists (i.e. the history
+ * playlist). This is a way for those subclasses to indicate that to the
+ * Playlist internals.
+ */
+ virtual bool readOnly() const { return false; }
+
+ /**
+ * Returns true if it's possible to reload this playlist.
+ */
+ virtual bool canReload() const { return !m_fileName.isNull(); }
+
+ /**
+ * Returns true if the playlist is a search playlist and the search should be
+ * editable.
+ */
+ virtual bool searchIsEditable() const { return false; }
+
+ /**
+ * Synchronizes the the playing item in this playlist with the playing item
+ * in \a sources. If \a setMaster is true, this list will become the source
+ * for determining the next item.
+ */
+ void synchronizePlayingItems(const PlaylistList &sources, bool setMaster);
+
+ /**
+ * Playlists have a common set of shared settings such as visible columns
+ * that should be applied just before the playlist is shown. Calling this
+ * method applies those.
+ */
+ void applySharedSettings();
+
+ void read(QDataStream &s);
+
+ static void setShuttingDown() { m_shuttingDown = true; }
+
+public slots:
+ /**
+ * Remove the currently selected items from the playlist and disk.
+ */
+ void slotRemoveSelectedItems() { removeFromDisk(selectedItems()); };
+
+ /*
+ * The edit slots are required to use the canonical names so that they are
+ * detected by the application wide framework.
+ */
+ virtual void cut() { copy(); clear(); }
+
+ /**
+ * Puts a list of URLs pointing to the files in the current selection on the
+ * clipboard.
+ */
+ virtual void copy();
+
+ /**
+ * Checks the clipboard for local URLs to be inserted into this playlist.
+ */
+ virtual void paste();
+
+ /**
+ * Removes the selected items from the list, but not the disk.
+ *
+ * @see clearItem()
+ * @see clearItems()
+ */
+ virtual void clear();
+ virtual void selectAll() { KListView::selectAll(true); }
+
+ /**
+ * Refreshes the tags of the selection from disk, or all of the files in the
+ * list if there is no selection.
+ */
+ virtual void slotRefresh();
+
+ void slotGuessTagInfo(TagGuesser::Type type);
+
+ /**
+ * Renames the selected items' files based on their tags contents.
+ *
+ * @see PlaylistItem::renameFile()
+ */
+ void slotRenameFile();
+
+ /**
+ * Sets the cover of the selected items, pass in true if you want to load from the local system,
+ * false if you want to load from the internet.
+ */
+ void slotAddCover(bool fromLocal);
+
+ /**
+ * Shows a large image of the cover
+ */
+ void slotViewCover();
+
+ /**
+ * Removes covers from the selected items
+ */
+ void slotRemoveCover();
+
+ /**
+ * Shows the cover manager GUI dialog
+ */
+ void slotShowCoverManager();
+
+ /**
+ * Reload the playlist contents from the m3u file.
+ */
+ virtual void slotReload();
+
+ /**
+ * Tells the listview that the next time that it paints that the weighted
+ * column widths must be recalculated. If this is called without a column
+ * all visible columns are marked as dirty.
+ */
+ void slotWeightDirty(int column = -1);
+
+ void slotShowPlaying();
+
+ void slotColumnResizeModeChanged();
+
+ virtual void dataChanged();
+
+protected:
+ /**
+ * Remove \a items from the playlist and disk. This will ignore items that
+ * are not actually in the list.
+ */
+ void removeFromDisk(const PlaylistItemList &items);
+
+ // the following are all reimplemented from base classes
+
+ virtual bool eventFilter(QObject *watched, QEvent *e);
+ virtual void keyPressEvent(QKeyEvent *e);
+ virtual QDragObject *dragObject(QWidget *parent);
+ virtual QDragObject *dragObject() { return dragObject(this); }
+ virtual bool canDecode(QMimeSource *s);
+ virtual void decode(QMimeSource *s, PlaylistItem *item = 0);
+ virtual void contentsDropEvent(QDropEvent *e);
+ virtual void contentsMouseDoubleClickEvent(QMouseEvent *e);
+ virtual void contentsDragEnterEvent(QDragEnterEvent *e);
+ virtual void showEvent(QShowEvent *e);
+ virtual bool acceptDrag(QDropEvent *e) const;
+ virtual void viewportPaintEvent(QPaintEvent *pe);
+ virtual void viewportResizeEvent(QResizeEvent *re);
+
+ virtual void insertItem(QListViewItem *item);
+ virtual void takeItem(QListViewItem *item);
+
+ virtual bool hasItem(const QString &file) const { return m_members.contains(file); }
+
+ void addColumn(const QString &label);
+
+ /**
+ * Here I'm using delayed setup of some things that aren't quite intuitive.
+ * Creating columns and setting up connections are both time consuming if
+ * there are a lot of playlists to initialize. This moves that cost from the
+ * startup time to the time when the widget is "polished" -- i.e. just before
+ * it's painted the first time.
+ */
+ virtual void polish();
+
+ /**
+ * Do some finial initialization of created items. Notably ensure that they
+ * are shown or hidden based on the contents of the current PlaylistSearch.
+ *
+ * This is called by the PlaylistItem constructor.
+ */
+ void setupItem(PlaylistItem *item);
+
+ /**
+ * Forwards the call to the parent to enable or disable automatic deletion
+ * of tree view playlists. Used by CollectionListItem.
+ */
+ void setDynamicListsFrozen(bool frozen);
+
+ template <class ItemType, class SiblingType>
+ ItemType *createItem(SiblingType *sibling, ItemType *after = 0);
+
+ /**
+ * As a template this allows us to use the same code to initialize the items
+ * in subclasses. CollectionItemType should always be CollectionListItem and
+ * ItemType should be a PlaylistItem subclass.
+ */
+ template <class CollectionItemType, class ItemType, class SiblingType>
+ void createItems(const QValueList<SiblingType *> &siblings, ItemType *after = 0);
+
+protected slots:
+ void slotPopulateBackMenu() const;
+ void slotPlayFromBackMenu(int number) const;
+
+signals:
+
+ /**
+ * This is connected to the PlaylistBox::Item to let it know when the
+ * playlist's name has changed.
+ */
+ void signalNameChanged(const QString &name);
+
+ /**
+ * This signal is emitted just before a playlist item is removed from the
+ * list allowing for any cleanup that needs to happen. Typically this
+ * is used to remove the item from the history and safeguard against
+ * dangling pointers.
+ */
+ void signalAboutToRemove(PlaylistItem *item);
+
+ void signalEnableDirWatch(bool enable);
+
+ void coverChanged();
+
+ void signalPlaylistItemsDropped(Playlist *p);
+
+private:
+ void setup();
+
+ /**
+ * This function is called to let the user know that JuK has automatically enabled
+ * manual column width adjust mode.
+ */
+ void notifyUserColumnWidthModeChanged();
+
+ /**
+ * Load the playlist from a file. \a fileName should be the absolute path.
+ * \a fileInfo should point to the same file as \a fileName. This is a
+ * little awkward API-wise, but keeps us from throwing away useful
+ * information.
+ */
+ void loadFile(const QString &fileName, const QFileInfo &fileInfo);
+
+ /**
+ * Writes \a text to \a item in \a column. This is used by the inline tag
+ * editor. Returns false if the tag update failed.
+ */
+ bool editTag(PlaylistItem *item, const QString &text, int column);
+
+ /**
+ * Returns the index of the left most visible column in the playlist.
+ *
+ * \see isColumnVisible()
+ */
+ int leftMostVisibleColumn() const;
+
+ /**
+ * This method is used internally to provide the backend to the other item
+ * lists.
+ *
+ * \see items()
+ * \see visibleItems()
+ * \see selectedItems()
+ */
+ PlaylistItemList items(QListViewItemIterator::IteratorFlag flags);
+
+ /**
+ * Build the column "weights" for the weighted width mode.
+ */
+ void calculateColumnWeights();
+
+ void addFile(const QString &file, FileHandleList &files, bool importPlaylists,
+ PlaylistItem **after);
+ void addFileHelper(FileHandleList &files, PlaylistItem **after,
+ bool ignoreTimer = false);
+
+ void redisplaySearch() { setSearch(m_search); }
+
+ /**
+ * Sets the cover for items to the cover identified by id.
+ */
+ void refreshAlbums(const PlaylistItemList &items, coverKey id = CoverManager::NoMatch);
+
+ void refreshAlbum(const QString &artist, const QString &album);
+
+ /**
+ * Returns the number of PlaylistItems in @p items that can be assigned a
+ * cover. Used to avoid wasting the users' time setting the cover for 20
+ * items when none are eligible.
+ */
+ unsigned int eligibleCoverItems(const PlaylistItemList &items);
+
+ void updatePlaying() const;
+
+ /**
+ * This class is used internally to store settings that are shared by all
+ * of the playlists, such as column order. It is implemented as a singleton.
+ */
+ class SharedSettings;
+
+private slots:
+
+ void slotUpdateColumnWidths();
+
+ void slotAddToUpcoming();
+
+ /**
+ * Show the RMB menu. Matches the signature for the signal
+ * QListView::contextMenuRequested().
+ */
+ void slotShowRMBMenu(QListViewItem *item, const QPoint &point, int column);
+
+ /**
+ * This slot is called when the inline tag editor has completed its editing
+ * and starts the process of renaming the values.
+ *
+ * \see editTag()
+ */
+ void slotInlineEditDone(QListViewItem *, const QString &, int column);
+
+ /**
+ * This starts the renaming process by displaying a line edit if the mouse is in
+ * an appropriate position.
+ */
+ void slotRenameTag();
+
+ /**
+ * The image fetcher will update the cover asynchronously, this internal
+ * slot is called when it happens.
+ */
+ void slotCoverChanged(int coverId);
+
+ /**
+ * Moves the column \a from to the position \a to. This matches the signature
+ * for the signal QHeader::indexChange().
+ */
+ void slotColumnOrderChanged(int, int from, int to);
+
+ /**
+ * Toggles a columns visible status. Useful for KActions.
+ *
+ * \see hideColumn()
+ * \see showColumn()
+ */
+ void slotToggleColumnVisible(int column);
+
+ /**
+ * Prompts the user to create a new playlist with from the selected items.
+ */
+ void slotCreateGroup();
+
+ /**
+ * This slot is called when the user drags the slider in the listview header
+ * to manually set the size of the column.
+ */
+ void slotColumnSizeChanged(int column, int oldSize, int newSize);
+
+ /**
+ * The slot is called when the completion mode for the line edit in the
+ * inline tag editor is changed. It saves the settings and through the
+ * magic of the SharedSettings class will apply it to the other playlists as
+ * well.
+ */
+ void slotInlineCompletionModeChanged(KGlobalSettings::Completion mode);
+
+ void slotPlayCurrent();
+
+private:
+ PlaylistCollection *m_collection;
+
+ StringHash m_members;
+
+ WebImageFetcher *m_fetcher;
+
+ int m_currentColumn;
+ int m_processed;
+ int m_rmbEditID;
+ int m_rmbUpcomingID;
+ int m_selectedCount;
+
+ bool m_allowDuplicates;
+ bool m_polished;
+ bool m_applySharedSettings;
+ bool m_columnWidthModeChanged;
+
+ QValueList<int> m_weightDirty;
+ bool m_disableColumnWidthUpdates;
+
+ mutable int m_time;
+ mutable PlaylistItemList m_addTime;
+ mutable PlaylistItemList m_subtractTime;
+
+ /**
+ * The average minimum widths of columns to be used in balancing calculations.
+ */
+ QValueVector<int> m_columnWeights;
+ QValueVector<int> m_columnFixedWidths;
+ bool m_widthsDirty;
+
+ static PlaylistItemList m_history;
+ PlaylistSearch m_search;
+
+ bool m_searchEnabled;
+
+ PlaylistItem *m_lastSelected;
+
+ /**
+ * Used to store the text for inline editing before it is changed so that
+ * we can know if something actually changed and as such if we need to save
+ * the tag.
+ */
+ QString m_editText;
+
+ /**
+ * This is only defined if the playlist name is something other than the
+ * file name.
+ */
+ QString m_playlistName;
+ QString m_fileName;
+
+ KPopupMenu *m_rmbMenu;
+ KPopupMenu *m_headerMenu;
+ KActionMenu *m_columnVisibleAction;
+ PlaylistToolTip *m_toolTip;
+
+ /**
+ * This is used to indicate if the list of visible items has changed (via a
+ * call to setVisibleItems()) while random play is playing.
+ */
+ static bool m_visibleChanged;
+ static bool m_shuttingDown;
+ static int m_leftColumn;
+ static QMap<int, PlaylistItem *> m_backMenuItems;
+
+ bool m_blockDataChanged;
+};
+
+bool processEvents();
+
+class FocusUpEvent : public QCustomEvent
+{
+public:
+ FocusUpEvent() : QCustomEvent(id) {}
+ static const int id = 999;
+};
+
+QDataStream &operator<<(QDataStream &s, const Playlist &p);
+QDataStream &operator>>(QDataStream &s, Playlist &p);
+
+// template method implementations
+
+template <class ItemType, class CollectionItemType, class CollectionListType>
+ItemType *Playlist::createItem(const FileHandle &file, QListViewItem *after,
+ bool emitChanged)
+{
+ CollectionItemType *item = CollectionListType::instance()->lookup(file.absFilePath());
+
+ if(!item) {
+ item = new CollectionItemType(file);
+ setupItem(item);
+
+ // If a valid tag was not created, destroy the CollectionListItem.
+
+ if(!item->isValid()) {
+ kdError(65432) << "Playlist::createItem() -- A valid tag was not created for \""
+ << file.absFilePath() << "\"" << endl;
+ delete item;
+ return 0;
+ }
+ }
+
+ if(item && !m_members.insert(file.absFilePath()) || m_allowDuplicates) {
+
+ ItemType *i = after ? new ItemType(item, this, after) : new ItemType(item, this);
+ setupItem(i);
+
+ if(emitChanged)
+ dataChanged();
+
+ return i;
+ }
+ else
+ return 0;
+}
+
+template <class ItemType, class SiblingType>
+ItemType *Playlist::createItem(SiblingType *sibling, ItemType *after)
+{
+ m_disableColumnWidthUpdates = true;
+
+ if(!m_members.insert(sibling->file().absFilePath()) || m_allowDuplicates) {
+ after = new ItemType(sibling->collectionItem(), this, after);
+ setupItem(after);
+ }
+
+ m_disableColumnWidthUpdates = false;
+
+ return after;
+}
+
+template <class CollectionItemType, class ItemType, class SiblingType>
+void Playlist::createItems(const QValueList<SiblingType *> &siblings, ItemType *after)
+{
+ if(siblings.isEmpty())
+ return;
+
+ QValueListConstIterator<SiblingType *> it = siblings.begin();
+ for(; it != siblings.end(); ++it)
+ after = createItem(*it, after);
+
+ dataChanged();
+ slotWeightDirty();
+}
+
+#endif
diff --git a/juk/playlistbox.cpp b/juk/playlistbox.cpp
new file mode 100644
index 00000000..092342a3
--- /dev/null
+++ b/juk/playlistbox.cpp
@@ -0,0 +1,790 @@
+/***************************************************************************
+ begin : Thu Sep 12 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler,
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kiconloader.h>
+#include <kurldrag.h>
+#include <kmessagebox.h>
+#include <kpopupmenu.h>
+#include <kaction.h>
+#include <kdebug.h>
+
+#include <qheader.h>
+#include <qpainter.h>
+#include <qtimer.h>
+
+#include "playlistbox.h"
+#include "playlist.h"
+#include "collectionlist.h"
+#include "covermanager.h"
+#include "dynamicplaylist.h"
+#include "historyplaylist.h"
+#include "upcomingplaylist.h"
+#include "viewmode.h"
+#include "searchplaylist.h"
+#include "treeviewitemplaylist.h"
+#include "actioncollection.h"
+#include "cache.h"
+#include "k3bexporter.h"
+#include "tracksequencemanager.h"
+#include "tagtransactionmanager.h"
+
+using namespace ActionCollection;
+
+////////////////////////////////////////////////////////////////////////////////
+// PlaylistBox public methods
+////////////////////////////////////////////////////////////////////////////////
+
+PlaylistBox::PlaylistBox(QWidget *parent, QWidgetStack *playlistStack,
+ const char *name) :
+ KListView(parent, name),
+ PlaylistCollection(playlistStack),
+ m_viewModeIndex(0),
+ m_hasSelection(false),
+ m_doingMultiSelect(false),
+ m_dropItem(0),
+ m_showTimer(0)
+{
+ readConfig();
+ addColumn("Playlists", width());
+
+ header()->blockSignals(true);
+ header()->hide();
+ header()->blockSignals(false);
+
+ setSorting(0);
+ setFullWidth(true);
+ setItemMargin(3);
+
+ setAcceptDrops(true);
+ setSelectionModeExt(Extended);
+
+ m_contextMenu = new KPopupMenu(this);
+
+ K3bPlaylistExporter *exporter = new K3bPlaylistExporter(this);
+ m_k3bAction = exporter->action();
+
+ action("file_new")->plug(m_contextMenu);
+ action("renamePlaylist")->plug(m_contextMenu);
+ action("editSearch")->plug(m_contextMenu);
+ action("duplicatePlaylist")->plug(m_contextMenu);
+ action("reloadPlaylist")->plug(m_contextMenu);
+ action("deleteItemPlaylist")->plug(m_contextMenu);
+ action("file_save")->plug(m_contextMenu);
+ action("file_save_as")->plug(m_contextMenu);
+ if(m_k3bAction)
+ m_k3bAction->plug(m_contextMenu);
+
+ m_contextMenu->insertSeparator();
+
+ // add the view modes stuff
+
+ KSelectAction *viewModeAction =
+ new KSelectAction(i18n("View Modes"), "view_choose", KShortcut(), actions(), "viewModeMenu");
+
+ m_viewModes.append(new ViewMode(this));
+ m_viewModes.append(new CompactViewMode(this));
+ m_viewModes.append(new TreeViewMode(this));
+ // m_viewModes.append(new CoverManagerMode(this));
+
+ QStringList modeNames;
+
+ for(QValueListIterator<ViewMode *> it = m_viewModes.begin(); it != m_viewModes.end(); ++it)
+ modeNames.append((*it)->name());
+
+ viewModeAction->setItems(modeNames);
+
+ QPopupMenu *p = viewModeAction->popupMenu();
+ p->changeItem(0, SmallIconSet("view_detailed"), modeNames[0]);
+ p->changeItem(1, SmallIconSet("view_text"), modeNames[1]);
+ p->changeItem(2, SmallIconSet("view_tree"), modeNames[2]);
+
+ CollectionList::initialize(this);
+ Cache::loadPlaylists(this);
+
+ viewModeAction->setCurrentItem(m_viewModeIndex);
+ m_viewModes[m_viewModeIndex]->setShown(true);
+
+ TrackSequenceManager::instance()->setCurrentPlaylist(CollectionList::instance());
+ raise(CollectionList::instance());
+
+ viewModeAction->plug(m_contextMenu);
+ connect(viewModeAction, SIGNAL(activated(int)), this, SLOT(slotSetViewMode(int)));
+
+ connect(this, SIGNAL(selectionChanged()),
+ this, SLOT(slotPlaylistChanged()));
+
+ connect(this, SIGNAL(doubleClicked(QListViewItem *)),
+ this, SLOT(slotDoubleClicked()));
+
+ connect(this, SIGNAL(contextMenuRequested(QListViewItem *, const QPoint &, int)),
+ this, SLOT(slotShowContextMenu(QListViewItem *, const QPoint &, int)));
+
+ TagTransactionManager *tagManager = TagTransactionManager::instance();
+ connect(tagManager, SIGNAL(signalAboutToModifyTags()), SLOT(slotFreezePlaylists()));
+ connect(tagManager, SIGNAL(signalDoneModifyingTags()), SLOT(slotUnfreezePlaylists()));
+
+ setupUpcomingPlaylist();
+
+ connect(CollectionList::instance(), SIGNAL(signalNewTag(const QString &, unsigned)),
+ this, SLOT(slotAddItem(const QString &, unsigned)));
+ connect(CollectionList::instance(), SIGNAL(signalRemovedTag(const QString &, unsigned)),
+ this, SLOT(slotRemoveItem(const QString &, unsigned)));
+
+ QTimer::singleShot(0, object(), SLOT(slotScanFolders()));
+ enableDirWatch(true);
+
+ // Auto-save playlists after 10 minutes
+ QTimer::singleShot(600000, this, SLOT(slotSavePlaylists()));
+
+ m_showTimer = new QTimer(this);
+ connect(m_showTimer, SIGNAL(timeout()), SLOT(slotShowDropTarget()));
+}
+
+PlaylistBox::~PlaylistBox()
+{
+ PlaylistList l;
+ CollectionList *collection = CollectionList::instance();
+ for(QListViewItem *i = firstChild(); i; i = i->nextSibling()) {
+ Item *item = static_cast<Item *>(i);
+ if(item->playlist() && item->playlist() != collection)
+ l.append(item->playlist());
+ }
+
+ Cache::savePlaylists(l);
+ saveConfig();
+}
+
+void PlaylistBox::raise(Playlist *playlist)
+{
+ if(!playlist)
+ return;
+
+ Item *i = m_playlistDict.find(playlist);
+
+ if(i) {
+ clearSelection();
+ setSelected(i, true);
+
+ setSingleItem(i);
+ ensureItemVisible(currentItem());
+ }
+ else
+ PlaylistCollection::raise(playlist);
+
+ slotPlaylistChanged();
+}
+
+void PlaylistBox::duplicate()
+{
+ Item *item = static_cast<Item *>(currentItem());
+ if(!item || !item->playlist())
+ return;
+
+ QString name = playlistNameDialog(i18n("Duplicate"), item->text(0));
+
+ if(name.isNull())
+ return;
+
+ Playlist *p = new Playlist(this, name);
+ p->createItems(item->playlist()->items());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlaylistBox public slots
+////////////////////////////////////////////////////////////////////////////////
+
+void PlaylistBox::paste()
+{
+ Item *i = static_cast<Item *>(currentItem());
+ decode(kapp->clipboard()->data(), i);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlaylistBox protected methods
+////////////////////////////////////////////////////////////////////////////////
+
+void PlaylistBox::slotFreezePlaylists()
+{
+ setDynamicListsFrozen(true);
+}
+
+void PlaylistBox::slotUnfreezePlaylists()
+{
+ setDynamicListsFrozen(false);
+}
+
+void PlaylistBox::setupPlaylist(Playlist *playlist, const QString &iconName)
+{
+ setupPlaylist(playlist, iconName, 0);
+}
+
+void PlaylistBox::setupPlaylist(Playlist *playlist, const QString &iconName, Item *parentItem)
+{
+ connect(playlist, SIGNAL(signalPlaylistItemsDropped(Playlist *)),
+ SLOT(slotPlaylistItemsDropped(Playlist *)));
+
+ PlaylistCollection::setupPlaylist(playlist, iconName);
+
+ if(parentItem)
+ new Item(parentItem, iconName, playlist->name(), playlist);
+ else
+ new Item(this, iconName, playlist->name(), playlist);
+}
+
+void PlaylistBox::removePlaylist(Playlist *playlist)
+{
+ removeNameFromDict(m_playlistDict[playlist]->text(0));
+ removeFileFromDict(playlist->fileName());
+ m_playlistDict.remove(playlist);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlaylistBox private methods
+////////////////////////////////////////////////////////////////////////////////
+
+void PlaylistBox::readConfig()
+{
+ KConfigGroup config(KGlobal::config(), "PlaylistBox");
+ m_viewModeIndex = config.readNumEntry("ViewMode", 0);
+}
+
+void PlaylistBox::saveConfig()
+{
+ KConfigGroup config(KGlobal::config(), "PlaylistBox");
+ config.writeEntry("ViewMode", action<KSelectAction>("viewModeMenu")->currentItem());
+ KGlobal::config()->sync();
+}
+
+void PlaylistBox::remove()
+{
+ ItemList items = selectedItems();
+
+ if(items.isEmpty())
+ return;
+
+ QStringList files;
+ QStringList names;
+
+ for(ItemList::ConstIterator it = items.begin(); it != items.end(); ++it) {
+ if(*it && (*it)->playlist() &&
+ !(*it)->playlist()->fileName().isEmpty() &&
+ QFileInfo((*it)->playlist()->fileName()).exists())
+ {
+ files.append((*it)->playlist()->fileName());
+ }
+ names.append((*it)->playlist()->name());
+ }
+
+ if(!files.isEmpty()) {
+ int remove = KMessageBox::warningYesNoCancelList(
+ this, i18n("Do you want to delete these files from the disk as well?"), files, QString::null, KStdGuiItem::del(), i18n("Keep"));
+
+ if(remove == KMessageBox::Yes) {
+ QStringList couldNotDelete;
+ for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it) {
+ if(!QFile::remove(*it))
+ couldNotDelete.append(*it);
+ }
+
+ if(!couldNotDelete.isEmpty())
+ KMessageBox::errorList(this, i18n("Could not delete these files."), couldNotDelete);
+ }
+ else if(remove == KMessageBox::Cancel)
+ return;
+ }
+ else if(items.count() > 1 || items.front()->playlist() != upcomingPlaylist()) {
+ if(KMessageBox::warningContinueCancelList(this,
+ i18n("Are you sure you want to remove these "
+ "playlists from your collection?"),
+ names,
+ i18n("Remove Items?"),
+ KGuiItem(i18n("&Remove"), "edittrash")) == KMessageBox::Cancel)
+ {
+ return;
+ }
+ }
+
+ PlaylistList removeQueue;
+
+ for(ItemList::ConstIterator it = items.begin(); it != items.end(); ++it) {
+ if(*it != Item::collectionItem() &&
+ (*it)->playlist() &&
+ (!(*it)->playlist()->readOnly()))
+ {
+ removeQueue.append((*it)->playlist());
+ }
+ }
+
+ if(items.back()->nextSibling() && static_cast<Item *>(items.back()->nextSibling())->playlist())
+ setSingleItem(items.back()->nextSibling());
+ else {
+ Item *i = static_cast<Item *>(items.front()->itemAbove());
+ while(i && !i->playlist())
+ i = static_cast<Item *>(i->itemAbove());
+
+ if(!i)
+ i = Item::collectionItem();
+
+ setSingleItem(i);
+ }
+
+ for(PlaylistList::ConstIterator it = removeQueue.begin(); it != removeQueue.end(); ++it) {
+ if(*it != upcomingPlaylist())
+ delete *it;
+ else {
+ action<KToggleAction>("showUpcoming")->setChecked(false);
+ setUpcomingPlaylistEnabled(false);
+ }
+ }
+}
+
+void PlaylistBox::setDynamicListsFrozen(bool frozen)
+{
+ for(QValueList<ViewMode *>::Iterator it = m_viewModes.begin();
+ it != m_viewModes.end();
+ ++it)
+ {
+ (*it)->setDynamicListsFrozen(frozen);
+ }
+}
+
+void PlaylistBox::slotSavePlaylists()
+{
+ kdDebug(65432) << "Auto-saving playlists and covers.\n";
+
+ PlaylistList l;
+ CollectionList *collection = CollectionList::instance();
+ for(QListViewItem *i = firstChild(); i; i = i->nextSibling()) {
+ Item *item = static_cast<Item *>(i);
+ if(item->playlist() && item->playlist() != collection)
+ l.append(item->playlist());
+ }
+
+ Cache::savePlaylists(l);
+ CoverManager::saveCovers();
+
+ QTimer::singleShot(600000, this, SLOT(slotSavePlaylists()));
+}
+
+void PlaylistBox::slotShowDropTarget()
+{
+ if(!m_dropItem) {
+ kdError(65432) << "Trying to show the playlist of a null item!\n";
+ return;
+ }
+
+ raise(m_dropItem->playlist());
+}
+
+void PlaylistBox::slotAddItem(const QString &tag, unsigned column)
+{
+ for(QValueListIterator<ViewMode *> it = m_viewModes.begin(); it != m_viewModes.end(); ++it)
+ (*it)->addItems(tag, column);
+}
+
+void PlaylistBox::slotRemoveItem(const QString &tag, unsigned column)
+{
+ for(QValueListIterator<ViewMode *> it = m_viewModes.begin(); it != m_viewModes.end(); ++it)
+ (*it)->removeItem(tag, column);
+}
+
+void PlaylistBox::decode(QMimeSource *s, Item *item)
+{
+ if(!s || (item && item->playlist() && item->playlist()->readOnly()))
+ return;
+
+ KURL::List urls;
+
+ if(KURLDrag::decode(s, urls) && !urls.isEmpty()) {
+ QStringList files;
+ for(KURL::List::Iterator it = urls.begin(); it != urls.end(); ++it)
+ files.append((*it).path());
+
+ if(item) {
+ TreeViewItemPlaylist *playlistItem;
+ playlistItem = dynamic_cast<TreeViewItemPlaylist *>(item->playlist());
+ if(playlistItem) {
+ playlistItem->retag(files, currentPlaylist());
+ TagTransactionManager::instance()->commit();
+ currentPlaylist()->update();
+ return;
+ }
+ }
+
+ if(item && item->playlist())
+ item->playlist()->addFiles(files);
+ else {
+ QString name = playlistNameDialog();
+ if(!name.isNull()) {
+ Playlist *p = new Playlist(this, name);
+ p->addFiles(files);
+ }
+ }
+ }
+}
+
+void PlaylistBox::contentsDropEvent(QDropEvent *e)
+{
+ m_showTimer->stop();
+
+ Item *i = static_cast<Item *>(itemAt(contentsToViewport(e->pos())));
+ decode(e, i);
+
+ if(m_dropItem) {
+ Item *old = m_dropItem;
+ m_dropItem = 0;
+ old->repaint();
+ }
+}
+
+void PlaylistBox::contentsDragMoveEvent(QDragMoveEvent *e)
+{
+ // If we can decode the input source, there is a non-null item at the "move"
+ // position, the playlist for that Item is non-null, is not the
+ // selected playlist and is not the CollectionList, then accept the event.
+ //
+ // Otherwise, do not accept the event.
+
+ if(!KURLDrag::canDecode(e)) {
+ e->accept(false);
+ return;
+ }
+
+ Item *target = static_cast<Item *>(itemAt(contentsToViewport(e->pos())));
+
+ if(target) {
+
+ if(target->playlist() && target->playlist()->readOnly())
+ return;
+
+ // This is a semi-dirty hack to check if the items are coming from within
+ // JuK. If they are not coming from a Playlist (or subclass) then the
+ // dynamic_cast will fail and we can safely assume that the item is
+ // coming from outside of JuK.
+
+ if(dynamic_cast<Playlist *>(e->source())) {
+ if(target->playlist() &&
+ target->playlist() != CollectionList::instance() &&
+ !target->isSelected())
+ {
+ e->accept(true);
+ }
+ else
+ e->accept(false);
+ }
+ else // the dropped items are coming from outside of JuK
+ e->accept(true);
+
+ if(m_dropItem != target) {
+ Item *old = m_dropItem;
+ m_showTimer->stop();
+
+ if(e->isAccepted()) {
+ m_dropItem = target;
+ target->repaint();
+ m_showTimer->start(1500, true);
+ }
+ else
+ m_dropItem = 0;
+
+ if(old)
+ old->repaint();
+ }
+ }
+ else {
+
+ // We're dragging over the whitespace. We'll use this case to make it
+ // possible to create new lists.
+
+ e->accept(true);
+ }
+}
+
+void PlaylistBox::contentsDragLeaveEvent(QDragLeaveEvent *e)
+{
+ if(m_dropItem) {
+ Item *old = m_dropItem;
+ m_dropItem = 0;
+ old->repaint();
+ }
+ KListView::contentsDragLeaveEvent(e);
+}
+
+void PlaylistBox::contentsMousePressEvent(QMouseEvent *e)
+{
+ if(e->button() == LeftButton)
+ m_doingMultiSelect = true;
+ KListView::contentsMousePressEvent(e);
+}
+
+void PlaylistBox::contentsMouseReleaseEvent(QMouseEvent *e)
+{
+ if(e->button() == LeftButton) {
+ m_doingMultiSelect = false;
+ slotPlaylistChanged();
+ }
+ KListView::contentsMouseReleaseEvent(e);
+}
+
+void PlaylistBox::keyPressEvent(QKeyEvent *e)
+{
+ if((e->key() == Key_Up || e->key() == Key_Down) && e->state() == ShiftButton)
+ m_doingMultiSelect = true;
+ KListView::keyPressEvent(e);
+}
+
+void PlaylistBox::keyReleaseEvent(QKeyEvent *e)
+{
+ if(m_doingMultiSelect && e->key() == Key_Shift) {
+ m_doingMultiSelect = false;
+ slotPlaylistChanged();
+ }
+ KListView::keyReleaseEvent(e);
+}
+
+PlaylistBox::ItemList PlaylistBox::selectedItems() const
+{
+ ItemList l;
+
+ for(QListViewItemIterator it(const_cast<PlaylistBox *>(this),
+ QListViewItemIterator::Selected); it.current(); ++it)
+ l.append(static_cast<Item *>(*it));
+
+ return l;
+}
+
+void PlaylistBox::setSingleItem(QListViewItem *item)
+{
+ setSelectionModeExt(Single);
+ KListView::setCurrentItem(item);
+ setSelectionModeExt(Extended);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlaylistBox private slots
+////////////////////////////////////////////////////////////////////////////////
+
+void PlaylistBox::slotPlaylistChanged()
+{
+ // Don't update while the mouse is pressed down.
+
+ if(m_doingMultiSelect)
+ return;
+
+ ItemList items = selectedItems();
+ m_hasSelection = !items.isEmpty();
+
+ bool allowReload = false;
+
+ PlaylistList playlists;
+ for(ItemList::ConstIterator it = items.begin(); it != items.end(); ++it) {
+
+ Playlist *p = (*it)->playlist();
+ if(p) {
+ if(p->canReload())
+ allowReload = true;
+ playlists.append(p);
+ }
+ }
+
+ bool singlePlaylist = playlists.count() == 1;
+
+ if(playlists.isEmpty() ||
+ (singlePlaylist &&
+ (playlists.front() == CollectionList::instance() ||
+ playlists.front()->readOnly())))
+ {
+ action("file_save")->setEnabled(false);
+ action("file_save_as")->setEnabled(false);
+ action("renamePlaylist")->setEnabled(false);
+ action("deleteItemPlaylist")->setEnabled(false);
+ }
+ else {
+ action("file_save")->setEnabled(true);
+ action("file_save_as")->setEnabled(true);
+ action("renamePlaylist")->setEnabled(playlists.count() == 1);
+ action("deleteItemPlaylist")->setEnabled(true);
+ }
+ action("reloadPlaylist")->setEnabled(allowReload);
+ action("duplicatePlaylist")->setEnabled(!playlists.isEmpty());
+
+ if(m_k3bAction)
+ m_k3bAction->setEnabled(!playlists.isEmpty());
+
+ action("editSearch")->setEnabled(singlePlaylist &&
+ playlists.front()->searchIsEditable());
+
+ if(singlePlaylist) {
+ PlaylistCollection::raise(playlists.front());
+
+ if(playlists.front() == upcomingPlaylist())
+ action("deleteItemPlaylist")->setText(i18n("Hid&e"));
+ else
+ action("deleteItemPlaylist")->setText(i18n("R&emove"));
+ }
+ else if(!playlists.isEmpty())
+ createDynamicPlaylist(playlists);
+}
+
+void PlaylistBox::slotDoubleClicked()
+{
+ action("stop")->activate();
+ action("play")->activate();
+}
+
+void PlaylistBox::slotShowContextMenu(QListViewItem *, const QPoint &point, int)
+{
+ m_contextMenu->popup(point);
+}
+
+void PlaylistBox::slotPlaylistItemsDropped(Playlist *p)
+{
+ raise(p);
+}
+
+void PlaylistBox::slotSetViewMode(int index)
+{
+ if(index == m_viewModeIndex)
+ return;
+
+ viewMode()->setShown(false);
+ m_viewModeIndex = index;
+ viewMode()->setShown(true);
+}
+
+void PlaylistBox::setupItem(Item *item)
+{
+ m_playlistDict.insert(item->playlist(), item);
+ viewMode()->queueRefresh();
+}
+
+void PlaylistBox::setupUpcomingPlaylist()
+{
+ KConfigGroup config(KGlobal::config(), "Playlists");
+ bool enable = config.readBoolEntry("showUpcoming", false);
+
+ setUpcomingPlaylistEnabled(enable);
+ action<KToggleAction>("showUpcoming")->setChecked(enable);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlaylistBox::Item protected methods
+////////////////////////////////////////////////////////////////////////////////
+
+PlaylistBox::Item *PlaylistBox::Item::m_collectionItem = 0;
+
+PlaylistBox::Item::Item(PlaylistBox *listBox, const QString &icon, const QString &text, Playlist *l)
+ : QObject(listBox), KListViewItem(listBox, 0, text),
+ m_playlist(l), m_text(text), m_iconName(icon), m_sortedFirst(false)
+{
+ init();
+}
+
+PlaylistBox::Item::Item(Item *parent, const QString &icon, const QString &text, Playlist *l)
+ : QObject(parent->listView()), KListViewItem(parent, text),
+ m_playlist(l), m_text(text), m_iconName(icon), m_sortedFirst(false)
+{
+ init();
+}
+
+PlaylistBox::Item::~Item()
+{
+
+}
+
+int PlaylistBox::Item::compare(QListViewItem *i, int col, bool) const
+{
+ Item *otherItem = static_cast<Item *>(i);
+ PlaylistBox *playlistBox = static_cast<PlaylistBox *>(listView());
+
+ if(m_playlist == playlistBox->upcomingPlaylist() && otherItem->m_playlist != CollectionList::instance())
+ return -1;
+ if(otherItem->m_playlist == playlistBox->upcomingPlaylist() && m_playlist != CollectionList::instance())
+ return 1;
+
+ if(m_sortedFirst && !otherItem->m_sortedFirst)
+ return -1;
+ else if(otherItem->m_sortedFirst && !m_sortedFirst)
+ return 1;
+
+ return text(col).lower().localeAwareCompare(i->text(col).lower());
+}
+
+void PlaylistBox::Item::paintCell(QPainter *painter, const QColorGroup &colorGroup, int column, int width, int align)
+{
+ PlaylistBox *playlistBox = static_cast<PlaylistBox *>(listView());
+ playlistBox->viewMode()->paintCell(this, painter, colorGroup, column, width, align);
+}
+
+void PlaylistBox::Item::setText(int column, const QString &text)
+{
+ m_text = text;
+ KListViewItem::setText(column, text);
+}
+
+void PlaylistBox::Item::setup()
+{
+ listView()->viewMode()->setupItem(this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlaylistBox::Item protected slots
+////////////////////////////////////////////////////////////////////////////////
+
+void PlaylistBox::Item::slotSetName(const QString &name)
+{
+ if(listView()) {
+ setText(0, name);
+ setSelected(true);
+
+ listView()->sort();
+ listView()->ensureItemVisible(listView()->currentItem());
+ listView()->viewMode()->queueRefresh();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlaylistBox::Item private methods
+////////////////////////////////////////////////////////////////////////////////
+
+void PlaylistBox::Item::init()
+{
+ PlaylistBox *list = listView();
+
+ list->setupItem(this);
+
+ int iconSize = list->viewModeIndex() == 0 ? 32 : 16;
+ setPixmap(0, SmallIcon(m_iconName, iconSize));
+ list->addNameToDict(m_text);
+
+ if(m_playlist) {
+ connect(m_playlist, SIGNAL(signalNameChanged(const QString &)),
+ this, SLOT(slotSetName(const QString &)));
+ connect(m_playlist, SIGNAL(destroyed()), this, SLOT(deleteLater()));
+ connect(m_playlist, SIGNAL(signalEnableDirWatch(bool)),
+ list->object(), SLOT(slotEnableDirWatch(bool)));
+ }
+
+ if(m_playlist == CollectionList::instance()) {
+ m_sortedFirst = true;
+ m_collectionItem = this;
+ list->viewMode()->setupDynamicPlaylists();
+ }
+
+ if(m_playlist == list->historyPlaylist() || m_playlist == list->upcomingPlaylist())
+ m_sortedFirst = true;
+}
+
+#include "playlistbox.moc"
diff --git a/juk/playlistbox.h b/juk/playlistbox.h
new file mode 100644
index 00000000..412ebb86
--- /dev/null
+++ b/juk/playlistbox.h
@@ -0,0 +1,189 @@
+/***************************************************************************
+ begin : Thu Sep 12 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef PLAYLISTBOX_H
+#define PLAYLISTBOX_H
+
+#include "playlistcollection.h"
+
+#include <klistview.h>
+
+#include <qptrdict.h>
+
+class Playlist;
+class PlaylistItem;
+class ViewMode;
+class PlaylistSearch;
+class SearchPlaylist;
+
+class KPopupMenu;
+class KSelectAction;
+
+typedef QValueList<Playlist *> PlaylistList;
+
+/**
+ * This is the play list selection box that is by default on the right side of
+ * JuK's main widget (PlaylistSplitter).
+ */
+
+class PlaylistBox : public KListView, public PlaylistCollection
+{
+ Q_OBJECT
+
+public:
+ class Item;
+ typedef QValueList<Item *> ItemList;
+
+ friend class Item;
+
+ PlaylistBox(QWidget *parent, QWidgetStack *playlistStack,
+ const char *name = 0);
+
+ virtual ~PlaylistBox();
+
+ virtual void raise(Playlist *playlist);
+ virtual void duplicate();
+ virtual void remove();
+
+ /**
+ * For view modes that have dynamic playlists, this freezes them from
+ * removing playlists.
+ */
+ virtual void setDynamicListsFrozen(bool frozen);
+
+ Item *dropItem() const { return m_dropItem; }
+
+ void setupPlaylist(Playlist *playlist, const QString &iconName, Item *parentItem = 0);
+
+public slots:
+ void paste();
+ void clear() {}
+
+ void slotFreezePlaylists();
+ void slotUnfreezePlaylists();
+
+protected:
+ virtual void setupPlaylist(Playlist *playlist, const QString &iconName);
+ virtual void removePlaylist(Playlist *playlist);
+
+signals:
+ void signalPlaylistDestroyed(Playlist *);
+
+private:
+ void readConfig();
+ void saveConfig();
+
+ virtual void decode(QMimeSource *s, Item *item);
+ virtual void contentsDropEvent(QDropEvent *e);
+ virtual void contentsDragMoveEvent(QDragMoveEvent *e);
+ virtual void contentsDragLeaveEvent(QDragLeaveEvent *e);
+ virtual void contentsMousePressEvent(QMouseEvent *e);
+ virtual void contentsMouseReleaseEvent(QMouseEvent *e);
+ virtual void keyPressEvent(QKeyEvent *e);
+ virtual void keyReleaseEvent(QKeyEvent *e);
+
+ QValueList<Item *> selectedItems() const;
+ void setSingleItem(QListViewItem *item);
+
+ void setupItem(Item *item);
+ void setupUpcomingPlaylist();
+ int viewModeIndex() const { return m_viewModeIndex; }
+ ViewMode *viewMode() const { return m_viewModes[m_viewModeIndex]; }
+
+private slots:
+ /**
+ * Catches QListBox::currentChanged(QListBoxItem *), does a cast and then re-emits
+ * the signal as currentChanged(Item *).
+ */
+ void slotPlaylistChanged();
+ void slotDoubleClicked();
+ void slotShowContextMenu(QListViewItem *, const QPoint &point, int);
+ void slotSetViewMode(int index);
+ void slotSavePlaylists();
+ void slotShowDropTarget();
+
+ void slotPlaylistItemsDropped(Playlist *p);
+
+ void slotAddItem(const QString &tag, unsigned column);
+ void slotRemoveItem(const QString &tag, unsigned column);
+
+private:
+ KPopupMenu *m_contextMenu;
+ QPtrDict<Item> m_playlistDict;
+ int m_viewModeIndex;
+ QValueList<ViewMode *> m_viewModes;
+ KAction *m_k3bAction;
+ bool m_hasSelection;
+ bool m_doingMultiSelect;
+ Item *m_dropItem;
+ QTimer *m_showTimer;
+};
+
+
+
+class PlaylistBox::Item : public QObject, public KListViewItem
+{
+ friend class PlaylistBox;
+ friend class ViewMode;
+ friend class CompactViewMode;
+ friend class TreeViewMode;
+
+ Q_OBJECT
+
+ // moc won't let me create private QObject subclasses and Qt won't let me
+ // make the destructor protected, so here's the closest hack that will
+ // compile.
+
+public:
+ virtual ~Item();
+
+protected:
+ Item(PlaylistBox *listBox, const QString &icon, const QString &text, Playlist *l = 0);
+ Item(Item *parent, const QString &icon, const QString &text, Playlist *l = 0);
+
+ Playlist *playlist() const { return m_playlist; }
+ PlaylistBox *listView() const { return static_cast<PlaylistBox *>(KListViewItem::listView()); }
+ QString iconName() const { return m_iconName; }
+ QString text() const { return m_text; }
+ void setSortedFirst(bool first = true) { m_sortedFirst = first; }
+
+ virtual int compare(QListViewItem *i, int col, bool) const;
+ virtual void paintCell(QPainter *p, const QColorGroup &colorGroup, int column, int width, int align);
+ virtual void paintFocus(QPainter *, const QColorGroup &, const QRect &) {}
+ virtual void setText(int column, const QString &text);
+
+ virtual QString text(int column) const { return KListViewItem::text(column); }
+
+ virtual void setup();
+
+ static Item *collectionItem() { return m_collectionItem; }
+ static void setCollectionItem(Item *item) { m_collectionItem = item; }
+
+
+protected slots:
+ void slotSetName(const QString &name);
+
+private:
+ // setup() was already taken.
+ void init();
+
+ Playlist *m_playlist;
+ QString m_text;
+ QString m_iconName;
+ bool m_sortedFirst;
+ static Item *m_collectionItem;
+};
+
+#endif
diff --git a/juk/playlistcollection.cpp b/juk/playlistcollection.cpp
new file mode 100644
index 00000000..548734c8
--- /dev/null
+++ b/juk/playlistcollection.cpp
@@ -0,0 +1,929 @@
+/***************************************************************************
+ copyright : (C) 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kurl.h>
+
+#include <config.h>
+#include <qobjectlist.h>
+
+#include <sys/types.h>
+#include <dirent.h>
+
+#include "collectionlist.h"
+#include "playlistcollection.h"
+#include "actioncollection.h"
+#include "advancedsearchdialog.h"
+#include "coverinfo.h"
+#include "searchplaylist.h"
+#include "folderplaylist.h"
+#include "historyplaylist.h"
+#include "upcomingplaylist.h"
+#include "directorylist.h"
+#include "mediafiles.h"
+#include "playermanager.h"
+#include "tracksequencemanager.h"
+
+#include <kiconloader.h>
+#include <kactionclasses.h>
+#include <kapplication.h>
+#include <kinputdialog.h>
+#include <kmessagebox.h>
+#include <kfiledialog.h>
+
+#include <qwidgetstack.h>
+#include <qhbox.h>
+
+#define widget (kapp->mainWidget())
+
+using namespace ActionCollection;
+
+////////////////////////////////////////////////////////////////////////////////
+// static methods
+////////////////////////////////////////////////////////////////////////////////
+
+PlaylistCollection *PlaylistCollection::m_instance = 0;
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+PlaylistCollection::PlaylistCollection(QWidgetStack *playlistStack) :
+ m_playlistStack(playlistStack),
+ m_historyPlaylist(0),
+ m_upcomingPlaylist(0),
+ m_importPlaylists(true),
+ m_searchEnabled(true),
+ m_playing(false),
+ m_showMorePlaylist(0),
+ m_belowShowMorePlaylist(0),
+ m_dynamicPlaylist(0),
+ m_belowDistraction(0),
+ m_distraction(0)
+{
+ m_instance = this;
+
+ m_actionHandler = new ActionHandler(this);
+ PlayerManager::instance()->setPlaylistInterface(this);
+
+ // KDirLister's auto error handling seems to crash JuK during startup in
+ // readConfig().
+
+ m_dirLister.setAutoErrorHandlingEnabled(false, playlistStack);
+ readConfig();
+}
+
+PlaylistCollection::~PlaylistCollection()
+{
+ saveConfig();
+ delete m_actionHandler;
+ PlayerManager::instance()->setPlaylistInterface(0);
+ Playlist::setShuttingDown();
+}
+
+QString PlaylistCollection::name() const
+{
+ return currentPlaylist()->name();
+}
+
+FileHandle PlaylistCollection::currentFile() const
+{
+ return currentPlaylist()->currentFile();
+}
+
+int PlaylistCollection::count() const
+{
+ return currentPlaylist()->count();
+}
+
+int PlaylistCollection::time() const
+{
+ return currentPlaylist()->time();
+}
+
+void PlaylistCollection::playFirst()
+{
+ m_playing = true;
+ currentPlaylist()->playFirst();
+ currentChanged();
+}
+
+void PlaylistCollection::playNextAlbum()
+{
+ m_playing = true;
+ currentPlaylist()->playNextAlbum();
+ currentChanged();
+}
+
+void PlaylistCollection::playPrevious()
+{
+ m_playing = true;
+ currentPlaylist()->playPrevious();
+ currentChanged();
+}
+
+void PlaylistCollection::playNext()
+{
+ m_playing = true;
+ currentPlaylist()->playNext();
+ currentChanged();
+}
+
+void PlaylistCollection::stop()
+{
+ m_playing = false;
+ currentPlaylist()->stop();
+ dataChanged();
+}
+
+bool PlaylistCollection::playing() const
+{
+ return m_playing;
+}
+
+QStringList PlaylistCollection::playlists() const
+{
+ QStringList l;
+
+ QObjectList *childList = m_playlistStack->queryList("Playlist");
+ QObject *obj;
+ for(obj = childList->first(); obj; obj = childList->next()) {
+ Playlist *p = static_cast<Playlist *>(obj);
+ l.append(p->name());
+ }
+
+ delete childList;
+ return l;
+}
+
+void PlaylistCollection::createPlaylist(const QString &name)
+{
+ raise(new Playlist(this, name));
+}
+
+void PlaylistCollection::createDynamicPlaylist(const PlaylistList &playlists)
+{
+ if(m_dynamicPlaylist)
+ m_dynamicPlaylist->setPlaylists(playlists);
+ else
+ m_dynamicPlaylist =
+ new DynamicPlaylist(playlists, this, i18n("Dynamic List"), "midi", false, true);
+
+ PlaylistCollection::raise(m_dynamicPlaylist);
+}
+
+void PlaylistCollection::showMore(const QString &artist, const QString &album)
+{
+
+ PlaylistList playlists;
+ PlaylistSearch::ComponentList components;
+
+ if(currentPlaylist() != CollectionList::instance() &&
+ currentPlaylist() != m_showMorePlaylist)
+ {
+ playlists.append(currentPlaylist());
+ }
+
+ playlists.append(CollectionList::instance());
+
+ { // Just setting off the artist stuff in its own block.
+ ColumnList columns;
+ columns.append(PlaylistItem::ArtistColumn);
+ PlaylistSearch::Component c(artist, false, columns,
+ PlaylistSearch::Component::Exact);
+ components.append(c);
+ }
+
+ if(!album.isNull()) {
+ ColumnList columns;
+ columns.append(PlaylistItem::AlbumColumn);
+ PlaylistSearch::Component c(album, false, columns,
+ PlaylistSearch::Component::Exact);
+ components.append(c);
+ }
+
+ PlaylistSearch search(playlists, components, PlaylistSearch::MatchAll);
+
+ if(m_showMorePlaylist)
+ m_showMorePlaylist->setPlaylistSearch(search);
+ else
+ m_showMorePlaylist = new SearchPlaylist(this, search, i18n("Now Playing"), false, true);
+
+ // The call to raise() below will end up clearing m_belowShowMorePlaylist,
+ // so cache the value we want it to have now.
+ Playlist *belowShowMore = visiblePlaylist();
+
+ PlaylistCollection::setupPlaylist(m_showMorePlaylist, QString::null);
+ PlaylistCollection::raise(m_showMorePlaylist);
+
+ m_belowShowMorePlaylist = belowShowMore;
+}
+
+void PlaylistCollection::removeTrack(const QString &playlist, const QStringList &files)
+{
+ Playlist *p = playlistByName(playlist);
+ PlaylistItemList itemList;
+ if(!p)
+ return;
+
+ QStringList::ConstIterator it;
+ for(it = files.begin(); it != files.end(); ++it) {
+ CollectionListItem *item = CollectionList::instance()->lookup(*it);
+
+ if(item) {
+ PlaylistItem *playlistItem = item->itemForPlaylist(p);
+ if(playlistItem)
+ itemList.append(playlistItem);
+ }
+ }
+
+ p->clearItems(itemList);
+}
+
+QString PlaylistCollection::playlist() const
+{
+ return visiblePlaylist() ? visiblePlaylist()->name() : QString::null;
+}
+
+QString PlaylistCollection::playingPlaylist() const
+{
+ return currentPlaylist() && m_playing ? currentPlaylist()->name() : QString::null;
+}
+
+void PlaylistCollection::setPlaylist(const QString &playlist)
+{
+ Playlist *p = playlistByName(playlist);
+ if(p)
+ raise(p);
+}
+
+QStringList PlaylistCollection::playlistTracks(const QString &playlist) const
+{
+ Playlist *p = playlistByName(playlist);
+
+ if(p)
+ return p->files();
+ return QStringList();
+}
+
+QString PlaylistCollection::trackProperty(const QString &file, const QString &property) const
+{
+ CollectionList *l = CollectionList::instance();
+ CollectionListItem *item = l->lookup(file);
+
+ return item ? item->file().property(property) : QString::null;
+}
+
+QPixmap PlaylistCollection::trackCover(const QString &file, const QString &size) const
+{
+ if(size.lower() != "small" && size.lower() != "large")
+ return QPixmap();
+
+ CollectionList *l = CollectionList::instance();
+ CollectionListItem *item = l->lookup(file);
+
+ if(!item)
+ return QPixmap();
+
+ if(size.lower() == "small")
+ return item->file().coverInfo()->pixmap(CoverInfo::Thumbnail);
+ else
+ return item->file().coverInfo()->pixmap(CoverInfo::FullSize);
+}
+
+void PlaylistCollection::open(const QStringList &l)
+{
+ QStringList files = l;
+
+ if(files.isEmpty())
+ files = MediaFiles::openDialog(widget);
+
+ if(files.isEmpty())
+ return;
+
+ bool justPlaylists = true;
+
+ for(QStringList::ConstIterator it = l.begin(); it != l.end() && justPlaylists; ++it)
+ justPlaylists = !MediaFiles::isPlaylistFile(*it);
+
+ if(visiblePlaylist() == CollectionList::instance() || justPlaylists ||
+ KMessageBox::questionYesNo(
+ widget,
+ i18n("Do you want to add these items to the current list or to the collection list?"),
+ QString::null,
+ KGuiItem(i18n("Current")),
+ KGuiItem(i18n("Collection"))) == KMessageBox::No)
+ {
+ CollectionList::instance()->addFiles(files);
+ }
+ else
+ visiblePlaylist()->addFiles(files);
+
+ dataChanged();
+}
+
+void PlaylistCollection::open(const QString &playlist, const QStringList &files)
+{
+ Playlist *p = playlistByName(playlist);
+
+ if(p)
+ p->addFiles(files);
+}
+
+void PlaylistCollection::addFolder()
+{
+ kdDebug(65432) << k_funcinfo << endl;
+ DirectoryList l(m_folderList, m_importPlaylists, widget, "directoryList");
+ DirectoryList::Result result = l.exec();
+
+ if(result.status == QDialog::Accepted) {
+
+ m_dirLister.blockSignals(true);
+
+ const bool reload = m_importPlaylists != result.addPlaylists;
+ m_importPlaylists = result.addPlaylists;
+
+ for(QStringList::Iterator it = result.addedDirs.begin();
+ it != result.addedDirs.end(); it++)
+ {
+ m_dirLister.openURL(*it, true);
+ m_folderList.append(*it);
+ }
+
+ for(QStringList::Iterator it = result.removedDirs.begin();
+ it != result.removedDirs.end(); it++)
+ {
+ m_dirLister.stop(*it);
+ m_folderList.remove(*it);
+ }
+
+ if(reload)
+ open(m_folderList);
+ else if(!result.addedDirs.isEmpty())
+ open(result.addedDirs);
+
+ saveConfig();
+
+ m_dirLister.blockSignals(false);
+ }
+}
+
+void PlaylistCollection::rename()
+{
+ QString old = visiblePlaylist()->name();
+ QString name = playlistNameDialog(i18n("Rename"), old, false);
+
+ m_playlistNames.remove(old);
+
+ if(name.isNull())
+ return;
+
+ visiblePlaylist()->setName(name);
+}
+
+void PlaylistCollection::duplicate()
+{
+ QString name = playlistNameDialog(i18n("Duplicate"), visiblePlaylist()->name());
+ if(name.isNull())
+ return;
+ raise(new Playlist(this, visiblePlaylist()->items(), name));
+}
+
+void PlaylistCollection::save()
+{
+ visiblePlaylist()->save();
+}
+
+void PlaylistCollection::saveAs()
+{
+ visiblePlaylist()->saveAs();
+}
+
+void PlaylistCollection::reload()
+{
+ if(visiblePlaylist() == CollectionList::instance())
+ CollectionList::instance()->addFiles(m_folderList);
+ else
+ visiblePlaylist()->slotReload();
+
+}
+
+void PlaylistCollection::editSearch()
+{
+ SearchPlaylist *p = dynamic_cast<SearchPlaylist *>(visiblePlaylist());
+
+ if(!p)
+ return;
+
+ AdvancedSearchDialog::Result r =
+ AdvancedSearchDialog(p->name(), p->playlistSearch(), widget).exec();
+
+ if(r.result == AdvancedSearchDialog::Accepted) {
+ p->setPlaylistSearch(r.search);
+ p->setName(r.playlistName);
+ }
+}
+
+void PlaylistCollection::removeItems()
+{
+ visiblePlaylist()->slotRemoveSelectedItems();
+}
+
+void PlaylistCollection::refreshItems()
+{
+ visiblePlaylist()->slotRefresh();
+}
+
+void PlaylistCollection::renameItems()
+{
+ visiblePlaylist()->slotRenameFile();
+}
+
+void PlaylistCollection::addCovers(bool fromFile)
+{
+ visiblePlaylist()->slotAddCover(fromFile);
+ dataChanged();
+}
+
+void PlaylistCollection::removeCovers()
+{
+ visiblePlaylist()->slotRemoveCover();
+ dataChanged();
+}
+
+void PlaylistCollection::viewCovers()
+{
+ visiblePlaylist()->slotViewCover();
+}
+
+void PlaylistCollection::showCoverManager()
+{
+ visiblePlaylist()->slotShowCoverManager();
+}
+
+PlaylistItemList PlaylistCollection::selectedItems()
+{
+ return visiblePlaylist()->selectedItems();
+}
+
+void PlaylistCollection::scanFolders()
+{
+ CollectionList::instance()->addFiles(m_folderList);
+
+ if(CollectionList::instance()->count() == 0)
+ addFolder();
+}
+
+void PlaylistCollection::createPlaylist()
+{
+ QString name = playlistNameDialog();
+ if(!name.isNull())
+ raise(new Playlist(this, name));
+}
+
+void PlaylistCollection::createSearchPlaylist()
+{
+ QString name = uniquePlaylistName(i18n("Search Playlist"));
+
+ AdvancedSearchDialog::Result r =
+ AdvancedSearchDialog(name, PlaylistSearch(), widget).exec();
+
+ if(r.result == AdvancedSearchDialog::Accepted)
+ raise(new SearchPlaylist(this, r.search, r.playlistName));
+}
+
+void PlaylistCollection::createFolderPlaylist()
+{
+ QString folder = KFileDialog::getExistingDirectory();
+
+ if(folder.isEmpty())
+ return;
+
+ QString name = uniquePlaylistName(folder.mid(folder.findRev('/') + 1));
+ name = playlistNameDialog(i18n("Create Folder Playlist"), name);
+
+ if(!name.isNull())
+ raise(new FolderPlaylist(this, folder, name));
+}
+
+void PlaylistCollection::guessTagFromFile()
+{
+ visiblePlaylist()->slotGuessTagInfo(TagGuesser::FileName);
+}
+
+void PlaylistCollection::guessTagFromInternet()
+{
+ visiblePlaylist()->slotGuessTagInfo(TagGuesser::MusicBrainz);
+}
+
+void PlaylistCollection::setSearchEnabled(bool enable)
+{
+ if(enable == m_searchEnabled)
+ return;
+
+ m_searchEnabled = enable;
+
+ visiblePlaylist()->setSearchEnabled(enable);
+}
+
+HistoryPlaylist *PlaylistCollection::historyPlaylist() const
+{
+ return m_historyPlaylist;
+}
+
+void PlaylistCollection::setHistoryPlaylistEnabled(bool enable)
+{
+ if((enable && m_historyPlaylist) || (!enable && !m_historyPlaylist))
+ return;
+
+ if(enable) {
+ action<KToggleAction>("showHistory")->setChecked(true);
+ m_historyPlaylist = new HistoryPlaylist(this);
+ m_historyPlaylist->setName(i18n("History"));
+ setupPlaylist(m_historyPlaylist, "history");
+ }
+ else {
+ delete m_historyPlaylist;
+ m_historyPlaylist = 0;
+ }
+}
+
+UpcomingPlaylist *PlaylistCollection::upcomingPlaylist() const
+{
+ return m_upcomingPlaylist;
+}
+
+void PlaylistCollection::setUpcomingPlaylistEnabled(bool enable)
+{
+ if((enable && m_upcomingPlaylist) || (!enable && !m_upcomingPlaylist))
+ return;
+
+ if(enable) {
+ action<KToggleAction>("showUpcoming")->setChecked(true);
+ if(!m_upcomingPlaylist)
+ m_upcomingPlaylist = new UpcomingPlaylist(this);
+
+ setupPlaylist(m_upcomingPlaylist, "today");
+ }
+ else {
+ action<KToggleAction>("showUpcoming")->setChecked(false);
+ bool raiseCollection = m_playlistStack->visibleWidget() == m_upcomingPlaylist;
+ delete m_upcomingPlaylist;
+ m_upcomingPlaylist = 0;
+
+ if(raiseCollection) {
+ kapp->processEvents(); // Seems to stop a crash, weird.
+ raise(CollectionList::instance());
+ }
+ }
+}
+
+QObject *PlaylistCollection::object() const
+{
+ return m_actionHandler;
+}
+
+Playlist *PlaylistCollection::currentPlaylist() const
+{
+ if(m_belowDistraction)
+ return m_belowDistraction;
+
+ if(m_upcomingPlaylist && m_upcomingPlaylist->active())
+ return m_upcomingPlaylist;
+
+ if(Playlist::playingItem())
+ return Playlist::playingItem()->playlist();
+ else
+ return visiblePlaylist();
+}
+
+Playlist *PlaylistCollection::visiblePlaylist() const
+{
+ return static_cast<Playlist *>(m_playlistStack->visibleWidget());
+}
+
+void PlaylistCollection::raise(Playlist *playlist)
+{
+ if(m_showMorePlaylist && currentPlaylist() == m_showMorePlaylist)
+ m_showMorePlaylist->lower(playlist);
+ if(m_dynamicPlaylist && currentPlaylist() == m_dynamicPlaylist)
+ m_dynamicPlaylist->lower(playlist);
+
+ TrackSequenceManager::instance()->setCurrentPlaylist(playlist);
+ playlist->applySharedSettings();
+ playlist->setSearchEnabled(m_searchEnabled);
+ m_playlistStack->raiseWidget(playlist);
+ clearShowMore(false);
+ dataChanged();
+}
+
+void PlaylistCollection::raiseDistraction()
+{
+ if(m_belowDistraction)
+ return;
+
+ m_belowDistraction = currentPlaylist();
+
+ if(!m_distraction) {
+ m_distraction = new QHBox(m_playlistStack);
+ m_playlistStack->addWidget(m_distraction);
+ }
+
+ m_playlistStack->raiseWidget(m_distraction);
+}
+
+void PlaylistCollection::lowerDistraction()
+{
+ if(!m_distraction)
+ return;
+
+ if(m_belowDistraction)
+ m_playlistStack->raiseWidget(m_belowDistraction);
+
+ m_belowDistraction = 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected methods
+////////////////////////////////////////////////////////////////////////////////
+
+QWidgetStack *PlaylistCollection::playlistStack() const
+{
+ return m_playlistStack;
+}
+
+void PlaylistCollection::setupPlaylist(Playlist *playlist, const QString &)
+{
+ if(!playlist->fileName().isNull())
+ m_playlistFiles.insert(playlist->fileName());
+
+ if(!playlist->name().isNull())
+ m_playlistNames.insert(playlist->name());
+
+ QObject::connect(playlist, SIGNAL(selectionChanged()),
+ object(), SIGNAL(signalSelectedItemsChanged()));
+}
+
+bool PlaylistCollection::importPlaylists() const
+{
+ return m_importPlaylists;
+}
+
+bool PlaylistCollection::containsPlaylistFile(const QString &file) const
+{
+ return m_playlistFiles.contains(file);
+}
+
+bool PlaylistCollection::showMoreActive() const
+{
+ return visiblePlaylist() == m_showMorePlaylist;
+}
+
+void PlaylistCollection::clearShowMore(bool raisePlaylist)
+{
+ if(!m_showMorePlaylist)
+ return;
+
+ if(raisePlaylist) {
+ if(m_belowShowMorePlaylist)
+ raise(m_belowShowMorePlaylist);
+ else
+ raise(CollectionList::instance());
+ }
+
+ m_belowShowMorePlaylist = 0;
+}
+
+void PlaylistCollection::enableDirWatch(bool enable)
+{
+ QObject *collection = CollectionList::instance();
+
+ m_dirLister.disconnect(object());
+ if(enable) {
+ QObject::connect(&m_dirLister, SIGNAL(newItems(const KFileItemList &)),
+ object(), SLOT(slotNewItems(const KFileItemList &)));
+ QObject::connect(&m_dirLister, SIGNAL(refreshItems(const KFileItemList &)),
+ collection, SLOT(slotRefreshItems(const KFileItemList &)));
+ QObject::connect(&m_dirLister, SIGNAL(deleteItem(KFileItem *)),
+ collection, SLOT(slotDeleteItem(KFileItem *)));
+ }
+}
+
+QString PlaylistCollection::playlistNameDialog(const QString &caption,
+ const QString &suggest,
+ bool forceUnique) const
+{
+ bool ok;
+
+ QString name = KInputDialog::getText(
+ caption,
+ i18n("Please enter a name for this playlist:"),
+ forceUnique ? uniquePlaylistName(suggest) : suggest,
+ &ok);
+
+ return ok ? uniquePlaylistName(name) : QString::null;
+}
+
+
+QString PlaylistCollection::uniquePlaylistName(const QString &suggest) const
+{
+ if(suggest.isEmpty())
+ return uniquePlaylistName();
+
+ if(!m_playlistNames.contains(suggest))
+ return suggest;
+
+ QString base = suggest;
+ base.remove(QRegExp("\\s\\([0-9]+\\)$"));
+
+ int count = 1;
+ QString s = QString("%1 (%2)").arg(base).arg(count);
+
+ while(m_playlistNames.contains(s)) {
+ count++;
+ s = QString("%1 (%2)").arg(base).arg(count);
+ }
+
+ return s;
+}
+
+void PlaylistCollection::addNameToDict(const QString &name)
+{
+ m_playlistNames.insert(name);
+}
+
+void PlaylistCollection::addFileToDict(const QString &file)
+{
+ m_playlistFiles.insert(file);
+}
+
+void PlaylistCollection::removeNameFromDict(const QString &name)
+{
+ m_playlistNames.remove(name);
+}
+
+void PlaylistCollection::removeFileFromDict(const QString &file)
+{
+ m_playlistFiles.remove(file);
+}
+
+void PlaylistCollection::dirChanged(const QString &path)
+{
+ CollectionList::instance()->addFiles(path);
+}
+
+Playlist *PlaylistCollection::playlistByName(const QString &name) const
+{
+ QObjectList *l = m_playlistStack->queryList("Playlist");
+ Playlist *list = 0;
+ QObject *obj;
+
+ for(obj = l->first(); obj; obj = l->next()) {
+ Playlist *p = static_cast<Playlist*>(obj);
+ if(p->name() == name) {
+ list = p;
+ break;
+ }
+ }
+
+ delete l;
+ return list;
+}
+
+void PlaylistCollection::newItems(const KFileItemList &list) const
+{
+ CollectionList::instance()->slotNewItems(list);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private methods
+////////////////////////////////////////////////////////////////////////////////
+
+void PlaylistCollection::readConfig()
+{
+ KConfigGroup config(KGlobal::config(), "Playlists");
+
+ m_importPlaylists = config.readBoolEntry("ImportPlaylists", true);
+ m_folderList = config.readPathListEntry("DirectoryList");
+
+ for(QStringList::ConstIterator it = m_folderList.begin(); it != m_folderList.end(); ++it)
+ m_dirLister.openURL(*it, true);
+}
+
+void PlaylistCollection::saveConfig()
+{
+ KConfigGroup config(KGlobal::config(), "Playlists");
+ config.writeEntry("ImportPlaylists", m_importPlaylists);
+ config.writeEntry("showUpcoming", action<KToggleAction>("showUpcoming")->isChecked());
+ config.writePathEntry("DirectoryList", m_folderList);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ActionHanlder implementation
+////////////////////////////////////////////////////////////////////////////////
+
+PlaylistCollection::ActionHandler::ActionHandler(PlaylistCollection *collection) :
+ QObject(0, "ActionHandler"),
+ m_collection(collection)
+{
+ KActionMenu *menu;
+
+ // "New" menu
+
+ menu = new KActionMenu(i18n("&New"), "filenew", actions(), "file_new");
+
+ menu->insert(createAction(i18n("&Empty Playlist..."), SLOT(slotCreatePlaylist()),
+ "newPlaylist", "window_new", "CTRL+n"));
+ menu->insert(createAction(i18n("&Search Playlist..."), SLOT(slotCreateSearchPlaylist()),
+ "newSearchPlaylist", "find", "CTRL+f"));
+ menu->insert(createAction(i18n("Playlist From &Folder..."), SLOT(slotCreateFolderPlaylist()),
+ "newDirectoryPlaylist", "fileopen", "CTRL+d"));
+
+ // Guess tag info menu
+
+#if HAVE_MUSICBRAINZ
+ menu = new KActionMenu(i18n("&Guess Tag Information"), QString::null, actions(), "guessTag");
+ menu->setIconSet(SmallIconSet("wizard"));
+
+ menu->insert(createAction(i18n("From &File Name"), SLOT(slotGuessTagFromFile()),
+ "guessTagFile", "fileimport", "CTRL+g"));
+ menu->insert(createAction(i18n("From &Internet"), SLOT(slotGuessTagFromInternet()),
+ "guessTagInternet", "connect_established", "CTRL+i"));
+#else
+ createAction(i18n("Guess Tag Information From &File Name"), SLOT(slotGuessTagFromFile()),
+ "guessTag", "fileimport", "CTRL+f");
+#endif
+
+
+ createAction(i18n("Play First Track"),SLOT(slotPlayFirst()), "playFirst");
+ createAction(i18n("Play Next Album"), SLOT(slotPlayNextAlbum()), "forwardAlbum", "next");
+
+ createAction(i18n("Open..."), SLOT(slotOpen()), "file_open", "fileopen", "CTRL+o");
+ createAction(i18n("Add &Folder..."), SLOT(slotAddFolder()), "openDirectory", "fileopen");
+ createAction(i18n("&Rename..."), SLOT(slotRename()), "renamePlaylist", "lineedit");
+ createAction(i18n("D&uplicate..."), SLOT(slotDuplicate()), "duplicatePlaylist", "editcopy");
+ createAction(i18n("Save"), SLOT(slotSave()), "file_save", "filesave", "CTRL+s");
+ createAction(i18n("Save As..."), SLOT(slotSaveAs()), "file_save_as", "filesaveas");
+ createAction(i18n("R&emove"), SLOT(slotRemove()), "deleteItemPlaylist", "edittrash");
+ createAction(i18n("Reload"), SLOT(slotReload()), "reloadPlaylist", "reload");
+ createAction(i18n("Edit Search..."), SLOT(slotEditSearch()), "editSearch", "editclear");
+
+ createAction(i18n("&Delete"), SLOT(slotRemoveItems()), "removeItem", "editdelete");
+ createAction(i18n("Refresh"), SLOT(slotRefreshItems()), "refresh", "reload");
+ createAction(i18n("&Rename File"), SLOT(slotRenameItems()), "renameFile", "filesaveas", "CTRL+r");
+
+ menu = new KActionMenu(i18n("Cover Manager"), QString::null, actions(), "coverManager");
+ menu->setIconSet(SmallIconSet("image"));
+ menu->insert(createAction(i18n("&View Cover"),
+ SLOT(slotViewCovers()), "viewCover", "viewmag"));
+ menu->insert(createAction(i18n("Get Cover From &File..."),
+ SLOT(slotAddLocalCover()), "addCover", "fileimport", "CTRL+SHIFT+f"));
+
+ // Do not rename googleCover for backward compatibility
+ menu->insert(createAction(i18n("Get Cover From &Internet..."),
+ SLOT(slotAddInternetCover()), "googleCover", "connect_established", "CTRL+SHIFT+g"));
+ menu->insert(createAction(i18n("&Delete Cover"),
+ SLOT(slotRemoveCovers()), "removeCover", "editdelete"));
+ menu->insert(createAction(i18n("Show Cover &Manager"),
+ SLOT(slotShowCoverManager()), "showCoverManager"));
+
+ KToggleAction *historyAction =
+ new KToggleAction(i18n("Show &History"), "history", 0, actions(), "showHistory");
+ historyAction->setCheckedState(i18n("Hide &History"));
+
+ KToggleAction *upcomingAction =
+ new KToggleAction(i18n("Show &Play Queue"), "today", 0, actions(), "showUpcoming");
+ upcomingAction->setCheckedState(i18n("Hide &Play Queue"));
+
+ connect(action<KToggleAction>("showHistory"), SIGNAL(toggled(bool)),
+ this, SLOT(slotSetHistoryPlaylistEnabled(bool)));
+ connect(action<KToggleAction>("showUpcoming"), SIGNAL(toggled(bool)),
+ this, SLOT(slotSetUpcomingPlaylistEnabled(bool)));
+}
+
+KAction *PlaylistCollection::ActionHandler::createAction(const QString &text,
+ const char *slot,
+ const char *name,
+ const QString &icon,
+ const KShortcut &shortcut)
+{
+ if(icon.isNull())
+ return new KAction(text, shortcut, this, slot, actions(), name);
+ else
+ return new KAction(text, icon, shortcut, this, slot, actions(), name);
+}
+
+#undef widget
+#include "playlistcollection.moc"
+
+// vim: set et sw=4:
diff --git a/juk/playlistcollection.h b/juk/playlistcollection.h
new file mode 100644
index 00000000..1ee4dea2
--- /dev/null
+++ b/juk/playlistcollection.h
@@ -0,0 +1,270 @@
+/***************************************************************************
+ copyright : (C) 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef PLAYLIST_COLLECTION_H
+#define PLAYLIST_COLLECTION_H
+
+#include "playlistinterface.h"
+#include "stringhash.h"
+#include "jukIface.h"
+
+#include <kshortcut.h>
+#include <klocale.h>
+#include <kdirlister.h>
+
+#include <qguardedptr.h>
+
+class QWidgetStack;
+class KAction;
+class Playlist;
+class PlaylistItem;
+class HistoryPlaylist;
+class UpcomingPlaylist;
+class SearchPlaylist;
+class DynamicPlaylist;
+
+typedef QValueList<Playlist *> PlaylistList;
+typedef QValueList<PlaylistItem *> PlaylistItemList;
+
+class PlaylistCollection : public PlaylistInterface, CollectionIface
+{
+ friend class Playlist;
+ friend class CollectionList;
+ friend class DynamicPlaylist;
+
+ static PlaylistCollection *m_instance;
+
+public:
+ PlaylistCollection(QWidgetStack *playlistStack);
+ virtual ~PlaylistCollection();
+
+ static PlaylistCollection *instance() { return m_instance; }
+
+ virtual QString name() const;
+ virtual FileHandle currentFile() const;
+ virtual int count() const;
+ virtual int time() const;
+ virtual void playNext();
+ virtual void playPrevious();
+ virtual void stop();
+ virtual bool playing() const;
+
+ void playFirst();
+ void playNextAlbum();
+
+ virtual QStringList playlists() const;
+ virtual void createPlaylist(const QString &name);
+ virtual void createDynamicPlaylist(const PlaylistList &playlists);
+ virtual void showMore(const QString &artist, const QString &album = QString::null);
+ virtual void removeTrack(const QString &playlist, const QStringList &files);
+
+ virtual QString playlist() const;
+ virtual QString playingPlaylist() const;
+ virtual void setPlaylist(const QString &playlist);
+
+ virtual QStringList playlistTracks(const QString &playlist) const;
+ virtual QString trackProperty(const QString &file, const QString &property) const;
+ virtual QPixmap trackCover(const QString &file, const QString &size = "Small") const;
+
+ virtual void open(const QStringList &files = QStringList());
+ virtual void open(const QString &playlist, const QStringList &files);
+ virtual void addFolder();
+ virtual void rename();
+ virtual void duplicate();
+ virtual void save();
+ virtual void saveAs();
+ virtual void remove() = 0;
+ virtual void reload();
+ virtual void editSearch();
+ virtual void setDynamicListsFrozen(bool) = 0;
+
+ bool showMoreActive() const;
+ void clearShowMore(bool raise = true);
+ void enableDirWatch(bool enable);
+
+ void removeItems();
+ void refreshItems();
+ void renameItems();
+ void addCovers(bool fromFile);
+ void removeCovers();
+ void viewCovers();
+ void showCoverManager();
+
+ virtual PlaylistItemList selectedItems();
+
+ void scanFolders();
+
+ void createPlaylist();
+ void createSearchPlaylist();
+ void createFolderPlaylist();
+
+ void guessTagFromFile();
+ void guessTagFromInternet();
+
+ void setSearchEnabled(bool enable);
+
+ HistoryPlaylist *historyPlaylist() const;
+ void setHistoryPlaylistEnabled(bool enable);
+
+ UpcomingPlaylist *upcomingPlaylist() const;
+ void setUpcomingPlaylistEnabled(bool enable);
+
+ void dirChanged(const QString &path);
+
+ /**
+ * Returns a pointer to the action handler.
+ */
+ QObject *object() const;
+
+ void newItems(const KFileItemList &list) const;
+
+ /**
+ * This is the current playlist in all things relating to the player. It
+ * represents the playlist that either should be played from or is currently
+ * playing.
+ */
+ virtual Playlist *currentPlaylist() const;
+
+ /**
+ * This is the currently visible playlist and should be used for all user
+ * interaction elements.
+ */
+ virtual Playlist *visiblePlaylist() const;
+
+ /**
+ * Makes \a playlist the currently visible playlist.
+ */
+ virtual void raise(Playlist *playlist);
+
+ /**
+ * This is used to put up a temporary widget over the top of the playlist
+ * stack. This is part of a trick to significantly speed up painting by
+ * hiding the playlist to which items are being added.
+ */
+ void raiseDistraction();
+ void lowerDistraction();
+
+ class ActionHandler;
+
+protected:
+ virtual QWidgetStack *playlistStack() const;
+ virtual void setupPlaylist(Playlist *playlist, const QString &iconName);
+ virtual void removePlaylist(Playlist *playlist) = 0;
+
+ bool importPlaylists() const;
+ bool containsPlaylistFile(const QString &file) const;
+
+ QString playlistNameDialog(const QString &caption = i18n("Create New Playlist"),
+ const QString &suggest = QString::null,
+ bool forceUnique = true) const;
+ QString uniquePlaylistName(const QString &suggest = i18n("Playlist")) const;
+
+ void addNameToDict(const QString &name);
+ void addFileToDict(const QString &file);
+ void removeNameFromDict(const QString &name);
+ void removeFileFromDict(const QString &file);
+
+ Playlist *playlistByName(const QString &name) const;
+
+private:
+ void readConfig();
+ void saveConfig();
+
+ QWidgetStack *m_playlistStack;
+ HistoryPlaylist *m_historyPlaylist;
+ UpcomingPlaylist *m_upcomingPlaylist;
+ ActionHandler *m_actionHandler;
+
+ KDirLister m_dirLister;
+ StringHash m_playlistNames;
+ StringHash m_playlistFiles;
+ QStringList m_folderList;
+ bool m_importPlaylists;
+ bool m_searchEnabled;
+ bool m_playing;
+
+ QGuardedPtr<SearchPlaylist> m_showMorePlaylist;
+ QGuardedPtr<Playlist> m_belowShowMorePlaylist;
+ QGuardedPtr<DynamicPlaylist> m_dynamicPlaylist;
+ QGuardedPtr<Playlist> m_belowDistraction;
+
+ QWidget *m_distraction;
+};
+
+/**
+ * This class is just used as a proxy to handle the signals coming from action
+ * activations without requiring PlaylistCollection to be a QObject.
+ */
+
+class PlaylistCollection::ActionHandler : public QObject
+{
+ Q_OBJECT
+public:
+ ActionHandler(PlaylistCollection *collection);
+
+private:
+ KAction *createAction(const QString &text,
+ const char *slot,
+ const char *name,
+ const QString &icon = QString::null,
+ const KShortcut &shortcut = KShortcut());
+private slots:
+ void slotPlayFirst() { m_collection->playFirst(); }
+ void slotPlayNextAlbum() { m_collection->playNextAlbum(); }
+
+ void slotOpen() { m_collection->open(); }
+ void slotAddFolder() { m_collection->addFolder(); }
+ void slotRename() { m_collection->rename(); }
+ void slotDuplicate() { m_collection->duplicate(); }
+ void slotSave() { m_collection->save(); }
+ void slotSaveAs() { m_collection->saveAs(); }
+ void slotReload() { m_collection->reload(); }
+ void slotRemove() { m_collection->remove(); }
+ void slotEditSearch() { m_collection->editSearch(); }
+
+ void slotRemoveItems() { m_collection->removeItems(); }
+ void slotRefreshItems() { m_collection->refreshItems(); }
+ void slotRenameItems() { m_collection->renameItems(); }
+ void slotScanFolders() { m_collection->scanFolders(); }
+
+ void slotViewCovers() { m_collection->viewCovers(); }
+ void slotRemoveCovers() { m_collection->removeCovers(); }
+ void slotAddLocalCover() { m_collection->addCovers(true); }
+ void slotAddInternetCover() { m_collection->addCovers(false); }
+
+ void slotCreatePlaylist() { m_collection->createPlaylist(); }
+ void slotCreateSearchPlaylist() { m_collection->createSearchPlaylist(); }
+ void slotCreateFolderPlaylist() { m_collection->createFolderPlaylist(); }
+
+ void slotGuessTagFromFile() { m_collection->guessTagFromFile(); }
+ void slotGuessTagFromInternet() { m_collection->guessTagFromInternet(); }
+
+ void slotSetSearchEnabled(bool enable) { m_collection->setSearchEnabled(enable); }
+ void slotSetHistoryPlaylistEnabled(bool enable) { m_collection->setHistoryPlaylistEnabled(enable); }
+ void slotSetUpcomingPlaylistEnabled(bool enable) { m_collection->setUpcomingPlaylistEnabled(enable); }
+ void slotShowCoverManager() { m_collection->showCoverManager(); }
+ void slotEnableDirWatch(bool enable) { m_collection->enableDirWatch(enable); }
+ void slotDirChanged(const QString &path) { m_collection->dirChanged(path); }
+
+ void slotNewItems(const KFileItemList &list) { m_collection->newItems(list); }
+
+signals:
+ void signalSelectedItemsChanged();
+ void signalCountChanged();
+
+private:
+ PlaylistCollection *m_collection;
+};
+
+#endif
diff --git a/juk/playlistexporter.h b/juk/playlistexporter.h
new file mode 100644
index 00000000..291648f5
--- /dev/null
+++ b/juk/playlistexporter.h
@@ -0,0 +1,49 @@
+/***************************************************************************
+ begin : Tue Jun 1 2004
+ copyright : (C) 2004 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef PLAYLISTEXPORTER_H
+#define PLAYLISTEXPORTER_H
+
+
+
+class KAction;
+class KActionCollection;
+
+/**
+ * Abstract base class to define an interface for classes that export
+ * PlaylistItem data.
+ *
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ * @see K3bExporter
+ */
+class PlaylistExporter : public QObject
+{
+public:
+ PlaylistExporter(QWidget *parent = 0) : QObject(parent) { }
+ virtual ~PlaylistExporter() { }
+
+ /**
+ * Returns a KAction that can be used to invoke the export.
+ * Returns 0 if it is not possible.
+ *
+ * @return pointer to a KAction that can invoke the export, or 0 on
+ * failure.
+ */
+ virtual KAction *action() = 0;
+};
+
+#endif /* PLAYLISTEXPORTER_H */
+
+// vim: set et ts=4 sw=4:
diff --git a/juk/playlistinterface.cpp b/juk/playlistinterface.cpp
new file mode 100644
index 00000000..9ec550a2
--- /dev/null
+++ b/juk/playlistinterface.cpp
@@ -0,0 +1,80 @@
+/***************************************************************************
+ copyright : (C) 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "playlistinterface.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// Watched implementation
+////////////////////////////////////////////////////////////////////////////////
+
+void Watched::currentChanged()
+{
+ for(QValueList<PlaylistObserver *>::ConstIterator it = m_observers.begin();
+ it != m_observers.end();
+ ++it)
+ {
+ (*it)->updateCurrent();
+ }
+}
+
+void Watched::dataChanged()
+{
+ for(QValueList<PlaylistObserver *>::ConstIterator it = m_observers.begin();
+ it != m_observers.end();
+ ++it)
+ {
+ (*it)->updateData();
+ }
+}
+
+void Watched::addObserver(PlaylistObserver *observer)
+{
+ m_observers.append(observer);
+}
+
+void Watched::removeObserver(PlaylistObserver *observer)
+{
+ m_observers.remove(observer);
+}
+
+Watched::~Watched()
+{
+ for(QValueList<PlaylistObserver *>::Iterator it = m_observers.begin();
+ it != m_observers.end();
+ ++it)
+ {
+ (*it)->clearWatched();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlaylistObserver implementation
+////////////////////////////////////////////////////////////////////////////////
+
+PlaylistObserver::~PlaylistObserver()
+{
+ if(m_playlist)
+ m_playlist->removeObserver(this);
+}
+
+PlaylistObserver::PlaylistObserver(PlaylistInterface *playlist) :
+ m_playlist(playlist)
+{
+ playlist->addObserver(this);
+}
+
+const PlaylistInterface *PlaylistObserver::playlist() const
+{
+ return m_playlist;
+}
diff --git a/juk/playlistinterface.h b/juk/playlistinterface.h
new file mode 100644
index 00000000..8251d4f2
--- /dev/null
+++ b/juk/playlistinterface.h
@@ -0,0 +1,102 @@
+/***************************************************************************
+ copyright : (C) 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef PLAYLISTINTERFACE_H
+#define PLAYLISTINTERFACE_H
+
+#include "filehandle.h"
+
+
+class PlaylistObserver;
+
+/**
+ * An interface implemented by PlaylistInterface to make it possible to watch
+ * for changes in the PlaylistInterface. This is a semi-standard observer
+ * pattern from i.e. Design Patterns.
+ */
+
+class Watched
+{
+public:
+ void addObserver(PlaylistObserver *observer);
+ void removeObserver(PlaylistObserver *observer);
+
+ /**
+ * This is triggered when the currently playing item has been changed.
+ */
+ virtual void currentChanged();
+
+ /**
+ * This is triggered when the data in the playlist -- i.e. the tag content
+ * changes.
+ */
+ virtual void dataChanged();
+
+protected:
+ virtual ~Watched();
+
+private:
+ QValueList<PlaylistObserver *> m_observers;
+};
+
+/**
+ * This is a simple interface that should be used by things that implement a
+ * playlist-like API.
+ */
+
+class PlaylistInterface : public Watched
+{
+public:
+ virtual QString name() const = 0;
+ virtual FileHandle currentFile() const = 0;
+ virtual int time() const = 0;
+ virtual int count() const = 0;
+
+ virtual void playNext() = 0;
+ virtual void playPrevious() = 0;
+ virtual void stop() = 0;
+
+ virtual bool playing() const = 0;
+};
+
+class PlaylistObserver
+{
+public:
+ virtual ~PlaylistObserver();
+
+ /**
+ * This method must be implemented in concrete implementations; it should
+ * define what action should be taken in the observer when the currently
+ * playing item changes.
+ */
+ virtual void updateCurrent() = 0;
+
+ /**
+ * This method must be implemented in concrete implementations; it should
+ * define what action should be taken when the data of the PlaylistItems in
+ * the playlist changes.
+ */
+ virtual void updateData() = 0;
+
+ void clearWatched() { m_playlist = 0; }
+
+protected:
+ PlaylistObserver(PlaylistInterface *playlist);
+ const PlaylistInterface *playlist() const;
+
+private:
+ PlaylistInterface *m_playlist;
+};
+
+#endif
diff --git a/juk/playlistitem.cpp b/juk/playlistitem.cpp
new file mode 100644
index 00000000..a86737c0
--- /dev/null
+++ b/juk/playlistitem.cpp
@@ -0,0 +1,464 @@
+/***************************************************************************
+ begin : Sun Feb 17 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <config.h>
+
+#include <kdebug.h>
+#include <kaction.h>
+#include <kiconloader.h>
+
+#include "playlistitem.h"
+#include "collectionlist.h"
+#include "musicbrainzquery.h"
+#include "tag.h"
+#include "actioncollection.h"
+#include "ktrm.h"
+#include "coverinfo.h"
+#include "tagtransactionmanager.h"
+
+PlaylistItemList PlaylistItem::m_playingItems; // static
+
+static void startMusicBrainzQuery(const FileHandle &file)
+{
+#if HAVE_MUSICBRAINZ
+ // This deletes itself when finished.
+ new MusicBrainzLookup(file);
+#else
+ Q_UNUSED(file)
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlaylistItem public methods
+////////////////////////////////////////////////////////////////////////////////
+
+PlaylistItem::~PlaylistItem()
+{
+ // Although this isn't the most efficient way to accomplish the task of
+ // stopping playback when deleting the item being played, it has the
+ // stark advantage of working reliably. I'll tell anyone who tries to
+ // optimize this, the timing issues can be *hard*. -- mpyne
+
+ m_collectionItem->removeChildItem(this);
+
+ if(m_playingItems.find(this) != m_playingItems.end()) {
+ m_playingItems.remove(this);
+ if(m_playingItems.isEmpty())
+ playlist()->setPlaying(0);
+ }
+
+ if(m_watched)
+ Pointer::clear(this);
+}
+
+void PlaylistItem::setFile(const FileHandle &file)
+{
+ m_collectionItem->updateCollectionDict(d->fileHandle.absFilePath(), file.absFilePath());
+ d->fileHandle = file;
+ refresh();
+}
+
+void PlaylistItem::setFile(const QString &file)
+{
+ QString oldPath = d->fileHandle.absFilePath();
+ d->fileHandle.setFile(file);
+ m_collectionItem->updateCollectionDict(oldPath, d->fileHandle.absFilePath());
+ refresh();
+}
+
+FileHandle PlaylistItem::file() const
+{
+ return d->fileHandle;
+}
+
+const QPixmap *PlaylistItem::pixmap(int column) const
+{
+ static QPixmap image(SmallIcon("image"));
+ static QPixmap playing(UserIcon("playing"));
+
+ int offset = playlist()->columnOffset();
+
+ if((column - offset) == CoverColumn && d->fileHandle.coverInfo()->hasCover())
+ return &image;
+
+ if(column == playlist()->leftColumn() &&
+ m_playingItems.contains(const_cast<PlaylistItem *>(this)))
+ return &playing;
+
+ return KListViewItem::pixmap(column);
+}
+
+QString PlaylistItem::text(int column) const
+{
+ if(!d->fileHandle.tag())
+ return QString::null;
+
+ int offset = playlist()->columnOffset();
+
+ switch(column - offset) {
+ case TrackColumn:
+ return d->fileHandle.tag()->title();
+ case ArtistColumn:
+ return d->fileHandle.tag()->artist();
+ case AlbumColumn:
+ return d->fileHandle.tag()->album();
+ case CoverColumn:
+ return QString::null;
+ case TrackNumberColumn:
+ return d->fileHandle.tag()->track() > 0
+ ? QString::number(d->fileHandle.tag()->track())
+ : QString::null;
+ case GenreColumn:
+ return d->fileHandle.tag()->genre();
+ case YearColumn:
+ return d->fileHandle.tag()->year() > 0
+ ? QString::number(d->fileHandle.tag()->year())
+ : QString::null;
+ case LengthColumn:
+ return d->fileHandle.tag()->lengthString();
+ case BitrateColumn:
+ return QString::number(d->fileHandle.tag()->bitrate());
+ case CommentColumn:
+ return d->fileHandle.tag()->comment();
+ case FileNameColumn:
+ return d->fileHandle.fileInfo().fileName();
+ case FullPathColumn:
+ return d->fileHandle.fileInfo().absFilePath();
+ default:
+ return KListViewItem::text(column);
+ }
+}
+
+void PlaylistItem::setText(int column, const QString &text)
+{
+ int offset = playlist()->columnOffset();
+ if(column - offset >= 0 && column + offset <= lastColumn()) {
+ KListViewItem::setText(column, QString::null);
+ return;
+ }
+
+ KListViewItem::setText(column, text);
+ playlist()->slotWeightDirty(column);
+}
+
+void PlaylistItem::setPlaying(bool playing, bool master)
+{
+ m_playingItems.remove(this);
+
+ if(playing) {
+ if(master)
+ m_playingItems.prepend(this);
+ else
+ m_playingItems.append(this);
+ }
+ else {
+
+ // This is a tricky little recursion, but it
+ // in fact does clear the list.
+
+ if(!m_playingItems.isEmpty())
+ m_playingItems.front()->setPlaying(false);
+ }
+
+ listView()->triggerUpdate();
+}
+
+void PlaylistItem::setSelected(bool selected)
+{
+ playlist()->markItemSelected(this, selected);
+ KListViewItem::setSelected(selected);
+}
+
+void PlaylistItem::guessTagInfo(TagGuesser::Type type)
+{
+ switch(type) {
+ case TagGuesser::FileName:
+ {
+ TagGuesser guesser(d->fileHandle.absFilePath());
+ Tag *tag = TagTransactionManager::duplicateTag(d->fileHandle.tag());
+
+ if(!guesser.title().isNull())
+ tag->setTitle(guesser.title());
+ if(!guesser.artist().isNull())
+ tag->setArtist(guesser.artist());
+ if(!guesser.album().isNull())
+ tag->setAlbum(guesser.album());
+ if(!guesser.track().isNull())
+ tag->setTrack(guesser.track().toInt());
+ if(!guesser.comment().isNull())
+ tag->setComment(guesser.comment());
+
+ TagTransactionManager::instance()->changeTagOnItem(this, tag);
+ break;
+ }
+ case TagGuesser::MusicBrainz:
+ startMusicBrainzQuery(d->fileHandle);
+ break;
+ }
+}
+
+Playlist *PlaylistItem::playlist() const
+{
+ return static_cast<Playlist *>(listView());
+}
+
+QValueVector<int> PlaylistItem::cachedWidths() const
+{
+ return d->cachedWidths;
+}
+
+void PlaylistItem::refresh()
+{
+ m_collectionItem->refresh();
+}
+
+void PlaylistItem::refreshFromDisk()
+{
+ d->fileHandle.refresh();
+ refresh();
+}
+
+void PlaylistItem::clear()
+{
+ playlist()->clearItem(this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlaylistItem protected methods
+////////////////////////////////////////////////////////////////////////////////
+
+PlaylistItem::PlaylistItem(CollectionListItem *item, Playlist *parent) :
+ KListViewItem(parent),
+ d(0),
+ m_watched(0)
+{
+ setup(item);
+}
+
+PlaylistItem::PlaylistItem(CollectionListItem *item, Playlist *parent, QListViewItem *after) :
+ KListViewItem(parent, after),
+ d(0),
+ m_watched(0)
+{
+ setup(item);
+}
+
+
+// This constructor should only be used by the CollectionList subclass.
+
+PlaylistItem::PlaylistItem(CollectionList *parent) :
+ KListViewItem(parent),
+ m_watched(0)
+{
+ d = new Data;
+ m_collectionItem = static_cast<CollectionListItem *>(this);
+ setDragEnabled(true);
+}
+
+void PlaylistItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align)
+{
+ if(!m_playingItems.contains(this))
+ return KListViewItem::paintCell(p, cg, column, width, align);
+
+ QColorGroup colorGroup = cg;
+
+ QColor base = colorGroup.base();
+ QColor selection = colorGroup.highlight();
+
+ int r = (base.red() + selection.red()) / 2;
+ int b = (base.blue() + selection.blue()) / 2;
+ int g = (base.green() + selection.green()) / 2;
+
+ QColor c(r, g, b);
+
+ colorGroup.setColor(QColorGroup::Base, c);
+ QListViewItem::paintCell(p, colorGroup, column, width, align);
+}
+
+int PlaylistItem::compare(QListViewItem *item, int column, bool ascending) const
+{
+ // reimplemented from QListViewItem
+
+ int offset = playlist()->columnOffset();
+
+ if(!item)
+ return 0;
+
+ PlaylistItem *playlistItem = static_cast<PlaylistItem *>(item);
+
+ // The following statments first check to see if you can sort based on the
+ // specified column. If the values for the two PlaylistItems are the same
+ // in that column it then trys to sort based on columns 1, 2, 3 and 0,
+ // (artist, album, track number, track name) in that order.
+
+ int c = compare(this, playlistItem, column, ascending);
+
+ if(c != 0)
+ return c;
+ else {
+ // Loop through the columns doing comparisons until something is differnt.
+ // If all else is the same, compare the track name.
+
+ int last = playlist()->isColumnVisible(AlbumColumn + offset) ? TrackNumberColumn : ArtistColumn;
+
+ for(int i = ArtistColumn; i <= last; i++) {
+ if(playlist()->isColumnVisible(i + offset)) {
+ c = compare(this, playlistItem, i, ascending);
+ if(c != 0)
+ return c;
+ }
+ }
+ return compare(this, playlistItem, TrackColumn + offset, ascending);
+ }
+}
+
+int PlaylistItem::compare(const PlaylistItem *firstItem, const PlaylistItem *secondItem, int column, bool) const
+{
+ int offset = playlist()->columnOffset();
+
+ if(column < 0 || column > lastColumn() + offset)
+ return 0;
+
+ if(column < offset) {
+ QString first = firstItem->text(column).lower();
+ QString second = secondItem->text(column).lower();
+ return first.localeAwareCompare(second);
+ }
+
+ switch(column - offset) {
+ case TrackNumberColumn:
+ if(firstItem->d->fileHandle.tag()->track() > secondItem->d->fileHandle.tag()->track())
+ return 1;
+ else if(firstItem->d->fileHandle.tag()->track() < secondItem->d->fileHandle.tag()->track())
+ return -1;
+ else
+ return 0;
+ break;
+ case LengthColumn:
+ if(firstItem->d->fileHandle.tag()->seconds() > secondItem->d->fileHandle.tag()->seconds())
+ return 1;
+ else if(firstItem->d->fileHandle.tag()->seconds() < secondItem->d->fileHandle.tag()->seconds())
+ return -1;
+ else
+ return 0;
+ break;
+ case BitrateColumn:
+ if(firstItem->d->fileHandle.tag()->bitrate() > secondItem->d->fileHandle.tag()->bitrate())
+ return 1;
+ else if(firstItem->d->fileHandle.tag()->bitrate() < secondItem->d->fileHandle.tag()->bitrate())
+ return -1;
+ else
+ return 0;
+ break;
+ case CoverColumn:
+ if(firstItem->d->fileHandle.coverInfo()->hasCover() == secondItem->d->fileHandle.coverInfo()->hasCover())
+ return 0;
+ else if (firstItem->d->fileHandle.coverInfo()->hasCover())
+ return -1;
+ else
+ return 1;
+ break;
+ default:
+ return strcoll(firstItem->d->local8Bit[column - offset],
+ secondItem->d->local8Bit[column - offset]);
+ }
+}
+
+bool PlaylistItem::isValid() const
+{
+ return bool(d->fileHandle.tag());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlaylistItem private methods
+////////////////////////////////////////////////////////////////////////////////
+
+void PlaylistItem::setup(CollectionListItem *item)
+{
+ m_collectionItem = item;
+
+ d = item->d;
+ item->addChildItem(this);
+ setDragEnabled(true);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PlaylistItem::Pointer implementation
+////////////////////////////////////////////////////////////////////////////////
+
+QMap<PlaylistItem *, QValueList<PlaylistItem::Pointer *> > PlaylistItem::Pointer::m_map; // static
+
+PlaylistItem::Pointer::Pointer(PlaylistItem *item) :
+ m_item(item)
+{
+ if(!m_item)
+ return;
+
+ m_item->m_watched = true;
+ m_map[m_item].append(this);
+}
+
+PlaylistItem::Pointer::Pointer(const Pointer &p) :
+ m_item(p.m_item)
+{
+ m_map[m_item].append(this);
+}
+
+PlaylistItem::Pointer::~Pointer()
+{
+ if(!m_item)
+ return;
+
+ m_map[m_item].remove(this);
+ if(m_map[m_item].isEmpty()) {
+ m_map.remove(m_item);
+ m_item->m_watched = false;
+ }
+}
+
+PlaylistItem::Pointer &PlaylistItem::Pointer::operator=(PlaylistItem *item)
+{
+ if(item == m_item)
+ return *this;
+
+ if(m_item) {
+ m_map[m_item].remove(this);
+ if(m_map[m_item].isEmpty()) {
+ m_map.remove(m_item);
+ m_item->m_watched = false;
+ }
+ }
+
+ if(item) {
+ m_map[item].append(this);
+ item->m_watched = true;
+ }
+
+ m_item = item;
+
+ return *this;
+}
+
+void PlaylistItem::Pointer::clear(PlaylistItem *item) // static
+{
+ if(!item)
+ return;
+
+ QValueList<Pointer *> l = m_map[item];
+ for(QValueList<Pointer *>::Iterator it = l.begin(); it != l.end(); ++it)
+ (*it)->m_item = 0;
+ m_map.remove(item);
+ item->m_watched = false;
+}
diff --git a/juk/playlistitem.h b/juk/playlistitem.h
new file mode 100644
index 00000000..c969d736
--- /dev/null
+++ b/juk/playlistitem.h
@@ -0,0 +1,213 @@
+/***************************************************************************
+ begin : Sun Feb 17 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef PLAYLISTITEM_H
+#define PLAYLISTITEM_H
+
+#include <klistview.h>
+#include <ksharedptr.h>
+#include <kdebug.h>
+
+#include <qvaluevector.h>
+#include <qptrdict.h>
+
+#include "tagguesser.h"
+#include "filehandle.h"
+
+class Playlist;
+class PlaylistItem;
+class CollectionListItem;
+class CollectionList;
+
+typedef QValueList<PlaylistItem *> PlaylistItemList;
+
+/**
+ * Items for the Playlist and the baseclass for CollectionListItem.
+ * The constructors and destructor are protected and new items should be
+ * created via Playlist::createItem(). Items should be removed by
+ * Playlist::clear(), Playlist::deleteFromDisk(), Playlist::clearItem() or
+ * Playlist::clearItem().
+ */
+
+class PlaylistItem : public KListViewItem
+{
+ friend class Playlist;
+ friend class SearchPlaylist;
+ friend class UpcomingPlaylist;
+ friend class CollectionList;
+ friend class CollectionListItem;
+ friend class QPtrDict<PlaylistItem>;
+ friend class Pointer;
+
+public:
+ enum ColumnType { TrackColumn = 0,
+ ArtistColumn = 1,
+ AlbumColumn = 2,
+ CoverColumn = 3,
+ TrackNumberColumn = 4,
+ GenreColumn = 5,
+ YearColumn = 6,
+ LengthColumn = 7,
+ BitrateColumn = 8,
+ CommentColumn = 9,
+ FileNameColumn = 10,
+ FullPathColumn = 11 };
+
+ /**
+ * A helper class to implement guarded pointer semantics.
+ */
+
+ class Pointer
+ {
+ public:
+ Pointer() : m_item(0) {}
+ Pointer(PlaylistItem *item);
+ Pointer(const Pointer &p);
+ ~Pointer();
+ Pointer &operator=(PlaylistItem *item);
+ bool operator==(const Pointer &p) const { return m_item == p.m_item; }
+ bool operator!=(const Pointer &p) const { return m_item != p.m_item; }
+ PlaylistItem *operator->() const { return m_item; }
+ PlaylistItem &operator*() const { return *m_item; }
+ operator PlaylistItem*() const { return m_item; }
+ static void clear(PlaylistItem *item);
+
+ private:
+ PlaylistItem *m_item;
+ static QMap<PlaylistItem *, QValueList<Pointer *> > m_map;
+ };
+ friend class Pointer;
+
+ static int lastColumn() { return FullPathColumn; }
+
+ void setFile(const FileHandle &file);
+ void setFile(const QString &file);
+ FileHandle file() const;
+
+ virtual const QPixmap *pixmap(int column) const;
+ virtual QString text(int column) const;
+ virtual void setText(int column, const QString &text);
+
+ void setPlaying(bool playing = true, bool master = true);
+
+ virtual void setSelected(bool selected);
+ void guessTagInfo(TagGuesser::Type type);
+
+ Playlist *playlist() const;
+
+ virtual CollectionListItem *collectionItem() const { return m_collectionItem; }
+
+ /**
+ * The widths of items are cached when they're updated for us in computations
+ * in the "weighted" listview column width mode.
+ */
+ QValueVector<int> cachedWidths() const;
+
+ /**
+ * This just refreshes from the in memory data. This may seem pointless at
+ * first, but this data is shared between all of the list view items that are
+ * based on the same file, so if another one of those items changes its data
+ * it is important to refresh the others.
+ */
+ virtual void refresh();
+
+ /**
+ * This rereads the tag from disk. This affects all PlaylistItems based on
+ * the same file.
+ */
+ virtual void refreshFromDisk();
+
+ /**
+ * Asks the item's playlist to remove the item (which uses deleteLater()).
+ */
+ virtual void clear();
+
+ /**
+ * Returns properly casted item below this one.
+ */
+ PlaylistItem *itemBelow() { return static_cast<PlaylistItem *>(KListViewItem::itemBelow()); }
+
+ /**
+ * Returns properly casted item above this one.
+ */
+ PlaylistItem *itemAbove() { return static_cast<PlaylistItem *>(KListViewItem::itemAbove()); }
+
+ /**
+ * Returns a reference to the list of the currnetly playing items, with the
+ * first being the "master" item (i.e. the item from which the next track is
+ * chosen).
+ */
+ static const PlaylistItemList &playingItems() { return m_playingItems; }
+
+protected:
+ /**
+ * Items should always be created using Playlist::createItem() or through a
+ * subclss or friend class.
+ */
+ PlaylistItem(CollectionListItem *item, Playlist *parent);
+ PlaylistItem(CollectionListItem *item, Playlist *parent, QListViewItem *after);
+
+ /**
+ * This is the constructor that shold be used by subclasses.
+ */
+ PlaylistItem(CollectionList *parent);
+
+ /**
+ * See the class documentation for an explanation of construction and deletion
+ * of PlaylistItems.
+ */
+ virtual ~PlaylistItem();
+
+ virtual void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align);
+ virtual void paintFocus(QPainter *, const QColorGroup &, const QRect &) {}
+
+ virtual int compare(QListViewItem *item, int column, bool ascending) const;
+ int compare(const PlaylistItem *firstItem, const PlaylistItem *secondItem, int column, bool ascending) const;
+
+ bool isValid() const;
+
+ struct Data : public KShared
+ {
+ Data() {}
+ Data(const QFileInfo &info, const QString &path) : fileHandle(info, path) {}
+ Data(const QString &path) : fileHandle(path) {}
+
+ FileHandle fileHandle;
+ QValueVector<QCString> local8Bit;
+ QValueVector<int> cachedWidths;
+ };
+
+ KSharedPtr<Data> data() const { return d; }
+
+private:
+ KSharedPtr<Data> d;
+
+ void setup(CollectionListItem *item);
+ CollectionListItem *m_collectionItem;
+ bool m_watched;
+ static PlaylistItemList m_playingItems;
+};
+
+inline kdbgstream &operator<<(kdbgstream &s, const PlaylistItem &item)
+{
+ if(&item == 0)
+ s << "(nil)";
+ else
+ s << item.text(PlaylistItem::TrackColumn);
+
+ return s;
+}
+
+#endif
diff --git a/juk/playlistsearch.cpp b/juk/playlistsearch.cpp
new file mode 100644
index 00000000..a2109b26
--- /dev/null
+++ b/juk/playlistsearch.cpp
@@ -0,0 +1,328 @@
+/***************************************************************************
+ begin : Sun Mar 6 2003
+ copyright : (C) 2003 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kdatastream.h>
+#include <kdebug.h>
+
+#include "playlistsearch.h"
+#include "playlist.h"
+#include "playlistitem.h"
+#include "collectionlist.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+PlaylistSearch::PlaylistSearch() :
+ m_mode(MatchAny)
+{
+
+}
+
+PlaylistSearch::PlaylistSearch(const PlaylistList &playlists,
+ const ComponentList &components,
+ SearchMode mode,
+ bool searchNow) :
+ m_playlists(playlists),
+ m_components(components),
+ m_mode(mode)
+{
+ if(searchNow)
+ search();
+}
+
+void PlaylistSearch::search()
+{
+ m_items.clear();
+ m_matchedItems.clear();
+ m_unmatchedItems.clear();
+
+ // This really isn't as bad as it looks. Despite the four nexted loops
+ // most of the time this will be searching one playlist for one search
+ // component -- possibly for one column.
+
+ // Also there should be some caching of previous searches in here and
+ // allowance for appending and removing chars. If one is added it
+ // should only search the current list. If one is removed it should
+ // pop the previous search results off of a stack.
+
+ PlaylistList::Iterator playlistIt = m_playlists.begin();
+ for(; playlistIt != m_playlists.end(); ++playlistIt) {
+ if(!isEmpty()) {
+ for(QListViewItemIterator it(*playlistIt); it.current(); ++it)
+ checkItem(static_cast<PlaylistItem *>(*it));
+ }
+ else {
+ m_items += (*playlistIt)->items();
+ m_matchedItems += (*playlistIt)->items();
+ }
+ }
+}
+
+bool PlaylistSearch::checkItem(PlaylistItem *item)
+{
+ m_items.append(item);
+
+ // set our default
+ bool match = bool(m_mode);
+
+ ComponentList::Iterator componentIt = m_components.begin();
+ for(; componentIt != m_components.end(); ++componentIt) {
+
+ bool componentMatches = (*componentIt).matches(item);
+
+ if(componentMatches && m_mode == MatchAny) {
+ match = true;
+ break;
+ }
+
+ if(!componentMatches && m_mode == MatchAll) {
+ match = false;
+ break;
+ }
+ }
+
+ if(match)
+ m_matchedItems.append(item);
+ else
+ m_unmatchedItems.append(item);
+
+ return match;
+}
+
+void PlaylistSearch::addComponent(const Component &c)
+{
+ m_components.append(c);
+}
+
+void PlaylistSearch::clearComponents()
+{
+ m_components.clear();
+}
+
+PlaylistSearch::ComponentList PlaylistSearch::components() const
+{
+ return m_components;
+}
+
+bool PlaylistSearch::isNull() const
+{
+ return m_components.isEmpty();
+}
+
+bool PlaylistSearch::isEmpty() const
+{
+ if(isNull())
+ return true;
+
+ ComponentList::ConstIterator it = m_components.begin();
+ for(; it != m_components.end(); ++it) {
+ if(!(*it).query().isEmpty() || !(*it).pattern().isEmpty())
+ return false;
+ }
+
+ return true;
+}
+
+void PlaylistSearch::clearItem(PlaylistItem *item)
+{
+ m_items.remove(item);
+ m_matchedItems.remove(item);
+ m_unmatchedItems.remove(item);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Component public methods
+////////////////////////////////////////////////////////////////////////////////
+
+PlaylistSearch::Component::Component() :
+ m_mode(Contains),
+ m_searchAllVisible(true),
+ m_caseSensitive(false)
+{
+
+}
+
+PlaylistSearch::Component::Component(const QString &query,
+ bool caseSensitive,
+ const ColumnList &columns,
+ MatchMode mode) :
+ m_query(query),
+ m_columns(columns),
+ m_mode(mode),
+ m_searchAllVisible(columns.isEmpty()),
+ m_caseSensitive(caseSensitive),
+ m_re(false)
+{
+
+}
+
+PlaylistSearch::Component::Component(const QRegExp &query, const ColumnList& columns) :
+ m_queryRe(query),
+ m_columns(columns),
+ m_mode(Exact),
+ m_searchAllVisible(columns.isEmpty()),
+ m_caseSensitive(false),
+ m_re(true)
+{
+
+}
+
+bool PlaylistSearch::Component::matches(PlaylistItem *item) const
+{
+ if((m_re && m_queryRe.isEmpty()) || (!m_re && m_query.isEmpty()))
+ return false;
+
+ if(m_columns.isEmpty()) {
+ Playlist *p = static_cast<Playlist *>(item->listView());
+ for(int i = 0; i < p->columns(); i++) {
+ if(p->isColumnVisible(i))
+ m_columns.append(i);
+ }
+ }
+
+
+ for(ColumnList::Iterator it = m_columns.begin(); it != m_columns.end(); ++it) {
+
+ if(m_re) {
+ if(item->text(*it).find(m_queryRe) > -1)
+ return true;
+ else
+ break;
+ }
+
+ switch(m_mode) {
+ case Contains:
+ if(item->text(*it).find(m_query, 0, m_caseSensitive) > -1)
+ return true;
+ break;
+ case Exact:
+ if(item->text(*it).length() == m_query.length()) {
+ if(m_caseSensitive) {
+ if(item->text(*it) == m_query)
+ return true;
+ }
+ else if(item->text(*it).lower() == m_query.lower())
+ return true;
+ }
+ break;
+ case ContainsWord:
+ {
+ QString s = item->text(*it);
+ int i = s.find(m_query, 0, m_caseSensitive);
+
+ if(i >= 0) {
+
+ // If we found the pattern and the lengths are the same, then
+ // this is a match.
+
+ if(s.length() == m_query.length())
+ return true;
+
+ // First: If the match starts at the beginning of the text or the
+ // character before the match is not a word character
+
+ // AND
+
+ // Second: Either the pattern was found at the end of the text,
+ // or the text following the match is a non-word character
+
+ // ...then we have a match
+
+ if((i == 0 || !s.at(i - 1).isLetterOrNumber()) &&
+ (i + m_query.length() == s.length() || !s.at(i + m_query.length()).isLetterOrNumber()))
+ return true;
+ break;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool PlaylistSearch::Component::operator==(const Component &v) const
+{
+ return m_query == v.m_query &&
+ m_queryRe == v.m_queryRe &&
+ m_columns == v.m_columns &&
+ m_mode == v.m_mode &&
+ m_searchAllVisible == v.m_searchAllVisible &&
+ m_caseSensitive == v.m_caseSensitive &&
+ m_re == v.m_re;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// helper functions
+////////////////////////////////////////////////////////////////////////////////
+
+QDataStream &operator<<(QDataStream &s, const PlaylistSearch &search)
+{
+ s << search.components()
+ << Q_INT32(search.searchMode());
+
+ return s;
+}
+
+QDataStream &operator>>(QDataStream &s, PlaylistSearch &search)
+{
+ search.clearPlaylists();
+ search.addPlaylist(CollectionList::instance());
+
+ search.clearComponents();
+ PlaylistSearch::ComponentList components;
+ s >> components;
+ PlaylistSearch::ComponentList::ConstIterator it = components.begin();
+ for(; it != components.end(); ++it)
+ search.addComponent(*it);
+
+ Q_INT32 mode;
+ s >> mode;
+ search.setSearchMode(PlaylistSearch::SearchMode(mode));
+
+ return s;
+}
+
+QDataStream &operator<<(QDataStream &s, const PlaylistSearch::Component &c)
+{
+ s << c.isPatternSearch()
+ << (c.isPatternSearch() ? c.pattern().pattern() : c.query())
+ << c.isCaseSensitive()
+ << c.columns()
+ << Q_INT32(c.matchMode());
+
+ return s;
+}
+
+QDataStream &operator>>(QDataStream &s, PlaylistSearch::Component &c)
+{
+ bool patternSearch;
+ QString pattern;
+ bool caseSensitive;
+ ColumnList columns;
+ Q_INT32 mode;
+
+ s >> patternSearch
+ >> pattern
+ >> caseSensitive
+ >> columns
+ >> mode;
+
+ if(patternSearch)
+ c = PlaylistSearch::Component(QRegExp(pattern), columns);
+ else
+ c = PlaylistSearch::Component(pattern, caseSensitive, columns, PlaylistSearch::Component::MatchMode(mode));
+
+ return s;
+}
diff --git a/juk/playlistsearch.h b/juk/playlistsearch.h
new file mode 100644
index 00000000..9b46a92c
--- /dev/null
+++ b/juk/playlistsearch.h
@@ -0,0 +1,150 @@
+/***************************************************************************
+ begin : Sun Mar 6 2003
+ copyright : (C) 2003 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef PLAYLISTSEARCH_H
+#define PLAYLISTSEARCH_H
+
+#include <qregexp.h>
+
+class Playlist;
+typedef QValueList<Playlist *> PlaylistList;
+
+class PlaylistItem;
+typedef QValueList<PlaylistItem *> PlaylistItemList;
+
+typedef QValueList<int> ColumnList;
+
+class PlaylistSearch
+{
+public:
+ class Component;
+ typedef QValueList<Component> ComponentList;
+
+ enum SearchMode { MatchAny = 0, MatchAll = 1 };
+
+ PlaylistSearch();
+ PlaylistSearch(const PlaylistList &playlists,
+ const ComponentList &components,
+ SearchMode mode = MatchAny,
+ bool searchNow = true);
+
+ void search();
+ bool checkItem(PlaylistItem *item);
+
+ PlaylistItemList searchedItems() const { return m_items; }
+ PlaylistItemList matchedItems() const { return m_matchedItems; }
+ PlaylistItemList unmatchedItems() const { return m_unmatchedItems; }
+
+ void addPlaylist(Playlist *p) { m_playlists.append(p); }
+ void clearPlaylists() { m_playlists.clear(); }
+ PlaylistList playlists() const { return m_playlists; }
+
+ void addComponent(const Component &c);
+ void clearComponents();
+ ComponentList components() const;
+
+ void setSearchMode(SearchMode m) { m_mode = m; }
+ SearchMode searchMode() const { return m_mode; }
+
+ bool isNull() const;
+ bool isEmpty() const;
+
+ /**
+ * This is used to clear an item from the matched and unmatched lists. This
+ * is useful because it can prevent keeping a dangling pointer around without
+ * requiring invalidating the search.
+ */
+ void clearItem(PlaylistItem *item);
+
+private:
+ PlaylistList m_playlists;
+ ComponentList m_components;
+ SearchMode m_mode;
+
+ PlaylistItemList m_items;
+ PlaylistItemList m_matchedItems;
+ PlaylistItemList m_unmatchedItems;
+};
+
+/**
+ * A search is built from several search components. These corespond to to lines
+ * in the search bar.
+ */
+
+class PlaylistSearch::Component
+{
+public:
+ enum MatchMode { Contains = 0, Exact = 1, ContainsWord = 2 };
+
+ /**
+ * Create an empty search component. This is only provided for use by
+ * QValueList and should not be used in any other context.
+ */
+ Component();
+
+ /**
+ * Create a query component. This defaults to searching all visible coulumns.
+ */
+ Component(const QString &query,
+ bool caseSensitive = false,
+ const ColumnList &columns = ColumnList(),
+ MatchMode mode = Contains);
+
+ /**
+ * Create a query component. This defaults to searching all visible coulumns.
+ */
+ Component(const QRegExp &query, const ColumnList &columns = ColumnList());
+
+ QString query() const { return m_query; }
+ QRegExp pattern() const { return m_queryRe; }
+ ColumnList columns() const { return m_columns; }
+
+ bool matches(PlaylistItem *item) const;
+ bool isPatternSearch() const { return m_re; }
+ bool isCaseSensitive() const { return m_caseSensitive; }
+ MatchMode matchMode() const { return m_mode; }
+
+ bool operator==(const Component &v) const;
+
+private:
+ QString m_query;
+ QRegExp m_queryRe;
+ mutable ColumnList m_columns;
+ MatchMode m_mode;
+ bool m_searchAllVisible;
+ bool m_caseSensitive;
+ bool m_re;
+};
+
+/**
+ * Streams \a search to the stream \a s.
+ * \note This does not save the playlist list, but instead will assume that the
+ * search is just relevant to the collection list. This is all that is presently
+ * needed by JuK.
+ */
+QDataStream &operator<<(QDataStream &s, const PlaylistSearch &search);
+
+/**
+ * Streams \a search from the stream \a s.
+ * \note This does not save the playlist list, but instead will assume that the
+ * search is just relevant to the collection list. This is all that is presently
+ * needed by JuK.
+ */
+QDataStream &operator>>(QDataStream &s, PlaylistSearch &search);
+
+QDataStream &operator<<(QDataStream &s, const PlaylistSearch::Component &c);
+QDataStream &operator>>(QDataStream &s, PlaylistSearch::Component &c);
+
+#endif
diff --git a/juk/playlistsplitter.cpp b/juk/playlistsplitter.cpp
new file mode 100644
index 00000000..5bc33a69
--- /dev/null
+++ b/juk/playlistsplitter.cpp
@@ -0,0 +1,237 @@
+/***************************************************************************
+ begin : Fri Sep 13 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kaction.h>
+#include <kdebug.h>
+
+#include <qlayout.h>
+#include <qevent.h>
+
+#include "playlistsplitter.h"
+#include "searchwidget.h"
+#include "playlistsearch.h"
+#include "actioncollection.h"
+#include "tageditor.h"
+#include "collectionlist.h"
+#include "playermanager.h"
+#include "nowplaying.h"
+
+using namespace ActionCollection;
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+PlaylistSplitter::PlaylistSplitter(QWidget *parent, const char *name) :
+ QSplitter(Qt::Horizontal, parent, name),
+ m_newVisible(0),
+ m_playlistBox(0),
+ m_searchWidget(0),
+ m_playlistStack(0),
+ m_editor(0)
+{
+ setupActions();
+ setupLayout();
+ readConfig();
+
+ m_editor->slotUpdateCollection();
+ m_editor->setupObservers();
+}
+
+PlaylistSplitter::~PlaylistSplitter()
+{
+ saveConfig();
+
+ // Since we want to ensure that the shutdown process for the PlaylistCollection
+ // (a base class for PlaylistBox) has a chance to write the playlists to disk
+ // before they are deleted we're explicitly deleting the PlaylistBox here.
+
+ delete m_playlistBox;
+}
+
+bool PlaylistSplitter::eventFilter(QObject *, QEvent *event)
+{
+ if(event->type() == FocusUpEvent::id) {
+ m_searchWidget->setFocus();
+ return true;
+ }
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public slots
+////////////////////////////////////////////////////////////////////////////////
+
+void PlaylistSplitter::setFocus()
+{
+ m_searchWidget->setFocus();
+}
+
+void PlaylistSplitter::slotFocusCurrentPlaylist()
+{
+ Playlist *playlist = m_playlistBox->visiblePlaylist();
+
+ if(playlist) {
+ playlist->setFocus();
+ playlist->KListView::selectAll(false);
+
+ // Select the top visible (and matching) item.
+
+ PlaylistItem *item = static_cast<PlaylistItem *>(playlist->itemAt(QPoint(0, 0)));
+
+ if(!item)
+ return;
+
+ // A little bit of a hack to make QListView repaint things properly. Switch
+ // to single selection mode, set the selection and then switch back.
+
+ playlist->setSelectionMode(QListView::Single);
+
+ playlist->markItemSelected(item, true);
+ playlist->setCurrentItem(item);
+
+ playlist->setSelectionMode(QListView::Extended);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+Playlist *PlaylistSplitter::visiblePlaylist() const
+{
+ return m_newVisible ? m_newVisible : m_playlistBox->visiblePlaylist();
+}
+
+void PlaylistSplitter::setupActions()
+{
+ KToggleAction *showSearch =
+ new KToggleAction(i18n("Show &Search Bar"), "filefind", 0, actions(), "showSearch");
+ showSearch->setCheckedState(i18n("Hide &Search Bar"));
+
+ new KAction(i18n("Edit Track Search"), "edit_clear", "F6", this, SLOT(setFocus()), actions(), "editTrackSearch");
+}
+
+void PlaylistSplitter::setupLayout()
+{
+ setOpaqueResize(false);
+
+ // Create a splitter to go between the playlists and the editor.
+
+ QSplitter *editorSplitter = new QSplitter(Qt::Vertical, this, "editorSplitter");
+
+ // Create the playlist and the editor.
+
+ QWidget *top = new QWidget(editorSplitter);
+ QVBoxLayout *topLayout = new QVBoxLayout(top);
+
+ m_playlistStack = new QWidgetStack(top, "playlistStack");
+ m_playlistStack->installEventFilter(this);
+
+ connect(m_playlistStack, SIGNAL(aboutToShow(QWidget *)), this, SLOT(slotPlaylistChanged(QWidget *)));
+
+ m_editor = new TagEditor(editorSplitter, "tagEditor");
+
+ // Make the editor as small as possible (or at least as small as recommended)
+
+ editorSplitter->setResizeMode(m_editor, QSplitter::FollowSizeHint);
+
+ // Create the PlaylistBox
+
+ m_playlistBox = new PlaylistBox(this, m_playlistStack, "playlistBox");
+
+ connect(m_playlistBox->object(), SIGNAL(signalSelectedItemsChanged()),
+ this, SLOT(slotPlaylistSelectionChanged()));
+ connect(m_playlistBox, SIGNAL(signalPlaylistDestroyed(Playlist *)),
+ m_editor, SLOT(slotPlaylistDestroyed(Playlist *)));
+
+ moveToFirst(m_playlistBox);
+
+ connect(CollectionList::instance(), SIGNAL(signalCollectionChanged()),
+ m_editor, SLOT(slotUpdateCollection()));
+
+ NowPlaying *nowPlaying = new NowPlaying(top, m_playlistBox);
+
+ // Create the search widget -- this must be done after the CollectionList is created.
+
+ m_searchWidget = new SearchWidget(top, "searchWidget");
+ connect(m_searchWidget, SIGNAL(signalQueryChanged()),
+ this, SLOT(slotShowSearchResults()));
+ connect(m_searchWidget, SIGNAL(signalDownPressed()),
+ this, SLOT(slotFocusCurrentPlaylist()));
+ connect(m_searchWidget, SIGNAL(signalAdvancedSearchClicked()),
+ m_playlistBox->object(), SLOT(slotCreateSearchPlaylist()));
+ connect(m_searchWidget, SIGNAL(signalShown(bool)),
+ m_playlistBox->object(), SLOT(slotSetSearchEnabled(bool)));
+ connect(action<KToggleAction>("showSearch"), SIGNAL(toggled(bool)),
+ m_searchWidget, SLOT(setEnabled(bool)));
+
+ topLayout->addWidget(nowPlaying);
+ topLayout->addWidget(m_searchWidget);
+ topLayout->addWidget(m_playlistStack);
+
+ // Show the collection on startup.
+ m_playlistBox->setSelected(0, true);
+}
+
+void PlaylistSplitter::readConfig()
+{
+ KConfigGroup config(KGlobal::config(), "Splitter");
+
+ QValueList<int> splitterSizes = config.readIntListEntry("PlaylistSplitterSizes");
+ if(splitterSizes.isEmpty()) {
+ splitterSizes.append(100);
+ splitterSizes.append(640);
+ }
+ setSizes(splitterSizes);
+
+ bool showSearch = config.readBoolEntry("ShowSearch", true);
+ action<KToggleAction>("showSearch")->setChecked(showSearch);
+ m_searchWidget->setShown(showSearch);
+}
+
+void PlaylistSplitter::saveConfig()
+{
+ KConfigGroup config(KGlobal::config(), "Splitter");
+ config.writeEntry("PlaylistSplitterSizes", sizes());
+ config.writeEntry("ShowSearch", action<KToggleAction>("showSearch")->isChecked());
+}
+
+void PlaylistSplitter::slotShowSearchResults()
+{
+ PlaylistList playlists;
+ playlists.append(visiblePlaylist());
+ PlaylistSearch search = m_searchWidget->search(playlists);
+ visiblePlaylist()->setSearch(search);
+}
+
+void PlaylistSplitter::slotPlaylistSelectionChanged()
+{
+ m_editor->slotSetItems(visiblePlaylist()->selectedItems());
+}
+
+void PlaylistSplitter::slotPlaylistChanged(QWidget *w)
+{
+ Playlist *p = dynamic_cast<Playlist *>(w);
+
+ if(!p)
+ return;
+
+ m_newVisible = p;
+ m_searchWidget->setSearch(p->search());
+ m_newVisible = 0;
+}
+
+#include "playlistsplitter.moc"
diff --git a/juk/playlistsplitter.h b/juk/playlistsplitter.h
new file mode 100644
index 00000000..0734ee05
--- /dev/null
+++ b/juk/playlistsplitter.h
@@ -0,0 +1,88 @@
+/***************************************************************************
+ begin : Fri Sep 13 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef PLAYLISTSPLITTER_H
+#define PLAYLISTSPLITTER_H
+
+#include <kfiledialog.h>
+
+#include <qwidgetstack.h>
+
+#include "playlistbox.h"
+
+class KActionMenu;
+class PlaylistItem;
+class SearchWidget;
+class HistoryPlaylist;
+class PlaylistInterface;
+class TagEditor;
+
+/**
+ * This is the main layout class of JuK. It should contain a PlaylistBox and
+ * a QWidgetStack of the Playlists.
+ *
+ * This class serves as a "mediator" (see "Design Patterns") between the JuK
+ * class and the playlist classes. Thus all access to the playlist classes from
+ * non-Playlist related classes should be through the public API of this class.
+ */
+
+class PlaylistSplitter : public QSplitter
+{
+ Q_OBJECT
+
+public:
+ PlaylistSplitter(QWidget *parent, const char *name = 0);
+ virtual ~PlaylistSplitter();
+
+ PlaylistInterface *playlist() const { return m_playlistBox; }
+
+ virtual bool eventFilter(QObject *watched, QEvent *event);
+
+public slots:
+ virtual void setFocus();
+ virtual void slotFocusCurrentPlaylist();
+
+private:
+
+ /**
+ * This returns a pointer to the first item in the playlist on the top
+ * of the QWidgetStack of playlists.
+ */
+ Playlist *visiblePlaylist() const;
+
+ void setupActions();
+ void setupLayout();
+ void readConfig();
+ void saveConfig();
+
+private slots:
+
+ /**
+ * Updates the visible search results based on the result of the search
+ * associated with the currently visible playlist.
+ */
+ void slotShowSearchResults();
+ void slotPlaylistSelectionChanged();
+ void slotPlaylistChanged(QWidget *w);
+
+private:
+ Playlist *m_newVisible;
+ PlaylistBox *m_playlistBox;
+ SearchWidget *m_searchWidget;
+ QWidgetStack *m_playlistStack;
+ TagEditor *m_editor;
+};
+
+#endif
diff --git a/juk/searchplaylist.cpp b/juk/searchplaylist.cpp
new file mode 100644
index 00000000..2afd8549
--- /dev/null
+++ b/juk/searchplaylist.cpp
@@ -0,0 +1,115 @@
+/***************************************************************************
+ begin : Mon May 5 2003
+ copyright : (C) 2003 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kdebug.h>
+
+#include <qptrdict.h>
+
+#include "searchplaylist.h"
+#include "playlistitem.h"
+#include "collectionlist.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+SearchPlaylist::SearchPlaylist(PlaylistCollection *collection,
+ const PlaylistSearch &search,
+ const QString &name,
+ bool setupPlaylist,
+ bool synchronizePlaying) :
+ DynamicPlaylist(search.playlists(), collection, name, "find",
+ setupPlaylist, synchronizePlaying),
+ m_search(search)
+{
+
+}
+
+void SearchPlaylist::setPlaylistSearch(const PlaylistSearch &s, bool update)
+{
+ m_search = s;
+ if(update)
+ setPlaylists(s.playlists());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected methods
+////////////////////////////////////////////////////////////////////////////////
+
+void SearchPlaylist::updateItems()
+{
+ // Here we don't simply use "clear" since that would involve a call to
+ // items() which would in turn call this method...
+
+ PlaylistItemList l = Playlist::items();
+
+ QPtrDict<PlaylistItem> oldItems(503);
+
+ for(PlaylistItemList::ConstIterator it = l.begin(); it != l.end(); ++it)
+ oldItems.insert((*it)->collectionItem(), *it);
+
+ m_search.search();
+ PlaylistItemList matched = m_search.matchedItems();
+ PlaylistItemList newItems;
+
+ for(PlaylistItemList::ConstIterator it = matched.begin(); it != matched.end(); ++it) {
+ if(!oldItems.remove((*it)->collectionItem()))
+ newItems.append((*it)->collectionItem());
+ }
+
+ // kdDebug(65432) << k_funcinfo << "newItems.size() == " << newItems.size() << endl;
+
+ for(QPtrDictIterator<PlaylistItem> it(oldItems); it.current(); ++it)
+ clearItem(it.current(), false);
+
+ if(!oldItems.isEmpty() && newItems.isEmpty())
+ dataChanged();
+
+ createItems(newItems);
+
+ if(synchronizePlaying()) {
+ kdDebug(65432) << k_funcinfo << "synchronizing playing" << endl;
+ synchronizePlayingItems(m_search.playlists(), true);
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// helper functions
+////////////////////////////////////////////////////////////////////////////////
+
+QDataStream &operator<<(QDataStream &s, const SearchPlaylist &p)
+{
+ s << p.name()
+ << p.playlistSearch();
+
+ return s;
+}
+
+QDataStream &operator>>(QDataStream &s, SearchPlaylist &p)
+{
+ QString name;
+ PlaylistSearch search;
+
+ s >> name
+ >> search;
+
+ p.setName(name);
+ p.setPlaylistSearch(search, false);
+
+ return s;
+}
+
+#include "searchplaylist.moc"
diff --git a/juk/searchplaylist.h b/juk/searchplaylist.h
new file mode 100644
index 00000000..2acd1e5c
--- /dev/null
+++ b/juk/searchplaylist.h
@@ -0,0 +1,48 @@
+/***************************************************************************
+ begin : Mon May 5 2003
+ copyright : (C) 2003 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef SEARCHPLAYLIST_H
+#define SEARCHPLAYLIST_H
+
+#include "dynamicplaylist.h"
+
+class SearchPlaylist : public DynamicPlaylist
+{
+ Q_OBJECT
+public:
+ SearchPlaylist(PlaylistCollection *collection,
+ const PlaylistSearch &search = PlaylistSearch(),
+ const QString &name = QString::null,
+ bool setupPlaylist = true,
+ bool synchronizePlaying = false);
+
+ PlaylistSearch playlistSearch() const { return m_search; }
+ void setPlaylistSearch(const PlaylistSearch &s, bool update = true);
+ virtual bool searchIsEditable() const { return true; }
+
+protected:
+ /**
+ * Runs the search to update the current items.
+ */
+ virtual void updateItems();
+
+private:
+ PlaylistSearch m_search;
+};
+
+QDataStream &operator<<(QDataStream &s, const SearchPlaylist &p);
+QDataStream &operator>>(QDataStream &s, SearchPlaylist &p);
+
+#endif
diff --git a/juk/searchwidget.cpp b/juk/searchwidget.cpp
new file mode 100644
index 00000000..83b7808e
--- /dev/null
+++ b/juk/searchwidget.cpp
@@ -0,0 +1,292 @@
+/***************************************************************************
+ begin : Sun Mar 6 2003
+ copyright : (C) 2003 by Richard Lrkng
+ email : nouseforaname@home.se
+
+ copyright : (C) 2003 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <klocale.h>
+#include <klineedit.h>
+#include <kiconloader.h>
+#include <kcombobox.h>
+#include <kdebug.h>
+#include <kaction.h>
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qpushbutton.h>
+#include <qtoolbutton.h>
+
+#include "searchwidget.h"
+#include "collectionlist.h"
+#include "actioncollection.h"
+
+using namespace ActionCollection;
+
+////////////////////////////////////////////////////////////////////////////////
+// SearchLine public methods
+////////////////////////////////////////////////////////////////////////////////
+
+SearchLine::SearchLine(QWidget *parent, bool simple, const char *name) :
+ QHBox(parent, name),
+ m_simple(simple),
+ m_searchFieldsBox(0)
+{
+ setSpacing(5);
+
+ if(!m_simple) {
+ m_searchFieldsBox = new KComboBox(this, "searchFields");
+ connect(m_searchFieldsBox, SIGNAL(activated(int)),
+ this, SIGNAL(signalQueryChanged()));
+ }
+
+ m_lineEdit = new KLineEdit(this, "searchLineEdit");
+ m_lineEdit->installEventFilter(this);
+ connect(m_lineEdit, SIGNAL(textChanged(const QString &)),
+ this, SIGNAL(signalQueryChanged()));
+ connect(m_lineEdit, SIGNAL(returnPressed()),
+ this, SLOT(slotActivate()));
+
+ if(!m_simple) {
+ m_caseSensitive = new KComboBox(this);
+ m_caseSensitive->insertItem(i18n("Normal Matching"), 0);
+ m_caseSensitive->insertItem(i18n("Case Sensitive"), 1);
+ m_caseSensitive->insertItem(i18n("Pattern Matching"), 2);
+ connect(m_caseSensitive, SIGNAL(activated(int)),
+ this, SIGNAL(signalQueryChanged()));
+ }
+ else
+ m_caseSensitive = 0;
+
+ updateColumns();
+}
+
+PlaylistSearch::Component SearchLine::searchComponent() const
+{
+ QString query = m_lineEdit->text();
+ bool caseSensitive = m_caseSensitive && m_caseSensitive->currentItem() == CaseSensitive;
+
+ Playlist *playlist = CollectionList::instance();
+
+ QValueList<int> searchedColumns;
+
+ if(!m_searchFieldsBox || m_searchFieldsBox->currentItem() == 0) {
+ QValueListConstIterator<int> it = m_columnList.begin();
+ for(; it != m_columnList.end(); ++it) {
+ if(playlist->isColumnVisible(*it))
+ searchedColumns.append(*it);
+ }
+ }
+ else
+ searchedColumns.append(m_columnList[m_searchFieldsBox->currentItem() - 1]);
+
+ if(m_caseSensitive && m_caseSensitive->currentItem() == Pattern)
+ return PlaylistSearch::Component(QRegExp(query), searchedColumns);
+ else
+ return PlaylistSearch::Component(query, caseSensitive, searchedColumns);
+}
+
+void SearchLine::setSearchComponent(const PlaylistSearch::Component &component)
+{
+ if(component == searchComponent())
+ return;
+
+ if(m_simple || !component.isPatternSearch()) {
+ m_lineEdit->setText(component.query());
+ if(m_caseSensitive)
+ m_caseSensitive->setCurrentItem(component.isCaseSensitive() ? CaseSensitive : Default);
+ }
+ else {
+ m_lineEdit->setText(component.pattern().pattern());
+ if(m_caseSensitive)
+ m_caseSensitive->setCurrentItem(Pattern);
+ }
+
+ if(!m_simple) {
+ if(component.columns().isEmpty() || component.columns().size() > 1)
+ m_searchFieldsBox->setCurrentItem(0);
+ else
+ m_searchFieldsBox->setCurrentItem(component.columns().front() + 1);
+ }
+}
+
+void SearchLine::clear()
+{
+ // We don't want to emit the signal if it's already empty.
+ if(!m_lineEdit->text().isEmpty())
+ m_lineEdit->clear();
+}
+
+void SearchLine::setFocus()
+{
+ m_lineEdit->setFocus();
+}
+
+bool SearchLine::eventFilter(QObject *watched, QEvent *e)
+{
+ if(watched != m_lineEdit || e->type() != QEvent::KeyPress)
+ return QHBox::eventFilter(watched, e);
+
+ QKeyEvent *key = static_cast<QKeyEvent *>(e);
+ if(key->key() == Qt::Key_Down)
+ emit signalDownPressed();
+
+ return QHBox::eventFilter(watched, e);
+}
+
+void SearchLine::slotActivate()
+{
+ action("stop")->activate();
+ action("playFirst")->activate();
+}
+
+void SearchLine::updateColumns()
+{
+ QString currentText;
+
+ if(m_searchFieldsBox) {
+ currentText = m_searchFieldsBox->currentText();
+ m_searchFieldsBox->clear();
+ }
+
+ QStringList columnHeaders;
+
+ columnHeaders.append(QString("<%1>").arg(i18n("All Visible")));
+
+ Playlist *playlist = CollectionList::instance();
+
+ int selection = -1;
+ m_columnList.clear();
+
+ for(int i = 0; i < playlist->columns(); i++) {
+ m_columnList.append(i);
+ QString text = playlist->columnText(i);
+ columnHeaders.append(text);
+ if(currentText == text)
+ selection = m_columnList.size() - 1;
+ }
+
+ if(m_searchFieldsBox) {
+ m_searchFieldsBox->insertStringList(columnHeaders);
+ m_searchFieldsBox->setCurrentItem(selection + 1);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// SearchWidget public methods
+////////////////////////////////////////////////////////////////////////////////
+
+SearchWidget::SearchWidget(QWidget *parent, const char *name) : KToolBar(parent, name)
+{
+ setupLayout();
+ updateColumns();
+}
+
+SearchWidget::~SearchWidget()
+{
+
+}
+
+void SearchWidget::setSearch(const PlaylistSearch &search)
+{
+ PlaylistSearch::ComponentList components = search.components();
+
+ if(components.isEmpty()) {
+ clear();
+ return;
+ }
+
+ m_searchLine->setSearchComponent(*components.begin());
+}
+
+QString SearchWidget::searchText() const
+{
+ return m_searchLine->searchComponent().query();
+}
+
+void SearchWidget::setSearchText(const QString &text)
+{
+ m_searchLine->setSearchComponent(PlaylistSearch::Component(text));
+}
+
+PlaylistSearch SearchWidget::search(const PlaylistList &playlists) const
+{
+ PlaylistSearch::ComponentList components;
+ components.append(m_searchLine->searchComponent());
+ return PlaylistSearch(playlists, components);
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// SearchWidget public slots
+////////////////////////////////////////////////////////////////////////////////
+
+void SearchWidget::clear()
+{
+ m_searchLine->clear();
+}
+
+void SearchWidget::setEnabled(bool enable)
+{
+ emit signalShown(enable);
+ setShown(enable);
+}
+
+void SearchWidget::setFocus()
+{
+ m_searchLine->setFocus();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// SearchWidget private methods
+////////////////////////////////////////////////////////////////////////////////
+
+void SearchWidget::updateColumns()
+{
+ m_searchLine->updateColumns();
+}
+
+void SearchWidget::setupLayout()
+{
+ boxLayout()->setSpacing(5);
+
+ QToolButton *clearSearchButton = new QToolButton(this);
+ clearSearchButton->setTextLabel(i18n("Clear Search"), true);
+ clearSearchButton->setIconSet(SmallIconSet("locationbar_erase"));
+
+ QLabel *label = new QLabel(i18n("Search:"), this, "kde toolbar widget");
+
+ m_searchLine = new SearchLine(this, true, "kde toolbar widget");
+
+ label->setBuddy(m_searchLine);
+
+ connect(m_searchLine, SIGNAL(signalQueryChanged()), this, SIGNAL(signalQueryChanged()));
+ connect(m_searchLine, SIGNAL(signalDownPressed()), this, SIGNAL(signalDownPressed()));
+ connect(clearSearchButton, SIGNAL(pressed()), m_searchLine, SLOT(clear()));
+ setStretchableWidget(m_searchLine);
+
+ // I've decided that I think this is ugly, for now.
+
+ /*
+ QToolButton *b = new QToolButton(this);
+ b->setTextLabel(i18n("Advanced Search"), true);
+ b->setIconSet(SmallIconSet("wizard"));
+
+ connect(b, SIGNAL(clicked()), this, SIGNAL(signalAdvancedSearchClicked()));
+ */
+}
+
+#include "searchwidget.moc"
diff --git a/juk/searchwidget.h b/juk/searchwidget.h
new file mode 100644
index 00000000..37a44cb7
--- /dev/null
+++ b/juk/searchwidget.h
@@ -0,0 +1,111 @@
+/***************************************************************************
+ begin : Sun Mar 6 2003
+ copyright : (C) 2003 by Richard Lrkng
+ email : nouseforaname@home.se
+
+ copyright : (C) 2003 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef SEARCHWIDGET_H
+#define SEARCHWIDGET_H
+
+#include <ktoolbar.h>
+
+#include <qhbox.h>
+
+#include "playlistsearch.h"
+#include "jukIface.h"
+
+class QCheckBox;
+
+class KComboBox;
+
+class Playlist;
+
+class SearchLine : public QHBox
+{
+ Q_OBJECT
+
+public:
+ enum Mode { Default = 0, CaseSensitive = 1, Pattern = 2 };
+
+ SearchLine(QWidget *parent, bool simple = false, const char *name = 0);
+ virtual ~SearchLine() {}
+
+ PlaylistSearch::Component searchComponent() const;
+ void setSearchComponent(const PlaylistSearch::Component &component);
+
+ void updateColumns();
+
+public slots:
+ void clear();
+ virtual void setFocus();
+
+protected:
+ virtual bool eventFilter(QObject *watched, QEvent *e);
+
+signals:
+ void signalQueryChanged();
+ void signalDownPressed();
+
+private slots:
+ void slotActivate();
+
+private:
+ bool m_simple;
+ KLineEdit *m_lineEdit;
+ KComboBox *m_searchFieldsBox;
+ KComboBox *m_caseSensitive;
+ QValueList<int> m_columnList;
+};
+
+class SearchWidget : public KToolBar, public SearchIface
+{
+ Q_OBJECT
+
+public:
+ SearchWidget(QWidget *parent, const char *name = 0);
+ virtual ~SearchWidget();
+
+ PlaylistSearch search(const PlaylistList &playlists) const;
+ void setSearch(const PlaylistSearch &search);
+
+ virtual QString searchText() const;
+ virtual void setSearchText(const QString &text);
+
+public slots:
+ void clear();
+ void setEnabled(bool enable);
+ virtual void setFocus();
+
+signals:
+ void signalQueryChanged();
+ void signalAdvancedSearchClicked();
+
+ // This signal is only emitted when the Show/Hide action is triggered.
+ // Minimizing/closing the JuK window will not trigger this signal.
+
+ void signalShown(bool shown);
+
+ void signalDownPressed();
+
+private:
+ void updateColumns();
+ void setupLayout();
+
+private:
+ SearchLine *m_searchLine;
+ QStringList m_columnHeaders;
+};
+
+#endif
diff --git a/juk/slideraction.cpp b/juk/slideraction.cpp
new file mode 100644
index 00000000..eacb4242
--- /dev/null
+++ b/juk/slideraction.cpp
@@ -0,0 +1,357 @@
+/***************************************************************************
+ begin : Wed Feb 6 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <ktoolbar.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+
+#include <qtooltip.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qtimer.h>
+
+#include "slideraction.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// convenience class
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * This "custom" slider reverses the left and middle buttons. Typically the
+ * middle button "instantly" seeks rather than moving the slider towards the
+ * click position in fixed intervals. This behavior has now been mapped on
+ * to the left mouse button.
+ */
+
+class TrackPositionSlider : public QSlider
+{
+public:
+ TrackPositionSlider(QWidget *parent, const char *name) : QSlider(parent, name)
+ {
+ setFocusPolicy(NoFocus);
+ }
+
+protected:
+ virtual void mousePressEvent(QMouseEvent *e)
+ {
+ if(e->button() == LeftButton) {
+ QMouseEvent reverse(QEvent::MouseButtonPress, e->pos(), MidButton, e->state());
+ QSlider::mousePressEvent(&reverse);
+ emit sliderPressed();
+ }
+ else if(e->button() == MidButton) {
+ QMouseEvent reverse(QEvent::MouseButtonPress, e->pos(), LeftButton, e->state());
+ QSlider::mousePressEvent(&reverse);
+ }
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// VolumeSlider implementation
+////////////////////////////////////////////////////////////////////////////////
+
+VolumeSlider::VolumeSlider(Orientation o, QWidget *parent, const char *name) :
+ QSlider(o, parent, name)
+{
+ connect(this, SIGNAL(valueChanged(int)), this, SLOT(slotValueChanged(int)));
+}
+
+void VolumeSlider::wheelEvent(QWheelEvent *e)
+{
+ if(orientation() == Horizontal) {
+ QWheelEvent transposed(e->pos(), -(e->delta()), e->state(), e->orientation());
+ QSlider::wheelEvent(&transposed);
+ }
+ else
+ QSlider::wheelEvent(e);
+}
+
+void VolumeSlider::focusInEvent(QFocusEvent *)
+{
+ clearFocus();
+}
+
+int VolumeSlider::volume() const
+{
+ if(orientation() == Horizontal)
+ return value();
+ else
+ return maxValue() - value();
+}
+
+void VolumeSlider::setVolume(int value)
+{
+ if(orientation() == Horizontal)
+ setValue(value);
+ else
+ setValue(maxValue() - value);
+}
+
+void VolumeSlider::setOrientation(Orientation o)
+{
+ if(o == orientation())
+ return;
+
+ blockSignals(true);
+ setValue(maxValue() - value());
+ blockSignals(false);
+ QSlider::setOrientation(o);
+}
+
+void VolumeSlider::slotValueChanged(int value)
+{
+ if(orientation() == Horizontal)
+ emit signalVolumeChanged(value);
+ else
+ emit signalVolumeChanged(maxValue() - value);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+const int SliderAction::minPosition = 0;
+const int SliderAction::maxPosition = 1000;
+
+SliderAction::SliderAction(const QString &text, QObject *parent, const char *name)
+ : KAction(text, 0, parent, name),
+ m_toolBar(0),
+ m_layout(0),
+ m_trackPositionSlider(0),
+ m_volumeSlider(0),
+ m_dragging(false),
+ m_volumeDragging(false)
+{
+
+}
+
+SliderAction::~SliderAction()
+{
+
+}
+
+int SliderAction::plug(QWidget *parent, int index)
+{
+ QWidget *w = createWidget(parent);
+
+ if(!w)
+ return -1;
+
+ // the check for null makes sure that there is only one toolbar that this is
+ // "plugged" in to
+
+ if(parent->inherits("KToolBar") && !m_toolBar) {
+ m_toolBar = static_cast<KToolBar *>(parent);
+
+ int id = KAction::getToolButtonID();
+
+ m_toolBar->insertWidget(id, w->width(), w, index);
+
+ addContainer(m_toolBar, id);
+
+ connect(m_toolBar, SIGNAL(destroyed()), this, SLOT(slotToolbarDestroyed()));
+ connect(m_toolBar, SIGNAL(orientationChanged(Orientation)),
+ this, SLOT(slotUpdateOrientation()));
+ connect(m_toolBar, SIGNAL(placeChanged(QDockWindow::Place)),
+ this, SLOT(slotUpdateOrientation()));
+
+ slotUpdateOrientation();
+ return (containerCount() - 1);
+ }
+ else
+ slotUpdateOrientation();
+
+ return -1;
+}
+
+
+void SliderAction::unplug(QWidget *parent)
+{
+ if (parent->inherits("KToolBar")) {
+ m_toolBar = static_cast<KToolBar *>(parent);
+
+ int index = findContainer(m_toolBar);
+ if (index != -1) {
+ m_toolBar->removeItem(itemId(index));
+ removeContainer(index);
+
+ m_toolBar = 0;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public slots
+////////////////////////////////////////////////////////////////////////////////
+
+void SliderAction::slotUpdateOrientation()
+{
+ // if the toolbar is not null and either the dockWindow not defined or is the toolbar
+
+ if(!m_toolBar)
+ return;
+
+ if(m_toolBar->barPos() == KToolBar::Right || m_toolBar->barPos() == KToolBar::Left) {
+ m_trackPositionSlider->setOrientation(Vertical);
+ m_volumeSlider->setOrientation(Vertical);
+ m_layout->setDirection(QBoxLayout::TopToBottom);
+ }
+ else {
+ m_trackPositionSlider->setOrientation(Horizontal);
+ m_volumeSlider->setOrientation(Horizontal);
+ m_layout->setDirection(QBoxLayout::LeftToRight);
+ }
+ slotUpdateSize();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+QWidget *SliderAction::createWidget(QWidget *parent) // virtual -- used by base class
+{
+ if(parent) {
+ QWidget *base = new QWidget(parent);
+ base->setBackgroundMode(parent->backgroundMode());
+ base->setName("kde toolbar widget");
+
+ KToolBar *toolBar = dynamic_cast<KToolBar *>(parent);
+
+ if(toolBar)
+ toolBar->setStretchableWidget(base);
+
+ Orientation orientation;
+
+ if(toolBar && toolBar->barPos() == KToolBar::Right || toolBar->barPos() == KToolBar::Left)
+ orientation = Vertical;
+ else
+ orientation = Horizontal;
+
+ m_layout = new QBoxLayout(base, QBoxLayout::TopToBottom, 5, 5);
+
+ m_layout->addItem(new QSpacerItem(20, 1));
+
+ QLabel *trackPositionLabel = new QLabel(base);
+ trackPositionLabel->setName("kde toolbar widget");
+ trackPositionLabel->setPixmap(SmallIcon("player_time"));
+ QToolTip::add(trackPositionLabel, i18n("Track position"));
+ m_layout->addWidget(trackPositionLabel);
+
+ m_trackPositionSlider = new TrackPositionSlider(base, "trackPositionSlider");
+ m_trackPositionSlider->setMaxValue(maxPosition);
+ QToolTip::add(m_trackPositionSlider, i18n("Track position"));
+ m_layout->addWidget(m_trackPositionSlider);
+ connect(m_trackPositionSlider, SIGNAL(sliderPressed()), this, SLOT(slotSliderPressed()));
+ connect(m_trackPositionSlider, SIGNAL(sliderReleased()), this, SLOT(slotSliderReleased()));
+
+ m_layout->addItem(new QSpacerItem(10, 1));
+
+ QLabel *volumeLabel = new QLabel(base);
+ volumeLabel->setName("kde toolbar widget");
+ volumeLabel->setPixmap(SmallIcon("player_volume"));
+ QToolTip::add(volumeLabel, i18n("Volume"));
+ m_layout->addWidget(volumeLabel);
+
+ m_volumeSlider = new VolumeSlider(orientation, base, "volumeSlider");
+ m_volumeSlider->setMaxValue(100);
+ QToolTip::add(m_volumeSlider, i18n("Volume"));
+ m_layout->addWidget(m_volumeSlider);
+ connect(m_volumeSlider, SIGNAL(signalVolumeChanged(int)), SIGNAL(signalVolumeChanged(int)));
+ connect(m_volumeSlider, SIGNAL(sliderPressed()), this, SLOT(slotVolumeSliderPressed()));
+ connect(m_volumeSlider, SIGNAL(sliderReleased()), this, SLOT(slotVolumeSliderReleased()));
+
+ m_volumeSlider->setName("kde toolbar widget");
+ m_trackPositionSlider->setName("kde toolbar widget");
+
+ m_layout->setStretchFactor(m_trackPositionSlider, 4);
+ m_layout->setStretchFactor(m_volumeSlider, 1);
+
+ connect(parent, SIGNAL(modechange()), this, SLOT(slotUpdateSize()));
+
+ return base;
+ }
+ else
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private slots
+////////////////////////////////////////////////////////////////////////////////
+
+void SliderAction::slotUpdateSize()
+{
+ static const int offset = 3;
+ static const int absoluteMax = 10000;
+
+ if(!m_toolBar)
+ return;
+
+ if(m_toolBar->barPos() == KToolBar::Right || m_toolBar->barPos() == KToolBar::Left) {
+ m_volumeSlider->setMaximumWidth(m_toolBar->iconSize() - offset);
+ m_volumeSlider->setMaximumHeight(volumeMax);
+
+ m_trackPositionSlider->setMaximumWidth(m_toolBar->iconSize() - offset);
+ m_trackPositionSlider->setMaximumHeight(absoluteMax);
+ }
+ else {
+ m_volumeSlider->setMaximumHeight(m_toolBar->iconSize() - offset);
+ m_volumeSlider->setMaximumWidth(volumeMax);
+
+ m_trackPositionSlider->setMaximumHeight(m_toolBar->iconSize() - offset);
+ m_trackPositionSlider->setMaximumWidth(absoluteMax);
+ }
+}
+
+void SliderAction::slotSliderPressed()
+{
+ m_dragging = true;
+}
+
+void SliderAction::slotSliderReleased()
+{
+ m_dragging = false;
+ emit signalPositionChanged(m_trackPositionSlider->value());
+}
+
+void SliderAction::slotVolumeSliderPressed()
+{
+ m_volumeDragging = true;
+}
+
+void SliderAction::slotVolumeSliderReleased()
+{
+ m_volumeDragging = false;
+ emit signalVolumeChanged(m_volumeSlider->value());
+}
+
+void SliderAction::slotToolbarDestroyed()
+{
+ int index = findContainer(m_toolBar);
+ if(index != -1)
+ removeContainer(index);
+
+ m_toolBar = 0;
+
+ // This is probably a leak, but this code path hardly ever occurs, and it's
+ // too hard to debug correctly.
+
+ m_trackPositionSlider = 0;
+ m_volumeSlider = 0;
+}
+
+#include "slideraction.moc"
+
+// vim: set et sw=4 ts=4:
diff --git a/juk/slideraction.h b/juk/slideraction.h
new file mode 100644
index 00000000..f312196c
--- /dev/null
+++ b/juk/slideraction.h
@@ -0,0 +1,97 @@
+/***************************************************************************
+ begin : Wed Feb 6 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef SLIDERACTION_H
+#define SLIDERACTION_H
+
+#include <kaction.h>
+#include <qslider.h>
+
+class QBoxLayout;
+class QDockWindow;
+
+class VolumeSlider : public QSlider
+{
+ Q_OBJECT
+
+public:
+ VolumeSlider(Orientation o, QWidget *parent, const char *name);
+
+ int volume() const;
+ void setVolume(int value);
+
+ void setOrientation(Orientation o);
+
+signals:
+ void signalVolumeChanged(int value);
+
+protected:
+ virtual void wheelEvent(QWheelEvent *e);
+ virtual void focusInEvent(QFocusEvent *);
+
+private slots:
+ void slotValueChanged(int value);
+};
+
+class SliderAction : public KAction
+{
+ Q_OBJECT
+
+public:
+ SliderAction(const QString &text, QObject *parent, const char *name);
+ virtual ~SliderAction();
+
+ VolumeSlider *volumeSlider() const { return m_volumeSlider; }
+ QSlider *trackPositionSlider() const { return m_trackPositionSlider; }
+
+ bool dragging() const { return m_dragging; }
+ bool volumeDragging() const { return m_volumeDragging; }
+
+ virtual int plug(QWidget *parent, int index = -1);
+ virtual void unplug(QWidget *widget);
+
+ static const int minPosition;
+ static const int maxPosition;
+
+public slots:
+ void slotUpdateOrientation();
+
+signals:
+ void signalPositionChanged(int position);
+ void signalVolumeChanged(int volume);
+
+private:
+ QWidget *createWidget(QWidget *parent);
+
+private slots:
+ void slotUpdateSize();
+ void slotVolumeSliderPressed();
+ void slotVolumeSliderReleased();
+ void slotSliderPressed();
+ void slotSliderReleased();
+ void slotToolbarDestroyed();
+
+private:
+ KToolBar *m_toolBar;
+ QBoxLayout *m_layout;
+ QSlider *m_trackPositionSlider;
+ VolumeSlider *m_volumeSlider;
+ bool m_dragging;
+ bool m_volumeDragging;
+
+ static const int volumeMax = 50;
+};
+
+#endif
diff --git a/juk/sortedstringlist.cpp b/juk/sortedstringlist.cpp
new file mode 100644
index 00000000..87f2938d
--- /dev/null
+++ b/juk/sortedstringlist.cpp
@@ -0,0 +1,180 @@
+/***************************************************************************
+ begin : Wed Jan 29 2003
+ copyright : (C) 2003 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kdebug.h>
+
+#include "sortedstringlist.h"
+
+class SortedStringList::Node
+{
+public:
+ Node(const QString &value) : key(value), parent(0), left(0), right(0) {}
+ ~Node() {}
+
+ QString key;
+ Node *parent;
+ Node *left;
+ Node *right;
+};
+
+SortedStringList::SortedStringList() : m_root(0)
+{
+
+}
+
+SortedStringList::~SortedStringList()
+{
+
+}
+
+bool SortedStringList::insert(const QString &value)
+{
+ return BSTInsert(value);
+}
+
+bool SortedStringList::contains(const QString &value) const
+{
+ return find(value);
+}
+
+SortedStringList::Node *SortedStringList::treeMinimum(Node *n) const
+{
+ while(n->left)
+ n = n->left;
+ return n;
+}
+
+SortedStringList::Node *SortedStringList::treeSuccessor(Node *n) const
+{
+ if(n->right)
+ return treeMinimum(n->right);
+
+ Node *p = n->parent;
+
+ while(p && n == p->right) {
+ n = p;
+ p = p->parent;
+ }
+
+ return p;
+}
+
+bool SortedStringList::remove(const QString &value)
+{
+ Node *n = find(value);
+
+ if(!n)
+ return false;
+
+ Node *y;
+ Node *x;
+
+ if(!n->left || !n->right)
+ y = n;
+ else
+ y = treeSuccessor(n);
+
+ if(y->left)
+ x = y->left;
+ else
+ x = y->right;
+
+ if(x)
+ x->parent = y->parent;
+
+ if(!y->parent)
+ m_root = x;
+ else {
+ if(y == y->parent->left)
+ y->parent->left = x;
+ else
+ y->parent->right = x;
+ }
+
+ if(y != x)
+ n->key = y->key;
+
+ delete y;
+
+ return true;
+}
+
+QStringList SortedStringList::values() const
+{
+ QStringList l;
+ traverse(m_root, l);
+ return l;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private methods
+////////////////////////////////////////////////////////////////////////////////
+
+SortedStringList::Node *SortedStringList::find(const QString &value) const
+{
+ Node *n = m_root;
+ while(n && value != n->key) {
+ if(value < n->key)
+ n = n->left;
+ else
+ n = n->right;
+ }
+
+ return n;
+}
+
+bool SortedStringList::BSTInsert(const QString &value)
+{
+ Node *previousNode = 0;
+ Node *node = m_root;
+
+ while(node) {
+ previousNode = node;
+ if(value == node->key)
+ return true;
+ else if(value < node->key)
+ node = node->left;
+ else
+ node = node->right;
+ }
+
+ if(previousNode && value == previousNode->key)
+ return true;
+
+ Node *n = new Node(value);
+
+ n->parent = previousNode;
+
+ if(!m_root)
+ m_root = n;
+ else {
+ if(value < previousNode->key)
+ previousNode->left = n;
+ else
+ previousNode->right = n;
+ }
+
+ return false;
+}
+
+void SortedStringList::traverse(const Node *n, QStringList &list) const
+{
+ if(!n)
+ return;
+
+ traverse(n->left, list);
+ list.append(n->key);
+ traverse(n->right, list);
+}
diff --git a/juk/sortedstringlist.h b/juk/sortedstringlist.h
new file mode 100644
index 00000000..108a16c6
--- /dev/null
+++ b/juk/sortedstringlist.h
@@ -0,0 +1,59 @@
+/***************************************************************************
+ begin : Wed Jan 29 2003
+ copyright : (C) 2003 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef SORTEDSTRINGLIST_H
+#define SORTEDSTRINGLIST_H
+
+#include <qstringlist.h>
+
+class SortedStringList
+{
+public:
+ SortedStringList();
+ ~SortedStringList();
+
+ /**
+ * Insert the value. Returns true if the item was already in the list
+ * or false otherwise.
+ */
+ bool insert(const QString &value);
+ bool contains(const QString &value) const;
+ bool remove(const QString &value);
+
+ /**
+ * Returns a sorted list of the values.
+ * Warning, this method is expensive and shouldn't be used except when
+ * necessary.
+ */
+ QStringList values() const;
+
+private:
+ class Node;
+
+ Node *find(const QString &value) const;
+ /**
+ * The insertion implementation. Returns true if the item was already
+ * present in the list.
+ */
+ bool BSTInsert(const QString &value);
+ void traverse(const Node *n, QStringList &list) const;
+
+ Node *treeMinimum(Node *n) const;
+ Node *treeSuccessor(Node *n) const;
+
+ Node *m_root;
+};
+
+#endif
diff --git a/juk/splashscreen.cpp b/juk/splashscreen.cpp
new file mode 100644
index 00000000..5f91a070
--- /dev/null
+++ b/juk/splashscreen.cpp
@@ -0,0 +1,104 @@
+/***************************************************************************
+ begin : Sun Dec 8 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kapplication.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kdebug.h>
+
+#include <qpainter.h>
+
+#include "splashscreen.h"
+
+SplashScreen *SplashScreen::splash = 0;
+bool SplashScreen::done = false;
+int SplashScreen::count = 0;
+
+static QString loadedText(int i)
+{
+ static QString loading = i18n("Loading").upper();
+ return loading + ": " + QString::number(i);;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// pubic members
+////////////////////////////////////////////////////////////////////////////////
+
+SplashScreen *SplashScreen::instance()
+{
+ if(!splash && !done)
+ splash = new SplashScreen();
+ return splash;
+}
+
+void SplashScreen::finishedLoading()
+{
+ done = true;
+ delete splash;
+ splash = 0;
+}
+
+void SplashScreen::increment()
+{
+ if(splash) {
+ count++;
+ if(( count & 63 ) == 0)
+ splash->processEvents();
+ }
+}
+
+void SplashScreen::update()
+{
+ if(splash)
+ splash->processEvents();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+SplashScreen::SplashScreen() : QLabel(0 , "splashScreen", Qt::WStyle_Splash)
+{
+ QPixmap background = UserIcon("splash");
+ resize(background.size());
+ setPaletteBackgroundPixmap(background);
+
+ setMargin(7);
+ setAlignment(AlignLeft | AlignBottom);
+
+ setPaletteForegroundColor(QColor(107, 158, 194));
+
+ QFont f = font();
+ f.setPixelSize(10);
+ setFont(f);
+
+ setText(loadedText(0));
+}
+
+SplashScreen::~SplashScreen()
+{
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private methods
+////////////////////////////////////////////////////////////////////////////////
+
+void SplashScreen::processEvents()
+{
+ setText(loadedText(count));
+ kapp->processEvents();
+}
diff --git a/juk/splashscreen.h b/juk/splashscreen.h
new file mode 100644
index 00000000..407ed509
--- /dev/null
+++ b/juk/splashscreen.h
@@ -0,0 +1,52 @@
+/***************************************************************************
+ begin : Sun Dec 8 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef SPLASHSCREEN_H
+#define SPLASHSCREEN_H
+
+#include <qlabel.h>
+
+/**
+ * Well, all of this session restoration sure is fun, but it's starting to take
+ * a while, especially say, if you're building KDE and indexing your file system
+ * in the background. ;-) So, despite my general hate of splashscreens I
+ * thought on appropriate here.
+ *
+ * As in other places, this is a singleton. That makes it relatively easy to
+ * handle the updates from whichever class seems appropriate through static
+ * methods.
+ */
+
+class SplashScreen : public QLabel
+{
+public:
+ static SplashScreen *instance();
+ static void finishedLoading();
+ static void increment();
+ static void update();
+
+protected:
+ SplashScreen();
+ virtual ~SplashScreen();
+
+private:
+ void processEvents();
+
+ static SplashScreen *splash;
+ static bool done;
+ static int count;
+};
+
+#endif
diff --git a/juk/statuslabel.cpp b/juk/statuslabel.cpp
new file mode 100644
index 00000000..fa8dfb38
--- /dev/null
+++ b/juk/statuslabel.cpp
@@ -0,0 +1,205 @@
+/***************************************************************************
+ begin : Fri Oct 18 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kaction.h>
+#include <kpushbutton.h>
+#include <kiconloader.h>
+#include <ksqueezedtextlabel.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <qtooltip.h>
+#include <qlayout.h>
+
+#include "statuslabel.h"
+#include "filehandle.h"
+#include "playlistinterface.h"
+#include "actioncollection.h"
+#include "tag.h"
+
+using namespace ActionCollection;
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+StatusLabel::StatusLabel(PlaylistInterface *playlist, QWidget *parent, const char *name) :
+ QHBox(parent, name),
+ PlaylistObserver(playlist),
+ m_showTimeRemaining(false)
+{
+ QFrame *trackAndPlaylist = new QFrame(this);
+ trackAndPlaylist->setFrameStyle(Box | Sunken);
+ trackAndPlaylist->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+
+ // Make sure that we have enough of a margin to suffice for the borders,
+ // hence the "lineWidth() * 2"
+ QHBoxLayout *trackAndPlaylistLayout = new QHBoxLayout(trackAndPlaylist,
+ trackAndPlaylist->lineWidth() * 2,
+ 5, "trackAndPlaylistLayout");
+ trackAndPlaylistLayout->addSpacing(5);
+
+ m_playlistLabel = new KSqueezedTextLabel(trackAndPlaylist, "playlistLabel");
+ trackAndPlaylistLayout->addWidget(m_playlistLabel);
+ m_playlistLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ m_playlistLabel->setTextFormat(PlainText);
+ m_playlistLabel->setAlignment(AlignLeft | AlignVCenter);
+
+ m_trackLabel = new KSqueezedTextLabel(trackAndPlaylist, "trackLabel");
+ trackAndPlaylistLayout->addWidget(m_trackLabel);
+ m_trackLabel->setAlignment(AlignRight | AlignVCenter);
+ m_trackLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ m_trackLabel->setTextFormat(PlainText);
+
+ trackAndPlaylistLayout->addSpacing(5);
+
+ m_itemTimeLabel = new QLabel(this);
+ QFontMetrics fontMetrics(font());
+ m_itemTimeLabel->setAlignment(AlignCenter);
+ m_itemTimeLabel->setMinimumWidth(fontMetrics.boundingRect("000:00 / 000:00").width());
+ m_itemTimeLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
+ m_itemTimeLabel->setFrameStyle(Box | Sunken);
+ m_itemTimeLabel->installEventFilter(this);
+
+ setItemTotalTime(0);
+ setItemCurrentTime(0);
+
+ QHBox *jumpBox = new QHBox(this);
+ jumpBox->setFrameStyle(Box | Sunken);
+ jumpBox->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Minimum);
+
+ QPushButton *jumpButton = new QPushButton(jumpBox);
+ jumpButton->setPixmap(SmallIcon("up"));
+ jumpButton->setFlat(true);
+
+ QToolTip::add(jumpButton, i18n("Jump to the currently playing item"));
+ connect(jumpButton, SIGNAL(clicked()), action("showPlaying"), SLOT(activate()));
+
+ installEventFilter(this);
+
+ updateData();
+}
+
+StatusLabel::~StatusLabel()
+{
+
+}
+
+void StatusLabel::updateCurrent()
+{
+ if(playlist()->playing()) {
+ FileHandle file = playlist()->currentFile();
+
+ QString mid = file.tag()->artist().isEmpty() || file.tag()->title().isEmpty()
+ ? QString::null : QString(" - ");
+
+ QString text = file.tag()->artist() + mid + file.tag()->title();
+
+ m_trackLabel->setText(text);
+ m_playlistLabel->setText(playlist()->name().simplifyWhiteSpace());
+ }
+}
+
+void StatusLabel::updateData()
+{
+ updateCurrent();
+
+ if(!playlist()->playing()) {
+ setItemTotalTime(0);
+ setItemCurrentTime(0);
+
+ int time = playlist()->time();
+
+ int days = time / (60 * 60 * 24);
+ int hours = time / (60 * 60) % 24;
+ int minutes = time / 60 % 60;
+ int seconds = time % 60;
+
+ QString timeString;
+
+ if(days > 0) {
+ timeString = i18n("1 day", "%n days", days);
+ timeString.append(" ");
+ }
+
+ if(days > 0 || hours > 0)
+ timeString.append(QString().sprintf("%1d:%02d:%02d", hours, minutes, seconds));
+ else
+ timeString.append(QString().sprintf("%1d:%02d", minutes, seconds));
+
+ m_playlistLabel->setText(playlist()->name());
+ m_trackLabel->setText(i18n("1 item", "%n items", playlist()->count()) + " - " + timeString);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private methods
+////////////////////////////////////////////////////////////////////////////////
+
+void StatusLabel::updateTime()
+{
+ int minutes;
+ int seconds;
+
+ if(m_showTimeRemaining) {
+ minutes = int((m_itemTotalTime - m_itemCurrentTime) / 60);
+ seconds = (m_itemTotalTime - m_itemCurrentTime) % 60;
+ }
+ else {
+ minutes = int(m_itemCurrentTime / 60);
+ seconds = m_itemCurrentTime % 60;
+ }
+
+ int totalMinutes = int(m_itemTotalTime / 60);
+ int totalSeconds = m_itemTotalTime % 60;
+
+ QString timeString = formatTime(minutes, seconds) + " / " +
+ formatTime(totalMinutes, totalSeconds);
+ m_itemTimeLabel->setText(timeString);
+}
+
+bool StatusLabel::eventFilter(QObject *o, QEvent *e)
+{
+ if(!o || !e)
+ return false;
+
+ QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(e);
+ if(e->type() == QEvent::MouseButtonRelease &&
+ mouseEvent->button() == LeftButton)
+ {
+ if(o == m_itemTimeLabel) {
+ m_showTimeRemaining = !m_showTimeRemaining;
+ updateTime();
+ }
+ else
+ action("showPlaying")->activate();
+
+ return true;
+ }
+ return false;
+}
+
+QString StatusLabel::formatTime(int minutes, int seconds) // static
+{
+ QString m = QString::number(minutes);
+ if(m.length() == 1)
+ m = "0" + m;
+ QString s = QString::number(seconds);
+ if(s.length() == 1)
+ s = "0" + s;
+ return m + ":" + s;
+}
+
+#include "statuslabel.moc"
diff --git a/juk/statuslabel.h b/juk/statuslabel.h
new file mode 100644
index 00000000..6c733f21
--- /dev/null
+++ b/juk/statuslabel.h
@@ -0,0 +1,64 @@
+/***************************************************************************
+ begin : Fri Oct 18 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef STATUSLABEL_H
+#define STATUSLABEL_H
+
+#include "playlistinterface.h"
+
+#include <qhbox.h>
+
+class QLabel;
+class KSqueezedTextLabel;
+
+class FileHandle;
+
+class StatusLabel : public QHBox, public PlaylistObserver
+{
+ Q_OBJECT
+
+public:
+ StatusLabel(PlaylistInterface *playlist, QWidget *parent = 0, const char *name = 0);
+ virtual ~StatusLabel();
+ virtual void updateCurrent();
+
+public slots:
+ /**
+ * This just sets internal variables that are used by setItemCurrentTime().
+ * Please call that method to display the time.
+ */
+ void setItemTotalTime(int time) { m_itemTotalTime = time; }
+ void setItemCurrentTime(int time) { m_itemCurrentTime = time; updateTime(); }
+ virtual void updateData();
+
+signals:
+ void jumpButtonClicked();
+
+private:
+ void updateTime();
+ virtual bool eventFilter(QObject *o, QEvent *e);
+
+ static QString formatTime(int minutes, int seconds);
+
+ int m_itemTotalTime;
+ int m_itemCurrentTime;
+ bool m_showTimeRemaining;
+
+ KSqueezedTextLabel *m_playlistLabel;
+ KSqueezedTextLabel *m_trackLabel;
+ QLabel *m_itemTimeLabel;
+};
+
+#endif
diff --git a/juk/stringhash.h b/juk/stringhash.h
new file mode 100644
index 00000000..75992eec
--- /dev/null
+++ b/juk/stringhash.h
@@ -0,0 +1,312 @@
+/***************************************************************************
+ begin : Sun Feb 2 2003
+ copyright : (C) 2003 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef STRINGHASH_H
+#define STRINGHASH_H
+
+#include <qptrvector.h>
+
+#include "filehandle.h"
+
+/**
+ * A simple hash representing an (un-mapped) set of data.
+ */
+
+template <class T> class Hash
+{
+ friend class Iterator;
+public:
+
+ Hash() : m_table(m_tableSize) {}
+ ~Hash();
+
+ /**
+ * To combine two operations into one (that takes the same amount as each
+ * independantly) this inserts an item and returns true if the item was
+ * already in the set or false if it did not.
+ */
+ bool insert(T value);
+
+ /**
+ * Returns true if the set contains the item \a value.
+ */
+ bool contains(T value) const;
+
+ /**
+ * Removes an item. Returns true if the item was present and false if not.
+ */
+ bool remove(T value);
+
+ QValueList<T> values() const;
+
+ int hash(T key) const;
+
+ static inline int tableSize() { return m_tableSize; }
+
+protected:
+
+ struct Node
+ {
+ Node(T value) : key(value), next(0) {}
+ T key;
+ Node *next;
+ };
+
+public:
+
+ class Iterator
+ {
+ friend class Hash<T>;
+ public:
+ Iterator(const Hash<T> &hash) : m_hash(hash), m_index(0), m_node(hash.m_table[0]) {}
+ const T &operator*() const { return m_node->key; }
+ T &operator*() { return m_node->key; }
+
+ bool operator==(const Iterator &it) const { return m_index == it.m_index && m_node == it.m_node; }
+ bool operator!=(const Iterator &it) const { return !(it == *this); }
+
+ T &operator++();
+
+ private:
+ const Hash<T> &m_hash;
+ int m_index;
+ Node *m_node;
+ };
+
+ Iterator begin() const
+ {
+ Iterator it(*this);
+ while(!it.m_node && it.m_index < m_tableSize - 1) {
+ it.m_index++;
+ it.m_node = m_table[it.m_index];
+ }
+
+ return it;
+ }
+
+ Iterator end() const
+ {
+ Iterator it(*this);
+ it.m_node = 0;
+ it.m_index = m_tableSize - 1;
+ return it;
+ }
+
+protected:
+
+ void deleteNode(Node *n);
+
+ QPtrVector<Node> m_table;
+ static const int m_tableSize = 5003;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// helper functions
+////////////////////////////////////////////////////////////////////////////////
+
+inline char hashStringAccess(const QString &in, int index)
+{
+ return in.unicode()[index].cell();
+}
+
+inline char hashStringAccess(const QCString &in, int index)
+{
+ return in[index];
+}
+
+// Based on QGDict's hash functions, Copyright (C) 1992-2000 Trolltech AS
+
+template <typename StringType>
+inline int hashString(const StringType &s)
+{
+ uint h = 0;
+ uint g;
+ for(uint i = 0; i < s.length(); i++)
+ {
+ h = (h << 4) + hashStringAccess(s, i);
+ if((g = h & 0xf0000000))
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ int index = h;
+ if(index < 0)
+ index = -index;
+ return index;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// specializations
+////////////////////////////////////////////////////////////////////////////////
+
+// StringHash
+
+template<> inline int Hash<QString>::hash(QString key) const
+{
+ return hashString(key) % tableSize();
+}
+typedef Hash<QString> StringHash;
+
+// PtrHash
+
+template<> inline int Hash<void *>::hash(void *key) const
+{
+ return long(key) % tableSize();
+}
+typedef Hash<void *> PtrHash;
+
+// FileHandleHash
+
+template<> inline int Hash<FileHandle>::hash(FileHandle key) const
+{
+ return hashString(key.absFilePath()) % tableSize();
+}
+
+class FileHandleHash : public Hash<FileHandle>
+{
+public:
+ FileHandleHash() : Hash<FileHandle>() {}
+
+ FileHandle value(const QString &key) const
+ {
+ int h = hashString(key) % tableSize();
+ Node *i = m_table[h];
+
+ while(i && i->key.absFilePath() != key)
+ i = i->next;
+
+ return i ? i->key : FileHandle::null();
+ }
+};
+
+
+////////////////////////////////////////////////////////////////////////////////
+// template method implementations
+////////////////////////////////////////////////////////////////////////////////
+
+template <class T>
+Hash<T>::~Hash()
+{
+ for(int i = 0; i < m_tableSize; i++)
+ deleteNode(m_table[i]);
+}
+
+template <class T>
+bool Hash<T>::insert(T value)
+{
+ int h = hash(value);
+ Node *i = m_table[h];
+ Node *j = 0;
+
+ while(i) {
+ if(i->key == value)
+ return true;
+ else {
+ j = i;
+ i = i->next;
+ }
+ }
+
+ if(j)
+ j->next = new Node(value);
+ else
+ m_table.insert(h, new Node(value));
+
+ return false;
+}
+
+template <class T>
+bool Hash<T>::contains(T value) const
+{
+ int h = hash(value);
+ Node *i = m_table[h];
+
+ while(i && i->key != value)
+ i = i->next;
+
+ return bool(i);
+}
+
+template <class T>
+bool Hash<T>::remove(T value)
+{
+ int h = hash(value);
+ Node *previous = 0;
+ Node *i = m_table[h];
+
+ while(i && i->key != value) {
+ previous = i;
+ i = i->next;
+ }
+
+ if(!i)
+ return false;
+
+ if(previous)
+ previous->next = i->next;
+ else {
+ if(i->next)
+ m_table.insert(h, i->next);
+ else
+ m_table.remove(h);
+ }
+
+ delete i;
+
+ return true;
+}
+
+template <class T>
+QValueList<T> Hash<T>::values() const
+{
+ QValueList<T> l;
+
+ Node *n;
+
+ for(int i = 0; i < tableSize(); i++) {
+ n = m_table[i];
+ while(n) {
+ l.append(n->key);
+ n = n->next;
+ }
+ }
+
+ return l;
+}
+
+template <class T>
+void Hash<T>::deleteNode(Node *n)
+{
+ if(n) {
+ deleteNode(n->next);
+ delete n;
+ }
+}
+
+template <class T>
+T &Hash<T>::Iterator::operator++()
+{
+ if(m_node)
+ m_node = m_node->next;
+
+ while(!m_node && m_index < m_tableSize - 1) {
+ m_index++;
+ m_node = m_hash.m_table[m_index];
+ }
+
+ return m_node->key;
+}
+
+
+#endif
diff --git a/juk/stringshare.cpp b/juk/stringshare.cpp
new file mode 100644
index 00000000..ddd6a1ba
--- /dev/null
+++ b/juk/stringshare.cpp
@@ -0,0 +1,73 @@
+/***************************************************************************
+ begin : Sat Oct 25 2003
+ copyright : (C) 2003 by Maksim Orlovich
+ email : maksim.orlovich@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+#include "stringshare.h"
+#include "stringhash.h"
+
+const int SIZE = 5003;
+
+StringShare::Data* StringShare::s_data = 0;
+
+/**
+ * We store the strings in two simple direct-mapped (i.e. no collision handling,
+ * just replace) hashes, which contain strings or null objects. This costs only
+ * 4 bytes per slot on 32-bit archs, so with the default constant size we only
+ * really use 40K or so.
+ *
+ * The end result is that many strings end up pointing to the same underlying data
+ * object, instead of each one having its own little copy.
+ */
+
+struct StringShare::Data
+{
+ QString qstringHash [SIZE];
+ QCString qcstringHash[SIZE];
+};
+
+StringShare::Data* StringShare::data()
+{
+ if (!s_data)
+ s_data = new Data;
+ return s_data;
+}
+
+QString StringShare::tryShare(const QString& in)
+{
+ int index = hashString(in) % SIZE;
+
+ Data* dat = data();
+ if (dat->qstringHash[index] == in) //Match
+ return dat->qstringHash[index];
+ else
+ {
+ //Else replace whatever was there before
+ dat->qstringHash[index] = in;
+ return in;
+ }
+}
+
+QCString StringShare::tryShare(const QCString& in)
+{
+ int index = hashString(in) % SIZE;
+
+ Data* dat = data();
+ if (dat->qcstringHash[index] == in) //Match
+ return dat->qcstringHash[index];
+ else
+ {
+ //Else replace whatever was there before
+ dat->qcstringHash[index] = in;
+ return in;
+ }
+}
diff --git a/juk/stringshare.h b/juk/stringshare.h
new file mode 100644
index 00000000..743b2c1e
--- /dev/null
+++ b/juk/stringshare.h
@@ -0,0 +1,37 @@
+/***************************************************************************
+ begin : Sat Oct 25 2003
+ copyright : (C) 2003 by Maksim Orlovich
+ email : maksim.orlovich@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef STRING_SHARE_H
+#define STRING_SHARE_H
+
+#include <qstring.h>
+
+/**
+ This class attempts to normalize repeated occurances of strings to use
+ the same shared object, if possible, by using a small hash
+*/
+class StringShare
+{
+ struct Data;
+public:
+ static QString tryShare(const QString& in);
+ static QCString tryShare(const QCString& in);
+
+private:
+ static Data* data();
+ static Data* s_data;
+};
+
+#endif
diff --git a/juk/systemtray.cpp b/juk/systemtray.cpp
new file mode 100644
index 00000000..1691bc91
--- /dev/null
+++ b/juk/systemtray.cpp
@@ -0,0 +1,639 @@
+/***************************************************************************
+ copyright : (C) 2002 by Daniel Molkentin
+ email : molkentin@kde.org
+
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kpassivepopup.h>
+#include <kiconeffect.h>
+#include <kaction.h>
+#include <kpopupmenu.h>
+#include <kglobalsettings.h>
+#include <kdebug.h>
+
+#include <qvbox.h>
+#include <qtimer.h>
+#include <qcolor.h>
+#include <qpushbutton.h>
+#include <qtooltip.h>
+#include <qpainter.h>
+#include <qvaluevector.h>
+#include <qstylesheet.h>
+#include <qpalette.h>
+
+#include <netwm.h>
+
+#include "tag.h"
+#include "systemtray.h"
+#include "actioncollection.h"
+#include "playermanager.h"
+#include "collectionlist.h"
+#include "coverinfo.h"
+
+using namespace ActionCollection;
+
+static bool copyImage(QImage &dest, QImage &src, int x, int y);
+
+class FlickerFreeLabel : public QLabel
+{
+public:
+ FlickerFreeLabel(const QString &text, QWidget *parent, const char *name = 0) :
+ QLabel(text, parent, name)
+ {
+ m_textColor = paletteForegroundColor();
+ m_bgColor = parentWidget()->paletteBackgroundColor();
+ setBackgroundMode(NoBackground);
+ }
+
+ QColor textColor() const
+ {
+ return m_textColor;
+ }
+
+ QColor backgroundColor() const
+ {
+ return m_bgColor;
+ }
+
+protected:
+ virtual void drawContents(QPainter *p)
+ {
+ // We want to intercept the drawContents call and draw on a pixmap
+ // instead of the window to keep flicker to an absolute minimum.
+ // Since Qt doesn't refresh the background, we need to do so
+ // ourselves.
+
+ QPixmap pix(size());
+ QPainter pixPainter(&pix);
+
+ pixPainter.fillRect(rect(), m_bgColor);
+ QLabel::drawContents(&pixPainter);
+
+ bitBlt(p->device(), QPoint(0, 0), &pix, rect(), CopyROP);
+ }
+
+ private:
+ QColor m_textColor;
+ QColor m_bgColor;
+};
+
+PassiveInfo::PassiveInfo(QWidget *parent, const char *name) :
+ KPassivePopup(parent, name), m_timer(new QTimer), m_justDie(false)
+{
+ // I'm so sick and tired of KPassivePopup screwing this up
+ // that I'll just handle the timeout myself, thank you very much.
+ KPassivePopup::setTimeout(0);
+
+ connect(m_timer, SIGNAL(timeout()), SLOT(timerExpired()));
+}
+
+void PassiveInfo::setTimeout(int delay)
+{
+ m_timer->changeInterval(delay);
+}
+
+void PassiveInfo::show()
+{
+ KPassivePopup::show();
+ m_timer->start(3500);
+}
+
+void PassiveInfo::timerExpired()
+{
+ // If m_justDie is set, we should just go, otherwise we should emit the
+ // signal and wait for the system tray to delete us.
+ if(m_justDie)
+ hide();
+ else
+ emit timeExpired();
+}
+
+void PassiveInfo::enterEvent(QEvent *)
+{
+ m_timer->stop();
+ emit mouseEntered();
+}
+
+void PassiveInfo::leaveEvent(QEvent *)
+{
+ m_justDie = true;
+ m_timer->start(50);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+SystemTray::SystemTray(QWidget *parent, const char *name) : KSystemTray(parent, name),
+ m_popup(0),
+ m_fadeTimer(0),
+ m_fade(true)
+
+{
+ // This should be initialized to the number of labels that are used.
+
+ m_labels.reserve(3);
+
+ m_appPix = loadIcon("juk_dock");
+
+ m_playPix = createPixmap("player_play");
+ m_pausePix = createPixmap("player_pause");
+
+ m_forwardPix = loadIcon("player_end");
+ m_backPix = loadIcon("player_start");
+
+ setPixmap(m_appPix);
+
+ setToolTip();
+
+ // Just create this here so that it show up in the DCOP interface and the key
+ // bindings dialog.
+
+ new KAction(i18n("Redisplay Popup"), KShortcut(), this,
+ SLOT(slotPlay()), actions(), "showPopup");
+
+ KPopupMenu *cm = contextMenu();
+
+ connect(PlayerManager::instance(), SIGNAL(signalPlay()), this, SLOT(slotPlay()));
+ connect(PlayerManager::instance(), SIGNAL(signalPause()), this, SLOT(slotPause()));
+ connect(PlayerManager::instance(), SIGNAL(signalStop()), this, SLOT(slotStop()));
+
+ action("play")->plug(cm);
+ action("pause")->plug(cm);
+ action("stop")->plug(cm);
+ action("forward")->plug(cm);
+ action("back")->plug(cm);
+
+ cm->insertSeparator();
+
+ // Pity the actionCollection doesn't keep track of what sub-menus it has.
+
+ KActionMenu *menu = new KActionMenu(i18n("&Random Play"), this);
+ menu->insert(action("disableRandomPlay"));
+ menu->insert(action("randomPlay"));
+ menu->insert(action("albumRandomPlay"));
+ menu->plug(cm);
+
+ action("togglePopups")->plug(cm);
+
+ m_fadeTimer = new QTimer(this, "systrayFadeTimer");
+ connect(m_fadeTimer, SIGNAL(timeout()), SLOT(slotNextStep()));
+
+ if(PlayerManager::instance()->playing())
+ slotPlay();
+ else if(PlayerManager::instance()->paused())
+ slotPause();
+}
+
+SystemTray::~SystemTray()
+{
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public slots
+////////////////////////////////////////////////////////////////////////////////
+
+void SystemTray::slotPlay()
+{
+ if(!PlayerManager::instance()->playing())
+ return;
+
+ QPixmap cover = PlayerManager::instance()->playingFile().coverInfo()->pixmap(CoverInfo::Thumbnail);
+
+ setPixmap(m_playPix);
+ setToolTip(PlayerManager::instance()->playingString(), cover);
+ createPopup();
+}
+
+void SystemTray::slotTogglePopup()
+{
+ if(m_popup && m_popup->view()->isVisible())
+ m_popup->setTimeout(50);
+ else
+ slotPlay();
+}
+
+void SystemTray::slotPopupLargeCover()
+{
+ if(!PlayerManager::instance()->playing())
+ return;
+
+ FileHandle playingFile = PlayerManager::instance()->playingFile();
+ playingFile.coverInfo()->popup();
+}
+
+void SystemTray::slotStop()
+{
+ setPixmap(m_appPix);
+ setToolTip();
+
+ delete m_popup;
+ m_popup = 0;
+}
+
+void SystemTray::slotPopupDestroyed()
+{
+ for(unsigned i = 0; i < m_labels.capacity(); ++i)
+ m_labels[i] = 0;
+}
+
+void SystemTray::slotNextStep()
+{
+ QColor result;
+
+ ++m_step;
+
+ // If we're not fading, immediately show the labels
+ if(!m_fade)
+ m_step = STEPS;
+
+ result = interpolateColor(m_step);
+
+ for(unsigned i = 0; i < m_labels.capacity() && m_labels[i]; ++i)
+ m_labels[i]->setPaletteForegroundColor(result);
+
+ if(m_step == STEPS) {
+ m_step = 0;
+ m_fadeTimer->stop();
+ emit fadeDone();
+ }
+}
+
+void SystemTray::slotFadeOut()
+{
+ m_startColor = m_labels[0]->textColor();
+ m_endColor = m_labels[0]->backgroundColor();
+
+ connect(this, SIGNAL(fadeDone()), m_popup, SLOT(hide()));
+ connect(m_popup, SIGNAL(mouseEntered()), this, SLOT(slotMouseInPopup()));
+ m_fadeTimer->start(1500 / STEPS);
+}
+
+// If we receive this signal, it's because we were called during fade out.
+// That means there is a single shot timer about to call slotNextStep, so we
+// don't have to do it ourselves.
+void SystemTray::slotMouseInPopup()
+{
+ m_endColor = m_labels[0]->textColor();
+ disconnect(SIGNAL(fadeDone()));
+
+ m_step = STEPS - 1; // Simulate end of fade to solid text
+ slotNextStep();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private methods
+////////////////////////////////////////////////////////////////////////////////
+
+QVBox *SystemTray::createPopupLayout(QWidget *parent, const FileHandle &file)
+{
+ QVBox *infoBox = 0;
+
+ if(buttonsToLeft()) {
+
+ // They go to the left because JuK is on that side
+
+ createButtonBox(parent);
+ addSeparatorLine(parent);
+
+ infoBox = new QVBox(parent);
+
+ // Another line, and the cover, if there's a cover, and if
+ // it's selected to be shown
+
+ if(file.coverInfo()->hasCover()) {
+ addSeparatorLine(parent);
+ addCoverButton(parent, file.coverInfo()->pixmap(CoverInfo::Thumbnail));
+ }
+ }
+ else {
+
+ // Like above, but reversed.
+
+ if(file.coverInfo()->hasCover()) {
+ addCoverButton(parent, file.coverInfo()->pixmap(CoverInfo::Thumbnail));
+ addSeparatorLine(parent);
+ }
+
+ infoBox = new QVBox(parent);
+
+ addSeparatorLine(parent);
+ createButtonBox(parent);
+ }
+
+ infoBox->setSpacing(3);
+ infoBox->setMargin(3);
+ return infoBox;
+}
+
+void SystemTray::createPopup()
+{
+ FileHandle playingFile = PlayerManager::instance()->playingFile();
+ Tag *playingInfo = playingFile.tag();
+
+ // If the action exists and it's checked, do our stuff
+
+ if(!action<KToggleAction>("togglePopups")->isChecked())
+ return;
+
+ delete m_popup;
+ m_popup = 0;
+ m_fadeTimer->stop();
+
+ // This will be reset after this function call by slot(Forward|Back)
+ // so it's safe to set it true here.
+ m_fade = true;
+ m_step = 0;
+
+ m_popup = new PassiveInfo(this);
+ connect(m_popup, SIGNAL(destroyed()), SLOT(slotPopupDestroyed()));
+ connect(m_popup, SIGNAL(timeExpired()), SLOT(slotFadeOut()));
+
+ QHBox *box = new QHBox(m_popup, "popupMainLayout");
+ box->setSpacing(15); // Add space between text and buttons
+
+ QVBox *infoBox = createPopupLayout(box, playingFile);
+
+ for(unsigned i = 0; i < m_labels.capacity(); ++i) {
+ m_labels[i] = new FlickerFreeLabel(" ", infoBox);
+ m_labels[i]->setAlignment(AlignRight | AlignVCenter);
+ }
+
+ // We don't want an autodelete popup. There are times when it will need
+ // to be hidden before the timeout.
+
+ m_popup->setAutoDelete(false);
+
+ // We have to set the text of the labels after all of the
+ // widgets have been added in order for the width to be calculated
+ // correctly.
+
+ int labelCount = 0;
+
+ QString title = QStyleSheet::escape(playingInfo->title());
+ m_labels[labelCount++]->setText(QString("<qt><nobr><h2>%1</h2></nobr><qt>").arg(title));
+
+ if(!playingInfo->artist().isEmpty())
+ m_labels[labelCount++]->setText(playingInfo->artist());
+
+ if(!playingInfo->album().isEmpty()) {
+ QString album = QStyleSheet::escape(playingInfo->album());
+ QString s = playingInfo->year() > 0
+ ? QString("<qt><nobr>%1 (%2)</nobr></qt>").arg(album).arg(playingInfo->year())
+ : QString("<qt><nobr>%1</nobr></qt>").arg(album);
+ m_labels[labelCount++]->setText(s);
+ }
+
+ m_startColor = m_labels[0]->backgroundColor();
+ m_endColor = m_labels[0]->textColor();
+
+ slotNextStep();
+ m_fadeTimer->start(1500 / STEPS);
+
+ m_popup->setView(box);
+ m_popup->show();
+}
+
+bool SystemTray::buttonsToLeft() const
+{
+ // The following code was nicked from kpassivepopup.cpp
+
+ NETWinInfo ni(qt_xdisplay(), winId(), qt_xrootwin(),
+ NET::WMIconGeometry | NET::WMKDESystemTrayWinFor);
+ NETRect frame, win;
+ ni.kdeGeometry(frame, win);
+
+ QRect bounds = KGlobalSettings::desktopGeometry(QPoint(win.pos.x, win.pos.y));
+
+ // This seems to accurately guess what side of the icon that
+ // KPassivePopup will popup on.
+ return(win.pos.x < bounds.center().x());
+}
+
+QPixmap SystemTray::createPixmap(const QString &pixName)
+{
+ QPixmap bgPix = m_appPix;
+ QPixmap fgPix = SmallIcon(pixName);
+
+ QImage bgImage = bgPix.convertToImage(); // Probably 22x22
+ QImage fgImage = fgPix.convertToImage(); // Should be 16x16
+
+ KIconEffect::semiTransparent(bgImage);
+ copyImage(bgImage, fgImage, (bgImage.width() - fgImage.width()) / 2,
+ (bgImage.height() - fgImage.height()) / 2);
+
+ bgPix.convertFromImage(bgImage);
+ return bgPix;
+}
+
+void SystemTray::createButtonBox(QWidget *parent)
+{
+ QVBox *buttonBox = new QVBox(parent);
+
+ buttonBox->setSpacing(3);
+
+ QPushButton *forwardButton = new QPushButton(m_forwardPix, 0, buttonBox, "popup_forward");
+ forwardButton->setFlat(true);
+ connect(forwardButton, SIGNAL(clicked()), SLOT(slotForward()));
+
+ QPushButton *backButton = new QPushButton(m_backPix, 0, buttonBox, "popup_back");
+ backButton->setFlat(true);
+ connect(backButton, SIGNAL(clicked()), SLOT(slotBack()));
+}
+
+/**
+ * What happens here is that the action->activate() call will end up invoking
+ * createPopup(), which sets m_fade to true. Before the text starts fading
+ * control returns to this function, which resets m_fade to false.
+ */
+void SystemTray::slotBack()
+{
+ action("back")->activate();
+ m_fade = false;
+}
+
+void SystemTray::slotForward()
+{
+ action("forward")->activate();
+ m_fade = false;
+}
+
+void SystemTray::addSeparatorLine(QWidget *parent)
+{
+ QFrame *line = new QFrame(parent);
+ line->setFrameShape(QFrame::VLine);
+
+ // Cover art takes up 80 pixels, make sure we take up at least 80 pixels
+ // even if we don't show the cover art for consistency.
+
+ line->setMinimumHeight(80);
+}
+
+void SystemTray::addCoverButton(QWidget *parent, const QPixmap &cover)
+{
+ QPushButton *coverButton = new QPushButton(parent);
+
+ coverButton->setPixmap(cover);
+ coverButton->setFixedSize(cover.size());
+ coverButton->setFlat(true);
+
+ connect(coverButton, SIGNAL(clicked()), this, SLOT(slotPopupLargeCover()));
+}
+
+QColor SystemTray::interpolateColor(int step, int steps)
+{
+ if(step < 0)
+ return m_startColor;
+ if(step >= steps)
+ return m_endColor;
+
+ // TODO: Perhaps the algorithm here could be better? For example, it might
+ // make sense to go rather quickly from start to end and then slow down
+ // the progression.
+ return QColor(
+ (step * m_endColor.red() + (steps - step) * m_startColor.red()) / steps,
+ (step * m_endColor.green() + (steps - step) * m_startColor.green()) / steps,
+ (step * m_endColor.blue() + (steps - step) * m_startColor.blue()) / steps
+ );
+}
+
+void SystemTray::setToolTip(const QString &tip, const QPixmap &cover)
+{
+ QToolTip::remove(this);
+
+ if(tip.isNull())
+ QToolTip::add(this, i18n("JuK"));
+ else {
+ QPixmap myCover = cover;
+ if(cover.isNull())
+ myCover = DesktopIcon("juk");
+
+ QImage coverImage = myCover.convertToImage();
+ if(coverImage.size().width() > 32 || coverImage.size().height() > 32)
+ coverImage = coverImage.smoothScale(32, 32);
+
+ QMimeSourceFactory::defaultFactory()->setImage("tipCover", coverImage);
+
+ QString html = i18n("%1 is Cover Art, %2 is the playing track, %3 is the appname",
+ "<center><table cellspacing=\"2\"><tr><td valign=\"middle\">%1</td>"
+ "<td valign=\"middle\">%2</td></tr></table><em>%3</em></center>");
+ html = html.arg("<img valign=\"middle\" src=\"tipCover\"");
+ html = html.arg(QString("<nobr>%1</nobr>").arg(tip), i18n("JuK"));
+
+ QToolTip::add(this, html);
+ }
+}
+
+void SystemTray::wheelEvent(QWheelEvent *e)
+{
+ if(e->orientation() == Horizontal)
+ return;
+
+ // I already know the type here, but this file doesn't (and I don't want it
+ // to) know about the JuK class, so a static_cast won't work, and I was told
+ // that a reinterpret_cast isn't portable when combined with multiple
+ // inheritance. (This is why I don't check the result.)
+
+ switch(e->state()) {
+ case ShiftButton:
+ if(e->delta() > 0)
+ action("volumeUp")->activate();
+ else
+ action("volumeDown")->activate();
+ break;
+ default:
+ if(e->delta() > 0)
+ action("forward")->activate();
+ else
+ action("back")->activate();
+ break;
+ }
+ e->accept();
+}
+
+/*
+ * Reimplemented this in order to use the middle mouse button
+ */
+void SystemTray::mousePressEvent(QMouseEvent *e)
+{
+ switch(e->button()) {
+ case LeftButton:
+ case RightButton:
+ default:
+ KSystemTray::mousePressEvent(e);
+ break;
+ case MidButton:
+ if(!rect().contains(e->pos()))
+ return;
+ if(action("pause")->isEnabled())
+ action("pause")->activate();
+ else
+ action("play")->activate();
+ break;
+ }
+}
+
+/*
+ * This function copies the entirety of src into dest, starting in
+ * dest at x and y. This function exists because I was unable to find
+ * a function like it in either QImage or kdefx
+ */
+
+static bool copyImage(QImage &dest, QImage &src, int x, int y)
+{
+ if(dest.depth() != src.depth())
+ return false;
+ if((x + src.width()) >= dest.width())
+ return false;
+ if((y + src.height()) >= dest.height())
+ return false;
+
+ // We want to use KIconEffect::overlay to do this, since it handles
+ // alpha, but the images need to be the same size. We can handle that.
+
+ QImage large_src(dest);
+
+ // It would perhaps be better to create large_src based on a size, but
+ // this is the easiest way to make a new image with the same depth, size,
+ // etc.
+
+ large_src.detach();
+
+ // However, we do have to specifically ensure that setAlphaBuffer is set
+ // to false
+
+ large_src.setAlphaBuffer(false);
+ large_src.fill(0); // All transparent pixels
+ large_src.setAlphaBuffer(true);
+
+ int w = src.width();
+ int h = src.height();
+ for(int dx = 0; dx < w; dx++)
+ for(int dy = 0; dy < h; dy++)
+ large_src.setPixel(dx + x, dy + y, src.pixel(dx, dy));
+
+ // Apply effect to image
+
+ KIconEffect::overlay(dest, large_src);
+
+ return true;
+}
+
+
+#include "systemtray.moc"
+
+// vim: et sw=4 ts=8
diff --git a/juk/systemtray.h b/juk/systemtray.h
new file mode 100644
index 00000000..8dd66a62
--- /dev/null
+++ b/juk/systemtray.h
@@ -0,0 +1,133 @@
+/***************************************************************************
+ copyright : (C) 2002 by Daniel Molkentin
+ email : molkentin@kde.org
+
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef SYSTEMTRAY_H
+#define SYSTEMTRAY_H
+
+#include <ksystemtray.h>
+#include <kpassivepopup.h>
+
+#include <qvaluevector.h>
+#include <qcolor.h>
+
+class FlickerFreeLabel;
+class QTimer;
+class QVBox;
+class FileHandle;
+
+/**
+ * Subclass of KPassivePopup intended to more easily support JuK's particular
+ * usage pattern, including things like staying open while under the mouse.
+ *
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ */
+class PassiveInfo : public KPassivePopup
+{
+ Q_OBJECT
+public:
+ PassiveInfo(QWidget *parent = 0, const char *name = 0);
+
+public slots:
+ void setTimeout(int delay);
+ virtual void show();
+
+signals:
+ void mouseEntered();
+ void timeExpired();
+
+protected:
+ virtual void enterEvent(QEvent *);
+ virtual void leaveEvent(QEvent *);
+
+private slots:
+ void timerExpired();
+
+private:
+ QTimer *m_timer;
+ bool m_justDie;
+};
+
+class SystemTray : public KSystemTray
+{
+ Q_OBJECT
+
+public:
+ SystemTray(QWidget *parent = 0, const char *name = 0);
+ virtual ~SystemTray();
+
+signals:
+ // Emitted when the fade process is complete.
+ void fadeDone();
+
+private:
+ static const int STEPS = 20; ///< Number of intermediate steps for fading.
+
+ virtual void wheelEvent(QWheelEvent *e);
+ void createPopup();
+ void setToolTip(const QString &tip = QString::null, const QPixmap &cover = QPixmap());
+ void mousePressEvent(QMouseEvent *e);
+ QPixmap createPixmap(const QString &pixName);
+
+ // Returns true if the popup will need to have its buttons on the left
+ // (because the JuK icon is on the left side of the screen.
+ bool buttonsToLeft() const;
+
+ void createButtonBox(QWidget *parent);
+
+ // Creates the widget layout for the popup, returning the QVBox that
+ // holds the text labels. Uses buttonsToLeft() to figure out which
+ // order to create them in. @p file is used to grab the cover.
+ QVBox *createPopupLayout(QWidget *parent, const FileHandle &file);
+
+ void addSeparatorLine(QWidget *parent);
+ void addCoverButton(QWidget *parent, const QPixmap &cover);
+
+ // Interpolates from start color to end color. If @p step == 0, then
+ // m_startColor is returned, while @p step == @steps returns
+ // m_endColor.
+ QColor interpolateColor(int step, int steps = STEPS);
+
+private slots:
+ void slotPlay();
+ void slotTogglePopup();
+ void slotPause() { setPixmap(m_pausePix); }
+ void slotStop();
+ void slotPopupDestroyed();
+ void slotNextStep(); ///< This is the fading routine.
+ void slotPopupLargeCover();
+ void slotForward();
+ void slotBack();
+ void slotFadeOut(); ///< Fades out the text
+ void slotMouseInPopup(); ///< Forces the text back to its normal color.
+
+private:
+ QPixmap m_playPix;
+ QPixmap m_pausePix;
+ QPixmap m_currentPix;
+ QPixmap m_backPix;
+ QPixmap m_forwardPix;
+ QPixmap m_appPix;
+ QColor m_startColor, m_endColor;
+
+ PassiveInfo *m_popup;
+ QValueVector<FlickerFreeLabel *> m_labels;
+ QTimer *m_fadeTimer;
+ int m_step;
+ bool m_fade;
+};
+
+#endif // SYSTEMTRAY_H
diff --git a/juk/tag.cpp b/juk/tag.cpp
new file mode 100644
index 00000000..972b9284
--- /dev/null
+++ b/juk/tag.cpp
@@ -0,0 +1,279 @@
+/***************************************************************************
+ begin : Sun Feb 17 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kdebug.h>
+
+#include <qregexp.h>
+#include <qfile.h>
+
+#include <taglib/tag.h>
+#include <taglib/mpegfile.h>
+#include <taglib/vorbisfile.h>
+#include <taglib/flacfile.h>
+#include <taglib/xiphcomment.h>
+#include <taglib/id3v2framefactory.h>
+
+#if (TAGLIB_MAJOR_VERSION > 1) || \
+ ((TAGLIB_MAJOR_VERSION == 1) && (TAGLIB_MINOR_VERSION >= 2))
+#include <taglib/oggflacfile.h>
+#define TAGLIB_1_2
+#endif
+#if (TAGLIB_MAJOR_VERSION > 1) || \
+ ((TAGLIB_MAJOR_VERSION == 1) && (TAGLIB_MINOR_VERSION >= 3))
+#include <taglib/mpcfile.h>
+#define TAGLIB_1_3
+#endif
+
+#include "cache.h"
+#include "tag.h"
+#include "mediafiles.h"
+#include "stringshare.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+
+Tag::Tag(const QString &fileName) :
+ m_fileName(fileName),
+ m_track(0),
+ m_year(0),
+ m_seconds(0),
+ m_bitrate(0),
+ m_isValid(false)
+{
+ // using qDebug here since we want this to show up in non-debug builds as well
+
+ qDebug("Reading tag for %s", fileName.local8Bit().data());
+
+ if(MediaFiles::isMP3(fileName)) {
+ TagLib::MPEG::File file(QFile::encodeName(fileName).data());
+ if(file.isValid())
+ setup(&file);
+ }
+
+ else if(MediaFiles::isFLAC(fileName)) {
+ TagLib::FLAC::File file(QFile::encodeName(fileName).data());
+ if(file.isValid())
+ setup(&file);
+ }
+#ifdef TAGLIB_1_3
+ else if(MediaFiles::isMPC(fileName)) {
+ kdDebug(65432) << "Trying to resolve Musepack file" << endl;
+ TagLib::MPC::File file(QFile::encodeName(fileName).data());
+ if(file.isValid())
+ setup(&file);
+ }
+#endif
+#ifdef TAGLIB_1_2
+ else if(MediaFiles::isOggFLAC(fileName)) {
+ TagLib::Ogg::FLAC::File file(QFile::encodeName(fileName).data());
+ if(file.isValid())
+ setup(&file);
+ }
+#endif
+ else if(MediaFiles::isVorbis(fileName)) {
+ TagLib::Vorbis::File file(QFile::encodeName(fileName).data());
+ if(file.isValid())
+ setup(&file);
+ }
+
+ else {
+ kdError(65432) << "Couldn't resolve the mime type of \"" <<
+ fileName << "\" -- this shouldn't happen." << endl;
+ }
+}
+
+bool Tag::save()
+{
+ bool result;
+ TagLib::ID3v2::FrameFactory::instance()->setDefaultTextEncoding(TagLib::String::UTF8);
+
+ TagLib::File *file = 0;
+
+ if(MediaFiles::isMP3(m_fileName))
+ file = new TagLib::MPEG::File(QFile::encodeName(m_fileName).data());
+ else if(MediaFiles::isFLAC(m_fileName))
+ file = new TagLib::FLAC::File(QFile::encodeName(m_fileName).data());
+#ifdef TAGLIB_1_3
+ else if(MediaFiles::isMPC(m_fileName))
+ file = new TagLib::MPC::File(QFile::encodeName(m_fileName).data());
+#endif
+#ifdef TAGLIB_1_2
+ else if(MediaFiles::isOggFLAC(m_fileName))
+ file = new TagLib::Ogg::FLAC::File(QFile::encodeName(m_fileName).data());
+#endif
+ else if(MediaFiles::isVorbis(m_fileName))
+ file = new TagLib::Vorbis::File(QFile::encodeName(m_fileName).data());
+
+ if(file && file->isValid() && file->tag() && !file->readOnly()) {
+ file->tag()->setTitle(QStringToTString(m_title));
+ file->tag()->setArtist(QStringToTString(m_artist));
+ file->tag()->setAlbum(QStringToTString(m_album));
+ file->tag()->setGenre(QStringToTString(m_genre));
+ file->tag()->setComment(QStringToTString(m_comment));
+ file->tag()->setTrack(m_track);
+ file->tag()->setYear(m_year);
+#ifdef TAGLIB_1_2
+ result = file->save();
+#else
+ file->save();
+ result = true;
+#endif
+ }
+ else {
+ kdError(65432) << "Couldn't save file." << endl;
+ result = false;
+ }
+
+ delete file;
+ return result;
+}
+
+CacheDataStream &Tag::read(CacheDataStream &s)
+{
+ switch(s.cacheVersion()) {
+ case 1: {
+ Q_INT32 track;
+ Q_INT32 year;
+ Q_INT32 bitrate;
+ Q_INT32 seconds;
+
+ s >> m_title
+ >> m_artist
+ >> m_album
+ >> m_genre
+ >> track
+ >> year
+ >> m_comment
+ >> bitrate
+ >> m_lengthString
+ >> seconds;
+
+ m_track = track;
+ m_year = year;
+ m_bitrate = bitrate;
+ m_seconds = seconds;
+ break;
+ }
+ default: {
+ static QString dummyString;
+ static int dummyInt;
+ QString bitrateString;
+
+ s >> dummyInt
+ >> m_title
+ >> m_artist
+ >> m_album
+ >> m_genre
+ >> dummyInt
+ >> m_track
+ >> dummyString
+ >> m_year
+ >> dummyString
+ >> m_comment
+ >> bitrateString
+ >> m_lengthString
+ >> m_seconds
+ >> dummyString;
+
+ bool ok;
+ m_bitrate = bitrateString.toInt(&ok);
+ if(!ok)
+ m_bitrate = 0;
+ break;
+ }
+ }
+
+ // Try to reduce memory usage: share tags that frequently repeat, squeeze others
+
+ m_title.squeeze();
+ m_lengthString.squeeze();
+
+ m_comment = StringShare::tryShare(m_comment);
+ m_artist = StringShare::tryShare(m_artist);
+ m_album = StringShare::tryShare(m_album);
+ m_genre = StringShare::tryShare(m_genre);
+
+ return s;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private methods
+////////////////////////////////////////////////////////////////////////////////
+
+Tag::Tag(const QString &fileName, bool) :
+ m_fileName(fileName),
+ m_track(0),
+ m_year(0),
+ m_seconds(0),
+ m_bitrate(0),
+ m_isValid(true)
+{
+
+}
+
+void Tag::setup(TagLib::File *file)
+{
+ m_title = TStringToQString(file->tag()->title()).stripWhiteSpace();
+ m_artist = TStringToQString(file->tag()->artist()).stripWhiteSpace();
+ m_album = TStringToQString(file->tag()->album()).stripWhiteSpace();
+ m_genre = TStringToQString(file->tag()->genre()).stripWhiteSpace();
+ m_comment = TStringToQString(file->tag()->comment()).stripWhiteSpace();
+
+ m_track = file->tag()->track();
+ m_year = file->tag()->year();
+
+ m_seconds = file->audioProperties()->length();
+ m_bitrate = file->audioProperties()->bitrate();
+
+ const int seconds = m_seconds % 60;
+ const int minutes = (m_seconds - seconds) / 60;
+
+ m_lengthString = QString::number(minutes) + (seconds >= 10 ? ":" : ":0") + QString::number(seconds);
+
+ if(m_title.isEmpty()) {
+ int i = m_fileName.findRev('/');
+ int j = m_fileName.findRev('.');
+ m_title = i > 0 ? m_fileName.mid(i + 1, j - i - 1) : m_fileName;
+ }
+
+ m_isValid = true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// related functions
+////////////////////////////////////////////////////////////////////////////////
+
+QDataStream &operator<<(QDataStream &s, const Tag &t)
+{
+ s << t.title()
+ << t.artist()
+ << t.album()
+ << t.genre()
+ << Q_INT32(t.track())
+ << Q_INT32(t.year())
+ << t.comment()
+ << Q_INT32(t.bitrate())
+ << t.lengthString()
+ << Q_INT32(t.seconds());
+
+ return s;
+}
+
+CacheDataStream &operator>>(CacheDataStream &s, Tag &t)
+{
+ return t.read(s);
+}
diff --git a/juk/tag.h b/juk/tag.h
new file mode 100644
index 00000000..66c01d1e
--- /dev/null
+++ b/juk/tag.h
@@ -0,0 +1,95 @@
+/***************************************************************************
+ begin : Sun Feb 17 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef TAG_H
+#define TAG_H
+
+#include <qfileinfo.h>
+
+namespace TagLib { class File; }
+
+class CacheDataStream;
+
+/*!
+ * This should really be called "metadata" and may at some point be titled as
+ * such. Right now it's mostly a Qt wrapper around TagLib.
+ */
+
+class Tag
+{
+ friend class FileHandle;
+public:
+ Tag(const QString &fileName);
+ /**
+ * Create an empty tag. Used in FileHandle for cache restoration.
+ */
+ Tag(const QString &fileName, bool);
+
+ bool save();
+
+ QString title() const { return m_title; }
+ QString artist() const { return m_artist; }
+ QString album() const { return m_album; }
+ QString genre() const { return m_genre; }
+ int track() const { return m_track; }
+ int year() const { return m_year; }
+ QString comment() const { return m_comment; }
+
+ QString fileName() const { return m_fileName; }
+
+ void setTitle(const QString &value) { m_title = value; }
+ void setArtist(const QString &value) { m_artist = value; }
+ void setAlbum(const QString &value) { m_album = value; }
+ void setGenre(const QString &value) { m_genre = value; }
+ void setTrack(int value) { m_track = value; }
+ void setYear(int value) { m_year = value; }
+ void setComment(const QString &value) { m_comment = value; }
+
+ void setFileName(const QString &value) { m_fileName = value; }
+
+ int seconds() const { return m_seconds; }
+ int bitrate() const { return m_bitrate; }
+
+ bool isValid() const { return m_isValid; }
+
+ /**
+ * As a convenience, since producing a length string from a number of second
+ * isn't a one liner, provide the lenght in string form.
+ */
+ QString lengthString() const { return m_lengthString; }
+ CacheDataStream &read(CacheDataStream &s);
+
+private:
+ void setup(TagLib::File *file);
+
+ QString m_fileName;
+ QString m_title;
+ QString m_artist;
+ QString m_album;
+ QString m_genre;
+ QString m_comment;
+ int m_track;
+ int m_year;
+ int m_seconds;
+ int m_bitrate;
+ QDateTime m_modificationTime;
+ QString m_lengthString;
+ bool m_isValid;
+};
+
+QDataStream &operator<<(QDataStream &s, const Tag &t);
+CacheDataStream &operator>>(CacheDataStream &s, Tag &t);
+
+#endif
diff --git a/juk/tageditor.cpp b/juk/tageditor.cpp
new file mode 100644
index 00000000..a9bc764d
--- /dev/null
+++ b/juk/tageditor.cpp
@@ -0,0 +1,800 @@
+/***************************************************************************
+ begin : Sat Sep 7 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "tageditor.h"
+#include "collectionlist.h"
+#include "playlistitem.h"
+#include "tag.h"
+#include "actioncollection.h"
+#include "tagtransactionmanager.h"
+
+#include <kcombobox.h>
+#include <klineedit.h>
+#include <knuminput.h>
+#include <keditcl.h>
+#include <kmessagebox.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <kactionclasses.h>
+
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qlayout.h>
+#include <qdir.h>
+#include <qvalidator.h>
+#include <qtooltip.h>
+#include <qeventloop.h>
+#include <qdict.h>
+
+#include <id3v1genres.h>
+
+#undef KeyRelease
+
+using namespace ActionCollection;
+
+class FileNameValidator : public QValidator
+{
+public:
+ FileNameValidator(QObject *parent, const char *name = 0) :
+ QValidator(parent, name) {}
+
+ virtual void fixup(QString &s) const
+ {
+ s.remove('/');
+ }
+
+ virtual State validate(QString &s, int &) const
+ {
+ if(s.find('/') != -1)
+ return Invalid;
+ return Acceptable;
+ }
+};
+
+class FileBoxToolTip : public QToolTip
+{
+public:
+ FileBoxToolTip(TagEditor *editor, QWidget *widget) :
+ QToolTip(widget), m_editor(editor) {}
+protected:
+ virtual void maybeTip(const QPoint &)
+ {
+ tip(parentWidget()->rect(), m_editor->items().first()->file().absFilePath());
+ }
+private:
+ TagEditor *m_editor;
+};
+
+class FixedHLayout : public QHBoxLayout
+{
+public:
+ FixedHLayout(QWidget *parent, int margin = 0, int spacing = -1, const char *name = 0) :
+ QHBoxLayout(parent, margin, spacing, name),
+ m_width(-1) {}
+ FixedHLayout(QLayout *parentLayout, int spacing = -1, const char *name = 0) :
+ QHBoxLayout(parentLayout, spacing, name),
+ m_width(-1) {}
+ void setWidth(int w = -1)
+ {
+ m_width = w == -1 ? QHBoxLayout::minimumSize().width() : w;
+ }
+ virtual QSize minimumSize() const
+ {
+ QSize s = QHBoxLayout::minimumSize();
+ s.setWidth(m_width);
+ return s;
+ }
+private:
+ int m_width;
+};
+
+class CollectionObserver : public PlaylistObserver
+{
+public:
+ CollectionObserver(TagEditor *parent) :
+ PlaylistObserver(CollectionList::instance()),
+ m_parent(parent)
+ {
+ }
+
+ virtual void updateData()
+ {
+ if(m_parent && m_parent->m_currentPlaylist && m_parent->isVisible())
+ m_parent->slotSetItems(m_parent->m_currentPlaylist->selectedItems());
+ }
+
+ virtual void updateCurrent() {}
+
+private:
+ TagEditor *m_parent;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+TagEditor::TagEditor(QWidget *parent, const char *name) :
+ QWidget(parent, name),
+ m_currentPlaylist(0),
+ m_observer(0),
+ m_performingSave(false)
+{
+ setupActions();
+ setupLayout();
+ readConfig();
+ m_dataChanged = false;
+ m_collectionChanged = false;
+}
+
+TagEditor::~TagEditor()
+{
+ delete m_observer;
+ saveConfig();
+}
+
+void TagEditor::setupObservers()
+{
+ m_observer = new CollectionObserver(this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public slots
+////////////////////////////////////////////////////////////////////////////////
+
+void TagEditor::slotSetItems(const PlaylistItemList &list)
+{
+ if(m_performingSave)
+ return;
+
+ // Store the playlist that we're setting because saveChangesPrompt
+ // can delete the PlaylistItems in list.
+
+ Playlist *itemPlaylist = 0;
+ if(!list.isEmpty())
+ itemPlaylist = list.first()->playlist();
+
+ bool hadPlaylist = m_currentPlaylist != 0;
+
+ saveChangesPrompt();
+
+ if(m_currentPlaylist) {
+ disconnect(m_currentPlaylist, SIGNAL(signalAboutToRemove(PlaylistItem *)),
+ this, SLOT(slotItemRemoved(PlaylistItem *)));
+ }
+
+ if(hadPlaylist && !m_currentPlaylist || !itemPlaylist) {
+ m_currentPlaylist = 0;
+ m_items.clear();
+ }
+ else {
+ m_currentPlaylist = itemPlaylist;
+
+ // We can't use list here, it may not be valid
+
+ m_items = itemPlaylist->selectedItems();
+ }
+
+ if(m_currentPlaylist) {
+ connect(m_currentPlaylist, SIGNAL(signalAboutToRemove(PlaylistItem *)),
+ this, SLOT(slotItemRemoved(PlaylistItem *)));
+ connect(m_currentPlaylist, SIGNAL(destroyed()), this, SLOT(slotPlaylistRemoved()));
+ }
+
+ if(isVisible())
+ slotRefresh();
+ else
+ m_collectionChanged = true;
+}
+
+void TagEditor::slotRefresh()
+{
+ // This method takes the list of currently selected m_items and tries to
+ // figure out how to show that in the tag editor. The current strategy --
+ // the most common case -- is to just process the first item. Then we
+ // check after that to see if there are other m_items and adjust accordingly.
+
+ if(m_items.isEmpty() || !m_items.first()->file().tag()) {
+ slotClear();
+ setEnabled(false);
+ return;
+ }
+
+ setEnabled(true);
+
+ PlaylistItem *item = m_items.first();
+
+ Q_ASSERT(item);
+
+ Tag *tag = item->file().tag();
+
+ QFileInfo fi(item->file().absFilePath());
+ if(!fi.isWritable() && m_items.count() == 1)
+ setEnabled(false);
+
+ m_artistNameBox->setEditText(tag->artist());
+ m_trackNameBox->setText(tag->title());
+ m_albumNameBox->setEditText(tag->album());
+
+ m_fileNameBox->setText(item->file().fileInfo().fileName());
+ new FileBoxToolTip(this, m_fileNameBox);
+ m_bitrateBox->setText(QString::number(tag->bitrate()));
+ m_lengthBox->setText(tag->lengthString());
+
+ if(m_genreList.findIndex(tag->genre()) >= 0)
+ m_genreBox->setCurrentItem(m_genreList.findIndex(tag->genre()) + 1);
+ else {
+ m_genreBox->setCurrentItem(0);
+ m_genreBox->setEditText(tag->genre());
+ }
+
+ m_trackSpin->setValue(tag->track());
+ m_yearSpin->setValue(tag->year());
+
+ m_commentBox->setText(tag->comment());
+
+ // Start at the second item, since we've already processed the first.
+
+ PlaylistItemList::Iterator it = m_items.begin();
+ ++it;
+
+ // If there is more than one item in the m_items that we're dealing with...
+
+ if(it != m_items.end()) {
+
+ QValueListIterator<QWidget *> hideIt = m_hideList.begin();
+ for(; hideIt != m_hideList.end(); ++hideIt)
+ (*hideIt)->hide();
+
+ BoxMap::Iterator boxIt = m_enableBoxes.begin();
+ for(; boxIt != m_enableBoxes.end(); boxIt++) {
+ (*boxIt)->setChecked(true);
+ (*boxIt)->show();
+ }
+
+ // Yep, this is ugly. Loop through all of the files checking to see
+ // if their fields are the same. If so, by default, enable their
+ // checkbox.
+
+ // Also, if there are more than 50 m_items, don't scan all of them.
+
+ if(m_items.count() > 50) {
+ m_enableBoxes[m_artistNameBox]->setChecked(false);
+ m_enableBoxes[m_trackNameBox]->setChecked(false);
+ m_enableBoxes[m_albumNameBox]->setChecked(false);
+ m_enableBoxes[m_genreBox]->setChecked(false);
+ m_enableBoxes[m_trackSpin]->setChecked(false);
+ m_enableBoxes[m_yearSpin]->setChecked(false);
+ m_enableBoxes[m_commentBox]->setChecked(false);
+ }
+ else {
+ for(; it != m_items.end(); ++it) {
+ tag = (*it)->file().tag();
+
+ if(tag) {
+
+ if(m_artistNameBox->currentText() != tag->artist() &&
+ m_enableBoxes.contains(m_artistNameBox))
+ {
+ m_artistNameBox->lineEdit()->clear();
+ m_enableBoxes[m_artistNameBox]->setChecked(false);
+ }
+ if(m_trackNameBox->text() != tag->title() &&
+ m_enableBoxes.contains(m_trackNameBox))
+ {
+ m_trackNameBox->clear();
+ m_enableBoxes[m_trackNameBox]->setChecked(false);
+ }
+ if(m_albumNameBox->currentText() != tag->album() &&
+ m_enableBoxes.contains(m_albumNameBox))
+ {
+ m_albumNameBox->lineEdit()->clear();
+ m_enableBoxes[m_albumNameBox]->setChecked(false);
+ }
+ if(m_genreBox->currentText() != tag->genre() &&
+ m_enableBoxes.contains(m_genreBox))
+ {
+ m_genreBox->lineEdit()->clear();
+ m_enableBoxes[m_genreBox]->setChecked(false);
+ }
+ if(m_trackSpin->value() != tag->track() &&
+ m_enableBoxes.contains(m_trackSpin))
+ {
+ m_trackSpin->setValue(0);
+ m_enableBoxes[m_trackSpin]->setChecked(false);
+ }
+ if(m_yearSpin->value() != tag->year() &&
+ m_enableBoxes.contains(m_yearSpin))
+ {
+ m_yearSpin->setValue(0);
+ m_enableBoxes[m_yearSpin]->setChecked(false);
+ }
+ if(m_commentBox->text() != tag->comment() &&
+ m_enableBoxes.contains(m_commentBox))
+ {
+ m_commentBox->clear();
+ m_enableBoxes[m_commentBox]->setChecked(false);
+ }
+ }
+ }
+ }
+ }
+ else {
+ // Clean up in the case that we are only handling one item.
+
+ QValueListIterator<QWidget *> showIt = m_hideList.begin();
+ for(; showIt != m_hideList.end(); ++showIt)
+ (*showIt)->show();
+
+ BoxMap::iterator boxIt = m_enableBoxes.begin();
+ for(; boxIt != m_enableBoxes.end(); boxIt++) {
+ (*boxIt)->setChecked(true);
+ (*boxIt)->hide();
+ }
+ }
+ m_dataChanged = false;
+}
+
+void TagEditor::slotClear()
+{
+ m_artistNameBox->lineEdit()->clear();
+ m_trackNameBox->clear();
+ m_albumNameBox->lineEdit()->clear();
+ m_genreBox->setCurrentItem(0);
+ m_fileNameBox->clear();
+ m_trackSpin->setValue(0);
+ m_yearSpin->setValue(0);
+ m_lengthBox->clear();
+ m_bitrateBox->clear();
+ m_commentBox->clear();
+}
+
+void TagEditor::slotUpdateCollection()
+{
+ if(isVisible())
+ updateCollection();
+ else
+ m_collectionChanged = true;
+}
+
+void TagEditor::updateCollection()
+{
+ m_collectionChanged = false;
+
+ CollectionList *list = CollectionList::instance();
+
+ if(!list)
+ return;
+
+ QStringList artistList = list->uniqueSet(CollectionList::Artists);
+ artistList.sort();
+ m_artistNameBox->listBox()->clear();
+ m_artistNameBox->listBox()->insertStringList(artistList);
+ m_artistNameBox->completionObject()->setItems(artistList);
+
+ QStringList albumList = list->uniqueSet(CollectionList::Albums);
+ albumList.sort();
+ m_albumNameBox->listBox()->clear();
+ m_albumNameBox->listBox()->insertStringList(albumList);
+ m_albumNameBox->completionObject()->setItems(albumList);
+
+ // Merge the list of genres found in tags with the standard ID3v1 set.
+
+ StringHash genreHash;
+
+ m_genreList = list->uniqueSet(CollectionList::Genres);
+
+ for(QStringList::ConstIterator it = m_genreList.begin(); it != m_genreList.end(); ++it)
+ genreHash.insert(*it);
+
+ TagLib::StringList genres = TagLib::ID3v1::genreList();
+
+ for(TagLib::StringList::ConstIterator it = genres.begin(); it != genres.end(); ++it)
+ genreHash.insert(TStringToQString((*it)));
+
+ m_genreList = genreHash.values();
+ m_genreList.sort();
+
+ m_genreBox->listBox()->clear();
+ m_genreBox->listBox()->insertItem(QString::null);
+ m_genreBox->listBox()->insertStringList(m_genreList);
+ m_genreBox->completionObject()->setItems(m_genreList);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void TagEditor::readConfig()
+{
+ // combo box completion modes
+
+ KConfigGroup config(KGlobal::config(), "TagEditor");
+ if(m_artistNameBox && m_albumNameBox) {
+ readCompletionMode(&config, m_artistNameBox, "ArtistNameBoxMode");
+ readCompletionMode(&config, m_albumNameBox, "AlbumNameBoxMode");
+ readCompletionMode(&config, m_genreBox, "GenreBoxMode");
+ }
+
+ bool show = config.readBoolEntry("Show", false);
+ action<KToggleAction>("showEditor")->setChecked(show);
+ setShown(show);
+
+ TagLib::StringList genres = TagLib::ID3v1::genreList();
+
+ for(TagLib::StringList::ConstIterator it = genres.begin(); it != genres.end(); ++it)
+ m_genreList.append(TStringToQString((*it)));
+ m_genreList.sort();
+
+ m_genreBox->clear();
+ m_genreBox->insertItem(QString::null);
+ m_genreBox->insertStringList(m_genreList);
+ m_genreBox->completionObject()->setItems(m_genreList);
+}
+
+void TagEditor::readCompletionMode(KConfigBase *config, KComboBox *box, const QString &key)
+{
+ KGlobalSettings::Completion mode =
+ KGlobalSettings::Completion(config->readNumEntry(key, KGlobalSettings::CompletionAuto));
+
+ box->setCompletionMode(mode);
+}
+
+void TagEditor::saveConfig()
+{
+ // combo box completion modes
+
+ KConfigGroup config(KGlobal::config(), "TagEditor");
+
+ if(m_artistNameBox && m_albumNameBox) {
+ config.writeEntry("ArtistNameBoxMode", m_artistNameBox->completionMode());
+ config.writeEntry("AlbumNameBoxMode", m_albumNameBox->completionMode());
+ config.writeEntry("GenreBoxMode", m_genreBox->completionMode());
+ }
+ config.writeEntry("Show", action<KToggleAction>("showEditor")->isChecked());
+}
+
+void TagEditor::setupActions()
+{
+ KToggleAction *show = new KToggleAction(i18n("Show &Tag Editor"), "edit", 0, actions(), "showEditor");
+ show->setCheckedState(i18n("Hide &Tag Editor"));
+ connect(show, SIGNAL(toggled(bool)), this, SLOT(setShown(bool)));
+
+ new KAction(i18n("&Save"), "filesave", "CTRL+t", this, SLOT(slotSave()), actions(), "saveItem");
+}
+
+void TagEditor::setupLayout()
+{
+ static const int horizontalSpacing = 12;
+ static const int verticalSpacing = 2;
+
+ QHBoxLayout *layout = new QHBoxLayout(this, 6, horizontalSpacing);
+
+ //////////////////////////////////////////////////////////////////////////////
+ // define two columns of the bottem layout
+ //////////////////////////////////////////////////////////////////////////////
+ QVBoxLayout *leftColumnLayout = new QVBoxLayout(layout, verticalSpacing);
+ QVBoxLayout *rightColumnLayout = new QVBoxLayout(layout, verticalSpacing);
+
+ layout->setStretchFactor(leftColumnLayout, 2);
+ layout->setStretchFactor(rightColumnLayout, 3);
+
+ //////////////////////////////////////////////////////////////////////////////
+ // put stuff in the left column -- all of the field names are class wide
+ //////////////////////////////////////////////////////////////////////////////
+ { // just for organization
+
+ m_artistNameBox = new KComboBox(true, this, "artistNameBox");
+ m_artistNameBox->setCompletionMode(KGlobalSettings::CompletionAuto);
+ addItem(i18n("&Artist name:"), m_artistNameBox, leftColumnLayout, "personal");
+
+ m_trackNameBox = new KLineEdit(this, "trackNameBox");
+ addItem(i18n("&Track name:"), m_trackNameBox, leftColumnLayout, "player_play");
+
+ m_albumNameBox = new KComboBox(true, this, "albumNameBox");
+ m_albumNameBox->setCompletionMode(KGlobalSettings::CompletionAuto);
+ addItem(i18n("Album &name:"), m_albumNameBox, leftColumnLayout, "cdrom_unmount");
+
+ m_genreBox = new KComboBox(true, this, "genreBox");
+ addItem(i18n("&Genre:"), m_genreBox, leftColumnLayout, "knotify");
+
+ // this fills the space at the bottem of the left column
+ leftColumnLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum,
+ QSizePolicy::Expanding));
+ }
+ //////////////////////////////////////////////////////////////////////////////
+ // put stuff in the right column
+ //////////////////////////////////////////////////////////////////////////////
+ { // just for organization
+
+ QHBoxLayout *fileNameLayout = new QHBoxLayout(rightColumnLayout,
+ horizontalSpacing);
+
+ m_fileNameBox = new KLineEdit(this, "fileNameBox");
+ m_fileNameBox->setValidator(new FileNameValidator(m_fileNameBox));
+
+ QLabel *fileNameIcon = new QLabel(this);
+ fileNameIcon->setPixmap(SmallIcon("sound"));
+ QWidget *fileNameLabel = addHidden(new QLabel(m_fileNameBox, i18n("&File name:"), this));
+
+ fileNameLayout->addWidget(addHidden(fileNameIcon));
+ fileNameLayout->addWidget(fileNameLabel);
+ fileNameLayout->setStretchFactor(fileNameIcon, 0);
+ fileNameLayout->setStretchFactor(fileNameLabel, 0);
+ fileNameLayout->insertStretch(-1, 1);
+ rightColumnLayout->addWidget(addHidden(m_fileNameBox));
+
+ { // lay out the track row
+ FixedHLayout *trackRowLayout = new FixedHLayout(rightColumnLayout,
+ horizontalSpacing);
+
+ m_trackSpin = new KIntSpinBox(0, 9999, 1, 0, 10, this, "trackSpin");
+ addItem(i18n("T&rack:"), m_trackSpin, trackRowLayout);
+ m_trackSpin->installEventFilter(this);
+
+ trackRowLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding,
+ QSizePolicy::Minimum));
+
+ m_yearSpin = new KIntSpinBox(0, 9999, 1, 0, 10, this, "yearSpin");
+ addItem(i18n("&Year:"), m_yearSpin, trackRowLayout);
+ m_yearSpin->installEventFilter(this);
+
+ trackRowLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding,
+ QSizePolicy::Minimum));
+
+ trackRowLayout->addWidget(addHidden(new QLabel(i18n("Length:"), this)));
+ m_lengthBox = new KLineEdit(this, "lengthBox");
+ // addItem(i18n("Length:"), m_lengthBox, trackRowLayout);
+ m_lengthBox->setMinimumWidth(fontMetrics().width("00:00") + trackRowLayout->spacing());
+ m_lengthBox->setMaximumWidth(50);
+ m_lengthBox->setAlignment(Qt::AlignRight);
+ m_lengthBox->setReadOnly(true);
+ trackRowLayout->addWidget(addHidden(m_lengthBox));
+
+ trackRowLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding,
+ QSizePolicy::Minimum));
+
+ trackRowLayout->addWidget(addHidden(new QLabel(i18n("Bitrate:"), this)));
+ m_bitrateBox = new KLineEdit(this, "bitrateBox");
+ // addItem(i18n("Bitrate:"), m_bitrateBox, trackRowLayout);
+ m_bitrateBox->setMinimumWidth(fontMetrics().width("000") + trackRowLayout->spacing());
+ m_bitrateBox->setMaximumWidth(50);
+ m_bitrateBox->setAlignment(Qt::AlignRight);
+ m_bitrateBox->setReadOnly(true);
+ trackRowLayout->addWidget(addHidden(m_bitrateBox));
+
+ trackRowLayout->setWidth();
+ }
+
+ m_commentBox = new KEdit(this, "commentBox");
+ m_commentBox->setTextFormat(Qt::PlainText);
+ addItem(i18n("&Comment:"), m_commentBox, rightColumnLayout, "edit");
+ fileNameLabel->setMinimumHeight(m_trackSpin->height());
+
+ }
+
+ connect(m_artistNameBox, SIGNAL(textChanged(const QString&)),
+ this, SLOT(slotDataChanged()));
+
+ connect(m_trackNameBox, SIGNAL(textChanged(const QString&)),
+ this, SLOT(slotDataChanged()));
+
+ connect(m_albumNameBox, SIGNAL(textChanged(const QString&)),
+ this, SLOT(slotDataChanged()));
+
+ connect(m_genreBox, SIGNAL(activated(int)),
+ this, SLOT(slotDataChanged()));
+
+ connect(m_genreBox, SIGNAL(textChanged(const QString&)),
+ this, SLOT(slotDataChanged()));
+
+ connect(m_fileNameBox, SIGNAL(textChanged(const QString&)),
+ this, SLOT(slotDataChanged()));
+
+ connect(m_yearSpin, SIGNAL(valueChanged(int)),
+ this, SLOT(slotDataChanged()));
+
+ connect(m_trackSpin, SIGNAL(valueChanged(int)),
+ this, SLOT(slotDataChanged()));
+
+ connect(m_commentBox, SIGNAL(textChanged()),
+ this, SLOT(slotDataChanged()));
+}
+
+void TagEditor::save(const PlaylistItemList &list)
+{
+ if(!list.isEmpty() && m_dataChanged) {
+
+ KApplication::setOverrideCursor(Qt::waitCursor);
+ m_dataChanged = false;
+ m_performingSave = true;
+
+ // The list variable can become corrupted if the playlist holding its
+ // items dies, which is possible as we edit tags. So we need to copy
+ // the end marker.
+
+ PlaylistItemList::ConstIterator end = list.end();
+
+ for(PlaylistItemList::ConstIterator it = list.begin(); it != end; /* Deliberatly missing */ ) {
+
+ // Process items before we being modifying tags, as the dynamic
+ // playlists will try to modify the file we edit if the tag changes
+ // due to our alterations here.
+
+ kapp->eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
+
+ PlaylistItem *item = *it;
+
+ // The playlist can be deleted from under us if this is the last
+ // item and we edit it so that it doesn't match the search, which
+ // means we can't increment the iterator, so let's do it now.
+
+ ++it;
+
+ QString fileName = item->file().fileInfo().dirPath() + QDir::separator() +
+ m_fileNameBox->text();
+ if(list.count() > 1)
+ fileName = item->file().fileInfo().absFilePath();
+
+ Tag *tag = TagTransactionManager::duplicateTag(item->file().tag(), fileName);
+
+ // A bit more ugliness. If there are multiple files that are
+ // being modified, they each have a "enabled" checkbox that
+ // says if that field is to be respected for the multiple
+ // files. We have to check to see if that is enabled before
+ // each field that we write.
+
+ if(m_enableBoxes[m_artistNameBox]->isOn())
+ tag->setArtist(m_artistNameBox->currentText());
+ if(m_enableBoxes[m_trackNameBox]->isOn())
+ tag->setTitle(m_trackNameBox->text());
+ if(m_enableBoxes[m_albumNameBox]->isOn())
+ tag->setAlbum(m_albumNameBox->currentText());
+ if(m_enableBoxes[m_trackSpin]->isOn()) {
+ if(m_trackSpin->text().isEmpty())
+ m_trackSpin->setValue(0);
+ tag->setTrack(m_trackSpin->value());
+ }
+ if(m_enableBoxes[m_yearSpin]->isOn()) {
+ if(m_yearSpin->text().isEmpty())
+ m_yearSpin->setValue(0);
+ tag->setYear(m_yearSpin->value());
+ }
+ if(m_enableBoxes[m_commentBox]->isOn())
+ tag->setComment(m_commentBox->text());
+
+ if(m_enableBoxes[m_genreBox]->isOn())
+ tag->setGenre(m_genreBox->currentText());
+
+ TagTransactionManager::instance()->changeTagOnItem(item, tag);
+ }
+
+ TagTransactionManager::instance()->commit();
+ CollectionList::instance()->dataChanged();
+ m_performingSave = false;
+ KApplication::restoreOverrideCursor();
+ }
+}
+
+void TagEditor::saveChangesPrompt()
+{
+ if(!isVisible() || !m_dataChanged || m_items.isEmpty())
+ return;
+
+ QStringList files;
+
+ for(PlaylistItemList::Iterator it = m_items.begin(); it != m_items.end(); ++it)
+ files.append((*it)->file().absFilePath());
+
+ if(KMessageBox::questionYesNoList(this,
+ i18n("Do you want to save your changes to:\n"),
+ files,
+ i18n("Save Changes"),
+ KStdGuiItem::save(),
+ KStdGuiItem::discard(),
+ "tagEditor_showSaveChangesBox") == KMessageBox::Yes)
+ {
+ save(m_items);
+ }
+}
+
+void TagEditor::addItem(const QString &text, QWidget *item, QBoxLayout *layout, const QString &iconName)
+{
+ if(!item || !layout)
+ return;
+
+ QLabel *label = new QLabel(item, text, this);
+ QLabel *iconLabel = new QLabel(item, 0, this);
+
+ if(!iconName.isNull())
+ iconLabel->setPixmap(SmallIcon(iconName));
+
+ QCheckBox *enableBox = new QCheckBox(i18n("Enable"), this);
+ enableBox->setChecked(true);
+
+ label->setMinimumHeight(enableBox->height());
+
+ if(layout->direction() == QBoxLayout::LeftToRight) {
+ layout->addWidget(iconLabel);
+ layout->addWidget(label);
+ layout->addWidget(item);
+ layout->addWidget(enableBox);
+ }
+ else {
+ QHBoxLayout *l = new QHBoxLayout(layout);
+
+ l->addWidget(iconLabel);
+ l->addWidget(label);
+ l->setStretchFactor(label, 1);
+
+ l->insertStretch(-1, 1);
+
+ l->addWidget(enableBox);
+ l->setStretchFactor(enableBox, 0);
+
+ layout->addWidget(item);
+ }
+
+ enableBox->hide();
+
+ connect(enableBox, SIGNAL(toggled(bool)), item, SLOT(setEnabled(bool)));
+ m_enableBoxes.insert(item, enableBox);
+}
+
+void TagEditor::showEvent(QShowEvent *e)
+{
+ if(m_collectionChanged) {
+ updateCollection();
+ slotRefresh();
+ }
+
+ QWidget::showEvent(e);
+}
+
+bool TagEditor::eventFilter(QObject *watched, QEvent *e)
+{
+ QKeyEvent *ke = static_cast<QKeyEvent*>(e);
+ if(watched->inherits("QSpinBox") && e->type() == QEvent::KeyRelease && ke->state() == 0)
+ slotDataChanged();
+
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private slots
+////////////////////////////////////////////////////////////////////////////////
+
+void TagEditor::slotDataChanged(bool c)
+{
+ m_dataChanged = c;
+}
+
+void TagEditor::slotItemRemoved(PlaylistItem *item)
+{
+ m_items.remove(item);
+ if(m_items.isEmpty())
+ slotRefresh();
+}
+
+void TagEditor::slotPlaylistDestroyed(Playlist *p)
+{
+ if(m_currentPlaylist == p) {
+ m_currentPlaylist = 0;
+ slotSetItems(PlaylistItemList());
+ }
+}
+
+#include "tageditor.moc"
diff --git a/juk/tageditor.h b/juk/tageditor.h
new file mode 100644
index 00000000..e9d9b1bf
--- /dev/null
+++ b/juk/tageditor.h
@@ -0,0 +1,118 @@
+/***************************************************************************
+ begin : Sat Sep 7 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef TAGEDITOR_H
+#define TAGEDITOR_H
+
+#include <qwidget.h>
+
+class KComboBox;
+class KLineEdit;
+class KIntSpinBox;
+class KEdit;
+class KPushButton;
+class KConfigBase;
+
+class QCheckBox;
+class QBoxLayout;
+
+class Playlist;
+class PlaylistItem;
+typedef QValueList<PlaylistItem *> PlaylistItemList;
+
+class CollectionObserver;
+
+class TagEditor : public QWidget
+{
+ Q_OBJECT
+
+public:
+ TagEditor(QWidget *parent = 0, const char *name = 0);
+ virtual ~TagEditor();
+ PlaylistItemList items() const { return m_items; }
+ void setupObservers();
+
+public slots:
+ void slotSave() { save(m_items); }
+ void slotSetItems(const PlaylistItemList &list);
+ void slotRefresh();
+ void slotClear();
+ void slotPlaylistDestroyed(Playlist *p);
+ /**
+ * Update collection if we're visible, or defer otherwise
+ */
+ void slotUpdateCollection();
+
+private:
+ void updateCollection();
+
+ void setupActions();
+ void setupLayout();
+ void readConfig();
+ void readCompletionMode(KConfigBase *config, KComboBox *box, const QString &key);
+ void saveConfig();
+ void save(const PlaylistItemList &list);
+ void saveChangesPrompt();
+ /**
+ * Adds an item to JuK's tagging layout. This handles the creation and
+ * placement of the "enable" box as well.
+ */
+ void addItem(const QString &text, QWidget *item, QBoxLayout *layout, const QString &iconName = QString::null);
+
+ /**
+ * Adds a widget to m_hideList and returns that widget.
+ */
+ QWidget *addHidden(QWidget *w) { m_hideList.append(w); return w; }
+
+ virtual void showEvent(QShowEvent *e);
+ virtual bool eventFilter(QObject *watched, QEvent *e);
+
+private slots:
+ void slotDataChanged(bool c = true);
+ void slotItemRemoved(PlaylistItem *item);
+ void slotPlaylistRemoved() { m_currentPlaylist = 0; }
+
+private:
+ typedef QMap<QWidget *, QCheckBox *> BoxMap;
+ BoxMap m_enableBoxes;
+
+ QStringList m_genreList;
+
+ KComboBox *m_artistNameBox;
+ KLineEdit *m_trackNameBox;
+ KComboBox *m_albumNameBox;
+ KComboBox *m_genreBox;
+ KLineEdit *m_fileNameBox;
+ KIntSpinBox *m_trackSpin;
+ KIntSpinBox *m_yearSpin;
+ KLineEdit *m_lengthBox;
+ KLineEdit *m_bitrateBox;
+ KEdit *m_commentBox;
+
+ QValueList<QWidget *> m_hideList;
+
+ PlaylistItemList m_items;
+ Playlist *m_currentPlaylist;
+
+ CollectionObserver *m_observer;
+
+ bool m_dataChanged;
+ bool m_collectionChanged;
+ bool m_performingSave;
+
+ friend class CollectionObserver;
+};
+
+#endif
diff --git a/juk/tagguesser.cpp b/juk/tagguesser.cpp
new file mode 100644
index 00000000..41795155
--- /dev/null
+++ b/juk/tagguesser.cpp
@@ -0,0 +1,218 @@
+/*
+ * tagguesser.cpp - (c) 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include "tagguesser.h"
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kmacroexpander.h>
+
+FileNameScheme::FileNameScheme(const QString &s)
+ : m_regExp(),
+ m_titleField(-1),
+ m_artistField(-1),
+ m_albumField(-1),
+ m_trackField(-1),
+ m_commentField(-1)
+{
+ int fieldNumber = 1;
+ int i = s.find('%');
+ while (i > -1) {
+ switch (s[ i + 1 ]) {
+ case 't': m_titleField = fieldNumber++;
+ break;
+ case 'a': m_artistField = fieldNumber++;
+ break;
+ case 'A': m_albumField = fieldNumber++;
+ break;
+ case 'T': m_trackField = fieldNumber++;
+ break;
+ case 'c': m_commentField = fieldNumber++;
+ break;
+ default:
+ break;
+ }
+ i = s.find('%', i + 1);
+ }
+ m_regExp.setPattern(composeRegExp(s));
+}
+
+bool FileNameScheme::matches(const QString &fileName) const
+{
+ /* Strip extension ('.mp3') because '.' may be part of a title, and thus
+ * does not work as a separator.
+ */
+ QString stripped = fileName;
+ stripped.truncate(stripped.findRev('.'));
+ return m_regExp.exactMatch(stripped);
+}
+
+QString FileNameScheme::title() const
+{
+ if(m_titleField == -1)
+ return QString::null;
+ return m_regExp.capturedTexts()[ m_titleField ];
+}
+
+QString FileNameScheme::artist() const
+{
+ if(m_artistField == -1)
+ return QString::null;
+ return m_regExp.capturedTexts()[ m_artistField ];
+}
+
+QString FileNameScheme::album() const
+{
+ if(m_albumField == -1)
+ return QString::null;
+ return m_regExp.capturedTexts()[ m_albumField ];
+}
+
+QString FileNameScheme::track() const
+{
+ if(m_trackField == -1)
+ return QString::null;
+ return m_regExp.capturedTexts()[ m_trackField ];
+}
+
+QString FileNameScheme::comment() const
+{
+ if(m_commentField == -1)
+ return QString::null;
+ return m_regExp.capturedTexts()[ m_commentField ];
+}
+
+QString FileNameScheme::composeRegExp(const QString &s) const
+{
+ QMap<QChar, QString> substitutions;
+
+ KConfigGroup config(KGlobal::config(), "TagGuesser");
+
+ substitutions[ 't' ] = config.readEntry("Title regexp", "([\\w\\s'&_,\\.]+)");
+ substitutions[ 'a' ] = config.readEntry("Artist regexp", "([\\w\\s'&_,\\.]+)");
+ substitutions[ 'A' ] = config.readEntry("Album regexp", "([\\w\\s'&_,\\.]+)");
+ substitutions[ 'T' ] = config.readEntry("Track regexp", "(\\d+)");
+ substitutions[ 'c' ] = config.readEntry("Comment regexp", "([\\w\\s_]+)");
+
+ QString regExp = QRegExp::escape(s.simplifyWhiteSpace());
+ regExp = ".*" + regExp;
+ regExp.replace(' ', "\\s+");
+ regExp = KMacroExpander::expandMacros(regExp, substitutions);
+ regExp += "[^/]*$";
+ return regExp;
+}
+
+QStringList TagGuesser::schemeStrings()
+{
+ QStringList schemes;
+
+ KConfigGroup config(KGlobal::config(), "TagGuesser");
+ schemes = config.readListEntry("Filename schemes");
+
+ if ( schemes.isEmpty() ) {
+ schemes += "%a - (%T) - %t [%c]";
+ schemes += "%a - (%T) - %t (%c)";
+ schemes += "%a - (%T) - %t";
+ schemes += "%a - [%T] - %t [%c]";
+ schemes += "%a - [%T] - %t (%c)";
+ schemes += "%a - [%T] - %t";
+ schemes += "%a - %T - %t [%c]";
+ schemes += "%a - %T - %t (%c)";
+ schemes += "%a - %T - %t";
+ schemes += "(%T) %a - %t [%c]";
+ schemes += "(%T) %a - %t (%c)";
+ schemes += "(%T) %a - %t";
+ schemes += "[%T] %a - %t [%c]";
+ schemes += "[%T] %a - %t (%c)";
+ schemes += "[%T] %a - %t";
+ schemes += "%T %a - %t [%c]";
+ schemes += "%T %a - %t (%c)";
+ schemes += "%T %a - %t";
+ schemes += "(%a) %t [%c]";
+ schemes += "(%a) %t (%c)";
+ schemes += "(%a) %t";
+ schemes += "%a - %t [%c]";
+ schemes += "%a - %t (%c)";
+ schemes += "%a - %t";
+ schemes += "%a/%A/[%T] %t [%c]";
+ schemes += "%a/%A/[%T] %t (%c)";
+ schemes += "%a/%A/[%T] %t";
+ }
+ return schemes;
+}
+
+void TagGuesser::setSchemeStrings(const QStringList &schemes)
+{
+ KConfig *cfg = kapp->config();
+ {
+ KConfigGroupSaver saver(cfg, "TagGuesser");
+ cfg->writeEntry("Filename schemes", schemes);
+ }
+ cfg->sync();
+}
+
+TagGuesser::TagGuesser()
+{
+ loadSchemes();
+}
+
+TagGuesser::TagGuesser(const QString &absFileName)
+{
+ loadSchemes();
+ guess(absFileName);
+}
+
+void TagGuesser::loadSchemes()
+{
+ const QStringList schemes = schemeStrings();
+ QStringList::ConstIterator it = schemes.begin();
+ QStringList::ConstIterator end = schemes.end();
+ for ( ; it != end; ++it )
+ m_schemes += FileNameScheme( *it );
+}
+
+void TagGuesser::guess(const QString &absFileName)
+{
+ m_title = m_artist = m_album = m_track = m_comment = QString::null;
+
+ FileNameScheme::List::ConstIterator it = m_schemes.begin();
+ FileNameScheme::List::ConstIterator end = m_schemes.end();
+ for (; it != end; ++it) {
+ const FileNameScheme schema(*it);
+ if(schema.matches(absFileName)) {
+ m_title = capitalizeWords(schema.title().replace('_', " ")).stripWhiteSpace();
+ m_artist = capitalizeWords(schema.artist().replace('_', " ")).stripWhiteSpace();
+ m_album = capitalizeWords(schema.album().replace('_', " ")).stripWhiteSpace();
+ m_track = schema.track().stripWhiteSpace();
+ m_comment = schema.comment().replace('_', " ").stripWhiteSpace();
+ break;
+ }
+ }
+}
+
+QString TagGuesser::capitalizeWords(const QString &s)
+{
+ if(s.isEmpty())
+ return s;
+
+ QString result = s;
+ result[ 0 ] = result[ 0 ].upper();
+
+ const QRegExp wordRegExp("\\s\\w");
+ int i = result.find( wordRegExp );
+ while ( i > -1 ) {
+ result[ i + 1 ] = result[ i + 1 ].upper();
+ i = result.find( wordRegExp, ++i );
+ }
+
+ return result;
+}
+
+// vim:ts=4:sw=4:noet
diff --git a/juk/tagguesser.h b/juk/tagguesser.h
new file mode 100644
index 00000000..eb040a4b
--- /dev/null
+++ b/juk/tagguesser.h
@@ -0,0 +1,74 @@
+/*
+ * tagguesser.h - (c) 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef TAGGUESSER_H
+#define TAGGUESSER_H
+
+#include <qregexp.h>
+
+class FileNameScheme
+{
+ public:
+ typedef QValueList<FileNameScheme> List;
+
+ FileNameScheme() { }
+ FileNameScheme(const QString &s);
+
+ bool matches(const QString &s) const;
+
+ QString title() const;
+ QString artist() const;
+ QString album() const;
+ QString track() const;
+ QString comment() const;
+
+ private:
+ QString composeRegExp(const QString &s) const;
+
+ mutable QRegExp m_regExp;
+ int m_titleField;
+ int m_artistField;
+ int m_albumField;
+ int m_trackField;
+ int m_commentField;
+};
+
+class TagGuesser
+{
+ public:
+
+ enum Type { FileName = 0, MusicBrainz = 1 };
+
+ static QStringList schemeStrings();
+ static void setSchemeStrings(const QStringList &schemes);
+
+ TagGuesser();
+ TagGuesser(const QString &absFileName);
+
+ void guess(const QString &absFileName);
+
+ QString title() const { return m_title; }
+ QString artist() const { return m_artist; }
+ QString album() const { return m_album; }
+ QString track() const { return m_track; }
+ QString comment() const { return m_comment; }
+
+ private:
+ void loadSchemes();
+ QString capitalizeWords(const QString &s);
+
+ FileNameScheme::List m_schemes;
+ QString m_title;
+ QString m_artist;
+ QString m_album;
+ QString m_track;
+ QString m_comment;
+};
+
+#endif // TAGGUESSER_H
+// vim:ts=4:sw=4:noet
diff --git a/juk/tagguesserconfigdlg.cpp b/juk/tagguesserconfigdlg.cpp
new file mode 100644
index 00000000..f3af1f43
--- /dev/null
+++ b/juk/tagguesserconfigdlg.cpp
@@ -0,0 +1,122 @@
+/*
+ * tagguesserconfigdlg.cpp - (c) 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include "tagguesser.h"
+#include "tagguesserconfigdlg.h"
+#include "tagguesserconfigdlgwidget.h"
+
+#include <kiconloader.h>
+#include <klistview.h>
+#include <klocale.h>
+#include <kpushbutton.h>
+#include <klineedit.h>
+#include <kapplication.h>
+
+#include <qtoolbutton.h>
+#include <qevent.h>
+
+TagGuesserConfigDlg::TagGuesserConfigDlg(QWidget *parent, const char *name)
+ : KDialogBase(parent, name, true, i18n("Tag Guesser Configuration"),
+ Ok | Cancel, Ok, true)
+{
+ m_child = new TagGuesserConfigDlgWidget(this, "child");
+ setMainWidget(m_child);
+
+ m_child->lvSchemes->setItemsRenameable(true);
+ m_child->lvSchemes->setSorting(-1);
+ m_child->lvSchemes->setDefaultRenameAction(QListView::Accept);
+ m_child->bMoveUp->setIconSet(BarIconSet("1uparrow"));
+ m_child->bMoveDown->setIconSet(BarIconSet("1downarrow"));
+
+ const QStringList schemes = TagGuesser::schemeStrings();
+ QStringList::ConstIterator it = schemes.begin();
+ QStringList::ConstIterator end = schemes.end();
+ for (; it != end; ++it) {
+ KListViewItem *item = new KListViewItem(m_child->lvSchemes, *it);
+ item->moveItem(m_child->lvSchemes->lastItem());
+ }
+
+ connect(m_child->lvSchemes, SIGNAL(currentChanged(QListViewItem *)),
+ this, SLOT(slotCurrentChanged(QListViewItem *)));
+ connect(m_child->lvSchemes, SIGNAL(doubleClicked(QListViewItem *, const QPoint &, int)),
+ this, SLOT(slotRenameItem(QListViewItem *, const QPoint &, int)));
+ connect(m_child->bMoveUp, SIGNAL(clicked()), this, SLOT(slotMoveUpClicked()));
+ connect(m_child->bMoveDown, SIGNAL(clicked()), this, SLOT(slotMoveDownClicked()));
+ connect(m_child->bAdd, SIGNAL(clicked()), this, SLOT(slotAddClicked()));
+ connect(m_child->bModify, SIGNAL(clicked()), this, SLOT(slotModifyClicked()));
+ connect(m_child->bRemove, SIGNAL(clicked()), this, SLOT(slotRemoveClicked()));
+
+ m_child->lvSchemes->setSelected(m_child->lvSchemes->firstChild(), true);
+ slotCurrentChanged(m_child->lvSchemes->currentItem());
+
+ resize( 400, 300 );
+}
+
+void TagGuesserConfigDlg::accept()
+{
+ if(m_child->lvSchemes->renameLineEdit()) {
+ QKeyEvent returnKeyPress(QEvent::KeyPress, Key_Return, 0, 0);
+ KApplication::sendEvent(m_child->lvSchemes->renameLineEdit(), &returnKeyPress);
+ }
+
+ QStringList schemes;
+ for (QListViewItem *it = m_child->lvSchemes->firstChild(); it; it = it->nextSibling())
+ schemes += it->text(0);
+ TagGuesser::setSchemeStrings(schemes);
+ KDialogBase::accept();
+}
+
+void TagGuesserConfigDlg::slotCurrentChanged(QListViewItem *item)
+{
+ m_child->bMoveUp->setEnabled(item != 0 && item->itemAbove() != 0);
+ m_child->bMoveDown->setEnabled(item != 0 && item->itemBelow() != 0);
+ m_child->bModify->setEnabled(item != 0);
+ m_child->bRemove->setEnabled(item != 0);
+}
+
+void TagGuesserConfigDlg::slotRenameItem(QListViewItem *item, const QPoint &, int c)
+{
+ m_child->lvSchemes->rename(item, c);
+}
+
+void TagGuesserConfigDlg::slotMoveUpClicked()
+{
+ QListViewItem *item = m_child->lvSchemes->currentItem();
+ if(item->itemAbove() == m_child->lvSchemes->firstChild())
+ item->itemAbove()->moveItem(item);
+ else
+ item->moveItem(item->itemAbove()->itemAbove());
+ m_child->lvSchemes->ensureItemVisible(item);
+ slotCurrentChanged(item);
+}
+
+void TagGuesserConfigDlg::slotMoveDownClicked()
+{
+ QListViewItem *item = m_child->lvSchemes->currentItem();
+ item->moveItem(item->itemBelow());
+ m_child->lvSchemes->ensureItemVisible(item);
+ slotCurrentChanged(item);
+}
+
+void TagGuesserConfigDlg::slotAddClicked()
+{
+ KListViewItem *item = new KListViewItem(m_child->lvSchemes);
+ m_child->lvSchemes->rename(item, 0);
+}
+
+void TagGuesserConfigDlg::slotModifyClicked()
+{
+ m_child->lvSchemes->rename(m_child->lvSchemes->currentItem(), 0);
+}
+
+void TagGuesserConfigDlg::slotRemoveClicked()
+{
+ delete m_child->lvSchemes->currentItem();
+}
+
+#include "tagguesserconfigdlg.moc"
diff --git a/juk/tagguesserconfigdlg.h b/juk/tagguesserconfigdlg.h
new file mode 100644
index 00000000..353e743a
--- /dev/null
+++ b/juk/tagguesserconfigdlg.h
@@ -0,0 +1,39 @@
+/*
+ * tagguesserconfigdlg.h - (c) 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef TAGGUESSERCONFIGDLG_H
+#define TAGGUESSERCONFIGDLG_H
+
+#include <kdialogbase.h>
+
+class QListViewItem;
+
+class TagGuesserConfigDlgWidget;
+class TagGuesserConfigDlg : public KDialogBase
+{
+ Q_OBJECT
+ public:
+ TagGuesserConfigDlg(QWidget *parent, const char *name = 0);
+
+ protected slots:
+ virtual void accept();
+
+ private slots:
+ void slotCurrentChanged(QListViewItem *item);
+ void slotRenameItem(QListViewItem *item, const QPoint &p, int c);
+ void slotMoveUpClicked();
+ void slotMoveDownClicked();
+ void slotAddClicked();
+ void slotModifyClicked();
+ void slotRemoveClicked();
+
+ private:
+ TagGuesserConfigDlgWidget *m_child;
+};
+
+#endif // TAGGUESSERCONFIGDLG_H
diff --git a/juk/tagguesserconfigdlgwidget.ui b/juk/tagguesserconfigdlgwidget.ui
new file mode 100644
index 00000000..25d86c0b
--- /dev/null
+++ b/juk/tagguesserconfigdlgwidget.ui
@@ -0,0 +1,159 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>TagGuesserConfigDlgWidget</class>
+<author>Frerich Raabe &lt;raabe@kde.org&gt;</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>Form1</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>600</width>
+ <height>480</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KListView" row="0" column="0" rowspan="5" colspan="1">
+ <column>
+ <property name="text">
+ <string>File Name Scheme</string>
+ </property>
+ <property name="clickable">
+ <bool>false</bool>
+ </property>
+ <property name="resizable">
+ <bool>false</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>lvSchemes</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Currently used file name schemes</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can see the currently configured file name schemes which the "Suggest" button in the tag editor uses to extract tag information from a file name. Each string may contain one of the following placeholders:&lt;ul&gt;
+&lt;li&gt;%t: Title&lt;/li&gt;
+&lt;li&gt;%a: Artist&lt;/li&gt;
+&lt;li&gt;%A: Album&lt;/li&gt;
+&lt;li&gt;%T: Track&lt;/li&gt;
+&lt;li&gt;%c: Comment&lt;/li&gt;
+&lt;/ul&gt;
+For example, the file name scheme "[%T] %a - %t" would match "[01] Deep Purple - Smoke on the water" but not "(Deep Purple) Smoke on the water". For that second name, you would use the scheme "(%a) %t".&lt;p/&gt;
+Note that the order in which the schemes appear in the list is relevant, since the tag guesser will go through the list from the top to the bottom, and use the first matching scheme.</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="1" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>bAdd</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Add</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Add a new scheme</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Press this button to add a new file name scheme to the end of the list.</string>
+ </property>
+ </widget>
+ <widget class="QToolButton" row="0" column="1">
+ <property name="name">
+ <cstring>bMoveUp</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Move scheme up</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Press this button to move the currently selected scheme one step upwards.</string>
+ </property>
+ </widget>
+ <widget class="QToolButton" row="0" column="3">
+ <property name="name">
+ <cstring>bMoveDown</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Move scheme down</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Press this button to move the currently selected scheme one step downwards.</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="2" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>bModify</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Modify</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Modify scheme</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Press this button to modify the currently selected scheme.</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="3" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>bRemove</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Remove scheme</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Press this button to remove the currently selected scheme from the list.</string>
+ </property>
+ </widget>
+ <spacer row="4" column="2">
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>130</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<includes>
+ <include location="global" impldecl="in implementation">kdialog.h</include>
+</includes>
+<layoutdefaults spacing="6" margin="0"/>
+<layoutfunctions spacing="KDialog::spacingHint"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/juk/tagguessertest.cpp b/juk/tagguessertest.cpp
new file mode 100644
index 00000000..f726e01b
--- /dev/null
+++ b/juk/tagguessertest.cpp
@@ -0,0 +1,128 @@
+// Copyright Frerich Raabe <raabe@kde.org>.
+// This notice was added by Michael Pyne <michael.pyne@kdemail.net>
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "tagguesser.h"
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <kapplication.h>
+#include <qdir.h>
+#include <iostream>
+
+#include <stdlib.h>
+
+using std::cout;
+using std::endl;
+
+void check( const QString &filename, const QString &title,
+ const QString &artist, const QString &track,
+ const QString &comment, const QString &album = QString::null )
+{
+ cout << "Checking " << filename.latin1() << "...";
+ TagGuesser guesser( filename );
+ if ( guesser.title() != title ) {
+ cout << "Error: In filename " << filename.latin1() << ", expected title " << title.latin1() << ", got title " << guesser.title().latin1() << endl;
+ exit( 1 );
+ }
+ if ( guesser.artist() != artist ) {
+ cout << "Error: In filename " << filename.latin1() << ", expected artist " << artist.latin1() << ", got artist " << guesser.artist().latin1() << endl;
+ exit( 1 );
+ }
+ if ( guesser.track() != track ) {
+ cout << "Error: In filename " << filename.latin1() << ", expected track " << track.latin1() << ", got track " << guesser.track().latin1() << endl;
+ exit( 1 );
+ }
+ if ( guesser.comment() != comment ) {
+ cout << "Error: In filename " << filename.latin1() << ", expected comment " << comment.latin1() << ", got comment " << guesser.comment().latin1() << endl;
+ exit( 1 );
+ }
+ if ( guesser.album() != album ) {
+ cout << "Error: In filename " << filename.latin1() << ", expected album " << album.latin1() << ", got album " << guesser.album().latin1() << endl;
+ exit( 1 );
+ }
+ cout << "OK" << endl;
+}
+
+int main( int argc, char **argv )
+{
+ KAboutData aboutData("tagguessertest", "tagguessertest", "0.1");
+ KCmdLineArgs::init(argc, argv, &aboutData);
+ KApplication app;
+ check( "/home/frerich/Chemical Brothers - (01) - Block rockin' beats [Live].mp3",
+ "Block Rockin' Beats", "Chemical Brothers", "01", "Live" );
+ check( "/home/frerich/Chemical Brothers - (01) - Block rockin' beats (Live).mp3",
+ "Block Rockin' Beats", "Chemical Brothers", "01", "Live" );
+ check( "/home/frerich/Chemical Brothers - (01) - Block rockin' beats.mp3",
+ "Block Rockin' Beats", "Chemical Brothers", "01", QString::null );
+ check( "/home/frerich/Chemical Brothers - [01] - Block rockin' beats [Live].mp3",
+ "Block Rockin' Beats", "Chemical Brothers", "01", "Live" );
+ check( "/home/frerich/Chemical Brothers - [01] - Block rockin' beats (Live).mp3",
+ "Block Rockin' Beats", "Chemical Brothers", "01", "Live" );
+ check( "/home/frerich/Chemical Brothers - [01] - Block rockin' beats.mp3",
+ "Block Rockin' Beats", "Chemical Brothers", "01", QString::null );
+ check( "/home/frerich/Chemical Brothers - 01 - Block rockin' beats [Live].mp3",
+ "Block Rockin' Beats", "Chemical Brothers", "01", "Live" );
+ check( "/home/frerich/Chemical Brothers - 01 - Block rockin' beats (Live).mp3",
+ "Block Rockin' Beats", "Chemical Brothers", "01", "Live" );
+ check( "/home/frerich/Chemical Brothers - 01 - Block rockin' beats.mp3",
+ "Block Rockin' Beats", "Chemical Brothers", "01", QString::null );
+ check( "/home/frerich/(01) Chemical Brothers - Block rockin' beats [Live].mp3",
+ "Block Rockin' Beats", "Chemical Brothers", "01", "Live" );
+ check( "/home/frerich/(01) Chemical Brothers - Block rockin' beats (Live).mp3",
+ "Block Rockin' Beats", "Chemical Brothers", "01", "Live" );
+ check( "/home/frerich/(01) Chemical Brothers - Block rockin' beats.mp3",
+ "Block Rockin' Beats", "Chemical Brothers", "01", QString::null );
+ check( "/home/frerich/[01] Chemical Brothers - Block rockin' beats [Live].mp3",
+ "Block Rockin' Beats", "Chemical Brothers", "01", "Live" );
+ check( "/home/frerich/[01] Chemical Brothers - Block rockin' beats (Live).mp3",
+ "Block Rockin' Beats", "Chemical Brothers", "01", "Live" );
+ check( "/home/frerich/[01] Chemical Brothers - Block rockin' beats.mp3",
+ "Block Rockin' Beats", "Chemical Brothers", "01", QString::null );
+ check( "/home/frerich/01 Chemical Brothers - Block rockin' beats [Live].mp3",
+ "Block Rockin' Beats", "Chemical Brothers", "01", "Live" );
+ check( "/home/frerich/01 Chemical Brothers - Block rockin' beats (Live).mp3",
+ "Block Rockin' Beats", "Chemical Brothers", "01", "Live" );
+ check( "/home/frerich/01 Chemical Brothers - Block rockin' beats.mp3",
+ "Block Rockin' Beats", "Chemical Brothers", "01", QString::null );
+ check( "/home/frerich/(Chemical Brothers) Block rockin' beats [Live].mp3",
+ "Block Rockin' Beats", "Chemical Brothers", QString::null, "Live" );
+ check( "/home/frerich/(Chemical Brothers) Block rockin' beats (Live).mp3",
+ "Block Rockin' Beats", "Chemical Brothers", QString::null, "Live" );
+ check( "/home/frerich/(Chemical Brothers) Block rockin' beats.mp3",
+ "Block Rockin' Beats", "Chemical Brothers", QString::null, QString::null );
+ check( "/home/frerich/Chemical Brothers - Block rockin' beats [Live].mp3",
+ "Block Rockin' Beats", "Chemical Brothers", QString::null, "Live" );
+ check( "/home/frerich/Chemical Brothers - Block rockin' beats (Live).mp3",
+ "Block Rockin' Beats", "Chemical Brothers", QString::null, "Live" );
+ check( "/home/frerich/Chemical Brothers - Block rockin' beats.mp3",
+ "Block Rockin' Beats", "Chemical Brothers", QString::null, QString::null );
+ check( "/home/frerich/mp3/Chemical Brothers/Dig your own hole/[01] Block rockin' beats.mp3",
+ "Block Rockin' Beats", "Chemical Brothers", "01", QString::null, "Dig Your Own Hole");
+ check( QDir::homeDirPath() + "/[01] Randy - Religion, religion.mp3",
+ "Religion, Religion", "Randy", "01", QString::null, QString::null );
+ check( QDir::homeDirPath() + "/(3) Mr. Doe - Punk.mp3",
+ "Punk", "Mr. Doe", "3", QString::null, QString::null );
+ check( "c:\\music\\mp3s\\(3) Mr. Doe - Punk.mp3",
+ "Punk", "Mr. Doe", "3", QString::null, QString::null );
+ cout << "All OK" << endl;
+ return 0;
+}
diff --git a/juk/tagrenameroptions.cpp b/juk/tagrenameroptions.cpp
new file mode 100644
index 00000000..4f7ceba8
--- /dev/null
+++ b/juk/tagrenameroptions.cpp
@@ -0,0 +1,158 @@
+/***************************************************************************
+ begin : Thu Oct 28 2004
+ copyright : (C) 2004 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kdebug.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kconfig.h>
+#include <kconfigbase.h>
+
+#include "tagrenameroptions.h"
+
+TagRenamerOptions::TagRenamerOptions() :
+ m_emptyAction(IgnoreEmptyTag),
+ m_trackWidth(0),
+ m_disabled(true),
+ m_category(Unknown)
+{
+}
+
+TagRenamerOptions::TagRenamerOptions(const TagRenamerOptions &other) :
+ m_prefix(other.m_prefix),
+ m_suffix(other.m_suffix),
+ m_emptyAction(other.m_emptyAction),
+ m_emptyText(other.m_emptyText),
+ m_trackWidth(other.m_trackWidth),
+ m_disabled(other.m_disabled),
+ m_category(other.m_category)
+{
+}
+
+TagRenamerOptions::TagRenamerOptions(const CategoryID &category)
+ : m_category(category.category)
+{
+ // Set some defaults
+
+ bool disabled;
+ unsigned categoryNum = category.categoryNumber;
+
+ switch(category.category) {
+ case Title:
+ case Artist:
+ case Genre:
+ case Year:
+ case Album:
+ case Track:
+ disabled = false;
+ break;
+ default:
+ disabled = true;
+ }
+
+ // Make sure we don't use translated strings for the config file keys.
+
+ QString typeKey = tagTypeText(category.category, false);
+ KConfigGroup config(KGlobal::config(), "FileRenamer");
+
+ if(categoryNum > 0)
+ typeKey.append(QString::number(categoryNum));
+
+ setSuffix(config.readEntry(QString("%1Suffix").arg(typeKey)));
+ setPrefix(config.readEntry(QString("%1Prefix").arg(typeKey)));
+
+ // Default the emptyAction to ignoring the empty tag.
+
+ const QString emptyAction = config.readEntry(QString("%1EmptyAction").arg(typeKey)).lower();
+ setEmptyAction(IgnoreEmptyTag);
+
+ if(emptyAction == "forceemptyinclude")
+ setEmptyAction(ForceEmptyInclude);
+ else if(emptyAction == "usereplacementvalue")
+ setEmptyAction(UseReplacementValue);
+
+ setEmptyText(config.readEntry(QString("%1EmptyText").arg(typeKey)));
+ setTrackWidth(config.readUnsignedNumEntry(QString("%1TrackWidth").arg(typeKey)));
+ setDisabled(config.readBoolEntry(QString("%1Disabled").arg(typeKey), disabled));
+}
+
+QString TagRenamerOptions::tagTypeText(TagType type, bool translate)
+{
+ // These must be declared in the same order that they are defined in
+ // the TagType enum in test.h. We can dynamically translate these strings,
+ // so make sure that I18N_NOOP() is used instead of i18n().
+
+ const char *tags[] = {
+ I18N_NOOP("Title"), I18N_NOOP("Artist"), I18N_NOOP("Album"),
+ I18N_NOOP("Track"), I18N_NOOP("Genre"), I18N_NOOP("Year")
+ };
+
+ if(type < StartTag || type >= NumTypes) {
+ kdWarning() << "I don't know what category we're looking up, this is a problem." << endl;
+ kdWarning() << "The category ID is " << (unsigned) type << endl;
+ return translate ? i18n("Unknown") : "Unknown";
+ }
+
+ return translate ? i18n(tags[type]) : tags[type];
+}
+
+void TagRenamerOptions::saveConfig(unsigned categoryNum) const
+{
+ // Make sure we don't use translated strings for the config file keys.
+
+ QString typeKey = tagTypeText(false);
+ if(categoryNum > 0)
+ typeKey.append(QString::number(categoryNum));
+
+ KConfigGroup config(KGlobal::config(), "FileRenamer");
+
+ config.writeEntry(QString("%1Suffix").arg(typeKey), suffix());
+ config.writeEntry(QString("%1Prefix").arg(typeKey), prefix());
+
+ QString emptyStr;
+
+ switch(emptyAction()) {
+ case ForceEmptyInclude:
+ emptyStr = "ForceEmptyInclude";
+ break;
+
+ case IgnoreEmptyTag:
+ emptyStr = "IgnoreEmptyTag";
+ break;
+
+ case UseReplacementValue:
+ emptyStr = "UseReplacementValue";
+ break;
+ }
+
+ config.writeEntry(QString("%1EmptyAction").arg(typeKey), emptyStr);
+ config.writeEntry(QString("%1EmptyText").arg(typeKey), emptyText());
+ config.writeEntry(QString("%1Disabled").arg(typeKey), disabled());
+
+ if(category() == Track)
+ config.writeEntry(QString("%1TrackWidth").arg(typeKey), trackWidth());
+
+ config.sync();
+}
+
+TagType TagRenamerOptions::tagFromCategoryText(const QString &text, bool translate)
+{
+ for(unsigned i = StartTag; i < NumTypes; ++i)
+ if(tagTypeText(static_cast<TagType>(i), translate) == text)
+ return static_cast<TagType>(i);
+
+ return Unknown;
+}
+
+// vim: set et ts=4 sw=4:
diff --git a/juk/tagrenameroptions.h b/juk/tagrenameroptions.h
new file mode 100644
index 00000000..b62e9bf4
--- /dev/null
+++ b/juk/tagrenameroptions.h
@@ -0,0 +1,176 @@
+/***************************************************************************
+ begin : Sun Oct 31 2004
+ copyright : (C) 2004 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef JUK_TAGRENAMEROPTIONS_H
+#define JUK_TAGRENAMEROPTIONS_H
+
+// Insert all new tag types before NumTypes, that way NumTypes will always be
+// the count of valid tag types.
+enum TagType {
+ StartTag, Title = StartTag, Artist, Album,
+ Track, Genre, Year, NumTypes, Unknown
+};
+
+/**
+ * Class that uniquely identifies a user's category (since the user may have
+ * the same category more than once in their file renaming structure).
+ */
+struct CategoryID
+{
+ CategoryID() : category(Unknown), categoryNumber(0)
+ {
+ }
+
+ CategoryID(const CategoryID &other) : category(other.category),
+ categoryNumber(other.categoryNumber)
+ {
+ }
+
+ CategoryID(TagType cat, unsigned num) : category(cat), categoryNumber(num)
+ {
+ }
+
+ CategoryID &operator=(const CategoryID &other)
+ {
+ if(this == &other)
+ return *this;
+
+ category = other.category;
+ categoryNumber = other.categoryNumber;
+
+ return *this;
+ }
+
+ bool operator==(const CategoryID &other) const
+ {
+ return category == other.category && categoryNumber == other.categoryNumber;
+ }
+
+ bool operator!=(const CategoryID &other) const
+ {
+ return !(*this == other);
+ }
+
+ bool operator<(const CategoryID &other) const
+ {
+ if(category == other.category)
+ return categoryNumber < other.categoryNumber;
+
+ return category < other.category;
+ }
+
+ TagType category;
+ unsigned categoryNumber;
+};
+
+/**
+ * Defines options for a tag type. Used by FileRenamerTagOptions as its
+ * data type.
+ *
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ */
+class TagRenamerOptions
+{
+public:
+ enum EmptyActions { ForceEmptyInclude, IgnoreEmptyTag, UseReplacementValue };
+
+ TagRenamerOptions();
+
+ /**
+ * Construct the options by loading from KConfig.
+ *
+ * @param category The category to load the options for.
+ */
+ TagRenamerOptions(const CategoryID &category);
+ TagRenamerOptions(const TagRenamerOptions &other);
+
+ QString prefix() const { return m_prefix; }
+ QString suffix() const { return m_suffix; }
+ QString emptyText() const { return m_emptyText; }
+ EmptyActions emptyAction() const { return m_emptyAction; }
+ unsigned trackWidth() const { return m_trackWidth; }
+ bool disabled() const { return m_disabled; }
+ TagType category() const { return m_category; }
+
+ void setPrefix(const QString &prefix) { m_prefix = prefix; }
+ void setSuffix(const QString &suffix) { m_suffix = suffix; }
+ void setEmptyText(const QString &emptyText) { m_emptyText = emptyText; }
+ void setEmptyAction(EmptyActions action) { m_emptyAction = action; }
+ void setTrackWidth(unsigned width) { m_trackWidth = width; }
+ void setDisabled(bool disabled) { m_disabled = disabled; }
+ void setCategory(TagType category) { m_category = category; }
+
+ /**
+ * Maps \p type to a textual representation of its name. E.g. Track => "Track"
+ *
+ * @param type the category to retrieve a text representation of.
+ * @param translate if true, the string is translated (if possible).
+ * @return text representation of category.
+ */
+ static QString tagTypeText(TagType category, bool translate = true);
+
+ QString tagTypeText(bool translate = true) const
+ {
+ return tagTypeText(category(), translate);
+ }
+
+ /**
+ * Function that tries to match a string back to its category. Uses
+ * the case-sensitive form of the string. If it fails it will return
+ * Unknown.
+ *
+ * @param translated If true, @p text is translated, if false, it is the untranslated
+ * version.
+ */
+ static TagType tagFromCategoryText(const QString &text, bool translate = true);
+
+ /**
+ * This saves the options to the global KConfig object.
+ *
+ * @param categoryNum The zero-based count of the number of this type of
+ * category. For example, this would be 1 for the
+ * second category of this type. The stored category
+ * number is not used in order to allow you to save with
+ * a different one (for compaction purposes perhaps).
+ */
+ void saveConfig(unsigned categoryNum) const;
+
+private:
+
+ // Member variables
+
+ QString m_prefix;
+ QString m_suffix;
+
+ /// Defines the action to take when the tag is empty.
+ EmptyActions m_emptyAction;
+
+ /// If m_emptyAction is UseReplacementValue, this holds the text of the value
+ /// to use.
+ QString m_emptyText;
+
+ /// Used only for the Track type. Defines the minimum track width when
+ /// expanding the track token.
+ unsigned m_trackWidth;
+
+ /// This is true if this tag is always disabled when expanding file names.
+ bool m_disabled;
+
+ TagType m_category;
+};
+
+#endif /* JUK_TAGRENAMEROPTIONS_H */
+
+// vim: set et ts=4 sw=4:
diff --git a/juk/tagtransactionmanager.cpp b/juk/tagtransactionmanager.cpp
new file mode 100644
index 00000000..e6c8adef
--- /dev/null
+++ b/juk/tagtransactionmanager.cpp
@@ -0,0 +1,214 @@
+/***************************************************************************
+ begin : Wed Sep 22 2004
+ copyright : (C) 2004 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kaction.h>
+#include <kapplication.h>
+
+#include <qfileinfo.h>
+#include <qdir.h>
+
+#include "tagtransactionmanager.h"
+#include "playlistitem.h"
+#include "collectionlist.h"
+#include "tag.h"
+#include "actioncollection.h"
+
+using ActionCollection::action;
+
+TagTransactionManager *TagTransactionManager::m_manager = 0;
+
+TagTransactionAtom::TagTransactionAtom() : m_item(0), m_tag(0)
+{
+ action("edit_undo")->setEnabled(false);
+}
+
+TagTransactionAtom::TagTransactionAtom(const TagTransactionAtom &other) :
+ m_item(other.m_item), m_tag(other.m_tag)
+{
+ other.m_tag = 0; // Only allow one owner
+}
+
+TagTransactionAtom::TagTransactionAtom(PlaylistItem *item, Tag *tag) :
+ m_item(item), m_tag(tag)
+{
+}
+
+TagTransactionAtom::~TagTransactionAtom()
+{
+ delete m_tag;
+}
+
+TagTransactionAtom &TagTransactionAtom::operator=(const TagTransactionAtom &other)
+{
+ m_item = other.m_item;
+ m_tag = other.m_tag;
+
+ other.m_tag = 0; // Only allow one owner
+
+ return *this;
+}
+
+TagTransactionManager *TagTransactionManager::instance()
+{
+ return m_manager;
+}
+
+void TagTransactionManager::changeTagOnItem(PlaylistItem *item, Tag *newTag)
+{
+ if(!item) {
+ kdWarning(65432) << "Trying to change tag on null PlaylistItem.\n";
+ return;
+ }
+
+ // Save the CollectionListItem, as it is the most likely to survive long
+ // enough for the commit(). I should probably intercept the item deleted
+ // signals from CollectionList to ensure that the commit list and the
+ // playlists stay in sync.
+
+ m_list.append(TagTransactionAtom(item->collectionItem(), newTag));
+}
+
+Tag *TagTransactionManager::duplicateTag(const Tag *tag, const QString &fileName)
+{
+ Q_ASSERT(tag);
+
+ QString name = fileName.isNull() ? tag->fileName() : fileName;
+ Tag *newTag = new Tag(*tag);
+
+ newTag->setFileName(name);
+ return newTag;
+}
+
+bool TagTransactionManager::commit()
+{
+ m_undoList.clear();
+ bool result = processChangeList();
+
+ m_list.clear();
+ return result;
+}
+
+void TagTransactionManager::forget()
+{
+ m_list.clear();
+}
+
+bool TagTransactionManager::undo()
+{
+ kdDebug(65432) << "Undoing " << m_undoList.count() << " changes.\n";
+
+ forget(); // Scrap our old changes (although the list should be empty
+ // anyways.
+
+ bool result = processChangeList(true);
+
+ m_undoList.clear();
+ action("edit_undo")->setEnabled(false);
+
+ return result;
+}
+
+TagTransactionManager::TagTransactionManager(QWidget *parent) : QObject(parent, "tagmanager")
+{
+ m_manager = this;
+}
+
+bool TagTransactionManager::renameFile(const QFileInfo &from, const QFileInfo &to) const
+{
+ if(!QFileInfo(to.dirPath()).isWritable() || !from.exists())
+ return false;
+
+ if(!to.exists() ||
+ KMessageBox::warningContinueCancel(
+ static_cast<QWidget *>(parent()),
+ i18n("This file already exists.\nDo you want to replace it?"),
+ i18n("File Exists"),i18n("Replace")) == KMessageBox::Continue)
+ {
+ kdDebug(65432) << "Renaming " << from.absFilePath() << " to " << to.absFilePath() << endl;
+ QDir currentDir;
+ return currentDir.rename(from.absFilePath(), to.absFilePath());
+ }
+
+ return false;
+}
+
+bool TagTransactionManager::processChangeList(bool undo)
+{
+ TagAlterationList::ConstIterator it, end;
+ QStringList errorItems;
+
+ it = undo ? m_undoList.begin() : m_list.begin();
+ end = undo ? m_undoList.end() : m_list.end();
+
+ emit signalAboutToModifyTags();
+
+ for(; it != end; ++it) {
+ PlaylistItem *item = (*it).item();
+ Tag *tag = (*it).tag();
+
+ QFileInfo newFile(tag->fileName());
+
+ if(item->file().fileInfo().fileName() != newFile.fileName()) {
+ if(!renameFile(item->file().fileInfo(), newFile)) {
+ errorItems.append(item->text(1) + QString(" - ") + item->text(0));
+ continue;
+ }
+ }
+
+ if(tag->save()) {
+ if(!undo)
+ m_undoList.append(TagTransactionAtom(item, duplicateTag(item->file().tag())));
+
+ item->file().setFile(tag->fileName());
+ item->refreshFromDisk();
+ item->repaint();
+ item->playlist()->dataChanged();
+ item->playlist()->update();
+ }
+ else {
+ Tag *errorTag = item->file().tag();
+ QString str = errorTag->artist() + " - " + errorTag->title();
+
+ if(errorTag->artist().isEmpty())
+ str = errorTag->title();
+
+ errorItems.append(str);
+ }
+
+ kapp->processEvents();
+ }
+
+ undo ? m_undoList.clear() : m_list.clear();
+ if(!undo && !m_undoList.isEmpty())
+ action("edit_undo")->setEnabled(true);
+ else
+ action("edit_undo")->setEnabled(false);
+
+ if(!errorItems.isEmpty())
+ KMessageBox::errorList(static_cast<QWidget *>(parent()),
+ i18n("The following files were unable to be changed."),
+ errorItems,
+ i18n("Error"));
+
+ emit signalDoneModifyingTags();
+ return errorItems.isEmpty();
+}
+
+#include "tagtransactionmanager.moc"
+
+// vim: set et ts=4 sw=4 tw=0:
diff --git a/juk/tagtransactionmanager.h b/juk/tagtransactionmanager.h
new file mode 100644
index 00000000..ce39393f
--- /dev/null
+++ b/juk/tagtransactionmanager.h
@@ -0,0 +1,213 @@
+/***************************************************************************
+ begin : Wed Sep 22 2004
+ copyright : (C) 2004 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef _TAGTRANSACTIONMANAGER_H
+#define _TAGTRANSACTIONMANAGER_H
+
+
+
+class PlaylistItem;
+class QWidget;
+class Tag;
+
+/**
+ * Class to encapsulate a change to the tag, and optionally the file name, of
+ * a PlaylistItem.
+ *
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ * @see TagTransactionManager
+ */
+class TagTransactionAtom
+{
+ public:
+ /**
+ * Default constructor, for use by QValueList.
+ */
+ TagTransactionAtom();
+
+ /**
+ * Copy constructor. This takes ownership of the m_tag pointer, so the
+ * object being copied no longer has access to the tag. This function also
+ * exists mainly for QValueList's benefit.
+ *
+ * @param other The TagTransactionAtom to copy.
+ */
+ TagTransactionAtom(const TagTransactionAtom &other);
+
+ /**
+ * Creates an atom detailing a change made by \p tag to \p item.
+ *
+ * @param tag Contains the new tag to apply to item.
+ * @param item The PlaylistItem to change.
+ */
+ TagTransactionAtom(PlaylistItem *item, Tag *tag);
+
+ /**
+ * Destroys the atom. This function deletes the tag, so make sure you've
+ * already copied out any data you need. The PlaylistItem is unaffected.
+ */
+ ~TagTransactionAtom();
+
+ /**
+ * Assignment operator. This operator takes ownership of the m_tag pointer,
+ * so the object being assigned from no longer has access to the tag. This
+ * function exists mainly for the benefit of QValueList.
+ *
+ * @param other The TagTransactionAtom to copy from.
+ * @return The TagTransactionAtom being assigned to.
+ */
+ TagTransactionAtom &operator=(const TagTransactionAtom &other);
+
+ /**
+ * Accessor function to retrieve the PlaylistItem.
+ *
+ * @return The PlaylistItem being changed.
+ */
+ PlaylistItem *item() const { return m_item; }
+
+ /**
+ * Accessor function to retrieve the changed Tag.
+ *
+ * @return The Tag containing the changes to apply to item().
+ */
+ Tag *tag() const { return m_tag; }
+
+ private:
+ PlaylistItem *m_item;
+ mutable Tag *m_tag;
+};
+
+typedef QValueList<TagTransactionAtom> TagAlterationList;
+
+/**
+ * This class manages alterations of a group of PlaylistItem's FileHandles. What this
+ * means in practice is that you will use this class to change the tags and/or
+ * filename of a PlaylistItem.
+ *
+ * This class supports a limited transactional interface. Once you commit a
+ * group of changes, you can call the undo() method to revert back to the way
+ * things were (except possibly for file renames). You can call forget() to
+ * forget a series of changes as well.
+ *
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ */
+class TagTransactionManager : public QObject
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructs a TagTransactionManager, owned by @p parent.
+ *
+ * @param parent The parent QWidget.
+ */
+ TagTransactionManager(QWidget *parent = 0);
+
+ /**
+ * Returns the global TagTransactionManager instance.
+ *
+ * @return The global TagTransactionManager.
+ */
+ static TagTransactionManager *instance();
+
+ /**
+ * Adds a change to the list of changes to apply. Internally this
+ * function extracts the CollectionListItem of @p item, and uses that
+ * instead, so there is no need to do so yourself.
+ *
+ * @param item The PlaylistItem to change.
+ * @param newTag The Tag containing the changed data.
+ */
+ void changeTagOnItem(PlaylistItem *item, Tag *newTag);
+
+ /**
+ * Convienience function to duplicate a Tag object, since the Tag
+ * object doesn't have a decent copy constructor.
+ *
+ * @param tag The Tag to duplicate.
+ * @param fileName The filename to assign to the tag. If QString::null
+ * (the default) is passed, the filename of the existing tag is
+ * used.
+ * @bug Tag should have a correct copy ctor and assignment operator.
+ * @return The duplicate Tag.
+ */
+ static Tag *duplicateTag(const Tag *tag, const QString &fileName = QString::null);
+
+ /**
+ * Commits the changes to the PlaylistItems. It is important that the
+ * PlaylistItems still exist when you call this function, although this
+ * shouldn't be a problem in practice. After altering the tags, and
+ * renaming the files if necessary, you can call undo() to back out the
+ * changes.
+ *
+ * If any errors have occurred, the user will be notified with a dialog
+ * box, and those files which were unabled to be altered will be excluded
+ * from the undo set.
+ *
+ * @return true if no errors occurred, false otherwise.
+ */
+ bool commit();
+
+ /**
+ * Clears the current update list. The current undo list is unaffected.
+ */
+ void forget();
+
+ /**
+ * Undoes the changes caused by commit(). Like commit(), if any errors
+ * occur changing the state back (for example, it may be impossible to
+ * rename a file back to its original name), the user will be shown notified
+ * via a dialog box.
+ *
+ * After performing the undo operation, it is impossible to call undo()
+ * again on the same set of files. Namely, you can't repeatedly call
+ * undo() to switch between two different file states.
+ *
+ * @return true if no errors occurred, false otherwise.
+ */
+ bool undo();
+
+ signals:
+ void signalAboutToModifyTags();
+ void signalDoneModifyingTags();
+
+ private:
+ /**
+ * Renames the file identified by @p from to have the name given by @p to,
+ * prompting the user to confirm if necessary.
+ *
+ * @param from QFileInfo with the filename of the original file.
+ * @param to QFileInfo with the new filename.
+ * @return true if no errors occurred, false otherwise.
+ */
+ bool renameFile(const QFileInfo &from, const QFileInfo &to) const;
+
+ /**
+ * Used internally by commit() and undo(). Performs the work of updating
+ * the PlaylistItems and then updating the various GUI elements that need
+ * to be updated.
+ *
+ * @param undo true if operating in undo mode, false otherwise.
+ */
+ bool processChangeList(bool undo = false);
+
+ TagAlterationList m_list; ///< holds a list of changes to commit
+ TagAlterationList m_undoList; ///< holds a list of changes to undo
+ static TagTransactionManager *m_manager; ///< used by instance()
+};
+
+#endif /* _TAGTRANSACTIONMANAGER_H */
+
+// vim: set et ts=4 sw=4 tw=0:
diff --git a/juk/trackpickerdialog.cpp b/juk/trackpickerdialog.cpp
new file mode 100644
index 00000000..44cf8f7a
--- /dev/null
+++ b/juk/trackpickerdialog.cpp
@@ -0,0 +1,101 @@
+/***************************************************************************
+ begin : Sat Sep 6 2003
+ copyright : (C) 2003 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <config.h>
+
+#if HAVE_MUSICBRAINZ
+
+#include <qlabel.h>
+
+#include <klistview.h>
+#include <klocale.h>
+
+#include "trackpickerdialog.h"
+#include "trackpickerdialogbase.h"
+
+#define NUMBER(x) (x == 0 ? QString::null : QString::number(x))
+
+class TrackPickerItem : public KListViewItem
+{
+public:
+ TrackPickerItem(KListView *parent, const KTRMResult &result) :
+ KListViewItem(parent, parent->lastChild(),
+ result.title(), result.artist(), result.album(),
+ NUMBER(result.track()), NUMBER(result.year())),
+ m_result(result) {}
+ KTRMResult result() const { return m_result; }
+
+private:
+ KTRMResult m_result;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+TrackPickerDialog::TrackPickerDialog(const QString &name,
+ const KTRMResultList &results,
+ QWidget *parent) :
+ KDialogBase(parent, name.latin1(), true, i18n("Internet Tag Guesser"), Ok | Cancel, Ok, true)
+{
+ m_base = new TrackPickerDialogBase(this);
+ setMainWidget(m_base);
+
+ m_base->fileLabel->setText(name);
+ m_base->trackList->setSorting(-1);
+
+ for(KTRMResultList::ConstIterator it = results.begin(); it != results.end(); ++it)
+ new TrackPickerItem(m_base->trackList, *it);
+
+ m_base->trackList->setSelected(m_base->trackList->firstChild(), true);
+
+ connect(m_base->trackList, SIGNAL(doubleClicked(QListViewItem *, const QPoint &, int)),
+ this, SLOT(accept()));
+
+ setMinimumWidth(kMax(400, width()));
+}
+
+TrackPickerDialog::~TrackPickerDialog()
+{
+
+}
+
+KTRMResult TrackPickerDialog::result() const
+{
+ if(m_base->trackList->selectedItem())
+ return static_cast<TrackPickerItem *>(m_base->trackList->selectedItem())->result();
+ else
+ return KTRMResult();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public slots
+////////////////////////////////////////////////////////////////////////////////
+
+int TrackPickerDialog::exec()
+{
+ int dialogCode = KDialogBase::exec();
+
+ // Only return true if an item was selected.
+
+ if(m_base->trackList->selectedItem())
+ return dialogCode;
+ else
+ return Rejected;
+}
+
+#include "trackpickerdialog.moc"
+
+#endif // HAVE_MUSICBRAINZ
diff --git a/juk/trackpickerdialog.h b/juk/trackpickerdialog.h
new file mode 100644
index 00000000..623944d8
--- /dev/null
+++ b/juk/trackpickerdialog.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+ begin : Sat Sep 6 2003
+ copyright : (C) 2003 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef TRACKPICKERDIALOG_H
+#define TRACKPICKERDIALOG_H
+
+#include <config.h>
+
+#if HAVE_MUSICBRAINZ
+
+#include <kdialogbase.h>
+
+#include "musicbrainzquery.h"
+
+class TrackPickerDialogBase;
+
+class TrackPickerDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ TrackPickerDialog(const QString &name,
+ const KTRMResultList &results,
+ QWidget *parent = 0);
+
+ virtual ~TrackPickerDialog();
+
+ KTRMResult result() const;
+
+public slots:
+ int exec();
+
+private:
+ TrackPickerDialogBase *m_base;
+};
+
+#endif // HAVE_MUSICBRAINZ
+
+#endif
diff --git a/juk/trackpickerdialogbase.ui b/juk/trackpickerdialogbase.ui
new file mode 100644
index 00000000..b938fb19
--- /dev/null
+++ b/juk/trackpickerdialogbase.ui
@@ -0,0 +1,179 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>TrackPickerDialogBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>trackPickerDialogBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>556</width>
+ <height>310</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>fileLayout</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>fileInfoGroup</cstring>
+ </property>
+ <property name="title">
+ <string>File Name</string>
+ </property>
+ <property name="alignment">
+ <set>AlignTop</set>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>fileLabel</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignLeft</set>
+ </property>
+ <property name="indent">
+ <number>9</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>trackLayout</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>trackInfoGroup</cstring>
+ </property>
+ <property name="title">
+ <string>Select Best Possible Match</string>
+ </property>
+ <property name="alignment">
+ <set>AlignTop</set>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Track Name</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>false</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Artist</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>false</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Album</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>false</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Track</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Year</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>trackList</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="itemMargin">
+ <number>1</number>
+ </property>
+ <property name="resizeMode">
+ <enum>LastColumn</enum>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<tabstops>
+ <tabstop>trackList</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+</includehints>
+</UI>
diff --git a/juk/tracksequenceiterator.cpp b/juk/tracksequenceiterator.cpp
new file mode 100644
index 00000000..2c71008a
--- /dev/null
+++ b/juk/tracksequenceiterator.cpp
@@ -0,0 +1,310 @@
+/***************************************************************************
+ begin : Thu Aug 19 2004
+ copyright : (C) 2002 - 2004 by Michael Pyne
+ email : michael.pyne@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kaction.h>
+#include <kapplication.h>
+#include <kdebug.h>
+
+#include "tracksequenceiterator.h"
+#include "playlist.h"
+#include "actioncollection.h"
+#include "tag.h"
+#include "filehandle.h"
+
+using namespace ActionCollection;
+
+TrackSequenceIterator::TrackSequenceIterator() :
+ m_current(0)
+{
+}
+
+TrackSequenceIterator::TrackSequenceIterator(const TrackSequenceIterator &other) :
+ m_current(other.m_current)
+{
+}
+
+TrackSequenceIterator::~TrackSequenceIterator()
+{
+}
+
+void TrackSequenceIterator::setCurrent(PlaylistItem *current)
+{
+ m_current = current;
+}
+
+void TrackSequenceIterator::playlistChanged()
+{
+}
+
+void TrackSequenceIterator::itemAboutToDie(const PlaylistItem *)
+{
+}
+
+DefaultSequenceIterator::DefaultSequenceIterator() :
+ TrackSequenceIterator()
+{
+}
+
+DefaultSequenceIterator::DefaultSequenceIterator(const DefaultSequenceIterator &other)
+ : TrackSequenceIterator(other)
+{
+}
+
+DefaultSequenceIterator::~DefaultSequenceIterator()
+{
+}
+
+void DefaultSequenceIterator::advance()
+{
+ if(!current())
+ return;
+
+ bool isRandom = action("randomPlay") && action<KToggleAction>("randomPlay")->isChecked();
+ bool loop = action("loopPlaylist") && action<KToggleAction>("loopPlaylist")->isChecked();
+ bool albumRandom = action("albumRandomPlay") && action<KToggleAction>("albumRandomPlay")->isChecked();
+
+ if(isRandom || albumRandom) {
+ if(m_randomItems.isEmpty() && loop) {
+
+ // Since refillRandomList will remove the currently playing item,
+ // we should clear it out first since that's not good for e.g.
+ // lists with 1-2 items. We need to remember the Playlist though.
+
+ Playlist *playlist = current()->playlist();
+ setCurrent(0);
+
+ refillRandomList(playlist);
+ }
+
+ if(m_randomItems.isEmpty()) {
+ setCurrent(0);
+ return;
+ }
+
+ PlaylistItem *item;
+
+ if(albumRandom) {
+ if(m_albumSearch.isNull() || m_albumSearch.matchedItems().isEmpty()) {
+ item = m_randomItems[KApplication::random() % m_randomItems.count()];
+ initAlbumSearch(item);
+ }
+
+ // This can be null if initAlbumSearch() left the m_albumSearch
+ // empty because the album text was empty. Since we initAlbumSearch()
+ // with an item, the matchedItems() should never be empty.
+
+ if(!m_albumSearch.isNull()) {
+ PlaylistItemList albumMatches = m_albumSearch.matchedItems();
+ if(albumMatches.isEmpty()) {
+ kdError(65432) << "Unable to initialize album random play.\n";
+ kdError(65432) << "List of potential results is empty.\n";
+
+ return; // item is still set to random song from a few lines earlier.
+ }
+
+ item = albumMatches[0];
+
+ // Pick first song remaining in list.
+
+ for(unsigned i = 0; i < albumMatches.count(); ++i)
+ if(albumMatches[i]->file().tag()->track() < item->file().tag()->track())
+ item = albumMatches[i];
+ m_albumSearch.clearItem(item);
+
+ if(m_albumSearch.matchedItems().isEmpty()) {
+ m_albumSearch.clearComponents();
+ m_albumSearch.search();
+ }
+ }
+ else
+ kdError(65432) << "Unable to perform album random play on " << *item << endl;
+ }
+ else
+ item = m_randomItems[KApplication::random() % m_randomItems.count()];
+
+ setCurrent(item);
+ m_randomItems.remove(item);
+ }
+ else {
+ PlaylistItem *next = current()->itemBelow();
+ if(!next && loop) {
+ Playlist *p = current()->playlist();
+ next = p->firstChild();
+ while(next && !next->isVisible())
+ next = static_cast<PlaylistItem *>(next->nextSibling());
+ }
+
+ setCurrent(next);
+ }
+}
+
+void DefaultSequenceIterator::backup()
+{
+ if(!current())
+ return;
+
+ PlaylistItem *item = current()->itemAbove();
+
+ if(item)
+ setCurrent(item);
+}
+
+void DefaultSequenceIterator::prepareToPlay(Playlist *playlist)
+{
+ bool random = action("randomPlay") && action<KToggleAction>("randomPlay")->isChecked();
+ bool albumRandom = action("albumRandomPlay") && action<KToggleAction>("albumRandomPlay")->isChecked();
+
+ if(random || albumRandom) {
+ PlaylistItemList items = playlist->selectedItems();
+ if(items.isEmpty())
+ items = playlist->visibleItems();
+
+ PlaylistItem *newItem = 0;
+ if(!items.isEmpty())
+ newItem = items[KApplication::random() % items.count()];
+
+ setCurrent(newItem);
+ refillRandomList();
+ }
+ else {
+ QListViewItemIterator it(playlist, QListViewItemIterator::Visible | QListViewItemIterator::Selected);
+ if(!it.current())
+ it = QListViewItemIterator(playlist, QListViewItemIterator::Visible);
+
+ setCurrent(static_cast<PlaylistItem *>(it.current()));
+ }
+}
+
+void DefaultSequenceIterator::reset()
+{
+ m_randomItems.clear();
+ m_albumSearch.clearComponents();
+ m_albumSearch.search();
+ setCurrent(0);
+}
+
+void DefaultSequenceIterator::playlistChanged()
+{
+ refillRandomList();
+}
+
+void DefaultSequenceIterator::itemAboutToDie(const PlaylistItem *item)
+{
+ PlaylistItem *stfu_gcc = const_cast<PlaylistItem *>(item);
+
+ m_randomItems.remove(stfu_gcc);
+}
+
+void DefaultSequenceIterator::setCurrent(PlaylistItem *current)
+{
+ PlaylistItem *oldCurrent = DefaultSequenceIterator::current();
+
+ TrackSequenceIterator::setCurrent(current);
+
+ bool random = action("randomPlay") && action<KToggleAction>("randomPlay")->isChecked();
+ bool albumRandom = action("albumRandomPlay") && action<KToggleAction>("albumRandomPlay")->isChecked();
+
+ if((albumRandom || random) && current && m_randomItems.isEmpty()) {
+
+ // We're setting a current item, refill the random list now, and remove
+ // the current item.
+
+ refillRandomList();
+ }
+
+ m_randomItems.remove(current);
+
+ if(albumRandom && current && !oldCurrent) {
+
+ // Same idea as above
+
+ initAlbumSearch(current);
+ m_albumSearch.clearItem(current);
+ }
+}
+
+DefaultSequenceIterator *DefaultSequenceIterator::clone() const
+{
+ return new DefaultSequenceIterator(*this);
+}
+
+void DefaultSequenceIterator::refillRandomList(Playlist *p)
+{
+ if(!p) {
+ if (!current())
+ return;
+
+ p = current()->playlist();
+
+ if(!p) {
+ kdError(65432) << k_funcinfo << "Item has no playlist!\n";
+ return;
+ }
+ }
+
+ m_randomItems = p->visibleItems();
+ m_randomItems.remove(current());
+ m_albumSearch.clearComponents();
+ m_albumSearch.search();
+}
+
+void DefaultSequenceIterator::initAlbumSearch(PlaylistItem *searchItem)
+{
+ if(!searchItem)
+ return;
+
+ m_albumSearch.clearPlaylists();
+ m_albumSearch.addPlaylist(searchItem->playlist());
+
+ ColumnList columns;
+
+ m_albumSearch.setSearchMode(PlaylistSearch::MatchAll);
+ m_albumSearch.clearComponents();
+
+ // If the album name is empty, it will mess up the search,
+ // so ignore empty album names.
+
+ if(searchItem->file().tag()->album().isEmpty())
+ return;
+
+ columns.append(PlaylistItem::AlbumColumn);
+
+ m_albumSearch.addComponent(PlaylistSearch::Component(
+ searchItem->file().tag()->album(),
+ true,
+ columns,
+ PlaylistSearch::Component::Exact)
+ );
+
+ // If there is an Artist tag with the track, match against it as well
+ // to avoid things like multiple "Greatest Hits" albums matching the
+ // search.
+
+ if(!searchItem->file().tag()->artist().isEmpty()) {
+ kdDebug(65432) << "Searching both artist and album.\n";
+ columns[0] = PlaylistItem::ArtistColumn;
+
+ m_albumSearch.addComponent(PlaylistSearch::Component(
+ searchItem->file().tag()->artist(),
+ true,
+ columns,
+ PlaylistSearch::Component::Exact)
+ );
+ }
+
+ m_albumSearch.search();
+}
+
+// vim: set et sw=4 tw=0:
diff --git a/juk/tracksequenceiterator.h b/juk/tracksequenceiterator.h
new file mode 100644
index 00000000..a2339f01
--- /dev/null
+++ b/juk/tracksequenceiterator.h
@@ -0,0 +1,232 @@
+/***************************************************************************
+ begin : Thu Aug 19 2004
+ copyright : (C) 2002 - 2004 by Michael Pyne
+ email : michael.pyne@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef _TRACKSEQUENCEITERATOR_H
+#define _TRACKSEQUENCEITERATOR_H
+
+#include "playlistitem.h"
+#include "playlistsearch.h"
+
+class Playlist;
+
+/**
+ * This abstract class defines an interface to be used by TrackSequenceManager,
+ * to iterate over the items in a playlist. Implement this class in a subclass
+ * in order to define your own ordering for playlist sequences. For an example,
+ * see the UpcomingPlaylist class.
+ *
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ * @see UpcomingPlaylist
+ * @see TrackSequenceManager
+ */
+class TrackSequenceIterator
+{
+public:
+ /**
+ * Default constructor.
+ */
+ TrackSequenceIterator();
+
+ /**
+ * Default copy constructor.
+ *
+ * @param other the TrackSequenceIterator we are copying
+ */
+ TrackSequenceIterator(const TrackSequenceIterator & other);
+
+ /**
+ * Default destructor.
+ */
+ virtual ~TrackSequenceIterator();
+
+ /**
+ * This function moves the current item to the next track. You must
+ * reimplement this function in your subclasses.
+ */
+ virtual void advance() = 0;
+
+ /**
+ * This function moves the current item to the previous track. This may
+ * not always make sense, and the history functionality of the Playlist
+ * class currently overrides this. You must reimplement this function in
+ * your subclass.
+ */
+ virtual void backup() = 0;
+
+ /**
+ * This function returns the current PlaylistItem, or 0 if the iterator is
+ * not pointing at any PlaylistItem.
+ *
+ * @return current track
+ */
+ virtual PlaylistItem *current() const { return m_current; }
+
+ /**
+ * This function creates a perfect copy of the object it is called on, to
+ * avoid the C++ slicing problem. When you reimplement this function, you
+ * should change the return type to the name of the subclass.
+ *
+ * @return pointer to a copy of the object
+ */
+ virtual TrackSequenceIterator *clone() const = 0;
+
+ /**
+ * This function is called by the TrackSequenceManager when current() returns
+ * 0, if the TrackSequenceManager has a playlist defined. This function
+ * should choose an appropriate starting track and set it as the current
+ * item. This function must be reimplemented in subclasses.
+ *
+ * @param playlist the playlist to iterate over
+ */
+ virtual void prepareToPlay(Playlist *playlist) = 0;
+
+ /**
+ * This function is called whenever the current playlist changes, such as
+ * having a new search applied, items added/removed, etc. If you need to
+ * update internal state, you should do so without affecting the current
+ * playing item. Default implementation does nothing.
+ */
+ virtual void playlistChanged();
+
+ /**
+ * This function is called by the manager when \p item is about to be
+ * removed. Subclasses should ensure that they're not still holding a
+ * pointer to the item. The default implementation does nothing.
+ *
+ * @param item the item about to be removed.
+ */
+ virtual void itemAboutToDie(const PlaylistItem *item);
+
+ /**
+ * This function is called by the TrackSequenceManager is some situations,
+ * such as when playback is being stopped. If you subclass needs to reset
+ * any internal data members, do so in this function. This function must
+ * be reimplemented in subclasses.
+ */
+ virtual void reset() = 0;
+
+ /**
+ * This function is a public mutator to set the current item.
+ *
+ * @param current the new current item
+ */
+ virtual void setCurrent(PlaylistItem *current);
+
+private:
+ PlaylistItem::Pointer m_current; ///< the current item
+};
+
+/**
+ * This is the default iterator for JuK, supporting normal, random, and album
+ * random playback with or without looping.
+ *
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ */
+class DefaultSequenceIterator : public TrackSequenceIterator
+{
+public:
+ /**
+ * Default constructor.
+ */
+ DefaultSequenceIterator();
+
+ /**
+ * Default copy constructor.
+ *
+ * @param other the DefaultSequenceIterator to copy.
+ */
+ DefaultSequenceIterator(const DefaultSequenceIterator &other);
+
+ /**
+ * Default destructor.
+ */
+ virtual ~DefaultSequenceIterator();
+
+ /**
+ * This function advances to the next item in the current sequence. The
+ * algorithm used depends on what playback mode is selected.
+ */
+ virtual void advance();
+
+ /**
+ * This function moves to the previous item in the playlist. This occurs
+ * no matter what playback mode is selected.
+ */
+ virtual void backup();
+
+ /**
+ * This function prepares the class for iterator. If no random play mode
+ * is selected, the first item in the given playlist is the starting item.
+ * Otherwise, an item is randomly picked to be the starting item.
+ *
+ * @param playlist The playlist to initialize for.
+ */
+ virtual void prepareToPlay(Playlist *playlist);
+
+ /**
+ * This function clears all internal state, including any random play lists,
+ * and what the current album is.
+ */
+ virtual void reset();
+
+ /**
+ * This function recalculates the random lists, and is should be called
+ * whenever its current playlist changes (at least for searches).
+ */
+ virtual void playlistChanged();
+
+ /**
+ * Called when \p item is about to be removed. This function ensures that
+ * it isn't remaining in the random play list.
+ */
+ virtual void itemAboutToDie(const PlaylistItem *item);
+
+ /**
+ * This function sets the current item, and initializes any internal lists
+ * that may be needed for playback.
+ *
+ * @param current The new current item.
+ */
+ virtual void setCurrent(PlaylistItem *current);
+
+ /**
+ * This function returns a perfect copy of the object it is called on, to
+ * get around the C++ slicing problem.
+ *
+ * @return A copy of the object the method is called on.
+ */
+ virtual DefaultSequenceIterator *clone() const;
+
+private:
+
+ /**
+ * Reinitializes the internal random play list based on the playlist given
+ * by \p p. The currently playing item, if any, is automatically removed
+ * from the list.
+ *
+ * @param p The Playlist to read items from. If p is 0, the playlist of
+ * the currently playing item is used instead.
+ */
+ void refillRandomList(Playlist *p = 0);
+ void initAlbumSearch(PlaylistItem *searchItem);
+
+private:
+ PlaylistItemList m_randomItems;
+ PlaylistSearch m_albumSearch;
+};
+
+#endif /* _TRACKSEQUENCEITERATOR_H */
+
+// vim: set et sw=4:
diff --git a/juk/tracksequencemanager.cpp b/juk/tracksequencemanager.cpp
new file mode 100644
index 00000000..7e05f825
--- /dev/null
+++ b/juk/tracksequencemanager.cpp
@@ -0,0 +1,183 @@
+/***************************************************************************
+ begin : Thu Aug 19 2004
+ copyright : (C) 2002 - 2004 by Michael Pyne
+ email : michael.pyne@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kconfig.h>
+#include <klocale.h>
+#include <kaction.h>
+#include <kpopupmenu.h>
+
+#include "actioncollection.h"
+#include "tracksequencemanager.h"
+#include "playlist.h"
+#include "playlistitem.h"
+#include "tracksequenceiterator.h"
+#include "tag.h"
+#include "filehandle.h"
+#include "collectionlist.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// public functions
+/////////////////////////////////////////////////////////////////////////////
+
+TrackSequenceManager::~TrackSequenceManager()
+{
+ // m_playlist and m_popupMenu don't belong to us, don't try to delete them
+ if(m_iterator == m_defaultIterator)
+ m_iterator = 0;
+
+ delete m_iterator;
+ delete m_defaultIterator;
+}
+
+bool TrackSequenceManager::installIterator(TrackSequenceIterator *iterator)
+{
+ PlaylistItem *oldItem = m_iterator ? m_iterator->current() : 0;
+
+ if(m_iterator != m_defaultIterator)
+ delete m_iterator;
+
+ m_iterator = m_defaultIterator;
+ if(iterator)
+ m_iterator = iterator;
+
+ m_iterator->setCurrent(oldItem);
+
+ return true;
+}
+
+PlaylistItem *TrackSequenceManager::currentItem() const
+{
+ return m_iterator->current();
+}
+
+TrackSequenceIterator *TrackSequenceManager::takeIterator()
+{
+ TrackSequenceIterator *temp = m_iterator;
+
+ m_iterator = 0;
+ return temp;
+}
+
+TrackSequenceManager *TrackSequenceManager::instance()
+{
+ static TrackSequenceManager manager;
+
+ if(!manager.m_initialized)
+ manager.initialize();
+
+ return &manager;
+}
+
+PlaylistItem *TrackSequenceManager::nextItem()
+{
+ if(m_playNextItem) {
+
+ // Force the iterator to reset state (such as random item lists)
+
+ m_iterator->reset();
+ m_iterator->setCurrent(m_playNextItem);
+ m_playNextItem = 0;
+ }
+ else if(m_iterator->current())
+ m_iterator->advance();
+ else if(currentPlaylist())
+ m_iterator->prepareToPlay(currentPlaylist());
+ else
+ m_iterator->prepareToPlay(CollectionList::instance());
+
+ return m_iterator->current();
+}
+
+PlaylistItem *TrackSequenceManager::previousItem()
+{
+ m_iterator->backup();
+ return m_iterator->current();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// public slots
+/////////////////////////////////////////////////////////////////////////////
+
+void TrackSequenceManager::setNextItem(PlaylistItem *item)
+{
+ m_playNextItem = item;
+}
+
+void TrackSequenceManager::setCurrentPlaylist(Playlist *list)
+{
+ if(m_playlist)
+ m_playlist->disconnect(this);
+ m_playlist = list;
+
+ connect(m_playlist, SIGNAL(signalAboutToRemove(PlaylistItem *)),
+ this, SLOT(slotItemAboutToDie(PlaylistItem *)));
+}
+
+void TrackSequenceManager::setCurrent(PlaylistItem *item)
+{
+ if(item != m_iterator->current()) {
+ m_iterator->setCurrent(item);
+ if(item)
+ setCurrentPlaylist(item->playlist());
+ else
+ m_iterator->reset();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// private functions
+/////////////////////////////////////////////////////////////////////////////
+
+void TrackSequenceManager::initialize()
+{
+ CollectionList *collection = CollectionList::instance();
+
+ if(!collection)
+ return;
+
+ // Make sure we don't use m_playNextItem if it's invalid.
+ connect(collection, SIGNAL(signalAboutToRemove(PlaylistItem *)),
+ this, SLOT(slotItemAboutToDie(PlaylistItem *)));
+
+ m_initialized = true;
+}
+
+TrackSequenceManager::TrackSequenceManager() :
+ QObject(),
+ m_playlist(0),
+ m_playNextItem(0),
+ m_popupMenu(0),
+ m_iterator(0),
+ m_initialized(false)
+{
+ m_defaultIterator = new DefaultSequenceIterator();
+ m_iterator = m_defaultIterator;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// protected slots
+/////////////////////////////////////////////////////////////////////////////
+
+void TrackSequenceManager::slotItemAboutToDie(PlaylistItem *item)
+{
+ if(item == m_playNextItem)
+ m_playNextItem = 0;
+
+ m_iterator->itemAboutToDie(item);
+}
+
+#include "tracksequencemanager.moc"
+
+// vim: set et sw=4 tw=0:
diff --git a/juk/tracksequencemanager.h b/juk/tracksequencemanager.h
new file mode 100644
index 00000000..3aa186cd
--- /dev/null
+++ b/juk/tracksequencemanager.h
@@ -0,0 +1,192 @@
+/***************************************************************************
+ begin : Thu Aug 19 2004
+ copyright : (C) 2002 - 2004 by Michael Pyne
+ email : michael.pyne@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef _TRACKSEQUENCEMANAGER_H
+#define _TRACKSEQUENCEMANAGER_H
+
+#include <qobject.h>
+
+class KPopupMenu;
+class TrackSequenceIterator;
+class PlaylistItem;
+class Playlist;
+
+/**
+ * This class is responsible for managing the music play sequence for JuK.
+ * Instead of playlists deciding which song goes next, this class is used to
+ * do so. You can replace the iterator used as well, although the class
+ * provides a default iterator that supports random play and playlist looping.
+ *
+ * @see Playlist
+ * @see TrackSequenceIterator
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ */
+class TrackSequenceManager : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Destroys the track sequence manager. The sequence iterators will also
+ * be deleted, but the playlist, popup menu, and playlist items will not be
+ * touched.
+ */
+ ~TrackSequenceManager();
+
+ /**
+ * This function installs a new iterator to be used instead of the old
+ * one. TrackSequenceManager will control the iterator after that,
+ * deleting the iterator when another is installed, or when the
+ * TrackSequenceManager is destroyed.
+ *
+ * @param iterator the iterator to install, or 0 for the default
+ * @return true if installation successfully happened
+ */
+ bool installIterator(TrackSequenceIterator *iterator);
+
+ /**
+ * @return currently selected iterator
+ */
+ TrackSequenceIterator *iterator() const { return m_iterator; }
+
+ /**
+ * This function returns a pointer to the currently set iterator, and
+ * then removes the TrackSequenceManager's pointer to the iterator without
+ * deleting the iterator. You should only do this if you are going to be
+ * using @see installIterator to give control of the iterator back to the
+ * TrackSequenceManager at some point. Also, you must install a
+ * replacement iterator before the TrackSequenceManager is otherwise
+ * used. If you use this function, you must manually set the current
+ * item of the iterator you replace the old one with (if you want).
+ *
+ * @see installIterator
+ * @return the currently set iterator.
+ */
+ TrackSequenceIterator *takeIterator();
+
+ /**
+ * Returns the global TrackSequenceManager object. This is the only way to
+ * access the TrackSequenceManager.
+ *
+ * @return the global TrackSequenceManager
+ */
+ static TrackSequenceManager *instance();
+
+ /**
+ * Returns the next track, and advances in the current sequence..
+ *
+ * @return the next track in the current sequence, or 0 if the end has
+ * been reached
+ */
+ PlaylistItem *nextItem();
+
+ /**
+ * Returns the previous track, and backs up in the current sequence. Note
+ * that if you have an item x, nextItem(previousItem(x)) is not guaranteed
+ * to equal x, even ignoring the effect of hitting the end of list.
+ *
+ * @return the previous track in the current sequence, or 0 if the
+ * beginning has been reached
+ */
+ PlaylistItem *previousItem();
+
+ /**
+ * @return the current track in the current sequence, or 0 if there is no
+ * current track (for example, an empty playlist)
+ */
+ PlaylistItem *currentItem() const;
+
+ /**
+ * @return the current KPopupMenu used by the manager, or 0 if none is
+ * set
+ */
+ KPopupMenu *popupMenu() const { return m_popupMenu; }
+
+ /**
+ * @return the TrackSequenceManager's idea of the current playlist
+ */
+ Playlist *currentPlaylist() const { return m_playlist; }
+
+public slots:
+ /**
+ * Set the next item to play to @p item
+ *
+ * @param item the next item to play
+ */
+ void setNextItem(PlaylistItem *item);
+
+ /**
+ * Sets the current playlist. This is necessary in order for some of the
+ * actions in the popup menu used by this class to work. Note that the
+ * current playlist is not necessarily the same as the playlist that is
+ * playlist. The TrackSequenceManager does not own @p list after this
+ * call.
+ *
+ * @param list the current playlist
+ */
+ void setCurrentPlaylist(Playlist *list);
+
+ /**
+ * Sets the current item to @p item. You should try to avoid calling this
+ * function, instead allowing the manager to perform its work. However,
+ * this function is useful for clearing the current item. Remember that
+ * you must have a valid playlist to iterate if you clear the current item.
+ *
+ * @param item the PlaylistItem that is currently playing. Set to 0 if
+ * there is no item playing.
+ */
+ void setCurrent(PlaylistItem *item);
+
+private:
+ /**
+ * Sets up various connections, to be run after the GUI is running.
+ * Automatically run by instance().
+ *
+ * @see instance
+ */
+ void initialize();
+
+ /**
+ * Constructs the sequence manager. The constructor will work even before
+ * the GUI has been created. Note that you can't actually construct an
+ * object with this function, use instance().
+ *
+ * @see instance
+ */
+ TrackSequenceManager();
+
+protected slots:
+
+ /**
+ * This slot should be called when @a item is about to be deleted, so that
+ * the TrackSequenceManager can make sure that any pointers held pointing
+ * to @a item are corrected.
+ *
+ * @param item The PlaylistItem about to be deleted.
+ */
+ void slotItemAboutToDie(PlaylistItem *item);
+
+private:
+ Playlist *m_playlist;
+ PlaylistItem *m_curItem, *m_playNextItem;
+ KPopupMenu *m_popupMenu;
+ TrackSequenceIterator *m_iterator;
+ TrackSequenceIterator *m_defaultIterator;
+ bool m_initialized;
+};
+
+#endif /* _TRACKSEQUENCEMANAGER_H */
+
+// vim: set et sw=4:
diff --git a/juk/treeviewitemplaylist.cpp b/juk/treeviewitemplaylist.cpp
new file mode 100644
index 00000000..9096979a
--- /dev/null
+++ b/juk/treeviewitemplaylist.cpp
@@ -0,0 +1,93 @@
+/***************************************************************************
+ begin : Mon Jun 21 2004
+ copyright : (C) 2004 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+
+#include <qstringlist.h>
+#include <qlistview.h>
+
+#include "collectionlist.h"
+#include "treeviewitemplaylist.h"
+#include "tag.h"
+#include "playlistitem.h"
+#include "playlistsearch.h"
+#include "tagtransactionmanager.h"
+
+TreeViewItemPlaylist::TreeViewItemPlaylist(PlaylistCollection *collection,
+ const PlaylistSearch &search,
+ const QString &name) :
+ SearchPlaylist(collection, search, name, false)
+{
+ PlaylistSearch::Component component = *(search.components().begin());
+ m_columnType = static_cast<PlaylistItem::ColumnType>(*(component.columns().begin()));
+}
+
+void TreeViewItemPlaylist::retag(const QStringList &files, Playlist *)
+{
+ CollectionList *collection = CollectionList::instance();
+
+ if(files.isEmpty())
+ return;
+
+ QString changedTag = i18n("artist");
+ if(m_columnType == PlaylistItem::GenreColumn)
+ changedTag = i18n("genre");
+ else if(m_columnType == PlaylistItem::AlbumColumn)
+ changedTag = i18n("album");
+
+ if(KMessageBox::warningContinueCancelList(
+ this,
+ i18n("You are about to change the %1 on these files.").arg(changedTag),
+ files,
+ i18n("Changing Track Tags"),
+ KStdGuiItem::cont(),
+ "dragDropRetagWarn"
+ ) == KMessageBox::Cancel)
+ {
+ return;
+ }
+
+ QStringList::ConstIterator it;
+ for(it = files.begin(); it != files.end(); ++it) {
+ CollectionListItem *item = collection->lookup(*it);
+ if(!item)
+ continue;
+
+ Tag *tag = TagTransactionManager::duplicateTag(item->file().tag());
+ switch(m_columnType) {
+ case PlaylistItem::ArtistColumn:
+ tag->setArtist(name());
+ break;
+
+ case PlaylistItem::AlbumColumn:
+ tag->setAlbum(name());
+ break;
+
+ case PlaylistItem::GenreColumn:
+ tag->setGenre(name());
+ break;
+
+ default:
+ kdDebug() << "Unhandled column type editing " << *it << endl;
+ }
+
+ TagTransactionManager::instance()->changeTagOnItem(item, tag);
+ }
+}
+
+#include "treeviewitemplaylist.moc"
diff --git a/juk/treeviewitemplaylist.h b/juk/treeviewitemplaylist.h
new file mode 100644
index 00000000..6b0e13c2
--- /dev/null
+++ b/juk/treeviewitemplaylist.h
@@ -0,0 +1,43 @@
+/***************************************************************************
+ begin : Mon Jun 21 2004
+ copyright : (C) 2004 by Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef TREEVIEWITEMPLAYLIST_H
+#define TREEVIEWITEMPLAYLIST_H
+
+#include "searchplaylist.h"
+#include "playlistitem.h"
+
+class QStringList;
+
+class TreeViewItemPlaylist : public SearchPlaylist
+{
+ Q_OBJECT
+
+public:
+ TreeViewItemPlaylist(PlaylistCollection *collection,
+ const PlaylistSearch &search = PlaylistSearch(),
+ const QString &name = QString::null);
+
+ virtual bool searchIsEditable() const { return false; }
+ void retag(const QStringList &files, Playlist *donorPlaylist);
+
+signals:
+ void signalTagsChanged();
+
+private:
+ PlaylistItem::ColumnType m_columnType;
+};
+
+#endif // TREEVIEWITEMPLAYLIST_H
diff --git a/juk/upcomingplaylist.cpp b/juk/upcomingplaylist.cpp
new file mode 100644
index 00000000..a9cdbcb7
--- /dev/null
+++ b/juk/upcomingplaylist.cpp
@@ -0,0 +1,277 @@
+/***************************************************************************
+ begin : Thu Aug 19 2004
+ copyright : (C) 2002 - 2004 by Michael Pyne
+ email : michael.pyne@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kaction.h>
+
+#include "upcomingplaylist.h"
+#include "playlistitem.h"
+#include "playlistcollection.h"
+#include "tracksequencemanager.h"
+#include "collectionlist.h"
+#include "actioncollection.h"
+
+using namespace ActionCollection;
+
+UpcomingPlaylist::UpcomingPlaylist(PlaylistCollection *collection, int defaultSize) :
+ Playlist(collection, true),
+ m_active(false),
+ m_oldIterator(0),
+ m_defaultSize(defaultSize)
+{
+ setName(i18n("Play Queue"));
+ setAllowDuplicates(true);
+ setSorting(-1);
+}
+
+UpcomingPlaylist::~UpcomingPlaylist()
+{
+ removeIteratorOverride();
+}
+
+void UpcomingPlaylist::initialize()
+{
+ // Prevent duplicate initialization.
+
+ if(m_active)
+ return;
+
+ m_active = true;
+
+ m_oldIterator = manager()->takeIterator();
+ manager()->installIterator(new UpcomingSequenceIterator(this));
+
+ if(!m_oldIterator->current())
+ m_oldIterator->prepareToPlay(CollectionList::instance());
+ else
+ manager()->iterator()->setCurrent(m_oldIterator->current());
+}
+
+void UpcomingPlaylist::appendItems(const PlaylistItemList &itemList)
+{
+ initialize();
+
+ if(itemList.isEmpty())
+ return;
+
+ PlaylistItem *after = static_cast<PlaylistItem *>(lastItem());
+
+ for(PlaylistItemList::ConstIterator it = itemList.begin(); it != itemList.end(); ++it) {
+ after = createItem(*it, after);
+ m_playlistIndex.insert(after, (*it)->playlist());
+ }
+
+ dataChanged();
+ slotWeightDirty();
+}
+
+void UpcomingPlaylist::playNext()
+{
+ initialize();
+
+ PlaylistItem *next = TrackSequenceManager::instance()->nextItem();
+
+ if(next) {
+ setPlaying(next);
+ Playlist *source = m_playlistIndex[next];
+ if(source) {
+ PlaylistList l;
+ l.append(this);
+ source->synchronizePlayingItems(l, false);
+ }
+ }
+ else {
+ removeIteratorOverride();
+
+ // Normally we continue to play the currently playing item that way
+ // a user can continue to hear their song when deselecting Play Queue.
+ // However we're technically still "playing" when the queue empties and
+ // we reinstall the old iterator so in this situation manually advance
+ // to the next track. (Otherwise we hear the same song twice in a row
+ // during the transition.
+
+ setPlaying(manager()->nextItem());
+ }
+}
+
+void UpcomingPlaylist::clearItem(PlaylistItem *item, bool emitChanged)
+{
+ m_playlistIndex.remove(item);
+ Playlist::clearItem(item, emitChanged);
+}
+
+void UpcomingPlaylist::addFiles(const QStringList &files, PlaylistItem *after)
+{
+ CollectionList::instance()->addFiles(files, after);
+
+ PlaylistItemList l;
+ for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it) {
+ FileHandle f(*it);
+ PlaylistItem *i = CollectionList::instance()->lookup(f.absFilePath());
+ if(i)
+ l.append(i);
+ }
+
+ appendItems(l);
+}
+
+QMap< PlaylistItem::Pointer, QGuardedPtr<Playlist> > &UpcomingPlaylist::playlistIndex()
+{
+ return m_playlistIndex;
+}
+
+void UpcomingPlaylist::removeIteratorOverride()
+{
+ if(!m_active)
+ return;
+
+ m_active = false; // Allow re-initialization.
+
+ if(!m_oldIterator)
+ return;
+
+ // Install the old track iterator.
+
+ manager()->installIterator(m_oldIterator);
+
+ // If we have an item that is currently playing, allow it to keep playing.
+ // Otherwise, just reset to the default iterator (probably not playing
+ // anything.)
+ // XXX: Reset to the last playing playlist?
+
+ m_oldIterator->reset();
+ if(playingItem())
+ m_oldIterator->setCurrent(playingItem()->collectionItem());
+
+ setPlaying(manager()->currentItem(), true);
+
+ Watched::currentChanged();
+}
+
+TrackSequenceManager *UpcomingPlaylist::manager() const
+{
+ return TrackSequenceManager::instance();
+}
+
+UpcomingPlaylist::UpcomingSequenceIterator::UpcomingSequenceIterator(UpcomingPlaylist *playlist) :
+ TrackSequenceIterator(), m_playlist(playlist)
+{
+}
+
+UpcomingPlaylist::UpcomingSequenceIterator::UpcomingSequenceIterator(const UpcomingSequenceIterator &other) :
+ TrackSequenceIterator(other), m_playlist(other.m_playlist)
+{
+}
+
+UpcomingPlaylist::UpcomingSequenceIterator::~UpcomingSequenceIterator()
+{
+}
+
+void UpcomingPlaylist::UpcomingSequenceIterator::advance()
+{
+ PlaylistItem *item = m_playlist->firstChild();
+
+ if(item) {
+ PlaylistItem *next = static_cast<PlaylistItem *>(item->nextSibling());
+ m_playlist->clearItem(item);
+ setCurrent(next);
+ }
+}
+
+void UpcomingPlaylist::UpcomingSequenceIterator::backup()
+{
+}
+
+UpcomingPlaylist::UpcomingSequenceIterator *UpcomingPlaylist::UpcomingSequenceIterator::clone() const
+{
+ return new UpcomingSequenceIterator(*this);
+}
+
+void UpcomingPlaylist::UpcomingSequenceIterator::setCurrent(PlaylistItem *currentItem)
+{
+ if(!currentItem) {
+ TrackSequenceIterator::setCurrent(currentItem);
+ return;
+ }
+
+ // If the upcoming playlist is playing something, clear it out since
+ // apparently the user didn't want to hear it.
+
+ PlaylistItem *playingItem = m_playlist->playingItem();
+ if(playingItem && playingItem->playlist() == m_playlist && currentItem != playingItem)
+ m_playlist->clearItem(playingItem);
+
+ // If a different playlist owns this item, add it to the upcoming playlist
+
+ Playlist *p = currentItem->playlist();
+
+ if(p != m_playlist) {
+ PlaylistItem *i = m_playlist->createItem(currentItem, (PlaylistItem *) 0);
+ m_playlist->playlistIndex().insert(i, p);
+ m_playlist->dataChanged();
+ m_playlist->slotWeightDirty();
+ }
+ else {
+ // if(p == m_playlist) {
+
+ // Bump this item up to the top
+ m_playlist->takeItem(currentItem);
+ m_playlist->insertItem(currentItem);
+ }
+
+ TrackSequenceIterator::setCurrent(m_playlist->firstChild());
+}
+
+void UpcomingPlaylist::UpcomingSequenceIterator::reset()
+{
+ setCurrent(0);
+}
+
+void UpcomingPlaylist::UpcomingSequenceIterator::prepareToPlay(Playlist *)
+{
+ if(!m_playlist->items().isEmpty())
+ setCurrent(m_playlist->firstChild());
+}
+
+QDataStream &operator<<(QDataStream &s, const UpcomingPlaylist &p)
+{
+ PlaylistItemList l = const_cast<UpcomingPlaylist *>(&p)->items();
+
+ s << Q_INT32(l.count());
+
+ for(PlaylistItemList::ConstIterator it = l.begin(); it != l.end(); ++it)
+ s << (*it)->file().absFilePath();
+
+ return s;
+}
+
+QDataStream &operator>>(QDataStream &s, UpcomingPlaylist &p)
+{
+ QString fileName;
+ PlaylistItem *newItem = 0;
+ Q_INT32 count;
+
+ s >> count;
+
+ for(Q_INT32 i = 0; i < count; ++i) {
+ s >> fileName;
+ newItem = p.createItem(FileHandle(fileName), newItem, false);
+ }
+
+ return s;
+}
+
+// vim: set et ts=4 sw=4:
diff --git a/juk/upcomingplaylist.h b/juk/upcomingplaylist.h
new file mode 100644
index 00000000..ee0570f9
--- /dev/null
+++ b/juk/upcomingplaylist.h
@@ -0,0 +1,213 @@
+/***************************************************************************
+ begin : Thu Aug 19 2004
+ copyright : (C) 2002 - 2004 by Michael Pyne
+ email : michael.pyne@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef _UPCOMINGPLAYLIST_H
+#define _UPCOMINGPLAYLIST_H
+
+
+#include <qguardedptr.h>
+
+#include "playlist.h"
+#include "tracksequenceiterator.h"
+
+class TrackSequenceManager;
+
+/**
+ * A class to implement upcoming playlist support in JuK. It is closely
+ * associated with the UpcomingSequenceIterator class. It works by using
+ * whatever iterator is currently being used by the TrackSequenceManager
+ * instance when this playlist is constructed to form a list of upcoming tracks.
+ * After the playlist is created, tracks are played from top to bottom until
+ * the list is empty. If loop playlist is enabled, tracks are automatically
+ * added as tracks are removed. Also, enabling this playlist causes the base
+ * Playlist class to add an item to the context-menu to add the selected items
+ * to this playlist. If the user double-clicks any track to force it to play,
+ * it is added to the top of this playlist automatically, replacing any
+ * currently playing song.
+ *
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ * @see UpcomingSequenceIterator
+ */
+class UpcomingPlaylist : public Playlist
+{
+public:
+ /**
+ * Constructor for the UpcomingPlaylist object. You should only ever have
+ * one instance of this class. You should call the initialize() function
+ * before using the created object.
+ *
+ * @see initialize
+ * @param collection The PlaylistCollection that owns this playlist.
+ * @param defaultSize The default number of tracks to place in the playlist.
+ */
+ UpcomingPlaylist(PlaylistCollection *collection, int defaultSize = 15);
+ /**
+ * Destructor for the UpcomingPlaylist. This destructor will restore the
+ * iterator for the TrackSequenceManager, and if a song is playing when
+ * this playlist is removed, it will remain playing after the playlist is
+ * destroyed.
+ */
+ virtual ~UpcomingPlaylist();
+
+ /**
+ * This function initializes the upcoming playlist, so that you can create
+ * it before the GUI has been completely setup. If a song is playing when
+ * this function is called, then the song will be added to this playlist,
+ * automatically with no interruption in playback.
+ */
+ void initialize();
+
+ /**
+ * Appends the given items to the end of the playlist. Use this function
+ * instead of createItems() since this function ensures that the items are
+ * added to the end of the playlist.
+ *
+ * @see createItems(const PlaylistItemList &, PlaylistItem *)
+ * @param itemList The list of PlaylistItems to append.
+ */
+ void appendItems(const PlaylistItemList &itemList);
+
+ /**
+ * Reimplemented to set the playing item in both the source playlist
+ * and the upcoming playlist.
+ */
+ virtual void playNext();
+
+ /**
+ * Reimplemented to remove the item from the Playlist index.
+ */
+ virtual void clearItem(PlaylistItem *item, bool emitChanged = true);
+
+ virtual void addFiles(const QStringList &files, PlaylistItem *after = 0);
+
+ /**
+ * Returns a reference to the index between items in the list and the
+ * playlist that they came from. This is used to remap the currently
+ * playing item to the source playlist.
+ */
+ QMap<PlaylistItem::Pointer, QGuardedPtr<Playlist> > &playlistIndex();
+
+ bool active() const { return m_active; }
+
+private:
+
+ /**
+ * Internal function to restore the TrackSequenceManager to the state
+ * it was in when the object was constructed, except for the playing
+ * item.
+ */
+ void removeIteratorOverride();
+
+ /**
+ * This function returns the instance of the TrackSequenceManager.
+ *
+ * @return the TrackSequenceManager instance.
+ * @see TrackSequenceManager::instance()
+ */
+ TrackSequenceManager *manager() const;
+
+private:
+ class UpcomingSequenceIterator;
+ friend class UpcomingSequenceIterator;
+
+ bool m_active;
+ TrackSequenceIterator *m_oldIterator;
+ int m_defaultSize;
+ QMap<PlaylistItem::Pointer, QGuardedPtr<Playlist> > m_playlistIndex;
+};
+
+/**
+ * An implementation of TrackSequenceIterator designed to work with
+ * UpcomingPlaylist. It is installed by UpcomingPlaylist to ensure that the
+ * UpcomingPlaylist is in charge of the playback sequence.
+ *
+ * @see UpcomingPlaylist
+ * @see TrackSequenceManager
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ */
+class UpcomingPlaylist::UpcomingSequenceIterator : public TrackSequenceIterator
+{
+public:
+ /**
+ * Default constructor.
+ *
+ * @param playlist The UpcomingPlaylist this iterator belongs to.
+ */
+ UpcomingSequenceIterator(UpcomingPlaylist *playlist);
+
+ /**
+ * Copy constructor.
+ *
+ * @param other The UpcomingSequenceIterator to copy.
+ */
+ UpcomingSequenceIterator(const UpcomingSequenceIterator &other);
+
+ /**
+ * Destructor.
+ */
+ virtual ~UpcomingSequenceIterator();
+
+ /**
+ * Advances to the next song in the UpcomingPlaylist.
+ */
+ virtual void advance();
+
+ /**
+ * This function does nothing, as the currently playing song in the
+ * UpcomingPlaylist is always the first song in the sequence.
+ */
+ virtual void backup();
+
+ /**
+ * This function returns a perfect duplicate of the object it is called
+ * on, to avoid the C++ slicing problem.
+ *
+ * @return A pointer to a copy of the object it is called on.
+ */
+ virtual UpcomingSequenceIterator *clone() const;
+
+ /**
+ * This function sets the currently playing item to @a currentItem. If the
+ * item doesn't belong to the parent UpcomingPlaylist, it will be added to
+ * the UpcomingPlaylist, replacing any track that may be playing.
+ * Otherwise, it is moved up and set to play, replacing any track that may
+ * be playing.
+ *
+ * @param currentItem The PlaylistItem to play.
+ */
+ virtual void setCurrent(PlaylistItem *currentItem);
+
+ /**
+ * This function resets any internet state.
+ */
+ virtual void reset();
+
+ /**
+ * This function readies the UpcomingSequenceIterator for playback, by
+ * making sure the parent UpcomingPlaylist has items to play if it is
+ * empty.
+ */
+ virtual void prepareToPlay(Playlist *);
+
+private:
+ UpcomingPlaylist *m_playlist;
+};
+
+QDataStream &operator<<(QDataStream &s, const UpcomingPlaylist &p);
+QDataStream &operator>>(QDataStream &s, UpcomingPlaylist &p);
+
+#endif /* _UPCOMINGPLAYLIST_H */
+
+// vim: set et sw=4 ts=4:
diff --git a/juk/viewmode.cpp b/juk/viewmode.cpp
new file mode 100644
index 00000000..4bd4b9d8
--- /dev/null
+++ b/juk/viewmode.cpp
@@ -0,0 +1,425 @@
+/***************************************************************************
+ begin : Sat Jun 7 2003
+ copyright : (C) 2003 - 2004 by Scott Wheeler,
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kiconloader.h>
+#include <kstandarddirs.h>
+#include <kdebug.h>
+
+#include <qpixmap.h>
+#include <qpainter.h>
+#include <qfile.h>
+#include <qdir.h>
+#include <qdatastream.h>
+
+#include "viewmode.h"
+#include "playlistbox.h"
+#include "searchplaylist.h"
+#include "treeviewitemplaylist.h"
+#include "collectionlist.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// ViewMode
+////////////////////////////////////////////////////////////////////////////////
+
+ViewMode::ViewMode(PlaylistBox *b) :
+ QObject(b),
+ m_playlistBox(b),
+ m_visible(false),
+ m_needsRefresh(false)
+{
+ m_playlistBox->viewport()->installEventFilter(this);
+}
+
+ViewMode::~ViewMode()
+{
+
+}
+
+void ViewMode::paintCell(PlaylistBox::Item *item,
+ QPainter *painter,
+ const QColorGroup &colorGroup,
+ int column, int width, int)
+{
+ if(width < item->pixmap(column)->width())
+ return;
+
+ if(m_needsRefresh)
+ updateHeights();
+
+ QFontMetrics fm = painter->fontMetrics();
+
+ int y = item->listView()->itemMargin() + border;
+ const QPixmap *pm = item->pixmap(column);
+
+ if(item->isSelected()) {
+
+ painter->eraseRect(0, 0, width, item->height());
+
+ QPen oldPen = painter->pen();
+ QPen newPen = oldPen;
+
+ newPen.setWidth(5);
+ newPen.setJoinStyle(RoundJoin);
+ newPen.setColor(QColorGroup::Highlight);
+
+ painter->setPen(newPen);
+ painter->drawRect(border, border, width - border * 2, item->height() - border * 2 + 1);
+ painter->setPen(oldPen);
+
+ painter->fillRect(border, border, width - border * 2, item->height() - border * 2 + 1,
+ colorGroup.brush(QColorGroup::Highlight));
+ painter->setPen(colorGroup.highlightedText());
+ }
+ else
+ painter->eraseRect(0, 0, width, item->height());
+
+ if (!pm->isNull()) {
+ int x = (width - pm->width()) / 2;
+ x = QMAX(x, item->listView()->itemMargin());
+ painter->drawPixmap(x, y, *pm);
+ }
+ y += pm->height() + fm.height() - fm.descent();
+ for(QStringList::Iterator it = m_lines[item].begin(); it != m_lines[item].end(); ++it) {
+ int x = (width - fm.width(*it)) / 2;
+ x = QMAX(x, item->listView()->itemMargin());
+ painter->drawText(x, y, *it);
+ y += fm.height() - fm.descent();
+ }
+
+ if(item == item->listView()->dropItem())
+ paintDropIndicator(painter, width, item->height());
+}
+
+bool ViewMode::eventFilter(QObject *watched, QEvent *e)
+{
+ if(m_visible && watched == m_playlistBox->viewport() && e->type() == QEvent::Resize) {
+ QResizeEvent *re = static_cast<QResizeEvent *>(e);
+ if(re->size().width() != re->oldSize().width())
+ m_needsRefresh = true;
+ }
+
+ if(e->type() == QEvent::Hide)
+ m_needsRefresh = true;
+
+ return QObject::eventFilter(watched, e);
+}
+
+void ViewMode::setShown(bool shown)
+{
+ m_visible = shown;
+ if(shown) {
+ updateIcons(32);
+ m_needsRefresh = true;
+ }
+}
+
+void ViewMode::updateIcons(int size)
+{
+ for(QListViewItemIterator it(m_playlistBox); it.current(); ++it) {
+ PlaylistBox::Item *i = static_cast<PlaylistBox::Item *>(*it);
+ i->setPixmap(0, SmallIcon(i->iconName(), size));
+ }
+}
+
+void ViewMode::setupItem(PlaylistBox::Item *item) const
+{
+ const PlaylistBox *box = item->listView();
+ const int width = box->width() - box->verticalScrollBar()->width() - border * 2;
+ const int baseHeight = 2 * box->itemMargin() + 32 + border * 2;
+ const QFontMetrics fm = box->fontMetrics();
+ item->setHeight(baseHeight + (fm.height() - fm.descent()) * lines(item, fm, width).count());
+}
+
+void ViewMode::updateHeights()
+{
+ const int width = m_playlistBox->width() - m_playlistBox->verticalScrollBar()->width() - border * 2;
+
+ const int baseHeight = 2 * m_playlistBox->itemMargin() + 32 + border * 2;
+ const QFontMetrics fm = m_playlistBox->fontMetrics();
+
+ for(QListViewItemIterator it(m_playlistBox); it.current(); ++it) {
+ PlaylistBox::Item *i = static_cast<PlaylistBox::Item *>(it.current());
+ m_lines[i] = lines(i, fm, width);
+ const int height = baseHeight + (fm.height() - fm.descent()) * m_lines[i].count();
+ i->setHeight(height);
+ }
+
+ m_needsRefresh = false;
+}
+
+void ViewMode::paintDropIndicator(QPainter *painter, int width, int height) // static
+{
+ static const int border = 1;
+ static const int lineWidth = 2;
+
+ QPen oldPen = painter->pen();
+ QPen newPen = oldPen;
+
+ newPen.setWidth(lineWidth);
+ newPen.setStyle(DotLine);
+
+ painter->setPen(newPen);
+ painter->drawRect(border, border, width - border * 2, height - border * 2);
+ painter->setPen(oldPen);
+}
+
+QStringList ViewMode::lines(const PlaylistBox::Item *item,
+ const QFontMetrics &fm,
+ int width)
+{
+ // Here 32 is a bit arbitrary, but that's the width of the icons in this
+ // mode and seems to a reasonable lower bound.
+
+ if(width < 32)
+ return QStringList();
+
+ QString line = item->text();
+
+ QStringList l;
+
+ while(!line.isEmpty()) {
+ int textLength = line.length();
+ while(textLength > 0 &&
+ fm.width(line.mid(0, textLength).stripWhiteSpace()) +
+ item->listView()->itemMargin() * 2 > width)
+ {
+ int i = line.findRev(QRegExp( "\\W"), textLength - 1);
+ if(i > 0)
+ textLength = i;
+ else
+ textLength--;
+ }
+
+ l.append(line.mid(0, textLength).stripWhiteSpace());
+ line = line.mid(textLength);
+ }
+ return l;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CompactViewMode
+////////////////////////////////////////////////////////////////////////////////
+
+CompactViewMode::CompactViewMode(PlaylistBox *b) : ViewMode(b)
+{
+
+}
+
+CompactViewMode::~CompactViewMode()
+{
+
+}
+
+void CompactViewMode::paintCell(PlaylistBox::Item *item,
+ QPainter *painter,
+ const QColorGroup &colorGroup,
+ int column, int width, int align)
+{
+ item->KListViewItem::paintCell(painter, colorGroup, column, width, align);
+ if(item == item->listView()->dropItem())
+ paintDropIndicator(painter, width, item->height());
+}
+
+void CompactViewMode::setShown(bool shown)
+{
+ setVisible(shown);
+
+ if(shown) {
+ updateIcons(16);
+ updateHeights();
+ }
+}
+
+void CompactViewMode::updateHeights()
+{
+ for(QListViewItemIterator it(playlistBox()); it.current(); ++it)
+ it.current()->setup();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TreeViewMode
+////////////////////////////////////////////////////////////////////////////////
+
+TreeViewMode::TreeViewMode(PlaylistBox *b) : CompactViewMode(b),
+ m_treeViewItems(5003, false), m_dynamicListsFrozen(false), m_setup(false)
+{
+
+}
+
+TreeViewMode::~TreeViewMode()
+{
+
+}
+
+void TreeViewMode::setShown(bool show)
+{
+ CompactViewMode::setShown(show);
+
+ playlistBox()->setRootIsDecorated(show);
+
+ if(show) {
+ PlaylistBox::Item *collectionItem = PlaylistBox::Item::collectionItem();
+
+ if(!collectionItem)
+ return;
+
+ if(collectionItem && m_searchCategories.isEmpty())
+ setupDynamicPlaylists();
+ else {
+ for(QDictIterator<PlaylistBox::Item> it(m_searchCategories); it.current(); ++it)
+ it.current()->setVisible(true);
+ }
+
+ if(!m_setup) {
+ m_setup = true;
+ playlistBox()->setSorting(-1);
+ CollectionList::instance()->setupTreeViewEntries(this);
+ playlistBox()->setSorting(0);
+ playlistBox()->sort();
+ }
+ }
+ else {
+ for(QDictIterator<PlaylistBox::Item> it(m_searchCategories); it.current(); ++it)
+ it.current()->setVisible(false);
+ }
+}
+
+void TreeViewMode::removeItem(const QString &item, unsigned column)
+{
+ if(!m_setup)
+ return;
+
+ QString itemKey;
+ if(column == PlaylistItem::ArtistColumn)
+ itemKey = "artists" + item;
+ else if(column == PlaylistItem::GenreColumn)
+ itemKey = "genres" + item;
+ else if(column == PlaylistItem::AlbumColumn)
+ itemKey = "albums" + item;
+ else {
+ kdWarning() << k_funcinfo << "Unhandled column type " << column << endl;
+ return;
+ }
+
+ if(!m_treeViewItems.find(itemKey))
+ return;
+
+ TreeViewItemPlaylist *itemPlaylist = m_treeViewItems[itemKey];
+
+ if(m_dynamicListsFrozen) {
+ m_pendingItemsToRemove << itemKey;
+ return;
+ }
+
+ m_treeViewItems.remove(itemKey);
+ itemPlaylist->deleteLater();
+ emit signalPlaylistDestroyed(itemPlaylist);
+}
+
+void TreeViewMode::addItems(const QStringList &items, unsigned column)
+{
+ if(!m_setup)
+ return;
+
+ QString searchCategory;
+ if(column == PlaylistItem::ArtistColumn)
+ searchCategory = "artists";
+ else if(column == PlaylistItem::GenreColumn)
+ searchCategory = "genres";
+ else if(column == PlaylistItem::AlbumColumn)
+ searchCategory = "albums";
+ else {
+ kdWarning() << k_funcinfo << "Unhandled column type " << column << endl;
+ return;
+ }
+
+ QValueList<int> columns;
+ columns.append(column);
+
+ PlaylistSearch::Component::MatchMode mode = PlaylistSearch::Component::ContainsWord;
+ if(column != PlaylistItem::ArtistColumn)
+ mode = PlaylistSearch::Component::Exact;
+
+ PlaylistSearch::ComponentList components;
+ PlaylistList playlists;
+ playlists.append(CollectionList::instance());
+
+ QString itemKey, item;
+ PlaylistBox::Item *itemParent = m_searchCategories[searchCategory];
+
+ for(QStringList::ConstIterator it = items.begin(); it != items.end(); ++it) {
+ item = *it;
+ itemKey = searchCategory + item;
+
+ if(m_treeViewItems.find(itemKey))
+ continue;
+
+ components.clear();
+ components.append(PlaylistSearch::Component(item, false, columns, mode));
+
+ PlaylistSearch s(playlists, components, PlaylistSearch::MatchAny, false);
+
+ TreeViewItemPlaylist *p = new TreeViewItemPlaylist(playlistBox(), s, item);
+ playlistBox()->setupPlaylist(p, "midi", itemParent);
+ m_treeViewItems.insert(itemKey, p);
+ }
+}
+
+void TreeViewMode::setDynamicListsFrozen(bool frozen)
+{
+ m_dynamicListsFrozen = frozen;
+
+ if(frozen)
+ return;
+
+ QStringList categories;
+ categories << "artists" << "albums" << "genres";
+
+ for(QStringList::ConstIterator it = m_pendingItemsToRemove.begin();
+ it != m_pendingItemsToRemove.end();
+ ++it)
+ {
+ m_treeViewItems[*it]->deleteLater();
+ m_treeViewItems.remove(*it);
+ }
+
+ m_pendingItemsToRemove.clear();
+}
+
+void TreeViewMode::setupDynamicPlaylists()
+{
+ PlaylistBox::Item *i;
+ PlaylistBox::Item *collectionItem = PlaylistBox::Item::collectionItem();
+
+ i = new PlaylistBox::Item(collectionItem, "cdimage", i18n("Artists"));
+ m_searchCategories.insert("artists", i);
+
+ i = new PlaylistBox::Item(collectionItem, "cdimage", i18n("Albums"));
+ m_searchCategories.insert("albums", i);
+
+ i = new PlaylistBox::Item(collectionItem, "cdimage", i18n("Genres"));
+ m_searchCategories.insert("genres", i);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CoverManagerMode
+////////////////////////////////////////////////////////////////////////////////
+
+CoverManagerMode::CoverManagerMode(PlaylistBox *b) : ViewMode(b)
+{
+
+}
+
+#include "viewmode.moc"
diff --git a/juk/viewmode.h b/juk/viewmode.h
new file mode 100644
index 00000000..dcb5574c
--- /dev/null
+++ b/juk/viewmode.h
@@ -0,0 +1,159 @@
+/***************************************************************************
+ begin : Sat Jun 7 2003
+ copyright : (C) 2003 - 2004 by Scott Wheeler,
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef VIEWMODE_H
+#define VIEWMODE_H
+
+
+#include <qdict.h>
+
+#include "playlistbox.h"
+
+class QPainter;
+class QColorGroup;
+
+class SearchPlaylist;
+
+class ViewMode : public QObject
+{
+ Q_OBJECT
+
+public:
+ ViewMode(PlaylistBox *b);
+ virtual ~ViewMode();
+
+ virtual QString name() const { return i18n("Default"); }
+ virtual void setShown(bool shown);
+
+ virtual void paintCell(PlaylistBox::Item *item,
+ QPainter *painter,
+ const QColorGroup &colorGroup,
+ int column, int width, int align);
+
+ virtual bool eventFilter(QObject *watched, QEvent *e);
+ void queueRefresh() { m_needsRefresh = true; }
+
+ virtual void setupItem(PlaylistBox::Item *item) const;
+
+ virtual void setupDynamicPlaylists() {}
+ /**
+ * If the view mode has dynamic lists, this function is used to temporarily
+ * freeze them to prevent them from deleting dynamic elements.
+ */
+ virtual void setDynamicListsFrozen(bool /* frozen */) {}
+
+ /**
+ * Used for dynamic view modes. This function will be called when \p items
+ * are added to \p column (even if the view mode hasn't been shown yet).
+ */
+ virtual void addItems(const QStringList &items, unsigned column)
+ {
+ (void) items;
+ (void) column;
+ }
+
+ /**
+ * Used for dynamic view modes. This function will be called when \p item
+ * is removed from \p column (even if the view mode hasn't been shown yet).
+ */
+ virtual void removeItem(const QString &item, unsigned column)
+ {
+ (void) item;
+ (void) column;
+ }
+
+protected:
+ PlaylistBox *playlistBox() const { return m_playlistBox; }
+ bool visible() const { return m_visible; }
+ void setVisible(bool v) { m_visible = v; }
+ void updateIcons(int size);
+ virtual void updateHeights();
+ static void paintDropIndicator(QPainter *painter, int width, int height);
+
+private:
+ static QStringList lines(const PlaylistBox::Item *item, const QFontMetrics &fm, int width);
+
+ PlaylistBox *m_playlistBox;
+ bool m_visible;
+ bool m_needsRefresh;
+ QMap<PlaylistBox::Item *, QStringList> m_lines;
+ static const int border = 4;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+class CompactViewMode : public ViewMode
+{
+public:
+ CompactViewMode(PlaylistBox *b);
+ virtual ~CompactViewMode();
+
+ virtual QString name() const { return i18n("Compact"); }
+ virtual void setShown(bool shown);
+
+ virtual void paintCell(PlaylistBox::Item *item,
+ QPainter *painter,
+ const QColorGroup &colorGroup,
+ int column, int width, int align);
+
+ virtual void setupItem(PlaylistBox::Item *item) const { item->KListViewItem::setup(); }
+protected:
+ virtual void updateHeights();
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+class TreeViewItemPlaylist;
+
+class TreeViewMode : public CompactViewMode
+{
+ Q_OBJECT
+
+public:
+ TreeViewMode(PlaylistBox *l);
+ virtual ~TreeViewMode();
+
+ virtual QString name() const { return i18n("Tree"); }
+ virtual void setShown(bool shown);
+ virtual void setupDynamicPlaylists();
+ virtual void setDynamicListsFrozen(bool frozen);
+
+ virtual void removeItem(const QString &item, unsigned column);
+ virtual void addItems(const QStringList &items, unsigned column);
+
+signals:
+ void signalPlaylistDestroyed(Playlist*);
+
+private:
+ QDict<PlaylistBox::Item> m_searchCategories;
+ QDict<TreeViewItemPlaylist> m_treeViewItems;
+ QStringList m_pendingItemsToRemove;
+ bool m_dynamicListsFrozen;
+ bool m_setup;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+class CoverManagerMode : public ViewMode
+{
+ Q_OBJECT
+
+public:
+ CoverManagerMode(PlaylistBox *b);
+ virtual QString name() const { return i18n("Cover Manager"); }
+ //virtual void setShown(bool shown);
+};
+
+#endif
diff --git a/juk/webimagefetcher.cpp b/juk/webimagefetcher.cpp
new file mode 100644
index 00000000..3e806bff
--- /dev/null
+++ b/juk/webimagefetcher.cpp
@@ -0,0 +1,224 @@
+/***************************************************************************
+ copyright : (C) 2004 Nathan Toone <nathan@toonetown.com>
+ copyright : (C) 2007 Michael Pyne <michael.pyne@kdemail.com>
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <qhttp.h>
+#include <qdom.h>
+#include <qwaitcondition.h>
+
+#include <kapplication.h>
+#include <kstatusbar.h>
+#include <kdebug.h>
+#include <kmainwindow.h>
+#include <klocale.h>
+#include <kinputdialog.h>
+#include <kurl.h>
+
+#include "covermanager.h"
+#include "webimagefetcher.h"
+#include "webimagefetcherdialog.h"
+#include "tag.h"
+
+WebImage::WebImage()
+{
+}
+
+WebImage::WebImage(const QString &imageURL, const QString &thumbURL,
+ int width, int height) :
+ m_imageURL(imageURL),
+ m_thumbURL(thumbURL),
+ m_size(QString("\n%1 x %2").arg(width).arg(height))
+{
+}
+
+
+WebImageFetcher::WebImageFetcher(QObject *parent)
+ : QObject(parent),
+ m_connection(new QHttp(this)),
+ m_connectionId(-1),
+ m_dialog(0)
+{
+ connect(m_connection, SIGNAL(requestFinished(int,bool)), SLOT(slotWebRequestFinished(int,bool)));
+}
+
+WebImageFetcher::~WebImageFetcher()
+{
+ delete m_dialog;
+}
+
+void WebImageFetcher::setFile(const FileHandle &file)
+{
+ m_file = file;
+ m_searchString = QString(file.tag()->artist() + ' ' + file.tag()->album());
+
+ if(m_dialog)
+ m_dialog->setFile(file);
+}
+
+void WebImageFetcher::abortSearch()
+{
+ m_connection->abort();
+}
+
+void WebImageFetcher::chooseCover()
+{
+ slotLoadImageURLs();
+}
+
+void WebImageFetcher::slotLoadImageURLs()
+{
+ m_imageList.clear();
+
+ KURL url("http://search.yahooapis.com/ImageSearchService/V1/imageSearch");
+ url.addQueryItem("appid", "org.kde.juk/kde3");
+ url.addQueryItem("query", m_searchString);
+ url.addQueryItem("results", "25");
+
+ kdDebug(65432) << "Using request " << url.encodedPathAndQuery() << endl;
+
+ m_connection->setHost(url.host());
+ m_connectionId = m_connection->get(url.encodedPathAndQuery());
+
+ // Wait for the results...
+}
+
+void WebImageFetcher::slotWebRequestFinished(int id, bool error)
+{
+ if(id != m_connectionId)
+ return;
+
+ if(error) {
+ kdError(65432) << "Error reading image results from Yahoo!\n";
+ kdError(65432) << m_connection->errorString() << endl;
+ return;
+ }
+
+ QDomDocument results("ResultSet");
+
+ QString errorStr;
+ int errorCol, errorLine;
+ if(!results.setContent(m_connection->readAll(), &errorStr, &errorLine, &errorCol)) {
+ kdError(65432) << "Unable to create XML document from Yahoo results.\n";
+ kdError(65432) << "Line " << errorLine << ", " << errorStr << endl;
+ return;
+ }
+
+ QDomNode n = results.documentElement();
+
+ bool hasNoResults = false;
+
+ if(n.isNull()) {
+ kdDebug(65432) << "No document root in XML results??\n";
+ hasNoResults = true;
+ }
+ else {
+ QDomElement result = n.toElement();
+ if(result.attribute("totalResultsReturned").toInt() == 0)
+ kdDebug(65432) << "Search returned " << result.attribute("totalResultsAvailable") << " results.\n";
+
+ if(result.isNull() || !result.hasAttribute("totalResultsReturned") ||
+ result.attribute("totalResultsReturned").toInt() == 0)
+ {
+ hasNoResults = true;
+ }
+ }
+
+ if(hasNoResults)
+ {
+ kdDebug(65432) << "Search returned no results.\n";
+ requestNewSearchTerms(true /* no results */);
+ return;
+ }
+
+ // Go through each of the top (result) nodes
+
+ n = n.firstChild();
+ while(!n.isNull()) {
+ QDomNode resultUrl = n.namedItem("Url");
+ QDomNode thumbnail = n.namedItem("Thumbnail");
+ QDomNode height = n.namedItem("Height");
+ QDomNode width = n.namedItem("Width");
+
+ // We have the necessary info, move to next node before we forget.
+ n = n.nextSibling();
+
+ if(resultUrl.isNull() || thumbnail.isNull() || height.isNull() || width.isNull()) {
+ kdError(65432) << "Invalid result returned, skipping.\n";
+ continue;
+ }
+
+ m_imageList.append(
+ WebImage(
+ resultUrl.toElement().text(),
+ thumbnail.namedItem("Url").toElement().text(),
+ width.toElement().text().toInt(),
+ height.toElement().text().toInt()
+ )
+ );
+ }
+
+ // Have results, show them and pick one.
+
+ if(!m_dialog) {
+ m_dialog = new WebImageFetcherDialog(m_imageList, m_file, 0);
+ m_dialog->setModal(true);
+
+ connect(m_dialog, SIGNAL(coverSelected()), SLOT(slotCoverChosen()));
+ connect(m_dialog, SIGNAL(newSearchRequested()), SLOT(slotNewSearch()));
+ }
+
+ m_dialog->refreshScreen(m_imageList);
+ m_dialog->show();
+}
+
+void WebImageFetcher::slotCoverChosen()
+{
+ QPixmap pixmap = m_dialog->result();
+ if(pixmap.isNull()) {
+ kdError(65432) << "Selected pixmap is null for some reason.\n";
+ return;
+ }
+
+ kdDebug(65432) << "Adding new cover for " << m_file.tag()->fileName() << endl;
+ coverKey newId = CoverManager::addCover(pixmap, m_file.tag()->artist(), m_file.tag()->album());
+ emit signalCoverChanged(newId);
+}
+
+void WebImageFetcher::slotNewSearch()
+{
+ requestNewSearchTerms();
+}
+
+void WebImageFetcher::displayWaitMessage()
+{
+ KStatusBar *statusBar = static_cast<KMainWindow *>(kapp->mainWidget())->statusBar();
+ statusBar->message(i18n("Searching for Images. Please Wait..."));
+ slotLoadImageURLs();
+ statusBar->clear();
+}
+
+void WebImageFetcher::requestNewSearchTerms(bool noResults)
+{
+ bool ok;
+ QString search = KInputDialog::getText(i18n("Cover Downloader"),
+ noResults ?
+ i18n("No matching images found, please enter new search terms:") :
+ i18n("Enter new search terms:"),
+ m_searchString, &ok);
+ if(ok && !search.isEmpty()) {
+ m_searchString = search;
+ displayWaitMessage(); // This kicks off the new search.
+ }
+}
+
+#include "webimagefetcher.moc"
diff --git a/juk/webimagefetcher.h b/juk/webimagefetcher.h
new file mode 100644
index 00000000..796e205e
--- /dev/null
+++ b/juk/webimagefetcher.h
@@ -0,0 +1,93 @@
+/***************************************************************************
+ copyright : (C) 2004 Nathan Toone
+ email : nathan@toonetown.com
+ copyright : (C) 2007 Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef WEBIMAGEFETCHER_H
+#define WEBIMAGEFETCHER_H
+
+#include <kdialogbase.h>
+
+#include <qpixmap.h>
+#include <qstringlist.h>
+#include <qregexp.h>
+
+#include "filehandle.h"
+
+class KURL;
+
+class QHttp;
+
+class WebImageFetcherDialog;
+
+class WebImage
+{
+public:
+ WebImage();
+
+ WebImage(const QString &imageURL,
+ const QString &thumbURL,
+ int width, int height);
+
+ QString imageURL() const { return m_imageURL; }
+ QString thumbURL() const { return m_thumbURL; }
+ QString size() const { return m_size; }
+
+private:
+ QString m_imageURL;
+ QString m_thumbURL;
+ QString m_size;
+};
+
+typedef QValueList<WebImage> WebImageList;
+
+class WebImageFetcher : public QObject
+{
+ Q_OBJECT
+
+public:
+ WebImageFetcher(QObject *parent);
+ ~WebImageFetcher();
+
+ void setFile(const FileHandle &file);
+ void chooseCover();
+
+public slots:
+ void abortSearch();
+
+signals:
+ void signalNewSearch(WebImageList &images);
+ void signalCoverChanged(int coverId);
+
+private:
+ void displayWaitMessage();
+ void requestNewSearchTerms(bool noResults = false);
+
+private slots:
+ void slotLoadImageURLs();
+ void slotWebRequestFinished(int id, bool error);
+ void slotCoverChosen();
+ void slotNewSearch();
+
+private:
+ FileHandle m_file;
+ QString m_searchString;
+ QString m_loadedQuery;
+ WebImageList m_imageList;
+ uint m_selectedIndex;
+ QHttp *m_connection;
+ int m_connectionId;
+ WebImageFetcherDialog *m_dialog;
+};
+#endif
diff --git a/juk/webimagefetcherdialog.cpp b/juk/webimagefetcherdialog.cpp
new file mode 100644
index 00000000..cc14ed61
--- /dev/null
+++ b/juk/webimagefetcherdialog.cpp
@@ -0,0 +1,235 @@
+/***************************************************************************
+ copyright : (C) 2004 Nathan Toone
+ email : nathan@toonetown.com
+ copyright : (C) 2007 Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kapplication.h>
+#include <kio/netaccess.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <krun.h>
+#include <kcombobox.h>
+#include <kiconloader.h>
+#include <kurllabel.h>
+
+#include <qvbox.h>
+#include <qlayout.h>
+#include <qimage.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qeventloop.h>
+
+#include "webimagefetcherdialog.h"
+#include "tag.h"
+
+WebImageFetcherDialog::WebImageFetcherDialog(const WebImageList &imageList,
+ const FileHandle &file,
+ QWidget *parent) :
+ KDialogBase(parent, "internet_image_fetcher", true, QString::null,
+ Ok | Cancel | User1 , NoDefault, true),
+ m_pixmap(QPixmap()),
+ m_imageList(imageList),
+ m_file(file)
+{
+ disableResize();
+
+ QWidget *mainBox = new QWidget(this);
+ QBoxLayout *mainLayout = new QVBoxLayout(mainBox);
+
+ m_iconWidget = new KIconView(mainBox);
+ m_iconWidget->setResizeMode(QIconView::Adjust);
+ m_iconWidget->setSpacing(10);
+ m_iconWidget->setFixedSize(500,550);
+ m_iconWidget->arrangeItemsInGrid();
+ m_iconWidget->setItemsMovable(false);
+ mainLayout->addWidget(m_iconWidget);
+ connect(m_iconWidget, SIGNAL(executed(QIconViewItem *)),
+ this, SLOT(slotOk()));
+
+ // Before changing the code below be sure to check the attribution terms
+ // of the Yahoo Image Search API.
+ // http://developer.yahoo.com/attribution/
+ KURLLabel *logoLabel = new KURLLabel(mainBox);
+ logoLabel->setURL("http://developer.yahoo.com/about/");
+ logoLabel->setPixmap(UserIcon("yahoo_credit"));
+ logoLabel->setMargin(15); // Allow large margin per attribution terms.
+ logoLabel->setUseTips(true); // Show URL in tooltip.
+ connect(logoLabel, SIGNAL(leftClickedURL(const QString &)),
+ SLOT(showCreditURL(const QString &)));
+
+ QBoxLayout *creditLayout = new QHBoxLayout(mainLayout);
+ creditLayout->addStretch(); // Left spacer
+ creditLayout->addWidget(logoLabel);
+ creditLayout->addStretch(); // Right spacer
+
+ setMainWidget(mainBox);
+ setButtonText(User1, i18n("New Search"));
+}
+
+WebImageFetcherDialog::~WebImageFetcherDialog()
+{
+}
+
+void WebImageFetcherDialog::showCreditURL(const QString &url)
+{
+ // Don't use static member since I'm sure that someday knowing my luck
+ // Yahoo will change their mimetype they serve.
+ (void) new KRun(KURL(url), topLevelWidget());
+}
+
+void WebImageFetcherDialog::setLayout()
+{
+ setCaption(QString("%1 - %2 (%3)")
+ .arg(m_file.tag()->artist())
+ .arg(m_file.tag()->album())
+ .arg(m_imageList.size()));
+
+ m_iconWidget->clear();
+ for(uint i = 0; i < m_imageList.size(); i++)
+ new CoverIconViewItem(m_iconWidget, m_imageList[i]);
+
+ adjustSize();
+}
+
+void WebImageFetcherDialog::setImageList(const WebImageList &imageList)
+{
+ m_imageList = imageList;
+}
+
+void WebImageFetcherDialog::setFile(const FileHandle &file)
+{
+ m_file = file;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public slots
+////////////////////////////////////////////////////////////////////////////////
+
+void WebImageFetcherDialog::refreshScreen(WebImageList &imageList)
+{
+ setImageList(imageList);
+ setLayout();
+}
+
+int WebImageFetcherDialog::exec()
+{
+ setLayout();
+ return KDialogBase::exec();
+}
+
+void WebImageFetcherDialog::slotOk()
+{
+ uint selectedIndex = m_iconWidget->index(m_iconWidget->currentItem());
+ m_pixmap = pixmapFromURL(m_imageList[selectedIndex].imageURL());
+
+ if(m_pixmap.isNull()) {
+ KMessageBox::sorry(this,
+ i18n("The cover you have selected is unavailable. Please select another."),
+ i18n("Cover Unavailable"));
+ QPixmap blankPix;
+ blankPix.resize(80, 80);
+ blankPix.fill();
+ m_iconWidget->currentItem()->setPixmap(blankPix, true, true);
+ return;
+ }
+
+ accept();
+ emit coverSelected();
+}
+
+void WebImageFetcherDialog::slotCancel()
+{
+ m_pixmap = QPixmap();
+ reject();
+}
+
+void WebImageFetcherDialog::slotUser1()
+{
+ m_pixmap = QPixmap();
+ accept();
+ emit newSearchRequested();
+}
+
+QPixmap WebImageFetcherDialog::fetchedImage(uint index) const
+{
+ return (index > m_imageList.count()) ? QPixmap() : pixmapFromURL(m_imageList[index].imageURL());
+}
+
+QPixmap WebImageFetcherDialog::pixmapFromURL(const KURL &url) const
+{
+ QString file;
+
+ if(KIO::NetAccess::download(url, file, 0)) {
+ QPixmap pixmap = QPixmap(file);
+ KIO::NetAccess::removeTempFile(file);
+ return pixmap;
+ }
+ KIO::NetAccess::removeTempFile(file);
+ return QPixmap();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CoverIconViewItem
+////////////////////////////////////////////////////////////////////////////////
+
+CoverIconViewItem::CoverIconViewItem(QIconView *parent, const WebImage &image) :
+ QObject(parent), KIconViewItem(parent, parent->lastItem(), image.size()), m_job(0)
+{
+ // Set up the iconViewItem
+
+ QPixmap mainMap;
+ mainMap.resize(80, 80);
+ mainMap.fill();
+ setPixmap(mainMap, true, true);
+
+ // Start downloading the image.
+
+ m_job = KIO::get(image.thumbURL(), false, false);
+ connect(m_job, SIGNAL(result(KIO::Job *)), this, SLOT(imageResult(KIO::Job *)));
+ connect(m_job, SIGNAL(data(KIO::Job *, const QByteArray &)),
+ this, SLOT(imageData(KIO::Job *, const QByteArray &)));
+}
+
+CoverIconViewItem::~CoverIconViewItem()
+{
+ if(m_job) {
+ m_job->kill();
+
+ // Drain results issued by KIO before being deleted,
+ // and before deleting the job.
+ kapp->eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
+
+ delete m_job;
+ }
+}
+
+void CoverIconViewItem::imageData(KIO::Job *, const QByteArray &data)
+{
+ int currentSize = m_buffer.size();
+ m_buffer.resize(currentSize + data.size(), QGArray::SpeedOptim);
+ memcpy(&(m_buffer.data()[currentSize]), data.data(), data.size());
+}
+
+void CoverIconViewItem::imageResult(KIO::Job *job)
+{
+ if(job->error())
+ return;
+
+ QPixmap iconImage(m_buffer);
+ iconImage = QImage(iconImage.convertToImage()).smoothScale(80, 80, QImage::ScaleMin);
+ setPixmap(iconImage, true, true);
+}
+
+#include "webimagefetcherdialog.moc"
diff --git a/juk/webimagefetcherdialog.h b/juk/webimagefetcherdialog.h
new file mode 100644
index 00000000..a4424a2f
--- /dev/null
+++ b/juk/webimagefetcherdialog.h
@@ -0,0 +1,90 @@
+/***************************************************************************
+ copyright : (C) 2004 Nathan Toone
+ email : nathan@toonetown.com
+ copyright : (C) 2007 Michael Pyne
+ email : michael.pyne@kdemail.net
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef WEBIMAGEFETCHERDIALOG_H
+#define WEBIMAGEFETCHERDIALOG_H
+
+#include <kiconview.h>
+#include <kio/job.h>
+
+#include "webimagefetcher.h"
+
+class KURL;
+
+class WebImageFetcherDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ WebImageFetcherDialog(const WebImageList &urlList,
+ const FileHandle &file,
+ QWidget *parent = 0);
+
+ virtual ~WebImageFetcherDialog();
+
+ QPixmap result() const { return m_pixmap; }
+
+ void setLayout();
+ void setImageList(const WebImageList &urlList);
+ void setFile(const FileHandle &file);
+
+signals:
+ void coverSelected();
+ void newSearchRequested();
+
+public slots:
+ int exec();
+ void refreshScreen(WebImageList &list);
+
+protected slots:
+ void slotOk();
+ void slotCancel();
+ void slotUser1();
+ void showCreditURL(const QString &url);
+
+private:
+ QPixmap fetchedImage(uint index) const;
+ QPixmap pixmapFromURL(const KURL &url) const;
+
+ QPixmap m_pixmap;
+ WebImageList m_imageList;
+ KIconView *m_iconWidget;
+ FileHandle m_file;
+};
+
+namespace KIO
+{
+ class TransferJob;
+}
+
+class CoverIconViewItem : public QObject, public KIconViewItem
+{
+ Q_OBJECT
+
+public:
+ CoverIconViewItem(QIconView *parent, const WebImage &image);
+ ~CoverIconViewItem();
+
+private slots:
+ void imageData(KIO::Job *job, const QByteArray &data);
+ void imageResult(KIO::Job* job);
+
+private:
+ QByteArray m_buffer;
+ QGuardedPtr<KIO::TransferJob> m_job;
+};
+
+#endif
diff --git a/kaboodle/AUTHORS b/kaboodle/AUTHORS
new file mode 100644
index 00000000..d9dbb241
--- /dev/null
+++ b/kaboodle/AUTHORS
@@ -0,0 +1,6 @@
+Neil Stevens <multivac@fcmail.com>
+Charles Samuels <charles@kde.org>
+Stefan Schimanski <1Stein@gmx.de>
+Malte Starostik <malte@kde.org>
+Stefan Westerfeld <stefan@space.twc.de>
+Nikolas Zimmermann <wildfox@kde.org>
diff --git a/kaboodle/Makefile.am b/kaboodle/Makefile.am
new file mode 100644
index 00000000..2c7183c1
--- /dev/null
+++ b/kaboodle/Makefile.am
@@ -0,0 +1,45 @@
+INCLUDES = -I$(kde_includes)/kio -I$(kde_includes)/arts $(all_includes)
+
+SUBDIRS = pics actions
+
+kde_module_LTLIBRARIES = libkaboodlepart.la
+
+noinst_LTLIBRARIES = libkaboodle_noinst.la
+libkaboodle_noinst_la_SOURCES = conf.cpp \
+ controls.cpp \
+ engine.cpp \
+ player.cpp \
+ view.cpp \
+ kaboodle_factory.cpp
+
+libs = $(LIB_KPARTS) -lqtmcop -lkmedia2_idl -lsoundserver_idl -lartskde -lkmediaplayer
+
+libkaboodlepart_la_SOURCES = dummy.cpp
+libkaboodlepart_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+libkaboodlepart_la_LIBADD = libkaboodle_noinst.la $(libs)
+
+kaboodle_SOURCES = main.cpp \
+ kaboodleapp.cpp \
+ userinterface.cpp
+
+dummy.cpp:
+ echo > dummy.cpp
+
+CLEANFILES = dummy.cpp
+
+kaboodle_LDADD = libkaboodle_noinst.la $(libs)
+kaboodle_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+bin_PROGRAMS = kaboodle
+
+METASOURCES = AUTO
+
+xdg_apps_DATA = kaboodle.desktop
+
+kde_services_DATA = kaboodle_component.desktop kaboodleengine.desktop
+
+appdata_DATA = kaboodleui.rc kaboodlepartui.rc
+appdatadir = $(kde_datadir)/kaboodle
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp *.h -o $(podir)/kaboodle.pot
diff --git a/kaboodle/actions/Makefile.am b/kaboodle/actions/Makefile.am
new file mode 100644
index 00000000..e96a10cd
--- /dev/null
+++ b/kaboodle/actions/Makefile.am
@@ -0,0 +1,2 @@
+kaboodleiconsdir = $(kde_datadir)/kaboodle/icons
+kaboodleicons_ICON = AUTO
diff --git a/kaboodle/actions/cr16-action-kaboodleloop.png b/kaboodle/actions/cr16-action-kaboodleloop.png
new file mode 100644
index 00000000..86c65667
--- /dev/null
+++ b/kaboodle/actions/cr16-action-kaboodleloop.png
Binary files differ
diff --git a/kaboodle/actions/cr22-action-kaboodleloop.png b/kaboodle/actions/cr22-action-kaboodleloop.png
new file mode 100644
index 00000000..d18f40a5
--- /dev/null
+++ b/kaboodle/actions/cr22-action-kaboodleloop.png
Binary files differ
diff --git a/kaboodle/conf.cpp b/kaboodle/conf.cpp
new file mode 100644
index 00000000..eb5b0d7d
--- /dev/null
+++ b/kaboodle/conf.cpp
@@ -0,0 +1,59 @@
+// conf.cpp
+//
+// Copyright (C) 2001 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#include <kconfig.h>
+#include <klocale.h>
+#include <qcheckbox.h>
+#include <qvbox.h>
+
+#include "conf.h"
+#include "view.h"
+
+Kaboodle::Conf::Conf(QWidget *_parent, const char *_name)
+ : KDialogBase(_parent, _name, true, QString::null, Ok | Cancel)
+{
+ QVBox *box = makeVBoxMainWidget();
+
+ autoPlay = new QCheckBox(i18n("Start playing automatically"), box);
+ quitAfterPlaying = new QCheckBox(i18n("Quit when finished playing"), box);
+
+ KConfig &config = *KGlobal::config();
+ config.setGroup("core");
+ autoPlay->setChecked(config.readBoolEntry("autoPlay", true));
+ quitAfterPlaying->setChecked(config.readBoolEntry("quitAfterPlaying", true));
+}
+
+void Kaboodle::Conf::accept(void)
+{
+ KConfig &config = *KGlobal::config();
+ config.setGroup("core");
+ config.writeEntry("autoPlay", autoPlay->isChecked());
+ config.writeEntry("quitAfterPlaying", quitAfterPlaying->isChecked());
+ config.sync();
+
+ KDialogBase::accept();
+}
+
+#include "conf.moc"
diff --git a/kaboodle/conf.h b/kaboodle/conf.h
new file mode 100644
index 00000000..ecd1d0da
--- /dev/null
+++ b/kaboodle/conf.h
@@ -0,0 +1,52 @@
+// conf.h
+//
+// Copyright (C) 2001 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifndef CONF_H
+#define CONF_H
+
+#include <kdialogbase.h>
+
+class QCheckBox;
+
+namespace Kaboodle
+{
+
+class Conf : public KDialogBase
+{
+Q_OBJECT
+
+public:
+ Conf(QWidget *_parent = 0, const char *_name = 0);
+
+protected:
+ virtual void accept(void);
+
+private:
+ QCheckBox *autoPlay, *quitAfterPlaying;
+};
+
+}
+
+#endif
diff --git a/kaboodle/configure.in.in b/kaboodle/configure.in.in
new file mode 100644
index 00000000..1d032bd4
--- /dev/null
+++ b/kaboodle/configure.in.in
@@ -0,0 +1,4 @@
+if test "x$build_arts" = "xno"; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE kaboodle"
+fi
+
diff --git a/kaboodle/controls.cpp b/kaboodle/controls.cpp
new file mode 100644
index 00000000..4d86e1c7
--- /dev/null
+++ b/kaboodle/controls.cpp
@@ -0,0 +1,138 @@
+/*****************************************************************
+
+Copyright (c) 2000-2001 the noatun authors. See file AUTHORS.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIAB\ILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#include "controls.h"
+
+Kaboodle::L33tSlider::L33tSlider(QWidget * parent, const char * name) :
+ QSlider(parent,name), pressed(false)
+{}
+Kaboodle::L33tSlider::L33tSlider(Orientation o, QWidget * parent, const char * name) :
+ QSlider(o,parent,name), pressed(false)
+{}
+Kaboodle::L33tSlider::L33tSlider(int minValue, int maxValue, int pageStep, int value,
+ Orientation o, QWidget * parent, const char * name) :
+ QSlider(minValue, maxValue, pageStep, value, o, parent,name), pressed(false)
+{}
+
+bool Kaboodle::L33tSlider::currentlyPressed() const
+{
+ return pressed;
+}
+
+void Kaboodle::L33tSlider::setValue(int i)
+{
+ if (!pressed)
+ QSlider::setValue(i);
+}
+
+void Kaboodle::L33tSlider::mousePressEvent(QMouseEvent*e)
+{
+ if (e->button()!=RightButton)
+ {
+ pressed=true;
+ QSlider::mousePressEvent(e);
+ }
+}
+
+void Kaboodle::L33tSlider::mouseReleaseEvent(QMouseEvent*e)
+{
+ pressed=false;
+ QSlider::mouseReleaseEvent(e);
+ emit userChanged(value());
+}
+
+void Kaboodle::L33tSlider::wheelEvent(QWheelEvent *e)
+{
+ QSlider::wheelEvent(e);
+ int newValue = value();
+
+ if(newValue < minValue())
+ newValue = minValue();
+ else if(newValue > maxValue())
+ newValue = maxValue();
+
+ setValue(newValue);
+ emit userChanged(newValue);
+}
+
+Kaboodle::SliderAction::SliderAction(const QString& text, int accel, const QObject *receiver,
+ const char *member, QObject* parent, const char* name )
+ : KAction( text, accel, parent, name )
+{
+ m_receiver = receiver;
+ m_member = member;
+}
+
+int Kaboodle::SliderAction::plug( QWidget *w, int index )
+{
+ if (!w->inherits("KToolBar")) return -1;
+
+ KToolBar *toolBar = (KToolBar *)w;
+ int id = KAction::getToolButtonID();
+
+ //Create it.
+ m_slider=new L33tSlider(0, 1000, 100, 0, Horizontal, toolBar);
+ m_slider->setMinimumWidth(10);
+ toolBar->insertWidget(id, 10, m_slider, index );
+
+
+ addContainer( toolBar, id );
+ connect( toolBar, SIGNAL( destroyed() ), this, SLOT( slotDestroyed() ) );
+ toolBar->setItemAutoSized( id, true );
+
+ if (w->inherits( "KToolBar" ))
+ connect(toolBar, SIGNAL(moved(KToolBar::BarPosition)), this, SLOT(toolbarMoved(KToolBar::BarPosition)));
+
+ emit plugged();
+
+ return containerCount() - 1;
+}
+
+void Kaboodle::SliderAction::toolbarMoved(KToolBar::BarPosition)
+{
+// I wish this worked :)
+return;
+/*
+ if (pos == KToolBar::Left || pos == KToolBar::Right)
+ {
+ m_slider->setOrientation(Vertical);
+ m_slider->setFixedWidth(m_slider->height());
+ }
+ else
+ {
+ m_slider->setOrientation(Horizontal);
+ m_slider->resize(m_slider->height(), m_slider->height());
+ }
+*/
+}
+
+void Kaboodle::SliderAction::unplug( QWidget *w )
+{
+ KToolBar *toolBar = (KToolBar *)w;
+ int idx = findContainer( w );
+
+ toolBar->removeItem( itemId( idx ) );
+ removeContainer( idx );
+}
+
+#include "controls.moc"
diff --git a/kaboodle/controls.h b/kaboodle/controls.h
new file mode 100644
index 00000000..d288d1fc
--- /dev/null
+++ b/kaboodle/controls.h
@@ -0,0 +1,97 @@
+/*****************************************************************
+
+Copyright (c) 2000-2001 the noatun authors. See file AUTHORS.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIAB\ILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#ifndef __CONTROLS_H
+#define __CONTROLS_H
+
+#include <qguardedptr.h>
+
+#include <kaction.h>
+#include <ktoolbar.h>
+#include <qslider.h>
+#include <qstringlist.h>
+
+class QComboBox;
+class QLabel;
+
+namespace Kaboodle
+{
+/**
+ * A slider that can be moved around while being
+ * changed internally
+ **/
+class L33tSlider : public QSlider
+{
+Q_OBJECT
+public:
+ L33tSlider(QWidget * parent, const char * name=0);
+ L33tSlider(Orientation, QWidget * parent, const char * name=0);
+ L33tSlider(int minValue, int maxValue, int pageStep, int value,
+ Orientation, QWidget * parent, const char * name=0);
+
+ bool currentlyPressed() const;
+signals:
+ /**
+ * emmited only when the user changes the value by hand
+ **/
+ void userChanged(int value);
+
+public slots:
+ virtual void setValue(int);
+protected:
+ virtual void mousePressEvent(QMouseEvent*);
+ virtual void mouseReleaseEvent(QMouseEvent*);
+ virtual void wheelEvent(QWheelEvent *e);
+
+private:
+ bool pressed;
+};
+
+/**
+ * A slider for your toolbar
+ **/
+class SliderAction : public KAction
+{
+Q_OBJECT
+public:
+ SliderAction(const QString& text, int accel, const QObject *receiver,
+ const char *member, QObject* parent, const char* name );
+ virtual int plug( QWidget *w, int index = -1 );
+ virtual void unplug( QWidget *w );
+ QSlider* slider() const { return m_slider; }
+
+signals:
+ void plugged();
+
+public slots:
+ void toolbarMoved(KToolBar::BarPosition pos);
+private:
+ QGuardedPtr<QSlider> m_slider;
+ QStringList m_items;
+ const QObject *m_receiver;
+ const char *m_member;
+};
+
+}
+
+#endif
diff --git a/kaboodle/engine.cpp b/kaboodle/engine.cpp
new file mode 100644
index 00000000..20d9d2e4
--- /dev/null
+++ b/kaboodle/engine.cpp
@@ -0,0 +1,207 @@
+/*****************************************************************
+
+Copyright (c) 2000-2001 the noatun authors. See file AUTHORS.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIAB\ILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+extern "C"
+{
+#include <sys/wait.h>
+}
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kmimetype.h>
+#include <kstandarddirs.h>
+#include <kurl.h>
+#include <qtimer.h>
+#include <qfile.h>
+#include <qdir.h>
+
+#include <connect.h>
+#include <dynamicrequest.h>
+#include <flowsystem.h>
+#include <kartsdispatcher.h>
+#include <kartsserver.h>
+#include <kplayobjectfactory.h>
+#include <soundserver.h>
+
+#include "engine.h"
+#include <string.h>
+
+using namespace std;
+
+class Kaboodle::Engine::EnginePrivate
+{
+public:
+ EnginePrivate()
+ : playobj(0)
+ , dispatcher()
+ , server()
+ {
+ }
+
+ ~EnginePrivate()
+ {
+ delete playobj;
+ }
+
+ KDE::PlayObject *playobj;
+ KArtsDispatcher dispatcher;
+ KArtsServer server;
+ KURL file;
+};
+
+Kaboodle::Engine::Engine(QObject *parent)
+ : QObject(parent)
+ , d(new EnginePrivate)
+{
+}
+
+Kaboodle::Engine::~Engine()
+{
+ stop();
+ delete d;
+}
+
+bool Kaboodle::Engine::load(const KURL &file)
+{
+ if(file.path().length())
+ {
+ d->file = file;
+ return reload();
+ }
+ else return false;
+}
+
+bool Kaboodle::Engine::reload(void)
+{
+ // Only You can prevent memory leaks
+ delete d->playobj;
+ d->playobj = 0;
+
+ KDE::PlayObjectFactory factory(d->server.server());
+ d->playobj = factory.createPlayObject(d->file, true);
+
+ needReload = false;
+
+ return !d->playobj->isNull();
+}
+
+void Kaboodle::Engine::play()
+{
+ if(d->playobj)
+ {
+ switch(d->playobj->state())
+ {
+ case Arts::posIdle:
+ if(needReload)
+ reload();
+ d->playobj->play();
+ break;
+ case Arts::posPaused:
+ d->playobj->play();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void Kaboodle::Engine::pause()
+{
+ if(d->playobj && !d->playobj->isNull())
+ d->playobj->pause();
+}
+
+void Kaboodle::Engine::stop()
+{
+ if(d->playobj && !d->playobj->isNull())
+ {
+ d->playobj->halt();
+ needReload = true;
+ }
+}
+
+// pass time in msecs
+void Kaboodle::Engine::seek(unsigned long msec)
+{
+ Arts::poTime t;
+
+ t.ms = (long) msec % 1000;
+ t.seconds = (long) ((long)msec - t.ms) / 1000;
+
+ if(d->playobj && !d->playobj->isNull())
+ d->playobj->seek(t);
+}
+
+// return position in milliseconds
+long Kaboodle::Engine::position()
+{
+ if(!d->playobj || d->playobj->isNull()) return 0;
+
+ Arts::poTime time(d->playobj->currentTime());
+ return (time.ms + (time.seconds*1000));
+}
+
+// return track-length in milliseconds
+unsigned long Kaboodle::Engine::length()
+{
+ if(!d->playobj || d->playobj->isNull()) return 0;
+
+ Arts::poTime time(d->playobj->overallTime());
+ return (time.ms + (time.seconds*1000));
+}
+
+KMediaPlayer::Player::State Kaboodle::Engine::state()
+{
+ if(!d->playobj || d->playobj->isNull()) return KMediaPlayer::Player::Empty;
+
+ switch(d->playobj->state())
+ {
+ case Arts::posIdle:
+ return KMediaPlayer::Player::Stop;
+ break;
+ case Arts::posPlaying:
+ return KMediaPlayer::Player::Play;
+ break;
+ case Arts::posPaused:
+ return KMediaPlayer::Player::Pause;
+ break;
+ default:
+ return KMediaPlayer::Player::Stop;
+ break;
+ }
+}
+
+bool Kaboodle::Engine::seekable(void)
+{
+ if(!d->playobj || d->playobj->isNull()) return false;
+ return d->playobj->capabilities() & Arts::capSeek;
+}
+
+Arts::PlayObject Kaboodle::Engine::playObject() const
+{
+ return d->playobj ? d->playobj->object() : Arts::PlayObject::null();
+}
+
+#include "engine.moc"
diff --git a/kaboodle/engine.h b/kaboodle/engine.h
new file mode 100644
index 00000000..7174af3a
--- /dev/null
+++ b/kaboodle/engine.h
@@ -0,0 +1,100 @@
+/*****************************************************************
+
+Copyright (c) 2000-2001 the noatun authors. See file AUTHORS.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIAB\ILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#ifndef _ENGINE_H
+#define _ENGINE_H
+
+#include <qobject.h>
+#include <kmedia2.h>
+#include <kmediaplayer/player.h>
+#include <kurl.h>
+
+namespace Arts
+{
+class PlayObject;
+class SoundServerV2;
+}
+
+namespace Kaboodle
+{
+/**
+ * Handles all playing, connecting to aRts.
+ * Does almost everything related to multimedia.
+ * Most interfacing should be done with Player
+ **/
+class Engine : public QObject
+{
+Q_OBJECT
+
+public:
+ Engine(QObject *parent=0);
+ ~Engine();
+
+ Arts::PlayObject playObject() const;
+
+public slots:
+ /**
+ * Load a file
+ **/
+ bool load(const KURL &file);
+
+ /**
+ * Pause while playing
+ **/
+ void pause();
+
+ /**
+ * Start
+ **/
+ void play();
+
+ /**
+ * stops, and unloads
+ **/
+ void stop();
+
+ /**
+ * skips to a time
+ **/
+ void seek(unsigned long msec);
+
+public:
+ KMediaPlayer::Player::State state();
+ long position(); // NOT unsigned
+ unsigned long length();
+
+ /**
+ * returns if the current track is seekable
+ */
+ bool seekable(void);
+
+private:
+ bool reload(void);
+ bool needReload;
+
+ class EnginePrivate;
+ EnginePrivate *d;
+};
+
+}
+#endif
diff --git a/kaboodle/kaboodle.desktop b/kaboodle/kaboodle.desktop
new file mode 100644
index 00000000..2bddd990
--- /dev/null
+++ b/kaboodle/kaboodle.desktop
@@ -0,0 +1,83 @@
+[Desktop Entry]
+Name=Kaboodle
+Name[bn]=ক্যাবুডল্
+Name[eo]=Kabudlo
+Name[hi]=के-बूडल
+Name[ne]=काबोल्डल
+Name[pa]=ਕੇਬੋਡਲੀ
+Name[ta]=கபூடல்
+Name[zh_TW]=Kaboodle 媒體播放器
+Exec=kaboodle %i %m -caption "%c" %U
+Icon=kaboodle
+X-KDE-StartupNotify=true
+Type=Application
+# Keep in sync with the audio types in kaboodle_component.desktop
+MimeType=audio/x-mp3;application/ogg;audio/x-mp2;video/mpeg;audio/x-wav;audio/x-mod;video/x-msvideo;video/quicktime;video/x-flic;audio/basic;video/x-ms-asf;
+GenericName=Media Player
+GenericName[af]=Media Speler
+GenericName[ar]=مشغل وسائط
+GenericName[bg]=Мултимедиен плеър
+GenericName[bn]=মিডিয়া প্লেয়ার
+GenericName[br]=Soner liesvedia
+GenericName[ca]=Reproductor multimèdia
+GenericName[cs]=Přehrávač médií
+GenericName[cy]=Chwaraeydd Cyfryngau
+GenericName[da]=Medieafspiller
+GenericName[de]=Multimedia-Wiedergabe
+GenericName[el]=Αναπαραγωγέας μέσων
+GenericName[eo]=Ludilo por sonor-dosieroj
+GenericName[es]=Reproductor multimedia
+GenericName[et]=Multimeedia mängija
+GenericName[eu]=Euskarri erreproduzigailua
+GenericName[fa]=پخش‌کنندۀ رسانه
+GenericName[fi]=Mediasoitin
+GenericName[fr]=Lecteur multimédia
+GenericName[ga]=Seinnteoir Meán
+GenericName[gl]=Reproductor Multimedia
+GenericName[he]=נגן מדיה
+GenericName[hi]=मीडिया प्लेयर
+GenericName[hu]=Médialejátszó
+GenericName[is]=Margmiðlunarforrit
+GenericName[it]=Lettore multimediale
+GenericName[ja]=メディアプレーヤ
+GenericName[kk]=Media ойнатқышы
+GenericName[km]=កម្មវិធី​ចាក់​មេ​ឌៀ
+GenericName[ko]=미디어 재생기
+GenericName[lt]=Media grotuvas
+GenericName[lv]=Mēdiju Atskaņotājs
+GenericName[mk]=Медијaплеер
+GenericName[nb]=Mediaspiller
+GenericName[nds]=Medienafspeler
+GenericName[ne]=मिडिया प्लेयर
+GenericName[nl]=Mediaspeler
+GenericName[nn]=Mediespelar
+GenericName[pa]=ਸੰਗੀਤ ਵਾਜਾ
+GenericName[pl]=Odtwarzacz multimedialny
+GenericName[pt]=Leitor Multimédia
+GenericName[pt_BR]=Reprodutor de Mídia
+GenericName[ro]=Program de redare multimedia
+GenericName[ru]=Медиаплеер
+GenericName[se]=Mediačuojaheaddji
+GenericName[sk]=Prehrávač médií
+GenericName[sl]=Večpredstavnostni predvajalnik
+GenericName[sr]=Медија плејер
+GenericName[sr@Latn]=Medija plejer
+GenericName[sv]=Mediaspelare
+GenericName[ta]=ஊடக இயக்கி
+GenericName[tg]=Бозингари Расона
+GenericName[th]=โปรแกรมเล่นแฟ้มสื่อ
+GenericName[tr]=Medya Yürütücüsü
+GenericName[uk]=Програвач медіа-матеріалів
+GenericName[uz]=Media pleyer
+GenericName[uz@cyrillic]=Медиа плейер
+GenericName[ven]=Tshitambi tsha Media
+GenericName[wa]=Djouweu multimedia
+GenericName[xh]=Umdlali we Midia
+GenericName[zh_CN]=媒体播放器
+GenericName[zh_HK]=媒體播放器
+GenericName[zh_TW]=媒體播放器
+GenericName[zu]=Umdlali Womculo
+Terminal=false
+InitialPreference=6
+X-DCOP-ServiceType=Multi
+Categories=Qt;KDE;AudioVideo;X-KDE-More;
diff --git a/kaboodle/kaboodle_component.desktop b/kaboodle/kaboodle_component.desktop
new file mode 100644
index 00000000..0f363cf6
--- /dev/null
+++ b/kaboodle/kaboodle_component.desktop
@@ -0,0 +1,72 @@
+[Desktop Entry]
+Name=Embedded Media Player
+Name[af]=Ingebedde Media Speler
+Name[ar]=مشغل وسائط متعددة معلّب
+Name[az]=Daxili Medya Çalğıcısı
+Name[bn]=সন্নিবিষ্ট মিডিয়া প্লেয়ার
+Name[br]=Soner liesvedia enframmet
+Name[bs]=Uključeni Media Player
+Name[ca]=Reproductor multimèdia incrustat
+Name[cs]=Vestavěný přehrávač médií
+Name[cy]=Chwaraeydd Cyfryngau Mewnol
+Name[da]=Indlejret medieafspiller
+Name[de]=Eingebettete Multimedia-Wiedergabe
+Name[el]=Ενσωματωμένος αναπαραγωγέας μέσων
+Name[eo]=Implantita Sondosierludilo
+Name[es]=Reproductor de multimedia empotrado
+Name[et]=Põimitav meediafailide mängija
+Name[eu]=Euskarri erreproduzigailu kapsulatua
+Name[fa]=پخش‌کنندۀ رسانۀ نهفته
+Name[fi]=Upotettava mediasoitin
+Name[fr]=Lecteur multimédia incorporé
+Name[ga]=Seinnteoir Meán Leabaithe
+Name[gl]=Reproductor Incrustado Multimedia
+Name[he]=נגן המדיה המוטבע
+Name[hi]=एम्बेडेड मीडिया प्लेयर
+Name[hr]=Ugradivi media player
+Name[hu]=Beágyazott médialejátszó
+Name[is]=Ívafinn Hljóðspilari KDE
+Name[it]=Lettore multimediale integrabile
+Name[ja]=埋め込みメディアプレーヤ
+Name[kk]=Ендірілетін медиа ойнатқышы
+Name[km]=កម្មវិធី​ចាក់​មេឌៀ​បង្កប់
+Name[ko]=포함된 미디어 재생기
+Name[lt]=Įdėtas media grotuvas
+Name[lv]=Iegults Mēdiju Atskaņotājs
+Name[mk]=Вгнезден медијаплеер
+Name[mt]=Plejer tal-Media integrat
+Name[nb]=Innebygget mediaspiller
+Name[nds]=Inbett Medienafspeler
+Name[ne]=सम्मिलित मिडिया प्लेयर
+Name[nl]=Ingebedde mediaspeler
+Name[nn]=Innebygd mediespelar
+Name[pa]=ਸ਼ਾਮਿਲ ਮੀਡਿਆ ਵਾਜਾ
+Name[pl]=Osadzony odtwarzacz mediów
+Name[pt]=Leitor Multimédia Embebido
+Name[pt_BR]=Reprodutor de Mídia Integrado KDE
+Name[ro]=Program de redare multimedia înglobat
+Name[ru]=Встроенный медиаплеер
+Name[se]=Vuojuhuvvon mediečuojaheaddji
+Name[sk]=Vložitelný prehrávač médií
+Name[sl]=Vključeni večpredstavnostni predvajalnik
+Name[sr]=Уграђени медија плејер
+Name[sr@Latn]=Ugrađeni medija plejer
+Name[sv]=Inbäddad mediaspelare
+Name[ta]=உட்பொதிந்த ஊடக இயக்கி
+Name[tg]=Бозингари Расонаи Дарунсохта
+Name[th]=โปรแกรมเล่นแฟ้มสื่อแบบฝังตัว
+Name[tr]=Gömülü Medya Yürütücüsü
+Name[uk]=Вмонтований програвач медіа
+Name[ven]=Tshitambi tsha media tsha Embedded
+Name[xh]=Umdlali ophakathi olungisiweyo
+Name[zh_CN]=嵌入式媒体播放器
+Name[zh_HK]=嵌入式媒體播放器
+Name[zh_TW]=嵌入式媒體播放器
+Name[zu]=Oxubiwe Umdlai Wezezindaba
+X-KDE-Library=libkaboodlepart
+Icon=kaboodle
+# Keep in sync with the audio types in kaboodle.desktop
+MimeType=audio/x-mp3;application/ogg;audio/x-mp2;video/mpeg;audio/x-wav;audio/x-mod;video/x-msvideo;video/quicktime;video/x-flic;audio/basic;video/x-ms-asf
+ServiceTypes=KParts/ReadOnlyPart,Browser/View,KMediaPlayer/Player
+Type=Service
+InitialPreference=8
diff --git a/kaboodle/kaboodle_factory.cpp b/kaboodle/kaboodle_factory.cpp
new file mode 100644
index 00000000..1706f3c3
--- /dev/null
+++ b/kaboodle/kaboodle_factory.cpp
@@ -0,0 +1,83 @@
+/*****************************************************************
+
+Copyright (c) 2000-2001 the noatun authors. See file AUTHORS.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIAB\ILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#include <klocale.h>
+#include "kaboodle_factory.h"
+#include "player.h"
+
+K_EXPORT_COMPONENT_FACTORY( libkaboodlepart, Kaboodle::KaboodleFactory)
+
+KInstance *Kaboodle::KaboodleFactory::s_instance = 0;
+KAboutData *Kaboodle::KaboodleFactory::s_aboutData = 0;
+
+Kaboodle::KaboodleFactory::KaboodleFactory() : KParts::Factory()
+{
+}
+
+Kaboodle::KaboodleFactory::~KaboodleFactory()
+{
+ delete s_aboutData;
+ s_aboutData = 0;
+ delete s_instance;
+ s_instance = 0;
+}
+
+KParts::Part *Kaboodle::KaboodleFactory::createPartObject(QWidget *widgetParent, const char *widgetName, QObject *parent, const char *name, const char *className, const QStringList &)
+{
+ if(className == QString("KMediaPlayer/Engine"))
+ {
+ return new Player(parent, name);
+ }
+ else
+ {
+ return new Player(widgetParent, widgetName, parent, name);
+ }
+}
+
+KInstance *Kaboodle::KaboodleFactory::instance()
+{
+ if(!s_instance)
+ s_instance = new KInstance(aboutData());
+
+ return s_instance;
+}
+
+const KAboutData *Kaboodle::KaboodleFactory::aboutData()
+{
+ if(!s_aboutData)
+ {
+ s_aboutData = new KAboutData("kaboodle", I18N_NOOP("Kaboodle"), "1.7",
+ I18N_NOOP("The Lean KDE Media Player"),
+ KAboutData::License_BSD,
+ "(c) 2001-2003 Kaboodle developers", 0,
+ "http://www.freekde.org/neil/kaboodle/");
+ s_aboutData->addCredit("Carsten Pfeiffer", I18N_NOOP("Maintainer"), "pfeiffer@kde.org");
+ s_aboutData->addAuthor("Neil Stevens", I18N_NOOP("Previous Maintainer"), "neil@qualityassistant.com");
+ s_aboutData->addCredit("Elhay Achiam", I18N_NOOP("Application icon"));
+ s_aboutData->addCredit("Charles Samuels", I18N_NOOP("Original Noatun Developer"), "charles@kde.org");
+ s_aboutData->addCredit("Nikolas Zimmermann", I18N_NOOP("Konqueror Embedding"), "wildfox@kde.org");
+ }
+ return s_aboutData;
+}
+
+#include "kaboodle_factory.moc"
diff --git a/kaboodle/kaboodle_factory.h b/kaboodle/kaboodle_factory.h
new file mode 100644
index 00000000..0c43c7c2
--- /dev/null
+++ b/kaboodle/kaboodle_factory.h
@@ -0,0 +1,55 @@
+/*****************************************************************
+
+Copyright (c) 2000-2001 the noatun authors. See file AUTHORS.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIAB\ILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#ifndef KABOODLE_FACTORY_H
+#define KABOODLE_FACTORY_H
+
+#include <kaboutdata.h>
+#include <kinstance.h>
+#include <kparts/factory.h>
+
+namespace Kaboodle
+{
+class Player;
+
+class KaboodleFactory : public KParts::Factory
+{
+Q_OBJECT
+
+public:
+ KaboodleFactory();
+ virtual ~KaboodleFactory();
+
+ virtual KParts::Part *createPartObject(QWidget *widgetParent, const char *widgetName, QObject *parent = 0, const char *name = 0, const char *classname = "QObject", const QStringList &args = QStringList());
+
+ static const KAboutData *aboutData();
+ static KInstance *instance();
+
+private:
+ static KInstance *s_instance;
+ static KAboutData *s_aboutData;
+};
+
+}
+
+#endif
diff --git a/kaboodle/kaboodleapp.cpp b/kaboodle/kaboodleapp.cpp
new file mode 100644
index 00000000..22de968a
--- /dev/null
+++ b/kaboodle/kaboodleapp.cpp
@@ -0,0 +1,53 @@
+/*****************************************************************
+
+Copyright (c) 2000-2001 the noatun authors. See file AUTHORS.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIAB\ILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#include <kcmdlineargs.h>
+#include <kconfig.h>
+
+#include "kaboodleapp.h"
+#include "userinterface.h"
+
+Kaboodle::KaboodleApp::KaboodleApp()
+ : KApplication()
+{
+ disableSessionManagement();
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ KURL openURL;
+ if(args->count() > 0)
+ {
+ KURL url = args->url(args->count() - 1);
+ if(url.isValid()) openURL = url;
+ }
+
+ ui = new UserInterface(0L, openURL);
+ setMainWidget( ui );
+}
+
+Kaboodle::KaboodleApp::~KaboodleApp()
+{
+ // do not delete ui here, it might be the one closing us via closeEvent
+ // ui is deleted automatically anyway.
+}
+
+#include "kaboodleapp.moc"
diff --git a/kaboodle/kaboodleapp.h b/kaboodle/kaboodleapp.h
new file mode 100644
index 00000000..92073c96
--- /dev/null
+++ b/kaboodle/kaboodleapp.h
@@ -0,0 +1,45 @@
+/*****************************************************************
+
+Copyright (c) 2000-2001 the noatun authors. See file AUTHORS.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIAB\ILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#ifndef KABOODLEAPP_H
+#define KABOODLEAPP_H
+
+#include <kapplication.h>
+
+namespace Kaboodle
+{
+class UserInterface;
+
+class KaboodleApp : public KApplication
+{
+Q_OBJECT
+
+public:
+ KaboodleApp();
+ virtual ~KaboodleApp();
+
+private:
+ UserInterface *ui;
+};
+}
+#endif
diff --git a/kaboodle/kaboodleengine.desktop b/kaboodle/kaboodleengine.desktop
new file mode 100644
index 00000000..853951bf
--- /dev/null
+++ b/kaboodle/kaboodleengine.desktop
@@ -0,0 +1,67 @@
+[Desktop Entry]
+Name=Embedded Media Player Engine
+Name[af]=Ingebedde Media Speler Masjien
+Name[bn]=সন্নিবিষ্ট মিডিয়া প্লেয়ার ইঞ্জিন
+Name[br]=Keflusker enframmet soner liesvedia
+Name[bs]=Uključeni Media Player
+Name[ca]=Motor del reproductor multimèdia incrustat
+Name[cs]=Vestavěný přehrávač médií
+Name[cy]=Peiriant Chwaraeydd Cyfryngau Mewnol
+Name[da]=Indlejret medieafspiller-motor
+Name[de]=Eingebettete Multimedia-Wiedergabe
+Name[el]=Ενσωματωμένη μηχανή αναπαραγωγής μέσων
+Name[eo]=Implantita Sondosierludilo
+Name[es]=Motor reproductor multimedia empotrado
+Name[et]=Põimitav meediafailide mängija
+Name[eu]=Euskarri erreproduzigailuaren motore kapsulatua
+Name[fa]=موتور پخش‌کنندۀ رسانۀ نهفته
+Name[fi]=Upotettava mediasoitinkoneisto
+Name[ga]=Inneall Seinnteora Leabaithe Meán
+Name[gl]=Mecanismo do Reproductor Incrustado Multimedia
+Name[he]=מנוע נגן המדיה המוטבע
+Name[hi]=एम्बेडेड मीडिया प्लेयर इंजिन
+Name[hr]=Ugradiva osnova za Media Player
+Name[hu]=Beágyazott médialejátszó motor
+Name[is]=Ívafinn Hljóðspilari KDE
+Name[it]=Motore del lettore multimediale integrato
+Name[ja]=埋め込みメディアプレーヤエンジン
+Name[kk]=Ендірілетін медиа ойнатқыш тетігі
+Name[km]=ម៉ាស៊ីន​កម្មវិធី​ចាក់​មេឌៀ​បង្កប់
+Name[ko]=포함된 미디어 재생기 엔진
+Name[lt]=Įdėto media grotuvo variklis
+Name[mk]=Вградена машина за медијаплеер
+Name[nb]=Innebygget motor for mediaavspiller
+Name[nds]=Inbett Medienafspeelmaschien
+Name[ne]=सम्मिलित मिडिया प्लेयर इन्जिन
+Name[nl]=Ingebedde mediaspeler
+Name[nn]=Innebygd mediespelemotor
+Name[pa]=ਸ਼ਾਮਿਲ ਮੀਡਿਆ ਵਾਜਾ ਇੰਜਣ
+Name[pl]=Osadzony odtwarzacz mediów
+Name[pt]=Motor do Leitor Multimédia Embebido
+Name[pt_BR]=Mecanismo integrado ao Reprodutor de Mídia
+Name[ro]=Program de redare multimedia înglobat
+Name[ru]=Движок встроенного медиаплеера
+Name[se]=Vuojuhuvvon mediačuojahanmutuvra
+Name[sk]=Vložiteľný prehrávač médií
+Name[sl]=Pogon vključenega večpredstavnostnega predvajalnika
+Name[sr]=Уграђени мотор медија плејера
+Name[sr@Latn]=Ugrađeni motor medija plejera
+Name[sv]=Inbäddad mediaspelarkomponent
+Name[ta]=உட்பொதிந்த மீடியா பிளேயர் பொறி
+Name[tg]=Бозингари Расонаи Дарунсохтаи Муҳаррик
+Name[th]=โปรแกรมประมวลผลสำหรับเล่นแฟ้มสื่อ
+Name[tr]=Gömülü Çoklu Ortam Yürütücüsü Motoru
+Name[uk]=Механізм вмонтованого програвача медіа
+Name[ven]=Tshitambi tsha media tsho dzheniswaho
+Name[xh]=Injini Yomdlali we Media Ebekiweyo
+Name[zh_CN]=嵌入式媒体播放器引擎
+Name[zh_HK]=嵌入式媒體播放器引擎
+Name[zh_TW]=嵌入式媒體播放器引擎
+Name[zu]=Injini Yomdlali Womculom Ohlanganisiwe
+X-KDE-Library=libkaboodlepart
+Icon=kaboodle
+# Keep in sync with the audio types in kaboodle.desktop
+MimeType=audio/x-mp3;application/ogg;audio/x-mp2;video/mpeg;audio/x-wav;audio/x-mod;video/x-msvideo;video/quicktime;video/x-flic;audio/basic;video/x-ms-asf
+ServiceTypes=KMediaPlayer/Engine
+Type=Service
+InitialPreference=8
diff --git a/kaboodle/kaboodlepartui.rc b/kaboodle/kaboodlepartui.rc
new file mode 100644
index 00000000..a5861c23
--- /dev/null
+++ b/kaboodle/kaboodlepartui.rc
@@ -0,0 +1,26 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kaboodle" version="6">
+<ActionProperties>
+ <Action name="play" icon="1rightarrow"/>
+ <Action name="pause" icon="player_pause"/>
+ <Action name="stop" icon="player_stop"/>
+ <Action name="loop" icon="kaboodleloop"/>
+</ActionProperties>
+<Toolbar name="KaboodleToolbar"><text>Kaboodle Toolbar</text>
+ <Action name="play"/>
+ <Action name="pause"/>
+ <Action name="stop"/>
+ <Separator/>
+ <Action name="loop"/>
+</Toolbar>
+<MenuBar>
+<Menu name="edit">
+ <Separator/>
+ <Action name="play"/>
+ <Action name="pause"/>
+ <Action name="stop"/>
+ <Separator/>
+ <Action name="loop"/>
+</Menu>
+</MenuBar>
+</kpartgui>
diff --git a/kaboodle/kaboodleui.rc b/kaboodle/kaboodleui.rc
new file mode 100644
index 00000000..4f46a170
--- /dev/null
+++ b/kaboodle/kaboodleui.rc
@@ -0,0 +1,17 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kaboodle" version="9">
+<MenuBar>
+ <Menu name="edit">
+ <text>&amp;Edit</text>
+ <Action name="properties"/>
+ </Menu>
+</MenuBar>
+<Toolbar name="KaboodleToolbar"><text>Kaboodle Toolbar</text>
+ <Action name="file_open"/>
+ <Action name="options_show_menubar"/>
+ <Separator line="true"/>
+ <Merge/>
+ <Separator line="true"/>
+ <Action name="file_quit"/>
+</Toolbar>
+</kpartgui>
diff --git a/kaboodle/main.cpp b/kaboodle/main.cpp
new file mode 100644
index 00000000..35e858df
--- /dev/null
+++ b/kaboodle/main.cpp
@@ -0,0 +1,59 @@
+/*****************************************************************
+
+Copyright (c) 2000-2001 the noatun authors. See file AUTHORS.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIAB\ILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+#include <qapplication.h>
+
+#include "kaboodle_factory.h"
+#include "kaboodleapp.h"
+
+void noMessageOutput(QtMsgType, const char *)
+{
+}
+
+static KCmdLineOptions options[] =
+{
+ { "+[URL]", I18N_NOOP("URL to open"), 0 },
+#ifndef NDEBUG
+ { "qdebug", I18N_NOOP("Turn on Qt Debug output"), 0 },
+#endif
+ KCmdLineLastOption
+};
+
+int main(int argc, char **argv)
+{
+ KCmdLineArgs::init(argc, argv, Kaboodle::KaboodleFactory::aboutData());
+ KCmdLineArgs::addCmdLineOptions(options);
+
+#ifndef NDEBUG
+ if(!KCmdLineArgs::parsedArgs()->isSet("qdebug"))
+ qInstallMsgHandler(noMessageOutput);
+#endif
+
+ Kaboodle::KaboodleApp::addCmdLineOptions();
+ Kaboodle::KaboodleApp app;
+
+ return app.exec();
+}
diff --git a/kaboodle/pics/Makefile.am b/kaboodle/pics/Makefile.am
new file mode 100644
index 00000000..e5515a85
--- /dev/null
+++ b/kaboodle/pics/Makefile.am
@@ -0,0 +1 @@
+KDE_ICON = AUTO
diff --git a/kaboodle/pics/hi128-app-kaboodle.png b/kaboodle/pics/hi128-app-kaboodle.png
new file mode 100644
index 00000000..3c041bbd
--- /dev/null
+++ b/kaboodle/pics/hi128-app-kaboodle.png
Binary files differ
diff --git a/kaboodle/pics/hi16-app-kaboodle.png b/kaboodle/pics/hi16-app-kaboodle.png
new file mode 100644
index 00000000..71e4dd53
--- /dev/null
+++ b/kaboodle/pics/hi16-app-kaboodle.png
Binary files differ
diff --git a/kaboodle/pics/hi22-app-kaboodle.png b/kaboodle/pics/hi22-app-kaboodle.png
new file mode 100644
index 00000000..838cf9d8
--- /dev/null
+++ b/kaboodle/pics/hi22-app-kaboodle.png
Binary files differ
diff --git a/kaboodle/pics/hi32-app-kaboodle.png b/kaboodle/pics/hi32-app-kaboodle.png
new file mode 100644
index 00000000..87ac58b8
--- /dev/null
+++ b/kaboodle/pics/hi32-app-kaboodle.png
Binary files differ
diff --git a/kaboodle/pics/hi48-app-kaboodle.png b/kaboodle/pics/hi48-app-kaboodle.png
new file mode 100644
index 00000000..509c0221
--- /dev/null
+++ b/kaboodle/pics/hi48-app-kaboodle.png
Binary files differ
diff --git a/kaboodle/pics/hi64-app-kaboodle.png b/kaboodle/pics/hi64-app-kaboodle.png
new file mode 100644
index 00000000..7086d4fd
--- /dev/null
+++ b/kaboodle/pics/hi64-app-kaboodle.png
Binary files differ
diff --git a/kaboodle/player.cpp b/kaboodle/player.cpp
new file mode 100644
index 00000000..2530c0c5
--- /dev/null
+++ b/kaboodle/player.cpp
@@ -0,0 +1,268 @@
+/*****************************************************************
+
+Copyright (c) 2000-2001 the noatun authors. See file AUTHORS.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIAB\ILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kurl.h>
+
+#include "engine.h"
+#include "kaboodleapp.h"
+#include "kaboodle_factory.h"
+#include "player.h"
+#include "view.h"
+
+Kaboodle::Player::Player(QObject *parent, const char *name)
+ : KMediaPlayer::Player(parent, name)
+ , engine(new Engine(this))
+ , widget(0)
+ , uncompleted(true)
+ , embedded(false)
+{
+ setInstance(KaboodleFactory::instance());
+
+ connect(&ticker, SIGNAL(timeout()), SLOT(tickerTimeout()));
+ ticker.start(500);
+ setState(Empty);
+}
+
+Kaboodle::Player::Player(QWidget *widgetParent, const char *widgetName,
+ QObject *parent, const char *name)
+ : KMediaPlayer::Player(widgetParent, widgetName, parent, name)
+ , engine(new Engine(this))
+ , widget(new View(widgetParent, widgetName, this))
+ , uncompleted(true)
+ , embedded(false)
+{
+ setInstance(KaboodleFactory::instance());
+
+ connect(&ticker, SIGNAL(timeout()), SLOT(tickerTimeout()));
+ ticker.start(500);
+ setState(Empty);
+
+ playAction = new KAction(i18n("&Play"), 0, this, SLOT(play()), actionCollection(), "play");
+ pauseAction = new KAction(i18n("&Pause"), 0, this, SLOT(pause()), actionCollection(), "pause");
+ stopAction = new KAction(i18n("&Stop"), 0, this, SLOT(stop()), actionCollection(), "stop");
+ loopAction = new KToggleAction(i18n("&Looping"), 0, this, SLOT(loop()), actionCollection(), "loop");
+ stopAction->setEnabled(false);
+ playAction->setEnabled(false);
+ pauseAction->setEnabled(false);
+ connect(this, SIGNAL(loopingChanged(bool)), loopAction, SLOT(setChecked(bool)));
+
+ KParts::Part::setWidget(widget);
+ setXMLFile("kaboodlepartui.rc");
+
+ extension = new BrowserExtension(this);
+ extension->setURLDropHandlingEnabled(true);
+}
+
+Kaboodle::Player::~Player()
+{
+}
+
+KMediaPlayer::View *Kaboodle::Player::view(void)
+{
+ return widget;
+}
+
+// notice how this is just an expanded stop() ? weird.
+bool Kaboodle::Player::openURL(const KURL &f)
+{
+ if(!current.isEmpty())
+ {
+ uncompleted = false;
+ engine->stop();
+ }
+
+ emit started(0);
+ current = f;
+ if(!engine->load(current))
+ {
+ current = KURL();
+ setState(Empty);
+ emit canceled(i18n("aRts could not load this file."));
+ return false;
+ }
+
+ stopAction->setEnabled(false);
+ playAction->setEnabled(true);
+ pauseAction->setEnabled(false);
+ setState(Empty); // so stateChanged() is emitted and autoPlay works
+ setState(Stop);
+
+ tickerTimeout();
+ return true;
+}
+
+KURL Kaboodle::Player::currentURL(void)
+{
+ return current;
+}
+
+bool Kaboodle::Player::openFile(void)
+{
+ return true;
+}
+
+QString Kaboodle::Player::timeString(unsigned long time)
+{
+ int posSecs = (int)(time / 1000);
+ int posSeconds = posSecs % 60;
+ int posMinutes = (posSecs - posSeconds) / 60;
+
+ QString result;
+ result.sprintf("%.2d:%.2d", posMinutes, posSeconds);
+ return result;
+}
+
+QString Kaboodle::Player::positionString(void)
+{
+ return timeString(engine->position());
+}
+
+QString Kaboodle::Player::lengthString(void)
+{
+ return timeString(engine->length());
+}
+
+void Kaboodle::Player::pause()
+{
+ if(engine->state() == Play)
+ {
+ stopAction->setEnabled(true);
+ playAction->setEnabled(true);
+ pauseAction->setEnabled(false);
+ engine->pause();
+ setState(Pause);
+ }
+}
+
+void Kaboodle::Player::play()
+{
+ stopAction->setEnabled(true);
+ playAction->setEnabled(false);
+ pauseAction->setEnabled(true);
+ engine->play();
+ setState(Play);
+ uncompleted = true;
+}
+
+void Kaboodle::Player::stop(void)
+{
+ engine->stop();
+ uncompleted = false;
+ stopAction->setEnabled(false);
+ playAction->setEnabled(true);
+ pauseAction->setEnabled(false);
+ setState(Stop);
+}
+
+void Kaboodle::Player::loop(void)
+{
+ setLooping(!isLooping());
+}
+
+void Kaboodle::Player::seek(unsigned long msec)
+{
+ if(!current.isEmpty())
+ engine->seek(msec);
+}
+
+bool Kaboodle::Player::isSeekable(void) const
+{
+ return engine->seekable();
+}
+
+unsigned long Kaboodle::Player::position(void) const
+{
+ return engine->position();
+}
+
+bool Kaboodle::Player::hasLength(void) const
+{
+ // TODO: replace this weird assumption with something nice in aRts
+ return engine->seekable();
+}
+
+unsigned long Kaboodle::Player::length(void) const
+{
+ return engine->length();
+}
+
+void Kaboodle::Player::tickerTimeout(void)
+{
+ if(engine->state() == Stop)
+ {
+ if ( uncompleted )
+ {
+ stop();
+ if( isLooping() )
+ {
+ play();
+ }
+ else
+ {
+ uncompleted = false;
+ emit completed();
+ }
+ }
+ if(embedded)
+ {
+ widget->embed(Arts::PlayObject::null());
+ embedded = false;
+ }
+ }
+ else if(engine->state() != Stop && engine->state() != Empty)
+ {
+ if(!embedded)
+ {
+ widget->embed(engine->playObject());
+ embedded = true;
+ }
+
+ emit timeout();
+
+ if(extension)
+ emit setStatusBarText(i18n("Playing %1 - %2")
+ .arg(current.prettyURL())
+ .arg(positionString() + "/" + lengthString()));
+
+ }
+ updateTitle();
+}
+
+void Kaboodle::Player::updateTitle()
+{
+ if(!current.isEmpty() && (lastEmitted != current))
+ {
+ lastEmitted = current;
+ emit setWindowCaption(current.prettyURL());
+ }
+}
+
+Kaboodle::BrowserExtension::BrowserExtension(Player *parent)
+ : KParts::BrowserExtension(parent, "Kaboodle Browser Extension")
+{
+}
+
+#include "player.moc"
diff --git a/kaboodle/player.h b/kaboodle/player.h
new file mode 100644
index 00000000..90ddad49
--- /dev/null
+++ b/kaboodle/player.h
@@ -0,0 +1,116 @@
+/*****************************************************************
+
+Copyright (c) 2000-2001 the noatun authors. See file AUTHORS.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIAB\ILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#ifndef PLAYER_H
+#define PLAYER_H
+
+#include <kmedia2.h>
+#include <kmediaplayer/player.h>
+#include <kparts/browserextension.h>
+#include <kurl.h>
+#include <qobject.h>
+#include <qtimer.h>
+
+class KAction;
+class KToggleAction;
+
+namespace Kaboodle
+{
+class Engine;
+class Player;
+class View;
+
+class BrowserExtension : public KParts::BrowserExtension
+{
+Q_OBJECT
+
+public:
+ BrowserExtension(Player *parent);
+};
+
+class Player : public KMediaPlayer::Player
+{
+Q_OBJECT
+
+public:
+ Player(QWidget *widgetParent, const char *widgetName,
+ QObject *parent, const char *name);
+ Player(QObject *parent, const char *name);
+ virtual ~Player();
+
+ virtual bool openURL(const KURL &);
+ KURL currentURL(void);
+
+ /**
+ * returns a string with the time that can
+ * be used in the UI:
+ * CC:CC/LL:LL (mm:ss)
+ **/
+ static QString timeString(unsigned long);
+ QString lengthString(void);
+ QString positionString(void);
+
+ virtual KMediaPlayer::View *view(void);
+
+public slots:
+ virtual void pause(void);
+ virtual void play(void);
+ virtual void stop(void);
+ void loop(void);
+
+ virtual void seek(unsigned long msec);
+
+public:
+ virtual bool isSeekable(void) const;
+ virtual unsigned long position(void) const;
+ virtual bool hasLength(void) const;
+ virtual unsigned long length(void) const;
+
+signals:
+ void timeout(void);
+
+protected:
+ virtual bool openFile(void);
+
+private slots:
+ void tickerTimeout(void);
+ void updateTitle(void);
+
+private:
+ Engine *engine;
+ View *widget;
+
+ BrowserExtension *extension;
+
+ KAction *playAction, *pauseAction, *stopAction;
+ KToggleAction *loopAction;
+
+ QTimer ticker;
+ KURL current;
+ bool uncompleted;
+
+ KURL lastEmitted;
+ bool embedded;
+};
+}
+#endif
diff --git a/kaboodle/userinterface.cpp b/kaboodle/userinterface.cpp
new file mode 100644
index 00000000..eca95c75
--- /dev/null
+++ b/kaboodle/userinterface.cpp
@@ -0,0 +1,182 @@
+// Copyright (C) 2002 Neil Stevens <neil@qualityassistant.com>
+// Copyright (C) 1999 Charles Samuels <charles@kde.org>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#include <arts/kplayobjectfactory.h>
+#include <kconfig.h>
+#include <kdialog.h>
+#include <kfiledialog.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmenubar.h>
+#include <kpropertiesdialog.h>
+#include <kstatusbar.h>
+#include <kstdaction.h>
+#include <kurldrag.h>
+#include <qdragobject.h>
+#include <qlayout.h>
+#include <qlcdnumber.h>
+#include <qvbox.h>
+#include <kkeydialog.h>
+#include <kvideowidget.h>
+
+#include "conf.h"
+#include "kaboodleapp.h"
+#include "kaboodle_factory.h"
+#include "player.h"
+#include "view.h"
+#include "userinterface.h"
+
+Kaboodle::UserInterface::UserInterface(QWidget *parent, const KURL &initialFile)
+ : KParts::MainWindow(parent)
+{
+ setAcceptDrops(true);
+ setStandardToolBarMenuEnabled(true);
+
+ KStdAction::open(this, SLOT(fileOpen()), actionCollection());
+ KStdAction::quit(kapp, SLOT(quit()), actionCollection());
+ KStdAction::preferences(this, SLOT(playerPreferences()), actionCollection());
+ KStdAction::keyBindings( this, SLOT( slotConfigureKeys() ), actionCollection() );
+
+ menubarAction = KStdAction::showMenubar(this, SLOT(showMenubar()), actionCollection());
+ propertiesAction = new KAction(i18n("Properties"), 0, this, SLOT(properties()), actionCollection(), "properties");
+ propertiesAction->setEnabled(false);
+
+ part = new Player(this, "KaboodlePlayer", this, "KaboodleView");
+ part->view()->setButtons(KMediaPlayer::View::Seeker);
+
+ setCentralWidget(part->view());
+ createGUI(part);
+ delete toolBar("mainToolBar");
+
+ statusBar()->show();
+
+ connect(part, SIGNAL(setWindowCaption(const QString &)), this, SLOT(updateTitle(const QString &)));
+ connect(part->view(), SIGNAL(adaptSize(int, int)), this, SLOT(adaptSize(int, int)));
+
+ setIcon(SmallIcon("kaboodle"));
+
+ resize(320, minimumHeight());
+ applyMainWindowSettings(KGlobal::config());
+ menubarAction->setChecked(!menuBar()->isHidden());
+
+ applySettings();
+
+ if(!initialFile.isEmpty())
+ {
+ part->openURL(initialFile);
+ propertiesAction->setEnabled(true);
+ }
+
+ show();
+}
+
+void Kaboodle::UserInterface::slotConfigureKeys()
+{
+ KKeyDialog dialog(this, 0);
+ dialog.insert(actionCollection(), KaboodleFactory::instance()->aboutData()->programName() );
+ dialog.insert(part->actionCollection(), i18n("Player") );
+ View *view = static_cast<View*>( part->view() );
+ dialog.insert(view->videoWidget()->actionCollection(), i18n("Video"));
+ (void) dialog.configure();
+}
+
+Kaboodle::UserInterface::~UserInterface(void)
+{
+ saveMainWindowSettings(KGlobal::config());
+}
+
+void Kaboodle::UserInterface::fileOpen(void)
+{
+ KURL file(KFileDialog::getOpenURL(QString::null, KDE::PlayObjectFactory::mimeTypes().join(" "), this, i18n("Select File to Play")));
+ if(file.isValid())
+ {
+ part->openURL(file);
+ propertiesAction->setEnabled(true);
+ }
+}
+
+void Kaboodle::UserInterface::dragEnterEvent(QDragEnterEvent *event)
+{
+ // accept uri drops only
+ event->accept(KURLDrag::canDecode(event));
+}
+
+void Kaboodle::UserInterface::dropEvent(QDropEvent *event)
+{
+ KURL::List list;
+ if (KURLDrag::decode(event, list))
+ {
+ if (!list.isEmpty())
+ part->openURL(list.first());
+ }
+}
+
+void Kaboodle::UserInterface::playerPreferences(void)
+{
+ Conf dlg(this);
+ dlg.exec();
+ applySettings();
+}
+
+void Kaboodle::UserInterface::applySettings(void)
+{
+ View *view = static_cast<View *>(part->view());
+ KConfig &config = *KGlobal::config();
+ config.setGroup("core");
+ view->setAutoPlay(config.readBoolEntry("autoPlay", true));
+ view->setQuitAfterPlaying(config.readBoolEntry("quitAfterPlaying", true));
+}
+
+void Kaboodle::UserInterface::showMenubar(void)
+{
+ if(menubarAction->isChecked())
+ menuBar()->show();
+ else
+ menuBar()->hide();
+}
+
+void Kaboodle::UserInterface::updateTitle(const QString &text)
+{
+ setCaption(text);
+ statusBar()->message(text);
+}
+
+void Kaboodle::UserInterface::properties(void)
+{
+ if(!part->currentURL().isEmpty())
+ (void)new KPropertiesDialog(part->currentURL());
+}
+
+void Kaboodle::UserInterface::adaptSize(int newViewWidth, int newViewHeight)
+{
+ if(!newViewWidth) return;
+ View *view = static_cast<View *>(part->view());
+ int extraWidth = width() - view->width();
+ int extraHeight = height() - view->height();
+ resize(newViewWidth + extraWidth, newViewHeight + extraHeight);
+}
+
+#include "userinterface.moc"
+
diff --git a/kaboodle/userinterface.h b/kaboodle/userinterface.h
new file mode 100644
index 00000000..db91ebad
--- /dev/null
+++ b/kaboodle/userinterface.h
@@ -0,0 +1,72 @@
+// Copyright (C) 2000 Neil Stevens <multivac@fcmail.com>
+// Copyright (C) 1999 Charles Samuels <charles@kde.org>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifndef USERINTERFACE_H
+#define USERINTERFACE_H
+
+#include "kaboodleapp.h"
+#include <kaction.h>
+#include <kparts/mainwindow.h>
+#include <kurl.h>
+
+#include "player.h"
+
+namespace Kaboodle
+{
+/**
+ * @short Main window class
+ * @author Neil Stevens <multivac@fcmail.com>
+ * @author Charles Samuels <charles@kde.org>
+ */
+class UserInterface : public KParts::MainWindow
+{
+Q_OBJECT
+public:
+ UserInterface(QWidget *parent, const KURL &initialFile = KURL());
+ virtual ~UserInterface(void);
+
+ void load(const QString& url);
+
+protected:
+ virtual void dragEnterEvent(QDragEnterEvent *);
+ virtual void dropEvent(QDropEvent *);
+
+public slots:
+ void playerPreferences(void);
+
+private slots:
+ void fileOpen(void);
+ void showMenubar(void);
+ void updateTitle(const QString &text);
+ void applySettings(void);
+ void properties(void);
+ void adaptSize(int width, int height);
+ void slotConfigureKeys();
+private:
+ Player *part;
+ KToggleAction *menubarAction;
+ KAction *propertiesAction;
+};
+}
+#endif
diff --git a/kaboodle/view.cpp b/kaboodle/view.cpp
new file mode 100644
index 00000000..3ddf3621
--- /dev/null
+++ b/kaboodle/view.cpp
@@ -0,0 +1,323 @@
+/*****************************************************************
+
+Copyright (c) 2000-2001 the noatun authors. See file AUTHORS.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIAB\ILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kdialog.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kvideowidget.h>
+#include <qdragobject.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qtoolbutton.h>
+#include <qtooltip.h>
+#include <qwidget.h>
+#include <qvbox.h>
+
+#include "view.h"
+#include "player.h"
+
+namespace
+{
+QButton *createButton(const QIconSet &_iconset, const QString &_tip, QObject *_receiver, const char *_slot, QWidget *_parent)
+{
+ QToolButton *button = new QToolButton(_parent);
+ button->setMaximumSize(50, 50);
+ button->setIconSet(_iconset);
+ QToolTip::add(button, _tip);
+ QObject::connect(button, SIGNAL(clicked()), _receiver, _slot);
+ button->show();
+ return button;
+}
+}
+
+Kaboodle::View::View(QWidget *parent, const char *name, Player *p)
+ : KMediaPlayer::View(parent, name)
+ , state((KMediaPlayer::Player::State)p->state())
+ , autoPlay(false)
+ , quitAfterPlaying(false)
+ , player(p)
+ , firstVideo(false)
+ , lastWidth(0)
+{
+ (new QHBoxLayout(this))->setAutoAdd(true);
+ QVBox *box = new QVBox(this);
+ box->setSpacing(KDialog::spacingHint());
+ box->setMargin(0);
+
+ video = new KVideoWidget(player, box);
+ video->actionCollection()->readShortcutSettings();
+ setVideoWidget(video);
+ connect(video, SIGNAL(adaptSize(int, int)), this, SLOT(calculateSize(int, int)));
+ connect(video, SIGNAL(mouseButtonPressed(int, const QPoint&, int)), this, SLOT(slotButtonPressed(int, const QPoint &, int) ) ) ;
+ connect(video, SIGNAL(mouseButtonDoubleClick(const QPoint&, int)), this, SLOT(slotDblClick(const QPoint &, int) ) ) ;
+
+ QWidget *sliderBox = new QWidget(box);
+ sliderBox->setFocusPolicy(QWidget::ClickFocus);
+ sliderBox->setAcceptDrops(true);
+
+ QHBoxLayout *layout = new QHBoxLayout(sliderBox);
+ layout->setSpacing(KDialog::spacingHint());
+ layout->setMargin(0);
+ layout->setAutoAdd(true);
+
+ playButton = createButton(BarIconSet("1rightarrow"), i18n("Play"), player, SLOT(play()), sliderBox);
+ pauseButton = createButton(BarIconSet("player_pause"), i18n("Pause"), player, SLOT(pause()), sliderBox);
+ stopButton = createButton(BarIconSet("player_stop"), i18n("Stop"), player, SLOT(stop()), sliderBox);
+
+ slider = new L33tSlider(0, 1000, 10, 0, L33tSlider::Horizontal, sliderBox);
+ slider->setTickmarks(QSlider::NoMarks);
+ slider->show();
+
+ elapsedLabel = new QLabel(sliderBox);
+ QFont labelFont = elapsedLabel->font();
+ labelFont.setPointSize(24);
+ labelFont.setBold(true);
+ QFontMetrics labelFontMetrics(labelFont);
+ elapsedLabel->setFont(labelFont);
+ elapsedLabel->setAlignment(AlignCenter | AlignVCenter | ExpandTabs);
+ elapsedLabel->setFixedHeight(labelFontMetrics.height());
+ elapsedLabel->setMinimumWidth(labelFontMetrics.width("00:00"));
+
+ connect(player, SIGNAL(stateChanged(int)), this, SLOT(stateChanged(int)));
+ connect(player, SIGNAL(completed()), this, SLOT(playerFinished()));
+ connect(player, SIGNAL(timeout()), this, SLOT(playerTimeout()));
+
+ connect(slider, SIGNAL(userChanged(int)), this, SLOT(skipToWrapper(int)));
+ connect(slider, SIGNAL(sliderMoved(int)), this, SLOT(sliderMoved(int)));
+ slider->setEnabled(false);
+
+ connect(this, SIGNAL(buttonsChanged(int)), this, SLOT(updateButtons(int)));
+ updateButtons(buttons());
+
+ updateLabel("--:--/--:--");
+
+ video->setMinimumHeight(0);
+}
+
+Kaboodle::View::~View(void)
+{
+ embed(Arts::PlayObject::null());
+}
+
+void Kaboodle::View::stateChanged(int s)
+{
+ KMediaPlayer::Player::State oldState = state;
+ state = (KMediaPlayer::Player::State)s;
+
+ switch(state)
+ {
+ case KMediaPlayer::Player::Play:
+ stopButton->setEnabled(true);
+ playButton->setEnabled(false);
+ pauseButton->setEnabled(true);
+ break;
+
+ case KMediaPlayer::Player::Empty:
+ slider->setEnabled(false);
+ slider->setValue(0);
+ updateLabel("--:--");
+ stopButton->setEnabled(false);
+ playButton->setEnabled(false);
+ pauseButton->setEnabled(false);
+ break;
+
+ case KMediaPlayer::Player::Stop:
+ slider->setEnabled(false);
+ slider->setValue(0);
+ updateLabel("00:00");
+ stopButton->setEnabled(false);
+ playButton->setEnabled(true);
+ pauseButton->setEnabled(false);
+
+ // loaded
+ if(oldState == KMediaPlayer::Player::Empty)
+ {
+ firstVideo = true;
+ if(autoPlay) player->play();
+ }
+
+ break;
+
+ case KMediaPlayer::Player::Pause:
+ slider->setEnabled(player->isSeekable());
+ stopButton->setEnabled(true);
+ playButton->setEnabled(true);
+ pauseButton->setEnabled(false);
+ break;
+ }
+}
+
+void Kaboodle::View::playerFinished()
+{
+ if(quitAfterPlaying) kapp->quit();
+}
+
+void Kaboodle::View::playerTimeout()
+{
+ if(player->currentURL().isEmpty())
+ return;
+
+ if(slider->currentlyPressed())
+ return;
+
+ updateTicks();
+
+ if(firstVideo)
+ {
+ if(!lastWidth)
+ {
+ video->setNormalSize();
+ }
+ else
+ {
+ firstVideo = false;
+ lastWidth = 0;
+ }
+ }
+
+ if(player->isSeekable())
+ {
+ slider->setEnabled(true);
+ slider->setValue((int)(player->position() / 1000));
+ }
+
+ updateLabel( player->positionString() );
+}
+
+void Kaboodle::View::updateTicks(void)
+{
+ if(player->hasLength())
+ {
+ int range = (int)(player->length() / 1000);
+ slider->setRange(0, range);
+ }
+ else
+ {
+ slider->setRange(0, 1);
+ }
+}
+
+void Kaboodle::View::sliderMoved(int seconds)
+{
+ if(!player->currentURL().isEmpty())
+ updateLabel(Player::timeString(seconds*1000));
+}
+
+void Kaboodle::View::skipToWrapper(int second)
+{
+ player->seek((unsigned long)(second*1000));
+}
+
+void Kaboodle::View::updateLabel(const QString &text)
+{
+ if(elapsedLabel)
+ elapsedLabel->setText(text.left(5));
+}
+
+void Kaboodle::View::calculateSize(int newWidth, int newHeight)
+{
+ lastWidth = newWidth;
+ int extraWidth = width() - video->width();
+ int extraHeight = height() - video->height();
+ newWidth += extraWidth;
+ newHeight += extraHeight;
+ emit adaptSize(newWidth, newHeight);
+}
+
+bool Kaboodle::View::isAutoPlay()
+{
+ return autoPlay;
+}
+
+void Kaboodle::View::setAutoPlay(bool b)
+{
+ autoPlay = b;
+}
+
+bool Kaboodle::View::isQuitAfterPlaying()
+{
+ return quitAfterPlaying;
+}
+
+void Kaboodle::View::setQuitAfterPlaying(bool b)
+{
+ quitAfterPlaying = b;
+}
+
+void Kaboodle::View::embed(Arts::PlayObject object)
+{
+ video->embed(Arts::DynamicCast(object));
+}
+
+void Kaboodle::View::updateButtons(int b)
+{
+ if(b & Play)
+ playButton->show();
+ else
+ playButton->hide();
+
+ if(b & Pause)
+ pauseButton->show();
+ else
+ pauseButton->hide();
+
+ if(b & Stop)
+ stopButton->show();
+ else
+ stopButton->hide();
+
+ if(b & Seeker)
+ {
+ slider->show();
+ elapsedLabel->show();
+ }
+ else
+ {
+ slider->hide();
+ elapsedLabel->hide();
+ }
+
+}
+
+void Kaboodle::View::slotButtonPressed(int /*type*/, const QPoint &, int /* state */)
+{
+ if((KMediaPlayer::Player::State)player->state() == KMediaPlayer::Player::Pause )
+ player->play();
+ else player->pause();
+}
+
+void Kaboodle::View::slotDblClick( const QPoint &, int /* state */)
+{
+ if ( video->isFullscreen() )
+ video->setWindowed();
+ else video->setFullscreen();
+
+ player->play(); // play() is called because the video is stopped when double-clicking ( slotButtonPressed is called )
+}
+
+#include "view.moc"
diff --git a/kaboodle/view.h b/kaboodle/view.h
new file mode 100644
index 00000000..f21de905
--- /dev/null
+++ b/kaboodle/view.h
@@ -0,0 +1,104 @@
+/*****************************************************************
+
+Copyright (c) 2000-2001 the noatun authors. See file AUTHORS.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIAB\ILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#ifndef KABOODLEVIEW_H
+#define KABOODLEVIEW_H
+
+#include <kmediaplayer/player.h>
+#include <kmediaplayer/view.h>
+#include <kurl.h>
+#include <qevent.h>
+#include <qhbox.h>
+#include <qlayout.h>
+#include <qlcdnumber.h>
+#include <qwidget.h>
+
+#include "controls.h"
+#include "player.h"
+
+class QButton;
+class QLabel;
+class KVideoWidget;
+
+namespace Kaboodle
+{
+class View : public KMediaPlayer::View
+{
+Q_OBJECT
+
+public:
+ View(QWidget *parent, const char *name, Player *player);
+ virtual ~View(void);
+
+ /**
+ * automatically play a file after opening it
+ */
+ bool isAutoPlay(void);
+ void setAutoPlay(bool);
+
+ /**
+ * if we opened a file on the command line, quit after playing it
+ */
+ bool isQuitAfterPlaying(void);
+ void setQuitAfterPlaying(bool);
+
+ void embed(Arts::PlayObject);
+ KVideoWidget *videoWidget() { return video; }
+
+public slots:
+ void stateChanged(int);
+ void updateButtons(int);
+ void playerFinished(void);
+ void playerTimeout(void);
+
+ void sliderMoved(int);
+ void skipToWrapper(int);
+
+signals:
+ void adaptSize(int width, int height);
+
+private slots:
+ void updateLabel(const QString &text);
+ void slotButtonPressed( int, const QPoint &, int state );
+ void slotDblClick( const QPoint &, int state );
+
+ void calculateSize(int width, int height);
+
+private:
+ KMediaPlayer::Player::State state;
+ bool autoPlay, quitAfterPlaying;
+ Player *player;
+
+ void updateTicks(void);
+
+ L33tSlider *slider;
+ QLabel *elapsedLabel;
+ KVideoWidget *video;
+ bool firstVideo;
+
+ int lastWidth;
+
+ QButton *playButton, *pauseButton, *stopButton;
+};
+}
+#endif
diff --git a/kappfinder-data/Makefile.am b/kappfinder-data/Makefile.am
new file mode 100644
index 00000000..6f63cfb2
--- /dev/null
+++ b/kappfinder-data/Makefile.am
@@ -0,0 +1,29 @@
+kappfinder_multimedia_data_DATA = \
+ ams.desktop \
+ amsynth.desktop \
+ ardour.desktop \
+ djplay.desktop \
+ ecamegapedal.desktop \
+ freebirth.desktop \
+ freqtweak.desktop \
+ galan.desktop \
+ hydrogen.desktop \
+ jack-rack.desktop \
+ jamin.desktop \
+ meterbridge.desktop \
+ mixxx.desktop \
+ muse.desktop \
+ qjackctl.desktop \
+ qsynth.desktop \
+ vkeybd.desktop \
+ zynaddsubfx.desktop
+
+kappfinder_multimedia_datadir = $(kde_datadir)/kappfinder/apps/Multimedia
+
+EXTRA_DIST = $(kappfinder_multimedia_data_DATA)
+
+xdg_directory_DATA = kde-multimedia-music.directory
+
+xdg_mergedmenu_DATA = kde-multimedia-music.menu
+xdg_mergedmenudir = $(xdg_menudir)/applications-merged
+
diff --git a/kappfinder-data/ams.desktop b/kappfinder-data/ams.desktop
new file mode 100644
index 00000000..54a28e19
--- /dev/null
+++ b/kappfinder-data/ams.desktop
@@ -0,0 +1,55 @@
+[Desktop Entry]
+Exec=ams
+Icon=
+Name=ams
+Name[sv]=AMS
+GenericName=Alsa Modular Synthesizer
+GenericName[bg]=Модулен синтезатор на ALSA
+GenericName[br]=Kenaozer Alsa gant molladoù
+GenericName[ca]=Sintetitzador modular ALSA
+GenericName[cs]=Modulární syntezátor Alsa
+GenericName[cy]=Syntheseisydd Modiwlaidd Alsa
+GenericName[de]=Modularer Alsa-Synthesizer
+GenericName[el]=Αρθρωτός συνθέτης Alsa
+GenericName[en_GB]=Alsa Modular Synthesiser
+GenericName[eo]=Alsa modulsintezilo
+GenericName[es]=Sintetizador modular de Alsa
+GenericName[et]=Alsa modulaarne süntesaator
+GenericName[eu]=Alsa sistetizatzaile modularra
+GenericName[fa]=ترکیب‌گر پیمانه‌ای Alsa
+GenericName[fi]=Alsan modulaarinen syntetisaattori
+GenericName[fr]=Synthétiseur modulaire Alsa
+GenericName[ga]=Sintéiseoir Modúlach Alsa
+GenericName[gl]=Sintetizador Modular Alsa
+GenericName[he]=הסינטיסייזר המודולרי של Alsa
+GenericName[hu]=Szintetizátor
+GenericName[is]=Alsa eininga-hljóðgerfill
+GenericName[it]=Sintetizzatore modulare di Alsa
+GenericName[ja]=ALSA モジュラーシンセサイザ
+GenericName[kk]=Alsa модульді синтезатор
+GenericName[lt]=Alsa modulinis sintezatorius
+GenericName[mk]=Модуларен синтисајзер на Alsa
+GenericName[nds]=Alsa-Klangteler mit Modulen
+GenericName[ne]=एल्सा मोड्युलर सिन्थेसाइजर
+GenericName[nl]=Alsa modulaire synthesizer
+GenericName[pl]=Modularny Syntezytator Alsa
+GenericName[pt]=Sintetizador Modular do Alsa
+GenericName[pt_BR]=Sintetizador Modular do Alsa
+GenericName[ro]=Sintetizator modular Alsa
+GenericName[ru]=Модульный синтезатор ALSA
+GenericName[sk]=Modulárny syntezátor pre Alsa
+GenericName[sl]=Modularni sintetizator Alsa
+GenericName[sr]=Модуларни Alsa-ин синтетизатор
+GenericName[sr@Latn]=Modularni Alsa-in sintetizator
+GenericName[sv]=Alsa modulär synthesizer
+GenericName[ta]=அல்சா ஏற்ற இறக்க தொகுப்பான்
+GenericName[tg]=Ҳамзамонсози Alsa Modular
+GenericName[th]=ตัวสังเคราะห์เสียงของ Alsa
+GenericName[tr]=Alsa Modüler Sentezleyicisi
+GenericName[uk]=Модульний синтезатор ALSA
+GenericName[zh_CN]=Alsa 模块化波表器
+GenericName[zh_HK]=Alsa 模組式合成器
+GenericName[zh_TW]=Alsa 模組合成器
+StartupNotify=true
+Type=Application
+Categories=Music;AudioVideo;
diff --git a/kappfinder-data/amsynth.desktop b/kappfinder-data/amsynth.desktop
new file mode 100644
index 00000000..fd1c0abf
--- /dev/null
+++ b/kappfinder-data/amsynth.desktop
@@ -0,0 +1,46 @@
+[Desktop Entry]
+Exec=amSynth
+Icon=
+Name=amSynth
+Name[pt_BR]=Sint. do Alsa Mixer
+Name[sv]=Amsynt
+Name[ta]=ஆம்சின்த்
+GenericName=Retro Analog - Modeling Softsynth
+GenericName[bg]=Аналогов синтезатор
+GenericName[cs]=Retro analogový softwarový syntetizátor
+GenericName[cy]=Synth Meddal Modelu Analog Retro
+GenericName[da]=En retro-analog - modelerende softsynth
+GenericName[de]=Ein retro-analoger Software-Synthesizer
+GenericName[el]=Αναλογικό ρετρό - Σύνθεση Softsynth
+GenericName[en_GB]=Retro Analogue - Modelling Softsynth
+GenericName[es]=Sintetizador analógico retro por software
+GenericName[et]=Retro analoog tarkvaraline süntesaator
+GenericName[eu]=Analogiko zaharra - Softsynth modelizazioa
+GenericName[fa]=قیاسی Retro - ترکیب‌دهندۀ نرم‌افزاری طرح‌سازی
+GenericName[fi]=Retro Analog - Mallioppiva pehmeä synteesi
+GenericName[hu]=Egy retró stílusú, analóg-modellű szoftveres szintetizátor
+GenericName[is]=Gamaldags hliðrænn - mjúkur hljóðgervill
+GenericName[it]=Sintetizzatore software analogico
+GenericName[ja]=レトロアナログ - モデリングソフトシンセ
+GenericName[kk]=Ретро аналог - бағдарламалық синтезаторы
+GenericName[nb]=Retro Analog – Modeling Softsynth
+GenericName[nds]=En ooltbacksch, analoog Software-Klangteler
+GenericName[ne]=रेट्रो एनालग - नमूना सफ्टसिन्थ
+GenericName[nl]=Retro Analog - modelleringsoftwaresynthesizer
+GenericName[nn]=Retro Analog – Modeling Softsynth
+GenericName[pl]=Retro Analog - naśladujący Softsynth
+GenericName[pt]=Retro Analógico - Sintetizador por Software
+GenericName[pt_BR]=Retro Analog - sintetização de software de modelagem
+GenericName[ru]=Ретро - программный синтезатор
+GenericName[sk]=Retro-analógované modelovanie Softsynth
+GenericName[sl]=Retro Analog - Modelirni Softsynth
+GenericName[sr]=Ретро аналогно моделирање софтверског синтетизатора
+GenericName[sr@Latn]=Retro analogno modeliranje softverskog sintetizatora
+GenericName[sv]=Programvarusynt med gammaldags analogmodellering
+GenericName[ta]=ரெட்ரோ அனலாக் - மாடலிங் சாஃப்ட்சின்த்
+GenericName[th]=โปรแกรมสังเคราะห์เสียงแบบ Retro Analog - Modeling
+GenericName[uk]=Програмний синтезатор - старі аналогові схеми
+GenericName[zh_CN]=火箭模拟 - 模式软波表
+StartupNotify=true
+Type=Application
+Categories=Music;AudioVideo;
diff --git a/kappfinder-data/ardour.desktop b/kappfinder-data/ardour.desktop
new file mode 100644
index 00000000..3a8c9386
--- /dev/null
+++ b/kappfinder-data/ardour.desktop
@@ -0,0 +1,57 @@
+[Desktop Entry]
+Exec=ardour
+Icon=
+Name=Ardour
+Name[bn]=আর্ডর
+Name[ne]=आर्डउर
+Name[ta]=ஆர்டோர்
+GenericName=Multitrack Audio Studio
+GenericName[bg]=Аудио студио
+GenericName[ca]=Estudi d'àudio multipista
+GenericName[cs]=Multitrack audio studio
+GenericName[cy]=Stiwdio Sain Aml-drac
+GenericName[da]=Multispor lydstudie
+GenericName[de]=Mehrspur-Aufnahme-Studio
+GenericName[el]=Στούντιο ήχου πολλαπλών κομματιών
+GenericName[es]=Estudio de audio multipista
+GenericName[et]=Mitmerealine audiostuudio
+GenericName[eu]=Pista anitzeko audio estudioa
+GenericName[fa]=استودیو صوتی چندشیاری
+GenericName[fi]=Moniräkkinen äänistudio
+GenericName[fr]=Studio audio multipiste
+GenericName[ga]=Stiúideo Fuaime Ilrian
+GenericName[he]=אולפן שמע רב־ערוצי
+GenericName[hu]=többsávos hangstúdió
+GenericName[is]=Multitrack hljóðstúdíó
+GenericName[it]=Studio audio multitraccia
+GenericName[ja]=マルチトラックオーディオスタジオ
+GenericName[kk]=Аудио студиясы
+GenericName[km]=ស្ទូឌីយោ​អូឌីយ៉ូ​ច្រើន​បទ
+GenericName[ko]=다중 트랙 오디오 스튜디오
+GenericName[lt]=Daugelio takelių audio studija
+GenericName[mk]=Повеќеканално аудиостудио
+GenericName[nb]=Flerspors lydstudio
+GenericName[nds]=Mehrspoor-Klangstudio
+GenericName[ne]=बहु ट्रयाक अडियो स्टुडियो
+GenericName[nl]=Multitrack geluidsstudio
+GenericName[nn]=Fleirspors lydstudio
+GenericName[pl]=Wielościeżkowe studio audio
+GenericName[pt]=Estúdio Áudio Multi-faixa
+GenericName[pt_BR]=Estúdio de Áudio Multitrack
+GenericName[ru]=Аудиостудия
+GenericName[sk]=Audio studio pre niekoľko stôp
+GenericName[sl]=Večstezni zvočni studio
+GenericName[sr]=Вишетрачни аудио студио
+GenericName[sr@Latn]=Višetračni audio studio
+GenericName[sv]=Flerspårs ljudstudio
+GenericName[ta]=பலவிதபாதையடைய கேட்பொலி ஸ்டுடியோ
+GenericName[tg]=Студияи Садоии Бисёршиора
+GenericName[th]=โปรแกรมสตูดิโอแก้ไขเสียงแบบหลายแทร็ค
+GenericName[tr]=Multitrack Ses Stüdyosu
+GenericName[uk]=Аудіостудія з багатьма доріжками
+GenericName[zh_CN]=多音轨音频工作室
+GenericName[zh_HK]=多音軌音訊製作程式
+GenericName[zh_TW]=多音軌音效工作室
+StartupNotify=true
+Type=Application
+Categories=Music;AudioVideo;
diff --git a/kappfinder-data/djplay.desktop b/kappfinder-data/djplay.desktop
new file mode 100644
index 00000000..3a3620f5
--- /dev/null
+++ b/kappfinder-data/djplay.desktop
@@ -0,0 +1,62 @@
+[Desktop Entry]
+Exec=djplay
+Icon=
+Name=DJPlay
+Name[bn]=ডি-জে-প্লে
+Name[pt_BR]=DjPlay
+Name[sv]=DJplay
+Name[ta]=டிஜேதொடங்கு
+GenericName=DJ-Mixer and Player
+GenericName[bg]=Диско миксер и плеър
+GenericName[br]=Ur mesker DJ ha soner
+GenericName[bs]=DJ-Mixer i Player
+GenericName[ca]=DJ-Mesclador i reproductor
+GenericName[cs]=DJ směšovač a přehrávač
+GenericName[cy]=Chwaraeydd a Chymysgydd DJ
+GenericName[da]=DJ-Mikser og afspiller
+GenericName[de]=DJ-Mixer und Abspieler
+GenericName[el]=Μείκτης DJ και Αναπαραγωγέας
+GenericName[eo]=DJ-miksilo kaj ludilo
+GenericName[es]=Mezclador y reproductor para DJs
+GenericName[et]=DJ mikser ja mängija
+GenericName[eu]=DJ-nahastaile eta erreproduzigailua
+GenericName[fa]=پخش‌کننده و مخلوط‌کن DJ
+GenericName[fi]=DJ-mikseri ja -soitin
+GenericName[fr]=Mixeur et lecteur DJ
+GenericName[gl]=Mesturador DJ e Reproductor
+GenericName[he]=DJ ונגן מערבל
+GenericName[hu]=DJ-keverő és -lejátszó
+GenericName[is]=Plötusnúðshljóðblandari og spilari
+GenericName[it]=Mixer e Lettore da DJ
+GenericName[ja]=DJ ミキサーとプレーヤ
+GenericName[kk]=DJ-микшер мен ойнатқышы
+GenericName[km]=កម្មវិធី​ចាក់ និង​លាយ​សំឡេង​សម្រាប់​ឌីជេ
+GenericName[ko]=DJ-믹서와 플레이어
+GenericName[lt]=DJ-maišytuvas ir plejeris
+GenericName[mk]=DJ-миксета и изведувач
+GenericName[nb]=DJ-mikser og -spiller
+GenericName[nds]=DJ-Mischer un Afspeler
+GenericName[ne]=DJ-मिक्सर र प्लेयर
+GenericName[nl]=DJ-mixer en speler
+GenericName[nn]=DJ-miksar og -spelar
+GenericName[pa]=DJ-ਮਿਕਸਰ ਤੇ ਪਲੇਅਰ
+GenericName[pl]=DJ-Mikser i odtwarzacz
+GenericName[pt]=Leitor e Mistura para DJs
+GenericName[pt_BR]=Reprodutor e Mixer
+GenericName[ru]=DJ-Mixer и проигрыватель
+GenericName[sk]=DJ-Mixer a prehrávač
+GenericName[sl]=DJ-mešalnik in predvajalnik
+GenericName[sr]=DJ-Mixer и плејер
+GenericName[sr@Latn]=DJ-Mixer i plejer
+GenericName[sv]=DJ-mixer och ljudspelare
+GenericName[ta]=டிஜே-ஒன்றுசேர்த்து இயக்கும்
+GenericName[tg]=DJ-Омехтакунак ва Бозингар
+GenericName[th]=โปรแกรมเล่นและผสมเสียงสำหรับ DJ
+GenericName[tr]=DJ-Karıştırıcı ve Çalıcı
+GenericName[uk]=Мікшер та програвач DJ
+GenericName[zh_CN]=DJ-Mixer 和播放器
+GenericName[zh_HK]=DJ-混音器及播放器
+GenericName[zh_TW]=DJ 混音器與播放器
+StartupNotify=true
+Type=Application
+Categories=Qt;Music;AudioVideo;
diff --git a/kappfinder-data/ecamegapedal.desktop b/kappfinder-data/ecamegapedal.desktop
new file mode 100644
index 00000000..6c349674
--- /dev/null
+++ b/kappfinder-data/ecamegapedal.desktop
@@ -0,0 +1,25 @@
+[Desktop Entry]
+Exec=ecamegapedal
+Icon=
+Name=EcaMegaPedal
+Name[bn]=একা-মেগা-পেডাল
+Name[sv]=Ecamegapedal
+Name[ta]=எகாமெகாபெடல்
+GenericName=Ecasound Effektrack
+GenericName[cs]=Ecasound effektrack
+GenericName[et]=Ecasound efektiräkk
+GenericName[hu]=Ecasound effektező
+GenericName[it]=Effetto rack Ecasound
+GenericName[nb]=Ecasound-effektspor
+GenericName[nds]=Ecasound-Effektboord
+GenericName[ne]=एकासाउन्ड इफेक्ट्रयाक
+GenericName[nn]=Ecasound-effektspor
+GenericName[pt_BR]=Faixas de Efeito do Ecasound
+GenericName[sr]=Ecasound трака са ефектима
+GenericName[sr@Latn]=Ecasound traka sa efektima
+GenericName[sv]=Ecasound-effektspår
+GenericName[ta]=எகாஒலி எபெஸ்பாதை
+GenericName[th]=โปรแกรมใส่เสียงพิเศษ Ecasound
+StartupNotify=true
+Type=Application
+Categories=Music;AudioVideo;
diff --git a/kappfinder-data/freebirth.desktop b/kappfinder-data/freebirth.desktop
new file mode 100644
index 00000000..6eb484a1
--- /dev/null
+++ b/kappfinder-data/freebirth.desktop
@@ -0,0 +1,61 @@
+[Desktop Entry]
+Exec=freebirth
+Icon=
+Name=FreeBirth
+Name[bn]=ফ্রী-বার্থ
+Name[ne]=फ्रिबर्थ
+Name[sv]=Freebirth
+Name[ta]=ஃப்ரீபர்த்
+GenericName=Drum Machine
+GenericName[bg]=Барабан
+GenericName[bs]=Ritam mašina
+GenericName[ca]=Bateria
+GenericName[cs]=Bubny
+GenericName[cy]=Peiriant Drymiau
+GenericName[da]=Trommemaskine
+GenericName[de]=Schlagzeugcomputer
+GenericName[el]=Μηχανή κρουστών
+GenericName[eo]=Tamburmaŝino
+GenericName[es]=Caja de ritmos
+GenericName[et]=Trummimasin
+GenericName[eu]=Tanbor-makina
+GenericName[fa]=ماشین طبله
+GenericName[fi]=Rumpukone
+GenericName[fr]=Boîte à rythmes
+GenericName[ga]=Meaisín Drumadóireachta
+GenericName[gl]=Máquina de Percusión
+GenericName[he]=מכונת תופים
+GenericName[hu]=Dobgép
+GenericName[is]=Trommuheili
+GenericName[ja]=ドラムマシーン
+GenericName[kk]=Дауылпаз машинасы
+GenericName[km]=ម៉ាស៊ីន​ស្គរ
+GenericName[ko]=드럼 머신
+GenericName[lt]=Bugnų mašina
+GenericName[mk]=Ритам-машина
+GenericName[nb]=Trommemaskin
+GenericName[nds]=Trummel-Maschien
+GenericName[ne]=ड्रम मेसिन
+GenericName[nl]=Drummachine
+GenericName[nn]=Trommemaskin
+GenericName[pa]=ਡਰੰਮ ਮਸ਼ੀਨ
+GenericName[pl]=Maszyna perkusyjna
+GenericName[pt]=Máquina de Percussão
+GenericName[pt_BR]=Bateria
+GenericName[ro]=Maşină de tobe
+GenericName[sk]=Nástroj pre bicie
+GenericName[sl]=Ritem mašina
+GenericName[sr]=Бубањ машина
+GenericName[sr@Latn]=Bubanj mašina
+GenericName[sv]=Trummaskin
+GenericName[ta]=டிரம் இயந்திரம்
+GenericName[tg]=Мошинаи Таблак
+GenericName[th]=โปรแกรมสร้างเสียงกลอง
+GenericName[tr]=Davul Makinası
+GenericName[uk]=Барабани
+GenericName[zh_CN]=鼓机
+GenericName[zh_HK]=打鼓機
+GenericName[zh_TW]=打鼓機器
+StartupNotify=true
+Type=Application
+Categories=Music;AudioVideo;
diff --git a/kappfinder-data/freqtweak.desktop b/kappfinder-data/freqtweak.desktop
new file mode 100644
index 00000000..ce75a659
--- /dev/null
+++ b/kappfinder-data/freqtweak.desktop
@@ -0,0 +1,60 @@
+[Desktop Entry]
+Exec=freqtweak
+Icon=
+Name=FreqTweak
+Name[bn]=ফ্রিক-টুইক
+Name[sv]=Freqtweak
+Name[ta]=பர்க்டீவீக்
+GenericName=Effects for Jack
+GenericName[bg]=Визуални ефекти за Jack
+GenericName[bs]=Efekti za Jack
+GenericName[ca]=Efectes del Jack
+GenericName[cs]=Efekty pro Jack
+GenericName[cy]=Effeithiau ar gyfer Jack
+GenericName[da]=Effekter for jack
+GenericName[de]=Effekte für Jack
+GenericName[el]=Εφέ για το Jack
+GenericName[eo]=Efektoj por Jack
+GenericName[es]=Efectos para Jack
+GenericName[et]=Jacki efektid
+GenericName[eu]=Jack-erako efektuak
+GenericName[fa]=تأثیرات برای Jack
+GenericName[fr]=Effects pour Jack
+GenericName[ga]=Maisíochtaí le haghaidh Jack
+GenericName[gl]=Efectos para Jack
+GenericName[he]=אפקטים עבור Jack
+GenericName[hu]=Effektező a JACK-hez
+GenericName[is]=Brellur fyrir Jack
+GenericName[it]=Effetti per Jack
+GenericName[ja]=Jack のエフェクト
+GenericName[kk]=Jack эффекттері
+GenericName[km]=បែបផែន​សម្រាប់ Jack
+GenericName[ko]=Jack 효과
+GenericName[lt]=Kištuko efektai
+GenericName[mk]=Ефекти за Jack
+GenericName[nb]=Effektar for Jack
+GenericName[nds]=Effekten för Jack
+GenericName[ne]=ज्याकका लाग प्रभाव
+GenericName[nl]=Effecten voor Jack
+GenericName[nn]=Effektar for Jack
+GenericName[pa]=ਜੈਕ ਦਾ ਪ੍ਰਭਾਵ
+GenericName[pl]=Efekty dla Jacka
+GenericName[pt]=Efeitos para o Jack
+GenericName[pt_BR]=Efeitos para o Jack
+GenericName[ro]=Efecte pentru Jack
+GenericName[ru]=Эффекты для Jack
+GenericName[sk]=Efekty pre Jack
+GenericName[sl]=Učinki za Jack
+GenericName[sr]=Ефекти за Jack
+GenericName[sr@Latn]=Efekti za Jack
+GenericName[sv]=Effekter för Jack
+GenericName[ta]=ஜாக்குக்கான ஒலி அமைப்புகள
+GenericName[tg]=Натиҷаҳо барои Jack
+GenericName[th]=รูปแบบพิเศษสำหรับ Jack
+GenericName[tr]=Jack için Efektler
+GenericName[uk]=Ефекти для Jack
+GenericName[zh_CN]=Jack 的效果
+GenericName[zh_HK]=用於 Jack 的效果
+StartupNotify=true
+Type=Application
+Categories=Music;AudioVideo;
diff --git a/kappfinder-data/galan.desktop b/kappfinder-data/galan.desktop
new file mode 100644
index 00000000..aa104e7d
--- /dev/null
+++ b/kappfinder-data/galan.desktop
@@ -0,0 +1,57 @@
+[Desktop Entry]
+Exec=galan
+Icon=
+Name=gAlan
+Name[bn]=জি-আলান
+Name[sv]=Galan
+GenericName=Modular Synth
+GenericName[bg]=Модулен синтезатор
+GenericName[bs]=Modularni sintisajzer
+GenericName[ca]=Sintetitzador modular
+GenericName[cs]=Modulární syntéza
+GenericName[cy]=Syntheseisydd Modiwlaidd
+GenericName[de]=Modularer Synthesizer
+GenericName[el]=Αρθρωτός συνθέτης
+GenericName[eo]=Modula Sintezilo
+GenericName[es]=Sintetizador modular
+GenericName[et]=Modulaarne süntesaator
+GenericName[eu]=Sintetizatzaile modularra
+GenericName[fa]=ترکیب‌دهندۀ پیمانه‌ای
+GenericName[fi]=Modulaarinen synteesi
+GenericName[fr]=Synthétiseur modulaire
+GenericName[ga]=Sintéiseoir Modúlach
+GenericName[gl]=Sintetizador Modular
+GenericName[he]=סינטיסייזר מודולרי
+GenericName[hu]=Moduláris szintetizátor
+GenericName[is]=Hljóðgerfilseining
+GenericName[it]=Sintetizzatore modulare
+GenericName[ja]=モジュラーシンセ
+GenericName[kk]=Модульді синтезатор
+GenericName[ko]=모듈러 신디사이저
+GenericName[lt]=Modulinis sintezatorius
+GenericName[mk]=Модуларен синтисајзер
+GenericName[nb]=Modulær synt
+GenericName[nds]=Klangteler mit Modulen
+GenericName[ne]=मोड्युलर सिन्थ
+GenericName[nl]=Modulaire Synthesizer
+GenericName[nn]=Modulær synt
+GenericName[pl]=Syntezator modularny
+GenericName[pt]=Sintetizador Modular
+GenericName[pt_BR]=Sintetizador Modular
+GenericName[ro]=Sintetizator modular
+GenericName[ru]=Синтезатор
+GenericName[sk]=Modulárny syntezátor
+GenericName[sl]=Modularni Synth
+GenericName[sr]=Модуларни синтетизатор
+GenericName[sr@Latn]=Modularni sintetizator
+GenericName[sv]=Modulär synthesizer
+GenericName[ta]= மாடுலர் சிந்த்
+GenericName[tg]=Ҳамзамонсози Модулӣ
+GenericName[th]=โปรแกรมสังเคราะห์เสียง
+GenericName[uk]=Модульний синтезатор
+GenericName[zh_CN]=模块波表
+GenericName[zh_HK]=模組式合成器
+GenericName[zh_TW]=模組合成器
+StartupNotify=true
+Type=Application
+Categories=Music;AudioVideo;
diff --git a/kappfinder-data/hydrogen.desktop b/kappfinder-data/hydrogen.desktop
new file mode 100644
index 00000000..96baff58
--- /dev/null
+++ b/kappfinder-data/hydrogen.desktop
@@ -0,0 +1,65 @@
+[Desktop Entry]
+Exec=hydrogen
+Icon=
+Name=Hydrogen
+Name[bn]=হাইড্রোজেন
+Name[fa]=هیدروژن
+Name[fr]=Hydrogène
+Name[ga]=Hidrigin
+Name[ne]=हाइड्रोजन
+Name[pa]=ਹਾਈਡਰੋਜਨ
+Name[ta]=ஹைட்ரோஜன்
+Name[tg]=Гидроген
+GenericName=Drum Machine
+GenericName[bg]=Барабан
+GenericName[bs]=Ritam mašina
+GenericName[ca]=Bateria
+GenericName[cs]=Bubny
+GenericName[cy]=Peiriant Drymiau
+GenericName[da]=Trommemaskine
+GenericName[de]=Schlagzeugcomputer
+GenericName[el]=Μηχανή κρουστών
+GenericName[eo]=Tamburmaŝino
+GenericName[es]=Caja de ritmos
+GenericName[et]=Trummimasin
+GenericName[eu]=Tanbor-makina
+GenericName[fa]=ماشین طبله
+GenericName[fi]=Rumpukone
+GenericName[fr]=Boîte à rythmes
+GenericName[ga]=Meaisín Drumadóireachta
+GenericName[gl]=Máquina de Percusión
+GenericName[he]=מכונת תופים
+GenericName[hu]=Dobgép
+GenericName[is]=Trommuheili
+GenericName[ja]=ドラムマシーン
+GenericName[kk]=Дауылпаз машинасы
+GenericName[km]=ម៉ាស៊ីន​ស្គរ
+GenericName[ko]=드럼 머신
+GenericName[lt]=Bugnų mašina
+GenericName[mk]=Ритам-машина
+GenericName[nb]=Trommemaskin
+GenericName[nds]=Trummel-Maschien
+GenericName[ne]=ड्रम मेसिन
+GenericName[nl]=Drummachine
+GenericName[nn]=Trommemaskin
+GenericName[pa]=ਡਰੰਮ ਮਸ਼ੀਨ
+GenericName[pl]=Maszyna perkusyjna
+GenericName[pt]=Máquina de Percussão
+GenericName[pt_BR]=Bateria
+GenericName[ro]=Maşină de tobe
+GenericName[sk]=Nástroj pre bicie
+GenericName[sl]=Ritem mašina
+GenericName[sr]=Бубањ машина
+GenericName[sr@Latn]=Bubanj mašina
+GenericName[sv]=Trummaskin
+GenericName[ta]=டிரம் இயந்திரம்
+GenericName[tg]=Мошинаи Таблак
+GenericName[th]=โปรแกรมสร้างเสียงกลอง
+GenericName[tr]=Davul Makinası
+GenericName[uk]=Барабани
+GenericName[zh_CN]=鼓机
+GenericName[zh_HK]=打鼓機
+GenericName[zh_TW]=打鼓機器
+StartupNotify=true
+Type=Application
+Categories=Qt;Music;AudioVideo;
diff --git a/kappfinder-data/jack-rack.desktop b/kappfinder-data/jack-rack.desktop
new file mode 100644
index 00000000..6135e47f
--- /dev/null
+++ b/kappfinder-data/jack-rack.desktop
@@ -0,0 +1,39 @@
+[Desktop Entry]
+Exec=jack-rack
+Icon=
+Name=Jack-Rack
+Name[bn]=জ্যাক-র‍্যাক
+Name[fa]=جک-رک
+Name[ne]=ज्याक र्याक
+Name[pa]=ਜੈਕ-ਰੈਕ
+Name[sv]=Jack-rack
+Name[ta]=ஜாக்-ராக்
+GenericName=Jack Effectrack
+GenericName[bg]=Визуални ефекти
+GenericName[bs]=Jack rack sa efektima
+GenericName[cs]=Jack effectrack
+GenericName[de]=Jack Effektrack
+GenericName[es]=Efectos Jack
+GenericName[et]=Jacki efektiräkk
+GenericName[hu]=JACK-effektező
+GenericName[it]=Effetto rack di Jack
+GenericName[ja]=Jack のエフェクトトラック
+GenericName[ko]=Jack 효과 랙
+GenericName[nb]=Jack-effektar
+GenericName[nds]=Jack-Effektboord
+GenericName[ne]=ज्याक इफेक्ट्रयाक
+GenericName[nl]=Jack effectenrack
+GenericName[nn]=Jack-effektar
+GenericName[pl]=Zestaw efektów Jacka
+GenericName[pt]=Efeitos Jack
+GenericName[pt_BR]=Faixas de Efeito do Jack
+GenericName[sl]=Večpredstavnostni predvajalnik
+GenericName[sr]=Jack-ова трака са ефектима
+GenericName[sr@Latn]=Jack-ova traka sa efektima
+GenericName[sv]=Effektbord för Jack
+GenericName[ta]=ஜாக் ஒலி அமைப்பு பாதை
+GenericName[th]=โปรแกรมใส่เสียงพิเศษสำหรับ Jack
+GenericName[uk]=Доріжка ефектів Jack
+StartupNotify=true
+Type=Application
+Categories=Music;AudioVideo;
diff --git a/kappfinder-data/jamin.desktop b/kappfinder-data/jamin.desktop
new file mode 100644
index 00000000..43d8fb6c
--- /dev/null
+++ b/kappfinder-data/jamin.desktop
@@ -0,0 +1,57 @@
+[Desktop Entry]
+Exec=jamin
+Icon=
+Name=Jamin
+Name[bn]=জ্যামিন
+Name[fa]=جامین
+Name[ne]=जामिन
+Name[ta]=ஜாமின்
+GenericName=Jack Mastering Tool
+GenericName[bg]=Управление на Jack
+GenericName[bs]=Jack mastering alat
+GenericName[ca]=Eina de codificació Jack
+GenericName[cs]=Jack mastering
+GenericName[cy]=Erfyn meistroli Jack
+GenericName[da]=Jack master-værktøj
+GenericName[de]=Werkzeug zur Audio-Endbearbeitung
+GenericName[el]=Εργαλείο εγγραφής Jack
+GenericName[es]=Herramienta de masterización de JACK
+GenericName[et]=Jacki masterdamise rakendus
+GenericName[eu]=Jack erabiltzeko tresna
+GenericName[fa]=ابزار اصلی جک
+GenericName[fi]=Jack-masterointityökalu
+GenericName[fr]=Outil de mastering Jack
+GenericName[ga]=Uirlis Mháistirchóipeála Jack
+GenericName[gl]=Ferramenta de Mastering de Jack
+GenericName[hu]=JACK-kezelő
+GenericName[is]=Hljóðblöndunartól Jack
+GenericName[it]=Strumento di masterizzazione Jack
+GenericName[ja]=Jack マスタリングツール
+GenericName[kk]=Jack меңгеру құралы
+GenericName[km]=ឧបករណ៍​ជំនាញ​របស់ Jack
+GenericName[ko]=Jack 마스터링 도구
+GenericName[lt]=Jack valdymo įrankis
+GenericName[mk]=Алатка за мастеринг за Jack
+GenericName[nb]=Jack-opptaksverktøy
+GenericName[nds]=Ennbewerk-Warktüüch för Jack
+GenericName[ne]=ज्याक मास्टरिङ उपकरण
+GenericName[nl]=Jack Mastering-hulpprogramma
+GenericName[nn]=Jack-opptaksverktøy
+GenericName[pa]=ਜੈਕ ਮਾਸਟਿੰਗ ਸੰਦ
+GenericName[pl]=Narzędzie kontroli Jacka
+GenericName[pt]=Ferramenta de Masterização do Jack
+GenericName[pt_BR]=Ferramenta de masterização Jack
+GenericName[ru]=Утилита мастеринга Jack
+GenericName[sk]=Jack Mastering nástroj
+GenericName[sl]=Orodje za Jack Mastering
+GenericName[sr]=Jack-ов алат за мастеровање
+GenericName[sr@Latn]=Jack-ov alat za masterovanje
+GenericName[sv]=Jack-inspelningsverktyg
+GenericName[th]=เครื่องมือสำหรับทำมาสเตอร์สำหรับ Jack
+GenericName[tr]=Jack Yönetim Aracı
+GenericName[uk]=Засіб керування Jack
+GenericName[zh_CN]=Jack 管理工具
+GenericName[zh_HK]=Jack 錄製工具
+StartupNotify=true
+Type=Application
+Categories=Music;AudioVideo;
diff --git a/kappfinder-data/kde-multimedia-music.directory b/kappfinder-data/kde-multimedia-music.directory
new file mode 100644
index 00000000..f93c7f2e
--- /dev/null
+++ b/kappfinder-data/kde-multimedia-music.directory
@@ -0,0 +1,89 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Music
+Name[br]=Sonerezh
+Name[bs]=Muzika
+Name[ca]=Música
+Name[da]=Musik
+Name[el]=Μουσική
+Name[eo]=Muziko
+Name[es]=Musica
+Name[et]=Muusika
+Name[eu]=Musika
+Name[fa]=موسیقی
+Name[fi]=Musiikki
+Name[fr]=Musique
+Name[ga]=Ceol
+Name[gl]=Música
+Name[he]=מוזיקה
+Name[hu]=Zene
+Name[is]=Tónlist
+Name[it]=Musica
+Name[ja]=音楽
+Name[km]=តន្ត្រី
+Name[lt]=Muzika
+Name[nb]=Musikk
+Name[ne]=सङ्गित
+Name[nl]=Muziek
+Name[nn]=Musikk
+Name[pa]=ਸੰਗੀਤ
+Name[pl]=Muzyka
+Name[pt]=Música
+Name[pt_BR]=Música
+Name[sk]=Hudba
+Name[sl]=Glasba
+Name[sr]=Музика
+Name[sr@Latn]=Muzika
+Name[sv]=Musik
+Name[tr]=Müzik
+Name[uk]=Музика
+Name[zh_CN]=音乐
+Name[zh_HK]=音樂
+GenericName=Professional Audio
+GenericName[bg]=Професионално аудио
+GenericName[bs]=Profesionalni audio
+GenericName[ca]=Àudio professional
+GenericName[cs]=Profesionální zvuk
+GenericName[da]=Professionel lyd
+GenericName[el]=Επαγγελματικός ήχος
+GenericName[es]=Audio profesional
+GenericName[et]=Professionaalne audio
+GenericName[eu]=Audio profesionala
+GenericName[fa]=صوتی حرفه‌ای
+GenericName[fi]=Ammattilaisääni
+GenericName[fr]=Audio professionnel
+GenericName[ga]=Fuaim Phroifisiúnta
+GenericName[gl]=Son Profesional
+GenericName[he]=שמע מקצועי
+GenericName[hu]=Professzinonális minőségű hanganyag
+GenericName[is]=Atvinnumannahljóð
+GenericName[it]=Audio professionale
+GenericName[ja]=プロフェッショナルオーディオ
+GenericName[kk]=Кәсіпқой Аудио
+GenericName[km]=អូឌីយ៉ូ​ឯកទេស
+GenericName[ko]=전문가 오디오
+GenericName[lt]=Profesionalus audio
+GenericName[mk]=Професионално аудио
+GenericName[nb]=Profesjonell lyd
+GenericName[nds]=Profeschonell Klang
+GenericName[ne]=पेशागत अडियो
+GenericName[nl]=Professionele audio
+GenericName[nn]=Profesjonell lydhandsaming
+GenericName[pa]=ਵਪਾਰਕ ਆਡੀਓ
+GenericName[pl]=Profesjonalne audio
+GenericName[pt]=Áudio Profissional
+GenericName[pt_BR]=Áudio Profissional
+GenericName[ru]=Профессиональное аудио
+GenericName[sk]=Profesionálne audio
+GenericName[sl]=Profesionalni zvok
+GenericName[sr]=Професионални звук
+GenericName[sr@Latn]=Profesionalni zvuk
+GenericName[sv]=Professionellt ljud
+GenericName[th]=โปรแกรมจัดการเสียงสำหรับมืออาชีพ
+GenericName[tr]=Profesyonel Ses
+GenericName[uk]=Професійне аудіо
+GenericName[zh_CN]=专业音频
+GenericName[zh_HK]=專業音效
+GenericName[zh_TW]=專業音效
+Icon=package
+
diff --git a/kappfinder-data/kde-multimedia-music.menu b/kappfinder-data/kde-multimedia-music.menu
new file mode 100644
index 00000000..e14e47f7
--- /dev/null
+++ b/kappfinder-data/kde-multimedia-music.menu
@@ -0,0 +1,20 @@
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+<Menu>
+ <Name>Multimedia</Name>
+ <Exclude>
+ <Category>Music</Category>
+ </Exclude>
+ <Menu>
+ <Name>Jack and More</Name>
+ <Directory>kde-multimedia-music.directory</Directory>
+ <Include>
+ <And>
+ <Category>Music</Category>
+ </And>
+ </Include>
+ </Menu>
+</Menu>
+</Menu>
diff --git a/kappfinder-data/meterbridge.desktop b/kappfinder-data/meterbridge.desktop
new file mode 100644
index 00000000..6c8d92e6
--- /dev/null
+++ b/kappfinder-data/meterbridge.desktop
@@ -0,0 +1,64 @@
+[Desktop Entry]
+Type=Application
+Exec=meterbridge -t vu in_1 in_2
+Icon=
+Path=
+DocPath=
+Terminal=false
+GenericName=Dual Channel VU-Meter
+GenericName[ca]=Mesurador VU de canal dual
+GenericName[cs]=Dvoukanálový VU Meter
+GenericName[cy]=VU-Meter Sianel Dwbl
+GenericName[de]=Zweikanal VU-Meter
+GenericName[el]=VU-Meter δύο καναλιών
+GenericName[eo]=Dukanala VU-Metro
+GenericName[es]=Contadores VU de dos canales
+GenericName[et]=Kahe kanaliga nivooindikaator
+GenericName[eu]=Kanal bikoitzeko VU-Meter-a
+GenericName[fa]=VU-Meter دو مجرایی
+GenericName[fi]=Kaksikanava VU-mittari
+GenericName[fr]=VU-mètre deux canaux
+GenericName[ga]=Méadar VU le dhá chainéal
+GenericName[gl]=Medidor VU de Canle Dual
+GenericName[he]=מודד VU דו־ערוצי
+GenericName[hu]=Kétcsatornás kijelző
+GenericName[is]=Tvírása VU-mælir
+GenericName[it]=VU-Meter a doppio canale
+GenericName[ja]=デュアルチャンネル VU メーター
+GenericName[kk]=Қос арналы деңгей өлшегіші
+GenericName[km]=ឧបករណ៍​វាស់ VU ឆានែល​ក្បាល​ពីរ
+GenericName[ko]=듀얼 채널 VU-미터
+GenericName[lt]=Dviejų kanalų VU-metras
+GenericName[mk]=Двоканален VU-метар
+GenericName[nb]=Tokanals VU-måler
+GenericName[nds]=Tweekanaal VU-Klock
+GenericName[ne]=दोहोरो च्यानल VU-मिटर
+GenericName[nl]=Tweekanaals VU-meter
+GenericName[nn]=Tokanals VU-målar
+GenericName[pa]=ਦੁਹਰਾ ਚੈਨਲ VU-ਮੀਟਰ
+GenericName[pl]=Dwukanałowy VU-Meter
+GenericName[pt]=Medidor VU de Dois Canais
+GenericName[pt_BR]=Vu-Meter de Canal duplo
+GenericName[ro]=VU-metru pe două canale
+GenericName[ru]=Двухканальный измеритель уровня
+GenericName[sk]=VU-Meter pre dvojitý kanál
+GenericName[sl]=Dvokanalni prikazovalnik glasnosti
+GenericName[sr]=Двоканални VU метар
+GenericName[sr@Latn]=Dvokanalni VU metar
+GenericName[sv]=VU-mätare med två kanaler
+GenericName[ta]=இரட்டை அலை இடை வெளிப்பகுதி VU-அளவுக்கருவி
+GenericName[tg]=Маҷрои Дучанди VU-Meter
+GenericName[th]=VU มิเตอร์แบบช่องคู่
+GenericName[tr]=İki Kanallı Ses Göstergesi
+GenericName[uk]=Двоканальний VU-лічильник
+GenericName[zh_CN]=双声道 VU 衡量器
+GenericName[zh_HK]=雙聲道 VU-Meter
+GenericName[zh_TW]=雙聲道 VU 量尺
+Name=Meterbridge
+Name[bn]=মিটার-ব্রিজ
+Name[ne]=मिटरब्रिज
+Name[nl]=Meterbrug
+Name[ta]=மீட்டர்பிரிட்ஜ்
+X-KDE-StartupNotify=false
+X-DCOP-ServiceType=Multi
+Categories=Music;AudioVideo;
diff --git a/kappfinder-data/mixxx.desktop b/kappfinder-data/mixxx.desktop
new file mode 100644
index 00000000..da305269
--- /dev/null
+++ b/kappfinder-data/mixxx.desktop
@@ -0,0 +1,59 @@
+[Desktop Entry]
+Exec=mixxx
+Icon=
+Name=Mixxx
+Name[ta]=மிக்ஸ்
+GenericName=DJ-Mixer and Player
+GenericName[bg]=Диско миксер и плеър
+GenericName[br]=Ur mesker DJ ha soner
+GenericName[bs]=DJ-Mixer i Player
+GenericName[ca]=DJ-Mesclador i reproductor
+GenericName[cs]=DJ směšovač a přehrávač
+GenericName[cy]=Chwaraeydd a Chymysgydd DJ
+GenericName[da]=DJ-Mikser og afspiller
+GenericName[de]=DJ-Mixer und Abspieler
+GenericName[el]=Μείκτης DJ και Αναπαραγωγέας
+GenericName[eo]=DJ-miksilo kaj ludilo
+GenericName[es]=Mezclador y reproductor para DJs
+GenericName[et]=DJ mikser ja mängija
+GenericName[eu]=DJ-nahastaile eta erreproduzigailua
+GenericName[fa]=پخش‌کننده و مخلوط‌کن DJ
+GenericName[fi]=DJ-mikseri ja -soitin
+GenericName[fr]=Mixeur et lecteur DJ
+GenericName[gl]=Mesturador DJ e Reproductor
+GenericName[he]=DJ ונגן מערבל
+GenericName[hu]=DJ-keverő és -lejátszó
+GenericName[is]=Plötusnúðshljóðblandari og spilari
+GenericName[it]=Mixer e Lettore da DJ
+GenericName[ja]=DJ ミキサーとプレーヤ
+GenericName[kk]=DJ-микшер мен ойнатқышы
+GenericName[km]=កម្មវិធី​ចាក់ និង​លាយ​សំឡេង​សម្រាប់​ឌីជេ
+GenericName[ko]=DJ-믹서와 플레이어
+GenericName[lt]=DJ-maišytuvas ir plejeris
+GenericName[mk]=DJ-миксета и изведувач
+GenericName[nb]=DJ-mikser og -spiller
+GenericName[nds]=DJ-Mischer un Afspeler
+GenericName[ne]=DJ-मिक्सर र प्लेयर
+GenericName[nl]=DJ-mixer en speler
+GenericName[nn]=DJ-miksar og -spelar
+GenericName[pa]=DJ-ਮਿਕਸਰ ਤੇ ਪਲੇਅਰ
+GenericName[pl]=DJ-Mikser i odtwarzacz
+GenericName[pt]=Leitor e Mistura para DJs
+GenericName[pt_BR]=Reprodutor e Mixer
+GenericName[ru]=DJ-Mixer и проигрыватель
+GenericName[sk]=DJ-Mixer a prehrávač
+GenericName[sl]=DJ-mešalnik in predvajalnik
+GenericName[sr]=DJ-Mixer и плејер
+GenericName[sr@Latn]=DJ-Mixer i plejer
+GenericName[sv]=DJ-mixer och ljudspelare
+GenericName[ta]=டிஜே-ஒன்றுசேர்த்து இயக்கும்
+GenericName[tg]=DJ-Омехтакунак ва Бозингар
+GenericName[th]=โปรแกรมเล่นและผสมเสียงสำหรับ DJ
+GenericName[tr]=DJ-Karıştırıcı ve Çalıcı
+GenericName[uk]=Мікшер та програвач DJ
+GenericName[zh_CN]=DJ-Mixer 和播放器
+GenericName[zh_HK]=DJ-混音器及播放器
+GenericName[zh_TW]=DJ 混音器與播放器
+StartupNotify=true
+Type=Application
+Categories=Qt;Music;AudioVideo;
diff --git a/kappfinder-data/muse.desktop b/kappfinder-data/muse.desktop
new file mode 100644
index 00000000..56026985
--- /dev/null
+++ b/kappfinder-data/muse.desktop
@@ -0,0 +1,60 @@
+[Desktop Entry]
+Exec=muse
+Name=MusE
+Name[bn]=মিউস
+Name[lt]=Mures
+Name[sv]=Muse
+Name[ta]=Mபயன்
+Icon=
+GenericName=Music Sequencer
+GenericName[bg]=Музикален синтезатор
+GenericName[ca]=Seqüenciador musical
+GenericName[cs]=Hudební sekvencér
+GenericName[cy]=Dilyniannydd Cerdd
+GenericName[da]=Musik sequencer
+GenericName[de]=Musiksequenzer
+GenericName[el]=Sequencer μουσικής
+GenericName[eo]=Muziksekvencilo
+GenericName[es]=Secuenciador de música
+GenericName[et]=Sekventser
+GenericName[eu]=Musika sekuentziadorea
+GenericName[fa]=دنباله‌رو موسیقی
+GenericName[fi]=Musiikkisekvensseri
+GenericName[fr]=Séquenceur musical
+GenericName[ga]=Seicheamhóir Ceoil
+GenericName[gl]=Secuenciador de Música
+GenericName[he]=מסנטז מוזיקה
+GenericName[hu]=Zeneszekvencer
+GenericName[is]=Tónlistarforrit
+GenericName[it]=Sequencer musicale
+GenericName[ja]=ミュージックシーケンサ
+GenericName[kk]=Музыкалық сиквенсоры
+GenericName[km]=Sequencer តន្ត្រី
+GenericName[ko]=음악 시퀀서
+GenericName[lt]=Muzikinis sinkvensorius
+GenericName[mk]=Музички секвенцер
+GenericName[nb]=Program for musikkomponering
+GenericName[nds]=Musiksequenzer
+GenericName[ne]=सङ्गित अनुक्रमक
+GenericName[nl]=Music sequencer
+GenericName[nn]=Program for musikkomponering
+GenericName[pl]=Sekwenser muzyczny
+GenericName[pt]=Sequenciador de Música
+GenericName[pt_BR]=Seqüenciador de Músicas
+GenericName[ro]=Secvenţiator muzică
+GenericName[ru]=Звуковой сиквенсор
+GenericName[sk]=Sekvencér pre hudbu
+GenericName[sl]=Glasbeni sekvenčnik
+GenericName[sr]=Музички секвенцер
+GenericName[sr@Latn]=Muzički sekvencer
+GenericName[sv]=Musik-sequencer
+GenericName[ta]=இசை வரிசைபடுத்தி
+GenericName[tg]=Тартибгузори Мусиқӣ
+GenericName[th]=โปรแกรมเรียงลำดับโน้ตดนตรี
+GenericName[tr]=Müzik Sıralayıcısı
+GenericName[uk]=Програвач музики
+GenericName[zh_CN]=音乐音序器
+GenericName[zh_HK]=音樂序列器
+GenericName[zh_TW]=音樂序列分析器
+Type=Application
+Categories=Music;AudioVideo;
diff --git a/kappfinder-data/qjackctl.desktop b/kappfinder-data/qjackctl.desktop
new file mode 100644
index 00000000..51e06cf1
--- /dev/null
+++ b/kappfinder-data/qjackctl.desktop
@@ -0,0 +1,60 @@
+[Desktop Entry]
+Exec=qjackctl
+Icon=qjackctl
+Name=QJackCtl
+Name[sv]=Qjackctl
+Name[ta]=வரிசை ஜாக் கட்டுப்பாடு
+GenericName=Control for Jack
+GenericName[bg]=Управление на Jack
+GenericName[br]=Renerezh Jack
+GenericName[bs]=Kontrola za Jack
+GenericName[ca]=Control per al Jack
+GenericName[cs]=Ovládání aplikace Jack
+GenericName[cy]=Rheolydd ar gyfer Jack
+GenericName[da]=Kontrol for Jack
+GenericName[de]=Jack Kontrolle
+GenericName[el]=Έλεγχος για το Jack
+GenericName[es]=Control de Jack
+GenericName[et]=Jacki juhtimine
+GenericName[eu]=Jack-erako kontrola
+GenericName[fa]=کنترل برای جک
+GenericName[fi]=Jackin hallinta
+GenericName[fr]=Contrôle pour Jack
+GenericName[ga]=Rialú Jack
+GenericName[gl]=Control para Jack
+GenericName[he]=בקרה עבור Jack
+GenericName[hu]=Vezérlő a Jackhez
+GenericName[is]=Stillingar fyrir Jack
+GenericName[it]=Controllo per Jack
+GenericName[ja]=Jack のコントロール
+GenericName[kk]=Jack басқаруы
+GenericName[km]=វត្ថុ​បញ្ជា​សម្រាប់ Jack
+GenericName[ko]=Jack 설정
+GenericName[lt]=Jack valdymas
+GenericName[mk]=Контрола за Jack
+GenericName[nb]=Styring av Jack
+GenericName[nds]=Jack-Kuntrull
+GenericName[ne]=ज्याकका लागि नियन्त्रण
+GenericName[nl]=Jack-bediening
+GenericName[nn]=Styring av Jack
+GenericName[pa]=ਜੈਕ ਲਈ ਕੰਟਰੋਲ
+GenericName[pl]=Sterowanie Jackiem
+GenericName[pt]=Controlo do Jack
+GenericName[pt_BR]=Controle para o Jack
+GenericName[ro]=Control pentru Jack
+GenericName[ru]=Управление Jack
+GenericName[sk]=Ovládanie pre Jack
+GenericName[sl]=Nadzor za Jack
+GenericName[sr]=Контрола за Jack
+GenericName[sr@Latn]=Kontrola za Jack
+GenericName[sv]=Styrning av Jack
+GenericName[ta]=ஜாக்குக்கான கட்டுப்பாடு
+GenericName[tg]=Идора барои Jack
+GenericName[th]=โปรแกรมควบคุม Jack
+GenericName[tr]=Jack için Kontrol
+GenericName[uk]=Керування для Jack
+GenericName[zh_CN]=Jack 的控制
+GenericName[zh_HK]=用於 Jack 的控制器
+GenericName[zh_TW]=Jack 控制器
+Type=Application
+Categories=Qt;Music;AudioVideo;
diff --git a/kappfinder-data/qsynth.desktop b/kappfinder-data/qsynth.desktop
new file mode 100644
index 00000000..d00c5ed8
--- /dev/null
+++ b/kappfinder-data/qsynth.desktop
@@ -0,0 +1,62 @@
+[Desktop Entry]
+Exec=qsynth
+Icon=qsynth
+Name=QSynth
+Name[bn]=কিউ-সিন্থ
+Name[sv]=Qsynth
+Name[ta]=வரிசை தொகுப்பு
+Name[zh_HK]=QSynth 合成器
+GenericName=Control for FluidSynth
+GenericName[bg]=Контрол на FluidSynth
+GenericName[br]=Renerezh FluidSynth
+GenericName[bs]=Kontrola za FluidSynth
+GenericName[ca]=Control per al FluidSynth
+GenericName[cs]=Ovládání aplikace FluidSynth
+GenericName[cy]=Rheolydd ar gyfer FluidSynth
+GenericName[da]=Kontrol for FluidSynth
+GenericName[de]=Kontrolloberfläche für FluidSynth
+GenericName[el]=Έλεγχος για το FluidSynth
+GenericName[eo]=Stirilo por FluidSynth
+GenericName[es]=Control de FluidSynth
+GenericName[et]=FluidSynthi juhtimine
+GenericName[eu]=FluidSynth-erako kontrola
+GenericName[fa]=کنترل برای FluidSynth
+GenericName[fi]=FluidSynthin hallinta
+GenericName[fr]=Contrôle pour FluidSynth
+GenericName[ga]=Rialú FluidSynth
+GenericName[gl]=Control de FluidSynth
+GenericName[he]=בקרה עבור FluidSynth
+GenericName[hu]=Vezérlő a FluidSynth-hez
+GenericName[is]=Stillingar fyrir FluidSynth
+GenericName[it]=Controllo per FluidSynth
+GenericName[ja]=FluidSynth のコントロール
+GenericName[kk]=FluidSynth басқаруы
+GenericName[km]=វត្ថុ​បញ្ជា​សម្រាប់ FluidSynth
+GenericName[ko]=FluidSynth 설정
+GenericName[lt]=FluidSynth valdymo priemonė
+GenericName[mk]=Контрола за FluidSynth
+GenericName[nb]=Styring av FluidSynth
+GenericName[nds]=Kuntrull för FluidSynth
+GenericName[ne]=फ्युड सिन्थका लागि नियन्त्रण
+GenericName[nl]=FluidSynth bediening
+GenericName[nn]=Styring av FluidSynth
+GenericName[pl]=Narzędzie kontroli FluidSynth
+GenericName[pt]=Controlo do FluidSynth
+GenericName[pt_BR]=Controle para o FluidSynth
+GenericName[ro]=Control pentru FluidSynth
+GenericName[ru]=Управление FluidSynth
+GenericName[sk]=Ovládanie pre FluidSynth
+GenericName[sl]=Nadzor za FluidSynth
+GenericName[sr]=Контрола за FluidSynth
+GenericName[sr@Latn]=Kontrola za FluidSynth
+GenericName[sv]=Styrning av Fluidsynth
+GenericName[ta]=ப்ளூயிட்சின்த்தின் கட்டுப்பாடு
+GenericName[tg]=Идора барои FluidSynth
+GenericName[th]=ส่วนควบคุมสำหรับ FluidSynth
+GenericName[tr]=FluidSynth için Kontrol
+GenericName[uk]=Керування для FluidSynth
+GenericName[zh_CN]=FluidSynth 的控制
+GenericName[zh_HK]=用於 FluidSynth 的控制器
+GenericName[zh_TW]=FluidSynth 控制器
+Type=Application
+Categories=Qt;Music;AudioVideo;
diff --git a/kappfinder-data/rezound.desktop b/kappfinder-data/rezound.desktop
new file mode 100644
index 00000000..c71aed41
--- /dev/null
+++ b/kappfinder-data/rezound.desktop
@@ -0,0 +1,68 @@
+[Desktop Entry]
+Exec=rezound
+Icon=
+Name=reZound
+Name[sv]=Rezound
+Name[ta]=ரிசவுண்டு
+GenericName=Audio Editor
+GenericName[bg]=Аудио редактор
+GenericName[bn]=অডিও সম্পাদক
+GenericName[br]=Un Aozer Klevet
+GenericName[bs]=Audio editor
+GenericName[ca]=Editor d'àudio
+GenericName[cs]=Zvukový editor
+GenericName[cy]=Golygydd Sain
+GenericName[da]=Lyd-editor
+GenericName[de]=Audio-Editor
+GenericName[el]=Επεξεργαστής ήχου
+GenericName[eo]=Sonredaktilo
+GenericName[es]=Editor de audio
+GenericName[et]=Audioredaktor
+GenericName[eu]=Audio editorea
+GenericName[fa]=ویرایشگر صوتی
+GenericName[fi]=Äänimuokkain
+GenericName[fr]=Éditeur audio
+GenericName[ga]=Eagarthóir Fuaime
+GenericName[gl]=Editor de Son
+GenericName[he]=עורך שמע
+GenericName[hr]=Uređivač zvuka
+GenericName[hu]=Hangszerkesztő
+GenericName[is]=Hljóðritill
+GenericName[it]=Editor audio
+GenericName[ja]=オーディオエディタ
+GenericName[kk]=Аудио өңдегіші
+GenericName[km]=កម្មវិធី​និពន្ធ​អូឌីយ៉ូ
+GenericName[ko]=오디오 편집기
+GenericName[lt]=Audio rengyklė
+GenericName[mk]=Аудио уредувач
+GenericName[nb]=Lydredigerer
+GenericName[nds]=Audio-Editor
+GenericName[ne]=अडियो सम्पादक
+GenericName[nl]=Audio-editor
+GenericName[nn]=Lydhandsamar
+GenericName[pa]=ਧੁਨੀ ਸੰਪਾਦਕ
+GenericName[pl]=Edytor audio
+GenericName[pt]=Editor de Áudio
+GenericName[pt_BR]=Editor de Áudio
+GenericName[ro]=Editor audio
+GenericName[ru]=Звуковой редактор
+GenericName[rw]=Muhinduzi w'Inyumvo
+GenericName[sk]=Audio editor
+GenericName[sl]=Urejevalnik zvoka
+GenericName[sr]=Аудио уређивач
+GenericName[sr@Latn]=Audio uređivač
+GenericName[sv]=Ljudeditor
+GenericName[ta]=கேட்பொலி தொகுப்பாளர்
+GenericName[tg]=Муҳаррири Садо
+GenericName[th]=โปรแกรมแก้ไขเสียง
+GenericName[tr]=Ses Düzenleyicisi
+GenericName[uk]=Аудіо редактор
+GenericName[uz]=Audio tahrirchi
+GenericName[uz@cyrillic]=Аудио таҳрирчи
+GenericName[wa]=Aspougneu d' sons
+GenericName[zh_CN]=音频编辑器
+GenericName[zh_HK]=音效編輯器
+GenericName[zh_TW]=音效編輯器
+StartupNotify=true
+Type=Application
+Categories=AudioVideo;AudioVideo;
diff --git a/kappfinder-data/vkeybd.desktop b/kappfinder-data/vkeybd.desktop
new file mode 100644
index 00000000..b4a18bc9
--- /dev/null
+++ b/kappfinder-data/vkeybd.desktop
@@ -0,0 +1,62 @@
+[Desktop Entry]
+Exec=vkeybd
+Icon=
+Name=vkeybd
+Name[sv]=Vkeybd
+Name[ta]=வி விசைப்பலகை
+GenericName=Virtual MIDI Keyboard
+GenericName[bg]=Виртуална клавиатура за MIDI
+GenericName[br]=Stokellaoueg MIDI galloudel
+GenericName[bs]=Virtualna MIDI klavijatura
+GenericName[ca]=Teclat de MIDI virtual
+GenericName[cs]=Virtuální MIDI klávesnice
+GenericName[cy]=Allweddell Rith MIDI
+GenericName[da]=Virtuelt MIDI keyboard
+GenericName[de]=Virtuelles MIDI-Keyboard
+GenericName[el]=Εικονικό πληκτρολόγιο MIDI
+GenericName[eo]=Virtual MIDI-klavaro
+GenericName[es]=Teclado MIDI virtual
+GenericName[et]=Virtuaalne MIDI klaviatuur
+GenericName[eu]=MIDI teklatu birtuala
+GenericName[fa]=صفحه کلید MIDI مجازی
+GenericName[fi]=Virtuaalinen midikosketinsoitin
+GenericName[fr]=Clavier MIDI virtuel
+GenericName[ga]=Méarchlár Fíorúil MIDI
+GenericName[gl]=Teclado MIDI Virtual
+GenericName[he]=מקלדת MIDI וירטואלית
+GenericName[hu]=Virtuális MIDI-billentyűzet
+GenericName[is]=Sýndar-MIDI-lyklaborð
+GenericName[it]=Tastiera MIDI virtuale
+GenericName[ja]=仮想 MIDI キーボード
+GenericName[kk]=Виртуалды MIDI пернетақтасы
+GenericName[km]=ក្ដារចុច MIDI និម្មិត
+GenericName[ko]=가상 MIDI 키보드
+GenericName[lt]=Virtuali MIDI klaviatūra
+GenericName[mk]=Виртуелна MIDI-клавијатура
+GenericName[nb]=Virtuelt MIDI-tastatur
+GenericName[nds]=Virtuell MIDI-Tastatuur
+GenericName[ne]=अवास्तविक MIDI कुञ्जीपाटी
+GenericName[nl]=Virtueel MIDI klavier
+GenericName[nn]=Virtuelt MIDI-tastatur
+GenericName[pa]=ਵੁਰਚੁਅਲ MIDI ਕੀ-ਬੋਰਡ
+GenericName[pl]=Wirtualna klawiatura MIDI
+GenericName[pt]=Teclado MIDI Virtual
+GenericName[pt_BR]=Teclado MIDI Virtual
+GenericName[ro]=Tastatură MIDI virtuală
+GenericName[ru]=Виртуальная клавиатура MIDI
+GenericName[sk]=Virtuálne klávesy MIDI
+GenericName[sl]=Navidezna klaviatura MIDI
+GenericName[sr]=Виртуелна MIDI клавијатура
+GenericName[sr@Latn]=Virtuelna MIDI klavijatura
+GenericName[sv]=Virtuellt MIDI-keyboard
+GenericName[ta]=மாய மேசை மிடி விசைப்பலகை
+GenericName[tg]=Забонаки Маҷозии MIDI
+GenericName[th]=คีย์บอร์ดมิดีเสมือน
+GenericName[tr]=Görsel Midi Klavyesi
+GenericName[uk]=Віртуальна MIDI клавіатура
+GenericName[zh_CN]=虚拟 MIDI 键盘
+GenericName[zh_HK]=虛擬 MIDI 鍵盤
+GenericName[zh_TW]=虛擬 MIDI 鍵盤
+StartupNotify=false
+Type=Application
+Categories=Music;AudioVideo;
diff --git a/kappfinder-data/zynaddsubfx.desktop b/kappfinder-data/zynaddsubfx.desktop
new file mode 100644
index 00000000..9de9a879
--- /dev/null
+++ b/kappfinder-data/zynaddsubfx.desktop
@@ -0,0 +1,53 @@
+[Desktop Entry]
+Exec=zynaddsubfx
+Icon=
+Name=ZynaddsubFX
+Name[lt]=Cynosure
+Name[sv]=Zynaddsubfx
+Name[ta]=சயண்டன்சப்பெஸ்
+GenericName=Soft Synth
+GenericName[bg]=Софтуерен синтезатор
+GenericName[br]=Kenaozer a-veziant
+GenericName[bs]=Softverski sintisajzer
+GenericName[cs]=Softwarová syntéza
+GenericName[cy]=Synth Meddal
+GenericName[da]=Blød synth
+GenericName[de]=Software-Synthesizer
+GenericName[es]=Sintetizador por software
+GenericName[et]=Tarkvaraline süntesaator
+GenericName[fa]=ترکیب‌دهندۀ نرم‌افزاری
+GenericName[fi]=Pehmeä synteesi
+GenericName[fr]=Synthétiseur logiciel
+GenericName[he]=סינטיסייזר רך
+GenericName[hu]=Szoftveres szintetizátor
+GenericName[is]=Mjúkur hljóðgervill
+GenericName[it]=Sintetizzatore Software
+GenericName[ja]=ソフトシンセ
+GenericName[kk]=Бағдарламалық синтезатор
+GenericName[km]=Synth ស្រទន់
+GenericName[ko]=소프트 신디사이저
+GenericName[lt]=Programinis sintezatorius
+GenericName[mk]=Софтверски синтисајзер
+GenericName[nb]=Programvaresynt
+GenericName[nds]=Software-Klangteler
+GenericName[ne]=सफ्ट सिन्थ
+GenericName[nl]=Software Synthesizer
+GenericName[nn]=Programvaresynt
+GenericName[pt]=Sintetizador por Software
+GenericName[pt_BR]=Sintetizador de software
+GenericName[ro]=Sintetizator software
+GenericName[ru]=Программный синтезатор
+GenericName[sl]=Mehki Synth
+GenericName[sr]=Софтверски синтетизатор
+GenericName[sr@Latn]=Softverski sintetizator
+GenericName[sv]=Programvarusynt
+GenericName[ta]=மென்மையான சிந்த்
+GenericName[th]=โปรแกรมสงเคราะห์เสียง
+GenericName[tr]=Yumuşak Sentez
+GenericName[uk]=Програмний синтезатор
+GenericName[zh_CN]=软波表
+GenericName[zh_HK]=軟件合成器
+GenericName[zh_TW]=軟體合成器
+StartupNotify=false
+Type=Application
+Categories=Music;AudioVideo;
diff --git a/kaudiocreator/Makefile.am b/kaudiocreator/Makefile.am
new file mode 100644
index 00000000..d14c030c
--- /dev/null
+++ b/kaudiocreator/Makefile.am
@@ -0,0 +1,44 @@
+INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/libkcddb \
+ -I$(top_builddir)/libkcddb -I$(top_srcdir)/kscd $(all_includes)
+
+bin_PROGRAMS = kaudiocreator
+
+kaudiocreator_SOURCES = main.cpp kaudiocreator.cpp encoder.cpp \
+ encoderconfigimp.cpp job.cpp jobqueimp.cpp ripper.cpp tracksimp.cpp \
+ encoderedit.ui jobque.ui tracks.ui infodialog.ui wizard.ui general.ui \
+ cdconfig.ui ripconfig.ui encoderoutput.ui encoderconfig.ui encodefile.ui \
+ prefs.kcfgc encoder_prefs.kcfgc encodefileimp.cpp
+
+EXTRA_DIST = encoder.h kaudiocreator.h kaudiocreator.desktop encoderconfigimp.h \
+ obqueimp.h ripper.h tracksimp.h encodefileimp.h
+
+kaudiocreator_LDFLAGS = $(all_libraries) $(KDE_RPATH) -lkutils
+
+METASOURCES = AUTO
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp *.h -o $(podir)/kaudiocreator.pot
+
+kaudiocreator_LDADD = $(LIB_KIO) $(top_builddir)/libkcddb/libkcddb.la \
+ $(top_builddir)/kscd/libkcompactdisc.la
+
+KDE_ICON = AUTO
+
+SUBDIRS = icons
+
+xdg_apps_DATA = kaudiocreator.desktop
+kde_kcfg_DATA = kaudiocreator.kcfg kaudiocreator_encoders.kcfg
+
+appdata_DATA = kaudiocreatorui.rc eventsrc
+appdatadir = $(kde_datadir)/kaudiocreator
+
+servicedata_DATA = audiocd_extract.desktop
+servicedatadir = $(kde_datadir)/konqueror/servicemenus
+
+updatedir = $(kde_datadir)/kconf_update
+update_DATA = kaudiocreator-meta.upd kaudiocreator-libkcddb.upd
+update_SCRIPTS = upgrade-kaudiocreator-metadata.sh
+
+encoderconfig.o: wizard.h
+kaudiocreator.o: ../libkcddb/configbase.h
+tracksimp.o: ../libkcddb/configbase.h ../libkcddb/cdinfodialogbase.h
diff --git a/kaudiocreator/TODO b/kaudiocreator/TODO
new file mode 100644
index 00000000..a53851e9
--- /dev/null
+++ b/kaudiocreator/TODO
@@ -0,0 +1,19 @@
+Change regexp infro to say that it is modifing the track names, not filename.
+
+Add option to disable message box
+
+Rip & Encode one track at a time."?
+Maybe even better is "maximum uncoded wav's" with a number field as grip has
+it (www.nostatic.org/grip).
+
+Good point, however I would prefer the dialog again on a manual query and not after startup or switching a cd.
+
+Add the ability to encode files.
+
+Rip and encode only 1 track at a time. (i.e. using audiocd:/ ioslave).
+Plugin system for encoders (user friendly things, again use audiocd plugin system)
+
+If possible restart jobs that have failed.
+---
+Enhance kdedirs kcontrol config dialog to let users specify global kde temp dir
+ Tmp files are stored in $KDEHOME/tmp-$HOST/appname
diff --git a/kaudiocreator/audiocd_extract.desktop b/kaudiocreator/audiocd_extract.desktop
new file mode 100644
index 00000000..6955ba06
--- /dev/null
+++ b/kaudiocreator/audiocd_extract.desktop
@@ -0,0 +1,54 @@
+[Desktop Entry]
+ServiceTypes=media/audiocd
+Actions=Extract;
+X-KDE-Priority=TopLevel
+
+[Desktop Action Extract]
+Name=Extract and Encode Audio Tracks
+Name[bg]=Извличане и кодиране на аудио диск
+Name[bs]=Ripujte i kodirajte audio CDove
+Name[ca]=Extracció i codificació de pistes d'àudio
+Name[cs]=Získat a enkódovat zvukové stopy
+Name[da]=Udtræk og indkod lydspor
+Name[de]=Musikstücke auslesen und kodieren
+Name[el]=Εξαγωγή και κωδικοποίηση Κομματιών ήχου
+Name[es]=Extrae y codifica pistas de audio
+Name[et]=Ekstrakti ja kodeeri audiorajad
+Name[eu]=Erauzi eta kodetu audio pistak
+Name[fa]=استخراج و کدبندی شیارهای صوتی
+Name[fi]=Siirrä ja koodaa ääniraitoja
+Name[fr]=Extrait et encode des pistes audio
+Name[ga]=Rianta Fuaime á mBaint Amach agus á nIonchódú
+Name[gl]=Extraer e Codificar Pistas de Son
+Name[he]=הוצאה וקידוד רצועות שמע
+Name[hu]=Hangsávok kinyerése és tömörítése
+Name[is]=Afrita og kóða hljóðspor
+Name[it]=Estrai e comprimi le tracce audio
+Name[ja]=オーディオトラックを取り出しエンコード
+Name[kk]=Аудио жолсызықтарды оқып алу мен қалыптастыру
+Name[km]=ស្រង់ចេញ និង​អ៊ិនកូដ​បទ​អូឌីយ៉ូ
+Name[ko]=오디오 트랙 추출 및 인코딩
+Name[lt]=Audio takelių išgavimas ir įkodavimas
+Name[nb]=Pakk ut og dekod lydfiler
+Name[nds]=Musikstücken utlesen un koderen
+Name[ne]=निकाल्ने र सङ्केतन अडियो ट्रयाक
+Name[nl]=Audio-tracks rippen en coderen
+Name[nn]=Hent og kod lydspor
+Name[pa]=ਆਡੀਓ ਟਰੈਕ ਖੋਲੋ ਅਤੇ ਇੰਕੋਡ
+Name[pl]=Odzyskaj i przekoduj ścieżki audio
+Name[pt]=Extracção e Codificação de Faixas de Áudio
+Name[pt_BR]=Extrair e Converter Faixas de Áudio
+Name[ru]=Извлечь и закодировать дорожки
+Name[sk]=Vytiahnuť a zakódovať audio stopy
+Name[sl]=Zajemite in zakodirajte glasbo
+Name[sr]=Издваја и кодира аудио стазе
+Name[sr@Latn]=Izdvaja i kodira audio staze
+Name[sv]=Hämta och koda ljudspår
+Name[th]=โปรแกรมสำหรับดึงข้อมูลและเข้ารหัสเพลง
+Name[tr]=Ses İzlerini Aktar ve Kodla
+Name[uk]=Витягає і кодує звукові доріжки
+Name[zh_CN]=提取并编码音轨
+Name[zh_HK]=擷取並將音軌編碼
+Name[zh_TW]=展開並編碼音軌
+Exec=kaudiocreator %u
+Icon=kaudiocreator
diff --git a/kaudiocreator/cdconfig.ui b/kaudiocreator/cdconfig.ui
new file mode 100644
index 00000000..3c688699
--- /dev/null
+++ b/kaudiocreator/cdconfig.ui
@@ -0,0 +1,61 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>CdConfig</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>CdConfig</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>517</width>
+ <height>148</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_performCDDBauto</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Perform CDDB lookup automatically</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_autoRip</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Automatically rip all tracks upon a successful CDDB retrieval</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<slots>
+ <slot access="protected" specifier="non virtual">configureAudioCD()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kaudiocreator/configure.in.in b/kaudiocreator/configure.in.in
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kaudiocreator/configure.in.in
diff --git a/kaudiocreator/encodefile.ui b/kaudiocreator/encodefile.ui
new file mode 100644
index 00000000..21163f87
--- /dev/null
+++ b/kaudiocreator/encodefile.ui
@@ -0,0 +1,345 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>EncodeFile</class>
+<widget class="KDialog">
+ <property name="name">
+ <cstring>EncodeFile</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>339</width>
+ <height>452</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Encode File</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;File to encode:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>file</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>file</cstring>
+ </property>
+ <property name="showLocalProtocol">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>Track</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QSpinBox" row="3" column="1">
+ <property name="name">
+ <cstring>track</cstring>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Track:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Comment:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="4" column="1">
+ <property name="name">
+ <cstring>track_comment</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Title:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="hAlign" stdset="0">
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Artist:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>track_title</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>track_artist</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Album</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>artist</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>Artist:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="hAlign" stdset="0">
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel7</cstring>
+ </property>
+ <property name="text">
+ <string>Album:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="hAlign" stdset="0">
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>album</cstring>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="3" column="1">
+ <property name="name">
+ <cstring>year</cstring>
+ </property>
+ <property name="maxValue">
+ <number>99999</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel8</cstring>
+ </property>
+ <property name="text">
+ <string>Year:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="hAlign" stdset="0">
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel9</cstring>
+ </property>
+ <property name="text">
+ <string>Genre:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="hAlign" stdset="0">
+ </property>
+ </widget>
+ <widget class="QComboBox" row="4" column="1">
+ <property name="name">
+ <cstring>genre</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>textLabel10</cstring>
+ </property>
+ <property name="text">
+ <string>Comment:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="5" column="1">
+ <property name="name">
+ <cstring>comment</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer8</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="Line">
+ <property name="name">
+ <cstring>line1</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>121</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>encodeButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Encode File</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>closeButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Close</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>closeButton</sender>
+ <signal>clicked()</signal>
+ <receiver>EncodeFile</receiver>
+ <slot>accept()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>file</tabstop>
+ <tabstop>track_artist</tabstop>
+ <tabstop>track_title</tabstop>
+ <tabstop>track</tabstop>
+ <tabstop>track_comment</tabstop>
+ <tabstop>artist</tabstop>
+ <tabstop>album</tabstop>
+ <tabstop>year</tabstop>
+ <tabstop>genre</tabstop>
+ <tabstop>comment</tabstop>
+ <tabstop>encodeButton</tabstop>
+ <tabstop>closeButton</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kdialog.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kaudiocreator/encodefileimp.cpp b/kaudiocreator/encodefileimp.cpp
new file mode 100644
index 00000000..e7345173
--- /dev/null
+++ b/kaudiocreator/encodefileimp.cpp
@@ -0,0 +1,82 @@
+/**
+ * This file is part of the KAudioCreator package
+ * Copyright (C) 2003 Benjamin C Meyer (ben+kaudiocreator at meyerhome dot net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "encodefileimp.h"
+#include "job.h"
+
+#include <qspinbox.h>
+#include <qlineedit.h>
+#include <kurlrequester.h>
+#include <qcombobox.h>
+#include <kmessagebox.h>
+
+EncodeFileImp::EncodeFileImp(QWidget* parent,
+ const char* name) : EncodeFile(parent, name), m_genres(KCDDB::Genres()) {
+ genre->insertStringList(m_genres.i18nList());
+ // Specify to only accept wav files
+ file->setFilter("*.wav|Wav Files");
+
+ connect(file,SIGNAL(textChanged(const QString &)),this,SLOT(enableEncodeButton(const QString &)));
+ connect(encodeButton,SIGNAL(clicked()),this,SLOT(encode()));
+}
+
+/**
+ * When the user presses the encode button create a job with all of the current
+ * selection options and emit a signal with it.
+ */
+void EncodeFileImp::encode(){
+ Job *newJob = new Job();
+
+ newJob->location = file->url();
+
+ newJob->album = album->text();
+ newJob->genre = m_genres.i18n2cddb(genre->currentText());
+ if(newJob->genre.isEmpty())
+ newJob->genre = "Pop";
+ newJob->group = artist->text();
+ newJob->comment = comment->text();
+ newJob->year = year->value();
+ newJob->track = track->value();
+
+ newJob->track_title = track_title->text();
+ if ((track_artist->text()).isEmpty())
+ newJob->track_artist = artist->text();
+ else
+ newJob->track_artist = track_artist->text();
+ newJob->track_comment = track_comment->text();
+
+ newJob->removeTempFile = false;
+
+ emit(startJob(newJob));
+
+ // Same message and *strings* from tracksimp.cpp
+ int counter(1);
+ KMessageBox::information(this,
+ i18n("%1 Job(s) have been started. You can watch their progress in the " \
+ "jobs section.").arg(counter),
+ i18n("Jobs have started"), i18n("Jobs have started"));
+}
+
+void EncodeFileImp::enableEncodeButton(const QString &text){
+ encodeButton->setEnabled(!text.isEmpty());
+}
+
+
+#include "encodefileimp.moc"
diff --git a/kaudiocreator/encodefileimp.h b/kaudiocreator/encodefileimp.h
new file mode 100644
index 00000000..f64b3b16
--- /dev/null
+++ b/kaudiocreator/encodefileimp.h
@@ -0,0 +1,57 @@
+/**
+ * This file is part of the KAudioCreator package
+ * Copyright (C) 2003 Benjamin C Meyer (ben+kaudiocreator at meyerhome dot net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ENCODEFILEIMP_H
+#define ENCODEFILEIMP_H
+
+#include "libkcddb/genres.h"
+#include "encodefile.h"
+#include "qmap.h"
+
+class Job;
+
+/**
+ * This class lets the user encode a file.
+ */
+class EncodeFileImp : public EncodeFile {
+
+Q_OBJECT
+
+signals:
+ // Start encoding this wav file
+ void startJob(Job *newJob);
+
+public:
+ EncodeFileImp(QWidget* parent = 0, const char* name = 0);
+
+protected slots:
+ // Encode button
+ void encode();
+ // Enable encode button when user selects a file
+ void enableEncodeButton(const QString &text);
+
+private:
+ // List of genres and i18n versions
+ KCDDB::Genres m_genres;
+
+};
+
+#endif // ENCODEFILEIMP_H
+
diff --git a/kaudiocreator/encoder.cpp b/kaudiocreator/encoder.cpp
new file mode 100644
index 00000000..5dc0ef54
--- /dev/null
+++ b/kaudiocreator/encoder.cpp
@@ -0,0 +1,331 @@
+/**
+ * This file is part of the KAudioCreator package
+ * Copyright (C) 2003 Benjamin C Meyer (ben+kaudiocreator at meyerhome dot net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "encoder.h"
+
+#include "prefs.h"
+#include "encoder_prefs.h"
+#include "encoderoutput.h"
+
+#include <qregexp.h>
+#include <qdir.h>
+#include <kstandarddirs.h>
+#include <kmessagebox.h>
+#include <kurl.h>
+#include <kdebug.h>
+#include <knotifyclient.h>
+#include <qtextedit.h>
+#include <kinputdialog.h>
+
+/**
+ * Constructor, load settings.
+ */
+Encoder::Encoder( QObject* parent, const char* name):QObject(parent,name),reportCount(0) {
+ loadSettings();
+}
+
+/**
+ * Load the settings for this class.
+ */
+void Encoder::loadSettings() {
+ loadEncoder(Prefs::currentEncoder());
+ // If the cpu count change then try
+ for(uint i=0; i<(uint)Prefs::numberOfCpus(); i++)
+ tendToNewJobs();
+}
+
+EncoderPrefs* Encoder::loadEncoder( int encoder ){
+ EncoderPrefs* prefs;
+ QString currentEncoderGroup = QString("Encoder_%1").arg(encoder);
+ prefs = EncoderPrefs::prefs(currentEncoderGroup);
+ if ( !EncoderPrefs::hasPrefs(currentEncoderGroup) ) {
+ KMessageBox::sorry(0, i18n("No encoder has been selected.\nPlease select an encoder in the configuration."), i18n("No Encoder Selected"));
+ prefs->setCommandLine(QString::null);
+ }
+
+ return prefs;
+}
+
+/**
+ * Deconstructor, remove pending jobs, remove current jobs.
+ */
+Encoder::~Encoder() {
+ pendingJobs.clear();
+
+ QMap<KShellProcess*, Job*>::Iterator pit;
+ for( pit = jobs.begin(); pit != jobs.end(); ++pit ) {
+ Job *job = jobs[pit.key()];
+ KShellProcess *process = pit.key();
+ threads.remove(process);
+ process->kill();
+ QFile::remove(job->newLocation);
+ delete job;
+ delete process;
+ }
+ jobs.clear();
+}
+
+/**
+ * @return The number of active jobs
+ */
+int Encoder::activeJobCount() {
+ return jobs.count();
+}
+
+/**
+ * @return The number of pending jobs
+ */
+int Encoder::pendingJobCount() {
+ return pendingJobs.count();
+}
+
+/**
+ * Stop this job with the matching id.
+ * @param id the id number of the job to stop.
+ */
+void Encoder::removeJob(int id ) {
+ QMap<KShellProcess*, Job*>::Iterator it;
+ for( it = jobs.begin(); it != jobs.end(); ++it ) {
+ if ( it.data()->id == id ) {
+ KShellProcess *process = it.key();
+ Job *job = jobs[it.key()];
+ threads.remove(process);
+ process->kill();
+ jobs.remove(process);
+ delete job;
+ delete process;
+ break;
+ }
+ }
+ Job *job = pendingJobs.first();
+ while(job ) {
+ if ( job->id == id)
+ break;
+ job = pendingJobs.next();
+ }
+ if ( job ) {
+ pendingJobs.remove(job);
+ delete job;
+ }
+ tendToNewJobs();
+}
+
+/**
+ * Adds job to the que of jobs to encode.
+ * @param job the job to encode.
+ */
+void Encoder::encodeWav(Job *job ) {
+ emit(addJob(job, i18n("Encoding (%1): %2 - %3").arg(loadEncoder(job->encoder)->extension())
+ .arg(job->track_artist).arg(job->track_title)));
+ pendingJobs.append(job);
+ tendToNewJobs();
+}
+
+/**
+ * See if there are are new jobs to attend too. If we are all loaded up
+ * then just loop back in a few seconds and check agian.
+ */
+void Encoder::tendToNewJobs() {
+ if ( pendingJobs.count() == 0 ) {
+ emit jobsChanged();
+ return;
+ }
+
+ // If we are currently ripping the max try again in a little bit.
+ if ( (int)threads.count() >= Prefs::numberOfCpus() ) {
+ emit jobsChanged();
+ return;
+ }
+
+ Job *job = pendingJobs.first();
+ pendingJobs.remove(job);
+
+ EncoderPrefs* prefs = loadEncoder(job->encoder);
+
+ QString desiredFile = Prefs::fileFormat();
+ desiredFile.replace( QRegExp("~"), QDir::homeDirPath() );
+ {
+ QMap <QString,QString> map;
+ map.insert("extension", prefs->extension());
+ Job jobx = *job;
+ jobx.fix(Prefs::replaceInput(), Prefs::replaceOutput());
+ jobx.fix("/", "%2f");
+ // If the user wants anything regexp replaced do it now...
+ desiredFile = jobx.replaceSpecialChars(desiredFile, false, map);
+ }
+
+ while ( QFile::exists( desiredFile ) ) {
+ bool ok;
+ QString text = KInputDialog::getText(
+ i18n("File Already Exists"), i18n("Sorry, file already exists. Please pick a new name:"),
+ desiredFile, &ok );
+ if ( ok && !text.isEmpty() )
+ desiredFile = text;
+ else {
+ emit jobsChanged();
+ updateProgress(job->id, -1);
+ return;
+ }
+ }
+
+ int lastSlash = desiredFile.findRev('/',-1);
+ if ( lastSlash == -1 ||
+ !(KStandardDirs::makeDir( desiredFile.mid(0,lastSlash), 0775)) ) {
+ KMessageBox::sorry(0, i18n("Cannot place file, unable to make directories."), i18n("Encoding Failed"));
+ emit jobsChanged();
+ updateProgress(job->id, -1);
+ return;
+ }
+
+ job->newLocation = desiredFile;
+ reportCount = 0;
+
+ QString command = prefs->commandLine(); {
+ QMap <QString,QString> map;
+ map.insert("extension", prefs->extension());
+ map.insert("f", job->location);
+ map.insert("o", desiredFile);
+ command = job->replaceSpecialChars(command, true, map);
+ }
+
+ updateProgress(job->id, 1);
+
+ job->errorString = command;
+ KShellProcess *proc = new KShellProcess();
+ proc->setPriority(Prefs::niceLevel());
+
+ *proc << QFile::encodeName(command);
+ connect(proc, SIGNAL(receivedStdout(KProcess *, char *, int )),
+ this, SLOT(receivedThreadOutput(KProcess *, char *, int )));
+ connect(proc, SIGNAL(receivedStderr(KProcess *, char *, int )),
+ this, SLOT(receivedThreadOutput(KProcess *, char *, int )));
+ connect(proc, SIGNAL(processExited(KProcess *)), this, SLOT(jobDone(KProcess *)));
+ jobs.insert(proc, job);
+ threads.append(proc);
+
+ proc->start(KShellProcess::NotifyOnExit, KShellProcess::AllOutput);
+ emit jobsChanged();
+}
+
+/**
+ * We have received some output from a thread. See if it contains %.
+ * @param proc the process that has new output.
+ * @param buffer the output from the process
+ * @param buflen the length of the buffer.
+ */
+void Encoder::receivedThreadOutput(KProcess *process, char *buffer, int length ) {
+ if ( Prefs::fullDecoderDebug() && buffer)
+ kdDebug(60002) << buffer << endl;
+
+ // Make sure we have a job to send an update too.
+ if(jobs.find((KShellProcess*)process) == jobs.end()){
+ kdDebug(60002) << "Encoder::receivedThreadOutput Job doesn't exists. Line: " << __LINE__ << endl;
+ return;
+ }
+
+ Job *job = jobs[(KShellProcess*)process];
+
+ // Keep the output in the event it fails.
+ job->output += QString(buffer).mid(0,length);
+
+ // Make sure the output string has a % symble in it.
+ QString output = QString(buffer).mid(0,length);
+ if ( output.find('%') == -1 && reportCount < 5 ) {
+ kdDebug(60002) << "No \'%%\' in output. Report as bug w/encoder options if progressbar doesn't fill." << endl;
+ reportCount++;
+ return;
+ }
+ //qDebug(QString("Pre cropped: %1").arg(output).latin1());
+ output = output.mid(output.find('%')-loadEncoder(job->encoder)->percentLength(),2);
+ //qDebug(QString("Post cropped: %1").arg(output).latin1());
+ bool conversionSuccessfull = false;
+ int percent = output.toInt(&conversionSuccessfull);
+ //qDebug(QString("number: %1").arg(percent).latin1());
+ if ( percent >= 0 && percent < 100 && conversionSuccessfull ) {
+ emit(updateProgress(job->id, percent));
+ }
+ // If it was just some random output that couldn't be converted then don't report the error.
+ else
+ if ( conversionSuccessfull )
+ kdWarning("Percent done:\"%d\" is not >= 0 && < 100.", percent);
+}
+
+/**
+ * When the process is done encoding a file this function is called.
+ * @param job the job that just finished.
+ */
+void Encoder::jobDone(KProcess *process ) {
+ // Normal error checking here.
+ if ( !process)
+ return;
+
+ //qDebug("Process exited with status: %d", process->exitStatus());
+
+ Job *job = jobs[(KShellProcess*)process];
+ threads.remove((KShellProcess*)process);
+ jobs.remove((KShellProcess*)process);
+
+ bool showDebugBox = false;
+ if ( process->exitStatus() == 127 ) {
+ KMessageBox::sorry(0, i18n("The selected encoder was not found.\nThe wav file has been removed. Command was: %1").arg(job->errorString), i18n("Encoding Failed"));
+ emit(updateProgress(job->id, -1));
+ }
+ else if ( QFile::exists(job->newLocation) ) {
+ // fyi segfaults return 136
+ if ( process->exitStatus() != 0 ) {
+ if ( KMessageBox::questionYesNo(0, i18n("The encoder exited with a error. Please check that the file was created.\nDo you want to see the full encoder output?"), i18n("Encoding Failed"),i18n("Show Output"),i18n("Skip Output")) == KMessageBox::Yes )
+ {
+ showDebugBox = true;
+ }
+ }
+ else{
+ //qDebug("Must be done: %d", (process->exitStatus()));
+ emit(updateProgress(job->id, 100));
+ KNotifyClient::event("track encoded");
+ if ( job->lastSongInAlbum)
+ KNotifyClient::event("cd encoded");
+ }
+ }
+ else
+ {
+ if ( KMessageBox::questionYesNo(0, i18n("The encoded file was not created.\nPlease check the encoder options.\nThe wav file has been removed.\nDo you want to see the full encoder output?"), i18n("Encoding Failed"),i18n("Show Output"),i18n("Skip Output")) == KMessageBox::Yes )
+ {
+ showDebugBox = true;
+ }
+ emit( updateProgress( job->id, -1 ) );
+ }
+
+ if ( job->removeTempFile )
+ QFile::remove( job->location );
+
+ if( showDebugBox ){
+ EncoderOutput dlg( 0, "Encoder Output" );
+ job->output = job->errorString + "\n\n\n" + job->output;
+ dlg.output->setText(job->output);
+ dlg.exec();
+ }
+
+ delete job;
+ delete process;
+ tendToNewJobs();
+}
+
+#include "encoder.moc"
+
diff --git a/kaudiocreator/encoder.h b/kaudiocreator/encoder.h
new file mode 100644
index 00000000..2398d2fb
--- /dev/null
+++ b/kaudiocreator/encoder.h
@@ -0,0 +1,68 @@
+/**
+ * This file is part of the KAudioCreator package
+ * Copyright (C) 2003 Benjamin C Meyer (ben+kaudiocreator at meyerhome dot net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ENCODER_H
+#define ENCODER_H
+
+#include <qobject.h>
+
+#include <qptrlist.h>
+#include <qmap.h>
+#include "job.h"
+#include <kprocess.h>
+
+class EncoderPrefs;
+
+class Encoder : public QObject {
+
+Q_OBJECT
+
+signals:
+ void addJob(Job *job, const QString &name);
+ void updateProgress(int id, int progress);
+ void jobsChanged();
+
+public:
+ Encoder( QObject* parent = 0, const char* name = 0);
+ ~Encoder();
+ int activeJobCount();
+ int pendingJobCount();
+
+public slots:
+ void removeJob(int id);
+ void encodeWav(Job *job);
+ void loadSettings();
+ EncoderPrefs* loadEncoder( int encoder );
+
+private slots:
+ void receivedThreadOutput(KProcess *process, char *buffer, int buflen);
+ void jobDone(KProcess *process);
+ void tendToNewJobs();
+
+private:
+ QPtrList<Job> pendingJobs;
+ QPtrList<KShellProcess> threads;
+ QMap<KShellProcess*, Job*> jobs;
+
+ int reportCount;
+};
+
+#endif // ENCODER_H
+
diff --git a/kaudiocreator/encoder_prefs.kcfgc b/kaudiocreator/encoder_prefs.kcfgc
new file mode 100644
index 00000000..075a4321
--- /dev/null
+++ b/kaudiocreator/encoder_prefs.kcfgc
@@ -0,0 +1,7 @@
+# Code generation options for kconfig_compiler
+File=kaudiocreator_encoders.kcfg
+#IncludeFiles=defines.h
+ClassName=EncoderPrefs
+Singleton=false
+CustomAdditions=true
+Mutators=true
diff --git a/kaudiocreator/encoder_prefs_addons.h b/kaudiocreator/encoder_prefs_addons.h
new file mode 100644
index 00000000..067f4d79
--- /dev/null
+++ b/kaudiocreator/encoder_prefs_addons.h
@@ -0,0 +1,8 @@
+
+public:
+ static EncoderPrefs *prefs(const QString &groupName);
+ static bool hasPrefs(const QString &groupName);
+ static void deletePrefs(const QString &groupName);
+
+private:
+ static QDict<EncoderPrefs> *m_prefs;
diff --git a/kaudiocreator/encoderconfig.ui b/kaudiocreator/encoderconfig.ui
new file mode 100644
index 00000000..edab93fb
--- /dev/null
+++ b/kaudiocreator/encoderconfig.ui
@@ -0,0 +1,293 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>EncoderConfig</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>EncoderConfig</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>434</width>
+ <height>349</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QListBox" row="0" column="0" rowspan="3" colspan="1">
+ <property name="name">
+ <cstring>kcfg_currentEncoder</cstring>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="0" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>addEncoder</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Add...</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>removeEncoder</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>configureEncoder</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Configure...</string>
+ </property>
+ </widget>
+ <widget class="Line" row="4" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>line1</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QGroupBox" row="5" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>GroupBox3</cstring>
+ </property>
+ <property name="title">
+ <string>Encoded File Location</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Location:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>kcfg_fileFormat</cstring>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>encoderWizardButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Wizard</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>TextLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Number of wav files to encode at a time:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_numberOfCpus</cstring>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="6" column="1">
+ <property name="name">
+ <cstring>kcfg_numberOfCpus</cstring>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ </widget>
+ <spacer row="6" column="2">
+ <property name="name">
+ <cstring>Spacer4_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget" row="3" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>currentEncoderText</cstring>
+ </property>
+ <property name="text">
+ <string>Current encoder:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>currentEncoderName</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>LineEditPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer row="10" column="0">
+ <property name="name">
+ <cstring>Spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>30</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="9" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>groupBox3</cstring>
+ </property>
+ <property name="title">
+ <string>Encoder Priority</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QSlider" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_NiceLevel</cstring>
+ </property>
+ <property name="minValue">
+ <number>-19</number>
+ </property>
+ <property name="maxValue">
+ <number>19</number>
+ </property>
+ <property name="lineStep">
+ <number>5</number>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="tickmarks">
+ <enum>NoMarks</enum>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Highest</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Lowest</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Normal</string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>kcfg_currentEncoder</sender>
+ <signal>highlighted(const QString&amp;)</signal>
+ <receiver>currentEncoderName</receiver>
+ <slot>setText(const QString&amp;)</slot>
+ </connection>
+ <connection>
+ <sender>encoderWizardButton</sender>
+ <signal>clicked()</signal>
+ <receiver>EncoderConfig</receiver>
+ <slot>encoderWizard()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>kcfg_currentEncoder</tabstop>
+ <tabstop>addEncoder</tabstop>
+ <tabstop>removeEncoder</tabstop>
+ <tabstop>configureEncoder</tabstop>
+ <tabstop>kcfg_fileFormat</tabstop>
+ <tabstop>encoderWizardButton</tabstop>
+ <tabstop>kcfg_numberOfCpus</tabstop>
+</tabstops>
+<includes>
+ <include location="local" impldecl="in implementation">encoderconfig.ui.h</include>
+</includes>
+<slots>
+ <slot>encoderWizard()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kaudiocreator/encoderconfig.ui.h b/kaudiocreator/encoderconfig.ui.h
new file mode 100644
index 00000000..fd95a75a
--- /dev/null
+++ b/kaudiocreator/encoderconfig.ui.h
@@ -0,0 +1,25 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+
+#include "wizard.h"
+#include <qlineedit.h>
+
+/**
+ * Load up the wizard with the encoder fileFormat string. Save it if OK is hit.
+ */
+void EncoderConfig::encoderWizard(){
+ fileWizard wizard(this, "Encoder File Format Wizard", true);
+ wizard.fileFormat->setText(kcfg_fileFormat->text());
+
+ // Show dialog and save results if ok is pressed.
+ bool okClicked = wizard.exec();
+ if(okClicked){
+ kcfg_fileFormat->setText(wizard.fileFormat->text());
+ }
+}
diff --git a/kaudiocreator/encoderconfigimp.cpp b/kaudiocreator/encoderconfigimp.cpp
new file mode 100644
index 00000000..feebae7d
--- /dev/null
+++ b/kaudiocreator/encoderconfigimp.cpp
@@ -0,0 +1,289 @@
+/**
+ * This file is part of the KAudioCreator package
+ * Copyright (C) 2003 Benjamin C Meyer (ben+kaudiocreator at meyerhome dot net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "encoderconfigimp.h"
+#include "encoderedit.h"
+#include "prefs.h"
+
+#include <qpushbutton.h>
+#include <qlineedit.h>
+#include <kconfigdialog.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kconfig.h>
+
+/**
+ * Constructor.
+ */
+EncoderConfigImp::EncoderConfigImp( QWidget* parent, const char* name) :
+ EncoderConfig (parent, name) {
+ connect(addEncoder, SIGNAL(clicked()), this, SLOT(addEncoderSlot()));
+ connect(removeEncoder, SIGNAL(clicked()), this, SLOT(removeEncoderSlot()));
+ connect(configureEncoder, SIGNAL(clicked()), this, SLOT(configureEncoderSlot()));
+ connect(kcfg_currentEncoder, SIGNAL(doubleClicked ( QListBoxItem * )),this, SLOT(configureEncoderSlot()));
+
+ // If there are no encoders then store the three default ones.
+ if( Prefs::lastKnownEncoder() == 0){
+ EncoderPrefs *encPrefs;
+
+ encPrefs = EncoderPrefs::prefs("Encoder_0");
+ encPrefs->setEncoderName(i18n("Ogg Vorbis"));
+ encPrefs->setCommandLine("oggenc -o %o --artist %{artist} --album %{albumtitle} --title %{title} --date %{year} --tracknum %{number} --genre %{genre} %f");
+ encPrefs->setExtension("ogg");
+ encPrefs->setPercentLength(4);
+ encPrefs->writeConfig();
+
+ encPrefs = EncoderPrefs::prefs("Encoder_1");
+ encPrefs->setEncoderName(i18n("MP3"));
+ encPrefs->setCommandLine("lame --preset standard --tt %{title} --ta %{artist} --tl %{albumtitle} --ty %{year} --tn %{number} --tg %{genre} %f %o");
+ encPrefs->setExtension("mp3");
+ encPrefs->setPercentLength(2);
+ encPrefs->writeConfig();
+
+ encPrefs = EncoderPrefs::prefs("Encoder_2");
+ encPrefs->setEncoderName(i18n("Wav"));
+ encPrefs->setCommandLine("mv %f %o");
+ encPrefs->setExtension("wav");
+ encPrefs->setPercentLength(2);
+ encPrefs->writeConfig();
+
+ encPrefs = EncoderPrefs::prefs("Encoder_3");
+ encPrefs->setEncoderName(i18n("FLAC"));
+ encPrefs->setCommandLine("flac --best -o %o --tag=Artist=%{artist} --tag=Album=%{albumtitle} --tag=Date=%{year} --tag=Title=%{title} --tag=Tracknumber=%{number} --tag=Genre=%{genre} %f");
+ encPrefs->setExtension("flac");
+ encPrefs->setPercentLength(2);
+ encPrefs->writeConfig();
+
+ Prefs::setLastKnownEncoder(3);
+ Prefs::writeConfig();
+ }
+
+ loadEncoderList();
+}
+
+/**
+ * Clear map
+ * Clear listbox
+ * Load list of encoders.
+ */
+void EncoderConfigImp::loadEncoderList(){
+ encoderNames.clear();
+ kcfg_currentEncoder->clear();
+
+ bool foundCurrentEncoder = false;
+
+ int lastEncoder = 0;
+ int lastKnownEncoder = Prefs::lastKnownEncoder();
+ lastKnownEncoder++;
+ for( int i=0; i<=lastKnownEncoder; i++ ){
+ QString currentGroup = QString("Encoder_%1").arg(i);
+ if(EncoderPrefs::hasPrefs(currentGroup)){
+ lastEncoder = i;
+ EncoderPrefs *encPrefs = EncoderPrefs::prefs(currentGroup);
+ QString encoderName = encPrefs->encoderName();
+ kcfg_currentEncoder->insertItem(encoderName);
+ encoderNames.insert(encoderName, currentGroup);
+ if(Prefs::currentEncoder() == i)
+ foundCurrentEncoder = true;
+ }
+ }
+ if(lastEncoder != Prefs::lastKnownEncoder()){
+ Prefs::setLastKnownEncoder(lastEncoder);
+ Prefs::writeConfig();
+ }
+
+ // Make sure that the current encoder is valid.
+ if(!foundCurrentEncoder && kcfg_currentEncoder->count() > 0)
+ kcfg_currentEncoder->setCurrentItem(0);
+}
+
+/**
+ * Find empty group
+ * bring up dialog for that group.
+ */
+void EncoderConfigImp::addEncoderSlot(){
+ bool foundEmptyGroup = false;
+ uint number = 0;
+ QString groupName;
+ while(!foundEmptyGroup){
+ groupName = QString("Encoder_%1").arg(number);
+ if(!EncoderPrefs::hasPrefs(groupName))
+ foundEmptyGroup = true;
+ else
+ number++;
+ }
+
+ if(KConfigDialog::showDialog(groupName.latin1()))
+ return;
+
+ KConfigDialog *dialog = new KConfigDialog(this, groupName.latin1(), EncoderPrefs::prefs(groupName),
+ KDialogBase::Swallow,
+ KDialogBase::Ok | KDialogBase::Cancel | KDialogBase::Help);
+ dialog->setCaption(i18n("Configure Encoder"));
+ dialog->addPage(new EncoderEdit(0, groupName.latin1()), i18n("Encoder Configuration"), "package_settings");
+ connect(dialog, SIGNAL(settingsChanged()), this, SLOT(loadEncoderList()));
+ dialog->show();
+}
+
+/**
+ * If
+ * Something is selected
+ * There is more then 1 thing left
+ * The user says ok to delete.
+ * Is not the current encoder.
+ * Then
+ * The group is removed from the list
+ * Deleted from the config.
+ */
+void EncoderConfigImp::removeEncoderSlot(){
+ if(!kcfg_currentEncoder->selectedItem()){
+ KMessageBox:: sorry(this, i18n("Please select an encoder."), i18n("No Encoder Selected"));
+ return;
+ }
+ if(kcfg_currentEncoder->count() <= 1){
+ KMessageBox:: sorry(this, i18n("At least one encoder must exist."), i18n("Can Not Remove"));
+ return;
+ }
+ if(KMessageBox::warningContinueCancel(this, i18n("Delete encoder?"), i18n("Delete Encoder"),KStdGuiItem::del())
+ == KMessageBox::Cancel )
+ return;
+
+ QString groupName = encoderNames[kcfg_currentEncoder->currentText()];
+ kcfg_currentEncoder->removeItem(kcfg_currentEncoder->currentItem());
+
+ delete KConfigDialog::exists(groupName.latin1());
+
+ EncoderPrefs::deletePrefs(groupName);
+}
+
+/**
+ * If
+ * Something is selected
+ * Group exists
+ * Then
+ * Bring up dialog
+ */
+void EncoderConfigImp::configureEncoderSlot() {
+ if(!kcfg_currentEncoder->selectedItem()){
+ KMessageBox:: sorry(this, i18n("Please select an encoder."), i18n("No Encoder Selected"));
+ return;
+ }
+ QString groupName = encoderNames[kcfg_currentEncoder->currentText()];
+ KConfig &config = *KGlobal::config();
+ if(!config.hasGroup(groupName))
+ return;
+
+ if(KConfigDialog::showDialog(groupName.latin1()))
+ return;
+
+ KConfigDialog *dialog = new KConfigDialog(this, groupName.latin1(), EncoderPrefs::prefs(groupName),
+ KDialogBase::Swallow,
+ KDialogBase::Ok | KDialogBase::Cancel | KDialogBase::Help);
+ dialog->setCaption(i18n("Configure Encoder"));
+ dialog->addPage(new EncoderEdit(0, groupName.latin1()), i18n("Encoder Configuration"), "package_settings");
+ connect(dialog, SIGNAL(destroyed(QObject *)), this, SLOT(updateEncoder(QObject *)));
+ connect(dialog, SIGNAL(settingsChanged()), this, SIGNAL(encoderUpdated()));
+ connect(dialog, SIGNAL(settingsChanged(const char *)), this, SLOT(updateEncoder(const char *)));
+ dialog->show();
+}
+
+/**
+ * If object exists update encoder
+ */
+void EncoderConfigImp::updateEncoder(QObject * obj){
+ if(!obj)
+ return;
+ updateEncoder(obj->name());
+}
+
+/**
+ * If
+ * Exists
+ * Then
+ * Get name
+ * Make sure group exists
+ * Update name
+ * Update Map
+ * If current encoder update also.
+ */
+void EncoderConfigImp::updateEncoder(const char *dialogName){
+ QString groupName = dialogName;
+ QString encoderName;
+ bool found = false;
+ QMap<QString, QString>::Iterator it;
+ for ( it = encoderNames.begin(); it != encoderNames.end(); ++it ) {
+ if(it.data() == groupName){
+ found = true;
+ encoderName = it.key();
+ }
+ }
+ if(!found)
+ return;
+ if(!EncoderPrefs::hasPrefs(groupName))
+ return;
+ QString newName = EncoderPrefs::prefs(groupName)->encoderName();
+ if(newName == encoderName)
+ return;
+
+ QListBoxItem *item = kcfg_currentEncoder->findItem(encoderName);
+ if(!item)
+ return;
+ kcfg_currentEncoder->changeItem(newName, kcfg_currentEncoder->index(item));
+
+ encoderNames.insert(newName, groupName);
+ encoderNames.erase(encoderName);
+}
+
+QDict<EncoderPrefs> *EncoderPrefs::m_prefs = 0;
+
+EncoderPrefs *EncoderPrefs::prefs(const QString &groupName)
+{
+ if (!m_prefs)
+ {
+ m_prefs = new QDict<EncoderPrefs>();
+ m_prefs->setAutoDelete(true);
+ }
+ EncoderPrefs *encPrefs = m_prefs->find(groupName);
+ if (encPrefs)
+ return encPrefs;
+
+ encPrefs = new EncoderPrefs(groupName);
+ encPrefs->readConfig();
+ m_prefs->insert(groupName, encPrefs);
+ return encPrefs;
+}
+
+bool EncoderPrefs::hasPrefs(const QString &groupName)
+{
+ KConfig &config = *KGlobal::config();
+ return config.hasGroup(groupName);
+}
+
+void EncoderPrefs::deletePrefs(const QString &groupName)
+{
+ KConfig &config = *KGlobal::config();
+ config.deleteGroup(groupName);
+ if (!m_prefs)
+ return;
+ m_prefs->remove(groupName);
+}
+
+#include "encoderconfigimp.moc"
+
diff --git a/kaudiocreator/encoderconfigimp.h b/kaudiocreator/encoderconfigimp.h
new file mode 100644
index 00000000..9d402637
--- /dev/null
+++ b/kaudiocreator/encoderconfigimp.h
@@ -0,0 +1,57 @@
+/**
+ * This file is part of the KAudioCreator package
+ * Copyright (C) 2003 Benjamin C Meyer (ben+kaudiocreator at meyerhome dot net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ENCODERCONFIGIMP_H
+#define ENCODERCONFIGIMP_H
+
+#include "encoderconfig.h"
+#include "encoder_prefs.h"
+#include <qmap.h>
+#include <qdict.h>
+
+/**
+ * Class mainly build to manage encoder list.
+ */
+class EncoderConfigImp : public EncoderConfig {
+
+Q_OBJECT
+
+signals:
+ void encoderUpdated();
+
+public:
+ EncoderConfigImp( QWidget* parent = 0, const char* name = 0);
+
+private slots:
+ void addEncoderSlot();
+ void removeEncoderSlot();
+ void configureEncoderSlot();
+
+ void updateEncoder(QObject * obj);
+ void updateEncoder(const char *dialogName);
+ void loadEncoderList();
+
+private:
+ // Name, groupName
+ QMap<QString, QString> encoderNames;
+};
+
+#endif
+
diff --git a/kaudiocreator/encoderedit.ui b/kaudiocreator/encoderedit.ui
new file mode 100644
index 00000000..6a0fa7f0
--- /dev/null
+++ b/kaudiocreator/encoderedit.ui
@@ -0,0 +1,98 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>EncoderEdit</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>EncoderEdit</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>456</width>
+ <height>129</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>kcfg_commandLine</cstring>
+ </property>
+ <property name="text">
+ <string>app.exe %f %o</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>kcfg_encoderName</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>320</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Unknown Encoder</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>kcfg_extension</cstring>
+ </property>
+ <property name="text">
+ <string>wav</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Extension:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>Command line:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Name:</string>
+ </property>
+ </widget>
+ <spacer row="3" column="1">
+ <property name="name">
+ <cstring>spacer14</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>kcfg_encoderName</tabstop>
+ <tabstop>kcfg_commandLine</tabstop>
+ <tabstop>kcfg_extension</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kaudiocreator/encoderoutput.ui b/kaudiocreator/encoderoutput.ui
new file mode 100644
index 00000000..1cc26be4
--- /dev/null
+++ b/kaudiocreator/encoderoutput.ui
@@ -0,0 +1,103 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>EncoderOutput</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>EncoderOutput</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>460</width>
+ <height>460</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Encoder Output</string>
+ </property>
+ <property name="sizeGripEnabled">
+ <bool>true</bool>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QTextEdit">
+ <property name="name">
+ <cstring>output</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>The output....</string>
+ </property>
+ <property name="wordWrap">
+ <enum>WidgetWidth</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>Horizontal Spacing2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonOk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;OK</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>buttonOk</sender>
+ <signal>clicked()</signal>
+ <receiver>EncoderOutput</receiver>
+ <slot>accept()</slot>
+ </connection>
+</connections>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kaudiocreator/eventsrc b/kaudiocreator/eventsrc
new file mode 100644
index 00000000..31ff062c
--- /dev/null
+++ b/kaudiocreator/eventsrc
@@ -0,0 +1,528 @@
+[!Global!]
+IconName=kaudiocreator
+Comment=KAudioCreator
+Comment[bn]=কে-অডিও-ক্রিয়েটার
+Comment[cy]=KCreuyddSain
+Comment[mk]=КАудиоКреатор
+Comment[ne]=केडीई अडियो सर्जक
+Comment[pa]=ਕੇ-ਆਡੀਓ-ਨਿਰਮਾਤਾ
+Comment[sv]=Kaudiocreator
+Comment[tg]=KОфарандаи Садо
+Comment[tr]=Kaudiocreator
+
+[no jobs left]
+Name=All jobs finished
+Name[bg]=Всички задачи завършиха
+Name[br]=Echu eo pep dlead
+Name[bs]=Svi zadaci su završeni
+Name[ca]=Han acabat tots els treballs
+Name[cs]=Všechny úkoly dokončeny
+Name[da]=Alle job færdige
+Name[de]=Alle Aufgaben sind abgeschlossen.
+Name[el]=Όλες οι εργασίες ολοκληρώθηκαν
+Name[eo]=Ĉiuj taskoj finiĝis
+Name[es]=Todos los trabajos finalizados
+Name[et]=Kõik tööd lõpetatud
+Name[eu]=Lan guztiak amaituta
+Name[fa]=همۀ کارها تمام شد
+Name[fi]=Kaikki työt ovat valmistuneet
+Name[fr]=Toutes les tâches sont terminées
+Name[gl]=Todos os traballos rematados
+Name[he]=כל העבודות הסתיימו
+Name[hu]=Az összes feladat befejeződött
+Name[is]=Öllum verkum lokið
+Name[it]=Tutti i processi sono terminati
+Name[ja]=全ジョブ完了
+Name[kk]=Бүкіл тапсырмалар бітті
+Name[km]=បាន​បញ្ចប់​ការងារ​ទាំងអស់
+Name[ko]=모든 작업 완료됨
+Name[lt]=Visi darbai baigti
+Name[mk]=Сите задачи се завршени
+Name[nb]=Alle jobbene er utført
+Name[nds]=All Opgaven beendt
+Name[ne]=सबै काम समाप्त भयो
+Name[nl]=Alle taken voltooid
+Name[nn]=Alle jobbar ferdige
+Name[pa]=ਸਭ ਕੰਮ ਖਤਮ
+Name[pl]=Wszystkie zadania ukończone
+Name[pt]=Todos os trabalhos terminados
+Name[pt_BR]=Todos os trabalhos finalizados
+Name[ru]=Все задания выполнены
+Name[sk]=Všetky úlohy skončili
+Name[sl]=Vsa opravila zaključena
+Name[sr]=Сви послови завршени
+Name[sr@Latn]=Svi poslovi završeni
+Name[sv]=Alla jobb är klara
+Name[th]=งานทั้งหมดเสร็จสิ้น
+Name[tr]=Tüm görevler tamamlandı
+Name[uk]=Всі задачі виконано
+Name[zh_CN]=全部任务都已完成
+Name[zh_HK]=工作全部完成
+Name[zh_TW]=所有工作均已完成
+Comment=All of the jobs in the queue have been finished
+Comment[bg]=Всички задачи в опашката завършиха
+Comment[br]=Echu eo pep dlead el lost
+Comment[bs]=Svi zadaci u redu čekanja su završeni
+Comment[ca]=Han acabat tots els treballs de la cua
+Comment[cs]=Všechny úkoly ve frontě byly dokončeny
+Comment[da]=Alle job i køen er færdige
+Comment[de]=Alle Aufgaben der Warteschlange sind fertig.
+Comment[el]=Όλες οι εργασίες της ουράς ολοκληρώθηκαν
+Comment[es]=Se han terminado todos los trabajos de la cola
+Comment[et]=Kõik järjekorras olnud tööd on lõpetatud
+Comment[eu]=Ilarako lan guztiak amaitu dira
+Comment[fa]=همۀ کارهای درون صف تمام شده‌اند
+Comment[fi]=Kaikki työt jonossa ovat valmistuneet
+Comment[fr]=Toutes les tâches dans la file d'attente sont terminées
+Comment[gl]=Atinxíronse todos os traballos na lista
+Comment[he]=כל העבודות בתור הסתיימו
+Comment[hu]=A sorban várakozó feladatok befejeződtek
+Comment[is]=Öllum verkum í biðröð hefur verið lokið
+Comment[it]=Tutti i processi in coda sono terminati
+Comment[ja]=キューにある全ジョブが完了しました
+Comment[kk]=Бүкіл кезектегі тапсырмалар бітті
+Comment[km]=ការងារ​ទាំងអស់​នៅ​ក្នុង​ជួរ ត្រូវ​បាន​បញ្ចប់​ហើយ
+Comment[ko]=큐에 있는 모든 작업이 완료됨
+Comment[lt]=Visi surikiuoti darbai atlikti
+Comment[mk]=Сите задачи во редицата завршија
+Comment[nb]=Alle jobbene i køen er utført
+Comment[nds]=All Opgaven binnen de Töövreeg sünd beendt
+Comment[ne]=लाममा रहेको सबै काम समाप्त भयो
+Comment[nl]=Alle taken in de wachtrij zijn voltooid
+Comment[nn]=Alle jobbane i køen er ferdige
+Comment[pa]=ਕਤਾਰ ਵਿੱਚ ਸਭ ਕੰਮਾਂ ਨੂੰ ਖਤਮ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ
+Comment[pl]=Wszystkie zadania z kolejko zostały ukończone
+Comment[pt]=Todos os trabalhos em fila foram terminados
+Comment[pt_BR]=Todos os trabalhos na fila foram finalizados
+Comment[ru]=Все задания очереди завершены
+Comment[sk]=Všetky úlohy v rade skončili
+Comment[sl]=Vsa opravila iz vrste so bila zaključena
+Comment[sr]=Сви послови из редоследа су завршени
+Comment[sr@Latn]=Svi poslovi iz redosleda su završeni
+Comment[sv]=Alla jobb i kön är klara
+Comment[th]=งานทั้งหมดในคิวเสร็จหมดแล้ว
+Comment[tr]=Listedeki tüm görevler tamamlandı.
+Comment[uk]=Закінчено всі задачі в черзі
+Comment[zh_CN]=队列中的全部任务都已完成
+Comment[zh_HK]=佇列中所有工作已完成
+Comment[zh_TW]=所有佇列中的工作都已完成
+
+[track ripped]
+Name=Track Ripped
+Name[bg]=Запис на пътечка
+Name[bn]=ট্র্যাক রিপ করা হয়েছে
+Name[br]=Roudenn eztennet
+Name[bs]=Numera ripovana
+Name[ca]=Extractor de peces
+Name[cs]=Stopa ripována
+Name[cy]=Trac wedi'i Rhwygo
+Name[da]=Spor rippet
+Name[de]=Stück ausgelesen
+Name[el]=Το κομμάτι εξήχθη
+Name[es]=Pista extraida
+Name[et]=Rada ripitud
+Name[eu]=Pista erauztua
+Name[fa]=شیار شکافته شد
+Name[fi]=Kappale kopioitu
+Name[fr]=Piste extraite
+Name[ga]=Rian Sractha
+Name[gl]=Pista Ext
+Name[he]=רצועות נקרעו
+Name[hu]=Hangsáv kimásolva
+Name[is]=Lag afritað
+Name[it]=Track compressa
+Name[ja]=トラックのリッピング終了
+Name[kk]=Жолсызық оқып алынды
+Name[km]=បាន​ច្រៀក​បទ
+Name[ko]=트랙 추출됨
+Name[lt]=Takelis nurašytas
+Name[mk]=Спуштена песна
+Name[nb]=Spor innlest
+Name[nds]=Stück utleest
+Name[ne]=ट्रयाक काटियो
+Name[nl]=Track geript
+Name[nn]=Spor innlese
+Name[pa]=ਟਰੈਕ ਰਿਪ
+Name[pl]=Zgrany utwór
+Name[pt]=Faixa Extraída
+Name[pt_BR]=Faixa convertida
+Name[ru]=Дорожка скопирована
+Name[sl]=Skladba zajeta
+Name[sr]=Нумера исчупана
+Name[sr@Latn]=Numera isčupana
+Name[sv]=Spår lagrat
+Name[ta]=தடம் பிரிக்கப்பட்டது
+Name[tg]=Шиор Канда шудааст
+Name[th]=เพลงที่ทำการดึงข้อมูลแล้ว
+Name[tr]=Parça Dönüştürüldü
+Name[uk]=Витягнута доріжка
+Name[zh_CN]=音轨已抓取
+Name[zh_HK]=已擷取的音軌
+Name[zh_TW]=音軌已擷取
+Comment=Track finished ripping
+Comment[bg]=Завършване на запис на пътечка
+Comment[bn]=ট্র্যাক রিপ করা শেষ
+Comment[br]=Echu eo eztenniñ ar roudenn
+Comment[bs]=Završeno ripovanje numere
+Comment[ca]=Ha acabat l'extracció de la peça
+Comment[cs]=Ripování stopy dokončeno
+Comment[cy]=Wedi gorffen rhwygo'r trac
+Comment[da]=Spor færdig med at blive rippet
+Comment[de]=Das Stück ist fertig ausgelesen.
+Comment[el]=Η εξαγωγή του κομματιού ολοκληρώθηκε
+Comment[es]=Finalizó la extracción de la pista
+Comment[et]=Raja rippimine lõpetatud
+Comment[eu]=Pistaren erauzketa amaituta
+Comment[fa]= شیار به شکافتن پایان داد
+Comment[fi]=Kappaleen kaappaaminen valmis
+Comment[fr]=Extraction de la piste terminée
+Comment[gl]=Rematou a extracción da pista
+Comment[he]=קריעת רצועות הסתיימה
+Comment[hu]=A hangsáv kimásolása befejeződött
+Comment[is]=Afritun lags lauk
+Comment[it]=Estrazione traccia terminata
+Comment[ja]=トラックのリッピングが終了しました
+Comment[kk]=Жолсызықты оқып алу аяқталды
+Comment[km]=បាន​បញ្ចប់​ការ​ច្រៀក​បទ
+Comment[ko]=트랙 추출 완료됨
+Comment[lt]=Takelio nurašymas baigėsi
+Comment[mk]=Крај на спуштањето на песната
+Comment[nb]=Spor ferdig innlest
+Comment[nds]=Dat Stück wöör utleest
+Comment[ne]=ट्रयाक कटाइ समाप्त भयो
+Comment[nl]=Klaar met rippen van track
+Comment[nn]=Spor ferdig innlese
+Comment[pl]=Zakończono zgrywanie utworu
+Comment[pt]=A faixa foi extraída
+Comment[pt_BR]=Finalização da conversão da faixa
+Comment[ru]=Копирование дорожки завершено
+Comment[sk]=Dokončené ripovanie stopy
+Comment[sl]=Zajemanje skladbe končano
+Comment[sr]=Чупање нумере завршено
+Comment[sr@Latn]=Čupanje numere završeno
+Comment[sv]=Spåret har lagrats klart
+Comment[ta]=தடம் பிரித்தலை முடித்தது
+Comment[th]=เพลงที่ทำการดึงข้อมูลเสียงเสร็จแล้ว
+Comment[tr]=Parçanın dönüştürülmesi tamamlandı
+Comment[uk]=Витягування доріжки закінчено
+Comment[zh_CN]=音轨抓取完成
+Comment[zh_HK]=已完成擷取的音軌
+Comment[zh_TW]=音軌已完成擷取
+
+[cd ripped]
+Name=CD Ripped
+Name[bg]=Запис на аудио диск
+Name[bn]=সিডি রিপ করা হয়েছে
+Name[br]=CD eztennet
+Name[bs]=CD ripovan
+Name[ca]=Extractor de CD
+Name[cs]=CD ripováno
+Name[cy]=CD wedi'i Rhwygo
+Name[da]=Cd rippet
+Name[de]=CD ausgelesen
+Name[el]=Το CD εξήχθη
+Name[es]=CD extraido
+Name[et]=CD ripitud
+Name[eu]=CD-a erauztua
+Name[fa]=دیسک فشرده شکافته شد
+Name[fi]=CD kopioitu
+Name[fr]=CD extrait
+Name[ga]=CD Sractha
+Name[gl]=CD Extraído
+Name[he]=תקליטור נקרע
+Name[hu]=CD kimásolva
+Name[is]=Diskur afritaður
+Name[it]=CD estratto
+Name[ja]=CD のリッピング終了
+Name[kk]=CD оқып алынды
+Name[km]=បាន​ច្រៀក​ស៊ីឌី
+Name[ko]=CD 추출됨
+Name[lt]=CD nurašytas
+Name[mk]=CD-то е спуштено
+Name[nb]=CD innlest
+Name[nds]=CD utleest
+Name[ne]=CD काटियो
+Name[nl]=CD geript
+Name[nn]=CD innlesen
+Name[pa]=CD ਰਿਪ
+Name[pl]=Zgrane CD
+Name[pt]=CD Extraído
+Name[pt_BR]=CD Convertido
+Name[ru]=CD скопирован
+Name[sk]=Ripovanie CD dokončené
+Name[sl]=CD zajet
+Name[sr]=CD је изгребован
+Name[sr@Latn]=CD je izgrebovan
+Name[sv]=Cd lagrad
+Name[ta]=CD பிரிக்கப்பட்டது
+Name[tg]=Диски Фишурда Канда шудааст
+Name[th]=ซีดีบันทึกเสียงที่ทำการดึงข้อมูลแล้ว
+Name[tr]=CD Dönüştürüldü
+Name[uk]=Витягнутий музичний КД
+Name[uz]=Kompakt-disk ripper
+Name[uz@cyrillic]=Компакт-диск риппер
+Name[zh_CN]=CD 已抓取
+Name[zh_HK]=已擷取的 CD
+Name[zh_TW]=光碟已擷取
+Comment=CD finished ripping
+Comment[bg]=Завършване на запис на аудио диск
+Comment[bn]=সিডি রিপ করা শেষ
+Comment[br]=Echu eo eztenniñ ar CD
+Comment[bs]=Završeno ripovanje CDa
+Comment[ca]=Ha acabat l'extracció del CD
+Comment[cs]=Ripování CD dokončeno
+Comment[cy]=Wedi gorffen rhwygo'r CD
+Comment[da]=Cd færdig med at blive rippet
+Comment[de]=Die CD ist fertig ausgelesen.
+Comment[el]=Η εξαγωγή του CD ολοκληρώθηκε
+Comment[es]=Finalizó la extracción del CD
+Comment[et]=CD rippimine lõpetatud
+Comment[eu]=CD-aren erauztea amaituta
+Comment[fa]=دیسک فشرده به شکافتن پایان داد
+Comment[fi]=CD:n kaappaaminen valmis
+Comment[fr]=extraction du CD terminée
+Comment[gl]=Rematou a extracción do CD
+Comment[he]=קריעת תקליטור הסתיימה
+Comment[hu]=A CD kimásolása befejeződött
+Comment[is]=Afritun disks lauk
+Comment[it]=Estrazione CD terminata
+Comment[ja]=CD のリッピングが終了しました
+Comment[kk]=CD оқып алуы аяқталды
+Comment[km]=បាន​បញ្ចប់​ការ​ច្រៀក​ស៊ីឌី
+Comment[ko]=CD 추출 완료됨
+Comment[lt]=CD nurašymas baigtas
+Comment[mk]=Крај на спуштањето на CD
+Comment[nb]=CD ferdig innlest
+Comment[nds]=De CD wöör utleest
+Comment[ne]=CD कटाइ समाप्त भयो
+Comment[nl]=Klaar met rippen van cd
+Comment[nn]=CD ferdig innlesen
+Comment[pa]=CD ਰਪਿੰਗ ਖਤਮ
+Comment[pl]=Zakończono zgrywanie CD
+Comment[pt]=O CD foi extraído
+Comment[pt_BR]=Finalização da conversão do CD
+Comment[ru]=Копирование CD завершено
+Comment[sk]=Dokončené ripovanie CD
+Comment[sl]=Zajemanje CD-ja končano
+Comment[sr]=Гребовање CD-а је завршено
+Comment[sr@Latn]=Grebovanje CD-a je završeno
+Comment[sv]=Cd:n har lagrats klart
+Comment[ta]=CD பிரித்தல் முடிந்தது
+Comment[th]=ซีดีบันทึกเสียงที่ทำการดึงข้อมูลเสร็จแล้ว
+Comment[tr]=CD dönüştürme işlemi tamamlandı
+Comment[uk]=Видирання КД завершено
+Comment[zh_CN]=CD 抓取完成
+Comment[zh_HK]=已完成擷取的 CD
+Comment[zh_TW]=光碟已完成擷取
+
+[track encoded]
+Name=Track Encoded
+Name[bg]=Кодиране на пътечка
+Name[bn]=ট্র্যাক এনকোড করা হয়েছে
+Name[br]=Roudenn kodet
+Name[bs]=Numera kodirana
+Name[ca]=Codificador de peces
+Name[cs]=Stopa převedena
+Name[cy]=Trac wedi'i Amgodio
+Name[da]=Spor indkodet
+Name[de]=Stück kodiert
+Name[el]=Το κομμάτι κωδικοποιήθηκε
+Name[eo]=Trako kodigita
+Name[es]=Pista codificada
+Name[et]=Rada kodeeritud
+Name[eu]=Pista kodetuta
+Name[fa]=شیار کدبندی شد
+Name[fi]=Kappale koodattu
+Name[fr]=Piste encodée
+Name[ga]=Rian Ionchódaithe
+Name[gl]=Pista Codificada
+Name[he]=רצועות קודדו
+Name[hu]=Hangsáv kódolása kész
+Name[is]=Lag kóðað
+Name[it]=Traccia codificata
+Name[ja]=トラックのエンコード終了
+Name[kk]=Жолсызық қалыптастырылды
+Name[km]=បាន​អ៊ិនកូដ​បទ
+Name[ko]=트랙 인코딩됨
+Name[lt]=Takelis įkoduotas
+Name[mk]=Кодирање на песната
+Name[nb]=Spor koda
+Name[nds]=Stück kodeert
+Name[ne]=ट्रयाक सङ्केतन गरियो
+Name[nl]=Track gecodeerd
+Name[nn]=Spor koda
+Name[pa]=ਟਰੈਕ ਇੰਕੋਡਿੰਡ
+Name[pl]=Utwór zakodowany
+Name[pt]=Faixa Codificada
+Name[pt_BR]=Faixa codificada
+Name[ro]=Pistă codată
+Name[ru]=Дорожка преобразована
+Name[sk]=Ripovanie stopy dokončené
+Name[sl]=Skladba kodirana
+Name[sr]=Нумера је искодирана
+Name[sr@Latn]=Numera je iskodirana
+Name[sv]=Spår kodat
+Name[ta]=தடம் மறையாக்கப்பட்டது
+Name[tg]=Шиор Рамзӣ кунонида шудааст
+Name[th]=เพลงที่เข้ารหัสแล้ว
+Name[tr]=Parça Dönüştürücü
+Name[uk]=Закодована доріжка
+Name[zh_CN]=音轨已编码
+Name[zh_HK]=已編碼的音軌
+Name[zh_TW]=音軌已編碼
+Comment=Track finished encoding
+Comment[bg]=Завършване на кодиране на пътечка
+Comment[bn]=ট্র্যাক এনকোডিং করা শেষ
+Comment[bs]=Završeno kodiranje numere
+Comment[ca]=Ha acabat la codificació de la peça
+Comment[cs]=Převod stopy dokončen
+Comment[cy]=Wedi gorffen amgodio'r trac
+Comment[da]=Spor færdig med at blive indkodet
+Comment[de]=Das Stück ist fertig kodiert.
+Comment[el]=Τέλος κωδικοποίησης κομματιού
+Comment[eo]=Kodigo de trako finiĝis
+Comment[es]=Finalizó la codificación de la pista
+Comment[et]=Raja kodeerimine lõpetatud
+Comment[eu]=Pistaren kodetzea amaituta
+Comment[fa]=شیار به کدبندی پایان داد
+Comment[fi]=Kappaleen koodaaminen valmis
+Comment[fr]=Encodage de la piste terminé
+Comment[gl]=Rematou a codificación da pista
+Comment[he]=קידוד רצועה הסתיים
+Comment[hu]=A hangsáv kódolása befejeződött
+Comment[is]=Kóðun lags lauk
+Comment[it]=Codifica traccia terminata
+Comment[ja]=トラックのエンコードが終了しました
+Comment[kk]=Жолсызықты қалыптастыру аяқталды
+Comment[km]=បាន​បញ្ចប់​ការ​អ៊ិនកូដ​បិទ
+Comment[ko]=트랙 인코딩 완료됨
+Comment[lt]=Takelio įkodavimas baigtas
+Comment[mk]=Заврши кодирањето на песната
+Comment[nb]=Spor ferdig koda
+Comment[nds]=Dat Stück wöör kodeert
+Comment[ne]=ट्रयाक सङ्केतन समाप्त भयो
+Comment[nl]=Klaar met coderen van track
+Comment[nn]=Spor ferdig koda
+Comment[pa]=ਟਰੈਕ ਮੁਕੰਮਲ ਇੰਕੋਡਿੰਗ
+Comment[pl]=Zakończono kodowanie utworu
+Comment[pt]=A faixa foi codificada
+Comment[pt_BR]=Finalização da codificação da faixa
+Comment[ru]=Преобразование дорожки завершено
+Comment[sk]=Dokončené kódovanie stopy
+Comment[sl]=Kodiranje skladbe končano
+Comment[sr]=Кодирање нумере је завршено
+Comment[sr@Latn]=Kodiranje numere je završeno
+Comment[sv]=Spåret har kodats klart
+Comment[ta]=தடம் மறையாக்கத்தை முடித்தது
+Comment[th]=เพลงที่ทำการเข้ารหัสเสร็จแล้ว
+Comment[tr]=Parça kodlama bitirildi
+Comment[uk]=Кодування доріжки закінчене
+Comment[zh_CN]=音轨编码完成
+Comment[zh_HK]=已完成編碼的音軌
+Comment[zh_TW]=音軌已完成編碼
+
+[cd encoded]
+Name=CD Encoded
+Name[bg]=Кодиране на аудио диск
+Name[bn]=সিডি এনকোড করা হয়েছে
+Name[br]=CD kodet
+Name[bs]=CD kodiran
+Name[ca]=Codificador de CD
+Name[cs]=CD převedeno
+Name[cy]=CD wedi'i Amgodio
+Name[da]=Cd indkodet
+Name[de]=CD kodiert
+Name[el]=Το CD κωδικοποιήθηκε
+Name[es]=CD codificado
+Name[et]=CD kodeeritud
+Name[eu]=CD-a kodetuta
+Name[fa]=دیسک فشرده کدبندی شد
+Name[fi]=CD koodattu
+Name[fr]=CD encodé
+Name[ga]=CD Ionchódaithe
+Name[gl]=CD Codificado
+Name[he]=תקליטור קודד
+Name[hu]=CD kódolása kész
+Name[is]=Diskur kóðaður
+Name[it]=CD codificato
+Name[ja]=CD のエンコード終了
+Name[kk]=CD қалыптастырылды
+Name[km]=បាន​អ៊ិនកូដ​ស៊ីឌី
+Name[ko]=CD 인코딩됨
+Name[lt]=CD įkoduotas
+Name[mk]=Кодирано CD
+Name[nb]=CD koda
+Name[nds]=CD kodeert
+Name[ne]=CD सङ्केतन गरियो
+Name[nl]=CD gecodeerd
+Name[nn]=CD koda
+Name[pa]=CD ਇੰਕੋਡਿੰਡ
+Name[pl]=Zakodowane CD
+Name[pt]=CD Codificado
+Name[pt_BR]=CD Codificado
+Name[ro]=CD codat
+Name[ru]=Преобразование CD завершено
+Name[sk]=CD zakódované
+Name[sl]=CD kodiran
+Name[sr]=CD је искодиран
+Name[sr@Latn]=CD je iskodiran
+Name[sv]=Cd kodad
+Name[ta]=CD மறையாக்கப்பட்டது
+Name[tg]=Диски Фишурда Рамзӣ кунонида шудааст
+Name[th]=ซีดีที่ทำการเข้ารหัส
+Name[tr]=CD Dönüştürücü
+Name[uk]=Закодовано КД
+Name[zh_CN]=CD 已编码
+Name[zh_HK]=已編碼的 CD
+Name[zh_TW]=光碟已編碼
+Comment=CD finished encoding
+Comment[bg]=Завършване на кодиране на аудио диск
+Comment[bn]=সিডি এনকোডিং করা শেষ
+Comment[bs]=Završeno kodiranje CDa
+Comment[ca]=Ha acabat la codificació del CD
+Comment[cs]=Převod CD dokončen
+Comment[cy]=Wedi gorffen amgodio'r CD
+Comment[da]=Cd færdig med at blive indkodet
+Comment[de]=Die CD ist fertig kodiert.
+Comment[el]=Τέλος κωδικοποίησης CD
+Comment[es]=Finalizó la codificación del CD
+Comment[et]=CD kodeerimine lõpetatud
+Comment[eu]=CD-aren kodetzea amaituta
+Comment[fa]=دیسک فشرده به کدبندی پایان داد
+Comment[fi]=CD:n koodaaminen valmis
+Comment[fr]=Encodage du CD terminé
+Comment[gl]=Rematou a codificación do CD
+Comment[he]=קידוד תקליטור הסתיים
+Comment[hu]=A CD kódolása befejeződött
+Comment[is]=Kóðun disks lauk
+Comment[it]=Codifica CD terminata
+Comment[ja]=CD のエンコードが終了しました
+Comment[kk]=CD қалыптастыру аяқталды
+Comment[km]=បាន​បញ្ចប់​ការ​អ៊ិនកូដ​ស៊ីឌី
+Comment[ko]=CD 인코딩 완료됨
+Comment[lt]=CD įkodavimas baigtas
+Comment[mk]=Заврши кодирањето на CD-то
+Comment[nb]=CD ferdig koda
+Comment[nds]=De CD wöör kodeert
+Comment[ne]=CD सङ्केतन समाप्त भयो
+Comment[nl]=Klaar met coderen van cd
+Comment[nn]=CD ferdig koda
+Comment[pa]=CD ਮੁਕਮੰਲ ਇੰਕੋਡਿੰਗ
+Comment[pl]=Zakończono kodowanie CD
+Comment[pt]=O CD foi codificado
+Comment[pt_BR]=Finalização da codificação do CD
+Comment[ru]=Преобразование диска завершено
+Comment[sk]=Dokončené kódovanie CD
+Comment[sl]=Kodiranje CD-ja končano
+Comment[sr]=Кодирање CD-а је завршено
+Comment[sr@Latn]=Kodiranje CD-a je završeno
+Comment[sv]=Cd:n har kodats klart
+Comment[ta]=CD மறையாக்கத்தை முடித்தது.
+Comment[th]=ซีดีที่ทำการเข้ารหัสเสร็จแล้ว
+Comment[tr]=CD çözümleyici sonlandırıldı
+Comment[uk]=Кодування КД закінчено
+Comment[zh_CN]=CD 编码完成
+Comment[zh_HK]=已完成編碼的 CD
+Comment[zh_TW]=光碟已完成編碼
+default_presentation=17
+default_sound=KDE_Beep_ClockChime.wav
diff --git a/kaudiocreator/general.ui b/kaudiocreator/general.ui
new file mode 100644
index 00000000..f20fea5b
--- /dev/null
+++ b/kaudiocreator/general.ui
@@ -0,0 +1,285 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>General</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>General</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>442</width>
+ <height>473</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>General</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_removeCompletedJobs</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Automatically remove jobs when finished</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_promptIfIncompleteInfo</cstring>
+ </property>
+ <property name="text">
+ <string>Prompt if information is not complete</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>File Regular Expression Replacement</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Selection:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Regular expression used on all file names. For example using selection " " and replace with "_" would replace all the spaces with underlines.
+</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>inputlabel</cstring>
+ </property>
+ <property name="text">
+ <string>Input:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>outputLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Output:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>exampleLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Example</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>exampleOutput</cstring>
+ </property>
+ <property name="text">
+ <string>Cool artist - example audio file.wav</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="5" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>example</cstring>
+ </property>
+ <property name="text">
+ <string>Cool artist - example audio file.wav</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_replaceInput</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="2">
+ <property name="name">
+ <cstring>kcfg_replaceOutput</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Replace with:</string>
+ </property>
+ </widget>
+ <widget class="Line" row="3" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>line1</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>kcfg_seperateMultiArtist</cstring>
+ </property>
+ <property name="title">
+ <string>Automatically Separate Multi-Artist CDDB Entries</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup2</cstring>
+ </property>
+ <property name="title">
+ <string>Format</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>kcfg_format_titleArtist</cstring>
+ </property>
+ <property name="text">
+ <string>Title - Artist</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>kcfg_format_artistTitle</cstring>
+ </property>
+ <property name="text">
+ <string>Artist - Title</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>delimiterLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Delimiter:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>genArtistLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Generic artist:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>kcfg_delimiter</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>kcfg_genericArtist</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>kcfg_replaceInput</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>General</receiver>
+ <slot>updateExample()</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_replaceOutput</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>General</receiver>
+ <slot>updateExample()</slot>
+ </connection>
+ <connection>
+ <sender>example</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>General</receiver>
+ <slot>updateExample()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>kcfg_replaceInput</tabstop>
+ <tabstop>kcfg_replaceOutput</tabstop>
+ <tabstop>example</tabstop>
+ <tabstop>kcfg_removeCompletedJobs</tabstop>
+</tabstops>
+<includes>
+ <include location="local" impldecl="in implementation">general.ui.h</include>
+</includes>
+<slots>
+ <slot access="protected" specifier="non virtual">updateExample()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kaudiocreator/general.ui.h b/kaudiocreator/general.ui.h
new file mode 100644
index 00000000..c54af842
--- /dev/null
+++ b/kaudiocreator/general.ui.h
@@ -0,0 +1,19 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+
+#include <qregexp.h>
+#include <qlineedit.h>
+#include <qlabel.h>
+
+void General::updateExample()
+{
+ QString text = example->text();
+ text.replace( QRegExp(kcfg_replaceInput->text()), kcfg_replaceOutput->text() );
+ exampleOutput->setText(text);
+}
diff --git a/kaudiocreator/hi16-app-kaudiocreator.png b/kaudiocreator/hi16-app-kaudiocreator.png
new file mode 100644
index 00000000..8b65f1b1
--- /dev/null
+++ b/kaudiocreator/hi16-app-kaudiocreator.png
Binary files differ
diff --git a/kaudiocreator/hi32-app-kaudiocreator.png b/kaudiocreator/hi32-app-kaudiocreator.png
new file mode 100644
index 00000000..ed12f257
--- /dev/null
+++ b/kaudiocreator/hi32-app-kaudiocreator.png
Binary files differ
diff --git a/kaudiocreator/icons/Makefile.am b/kaudiocreator/icons/Makefile.am
new file mode 100644
index 00000000..8ac79054
--- /dev/null
+++ b/kaudiocreator/icons/Makefile.am
@@ -0,0 +1,4 @@
+
+icondir = $(kde_datadir)/kaudiocreator/pics
+icon_DATA = check.png
+
diff --git a/kaudiocreator/icons/check.png b/kaudiocreator/icons/check.png
new file mode 100644
index 00000000..2890a7a4
--- /dev/null
+++ b/kaudiocreator/icons/check.png
Binary files differ
diff --git a/kaudiocreator/infodialog.ui b/kaudiocreator/infodialog.ui
new file mode 100644
index 00000000..a488e3dc
--- /dev/null
+++ b/kaudiocreator/infodialog.ui
@@ -0,0 +1,297 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>AlbumEditor</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>AlbumEditor</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>423</width>
+ <height>365</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Album Editor</string>
+ </property>
+ <property name="sizeGripEnabled">
+ <bool>true</bool>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>Current Track</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="2" column="0" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Comment:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>track_comment</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>trackLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Title:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="hAlign" stdset="0">
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>track_title</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Artist:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>track_artist</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Album</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>artist</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Artist:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="hAlign" stdset="0">
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>Album:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="hAlign" stdset="0">
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>album</cstring>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="3" column="1">
+ <property name="name">
+ <cstring>year</cstring>
+ </property>
+ <property name="maxValue">
+ <number>99999</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel7</cstring>
+ </property>
+ <property name="text">
+ <string>Year:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="hAlign" stdset="0">
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Genre:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="hAlign" stdset="0">
+ </property>
+ </widget>
+ <widget class="QComboBox" row="4" column="1">
+ <property name="name">
+ <cstring>genre</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Comment:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="5" column="1">
+ <property name="name">
+ <cstring>comment</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>100</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonPrevious</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Previous Track</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonNext</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Next Track</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonOk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;OK</string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonCancel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Cancel</string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>buttonOk</sender>
+ <signal>clicked()</signal>
+ <receiver>AlbumEditor</receiver>
+ <slot>accept()</slot>
+ </connection>
+ <connection>
+ <sender>buttonCancel</sender>
+ <signal>clicked()</signal>
+ <receiver>AlbumEditor</receiver>
+ <slot>reject()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>track_artist</tabstop>
+ <tabstop>track_title</tabstop>
+ <tabstop>track_comment</tabstop>
+ <tabstop>artist</tabstop>
+ <tabstop>album</tabstop>
+ <tabstop>year</tabstop>
+ <tabstop>genre</tabstop>
+ <tabstop>comment</tabstop>
+ <tabstop>buttonPrevious</tabstop>
+ <tabstop>buttonNext</tabstop>
+ <tabstop>buttonOk</tabstop>
+ <tabstop>buttonCancel</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kaudiocreator/job.cpp b/kaudiocreator/job.cpp
new file mode 100644
index 00000000..159b6ebb
--- /dev/null
+++ b/kaudiocreator/job.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ **/
+
+#include "job.h"
+#include <kmacroexpander.h>
+#include <qregexp.h>
+
+/**
+ * A helper function to replace %X with the stuff in the album.
+ * if quote is true then put "" around the %X
+ */
+QString Job::replaceSpecialChars(const QString &string, bool quote, QMap<QString, QString> _map){
+ QMap<QString,QString> map = _map;
+
+ map.insert("title", track_title);
+ map.insert("artist", track_artist);
+ map.insert("number", QString().sprintf("%02d", track));
+ map.insert("comment", track_comment);
+ map.insert("year", QString::number(year));
+ map.insert("genre", genre);
+
+ map.insert("albumtitle", album);
+ map.insert("albumcomment", comment);
+ map.insert("albumartist", group);
+
+ if (quote)
+ return (KMacroExpander::expandMacrosShellQuote(string, map));
+ else
+ return (KMacroExpander::expandMacros(string, map));
+}
+
+void Job::fix(const QString &in, const QString &out){
+ track_title.replace( QRegExp(in), out );
+ track_artist.replace( QRegExp(in), out );
+ track_comment.replace( QRegExp(in), out );
+ // year
+ // track
+ genre.replace( QRegExp(in), out );
+ album.replace( QRegExp(in), out );
+ comment.replace( QRegExp(in), out );
+ group.replace( QRegExp(in), out );
+}
+
diff --git a/kaudiocreator/job.h b/kaudiocreator/job.h
new file mode 100644
index 00000000..e0a4d577
--- /dev/null
+++ b/kaudiocreator/job.h
@@ -0,0 +1,78 @@
+/**
+ * This file is part of the KAudioCreator package
+ * Copyright (C) 2003 Benjamin C Meyer (ben+kaudiocreator at meyerhome dot net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef JOB_H
+#define JOB_H
+
+#include <qmap.h>
+#include <klocale.h>
+
+/**
+ * The job class is what is passed around the system. All of the data about
+ * the file being ripped and encoded is stored in here.
+ */
+class Job{
+
+public:
+ inline Job():id(-1),track_title(""),track_artist(""), track(-1),track_comment(""), year(-1), genre(i18n("Other")), group(""), album(""), comment(""), lastSongInAlbum(false), removeTempFile(true), encoder(-1) {};
+
+ QString replaceSpecialChars(const QString &string, bool quote, QMap<QString,QString> map);
+
+ void fix( const QString &in, const QString &out );
+
+ // The device to obtain the file such as /dev/cdrom/ (Used when ripping and ejecting)
+ QString device;
+ int id;
+
+ QString track_title;
+ QString track_artist;
+ int track;
+ QString track_comment;
+ int year;
+ QString genre;
+
+ QString group;
+ QString album;
+ QString comment;
+
+ // Currently location of file X
+ QString location;
+
+ // New location of a file after current opertation is complete (rip/encode).
+ QString newLocation;
+
+ // What was just attempted to do via this job and is spit out in the event
+ // of an error.
+ QString errorString;
+
+ // If this is the last track to be ripped then value is true.
+ bool lastSongInAlbum;
+
+ // If the file should be removed when finished encoding
+ bool removeTempFile;
+
+ // output from the processing.
+ QString output;
+
+ int encoder;
+};
+
+#endif // JOB_H
+
diff --git a/kaudiocreator/jobque.ui b/kaudiocreator/jobque.ui
new file mode 100644
index 00000000..137ed6df
--- /dev/null
+++ b/kaudiocreator/jobque.ui
@@ -0,0 +1,132 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>JobQue</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>JobQue</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>629</width>
+ <height>513</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QPushButton" row="2" column="2">
+ <property name="name">
+ <cstring>removeAll</cstring>
+ </property>
+ <property name="text">
+ <string>Remove All Jobs</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="3">
+ <property name="name">
+ <cstring>removeSelected</cstring>
+ </property>
+ <property name="text">
+ <string>Remove Selected Jobs</string>
+ </property>
+ </widget>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>Spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QListView" row="1" column="0" rowspan="1" colspan="4">
+ <column>
+ <property name="text">
+ <string>Job</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Progress</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Description</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>todoQue</cstring>
+ </property>
+ <property name="selectionMode">
+ <enum>Multi</enum>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="resizeMode">
+ <enum>LastColumn</enum>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>queLabel</cstring>
+ </property>
+ <property name="text">
+ <string>No jobs are in the queue</string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ <property name="hAlign" stdset="0">
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="1">
+ <property name="name">
+ <cstring>removeDoneJobs</cstring>
+ </property>
+ <property name="text">
+ <string>Remove Completed Jobs</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>todoQue</tabstop>
+ <tabstop>removeDoneJobs</tabstop>
+ <tabstop>removeAll</tabstop>
+ <tabstop>removeSelected</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kaudiocreator/jobqueimp.cpp b/kaudiocreator/jobqueimp.cpp
new file mode 100644
index 00000000..e9d8fa5a
--- /dev/null
+++ b/kaudiocreator/jobqueimp.cpp
@@ -0,0 +1,310 @@
+/**
+ * This file is part of the KAudioCreator package
+ * Copyright (C) 2003 Benjamin C Meyer (ben+kaudiocreator at meyerhome dot net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "jobqueimp.h"
+#include "job.h"
+#include "prefs.h"
+#include <qpushbutton.h>
+#include <qlabel.h>
+#include <qpainter.h>
+#include <kconfig.h>
+#include <kglobal.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <kmessagebox.h>
+#include <kurl.h>
+#include <qfile.h>
+#include <qregexp.h>
+#include <qfileinfo.h>
+#include <kstandarddirs.h>
+#include <knotifyclient.h>
+#include <qdir.h>
+
+#define HEADER_JOB 0
+#define HEADER_PROGRESS 1
+#define HEADER_DESCRIPTION 2
+#define ICON_LOC HEADER_DESCRIPTION
+
+#define DEFAULT_HIGHEST_NUMBER 9
+
+/**
+ * Constructor, set up signals.
+ * @param parent - parent widget
+ * @param name - widget name
+ */
+JobQueImp::JobQueImp( QWidget* parent, const char* name) :
+ JobQue(parent,name),highestNumber(DEFAULT_HIGHEST_NUMBER), currentId(0){
+ connect(removeSelected,SIGNAL(clicked()), this, SLOT( removeSelectedJob()));
+ connect(removeAll, SIGNAL(clicked()), this, SLOT(removeAllJobs()));
+ connect(removeDoneJobs, SIGNAL(clicked()), this, SLOT(clearDoneJobs()));
+}
+
+/**
+ * Return a buffer of "000" so that new, updated jobs strings will be able to
+ * sort via the columns.
+ * Based upon a highest number that is kept.
+ * @param number the number to fill out.
+ */
+QString JobQueImp::getStringFromNumber(int number){
+ if(number > highestNumber){
+ int diff = QString("%1").arg(number).length() - QString("%1").arg(highestNumber).length();
+ highestNumber = number;
+ if(diff > 0){
+ // We have to update all of the cells.
+ QueListViewItem * currentItem = (QueListViewItem*)todoQue->firstChild();
+ while( currentItem != NULL ){
+ currentItem->setText(HEADER_JOB, "0" + currentItem->text(HEADER_JOB));
+ currentItem = (QueListViewItem*)currentItem->itemBelow();
+ }
+ }
+ }
+
+ QString buffer = "";
+ uint newLength = QString("%1").arg(highestNumber).length() - QString("%1").arg(number).length();
+ for(uint i=0; i < newLength; i++)
+ buffer += "0";
+ return buffer;
+}
+
+/**
+ * Add a new job to the que
+ * @param id the id of the job.
+ * @param name the name of the job.
+ */
+void JobQueImp::addJob(Job*job, const QString &name ){
+ if(!job)
+ return;
+ job->id = ++currentId;
+ QueListViewItem *currentItem = new QueListViewItem(todoQue, QString("%1%2").arg(getStringFromNumber(currentId)).arg(currentId), "0", name);
+ currentItem->setPixmap(ICON_LOC, SmallIcon("player_pause", currentItem->height()-2));
+ queLabel->setText(i18n("Number of jobs in the queue: %1").arg(todoQue->childCount()));
+}
+
+/**
+ * Locate the job and update the progress.
+ * @param id the id of the job to update
+ * @param progress the new progress of the job.
+ */
+void JobQueImp::updateProgress(int id, int progress){
+ int currentJobCount = numberOfJobsNotFinished();
+ QueListViewItem * currentItem = (QueListViewItem*)todoQue->firstChild();
+ QString buffer = getStringFromNumber(id);
+ buffer += QString("%1").arg(id);
+
+ // Find the current item
+ while( currentItem != NULL ){
+ if(currentItem->text(HEADER_JOB) == buffer)
+ break;
+ currentItem = (QueListViewItem*)currentItem->nextSibling();
+ }
+ if( !currentItem ){
+ kdDebug(60002) << "JobQueImp::updateProgress An update was received about a job, but the job couldn't be found: " << id << endl;
+ return;
+ }
+
+ // Only update the % if it changed.
+ if(currentItem->percentDone == progress)
+ return;
+
+ currentItem->percentDone = progress;
+ currentItem->repaint();
+
+ // Update the icon if needed
+ if(progress > 0 && progress < 100 && !currentItem->progressing ){
+ currentItem->setPixmap(ICON_LOC, SmallIcon("gear", currentItem->height()-2));
+ currentItem->progressing = true;
+ }
+ else if(progress == -1){
+ currentItem->setPixmap(ICON_LOC, SmallIcon("button_cancel", currentItem->height()-2));
+ }
+ else if(progress == 100){
+ // Remove the job if requested.
+ if(Prefs::removeCompletedJobs()){
+ removeJob(currentItem, false);
+ return;
+ }
+ currentItem->setPixmap(ICON_LOC, SmallIcon("button_ok", currentItem->height()));
+ }
+ if(currentJobCount > 0 && numberOfJobsNotFinished() == 0)
+ KNotifyClient::event("no jobs left");
+}
+
+/**
+ * Remove job listed in item
+ * @param item to remove. Note that it WILL be deleted and set to NULL.
+ * @param kill kill the actual job/process. A bool here because this CAN cause
+ * a race condition when the encoder is 100%, but hasn't exited.
+ * @param prompt the user if the job isn't finished
+ * @return bool if remove was successfull or not.
+ */
+bool JobQueImp::removeJob(QueListViewItem *item, bool kill, bool prompt){
+ if(!item)
+ return false;
+
+ if(item->percentDone < 100 && item->percentDone > -1 && (prompt && KMessageBox::questionYesNo(this, i18n("KAudioCreator has not finished %1. Remove anyway?").arg(item->text(HEADER_DESCRIPTION)), i18n("Unfinished Job in Queue"), KStdGuiItem::del(), i18n("Keep"))
+ == KMessageBox::No ))
+ return false;
+
+ // "Thread" safe
+ if(!item)
+ return false;
+
+ if(kill)
+ emit (removeJob(item->text(HEADER_JOB).toInt()));
+
+ todoQue->takeItem(item);
+ delete(item);
+ item = NULL;
+
+ // See if the Que needs to be updated...
+ if(todoQue->childCount() == 0){
+ queLabel->setText(i18n("No jobs are in the queue"));
+ highestNumber = DEFAULT_HIGHEST_NUMBER;
+ currentId = 0;
+ }
+ else
+ queLabel->setText(i18n("Number of jobs in the queue: %1").arg(todoQue->childCount()));
+ return true;
+}
+
+/**
+ * Remove the currently selected Job
+ */
+void JobQueImp::removeSelectedJob(){
+ QueListViewItem * currentItem = (QueListViewItem*)todoQue->firstChild();
+ while(currentItem != NULL){
+ if(currentItem->isSelected()){
+ QueListViewItem *t = currentItem;
+ currentItem = (QueListViewItem*)currentItem->nextSibling();
+ removeJob(t);
+ }
+ else
+ currentItem = (QueListViewItem*)currentItem->nextSibling();
+ }
+}
+
+/**
+ * Remove all of the jobs in the list.
+ */
+void JobQueImp::removeAllJobs(){
+ // First determine if there are jobs not finished and prompt once here
+ bool finished=true;
+ QueListViewItem * item = (QueListViewItem*)todoQue->firstChild();
+ while( item != NULL ){
+ if(item && item->percentDone < 100 && item->percentDone > -1)
+ finished = false;
+ item = (QueListViewItem*)item->nextSibling();
+ }
+ if(!finished){
+ if(KMessageBox::questionYesNo(this, i18n("KAudioCreator has not finished all of the jobs. Remove them anyway?"), i18n("Unfinished Job in Queue"), KStdGuiItem::del(), i18n("Keep"))
+ == KMessageBox::No )
+ return;
+ }
+
+ QueListViewItem * currentItem = (QueListViewItem*)todoQue->firstChild();
+ while( currentItem != NULL ){
+ QueListViewItem *next = (QueListViewItem*)currentItem->nextSibling();
+ removeJob(currentItem, true, false);
+ currentItem = next;
+ }
+}
+
+/**
+ * Remove any jobs that are in the list that are done.
+ */
+void JobQueImp::clearDoneJobs(){
+ QueListViewItem * currentItem = (QueListViewItem*)todoQue->firstChild();
+ while( currentItem != NULL ){
+ QueListViewItem *itemToRemove = NULL;
+ if( currentItem->percentDone == 100 ){
+ itemToRemove = currentItem;
+ }
+ currentItem = (QueListViewItem*)currentItem->itemBelow();
+ if(itemToRemove){
+ emit (removeJob(itemToRemove->text(HEADER_JOB).toInt()));
+ todoQue->takeItem(itemToRemove);
+ }
+ }
+ if(todoQue->childCount() == 0){
+ queLabel->setText(i18n("No jobs are in the queue"));
+ highestNumber = DEFAULT_HIGHEST_NUMBER;
+ currentId = 0;
+ }
+ else
+ queLabel->setText(i18n("Number of jobs in the queue: %1").arg(todoQue->childCount()));
+}
+
+/**
+ * Return the number of jobs in the que that don't have 100% or Error in the
+ * Progress column
+ * @return the number of jobs that are in the que that haven't been finished.
+ */
+int JobQueImp::numberOfJobsNotFinished(){
+ int totalJobsToDo = 0;
+ QueListViewItem * currentItem = (QueListViewItem*)todoQue->firstChild();
+ while( currentItem != NULL ){
+ if( currentItem->percentDone == 100 || currentItem->percentDone == -1 ){
+ }
+ else
+ totalJobsToDo++;
+ currentItem = (QueListViewItem*)currentItem->itemBelow();
+ }
+ return totalJobsToDo;
+}
+
+/**
+ * The repaint function overloaded so that we can have a built in progressbar.
+ */
+void QueListViewItem::paintCell (QPainter * p,const QColorGroup &cg,int column,
+ int width,int align){
+ if(column != HEADER_PROGRESS){
+ QListViewItem::paintCell(p,cg,column,width,align);
+ return;
+ }
+
+ p->setPen(cg.base());
+ p->drawRect(0,0,width,height());
+ if(isSelected())
+ p->fillRect(1,1,width-2,height()-2,cg.highlight());
+ else
+ p->fillRect(1,1,width-2,height()-2,cg.base());
+
+ int percent = (int)(((double)(width-2)) * (percentDone/100));
+
+ p->fillRect(1,1,percent,height()-2,cg.mid());
+
+ // show the text
+ p->setPen(cg.text());
+ if(isSelected())
+ p->setPen(cg.highlightedText());
+ if(percentDone != -1)
+ p->drawText(0,0,width-1,height()-1,AlignCenter,QString().setNum((int)percentDone) + "%");
+ else
+ p->drawText(0,0,width-1,height()-1,AlignCenter,i18n("Error"));
+}
+
+/**
+ * Header for built in treelist item so we can have a progress bar in them.
+ */
+QueListViewItem::QueListViewItem(QListView *parent, const QString id, const QString p , const QString name, const QString d, const QString e) : QListViewItem(parent, id, p, name,d,e), percentDone(0), progressing(false) {
+}
+
+#include "jobqueimp.moc"
+
diff --git a/kaudiocreator/jobqueimp.h b/kaudiocreator/jobqueimp.h
new file mode 100644
index 00000000..a797f22c
--- /dev/null
+++ b/kaudiocreator/jobqueimp.h
@@ -0,0 +1,80 @@
+/**
+ * This file is part of the KAudioCreator package
+ * Copyright (C) 2003 Benjamin C Meyer (ben+kaudiocreator at meyerhome dot net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef JOBQUEIMP_H
+#define JOBQUEIMP_H
+
+#include "jobque.h"
+#include <qstring.h>
+#include <qlistview.h>
+
+class Job;
+class QPainter;
+class QColorGroup;
+class QListView;
+
+/**
+ * Helper class to allow for progress bars in list view items.
+ */
+class QueListViewItem : public QListViewItem {
+
+public:
+ QueListViewItem (QListView * p = NULL, const QString a=0, const QString b=0, const QString c=0, const QString d=0, const QString e=0);
+ virtual void paintCell (QPainter * p,const QColorGroup &cg,int column,
+ int width,int align);
+ double percentDone;
+ // Has the percentDone gone beyond 0
+ // Here because percentDone might go 1,2,3,4 or it could go 1,20,21,78 or ...
+ bool progressing;
+};
+
+
+class JobQueImp : public JobQue {
+
+Q_OBJECT
+
+signals:
+ void removeJob( int idNumber );
+
+public:
+ JobQueImp( QWidget* parent = 0, const char* name = 0 );
+ int numberOfJobsNotFinished();
+
+public slots:
+ void updateProgress( int id, int progress );
+ void addJob( Job* job, const QString &name );
+
+ // Toolbar Button
+ void clearDoneJobs();
+
+private slots:
+ void removeSelectedJob();
+ void removeAllJobs();
+
+private:
+ bool removeJob( QueListViewItem *item, bool kill=true, bool prompt=true );
+ QString getStringFromNumber( int number );
+ int highestNumber;
+
+ int currentId;
+};
+
+#endif
+
diff --git a/kaudiocreator/kaudiocreator-libkcddb.upd b/kaudiocreator/kaudiocreator-libkcddb.upd
new file mode 100644
index 00000000..f878b1f6
--- /dev/null
+++ b/kaudiocreator/kaudiocreator-libkcddb.upd
@@ -0,0 +1,6 @@
+Id=kaudiocreator-libkcddb
+
+File=kaudiocreatorrc,kcmcddbrc
+Group=cdconfig,Lookup
+Key=databasePort,port
+Key=databaseServer,hostname
diff --git a/kaudiocreator/kaudiocreator-meta.upd b/kaudiocreator/kaudiocreator-meta.upd
new file mode 100644
index 00000000..223f6e62
--- /dev/null
+++ b/kaudiocreator/kaudiocreator-meta.upd
@@ -0,0 +1,3 @@
+Id=3
+File=kaudiocreatorrc
+Script=upgrade-kaudiocreator-metadata.sh,bash
diff --git a/kaudiocreator/kaudiocreator.cpp b/kaudiocreator/kaudiocreator.cpp
new file mode 100644
index 00000000..12267ed0
--- /dev/null
+++ b/kaudiocreator/kaudiocreator.cpp
@@ -0,0 +1,305 @@
+/**
+ * This file is part of the KAudioCreator package
+ * Copyright (C) 2003 Benjamin C Meyer (ben+kaudiocreator at meyerhome dot net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "kaudiocreator.h"
+
+#include <qvbox.h>
+#include <kiconloader.h>
+
+#include <kmessagebox.h>
+#include <kpopupmenu.h>
+#include <kaction.h>
+#include <kstatusbar.h>
+#include <knotifydialog.h>
+#include <kcombobox.h>
+
+#include "tracksimp.h"
+#include "jobqueimp.h"
+#include "ripper.h"
+#include "encoder.h"
+#include "prefs.h"
+#include "encodefileimp.h"
+#include "job.h"
+
+// Settings
+#include "ripconfig.h"
+#include "cdconfig.h"
+#include "encoderconfigimp.h"
+#include "general.h"
+#include <kcmoduleloader.h>
+#include <kurlrequester.h>
+
+/**
+ * Constructor. Connect all of the object and the job control.
+ */
+KAudioCreator::KAudioCreator( QWidget* parent, const char* name) :
+ KMainWindow(parent, name)
+{
+ janusWidget = new KJanusWidget(this, name, KJanusWidget::Tabbed);
+ setCentralWidget(janusWidget);
+
+ QVBox *frame = janusWidget->addVBoxPage(i18n("&CD Tracks"), QString::null, SmallIcon("cdaudio_unmount", 32));
+ tracks = new TracksImp(frame, "Tracks");
+
+ ripper = new Ripper ( frame, "Rip" );
+ encoder = new Encoder( frame, "Encoder" );
+
+ frame = janusWidget->addVBoxPage( i18n("&Jobs"), QString::null, SmallIcon( "run", 32 ) );
+ jobQue = new JobQueImp( frame, "Que" );
+
+ resize(500, 440);
+
+ /*KAction *eject = */new KAction( i18n("&Eject CD"), 0, tracks,
+ SLOT( eject() ), actionCollection(), "eject" );
+
+ (void)new KAction( i18n("&Configure KAudioCreator..."), 0, this,
+ SLOT( showSettings() ), actionCollection(), "configure_kaudiocreator" );
+
+ KAction *selectAll = new KAction( i18n( "Select &All Tracks"), 0, tracks,
+ SLOT( selectAllTracks() ), actionCollection(), "select_all" ) ;
+ KAction *deselectAll = new KAction( i18n( "Deselect &All Tracks"), 0, tracks,
+ SLOT( deselectAllTracks() ), actionCollection(), "deselect_all" );
+ selectAll->setEnabled( false );
+ deselectAll->setEnabled( false );
+
+ KActionMenu *actActionMenu = new KActionMenu( i18n("Rip &Selection"), "rip", actionCollection(), "rip" );
+ actActionMenu->setDelayed(true); //needed for checking "all accounts"
+ actActionMenu->setEnabled( false );
+ connect( actActionMenu, SIGNAL( activated() ), tracks, SLOT( startSession() ) );
+
+ ripMenu = actActionMenu->popupMenu();
+ connect( ripMenu, SIGNAL( activated(int) ), this, SLOT( slotRipSelection(int)) );
+ connect( ripMenu, SIGNAL( aboutToShow() ), this, SLOT( getRipMenu()) );
+
+ KAction *rip = new KAction( i18n( "Rip &Selection" ), 0, tracks,
+ SLOT( startSession() ), actionCollection(), "rip_selected" );
+ rip->setEnabled( false );
+
+ connect( jobQue, SIGNAL( removeJob(int) ), this, SLOT( updateStatus() ) );
+ connect( jobQue, SIGNAL( removeJob(int) ), ripper, SLOT( removeJob(int) ) );
+ connect( jobQue, SIGNAL( removeJob(int) ), encoder, SLOT( removeJob(int)) );
+
+ connect( ripper, SIGNAL( updateProgress(int, int) ) , jobQue, SLOT( updateProgress(int,int) ) );
+ connect( ripper, SIGNAL( addJob(Job*, const QString &) ), jobQue, SLOT( addJob(Job*, const QString &)) );
+ connect( ripper, SIGNAL( eject(const QString &) ) , tracks, SLOT( ejectDevice(const QString &)) );
+ connect( ripper, SIGNAL( encodeWav(Job *) ) , encoder, SLOT( encodeWav(Job *)) );
+ connect( ripper, SIGNAL( jobsChanged() ) , this, SLOT( updateStatus() ) );
+
+ connect( encoder, SIGNAL( updateProgress(int, int) ) , jobQue, SLOT( updateProgress(int,int)) );
+ connect( encoder, SIGNAL( addJob(Job*, const QString&)), jobQue, SLOT( addJob(Job*, const QString &)) );
+ connect( encoder, SIGNAL( jobsChanged() ) , this, SLOT( updateStatus() ) );
+
+ connect( tracks, SIGNAL( hasCD(bool) ) , this, SLOT( hasCD(bool) ) );
+ connect( tracks, SIGNAL( ripTrack(Job *) ), ripper, SLOT( ripTrack(Job *)) );
+ connect( tracks, SIGNAL( hasTracks(bool) ), rip, SLOT( setEnabled(bool)) );
+ connect( tracks, SIGNAL( hasTracks(bool) ), actActionMenu, SLOT( setEnabled(bool)) );
+ connect( tracks, SIGNAL( hasTracks(bool) ), deselectAll, SLOT( setEnabled(bool)) );
+ connect( tracks, SIGNAL( hasTracks(bool) ), selectAll, SLOT( setEnabled(bool)) );
+
+ (void)new KAction(i18n("Remove &Completed Jobs"), 0, jobQue,
+ SLOT(clearDoneJobs()), actionCollection(), "clear_done_jobs" );
+
+ KAction *edit = new KAction(i18n("&Edit Album..."), 0, tracks,
+ SLOT(editInformation()), actionCollection(), "edit_cd");
+ connect(tracks, SIGNAL(hasCD(bool)), edit, SLOT(setEnabled(bool)));
+ edit->setEnabled( false );
+
+ (void)new KAction(i18n("Encode &File..."), 0, this,
+ SLOT(encodeFile()), actionCollection(), "encode_file");
+
+ KAction *cddb = new KAction(i18n("&CDDB Lookup"), 0, tracks,
+ SLOT(performCDDB()), actionCollection(), "cddb_now");
+ connect(tracks, SIGNAL(hasCD(bool)), cddb, SLOT(setEnabled(bool)));
+ cddb->setEnabled( false );
+
+ KStdAction::configureNotifications(this, SLOT(configureNotifications()),
+ actionCollection());
+ KStdAction::quit( this, SLOT(close()), actionCollection(), "quit" );
+
+ // Init statusbar
+ statusBar()->insertItem(i18n("No Audio CD detected"), 0 );
+ hasCD(tracks->hasCD());
+
+ setupGUI();
+}
+
+void KAudioCreator::setDevice( const QString &device )
+{
+ tracks->deviceCombo->setCurrentText( device );
+}
+
+void KAudioCreator::slotRipSelection(int selection){
+ tracks->startSession( selection );
+}
+
+void KAudioCreator::getRipMenu(){
+ ripMenu->clear();
+
+ int i=0;
+ QString currentGroup = QString("Encoder_%1").arg(i);
+ while(EncoderPrefs::hasPrefs(currentGroup)){
+ ripMenu->insertItem(EncoderPrefs::prefs(currentGroup)->encoderName(), i);
+ currentGroup = QString("Encoder_%1").arg(++i);
+ }
+}
+
+/**
+ * Changes the status bar to let the user no if a cd was not detected or inserted.
+ */
+void KAudioCreator::hasCD(bool cd){
+ if(cd)
+ statusBar()->changeItem(i18n("CD Inserted"), 0 );
+ else
+ statusBar()->changeItem(i18n("No Audio CD detected"), 0 );
+}
+
+void KAudioCreator::updateStatus() {
+ QString status = i18n("Idle.");
+ QString rippingStatus;
+ QString encodingStatus;
+ int activeRippingJobs = ripper->activeJobCount();
+ int pendingRippingJobs = ripper->pendingJobCount();
+ int activeEncodingJobs = encoder->activeJobCount();
+ int pendingEncodingJobs = encoder->pendingJobCount();
+
+ if ( activeRippingJobs ) {
+ rippingStatus = i18n("Ripping (%1 active, %2 queued)").arg( activeRippingJobs ).arg( pendingRippingJobs );
+ status = rippingStatus;
+ }
+ if ( activeEncodingJobs ) {
+ encodingStatus = i18n("Encoding (%1 active, %2 queued)").arg( activeEncodingJobs ).arg( pendingEncodingJobs );
+
+ if ( activeRippingJobs ) {
+ status.append(" : ");
+ status.append( encodingStatus );
+ }
+ else {
+ status = encodingStatus;
+ }
+ }
+
+ statusBar()->changeItem( status, 0 );
+}
+
+/**
+ * Ask the user if they really want to quit if there are open jobs.
+ */
+bool KAudioCreator::queryClose() {
+ if(jobQue->numberOfJobsNotFinished() > 0 &&
+ (KMessageBox::warningContinueCancel(this, i18n("There are unfinished jobs in the queue. Would you like to quit anyway?"), i18n("Unfinished Jobs in Queue"),KStdGuiItem::quit())
+ == KMessageBox::Cancel ))
+ return false;
+ return true;
+}
+
+void KAudioCreator::configureNotifications() {
+ KNotifyDialog *dialog = new KNotifyDialog(this, "KNotifyDialog", false);
+ dialog->show();
+}
+
+void KAudioCreator::encodeFile(){
+ EncodeFileImp *file = new EncodeFileImp(this, "EncodeFile");
+ connect(file, SIGNAL(startJob(Job*)),encoder, SLOT(encodeWav(Job*)));
+ file->show();
+}
+
+/**
+ * Show Settings dialog.
+ */
+void KAudioCreator::showSettings(){
+ if(KConfigDialog::showDialog("settings"))
+ return;
+
+ SettingsDialog *dialog = new SettingsDialog(this, "settings", Prefs::self());
+ connect(dialog, SIGNAL(settingsChanged()), ripper, SLOT(loadSettings()));
+ connect(dialog, SIGNAL(settingsChanged()), encoder, SLOT(loadSettings()));
+ connect(dialog, SIGNAL(settingsChanged()), tracks, SLOT(loadSettings()));
+ connect(dialog->encoderConfigImp, SIGNAL(encoderUpdated()), encoder, SLOT(loadSettings()));
+ dialog->show();
+}
+
+SettingsDialog::SettingsDialog(QWidget *parent, const char *name,KConfigSkeleton *config)
+ : KConfigDialog(parent, name, config),
+ cddb(0), cddbChanged(false)
+{
+ addPage(new General(0, "General"), i18n("General"), "package_settings",
+ i18n("General Configuration"));
+ addPage(new CdConfig(0, "CD"), i18n("CD"), "package_system",
+ i18n("CD Configuration"));
+
+ // Because WE don't segfault on our users...
+ KService::Ptr libkcddb = KService::serviceByDesktopName("libkcddb");
+ if (libkcddb && libkcddb->isValid())
+ {
+ cddb = KCModuleLoader::loadModule(QString("libkcddb"), KCModuleLoader::Inline);
+ if (cddb)
+ {
+ cddb->load();
+ addPage(cddb, i18n("CDDB"), "cdaudio_mount", i18n("CDDB Configuration"), false);
+ connect(cddb, SIGNAL(changed(bool)), this, SLOT(slotCddbChanged(bool)));
+ }
+ }
+ RipConfig *rip = new RipConfig(0, "Ripper");
+ rip->kcfg_tempDir->setMode(KFile::Directory);
+ addPage(rip, i18n("Ripper"), "gear", i18n("Ripper Configuration") );
+
+ encoderConfigImp = new EncoderConfigImp(0, "Encoder");
+ addPage(encoderConfigImp, i18n("Encoder"), "filter", i18n("Encoder Configuration") );
+}
+
+void SettingsDialog::updateSettings()
+{
+ if (cddb)
+ cddb->save();
+}
+
+void SettingsDialog::updateWidgets()
+{
+ if (cddb)
+ cddb->load();
+ cddbChanged = false;
+}
+
+void SettingsDialog::updateWidgetsDefault()
+{
+ if (cddb)
+ cddb->defaults();
+}
+
+bool SettingsDialog::hasChanged()
+{
+ return cddbChanged;
+}
+
+bool SettingsDialog::isDefault()
+{
+ if (cddb)
+ return false;
+ return true;
+}
+
+void SettingsDialog::slotCddbChanged(bool changed)
+{
+ cddbChanged = changed;
+ updateButtons();
+}
+
+#include "kaudiocreator.moc"
+
diff --git a/kaudiocreator/kaudiocreator.desktop b/kaudiocreator/kaudiocreator.desktop
new file mode 100644
index 00000000..585e4918
--- /dev/null
+++ b/kaudiocreator/kaudiocreator.desktop
@@ -0,0 +1,137 @@
+[Desktop Entry]
+Comment=Frontend for audio file creation
+Comment[af]=Voorprogram vir audio lêer skep
+Comment[bg]=Инструмент за кодиране на аудио файлове
+Comment[bn]=অডিও ফাইল তৈরি করার জন্য ফ্রন্ট-এন্ড
+Comment[bs]=Interfejs za pravljenje audio datoteka
+Comment[ca]=Interfície per la creació de fitxers àudio
+Comment[cs]=Rozhraní pro tvorbu zvukových souborů
+Comment[cy]=Blaen i greu ffeiliau sain
+Comment[da]=Grænseflade til at lave lydfiler
+Comment[de]=Oberfläche zur Erstellung von Audio-Dateien
+Comment[el]=Σύστημα υποστήριξης χρήστη για δημιουργία αρχείου ήχου
+Comment[eo]=Fasado por kreado de sonordosieroj
+Comment[es]=Una interfaz para la creación de archivos audio
+Comment[et]=Rippimisprogrammide kasutajaliides
+Comment[eu]=Audio fitxategiak sortzeko interfazea
+Comment[fa]=پایانه برای ایجاد پروندۀ صوتی
+Comment[fi]=Käyttöliittymä äänitiedostojen luontiin
+Comment[fr]=Une interface pour la création de fichiers sons
+Comment[gl]=Interface para a creación de ficheiros de son
+Comment[he]=ממשק ליצירת קבצי שמע
+Comment[hi]=ऑडियो फ़ाइल बनाने के लिए फ्रन्टएण्ड
+Comment[hr]=Sučelje za rađenje audio datoteka
+Comment[hu]=Grafikus előtétprogram hangfájlok létrehozásához
+Comment[is]=Andlit á tól sem búa til hljóðskrár
+Comment[it]=Interfaccia per la creazione dei file audio
+Comment[ja]=オーディオファイル作成のフロントエンド
+Comment[kk]=Аудиофайл құру интерфейсі
+Comment[km]=កម្មវិធី​សម្រាប់​បង្កើត​ឯកសារ​អូឌីយ៉ូ
+Comment[ko]=오디오 파일 생성 프론트엔드
+Comment[lt]=Išorinė audio bylų kūrimo programa
+Comment[lv]=Frontends audio failu veidošanai
+Comment[mk]=Алатка за правење на аудио датотеки
+Comment[ms]=Bahagian hadapan untuk penciptaan fail audio
+Comment[nb]=Program for å lage av lydfiler
+Comment[nds]=Böversiet för't Opstellen vun Klangdateien
+Comment[ne]=अडियो फाइल सर्जकका लागि सुरुआत
+Comment[nl]=Grafische schil voor aanmaken van geluidsbestanden
+Comment[nn]=Grensesnitt for oppretting av lydfiler
+Comment[pa]=ਆਡੀਓ ਫਾਇਲਾਂ ਬਣਾਉਣ ਲਈ ਮੁੱਖ
+Comment[pl]=Środowisko do tworzenia plików audio
+Comment[pt]=Uma interface para a criação de ficheiros de áudio
+Comment[pt_BR]=Uma interface para a criação de arquivos de áudio
+Comment[ro]=Interfaţă pentru crearea de fişiere audio
+Comment[ru]=Интерфейс для создания аудиофайлов
+Comment[se]=Lakta jietnafiillaid ráhkadeapmái
+Comment[sk]=Rozhranie pre tvorbu zvukových súborov
+Comment[sl]=Vmesnik za ustvarjanje zvočnih datotek
+Comment[sr]=Интерфејс за прављење аудио фајлова
+Comment[sr@Latn]=Interfejs za pravljenje audio fajlova
+Comment[sv]=Gränssnitt för att skapa ljudfiler
+Comment[ta]=கேட்பொலி கோப்பு உருவாக்குவதற்கான முன்னிலை
+Comment[tg]=Пешохир барои офарандаи файлҳои садо
+Comment[th]=ฟรอนต์เอนด์สำหรับการสร้างแฟ้มเสียง
+Comment[tr]=Ses dosyası oluşturmak için bir önyüz
+Comment[uk]=Зовнішній інтерфейс створення аудіофайлів
+Comment[ven]=Zwo vhewa phanda uitela nyito ya faela ya zwipfiwa
+Comment[zh_CN]=音频文件创建前端
+Comment[zh_HK]=製作音訊檔案的前端
+Comment[zh_TW]=製作聲音檔案的前端
+Comment[zu]=Isiqalo sesiphelo sokuzwakalayo kwefayela lokludala
+Name=KAudioCreator
+Name[bn]=কে-অডিও-ক্রিয়েটার
+Name[cy]=KCreuwrSain
+Name[eo]=Sonorkreilo
+Name[hi]=के-ऑडियो-क्रिएटर
+Name[mk]=KАудиоКреатор
+Name[ne]=केडीई अडियो सर्जक
+Name[pa]=ਕੇ-ਆਡੀਓ ਨਿਰਮਾਤਾ
+Name[pt_BR]=Criação de Áudio
+Name[sv]=Kaudiocreator
+Name[ta]=கேஒலிஉருவாக்கி
+Name[tg]=KОфарандаи Садо
+Name[tr]=Kaudiocreator
+Name[ven]=MuitiwazwamibvumoyaK
+Name[zh_TW]=KAudioCreator 聲音製作
+GenericName=CD Ripper
+GenericName[bg]=Кодиране на аудио дискове
+GenericName[bn]=সিডি রিপার
+GenericName[br]=Un eztenner CD
+GenericName[ca]=Extractor de CD
+GenericName[cs]=CD ripper
+GenericName[cy]=Rhwygydd CD
+GenericName[da]=Cd-Ripper
+GenericName[de]=CD-Ripper
+GenericName[el]=Εξαγωγέας CD
+GenericName[eo]=Ellegilo por muzikaj lumdiskoj
+GenericName[es]=Extractor de audio de CDs
+GenericName[et]=CD rippija
+GenericName[eu]=CD erauzlea
+GenericName[fa]=شکافندۀ دیسک فشرده
+GenericName[fi]=CD-kaappaaja
+GenericName[fr]=Extracteur de CD audio
+GenericName[gl]=Extractor de CD
+GenericName[he]=קורע תקליטורים
+GenericName[hi]=सीडी रिप्पर
+GenericName[hu]=CD-másoló
+GenericName[is]=CD afritunartól
+GenericName[it]=Estrattore di CD
+GenericName[ja]=CD リッパー
+GenericName[kk]=CD риппері
+GenericName[km]=កម្មវិធី​ច្រៀក​ស៊ីឌី
+GenericName[ko]=CD 추출기
+GenericName[lt]=CD grotuvas/nurašymo priemonė
+GenericName[mk]=Спуштач на CD
+GenericName[nb]=CD-opptaker
+GenericName[nds]=CD-Utleser
+GenericName[ne]=CD काट्ने
+GenericName[nl]=CD-ripper
+GenericName[nn]=CD-opptakar
+GenericName[pa]=CD ਰਿਪਰ
+GenericName[pl]=Zgrywanie CD
+GenericName[pt]=Extractor de CDs
+GenericName[pt_BR]=Reprodutor de CDs/Ripar CDs
+GenericName[ru]=Копирование CD
+GenericName[sk]=CD získavanie
+GenericName[sl]=Zajemalnik CD-jev
+GenericName[sr]=CD гребач
+GenericName[sr@Latn]=CD grebač
+GenericName[sv]=Cd-lagring
+GenericName[ta]=சிடி ரிப்பர்
+GenericName[tg]=Кандакунандаи Диски Фишурда
+GenericName[th]=โปรแกรมดึงข้อมูลจากซีดีบันทึกเสียง
+GenericName[tr]=Cd Aktarıcı
+GenericName[uk]=Видирання з КД
+GenericName[uz]=Kompakt-disk ripper
+GenericName[uz@cyrillic]=Компакт-диск риппер
+GenericName[zh_CN]=CD 提取器
+GenericName[zh_HK]=CD 擷取器
+GenericName[zh_TW]=光碟擷取器
+MimeType=
+Exec=kaudiocreator
+Icon=kaudiocreator
+Path=
+Type=Application
+Terminal=false
+Categories=Qt;KDE;AudioVideo;Audio;
diff --git a/kaudiocreator/kaudiocreator.h b/kaudiocreator/kaudiocreator.h
new file mode 100644
index 00000000..27018908
--- /dev/null
+++ b/kaudiocreator/kaudiocreator.h
@@ -0,0 +1,93 @@
+/**
+ * This file is part of the KAudioCreator package
+ * Copyright (C) 2003 Benjamin C Meyer (ben+kaudiocreator at meyerhome dot net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef KAUDIOCREATOR_H
+#define KAUDIOCREATOR_H
+
+#include <kmainwindow.h>
+#include <kconfigdialog.h>
+
+class KJanusWidget;
+class TracksImp;
+class JobQueImp;
+class Ripper;
+class Encoder;
+class KCModule;
+class EncoderConfigImp;
+class KPopupMenu;
+
+class KAudioCreator : public KMainWindow {
+
+Q_OBJECT
+
+public:
+ KAudioCreator( QWidget* parent = 0, const char* name = 0);
+ void setDevice( const QString &device );
+
+protected:
+ virtual bool queryClose();
+
+private slots:
+ void showSettings();
+ void updateStatus();
+
+ void hasCD(bool);
+ void configureNotifications();
+ void encodeFile();
+
+ void slotRipSelection(int);
+ void getRipMenu();
+
+private:
+ KJanusWidget *janusWidget;
+ TracksImp *tracks;
+ JobQueImp *jobQue;
+ Ripper *ripper;
+ Encoder *encoder;
+ KPopupMenu *ripMenu;
+
+};
+
+class SettingsDialog: public KConfigDialog {
+Q_OBJECT
+
+public:
+ SettingsDialog(QWidget *parent, const char *name,KConfigSkeleton *config);
+
+protected slots:
+ void updateSettings();
+ void updateWidgets();
+ void updateWidgetsDefault();
+ void slotCddbChanged(bool);
+
+protected:
+ bool hasChanged();
+ bool isDefault();
+
+public:
+ EncoderConfigImp *encoderConfigImp;
+
+private:
+ KCModule *cddb;
+ bool cddbChanged;
+};
+
+#endif
+
diff --git a/kaudiocreator/kaudiocreator.kcfg b/kaudiocreator/kaudiocreator.kcfg
new file mode 100644
index 00000000..9e8addd0
--- /dev/null
+++ b/kaudiocreator/kaudiocreator.kcfg
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+ <include>kaudiocreator_workman.h</include>
+ <kcfgfile name="kaudiocreatorrc"/>
+ <group name="CD">
+ <entry name="performCDDBauto" type="Bool">
+ <label>Perform automatic CDDB lookups.</label>
+ <default>true</default>
+ </entry>
+ <entry name="autoRip" type="Bool">
+ <label>Automatically rip all tracks upon a successful CDDB retrieval</label>
+ <default>false</default>
+ </entry>
+ <entry name="device" type="StringList">
+ <label>CD device</label>
+ <default code="true">DEFAULT_CD_DEVICE</default>
+ </entry>
+ <entry name="promptIfIncompleteInfo" type="Bool">
+ <label>Prompt if information is not complete</label>
+ <default>true</default>
+ </entry>
+ </group>
+ <group name="General">
+ <entry name="removeCompletedJobs" type="Bool">
+ <label>Automatically remove jobs when finished</label>
+ <default>true</default>
+ </entry>
+ <entry name="replaceInput" type="String" key="selection">
+ <label>Regexp to match file names with</label>
+ </entry>
+ <entry name="replaceOutput" type="String" key="replace">
+ <label>String used to replace the parts that match the selection regexp</label>
+ </entry>
+ <entry name="seperateMultiArtist" type="Bool">
+ <default>false</default>
+ </entry>
+ <entry name="genericArtist" type="String">
+ <default>Various Artists</default>
+ </entry>
+ <entry name="delimiter" type="String">
+ <default> - </default>
+ </entry>
+ <entry name="format_artistTitle" type="Bool">
+ <default>true</default>
+ </entry>
+ <entry name="format_titleArtist" type="Bool">
+ <default>false</default>
+ </entry>
+ </group>
+ <group name="Encoder">
+ <entry name="currentEncoder" type="Int" key="encoderChoice">
+ <label>Currently selected encoder</label>
+ <default>0</default>
+ </entry>
+ <entry name="lastKnownEncoder" type="Int">
+ <label>Last encoder in the list</label>
+ <default>0</default>
+ </entry>
+ <entry name="numberOfCpus" type="Int">
+ <label>Number of files to encode at a time</label>
+ <default>1</default>
+ </entry>
+ <entry name="fileFormat" type="String">
+ <label>Location pattern for encoded files</label>
+ <default>~/%{extension}/%{albumartist}/%{albumtitle}/%{artist} - %{number} - %{title}.%{extension}</default>
+ </entry>
+ <entry name="NiceLevel" type="Int">
+ <label>Encoder priority</label>
+ <default>0</default>
+ <min>-19</min>
+ <max>19</max>
+ </entry>
+ <entry name="FullDecoderDebug" type="Bool">
+ <label>Enable full decoder debugging</label>
+ <default>false</default>
+ </entry>
+ </group>
+ <group name="Ripper">
+ <entry name="maxWavFiles" type="Int">
+ <label>Number of tracks to rip at a time</label>
+ <default>1</default>
+ </entry>
+ <entry name="beepAfterRip" type="Bool">
+ <label>Beep after rip</label>
+ <default>true</default>
+ </entry>
+ <entry name="autoEjectAfterRip" type="Bool">
+ <label>Eject CD after last track is ripped</label>
+ <default>false</default>
+ </entry>
+ <entry name="autoEjectDelay" type="Int">
+ <label>Auto-eject delay</label>
+ <default>0</default>
+ </entry>
+ <entry name="enableTempDir" type="Bool">
+ <label>Specify temporary directory</label>
+ <default>false</default>
+ </entry>
+ <entry name="tempDir" type="String">
+ <label>Location of temporary directory to use</label>
+ <default></default>
+ </entry>
+ </group>
+</kcfg>
diff --git a/kaudiocreator/kaudiocreator_encoders.kcfg b/kaudiocreator/kaudiocreator_encoders.kcfg
new file mode 100644
index 00000000..cbe9372b
--- /dev/null
+++ b/kaudiocreator/kaudiocreator_encoders.kcfg
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+ <kcfgfile name="kaudiocreatorrc">
+ <parameter name="encoder" />
+ </kcfgfile>
+
+ <group name="$(encoder)">
+ <entry name="encoderName" type="String">
+ <label>Name of the encoder</label>
+ <default>Unknown Encoder</default>
+ </entry>
+ <entry name="commandLine" type="Path">
+ <label>Command line to invoke encoder</label>
+ <default>app.exe %f %o</default>
+ </entry>
+ <entry name="extension" type="String">
+ <label>File extension</label>
+ <default>wav</default>
+ </entry>
+ <entry name="percentLength" type="Int">
+ <label></label>
+ <default>2</default>
+ </entry>
+ </group>
+</kcfg>
diff --git a/kaudiocreator/kaudiocreator_workman.h b/kaudiocreator/kaudiocreator_workman.h
new file mode 100644
index 00000000..268b4f90
--- /dev/null
+++ b/kaudiocreator/kaudiocreator_workman.h
@@ -0,0 +1,27 @@
+/**
+ * This file is part of the KAudioCreator package
+ * Copyright (C) 2003 Waldo Bastian <bastian@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef KAUDIOCREATOR_WORKMAN_H
+#define KAUDIOCREATOR_WORKMAN_H
+
+extern "C" {
+ #include "../kscd/libwm/include/workman.h"
+}
+
+#endif
diff --git a/kaudiocreator/kaudiocreatorui.rc b/kaudiocreator/kaudiocreatorui.rc
new file mode 100644
index 00000000..597eda1d
--- /dev/null
+++ b/kaudiocreator/kaudiocreatorui.rc
@@ -0,0 +1,41 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kaudiocreator" version="3">
+<ActionProperties>
+ <Action name="rip_selected" icon="gear"/>
+ <Action name="rip" icon="gear"/>
+ <Action name="configure_kaudiocreator" icon="configure"/>
+ <Action name="clear_done_jobs" icon="edittrash"/>
+ <Action name="eject" icon="player_eject"/>
+ <Action name="edit_cd" icon="edit"/>
+ <Action name="cddb_now" icon="cdaudio_mount"/>
+</ActionProperties>
+
+<MenuBar>
+ <Menu name="file"><Text>&amp;Program</Text>
+ <Action name="eject"/>
+ <Action name="cddb_now"/>
+ <Action name="edit_cd"/>
+ <Action name="select_all"/>
+ <Action name="deselect_all"/>
+ <Action name="rip_selected"/>
+ <Action name="rip"/>
+ <Action name="clear_done_jobs"/>
+ <Separator/>
+ <Action name="encode_file"/>
+ <Separator/>
+ <Action name="quit"/>
+ </Menu>
+ <Menu name="settings">
+ <Action name="configuretoolbars" append="configure_merge"/>
+ <Action name="configure_kaudiocreator" append="configure_merge"/>
+ </Menu>
+</MenuBar>
+
+<ToolBar name="Main ToolBar">
+ <Action name="cddb_now"/>
+ <Action name="edit_cd"/>
+ <Action name="rip"/>
+ <Action name="eject"/>
+</ToolBar>
+
+</kpartgui>
diff --git a/kaudiocreator/lo16-app-kaudiocreator.png b/kaudiocreator/lo16-app-kaudiocreator.png
new file mode 100644
index 00000000..c518d066
--- /dev/null
+++ b/kaudiocreator/lo16-app-kaudiocreator.png
Binary files differ
diff --git a/kaudiocreator/lo32-app-kaudiocreator.png b/kaudiocreator/lo32-app-kaudiocreator.png
new file mode 100644
index 00000000..6f790413
--- /dev/null
+++ b/kaudiocreator/lo32-app-kaudiocreator.png
Binary files differ
diff --git a/kaudiocreator/main.cpp b/kaudiocreator/main.cpp
new file mode 100644
index 00000000..a8b6649f
--- /dev/null
+++ b/kaudiocreator/main.cpp
@@ -0,0 +1,56 @@
+/**
+ * This file is part of the KAudioCreator package
+ * Copyright (C) 2003-2005 Benjamin C Meyer (ben+kaudiocreator at meyerhome dot net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <kapplication.h>
+
+#include "kaudiocreator.h"
+#include <kcmdlineargs.h>
+#include <kaboutdata.h>
+
+static const KCmdLineOptions options[] = {
+ {"+[device]",I18N_NOOP( "CD device, can be a path or a media:/ URL" ),0},
+ KCmdLineLastOption
+};
+
+
+int main(int argc, char *argv[]){
+ KAboutData aboutData("kaudiocreator", I18N_NOOP("KAudioCreator"), "1.13",
+ I18N_NOOP("CD ripper and audio encoder frontend"), KAboutData::License_LGPL, "(c) 2003-2005, Benjamin Meyer",
+ 0, "http://www.icefox.net/");
+ aboutData.addAuthor("Benjamin Meyer", I18N_NOOP("Original author"), "ben+kaudiocreator@meyerhome.net", "http://www.icefox.net/");
+
+ // command line
+ KCmdLineArgs::init(argc, argv, &aboutData);
+ KCmdLineArgs::addCmdLineOptions( options );
+ KApplication a(argc, argv);
+ KAudioCreator *app = new KAudioCreator(0, "MainWindow");
+
+ // we need some strings from libkcddb for the cddb album dialog
+ KGlobal::locale()->insertCatalogue("libkcddb");
+
+ a.setMainWidget(app);
+
+ KCmdLineArgs* args = KCmdLineArgs::parsedArgs();
+ if ( args->count()>0 ) app->setDevice( args->arg( 0 ) );
+ args->clear();
+ app->show();
+ return a.exec();
+}
+
diff --git a/kaudiocreator/prefs.kcfgc b/kaudiocreator/prefs.kcfgc
new file mode 100644
index 00000000..dc784e77
--- /dev/null
+++ b/kaudiocreator/prefs.kcfgc
@@ -0,0 +1,7 @@
+# Code generation options for kconfig_compiler
+File=kaudiocreator.kcfg
+#IncludeFiles=defines.h
+ClassName=Prefs
+Singleton=true
+#CustomAdditions=true
+Mutators=device,lastKnownEncoder
diff --git a/kaudiocreator/ripconfig.ui b/kaudiocreator/ripconfig.ui
new file mode 100644
index 00000000..4ce85f1b
--- /dev/null
+++ b/kaudiocreator/ripconfig.ui
@@ -0,0 +1,191 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>RipConfig</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>RipConfig</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>525</width>
+ <height>526</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_beepAfterRip</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Beep after each rip is done</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel14</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Number of tracks to rip at a time:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_maxWavFiles</cstring>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>kcfg_maxWavFiles</cstring>
+ </property>
+ <property name="maxValue">
+ <number>99999</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_autoEjectAfterRip</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Auto-eject CD after last track is ripped</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel15</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Auto-&amp;eject delay:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_autoEjectDelay</cstring>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>kcfg_autoEjectDelay</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="suffix">
+ <string> seconds</string>
+ </property>
+ <property name="maxValue">
+ <number>300</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>kcfg_enableTempDir</cstring>
+ </property>
+ <property name="title">
+ <string>Default Temporary Directory</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>kcfg_tempDir</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>280</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>kcfg_autoEjectAfterRip</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_autoEjectDelay</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_autoEjectAfterRip</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>TextLabel15</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>kcfg_beepAfterRip</tabstop>
+ <tabstop>kcfg_maxWavFiles</tabstop>
+ <tabstop>kcfg_autoEjectAfterRip</tabstop>
+ <tabstop>kcfg_autoEjectDelay</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kaudiocreator/ripper.cpp b/kaudiocreator/ripper.cpp
new file mode 100644
index 00000000..2dd8795d
--- /dev/null
+++ b/kaudiocreator/ripper.cpp
@@ -0,0 +1,263 @@
+/**
+ * This file is part of the KAudioCreator package
+ * Copyright (C) 2003 Benjamin C Meyer (ben+kaudiocreator at meyerhome dot net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "ripper.h"
+#include "prefs.h"
+
+#include <qfile.h>
+#include <qtimer.h>
+#include <ktempfile.h>
+#include <kmessagebox.h>
+#include <knotifyclient.h>
+#include <kstandarddirs.h>
+#include <kio/scheduler.h>
+
+/**
+ * Constructor, load settings.
+ */
+Ripper::Ripper( QObject* parent, const char* name) : QObject(parent,name) {
+ loadSettings();
+}
+
+/**
+ * Loads the settings
+ */
+void Ripper::loadSettings(){
+ for(uint i=0; i<(uint)Prefs::maxWavFiles(); i++)
+ tendToNewJobs();
+}
+
+/**
+ * Deconstructor, remove pending jobs, remove current jobs, save settings.
+ */
+Ripper::~Ripper(){
+ pendingJobs.clear();
+ QMap<KIO::Job*, Job*>::Iterator it;
+ for( it = jobs.begin(); it != jobs.end(); ++it ){
+ KIO::Job* ioJob = it.key();
+ Job *job = it.data();
+ delete job;
+
+ if(ioJob){
+ KIO::FileCopyJob *copyJob = static_cast<KIO::FileCopyJob*> (ioJob);
+ disconnect(copyJob, SIGNAL(result(KIO::Job*)), this, SLOT(copyJobResult(KIO::Job*)));
+ disconnect(copyJob, SIGNAL(percent ( KIO::Job *, unsigned long)), this, SLOT(updateProgress ( KIO::Job *, unsigned long)));
+ QString fileDestination = (copyJob->destURL()).path();
+ copyJob->kill();
+ QFile file( fileDestination );
+ file.remove();
+ }
+ }
+ jobs.clear();
+}
+
+/**
+ * @return The number of active jobs
+ */
+int Ripper::activeJobCount() {
+ return jobs.count();
+}
+
+/**
+ * @return The number of pending jobs
+ */
+int Ripper::pendingJobCount() {
+ return pendingJobs.count();
+}
+
+/**
+ * Cancel and remove the job with the matching id.
+ * Remove it from the local collection of jobs, delete the temp file if
+ * there is one.
+ * @param id the id number of the job to remove.
+ */
+void Ripper::removeJob(int id){
+ QMap<KIO::Job*, Job*>::Iterator it;
+ for( it = jobs.begin(); it != jobs.end(); ++it ){
+ if(it.data()->id == id){
+ KIO::FileCopyJob *copyJob = dynamic_cast<KIO::FileCopyJob*> (it.key());
+ if(copyJob){
+ QString fileDestination = (copyJob->destURL()).path();
+ copyJob->kill();
+ // This here is such a hack, shouldn't kill() do this, or why isn't there a stop()?
+ // TODO add to copyJob a stop() function.
+ QFile file( fileDestination );
+ if(file.exists())
+ file.remove();
+ else {
+ QFile f( fileDestination+".part" );
+ f.remove();
+ }
+ }
+ jobs.remove(it.key());
+ break;
+ }
+ }
+ Job *job = pendingJobs.first();
+ while(job){
+ if(job->id == id)
+ break;
+ job = pendingJobs.next();
+ }
+ if(job){
+ pendingJobs.remove(job);
+ delete job;
+ }
+ //qDebug(QString("Done removing Job:%1").arg(id).latin1());
+ tendToNewJobs();
+}
+
+/**
+ * Begin to rip the track specified in job.
+ * @param job the new job that this module should take over.
+ * @param job the new job that we need to handle.
+ */
+void Ripper::ripTrack(Job *job){
+ if(!job)
+ return;
+ emit(addJob(job, i18n("Ripping: %1 - %2").arg(job->track_artist).arg(job->track_title)));
+ pendingJobs.append(job);
+ tendToNewJobs();
+}
+
+/**
+ * See if there are are new jobs to attend too. If we are all loaded up
+ * then just loop.
+ */
+void Ripper::tendToNewJobs(){
+ if(pendingJobs.count() == 0){
+ emit jobsChanged();
+ return;
+ }
+
+ // If we are currently ripping the max try again in a little bit.
+ if(jobs.count() >= (uint)Prefs::maxWavFiles()){
+ emit jobsChanged();
+ return;
+ }
+
+ Job *job = pendingJobs.first();
+ if(!job)
+ return;
+ pendingJobs.remove(job);
+
+ QMap<QString, QString> map;
+ QString defaultTempDir;
+ if(Prefs::enableTempDir())
+ defaultTempDir = Prefs::tempDir();
+ else
+ defaultTempDir = locateLocal("tmp", "");
+ // For cases like "/tmp" where there is a missing /
+ defaultTempDir = KURL(defaultTempDir).path(1);
+ KTempFile tmp( defaultTempDir, ".wav" );
+ tmp.setAutoDelete(true);
+
+ 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);
+
+ KURL source(wavFile);
+ KURL dest(tmp.name());
+
+ KIO::FileCopyJob *copyJob = new KIO::FileCopyJob(source, dest, 0644, false, true, false, false);
+ jobs.insert(copyJob, job);
+ connect(copyJob, SIGNAL(result(KIO::Job*)), this, SLOT(copyJobResult(KIO::Job*)));
+ connect(copyJob, SIGNAL(percent ( KIO::Job *, unsigned long)), this, SLOT(updateProgress ( KIO::Job *, unsigned long)));
+
+ emit jobsChanged();
+}
+
+/**
+ * Copies the data from the KIO Job. Uses this data to fill in the
+ * information dialog.
+ * @param copyjob the IO job to copy from
+ */
+void Ripper::copyJobResult(KIO::Job *copyjob){
+ if(!copyjob)
+ return;
+ KIO::FileCopyJob *copyJob = dynamic_cast<KIO::FileCopyJob*> (copyjob);
+ KNotifyClient::event("track ripped");
+
+ if(jobs.find(copyjob) == jobs.end())
+ return;
+ Job *newJob = jobs[copyjob];
+ jobs.remove(copyjob);
+
+ if(Prefs::beepAfterRip())
+ KNotifyClient::beep();
+
+ if ( copyJob->error() == 0 ){
+ emit updateProgress(newJob->id, 100);
+ newJob->location = copyJob->destURL().path();
+ emit( encodeWav(newJob));
+ }
+ else{
+ copyJob->showErrorDialog(0);
+ QFile file( (copyJob->destURL()).path());
+ file.remove();
+ emit updateProgress(newJob->id, -1);
+ delete newJob;
+ newJob = 0;
+ }
+
+ if(newJob && newJob->lastSongInAlbum){
+ if(Prefs::autoEjectAfterRip()){
+ // Don't eject device if a pending job has that device
+ Job *job = pendingJobs.first();
+ while( job ){
+ if( job->device == newJob->device )
+ break;
+ job = pendingJobs.next();
+ }
+ if( !job ){
+ deviceToEject = newJob->device;
+ QTimer::singleShot( Prefs::autoEjectDelay()*1000 + 500, this, SLOT(ejectNow()));
+ }
+ }
+ KNotifyClient::event("cd ripped");
+ }
+ tendToNewJobs();
+}
+
+void Ripper::ejectNow(){
+ emit eject(deviceToEject);
+}
+
+/**
+ * Update the progress of the file copy.
+ * @param job the current ioslave job in progress
+ * @param percent the current percent that the ioslave has done.
+ */
+void Ripper::updateProgress( KIO::Job *job, unsigned long percent){
+ if(job){
+ Job *ripJob = (jobs[job]);
+ if(ripJob)
+ emit updateProgress(ripJob->id, percent);
+ }
+}
+
+#include "ripper.moc"
+
diff --git a/kaudiocreator/ripper.h b/kaudiocreator/ripper.h
new file mode 100644
index 00000000..7cd09522
--- /dev/null
+++ b/kaudiocreator/ripper.h
@@ -0,0 +1,68 @@
+/**
+ * This file is part of the KAudioCreator package
+ * Copyright (C) 2003 Benjamin C Meyer (ben+kaudiocreator at meyerhome dot net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef RIPPER_H
+#define RIPPER_H
+
+#include "job.h"
+#include <kio/jobclasses.h>
+#include <qmap.h>
+#include <qptrlist.h>
+
+class Job;
+
+class Ripper : public QObject {
+
+Q_OBJECT
+
+signals:
+ void addJob( Job *job, const QString &name );
+ void updateProgress( int id, int progress );
+ void encodeWav( Job *job );
+ void eject( const QString &device );
+ void jobsChanged();
+
+public:
+ Ripper( QObject* parent = 0, const char* name = 0 );
+ ~Ripper();
+ int activeJobCount();
+ int pendingJobCount();
+
+public slots:
+ void loadSettings();
+ void ripTrack( Job * );
+ void removeJob( int id );
+
+private slots:
+ void copyJobResult( KIO::Job *job );
+ void updateProgress( KIO::Job *job, unsigned long percent );
+ void tendToNewJobs();
+ void ejectNow();
+
+private:
+ QString deviceToEject;
+ // Jobs that we are currently doing.
+ QMap<KIO::Job*, Job*> jobs;
+ // Jobs that we want to do , but haven't done yet
+ QPtrList<Job> pendingJobs;
+};
+
+#endif // RIPPER_H
+
diff --git a/kaudiocreator/tracks.ui b/kaudiocreator/tracks.ui
new file mode 100644
index 00000000..a225f041
--- /dev/null
+++ b/kaudiocreator/tracks.ui
@@ -0,0 +1,225 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>Tracks</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>Tracks</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>501</width>
+ <height>377</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Tracks</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>albumName</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <property name="text">
+ <string>Unknown Artist - Unknown Album</string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ <property name="hAlign" stdset="0">
+ </property>
+ </widget>
+ <widget class="KListView" row="2" column="0">
+ <column>
+ <property name="text">
+ <string>Rip</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Track</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Length</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Title</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>trackListing</cstring>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="resizeMode">
+ <enum>LastColumn</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="3" column="0">
+ <property name="name">
+ <cstring>Layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>selectAllTracksButton</cstring>
+ </property>
+ <property name="text">
+ <string>Select &amp;All Tracks</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>deselectAllTracksButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Deselect All Tracks</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Device:</string>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <item>
+ <property name="text">
+ <string>/dev/cdrom</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>deviceCombo</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>246</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>deviceCombo</tabstop>
+ <tabstop>trackListing</tabstop>
+ <tabstop>selectAllTracksButton</tabstop>
+ <tabstop>deselectAllTracksButton</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kcombobox.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kaudiocreator/tracksimp.cpp b/kaudiocreator/tracksimp.cpp
new file mode 100644
index 00000000..591e7f2b
--- /dev/null
+++ b/kaudiocreator/tracksimp.cpp
@@ -0,0 +1,578 @@
+/**
+ * This file is part of the KAudioCreator package
+ * Copyright (C) 2003 Benjamin C Meyer (ben+kaudiocreator at meyerhome dot net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#include <dcopref.h>
+#include <fixx11h.h>
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kcombobox.h>
+#include <kdebug.h>
+#include <kinputdialog.h>
+#include <klistview.h>
+#include <kprocess.h>
+#include <kmessagebox.h>
+#include <kurl.h>
+
+#include <qfileinfo.h>
+#include <qlabel.h>
+#include <qptrlist.h>
+#include <qpushbutton.h>
+#include <qregexp.h>
+#include <qspinbox.h>
+#include <qtimer.h>
+
+#include "job.h"
+#include "kcompactdisc.h"
+#include "libkcddb/cdinfodialogbase.h"
+#include "prefs.h"
+#include "tracksimp.h"
+
+/**
+ * Constructor, connect up slots and signals.
+ */
+TracksImp::TracksImp( QWidget* parent, const char* name) :
+ Tracks(parent,name),
+ cddbInfo()
+{
+ cd = new KCompactDisc;
+
+ connect(cd,SIGNAL(discChanged(unsigned)),this,SLOT(newDisc(unsigned)));
+
+ connect(trackListing, SIGNAL(clicked( QListViewItem * )), this, SLOT(selectTrack(QListViewItem*)));
+ connect(trackListing, SIGNAL(doubleClicked(QListViewItem *)), this, SLOT(editInformation()));
+ connect(trackListing, SIGNAL(returnPressed(QListViewItem *)), this, SLOT(editInformation()));
+ connect(selectAllTracksButton, SIGNAL(clicked()), this, SLOT(selectAllTracks()));
+ connect(deselectAllTracksButton, SIGNAL(clicked()), this, SLOT(deselectAllTracks()));
+
+ connect(deviceCombo, SIGNAL(textChanged(const QString &)), this, SLOT(changeDevice(const QString &)));
+
+ selectAllTracksButton->setEnabled( false );
+ deselectAllTracksButton->setEnabled( false );
+
+ cddb = new KCDDB::Client();
+ cddb->setBlockingMode(false);
+ connect(cddb, SIGNAL(finished(CDDB::Result)), this, SLOT(lookupCDDBDone(CDDB::Result)));
+ trackListing->setSorting(-1, false);
+ loadSettings();
+}
+
+/**
+ * store the current device from the combo.
+ */
+TracksImp::~TracksImp() {
+ QStringList list;
+ if( deviceCombo->count() != 0)
+ list.append(deviceCombo->currentText());
+ for ( int i=0; i<deviceCombo->count();i++ ) {
+ QString text = deviceCombo->text(i);
+ if( list.find(text) == list.end())
+ list.append(text);
+ if( list.count() == 5)
+ break;
+ }
+
+ Prefs::setDevice(list);
+ Prefs::writeConfig();
+}
+
+/**
+ * Load the class settings.
+ */
+void TracksImp::loadSettings() {
+ QStringList list;
+
+ // Add the saved list, no dups
+ QStringList prefsList = Prefs::device();
+ QStringList::Iterator it;
+ for ( it = prefsList.begin(); it != prefsList.end(); ++it ) {
+ if( list.find( *it ) == list.end())
+ list.append(*it);
+ }
+ // Get current list, no dups
+ for ( int i=0; i<deviceCombo->count();i++ ) {
+ QString text = deviceCombo->text(i);
+ if( list.find(text) == list.end())
+ list.append(text);
+ }
+
+ // Set list, get top one
+ deviceCombo->clear();
+ deviceCombo->insertStringList(list);
+
+ changeDevice(deviceCombo->currentText());
+}
+
+void TracksImp::newDisc(unsigned discId)
+{
+ if (discId == KCompactDisc::missingDisc)
+ {
+ kdDebug() << "newDisc - No disc" << endl;
+ cddbInfo.clear();
+ cddbInfo.title = i18n("No disc");
+ newAlbum();
+ emit(hasCD(false));
+
+ selectAllTracksButton->setEnabled( false );
+ deselectAllTracksButton->setEnabled( false );
+
+ return;
+ }
+
+ kdDebug() << "newDisc - " << discId << endl;
+ emit(hasCD(true));
+
+ selectAllTracksButton->setEnabled( true );
+ deselectAllTracksButton->setEnabled( true );
+
+ cddbInfo.clear();
+
+ cddbInfo.id = QString::number(discId, 16).rightJustify(8,'0');
+ cddbInfo.length = cd->discLength();
+
+ cddbInfo.artist = cd->discArtist();
+ cddbInfo.title = cd->discTitle();
+
+ // If it's a sampler, we'll do artist/title.
+ bool isSampler = (cddbInfo.title.compare("Various") == 0);
+ KCDDB::TrackInfo track;
+ for (unsigned i = 1; i <= cd->tracks(); i++) {
+ if (isSampler)
+ track.title = cd->trackArtist(i) + " / " + cd->trackTitle(i);
+ else
+ track.title = cd->trackTitle(i);
+ cddbInfo.trackInfoList.append(track);
+ }
+
+ newAlbum();
+
+ if (Prefs::performCDDBauto())
+ lookupCDDB();
+}
+
+/**
+ * @return if there is a cd inserted or not.
+ */
+bool TracksImp::hasCD(){
+ return cd->discId() != KCompactDisc::missingDisc;
+}
+
+/**
+ * The device text has changed.
+ * @param file - the new text to check.
+ */
+void TracksImp::changeDevice(const QString &file ) {
+ QString newDevice = KCompactDisc::urlToDevice(file);
+
+ if( newDevice == cd->device() ) {
+ //qDebug("Device names match, returning");
+ return;
+ }
+
+ QFileInfo fileInfo(newDevice);
+ if( !fileInfo.exists() || fileInfo.isDir()) {
+ //qDebug("Device file !exist or isDir or !file");
+ return;
+ }
+
+ if (!cd->setDevice(newDevice, 50, false))
+ {
+ QString errstring =
+ i18n("CDROM read or access error (or no audio disk in drive).\n"\
+ "Please make sure you have access permissions to:\n%1")
+ .arg(file);
+ KMessageBox::error(this, errstring, i18n("Error"));
+ }
+}
+
+/**
+ * Helper function (toolbar button) for users.
+ **/
+void TracksImp::performCDDB() {
+ if (!hasCD()) {
+ KMessageBox::sorry(this, i18n("Please insert a disk."),
+ i18n("CDDB Failed"));
+ return;
+ }
+
+ lookupCDDB();
+}
+
+/**
+ * See if we can't get the cddb value for this cd.
+ */
+void TracksImp::lookupCDDB() {
+ cddb->config().reparse();
+ cddb->lookup(cd->discSignature());
+}
+
+/**
+ * The non blocking CDDB function calling has finished. Report an error or
+ * continue.
+ * @param result the success or failure of the cddb retrieval.
+ */
+void TracksImp::lookupCDDBDone(CDDB::Result result ) {
+ if ((result != KCDDB::CDDB::Success) &&
+ (result != KCDDB::CDDB::MultipleRecordFound))
+ {
+ KMessageBox::sorry(this, i18n("Unable to retrieve CDDB information."), i18n("CDDB Failed"));
+ return;
+ }
+
+ // Choose the cddb entry
+ KCDDB::CDInfo info = cddb->bestLookupResponse();
+ // TODO Why doesn't libcddb not return MultipleRecordFound?
+ //if( result == KCDDB::CDDB::MultipleRecordFound ) {
+ if( Prefs::promptIfIncompleteInfo() && cddb->lookupResponse().count() > 1 ) {
+ QString searchedCDId = cddbInfo.id;
+ CDInfoList cddb_info = cddb->lookupResponse();
+ CDInfoList::iterator it;
+ QStringList list;
+ for ( it = cddb_info.begin(); it != cddb_info.end(); ++it ) {
+ list.append( QString("%1, %2, %3").arg((*it).artist).arg((*it).title)
+ .arg((*it).genre));
+ }
+
+ bool ok(false);
+ QString res = KInputDialog::getItem(
+ i18n("Select CDDB entry"),
+ i18n("Select a CDDB entry:"), list, 0, false, &ok,
+ this );
+ if ( ok ) {
+ // The user selected and item and pressed OK
+ uint c = 0;
+ for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
+ if( *it == res) break;
+ c++;
+ }
+ if( c < cddb_info.size() )
+ info = cddb_info[c];
+ } else {
+ return;
+ // user pressed Cancel
+ }
+ // Check that the CD we looked up is the one now loaded.
+ // The user might have ejected the CD while we were in the
+ // KInputDialog event loop, and replaced it with another one.
+ if ( searchedCDId != cddbInfo.id )
+ return;
+ }
+
+ // Some sanity provisions to ensure that the number of records matches what
+ // the CD actually contains.
+ while (info.trackInfoList.count() < cddbInfo.trackInfoList.count())
+ {
+ info.trackInfoList.append(KCDDB::TrackInfo());
+ }
+ while (info.trackInfoList.count() > cddbInfo.trackInfoList.count())
+ {
+ info.trackInfoList.pop_back();
+ }
+ cddbInfo = info;
+ newAlbum();
+
+ // See if the user wishes to automaticly rip after successfully retrieving
+ if( Prefs::autoRip())
+ ripWholeAlbum();
+}
+
+/**
+ * Bring up the dialog to edit the information about this album.
+ * If there is not currently selected track return.
+ * If ok is pressed then store the information and update track name.
+ */
+void TracksImp::editInformation( )
+{
+ if( !hasCD() ) return;
+ // Create dialog.
+ KDialogBase *dialog = new KDialogBase( this, "name", false, i18n( "CD Editor" ),
+ KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true );
+ CDInfoDialogBase *base = new CDInfoDialogBase(dialog, "Album info editor dialog");
+ // Workaround the fact that CDInfoDialogBase doesn't take
+ // a const TrackOffsetList
+ QValueList<unsigned> discSig = cd->discSignature();
+ base->setInfo(cddbInfo, discSig);
+ dialog->setMainWidget(base);
+
+ // Show dialog->and save results.
+ bool okClicked = dialog->exec();
+ if( okClicked ) {
+ cddbInfo = base->info();
+ newAlbum();
+ KCDDB::Cache::store(cddbInfo);
+ }
+ delete dialog;
+}
+
+QString TracksImp::formatTime(unsigned ms)
+{
+ QTime time;
+
+ time = time.addMSecs((int)ms);
+
+ // Use ".zzz" for milliseconds...
+ QString temp2;
+ if (time.hour() > 0)
+ temp2 = time.toString("hh:mm:ss");
+ else
+ temp2 = time.toString("mm:ss");
+ return temp2;
+}
+
+/**
+ * Helper function.
+ * Selects all tracks and then calls startSession to rip them all.
+ */
+void TracksImp::ripWholeAlbum() {
+ selectAllTracks();
+ startSession();
+}
+
+/**
+ * Start of the "ripping session" by emiting signals to rip the selected tracks.
+ * If any album information is not set, notify the user first.
+ */
+void TracksImp::startSession( int encoder )
+{
+ QPtrList<TracksItem> selected = selectedTracks();
+
+ if( selected.isEmpty() )
+ {
+ int i = KMessageBox::questionYesNo( this, i18n("No tracks have been selected. Would you like to rip the entire CD?"),
+ i18n("No Tracks Selected"), i18n("Rip CD"), KStdGuiItem::cancel() );
+ if( i == KMessageBox::No )
+ return;
+
+ selectAllTracks();
+ selected = selectedTracks();
+ }
+
+ QStringList list;
+ if( cddbInfo.genre == "Unknown" )
+ list += "Genre";
+ if( cddbInfo.year == 0 )
+ list += "Year";
+ if( cddbInfo.artist == "Unknown Artist")
+ list += "Artist";
+ if( cddbInfo.title == "Unknown Album")
+ list += "Album";
+
+ if( Prefs::promptIfIncompleteInfo() && list.count() > 0 )
+ {
+ int r = KMessageBox::questionYesNo( this,
+ i18n( "Part of the album is not set: %1.\n (To change album information click the \"Edit Information\" button.)\n Would you like to rip the selected tracks anyway?").arg(list.join(", ")), i18n("Album Information Incomplete"), i18n("Rip"), KStdGuiItem::cancel() );
+
+ if( r == KMessageBox::No )
+ return;
+ }
+
+ if ( encoder == -1 )
+ encoder = Prefs::currentEncoder();
+
+ Job *lastJob = 0;
+ TracksItem *item = selected.first();
+
+ for( ; item ; item = selected.next() )
+ {
+ Job *newJob = new Job();
+ newJob->encoder = encoder;
+ newJob->device = cd->device();
+ newJob->album = cddbInfo.title;
+ newJob->genre = cddbInfo.genre;
+ if( newJob->genre.isEmpty() )
+ newJob->genre = "Pop";
+
+ newJob->group = cddbInfo.artist;
+ newJob->comment = cddbInfo.extd;
+ newJob->year = cddbInfo.year;
+ newJob->track = item->track();
+
+// newJob->track_title = item->title();
+ newJob->track_title = item->text( HEADER_TRACK_NAME );
+ newJob->track_artist = item->artist();
+ newJob->track_comment = item->comment();
+ lastJob = newJob;
+ emit( ripTrack(newJob) );
+ }
+ if( lastJob)
+ lastJob->lastSongInAlbum = true;
+
+ KMessageBox::information(this,
+ i18n("%1 Job(s) have been started. You can watch their progress in the "
+ "jobs section.").arg( selected.count() ),
+ i18n("Jobs have started"), i18n("Jobs have started"));
+}
+
+/**
+ * Selects and unselects the tracks.
+ * @param currentItem the track to swich the selection choice.
+ */
+void TracksImp::selectTrack( QListViewItem *item )
+{
+ if( !item )
+ return;
+
+#define item static_cast<TracksItem*>(item)
+ item->setChecked( !item->checked() );
+#undef item
+}
+
+QPtrList<TracksItem> TracksImp::selectedTracks()
+{
+ QPtrList<TracksItem> selected;
+ TracksItem *item = static_cast<TracksItem*>(trackListing->firstChild());
+
+ while( item )
+ {
+ if( item->checked() )
+ selected.append( item );
+ item = static_cast<TracksItem*>(item->nextSibling());
+ }
+ return selected;
+}
+
+/**
+ * Turn on all of the tracks.
+ */
+void TracksImp::selectAllTracks()
+{
+ TracksItem *currentItem = static_cast<TracksItem*>(trackListing->firstChild());
+ while( currentItem )
+ {
+ currentItem->setChecked( true );
+ currentItem = static_cast<TracksItem*>(currentItem->nextSibling());
+ }
+}
+
+/**
+ * Turn off all of the tracks.
+ */
+void TracksImp::deselectAllTracks()
+{
+ TracksItem *currentItem = static_cast<TracksItem*>(trackListing->firstChild());
+ while( currentItem )
+ {
+ currentItem->setChecked( false );
+ currentItem = static_cast<TracksItem*>(currentItem->nextSibling());
+ }
+}
+
+/**
+ * Set the current stats for the new album being displayed.
+ */
+void TracksImp::newAlbum()
+{
+ QString albumText = cddbInfo.title;
+ if( !cddbInfo.artist.isEmpty() )
+ albumText = cddbInfo.artist + i18n( " - " ) + albumText;
+
+ albumName->setText( albumText );
+ trackListing->clear();
+ selectAllTracksButton->setEnabled(false);
+ deselectAllTracksButton->setEnabled(false);
+
+ emit( hasTracks(false) );
+
+ KCDDB::TrackInfoList t = cddbInfo.trackInfoList;
+
+ bool isSampler = true;
+ for( unsigned i = 0; i < t.count(); i++ )
+ {
+ if (t[i].title.find(" / ") == -1)
+ {
+ isSampler = false;
+ break;
+ }
+ }
+
+ TracksItem *last = 0;
+ for( unsigned i = 0; i < t.count(); i++ )
+ {
+ QString trackArtist;
+ QString title;
+
+ if( isSampler )
+ {
+ // Support for multiple artists stripping.
+ int delimiter = t[i].title.find(" / ");
+ Q_ASSERT( delimiter != -1 );
+ trackArtist = t[i].title.left(delimiter);
+ title = t[i].title.mid(delimiter + 3);
+ }
+ else {
+ trackArtist = cddbInfo.artist;
+ title = t[i].title;
+ }
+
+ // There is a new track for this title. Add it to the list of tracks.
+ QString trackLength = formatTime(cd->trackLength(i+1));
+ last = new TracksItem( trackListing, last, title, trackArtist, i+1, trackLength, t[i].extt );
+ }
+
+ if( t.count() )
+ {
+ // Set the current selected track to the first one.
+ trackListing->setCurrentItem(trackListing->firstChild());
+ selectAllTracksButton->setEnabled(true);
+ deselectAllTracksButton->setEnabled(true);
+ emit(hasTracks(true));
+ }
+}
+
+
+/**
+ * If the user presses the F2 key, trigger renaming of the title.
+ * @param event the QKeyEvent passed to this event handler.
+ */
+void TracksImp::keyPressEvent(QKeyEvent *event)
+{
+ QListViewItem *item = trackListing->selectedItem();
+ if( !item ) return;
+
+ if( event->key() == Qt::Key_F2 )
+ {
+ item->setRenameEnabled( HEADER_TRACK_NAME, true );
+ item->startRename( HEADER_TRACK_NAME );
+ event->accept();
+ }
+ else
+ Tracks::keyPressEvent(event);
+}
+
+/**
+ * Eject the current cd device
+ */
+void TracksImp::eject() {
+ ejectDevice(cd->device());
+}
+
+/**
+ * Eject a device
+ * @param deviceToEject the device to eject.
+ */
+void TracksImp::ejectDevice(const QString &deviceToEject) {
+ changeDevice(deviceToEject);
+
+ cd->eject();
+}
+
+#include "tracksimp.moc"
+
diff --git a/kaudiocreator/tracksimp.h b/kaudiocreator/tracksimp.h
new file mode 100644
index 00000000..f193ac3a
--- /dev/null
+++ b/kaudiocreator/tracksimp.h
@@ -0,0 +1,136 @@
+/**
+ * This file is part of the KAudioCreator package
+ * Copyright (C) 2003 Benjamin C Meyer (ben+kaudiocreator at meyerhome dot net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef TRACKSIMP_H
+#define TRACKSIMP_H
+
+#include "tracks.h"
+#include <kiconloader.h>
+#include <klistview.h>
+#include <klocale.h>
+
+// CDDB support via libkcddb
+#include <libkcddb/client.h>
+
+#define HEADER_RIP 0
+#define HEADER_TRACK 1
+#define HEADER_LENGTH 2
+#define HEADER_TRACK_NAME 3
+#define HEADER_TRACK_ARTIST 4
+#define HEADER_TRACK_COMMENT 5
+
+using namespace KCDDB;
+class Job;
+class KProcess;
+class KCompactDisc;
+
+class TracksItem : public KListViewItem
+{
+public:
+ TracksItem( KListView *parent, KListViewItem *after, QString t, QString a, int tr, QString l, QString c )
+ : KListViewItem( parent, after, QString::null/*rip*/, QString::number(tr), l, t )
+ {
+ m_title = t;
+ m_artist = a;
+ m_length = l;
+ m_track = tr;
+ m_comment = c;
+ m_checked = false;
+ }
+
+ QString title() const { return m_title; }
+ QString artist() const { return m_artist; }
+ int track() const { return m_track; }
+ QString length() const { return m_length; }
+ bool checked() const { return m_checked; }
+ QString comment() const { return m_comment; }
+ #include <kdebug.h>
+ void setTitle( const QString &t ) { m_title = t; kdDebug() << "title: " << m_title << endl; }
+ void setChecked( const bool &b ) {
+ m_checked = b;
+ b ? setPixmap( HEADER_RIP, SmallIcon( "apply", height()-2 ) ) :
+ setPixmap( HEADER_RIP, 0 );
+ }
+
+private:
+ QString m_title;
+ QString m_artist;
+ int m_track;
+ QString m_length;
+ QString m_comment;
+ bool m_checked; // marked for ripping
+};
+
+
+
+/**
+ * This class handles the display of the tracks. It also starts off the job que.
+ */
+class TracksImp : public Tracks {
+
+Q_OBJECT
+
+signals:
+ void ripTrack(Job *job);
+ void hasCD(bool);
+ void hasTracks(bool);
+
+public:
+ TracksImp( QWidget* parent = 0, const char* name = 0);
+ ~TracksImp();
+
+ bool hasCD();
+
+public slots:
+ void loadSettings();
+
+ // Toolbar Buttons
+ void startSession( int encoder = -1 );
+ void editInformation();
+ void performCDDB();
+ void ejectDevice(const QString &deviceToEject);
+ void eject();
+ void selectAllTracks();
+ void deselectAllTracks();
+
+private slots:
+ void changeDevice(const QString &file);
+ void keyPressEvent(QKeyEvent *event);
+ void lookupCDDBDone(CDDB::Result result);
+ void newDisc(unsigned discId);
+ void selectTrack(QListViewItem *);
+
+private:
+ void lookupDevice();
+ void lookupCDDB();
+ void newAlbum();
+ void ripWholeAlbum();
+ QPtrList<TracksItem> selectedTracks();
+
+ QString formatTime(unsigned ms);
+
+ KCDDB::Client *cddb;
+ KCompactDisc *cd;
+
+ // Current album
+ KCDDB::CDInfo cddbInfo;
+};
+
+#endif // TRACKSIMP_H
diff --git a/kaudiocreator/upgrade-kaudiocreator-metadata.sh b/kaudiocreator/upgrade-kaudiocreator-metadata.sh
new file mode 100755
index 00000000..a288c411
--- /dev/null
+++ b/kaudiocreator/upgrade-kaudiocreator-metadata.sh
@@ -0,0 +1,77 @@
+#! /usr/bin/env bash
+
+while read; do
+ if [ "${REPLY#\[}" != "$REPLY" ] ; then # group name
+ GROUP="${REPLY:1:${#REPLY}-2}"
+ continue;
+ fi
+ # normal key=value pair:
+ KEY="${REPLY%%=*}"
+ VALUE="${REPLY#*=}"
+
+ case "$GROUP/$KEY" in
+ encoderconfig/fileFormat)
+ VALUE=`echo $VALUE | sed -e s/%album/%{albumtitle}/g`;
+ VALUE=`echo $VALUE | sed -e s/%artist/%{albumartist}/g`;
+ VALUE=`echo $VALUE | sed -e s/%song/%{title}/g`;
+ VALUE=`echo $VALUE | sed -e s/%track/%{number}/g`;
+ VALUE=`echo $VALUE | sed -e s/%genre/%{genre}/g`;
+ VALUE=`echo $VALUE | sed -e s/%extension/%{extension}/g`;
+ VALUE=`echo $VALUE | sed -e s/%year/%{year}/g`;
+ echo "[encoderconfig]";
+ echo "fileFormat=$VALUE"
+ echo "# DELETE [encoderconfig]fileFormat"
+ ;;
+
+ Encoder_0/commandLine)
+ VALUE=`echo $VALUE | sed -e s/%album/%{albumtitle}/g`;
+ VALUE=`echo $VALUE | sed -e s/%artist/%{albumartist}/g`;
+ VALUE=`echo $VALUE | sed -e s/%song/%{title}/g`;
+ VALUE=`echo $VALUE | sed -e s/%track/%{number}/g`;
+ VALUE=`echo $VALUE | sed -e s/%genre/%{genre}/g`;
+ VALUE=`echo $VALUE | sed -e s/%extension/%{extension}/g`;
+ VALUE=`echo $VALUE | sed -e s/%year/%{year}/g`;
+ echo "[Encoder_0]";
+ echo "commandLine=$VALUE"
+ echo "# DELETE [Encoder_0]commandLine"
+ ;;
+
+ Encoder_1/commandLine)
+ VALUE=`echo $VALUE | sed -e s/%album/%{albumtitle}/g`;
+ VALUE=`echo $VALUE | sed -e s/%artist/%{albumartist}/g`;
+ VALUE=`echo $VALUE | sed -e s/%song/%{title}/g`;
+ VALUE=`echo $VALUE | sed -e s/%track/%{number}/g`;
+ VALUE=`echo $VALUE | sed -e s/%genre/%{genre}/g`;
+ VALUE=`echo $VALUE | sed -e s/%extension/%{extension}/g`;
+ VALUE=`echo $VALUE | sed -e s/%year/%{year}/g`;
+ echo "[Encoder_1]";
+ echo "commandLine=$VALUE"
+ echo "# DELETE [Encoder_1]commandLine"
+ ;;
+
+ Encoder_2/commandLine)
+ VALUE=`echo $VALUE | sed -e s/%album/%{albumtitle}/g`;
+ VALUE=`echo $VALUE | sed -e s/%artist/%{albumartist}/g`;
+ VALUE=`echo $VALUE | sed -e s/%song/%{title}/g`;
+ VALUE=`echo $VALUE | sed -e s/%track/%{number}/g`;
+ VALUE=`echo $VALUE | sed -e s/%genre/%{genre}/g`;
+ VALUE=`echo $VALUE | sed -e s/%extension/%{extension}/g`;
+ VALUE=`echo $VALUE | sed -e s/%year/%{year}/g`;
+ echo "[Encoder_2]";
+ echo "commandLine=$VALUE"
+ echo "# DELETE [Encoder_2]commandLine"
+ ;;
+ Encoder_3/commandLine)
+ VALUE=`echo $VALUE | sed -e s/%album/%{albumtitle}/g`;
+ VALUE=`echo $VALUE | sed -e s/%artist/%{albumartist}/g`;
+ VALUE=`echo $VALUE | sed -e s/%song/%{title}/g`;
+ VALUE=`echo $VALUE | sed -e s/%track/%{number}/g`;
+ VALUE=`echo $VALUE | sed -e s/%genre/%{genre}/g`;
+ VALUE=`echo $VALUE | sed -e s/%extension/%{extension}/g`;
+ VALUE=`echo $VALUE | sed -e s/%year/%{year}/g`;
+ echo "[Encoder_3]";
+ echo "commandLine=$VALUE"
+ echo "# DELETE [Encoder_3]commandLine"
+ ;;
+ esac
+done
diff --git a/kaudiocreator/wizard.ui b/kaudiocreator/wizard.ui
new file mode 100644
index 00000000..7658557b
--- /dev/null
+++ b/kaudiocreator/wizard.ui
@@ -0,0 +1,410 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>fileWizard</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>fileWizard</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>582</width>
+ <height>456</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="caption">
+ <string>File Location Wizard</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel8</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>When files have finished being processed, they are saved based upon the &lt;i&gt;File Location&lt;/i&gt;. Information about the track should be used within that text. There are eleven special words starting with a % that will be replaced with the corresponding track's information. Each of the buttons below will insert its replacement word into the &lt;i&gt;File Location&lt;/i&gt; where the cursor is. Use at least one replacement string to make sure that the &lt;i&gt;File Location&lt;/i&gt; is unique.</string>
+ </property>
+ <property name="textFormat">
+ <enum>RichText</enum>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignTop|AlignLeft</set>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout8</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel9</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;File location:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>playlistFormat</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>fileFormat</cstring>
+ </property>
+ <property name="text">
+ <string>~/%{extension}/%{artist}/%{album}/%{artist} - %{album}.m3u</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>exampleLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Example: ~/mp3/Staind/Lost Love/Staind - Lost Love.m3u</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout15</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton" row="0" column="1">
+ <property name="name">
+ <cstring>artistButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Artist</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="2">
+ <property name="name">
+ <cstring>homeDirButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Home Folder</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="3">
+ <property name="name">
+ <cstring>trackNumberButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Track Number</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="1">
+ <property name="name">
+ <cstring>extensionButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Extension</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="0">
+ <property name="name">
+ <cstring>trackTitleButton</cstring>
+ </property>
+ <property name="text">
+ <string>Track T&amp;itle</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="2">
+ <property name="name">
+ <cstring>trackCommentButton</cstring>
+ </property>
+ <property name="text">
+ <string>T&amp;rack Comment</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="0">
+ <property name="name">
+ <cstring>genreButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Genre</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="0" column="3">
+ <property name="name">
+ <cstring>yearButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Year</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="0" column="0">
+ <property name="name">
+ <cstring>albumButton</cstring>
+ </property>
+ <property name="text">
+ <string>Al&amp;bum</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>trackArtistButton</cstring>
+ </property>
+ <property name="text">
+ <string>Trac&amp;k Artist</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="0" column="2">
+ <property name="name">
+ <cstring>commentButton</cstring>
+ </property>
+ <property name="text">
+ <string>Co&amp;mment</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="Line">
+ <property name="name">
+ <cstring>Line2</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>Horizontal Spacing2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonOk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;OK</string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonCancel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Cancel</string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>buttonOk</sender>
+ <signal>clicked()</signal>
+ <receiver>fileWizard</receiver>
+ <slot>accept()</slot>
+ </connection>
+ <connection>
+ <sender>buttonCancel</sender>
+ <signal>clicked()</signal>
+ <receiver>fileWizard</receiver>
+ <slot>reject()</slot>
+ </connection>
+ <connection>
+ <sender>homeDirButton</sender>
+ <signal>clicked()</signal>
+ <receiver>fileWizard</receiver>
+ <slot>homePressed()</slot>
+ </connection>
+ <connection>
+ <sender>artistButton</sender>
+ <signal>clicked()</signal>
+ <receiver>fileWizard</receiver>
+ <slot>artistPressed()</slot>
+ </connection>
+ <connection>
+ <sender>trackNumberButton</sender>
+ <signal>clicked()</signal>
+ <receiver>fileWizard</receiver>
+ <slot>trackNumberPressed()</slot>
+ </connection>
+ <connection>
+ <sender>yearButton</sender>
+ <signal>clicked()</signal>
+ <receiver>fileWizard</receiver>
+ <slot>yearPressed()</slot>
+ </connection>
+ <connection>
+ <sender>extensionButton</sender>
+ <signal>clicked()</signal>
+ <receiver>fileWizard</receiver>
+ <slot>extensionPressed()</slot>
+ </connection>
+ <connection>
+ <sender>albumButton</sender>
+ <signal>clicked()</signal>
+ <receiver>fileWizard</receiver>
+ <slot>albumPressed()</slot>
+ </connection>
+ <connection>
+ <sender>trackTitleButton</sender>
+ <signal>clicked()</signal>
+ <receiver>fileWizard</receiver>
+ <slot>trackTitlePressed()</slot>
+ </connection>
+ <connection>
+ <sender>genreButton</sender>
+ <signal>clicked()</signal>
+ <receiver>fileWizard</receiver>
+ <slot>genrePressed()</slot>
+ </connection>
+ <connection>
+ <sender>fileFormat</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>fileWizard</receiver>
+ <slot>fileFormatTextChanged(const QString&amp;)</slot>
+ </connection>
+ <connection>
+ <sender>trackArtistButton</sender>
+ <signal>clicked()</signal>
+ <receiver>fileWizard</receiver>
+ <slot>trackArtistPressed()</slot>
+ </connection>
+ <connection>
+ <sender>trackCommentButton</sender>
+ <signal>clicked()</signal>
+ <receiver>fileWizard</receiver>
+ <slot>trackCommentPressed()</slot>
+ </connection>
+ <connection>
+ <sender>commentButton</sender>
+ <signal>clicked()</signal>
+ <receiver>fileWizard</receiver>
+ <slot>commentPressed()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>fileFormat</tabstop>
+ <tabstop>albumButton</tabstop>
+ <tabstop>artistButton</tabstop>
+ <tabstop>commentButton</tabstop>
+ <tabstop>yearButton</tabstop>
+ <tabstop>trackNumberButton</tabstop>
+ <tabstop>trackArtistButton</tabstop>
+ <tabstop>trackCommentButton</tabstop>
+ <tabstop>trackTitleButton</tabstop>
+ <tabstop>genreButton</tabstop>
+ <tabstop>extensionButton</tabstop>
+ <tabstop>homeDirButton</tabstop>
+ <tabstop>buttonOk</tabstop>
+ <tabstop>buttonCancel</tabstop>
+</tabstops>
+<includes>
+ <include location="local" impldecl="in implementation">wizard.ui.h</include>
+</includes>
+<slots>
+ <slot access="private">homePressed()</slot>
+ <slot>extensionPressed()</slot>
+ <slot access="private">trackTitlePressed()</slot>
+ <slot>trackArtistPressed()</slot>
+ <slot access="private">trackNumberPressed()</slot>
+ <slot>trackCommentPressed()</slot>
+ <slot access="private">yearPressed()</slot>
+ <slot>genrePressed()</slot>
+ <slot>albumPressed()</slot>
+ <slot>artistPressed()</slot>
+ <slot>commentPressed()</slot>
+ <slot>fileFormatTextChanged( const QString &amp; text )</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kaudiocreator/wizard.ui.h b/kaudiocreator/wizard.ui.h
new file mode 100644
index 00000000..95b26b7b
--- /dev/null
+++ b/kaudiocreator/wizard.ui.h
@@ -0,0 +1,110 @@
+/**
+ * This file is part of the KAudioCreator package
+ * Copyright (C) 2003 Benjamin C Meyer (ben+kaudiocreator at meyerhome dot net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename slots use Qt Designer which will
+** update this file, preserving your code. Create an init() slot in place of
+** a constructor, and a destroy() slot in place of a destructor.
+*****************************************************************************/
+
+#include <qstring.h>
+#include <qregexp.h>
+#include <qmap.h>
+#include <klocale.h>
+#include "job.h"
+
+void fileWizard::homePressed()
+{
+ fileFormat->insert("~");
+}
+
+void fileWizard::extensionPressed()
+{
+ fileFormat->insert("%{extension}");
+}
+
+void fileWizard::trackTitlePressed()
+{
+ fileFormat->insert("%{title}");
+}
+
+void fileWizard::trackArtistPressed()
+{
+ fileFormat->insert("%{artist}");
+}
+
+void fileWizard::trackNumberPressed()
+{
+ fileFormat->insert("%{number}");
+}
+
+void fileWizard::trackCommentPressed()
+{
+ fileFormat->insert("%{comment}");
+}
+
+void fileWizard::yearPressed()
+{
+ fileFormat->insert("%{year}");
+}
+
+void fileWizard::genrePressed()
+{
+ fileFormat->insert("%{genre}");
+}
+
+void fileWizard::albumPressed()
+{
+ fileFormat->insert("%{albumtitle}");
+}
+
+void fileWizard::artistPressed()
+{
+ fileFormat->insert("%{albumartist}");
+}
+
+void fileWizard::commentPressed()
+{
+ fileFormat->insert("%{albumcomment}");
+}
+
+void fileWizard::fileFormatTextChanged(const QString& text)
+{
+ QString string = text;
+ string.replace(QRegExp("~"), "/home/foo");
+ Job job;
+ job.genre = "Rock";
+ job.group = "J Rocker";
+ job.album = "Why Rain";
+ job.year = 2002;
+ job.track = 9;
+ job.comment = "This Album rocks!";
+
+ job.track_title = "Time";
+ job.track_artist = "John Rocker";
+ job.track_comment = "This Song Rocks!";
+ QMap<QString,QString> map;
+ map.insert("extension", "mp3");
+ string = job.replaceSpecialChars(string, false, map);
+ exampleLabel->setText(i18n("Example: %1").arg(string));
+}
+
diff --git a/kdemultimedia.lsm b/kdemultimedia.lsm
new file mode 100644
index 00000000..c09c2c72
--- /dev/null
+++ b/kdemultimedia.lsm
@@ -0,0 +1,11 @@
+Begin4
+Title: kdemultimedia
+Version: 3.5.10
+Entered-date: 2008-08-26
+Description: Multimedia written for the K Desktop Environment (KDE)
+Keywords: KDE X11 desktop Qt
+Author: http://bugs.kde.org/ (KDE Bugtracking System)
+Primary-site: http://www.kde.org/download/
+Platforms: Unix, Qt
+Copying-policy: GPL, Artistic
+End
diff --git a/kfile-plugins/Makefile.am b/kfile-plugins/Makefile.am
new file mode 100644
index 00000000..fc127f02
--- /dev/null
+++ b/kfile-plugins/Makefile.am
@@ -0,0 +1,22 @@
+if include_ogg_SUBDIR
+KFILE_OGG_SUBDIR=ogg
+endif
+
+if include_flac_SUBDIR
+KFILE_FLAC_SUBDIR=flac
+endif
+
+if include_mp3_SUBDIR
+KFILE_MP3_SUBDIR=mp3
+endif
+
+if include_mpc_SUBDIR
+KFILE_MPC_SUBDIR=mpc
+endif
+
+if include_theora_SUBDIR
+KFILE_THEORA_SUBDIR=theora
+endif
+
+SUBDIRS=m3u $(KFILE_MP3_SUBDIR) $(KFILE_MPC_SUBDIR) au avi mpeg wav sid $(KFILE_OGG_SUBDIR) \
+ $(KFILE_FLAC_SUBDIR) $(KFILE_THEORA_SUBDIR)
diff --git a/kfile-plugins/RETURNED_ITEMS b/kfile-plugins/RETURNED_ITEMS
new file mode 100644
index 00000000..ffe878d3
--- /dev/null
+++ b/kfile-plugins/RETURNED_ITEMS
@@ -0,0 +1,86 @@
+If you add a new plugin here, add the list of returned items to this file.
+
+The returned items are:
+
+
+mp3 plugin:
+===========
+
+type key W/A details
+------------------------------------------------------------------------
+String Title +/+ only if id3v1 tag exists / max. 30 characters
+String Artist +/+ only if id3v1 tag exists / max. 30 characters
+String Album +/+ only if id3v1 tag exists / max. 30 characters
+String Comment +/+ only if id3v1 tag exists / max. 28 characters
+String Date +/+ only if id3v1 tag exists / max. 4 characters
+Int Tracknumber +/+ only if id3v1.1 tag exists / 0-255
+Bool CRC -/-
+Bool Original -/-
+Bool Copyright -/-
+String Length -/-
+Int Bitrate -/- in kbps
+Int Frequency -/- in Hz
+Double Version -/- mpeg version, 1, 2, or 2.5
+Int Layer -/- 1, 2 or 3
+Int Channels -/- number of audio channels, 1 for mono, 2 stereo
+
+type is the QVariant::type() of that key.
+W/A is writable/addable, - means no, + means yes. If a key is addable, it's
+also removable
+
+ogg plugin:
+===========
+
+type key W/A details
+------------------------------------------------------------------------
+Int Version -/-
+Int Channels -/-
+Int Bitrate upper -/- might also be a string containing i18n("none")
+Int Bitrate lower -/- might also be a string containing i18n("none")
+Int Bitrate nominal -/- might also be a string containing i18n("none")
+Int Bitrate -/- average bitrate
+String Length -/-
+
+Other keys corresponding to the vorbis comment keys are returned as editable
+String. If there are several equal vorbis comment keys, e.g. 3 Artists, the
+first one is called "Artist", the second one (not yet) "Artist(2)" and so on.
+
+Common keys that are recommended in the vorbiscomment docs:
+Title, Version, Album, Tracknumber, Artist, Organization, Description, Genre,
+Date, Location, Copyright, Isirc
+
+
+
+au plugin:
+===========
+
+type key W/A details
+------------------------------------------------------------------------
+Int Length -/- Length in seconds
+Int Sample Rate -/- Sample rate of sample in Hz
+Int Channels -/- No. of channels
+String Encoding -/- Data encoding (e.g. 8-bit ISDN u-law)
+
+
+avi plugin:
+===========
+
+type key W/A details
+------------------------------------------------------------------------
+Int Length -/- Length in seconds/minutes/hours
+Size Resolution -/- Resolution in pixels
+Int Frame rate -/- Frame rate
+String Video codec -/- The video codec symbol (e.g. mp42 or div3)
+
+
+theora plugin:
+===========
+
+type key W/A details
+------------------------------------------------------------------------
+Int Length -/- Length in seconds/minutes/hours
+Size Resolution -/- Resolution in pixels
+Int Frame rate -/- Frame rate
+Int Quality -/- Quality of the encoding
+Int Channels -/- Number of audio channels
+Int Sample rate -/- Sample rate of sample in Hz
diff --git a/kfile-plugins/au/Makefile.am b/kfile-plugins/au/Makefile.am
new file mode 100644
index 00000000..4dc86f65
--- /dev/null
+++ b/kfile-plugins/au/Makefile.am
@@ -0,0 +1,22 @@
+## Makefile.am for au file meta info plugin
+
+# set the include path for X, qt and KDE
+INCLUDES = $(all_includes)
+
+# these are the headers for your project
+noinst_HEADERS = kfile_au.h
+
+kde_module_LTLIBRARIES = kfile_au.la
+
+kfile_au_la_SOURCES = kfile_au.cpp
+kfile_au_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+kfile_au_la_LIBADD = $(LIB_KIO)
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
+
+messages:
+ $(XGETTEXT) kfile_au.cpp -o $(podir)/kfile_au.pot
+
+services_DATA = kfile_au.desktop
+servicesdir = $(kde_servicesdir)
diff --git a/kfile-plugins/au/kfile_au.cpp b/kfile-plugins/au/kfile_au.cpp
new file mode 100644
index 00000000..084bd669
--- /dev/null
+++ b/kfile-plugins/au/kfile_au.cpp
@@ -0,0 +1,172 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2002 Shane Wright <me@shanewright.co.uk>
+ *
+ * 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 version 2.
+ *
+ * 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+#include "kfile_au.h"
+
+#include <kprocess.h>
+#include <klocale.h>
+#include <kgenericfactory.h>
+#include <kstringvalidator.h>
+#include <kdebug.h>
+
+#include <qdict.h>
+#include <qvalidator.h>
+#include <qcstring.h>
+#include <qfile.h>
+#include <qdatetime.h>
+
+#if !defined(__osf__)
+#include <inttypes.h>
+#else
+typedef unsigned long uint32_t;
+typedef unsigned short uint16_t;
+#endif
+
+typedef KGenericFactory<KAuPlugin> AuFactory;
+
+K_EXPORT_COMPONENT_FACTORY(kfile_au, AuFactory( "kfile_au" ))
+
+KAuPlugin::KAuPlugin(QObject *parent, const char *name,
+ const QStringList &args)
+
+ : KFilePlugin(parent, name, args)
+{
+ KFileMimeTypeInfo* info = addMimeTypeInfo( "audio/basic" );
+
+ KFileMimeTypeInfo::GroupInfo* group = 0L;
+
+ group = addGroupInfo(info, "Technical", i18n("Technical Details"));
+
+ KFileMimeTypeInfo::ItemInfo* item;
+
+ item = addItemInfo(group, "Length", i18n("Length"), QVariant::Int);
+ setSuffix(item, "s");
+
+ item = addItemInfo(group, "Sample Rate", i18n("Sample Rate"), QVariant::Int);
+ setSuffix(item, "Hz");
+
+ item = addItemInfo(group, "Channels", i18n("Channels"), QVariant::Int);
+
+ item = addItemInfo(group, "Encoding", i18n("Encoding"), QVariant::String);
+
+}
+
+bool KAuPlugin::readInfo( KFileMetaInfo& info, uint what)
+{
+ // the file signature, wants to be tidier...
+ const char fsig[] = { 0x2e, 0x73, 0x6e, 0x64 };
+
+ // a dword buffer for input
+ char inbuf[4];
+
+ // some vars for the file properties
+ uint32_t datasize;
+ uint32_t encoding;
+ uint32_t samplerate;
+ uint32_t channels;
+ uint16_t bytespersample;
+
+ if ( info.path().isEmpty() ) // remote file
+ return false;
+
+ QFile file(info.path());
+
+ if (!file.open(IO_ReadOnly))
+ {
+ kdDebug(7034) << "Couldn't open " << QFile::encodeName(info.path()) << endl;
+ return false;
+ }
+
+ QDataStream dstream(&file);
+
+ // AU files are big-endian
+ dstream.setByteOrder(QDataStream::BigEndian);
+
+
+ // Read and verify the signature
+ dstream.readRawBytes(inbuf, 4);
+ if (memcmp(fsig, inbuf, 4))
+ return false;
+
+ // skip unwanted bits
+ file.at(8);
+
+ // grab the bits we want
+ dstream >> datasize;
+ dstream >> encoding;
+ dstream >> samplerate;
+ dstream >> channels;
+
+ // add the info
+ KFileMetaInfoGroup group = appendGroup(info, "Technical");
+ appendItem(group, "Sample Rate", (uint) samplerate);
+ appendItem(group, "Channels", (uint) channels);
+
+ // work out the encoding
+ switch (encoding) {
+ case 1 :
+ appendItem(group, "Encoding", i18n("8-bit ISDN u-law"));
+ bytespersample = 1;
+ break;
+ case 2 :
+ appendItem(group, "Encoding", i18n("8-bit linear PCM [REF-PCM]"));
+ bytespersample = 1;
+ break;
+ case 3 :
+ appendItem(group, "Encoding", i18n("16-bit linear PCM"));
+ bytespersample = 2;
+ break;
+ case 4 :
+ appendItem(group, "Encoding", i18n("24-bit linear PCM"));
+ bytespersample = 3;
+ break;
+ case 5 :
+ appendItem(group, "Encoding", i18n("32-bit linear PCM"));
+ bytespersample = 4;
+ break;
+ case 6 :
+ appendItem(group, "Encoding", i18n("32-bit IEEE floating point"));
+ bytespersample = 4;
+ break;
+ case 7 :
+ appendItem(group, "Encoding", i18n("64-bit IEEE floating point"));
+ bytespersample = 8;
+ break;
+ case 23 :
+ appendItem(group, "Encoding", i18n("8-bit ISDN u-law compressed"));
+ bytespersample = 1;
+ break;
+ default :
+ appendItem(group, "Encoding", i18n("Unknown"));
+ bytespersample = 0;
+ }
+
+ // work out length from bytespersample + channels + size
+ if ((channels > 0) && (datasize > 0) && (datasize != 0xFFFFFFFF) && (bytespersample > 0) && (samplerate > 0)) {
+ uint32_t length = datasize / channels / bytespersample / samplerate;
+ appendItem(group, "Length", (uint) length);
+ } else {
+ appendItem(group, "Length", "???");
+ }
+
+ return true;
+}
+
+#include "kfile_au.moc"
diff --git a/kfile-plugins/au/kfile_au.desktop b/kfile-plugins/au/kfile_au.desktop
new file mode 100644
index 00000000..44256bd3
--- /dev/null
+++ b/kfile-plugins/au/kfile_au.desktop
@@ -0,0 +1,67 @@
+[Desktop Entry]
+Type=Service
+Name=AU Info
+Name[af]=Au Inligting
+Name[bg]=Информация за AU
+Name[br]=Titouroù AU
+Name[bs]=AU informacije
+Name[ca]=Informació AU
+Name[cs]=AU info
+Name[cy]=Gwybodaeth AU
+Name[da]=AU-info
+Name[de]=AU-Info
+Name[el]=Πληροφορίες AU
+Name[eo]=AU-informo
+Name[es]=Info AU
+Name[et]=AU info
+Name[eu]=AU informazioa
+Name[fa]=اطلاعات AU
+Name[fi]=AU-tiedot
+Name[fr]=Informations AU
+Name[gl]=Información AU
+Name[he]=מידע AU
+Name[hi]=AU जानकारी
+Name[hr]=AU Informacije
+Name[hu]=AU-jellemzők
+Name[is]=AU upplýsingar
+Name[it]=Informazioni AU
+Name[ja]=AU 情報
+Name[kk]=AU мәліметі
+Name[km]=ព័ត៌មាន AU
+Name[ko]=AU 정보
+Name[lt]=AU informacija
+Name[mk]=AU информации
+Name[nb]=AU informasjon
+Name[nds]=AU-Info
+Name[ne]=AU सूचना
+Name[nl]=AU-informatie
+Name[nn]=AU-info
+Name[pa]=AU ਜਾਣਕਾਰੀ
+Name[pl]=Informacja o pliku AU
+Name[pt]=Informação do AU
+Name[pt_BR]=Informação sobre AU
+Name[ro]=Informaţii AU
+Name[ru]=Сведения о AU
+Name[se]=AU-dieđut
+Name[sk]=AU info
+Name[sl]=Podatki o AU
+Name[sr]=Информације о AU-у
+Name[sr@Latn]=Informacije o AU-u
+Name[sv]=AU-information
+Name[ta]=AU தகவல்
+Name[tg]=AU Ахборот
+Name[th]=ข้อมูล AU
+Name[tr]=AU Bilgisi
+Name[uk]=Інформація по AU
+Name[uz]=XBM haqida maʼlumot
+Name[uz@cyrillic]=XBM ҳақида маълумот
+Name[xh]=Ulwazi lwe AU
+Name[zh_CN]=AU 信息
+Name[zh_HK]=AU 資訊
+Name[zh_TW]=AU 資訊
+Name[zu]=Ulwazi lwe-AU
+ServiceTypes=KFilePlugin
+X-KDE-Library=kfile_au
+MimeType=audio/basic
+PreferredGrous=Technical
+PreferredItems=Length,Sample Rate,Channels,Encoding
diff --git a/kfile-plugins/au/kfile_au.h b/kfile-plugins/au/kfile_au.h
new file mode 100644
index 00000000..0d39e477
--- /dev/null
+++ b/kfile-plugins/au/kfile_au.h
@@ -0,0 +1,37 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2002 Shane Wright <me@shanewright.co.uk>
+ *
+ * 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 version 2.
+ *
+ * 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __KFILE_AU_H__
+#define __KFILE_AU_H__
+
+#include <kfilemetainfo.h>
+
+class QStringList;
+
+class KAuPlugin: public KFilePlugin
+{
+ Q_OBJECT
+
+public:
+ KAuPlugin( QObject *parent, const char *name, const QStringList& args );
+
+ virtual bool readInfo( KFileMetaInfo& info, uint what);
+};
+
+#endif
diff --git a/kfile-plugins/avi/Makefile.am b/kfile-plugins/avi/Makefile.am
new file mode 100644
index 00000000..9976e127
--- /dev/null
+++ b/kfile-plugins/avi/Makefile.am
@@ -0,0 +1,22 @@
+## Makefile.am for avi file meta info plugin
+
+# set the include path for X, qt and KDE
+INCLUDES = $(all_includes)
+
+# these are the headers for your project
+noinst_HEADERS = kfile_avi.h
+
+kde_module_LTLIBRARIES = kfile_avi.la
+
+kfile_avi_la_SOURCES = kfile_avi.cpp
+kfile_avi_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+kfile_avi_la_LIBADD = $(LIB_KIO)
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
+
+messages: rc.cpp
+ $(XGETTEXT) kfile_avi.cpp -o $(podir)/kfile_avi.pot
+
+services_DATA = kfile_avi.desktop
+servicesdir = $(kde_servicesdir)
diff --git a/kfile-plugins/avi/kfile_avi.cpp b/kfile-plugins/avi/kfile_avi.cpp
new file mode 100644
index 00000000..ee30ffee
--- /dev/null
+++ b/kfile-plugins/avi/kfile_avi.cpp
@@ -0,0 +1,540 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2002 Shane Wright <me@shanewright.co.uk>
+ *
+ * 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 version 2.
+ *
+ * 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#include <config.h>
+#include "kfile_avi.h"
+
+#include <kprocess.h>
+#include <klocale.h>
+#include <kgenericfactory.h>
+#include <kstringvalidator.h>
+#include <kdebug.h>
+
+#include <qdict.h>
+#include <qvalidator.h>
+#include <qcstring.h>
+#include <qfile.h>
+#include <qdatetime.h>
+
+#if !defined(__osf__)
+#include <inttypes.h>
+#else
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+typedef unsigned long long uint64_t;
+#endif
+
+typedef KGenericFactory<KAviPlugin> AviFactory;
+
+K_EXPORT_COMPONENT_FACTORY(kfile_avi, AviFactory( "kfile_avi" ))
+
+KAviPlugin::KAviPlugin(QObject *parent, const char *name,
+ const QStringList &args)
+
+ : KFilePlugin(parent, name, args)
+{
+ KFileMimeTypeInfo* info = addMimeTypeInfo( "video/x-msvideo" );
+
+ KFileMimeTypeInfo::GroupInfo* group = 0L;
+
+ group = addGroupInfo(info, "Technical", i18n("Technical Details"));
+
+ KFileMimeTypeInfo::ItemInfo* item;
+
+ item = addItemInfo(group, "Length", i18n("Length"), QVariant::Int);
+ setUnit(item, KFileMimeTypeInfo::Seconds);
+
+ item = addItemInfo(group, "Resolution", i18n("Resolution"), QVariant::Size);
+
+ item = addItemInfo(group, "Frame rate", i18n("Frame Rate"), QVariant::Int);
+ setSuffix(item, i18n("fps"));
+
+ item = addItemInfo(group, "Video codec", i18n("Video Codec"), QVariant::String);
+ item = addItemInfo(group, "Audio codec", i18n("Audio Codec"), QVariant::String);
+
+}
+
+bool KAviPlugin::read_avi()
+{
+ static const char sig_riff[] = "RIFF";
+ static const char sig_avi[] = "AVI ";
+ static const char sig_list[] = "LIST";
+ static const char sig_junk[] = "JUNK";
+ uint32_t dwbuf1;
+
+ done_avih = false;
+ done_audio = false;
+
+ // read AVI header
+ char charbuf1[5];
+ charbuf1[4] = '\0';
+
+ // this must be RIFF
+ f.readBlock(charbuf1, 4);
+ if (memcmp(charbuf1, sig_riff, 4) != 0)
+ return false;
+
+ dstream >> dwbuf1;
+
+ // this must be AVI
+ f.readBlock(charbuf1, 4);
+ if (memcmp(charbuf1, sig_avi, 4) != 0)
+ return false;
+
+
+ // start reading AVI file
+ int counter = 0;
+ bool done = false;
+ do {
+
+ // read header
+ f.readBlock(charbuf1, 4);
+
+ kdDebug(7034) << "about to handle chunk with ID: " << charbuf1 << "\n";
+
+ if (memcmp(charbuf1, sig_list, 4) == 0) {
+ // if list
+ if (!read_list())
+ return false;
+
+ } else if (memcmp(charbuf1, sig_junk, 4) == 0) {
+ // if junk
+
+ // read chunk size
+ dstream >> dwbuf1;
+
+ kdDebug(7034) << "Skipping junk chunk length: " << dwbuf1 << "\n";
+
+ // skip junk
+ f.at( f.at() + dwbuf1 );
+
+ } else {
+ // something we dont understand yet
+ kdDebug(7034) << "Unknown chunk header found: " << charbuf1 << "\n";
+ return false;
+ };
+
+ if (
+ ((done_avih) && (strlen(handler_vids) > 0) && (done_audio)) ||
+ f.atEnd()) {
+ kdDebug(7034) << "We're done!\n";
+ done = true;
+ }
+
+ // make sure we dont stay here forever
+ ++counter;
+ if (counter > 10)
+ done = true;
+
+ } while (!done);
+
+ return true;
+}
+
+
+bool KAviPlugin::read_list()
+{
+ const char sig_hdrl[] = "hdrl"; // header list
+ const char sig_strl[] = "strl"; // ...list
+ const char sig_movi[] = "movi"; // movie list
+
+ uint32_t dwbuf1;
+ char charbuf1[5];
+ charbuf1[4] = '\0';
+
+ kdDebug(7034) << "In read_list()\n";
+
+ // read size & list type
+ dstream >> dwbuf1;
+ f.readBlock(charbuf1, 4);
+
+ // read the relevant bits of the list
+ if (memcmp(charbuf1, sig_hdrl, 4) == 0) {
+ // should be the main AVI header
+ if (!read_avih())
+ return false;
+
+ } else if (memcmp(charbuf1, sig_strl, 4) == 0) {
+ // should be some stream info
+ if (!read_strl())
+ return false;
+
+ } else if (memcmp(charbuf1, sig_movi, 4) == 0) {
+ // movie list
+
+ kdDebug(7034) << "Skipping movi chunk length: " << dwbuf1 << "\n";
+
+ // skip past it
+ f.at( f.at() + dwbuf1 );
+
+ } else {
+ // unknown list type
+ kdDebug(7034) << "Unknown list type found: " << charbuf1 << "\n";
+ }
+
+ return true;
+}
+
+
+bool KAviPlugin::read_avih()
+{
+ static const char sig_avih[] = "avih"; // header list
+
+ uint32_t dwbuf1;
+ char charbuf1[5];
+
+ // read header and length
+ f.readBlock(charbuf1, 4);
+ dstream >> dwbuf1;
+
+ // not a valid avih?
+ if (memcmp(charbuf1, sig_avih, 4) != 0) {
+ kdDebug(7034) << "Chunk ID error, expected avih, got: " << charbuf1 << "\n";
+ return false;
+ }
+
+ // read all the avih fields
+ dstream >> avih_microsecperframe;
+ dstream >> avih_maxbytespersec;
+ dstream >> avih_reserved1;
+ dstream >> avih_flags;
+ dstream >> avih_totalframes;
+ dstream >> avih_initialframes;
+ dstream >> avih_streams;
+ dstream >> avih_buffersize;
+ dstream >> avih_width;
+ dstream >> avih_height;
+ dstream >> avih_scale;
+ dstream >> avih_rate;
+ dstream >> avih_start;
+ dstream >> avih_length;
+
+ done_avih = true;
+
+ return true;
+}
+
+
+bool KAviPlugin::read_strl()
+{
+ static const char sig_strh[] = "strh";
+ static const char sig_strf[] = "strf";
+ //static const char sig_strd[] = "strd";
+ static const char sig_strn[] = "strn";
+ static const char sig_list[] = "LIST";
+ static const char sig_junk[] = "JUNK";
+
+ kdDebug(7034) << "in strl handler\n";
+
+ uint32_t dwbuf1; // buffer for block sizes
+ char charbuf1[5];
+
+ // loop through blocks
+ int counter = 0;
+ while (true) {
+
+ // read type and size
+ f.readBlock(charbuf1, 4); // type
+ dstream >> dwbuf1; // size
+
+ // detect type
+ if (memcmp(charbuf1, sig_strh, 4) == 0) {
+ // got strh - stream header
+ kdDebug(7034) << "Found strh, calling read_strh()\n";
+ read_strh(dwbuf1);
+
+ } else if (memcmp(charbuf1, sig_strf, 4) == 0) {
+ // got strf - stream format
+ kdDebug(7034) << "Found strf, calling read_strf()\n";
+ read_strf(dwbuf1);
+
+ } else if (memcmp(charbuf1, sig_strn, 4) == 0) {
+ // we ignore strn, but it can be recorded incorrectly so we have to cope especially
+
+ // skip it
+ kdDebug(7034) << "Skipping strn chunk length: " << dwbuf1 << "\n";
+ f.at( f.at() + dwbuf1 );
+
+ /*
+ this is a pretty annoying hack; many AVIs incorrectly report the
+ length of the strn field by 1 byte. Its possible that strn's
+ should be word aligned, but no mention in the specs...
+
+ I'll clean/optimise this a touch soon
+ */
+
+ bool done = false;
+ unsigned char counter = 0;
+ while (!done) {
+ // read next marker
+ f.readBlock(charbuf1, 4);
+
+ // does it look ok?
+ if ((memcmp(charbuf1, sig_list, 4) == 0) ||
+ (memcmp(charbuf1, sig_junk, 4) == 0)) {
+ // yes, go back before it
+ f.at( f.at() - 4);
+ done = true;
+ } else {
+ // no, skip one space forward from where we were
+ f.at( f.at() - 3);
+ kdDebug(7034) << "Working around incorrectly marked strn length..." << "\n";
+ }
+
+ // make sure we don't stay here too long
+ ++counter;
+ if (counter>10)
+ done = true;
+ }
+
+ } else if ((memcmp(charbuf1, sig_list, 4) == 0) || (memcmp(charbuf1, sig_junk, 4) == 0)) {
+ // we have come to the end of our stay here in strl, time to leave
+
+ kdDebug(7034) << "Found LIST/JUNK, returning...\n";
+
+ // rollback before the id and size
+ f.at( f.at() - 8 );
+
+ // return back to the main avi parser
+ return true;
+
+ } else {
+ // we have some other unrecognised block type
+
+ kdDebug(7034) << "Sskipping unrecognised block\n";
+ // just skip over it
+ f.at( f.at() + dwbuf1);
+
+ } /* switch block type */
+
+ ++counter;
+ if (counter > 10)
+ return true;
+
+ } /* while (true) */
+
+ // we should never get here
+}
+
+
+bool KAviPlugin::read_strh(uint32_t blocksize)
+{
+ static const char sig_vids[] = "vids"; // ...video
+ static const char sig_auds[] = "auds"; // ...audio
+
+ uint32_t strh_flags;
+ uint32_t strh_reserved1;
+ uint32_t strh_initialframes;
+ uint32_t strh_scale;
+ uint32_t strh_rate;
+ uint32_t strh_start;
+ uint32_t strh_length;
+ uint32_t strh_buffersize;
+ uint32_t strh_quality;
+ uint32_t strh_samplesize;
+
+ char charbuf1[5];
+ char charbuf2[5];
+
+
+ // get stream info type, and handler id
+ f.readBlock(charbuf1, 4);
+ f.readBlock(charbuf2, 4);
+
+ // read the strh fields
+ dstream >> strh_flags;
+ dstream >> strh_reserved1;
+ dstream >> strh_initialframes;
+ dstream >> strh_scale;
+ dstream >> strh_rate;
+ dstream >> strh_start;
+ dstream >> strh_length;
+ dstream >> strh_buffersize;
+ dstream >> strh_quality;
+ dstream >> strh_samplesize;
+
+ if (memcmp(&charbuf1, sig_vids, 4) == 0) {
+ // we are video!
+
+ // save the handler
+ memcpy(handler_vids, charbuf2, 4);
+ kdDebug(7034) << "Video handler: " << handler_vids << "\n";
+
+
+ } else if (memcmp(&charbuf1, sig_auds, 4) == 0) {
+ // we are audio!
+
+ // save the handler
+ memcpy(handler_auds, charbuf2, 4);
+ kdDebug(7034) << "Audio handler: " << handler_auds << "\n";
+
+ // we want strf to get the audio codec
+ wantstrf = true;
+
+ } else {
+ // we are something that we don't understand
+
+ }
+
+ // do we need to skip ahead any more? (usually yes , contrary to
+ // the AVI specs I've read...)
+ // note: 48 is 10 * uint32_t + 2*FOURCC; the 10 fields we read above, plus the two character fields
+ if (blocksize > 48)
+ f.at( f.at() + (blocksize - 48) );
+
+ return true;
+}
+
+
+bool KAviPlugin::read_strf(uint32_t blocksize)
+{
+ // do we want to do the strf?
+ if (wantstrf) {
+ // yes. we want the audio codec identifier out of it
+
+ // get the 16bit audio codec ID
+ dstream >> handler_audio;
+ kdDebug(7034) << "Read audio codec ID: " << handler_audio << "\n";
+ // skip past the rest of the stuff here for now
+ f.at( f.at() + blocksize - 2);
+ // we have audio
+ done_audio = true;
+
+ } else {
+ // no, skip the strf
+ f.at( f.at() + blocksize );
+ }
+
+ return true;
+}
+
+
+
+const char * KAviPlugin::resolve_audio(uint16_t id)
+{
+ /*
+ this really wants to use some sort of KDE global
+ list. To avoid bloat for the moment it only does
+ a few common codecs
+ */
+
+ static const char codec_unknown[] = I18N_NOOP("Unknown");
+ static const char codec_01[] = "Microsoft PCM";
+ static const char codec_02[] = "Microsoft ADPCM";
+ static const char codec_50[] = "MPEG";
+ static const char codec_55[] = "MP3";
+ static const char codec_92[] = "AC3";
+ static const char codec_160[] = "WMA1";
+ static const char codec_161[] = "WMA2";
+ static const char codec_162[] = "WMA3";
+ static const char codec_2000[] = "DVM";
+ switch (id) {
+ case 0x000 : return codec_unknown; break;
+ case 0x001 : return codec_01; break;
+ case 0x002 : return codec_02; break;
+ case 0x050 : return codec_50; break;
+ case 0x055 : return codec_55; break;
+ case 0x092 : return codec_92; break;
+ case 0x160 : return codec_160; break;
+ case 0x161 : return codec_161; break;
+ case 0x162 : return codec_162; break;
+ case 0x2000 : return codec_2000; break;
+ default : return codec_unknown;
+ }
+
+ return NULL;
+}
+
+
+bool KAviPlugin::readInfo( KFileMetaInfo& info, uint /*what*/)
+{
+ /***************************************************/
+ // prep
+
+ memset(handler_vids, 0x00, 5);
+ memset(handler_auds, 0x00, 5);
+
+
+ /***************************************************/
+ // sort out the file
+
+ if (f.isOpen())
+ f.close();
+
+ if ( info.path().isEmpty() ) // remote file
+ return false;
+
+ f.setName(info.path());
+
+ // open file, set up stream and set endianness
+ if (!f.open(IO_ReadOnly))
+ {
+ kdDebug(7034) << "Couldn't open " << QFile::encodeName(info.path()) << endl;
+ return false;
+ }
+ //QDataStream dstream(&file);
+ dstream.setDevice(&f);
+
+ dstream.setByteOrder(QDataStream::LittleEndian);
+
+
+ /***************************************************/
+ // start reading stuff from it
+
+ wantstrf = false;
+
+ if (!read_avi()) {
+ kdDebug(7034) << "read_avi() failed!" << endl;
+ }
+
+ /***************************************************/
+ // set up our output
+
+ if (done_avih) {
+
+ KFileMetaInfoGroup group = appendGroup(info, "Technical");
+
+ if (0 != avih_microsecperframe) {
+ appendItem(group, "Frame rate", int(1000000 / avih_microsecperframe));
+ }
+ appendItem(group, "Resolution", QSize(avih_width, avih_height));
+
+ // work out and add length
+ uint64_t mylength = (uint64_t) ((float) avih_totalframes * (float) avih_microsecperframe / 1000000.0);
+ appendItem(group, "Length", int(mylength));
+
+
+ if (strlen(handler_vids) > 0)
+ appendItem(group, "Video codec", handler_vids);
+ else
+ appendItem(group, "Video codec", i18n("Unknown"));
+
+ if (done_audio)
+ appendItem(group, "Audio codec", i18n(resolve_audio(handler_audio)));
+ else
+ appendItem(group, "Audio codec", i18n("None"));
+
+ }
+
+ f.close();
+ return true;
+}
+
+#include "kfile_avi.moc"
diff --git a/kfile-plugins/avi/kfile_avi.desktop b/kfile-plugins/avi/kfile_avi.desktop
new file mode 100644
index 00000000..bbc4e72f
--- /dev/null
+++ b/kfile-plugins/avi/kfile_avi.desktop
@@ -0,0 +1,68 @@
+[Desktop Entry]
+Type=Service
+Name=AVI Info
+Name[af]=Avi Inligting
+Name[bg]=Информация за AVI
+Name[bn]=এ-ভি-আই (AVI) তথ্য
+Name[br]=Titouroù AVI
+Name[bs]=AVI informacije
+Name[ca]=Informació AVI
+Name[cs]=AVI info
+Name[cy]=Gwybodaeth AVI
+Name[da]=AVI-info
+Name[de]=AVI-Info
+Name[el]=Πληροφορίες AVI
+Name[eo]=AVI-informo
+Name[es]=Info AVI
+Name[et]=AVI info
+Name[eu]=AVI informazioa
+Name[fa]=اطلاعات AVI
+Name[fi]=AVI-tiedot
+Name[fr]=Informations AVI
+Name[gl]=Información AVI
+Name[he]=מידע AVI
+Name[hi]=AVI जानकारी
+Name[hr]=AVI Informacije
+Name[hu]=AVI-jellemzők
+Name[is]=AVI upplýsingar
+Name[it]=Informazioni AVI
+Name[ja]=AVI 情報
+Name[kk]=AVI мәліметі
+Name[km]=ព័ត៌មាន AVI
+Name[ko]=AVI 정보
+Name[lt]=AVI informacija
+Name[mk]=AVI информации
+Name[nb]=AVI informasjon
+Name[nds]=AVI-Info
+Name[ne]=AVI सूचना
+Name[nl]=AVI-informatie
+Name[nn]=AVI-info
+Name[pa]=AVI ਜਾਣਕਾਰੀ
+Name[pl]=Informacja o pliku AVI
+Name[pt]=Informação do AVI
+Name[pt_BR]=Informação sobre AVI
+Name[ro]=Informaţii AVI
+Name[ru]=Сведения о AVI
+Name[se]=AVI-dieđut
+Name[sk]=AVI info
+Name[sl]=Podatki o AVI
+Name[sr]=Информације о AVI-ју
+Name[sr@Latn]=Informacije o AVI-ju
+Name[sv]=Avi-information
+Name[ta]=AVI தகவல்
+Name[tg]=AVI Ахборот
+Name[th]=ข้อมูล AVI
+Name[tr]=AVI Bilgisi
+Name[uk]=Інформація по AVI
+Name[uz]=AVI haqida maʼlumot
+Name[uz@cyrillic]=AVI ҳақида маълумот
+Name[xh]=Ulwazi lwe AVI
+Name[zh_CN]=AVI 信息
+Name[zh_HK]=AVI 資訊
+Name[zh_TW]=AVI 資訊
+Name[zu]=Ulwazi lwe-AVI
+ServiceTypes=KFilePlugin
+X-KDE-Library=kfile_avi
+MimeType=video/x-msvideo
+PreferredGrous=Technical
+PreferredItems=Length,Resolution,Frame rate,Video codec,Audio codec
diff --git a/kfile-plugins/avi/kfile_avi.h b/kfile-plugins/avi/kfile_avi.h
new file mode 100644
index 00000000..62a2bf22
--- /dev/null
+++ b/kfile-plugins/avi/kfile_avi.h
@@ -0,0 +1,90 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2002 Shane Wright <me@shanewright.co.uk>
+ *
+ * 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 version 2.
+ *
+ * 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __KFILE_AVI_H__
+#define __KFILE_AVI_H__
+
+#include <kfilemetainfo.h>
+#include <qfile.h>
+
+#if !defined(__osf__)
+#include <inttypes.h>
+#else
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+#endif
+
+
+
+class QStringList;
+
+class KAviPlugin: public KFilePlugin
+{
+ Q_OBJECT
+
+public:
+ KAviPlugin( QObject *parent, const char *name, const QStringList& args );
+
+
+
+ virtual bool readInfo( KFileMetaInfo& info, uint what);
+
+private:
+
+ bool read_avi();
+ bool read_list();
+ bool read_avih();
+ bool read_strl();
+
+ bool read_strf(uint32_t blocksize);
+ bool read_strh(uint32_t blocksize);
+
+ // methods to sort out human readable names for the codecs
+ const char * resolve_audio(uint16_t id);
+
+ QFile f;
+ QDataStream dstream;
+
+ // AVI header information
+ bool done_avih;
+ uint32_t avih_microsecperframe;
+ uint32_t avih_maxbytespersec;
+ uint32_t avih_reserved1;
+ uint32_t avih_flags;
+ uint32_t avih_totalframes;
+ uint32_t avih_initialframes;
+ uint32_t avih_streams;
+ uint32_t avih_buffersize;
+ uint32_t avih_width;
+ uint32_t avih_height;
+ uint32_t avih_scale;
+ uint32_t avih_rate;
+ uint32_t avih_start;
+ uint32_t avih_length;
+
+ char handler_vids[5]; // leave room for trailing \0
+ char handler_auds[5];
+ uint16_t handler_audio; // the ID of the audio codec
+ bool done_audio;
+
+ bool wantstrf;
+
+};
+
+#endif
diff --git a/kfile-plugins/flac/Makefile.am b/kfile-plugins/flac/Makefile.am
new file mode 100644
index 00000000..bb3a08e3
--- /dev/null
+++ b/kfile-plugins/flac/Makefile.am
@@ -0,0 +1,22 @@
+## Makefile.am for FLAC file meta info plugin
+
+# set the include path for X, qt, KDE and TagLib
+INCLUDES = $(all_includes) $(taglib_includes)
+
+# these are the headers for your project
+noinst_HEADERS = kfile_flac.h
+
+kde_module_LTLIBRARIES = kfile_flac.la
+
+kfile_flac_la_SOURCES = kfile_flac.cpp
+kfile_flac_la_LDFLAGS = $(all_libraries) $(taglib_libs) -module $(KDE_PLUGIN)
+kfile_flac_la_LIBADD = $(LIB_KIO)
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
+
+messages: rc.cpp
+ $(XGETTEXT) kfile_flac.cpp -o $(podir)/kfile_flac.pot
+
+services_DATA = kfile_flac.desktop
+servicesdir = $(kde_servicesdir)
diff --git a/kfile-plugins/flac/configure.in.in b/kfile-plugins/flac/configure.in.in
new file mode 100644
index 00000000..eeaeebf5
--- /dev/null
+++ b/kfile-plugins/flac/configure.in.in
@@ -0,0 +1 @@
+AM_CONDITIONAL(include_flac_SUBDIR, test "x$have_taglib" = xyes)
diff --git a/kfile-plugins/flac/kfile_flac.cpp b/kfile-plugins/flac/kfile_flac.cpp
new file mode 100644
index 00000000..e2609401
--- /dev/null
+++ b/kfile-plugins/flac/kfile_flac.cpp
@@ -0,0 +1,282 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2003-2004 Allan Sandfeld Jensen <kde@carewolf.com>
+ *
+ * Originally based upon the kfile_ogg plugin:
+ * Copyright (C) 2001, 2002 Rolf Magnus <ramagnus@kde.org>
+ * Interfacing to TagLib is copied from kfile_mp3 plugin:
+ * Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation version 2.
+ *
+ * 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "kfile_flac.h"
+
+#include <qcstring.h>
+#include <qfile.h>
+#include <qdatetime.h>
+#include <qdict.h>
+#include <qvalidator.h>
+#include <qfileinfo.h>
+
+#include <kdebug.h>
+#include <kurl.h>
+#include <kprocess.h>
+#include <klocale.h>
+#include <kgenericfactory.h>
+#include <ksavefile.h>
+
+#include <tag.h>
+#if (TAGLIB_MAJOR_VERSION>1) || \
+ ((TAGLIB_MAJOR_VERSION==1) && (TAGLIB_MINOR_VERSION>=2))
+#define TAGLIB_1_2
+#endif
+
+#include <tstring.h>
+#include <tfile.h>
+#include <flacfile.h>
+#ifdef TAGLIB_1_2
+#include <oggflacfile.h>
+#endif
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <ctype.h>
+
+K_EXPORT_COMPONENT_FACTORY(kfile_flac, KGenericFactory<KFlacPlugin>("kfile_flac"))
+
+KFlacPlugin::KFlacPlugin( QObject *parent, const char *name,
+ const QStringList &args )
+ : KFilePlugin( parent, name, args )
+{
+ kdDebug(7034) << "flac plugin\n";
+
+ makeMimeTypeInfo( "audio/x-flac" );
+#ifdef TAGLIB_1_2
+ makeMimeTypeInfo( "audio/x-oggflac" );
+#endif
+
+}
+
+void KFlacPlugin::makeMimeTypeInfo(const QString& mimeType)
+{
+ KFileMimeTypeInfo* info = addMimeTypeInfo( mimeType );
+
+ KFileMimeTypeInfo::GroupInfo* group = 0;
+
+ // comment group
+ group = addGroupInfo(info, "Comment", i18n("Comment"));
+ setAttributes(group, KFileMimeTypeInfo::Addable |
+ KFileMimeTypeInfo::Removable);
+
+ KFileMimeTypeInfo::ItemInfo* item = 0;
+
+ item = addItemInfo(group, "Artist", i18n("Artist"), QVariant::String);
+ setHint(item, KFileMimeTypeInfo::Author);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Title", i18n("Title"), QVariant::String);
+ setHint(item, KFileMimeTypeInfo::Name);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Album", i18n("Album"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Genre", i18n("Genre"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Tracknumber", i18n("Track Number"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Date", i18n("Date"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Description", i18n("Description"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Organization", i18n("Organization"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Location", i18n("Location"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Copyright", i18n("Copyright"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+
+ addVariableInfo(group, QVariant::String, KFileMimeTypeInfo::Addable |
+ KFileMimeTypeInfo::Removable |
+ KFileMimeTypeInfo::Modifiable);
+
+ // technical group
+ group = addGroupInfo(info, "Technical", i18n("Technical Details"));
+ setAttributes(group, 0);
+
+ addItemInfo(group, "Channels", i18n("Channels"), QVariant::Int);
+
+ item = addItemInfo(group, "Sample Rate", i18n("Sample Rate"), QVariant::Int);
+ setSuffix(item, i18n(" Hz"));
+
+ item = addItemInfo(group, "Sample Width", i18n("Sample Width"), QVariant::Int);
+ setSuffix(item, i18n(" bits"));
+
+ item = addItemInfo(group, "Bitrate", i18n("Average Bitrate"),
+ QVariant::Int);
+ setAttributes(item, KFileMimeTypeInfo::Averaged);
+ setHint(item, KFileMimeTypeInfo::Bitrate);
+ setSuffix(item, i18n( " kbps"));
+
+ item = addItemInfo(group, "Length", i18n("Length"), QVariant::Int);
+ setAttributes(item, KFileMimeTypeInfo::Cummulative);
+ setHint(item, KFileMimeTypeInfo::Length);
+ setUnit(item, KFileMimeTypeInfo::Seconds);
+}
+
+bool KFlacPlugin::readInfo( KFileMetaInfo& info, uint what )
+{
+ if ( info.path().isEmpty() ) // remote file
+ return false;
+
+ bool readComment = false;
+ bool readTech = false;
+ if (what & (KFileMetaInfo::Fastest |
+ KFileMetaInfo::DontCare |
+ KFileMetaInfo::ContentInfo)) readComment = true;
+
+ if (what & (KFileMetaInfo::Fastest |
+ KFileMetaInfo::DontCare |
+ KFileMetaInfo::TechnicalInfo)) readTech = true;
+
+ TagLib::File *file = 0;
+
+ if (info.mimeType() == "audio/x-flac")
+ file = new TagLib::FLAC::File(QFile::encodeName(info.path()).data(), readTech);
+#ifdef TAGLIB_1_2
+ else
+ file = new TagLib::Ogg::FLAC::File(QFile::encodeName(info.path()).data(), readTech);
+#endif
+
+ if (!file || !file->isValid())
+ {
+ kdDebug(7034) << "Couldn't open " << file->name() << endl;
+ delete file;
+ return false;
+ }
+
+ if(readComment && file->tag())
+ {
+ KFileMetaInfoGroup commentgroup = appendGroup(info, "Comment");
+
+ QString date = file->tag()->year() > 0 ? QString::number(file->tag()->year()) : QString::null;
+ QString track = file->tag()->track() > 0 ? QString::number(file->tag()->track()) : QString::null;
+
+ appendItem(commentgroup, "Title", TStringToQString(file->tag()->title()).stripWhiteSpace());
+ appendItem(commentgroup, "Artist", TStringToQString(file->tag()->artist()).stripWhiteSpace());
+ appendItem(commentgroup, "Album", TStringToQString(file->tag()->album()).stripWhiteSpace());
+ appendItem(commentgroup, "Date", date);
+ appendItem(commentgroup, "Comment", TStringToQString(file->tag()->comment()).stripWhiteSpace());
+ appendItem(commentgroup, "Tracknumber", track);
+ appendItem(commentgroup, "Genre", TStringToQString(file->tag()->genre()).stripWhiteSpace());
+ }
+
+ if (readTech && file->audioProperties())
+ {
+ KFileMetaInfoGroup techgroup = appendGroup(info, "Technical");
+ TagLib::FLAC::Properties *properties =
+ (TagLib::FLAC::Properties*)(file->audioProperties());
+
+ appendItem(techgroup, "Bitrate", properties->bitrate());
+ appendItem(techgroup, "Sample Rate", properties->sampleRate());
+ appendItem(techgroup, "Sample Width", properties->sampleWidth());
+ appendItem(techgroup, "Channels", properties->channels());
+ appendItem(techgroup, "Length", properties->length());
+ }
+
+ delete file;
+ return true;
+
+}
+
+/**
+ * Do translation between KFileMetaInfo items and TagLib::String in a tidy way.
+ */
+
+class Translator
+{
+public:
+ Translator(const KFileMetaInfo &info) : m_info(info) {}
+ TagLib::String operator[](const char *key) const
+ {
+ return QStringToTString(m_info["Comment"][key].value().toString());
+ }
+ int toInt(const char *key) const
+ {
+ return m_info["Comment"][key].value().toInt();
+ }
+private:
+ const KFileMetaInfo &m_info;
+};
+
+bool KFlacPlugin::writeInfo(const KFileMetaInfo& info) const
+{
+ TagLib::File *file;
+
+ if (!TagLib::File::isWritable(QFile::encodeName(info.path()).data())) {
+ kdDebug(7034) << "can't write to " << info.path() << endl;
+ return false;
+ }
+
+ if (info.mimeType() == "audio/x-flac")
+ file = new TagLib::FLAC::File(QFile::encodeName(info.path()).data(), false);
+#ifdef TAGLIB_1_2
+ else
+ file = new TagLib::Ogg::FLAC::File(QFile::encodeName(info.path()).data(), false);
+#endif
+
+ if(!file->isOpen())
+ {
+ kdDebug(7034) << "couldn't open " << info.path() << endl;
+ delete file;
+ return false;
+ }
+
+ Translator t(info);
+
+ file->tag()->setTitle(t["Title"]);
+ file->tag()->setArtist(t["Artist"]);
+ file->tag()->setAlbum(t["Album"]);
+ file->tag()->setYear(t.toInt("Date"));
+ file->tag()->setComment(t["Comment"]);
+ file->tag()->setTrack(t.toInt("Tracknumber"));
+ file->tag()->setGenre(t["Genre"]);
+
+ file->save();
+
+ delete file;
+ return true;
+}
+
+QValidator* KFlacPlugin::createValidator( const QString&,
+ const QString &group, const QString &key,
+ QObject* parent, const char* name) const
+{
+ if(key == "Tracknumber" || key == "Date")
+ {
+ return new QIntValidator(0, 9999, parent, name);
+ }
+ else
+ return new QRegExpValidator(QRegExp(".*"), parent, name);
+}
+
+#include "kfile_flac.moc"
diff --git a/kfile-plugins/flac/kfile_flac.desktop b/kfile-plugins/flac/kfile_flac.desktop
new file mode 100644
index 00000000..b8b551f9
--- /dev/null
+++ b/kfile-plugins/flac/kfile_flac.desktop
@@ -0,0 +1,62 @@
+[Desktop Entry]
+Type=Service
+Name=FLAC Info
+Name[bg]=Информация за FLAC
+Name[bn]=ফ্ল্যাক (FLAC) তথ্য
+Name[br]=Titouroù FLAC
+Name[bs]=FLAC informacije
+Name[ca]=Informació FLAC
+Name[cs]=FLAC info
+Name[cy]=Gwybodaeth FLAC
+Name[da]=FLAC-Info
+Name[de]=FLAC-Info
+Name[el]=Πληροφορίες FLAC
+Name[es]=Info FLAC
+Name[et]=FLAC info
+Name[eu]=FLAC Informazioa
+Name[fa]=اطلاعات FLAC
+Name[fi]=FLAC-tiedot
+Name[fr]=Informations FLAC
+Name[gl]=Información FLAC
+Name[he]=מידע FLAC
+Name[hi]=FLAC जानकारी
+Name[hu]=FLAC-jellemzők
+Name[is]=AU upplýsingar
+Name[it]=Informazioni FLAC
+Name[ja]=FLAC 情報
+Name[kk]=FLAC мәліметі
+Name[km]=ព័ត៌មាន FLAC
+Name[ko]=FLAC 정보
+Name[lt]=FLAC informacija
+Name[mk]=FLAC информации
+Name[nb]=FLAC informasjon
+Name[nds]=FLAC-Info
+Name[ne]=FLAC सूचना
+Name[nl]=FLAC-informatie
+Name[nn]=FLAC-info
+Name[pa]=FLAC ਜਾਣਕਾਰੀ
+Name[pl]=Informacja o pliku FLAC
+Name[pt]=Informação do FLAC
+Name[pt_BR]=Informação sobre FLAC
+Name[ro]=Informaţii FLAC
+Name[ru]=Сведения о FLAC
+Name[sk]=FLAC info
+Name[sl]=Podatki o FLAC
+Name[sr]=Информације о FLAC-у
+Name[sr@Latn]=Informacije o FLAC-u
+Name[sv]=FLAC-information
+Name[ta]=FLAC தகவல்
+Name[tg]=FLAC Ахборот
+Name[th]=ขอมูล FLAC
+Name[tr]=FLAC Bilgisi
+Name[uk]=Інформація по FLAC
+Name[uz]=FLAC haqida maʼlumot
+Name[uz@cyrillic]=FLAC ҳақида маълумот
+Name[zh_CN]=FLAC 信息
+Name[zh_HK]=FLAC 資訊
+Name[zh_TW]=FLAC 資訊
+ServiceTypes=KFilePlugin
+X-KDE-Library=kfile_flac
+MimeType=audio/x-flac;audio/x-oggflac
+PreferredGroups=Comment,Technical
+PreferredItems=Title,Artist,Album,Tracknumber,Genre,Bitrate,Length,Channels, Date,Description,Organization,Location,Copyright
diff --git a/kfile-plugins/flac/kfile_flac.h b/kfile-plugins/flac/kfile_flac.h
new file mode 100644
index 00000000..9010875d
--- /dev/null
+++ b/kfile-plugins/flac/kfile_flac.h
@@ -0,0 +1,50 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2003 Allan Sandfeld Jensen <kde@carewolf.com>
+ *
+ * Originally based upon the kfile_ogg plugin:
+ * Copyright (C) 2001, 2002 Rolf Magnus <ramagnus@kde.org>
+ * Interfacing to TagLib is copied from kfile_mp3 plugin:
+ * Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation version 2.
+ *
+ * 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __KFILE_FLAC_H__
+#define __KFILE_FLAC_H__
+
+#include <kfilemetainfo.h>
+
+class QString;
+class QStringList;
+
+class KFlacPlugin: public KFilePlugin
+{
+ Q_OBJECT
+
+public:
+ KFlacPlugin( QObject *parent, const char *name, const QStringList& args );
+
+ virtual bool readInfo( KFileMetaInfo& info, uint what);
+ virtual bool writeInfo( const KFileMetaInfo& info ) const;
+ virtual QValidator* createValidator( const QString& mimetype,
+ const QString &group,
+ const QString &key,
+ QObject* parent, const char* name) const;
+protected:
+ virtual void makeMimeTypeInfo(const QString& mimeType);
+};
+
+
+#endif
diff --git a/kfile-plugins/m3u/Makefile.am b/kfile-plugins/m3u/Makefile.am
new file mode 100644
index 00000000..d8dcb33a
--- /dev/null
+++ b/kfile-plugins/m3u/Makefile.am
@@ -0,0 +1,22 @@
+## Makefile.am for m3u file meta info plugin
+
+# set the include path for X, qt and KDE
+INCLUDES = $(all_includes)
+
+# these are the headers for your project
+noinst_HEADERS = kfile_m3u.h
+
+kde_module_LTLIBRARIES = kfile_m3u.la
+
+kfile_m3u_la_SOURCES = kfile_m3u.cpp
+kfile_m3u_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+kfile_m3u_la_LIBADD = $(LIB_KSYCOCA)
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
+
+messages: rc.cpp
+ $(XGETTEXT) kfile_m3u.cpp -o $(podir)/kfile_m3u.pot
+
+services_DATA = kfile_m3u.desktop
+servicesdir = $(kde_servicesdir)
diff --git a/kfile-plugins/m3u/kfile_m3u.cpp b/kfile-plugins/m3u/kfile_m3u.cpp
new file mode 100644
index 00000000..f6003667
--- /dev/null
+++ b/kfile-plugins/m3u/kfile_m3u.cpp
@@ -0,0 +1,87 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2001, 2002 Rolf Magnus <ramagnus@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation version 2.
+ *
+ * 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * $Id$
+ */
+
+#include "kfile_m3u.h"
+
+#include <kdebug.h>
+#include <kurl.h>
+#include <kprocess.h>
+#include <klocale.h>
+#include <kgenericfactory.h>
+
+#include <qcstring.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qdatetime.h>
+#include <qdict.h>
+#include <qvalidator.h>
+
+typedef KGenericFactory<KM3uPlugin> M3uFactory;
+
+K_EXPORT_COMPONENT_FACTORY( kfile_m3u, M3uFactory( "kfile_m3u" ) )
+
+KM3uPlugin::KM3uPlugin( QObject *parent, const char *name,
+ const QStringList &preferredItems )
+ : KFilePlugin( parent, name, preferredItems )
+{
+ kdDebug(7034) << "m3u plugin\n";
+
+ KFileMimeTypeInfo* info = addMimeTypeInfo( "audio/x-mpegurl" );
+
+ KFileMimeTypeInfo::GroupInfo* group;
+
+ // tracks group
+ group = addGroupInfo(info, "Tracks", i18n("Tracks"));
+ addVariableInfo(group, QVariant::String, 0);
+}
+
+bool KM3uPlugin::readInfo( KFileMetaInfo& info, uint )
+{
+ if ( info.path().isEmpty() ) // remote file
+ return false;
+
+ QFile f(info.path());
+ if (!f.open(IO_ReadOnly)) return false;
+ QTextStream str(&f);
+ str.setEncoding(QTextStream::Locale);
+
+
+ KFileMetaInfoGroup group = appendGroup(info, "Tracks");
+
+ // for now treat all lines that don't start with # like entries
+ int num = 1;
+ while (!str.atEnd())
+ {
+ QString s = str.readLine();
+ if (!s.startsWith("#"))
+ {
+ if (s.endsWith("\n")) s.truncate(s.length()-1);
+
+ if (!s.stripWhiteSpace().isEmpty()) {
+ appendItem(group, i18n("Track %1").arg(num, 3), s);
+ num++;
+ }
+ }
+ }
+
+ return true;
+}
+
+#include "kfile_m3u.moc"
diff --git a/kfile-plugins/m3u/kfile_m3u.desktop b/kfile-plugins/m3u/kfile_m3u.desktop
new file mode 100644
index 00000000..aedd958a
--- /dev/null
+++ b/kfile-plugins/m3u/kfile_m3u.desktop
@@ -0,0 +1,71 @@
+[Desktop Entry]
+Type=Service
+Name=M3U Playlist Info
+Name[af]=M3u Liedjielys Inligting
+Name[ar]=معلومات قوائم M3U
+Name[az]=MP3 Mahnı Siyahısı İnfosu
+Name[bg]=Информация за списък M3U
+Name[bn]=এম-৩-ইউ (M3U) সঙ্গীত-তালিকা তথ্য
+Name[br]=Titouroù Rollc'hoari MP3
+Name[bs]=M3U Playlist informacije
+Name[ca]=Informació de selecció de peces M3U
+Name[cs]=Informace o seznamu skladeb M3U
+Name[cy]=Gwybodaeth Rhestr Chwarae M3U
+Name[da]=M3U-spillelisteinfo
+Name[de]=M3U-Wiedergabelisten-Info
+Name[el]=Πληροφορίες λίστας αναπαραγωγής M3U
+Name[eo]=M3U-ludlistinformo
+Name[es]=Información de lista de reproducción M3U
+Name[et]=M3U esitusnimekirja info
+Name[eu]=M3U erreprodukzio-zerrenda informazioa
+Name[fa]=اطلاعات فهرست پخش M3U
+Name[fi]=M3U-soittolistan tiedot
+Name[fr]=Informations sur une liste de lecture M3U
+Name[ga]=Eolas Seinmliosta M3U
+Name[gl]=Información de Lista M3U
+Name[he]=מידע על רשימות ניגון של M3U
+Name[hi]=M3U गीत-सूची जानकारी
+Name[hr]=Informacije o M3U listi pjesama
+Name[hu]=Az M3U lejátszási lista jellemzői
+Name[is]=M3U lagalistaupplýsingar
+Name[it]=Informazioni playlist M3U
+Name[ja]=M3U プレイリスト情報
+Name[kk]=M3U орындау тізім мәліметі
+Name[km]=ព័ត៌មាន​បញ្ជី​ចាក់ M3U
+Name[ko]=M3U 재생목록 정보
+Name[lt]=M3U gaidaraščio informacija
+Name[lv]=M3U Plejlistu Info
+Name[mk]=Информации за M3U листа со нумери
+Name[nb]=M3U spilleliste informasjon
+Name[nds]=M3U-Weddergaavlist-Info
+Name[ne]=M3U बजाउने सूची सूचना
+Name[nl]=M3U Speellijst-informatie
+Name[nn]=M3U-spelelisteinfo
+Name[pa]=M3U ਸੰਗੀਤ-ਸੂਚੀ ਜਾਣਕਾਰੀ
+Name[pl]=Informacja o liście nagrań w M3U
+Name[pt]=Informação da Lista de Músicas M3U
+Name[pt_BR]=Informações de listas de reprodução M3U
+Name[ro]=Informaţii listă M3U
+Name[ru]=Сведения о списке песен M3U
+Name[se]=M3U-čuojahanlistodieđut
+Name[sk]=Informácie o playliste M3U
+Name[sl]=Podatki o predvajalnem seznamu M3U
+Name[sr]=Информације о M3U листи нумера
+Name[sr@Latn]=Informacije o M3U listi numera
+Name[sv]=Information om M3U-spellista
+Name[ta]=M3U பாடல்பட்டியல் தகவல்
+Name[tg]=M3U Ахбороти Рӯйхати бозикуниҳо
+Name[th]=ข้อมูลรายการเพลง M3U
+Name[tr]=M3U Çalma Listesi Bilgisi
+Name[uk]=Інформація списку композицій M3U
+Name[ven]=Mavhungo a mutevhe wa tshitambi tsha M3U
+Name[xh]=M3U Ulwazi Loluhlu Lokudlala
+Name[zh_CN]=M3U 播放列表信息
+Name[zh_HK]=M3U 播放清單資訊
+Name[zh_TW]=M3U 播放清單資訊
+Name[zu]=Ulwazi Lohlu lokudlala lwe-M3U
+ServiceTypes=KFilePlugin
+X-KDE-Library=kfile_m3u
+MimeType=audio/x-mpegurl
+PreferredGroups=Tracks
+PreferredItems=
diff --git a/kfile-plugins/m3u/kfile_m3u.h b/kfile-plugins/m3u/kfile_m3u.h
new file mode 100644
index 00000000..4a25cf54
--- /dev/null
+++ b/kfile-plugins/m3u/kfile_m3u.h
@@ -0,0 +1,39 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2001, 2002 Rolf Magnus <ramagnus@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation version 2.
+ *
+ * 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * $Id$
+ */
+
+#ifndef __KMIME_M3U_H__
+#define __KMIME_M3U_H__
+
+#include <kfilemetainfo.h>
+#include <kurl.h>
+
+class QStringList;
+
+class KM3uPlugin: public KFilePlugin
+{
+ Q_OBJECT
+
+public:
+ KM3uPlugin( QObject *parent, const char *name, const QStringList& args );
+
+ virtual bool readInfo( KFileMetaInfo& info, uint what );
+};
+
+#endif
diff --git a/kfile-plugins/mp3/Makefile.am b/kfile-plugins/mp3/Makefile.am
new file mode 100644
index 00000000..91b43e73
--- /dev/null
+++ b/kfile-plugins/mp3/Makefile.am
@@ -0,0 +1,22 @@
+## Makefile.am for mp3 file meta info plugin
+
+# set the include path for X, qt and KDE
+INCLUDES = $(all_includes) $(taglib_includes)
+
+# these are the headers for your project
+noinst_HEADERS = kfile_mp3.h
+
+kde_module_LTLIBRARIES = kfile_mp3.la
+
+kfile_mp3_la_SOURCES = kfile_mp3.cpp
+kfile_mp3_la_LDFLAGS = $(all_libraries) $(taglib_libs) -module $(KDE_PLUGIN)
+kfile_mp3_la_LIBADD = $(LIB_KIO)
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
+
+messages: rc.cpp
+ $(XGETTEXT) kfile_mp3.cpp -o $(podir)/kfile_mp3.pot
+
+services_DATA = kfile_mp3.desktop
+servicesdir = $(kde_servicesdir)
diff --git a/kfile-plugins/mp3/configure.in.in b/kfile-plugins/mp3/configure.in.in
new file mode 100644
index 00000000..d04bc223
--- /dev/null
+++ b/kfile-plugins/mp3/configure.in.in
@@ -0,0 +1 @@
+AM_CONDITIONAL(include_mp3_SUBDIR, test "x$have_taglib" = xyes)
diff --git a/kfile-plugins/mp3/kfile_mp3.cpp b/kfile-plugins/mp3/kfile_mp3.cpp
new file mode 100644
index 00000000..343b37cd
--- /dev/null
+++ b/kfile-plugins/mp3/kfile_mp3.cpp
@@ -0,0 +1,307 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2001, 2002 Rolf Magnus <ramagnus@kde.org>
+ * Copyright (C) 2002 Ryan Cumming <bodnar42@phalynx.dhs.org>
+ * Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation version 2.
+ *
+ * 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "kfile_mp3.h"
+
+#include <kprocess.h>
+#include <klocale.h>
+#include <kgenericfactory.h>
+#include <kstringvalidator.h>
+#include <kdebug.h>
+
+#include <qdict.h>
+#include <qvalidator.h>
+#include <qcstring.h>
+#include <qfile.h>
+#include <qdatetime.h>
+
+#include <tstring.h>
+#include <tag.h>
+#include <mpegfile.h>
+#include <id3v1genres.h>
+#include <id3v2framefactory.h>
+
+typedef KGenericFactory<KMp3Plugin> Mp3Factory;
+
+K_EXPORT_COMPONENT_FACTORY(kfile_mp3, Mp3Factory( "kfile_mp3" ))
+
+KMp3Plugin::KMp3Plugin(QObject *parent, const char *name, const QStringList &args)
+ : KFilePlugin(parent, name, args)
+{
+ kdDebug(7034) << "mp3 plugin\n";
+
+ KFileMimeTypeInfo *info = addMimeTypeInfo("audio/x-mp3");
+
+ // id3 group
+
+ KFileMimeTypeInfo::GroupInfo *group = addGroupInfo(info, "id3", i18n("ID3 Tag"));
+
+ setAttributes(group, KFileMimeTypeInfo::Addable |
+ KFileMimeTypeInfo::Removable);
+
+ KFileMimeTypeInfo::ItemInfo *item;
+
+ item = addItemInfo(group, "Title", i18n("Title"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+ setHint(item, KFileMimeTypeInfo::Name);
+
+ item = addItemInfo(group, "Artist", i18n("Artist"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+ setHint(item, KFileMimeTypeInfo::Author);
+
+ item = addItemInfo(group, "Album", i18n("Album"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Date", i18n("Year"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Comment", i18n("Comment"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+ setHint(item, KFileMimeTypeInfo::Description);
+
+ item = addItemInfo(group, "Tracknumber", i18n("Track"), QVariant::Int);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Genre", i18n("Genre"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ // technical group
+
+ group = addGroupInfo(info, "Technical", i18n("Technical Details"));
+
+ item = addItemInfo(group, "Version", i18n("Version"), QVariant::Int);
+ setPrefix(item, i18n("MPEG "));
+
+ item = addItemInfo(group, "Layer", i18n("Layer"), QVariant::Int);
+ item = addItemInfo(group, "CRC", i18n("CRC"), QVariant::Bool);
+ item = addItemInfo(group, "Bitrate", i18n("Bitrate"), QVariant::Int);
+ setAttributes(item, KFileMimeTypeInfo::Averaged);
+ setHint(item, KFileMimeTypeInfo::Bitrate);
+ setSuffix(item, i18n(" kbps"));
+
+ item = addItemInfo(group, "Sample Rate", i18n("Sample Rate"), QVariant::Int);
+ setSuffix(item, i18n("Hz"));
+
+ item = addItemInfo(group, "Channels", i18n("Channels"), QVariant::Int);
+ item = addItemInfo(group, "Copyright", i18n("Copyright"), QVariant::Bool);
+ item = addItemInfo(group, "Original", i18n("Original"), QVariant::Bool);
+ item = addItemInfo(group, "Length", i18n("Length"), QVariant::Int);
+ setAttributes(item, KFileMimeTypeInfo::Cummulative);
+ setUnit(item, KFileMimeTypeInfo::Seconds);
+ item = addItemInfo(group, "Emphasis", i18n("Emphasis"), QVariant::String);
+}
+
+bool KMp3Plugin::readInfo(KFileMetaInfo &info, uint what)
+{
+ kdDebug(7034) << "mp3 plugin readInfo\n";
+
+ bool readId3 = false;
+ bool readTech = false;
+
+ typedef enum KFileMetaInfo::What What;
+
+ if(what & (KFileMetaInfo::Fastest |
+ KFileMetaInfo::DontCare |
+ KFileMetaInfo::ContentInfo))
+ {
+ readId3 = true;
+ }
+
+ if(what & (KFileMetaInfo::Fastest |
+ KFileMetaInfo::DontCare |
+ KFileMetaInfo::TechnicalInfo))
+ {
+ readTech = true;
+ }
+
+ if(!readId3 && !readTech)
+ return true;
+
+ if ( info.path().isEmpty() ) // remote file
+ return false;
+
+ TagLib::MPEG::File file(QFile::encodeName(info.path()).data(), readTech);
+
+ if(!file.isOpen())
+ {
+ kdDebug(7034) << "Couldn't open " << file.name() << endl;
+ return false;
+ }
+
+ if(readId3)
+ {
+ KFileMetaInfoGroup id3group = appendGroup(info, "id3");
+
+ QString date = file.tag()->year() > 0 ? QString::number(file.tag()->year()) : QString::null;
+ QString track = file.tag()->track() > 0 ? QString::number(file.tag()->track()) : QString::null;
+
+ QString title = TStringToQString(file.tag()->title()).stripWhiteSpace();
+ if (!title.isEmpty())
+ appendItem(id3group, "Title", title);
+ QString artist = TStringToQString(file.tag()->artist()).stripWhiteSpace();
+ if (!artist.isEmpty())
+ appendItem(id3group, "Artist", artist);
+ QString album = TStringToQString(file.tag()->album()).stripWhiteSpace();
+ if (!album.isEmpty())
+ appendItem(id3group, "Album", album);
+ appendItem(id3group, "Date", date);
+ QString comment = TStringToQString(file.tag()->comment()).stripWhiteSpace();
+ if (!comment.isEmpty())
+ appendItem(id3group, "Comment", comment);
+ appendItem(id3group, "Tracknumber", track);
+ QString genre = TStringToQString(file.tag()->genre()).stripWhiteSpace();
+ if (!genre.isEmpty())
+ appendItem(id3group, "Genre", genre);
+ }
+
+ if(readTech)
+ {
+ KFileMetaInfoGroup techgroup = appendGroup(info, "Technical");
+
+ QString version;
+ switch(file.audioProperties()->version())
+ {
+ case TagLib::MPEG::Header::Version1:
+ version = "1.0";
+ break;
+ case TagLib::MPEG::Header::Version2:
+ version = "2.0";
+ break;
+ case TagLib::MPEG::Header::Version2_5:
+ version = "2.5";
+ break;
+ }
+
+ static const int dummy = 0; // QVariant's bool constructor requires a dummy int value.
+
+ // CRC and Emphasis aren't yet implemented in TagLib (not that I think anyone cares)
+
+ appendItem(techgroup, "Version", version);
+ appendItem(techgroup, "Layer", file.audioProperties()->layer());
+ // appendItem(techgroup, "CRC", file.audioProperties()->crc());
+ appendItem(techgroup, "Bitrate", file.audioProperties()->bitrate());
+ appendItem(techgroup, "Sample Rate", file.audioProperties()->sampleRate());
+ appendItem(techgroup, "Channels", file.audioProperties()->channels());
+ appendItem(techgroup, "Copyright", QVariant(file.audioProperties()->isCopyrighted(), dummy));
+ appendItem(techgroup, "Original", QVariant(file.audioProperties()->isOriginal(), dummy));
+ appendItem(techgroup, "Length", file.audioProperties()->length());
+ // appendItem(techgroup, "Emphasis", file.audioProperties()->empahsis());
+ }
+
+ kdDebug(7034) << "reading finished\n";
+
+ return true;
+}
+
+/**
+ * Do translation between KFileMetaInfo items and TagLib::String in a tidy way.
+ */
+
+class Translator
+{
+public:
+ Translator(const KFileMetaInfo &info) : m_info(info) {}
+ TagLib::String operator[](const char *key) const
+ {
+ return QStringToTString(m_info["id3"][key].value().toString());
+ }
+ int toInt(const char *key) const
+ {
+ return m_info["id3"][key].value().toInt();
+ }
+private:
+ const KFileMetaInfo &m_info;
+};
+
+bool KMp3Plugin::writeInfo(const KFileMetaInfo &info) const
+{
+ TagLib::ID3v2::FrameFactory::instance()->setDefaultTextEncoding(TagLib::String::UTF8);
+ TagLib::MPEG::File file(QFile::encodeName(info.path()).data(), false);
+
+ if(!file.isOpen() || !TagLib::File::isWritable(file.name()))
+ {
+ kdDebug(7034) << "couldn't open " << info.path() << endl;
+ return false;
+ }
+
+ Translator t(info);
+
+ file.tag()->setTitle(t["Title"]);
+ file.tag()->setArtist(t["Artist"]);
+ file.tag()->setAlbum(t["Album"]);
+ file.tag()->setYear(t.toInt("Date"));
+ file.tag()->setComment(t["Comment"]);
+ file.tag()->setTrack(t.toInt("Tracknumber"));
+ file.tag()->setGenre(t["Genre"]);
+
+ file.save();
+
+ return true;
+}
+
+/**
+ * A validator that will suggest a list of strings, but allow for free form
+ * strings as well.
+ */
+
+class ComboValidator : public KStringListValidator
+{
+public:
+ ComboValidator(const QStringList &list, bool rejecting,
+ bool fixupEnabled, QObject *parent, const char *name) :
+ KStringListValidator(list, rejecting, fixupEnabled, parent, name)
+ {
+
+ }
+
+ virtual QValidator::State validate(QString &, int &) const
+ {
+ return QValidator::Acceptable;
+ }
+};
+
+QValidator *KMp3Plugin::createValidator(const QString & /* mimetype */,
+ const QString &group, const QString &key,
+ QObject *parent, const char *name) const
+{
+ kdDebug(7034) << "making a validator for " << group << "/" << key << endl;
+
+ if(key == "Tracknumber" || key == "Date")
+ {
+ return new QIntValidator(0, 9999, parent, name);
+ }
+
+ if(key == "Genre")
+ {
+ QStringList l;
+ TagLib::StringList genres = TagLib::ID3v1::genreList();
+ for(TagLib::StringList::ConstIterator it = genres.begin(); it != genres.end(); ++it)
+ {
+ l.append(TStringToQString((*it)));
+ }
+ return new ComboValidator(l, false, true, parent, name);
+ }
+
+ return 0;
+}
+
+#include "kfile_mp3.moc"
diff --git a/kfile-plugins/mp3/kfile_mp3.desktop b/kfile-plugins/mp3/kfile_mp3.desktop
new file mode 100644
index 00000000..90527766
--- /dev/null
+++ b/kfile-plugins/mp3/kfile_mp3.desktop
@@ -0,0 +1,70 @@
+[Desktop Entry]
+Type=Service
+Name=MP3 Info
+Name[af]=Mp3 Inligting
+Name[ar]=معلومات MP3
+Name[bg]=Информация за MP3
+Name[bn]=এম-পি-৩ (MP3) তথ্য
+Name[br]=Titouroù MP3
+Name[bs]=MP3 informacije
+Name[ca]=Informació MP3
+Name[cs]=MP3 info
+Name[cy]=Gwybodaeth MP3
+Name[da]=MP3-info
+Name[de]=MP3-Info
+Name[el]=Πληροφορίες MP3
+Name[eo]=MP3-informo
+Name[es]=Info MP3
+Name[et]=MP3 info
+Name[eu]=MP3 informazioa
+Name[fa]=اطلاعات MP3
+Name[fi]=MP3-tiedot
+Name[fr]=Informations Mpeg3
+Name[gl]=Información MP3
+Name[he]=מידע MP3
+Name[hi]=MP3 जानकारी
+Name[hr]=Informacije o MP3 datoteci
+Name[hu]=MP3-jellemzők
+Name[is]=MP3 upplýsingar
+Name[it]=Informazioni MP3
+Name[ja]=MP3 情報
+Name[kk]=MP3 мәліметі
+Name[km]=ព័ត៌មាន MP3
+Name[ko]=MP3 정보
+Name[lt]=MP3 informacija
+Name[mk]=MP3 информации
+Name[nb]=MP3 informasjon
+Name[nds]=MP3-Info
+Name[ne]=MP3 सूचना
+Name[nl]=MP3-informatie
+Name[nn]=MP3-info
+Name[pa]=MP3 ਜਾਣਕਾਰੀ
+Name[pl]=Informacja o pliku MP3
+Name[pt]=Informação do MP3
+Name[pt_BR]=Informações sobre MP3
+Name[ro]=Informaţii MP3
+Name[ru]=Сведения о MP3
+Name[rw]=Amakuru MP3
+Name[se]=MP3-dieđut
+Name[sl]=Podatki o MP3
+Name[sr]=Информације о MP3-ју
+Name[sr@Latn]=Informacije o MP3-ju
+Name[sv]=MP3-information
+Name[ta]=MP3 தகவல்
+Name[tg]=MP3 Ахборот
+Name[th]=ข้อมูล MP3
+Name[tr]=MP3 Bilgisi
+Name[uk]=Інформація по MP3
+Name[uz]=MP3 haqida maʼlumot
+Name[uz@cyrillic]=MP3 ҳақида маълумот
+Name[wa]=Infôrmåcion MP3
+Name[xh]=MP3 ulwazi
+Name[zh_CN]=MP3 信息
+Name[zh_HK]=MP3 資訊
+Name[zh_TW]=MP3 資訊
+Name[zu]=Ulwazi lwe-MP3
+ServiceTypes=KFilePlugin
+X-KDE-Library=kfile_mp3
+MimeType=audio/x-mp3
+PreferredGroups=id3,Technical
+PreferredItems=Title,Artist,Album,Tracknumber,Genre,Bitrate,Length,Date,Comment,Sample Rate,Channels,Version,Layer,Copyright,Original,CRC
diff --git a/kfile-plugins/mp3/kfile_mp3.h b/kfile-plugins/mp3/kfile_mp3.h
new file mode 100644
index 00000000..6479b52f
--- /dev/null
+++ b/kfile-plugins/mp3/kfile_mp3.h
@@ -0,0 +1,41 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2001, 2002 Rolf Magnus <ramagnus@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation version 2.
+ *
+ * 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __KFILE_MP3_H__
+#define __KFILE_MP3_H__
+
+#include <kfilemetainfo.h>
+
+class QStringList;
+
+class KMp3Plugin: public KFilePlugin
+{
+ Q_OBJECT
+
+public:
+ KMp3Plugin(QObject *parent, const char *name, const QStringList &args);
+
+ virtual bool readInfo( KFileMetaInfo& info, uint what );
+ virtual bool writeInfo( const KFileMetaInfo& info) const;
+ virtual QValidator *createValidator(const QString &mimetype,
+ const QString &group,
+ const QString &key,
+ QObject *parent, const char *name) const;
+};
+
+#endif
diff --git a/kfile-plugins/mpc/Makefile.am b/kfile-plugins/mpc/Makefile.am
new file mode 100644
index 00000000..a2fa88d6
--- /dev/null
+++ b/kfile-plugins/mpc/Makefile.am
@@ -0,0 +1,22 @@
+## Makefile.am for MPC file meta info plugin
+
+# set the include path for X, qt, KDE and TagLib
+INCLUDES = $(all_includes) $(taglib_includes)
+
+# these are the headers for your project
+noinst_HEADERS = kfile_mpc.h
+
+kde_module_LTLIBRARIES = kfile_mpc.la
+
+kfile_mpc_la_SOURCES = kfile_mpc.cpp
+kfile_mpc_la_LDFLAGS = $(all_libraries) $(taglib_libs) -module $(KDE_PLUGIN)
+kfile_mpc_la_LIBADD = $(LIB_KIO)
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
+
+messages: rc.cpp
+ $(XGETTEXT) kfile_mpc.cpp -o $(podir)/kfile_mpc.pot
+
+services_DATA = kfile_mpc.desktop
+servicesdir = $(kde_servicesdir)
diff --git a/kfile-plugins/mpc/configure.in.in b/kfile-plugins/mpc/configure.in.in
new file mode 100644
index 00000000..6590e6c5
--- /dev/null
+++ b/kfile-plugins/mpc/configure.in.in
@@ -0,0 +1 @@
+AM_CONDITIONAL(include_mpc_SUBDIR, test "x$have_taglib_mpc" = xyes)
diff --git a/kfile-plugins/mpc/kfile_mpc.cpp b/kfile-plugins/mpc/kfile_mpc.cpp
new file mode 100644
index 00000000..dae3e232
--- /dev/null
+++ b/kfile-plugins/mpc/kfile_mpc.cpp
@@ -0,0 +1,253 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2003-2004 Allan Sandfeld Jensen <kde@carewolf.com>
+ *
+ * Originally based upon the kfile_ogg plugin:
+ * Copyright (C) 2001, 2002 Rolf Magnus <ramagnus@kde.org>
+ * Interfacing to TagLib is copied from kfile_mp3 plugin:
+ * Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation version 2.
+ *
+ * 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "kfile_mpc.h"
+
+#include <qcstring.h>
+#include <qfile.h>
+#include <qdatetime.h>
+#include <qdict.h>
+#include <qvalidator.h>
+#include <qfileinfo.h>
+
+#include <kdebug.h>
+#include <kurl.h>
+#include <kprocess.h>
+#include <klocale.h>
+#include <kgenericfactory.h>
+#include <ksavefile.h>
+
+#include <tstring.h>
+#include <tfile.h>
+#include <mpcfile.h>
+#include <tag.h>
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <ctype.h>
+
+K_EXPORT_COMPONENT_FACTORY(kfile_mpc, KGenericFactory<KMpcPlugin>("kfile_mpc"))
+
+KMpcPlugin::KMpcPlugin( QObject *parent, const char *name,
+ const QStringList &args )
+ : KFilePlugin( parent, name, args )
+{
+ kdDebug(7034) << "mpc plugin\n";
+
+ KFileMimeTypeInfo* info = addMimeTypeInfo( "audio/x-musepack" );
+
+ KFileMimeTypeInfo::GroupInfo* group = 0;
+
+ // comment group
+ group = addGroupInfo(info, "Comment", i18n("Comment"));
+ setAttributes(group, KFileMimeTypeInfo::Addable |
+ KFileMimeTypeInfo::Removable);
+
+ KFileMimeTypeInfo::ItemInfo* item = 0;
+
+ item = addItemInfo(group, "Artist", i18n("Artist"), QVariant::String);
+ setHint(item, KFileMimeTypeInfo::Author);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Title", i18n("Title"), QVariant::String);
+ setHint(item, KFileMimeTypeInfo::Name);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Album", i18n("Album"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Genre", i18n("Genre"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Tracknumber", i18n("Track Number"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Date", i18n("Date"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Description", i18n("Description"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Organization", i18n("Organization"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Location", i18n("Location"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Copyright", i18n("Copyright"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+
+ addVariableInfo(group, QVariant::String, KFileMimeTypeInfo::Addable |
+ KFileMimeTypeInfo::Removable |
+ KFileMimeTypeInfo::Modifiable);
+
+ // technical group
+ group = addGroupInfo(info, "Technical", i18n("Technical Details"));
+ setAttributes(group, 0);
+
+ addItemInfo(group, "Channels", i18n("Channels"), QVariant::Int);
+
+ addItemInfo(group, "Version", i18n("Version"), QVariant::Int);
+
+ item = addItemInfo(group, "Sample Rate", i18n("Sample Rate"), QVariant::Int);
+ setSuffix(item, i18n(" Hz"));
+
+ item = addItemInfo(group, "Bitrate", i18n("Average Bitrate"),
+ QVariant::Int);
+ setAttributes(item, KFileMimeTypeInfo::Averaged);
+ setHint(item, KFileMimeTypeInfo::Bitrate);
+ setSuffix(item, i18n( " kbps"));
+
+ item = addItemInfo(group, "Length", i18n("Length"), QVariant::Int);
+ setAttributes(item, KFileMimeTypeInfo::Cummulative);
+ setHint(item, KFileMimeTypeInfo::Length);
+ setUnit(item, KFileMimeTypeInfo::Seconds);
+}
+
+bool KMpcPlugin::readInfo( KFileMetaInfo& info, uint what )
+{
+
+ bool readComment = false;
+ bool readTech = false;
+ if (what & (KFileMetaInfo::Fastest |
+ KFileMetaInfo::DontCare |
+ KFileMetaInfo::ContentInfo)) readComment = true;
+
+ if (what & (KFileMetaInfo::Fastest |
+ KFileMetaInfo::DontCare |
+ KFileMetaInfo::TechnicalInfo)) readTech = true;
+
+ if ( info.path().isEmpty() ) // remote file
+ return false;
+
+ TagLib::File *file = new TagLib::MPC::File(QFile::encodeName(info.path()).data(), readTech);
+
+ if (!file->isOpen())
+ {
+ kdDebug(7034) << "Couldn't open " << file->name() << endl;
+ delete file;
+ return false;
+ }
+
+ if(readComment)
+ {
+ KFileMetaInfoGroup commentgroup = appendGroup(info, "Comment");
+
+ QString date = file->tag()->year() > 0 ? QString::number(file->tag()->year()) : QString::null;
+ QString track = file->tag()->track() > 0 ? QString::number(file->tag()->track()) : QString::null;
+
+ appendItem(commentgroup, "Title", TStringToQString(file->tag()->title()).stripWhiteSpace());
+ appendItem(commentgroup, "Artist", TStringToQString(file->tag()->artist()).stripWhiteSpace());
+ appendItem(commentgroup, "Album", TStringToQString(file->tag()->album()).stripWhiteSpace());
+ appendItem(commentgroup, "Date", date);
+ appendItem(commentgroup, "Comment", TStringToQString(file->tag()->comment()).stripWhiteSpace());
+ appendItem(commentgroup, "Tracknumber", track);
+ appendItem(commentgroup, "Genre", TStringToQString(file->tag()->genre()).stripWhiteSpace());
+ }
+
+ if (readTech)
+ {
+ KFileMetaInfoGroup techgroup = appendGroup(info, "Technical");
+ TagLib::MPC::Properties *properties =
+ (TagLib::MPC::Properties*)(file->audioProperties());
+
+ appendItem(techgroup, "Bitrate", properties->bitrate());
+ appendItem(techgroup, "Sample Rate", properties->sampleRate());
+ appendItem(techgroup, "Channels", properties->channels());
+ appendItem(techgroup, "Length", properties->length());
+ appendItem(techgroup, "Version", properties->mpcVersion());
+ }
+
+ delete file;
+ return true;
+
+}
+
+/**
+ * Do translation between KFileMetaInfo items and TagLib::String in a tidy way.
+ */
+
+class Translator
+{
+public:
+ Translator(const KFileMetaInfo &info) : m_info(info) {}
+ TagLib::String operator[](const char *key) const
+ {
+ return QStringToTString(m_info["Comment"][key].value().toString());
+ }
+ int toInt(const char *key) const
+ {
+ return m_info["Comment"][key].value().toInt();
+ }
+private:
+ const KFileMetaInfo &m_info;
+};
+
+bool KMpcPlugin::writeInfo(const KFileMetaInfo& info) const
+{
+ TagLib::File *file;
+
+ if (!TagLib::File::isWritable(QFile::encodeName(info.path()).data())) {
+ kdDebug(7034) << "can't write to " << info.path() << endl;
+ return false;
+ }
+
+ file = new TagLib::MPC::File(QFile::encodeName(info.path()).data(), false);
+
+ if(!file->isOpen())
+ {
+ kdDebug(7034) << "couldn't open " << info.path() << endl;
+ delete file;
+ return false;
+ }
+
+ Translator t(info);
+
+ file->tag()->setTitle(t["Title"]);
+ file->tag()->setArtist(t["Artist"]);
+ file->tag()->setAlbum(t["Album"]);
+ file->tag()->setYear(t.toInt("Date"));
+ file->tag()->setComment(t["Comment"]);
+ file->tag()->setTrack(t.toInt("Tracknumber"));
+ file->tag()->setGenre(t["Genre"]);
+
+ file->save();
+
+ delete file;
+ return true;
+}
+
+QValidator* KMpcPlugin::createValidator( const QString&,
+ const QString &group, const QString &key,
+ QObject* parent, const char* name) const
+{
+ if(key == "Tracknumber" || key == "Date")
+ {
+ return new QIntValidator(0, 9999, parent, name);
+ }
+ else
+ return new QRegExpValidator(QRegExp(".*"), parent, name);
+}
+
+#include "kfile_mpc.moc"
diff --git a/kfile-plugins/mpc/kfile_mpc.desktop b/kfile-plugins/mpc/kfile_mpc.desktop
new file mode 100644
index 00000000..c35f1bde
--- /dev/null
+++ b/kfile-plugins/mpc/kfile_mpc.desktop
@@ -0,0 +1,56 @@
+[Desktop Entry]
+Type=Service
+Name=Musepack Info
+Name[bg]=Информация за Musepack
+Name[br]=Titouroù Musepack
+Name[bs]=Musepack informacije
+Name[ca]=Informació Musepack
+Name[cs]=Musepack info
+Name[de]=Musepack-Info
+Name[el]=Πληροφορίες Musepack
+Name[eo]=epack-informo
+Name[es]=Info Musepack
+Name[et]=Musepacki info
+Name[eu]=Musepack informazioa
+Name[fa]=اطلاعات Musepack
+Name[fi]=Musepack-tiedot
+Name[fr]=Informations Musepack
+Name[ga]=Eolas faoi Musepack
+Name[gl]=Información Musepack
+Name[he]=מידע Musepack
+Name[hu]=Musepack-jellemzők
+Name[is]=Musepack upplýsingar
+Name[it]=Informazioni Musepack
+Name[ja]=Musepack 情報
+Name[kk]=Musepack мәліметі
+Name[km]=ព័ត៌មាន Musepack
+Name[ko]=Musepack 정보
+Name[lt]=Musepack informacija
+Name[mk]=Musepack информации
+Name[nb]=Musepack-info
+Name[nds]=Musepack-Info
+Name[ne]=म्युजप्याक सूचना
+Name[nl]=Musepack-informatie
+Name[nn]=Musepack-info
+Name[pa]=Musepack ਜਾਣਕਾਰੀ
+Name[pl]=Informacja Musepack
+Name[pt]=Informação do Musepack
+Name[pt_BR]=Informações sobre Musepack
+Name[ro]=Informaţii Musepack
+Name[ru]=Сведения о Musepack
+Name[sl]=Podatki o Musepack
+Name[sr]=Информације о Musepack-у
+Name[sr@Latn]=Informacije o Musepack-u
+Name[sv]=Musepack-information
+Name[ta]=MP3 தகவல்
+Name[th]=ข้อมูล Musepack
+Name[tr]=MusePack Bilgisi
+Name[uk]=Інформація по Musepack
+Name[zh_CN]=Musepack 信息
+Name[zh_HK]=Musepack 資訊
+Name[zh_TW]=Musepack 資訊
+ServiceTypes=KFilePlugin
+X-KDE-Library=kfile_mpc
+MimeType=audio/x-musepack
+PreferredGroups=Comment,Technical
+PreferredItems=Title,Artist,Album,Tracknumber,Genre,Bitrate,Length,Channels, Date,Description,Organization,Location,Copyright
diff --git a/kfile-plugins/mpc/kfile_mpc.h b/kfile-plugins/mpc/kfile_mpc.h
new file mode 100644
index 00000000..88aabbb4
--- /dev/null
+++ b/kfile-plugins/mpc/kfile_mpc.h
@@ -0,0 +1,48 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2004 Allan Sandfeld Jensen <kde@carewolf.com>
+ *
+ * Originally based upon the kfile_ogg plugin:
+ * Copyright (C) 2001, 2002 Rolf Magnus <ramagnus@kde.org>
+ * Interfacing to TagLib is copied from kfile_mp3 plugin:
+ * Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation version 2.
+ *
+ * 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __KFILE_MPC_H__
+#define __KFILE_MPC_H__
+
+#include <kfilemetainfo.h>
+
+class QString;
+class QStringList;
+
+class KMpcPlugin: public KFilePlugin
+{
+ Q_OBJECT
+
+public:
+ KMpcPlugin( QObject *parent, const char *name, const QStringList& args );
+
+ virtual bool readInfo( KFileMetaInfo& info, uint what);
+ virtual bool writeInfo( const KFileMetaInfo& info ) const;
+ virtual QValidator* createValidator( const QString& mimetype,
+ const QString &group,
+ const QString &key,
+ QObject* parent, const char* name) const;
+};
+
+
+#endif
diff --git a/kfile-plugins/mpeg/Makefile.am b/kfile-plugins/mpeg/Makefile.am
new file mode 100644
index 00000000..1a39ca2e
--- /dev/null
+++ b/kfile-plugins/mpeg/Makefile.am
@@ -0,0 +1,22 @@
+## Makefile.am for mpeg file meta info plugin
+
+# set the include path for X, qt and KDE
+INCLUDES = $(all_includes)
+
+# these are the headers for your project
+noinst_HEADERS = kfile_mpeg.h
+
+kde_module_LTLIBRARIES = kfile_mpeg.la
+
+kfile_mpeg_la_SOURCES = kfile_mpeg.cpp
+kfile_mpeg_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+kfile_mpeg_la_LIBADD = $(LIB_KIO)
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
+
+messages: rc.cpp
+ $(XGETTEXT) kfile_mpeg.cpp -o $(podir)/kfile_mpeg.pot
+
+services_DATA = kfile_mpeg.desktop
+servicesdir = $(kde_servicesdir)
diff --git a/kfile-plugins/mpeg/kfile_mpeg.cpp b/kfile-plugins/mpeg/kfile_mpeg.cpp
new file mode 100644
index 00000000..a0df2fe8
--- /dev/null
+++ b/kfile-plugins/mpeg/kfile_mpeg.cpp
@@ -0,0 +1,582 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2005 Allan Sandfeld Jensen <kde@carewolf.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation version 2.
+ *
+ * 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+// MPEG KFile plugin
+// Based on reading sourcecode of mpeglib, xinelib and libmpeg3
+// and studying MPEG dumps.
+
+#include <config.h>
+#include "kfile_mpeg.h"
+
+#include <kprocess.h>
+#include <klocale.h>
+#include <kgenericfactory.h>
+#include <kstringvalidator.h>
+#include <kdebug.h>
+
+#include <qdict.h>
+#include <qvalidator.h>
+#include <qcstring.h>
+#include <qfile.h>
+#include <qdatetime.h>
+
+// #include <iostream>
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+
+typedef KGenericFactory<KMpegPlugin> MpegFactory;
+
+K_EXPORT_COMPONENT_FACTORY(kfile_mpeg, MpegFactory( "kfile_mpeg" ))
+
+KMpegPlugin::KMpegPlugin(QObject *parent, const char *name,
+ const QStringList &args)
+
+ : KFilePlugin(parent, name, args)
+{
+ KFileMimeTypeInfo* info = addMimeTypeInfo( "video/mpeg" );
+
+ KFileMimeTypeInfo::GroupInfo* group = 0L;
+
+ group = addGroupInfo(info, "Technical", i18n("Technical Details"));
+
+ KFileMimeTypeInfo::ItemInfo* item;
+
+ item = addItemInfo(group, "Length", i18n("Length"), QVariant::Int);
+ setUnit(item, KFileMimeTypeInfo::Seconds);
+
+ item = addItemInfo(group, "Resolution", i18n("Resolution"), QVariant::Size);
+
+ item = addItemInfo(group, "Frame rate", i18n("Frame Rate"), QVariant::Double);
+ setSuffix(item, i18n("fps"));
+
+ item = addItemInfo(group, "Video codec", i18n("Video Codec"), QVariant::String);
+ item = addItemInfo(group, "Audio codec", i18n("Audio Codec"), QVariant::String);
+
+ item = addItemInfo(group, "Aspect ratio", i18n("Aspect ratio"), QVariant::String);
+}
+
+// Frame-rate table from libmpeg3
+float frame_rate_table[16] =
+{
+ 0.0, /* Pad */
+ (float)24000.0/1001.0, /* Official frame rates */
+ (float)24.0,
+ (float)25.0,
+ (float)30000.0/1001.0,
+ (float)30.0,
+ (float)50.0,
+ (float)60000.0/1001.0,
+ (float)60.0,
+
+ 1, /* Unofficial economy rates */
+ 5,
+ 10,
+ 12,
+ 15,
+ 0,
+ 0,
+};
+
+/* Bitrate indexes */
+int bitrate_123[3][16] =
+ { {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,},
+ {0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,},
+ {0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,} };
+
+static const uint16_t sequence_start = 0x01b3;
+static const uint16_t ext_sequence_start = 0x01b5;
+static const uint16_t gop_start = 0x01b8;
+static const uint16_t audio1_packet = 0x01c0;
+static const uint16_t audio2_packet = 0x01d0;
+static const uint16_t private1_packet = 0x01bd;
+static const uint16_t private2_packet = 0x01bf;
+
+int KMpegPlugin::parse_seq() {
+ uint32_t buf;
+ dstream >> buf;
+
+ horizontal_size = (buf >> 20);
+ vertical_size = (buf >> 8) & ((1<<12)-1);
+ aspect_ratio = (buf >> 4) & ((1<<4)-1);
+ int framerate_code = buf & ((1<<4)-1);
+ frame_rate = frame_rate_table[framerate_code];
+
+ dstream >> buf;
+
+ bitrate = (buf >> 14);
+// kdDebug(7034) << "bitrate: " << bitrate << endl;
+ bool has_intra_matrix = buf & 2;
+ bool has_non_intra_matrix = buf & 1;
+
+ int matrix = 0;
+ if (has_intra_matrix) matrix +=64;
+ if (has_non_intra_matrix) matrix +=64;
+
+ mpeg = 1;
+ return matrix;
+}
+
+void KMpegPlugin::parse_seq_ext() {
+ uint32_t buf;
+ dstream >> buf;
+
+ uint8_t type = buf >> 28;
+ if (type == 1)
+ mpeg = 2;
+
+ /*
+ else
+ if (type == 2) {
+ dstream >> buf;
+ // These are display-sizes. I let them override physical sizes.
+ horizontal_size = (buf >> 18);
+ vertical_size = (buf >> 1) & ((1<<14)-1);
+} */
+}
+
+long KMpegPlugin::parse_gop() {
+ uint32_t buf;
+ dstream >> buf;
+ dstream >> buf;
+
+ int gop_hour = (buf>>26) & ((1<<5)-1);
+ kdDebug(7034) << "gop_hour: " << gop_hour << endl;
+ int gop_minute = (buf>>20) & ((1<<6)-1);
+ kdDebug(7034) << "gop_minute: " << gop_minute << endl;
+ int gop_second = (buf>>13) & ((1<<6)-1);
+ kdDebug(7034) << "gop_second: " << gop_second << endl;
+ int gop_frame = (buf>>7) & ((1<<6)-1);
+ kdDebug(7034) << "gop_frame: " << gop_frame << endl;
+
+ long seconds = gop_hour*60*60 + gop_minute*60 + gop_second;
+ return (long)seconds;
+}
+
+int KMpegPlugin::parse_audio() {
+ uint16_t len;
+ dstream >> len;
+// kdDebug(7034) << "Length of audio packet: " << len << endl;
+
+ uint8_t buf;
+ int i = 0;
+ for(i=0; i<20; i++) {
+ dstream >> buf;
+ if (buf == 0xff) {
+ dstream >> buf;
+ if ((buf & 0xe0) == 0xe0)
+ goto found_sync;
+ }
+ }
+ kdDebug(7034) << "MPEG audio sync not found" << endl;
+ return len-i;
+
+found_sync:
+
+ int layer = ((buf >> 1) & 0x3);
+ if (layer == 1)
+ audio_type = 3;
+ else if (layer == 2)
+ audio_type = 2;
+ else if (layer == 3)
+ audio_type = 1;
+ else
+ kdDebug(7034) << "Invalid MPEG audio layer" << endl;
+
+ dstream >> buf;
+ int bitrate_index = (buf & 0xf0) >> 4;
+ audio_rate = bitrate_123[3-layer][bitrate_index];
+
+ return len-3-i;
+}
+
+int KMpegPlugin::skip_packet() {
+ uint16_t len;
+ dstream >> len;
+// kdDebug(7034) << "Length of skipped packet: " << len << endl;
+
+ return len;
+}
+
+int KMpegPlugin::skip_riff_chunk() {
+ dstream.setByteOrder(QDataStream::LittleEndian);
+ uint32_t len;
+ dstream >> len;
+// std::cerr << "Length of skipped chunk: " << len << std::endl;
+
+ dstream.setByteOrder(QDataStream::BigEndian);
+ return len;
+}
+
+int KMpegPlugin::parse_private() {
+ uint16_t len;
+ dstream >> len;
+// kdDebug(7034) << "Length of private packet: " << len << endl;
+
+ // Match AC3 packets
+ uint8_t subtype;
+ dstream >> subtype;
+ subtype = subtype >> 4;
+ if (subtype == 8) // AC3
+ audio_type = 5;
+ else
+ if (subtype == 10) // LPCM
+ audio_type = 7;
+
+ return len-1;
+}
+
+bool KMpegPlugin::find_mpeg_in_cdxa()
+{
+ int skip_len = 0;
+ uint32_t magic;
+ uint32_t data_len;
+ // search for data chunk
+ while (true) {
+ dstream >> magic;
+ if (magic != 0x64617461) { // "fmt "
+ skip_len = skip_riff_chunk();
+ if (!file.at(file.at()+skip_len)) return false;
+ continue;
+ } else {
+ // size of chunk
+ dstream >> data_len;
+ int block = 0;
+ // search for mpeg part
+ while(block < 32) {
+ // check for CDXA sync thingy
+ dstream >> magic;
+ // 00 ff ff ff ff ff ff ff ff ff ff ff 00
+ if (magic == 0x00ffffff) {
+// std::cerr << "Found CD sync" << std::endl;
+ // skip 20 bytes
+ if (!file.at(file.at()+20)) return false;
+ dstream >> magic;
+ if (magic == 0x000001ba) {
+// std::cerr << "Found CDXA mpeg" << std::endl;
+ return true;
+ }
+ else {
+// std::cerr << "CDXA block: #" <<block << ": " << magic << std::endl;
+ if (!file.at(file.at()+2324)) return false;
+ block++;
+ continue;
+ }
+ } else {
+// std::cerr << "Incorrect CDXA block" << std::endl;
+ // shouldn't happen
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
+
+bool KMpegPlugin::read_mpeg()
+{
+ mpeg = 0;
+ audio_type = 0;
+ audio_rate = 0;
+
+ uint32_t magic;
+ dstream >> magic;
+ if (magic == 0x52494646) // == "RIFF"
+ {
+ dstream >> magic;
+ dstream >> magic;
+ if (magic != 0x43445841) { // 0x43445841 == "CDXA"
+ kdDebug(7034) << "Unknown RIFF file" << endl;
+ return false;
+ } else {
+ if (!find_mpeg_in_cdxa()) return false;
+ }
+ }
+ else
+ if (magic != 0x000001ba) {
+ kdDebug(7034) << "Not a MPEG-PS file" << endl;
+ return false;
+ }
+// file.at(0);
+
+ uint8_t byte;
+ int skip_len = 0;
+ int state = 0;
+ int skimmed = 0;
+ int video_len = 0;
+ int searched = 0;
+ bool video_found = false, audio_found = false, gop_found = false;
+ // Search for MPEG packets
+ for(int i=0; i < 2048; i++) {
+ dstream >> byte;
+ skimmed++;
+ searched++;
+ if (video_len > 0) video_len--;
+ // Use a fast state machine to find 00 00 01 sync code
+ switch (state) {
+ case 0:
+ if (byte == 0)
+ state = 1;
+ else
+ state = 0;
+ break;
+ case 1:
+ if (byte == 0)
+ state = 2;
+ else
+ state = 0;
+ break;
+ case 2:
+ if (byte == 0)
+ state = 2;
+ else
+ if (byte == 1)
+ state = 3;
+ else
+ state = 0;
+ break;
+ case 3: {
+ skimmed -= 4;
+ if (skimmed) {
+// kdDebug(7034) << "Bytes skimmed:" << skimmed << endl;
+ skimmed = 0;
+ }
+// kdDebug(7034) << "Packet of type:" << QString::number(byte,16) << endl;
+ switch (byte) {
+ case 0xb3:
+ if (video_found) break;
+ skip_len = parse_seq();
+ video_found = true;
+ video_len -= 8;
+ video_len -= skip_len;
+ break;
+ case 0xb5:
+ parse_seq_ext();
+ video_len -= 4;
+ break;
+ case 0xb8:
+ /*
+ if (!gop_found) {
+ start_time = parse_gop();
+ gop_found = true;
+ kdDebug(7034) << "start_time: " << start_time << endl;
+ }
+ */
+ /* nobreak */
+ case 0x00:
+ case 0x01:
+ // skip the rest of the video data
+ if (video_len > 0 && video_found)
+ skip_len = video_len;
+ break;
+ /*
+ case 0xb2:
+ skip_len = parse_user();
+ break;
+ */
+ case 0xba:
+ skip_len = 8;
+ break;
+ case 0xbe:
+ // padding
+ skip_len = skip_packet();
+ break;
+ case 0xe0:
+ // video data
+ if (video_found)
+ skip_len = skip_packet();
+ else
+ video_len = skip_packet();
+ break;
+ case 0xbd:
+ case 0xbf:
+ skip_len = parse_private();
+ break;
+ case 0xc0:
+ case 0xd0:
+ skip_len = parse_audio();
+ audio_found = true;
+ break;
+ default:
+// kdDebug(7034) << "Unhandled packet of type:" << QString::number(byte,16) << endl;
+ break;
+ }
+ state = 0;
+ break;
+ }
+ }
+
+ if (video_found && audio_found /*&& gop_found*/) break;
+ if (skip_len) {
+ if (!file.at(file.at()+skip_len))
+ return false;
+ searched += skip_len;
+ skip_len = 0;
+ }
+ }
+ /*
+ if (skimmed)
+ kdDebug(7034) << "Bytes skimmed:" << skimmed << endl;
+ kdDebug(7034) << "Bytes searched:" << searched << endl;
+ */
+
+ if (mpeg == 0) {
+ kdDebug(7034) << "No sequence-start found" << endl;
+ return false;
+ }
+ return true;
+}
+
+// Search for the last GOP packet and read the time field
+void KMpegPlugin::read_length()
+{
+ end_time = 0;
+ uint8_t byte;
+ int state = 0;
+ // Search for the last gop
+ file.at(file.size()-1024);
+ for(int j=1; j<64; j++) {
+// dstream.setDevice(&file);
+// dstream.setByteOrder(QDataStream::BigEndian);
+ for(int i=0; i<1024; i++) {
+ dstream >> byte;
+ switch (state) {
+ case 0:
+ if (byte == 0)
+ state = 1;
+ else
+ state = 0;
+ break;
+ case 1:
+ if (byte == 0)
+ state = 2;
+ else
+ state = 0;
+ case 2:
+ if (byte == 0)
+ state = 2;
+ else
+ if (byte == 1)
+ state = 3;
+ else
+ state = 0;
+ case 3:
+ if (byte == 0xb8) {
+ end_time = parse_gop();
+ kdDebug(7034) << "end_time: " << end_time << endl;
+ return;
+ }
+ state = 0;
+ }
+ }
+ state = 0;
+ file.at(file.size()-j*1024);
+ }
+ kdDebug(7034) << "No end GOP found" << endl;
+}
+
+bool KMpegPlugin::readInfo( KFileMetaInfo& info, uint /*what*/)
+{
+ if ( info.path().isEmpty() ) // remote file
+ return false;
+
+ file.setName(info.path());
+
+ // open file, set up stream and set endianness
+ if (!file.open(IO_ReadOnly))
+ {
+ kdDebug(7034) << "Couldn't open " << QFile::encodeName(info.path()) << endl;
+ return false;
+ }
+
+ dstream.setDevice(&file);
+ dstream.setByteOrder(QDataStream::BigEndian);
+
+ start_time = end_time = 0L;
+
+ if (!read_mpeg()) {
+ kdDebug(7034) << "read_mpeg() failed!" << endl;
+ }
+ else {
+ KFileMetaInfoGroup group = appendGroup(info, "Technical");
+
+ appendItem(group, "Frame rate", double(frame_rate));
+
+ appendItem(group, "Resolution", QSize(horizontal_size, vertical_size));
+ /* The GOP timings are completely bogus
+ read_length();
+ if (end_time != 0) {
+ //long total_frames = end_time-start_time + 1;
+ long total_time = end_time;
+ appendItem(group, "Length", int(total_time));
+ }
+ // and so is bitrate
+ long total_time = file.size()/((bitrate+audio_rate)*50);
+ appendItem(group, "Length", int(total_time));
+ */
+ if (mpeg == 1)
+ appendItem(group, "Video codec", "MPEG-1");
+ else
+ appendItem(group, "Video codec", "MPEG-2");
+
+ switch (audio_type) {
+ case 1:
+ appendItem(group, "Audio codec", "MP1");
+ break;
+ case 2:
+ appendItem(group, "Audio codec", "MP2");
+ break;
+ case 3:
+ appendItem(group, "Audio codec", "MP3");
+ break;
+ case 5:
+ appendItem(group, "Audio codec", "AC3");
+ break;
+ case 7:
+ appendItem(group, "Audio codec", "PCM");
+ break;
+ default:
+ appendItem(group, "Audio codec", i18n("Unknown"));
+ }
+ // MPEG 1 also has an aspect ratio setting, but it works differently,
+ // and I am not sure if it is used.
+ if (mpeg == 2) {
+ switch (aspect_ratio) {
+ case 1:
+ appendItem(group, "Aspect ratio", i18n("default"));
+ break;
+ case 2:
+ appendItem(group, "Aspect ratio", "4/3");
+ break;
+ case 3:
+ appendItem(group, "Aspect ratio", "16/9");
+ break;
+ case 4:
+ appendItem(group, "Aspect ratio", "2.11/1");
+ break;
+ }
+ }
+ }
+
+ file.close();
+ return true;
+}
+
+#include "kfile_mpeg.moc"
diff --git a/kfile-plugins/mpeg/kfile_mpeg.desktop b/kfile-plugins/mpeg/kfile_mpeg.desktop
new file mode 100644
index 00000000..1d5f18a4
--- /dev/null
+++ b/kfile-plugins/mpeg/kfile_mpeg.desktop
@@ -0,0 +1,55 @@
+[Desktop Entry]
+Type=Service
+Name=MPEG Info
+Name[bg]=Информация за MPEG
+Name[bn]=এম-পেগ তথ্য
+Name[br]=Titouroù MPEG
+Name[bs]=MPEG informacije
+Name[ca]=Informació MPEG
+Name[cs]=MPEG info
+Name[da]=MPEG-info
+Name[de]=MPEG-Info
+Name[el]=Πληροφορίες MPEG
+Name[eo]=MPEG-informo
+Name[es]=Info MPEG
+Name[et]=MPEG info
+Name[eu]=MPEG informazioa
+Name[fa]=اطلاعات MPEG
+Name[fi]=MPEG-tiedot
+Name[fr]=Informations MPEG
+Name[ga]=Eolas MPEG
+Name[gl]=Información MPEG
+Name[he]=מידע MPEG
+Name[hu]=MPEG-jellemzők
+Name[is]=MPEG upplýsingar
+Name[it]=Informazioni MPEG
+Name[ja]=MPEG 情報
+Name[kk]=MPEG мәліметі
+Name[km]=ព័ត៌មាន MPEG
+Name[ko]=MPEG 정보
+Name[lt]=MPEG informacija
+Name[nb]=MPEG informasjon
+Name[nds]=MPEG-Info
+Name[ne]=एमपीईजी सूचना
+Name[nl]=MPEG-info
+Name[nn]=MPEG-info
+Name[pa]=MPEG ਜਾਣਕਾਰੀ
+Name[pl]=Informacja o pliku MPEG
+Name[pt]=Informação do MPEG
+Name[pt_BR]=Informações sobre MPEG
+Name[ru]=Сведения о MPEG
+Name[sl]=Podatki o MPEG
+Name[sr]=Информације о MPEG-у
+Name[sr@Latn]=Informacije o MPEG-u
+Name[sv]=MPEG-information
+Name[th]=ข้อมูล MPEG
+Name[tr]=MP3 Bilgisi
+Name[uk]=Інформація про MPEG
+Name[zh_CN]=MPEG 信息
+Name[zh_HK]=MPEG 資訊
+Name[zh_TW]=MPEG 資訊
+ServiceTypes=KFilePlugin
+X-KDE-Library=kfile_mpeg
+MimeType=video/mpeg
+PreferredGrous=Technical
+PreferredItems=Length,Resolution,Frame rate,Video codec,Audio codec
diff --git a/kfile-plugins/mpeg/kfile_mpeg.h b/kfile-plugins/mpeg/kfile_mpeg.h
new file mode 100644
index 00000000..732b2a31
--- /dev/null
+++ b/kfile-plugins/mpeg/kfile_mpeg.h
@@ -0,0 +1,70 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2005 Allan Sandfeld Jensen <kde@carewolf.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation version 2.
+ *
+ * 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __KFILE_MPEG_H__
+#define __KFILE_MPEG_H__
+
+#include <kfilemetainfo.h>
+#include <qfile.h>
+
+class QStringList;
+
+class KMpegPlugin: public KFilePlugin
+{
+ Q_OBJECT
+
+public:
+ KMpegPlugin( QObject *parent, const char *name, const QStringList& args );
+
+ virtual bool readInfo( KFileMetaInfo& info, uint what);
+
+private:
+ int parse_seq();
+ void parse_seq_ext();
+ long parse_gop();
+ int parse_audio();
+ int parse_private();
+ int skip_packet();
+ int skip_riff_chunk();
+ bool find_mpeg_in_cdxa();
+
+ bool read_mpeg();
+ void read_length();
+
+ QFile file;
+ QDataStream dstream;
+
+ // MPEG information
+ int horizontal_size;
+ int vertical_size;
+ int aspect_ratio;
+ int bitrate;
+ float frame_rate;
+
+ int mpeg;
+ int audio_type;
+ int audio_rate;
+
+ long start_time;
+ long end_time;
+
+
+};
+
+#endif
diff --git a/kfile-plugins/ogg/Makefile.am b/kfile-plugins/ogg/Makefile.am
new file mode 100644
index 00000000..d10bfeb9
--- /dev/null
+++ b/kfile-plugins/ogg/Makefile.am
@@ -0,0 +1,22 @@
+## Makefile.am for ogg/vorbis file meta info plugin
+
+# set the include path for X, qt and KDE
+INCLUDES = $(all_includes)
+
+# these are the headers for your project
+noinst_HEADERS = kfile_ogg.h
+
+kde_module_LTLIBRARIES = kfile_ogg.la
+
+kfile_ogg_la_SOURCES = kfile_ogg.cpp vcedit.c
+kfile_ogg_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+kfile_ogg_la_LIBADD = $(LIB_KIO) -logg -lvorbis -lvorbisfile
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
+
+messages: rc.cpp
+ $(XGETTEXT) kfile_ogg.cpp -o $(podir)/kfile_ogg.pot
+
+services_DATA = kfile_ogg.desktop
+servicesdir = $(kde_servicesdir)
diff --git a/kfile-plugins/ogg/configure.in.in b/kfile-plugins/ogg/configure.in.in
new file mode 100644
index 00000000..35218d34
--- /dev/null
+++ b/kfile-plugins/ogg/configure.in.in
@@ -0,0 +1 @@
+AM_CONDITIONAL(include_ogg_SUBDIR, test "x$have_oggvorbis" = xyes)
diff --git a/kfile-plugins/ogg/kfile_ogg.cpp b/kfile-plugins/ogg/kfile_ogg.cpp
new file mode 100644
index 00000000..a7c6cfd5
--- /dev/null
+++ b/kfile-plugins/ogg/kfile_ogg.cpp
@@ -0,0 +1,357 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2001, 2002 Rolf Magnus <ramagnus@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation version 2.
+ *
+ * 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * $Id$
+ */
+
+#include "kfile_ogg.h"
+#include "vcedit.h"
+
+#include <qcstring.h>
+#include <qfile.h>
+#include <qdatetime.h>
+#include <qdict.h>
+#include <qvalidator.h>
+#include <qfileinfo.h>
+
+#include <kdebug.h>
+#include <kurl.h>
+#include <kprocess.h>
+#include <klocale.h>
+#include <kgenericfactory.h>
+#include <ksavefile.h>
+
+#include <ogg/ogg.h>
+#include <vorbis/codec.h>
+#include <vorbis/vorbisfile.h>
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+// known translations for common ogg/vorbis keys
+// from http://www.ogg.org/ogg/vorbis/doc/v-comment.html
+static const char* const knownTranslations[] = {
+ I18N_NOOP("Title"),
+ I18N_NOOP("Version"),
+ I18N_NOOP("Album"),
+ I18N_NOOP("Tracknumber"),
+ I18N_NOOP("Artist"),
+ I18N_NOOP("Organization"),
+ I18N_NOOP("Description"),
+ I18N_NOOP("Genre"),
+ I18N_NOOP("Date"),
+ I18N_NOOP("Location"),
+ I18N_NOOP("Copyright")
+// I18N_NOOP("Isrc") // dunno what an Isrc number is, the link is broken
+};
+
+K_EXPORT_COMPONENT_FACTORY(kfile_ogg, KGenericFactory<KOggPlugin>("kfile_ogg"))
+
+KOggPlugin::KOggPlugin( QObject *parent, const char *name,
+ const QStringList &args )
+ : KFilePlugin( parent, name, args )
+{
+ kdDebug(7034) << "ogg plugin\n";
+
+ KFileMimeTypeInfo* info = addMimeTypeInfo( "audio/vorbis" );
+
+ KFileMimeTypeInfo::GroupInfo* group = 0;
+
+ // comment group
+ group = addGroupInfo(info, "Comment", i18n("Comment"));
+ setAttributes(group, KFileMimeTypeInfo::Addable |
+ KFileMimeTypeInfo::Removable);
+
+ KFileMimeTypeInfo::ItemInfo* item = 0;
+
+ item = addItemInfo(group, "Artist", i18n("Artist"), QVariant::String);
+ setHint(item, KFileMimeTypeInfo::Author);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Title", i18n("Title"), QVariant::String);
+ setHint(item, KFileMimeTypeInfo::Name);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Album", i18n("Album"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Genre", i18n("Genre"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Tracknumber", i18n("Track Number"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Date", i18n("Date"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Description", i18n("Description"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Organization", i18n("Organization"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Location", i18n("Location"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "Copyright", i18n("Copyright"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+
+ addVariableInfo(group, QVariant::String, KFileMimeTypeInfo::Addable |
+ KFileMimeTypeInfo::Removable |
+ KFileMimeTypeInfo::Modifiable);
+
+ // technical group
+
+ group = addGroupInfo(info, "Technical", i18n("Technical Details"));
+ setAttributes(group, 0);
+
+ addItemInfo(group, "Version", i18n("Version"), QVariant::Int);
+ addItemInfo(group, "Channels", i18n("Channels"), QVariant::Int);
+
+ item = addItemInfo(group, "Sample Rate", i18n("Sample Rate"), QVariant::Int);
+ setSuffix(item, i18n(" Hz"));
+
+ item = addItemInfo(group, "UpperBitrate", i18n("Upper Bitrate"),
+ QVariant::Int);
+ setSuffix(item, i18n(" kbps"));
+
+ item = addItemInfo(group, "LowerBitrate", i18n("Lower Bitrate"),
+ QVariant::Int);
+ setSuffix(item, i18n(" kbps"));
+
+ item = addItemInfo(group, "NominalBitrate", i18n("Nominal Bitrate"),
+ QVariant::Int);
+ setSuffix(item, i18n(" kbps"));
+
+ item = addItemInfo(group, "Bitrate", i18n("Average Bitrate"),
+ QVariant::Int);
+ setAttributes(item, KFileMimeTypeInfo::Averaged);
+ setHint(item, KFileMimeTypeInfo::Bitrate);
+ setSuffix(item, i18n( " kbps"));
+
+ item = addItemInfo(group, "Length", i18n("Length"), QVariant::Int);
+ setAttributes(item, KFileMimeTypeInfo::Cummulative);
+ setUnit(item, KFileMimeTypeInfo::Seconds);
+}
+
+bool KOggPlugin::readInfo( KFileMetaInfo& info, uint what )
+{
+ // parts of this code taken from ogginfo.c of the vorbis-tools v1.0rc2
+ FILE *fp;
+ OggVorbis_File vf;
+ int rc,i;
+ vorbis_comment *vc;
+ vorbis_info *vi;
+
+ bool readComment = false;
+ bool readTech = false;
+ if (what & (KFileMetaInfo::Fastest |
+ KFileMetaInfo::DontCare |
+ KFileMetaInfo::ContentInfo)) readComment = true;
+
+ if (what & (KFileMetaInfo::Fastest |
+ KFileMetaInfo::DontCare |
+ KFileMetaInfo::TechnicalInfo)) readTech = true;
+
+ memset(&vf, 0, sizeof(OggVorbis_File));
+
+ if ( info.path().isEmpty() ) // remote file
+ return false;
+
+ fp = fopen(QFile::encodeName(info.path()),"rb");
+ if (!fp)
+ {
+ kdDebug(7034) << "Unable to open " << QFile::encodeName(info.path()) << endl;
+ return false;
+ }
+
+ rc = ov_open(fp,&vf,NULL,0);
+
+ if (rc < 0)
+ {
+ kdDebug(7034) << "Unable to understand " << QFile::encodeName(info.path())
+ << ", errorcode=" << rc << endl;
+ return false;
+ }
+
+// info.insert(KFileMetaInfoItem("Vendor", i18n("Vendor"),
+// QVariant(QString(vi->vendor))));
+
+ // get the vorbis comments
+ if (readComment)
+ {
+ vc = ov_comment(&vf,-1);
+
+ KFileMetaInfoGroup commentGroup = appendGroup(info, "Comment");
+
+ for (i=0; i < vc->comments; i++)
+ {
+ kdDebug(7034) << vc->user_comments[i] << endl;
+ QStringList split = QStringList::split("=", QString::fromUtf8(vc->user_comments[i]));
+ split[0] = split[0].lower();
+ split[0][0] = split[0][0].upper();
+
+ // we have to be sure that the i18n() string always has the same
+ // case. Oh, and is UTF8 ok here?
+ appendItem(commentGroup, split[0], split[1]);
+ }
+ }
+
+ if (readTech)
+ {
+ KFileMetaInfoGroup techgroup = appendGroup(info, "Technical");
+ // get other information about the file
+ vi = ov_info(&vf,-1);
+ if (vi)
+ {
+
+ appendItem(techgroup, "Version", int(vi->version));
+ appendItem(techgroup, "Channels", int(vi->channels));
+ appendItem(techgroup, "Sample Rate", int(vi->rate));
+
+ if (vi->bitrate_upper > 0)
+ appendItem(techgroup, "UpperBitrate",
+ int(vi->bitrate_upper+500)/1000);
+ if (vi->bitrate_lower > 0)
+ appendItem(techgroup, "LowerBitrate",
+ int(vi->bitrate_lower+500)/1000);
+ if (vi->bitrate_nominal > 0)
+ appendItem(techgroup, "NominalBitrate",
+ int(vi->bitrate_nominal+500)/1000);
+
+ if (ov_bitrate(&vf,-1) > 0)
+ appendItem(techgroup, "Bitrate", int(ov_bitrate(&vf,-1)+500)/1000);
+
+ }
+
+ appendItem(techgroup, "Length", int(ov_time_total(&vf,-1)));
+ }
+
+ ov_clear(&vf);
+
+ return true;
+}
+
+bool KOggPlugin::writeInfo(const KFileMetaInfo& info) const
+{
+ // todo: add writing support
+ FILE* infile;
+
+ infile = fopen(QFile::encodeName(info.path()), "r");
+
+ if (!infile)
+ {
+ kdDebug(7034) << "couldn't open " << info.path() << endl;
+ return false;
+ }
+
+ vcedit_state *state=vcedit_new_state();
+
+ if ( vcedit_open(state, infile)==-1 )
+ {
+ kdDebug(7034) << "error in vcedit_open for " << info.path() << endl;
+ return false;
+ }
+
+ struct vorbis_comment* oc = vcedit_comments(state);
+ struct vorbis_comment* vc = state->vc;
+
+ if(vc) vorbis_comment_clear(vc);
+
+ if (oc && oc->vendor)
+ {
+ vc->vendor = strdup(oc->vendor);
+ }
+ else
+ {
+ vc->vendor = strdup("");
+ }
+
+ KFileMetaInfoGroup group = info["Comment"];
+
+ QStringList keys = group.keys();
+ QStringList::Iterator it;
+ for (it = keys.begin(); it!=keys.end(); ++it)
+ {
+ KFileMetaInfoItem item = group[*it];
+
+ if (!item.isEditable() || !(item.type()==QVariant::String) )
+ continue;
+
+ QCString key = item.key().upper().utf8();
+ if (item.value().canCast(QVariant::String))
+ {
+ QCString value = item.value().toString().utf8();
+
+ kdDebug(7034) << " writing tag " << key << "=" << value << endl;
+
+ vorbis_comment_add_tag(vc,
+ const_cast<char*>(static_cast<const char*>(key)),
+ const_cast<char*>(static_cast<const char*>(value)));
+ }
+ else
+ kdWarning(7034) << "ignoring " << key << endl;
+
+ }
+
+ QString filename;
+
+ QFileInfo fileinfo(info.path());
+
+ // follow symlinks
+ if (fileinfo.isSymLink())
+ filename = fileinfo.readLink();
+ else
+ filename = info.path();
+
+ // nothing in Qt or KDE to get the mode as an int?
+ struct stat s;
+ stat(QFile::encodeName(filename), &s);
+
+ KSaveFile sf(filename, s.st_mode);
+ FILE* outfile = sf.fstream();
+
+ if ( sf.status() || !outfile)
+ {
+ kdDebug(7034) << "couldn't create temp file\n";
+ vcedit_clear(state); // frees comment entries and vendor
+ sf.abort();
+ if (vc->vendor) free(vc->vendor);
+ vc->vendor = 0;
+ return false;
+ }
+
+
+ vcedit_write(state,outfile); // calls vcedit_clear() itself so we don't free anything
+
+ if (vc->vendor) free(vc->vendor);
+ vc->vendor = 0;
+
+ fclose(infile);
+ sf.close();
+
+ return true;
+}
+
+QValidator* KOggPlugin::createValidator( const QString&,
+ const QString &, const QString &,
+ QObject* parent, const char* name) const {
+ return new QRegExpValidator(QRegExp(".*"), parent, name);
+}
+
+#include "kfile_ogg.moc"
diff --git a/kfile-plugins/ogg/kfile_ogg.desktop b/kfile-plugins/ogg/kfile_ogg.desktop
new file mode 100644
index 00000000..ef8c0bc2
--- /dev/null
+++ b/kfile-plugins/ogg/kfile_ogg.desktop
@@ -0,0 +1,68 @@
+[Desktop Entry]
+Type=Service
+Name=OGG Info
+Name[af]=Ogg Inligting
+Name[ar]=معلومات OGG
+Name[bg]=Информация за OGG
+Name[bn]=অগ তথ্য
+Name[br]=Titouroù OGG
+Name[bs]=OGG informacije
+Name[ca]=Informació OGG
+Name[cs]=OGG info
+Name[cy]=Gwybodaeth OGG
+Name[da]=OGG-info
+Name[de]=OGG-Info
+Name[el]=Πληροφορίες OGG
+Name[eo]=OGG-informo
+Name[es]=Info OGG
+Name[et]=OGG info
+Name[eu]=OGG informazioa
+Name[fa]=اطلاعات OGG
+Name[fi]=OGG-tiedot
+Name[fr]=Informations Ogg Vorbis
+Name[gl]=Información OGG
+Name[he]=מידע OGG
+Name[hi]=OGG जानकारी
+Name[hr]=Informacije o OGG datoteci
+Name[hu]=OGG-jellemzők
+Name[is]=OGG upplýsingar
+Name[it]=Informazioni OGG
+Name[ja]=OGG 情報
+Name[kk]=OGG мәліметі
+Name[km]=ព័ត៌មាន OGG
+Name[ko]=OGG 정보
+Name[lt]=OGG informacija
+Name[mk]=OGG информации
+Name[nb]=OGG informasjon
+Name[nds]=Ogg-Info
+Name[ne]=अग सूचना
+Name[nl]=OGG-informatie
+Name[nn]=OGG-info
+Name[pa]=OGG ਜਾਣਕਾਰੀ
+Name[pl]=Informacja o pliku OGG
+Name[pt]=Informação do OGG
+Name[pt_BR]=Informação sobre OGG
+Name[ro]=Informaţii OGG
+Name[ru]=Сведения о OGG
+Name[se]=OGG-dieđut
+Name[sl]=Podatki o OGG
+Name[sr]=Информације о OGG-у
+Name[sr@Latn]=Informacije o OGG-u
+Name[sv]=Ogg-information
+Name[ta]=OGG தகவல்
+Name[tg]=OGG Ахборот
+Name[th]=ข้อมูล OGG
+Name[tr]=OGG Bilgisi
+Name[uk]=Інформація по OGG
+Name[uz]=OGG haqida maʼlumot
+Name[uz@cyrillic]=OGG ҳақида маълумот
+Name[xh]=OGG Ulwazi
+Name[zh_CN]=OGG 信息
+Name[zh_HK]=OGG 資訊
+Name[zh_TW]=OGG 資訊
+Name[zu]=Ulwazi lwe OGG
+ServiceTypes=KFilePlugin
+X-KDE-Library=kfile_ogg
+MimeType=audio/vorbis
+PreferredGroups=Comment,Technical
+PreferredItems=Title,Artist,Album,Tracknumber,Genre,Bitrate,Length,Date,Description,Organization,Channels,UpperBitrate,LowerBitrate,NominalBitrate,Location,Copyright,Version
diff --git a/kfile-plugins/ogg/kfile_ogg.h b/kfile-plugins/ogg/kfile_ogg.h
new file mode 100644
index 00000000..7a7e75a4
--- /dev/null
+++ b/kfile-plugins/ogg/kfile_ogg.h
@@ -0,0 +1,45 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2001, 2002 Rolf Magnus <ramagnus@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation version 2.
+ *
+ * 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * $Id$
+ */
+
+#ifndef __KFILE_OGG_H__
+#define __KFILE_OGG_H__
+
+#include <kfilemetainfo.h>
+
+class QString;
+class QStringList;
+
+class KOggPlugin: public KFilePlugin
+{
+ Q_OBJECT
+
+public:
+ KOggPlugin( QObject *parent, const char *name, const QStringList& args );
+
+ virtual bool readInfo( KFileMetaInfo& info, uint what);
+ virtual bool writeInfo( const KFileMetaInfo& info ) const;
+ virtual QValidator* createValidator( const QString& mimetype,
+ const QString &group,
+ const QString &key,
+ QObject* parent, const char* name) const;
+};
+
+
+#endif
diff --git a/kfile-plugins/ogg/vcedit.c b/kfile-plugins/ogg/vcedit.c
new file mode 100644
index 00000000..76e31f6c
--- /dev/null
+++ b/kfile-plugins/ogg/vcedit.c
@@ -0,0 +1,331 @@
+/* This program is licensed under the GNU Library General Public License, version 2
+ *
+ * (c) 2000-2001 Michael Smith <msmith@labyrinth.net.au>
+ *
+ * Modified by Warren Spits <spits@cyberdude.com>
+ * - Handles vorbis files that are truncated or missing an eos flag.
+ *
+ * Comment editing backend, suitable for use by nice frontend interfaces.
+ *
+ * last modified: $Id$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ogg/ogg.h>
+#include <vorbis/codec.h>
+
+#include "vcedit.h"
+
+#define CHUNKSIZE 4096
+
+vcedit_state *vcedit_new_state(void)
+{
+ vcedit_state *state = malloc(sizeof(vcedit_state));
+ memset(state, 0, sizeof(vcedit_state));
+
+ return state;
+}
+
+char *vcedit_error(vcedit_state *state)
+{
+ return state->lasterror;
+}
+
+vorbis_comment *vcedit_comments(vcedit_state *state)
+{
+ return state->vc;
+}
+
+static void vcedit_clear_internals(vcedit_state *state)
+{
+ if(state->vc)
+ {
+ vorbis_comment_clear(state->vc);
+ free(state->vc);
+ state->vc=NULL;
+ }
+ if(state->os)
+ {
+ ogg_stream_clear(state->os);
+ free(state->os);
+ state->os=NULL;
+ }
+ if(state->oy)
+ {
+ ogg_sync_clear(state->oy);
+ free(state->oy);
+ state->oy=NULL;
+ }
+}
+
+void vcedit_clear(vcedit_state *state)
+{
+ if(state)
+ {
+ vcedit_clear_internals(state);
+ free(state);
+ }
+}
+
+int vcedit_open(vcedit_state *state, FILE *in)
+{
+ return vcedit_open_callbacks(state, (void *)in,
+ (vcedit_read_func)fread, (vcedit_write_func)fwrite);
+}
+
+int vcedit_open_callbacks(vcedit_state *state, void *in,
+ vcedit_read_func read_func, vcedit_write_func write_func)
+{
+
+ char *buffer;
+ int bytes,i;
+ ogg_packet *header;
+ ogg_packet header_main;
+ ogg_packet header_comments;
+ ogg_packet header_codebooks;
+ ogg_page og;
+ vorbis_info vi;
+
+
+ state->in = in;
+ state->read = read_func;
+ state->write = write_func;
+ state->lasterror = 0;
+
+ state->oy = malloc(sizeof(ogg_sync_state));
+ ogg_sync_init(state->oy);
+
+ buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
+ bytes = state->read(buffer, 1, CHUNKSIZE, state->in);
+
+ ogg_sync_wrote(state->oy, bytes);
+
+ if(ogg_sync_pageout(state->oy, &og) != 1)
+ {
+ if(bytes<CHUNKSIZE)
+ state->lasterror = "Input truncated or empty.";
+ else
+ state->lasterror = "Input is not an Ogg bitstream.";
+ goto err;
+ }
+
+ state->serial = ogg_page_serialno(&og);
+
+ state->os = malloc(sizeof(ogg_stream_state));
+ ogg_stream_init(state->os, state->serial);
+
+ vorbis_info_init(&vi);
+
+ state->vc = malloc(sizeof(vorbis_comment));
+ vorbis_comment_init(state->vc);
+
+ if(ogg_stream_pagein(state->os, &og) < 0)
+ {
+ state->lasterror = "Error reading first page of Ogg bitstream.";
+ goto err;
+ }
+
+ if(ogg_stream_packetout(state->os, &header_main) != 1)
+ {
+ state->lasterror = "Error reading initial header packet.";
+ goto err;
+ }
+
+ if(vorbis_synthesis_headerin(&vi, state->vc, &header_main) < 0)
+ {
+ state->lasterror = "Ogg bitstream does not contain vorbis data.";
+ goto err;
+ }
+
+ state->mainlen = header_main.bytes;
+ state->mainbuf = malloc(state->mainlen);
+ memcpy(state->mainbuf, header_main.packet, header_main.bytes);
+
+ i = 0;
+ header = &header_comments;
+ while(i<2) {
+ while(i<2) {
+ int result = ogg_sync_pageout(state->oy, &og);
+ if(result == 0) break; /* Too little data so far */
+ else if(result == 1)
+ {
+ ogg_stream_pagein(state->os, &og);
+ while(i<2)
+ {
+ result = ogg_stream_packetout(state->os, header);
+ if(result == 0) break;
+ if(result == -1)
+ {
+ state->lasterror = "Corrupt secondary header.";
+ goto err;
+ }
+ vorbis_synthesis_headerin(&vi, state->vc, header);
+ if(i==1)
+ {
+ state->booklen = header->bytes;
+ state->bookbuf = malloc(state->booklen);
+ memcpy(state->bookbuf, header->packet,
+ header->bytes);
+ }
+ i++;
+ header = &header_codebooks;
+ }
+ }
+ }
+
+ buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
+ bytes = state->read(buffer, 1, CHUNKSIZE, state->in);
+ if(bytes == 0 && i < 2)
+ {
+ state->lasterror = "EOF before end of vorbis headers.";
+ goto err;
+ }
+ ogg_sync_wrote(state->oy, bytes);
+ }
+
+ /* Headers are done! */
+ vorbis_info_clear(&vi);
+ return 0;
+
+err:
+ vcedit_clear_internals(state);
+ return -1;
+}
+
+int vcedit_write(vcedit_state *state, void *out)
+{
+ ogg_stream_state streamout;
+ ogg_packet header_main;
+ ogg_packet header_comments;
+ ogg_packet header_codebooks;
+
+ ogg_page ogout, ogin;
+ ogg_packet op;
+ int result, outresult;
+ char *buffer;
+ int bytes, eosin=0, eosout=0;
+
+ state->lasterror = 0;
+
+ header_main.bytes = state->mainlen;
+ header_main.packet = state->mainbuf;
+ header_main.b_o_s = 1;
+ header_main.e_o_s = 0;
+ header_main.granulepos = 0;
+
+ header_codebooks.bytes = state->booklen;
+ header_codebooks.packet = state->bookbuf;
+ header_codebooks.b_o_s = 0;
+ header_codebooks.e_o_s = 0;
+ header_codebooks.granulepos = 0;
+
+ ogg_stream_init(&streamout, state->serial);
+
+ vorbis_commentheader_out(state->vc, &header_comments);
+
+ ogg_stream_packetin(&streamout, &header_main);
+ ogg_stream_packetin(&streamout, &header_comments);
+ ogg_stream_packetin(&streamout, &header_codebooks);
+
+ while((result = ogg_stream_flush(&streamout, &ogout)))
+ {
+ if(state->write(ogout.header,1,ogout.header_len, out) !=
+ (size_t) ogout.header_len)
+ goto cleanup;
+ if(state->write(ogout.body,1,ogout.body_len, out) !=
+ (size_t) ogout.body_len)
+ goto cleanup;
+ }
+
+ /* We copy the first logical stream
+ * through, rewriting the stream. */
+ while (1)
+ {
+ outresult = eosin ? ogg_stream_flush(&streamout, &ogout) :
+ ogg_stream_pageout(&streamout, &ogout);
+ if (outresult > 0)
+ {
+ if (state->write(ogout.header,1,ogout.header_len,
+ out) != (size_t) ogout.header_len)
+ goto cleanup;
+ if (state->write(ogout.body,1,ogout.body_len,
+ out) != (size_t) ogout.body_len)
+ goto cleanup;
+ if (ogg_page_eos(&ogout)) eosout = 1;
+ }
+ if (outresult != 0) continue;
+ if (eosout || (eosin && (result == 0))) break;
+
+ while (1)
+ {
+ result = ogg_stream_packetout(state->os, &op);
+ if (result < 0) continue;
+ if (result > 0) ogg_stream_packetin(&streamout, &op);
+ if (eosin || (result > 0)) break;
+
+ while (1)
+ {
+ result = ogg_sync_pageout(state->oy, &ogin);
+
+ if (result < 0) continue;
+ if (result > 0)
+ {
+ ogg_stream_pagein(state->os, &ogin);
+ if (ogg_page_eos(&ogin)) eosin = 1;
+ }
+ if (eosin || (result > 0)) break;
+
+ buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
+ bytes = state->read(buffer,1, CHUNKSIZE, state->in);
+ ogg_sync_wrote(state->oy, bytes);
+ if (bytes < CHUNKSIZE) eosin = 1;
+ }
+ }
+ }
+
+ eosin=0; /* clear it, because not all paths to here do */
+ eosout=1; /* handle input files that are truncated or without an eos flag */
+
+ /* We copy the rest of the stream (other logical streams)
+ * through, a page at a time. */
+ while (1)
+ {
+ result = ogg_sync_pageout(state->oy, &ogout);
+ if (result > 0)
+ {
+ if (state->write(ogout.header,1,ogout.header_len,
+ out) != (size_t) ogout.header_len)
+ goto cleanup;
+ if (state->write(ogout.body,1,ogout.body_len, out) !=
+ (size_t) ogout.body_len)
+ goto cleanup;
+ }
+ if (result != 0) continue;
+ if (eosin) break;
+
+ buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
+ bytes = state->read(buffer,1, CHUNKSIZE, state->in);
+ ogg_sync_wrote(state->oy, bytes);
+ eosin = (bytes < CHUNKSIZE);
+ }
+
+cleanup:
+ ogg_stream_clear(&streamout);
+ ogg_packet_clear(&header_comments);
+
+ free(state->mainbuf);
+ free(state->bookbuf);
+
+ vcedit_clear_internals(state);
+ if(!(eosin && eosout))
+ {
+ state->lasterror =
+ "Error writing stream to output. "
+ "Output stream may be corrupted or truncated.";
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/kfile-plugins/ogg/vcedit.h b/kfile-plugins/ogg/vcedit.h
new file mode 100644
index 00000000..6be136ba
--- /dev/null
+++ b/kfile-plugins/ogg/vcedit.h
@@ -0,0 +1,56 @@
+/* This program is licensed under the GNU General Public License, version 2,
+ * a copy of which is included with this program.
+ *
+ * (c) 2000-2001 Michael Smith <msmith@labyrinth.net.au>
+ *
+ * VCEdit header.
+ *
+ * last modified: $ID:$
+ */
+
+#ifndef __VCEDIT_H
+#define __VCEDIT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <ogg/ogg.h>
+#include <vorbis/codec.h>
+
+typedef size_t (*vcedit_read_func)(void *, size_t, size_t, void *);
+typedef size_t (*vcedit_write_func)(const void *, size_t, size_t, void *);
+
+typedef struct {
+ ogg_sync_state *oy;
+ ogg_stream_state *os;
+
+ vorbis_comment *vc;
+
+ vcedit_read_func read;
+ vcedit_write_func write;
+
+ void *in;
+ long serial;
+ unsigned char *mainbuf;
+ unsigned char *bookbuf;
+ int mainlen;
+ int booklen;
+ char *lasterror;
+} vcedit_state;
+
+extern vcedit_state * vcedit_new_state(void);
+extern void vcedit_clear(vcedit_state *state);
+extern vorbis_comment * vcedit_comments(vcedit_state *state);
+extern int vcedit_open(vcedit_state *state, FILE *in);
+extern int vcedit_open_callbacks(vcedit_state *state, void *in,
+ vcedit_read_func read_func, vcedit_write_func write_func);
+extern int vcedit_write(vcedit_state *state, void *out);
+extern char * vcedit_error(vcedit_state *state);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __VCEDIT_H */
diff --git a/kfile-plugins/sid/Makefile.am b/kfile-plugins/sid/Makefile.am
new file mode 100644
index 00000000..5a3c33e1
--- /dev/null
+++ b/kfile-plugins/sid/Makefile.am
@@ -0,0 +1,22 @@
+## Makefile.am for sid file meta info plugin
+
+# set the include path for X, qt and KDE
+INCLUDES = $(all_includes) $(taglib_includes)
+
+# these are the headers for your project
+noinst_HEADERS = kfile_sid.h
+
+kde_module_LTLIBRARIES = kfile_sid.la
+
+kfile_sid_la_SOURCES = kfile_sid.cpp
+kfile_sid_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+kfile_sid_la_LIBADD = $(LIB_KIO)
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
+
+messages: rc.cpp
+ $(XGETTEXT) kfile_sid.cpp -o $(podir)/kfile_sid.pot
+
+services_DATA = kfile_sid.desktop
+servicesdir = $(kde_servicesdir)
diff --git a/kfile-plugins/sid/kfile_sid.cpp b/kfile-plugins/sid/kfile_sid.cpp
new file mode 100644
index 00000000..92b03c30
--- /dev/null
+++ b/kfile-plugins/sid/kfile_sid.cpp
@@ -0,0 +1,227 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2003 Rolf Magnus <ramagnus@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation version 2.
+ *
+ * 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "kfile_sid.h"
+
+#include <klocale.h>
+#include <kgenericfactory.h>
+#include <kstringvalidator.h>
+#include <kdebug.h>
+
+#include <qfile.h>
+#include <qvalidator.h>
+#include <qwidget.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+typedef KGenericFactory<KSidPlugin> SidFactory;
+
+K_EXPORT_COMPONENT_FACTORY(kfile_sid, SidFactory("kfile_sid"))
+
+KSidPlugin::KSidPlugin(QObject *parent, const char *name,
+ const QStringList &args)
+
+ : KFilePlugin(parent, name, args)
+{
+ kdDebug(7034) << "sid plugin\n";
+
+ KFileMimeTypeInfo* info = addMimeTypeInfo("audio/prs.sid");
+
+ KFileMimeTypeInfo::GroupInfo* group = 0L;
+
+ // General group
+ group = addGroupInfo(info, "General", i18n("General"));
+
+ KFileMimeTypeInfo::ItemInfo* item;
+
+ item = addItemInfo(group, "Title", i18n("Title"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+ setHint(item, KFileMimeTypeInfo::Name);
+
+ item = addItemInfo(group, "Artist", i18n("Artist"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+ setHint(item, KFileMimeTypeInfo::Author);
+
+ item = addItemInfo(group, "Copyright", i18n("Copyright"), QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+ setHint(item, KFileMimeTypeInfo::Description);
+
+ // technical group
+ group = addGroupInfo(info, "Technical", i18n("Technical Details"));
+
+ item = addItemInfo(group, "Version", i18n("Version"), QVariant::Int);
+ setPrefix(item, i18n("PSID v"));
+
+ addItemInfo(group, "Number of Songs", i18n("Number of Songs"), QVariant::Int);
+ item = addItemInfo(group, "Start Song", i18n("Start Song"), QVariant::Int);
+}
+
+bool KSidPlugin::readInfo(KFileMetaInfo& info, uint /*what*/)
+{
+ if ( info.path().isEmpty() ) // remote file
+ return false;
+ QFile file(info.path());
+ if ( !file.open(IO_ReadOnly) )
+ return false;
+
+ int version;
+ int num_songs;
+ int start_song;
+ QString name;
+ QString artist;
+ QString copyright;
+
+ char buf[64] = { 0 };
+
+ if (4 != file.readBlock(buf, 4))
+ return false;
+ if (strncmp(buf, "PSID", 4))
+ return false;
+
+ //read version
+ int ch;
+ if (0 > (ch = file.getch()))
+ return false;
+ version = ch << 8;
+ if (0 > (ch = file.getch()))
+ return false;
+ version+= ch;
+
+ //read number of songs
+ file.at(0xE);
+ if (0 > (ch = file.getch()))
+ return false;
+ num_songs = ch << 8;
+ if (0 > (ch = file.getch()))
+ return false;
+ num_songs += ch;
+
+ //start song
+ if (0 > (ch = file.getch()))
+ return false;
+ start_song = ch << 8;
+ if (0 > (ch = file.getch()))
+ return false;
+ start_song += ch;
+
+ //name
+ file.at(0x16);
+ if (32 != file.readBlock(buf, 32))
+ return false;
+ name = buf;
+
+ //artist
+ if (32 != file.readBlock(buf, 32))
+ return false;
+ artist = buf;
+
+ //copyright
+ if (32 != file.readBlock(buf, 32))
+ return false;
+ copyright = buf;
+
+ QString TODO("TODO");
+ kdDebug(7034) << "sid plugin readInfo\n";
+
+ KFileMetaInfoGroup general = appendGroup(info, "General");
+
+ appendItem(general, "Title", name);
+ appendItem(general, "Artist", artist);
+ appendItem(general, "Copyright", copyright);
+
+ KFileMetaInfoGroup tech = appendGroup(info, "Technical");
+
+ appendItem(tech, "Version", version);
+ appendItem(tech, "Number of Songs", num_songs);
+ appendItem(tech, "Start Song", start_song);
+
+ kdDebug(7034) << "reading finished\n";
+ return true;
+}
+
+bool KSidPlugin::writeInfo(const KFileMetaInfo& info) const
+{
+ kdDebug(7034) << k_funcinfo << endl;
+
+ char name[32] = {0};
+ char artist[32] = {0};
+ char copyright[32] = {0};
+
+ int file = 0;
+ QString s;
+
+ KFileMetaInfoGroup group = info.group("General");
+ if (!group.isValid())
+ goto failure;
+
+ s = group.item("Title").value().toString();
+ if (s.isNull()) goto failure;
+ strncpy(name, s.local8Bit(), 31);
+
+ s = group.item("Artist").value().toString();
+ if (s.isNull()) goto failure;
+ strncpy(artist, s.local8Bit(), 31);
+
+ s = group.item("Copyright").value().toString();
+ if (s.isNull()) goto failure;
+ strncpy(copyright, s.local8Bit(), 31);
+
+ kdDebug(7034) << "Opening sid file " << info.path() << endl;
+ file = ::open(QFile::encodeName(info.path()), O_WRONLY);
+ //name
+ if (-1 == ::lseek(file, 0x16, SEEK_SET))
+ goto failure;
+ if (32 != ::write(file, name, 32))
+ goto failure;
+
+ //artist
+ if (32 != ::write(file, artist, 32))
+ goto failure;
+
+ //copyright
+ if (32 != write(file, copyright, 32))
+ goto failure;
+
+ close(file);
+ return true;
+
+failure:
+ if (file) close(file);
+ kdDebug(7034) << "something went wrong writing to sid file\n";
+ return false;
+}
+
+QValidator*
+KSidPlugin::createValidator(const QString& /*mimetype*/, const QString& group,
+ const QString& /*key*/, QObject* parent,
+ const char* name) const
+{
+ kdDebug(7034) << k_funcinfo << endl;
+ // all items in "General" group are strings of max lenght 31
+ if (group == "General")
+ return new QRegExpValidator(QRegExp(".{,31}"), parent, name);
+ // all others are read-only
+ return 0;
+}
+
+
+
+#include "kfile_sid.moc"
diff --git a/kfile-plugins/sid/kfile_sid.desktop b/kfile-plugins/sid/kfile_sid.desktop
new file mode 100644
index 00000000..e3f29ebc
--- /dev/null
+++ b/kfile-plugins/sid/kfile_sid.desktop
@@ -0,0 +1,60 @@
+[Desktop Entry]
+Type=Service
+Name=SID Info
+Name[bg]=Информация за SID
+Name[br]=Titouroù SID
+Name[bs]=SID informacije
+Name[ca]=Informació SID
+Name[cs]=SID info
+Name[cy]=Gwybodaeth SID
+Name[da]=SID-info
+Name[de]=SID-Info
+Name[el]=Πληροφορίες SID
+Name[eo]=SID-informo
+Name[es]=Info SID
+Name[et]=SID info
+Name[eu]=SID informazioa
+Name[fa]=اطلاعات SID
+Name[fi]=SID-tiedot
+Name[fr]=Informations SID
+Name[ga]=Eolas faoi SID
+Name[gl]=Información SID
+Name[he]=מידע SID
+Name[hu]=SID-jellemzők
+Name[is]=SID upplýsingar
+Name[it]=Informazioni SID
+Name[ja]=SID 情報
+Name[kk]=SID мәліметі
+Name[km]=ព័ត៌មាន SID
+Name[ko]=SID 정보
+Name[lt]=SID Informacija
+Name[mk]=SID информации
+Name[nb]=SID-info
+Name[nds]=SID-Info
+Name[ne]=एसआईडी सूचना
+Name[nl]=SID-informatie
+Name[nn]=SID-info
+Name[pa]=SID ਜਾਣਕਾਰੀ
+Name[pl]=Informacja CD
+Name[pt]=Informação do SID
+Name[pt_BR]=Informação sobre SID
+Name[ro]=Informaţii SID
+Name[ru]=Сведения о SID
+Name[sk]=SID info
+Name[sl]=Podatki o SID
+Name[sr]=Информације о SID-у
+Name[sr@Latn]=Informacije o SID-u
+Name[sv]=SID-information
+Name[ta]=SID தகவல்
+Name[tg]=SID Ахборот
+Name[th]=ข้อมูล SID
+Name[tr]=SID Bilgisi
+Name[uk]=Інформація по SID
+Name[zh_CN]=SID 信息
+Name[zh_HK]=SID 資訊
+Name[zh_TW]=SID 資訊
+ServiceTypes=KFilePlugin
+X-KDE-Library=kfile_sid
+MimeType=audio/prs.sid
+PreferredGroups=General,Technical
+PreferredItems=Title,Artist,Copyright,Number of Songs,Start Song,Version
diff --git a/kfile-plugins/sid/kfile_sid.h b/kfile-plugins/sid/kfile_sid.h
new file mode 100644
index 00000000..13978320
--- /dev/null
+++ b/kfile-plugins/sid/kfile_sid.h
@@ -0,0 +1,42 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2003 Rolf Magnus <ramagnus@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation version 2.
+ *
+ * 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * $Id$
+ */
+
+#ifndef KFILE_SID_H
+#define KFILE_SID_H
+
+#include <kfilemetainfo.h>
+
+class QStringList;
+
+class KSidPlugin: public KFilePlugin
+{
+ Q_OBJECT
+
+public:
+ KSidPlugin(QObject *parent, const char *name, const QStringList& args);
+
+ virtual bool readInfo(KFileMetaInfo& info, uint what);
+ virtual bool writeInfo(const KFileMetaInfo& info) const;
+ QValidator* createValidator(const QString& mimetype, const QString& group,
+ const QString& key, QObject* parent,
+ const char* name) const;
+};
+
+#endif
diff --git a/kfile-plugins/theora/Makefile.am b/kfile-plugins/theora/Makefile.am
new file mode 100644
index 00000000..e2029d87
--- /dev/null
+++ b/kfile-plugins/theora/Makefile.am
@@ -0,0 +1,22 @@
+## Makefile.am for ogg/vorbis file meta info plugin
+
+# set the include path for X, qt and KDE
+INCLUDES = $(all_includes)
+
+# these are the headers for your project
+noinst_HEADERS = kfile_theora.h
+
+kde_module_LTLIBRARIES = kfile_theora.la
+
+kfile_theora_la_SOURCES = kfile_theora.cpp
+kfile_theora_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+kfile_theora_la_LIBADD = $(LIB_KIO) -logg -lvorbis -ltheora
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
+
+messages: rc.cpp
+ $(XGETTEXT) kfile_theora.cpp -o $(podir)/kfile_theora.pot
+
+services_DATA = kfile_theora.desktop
+servicesdir = $(kde_servicesdir)
diff --git a/kfile-plugins/theora/configure.in.bot b/kfile-plugins/theora/configure.in.bot
new file mode 100644
index 00000000..926a39e9
--- /dev/null
+++ b/kfile-plugins/theora/configure.in.bot
@@ -0,0 +1,7 @@
+if test "x$with_theora" = xcheck && test "x$have_theora" = xno; then
+ echo ""
+ echo "Ogg Theora support was not found."
+ echo "a KFile-plugin for displaying Ogg Theora Information"
+ echo "has been disabled from compilation."
+ all_tests=bad
+fi
diff --git a/kfile-plugins/theora/configure.in.in b/kfile-plugins/theora/configure.in.in
new file mode 100644
index 00000000..b9d8836d
--- /dev/null
+++ b/kfile-plugins/theora/configure.in.in
@@ -0,0 +1,26 @@
+AC_DEFUN([KDE_CHECK_THEORA],
+[
+have_theora=yes
+
+KDE_CHECK_HEADER(theora/theora.h,
+ [], [have_theora=no])
+
+KDE_CHECK_LIB(theora, theora_info_init,
+ [], [have_theora=no], [-lvorbis -logg])
+])
+
+AC_ARG_WITH(theora,
+ [AC_HELP_STRING(--with-theora,
+ [enable support for Ogg Theora @<:@default=check@:>@])],
+ [], with_theora=check)
+
+have_theora=no
+if test "x$with_theora" != xno; then
+ KDE_CHECK_THEORA
+
+ if test "x$with_theora" != xcheck && test "x$have_theora" != xyes; then
+ AC_MSG_ERROR([--with-theora was given, but test for Theora failed])
+ fi
+fi
+
+AM_CONDITIONAL(include_theora_SUBDIR, test "x$have_theora" = xyes)
diff --git a/kfile-plugins/theora/kfile_theora.cpp b/kfile-plugins/theora/kfile_theora.cpp
new file mode 100644
index 00000000..35dee3d9
--- /dev/null
+++ b/kfile-plugins/theora/kfile_theora.cpp
@@ -0,0 +1,322 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Jean-Baptiste Mardelle *
+ * bj@altern.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <qfile.h>
+#include <config.h>
+#include "kfile_theora.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kgenericfactory.h>
+
+#include "theora/theora.h"
+#include "vorbis/codec.h"
+
+ogg_stream_state t_stream_state;
+ogg_stream_state v_stream_state;
+
+int theora_p=0;
+int vorbis_p=0;
+
+static int queue_page(ogg_page *page)
+{
+ if(theora_p)
+ ogg_stream_pagein(&t_stream_state,page);
+ if(vorbis_p)
+ ogg_stream_pagein(&v_stream_state,page);
+ return 0;
+}
+
+static int buffer_data(FILE *in,ogg_sync_state *oy)
+{
+ char *buffer=ogg_sync_buffer(oy,4096);
+ int bytes=fread(buffer,1,4096,in);
+ ogg_sync_wrote(oy,bytes);
+ return(bytes);
+}
+
+typedef KGenericFactory<theoraPlugin> theoraFactory;
+
+K_EXPORT_COMPONENT_FACTORY(kfile_theora, theoraFactory( "kfile_theora" ))
+
+theoraPlugin::theoraPlugin(QObject *parent, const char *name,
+ const QStringList &args)
+ : KFilePlugin(parent, name, args)
+{
+// kdDebug(7034) << "theora plugin\n";
+
+ KFileMimeTypeInfo* info = addMimeTypeInfo( "video/x-theora" );
+
+ KFileMimeTypeInfo::GroupInfo* group = 0;
+ KFileMimeTypeInfo::ItemInfo* item;
+
+ // video group
+
+ group = addGroupInfo(info, "Video", i18n("Video Details"));
+ setAttributes(group, 0);
+ item = addItemInfo(group, "Length", i18n("Length"), QVariant::Int);
+ setUnit(item, KFileMimeTypeInfo::Seconds);
+ setHint(item, KFileMimeTypeInfo::Length);
+ item = addItemInfo(group, "Resolution", i18n("Resolution"), QVariant::Size);
+ setHint(item, KFileMimeTypeInfo::Size);
+ setUnit(item, KFileMimeTypeInfo::Pixels);
+ item = addItemInfo(group, "FrameRate", i18n("Frame Rate"), QVariant::Int);
+ setUnit(item, KFileMimeTypeInfo::FramesPerSecond);
+ item = addItemInfo(group, "TargetBitrate", i18n("Target Bitrate"), QVariant::Int);
+ setUnit(item, KFileMimeTypeInfo::Bitrate);
+ item = addItemInfo(group, "Quality", i18n("Quality"), QVariant::Int);
+
+ // audio group
+
+ group = addGroupInfo(info, "Audio", i18n("Audio Details"));
+ setAttributes(group, 0);
+ addItemInfo(group, "Channels", i18n("Channels"), QVariant::Int);
+
+ item = addItemInfo(group, "SampleRate", i18n("Sample Rate"), QVariant::Int);
+ setUnit(item, KFileMimeTypeInfo::Hertz);
+}
+
+bool theoraPlugin::readInfo( KFileMetaInfo& info, uint what)
+{
+ // most of the ogg stuff was borrowed from libtheora/examples/player_example.c
+ FILE *fp;
+
+ ogg_sync_state o_sync_state;
+ ogg_packet o_packet;
+ ogg_page o_page;
+
+ theora_info t_info;
+ theora_comment t_comment;
+ theora_state t_state;
+ vorbis_info v_info;
+ vorbis_comment v_comment;
+
+ theora_p=0;
+ vorbis_p=0;
+ int theora_serial=0;
+ int stateflag=0;
+
+ ogg_int64_t duration=0;
+
+ // libtheora is still a bit unstable and sadly the init_ functions don't
+ // take care of things the way one would expect. So, let's do some explicit
+ // clearing of these fields.
+
+ memset(&t_info, 0, sizeof(theora_info));
+ memset(&t_comment, 0, sizeof(theora_comment));
+ memset(&t_state, 0, sizeof(theora_state));
+
+ bool readTech = false;
+
+ if (what & (KFileMetaInfo::Fastest |
+ KFileMetaInfo::DontCare |
+ KFileMetaInfo::TechnicalInfo))
+ readTech = true;
+
+ if ( info.path().isEmpty() ) // remote file
+ return false;
+
+ fp = fopen(QFile::encodeName(info.path()),"rb");
+ if (!fp)
+ {
+ kdDebug(7034) << "Unable to open " << QFile::encodeName(info.path()) << endl;
+ return false;
+ }
+
+ ogg_sync_init(&o_sync_state);
+
+ /* init supporting Vorbis structures needed in header parsing */
+ vorbis_info_init(&v_info);
+ vorbis_comment_init(&v_comment);
+
+ /* init supporting Theora structures needed in header parsing */
+ theora_comment_init(&t_comment);
+ theora_info_init(&t_info);
+
+ while(!stateflag && buffer_data(fp,&o_sync_state)!=0)
+ {
+ while (ogg_sync_pageout(&o_sync_state,&o_page)>0)
+ {
+ ogg_stream_state stream_test;
+ /* is this a mandated initial header? If not, stop parsing */
+ if(!ogg_page_bos(&o_page))
+ {
+ queue_page(&o_page);
+ stateflag=1;
+ break;
+ }
+
+ ogg_stream_init(&stream_test,ogg_page_serialno(&o_page));
+ ogg_stream_pagein(&stream_test,&o_page);
+ ogg_stream_packetout(&stream_test,&o_packet);
+
+ /* identify the codec: try theora */
+ if(!theora_p && theora_decode_header(&t_info,&t_comment,&o_packet)>=0)
+ {
+ /* it is theora */
+ memcpy(&t_stream_state,&stream_test,sizeof(stream_test));
+ theora_serial=ogg_page_serialno(&o_page);
+ theora_p=1;
+ }
+ else if(!vorbis_p && vorbis_synthesis_headerin(&v_info,&v_comment,&o_packet)>=0)
+ {
+ /* it is vorbis */
+ memcpy(&v_stream_state,&stream_test,sizeof(stream_test));
+ vorbis_p=1;
+ }
+ else
+ {
+ /* whatever it is, we don't care about it */
+ ogg_stream_clear(&stream_test);
+ }
+ }
+ }
+
+ /* we're expecting more header packets. */
+ bool corruptedHeaders=false;
+
+ while((theora_p && theora_p<3) || (vorbis_p && vorbis_p<3))
+ {
+ int ret;
+ /* look for further theora headers */
+ while(theora_p && (theora_p<3) && (ret=ogg_stream_packetout(&t_stream_state,&o_packet)))
+ {
+ if(ret<0)
+ {
+ kdDebug(7034)<<"Error parsing Theora stream headers; corrupt stream?\n"<<endl;
+ corruptedHeaders=true;
+ }
+ if(theora_decode_header(&t_info,&t_comment,&o_packet))
+ {
+ kdDebug(7034)<<"Error parsing Theora stream headers; corrupt stream?"<<endl;
+ corruptedHeaders=true;
+ }
+ theora_p++;
+ if(theora_p==3)
+ break;
+ }
+
+ /* look for more vorbis header packets */
+ while(vorbis_p && (vorbis_p<3) && (ret=ogg_stream_packetout(&v_stream_state,&o_packet)))
+ {
+ if(ret<0)
+ {
+ kdDebug(7034)<<"Error parsing Vorbis stream headers; corrupt stream"<<endl;
+ corruptedHeaders=true;
+ }
+ if(vorbis_synthesis_headerin(&v_info,&v_comment,&o_packet))
+ {
+ kdDebug(7034)<<"Error parsing Vorbis stream headers; corrupt stream?"<<endl;
+ corruptedHeaders=true;
+ }
+ vorbis_p++;
+ if(vorbis_p==3)
+ break;
+ }
+ /* The header pages/packets will arrive before anything else we
+ care about, or the stream is not obeying spec */
+
+ if(ogg_sync_pageout(&o_sync_state,&o_page)>0)
+ {
+ queue_page(&o_page);
+ /* demux into the appropriate stream */
+ }
+ else
+ {
+ int ret=buffer_data(fp,&o_sync_state); /* someone needs more data */
+ if(ret==0)
+ {
+ kdDebug(7034)<<"End of file while searching for codec headers."<<endl;
+ corruptedHeaders=true;
+ }
+ }
+ }
+
+ /* and now we have it all. initialize decoders */
+ if(theora_p && !corruptedHeaders)
+ {
+ theora_decode_init(&t_state,&t_info);
+ }
+ else
+ {
+ /* tear down the partial theora setup */
+ theora_info_clear(&t_info);
+ theora_comment_clear(&t_comment);
+
+ vorbis_info_clear(&v_info);
+ vorbis_comment_clear(&v_comment);
+ ogg_sync_clear(&o_sync_state);
+ fclose(fp);
+ return false;
+ }
+ //queue_page(&o_page);
+
+ while (buffer_data(fp,&o_sync_state))
+ {
+ while (ogg_sync_pageout(&o_sync_state,&o_page)>0)
+ {
+ // The following line was commented out by Scott Wheeler <wheeler@kde.org>
+ // We don't actually need to store all of the pages / packets in memory since
+ // (a) libtheora doesn't use them anyway in the one call that we make after this
+ // that usese t_state and (b) it basically buffers the entire file to memory if
+ // we queue them up like this and that sucks where a typical file size is a few
+ // hundred megs.
+
+ // queue_page(&o_page);
+ }
+ if (theora_serial==ogg_page_serialno(&o_page))
+ duration=(ogg_int64_t) theora_granule_time(&t_state,ogg_page_granulepos(&o_page));
+ }
+
+ if (readTech)
+ {
+ int stream_fps=0;
+ if (t_info.fps_denominator!=0)
+ stream_fps=t_info.fps_numerator/t_info.fps_denominator;
+ KFileMetaInfoGroup videogroup = appendGroup(info, "Video");
+ appendItem(videogroup, "Length", int (duration));
+ appendItem(videogroup, "Resolution", QSize(t_info.frame_width,t_info.frame_height));
+ appendItem(videogroup, "FrameRate", stream_fps);
+ appendItem(videogroup, "Quality", (int) t_info.quality);
+
+ KFileMetaInfoGroup audiogroup = appendGroup(info, "Audio");
+ appendItem(audiogroup, "Channels", v_info.channels);
+ appendItem(audiogroup, "SampleRate", int(v_info.rate));
+ }
+ fclose(fp);
+
+ if (vorbis_p)
+ {
+ ogg_stream_clear(&v_stream_state);
+ vorbis_comment_clear(&v_comment);
+ vorbis_info_clear(&v_info);
+ }
+
+ ogg_stream_clear(&t_stream_state);
+ theora_clear(&t_state);
+ theora_comment_clear(&t_comment);
+ theora_info_clear(&t_info);
+ ogg_sync_clear(&o_sync_state);
+
+ return true;
+}
+
+#include "kfile_theora.moc"
+
diff --git a/kfile-plugins/theora/kfile_theora.desktop b/kfile-plugins/theora/kfile_theora.desktop
new file mode 100644
index 00000000..6e519085
--- /dev/null
+++ b/kfile-plugins/theora/kfile_theora.desktop
@@ -0,0 +1,60 @@
+[Desktop Entry]
+Type=Service
+Name=theora Info
+Name[bg]=Информация за theora
+Name[bn]=থিওরা তথ্য
+Name[br]=Titouroù diwar-benn theora
+Name[bs]=theora informacije
+Name[ca]=Informació theora
+Name[cs]=theora info
+Name[de]=Theora-Info
+Name[el]=Πληροφορίες theora
+Name[eo]=theora-informo
+Name[es]=Info Theora
+Name[et]=Theora info
+Name[eu]=theora informazioa
+Name[fa]=اطلاعات theora
+Name[fi]=Theoran tiedot
+Name[fr]=Informations theora
+Name[ga]=Eolas faoi theora
+Name[gl]=Información theora
+Name[he]=מידע theora
+Name[hu]=Theora-jellemzők
+Name[is]=theora upplýsingar
+Name[it]=Informazioni su theora
+Name[ja]=theora 情報
+Name[kk]=theora мәліметі
+Name[km]=ព័ត៌មាន theora
+Name[ko]=theora 정보
+Name[lt]=theora Informacija
+Name[mk]=theora информации
+Name[nb]=theora-info
+Name[nds]=Theora-Info
+Name[ne]=थिवरा सूचना
+Name[nl]=theora-informatie
+Name[nn]=theora-info
+Name[pa]=theora (ਥੋਰਾ) ਜਾਣਕਾਰੀ
+Name[pl]=Informacja o pliku theora
+Name[pt]=Informação do theora
+Name[pt_BR]=Informação sobre theora
+Name[ru]=Сведения о theora
+Name[sk]=theora info
+Name[sl]=Podatki o Theora
+Name[sr]=Информације о theora-и
+Name[sr@Latn]=Informacije o theora-i
+Name[sv]=Theora-information
+Name[ta]= தியோரா தகவல்
+Name[th]=ข้อมูล theora
+Name[tr]=theora Bilgisi
+Name[uk]=Інформація по theora
+Name[zh_CN]=theora 信息
+Name[zh_HK]=theora 資訊
+Name[zh_TW]=theora 資訊
+ServiceTypes=KFilePlugin
+X-KDE-Library=kfile_theora
+# change MimeType here! (example: inode/directory)
+MimeType=video/x-theora
+# change PreferredGroups here! (example: FolderInfo)
+PreferredGroups=
+# change PreferredItems here! (example: Items;Size)
+PreferredItems=Size,Length
diff --git a/kfile-plugins/theora/kfile_theora.h b/kfile-plugins/theora/kfile_theora.h
new file mode 100644
index 00000000..c2cb990e
--- /dev/null
+++ b/kfile-plugins/theora/kfile_theora.h
@@ -0,0 +1,42 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Jean-Baptiste Mardelle *
+ * bj@altern.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef __KFILE_THEORA_H__
+#define __KFILE_THEORA_H__
+
+/**
+ * Note: For further information look into <$KDEDIR/include/kfilemetainfo.h>
+ */
+#include <kfilemetainfo.h>
+
+class QStringList;
+
+class theoraPlugin: public KFilePlugin
+{
+ Q_OBJECT
+
+public:
+ theoraPlugin( QObject *parent, const char *name, const QStringList& args );
+
+ virtual bool readInfo( KFileMetaInfo& info, uint what);
+};
+
+#endif // __KFILE_THEORA_H__
+
diff --git a/kfile-plugins/wav/Makefile.am b/kfile-plugins/wav/Makefile.am
new file mode 100644
index 00000000..0a533384
--- /dev/null
+++ b/kfile-plugins/wav/Makefile.am
@@ -0,0 +1,22 @@
+## Makefile.am for wav file meta info plugin
+
+# set the include path for X, qt and KDE
+INCLUDES = $(all_includes)
+
+# these are the headers for your project
+noinst_HEADERS = kfile_wav.h
+
+kde_module_LTLIBRARIES = kfile_wav.la
+
+kfile_wav_la_SOURCES = kfile_wav.cpp
+kfile_wav_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+kfile_wav_la_LIBADD = $(LIB_KIO)
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
+
+messages: rc.cpp
+ $(XGETTEXT) kfile_wav.cpp -o $(podir)/kfile_wav.pot
+
+services_DATA = kfile_wav.desktop
+servicesdir = $(kde_servicesdir)
diff --git a/kfile-plugins/wav/kfile_wav.cpp b/kfile-plugins/wav/kfile_wav.cpp
new file mode 100644
index 00000000..4eda7bce
--- /dev/null
+++ b/kfile-plugins/wav/kfile_wav.cpp
@@ -0,0 +1,173 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2002 Ryan Cumming <bodnar42@phalynx.dhs.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation version 2.
+ *
+ * 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+#include "kfile_wav.h"
+
+#include <kprocess.h>
+#include <klocale.h>
+#include <kgenericfactory.h>
+#include <kstringvalidator.h>
+#include <kdebug.h>
+
+#include <qdict.h>
+#include <qvalidator.h>
+#include <qcstring.h>
+#include <qfile.h>
+#include <qdatetime.h>
+
+#if !defined(__osf__)
+#include <inttypes.h>
+#else
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+#endif
+
+typedef KGenericFactory<KWavPlugin> WavFactory;
+
+K_EXPORT_COMPONENT_FACTORY(kfile_wav, WavFactory( "kfile_wav" ))
+
+KWavPlugin::KWavPlugin(QObject *parent, const char *name,
+ const QStringList &args)
+
+ : KFilePlugin(parent, name, args)
+{
+ KFileMimeTypeInfo* info = addMimeTypeInfo( "audio/x-wav" );
+
+ KFileMimeTypeInfo::GroupInfo* group = 0L;
+
+ // "the" group
+ group = addGroupInfo(info, "Technical", i18n("Technical Details"));
+
+ KFileMimeTypeInfo::ItemInfo* item;
+
+ item = addItemInfo(group, "Sample Size", i18n("Sample Size"), QVariant::Int);
+ setSuffix(item, i18n(" bits"));
+
+ item = addItemInfo(group, "Sample Rate", i18n("Sample Rate"), QVariant::Int);
+ setSuffix(item, i18n(" Hz"));
+
+ item = addItemInfo(group, "Channels", i18n("Channels"), QVariant::Int);
+
+ item = addItemInfo(group, "Length", i18n("Length"), QVariant::Int);
+ setAttributes(item, KFileMimeTypeInfo::Cummulative);
+ setUnit(item, KFileMimeTypeInfo::Seconds);
+}
+
+bool KWavPlugin::readInfo( KFileMetaInfo& info, uint /*what*/)
+{
+ if ( info.path().isEmpty() ) // remote file
+ return false;
+
+ QFile file(info.path());
+
+ uint32_t format_size;
+ uint16_t format_tag;
+ uint16_t channel_count;
+ uint32_t sample_rate;
+ uint32_t bytes_per_second;
+ uint16_t bytes_per_sample;
+ uint16_t sample_size;
+ uint32_t data_size;
+ uint32_t unknown_chunk_size;
+ uint16_t unknown_chunk16;
+ bool have_fmt = false;
+ bool have_data = false;
+ QIODevice::Offset file_length;
+
+ const char *riff_signature = "RIFF";
+ const char *wav_signature = "WAVE";
+ const char *fmt_signature = "fmt ";
+ const char *data_signature = "data";
+ char signature_buffer[4];
+
+ if (!file.open(IO_ReadOnly))
+ {
+ kdDebug(7034) << "Couldn't open " << QFile::encodeName(info.path()) << endl;
+ return false;
+ }
+
+ file_length = file.size() - 100; // a bit of insurance
+ QDataStream dstream(&file);
+
+ // WAV files are little-endian
+ dstream.setByteOrder(QDataStream::LittleEndian);
+
+ // Read and verify the RIFF signature
+ dstream.readRawBytes(signature_buffer, 4);
+ if (memcmp(signature_buffer, riff_signature, 4))
+ return false;
+
+ // Skip the next bit (total file size, pretty useless)
+ file.at(8);
+
+ // Read and verify the WAVE signature
+ dstream.readRawBytes(signature_buffer, 4);
+ if (memcmp(signature_buffer, wav_signature, 4))
+ return false;
+
+ // pretty dumb scanner, but better than what we had!
+ do
+ {
+ dstream.readRawBytes(signature_buffer, 4);
+ if (!memcmp(signature_buffer, fmt_signature, 4)) {
+ dstream >> format_size;
+ dstream >> format_tag;
+ dstream >> channel_count;
+ dstream >> sample_rate;
+ dstream >> bytes_per_second;
+ dstream >> bytes_per_sample;
+ dstream >> sample_size;
+ have_fmt = true;
+ if ( format_size > 16 ) {
+ for (unsigned int i = 0; i < (format_size-16+1)/2; i++)
+ dstream >> unknown_chunk16;
+ }
+ } else if (!memcmp(signature_buffer, data_signature, 4)) {
+ dstream >> data_size;
+ have_data = true;
+ } else {
+ dstream >> unknown_chunk_size;
+ for (unsigned int i = 0; i < (unknown_chunk_size+1)/2; i++)
+ dstream >> unknown_chunk16;
+ }
+ if (have_data && have_fmt)
+ break;
+ } while (file.at() < file_length);
+
+ if ( (!have_data) || (!have_fmt) )
+ return false;
+
+ // These values are downright illegal
+ if ((!channel_count) || (!bytes_per_second))
+ return false;
+
+ KFileMetaInfoGroup group = appendGroup(info, "Technical");
+
+
+ appendItem(group, "Sample Size", int(sample_size));
+ appendItem(group, "Sample Rate", int(sample_rate));
+ appendItem(group, "Channels", int(channel_count));
+ unsigned int wav_seconds = data_size / bytes_per_second;
+ appendItem(group, "Length", int(wav_seconds));
+
+ return true;
+}
+
+#include "kfile_wav.moc"
diff --git a/kfile-plugins/wav/kfile_wav.desktop b/kfile-plugins/wav/kfile_wav.desktop
new file mode 100644
index 00000000..c25e8b8f
--- /dev/null
+++ b/kfile-plugins/wav/kfile_wav.desktop
@@ -0,0 +1,67 @@
+[Desktop Entry]
+Type=Service
+Name=WAV Info
+Name[af]=Wav Inligting
+Name[ar]=معلومات WAV
+Name[bg]=Информация за WAV
+Name[br]=Titouroù WAV
+Name[bs]=WAV informacije
+Name[ca]=Informació WAV
+Name[cs]=WAV info
+Name[cy]=Gwybodaeth WAV
+Name[da]=WAV-info
+Name[de]=WAV-Info
+Name[el]=Πληροφορίες WAV
+Name[eo]=WAV-informo
+Name[es]=Info WAV
+Name[et]=WAV info
+Name[eu]=WAV informazioa
+Name[fa]=اطلاعات WAV
+Name[fi]=WAV-tiedot
+Name[fr]=Informations Wave
+Name[gl]=Información WAV
+Name[he]=מידע WAV
+Name[hi]=WAV जानकारी
+Name[hr]=Informacije o WAV datoteci
+Name[hu]=WAV-jellemzők
+Name[is]=WAV upplýsingar
+Name[it]=Informazioni WAV
+Name[ja]=WAV 情報
+Name[kk]=WAV мәліметі
+Name[km]=ព័ត៌មាន WAV
+Name[ko]=WAV 정보
+Name[lt]=WAV informacija
+Name[mk]=WAV информации
+Name[nb]=WAV informasjon
+Name[nds]=WAV-Info
+Name[ne]=वाभ सूचना
+Name[nl]=WAV-informatie
+Name[nn]=WAV-info
+Name[pa]=WAV ਜਾਣਕਾਰੀ
+Name[pl]=Informacja o pliku WAV
+Name[pt]=Informação do WAV
+Name[pt_BR]=Informação sobre WAV
+Name[ro]=Informaţii WAV
+Name[ru]=Сведения о WAV
+Name[se]=WAV-dieđut
+Name[sl]=Podatki o WAV
+Name[sr]=Информације о WAV-у
+Name[sr@Latn]=Informacije o WAV-u
+Name[sv]=Wav-information
+Name[ta]=WAV தகவல்
+Name[tg]=WAV Ахборот
+Name[th]=ข้อมูล WAV
+Name[tr]=WAV Bilgisi
+Name[uk]=Інформація по WAV
+Name[uz]=WAV haqida maʼlumot
+Name[uz@cyrillic]=WAV ҳақида маълумот
+Name[xh]=MAV Ulwazi
+Name[zh_CN]=WAV 信息
+Name[zh_HK]=WAV 資訊
+Name[zh_TW]=WAV 資訊
+Name[zu]=Ulwazi lwe WAV
+ServiceTypes=KFilePlugin
+X-KDE-Library=kfile_wav
+MimeType=audio/x-wav
+PreferredGrous=Technical
+PreferredItems=Length,Sample Rate,Sample Size,Channels
diff --git a/kfile-plugins/wav/kfile_wav.h b/kfile-plugins/wav/kfile_wav.h
new file mode 100644
index 00000000..318ee2f5
--- /dev/null
+++ b/kfile-plugins/wav/kfile_wav.h
@@ -0,0 +1,37 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2002 Ryan Cumming <bodnar42@phalynx.dhs.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation version 2.
+ *
+ * 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __KFILE_WAV_H__
+#define __KFILE_WAV_H__
+
+#include <kfilemetainfo.h>
+
+class QStringList;
+
+class KWavPlugin: public KFilePlugin
+{
+ Q_OBJECT
+
+public:
+ KWavPlugin( QObject *parent, const char *name, const QStringList& args );
+
+ virtual bool readInfo( KFileMetaInfo& info, uint what);
+};
+
+#endif
diff --git a/kioslave/Makefile.am b/kioslave/Makefile.am
new file mode 100644
index 00000000..4586dcda
--- /dev/null
+++ b/kioslave/Makefile.am
@@ -0,0 +1,5 @@
+if include_kioslave_audiocd
+AUDIOCD_SUBDIR=audiocd
+endif
+
+SUBDIRS = $(AUDIOCD_SUBDIR)
diff --git a/kioslave/audiocd/HACKING b/kioslave/audiocd/HACKING
new file mode 100644
index 00000000..dde6efc1
--- /dev/null
+++ b/kioslave/audiocd/HACKING
@@ -0,0 +1,25 @@
+// CODE LAYOUT
+
+audiocd.[h,cpp] - The main ioslave code. It contains the logic to rip audio from cd's. It loads all of the plugins that it can find. When it needs to encode, generate directories (and names of directories) it goes through it's list. audiocd uses libkcddb to retrieve its cddb entries and cdparinoia to retrieve the audio from the CD.
+
+plugins/audiocdencoder.[h,cpp] - The base class for all of the encoder plugins
+
+plugins/wav/* - The cda and wav "encoders"
+plugins/flac/* - the flac encoder
+plugins/lame/* - The mp3 encoder
+plugins/vorbis/* - The ogg encoder
+
+kcmaudiocd/ - kcm configure dialog for the audiocd ioslave. It also loads the plugins and gets their configure dialogs and puts them into the tab dialog.
+
+// USERS
+
+Audiocd's "interface" is presented first to the users, It should be simple. The number of files/directories should be kept to a minimum.
+
+// APP SKIMMING
+
+Audiocd also has the ability to be "skimmed" by other tools. For example an outside application could retrieve the second track with:
+
+audiocd:/Wav/Track 02.wav?device=/dev/hd2&fileNameTemplate=Track %{number}&cddbChoice=1
+
+This way apps can just query audiocd:/ for CDDB, present them to the users, and rip the tracks all without having to impliment it themselves.
+
diff --git a/kioslave/audiocd/Makefile.am b/kioslave/audiocd/Makefile.am
new file mode 100644
index 00000000..736828e1
--- /dev/null
+++ b/kioslave/audiocd/Makefile.am
@@ -0,0 +1,33 @@
+if include_kcm_audiocd
+AUDIO_CD_SUBDIRS = kcmaudiocd
+endif
+SUBDIRS = plugins $(AUDIO_CD_SUBDIRS)
+
+INCLUDES = -I$(top_srcdir)/libkcddb \
+ -I$(top_builddir)/libkcddb $(all_includes) \
+ -I$(top_srcdir)/kscd $(all_includes) \
+ -I$(srcdir)/plugins $(all_includes)
+
+KDE_CXXFLAGS=$(ENABLE_PERMISSIVE_FLAG)
+
+kde_module_LTLIBRARIES = kio_audiocd.la
+
+kio_audiocd_la_SOURCES = audiocd.cpp
+
+kio_audiocd_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+
+kio_audiocd_la_LIBADD = ./plugins/libaudiocdplugins.la $(LIB_KIO) $(CDPARANOIA_LIBS) $(top_builddir)/libkcddb/libkcddb.la $(top_builddir)/kscd/libkcompactdisc.la
+
+noinst_HEADERS = audiocd.h
+
+protocoldir = $(kde_servicesdir)
+protocol_DATA = audiocd.protocol
+
+messages:
+ $(XGETTEXT) *.cpp -o $(podir)/kio_audiocd.pot
+
+updatedir = $(kde_datadir)/kconf_update
+update_DATA = audiocd.upd
+update_SCRIPTS = upgrade-metadata.sh
+
+audiocd.lo: ../../libkcddb/configbase.h
diff --git a/kioslave/audiocd/audiocd.cpp b/kioslave/audiocd/audiocd.cpp
new file mode 100644
index 00000000..bd9bb14b
--- /dev/null
+++ b/kioslave/audiocd/audiocd.cpp
@@ -0,0 +1,1159 @@
+/*
+ * Copyright (C) 2000 Rik Hemsley (rikkus) <rik@kde.org>
+ * Copyright (C) 2000, 2001, 2002 Michael Matz <matz@kde.org>
+ * Copyright (C) 2001 Carsten Duvenhorst <duvenhorst@m2.uni-hannover.de>
+ * Copyright (C) 2001 Adrian Schroeter <adrian@suse.de>
+ * Copyright (C) 2003 Richard Lärkäng <richard@goteborg.utfors.se>
+ * Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
+ * Copyright (C) 2004, 2005 Benjamin Meyer <ben at meyerhome dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * ERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+extern "C"
+{
+ #include <cdda_interface.h>
+ #include <cdda_paranoia.h>
+ void paranoiaCallback(long, int);
+
+ #include <kdemacros.h>
+ KDE_EXPORT int kdemain(int argc, char ** argv);
+}
+
+#include "audiocd.h"
+#include "plugins/audiocdencoder.h"
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <kmacroexpander.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <kcmdlineargs.h>
+#include <kdebug.h>
+#include <kapplication.h>
+#include <klocale.h>
+#include <qregexp.h>
+
+// CDDB
+#include <client.h>
+#include "kcompactdisc.h"
+
+using namespace KIO;
+using namespace KCDDB;
+
+#define QFL1(x) QString::fromLatin1(x)
+#define DEFAULT_CD_DEVICE "/dev/cdrom"
+#define CDDB_INFORMATION "CDDB Information"
+
+using namespace AudioCD;
+
+static const KCmdLineOptions options[] =
+{
+ { "+protocol", I18N_NOOP("Protocol name"), 0 },
+ { "+pool", I18N_NOOP("Socket name"), 0 },
+ { "+app", I18N_NOOP("Socket name"), 0 },
+ KCmdLineLastOption
+};
+
+int kdemain(int argc, char ** argv)
+{
+ // KApplication uses libkcddb which needs a valid kapp pointer
+ // GUIenabled must be true as libkcddb sometimes wants to communicate
+ // with the user
+ putenv(strdup("SESSION_MANAGER="));
+ KApplication::disableAutoDcopRegistration();
+ KCmdLineArgs::init(argc, argv, "kio_audiocd", 0, 0, 0, 0);
+ KCmdLineArgs::addCmdLineOptions(options);
+ KApplication app(false, true);
+
+ kdDebug(7117) << "Starting " << getpid() << endl;
+
+ KCmdLineArgs* args = KCmdLineArgs::parsedArgs();
+ AudioCDProtocol slave(args->arg(0), args->arg(1), args->arg(2));
+ slave.dispatchLoop();
+
+ kdDebug(7117) << "Done" << endl;
+ return 0;
+}
+
+enum Which_dir {
+ Unknown = 0, // Error
+ Info, // CDDB info
+ Root, // The root directory, shows all these :)
+ FullCD, // Show a single file containing all of the data
+ EncoderDir // A directory created by an encoder
+};
+
+class AudioCDProtocol::Private {
+public:
+ Private() : cd(KCompactDisc::Asynchronous) {
+ clearURLargs();
+ s_info = i18n("Information");
+ s_fullCD = i18n("Full CD");
+ }
+
+ void clearURLargs() {
+ req_allTracks = false;
+ which_dir = Unknown;
+ req_track = -1;
+ cddbUserChoice = -1;
+ }
+
+ // The type/which of request
+ bool req_allTracks;
+ Which_dir which_dir;
+ int req_track;
+ QString fname;
+ AudioCDEncoder *encoder_dir_type;
+
+ // Misc settings
+ QString device; // URL settable
+ int paranoiaLevel; // URL settable
+ bool reportErrors;
+
+ // Directory strings, never change after init
+ QString s_info;
+ QString s_fullCD;
+
+ // Current CD
+ unsigned discid;
+ unsigned tracks;
+ bool trackIsAudio[100];
+ KCompactDisc cd; // keep it around so that we don't assume the disk changed between every stat()
+
+ // CDDB items
+ KCDDB::CDDB::Result cddbResult;
+ CDInfoList cddbList;
+ int cddbUserChoice; // URL settable
+ KCDDB::CDInfo cddbBestChoice;
+
+ // Template for ..
+ QString fileNameTemplate; // URL settable
+ QString albumTemplate; // URL settable
+ QString rsearch;
+ QString rreplace;
+
+ // Current strings for this CD and or cddb selection
+ QStringList templateTitles;
+ QString templateAlbumName;
+};
+
+int paranoia_read_limited_error;
+
+AudioCDProtocol::AudioCDProtocol(const QCString & protocol, const QCString & pool, const QCString & app)
+ : SlaveBase(protocol, pool, app)
+{
+ d = new Private;
+
+ // Add encoders
+ AudioCDEncoder::findAllPlugins(this, encoders);
+ encoderTypeCDA = encoderFromExtension(".cda");
+ encoderTypeWAV = encoderFromExtension(".wav");
+ encoders.setAutoDelete(true);
+}
+
+AudioCDProtocol::~AudioCDProtocol()
+{
+ delete d;
+}
+
+AudioCDEncoder *AudioCDProtocol::encoderFromExtension(const QString& extension)
+{
+ AudioCDEncoder *encoder;
+ for ( encoder = encoders.first(); encoder; encoder = encoders.next() ){
+ if(QString(".")+encoder->fileType() == extension)
+ return encoder;
+ }
+ Q_ASSERT(false);
+ return NULL;
+}
+
+AudioCDEncoder *AudioCDProtocol::determineEncoder(const QString & filename)
+{
+ int len = filename.length();
+ int pos = filename.findRev('.');
+ return encoderFromExtension(filename.right(len - pos));
+}
+
+struct cdrom_drive * AudioCDProtocol::initRequest(const KURL & url)
+{
+ if (url.hasHost())
+ {
+ error(KIO::ERR_UNSUPPORTED_ACTION,
+ i18n("You cannot specify a host with this protocol. "
+ "Please use the audiocd:/ format instead."));
+ return 0;
+ }
+
+ // Load OUR Settings.
+ loadSettings();
+ // Then url parameters can overrule our settings.
+ parseURLArgs(url);
+
+ struct cdrom_drive * drive = getDrive();
+ if (0 == drive)
+ return 0;
+
+ // Update our knowledge of the disc
+
+#if defined(__linux__)
+ if(drive->ioctl_device_name && drive->ioctl_device_name[0])
+ d->cd.setDevice(drive->ioctl_device_name, 50, false);
+ else
+ d->cd.setDevice(drive->cdda_device_name, 50, false);
+#else
+ d->cd.setDevice(drive->cdda_device_name, 50, false);
+#endif
+
+#if 0
+ // FreeBSD's cdparanoia as of january 5th 2006 has rather broken
+ // support for non-SCSI devices. Although it finds ATA cdroms just
+ // fine, there is no straightforward way to discover the device
+ // name associated with the device, which throws the rest of audiocd
+ // for a loop.
+ //
+ if ( !(drive->dev) || (COOKED_IOCTL == drive->interface) )
+ {
+ // For ATAPI devices, we have no real choice. Use the
+ // user selected value, even if there is none.
+ //
+ kdWarning(7117) << "Found an ATAPI device, assuming it is the one specified by the user." << endl;
+ d->cd.setDevice( d->device );
+ }
+ else
+ {
+ kdDebug(7117) << "Found a SCSI or ATAPICAM device." << endl;
+ if ( strlen(drive->dev->device_path) > 0 )
+ {
+ d->cd.setDevice( drive->dev->device_path );
+ }
+ else
+ {
+ // But the device_path can be empty under some
+ // circumstances, so build a representation from
+ // the unit number and SCSI device name.
+ //
+ QString devname = QString::fromLatin1( "/dev/%1%2" )
+ .arg( drive->dev->given_dev_name )
+ .arg( drive->dev->given_unit_number ) ;
+ kdDebug(7117) << " Using derived name " << devname << endl;
+ d->cd.setDevice( devname );
+ }
+ }
+#endif
+
+ if (d->cd.discId() != d->discid && d->cd.discId() != d->cd.missingDisc){
+ d->discid = d->cd.discId();
+ d->tracks = d->cd.tracks();
+ for(uint i=0; i< d->cd.tracks(); i++)
+ d->trackIsAudio[i] = d->cd.isAudio(i+1);
+
+ KCDDB::Client c;
+ d->cddbResult = c.lookup(d->cd.discSignature());
+ d->cddbList = c.lookupResponse();
+ d->cddbBestChoice = c.bestLookupResponse();
+ generateTemplateTitles();
+ }
+
+ // Determine what file or folder that is wanted.
+ d->fname = url.fileName(false);
+ QString dname = url.directory(true, false);
+ if (!dname.isEmpty() && dname[0] == '/')
+ dname = dname.mid(1);
+
+ // Kong issue where they send dirs as files, double check
+ /* A hack, for when konqi wants to list the directory audiocd:/Bla
+ it really submits this URL, instead of audiocd:/Bla/ to us. We could
+ send (in listDir) the UDS_NAME as "Bla/" for directories, but then
+ konqi shows them as "Bla//" in the status line. */
+ // See if it is an encoder directory
+ AudioCDEncoder *encoder;
+ for ( encoder = encoders.first(); encoder; encoder = encoders.next() ){
+ if(encoder->type() == d->fname){
+ dname = d->fname;
+ d->fname = "";
+ break;
+ }
+ }
+ // Other Hard coded directories
+ if (dname.isEmpty() && (d->fname == d->s_info || d->fname == d->s_fullCD ))
+ {
+ dname = d->fname;
+ d->fname = "";
+ }
+ // end hack
+
+
+ // See which directory they want
+ d->which_dir = Unknown;
+ for ( encoder = encoders.first(); encoder; encoder = encoders.next() ){
+ if(encoder->type() == dname){
+ d->which_dir = EncoderDir;
+ d->encoder_dir_type = encoder;
+ break;
+ }
+ }
+ if ( Unknown == d->which_dir ){
+ if (dname.isEmpty())
+ d->which_dir = Root;
+ else if (dname == d->s_info)
+ d->which_dir = Info;
+ else if (dname == d->s_fullCD)
+ d->which_dir = FullCD;
+ }
+
+ // See if the url is a track
+ d->req_track = -1;
+ if (!d->fname.isEmpty()){
+ QString name(d->fname);
+
+ // Remove extension
+ int dot = name.findRev('.');
+ if (dot >= 0)
+ name.truncate(dot);
+
+ // See if it matches a cddb title
+ uint trackNumber;
+ for (trackNumber = 0; trackNumber < d->tracks; trackNumber++){
+ if (d->templateTitles[trackNumber] == name)
+ break;
+ }
+ if (trackNumber < d->tracks)
+ d->req_track = trackNumber;
+ else {
+ /* Not found in title list. Try hard to find a number in the
+ string. */
+ unsigned int start = 0;
+ unsigned int end = 0;
+ // Find where the numbers start
+ while (start < name.length()){
+ if (name[start++].isDigit())
+ break;
+ }
+ // Find where the numbers end
+ for (end = start; end < name.length(); end++)
+ if (!name[end].isDigit())
+ break;
+ if (start < name.length()){
+ bool ok;
+ // The external representation counts from 1 so subtrac 1.
+ d->req_track = name.mid(start-1, end - start+2).toInt(&ok) - 1;
+ if (!ok)
+ d->req_track = -1;
+ }
+ }
+ }
+ if (d->req_track >= (int)d->tracks)
+ d->req_track = -1;
+
+ // Are we in the directory that lists "full CD" files?
+ d->req_allTracks = (dname.contains(d->s_fullCD));
+
+ kdDebug(7117) << "dir=" << dname << " file=" << d->fname
+ << " req_track=" << d->req_track << " which_dir=" << d->which_dir << " full CD?=" << d->req_allTracks << endl;
+ return drive;
+}
+
+bool AudioCDProtocol::getSectorsForRequest(struct cdrom_drive * drive, long & firstSector, long & lastSector) const
+{
+ if (d->req_allTracks)
+ { // we rip all the tracks of the CD
+ firstSector = cdda_track_firstsector(drive, 1);
+ lastSector = cdda_track_lastsector(drive, cdda_tracks(drive));
+ }
+ else
+ { // we only rip the selected track
+ int trackNumber = d->req_track + 1;
+
+ if (trackNumber <= 0 || trackNumber > cdda_tracks(drive))
+ return false;
+ firstSector = cdda_track_firstsector(drive, trackNumber);
+ lastSector = cdda_track_lastsector(drive, trackNumber);
+ }
+ return true;
+}
+
+void AudioCDProtocol::get(const KURL & url)
+{
+ struct cdrom_drive * drive = initRequest(url);
+ if (!drive)
+ return;
+
+ if( d->fname.contains(i18n(CDDB_INFORMATION))){
+ uint choice = 1;
+ if(d->fname != QString("%1.txt").arg(i18n(CDDB_INFORMATION))){
+ choice= d->fname.section('_',1,1).section('.',0,0).toInt();
+ }
+ uint count = 1;
+ CDInfoList::iterator it;
+ bool found = false;
+ for ( it = d->cddbList.begin(); it != d->cddbList.end(); ++it ){
+ if(count == choice){
+ mimeType("text/html");
+ data(QCString( (*it).toString().latin1() ));
+ // send an empty QByteArray to signal end of data.
+ data(QByteArray());
+ finished();
+ found = true;
+ break;
+ }
+ count++;
+ }
+ if(!found && d->fname.contains(i18n(CDDB_INFORMATION)+":")){
+ mimeType("text/html");
+ //data(QCString( d->fname.latin1() ));
+ // send an empty QByteArray to signal end of data.
+ data(QByteArray());
+ finished();
+ found = true;
+ }
+ if( !found )
+ error(KIO::ERR_DOES_NOT_EXIST, url.path());
+ cdda_close(drive);
+ return;
+ }
+
+ long firstSector, lastSector;
+ if (!getSectorsForRequest(drive, firstSector, lastSector))
+ {
+ error(KIO::ERR_DOES_NOT_EXIST, url.path());
+ cdda_close(drive);
+ return;
+ }
+
+ AudioCDEncoder *encoder = determineEncoder(d->fname);
+ if(!encoder){
+ cdda_close(drive);
+ return;
+ }
+
+ KCDDB::CDInfo info;
+ if(d->cddbResult == KCDDB::CDDB::Success){
+ info = d->cddbBestChoice;
+
+ int track = d->req_track;
+
+ // hack
+ // do we rip the whole CD?
+ if (d->req_allTracks){
+ track = 0;
+ // YES => the title of the file is the title of the CD
+ info.trackInfoList[track].title = info.title.utf8().data();
+ }
+ encoder->fillSongInfo(info, track, "");
+ }
+ long totalByteCount = CD_FRAMESIZE_RAW * (lastSector - firstSector + 1);
+ long time_secs = (8 * totalByteCount) / (44100 * 2 * 16);
+
+ unsigned long size = encoder->size(time_secs);
+ totalSize(size);
+ emit mimeType(QFL1(encoder->mimeType()));
+
+ // Read data (track/disk) from the cd
+ paranoiaRead(drive, firstSector, lastSector, encoder, url.fileName(), size);
+
+ // send an empty QByteArray to signal end of data.
+ data(QByteArray());
+
+ cdda_close(drive);
+
+ finished();
+}
+
+void AudioCDProtocol::stat(const KURL & url)
+{
+ struct cdrom_drive * drive = initRequest(url);
+ if (!drive)
+ return;
+
+ bool isFile = !d->fname.isEmpty();
+
+ // the track number. 0 if ripping
+ // the whole CD.
+ uint trackNumber = d->req_track + 1;
+
+ if (!d->req_allTracks)
+ { // we only want to rip one track.
+ // does this track exist?
+ if (isFile && (trackNumber < 1 || trackNumber > d->tracks))
+ {
+ error(KIO::ERR_DOES_NOT_EXIST, url.path());
+ return;
+ }
+ }
+
+ UDSEntry entry;
+
+ UDSAtom atom;
+ atom.m_uds = KIO::UDS_NAME;
+ atom.m_str = url.fileName().replace('/', QFL1("%2F"));
+ kdDebug(7117) << k_funcinfo << atom.m_str << endl;
+ entry.append(atom);
+
+ atom.m_uds = KIO::UDS_FILE_TYPE;
+ atom.m_long = isFile ? S_IFREG : S_IFDIR;
+ entry.append(atom);
+
+ const mode_t _umask = ::umask(0);
+ ::umask(_umask);
+
+ atom.m_uds = KIO::UDS_ACCESS;
+ atom.m_long = 0666 & (~_umask);
+ entry.append(atom);
+
+ atom.m_uds = KIO::UDS_SIZE;
+ if (!isFile)
+ {
+ atom.m_long = cdda_tracks(drive);
+ }
+ else
+ {
+ AudioCDEncoder *encoder = determineEncoder(d->fname);
+ long firstSector, lastSector;
+ getSectorsForRequest(drive, firstSector, lastSector);
+ atom.m_long = fileSize(firstSector, lastSector, encoder);
+ }
+
+ entry.append(atom);
+
+ statEntry(entry);
+
+ cdda_close(drive);
+
+ finished();
+}
+
+static void app_entry(UDSEntry& e, unsigned int uds, const QString& str)
+{
+ UDSAtom a;
+ a.m_uds = uds;
+ a.m_str = str;
+ e.append(a);
+}
+
+static void app_entry(UDSEntry& e, unsigned int uds, long l)
+{
+ UDSAtom a;
+ a.m_uds = uds;
+ a.m_long = l;
+ e.append(a);
+}
+
+static void app_dir(UDSEntry& e, const QString & n, size_t s)
+{
+ e.clear();
+ app_entry(e, KIO::UDS_NAME, QFile::decodeName(n.local8Bit()));
+ app_entry(e, KIO::UDS_FILE_TYPE, S_IFDIR);
+ app_entry(e, KIO::UDS_ACCESS, 0400);
+ app_entry(e, KIO::UDS_SIZE, s);
+ app_entry(e, KIO::UDS_MIME_TYPE, "inode/directory");
+}
+
+static void app_file(UDSEntry& e, const QString & n, size_t s)
+{
+ e.clear();
+ app_entry(e, KIO::UDS_NAME, QFile::decodeName(n.local8Bit()));
+ app_entry(e, KIO::UDS_FILE_TYPE, S_IFREG);
+ app_entry(e, KIO::UDS_ACCESS, 0400);
+ app_entry(e, KIO::UDS_SIZE, s);
+}
+
+void AudioCDProtocol::listDir(const KURL & url)
+{
+ struct cdrom_drive * drive = initRequest(url);
+
+ // Some error checking before proceeding
+ if (!drive)
+ return;
+
+ if (d->which_dir == Unknown){
+ error(KIO::ERR_DOES_NOT_EXIST, url.path());
+ cdda_close(drive);
+ return;
+ }
+
+ if ( !d->fname.isEmpty() ){
+ error(KIO::ERR_IS_FILE, url.path());
+ cdda_close(drive);
+ return;
+ }
+
+ // Generate templated names every time
+ // because the template might have changed.
+ generateTemplateTitles();
+
+ UDSEntry entry;
+ // If the tracks should be listed in this directory
+ bool list_tracks = true;
+
+ if (d->which_dir == Info){
+ CDInfoList::iterator it;
+ uint count = 1;
+ for ( it = d->cddbList.begin(); it != d->cddbList.end(); ++it ){
+ (*it).toString();
+ if(count == 1)
+ app_file(entry, QString("%1.txt").arg(i18n(CDDB_INFORMATION)), ((*it).toString().length())+1);
+ else
+ app_file(entry, QString("%1_%2.txt").arg(i18n(CDDB_INFORMATION)).arg(count), ((*it).toString().length())+1);
+ count++;
+ listEntry(entry, false);
+ }
+ // Error
+ if( count == 1 ) {
+ app_file(entry, QString("%1: %2.txt").arg(i18n(CDDB_INFORMATION)).arg(CDDB::resultToString(d->cddbResult)), ((*it).toString().length())+1);
+ count++;
+ listEntry(entry, false);
+ }
+
+ list_tracks = false;
+ }
+
+ if (d->which_dir == Root){
+ // List virtual directories.
+ app_dir(entry, d->s_fullCD, encoders.count());
+ listEntry(entry, false);
+
+ // Either >0 cddb results or cddb error file
+ app_dir(entry, d->s_info, d->cddbList.count());
+ listEntry(entry, false);
+
+ // List the encoders
+ AudioCDEncoder *encoder;
+ for ( encoder = encoders.first(); encoder; encoder = encoders.next() ){
+ // Skip the directory that is in the root (you can still go in it, just don't show it)
+ if( encoder == encoderTypeWAV )
+ continue;
+ app_dir(entry, encoder->type(), d->tracks);
+ listEntry(entry, false);
+ }
+ }
+
+ // Now fill in the tracks for the current directory
+ if (list_tracks && d->which_dir == FullCD) {
+ // if we're listing the "full CD" subdirectory :
+ if ( (d->which_dir == FullCD) ) {
+ AudioCDEncoder *encoder;
+ for ( encoder = encoders.first(); encoder; encoder = encoders.next() ){
+ if (d->cddbResult != KCDDB::CDDB::Success)
+ addEntry(d->s_fullCD, encoder, drive, -1);
+ else
+ addEntry(d->templateAlbumName, encoder, drive, -1);
+ }
+ }
+ }
+
+ if (list_tracks && d->which_dir != FullCD) {
+ // listing another dir than the "FullCD" one.
+ for (uint trackNumber = 1; trackNumber <= d->tracks; trackNumber++)
+ {
+ // Skip data tracks
+ if (!d->trackIsAudio[trackNumber-1])
+ continue;
+
+ switch (d->which_dir) {
+ case Root:{
+ addEntry(d->templateTitles[trackNumber - 1],
+ encoderTypeWAV, drive, trackNumber);
+ break;
+ }
+ case EncoderDir:
+ addEntry(d->templateTitles[trackNumber - 1],
+ d->encoder_dir_type, drive, trackNumber);
+ break;
+ case Info:
+ case Unknown:
+ default:
+ error(KIO::ERR_INTERNAL, url.path());
+ cdda_close(drive);
+ return;
+ }
+ }
+ }
+
+ totalSize(entry.count());
+ listEntry(entry, true);
+ cdda_close(drive);
+ finished();
+}
+
+void AudioCDProtocol::addEntry(const QString& trackTitle, AudioCDEncoder *encoder, struct cdrom_drive * drive, int trackNo)
+{
+ if(!encoder || !drive)
+ return;
+
+ long theFileSize = 0;
+ if (trackNo == -1)
+ { // adding entry for the full CD
+ theFileSize = fileSize(cdda_track_firstsector(drive, 1),
+ cdda_track_lastsector(drive, cdda_tracks(drive)),
+ encoder);
+ }
+ else
+ { // adding one regular track
+ long firstSector = cdda_track_firstsector(drive, trackNo);
+ long lastSector = cdda_track_lastsector(drive, trackNo);
+ theFileSize = fileSize(firstSector, lastSector, encoder);
+ }
+ UDSEntry entry;
+ app_file(entry, trackTitle + QString(".")+encoder->fileType(), theFileSize);
+ listEntry(entry, false);
+}
+
+long AudioCDProtocol::fileSize(long firstSector, long lastSector, AudioCDEncoder *encoder)
+{
+ if(!encoder)
+ return 0;
+
+ long filesize = CD_FRAMESIZE_RAW * ( lastSector - firstSector + 1 );
+ long length_seconds = (filesize) / 176400;
+
+ return encoder->size(length_seconds);
+}
+
+struct cdrom_drive *AudioCDProtocol::getDrive()
+{
+ QCString device(QFile::encodeName(d->device));
+
+ struct cdrom_drive * drive = 0;
+
+ if (!device.isEmpty() && device != "/")
+ drive = cdda_identify(device, CDDA_MESSAGE_PRINTIT, 0);
+ else
+ {
+ drive = cdda_find_a_cdrom(CDDA_MESSAGE_PRINTIT, 0);
+
+ if (0 == drive)
+ {
+ if (QFile(QFile::decodeName(DEFAULT_CD_DEVICE)).exists())
+ drive = cdda_identify(DEFAULT_CD_DEVICE, CDDA_MESSAGE_PRINTIT, 0);
+ }
+ }
+
+ if (0 == drive) {
+ kdDebug(7117) << "Can't find an audio CD on: \"" << d->device << "\"" << endl;
+
+ QFileInfo fi(d->device);
+ if(!fi.isReadable())
+ error(KIO::ERR_SLAVE_DEFINED, i18n("Device doesn't have read permissions for this account. Check the read permissions on the device."));
+ else if(!fi.isWritable())
+ error(KIO::ERR_SLAVE_DEFINED, i18n("Device doesn't have write permissions for this account. Check the write permissions on the device."));
+ else if(!fi.exists())
+ error(KIO::ERR_DOES_NOT_EXIST, d->device);
+ else error(KIO::ERR_SLAVE_DEFINED,
+i18n("Unknown error. If you have a cd in the drive try running cdparanoia -vsQ as yourself (not root). Do you see a track list? If not, make sure you have permission to access the CD device. If you are using SCSI emulation (possible if you have an IDE CD writer) then make sure you check that you have read and write permissions on the generic SCSI device, which is probably /dev/sg0, /dev/sg1, etc.. If it still does not work, try typing audiocd:/?device=/dev/sg0 (or similar) to tell kio_audiocd which device your CD-ROM is."));
+ return 0;
+ }
+
+ if (0 != cdda_open(drive))
+ {
+ kdDebug(7117) << "cdda_open failed" << endl;
+ error(KIO::ERR_CANNOT_OPEN_FOR_READING, d->device);
+ cdda_close(drive);
+ return 0;
+ }
+
+ return drive;
+}
+
+void AudioCDProtocol::paranoiaRead(
+ struct cdrom_drive * drive,
+ long firstSector,
+ long lastSector,
+ AudioCDEncoder* encoder,
+ const QString& fileName,
+ unsigned long size
+ )
+{
+ if(!encoder || !drive)
+ return;
+
+ cdrom_paranoia * paranoia = paranoia_init(drive);
+ if (0 == paranoia) {
+ kdDebug(7117) << "paranoia_init failed" << endl;
+ return;
+ }
+
+ int paranoiaLevel = PARANOIA_MODE_FULL ^ PARANOIA_MODE_NEVERSKIP;
+ switch (d->paranoiaLevel)
+ {
+ case 0:
+ paranoiaLevel = PARANOIA_MODE_DISABLE;
+ break;
+
+ case 1:
+ paranoiaLevel |= PARANOIA_MODE_OVERLAP;
+ paranoiaLevel &= ~PARANOIA_MODE_VERIFY;
+ break;
+
+ case 2:
+ paranoiaLevel |= PARANOIA_MODE_NEVERSKIP;
+ default:
+ break;
+ }
+
+ paranoia_modeset(paranoia, paranoiaLevel);
+
+ cdda_verbose_set(drive, CDDA_MESSAGE_PRINTIT, CDDA_MESSAGE_PRINTIT);
+
+ paranoia_seek(paranoia, firstSector, SEEK_SET);
+
+ long currentSector(firstSector);
+
+ unsigned long processed = encoder->readInit(CD_FRAMESIZE_RAW * (lastSector - firstSector + 1));
+ // TODO test for errors (processed<0)?
+ processedSize(processed);
+ bool ok = true;
+
+ unsigned long lastSize = size;
+ unsigned long diff = 0;
+
+ paranoia_read_limited_error = 0;
+ int warned = 0;
+ while (currentSector <= lastSector)
+ {
+ // TODO make the 5 configurable? The default in the lib is 20 fyi
+ int16_t * buf = paranoia_read_limited(paranoia, paranoiaCallback, 5);
+ if( warned == 0 && paranoia_read_limited_error >= 5 && d->reportErrors ){
+ warning(i18n("AudioCD: Disk damage detected on this track, risk of data corruption."));
+ warned = 1;
+ }
+ if (0 == buf) {
+ kdDebug(7117) << "Unrecoverable error in paranoia_read" << endl;
+ ok = false;
+ error( ERR_SLAVE_DEFINED, i18n( "Error reading audio data for %1 from the CD" ).arg( fileName ) );
+ break;
+ }
+
+ ++currentSector;
+
+ int encoderProcessed = encoder->read(buf, CD_FRAMESAMPLES);
+ if(encoderProcessed == -1){
+ kdDebug(7117) << "Encoder processing error, stopping." << endl;
+ ok = false;
+ QString errMsg = i18n( "Couldn't read %1: encoding failed" ).arg( fileName );
+ QString details = encoder->lastErrorMessage();
+ if ( !details.isEmpty() )
+ errMsg += "\n" + details;
+ error( ERR_SLAVE_DEFINED, errMsg );
+ break;
+ }
+ processed += encoderProcessed;
+
+ /**
+ * Because compression size is so 'unknown' use some guesswork
+ *
+ * 1) First assume that the reported size is correct and
+ * only change the totalSize if the guess it outside a range of %5.
+ * 2) Only increase in size unless the decrease is %5 of last estimate.
+ * This prevents continues small changes which is just annoying.
+ */
+ unsigned long end = lastSector - firstSector;
+ unsigned long cur = currentSector - firstSector;
+ unsigned long estSize = (processed / cur ) * end;
+
+ // If our guess is within 5% of reported
+ // size then use the reported size.
+ unsigned long guess = (long)((100/(float)size)*estSize);
+ if((guess > 97 && guess < 103) || estSize == 0){
+ if(processed > lastSize){
+ totalSize(processed+1);
+ lastSize = processed;
+ }
+ }
+ else{
+ float percentDone = ((float)cur/(float)end);
+ // Calculate estimated amount that will be wrong
+ diff = estSize - lastSize;
+ diff = (diff*(unsigned long)((100/(float)end)*(end-cur)))/2;
+ // Need 1% of data calculated as initial buffer, use %2 to be safe
+ if( percentDone < .02 ){
+ //qDebug("val: %f, diff: %ld", ((float)cur/(float)end), diff);
+ diff = 0;
+ }
+
+ // We are growing larger, increase total.
+ if(lastSize < estSize){
+ //qDebug("lastGuess: %ld, guess: %ld diff: %ld", lastSize, estSize, diff);
+ totalSize(estSize+diff);
+ lastSize = estSize+diff;
+ }
+ else{
+ int margin = (int)((percentDone)*75);
+ // Don't bother really trying until almost half way done.
+ if( percentDone <= .40 )
+ margin = 7;
+ unsigned long low = lastSize - lastSize/margin;
+ if(estSize < low){
+ //qDebug("low: %ld, estSize: %ld, num: %i", low, estSize, margin);
+ totalSize( estSize );
+ lastSize = estSize;
+ }
+ }
+ }
+ /**
+ * End estimation.
+ */
+ //qDebug("processed: %ld, totalSize: %ld", processed, estSize);
+ processedSize(processed);
+ }
+
+ if(processed > size)
+ totalSize(processed);
+
+ long encoderProcessed = encoder->readCleanup();
+ if ( encoderProcessed >= 0 ) {
+ processed += encoderProcessed;
+ if(processed > size)
+ totalSize(processed);
+ processedSize(processed);
+ }
+ else if ( ok ) // i.e. no error message already emitted
+ error( ERR_SLAVE_DEFINED, i18n( "Couldn't read %1: encoding failed" ).arg( fileName ) );
+
+ paranoia_free(paranoia);
+ paranoia = 0;
+}
+
+/**
+ * Read the settings from the URL
+ * @see loadSettings()
+ */
+void AudioCDProtocol::parseURLArgs(const KURL & url)
+{
+ d->clearURLargs();
+
+ QString query(KURL::decode_string(url.query()));
+
+ if (query.isEmpty() || query[0] != '?')
+ return;
+
+ query = query.mid(1); // Strip leading '?'.
+
+ QStringList tokens(QStringList::split('&', query));
+
+ for (QStringList::ConstIterator it(tokens.begin()); it != tokens.end(); ++it)
+ {
+ QString token(*it);
+
+ int equalsPos(token.find('='));
+ if (-1 == equalsPos)
+ continue;
+
+ QString attribute(token.left(equalsPos));
+ QString value(token.mid(equalsPos + 1));
+
+ if (attribute == QFL1("device"))
+ d->device = value;
+ else if (attribute == QFL1("paranoia_level"))
+ d->paranoiaLevel = value.toInt();
+ else if (attribute == QFL1("fileNameTemplate"))
+ d->fileNameTemplate = value;
+ else if (attribute == QFL1("albumNameTemplate"))
+ d->albumTemplate = value;
+ else if (attribute == QFL1("cddbChoice"))
+ d->cddbUserChoice = value.toInt();
+ else if (attribute == QFL1("niceLevel")){
+ int niceLevel = value.toInt();
+ if(setpriority(PRIO_PROCESS, getpid(), niceLevel) != 0)
+ kdDebug(7117) << "Setting nice level to (" << niceLevel << ") failed." << endl;
+ }
+ }
+}
+
+/**
+ * Read the settings set by the kcm modules
+ * @see parseURLArgs()
+ */
+void AudioCDProtocol::loadSettings()
+{
+ KConfig *config = new KConfig(QFL1("kcmaudiocdrc"), true /*readonly*/, false /*no kdeglobals*/);
+
+ config->setGroup(QFL1("CDDA"));
+
+ if (!config->readBoolEntry(QFL1("autosearch"),true)) {
+ d->device = config->readEntry(QFL1("device"),QFL1(DEFAULT_CD_DEVICE));
+ }
+
+ d->paranoiaLevel = 1; // enable paranoia error correction, but allow skipping
+
+ if (config->readBoolEntry("disable_paranoia",false)) {
+ d->paranoiaLevel = 0; // disable all paranoia error correction
+ }
+
+ if (config->readBoolEntry("never_skip",true)) {
+ d->paranoiaLevel = 2;
+ // never skip on errors of the medium, should be default for high quality
+ }
+
+ d->reportErrors = config->readBoolEntry( "report_errors", false );
+
+ if(config->hasKey("niceLevel")) {
+ int niceLevel = config->readNumEntry("niceLevel", 0);
+ if(setpriority(PRIO_PROCESS, getpid(), niceLevel) != 0)
+ kdDebug(7117) << "Setting nice level to (" << niceLevel << ") failed." << endl;
+ }
+
+ // The default track filename template
+ config->setGroup("FileName");
+ d->fileNameTemplate = config->readEntry("file_name_template", "%{albumartist} - %{number} - %{title}");
+ d->albumTemplate = config->readEntry("album_template", "%{albumartist} - %{albumtitle}");
+ d->rsearch = config->readEntry("regexp_search");
+ d->rreplace = config->readEntry("regexp_replace");
+ // if the regular expressions are enclosed in qoutes. remove them
+ // otherwise it is not possible to search for a space " ", since an empty (only spaces) value is not
+ // supported by KConfig, so the space has to be qouted, but then here the regexp searches really for " "
+ // instead of just the space. Alex
+ QRegExp qoutedString("^\".*\"$");
+ if (qoutedString.exactMatch(d->rsearch))
+ {
+ d->rsearch=d->rsearch.mid(1, d->rsearch.length()-2);
+ }
+ if (qoutedString.exactMatch(d->rreplace))
+ {
+ d->rreplace=d->rreplace.mid(1, d->rreplace.length()-2);
+ }
+
+ // Tell the encoders to load their settings
+ AudioCDEncoder *encoder = encoders.first();
+ while ( encoder ) {
+ if ( encoder->init() ) {
+ kdDebug(7117) << "Encoder for " << encoder->type() << " is available." << endl;
+ encoder->loadSettings();
+ encoder = encoders.next();
+ } else {
+ kdDebug(7117) << "Encoder for " << encoder->type() << " is NOT available." << endl;
+ encoders.remove( encoder );
+ encoder = encoders.current();
+ }
+ }
+
+ delete config;
+}
+
+/**
+ * Generates the track titles from the template using the cddb information.
+ */
+void AudioCDProtocol::generateTemplateTitles()
+{
+ d->templateTitles.clear();
+ if (d->cddbResult != KCDDB::CDDB::Success)
+ {
+ for (unsigned int i = 0; i < d->tracks; i++){
+ QString n;
+ d->templateTitles.append( i18n("Track %1").arg(n.sprintf("%02d", i + 1)));
+ }
+ return;
+ }
+
+ KCDDB::CDInfo info = d->cddbBestChoice;
+ if(d->cddbUserChoice >= 0 && (((uint)d->cddbUserChoice) < d->cddbList.count()))
+ info = d->cddbList[d->cddbUserChoice];
+
+ // Then generate the templates
+ d->templateTitles.clear();
+ for (uint i = 0; i < d->tracks; i++) {
+ QMap<QString, QString> macros;
+ macros["albumartist"] = info.artist;
+ macros["albumtitle"] = info.title;
+ macros["title"] = (info.trackInfoList[i].title);
+ QString n;
+ macros["number"] = n.sprintf("%02d", i + 1);
+ //macros["number"] = QString("%1").arg(i+1, 2, 10);
+ macros["genre"] = info.genre;
+ macros["year"] = QString::number(info.year);
+
+ QString title = KMacroExpander::expandMacros(d->fileNameTemplate, macros, '%').replace('/', QFL1("%2F"));
+ title.replace( QRegExp(d->rsearch), d->rreplace );
+ d->templateTitles.append(title);
+ }
+
+ QMap<QString, QString> macros;
+ macros["albumartist"] = info.artist;
+ macros["albumtitle"] = info.title;
+ macros["genre"] = info.genre;
+ macros["year"] = QString::number(info.year);
+ d->templateAlbumName = KMacroExpander::expandMacros(d->albumTemplate, macros, '%').replace('/', QFL1("%2F"));
+ d->templateAlbumName.replace( QRegExp(d->rsearch), d->rreplace );
+}
+
+/**
+ * Based upon the cdparinoia ripping application
+ * Only output BAD stuff
+ * The higher the paranoia_read_limited_error the worse the problem is
+ * FYI: PARANOIA_CB_READ & PARANOIA_CB_VERIFY happen continusly when ripping
+ */
+void paranoiaCallback(long, int function)
+{
+ switch(function){
+ case PARANOIA_CB_VERIFY:
+ //kdDebug(7117) << "PARANOIA_CB_VERIFY" << endl;
+ break;
+
+ case PARANOIA_CB_READ:
+ //kdDebug(7117) << "PARANOIA_CB_READ" << endl;
+ break;
+
+ case PARANOIA_CB_FIXUP_EDGE:
+ //kdDebug(7117) << "PARANOIA_CB_FIXUP_EDGE" << endl;
+ paranoia_read_limited_error = 2;
+ break;
+
+ case PARANOIA_CB_FIXUP_ATOM:
+ //kdDebug(7117) << "PARANOIA_CB_FIXUP_ATOM" << endl;
+ paranoia_read_limited_error = 6;
+ break;
+
+ case PARANOIA_CB_READERR:
+ kdDebug(7117) << "PARANOIA_CB_READERR" << endl;
+ paranoia_read_limited_error = 6;
+ break;
+
+ case PARANOIA_CB_SKIP:
+ kdDebug(7117) << "PARANOIA_CB_SKIP" << endl;
+ paranoia_read_limited_error = 8;
+ break;
+
+ case PARANOIA_CB_OVERLAP:
+ //kdDebug(7117) << "PARANOIA_CB_OVERLAP" << endl;
+ break;
+
+ case PARANOIA_CB_SCRATCH:
+ kdDebug(7117) << "PARANOIA_CB_SCRATCH" << endl;
+ paranoia_read_limited_error = 7;
+ break;
+
+ case PARANOIA_CB_DRIFT:
+ //kdDebug(7117) << "PARANOIA_CB_DRIFT" << endl;
+ paranoia_read_limited_error = 4;
+ break;
+
+ case PARANOIA_CB_FIXUP_DROPPED:
+ kdDebug(7117) << "PARANOIA_CB_FIXUP_DROPPED" << endl;
+ paranoia_read_limited_error = 5;
+ break;
+
+ case PARANOIA_CB_FIXUP_DUPED:
+ kdDebug(7117) << "PARANOIA_CB_FIXUP_DUPED" << endl;
+ paranoia_read_limited_error = 5;
+ break;
+ }
+}
+
diff --git a/kioslave/audiocd/audiocd.h b/kioslave/audiocd/audiocd.h
new file mode 100644
index 00000000..a9038496
--- /dev/null
+++ b/kioslave/audiocd/audiocd.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2000 Rik Hemsley (rikkus) <rik@kde.org>
+ * Copyright (C) 2000, 2001, 2002 Michael Matz <matz@kde.org>
+ * Copyright (C) 2001 Carsten Duvenhorst <duvenhorst@m2.uni-hannover.de>
+ * Copyright (C) 2001 Adrian Schroeter <adrian@suse.de>
+ * Copyright (C) 2003 Richard Lärkäng <richard@goteborg.utfors.se>
+ * Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
+ * Copyright (C) 2004, 2005 Benjamin Meyer <ben at meyerhome dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * ERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef AUDIO_CD_H
+#define AUDIO_CD_H
+
+#include <kio/slavebase.h>
+
+class AudioCDEncoder;
+
+struct cdrom_drive;
+
+namespace AudioCD {
+
+/**
+ * The core class of the audiocd:// ioslave.
+ * It has the iosalve login and the ripping logic. The actual encoding
+ * is done by encoders that are seperate objects.
+ */
+class AudioCDProtocol : public KIO::SlaveBase
+{
+ public:
+
+ AudioCDProtocol(const QCString & protocol, const QCString & pool, const QCString & app);
+ virtual ~AudioCDProtocol();
+
+ virtual void get(const KURL &);
+ virtual void stat(const KURL &);
+ virtual void listDir(const KURL &);
+
+ protected:
+ AudioCDEncoder *encoderFromExtension(const QString& extension);
+ AudioCDEncoder *determineEncoder(const QString & filename);
+
+ struct cdrom_drive *findDrive(bool &noPermission);
+ void parseURLArgs(const KURL &);
+
+ void loadSettings();
+
+ /**
+ * From the request information (Private member "d"),
+ * get the first and last sector for the request.
+ * return false if the parameters are invalid (for instance,
+ * track number which does not exist on this CD)
+ */
+ bool getSectorsForRequest(struct cdrom_drive * drive,
+ long & firstSector, long & lastSector) const;
+
+ /**
+ * Give the size in bytes of the space between those two
+ * sectors, depending on the file type.
+ */
+ long fileSize(long firstSector, long lastSector, AudioCDEncoder *encoder);
+
+ /**
+ * The heart of this ioslave.
+ * Reads data off the cd and then passes it to an encoder to encode
+ */
+ void paranoiaRead(
+ struct cdrom_drive * drive,
+ long firstSector,
+ long lastSector,
+ AudioCDEncoder* encoder,
+ const QString& fileName,
+ unsigned long size
+ );
+
+ struct cdrom_drive *initRequest(const KURL &);
+ uint discid(struct cdrom_drive *);
+
+ /**
+ * Add an entry in the KIO directory, using the title you give,
+ * it will set the extension itself depending on the fileType.
+ * You must also give the trackNumber for the size of the file
+ * to be calculated.
+ * NOTE: if you want to add a file which is the whole CD, give
+ * trackNo = -1
+ * NOTE2: you can safely add MP3 or OGG files always. the function
+ * will check if kio_audiocd was compiled with support for those,
+ * so NO NEED to wrap your calls with #ifdef for lame or vorbis.
+ * this function will do the right thing.
+ */
+ void addEntry(const QString& trackTitle, AudioCDEncoder *encoder,
+ struct cdrom_drive * drive, int trackNo);
+
+ class Private;
+ Private * d;
+
+private:
+
+ void generateTemplateTitles();
+
+ QPtrList<AudioCDEncoder> encoders;
+ cdrom_drive * getDrive();
+
+ // These are the only garenteed encoders to be built, the rest
+ // are dynamic depending on other libraries on the system
+ AudioCDEncoder *encoderTypeCDA;
+ AudioCDEncoder *encoderTypeWAV;
+};
+
+} // end namespace AudioCD
+
+#endif // AUDIO_CD_H
+
diff --git a/kioslave/audiocd/audiocd.protocol b/kioslave/audiocd/audiocd.protocol
new file mode 100644
index 00000000..7a3f497f
--- /dev/null
+++ b/kioslave/audiocd/audiocd.protocol
@@ -0,0 +1,16 @@
+[Protocol]
+exec=kio_audiocd
+protocol=audiocd
+input=none
+output=filesystem
+listing=Name,Type,Size,Access
+reading=true
+writing=false
+makedir=false
+deleting=false
+linking=false
+moving=false
+Icon=cdaudio_unmount
+DocPath=kioslave/audiocd.html
+Class=:local
+ShowPreviews=false
diff --git a/kioslave/audiocd/audiocd.upd b/kioslave/audiocd/audiocd.upd
new file mode 100644
index 00000000..f4818e5f
--- /dev/null
+++ b/kioslave/audiocd/audiocd.upd
@@ -0,0 +1,4 @@
+# Update for transport configuration
+Id=1
+File=kcmaudiocdrc
+Script=upgrade-metadata.sh,bash
diff --git a/kioslave/audiocd/configure.in.bot b/kioslave/audiocd/configure.in.bot
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kioslave/audiocd/configure.in.bot
diff --git a/kioslave/audiocd/configure.in.in b/kioslave/audiocd/configure.in.in
new file mode 100644
index 00000000..073d0f96
--- /dev/null
+++ b/kioslave/audiocd/configure.in.in
@@ -0,0 +1,46 @@
+AC_DEFUN([AC_CHECK_LIBFLAC],
+[
+ AC_LANG_SAVE
+ AC_LANG_C
+ have_libFLAC=no
+ KDE_CHECK_HEADER(FLAC/metadata.h,
+ [
+ KDE_CHECK_LIB(FLAC,FLAC__seekable_stream_decoder_process_single,
+ have_libFLAC=yes)
+
+ ])
+ if test "x$have_libFLAC" = "xyes"; then
+ LIBFLAC="-lFLAC"
+ AC_DEFINE(HAVE_LIBFLAC, 1,
+ [Define if you have libFLAC (required for loading FLAC files)])
+ fi
+ AC_SUBST(LIBFLAC)
+ AC_LANG_RESTORE
+])
+
+AC_DEFUN([AC_CHECK_LIBOGGFLAC],
+[
+ AC_LANG_SAVE
+ AC_LANG_C
+ have_libOggFLAC=no
+ KDE_CHECK_HEADER(OggFLAC/seekable_stream_decoder.h,
+ [
+ KDE_CHECK_LIB(OggFLAC,OggFLAC__seekable_stream_decoder_process_single,
+ have_libOggFLAC=yes,,[-lm -lOggFLAC -lFLAC])
+
+ ])
+ if test "x$have_libOggFLAC" = "xyes"; then
+ LIBOGGFLAC="-lOggFLAC"
+ AC_DEFINE(HAVE_LIBOGGFLAC, 1,
+ [Define if you have libOggFLAC (required for loading OggFLAC files)])
+ fi
+ AC_SUBST(LIBOGGFLAC)
+ AC_LANG_RESTORE
+])
+
+AC_ARG_WITH(flac,AC_HELP_STRING([--with-flac],[Enable FLAC support @<:@default=check@:>@]),[flac_test="$withval"],[flac_test="yes"])
+
+if test "x$flac_test" = "xyes" ; then
+ AC_CHECK_LIBFLAC
+ AC_CHECK_LIBOGGFLAC
+fi
diff --git a/kioslave/audiocd/kcmaudiocd/Makefile.am b/kioslave/audiocd/kcmaudiocd/Makefile.am
new file mode 100644
index 00000000..962c3fa0
--- /dev/null
+++ b/kioslave/audiocd/kcmaudiocd/Makefile.am
@@ -0,0 +1,17 @@
+kde_module_LTLIBRARIES = kcm_audiocd.la
+
+kcm_audiocd_la_SOURCES = audiocdconfig.ui kcmaudiocd.cpp
+
+kcm_audiocd_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module
+kcm_audiocd_la_LIBADD = ../plugins/libaudiocdplugins.la $(LIB_KDEUI)
+
+INCLUDES = -I$(srcdir)/../plugins -I$(top_srcdir)/libkcddb $(all_includes)
+
+kcm_audiocd_la_METASOURCES = AUTO
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/kcmaudiocd.pot
+
+xdg_apps_DATA = audiocd.desktop
+
+audiocdconfig.cpp: audiocdconfig.h
diff --git a/kioslave/audiocd/kcmaudiocd/audiocd.desktop b/kioslave/audiocd/kcmaudiocd/audiocd.desktop
new file mode 100644
index 00000000..5a9df9a5
--- /dev/null
+++ b/kioslave/audiocd/kcmaudiocd/audiocd.desktop
@@ -0,0 +1,182 @@
+[Desktop Entry]
+Exec=kcmshell audiocd
+Icon=cdaudio_unmount
+Type=Application
+
+X-KDE-ModuleType=Library
+X-KDE-Library=audiocd
+
+Name=Audio CDs
+Name[af]=Audio Cds
+Name[ar]=أقراص مدمجة صوتية
+Name[bg]=Аудио диск
+Name[bn]=অডিও সিডি
+Name[br]=CD klevet
+Name[bs]=Audio CDi
+Name[ca]=CD d'àudio
+Name[cs]=Zvuková CD
+Name[cy]=CDau Sain
+Name[da]=Lyd-cd'er
+Name[de]=Audio-CDs
+Name[el]=CD μουσικής
+Name[eo]=Son-KDoj
+Name[es]=CDs audio
+Name[et]=Audioplaadid
+Name[eu]=Audio CD-ak
+Name[fa]=دیسکهای فشردۀ صوتی
+Name[fi]=Ääni-CD:t
+Name[fr]=CD audio
+Name[ga]=Dlúthdhioscaí Fuaime
+Name[he]=תקליטורי שמע
+Name[hi]=ऑडीयो सीडी
+Name[hr]=Audio CD-i
+Name[hu]=Hang-CD-böngésző
+Name[is]=Tónlistardiskaflakkari
+Name[it]=CD Audio
+Name[ja]=オーディオ CD
+Name[kk]=Аудио CD
+Name[km]=ស៊ីឌី​អ៉ូឌីយ៉ូ
+Name[ko]=오디오 CD
+Name[lt]=Audio kompaktai
+Name[mk]=Аудио ЦД-а
+Name[nb]=Lyd-CD-er
+Name[nds]=Klang-CDs
+Name[ne]=अडियो सीडी
+Name[nl]=Audio-cd's
+Name[nn]=Lyd-CD-ar
+Name[pa]=ਆਡੀਓ CD
+Name[pl]=Przeglądarka audio CD
+Name[pt]=CDs de Áudio
+Name[pt_BR]=CDs de Áudio
+Name[ro]=CD-uri audio
+Name[ru]=Аудио CD
+Name[se]=Jietna-CD:at
+Name[sk]=Zvukové CD
+Name[sl]=Avdio CD-ji
+Name[sr]=Аудио CD-ови
+Name[sr@Latn]=Audio CD-ovi
+Name[sv]=Ljud-cd
+Name[ta]=கேட்பொலி குறுந்தகடுகள்
+Name[tg]=Дискҳои Фишурдаи Садо
+Name[th]=ซีดีบันทึกเสียง
+Name[tr]=Müzik CD
+Name[uk]=Аудіо-КД
+Name[uz]=Audio kompakt-disklar
+Name[uz@cyrillic]=Аудио компакт-дисклар
+Name[ven]=CD ino thetsheleswa
+Name[xh]=CDs Zokuvakalayo
+Name[zh_CN]=音频 CD
+Name[zh_HK]=音樂 CD
+Name[zh_TW]=音樂光碟
+Name[zu]=Ama-CD Okuzwakalayo
+Comment=Audiocd IO Slave Configuration
+Comment[af]=Klank cd Io Slaaf Opstelling
+Comment[az]=Name=Audiosd IO Kölələri Quraşdırması
+Comment[bg]=Настройване на аудио диска
+Comment[bn]=অডিও-সিডি আই/ও স্লেভ কনফিগারেশন
+Comment[br]=Kefluniadur Sklav IO Audiocd
+Comment[bs]=Podešavanje Audiocd IO Slave
+Comment[ca]=Configuració de l'E/S esclava dels CD àudio
+Comment[cs]=Nastavení IO klienta pro zvuková CD
+Comment[cy]=Gosodiad Gwas IO ar gyfer CDau Sain
+Comment[da]=Lyd-cd IO-slave-indstilling
+Comment[de]=Einrichtung des Ein-/Ausgabemoduls für Audio-CDs
+Comment[el]=Ρύθμιση Audiocd IO Slave
+Comment[eo]=Agordo por la muzikdiska sklavo
+Comment[es]=Configuración del esclavo de E/S de audiocd
+Comment[et]=Audio CD IO mooduli seadistamine
+Comment[eu]=Audiocd IO mendekoaren konfigurazioa
+Comment[fa]=پیکربندی پی‌رو IO دیسک فشردۀ صوتی
+Comment[fi]=Audiocd-siirräntätyöskentelijän asetukset
+Comment[fr]=Configuration du module pour CD audio
+Comment[gl]=Configuración do escravo IO de audiocd
+Comment[he]=שינוי הגדרות פרוטוקול תקליטורי השמע
+Comment[hi]=ऑडियो-सीडी आई-ओ स्लेव कॉन्फ़िगरेशन
+Comment[hr]=Postava Audiocd IO poslužnika
+Comment[hu]=Az audiocd KDE-protokoll beállításai
+Comment[is]=Stillingar Audiocd I/O þrælsins
+Comment[it]=Configurazione dell'IO Slave per i CD Audio
+Comment[ja]=オーディオ CD IO スレーブの設定
+Comment[kk]=AudioCD IO Slave баптауы
+Comment[km]=ការ​កំណត់​រចនាសម្ព័ន្ធ IO Slave របស់​ស៊ីឌី​អូឌីយ៉ូ
+Comment[ko]=오디오 CD IO 슬레이브 설정
+Comment[lt]=Audio kompakto IO vergo derinimas
+Comment[ms]=Penyelarasan Hamba IO Audiocd
+Comment[mt]=Konfigurazzjoni tal-iskjav IO AudioCD
+Comment[nb]=Oppsett av lyd-CD IO-slave
+Comment[nds]=Instellen för dat In-/Utgaavmoduul för Klang-CDs
+Comment[ne]=Audiocd IO स्लेभ कन्फिगरेसन
+Comment[nl]=AudioCD IO slave instellen
+Comment[nn]=Oppsett av IU-slave for lyd-CD
+Comment[pa]=ਆਡੀਓ ਸੀਡੀ IO ਸਲੇਵ ਸੰਰਚਨਾ
+Comment[pl]=Konfiguracja procedury we/wy dla audio CD
+Comment[pt]=Configuração do IO Slave de CDs-Áudio
+Comment[pt_BR]=Configuração do Áudio CD Escravo
+Comment[ro]=Configurează dispozitivul I/O pentru CD audio
+Comment[ru]=Настройка протокола AudioCD
+Comment[se]=Heivet SO-šláva jietna-CD:aid várás
+Comment[sk]=Nastavenie IO klienta pre zvukové CD
+Comment[sl]=Nastavitve Audiocd podrejeni V/I
+Comment[sr]=Подешавање Audiocd IO Slave-а
+Comment[sr@Latn]=Podešavanje Audiocd IO Slave-a
+Comment[sv]=Anpassa I/O-slav för ljud-cd
+Comment[ta]=ஒலிக் குறுந்தகடு உள்-வெளி அடிமை வடிவமைப்பு
+Comment[tg]=Танзими Фармонбари Ворид/Хориҷи Дискҳои Фишурдаи Садо
+Comment[th]=ปรับแต่ง Audiocd IO Slave
+Comment[tr]=Müzik CD Yapılandırması
+Comment[uk]=Налаштування підлеглого В/В "Audiocd"
+Comment[ven]=Nzudzanyo Audiocd IO Slave
+Comment[xh]=Video-DVD IO Slave Uqwalaselo
+Comment[zh_CN]=音频 CD 输入输出从属模块配置
+Comment[zh_HK]=音樂 CD IO Slave 設定
+Comment[zh_TW]=音樂光碟 IO Slave 組態
+Comment[zu]=Inhlanganiselo ye-Audiocd IO Slave
+Keywords=Audio CD,CD,Ogg,Vorbis,Encoding,CDDA,Bitrate
+Keywords[bg]=аудио, диск, компактдиск, КД, кодек, музика, Audio CD, CD, Ogg, Vorbis, Encoding, CDDA, Bitrate
+Keywords[br]=CD klevet,CD,Ogg,Vorbis,Kodadur,CDDA,Feur
+Keywords[ca]=Àudio CD,CD,Ogg,Vorbis,Codificació,CDDA,Taxa de bits
+Keywords[cs]=Zvukové CD,CD,Ogg,Vorbis,Kódování,CDDA,Bitrate,CDDB
+Keywords[cy]=CD Sain,CD,crynoddisg,Ogg,Vorbis,Amgodio,CDDA,Cyfradd Ddidau
+Keywords[da]=Lyd-cd,CD,Ogg,Vorbis,Indkodning,CDDA,Bitrate
+Keywords[de]=Audio CD,CD,Ogg,Vorbis,Encoding,CDDA,Bitrate,MP3
+Keywords[el]=CD μουσικής,CD,Ogg,Vorbis,Κωδικοποίηση,CDDA,Ρυθμός bit
+Keywords[es]=CD de audio,CD,Ogg,Vorbis,Codificación,CDDA,Ratio de bits
+Keywords[et]=audio,CD,Ogg,Vorbis,kodeering,CDDA,bitikiirus
+Keywords[eu]=Audio CD,CD,Ogg,Vorbis,Kdeketa,CDDA,bit-maiztasuna
+Keywords[fa]=دیسک فشرده، دیسک فشرده، Ogg، Vorbis، کدبندی، CDDA، میزان ارسال بیت صوتی
+Keywords[fi]=Ääni-CD,CD,Ogg,Vorbis,Koodaus,CDDA,Bittinopeus
+Keywords[fr]=CD audio,CD,Ogg,Vorbis,Encodage,CDDA,débit
+Keywords[ga]=CD Fuaime,CD,Ogg,Vorbis,Ionchódú,CDDA,Ráta Giotán
+Keywords[gl]=Audio CD,CD,Ogg,Vorbis,Codificación,CDDA,Razón de Bits
+Keywords[hi]=ऑडियो सीडी,सीडी,ऑग,वॉर्बिस,एनकोडिंग,सीडीडीए,बिटरेट
+Keywords[hu]=hang-CD,CD,Ogg,Vorbis,kódolás,CDDA,bitráta
+Keywords[it]=CD Audio,CD,Ogg,Vorbis,Codifica,CDDA,Bitrate
+Keywords[ja]=オーディオ CD,CD,Ogg,Vorbis,エンコーディング,CDDA,ビットレート
+Keywords[km]=ស៊ីឌី​អូឌីយ៉ូ,ស៊ីឌី,Ogg,Vorbis,អ៊ិនកូដ,CDDA,អត្រាប៊ីត
+Keywords[ko]=오디오 CD,CD,Ogg,인코딩,CDDA
+Keywords[lt]=Audio CD,CD,Ogg,Vorbis,Encoding,CDDA,Bitrate,įkodavimas
+Keywords[mk]=Аудио CD,CD,Ogg,Vorbis,Кодирање,CDDA,Брзина во битови
+Keywords[nb]=Audio CD,CD,Ogg,Vorbis,Koding,CDDA,Bitrate
+Keywords[nds]=Audio-CD,Klang-CD,CD,Ogg,Vorbis,Koderen,CDDA,Bitrate
+Keywords[ne]=अडियो सीडी,सीडी,अग,भर्बिस,सङ्केतन,सीडीडीए,बिटरेट
+Keywords[nl]=Audio-cd,cd,Ogg,Vorbis,Encoding,CDDA,Bitrate
+Keywords[nn]=lyd-CD,CD,Ogg,Vorbis,koding,CDDA,bitrate
+Keywords[pa]=ਆਡੀਓ CD,CD,Ogg,Vorbis,ਇਕੋਡਿੰਗ,CDDA,ਬਿੱਟਰੇਟ
+Keywords[pl]=Audio CD,CD,Ogg,Vorbis,Encoding,CDDA,Bitrate, Kodowanie
+Keywords[pt]=CD de Áudio,CD,Ogg,Vorbis,Codificação,CDDA,Taxa de Bits
+Keywords[pt_BR]=CD de Áudio,CD,Ogg,Vorbis,Codificação,CDDA,Bitrate
+Keywords[ro]=CD audio,ogg,vorbis,codare,CDDA,rată de bit
+Keywords[ru]=Audio CD,CD,Ogg,Vorbis,Encoding,CDDA,Bitrate,битрейт
+Keywords[sk]=zvukové CD,CD,Ogg,Vorbis,kódovanie,CDDA,bitová frekvencia
+Keywords[sl]=Avdio CD,CD,Ogg,Vorbis,kodiranje,CDDA,bitna hitrost
+Keywords[sr]=Audio CD,CD,Ogg,Vorbis,Encoding,CDDA,Bitrate, Аудио CD, кодирање,битрата
+Keywords[sr@Latn]=Audio CD,CD,Ogg,Vorbis,Encoding,CDDA,Bitrate, Audio CD, kodiranje,bitrata
+Keywords[sv]=ljud-cd,cd,Ogg,Vorbis,kodning,CDDA,bithastighet
+Keywords[ta]=கேட்பொலி குறுந்தகடு,குறுந்தகடு,ஓஜிஜி,வோர்பிஸ்,குறியிடுதல்,சிடிடேஏ,பிட் மதிப்பு
+Keywords[tg]=Диски Фишурдаи Садо,Диски Фишурда,Ogg,Vorbis,Рамзигузор,CDDA,Bitrate
+Keywords[th]=ซีดีบันทึกเสียง, ซีดี,Ogg,Vorbis,เข้ารหัส,CDDA,บิตเรต
+Keywords[tr]=Ses CD,CD,Ogg,Vorbis,Kodlama,CDDA,Bitrate
+Keywords[uk]=Аудіо-КД,КД,Ogg,Vorbis,кодування,CDDA,частота вибірки
+Keywords[zh_CN]=音频 CD,CD,Ogg,Vorbis,编码,CDDA,Bitrate
+
+Categories=Qt;KDE;Settings;X-KDE-settings-sound;
diff --git a/kioslave/audiocd/kcmaudiocd/audiocdconfig.ui b/kioslave/audiocd/kcmaudiocd/audiocdconfig.ui
new file mode 100644
index 00000000..a742a4cc
--- /dev/null
+++ b/kioslave/audiocd/kcmaudiocd/audiocdconfig.ui
@@ -0,0 +1,628 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>AudiocdConfig</class>
+<widget class="KCModule">
+ <property name="name">
+ <cstring>AudiocdConfig</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>640</width>
+ <height>563</height>
+ </rect>
+ </property>
+ <property name="layoutMargin" stdset="0">
+ </property>
+ <property name="layoutSpacing" stdset="0">
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>tabWidget</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="layoutMargin" stdset="0">
+ </property>
+ <property name="layoutSpacing" stdset="0">
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;General</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="4" column="2">
+ <property name="name">
+ <cstring>Spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>210</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="3" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>encoderPriority</cstring>
+ </property>
+ <property name="title">
+ <string>Encoder Priority</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QSlider" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>niceLevel</cstring>
+ </property>
+ <property name="minValue">
+ <number>-19</number>
+ </property>
+ <property name="maxValue">
+ <number>19</number>
+ </property>
+ <property name="lineStep">
+ <number>5</number>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="tickmarks">
+ <enum>NoMarks</enum>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Highest</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Lowest</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Normal</string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLineEdit" row="0" column="2">
+ <property name="name">
+ <cstring>cd_device_string</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>/dev/cdrom</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Specify a location for the drive you want to use. Normally, this is a file inside the /dev folder representing your CD or DVD drive.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>cd_specify_device</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Specify CD device:</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Check this to specify a CD device different from the one which is detected automatically</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>ec_enable_check</cstring>
+ </property>
+ <property name="text">
+ <string>Use &amp;error correction when reading the CD</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If you uncheck this option, the slave will not try to use error correction which can be useful for reading damaged CDs. However, this feature can be problematic in some cases, so you can switch it off here.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>ec_skip_check</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Skip on errors</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>spacer4_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Names</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>fileNameGroupBox</cstring>
+ </property>
+ <property name="title">
+ <string>File Name (without extension)</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>The following macros will be expanded:</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="5" column="1">
+ <property name="name">
+ <cstring>textLabel13</cstring>
+ </property>
+ <property name="text">
+ <string>Genre</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>textLabel7</cstring>
+ </property>
+ <property name="text">
+ <string>Track Number</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel2_3</cstring>
+ </property>
+ <property name="text">
+ <string>%{title}</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="1">
+ <property name="name">
+ <cstring>textLabel11</cstring>
+ </property>
+ <property name="text">
+ <string>Year</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>textLabel3_3</cstring>
+ </property>
+ <property name="text">
+ <string>Track Title</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="1">
+ <property name="name">
+ <cstring>textLabel9</cstring>
+ </property>
+ <property name="text">
+ <string>Album Artist</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel10</cstring>
+ </property>
+ <property name="text">
+ <string>%{year}</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>%{albumtitle}</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="1">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Album Title</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>textLabel12</cstring>
+ </property>
+ <property name="text">
+ <string>%{genre}</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel8</cstring>
+ </property>
+ <property name="text">
+ <string>%{albumartist}</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel4_2</cstring>
+ </property>
+ <property name="text">
+ <string>%{number}</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>fileNameLineEdit</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>Name Regular Expression Replacement</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Selection:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Regular expression used on all file names. For example using selection " " and replace with "_" would replace all the spaces with underlines.
+</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>inputlabel</cstring>
+ </property>
+ <property name="text">
+ <string>Input:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>outputLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Output:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>exampleLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Example</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>exampleOutput</cstring>
+ </property>
+ <property name="text">
+ <string>Cool artist - example audio file.wav</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="5" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>example</cstring>
+ </property>
+ <property name="text">
+ <string>Cool artist - example audio file.wav</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_replaceInput</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="2">
+ <property name="name">
+ <cstring>kcfg_replaceOutput</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>textLabel3_2</cstring>
+ </property>
+ <property name="text">
+ <string>Replace with:</string>
+ </property>
+ </widget>
+ <widget class="Line" row="3" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>line1</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="0" column="1">
+ <property name="name">
+ <cstring>fileNameGroupBox_2</cstring>
+ </property>
+ <property name="title">
+ <string>Album Name</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>fileNameLabel_2</cstring>
+ </property>
+ <property name="text">
+ <string>The following macros will be expanded:</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout9</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="2" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>textLabel20</cstring>
+ </property>
+ <property name="text">
+ <string>Year</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>textLabel15</cstring>
+ </property>
+ <property name="text">
+ <string>%{albumartist}</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel16</cstring>
+ </property>
+ <property name="text">
+ <string>%{year}</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="1">
+ <property name="name">
+ <cstring>textLabel21</cstring>
+ </property>
+ <property name="text">
+ <string>Genre</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>textLabel19</cstring>
+ </property>
+ <property name="text">
+ <string>Album Artist</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>textLabel18</cstring>
+ </property>
+ <property name="text">
+ <string>Album Title</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel17</cstring>
+ </property>
+ <property name="text">
+ <string>%{genre}</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel14</cstring>
+ </property>
+ <property name="text">
+ <string>%{albumtitle}</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>31</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>albumNameLineEdit</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>ec_enable_check</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>ec_skip_check</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>cd_specify_device</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>cd_device_string</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>tabWidget</tabstop>
+ <tabstop>cd_device_string</tabstop>
+ <tabstop>ec_enable_check</tabstop>
+ <tabstop>ec_skip_check</tabstop>
+ <tabstop>niceLevel</tabstop>
+ <tabstop>fileNameLineEdit</tabstop>
+ <tabstop>albumNameLineEdit</tabstop>
+ <tabstop>kcfg_replaceInput</tabstop>
+ <tabstop>kcfg_replaceOutput</tabstop>
+ <tabstop>example</tabstop>
+</tabstops>
+<includes>
+ <include location="global" impldecl="in declaration">kcmodule.h</include>
+</includes>
+<slots>
+ <slot>toggleLowpass()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kioslave/audiocd/kcmaudiocd/kcmaudiocd.cpp b/kioslave/audiocd/kcmaudiocd/kcmaudiocd.cpp
new file mode 100644
index 00000000..7551b7ad
--- /dev/null
+++ b/kioslave/audiocd/kcmaudiocd/kcmaudiocd.cpp
@@ -0,0 +1,267 @@
+/*
+ Copyright (C) 2001 Carsten Duvenhorst <duvenhorst@m2.uni-hannover.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <kconfig.h>
+#include <klineedit.h>
+#include <klocale.h>
+
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qgroupbox.h>
+#include <qslider.h>
+#include <qtabwidget.h>
+#include <kaboutdata.h>
+#include <knuminput.h>
+#include <qregexp.h>
+#include <qlabel.h>
+
+#include <audiocdencoder.h>
+#include "kcmaudiocd.moc"
+#include <kconfigdialogmanager.h>
+
+KAudiocdModule::KAudiocdModule(QWidget *parent, const char *name)
+ : AudiocdConfig(parent, name), configChanged(false)
+{
+ QString foo = i18n("Report errors found on the cd.");
+
+ setButtons(Default|Apply);
+
+ config = new KConfig("kcmaudiocdrc");
+
+ QPtrList<AudioCDEncoder> encoders;
+ AudioCDEncoder::findAllPlugins(0, encoders);
+ AudioCDEncoder *encoder;
+ for ( encoder = encoders.first(); encoder; encoder = encoders.next() ){
+ if (encoder->init()) {
+ KConfigSkeleton *config = NULL;
+ QWidget *widget = encoder->getConfigureWidget(&config);
+ if(widget && config){
+ tabWidget->addTab(widget, i18n("%1 Encoder").arg(encoder->type()));
+ KConfigDialogManager *configManager = new KConfigDialogManager(widget, config, QString(encoder->type()+" EncoderConfigManager").latin1());
+ encoderSettings.append(configManager);
+ }
+ }
+ }
+
+ load();
+
+ KConfigDialogManager *widget;
+ for ( widget = encoderSettings.first(); widget; widget = encoderSettings.next() ){
+ connect(widget, SIGNAL(widgetModified()), this, SLOT(slotModuleChanged()));
+ }
+
+ //CDDA Options
+ connect(cd_specify_device,SIGNAL(clicked()),this,SLOT(slotConfigChanged()));
+ connect(ec_enable_check,SIGNAL(clicked()),this,SLOT(slotEcEnable()));
+ connect(ec_skip_check,SIGNAL(clicked()),SLOT(slotConfigChanged()));
+ connect(cd_device_string,SIGNAL(textChanged(const QString &)),SLOT(slotConfigChanged()));
+ connect(niceLevel,SIGNAL(valueChanged(int)),SLOT(slotConfigChanged()));
+
+ // File Name
+ connect(fileNameLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(slotConfigChanged()));
+ connect(albumNameLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(slotConfigChanged()));
+ connect( kcfg_replaceInput, SIGNAL( textChanged(const QString&) ), this, SLOT( updateExample() ) );
+ connect( kcfg_replaceOutput, SIGNAL( textChanged(const QString&) ), this, SLOT( updateExample() ) );
+ connect( example, SIGNAL( textChanged(const QString&) ), this, SLOT( updateExample() ) );
+ connect( kcfg_replaceInput, SIGNAL( textChanged(const QString&) ), this, SLOT( slotConfigChanged() ) );
+ connect( kcfg_replaceOutput, SIGNAL( textChanged(const QString&) ), this, SLOT( slotConfigChanged() ) );
+ connect( example, SIGNAL( textChanged(const QString&) ), this, SLOT( slotConfigChanged() ) );
+
+ KAboutData *about =
+ new KAboutData(I18N_NOOP("kcmaudiocd"), I18N_NOOP("KDE Audio CD IO Slave"),
+ 0, 0, KAboutData::License_GPL,
+ I18N_NOOP("(c) 2000 - 2005 Audio CD developers"));
+
+ about->addAuthor("Benjamin C. Meyer", I18N_NOOP("Current Maintainer"), "ben@meyerhome.net");
+ about->addAuthor("Carsten Duvenhorst", 0, "duvenhorst@duvnet.de");
+ setAboutData(about);
+}
+
+KAudiocdModule::~KAudiocdModule()
+{
+ delete config;
+}
+
+QString removeQoutes(const QString& text)
+{
+ QString deqoutedString=text;
+ QRegExp qoutedStringRegExp("^\".*\"$");
+ if (qoutedStringRegExp.exactMatch(text))
+ {
+ deqoutedString=text.mid(1, text.length()-2);
+ }
+ return deqoutedString;
+}
+
+bool needsQoutes(const QString& text)
+{
+ QRegExp spaceAtTheBeginning("^\\s+.*$");
+ QRegExp spaceAtTheEnd("^.*\\s+$");
+ return (spaceAtTheBeginning.exactMatch(text) || spaceAtTheEnd.exactMatch(text));
+
+
+}
+
+void KAudiocdModule::updateExample()
+{
+ QString text = example->text();
+ QString deqoutedReplaceInput=removeQoutes(kcfg_replaceInput->text());
+ QString deqoutedReplaceOutput=removeQoutes(kcfg_replaceOutput->text());
+
+ text.replace( QRegExp(deqoutedReplaceInput), deqoutedReplaceOutput );
+ exampleOutput->setText(text);
+}
+
+void KAudiocdModule::defaults() {
+ load( false );
+}
+
+void KAudiocdModule::save() {
+ if (!configChanged ) return;
+
+ {
+ KConfigGroupSaver saver(config, "CDDA");
+
+ // autosearch is the name of the config option, which has the
+ // reverse sense of the current text of the configuration option,
+ // which is specify the device. Therefore, invert the value on write.
+ //
+ config->writeEntry("autosearch", !(cd_specify_device->isChecked()) );
+ config->writeEntry("device",cd_device_string->text());
+ config->writeEntry("disable_paranoia",!(ec_enable_check->isChecked()));
+ config->writeEntry("never_skip",!(ec_skip_check->isChecked()));
+ config->writeEntry("niceLevel", niceLevel->value());
+ }
+
+ {
+ KConfigGroupSaver saver(config, "FileName");
+ config->writeEntry("file_name_template", fileNameLineEdit->text());
+ config->writeEntry("album_name_template", albumNameLineEdit->text());
+ config->writeEntry("regexp_example", example->text());
+ // save qouted if required
+ QString replaceInput=kcfg_replaceInput->text();
+ QString replaceOutput=kcfg_replaceOutput->text();
+ if (needsQoutes(replaceInput))
+ {
+ replaceInput=QString("\"")+replaceInput+QString("\"");
+ }
+ if (needsQoutes(replaceOutput))
+ {
+ replaceOutput=QString("\"")+replaceOutput+QString("\"");
+ }
+ config->writeEntry("regexp_search", replaceInput);
+ config->writeEntry("regexp_replace", replaceOutput);
+ }
+
+ KConfigDialogManager *widget;
+ for ( widget = encoderSettings.first(); widget; widget = encoderSettings.next() ){
+ widget->updateSettings();
+ }
+
+ config->sync();
+
+ configChanged = false;
+
+}
+
+void KAudiocdModule::load() {
+ load( false );
+}
+
+void KAudiocdModule::load(bool useDefaults) {
+
+ config->setReadDefaults( useDefaults );
+
+ {
+ KConfigGroupSaver saver(config, "CDDA");
+
+
+ // Specify <=> not autosearch, as explained above in ::save()
+ cd_specify_device->setChecked( !(config->readBoolEntry("autosearch",true)) );
+ cd_device_string->setText(config->readEntry("device","/dev/cdrom"));
+ ec_enable_check->setChecked(!(config->readBoolEntry("disable_paranoia",false)));
+ ec_skip_check->setChecked(!(config->readBoolEntry("never_skip",true)));
+ niceLevel->setValue(config->readNumEntry("niceLevel", 0));
+ }
+
+ {
+ KConfigGroupSaver saver(config, "FileName");
+ fileNameLineEdit->setText(config->readEntry("file_name_template", "%{albumartist} - %{number} - %{title}"));
+ albumNameLineEdit->setText(config->readEntry("album_name_template", "%{albumartist} - %{albumtitle}"));
+ kcfg_replaceInput->setText(config->readEntry("regexp_search"));
+ kcfg_replaceOutput->setText(config->readEntry("regexp_replace"));
+ example->setText(config->readEntry("example", i18n("Cool artist - example audio file.wav")));
+ }
+
+ KConfigDialogManager *widget;
+ for ( widget = encoderSettings.first(); widget; widget = encoderSettings.next() ){
+ widget->updateWidgets();
+ }
+
+ emit changed( useDefaults );
+}
+
+void KAudiocdModule::slotModuleChanged() {
+ KConfigDialogManager *widget;
+ for ( widget = encoderSettings.first(); widget; widget = encoderSettings.next() ){
+ if(widget->hasChanged()){
+ slotConfigChanged();
+ break;
+ }
+ }
+}
+
+void KAudiocdModule::slotConfigChanged() {
+ configChanged = true;
+ emit changed(true);
+}
+
+/*
+# slot for the error correction settings
+*/
+void KAudiocdModule::slotEcEnable() {
+ if (!(ec_skip_check->isChecked())) {
+ ec_skip_check->setChecked(true);
+ } else {
+ if (ec_skip_check->isEnabled()) {
+ ec_skip_check->setChecked(false);
+ }
+ }
+
+ slotConfigChanged();
+}
+
+QString KAudiocdModule::quickHelp() const
+{
+ return i18n("<h1>Audio CDs</h1> The Audio CD IO-Slave enables you to easily"
+ " create wav, MP3 or Ogg Vorbis files from your audio CD-ROMs or DVDs."
+ " The slave is invoked by typing <i>\"audiocd:/\"</i> in Konqueror's location"
+ " bar. In this module, you can configure"
+ " encoding, and device settings. Note that MP3 and Ogg"
+ " Vorbis encoding are only available if KDE was built with a recent"
+ " version of the LAME or Ogg Vorbis libraries.");
+}
+
+extern "C"
+{
+ KCModule *create_audiocd(QWidget *parent, const char */*name*/)
+ {
+ return new KAudiocdModule(parent, "kcmaudiocd");
+ }
+
+}
diff --git a/kioslave/audiocd/kcmaudiocd/kcmaudiocd.h b/kioslave/audiocd/kcmaudiocd/kcmaudiocd.h
new file mode 100644
index 00000000..7c322839
--- /dev/null
+++ b/kioslave/audiocd/kcmaudiocd/kcmaudiocd.h
@@ -0,0 +1,65 @@
+/*
+
+ Copyright (C) 2001 Carsten Duvenhorst <duvenhorst@m2.uni-hannover.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Permission is also granted to link this program with the Qt
+ library, treating Qt like a library that normally accompanies the
+ operating system kernel, whether or not that is in fact the case.
+
+*/
+
+
+#ifndef KAUDIOCDCONFIG_H
+#define KAUDIOCDCONFIG_H
+
+class KConfigDialogManager;
+
+#include "audiocdconfig.h"
+class KAudiocdModule : public AudiocdConfig
+{
+ Q_OBJECT
+
+public:
+
+ KAudiocdModule(QWidget *parent=0, const char *name=0);
+ ~KAudiocdModule();
+
+ QString quickHelp() const;
+
+public slots:
+ void defaults();
+ void save();
+ void load();
+ void load(bool useDefaults);
+
+private slots:
+ void updateExample();
+ void slotConfigChanged();
+ void slotEcEnable();
+ void slotModuleChanged();
+
+private:
+ KConfig *config;
+ bool configChanged;
+
+ int getBitrateIndex(int value);
+
+ QPtrList<KConfigDialogManager> encoderSettings;
+};
+
+#endif // KAUDIOCDCONFIG_H
+
diff --git a/kioslave/audiocd/plugins/Makefile.am b/kioslave/audiocd/plugins/Makefile.am
new file mode 100644
index 00000000..2dd4589c
--- /dev/null
+++ b/kioslave/audiocd/plugins/Makefile.am
@@ -0,0 +1,21 @@
+#if HAVE_VORBIS
+#AUDIOCD_PLUGINS_SUBDIR=vorbis
+#endif
+
+SUBDIRS = . wav vorbis lame flac
+
+INCLUDES = -I$(top_srcdir)/libkcddb $(all_includes)
+
+lib_LTLIBRARIES = libaudiocdplugins.la
+
+libaudiocdplugins_la_LIBADD = $(LIB_KDECORE) $(top_builddir)/libkcddb/libkcddb.la
+
+libaudiocdplugins_la_LDFLAGS = $(all_libraries) -version-info 1:0:0 -no-undefined
+
+libaudiocdplugins_la_SOURCES = audiocdencoder.cpp
+
+include_HEADERS = audiocdencoder.h
+
+messages:
+ $(XGETTEXT) *.cpp -o $(podir)/kio_audiocd.pot
+
diff --git a/kioslave/audiocd/plugins/audiocdencoder.cpp b/kioslave/audiocd/plugins/audiocdencoder.cpp
new file mode 100644
index 00000000..2a18322a
--- /dev/null
+++ b/kioslave/audiocd/plugins/audiocdencoder.cpp
@@ -0,0 +1,95 @@
+/*
+ Copyright (C) 2004, 2005 Benjamin Meyer <ben at meyerhome dot net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <audiocdencoder.h>
+#include <klibloader.h>
+#include <kdebug.h>
+#include <qdir.h>
+#include <qregexp.h>
+#include <kstandarddirs.h>
+
+/**
+ * Attempt to load a plugin and see if it has the symbol create_audiocd_encoders.
+ * @param libFileName file to try to load.
+ * @returns pointer to the symbol or NULL
+ */
+void *loadPlugin(const QString &libFileName)
+{
+#ifdef DEBUG
+ kdDebug(7117) << "Trying to load library. File: \"" << libFileName.latin1() << "\"." << endl;
+#endif
+ KLibLoader *loader = KLibLoader::self();
+ if (!loader)
+ return NULL;
+#ifdef DEBUG
+ kdDebug(7117) << "We have a loader. File: \"" << libFileName.latin1() << "\"." << endl;
+#endif
+ KLibrary *lib = loader->library(libFileName.latin1());
+ if (!lib)
+ return NULL;
+#ifdef DEBUG
+ kdDebug(7117) << "We have a library. File: \"" << libFileName.latin1() << "\"." << endl;
+#endif
+ void *cplugin = lib->symbol("create_audiocd_encoders");
+ if (!cplugin)
+ return NULL;
+#ifdef DEBUG
+ kdDebug(7117) << "We have a plugin. File: \"" << libFileName.latin1() << "\"." << endl;
+#endif
+ return cplugin;
+}
+
+/**
+ * There might be a "better" way of doing this, but I don't know it,
+ * but I do know that this does work. :) Feel free to improve the loading system,
+ * there isn't much code anyway.
+ */
+void AudioCDEncoder::findAllPlugins(KIO::SlaveBase *slave, QPtrList<AudioCDEncoder> &encoders){
+ QString foundEncoders;
+
+ KStandardDirs standardDirs;
+ QStringList dirs = standardDirs.findDirs("module", "");
+ for (QStringList::Iterator it = dirs.begin(); it != dirs.end(); ++it) {
+ QDir dir(*it);
+ if (!dir.exists()) {
+ kdDebug(7117) << "Directory given by KStandardDirs: " << dir.path() << " doesn't exists!" << endl;
+ continue;
+ }
+ dir.setFilter(QDir::Files | QDir::Hidden);
+
+ QStringList list = dir.entryList( "libaudiocd_encoder_*.so");
+ kdDebug() << "list " << list << endl;
+ for (QStringList::ConstIterator it2 = list.begin(); it2 != list.end(); ++it2)
+ {
+ QString fileName = *it2;
+ kdDebug() << fileName << endl;
+ if (foundEncoders.contains(fileName)) {
+ kdDebug(7117) << "Warning, encoder has been found twice!" << endl;
+ continue;
+ }
+ foundEncoders.append(fileName);
+ fileName = fileName.mid(0, fileName.find('.'));
+ void *function = loadPlugin(fileName);
+ if(function){
+ void (*functionPointer)(KIO::SlaveBase *, QPtrList<AudioCDEncoder> &) = (void (*)(KIO::SlaveBase *slave, QPtrList<AudioCDEncoder> &encoders)) function;
+ functionPointer(slave, encoders);
+ }
+ }
+ }
+}
+
diff --git a/kioslave/audiocd/plugins/audiocdencoder.h b/kioslave/audiocd/plugins/audiocdencoder.h
new file mode 100644
index 00000000..089be080
--- /dev/null
+++ b/kioslave/audiocd/plugins/audiocdencoder.h
@@ -0,0 +1,145 @@
+/*
+ Copyright (C) 2004, 2005 Benjamin Meyer <ben at meyerhome dot net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef AUDIOCD_ENCODER_H
+#define AUDIOCD_ENCODER_H
+
+#include <sys/types.h>
+#include <kio/slavebase.h>
+#include <cdinfo.h>
+
+class KConfigSkeleton;
+using namespace KCDDB;
+
+class AudioCDEncoder {
+
+public:
+ /**
+ * Constructor.
+ * @param slave parent that this classes can use to call data() with
+ * when finished encoding bits.
+ */
+ explicit AudioCDEncoder(KIO::SlaveBase *slave) : ioslave(slave) {};
+
+ /**
+ * Deconstructor.
+ */
+ virtual ~AudioCDEncoder(){};
+
+ /**
+ * Initiallizes the decoder, loading libraries, etc. Encoders
+ * that don't return true will will deleted and not used.
+ * @returns false if unable to initialize the encoder.
+ */
+ virtual bool init() = 0;
+
+ /**
+ * The encoder should read in its config data here.
+ */
+ virtual void loadSettings() = 0;
+
+ /**
+ * Helper function to determine the end size of a
+ * encoded file.
+ * @param time_secs the lengh of the audio track in seconds.
+ * @returns the size of a file if it is time_secs in length.
+ */
+ virtual unsigned long size(long time_secs) const = 0;
+
+ /**
+ * @returns the generic user string type/name of this encoder
+ * Examples: "MP3", "Ogg Vorbis", "Wav", "FID Level 2", etc
+ */
+ virtual QString type() const = 0;
+
+ /**
+ * @returns the mime type for the files this encoder produces.
+ * Example: "audio/x-wav"
+ */
+ virtual const char * mimeType() const = 0;
+
+ /**
+ * @returns the file type for the files this encoder produces.
+ * Used in naming of the file for example foo.mp3
+ * Examples: "mp3", "ogg", "wav"
+ */
+ virtual const char * fileType() const = 0;
+
+ /**
+ * Before the read functions are called this is
+ * called to allow the encoders to store the cddb
+ * information if they want to so it can be inserted
+ * where neccessary (start, middle, end, or combos etc).
+ */
+ virtual void fillSongInfo( KCDDB::CDInfo info, int track, const QString &comment ) = 0;
+
+ /**
+ * Perform any initial file creation necessary for a new song (that
+ * has just been sent via fillSongInfo())
+ * @param size - the total binary size of the end file (via size()).
+ * @return size of the data that was created by this function.
+ */
+ virtual long readInit(long size) = 0;
+
+ /**
+ * Passes a little bit of cd data to be encoded
+ * This function is most likly called many many times.
+ * @param buf pointer to the audio that has been read in so far
+ * @param frames the number of frames of audio that are in buf
+ * @return size of the data that was created by this function, -1 on error.
+ */
+ virtual long read(int16_t * buf, int frames) = 0;
+
+ /**
+ * Perform any final file creation/padding that is necessary
+ * @return size of the data that was created by this function.
+ */
+ virtual long readCleanup() = 0;
+
+ /**
+ * Returns a configure widget for the encoder
+ */
+ virtual QWidget* getConfigureWidget(KConfigSkeleton** manager) const
+ { Q_UNUSED(manager); return NULL; };
+
+ /**
+ * Returns the last error message; called when e.g. read() returns -1.
+ */
+ virtual QString lastErrorMessage() const { return QString::null; }
+
+ /**
+ * Helper function to load all of the AudioCD Encoders from libraries.
+ * Uses KStandardDirs to find where libraries could be, opens all of the ones
+ * that we might own audiocd_encoder_* and then uses the symbol
+ * create_audiocd_encoders to obtain the encoders from that library.
+ * @param slave ioslave needed if the plugin is going to be used to encode something.
+ * @param encoders container for new encoders.
+ */
+ static void findAllPlugins(KIO::SlaveBase *slave, QPtrList<AudioCDEncoder> &encoders);
+
+protected:
+ /**
+ * Pointer to the ioslave that is running this encoder.
+ * Used (only?) for the data() function to pass back encoded data.
+ */
+ KIO::SlaveBase *ioslave;
+
+};
+
+#endif // AUDIOCD_ENCODER_H
+
diff --git a/kioslave/audiocd/plugins/flac/Makefile.am b/kioslave/audiocd/plugins/flac/Makefile.am
new file mode 100644
index 00000000..4e693c53
--- /dev/null
+++ b/kioslave/audiocd/plugins/flac/Makefile.am
@@ -0,0 +1,13 @@
+INCLUDES = -I$(top_srcdir)/libkcddb -I$(srcdir)/.. $(all_includes)
+
+kde_module_LTLIBRARIES = libaudiocd_encoder_flac.la
+
+libaudiocd_encoder_flac_la_SOURCES = encoderflac.cpp
+
+libaudiocd_encoder_flac_la_LIBADD = $(LIBFLAC) $(LIB_KIO) ../libaudiocdplugins.la
+
+libaudiocd_encoder_flac_la_LDFLAGS = -avoid-version -module -no-undefined $(all_libraries)
+
+pluginsdir = $(kde_datadir)/audiocd/plugins
+
+METASOURCES = AUTO
diff --git a/kioslave/audiocd/plugins/flac/encoderflac.cpp b/kioslave/audiocd/plugins/flac/encoderflac.cpp
new file mode 100644
index 00000000..ece58708
--- /dev/null
+++ b/kioslave/audiocd/plugins/flac/encoderflac.cpp
@@ -0,0 +1,197 @@
+/*
+ Copyright (C) 2004 Allan Sandfeld Jensen <kde@carewolf.com>
+ Copyright (C) 2005 Benjamin Meyer <ben at meyerhome dot net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "encoderflac.h"
+
+#ifdef HAVE_LIBFLAC
+
+#include <FLAC/format.h>
+#include <FLAC/metadata.h>
+#include <FLAC/stream_encoder.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+
+
+extern "C"
+{
+ KDE_EXPORT void create_audiocd_encoders(KIO::SlaveBase *slave, QPtrList<AudioCDEncoder> &encoders)
+ {
+ encoders.append(new EncoderFLAC(slave));
+ }
+}
+
+class EncoderFLAC::Private {
+
+public:
+ FLAC__StreamEncoder *encoder;
+ FLAC__StreamMetadata** metadata;
+ KIO::SlaveBase* ioslave;
+ unsigned long data;
+};
+
+static FLAC__StreamEncoderWriteStatus WriteCallback(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data)
+{
+ EncoderFLAC::Private *d = (EncoderFLAC::Private*)client_data;
+
+ d->data += bytes;
+
+ QByteArray output;
+
+ if (bytes) {
+ output.setRawData((const char*)buffer, bytes);
+ d->ioslave->data(output);
+ output.resetRawData((const char*)buffer, bytes);
+ }
+
+ return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
+}
+
+static void MetadataCallback (const FLAC__StreamEncoder *encoder, const FLAC__StreamMetadata *metadata, void *client_data)
+{
+ // We do not have seekable writeback so we just discard the updated metadata
+
+}
+
+/*
+static FLAC__SeekableStreamEncoderSeekStatus SeekCallback(const FLAC__SeekableStreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data)
+{} ; */
+
+
+
+
+EncoderFLAC::EncoderFLAC(KIO::SlaveBase *slave) : AudioCDEncoder(slave) {
+ d = new Private();
+ d->ioslave = slave;
+ d->encoder = 0;
+}
+
+EncoderFLAC::~EncoderFLAC() {
+ if (d->encoder) FLAC__stream_encoder_delete(d->encoder);
+ delete d;
+}
+
+bool EncoderFLAC::init(){
+ d->encoder = FLAC__stream_encoder_new();
+ d->metadata = 0;
+ d->data = 0;
+ return true;
+}
+
+void EncoderFLAC::loadSettings() {
+// config->setGroup("FLAC");
+
+}
+
+// Estimate size to be 5/8 of uncompresed size.
+unsigned long EncoderFLAC::size(long time_secs) const {
+ long uncompressed = (time_secs * (44100*2*2));
+ return (uncompressed/8)*5 + 1000;
+}
+
+long EncoderFLAC::readInit(long size) {
+ kdDebug(7117) << "EncoderFLAC::readInit() called"<< endl;
+ d->data = 0;
+ FLAC__stream_encoder_set_write_callback(d->encoder, WriteCallback);
+ FLAC__stream_encoder_set_metadata_callback(d->encoder, MetadataCallback);
+ FLAC__stream_encoder_set_client_data(d->encoder, d);
+
+ // The options match approximely those of flac compression-level-3
+ FLAC__stream_encoder_set_do_mid_side_stereo(d->encoder, true);
+ FLAC__stream_encoder_set_loose_mid_side_stereo(d->encoder, true); // flac -M
+ FLAC__stream_encoder_set_max_lpc_order(d->encoder, 6); // flac -l6
+ FLAC__stream_encoder_set_min_residual_partition_order(d->encoder, 3);
+ FLAC__stream_encoder_set_max_residual_partition_order(d->encoder, 3); // flac -r3,3
+ FLAC__stream_encoder_set_blocksize(d->encoder, 4608);
+ FLAC__stream_encoder_set_streamable_subset(d->encoder, true);
+ if (size > 0)
+ FLAC__stream_encoder_set_total_samples_estimate(d->encoder, size/4);
+
+ FLAC__stream_encoder_init(d->encoder);
+ return d->data;
+}
+
+long EncoderFLAC::read(int16_t * buf, int frames)
+{
+ unsigned long olddata = d->data;
+ FLAC__int32 *buffer = new FLAC__int32[frames*2];
+ for(int i=0; i<frames*2;i++) {
+ buffer[i] = (FLAC__int32)buf[i];
+ }
+
+ bool res = FLAC__stream_encoder_process_interleaved (d->encoder, buffer, frames);
+ delete[] buffer;
+ return d->data - olddata;
+}
+
+long EncoderFLAC::readCleanup()
+{
+ FLAC__stream_encoder_finish(d->encoder);
+// FLAC__stream_encoder_delete(d->encoder);
+ if (d->metadata) {
+ FLAC__metadata_object_delete(d->metadata[0]);
+ delete[] d->metadata;
+ d->metadata = 0;
+ }
+ return 0;
+}
+
+void EncoderFLAC::fillSongInfo( KCDDB::CDInfo info, int track, const QString &comment )
+{
+ d->metadata = new FLAC__StreamMetadata*[1];
+ d->metadata[0] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
+// d->metadata[1] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
+// d->metadata[2] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_SEEKTABLE)
+
+ typedef QPair<QString, QVariant> Comment;
+ Comment comments[7] = { Comment("Title", info.trackInfoList[track].get("title")),
+ Comment("Artist", info.get("artist")),
+ Comment("Album", info.get("title")),
+ Comment("Genre", info.get("genre")),
+ Comment("Tracknumber", QString::number(track+1)),
+ Comment("Comment", comment),
+ Comment("Date", QString::null )};
+ if (info.get("Year").toInt() > 0) {
+ QDateTime dt(QDate(info.get("Year").toInt(), 1, 1));
+ comments[6] = Comment("Date", dt.toString(Qt::ISODate));
+ }
+
+ FLAC__StreamMetadata_VorbisComment_Entry entry;
+ QString field;
+ QCString cfield;
+ int num_comments = 0;
+
+ for(int i=0; i<7; i++) {
+ if (!comments[i].second.toString().isEmpty()) {
+ field = comments[i].first+"="+comments[i].second.toString();
+ cfield = field.utf8();
+ entry.entry = (FLAC__byte*)qstrdup(cfield);
+ entry.length = cfield.length();
+ // Insert in vorbiscomment and assign ownership of pointers to FLAC
+ FLAC__metadata_object_vorbiscomment_insert_comment(d->metadata[0], num_comments, entry, false);
+ num_comments++;
+ }
+ }
+
+ FLAC__stream_encoder_set_metadata(d->encoder, d->metadata, 1);
+}
+
+#endif // HAVE_LIBFLAC
+
diff --git a/kioslave/audiocd/plugins/flac/encoderflac.h b/kioslave/audiocd/plugins/flac/encoderflac.h
new file mode 100644
index 00000000..f933e809
--- /dev/null
+++ b/kioslave/audiocd/plugins/flac/encoderflac.h
@@ -0,0 +1,61 @@
+/*
+ Copyright (C) 2004 Allan Sandfeld Jensen <kde@carewolf.com>
+ Copyright (C) 2005 Benjamin Meyer <ben at meyerhome dot net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef ENCODER_FLAC_H
+#define ENCODER_FLAC_H
+
+#include <config.h>
+
+#ifdef HAVE_LIBFLAC
+
+#include <audiocdencoder.h>
+
+/**
+ * FLAC encoder.
+ * This encoder is only enabled when HAVE_LIBFLAC is set.
+ * Check out http://flac.sourceforge.net/ for more information.
+ */
+class EncoderFLAC : public AudioCDEncoder {
+
+public:
+ EncoderFLAC(KIO::SlaveBase *slave);
+ ~EncoderFLAC();
+
+ virtual QString type() const { return "FLAC"; };
+ virtual bool init();
+ virtual void loadSettings();
+ virtual unsigned long size(long time_secs) const;
+ virtual const char * fileType() const { return "flac"; };
+ virtual const char * mimeType() const { return "audio/x-flac"; }
+ virtual void fillSongInfo( KCDDB::CDInfo info, int track, const QString &comment );
+ virtual long readInit(long size);
+ virtual long read(int16_t * buf, int frames);
+ virtual long readCleanup();
+
+ class Private;
+private:
+ Private * d;
+
+};
+
+#endif // HAVE_FLAC
+
+#endif // ENCODER_FLAC_H
+
diff --git a/kioslave/audiocd/plugins/lame/Makefile.am b/kioslave/audiocd/plugins/lame/Makefile.am
new file mode 100644
index 00000000..49ddddd9
--- /dev/null
+++ b/kioslave/audiocd/plugins/lame/Makefile.am
@@ -0,0 +1,18 @@
+INCLUDES = -I$(top_srcdir)/libkcddb -I$(srcdir)/.. $(taglib_includes) $(all_includes)
+
+kde_kcfg_DATA = audiocd_lame_encoder.kcfg
+
+kde_module_LTLIBRARIES = libaudiocd_encoder_lame.la
+
+libaudiocd_encoder_lame_la_SOURCES = audiocd_lame_encoder.kcfgc encoderlame.cpp encoderlameconfig.ui collectingprocess.cpp
+
+libaudiocd_encoder_lame_la_LIBADD = $(LIB_KIO) ../libaudiocdplugins.la
+
+libaudiocd_encoder_lame_la_LDFLAGS = -avoid-version -module -no-undefined $(taglib_libs) $(all_libraries)
+
+pluginsdir = $(kde_datadir)/audiocd/plugins
+
+METASOURCES = AUTO
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/audiocd_encoder_lame.pot
diff --git a/kioslave/audiocd/plugins/lame/audiocd_lame_encoder.kcfg b/kioslave/audiocd/plugins/lame/audiocd_lame_encoder.kcfg
new file mode 100644
index 00000000..c752fd81
--- /dev/null
+++ b/kioslave/audiocd/plugins/lame/audiocd_lame_encoder.kcfg
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+ <kcfgfile name="kcmaudiocd_encoder_lame_rc"/>
+ <group name="Lame">
+
+<!-- Encoding Method -->
+ <entry name="bitrate_constant" type="Bool">
+ <label>Constant Bitrate</label>
+ <default>false</default>
+ </entry>
+ <entry name="bitrate_variable" type="Bool">
+ <label>Variable Bitrate</label>
+ <default>true</default>
+ </entry>
+
+ <entry name="stereo" type="Int">
+ <label>Stereo Mode</label>
+ <default>0</default>
+ </entry>
+ <entry name="quality" type="Int">
+ <label>Quality</label>
+ <default>-2</default>
+ </entry>
+
+<!-- Options -->
+ <entry name="copyright" type="Bool">
+ <label>Copyrighted</label>
+ <default>false</default>
+ </entry>
+ <entry name="original" type="Bool">
+ <label>Original</label>
+ <default>true</default>
+ </entry>
+ <entry name="iso" type="Bool">
+ <label>ISO Encoding</label>
+ <default>false</default>
+ </entry>
+ <entry name="crc" type="Bool">
+ <label>Error protection</label>
+ <default>false</default>
+ </entry>
+ <entry name="id3_tag" type="Bool">
+ <label>Write ID3 Tag</label>
+ <default>true</default>
+ </entry>
+
+<!-- Constant Bitrate Settings -->
+ <entry name="cbr_bitrate" type="Int">
+ <label>Constant Bitrate Settings</label>
+ <default>10</default>
+ <min>0</min>
+ <max>13</max>
+ </entry>
+
+<!-- Variable Bitrate Settings -->
+ <entry name="vbr_min_br" type="Bool">
+ <label>Minimal bitrate</label>
+ <default>false</default>
+ </entry>
+ <entry name="vbr_min_hard" type="Bool">
+ <label>Minimal Value is a hard limit</label>
+ <default>false</default>
+ </entry>
+ <entry name="vbr_max_br" type="Bool">
+ <label>Maximal bitrate</label>
+ <default>false</default>
+ </entry>
+ <entry name="vbr_average_br" type="Bool">
+ <label>Average bitrate</label>
+ <default>false</default>
+ </entry>
+ <entry name="vbr_xing_tag" type="Bool">
+ <label>Write Xing VBR tag</label>
+ <default>true</default>
+ </entry>
+
+ <entry name="vbr_min_brate" type="Int">
+ <label>Minimal bitrate value</label>
+ <default>40</default>
+ <min>0</min>
+ <max>13</max>
+ </entry>
+ <entry name="vbr_max_brate" type="Int">
+ <label>Maximal bitrate value</label>
+ <default>13</default>
+ <min>0</min>
+ <max>13</max>
+ </entry>
+ <entry name="vbr_mean_brate" type="Int">
+ <label>Average bitrate value</label>
+ <default>10</default>
+ <min>0</min>
+ <max>13</max>
+ </entry>
+
+
+<!-- Filter Settings -->
+ <entry name="enable_lowpass" type="Bool">
+ <label>Lowpass filter cutoff above</label>
+ <default>false</default>
+ </entry>
+ <entry name="lowfilterfreq" type="Int">
+ <label>Lowpass filter cutoff above Value</label>
+ <default>18000</default>
+ </entry>
+
+ <entry name="enable_highpass" type="Bool">
+ <label>Highpass filter cutoff above</label>
+ <default>false</default>
+ </entry>
+ <entry name="highfilterfreq" type="Int">
+ <label>Highpass filter cutoff above Value</label>
+ <default>0</default>
+ </entry>
+
+ <entry name="set_lpf_width" type="Bool">
+ <label>Lowpass filter width</label>
+ <default>false</default>
+ </entry>
+ <entry name="lowfilterwidth" type="Int">
+ <label>Lowpass filter width value</label>
+ <default>900</default>
+ </entry>
+
+ <entry name="set_hpf_width" type="Bool">
+ <label>Highpass filter width</label>
+ <default>false</default>
+ </entry>
+ <entry name="highfilterwidth" type="Int">
+ <label>Highpass filter width</label>
+ <default>0</default>
+ </entry>
+ </group>
+</kcfg>
+
diff --git a/kioslave/audiocd/plugins/lame/audiocd_lame_encoder.kcfgc b/kioslave/audiocd/plugins/lame/audiocd_lame_encoder.kcfgc
new file mode 100644
index 00000000..b96ee704
--- /dev/null
+++ b/kioslave/audiocd/plugins/lame/audiocd_lame_encoder.kcfgc
@@ -0,0 +1,4 @@
+# Code generation options for kconfig_compiler
+File=audiocd_lame_encoder.kcfg
+ClassName=Settings
+Singleton=true
diff --git a/kioslave/audiocd/plugins/lame/collectingprocess.cpp b/kioslave/audiocd/plugins/lame/collectingprocess.cpp
new file mode 100644
index 00000000..b3a13755
--- /dev/null
+++ b/kioslave/audiocd/plugins/lame/collectingprocess.cpp
@@ -0,0 +1,134 @@
+/*
+ collectingprocess.cpp
+
+ This file is part of libkdepim.
+ Copyright (c) 2004 Ingo Kloecker <kloecker@kde.org>
+
+ This library 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of this program with any edition of
+ the Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), and distribute linked
+ combinations including the two. You must obey the GNU General
+ Public License in all respects for all of the code used other than
+ Qt. If you modify this file, you may extend this exception to
+ your version of the file, but you are not obligated to do so. If
+ you do not wish to do so, delete this exception statement from
+ your version.
+*/
+
+#include "collectingprocess.h"
+
+#include <qvaluelist.h>
+
+#include <string.h>
+
+struct CollectingProcess::Private {
+ Private() : stdoutSize( 0 ), stderrSize( 0 )
+ {}
+
+ uint stdoutSize;
+ QValueList<QByteArray> stdoutBuffer;
+ uint stderrSize;
+ QValueList<QByteArray> stderrBuffer;
+};
+
+
+CollectingProcess::CollectingProcess( QObject * parent, const char * name )
+ : KProcess( parent, name )
+{
+ d = new Private();
+}
+
+CollectingProcess::~CollectingProcess() {
+ delete d; d = 0;
+}
+
+bool CollectingProcess::start( RunMode runmode, Communication comm ) {
+ // prevent duplicate connection
+ disconnect( this, SIGNAL( receivedStdout( KProcess *, char *, int ) ),
+ this, SLOT( slotReceivedStdout( KProcess *, char *, int ) ) );
+ if ( comm & Stdout ) {
+ connect( this, SIGNAL( receivedStdout( KProcess *, char *, int ) ),
+ this, SLOT( slotReceivedStdout( KProcess *, char *, int ) ) );
+ }
+ // prevent duplicate connection
+ disconnect( this, SIGNAL( receivedStderr( KProcess *, char *, int ) ),
+ this, SLOT( slotReceivedStderr( KProcess *, char *, int ) ) );
+ if ( comm & Stderr ) {
+ connect( this, SIGNAL( receivedStderr( KProcess *, char *, int ) ),
+ this, SLOT( slotReceivedStderr( KProcess *, char *, int ) ) );
+ }
+ return KProcess::start( runmode, comm );
+}
+
+void CollectingProcess::slotReceivedStdout( KProcess *, char *buf, int len )
+{
+ QByteArray b;
+ b.duplicate( buf, len );
+ d->stdoutBuffer.append( b );
+ d->stdoutSize += len;
+}
+
+void CollectingProcess::slotReceivedStderr( KProcess *, char *buf, int len )
+{
+ QByteArray b;
+ b.duplicate( buf, len );
+ d->stderrBuffer.append( b );
+ d->stderrSize += len;
+}
+
+QByteArray CollectingProcess::collectedStdout()
+{
+ if ( d->stdoutSize == 0 ) {
+ return QByteArray();
+ }
+
+ uint offset = 0;
+ QByteArray b( d->stdoutSize );
+ for ( QValueList<QByteArray>::const_iterator it = d->stdoutBuffer.begin();
+ it != d->stdoutBuffer.end();
+ ++it ) {
+ memcpy( b.data() + offset, (*it).data(), (*it).size() );
+ offset += (*it).size();
+ }
+ d->stdoutBuffer.clear();
+ d->stdoutSize = 0;
+
+ return b;
+}
+
+QByteArray CollectingProcess::collectedStderr()
+{
+ if ( d->stderrSize == 0 ) {
+ return QByteArray();
+ }
+
+ uint offset = 0;
+ QByteArray b( d->stderrSize );
+ for ( QValueList<QByteArray>::const_iterator it = d->stderrBuffer.begin();
+ it != d->stderrBuffer.end();
+ ++it ) {
+ memcpy( b.data() + offset, (*it).data(), (*it).size() );
+ offset += (*it).size();
+ }
+ d->stderrBuffer.clear();
+ d->stderrSize = 0;
+
+ return b;
+}
+
+#include "collectingprocess.moc"
diff --git a/kioslave/audiocd/plugins/lame/collectingprocess.h b/kioslave/audiocd/plugins/lame/collectingprocess.h
new file mode 100644
index 00000000..7549fee3
--- /dev/null
+++ b/kioslave/audiocd/plugins/lame/collectingprocess.h
@@ -0,0 +1,72 @@
+/* -*- mode: C++ -*-
+ collectingprocess.h
+
+ This file is a copy of the collectingprocess.h which is part of kdepim/libkdepim.
+ Copyright (c) 2004 Ingo Kloecker <kloecker@kde.org>
+
+ This library 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of this program with any edition of
+ the Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), and distribute linked
+ combinations including the two. You must obey the GNU General
+ Public License in all respects for all of the code used other than
+ Qt. If you modify this file, you may extend this exception to
+ your version of the file, but you are not obligated to do so. If
+ you do not wish to do so, delete this exception statement from
+ your version.
+*/
+
+#ifndef __COLLECTINGPROCESS_H__
+#define __COLLECTINGPROCESS_H__
+
+#include <kprocess.h>
+
+/**
+ * @short An output collecting KProcess class.
+ *
+ * This class simplifies the usage of KProcess by collecting all output
+ * (stdout/stderr) of the process.
+ *
+ * @author Ingo Kloecker <kloecker@kde.org>
+ */
+class CollectingProcess : public KProcess {
+ Q_OBJECT
+public:
+ CollectingProcess( QObject * parent = 0, const char * name = 0 );
+ ~CollectingProcess();
+
+ /** Starts the process in NotifyOnExit mode and writes in to stdin of
+ the process.
+ */
+ bool start( RunMode runmode, Communication comm );
+
+ /** Returns the contents of the stdout buffer and clears it afterwards. */
+ QByteArray collectedStdout();
+ /** Returns the contents of the stderr buffer and clears it afterwards. */
+ QByteArray collectedStderr();
+
+private slots:
+ void slotReceivedStdout( KProcess *, char *, int );
+ void slotReceivedStderr( KProcess *, char *, int );
+
+private:
+ class Private;
+ Private * d;
+protected:
+};
+
+#endif // __COLLECTINGPROCESS_H__
diff --git a/kioslave/audiocd/plugins/lame/encoderlame.cpp b/kioslave/audiocd/plugins/lame/encoderlame.cpp
new file mode 100644
index 00000000..a00c60fd
--- /dev/null
+++ b/kioslave/audiocd/plugins/lame/encoderlame.cpp
@@ -0,0 +1,366 @@
+/*
+ Copyright (C) 2005 Benjamin Meyer <ben at meyerhome dot net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <config.h>
+#include <endian.h>
+#include "encoderlame.h"
+#include "encoderlameconfig.h"
+#include "audiocd_lame_encoder.h"
+
+#include <kdebug.h>
+#include <qgroupbox.h>
+#include <kprocess.h>
+#include <kdebug.h>
+
+#include <kglobal.h>
+#include <klocale.h>
+#include <kapplication.h>
+#include <qfileinfo.h>
+#include <ktempfile.h>
+#include <kstandarddirs.h>
+#include "collectingprocess.h"
+
+extern "C"
+{
+ KDE_EXPORT void create_audiocd_encoders(KIO::SlaveBase *slave, QPtrList<AudioCDEncoder> &encoders) {
+ encoders.append(new EncoderLame(slave));
+ }
+}
+
+static int bitrates[] = { 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 };
+
+class EncoderLame::Private
+{
+public:
+ int bitrate;
+ bool waitingForWrite;
+ bool processHasExited;
+ QString lastErrorMessage;
+ QStringList genreList;
+ uint lastSize;
+ KProcess *currentEncodeProcess;
+ KTempFile *tempFile;
+};
+
+EncoderLame::EncoderLame(KIO::SlaveBase *slave) : QObject(), AudioCDEncoder(slave) {
+ d = new Private();
+ d->waitingForWrite = false;
+ d->processHasExited = false;
+ d->lastSize = 0;
+ loadSettings();
+}
+
+EncoderLame::~EncoderLame(){
+ delete d;
+}
+
+QWidget* EncoderLame::getConfigureWidget(KConfigSkeleton** manager) const {
+ (*manager) = Settings::self();
+ KGlobal::locale()->insertCatalogue("audiocd_encoder_lame");
+ EncoderLameConfig *config = new EncoderLameConfig();
+ config->cbr_settings->hide();
+ return config;
+}
+
+bool EncoderLame::init(){
+ // Determine if lame is installed on the system or not.
+ if ( KStandardDirs::findExe( "lame" ).isEmpty() )
+ return false;
+
+ // Ask lame for the list of genres it knows; otherwise it barfs when doing
+ // e.g. lame --tg 'Vocal Jazz'
+ CollectingProcess proc;
+ proc << "lame" << "--genre-list";
+ proc.start(KProcess::Block, KProcess::Stdout);
+
+ if(proc.exitStatus() != 0)
+ return false;
+
+ const QByteArray data = proc.collectedStdout();
+ QString str;
+ if ( !data.isEmpty() )
+ str = QString::fromLocal8Bit( data, data.size() );
+
+ d->genreList = QStringList::split( '\n', str );
+ // Remove the numbers in front of every genre
+ for( QStringList::Iterator it = d->genreList.begin(); it != d->genreList.end(); ++it ) {
+ QString& genre = *it;
+ uint i = 0;
+ while ( i < genre.length() && ( genre[i].isSpace() || genre[i].isDigit() ) )
+ ++i;
+ genre = genre.mid( i );
+
+ }
+ //kdDebug(7117) << "Available genres:" << d->genreList << endl;
+
+ return true;
+}
+
+void EncoderLame::loadSettings(){
+ // Generate the command line arguments for the current settings
+ args.clear();
+
+ Settings *settings = Settings::self();
+
+ int quality = settings->quality();
+ if (quality < 0 ) quality = quality *-1;
+ if (quality > 9) quality = 9;
+
+ int method = settings->bitrate_constant() ? 0 : 1 ;
+
+ if (method == 0) {
+ // Constant Bitrate Encoding
+ args.append("-b");
+ args.append(QString("%1").arg(bitrates[settings->cbr_bitrate()]));
+ d->bitrate = bitrates[settings->cbr_bitrate()];
+ args.append("-q");
+ args.append(QString("%1").arg(quality));
+ }
+ else {
+ // Variable Bitrate Encoding
+ if (settings->vbr_average_br()) {
+ args.append("--abr");
+ args.append(QString("%1").arg(bitrates[settings->vbr_mean_brate()]));
+ d->bitrate = bitrates[settings->vbr_mean_brate()];
+ if (settings->vbr_min_br()){
+ args.append("-b");
+ args.append(QString("%1").arg(bitrates[settings->vbr_min_brate()]));
+ }
+ if (settings->vbr_min_hard())
+ args.append("-F");
+ if (settings->vbr_max_br()){
+ args.append("-B");
+ args.append(QString("%1").arg(bitrates[settings->vbr_max_brate()]));
+ }
+ } else {
+ d->bitrate = 128;
+ args.append("-V");
+ args.append(QString("%1").arg(quality));
+ }
+ if ( !settings->vbr_xing_tag() )
+ args.append("-t");
+ }
+
+ args.append("-m");
+ switch ( settings->stereo() ) {
+ case 0:
+ args.append("s");
+ break;
+ case 1:
+ args.append("j");
+ break;
+ case 2:
+ args.append("d");
+ break;
+ case 3:
+ args.append("m");
+ break;
+ default:
+ args.append("s");
+ break;
+ }
+
+ if(settings->copyright())
+ args.append("-c");
+ if(!settings->original())
+ args.append("-o");
+ if(settings->iso())
+ args.append("--strictly-enforce-ISO");
+ if(settings->crc())
+ args.append("-p");
+
+ if ( settings->enable_lowpass() ) {
+ args.append("--lowpass");
+ args.append(QString("%1").arg(settings->lowfilterfreq()));
+
+ if (settings->set_lpf_width()){
+ args.append("--lowpass-width");
+ args.append(QString("%1").arg(settings->lowfilterwidth()));
+ }
+ }
+
+ if ( settings->enable_highpass()) {
+ args.append("--hipass");
+ args.append(QString("%1").arg(settings->highfilterfreq()));
+
+ if (settings->set_hpf_width()){
+ args.append("--hipass-width");
+ args.append(QString("%1").arg(settings->highfilterwidth()));
+ }
+ }
+}
+
+unsigned long EncoderLame::size(long time_secs) const {
+ return (time_secs * d->bitrate * 1000)/8;
+}
+
+long EncoderLame::readInit(long /*size*/){
+ // Create KProcess
+ d->currentEncodeProcess = new KProcess(0);
+ QString prefix = locateLocal("tmp", "");
+ d->tempFile = new KTempFile(prefix, ".mp3");
+ d->tempFile->setAutoDelete(true);
+ d->lastErrorMessage = QString::null;
+ d->processHasExited = false;
+
+ // -x bitswap
+ // -r raw/pcm
+ // -s 44.1 (because it is raw you have to specify this)
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ *(d->currentEncodeProcess) << "lame" << "--verbose" << "-x" << "-r" << "-s" << "44.1";
+#else
+ *(d->currentEncodeProcess) << "lame" << "--verbose" << "-r" << "-s" << "44.1";
+#endif
+
+ *(d->currentEncodeProcess) << args;
+ if(Settings::self()->id3_tag())
+ *d->currentEncodeProcess << trackInfo;
+
+ // Read in stdin, output to the temp file
+ *d->currentEncodeProcess << "-" << d->tempFile->name().latin1();
+
+ //kdDebug(7117) << d->currentEncodeProcess->args() << endl;
+
+
+ connect(d->currentEncodeProcess, SIGNAL(receivedStdout(KProcess *, char *, int)),
+ this, SLOT(receivedStdout(KProcess *, char *, int)));
+ connect(d->currentEncodeProcess, SIGNAL(receivedStderr(KProcess *, char *, int)),
+ this, SLOT(receivedStderr(KProcess *, char *, int)));
+ connect(d->currentEncodeProcess, SIGNAL(wroteStdin(KProcess *)),
+ this, SLOT(wroteStdin(KProcess *)));
+
+ connect(d->currentEncodeProcess, SIGNAL(processExited(KProcess *)),
+ this, SLOT(processExited(KProcess *)));
+
+ // Launch!
+ d->currentEncodeProcess->start(KProcess::NotifyOnExit, KShellProcess::All);
+ return 0;
+}
+
+void EncoderLame::processExited ( KProcess *process ){
+ kdDebug(7117) << "Lame Encoding process exited with: " << process->exitStatus() << endl;
+ d->processHasExited = true;
+}
+
+void EncoderLame::receivedStderr( KProcess * /*process*/, char *buffer, int /*buflen*/ ){
+ kdDebug(7117) << "Lame stderr: " << buffer << endl;
+ if ( !d->lastErrorMessage.isEmpty() )
+ d->lastErrorMessage += '\t';
+ d->lastErrorMessage += QString::fromLocal8Bit( buffer );
+}
+
+void EncoderLame::receivedStdout( KProcess * /*process*/, char *buffer, int /*length*/ ){
+ kdDebug(7117) << "Lame stdout: " << buffer << endl;
+}
+
+void EncoderLame::wroteStdin( KProcess * /*procces*/ ){
+ d->waitingForWrite = false;
+}
+
+long EncoderLame::read(int16_t *buf, int frames){
+ if(!d->currentEncodeProcess)
+ return 0;
+ if (d->processHasExited)
+ return -1;
+
+ // Pipe the raw data to lame
+ char * cbuf = reinterpret_cast<char *>(buf);
+ d->currentEncodeProcess->writeStdin( cbuf, frames*4);
+
+ // We can't return until the buffer has been written
+ d->waitingForWrite = true;
+ while(d->waitingForWrite && d->currentEncodeProcess->isRunning()){
+ kapp->processEvents();
+ usleep(1);
+ }
+
+ // Determine the file size increase
+ QFileInfo file(d->tempFile->name());
+ uint change = file.size() - d->lastSize;
+ d->lastSize = file.size();
+ return change;
+}
+
+long EncoderLame::readCleanup(){
+ if(!d->currentEncodeProcess)
+ return 0;
+
+ // Let lame tag the first frame of the mp3
+ d->currentEncodeProcess->closeStdin();
+ while( d->currentEncodeProcess->isRunning()){
+ kapp->processEvents();
+ usleep(1);
+ }
+
+ // Now copy the file out of the temp into kio
+ QFile file( d->tempFile->name() );
+ if ( file.open( IO_ReadOnly ) ) {
+ QByteArray output;
+ char data[1024];
+ while ( !file.atEnd() ) {
+ uint read = file.readBlock(data, 1024);
+ output.setRawData(data, read);
+ ioslave->data(output);
+ output.resetRawData(data, read);
+ }
+ file.close();
+ }
+
+ // cleanup the process and temp
+ delete d->currentEncodeProcess;
+ delete d->tempFile;
+ d->lastSize = 0;
+
+ return 0;
+}
+
+void EncoderLame::fillSongInfo( KCDDB::CDInfo info, int track, const QString &comment ){
+ trackInfo.clear();
+ trackInfo.append("--tt");
+ trackInfo.append(info.trackInfoList[track].get("title").toString());
+
+ trackInfo.append("--ta");
+ trackInfo.append(info.get("artist").toString());
+
+ trackInfo.append("--tl");
+ trackInfo.append(info.get("title").toString());
+
+ trackInfo.append("--ty");
+ trackInfo.append(QString("%1").arg(info.get("year").toString()));
+
+ trackInfo.append("--tc");
+ trackInfo.append(comment);
+
+ trackInfo.append("--tn");
+ trackInfo.append(QString("%1").arg(track+1));
+
+ const QString genre = info.get( "genre" ).toString();
+ if ( d->genreList.find( genre ) != d->genreList.end() )
+ {
+ trackInfo.append("--tg");
+ trackInfo.append(genre);
+ }
+}
+
+
+QString EncoderLame::lastErrorMessage() const
+{
+ return d->lastErrorMessage;
+}
+
+#include "encoderlame.moc"
diff --git a/kioslave/audiocd/plugins/lame/encoderlame.h b/kioslave/audiocd/plugins/lame/encoderlame.h
new file mode 100644
index 00000000..76aeb59b
--- /dev/null
+++ b/kioslave/audiocd/plugins/lame/encoderlame.h
@@ -0,0 +1,67 @@
+/*
+ Copyright (C) 2005 Benjamin Meyer <ben at meyerhome dot net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef ENCODER_LAME_H
+#define ENCODER_LAME_H
+
+#include "audiocdencoder.h"
+
+class KProcess;
+
+/**
+ * MP3 encoder using the LAME encoder.
+ * Go to http://lame.sourceforge.net/ for lots of information.
+ */
+class EncoderLame : public QObject, public AudioCDEncoder {
+
+Q_OBJECT
+
+public:
+ EncoderLame(KIO::SlaveBase *slave);
+ ~EncoderLame();
+
+ virtual QString type() const { return "MP3"; };
+ virtual bool init();
+ virtual void loadSettings();
+ virtual unsigned long size(long time_secs) const;
+ virtual const char * fileType() const { return "mp3"; };
+ virtual const char * mimeType() const { return "audio/x-mp3"; };
+ virtual void fillSongInfo( KCDDB::CDInfo info, int track, const QString &comment );
+ virtual long readInit(long size);
+ virtual long read(int16_t * buf, int frames);
+ virtual long readCleanup();
+ virtual QString lastErrorMessage() const;
+
+ virtual QWidget* getConfigureWidget(KConfigSkeleton** manager) const;
+
+protected slots:
+ void wroteStdin(KProcess *proc);
+ void receivedStdout(KProcess *, char *buffer, int length);
+ void receivedStderr(KProcess *proc, char *buffer, int buflen);
+ void processExited(KProcess *proc);
+
+private:
+ class Private;
+ Private * d;
+
+ QStringList args;
+ QStringList trackInfo;
+};
+
+#endif // ENCODER_LAME_H
+
diff --git a/kioslave/audiocd/plugins/lame/encoderlameconfig.ui b/kioslave/audiocd/plugins/lame/encoderlameconfig.ui
new file mode 100644
index 00000000..74060353
--- /dev/null
+++ b/kioslave/audiocd/plugins/lame/encoderlameconfig.ui
@@ -0,0 +1,930 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>EncoderLameConfig</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>LameConfig</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>471</width>
+ <height>598</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox" row="0" column="1">
+ <property name="name">
+ <cstring>GroupBox1_2</cstring>
+ </property>
+ <property name="title">
+ <string>Options</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_copyright</cstring>
+ </property>
+ <property name="text">
+ <string>Cop&amp;yrighted</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Mark MP3 file as copyrighted</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Mark MP3 file as copyrighted.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_original</cstring>
+ </property>
+ <property name="text">
+ <string>Origi&amp;nal</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Mark MP3 file as an original</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Mark MP3 file as an original.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_iso</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;ISO encoding</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Try to use strict ISO encoding</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This selects the maximal bitrate used for encoding.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_crc</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Error protection</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_id3_tag</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Write ID3 tag</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>If checked and if cddb support is available, an id3 tag will be appended</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If checked and if cddb support is available, an id3 tag will be appended</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QButtonGroup" row="0" column="0">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>Encoding Method</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="3" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>Layout21</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel3_2</cstring>
+ </property>
+ <property name="text">
+ <string>Low</string>
+ </property>
+ </widget>
+ <widget class="QSlider">
+ <property name="name">
+ <cstring>kcfg_quality</cstring>
+ </property>
+ <property name="minValue">
+ <number>-9</number>
+ </property>
+ <property name="maxValue">
+ <number>0</number>
+ </property>
+ <property name="pageStep">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>-7</number>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="tickmarks">
+ <enum>NoMarks</enum>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>High</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel" row="2" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>TextLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Quality:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignLeft</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_quality</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="1" column="0" rowspan="1" colspan="2">
+ <item>
+ <property name="text">
+ <string>Stereo</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joint Stereo</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Dual Channel</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Mono</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>kcfg_stereo</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This option controls whether MP3 files are recorded with one or two channels. Note that choosing &lt;i&gt;"Mono"&lt;/i&gt; reduces file size, but also kills the stereo signal.</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="0" column="0">
+ <property name="name">
+ <cstring>kcfg_bitrate_constant</cstring>
+ </property>
+ <property name="text">
+ <string>Constant bitrate</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="0" column="1">
+ <property name="name">
+ <cstring>kcfg_bitrate_variable</cstring>
+ </property>
+ <property name="text">
+ <string>Variable bitrate</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer row="4" column="0">
+ <property name="name">
+ <cstring>spacer11</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>vbr_settings</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>Variable Bitrate Settings</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox" row="3" column="0">
+ <property name="name">
+ <cstring>kcfg_vbr_average_br</cstring>
+ </property>
+ <property name="text">
+ <string>Specify avera&amp;ge bitrate:</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This selects the maximal bitrate used for encoding.</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="2" column="1">
+ <item>
+ <property name="text">
+ <string>32 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>40 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>48 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>56 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>64 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>80 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>96 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>112 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>128 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>160 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>192 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>224 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>256 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>320 kbs</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>kcfg_vbr_max_brate</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="currentItem">
+ <number>13</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>kcfg_vbr_max_br</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Maximal bi&amp;trate:</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This selects the maximal bitrate used for encoding.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_vbr_xing_tag</cstring>
+ </property>
+ <property name="text">
+ <string>Write &amp;Xing VBR tag</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This writes additional information related to VBR as introduced by Xing.</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="3" column="1">
+ <item>
+ <property name="text">
+ <string>32 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>40 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>48 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>56 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>64 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>80 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>96 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>112 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>128 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>160 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>192 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>224 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>256 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>320 kbs</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>kcfg_vbr_mean_brate</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="currentItem">
+ <number>9</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_vbr_min_hard</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Minimal &amp;value is a hard limit</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>kcfg_vbr_min_br</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Minimal &amp;bitrate:</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This selects the minimal bitrate used for encoding.</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="0" column="1">
+ <item>
+ <property name="text">
+ <string>32 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>40 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>48 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>56 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>64 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>80 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>96 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>112 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>128 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>160 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>192 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>224 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>256 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>320 kbs</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>kcfg_vbr_min_brate</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="currentItem">
+ <number>1</number>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>cbr_settings</cstring>
+ </property>
+ <property name="title">
+ <string>Constant Bitrate Settings</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel4_2</cstring>
+ </property>
+ <property name="text">
+ <string>Bitrate:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_cbr_bitrate</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>32 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>40 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>48 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>56 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>64 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>80 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>96 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>112 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>128 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>160 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>192 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>224 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>256 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>320 kbs</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>kcfg_cbr_bitrate</cstring>
+ </property>
+ <property name="currentItem">
+ <number>9</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The higher the bitrate, the better the quality and the larger the file.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>GroupBox83_2</cstring>
+ </property>
+ <property name="title">
+ <string>Filter Settings</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignLeft</set>
+ </property>
+ <property name="hAlign" stdset="0">
+ </property>
+ <property name="vAlign" stdset="0">
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>kcfg_enable_lowpass</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Lowpass filter cutoff above</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="1" column="0">
+ <property name="name">
+ <cstring>kcfg_lowfilterfreq</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="suffix">
+ <string> Hz</string>
+ </property>
+ <property name="maxValue">
+ <number>20000</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>kcfg_enable_highpass</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Highpass filter cutoff below</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="3" column="0">
+ <property name="name">
+ <cstring>kcfg_highfilterfreq</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="suffix">
+ <string> Hz</string>
+ </property>
+ <property name="maxValue">
+ <number>200</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="1">
+ <property name="name">
+ <cstring>kcfg_set_lpf_width</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Low&amp;pass filter width</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="1" column="1">
+ <property name="name">
+ <cstring>kcfg_lowfilterwidth</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="suffix">
+ <string> Hz</string>
+ </property>
+ <property name="maxValue">
+ <number>5000</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="1">
+ <property name="name">
+ <cstring>kcfg_set_hpf_width</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Highpa&amp;ss filter width</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="3" column="1">
+ <property name="name">
+ <cstring>kcfg_highfilterwidth</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="suffix">
+ <string> Hz</string>
+ </property>
+ <property name="maxValue">
+ <number>50</number>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>kcfg_bitrate_constant</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>cbr_settings</receiver>
+ <slot>setShown(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_enable_highpass</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_highfilterfreq</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_enable_highpass</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_highfilterwidth</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_enable_lowpass</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_lowfilterfreq</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_enable_lowpass</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_lowfilterwidth</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_enable_highpass</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_set_hpf_width</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_enable_lowpass</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_set_lpf_width</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_vbr_average_br</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_vbr_max_br</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_vbr_max_br</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_vbr_max_brate</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_vbr_average_br</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_vbr_mean_brate</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_vbr_average_br</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_vbr_min_br</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_vbr_min_br</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_vbr_min_brate</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_vbr_min_br</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_vbr_min_hard</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_bitrate_variable</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>vbr_settings</receiver>
+ <slot>setShown(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>kcfg_bitrate_variable</tabstop>
+ <tabstop>kcfg_stereo</tabstop>
+ <tabstop>kcfg_quality</tabstop>
+ <tabstop>kcfg_copyright</tabstop>
+ <tabstop>kcfg_original</tabstop>
+ <tabstop>kcfg_iso</tabstop>
+ <tabstop>kcfg_crc</tabstop>
+ <tabstop>kcfg_id3_tag</tabstop>
+ <tabstop>kcfg_cbr_bitrate</tabstop>
+ <tabstop>kcfg_vbr_min_br</tabstop>
+ <tabstop>kcfg_vbr_min_hard</tabstop>
+ <tabstop>kcfg_vbr_max_br</tabstop>
+ <tabstop>kcfg_vbr_average_br</tabstop>
+ <tabstop>kcfg_vbr_xing_tag</tabstop>
+ <tabstop>kcfg_vbr_min_brate</tabstop>
+ <tabstop>kcfg_vbr_max_brate</tabstop>
+ <tabstop>kcfg_vbr_mean_brate</tabstop>
+ <tabstop>kcfg_enable_lowpass</tabstop>
+ <tabstop>kcfg_lowfilterfreq</tabstop>
+ <tabstop>kcfg_enable_highpass</tabstop>
+ <tabstop>kcfg_highfilterfreq</tabstop>
+ <tabstop>kcfg_set_lpf_width</tabstop>
+ <tabstop>kcfg_lowfilterwidth</tabstop>
+ <tabstop>kcfg_set_hpf_width</tabstop>
+ <tabstop>kcfg_highfilterwidth</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kioslave/audiocd/plugins/vorbis/Makefile.am b/kioslave/audiocd/plugins/vorbis/Makefile.am
new file mode 100644
index 00000000..e83240ff
--- /dev/null
+++ b/kioslave/audiocd/plugins/vorbis/Makefile.am
@@ -0,0 +1,18 @@
+INCLUDES = -I$(top_srcdir)/libkcddb -I$(srcdir)/.. $(all_includes)
+
+kde_kcfg_DATA = audiocd_vorbis_encoder.kcfg
+
+kde_module_LTLIBRARIES = libaudiocd_encoder_vorbis.la
+
+libaudiocd_encoder_vorbis_la_SOURCES = audiocd_vorbis_encoder.kcfgc encodervorbis.cpp encodervorbisconfig.ui
+
+libaudiocd_encoder_vorbis_la_LIBADD = $(VORBIS_LIBS) $(VORBISFILE_LIBS) $(VORBISENC_LIBS) $(LIB_KIO) ../libaudiocdplugins.la
+
+libaudiocd_encoder_vorbis_la_LDFLAGS = -avoid-version -module -no-undefined $(all_libraries)
+
+pluginsdir = $(kde_datadir)/audiocd/plugins
+
+METASOURCES = AUTO
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/audiocd_encoder_vorbis.pot
diff --git a/kioslave/audiocd/plugins/vorbis/audiocd_vorbis_encoder.kcfg b/kioslave/audiocd/plugins/vorbis/audiocd_vorbis_encoder.kcfg
new file mode 100644
index 00000000..53465f6c
--- /dev/null
+++ b/kioslave/audiocd/plugins/vorbis/audiocd_vorbis_encoder.kcfg
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+ <kcfgfile name="kcmaudiocd_encoder_vorbis_rc"/>
+ <group name="Vorbis">
+
+ <entry name="vorbis_enc_method" type="Int">
+ <label>Vorbis Encoding Quality or Bitrate</label>
+ <default>0</default>
+ </entry>
+
+ <entry name="set_vorbis_min_br" type="Bool">
+ <label>Minimal bitrate specified</label>
+ <default>false</default>
+ </entry>
+ <entry name="set_vorbis_max_br" type="Bool">
+ <label>Maximal bitrate specified</label>
+ <default>false</default>
+ </entry>
+ <entry name="set_vorbis_nominal_br" type="Bool">
+ <label>Average bitrate specified</label>
+ <default>true</default>
+ </entry>
+
+ <entry name="vorbis_comments" type="Bool">
+ <label>Add Comments</label>
+ <default>true</default>
+ </entry>
+ <entry name="vorbis_quality" type="Double">
+ <label>Quality</label>
+ <default>3</default>
+ <min>-1</min>
+ <max>10</max>
+ </entry>
+
+ <entry name="vorbis_min_br" type="Int">
+ <label>Minimal bitrate</label>
+ <default>1</default>
+ <min>0</min>
+ <max>13</max>
+ </entry>
+
+ <entry name="vorbis_max_br" type="Int">
+ <label>maximal bitrate</label>
+ <default>13</default>
+ <min>0</min>
+ <max>13</max>
+ </entry>
+
+ <entry name="vorbis_nominal_br" type="Int">
+ <label>maximal bitrate</label>
+ <default>3</default>
+ <min>0</min>
+ <max>5</max>
+ </entry>
+
+<!--
+ <entry name="vorbis_nominal_br" type="Enum">
+ <label>maximal bitrate</label>
+ <default>150</default>
+ <choices>
+ <choice name="32"/>
+ <choice name="40"/>
+ <choice name="48"/>
+ <choice name="56"/>
+ <choice name="64"/>
+ <choice name="80"/>
+ <choice name="96"/>
+ <choice name="112"/>
+ <choice name="128"/>
+ <choice name="160"/>
+ <choice name="192"/>
+ <choice name="224"/>
+ <choice name="256"/>
+ <choice name="350"/>
+ </choices>
+ </entry> -->
+
+
+ </group>
+</kcfg>
+
diff --git a/kioslave/audiocd/plugins/vorbis/audiocd_vorbis_encoder.kcfgc b/kioslave/audiocd/plugins/vorbis/audiocd_vorbis_encoder.kcfgc
new file mode 100644
index 00000000..e213ccbd
--- /dev/null
+++ b/kioslave/audiocd/plugins/vorbis/audiocd_vorbis_encoder.kcfgc
@@ -0,0 +1,4 @@
+# Code generation options for kconfig_compiler
+File=audiocd_vorbis_encoder.kcfg
+ClassName=Settings
+Singleton=true
diff --git a/kioslave/audiocd/plugins/vorbis/encodervorbis.cpp b/kioslave/audiocd/plugins/vorbis/encodervorbis.cpp
new file mode 100644
index 00000000..01d8b00b
--- /dev/null
+++ b/kioslave/audiocd/plugins/vorbis/encodervorbis.cpp
@@ -0,0 +1,336 @@
+/*
+ Copyright (C) 2000 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2000, 2001, 2002 Michael Matz <matz@kde.org>
+ Copyright (C) 2001 Carsten Duvenhorst <duvenhorst@m2.uni-hannover.de>
+ Copyright (C) 2001 Adrian Schroeter <adrian@suse.de>
+ Copyright (C) 2003 Richard Lärkäng <richard@goteborg.utfors.se>
+ Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
+ Copyright (C) 2004, 2005 Benjamin Meyer <ben at meyerhome dot net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "encodervorbis.h"
+#include "audiocd_vorbis_encoder.h"
+#include "encodervorbisconfig.h"
+
+#ifdef HAVE_VORBIS
+
+#include <vorbis/vorbisenc.h>
+#include <time.h>
+#include <stdlib.h>
+#include <kconfig.h>
+#include <knuminput.h>
+#include <qgroupbox.h>
+
+#include <kglobal.h>
+#include <klocale.h>
+
+extern "C"
+{
+ KDE_EXPORT void create_audiocd_encoders(KIO::SlaveBase *slave, QPtrList<AudioCDEncoder> &encoders)
+ {
+ encoders.append(new EncoderVorbis(slave));
+ }
+}
+
+// these are the approx. bitrates for the current 5 Vorbis modes
+static int vorbis_nominal_bitrates[] = { 128, 160, 192, 256, 350 };
+static int vorbis_bitrates[] = { 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 350 };
+
+class EncoderVorbis::Private {
+
+public:
+ ogg_stream_state os; /* take physical pages, weld into a logical stream of packets */
+ ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */
+ ogg_packet op; /* one raw packet of data for decode */
+
+ vorbis_info vi; /* struct that stores all the static vorbis bitstream settings */
+ vorbis_comment vc; /* struct that stores all the user comments */
+
+ vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
+ vorbis_block vb; /* local working space for packet->PCM decode */
+ bool write_vorbis_comments;
+ long vorbis_bitrate_lower;
+ long vorbis_bitrate_upper;
+ long vorbis_bitrate_nominal;
+ int vorbis_encode_method;
+ double vorbis_quality;
+ int vorbis_bitrate;
+};
+
+EncoderVorbis::EncoderVorbis(KIO::SlaveBase *slave) : AudioCDEncoder(slave) {
+ d = new Private();
+}
+
+EncoderVorbis::~EncoderVorbis(){
+ delete d;
+}
+
+QWidget* EncoderVorbis::getConfigureWidget(KConfigSkeleton** manager) const {
+ (*manager) = Settings::self();
+ KGlobal::locale()->insertCatalogue("audiocd_encoder_vorbis");
+ EncoderVorbisConfig *config = new EncoderVorbisConfig();
+ config->kcfg_vorbis_quality->setRange(0.0, 10.0, 0.2, true);
+ config->vorbis_bitrate_settings->hide();
+ return config;
+}
+
+bool EncoderVorbis::init(){
+ vorbis_info_init(&d->vi);
+ vorbis_comment_init(&d->vc);
+
+ vorbis_comment_add_tag
+ (
+ &d->vc,
+ const_cast<char *>("kde-encoder"),
+ const_cast<char *>("kio_audiocd")
+ );
+ return true;
+}
+
+void EncoderVorbis::loadSettings(){
+ Settings *settings = Settings::self();
+
+ d->vorbis_encode_method = settings->vorbis_enc_method();
+ d->vorbis_quality = settings->vorbis_quality();
+
+ if ( settings->set_vorbis_min_br()) {
+ d->vorbis_bitrate_lower = vorbis_bitrates[settings->vorbis_min_br()] * 1000;
+ } else {
+ d->vorbis_bitrate_lower = -1;
+ }
+
+ if ( settings->set_vorbis_max_br() ) {
+ d->vorbis_bitrate_upper = vorbis_bitrates[settings->vorbis_max_br()] * 1000;
+ } else {
+ d->vorbis_bitrate_upper = -1;
+ }
+
+ // this is such a hack!
+ if ( d->vorbis_bitrate_upper != -1 && d->vorbis_bitrate_lower != -1 ) {
+ d->vorbis_bitrate = 104000; // empirically determined ...?!
+ } else {
+ d->vorbis_bitrate = 160 * 1000;
+ }
+
+ if ( settings->set_vorbis_nominal_br() ) {
+ d->vorbis_bitrate_nominal = vorbis_nominal_bitrates[settings->vorbis_nominal_br()] * 1000;
+ d->vorbis_bitrate = d->vorbis_bitrate_nominal;
+ } else {
+ d->vorbis_bitrate_nominal = -1;
+ }
+
+ d->write_vorbis_comments = settings->vorbis_comments();
+
+ // Now that we have read in the settings apply them to the encoder lib
+ switch (d->vorbis_encode_method) {
+ case 0:
+/* Support very old libvorbis by simply falling through. */
+#if HAVE_VORBIS >= 2
+ vorbis_encode_init_vbr(&d->vi, 2, 44100, d->vorbis_quality/10.0);
+ break;
+#endif
+ case 1:
+ vorbis_encode_init(&d->vi, 2, 44100, d->vorbis_bitrate_upper, d->vorbis_bitrate_nominal, d->vorbis_bitrate_lower);
+ break;
+ }
+
+}
+
+long EncoderVorbis::flush_vorbis(void) {
+ long processed(0);
+
+ while(vorbis_analysis_blockout(&d->vd,&d->vb)==1) {
+ /* Support ancient libvorbis (< RC3). */
+#if HAVE_VORBIS >= 2
+ vorbis_analysis(&d->vb,NULL);
+ /* Non-ancient case. */
+ vorbis_bitrate_addblock(&d->vb);
+
+ while(vorbis_bitrate_flushpacket(&d->vd, &d->op)) {
+#else
+ vorbis_analysis(&d->vb,&d->op);
+ /* Make a lexical block to place the #ifdef's nearby. */
+ if (1) {
+#endif
+ ogg_stream_packetin(&d->os,&d->op);
+ while(int result=ogg_stream_pageout(&d->os,&d->og)) {
+ if (!result) break;
+
+ QByteArray output;
+
+ char * oggheader = reinterpret_cast<char *>(d->og.header);
+ char * oggbody = reinterpret_cast<char *>(d->og.body);
+
+ if (d->og.header_len) {
+ output.setRawData(oggheader, d->og.header_len);
+ ioslave->data(output);
+ output.resetRawData(oggheader, d->og.header_len);
+ }
+
+ if (d->og.body_len) {
+ output.setRawData(oggbody, d->og.body_len);
+ ioslave->data(output);
+ output.resetRawData(oggbody, d->og.body_len);
+ }
+ processed += d->og.header_len + d->og.body_len;
+ }
+ }
+ }
+ return processed;
+}
+
+unsigned long EncoderVorbis::size(long time_secs) const {
+ long vorbis_size;
+ switch (d->vorbis_encode_method)
+ {
+ case 0: // quality based encoding
+
+#if HAVE_VORBIS >= 2 // If really old Vorbis is being used, skip this nicely.
+
+ {
+ // Estimated numbers based on the Vorbis FAQ:
+ // http://www.xiph.org/archives/vorbis-faq/200203/0030.html
+
+ static long vorbis_q_bitrate[] = { 60, 74, 86, 106, 120, 152,
+ 183, 207, 239, 309, 440 };
+ long quality = static_cast<long>(d->vorbis_quality);
+ if (quality < 0 || quality > 10)
+ quality = 3;
+ vorbis_size = (time_secs * vorbis_q_bitrate[quality] * 1000) / 8;
+
+ break;
+ }
+
+#endif // HAVE_VORBIS >= 2
+
+ default: // bitrate based encoding
+ vorbis_size = (time_secs * d->vorbis_bitrate/8);
+ break;
+ }
+
+ return vorbis_size;
+}
+
+const char * EncoderVorbis::mimeType() const{
+ return "audio/vorbis";
+}
+
+long EncoderVorbis::readInit(long /*size*/){
+ ogg_packet header;
+ ogg_packet header_comm;
+ ogg_packet header_code;
+
+ vorbis_analysis_init(&d->vd,&d->vi);
+ vorbis_block_init(&d->vd,&d->vb);
+
+ srand(time(NULL));
+ ogg_stream_init(&d->os,rand());
+
+ vorbis_analysis_headerout(&d->vd,&d->vc,&header,&header_comm,&header_code);
+
+ ogg_stream_packetin(&d->os,&header);
+ ogg_stream_packetin(&d->os,&header_comm);
+ ogg_stream_packetin(&d->os,&header_code);
+
+ while (int result = ogg_stream_flush(&d->os,&d->og)) {
+
+ if (!result) break;
+
+ QByteArray output;
+
+ char * oggheader = reinterpret_cast<char *>(d->og.header);
+ char * oggbody = reinterpret_cast<char *>(d->og.body);
+
+ if (d->og.header_len) {
+ output.setRawData(oggheader, d->og.header_len);
+ ioslave->data(output);
+ output.resetRawData(oggheader, d->og.header_len);
+ }
+
+ if (d->og.body_len) {
+ output.setRawData(oggbody, d->og.body_len);
+ ioslave->data(output);
+ output.resetRawData(oggbody, d->og.body_len);
+ }
+ }
+ return 0;
+}
+
+long EncoderVorbis::read(int16_t * buf, int frames){
+ int i;
+ float **buffer=vorbis_analysis_buffer(&d->vd,frames);
+
+ /* uninterleave samples */
+ for(i=0;i<frames;i++){
+ buffer[0][i]=buf[2*i]/32768.0;
+ buffer[1][i]=buf[2*i+1]/32768.0;
+ }
+
+ /* process chunk of data */
+ vorbis_analysis_wrote(&d->vd,i);
+ return flush_vorbis();
+}
+
+long EncoderVorbis::readCleanup(){
+ // send end-of-stream and flush the encoder
+ vorbis_analysis_wrote(&d->vd,0);
+ long processed = flush_vorbis();
+ ogg_stream_clear(&d->os);
+ vorbis_block_clear(&d->vb);
+ vorbis_dsp_clear(&d->vd);
+ vorbis_info_clear(&d->vi);
+ return processed;
+}
+
+void EncoderVorbis::fillSongInfo( KCDDB::CDInfo info, int track, const QString &comment )
+{
+ if( !d->write_vorbis_comments )
+ return;
+
+ typedef QPair<QCString, QVariant> CommentField;
+ QValueList<CommentField> commentFields;
+
+ commentFields.append(CommentField("title", info.trackInfoList[track].get("title")));
+ commentFields.append(CommentField("artist", info.get("artist")));
+ commentFields.append(CommentField("album", info.get("title")));
+ commentFields.append(CommentField("genre", info.get("genre")));
+ commentFields.append(CommentField("tracknumber", QString::number(track+1)));
+ commentFields.append(CommentField("comment", comment));
+
+ if (info.get("year").toInt() > 0) {
+ QDateTime dt(QDate(info.get("year").toInt(), 1, 1));
+ commentFields.append(CommentField("date", dt.toString(Qt::ISODate).utf8().data()));
+ }
+
+ for(QValueListIterator<CommentField> it = commentFields.begin(); it != commentFields.end(); ++it) {
+
+ // if the value is not empty
+ if(!(*it).second.toString().isEmpty()) {
+
+ char *key = qstrdup((*it).first);
+ char *value = qstrdup((*it).second.toString().utf8().data());
+
+ vorbis_comment_add_tag(&d->vc, key, value);
+
+ delete [] key;
+ delete [] value;
+ }
+ }
+}
+
+#endif // HAVE_VORBIS
+
diff --git a/kioslave/audiocd/plugins/vorbis/encodervorbis.h b/kioslave/audiocd/plugins/vorbis/encodervorbis.h
new file mode 100644
index 00000000..28b837ce
--- /dev/null
+++ b/kioslave/audiocd/plugins/vorbis/encodervorbis.h
@@ -0,0 +1,68 @@
+/*
+ Copyright (C) 2000 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2000, 2001, 2002 Michael Matz <matz@kde.org>
+ Copyright (C) 2001 Carsten Duvenhorst <duvenhorst@m2.uni-hannover.de>
+ Copyright (C) 2001 Adrian Schroeter <adrian@suse.de>
+ Copyright (C) 2003 Richard Lärkäng <richard@goteborg.utfors.se>
+ Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
+ Copyright (C) 2004, 2005 Benjamin Meyer <ben at meyerhome dot net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef ENCODER_VORBIS_H
+#define ENCODER_VORBIS_H
+
+#include <config.h>
+
+#ifdef HAVE_VORBIS
+
+#include <audiocdencoder.h>
+
+/**
+ * Ogg Vorbis encoder.
+ * This encoder is only enabled when HAVE_VORBIS is set.
+ * Check out http://www.vorbis.com/ for lots of information.
+ */
+class EncoderVorbis : public AudioCDEncoder {
+
+public:
+ EncoderVorbis(KIO::SlaveBase *slave);
+ ~EncoderVorbis();
+
+ virtual QString type() const { return "Ogg Vorbis"; };
+ virtual bool init();
+ virtual void loadSettings();
+ virtual unsigned long size(long time_secs) const;
+ virtual const char * fileType() const { return "ogg"; };
+ virtual const char * mimeType() const;
+ virtual void fillSongInfo( KCDDB::CDInfo info, int track, const QString &comment );
+ virtual long readInit(long size);
+ virtual long read(int16_t * buf, int frames);
+ virtual long readCleanup();
+ virtual QWidget* getConfigureWidget(KConfigSkeleton** manager) const;
+
+private:
+ long flush_vorbis();
+
+ class Private;
+ Private * d;
+
+};
+
+#endif // HAVE_VORBIS
+
+#endif // ENCODER_VORBIS_H
+
diff --git a/kioslave/audiocd/plugins/vorbis/encodervorbisconfig.ui b/kioslave/audiocd/plugins/vorbis/encodervorbisconfig.ui
new file mode 100644
index 00000000..17000745
--- /dev/null
+++ b/kioslave/audiocd/plugins/vorbis/encodervorbisconfig.ui
@@ -0,0 +1,425 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>EncoderVorbisConfig</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>VorbisConfig</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>375</width>
+ <height>408</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>kcfg_vorbis_enc_method</cstring>
+ </property>
+ <property name="title">
+ <string>&amp;Encoding Method</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>vorbis_enc_quality</cstring>
+ </property>
+ <property name="text">
+ <string>Quality based</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>vorbis_enc_bitrate</cstring>
+ </property>
+ <property name="text">
+ <string>Bitrate based</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>vorbis_bitrate_settings</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>Vorbis Bitrate Settings</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QComboBox" row="0" column="1">
+ <item>
+ <property name="text">
+ <string>32 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>40 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>48 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>56 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>64 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>80 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>96 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>112 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>128 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>160 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>192 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>224 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>256 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>350 kbs</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>kcfg_vorbis_min_br</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="currentItem">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="1" column="1">
+ <item>
+ <property name="text">
+ <string>32 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>40 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>48 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>56 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>64 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>80 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>96 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>112 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>128 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>160 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>192 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>224 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>256 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>350 kbs</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>kcfg_vorbis_max_br</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="currentItem">
+ <number>13</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>kcfg_set_vorbis_min_br</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>M&amp;inimal bitrate:</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>kcfg_set_vorbis_max_br</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Ma&amp;ximal bitrate:</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="2" column="1">
+ <item>
+ <property name="text">
+ <string>128 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>160 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>192 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>256 kbs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>350 kbs</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>kcfg_vorbis_nominal_br</cstring>
+ </property>
+ <property name="currentItem">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>kcfg_set_vorbis_nominal_br</cstring>
+ </property>
+ <property name="text">
+ <string>A&amp;verage bitrate:</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>vorbis_quality_settings</cstring>
+ </property>
+ <property name="title">
+ <string>Vorbis &amp;Quality Setting</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>You can set the quality of the encoded stream here. A higher value implies a higher quality but encodes slower.</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="KDoubleNumInput">
+ <property name="name">
+ <cstring>kcfg_vorbis_quality</cstring>
+ </property>
+ <property name="value">
+ <number>0</number>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ <property name="maxValue">
+ <number>10000</number>
+ </property>
+ <property name="precision">
+ <number>1</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Higher is better but slower</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>GroupBox193</cstring>
+ </property>
+ <property name="title">
+ <string>Options</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_vorbis_comments</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Add &amp;track information</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Add a description of the song to the file header. This makes it easy for the user to get advanced song information shown by his media player. You can get this information automatically via the Internet. Look at the &lt;i&gt;"CDDB Retrieval"&lt;/i&gt; control module for details.</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer9</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>51</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>vorbis_enc_bitrate</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>vorbis_bitrate_settings</receiver>
+ <slot>setShown(bool)</slot>
+ </connection>
+ <connection>
+ <sender>vorbis_enc_quality</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>vorbis_quality_settings</receiver>
+ <slot>setShown(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>vorbis_enc_quality</tabstop>
+ <tabstop>kcfg_set_vorbis_min_br</tabstop>
+ <tabstop>kcfg_set_vorbis_max_br</tabstop>
+ <tabstop>kcfg_set_vorbis_nominal_br</tabstop>
+ <tabstop>kcfg_vorbis_min_br</tabstop>
+ <tabstop>kcfg_vorbis_max_br</tabstop>
+ <tabstop>kcfg_vorbis_nominal_br</tabstop>
+ <tabstop>kcfg_vorbis_quality</tabstop>
+ <tabstop>kcfg_vorbis_comments</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+</includehints>
+</UI>
diff --git a/kioslave/audiocd/plugins/wav/Makefile.am b/kioslave/audiocd/plugins/wav/Makefile.am
new file mode 100644
index 00000000..82e6ab28
--- /dev/null
+++ b/kioslave/audiocd/plugins/wav/Makefile.am
@@ -0,0 +1,15 @@
+AM_CPPFLAGS = -I$(srcdir)/.. $(all_includes)
+
+INCLUDES = -I$(top_srcdir)/libkcddb
+
+kde_module_LTLIBRARIES = libaudiocd_encoder_wav.la
+
+libaudiocd_encoder_wav_la_SOURCES = encoderwav.cpp encodercda.cpp
+
+libaudiocd_encoder_wav_la_LIBADD = $(LIB_KIO) ../libaudiocdplugins.la $(CDPARANOIA_LIBS)
+
+libaudiocd_encoder_wav_la_LDFLAGS = -avoid-version -module -no-undefined $(all_libraries)
+
+pluginsdir = $(kde_datadir)/audiocd/plugins
+
+METASOURCES = AUTO
diff --git a/kioslave/audiocd/plugins/wav/encodercda.cpp b/kioslave/audiocd/plugins/wav/encodercda.cpp
new file mode 100644
index 00000000..fb8a794a
--- /dev/null
+++ b/kioslave/audiocd/plugins/wav/encodercda.cpp
@@ -0,0 +1,67 @@
+/*
+ Copyright (C) 2000 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2000, 2001, 2002 Michael Matz <matz@kde.org>
+ Copyright (C) 2001 Carsten Duvenhorst <duvenhorst@m2.uni-hannover.de>
+ Copyright (C) 2001 Adrian Schroeter <adrian@suse.de>
+ Copyright (C) 2003 Richard Lärkäng <richard@goteborg.utfors.se>
+ Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
+ Copyright (C) 2004, 2005 Benjamin Meyer <ben at meyerhome dot net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "encodercda.h"
+
+class EncoderCda::Private
+{
+ public:
+
+};
+
+unsigned long EncoderCda::size(long time_secs) const {
+ //return (time_secs * (44100 * 2 * 16))/8;
+ return (time_secs) * 176400;
+}
+
+const char * EncoderCda::mimeType() const {
+ return "audio/x-cda";
+}
+
+// Remove this by calculating CD_FRAMESIZE_RAW from the frames
+extern "C"
+{
+ #include <cdda_interface.h>
+}
+
+inline int16_t swap16 (int16_t i)
+{
+ return (((i >> 8) & 0xFF) | ((i << 8) & 0xFF00));
+}
+
+long EncoderCda::read(int16_t * buf, int frames){
+ QByteArray output;
+ int16_t i16 = 1;
+ /* WAV is defined to be little endian, so we need to swap it
+ on big endian platforms. */
+ if (((char*)&i16)[0] == 0)
+ for (int i=0; i < 2 * frames; i++)
+ buf[i] = swap16 (buf[i]);
+ char * cbuf = reinterpret_cast<char *>(buf);
+ output.setRawData(cbuf, CD_FRAMESIZE_RAW);
+ ioslave->data(output);
+ output.resetRawData(cbuf, CD_FRAMESIZE_RAW);
+ return CD_FRAMESIZE_RAW;
+}
+
diff --git a/kioslave/audiocd/plugins/wav/encodercda.h b/kioslave/audiocd/plugins/wav/encodercda.h
new file mode 100644
index 00000000..0a95aa51
--- /dev/null
+++ b/kioslave/audiocd/plugins/wav/encodercda.h
@@ -0,0 +1,61 @@
+/*
+ Copyright (C) 2000 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2000, 2001, 2002 Michael Matz <matz@kde.org>
+ Copyright (C) 2001 Carsten Duvenhorst <duvenhorst@m2.uni-hannover.de>
+ Copyright (C) 2001 Adrian Schroeter <adrian@suse.de>
+ Copyright (C) 2003 Richard Lärkäng <richard@goteborg.utfors.se>
+ Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
+ Copyright (C) 2004, 2005 Benjamin Meyer <ben at meyerhome dot net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef ENCODER_CDA_H
+#define ENCODER_CDA_H
+
+#include <audiocdencoder.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+/**
+ * Raw cd "encoder"
+ * Does little more then copy the data and make sure it is in the right
+ * endian.
+ */
+class EncoderCda : public AudioCDEncoder {
+
+public:
+ EncoderCda(KIO::SlaveBase *slave) : AudioCDEncoder(slave) {};
+ ~EncoderCda(){};
+ virtual bool init(){ return true; };
+ virtual void loadSettings(){};
+ virtual unsigned long size(long time_secs) const;
+ virtual QString type() const { return "CDA"; };
+ virtual const char * mimeType() const;
+ virtual const char * fileType() const { return "cda"; };
+ virtual void fillSongInfo( KCDDB::CDInfo, int, const QString &){};
+ virtual long readInit(long){ return 0; };
+ virtual long read(int16_t * buf, int frames);
+ virtual long readCleanup(){ return 0; };
+
+private:
+ class Private;
+ Private * d;
+
+};
+
+#endif // ENCODER_CDA_H
+
diff --git a/kioslave/audiocd/plugins/wav/encoderwav.cpp b/kioslave/audiocd/plugins/wav/encoderwav.cpp
new file mode 100644
index 00000000..ad658e2c
--- /dev/null
+++ b/kioslave/audiocd/plugins/wav/encoderwav.cpp
@@ -0,0 +1,85 @@
+/*
+ Copyright (C) 2000 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2000, 2001, 2002 Michael Matz <matz@kde.org>
+ Copyright (C) 2001 Carsten Duvenhorst <duvenhorst@m2.uni-hannover.de>
+ Copyright (C) 2001 Adrian Schroeter <adrian@suse.de>
+ Copyright (C) 2003 Richard Lärkäng <richard@goteborg.utfors.se>
+ Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
+ Copyright (C) 2004, 2005 Benjamin Meyer <ben at meyerhome dot net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "encoderwav.h"
+
+extern "C"
+{
+ KDE_EXPORT void create_audiocd_encoders(KIO::SlaveBase *slave, QPtrList<AudioCDEncoder> &encoders)
+ {
+ encoders.append( new EncoderWav(slave));
+ encoders.append( new EncoderCda(slave));
+ }
+}
+
+class EncoderWav::Private
+{
+ public:
+
+};
+
+unsigned long EncoderWav::size(long time_secs) const {
+ return (EncoderCda::size(time_secs) + 44);
+}
+
+const char * EncoderWav::mimeType() const {
+ return "audio/x-wav";
+}
+
+long EncoderWav::readInit(long byteCount){
+ static char riffHeader[] =
+ {
+ 0x52, 0x49, 0x46, 0x46, // 0 "AIFF"
+ 0x00, 0x00, 0x00, 0x00, // 4 wavSize
+ 0x57, 0x41, 0x56, 0x45, // 8 "WAVE"
+ 0x66, 0x6d, 0x74, 0x20, // 12 "fmt "
+ 0x10, 0x00, 0x00, 0x00, // 16
+ 0x01, 0x00, 0x02, 0x00, // 20
+ 0x44, 0xac, 0x00, 0x00, // 24
+ 0x10, 0xb1, 0x02, 0x00, // 28
+ 0x04, 0x00, 0x10, 0x00, // 32
+ 0x64, 0x61, 0x74, 0x61, // 36 "data"
+ 0x00, 0x00, 0x00, 0x00 // 40 byteCount
+ };
+
+ Q_INT32 wavSize(byteCount + 44 - 8);
+
+
+ riffHeader[4] = (wavSize >> 0 ) & 0xff;
+ riffHeader[5] = (wavSize >> 8 ) & 0xff;
+ riffHeader[6] = (wavSize >> 16) & 0xff;
+ riffHeader[7] = (wavSize >> 24) & 0xff;
+
+ riffHeader[40] = (byteCount >> 0 ) & 0xff;
+ riffHeader[41] = (byteCount >> 8 ) & 0xff;
+ riffHeader[42] = (byteCount >> 16) & 0xff;
+ riffHeader[43] = (byteCount >> 24) & 0xff;
+
+ QByteArray output;
+ output.setRawData(riffHeader, 44);
+ ioslave->data(output);
+ output.resetRawData(riffHeader, 44);
+ return 44;
+}
+
diff --git a/kioslave/audiocd/plugins/wav/encoderwav.h b/kioslave/audiocd/plugins/wav/encoderwav.h
new file mode 100644
index 00000000..09301ae5
--- /dev/null
+++ b/kioslave/audiocd/plugins/wav/encoderwav.h
@@ -0,0 +1,56 @@
+/*
+ Copyright (C) 2000 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2000, 2001, 2002 Michael Matz <matz@kde.org>
+ Copyright (C) 2001 Carsten Duvenhorst <duvenhorst@m2.uni-hannover.de>
+ Copyright (C) 2001 Adrian Schroeter <adrian@suse.de>
+ Copyright (C) 2003 Richard Lärkäng <richard@goteborg.utfors.se>
+ Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
+ Copyright (C) 2004, 2005 Benjamin Meyer <ben at meyerhome dot net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef ENCODER_WAV_H
+#define ENCODER_WAV_H
+
+#include "encodercda.h"
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+/**
+ * Wav audio "encoder"
+ * Takes the CDA take and adds the standard wav header.
+ */
+class EncoderWav : public EncoderCda {
+
+public:
+ EncoderWav(KIO::SlaveBase *slave) : EncoderCda(slave) {};
+ ~EncoderWav(){};
+ virtual bool init(){ return true; };
+ virtual unsigned long size(long time_secs) const;
+ virtual QString type() const { return "Wav"; };
+ virtual const char * fileType() const { return "wav"; };
+ virtual const char * mimeType() const;
+ virtual void fillSongInfo( KCDDB::CDInfo, int, const QString &){};
+ virtual long readInit(long size);
+
+private:
+ class Private;
+ Private * d;
+
+};
+
+#endif // ENCODER_WAV_H
diff --git a/kioslave/audiocd/upgrade-metadata.sh b/kioslave/audiocd/upgrade-metadata.sh
new file mode 100755
index 00000000..000323b1
--- /dev/null
+++ b/kioslave/audiocd/upgrade-metadata.sh
@@ -0,0 +1,28 @@
+#! /usr/bin/env bash
+
+while read; do
+ if [ "${REPLY#\[}" != "$REPLY" ] ; then # group name
+ GROUP="${REPLY:1:${#REPLY}-2}"
+ continue;
+ fi
+ # normal key=value pair:
+ KEY="${REPLY%%=*}"
+ VALUE="${REPLY#*=}"
+
+ case "$GROUP/$KEY" in
+ FileName/file_name_template)
+ #
+ # Swap
+ #
+ VALUE=`echo $VALUE | sed -e s/%a/%{albumtitle}/g`;
+ VALUE=`echo $VALUE | sed -e s/%t/%{title}/g`;
+ VALUE=`echo $VALUE | sed -e s/%n/%{number}/g`;
+ VALUE=`echo $VALUE | sed -e s/%g/%{genre}/g`;
+ VALUE=`echo $VALUE | sed -e s/%y/%{year}/g`;
+ VALUE=`echo $VALUE | sed -e s/%A/%{albumartist}/g`;
+ echo "[FileName]";
+ echo "file_name_template=$VALUE"
+ echo "# DELETE [FileName]file_name_template"
+ ;;
+ esac
+done
diff --git a/kmid/ChangeLog b/kmid/ChangeLog
new file mode 100644
index 00000000..6914c1d0
--- /dev/null
+++ b/kmid/ChangeLog
@@ -0,0 +1,104 @@
+0.1 30-10-97
+----- --------
+First version
+
+0.2 9-11-97
+----- -------
+Better syncronization between lyrics and music.
+Drag&Drop, better installation, icons, Customizable fonts.
+
+0.3 16-11-97
+----- --------
+The lyrics handler is very improved.
+Option to automagically select lyrics or text.
+Better handling for broken files.
+Few bug fixes.
+Real Session Management.
+First rpm version.
+
+0.4 30-12-97
+----- --------
+Fixed the pthread bug (sorry to everyone that suffered it ;-))
+Added support for AWE and GUS cards
+Can modify the midi device without recompiling.
+Can play gzipped files
+
+0.41 22-2-98
+----- -------
+Fixed a problem with GUS cards
+Improved midi mapper
+Fixed a non-feature :-) related to FM
+New icon and toolbar pixmaps which use fewer colors
+
+0.5 15-3-98
+----- -------
+FM support
+Playlists (Collections)
+Some small improvements here and there
+
+0.5.1 15-3-98
+----- -------
+A very small bug fix
+
+0.5.2 5-4-98
+----- ------
+Save Lyrics
+Some bug fixes
+More keys binded to functions (arrows, space, backspace, etc.)
+
+0.5.3 24-4-98
+----- -------
+Improved Synchronization between gui and player
+Some bug fixes:
+ - One which caused kmid to freeze when playing a collection with only 1 song
+ in aleatory mode and changing to next song
+ (reported by Jose Luis Sanchez)
+ - It was not possible to play a gzipped file that have spaces in its name
+ - There were some songs that played at a lower volume when moving the
+ slider, but not if playing from the beginning.
+Better support for shuffle mode (now it doesn't repeat songs :-))
+
+0.6 7-5-98
+----- ------
+Just thought that everything worked fine and did a major release to
+put it in my homepage
+
+1.0 22-6-98
+----- -------
+Well, I should be studying, but KDE 1.0 good deserves a few minutes
+to change the version number everywhere, remove debug printfs, and
+clean the latest bugs, isn't it ? :-)
+
+1.2 14-9-98
+----- -------
+This version includes a new ChannelView to see notes played by each
+instrument and a volume bar.
+It also has some bug fixes and support for really broken midi files.
+
+1.6 27-11-98
+----- --------
+This version fix a bug that made kmid totally unusable to many users
+because it played music at different tempos (sometimes faster and
+sometimes slower), without any rhythm. Anyway, this isn't a public
+version yet, it's only available from CVS.
+
+1.7 2-4-99
+----- -------
+Modified KMid to work with Linux kernels from the 2.2.x series which
+work very different than the ones in 2.0.x . That problem made KMid
+continue playing music for a while (up to a few minutes !) after the
+user had pressed the stop button.
+Fixed a problem with AWE cards that made it play files with the pitch
+bender raised.
+Fixed a problem that made kmid not to play the last few milliseconds of
+some songs (sometimes up to half a second)
+Another usual fixes (better support for kernel modules, workarounds for
+compiler bugs, etc.)
+
+2.0 1-10-2000
+----- ---------
+KMid has been ported to KDE 2.0
+Now has a separated ui and engine, it also has native ALSA support
+It's a part now, so you can embed KMid in Konqueror and other apps.
+Ah, and the scroll is smooth now :)
+
diff --git a/kmid/Makefile.am b/kmid/Makefile.am
new file mode 100644
index 00000000..6dd4acb7
--- /dev/null
+++ b/kmid/Makefile.am
@@ -0,0 +1,74 @@
+INCLUDES = $(all_includes)
+
+kde_module_LTLIBRARIES = libkmidpart.la
+lib_LTLIBRARIES = libkmidlib.la
+
+libkmidlib_la_LDFLAGS = $(all_libraries)
+libkmidlib_la_LIBADD = $(LIB_KFILE) $(LIB_KPARTS) $(LIB_KIO) -lkmid
+
+libkmidpart_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module
+libkmidpart_la_LIBADD = libkmidlib.la
+
+libkmidlib_la_SOURCES = kmidframe.cpp kmidclient.cpp kdisptext.cpp \
+ qslidertime.cpp klcdnumber.cpp ktrianglebutton.cpp \
+ midicfgdlg.cpp collectdlg.cpp randomlist.cpp \
+ channelview.cpp channel.cpp channel3d.cpp channel4d.cpp \
+ channelcfgdlg.cpp instrname.cpp rhythmview.cpp \
+ songlist.cpp slman.cpp kmid_part.cpp kmidIface.skel
+
+libkmidpart_la_SOURCES = part_dummy.cpp
+
+part_dummy.cpp:
+ touch part_dummy.cpp
+
+noinst_HEADERS = kmidframe.h kmidclient.h kdisptext.h \
+ qslidertime.h klcdnumber.h ktrianglebutton.h \
+ midicfgdlg.h collectdlg.h randomlist.h \
+ channelview.h channel.h channel3d.h channel4d.h \
+ channelcfgdlg.h instrname.h rhythmview.h \
+ songlist.h slman.h kmid_part.h
+
+kmid_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+kmid_SOURCES = main.cpp
+kmid_LDADD = libkmidlib.la
+
+bin_PROGRAMS = kmid
+METASOURCES = AUTO
+
+servicetype_DATA = audiomidi.desktop
+servicetypedir = $(kde_servicetypesdir)
+
+SUBDIRS = maps examples pics
+
+xdg_apps_DATA = kmid.desktop
+
+mime_DATA = x-karaoke.desktop
+mimedir = $(kde_mimedir)/audio
+
+rc_DATA = kmidui.rc
+rcdir = $(kde_datadir)/kmid
+
+KDE_ICON = kmid
+
+fmpatches_DATA = std.sb drums.sb std.o3 drums.o3
+fmpatchesdir = $(kde_datadir)/kmid/fm
+
+partdir = $(kde_datadir)/kmid
+part_DATA = kmid_partui.rc
+
+channelview_DATA = keyboard.xpm button1.xpm button2.xpm
+channelviewdir = $(kde_datadir)/kmid/icons
+
+EXTRA_DIST = $(xdg_apps_DATA) $(mime_DATA) $(fmpatches_DATA) $(part_DATA)
+
+instrname.i18n: instrname.cpp
+ echo "/* This is an automatically generated file" > instrname.i18n
+ echo " * DO NOT EDIT !!!" >> instrname.i18n
+ echo " * To create this file run : ""make instrname.i18n"" " >> instrname.i18n
+ echo " */" >> instrname.i18n
+ echo "" >> instrname.i18n
+ awk -f instrfilter.awk instrname.cpp >> instrname.i18n
+
+
+messages: instrname.i18n rc.cpp
+ $(XGETTEXT) *.cpp instrname.i18n -o $(podir)/kmid.pot
diff --git a/kmid/PEOPLE b/kmid/PEOPLE
new file mode 100644
index 00000000..4cd777c2
--- /dev/null
+++ b/kmid/PEOPLE
@@ -0,0 +1,4 @@
+Please take a look at the online help for a list of people who have helped
+me in the development of KMid (emotionally or with patches :-) ).
+
+ - Antonio Larrosa
diff --git a/kmid/README b/kmid/README
new file mode 100644
index 00000000..52c5f759
--- /dev/null
+++ b/kmid/README
@@ -0,0 +1,123 @@
+**********************************************************************
+ KKK KK MMM MMM IIIIII DDDDD
+ KKK KK MMMMMMMMMM IIII DDDDDDD
+ KKKKK MMMMMMMMMM II DDD DD
+ Console KKKKKK MMM MM MMM IIII DDDDDDD
+ KKK KKK MMM MMM IIIIII DDDDD
+
+ ConsoleKMID
+
+
+ This info is up to date if you have KMid version 1.7
+
+________________________________________________
+If you are looking for a KMid README file, then this is NOT the one you are
+looking for. Please look the online documentation, that is, the html help
+
+I've left this file only for help on the small cousin of KMid, ConsoleKMid.
+
+ - Antonio Larrosa
+------------------------------------------------
+
+Index
+-------
+ 1. What is ConsoleKMid ?
+ 2. Requirements of consoleKMid
+ 3. Compiling consoleKMid
+ 4. Installing
+ 5. Running
+ 6. How do I use a Midi Mapper ?
+ 7. Where can I download KMid and consoleKMid from ?
+ 8. To do list
+
+1. What is ConsoleKMid ?
+------------------------
+ ConsoleKMid is a small program for Unix in console mode, which uses the same
+ player engine of KMid. Of course it doesn't have the same friendly
+ interface of KMid but it works to play midi files.
+
+ If you have the rpm distribution, you will not find the consolekmid program
+ unless you have compiled it from the src.rpm distribution
+
+
+2. Requirements of consoleKMid
+-------------------------------
+
+ A UNIX machine, a soundcard and a /dev/sequencer device well installed.
+ If you have a GUS, you need a properly installed driver from the Linux
+ Ultrasound Project, AWE cards are also supported, but you will probably
+ need to compile consoleKMid in order to get an AWE card to work.
+ FM devices are also supported.
+ I have only tested KMid in Linux, but I have had sucessful reports getting
+ KMid to work on FreeBSD
+ If you sucessfully run KMid in other unices, please tell me !
+
+ Currently GUS cards make a little sound when using the oss driver, but are
+ not supported officially, I'm looking to find people with GUS cards that
+ want to help me to provide such support (either programming or testing),
+ as I think that if nobody has worried about it it's because there's no
+ need for it ;-)
+
+3. Compiling consoleKMid
+-----------------
+ Do a configure from the parent directory, then go to the player directory,
+ and do a 'make' . Simple , isn't it ?
+
+ By default it will use the external midi device, if you want to use another
+ one (fm, awe, or anything else), edit the file main.cc and change the value
+ of DEFAULT_DEVICE to the one you want, or specify the -d option as in :
+
+ consolekmid -d 1 mymidifile.mid
+
+4. Installing
+-----------------
+ No installing of consoleKMid is actually done, just copy the consoleKMid file
+ yourself to where you want (usually /usr/local/bin ). To use the fm device,
+ you should specify in fmout.cc the path to where the fm patches are installed
+ ( /etc by default)
+
+5. Running
+--------------
+ Well, it's quite easy, just type "consolekmid" and it should run :-)
+
+6. How do I use a Midi Mapper ?
+-------------------------------
+ A Midi Mapper is the feature you have been waiting for in Linux for years,
+ (at least I have been :-) it makes a non General Midi synth, appear to the
+ computer as General Midi compliant. Thus, a GM synth is emulated with a
+ nonGM.
+ Look the KMid docs for more info on creating/using a midi map.
+ If you want to use one with consolekmid, you can use the -M option,
+ use it like this example :
+
+ consolekmid -M /opt/kde/share/apps/kmid/maps/YamahaPSS790.map mymidifile
+
+ You can also edit main.cc in the player directory and change the value of
+ DEFAULT_MAP to whatever you want, so that you don't need to specify the
+ -M option each time you run consolekmid.
+
+ If you want to define a new map for your midi keyboard, I recommend you to
+ start by copying the file yamaha790.map and modifying it. And remember to
+ send it to me so that it will be included in future revisions and other
+ people can use it !
+
+7. Where can I download KMid and consoleKMid from ?
+---------------------------------------------------
+
+ KMid's homepage is at :
+ http://developer.kde.org/~larrosa/kmid.html
+
+ From there you can download latest versions as well as know the latest
+ changes and improvements of KMid and consoleKMid
+
+8. To do list
+---------------
+ GUS support is nearly done (and also SoftOSS) but still need little
+ improvements. It should mostly work now.
+ Better AWE support and use Soundfonts
+ The KMidServer and make KMid use it.
+
+-------
+30-4-2001
+Antonio Larrosa Jimenez
+larrosa@kde.org
diff --git a/kmid/audiomidi.desktop b/kmid/audiomidi.desktop
new file mode 100644
index 00000000..420045bf
--- /dev/null
+++ b/kmid/audiomidi.desktop
@@ -0,0 +1,3 @@
+# This file used to define audio/x-midi, which is already defined as a mime type!
+[Desktop Entry]
+Hidden=true
diff --git a/kmid/button1.xpm b/kmid/button1.xpm
new file mode 100644
index 00000000..93d38a73
--- /dev/null
+++ b/kmid/button1.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * button1_xpm[] = {
+"16 16 3 1",
+" c None",
+". c #000000000000",
+"X c #FFFF00000000",
+" ",
+" .... ",
+" ........ ",
+" .......... ",
+" .....XX..... ",
+" ...XXXXXX... ",
+" ....XXXXXX.... ",
+" ...XXXXXXXX... ",
+" ...XXXXXXXX... ",
+" ....XXXXXX.... ",
+" ...XXXXXX... ",
+" .....XX..... ",
+" .......... ",
+" ........ ",
+" .... ",
+" "};
diff --git a/kmid/button2.xpm b/kmid/button2.xpm
new file mode 100644
index 00000000..8c8922df
--- /dev/null
+++ b/kmid/button2.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * button2_xpm[] = {
+"16 16 3 1",
+" c None",
+". c #000000000000",
+"X c #0000FFFF0000",
+" ",
+" .... ",
+" ........ ",
+" .......... ",
+" .....XX..... ",
+" ...XXXXXX... ",
+" ....XXXXXX.... ",
+" ...XXXXXXXX... ",
+" ...XXXXXXXX... ",
+" ....XXXXXX.... ",
+" ...XXXXXX... ",
+" .....XX..... ",
+" .......... ",
+" ........ ",
+" .... ",
+" "};
diff --git a/kmid/channel.cpp b/kmid/channel.cpp
new file mode 100644
index 00000000..14dd95a9
--- /dev/null
+++ b/kmid/channel.cpp
@@ -0,0 +1,233 @@
+/**************************************************************************
+
+ channel.cpp - The KMidChannel widget (with pure virtual members)
+ Copyright (C) 1998 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <qpainter.h>
+#include <qcombobox.h>
+
+#include <kglobal.h>
+#include <kinstance.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include "kmidbutton.h"
+
+#include "channel.h"
+#include "instrname.h"
+#include <kiconloader.h>
+
+KMidChannel::KMidChannel(int chn,QWidget *parent) : QWidget (parent)
+{
+ channel=chn;
+ replay=TRUE;
+ int i;
+ for (i=0;i<128;i++) pressed[i]=0;
+ keyboard = QPixmap(locate("appdata","icons/keyboard.xpm"));
+ penB = new QPen (black);
+ penW = new QPen (white);
+ penT = new QPen (colorGroup().background());
+
+ KConfig *kcfg=KGlobal::instance()->config();
+ kcfg->setGroup("KMid");
+ QFont *qtextfontdefault=new QFont("lucida",18,QFont::Bold,TRUE);
+ qcvfont=new QFont(kcfg->readFontEntry("ChannelViewFont",qtextfontdefault));
+ delete qtextfontdefault;
+
+ instrumentCombo = new QComboBox(FALSE,this,"instr");
+ instrumentCombo->setGeometry(160,2,200,20);
+
+ for (i=0;i<128;i++)
+ instrumentCombo->insertItem(i18n(instrumentName[i]),i);
+
+ connect(instrumentCombo,SIGNAL(activated(int)),this,SLOT(pgmChanged(int)));
+
+ forcepgm=new KMidButton(this,"forcepgm");
+ forcepgm->setGeometry(135,4,16,16);
+ forcepgm->setToggleButton(TRUE);
+ button1 = QPixmap(locate("appdata","icons/button1.xpm"));
+ button2 = QPixmap(locate("appdata","icons/button2.xpm"));
+ forcepgm->setPixmaps(button1,button2);
+ forcepgm->show();
+ connect(forcepgm,SIGNAL(toggled(bool)),this,SLOT(changeForcedState(bool)));
+
+}
+
+KMidChannel::~KMidChannel()
+{
+ delete penB;
+ delete penW;
+ delete penT;
+}
+
+void KMidChannel::paintEvent( QPaintEvent * )
+{
+
+ QPainter *qpaint=new QPainter(this);
+
+ QString tmp = i18n("Channel %1").arg(channel);
+ qpaint->setFont(*qcvfont);
+ qpaint->setPen(*penB);
+ qpaint->drawText(2,20,tmp);
+ qpaint->setPen(*penW);
+ qpaint->drawText(0,18,tmp);
+
+ drawKeyboard(qpaint);
+ drawPressedKeys(qpaint);
+
+ delete qpaint;
+}
+
+void KMidChannel::drawKeyboard(QPainter *qpaint)
+{
+ int x=1;
+ for (int i=0;(i<12) && (x<width());i++,x+=63)
+ {
+ qpaint->drawPixmap(x,KEYBOARDY,keyboard);
+ };
+ qpaint->setPen(*penB);
+ qpaint->drawLine(0,KEYBOARDY,0,KEYBOARDY+44);
+
+ qpaint->setPen(*penT);
+ qpaint->drawLine(0,KEYBOARDY+45,x+63,KEYBOARDY+45);
+
+
+}
+
+void KMidChannel::drawPressedKeys(QPainter *qpaint)
+{
+ for (int i=0;i<128;i++)
+ if (pressed[i]) drawKey(qpaint,i);
+}
+
+void KMidChannel::drawKey(QPainter *qpaint,int key)
+{
+ int octave=key/12;
+ int note=key%12;
+ int x=octave*63+1;
+
+ switch (note)
+ {
+ case (0) : drawDo (qpaint,x,pressed[key]);break;
+ case (1) : drawDo__ (qpaint,x,pressed[key]);break;
+ case (2) : drawRe (qpaint,x,pressed[key]);break;
+ case (3) : drawRe__ (qpaint,x,pressed[key]);break;
+ case (4) : drawMi (qpaint,x,pressed[key]);break;
+ case (5) : drawFa (qpaint,x,pressed[key]);break;
+ case (6) : drawFa__ (qpaint,x,pressed[key]);break;
+ case (7) : drawSol (qpaint,x,pressed[key]);break;
+ case (8) : drawSol__(qpaint,x,pressed[key]);break;
+ case (9) : drawLa (qpaint,x,pressed[key]);break;
+ case (10) : drawLa__ (qpaint,x,pressed[key]);break;
+ case (11) : drawSi (qpaint,x,pressed[key]);break;
+ };
+// qpaint->flush();
+}
+
+void KMidChannel::noteOn(int key)
+{
+ pressed[key]=1;
+ QPainter *qpaint=new QPainter(this);
+ drawKey(qpaint,key);
+ delete qpaint;
+}
+
+void KMidChannel::noteOff(int key)
+{
+ pressed[key]=0;
+ QPainter *qpaint=new QPainter(this);
+ drawKey(qpaint,key);
+ delete qpaint;
+}
+
+void KMidChannel::changeInstrument(int pgm)
+{
+ instrumentCombo->setCurrentItem(pgm);
+}
+
+void KMidChannel::changeForceState(bool i)
+{
+ replay=FALSE;
+ forcepgm->setOn(i);
+ replay=TRUE;
+}
+
+void KMidChannel::reset(int level)
+{
+ for (int i=0;i<128;i++) pressed[i]=0;
+ if (level>=1)
+ {
+ instrumentCombo->setCurrentItem(0);
+ replay=FALSE;
+ forcepgm->setOn(FALSE);
+ replay=TRUE;
+ };
+
+ repaint(FALSE);
+}
+
+void KMidChannel::saveState(bool *p,int *pgm)
+{
+ for (int i=0;i<128;i++) p[i]=pressed[i];
+ *pgm=instrumentCombo->currentItem();
+}
+
+void KMidChannel::loadState(bool *p,int *pgm)
+{
+ for (int i=0;i<128;i++) pressed[i]=p[i];
+ instrumentCombo->setCurrentItem(*pgm);
+ repaint(FALSE);
+}
+
+void KMidChannel::pgmChanged(int i)
+{
+ int data[4];
+ data[0]=CHN_CHANGE_PGM;
+ data[1]=channel;
+ data[2]=i;
+ data[3]=0;
+
+ replay=FALSE;
+ forcepgm->setOn(TRUE);
+ replay=TRUE;
+
+ emit signalToKMidClient(data);
+
+
+}
+
+
+void KMidChannel::changeForcedState(bool i)
+{
+ int data[4];
+ data[0]=CHN_CHANGE_FORCED_STATE;
+ data[1]=channel;
+ data[2]=i;
+ data[3]=(replay)? 1 : 0;
+
+ emit signalToKMidClient(data);
+
+}
+#include "channel.moc"
diff --git a/kmid/channel.h b/kmid/channel.h
new file mode 100644
index 00000000..29ddfcd2
--- /dev/null
+++ b/kmid/channel.h
@@ -0,0 +1,104 @@
+/**************************************************************************
+
+ channel.h - The KMidChannel widget (with pure virtual members)
+ Copyright (C) 1998 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+
+#ifndef KMIDCHANNEL_H
+#define KMIDCHANNEL_H
+#include <qwidget.h>
+#include <qpixmap.h>
+
+#define CHANNELHEIGHT 71
+#define KEYBOARDY CHANNELHEIGHT-46
+
+#define CHN_CHANGE_PGM 1
+#define CHN_CHANGE_FORCED_STATE 2
+
+class QFont;
+class QComboBox;
+class KMidButton;
+class KMidChannel : public QWidget
+{
+ Q_OBJECT
+
+private:
+ bool pressed[128]; // The 128 keys
+ int channel;
+ bool replay; // Indicates if music should restart playing after
+ // changing the force state
+
+protected:
+ QPixmap keyboard;
+ QPixmap button1;
+ QPixmap button2;
+
+ QComboBox *instrumentCombo;
+
+ KMidButton *forcepgm;
+ QFont *qcvfont;
+
+ QPen *penB; // Black
+ QPen *penW; // White
+ QPen *penT; // "Transparent" for Background
+
+ void paintEvent( QPaintEvent * );
+public:
+ KMidChannel(int i,QWidget *Parent);
+ virtual ~KMidChannel();
+
+ void drawKeyboard(QPainter *qpaint);
+ void drawPressedKeys(QPainter *qpaint);
+ void drawKey(QPainter *qpaint,int key);
+
+ virtual void drawDo (QPainter *qpaint,int x,int p) = 0;
+ virtual void drawDo__ (QPainter *qpaint,int x,int p) = 0;
+ virtual void drawRe (QPainter *qpaint,int x,int p) = 0;
+ virtual void drawRe__ (QPainter *qpaint,int x,int p) = 0;
+ virtual void drawMi (QPainter *qpaint,int x,int p) = 0;
+ virtual void drawFa (QPainter *qpaint,int x,int p) = 0;
+ virtual void drawFa__ (QPainter *qpaint,int x,int p) = 0;
+ virtual void drawSol (QPainter *qpaint,int x,int p) = 0;
+ virtual void drawSol__(QPainter *qpaint,int x,int p) = 0;
+ virtual void drawLa (QPainter *qpaint,int x,int p) = 0;
+ virtual void drawLa__ (QPainter *qpaint,int x,int p) = 0;
+ virtual void drawSi (QPainter *qpaint,int x,int p) = 0;
+
+ void noteOn(int key);
+ void noteOff(int key);
+ void changeInstrument(int pgm);
+ void changeForceState(bool i);
+
+ void reset(int level=1); // 0 only release notes, 1 also set instr to 0 ...
+
+ void saveState(bool *p,int *pgm);
+ void loadState(bool *p,int *pgm);
+
+public slots:
+ void pgmChanged(int i);
+ void changeForcedState(bool);
+
+signals:
+ void signalToKMidClient(int *data);
+
+};
+
+#endif
diff --git a/kmid/channel3d.cpp b/kmid/channel3d.cpp
new file mode 100644
index 00000000..2a2451c6
--- /dev/null
+++ b/kmid/channel3d.cpp
@@ -0,0 +1,149 @@
+/**************************************************************************
+
+ channel3d.cpp - KMidChannel3D widget, a Channel widget with 3D look
+ Copyright (C) 1998 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+
+#include "channel3d.h"
+#include <qpainter.h>
+
+KMidChannel3D::KMidChannel3D (int chn,QWidget *parent) : KMidChannel(chn,parent)
+{
+}
+
+void KMidChannel3D::drawDo(QPainter *qpaint,int x,int p)
+{
+ qpaint->setPen(p?(*penW):(*penB));
+ qpaint->drawLine(x,KEYBOARDY+43,x+6,KEYBOARDY+43);
+ qpaint->drawLine(x,KEYBOARDY+44,x+6,KEYBOARDY+44);
+ qpaint->setPen(p?(*penB):(*penT));
+ qpaint->drawLine(x-1,KEYBOARDY+45,x+8,KEYBOARDY+45);
+ qpaint->setPen(p?(*penB):(*penW));
+ qpaint->drawLine(x+7,KEYBOARDY+28,x+7,KEYBOARDY+42);
+}
+void KMidChannel3D::drawDo__(QPainter *qpaint,int x,int p)
+{
+ qpaint->setPen(p?(*penB):(*penW));
+ qpaint->drawLine(x+6,KEYBOARDY+2,x+6,KEYBOARDY+25);
+ qpaint->drawLine(x+7,KEYBOARDY+25,x+11,KEYBOARDY+25);
+
+ qpaint->setPen(p?(*penW):(*penB));
+ qpaint->drawLine(x+11,KEYBOARDY+1,x+11,KEYBOARDY+27);
+ qpaint->drawPoint(x+10,KEYBOARDY+27);
+
+}
+void KMidChannel3D::drawRe(QPainter *qpaint,int x,int p)
+{
+ qpaint->setPen(p?(*penW):(*penB));
+ qpaint->drawLine(x+9,KEYBOARDY+43,x+15,KEYBOARDY+43);
+ qpaint->drawLine(x+9,KEYBOARDY+44,x+15,KEYBOARDY+44);
+ qpaint->setPen(p?(*penB):(*penT));
+ qpaint->drawLine(x+8,KEYBOARDY+45,x+17,KEYBOARDY+45);
+ qpaint->setPen(p?(*penB):(*penW));
+ qpaint->drawLine(x+16,KEYBOARDY+28,x+16,KEYBOARDY+42);
+}
+void KMidChannel3D::drawRe__(QPainter *qpaint,int x,int p)
+{
+ qpaint->setPen(p?(*penB):(*penW));
+ qpaint->drawLine(x+15,KEYBOARDY+2,x+15,KEYBOARDY+25);
+ qpaint->drawLine(x+16,KEYBOARDY+25,x+20,KEYBOARDY+25);
+
+ qpaint->setPen(p?(*penW):(*penB));
+ qpaint->drawLine(x+20,KEYBOARDY+1,x+20,KEYBOARDY+27);
+ qpaint->drawPoint(x+19,KEYBOARDY+27);
+}
+void KMidChannel3D::drawMi(QPainter *qpaint,int x,int p)
+{
+ qpaint->setPen(p?(*penW):(*penB));
+ qpaint->drawLine(x+18,KEYBOARDY+43,x+24,KEYBOARDY+43);
+ qpaint->drawLine(x+18,KEYBOARDY+44,x+24,KEYBOARDY+44);
+ qpaint->setPen(p?(*penB):(*penT));
+ qpaint->drawLine(x+17,KEYBOARDY+45,x+26,KEYBOARDY+45);
+ qpaint->setPen(p?(*penB):(*penW));
+ qpaint->drawLine(x+25,KEYBOARDY+1,x+25,KEYBOARDY+42);
+}
+void KMidChannel3D::drawFa(QPainter *qpaint,int x,int p)
+{
+ qpaint->setPen(p?(*penW):(*penB));
+ qpaint->drawLine(x+27,KEYBOARDY+43,x+33,KEYBOARDY+43);
+ qpaint->drawLine(x+27,KEYBOARDY+44,x+33,KEYBOARDY+44);
+ qpaint->setPen(p?(*penB):(*penT));
+ qpaint->drawLine(x+26,KEYBOARDY+45,x+35,KEYBOARDY+45);
+ qpaint->setPen(p?(*penB):(*penW));
+ qpaint->drawLine(x+34,KEYBOARDY+28,x+34,KEYBOARDY+42);
+}
+void KMidChannel3D::drawFa__(QPainter *qpaint,int x,int p)
+{
+ qpaint->setPen(p?(*penB):(*penW));
+ qpaint->drawLine(x+33,KEYBOARDY+2,x+33,KEYBOARDY+25);
+ qpaint->drawLine(x+34,KEYBOARDY+25,x+38,KEYBOARDY+25);
+ qpaint->setPen(p?(*penW):(*penB));
+ qpaint->drawLine(x+38,KEYBOARDY+1,x+38,KEYBOARDY+27);
+ qpaint->drawPoint(x+37,KEYBOARDY+27);
+}
+void KMidChannel3D::drawSol(QPainter *qpaint,int x,int p)
+{
+ qpaint->setPen(p?(*penW):(*penB));
+ qpaint->drawLine(x+36,KEYBOARDY+43,x+42,KEYBOARDY+43);
+ qpaint->drawLine(x+36,KEYBOARDY+44,x+42,KEYBOARDY+44);
+ qpaint->setPen(p?(*penB):(*penT));
+ qpaint->drawLine(x+35,KEYBOARDY+45,x+44,KEYBOARDY+45);
+ qpaint->setPen(p?(*penB):(*penW));
+ qpaint->drawLine(x+43,KEYBOARDY+28,x+43,KEYBOARDY+42);
+}
+void KMidChannel3D::drawSol__(QPainter *qpaint,int x,int p)
+{
+ qpaint->setPen(p?(*penB):(*penW));
+ qpaint->drawLine(x+42,KEYBOARDY+2,x+42,KEYBOARDY+25);
+ qpaint->drawLine(x+43,KEYBOARDY+25,x+47,KEYBOARDY+25);
+ qpaint->setPen(p?(*penW):(*penB));
+ qpaint->drawLine(x+47,KEYBOARDY+1,x+47,KEYBOARDY+27);
+ qpaint->drawPoint(x+46,KEYBOARDY+27);
+}
+void KMidChannel3D::drawLa(QPainter *qpaint,int x,int p)
+{
+ qpaint->setPen(p?(*penW):(*penB));
+ qpaint->drawLine(x+45,KEYBOARDY+43,x+51,KEYBOARDY+43);
+ qpaint->drawLine(x+45,KEYBOARDY+44,x+51,KEYBOARDY+44);
+ qpaint->setPen(p?(*penB):(*penT));
+ qpaint->drawLine(x+44,KEYBOARDY+45,x+53,KEYBOARDY+45);
+ qpaint->setPen(p?(*penB):(*penW));
+ qpaint->drawLine(x+52,KEYBOARDY+28,x+52,KEYBOARDY+42);
+}
+void KMidChannel3D::drawLa__(QPainter *qpaint,int x,int p)
+{
+ qpaint->setPen(p?(*penB):(*penW));
+ qpaint->drawLine(x+51,KEYBOARDY+2,x+51,KEYBOARDY+25);
+ qpaint->drawLine(x+52,KEYBOARDY+25,x+56,KEYBOARDY+25);
+ qpaint->setPen(p?(*penW):(*penB));
+ qpaint->drawLine(x+56,KEYBOARDY+1,x+56,KEYBOARDY+27);
+ qpaint->drawPoint(x+55,KEYBOARDY+27);
+}
+void KMidChannel3D::drawSi(QPainter *qpaint,int x,int p)
+{
+ qpaint->setPen(p?(*penW):(*penB));
+ qpaint->drawLine(x+54,KEYBOARDY+43,x+60,KEYBOARDY+43);
+ qpaint->drawLine(x+54,KEYBOARDY+44,x+60,KEYBOARDY+44);
+ qpaint->setPen(p?(*penB):(*penT));
+ qpaint->drawLine(x+53,KEYBOARDY+45,x+62,KEYBOARDY+45);
+ qpaint->setPen(p?(*penB):(*penW));
+ qpaint->drawLine(x+61,KEYBOARDY+1,x+61,KEYBOARDY+42);
+}
diff --git a/kmid/channel3d.h b/kmid/channel3d.h
new file mode 100644
index 00000000..e0bfcc67
--- /dev/null
+++ b/kmid/channel3d.h
@@ -0,0 +1,52 @@
+/**************************************************************************
+
+ channel3d.h - KMidChannel3D widget, a Channel widget with 3D look
+ Copyright (C) 1998 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+
+#ifndef KMIDCHANNEL3D_H
+#define KMIDCHANNEL3D_H
+
+#include "channel.h"
+
+class KMidChannel3D : public KMidChannel
+{
+public:
+ KMidChannel3D(int chn,QWidget *parent);
+
+
+ virtual void drawDo (QPainter *qpaint,int x,int p);
+ virtual void drawDo__ (QPainter *qpaint,int x,int p);
+ virtual void drawRe (QPainter *qpaint,int x,int p);
+ virtual void drawRe__ (QPainter *qpaint,int x,int p);
+ virtual void drawMi (QPainter *qpaint,int x,int p);
+ virtual void drawFa (QPainter *qpaint,int x,int p);
+ virtual void drawFa__ (QPainter *qpaint,int x,int p);
+ virtual void drawSol (QPainter *qpaint,int x,int p);
+ virtual void drawSol__(QPainter *qpaint,int x,int p);
+ virtual void drawLa (QPainter *qpaint,int x,int p);
+ virtual void drawLa__ (QPainter *qpaint,int x,int p);
+ virtual void drawSi (QPainter *qpaint,int x,int p);
+
+
+};
+
+#endif
diff --git a/kmid/channel4d.cpp b/kmid/channel4d.cpp
new file mode 100644
index 00000000..a05cc5df
--- /dev/null
+++ b/kmid/channel4d.cpp
@@ -0,0 +1,232 @@
+/**************************************************************************
+
+ channel4d.cpp - KMidChannel4D widget, with 3D look and filled
+ Copyright (C) 1998 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+
+#include "channel4d.h"
+#include <qpainter.h>
+
+KMidChannel4D::KMidChannel4D (int chn,QWidget *parent) : KMidChannel(chn,parent)
+{
+ brushR = new QBrush (red);
+ brushW = new QBrush (white);
+ brushB = new QBrush (black);
+ penR = new QPen (red);
+}
+
+
+void KMidChannel4D::drawDo(QPainter *qpaint,int x,int p)
+{
+ qpaint->setPen(p?(*penR):(*penB));
+
+ qpaint->drawLine(x,KEYBOARDY+43,x+6,KEYBOARDY+43);
+ qpaint->drawLine(x,KEYBOARDY+44,x+6,KEYBOARDY+44);
+
+ qpaint->setPen(p?(*penB):(*penT));
+ qpaint->drawLine(x-1,KEYBOARDY+45,x+8,KEYBOARDY+45);
+ qpaint->setPen(p?(*penB):(*penW));
+ qpaint->drawLine(x+7,KEYBOARDY+28,x+7,KEYBOARDY+42);
+
+ qpaint->fillRect(x,KEYBOARDY+28,7,15,((p)?*brushR:*brushW));
+ qpaint->fillRect(x,KEYBOARDY+1,5,27,((p)?*brushR:*brushW));
+ qpaint->setPen(p?(*penR):(*penW));
+ qpaint->drawPoint(x+5,KEYBOARDY+27);
+}
+void KMidChannel4D::drawDo__(QPainter *qpaint,int x,int p)
+{
+ qpaint->fillRect(x+6,KEYBOARDY+1,5,26,((p)?*brushR:*brushB));
+
+ if (!p)
+ {
+ qpaint->setPen(*penW);
+ qpaint->drawLine(x+6,KEYBOARDY+2,x+6,KEYBOARDY+25);
+ qpaint->drawLine(x+7,KEYBOARDY+25,x+11,KEYBOARDY+25);
+ };
+
+ qpaint->setPen(p?(*penW):(*penB));
+ qpaint->drawLine(x+11,KEYBOARDY+1,x+11,KEYBOARDY+27);
+ qpaint->drawPoint(x+10,KEYBOARDY+27);
+
+
+}
+void KMidChannel4D::drawRe(QPainter *qpaint,int x,int p)
+{
+ qpaint->setPen(p?(*penR):(*penB));
+ qpaint->drawLine(x+9,KEYBOARDY+43,x+15,KEYBOARDY+43);
+ qpaint->drawLine(x+9,KEYBOARDY+44,x+15,KEYBOARDY+44);
+ qpaint->setPen(p?(*penB):(*penT));
+ qpaint->drawLine(x+8,KEYBOARDY+45,x+17,KEYBOARDY+45);
+ qpaint->setPen(p?(*penB):(*penW));
+ qpaint->drawLine(x+16,KEYBOARDY+28,x+16,KEYBOARDY+42);
+
+ qpaint->fillRect(x+9,KEYBOARDY+28,7,15,((p)?*brushR:*brushW));
+ qpaint->fillRect(x+12,KEYBOARDY+1,2,27,((p)?*brushR:*brushW));
+ qpaint->setPen(p?(*penR):(*penW));
+ qpaint->drawPoint(x+11,KEYBOARDY+27);
+ qpaint->drawPoint(x+14,KEYBOARDY+27);
+
+}
+
+void KMidChannel4D::drawRe__(QPainter *qpaint,int x,int p)
+{
+ qpaint->fillRect(x+15,KEYBOARDY+1,5,26,((p)?*brushR:*brushB));
+
+ if (!p)
+ {
+ qpaint->setPen(*penW);
+ qpaint->drawLine(x+15,KEYBOARDY+2,x+15,KEYBOARDY+25);
+ qpaint->drawLine(x+16,KEYBOARDY+25,x+20,KEYBOARDY+25);
+ };
+
+ qpaint->setPen(p?(*penW):(*penB));
+ qpaint->drawLine(x+20,KEYBOARDY+1,x+20,KEYBOARDY+27);
+ qpaint->drawPoint(x+19,KEYBOARDY+27);
+
+
+}
+void KMidChannel4D::drawMi(QPainter *qpaint,int x,int p)
+{
+ qpaint->setPen(p?(*penR):(*penB));
+ qpaint->drawLine(x+18,KEYBOARDY+43,x+24,KEYBOARDY+43);
+ qpaint->drawLine(x+18,KEYBOARDY+44,x+24,KEYBOARDY+44);
+ qpaint->setPen(p?(*penB):(*penT));
+ qpaint->drawLine(x+17,KEYBOARDY+45,x+26,KEYBOARDY+45);
+ qpaint->setPen(p?(*penB):(*penW));
+ qpaint->drawLine(x+25,KEYBOARDY+1,x+25,KEYBOARDY+42);
+
+ qpaint->fillRect(x+18,KEYBOARDY+28,7,15,((p)?*brushR:*brushW));
+ qpaint->fillRect(x+21,KEYBOARDY+1,4,27,((p)?*brushR:*brushW));
+ qpaint->setPen(p?(*penR):(*penW));
+ qpaint->drawPoint(x+20,KEYBOARDY+27);
+
+}
+void KMidChannel4D::drawFa(QPainter *qpaint,int x,int p)
+{
+ qpaint->setPen(p?(*penR):(*penB));
+ qpaint->drawLine(x+27,KEYBOARDY+43,x+33,KEYBOARDY+43);
+ qpaint->drawLine(x+27,KEYBOARDY+44,x+33,KEYBOARDY+44);
+ qpaint->setPen(p?(*penB):(*penT));
+ qpaint->drawLine(x+26,KEYBOARDY+45,x+35,KEYBOARDY+45);
+ qpaint->setPen(p?(*penB):(*penW));
+ qpaint->drawLine(x+34,KEYBOARDY+28,x+34,KEYBOARDY+42);
+
+ qpaint->fillRect(x+27,KEYBOARDY+28,7,15,((p)?*brushR:*brushW));
+ qpaint->fillRect(x+27,KEYBOARDY+1,5,27,((p)?*brushR:*brushW));
+ qpaint->setPen(p?(*penR):(*penW));
+ qpaint->drawPoint(x+32,KEYBOARDY+27);
+
+}
+void KMidChannel4D::drawFa__(QPainter *qpaint,int x,int p)
+{
+ qpaint->fillRect(x+33,KEYBOARDY+1,5,26,((p)?*brushR:*brushB));
+ if (!p)
+ {
+ qpaint->setPen(*penW);
+ qpaint->drawLine(x+33,KEYBOARDY+2,x+33,KEYBOARDY+25);
+ qpaint->drawLine(x+34,KEYBOARDY+25,x+38,KEYBOARDY+25);
+ };
+ qpaint->setPen(p?(*penW):(*penB));
+ qpaint->drawLine(x+38,KEYBOARDY+1,x+38,KEYBOARDY+27);
+ qpaint->drawPoint(x+37,KEYBOARDY+27);
+
+}
+
+void KMidChannel4D::drawSol(QPainter *qpaint,int x,int p)
+{
+ qpaint->setPen(p?(*penR):(*penB));
+ qpaint->drawLine(x+36,KEYBOARDY+43,x+42,KEYBOARDY+43);
+ qpaint->drawLine(x+36,KEYBOARDY+44,x+42,KEYBOARDY+44);
+ qpaint->setPen(p?(*penB):(*penT));
+ qpaint->drawLine(x+35,KEYBOARDY+45,x+44,KEYBOARDY+45);
+ qpaint->setPen(p?(*penB):(*penW));
+ qpaint->drawLine(x+43,KEYBOARDY+28,x+43,KEYBOARDY+42);
+
+ qpaint->fillRect(x+36,KEYBOARDY+28,7,15,((p)?*brushR:*brushW));
+ qpaint->fillRect(x+39,KEYBOARDY+1,2,27,((p)?*brushR:*brushW));
+ qpaint->setPen(p?(*penR):(*penW));
+ qpaint->drawPoint(x+38,KEYBOARDY+27);
+ qpaint->drawPoint(x+41,KEYBOARDY+27);
+
+}
+
+void KMidChannel4D::drawSol__(QPainter *qpaint,int x,int p)
+{
+ qpaint->fillRect(x+42,KEYBOARDY+1,5,26,((p)?*brushR:*brushB));
+
+ if (!p)
+ {
+ qpaint->setPen(*penW);
+ qpaint->drawLine(x+42,KEYBOARDY+2,x+42,KEYBOARDY+25);
+ qpaint->drawLine(x+43,KEYBOARDY+25,x+47,KEYBOARDY+25);
+ };
+ qpaint->setPen(p?(*penW):(*penB));
+ qpaint->drawLine(x+47,KEYBOARDY+1,x+47,KEYBOARDY+27);
+ qpaint->drawPoint(x+46,KEYBOARDY+27);
+
+
+}
+void KMidChannel4D::drawLa(QPainter *qpaint,int x,int p)
+{
+ qpaint->setPen(p?(*penR):(*penB));
+ qpaint->drawLine(x+45,KEYBOARDY+43,x+51,KEYBOARDY+43);
+ qpaint->drawLine(x+45,KEYBOARDY+44,x+51,KEYBOARDY+44);
+ qpaint->setPen(p?(*penB):(*penT));
+ qpaint->drawLine(x+44,KEYBOARDY+45,x+53,KEYBOARDY+45);
+ qpaint->setPen(p?(*penB):(*penW));
+ qpaint->drawLine(x+52,KEYBOARDY+28,x+52,KEYBOARDY+42);
+
+ qpaint->fillRect(x+45,KEYBOARDY+28,7,15,((p)?*brushR:*brushW));
+ qpaint->fillRect(x+48,KEYBOARDY+1,2,27,((p)?*brushR:*brushW));
+ qpaint->setPen(p?(*penR):(*penW));
+ qpaint->drawPoint(x+47,KEYBOARDY+27);
+ qpaint->drawPoint(x+50,KEYBOARDY+27);
+}
+void KMidChannel4D::drawLa__(QPainter *qpaint,int x,int p)
+{
+ qpaint->fillRect(x+51,KEYBOARDY+1,5,26,((p)?*brushR:*brushB));
+
+ if (!p)
+ {
+ qpaint->setPen(*penW);
+ qpaint->drawLine(x+51,KEYBOARDY+2,x+51,KEYBOARDY+25);
+ qpaint->drawLine(x+52,KEYBOARDY+25,x+56,KEYBOARDY+25);
+ };
+ qpaint->setPen(p?(*penW):(*penB));
+ qpaint->drawLine(x+56,KEYBOARDY+1,x+56,KEYBOARDY+27);
+ qpaint->drawPoint(x+55,KEYBOARDY+27);
+
+}
+void KMidChannel4D::drawSi(QPainter *qpaint,int x,int p)
+{
+ qpaint->setPen(p?(*penR):(*penB));
+ qpaint->drawLine(x+54,KEYBOARDY+43,x+60,KEYBOARDY+43);
+ qpaint->drawLine(x+54,KEYBOARDY+44,x+60,KEYBOARDY+44);
+ qpaint->setPen(p?(*penB):(*penT));
+ qpaint->drawLine(x+53,KEYBOARDY+45,x+62,KEYBOARDY+45);
+ qpaint->setPen(p?(*penB):(*penW));
+ qpaint->drawLine(x+61,KEYBOARDY+1,x+61,KEYBOARDY+42);
+
+ qpaint->fillRect(x+54,KEYBOARDY+28,7,15,((p)?*brushR:*brushW));
+ qpaint->fillRect(x+57,KEYBOARDY+1,4,27,((p)?*brushR:*brushW));
+ qpaint->setPen(p?(*penR):(*penW));
+ qpaint->drawPoint(x+56,KEYBOARDY+27);
+}
diff --git a/kmid/channel4d.h b/kmid/channel4d.h
new file mode 100644
index 00000000..b97dc0e1
--- /dev/null
+++ b/kmid/channel4d.h
@@ -0,0 +1,58 @@
+/**************************************************************************
+
+ channel4d.h - KMidChannel4D widget, with 3D look and filled
+ Copyright (C) 1998 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+
+#ifndef KMIDCHANNEL4D_H
+#define KMIDCHANNEL4D_H
+
+#include "channel.h"
+
+class KMidChannel4D : public KMidChannel
+{
+private:
+ QBrush *brushR;
+ QBrush *brushW;
+ QBrush *brushB;
+ QPen *penR;
+
+public:
+ KMidChannel4D(int chn,QWidget *parent);
+
+
+ virtual void drawDo (QPainter *qpaint,int x,int p);
+ virtual void drawDo__ (QPainter *qpaint,int x,int p);
+ virtual void drawRe (QPainter *qpaint,int x,int p);
+ virtual void drawRe__ (QPainter *qpaint,int x,int p);
+ virtual void drawMi (QPainter *qpaint,int x,int p);
+ virtual void drawFa (QPainter *qpaint,int x,int p);
+ virtual void drawFa__ (QPainter *qpaint,int x,int p);
+ virtual void drawSol (QPainter *qpaint,int x,int p);
+ virtual void drawSol__(QPainter *qpaint,int x,int p);
+ virtual void drawLa (QPainter *qpaint,int x,int p);
+ virtual void drawLa__ (QPainter *qpaint,int x,int p);
+ virtual void drawSi (QPainter *qpaint,int x,int p);
+
+
+};
+
+#endif
diff --git a/kmid/channelcfgdlg.cpp b/kmid/channelcfgdlg.cpp
new file mode 100644
index 00000000..8ed5ee66
--- /dev/null
+++ b/kmid/channelcfgdlg.cpp
@@ -0,0 +1,39 @@
+#include <qpushbutton.h>
+#include <qbuttongroup.h>
+#include <qradiobutton.h>
+#include <kdialogbase.h>
+
+#include <kapplication.h>
+#include <klocale.h>
+
+#include "channelview.h"
+#include "channelcfgdlg.h"
+#include "version.h"
+#include <qlayout.h>
+#include <qvbuttongroup.h>
+
+ChannelViewConfigDialog::ChannelViewConfigDialog(QWidget *parent,const char *name) : KDialogBase(parent,name,TRUE,i18n("Configure Channel View"),Ok|Cancel, Ok)
+{
+ QWidget *page = new QWidget( this );
+ setMainWidget(page);
+ QVBoxLayout *topLayout = new QVBoxLayout( page, 0, spacingHint() );
+ qbg = new QVButtonGroup(i18n("Choose Look Mode"),page,"qbg");
+
+ rb0=new QRadioButton(i18n("3D look"),qbg,"3d");
+ rb1=new QRadioButton(i18n("3D - filled"),qbg,"4d");
+
+ qbg->setExclusive(TRUE);
+
+ topLayout->addWidget( qbg );
+ ((ChannelView::lookMode()==0)?rb0:rb1)->setChecked(TRUE);
+
+ connect (qbg, SIGNAL(pressed(int)),this,SLOT(modeselected(int)));
+}
+
+void ChannelViewConfigDialog::modeselected(int idx)
+{
+selectedmode=idx;
+}
+
+int ChannelViewConfigDialog::selectedmode;
+#include "channelcfgdlg.moc"
diff --git a/kmid/channelcfgdlg.h b/kmid/channelcfgdlg.h
new file mode 100644
index 00000000..da7b7f82
--- /dev/null
+++ b/kmid/channelcfgdlg.h
@@ -0,0 +1,51 @@
+/**************************************************************************
+
+ channelcfgdlg.h - The channel view config dialog
+ Copyright (C) 1998 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#ifndef _channelcfgdlg_h_
+#define _channelcfgdlg_h_
+
+#include <kdialogbase.h>
+
+class QVButtonGroup;
+class QRadioButton;
+class ChannelViewConfigDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+
+ ChannelViewConfigDialog(QWidget *parent,const char *name);
+
+private:
+ QVButtonGroup *qbg;
+ QRadioButton *rb0;
+ QRadioButton *rb1;
+
+public slots:
+ void modeselected(int idx);
+
+public:
+ static int selectedmode;
+};
+
+#endif
diff --git a/kmid/channelview.cpp b/kmid/channelview.cpp
new file mode 100644
index 00000000..e40857dc
--- /dev/null
+++ b/kmid/channelview.cpp
@@ -0,0 +1,165 @@
+/**************************************************************************
+
+ channelview.cpp - The ChannelView dialog
+ Copyright (C) 1998 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+
+#include <kapplication.h>
+#include <klocale.h>
+
+#include "channelview.h"
+#include "channel3d.h"
+#include "channel4d.h"
+#include <kconfig.h>
+
+
+ChannelView::ChannelView(void) : KMainWindow(0, "ChannelView")
+{
+ setCaption(i18n("Channel View"));
+ for (int i=0;i<16;i++)
+ {
+ if (lookMode()==0)
+ Channel[i]=new KMidChannel3D(i+1,this);
+ else
+ Channel[i]=new KMidChannel4D(i+1,this);
+ connect(Channel[i],SIGNAL(signalToKMidClient(int *)),this,SLOT(slottokmidclient(int *)));
+ Channel[i]->setGeometry(5,5+i*CHANNELHEIGHT,width()-20,CHANNELHEIGHT);
+ Channel[i]->show();
+ }
+ scrollbar=new QScrollBar(1,16,1,1,1,QScrollBar::Vertical,this,"Channelscrollbar");
+ connect(scrollbar,SIGNAL(valueChanged(int)),this,SLOT(ScrollChn(int)));
+ setScrollBarRange();
+}
+
+ChannelView::~ChannelView()
+{
+
+}
+
+void ChannelView::closeEvent(QCloseEvent *e)
+{
+ emit destroyMe();
+ e->accept();
+}
+
+void ChannelView::resizeEvent(QResizeEvent *)
+{
+ scrollbar->setGeometry(width()-16,0,16,height());
+ for (int i=0;i<16;i++)
+ {
+ Channel[i]->setGeometry(5,5+(i-(scrollbar->value()-1))*CHANNELHEIGHT,width()-20,CHANNELHEIGHT);
+ }
+ setScrollBarRange();
+
+}
+
+void ChannelView::setScrollBarRange(void)
+{
+ nvisiblechannels=height()/CHANNELHEIGHT;
+ if (nvisiblechannels<16)
+ scrollbar->setRange(1,16-nvisiblechannels+1);
+ else
+ scrollbar->setRange(1,1);
+}
+
+void ChannelView::ScrollChn(int v)
+{
+ for (int i=0;i<16;i++)
+ {
+ Channel[i]->move(5,5+(i-(v-1))*CHANNELHEIGHT);
+ }
+}
+
+void ChannelView::noteOn(int chn,int note)
+{
+ Channel[chn]->noteOn(note);
+}
+
+void ChannelView::noteOff(int chn,int note)
+{
+ Channel[chn]->noteOff(note);
+}
+
+void ChannelView::changeInstrument(int chn,int pgm)
+{
+ Channel[chn]->changeInstrument(pgm);
+}
+
+void ChannelView::changeForceState(int chn,bool i)
+{
+ Channel[chn]->changeForceState(i);
+}
+
+
+void ChannelView::reset(int level)
+{
+ for (int i=0;i<16;i++)
+ {
+ Channel[i]->reset(level);
+ }
+}
+
+int ChannelView::lookmode=0;
+
+int ChannelView::lookMode(void)
+{
+ KConfig *kcfg=(KApplication::kApplication())->config();
+
+ kcfg->setGroup("KMid");
+ lookmode=kcfg->readNumEntry("ChannelViewLookMode",0);
+
+ return lookmode;
+}
+
+void ChannelView::lookMode(int i)
+{
+ KConfig *kcfg=(KApplication::kApplication())->config();
+
+ lookmode=i;
+
+ kcfg->setGroup("KMid");
+ kcfg->writeEntry("ChannelViewLookMode",lookmode);
+
+ bool tmp[128];
+ int pgm;
+ for (int i=0;i<16;i++)
+ {
+ Channel[i]->saveState(tmp,&pgm);
+ delete Channel[i];
+
+ if (lookmode==0)
+ Channel[i]=new KMidChannel3D(i+1,this);
+ else
+ Channel[i]=new KMidChannel4D(i+1,this);
+
+ connect(Channel[i],SIGNAL(signalToKMidClient(int *)),this,SLOT(slottokmidclient(int *)));
+ Channel[i]->setGeometry(5,5+(i-(scrollbar->value()-1))*CHANNELHEIGHT,width()-20,CHANNELHEIGHT);
+ Channel[i]->loadState(tmp,&pgm);
+ Channel[i]->show();
+ }
+
+}
+
+void ChannelView::slottokmidclient(int *data)
+{
+ emit signalToKMidClient(data);
+}
+#include "channelview.moc"
diff --git a/kmid/channelview.h b/kmid/channelview.h
new file mode 100644
index 00000000..9534e73a
--- /dev/null
+++ b/kmid/channelview.h
@@ -0,0 +1,76 @@
+/**************************************************************************
+
+ channelview.h - The ChannelView dialog
+ Copyright (C) 1998 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+
+#ifndef CHANNELVIEW_H
+#define CHANNELVIEW_H
+
+#include <kmainwindow.h>
+#include <qscrollbar.h>
+#include "channel.h"
+
+
+class ChannelView : public KMainWindow
+{
+ Q_OBJECT
+private:
+ KMidChannel *Channel[16];
+ int nvisiblechannels;
+
+ virtual void resizeEvent(QResizeEvent *);
+ virtual void closeEvent(QCloseEvent *e);
+
+public:
+ ChannelView(void);
+ virtual ~ChannelView();
+
+ void setScrollBarRange(void);
+
+ void noteOn(int chn,int note);
+ void noteOff(int chn,int note);
+ void changeInstrument(int chn,int pgm);
+ void changeForceState(int chn,bool i);
+
+ void reset(int level=1);
+
+ static int lookMode(void);
+
+ void lookMode(int i);
+
+public slots:
+ void ScrollChn(int i);
+ void slottokmidclient(int *data);
+
+signals:
+ void destroyMe();
+ void signalToKMidClient(int *data);
+
+private:
+ QScrollBar *scrollbar;
+
+
+ static int lookmode;
+
+
+};
+#endif
diff --git a/kmid/collectdlg.cpp b/kmid/collectdlg.cpp
new file mode 100644
index 00000000..fb79667b
--- /dev/null
+++ b/kmid/collectdlg.cpp
@@ -0,0 +1,307 @@
+/**************************************************************************
+
+ collectdlg.cpp - The collections manager dialog
+ Copyright (C) 1998 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#include <qpushbutton.h>
+#include <qlistbox.h>
+#include <qlabel.h>
+
+#include <kapplication.h>
+#include <kfiledialog.h>
+#include <klocale.h>
+#include <kinputdialog.h>
+#include <kmessagebox.h>
+#include <kurl.h>
+#include <kpushbutton.h>
+#include <kstdguiitem.h>
+#include "collectdlg.h"
+#include "version.h"
+#include "songlist.h"
+#include "slman.h"
+
+CollectionDialog::CollectionDialog(SLManager *slm,int selC,QWidget *parent,const char *name) : QDialog(parent,name,TRUE)
+{
+setCaption(i18n("Collections Manager"));
+ok=new KPushButton(KStdGuiItem::ok(),this);
+ok->setGeometry(140,200,100,30);
+connect(ok,SIGNAL(clicked()),SLOT(accept()) );
+cancel=new KPushButton(KStdGuiItem::cancel(),this);
+cancel->setGeometry(250,200,100,30);
+connect(cancel,SIGNAL(clicked()),SLOT(reject()) );
+
+label=new QLabel(i18n("Available collections:"),this);
+label->adjustSize();
+label->move(10,10);
+collections=new QListBox(this,"collectionlist");
+collections->setGeometry(10,20+label->height(),340,90);
+connect(collections,SIGNAL(highlighted(int)),SLOT(collectionselected(int)));
+connect(collections,SIGNAL(selected(int)),SLOT(changeCollectionName(int)));
+slman=slm;
+for (int i=0;i<=slman->numberOfCollections();i++)
+ {
+ collections->insertItem(i18n( slman->getCollectionName(i) ),i);
+#ifdef COLLECTDLGDEBUG
+ printf("Name : %s\n",slman->getCollectionName(i));
+#endif
+ };
+selectedC=selC;
+#ifdef COLLECTDLGDEBUG
+printf("selectedC : %d\n",selectedC);
+#endif
+
+label2=new QLabel(i18n("Songs in selected collection:"),this);
+label2->adjustSize();
+label2->move(10,collections->y()+collections->height()+10);
+
+songs=new QListBox(this,"songlist");
+songs->setGeometry(10,label2->y()+label2->height()+10,340,120);
+connect(songs,SIGNAL(highlighted(int)),SLOT(songselected(int)));
+currentsl=slman->getCollection(selectedC);
+if (slman->numberOfCollections()>0)
+ {
+ collections->setCurrentItem(selectedC);
+ collections->centerCurrentItem();
+ };
+//fillInSongList();
+newC=new QPushButton(i18n("&New..."),this);
+newC->adjustSize();
+newC->move(360,collections->y()+5);
+connect(newC,SIGNAL(clicked()),SLOT(newCollection()) );
+copyC=new QPushButton(i18n("&Copy..."),this);
+copyC->adjustSize();
+copyC->move(360,newC->y()+newC->height()+5);
+connect(copyC,SIGNAL(clicked()),SLOT(copyCollection()) );
+deleteC=new QPushButton(i18n("Delete"),this);
+deleteC->adjustSize();
+deleteC->move(360,copyC->y()+copyC->height()+5);
+connect(deleteC,SIGNAL(clicked()),SLOT(deleteCollection()) );
+
+addS=new QPushButton(i18n("&Add..."),this);
+addS->adjustSize();
+addS->move(360,songs->y()+5);
+connect(addS,SIGNAL(clicked()),SLOT(addSong()) );
+delS=new QPushButton(i18n("&Remove"),this);
+delS->adjustSize();
+delS->move(360,addS->y()+addS->height()+5);
+connect(delS,SIGNAL(clicked()),SLOT(removeSong()) );
+
+ok->move(ok->x(),songs->y()+songs->height()+10);
+cancel->move(ok->x()+ok->width()+5,ok->y());
+
+setMinimumSize(400,ok->y()+ok->height()+5);
+//setMaximumSize(360,240);
+}
+
+void CollectionDialog::collectionselected(int idx)
+{
+selectedC=idx;
+#ifdef COLLECTDLGDEBUG
+printf("Selected collection: %d\n",selectedC);
+#endif
+currentsl=slman->getCollection(selectedC);
+fillInSongList();
+}
+
+void CollectionDialog::fillInSongList(void)
+{
+QString qs;
+songs->clear();
+if (currentsl!=NULL)
+ {
+ currentsl->iteratorStart();
+ int i=0;
+ while (!currentsl->iteratorAtEnd())
+ {
+ qs=QString(currentsl->getIteratorName());
+ //KURL::decode(qs);
+ songs->insertItem(qs,i);
+ currentsl->iteratorNext();
+ i++;
+ };
+ songs->setCurrentItem(currentsl->getActiveSongID()-1);
+ songs->centerCurrentItem();
+ };
+}
+
+void CollectionDialog::songselected(int idx)
+{
+selectedS=idx;
+currentsl->setActiveSong(idx+1);
+
+#ifdef COLLECTDLGDEBUG
+printf("Selected song: %d\n",selectedS);
+#endif
+}
+
+void CollectionDialog::newCollection()
+{
+ bool ok;
+ QString name = KInputDialog::getText( i18n( "New Collection" ),
+ i18n( "Enter the name of the new collection:" ), QString::null,
+ &ok, this );
+
+ if (ok)
+ {
+ int i=slman->createCollection(name.ascii());
+ if (i==-1)
+ {
+ QString s = i18n("The name '%1' is already used").arg(name);
+ KMessageBox::sorry(this, s);
+ }
+ else
+ {
+ collections->insertItem(name,i);
+ collections->setCurrentItem(i);
+ collections->centerCurrentItem();
+ };
+ };
+}
+
+void CollectionDialog::copyCollection()
+{
+SongList *src=currentsl;
+int i;
+ bool ok;
+ QString name = KInputDialog::getText( i18n( "Copy Collection" ),
+ i18n( "Enter the name of the copy collection:" ), QString::null,
+ &ok, this );
+
+ if (ok)
+ {
+ i=slman->createCollection(name.ascii());
+ if (i==-1)
+ {
+ QString s = i18n("The name '%1' is already used").arg(name);
+ KMessageBox::sorry(this, s);
+ }
+ else
+ {
+ collections->insertItem(name,i);
+ SongList *tgt=slman->getCollection(i);
+ src->iteratorStart();
+ while (!src->iteratorAtEnd())
+ {
+ tgt->AddSong(src->getIteratorName());
+ src->iteratorNext();
+ };
+ collections->setCurrentItem(i);
+ collections->centerCurrentItem();
+ };
+ };
+}
+
+void CollectionDialog::deleteCollection()
+{
+if (selectedC==0) return;
+slman->deleteCollection(selectedC);
+int i=selectedC;
+collections->removeItem(selectedC);
+collections->setCurrentItem(i);
+collections->centerCurrentItem();
+}
+
+void CollectionDialog::changeCollectionName(int idx)
+{
+if (idx==0) return;
+ bool ok;
+
+ QString name = KInputDialog::getText( i18n( "Change Collection Name" ),
+ i18n( "Enter the name of the selected collection:" ), QString::null,
+ &ok, this );
+
+ if (ok)
+ {
+ if (slman->getCollection(name.ascii())!=NULL)
+ {
+ QString s = i18n("The name '%1' is already used").arg(name);
+ KMessageBox::sorry(this, s);
+ }
+ else
+ {
+ slman->changeCollectionName(idx,name.ascii());
+ collections->changeItem(name,idx);
+ };
+ };
+}
+
+void CollectionDialog::addSong(const KURL &url)
+{
+ if (currentsl==NULL) return;
+ if( url.isEmpty()) return;
+
+ int id=currentsl->AddSong(QFile::encodeName(url.path()));
+
+ if (id==-1)
+ {
+ printf("Couldn't add song to collection\n");
+ return;
+ };
+
+ songs->insertItem(url.fileName(),id-1);
+}
+
+
+void CollectionDialog::addSong()
+{
+ KURL::List urls = KFileDialog::getOpenURLs(":MidiFiles",
+ "*.kar *.mid *.kar.gz *.mid.gz\n*.kar *.kar.gz\n*.mid *.mid.gz\n*",this);
+
+ KURL::List::Iterator it;
+ for( it = urls.begin(); it != urls.end(); ++it )
+ addSong(*it);
+
+}
+
+void CollectionDialog::removeSong()
+{
+if (currentsl==NULL) return;
+currentsl->DelSong(selectedS+1);
+int i=selectedS;
+songs->removeItem(selectedS);
+songs->setCurrentItem(i);
+}
+
+void CollectionDialog::resizeEvent(QResizeEvent *)
+{
+int maxw=newC->width();
+if (copyC->width()>maxw) maxw=copyC->width();
+if (deleteC->width()>maxw) maxw=deleteC->width();
+if (addS->width()>maxw) maxw=addS->width();
+if (delS->width()>maxw) maxw=delS->width();
+
+newC->setGeometry(width()-maxw-5,newC->y(),maxw,newC->height());
+copyC->setGeometry(width()-maxw-5,copyC->y(),maxw,copyC->height());
+deleteC->setGeometry(width()-maxw-5,deleteC->y(),maxw,deleteC->height());
+
+collections->resize(width()-maxw-20,(height()*35)/100);
+label2->move(10,collections->y()+collections->height()+10);
+songs->setGeometry(10,label2->y()+label2->height()+10,width()-maxw-20,height()-(label2->y()+label2->height()+10+ok->height()+10));
+addS->setGeometry(width()-maxw-5,songs->y()+5,maxw,addS->height());
+delS->setGeometry(width()-maxw-5,addS->y()+addS->height()+5,maxw,delS->height());
+
+cancel->move(width()-cancel->width()-5,height()-cancel->height()-5);
+ok->move(cancel->x()-5-ok->width(),height()-ok->height()-5);
+}
+
+int CollectionDialog::selectedC=0;
+int CollectionDialog::selectedS=0;
+#include "collectdlg.moc"
diff --git a/kmid/collectdlg.h b/kmid/collectdlg.h
new file mode 100644
index 00000000..46f96ea4
--- /dev/null
+++ b/kmid/collectdlg.h
@@ -0,0 +1,83 @@
+/**************************************************************************
+
+ collectdlg.h - The collection manager dialog
+ Copyright (C) 1998 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#ifndef _collectdlg_h_
+#define _collectdlg_h_
+
+#include <qdialog.h>
+#include "songlist.h"
+#include "slman.h"
+
+class QLabel;
+class KPushButton;
+class QPushButton;
+class QListBox;
+class KURL;
+class CollectionDialog : public QDialog
+{
+ Q_OBJECT
+private:
+ SLManager *slman;
+ SongList *currentsl;
+
+protected:
+
+ virtual void resizeEvent(QResizeEvent *qre);
+ void fillInSongList(void); //clear the songs list and insert in it
+ // the songs in currentsl
+ void addSong(const KURL &);
+public:
+
+ CollectionDialog(SLManager *slm,int selc,QWidget *parent,const char *name);
+
+public slots:
+ void collectionselected(int idx);
+ void songselected(int idx);
+ void newCollection();
+ void copyCollection();
+ void deleteCollection();
+ void changeCollectionName(int idx);
+ void addSong();
+ void removeSong();
+
+private:
+QLabel *label;
+QLabel *label2;
+KPushButton *ok;
+KPushButton *cancel;
+QListBox *collections;
+QListBox *songs;
+QPushButton *newC;
+QPushButton *copyC;
+QPushButton *deleteC;
+QPushButton *addS;
+QPushButton *delS;
+
+
+public:
+static int selectedC;
+static int selectedS;
+
+};
+
+#endif
diff --git a/kmid/configure.in.in b/kmid/configure.in.in
new file mode 100644
index 00000000..f1e1d100
--- /dev/null
+++ b/kmid/configure.in.in
@@ -0,0 +1,5 @@
+KDE_CHECK_HEADER([libkmid/libkmid.h], [kde_mid_compiles=yes], [kde_mid_compiles=no])
+if test $kde_mid_compiles = no; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE kmid"
+fi
+
diff --git a/kmid/drums.o3 b/kmid/drums.o3
new file mode 100644
index 00000000..01d9b9c5
--- /dev/null
+++ b/kmid/drums.o3
Binary files differ
diff --git a/kmid/drums.sb b/kmid/drums.sb
new file mode 100644
index 00000000..4bfaf8a3
--- /dev/null
+++ b/kmid/drums.sb
Binary files differ
diff --git a/kmid/examples/DiesIrae.kar b/kmid/examples/DiesIrae.kar
new file mode 100644
index 00000000..c54c6fb6
--- /dev/null
+++ b/kmid/examples/DiesIrae.kar
Binary files differ
diff --git a/kmid/examples/Guantanamera.kar b/kmid/examples/Guantanamera.kar
new file mode 100644
index 00000000..7fee4b1a
--- /dev/null
+++ b/kmid/examples/Guantanamera.kar
Binary files differ
diff --git a/kmid/examples/Makefile.am b/kmid/examples/Makefile.am
new file mode 100644
index 00000000..b53f7c54
--- /dev/null
+++ b/kmid/examples/Makefile.am
@@ -0,0 +1,6 @@
+
+example_DATA = OFortuna.kar DiesIrae.kar Guantanamera.kar \
+ MariaDeLasMercedes.kar
+exampledir = $(kde_datadir)/kmid
+
+EXTRA_DIST = $(example_DATA)
diff --git a/kmid/examples/MariaDeLasMercedes.kar b/kmid/examples/MariaDeLasMercedes.kar
new file mode 100644
index 00000000..7a2e305e
--- /dev/null
+++ b/kmid/examples/MariaDeLasMercedes.kar
Binary files differ
diff --git a/kmid/examples/OFortuna.kar b/kmid/examples/OFortuna.kar
new file mode 100644
index 00000000..cc59960e
--- /dev/null
+++ b/kmid/examples/OFortuna.kar
Binary files differ
diff --git a/kmid/hi16-app-kmid.png b/kmid/hi16-app-kmid.png
new file mode 100644
index 00000000..dcad9f23
--- /dev/null
+++ b/kmid/hi16-app-kmid.png
Binary files differ
diff --git a/kmid/hi32-app-kmid.png b/kmid/hi32-app-kmid.png
new file mode 100644
index 00000000..4538ad78
--- /dev/null
+++ b/kmid/hi32-app-kmid.png
Binary files differ
diff --git a/kmid/hi48-app-kmid.png b/kmid/hi48-app-kmid.png
new file mode 100644
index 00000000..928cebca
--- /dev/null
+++ b/kmid/hi48-app-kmid.png
Binary files differ
diff --git a/kmid/history.txt b/kmid/history.txt
new file mode 100644
index 00000000..c436db0b
--- /dev/null
+++ b/kmid/history.txt
@@ -0,0 +1,567 @@
+Thursday 11-9-97
+- I have started with some tries to make the midi work and due to the
+ success I have even made an object to manage the midi : midiOut
+
+Friday 12-9-97
+- Waking up at 7:00am, working from 8:20 to 11:20 and doing some fixes
+ at 14:20, I have been able to play a midi file through the /dev/sequencer
+ using the class midiOut and writing some new classes like : midfile
+ track and some more.
+
+Saturday 13-9-97
+- Today I have made a midi mapper (object mapper) which read a map config file
+ and is used to support older synths without general midi compliance.
+
+Sunday 14-9-97
+- I have made a window, with a KMenuBar, and a KSilder, which reads a mid file
+ and play it (using a fork)
+
+Monday 15-9-97
+- fork copy the memory and thus, I cannot tell the player to pause, I have read
+ much about the subject, and about threads
+
+Tuesday 16-9-97
+- I have made a small program to test the threads and forks, threads apparently
+are faster, but it seems to have a problem with libqt or anyone, and I get
+core dumpes what I run kmid even if I only link libpthread but don't use it.
+So I've decided to make a fork, in the other way, I've learned to share memory
+
+Wednesday 17-9-97
+- Christian Esken has told me to use mediatool, but I don't have the
+documentation, so I cannot continue work until I get it.
+
+Thursday 18-9-97
+- Well, I'll continue even without the docs, and when I have them, I will
+make the neccesary changes to the code. I have made a few details in windows
+
+Friday 19-9-97
+- Today I have made the Pause and Stop buttons work. Also, I have made the
+slide bar move. And also, I have made a parser of the midi file, which tells
+how long the song is, in seconds. If this was little, I have made the interface
+between the ui and the player.
+
+Saturday 20-9-97
+- The interface between the ui and the player doesn't work well, I have to
+redesign it because it's difficult to be accurate writing the karaoke at the same that the note plays
+
+Sunday 21-9-97
+- I have redesigned twice the interface between ui and player and it still
+doesn't work well, the first method was creating a list of the karaoke events
+and using a timer to process the next event at the right time, but that doesn't
+work perfectly, because the text appears a moment after or before the note is
+played. The second method is a while loop, which calls processEvents, but
+that isn't a very good option, because, though text appears just at the same
+time of the note is played, top tells me that kmid is consuming up to 98% of
+CPU time, and I don't want to make such a monster.
+
+Monday 22-9-97
+- Another failed method, and another passed day
+
+Tuesday 23-9-97
+- Some small changes beautifying the monster to send it to Christian
+
+Wednesday 24-9-97
+- More debugging and no result. I have uploaded current version to arrakis for
+Christian to see it.
+
+Thursday 25-9-97
+- Eureka ! , at last, I have made the player and the ui run at the same time
+with PERFECT syncronisation :-) (at 10:30 pm)
+
+Friday,Saturday,Sunday 26,27,28-9-97
+- I've been looking at the birds flying freely from tree to tree in all this
+weekend, also, I've studied for the test drive.
+
+Monday 29-9-97
+- I have passed the exam and also, I have (almost) done the karaoke widget
+and it works very well !
+
+Tuesday 30-9-97
+- The karaoke widget is finished, it also have a scrollbar to see all the
+lyrics. By the way, StarTrek : First Contact is a very recommended film
+
+Wednesday 1-10-97
+- I have fixed pause and stop, which were broken during the karaoke syncro
+By the way, kmid consumes 0.0% CPU and 00:00 time while playing a 6 minute song
+(and I think that even in longer songs !)
+
+Thursday 2-10-97
+- I have started classes, so I think I will have less time :-(
+
+Wednesday 8-10-97
+- The player now recognizes the SETPOS message, but the ui is not syncronized
+yet
+
+Thursday 9-10-97
+- S. R. has "show"ed me the way to initialize the menu, toolbar and client of a
+KTopLevelWidget, without the need to resize the window.
+
+Sunday 12-10-97
+- I have made that all time variables are expressed in milliseconds, so no
+more change units !!
+
+Sunday 19-10-97
+- I have programmed very little this week, but today I have kill a big bug,
+now all the songs play well. Some songs were played with some parts faster and
+some parts slower than they should (thanks to Christian Esken for reporting
+ the bug!)
+
+Tuesday 21-10-97
+- WOE, KDE-Beta-ONE is even better than before. But now kmid doesn't compile (?)
+
+Wednesday 22-10-97
+- After compiling the kde libs, kmid compiles ok. KSlider has changed and its
+behaviour (Arrrgh!), so I have trickered a little the code.
+
+Thursday 23-10-97
+- I have cleaned a little the code.
+
+Friday 24-10-97
+- I started to "rewrite" the track class to make the slider work well
+
+Saturday 25-10-97
+- I have passed the day working in the time.
+
+Sunday 26-10-97
+- Eureka !!!, KMid works great! with good timings and syncronization between
+ O ~ music and lyrics.
+ U
+ \_/
+
+Monday 27-10-97
+- After 10 hours in the university, I have made the final touches to send
+kmid 0.1 to the internet.
+
+??? 7-11-97
+- Finally, there's an impossible-better syncro between the program
+and the user !!!. I mean, that when you press the pause button, the music
+is inmediatly paused !!!
+
+Saturday 8-11-97
+- So I suppose yesterday was Friday, but I should demostrate it :-)
+I have added drag & drop support in kmid to load files by dropping them from kfm
+Also made the slider show the time played at resizeable steps
+
+Sunday 9-11-97
+- Today I have made possible to change the font and I have made an icon.
+Prepare a version to upload tomorrow to the internet.
+
+Thursday 13-11-97
+- I have nearly rewritten (made lots of changes to) the lyrics display widget
+Also fixed that old "bug" that let in black the last word of a phrase in some
+songs.
+
+Friday 14-11-97
+- Automatic text event chooser is done !
+- Fixed a bug that could freeze kmid under very rare circunstances.
+- Added more support for incomplete/broken files
+
+Saturday 15-11-97
+- I have changed the exit-behaviour for a messagebox-behaviour
+- Fixed a bug that could leave the children running when closing kmid
+
+Sunday 16-11-97
+- KMid adds a mime entry for karaoke files in audio/x-karaoke
+- Real Session Management.
+- Made the first rpm of kmid.
+
+Monday 17-11-97
+- Release version 0.3 in tgz and rpm
+
+Friday 28-11-97
+- I've been quite busy this week, but I have had reports and patches :-) on
+how to make kmid to work with an AWE, a GUS, and in FreeBSD :-))))
+
+Monday 8-12-97
+- I have fixed a small bug with which the time that a song longs was not well
+reported.
+- Also a tiny bug in consolekmid
+
+Tuesday 9-12-97
+- I've made the first feature that I've been asked for. Another thing to map
+with the midi mapper for Dietmar's PSR500
+
+Friday 12-12-97
+- Nearly done the map chooser so that there will no be needed to edit the kmidrc
+for anything.
+Now when you choose a new map or change GM/MT32 mode, the music is automatically
+changed while playing !.
+
+...... **-12-97
+- I have been the rest of the month implementing a midi server that I will
+call KMidServer and that will provide any application with easy control over
+midi functions.
+
+30-12-97
+- KMid now can play gzipped midi files just as any other normal file.
+
+.... **-1-98
+- I have improved the midi mapper by allowing to factor the pitch bender.
+ Now pitch benders works OK !! (at last I have improved the windows mapper :-)
+ I have also added support to convert expression events to volume events.
+* I have been doing the kfourier this month and studying (little time to do more)
+
+
+Thursday 5-2-98
+- After doing my first exam this year, I have decided to calm things a little
+ by programming something. I have made the options added to the mapper available
+through the config map file.
+
+Monday 9-2-98
+- 2nd exam done and a bug that I introduced last thrusday fixed.
+
+Friday 20-2-98
+- I've finished exams, have installed the yesterday snapshots and have
+updated the configure scripts to add rpath and all that stuff to my copy
+of kmid (thanks to Kulow, who added it to kmid in kdemultimedia)
+
+Saturday 21-2-98
+- Fixed the not-a-feature problem with FM, next time I will not let the
+option for the user to select something that doesn't work.
+Final touches to release kmid 0.41 tomorrow morning.
+
+Monday 23-2-98
+- I've started working on FM support (and it goes very fast)
+
+Tuesday 24-2-98
+- fm already works, but notes are shifted to a higher key !!!
+Also pitch bender seems not to be accurate.
+
+Wednesday 25-2-98
+- pitch bender is now fixed, and also the shift of notes, now the problem
+ is that some notes are lost (always from the same track/channel/patch?!?!)
+
+Friday 27-2-98
+- The problem with the lost notes is fixed :-/, the problem was that some
+notes are played through the left speaker and some thru the right speaker,
+and my wires for the right speaker were broken, but it wasn't noted with
+the surround activated :-))))))) (Sometimes I feel real shame). Three days
+lost in that !!
+
+Saturday 28-2-98
+- I've gone to the country and I've done the internals of the playlists in a
+486/50 without linux, so I'll compile the Monday.
+
+Sunday 1-3-98
+- Some test with multiple toolbars.
+
+Monday 2-3-98
+- Started to do the collections manager dialog
+
+Tuesday 3-3-98
+- The collections manager dialog is done.
+
+Wednesday 4-3-98
+- KKeyConfig is a really good class, easy to use to make the karaoke scroll
+when arrow keys are pressed.
+
+Thursday 5-3-98
+- I've done the playlist hardest part, now it works and there's only left
+ some final touches to do
+
+Tuesday 10-3-98
+- Playlists are finally working ok.
+
+Wednesday 11-3-98
+- Fixed the session management (in fact, I've changed it a little to support
+collections)
+
+Thursday 12-3-98
+- Fixed some small bugs.
+
+Friday 13-3-98
+- I have replaced my new Makefiles.am (just two days old) with the one
+of Stephan Kulow which look much better.
+
+Saturday 14-3-98
+- Finished deleting printf's to upload kmid tomorrow :-)
+
+Sunday 15-3-98
+- Upload it, but as I forgot to delete some printf messages :-( , I had
+to quickly do a fix and send also 0.5.1
+
+**** **-3-98
+- I've passed many days configuring the modem, the internet connection,
+sendmail, kppp, cvs and all those programs :-)
+
+Monday 30-3-98
+- I've implemented the save-Lyrics feature that Adrian Knoth suggested (thanks!)
+
+Tuesday 31-3-98
+- I've done some changes to support the new KFileDialog and the new
+changes in kdelibs done by Matthias.
+
+Thursday 2-4-98
+- Some bug fixes
+
+Monday 6-4-98
+- I've found and fixed a memory leakage bug, I forgot to delete the
+SpecialEvents from each song, so kmid was using more and more memory with
+each opened song.
+
+Tuesday 7-4-98
+- Aaaargh !, I have to remove the changes that I did last tuesday because it
+seems that the changes in the kdelibs have been removed.
+
+Monday 13-4-98
+- Added support for midi files with strange headers not related to midi files.
+ (suggested by Adrian Knoth, thanks !)
+
+Friday 24-4-98
+- At last, I have had some free time and have done some things:
+ 1. Finally fixed the synchronization between gui and player (hopefully forever)
+ 2. Fixed a problem openning a gzipped file with spaces in its name
+ 3. Fixed a bug which caused kmid to freeze if collection has only 1 song, and
+ trying to play next song in aleatory mode.
+ 4. Fixed the problem which made some songs to play at a lower volume when
+ moving the slider, but no if playing from the beginning
+ That's all for now :-) , there have been so many changes that I've call
+ this version 0.5.3
+
+Thursday 7-5-98
+- Released version 0.6 to update my homepage which was getting very outdated
+
+Saturday 9-5-98
+- After fixing my midi cable, I've noticed that fm is (as Gyula Kerekes
+ reported), playing in mono, and I've fixed it.
+
+Sunday 10-5-98
+- I've fixed a small bug with which midi files without text events (nor lyrics)
+got the time slider very desynchronized after pausing for a long time.
+
+Monday 25-5-98
+- Applied a patch submitted by Kevin Street, to compile kmid on FreeBSD 3.0
+systems with awe support (directory settings has changed since 2.2)
+
+Saturday 20-6-98
+- I'm on exams, but just to relax a few minutes I've been hunting for bugs :-)
+
+Sunday 21-6-98
+- Wow, KDE-1.0 is finally here, and it is REAL, so I will prepare very quickly
+KMid 1.0 before the freeze of kdemultimedia.
+
+Saturday 4-7-98
+- Just finished exams :-), and I've implemented the (long awaited feature of a)
+volume bar.
+
+Sunday 5-7-98
+- I'm doing a channel view in which you can see the keys being pressed
+and released, it's very nice :-)
+
+Monday 6-7-98
+- Using the (still hot) shutDown signal (the most awaited thing for
+kmid)
+
+Monday 13-7-98
+- I've restructured the Channel view, it's much nicer and allows for easier
+expansion. It also shows the used instrument
+
+Saturday 15-8-98
+- Wow, I haven't updated this file for so long ...
+ I've finished the changing instrument feature and so the channel view.
+ I've also improved speed a little by supressing some parses to the midi file.
+ I have improved consolekmid, now it is much more usuable.
+ Finally, I've changed the sources to use the KAccel class.
+
+Monday 31-8-98
+- I've fixed some bugs, the most important one being that kmid left its player
+ processes as zombies after stopping playing (it worked ok, but now it doesn't,
+ dunno why). That problem made to appear some weird dialogs telling "song is already
+ playing" at each moment
+- I left a color hardcoded in button*.xpm when it should be Transparent (not
+ everybody has a window background color of #5D5D8F8FA0A0 :-) )
+- I created a Motif 1.0 combo box in the channelview, but it should be Motif 2.0
+ because 1.0 has a "feature" for large como boxes (as this one)
+
+
+Wednesday 9-9-98
+- Fixed the last bug for leaving zombies around.
+
+Monday 14-9-98
+- Released Version 1.2
+
+Tuesday 15-9-98
+- Added the change tempo widget and the KTriangleButton widget
+
+Wednesday 16-9-98
+- It's possible now to change the tempo of a song.
+
+Thursday 1-10-98
+- Fixed some problems
+
+Thursday 15-10-98
+- FIXED !!!
+ The long awaited fix has arrived for songs keeping playing after X11' logout
+ when channelview was actived.
+ Now also the ChannelView change its colors when the user change the
+ global colors, kdisptext paints a frame over the karaoke text,
+ KLCDNumber only accepts a range of values (from 3 to 999)
+
+Friday 16-10-98
+- Improved support for really broken files.
+- Added some translations to Spanish
+
+Friday 23-10-98
+- I've started writing the internals of a rhythm view
+
+Saturday 24-10-98
+- Now KMid shows a new widget to make easier follow the rhythm of songs.
+ I've also fixed a small bug by which the tempo (and rhythm) changed its
+ state before the event actually happening if you moved the timebar near
+ before the event time. The problem was caused by volatile shared variables.
+
+Sunday 25-10-98
+- Fixed a serious bug I introduced in the previous commitment
+
+Monday 2-11-98
+- Fixed some problems
+
+Sunday 8-11-98
+- Some small changes
+
+Wednesday 25-11-98
+- I've finally fixed the bug that made kmid play music sometimes
+faster and sometimes slower on some machines (i.e. mine, since I installed
+kernel 2.0.36)
+This bug was there since the real beginning, and now, it is FIXED.
+This deserves a new 2.0 version, probably next week.
+
+Friday 27-11-98
+- Fixed a broken link in the .kdelnk file (DocPath) and raised version
+number to 1.6
+
+Thursday 10-12-98
+- Added new sgml documentation, updated up to KMid 1.6
+
+Friday 11-12-98
+- Fixed a shared memory leackage bug (hopefully the last bug in KMid :) ).
+
+Thursday 17-12-98
+- Initial support for SoftOSS (this is making some sound now :))
+- Indentified the player directory sources and beautified them a little
+
+Friday 18-12-98
+- Fixed a geometry layout bug
+
+Sunday 20-12-98
+- Fixed a problem when [un]installing midi devices (not such a mad idea if you
+use kernel modules) that made kmid segfault because it couldn't found
+the device.
+
+Saturday 16-1-99
+- Decode URL before showing it on the screen.
+
+Thursday 21-1-99
+- Fixed a null pointer access while generating beats, because I didn't
+checked for songs which didn't contain any spev (songs which use absolute 0 spev
+are extremely rare to find, as any song must include at least a tempo event)
+
+Tuesday 26-1-99
+- Removed ; after QOBJECT and fixed default midi map in consolekmid (not kmid)
+
+Sunday 21-2-99
+- Made a quick hack (no more than 15 lines) and kmid uses KIOJob to be
+net transparent (now it can download files using an http or ftp protocol just
+using dnd from a kfm window or using the open file dialog)
+
+Saturday 6-3-99
+- The sound driver in Linux kernels from the 2.2.x series work very different
+than the ones in 2.0.x so kmid was severely damaged. After several days of
+hacking (and after getting from A.Cox that this wasn't going to be fixed in the
+kernel) I've found a fix that even works in the 2.0.x kernels.
+
+Sunday 21-3-99
+- Fixed the high pitch of AWE cards (thanks to Joseph H. Buehler and Takashi Iwai for
+ the indications)
+- Raised version number to 1.7
+
+Friday 2-4-99
+- Did a workaround for a bug in egcs which doesn't seem to optimize simple
+return-a-value function as an inline function when using -O2 and it made kmid
+freeze under certain conditions when parsing some midi files.
+
+- Removed a comparison of double variables which is very dangerous (_even_ after
+storing in one of them the value of the other one)
+- Fixed a problem by which kmid didn't play the last half second or so of some songs.
+
+Thursday 8-4-99
+- Fixed a problem that made Kmid consume 99% of CPU since a month ago. Now it's back
+to the usual 0.00% of CPU use :-)
+
+Monday 12-4-99
+- Fixed a bug that made KMid freeze when trying to open a directory
+
+Thursday 15-4-99
+- Better support for broken karaoke files which has duplicated lyrics
+
+Thursday 11-11-99
+- Fixed a problem with corrupt files that suddenly doesn't work (but KMid
+ from the 1.1 branch works ok (?) ). Anyway, I've got it to work even
+ better with broken files
+- Fixed the KLed usage, which was broken since the KLedLamp -> KLed transition.
+- Fixed a repaint bug with the time tags that has been here for too long.
+
+Friday 12-11-99
+- Instead of using manual layout, let's use QLayouts (there are still some
+ problems with this )
+- Added xdnd support (finally !)
+
+Thursday 25-11-99
+- Changed a lot of code to look much better
+- Fixed many problems when painting lyrics in different colors
+
+Friday 26-11-99
+- The lyrics display doesn't flicker anymore (finally :-) )
+- Reworked the scrolling code, it's smooth now instead of line-by-line (but also
+ makes consumes a bit of CPU, so I'll make it configurable for people who don't
+ want to waste resources)
+- note: don't call resizeContents from drawContents, or you'll have some
+ problems (QScrollView bug ?, anyway, this note should be on the docs)
+
+Tuesday 11-1-2000
+- SoftOSS is working now !
+- Added LOTS of documentation to the MIDI library. Anyway, there's a lot more
+to come.
+- Also fixed a few problems in the lyrics display, and added a new option
+to consolekmid
+
+Sunday 27-2-2000
+- Ported KMid to use the "official" LibKMid and KXMLGUI, with the following
+ result :
+ # wc kmidframe.cpp
+ Before: 769 1450 24820 kmidframe.cpp
+ After : 622 1171 19045 kmidframe.cpp
+
+Monday 28-2-2000
+- Added ALSA support to libkmid
+
+Tuesday 29-2-2000
+- I'm making KMid a part
+
+It took me : exactly 13 minutes to port the app
+ 30 seconds to do /LD/LIB/ in Makefile.am
+ 5:14 to compile it and see that kbrowser.h doesn't exist now :(
+
+Sunday 5-3-2000
+- Now libkmid does build and run on systems where OSS is not installed.
+- Updated the code to simplify it a bit due to libkmid changes.
+- I still have to search for a bug that makes KMid (and the part!) to crash
+ when destroying KDisplayText (the curious thing is that it crashes on
+ QScrollView)
+
+Sunday 2-4-2000
+- Now Libkmid links with kdecore and qt in order to use a KConfig object to
+read the default settings as saved by the user with the midi kcontrol module.
+- It creates a "dummy" KInstance in case of the application being a non-KDE
+application.
+- Fixed compilation of KMid, as 1) someone renamed klinedit.h to klineedit.h
+and 2) now we have to include qiconset.h when creating an action with pictures
+as in kmid_part.cpp
+- Now I just have to find why KMid doesn't show any interface except the
+standard actions
+
+Saturday 30-9-2000
+- Hmm, it's been a long time since I don't update this file. In summer
+ I've fixed ALSA support and many related bugs.
+- Today, I've finally added the accelerators.
+- Raised version to 2.0
diff --git a/kmid/instrfilter.awk b/kmid/instrfilter.awk
new file mode 100644
index 00000000..078169aa
--- /dev/null
+++ b/kmid/instrfilter.awk
@@ -0,0 +1,2 @@
+{ match ($0,"\".*\"") }
+{ if (RSTART>0) print "i18n("substr($0,RSTART,RLENGTH)");" };
diff --git a/kmid/instrname.cpp b/kmid/instrname.cpp
new file mode 100644
index 00000000..551b85c9
--- /dev/null
+++ b/kmid/instrname.cpp
@@ -0,0 +1,132 @@
+const char *instrumentName[128]={
+ "Acoustic Grand Piano",
+ "Bright Acoustic Piano",
+ "Electric Grand Piano",
+ "Honky-Tonk",
+ "Rhodes Piano",
+ "Chorused Piano",
+ "Harpsichord",
+ "Clavinet",
+ "Celesta",
+ "Glockenspiel",
+ "Music Box",
+ "Vibraphone",
+ "Marimba",
+ "Xylophone",
+ "Tubular Bells",
+ "Dulcimer",
+ "Hammond Organ",
+ "Percussive Organ",
+ "Rock Organ",
+ "Church Organ",
+ "Reed Organ",
+ "Accordion",
+ "Harmonica",
+ "Tango Accordion",
+ "Acoustic Guitar (Nylon)",
+ "Acoustic Guitar (Steel)",
+ "Electric Guitar (Jazz)",
+ "Electric Guitar (Clean)",
+ "Electric Guitar (Muted)",
+ "Overdriven Guitar",
+ "Distortion Guitar",
+ "Guitar Harmonics",
+ "Acoustic Bass",
+ "Electric Bass (Finger)",
+ "Electric Bass (Pick)",
+ "Fretless Bass",
+ "Slap Bass 1",
+ "Slap Bass 2",
+ "Synth Bass 1",
+ "Synth Bass 2",
+ "Violin",
+ "Viola",
+ "Cello",
+ "Contrabass",
+ "Tremolo Strings",
+ "Pizzicato Strings",
+ "Orchestral Harp",
+ "Timpani",
+ "String Ensemble 1",
+ "String Ensemble 2",
+ "Synth Strings 1",
+ "Synth Strings 2",
+ "Choir Aahs",
+ "Voice Oohs",
+ "Synth Voice",
+ "Orchestra Hit",
+ "Trumpet",
+ "Trombone",
+ "Tuba",
+ "Muted Trumpet",
+ "French Horn",
+ "Brass Section",
+ "Synth Brass 1",
+ "Synth Brass 2",
+ "Soprano Sax",
+ "Alto Sax",
+ "Tenor Sax",
+ "Baritone Sax",
+ "Oboe",
+ "English Horn",
+ "Bassoon",
+ "Clarinet",
+ "Piccolo",
+ "Flute",
+ "Recorder",
+ "Pan Flute",
+ "Blown Bottle",
+ "Shakuhachi",
+ "Whistle",
+ "Ocarina",
+ "Lead 1 - Square Wave",
+ "Lead 2 - Saw Tooth",
+ "Lead 3 - Calliope",
+ "Lead 4 - Chiflead",
+ "Lead 5 - Charang",
+ "Lead 6 - Voice",
+ "Lead 7 - Fifths",
+ "Lead 8 - Bass+Lead",
+ "Pad 1 - New Age",
+ "Pad 2 - Warm",
+ "Pad 3 - Polysynth",
+ "Pad 4 - Choir",
+ "Pad 5 - Bow",
+ "Pad 6 - Metallic",
+ "Pad 7 - Halo",
+ "Pad 8 - Sweep",
+ "FX 1 - Rain",
+ "FX 2 - Soundtrack",
+ "FX 3 - Crystal",
+ "FX 4 - Atmosphere",
+ "FX 5 - Brightness",
+ "FX 6 - Goblins",
+ "FX 7 - Echoes",
+ "FX 8 - Sci-fi",
+ "Sitar",
+ "Banjo",
+ "Shamisen",
+ "Koto",
+ "Kalimba",
+ "Bagpipe",
+ "Fiddle",
+ "Shannai",
+ "Tinkle Bell",
+ "Agogo",
+ "Steel Drum",
+ "Wook Block",
+ "Taiko Drum",
+ "Melodic Tom",
+ "Synth Drum",
+ "Reverse Cymbal",
+ "Guitar Fret Noise",
+ "Breath Noise",
+ "Seashore",
+ "Bird Tweet",
+ "Telephone",
+ "Helicopter",
+ "Applause",
+ "Gunshot"
+};
+
+
diff --git a/kmid/instrname.h b/kmid/instrname.h
new file mode 100644
index 00000000..80e199c4
--- /dev/null
+++ b/kmid/instrname.h
@@ -0,0 +1,6 @@
+#ifndef _INSTRNAME_H
+#define _INSTRNAME_H
+
+extern const char *instrumentName[128];
+
+#endif
diff --git a/kmid/instrname.i18n b/kmid/instrname.i18n
new file mode 100644
index 00000000..08ec0938
--- /dev/null
+++ b/kmid/instrname.i18n
@@ -0,0 +1,133 @@
+/* This is an automatically generated file
+ * DO NOT EDIT !!!
+ * To create this file run : make instrname.i18n
+ */
+
+i18n("Acoustic Grand Piano");
+i18n("Bright Acoustic Piano");
+i18n("Electric Grand Piano");
+i18n("Honky-Tonk");
+i18n("Rhodes Piano");
+i18n("Chorused Piano");
+i18n("Harpsichord");
+i18n("Clavinet");
+i18n("Celesta");
+i18n("Glockenspiel");
+i18n("Music Box");
+i18n("Vibraphone");
+i18n("Marimba");
+i18n("Xylophone");
+i18n("Tubular Bells");
+i18n("Dulcimer");
+i18n("Hammond Organ");
+i18n("Percussive Organ");
+i18n("Rock Organ");
+i18n("Church Organ");
+i18n("Reed Organ");
+i18n("Accordion");
+i18n("Harmonica");
+i18n("Tango Accordion");
+i18n("Acoustic Guitar (Nylon)");
+i18n("Acoustic Guitar (Steel)");
+i18n("Electric Guitar (Jazz)");
+i18n("Electric Guitar (Clean)");
+i18n("Electric Guitar (Muted)");
+i18n("Overdriven Guitar");
+i18n("Distortion Guitar");
+i18n("Guitar Harmonics");
+i18n("Acoustic Bass");
+i18n("Electric Bass (Finger)");
+i18n("Electric Bass (Pick)");
+i18n("Fretless Bass");
+i18n("Slap Bass 1");
+i18n("Slap Bass 2");
+i18n("Synth Bass 1");
+i18n("Synth Bass 2");
+i18n("Violin");
+i18n("Viola");
+i18n("Cello");
+i18n("Contrabass");
+i18n("Tremolo Strings");
+i18n("Pizzicato Strings");
+i18n("Orchestral Harp");
+i18n("Timpani");
+i18n("String Ensemble 1");
+i18n("String Ensemble 2");
+i18n("Synth Strings 1");
+i18n("Synth Strings 2");
+i18n("Choir Aahs");
+i18n("Voice Oohs");
+i18n("Synth Voice");
+i18n("Orchestra Hit");
+i18n("Trumpet");
+i18n("Trombone");
+i18n("Tuba");
+i18n("Muted Trumpet");
+i18n("French Horn");
+i18n("Brass Section");
+i18n("Synth Brass 1");
+i18n("Synth Brass 2");
+i18n("Soprano Sax");
+i18n("Alto Sax");
+i18n("Tenor Sax");
+i18n("Baritone Sax");
+i18n("Oboe");
+i18n("English Horn");
+i18n("Bassoon");
+i18n("Clarinet");
+i18n("Piccolo");
+i18n("Flute");
+i18n("Recorder");
+i18n("Pan Flute");
+i18n("Blown Bottle");
+i18n("Shakuhachi");
+i18n("Whistle");
+i18n("Ocarina");
+i18n("Lead 1 - Square Wave");
+i18n("Lead 2 - Saw Tooth");
+i18n("Lead 3 - Calliope");
+i18n("Lead 4 - Chiflead");
+i18n("Lead 5 - Charang");
+i18n("Lead 6 - Voice");
+i18n("Lead 7 - Fifths");
+i18n("Lead 8 - Bass+Lead");
+i18n("Pad 1 - New Age");
+i18n("Pad 2 - Warm");
+i18n("Pad 3 - Polysynth");
+i18n("Pad 4 - Choir");
+i18n("Pad 5 - Bow");
+i18n("Pad 6 - Metallic");
+i18n("Pad 7 - Halo");
+i18n("Pad 8 - Sweep");
+i18n("FX 1 - Rain");
+i18n("FX 2 - Soundtrack");
+i18n("FX 3 - Crystal");
+i18n("FX 4 - Atmosphere");
+i18n("FX 5 - Brightness");
+i18n("FX 6 - Goblins");
+i18n("FX 7 - Echoes");
+i18n("FX 8 - Sci-fi");
+i18n("Sitar");
+i18n("Banjo");
+i18n("Shamisen");
+i18n("Koto");
+i18n("Kalimba");
+i18n("Bagpipe");
+i18n("Fiddle");
+i18n("Shannai");
+i18n("Tinkle Bell");
+i18n("Agogo");
+i18n("Steel Drum");
+i18n("Wook Block");
+i18n("Taiko Drum");
+i18n("Melodic Tom");
+i18n("Synth Drum");
+i18n("Reverse Cymbal");
+i18n("Guitar Fret Noise");
+i18n("Breath Noise");
+i18n("Seashore");
+i18n("Bird Tweet");
+i18n("Telephone");
+i18n("Helicopter");
+i18n("Applause");
+i18n("Gunshot");
diff --git a/kmid/kdisptext.cpp b/kmid/kdisptext.cpp
new file mode 100644
index 00000000..c871e55a
--- /dev/null
+++ b/kmid/kdisptext.cpp
@@ -0,0 +1,640 @@
+/**************************************************************************
+
+ kdisptext.cpp - The widget that displays the karaoke/lyrics text
+ Copyright (C) 1997,98 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#include "kdisptext.h"
+#include <qpainter.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qrect.h>
+#include <qtextcodec.h>
+
+#include <kconfig.h>
+#include <kglobal.h>
+#include <kglobalsettings.h>
+#include <kinstance.h>
+#include <klocale.h>
+#include "version.h"
+
+
+#define NUMPREVLINES 2
+//#define DRAW_BOUNDING_RECTS
+
+
+KDisplayText::KDisplayText(QWidget *parent,const char *name) : QScrollView(parent,name)
+{
+ first_line_[0]=first_line_[1]=NULL;
+ linked_list_[0]=linked_list_[1]=NULL;
+ cursor_line_[0]=cursor_line_[1]=NULL;
+ cursor_[0]=cursor_[1]=NULL;
+ nlines_[0]=nlines_[1]=0;
+ linked_list=NULL;
+ cursor_line=NULL;
+ first_line=NULL;
+ cursor=NULL;
+ nlines=0;
+ lyrics_codec=KGlobal::locale()->codecForEncoding();
+
+ viewport()->setBackgroundColor(QColor (110,110,110));
+// setBackgroundMode(NoBackground);
+ KConfig *kcfg=KGlobal::instance()->config();
+ kcfg->setGroup("KMid");
+ typeoftextevents=kcfg->readNumEntry("TypeOfTextEvents",1);
+ QFont *qtextfontdefault=new QFont(KGlobalSettings::fixedFont().family(),22);
+ qtextfont=new QFont(kcfg->readFontEntry("KaraokeFont",qtextfontdefault));
+ delete qtextfontdefault;
+ qfmetr=new QFontMetrics(*qtextfont);
+ nvisiblelines=height()/qfmetr->lineSpacing();
+ autoscrollv=0;
+}
+
+KDisplayText::~KDisplayText()
+{
+ RemoveLinkedList();
+}
+
+/*void KDisplayText::PreDestroyer(void)
+{
+ delete qfmetr;
+ delete qtextfont;
+}
+*/
+
+void KDisplayText::RemoveLine(kdispt_line *tmpl)
+{
+ kdispt_ev *tmpe;
+ while (tmpl->ev!=NULL)
+ {
+ tmpe=tmpl->ev;
+ tmpl->ev=tmpe->next;
+ // delete tmpe->spev; Remember that the Special Events that this pointer
+ // points to is the same that the Player object has instantiated
+ delete tmpe;
+ }
+}
+
+void KDisplayText::RemoveLinkedList(void)
+{
+ cursor=NULL;
+ cursor_line=NULL;
+ first_line=NULL;
+ linked_list=NULL;
+ nlines=0;
+ nlines_[0]=nlines_[1]=0;
+ first_line_[0]=first_line_[1]=NULL;
+ cursor_line_[0]=cursor_line_[1]=NULL;
+ cursor_[0]=cursor_[1]=NULL;
+
+ kdispt_line *tmpl;
+ for (int i=0;i<2;i++)
+ {
+ while (linked_list_[i]!=NULL)
+ {
+ RemoveLine(linked_list_[i]);
+ tmpl=linked_list_[i];
+ linked_list_[i]=linked_list_[i]->next;
+ delete tmpl;
+ }
+ }
+}
+
+void KDisplayText::ClearEv(bool totally)
+{
+ RemoveLinkedList();
+ if (totally)
+ {
+ killTimers();
+ autoscrollv=0;
+ resizeContents(0,0);
+ viewport()->repaint(TRUE);
+ }
+}
+
+int KDisplayText::IsLineFeed(char c,int type)
+{
+ switch (type)
+ {
+ case (1) : if (/*(c==0)||*/(c=='\\')||(c=='/')||(c=='@')) return 1;break;
+ case (5) : if (/*(c==0)||*/(c==10)||(c==13)) return 1;break;
+ default : if ((c==0)||(c==10)||(c==13)||(c=='\\')||(c=='/')||(c=='@')) return 1;break;
+ }
+ return 0;
+}
+
+void KDisplayText::AddEv(SpecialEvent *ev)
+{
+ if ((ev->type==1) || (ev->type==5))
+ {
+ int idx=(ev->type==1)? 0 : 1;
+ if (linked_list_[idx]==NULL)
+ {
+ linked_list_[idx]=new kdispt_line;
+ linked_list_[idx]->next=NULL;
+ linked_list_[idx]->num=1;
+ linked_list_[idx]->ev=new kdispt_ev;
+ cursor_line_[idx]=linked_list_[idx];
+ cursor_[idx]=cursor_line_[idx]->ev;
+ cursor_[idx]->spev=ev;
+ cursor_[idx]->next=NULL;
+ first_line_[idx]=linked_list_[idx];
+ first_line=first_line_[idx];
+ nlines_[idx]=1;
+ }
+ else
+ {
+ if (IsLineFeed(ev->text[0],ev->type))
+ {
+ nlines_[idx]++;
+ cursor_line_[idx]->next=new kdispt_line;
+ cursor_line_[idx]=cursor_line_[idx]->next;
+ cursor_line_[idx]->num=nlines_[idx];
+ cursor_line_[idx]->ev=new kdispt_ev;
+ cursor_line_[idx]->next=NULL;
+ cursor_[idx]=cursor_line_[idx]->ev;
+ }
+ else
+ {
+ cursor_[idx]->next=new kdispt_ev;
+ cursor_[idx]=cursor_[idx]->next;
+ }
+ cursor_[idx]->spev=ev;
+ cursor_[idx]->next=NULL;
+ }
+ }
+}
+
+void KDisplayText::calculatePositions(void)
+{
+ int typeoftextevents=1;
+ int fin=0;
+ kdispt_line *tmpl;
+ kdispt_ev *tmp;
+ int tmpx=0;
+ int tmpy=0;
+ int tmpw=0;
+ int maxx=0;
+ nlines=nlines_[(typeoftextevents==1)? 0:1];
+ int lineSpacing=qfmetr->lineSpacing();
+ int descent=qfmetr->descent();
+ while (!fin)
+ {
+ tmpl=linked_list_[(typeoftextevents==1)?0:1];
+ tmpy=lineSpacing;
+ maxx=0;
+ while (tmpl!=NULL)
+ {
+ tmp=tmpl->ev;
+ tmpx=5;
+ while (tmp!=NULL)
+ {
+ if (tmp->spev->type==typeoftextevents)
+ {
+ if (IsLineFeed(tmp->spev->text[0],typeoftextevents))
+ {
+ tmpy+=lineSpacing;
+ tmpx=5;
+
+ tmp->xpos=tmpx;
+ if (tmp->spev->text[0]!=0) tmpw=qfmetr->width(lyrics_codec->toUnicode(&tmp->spev->text[1]));
+ else tmpw=0;
+ tmp->r=qfmetr->boundingRect(lyrics_codec->toUnicode(&tmp->spev->text[1]));
+ }
+ else
+ {
+ tmp->xpos=tmpx;
+ tmpw=qfmetr->width(lyrics_codec->toUnicode(tmp->spev->text));
+ tmp->r=qfmetr->boundingRect(lyrics_codec->toUnicode(tmp->spev->text));
+ }
+ // We add 5 pixels above, below and to the right because of a
+ // problem with latest released Xft
+ tmp->r.moveBy(tmpx,tmpy-tmp->r.height()-tmp->r.y()-5);
+ tmp->r.setHeight(tmp->r.height()+descent+10);
+ tmp->r.setWidth(tmp->r.width()+5);
+ tmpx+=tmpw;
+ if (tmpx>maxx) maxx=tmpx;
+ }
+ tmp=tmp->next;
+ }
+ tmpl->ypos=tmpl->num*lineSpacing;
+ tmpy=tmpl->ypos;
+ tmpl=tmpl->next;
+ }
+ maxX[(typeoftextevents==1)?0:1]=maxx+10;
+ maxY[(typeoftextevents==1)?0:1]=
+ nlines_[(typeoftextevents==1)?0:1]*lineSpacing+descent+10;
+
+ if (typeoftextevents==1) typeoftextevents=5;
+ else fin=1;
+ }
+
+}
+
+kdispt_line *KDisplayText::searchYOffset(int y, kdispt_line *start)
+{
+kdispt_line *t=start;
+while (t!=NULL)
+ {
+// if (t->ypos+qfmetr->descent()>y) return start;
+ if (t->ypos+qfmetr->descent()+20>y) return start;
+ start=t;
+ t=t->next;
+ };
+return start;
+}
+
+void KDisplayText::drawContents(QPainter *p, int /*clipx*/, int clipy, int /*clipw*/, int cliph)
+{
+ p->setFont(*qtextfont);
+ if (linked_list==NULL) return;
+ int i=0;
+ p->setPen(yellow);
+ int colorplayed=1;
+ if (cursor==NULL) colorplayed=0; // Thus, the program doesn't change the color
+ kdispt_line *tmpl=linked_list;
+ kdispt_ev *tmp;
+
+#ifdef KDISPTEXTDEBUG
+ printf("events displayed %d\n",typeoftextevents);
+#endif
+
+ tmpl=searchYOffset(clipy,linked_list);
+ int nlinestodraw=1;
+ kdispt_line *t=tmpl;
+ while ((t!=NULL)&&(t->ypos+qfmetr->descent()<clipy+cliph))
+ {
+ nlinestodraw++;
+ t=t->next;
+ }
+
+ i=0;
+ while ((i<nlinestodraw)&&(tmpl!=NULL))
+ {
+ tmp=tmpl->ev;
+ while ((tmp!=NULL)&&(tmp->spev->type!=typeoftextevents)) tmp=tmp->next;
+ while (tmp!=NULL)
+ {
+ if ( colorplayed &&
+ // (tmp->spev->absmilliseconds>=cursor->spev->absmilliseconds))
+ (tmp->spev->id>=cursor->spev->id))
+ {
+ p->setPen(black);
+ colorplayed=0;
+ }
+
+ if (IsLineFeed(tmp->spev->text[0],tmp->spev->type))
+ p->drawText(tmp->xpos,tmpl->ypos,lyrics_codec->toUnicode(&tmp->spev->text[1]));
+ else
+ p->drawText(tmp->xpos,tmpl->ypos,lyrics_codec->toUnicode(tmp->spev->text));
+
+#ifdef DRAW_BOUNDING_RECTS
+ p->setPen(red);
+ p->drawRect(tmp->r);
+ p->setPen((colorplayed)?yellow:black);
+#endif
+ tmp=tmp->next;
+ while ((tmp!=NULL)&&(tmp->spev->type!=typeoftextevents)) tmp=tmp->next;
+ }
+ i++;
+ tmpl=tmpl->next;
+ }
+
+}
+
+
+void KDisplayText::resizeEvent(QResizeEvent *e)
+{
+ QScrollView::resizeEvent(e);
+ nvisiblelines=visibleHeight()/qfmetr->lineSpacing();
+ if ( (nlines>nvisiblelines) || (nvisiblelines==0) )
+ resizeContents(maxX[(typeoftextevents==1)?0:1],maxY[(typeoftextevents==1)?0:1]);
+ else
+ resizeContents(0,0);
+
+}
+
+void KDisplayText::CursorToHome(void)
+{
+/* KConfig *kcfg=KGlobal::instance()->config();
+ kcfg->setGroup("KMid");
+ typeoftextevents=kcfg->readNumEntry("TypeOfTextEvents",1);
+*/
+ linked_list=linked_list_[(typeoftextevents==1)? 0:1];
+ nlines=nlines_[(typeoftextevents==1)? 0:1];
+ cursor_line_[0]=linked_list_[0];
+ first_line_[0]=cursor_line_[0];
+ if (cursor_line_[0]!=NULL) cursor_[0]=cursor_line_[0]->ev;
+ cursor_line_[1]=linked_list_[1];
+ first_line_[1]=cursor_line_[1];
+ if (cursor_line_[1]!=NULL) cursor_[1]=cursor_line_[1]->ev;
+
+ if (linked_list==NULL)
+ {
+ cursor_line=NULL;
+ cursor=NULL;
+ first_line=NULL;
+ }
+ else
+ {
+ cursor_line=linked_list;
+ cursor=cursor_line->ev;
+ first_line=linked_list;
+ }
+
+ nvisiblelines=visibleHeight()/qfmetr->lineSpacing();
+ if (nlines>nvisiblelines)
+ resizeContents(maxX[(typeoftextevents==1)?0:1],maxY[(typeoftextevents==1)?0:1]);
+ else
+ resizeContents(0,0);
+
+
+ setContentsPos(0,0);
+ viewport()->repaint(true);
+}
+
+void KDisplayText::PaintIn(int type)
+{
+ bool paint=false;
+ if (type!=typeoftextevents)
+ {
+ int idx=(type==1)?0:1;
+ if (cursor_[idx]==NULL) return;
+ cursor_[idx]=cursor_[idx]->next;
+ while ((cursor_[idx]==NULL)&&(cursor_line_[idx]!=NULL))
+ {
+ cursor_line_[idx]=cursor_line_[idx]->next;
+ if (cursor_line_[idx]!=NULL)
+ {
+ cursor_[idx]=cursor_line_[idx]->ev;
+ if ((cursor_line_[idx]->num>first_line_[idx]->num+NUMPREVLINES)
+ &&(cursor_line_[idx]->num<first_line_[idx]->num+nvisiblelines+1))
+ if ((first_line_[idx]!=NULL)&&(first_line_[idx]->num+nvisiblelines<=nlines_[idx])) first_line_[idx]=first_line_[idx]->next;
+ }
+ }
+ return;
+ }
+
+ if ((cursor==NULL)||(cursor_line==NULL))
+ {
+ printf("KDispT : cursor == NULL !!!\n");
+ return;
+ }
+
+ kdispt_ev *tmp=cursor;
+ if (cursor->spev->type==typeoftextevents)
+ {
+// int x,y;
+// contentsToViewport(cursor->xpos,cursor_line->ypos,x,y);
+
+ cursor=cursor->next;
+ paint=true;
+
+ }
+ else
+ cursor=cursor->next;
+
+
+ while ((cursor==NULL)&&(cursor_line!=NULL))
+ {
+ cursor_line=cursor_line->next;
+ if (cursor_line!=NULL)
+ {
+ cursor=cursor_line->ev;
+ if ((cursor_line->ypos>contentsY()+(visibleHeight()*5/8))&&
+ (cursor_line->ypos<contentsY()+visibleHeight()+autoscrollv))
+ {
+ bool b=(autoscrollv==0);
+ autoscrollv+=qfmetr->lineSpacing();
+ if (b) startTimer(100);
+ else {
+ killTimers();
+ startTimer(100/(autoscrollv/qfmetr->lineSpacing()+1));
+ }
+ }
+// scrollBy(0,qfmetr->lineSpacing());
+ }
+ }
+ if (paint) repaintContents(tmp->r);
+}
+
+void KDisplayText::gotomsec(ulong i)
+{
+ int notidx=(typeoftextevents==1)?1:0;
+
+ if (linked_list_[notidx]!=NULL)
+ {
+ cursor_line_[notidx]=linked_list_[notidx];
+ first_line_[notidx]=cursor_line_[notidx];
+ cursor_[notidx]=cursor_line_[notidx]->ev;
+ while ((cursor_line_[notidx]!=NULL)&&(cursor_[notidx]->spev->absmilliseconds<i))
+ {
+ cursor_[notidx]=cursor_[notidx]->next;
+ while ((cursor_[notidx]==NULL)&&(cursor_line_[notidx]!=NULL))
+ {
+ cursor_line_[notidx]=cursor_line_[notidx]->next;
+ if (cursor_line_[notidx]!=NULL)
+ {
+ cursor_[notidx]=cursor_line_[notidx]->ev;
+ if ((cursor_line_[notidx]->num>first_line_[notidx]->num+NUMPREVLINES)
+ &&(cursor_line_[notidx]->num<first_line_[notidx]->num+nvisiblelines+1))
+ if ((first_line_[notidx]!=NULL)&&(first_line_[notidx]->num+nvisiblelines<=nlines_[notidx])) first_line_[notidx]=first_line_[notidx]->next;
+ }
+ }
+ }
+ }
+
+ if (linked_list!=NULL)
+ {
+ cursor_line=linked_list;
+ cursor=cursor_line->ev;
+ first_line=linked_list;
+ while ((cursor_line!=NULL)&&(cursor->spev->absmilliseconds<i))
+ {
+ cursor=cursor->next;
+ while ((cursor==NULL)&&(cursor_line!=NULL))
+ {
+ cursor_line=cursor_line->next;
+ if (cursor_line!=NULL)
+ {
+ cursor=cursor_line->ev;
+ if ((cursor_line->num>first_line->num+NUMPREVLINES)
+ &&(cursor_line->num<first_line->num+nvisiblelines+1))
+ if ((first_line!=NULL)&&(first_line->num+nvisiblelines<=nlines)) first_line=first_line->next;
+ }
+ }
+
+
+ }
+
+ killTimers();
+ autoscrollv=0;
+ setContentsPos(0,first_line->ypos);
+ viewport()->repaint();
+
+ }
+}
+
+QFont *KDisplayText::getFont(void)
+{
+ return qtextfont;
+}
+
+void KDisplayText::fontChanged(void)
+{
+ KConfig *kcfg=KGlobal::instance()->config();
+ kcfg->setGroup("KMid");
+ QFont *qtextfontdefault=new QFont(*qtextfont);
+ delete qtextfont;
+ qtextfont=new QFont(kcfg->readFontEntry("KaraokeFont",qtextfontdefault));
+ delete qtextfontdefault;
+ qfmetr=new QFontMetrics(*qtextfont);
+ calculatePositions();
+ nvisiblelines=height()/qfmetr->lineSpacing();
+ viewport()->repaint(TRUE);
+}
+
+void KDisplayText::ChangeTypeOfTextEvents(int type)
+{
+ int idxold=(typeoftextevents==1)?0:1;
+ int idxnew=(type==1)?0:1;
+ cursor_line_[idxold]=cursor_line;
+ first_line_[idxold]=first_line;
+ cursor_[idxold]=cursor;
+ linked_list=linked_list_[idxnew];
+ cursor_line=cursor_line_[idxnew];
+ first_line=first_line_[idxnew];
+ cursor=cursor_[idxnew];
+ nlines=nlines_[idxnew];
+ typeoftextevents=type;
+ if (first_line!=NULL)
+ {
+ nvisiblelines=height()/qfmetr->lineSpacing();
+ if (nlines>nvisiblelines)
+ resizeContents(maxX[(typeoftextevents==1)?0:1],maxY[(typeoftextevents==1)?0:1]);
+ else
+ resizeContents(0,0);
+
+ setContentsPos(0,first_line->ypos);
+ }
+ viewport()->repaint(TRUE);
+}
+
+int KDisplayText::ChooseTypeOfTextEvents(void)
+{
+ return (nlines_[0]>nlines_[1])? 1 : 5;
+}
+
+void KDisplayText::ScrollDown()
+{
+ scrollBy(0,2/**qfmetr->lineSpacing()*/);
+}
+
+void KDisplayText::ScrollUp()
+{
+ scrollBy(0,-2/**qfmetr->lineSpacing()*/);
+}
+
+void KDisplayText::ScrollPageDown()
+{
+ scrollBy(0,nvisiblelines*qfmetr->lineSpacing());
+}
+
+void KDisplayText::ScrollPageUp()
+{
+ scrollBy(0,-nvisiblelines*qfmetr->lineSpacing());
+}
+
+void KDisplayText::saveLyrics(FILE *fh)
+{
+ kdispt_line *Lptr=linked_list_[(typeoftextevents==1)? 0:1];
+ while (Lptr!=NULL)
+ {
+ kdispt_ev *Cptr=Lptr->ev;
+ if (Cptr!=NULL)
+ {
+ if (strcmp(Cptr->spev->text,"")!=0)
+ if (IsLineFeed(Cptr->spev->text[0],Cptr->spev->type))
+ fputs(&Cptr->spev->text[1],fh);
+ else
+ fputs(Cptr->spev->text,fh);
+ Cptr=Cptr->next;
+ }
+ while (Cptr!=NULL)
+ {
+ fputs(Cptr->spev->text,fh);
+ Cptr=Cptr->next;
+ }
+ fputs("\n",fh);
+ Lptr=Lptr->next;
+ }
+}
+
+void KDisplayText::timerEvent(QTimerEvent *e)
+{
+ int dy;
+ if (autoscrollv>0)
+ {
+ dy=2+autoscrollv/50;
+ scrollBy(0,dy);
+ autoscrollv-=dy;
+ if (autoscrollv<0)
+ {
+ killTimer(e->timerId());
+ autoscrollv=0;
+ }
+ }
+ else
+ if (autoscrollv<0)
+ {
+// dy=(autoscrollv<-2*qfmetr->lineSpacing())?-7:-2;
+ dy=-2+autoscrollv/50;
+ scrollBy(0,dy);
+ autoscrollv-=dy;
+ if (autoscrollv>0)
+ {
+ killTimer(e->timerId());
+ autoscrollv=0;
+ }
+ }
+ else
+ killTimer(e->timerId());
+
+}
+
+void KDisplayText::setLyricsEncoding(const QString &enc)
+{
+ QTextCodec *newcodec;
+ if (enc.isEmpty())
+ newcodec=KGlobal::locale()->codecForEncoding();
+ else
+ newcodec=QTextCodec::codecForName(enc.latin1());
+
+ if (newcodec!=lyrics_codec)
+ {
+ if (newcodec)
+ {
+ lyrics_codec=newcodec;
+ fontChanged();
+ }
+ }
+}
+
+#include "kdisptext.moc"
diff --git a/kmid/kdisptext.h b/kmid/kdisptext.h
new file mode 100644
index 00000000..e47026c6
--- /dev/null
+++ b/kmid/kdisptext.h
@@ -0,0 +1,125 @@
+/**************************************************************************
+
+ kdisptext.h - The widget that displays the karaoke/lyrics text
+ Copyright (C) 1997,98 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#ifndef _kdisptext_h_
+#define _kdisptext_h_
+
+#include <qwidget.h>
+#include <qscrollview.h>
+#include <libkmid/player.h>
+
+struct kdispt_ev
+{
+ SpecialEvent *spev;
+ int xpos;
+ class QRect r;
+ kdispt_ev *next;
+};
+
+struct kdispt_line
+{
+ kdispt_ev *ev;
+ int num;
+ int ypos;
+ kdispt_line *next;
+};
+
+class QFont;
+class QFontMetrics;
+class QScrollBar;
+
+class KDisplayText : public QScrollView
+{
+ Q_OBJECT
+private:
+ QTextCodec *lyrics_codec;
+
+ QFontMetrics *qfmetr;
+ QFont *qtextfont;
+
+ int typeoftextevents;
+
+ kdispt_line *linked_list_[2];
+ int nlines_[2];
+ kdispt_line *first_line_[2];
+ kdispt_line *cursor_line_[2];
+ kdispt_ev *cursor_[2];
+
+ kdispt_line *linked_list;
+ kdispt_line *cursor_line;
+ kdispt_ev *cursor;
+
+ kdispt_line *first_line; // Pointer to first text at first visible line
+
+ int nlines; // Total number of lines
+ int nvisiblelines; // Number of visible lines
+
+ void RemoveLine(kdispt_line *tmpl);
+ void RemoveLinkedList(void);
+
+ int IsLineFeed(char c,int type=0);
+ virtual void drawContents(QPainter *qpaint,int clipx,int clipy,int clipw,int cliph);
+
+ kdispt_line * searchYOffset(int y, kdispt_line *start);
+
+ int maxX[2];
+ int maxY[2];
+ int autoscrollv;
+
+protected:
+ virtual void resizeEvent(QResizeEvent *qre);
+ virtual void timerEvent(QTimerEvent *e);
+
+public:
+ KDisplayText(QWidget *parent,const char *name);
+ virtual ~KDisplayText();
+
+ void ClearEv(bool totally=true);
+ void AddEv(SpecialEvent *ev);
+ void calculatePositions(void);
+
+ void CursorToHome(void);
+
+ void ChangeTypeOfTextEvents(int type);
+ int ChooseTypeOfTextEvents(void);
+ void PaintIn(int type);
+ void gotomsec(ulong i);
+
+ QFont *getFont(void);
+ void fontChanged(void);
+
+ void saveLyrics(FILE *fh);
+
+ void setLyricsEncoding(const QString &enc);
+
+// QSize sizeHint();
+
+public slots:
+ void ScrollDown();
+ void ScrollUp();
+ void ScrollPageDown();
+ void ScrollPageUp();
+
+};
+
+#endif
diff --git a/kmid/keyboard.xpm b/kmid/keyboard.xpm
new file mode 100644
index 00000000..9859e3a7
--- /dev/null
+++ b/kmid/keyboard.xpm
@@ -0,0 +1,50 @@
+/* XPM */
+static char * keyboard_xpm[] = {
+"63 45 2 1",
+" c #000000000000",
+". c #FFFFFFFFFFFF",
+" ",
+"..... .. ..... ..... .. .. ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... . .. . ..... ..... . .. . .. . ..... ",
+"..... .... .. .... ..... ..... .... .. .... .. .... ..... ",
+"..... .. ..... ..... .. .. ..... ",
+"...... .... ...... ...... .... .... ...... ",
+"........ ........ ........ ........ ........ ........ ........ ",
+"........ ........ ........ ........ ........ ........ ........ ",
+"........ ........ ........ ........ ........ ........ ........ ",
+"........ ........ ........ ........ ........ ........ ........ ",
+"........ ........ ........ ........ ........ ........ ........ ",
+"........ ........ ........ ........ ........ ........ ........ ",
+"........ ........ ........ ........ ........ ........ ........ ",
+"........ ........ ........ ........ ........ ........ ........ ",
+"........ ........ ........ ........ ........ ........ ........ ",
+"........ ........ ........ ........ ........ ........ ........ ",
+"........ ........ ........ ........ ........ ........ ........ ",
+"........ ........ ........ ........ ........ ........ ........ ",
+"........ ........ ........ ........ ........ ........ ........ ",
+"........ ........ ........ ........ ........ ........ ........ ",
+"........ ........ ........ ........ ........ ........ ........ ",
+" ",
+" "};
diff --git a/kmid/klcdnumber.cpp b/kmid/klcdnumber.cpp
new file mode 100644
index 00000000..190d8da4
--- /dev/null
+++ b/kmid/klcdnumber.cpp
@@ -0,0 +1,339 @@
+/**************************************************************************
+
+ klcdnumber.cpp - The KLCDNumber widget (displays a lcd number)
+ Copyright (C) 1998 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#include "klcdnumber.h"
+#include <qpainter.h>
+#include <stdio.h>
+#include "ktrianglebutton.h"
+
+#define BUTTONWIDTH 9
+
+KLCDNumber::KLCDNumber(int _numDigits,QWidget *parent,const char *name)
+ : QWidget(parent,name)
+{
+ initDigits();
+ setUserChangeValue=false;
+ numDigits=_numDigits;
+ value=0;
+ minValue=0;
+ maxValue=1000;
+ oldvalue=-1;
+ // setPalette( QPalette (QColor(0,0,0)));
+ upBtn=NULL;
+ downBtn=NULL;
+ setUserDefaultValue=false;
+ doubleclicked=false;
+ setLCDBackgroundColor(0,0,0);
+ setLCDColor(100,255,100);
+}
+
+KLCDNumber::KLCDNumber(bool _setUserChangeValue,int _numDigits,QWidget *parent,const char *name)
+ : QWidget(parent,name)
+{
+ initDigits();
+ setUserChangeValue=_setUserChangeValue;
+ numDigits=_numDigits;
+ value=0;
+ minValue=0;
+ maxValue=1000;
+ oldvalue=-1;
+// setBackgroundColor(QColor(0,0,0));
+// setPalette( QPalette (QColor(0,0,0)));
+ upBtn=NULL;
+ downBtn=NULL;
+ setUserDefaultValue=false;
+ doubleclicked=false;
+ setLCDBackgroundColor(0,0,0);
+ setLCDColor(100,255,100);
+ if (setUserChangeValue)
+ {
+ upBtn=new KTriangleButton(KTriangleButton::Right,this,"Up");
+ downBtn=new KTriangleButton(KTriangleButton::Left,this,"Down");
+ upBtn->setGeometry(width()-BUTTONWIDTH,0,BUTTONWIDTH,height());
+ downBtn->setGeometry(0,0,BUTTONWIDTH,height());
+ connect(upBtn,SIGNAL(clicked()),this,SLOT(increaseValue()));
+ connect(downBtn,SIGNAL(clicked()),this,SLOT(decreaseValue()));
+ connect(upBtn,SIGNAL(clickedQuickly()),this,SLOT(increaseValueFast()));
+ connect(downBtn,SIGNAL(clickedQuickly()),this,SLOT(decreaseValueFast()));
+ };
+}
+
+void KLCDNumber::initDigits (void)
+{
+Digit[0] = digit(true,true,true,false,true,true,true);
+Digit[1] = digit(false,false,true,false,false,true,false);
+Digit[2] = digit(true,false,true,true,true,false,true);
+Digit[3] = digit(true,false,true,true,false,true,true);
+Digit[4] = digit(false,true,true,true,false,true,false);
+Digit[5] = digit(true,true,false,true,false,true,true);
+Digit[6] = digit(true,true,false,true,true,true,true);
+Digit[7] = digit(true,false,true,false,false,true,false);
+Digit[8] = digit(true,true,true,true,true,true,true);
+Digit[9] = digit(true,true,true,true,false,true,true);
+Digit[10] = digit(false,false,false,false,false,false,false);
+}
+
+void KLCDNumber::resizeEvent ( QResizeEvent *)
+{
+ if (setUserChangeValue)
+ {
+ upBtn->setGeometry(width()-BUTTONWIDTH,0,BUTTONWIDTH,height());
+ downBtn->setGeometry(0,0,BUTTONWIDTH,height());
+ }
+}
+
+void KLCDNumber::drawVerticalBar(QPainter *qpaint,int x,int y,int w,int h,int d)
+{
+ /* If d is 0 it means that it is a bar on the left and if 1 it is on the right */
+ /*
+ |\ /|
+ | | | |
+ | | = 0 1 = | |
+ | | | |
+ |/ \|
+ */
+
+ y++;
+ h-=2;
+ if (d==0)
+ {
+ for (int i=x;i<x+w;i++)
+ {
+ qpaint->drawLine(i,y,i,y+h);
+ y++;
+ h-=2;
+ }
+ } else {
+ for (int i=x+w;i>x;i--)
+ {
+ qpaint->drawLine(i,y,i,y+h);
+ y++;
+ h-=2;
+ }
+ }
+}
+
+void KLCDNumber::drawHorizBar(QPainter *qpaint,int x,int y,int w,int h,int d)
+{
+ // 0 is upper, 1 is bottom and 2 is the middle bar.
+ x++;
+ w-=3;
+ if (d==0)
+ {
+ for (int i=y;i<y+h;i++)
+ {
+ qpaint->drawLine(x,i,x+w,i);
+ x++;
+ w-=2;
+ }
+ }
+ else if (d==1)
+ {
+ for (int i=y+h;i>y;i--)
+ {
+ qpaint->drawLine(x,i,x+w,i);
+ x++;
+ w-=2;
+ }
+ }
+ else
+ {
+ for (int i=0;i<=h/2;i++)
+ {
+ qpaint->drawLine(x,y-i,x+w,y-i);
+ qpaint->drawLine(x,y+i,x+w,y+i);
+ x++;
+ w-=2;
+ }
+
+ };
+
+}
+
+void KLCDNumber::drawDigit(QPainter *qpaint,int x,int y,int w,int h,digit d)
+{
+if (d.nw) drawVerticalBar(qpaint,x,y,w/5,h/2,0);
+if (d.ne) drawVerticalBar(qpaint,x+w*4/5,y,w/5,h/2,1);
+if (d.sw) drawVerticalBar(qpaint,x,y+h/2,w/5,h/2,0);
+if (d.se) drawVerticalBar(qpaint,x+w*4/5,y+h/2,w/5,h/2,1);
+if (d.up) drawHorizBar (qpaint,x,y,w,w/5,0);
+if (d.bt) drawHorizBar (qpaint,x,y+h-w/5,w,w/5,1);
+if (d.md) drawHorizBar (qpaint,x,y+h/2,w,w/5,2);
+}
+
+
+void KLCDNumber::setValue(double v)
+{
+ oldvalue=value;
+ if (v<minValue)
+ value=minValue;
+ else if (v>maxValue)
+ value=maxValue;
+ else
+ value=v;
+
+}
+
+void KLCDNumber::display (double v)
+{
+ setValue(v);
+ repaint(FALSE);
+}
+
+void KLCDNumber::display (int v)
+{
+ display((double)v);
+}
+
+
+void KLCDNumber::paintEvent ( QPaintEvent * )
+{
+ QPainter qpaint(this);
+ qpaint.fillRect(0,0,width(),height(),backgcolor);
+ qpaint.setPen(LCDcolor);
+
+ QString numStr;
+ numStr.setNum((int) value);
+ numStr = numStr.rightJustify( numDigits, ' ', true);
+
+ int dx,dy,dw,dh;
+ if (setUserChangeValue)
+ {
+ dx=BUTTONWIDTH;
+ dy=height()/10;
+ dh=height()-dy*2;
+ dw=(width()-(BUTTONWIDTH*2))/numDigits;
+ } else
+ {
+ dx=0;
+ dy=height()/10;
+ dh=height()-dy*2;
+ dw=width()/numDigits;
+ };
+ int sep=dw/10;
+
+ for(int i=0; i < numDigits; i++)
+ {
+ char dig = numStr[i].latin1();
+ if ((dig>='0')&&(dig<='9'))
+ drawDigit(&qpaint,dx+sep,dy,dw-sep*2,dh,Digit[dig-'0']);
+ else
+ drawDigit(&qpaint,dx+sep,dy,dw-sep*2,dh,KLCDNumber::Digit[10]);
+ dx+=dw;
+ };
+}
+
+void KLCDNumber::setUserSetDefaultValue(bool _userSetDefaultValue)
+{
+ if (setUserDefaultValue!=_userSetDefaultValue)
+ {
+ setUserDefaultValue=_userSetDefaultValue;
+ /* if (setUserDefaultValue)
+ connect();
+ else
+ disconnect();
+ */
+ }
+
+}
+
+void KLCDNumber::setDefaultValue(double v)
+{
+ defaultValue=v;
+}
+
+
+void KLCDNumber::decreaseValue()
+{
+ display( value-1 );
+ emit valueChanged( value );
+}
+
+void KLCDNumber::increaseValue()
+{
+ display( value+1 );
+ emit valueChanged( value );
+}
+
+void KLCDNumber::decreaseValueFast()
+{
+ display( value-1 );
+}
+
+void KLCDNumber::increaseValueFast()
+{
+ display( value+1 );
+}
+
+void KLCDNumber::mouseDoubleClickEvent (QMouseEvent *)
+{
+ doubleclicked=true;
+ defaultValueClicked();
+}
+
+void KLCDNumber::mousePressEvent (QMouseEvent *e)
+{
+ startTimer(200);
+ QWidget::mousePressEvent(e);
+}
+
+void KLCDNumber::timerEvent(QTimerEvent *)
+{
+ killTimers();
+ doubleclicked=false;
+
+}
+
+
+void KLCDNumber::defaultValueClicked()
+{
+ if (setUserDefaultValue)
+ {
+ display( defaultValue );
+ emit valueChanged( value );
+ }
+}
+
+void KLCDNumber::setLCDBackgroundColor(int r,int g,int b)
+{
+ backgcolor=QColor(r,g,b);
+ repaint(FALSE);
+}
+
+void KLCDNumber::setLCDColor(int r,int g,int b)
+{
+ LCDcolor=QColor(r,g,b);
+ repaint(FALSE);
+}
+
+void KLCDNumber::setRange(double min, double max)
+{
+ minValue=min;
+ maxValue=max;
+}
+
+QSize KLCDNumber::sizeHint()
+{
+ return QSize(83,28);
+}
+#include "klcdnumber.moc"
diff --git a/kmid/klcdnumber.h b/kmid/klcdnumber.h
new file mode 100644
index 00000000..0359e472
--- /dev/null
+++ b/kmid/klcdnumber.h
@@ -0,0 +1,153 @@
+/**************************************************************************
+
+ klcdnumber.h - The KLCDNumber widget (displays a lcd number)
+ Copyright (C) 1998 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#ifndef _klcdnumber_h_
+#define _klcdnumber_h_
+
+#include <qwidget.h>
+
+class QPainter;
+class KTriangleButton;
+class QColor;
+
+class KLCDNumber : public QWidget
+{
+ Q_OBJECT
+protected:
+ class digit {
+public:
+ digit()
+ : up(false), nw(false), ne(false), md(false),
+ sw(false), se(false), bt(false) { }
+ digit( bool _up, bool _nw, bool _ne,
+ bool _md, bool _sw, bool _se, bool _bt)
+ : up(_up), nw(_nw), ne(_ne), md(_md),
+ sw(_sw), se(_se), bt(_bt) { }
+ bool up;
+ bool nw;
+ bool ne;
+ bool md;
+ bool sw;
+ bool se;
+ bool bt;
+ };
+ /*
+ up
+ ---
+ nw| |ne
+ |___|<------ md
+ | |
+ sw|___|se
+ bt
+ */
+
+
+ KLCDNumber::digit Digit[11];
+/*
+={
+ / 0 / {true,true,true,false,true,true,true},
+ / 1 / {false,false,true,false,false,true,false},
+ / 2 / {true,false,true,true,true,false,true},
+ / 3 / {true,false,true,true,false,true,true},
+ / 4 / {false,true,true,true,false,true,false},
+ / 5 / {true,true,false,true,false,true,true},
+ / 6 / {true,true,false,true,true,true,true},
+ / 7 / {true,false,true,false,false,true,false},
+ / 8 / {true,true,true,true,true,true,true},
+ / 9 / {true,true,true,true,false,true,true},
+ / / {false,false,false,false,false,false,false}
+ };*/
+
+ int numDigits;
+ bool setUserChangeValue;
+ bool setUserDefaultValue;
+ bool doubleclicked;
+
+ QColor backgcolor;
+ QColor LCDcolor;
+
+ double value;
+ double oldvalue;
+ double defaultValue;
+
+ double minValue;
+ double maxValue;
+
+ void drawVerticalBar(QPainter *qpaint,int x,int y,int w,int h,int d);
+ void drawHorizBar(QPainter *qpaint,int x,int y,int w,int h,int d);
+ void drawDigit(QPainter *qpaint,int x,int y,int w,int h,digit d);
+
+ void initDigits(void);
+
+public:
+ KLCDNumber(int _numDigits,QWidget *parent,const char *name);
+ KLCDNumber(bool _setUserChangeValue,int _numDigits,QWidget *parent,const char *name);
+
+ void setUserSetDefaultValue(bool _userSetDefaultValue);
+ void setDefaultValue(double v);
+
+ void setValue(double v);
+ double getValue(void) { return value; };
+ double getOldValue(void) { return oldvalue; };
+
+ double getMinValue(void) { return minValue;};
+ double getMaxValue(void) { return maxValue;};
+ void setRange(double min, double max);
+
+ void setLCDBackgroundColor (int r,int g,int b);
+ void setLCDColor (int r,int g,int b);
+
+ void display (int v);
+ void display (double v);
+
+ QSize sizeHint ();
+// QSizePolicy sizePolicy();
+
+protected:
+
+ virtual void paintEvent ( QPaintEvent *e );
+ virtual void resizeEvent ( QResizeEvent *e);
+ virtual void mouseDoubleClickEvent (QMouseEvent *e);
+ virtual void mousePressEvent (QMouseEvent *e);
+ virtual void timerEvent(QTimerEvent *e);
+ void defaultValueClicked();
+
+ KTriangleButton *downBtn;
+ KTriangleButton *upBtn;
+
+
+
+public slots:
+
+ void decreaseValue();
+ void increaseValue();
+ void decreaseValueFast();
+ void increaseValueFast();
+
+signals:
+
+ void valueChanged(double v);
+
+};
+
+#endif
diff --git a/kmid/kmid.desktop b/kmid/kmid.desktop
new file mode 100644
index 00000000..3920b855
--- /dev/null
+++ b/kmid/kmid.desktop
@@ -0,0 +1,88 @@
+[Desktop Entry]
+Name=KMid
+Name[af]=Kmid
+Name[bn]=কে-মিড
+Name[eo]=MIDI-ludilo
+Name[hi]=के-मिड
+Name[ne]=केडीई मिड
+Name[sv]=Kmid
+Name[tr]=Kmid
+Name[zh_HK]=KMidi
+Name[zh_TW]=KMid 播放器
+Name[zu]=KMidi
+MimeType=audio/x-karaoke;audio/x-midi;audio/midi;
+GenericName=Midi/Karaoke Player
+GenericName[af]=Midi/Kareokie Speler
+GenericName[ar]=مشغل Midi/Karaoke
+GenericName[bg]=Midi/Karaoke плеър
+GenericName[br]=C'hoarier Midi/Karaoke
+GenericName[bs]=Preglednik Midi/Karaoke datoteka
+GenericName[ca]=Reproductor Midi/Karaoke
+GenericName[cs]=Přehrávač Midi/Karaoke
+GenericName[cy]=Chwaraewr Midi/Karaoke
+GenericName[da]=Midi/Karaoke-afspiller
+GenericName[de]=Midi/Karaoke-Programm
+GenericName[el]=Αναπαραγωγέας Midi/Karaoke
+GenericName[eo]=Ludilo por Midi/Karaoko-dosieroj
+GenericName[es]=Un reproductor MIDI/Karaoke
+GenericName[et]=Midi/karaoke mängija
+GenericName[eu]=Midi/Karaoke erreproduzigailua
+GenericName[fa]=پخش‌کنندۀ Midi/Karaoke
+GenericName[fi]=Midi/Karaokesoitin
+GenericName[fr]=Platine MIDI / Karaoké
+GenericName[ga]=Seinnteoir Midi/Karaoke
+GenericName[gl]=Reproductor de Midi/Karaoke
+GenericName[he]=נגן MIDI/קריוקי
+GenericName[hi]=मिडी/कराओके प्लेयर
+GenericName[hr]=Midi/Karaoke player
+GenericName[hu]=MIDI/Karaoke-lejátszó
+GenericName[is]=Midi / Karaoke spilari
+GenericName[it]=Lettore MIDI/Karaoke
+GenericName[ja]=Midi/カラオケプレーヤ
+GenericName[kk]=Midi/Караоке ойнатқышы
+GenericName[km]=កម្មវិធី​ចាក់ Midi/ខារ៉ាអូខេ
+GenericName[ko]=미디/노래방 재생기
+GenericName[lt]=Midi/Karaoke grotuvas
+GenericName[lv]=Midi/Karaoke Atskaņotājs
+GenericName[mk]=Midi/Karaoke изведувач
+GenericName[ms]=Pemain Midi/Karaoke
+GenericName[nb]=Midi-/karaokespiller
+GenericName[nds]=Midi-/Karaoke-Afspeler
+GenericName[ne]=मिडि/कारावके प्लेयर
+GenericName[nl]=Midi/karaokespeler
+GenericName[nn]=Midi-/karaoke-spelar
+GenericName[pl]=Odtwarzacz Midi/Karaoke
+GenericName[pt]=Leitor de MIDI/Karaoke
+GenericName[pt_BR]=Reprodutor de Midi/Karaokê
+GenericName[ro]=Program de redare MIDI/Karaoke
+GenericName[ru]=Проигрыватель midi/караоке
+GenericName[se]=Midi-/karaoke-čuojaheaddji
+GenericName[sk]=Prehrávač Midi/Karaoke
+GenericName[sl]=Predvajalnik datotek MIDI/Karaoke
+GenericName[sr]=MIDI/караока плејер
+GenericName[sr@Latn]=MIDI/karaoka plejer
+GenericName[sv]=Midi/Karaoke-spelare
+GenericName[ta]=ஒரு மிடி/கரயோக்கே இயக்கி
+GenericName[tg]=Бозингари Midi/Karaoke
+GenericName[th]=โปรแกรมเล่นแฟ้มมิดี้/คาราโอเกะ
+GenericName[tr]=Midi ve Karaoke Yürütücü
+GenericName[uk]=Програвач Midi/Karaoke
+GenericName[uz]=Midi/Karaoki pleyer
+GenericName[uz@cyrillic]=Midi/Караоки плейер
+GenericName[ven]=Midi/Tshitambi tsha Karaoke
+GenericName[xh]=Umdlali we Midi/Karaoke
+GenericName[zh_CN]=Midi/卡拉 OK 播放器
+GenericName[zh_HK]=Midi/卡拉OK 播放器
+GenericName[zh_TW]=Midi/卡拉OK播放器
+GenericName[zu]=Umdlali weMidi/Karaoke
+Exec=kmid %u
+Icon=kmid
+Path=
+DocPath=kmid/index.html
+Type=Application
+Terminal=false
+ServiceTypes=Browser/View
+X-KDE-Library=libkmidpart
+X-KDE-StartupNotify=true
+X-DCOP-ServiceType=Multi
+Categories=Qt;KDE;AudioVideo;X-KDE-More;
diff --git a/kmid/kmid.lsm b/kmid/kmid.lsm
new file mode 100644
index 00000000..bfae2aa6
--- /dev/null
+++ b/kmid/kmid.lsm
@@ -0,0 +1,22 @@
+Begin3
+Title: kmid
+Version: 2.0
+Entered-date: 30/04/2001
+Description: A midi/karaoke player with a very friendly interface.
+ Main features of kmid :
+ - Shows the text of karaoke files changing its color synchronized with music
+ - Use external synths, fm, awe or gus cards
+ - Has midi maps to allow using old keyboards with GM midi/karaoke files
+ - Stores an unlimited number of collections (also called playlists)
+ - Shows in a keyboard the notes played by each instrument (at realtime).
+ - Allows to change the tempo
+Keywords: mid kar player midi karaoke KDE X11 Qt kmid fm awe gus synth
+Author: larrosa@kde.org (Antonio Larrosa Jimenez)
+Maintained-by: larrosa@kde.org (Antonio Larrosa Jimenez)
+Primary-site: http://perso.wanadoo.es/antlarr/kmid.html
+Original-site: http://perso.wanadoo.es/antlarr/kmid.html
+Alternate-site: ftp://ftp.kde.org/pub/kde/apps/multimedia
+Platform: Linux, BSD and every OS in which OSS works, needs Qt 1.3,
+ X11 and the KDE libs
+Copying-policy: GPL
+End
diff --git a/kmid/kmid.spec b/kmid/kmid.spec
new file mode 100644
index 00000000..959ce342
--- /dev/null
+++ b/kmid/kmid.spec
@@ -0,0 +1,62 @@
+Summary: Play midi/karaoke files and show lyrics. Support External synths, AWE, GUS, and FM devices. For X11/KDE
+Name: kmid
+Version: 1.2
+Release: 1
+Copyright: GPL
+Group: X11/K Desktop Environment/Multimedia
+Source: kmid-1.2.tar.gz
+Requires: qt >= 1.3 , kdelibs
+
+%description
+KMid is a midi/karaoke file player, with configurable midi mapper,
+real Session Management, drag & drop, customizable fonts, etc.
+It has a very nice interface which let you easily follow the tune while
+changing the color of the lyrics.
+It supports output through external synthesizers, AWE, FM and GUS cards.
+It also has a keyboard view to see the notes played by each instrument
+
+%prep
+%setup
+
+%build
+export KDEDIR=/opt/kde
+./configure
+make all
+
+%install
+export KDEDIR=/opt/kde
+make install
+
+%files
+%doc kmid/README kmid/COPYING kmid/PEOPLE kmid/kmid.lsm kmid/ChangeLog
+
+/opt/kde/bin/kmid
+/opt/kde/share/doc/HTML/en/kmid
+/opt/kde/share/doc/HTML/es/kmid
+/opt/kde/share/icons/kmid.xpm
+/opt/kde/share/icons/mini/kmid.xpm
+/opt/kde/share/applnk/Multimedia/kmid.kdelnk
+/opt/kde/share/mimelnk/audio/x-karaoke.kdelnk
+/opt/kde/share/apps/kmid/toolbar
+/opt/kde/share/apps/kmid/fm
+/opt/kde/share/apps/kmid/maps/gm.map
+/opt/kde/share/apps/kmid/maps/YamahaPSS790.map
+/opt/kde/share/apps/kmid/maps/YamahaPSR500.map
+/opt/kde/share/apps/kmid/OFortuna.kar
+/opt/kde/share/apps/kmid/StopInTheNameOfLove.kar
+/opt/kde/share/apps/kmid/TheGirlFromIpanema.kar
+/opt/kde/share/apps/kmid/AnotherGlitchInTheCall.kar
+/opt/kde/share/locale/es/LC_MESSAGES/kmid.mo
+/opt/kde/share/locale/cs/LC_MESSAGES/kmid.mo
+/opt/kde/share/locale/de/LC_MESSAGES/kmid.mo
+/opt/kde/share/locale/fi/LC_MESSAGES/kmid.mo
+/opt/kde/share/locale/hr/LC_MESSAGES/kmid.mo
+/opt/kde/share/locale/pl/LC_MESSAGES/kmid.mo
+/opt/kde/share/locale/pt/LC_MESSAGES/kmid.mo
+/opt/kde/share/locale/sk/LC_MESSAGES/kmid.mo
+/opt/kde/share/locale/eo/LC_MESSAGES/kmid.mo
+/opt/kde/share/locale/no/LC_MESSAGES/kmid.mo
+/opt/kde/share/locale/sv/LC_MESSAGES/kmid.mo
+/opt/kde/share/locale/pt_BR/LC_MESSAGES/kmid.mo
+/opt/kde/share/locale/zh_CN.GB2312/LC_MESSAGES/kmid.mo
+/opt/kde/share/locale/zh_TW.Big5/LC_MESSAGES/kmid.mo
diff --git a/kmid/kmidIface.h b/kmid/kmidIface.h
new file mode 100644
index 00000000..75f2ca72
--- /dev/null
+++ b/kmid/kmidIface.h
@@ -0,0 +1,46 @@
+#ifndef _KMIDIFACE_H_
+#define _KMIDIFACE_H_
+
+#include <dcopobject.h>
+
+class KMidIface : virtual public DCOPObject
+{
+ K_DCOP
+
+k_dcop:
+// File
+
+ virtual int openURL( const QString s )=0;
+
+ // Song
+ virtual void play()=0;
+ virtual void pause()=0;
+ virtual void stop()=0;
+ virtual void rewind()=0;
+ virtual void forward()=0;
+ virtual void seek(int ms)=0;
+ virtual void prevSong()=0;
+ virtual void nextSong()=0;
+
+ virtual void setSongLoop(int i)=0;
+ virtual void setVolume(int i)=0;
+ virtual void setTempo(int i)=0;
+// GUI
+
+
+ virtual void setSongEncoding( int i )=0;
+ virtual void setLyricEvents( int i )=0;
+
+// Collections
+ virtual void setCurrentSong(int i)=0;
+
+ virtual void setActiveCollection( int i )=0;
+ virtual void setCollectionPlayMode(int i)=0;
+
+// Midi device
+
+ virtual void setMidiDevice(int i)=0;
+
+};
+#endif
+
diff --git a/kmid/kmid_part.cpp b/kmid/kmid_part.cpp
new file mode 100644
index 00000000..98241ac1
--- /dev/null
+++ b/kmid/kmid_part.cpp
@@ -0,0 +1,144 @@
+#include "kmid_part.h"
+
+#include <kinstance.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kaboutdata.h>
+#include <kaction.h>
+#include <qiconset.h>
+
+#include <kparts/partmanager.h>
+
+#include "kmidclient.h"
+#include <qtimer.h>
+#include <kdelibs_export.h>
+
+K_EXPORT_COMPONENT_FACTORY( libkmidpart, KMidFactory )
+
+/**
+ * We need one static instance of the factory for our C 'main'
+ * function
+ */
+KInstance *KMidFactory::s_instance = 0L;
+
+KMidFactory::KMidFactory()
+{
+ s_instance=0L;
+}
+
+KMidFactory::~KMidFactory()
+{
+ if (s_instance)
+ {
+ delete s_instance->aboutData();
+ delete s_instance;
+ }
+
+ s_instance = 0;
+}
+
+KParts::Part *KMidFactory::createPartObject(QWidget *parentWidget, const char *widgetName,
+ QObject *parent, const char *name, const char*,
+ const QStringList& )
+{
+ KParts::Part *obj = new KMidPart(parentWidget, widgetName, parent, name);
+ return obj;
+}
+
+KAboutData *KMidFactory::aboutData()
+{
+ KAboutData *aboutdata = new KAboutData("kmid", "KMid", "2.0",
+ I18N_NOOP("MIDI/Karaoke file player"), KAboutData::License_GPL,
+ I18N_NOOP("(c) 1997,98,99,2000, Antonio Larrosa Jimenez"),"",
+ "http://perso.wanadoo.es/antlarr/kmid.html");
+ aboutdata->addAuthor("Antonio Larrosa Jimenez",
+ I18N_NOOP("Original Developer/Maintainer"),"larrosa@kde.org",
+ "http://perso.wanadoo.es/antlarr/index.html");
+ return aboutdata;
+}
+
+KInstance *KMidFactory::instance()
+{
+ if ( !s_instance )
+ s_instance = new KInstance( aboutData() );
+
+ return s_instance;
+}
+
+ KMidPart::KMidPart(QWidget *parentWidget, const char *widgetName,
+ QObject *parent, const char *name)
+: KParts::ReadOnlyPart(parent, name)
+{
+ setInstance(KMidFactory::instance());
+
+ widget = new kmidClient(parentWidget, actionCollection());
+ widget->show();
+ widget->setFocusPolicy(QWidget::ClickFocus);
+ setWidget(widget);
+
+ // create and connect our actions
+ (void)new KAction(i18n("Play"), "player_play", 0, this,
+ SLOT(slotPlay()), actionCollection(),
+ "play");
+
+ (void)new KAction(i18n("Stop"), "player_stop", 0, this,
+ SLOT(slotStop()), actionCollection(),
+ "stop");
+
+ (void)new KAction(i18n("Backward"),
+ "2leftarrow", 0, this,
+ SLOT(slotBackward()), actionCollection(),
+ "backward");
+
+ (void)new KAction(i18n("Forward"),
+ "2rightarrow", 0, this,
+ SLOT(slotForward()), actionCollection(),
+ "forward");
+
+ m_extension = new KMidBrowserExtension(this);
+
+ setXMLFile("kmid_partui.rc");
+
+
+}
+
+KMidPart::~KMidPart()
+{
+}
+
+bool KMidPart::openFile()
+{
+ widget->openURL(m_file);
+ widget->stop();
+ widget->show();
+ QTimer::singleShot(2000, this, SLOT(slotPlay()));
+
+ return true;
+}
+
+bool KMidPart::closeURL()
+{
+ slotStop();
+ return true;
+}
+
+void KMidPart::slotPlay()
+{
+ widget->stop();
+ widget->play();
+}
+
+
+void KMidPart::slotStop()
+{
+ widget->stop();
+}
+KMidBrowserExtension::KMidBrowserExtension(KMidPart *parent)
+ : KParts::BrowserExtension(parent, "KMidBrowserExtension")
+{
+}
+
+KMidBrowserExtension::~KMidBrowserExtension()
+{
+}
+#include "kmid_part.moc"
diff --git a/kmid/kmid_part.h b/kmid/kmid_part.h
new file mode 100644
index 00000000..4b02e099
--- /dev/null
+++ b/kmid/kmid_part.h
@@ -0,0 +1,63 @@
+#ifndef __KMIDPART_H__
+#define __KMIDPART_H__
+
+#include <kparts/browserextension.h>
+#include <kparts/factory.h>
+#include <kdelibs_export.h>
+class KAboutData;
+class KInstance;
+class KMidBrowserExtension;
+class kmidClient;
+
+class KDE_EXPORT KMidFactory : public KParts::Factory
+{
+ Q_OBJECT
+ public:
+ KMidFactory();
+ virtual ~KMidFactory();
+
+ virtual KParts::Part* createPartObject(QWidget *parentWidget, const char *widgetName,
+ QObject* parent = 0, const char* name = 0,
+ const char* classname = "QObject",
+ const QStringList &args = QStringList());
+
+ static KInstance *instance();
+
+ static KAboutData *aboutData();
+ private:
+ static KInstance *s_instance;
+};
+
+class KMidPart: public KParts::ReadOnlyPart
+{
+ Q_OBJECT
+ public:
+ KMidPart(QWidget *parentWidget, const char *widgetName,
+ QObject *parent, const char *name);
+ virtual ~KMidPart();
+
+
+ virtual bool closeURL();
+
+ protected:
+ virtual bool openFile();
+
+ protected slots:
+ void slotPlay();
+ void slotStop();
+
+ private:
+ kmidClient *widget;
+ KMidBrowserExtension *m_extension;
+};
+
+class KMidBrowserExtension : public KParts::BrowserExtension
+{
+ Q_OBJECT
+ friend class KMidPart;
+ public:
+ KMidBrowserExtension(KMidPart *parent);
+ virtual ~KMidBrowserExtension();
+};
+
+#endif
diff --git a/kmid/kmid_partui.rc b/kmid/kmid_partui.rc
new file mode 100644
index 00000000..aad5c6b2
--- /dev/null
+++ b/kmid/kmid_partui.rc
@@ -0,0 +1,17 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui name="kmid" version="2">
+<MenuBar>
+ <Menu name="view">
+ <Action name="play"/>
+ <Action name="stop"/>
+ <Action name="backward"/>
+ <Action name="forward"/>
+ </Menu>
+</MenuBar>
+<ToolBar name="KMid-ToolBar">
+ <Action name="play"/>
+ <Action name="stop"/>
+ <Action name="backward"/>
+ <Action name="forward"/>
+</ToolBar>
+</kpartgui>
diff --git a/kmid/kmidbutton.h b/kmid/kmidbutton.h
new file mode 100644
index 00000000..cda01f59
--- /dev/null
+++ b/kmid/kmidbutton.h
@@ -0,0 +1,32 @@
+#include <qpushbutton.h>
+#include <qpainter.h>
+
+class KMidButton : public QPushButton
+{
+protected:
+
+ QPixmap pixmap1,pixmap2;
+
+ virtual void drawButton(QPainter *paint)
+ {
+ if ((isOn())&&(!pixmap1.isNull())) paint->drawPixmap(0,0,pixmap1);
+ else if ((!isOn())&&(!pixmap2.isNull())) paint->drawPixmap(0,0,pixmap2);
+ };
+
+public:
+
+ KMidButton (QWidget *parent,const char *name) : QPushButton (parent,name)
+ {
+ };
+
+ ~KMidButton()
+ {
+ };
+
+ void setPixmaps(const QPixmap& p1, const QPixmap& p2)
+ {
+ pixmap1=p1;
+ pixmap2=p2;
+ };
+
+};
diff --git a/kmid/kmidclient.cpp b/kmid/kmidclient.cpp
new file mode 100644
index 00000000..c7dcd009
--- /dev/null
+++ b/kmid/kmidclient.cpp
@@ -0,0 +1,1606 @@
+/**************************************************************************
+
+ kmidclient.cpp - The main client widget of KMid
+ Copyright (C) 1997,98 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/shm.h>
+#include <sys/wait.h>
+#include <signal.h> // kill is declared on signal.h on bsd, not sys/signal.h
+#include <sys/signal.h>
+
+#include <qkeycode.h>
+#include <qfiledialog.h>
+#include <qstring.h>
+#include <qlabel.h>
+#include <qfile.h>
+#include <qcombobox.h>
+#include <qlayout.h>
+
+#include <kapplication.h>
+#include <kcharsets.h>
+#include <kconfig.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <kurl.h>
+#include <kaction.h>
+#include <kdebug.h>
+
+#include <libkmid/midimapper.h>
+#include <libkmid/fmout.h>
+#include <libkmid/track.h>
+#include <libkmid/midispec.h>
+#include <libkmid/deviceman.h>
+#include <libkmid/mt32togm.h>
+#include "kmidclient.h"
+#include "klcdnumber.h"
+#include "randomlist.h"
+#include "channelview.h"
+#include "channel.h"
+#include "version.h"
+#include "rhythmview.h"
+
+//#define TEMPHACK
+
+
+kmidClient::kmidClient(QWidget *parent, KActionCollection *ac, const char *name)
+ : DCOPObject("KMidIface"), QWidget(parent,name)
+{
+ actionCollection=ac;
+ KConfig *cfg=kapp->config();
+ cfg->setGroup("KMid");
+ midifile_opened=0L;
+ loopsong=cfg->readNumEntry("Loop",0);
+ collectionplaymode=0;
+ collectionplaylist=0L;
+ channelView=0L;
+ noteArray=0L;
+ shuttingdown=false;
+
+ KConfig *kconf=KGlobal::instance()->config();
+
+ kconf->setGroup("KMid");
+ QString tmp2 = locateLocal("appdata", "collections");
+ collectionsfile=kconf->readPathEntry("CollectionsFile",tmp2);
+ slman=new SLManager();
+ slman->loadConfig(QFile::encodeName(collectionsfile));
+ currentsl=NULL;
+ // currentsl=slman->getCollection(activecollection);
+ itsme=0;
+ m_kMid.pid=0;
+ timebar = new QSlider(0,240000,30000,60000,QSlider::Horizontal, this);
+ timebar->setSteps(30000,60000);
+ timebar->setValue(0);
+ connect (timebar,SIGNAL(valueChanged(int)),this,SLOT(slotSeek(int)));
+
+ timetags = new QSliderTime(timebar,this);
+ timetags->setMinimumSize(timetags->sizeHint());
+
+ qlabelTempo= new QLabel(i18n("Tempo:"), this,"tempolabel",
+ QLabel::NoFrame);
+
+ tempoLCD = new KLCDNumber( true, 3, this, "TempoLCD");
+ tempoLCD->setValue(120);
+ tempoLCD->display(120);
+ currentTempo=120;
+ tempoLCD->setRange(3,999);
+ tempoLCD->setDefaultValue(120);
+ tempoLCD->setUserSetDefaultValue(true);
+ tempoLCD->setMinimumSize(tempoLCD->sizeHint());
+ connect(tempoLCD,SIGNAL(valueChanged(double)),this,SLOT(slotSetTempo(double)));
+
+ comboSongs = new QComboBox(FALSE, this,"Songs");
+ connect (comboSongs,SIGNAL(activated(int)),this,SLOT(slotSelectSong(int)));
+ comboSongs->setMinimumWidth(200);
+
+ comboEncodings = new QComboBox(FALSE, this, "Encodings");
+ connect (comboEncodings,SIGNAL(activated(int)),this,SLOT(slotSelectEncoding(int)));
+ comboEncodings->insertItem(i18n("Default"));
+ comboEncodings->insertStringList( KGlobal::charsets()->descriptiveEncodingNames() );
+ comboEncodings->setCurrentItem(0);
+
+ rhythmview= new RhythmView( this, "RhythmView");
+ rhythmview->setMaximumHeight(7);
+ rhythmview->setMinimumHeight(7);
+
+ volumebar = new QSlider(0,200,10,100,QSlider::Vertical, this );
+ volumebar->setSteps(10,20);
+ volumebar->setValue(100);
+ volumebar->setTickmarks(QSlider::NoMarks);
+ volumebar->setTickInterval(50);
+ connect (volumebar,SIGNAL(valueChanged(int)),this,SLOT(slotSetVolume(int)));
+
+ visiblevolumebar=cfg->readNumEntry("ShowVolumeBar",0);
+ if (visiblevolumebar) volumebar->show();
+ else volumebar->hide();
+
+ typeoftextevents=1;
+ kdispt=new KDisplayText( this, "KaraokeWindow");
+ kdispt->show();
+
+ timer4timebar=new QTimer(this);
+ connect (timer4timebar,SIGNAL(timeout()),this,SLOT(timebarUpdate()));
+ timer4events=new QTimer(this);
+ connect (timer4events,SIGNAL(timeout()),this,SLOT(processSpecialEvent()));
+
+ QString samplefile =
+ KGlobal::dirs()->findAllResources("appdata", "fm/*.o3").last();
+ samplefile.truncate(samplefile.findRev('/'));
+ FMOut::setFMPatchesDirectory(QFile::encodeName(samplefile));
+
+ m_kMid.pctlsmID=shmget(IPC_PRIVATE,sizeof(PlayerController),0666 | IPC_CREAT );
+ if (m_kMid.pctlsmID==-1)
+ {
+ printf("ERROR: Cannot allocate shared memory !!!\n"
+ "Please report to larrosa@kde.org\n");
+ exit(1);
+ }
+
+ m_kMid.pctl=(PlayerController *)shmat(m_kMid.pctlsmID,0L,0);
+ if (!m_kMid.pctl)
+ printf("ERROR: Cannot get shared memory !!! "
+ "Please report to larrosa@kde.org\n");
+ m_kMid.pctl->playing=0;
+ m_kMid.pctl->gm=1;
+ m_kMid.pctl->volumepercentage=100;
+ m_kMid.pctl->tempo=500000;
+ m_kMid.pctl->ratioTempo=1.0;
+ for (int i=0;i<16;i++)
+ {
+ m_kMid.pctl->forcepgm[i]=0;
+ m_kMid.pctl->pgm[i]=0;
+ }
+
+
+ kconf->setGroup("KMid");
+ int mididev=kconf->readNumEntry("MidiPortNumber",-1);
+
+ midi = new DeviceManager(mididev);
+ midi->initManager();
+ m_kMid.midi=midi;
+ player= new MidiPlayer(midi,m_kMid.pctl);
+
+ kconf->setGroup("Midimapper");
+ QCString qs=QFile::encodeName(kconf->readPathEntry("Loadfile","gm.map"));
+
+#ifdef KMidDEBUG
+ printf("Read Config file: %s\n",qs.data());
+#endif
+ setMidiMapFilename(qs.data());
+
+ initializing_songs=1;
+ kconf->setGroup("KMid");
+ setActiveCollection(kconf->readNumEntry("ActiveCollection",0));
+ initializing_songs=0;
+
+ QVBoxLayout *lv=new QVBoxLayout( this );
+ lv->addWidget( timebar );
+ lv->addWidget( timetags );
+ lv->addSpacing(5);
+ QHBoxLayout *lh=new QHBoxLayout( lv );
+ lh->addWidget( qlabelTempo );
+ lh->addWidget( tempoLCD );
+ lh->addWidget( comboSongs, 6 );
+ lv->addSpacing(5);
+ lh->addWidget( comboEncodings, 1 );
+ lv->addSpacing(5);
+ lv->addWidget( rhythmview );
+ lv->addSpacing(2);
+ QHBoxLayout *lh2=new QHBoxLayout( lv );
+ lh2->addWidget( volumebar );
+ lh2->addWidget( kdispt );
+}
+
+/*void kmidClient::resizeEvent(QResizeEvent *)
+{
+ //timebar->resize(width()-5,timebar->height());
+ timebar->setGeometry(5,10,width()-5,timebar->height());
+ timetags->setGeometry(5,10+timebar->height(),width()-5,timetags->getFontHeight());
+ comboSongs->setGeometry(tempoLCD->x()+tempoLCD->width()+15,tempoLCD->y(),width()-(tempoLCD->x()+tempoLCD->width()+25),tempoLCD->height());
+ rhythmview->setGeometry(5,10+timebar->height()+timetags->height()+5+tempoLCD->height()+2,width()-10,7);
+ volumebar->setGeometry(5,10+timebar->height()+timetags->height()+5+tempoLCD->height()+10,15,height()-(10+timebar->height()+timetags->height()+5+tempoLCD->height()+15));
+ kdispt->setGeometry(((visiblevolumebar)?25:5),10+timebar->height()+timetags->height()+5+tempoLCD->height()+10,width()-(5+((visiblevolumebar)?25:5)),height()-(10+timebar->height()+timetags->height()+5+tempoLCD->height()+10));
+}
+*/
+
+kmidClient::~kmidClient()
+{
+ if (m_kMid.pctl->playing==1)
+ {
+ stop();
+ // sleep(1);
+ }
+
+ if (m_kMid.pid!=0)
+ {
+ kill(m_kMid.pid,SIGTERM);
+ waitpid(m_kMid.pid, 0L, 0);
+ m_kMid.midi->closeDev();
+ m_kMid.pid=0;
+ }
+
+ allNotesOff();
+
+ delete midifile_opened;
+ delete player;
+ delete midi;
+ delete [] collectionplaylist;
+
+ saveCollections();
+ delete slman;
+
+// Let's detach and delete shared memory
+ shmdt((char *)m_kMid.pctl);
+ shmctl(m_kMid.pctlsmID, IPC_RMID, 0L);
+ m_kMid.pctl=0L;
+}
+
+// Use KURL::filename ! (David)
+char *extractFilename(const char *in,char *out)
+{
+ char *p=(char *)in;
+ char *result=out;
+ char *filename=(char *)in;
+ while (*p!=0)
+ {
+ if (*p=='/') filename=p+1;
+ p++;
+ }
+ while (*filename!=0)
+ {
+ *out=*filename;
+ out++;
+ filename++;
+ }
+ *out=0;
+ return result;
+}
+
+int kmidClient::openFile(const char *filename)
+{
+ m_kMid.pctl->message|=PLAYER_HALT;
+ stop();
+ int r;
+ player->setGenerateBeats(true);
+ if ((r=player->loadSong(filename))!=0)
+ {
+ QString errormsg;
+ switch (r)
+ {
+ case (-1) : errormsg =
+ i18n("The file %1 does not exist or cannot be opened.").arg(filename);
+ break;
+ case (-2) : errormsg =
+ i18n("The file %1 is not a MIDI file.").arg(filename);break;
+ case (-3) : errormsg =
+ i18n("Ticks per quarter note is negative. Please send this file to larrosa@kde.org");break;
+ case (-4) : errormsg =
+ i18n("Not enough memory.");break;
+ case (-5) : errormsg =
+ i18n("This file is corrupted or not well built.");break;
+ case (-6) : errormsg =
+ i18n("%1 is not a regular file.").arg(filename);break;
+ default : errormsg = i18n("Unknown error message");break;
+ }
+ KMessageBox::error(this, errormsg);
+ // player->loadSong(midifile_opened);
+ if (midifile_opened) delete midifile_opened;
+ midifile_opened=0L;
+ timebar->setRange(0,240000);
+ timebar->setValue(0);
+ timetags->repaint(TRUE);
+ kdispt->ClearEv();
+ kdispt->repaint(TRUE);
+ topLevelWidget()->setCaption("KMid");
+
+ return -1;
+ }
+
+ if (midifile_opened) delete midifile_opened;
+ midifile_opened=new char[strlen(filename)+1];
+ strcpy(midifile_opened,filename);
+#ifdef KMidDEBUG
+ printf("TOTAL TIME: %g milliseconds\n",player->information()->millisecsTotal);
+#endif
+ // noteArray=player->parseNotes();
+ noteArray=player->noteArray();
+ timebar->setRange(0,(int)(player->information()->millisecsTotal));
+ timetags->repaint(TRUE);
+ kdispt->ClearEv();
+ spev=player->specialEvents();
+ while (spev)
+ {
+ if ((spev->type==1) || (spev->type==5))
+ {
+ kdispt->AddEv(spev);
+ }
+ spev=spev->next;
+ }
+
+ kdispt->calculatePositions();
+ kdispt->CursorToHome();
+// kdispt->updateScrollBars();
+ emit mustRechooseTextEvent();
+ kdispt->repaint(TRUE);
+ tempoLCD->display(tempoToMetronomeTempo(m_kMid.pctl->tempo));
+ currentTempo=tempoLCD->getValue();
+ tempoLCD->setDefaultValue(tempoToMetronomeTempo(m_kMid.pctl->tempo)*m_kMid.pctl->ratioTempo);
+
+ char *fn=new char[strlen(filename)+20];
+ extractFilename(filename,fn);
+ char *capt=new char[strlen(fn)+20];
+ sprintf(capt,"KMid - %s",fn);
+ delete fn;
+ topLevelWidget()->setCaption(capt);
+ delete capt;
+
+ timebar->setValue(0);
+ return 0;
+}
+
+int kmidClient::openURL(const QString _url)
+{
+ KURL u(_url);
+ if (!u.isValid()) {printf("Malformed URL\n");return -1;};
+
+ QString filename;
+ bool deleteFile=false;
+ if (!u.isLocalFile())
+ {
+ filename = QString("/tmp/") + u.filename();
+ KIO::Job *iojob = KIO::copy( u, KURL::fromPathOrURL( filename ) );
+ downloaded=false;
+ connect( iojob, SIGNAL( result( KIO::Job *) ), this, SLOT(downloadFinished( KIO::Job * ) ) );
+
+ if (!downloaded)
+ kapp->enter_loop();
+ deleteFile=true;
+
+ }
+ else
+ {
+ filename=u.path();
+ }
+
+ QCString filename_8bit = QFile::encodeName(filename);
+ int r=-1;
+ if (!filename_8bit.isEmpty())
+ {
+ r=openFile(filename_8bit.data());
+
+ KConfig *cfg=KGlobal::instance()->config();
+ if (cfg->readBoolEntry("deleteTmpNonLocalFiles",false))
+ {
+ unlink(filename_8bit.data());
+ }
+ }
+ return r;
+}
+
+ulong kmidClient::timeOfNextEvent(int *type)
+{
+ int t=0;
+ ulong x=0;
+
+
+ if (!channelView)
+ {
+ if ((spev)&&(spev->type!=0))
+ {
+ t=1;
+ x=spev->absmilliseconds;
+ }
+ }
+ else
+ {
+ if (noteArray)
+ {
+ NoteArray::noteCmd *ncmd=noteArray->get();
+ if (!ncmd)
+ {
+ if ((spev)&&(spev->type!=0))
+ {
+ t=1;
+ x=spev->absmilliseconds;
+ }
+ }
+ else
+ {
+ if ((!spev)||(spev->type==0))
+ {
+ t=2;
+ x=ncmd->ms;
+ }
+ else
+ {
+ if (spev->absmilliseconds<ncmd->ms)
+ {
+ t=1;
+ x=spev->absmilliseconds;
+ }
+ else
+ {
+ t=2;
+ x=ncmd->ms;
+ }
+
+ }
+ }
+ }
+ }
+
+ if (type) *type=t;
+ return x;
+ /*
+
+ if (type!=NULL) *type=0;
+ if (channelView==NULL)
+ {
+ if ((spev!=NULL)&&(spev->type!=0))
+ {
+ if (type!=NULL) *type=1;
+ return spev->absmilliseconds;
+ }
+ else return 0;
+ }
+
+ if (noteArray==NULL) return 0;
+ noteCmd *ncmd=noteArray->get();
+ if (ncmd==NULL)
+ {
+ if ((spev!=NULL)&&(spev->type!=0))
+ {
+ if (type!=NULL) *type=1;
+ return spev->absmilliseconds;
+ }
+ else return 0;
+ }
+ else
+ {
+ if ((spev==NULL)||(spev->type==0))
+ {
+ if (type!=NULL) *type=2;
+ return ncmd->ms;
+ }
+ else
+ {
+ if (spev->absmilliseconds<ncmd->ms)
+ {
+ if (type!=NULL) *type=1;
+ return spev->absmilliseconds;
+ }
+ else
+ {
+ if (type!=NULL) *type=2;
+ return ncmd->ms;
+ }
+
+ }
+ }
+ */
+}
+
+void kmidClient::slotPlay()
+{
+ if (!player->isSongLoaded())
+ {
+ KMessageBox::sorry(this,
+ i18n("You must load a file before playing it."));
+ return;
+ }
+ if (m_kMid.pctl->playing==1)
+ {
+ KMessageBox::sorry(this,
+ i18n("A song is already being played."));
+ return;
+ }
+ if (midi->checkInit()==-1)
+ {
+ KMessageBox::error(this,
+ i18n("Could not open /dev/sequencer.\nProbably there is another program using it."));
+ return;
+ }
+
+ kdispt->CursorToHome();
+ m_kMid.pctl->message=0;
+ m_kMid.pctl->playing=0;
+ m_kMid.pctl->finished=0;
+ m_kMid.pctl->error=0;
+ m_kMid.pctl->SPEVplayed=0;
+ m_kMid.pctl->SPEVprocessed=0;
+#ifdef KMidDEBUG
+ passcount=0;
+#endif
+ noteArray->iteratorBegin();
+
+ QApplication::flushX();
+ if ((m_kMid.pid=fork())==0)
+ {
+#ifdef KMidDEBUG
+ printf("PlayerProcessID: %d\n",getpid());
+#endif
+ player->play(0,(void (*)(void))kmidOutput);
+#ifdef KMidDEBUG
+ printf("End of child process\n");
+#endif
+ _exit(0);
+ }
+ m_kMid.pctl->millisecsPlayed=0;
+
+
+ spev=player->specialEvents();
+#ifdef KMidDEBUG
+ printf("writing SPEV\n");
+ player->debugSpecialEvents();
+ printf("writing SPEV(END)\n");
+#endif
+
+ while ((m_kMid.pctl->playing==0)&&(m_kMid.pctl->error==0)) ;
+
+ if (m_kMid.pctl->error==1) return;
+ beginmillisec=m_kMid.pctl->beginmillisec;
+
+ int type;
+ ulong x=timeOfNextEvent(&type);
+ if (type!=0)
+ timer4events->start(x,TRUE);
+
+ timer4timebar->start(1000);
+
+#ifdef KMidDEBUG
+ printf("PlayerProcess: %d . ParentProcessID: %d\n",m_kMid.pid,getpid());
+ printf("******************************-\n");
+#endif
+}
+
+void kmidClient::timebarUpdate()
+{
+ itsme=1;
+ if (m_kMid.pctl->playing==0)
+ {
+ timer4timebar->stop();
+ }
+
+ timeval tv;
+ gettimeofday(&tv, NULL);
+ ulong currentmillisec=tv.tv_sec*1000+tv.tv_usec/1000;
+ m_kMid.pctl->millisecsPlayed=(currentmillisec-beginmillisec);
+
+ timebar->setValue((int)(m_kMid.pctl->millisecsPlayed));
+ itsme=0;
+ if ((m_kMid.pctl->playing==0)&&(m_kMid.pctl->finished==1))
+ {
+ waitpid(m_kMid.pid, NULL, 0);
+ if (loopsong)
+ {
+ play();
+ return;
+ }
+ else
+ nextSong();
+ }
+}
+
+void kmidClient::slotSeek(int i)
+{
+ if (itsme) return;
+
+ if (m_kMid.pctl->playing==0)
+ {
+ itsme=1;
+ timebar->setValue(0);
+ itsme=0;
+ return;
+ }
+
+ if (m_kMid.pctl->paused) return;
+
+ if (m_kMid.pid!=0)
+ {
+ kill(m_kMid.pid,SIGTERM);
+#ifdef KMidDEBUG
+ printf("Waiting for Process %d to be killed\n",m_kMid.pid);
+#endif
+ waitpid(m_kMid.pid, NULL, 0);
+ m_kMid.midi->closeDev();
+ m_kMid.pid=0;
+ }
+ allNotesOff();
+
+
+#ifdef KMidDEBUG
+ printf("change Time: %d\n",i);
+#endif
+
+ timer4events->stop();
+ if (channelView!=NULL) channelView->reset(0);
+
+ moveEventPointersTo((ulong)i);
+
+ m_kMid.pctl->playing=0;
+ m_kMid.pctl->OK=0;
+ m_kMid.pctl->error=0;
+ m_kMid.pctl->gotomsec=i;
+ m_kMid.pctl->message|=PLAYER_SETPOS;
+
+ QApplication::flushX();
+ if ((m_kMid.pid=fork())==0)
+ {
+#ifdef KMidDEBUG
+ printf("Player_ProcessID: %d\n",getpid());
+#endif
+
+ player->play(0,(void (*)(void))kmidOutput);
+
+#ifdef KMidDEBUG
+ printf("End of child process\n");
+#endif
+ _exit(0);
+ }
+
+ while ((m_kMid.pctl->playing==0)&&(m_kMid.pctl->error==0)) ;
+
+ if (m_kMid.pctl->error==1) return;
+ beginmillisec=m_kMid.pctl->beginmillisec-i;
+ ulong currentmillisec=m_kMid.pctl->beginmillisec;
+
+ int type;
+ ulong x=timeOfNextEvent(&type);
+ if (type!=0)
+ timer4events->start(x-(currentmillisec-beginmillisec),TRUE);
+
+ /*
+ if (spev==NULL) return;
+ ulong delaymillisec=spev->absmilliseconds-(currentmillisec-beginmillisec);
+ timer4events->start(delaymillisec,TRUE);
+ */
+
+ m_kMid.pctl->OK=0;
+/*
+ tempoLCD->display(tempoToMetronomeTempo(m_kMid.pctl->tempo));
+ currentTempo=tempoLCD->getValue();
+ tempoLCD->setDefaultValue(tempoToMetronomeTempo(m_kMid.pctl->tempo)*m_kMid.pctl->ratioTempo);
+*/
+}
+
+void kmidClient::moveEventPointersTo(ulong ms)
+{
+#ifdef KMidDEBUG
+ printf("Move To: %lu\n",ms);
+#endif
+ spev=player->specialEvents();
+
+ ulong tempo=(ulong)(500000 * m_kMid.pctl->ratioTempo);
+ int num=4;
+ int den=4;
+
+ while ((spev!=NULL)&&(spev->absmilliseconds<ms))
+ {
+ if (spev->type==3) tempo=spev->tempo;
+ else if (spev->type==6) {num=spev->num;den=spev->den;}
+ spev=spev->next;
+ }
+ tempoLCD->display(tempoToMetronomeTempo(tempo));
+ currentTempo=tempoLCD->getValue();
+ tempoLCD->setDefaultValue(tempoToMetronomeTempo(tempo)*m_kMid.pctl->ratioTempo);
+
+ rhythmview->setRhythm(num,den);
+
+ kdispt->gotomsec(ms);
+// if (noteArray!=NULL) noteArray->moveIteratorTo(ms);
+ if (noteArray!=NULL)
+ {
+ int pgm[16];
+ noteArray->moveIteratorTo(ms,pgm);
+ if (channelView!=NULL)
+ {
+ for (int j=0;j<16;j++)
+ {
+ if (!m_kMid.pctl->forcepgm[j]) channelView->changeInstrument(j,(m_kMid.pctl->gm==1)?(pgm[j]):(MT32toGM[pgm[j]]));
+ else channelView->changeInstrument(j,(m_kMid.pctl->pgm[j]));
+ }
+ }
+ }
+
+ /*
+ if (noteArray!=NULL)
+ {
+ noteCmd *ncmd;
+ noteArray->iteratorBegin();
+ ncmd=noteArray->get();
+ while ((ncmd!=NULL)&&(ncmd->ms<ms))
+ {
+ noteArray->next();
+ ncmd=noteArray->get();
+ }
+ }
+ */
+}
+
+void kmidClient::slotSetVolume(int i)
+{
+ int autochangemap=0;
+ if ((m_kMid.pctl->playing==1)&&(m_kMid.pctl->paused==0)) autochangemap=1;
+
+ if (autochangemap)
+ {
+ pause();
+ }
+ i=200-i;
+ m_kMid.pctl->volumepercentage=i;
+
+ if (autochangemap)
+ {
+ pause();
+ }
+}
+
+
+void kmidClient::slotPrevSong()
+{
+ if (currentsl==NULL) return;
+ if (collectionplaylist==NULL) generateCPL();
+ if (collectionplaylist==NULL) return;
+ /*
+ if (collectionplaymode==0)
+ {
+ if (currentsl->getActiveSongID()==1) return;
+ currentsl->previous();
+ }
+ else
+ {
+ int r;
+ while ((r=1+(int) ((double)(currentsl->NumberOfSongs())*rand()/(RAND_MAX+1.0)))==currentsl->getActiveSongID()) ;
+
+ currentsl->setActiveSong(r);
+ }
+ */
+ int idx=searchInCPL(currentsl->getActiveSongID());
+ if (idx==0) return;
+ idx--;
+ currentsl->setActiveSong(collectionplaylist[idx]);
+
+ if (currentsl->getActiveSongID()==-1)
+ {
+ // comboSongs->setCurrentItem(0);
+ // currentsl->setActiveSong(1);
+ return;
+ }
+
+ if (m_kMid.pctl->paused) emit stopPause();
+ comboSongs->setCurrentItem(currentsl->getActiveSongID()-1);
+ if (openURL(currentsl->getActiveSongName())==-1) return;
+ play();
+
+}
+
+void kmidClient::slotNextSong()
+{
+ if (currentsl==NULL) return;
+ if (collectionplaylist==NULL) generateCPL();
+ if (collectionplaylist==NULL) return;
+
+ /*if (collectionplaymode==0)
+ {
+ if (currentsl->getActiveSongID()==currentsl->NumberOfSongs()) return;
+ currentsl->next();
+ }
+ else
+ {
+ int r;
+ while ((r=1+(int) ((double)(currentsl->NumberOfSongs())*rand()/(RAND_MAX+1.0)))==currentsl->getActiveSongID()) ;
+
+ #ifdef KMidDEBUG
+ printf("random number:%d\n",r);
+ #endif
+ currentsl->setActiveSong(r);
+ }
+ */
+ int idx=searchInCPL(currentsl->getActiveSongID());
+ idx++;
+ if (idx==currentsl->NumberOfSongs()) return;
+ currentsl->setActiveSong(collectionplaylist[idx]);
+ if (currentsl->getActiveSongID()==-1)
+ {
+ //// comboSongs->setCurrentItem(0);
+ // currentsl->setActiveSong(1);
+ return;
+ }
+
+ if (m_kMid.pctl->paused) emit stopPause();
+ comboSongs->setCurrentItem(currentsl->getActiveSongID()-1);
+ if (openURL(currentsl->getActiveSongName())==-1) return;
+ play();
+}
+
+void kmidClient::slotPause()
+{
+ if (m_kMid.pctl->playing==0) return;
+#ifdef KMidDEBUG
+ printf("song Pause\n");
+#endif
+ if (m_kMid.pctl->paused==0)
+ {
+ if (m_kMid.pid!=0)
+ {
+ kill(m_kMid.pid,SIGTERM);
+ waitpid(m_kMid.pid, NULL, 0);
+ m_kMid.midi->closeDev();
+ m_kMid.pid=0;
+ }
+ pausedatmillisec=(ulong)m_kMid.pctl->millisecsPlayed;
+ m_kMid.pctl->paused=1;
+ timer4timebar->stop();
+ timer4events->stop();
+ allNotesOff();
+ // kill(m_kMid.pid,SIGSTOP);
+ // The previous line doesn't work because it stops the two processes (!?)
+ }
+ else
+ {
+ m_kMid.pctl->playing=0;
+ m_kMid.pctl->OK=0;
+ m_kMid.pctl->error=0;
+ m_kMid.pctl->gotomsec=pausedatmillisec;
+ m_kMid.pctl->message|=PLAYER_SETPOS;
+
+ QApplication::flushX();
+ if ((m_kMid.pid=fork())==0)
+ {
+#ifdef KMidDEBUG
+ printf("PlayerProcessID: %d\n",getpid());
+#endif
+ player->play(0,(void (*)(void))kmidOutput);
+#ifdef KMidDEBUG
+ printf("End of child process\n");
+#endif
+ _exit(0);
+ }
+
+ while ((m_kMid.pctl->playing==0)&&(m_kMid.pctl->error==0)) ;
+
+ if (m_kMid.pctl->error) return;
+
+ m_kMid.pctl->OK=0;
+ m_kMid.pctl->paused=0;
+
+ beginmillisec=m_kMid.pctl->beginmillisec-pausedatmillisec;
+ ulong currentmillisec=m_kMid.pctl->beginmillisec;
+
+ int type;
+ ulong x=timeOfNextEvent(&type);
+ if (type!=0)
+ timer4events->start(x-(currentmillisec-beginmillisec),TRUE);
+ timer4timebar->start(1000);
+
+ if (noteArray!=NULL)
+ {
+ int pgm[16];
+ noteArray->moveIteratorTo(pausedatmillisec,pgm);
+ if (channelView!=NULL)
+ {
+ for (int j=0;j<16;j++)
+ {
+ if (!m_kMid.pctl->forcepgm[j]) channelView->changeInstrument(j,(m_kMid.pctl->gm==1)?(pgm[j]):(MT32toGM[pgm[j]]));
+ else channelView->changeInstrument(j,(m_kMid.pctl->pgm[j]));
+ }
+ }
+
+ }
+
+ }
+}
+
+void kmidClient::shuttingDown(void)
+{
+ shuttingdown=true;
+ stop();
+}
+
+void kmidClient::slotStop()
+{
+ if (!m_kMid.pctl) return;
+
+ if (!shuttingdown)
+ {
+ for (int i=0;i<16;i++) m_kMid.pctl->forcepgm[i]=FALSE;
+ if (channelView) channelView->reset();
+ if (tempoLCD)
+ {
+ tempoLCD->display(tempoToMetronomeTempo(m_kMid.pctl->tempo));
+ currentTempo=tempoLCD->getValue();
+ tempoLCD->setDefaultValue(tempoToMetronomeTempo(m_kMid.pctl->tempo)*m_kMid.pctl->ratioTempo);
+ }
+ }
+
+ if (m_kMid.pctl->playing==0) return;
+
+ if (m_kMid.pctl->paused) return;
+#ifdef KMidDEBUG
+ printf("song Stop\n");
+#endif
+ if (m_kMid.pid!=0)
+ {
+ kill(m_kMid.pid,SIGTERM);
+#ifdef KMidDEBUG
+ printf("Killing\n");
+#endif
+ waitpid(m_kMid.pid, NULL, 0);
+ m_kMid.midi->closeDev();
+ m_kMid.pid=0;
+ }
+
+ m_kMid.pctl->playing=0;
+ ////////m_kMid.pctl->OK=0;
+ ////////m_kMid.pctl->message|=PLAYER_HALT;
+ timer4timebar->stop();
+ timer4events->stop();
+
+ allNotesOff();
+
+ //m_kMid.pctl->playing=0;
+ //m_kMid.pctl->paused=0;
+ ////////while (m_kMid.pctl->OK==0) ;
+}
+
+void kmidClient::slotRewind()
+{
+ if ((m_kMid.pctl->playing)&&(!m_kMid.pctl->paused))
+ {
+ timebar->subtractPage();
+ slotSeek(timebar->value());
+ }
+}
+
+void kmidClient::slotForward()
+{
+ if ((m_kMid.pctl->playing)&&(!m_kMid.pctl->paused))
+ {
+ timebar->addPage();
+ slotSeek(timebar->value());
+ }
+}
+
+
+void kmidClient::allNotesOff()
+{
+ bool done=false;
+ m_kMid.pctl->isSendingAllNotesOff=true;
+ DeviceManager *_midi=new DeviceManager();
+ _midi->initManager();
+ _midi->openDev();
+ _midi->allNotesOff();
+ _midi->closeDev();
+ delete _midi;
+ done=true;
+ m_kMid.pctl->isSendingAllNotesOff=false;
+}
+
+void kmidClient::kmidOutput(void)
+{
+// Should do nothing
+ /*
+ Midi_event *ev=pctl->ev;
+
+ timeval tv;
+ gettimeofday(&tv, NULL);
+ ulong currentmillisec=tv.tv_sec*1000+tv.tv_usec/1000;
+
+ if ((ev->command==MIDI_SYSTEM_PREFIX)&&((ev->command|ev->chn)==META_EVENT))
+ {
+ if ((ev->d1==5)||(ev->d1==1))
+ {
+ char *text=new char[ev->length+1];
+ strncpy(text,(char *)ev->data,ev->length);
+ text[ev->length]=0;
+#ifdef KMidDEBUG
+ printf("%s , played at: %ld\n",text,currentmillisec-beginmillisec);
+#endif
+ }
+ else if (ev->d1==ME_SET_TEMPO)
+ {
+ int tempo=(ev->data[0]<<16)|(ev->data[1]<<8)|(ev->data[2]);
+ // printf("Change tempo: %d , %g, played at:%ld\n",tempo,tempoToMetronomeTempo(tempo),currentmillisec-beginmillisec);
+ }
+
+ }
+ */
+}
+
+
+void kmidClient::processSpecialEvent()
+{
+/*
+ if (spev==NULL)
+ {
+ printf("SPEV == NULL !!!!!\n");
+ return;
+ }
+*/
+
+//#ifdef KMidDEBUG
+// printf(":::: %ld",passcount++);
+// printf("%d %s %ld",spev->type,spev->text,spev->absmilliseconds);
+//#endif
+
+ int processNext=1;
+ int type;
+ ulong x;
+
+ long delaymillisec=~0;
+
+ while (processNext)
+ {
+ /*
+ timeval tv;
+ gettimeofday(&tv, NULL);
+ ulong currentmillisec=tv.tv_sec*1000+tv.tv_usec/1000;
+ */
+
+ x=timeOfNextEvent(&type);
+
+ if (type==0) return;
+ if (type==1)
+ {
+ if ((spev->type==1) || (spev->type==5))
+ {
+ kdispt->PaintIn(spev->type);
+ }
+ else if (spev->type==3)
+ {
+ tempoLCD->display(tempoToMetronomeTempo(spev->tempo));
+#ifdef KMidDEBUG
+ printf("Changing lcd tempo: spev->tempo: %d , ratio: %.9g\n",spev->tempo,m_kMid.pctl->ratioTempo);
+ printf("Result: %g %.9g %d\n",tempoToMetronomeTempo(spev->tempo),tempoToMetronomeTempo(spev->tempo),(int)tempoToMetronomeTempo(spev->tempo));
+#endif
+ currentTempo=tempoLCD->getValue();
+ tempoLCD->setDefaultValue(tempoToMetronomeTempo(spev->tempo)*m_kMid.pctl->ratioTempo);
+ }
+ else if (spev->type==6)
+ {
+ rhythmview->setRhythm(spev->num,spev->den);
+ }
+ else if (spev->type==7)
+ {
+#ifdef KMidDEBUG
+ printf("Beat: %d/%d\n",spev->num,spev->den);
+#endif
+ rhythmview->Beat(spev->num);
+ }
+ m_kMid.pctl->SPEVprocessed++;
+ spev=spev->next;
+ }
+ if (type==2)
+ {
+ NoteArray::noteCmd *ncmd=noteArray->get();
+ if (ncmd==NULL) {printf("ncmd is NULL !!!");return;}
+ if (channelView!=NULL)
+ {
+ if (ncmd->cmd==1) channelView->noteOn(ncmd->chn,ncmd->note);
+ else if (ncmd->cmd==0) channelView->noteOff(ncmd->chn,ncmd->note);
+ else if (ncmd->cmd==2)
+ if (!m_kMid.pctl->forcepgm[ncmd->chn]) channelView->changeInstrument(ncmd->chn,(m_kMid.pctl->gm==1)?(ncmd->note):(MT32toGM[ncmd->note]));
+ else channelView->changeInstrument(ncmd->chn,(m_kMid.pctl->pgm[ncmd->chn]));
+
+ noteArray->next();
+ }
+ }
+ processNext=0;
+
+ x=timeOfNextEvent(&type);
+
+ if (type==0) return;
+
+ timeval tv;
+ ulong currentmillisec;
+ gettimeofday(&tv, NULL);
+ currentmillisec=tv.tv_sec*1000+tv.tv_usec/1000;
+ delaymillisec=x-(currentmillisec-beginmillisec);
+ if (delaymillisec<10) processNext=1;
+ }
+
+ if (delaymillisec!=~(long)0) timer4events->start(delaymillisec,TRUE);
+
+}
+
+void kmidClient::repaintText(int type)
+{
+ kdispt->ChangeTypeOfTextEvents(type);
+ typeoftextevents=type;
+ kdispt->repaint(TRUE);
+}
+
+int kmidClient::ChooseTypeOfTextEvents(void)
+{
+ return kdispt->ChooseTypeOfTextEvents();
+}
+
+void kmidClient::setSongType(int i)
+{
+ int autochangetype=0;
+ if ((m_kMid.pctl->playing==1)&&(m_kMid.pctl->paused==0)) autochangetype=1;
+
+ if (autochangetype)
+ {
+ pause();
+ }
+ m_kMid.pctl->gm=i;
+
+ if (autochangetype)
+ {
+ pause();
+ }
+
+}
+
+
+QFont * kmidClient::getFont(void)
+{
+return kdispt->getFont();
+}
+
+void kmidClient::fontChanged(void)
+{
+ kdispt->fontChanged();
+}
+
+void kmidClient::setMidiDevice(int i)
+{
+ midi->setDefaultDevice(i);
+}
+
+void kmidClient::setMidiMapFilename(const char *mapfilename)
+{
+ MidiMapper *map=new MidiMapper(mapfilename);
+ if (map->ok()==-1)
+ {
+ QString tmp = locate("appdata", QString("maps/") + mapfilename);
+ delete map;
+ map=new MidiMapper(tmp.local8Bit());
+ if (map->ok()!=1)
+ {
+ delete map;
+ map=new MidiMapper(NULL);
+ }
+ }
+ int autochangemap=0;
+ if ((m_kMid.pctl->playing==1)&&(m_kMid.pctl->paused==0)) autochangemap=1;
+
+ if (autochangemap)
+ {
+ pause();
+ }
+ midi->setMidiMap(map);
+ if (autochangemap)
+ {
+ pause();
+ }
+}
+
+void kmidClient::setSLManager(SLManager *slm)
+{
+ if (slman!=NULL) delete slman;
+ slman=slm;
+}
+
+void kmidClient::setActiveCollection(int i)
+{
+ activecollection=i;
+ KConfig *kconf=KGlobal::instance()->config();
+
+ kconf->setGroup("KMid");
+ kconf->writeEntry("ActiveCollection",activecollection);
+ currentsl=slman->getCollection(activecollection);
+ generateCPL();
+ initializing_songs=1;
+ fillInComboSongs();
+ initializing_songs=0;
+}
+
+void kmidClient::fillInComboSongs(void)
+{
+ //int oldselected=comboSongs->currentItem();
+ comboSongs->clear();
+ //comboSongs->setCurrentItem(-1);
+ if (currentsl==NULL) return;
+ currentsl->iteratorStart();
+ char temp[FILENAME_MAX];
+ char temp2[FILENAME_MAX];
+ QString qs;
+ while (!currentsl->iteratorAtEnd())
+ {
+ qs=currentsl->getIteratorName();
+ //KURL::decode(qs);
+ sprintf(temp,"%d - %s",currentsl->getIteratorID(),
+ extractFilename(KURL::decode_string(qs).ascii(),temp2));
+ comboSongs->insertItem(temp);
+ currentsl->iteratorNext();
+ }
+ if (currentsl->getActiveSongID()==-1) return;
+ comboSongs->setCurrentItem(currentsl->getActiveSongID()-1);
+ /*
+ if (oldselected==currentsl->getActiveSongID()-1)
+ {
+ slotSelectSong(currentsl->getActiveSongID()-1);
+ }
+ */
+ slotSelectSong(currentsl->getActiveSongID()-1);
+}
+
+void kmidClient::slotSelectSong(int i)
+{
+ if (currentsl==NULL) return;
+ i++;
+ if ((i<=0)) // The collection may be empty, or it may be just a bug :-)
+ {
+#ifdef KMidDEBUG
+ printf("Empty\n");
+#endif
+ emit stopPause();
+ if (m_kMid.pctl->playing) stop();
+ if (midifile_opened!=NULL) delete midifile_opened;
+ midifile_opened=NULL;
+ player->removeSong();
+ timebar->setRange(0,240000);
+ timebar->setValue(0);
+ timetags->repaint(TRUE);
+ kdispt->ClearEv();
+ kdispt->repaint(TRUE);
+ comboSongs->clear();
+ comboSongs->repaint(TRUE);
+ topLevelWidget()->setCaption("KMid");
+ return;
+ }
+
+ if ((i==currentsl->getActiveSongID())&&(!initializing_songs)) return;
+ int pl=0;
+ if (m_kMid.pctl->playing==1) pl=1;
+
+ if (m_kMid.pctl->paused) emit stopPause();
+ if (/*(i!=currentsl->getActiveSongID())&&*/(pl==1)) stop();
+ currentsl->setActiveSong(i);
+ if (openURL(currentsl->getActiveSongName())==-1) return;
+ if (pl) play();
+
+}
+
+
+int kmidClient::getSelectedSong(void)
+{
+ if (currentsl==NULL) return -1;
+ return currentsl->getActiveSongID();
+}
+
+
+void kmidClient::setSongLoop(int i)
+{
+ loopsong=i;
+}
+
+
+void kmidClient::generateCPL(void)
+{
+ delete [] collectionplaylist;
+ collectionplaylist=0;
+
+ if (currentsl==NULL) return;
+
+ if (collectionplaymode==0)
+ collectionplaylist=generate_list(currentsl->NumberOfSongs());
+ else
+ collectionplaylist=generate_random_list(currentsl->NumberOfSongs());
+}
+
+
+void kmidClient::setCollectionPlayMode(int i)
+{
+ collectionplaymode=i;
+ generateCPL();
+}
+
+void kmidClient::saveCollections(void)
+{
+ if (slman==NULL) return;
+#ifdef KMidDEBUG
+ printf("Saving collections in: %s\n",collectionsfile.ascii());
+#endif
+ slman->saveConfig(QFile::encodeName(collectionsfile));
+}
+
+void kmidClient::saveLyrics(FILE *fh)
+{
+ if (kdispt!=NULL) kdispt->saveLyrics(fh);
+}
+
+int kmidClient::searchInCPL(int song)
+{
+ if (currentsl==NULL) return -1;
+ int i=0;
+ int n=currentsl->NumberOfSongs();
+ while ((i<n)&&(collectionplaylist[i]!=song)) i++;
+ if (i<n) return i;
+ return -1;
+}
+
+void kmidClient::visibleVolumeBar(int i)
+{
+#ifndef TEMPHACK
+ visiblevolumebar=i;
+
+ if (visiblevolumebar)
+ volumebar->show();
+ else
+ volumebar->hide();
+#endif
+}
+
+void kmidClient::visibleChannelView(int i)
+{
+ if ((channelView==NULL)&&(i==1))
+ {
+ channelView=new ChannelView();
+ if (noteArray!=NULL)
+ {
+ int pgm[16],j;
+ noteArray->moveIteratorTo((ulong)m_kMid.pctl->millisecsPlayed,pgm);
+ for (j=0;j<16;j++)
+ {
+ if (!m_kMid.pctl->forcepgm[j]) channelView->changeInstrument(j,(m_kMid.pctl->gm==1)?(pgm[j]):(MT32toGM[pgm[j]]));
+ else channelView->changeInstrument(j,(m_kMid.pctl->pgm[j]));
+ channelView->changeForceState(j,m_kMid.pctl->forcepgm[j]);
+ }
+ }
+ channelView->show();
+ connect(channelView,SIGNAL(signalToKMidClient(int *)),this,SLOT(communicationFromChannelView(int *)));
+ connect(kapp,SIGNAL(shutDown()),parentWidget(),SLOT(shuttingDown()));
+
+ }
+ else if ((channelView!=NULL)&&(i==0))
+ {
+ delete channelView;
+ channelView=NULL;
+
+ }
+ rethinkNextEvent();
+}
+
+void kmidClient::channelViewDestroyed()
+{
+ channelView=NULL;
+ rethinkNextEvent();
+}
+
+
+void kmidClient::rethinkNextEvent(void)
+{
+ if (m_kMid.pctl->playing==0) return;
+ timer4events->stop();
+
+ int type;
+ ulong delaymillisec;
+ ulong x=timeOfNextEvent(&type);
+
+ if (type==0) return;
+
+ timeval tv;
+ ulong currentmillisec;
+ gettimeofday(&tv, NULL);
+ currentmillisec=tv.tv_sec*1000+tv.tv_usec/1000;
+ delaymillisec=x-(currentmillisec-beginmillisec);
+
+ timer4events->start(delaymillisec,TRUE);
+}
+
+void kmidClient::communicationFromChannelView(int *i)
+{
+ if (i==NULL) return;
+ int autocontplaying=0;
+ if ((i[0]==CHN_CHANGE_PGM)||((i[0]==CHN_CHANGE_FORCED_STATE)&&(i[3]==1)))
+ {
+ if ((m_kMid.pctl->playing==1)&&(m_kMid.pctl->paused==0)) autocontplaying=1;
+
+ if (autocontplaying)
+ {
+ pause();
+ }
+ }
+ if (i[0]==CHN_CHANGE_PGM)
+ m_kMid.pctl->pgm[i[1]-1]=i[2];
+ else if (i[0]==CHN_CHANGE_FORCED_STATE)
+ m_kMid.pctl->forcepgm[i[1]-1]=i[2];
+ if ((i[0]==CHN_CHANGE_PGM)||((i[0]==CHN_CHANGE_FORCED_STATE)&&(i[3]==1)))
+ {
+ if (autocontplaying)
+ {
+ pause();
+ }
+ }
+
+}
+
+void kmidClient::slotSetTempo(double value)
+{
+ if (!player->isSongLoaded())
+ {
+ tempoLCD->display(120);
+ currentTempo=120;
+ tempoLCD->setDefaultValue(120);
+ return;
+ }
+
+#ifdef KMidDEBUG
+ printf("Change tempo to %g\n",value);
+#endif
+ int autocontplaying=0;
+
+ if ((m_kMid.pctl->playing==1)&&(m_kMid.pctl->paused==0)) autocontplaying=1;
+
+
+ if (autocontplaying)
+ {
+ pause();
+ }
+
+// double ratio=(tempoToMetronomeTempo(m_kMid.pctl->tempo)*m_kMid.pctl->ratioTempo)/(value);
+// double ratio=(tempoLCD->getOldValue()*m_kMid.pctl->ratioTempo)/(value);
+ double ratio=(currentTempo*m_kMid.pctl->ratioTempo)/value;
+
+ char s[20];
+ sprintf(s,"%g",ratio);
+ if (strcmp(s,"1")!=0) tempoLCD->setLCDColor (255,100,100);
+ else tempoLCD->setLCDColor (100,255,100);
+#ifdef KMidDEBUG
+ printf("ratio: (%.9g = %g ) tempo now: %g , new tempo %g\n",ratio,ratio,tempoToMetronomeTempo(m_kMid.pctl->tempo),value);
+ printf("OldValue: %g , value %g\n",tempoLCD->getOldValue(),value);
+#endif
+
+ if (m_kMid.pctl->paused==1)
+ {
+ pausedatmillisec=(long)(((double)pausedatmillisec/m_kMid.pctl->ratioTempo)*ratio);
+#ifdef KMidDEBUG
+ printf("pausedat: %ld\n",pausedatmillisec);
+#endif
+ }
+ player->setTempoRatio(ratio);
+
+ timebar->setRange(0,(int)(player->information()->millisecsTotal));
+ timebar->setValue(pausedatmillisec);
+ timetags->repaint(TRUE);
+
+ kdispt->ClearEv(false);
+
+ noteArray=player->noteArray();
+ spev=player->specialEvents();
+ currentTempo=value;
+
+ while (spev!=NULL)
+ {
+ if ((spev->type==1) || (spev->type==5))
+ {
+ kdispt->AddEv(spev);
+ }
+ spev=spev->next;
+ }
+
+ kdispt->calculatePositions();
+ kdispt->CursorToHome();
+ if (m_kMid.pctl->paused==1)
+ moveEventPointersTo(pausedatmillisec);
+
+ if (autocontplaying)
+ {
+ pause();
+ }
+
+}
+
+void kmidClient::downloadFinished(KIO::Job *)
+{
+ downloaded=true;
+ kapp->exit_loop();
+}
+
+QSize kmidClient::sizeHint() const
+{
+ QSize sh = QWidget::sizeHint();
+ return sh.expandedTo(QSize(560,420));
+}
+
+QSizePolicy kmidClient::sizePolicy()
+{
+ return QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+}
+
+
+void kmidClient::play()
+{
+ slotPlay();
+}
+void kmidClient::pause()
+{
+ slotPause();
+}
+void kmidClient::stop()
+{
+ slotStop();
+}
+void kmidClient::rewind()
+{
+ slotRewind();
+}
+void kmidClient::forward()
+{
+ slotForward();
+}
+void kmidClient::seek(int ms)
+{
+ slotSeek(ms);
+}
+void kmidClient::prevSong()
+{
+ slotPrevSong();
+}
+void kmidClient::nextSong()
+{
+ slotNextSong();
+}
+void kmidClient::setVolume(int i)
+{
+ slotSetVolume(200-i);
+}
+void kmidClient::setTempo(int i)
+{
+ slotSetTempo(i);
+}
+void kmidClient::setSongEncoding( int i )
+{
+ KListAction *tmplistaction=
+ ((KListAction*)actionCollection->action("file_type"));
+
+ tmplistaction->setCurrentItem(i);
+}
+void kmidClient::setLyricEvents( int i )
+{
+ KListAction *tmplistaction=
+ ((KListAction*)actionCollection->action("display_events"));
+ tmplistaction->setCurrentItem(i);
+}
+void kmidClient::setCurrentSong(int i)
+{
+ getComboSongs()->setCurrentItem(i-1);
+ slotSelectSong(i-1);
+}
+void kmidClient::setPlayListMode(int i)
+{
+ ((KListAction*)actionCollection->action("play_order"))->setCurrentItem(i);
+}
+void kmidClient::slotSelectEncoding(int i)
+{
+ if (i == 0)
+ kdispt->setLyricsEncoding(QString::null); // Default
+ else
+ kdispt->setLyricsEncoding(KGlobal::charsets()->encodingForName(comboEncodings->text(i)));
+}
+#include "kmidclient.moc"
diff --git a/kmid/kmidclient.h b/kmid/kmidclient.h
new file mode 100644
index 00000000..18d33d66
--- /dev/null
+++ b/kmid/kmidclient.h
@@ -0,0 +1,240 @@
+/**************************************************************************
+
+ kmidclient.h - The main client widget of KMid
+ Copyright (C) 1997,98 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#ifndef _KMIDCLIENT_H
+#define _KMIDCLIENT_H
+
+#include "qslidertime.h"
+#include <libkmid/player.h>
+#include <libkmid/track.h>
+#include <libkmid/notearray.h>
+#include <libkmid/libkmid.h>
+#include <qtimer.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include "kdisptext.h"
+#include "slman.h"
+#include "version.h"
+#include "kmidIface.h"
+
+#include <kio/job.h>
+
+class DeviceManager;
+
+
+class KApplication;
+class KConfig;
+class KLCDNumber;
+class QLabel;
+class QComboBox;
+class RhythmView;
+class ChannelView;
+class QString;
+
+class kmidClient : public QWidget, virtual public KMidIface
+{
+ Q_OBJECT
+private:
+#ifdef KMidDEBUG
+ long passcount;
+#endif
+
+ DeviceManager *midi;
+ MidiPlayer *player;
+
+ struct kMidData m_kMid;
+
+ QTimer *timer4timebar;
+ QTimer *timer4events;
+
+ ulong beginmillisec;
+ ulong pausedatmillisec;
+ double currentTempo;
+ SpecialEvent *spev;
+ NoteArray *noteArray;
+
+ bool downloaded;
+
+ int itsme;
+ bool shuttingdown;
+ int visiblevolumebar;
+
+ char *midifile_opened;
+ int hasbeenopened;
+
+ int typeoftextevents;
+
+ SLManager *slman;
+ int activecollection;
+ QString collectionsfile;
+ SongList *currentsl;
+ int initializing_songs;
+ int loopsong;
+ int collectionplaymode;
+ int *collectionplaylist; // the list of songs ordered in the
+ // user selected mode
+
+ class KActionCollection *actionCollection;
+
+ void generateCPL(void);
+ int searchInCPL(int song); // Returns the index of song
+
+ void fillInComboSongs(void);
+
+ int openFile(const char *filename);
+ void allNotesOff(void);
+public:
+ kmidClient(QWidget *parent, KActionCollection *ac, const char *name=0);
+ ~kmidClient();
+
+ char *midiFileName(void) {return midifile_opened;};
+ // If it returns NULL then there isn't any file opened
+
+ int isPlaying(void) {return m_kMid.pctl->playing;};
+ int isPaused(void) {return (m_kMid.pctl->playing)&&(m_kMid.pctl->paused);};
+
+ int openURL(const QString s);
+
+ void repaintText(int typeoftextevents);
+ static void kmidOutput(void);
+
+ int ChooseTypeOfTextEvents(void);
+
+ QFont *getFont(void);
+ void fontChanged(void); // The new font is already in KConfig
+
+ SLManager *getSLManager(void) {return slman;};
+ void setSLManager(SLManager *slm);
+ // setSLManager only change the pointer, so DO NOT DELETE
+ // the objectr you pass to it
+ int getActiveCollection(void) {return activecollection;};
+ void setActiveCollection(int i);
+ int getSelectedSong(void);
+ void saveCollections(void);
+
+ void setSongType(int i);
+ void setSongLoop(int i);
+ void setCollectionPlayMode(int i);
+
+ void visibleVolumeBar(int i); // 1 shows it, and 0 hides it
+ // int isVisibleVolumeBar(void) {return visiblevolumebar;};
+ void visibleChannelView(int i);
+
+ void shuttingDown(void);
+
+ ulong timeOfNextEvent(int *type=NULL);
+ void rethinkNextEvent(void);
+ // Recalculates time of next event and updates the timer4events according to it
+
+
+ void moveEventPointersTo(ulong ms);
+
+protected:
+// void resizeEvent(QResizeEvent *qre);
+
+
+public slots:
+// void help_Help();
+// void help_About();
+ void slotPlay();
+ void slotPause();
+ void slotStop();
+ void slotRewind();
+ void slotForward();
+ void slotPrevSong();
+ void slotNextSong();
+
+ void timebarUpdate();
+ void slotSeek(int i);
+ void slotSetVolume(int i);
+ void slotSelectSong(int i);
+ void slotSelectEncoding(int i);
+
+ void downloadFinished( KIO::Job * );
+
+ void processSpecialEvent();
+
+ void channelViewDestroyed();
+
+ void communicationFromChannelView(int *);
+
+ void slotSetTempo(double value);
+
+signals:
+ void mustRechooseTextEvent();
+ void stopPause();
+// void channelView_Destroyed();
+
+public:
+ void saveLyrics(FILE *fh);
+
+ DeviceManager *devman(void) {return midi;};
+ void setMidiDevice(int i);
+ void setMidiMapFilename(const char *mapfilename);
+
+ ChannelView *getChannelView(void) { return channelView; };
+ KDisplayText *getKDisplayText(void) { return kdispt; };
+
+ QSizePolicy sizePolicy();
+
+ QComboBox *getComboSongs() { return comboSongs; };
+
+
+ void play();
+ void pause();
+ void stop();
+ void rewind();
+ void forward();
+ void seek(int ms);
+ void prevSong();
+ void nextSong();
+ void setVolume(int i);
+ void setTempo(int i);
+ void setSongEncoding( int i );
+ void setLyricEvents( int i );
+ void setCurrentSong(int i);
+ void setPlayListMode(int i);
+
+ QSize sizeHint() const;
+private:
+ QSlider *timebar;
+ QSliderTime *timetags;
+ QSlider *volumebar;
+
+ KLCDNumber *tempoLCD;
+ KDisplayText *kdispt;
+ QLabel *qlabelTempo;
+ QComboBox *comboSongs;
+ QComboBox *comboEncodings;
+ RhythmView *rhythmview;
+
+
+ ChannelView *channelView;
+
+};
+
+char *extractFilename(const char *in,char *out); // returns a pointer to out
+
+#endif
+
diff --git a/kmid/kmidframe.cpp b/kmid/kmidframe.cpp
new file mode 100644
index 00000000..007f0b82
--- /dev/null
+++ b/kmid/kmidframe.cpp
@@ -0,0 +1,728 @@
+/**************************************************************************
+
+ kmidframe.cpp - The main widget of KMid
+ Copyright (C) 1997,98 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/shm.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include <qkeycode.h>
+#include <qstring.h>
+
+#include <kaccel.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kcmdlineargs.h>
+#include <kfiledialog.h>
+#include <kfontdialog.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <ktoolbar.h>
+#include <kurl.h>
+#include <kdebug.h>
+#include <kaction.h>
+#include <kstdaction.h>
+#include <kurldrag.h>
+#include <kedittoolbar.h>
+#include <dcopclient.h>
+
+#include "kmidframe.h"
+#include "kmidclient.h"
+#include "midicfgdlg.h"
+#include "collectdlg.h"
+#include "channelcfgdlg.h"
+#include "channelview.h"
+#include "version.h"
+
+kmidFrame::kmidFrame(const char *name)
+ :KMainWindow(0, name)
+{
+ kmidclient=new kmidClient(this,actionCollection(), "KMidClient");
+ kmidclient->setSongType(1);
+ kmidclient->show();
+ setCentralWidget( kmidclient );
+/*
+ kKeysAccel=new KAccel(this);
+ kKeysAccel->insertItem(i18n("Play/Pause"),"Play/Pause", Key_Space);
+ kKeysAccel->connectItem("Play/Pause", this, SLOT(spacePressed()));
+ kKeysAccel->insertItem(i18n("Stop"),"Stop", Key_Backspace);
+ kKeysAccel->connectItem("Stop",kmidclient,SLOT(song_Stop()));
+ kKeysAccel->insertItem(i18n("Previous Song"),"Previous Song", Key_Left);
+ kKeysAccel->connectItem("Previous Song",kmidclient,SLOT(song_PlayPrevSong()));
+ kKeysAccel->insertItem(i18n("Next Song"),"Next Song", Key_Right);
+ kKeysAccel->connectItem("Next Song",kmidclient,SLOT(song_PlayNextSong()));
+ kKeysAccel->insertItem(i18n("Scroll Down Karaoke"),"Scroll down karaoke",Key_Down);
+ kKeysAccel->connectItem("Scroll Down karaoke",kmidclient->getKDisplayText(),SLOT(ScrollDown()));
+ kKeysAccel->insertItem(i18n("Scroll Up Karaoke"),"Scroll up karaoke",Key_Up);
+ kKeysAccel->connectItem("Scroll Up Karaoke",kmidclient->getKDisplayText(),SLOT(ScrollUp()));
+ kKeysAccel->insertItem(i18n("Scroll Page Down Karaoke"),"Scroll page down karaoke",Key_PageDown);
+ kKeysAccel->connectItem("Scroll Page Down Karaoke",kmidclient->getKDisplayText(),SLOT(ScrollPageDown()));
+ kKeysAccel->insertItem(i18n("Scroll Page Up Karaoke"),"Scroll page up karaoke",Key_PageUp);
+ kKeysAccel->connectItem("Scroll Page Up Karaoke",kmidclient->getKDisplayText(),SLOT(ScrollPageUp()));
+
+ kKeysAccel->readSettings();
+*/
+ KStdAction::open(this, SLOT(file_Open()), actionCollection());
+ (void)new KAction(i18n("&Save Lyrics..."), 0, this,
+ SLOT(file_SaveLyrics()), actionCollection(), "file_save_lyrics");
+ KStdAction::quit(kapp, SLOT(quit()), actionCollection());
+
+ (void)new KAction(i18n("&Play"), "player_play", Qt::Key_Space,
+ kmidclient, SLOT(slotPlay()), actionCollection(), "song_play");
+ (void)new KAction(i18n("P&ause"), "player_pause", Qt::Key_P, kmidclient,
+ SLOT(slotPause()), actionCollection(), "song_pause");
+ (void)new KAction(i18n("&Stop"), "player_stop", Qt::Key_Backspace,
+ kmidclient, SLOT(slotStop()), actionCollection(), "song_stop");
+
+ (void)new KAction(i18n("P&revious Song"), "player_start", Key_Left,
+ kmidclient, SLOT(slotPrevSong()), actionCollection(),
+ "song_previous");
+ (void)new KAction(i18n("&Next Song"), "player_end", Key_Right,
+ kmidclient, SLOT(slotNextSong()), actionCollection(),
+ "song_next");
+ (void)new KToggleAction(i18n("&Loop"), 0, this, SLOT(song_Loop()),
+ actionCollection(), "song_loop");
+
+ (void)new KAction(i18n("Rewind"), "2leftarrow", 0, kmidclient,
+ SLOT(slotRewind()), actionCollection(), "song_rewind");
+
+ (void)new KAction(i18n("Forward"), "2rightarrow", 0, kmidclient,
+ SLOT(slotForward()), actionCollection(), "song_forward");
+
+ (void)new KAction(i18n("&Organize..."), 0, this, SLOT(collect_organize()),
+ actionCollection(), "collect_organize");
+
+ QStringList playmodes;
+ playmodes.append(i18n("In Order"));
+ playmodes.append(i18n("Shuffle"));
+
+ KSelectAction *act=new KSelectAction(i18n("Play Order"), 0, /*this, SLOT(collect_PlayOrder(int)),*/
+ actionCollection(), "play_order");
+ connect(act,SIGNAL(activated(int)),this, SLOT(collect_PlayOrder(int)));
+ act->setItems(playmodes);
+
+ (void)new KToggleAction(i18n("Auto-Add to Collection"), 0, this,
+ SLOT(collect_AutoAdd()), actionCollection(), "collect_autoadd");
+
+ playmodes.clear();
+ playmodes.append(i18n("&General MIDI"));
+ playmodes.append(i18n("&MT-32"));
+
+ act=new KSelectAction(i18n("File Type"), 0,/* this, SLOT(options_FileType(int)),*/
+ actionCollection(), "file_type");
+ connect(act,SIGNAL(activated(int)),this, SLOT(options_FileType(int)));
+ act->setItems(playmodes);
+
+ playmodes.clear();
+ playmodes.append(i18n("&Text Events"));
+ playmodes.append(i18n("&Lyric Events"));
+
+ act=new KSelectAction(i18n("Display Events"), Key_T, /*this,
+ SLOT(options_DisplayEvents(int)),*/ actionCollection(),
+ "display_events");
+ connect(act,SIGNAL(activated(int)),this, SLOT(options_DisplayEvents(int)));
+ act->setItems(playmodes);
+
+ (void)new KToggleAction(i18n("Automatic Text Chooser"), 0, this,
+ SLOT(options_AutomaticText()), actionCollection(),
+ "option_automatictext");
+
+ KToggleAction* togact = new KToggleAction(i18n("Show &Volume Bar"), "volume",
+ 0, this, SLOT(options_ShowVolumeBar()), actionCollection(),
+ "toggle_volumebar");
+ togact->setCheckedState(i18n("Hide &Volume Bar"));
+
+ togact = new KToggleAction(i18n("Show &Channel View"), "piano",
+ 0, this, SLOT(options_ShowChannelView()), actionCollection(),
+ "toggle_channelview");
+ togact->setCheckedState(i18n("Hide &Channel View"));
+
+ (void)new KAction(i18n("Channel View &Options..."), 0, this,
+ SLOT(options_ChannelViewOptions()), actionCollection(),
+ "channelview_options");
+
+ (void)new KAction(i18n("&Font Change..."), 0, this,
+ SLOT(options_FontChange()), actionCollection(),
+ "change_font");
+
+ (void)new KAction(i18n("MIDI &Setup..."), 0, this,
+ SLOT(options_MidiSetup()), actionCollection(), "midi_setup");
+
+
+ setupGUI((ToolBar | Keys | StatusBar | Save | Create ), "kmidui.rc" );
+
+ KConfig *cfg=kapp->config();
+ cfg->setGroup("KMid");
+ KSelectAction *tmplistaction=
+ ((KSelectAction*)actionCollection()->action("display_events"));
+
+ if (cfg->readNumEntry("TypeOfTextEvents",5)==5)
+ tmplistaction->setCurrentItem(1);
+ else
+ tmplistaction->setCurrentItem(0);
+
+ tmplistaction=((KSelectAction*)actionCollection()->action("file_type"));
+ if (cfg->readNumEntry("TypeOfMidiFile",0)==0)
+ tmplistaction->setCurrentItem(0);
+ else
+ tmplistaction->setCurrentItem(1);
+
+ if (cfg->readNumEntry("Loop",0)==1)
+ ((KToggleAction*)actionCollection()->action("song_loop"))->setChecked(true);
+
+ if (cfg->readNumEntry("ShowVolumeBar",0)==1)
+ ((KToggleAction*)actionCollection()->action("toggle_volumebar"))->setChecked(true);
+
+
+ tmplistaction=((KSelectAction*)actionCollection()->action("play_order"));
+ if (cfg->readNumEntry("CollectionPlayMode",0)==0)
+ tmplistaction->setCurrentItem(0);
+ else
+ tmplistaction->setCurrentItem(1);
+
+ if ((cfg->readNumEntry("AutoAddToCollection",0))==1)
+ ((KToggleAction*)actionCollection()->action("collect_autoadd"))->setChecked(true);
+
+ if ((cfg->readNumEntry("AutomaticTextEventChooser",1))==1)
+ ((KToggleAction*)actionCollection()->action("option_automatictext"))->setChecked(true);
+
+ setAcceptDrops(true);
+
+ connect( kmidclient, SIGNAL( mustRechooseTextEvent() ),
+ this, SLOT( rechooseTextEvent() ) );
+
+ connect( kmidclient, SIGNAL( stopPause() ),
+ this, SLOT( song_stopPause() ) );
+
+// connect( kmidclient, SIGNAL( channelView_Destroyed() ),
+// this, SLOT( channelViewDestroyed() ) );
+
+ KCmdLineArgs * args = KCmdLineArgs::parsedArgs();
+
+ if ( args->count() > 0 )
+ {
+// printf("Opening command line file...\n");
+ int backautoadd=cfg->readNumEntry("AutoAddToCollection",0);
+ cfg->writeEntry("AutoAddToCollection",0);
+
+ char ttt[40];
+ sprintf(ttt,"%d",kapp->argc());
+ int i=0;
+ int c=autoAddSongToCollection( args->url( 0 ).path() , 1 );
+ i++;
+ while (i<args->count())
+ {
+ autoAddSongToCollection( args->url( i ).path() , 0 );
+ i++;
+ }
+
+ kmidclient->setActiveCollection(c);
+
+ /// kmidclient->openURL((kapp->argv())[1]);
+ /* if ((cfg->readNumEntry("AutomaticTextEventChooser",1))==1)
+ {
+ if (kmidclient->ChooseTypeOfTextEvents()==1)
+ options_Text();
+ else
+ options_Lyrics();
+ }*/
+ if (kmidclient->midiFileName()!=NULL) kmidclient->play();
+ cfg->writeEntry("AutoAddToCollection",backautoadd);
+ }
+
+ args->clear();
+ /*
+ kKeys->addKey("Play/Pause",Key_Space);
+ kKeys->registerWidget("KMidFrame",this);
+ kKeys->connectFunction("KMidFrame","Play/Pause",this,SLOT(spacePressed()));
+ */
+// kKeysAccel->writeSettings(cfg);
+
+ DCOPClient *client = kapp->dcopClient();
+ if (!client->isRegistered()) // just in case we're embeeded
+ {
+ client->attach();
+ client->registerAs("kmid");
+ }
+}
+
+kmidFrame::~kmidFrame()
+{
+}
+
+void kmidFrame::file_Open()
+{
+ KURL url = KFileDialog::getOpenURL(QString::null, "*.kar *.mid *.kar.gz *.mid.gz\n*.kar *.kar.gz\n*.mid *.mid.gz\n*",this);
+
+ if( url.isEmpty() )
+ return;
+
+ if( !url.isLocalFile() )
+ {
+ KMessageBox::sorry( 0L, i18n( "Only local files are currently supported." ) );
+ return;
+ }
+
+ openURL(url.path());
+}
+
+
+
+void kmidFrame::song_stopPause()
+{
+ if (kmidclient->isPaused())
+ {
+ // song_Pause();
+ ((KToggleAction*)actionCollection()->action("song_pause"))->setChecked(false);
+ kmidclient->pause();
+ }
+}
+
+void kmidFrame::options_FileType(int i)
+{
+ KConfig *cfg=kapp->config();
+ cfg->setGroup("KMid");
+ cfg->writeEntry("TypeOfMidiFile",i);
+ kmidclient->setSongType(1-i);
+}
+
+void kmidFrame::options_DisplayEvents(int i)
+{
+ KConfig *cfg=kapp->config();
+ cfg->setGroup("KMid");
+ cfg->writeEntry("TypeOfTextEvents",(i==0)?1:5);
+ cfg->sync();
+ kmidclient->repaintText((i==0)?1:5);
+}
+
+void kmidFrame::options_AutomaticText()
+{
+ KConfig *cfg=kapp->config();
+ cfg->setGroup("KMid");
+ cfg->writeEntry("AutomaticTextEventChooser",1-cfg->readNumEntry("AutomaticTextEventChooser",1));
+}
+
+void kmidFrame::options_FontChange()
+{
+ KFontDialog *kfd=new KFontDialog(this);
+ QFont font;
+ font=*kmidclient->getFont();
+ kfd->getFont(font);
+ delete kfd;
+ KConfig *cfg=kapp->config();
+ cfg->setGroup("KMid");
+ cfg->writeEntry("KaraokeFont",font);
+ cfg->sync();
+ kmidclient->fontChanged();
+}
+
+int kmidFrame::autoAddSongToCollection(const QString& filename,int setactive)
+{
+ int r;
+ SLManager *slman;
+ SongList *sl;
+ KConfig *cfg=kapp->config();
+ cfg->setGroup("KMid");
+ if (cfg->readNumEntry("AutoAddToCollection",0)==0)
+ {
+ r=0;
+ slman=kmidclient->getSLManager();
+ if (setactive) slman->createTemporaryCollection();
+ sl=slman->getCollection(0);
+ if (filename==NULL) sl->AddSong(kmidclient->midiFileName());
+ else sl->AddSong(QFile::encodeName(filename));
+ }
+ else
+ {
+ slman=kmidclient->getSLManager();
+ sl=slman->getCollection(kmidclient->getActiveCollection());
+ r=kmidclient->getActiveCollection();
+ if (sl==NULL) return 0;
+ int id;
+ if (filename==NULL) id=sl->AddSong(kmidclient->midiFileName());
+ else id=sl->AddSong(QFile::encodeName(filename));
+ if (setactive)
+ {
+ sl->setActiveSong(id);
+ }
+ }
+ return r;
+}
+
+void kmidFrame::urlDrop_slot(QDropEvent* e)
+{
+ dropEvent(e);
+}
+
+void kmidFrame::dragEnterEvent(QDragEnterEvent* e)
+{
+ e->accept(KURLDrag::canDecode(e));
+}
+
+void kmidFrame::dropEvent( QDropEvent * event )
+{
+ KURL::List list;
+ KURLDrag::decode(event, list);
+
+ if (list.count()==0) return;
+
+ bool first = true;
+ int c = true;
+
+ for (KURL::List::ConstIterator it = list.begin();
+ it != list.end(); ++it)
+ {
+ if (!(*it).isLocalFile())
+ continue;
+
+ if (first)
+ {
+ c = autoAddSongToCollection((*it).path(),1);
+ first = false;
+ }
+ else
+ {
+ autoAddSongToCollection((*it).path(),0);
+ }
+ }
+
+ if (!first)
+ {
+ kmidclient->setActiveCollection(c);
+
+ if ((!kmidclient->isPlaying())&&(kmidclient->midiFileName()!=NULL))
+ kmidclient->play();
+ }
+}
+
+void kmidFrame::shuttingDown()
+{
+ if (kmidclient->isPlaying()==1)
+ {
+// kmidclient->stop();
+ kmidclient->shuttingDown();
+ }
+ kmidclient->saveCollections();
+}
+
+void kmidFrame::saveProperties(KConfig *cfg)
+{
+ kmidclient->saveCollections();
+ int play=kmidclient->isPlaying();
+ cfg->writeEntry("File",kmidclient->midiFileName());
+ cfg->writeEntry("ActiveCollection",kmidclient->getActiveCollection());
+ cfg->writeEntry("ActiveSong",kmidclient->getSelectedSong());
+ cfg->writeEntry("Playing",play);
+}
+
+void kmidFrame::readProperties(KConfig *cfg)
+{
+ int activecol=cfg->readNumEntry("ActiveCollection",0);
+ int activesong=cfg->readNumEntry("ActiveSong",0);
+ int wasplaying=cfg->readNumEntry("Playing",0);
+ SLManager *slman=kmidclient->getSLManager();
+ SongList *sl=slman->getCollection(activecol);
+ sl->setActiveSong(activesong);
+ kmidclient->setActiveCollection(activecol);
+ kmidclient->slotSelectSong(activesong-1);
+
+ if ((activecol==0)&&(wasplaying)) // It was the temporary collection,
+ { // surely the user would like to continue
+ // hearing the song
+ QString s = cfg->readPathEntry("File");
+ int c=autoAddSongToCollection(s,1);
+ kmidclient->setActiveCollection(c);
+ }
+
+ if ((wasplaying)&&(kmidclient->midiFileName()!=NULL)) kmidclient->play();
+}
+
+void kmidFrame::options_MidiSetup()
+{
+ if (kmidclient->devman()->checkInit()<0)
+ {
+ KMessageBox::error(this,
+ i18n("Could not open /dev/sequencer to get some info.\nProbably there is another program using it."));
+ return;
+ }
+ MidiConfigDialog *dlg;
+
+ dlg=new MidiConfigDialog(kmidclient->devman(),NULL,"MidiDialog");
+ if (dlg->exec() == QDialog::Accepted)
+ {
+ KConfig *cfg=kapp->config();
+ cfg->setGroup("KMid");
+ cfg->writeEntry("MidiPortNumber",MidiConfigDialog::selecteddevice);
+ kmidclient->setMidiDevice(MidiConfigDialog::selecteddevice);
+ cfg->setGroup("Midimapper");
+ cfg->writePathEntry("LoadFile",
+ (MidiConfigDialog::selectedmap==NULL)? QString::null:MidiConfigDialog::selectedmap);
+ kmidclient->setMidiMapFilename(MidiConfigDialog::selectedmap);
+ }
+ delete dlg;
+}
+
+void kmidFrame::collect_organize()
+{
+ CollectionDialog *dlg;
+ SLManager *slman=new SLManager(*kmidclient->getSLManager());
+ int idx=kmidclient->getActiveCollection();
+
+ dlg=new CollectionDialog(slman,idx,NULL,"MidiDialog");
+ if (dlg->exec() == QDialog::Accepted)
+ {
+ kmidclient->setSLManager(slman);
+ kmidclient->setActiveCollection(CollectionDialog::selectedC);
+ kmidclient->slotSelectSong(
+ (slman->getCollection(CollectionDialog::selectedC))->getActiveSongID()-1);
+ }
+ else
+ {
+ delete slman;
+ }
+delete dlg;
+}
+
+void kmidFrame::rechooseTextEvent()
+{
+ KConfig *cfg=kapp->config();
+ cfg->setGroup("KMid");
+ if ((cfg->readNumEntry("AutomaticTextEventChooser",1))==1)
+ {
+ int t=kmidclient->ChooseTypeOfTextEvents();
+ kmidclient->repaintText(t);
+ if (t==1)
+ ((KSelectAction*)actionCollection()->action("display_events"))->setCurrentItem(0);
+ else
+ ((KSelectAction*)actionCollection()->action("display_events"))->setCurrentItem(1);
+ }
+}
+
+void kmidFrame::song_Loop()
+{
+ KConfig *cfg=kapp->config();
+ cfg->setGroup("KMid");
+ int i=1-cfg->readNumEntry("Loop",0);
+ cfg->writeEntry("Loop",i);
+ cfg->sync();
+ kmidclient->setSongLoop(i);
+}
+
+void kmidFrame::collect_PlayOrder(int i)
+{
+ KConfig *cfg=kapp->config();
+ cfg->setGroup("KMid");
+ cfg->writeEntry("CollectionPlayMode", i);
+ cfg->sync();
+ kmidclient->setCollectionPlayMode(i);
+}
+
+void kmidFrame::collect_AutoAdd()
+{
+ KConfig *cfg=kapp->config();
+ cfg->setGroup("KMid");
+ cfg->writeEntry("AutoAddToCollection",1-cfg->readNumEntry("AutoAddToCollection",0));
+ cfg->sync();
+}
+
+void kmidFrame::file_SaveLyrics()
+{
+ KURL url = KFileDialog::getSaveURL(QString::null,"*",this);
+
+ if( url.isEmpty() )
+ return;
+
+ if( !url.isLocalFile() )
+ {
+ KMessageBox::sorry( 0L, i18n( "Only local files are currently supported." ) );
+ return;
+ }
+
+ QString filename = url.path();
+ struct stat statbuf;
+
+ if (stat(QFile::encodeName(filename), &statbuf)!=-1)
+ {
+ QString s = i18n("File %1 already exists\nDo you want to overwrite it?").arg(filename);
+ if (KMessageBox::warningYesNo(this,s,QString::null,i18n("Overwrite"),KStdGuiItem::cancel())==1)
+ return;
+ }
+
+ FILE *fh=fopen(QFile::encodeName(filename),"wt");
+ kmidclient->saveLyrics(fh);
+ fclose(fh);
+}
+
+void kmidFrame::spacePressed()
+{
+ if (!kmidclient->isPlaying()) kmidclient->play();
+ else pause();
+}
+void kmidFrame::options_ShowVolumeBar()
+{
+ KConfig *cfg=kapp->config();
+ cfg->setGroup("KMid");
+ int i=1-cfg->readNumEntry("ShowVolumeBar",0);
+ cfg->writeEntry("ShowVolumeBar",i);
+ kmidclient->visibleVolumeBar(i);
+}
+
+void kmidFrame::options_ShowChannelView()
+{
+ if (!((KToggleAction*)actionCollection()->action("toggle_channelview"))->isChecked())
+ {
+ kmidclient->visibleChannelView(0);
+ } else
+ {
+ kmidclient->visibleChannelView(1);
+ connect (kmidclient->getChannelView(),SIGNAL(destroyMe()),this,SLOT(channelViewDestroyed()));
+ }
+
+}
+
+void kmidFrame::channelViewDestroyed()
+{
+ kmidclient->channelViewDestroyed();
+ ((KToggleAction*)actionCollection()->action("toggle_channelview"))->setChecked(false);
+}
+
+void kmidFrame::options_ChannelViewOptions()
+{
+ ChannelViewConfigDialog *dlg;
+
+ dlg=new ChannelViewConfigDialog(NULL,"ChannelViewConfigDialog");
+ if (dlg->exec() == QDialog::Accepted)
+ {
+ if (kmidclient->getChannelView())
+ kmidclient->getChannelView()->lookMode(
+ ChannelViewConfigDialog::selectedmode );
+ }
+ delete dlg;
+}
+
+void kmidFrame::openURL( const QString url )
+{
+ int c=autoAddSongToCollection(url,1);
+ kmidclient->setActiveCollection(c);
+}
+
+/*void kmidFrame::play()
+{
+ kmidclient->play();
+}
+
+void kmidFrame::pause()
+{
+// kmidclient->pause();
+ ((KAction*)actionCollection()->action("pause"))->activate();
+}
+
+void kmidFrame::stop()
+{
+ kmidclient->stop();
+}
+
+void kmidFrame::rewind()
+{
+ kmidclient->rewind();
+}
+
+void kmidFrame::forward()
+{
+ kmidclient->forward();
+}
+
+void kmidFrame::seek(int ms)
+{
+ kmidclient->timebarChange(ms);
+}
+
+void kmidFrame::prevSong()
+{
+ kmidclient->prevSong();
+}
+
+void kmidFrame::nextSong()
+{
+ kmidclient->nextSong();
+}
+
+void kmidFrame::setSongLoop(int i)
+{
+ ((KToggleAction*)actionCollection()->action("song_loop"))->setChecked(i!=0);
+}
+
+void kmidFrame::setVolume(int i)
+{
+ kmidclient->volumebarChange(200-i);
+}
+
+void kmidFrame::setTempo(int i)
+{
+ kmidclient->changeTempo(i);
+}
+
+void kmidFrame::setSongType( int i )
+{
+ KSelectAction *tmplistaction=
+ ((KSelectAction*)actionCollection()->action("file_type"));
+
+ tmplistaction->setCurrentItem(i);
+}
+
+void kmidFrame::setLyricEvents( int i )
+{
+ KSelectAction *tmplistaction=
+ ((KSelectAction*)actionCollection()->action("display_events"));
+ tmplistaction->setCurrentItem(i);
+}
+
+void kmidFrame::selectSong(int i)
+{
+ kmidclient->getComboSongs()->setCurrentItem(i-1);
+ kmidclient->selectSong(i-1);
+}
+
+void kmidFrame::setActiveCollection( int i )
+{
+ kmidclient->setActiveCollection(i);
+}
+
+void kmidFrame::setCollectionPlayMode(int i)
+{
+ ((KSelectAction*)actionCollection()->action("play_order"))->setCurrentItem(i);
+}
+
+void kmidFrame::setMidiDevice(int i)
+{
+
+
+}
+*/
+
+
+#include "kmidframe.moc"
diff --git a/kmid/kmidframe.h b/kmid/kmidframe.h
new file mode 100644
index 00000000..e88c902a
--- /dev/null
+++ b/kmid/kmidframe.h
@@ -0,0 +1,133 @@
+/**************************************************************************
+
+ kmidframe.h - The main widget of KMid
+ Copyright (C) 1997,98 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#ifndef _KMIDFRAME_H
+#define _KMIDFRAME_H
+
+#include <kmainwindow.h>
+#include <kmenubar.h>
+#include <libkmid/midiout.h>
+#include <libkmid/player.h>
+#include <libkmid/track.h>
+#include <qtimer.h>
+#include <kdelibs_export.h>
+
+class KApplication;
+class KConfig;
+class kmidClient;
+class KToolBar;
+class QDragEvent;
+class KAccel;
+
+class KDE_EXPORT kmidFrame : public KMainWindow
+{
+ Q_OBJECT
+private:
+#ifdef KMidDEBUG
+ long passcount;
+#endif
+
+ MidiOut *Midi;
+ MidiPlayer *Player;
+/*
+ int playerProcessID;
+ PlayerController *pctl;
+
+ int donttoggle;
+
+ QPopupMenu *m_file;
+ QPopupMenu *m_song;
+ QPopupMenu *m_collections;
+ QPopupMenu *m_options;
+ QPopupMenu *m_help;
+*/
+ KAccel *kKeysAccel;
+protected:
+ int autoAddSongToCollection(const QString& filename=QString::null,int setactive=1);
+
+
+ virtual void saveProperties(KConfig *kcfg);
+ virtual void readProperties(KConfig *kcfg);
+
+// virtual void closeEvent(QCloseEvent *e);
+
+ void dragEnterEvent(QDragEnterEvent* e);
+ void dropEvent ( QDropEvent * event );
+
+
+public:
+ kmidFrame(const char *name=0);
+ virtual ~kmidFrame();
+
+public slots:
+
+ void file_Open();
+ void file_SaveLyrics();
+ void song_stopPause(); // release the pause button and quit the pause mode
+ void song_Loop();
+ void collect_organize();
+ void collect_PlayOrder(int i);
+ void collect_AutoAdd();
+ void options_FileType(int i);
+ void options_DisplayEvents(int i);
+ void options_AutomaticText();
+ void options_ShowVolumeBar();
+ void options_ShowChannelView();
+ void options_ChannelViewOptions();
+ void options_FontChange();
+
+ void options_MidiSetup();
+ void spacePressed();
+
+
+ virtual void openURL( const QString s );
+/* virtual void play();
+ virtual void pause();
+ virtual void stop();
+ virtual void rewind();
+ virtual void forward();
+ virtual void seek(int ms);
+ virtual void prevSong();
+ virtual void nextSong();
+ virtual void setSongLoop(int i);
+ virtual void setVolume(int i);
+ virtual void setTempo(int i);
+ virtual void setSongType( int i );
+ virtual void setLyricEvents( int i );
+ virtual void selectSong(int i);
+ virtual void setActiveCollection( int i );
+ virtual void setCollectionPlayMode(int i);
+ virtual void setMidiDevice(int i);
+*/
+ void urlDrop_slot(class QDropEvent *);
+
+ void rechooseTextEvent();
+
+ void channelViewDestroyed();
+ void shuttingDown();
+
+private:
+ kmidClient *kmidclient;
+};
+
+#endif
diff --git a/kmid/kmidui.rc b/kmid/kmidui.rc
new file mode 100644
index 00000000..8553ad2e
--- /dev/null
+++ b/kmid/kmidui.rc
@@ -0,0 +1,44 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kmid" version="3">
+<MenuBar>
+ <Menu name="file"><text>&amp;File</text>
+ <Action name="file_save_lyrics"/>
+ </Menu>
+ <Menu name="song"><text>&amp;Song</text>
+ <Action name="song_play"/>
+ <Action name="song_pause"/>
+ <Action name="song_stop"/>
+ <Action name="song_previous"/>
+ <Action name="song_next"/>
+ <Action name="song_loop"/>
+ </Menu>
+ <Menu name="collections"><text>&amp;Collections</text>
+ <Action name="collect_organize"/>
+ <Action name="play_order"/>
+ <Action name="collect_autoadd"/>
+ </Menu>
+ <Menu name="settings">
+ <Action name="toggle_volumebar" append="show_merge"/>
+ <Action name="toggle_channelview" append="show_merge"/>
+ <Action name="file_type" append="save_merge"/>
+ <Action name="display_events" append="save_merge"/>
+ <Action name="option_automatictext" append="save_merge"/>
+ <Separator append="save_merge"/>
+ <Action name="channelview_options" append="save_merge"/>
+ <Action name="change_font" append="save_merge"/>
+ <Action name="midi_setup" append="save_merge"/>
+ </Menu>
+</MenuBar>
+<ToolBar fullWidth="true" name="mainToolBar"><text>Main Toolbar</text>
+ <Action name="song_previous"/>
+ <Action name="song_rewind"/>
+ <Action name="song_stop"/>
+ <Action name="song_pause"/>
+ <Action name="song_play"/>
+ <Action name="song_forward"/>
+ <Action name="song_next"/>
+ <Separator/>
+ <Action name="toggle_volumebar"/>
+ <Action name="toggle_channelview"/>
+</ToolBar>
+</kpartgui>
diff --git a/kmid/ktrianglebutton.cpp b/kmid/ktrianglebutton.cpp
new file mode 100644
index 00000000..61e1af96
--- /dev/null
+++ b/kmid/ktrianglebutton.cpp
@@ -0,0 +1,164 @@
+/**************************************************************************
+
+ ktrianglebutton.cpp - The KTriangleButton widget (button with an arrow)
+ Copyright (C) 1998 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+ Note: This widget was based on KButton as found in the kdelibs/kdeui
+ KButton was originally copyrighted by Torben Weis (weis@kde.org)
+ and Matthias Ettrich (ettrich@kde.org) on 1997
+
+***************************************************************************/
+#include "ktrianglebutton.h"
+#include <qpainter.h>
+#include <qdrawutil.h>
+#include <qstyle.h>
+
+KTriangleButton::KTriangleButton( Direction d,QWidget *_parent, const char *name )
+ : QButton( _parent , name)
+{
+ dir=d;
+ raised = FALSE;
+ setFocusPolicy( NoFocus );
+}
+
+KTriangleButton::~KTriangleButton()
+{
+}
+
+void KTriangleButton::enterEvent( QEvent* )
+{
+ if ( isEnabled() )
+ {
+ raised = TRUE;
+ repaint(FALSE);
+ }
+}
+
+void KTriangleButton::leaveEvent( QEvent * )
+{
+ if( raised != FALSE )
+ {
+ raised = FALSE;
+ repaint();
+ }
+}
+
+
+void KTriangleButton::drawButton( QPainter *_painter )
+{
+ paint( _painter );
+}
+
+void KTriangleButton::drawButtonLabel( QPainter *_painter )
+{
+ paint( _painter );
+}
+
+void KTriangleButton::paint( QPainter *painter )
+{
+ if ( isDown() || isOn() )
+ {
+ if ( style().styleHint(QStyle::SH_GUIStyle) == WindowsStyle )
+ qDrawWinButton( painter, 0, 0, width(),
+ height(), colorGroup(), TRUE );
+ else
+ qDrawShadePanel( painter, 0, 0, width(),
+ height(), colorGroup(), TRUE, 2, 0L );
+ }
+ else if ( raised )
+ {
+ if ( style().styleHint(QStyle::SH_GUIStyle) == WindowsStyle )
+ qDrawWinButton( painter, 0, 0, width(), height(),
+ colorGroup(), FALSE );
+ else
+ qDrawShadePanel( painter, 0, 0, width(), height(),
+ colorGroup(), FALSE, 2, 0L );
+ }
+
+ if (dir==Right)
+ {
+ int x=width()/4;
+ int y=height()/6;
+ int l=height()-y*2;
+ int i=0;
+ int maxi=width()-2*x;
+ double m=(double)(l/2)/maxi;
+ while (i<=maxi)
+ {
+ painter->drawLine(x,y+(int)(i*m),x,y+l-(int)(i*m));
+ x++;
+ i++;
+ };
+ }
+ else if (dir==Left)
+ {
+ int x=width()/4;
+ int y=height()/6;
+ int l=height()-y*2;
+ int i=0;
+ int maxi=width()-2*x;
+ x=width()-x;
+ double m=(double)(l/2)/maxi;
+ while (i<=maxi)
+ {
+ painter->drawLine(x,y+(int)(i*m),x,y+l-(int)(i*m));
+ x--;
+ i++;
+ };
+
+ };
+
+}
+
+void KTriangleButton::mousePressEvent(QMouseEvent *e)
+{
+ QButton::mousePressEvent(e);
+ usingTimer=true;
+ startTimer(500);
+ timeCount=0;
+
+}
+
+void KTriangleButton::mouseReleaseEvent(QMouseEvent *e)
+{
+ usingTimer=false;
+ QButton::mouseReleaseEvent(e);
+}
+
+void KTriangleButton::timerEvent(QTimerEvent *)
+{
+ if (!usingTimer) {killTimers();return;};
+ if (timeCount==0)
+ {
+ timeCount++;
+ killTimers();
+ startTimer(120);
+ } else
+ if (timeCount==30)
+ {
+ timeCount=-1;
+ killTimers();
+ startTimer(80);
+ }
+ else if (timeCount>0) timeCount++;
+ emit clickedQuickly();
+
+}
+#include "ktrianglebutton.moc"
diff --git a/kmid/ktrianglebutton.h b/kmid/ktrianglebutton.h
new file mode 100644
index 00000000..f7bad7fd
--- /dev/null
+++ b/kmid/ktrianglebutton.h
@@ -0,0 +1,73 @@
+/**************************************************************************
+
+ ktrianglebutton.h - The KTriangleButton widget (button with an arrow)
+ Copyright (C) 1998 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+ Note: This widget was copied from KButton as found in the kdelibs/kdeui
+ KButton was originally copyrighted by Torben Weis (weis@kde.org)
+ and Matthias Ettrich (ettrich@kde.org) on 1997
+
+***************************************************************************/
+#ifndef _ktrianglebutton_h_
+#define _ktrianglebutton_h_
+
+#include <qbutton.h>
+
+class KTriangleButton : public QButton
+{
+ Q_OBJECT
+
+public:
+ enum Direction {Left , Right, Up, Down};
+ /*
+ Up and Down are not implemented, feel free to implement them yourself
+ if you need them :-)
+ */
+protected:
+
+ Direction dir;
+ bool usingTimer;
+ int timeCount;
+
+public:
+ KTriangleButton( Direction d,QWidget *_parent = 0L, const char *name = 0L );
+ ~KTriangleButton();
+
+ virtual void leaveEvent( QEvent *_ev );
+ virtual void enterEvent( QEvent *_ev );
+
+ virtual void drawButton( QPainter *_painter );
+ virtual void drawButtonLabel( QPainter *_painter );
+
+ void paint( QPainter *_painter );
+
+signals:
+ void clickedQuickly();
+
+protected:
+ bool raised;
+
+ virtual void mousePressEvent(QMouseEvent *e);
+ virtual void mouseReleaseEvent(QMouseEvent *e);
+ virtual void timerEvent(QTimerEvent *);
+
+};
+
+#endif
diff --git a/kmid/main.cpp b/kmid/main.cpp
new file mode 100644
index 00000000..bb991d74
--- /dev/null
+++ b/kmid/main.cpp
@@ -0,0 +1,101 @@
+/**************************************************************************
+
+ main.cpp - The main function for KMid
+ Copyright (C) 1997,98 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+#include <qwidget.h>
+#include <qtextcodec.h>
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <kio/job.h>
+#include <kcmdlineargs.h>
+#include <kaboutdata.h>
+
+#include "kmidframe.h"
+#include "kmid_part.h"
+#include "version.h"
+
+int main(int argc, char **argv)
+{
+ printf("%s Copyright (C) 1997,98,99,2000,01 Antonio Larrosa Jimenez. Malaga (Spain)\n",VERSION_TXT);
+ printf("KMid comes with ABSOLUTELY NO WARRANTY; for details view file COPYING\n");
+ printf("This is free software, and you are welcome to redistribute it\n");
+ printf("under certain conditions\n\n");
+
+ QTextCodec::setCodecForCStrings(QTextCodec::codecForLocale());
+/*
+ struct sigaction act;
+ act.sa_handler = SIG_DFL;
+ sigemptyset(&(act.sa_mask));
+ act.sa_flags=0;
+
+ sigaction(SIGINT, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+*/
+
+ KCmdLineArgs::init(argc, argv, KMidFactory::aboutData() );
+
+ static KCmdLineOptions options[] =
+ {
+ { "+file", I18N_NOOP("File to open"), 0 },
+ KCmdLineLastOption
+ };
+ KCmdLineArgs::addCmdLineOptions(options);
+
+ KApplication app;
+
+ kmidFrame *kmidframe=new kmidFrame("KMid");
+
+ app.setMainWidget ( kmidframe );
+
+ QObject::connect(&app,SIGNAL(shutDown()),kmidframe,SLOT(shuttingDown()));
+
+ if (app.isRestored())
+ {
+ if (kmidframe->canBeRestored(1)) kmidframe->restore(1);
+ }
+
+ kmidframe->show();
+
+ //CT KIOJob::initStatic();
+
+/*
+ if (app->isRestored())
+ {
+ RESTORE(kmidFrame);
+ }
+ else
+ {
+ kmidFrame *kmidframe=new kmidFrame("KMid");
+ kmidframe->show();
+ };
+*/
+
+
+
+ return app.exec();
+}
+
diff --git a/kmid/maps/Makefile.am b/kmid/maps/Makefile.am
new file mode 100644
index 00000000..d276e13e
--- /dev/null
+++ b/kmid/maps/Makefile.am
@@ -0,0 +1,5 @@
+
+maps_DATA = gm.map YamahaPSS790.map YamahaPSR500.map YamahaQY10.map
+mapsdir = $(kde_datadir)/kmid/maps
+
+EXTRA_DIST = $(maps_DATA)
diff --git a/kmid/maps/YamahaPSR500.map b/kmid/maps/YamahaPSR500.map
new file mode 100644
index 00000000..16068c21
--- /dev/null
+++ b/kmid/maps/YamahaPSR500.map
@@ -0,0 +1,293 @@
+# Blank lines and lines beginning with # are ignored
+# This is a map for a Yamaha PSR-500 keyboard
+# This file was done by Dietmar Schnabel (thanks !)
+
+# If you make a new map, please send it to Antonio, so future releases
+# will include them.
+# Antonio Larrosa Jimenez, larrosa@kde.org
+
+# General Midi Map File
+
+DEFINE PATCHMAP
+AcusticPiano =0
+BrightPiano =0
+SynthPiano =3
+HonkyTonky =2
+ElectricPiano1=4
+ElectricPiano2=5
+Harpsichord =6
+Clavinet =8
+Celeste =9
+Glockenspiel =40
+Musicbox =41
+Vibes =40
+Marimba =41
+Xylophon =41
+TubeBell =41
+Santur =42
+HomeOrg =12
+PercussionOrg =13
+RockOrg =14
+ChurchOrg =10
+ReedOrg =11
+Accordion =16
+Harmonica =17
+Concrtna =17
+NylonGuitar =29
+AcusticGuitar =31
+JazzGuitar =24
+CleanGuitar =19
+MuteGuitar =26
+OdGuitar =18
+DistortionGuit=23
+GtrHarm =20
+AcusticBass =88
+FingerBass =89
+PickBass =87
+FretlessBass =84
+SlapBass1 =87
+SlapBass2 =87
+SynthBass1 =90
+SynthBass2 =91
+Violin =32
+Viola =33
+Cello =34
+Contrabajo =33
+Marcato =34
+Pizzicato =38
+Harp =38
+Timpani =33
+Marcato =34
+SlowStrings =35
+SynthStrings1 =36
+SynthStrings2 =75
+Choir =72
+Doo =72
+Voices =72
+OrchestraHit =37
+Trumpet =43
+Trombone =47
+Tuba =50
+MuteTrumpet =44
+FrenchHorn =49
+HitBrass =51
+SynthBrass1 =52
+SynthBrass2 =74
+SopranoSax =60
+AltoSax =61
+TenorSax =62
+BaritoneSax =63
+Oboe =57
+EnglishHorn =58
+Bassoon =59
+Clarinet =55
+Piccolo =53
+Flute =54
+Recorder =66
+WoodFlute =65
+Bottle =55
+Shakazul =55
+Whistle =68
+Ocarina =79
+SquareWave =80
+SawWave =81
+Calliope =82
+Chiflead =83
+Charang =84
+VoxLead =85
+Lead5th =86
+BassLead =87
+Fantasia =88
+WarmPad =89
+Polysyn =90
+Ghostie =91
+BowGlass =92
+MetalPad =93
+HaloPad =94
+Sweeper =95
+Aurora =72
+SoundTrack =72
+Crystal =72
+Atmosphear =72
+FreshAir =100
+Unicorn =101
+Sweeper =102
+StarTrak =103
+Sitar =104
+Banjo =105
+Shamisen =106
+Koto =107
+Kalimba =108
+BagPipes =109
+Fiddle =110
+Shannai =111
+Carillon =112
+Agogo =113
+SteelDrm =114
+WoodBlk =115
+Taiko =116
+Toms =117
+Syntom =118
+RevCymbal =119
+Fx-Fret =120
+Fx-Blow =121
+Seashore =122
+Jungle =123
+Telephon =124
+Helicopter =125
+Applause =126
+Gunshot =127
+END
+
+DEFINE KEYMAP "Drumset"
+C 0 =0
+C#0 =1
+D 0 =2
+D#0 =3
+E 0 =4
+F 0 =5
+F#0 =6
+G 0 =7
+G#0 =8
+A 0 =9
+A#0 =10
+B 0 =11
+C 1 =12
+C#1 =13
+D 1 =14
+D#1 =15
+E 1 =16
+F 1 =17
+F#1 =18
+G 1 =19
+G#1 =20
+A 1 =21
+A#1 =22
+B 1 =23
+C 2 =24
+C#2 =25
+D 2 =26
+D#2 27 Highq =27
+E 2 28 Slap =54
+F 2 29 Scratch1 =62
+F#2 30 Scratch2 =63
+G 2 31 Sticks =22
+G#2 32 SqrClick =87
+A 2 33 MetaClick=57
+A#2 34 MetBell =55
+B 2 35 Kick1 =44
+C 3 36 Kick2 =45
+C#3 37 StickRim =27
+D 3 38 Snare1 =28
+D#3 39 Claps =30
+E 3 40 snare2 =25
+F 3 41 tomlo2 =24
+F#3 42 Hihatclos=72
+G 3 43 Tomlo1 =17
+G#3 44 HihatPd =56
+A 3 45 Tommid2 =26
+A#3 46 Hihatop =35
+B 3 47 Tommid1 =18
+C 4 48 Tomhi2 =29
+C#4 49 Cymbalcrsh1 =36
+D 4 50 Tomhi1 =19
+D#4 51 Cymbalride1 =38
+E 4 52 Cymbalchina =38
+F 4 53 Cymbell =38
+F#4 54 Tamborin =47
+G 4 55 CymbalSplash=37
+G#4 56 Cowbell =31
+A 4 57 Cymbalcrash2=36
+A#4 58 Vibslap =39
+B 4 59 Cymbalride2 =39
+C 5 60 Bongohi =44
+C#5 61 Bongolo =43
+D 5 62 Congahi1 =41
+D#5 63 congahi2 =42
+E 5 64 Congalo =40
+F 5 65 Timbalehi=46
+F#5 66 Timbalelo=45
+G 5 67 Agogohi =51
+G#5 68 Agogolo =50
+A 5 69 Cabasa =32
+A#5 70 Maracas =65
+B 5 71 whistle1 =52
+C 6 72 whistle2 =53
+C#6 73 Guiro1 =74
+D 6 74 Guiro2 =74
+D#6 75 Clave =29
+E 6 76 Woodblock1=73
+F 6 77 Woodblock2=72
+F#6 78 Cuica1 =76
+G 6 79 Cuica2 =77
+G#6 80 Triangle1=13
+A 6 81 Triangle2=15
+A#6 82 Shaker =56
+B 6 83 Jingles =75
+C 7 84 Belltree =71
+C#7 85 Canstinet=85
+D 7 86 Surdo1 =86
+D#7 87 Surdo2 =87
+E 7 =88
+F 7 =89
+F#7 =90
+G 7 =91
+G#7 =92
+A 7 =93
+A#7 =94
+B 7 =95
+C 8 =96
+C#8 =97
+D 8 =98
+D#8 =99
+E 8 =100
+F 8 =101
+F#8 =102
+G 8 =103
+G#8 =104
+A 8 =105
+A#8 =106
+B 8 =107
+C 9 =108
+C#9 =109
+D 9 =110
+D#9 =111
+E 9 =112
+F 9 =113
+F#9 =114
+G 9 =115
+G#9 =116
+A 9 =117
+A#9 =118
+B 9 =119
+C 10=120
+C#10=121
+D 10=122
+D#10=123
+E 10=124
+F 10=125
+F#10=126
+G 10=127
+# This line should be ignored
+END
+
+
+DEFINE CHANNELMAP
+0 = 0
+1 = 1
+2 = 2
+3 = 3
+4 = 4
+5 = 5
+6 = 6
+7 = 7
+8 = 8
+9 = 9 Keymap "Drumset" ForcePatch 99
+10 = 10
+11 = 11
+12 = 12
+13 = 13
+14 = 14
+15 = 15
+END
+
diff --git a/kmid/maps/YamahaPSS790.map b/kmid/maps/YamahaPSS790.map
new file mode 100644
index 00000000..5017832e
--- /dev/null
+++ b/kmid/maps/YamahaPSS790.map
@@ -0,0 +1,299 @@
+# Blank lines and lines beginning with # are ignored
+# This is my a map for the Yamaha PSS-790 keyboard
+# it can be used as a template to make any new map for any
+# other keyboard.
+# If you make a new map, please send it to me, so future releases
+# will include it.
+# Antonio Larrosa Jimenez, larrosa@kde.org
+
+# Yamaha PSS-790 Map File
+
+DEFINE PATCHMAP
+AcusticPiano =3
+BrightPiano =3
+SynthPiano =54
+HonkyTonky =25
+ElectricPiano1=25
+ElectricPiano2=25
+Harpsichord =53
+Clavinet =30
+Celeste =6
+Glockenspiel =64
+Musicbox =24
+Vibes =28
+Marimba =8
+Xylophon =24
+TubeBell =95
+Santur =30
+HomeOrg =1
+PercussionOrg =2
+RockOrg =11
+ChurchOrg =29
+ReedOrg =49
+Accordion =31
+Harmonica =22
+Concrtna =3
+NylonGuitar =36
+AcusticGuitar =68
+JazzGuitar =122
+CleanGuitar =68
+MuteGuitar =12
+OdGuitar =123
+DistortionGuit=13
+GtrHarm =72
+AcusticBass =38
+FingerBass =39
+PickBass =79
+FretlessBass =80
+SlapBass1 =14
+SlapBass2 =81
+SynthBass1 =58
+SynthBass2 =86
+Violin =10
+Viola =67
+Cello =67
+Contrabajo =41
+Marcato =41
+Pizzicato =78
+Harp =37
+Timpani =66
+Marcato =41
+SlowStrings =50
+SynthStrings1 =41
+SynthStrings2 =50
+Choir =96
+Doo =94
+Voices =96
+OrchestraHit =90
+Trumpet =15
+Trombone =16
+Tuba =84
+MuteTrumpet =44
+FrenchHorn =17
+HitBrass =92
+SynthBrass1 =0
+SynthBrass2 =82
+SopranoSax =101
+AltoSax =18
+TenorSax =102
+BaritoneSax =101
+Oboe =19
+EnglishHorn =87
+Bassoon =87
+Clarinet =19
+Piccolo =103
+Flute =20
+Recorder =88
+WoodFlute =111
+Bottle =111
+Shakazul =110
+Whistle =88
+Ocarina =93
+SquareWave =54
+SawWave =54
+Calliope =22
+Chiflead =9
+Charang =19
+VoxLead =96
+Lead5th =41
+BassLead =95
+Fantasia =111
+WarmPad =51
+Polysyn =60
+Ghostie =94
+BowGlass =33
+MetalPad =9
+HaloPad =111
+Sweeper =78
+Aurora =60
+SoundTrack =84
+Crystal =60
+Atmosphear =51
+FreshAir =60
+Unicorn =93
+Sweeper =112
+StarTrak =117
+Sitar =73
+Banjo =34
+Shamisen =73
+Koto =70
+Kalimba =73
+BagPipes =11
+Fiddle =67
+Shannai =11
+Carillon =64
+Agogo =91
+SteelDrm =9
+WoodBlk =35
+Taiko =100 AllKeysTo 60
+Toms =66
+Syntom =85
+RevCymbal =100 AllKeysTo 60
+Fx-Fret =121
+Fx-Blow =94
+Seashore =100 AllKeysTo 79
+Jungle =70
+Telephon =91
+Helicopter =111
+Applause =100 AllKeysTo 79
+Gunshot =100 AllKeysTo 38
+
+END
+
+DEFINE KEYMAP "Drumset"
+C 0 =0
+C#0 =1
+D 0 =2
+D#0 =3
+E 0 =4
+F 0 =5
+F#0 =6
+G 0 =7
+G#0 =8
+A 0 =9
+A#0 =10
+B 0 =11
+C 1 =12
+C#1 =13
+D 1 =14
+D#1 =15
+E 1 =16
+F 1 =17
+F#1 =18
+G 1 =19
+G#1 =20
+A 1 =21
+A#1 =22
+B 1 =23
+C 2 =24
+C#2 =25
+D 2 =26
+D#2 27 Highq =27
+E 2 28 Slap =54
+F 2 29 Scratch1 =29
+F#2 30 Scratch2 =30
+G 2 31 Sticks =46
+G#2 32 SqrClick =87
+A 2 33 MetaClick=57
+A#2 34 MetBell =55
+B 2 35 AcousticBassDrum=44
+C 3 36 BassDrum1=47
+C#3 37 StickRim =46
+D 3 38 AcSnare =47
+D#3 39 Claps =63
+E 3 40 ElectSnare =49
+F 3 41 LowFloorTom =48
+F#3 42 Hihatclos =57
+G 3 43 HighFloorTom=50
+G#3 44 PedalHihat =80
+A 3 45 LowTom =47
+A#3 46 OpenHihat =59
+B 3 47 LowMidTom =48
+C 4 48 HiMidTom =50
+C#4 49 Cymbalcrsh1 =60
+D 4 50 HiTom =53
+D#4 51 Cymbalride1 =63
+E 4 52 Cymbalchina =60
+F 4 53 RideBell =62
+F#4 54 Tambourin =71
+G 4 55 CymbalSplash=61
+G#4 56 Cowbell =55
+A 4 57 Cymbalcrash2=60
+A#4 58 Vibraslap =79
+B 4 59 Cymbalride2 =62
+C 5 60 Bongohi =68
+C#5 61 Bongolo =67
+D 5 62 MuteHiConga =66
+D#5 63 OpenHiConga =65
+E 5 64 LowConga =64
+F 5 65 HiTimbale =70
+F#5 66 LoTimbale =69
+G 5 67 HiAgogo =75
+G#5 68 LoAgogo =74
+A 5 69 Cabasa =56
+A#5 70 Maracas =56
+B 5 71 ShortWhistle=78
+C 6 72 LongWhistle =78
+C#6 73 ShortGuiro =79
+D 6 74 LongGuiro =71
+D#6 75 Clave =72
+E 6 76 HiWoodBlock =73
+F 6 77 LoWoodBlock =72
+F#6 78 MuteCuica =77
+G 6 79 OpenCuica =76
+G#6 80 MuteTriangle=37
+A 6 81 OpenTriangle=39
+A#6 82 Shaker =56
+B 6 83 Jingles =75
+C 7 84 Belltree =63
+C#7 85 Canstinet=85
+D 7 86 Surdo1 =86
+D#7 87 Surdo2 =87
+E 7 =88
+F 7 =89
+F#7 =90
+G 7 =91
+G#7 =92
+A 7 =93
+A#7 =94
+B 7 =95
+C 8 =96
+C#8 =97
+D 8 =98
+D#8 =99
+E 8 =100
+F 8 =101
+F#8 =102
+G 8 =103
+G#8 =104
+A 8 =105
+A#8 =106
+B 8 =107
+C 9 =108
+C#9 =109
+D 9 =110
+D#9 =111
+E 9 =112
+F 9 =113
+F#9 =114
+G 9 =115
+G#9 =116
+A 9 =117
+A#9 =118
+B 9 =119
+C 10=120
+C#10=121
+D 10=122
+D#10=123
+E 10=124
+F 10=125
+F#10=126
+G 10=127
+# This line should be ignored
+END
+
+
+DEFINE CHANNELMAP
+0 = 0
+1 = 1
+2 = 2
+3 = 3
+4 = 4
+5 = 5
+6 = 6
+7 = 7
+8 = 8
+9 = 15 Keymap "Drumset" ForcePatch 100
+10 = 10
+11 = 11
+12 = 12
+13 = 13
+14 = 14
+15 = 9
+END
+
+OPTIONS
+PitchBenderRatio = 672
+MapExpressionToVolumeEvents
+END
+
diff --git a/kmid/maps/YamahaQY10.map b/kmid/maps/YamahaQY10.map
new file mode 100644
index 00000000..1a4914b4
--- /dev/null
+++ b/kmid/maps/YamahaQY10.map
@@ -0,0 +1,305 @@
+# Midi map file for the Yamaha QY10 "walkstation", a basic sequencer / tone
+# module.
+
+# Provided by Malcolm Tyrrell. If there is a problem with this file, please
+# e-mail tyrrelmr@cs.tcd.ie
+
+# If you make a new map, please send it to Antonio, so future releases
+# will include them.
+# Antonio Larrosa Jimenez, larrosa@kde.org
+
+# Yamaha QY10 Map File
+
+DEFINE PATCHMAP
+AcusticPiano =0
+BrightPiano =0
+SynthPiano =1
+HonkyTonky =0
+ElectricPiano1=1
+ElectricPiano2=2
+Harpsichord =3
+Clavinet =3
+Celeste =18
+Glockenspiel =18
+Musicbox =20
+Vibes =19
+Marimba =18
+Xylophon =20
+TubeBell =19
+Santur =12
+HomeOrg =4
+PercussionOrg =4
+RockOrg =4
+ChurchOrg =5
+ReedOrg =5
+Accordion =5
+Harmonica =23
+Concrtna =23
+NylonGuitar =12
+AcusticGuitar =12
+JazzGuitar =9
+CleanGuitar =9
+MuteGuitar =11
+OdGuitar =9
+DistortionGuit=10
+GtrHarm =25
+AcusticBass =13
+FingerBass =14
+PickBass =14
+FretlessBass =13
+SlapBass1 =15
+SlapBass2 =15
+SynthBass1 =16
+SynthBass2 =16
+Violin =23
+Viola =23
+Cello =23
+Contrabajo =23
+Marcato =6
+Pizzicato =16
+Harp =12
+Timpani =20
+Marcato =6
+SlowStrings =6
+SynthStrings1 =22
+SynthStrings2 =22
+Choir =27
+Doo =21
+Voices =21
+OrchestraHit =8
+Trumpet =26
+Trombone =26
+Tuba =21
+MuteTrumpet =8
+FrenchHorn =26
+HitBrass =8
+SynthBrass1 =21
+SynthBrass2 =21
+SopranoSax =26
+AltoSax =26
+TenorSax =26
+BaritoneSax =26
+Oboe =27
+EnglishHorn =26
+Bassoon =27
+Clarinet =27
+Piccolo =27
+Flute =27
+Recorder =27
+WoodFlute =27
+Bottle =27
+Shakazul =27
+Whistle =27
+Ocarina =27
+SquareWave =28
+SawWave =28
+Calliope =29
+Chiflead =29
+Charang =24
+VoxLead =21
+Lead5th =21
+BassLead =21
+Fantasia =24
+WarmPad =21
+Polysyn =22
+Ghostie =27
+BowGlass =26
+MetalPad =21
+HaloPad =27
+Sweeper =24
+Aurora =31
+SoundTrack =21
+Crystal =19
+Atmosphear =24
+FreshAir =21
+Unicorn =24
+Sweeper =24
+StarTrak =21
+Sitar =9
+Banjo =9
+Shamisen =12
+Koto =12
+Kalimba =18
+BagPipes =28
+Fiddle =28
+Shannai =23
+Carillon =19
+Agogo =20
+SteelDrm =18
+WoodBlk =20
+Taiko =20
+Toms =18
+Syntom =18
+RevCymbal =31
+Fx-Fret =31
+Fx-Blow =31
+Seashore =31
+Jungle =31
+Telephon =5
+Helicopter =31
+Applause =31
+Gunshot =31
+END
+
+DEFINE KEYMAP "Drumset"
+C 0 =0
+C#0 =1
+D 0 =2
+D#0 =3
+E 0 =4
+F 0 =5
+F#0 =6
+G 0 =7
+G#0 =8
+A 0 =9
+A#0 =10
+B 0 =11
+C 1 =12
+C#1 =13
+D 1 =14
+D#1 =15
+E 1 =16
+F 1 =17
+F#1 =18
+G 1 =19
+G#1 =20
+A 1 =21
+A#1 =22
+B 1 =23
+C 2 =24
+C#2 =25
+D 2 =26
+# What is a high q?
+D#2 27 Highq =40
+E 2 28 Slap =37
+F 2 29 Scratch1 =23
+F#2 30 Scratch2 =48
+G 2 31 Sticks =21
+G#2 32 SqrClick =42
+A 2 33 MetaClick=42
+A#2 34 MetBell =43
+B 2 35 Kick1 =36
+C 3 36 Kick2 =12
+C#3 37 StickRim =21
+D 3 38 Snare1 =15
+D#3 39 Claps =19
+E 3 40 snare2 =14
+F 3 41 tomlo2 =16
+F#3 42 Hihatclos=23
+G 3 43 Tomlo1 =16
+G#3 44 HihatPd =23
+A 3 45 Tommid2 =17
+A#3 46 Hihatop =24
+B 3 47 Tommid1 =17
+C 4 48 Tomhi2 =18
+C#4 49 Cymbalcrsh1 =20
+D 4 50 Tomhi1 =18
+D#4 51 Cymbalride1 =22
+E 4 52 Cymbalchina =22
+F 4 53 Cymbell =22
+F#4 54 Tamborin =21
+G 4 55 CymbalSplash=20
+G#4 56 Cowbell =37
+A 4 57 Cymbalcrash2=20
+A#4 58 Vibslap =0
+B 4 59 Cymbalride2 =22
+C 5 60 Bongohi =41
+C#5 61 Bongolo =40
+D 5 62 Congahi1 =41
+D#5 63 congahi2 =41
+E 5 64 Congalo =40
+F 5 65 Timbalehi=46
+F#5 66 Timbalelo=45
+G 5 67 Agogohi =44
+G#5 68 Agogolo =43
+A 5 69 Cabasa =48
+A#5 70 Maracas =48
+B 5 71 whistle1 =0
+C 6 72 whistle2 =0
+C#6 73 Guiro1 =0
+D 6 74 Guiro2 =0
+D#6 75 Clave =44
+E 6 76 Woodblock1=40
+F 6 77 Woodblock2=41
+# Don't know about the following two
+F#6 78 Cuica1 =0
+G 6 79 Cuica2 =0
+G#6 80 Triangle1=44
+A 6 81 Triangle2=44
+A#6 82 Shaker =48
+B 6 83 Jingles =23
+C 7 84 Belltree =0
+C#7 85 Canstinet=21
+D 7 86 Surdo1 =16
+D#7 87 Surdo2 =17
+E 7 =88
+F 7 =89
+F#7 =90
+G 7 =91
+G#7 =92
+A 7 =93
+A#7 =94
+B 7 =95
+C 8 =96
+C#8 =97
+D 8 =98
+D#8 =99
+E 8 =100
+F 8 =101
+F#8 =102
+G 8 =103
+G#8 =104
+A 8 =105
+A#8 =106
+B 8 =107
+C 9 =108
+C#9 =109
+D 9 =110
+D#9 =111
+E 9 =112
+F 9 =113
+F#9 =114
+G 9 =115
+G#9 =116
+A 9 =117
+A#9 =118
+B 9 =119
+C 10=120
+C#10=121
+D 10=122
+D#10=123
+E 10=124
+F 10=125
+F#10=126
+G 10=127
+END
+
+
+DEFINE CHANNELMAP
+# The information on channels 7-8 and 11-15 is ignored since the QY10 is only
+# 8 part multi-timbral. The polyphony should be okay.
+0 = 0
+1 = 1
+2 = 2
+3 = 3
+4 = 4
+5 = 5
+6 = 6
+# The next two channels are ignored
+7 = 9
+8 = 8
+# Play GM channel 9 (drums) on QY10 track 7 (rhythm track)
+9 = 7 Keymap "Drumset" ForcePatch 30
+# All the following channels are ignored.
+10 = 10
+11 = 11
+12 = 12
+13 = 13
+14 = 14
+15 = 15
+END
+
+# I don't think the QY10 responds to Expression Events, so...
+OPTIONS
+MapExpressionToVolumeEvents
+END
diff --git a/kmid/maps/gm.map b/kmid/maps/gm.map
new file mode 100644
index 00000000..fd188ec0
--- /dev/null
+++ b/kmid/maps/gm.map
@@ -0,0 +1,290 @@
+# Blank lines and lines beginning with # are ignored
+# This is a map for any General Midi synthesizer
+# If you make a new map, please send it to me, so future releases
+# will include them.
+# Antonio Larrosa Jimenez, larrosa@kde.org
+
+# General Midi Map File
+
+DEFINE PATCHMAP
+AcusticPiano =0
+BrightPiano =1
+SynthPiano =2
+HonkyTonky =3
+ElectricPiano1=4
+ElectricPiano2=5
+Harpsichord =6
+Clavinet =7
+Celeste =8
+Glockenspiel =9
+Musicbox =10
+Vibes =11
+Marimba =12
+Xylophon =13
+TubeBell =14
+Santur =15
+HomeOrg =16
+PercussionOrg =17
+RockOrg =18
+ChurchOrg =19
+ReedOrg =20
+Accordion =21
+Harmonica =22
+Concrtna =23
+NylonGuitar =24
+AcusticGuitar =25
+JazzGuitar =26
+CleanGuitar =27
+MuteGuitar =28
+OdGuitar =29
+DistortionGuit=30
+GtrHarm =31
+AcusticBass =32
+FingerBass =33
+PickBass =34
+FretlessBass =35
+SlapBass1 =36
+SlapBass2 =37
+SynthBass1 =38
+SynthBass2 =39
+Violin =40
+Viola =41
+Cello =42
+Contrabajo =43
+Marcato =44
+Pizzicato =45
+Harp =46
+Timpani =47
+Marcato =48
+SlowStrings =49
+SynthStrings1 =50
+SynthStrings2 =51
+Choir =52
+Doo =53
+Voices =54
+OrchestraHit =55
+Trumpet =56
+Trombone =57
+Tuba =58
+MuteTrumpet =59
+FrenchHorn =60
+HitBrass =61
+SynthBrass1 =62
+SynthBrass2 =63
+SopranoSax =64
+AltoSax =65
+TenorSax =66
+BaritoneSax =67
+Oboe =68
+EnglishHorn =69
+Bassoon =70
+Clarinet =71
+Piccolo =72
+Flute =73
+Recorder =74
+WoodFlute =75
+Bottle =76
+Shakazul =77
+Whistle =78
+Ocarina =79
+SquareWave =80
+SawWave =81
+Calliope =82
+Chiflead =83
+Charang =84
+VoxLead =85
+Lead5th =86
+BassLead =87
+Fantasia =88
+WarmPad =89
+Polysyn =90
+Ghostie =91
+BowGlass =92
+MetalPad =93
+HaloPad =94
+Sweeper =95
+Aurora =96
+SoundTrack =97
+Crystal =98
+Atmosphear =99
+FreshAir =100
+Unicorn =101
+Sweeper =102
+StarTrak =103
+Sitar =104
+Banjo =105
+Shamisen =106
+Koto =107
+Kalimba =108
+BagPipes =109
+Fiddle =110
+Shannai =111
+Carillon =112
+Agogo =113
+SteelDrm =114
+WoodBlk =115
+Taiko =116
+Toms =117
+Syntom =118
+RevCymbal =119
+Fx-Fret =120
+Fx-Blow =121
+Seashore =122
+Jungle =123
+Telephon =124
+Helicopter =125
+Applause =126
+Gunshot =127
+END
+
+DEFINE KEYMAP "Drumset"
+C 0 =0
+C#0 =1
+D 0 =2
+D#0 =3
+E 0 =4
+F 0 =5
+F#0 =6
+G 0 =7
+G#0 =8
+A 0 =9
+A#0 =10
+B 0 =11
+C 1 =12
+C#1 =13
+D 1 =14
+D#1 =15
+E 1 =16
+F 1 =17
+F#1 =18
+G 1 =19
+G#1 =20
+A 1 =21
+A#1 =22
+B 1 =23
+C 2 =24
+C#2 =25
+D 2 =26
+D#2 27 Highq =27
+E 2 28 Slap =28
+F 2 29 Scratch1 =29
+F#2 30 Scratch2 =30
+G 2 31 Sticks =31
+G#2 32 SqrClick =32
+A 2 33 MetaClick=33
+A#2 34 MetBell =34
+B 2 35 Kick1 =35
+C 3 36 Kick2 =36
+C#3 37 StickRim =37
+D 3 38 Snare1 =38
+D#3 39 Claps =39
+E 3 40 snare2 =40
+F 3 41 tomlo2 =41
+F#3 42 Hihatclos=42
+G 3 43 Tomlo1 =43
+G#3 44 HihatPd =44
+A 3 45 Tommid2 =45
+A#3 46 Hihatop =46
+B 3 47 Tommid1 =47
+C 4 48 Tomhi2 =48
+C#4 49 Cymbalcrsh1 =49
+D 4 50 Tomhi1 =50
+D#4 51 Cymbalride1 =51
+E 4 52 Cymbalchina =52
+F 4 53 Cymbell =53
+F#4 54 Tamborin =54
+G 4 55 CymbalSplash=55
+G#4 56 Cowbell =56
+A 4 57 Cymbalcrash2=57
+A#4 58 Vibslap =58
+B 4 59 Cymbalride2 =59
+C 5 60 Bongohi =60
+C#5 61 Bongolo =61
+D 5 62 Congahi1 =62
+D#5 63 congahi2 =63
+E 5 64 Congalo =64
+F 5 65 Timbalehi=65
+F#5 66 Timbalelo=66
+G 5 67 Agogohi =67
+G#5 68 Agogolo =68
+A 5 69 Cabasa =69
+A#5 70 Maracas =70
+B 5 71 whistle1 =71
+C 6 72 whistle2 =72
+C#6 73 Guiro1 =73
+D 6 74 Guiro2 =74
+D#6 75 Clave =75
+E 6 76 Woodblock1=76
+F 6 77 Woodblock2=77
+F#6 78 Cuica1 =78
+G 6 79 Cuica2 =79
+G#6 80 Triangle1=80
+A 6 81 Triangle2=81
+A#6 82 Shaker =82
+B 6 83 Jingles =83
+C 7 84 Belltree =84
+C#7 85 Canstinet=85
+D 7 86 Surdo1 =86
+D#7 87 Surdo2 =87
+E 7 =88
+F 7 =89
+F#7 =90
+G 7 =91
+G#7 =92
+A 7 =93
+A#7 =94
+B 7 =95
+C 8 =96
+C#8 =97
+D 8 =98
+D#8 =99
+E 8 =100
+F 8 =101
+F#8 =102
+G 8 =103
+G#8 =104
+A 8 =105
+A#8 =106
+B 8 =107
+C 9 =108
+C#9 =109
+D 9 =110
+D#9 =111
+E 9 =112
+F 9 =113
+F#9 =114
+G 9 =115
+G#9 =116
+A 9 =117
+A#9 =118
+B 9 =119
+C 10=120
+C#10=121
+D 10=122
+D#10=123
+E 10=124
+F 10=125
+F#10=126
+G 10=127
+END
+
+
+DEFINE CHANNELMAP
+0 = 0
+1 = 1
+2 = 2
+3 = 3
+4 = 4
+5 = 5
+6 = 6
+7 = 7
+8 = 8
+9 = 9
+10 = 10
+11 = 11
+12 = 12
+13 = 13
+14 = 14
+15 = 15
+END
+
diff --git a/kmid/midicfgdlg.cpp b/kmid/midicfgdlg.cpp
new file mode 100644
index 00000000..c33b5043
--- /dev/null
+++ b/kmid/midicfgdlg.cpp
@@ -0,0 +1,138 @@
+/**************************************************************************
+
+ midicfgdlg.cpp - The midi config dialog
+ Copyright (C) 1997,98 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#include <qpushbutton.h>
+#include <qlistbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+
+#include <kapplication.h>
+#include <kfiledialog.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <libkmid/deviceman.h>
+
+#include "midicfgdlg.h"
+#include "version.h"
+#include <kglobal.h>
+#include <kstandarddirs.h>
+
+MidiConfigDialog::MidiConfigDialog(DeviceManager *dm,
+ QWidget *parent,const char *name) : KDialogBase(parent,name,TRUE,
+ i18n("Configure MIDI Devices"), KDialogBase::Ok|KDialogBase::Cancel)
+{
+ setMinimumSize(360,240);
+ QWidget *page = new QWidget( this );
+ setMainWidget(page);
+
+ QVBoxLayout *topLayout=new QVBoxLayout(page, 0, spacingHint());
+ QLabel *label=new QLabel(i18n("Select the MIDI device you want to use:"),page);
+ topLayout->addWidget(label);
+ mididevices=new QListBox(page,"midideviceslist");
+ topLayout->addWidget(mididevices,3);
+ connect(mididevices,SIGNAL(highlighted(int)),SLOT(deviceselected(int)));
+ devman=dm;
+ QString temp;
+ for (int i=0;i<devman->midiPorts()+devman->synthDevices();i++)
+ {
+ if (strcmp(devman->type(i),"")!=0)
+ temp = QString("%1 - %2").arg(devman->name(i)).arg(devman->type(i));
+ else
+ temp = devman->name(i);
+
+ mididevices->insertItem(temp,i);
+ };
+ selecteddevice=devman->defaultDevice();
+ mididevices->setCurrentItem(selecteddevice);
+
+ QLabel *label2=new QLabel(i18n("Use the MIDI map:"),page);
+ topLayout->addWidget(label2);
+
+
+ if (selectedmap!=NULL) delete selectedmap;
+ if (strcmp(devman->midiMapFilename(),"")==0)
+ selectedmap=NULL;
+ else
+ {
+ selectedmap=new char[strlen(devman->midiMapFilename())+1];
+ strcpy(selectedmap,devman->midiMapFilename());
+ }
+
+ if (selectedmap!=NULL) maplabel=new QLabel(selectedmap,page);
+ else maplabel=new QLabel(i18n("None"),page);
+
+ topLayout->addWidget(maplabel);
+
+ QHBoxLayout *hbox=new QHBoxLayout(topLayout);
+ hbox->addStretch(1);
+ mapbrowse=new QPushButton(i18n("Browse..."),page);
+ hbox->addWidget(mapbrowse);
+ connect(mapbrowse,SIGNAL(clicked()),SLOT(browseMap()) );
+
+ mapnone=new QPushButton(i18n("None"),page);
+ hbox->addWidget(mapnone);
+ connect(mapnone,SIGNAL(clicked()),SLOT(noMap()) );
+
+ topLayout->addStretch(1);
+
+}
+
+void MidiConfigDialog::deviceselected(int idx)
+{
+ selecteddevice=idx;
+}
+
+void MidiConfigDialog::browseMap()
+{
+ QString path = KGlobal::dirs()->findAllResources("appdata", "maps/*.map").last();
+ path.truncate(path.findRev('/'));
+
+ KURL url = KFileDialog::getOpenURL(path,"*.map",this);
+
+ if( url.isEmpty() )
+ return;
+
+ if( !url.isLocalFile() )
+ {
+ KMessageBox::sorry( 0L, i18n( "Only local files are currently supported." ) );
+ return;
+ }
+
+ QString filename = url.path();
+
+ delete selectedmap;
+ selectedmap=new char[filename.length()+1];
+ strcpy(selectedmap,QFile::encodeName(filename));
+ maplabel->setText(selectedmap);
+}
+
+void MidiConfigDialog::noMap()
+{
+ if (selectedmap!=NULL) {delete selectedmap;selectedmap=NULL;};
+ maplabel->setText(i18n("None"));
+}
+
+int MidiConfigDialog::selecteddevice=0;
+char *MidiConfigDialog::selectedmap=NULL;
+
+#include "midicfgdlg.moc"
diff --git a/kmid/midicfgdlg.h b/kmid/midicfgdlg.h
new file mode 100644
index 00000000..37417b36
--- /dev/null
+++ b/kmid/midicfgdlg.h
@@ -0,0 +1,60 @@
+/**************************************************************************
+
+ midicfgdlg.h - The midi config dialog
+ Copyright (C) 1997,98 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#ifndef _midicfgdlg_h_
+#define _midicfgdlg_h_
+
+#include <kdialogbase.h>
+
+class DeviceManager;
+
+class QLabel;
+class QPushButton;
+class QListBox;
+class MidiConfigDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+
+ MidiConfigDialog(DeviceManager *dm,QWidget *parent,const char *name);
+
+public slots:
+ void deviceselected(int idx);
+ void browseMap();
+ void noMap();
+
+private:
+ QLabel *maplabel;
+ QListBox *mididevices;
+ QPushButton *mapbrowse;
+ QPushButton *mapnone;
+
+ DeviceManager *devman;
+
+public:
+ static int selecteddevice;
+ static char *selectedmap;
+};
+
+#endif
diff --git a/kmid/pics/Makefile.am b/kmid/pics/Makefile.am
new file mode 100644
index 00000000..4bf75950
--- /dev/null
+++ b/kmid/pics/Makefile.am
@@ -0,0 +1,3 @@
+kmidicondir = $(kde_datadir)/kmid/icons
+kmidicon_ICON = AUTO
+
diff --git a/kmid/pics/cr16-action-piano.png b/kmid/pics/cr16-action-piano.png
new file mode 100644
index 00000000..29536a0a
--- /dev/null
+++ b/kmid/pics/cr16-action-piano.png
Binary files differ
diff --git a/kmid/pics/cr16-action-volume.png b/kmid/pics/cr16-action-volume.png
new file mode 100644
index 00000000..3b75a38c
--- /dev/null
+++ b/kmid/pics/cr16-action-volume.png
Binary files differ
diff --git a/kmid/pics/cr22-action-piano.png b/kmid/pics/cr22-action-piano.png
new file mode 100644
index 00000000..226e4444
--- /dev/null
+++ b/kmid/pics/cr22-action-piano.png
Binary files differ
diff --git a/kmid/pics/cr22-action-volume.png b/kmid/pics/cr22-action-volume.png
new file mode 100644
index 00000000..e87c5147
--- /dev/null
+++ b/kmid/pics/cr22-action-volume.png
Binary files differ
diff --git a/kmid/pics/cr32-action-piano.png b/kmid/pics/cr32-action-piano.png
new file mode 100644
index 00000000..9d75a570
--- /dev/null
+++ b/kmid/pics/cr32-action-piano.png
Binary files differ
diff --git a/kmid/pics/cr32-action-volume.png b/kmid/pics/cr32-action-volume.png
new file mode 100644
index 00000000..0e1f3dec
--- /dev/null
+++ b/kmid/pics/cr32-action-volume.png
Binary files differ
diff --git a/kmid/qslidertime.cpp b/kmid/qslidertime.cpp
new file mode 100644
index 00000000..be5fdab2
--- /dev/null
+++ b/kmid/qslidertime.cpp
@@ -0,0 +1,120 @@
+/**************************************************************************
+
+ kslidertime.cpp - A widget that displays time tags under a KSlider
+ Copyright (C) 1997,98 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#include "qslidertime.h"
+#include <qwidget.h>
+#include <qpainter.h>
+#include <stdio.h>
+
+#define ARROW_LENGTH 13
+
+QSliderTime::QSliderTime( QSlider *ksl, QWidget *parent, const char *name)
+ : QWidget (parent,name)
+{
+kslider=ksl;
+
+QPainter painter;
+QFontMetrics qfmt(painter.font());
+fontheight=qfmt.height();
+
+}
+
+char *QSliderTime::formatMillisecs(int ms,char *tmp)
+{
+ if (ms<60000)
+ {
+ sprintf(tmp,"0:%02d",ms/1000);
+ }
+ else
+ sprintf(tmp,"%d:%02d",ms/60000,(ms%60000)/1000);
+return tmp;
+}
+
+void QSliderTime::paintEvent( QPaintEvent * )
+{
+ QPainter painter(this);
+
+// erase();
+ drawTimeMarks(&painter);
+}
+
+int quantizeTimeStep(int t)
+{
+if (t<=2000) t=2000;
+ else if (t<=5000) t=5000;
+ else if (t<=10000) t=10000;
+ else if (t<=15000) t=15000;
+ else if (t<=30000) t=30000;
+ else if (t<=60000) t=60000;
+ else if (t<=120000) t=120000;
+return t;
+}
+
+void QSliderTime::drawTimeMarks(QPainter *painter)
+{
+if (kslider==NULL) return;
+ int i;
+ int maxV = kslider->maxValue();
+ QFontMetrics qfmt(painter->font());
+ fontheight=qfmt.height();
+ int ntimetags = width()/(qfmt.width("-88:88-"));
+ int timestep;
+ if (ntimetags>1) timestep = maxV/(ntimetags);
+ else timestep=maxV;
+ timestep = quantizeTimeStep(timestep);
+ ntimetags = maxV/timestep;
+
+ // draw time tags (only in horizontal !!)
+ int posy=qfmt.height();
+ char *tmp=new char[100];
+ int pos=0;
+ int deltapos=0;
+ formatMillisecs(0,tmp);
+ painter->drawText( 0, posy,tmp);
+ for ( i = timestep; i <= maxV - timestep; i += timestep )
+ {
+ pos = (width()-10) * i / maxV + 5;
+ formatMillisecs(i,tmp);
+ deltapos=qfmt.width(tmp)/2;
+ painter->drawText( pos-deltapos, posy,tmp);
+ }
+
+ pos = width()- 5;
+ formatMillisecs(maxV,tmp);
+ deltapos=qfmt.width(tmp);
+
+ painter->drawText( pos-deltapos, posy,tmp);
+
+}
+
+
+QSize QSliderTime::sizeHint()
+{
+ return QSize(10,fontheight+2);
+}
+
+QSizePolicy QSliderTime::sizePolicy()
+{
+ return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed);
+}
+#include "qslidertime.moc"
diff --git a/kmid/qslidertime.h b/kmid/qslidertime.h
new file mode 100644
index 00000000..4b42d7d9
--- /dev/null
+++ b/kmid/qslidertime.h
@@ -0,0 +1,56 @@
+/**************************************************************************
+
+ kslidertime.h - A widget that displays time tags under a KSlider
+ Copyright (C) 1997,98 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#ifndef _kslidertime_h_
+#define _kslidertime_h_
+
+#include <qslider.h>
+
+class QWidget;
+
+class QSliderTime : public QWidget
+{
+ Q_OBJECT
+private:
+
+ QSlider *kslider;
+
+int fontheight;
+public:
+
+ QSliderTime( QSlider *ksl, QWidget *parent = NULL, const char *name = NULL );
+
+ int getFontHeight(void) {return fontheight;};
+
+ QSize sizeHint();
+ QSizePolicy sizePolicy();
+
+private:
+
+ virtual void paintEvent( QPaintEvent * );
+ void drawTimeMarks(QPainter *painter);
+
+ char *formatMillisecs(int ms,char *tmp);
+};
+
+#endif
diff --git a/kmid/randomlist.cpp b/kmid/randomlist.cpp
new file mode 100644
index 00000000..5ac736a9
--- /dev/null
+++ b/kmid/randomlist.cpp
@@ -0,0 +1,103 @@
+/**************************************************************************
+
+ randomlist.cpp - Some "random functions" :-)
+ Copyright (C) 1997,98 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#include "randomlist.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+#define RAND_UNIFORM (double)rand()/(double)RAND_MAX
+
+int random_discrete(double *distrib,int n)
+{
+ int i=0;
+ double g=0.0;
+ double z=0.0;
+ while ((z==0.0)||(z==1.0)) z=RAND_UNIFORM;
+ while ((g<z)&&(i<n))
+ {
+ g+=distrib[i];
+ i++;
+ };
+ return i-1;
+}
+
+double *generate_discrete_uniform_distrib(int n)
+{
+ double *distrib=new double[n];
+
+ for (int i=0;i<n;i++)
+ distrib[i]=1.0/(double)n;
+
+ return distrib;
+}
+
+void remove_lmn_from_discrete_distrib(int k,double *distrib,int n,int used=0)
+{
+ int i;
+ if (used==0) //guess how many values of the v.a. are strictly positive
+ {
+ for (i=0;i<n;i++) if (distrib[i]>0) used++;
+ };
+ used--; // k will no longer be used :-)
+ if (used==0) return;
+ double piece=distrib[k]/(double)used;
+ distrib[k]=0.0;
+ for (i=0;i<n;i++) if (distrib[i]>0) distrib[i]+=piece;
+}
+
+void show_distrib(double *distrib,int n)
+{
+ printf("(");
+ for (int j=0;j<n;j++) printf("%g,",distrib[j]);
+ printf(")\n");
+}
+
+int *generate_random_list(int n)
+{
+ if (n==0) return NULL;
+ int *list=new int[n];
+ double *distrib=generate_discrete_uniform_distrib(n);
+ int used=n;
+ int x;
+ int i=1;
+ while (used>0)
+ {
+ x=random_discrete(distrib,n);
+ list[x]=i;
+ i++;
+ remove_lmn_from_discrete_distrib(x,distrib,n,used);
+ used--;
+ };
+ delete distrib;
+
+ return list;
+}
+
+int *generate_list(int n)
+{
+ int *list=new int[n];
+ for (int i=0;i<n;i++)
+ list[i]=i+1;
+
+ return list;
+}
diff --git a/kmid/randomlist.h b/kmid/randomlist.h
new file mode 100644
index 00000000..e351aaad
--- /dev/null
+++ b/kmid/randomlist.h
@@ -0,0 +1,34 @@
+/**************************************************************************
+
+ randomlist.h - Some "random functions" :-)
+ Copyright (C) 1997,98 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+
+#ifndef _RANDOMLIST_H
+#define _RANDOMLIST_H
+
+// Returns a list (created with new) of num elements, filled with 1,2,..., num
+// at random places.
+int *generate_random_list(int num);
+
+int *generate_list(int num);
+
+#endif
diff --git a/kmid/rhythmview.cpp b/kmid/rhythmview.cpp
new file mode 100644
index 00000000..2b9136aa
--- /dev/null
+++ b/kmid/rhythmview.cpp
@@ -0,0 +1,108 @@
+/**************************************************************************
+
+ rhythmview.cpp - The RhythmView widget
+ Copyright (C) 1998 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#include "rhythmview.h"
+
+RhythmView::RhythmView (QWidget *parent, const char *name) : QWidget (parent,name)
+{
+ lamps=NULL;
+ setRhythm(4,4);
+}
+
+RhythmView::~RhythmView()
+{
+ if (lamps!=NULL)
+ {
+ for (int i=0;i<num;i++) delete lamps[i];
+ delete [] lamps;
+ lamps=NULL;
+ }
+}
+
+void RhythmView::setRhythm(int numerator,int denominator)
+{
+ int i;
+ if (lamps!=NULL)
+ {
+ for (i=0;i<num;i++) delete lamps[i];
+ delete [] lamps;
+ lamps=NULL;
+ }
+ num=numerator;
+ den=denominator;
+
+ lamps=new KLed *[num];
+ int w=width()/num;
+ int x=0;
+
+ for (i=0;i<num;i++)
+ {
+ lamps[i]=new KLed(Qt::yellow, KLed::Off, KLed::Sunken, KLed::Rectangular, this);
+// lamps[i]->setState(KLed::Off);
+ lamps[i]->setGeometry(x+2,0,w-4,height());
+ lamps[i]->show();
+ x+=w;
+ }
+// lamps[0]->setState(KLed::On);
+
+}
+
+void RhythmView::Beat(int j)
+{
+ if (j>num) setRhythm(j,4); // This is a preventive case
+
+ for (int i=0;i<num;i++)
+ {
+ lamps[i]->off();
+ }
+ lamps[j-1]->on();
+}
+
+void RhythmView::Beat(void)
+{
+
+}
+
+void RhythmView::resizeEvent(QResizeEvent *)
+{
+ int w=width()/num;
+ int x=0;
+
+ for (int i=0;i<num;i++)
+ {
+ lamps[i]->setGeometry(x+2,0,w-4,height());
+ x+=w;
+ }
+}
+
+QSize RhythmView::sizeHint()
+{
+ return QSize(10,10);
+}
+
+QSizePolicy RhythmView::sizePolicy()
+{
+// return QWidget::sizePolicy();
+ return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Minimum);
+}
+
diff --git a/kmid/rhythmview.h b/kmid/rhythmview.h
new file mode 100644
index 00000000..80402fad
--- /dev/null
+++ b/kmid/rhythmview.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+
+ rhythmview.h - The RhythmView widget
+ Copyright (C) 1998 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#ifndef _rhythmview_h_
+#define _rhythmview_h_
+
+#include <qwidget.h>
+#include <kled.h>
+
+class RhythmView : public QWidget
+{
+ int num,den;
+
+ KLed **lamps;
+
+public:
+ RhythmView (QWidget *parent, const char *name);
+ ~RhythmView();
+
+ void setRhythm(int numerator,int denominator);
+
+ void Beat(int i); // Sets the beat number
+
+ void Beat(void); // Just increase the beat
+
+ QSize sizeHint();
+ QSizePolicy sizePolicy();
+
+private:
+ void resizeEvent(QResizeEvent *);
+
+};
+
+#endif
diff --git a/kmid/slman.cpp b/kmid/slman.cpp
new file mode 100644
index 00000000..62acdd2b
--- /dev/null
+++ b/kmid/slman.cpp
@@ -0,0 +1,320 @@
+/**************************************************************************
+
+ slman.cc - SongList Manager, which holds a set of collections (SongLists)
+ Copyright (C) 1997,98 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#include "slman.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "version.h"
+#include <klocale.h>
+
+SLManager::SLManager()
+{
+ list=NULL;
+ ntotal=0;
+ tempsl=NULL;
+ createTemporaryCollection();
+}
+
+SLManager::SLManager(SLManager &src)
+{
+ list=NULL;
+ ntotal=0;
+ tempsl=NULL;
+ SongListNode *srcSL=src.list;
+ SongList *tmpSL;
+ int i;
+ while (srcSL!=NULL)
+ {
+ i=createCollection(srcSL->name);
+ tmpSL=getCollection(i);
+ if (tmpSL!=NULL)
+ tmpSL->copy(*srcSL->SL);
+ srcSL=srcSL->next;
+ }
+ if (src.tempsl!=NULL) tempsl=new SongList(*src.tempsl);
+ else tempsl=NULL;
+}
+
+SLManager::~SLManager()
+{
+ SongListNode *ptr;
+ ptr=list;
+ while (ptr!=NULL)
+ {
+ list=ptr->next;
+ if (ptr->SL!=NULL) delete ptr->SL;
+ if (ptr->name!=NULL) delete ptr->name;
+ delete ptr;
+ ptr=list;
+ }
+ if (tempsl!=NULL) delete tempsl;
+ ntotal=0;
+}
+
+int SLManager::createCollection(const char *name)
+{
+ SongListNode *ptr;
+ if (nameUsed(name))
+ {
+ printf("Name '%s' is already used\n",name);
+ return -1;
+ }
+ if (list==NULL)
+ {
+ list=new SongListNode;
+ list->id=1;
+ ntotal=1;
+ ptr=list;
+ }
+ else
+ {
+ ptr=list;
+ while (ptr->next!=NULL) ptr=ptr->next;
+ ptr->next=new SongListNode;
+ ptr=ptr->next;
+
+ ptr->id= ++ntotal;
+ }
+ ptr->SL=new SongList;
+ ptr->next=NULL;
+ if (name!=NULL)
+ {
+ ptr->name=new char[strlen(name)+1];
+ strcpy(ptr->name,name);
+ }
+ else
+ {
+ ptr->name=getNotUsedName();
+ }
+return ptr->id;
+}
+
+char *SLManager::getNotUsedName(void)
+{
+ char *trythis;
+ trythis=new char[100];
+ strcpy(trythis,"No Name");
+ int tries=1;
+ int success=0;
+ while (!success)
+ {
+ if (nameUsed(trythis)) sprintf(trythis,"No Name - %d",++tries);
+ else
+ success=1;
+ }
+ return trythis;
+}
+
+int SLManager::nameUsed(const char *name)
+{
+ /*
+ SongListNode *ptr=list;
+ int used=0;
+ while ((!used)&&(ptr!=NULL))
+ {
+ if (strcmp(ptr->name,name)==0) used=1;
+ ptr=ptr->next;
+ };
+ return used;
+ */
+ if (getCollection(name)==NULL) return 0;
+ return 1;
+}
+
+void SLManager::deleteCollection(int id)
+{
+ if (list==NULL) return;
+ SongListNode *ptr=list;
+ SongListNode *ptr2;
+ if (id==1) list=list->next;
+ else
+ {
+ ptr2=list;
+ while ((ptr!=NULL)&&(ptr->id!=id))
+ {
+ ptr2=ptr;
+ ptr=ptr->next;
+ }
+ if (ptr==NULL)
+ {
+ printf("Trying to delete a not used id\n");
+ return;
+ }
+ ptr2->next=ptr->next;
+ }
+ ptr2=ptr->next;
+ delete ptr->SL;
+ delete ptr->name;
+ delete ptr;
+ regenerateid(ptr2,id);
+
+}
+
+void SLManager::regenerateid(SongListNode *sl,int id)
+{
+ SongListNode *tmp=sl;
+ int i=id;
+ while (tmp!=NULL)
+ {
+ tmp->id=i++;
+ tmp=tmp->next;
+ }
+ ntotal=i-1;
+}
+
+void SLManager::changeCollectionName(int id,const char *newname)
+{
+ if (id<1) return;
+ if (nameUsed(newname))
+ {
+ printf("Cannot change name, '%s' is already used\n",newname);
+ return;
+ }
+ SongListNode *ptr=list;
+ while ((ptr!=NULL)&&(ptr->id!=id)) ptr=ptr->next;
+ if (ptr==NULL) return;
+
+ delete ptr->name;
+ ptr->name=new char[strlen(newname)+1];
+ strcpy(ptr->name,newname);
+}
+
+SongList *SLManager::getCollection(int id)
+{
+ if (id==0) return tempsl;
+
+ SongListNode *ptr=list;
+ while ((ptr!=NULL)&&(ptr->id!=id)) ptr=ptr->next;
+
+ if (ptr==NULL) return NULL;
+ return ptr->SL;
+}
+
+SongList *SLManager::getCollection(const char *name)
+{
+ SongListNode *ptr=list;
+ while ((ptr!=NULL)&&(strcmp(ptr->name,name)!=0)) ptr=ptr->next;
+
+ if (ptr==NULL) return NULL;
+ return ptr->SL;
+}
+
+const char *SLManager::getCollectionName(int id)
+{
+ if (id==0) return I18N_NOOP("Temporary Collection");
+ SongListNode *ptr=list;
+ while ((ptr!=NULL)&&(ptr->id!=id)) ptr=ptr->next;
+
+ if (ptr==NULL) return NULL;
+ return ptr->name;
+}
+
+void SLManager::loadConfig(const char *filename)
+{
+#ifdef GENERAL_DEBUG_MESSAGES
+ printf("Loading collections\n");
+#endif
+ FILE *fh=fopen(filename,"rt");
+ if (fh==NULL)
+ {
+ printf("Collections cannot be loaded\n(File %s doesn't exist or can't be opened)\n",filename);
+ return;
+ }
+ char s[300];
+ SongList *sl=NULL;
+ int activeid=0;
+ while (!feof(fh))
+ {
+ s[0] = 0;
+ fgets(s,299,fh);
+ if ((strlen(s)>0)&&(s[strlen(s)-1]==10)) s[strlen(s)-1]=0;
+ switch (s[0])
+ {
+ case (0) : break;
+ case (10) : break;
+ case ('=') :
+ {
+ if (sl!=NULL) sl->setActiveSong(activeid);
+ int id=createCollection(&s[1]);
+ sl=getCollection(id);
+ fgets(s,299,fh);
+ activeid=atoi(s);
+ }
+ break;
+ default :
+ {
+ if (sl!=NULL) sl->AddSong((const char *)s);
+ }
+ }
+ }
+ if (sl!=NULL) sl->setActiveSong(activeid);
+
+ fclose(fh);
+}
+
+void SLManager::saveConfig(const char *filename)
+{
+ SongListNode *ptr=list;
+ FILE *fh=fopen(filename,"wt");
+ if (fh==NULL)
+ {
+ printf("Collections couldn't be saved\n");
+ return;
+ }
+ char s[FILENAME_MAX];
+ SongList *sl;
+ while (ptr!=NULL)
+ {
+ sprintf(s,"=%s\n",ptr->name);
+ fputs(s,fh);
+
+ sl=ptr->SL;
+ sprintf(s,"%d\n",sl->getActiveSongID());
+ fputs(s,fh);
+
+ sl->iteratorStart();
+ while (!sl->iteratorAtEnd())
+ {
+ sprintf(s,"%s\n",sl->getIteratorName());
+ fputs(s,fh);
+ sl->iteratorNext();
+ }
+ // ptr->SL->saveList(fh);
+ fputs("\n",fh);
+
+ ptr=ptr->next;
+ }
+ fclose(fh);
+
+}
+
+SongList *SLManager::createTemporaryCollection(void)
+{
+ if (tempsl==NULL)
+ tempsl=new SongList();
+ else
+ tempsl->clean();
+
+ return tempsl;
+}
diff --git a/kmid/slman.h b/kmid/slman.h
new file mode 100644
index 00000000..3013f242
--- /dev/null
+++ b/kmid/slman.h
@@ -0,0 +1,74 @@
+/* slman.h - SongList Manager, which holds a set of collections (SongLists)
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#ifndef SLMAN_H
+#define SLMAN_H
+
+#include "songlist.h"
+
+class SLManager
+{
+protected:
+int ntotal;
+
+struct SongListNode
+ {
+ int id;
+ char *name;
+ SongList *SL;
+
+ SongListNode *next;
+ };
+SongListNode *list;
+SongList *tempsl;
+
+char *getNotUsedName(void);
+int nameUsed(const char *name);
+void regenerateid(SongListNode *sl,int id);
+
+public:
+SLManager();
+SLManager(SLManager &src); // Copy constructor
+~SLManager();
+
+int createCollection(const char *name=NULL); // Returns the id associated to the
+ // new collection
+ // -1 if name already is used and Collection was
+ // not created
+
+void deleteCollection(int id);
+void changeCollectionName(int id, const char *newname);
+
+SongList *getCollection(int id);
+SongList *getCollection(const char *name);
+const char *getCollectionName(int id);
+
+int numberOfCollections(void) {return ntotal;};
+
+void loadConfig(const char *filename);
+void saveConfig(const char *filename);
+
+SongList *createTemporaryCollection(void);
+SongList *getTemporaryCollection(void) {return tempsl;};
+
+};
+
+#endif
diff --git a/kmid/songlist.cpp b/kmid/songlist.cpp
new file mode 100644
index 00000000..8d98600c
--- /dev/null
+++ b/kmid/songlist.cpp
@@ -0,0 +1,247 @@
+/**************************************************************************
+
+ songlist.cc - class SongList, which holds a list of songs (collection)
+ Copyright (C) 1997,98 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#include "songlist.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+SongList::SongList(void)
+{
+ list=NULL;
+ last=NULL;
+ active=NULL;
+ ntotal=0;
+}
+
+SongList::SongList(SongList &src)
+{
+ list=last=active=NULL;
+ ntotal=0;
+ src.iteratorStart();
+ while (!src.iteratorAtEnd())
+ {
+ AddSong(src.getIteratorName());
+ src.iteratorNext();
+ }
+ if (src.active!=NULL) active=getSongid(src.active->id);
+}
+
+SongList::~SongList()
+{
+ clean();
+ Song *ptr=list;
+ active=last=NULL;
+ ntotal=0;
+
+ while (ptr!=NULL)
+ {
+ list=ptr->next;
+ delete ptr->name;
+ delete ptr;
+ ptr=list;
+ }
+
+}
+
+SongList::Song *SongList::getSongid(int id)
+{
+ Song *ptr=list;
+ while ((ptr!=NULL)&&(ptr->id!=id))
+ ptr=ptr->next;
+
+ return ptr;
+}
+
+int SongList::AddSong(const char *song)
+{
+ if (!song) return 0;
+
+ if (last==NULL)
+ {
+ last=new Song;
+ list=last;
+ }
+ else
+ {
+ last->next=new Song;
+ last=last->next;
+ }
+ last->name=new char[strlen(song)+1];
+ strcpy(last->name,song);
+ last->id= ++ntotal;
+ last->next=NULL;
+ if (active==NULL) active=last;
+ return last->id;
+}
+
+void SongList::DelSong(int id)
+{
+ Song *ptr;
+
+ if (list==NULL) return;
+ if (id==1)
+ {
+ if (last->id==1)
+ {
+ list=last=active=NULL;
+ ntotal=0;
+ }
+ else
+ {
+ ptr=list;
+ if (active->id==1) active=list->next;
+ list=list->next;
+ delete ptr->name;
+ delete ptr;
+ ntotal--;
+
+ regenerateid(list,1);
+ }
+ return;
+ }
+ Song *ptr_prev=getSongid(id-1);
+ ptr=ptr_prev->next;
+ if (last->id==id) last=ptr_prev;
+ if (active->id==id)
+ if (active->next!=NULL) active=active->next;
+ else active=ptr_prev;
+
+ ntotal--;
+ ptr_prev->next=ptr->next;
+ delete ptr->name;
+ delete ptr;
+ regenerateid(ptr_prev->next,id);
+
+}
+
+void SongList::regenerateid(Song *song,int id)
+{
+ Song *tmp=song;
+ int i=id;
+ while (tmp!=NULL)
+ {
+ tmp->id=i++;
+ tmp=tmp->next;
+ }
+ ntotal=i-1;
+}
+
+void SongList::setActiveSong(int id)
+{
+ Song *tmp=getSongid(id);
+ if (tmp!=NULL) active=tmp;
+}
+
+char *SongList::getName(int id)
+{
+ Song *tmp=getSongid(id);
+ if (tmp!=NULL) return tmp->name;
+ return NULL;
+}
+
+/*
+void SongList::saveList(FILE *fh)
+{
+ Song *ptr=list;
+ while (ptr!=NULL)
+ {
+ fputs(fh,ptr->name);
+ ptr=ptr->next;
+ }
+}
+*/
+
+void SongList::iteratorStart(void)
+{
+ it=list;
+}
+
+void SongList::iteratorNext(void)
+{
+ if (it!=NULL) it=it->next;
+}
+
+int SongList::getIteratorID(void)
+{
+ if (it==NULL) return -1;
+ return it->id;
+}
+
+char *SongList::getIteratorName(void)
+{
+ if (it==NULL) return NULL;
+ return it->name;
+}
+
+
+void SongList::clean(void)
+{
+ Song *tmp=list;
+ active=last=NULL;
+ ntotal=0;
+
+ while (tmp!=NULL)
+ {
+ list=tmp->next;
+ delete [] tmp->name;
+ delete tmp;
+ tmp=list;
+ }
+}
+
+void SongList::copy(SongList &src)
+{
+ clean();
+ src.iteratorStart();
+ while (!src.iteratorAtEnd())
+ {
+ AddSong(src.getIteratorName());
+ src.iteratorNext();
+ }
+ if (src.active!=NULL) active=getSongid(src.active->id);
+}
+
+int SongList::next(void)
+{
+ if (list==NULL) {active=NULL;return 0;};
+ if (active!=NULL) active=active->next;
+ if (active==NULL)
+ {
+ Song *tmp=list;
+ while (tmp->next!=NULL) tmp=tmp->next;
+ active=tmp;
+ return 0;
+ }
+ return 1;
+}
+
+
+void SongList::previous(void)
+{
+ if (list==NULL) {active=NULL;return;};
+ Song *tmp=list;
+ while ((tmp->next!=NULL)&&(tmp->next->id!=active->id)) tmp=tmp->next;
+ if (tmp->next==NULL) {active=list;return;};
+ active=tmp;
+}
diff --git a/kmid/songlist.h b/kmid/songlist.h
new file mode 100644
index 00000000..85203c46
--- /dev/null
+++ b/kmid/songlist.h
@@ -0,0 +1,84 @@
+/* songlist.h - class SongList, which holds a list of songs (collection)
+ Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Send comments and bug fixes to larrosa@kde.org
+ or to Antonio Larrosa, Rio Arnoya, 10 5B, 29006 Malaga, Spain
+
+***************************************************************************/
+#ifndef SONGLIST_H
+#define SONGLIST_H
+
+#include <stdio.h>
+
+class SongList
+{
+protected:
+int ntotal;
+
+struct Song
+{
+ int id;
+ char *name; // complete path and file name
+
+ Song *next;
+};
+
+Song *list;
+Song *last;
+Song *active;
+
+Song *it; // Iterator, just a helper variable to make easy (and fast) reading
+ // all the list
+
+Song *getSongid(int id);
+
+void regenerateid(Song *song,int id);
+
+public:
+SongList(void);
+SongList(SongList &src); // Copy constructor
+~SongList();
+
+int AddSong(const char *song); // Returns the id number assigned to the song
+void DelSong(int id);
+
+int NumberOfSongs(void) { return ntotal; };
+
+void setActiveSong(int id);
+int getActiveSongID(void) {return ((active!=NULL)? (active->id ):(-1)); };
+char *getActiveSongName(void)
+ {
+ return ((active!=NULL)? (active->name):((char *)NULL));
+ };
+
+char *getName(int id); // Returns the name of the song with id id
+
+void previous(void);
+int next(void); // returns 1 if evrything is ok, and 0 if it was the last element
+ // (but leaves active the last element instead of NULL)
+
+void iteratorStart(void);
+void iteratorNext(void);
+int iteratorAtEnd (void) {return (it==NULL);};
+int getIteratorID(void);
+char *getIteratorName(void);
+
+void clean(void); // Clean this list
+void copy(SongList &src); // Makes this object a copy of src (really copied)
+};
+
+#endif
diff --git a/kmid/std.o3 b/kmid/std.o3
new file mode 100644
index 00000000..e58311da
--- /dev/null
+++ b/kmid/std.o3
Binary files differ
diff --git a/kmid/std.sb b/kmid/std.sb
new file mode 100644
index 00000000..4fe2d0be
--- /dev/null
+++ b/kmid/std.sb
Binary files differ
diff --git a/kmid/version.h b/kmid/version.h
new file mode 100644
index 00000000..be282fff
--- /dev/null
+++ b/kmid/version.h
@@ -0,0 +1,48 @@
+#ifndef _VERSION_H
+
+
+#define VERSION_NUM 2.0
+#define VERSION_SHORTTXT "2.0"
+#define VERSION_TXT "KMid 2.0"
+
+// Undefine WORKING for release versions
+//#define WORKING
+
+// I want to keep some special variables defined at home for personal
+// adjustments
+//#define AT_HOME
+
+
+// The next definition makes kmid display some general debug messages
+// that may be helpful to locate any bug, but without making lots of
+// messages. Please when reporting a bug, be sure that this is set and
+// send me the output.
+//#define GENERAL_DEBUG_MESSAGES
+
+// Define MODE_DEMO_ONLYVISUAL for kmid to work on systems with no soundcard,
+// or when it is not well configured. You will only get visual feedback, that
+// is, you will see the letter changing of colors, but no music. Really, you
+// would get music if it is possible, but by defining this, many synchronization
+// routines will be turned off.
+//#define MODE_DEMO_ONLYVISUAL
+
+
+// And now, DEBUG messages :
+#ifdef WORKING
+#define KMidDEBUG
+//#define KDISPTEXTDEBUG
+//#define COLLECTDLGDEBUG
+//#define DEVICEMANDEBUG
+//#define FMOUTDEBUG
+//#define GUSOUTDEBUG
+//#define MIDFILEDEBUG
+//#define MIDIOUTDEBUG
+//#define MIDISTATDEBUG
+//#define MIDIMAPPERDEBUG
+//#define SYNTHOUTDEBUG
+//#define VOICEMANDEBUG
+//#define PLAYERDEBUG
+//#define TRACKDEBUG
+#endif
+
+#endif
diff --git a/kmid/x-karaoke.desktop b/kmid/x-karaoke.desktop
new file mode 100644
index 00000000..2784f27a
--- /dev/null
+++ b/kmid/x-karaoke.desktop
@@ -0,0 +1,61 @@
+[Desktop Entry]
+Type=MimeType
+MimeType=audio/x-karaoke
+Icon=sound
+Patterns=*.kar;*.KAR;
+Comment=Karaoke File
+Comment[ar]=ملف Karaoke
+Comment[bg]=Файл на Karaoke
+Comment[bn]=কারাওকে ফাইল
+Comment[br]=Restr Karaoke
+Comment[bs]=Karaoke datoteka
+Comment[ca]=Fitxer karaoke
+Comment[cs]=Karaoke soubor
+Comment[cy]=Ffeil Karaoke
+Comment[da]=Karaoke-fil
+Comment[de]=Karaoke-Datei
+Comment[el]=Αρχείο Karaoke
+Comment[eo]=Karaoko-dosiero
+Comment[es]=Archivo Karaoke
+Comment[et]=Karaoke fail
+Comment[eu]=Karaoke fitxategia
+Comment[fa]=پروندۀ Karaoke
+Comment[fi]=Karaoketiedosto
+Comment[fr]=Fichier de karaoké
+Comment[ga]=Comhad Karaoke
+Comment[gl]=Ficheiro Karaoke
+Comment[he]=קובץ קריוקי
+Comment[hi]=कराओके फ़ाइल
+Comment[hu]=Karaoke-fájl
+Comment[is]=Karaoke skrá
+Comment[ja]=カラオケファイル
+Comment[kk]=Караоке файлы
+Comment[km]=ឯកសារ​ខារ៉ាអូខេ
+Comment[ko]=노래방 파일
+Comment[lt]=Karaoke byla
+Comment[mk]=Karaoke датотека
+Comment[nb]=Karaokefil
+Comment[nds]=Karaoke-Datei
+Comment[ne]=कारावके फाइल
+Comment[nl]=Karaoke-bestand
+Comment[nn]=Karaokefil
+Comment[pl]=Plik Karaoke
+Comment[pt]=Ficheiro de Karaoke
+Comment[pt_BR]=Arquivo Karaokê
+Comment[ro]=Fişier karaoke
+Comment[ru]=Файл караоке
+Comment[sk]=Karaoke súbor
+Comment[sl]=Datoteka Karaoke
+Comment[sr]=Караоке фајл
+Comment[sr@Latn]=Karaoke fajl
+Comment[sv]=Karaoke-fil
+Comment[ta]=கரோக்கி கோப்பு
+Comment[tg]=Файли Karaoke
+Comment[th]=แฟ้มคาราโอเกะ
+Comment[tr]=Karaoke Dosyası
+Comment[uk]=Файл Karaoke
+Comment[uz]=Karaoki fayli
+Comment[uz@cyrillic]=Караоки файли
+Comment[zh_CN]=卡拉 OK 文件
+Comment[zh_HK]=卡拉OK 檔案
+Comment[zh_TW]=卡拉OK檔案
diff --git a/kmix/AUTHORS b/kmix/AUTHORS
new file mode 100644
index 00000000..8f72f47d
--- /dev/null
+++ b/kmix/AUTHORS
@@ -0,0 +1,11 @@
+kmix
+Written by Christian Esken (esken@kde.org)
+and Stefan Schimanski (1Stein@gmx.de)
+SGI port done Paul Kendall (paul@orion.co.nz)
+Fixes for FreeBSD Sebestyen Zoltan (szoli@digo.inf.elte.hu)
+Solaris fixes Faraut Jean-Louis <jlf@essi.fr>
+ALSA port Nick Lopez <kimo_sabe@usa.net>
+ALSA 0.9.x port Helio Chissini de Castro <helio@conectiva.com>
+HP-UX port Helge Deller <deller@gmx.de>
+
+The package is maintained by Christian Esken (esken@kde.org)
diff --git a/kmix/ChangeLog b/kmix/ChangeLog
new file mode 100644
index 00000000..df5588b3
--- /dev/null
+++ b/kmix/ChangeLog
@@ -0,0 +1,10 @@
+V1.90
+Version shipped with KDE3.1
+
+V1.91
+- Multiple soundcards on ALSA are now supported
+- Mixer device names are now shown with a vertical label (saves space and looks nice)
+- MixerDevice categories. Allows to distribute devices on "New Mixer Tab...". It is used
+ in the panel applet to show only important devices initally.
+- Much nicer "New Mixer Tab..." dialog
+
diff --git a/kmix/KMixApp.cpp b/kmix/KMixApp.cpp
new file mode 100644
index 00000000..74b84f43
--- /dev/null
+++ b/kmix/KMixApp.cpp
@@ -0,0 +1,71 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ * Copyright (C) 2000 Stefan Schimanski <schimmi@kde.org>
+ * Copyright (C) 2001 Preston Brown <pbrown@kde.org>
+ * Copyright (C) 2003 Sven Leiber <s.leiber@web.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "KMixApp.h"
+#include "kmix.h"
+#include <kdebug.h>
+
+
+KMixApp::KMixApp()
+ : KUniqueApplication(), m_kmix( 0 )
+{
+}
+
+
+KMixApp::~KMixApp()
+{
+ delete m_kmix;
+}
+
+
+int
+KMixApp::newInstance()
+{
+ if ( m_kmix )
+ {
+ m_kmix->show();
+ }
+ else
+ {
+ m_kmix = new KMixWindow;
+ connect(this, SIGNAL(stopUpdatesOnVisibility()), m_kmix, SLOT(stopVisibilityUpdates()));
+ if ( isRestored() && KMainWindow::canBeRestored(0) )
+ {
+ m_kmix->restore(0, FALSE);
+ }
+ }
+
+ return 0;
+}
+
+
+void
+KMixApp::quitExtended()
+{
+ // This method is here for quiting from the dock icon: When directly calling
+ // quit(), the main window will be hidden before saving the configuration.
+ // isVisible() would return on quit always false (which would be bad).
+ emit stopUpdatesOnVisibility();
+ quit();
+}
+
+#include "KMixApp.moc"
diff --git a/kmix/KMixApp.h b/kmix/KMixApp.h
new file mode 100644
index 00000000..79e4fc4e
--- /dev/null
+++ b/kmix/KMixApp.h
@@ -0,0 +1,26 @@
+#ifndef KMixApp_h
+#define KMixApp_h
+
+#include <kuniqueapplication.h>
+
+class KMixWindow;
+
+class KMixApp : public KUniqueApplication
+{
+Q_OBJECT
+ public:
+ KMixApp();
+ ~KMixApp();
+ int newInstance ();
+
+ public slots:
+ void quitExtended(); // For a hack on visibility()
+
+ signals:
+ void stopUpdatesOnVisibility();
+
+ private:
+ KMixWindow *m_kmix;
+};
+
+#endif
diff --git a/kmix/Makefile.am b/kmix/Makefile.am
new file mode 100644
index 00000000..307c8168
--- /dev/null
+++ b/kmix/Makefile.am
@@ -0,0 +1,63 @@
+SUBDIRS = pics
+INCLUDES= $(all_includes)
+
+bin_PROGRAMS =
+lib_LTLIBRARIES =
+kdeinit_LTLIBRARIES = kmix.la kmixctrl.la
+kde_module_LTLIBRARIES = kmix_panelapplet.la
+
+
+noinst_HEADERS = kmix.h kmixdockwidget.h kmixprefdlg.h kmixerwidget.h \
+ viewbase.h viewoutput.h viewinput.h viewsliders.h viewswitches.h viewsurround.h viewdockareapopup.h viewgrid.h \
+ mixdevicewidget.h mdwslider.h mdwswitch.h mdwenum.h \
+ mixer.h mixset.h mixdevice.h mixer_backend.h volume.h kledbutton.h version.h kmixtoolbox.h \
+ ksmallslider.h kmixapplet.h mixerIface.h verticaltext.h \
+ KMixApp.h dialogviewconfiguration.h kmixtoolbox.h mixertoolbox.h dialogselectmaster.h
+
+
+METASOURCES = AUTO
+
+kmix_la_SOURCES = main.cpp kmix.cpp kmixdockwidget.cpp kmixprefdlg.cpp \
+ viewbase.cpp viewoutput.cpp viewinput.cpp viewswitches.cpp viewsurround.cpp viewdockareapopup.cpp \
+ viewsliders.cpp viewgrid.cpp \
+ mixdevicewidget.cpp mdwslider.cpp mdwswitch.cpp mdwenum.cpp \
+ kmixerwidget.cpp mixer.cpp mixset.cpp mixdevice.cpp mixer_backend.cpp ksmallslider.cpp \
+ volume.cpp kledbutton.cpp verticaltext.cpp mixerIface.skel \
+ kmixtoolbox.cpp mixertoolbox.cpp dialogviewconfiguration.cpp KMixApp.cpp dialogselectmaster.cpp
+
+kmix_la_LIBADD = $(LIB_KDEUI) $(LIB_KUTILS) $(LIBALIB) $(LIBOSSAUDIO) $(LIBASOUND)
+kmix_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) -avoid-version
+
+kmixctrl_la_SOURCES = kmixctrl.cpp mixer.cpp mixset.cpp mixdevice.cpp volume.cpp mixerIface.skel \
+ mixertoolbox.cpp mixer_backend.cpp
+
+kmixctrl_la_LIBADD = $(LIB_KDECORE) $(LIBALIB) $(LIBOSSAUDIO) $(LIBASOUND)
+kmixctrl_la_LDFLAGS = $(all_libraries) -module -avoid-version
+
+kmix_panelapplet_la_SOURCES = kmixapplet.cpp \
+ viewbase.cpp viewapplet.cpp \
+ mixdevicewidget.cpp mdwslider.cpp \
+ mixer.cpp mixset.cpp mixdevice.cpp mixer_backend.cpp ksmallslider.cpp volume.cpp kledbutton.cpp \
+ verticaltext.cpp mixerIface.skel colorwidget.ui dialogviewconfiguration.cpp \
+ kmixtoolbox.cpp mixertoolbox.cpp dialogselectmaster.cpp
+
+kmix_panelapplet_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module
+kmix_panelapplet_la_LIBADD = $(LIB_KDEUI) $(LIB_KUTILS) $(LIBALIB) $(LIBOSSAUDIO) $(LIBASOUND)
+
+xdg_apps_DATA = kmix.desktop
+
+rcdir = $(kde_datadir)/kmix
+rc_DATA = kmixui.rc
+
+autostart_DATA = restore_kmix_volumes.desktop
+autostartdir = $(datadir)/autostart
+
+lnkdir = $(kde_datadir)/kicker/applets
+lnk_DATA = kmixapplet.desktop
+
+servicesdir = $(kde_servicesdir)
+services_DATA = kmixctrl_restore.desktop
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/kmix.pot
+
diff --git a/kmix/TODO b/kmix/TODO
new file mode 100644
index 00000000..c48ea54c
--- /dev/null
+++ b/kmix/TODO
@@ -0,0 +1,47 @@
+TODO list
+
+Bug2 in KMix2.1pre:
+- Keys not saved/restored (pending)
+- DockApplet shows "show/hide menubar" (DONE)
+- "reversed" in panelapplet is broken. Remove it for now (DONE)
+- Initial paint of sliders in KMixApplet is broken. (DONE)
+- PanelApplet width wrong. (DONE)
+
+IMPORTANT:
+1) Get Switches working
+MUTE-LED's : Read: OK, Click: OK , Saved/Restored: no (was not in KMix2.0 and is shifted)
+Record-LED's: Read: OK, Click: OK , Saved/Restored: yes
+Switches : Read: OK, Click: OK , Saved/Restored: yes
+Dockarea : Read: OK, Click: OK , Saved/Restored: n/a (its a view, not HW)
+
+2a) Splitted sliders and balance
+OK
+Splitted sliders: Read: OK, Click: OK, Drag: OK, Wheel: OK
+Balance slider : OK (works like in KMix2.0)
+
+3) Mouse wheel
+OK
+
+4) Make Volume Tip work (is currently always "0%")
+OK
+
+5) Switches MUST be restored properly
+OK
+
+6) kmixapplet restoring and working
+OK
+
+7) Keys are not saved
+Pending
+
+
+14.6.2004 Christian Esken
+I believe Mixer_IRIX is broken.
+This is due to the old "writeVolumeToHW" and "readVolumeFromHW" interface.
+
+14 december 2002 - Helio Chissini de Castro
+- Figure out devices like SBLive with external Live Drives and their multiple in/outs. As a sample, using headphone output from live drive, mute switch not work as they must, but we need mutting the headphone lfe and center channel to really mute headphone output.
+
+- Introduce a new widget to enable route control in pro's ( Turtle ) and pro like ( Audigy, Live ) cards.
+
+
diff --git a/kmix/TestCases b/kmix/TestCases
new file mode 100644
index 00000000..edca34e0
--- /dev/null
+++ b/kmix/TestCases
@@ -0,0 +1,36 @@
+These are the recommended test cases for KMix.
+They should be tested before a new KDE version is being shipped.
+
+01: Basic: Start KMix with existing profile (kmixrc)
+02: Basic: Close KMix -> Dock
+03: Basic: Close KMix -> Exit
+04: Show/Hide Labels
+05: Show/Hide Tickmarks
+06: New Mixer Tab ("distribute" and "no distribute")
+07: System tray volume control
+08: Start KMix with no Mixer Hardware/Drivers available
+09: Basic: Start KMix with NO existing profile (kmixrc)
+10: Basic: KMix KControl module
+11: kmixctrl --restore (with existing .kmixctrlrc) must restore volumes
+12: kmixctrl --restore (without existing .kmixctrlrc) must NOT change volumes
+
+
+-----------+----+----+----+----+----+----+----+----+----+----+----+----+
+TestCase> | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
+Version | | | | | | | | | | | | |
+-----------+----+----+----+----+----+----+----+----+----+----+----+----+
+CVS20030502| OK | OK | OK | OK | OK | OK | OK | | | | | |
+-----------+----+----+----+----+----+----+----+ | +----+ | |
+CVS20031102| | | | | | na | | | | OK | | |
+-----------+----+----+----+----+----+----+----+ | +----+ | |
+KDE3.2 | OK | | | | | na | | | | | | |
+-----------+----+----+----+----+----+----+----+----+----+----+----+----+
+KDE3.4 | OK | OK | OK | OK | OK | na | OK | OK | OK | na | OK | OK |
+-----------+----+----+----+----+----+----+----+----+----+----+----+----+
+
+Test case results:
+OK : Fully OK
+ : Not tested
+xx : Failure
+na : not applicable (Feature discontinued)
+
diff --git a/kmix/colorwidget.ui b/kmix/colorwidget.ui
new file mode 100644
index 00000000..56f1d60e
--- /dev/null
+++ b/kmix/colorwidget.ui
@@ -0,0 +1,276 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>ColorWidget</class>
+<author>Stefan Schimanski &lt;1Stein@gmx.de&gt;</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ColorWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>272</width>
+ <height>305</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>customColors</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Use custom colors</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>activeColors</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>Active</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KColorButton" row="2" column="1">
+ <property name="name">
+ <cstring>activeBack</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel3</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Silent:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>activeLow</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="1">
+ <property name="name">
+ <cstring>activeLow</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="0" column="1">
+ <property name="name">
+ <cstring>activeHigh</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>labelLoad</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Loud:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>activeHigh</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel4</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Background:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>activeBack</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>mutedColors</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>Muted</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel6</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Lou&amp;d:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mutedHigh</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel8</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Backgrou&amp;nd:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mutedBack</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel7</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Silen&amp;t:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mutedLow</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="0" column="1">
+ <property name="name">
+ <cstring>mutedHigh</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="1">
+ <property name="name">
+ <cstring>mutedLow</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="2" column="1">
+ <property name="name">
+ <cstring>mutedBack</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>customColors</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>activeColors</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>customColors</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mutedColors</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>customColors</tabstop>
+ <tabstop>activeHigh</tabstop>
+ <tabstop>activeLow</tabstop>
+ <tabstop>activeBack</tabstop>
+ <tabstop>mutedHigh</tabstop>
+ <tabstop>mutedLow</tabstop>
+ <tabstop>mutedBack</tabstop>
+</tabstops>
+<includes>
+ <include location="global" impldecl="in declaration">klocale.h</include>
+ <include location="global" impldecl="in declaration">kseparator.h</include>
+</includes>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kmix/configure.in.in b/kmix/configure.in.in
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kmix/configure.in.in
diff --git a/kmix/dialogselectmaster.cpp b/kmix/dialogselectmaster.cpp
new file mode 100644
index 00000000..44f38626
--- /dev/null
+++ b/kmix/dialogselectmaster.cpp
@@ -0,0 +1,197 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <qbuttongroup.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qptrlist.h>
+#include <qradiobutton.h>
+#include <qscrollview.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+
+#include <kcombobox.h>
+#include <kdebug.h>
+#include <kdialogbase.h>
+#include <klocale.h>
+
+#include "dialogselectmaster.h"
+#include "mixdevice.h"
+#include "mixer.h"
+
+DialogSelectMaster::DialogSelectMaster( Mixer *mixer )
+ : KDialogBase( Plain, i18n( "Select Master Channel" ), Ok|Cancel, Ok )
+{
+ _layout = 0;
+ m_vboxForScrollView = 0;
+ createWidgets(mixer); // Open with Mixer Hardware #0
+
+}
+
+DialogSelectMaster::~DialogSelectMaster()
+{
+ delete _layout;
+ delete m_vboxForScrollView;
+}
+
+/**
+ * Create basic widgets of the Dialog.
+ */
+void DialogSelectMaster::createWidgets(Mixer *ptr_mixer)
+{
+ QFrame *m_mainFrame = plainPage();
+ _layout = new QVBoxLayout(m_mainFrame,0,-1, "_layout" );
+
+ if ( Mixer::mixers().count() > 1 ) {
+ //kdDebug(67100) << "DialogSelectMaster::createPage count()>1" << "\n";
+ // More than one Mixer => show Combo-Box to select Mixer
+ // Mixer widget line
+ QHBoxLayout* mixerNameLayout = new QHBoxLayout( _layout );
+ //widgetsLayout->setStretchFactor( mixerNameLayout, 0 );
+ //QSizePolicy qsp( QSizePolicy::Ignored, QSizePolicy::Maximum);
+ //mixerNameLayout->setSizePolicy(qsp);
+ mixerNameLayout->setSpacing(KDialog::spacingHint());
+
+ QLabel *qlbl = new QLabel( i18n("Current Mixer"), m_mainFrame );
+ mixerNameLayout->addWidget(qlbl);
+ qlbl->setFixedHeight(qlbl->sizeHint().height());
+
+ m_cMixer = new KComboBox( FALSE, m_mainFrame, "mixerCombo" );
+ m_cMixer->setFixedHeight(m_cMixer->sizeHint().height());
+ connect( m_cMixer, SIGNAL( activated( int ) ), this, SLOT( createPageByID( int ) ) );
+
+ //int id=1;
+ for ( Mixer *mixer = Mixer::mixers().first(); mixer !=0; mixer = Mixer::mixers().next() ) {
+ m_cMixer->insertItem( mixer->mixerName() );
+ if ( ptr_mixer == mixer ) {
+ // Make the current Mixer the current item in the ComboBos
+ m_cMixer->setCurrentItem( m_cMixer->count()-1 );
+ }
+ //id++;
+ } // end for all_Mixers
+
+ QToolTip::add( m_cMixer, i18n("Current mixer" ) );
+ mixerNameLayout->addWidget(m_cMixer);
+
+ } // end if (more_than_1_Mixer)
+
+ QLabel *qlbl = new QLabel( i18n("Select the channel representing the master volume:"), m_mainFrame );
+ _layout->addWidget(qlbl);
+
+ m_scrollableChannelSelector = new QScrollView(m_mainFrame, "scrollableChannelSelector");
+ m_scrollableChannelSelector->viewport()->setBackgroundMode(Qt::PaletteBackground);
+ _layout->add(m_scrollableChannelSelector);
+
+ m_buttonGroupForScrollView = new QButtonGroup(this); // invisible QButtonGroup
+ m_buttonGroupForScrollView->hide();
+
+ createPage(ptr_mixer);
+ connect( this, SIGNAL(okClicked()) , this, SLOT(apply()) );
+}
+
+/**
+ * Create RadioButton's for the Mixer with number 'mixerId'.
+ * @par mixerId The Mixer, for which the RadioButton's should be created.
+ */
+void DialogSelectMaster::createPageByID(int mixerId)
+{
+ //kdDebug(67100) << "DialogSelectMaster::createPage()" << endl;
+ Mixer *mixer = Mixer::mixers().at(mixerId);
+ if ( mixer == 0 ) {
+ kdError(67100) << "DialogSelectMaster::createPage(): Invalid Mixer (mixerID=" << mixerId << ")" << endl;
+ return; // can not happen
+ }
+ createPage(mixer);
+}
+
+/**
+ * Create RadioButton's for the Mixer with number 'mixerId'.
+ * @par mixerId The Mixer, for which the RadioButton's should be created.
+ */
+void DialogSelectMaster::createPage(Mixer* mixer)
+{
+
+ /** --- Reset page -----------------------------------------------
+ * In case the user selected a new Mixer via m_cMixer, we need
+ * to remove the stuff created on the last call.
+ */
+ // delete the VBox. This should automatically remove all contained QRadioButton's.
+ delete m_vboxForScrollView;
+ m_mixerPKs.clear();
+ /** Reset page end -------------------------------------------------- */
+
+ m_vboxForScrollView = new QVBox(m_scrollableChannelSelector->viewport());
+ m_scrollableChannelSelector->addChild(m_vboxForScrollView);
+
+ QString masterKey = "----noMaster---"; // Use a non-matching name as default
+ MixDevice* master = mixer->masterDevice();
+ if ( master != 0 ) masterKey = master->getPK();
+
+ const MixSet& mixset = mixer->getMixSet();
+ MixSet& mset = const_cast<MixSet&>(mixset);
+ for( MixDevice* md = mset.first(); md != 0; md = mset.next() )
+ {
+ // Create a RadioButton for each MixDevice (excluding Enum's)
+ if ( ! md->isEnum() && ! md->isSwitch() ) {
+ //kdDebug(67100) << "DialogSelectMaster::createPage() mset append qrb" << endl;
+ QString mdName = md->name();
+ mdName.replace('&', "&&"); // Quoting the '&' needed, to prevent QRadioButton creating an accelerator
+ QRadioButton* qrb = new QRadioButton( mdName, m_vboxForScrollView);
+ m_buttonGroupForScrollView->insert(qrb); //(qrb, md->num());
+ //_qEnabledCB.append(qrb);
+ m_mixerPKs.push_back(md->getPK());
+ if ( md->getPK() == masterKey ) {
+ qrb->setChecked(true); // preselect the current master
+ }
+ else {
+ qrb->setChecked(false);
+ }
+ }
+ }
+
+ m_vboxForScrollView->show(); // show() is neccesary starting with the second call to createPage()
+}
+
+
+void DialogSelectMaster::apply()
+{
+ int soundcard_id = 0;
+ if ( Mixer::mixers().count() > 1 ) {
+ soundcard_id = m_cMixer->currentItem();
+ }
+ int channel_id = m_buttonGroupForScrollView->selectedId();
+ if ( channel_id != -1 ) {
+ // A channel was selected by the user => emit the "newMasterSelected()" signal
+ //kdDebug(67100) << "DialogSelectMaster::apply(): card=" << soundcard_id << ", channel=" << channel_id << endl;
+ Mixer *mixer = Mixer::mixers().at(soundcard_id);
+ if ( mixer == 0 ) {
+ kdError(67100) << "DialogSelectMaster::createPage(): Invalid Mixer (mixerID=" << soundcard_id << ")" << endl;
+ return; // can not happen
+ }
+ else {
+ mixer->setMasterDevice( m_mixerPKs[channel_id] );
+ emit newMasterSelected(soundcard_id, m_mixerPKs[channel_id] );
+ }
+ }
+}
+
+#include "dialogselectmaster.moc"
+
diff --git a/kmix/dialogselectmaster.h b/kmix/dialogselectmaster.h
new file mode 100644
index 00000000..912d870d
--- /dev/null
+++ b/kmix/dialogselectmaster.h
@@ -0,0 +1,43 @@
+#ifndef DIALOGSELECTMASTER_H
+#define DIALOGSELECTMASTER_H
+
+class QButtonGroup;
+#include <qradiobutton.h>
+class QScrollView;
+#include <qstringlist.h>
+class QVBox;
+class QVBoxLayout;
+
+class KComboBox;
+#include <kdialogbase.h>
+
+class Mixer;
+
+class DialogSelectMaster : public KDialogBase
+{
+ Q_OBJECT
+ public:
+ DialogSelectMaster(Mixer *);
+ ~DialogSelectMaster();
+
+ signals:
+ void newMasterSelected(int, QString& );
+
+ public slots:
+ void apply();
+
+ private:
+ void createWidgets(Mixer*);
+ void createPage(Mixer*);
+ QVBoxLayout* _layout;
+ KComboBox* m_cMixer;
+ QScrollView* m_scrollableChannelSelector;
+ QVBox *m_vboxForScrollView;
+ QButtonGroup *m_buttonGroupForScrollView;
+ QStringList m_mixerPKs;
+
+ private slots:
+ void createPageByID(int mixerId);
+};
+
+#endif
diff --git a/kmix/dialogviewconfiguration.cpp b/kmix/dialogviewconfiguration.cpp
new file mode 100644
index 00000000..1c94ccf7
--- /dev/null
+++ b/kmix/dialogviewconfiguration.cpp
@@ -0,0 +1,102 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <qcheckbox.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qptrlist.h>
+
+#include <kdebug.h>
+#include <kdialogbase.h>
+#include <klocale.h>
+
+#include "dialogviewconfiguration.h"
+#include "mixdevicewidget.h"
+#include "mixdevice.h"
+
+
+DialogViewConfiguration::DialogViewConfiguration( QWidget*, ViewBase& view)
+ : KDialogBase( Plain, i18n( "Configure" ), Ok|Cancel, Ok ),
+ _view(view)
+{
+ QPtrList<QWidget> &mdws = view._mdws;
+ _layout = new QVBoxLayout(plainPage(),0,-1, "_layout" );
+
+ // kdDebug(67100) << "DialogViewConfiguration::DialogViewConfiguration add header" << "\n";
+ QLabel* qlb = new QLabel( i18n("Configure"), plainPage() );
+ //QLabel* qlb = new QLabel( i18n("Show"), plainPage() );
+ _layout->addWidget(qlb);
+
+ for ( QWidget *qw = mdws.first(); qw != 0; qw = mdws.next())
+ {
+ if ( qw->inherits("MixDeviceWidget") ) {
+ MixDeviceWidget *mdw = static_cast<MixDeviceWidget*>(qw);
+ QString mdName = mdw->mixDevice()->name();
+ mdName.replace('&', "&&"); // Quoting the '&' needed, to prevent QCheckBox creating an accelerator
+ QCheckBox* cb = new QCheckBox( mdName, plainPage() );
+ _qEnabledCB.append(cb);
+ cb->setChecked( !mdw->isDisabled() ); //mdw->isVisible() );
+ _layout->addWidget(cb);
+ }
+ }
+ _layout->activate();
+ resize(_layout->sizeHint() );
+ connect( this, SIGNAL(okClicked()) , this, SLOT(apply()) );
+}
+
+DialogViewConfiguration::~DialogViewConfiguration()
+{
+}
+
+void DialogViewConfiguration::apply()
+{
+ QPtrList<QWidget> &mdws = _view._mdws;
+
+ // --- 2-Step Apply ---
+
+ // --- Step 1: Show and Hide Widgets ---
+ QCheckBox *cb = _qEnabledCB.first();
+ for ( QWidget *qw = mdws.first(); qw != 0; qw = mdws.next())
+ {
+ if ( qw->inherits("MixDeviceWidget") ) {
+ MixDeviceWidget *mdw = static_cast<MixDeviceWidget*>(qw);
+ if ( cb->isChecked() ) {
+ mdw->setDisabled(false);
+ }
+ else {
+ mdw->setDisabled(true);
+ }
+
+ cb = _qEnabledCB.next();
+ }
+ }
+
+ // --- Step 2: Tell the view, that it has changed (probably it needs some "polishing" ---
+ _view.configurationUpdate();
+}
+
+QSize DialogViewConfiguration::sizeHint() const {
+ // kdDebug(67100) << "DialogViewConfiguration::sizeHint() is (100,500)\n";
+ return _layout->sizeHint();
+}
+
+#include "dialogviewconfiguration.moc"
+
diff --git a/kmix/dialogviewconfiguration.h b/kmix/dialogviewconfiguration.h
new file mode 100644
index 00000000..d63d1031
--- /dev/null
+++ b/kmix/dialogviewconfiguration.h
@@ -0,0 +1,30 @@
+#ifndef DIALOGVIEWCONFIGURATION_H
+#define DIALOGVIEWCONFIGURATION_H
+
+#include <qcheckbox.h>
+#include <qptrlist.h>
+class QVBoxLayout;
+
+#include <kdialogbase.h>
+
+#include "viewbase.h"
+
+
+class DialogViewConfiguration : public KDialogBase
+{
+ Q_OBJECT
+ public:
+ DialogViewConfiguration(QWidget* parent, ViewBase& view);
+ ~DialogViewConfiguration();
+
+ QSize sizeHint() const;
+ public slots:
+ void apply();
+
+ private:
+ QVBoxLayout* _layout;
+ ViewBase& _view;
+ QPtrList<QCheckBox> _qEnabledCB;
+};
+
+#endif
diff --git a/kmix/doc/ControlNames.txt b/kmix/doc/ControlNames.txt
new file mode 100644
index 00000000..5b18298e
--- /dev/null
+++ b/kmix/doc/ControlNames.txt
@@ -0,0 +1,84 @@
+This document describes standard names of mixer controls.
+
+Syntax: SOURCE [DIRECTION] FUNCTION
+
+DIRECTION:
+ <nothing> (both directions)
+ Playback
+ Capture
+ Bypass Playback
+ Bypass Capture
+
+FUNCTION:
+ Switch (on/off switch)
+ Volume
+ Route (route control, hardware specific)
+
+SOURCE:
+ Master
+ Master Mono
+ Hardware Master
+ Headphone
+ PC Speaker
+ Phone
+ Phone Input
+ Phone Output
+ Synth
+ FM
+ Mic
+ Line
+ CD
+ Video
+ Zoom Video
+ Aux
+ PCM
+ PCM Front
+ PCM Rear
+ PCM Pan
+ Loopback
+ Analog Loopback (D/A -> A/D loopback)
+ Digital Loopback (playback -> capture loopback - without analog path)
+ Mono
+ Mono Output
+ Multi
+ ADC
+ Wave
+ Music
+ I2S
+ IEC958
+
+Exceptions:
+ [Digital] Capture Source
+ [Digital] Capture Switch (aka input gain switch)
+ [Digital] Capture Volume (aka input gain volume)
+ [Digital] Playback Switch (aka output gain switch)
+ [Digital] Playback Volume (aka output gain volume)
+ Tone Control - Switch
+ Tone Control - Bass
+ Tone Control - Treble
+ 3D Control - Switch
+ 3D Control - Center
+ 3D Control - Depth
+ 3D Control - Wide
+ 3D Control - Space
+ 3D Control - Level
+ Mic Boost [(?dB)]
+
+PCM interface:
+
+ Sample Clock Source { "Word", "Internal", "AutoSync" }
+ Clock Sync Status { "Lock", "Sync", "No Lock" }
+ External Rate /* external capture rate */
+ Capture Rate /* capture rate taken from external source */
+
+IEC958 (S/PDIF) interface:
+
+ IEC958 [...] [Playback|Capture] Switch /* turn on/off the IEC958 interface */
+ IEC958 [...] [Playback|Capture] Volume /* digital volume control */
+ IEC958 [...] [Playback|Capture] Default /* default or global value - read/write */
+ IEC958 [...] [Playback|Capture] Mask /* consumer and professional mask */
+ IEC958 [...] [Playback|Capture] Con Mask /* consumer mask */
+ IEC958 [...] [Playback|Capture] Pro Mask /* professional mask */
+ IEC958 [...] [Playback|Capture] PCM Stream /* the settings assigned to a PCM stream */
+ IEC958 Q-subcode [Playback|Capture] Default /* Q-subcode bits */
+ IEC958 Preamble [Playback|Capture] Default /* burst preamble words (4*16bits) */
diff --git a/kmix/kledbutton.cpp b/kmix/kledbutton.cpp
new file mode 100644
index 00000000..1cddb148
--- /dev/null
+++ b/kmix/kledbutton.cpp
@@ -0,0 +1,79 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <qsizepolicy.h>
+
+#include "kledbutton.h"
+
+
+KLedButton::KLedButton(const QColor &col, QWidget *parent, const char *name)
+ : KLed( col, parent, name )
+{
+ // KLed and thus KLedButtung does not do proper positioning in QLayout's.
+ // Thus I will do a dirty trick here
+ installEventFilter(parent);
+}
+
+KLedButton::KLedButton(const QColor& col, KLed::State st, KLed::Look look,
+ KLed::Shape shape, QWidget *parent, const char *name)
+ : KLed( col, st, look, shape, parent, name )
+{
+}
+
+KLedButton::~KLedButton()
+{
+}
+
+void KLedButton::mousePressEvent( QMouseEvent *e )
+{
+ if (e->button() == LeftButton)
+ {
+ toggle();
+ emit stateChanged( state() );
+ }
+}
+
+bool KLedButton::eventFilter( QObject* /*obj*/ , QEvent* /*ev*/ ) {
+ // KLed and thus KLedButtung does not do proper positioning in QLayout's.
+ // Thus I listen to my parents resize events and do it here ... OUCH, that's ugly
+ /* No, this cannot work !
+ if ( ev->type() == QEvent::Resize ) {
+ QResizeEvent* qre = (QResizeEvent*)ev;
+ this->move( qre->size().width() - width()/2,
+ qre->size().height() - height()/2 );
+ }
+ */
+ return false;
+ // KLed::eventFilter(obj, ev);
+
+}
+
+QSize KLedButton::sizeHint() const
+{
+ return size();
+}
+
+QSizePolicy KLedButton::sizePolicy () const
+{
+ return QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+}
+
+#include "kledbutton.moc"
diff --git a/kmix/kledbutton.h b/kmix/kledbutton.h
new file mode 100644
index 00000000..261f829c
--- /dev/null
+++ b/kmix/kledbutton.h
@@ -0,0 +1,53 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef KLEDBUTTON_H
+#define KLEDBUTTON_H
+
+#include <qwidget.h>
+
+#include <kled.h>
+
+/**
+ *@author Stefan Schimanski
+ */
+
+class KLedButton : public KLed {
+ Q_OBJECT
+ public:
+ KLedButton(const QColor &col=Qt::green, QWidget *parent=0, const char *name=0);
+ KLedButton(const QColor& col, KLed::State st, KLed::Look look, KLed::Shape shape,
+ QWidget *parent=0, const char *name=0);
+ ~KLedButton();
+
+ QSize sizeHint () const;
+ QSizePolicy sizePolicy () const;
+ signals:
+ void stateChanged( bool newState );
+
+ protected:
+ void mousePressEvent ( QMouseEvent *e );
+
+ private:
+ bool eventFilter( QObject*, QEvent* );
+};
+
+#endif
diff --git a/kmix/kmix-platforms.cpp b/kmix/kmix-platforms.cpp
new file mode 100644
index 00000000..7947043b
--- /dev/null
+++ b/kmix/kmix-platforms.cpp
@@ -0,0 +1,149 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2000 Christian Esken
+ * esken@kde.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* This code is being #include'd from mixer.cpp */
+
+#include <config.h>
+
+#include <qstring.h>
+
+#include "mixer_backend.h"
+
+#if defined(sun) || defined(__sun__)
+#define SUN_MIXER
+#endif
+
+#ifdef sgi
+#include <sys/fcntl.h>
+#define IRIX_MIXER
+#endif
+
+#ifdef __linux__
+
+#ifdef HAVE_LIBASOUND2
+#define ALSA_MIXER
+#endif
+
+#define OSS_MIXER
+#endif
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(_UNIXWARE) || defined(__DragonFly__)
+#define OSS_MIXER
+#endif
+
+#if defined(hpux)
+# if defined(HAVE_ALIB_H)
+# define HPUX_MIXER
+# else
+# warning ** YOU NEED to have libAlib installed to use the HP-UX-Mixer **
+# endif // HAVE_ALIB_H
+#endif // hpux
+
+// PORTING: add #ifdef PLATFORM , commands , #endif, add your new mixer below
+#if defined(NAS_MIXER)
+#include "mixer_nas.cpp"
+#endif
+
+#if defined(SUN_MIXER)
+#include "mixer_sun.cpp"
+#endif
+
+#if defined(IRIX_MIXER)
+#include "mixer_irix.cpp"
+#endif
+
+// Alsa API's
+#if defined(ALSA_MIXER)
+#include "mixer_alsa9.cpp"
+#endif
+
+#if defined(OSS_MIXER)
+#include "mixer_oss.cpp"
+
+#if !defined(__NetBSD__) && !defined(__OpenBSD__)
+#include <sys/soundcard.h>
+#else
+#include <soundcard.h>
+#endif
+#if SOUND_VERSION >= 0x040000
+#define OSS4_MIXER
+#endif
+
+#endif
+
+#if defined(OSS4_MIXER)
+#include "mixer_oss4.cpp"
+#endif
+
+#if defined(HPUX_MIXER)
+#include "mixer_hpux.cpp"
+#endif
+
+/*
+#else
+// We cannot handle this! I install a dummy mixer instead.
+#define NO_MIXER
+#include "mixer_none.cpp"
+#endif
+*/
+
+typedef Mixer_Backend *getMixerFunc( int device );
+typedef QString getDriverNameFunc( );
+
+struct MixerFactory {
+ getMixerFunc *getMixer;
+ getDriverNameFunc *getDriverName;
+};
+
+MixerFactory g_mixerFactories[] = {
+
+#if defined(NAS_MIXER)
+ { NAS_getMixer, 0 },
+#endif
+
+#if defined(SUN_MIXER)
+ { SUN_getMixer, SUN_getDriverName },
+#endif
+
+#if defined(IRIX_MIXER)
+ { IRIX_getMixer, IRIX_getDriverName },
+#endif
+
+#if defined(ALSA_MIXER)
+ { ALSA_getMixer, ALSA_getDriverName },
+#endif
+
+#if defined(OSS4_MIXER)
+ { OSS4_getMixer, OSS4_getDriverName },
+#endif
+
+#if defined(OSS_MIXER)
+ { OSS_getMixer, OSS_getDriverName },
+#endif
+
+#if defined(HPUX_MIXER)
+ { HPUX_getMixer, HPUX_getDriverName },
+#endif
+
+ { 0, 0 }
+};
+
diff --git a/kmix/kmix.cpp b/kmix/kmix.cpp
new file mode 100644
index 00000000..e4cd68be
--- /dev/null
+++ b/kmix/kmix.cpp
@@ -0,0 +1,648 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ * Copyright (C) 2000 Stefan Schimanski <schimmi@kde.org>
+ * Copyright (C) 2001 Preston Brown <pbrown@kde.org>
+ * Copyright (C) 2003 Sven Leiber <s.leiber@web.de>
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// include files for QT
+#include <qmap.h>
+#include <qhbox.h>
+#include <qcheckbox.h>
+#include <qradiobutton.h>
+#include <qwidgetstack.h>
+#include <qlayout.h>
+#include <qtooltip.h>
+
+// include files for KDE
+#include <kcombobox.h>
+#include <kiconloader.h>
+#include <kmessagebox.h>
+#include <kmenubar.h>
+#include <klineeditdlg.h>
+#include <klocale.h>
+#include <kconfig.h>
+#include <kaction.h>
+#include <kapplication.h>
+#include <kstdaction.h>
+#include <kpanelapplet.h>
+#include <kpopupmenu.h>
+#include <khelpmenu.h>
+#include <kdebug.h>
+#include <kaccel.h>
+#include <kglobalaccel.h>
+#include <kkeydialog.h>
+#include <kpopupmenu.h>
+
+// application specific includes
+#include "mixertoolbox.h"
+#include "kmix.h"
+#include "kmixerwidget.h"
+#include "kmixprefdlg.h"
+#include "kmixdockwidget.h"
+#include "kmixtoolbox.h"
+
+
+/**
+ * Constructs a mixer window (KMix main window)
+ */
+KMixWindow::KMixWindow()
+ : KMainWindow(0, 0, 0, 0), m_showTicks( true ),
+ m_dockWidget( 0L )
+{
+ m_visibilityUpdateAllowed = true;
+ m_multiDriverMode = false; // -<- I never-ever want the multi-drivermode to be activated by accident
+ m_surroundView = false; // -<- Also the experimental surround View (3D)
+ m_gridView = false; // -<- Also the experimental Grid View
+ // As long as we do not know better, we assume to start hidden. We need
+ // to initialize this variable here, as we don't trigger a hideEvent().
+ m_isVisible = false;
+ m_mixerWidgets.setAutoDelete(true);
+ loadConfig(); // Need to load config before initMixer(), due to "MultiDriver" keyword
+ MixerToolBox::initMixer(Mixer::mixers(), m_multiDriverMode, m_hwInfoString);
+ initActions();
+ initWidgets();
+ initMixerWidgets();
+
+ initPrefDlg();
+ updateDocking();
+
+ if ( m_startVisible )
+ {
+ /* Started visible: We should do probably do:
+ * m_isVisible = true;
+ * But as a showEvent() is triggered by show() we don't need it.
+ */
+ show();
+ }
+ else
+ {
+ hide();
+ }
+ connect( kapp, SIGNAL( aboutToQuit()), SLOT( saveSettings()) );
+}
+
+
+KMixWindow::~KMixWindow()
+{
+ MixerToolBox::deinitMixer();
+}
+
+
+void
+KMixWindow::initActions()
+{
+ // file menu
+ KStdAction::quit( this, SLOT(quit()), actionCollection());
+
+ // settings menu
+ KStdAction::showMenubar( this, SLOT(toggleMenuBar()), actionCollection());
+ KStdAction::preferences( this, SLOT(showSettings()), actionCollection());
+ new KAction( i18n( "Configure &Global Shortcuts..." ), "configure_shortcuts", 0, this,
+ SLOT( configureGlobalShortcuts() ), actionCollection(), "settings_global" );
+ KStdAction::keyBindings( guiFactory(), SLOT(configureShortcuts()), actionCollection());
+
+ (void) new KAction( i18n( "Hardware &Information" ), 0, this, SLOT( slotHWInfo() ), actionCollection(), "hwinfo" );
+ (void) new KAction( i18n( "Hide Mixer Window" ), Key_Escape, this, SLOT(hide()), actionCollection(), "hide_kmixwindow" );
+
+ m_globalAccel = new KGlobalAccel( this );
+ m_globalAccel->insert( "Increase volume", i18n( "Increase Volume of Master Channel"), QString::null,
+ KShortcut(), KShortcut(), this, SLOT( increaseVolume() ) );
+ m_globalAccel->insert( "Decrease volume", i18n( "Decrease Volume of Master Channel"), QString::null,
+ KShortcut(), KShortcut(), this, SLOT( decreaseVolume() ) );
+ m_globalAccel->insert( "Toggle mute", i18n( "Toggle Mute of Master Channel"), QString::null,
+ KShortcut(), KShortcut(), this, SLOT( toggleMuted() ) );
+ m_globalAccel->readSettings();
+ m_globalAccel->updateConnections();
+
+ createGUI( "kmixui.rc" );
+}
+
+
+void
+KMixWindow::initPrefDlg()
+{
+ m_prefDlg = new KMixPrefDlg( this );
+ connect( m_prefDlg, SIGNAL(signalApplied(KMixPrefDlg *)),
+ this, SLOT(applyPrefs(KMixPrefDlg *)) );
+}
+
+
+void
+KMixWindow::initWidgets()
+{
+ // Main widget and layout
+ setCentralWidget( new QWidget( this, "qt_central_widget" ) );
+
+ // Widgets layout
+ widgetsLayout = new QVBoxLayout( centralWidget(), 0, 0, "widgetsLayout" );
+ widgetsLayout->setResizeMode(QLayout::Minimum); // works fine
+
+
+ // Mixer widget line
+ mixerNameLayout = new QHBox( centralWidget(), "mixerNameLayout" );
+ widgetsLayout->setStretchFactor( mixerNameLayout, 0 );
+ QSizePolicy qsp( QSizePolicy::Ignored, QSizePolicy::Maximum);
+ mixerNameLayout->setSizePolicy(qsp);
+ mixerNameLayout->setSpacing(KDialog::spacingHint());
+ QLabel *qlbl = new QLabel( i18n("Current mixer:"), mixerNameLayout );
+ qlbl->setFixedHeight(qlbl->sizeHint().height());
+ m_cMixer = new KComboBox( FALSE, mixerNameLayout, "mixerCombo" );
+ m_cMixer->setFixedHeight(m_cMixer->sizeHint().height());
+ connect( m_cMixer, SIGNAL( activated( int ) ), this, SLOT( showSelectedMixer( int ) ) );
+ QToolTip::add( m_cMixer, i18n("Current mixer" ) );
+
+ // Add first layout to widgets
+ widgetsLayout->addWidget( mixerNameLayout );
+
+ m_wsMixers = new QWidgetStack( centralWidget(), "MixerWidgetStack" );
+ widgetsLayout->setStretchFactor( m_wsMixers, 10 );
+ widgetsLayout->addWidget( m_wsMixers );
+
+ if ( m_showMenubar )
+ menuBar()->show();
+ else
+ menuBar()->hide();
+
+ widgetsLayout->activate();
+}
+
+
+void
+KMixWindow::updateDocking()
+{
+ // delete old dock widget
+ if (m_dockWidget)
+ {
+ delete m_dockWidget;
+ m_dockWidget = 0L;
+ }
+
+ if (m_showDockWidget)
+ {
+
+ // create dock widget
+ // !! This should be a View in the future
+ m_dockWidget = new KMixDockWidget( Mixer::mixers().first(), this, "mainDockWidget", m_volumeWidget );
+
+/* Belongs in KMixDockWidget
+ // create RMB menu
+ KPopupMenu *menu = m_dockWidget->contextMenu();
+
+ // !! check this
+ KAction *a = actionCollection()->action( "dock_mute" );
+ if ( a ) a->plug( menu );
+*/
+
+ /*
+ * Mail from 31.1.2005: "make sure your features are at least string complete"
+ * Preparation for fixing Bug #55078 - scheduled for KDE3.4.1 .
+ * This text will be plugged into the dock-icon popup menu.
+ */
+ QString selectChannel = i18n("Select Channel"); // This text will be used in KDE3.4.1 !!!
+
+ m_dockWidget->show();
+ }
+}
+
+void
+KMixWindow::saveSettings()
+{
+ saveConfig();
+ saveVolumes();
+}
+
+void
+KMixWindow::saveConfig()
+{
+ KConfig *config = kapp->config();
+ config->setGroup(0);
+
+ // make sure we don't start without any UI
+ // can happen e.g. when not docked and kmix closed via 'X' button
+ bool startVisible = m_isVisible;
+ if ( !m_showDockWidget )
+ startVisible = true;
+
+ config->writeEntry( "Size", size() );
+ config->writeEntry( "Position", pos() );
+ // Cannot use isVisible() here, as in the "aboutToQuit()" case this widget is already hidden.
+ // (Please note that the problem was only there when quitting via Systray - esken).
+ config->writeEntry( "Visible", startVisible );
+ config->writeEntry( "Menubar", m_showMenubar );
+ config->writeEntry( "AllowDocking", m_showDockWidget );
+ config->writeEntry( "TrayVolumeControl", m_volumeWidget );
+ config->writeEntry( "Tickmarks", m_showTicks );
+ config->writeEntry( "Labels", m_showLabels );
+ config->writeEntry( "startkdeRestore", m_onLogin );
+ Mixer* mixerMasterCard = Mixer::masterCard();
+ if ( mixerMasterCard != 0 ) {
+ config->writeEntry( "MasterMixer", mixerMasterCard->id() );
+ }
+ MixDevice* mdMaster = Mixer::masterCardDevice();
+ if ( mdMaster != 0 ) {
+ config->writeEntry( "MasterMixerDevice", mdMaster->getPK() );
+ }
+
+ if ( m_valueStyle == MixDeviceWidget::NABSOLUTE )
+ config->writeEntry( "ValueStyle", "Absolute");
+ else if ( m_valueStyle == MixDeviceWidget::NRELATIVE )
+ config->writeEntry( "ValueStyle", "Relative");
+ else
+ config->writeEntry( "ValueStyle", "None" );
+
+ if ( m_toplevelOrientation == Qt::Vertical )
+ config->writeEntry( "Orientation","Vertical" );
+ else
+ config->writeEntry( "Orientation","Horizontal" );
+
+ // save mixer widgets
+ for ( KMixerWidget *mw = m_mixerWidgets.first(); mw != 0; mw = m_mixerWidgets.next() )
+ {
+ if ( mw->mixer()->isOpen() )
+ { // protect from unplugged devices (better do *not* save them)
+ QString grp;
+ grp.sprintf( "%i", mw->id() );
+ mw->saveConfig( config, grp );
+ }
+ }
+
+ config->setGroup(0);
+}
+
+void
+KMixWindow::loadConfig()
+{
+ KConfig *config = kapp->config();
+ config->setGroup(0);
+
+ m_showDockWidget = config->readBoolEntry("AllowDocking", true);
+ m_volumeWidget = config->readBoolEntry("TrayVolumeControl", true);
+ //hide on close has to stay true for usability. KMixPrefDlg option commented out. nolden
+ m_hideOnClose = config->readBoolEntry("HideOnClose", true);
+ m_showTicks = config->readBoolEntry("Tickmarks", true);
+ m_showLabels = config->readBoolEntry("Labels", true);
+ const QString& valueStyleString = config->readEntry("ValueStyle", "None");
+ m_onLogin = config->readBoolEntry("startkdeRestore", true );
+ m_startVisible = config->readBoolEntry("Visible", true);
+ m_multiDriverMode = config->readBoolEntry("MultiDriver", false);
+ m_surroundView = config->readBoolEntry("Experimental-ViewSurround", false );
+ m_gridView = config->readBoolEntry("Experimental-ViewGrid", false );
+ const QString& orientationString = config->readEntry("Orientation", "Horizontal");
+ QString mixerMasterCard = config->readEntry( "MasterMixer", "" );
+ Mixer::setMasterCard(mixerMasterCard);
+ QString masterDev = config->readEntry( "MasterMixerDevice", "" );
+ Mixer::setMasterCardDevice(masterDev);
+
+ if ( valueStyleString == "Absolute" )
+ m_valueStyle = MixDeviceWidget::NABSOLUTE;
+ else if ( valueStyleString == "Relative" )
+ m_valueStyle = MixDeviceWidget::NRELATIVE;
+ else
+ m_valueStyle = MixDeviceWidget::NNONE;
+
+ if ( orientationString == "Vertical" )
+ m_toplevelOrientation = Qt::Vertical;
+ else
+ m_toplevelOrientation = Qt::Horizontal;
+
+ // show/hide menu bar
+ m_showMenubar = config->readBoolEntry("Menubar", true);
+
+ KToggleAction *a = static_cast<KToggleAction*>(actionCollection()->action("options_show_menubar"));
+ if (a) a->setChecked( m_showMenubar );
+
+ // restore window size and position
+ if ( !kapp->isRestored() ) // done by the session manager otherwise
+ {
+ QSize defSize( minimumWidth(), height() );
+ QSize size = config->readSizeEntry("Size", &defSize );
+ if(!size.isEmpty()) resize(size);
+
+ QPoint defPos = pos();
+ QPoint pos = config->readPointEntry("Position", &defPos);
+ move(pos);
+ }
+}
+
+
+void
+KMixWindow::initMixerWidgets()
+{
+ m_mixerWidgets.clear();
+
+ int id=0;
+ Mixer *mixer;
+
+ // Attention!! If Mixer::mixers() is empty, we behave stupid. We don't show nothing and there
+ // is not even a context menu.
+ for ( mixer=Mixer::mixers().first(),id=0; mixer!=0; mixer=Mixer::mixers().next(),id++ )
+ {
+ //kdDebug(67100) << "Mixer number: " << id << " Name: " << mixer->mixerName() << endl ;
+ ViewBase::ViewFlags vflags = ViewBase::HasMenuBar;
+ if ( m_showMenubar ) {
+ vflags |= ViewBase::MenuBarVisible;
+ }
+ if ( m_surroundView ) {
+ vflags |= ViewBase::Experimental_SurroundView;
+ }
+ if ( m_gridView ) {
+ vflags |= ViewBase::Experimental_GridView;
+ }
+ if ( m_toplevelOrientation == Qt::Vertical ) {
+ vflags |= ViewBase::Vertical;
+ }
+ else {
+ vflags |= ViewBase::Horizontal;
+ }
+
+
+ KMixerWidget *mw = new KMixerWidget( id, mixer, mixer->mixerName(),
+ MixDevice::ALL, this, "KMixerWidget", vflags );
+ m_mixerWidgets.append( mw );
+
+ // Add to Combo and Stack
+ m_cMixer->insertItem( mixer->mixerName() );
+ m_wsMixers->addWidget( mw, id );
+
+ QString grp;
+ grp.sprintf( "%i", mw->id() );
+ mw->loadConfig( kapp->config(), grp );
+
+ mw->setTicks( m_showTicks );
+ mw->setLabels( m_showLabels );
+ mw->setValueStyle ( m_valueStyle );
+ // !! I am still not sure whether this works 100% reliably - chris
+ mw->show();
+ }
+
+ if (id == 1)
+ {
+ // don't show the Current Mixer bit unless we have multiple mixers
+ mixerNameLayout->hide();
+ }
+}
+
+
+
+bool
+KMixWindow::queryClose ( )
+{
+ if ( m_showDockWidget && !kapp->sessionSaving() )
+ {
+ hide();
+ return false;
+ }
+ return true;
+}
+
+
+void
+KMixWindow::quit()
+{
+ kapp->quit();
+}
+
+
+void
+KMixWindow::showSettings()
+{
+ if (!m_prefDlg->isVisible())
+ {
+ m_prefDlg->m_dockingChk->setChecked( m_showDockWidget );
+ m_prefDlg->m_volumeChk->setChecked(m_volumeWidget);
+ m_prefDlg->m_showTicks->setChecked( m_showTicks );
+ m_prefDlg->m_showLabels->setChecked( m_showLabels );
+ m_prefDlg->m_onLogin->setChecked( m_onLogin );
+ m_prefDlg->_rbVertical ->setChecked( m_toplevelOrientation == Qt::Vertical );
+ m_prefDlg->_rbHorizontal->setChecked( m_toplevelOrientation == Qt::Horizontal );
+ m_prefDlg->_rbNone->setChecked( m_valueStyle == MixDeviceWidget::NNONE );
+ m_prefDlg->_rbAbsolute->setChecked( m_valueStyle == MixDeviceWidget::NABSOLUTE );
+ m_prefDlg->_rbRelative->setChecked( m_valueStyle == MixDeviceWidget::NRELATIVE );
+
+ m_prefDlg->show();
+ }
+}
+
+
+void
+KMixWindow::showHelp()
+{
+ actionCollection()->action( "help_contents" )->activate();
+}
+
+
+void
+KMixWindow::showAbout()
+{
+ actionCollection()->action( "help_about_app" )->activate();
+}
+
+/**
+ * Loads the volumes of all mixers from kmixctrlrc.
+ * In other words:
+ * Restores the default voumes as stored via saveVolumes() or the
+ * execution of "kmixctrl --save"
+ */
+/* Currently this is not in use
+void
+KMixWindow::loadVolumes()
+{
+ KConfig *cfg = new KConfig( "kmixctrlrc", true );
+ for (Mixer *mixer=Mixer::mixers().first(); mixer!=0; mixer=Mixer::mixers().next())
+ {
+ mixer->volumeLoad( cfg );
+ }
+ delete cfg;
+}
+*/
+
+/**
+ * Stores the volumes of all mixers Can be restored via loadVolumes() or
+ * the kmixctrl application.
+ */
+void
+KMixWindow::saveVolumes()
+{
+ KConfig *cfg = new KConfig( "kmixctrlrc", false );
+ for (Mixer *mixer=Mixer::mixers().first(); mixer!=0; mixer=Mixer::mixers().next()) {
+ //kdDebug(67100) << "KMixWindow::saveConfig()" << endl;
+ if ( mixer->isOpen() ) { // protect from unplugged devices (better do *not* save them)
+ mixer->volumeSave( cfg );
+ }
+ }
+ delete cfg;
+}
+
+
+void
+KMixWindow::applyPrefs( KMixPrefDlg *prefDlg )
+{
+ m_showDockWidget = prefDlg->m_dockingChk->isChecked();
+ m_volumeWidget = prefDlg->m_volumeChk->isChecked();
+ m_showTicks = prefDlg->m_showTicks->isChecked();
+ m_showLabels = prefDlg->m_showLabels->isChecked();
+ m_onLogin = prefDlg->m_onLogin->isChecked();
+
+ if ( prefDlg->_rbNone->isChecked() ) {
+ m_valueStyle = MixDeviceWidget::NNONE;
+ } else if ( prefDlg->_rbAbsolute->isChecked() ) {
+ m_valueStyle = MixDeviceWidget::NABSOLUTE;
+ } else if ( prefDlg->_rbRelative->isChecked() ) {
+ m_valueStyle = MixDeviceWidget::NRELATIVE;
+ }
+
+ bool toplevelOrientationHasChanged =
+ ( prefDlg->_rbVertical->isChecked() && m_toplevelOrientation == Qt::Horizontal )
+ || ( prefDlg->_rbHorizontal->isChecked() && m_toplevelOrientation == Qt::Vertical );
+ if ( toplevelOrientationHasChanged ) {
+ QString msg = i18n("The change of orientation will be adopted on the next start of KMix.");
+ KMessageBox::information(0,msg);
+ }
+ if ( prefDlg->_rbVertical->isChecked() ) {
+ //QString "For a change of language to take place, quit and restart KDiff3.";
+ //kdDebug(67100) << "KMix should change to Vertical layout\n";
+ m_toplevelOrientation = Qt::Vertical;
+ }
+ else if ( prefDlg->_rbHorizontal->isChecked() ) {
+ //kdDebug(67100) << "KMix should change to Horizontal layout\n";
+ m_toplevelOrientation = Qt::Horizontal;
+ }
+
+
+ this->setUpdatesEnabled(false);
+ updateDocking();
+
+ for (KMixerWidget *mw=m_mixerWidgets.first(); mw!=0; mw=m_mixerWidgets.next())
+ {
+ mw->setTicks( m_showTicks );
+ mw->setLabels( m_showLabels );
+ mw->setValueStyle ( m_valueStyle );
+ mw->mixer()->readSetFromHWforceUpdate(); // needed, as updateDocking() has reconstructed the DockWidget
+ }
+
+ this->setUpdatesEnabled(true);
+
+ // avoid invisible and unaccessible main window
+ if( !m_showDockWidget && !isVisible() )
+ {
+ show();
+ }
+
+ this->repaint(); // make KMix look fast (saveConfig() often uses several seconds)
+ kapp->processEvents();
+ saveConfig();
+}
+
+
+void
+KMixWindow::toggleMenuBar()
+{
+ m_showMenubar = !m_showMenubar;
+ if( m_showMenubar )
+ {
+ menuBar()->show();
+ }
+ else
+ {
+ menuBar()->hide();
+ }
+}
+
+void
+KMixWindow::showEvent( QShowEvent * )
+{
+ if ( m_visibilityUpdateAllowed )
+ m_isVisible = isVisible();
+ // !! could possibly start polling now (idea: use someting like ref() and unref() on Mixer instance
+}
+
+void
+KMixWindow::hideEvent( QHideEvent * )
+{
+ if ( m_visibilityUpdateAllowed )
+ {
+ m_isVisible = isVisible();
+ }
+ // !! could possibly stop polling now (idea: use someting like ref() and unref() on Mixer instance
+ // Update: This is a stupid idea, because now the views are responsible for updating. So it will be done in the Views.
+ // But the dock icon is currently no View, so that must be done first.
+}
+
+
+void
+KMixWindow::stopVisibilityUpdates() {
+ m_visibilityUpdateAllowed = false;
+}
+
+void
+KMixWindow::slotHWInfo() {
+ KMessageBox::information( 0, m_hwInfoString, i18n("Mixer Hardware Information") );
+}
+
+void
+KMixWindow::showSelectedMixer( int mixer )
+{
+ m_wsMixers->raiseWidget( mixer );
+}
+
+void
+KMixWindow::configureGlobalShortcuts()
+{
+ KKeyDialog::configure( m_globalAccel, 0, false ) ;
+ m_globalAccel->writeSettings();
+ m_globalAccel->updateConnections();
+}
+
+void
+KMixWindow::toggleMuted()
+{
+ Mixer* mixerMaster = Mixer::masterCard();
+ if ( mixerMaster != 0 ) {
+ MixDevice* md = mixerMaster->masterDevice();
+ if ( md != 0 && md->hasMute() ) {
+ mixerMaster->toggleMute(md->num());
+ }
+ }
+}
+
+void
+KMixWindow::increaseVolume()
+{
+ Mixer* mixerMaster = Mixer::masterCard();
+ if ( mixerMaster != 0 ) {
+ MixDevice* md = mixerMaster->masterDevice();
+ if ( md != 0 ) {
+ mixerMaster->increaseVolume(md->num());
+ }
+ }
+}
+
+void
+KMixWindow::decreaseVolume()
+{
+ Mixer* mixerMaster = Mixer::masterCard();
+ if ( mixerMaster != 0 ) {
+ MixDevice* md = mixerMaster->masterDevice();
+ if ( md != 0 ) {
+ mixerMaster->decreaseVolume(md->num());
+ }
+ }
+}
+
+#include "kmix.moc"
+
diff --git a/kmix/kmix.desktop b/kmix/kmix.desktop
new file mode 100644
index 00000000..4cb25f7a
--- /dev/null
+++ b/kmix/kmix.desktop
@@ -0,0 +1,92 @@
+[Desktop Entry]
+Exec=kmix -caption "%c" %i %m
+DocPath=kmix/index.html
+OnlyShowIn=KDE;
+Path=
+Type=Application
+MimeType=
+Terminal=false
+Icon=kmix
+GenericName=Sound Mixer
+GenericName[af]=Klank Menger
+GenericName[ar]=مازج الصوت
+GenericName[bg]=Аудио миксер
+GenericName[br]=Mesker ar Son
+GenericName[bs]=Zvučni mikser
+GenericName[ca]=Mesclador de so
+GenericName[cs]=Zvukový směšovač
+GenericName[cy]=Cymysgydd Sŵn
+GenericName[da]=Lydmikser
+GenericName[de]=Lautstärkeregler
+GenericName[el]=Μείκτης ήχου
+GenericName[eo]=Sonormiksilo
+GenericName[es]=Un mezclador audio
+GenericName[et]=Helimikser
+GenericName[eu]=Soinu nahasgailua
+GenericName[fa]=مخلوط‌کن صدا
+GenericName[fi]=Äänimikseri
+GenericName[fr]=Console de mixage
+GenericName[ga]=Meascthóir Fuaime
+GenericName[gl]=Mesturador de Son
+GenericName[he]=מערבל צליל
+GenericName[hi]=ध्वनि मिक्सर
+GenericName[hr]=Mikser zvuka
+GenericName[hu]=Hangkeverő
+GenericName[is]=Hljóðblöndun
+GenericName[it]=Mixer audio
+GenericName[ja]=サウンドミキサー
+GenericName[kk]=Дыбыс микшері
+GenericName[km]=កម្មវិធី​លាយ​សំឡេង
+GenericName[ko]=소리 믹서
+GenericName[lt]=Garsų maišiklis
+GenericName[lv]=Skaņas Mikšeris
+GenericName[mk]=Миксета за звук
+GenericName[ms]=Pengadun Bunyi
+GenericName[nb]=Lydmikser
+GenericName[nds]=Klangmischer
+GenericName[ne]=ध्वनि मिक्सर
+GenericName[nl]=Geluidsmixer
+GenericName[nn]=Lydmiksar
+GenericName[pa]=ਧੁਨੀ ਮਿਕਸਰ
+GenericName[pl]=Ustawienia głośności
+GenericName[pt]=Mesa de Mistura de Áudio
+GenericName[pt_BR]=Mixagem de som
+GenericName[ro]=Mixer de sunet
+GenericName[ru]=Звуковой микшер
+GenericName[se]=Jietnamixer
+GenericName[sk]=Zvukový mixér
+GenericName[sl]=Mešalnik zvoka
+GenericName[sr]=Звучна миксета
+GenericName[sr@Latn]=Zvučna mikseta
+GenericName[sv]=Ljudmixer
+GenericName[ta]=ஒலி ஒன்றுசேர்ப்பான்
+GenericName[tg]=Омехтакунаки Овоз
+GenericName[th]=โปรแกรมผสมเสียง
+GenericName[tr]=Ses Denetimleri
+GenericName[uk]=Аудіомікшер
+GenericName[uz]=Audio mikser
+GenericName[uz@cyrillic]=Аудио миксер
+GenericName[ven]=Tshitanganisi tsha mubvumo
+GenericName[wa]=Maxheu d' sons
+GenericName[xh]=Umxubi WokuvakalayoU
+GenericName[zh_CN]=混音器
+GenericName[zh_HK]=聲音混音器
+GenericName[zh_TW]=音效混音器
+GenericName[zu]=Umxubi Womsindo
+Name=KMix
+Name[af]=Kmix
+Name[bn]=কে-মিক্স
+Name[ca]=Kmix
+Name[eo]=Miksilo
+Name[hi]=के-मिक्स
+Name[lv]=KMiks
+Name[ne]=केडीई मिक्स
+Name[pa]=ਕੇ-ਮਿਕਸ
+Name[sv]=Kmix
+Name[ta]=கேமிக்ஸ்
+Name[tg]=KОмезиш
+Name[ven]=U tanganisa ha K
+Name[zh_TW]=KMix 混音器
+X-KDE-StartupNotify=true
+X-DCOP-ServiceType=Unique
+Categories=Qt;KDE;AudioVideo;Audio;Mixer;
diff --git a/kmix/kmix.h b/kmix/kmix.h
new file mode 100644
index 00000000..4805d4d1
--- /dev/null
+++ b/kmix/kmix.h
@@ -0,0 +1,135 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef KMIX_H
+#define KMIX_H
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+// include files for Qt
+#include <qstring.h>
+#include <qmap.h>
+
+class QHBox;
+class QWidgetStack;
+
+// include files for KDE
+#include <kmainwindow.h>
+
+class KAccel;
+class KGlobalAccel;
+class KComboBox;
+class KMixerWidget;
+class KMixerPrefWidget;
+class KMixPrefDlg;
+class KMixDockWidget;
+class KMixWindow;
+class Mixer;
+
+#include "mixer.h"
+#include "mixdevicewidget.h"
+
+
+class
+KMixWindow : public KMainWindow
+{
+ Q_OBJECT
+
+ public:
+ KMixWindow();
+ ~KMixWindow();
+
+ protected slots:
+ void saveSettings();
+
+ protected:
+ void saveConfig();
+ void loadConfig();
+
+ void initPrefDlg();
+ void initActions();
+ void initWidgets();
+ void initMixerWidgets();
+
+ void updateDocking();
+
+ bool queryClose();
+ void showEvent( QShowEvent * );
+ void hideEvent( QHideEvent * );
+
+ public slots:
+ void quit();
+ void showSettings();
+ void showHelp();
+ void showAbout();
+ void toggleMenuBar();
+ //void loadVolumes();
+ void saveVolumes();
+ virtual void applyPrefs( KMixPrefDlg *prefDlg );
+ void stopVisibilityUpdates();
+
+ private:
+ KAccel *m_keyAccel;
+ KGlobalAccel *m_globalAccel;
+ QPopupMenu *m_fileMenu;
+ QPopupMenu *m_viewMenu;
+ QPopupMenu *m_helpMenu;
+
+ bool m_showDockWidget;
+ bool m_volumeWidget;
+ bool m_hideOnClose;
+ bool m_showTicks;
+ bool m_showLabels;
+ MixDeviceWidget::ValueStyle m_valueStyle; // No numbers by default
+ bool m_onLogin;
+ bool m_startVisible;
+ bool m_showMenubar;
+ bool m_isVisible;
+ bool m_visibilityUpdateAllowed;
+ bool m_multiDriverMode; // Not officially supported.
+ bool m_surroundView; // Experimental. Off by defualt
+ bool m_gridView; // Experimental. Off by default
+ Qt::Orientation m_toplevelOrientation;
+
+ QPtrList<KMixerWidget> m_mixerWidgets;
+
+ QHBox* mixerNameLayout;
+ KComboBox *m_cMixer;
+ QWidgetStack *m_wsMixers;
+ KMixPrefDlg *m_prefDlg;
+ KMixDockWidget *m_dockWidget;
+ QString m_hwInfoString;
+ QVBoxLayout *widgetsLayout;
+
+ private slots:
+ //void removeMixerWidget( KMixerWidget *mw );
+ void slotHWInfo();
+ void showSelectedMixer( int mixer );
+ void configureGlobalShortcuts();
+ void toggleMuted();
+ void increaseVolume();
+ void decreaseVolume();
+};
+
+#endif // KMIX_H
diff --git a/kmix/kmixapplet.cpp b/kmix/kmixapplet.cpp
new file mode 100644
index 00000000..7ab8c00b
--- /dev/null
+++ b/kmix/kmixapplet.cpp
@@ -0,0 +1,566 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2000 Stefan Schimanski <schimmi@kde.org>
+ * Copyright (C) 2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// System
+#include <stdlib.h>
+
+// QT
+#include <qgroupbox.h>
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpixmap.h>
+#include <qpushbutton.h>
+#include <qradiobutton.h>
+#include <qwmatrix.h>
+
+
+// KDE
+#include <kaboutapplication.h>
+#include <kaboutdata.h>
+#include <kaction.h>
+#include <kapplication.h>
+#include <kbugreport.h>
+#include <kcolorbutton.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kglobalaccel.h>
+#include <kglobalsettings.h>
+#include <kiconloader.h>
+#include <kinputdialog.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+
+// // KMix
+#include "colorwidget.h"
+#include "mixertoolbox.h"
+#include "kmixapplet.h"
+#include "kmixtoolbox.h"
+#include "mdwslider.h"
+#include "mixdevicewidget.h"
+#include "mixer.h"
+#include "version.h"
+#include "viewapplet.h"
+
+
+extern "C"
+{
+ KDE_EXPORT KPanelApplet* init(QWidget *parent, const QString& configFile)
+ {
+ KGlobal::locale()->insertCatalogue("kmix");
+ return new KMixApplet(configFile, KPanelApplet::Normal,
+ parent, "kmixapplet");
+ }
+}
+
+int KMixApplet::s_instCount = 0;
+//<Mixer> KMixApplet::Mixer::mixers();
+
+static const QColor highColor = KGlobalSettings::baseColor();
+static const QColor lowColor = KGlobalSettings::highlightColor();
+static const QColor backColor = "#000000";
+static const QColor mutedHighColor = "#FFFFFF";
+static const QColor mutedLowColor = "#808080";
+static const QColor mutedBackColor = "#000000";
+
+AppletConfigDialog::AppletConfigDialog( QWidget * parent, const char * name )
+ : KDialogBase( KDialogBase::Plain, QString::null,
+ KDialogBase::Ok | KDialogBase::Apply | KDialogBase::Cancel,
+ KDialogBase::Ok, parent, name, false, true)
+{
+ setPlainCaption(i18n("Configure - Mixer Applet"));
+ QFrame* page = plainPage();
+ QVBoxLayout *topLayout = new QVBoxLayout(page);
+ colorWidget = new ColorWidget(page);
+ topLayout->addWidget(colorWidget);
+ setUseCustomColors(false);
+}
+
+void AppletConfigDialog::slotOk()
+{
+ slotApply();
+ KDialogBase::slotOk();
+}
+
+void AppletConfigDialog::slotApply()
+{
+ emit applied();
+}
+
+void AppletConfigDialog::setActiveColors(const QColor& high, const QColor& low, const QColor& back)
+{
+ colorWidget->activeHigh->setColor(high);
+ colorWidget->activeLow->setColor(low);
+ colorWidget->activeBack->setColor(back);
+}
+
+void AppletConfigDialog::activeColors(QColor& high, QColor& low, QColor& back) const
+{
+ high = colorWidget->activeHigh->color();
+ low = colorWidget->activeLow->color();
+ back = colorWidget->activeBack->color();
+}
+
+void AppletConfigDialog::setMutedColors(const QColor& high, const QColor& low, const QColor& back)
+{
+ colorWidget->mutedHigh->setColor(high);
+ colorWidget->mutedLow->setColor(low);
+ colorWidget->mutedBack->setColor(back);
+}
+
+void AppletConfigDialog::mutedColors(QColor& high, QColor& low, QColor& back) const
+{
+ high = colorWidget->mutedHigh->color();
+ low = colorWidget->mutedLow->color();
+ back = colorWidget->mutedBack->color();
+}
+
+void AppletConfigDialog::setUseCustomColors(bool custom)
+{
+ colorWidget->customColors->setChecked(custom);
+ colorWidget->activeColors->setEnabled(custom);
+ colorWidget->mutedColors->setEnabled(custom);
+}
+
+bool AppletConfigDialog::useCustomColors() const
+{
+ return colorWidget->customColors->isChecked();
+}
+
+
+KMixApplet::KMixApplet( const QString& configFile, Type t,
+ QWidget *parent, const char *name )
+
+ : KPanelApplet( configFile, t, KPanelApplet::Preferences | KPanelApplet::ReportBug | KPanelApplet::About, parent, name ),
+ m_mixerWidget(0), m_errorLabel(0), m_pref(0),
+ m_aboutData( "kmix", I18N_NOOP("KMix Panel Applet"),
+ APP_VERSION, "Mini Sound Mixer Applet", KAboutData::License_GPL,
+ I18N_NOOP( "(c) 1996-2000 Christian Esken\n(c) 2000-2003 Christian Esken, Stefan Schimanski") )
+{
+ setBackgroundOrigin(AncestorOrigin);
+ kdDebug(67100) << "KMixApplet::KMixApplet instancing Applet. Old s_instCount="<< s_instCount << " configfile=" << configFile << endl;
+ //kdDebug(67100) << "KMixApplet::KMixApplet()" << endl;
+ _layout = new QHBoxLayout(this); // it will always only be one item in it, so we don't care whether it is HBox or VBox
+
+ // init static vars
+ if ( s_instCount == 0) {
+ Mixer::mixers().setAutoDelete( TRUE );
+ QString dummyStringHwinfo;
+ MixerToolBox::initMixer(Mixer::mixers(), false, dummyStringHwinfo);
+ }
+ s_instCount++;
+ kdDebug(67100) << "KMixApplet::KMixApplet instancing Applet, s_instCount="<< s_instCount << endl;
+
+ KGlobal::dirs()->addResourceType( "appicon", KStandardDirs::kde_default("data") + "kmix/pics" );
+
+ loadConfig();
+
+
+ /********** find out to use which mixer ****************************************/
+ _mixer = 0;
+ for (_mixer= Mixer::mixers().first(); _mixer!=0; _mixer=Mixer::mixers().next())
+ {
+ if ( _mixer->id() == _mixerId ) break;
+ }
+ if ( _mixer == 0 ) {
+ /* Until KMix V3.4-0 the mixerNumber (int) was stored. This was too complicated to handle, so we use an
+ * unique ID (_mixer->mixerId(). But in case when the user changes soundcards (or when upgrading from
+ * KMix 3.4-0 to a 3.4-1 or newer), we scan also for the soundcard name */
+ for (_mixer= Mixer::mixers().first(); _mixer!=0; _mixer=Mixer::mixers().next())
+ {
+ if ( _mixer->mixerName() == _mixerName ) break;
+ }
+ }
+
+ // don't prompt for a mixer if there is just one available
+ if ( !_mixer && Mixer::mixers().count() == 1 ) {
+ _mixer = Mixer::mixers().first();
+ }
+
+
+
+ if ( _mixer == 0 )
+ {
+ // No mixer set by user (kmixappletrc_*) and more than one to choose
+ // We do NOT know which mixer to use => ask the User
+ m_errorLabel = new QPushButton( i18n("Select Mixer"), this );
+ m_errorLabel->setGeometry(0, 0, m_errorLabel->sizeHint().width(), m_errorLabel->sizeHint().height() );
+ resize( m_errorLabel->sizeHint() );
+ connect( m_errorLabel, SIGNAL(clicked()), this, SLOT(selectMixer()) );
+ }
+ else {
+ // We know which mixer to use: Call positionChange(), which does all the creating
+ positionChange(position());
+ }
+ m_aboutData.addCredit( I18N_NOOP( "For detailed credits, please refer to the About information of the KMix program" ) );
+}
+
+KMixApplet::~KMixApplet()
+{
+ saveConfig();
+
+ /* !!! no cleanup for now: I get strange crashes on exiting
+ // destroy static vars
+ s_instCount--;
+ if ( s_instCount == 0)
+ {
+ MixerToolBox::deinitMixer();
+ }
+ */
+}
+
+void KMixApplet::saveConfig()
+{
+ kdDebug(67100) << "KMixApplet::saveConfig()" << endl;
+ if ( m_mixerWidget != 0) {
+ //kdDebug(67100) << "KMixApplet::saveConfig() save" << endl;
+ KConfig *cfg = this->config();
+ //kdDebug(67100) << "KMixApplet::saveConfig() save cfg=" << cfg << endl;
+ cfg->setGroup( 0 );
+ cfg->writeEntry( "Mixer", _mixer->id() );
+ cfg->writeEntry( "MixerName", _mixer->mixerName() );
+
+ cfg->writeEntry( "ColorCustom", _customColors );
+
+ cfg->writeEntry( "ColorHigh", _colors.high.name() );
+ cfg->writeEntry( "ColorLow", _colors.low.name() );
+ cfg->writeEntry( "ColorBack", _colors.back.name() );
+
+ cfg->writeEntry( "ColorMutedHigh", _colors.mutedHigh.name() );
+ cfg->writeEntry( "ColorMutedLow", _colors.mutedLow.name() );
+ cfg->writeEntry( "ColorMutedBack", _colors.mutedBack.name() );
+
+ //cfg->writeEntry( "ReversedDirection", reversedDir );
+
+ saveConfig( cfg, "Widget" );
+ cfg->sync();
+ }
+}
+
+
+void KMixApplet::loadConfig()
+{
+ kdDebug(67100) << "KMixApplet::loadConfig()" << endl;
+ KConfig *cfg = this->config();
+ cfg->setGroup(0);
+
+ _mixerId = cfg->readEntry( "Mixer", "undef" );
+ _mixerName = cfg->readEntry( "MixerName", QString::null );
+
+ _customColors = cfg->readBoolEntry( "ColorCustom", false );
+
+ _colors.high = cfg->readColorEntry("ColorHigh", &highColor);
+ _colors.low = cfg->readColorEntry("ColorLow", &lowColor);
+ _colors.back = cfg->readColorEntry("ColorBack", &backColor);
+
+ _colors.mutedHigh = cfg->readColorEntry("ColorMutedHigh", &mutedHighColor);
+ _colors.mutedLow = cfg->readColorEntry("ColorMutedLow", &mutedLowColor);
+ _colors.mutedBack = cfg->readColorEntry("ColorMutedBack", &mutedBackColor);
+
+ loadConfig( cfg, "Widget");
+}
+
+
+void KMixApplet::loadConfig( KConfig *config, const QString &grp )
+{
+ if ( m_mixerWidget ) {
+ //config->setGroup( grp );
+ KMixToolBox::loadConfig(m_mixerWidget->_mdws, config, grp, "PanelApplet" );
+ }
+}
+
+
+void KMixApplet::saveConfig( KConfig *config, const QString &grp )
+{
+ if ( m_mixerWidget ) {
+ config->setGroup( grp );
+ // Write mixer name. It cannot be changed in the Mixer instance,
+ // it is only saved for diagnostical purposes (analyzing the config file).
+ config->writeEntry("Mixer_Name_Key", _mixer->mixerName());
+ KMixToolBox::saveConfig(m_mixerWidget->_mdws, config, grp, "PanelApplet" );
+ }
+}
+
+/**
+ * Opens a dialog box with all available mixers and let the user choose one.
+ * If the user selects a mixer, "_mixer" will be set and positionChange() is called.
+ */
+void KMixApplet::selectMixer()
+{
+ QStringList lst;
+
+ int n=1;
+ for (Mixer *mixer=Mixer::mixers().first(); mixer!=0; mixer=Mixer::mixers().next())
+ {
+ QString s;
+ s.sprintf("%i. %s", n, mixer->mixerName().ascii());
+ lst << s;
+ n++;
+ }
+
+ bool ok = FALSE;
+ QString res = KInputDialog::getItem( i18n("Mixers"),
+ i18n("Available mixers:"),
+ lst, 1, FALSE, &ok, this );
+ if ( ok )
+ {
+ Mixer *mixer = Mixer::mixers().at( lst.findIndex( res ) );
+ if (!mixer)
+ KMessageBox::sorry( this, i18n("Invalid mixer entered.") );
+ else
+ {
+ delete m_errorLabel;
+ m_errorLabel = 0;
+
+ _mixer = mixer;
+ // Create the ViewApplet by calling positionChange() ... :)
+ // To take over reversedDir and (more important) to create the mixer widget
+ // if necessary!
+ positionChange(position());
+ }
+ }
+}
+
+
+void KMixApplet::about()
+{
+ KAboutApplication aboutDlg(&m_aboutData);
+ aboutDlg.exec();
+}
+
+void KMixApplet::help()
+{
+}
+
+
+void KMixApplet::positionChange(Position pos) {
+ orientationChange( orientation() );
+ QResizeEvent e( size(), size() ); // from KPanelApplet::positionChange
+ resizeEvent( &e ); // from KPanelApplet::positionChange
+
+ if ( m_errorLabel == 0) {
+ // do this only after we deleted the error label
+ if (m_mixerWidget) {
+ saveConfig(); // save the applet before recreating it
+ _layout->remove(m_mixerWidget);
+ delete m_mixerWidget;
+ }
+ m_mixerWidget = new ViewApplet( this, _mixer->name(), _mixer, 0, pos );
+ connect ( m_mixerWidget, SIGNAL(appletContentChanged()), this, SLOT(updateGeometrySlot()) );
+ m_mixerWidget->createDeviceWidgets();
+ _layout->add(m_mixerWidget);
+ _layout->activate();
+
+ loadConfig();
+ setColors();
+
+ const QSize panelAppletConstrainedSize = sizeHint();
+ m_mixerWidget->setGeometry( 0, 0, panelAppletConstrainedSize.width(), panelAppletConstrainedSize.height() );
+ resize( panelAppletConstrainedSize.width(), panelAppletConstrainedSize.height() );
+ //setFixedSize(panelAppletConstrainedSize.width(), panelAppletConstrainedSize.height() );
+ //kdDebug(67100) << "KMixApplet::positionChange(). New MDW is at " << panelAppletConstrainedSize << endl;
+ m_mixerWidget->show();
+ //connect( _mixer, SIGNAL(newVolumeLevels()), m_mixerWidget, SLOT(refreshVolumeLevels()) );
+ }
+}
+
+
+/************* GEOMETRY STUFF START ********************************/
+void KMixApplet::resizeEvent(QResizeEvent *e)
+{
+ //kdDebug(67100) << "KMixApplet::resizeEvent(). New MDW is at " << e->size() << endl;
+
+ if ( position() == KPanelApplet::pLeft || position() == KPanelApplet::pRight ) {
+ if ( m_mixerWidget ) m_mixerWidget->resize(e->size().width(),m_mixerWidget->height());
+ if ( m_errorLabel ) m_errorLabel ->resize(e->size().width(),m_errorLabel ->height());
+ }
+ else {
+ if ( m_mixerWidget ) m_mixerWidget->resize(m_mixerWidget->width(), e->size().height());
+ if ( m_errorLabel ) m_errorLabel ->resize(m_errorLabel ->width() ,e->size().height());
+ }
+
+
+ // resizing changes our own sizeHint(), because we must take the new PanelSize in account.
+ // So updateGeometry() is amust for us.
+ //kdDebug(67100) << "KMixApplet::resizeEvent(). UPDATE GEOMETRY" << endl;
+ updateGeometry();
+ //kdDebug(67100) << "KMixApplet::resizeEvent(). EMIT UPDATE LAYOUT" << endl;
+ emit updateLayout();
+}
+
+void KMixApplet::updateGeometrySlot() {
+ updateGeometry();
+}
+
+
+QSize KMixApplet::sizeHint() const {
+ //kdDebug(67100) << "KMixApplet::sizeHint()\n";
+ QSize qsz;
+ if ( m_errorLabel !=0 ) {
+ qsz = m_errorLabel->sizeHint();
+ }
+ else if ( m_mixerWidget != 0) {
+ qsz = m_mixerWidget->sizeHint();
+ }
+ else {
+ // During construction of m_mixerWidget or if something goes wrong:
+ // Return something that should resemble our former sizeHint().
+ qsz = size();
+ }
+ //kdDebug(67100) << "KMixApplet::sizeHint() leftright =" << qsz << "\n";
+ return qsz;
+}
+
+/**
+ We need widthForHeight() and heigthForWidth() only because KPanelApplet::updateLayout does relayouts
+ using this method. Actually we ignore the passed paramater and just return our preferred size.
+*/
+int KMixApplet::widthForHeight(int) const {
+ //kdDebug(67100) << "KMixApplet::widthForHeight() = " << sizeHint().width() << endl;
+ return sizeHint().width();
+}
+int KMixApplet::heightForWidth(int) const {
+ //kdDebug(67100) << "KMixApplet::heightForWidth() = " << sizeHint().height() << endl;
+ return sizeHint().height();
+}
+
+
+
+
+QSizePolicy KMixApplet::sizePolicy() const {
+ // return QSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred);
+ if ( orientation() == Qt::Vertical ) {
+ //kdDebug(67100) << "KMixApplet::sizePolicy=(Ignored,Fixed)\n";
+ return QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
+ }
+ else {
+ //kdDebug(67100) << "KMixApplet::sizePolicy=(Fixed,Ignored)\n";
+ return QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
+ }
+}
+
+/************* GEOMETRY STUFF END ********************************/
+
+
+void KMixApplet::reportBug()
+{
+ KBugReport bugReportDlg(this, true, &m_aboutData);
+ bugReportDlg.exec();
+}
+
+
+/******************* COLOR STUFF START ***********************************/
+
+void KMixApplet::preferences()
+{
+ if ( !m_pref )
+ {
+ m_pref = new AppletConfigDialog( this );
+ connect(m_pref, SIGNAL(finished()), SLOT(preferencesDone()));
+ connect( m_pref, SIGNAL(applied()), SLOT(applyPreferences()) );
+
+ m_pref->setActiveColors(_colors.high , _colors.low , _colors.back);
+ m_pref->setMutedColors (_colors.mutedHigh, _colors.mutedLow, _colors.mutedBack);
+
+ m_pref->setUseCustomColors( _customColors );
+
+ }
+
+ m_pref->show();
+ m_pref->raise();
+}
+
+
+void KMixApplet::preferencesDone()
+{
+ m_pref->delayedDestruct();
+ m_pref = 0;
+}
+
+void KMixApplet::applyPreferences()
+{
+ if (!m_pref)
+ return;
+
+ // copy the colors from the prefs dialog
+ m_pref->activeColors(_colors.high , _colors.low , _colors.back);
+ m_pref->mutedColors (_colors.mutedHigh, _colors.mutedLow, _colors.mutedBack);
+ _customColors = m_pref->useCustomColors();
+ if (!m_mixerWidget)
+ return;
+
+ /*
+ QSize si = m_mixerWidget->size();
+ m_mixerWidget->resize( si );
+ */
+ setColors();
+ saveConfig();
+}
+
+void KMixApplet::paletteChange ( const QPalette &) {
+ if ( ! _customColors ) {
+ // We take over Colors from paletteChange(), if the user has not set custom colors.
+ // ignore the given QPalette and use the values from KGlobalSettings instead
+ _colors.high = KGlobalSettings::highlightColor();
+ _colors.low = KGlobalSettings::baseColor();
+ _colors.back = backColor;
+ setColors( _colors );
+ }
+}
+
+void KMixApplet::setColors()
+{
+ if ( !_customColors ) {
+ KMixApplet::Colors cols;
+ cols.high = highColor;
+ cols.low = lowColor;
+ cols.back = backColor;
+ cols.mutedHigh = mutedHighColor;
+ cols.mutedLow = mutedLowColor;
+ cols.mutedBack = mutedBackColor;
+
+ setColors( cols );
+ } else
+ setColors( _colors );
+}
+
+void KMixApplet::setColors( const Colors &color )
+{
+ if ( m_mixerWidget == 0 ) {
+ // can happen for example after a paletteChange()
+ return;
+ }
+ QPtrList<QWidget> &mdws = m_mixerWidget->_mdws;
+ for ( QWidget* qmdw=mdws.first(); qmdw != 0; qmdw=mdws.next() ) {
+ if ( qmdw->inherits("MixDeviceWidget") ) { // -<- temporary check. Later we *know* that it is correct
+ static_cast<MixDeviceWidget*>(qmdw)->setColors( color.high, color.low, color.back );
+ static_cast<MixDeviceWidget*>(qmdw)->setMutedColors( color.mutedHigh, color.mutedLow, color.mutedBack );
+ }
+ }
+}
+
+/******************* COLOR STUFF END ***********************************/
+
+#include "kmixapplet.moc"
+
diff --git a/kmix/kmixapplet.desktop b/kmix/kmixapplet.desktop
new file mode 100644
index 00000000..1295ecdd
--- /dev/null
+++ b/kmix/kmixapplet.desktop
@@ -0,0 +1,110 @@
+[Desktop Entry]
+Type=Plugin
+Name=Sound Mixer
+Name[bg]=Аудио миксер
+Name[br]=Mesker ar Son
+Name[bs]=Zvučni mikser
+Name[ca]=Mesclador de so
+Name[cs]=Zvukový směšovač
+Name[cy]=Cymysgydd Sŵn
+Name[da]=Lydmikser
+Name[de]=Lautstärkeregler
+Name[el]=Μείκτης ήχου
+Name[eo]=Sonmiksilo
+Name[es]=Mezclador de audio
+Name[et]=Helimikser
+Name[eu]=Soinu nahasgailua
+Name[fa]=مخلوط‌کن صدا
+Name[fi]=Äänimikseri
+Name[fr]=Console de mixage
+Name[ga]=Meascthóir Fuaime
+Name[gl]=Mesturador de Son
+Name[he]=מערבל צליל
+Name[hi]=ध्वनि मिक्सर
+Name[hr]=Mixer zvuka
+Name[hu]=Hangkeverő
+Name[is]=Hljóðblöndun
+Name[it]=Mixer sonoro
+Name[ja]=サウンドミキサー
+Name[kk]=Дыбыс микшері
+Name[km]=កម្មវិធី​លាយ​សំឡេង
+Name[ko]=소리 믹서
+Name[mk]=Миксета за звук
+Name[ms]=Pengadun Bunyi
+Name[nb]=Lydmikser
+Name[nds]=Klangmischer
+Name[ne]=ध्वनि मिक्सर
+Name[nl]=Geluidsmixer
+Name[nn]=Lydmiksar
+Name[pa]=ਸਾਊਂਡ ਮਿਕਸਰ
+Name[pl]=Mikser dźwięku
+Name[pt]=Mistura de Áudio
+Name[pt_BR]=Mixagem de som
+Name[ro]=Mixer de sunet
+Name[ru]=Микшер
+Name[sk]=Zvukový mixér
+Name[sl]=Mešalnik zvoka
+Name[sr]=Звучна миксета
+Name[sr@Latn]=Zvučna mikseta
+Name[sv]=Ljudmixer
+Name[ta]=ஒலிக் ஒன்றுசேர்ப்பான்
+Name[tg]=Омехтакунаки Овоз
+Name[th]=ปรับแต่งผสมเสียง
+Name[tr]=Ses Karıştırıcı
+Name[uk]=Аудіомікшер
+Name[uz]=Audio mikser
+Name[uz@cyrillic]=Аудио миксер
+Name[wa]=Maxheu d' sons
+Name[zh_CN]=混音器
+Name[zh_HK]=聲音混音器
+Name[zh_TW]=音效混音器
+
+Comment=Volume and sound channel mixer control
+Comment[bg]=Управление на силата на звука и каналите
+Comment[br]=Renadur meskañ kanol ha nerzh
+Comment[bs]=Kontrola miksera kanala glasnoće i zvuka
+Comment[ca]=Control i mesclador de volum i canals de so
+Comment[cs]=Ovládání hlasitosti a zvukových kanálů
+Comment[da]=Mikserkontrol for lydstyrke og lydkanal
+Comment[de]=Kontrolle der Lautstärke
+Comment[el]=Στοιχείο μείκτη ήχων και έντασης καναλιών
+Comment[eo]=Stirado de volumo kaj sonkanalmiksilo
+Comment[es]=Control del volumen y los canales de sonido del mezclador
+Comment[et]=Helitugevuse ja helikanalite seadistamine
+Comment[eu]=Bolumena eta soinu-kanalen nahasketarako kontrola
+Comment[fa]=کنترل مخلوط‌کن مجرای صدا و حجم صدا
+Comment[fi]=Äänenvoimakkuuden ja äänikanavien mikserin hallinta
+Comment[fr]=Contrôle du volume et des canaux de la console de mixage
+Comment[gl]=Control do volume e da canle de son do mesturador
+Comment[he]=שינוי הגדרות עוצמת קול של כרטיס הקול
+Comment[hu]=Hangerőbeállító és keverő
+Comment[is]=Stjórnun á styrk og hljóðrása blöndun
+Comment[it]=Controllo del volume e del mixer sonoro
+Comment[ja]=ボリュームおよびサウンドチャンネルミキサー制御
+Comment[kk]=Дыбыс үнділігі мен арналарды басқару
+Comment[km]=ឧបករណ៍​លាយ​ឆានែល​កម្រិត​សំឡេង និង​សំឡេង​
+Comment[ko]=음량과 사운드 채널 믹서 조정
+Comment[lt]=Garso ir garso kanalų maišyklės valdymas
+Comment[nb]=Miksekontroll for lydnivået og lydkanalene
+Comment[nds]=Kuntrull för den Luutstärk- un Klangkanaalmischer
+Comment[ne]=भोल्युम र ध्वनि च्यानल मिक्सर नियन्त्रण
+Comment[nl]=Bedieningspaneel voor het regelen van het volume en de geluidskanalen
+Comment[nn]=Lydstyrke- og kanalmiksarkontroll
+Comment[pl]=Sterowanie mikserem dźwięku i kanałów
+Comment[pt]=Controlo de volume e canais de som
+Comment[pt_BR]=Controle de volume e mixer de canais de som
+Comment[ru]=Управление громкостью и звуковыми каналами
+Comment[sk]=Riadenie hlasitosti a zvukového kanálu mixéru
+Comment[sl]=Nadzor glasnosti in mešalnik zvočnih kanalov
+Comment[sr]=Контрола јачине и миксета звучних канала
+Comment[sr@Latn]=Kontrola jačine i mikseta zvučnih kanala
+Comment[sv]=Volymkontroll och ljudkanalmixer
+Comment[th]=โปรแกรมควบคุมช่องเสียงและระดับเสียง
+Comment[tr]=Ses düzeyi ve ses kanalı karıştırıcısını kontrol edin
+Comment[uk]=Керування гучністю і міксером звукових каналів
+Comment[zh_CN]=音量和声道混音器控制
+Comment[zh_HK]=音量與聲道混音控制器
+Comment[zh_TW]=音量與聲道混音器控制
+
+Icon=kmix
+X-KDE-Library=kmix_panelapplet
diff --git a/kmix/kmixapplet.h b/kmix/kmixapplet.h
new file mode 100644
index 00000000..11c6225a
--- /dev/null
+++ b/kmix/kmixapplet.h
@@ -0,0 +1,131 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef KMIXAPPLET_H
+#define KMIXAPPLET_H
+
+// Qt
+#include <qlayout.h>
+#include <qptrlist.h>
+#include <qwidget.h>
+
+// KDE
+#include <kaboutdata.h>
+#include <kdialogbase.h>
+#include <kpanelapplet.h>
+
+//KMix
+#include "viewapplet.h"
+
+class Mixer;
+class ColorWidget;
+class KMixApplet;
+
+
+class AppletConfigDialog : public KDialogBase
+{
+ Q_OBJECT
+ public:
+ AppletConfigDialog( QWidget * parent=0, const char * name=0 );
+ virtual ~AppletConfigDialog() {};
+
+ void setActiveColors(const QColor& high, const QColor& low, const QColor& back);
+ void activeColors(QColor& high, QColor& low, QColor& back) const;
+
+ void setMutedColors(const QColor& high, const QColor& low, const QColor& back);
+ void mutedColors(QColor& high, QColor& low, QColor& back) const;
+
+ void setUseCustomColors(bool);
+ bool useCustomColors() const;
+
+ protected slots:
+ virtual void slotOk();
+ virtual void slotApply();
+
+ signals:
+ void applied();
+ private:
+ ColorWidget* colorWidget;
+};
+
+
+class KMixApplet : public KPanelApplet
+{
+ Q_OBJECT
+
+public:
+ KMixApplet( const QString& configFile, Type t = Normal,
+ QWidget *parent = 0, const char *name = 0 );
+ virtual ~KMixApplet();
+
+ struct Colors {
+ QColor high, low, back, mutedHigh, mutedLow, mutedBack;
+ };
+
+ void about();
+ void help();
+ void preferences();
+ void reportBug();
+ void paletteChange ( const QPalette & oldPalette );
+
+ QSize sizeHint() const;
+ QSizePolicy sizePolicy() const;
+ int widthForHeight(int) const;
+ int heightForWidth(int) const;
+
+protected slots:
+ void selectMixer();
+ void applyPreferences();
+ void preferencesDone();
+ void updateGeometrySlot();
+
+protected:
+ void resizeEvent( QResizeEvent * );
+ void saveConfig();
+ void saveConfig( KConfig *config, const QString &grp );
+ void loadConfig();
+ void loadConfig( KConfig *config, const QString &grp );
+
+private:
+ void positionChange(Position);
+ void setColors();
+ void setColors( const Colors &color );
+
+ ViewApplet *m_mixerWidget;
+ QPushButton *m_errorLabel;
+ AppletConfigDialog *m_pref;
+
+ static int s_instCount;
+ Mixer *_mixer;
+
+ KMixApplet::Colors _colors;
+ bool _customColors;
+
+ QLayout* _layout;
+
+ QString _mixerId;
+ QString _mixerName;
+
+ KAboutData m_aboutData;
+};
+
+
+#endif
diff --git a/kmix/kmixctrl.cpp b/kmix/kmixctrl.cpp
new file mode 100644
index 00000000..3dbf4f4e
--- /dev/null
+++ b/kmix/kmixctrl.cpp
@@ -0,0 +1,90 @@
+/*
+ * kmixctrl - kmix volume save/restore utility
+ *
+ * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "mixertoolbox.h"
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kaboutdata.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <qptrlist.h>
+
+#include "kmixtoolbox.h"
+#include "mixer.h"
+#include "version.h"
+
+static const char description[] =
+I18N_NOOP("kmixctrl - kmix volume save/restore utility");
+
+static KCmdLineOptions options[] =
+{
+ { "s", 0, 0 },
+ { "save", I18N_NOOP("Save current volumes as default"), 0 },
+ { "r", 0, 0 },
+ { "restore", I18N_NOOP("Restore default volumes"), 0 },
+ KCmdLineLastOption
+ // INSERT YOUR COMMANDLINE OPTIONS HERE
+};
+
+extern "C" KDE_EXPORT int kdemain(int argc, char *argv[])
+{
+ KLocale::setMainCatalogue("kmix");
+ KAboutData aboutData( "kmixctrl", I18N_NOOP("KMixCtrl"),
+ APP_VERSION, description, KAboutData::License_GPL,
+ "(c) 2000 by Stefan Schimanski");
+
+ aboutData.addAuthor("Stefan Schimanski", 0, "1Stein@gmx.de");
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ KApplication app( false, false );
+
+ // get maximum values
+ KConfig *config= new KConfig("kmixrc", true, false);
+ config->setGroup("Misc");
+ delete config;
+
+ // create mixers
+ QString dummyStringHwinfo;
+ MixerToolBox::initMixer(Mixer::mixers(), false, dummyStringHwinfo);
+
+ // load volumes
+ if ( args->isSet("restore") )
+ {
+ for (Mixer *mixer=Mixer::mixers().first(); mixer!=0; mixer=Mixer::mixers().next()) {
+ mixer->volumeLoad( KGlobal::config() );
+ }
+ }
+
+ // save volumes
+ if ( args->isSet("save") )
+ {
+ for (Mixer *mixer=Mixer::mixers().first(); mixer!=0; mixer=Mixer::mixers().next())
+ mixer->volumeSave( KGlobal::config() );
+ }
+
+ MixerToolBox::deinitMixer();
+
+ return 0;
+}
diff --git a/kmix/kmixctrl_restore.desktop b/kmix/kmixctrl_restore.desktop
new file mode 100644
index 00000000..52e25fc8
--- /dev/null
+++ b/kmix/kmixctrl_restore.desktop
@@ -0,0 +1,70 @@
+[Desktop Entry]
+Type=Service
+Name=Restore Mixer Settings
+Name[af]=Herstel Menger Instellings
+Name[az]=Qarışdırıcı Qurğularını Yenidən Yükle
+Name[bg]=Възстановяване на настройките на миксера
+Name[bn]=মিক্সার সেটিংস পুনঃস্থাপন করে
+Name[br]=Assav kefluniadur ar mesker
+Name[bs]=Vrati postavke miksera
+Name[ca]=Restaura l'arranjament del mesclador
+Name[cs]=Obnovit nastavení směšovače
+Name[cy]=Adfer Gosodiadau Cymysgydd
+Name[da]=Genopret mikseropsætning
+Name[de]=Lautstärkeeinstellungen wiederherstellen
+Name[el]=Αποκατάσταση ρυθμίσεων μείκτη
+Name[eo]=Restarigu Miksilagordon
+Name[es]=Restaurar opciones del mezclador
+Name[et]=Mikseri seadistuste taastamine
+Name[eu]=Nahasgailuaren ezarpenak berreskuratu
+Name[fa]=باز‌گردانی تنظیمات مخلوط‌کن
+Name[fi]=Palauta mikserin asetukset
+Name[fr]=Restaurer la configuration du mixage
+Name[gl]=Restaura-los Parámetros do Mesturador
+Name[he]=שיחזור הגדרות המערבל
+Name[hi]=मिक्सर विन्यास बहाल करें
+Name[hr]=Vrati postavke miksera
+Name[hu]=A hangkeverő beállításainak visszatöltése
+Name[id]=Kembalikan seting Mixer
+Name[is]=Sækja aftur stillingar hljóðrása
+Name[it]=Ripristina le impostazioni del mixer
+Name[ja]=ミキサーの設定を復元
+Name[kk]=Микшер баптауларын қалпына келтіру
+Name[km]=ស្ដារ​ការ​កំណត់​ឧបករណ៍​លាយ​ឡើង​វិញ
+Name[ko]=믹서 설정 복원
+Name[lt]=Gražinti mikšerio parametrus
+Name[lv]=Atjauno Miksera Uzstādījumus
+Name[mk]=Враќање на поставувањата на миксетата
+Name[ms]=Pulihkan Tetapan Pengadun
+Name[mt]=Reġġa' lura setings tal-Mixer
+Name[nb]=Gjennopprett mikserinnstillinger
+Name[nds]=Mischerinstellen wedderherstellen
+Name[ne]=मिक्सर सेटिङ पूर्वावस्थामा ल्याउनुहोस्
+Name[nl]=Mixerinstellingen herstellen
+Name[nn]=Gjenopprett miksarinnstillingar
+Name[pl]=Odtwarzanie ustawień miksera
+Name[pt]=Repor a Configuração do Volume
+Name[pt_BR]=Restaurar preferências do mixer
+Name[ro]=Reface setările mixerului
+Name[ru]=Восстанавливает настройки микшера
+Name[se]=Máhcat mixerheivehusat
+Name[sk]=Obnov Nastavenia Mixéra
+Name[sl]=Obnovi nastavitve mešalnika
+Name[sr]=Поврати поставке миксете
+Name[sr@Latn]=Povrati postavke miksete
+Name[sv]=Återställ mixerinställningar
+Name[ta]=ஒன்றுசேர்க்கும் அமைப்புகளை மீட்கவும்
+Name[tg]=Аз нав захиракунии Гузоришҳои Омехтакунак
+Name[th]=เรียกคืนค่าที่ตั้งไว้ของมิกเซอร์
+Name[tr]=Karıştırıcı Ayarlarını Yeniden Yükle
+Name[uk]=Відновити параметри мікшера
+Name[uz]=Mikserning moslamalarini qayta tiklash
+Name[uz@cyrillic]=Миксернинг мосламаларини қайта тиклаш
+Name[ven]=Vhuedzedzani vhuvha ha tshitanganisi
+Name[wa]=Rimete come divant les apontiaedjes d maxheu d' sons
+Name[xh]=Gcina kwakhona izicwangciso zoMxubi
+Name[zh_CN]=恢复混音器设置
+Name[zh_HK]=回復混音器設置
+Name[zh_TW]=回復混音器設定
+Name[zu]=Gcina futhi izilungiso zoMxubi
+Exec=kmixctrl --restore
diff --git a/kmix/kmixdockwidget.cpp b/kmix/kmixdockwidget.cpp
new file mode 100644
index 00000000..18e19af0
--- /dev/null
+++ b/kmix/kmixdockwidget.cpp
@@ -0,0 +1,391 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
+ * Copyright (C) 2001 Preston Brown <pbrown@kde.org>
+ * Copyright (C) 2003 Sven Leiber <s.leiber@web.de>
+ * Copyright (C) 2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <kaction.h>
+#include <klocale.h>
+#include <kapplication.h>
+#include <kpanelapplet.h>
+#include <kpopupmenu.h>
+#include <kglobalsettings.h>
+#include <kdialog.h>
+#include <kaudioplayer.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <kwin.h>
+
+#include <qapplication.h>
+#include <qcursor.h>
+#include <qtooltip.h>
+#include <X11/Xlib.h>
+#include <fixx11h.h>
+
+#include "dialogselectmaster.h"
+#include "mixer.h"
+#include "mixdevicewidget.h"
+#include "kmixdockwidget.h"
+#include "kwin.h"
+#include "viewdockareapopup.h"
+
+KMixDockWidget::KMixDockWidget( Mixer *mixer, QWidget *parent, const char *name, bool volumePopup )
+ : KSystemTray( parent, name ),
+ m_mixer(mixer),
+ _dockAreaPopup(0L),
+ _audioPlayer(0L),
+ _playBeepOnVolumeChange(false), // disabled due to triggering a "Bug"
+ _oldToolTipValue(-1),
+ _oldPixmapType('-'),
+ _volumePopup(volumePopup)
+{
+ Mixer* preferredMasterMixer = Mixer::masterCard();
+ if ( preferredMasterMixer != 0 ) {
+ m_mixer = preferredMasterMixer;
+ }
+ MixDevice* mdMaster = Mixer::masterCardDevice();
+ if ( mdMaster != 0 ) {
+ m_mixer->setMasterDevice(mdMaster->getPK()); // !! using both Mixer::masterCard() and m_mixer->masterDevice() is nonsense !!
+ }
+ createActions();
+ createMasterVolWidget();
+ connect(this, SIGNAL(quitSelected()), kapp, SLOT(quitExtended()));
+}
+
+KMixDockWidget::~KMixDockWidget()
+{
+ delete _audioPlayer;
+ delete _dockAreaPopup;
+}
+
+void KMixDockWidget::createActions()
+{
+ // Put "Mute" selector in context menu
+ (void)new KToggleAction( i18n( "M&ute" ), 0, this, SLOT( dockMute() ),
+ actionCollection(), "dock_mute" );
+ KAction *a = actionCollection()->action( "dock_mute" );
+ KPopupMenu *popupMenu = contextMenu();
+ if ( a ) a->plug( popupMenu );
+
+ // Put "Select Master Channel" dialog in context menu
+ if ( m_mixer != 0 ) {
+ (void)new KAction( i18n("Select Master Channel..."), 0, this, SLOT(selectMaster()),
+ actionCollection(), "select_master");
+ KAction *a2 = actionCollection()->action( "select_master" );
+ if (a2) a2->plug( popupMenu );
+ }
+
+ // Setup volume preview
+ if ( _playBeepOnVolumeChange ) {
+ _audioPlayer = new KAudioPlayer("KDE_Beep_Digital_1.ogg");
+ }
+}
+
+
+void
+KMixDockWidget::createMasterVolWidget()
+{
+ // Reset flags, so that the dock icon will be reconstructed
+ _oldToolTipValue = -1;
+ _oldPixmapType = '-';
+
+ if (m_mixer == 0) {
+ // In case that there is no mixer installed, there will be no newVolumeLevels() signal's
+ // Thus we prepare the dock areas manually
+ setVolumeTip();
+ updatePixmap();
+ return;
+ }
+ // create devices
+
+ _dockAreaPopup = new ViewDockAreaPopup(0, "dockArea", m_mixer, 0, this);
+ _dockAreaPopup->createDeviceWidgets();
+ m_mixer->readSetFromHWforceUpdate(); // after changing the master device, make sure to re-read (otherwise no "changed()" signals might get sent by the Mixer
+ /* With the recently introduced QSocketNotifier stuff, we can't rely on regular timer updates
+ any longer. Also the readSetFromHWforceUpdate() won't be enough. As a workaround, we trigger
+ all "repaints" manually here.
+ The call to m_mixer->readSetFromHWforceUpdate() is most likely superfluous, even if we don't use QSocketNotifier (e.g. in backends OSS, Solaris, ...)
+ */
+ setVolumeTip();
+ updatePixmap();
+ /* We are setting up 3 connections:
+ * Refreshig the _dockAreaPopup (not anymore neccesary, because ViewBase already does it)
+ * Refreshing the Tooltip
+ * Refreshing the Icon
+ *
+ */
+ // connect( m_mixer, SIGNAL(newVolumeLevels()), _dockAreaPopup, SLOT(refreshVolumeLevels()) );
+ connect( m_mixer, SIGNAL(newVolumeLevels()), this, SLOT(setVolumeTip() ) );
+ connect( m_mixer, SIGNAL(newVolumeLevels()), this, SLOT(updatePixmap() ) );
+}
+
+
+void KMixDockWidget::selectMaster()
+{
+ DialogSelectMaster* dsm = new DialogSelectMaster(m_mixer);
+ connect ( dsm, SIGNAL(newMasterSelected(int, QString&)), SLOT( handleNewMaster(int,QString&)) );
+ dsm->show();
+ // !! The dialog is modal. Does it delete itself?
+}
+
+
+void KMixDockWidget::handleNewMaster(int soundcard_id, QString& channel_id) // !! @todo rework parameters
+{
+ //kdDebug(67100) << "KMixDockWidget::handleNewMaster() soundcard_id=" << soundcard_id << " , channel_id=" << channel_id << endl;
+ Mixer *mixer = Mixer::mixers().at(soundcard_id);
+ if ( mixer == 0 ) {
+ kdError(67100) << "KMixDockWidget::createPage(): Invalid Mixer (soundcard_id=" << soundcard_id << ")" << endl;
+ return; // can not happen
+ }
+ m_mixer = mixer;
+ Mixer::setMasterCard(mixer->id()); // We must save this information "somewhere".
+ Mixer::setMasterCardDevice( channel_id );
+ createMasterVolWidget();
+}
+
+
+void
+KMixDockWidget::setVolumeTip()
+{
+ MixDevice *md = 0;
+ if ( _dockAreaPopup != 0 ) {
+ md = _dockAreaPopup->dockDevice();
+ }
+ QString tip = "";
+
+ int newToolTipValue = 0;
+ if ( md == 0 )
+ {
+ tip = i18n("Mixer cannot be found"); // !! text could be reworked
+ newToolTipValue = -2;
+ }
+ else
+ {
+ long val = -1;
+ if ( md->maxVolume() != 0 ) {
+ val = (md->getVolume().getAvgVolume(Volume::MMAIN)*100 )/( md->maxVolume() );
+ }
+ newToolTipValue = val + 10000*md->isMuted();
+ if ( _oldToolTipValue != newToolTipValue ) {
+ tip = i18n( "Volume at %1%" ).arg( val );
+ if ( md->isMuted() ) {
+ tip += i18n( " (Muted)" );
+ }
+ }
+ // create a new "virtual" value. With that we see "volume changes" as well as "muted changes"
+ newToolTipValue = val + 10000*md->isMuted();
+ }
+
+ // The actual updating is only done when the "toolTipValue" was changed
+ if ( newToolTipValue != _oldToolTipValue ) {
+ // changed (or completely new tooltip)
+ if ( _oldToolTipValue >= 0 ) {
+ // there was an old Tooltip: remove it
+ QToolTip::remove(this);
+ }
+ QToolTip::add(this, tip);
+ }
+ _oldToolTipValue = newToolTipValue;
+}
+
+void
+KMixDockWidget::updatePixmap()
+{
+ MixDevice *md = 0;
+ if ( _dockAreaPopup != 0 ) {
+ md = _dockAreaPopup->dockDevice();
+ }
+ char newPixmapType;
+ if ( md == 0 )
+ {
+ newPixmapType = 'e';
+ }
+ else if ( md->isMuted() )
+ {
+ newPixmapType = 'm';
+ }
+ else
+ {
+ newPixmapType = 'd';
+ }
+
+
+ if ( newPixmapType != _oldPixmapType ) {
+ // Pixmap must be changed => do so
+ switch ( newPixmapType ) {
+ case 'e': setPixmap( loadIcon( "kmixdocked_error" ) ); break;
+ case 'm': setPixmap( loadIcon( "kmixdocked_mute" ) ); break;
+ case 'd': setPixmap( loadIcon( "kmixdocked" ) ); break;
+ }
+ }
+
+ _oldPixmapType = newPixmapType;
+}
+
+void
+KMixDockWidget::mousePressEvent(QMouseEvent *me)
+{
+ if ( _dockAreaPopup == 0 ) {
+ return KSystemTray::mousePressEvent(me);
+ }
+
+ // esken: Due to overwhelming request, LeftButton shows the ViewDockAreaPopup, if configured
+ // to do so. Otherwise the main window will be shown.
+ if ( me->button() == LeftButton )
+ {
+ if ( ! _volumePopup ) {
+ // Case 1: User wants to show main window => This is the KSystemTray default action
+ return KSystemTray::mousePressEvent(me);
+ }
+
+ // Case 2: User wants to show volume popup
+ if ( _dockAreaPopup->justHidden() )
+ return;
+
+ if ( _dockAreaPopup->isVisible() )
+ {
+ _dockAreaPopup->hide();
+ return;
+ }
+
+ int h = _dockAreaPopup->height();
+ int x = this->mapToGlobal( QPoint( 0, 0 ) ).x() + this->width()/2 - _dockAreaPopup->width()/2;
+ int y = this->mapToGlobal( QPoint( 0, 0 ) ).y() - h;
+ if ( y < 0 )
+ y = y + h + this->height();
+
+ _dockAreaPopup->move(x, y); // so that the mouse is outside of the widget
+
+ // Now handle Multihead displays. And also make sure that the dialog is not
+ // moved out-of-the screen on the right (see Bug 101742).
+ QDesktopWidget* vdesktop = QApplication::desktop();
+ const QRect& vScreenSize = vdesktop->screenGeometry(_dockAreaPopup);
+ if ( (x+_dockAreaPopup->width()) > (vScreenSize.width() + vScreenSize.x()) ) {
+ // move horizontally, so that it is completely visible
+ _dockAreaPopup->move(vScreenSize.width() + vScreenSize.x() - _dockAreaPopup->width() -1 , y);
+ } // horizontally out-of bound
+ else if ( x < vScreenSize.x() ) {
+ _dockAreaPopup->move(vScreenSize.x(), y);
+ }
+ // the above stuff could also be implemented vertically
+
+ _dockAreaPopup->show();
+ KWin::setState(_dockAreaPopup->winId(), NET::StaysOnTop | NET::SkipTaskbar | NET::SkipPager );
+
+ QWidget::mousePressEvent(me); // KSystemTray's shouldn't do the default action for this
+ return;
+ } // LeftMouseButton pressed
+ else if ( me->button() == MidButton ) {
+ toggleActive();
+ return;
+ }
+ else {
+ KSystemTray::mousePressEvent(me);
+ } // Other MouseButton pressed
+
+}
+
+void
+KMixDockWidget::mouseReleaseEvent( QMouseEvent *me )
+{
+
+ KSystemTray::mouseReleaseEvent(me);
+}
+
+void
+KMixDockWidget::wheelEvent(QWheelEvent *e)
+{
+ MixDevice *md = 0;
+ if ( _dockAreaPopup != 0 ) {
+ md = _dockAreaPopup->dockDevice();
+ }
+ if ( md != 0 )
+ {
+ Volume vol = md->getVolume();
+ int inc = vol.maxVolume() / 20;
+
+ if ( inc == 0 ) inc = 1;
+
+ for ( int i = 0; i < vol.count(); i++ ) {
+ int newVal = vol[i] + (inc * (e->delta() / 120));
+ if( newVal < 0 ) newVal = 0;
+ vol.setVolume( (Volume::ChannelID)i, newVal < vol.maxVolume() ? newVal : vol.maxVolume() );
+ }
+
+ if ( _playBeepOnVolumeChange ) {
+ _audioPlayer->play();
+ }
+ md->getVolume().setVolume(vol);
+ m_mixer->commitVolumeChange(md);
+ // refresh the toolTip (Qt removes it on a MouseWheel event)
+ // Mhhh, it doesn't work. Qt does not show it again.
+ setVolumeTip();
+ // Simulate a mouse move to make Qt show the tooltip again
+ QApplication::postEvent( this, new QMouseEvent( QEvent::MouseMove, QCursor::pos(), Qt::NoButton, Qt::NoButton ) );
+
+ }
+}
+
+void
+KMixDockWidget::dockMute()
+{
+ MixDevice *md = 0;
+ if ( _dockAreaPopup != 0 )
+ {
+ md = _dockAreaPopup->dockDevice();
+ if ( md != 0 ) {
+ md->setMuted( !md->isMuted() );
+ m_mixer->commitVolumeChange( md );
+ }
+ }
+}
+
+void
+KMixDockWidget::contextMenuAboutToShow( KPopupMenu* /* menu */ )
+{
+ KAction* showAction = actionCollection()->action("minimizeRestore");
+ if ( parentWidget() && showAction )
+ {
+ if ( parentWidget()->isVisible() )
+ {
+ showAction->setText( i18n("Hide Mixer Window") );
+ }
+ else
+ {
+ showAction->setText( i18n("Show Mixer Window") );
+ }
+ }
+
+ // Enable/Disable "Muted" menu item
+ MixDevice *md = 0;
+ if ( _dockAreaPopup != 0 )
+ {
+ md = _dockAreaPopup->dockDevice();
+ KToggleAction *dockMuteAction = static_cast<KToggleAction*>(actionCollection()->action("dock_mute"));
+ //kdDebug(67100) << "---> md=" << md << "dockMuteAction=" << dockMuteAction << "isMuted=" << md->isMuted() << endl;
+ if ( md != 0 && dockMuteAction != 0 ) {
+ dockMuteAction->setChecked( md->isMuted() );
+ }
+ }
+}
+
+#include "kmixdockwidget.moc"
+
diff --git a/kmix/kmixdockwidget.h b/kmix/kmixdockwidget.h
new file mode 100644
index 00000000..273e8533
--- /dev/null
+++ b/kmix/kmixdockwidget.h
@@ -0,0 +1,82 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
+ * Copyright (C) 2003 Sven Leiber <s.leiber@web.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef KMIXDOCKWIDGET_H
+#define KMIXDOCKWIDGET_H
+
+class QFrame;
+class QString;
+#include <qwidget.h>
+#include <qvbox.h>
+
+#include <ksystemtray.h>
+
+class Mixer;
+class KAudioPlayer;
+class MixDeviceWidget;
+class Mixer;
+class ViewDockAreaPopup;
+class Volume;
+
+class KMixDockWidget : public KSystemTray {
+ Q_OBJECT
+
+ friend class KMixWindow;
+
+ public:
+ KMixDockWidget(Mixer *, QWidget *parent=0, const char *name=0, bool volumePopup=true);
+ ~KMixDockWidget();
+
+ void setErrorPixmap();
+ void ignoreNextEvent();
+ ViewDockAreaPopup* getDockAreaPopup();
+
+ Mixer *m_mixer;
+ ViewDockAreaPopup *_dockAreaPopup;
+ KAudioPlayer *_audioPlayer;
+
+ public slots:
+ void setVolumeTip();
+ void updatePixmap();
+
+ protected:
+ void createMasterVolWidget();
+ void createActions();
+ void mousePressEvent(QMouseEvent *);
+ void mouseReleaseEvent(QMouseEvent *);
+ void wheelEvent(QWheelEvent *);
+ void contextMenuAboutToShow( KPopupMenu* menu );
+ void toggleMinimizeRestore();
+
+ private:
+ bool _playBeepOnVolumeChange;
+ bool _ignoreNextEvent;
+ int _oldToolTipValue;
+ char _oldPixmapType;
+ bool _volumePopup;
+ private slots:
+ void dockMute();
+ void selectMaster();
+ void handleNewMaster(int soundcard_id, QString& channel_id);
+};
+
+#endif
diff --git a/kmix/kmixerwidget.cpp b/kmix/kmixerwidget.cpp
new file mode 100644
index 00000000..cf597fb4
--- /dev/null
+++ b/kmix/kmixerwidget.cpp
@@ -0,0 +1,266 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ * Copyright (C) 2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+// Qt
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qslider.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qapplication.h> // for QApplication::revsreseLayout()
+
+// KDE
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <ktabwidget.h>
+
+// KMix
+#include "mixdevicewidget.h"
+#include "kmixerwidget.h"
+#include "kmixtoolbox.h"
+#include "mixer.h"
+#include "viewinput.h"
+#include "viewoutput.h"
+#include "viewswitches.h"
+// KMix experimental
+#include "viewgrid.h"
+#include "viewsurround.h"
+
+
+/**
+ This widget is embedded in the KMix Main window. Each Hardware Mixer is visualized by one KMixerWidget.
+ KMixerWidget contains
+ (a) a headline where you can change Mixer's (if you got more than one Mixer)
+ (b) a Tab with 2-4 Tabs (containing View's with sliders, switches and other GUI elements visualizing the Mixer)
+ (c) A balancing slider
+ (d) A label containg the mixer name
+*/
+KMixerWidget::KMixerWidget( int _id, Mixer *mixer, const QString &mixerName,
+ MixDevice::DeviceCategory categoryMask,
+ QWidget * parent, const char * name, ViewBase::ViewFlags vflags )
+ : QWidget( parent, name ), _mixer(mixer), m_balanceSlider(0),
+ m_topLayout(0),
+ m_id( _id ),
+ _iconsEnabled( true ), _labelsEnabled( false ), _ticksEnabled( false ),
+ _valueStyle ( -1 ) // this definitely does not correspond to the 'default value display' style,
+ // so the style will be set by a later call to setValueStyle()
+
+{
+ m_categoryMask = categoryMask;
+
+ if ( _mixer )
+ {
+ createLayout(vflags);
+ }
+ else
+ {
+ // No mixer found
+ // !! Fix this: This is actually never shown!
+ QBoxLayout *layout = new QHBoxLayout( this );
+ QString s = i18n("Invalid mixer");
+ if ( !mixerName.isEmpty() )
+ s.append(" \"").append(mixerName).append("\"");
+ QLabel *errorLabel = new QLabel( s, this );
+ errorLabel->setAlignment( QLabel::AlignCenter | QLabel::WordBreak );
+ layout->addWidget( errorLabel );
+ }
+}
+
+KMixerWidget::~KMixerWidget()
+{
+}
+
+/**
+ * Creates the widgets as described in the KMixerWidget constructor
+ */
+void KMixerWidget::createLayout(ViewBase::ViewFlags vflags)
+{
+ // delete old objects
+ if( m_balanceSlider ) {
+ delete m_balanceSlider;
+ }
+ if( m_topLayout ) {
+ delete m_topLayout;
+ }
+
+ // create main layout
+ m_topLayout = new QVBoxLayout( this, 0, 3, "m_topLayout" );
+
+ // Create tabs of input + output + [...]
+ m_ioTab = new KTabWidget( this, "ioTab" );
+ m_topLayout->add( m_ioTab );
+
+
+ /*******************************************************************
+ * Now the main GUI is created.
+ * 1) Select a (GUI) profile, which defines which controls to show on which Tab
+ * 2a) Create the Tab's and the corresponding Views
+ * 2b) Create device widgets
+ * 2c) Add Views to Tab
+ ********************************************************************/
+ //KMixGUIProfile* prof = MixerToolbox::selectProfile(_mixer);
+
+
+ possiblyAddView(new ViewOutput ( m_ioTab, "output", i18n("Output"), _mixer, vflags ) );
+ possiblyAddView(new ViewInput( m_ioTab, "input", i18n("Input"), _mixer, vflags ) );
+ possiblyAddView(new ViewSwitches( m_ioTab, "switches", i18n("Switches"), _mixer, vflags ) );
+ if ( vflags & ViewBase::Experimental_SurroundView )
+ possiblyAddView( new ViewSurround( m_ioTab, "surround", i18n("Surround"), _mixer, vflags ) );
+ if ( vflags & ViewBase::Experimental_GridView )
+ possiblyAddView( new ViewGrid( m_ioTab, "grid", i18n("Grid"), _mixer, vflags ) );
+
+
+ // *** Lower part: Slider and Mixer Name ************************************************
+ QHBoxLayout *balanceAndDetail = new QHBoxLayout( m_topLayout, 8, "balanceAndDetail");
+ // Create the left-right-slider
+ m_balanceSlider = new QSlider( -100, 100, 25, 0, QSlider::Horizontal, this, "RightLeft" );
+ m_balanceSlider->setTickmarks( QSlider::Below );
+ m_balanceSlider->setTickInterval( 25 );
+ m_balanceSlider->setMinimumSize( m_balanceSlider->sizeHint() );
+ m_balanceSlider->setFixedHeight( m_balanceSlider->sizeHint().height() );
+
+ QLabel *mixerName = new QLabel(this, "mixerName");
+ mixerName->setText( _mixer->mixerName() );
+
+ balanceAndDetail->addSpacing( 10 );
+
+ balanceAndDetail->addWidget( m_balanceSlider );
+ balanceAndDetail->addWidget( mixerName );
+ balanceAndDetail->addSpacing( 10 );
+
+ connect( m_balanceSlider, SIGNAL(valueChanged(int)), this, SLOT(balanceChanged(int)) );
+ QToolTip::add( m_balanceSlider, i18n("Left/Right balancing") );
+
+ // --- "MenuBar" toggling from the various View's ---
+
+
+
+ show();
+ // kdDebug(67100) << "KMixerWidget::createLayout(): EXIT\n";
+}
+
+void KMixerWidget::possiblyAddView(ViewBase* vbase)
+{
+ if ( vbase->count() == 0 )
+ delete vbase;
+ else {
+ _views.push_back(vbase);
+ vbase ->createDeviceWidgets();
+ m_ioTab->addTab( vbase , vbase->caption() );
+ connect( vbase, SIGNAL(toggleMenuBar()), parentWidget(), SLOT(toggleMenuBar()) );
+ }
+}
+
+void KMixerWidget::setIcons( bool on )
+{
+ for ( std::vector<ViewBase*>::iterator it = _views.begin(); it != _views.end(); it++) {
+ ViewBase* mixerWidget = *it;
+ KMixToolBox::setIcons(mixerWidget->_mdws, on);
+ } // for all tabs
+}
+
+void KMixerWidget::setLabels( bool on )
+{
+ if ( _labelsEnabled!=on ) {
+ // value was changed
+ _labelsEnabled = on;
+ for ( std::vector<ViewBase*>::iterator it = _views.begin(); it != _views.end(); it++) {
+ ViewBase* mixerWidget = *it;
+ KMixToolBox::setLabels(mixerWidget->_mdws, on);
+ } // for all tabs
+ }
+}
+
+void KMixerWidget::setTicks( bool on )
+{
+ if ( _ticksEnabled!=on ) {
+ // value was changed
+ _ticksEnabled = on;
+ for ( std::vector<ViewBase*>::iterator it = _views.begin(); it != _views.end(); it++) {
+ ViewBase* mixerWidget = *it;
+ KMixToolBox::setTicks(mixerWidget->_mdws, on);
+ } // for all tabs
+ }
+}
+
+void KMixerWidget::setValueStyle( int vs )
+{
+ if ( _valueStyle!=vs ) {
+ // value was changed
+ _valueStyle = vs;
+ for ( std::vector<ViewBase*>::iterator it = _views.begin(); it != _views.end(); it++) {
+ ViewBase* mixerWidget = *it;
+ KMixToolBox::setValueStyle(mixerWidget->_mdws, vs);
+ } // for all tabs
+ }
+}
+
+
+/**
+ * @todo : Is the view list already filled, when loadConfig() is called?
+ */
+void KMixerWidget::loadConfig( KConfig *config, const QString &grp )
+{
+
+ for ( std::vector<ViewBase*>::iterator it = _views.begin(); it != _views.end(); it++) {
+ ViewBase* mixerWidget = *it;
+ QString viewPrefix = "View.";
+ viewPrefix += mixerWidget->name();
+ KMixToolBox::loadConfig(mixerWidget->_mdws, config, grp, viewPrefix );
+ mixerWidget->configurationUpdate();
+ } // for all tabs
+}
+
+
+
+void KMixerWidget::saveConfig( KConfig *config, const QString &grp )
+{
+ config->setGroup( grp );
+ // Write mixer name. It cannot be changed in the Mixer instance,
+ // it is only saved for diagnostical purposes (analyzing the config file).
+ config->writeEntry("Mixer_Name_Key", _mixer->mixerName());
+
+ for ( std::vector<ViewBase*>::iterator it = _views.begin(); it != _views.end(); it++) {
+ ViewBase* mixerWidget = *it;
+ QString viewPrefix = "View.";
+ viewPrefix += mixerWidget->name();
+ KMixToolBox::saveConfig(mixerWidget->_mdws, config, grp, viewPrefix );
+ } // for all tabs
+}
+
+
+void KMixerWidget::toggleMenuBarSlot() {
+ emit toggleMenuBar();
+}
+
+// in RTL mode, the slider is reversed, we cannot just connect the signal to setBalance()
+// hack arround it before calling _mixer->setBalance()
+void KMixerWidget::balanceChanged(int balance)
+{
+ if (QApplication::reverseLayout())
+ balance = -balance;
+
+ _mixer->setBalance( balance );
+}
+
+#include "kmixerwidget.moc"
diff --git a/kmix/kmixerwidget.h b/kmix/kmixerwidget.h
new file mode 100644
index 00000000..442bded5
--- /dev/null
+++ b/kmix/kmixerwidget.h
@@ -0,0 +1,118 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef KMIXERWIDGET_H
+#define KMIXERWIDGET_H
+
+#include <vector>
+
+#include <qwidget.h>
+#include <qptrlist.h>
+class QString;
+class QGridLayout;
+
+#include <kpanelapplet.h>
+class KPopupMenu;
+
+#include "mixer.h"
+#include "mixdevicewidget.h"
+
+// QT
+class QSlider;
+
+
+// KDE
+class KActionCollection;
+class KActionMenu;
+class KConfig;
+class KTabWidget;
+
+// KMix
+class Mixer;
+#include "viewbase.h"
+class ViewInput;
+class ViewOutput;
+class ViewSwitches;
+// KMix experimental
+class ViewGrid;
+class ViewSurround;
+
+
+class KMixerWidget : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ KMixerWidget( int _id, Mixer *mixer, const QString &mixerName,
+ MixDevice::DeviceCategory categoryMask = MixDevice::ALL ,
+ QWidget *parent=0, const char *name=0, ViewBase::ViewFlags vflags=0 );
+ ~KMixerWidget();
+
+ enum KMixerWidgetIO { OUTPUT=0, INPUT };
+
+ const Mixer *mixer() const { return _mixer; };
+
+ int id() const { return m_id; };
+
+ KActionCollection* getActionCollection() const { return 0; /* m_actions; */ }
+
+ signals:
+ void masterMuted( bool );
+ void newMasterVolume(Volume vol);
+ void toggleMenuBar();
+
+ public slots:
+ void setTicks( bool on );
+ void setLabels( bool on );
+ void setIcons( bool on );
+ void setValueStyle( int vs );
+ void toggleMenuBarSlot();
+
+ void saveConfig( KConfig *config, const QString &grp );
+ void loadConfig( KConfig *config, const QString &grp );
+
+ private slots:
+ //void updateBalance();
+ void balanceChanged(int balance);
+
+ private:
+ Mixer *_mixer;
+ QSlider *m_balanceSlider;
+ QVBoxLayout *m_topLayout; // contains the Card selector, TabWidget and balance slider
+
+ KTabWidget* m_ioTab;
+
+ std::vector<ViewBase*> _views;
+ int m_id;
+
+ KActionMenu *m_toggleMixerChannels;
+
+ bool _iconsEnabled;
+ bool _labelsEnabled;
+ bool _ticksEnabled;
+ int _valueStyle;
+ MixDevice::DeviceCategory m_categoryMask;
+
+ void createLayout(ViewBase::ViewFlags vflags);
+ void possiblyAddView(ViewBase* vbase);
+};
+
+#endif
diff --git a/kmix/kmixprefdlg.cpp b/kmix/kmixprefdlg.cpp
new file mode 100644
index 00000000..e2788ceb
--- /dev/null
+++ b/kmix/kmixprefdlg.cpp
@@ -0,0 +1,132 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
+ * Copyright (C) 2001 Preston Brown <pbrown@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <qbuttongroup.h>
+#include <qlayout.h>
+#include <qwhatsthis.h>
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <qradiobutton.h>
+
+#include <klocale.h>
+// For "kapp"
+#include <kapplication.h>
+
+#include "kmix.h"
+#include "kmixprefdlg.h"
+#include "kmixerwidget.h"
+
+
+KMixPrefDlg::KMixPrefDlg( QWidget *parent )
+ : KDialogBase( Plain, i18n( "Configure" ),
+ Ok|Cancel|Apply, Ok, parent )
+{
+ // general buttons
+ m_generalTab = plainPage( /* i18n("&General") */ );
+
+ QBoxLayout *layout = new QVBoxLayout( m_generalTab );
+ layout->setSpacing( KDialog::spacingHint() );
+
+ m_dockingChk = new QCheckBox( i18n("&Dock into panel"), m_generalTab );
+ layout->addWidget( m_dockingChk );
+ QWhatsThis::add(m_dockingChk, i18n("Docks the mixer into the KDE panel"));
+
+ m_volumeChk = new QCheckBox(i18n("Enable system tray &volume control"),
+ m_generalTab);
+ layout->addWidget(m_volumeChk);
+
+ m_showTicks = new QCheckBox( i18n("Show &tickmarks"), m_generalTab );
+ layout->addWidget( m_showTicks );
+ QWhatsThis::add(m_showTicks,
+ i18n("Enable/disable tickmark scales on the sliders"));
+
+ m_showLabels = new QCheckBox( i18n("Show &labels"), m_generalTab );
+ layout->addWidget( m_showLabels );
+ QWhatsThis::add(m_showLabels,
+ i18n("Enables/disables description labels above the sliders"));
+
+
+ m_onLogin = new QCheckBox( i18n("Restore volumes on login"), m_generalTab );
+ layout->addWidget( m_onLogin );
+
+ QBoxLayout *numbersLayout = new QHBoxLayout( layout );
+ QButtonGroup *numbersGroup = new QButtonGroup( 3, Qt::Horizontal, i18n("Numbers"), m_generalTab );
+ numbersGroup->setRadioButtonExclusive(true);
+ QLabel* qlbl = new QLabel( i18n("Volume Values: "), m_generalTab );
+ _rbNone = new QRadioButton( i18n("&None"), m_generalTab );
+ _rbAbsolute = new QRadioButton( i18n("A&bsolute"), m_generalTab );
+ _rbRelative = new QRadioButton( i18n("&Relative"), m_generalTab );
+ numbersGroup->insert(_rbNone);
+ numbersGroup->insert(_rbAbsolute);
+ numbersGroup->insert(_rbRelative);
+ numbersGroup->hide();
+
+ numbersLayout->add(qlbl);
+ numbersLayout->add(_rbNone);
+ numbersLayout->add(_rbAbsolute);
+ numbersLayout->add(_rbRelative);
+ numbersLayout->addStretch();
+
+ QBoxLayout *orientationLayout = new QHBoxLayout( layout );
+ QButtonGroup* orientationGroup = new QButtonGroup( 2, Qt::Horizontal, i18n("Orientation"), m_generalTab );
+ //orientationLayout->add(orientationGroup);
+ orientationGroup->setRadioButtonExclusive(true);
+ QLabel* qlb = new QLabel( i18n("Slider Orientation: "), m_generalTab );
+ _rbHorizontal = new QRadioButton(i18n("&Horizontal"), m_generalTab );
+ _rbVertical = new QRadioButton(i18n("&Vertical" ), m_generalTab );
+ orientationGroup->insert(_rbHorizontal);
+ orientationGroup->insert(_rbVertical);
+ orientationGroup->hide();
+ //orientationLayout->add(qlb);
+ //orientationLayout->add(orientationGroup);
+
+ orientationLayout->add(qlb);
+ orientationLayout->add(_rbHorizontal);
+ orientationLayout->add(_rbVertical);
+
+ orientationLayout->addStretch();
+ layout->addStretch();
+ enableButtonSeparator(true);
+
+ connect( this, SIGNAL(applyClicked()), this, SLOT(apply()) );
+ connect( this, SIGNAL(okClicked()), this, SLOT(apply()) );
+}
+
+KMixPrefDlg::~KMixPrefDlg()
+{
+}
+
+void KMixPrefDlg::apply()
+{
+ // disabling buttons => users sees that we are working
+ enableButtonOK(false);
+ enableButtonCancel(false);
+ enableButtonApply(false);
+ kapp->processEvents();
+ emit signalApplied( this );
+ // re-enable (in case of "Apply")
+ enableButtonOK(true);
+ enableButtonCancel(true);
+ enableButtonApply(true);
+}
+
+#include "kmixprefdlg.moc"
diff --git a/kmix/kmixprefdlg.h b/kmix/kmixprefdlg.h
new file mode 100644
index 00000000..3519d045
--- /dev/null
+++ b/kmix/kmixprefdlg.h
@@ -0,0 +1,67 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef KPREFDLG_H
+#define KPREFDLG_H
+
+#include <kdialogbase.h>
+
+class KMixPrefWidget;
+class KMixApp;
+class QCheckBox;
+class QRadioButton;
+
+class
+KMixPrefDlg : public KDialogBase
+{
+ Q_OBJECT
+
+ friend class KMixWindow;
+
+ public:
+ KMixPrefDlg( QWidget *parent );
+ ~KMixPrefDlg();
+
+ signals:
+ void signalApplied( KMixPrefDlg *prefDlg );
+
+ private slots:
+ void apply();
+
+ private:
+ QFrame *m_generalTab;
+ KMixApp *m_mixApp;
+ KMixPrefWidget *m_mixPrefTab;
+
+ QCheckBox *m_dockingChk;
+ QCheckBox *m_volumeChk;
+ QCheckBox *m_hideOnCloseChk;
+ QCheckBox *m_showTicks;
+ QCheckBox *m_showLabels;
+ QCheckBox *m_onLogin;
+ QRadioButton *_rbVertical;
+ QRadioButton *_rbHorizontal;
+ QRadioButton *_rbNone;
+ QRadioButton *_rbAbsolute;
+ QRadioButton *_rbRelative;
+};
+
+#endif
diff --git a/kmix/kmixtoolbox.cpp b/kmix/kmixtoolbox.cpp
new file mode 100644
index 00000000..06a6ed36
--- /dev/null
+++ b/kmix/kmixtoolbox.cpp
@@ -0,0 +1,215 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#include "qcolor.h"
+#include "qwidget.h"
+#include "qstring.h"
+
+//#include <kdebug.h>
+#include <kglobalaccel.h>
+#include <klocale.h>
+
+#include "mdwslider.h"
+#include "mdwswitch.h"
+#include "mixdevicewidget.h"
+#include "mixdevice.h"
+#include "mixer.h"
+
+#include "kmixtoolbox.h"
+
+/***********************************************************************************
+ KMixToolbox contains several GUI relevant methods that are shared between the
+ KMix Main Program, and the KMix Applet.
+ kmixctrl - as not non-GUI application - does NOT link to KMixToolBox.
+
+ This means: Shared GUI stuff goes into the KMixToolBox class , non-GUI stuff goes
+ into the MixerToolBox class.
+ ***********************************************************************************/
+void KMixToolBox::setIcons(QPtrList<QWidget> &mdws, bool on ) {
+ for ( QWidget *qmdw=mdws.first(); qmdw!=0; qmdw=mdws.next() ) {
+ if ( qmdw->inherits("MixDeviceWidget") ) { // -<- play safe here
+ static_cast<MixDeviceWidget*>(qmdw)->setIcons( on );
+ }
+ }
+}
+
+void KMixToolBox::setLabels(QPtrList<QWidget> &mdws, bool on ) {
+ QWidget *qmdw;
+ for ( qmdw=mdws.first(); qmdw != 0; qmdw=mdws.next() ) {
+ if ( qmdw->inherits("MixDeviceWidget") ) { // -<- play safe here
+ static_cast<MixDeviceWidget*>(qmdw)->setLabeled( on );
+ }
+ }
+}
+
+void KMixToolBox::setTicks(QPtrList<QWidget> &mdws, bool on ) {
+ QWidget *qmdw;
+ for ( qmdw=mdws.first(); qmdw != 0; qmdw=mdws.next() ) {
+ if ( qmdw->inherits("MixDeviceWidget") ) { // -<- in reality it is only in MDWSlider
+ static_cast<MixDeviceWidget*>(qmdw)->setTicks( on );
+ }
+ }
+}
+
+void KMixToolBox::setValueStyle(QPtrList<QWidget> &mdws, int vs ) {
+ QWidget *qmdw;
+ for ( qmdw=mdws.first(); qmdw != 0; qmdw=mdws.next() ) {
+ if ( qmdw->inherits("MixDeviceWidget") ) { // -<- in reality it is only in MDWSlider
+ static_cast<MixDeviceWidget*>(qmdw)->setValueStyle( (MixDeviceWidget::ValueStyle) vs );
+ }
+ }
+}
+
+void KMixToolBox::loadConfig(QPtrList<QWidget> &mdws, KConfig *config, const QString &grp, const QString &viewPrefix) {
+ int n = 0;
+ config->setGroup( grp );
+ int num = config->readNumEntry( viewPrefix + ".Devs", 0);
+
+ for ( QWidget *qmdw=mdws.first(); qmdw!=0 && n<num; qmdw=mdws.next() ) {
+ if ( qmdw->inherits("MixDeviceWidget") ) { // -<- play safe here
+ MixDeviceWidget* mdw = static_cast<MixDeviceWidget*>(qmdw);
+ QString devgrp;
+
+ /*
+ * Compatibility config loader! We use the old config group only, if the
+ * new one does not exist.
+ * The new group system has been introduced, because it accounts much
+ * better for soundcard driver updates (if numbering changes, or semantics
+ * of an ID changes like ALSA changing from "Disable Amplifier" to "External Amplifier").
+ */
+ // !!! check
+ devgrp.sprintf( "%s.%s.Dev%s", viewPrefix.ascii(), grp.ascii(), mdw->mixDevice()->getPK().ascii() );
+
+ /**
+ Find an appropriate group name for capture GUI elements.
+ We try devgrp.append(".Capture")
+ If it doesn't exist, we fall back to devgrp.
+ This is the second compatibility measure, and was introduced for KDE3.5.2.
+ */
+ if ( mdw->mixDevice()->getVolume().isCapture() ) {
+ /* A "capture" GUI element must save its own state. Otherwise playback and capture
+ properties would be written twice under the same name. This would mean, when
+ restoring, both would get the same value. This is bad, because hidden sliders will re-appear
+ after restart of KMix, and a lot of other nasty GUI-related problems.
+ So we add ".Capture" to the group name.
+ See bug 121451 "KMix panel applet shows broken duplicates of bass, treble sliders"
+
+ The name should have been set in the backend class, but we REALLY cannot do this for KDE3.5.x. !!
+ This issue will be fixed in KDE4 by the great config cleanup.
+ */
+ QString devgrpTmp(devgrp);
+ devgrpTmp.append(".Capture");
+ if ( config->hasGroup(devgrpTmp) ) {
+ // Group for capture device exists => take over the name
+ devgrp = devgrpTmp;
+ }
+ else {
+ // do nothing => keep old name (devgrp).
+ // Saving wil autmatically create the group 'devgrp.append(".Capture")'
+ kdDebug(67100) << "KMixToolBox::loadConfig() capture fallback activcated. Fallback group is " << devgrp << endl;
+ }
+ } // isCapture()
+ if ( ! config->hasGroup(devgrp) ) {
+ // fall back to old-Style configuration (KMix2.1 and earlier)
+ devgrp.sprintf( "%s.%s.Dev%i", viewPrefix.ascii(), grp.ascii(), n );
+ // this configuration group will be deleted when config is saved
+ }
+ config->setGroup( devgrp );
+
+ if ( qmdw->inherits("MixDeviceWidget") ) { // -<- in reality it is only in MDWSlider
+ // only sliders have the ability to split apart in mutliple channels
+ bool splitChannels = config->readBoolEntry("Split", false);
+ mdw->setStereoLinked( !splitChannels );
+ }
+ mdw->setDisabled( !config->readBoolEntry("Show", true) );
+
+ KGlobalAccel *keys=mdw->keys();
+ if ( keys )
+ {
+ QString devgrpkeys;
+ devgrpkeys.sprintf( "%s.%s.Dev%i.keys", viewPrefix.ascii(), grp.ascii(), n );
+ //kdDebug(67100) << "KMixToolBox::loadConfig() load Keys " << devgrpkeys << endl;
+
+ // please see KMixToolBox::saveConfig() for some rambling about saving/loading Keys
+ keys->setConfigGroup(devgrpkeys);
+ keys->readSettings(config);
+ keys->updateConnections();
+ }
+
+ n++;
+ } // if it is a MixDeviceWidget
+ } // for all widgets
+}
+
+
+void KMixToolBox::saveConfig(QPtrList<QWidget> &mdws, KConfig *config, const QString &grp, const QString &viewPrefix) {
+ config->setGroup( grp );
+ config->writeEntry( viewPrefix + ".Devs", mdws.count() );
+
+ int n=0;
+ for ( QWidget *qmdw=mdws.first(); qmdw!=0; qmdw=mdws.next() ) {
+ if ( qmdw->inherits("MixDeviceWidget") ) { // -<- play safe here
+ MixDeviceWidget* mdw = static_cast<MixDeviceWidget*>(qmdw);
+
+ QString devgrp;
+ devgrp.sprintf( "%s.%s.Dev%i", viewPrefix.ascii(), grp.ascii(), n );
+ if ( ! config->hasGroup(devgrp) ) {
+ // old-Style configuration (KMix2.1 and earlier => remove now unused group
+ config->deleteGroup(devgrp);
+ }
+ devgrp.sprintf( "%s.%s.Dev%s", viewPrefix.ascii(), grp.ascii(), mdw->mixDevice()->getPK().ascii() );
+ //devgrp.sprintf( "%s.%s.Dev%i", viewPrefix.ascii(), grp.ascii(), n );
+
+ if ( mdw->mixDevice()->getVolume().isCapture() ) {
+ /* see loadConfig() for the rationale of having an own name for capture devices. */
+ devgrp.append(".Capture");
+ } // isCapture()
+
+ config->setGroup( devgrp );
+
+ if ( qmdw->inherits("MixDeviceWidget") ) { // -<- in reality it is only in MDWSlider
+ // only sliders have the ability to split apart in mutliple channels
+ config->writeEntry( "Split", ! mdw->isStereoLinked() );
+ }
+ config->writeEntry( "Show" , ! mdw->isDisabled() );
+
+ // Save key bindings
+ /*
+ Implementation hint: Conceptually keys SHOULD be bound to the actual hardware, and not
+ to one GUI representation. Both work, but it COULD confuse users, if we have multiple
+ GUI representations (e.g. "Dock Icon" and "Main Window").
+ If you think about this aspect more deeply, you will find out that this is the case already
+ today with "kmixapplet" and "kmix main application". It would really nice to rework this.
+ */
+ KGlobalAccel *keys=mdw->keys();
+ if (keys) {
+ QString devgrpkeys;
+ devgrpkeys.sprintf( "%s.%s.Dev%i.keys", viewPrefix.ascii(), grp.ascii(), n );
+ //kdDebug(67100) << "KMixToolBox::saveConfig() save Keys " << devgrpkeys << endl;
+ keys->setConfigGroup(devgrpkeys);
+ keys->writeSettings(config);
+ }
+ n++;
+ } // if it is a MixDeviceWidget
+ } // for all widgets
+}
+
diff --git a/kmix/kmixtoolbox.h b/kmix/kmixtoolbox.h
new file mode 100644
index 00000000..4cb5dd8f
--- /dev/null
+++ b/kmix/kmixtoolbox.h
@@ -0,0 +1,28 @@
+#ifndef KMIXTOOLBOX_H
+#define KMIXTOOLBOX_H
+
+#include "qptrlist.h"
+#include "qwidget.h"
+
+class Mixer;
+
+class KConfig;
+
+/**
+ * This toolbox contains various static methods that are shared throughout KMix.
+ * The reason, why it is not put in a common base class is, that the classes are
+ * very different and cannot be changed (e.g. KPanelApplet) without major headache.
+ */
+
+class KMixToolBox {
+ public:
+ static void setIcons (QPtrList<QWidget> &mdws, bool on );
+ static void setLabels (QPtrList<QWidget> &mdws, bool on );
+ static void setTicks (QPtrList<QWidget> &mdws, bool on );
+ static void setValueStyle (QPtrList<QWidget> &mdws, int vs );
+ static void loadConfig(QPtrList<QWidget> &mdws, KConfig *config, const QString &grp, const QString &viewPrefix );
+ static void saveConfig(QPtrList<QWidget> &mdws, KConfig *config, const QString &grp, const QString &viewPrefix );
+};
+
+
+#endif
diff --git a/kmix/kmixui.rc b/kmix/kmixui.rc
new file mode 100644
index 00000000..aada37d8
--- /dev/null
+++ b/kmix/kmixui.rc
@@ -0,0 +1,21 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kmix" version="3">
+
+<ActionProperties>
+ <Action name="hide_kmixwindow" icon="window"/>
+</ActionProperties>
+
+<MenuBar>
+ <Menu name="file"><text>&amp;File</text>
+ <Action name="file_new_tab"/>
+ <Action name="file_close_tab"/>
+ </Menu>
+ <Menu name="settings">
+ <Action name="settings_global" append="configure_merge"/>
+ </Menu>
+ <Menu name="help" append="about_merge"><text>&amp;Help</text>
+ <Action name="hwinfo"/>
+ </Menu>
+</MenuBar>
+
+</kpartgui>
diff --git a/kmix/ksmallslider.cpp b/kmix/ksmallslider.cpp
new file mode 100644
index 00000000..38e43639
--- /dev/null
+++ b/kmix/ksmallslider.cpp
@@ -0,0 +1,516 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <kdebug.h>
+
+#include <qwidget.h>
+#include <qpainter.h>
+#include <qcolor.h>
+#include <qbrush.h>
+#include <qstyle.h>
+
+#include "kglobalsettings.h"
+#include "ksmallslider.h"
+
+/*
+static const QColor mutedHighColor2 = "#FFFFFF";
+static const QColor mutedLowColor2 = "#808080";
+static const QColor backColor2 = "#000000";
+*/
+
+KSmallSlider::KSmallSlider( QWidget *parent, const char *name )
+ : QWidget( parent, name ), _orientation( Qt::Vertical )
+{
+ init();
+}
+
+KSmallSlider::KSmallSlider( Qt::Orientation orientation, QWidget *parent, const char *name )
+ : QWidget( parent, name ), _orientation( orientation )
+{
+ init();
+}
+
+KSmallSlider::KSmallSlider( int minValue, int maxValue, int pageStep,
+ int value, Qt::Orientation orientation,
+ QWidget *parent, const char *name )
+ : QWidget( parent, name ),
+ QRangeControl( minValue, maxValue, 1, pageStep, value ), _orientation( orientation)
+{
+ init();
+ // sliderVal = value;
+}
+
+void KSmallSlider::init()
+{
+ // !! the following 2 values must be -1, to make sure the values are not the real values.
+ // Otherwise some code below could determine that no change has happened and to send
+ // no signals or to do no initial paint.
+ // sliderPos = -1;
+ // state = Idle;
+ //track = TRUE;
+ //setMouseTracking(true);
+ grayed = false;
+ setFocusPolicy( TabFocus );
+
+ colHigh = QColor(0,255,0);
+ colLow = QColor(255,0,0);
+ colBack = QColor(0,0,0);
+
+ grayHigh = QColor(255,255,255);
+ grayLow = QColor(128,128,128);
+ grayBack = QColor(0,0,0);
+}
+/*
+void KSmallSlider::setTracking( bool enable )
+{
+ track = enable;
+}
+*/
+int KSmallSlider::positionFromValue( int v ) const
+{
+ return QRangeControl::positionFromValue( v, available() );
+}
+
+int KSmallSlider::valueFromPosition( int p ) const
+{
+ if ( _orientation == Qt::Vertical ) {
+ // Coordiante System starts at TopLeft, but the slider values increase from Bottom to Top
+ // Thus "revert" the position
+ int avail = available();
+ return QRangeControl::valueFromPosition( avail - p, avail );
+ }
+ else {
+ // Horizontal everything is fine. Slider values match with Coordinate System
+ return QRangeControl::valueFromPosition( p, available() );
+ }
+}
+
+void KSmallSlider::rangeChange()
+{
+ /*
+ int newPos = positionFromValue( QRangeControl::value() );
+ if ( newPos != sliderPos ) {
+ sliderPos = newPos;
+ }
+ */
+ update();
+}
+
+void KSmallSlider::valueChange()
+{
+ //kdDebug(67100) << "KSmallSlider::valueChange() value=" << value() << "\n";
+ update();
+ emit valueChanged(value());
+ /*
+ if ( sliderVal != QRangeControl::value() ) {
+ //int newPos = positionFromValue( QRangeControl::value() );
+ //sliderPos = newPos;
+ sliderVal = QRangeControl::value();
+ update();
+ emit valueChanged(value());
+ }
+ */
+}
+
+void KSmallSlider::resizeEvent( QResizeEvent * )
+{
+ update();
+ //QWidget::resizeEvent( ev );
+}
+
+// Returns the really available space for the slider. If there is no space, 0 is returned;
+int KSmallSlider::available() const
+{
+ int available = 0;
+ if ( _orientation == Qt::Vertical) {
+ available = height();
+ }
+ else {
+ available = width();
+ }
+ if ( available > 1 ) {
+ available -= 2;
+ }
+ else {
+ available = 0;
+ }
+ return available;
+}
+
+
+
+namespace
+{
+
+void gradient( QPainter &p, bool hor, const QRect &rect, const QColor &ca, const QColor &cb, int /*ncols*/)
+{
+ int rDiff, gDiff, bDiff;
+ int rca, gca, bca, rcb, gcb, bcb;
+
+ register int x, y;
+
+ if ((rect.width()<=0) || (rect.height()<=0)) return;
+
+ rDiff = (rcb = cb.red()) - (rca = ca.red());
+ gDiff = (gcb = cb.green()) - (gca = ca.green());
+ bDiff = (bcb = cb.blue()) - (bca = ca.blue());
+
+ register int rl = rca << 16;
+ register int gl = gca << 16;
+ register int bl = bca << 16;
+
+ int rcdelta = ((1<<16) / ((!hor) ? rect.height() : rect.width())) * rDiff;
+ int gcdelta = ((1<<16) / ((!hor) ? rect.height() : rect.width())) * gDiff;
+ int bcdelta = ((1<<16) / ((!hor) ? rect.height() : rect.width())) * bDiff;
+
+ // these for-loops could be merged, but the if's in the inner loop
+ // would make it slow
+ if (!hor)
+ {
+ for ( y = rect.top(); y <= rect.bottom(); y++ ) {
+ rl += rcdelta;
+ gl += gcdelta;
+ bl += bcdelta;
+
+ p.setPen(QColor(rl>>16, gl>>16, bl>>16));
+ p.drawLine(rect.left(), y, rect.right(), y);
+ }
+ } else
+ {
+ for( x = rect.left(); x <= rect.right(); x++) {
+ rl += rcdelta;
+ gl += gcdelta;
+ bl += bcdelta;
+
+ p.setPen(QColor(rl>>16, gl>>16, bl>>16));
+ p.drawLine(x, rect.top(), x, rect.bottom());
+ }
+ }
+}
+
+QColor interpolate( QColor low, QColor high, int percent ) {
+ if ( percent<=0 ) return low; else
+ if ( percent>=100 ) return high; else
+ return QColor(
+ low.red() + (high.red()-low.red()) * percent/100,
+ low.green() + (high.green()-low.green()) * percent/100,
+ low.blue() + (high.blue()-low.blue()) * percent/100 );
+}
+
+}
+
+void KSmallSlider::paintEvent( QPaintEvent * )
+{
+// kdDebug(67100) << "KSmallSlider::paintEvent: width() = " << width() << ", height() = " << height() << endl;
+ QPainter p( this );
+
+ int sliderPos = positionFromValue( QRangeControl::value() );
+
+ // ------------------------ draw 3d border ---------------------------------------------
+ style().drawPrimitive ( QStyle::PE_Panel, &p, QRect( 0, 0, width(), height() ), colorGroup(), TRUE );
+
+
+ // ------------------------ draw lower/left part ----------------------------------------
+ if ( width()>2 && height()>2 )
+ {
+ if ( _orientation == Qt::Horizontal ) {
+ QRect outer = QRect( 1, 1, sliderPos, height() - 2 );
+// kdDebug(67100) << "KSmallSlider::paintEvent: outer = " << outer << endl;
+
+ if ( grayed )
+ gradient( p, true, outer, grayLow,
+ interpolate( grayLow, grayHigh, 100*sliderPos/(width()-2) ),
+ 32 );
+ else
+ gradient( p, true, outer, colLow,
+ interpolate( colLow, colHigh, 100*sliderPos/(width()-2) ),
+ 32 );
+ }
+ else {
+ QRect outer = QRect( 1, height()-sliderPos-1, width() - 2, sliderPos-1 );
+/*
+ kdDebug(67100) << "KSmallSlider::paintEvent: sliderPos=" << sliderPos
+ << "height()=" << height()
+ << "width()=" << width()
+ << "outer = " << outer << endl;
+*/
+ if ( grayed )
+ gradient( p, false, outer,
+ interpolate( grayLow, grayHigh, 100*sliderPos/(height()-2) ),
+ grayLow, 32 );
+ else
+ gradient( p, false, outer,
+ interpolate( colLow, colHigh, 100*sliderPos/(height()-2) ),
+ colLow, 32 );
+ }
+
+ // -------- draw upper/right part --------------------------------------------------
+ QRect inner;
+ if ( _orientation == Qt::Vertical ) {
+ inner = QRect( 1, 1, width() - 2, height() - 2 -sliderPos );
+ }
+ else {
+ inner = QRect( sliderPos + 1, 1, width() - 2 - sliderPos, height() - 2 );
+ }
+
+ if ( grayed ) {
+ p.setBrush( grayBack );
+ p.setPen( grayBack );
+ } else {
+ p.setBrush( colBack );
+ p.setPen( colBack );
+ }
+ p.drawRect( inner );
+ }
+}
+
+void KSmallSlider::mousePressEvent( QMouseEvent *e )
+{
+ //resetState();
+
+ if ( e->button() == RightButton ) {
+ return;
+ }
+
+ // state = Dragging;
+ //emit sliderPressed();
+
+ int pos = goodPart( e->pos() );
+ moveSlider( pos );
+}
+
+void KSmallSlider::mouseMoveEvent( QMouseEvent *e )
+{
+ /*
+ if ( state != Dragging )
+ return;
+ */
+ int pos = goodPart( e->pos() );
+ moveSlider( pos );
+}
+
+void KSmallSlider::wheelEvent( QWheelEvent * e)
+{
+// kdDebug(67100) << "KSmallslider::wheelEvent()" << endl;
+ /* Unfortunately KSmallSlider is no MixDeviceWidget, so we don't have access to
+ * the MixDevice.
+ */
+ int inc = ( maxValue() - minValue() ) / 20;
+ if ( inc < 1)
+ inc = 1;
+
+ //kdDebug(67100) << "KSmallslider::wheelEvent() inc=" << inc << "delta=" << e->delta() << endl;
+ if ( e->delta() > 0 ) {
+ QRangeControl::setValue( QRangeControl::value() + inc );
+ }
+ else {
+ QRangeControl::setValue( QRangeControl::value() - inc );
+ }
+ e->accept(); // Accept the event
+
+ // Hint: Qt autmatically triggers a valueChange() when we do setValue()
+}
+
+void KSmallSlider::mouseReleaseEvent( QMouseEvent * )
+{
+ //resetState();
+}
+
+/*
+ * Moves slider to a dedicated position. If the value has changed
+ */
+void KSmallSlider::moveSlider( int pos )
+{
+ int a = available();
+ int newPos = QMIN( a, QMAX( 0, pos ) ); // keep it inside the available bounds of the slider
+ int newVal = valueFromPosition( newPos );
+
+ if ( newVal != QRangeControl::value() ) {
+ //QRangeControl::directSetValue( sliderVal );
+ QRangeControl::setValue( newVal );
+ emit valueChanged( value() ); // Only for external use
+ }
+ update();
+}
+
+/*
+void KSmallSlider::resetState()
+{
+ switch ( state ) {
+ case Dragging: {
+ QRangeControl::setValue( valueFromPosition( sliderPos ) );
+ emit sliderReleased();
+ break;
+ }
+ case Idle:
+ break;
+
+ default:
+ qWarning("KSmallSlider: (%s) in wrong state", name( "unnamed" ) );
+ }
+ state = Idle;
+}
+*/
+
+void KSmallSlider::setValue( int value )
+{
+ QRangeControl::setValue( value );
+}
+
+void KSmallSlider::addStep()
+{
+ addPage();
+}
+
+void KSmallSlider::subtractStep()
+{
+ subtractPage();
+}
+
+int KSmallSlider::goodPart( const QPoint &p ) const
+{
+ if ( _orientation == Qt::Vertical ) {
+ return p.y() - 1;
+ }
+ else {
+ return p.x() - 1;
+ }
+}
+
+/***************** SIZE STUFF START ***************/
+QSize KSmallSlider::sizeHint() const
+{
+ //constPolish();
+ const int length = 25;
+ const int thick = 10;
+
+ if ( _orientation == Qt::Vertical )
+ return QSize( thick, length );
+ else
+ return QSize( length, thick );
+}
+
+
+QSize KSmallSlider::minimumSizeHint() const
+{
+ QSize s(10,10);
+ return s;
+}
+
+
+QSizePolicy KSmallSlider::sizePolicy() const
+{
+
+ if ( _orientation == Qt::Vertical ) {
+ //kdDebug(67100) << "KSmallSlider::sizePolicy() vertical value=(Fixed,MinimumExpanding)\n";
+ return QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding );
+ }
+ else {
+ //kdDebug(67100) << "KSmallSlider::sizePolicy() horizontal value=(MinimumExpanding,Fixed)\n";
+ return QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
+ }
+}
+/***************** SIZE STUFF END ***************/
+
+
+int KSmallSlider::minValue() const
+{
+ return QRangeControl::minValue();
+}
+
+int KSmallSlider::maxValue() const
+{
+ return QRangeControl::maxValue();
+}
+
+int KSmallSlider::lineStep() const
+{
+ return QRangeControl::lineStep();
+}
+
+int KSmallSlider::pageStep() const
+{
+ return QRangeControl::pageStep();
+}
+
+void KSmallSlider::setLineStep( int i )
+{
+ setSteps( i, pageStep() );
+}
+
+void KSmallSlider::setPageStep( int i )
+{
+ setSteps( lineStep(), i );
+}
+
+// Only for external acces. You MUST use QRangeControl::value() internally.
+int KSmallSlider::value() const
+{
+ return QRangeControl::value();
+}
+
+/*
+void KSmallSlider::paletteChange ( const QPalette &) {
+ if ( grayed ) {
+ setColors(mutedLowColor2, mutedHighColor2, backColor2 );
+ }
+ else {
+ // ignore the QPalette and use the values from KGlobalSettings instead
+ //const QColorGroup& qcg = palette().active();
+ setColors(KGlobalSettings::baseColor(), KGlobalSettings::highlightColor(), backColor2 );
+ }
+}
+*/
+
+void KSmallSlider::setGray( bool value )
+{
+ if ( grayed!=value )
+ {
+ grayed = value;
+ update();
+ //repaint();
+ }
+}
+
+bool KSmallSlider::gray() const
+{
+ return grayed;
+}
+
+void KSmallSlider::setColors( QColor high, QColor low, QColor back )
+{
+ colHigh = high;
+ colLow = low;
+ colBack = back;
+ update();
+ //repaint();
+}
+
+void KSmallSlider::setGrayColors( QColor high, QColor low, QColor back )
+{
+ grayHigh = high;
+ grayLow = low;
+ grayBack = back;
+ update();
+ //repaint();
+}
+
+#include "ksmallslider.moc"
diff --git a/kmix/ksmallslider.h b/kmix/ksmallslider.h
new file mode 100644
index 00000000..398d7728
--- /dev/null
+++ b/kmix/ksmallslider.h
@@ -0,0 +1,119 @@
+//-*-C++-*-
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef KSMALLSLIDER_H
+#define KSMALLSLIDER_H
+
+#include <kpanelapplet.h>
+
+#include <qwidget.h>
+#include <qpixmap.h>
+#include <qrangecontrol.h>
+
+class KSmallSlider : public QWidget, public QRangeControl
+{
+ Q_OBJECT
+
+ public:
+ KSmallSlider( QWidget *parent, const char *name=0 );
+ KSmallSlider( Qt::Orientation, QWidget *parent, const char *name=0 );
+ KSmallSlider( int minValue, int maxValue, int pageStep, int value,
+ Qt::Orientation, QWidget *parent, const char *name=0 );
+
+ //virtual void setTracking( bool enable );
+ //bool tracking() const;
+ QSize sizeHint() const;
+ QSizePolicy sizePolicy() const;
+ QSize minimumSizeHint() const;
+
+ int minValue() const;
+ int maxValue() const;
+ void setMinValue( int ); // Don't use these unless you make versions
+ void setMaxValue( int ); // that work. -esigra
+ int lineStep() const;
+ int pageStep() const;
+ void setLineStep( int );
+ void setPageStep( int );
+ int value() const;
+
+ //void paletteChange ( const QPalette & oldPalette );
+ bool gray() const;
+
+public slots:
+ virtual void setValue( int );
+ void addStep();
+ void subtractStep();
+
+ void setGray( bool value );
+ void setColors( QColor high, QColor low, QColor back );
+ void setGrayColors( QColor high, QColor low, QColor back );
+
+ signals:
+ void valueChanged( int value );
+ void sliderPressed();
+ void sliderMoved( int value );
+ void sliderReleased();
+
+ protected:
+ void resizeEvent( QResizeEvent * );
+ void paintEvent( QPaintEvent * );
+
+ void mousePressEvent( QMouseEvent * );
+ void mouseReleaseEvent( QMouseEvent * );
+ void mouseMoveEvent( QMouseEvent * );
+ void wheelEvent( QWheelEvent * );
+
+ void valueChange();
+ void rangeChange();
+
+ private:
+ //enum State { Idle, Dragging };
+
+ void init();
+ int positionFromValue( int ) const;
+ int valueFromPosition( int ) const;
+ void moveSlider( int );
+ //void resetState();
+
+ // int slideLength() const;
+ int available() const;
+ int goodPart( const QPoint& ) const;
+ //void initTicks();
+
+ //QCOORD sliderPos;
+ //int sliderVal;
+ //State state;
+ //bool track;
+ bool grayed;
+ Qt::Orientation _orientation;
+ QColor colHigh, colLow, colBack;
+ QColor grayHigh, grayLow, grayBack;
+
+};
+
+/*
+inline bool KSmallSlider::tracking() const
+{
+ return track;
+}
+*/
+#endif
diff --git a/kmix/main.cpp b/kmix/main.cpp
new file mode 100644
index 00000000..3842ecaa
--- /dev/null
+++ b/kmix/main.cpp
@@ -0,0 +1,69 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2000 Stefan Schimanski <schimmi@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <kcmdlineargs.h>
+#include <kaboutdata.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+
+#include "KMixApp.h"
+#include "version.h"
+
+static const char description[] =
+I18N_NOOP("KMix - KDE's full featured mini mixer");
+
+static KCmdLineOptions options[] =
+{
+ KCmdLineLastOption
+ // INSERT YOUR COMMANDLINE OPTIONS HERE
+};
+
+extern "C" KDE_EXPORT int kdemain(int argc, char *argv[])
+{
+ KAboutData aboutData( "kmix", I18N_NOOP("KMix"),
+ APP_VERSION, description, KAboutData::License_GPL,
+ I18N_NOOP("(c) 1996-2000 Christian Esken\n(c) 2000-2003 Christian Esken, Stefan Schimanski\n(c) 2002-2005 Christian Esken, Helio Chissini de Castro"));
+
+ aboutData.addAuthor("Christian Esken", "Current maintainer", "esken@kde.org");
+ aboutData.addAuthor("Helio Chissini de Castro", I18N_NOOP("Current redesign and co-maintainer, Alsa 0.9x port"), "helio@kde.org" );
+ aboutData.addAuthor("Stefan Schimanski", 0, "schimmi@kde.org");
+ aboutData.addAuthor("Sven Leiber", 0, "s.leiber@web.de");
+ aboutData.addAuthor("Brian Hanson", I18N_NOOP("Solaris port"), "bhanson@hotmail.com");
+ aboutData.addAuthor("Paul Kendall", I18N_NOOP("SGI Port"), "paul@orion.co.nz");
+ aboutData.addAuthor("Sebestyen Zoltan", I18N_NOOP("*BSD fixes"), "szoli@digo.inf.elte.hu");
+ aboutData.addAuthor("Lennart Augustsson", I18N_NOOP("*BSD fixes"), "augustss@cs.chalmers.se");
+ aboutData.addAuthor("Nick Lopez", I18N_NOOP("ALSA port"), "kimo_sabe@usa.net");
+ aboutData.addAuthor("Helge Deller", I18N_NOOP("HP/UX port"), "deller@gmx.de");
+ aboutData.addAuthor("Jean Labrousse", I18N_NOOP("NAS port"), "jean.labrousse@alcatel.com" );
+ aboutData.addCredit("Nadeem Hasan", I18N_NOOP("Mute and volume preview, other fixes"), "nhasan@kde.org");
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.
+
+ if (!KMixApp::start())
+ return 0;
+
+ KMixApp *app = new KMixApp();
+ int ret = app->exec();
+ delete app;
+ return ret;
+}
diff --git a/kmix/mdwenum.cpp b/kmix/mdwenum.cpp
new file mode 100644
index 00000000..91220902
--- /dev/null
+++ b/kmix/mdwenum.cpp
@@ -0,0 +1,206 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <qcursor.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qobject.h>
+#include <qtooltip.h>
+
+#include <klocale.h>
+#include <kconfig.h>
+#include <kcombobox.h>
+#include <kaction.h>
+#include <kpopupmenu.h>
+
+#include <kglobalaccel.h>
+#include <kkeydialog.h>
+
+#include <kdebug.h>
+
+#include "mdwenum.h"
+#include "mixer.h"
+#include "viewbase.h"
+
+/**
+ * Class that represents an Enum element (a select one-from-many selector)
+ * The orientation (horizontal, vertical) is ignored
+ */
+MDWEnum::MDWEnum(Mixer *mixer, MixDevice* md,
+ Qt::Orientation orientation,
+ QWidget* parent, ViewBase* mw, const char* name) :
+ MixDeviceWidget(mixer,md,false,orientation,parent,mw,name),
+ _label(0), _enumCombo(0), _layout(0)
+{
+ // create actions (on _mdwActions, see MixDeviceWidget)
+
+ // KStdAction::showMenubar() is in MixDeviceWidget now
+ new KToggleAction( i18n("&Hide"), 0, this, SLOT(setDisabled()), _mdwActions, "hide" );
+ new KAction( i18n("C&onfigure Shortcuts..."), 0, this, SLOT(defineKeys()), _mdwActions, "keys" );
+
+ // create widgets
+ createWidgets();
+
+ /* !!! remove this for production version */
+ m_keys->insert( "Next Value", i18n( "Next Value" ), QString::null,
+ KShortcut(), KShortcut(), this, SLOT( nextEnumId() ) );
+
+ installEventFilter( this ); // filter for popup
+}
+
+MDWEnum::~MDWEnum()
+{
+}
+
+
+void MDWEnum::createWidgets()
+{
+ if ( _orientation == Qt::Vertical ) {
+ _layout = new QVBoxLayout( this );
+ _layout->setAlignment(Qt::AlignHCenter);
+ }
+ else {
+ _layout = new QHBoxLayout( this );
+ _layout->setAlignment(Qt::AlignVCenter);
+ }
+ QToolTip::add( this, m_mixdevice->name() );
+
+ //this->setStretchFactor( _layout, 0 );
+ //QSizePolicy qsp( QSizePolicy::Ignored, QSizePolicy::Maximum);
+ //_layout->setSizePolicy(qsp);
+ //_layout->setSpacing(KDialog::spacingHint());
+ _label = new QLabel( m_mixdevice->name(), this);
+ _layout->addWidget(_label);
+ _label->setFixedHeight(_label->sizeHint().height());
+ _enumCombo = new KComboBox( FALSE, this, "mixerCombo" );
+ // ------------ fill ComboBox start ------------
+ int maxEnumId= m_mixdevice->enumValues().count();
+ for (int i=0; i<maxEnumId; i++ ) {
+ _enumCombo->insertItem( *(m_mixdevice->enumValues().at(i)),i);
+ }
+ // ------------ fill ComboBox end --------------
+ _layout->addWidget(_enumCombo);
+ _enumCombo->setFixedHeight(_enumCombo->sizeHint().height());
+ connect( _enumCombo, SIGNAL( activated( int ) ), this, SLOT( setEnumId( int ) ) );
+ QToolTip::add( _enumCombo, m_mixdevice->name() );
+
+ //_layout->addSpacing( 4 );
+}
+
+void MDWEnum::update()
+{
+ if ( m_mixdevice->isEnum() ) {
+ //kdDebug(67100) << "MDWEnum::update() enumID=" << m_mixdevice->enumId() << endl;
+ _enumCombo->setCurrentItem( m_mixdevice->enumId() );
+ }
+ else {
+ // !!! print warning message
+ }
+}
+
+void MDWEnum::showContextMenu()
+{
+ if( m_mixerwidget == NULL )
+ return;
+
+ KPopupMenu *menu = m_mixerwidget->getPopup();
+
+ QPoint pos = QCursor::pos();
+ menu->popup( pos );
+}
+
+QSize MDWEnum::sizeHint() const {
+ if ( _layout != 0 ) {
+ return _layout->sizeHint();
+ }
+ else {
+ // layout not (yet) created
+ return QWidget::sizeHint();
+ }
+}
+
+
+/**
+ This slot is called, when a user has clicked the mute button. Also it is called by any other
+ associated KAction like the context menu.
+*/
+void MDWEnum::nextEnumId() {
+ if( m_mixdevice->isEnum() ) {
+ int curEnum = enumId();
+ if ( (unsigned int)curEnum < m_mixdevice->enumValues().count() ) {
+ // next enum value
+ setEnumId(curEnum+1);
+ }
+ else {
+ // wrap around
+ setEnumId(0);
+ }
+ } // isEnum
+}
+
+void MDWEnum::setEnumId(int value)
+{
+ if ( m_mixdevice->isEnum() ) {
+ m_mixdevice->setEnumId( value );
+ m_mixer->commitVolumeChange( m_mixdevice );
+ }
+}
+
+int MDWEnum::enumId()
+{
+ if ( m_mixdevice->isEnum() ) {
+ return m_mixdevice->enumId();
+ }
+ else {
+ return 0;
+ }
+}
+
+void MDWEnum::setDisabled()
+{
+ setDisabled( true );
+}
+
+void MDWEnum::setDisabled( bool value ) {
+ if ( m_disabled!=value)
+ {
+ value ? hide() : show();
+ m_disabled = value;
+ }
+}
+
+/**
+ * An event filter for the various QWidgets. We watch for Mouse press Events, so
+ * that we can popup the context menu.
+ */
+bool MDWEnum::eventFilter( QObject* obj, QEvent* e )
+{
+ if (e->type() == QEvent::MouseButtonPress) {
+ QMouseEvent *qme = static_cast<QMouseEvent*>(e);
+ if (qme->button() == Qt::RightButton) {
+ showContextMenu();
+ return true;
+ }
+ }
+ return QWidget::eventFilter(obj,e);
+}
+
+#include "mdwenum.moc"
diff --git a/kmix/mdwenum.h b/kmix/mdwenum.h
new file mode 100644
index 00000000..09b2d0a4
--- /dev/null
+++ b/kmix/mdwenum.h
@@ -0,0 +1,77 @@
+//-*-C++-*-
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2004 Chrisitan Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef MDWENUM_H
+#define MDWENUM_H
+
+#include <qwidget.h>
+#include "volume.h"
+
+class QBoxLayout;
+
+class KAction;
+class KActionCollection;
+class KComboBox;
+class KGlobalAccel;
+
+class MixDevice;
+class Mixer;
+class ViewBase;
+
+#include "mixdevicewidget.h"
+
+class MDWEnum : public MixDeviceWidget
+{
+ Q_OBJECT
+
+public:
+ MDWEnum( Mixer *mixer, MixDevice* md,
+ Qt::Orientation orientation,
+ QWidget* parent = 0, ViewBase* mw = 0, const char* name = 0);
+ ~MDWEnum();
+
+ void addActionToPopup( KAction *action );
+ QSize sizeHint() const;
+ bool eventFilter( QObject* obj, QEvent* e );
+
+public slots:
+ // GUI hide and show
+ void setDisabled();
+ void setDisabled(bool);
+
+ // Enum handling: next and selecting
+ void nextEnumId();
+ int enumId();
+ void setEnumId(int value);
+
+ void update();
+ virtual void showContextMenu();
+
+private:
+ void createWidgets();
+
+ QLabel *_label;
+ KComboBox *_enumCombo;
+ QBoxLayout *_layout;
+};
+
+#endif
diff --git a/kmix/mdwslider.cpp b/kmix/mdwslider.cpp
new file mode 100644
index 00000000..6ee8166c
--- /dev/null
+++ b/kmix/mdwslider.cpp
@@ -0,0 +1,974 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <klocale.h>
+#include <kled.h>
+#include <kiconloader.h>
+#include <kconfig.h>
+#include <kaction.h>
+#include <kpopupmenu.h>
+#include <kglobalaccel.h>
+#include <kkeydialog.h>
+#include <kdebug.h>
+
+#include <qobject.h>
+#include <qcursor.h>
+#include <qslider.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpixmap.h>
+#include <qtooltip.h>
+#include <qwmatrix.h>
+
+#include "mdwslider.h"
+#include "mixer.h"
+#include "viewbase.h"
+#include "kledbutton.h"
+#include "ksmallslider.h"
+#include "verticaltext.h"
+
+/**
+ * MixDeviceWidget that represents a single mix device, inlcuding PopUp, muteLED, ...
+ *
+ * Used in KMix main window and DockWidget and PanelApplet.
+ * It can be configured to include or exclude the recordLED and the muteLED.
+ * The direction (horizontal, vertical) can be configured and whether it should
+ * be "small" (uses KSmallSlider instead of QSlider then).
+ *
+ * Due to the many options, this is the most complicated MixDeviceWidget subclass.
+ */
+MDWSlider::MDWSlider(Mixer *mixer, MixDevice* md,
+ bool showMuteLED, bool showRecordLED,
+ bool small, Qt::Orientation orientation,
+ QWidget* parent, ViewBase* mw, const char* name) :
+ MixDeviceWidget(mixer,md,small,orientation,parent,mw,name),
+ m_linked(true), m_valueStyle( NNONE), m_iconLabel( 0 ), m_muteLED( 0 ), m_recordLED( 0 ), m_label( 0 ), _layout(0)
+{
+ // create actions (on _mdwActions, see MixDeviceWidget)
+
+ new KToggleAction( i18n("&Split Channels"), 0, this, SLOT(toggleStereoLinked()),
+ _mdwActions, "stereo" );
+ new KToggleAction( i18n("&Hide"), 0, this, SLOT(setDisabled()), _mdwActions, "hide" );
+
+ KToggleAction *a = new KToggleAction(i18n("&Muted"), 0, 0, 0, _mdwActions, "mute" );
+ connect( a, SIGNAL(toggled(bool)), SLOT(toggleMuted()) );
+
+ if( m_mixdevice->isRecordable() ) {
+ a = new KToggleAction( i18n("Set &Record Source"), 0, 0, 0, _mdwActions, "recsrc" );
+ connect( a, SIGNAL(toggled(bool)), SLOT( toggleRecsrc()) );
+ }
+
+ new KAction( i18n("C&onfigure Global Shortcuts..."), 0, this, SLOT(defineKeys()), _mdwActions, "keys" );
+
+ // create widgets
+ createWidgets( showMuteLED, showRecordLED );
+
+ m_keys->insert( "Increase volume", i18n( "Increase Volume of '%1'" ).arg(m_mixdevice->name().utf8().data()), QString::null,
+ KShortcut(), KShortcut(), this, SLOT( increaseVolume() ) );
+ m_keys->insert( "Decrease volume", i18n( "Decrease Volume of '%1'" ).arg(m_mixdevice->name().utf8().data()), QString::null,
+ KShortcut(), KShortcut(), this, SLOT( decreaseVolume() ) );
+ m_keys->insert( "Toggle mute", i18n( "Toggle Mute of '%1'" ).arg(m_mixdevice->name().utf8().data()), QString::null,
+ KShortcut(), KShortcut(), this, SLOT( toggleMuted() ) );
+
+ installEventFilter( this ); // filter for popup
+
+ update();
+}
+
+
+QSizePolicy MDWSlider::sizePolicy() const
+{
+ if ( _orientation == Qt::Vertical ) {
+ return QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding );
+ }
+ else {
+ return QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
+ }
+}
+
+
+/**
+ * Creates up to 4 widgets - Icon, Mute-Button, Slider and Record-Button.
+ *
+ * Those widgets are placed into
+
+*/
+void MDWSlider::createWidgets( bool showMuteLED, bool showRecordLED )
+{
+ if ( _orientation == Qt::Vertical ) {
+ _layout = new QVBoxLayout( this );
+ _layout->setAlignment(Qt::AlignCenter);
+ }
+ else {
+ _layout = new QHBoxLayout( this );
+ _layout->setAlignment(Qt::AlignCenter);
+ }
+
+ // -- MAIN SLIDERS LAYOUT ---
+ QBoxLayout *slidersLayout;
+ if ( _orientation == Qt::Vertical ) {
+ slidersLayout = new QHBoxLayout( _layout );
+ slidersLayout->setAlignment(Qt::AlignVCenter);
+ }
+ else {
+ slidersLayout = new QVBoxLayout( _layout );
+ slidersLayout->setAlignment(Qt::AlignHCenter);
+ }
+
+ /* cesken: This is inconsistent. Why should vertical and horizontal layout differ?
+ * Also it eats too much space - especially when you don't show sliders at all.
+ * Even more on the vertical panel applet (see Bug #97667)
+ if ( _orientation == Qt::Horizontal )
+ slidersLayout->addSpacing( 10 );
+ */
+
+
+ // -- LABEL LAYOUT TO POSITION
+ QBoxLayout *labelLayout;
+ if ( _orientation == Qt::Vertical ) {
+ labelLayout = new QVBoxLayout( slidersLayout );
+ labelLayout->setAlignment(Qt::AlignHCenter);
+ }
+ else {
+ labelLayout = new QHBoxLayout( slidersLayout );
+ labelLayout->setAlignment(Qt::AlignVCenter);
+ }
+ if ( _orientation == Qt::Vertical ) {
+ m_label = new VerticalText( this, m_mixdevice->name().utf8().data() );
+ QToolTip::add( m_label, m_mixdevice->name() );
+
+ }
+ else {
+ m_label = new QLabel(this);
+ static_cast<QLabel*>(m_label) ->setText(m_mixdevice->name());
+ QToolTip::add( m_label, m_mixdevice->name() );
+ }
+
+ m_label->hide();
+
+/* This addSpacing() looks VERY bizarre => removing it (cesken, 21.2.2006).
+ Also horizontal and vertical spacing differs. This doesn't look sensible.
+ if ( _orientation == Qt::Horizontal )
+ labelLayout->addSpacing( 36 );
+*/
+ labelLayout->addWidget( m_label );
+ m_label->installEventFilter( this );
+
+/* This addSpacing() looks VERY bizarre => removing it (cesken, 21.2.2006)
+ Also horizontal and vertical spacing differs. This doesn't look sensible.
+ if ( _orientation == Qt::Vertical ) {
+ labelLayout->addSpacing( 18 );
+ }
+*/
+
+ // -- SLIDERS, LEDS AND ICON
+ QBoxLayout *sliLayout;
+ if ( _orientation == Qt::Vertical ) {
+ sliLayout = new QVBoxLayout( slidersLayout );
+ sliLayout->setAlignment(Qt::AlignHCenter);
+ }
+ else {
+ sliLayout = new QHBoxLayout( slidersLayout );
+ sliLayout->setAlignment(Qt::AlignVCenter);
+ }
+
+ // --- ICON ----------------------------
+ QBoxLayout *iconLayout;
+ if ( _orientation == Qt::Vertical ) {
+ iconLayout = new QHBoxLayout( sliLayout );
+ iconLayout->setAlignment(Qt::AlignVCenter);
+ }
+ else {
+ iconLayout = new QVBoxLayout( sliLayout );
+ iconLayout->setAlignment(Qt::AlignHCenter);
+ }
+
+ m_iconLabel = 0L;
+ setIcon( m_mixdevice->type() );
+ iconLayout->addStretch();
+ iconLayout->addWidget( m_iconLabel );
+ iconLayout->addStretch();
+ m_iconLabel->installEventFilter( this );
+
+ sliLayout->addSpacing( 5 );
+
+
+ // --- MUTE LED
+ if ( showMuteLED ) {
+ QBoxLayout *ledlayout;
+ if ( _orientation == Qt::Vertical ) {
+ ledlayout = new QHBoxLayout( sliLayout );
+ ledlayout->setAlignment(Qt::AlignVCenter);
+ }
+ else {
+ ledlayout = new QVBoxLayout( sliLayout );
+ ledlayout->setAlignment(Qt::AlignHCenter);
+ }
+
+ if( m_mixdevice->hasMute() )
+ {
+ ledlayout->addStretch();
+ // create mute LED
+ m_muteLED = new KLedButton( Qt::green, KLed::On, KLed::Sunken,
+ KLed::Circular, this, "MuteLED" );
+ m_muteLED->setFixedSize( QSize(16, 16) );
+ m_muteLED->resize( QSize(16, 16) );
+ ledlayout->addWidget( m_muteLED );
+ QToolTip::add( m_muteLED, i18n( "Mute" ) );
+ connect( m_muteLED, SIGNAL(stateChanged(bool)), this, SLOT(toggleMuted()) );
+ m_muteLED->installEventFilter( this );
+ ledlayout->addStretch();
+ } // has Mute LED
+ else {
+ // we don't have a MUTE LED. We create a dummy widget
+ // !! possibly not neccesary any more (we are layouted)
+ QWidget *qw = new QWidget(this, "Spacer");
+ qw->setFixedSize( QSize(16, 16) );
+ ledlayout->addWidget(qw);
+ qw->installEventFilter( this );
+ } // has no Mute LED
+
+ sliLayout->addSpacing( 3 );
+ } // showMuteLED
+
+ // --- SLIDERS ---------------------------
+ QBoxLayout *volLayout;
+ if ( _orientation == Qt::Vertical ) {
+ volLayout = new QHBoxLayout( sliLayout );
+ volLayout->setAlignment(Qt::AlignVCenter);
+ }
+ else {
+ volLayout = new QVBoxLayout( sliLayout );
+ volLayout->setAlignment(Qt::AlignHCenter);
+ }
+
+ // Sliders and volume number indication
+ QBoxLayout *slinumLayout;
+ for( int i = 0; i < m_mixdevice->getVolume().count(); i++ )
+ {
+ Volume::ChannelID chid = Volume::ChannelID(i);
+ // @todo !! Normally the mixdevicewidget SHOULD know, which slider represents which channel.
+ // We should look up the mapping here, but for now, we simply assume "chid == i".
+
+ int maxvol = m_mixdevice->getVolume().maxVolume();
+ int minvol = m_mixdevice->getVolume().minVolume();
+
+ if ( _orientation == Qt::Vertical ) {
+ slinumLayout = new QVBoxLayout( volLayout );
+ slinumLayout->setAlignment(Qt::AlignHCenter);
+ }
+ else {
+ slinumLayout = new QHBoxLayout( volLayout );
+ slinumLayout->setAlignment(Qt::AlignVCenter);
+ }
+
+ // create labels to hold volume values (taken from qamix/kamix)
+ QLabel *number = new QLabel( "100", this );
+ slinumLayout->addWidget( number );
+ number->setFrameStyle( QFrame::Panel | QFrame::Sunken );
+ number->setLineWidth( 2 );
+ number->setMinimumWidth( number->sizeHint().width() );
+ number->setPaletteBackgroundColor( QColor(190, 250, 190) );
+ // don't show the value by default
+ number->hide();
+ updateValue( number, chid );
+ _numbers.append( number );
+
+ QWidget* slider;
+ if ( m_small ) {
+ slider = new KSmallSlider( minvol, maxvol, maxvol/10,
+ m_mixdevice->getVolume( chid ), _orientation,
+ this, m_mixdevice->name().ascii() );
+ }
+ else {
+ slider = new QSlider( 0, maxvol, maxvol/10,
+ maxvol - m_mixdevice->getVolume( chid ), _orientation,
+ this, m_mixdevice->name().ascii() );
+ slider->setMinimumSize( slider->sizeHint() );
+ }
+
+ slider->setBackgroundOrigin(AncestorOrigin);
+ slider->installEventFilter( this );
+ QToolTip::add( slider, m_mixdevice->name() );
+
+ if( i>0 && isStereoLinked() ) {
+ // show only one (the first) slider, when the user wants it so
+ slider->hide();
+ number->hide();
+ }
+ slinumLayout->addWidget( slider ); // add to layout
+ m_sliders.append ( slider ); // add to list
+ _slidersChids.append(chid); // Remember slider-chid association
+ connect( slider, SIGNAL(valueChanged(int)), SLOT(volumeChange(int)) );
+ } // for all channels of this device
+
+
+ // --- RECORD SOURCE LED --------------------------
+ if ( showRecordLED )
+ {
+ sliLayout->addSpacing( 5 );
+
+ // --- LED LAYOUT TO CENTER ---
+ QBoxLayout *reclayout;
+ if ( _orientation == Qt::Vertical ) {
+ reclayout = new QHBoxLayout( sliLayout );
+ reclayout->setAlignment(Qt::AlignVCenter);
+ }
+ else {
+ reclayout = new QVBoxLayout( sliLayout );
+ reclayout->setAlignment(Qt::AlignHCenter);
+ }
+
+ if( m_mixdevice->isRecordable() ) {
+ reclayout->addStretch();
+ m_recordLED = new KLedButton( Qt::red,
+ m_mixdevice->isRecSource()?KLed::On:KLed::Off,
+ KLed::Sunken, KLed::Circular, this, "RecordLED" );
+ m_recordLED->setFixedSize( QSize(16, 16) );
+ reclayout->addWidget( m_recordLED );
+ connect(m_recordLED, SIGNAL(stateChanged(bool)), this, SLOT(setRecsrc(bool)));
+ m_recordLED->installEventFilter( this );
+ QToolTip::add( m_recordLED, i18n( "Record" ) );
+ reclayout->addStretch();
+ }
+ else
+ {
+ // we don't have a RECORD LED. We create a dummy widget
+ // !! possibly not neccesary any more (we are layouted)
+ QWidget *qw = new QWidget(this, "Spacer");
+ qw->setFixedSize( QSize(16, 16) );
+ reclayout->addWidget(qw);
+ qw->installEventFilter( this );
+ } // has no Record LED
+ } // showRecordLED
+
+ layout()->activate();
+}
+
+
+QPixmap
+MDWSlider::icon( int icontype )
+{
+ QPixmap miniDevPM;
+ switch (icontype) {
+ case MixDevice::AUDIO:
+ miniDevPM = UserIcon("mix_audio"); break;
+ case MixDevice::BASS:
+ case MixDevice::SURROUND_LFE: // "LFE" SHOULD have an own icon
+ miniDevPM = UserIcon("mix_bass"); break;
+ case MixDevice::CD:
+ miniDevPM = UserIcon("mix_cd"); break;
+ case MixDevice::EXTERNAL:
+ miniDevPM = UserIcon("mix_ext"); break;
+ case MixDevice::MICROPHONE:
+ miniDevPM = UserIcon("mix_microphone");break;
+ case MixDevice::MIDI:
+ miniDevPM = UserIcon("mix_midi"); break;
+ case MixDevice::RECMONITOR:
+ miniDevPM = UserIcon("mix_recmon"); break;
+ case MixDevice::TREBLE:
+ miniDevPM = UserIcon("mix_treble"); break;
+ case MixDevice::UNKNOWN:
+ miniDevPM = UserIcon("mix_unknown"); break;
+ case MixDevice::VOLUME:
+ miniDevPM = UserIcon("mix_volume"); break;
+ case MixDevice::VIDEO:
+ miniDevPM = UserIcon("mix_video"); break;
+ case MixDevice::SURROUND:
+ case MixDevice::SURROUND_BACK:
+ case MixDevice::SURROUND_CENTERFRONT:
+ case MixDevice::SURROUND_CENTERBACK:
+ miniDevPM = UserIcon("mix_surround"); break;
+ case MixDevice::HEADPHONE:
+ miniDevPM = UserIcon( "mix_headphone" ); break;
+ case MixDevice::DIGITAL:
+ miniDevPM = UserIcon( "mix_digital" ); break;
+ case MixDevice::AC97:
+ miniDevPM = UserIcon( "mix_ac97" ); break;
+ default:
+ miniDevPM = UserIcon("mix_unknown"); break;
+ }
+
+ return miniDevPM;
+}
+
+void
+MDWSlider::setIcon( int icontype )
+{
+ if( !m_iconLabel )
+ {
+ m_iconLabel = new QLabel(this);
+ m_iconLabel->setBackgroundOrigin(AncestorOrigin);
+ installEventFilter( m_iconLabel );
+ }
+
+ QPixmap miniDevPM = icon( icontype );
+ if ( !miniDevPM.isNull() )
+ {
+ if ( m_small )
+ {
+ // scale icon
+ QWMatrix t;
+ t = t.scale( 10.0/miniDevPM.width(), 10.0/miniDevPM.height() );
+ m_iconLabel->setPixmap( miniDevPM.xForm( t ) );
+ m_iconLabel->resize( 10, 10 );
+ } else
+ m_iconLabel->setPixmap( miniDevPM );
+ m_iconLabel->setAlignment( Qt::AlignCenter );
+ } else
+ {
+ kdError(67100) << "Pixmap missing." << endl;
+ }
+
+ layout()->activate();
+}
+
+bool
+MDWSlider::isLabeled() const
+{
+ if ( m_label == 0 )
+ return false;
+ else
+ return !m_label->isHidden();
+}
+
+void
+MDWSlider::toggleStereoLinked()
+{
+ setStereoLinked( !isStereoLinked() );
+}
+
+void
+MDWSlider::setStereoLinked(bool value)
+{
+ m_linked = value;
+
+ QWidget *slider = m_sliders.first();
+ QLabel *number = _numbers.first();
+ QString qs = number->text();
+
+ /***********************************************************
+ Remember value of first slider, so that it can be copied
+ to the other sliders.
+ ***********************************************************/
+ int firstSliderValue = 0;
+ bool firstSliderValueValid = false;
+ if (slider->isA("QSlider") ) {
+ QSlider *sld = static_cast<QSlider*>(slider);
+ firstSliderValue = sld->value();
+ firstSliderValueValid = true;
+ }
+ else if ( slider->isA("KSmallSlider") ) {
+ KSmallSlider *sld = static_cast<KSmallSlider*>(slider);
+ firstSliderValue = sld->value();
+ firstSliderValueValid = true;
+ }
+
+ for( slider=m_sliders.next(), number=_numbers.next(); slider!=0 && number!=0; slider=m_sliders.next(), number=_numbers.next() ) {
+ if ( m_linked ) {
+ slider->hide();
+ number->hide();
+ }
+ else {
+ // When splitting, make the next sliders show the same value as the first.
+ // This might not be entirely true, but better than showing the random value
+ // that was used to be shown before hot-fixing this. !! must be revised
+ if ( firstSliderValueValid ) {
+ // Remark: firstSlider== 0 could happen, if the static_cast<QRangeControl*> above fails.
+ // It's a safety measure, if we got other Slider types in the future.
+ if (slider->isA("QSlider") ) {
+ QSlider *sld = static_cast<QSlider*>(slider);
+ sld->setValue( firstSliderValue );
+ }
+ if (slider->isA("KSmallSlider") ) {
+ KSmallSlider *sld = static_cast<KSmallSlider*>(slider);
+ sld->setValue( firstSliderValue );
+ }
+ }
+ slider->show();
+ number->setText(qs);
+ if (m_valueStyle != NNONE)
+ number->show();
+ }
+ }
+
+ slider = m_sliders.last();
+ if( slider && static_cast<QSlider *>(slider)->tickmarks() )
+ setTicks( true );
+
+ layout()->activate();
+}
+
+
+void
+MDWSlider::setLabeled(bool value)
+{
+ if ( m_label == 0 )
+ return;
+
+ if (value )
+ m_label->show();
+ else
+ m_label->hide();
+
+ layout()->activate();
+}
+
+void
+MDWSlider::setTicks( bool ticks )
+{
+ QWidget* slider;
+
+ slider = m_sliders.first();
+
+ if ( slider->inherits( "QSlider" ) )
+ {
+ if( ticks )
+ if( isStereoLinked() )
+ static_cast<QSlider *>(slider)->setTickmarks( QSlider::Right );
+ else
+ {
+ static_cast<QSlider *>(slider)->setTickmarks( QSlider::NoMarks );
+ slider = m_sliders.last();
+ static_cast<QSlider *>(slider)->setTickmarks( QSlider::Left );
+ }
+ else
+ {
+ static_cast<QSlider *>(slider)->setTickmarks( QSlider::NoMarks );
+ slider = m_sliders.last();
+ static_cast<QSlider *>(slider)->setTickmarks( QSlider::NoMarks );
+ }
+ }
+
+ layout()->activate();
+}
+
+void
+MDWSlider::setValueStyle( ValueStyle valueStyle )
+{
+ m_valueStyle = valueStyle;
+
+ int i = 0;
+ QValueList<Volume::ChannelID>::Iterator it = _slidersChids.begin();
+ for(QLabel *number = _numbers.first(); number!=0; number = _numbers.next(), ++i, ++it) {
+ Volume::ChannelID chid = *it;
+ switch ( m_valueStyle ) {
+ case NNONE: number->hide(); break;
+ default:
+ if ( !isStereoLinked() || (i == 0)) {
+ updateValue( number, chid );
+ number->show();
+ }
+ }
+ }
+ layout()->activate();
+}
+
+void
+MDWSlider::setIcons(bool value)
+{
+ if ( m_iconLabel != 0 ) {
+ if ( ( !m_iconLabel->isHidden()) !=value ) {
+ if (value)
+ m_iconLabel->show();
+ else
+ m_iconLabel->hide();
+
+ layout()->activate();
+ }
+ } // if it has an icon
+}
+
+void
+MDWSlider::setColors( QColor high, QColor low, QColor back )
+{
+ for( QWidget *slider=m_sliders.first(); slider!=0; slider=m_sliders.next() ) {
+ KSmallSlider *smallSlider = dynamic_cast<KSmallSlider*>(slider);
+ if ( smallSlider ) smallSlider->setColors( high, low, back );
+ }
+}
+
+void
+MDWSlider::setMutedColors( QColor high, QColor low, QColor back )
+{
+ for( QWidget *slider=m_sliders.first(); slider!=0; slider=m_sliders.next() ) {
+ KSmallSlider *smallSlider = dynamic_cast<KSmallSlider*>(slider);
+ if ( smallSlider ) smallSlider->setGrayColors( high, low, back );
+ }
+}
+
+void
+MDWSlider::updateValue( QLabel *value, Volume::ChannelID chid ) {
+ QString qs;
+ Volume& vol = m_mixdevice->getVolume();
+
+ if (m_valueStyle == NABSOLUTE )
+ qs.sprintf("%3d", (int) vol.getVolume( chid ) );
+ else
+ qs.sprintf("%3d", (int)( vol.getVolume( chid ) / (double)vol.maxVolume() * 100 ) );
+ value->setText(qs);
+}
+
+
+/** This slot is called, when a user has changed the volume via the KMix Slider */
+void MDWSlider::volumeChange( int )
+{
+ // --- Step 1: Get a REFERENCE of the volume Object ---
+ Volume& vol = m_mixdevice->getVolume();
+
+ // --- Step 2: Change the volumes directly in the Volume object to reflect the Sliders ---
+ if ( isStereoLinked() )
+ {
+ QWidget *slider = m_sliders.first();
+ Volume::ChannelID chid = _slidersChids.first();
+
+ int sliderValue = 0;
+ if ( slider->inherits( "KSmallSlider" ) )
+ {
+ KSmallSlider *slider = dynamic_cast<KSmallSlider *>(m_sliders.first());
+ if (slider) {
+ sliderValue= slider->value();
+ }
+ }
+ else {
+ QSlider *slider = dynamic_cast<QSlider *>(m_sliders.first());
+ if (slider) {
+ if ( _orientation == Qt::Vertical )
+ sliderValue= slider->maxValue() - slider->value();
+ else
+ sliderValue= slider->value();
+
+ }
+ }
+
+ // With balance proper working, we must change relative volumes,
+ // not absolute, which leads a make some difference calc related
+ // to new sliders position against the top volume on channels
+ long volumeDif = sliderValue - vol.getTopStereoVolume( Volume::MMAIN );
+
+ if ( chid == Volume::LEFT ) {
+ vol.setVolume( Volume::LEFT , vol.getVolume( Volume::LEFT ) + volumeDif );
+ vol.setVolume( Volume::RIGHT, vol.getVolume( Volume::RIGHT ) + volumeDif );
+ }
+ else {
+ kdDebug(67100) << "MDWSlider::volumeChange(), unknown chid " << chid << endl;
+ }
+
+ updateValue( _numbers.first(), Volume::LEFT );
+ } // joined
+ else {
+ int n = 0;
+ QValueList<Volume::ChannelID>::Iterator it = _slidersChids.begin();
+ QLabel *number = _numbers.first();
+ for( QWidget *slider=m_sliders.first(); slider!=0 && number!=0; slider=m_sliders.next(), number=_numbers.next(), ++it )
+ {
+ Volume::ChannelID chid = *it;
+ if ( slider->inherits( "KSmallSlider" ) )
+ {
+ KSmallSlider *smallSlider = dynamic_cast<KSmallSlider *>(slider);
+ if (smallSlider)
+ vol.setVolume( chid, smallSlider->value() );
+ }
+ else
+ {
+ QSlider *bigSlider = dynamic_cast<QSlider *>(slider);
+ if (bigSlider)
+ if ( _orientation == Qt::Vertical )
+ vol.setVolume( chid, bigSlider->maxValue() - bigSlider->value() );
+ else
+ vol.setVolume( chid, bigSlider->value() );
+ }
+ updateValue( number, chid );
+ n++;
+ }
+ }
+
+ // --- Step 3: Write back the new volumes to the HW ---
+ m_mixer->commitVolumeChange(m_mixdevice);
+}
+
+
+/**
+ This slot is called, when a user has clicked the recsrc button. Also it is called by any other
+ associated KAction like the context menu.
+*/
+void MDWSlider::toggleRecsrc() {
+ setRecsrc( !m_mixdevice->isRecSource() );
+}
+
+
+void MDWSlider::setRecsrc(bool value )
+{
+ if ( m_mixdevice->isRecordable() ) {
+ m_mixer->setRecordSource( m_mixdevice->num(), value );
+ }
+}
+
+
+/**
+ This slot is called, when a user has clicked the mute button. Also it is called by any other
+ associated KAction like the context menu.
+*/
+void MDWSlider::toggleMuted() {
+ setMuted( !m_mixdevice->isMuted() );
+}
+
+void MDWSlider::setMuted(bool value)
+{
+ if ( m_mixdevice->hasMute() ) {
+ m_mixdevice->setMuted( value );
+ m_mixer->commitVolumeChange(m_mixdevice);
+ }
+}
+
+
+void MDWSlider::setDisabled()
+{
+ setDisabled( true );
+}
+
+void MDWSlider::setDisabled( bool value )
+{
+ if ( m_disabled!=value) {
+ value ? hide() : show();
+ m_disabled = value;
+ }
+}
+
+
+/**
+ This slot is called on a MouseWheel event. Also it is called by any other
+ associated KAction like the context menu.
+*/
+void MDWSlider::increaseVolume()
+{
+ Volume vol = m_mixdevice->getVolume();
+ long inc = vol.maxVolume() / 20;
+ if ( inc == 0 )
+ inc = 1;
+ for ( int i = 0; i < vol.count(); i++ ) {
+ long newVal = (vol[i]) + inc;
+ m_mixdevice->setVolume( i, newVal < vol.maxVolume() ? newVal : vol.maxVolume() );
+ }
+ m_mixer->commitVolumeChange(m_mixdevice);
+}
+
+/**
+ This slot is called on a MouseWheel event. Also it is called by any other
+ associated KAction like the context menu.
+*/
+void MDWSlider::decreaseVolume()
+{
+ Volume vol = m_mixdevice->getVolume();
+ long inc = vol.maxVolume() / 20;
+ if ( inc == 0 )
+ inc = 1;
+ for ( int i = 0; i < vol.count(); i++ ) {
+ long newVal = (vol[i]) - inc;
+ m_mixdevice->setVolume( i, newVal > 0 ? newVal : 0 );
+ }
+ m_mixer->commitVolumeChange(m_mixdevice);
+}
+
+
+/**
+ This is called whenever there are volume updates pending from the hardware for this MDW.
+ At the moment it is called regulary via a QTimer (implicitely).
+*/
+void MDWSlider::update()
+{
+ // update volumes
+ Volume vol = m_mixdevice->getVolume();
+ if( isStereoLinked() )
+ {
+ QValueList<Volume::ChannelID>::Iterator it = _slidersChids.begin();
+
+ long avgVol = vol.getAvgVolume( Volume::MMAIN );
+
+ QWidget *slider = m_sliders.first();
+ if ( slider == 0 ) {
+ return; // !!! Development version, check this !!!
+ }
+ slider->blockSignals( true );
+ if ( slider->inherits( "KSmallSlider" ) )
+ {
+ KSmallSlider *smallSlider = dynamic_cast<KSmallSlider *>(slider);
+ if (smallSlider) {
+ smallSlider->setValue( avgVol ); // !! inverted ?!?
+ smallSlider->setGray( m_mixdevice->isMuted() );
+ }
+ } // small slider
+ else {
+ QSlider *bigSlider = dynamic_cast<QSlider *>(slider);
+ if (bigSlider)
+ {
+ // In case of stereo linked and single slider, slider must
+ // show the top of both volumes, and not strangely low down
+ // the main volume by half
+
+ if ( _orientation == Qt::Vertical )
+ bigSlider->setValue( vol.maxVolume() - vol.getTopStereoVolume( Volume::MMAIN ) );
+ else
+ bigSlider->setValue( vol.getTopStereoVolume( Volume::MMAIN ) );
+ }
+ } // big slider
+
+ updateValue( _numbers.first(), Volume::LEFT );
+ slider->blockSignals( false );
+ } // only 1 slider (stereo-linked)
+ else {
+ QValueList<Volume::ChannelID>::Iterator it = _slidersChids.begin();
+ for( int i=0; i<vol.count(); i++, ++it ) {
+ QWidget *slider = m_sliders.at( i );
+ Volume::ChannelID chid = *it;
+ if (slider == 0) {
+ // !!! not implemented !!!
+ // not implemented: happens if there are record and playback
+ // sliders in the same device. Or if you only show
+ // the right slider (or any other fancy occasion)
+ continue;
+ }
+ slider->blockSignals( true );
+
+ if ( slider->inherits( "KSmallSlider" ) )
+ {
+ KSmallSlider *smallSlider = dynamic_cast<KSmallSlider *>(slider);
+ if (smallSlider) {
+ smallSlider->setValue( vol[chid] );
+ smallSlider->setGray( m_mixdevice->isMuted() );
+ }
+ }
+ else
+ {
+ QSlider *bigSlider = dynamic_cast<QSlider *>(slider);
+ if (bigSlider)
+ if ( _orientation == Qt::Vertical ) {
+ bigSlider->setValue( vol.maxVolume() - vol[i] );
+ }
+ else {
+ bigSlider->setValue( vol[i] );
+ }
+ }
+
+ updateValue( _numbers.at ( i ), chid );
+
+ slider->blockSignals( false );
+ } // for all sliders
+ } // more than 1 slider
+
+ // update mute led
+ if ( m_muteLED ) {
+ m_muteLED->blockSignals( true );
+ m_muteLED->setState( m_mixdevice->isMuted() ? KLed::Off : KLed::On );
+ m_muteLED->blockSignals( false );
+ }
+
+ // update recsrc
+ if( m_recordLED ) {
+ m_recordLED->blockSignals( true );
+ m_recordLED->setState( m_mixdevice->isRecSource() ? KLed::On : KLed::Off );
+ m_recordLED->blockSignals( false );
+ }
+}
+
+void MDWSlider::showContextMenu()
+{
+ if( m_mixerwidget == NULL )
+ return;
+
+ KPopupMenu *menu = m_mixerwidget->getPopup();
+ menu->insertTitle( SmallIcon( "kmix" ), m_mixdevice->name() );
+
+ if ( m_sliders.count()>1 ) {
+ KToggleAction *stereo = (KToggleAction *)_mdwActions->action( "stereo" );
+ if ( stereo ) {
+ stereo->setChecked( !isStereoLinked() );
+ stereo->plug( menu );
+ }
+ }
+
+ KToggleAction *ta = (KToggleAction *)_mdwActions->action( "recsrc" );
+ if ( ta ) {
+ ta->setChecked( m_mixdevice->isRecSource() );
+ ta->plug( menu );
+ }
+
+ if ( m_mixdevice->hasMute() ) {
+ ta = ( KToggleAction* )_mdwActions->action( "mute" );
+ if ( ta ) {
+ ta->setChecked( m_mixdevice->isMuted() );
+ ta->plug( menu );
+ }
+ }
+
+ KAction *a = _mdwActions->action( "hide" );
+ if ( a )
+ a->plug( menu );
+
+ a = _mdwActions->action( "keys" );
+ if ( a && m_keys ) {
+ KActionSeparator sep( this );
+ sep.plug( menu );
+ a->plug( menu );
+ }
+
+ QPoint pos = QCursor::pos();
+ menu->popup( pos );
+}
+
+QSize MDWSlider::sizeHint() const {
+ if ( _layout != 0 ) {
+ return _layout->sizeHint();
+ }
+ else {
+ // layout not (yet) created
+ return QWidget::sizeHint();
+ }
+}
+
+/**
+ * An event filter for the various QWidgets. We watch for Mouse press Events, so
+ * that we can popup the context menu.
+ */
+bool MDWSlider::eventFilter( QObject* obj, QEvent* e )
+{
+ if (e->type() == QEvent::MouseButtonPress) {
+ QMouseEvent *qme = static_cast<QMouseEvent*>(e);
+ if (qme->button() == Qt::RightButton) {
+ showContextMenu();
+ return true;
+ }
+ }
+ // Attention: We don't filter WheelEvents for KSmallSlider, because it handles WheelEvents itself
+ else if ( (e->type() == QEvent::Wheel) && !obj->isA("KSmallSlider") ) {
+ QWheelEvent *qwe = static_cast<QWheelEvent*>(e);
+ if (qwe->delta() > 0) {
+ increaseVolume();
+ }
+ else {
+ decreaseVolume();
+ }
+ return true;
+ }
+ return QWidget::eventFilter(obj,e);
+}
+
+#include "mdwslider.moc"
diff --git a/kmix/mdwslider.h b/kmix/mdwslider.h
new file mode 100644
index 00000000..2083c9e4
--- /dev/null
+++ b/kmix/mdwslider.h
@@ -0,0 +1,128 @@
+//-*-C++-*-
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2004 Chrisitan Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef MDWSLIDER_H
+#define MDWSLIDER_H
+
+#include <kpanelapplet.h>
+
+#include <qvaluelist.h>
+#include <qwidget.h>
+#include <qlabel.h>
+#include <qptrlist.h>
+#include <qpixmap.h>
+#include <qrangecontrol.h>
+
+class QBoxLayout;
+class QLabel;
+class QPopupMenu;
+class QSlider;
+
+class KLed;
+class KLedButton;
+class KAction;
+class KActionCollection;
+class KSmallSlider;
+class KGlobalAccel;
+
+class MixDevice;
+class VerticalText;
+class Mixer;
+class ViewBase;
+
+#include "mixdevicewidget.h"
+#include "volume.h"
+
+
+class MDWSlider : public MixDeviceWidget
+{
+ Q_OBJECT
+
+public:
+ MDWSlider( Mixer *mixer, MixDevice* md,
+ bool showMuteLED, bool showRecordLED,
+ bool small, Qt::Orientation,
+ QWidget* parent = 0, ViewBase* mw = 0, const char* name = 0);
+ ~MDWSlider() {}
+
+ void addActionToPopup( KAction *action );
+
+ bool isStereoLinked() const { return m_linked; };
+ bool isLabeled() const;
+
+ void setStereoLinked( bool value );
+ void setLabeled( bool value );
+ void setTicks( bool ticks );
+ void setIcons( bool value );
+ void setValueStyle( ValueStyle valueStyle );
+ void setColors( QColor high, QColor low, QColor back );
+ void setMutedColors( QColor high, QColor low, QColor back );
+ QSize sizeHint() const;
+ bool eventFilter( QObject* obj, QEvent* e );
+ QSizePolicy sizePolicy() const;
+
+public slots:
+ void toggleRecsrc();
+ void toggleMuted();
+ void toggleStereoLinked();
+
+ void setDisabled();
+ void setDisabled( bool value );
+ void update();
+ virtual void showContextMenu();
+
+
+signals:
+ void newVolume( int num, Volume volume );
+ void newMasterVolume( Volume volume );
+ void masterMuted( bool );
+ void newRecsrc( int num, bool on );
+ void toggleMenuBar(bool value);
+
+private slots:
+ void setRecsrc( bool value );
+ void setMuted(bool value);
+ void volumeChange( int );
+
+ void increaseVolume();
+ void decreaseVolume();
+
+private:
+ QPixmap icon( int icontype );
+ void setIcon( int icontype );
+ void createWidgets( bool showMuteLED, bool showRecordLED );
+ void updateValue( QLabel *value, Volume::ChannelID chid );
+
+ bool m_linked;
+ ValueStyle m_valueStyle;
+ QLabel *m_iconLabel;
+ KLedButton *m_muteLED;
+ KLedButton *m_recordLED;
+ QWidget *m_label; // is either QLabel or VerticalText
+ QBoxLayout *_layout;
+ QPtrList<QWidget> m_sliders;
+ QValueList<Volume::ChannelID> _slidersChids;
+ QPtrList<QLabel> _numbers;
+ // QValueList<Volume::ChannelID> _numbersChids;
+};
+
+#endif
diff --git a/kmix/mdwswitch.cpp b/kmix/mdwswitch.cpp
new file mode 100644
index 00000000..54f6def1
--- /dev/null
+++ b/kmix/mdwswitch.cpp
@@ -0,0 +1,231 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <qcursor.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qobject.h>
+#include <qslider.h>
+#include <qtooltip.h>
+
+#include <klocale.h>
+#include <kconfig.h>
+#include <kaction.h>
+#include <kpopupmenu.h>
+#include <kglobalaccel.h>
+#include <kkeydialog.h>
+#include <kdebug.h>
+
+#include "kledbutton.h"
+#include "mdwswitch.h"
+#include "mixer.h"
+#include "viewbase.h"
+#include "verticaltext.h"
+
+/**
+ * Class that represents a single Switch
+ * The orientation (horizontal, vertical) can be configured and whether it should
+ * be "small" (uses KSmallSlider instead of QSlider then).
+ */
+MDWSwitch::MDWSwitch(Mixer *mixer, MixDevice* md,
+ bool small, Qt::Orientation orientation,
+ QWidget* parent, ViewBase* mw, const char* name) :
+ MixDeviceWidget(mixer,md,small,orientation,parent,mw,name),
+ _label(0) , _labelV(0) , _switchLED(0), _layout(0)
+{
+ // create actions (on _mdwActions, see MixDeviceWidget)
+
+ // KStdAction::showMenubar() is in MixDeviceWidget now
+ new KToggleAction( i18n("&Hide"), 0, this, SLOT(setDisabled()), _mdwActions, "hide" );
+ new KAction( i18n("C&onfigure Shortcuts..."), 0, this, SLOT(defineKeys()), _mdwActions, "keys" );
+
+ // create widgets
+ createWidgets();
+
+ m_keys->insert( "Toggle switch", i18n( "Toggle Switch" ), QString::null,
+ KShortcut(), KShortcut(), this, SLOT( toggleSwitch() ) );
+
+ // The keys are loaded in KMixerWidget::loadConfig, see kmixerwidget.cpp (now: kmixtoolbox.cpp)
+ //m_keys->readSettings();
+ //m_keys->updateConnections();
+
+ installEventFilter( this ); // filter for popup
+}
+
+MDWSwitch::~MDWSwitch()
+{
+}
+
+
+void MDWSwitch::createWidgets()
+{
+ if ( _orientation == Qt::Vertical ) {
+ _layout = new QVBoxLayout( this );
+ _layout->setAlignment(Qt::AlignHCenter);
+ }
+ else {
+ _layout = new QHBoxLayout( this );
+ _layout->setAlignment(Qt::AlignVCenter);
+ }
+ QToolTip::add( this, m_mixdevice->name() );
+
+
+ _layout->addSpacing( 4 );
+ // --- LEDS --------------------------
+ if ( _orientation == Qt::Vertical ) {
+ if( m_mixdevice->isRecordable() )
+ _switchLED = new KLedButton( Qt::red,
+ m_mixdevice->isRecSource()?KLed::On:KLed::Off,
+ KLed::Sunken, KLed::Circular, this, "RecordLED" );
+ else
+ _switchLED = new KLedButton( Qt::yellow, KLed::On, KLed::Sunken, KLed::Circular, this, "SwitchLED" );
+ _switchLED->setFixedSize(16,16);
+ _labelV = new VerticalText( this, m_mixdevice->name().utf8().data() );
+
+ _layout->addWidget( _switchLED );
+ _layout->addSpacing( 2 );
+ _layout->addWidget( _labelV );
+
+ _switchLED->installEventFilter( this );
+ _labelV->installEventFilter( this );
+ }
+ else
+ {
+ if( m_mixdevice->isRecordable() )
+ _switchLED = new KLedButton( Qt::red,
+ m_mixdevice->isRecSource()?KLed::On:KLed::Off,
+ KLed::Sunken, KLed::Circular, this, "RecordLED" );
+ else
+ _switchLED = new KLedButton( Qt::yellow, KLed::On, KLed::Sunken, KLed::Circular, this, "SwitchLED" );
+ _switchLED->setFixedSize(16,16);
+ _label = new QLabel(m_mixdevice->name(), this, "SwitchName");
+
+ _layout->addWidget( _switchLED );
+ _layout->addSpacing( 1 );
+ _layout->addWidget( _label );
+ _switchLED->installEventFilter( this );
+ _label->installEventFilter( this );
+ }
+ connect( _switchLED, SIGNAL(stateChanged(bool)), this, SLOT(toggleSwitch()) );
+ _layout->addSpacing( 4 );
+}
+
+void MDWSwitch::update()
+{
+ if ( _switchLED != 0 ) {
+ _switchLED->blockSignals( true );
+ if( m_mixdevice->isRecordable() )
+ _switchLED->setState( m_mixdevice->isRecSource() ? KLed::On : KLed::Off );
+ else
+ _switchLED->setState( m_mixdevice->isMuted() ? KLed::Off : KLed::On );
+
+ _switchLED->blockSignals( false );
+ }
+}
+
+void MDWSwitch::setBackgroundMode(BackgroundMode m)
+{
+ if ( _label != 0 ){
+ _label->setBackgroundMode(m);
+ }
+ if ( _labelV != 0 ){
+ _labelV->setBackgroundMode(m);
+ }
+ _switchLED->setBackgroundMode(m);
+ MixDeviceWidget::setBackgroundMode(m);
+}
+
+void MDWSwitch::showContextMenu()
+{
+ if( m_mixerwidget == NULL )
+ return;
+
+ KPopupMenu *menu = m_mixerwidget->getPopup();
+
+ QPoint pos = QCursor::pos();
+ menu->popup( pos );
+}
+
+QSize MDWSwitch::sizeHint() const {
+ if ( _layout != 0 ) {
+ return _layout->sizeHint();
+ }
+ else {
+ // layout not (yet) created
+ return QWidget::sizeHint();
+ }
+}
+
+
+/**
+ This slot is called, when a user has clicked the mute button. Also it is called by any other
+ associated KAction like the context menu.
+*/
+void MDWSwitch::toggleSwitch() {
+ if( m_mixdevice->isRecordable() )
+ setSwitch( !m_mixdevice->isRecSource() );
+ else
+ setSwitch( !m_mixdevice->isMuted() );
+}
+
+void MDWSwitch::setSwitch(bool value)
+{
+ if ( m_mixdevice->isSwitch() ) {
+ if ( m_mixdevice->isRecordable() ) {
+ m_mixer->setRecordSource( m_mixdevice->num(), value );
+ }
+ else {
+ m_mixdevice->setMuted( value );
+ m_mixer->commitVolumeChange( m_mixdevice );
+ }
+ }
+}
+
+void MDWSwitch::setDisabled()
+{
+ setDisabled( true );
+}
+
+void MDWSwitch::setDisabled( bool value ) {
+ if ( m_disabled!=value)
+ {
+ value ? hide() : show();
+ m_disabled = value;
+ }
+}
+
+/**
+ * An event filter for the various QWidgets. We watch for Mouse press Events, so
+ * that we can popup the context menu.
+ */
+bool MDWSwitch::eventFilter( QObject* obj, QEvent* e )
+{
+ if (e->type() == QEvent::MouseButtonPress) {
+ QMouseEvent *qme = static_cast<QMouseEvent*>(e);
+ if (qme->button() == Qt::RightButton) {
+ showContextMenu();
+ return true;
+ }
+ }
+ return QWidget::eventFilter(obj,e);
+}
+
+#include "mdwswitch.moc"
diff --git a/kmix/mdwswitch.h b/kmix/mdwswitch.h
new file mode 100644
index 00000000..fd145623
--- /dev/null
+++ b/kmix/mdwswitch.h
@@ -0,0 +1,88 @@
+//-*-C++-*-
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2004 Chrisitan Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef MDWSWITCH_H
+#define MDWSWITCH_H
+
+#include <kpanelapplet.h>
+
+#include <qwidget.h>
+#include "volume.h"
+#include <qptrlist.h>
+#include <qpixmap.h>
+#include <qrangecontrol.h>
+
+class QBoxLayout;
+class QLabel;
+class QPopupMenu;
+class QSlider;
+
+class KLedButton;
+class KAction;
+class KActionCollection;
+class KSmallSlider;
+class KGlobalAccel;
+
+class MixDevice;
+class VerticalText;
+class Mixer;
+class ViewBase;
+
+#include "mixdevicewidget.h"
+
+class MDWSwitch : public MixDeviceWidget
+{
+ Q_OBJECT
+
+public:
+ MDWSwitch( Mixer *mixer, MixDevice* md,
+ bool small, Qt::Orientation orientation,
+ QWidget* parent = 0, ViewBase* mw = 0, const char* name = 0);
+ ~MDWSwitch();
+
+ void addActionToPopup( KAction *action );
+ QSize sizeHint() const;
+ void setBackgroundMode(BackgroundMode m);
+ bool eventFilter( QObject* obj, QEvent* e );
+
+public slots:
+ // GUI hide and show
+ void setDisabled();
+ void setDisabled(bool);
+
+ // Switch on/off
+ void toggleSwitch();
+ void setSwitch(bool value);
+
+ void update();
+ virtual void showContextMenu();
+
+private:
+ void createWidgets();
+
+ QLabel *_label;
+ VerticalText *_labelV;
+ KLedButton *_switchLED;
+ QBoxLayout *_layout;
+};
+
+#endif
diff --git a/kmix/mixdevice.cpp b/kmix/mixdevice.cpp
new file mode 100644
index 00000000..16984ceb
--- /dev/null
+++ b/kmix/mixdevice.cpp
@@ -0,0 +1,221 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include "mixdevice.h"
+
+
+/**
+ * Constructs a MixDevice. A MixDevice represents one channel or control of
+ * the mixer hardware. A MixDevice has a type (e.g. PCM), a descriptive name
+ * (for example "Master" or "Headphone" or "IEC 958 Output"),
+ * can have a volume level (2 when stereo), can be recordable and muted.
+ * The category tells which kind of control the MixDevice is.
+ *
+ * Hints: Meaning of "category" has changed. In future the MixDevice might contain two
+ * Volume objects, one for Output (Playback volume) and one for Input (Record volume).
+ */
+MixDevice::MixDevice( int num, Volume &vol, bool recordable, bool mute,
+ QString name, ChannelType type, DeviceCategory category ) :
+ _volume( vol ), _type( type ), _num( num ), _recordable( recordable ),
+ _mute( mute ), _category( category )
+{
+ // Hint: "_volume" gets COPIED from "vol" due to the fact that the copy-constructor actually copies the volume levels.
+ _switch = false;
+ _recSource = false;
+ if( name.isEmpty() )
+ _name = i18n("unknown");
+ else
+ _name = name;
+
+ _pk.setNum(num);
+
+
+ if( category == MixDevice::SWITCH )
+ _switch = true;
+}
+
+MixDevice::MixDevice(const MixDevice &md) : QObject()
+{
+ _name = md._name;
+ _volume = md._volume;
+ _type = md._type;
+ _num = md._num;
+ _pk = md._pk;
+ _recordable = md._recordable;
+ _recSource = md._recSource;
+ _category = md._category;
+ _switch = md._switch;
+ _mute = md._mute;
+ _enumValues = md._enumValues;
+}
+
+MixDevice::~MixDevice() {
+ // Clear MixDevices enum Strings (switch on auto-delete, so the QString's inside will be cleared)
+ _enumValues.setAutoDelete(true);
+ _enumValues.clear();
+}
+
+Volume& MixDevice::getVolume()
+{
+ return _volume;
+}
+
+long MixDevice::getVolume(Volume::ChannelID chid) {
+ return _volume.getVolume(chid);
+}
+
+long MixDevice::maxVolume() {
+ return _volume.maxVolume();
+}
+
+long MixDevice::minVolume() {
+ return _volume.minVolume();
+}
+
+void MixDevice::setEnumId(int enumId)
+{
+ if ( enumId < _enumValues.count() ) {
+ _enumCurrentId = enumId;
+ }
+}
+
+unsigned int MixDevice::enumId()
+{
+ return _enumCurrentId;
+}
+
+QPtrList<QString>& MixDevice::enumValues() {
+ return _enumValues;
+}
+
+
+// @todo Used only at mixdevicewidget.cpp:625 . Replace that ASAP !!!
+void MixDevice::setVolume( int channel, int volume )
+{
+ _volume.setVolume( (Volume::ChannelID)channel /* ARGH! */, volume );
+}
+
+QString& MixDevice::getPK() {
+ return _pk;
+}
+
+void MixDevice::setPK(QString &PK) {
+ _pk = PK;
+ // The key is used in the config file. It should not contain spaces
+ _pk.replace(' ', '_');
+}
+
+/**
+ * This mehtod is currently only called on "kmixctrl --restore"
+ *
+ * Normally we have a working _volume object already, which is very important,
+ * because we need to read the minimum and maximum volume levels.
+ * (Another solutien would be to "equip" volFromConfig with maxInt and minInt values).
+ */
+void MixDevice::read( KConfig *config, const QString& grp )
+{
+ QString devgrp;
+ devgrp.sprintf( "%s.Dev%i", grp.ascii(), _num );
+ config->setGroup( devgrp );
+ //kdDebug(67100) << "MixDevice::read() of group devgrp=" << devgrp << endl;
+
+ char *nameLeftVolume, *nameRightVolume;
+ if ( _volume.isCapture() ) {
+ nameLeftVolume = "volumeLCapture";
+ nameRightVolume = "volumeRCapture";
+ } else {
+ nameLeftVolume = "volumeL";
+ nameRightVolume = "volumeR";
+ }
+ Volume::ChannelMask chMask = Volume::MNONE;
+ int vl = config->readNumEntry(nameLeftVolume, -1);
+ if (vl!=-1) {
+ chMask = (Volume::ChannelMask)(chMask | Volume::MLEFT);
+ }
+ int vr = config->readNumEntry(nameRightVolume, -1);
+ if (vr!=-1) {
+ chMask = (Volume::ChannelMask)(chMask | Volume::MRIGHT);
+ }
+
+ /*
+ * Now start construction a temporary Volume object.
+ * We take the maxvol and minvol values from _volume, which is already constructed.
+ * Otherwise we would have to wildly guess those values
+ */
+ Volume *volFromConfig = new Volume(chMask, _volume._maxVolume, _volume._minVolume);
+ if (vl!=-1) {
+ volFromConfig->setVolume(Volume::LEFT , vl);
+ }
+ if (vr!=-1) {
+ volFromConfig->setVolume(Volume::RIGHT, vr);
+ }
+ // commit the read config
+ _volume.setVolume(*volFromConfig);
+ delete volFromConfig;
+
+ int mute = config->readNumEntry("is_muted", -1);
+ if ( mute!=-1 ) {
+ _volume.setMuted( mute!=0 );
+ }
+
+ int recsrc = config->readNumEntry("is_recsrc", -1);
+ if ( recsrc!=-1 ) {
+ setRecSource( recsrc!=0 );
+ }
+
+ int enumId = config->readNumEntry("enum_id", -1);
+ if ( enumId != -1 ) {
+ setEnumId( enumId );
+ }
+}
+
+/**
+ * called on "kmixctrl --save" and from the GUI's (currently only on exit)
+ */
+void MixDevice::write( KConfig *config, const QString& grp )
+{
+ QString devgrp;
+ devgrp.sprintf( "%s.Dev%i", grp.ascii(), _num );
+ config->setGroup(devgrp);
+ // kdDebug(67100) << "MixDevice::write() of group devgrp=" << devgrp << endl;
+ char *nameLeftVolume, *nameRightVolume;
+ if ( _volume.isCapture() ) {
+ nameLeftVolume = "volumeLCapture";
+ nameRightVolume = "volumeRCapture";
+ } else {
+ nameLeftVolume = "volumeL";
+ nameRightVolume = "volumeR";
+ }
+ config->writeEntry(nameLeftVolume, getVolume( Volume::LEFT ) );
+ config->writeEntry(nameRightVolume, getVolume( Volume::RIGHT ) );
+ config->writeEntry("is_muted", (int)_volume.isMuted() );
+ config->writeEntry("is_recsrc", (int)isRecSource() );
+ config->writeEntry("name", _name);
+ if ( isEnum() ) {
+ config->writeEntry("enum_id", enumId() );
+ }
+}
+
+#include "mixdevice.moc"
+
diff --git a/kmix/mixdevice.h b/kmix/mixdevice.h
new file mode 100644
index 00000000..89ec052c
--- /dev/null
+++ b/kmix/mixdevice.h
@@ -0,0 +1,114 @@
+#ifndef MixDevice_h
+#define MixDevice_h
+
+#include "volume.h"
+#include <qstring.h>
+#include <kconfig.h>
+#include <qobject.h>
+#include <qptrlist.h>
+
+// ! @todo : CONSIDER MERGING OF MixDevice and Volume classes:
+// Not easy possible, because Volume is used in the driver backends
+
+/* !! @todo : Add 2 fields:
+ * bool update_from_Hardware;
+ * bool update_from_UI;
+ * They will show whether there are pending changes from both sides.
+ * Updates will be faster and more reliable by this.
+ */
+class MixDevice : public QObject
+{
+ Q_OBJECT
+
+ public:
+ // For each ChannelType a special icon exists
+ enum ChannelType {AUDIO = 1, BASS, CD, EXTERNAL, MICROPHONE,
+ MIDI, RECMONITOR, TREBLE, UNKNOWN, VOLUME,
+ VIDEO, SURROUND, HEADPHONE, DIGITAL, AC97,
+ SURROUND_BACK, SURROUND_LFE, SURROUND_CENTERFRONT, SURROUND_CENTERBACK
+ };
+
+
+ // The DeviceCategory tells the type of the device
+ // It is used in bitmasks, so you must use values of 2^n .
+ enum DeviceCategory { UNDEFINED= 0x00, SLIDER=0x01, SWITCH=0x02, ENUM=0x04, ALL=0xff };
+
+
+ MixDevice(int num, Volume &vol, bool recordable, bool mute,
+ QString name, ChannelType type = UNKNOWN, DeviceCategory category =
+SLIDER );
+ MixDevice(const MixDevice &md);
+ ~MixDevice();
+
+ int num() { return _num; };
+ QString name() { return _name; };
+ /**
+ * Returns an unique ID of this MixDevice. By default the number
+ * 'num' from the constructor is returned. It is recommended that
+ * a better ID is set directly after constructing the MixDevice using
+ * the setUniqueID().
+ */
+ QString& getPK();
+ /**
+ * Set a suitable PK for this MixDevice. It is used in looking up
+ * the keys in kmixrc. It is advised to set a nice name, like
+ * 'PCM_2', which would mean "2nd PCM device of the sound card".
+ */
+ void setPK(QString &id);
+ bool isRecordable() { return _recordable; };
+ bool isRecSource() { return _recSource; };
+ bool isSwitch() { return _switch; } // !! change to _category == MixDevice::SWITCH
+ bool isEnum() { return _category == MixDevice::ENUM; }
+ bool isMuted() { return _volume.isMuted(); };
+ bool hasMute() { return _mute; }
+
+ void setMuted(bool value) { _volume.setMuted( value ); };
+ void setVolume( int channel, int volume );
+ void setRecSource( bool rec ) { _recSource = rec; }
+ long getVolume(Volume::ChannelID chid);
+ Volume& getVolume();
+ long maxVolume();
+ long minVolume();
+
+ void setEnumId(int);
+ unsigned int enumId();
+ QPtrList<QString>& enumValues();
+
+ void read( KConfig *config, const QString& grp );
+ void write( KConfig *config, const QString& grp );
+
+ void setType( ChannelType channeltype ) { _type = channeltype; };
+ ChannelType type() { return _type; };
+
+ DeviceCategory category() { return _category; };
+
+ signals:
+ void newVolume( int num, Volume volume );
+
+ protected:
+ Volume _volume;
+ ChannelType _type;
+ // The DeviceCategory tells, how "important" a MixDevice is.
+ // The driver (e.g. mixer_oss.cpp) must set this value. It is
+ // used for deciding what Sliders to show and for distributing
+ // the sliders. It is advised to use the following categories:
+ // BASIC: Master, PCM
+ // PRIMARY: CD, Headphone, Microphone, Line
+ // SECONDARY: All others
+ // SWITCH: All devices which only have a On/Off-Switch
+ int _num; // ioctl() device number of mixer
+ bool _recordable; // Can it be recorded?
+ bool _switch; // On/Off switch // !! remove
+ bool _mute; // Available mute option
+ bool _recSource; // Current rec status
+ DeviceCategory _category; // category
+ QString _name; // Ascii channel name
+ QString _pk; // Primary key, used as part in config file keys
+ // A MixDevice, that is an ENUM, has these _enumValues
+ QPtrList<QString> _enumValues;
+ int _enumCurrentId;
+
+};
+
+#endif
+
diff --git a/kmix/mixdevicewidget.cpp b/kmix/mixdevicewidget.cpp
new file mode 100644
index 00000000..e032f361
--- /dev/null
+++ b/kmix/mixdevicewidget.cpp
@@ -0,0 +1,120 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <klocale.h>
+#include <kled.h>
+#include <kiconloader.h>
+#include <kconfig.h>
+#include <kaction.h>
+#include <kpopupmenu.h>
+#include <kglobalaccel.h>
+#include <kkeydialog.h>
+#include <kdebug.h>
+
+#include <qobject.h>
+#include <qcursor.h>
+#include <qslider.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpixmap.h>
+#include <qtooltip.h>
+#include <qwmatrix.h>
+
+#include "mixer.h"
+#include "mixdevicewidget.h"
+#include "viewbase.h"
+#include "kledbutton.h"
+#include "ksmallslider.h"
+#include "verticaltext.h"
+
+/**
+ * Class that represents a single mix device, inlcuding PopUp, muteLED, ...
+ * Used in KMix main window and DockWidget and PanelApplet.
+ * It can be configured to include or exclude the recordLED and the muteLED.
+ * The direction (horizontal, vertical) can be configured and whether it should
+ * be "small" (uses KSmallSlider instead of QSlider then).
+ */
+MixDeviceWidget::MixDeviceWidget(Mixer *mixer, MixDevice* md,
+ bool small, Qt::Orientation orientation,
+ QWidget* parent, ViewBase* mw, const char* name) :
+ QWidget( parent, name ), m_mixer(mixer), m_mixdevice( md ), m_mixerwidget( mw ),
+ m_disabled( false ), _orientation( orientation ), m_small( small )
+{
+ _mdwActions = new KActionCollection( this );
+ m_keys = new KGlobalAccel( this, "Keys" );
+}
+
+MixDeviceWidget::~MixDeviceWidget()
+{
+}
+
+
+void MixDeviceWidget::addActionToPopup( KAction *action )
+{
+ _mdwActions->insert( action );
+}
+
+
+bool MixDeviceWidget::isDisabled() const
+{
+ return m_disabled;
+}
+
+
+KGlobalAccel *MixDeviceWidget::keys( void )
+{
+ return m_keys;
+}
+
+void MixDeviceWidget::defineKeys()
+{
+ if (m_keys) {
+ KKeyDialog::configure(m_keys, 0, false);
+ // The keys are saved in KMixerWidget::saveConfig, see kmixerwidget.cpp
+ m_keys->updateConnections();
+ }
+}
+
+void MixDeviceWidget::volumeChange( int ) { /* is virtual */ }
+void MixDeviceWidget::setDisabled( bool ) { /* is virtual */ }
+void MixDeviceWidget::setVolume( int /*channel*/, int /*vol*/ ) { /* is virtual */ }
+void MixDeviceWidget::setVolume( Volume /*vol*/ ) { /* is virtual */ }
+void MixDeviceWidget::update() { /* is virtual */ }
+void MixDeviceWidget::showContextMenu() { /* is virtual */ }
+void MixDeviceWidget::setColors( QColor , QColor , QColor ) { /* is virtual */ }
+void MixDeviceWidget::setIcons( bool ) { /* is virtual */ }
+void MixDeviceWidget::setLabeled( bool ) { /* is virtual */ }
+void MixDeviceWidget::setMutedColors( QColor , QColor , QColor ) { /* is virtual */ }
+
+
+
+
+void MixDeviceWidget::mousePressEvent( QMouseEvent *e )
+{
+ if ( e->button()==RightButton )
+ showContextMenu();
+ else {
+ QWidget::mousePressEvent(e);
+ }
+}
+
+
+#include "mixdevicewidget.moc"
diff --git a/kmix/mixdevicewidget.h b/kmix/mixdevicewidget.h
new file mode 100644
index 00000000..96242e73
--- /dev/null
+++ b/kmix/mixdevicewidget.h
@@ -0,0 +1,115 @@
+//-*-C++-*-
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
+ * 1996-2000 Christian Esken <esken@kde.org>
+ * Sven Fischer <herpes@kawo2.rwth-aachen.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef MIXDEVICEWIDGET_H
+#define MIXDEVICEWIDGET_H
+
+#include <kpanelapplet.h>
+
+#include <qwidget.h>
+#include "volume.h"
+#include <qptrlist.h>
+#include <qpixmap.h>
+#include <qrangecontrol.h>
+
+class QBoxLayout;
+class QLabel;
+class QPopupMenu;
+class QSlider;
+
+class KLed;
+class KLedButton;
+class KAction;
+class KActionCollection;
+class KSmallSlider;
+class KGlobalAccel;
+
+class MixDevice;
+class VerticalText;
+class Mixer;
+class ViewBase;
+
+class MixDeviceWidget
+ : public QWidget
+{
+ Q_OBJECT
+
+public:
+ enum ValueStyle { NNONE = 0, NABSOLUTE = 1, NRELATIVE = 2 } ;
+
+ MixDeviceWidget( Mixer *mixer, MixDevice* md,
+ bool small, Qt::Orientation orientation,
+ QWidget* parent = 0, ViewBase* mw = 0, const char* name = 0);
+ ~MixDeviceWidget();
+
+ void addActionToPopup( KAction *action );
+
+ virtual bool isDisabled() const;
+ MixDevice* mixDevice() { return m_mixdevice; };
+
+ virtual void setColors( QColor high, QColor low, QColor back );
+ virtual void setIcons( bool value );
+ virtual void setMutedColors( QColor high, QColor low, QColor back );
+
+ virtual bool isStereoLinked() const { return false; };
+ //virtual bool isLabeled() const { return false; };
+ virtual void setStereoLinked( bool ) {};
+ virtual void setLabeled( bool );
+ virtual void setTicks( bool ) {};
+ virtual void setValueStyle( ValueStyle ) {};
+
+ virtual KGlobalAccel *keys(void);
+
+public slots:
+ virtual void setDisabled( bool value );
+ virtual void defineKeys();
+ virtual void update();
+ virtual void showContextMenu();
+
+signals:
+ void newVolume( int num, Volume volume );
+ void newMasterVolume( Volume volume );
+ void masterMuted( bool );
+ void newRecsrc( int num, bool on );
+
+protected slots:
+ void volumeChange( int );
+ virtual void setVolume( int channel, int volume );
+ virtual void setVolume( Volume volume );
+
+protected:
+ Mixer* m_mixer;
+ MixDevice* m_mixdevice;
+ KActionCollection* _mdwActions;
+ KGlobalAccel* m_keys;
+ ViewBase* m_mixerwidget;
+ bool m_disabled;
+ Qt::Orientation _orientation;
+ bool m_small;
+
+private:
+ void mousePressEvent( QMouseEvent *e );
+};
+
+#endif
diff --git a/kmix/mixer.cpp b/kmix/mixer.cpp
new file mode 100644
index 00000000..316625e0
--- /dev/null
+++ b/kmix/mixer.cpp
@@ -0,0 +1,753 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2004 Christian Esken - esken@kde.org
+ * 2002 Helio Chissini de Castro - helio@conectiva.com.br
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <qtimer.h>
+
+#include <klocale.h>
+#include <kconfig.h>
+#include <kglobal.h>
+#include <kdebug.h>
+#include <dcopobject.h>
+
+#include "mixer.h"
+#include "mixer_backend.h"
+#include "kmix-platforms.cpp"
+#include "volume.h"
+
+//#define MIXER_MASTER_DEBUG
+
+#ifdef MIXER_MASTER_DEBUG
+#warning MIXER_MASTER_DEBUG is enabled. DO NOT SHIP KMIX LIKE THIS !!!
+#endif
+
+/**
+ * Some general design hints. Hierachy is Mixer->MixDevice->Volume
+ */
+
+// !! Warning: Don't commit with "KMIX_DCOP_OBJID_TEST" #define'd (cesken)
+#undef KMIX_DCOP_OBJID_TEST
+int Mixer::_dcopID = 0;
+
+QPtrList<Mixer> Mixer::s_mixers;
+QString Mixer::_masterCard;
+QString Mixer::_masterCardDevice;
+
+int Mixer::numDrivers()
+{
+ MixerFactory *factory = g_mixerFactories;
+ int num = 0;
+ while( factory->getMixer!=0 )
+ {
+ num++;
+ factory++;
+ }
+
+ return num;
+}
+
+/*
+ * Returns a reference of the current mixer list.
+ */
+QPtrList<Mixer>& Mixer::mixers()
+{
+ return s_mixers;
+}
+
+
+Mixer::Mixer( int driver, int device ) : DCOPObject( "Mixer" )
+{
+ _pollingTimer = 0;
+
+ _mixerBackend = 0;
+ getMixerFunc *f = g_mixerFactories[driver].getMixer;
+ if( f!=0 ) {
+ _mixerBackend = f( device );
+ }
+
+ readSetFromHWforceUpdate(); // enforce an initial update on first readSetFromHW()
+
+ m_balance = 0;
+ m_profiles.setAutoDelete( true );
+
+ _pollingTimer = new QTimer(); // will be started on open() and stopped on close()
+ connect( _pollingTimer, SIGNAL(timeout()), this, SLOT(readSetFromHW()));
+
+ QCString objid;
+#ifndef KMIX_DCOP_OBJID_TEST
+ objid.setNum(_mixerBackend->m_devnum);
+#else
+// should use a nice name like the Unique-Card-ID instead !!
+ objid.setNum(Mixer::_dcopID);
+ Mixer::_dcopID ++;
+#endif
+ objid.prepend("Mixer");
+ DCOPObject::setObjId( objid );
+
+}
+
+Mixer::~Mixer() {
+ // Close the mixer. This might also free memory, depending on the called backend method
+ close();
+ delete _pollingTimer;
+}
+
+void Mixer::volumeSave( KConfig *config )
+{
+ // kdDebug(67100) << "Mixer::volumeSave()" << endl;
+ readSetFromHW();
+ QString grp("Mixer");
+ grp.append(mixerName());
+ _mixerBackend->m_mixDevices.write( config, grp );
+}
+
+void Mixer::volumeLoad( KConfig *config )
+{
+ QString grp("Mixer");
+ grp.append(mixerName());
+ if ( ! config->hasGroup(grp) ) {
+ // no such group. Volumes (of this mixer) were never saved beforehand.
+ // Thus don't restore anything (also see Bug #69320 for understanding the real reason)
+ return; // make sure to bail out immediately
+ }
+
+ // else restore the volumes
+ _mixerBackend->m_mixDevices.read( config, grp );
+
+ // set new settings
+ QPtrListIterator<MixDevice> it( _mixerBackend->m_mixDevices );
+ for(MixDevice *md=it.toFirst(); md!=0; md=++it )
+ {
+ // kdDebug(67100) << "Mixer::volumeLoad() writeVolumeToHW(" << md->num() << ", "<< md->getVolume() << ")" << endl;
+ // !! @todo Restore record source
+ //setRecordSource( md->num(), md->isRecSource() );
+ _mixerBackend->setRecsrcHW( md->num(), md->isRecSource() );
+ _mixerBackend->writeVolumeToHW( md->num(), md->getVolume() );
+ if ( md->isEnum() ) _mixerBackend->setEnumIdHW( md->num(), md->enumId() );
+ }
+}
+
+
+/**
+ * Opens the mixer.
+ * Also, starts the polling timer, for polling the Volumes from the Mixer.
+ *
+ * @return 0, if OK. An Mixer::ERR_ error code otherwise
+ */
+int Mixer::open()
+{
+ int err = _mixerBackend->open();
+ // A better ID is now calculated in mixertoolbox.cpp, and set via setID(),
+ // but we want a somhow usable fallback just in case.
+ _id = mixerName();
+
+ if( err == ERR_INCOMPATIBLESET ) // !!! When does this happen ?!?
+ {
+ // Clear the mixdevices list
+ _mixerBackend->m_mixDevices.clear();
+ // try again with fresh set
+ err = _mixerBackend->open();
+ }
+
+ MixDevice* recommendedMaster = _mixerBackend->recommendedMaster();
+ if ( recommendedMaster != 0 ) {
+ setMasterDevice(recommendedMaster->getPK() );
+ }
+ else {
+ kdError(67100) << "Mixer::open() no master detected." << endl;
+ QString noMaster = "---no-master-detected---";
+ setMasterDevice(noMaster); // no master
+ }
+ /*
+ // --------- Copy the hardware values to the MixDevice -------------------
+ MixSet &mset = _mixerBackend->m_mixDevices;
+ if( !mset.isEmpty() ) {
+ // Copy the initial mix set
+ // kdDebug(67100) << "Mixer::setupMixer() copy Set" << endl;
+ MixDevice* md;
+ for( md = mset.first(); md != 0; md = mset.next() )
+ {
+ MixDevice* mdCopy = _mixerBackend->m_mixDevices.first();
+ while( mdCopy!=0 && mdCopy->num() != md->num() ) {
+ mdCopy = _mixerBackend->m_mixDevices.next();
+ }
+ if ( mdCopy != 0 ) {
+ // The "mdCopy != 0" was not checked before, but its safer to do so
+ setRecordSource( md->num(), md->isRecSource() );
+ Volume &vol = mdCopy->getVolume();
+ vol.setVolume( md->getVolume() );
+ mdCopy->setMuted( md->isMuted() );
+
+ // !! might need writeVolumeToHW( mdCopy->num(), mdCopy->getVolume() );
+ }
+ }
+ }
+ */
+ if ( _mixerBackend->needsPolling() ) {
+ _pollingTimer->start(50);
+ }
+ else {
+ _mixerBackend->prepareSignalling(this);
+ // poll once to give the GUI a chance to rebuild it's info
+ QTimer::singleShot( 50, this, SLOT( readSetFromHW() ) );
+ }
+ return err;
+}
+
+
+/**
+ * Closes the mixer.
+ * Also, stops the polling timer.
+ *
+ * @return 0 (always)
+ */
+int Mixer::close()
+{
+ _pollingTimer->stop();
+ return _mixerBackend->close();
+}
+
+
+/* ------- WRAPPER METHODS. START ------------------------------ */
+unsigned int Mixer::size() const
+{
+ return _mixerBackend->m_mixDevices.count();
+}
+
+MixDevice* Mixer::operator[](int num)
+{
+ MixDevice* md = _mixerBackend->m_mixDevices.at( num );
+ Q_ASSERT( md );
+ return md;
+}
+
+MixSet Mixer::getMixSet()
+{
+ return _mixerBackend->m_mixDevices;
+}
+
+bool Mixer::isValid() {
+ return _mixerBackend->isValid();
+}
+
+bool Mixer::isOpen() const {
+ if ( _mixerBackend == 0 )
+ return false;
+ else
+ return _mixerBackend->isOpen();
+}
+
+/* ------- WRAPPER METHODS. END -------------------------------- */
+
+/**
+ * After calling this, readSetFromHW() will do a complete update. This will
+ * trigger emitting the appropriate signals like newVolumeLevels().
+ *
+ * This method is useful, if you need to get a "refresh signal" - used at:
+ * 1) Start of KMix - so that we can be sure an initial signal is emitted
+ * 2) When reconstructing any MixerWidget (e.g. DockIcon after applying preferences)
+ */
+void Mixer::readSetFromHWforceUpdate() const {
+ _readSetFromHWforceUpdate = true;
+}
+
+/**
+ You can call this to retrieve the freshest information from the mixer HW.
+ This method is also called regulary by the mixer timer.
+*/
+void Mixer::readSetFromHW()
+{
+ if ( ! _mixerBackend->isOpen() ) {
+ // bail out immediately, if the mixer is not open.
+ // This can happen currently only, if the user executes the DCOP close() call.
+ return;
+ }
+ bool updated = _mixerBackend->prepareUpdateFromHW();
+ if ( (! updated) && (! _readSetFromHWforceUpdate) ) {
+ // Some drivers (ALSA) are smart. We don't need to run the following
+ // time-consuming update loop if there was no change
+ return;
+ }
+ _readSetFromHWforceUpdate = false;
+ MixDevice* md;
+ for( md = _mixerBackend->m_mixDevices.first(); md != 0; md = _mixerBackend->m_mixDevices.next() )
+ {
+ Volume& vol = md->getVolume();
+ _mixerBackend->readVolumeFromHW( md->num(), vol );
+ md->setRecSource( _mixerBackend->isRecsrcHW( md->num() ) );
+ if (md->isEnum() ) {
+ md->setEnumId( _mixerBackend->enumIdHW(md->num()) );
+ }
+ }
+ // Trivial implementation. Without looking at the devices
+ // kdDebug(67100) << "Mixer::readSetFromHW(): emit newVolumeLevels()" << endl;
+ emit newVolumeLevels();
+ emit newRecsrc(); // cheap, but works
+}
+
+
+void Mixer::setBalance(int balance)
+{
+ // !! BAD, because balance only works on the master device. If you have not Master, the slider is a NOP
+ if( balance == m_balance ) {
+ // balance unchanged => return
+ return;
+ }
+
+ m_balance = balance;
+
+ MixDevice* master = masterDevice();
+ if ( master == 0 ) {
+ // no master device available => return
+ return;
+ }
+
+ Volume& vol = master->getVolume();
+ _mixerBackend->readVolumeFromHW( master->num(), vol );
+
+ int left = vol[ Volume::LEFT ];
+ int right = vol[ Volume::RIGHT ];
+ int refvol = left > right ? left : right;
+ if( balance < 0 ) // balance left
+ {
+ vol.setVolume( Volume::LEFT, refvol);
+ vol.setVolume( Volume::RIGHT, (balance * refvol) / 100 + refvol );
+ }
+ else
+ {
+ vol.setVolume( Volume::LEFT, -(balance * refvol) / 100 + refvol );
+ vol.setVolume( Volume::RIGHT, refvol);
+ }
+
+ _mixerBackend->writeVolumeToHW( master->num(), vol );
+
+ emit newBalance( vol );
+}
+
+QString Mixer::mixerName()
+{
+ return _mixerBackend->m_mixerName;
+}
+
+QString Mixer::driverName( int driver )
+{
+ getDriverNameFunc *f = g_mixerFactories[driver].getDriverName;
+ if( f!=0 )
+ return f();
+ else
+ return "unknown";
+}
+
+void Mixer::setID(QString& ref_id)
+{
+ _id = ref_id;
+}
+
+
+QString& Mixer::id()
+{
+ return _id;
+}
+
+void Mixer::setMasterCard(QString& ref_id)
+{
+ // The value is taken over without checking on existance. This allows the User to define
+ // a MasterCard that is not always available (e.g. it is an USB hotplugging device).
+ // Also you can set the master at any time you like, e.g. after reading the KMix configuration file
+ // and before actually constructing the Mixer instances (hint: this mehtod is static!).
+ _masterCard = ref_id;
+}
+
+Mixer* Mixer::masterCard()
+{
+ Mixer *mixer = 0;
+ kdDebug(67100) << "Mixer::masterCard() searching for id=" << _masterCard << "\n";
+ for (mixer=Mixer::mixers().first(); mixer!=0; mixer=Mixer::mixers().next())
+ {
+ if ( mixer->id() == _masterCard ) {
+#ifdef MIXER_MASTER_DEBUG
+ kdDebug(67100) << "Mixer::masterCard() found id=" << mixer->id() << "\n";
+#endif
+ break;
+ }
+ }
+#ifdef MIXER_MASTER_DEBUG
+ if ( mixer == 0) kdDebug(67100) << "Mixer::masterCard() found no Mixer* mixer \n";
+#endif
+ return mixer;
+}
+
+void Mixer::setMasterCardDevice(QString& ref_id)
+{
+ // The value is taken over without checking on existance. This allows the User to define
+ // a MasterCard that is not always available (e.g. it is an USB hotplugging device).
+ // Also you can set the master at any time you like, e.g. after reading the KMix configuration file
+ // and before actually constructing the Mixer instances (hint: this mehtod is static!).
+ _masterCardDevice = ref_id;
+#ifdef MIXER_MASTER_DEBUG
+ kdDebug(67100) << "Mixer::setMasterCardDevice(\"" << ref_id << "\")\n";
+#endif
+}
+
+MixDevice* Mixer::masterCardDevice()
+{
+ MixDevice* md = 0;
+ Mixer *mixer = masterCard();
+ if ( mixer != 0 ) {
+ for( md = mixer->_mixerBackend->m_mixDevices.first(); md != 0; md = mixer->_mixerBackend->m_mixDevices.next() ) {
+
+
+ if ( md->getPK() == _masterCardDevice )
+ {
+#ifdef MIXER_MASTER_DEBUG
+ kdDebug(67100) << "Mixer::masterCardDevice() getPK()="
+ << md->getPK() << " , _masterCardDevice="
+ << _masterCardDevice << "\n";
+#endif
+ break;
+ }
+ }
+ }
+
+#ifdef MIXER_MASTER_DEBUG
+ if ( md == 0) kdDebug(67100) << "Mixer::masterCardDevice() found no MixDevice* md" "\n";
+#endif
+
+ return md;
+}
+
+
+
+
+/**
+ Used internally by the Mixer class and as DCOP method
+*/
+void Mixer::setRecordSource( int devnum, bool on )
+{
+ if( !_mixerBackend->setRecsrcHW( devnum, on ) ) // others have to be updated
+ {
+ for( MixDevice* md = _mixerBackend->m_mixDevices.first(); md != 0; md = _mixerBackend->m_mixDevices.next() ) {
+ bool isRecsrc = _mixerBackend->isRecsrcHW( md->num() );
+// kdDebug(67100) << "Mixer::setRecordSource(): isRecsrcHW(" << md->num() << ") =" << isRecsrc << endl;
+ md->setRecSource( isRecsrc );
+ }
+ // emitting is done after read
+ //emit newRecsrc(); // like "emit newVolumeLevels()", but for record source
+ }
+ else {
+ // just the actual mixdevice
+ for( MixDevice* md = _mixerBackend->m_mixDevices.first(); md != 0; md = _mixerBackend->m_mixDevices.next() ) {
+ if( md->num() == devnum ) {
+ bool isRecsrc = _mixerBackend->isRecsrcHW( md->num() );
+ md->setRecSource( isRecsrc );
+ }
+ }
+ // emitting is done after read
+ //emit newRecsrc(); // like "emit newVolumeLevels()", but for record source
+ }
+
+}
+
+
+MixDevice* Mixer::masterDevice()
+{
+ return find( _masterDevicePK );
+}
+
+void Mixer::setMasterDevice(QString &devPK)
+{
+ _masterDevicePK = devPK;
+}
+
+
+MixDevice* Mixer::find(QString& devPK)
+{
+ MixDevice* md = 0;
+ for( md = _mixerBackend->m_mixDevices.first(); md != 0; md = _mixerBackend->m_mixDevices.next() ) {
+ if( devPK == md->getPK() ) {
+ break;
+ }
+ }
+ return md;
+}
+
+
+MixDevice *Mixer::mixDeviceByType( int deviceidx )
+{
+ unsigned int i=0;
+ while (i<size() && (*this)[i]->num()!=deviceidx) i++;
+ if (i==size()) return 0;
+
+ return (*this)[i];
+}
+
+// @dcop
+// Used also by the setMasterVolume() method.
+void Mixer::setVolume( int deviceidx, int percentage )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (!mixdev) return;
+
+ Volume vol=mixdev->getVolume();
+ // @todo The next call doesn't handle negative volumes correctly.
+ vol.setAllVolumes( (percentage*vol.maxVolume())/100 );
+ _mixerBackend->writeVolumeToHW(deviceidx, vol);
+}
+
+/**
+ Call this if you have a *reference* to a Volume object and have modified that locally.
+ Pass the MixDevice associated to that Volume to this method for writing back
+ the changed value to the mixer.
+ Hint: Why do we do it this way?
+ - It is fast (no copying of Volume objects required)
+ - It is easy to understand ( read - modify - commit )
+*/
+void Mixer::commitVolumeChange( MixDevice* md ) {
+ _mixerBackend->writeVolumeToHW(md->num(), md->getVolume() );
+ _mixerBackend->setEnumIdHW(md->num(), md->enumId() );
+}
+
+// @dcop only
+void Mixer::setMasterVolume( int percentage )
+{
+ MixDevice *master = masterDevice();
+ if (master != 0 ) {
+ setVolume( master->num(), percentage );
+ }
+}
+
+// @dcop
+int Mixer::volume( int deviceidx )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (!mixdev) return 0;
+
+ Volume vol=mixdev->getVolume();
+ // @todo This will not work, if minVolume != 0 !!!
+ // e.g.: minVolume=5 or minVolume=-10
+ // The solution is to check two cases:
+ // volume < 0 => use minVolume for volumeRange
+ // volume > 0 => use maxVolume for volumeRange
+ // If chosen volumeRange==0 => return 0
+ // As this is potentially used often (Sliders, ...), it
+ // should beimplemented in the Volume class.
+
+ // For now we go with "maxVolume()", like in the rest of KMix.
+ long volumeRange = vol.maxVolume(); // -vol.minVolume() ;
+ if ( volumeRange == 0 )
+ {
+ return 0;
+ }
+ else
+ {
+ return ( vol.getVolume( Volume::LEFT )*100) / volumeRange ;
+ }
+}
+
+// @dcop , especially for use in KMilo
+void Mixer::setAbsoluteVolume( int deviceidx, long absoluteVolume ) {
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (!mixdev) return;
+
+ Volume vol=mixdev->getVolume();
+ vol.setAllVolumes( absoluteVolume );
+ _mixerBackend->writeVolumeToHW(deviceidx, vol);
+}
+
+// @dcop , especially for use in KMilo
+long Mixer::absoluteVolume( int deviceidx )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (!mixdev) return 0;
+
+ Volume vol=mixdev->getVolume();
+ long avgVolume=vol.getAvgVolume((Volume::ChannelMask)(Volume::MLEFT | Volume::MRIGHT));
+ return avgVolume;
+}
+
+// @dcop , especially for use in KMilo
+long Mixer::absoluteVolumeMax( int deviceidx )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (!mixdev) return 0;
+
+ Volume vol=mixdev->getVolume();
+ long maxVolume=vol.maxVolume();
+ return maxVolume;
+}
+
+// @dcop , especially for use in KMilo
+long Mixer::absoluteVolumeMin( int deviceidx )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (!mixdev) return 0;
+
+ Volume vol=mixdev->getVolume();
+ long minVolume=vol.minVolume();
+ return minVolume;
+}
+
+// @dcop
+int Mixer::masterVolume()
+{
+ int vol = 0;
+ MixDevice *master = masterDevice();
+ if (master != 0 ) {
+ vol = volume( master->num() );
+ }
+ return vol;
+}
+
+// @dcop
+void Mixer::increaseVolume( int deviceidx )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (mixdev != 0) {
+ Volume vol=mixdev->getVolume();
+ double fivePercent = (vol.maxVolume()-vol.minVolume()+1) / 20;
+ for (unsigned int i=Volume::CHIDMIN; i <= Volume::CHIDMAX; i++) {
+ int volToChange = vol.getVolume((Volume::ChannelID)i);
+ if ( fivePercent < 1 ) fivePercent = 1;
+ volToChange += (int)fivePercent;
+ vol.setVolume((Volume::ChannelID)i, volToChange);
+ }
+ _mixerBackend->writeVolumeToHW(deviceidx, vol);
+ }
+
+ /* see comment at the end of decreaseVolume()
+ int vol=volume(deviceidx);
+ setVolume(deviceidx, vol+5);
+ */
+}
+
+// @dcop
+void Mixer::decreaseVolume( int deviceidx )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (mixdev != 0) {
+ Volume vol=mixdev->getVolume();
+ double fivePercent = (vol.maxVolume()-vol.minVolume()+1) / 20;
+ for (unsigned int i=Volume::CHIDMIN; i <= Volume::CHIDMAX; i++) {
+ int volToChange = vol.getVolume((Volume::ChannelID)i);
+ //std::cout << "Mixer::decreaseVolume(): before: volToChange " <<i<< "=" <<volToChange << std::endl;
+ if ( fivePercent < 1 ) fivePercent = 1;
+ volToChange -= (int)fivePercent;
+ //std::cout << "Mixer::decreaseVolume(): after: volToChange " <<i<< "=" <<volToChange << std::endl;
+ vol.setVolume((Volume::ChannelID)i, volToChange);
+ //int volChanged = vol.getVolume((Volume::ChannelID)i);
+ //std::cout << "Mixer::decreaseVolume(): check: volChanged " <<i<< "=" <<volChanged << std::endl;
+ } // for
+ _mixerBackend->writeVolumeToHW(deviceidx, vol);
+ }
+
+ /************************************************************
+ It is important, not to implement this method like this:
+ int vol=volume(deviceidx);
+ setVolume(deviceidx, vol-5);
+ It creates too big rounding errors. If you don't beleive me, then
+ do a decreaseVolume() and increaseVolume() with "vol.maxVolume() == 31".
+ ***********************************************************/
+}
+
+// @dcop
+void Mixer::setMute( int deviceidx, bool on )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (!mixdev) return;
+
+ mixdev->setMuted( on );
+
+ _mixerBackend->writeVolumeToHW(deviceidx, mixdev->getVolume() );
+}
+
+// @dcop only
+void Mixer::setMasterMute( bool on )
+{
+ MixDevice *master = masterDevice();
+ if (master != 0 ) {
+ setMute( master->num(), on );
+ }
+}
+
+
+// @dcop
+void Mixer::toggleMute( int deviceidx )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (!mixdev) return;
+
+ bool previousState= mixdev->isMuted();
+
+ mixdev->setMuted( !previousState );
+
+ _mixerBackend->writeVolumeToHW(deviceidx, mixdev->getVolume() );
+}
+
+// @dcop only
+void Mixer::toggleMasterMute()
+{
+ MixDevice *master = masterDevice();
+ if (master != 0 ) {
+ toggleMute( master->num() );
+ }
+}
+
+
+// @dcop
+bool Mixer::mute( int deviceidx )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (!mixdev) return true;
+
+ return mixdev->isMuted();
+}
+
+// @dcop only
+bool Mixer::masterMute()
+{
+ MixDevice *master = masterDevice();
+ if (master != 0 ) {
+ return mute( master->num() );
+ }
+ return true;
+}
+
+// @dcop only
+int Mixer::masterDeviceIndex()
+{
+ return masterDevice()->num();
+}
+
+bool Mixer::isRecordSource( int deviceidx )
+{
+ MixDevice *mixdev= mixDeviceByType( deviceidx );
+ if (!mixdev) return false;
+
+ return mixdev->isRecSource();
+}
+
+/// @DCOP WHAT DOES THIS METHOD?!?!?
+bool Mixer::isAvailableDevice( int deviceidx )
+{
+ return mixDeviceByType( deviceidx );
+}
+
+#include "mixer.moc"
diff --git a/kmix/mixer.h b/kmix/mixer.h
new file mode 100644
index 00000000..b6d0917a
--- /dev/null
+++ b/kmix/mixer.h
@@ -0,0 +1,174 @@
+//-*-C++-*-
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
+ * 1996-2000 Christian Esken <esken@kde.org>
+ * Sven Fischer <herpes@kawo2.rwth-aachen.de>
+ * 2002 - Helio Chissini de Castro <helio@conectiva.com.br>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef MIXER_H
+#define MIXER_H
+
+#include <qstring.h>
+#include <qtimer.h>
+#include <qobject.h>
+#include <qintdict.h>
+#include <qptrlist.h>
+
+#include "volume.h"
+class Mixer_Backend;
+#include "mixerIface.h"
+#include "mixset.h"
+#include "mixdevice.h"
+
+class Volume;
+class KConfig;
+
+class Mixer : public QObject, virtual public MixerIface
+{
+ Q_OBJECT
+
+ public:
+ enum MixerError { ERR_PERM=1, ERR_WRITE, ERR_READ, ERR_NODEV, ERR_NOTSUPP,
+ ERR_OPEN, ERR_LASTERR, ERR_NOMEM, ERR_INCOMPATIBLESET, ERR_MIXEROPEN };
+
+ Mixer( int driver, int device );
+ virtual ~Mixer();
+
+ static int numDrivers();
+
+ MixDevice* find(QString& devPK);
+
+ void volumeSave( KConfig *config );
+ void volumeLoad( KConfig *config );
+
+ /// Tells the number of the mixing devices
+ unsigned int size() const;
+
+ bool isValid();
+ bool isOpen() const;
+
+ /// Returns a pointer to the mix device with the given number
+ MixDevice* operator[](int val_i_num);
+
+ /// Returns a pointer to the mix device whose type matches the value
+ /// given by the parameter and the array MixerDevNames given in
+ /// mixer_oss.cpp (0 is Volume, 4 is PCM, etc.)
+ MixDevice *mixDeviceByType( int deviceidx );
+
+ /// Open/grab the mixer for further intraction
+ virtual int open();
+ /// Close/release the mixer
+ virtual int close();
+
+ /// Returns a detailed state message after errors. Only for diagnostic purposes, no i18n.
+ QString& stateMessage() const;
+
+ virtual QString mixerName();
+
+ // Returns the name of the driver, e.g. "OSS" or "ALSA0.9"
+ static QString driverName(int num);
+
+ /// Returns an unique ID of the Mixer. It currently looks like "<soundcard_descr>:<hw_number>@<driver>"
+ QString& id();
+ /// The owner/creator of the Mixer can set an unique name here. This key should never displayed to
+ /// the user, but can be used for referencing configuration items and such.
+ void setID(QString& ref_id);
+
+ /// The KMix global master card. Please note that KMix and KMixPanelApplet can have a
+ /// different MasterCard's at the moment (but actually KMixPanelApplet does not read/save this yet).
+ /// At the moment it is only used for selecting the Mixer to use in KMix's DockIcon.
+ static void setMasterCard(QString& ref_id);
+ static Mixer* masterCard();
+ /// The global Master Device inside the current MasterCard (as returned by masterCard()).
+ static void setMasterCardDevice(QString& ref_id);
+ static MixDevice* masterCardDevice();
+
+
+ /// get the actual MixSet
+ MixSet getMixSet();
+
+ /// Returns the master volume device (doesn't work out :-(. See masterCard() and masterCardDevice() instead)
+ MixDevice* masterDevice();
+ /// Sets the master volume device (doesn't work out :-(. See setMasterCard() and setMasterCardDevice() instead)
+ void setMasterDevice(QString&);
+
+ /// DCOP oriented methods (look at mixerIface.h for the descriptions)
+ virtual void setVolume( int deviceidx, int percentage );
+ virtual void setAbsoluteVolume( int deviceidx, long absoluteVolume );
+ virtual void setMasterVolume( int percentage );
+
+ virtual void increaseVolume( int deviceidx );
+ virtual void decreaseVolume( int deviceidx );
+
+ virtual long absoluteVolume( int deviceidx );
+ virtual long absoluteVolumeMin( int deviceidx );
+ virtual long absoluteVolumeMax( int deviceidx );
+ virtual int volume( int deviceidx );
+ virtual int masterVolume();
+ virtual int masterDeviceIndex();
+
+ virtual void setMute( int deviceidx, bool on );
+ virtual void setMasterMute( bool on );
+ virtual bool mute( int deviceidx );
+ virtual bool masterMute();
+ virtual void toggleMute( int deviceidx );
+ virtual void toggleMasterMute();
+ virtual bool isRecordSource( int deviceidx );
+
+ virtual bool isAvailableDevice( int deviceidx );
+
+ void commitVolumeChange( MixDevice* md );
+
+ public slots:
+ virtual void readSetFromHW();
+ void readSetFromHWforceUpdate() const;
+ virtual void setRecordSource( int deviceidx, bool on );
+
+ virtual void setBalance(int balance); // sets the m_balance (see there)
+
+ signals:
+ void newBalance( Volume& );
+ void newRecsrc( void );
+ void newVolumeLevels(void);
+
+ protected:
+ QTimer* _pollingTimer;
+
+ int m_balance; // from -100 (just left) to 100 (just right)
+
+ QPtrList<MixSet> m_profiles;
+ static QPtrList<Mixer> s_mixers;
+
+ public:
+ int setupMixer( MixSet set );
+ static QPtrList<Mixer>& mixers();
+
+ private:
+ Mixer_Backend *_mixerBackend;
+ mutable bool _readSetFromHWforceUpdate;
+ static int _dcopID;
+ QString _id;
+ QString _masterDevicePK;
+ static QString _masterCard;
+ static QString _masterCardDevice;
+};
+
+#endif
diff --git a/kmix/mixerIface.h b/kmix/mixerIface.h
new file mode 100644
index 00000000..6c8da9fd
--- /dev/null
+++ b/kmix/mixerIface.h
@@ -0,0 +1,135 @@
+#ifndef __MIXER_IFACE_H
+#define __MIXER_IFACE_H
+
+#include <dcopobject.h>
+
+class MixerIface : virtual public DCOPObject
+{
+ K_DCOP
+
+k_dcop:
+ /**
+ Sets the volume of the device with index deviceidx to the percentage
+ specified in the second parameter. The deviceidx is got from the array
+ at the start of mixer_oss.cpp .
+ */
+ virtual void setVolume( int deviceidx, int percentage )=0;
+ /**
+ A simpler way to access the master volume (which is deviceidx 0).
+ */
+ virtual void setMasterVolume( int percentage )=0;
+
+ /**
+ Increase the volume of the given device by a 5% .
+ */
+ virtual void increaseVolume( int deviceidx )=0;
+ /**
+ Decrease the volume of the given device by a 5% .
+ */
+ virtual void decreaseVolume( int deviceidx )=0;
+
+ /**
+ Returns the volume of the device (as a percentage, 0..100).
+ */
+ virtual int volume( int deviceidx )=0;
+ /**
+ Returns the volume of the master device (as a percentage, 0..100).
+ */
+ virtual int masterVolume()=0;
+
+
+ /**
+ Sets the absolute volume of the device. Lower bound is absoluteVolumeMin(),
+ upper bound is absoluteVolumeMax().
+ */
+ virtual void setAbsoluteVolume( int deviceidx, long absoluteVolume )=0;
+ /**
+ Returns the absolute volume of the device. The volume is in the range of
+ absoluteVolumeMin() <= absoluteVolume() <= absoluteVolumeMax()
+ */
+ virtual long absoluteVolume( int deviceidx )=0;
+ /**
+ Returns the absolute maximum volume of the device.
+ */
+ virtual long absoluteVolumeMin( int deviceidx )=0;
+ /**
+ Returns the absolute minimum volume of the device.
+ */
+ virtual long absoluteVolumeMax( int deviceidx )=0;
+
+ /**
+ Mutes or unmutes the specified device.
+ */
+ virtual void setMute( int deviceidx, bool on )=0;
+ /**
+ Mutes or unmutes the master device.
+ */
+ virtual void setMasterMute( bool on )=0;
+ /**
+ Toggles mute-state for the given device.
+ */
+ virtual void toggleMute( int deviceidx )=0;
+ /**
+ Toggles mute-state for the master device.
+ */
+ virtual void toggleMasterMute()=0;
+ /**
+ Returns if the given device is muted or not. If the device is not
+ available in this mixer, it is reported as muted.
+ */
+ virtual bool mute( int deviceidx )=0;
+ /**
+ Returns if the master device is muted or not. If the device is not
+ available in this mixer, it is reported as muted.
+ */
+ virtual bool masterMute()=0;
+
+ /**
+ Returns the index of the master device
+ */
+ virtual int masterDeviceIndex()=0;
+
+ /**
+ Makes the given device a record source.
+ */
+ virtual void setRecordSource( int deviceidx, bool on )=0;
+
+ /**
+ Returns if the given device is a record source.
+ */
+ virtual bool isRecordSource( int deviceidx )=0;
+
+ /**
+ Sets the balance of the master device (negative means balanced to the left
+ speaker and positive to the right one)
+ */
+ virtual void setBalance( int balance )=0;
+
+ /**
+ Returns true if the given device is available in the current mixer
+ and false if it's not.
+ */
+ virtual bool isAvailableDevice( int deviceidx )=0;
+
+ /**
+ Returns the name of the mixer.
+ */
+ virtual QString mixerName()=0;
+
+ /**
+ * Open/grab the mixer for further intraction
+ * You should use this method after a prior call of close(). See close() for usage directions.
+ */
+ virtual int open()=0;
+
+ /**
+ * Close/release the mixer
+ * This method SHOULD NOT be used via DCOP. You MAY use it if you really need to close the mixer device,
+ * for example when a software suspend action (ACPI) takes place, and the soundcard driver
+ * doesn't handle this situation gracefully.
+ */
+ virtual int close()=0;
+
+};
+
+#endif
diff --git a/kmix/mixer_alsa.h b/kmix/mixer_alsa.h
new file mode 100644
index 00000000..da04e372
--- /dev/null
+++ b/kmix/mixer_alsa.h
@@ -0,0 +1,53 @@
+#ifndef MIXER_ALSA_H
+#define MIXER_ALSA_H
+
+// QT includes
+#include <qvaluelist.h>
+
+// Forward QT includes
+class QString;
+class QSocketNotifier;
+
+#include "mixer_backend.h"
+
+class Mixer_ALSA : public Mixer_Backend
+{
+ public:
+ Mixer_ALSA( int device = -1 );
+ ~Mixer_ALSA();
+
+ virtual int readVolumeFromHW( int devnum, Volume &vol );
+ virtual int writeVolumeToHW( int devnum, Volume &vol );
+ virtual bool setRecsrcHW( int devnum, bool on);
+ virtual bool isRecsrcHW( int devnum );
+ virtual void setEnumIdHW(int mixerIdx, unsigned int);
+ virtual unsigned int enumIdHW(int mixerIdx);
+ virtual bool prepareUpdateFromHW();
+ virtual bool needsPolling() { return false; }
+ virtual void prepareSignalling( Mixer *mixer );
+
+ protected:
+ virtual int open();
+ virtual int close();
+
+ private:
+ int identify( snd_mixer_selem_id_t *sid );
+ snd_mixer_elem_t* getMixerElem(int devnum);
+ void removeSignalling();
+
+ virtual QString errorText(int mixer_error);
+ typedef QValueList<snd_mixer_selem_id_t *>AlsaMixerSidList;
+ AlsaMixerSidList mixer_sid_list;
+ typedef QValueList<snd_mixer_elem_t *> AlsaMixerElemList; // !! remove
+ AlsaMixerElemList mixer_elem_list; // !! remove
+
+ bool _initialUpdate;
+ snd_mixer_t *_handle;
+ QString devName;
+ struct pollfd *m_fds;
+ QSocketNotifier **m_sns;
+ int m_count;
+
+};
+
+#endif
diff --git a/kmix/mixer_alsa9.cpp b/kmix/mixer_alsa9.cpp
new file mode 100644
index 00000000..e55f6112
--- /dev/null
+++ b/kmix/mixer_alsa9.cpp
@@ -0,0 +1,828 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ * Alsa 0.9x and 1.0 - Based on original alsamixer code
+ * from alsa-project ( www/alsa-project.org )
+ *
+ *
+ * Copyright (C) 2002 Helio Chissini de Castro <helio@conectiva.com.br>
+ * 2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// STD Headers
+#include <stdlib.h>
+#include <stdio.h>
+#include <iostream>
+#include <assert.h>
+#include <qsocketnotifier.h>
+
+extern "C"
+{
+ #include <alsa/asoundlib.h>
+}
+
+// KDE Headers
+#include <klocale.h>
+#include <kdebug.h>
+
+// Local Headers
+#include "mixer_alsa.h"
+//#include "mixer.h"
+#include "volume.h"
+// #define if you want MUCH debugging output
+//#define ALSA_SWITCH_DEBUG
+//#define KMIX_ALSA_VOLUME_DEBUG
+
+Mixer_Backend*
+ALSA_getMixer( int device )
+{
+ Mixer_Backend *l_mixer;
+ l_mixer = new Mixer_ALSA( device );
+ return l_mixer;
+}
+
+Mixer_ALSA::Mixer_ALSA( int device ) : Mixer_Backend( device )
+{
+ m_fds = 0;
+ m_sns = 0;
+ _handle = 0;
+ _initialUpdate = true;
+}
+
+Mixer_ALSA::~Mixer_ALSA()
+{
+ close();
+}
+
+int
+Mixer_ALSA::identify( snd_mixer_selem_id_t *sid )
+{
+ QString name = snd_mixer_selem_id_get_name( sid );
+
+ if ( name == "Master" ) return MixDevice::VOLUME;
+ if ( name == "Capture" ) return MixDevice::RECMONITOR;
+ if ( name == "Master Mono" ) return MixDevice::VOLUME;
+ if ( name == "PC Speaker" ) return MixDevice::VOLUME;
+ if ( name == "Music" || name == "Synth" || name == "FM" ) return MixDevice::MIDI;
+ if ( name.find( "Headphone", 0, false ) != -1 ) return MixDevice::HEADPHONE;
+ if ( name == "Bass" ) return MixDevice::BASS;
+ if ( name == "Treble" ) return MixDevice::TREBLE;
+ if ( name == "CD" ) return MixDevice::CD;
+ if ( name == "Video" ) return MixDevice::VIDEO;
+ if ( name == "PCM" || name == "Wave" ) return MixDevice::AUDIO;
+ if ( name == "Surround" ) return MixDevice::SURROUND_BACK;
+ if ( name == "Center" ) return MixDevice::SURROUND_CENTERFRONT;
+ if ( name.find( "ac97", 0, false ) != -1 ) return MixDevice::AC97;
+ if ( name.find( "coaxial", 0, false ) != -1 ) return MixDevice::DIGITAL;
+ if ( name.find( "optical", 0, false ) != -1 ) return MixDevice::DIGITAL;
+ if ( name.find( "IEC958", 0, false ) != -1 ) return MixDevice::DIGITAL;
+ if ( name.find( "Mic" ) != -1 ) return MixDevice::MICROPHONE;
+ if ( name.find( "LFE" ) != -1 ) return MixDevice::SURROUND_LFE;
+ if ( name.find( "Monitor" ) != -1 ) return MixDevice::RECMONITOR;
+ if ( name.find( "3D", 0, false ) != -1 ) return MixDevice::SURROUND; // Should be probably some own icon
+
+ return MixDevice::EXTERNAL;
+}
+
+int
+Mixer_ALSA::open()
+{
+ bool virginOpen = m_mixDevices.isEmpty();
+ bool validDevice = false;
+ bool masterChosen = false;
+ int err;
+
+ snd_ctl_t *ctl_handle;
+ snd_ctl_card_info_t *hw_info;
+ snd_ctl_card_info_alloca(&hw_info);
+
+ snd_mixer_elem_t *elem;
+ snd_mixer_selem_id_t *sid;
+ snd_mixer_selem_id_alloca( &sid );
+
+ // Card information
+ if( m_devnum == -1 )
+ m_devnum = 0;
+ if ( (unsigned)m_devnum > 31 )
+ devName = "default";
+ else
+ devName = QString( "hw:%1" ).arg( m_devnum );
+
+ QString probeMessage;
+
+ if (virginOpen)
+ probeMessage += "Trying ALSA Device '" + devName + "': ";
+
+ if ( ( err = snd_ctl_open ( &ctl_handle, devName.latin1(), 0 ) ) < 0 )
+ {
+ kdDebug(67100) << probeMessage << "not found: snd_ctl_open err=" << snd_strerror(err) << endl;
+ //_stateMessage = errorText( Mixer::ERR_NODEV );
+ return Mixer::ERR_OPEN;
+ }
+
+ if ( ( err = snd_ctl_card_info ( ctl_handle, hw_info ) ) < 0 )
+ {
+ kdDebug(67100) << probeMessage << "not found: snd_ctl_card_info err=" << snd_strerror(err) << endl;
+ //_stateMessage = errorText( Mixer::ERR_READ );
+ snd_ctl_close( ctl_handle );
+ return Mixer::ERR_READ;
+ }
+
+ // Device and mixer names
+ const char* mixer_card_name = snd_ctl_card_info_get_name( hw_info );
+ //mixer_device_name = snd_ctl_card_info_get_mixername( hw_info );
+ // Copy the name of kmix mixer from card name (mixername is rumoured to be not that good)
+ m_mixerName = mixer_card_name;
+
+ snd_ctl_close( ctl_handle );
+
+ /* open mixer device */
+
+ //kdDebug(67100) << "IN Mixer_ALSA snd_mixer_open()" << endl;
+ if ( ( err = snd_mixer_open ( &_handle, 0 ) ) < 0 )
+ {
+ kdDebug(67100) << probeMessage << "not found: snd_mixer_open err=" << snd_strerror(err) << endl;
+ //errormsg( Mixer::ERR_NODEV );
+ _handle = 0;
+ return Mixer::ERR_NODEV; // if we cannot open the mixer, we have no devices
+ }
+ //kdDebug(67100) << "OUT Mixer_ALSA snd_mixer_open()" << endl;
+
+ if ( ( err = snd_mixer_attach ( _handle, devName.latin1() ) ) < 0 )
+ {
+ kdDebug(67100) << probeMessage << "not found: snd_mixer_attach err=" << snd_strerror(err) << endl;
+ //errormsg( Mixer::ERR_PERM );
+ return Mixer::ERR_OPEN;
+ }
+
+ if ( ( err = snd_mixer_selem_register ( _handle, NULL, NULL ) ) < 0 )
+ {
+ kdDebug(67100) << probeMessage << "not found: snd_mixer_selem_register err=" << snd_strerror(err) << endl;
+ //errormsg( Mixer::ERR_READ );
+ return Mixer::ERR_READ;
+ }
+
+ if ( ( err = snd_mixer_load ( _handle ) ) < 0 )
+ {
+ kdDebug(67100) << probeMessage << "not found: snd_mixer_load err=" << snd_strerror(err) << endl;
+ //errormsg( Mixer::ERR_READ );
+ close();
+ return Mixer::ERR_READ;
+ }
+
+ kdDebug(67100) << probeMessage << "found" << endl;
+
+ unsigned int mixerIdx = 0;
+ for ( elem = snd_mixer_first_elem( _handle ); elem; elem = snd_mixer_elem_next( elem ), mixerIdx++ )
+ {
+ // If element is not active, just skip
+ if ( ! snd_mixer_selem_is_active ( elem ) ) {
+ // ...but we still want to insert a null value into our mixer element
+ // list so that the list indexes match up.
+ mixer_elem_list.append( 0 );
+ mixer_sid_list.append( 0 );
+ continue;
+ }
+
+
+ sid = (snd_mixer_selem_id_t*)malloc(snd_mixer_selem_id_sizeof()); // I believe *we* must malloc it for ourself
+ snd_mixer_selem_get_id( elem, sid );
+
+ bool canRecord = false;
+ bool canMute = false;
+ bool canCapture = false;
+ long maxVolumePlay= 0, minVolumePlay= 0;
+ long maxVolumeRec = 0, minVolumeRec = 0;
+ validDevice = true;
+
+ snd_mixer_selem_get_playback_volume_range( elem, &minVolumePlay, &maxVolumePlay );
+ snd_mixer_selem_get_capture_volume_range( elem, &minVolumeRec , &maxVolumeRec );
+ // New mix device
+ MixDevice::ChannelType ct = (MixDevice::ChannelType)identify( sid );
+/*
+ if (!masterChosen && ct==MixDevice::VOLUME) {
+ // Determine a nicer MasterVolume
+ m_masterDevice = mixerIdx;
+ masterChosen = true;
+ }
+*/
+ if( virginOpen )
+ {
+ MixDevice::DeviceCategory cc = MixDevice::UNDEFINED;
+
+ //kdDebug() << "--- Loop: name=" << snd_mixer_selem_id_get_name( sid ) << " , mixerIdx=" << mixerIdx << "------------" << endl;
+
+ Volume* volPlay = 0, *volCapture = 0;
+ QPtrList<QString> enumList;
+ if ( snd_mixer_selem_is_enumerated(elem) ) {
+ cc = MixDevice::ENUM;
+ volPlay = new Volume(); // Dummy, unused
+ volCapture = new Volume();
+ mixer_elem_list.append( elem );
+ mixer_sid_list.append( sid );
+
+ // --- get Enum names START ---
+ int numEnumitems = snd_mixer_selem_get_enum_items(elem);
+ if ( numEnumitems > 0 ) {
+ // OK. no error
+ for (int iEnum = 0; iEnum<numEnumitems; iEnum++ ) {
+ char buffer[100];
+ int ret = snd_mixer_selem_get_enum_item_name(elem, iEnum, 99, buffer);
+ if ( ret == 0 ) {
+ QString* enumName = new QString(buffer);
+ //enumName->append(buffer);
+ enumList.append( enumName);
+ } // enumName could be read succesfully
+ } // for all enum items of this device
+ } // no error in reading enum list
+ else {
+ // 0 items or Error code => ignore this entry
+ }
+ // --- get Enum names END ---
+ } // is an enum
+
+ else {
+ Volume::ChannelMask chn = Volume::MNONE;
+ Volume::ChannelMask chnTmp;
+ if ( snd_mixer_selem_has_playback_volume(elem) ) {
+ //kdDebug(67100) << "has_playback_volume()" << endl;
+ chnTmp = snd_mixer_selem_is_playback_mono ( elem )
+ ? Volume::MLEFT : (Volume::ChannelMask)(Volume::MLEFT | Volume::MRIGHT);
+ chn = (Volume::ChannelMask) (chn | chnTmp);
+ cc = MixDevice::SLIDER;
+ volPlay = new Volume( chn, maxVolumePlay, minVolumePlay );
+ } else {
+ volPlay = new Volume();
+ }
+ if ( snd_mixer_selem_has_capture_volume(elem) ) {
+ //kdDebug(67100) << "has_capture_volume()" << endl;
+ chnTmp = snd_mixer_selem_is_capture_mono( elem )
+ ? Volume::MLEFT : (Volume::ChannelMask)(Volume::MLEFT | Volume::MRIGHT );
+ chn = (Volume::ChannelMask) (chn | chnTmp);
+ cc = MixDevice::SLIDER;
+ canCapture = true;
+ volCapture = new Volume( chn, maxVolumeRec, minVolumeRec, true );
+ } else {
+ volCapture = new Volume();
+ }
+
+ /* Create Volume object. If there is no volume on this device,
+ * it will be created with maxVolume == 0 && minVolume == 0 */
+ mixer_elem_list.append( elem );
+ mixer_sid_list.append( sid );
+
+ if ( snd_mixer_selem_has_playback_switch ( elem ) ) {
+ //kdDebug(67100) << "has_playback_switch()" << endl;
+ canMute = true;
+ }
+ if ( snd_mixer_selem_has_capture_switch ( elem ) ) {
+ //kdDebug(67100) << "has_capture_switch()" << endl;
+ canRecord = true;
+ }
+ if ( snd_mixer_selem_has_common_switch ( elem ) ) {
+ //kdDebug(67100) << "has_common_switch()" << endl;
+ canMute = true;
+ canRecord = true;
+ }
+
+ if ( /*snd_mixer_selem_has_common_switch ( elem ) || */
+ cc == MixDevice::UNDEFINED )
+ {
+ // Everything unknown is handled as switch
+ cc = MixDevice::SWITCH;
+ }
+ } // is ordinary mixer element (NOT an enum)
+
+ MixDevice* md = new MixDevice( mixerIdx,
+ *volPlay,
+ canRecord,
+ canMute,
+ snd_mixer_selem_id_get_name( sid ),
+ ct,
+ cc );
+
+ m_mixDevices.append( md );
+
+
+ if (!masterChosen && ct==MixDevice::VOLUME) {
+ // Determine a nicer MasterVolume
+ m_recommendedMaster = md;
+ masterChosen = true;
+ }
+
+ if ( canCapture && !canRecord ) {
+ MixDevice *mdCapture =
+ new MixDevice( mixerIdx,
+ *volCapture,
+ true,
+ canMute,
+ snd_mixer_selem_id_get_name( sid ),
+ ct,
+ cc );
+ m_mixDevices.append( mdCapture );
+ }
+
+ if ( enumList.count() > 0 ) {
+ int maxEnumId= enumList.count();
+ QPtrList<QString>& enumValuesRef = md->enumValues(); // retrieve a ref
+ for (int i=0; i<maxEnumId; i++ ) {
+ // we have an enum. Lets set the names of the enum items in the MixDevice
+ // the enum names are assumed to be static!
+ enumValuesRef.append(enumList.at(i) );
+ }
+ }
+ //kdDebug(67100) << "ALSA create MDW, vol= " << *vol << endl;
+ delete volPlay;
+ delete volCapture;
+ } // virginOpen
+ else
+ {
+ MixDevice* md;
+ bool found = false;
+ for ( md = m_mixDevices.first(); md != 0; md = m_mixDevices.next() ) {
+ if ( md->num() == mixerIdx ) {
+ found = true;
+ writeVolumeToHW( mixerIdx, md->getVolume() );
+ }
+ }
+ if( !found )
+ {
+ return Mixer::ERR_INCOMPATIBLESET;
+ }
+ } // !virginOpen
+ } // for all elems
+
+ /**************************************************************************************
+ // If no devices are supported by this soundcard, return "NO Devices"
+ It is VERY important to return THIS error code, so that the caller knows, that the
+ the device exists.
+ This is used for scanning for existing soundcard devices, see MixerToolBox::initMixer().
+ ***************************************************************************************/
+ if ( !validDevice )
+ {
+ return Mixer::ERR_NODEV;
+ }
+
+ // Copy the name of kmix mixer from card name
+ // Real name of mixer is not too good
+ m_mixerName = mixer_card_name;
+
+ // return with success
+ m_isOpen = true;
+
+ /* setup for select on stdin and the mixer fd */
+ if ((m_count = snd_mixer_poll_descriptors_count(_handle)) < 0) {
+ kdDebug(67100) << "Mixer_ALSA::poll() , snd_mixer_poll_descriptors_count() err=" << m_count << "\n";
+ return Mixer::ERR_OPEN;
+ }
+
+ //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() 2\n";
+
+ m_fds = (struct pollfd*)calloc(m_count, sizeof(struct pollfd));
+ if (m_fds == NULL) {
+ kdDebug(67100) << "Mixer_ALSA::poll() , calloc() = null" << "\n";
+ return Mixer::ERR_OPEN;
+ }
+
+ m_fds->events = POLLIN;
+ if ((err = snd_mixer_poll_descriptors(_handle, m_fds, m_count)) < 0) {
+ kdDebug(67100) << "Mixer_ALSA::poll() , snd_mixer_poll_descriptors_count() err=" << err << "\n";
+ return Mixer::ERR_OPEN;
+ }
+ if (err != m_count) {
+ kdDebug(67100) << "Mixer_ALSA::poll() , snd_mixer_poll_descriptors_count() err=" << err << " m_count=" << m_count << "\n";
+ return Mixer::ERR_OPEN;
+ }
+
+ return 0;
+}
+
+void Mixer_ALSA::prepareSignalling( Mixer *mixer )
+{
+ assert( !m_sns );
+
+ m_sns = new QSocketNotifier*[m_count];
+ for ( int i = 0; i < m_count; ++i )
+ {
+ kdDebug() << "socket " << i << endl;
+ m_sns[i] = new QSocketNotifier(m_fds[i].fd, QSocketNotifier::Read);
+ mixer->connect(m_sns[i], SIGNAL(activated(int)), mixer, SLOT(readSetFromHW()));
+ }
+}
+
+void Mixer_ALSA::removeSignalling()
+{
+ if ( m_fds )
+ free( m_fds );
+ m_fds = 0;
+
+ if ( m_sns )
+ {
+ for ( int i = 0; i < m_count; i++ )
+ delete m_sns[i];
+ delete [] m_sns;
+ m_sns = 0;
+ }
+}
+
+int
+Mixer_ALSA::close()
+{
+ int ret=0;
+ m_isOpen = false;
+ if ( _handle != 0 )
+ {
+ //kdDebug(67100) << "IN Mixer_ALSA::close()" << endl;
+ snd_mixer_free ( _handle );
+ if ( ( ret = snd_mixer_detach ( _handle, devName.latin1() ) ) < 0 )
+ {
+ kdDebug(67100) << "snd_mixer_detach err=" << snd_strerror(ret) << endl;
+ }
+ int ret2 = 0;
+ if ( ( ret2 = snd_mixer_close ( _handle ) ) < 0 )
+ {
+ kdDebug(67100) << "snd_mixer_close err=" << snd_strerror(ret2) << endl;
+ if ( ret == 0 ) ret = ret2; // no error before => use current error code
+ }
+
+ _handle = 0;
+ //kdDebug(67100) << "OUT Mixer_ALSA::close()" << endl;
+
+ }
+
+ mixer_elem_list.clear();
+ mixer_sid_list.clear();
+ m_mixDevices.clear();
+
+ removeSignalling();
+
+ return ret;
+}
+
+
+snd_mixer_elem_t* Mixer_ALSA::getMixerElem(int devnum) {
+ snd_mixer_elem_t* elem = 0;
+ if ( ! m_isOpen ) return elem; // unplugging guard
+
+ if ( int( mixer_sid_list.count() ) > devnum ) {
+ snd_mixer_selem_id_t * sid = mixer_sid_list[ devnum ];
+ // The next line (hopefully) only finds selem's, not elem's.
+ elem = snd_mixer_find_selem(_handle, sid);
+
+ if ( elem == 0 ) {
+ // !! Check, whether the warning should be omitted. Probably
+ // Route controls are non-simple elements.
+ kdDebug(67100) << "Error finding mixer element " << devnum << endl;
+ }
+ }
+ return elem;
+
+/*
+ I would have liked to use the following trivial implementation instead of the
+ code above. But it will also return elem's. which are not selem's. As there is
+ no way to check an elem's type (e.g. elem->type == SND_MIXER_ELEM_SIMPLE), callers
+ of getMixerElem() cannot check the type. :-(
+ snd_mixer_elem_t* elem = mixer_elem_list[ devnum ];
+ return elem;
+ */
+}
+
+bool Mixer_ALSA::prepareUpdateFromHW()
+{
+ if ( !m_fds || !m_isOpen )
+ return false;
+
+ //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() 1\n";
+
+ // Poll on fds with 10ms timeout
+ // Hint: alsamixer has an infinite timeout, but we cannot do this because we would block
+ // the X11 event handling (Qt event loop) with this.
+ //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() 3\n";
+ int finished = poll(m_fds, m_count, 10);
+ //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() 4\n";
+
+ bool updated = false;
+ if (finished > 0) {
+ //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() 5\n";
+
+ unsigned short revents;
+
+ if (snd_mixer_poll_descriptors_revents(_handle, m_fds, m_count, &revents) >= 0) {
+ //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() 6\n";
+
+ if (revents & POLLNVAL) {
+ /* Bug 127294 shows, that we receieve POLLNVAL when the user
+ unplugs an USB soundcard. Lets close the card. */
+ kdDebug(67100) << "Mixer_ALSA::poll() , Error: poll() returns POLLNVAL\n";
+ close(); // Card was unplugged (unplug, driver unloaded)
+ return false;
+ }
+ if (revents & POLLERR) {
+ kdDebug(67100) << "Mixer_ALSA::poll() , Error: poll() returns POLLERR\n";
+ return false;
+ }
+ if (revents & POLLIN) {
+ //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() 7\n";
+
+ snd_mixer_handle_events(_handle);
+ updated = true;
+ }
+ }
+
+ }
+ //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() " << updated << endl;;
+ return updated;
+}
+
+bool
+Mixer_ALSA::isRecsrcHW( int devnum )
+{
+ bool isCurrentlyRecSrc = false;
+ snd_mixer_elem_t *elem = getMixerElem( devnum );
+
+ if ( !elem ) {
+ return false;
+ }
+
+ if ( snd_mixer_selem_has_capture_switch( elem ) ) {
+ // Has a on-off switch
+ // Yes, this element can be record source. But the user can switch it off, so lets see if it is switched on.
+ int swLeft;
+ int ret = snd_mixer_selem_get_capture_switch( elem, SND_MIXER_SCHN_FRONT_LEFT, &swLeft );
+ if ( ret != 0 ) {
+ kdDebug(67100) << "snd_mixer_selem_get_capture_switch() failed 1\n";
+ }
+
+ if (snd_mixer_selem_has_capture_switch_joined( elem ) ) {
+ isCurrentlyRecSrc = (swLeft != 0);
+#ifdef ALSA_SWITCH_DEBUG
+ kdDebug(67100) << "Mixer_ALSA::isRecsrcHW() has_switch joined: #" << devnum << " >>> " << swLeft << " : " << isCurrentlyRecSrc << endl;
+#endif
+ }
+ else {
+ int swRight;
+ snd_mixer_selem_get_capture_switch( elem, SND_MIXER_SCHN_FRONT_RIGHT, &swRight );
+ isCurrentlyRecSrc = ( (swLeft != 0) || (swRight != 0) );
+#ifdef ALSA_SWITCH_DEBUG
+ kdDebug(67100) << "Mixer_ALSA::isRecsrcHW() has_switch non-joined, state " << isCurrentlyRecSrc << endl;
+#endif
+ }
+ }
+ else {
+ // Has no on-off switch
+ if ( snd_mixer_selem_has_capture_volume( elem ) ) {
+ // Has a volume, but has no OnOffSwitch => We assume that this is a fixed record source (always on). (esken)
+ isCurrentlyRecSrc = true;
+#ifdef ALSA_SWITCH_DEBUG
+ kdDebug(67100) << "Mixer_ALSA::isRecsrcHW() has_no_switch, state " << isCurrentlyRecSrc << endl;
+#endif
+ }
+ }
+
+ return isCurrentlyRecSrc;
+}
+
+bool
+Mixer_ALSA::setRecsrcHW( int devnum, bool on )
+{
+ int sw = 0;
+ if (on)
+ sw = !sw;
+
+ snd_mixer_elem_t *elem = getMixerElem( devnum );
+ if ( !elem )
+ {
+ return 0;
+ }
+
+ if (snd_mixer_selem_has_capture_switch_joined( elem ) )
+ {
+ int before, after;
+ int ret = snd_mixer_selem_get_capture_switch( elem, SND_MIXER_SCHN_FRONT_LEFT, &before );
+ if ( ret != 0 ) {
+ kdDebug(67100) << "snd_mixer_selem_get_capture_switch() failed 1\n";
+ }
+
+ ret = snd_mixer_selem_set_capture_switch_all( elem, sw );
+ if ( ret != 0 ) {
+ kdDebug(67100) << "snd_mixer_selem_set_capture_switch_all() failed 2: errno=" << ret << "\n";
+ }
+ ret = snd_mixer_selem_get_capture_switch( elem, SND_MIXER_SCHN_FRONT_LEFT, &after );
+ if ( ret != 0 ) {
+ kdDebug(67100) << "snd_mixer_selem_get_capture_switch() failed 3: errno=" << ret << "\n";
+ }
+
+#ifdef ALSA_SWITCH_DEBUG
+ kdDebug(67100) << "Mixer_ALSA::setRecsrcHW(" << devnum << "," << on << ")joined. Before=" << before << " Set=" << sw << " After=" << after <<"\n";
+#endif
+
+ }
+ else
+ {
+#ifdef ALSA_SWITCH_DEBUG
+ kdDebug(67100) << "Mixer_ALSA::setRecsrcHW LEFT\n";
+#endif
+ snd_mixer_selem_set_capture_switch( elem, SND_MIXER_SCHN_FRONT_LEFT, sw );
+#ifdef ALSA_SWITCH_DEBUG
+ kdDebug(67100) << "Mixer_ALSA::setRecsrcHW RIGHT\n";
+#endif
+ snd_mixer_selem_set_capture_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, sw);
+ }
+
+#ifdef ALSA_SWITCH_DEBUG
+ kdDebug(67100) << "EXIT Mixer_ALSA::setRecsrcHW(" << devnum << "," << on << ")\n";
+#endif
+ return false; // we should always return false, so that other devnum's get updated
+ // The ALSA polling has been implemented some time ago. So it should be safe to
+ // return "true" here.
+ // The other devnum's Rec-Sources won't get update by KMix code, but ALSA will send
+ // us an event, if neccesary. But OTOH it is possibly better not to trust alsalib fully,
+ // because the old code is working also well (just takes more processing time).
+ // return true;
+}
+
+/**
+ * Sets the ID of the currently selected Enum entry.
+ * Warning: ALSA supports to have different enums selected on each channel
+ * of the SAME snd_mixer_elem_t. KMix does NOT support that and
+ * always sets both channels (0 and 1).
+ */
+void Mixer_ALSA::setEnumIdHW(int mixerIdx, unsigned int idx) {
+ //kdDebug(67100) << "Mixer_ALSA::setEnumIdHW(" << mixerIdx << ", idx=" << idx << ") 1\n";
+ snd_mixer_elem_t *elem = getMixerElem( mixerIdx );
+ if ( elem==0 || ( !snd_mixer_selem_is_enumerated(elem)) )
+ {
+ return;
+ }
+
+ //kdDebug(67100) << "Mixer_ALSA::setEnumIdHW(" << mixerIdx << ", idx=" << idx << ") 2\n";
+ int ret = snd_mixer_selem_set_enum_item(elem,SND_MIXER_SCHN_FRONT_LEFT,idx);
+ if (ret < 0) {
+ kdError(67100) << "Mixer_ALSA::setEnumIdHW(" << mixerIdx << "), errno=" << ret << "\n";
+ }
+ snd_mixer_selem_set_enum_item(elem,SND_MIXER_SCHN_FRONT_RIGHT,idx);
+ // we don't care about possible error codes on channel 1
+ return;
+}
+
+/**
+ * Return the ID of the currently selected Enum entry.
+ * Warning: ALSA supports to have different enums selected on each channel
+ * of the SAME snd_mixer_elem_t. KMix does NOT support that and
+ * always shows the value of the first channel.
+ */
+unsigned int Mixer_ALSA::enumIdHW(int mixerIdx) {
+ snd_mixer_elem_t *elem = getMixerElem( mixerIdx );
+ if ( elem==0 || ( !snd_mixer_selem_is_enumerated(elem)) )
+ {
+ return 0;
+ }
+
+ unsigned int idx = 0;
+ int ret = snd_mixer_selem_get_enum_item(elem,SND_MIXER_SCHN_FRONT_LEFT,&idx);
+ //kdDebug(67100) << "Mixer_ALSA::enumIdHW(" << mixerIdx << ") idx=" << idx << "\n";
+ if (ret < 0) {
+ idx = 0;
+ kdError(67100) << "Mixer_ALSA::enumIdHW(" << mixerIdx << "), errno=" << ret << "\n";
+ }
+ return idx;
+}
+
+
+int
+Mixer_ALSA::readVolumeFromHW( int mixerIdx, Volume &volume )
+{
+ int elem_sw;
+ long left, right;
+
+ snd_mixer_elem_t *elem = getMixerElem( mixerIdx );
+ if ( !elem )
+ {
+ return 0;
+ }
+
+
+ // *** READ PLAYBACK VOLUMES *************
+ if ( snd_mixer_selem_has_playback_volume( elem ) && !volume.isCapture() )
+ {
+ int ret = snd_mixer_selem_get_playback_volume( elem, SND_MIXER_SCHN_FRONT_LEFT, &left );
+ if ( ret != 0 ) kdDebug(67100) << "readVolumeFromHW(" << mixerIdx << ") [has_playback_volume,R] failed, errno=" << ret << endl;
+ if ( snd_mixer_selem_is_playback_mono ( elem )) {
+ volume.setVolume( Volume::LEFT , left );
+ volume.setVolume( Volume::RIGHT, left );
+ }
+ else {
+ int ret = snd_mixer_selem_get_playback_volume( elem, SND_MIXER_SCHN_FRONT_RIGHT, &right );
+ if ( ret != 0 ) kdDebug(67100) << "readVolumeFromHW(" << mixerIdx << ") [has_playback_volume,R] failed, errno=" << ret << endl;
+ volume.setVolume( Volume::LEFT , left );
+ volume.setVolume( Volume::RIGHT, right );
+ }
+ }
+ else
+ if ( snd_mixer_selem_has_capture_volume ( elem ) && volume.isCapture() )
+ {
+ int ret = snd_mixer_selem_get_capture_volume ( elem, SND_MIXER_SCHN_FRONT_LEFT, &left );
+ if ( ret != 0 ) kdDebug(67100) << "readVolumeFromHW(" << mixerIdx << ") [get_capture_volume,L] failed, errno=" << ret << endl;
+ if ( snd_mixer_selem_is_capture_mono ( elem )) {
+ volume.setVolume( Volume::LEFT , left );
+ volume.setVolume( Volume::RIGHT, left );
+ }
+ else
+ {
+ int ret = snd_mixer_selem_get_capture_volume( elem, SND_MIXER_SCHN_FRONT_RIGHT, &right );
+ if ( ret != 0 ) kdDebug(67100) << "readVolumeFromHW(" << mixerIdx << ") [has_capture_volume,R] failed, errno=" << ret << endl;
+ volume.setVolume( Volume::LEFT , left );
+ volume.setVolume( Volume::RIGHT, right );
+ }
+ }
+
+ //kdDebug() << "snd_mixer_selem_has_playback_volume " << mixerIdx << " " << snd_mixer_selem_has_playback_switch( elem ) << endl;
+ if ( snd_mixer_selem_has_playback_switch( elem ) )
+ {
+ snd_mixer_selem_get_playback_switch( elem, SND_MIXER_SCHN_FRONT_LEFT, &elem_sw );
+ volume.setMuted( elem_sw == 0 );
+ }
+
+ return 0;
+}
+
+int
+Mixer_ALSA::writeVolumeToHW( int devnum, Volume& volume )
+{
+ int left, right;
+
+ snd_mixer_elem_t *elem = getMixerElem( devnum );
+ if ( !elem )
+ {
+ return 0;
+ }
+
+ // --- VOLUME - WE HAVE JUST ONE TYPE OF VOLUME A TIME,
+ // CAPTURE OR PLAYBACK, SO IT"S JUST USE VOLUME ------------
+ left = volume[ Volume::LEFT ];
+ right = volume[ Volume::RIGHT ];
+
+ if (snd_mixer_selem_has_playback_volume( elem ) && !volume.isCapture() ) {
+ snd_mixer_selem_set_playback_volume ( elem, SND_MIXER_SCHN_FRONT_LEFT, left );
+ if ( ! snd_mixer_selem_is_playback_mono ( elem ) )
+ snd_mixer_selem_set_playback_volume ( elem, SND_MIXER_SCHN_FRONT_RIGHT, right );
+ }
+ else if ( snd_mixer_selem_has_capture_volume( elem ) && volume.isCapture() ) {
+ snd_mixer_selem_set_capture_volume ( elem, SND_MIXER_SCHN_FRONT_LEFT, left );
+ if ( ! snd_mixer_selem_is_playback_mono ( elem ) )
+ snd_mixer_selem_set_capture_volume ( elem, SND_MIXER_SCHN_FRONT_RIGHT, right );
+ }
+
+ if ( snd_mixer_selem_has_playback_switch( elem ) )
+ {
+ int sw = 0;
+ if (! volume.isMuted())
+ sw = !sw;
+ snd_mixer_selem_set_playback_switch_all(elem, sw);
+ }
+
+ return 0;
+}
+
+QString
+Mixer_ALSA::errorText( int mixer_error )
+{
+ QString l_s_errmsg;
+ switch ( mixer_error )
+ {
+ case Mixer::ERR_PERM:
+ l_s_errmsg = i18n("You do not have permission to access the alsa mixer device.\n" \
+ "Please verify if all alsa devices are properly created.");
+ break;
+ case Mixer::ERR_OPEN:
+ l_s_errmsg = i18n("Alsa mixer cannot be found.\n" \
+ "Please check that the soundcard is installed and the\n" \
+ "soundcard driver is loaded.\n" );
+ break;
+ default:
+ l_s_errmsg = Mixer_Backend::errorText( mixer_error );
+ }
+ return l_s_errmsg;
+}
+
+
+QString
+ALSA_getDriverName()
+{
+ return "ALSA";
+}
+
+
diff --git a/kmix/mixer_backend.cpp b/kmix/mixer_backend.cpp
new file mode 100644
index 00000000..2eb61089
--- /dev/null
+++ b/kmix/mixer_backend.cpp
@@ -0,0 +1,147 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <klocale.h>
+
+#include "mixer_backend.h"
+// for the "ERR_" declartions, #include mixer.h
+#include "mixer.h"
+
+Mixer_Backend::Mixer_Backend(int device) :
+ m_devnum (device) , m_isOpen(false), m_recommendedMaster(0)
+{
+ m_mixDevices.setAutoDelete( true );
+}
+
+Mixer_Backend::~Mixer_Backend()
+{
+}
+
+
+bool Mixer_Backend::isValid() {
+ bool valid = false;
+ int ret = open();
+ if ( ret == 0 && m_mixDevices.count() > 0) {
+ valid = true;
+ }
+ close();
+ return valid;
+}
+
+bool Mixer_Backend::isOpen() {
+ return m_isOpen;
+}
+
+/**
+ * Queries the backend driver whether there are new changes in any of the controls.
+ * If you cannot find out for a backend, return "true" - this is also the default implementation.
+ * @return true, if there are changes. Otherwise false is returned.
+ */
+bool Mixer_Backend::prepareUpdateFromHW() {
+ return true;
+}
+
+/**
+ * Return the MixDevice, that would qualify best as MasterDevice. The default is to return the
+ * first device in the device list. Backends can override this (i.e. the ALSA Backend does so).
+ * The users preference is NOT returned by this method - see the Mixer class for that.
+ */
+MixDevice* Mixer_Backend::recommendedMaster() {
+ MixDevice* recommendedMixDevice = 0;
+ if ( m_recommendedMaster != 0 ) {
+ recommendedMixDevice = m_recommendedMaster;
+ } // recommendation from Backend
+ else {
+ if ( m_mixDevices.count() > 0 ) {
+ recommendedMixDevice = m_mixDevices.at(0);
+ } //first device (if exists)
+ }
+ return recommendedMixDevice;
+}
+
+/**
+ * Sets the ID of the currently selected Enum entry.
+ * This is a dummy implementation - if the Mixer backend
+ * wants to support it, it must implement the driver specific
+ * code in its subclass (see Mixer_ALSA.cpp for an example).
+ */
+void Mixer_Backend::setEnumIdHW(int, unsigned int) {
+ return;
+}
+
+/**
+ * Return the ID of the currently selected Enum entry.
+ * This is a dummy implementation - if the Mixer backend
+ * wants to support it, it must implement the driver specific
+ * code in its subclass (see Mixer_ALSA.cpp for an example).
+ */
+unsigned int Mixer_Backend::enumIdHW(int) {
+ return 0;
+}
+
+void Mixer_Backend::errormsg(int mixer_error)
+{
+ QString l_s_errText;
+ l_s_errText = errorText(mixer_error);
+ kdError() << l_s_errText << "\n";
+}
+
+QString Mixer_Backend::errorText(int mixer_error)
+{
+ QString l_s_errmsg;
+ switch (mixer_error)
+ {
+ case Mixer::ERR_PERM:
+ l_s_errmsg = i18n("kmix:You do not have permission to access the mixer device.\n" \
+ "Please check your operating systems manual to allow the access.");
+ break;
+ case Mixer::ERR_WRITE:
+ l_s_errmsg = i18n("kmix: Could not write to mixer.");
+ break;
+ case Mixer::ERR_READ:
+ l_s_errmsg = i18n("kmix: Could not read from mixer.");
+ break;
+ case Mixer::ERR_NODEV:
+ l_s_errmsg = i18n("kmix: Your mixer does not control any devices.");
+ break;
+ case Mixer::ERR_NOTSUPP:
+ l_s_errmsg = i18n("kmix: Mixer does not support your platform. See mixer.cpp for porting hints (PORTING).");
+ break;
+ case Mixer::ERR_NOMEM:
+ l_s_errmsg = i18n("kmix: Not enough memory.");
+ break;
+ case Mixer::ERR_OPEN:
+ case Mixer::ERR_MIXEROPEN:
+ // ERR_MIXEROPEN means: Soundcard could be opened, but has no mixer. ERR_MIXEROPEN is normally never
+ // passed to the errorText() method, because KMix handles that case explicitely
+ l_s_errmsg = i18n("kmix: Mixer cannot be found.\n" \
+ "Please check that the soundcard is installed and that\n" \
+ "the soundcard driver is loaded.\n");
+ break;
+ case Mixer::ERR_INCOMPATIBLESET:
+ l_s_errmsg = i18n("kmix: Initial set is incompatible.\n" \
+ "Using a default set.\n");
+ break;
+ default:
+ l_s_errmsg = i18n("kmix: Unknown error. Please report how you produced this error.");
+ break;
+ }
+ return l_s_errmsg;
+}
+
diff --git a/kmix/mixer_backend.h b/kmix/mixer_backend.h
new file mode 100644
index 00000000..8132ea04
--- /dev/null
+++ b/kmix/mixer_backend.h
@@ -0,0 +1,104 @@
+//-*-C++-*-
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef MIXER_BACKEND_H
+#define MIXER_BACKEND_H
+
+#include "mixer.h"
+
+class Mixer_Backend
+{
+// The Mixer Backend's may only be accessed from the Mixer class.
+friend class Mixer;
+
+protected:
+ Mixer_Backend(int devnum);
+ virtual ~Mixer_Backend();
+
+ /// Derived classes MUST implement this to open the mixer. Returns a KMix error code (O=OK).
+ virtual int open() = 0;
+ virtual int close() = 0;
+
+ /** Returns, whether this Mixer object contains a valid Mixer. You should return "false", when
+ * the Mixer with the devnum given in the constructor is not supported by the Backend. The two
+ * typical cases are:
+ * (1) No such hardware installed
+ * (2) The hardware exists, but has no mixer support (e.g. external soundcard with only mechanical volume knobs)
+ * The default implementation calls open(), checks the return code and whether the number of
+ * supported channels is > 0. Then it calls close().
+ * You should reimplement this method in your backend, when there is a less time-consuming method than
+ * calling open() and close() for checking the existance of a Mixer.
+ */
+ virtual bool isValid();
+
+ /** @return true, if the Mixer is open (and thus can be operated) */
+ bool isOpen();
+
+ virtual bool prepareUpdateFromHW();
+
+ /// Volume Read
+ virtual int readVolumeFromHW( int devnum, Volume &vol ) = 0;
+ /// Volume Write
+ virtual int writeVolumeToHW( int devnum, Volume &volume ) = 0;
+
+ /// Enums
+ virtual void setEnumIdHW(int mixerIdx, unsigned int);
+ virtual unsigned int enumIdHW(int mixerIdx);
+
+ /// Recording Switches
+ virtual bool setRecsrcHW( int devnum, bool on) = 0;
+ virtual bool isRecsrcHW( int devnum ) = 0;
+
+ /// Overwrite in the backend if the backend can see changes without polling
+ virtual bool needsPolling() { return true; }
+
+ /** overwrite this if you need to connect to slots in the mixer (e.g. readSetFromHW)
+ this called in the very beginning and only if !needsPolling
+ */
+ virtual void prepareSignalling( Mixer * ) {}
+
+ MixDevice* recommendedMaster();
+
+ /** Return a translated error text for the given error number.
+ * Subclasses can override this method to produce platform
+ * specific error descriptions.
+ */
+ virtual QString errorText(int mixer_error);
+ /// Prints out a translated error text for the given error number on stderr
+ void errormsg(int mixer_error);
+
+
+ int m_devnum;
+ /// User friendly name of the Mixer (e.g. "IRIX Audio Mixer"). If your mixer API
+ /// gives you a usable name, use that name.
+ QString m_mixerName;
+ // All mix devices of this phyisical device.
+ MixSet m_mixDevices;
+
+ /******************************************************************************************
+ * Please don't access the next vars from the Mixer class (even though Mixer is a friend).
+ * There are proper accesor methods for them.
+ ******************************************************************************************/
+ bool m_isOpen;
+ // The MixDevice that would qualify best as MasterDevice (according to the taste of the Backend developer)
+ MixDevice* m_recommendedMaster;
+};
+
+#endif
diff --git a/kmix/mixer_hpux.cpp b/kmix/mixer_hpux.cpp
new file mode 100644
index 00000000..e7ca0a83
--- /dev/null
+++ b/kmix/mixer_hpux.cpp
@@ -0,0 +1,257 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2000 Christian Esken
+ * esken@kde.org
+ *
+ * HP/UX-Port: Copyright (C) 1999 by Helge Deller
+ * deller@gmx.de
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "mixer_hpux.h"
+
+#warning "HP/UX mixer (maybe) doesn't work yet !"
+
+#define HPUX_ERROR_OFFSET 1024
+
+#define myGain AUnityGain /* AUnityGain or AZeroGain */
+
+#define GAIN_OUT_DIFF ((long) ((int)aMaxOutputGain(audio) - (int)aMinOutputGain(audio)))
+#define GAIN_OUT_MIN ((long) aMinOutputGain(audio))
+#define GAIN_IN_DIFF ((long) ((int)aMaxInputGain(audio) - (int)aMinInputGain(audio)))
+#define GAIN_IN_MIN ((long) aMinOutputGain(audio))
+
+/* standard */
+#define ID_PCM 4
+
+/* AInputSrcType: */ /*OSS:*/
+#define ID_IN_MICROPHONE 7 /* AISTMonoMicrophone */
+#define ID_IN_AUX 6 /* AISTLeftAuxiliary, AISTRightAuxiliary */
+
+/* AOutputDstType: */
+#define ID_OUT_INT_SPEAKER 0 /* AODTMonoIntSpeaker */
+
+/* not yet implemented:
+ AODTLeftJack, AODTRightJack,
+ AODTLeftLineOut, AODTRightLineOut,
+ AODTLeftHeadphone, AODTRightHeadphone
+
+const char* MixerDevNames[32]={"Volume" , "Bass" , "Treble" , "Synth" , "Pcm" , \
+ "Speaker" , "Line" , "Microphone", "CD" , "Mix" , \
+ "Pcm2" , "RecMon" , "IGain" , "OGain" , "Line1", \
+ "Line2" , "Line3" , "Digital1" , "Digital2", "Digital3", \
+ "PhoneIn" , "PhoneOut", "Video" , "Radio" , "Monitor", \
+ "3D-depth", "3D-center", "unknown" , "unknown" , "unknown", \
+ "unknown" , "unused" };
+*/
+
+
+
+Mixer_HPUX::Mixer_HPUX(int devnum) : Mixer_Backend(devnum)
+{
+ char ServerName[10];
+ ServerName[0] = 0;
+ audio = AOpenAudio(ServerName,NULL);
+}
+
+Mixer_HPUX::~Mixer_HPUX()
+{
+ if (audio) {
+ ACloseAudio(audio,0);
+ audio = 0;
+ }
+}
+
+
+int Mixer_HPUX::open()
+{
+ if (audio==0) {
+ return Mixer::ERR_OPEN;
+ }
+ else
+ {
+ /* Mixer is open. Now define properties */
+ stereodevs = devmask = (1<<ID_PCM); /* activate pcm */
+ recmask = 0;
+
+ /* check Input devices... */
+ if (AInputSources(audio) & AMonoMicrophoneMask) {
+ devmask |= (1<<ID_IN_MICROPHONE);
+ recmask |= (1<<ID_IN_MICROPHONE);
+ }
+ if (AInputSources(audio) & (ALeftAuxiliaryMask|ARightAuxiliaryMask)) {
+ devmask |= (1<<ID_IN_AUX);
+ recmask |= (1<<ID_IN_AUX);
+ stereodevs |= (1<<ID_IN_AUX);
+ }
+
+ /* check Output devices... */
+ if (AOutputDestinations(audio) & AMonoIntSpeakerMask) {
+ devmask |= (1<<ID_OUT_INT_SPEAKER);
+ stereodevs |= (1<<ID_OUT_INT_SPEAKER);
+ }
+
+/* implement later:
+ ----------------
+ if (AOutputDestinations(audio) & AMonoLineOutMask) devmask |= 64; // Line
+ if (AOutputDestinations(audio) & AMonoJackMask) devmask |= (1<<14); // Line1
+ if (AOutputDestinations(audio) & AMonoHeadphoneMask) devmask |= (1<<15); // Line2
+*/
+
+ MaxVolume = 255;
+
+ long error = 0;
+ ASetSystemPlayGain(audio, myGain, &error);
+ if (error) errorText(error + HPUX_ERROR_OFFSET);
+ ASetSystemRecordGain(audio, myGain, &error);
+ if (error) errorText(error + HPUX_ERROR_OFFSET);
+
+ i_recsrc = 0;
+ m_isOpen = true;
+
+ m_mixerName = "HP Mixer"; /* AAudioString(audio); */
+ return 0;
+ }
+}
+
+int Mixer_HPUX::close()
+{
+ m_isOpen = false;
+ m_mixDevices.clear();
+ return 0;
+}
+
+
+/*
+void Mixer_HPUX::setDevNumName_I(int devnum)
+{
+ devname = "HP Mixer";
+}
+*/
+bool Mixer_HPUX::setRecsrcHW( int devnum, bool on )
+{
+ return FALSE;
+}
+
+bool Mixer_HPUX::isRecsrcHW( int devnum )
+{
+ return FALSE;
+}
+
+int Mixer_HPUX::readVolumeFromHW( int devnum, Volume &vol )
+{
+ long Gain;
+ long error = 0;
+ int vl,vr;
+
+ switch (devnum) {
+ case ID_OUT_INT_SPEAKER: /* AODTMonoIntSpeaker */
+ AGetSystemChannelGain(audio, ASGTPlay, ACTMono, &Gain, &error );
+ vl = vr = (Gain-GAIN_OUT_MIN)*255 / GAIN_OUT_DIFF;
+ vol.setVolume( Volume::LEFT, vl);
+ vol.setVolume( Volume::RIGHT, vr);
+printf("READ - Devnum: %d, Left: %d, Right: %d\n", devnum, vl, vr );
+ break;
+
+ case ID_IN_AUX: /* AISTLeftAuxiliary, AISTRightAuxiliary */
+ case ID_IN_MICROPHONE: /* AISTMonoMicrophone */
+ AGetSystemChannelGain(audio, ASGTRecord, ACTMono, &Gain, &error );
+ vl = vr = (Gain-GAIN_IN_MIN)*255 / GAIN_IN_DIFF;
+ vol.setVolume( Volume::LEFT, vl);
+ vol.setVolume( Volume::RIGHT, vr);
+ break;
+
+ default:
+ error = Mixer::ERR_NODEV - HPUX_ERROR_OFFSET;
+ break;
+ };
+
+ return (error ? (error+HPUX_ERROR_OFFSET) : 0);
+}
+
+/*
+ ASystemGainType = ASGTPlay, ASGTRecord, ASGTMonitor
+ AChType = ACTMono, ACTLeft, ACTRight
+*/
+
+int Mixer_HPUX::writeVolumeToHW( int devnum, Volume& vol )
+{
+ long Gain;
+ long error = 0;
+ int vl = vol.getVolume(Volume::LEFT);
+ int vr = vol.getVolume(Volume::RIGHT);
+
+ switch (devnum) {
+ case ID_OUT_INT_SPEAKER: /* AODTMonoIntSpeaker */
+printf("WRITE - Devnum: %d, Left: %d, Right: %d\n", devnum, vl, vr);
+ Gain = vl; // only left Volume
+ Gain = (Gain*GAIN_OUT_DIFF) / 255 - GAIN_OUT_MIN;
+ ASetSystemChannelGain(audio, ASGTPlay, ACTMono, (AGainDB) Gain, &error );
+ break;
+
+ case ID_IN_MICROPHONE: /* AISTMonoMicrophone */
+ Gain = vl; // only left Volume
+ Gain = (Gain*GAIN_IN_DIFF) / 255 - GAIN_IN_MIN;
+ ASetSystemChannelGain(audio, ASGTRecord, ACTMono, (AGainDB) Gain, &error );
+ break;
+
+ case ID_IN_AUX: /* AISTLeftAuxiliary, AISTRightAuxiliary */
+ Gain = (vl*GAIN_IN_DIFF) / 255 - GAIN_IN_MIN;
+ ASetSystemChannelGain(audio, ASGTRecord, ACTLeft, (AGainDB) Gain, &error );
+ Gain = (vr*GAIN_IN_DIFF) / 255 - GAIN_IN_MIN;
+ ASetSystemChannelGain(audio, ASGTRecord, ACTRight, (AGainDB) Gain, &error );
+ break;
+
+ default:
+ error = Mixer::ERR_NODEV - HPUX_ERROR_OFFSET;
+ break;
+ };
+ return (error ? (error+HPUX_ERROR_OFFSET) : 0);
+}
+
+
+QString Mixer_HPUX::errorText(int mixer_error)
+{
+ QString l_s_errmsg;
+ if (mixer_error >= HPUX_ERROR_OFFSET) {
+ char errorstr[200];
+ AGetErrorText(audio, (AError) (mixer_error-HPUX_ERROR_OFFSET),
+ errorstr, sizeof(errorstr));
+ printf("kmix: %s: %s\n",mixerName().data(), errorstr);
+ l_s_errmsg = errorstr;
+ } else
+ switch (mixer_error)
+ {
+ case Mixer::ERR_OPEN:
+ // should use i18n...
+ l_s_errmsg = "kmix: HP-UX Alib-Mixer cannot be found.\n" \
+ "Please check that you have:\n" \
+ " 1. Installed the libAlib package and\n" \
+ " 2. started the Aserver program from the /opt/audio/bin directory\n";
+ break;
+ default:
+ l_s_errmsg = Mixer_Backend::errorText(mixer_error);
+ break;
+ }
+ return l_s_errmsg;
+}
+
+QString HPUX_getDriverName() {
+ return "HPUX";
+}
+
diff --git a/kmix/mixer_hpux.h b/kmix/mixer_hpux.h
new file mode 100644
index 00000000..bdc0d3d7
--- /dev/null
+++ b/kmix/mixer_hpux.h
@@ -0,0 +1,36 @@
+#ifndef MIXER_HPUX_H
+#define MIXER_HPUX_H
+
+#define DEFAULT_MIXER "HP-UX Mixer"
+#ifdef HAVE_ALIB_H
+#include <Alib.h>
+#define HPUX_MIXER
+#endif
+
+#include "mixer_backend.h"
+
+class Mixer_HPUX : public Mixer_Backend
+{
+public:
+ Mixer_HPUX(int devnum);
+ virtual ~Mixer_HPUX();
+
+ virtual QString errorText(int mixer_error);
+
+ virtual int readVolumeFromHW( int devnum, Volume &vol );
+ virtual int writeVolumeToHW( int devnum, Volume &vol );
+
+protected:
+ virtual bool setRecsrcHW( int devnum, bool on = true );
+ virtual bool isRecsrcHW( int devnum );
+
+ virtual int open();
+ virtual int close();
+
+ Audio *audio;
+ unsigned int stereodevs,devmask, recmask, MaxVolume, i_recsrc;
+
+
+};
+
+#endif
diff --git a/kmix/mixer_irix.cpp b/kmix/mixer_irix.cpp
new file mode 100644
index 00000000..9c88b6f4
--- /dev/null
+++ b/kmix/mixer_irix.cpp
@@ -0,0 +1,133 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2000 Christian Esken
+ * esken@kde.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "mixer_irix.h"
+
+Mixer_Backend* IRIX_getMixer(int devnum)
+{
+ Mixer_Backend *l_mixer;
+ l_mixer = new Mixer_IRIX( devnum);
+ l_mixer->init(devnum);
+ return l_mixer;
+}
+
+
+
+Mixer_IRIX::Mixer_IRIX(int devnum) : Mixer_Backend(devnum)
+{
+ close();
+}
+
+int Mixer_IRIX::open()
+{
+ // Create config
+ m_config = ALnewconfig();
+ if (m_config == (ALconfig)0) {
+ cerr << "OpenAudioDevice(): ALnewconfig() failed\n";
+ return Mixer::ERR_OPEN;
+ }
+ // Open audio device
+ m_port = ALopenport("XVDOPlayer", "w", m_config);
+ if (m_port == (ALport)0) {
+ return Mixer::ERR_OPEN;
+ }
+ else {
+ // Mixer is open. Now define properties
+ devmask = 1+128+2048;
+ recmask = 128;
+ i_recsrc = 128;
+ stereodevs = 1+128+2048;
+ MaxVolume = 255;
+
+ i_s_mixer_name = "HPUX Audio Mixer";
+
+ isOpen = true;
+ return 0;
+ }
+}
+
+int Mixer_IRIX::close()
+{
+ m_isOpen = false;
+ ALfreeconfig(m_config);
+ ALcloseport(m_port);
+ m_mixDevices.clear();
+ return 0;
+}
+
+int Mixer_IRIX::readVolumeFromHW( int devnum, int *VolLeft, int *VolRight )
+{
+ long in_buf[4];
+ switch( devnum() ) {
+ case 0: // Speaker Output
+ in_buf[0] = AL_RIGHT_SPEAKER_GAIN;
+ in_buf[2] = AL_LEFT_SPEAKER_GAIN;
+ break;
+ case 7: // Microphone Input (actually selectable).
+ in_buf[0] = AL_RIGHT_INPUT_ATTEN;
+ in_buf[2] = AL_LEFT_INPUT_ATTEN;
+ break;
+ case 11: // Record monitor
+ in_buf[0] = AL_RIGHT_MONITOR_ATTEN;
+ in_buf[2] = AL_LEFT_MONITOR_ATTEN;
+ break;
+ default:
+ printf("Unknown device %d\n", MixPtr->num() );
+ }
+ ALgetparams(AL_DEFAULT_DEVICE, in_buf, 4);
+ *VolRight = in_buf[1]*100/255;
+ *VolLeft = in_buf[3]*100/255;
+
+ return 0;
+}
+
+int Mixer_IRIX::writeVolumeToHW( int devnum, int volLeft, int volRight )
+{
+ // Set volume (right&left)
+ long out_buf[4] =
+ {
+ 0, volRight,
+ 0, volLeft
+ };
+ switch( mixdevice->num() ) {
+ case 0: // Speaker
+ out_buf[0] = AL_RIGHT_SPEAKER_GAIN;
+ out_buf[2] = AL_LEFT_SPEAKER_GAIN;
+ break;
+ case 7: // Microphone (Input)
+ out_buf[0] = AL_RIGHT_INPUT_ATTEN;
+ out_buf[2] = AL_LEFT_INPUT_ATTEN;
+ break;
+ case 11: // Record monitor
+ out_buf[0] = AL_RIGHT_MONITOR_ATTEN;
+ out_buf[2] = AL_LEFT_MONITOR_ATTEN;
+ break;
+ }
+ ALsetparams(AL_DEFAULT_DEVICE, out_buf, 4);
+
+ return 0;
+}
+
+QString IRIX_getDriverName() {
+ return "IRIX";
+}
+
diff --git a/kmix/mixer_irix.h b/kmix/mixer_irix.h
new file mode 100644
index 00000000..decf143c
--- /dev/null
+++ b/kmix/mixer_irix.h
@@ -0,0 +1,28 @@
+#ifndef MIXER_IRIX_H
+#define MIXER_IRIX_H
+
+#define _LANGUAGE_C_PLUS_PLUS
+#include <dmedia/audio.h>
+
+#include "mixer_backend.h"
+
+class Mixer_IRIX : public Mixer_Backend
+{
+public:
+ Mixer_IRIX(int devnum);
+ virtual ~Mixer_IRIX();
+
+ virtual void setRecsrc(unsigned int newRecsrc);
+ virtual int readVolumeFromHW( int devnum, int *VolLeft, int *VolRight );
+ virtual int writeVolumeToHW( int devnum, int volLeft, int volRight );
+
+protected:
+ virtual void setDevNumName_I(int devnum);
+ virtual int open();
+ virtual int close();
+
+ ALport m_port;
+ ALconfig m_config;
+};
+
+#endif
diff --git a/kmix/mixer_none.cpp b/kmix/mixer_none.cpp
new file mode 100644
index 00000000..41e3d9b3
--- /dev/null
+++ b/kmix/mixer_none.cpp
@@ -0,0 +1,78 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2000 Christian Esken
+ * esken@kde.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "mixer_none.h"
+
+// This static method must be implemented (as fallback)
+Mixer_Backend* Mixer::getMixer(int devnum)
+{
+ Mixer_Backend *l_mixer;
+ l_mixer = new Mixer_None( devnum);
+ return l_mixer;
+}
+
+Mixer_None::Mixer_None(int devnum) : Mixer_Backend( device )
+{
+}
+
+Mixer_None::~Mixer_None()
+{
+ close();
+}
+
+int Mixer_None::open()
+{
+ //i_s_mixer_name = "No mixer";
+ return Mixer::ERR_NOTSUPP;
+}
+
+int Mixer_None::close()
+{
+ m_isOpen = false;
+ m_mixDevices.clear();
+ return Mixer::ERR_NOTSUPP;
+}
+
+int Mixer_None::readVolumeFromHW( int , Volume &vol )
+{
+ return Mixer::ERR_NOTSUPP;
+}
+
+int Mixer_None::writeVolumeToHW( int , Volume &vol )
+{
+ return Mixer::ERR_NOTSUPP;
+}
+
+bool Mixer_None::setRecsrcHW( int devnum, bool on)
+{
+ return false;
+}
+
+bool Mixer_None::isRecsrcHW( int devnum )
+{
+ return false;
+}
+
+QString NONE_getDriverName() {
+ return "None";
+}
+
diff --git a/kmix/mixer_none.h b/kmix/mixer_none.h
new file mode 100644
index 00000000..35d5dc93
--- /dev/null
+++ b/kmix/mixer_none.h
@@ -0,0 +1,22 @@
+#ifndef MIXER_NONE_H
+#define MIXER_NONE_H
+
+#include "mixer_backend.h"
+
+class Mixer_None : public Mixer_Backend
+{
+public:
+ Mixer_None(int devnum);
+ virtual ~Mixer_None();
+
+ virtual int readVolumeFromHW( int devnum, Volume& vol );
+ virtual int writeVolumeToHW( int devnum, Volume& vol );
+ virtual bool setRecsrcHW( int devnum, bool on);
+ virtual bool isRecsrcHW( int devnum );
+
+protected:
+ virtual int open();
+ virtual int close();
+};
+
+#endif
diff --git a/kmix/mixer_oss.cpp b/kmix/mixer_oss.cpp
new file mode 100644
index 00000000..6991c1aa
--- /dev/null
+++ b/kmix/mixer_oss.cpp
@@ -0,0 +1,330 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ * Copyright (C) 1996-2000 Christian Esken
+ * esken@kde.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+// Since we're guaranteed an OSS setup here, let's make life easier
+#if !defined(__NetBSD__) && !defined(__OpenBSD__)
+ #include <sys/soundcard.h>
+#else
+ #include <soundcard.h>
+#endif
+
+#include "mixer_oss.h"
+#include <klocale.h>
+
+/*
+ I am using a fixed MAX_MIXDEVS #define here.
+ People might argue, that I should rather use the SOUND_MIXER_NRDEVICES
+ #define used by OSS. But using this #define is not good, because it is
+ evaluated during compile time. Compiling on one platform and running
+ on another with another version of OSS with a different value of
+ SOUND_MIXER_NRDEVICES is very bad. Because of this, usage of
+ SOUND_MIXER_NRDEVICES should be discouraged.
+
+ The #define below is only there for internal reasons.
+ In other words: Don't play around with this value
+ */
+#define MAX_MIXDEVS 32
+
+const char* MixerDevNames[32]={
+ I18N_NOOP("Volume"), I18N_NOOP("Bass"), I18N_NOOP("Treble"),
+ I18N_NOOP("Synth"), I18N_NOOP("Pcm"), I18N_NOOP("Speaker"),
+ I18N_NOOP("Line"), I18N_NOOP("Microphone"), I18N_NOOP("CD"),
+ I18N_NOOP("Mix"), I18N_NOOP("Pcm2"), I18N_NOOP("RecMon"),
+ I18N_NOOP("IGain"), I18N_NOOP("OGain"), I18N_NOOP("Line1"),
+ I18N_NOOP("Line2"), I18N_NOOP("Line3"), I18N_NOOP("Digital1"),
+ I18N_NOOP("Digital2"), I18N_NOOP("Digital3"), I18N_NOOP("PhoneIn"),
+ I18N_NOOP("PhoneOut"), I18N_NOOP("Video"), I18N_NOOP("Radio"),
+ I18N_NOOP("Monitor"), I18N_NOOP("3D-depth"), I18N_NOOP("3D-center"),
+ I18N_NOOP("unknown"), I18N_NOOP("unknown"), I18N_NOOP("unknown"),
+ I18N_NOOP("unknown") , I18N_NOOP("unused") };
+
+const MixDevice::ChannelType MixerChannelTypes[32] = {
+ MixDevice::VOLUME, MixDevice::BASS, MixDevice::TREBLE, MixDevice::MIDI,
+ MixDevice::AUDIO, MixDevice::EXTERNAL, MixDevice::EXTERNAL, MixDevice::MICROPHONE,
+ MixDevice::CD, MixDevice::VOLUME, MixDevice::AUDIO, MixDevice::RECMONITOR,
+ MixDevice::VOLUME, MixDevice::RECMONITOR, MixDevice::EXTERNAL, MixDevice::EXTERNAL,
+ MixDevice::EXTERNAL, MixDevice::AUDIO, MixDevice::AUDIO, MixDevice::AUDIO,
+ MixDevice::EXTERNAL, MixDevice::EXTERNAL, MixDevice::EXTERNAL, MixDevice::EXTERNAL,
+ MixDevice::EXTERNAL, MixDevice::VOLUME, MixDevice::VOLUME, MixDevice::UNKNOWN,
+ MixDevice::UNKNOWN, MixDevice::UNKNOWN, MixDevice::UNKNOWN, MixDevice::UNKNOWN };
+
+Mixer_Backend* OSS_getMixer( int device )
+{
+ Mixer_Backend *l_mixer;
+ l_mixer = new Mixer_OSS( device );
+ return l_mixer;
+}
+
+Mixer_OSS::Mixer_OSS(int device) : Mixer_Backend(device)
+{
+ if( device == -1 ) m_devnum = 0;
+}
+
+Mixer_OSS::~Mixer_OSS()
+{
+ close();
+}
+
+int Mixer_OSS::open()
+{
+ if ((m_fd= ::open( deviceName( m_devnum ).latin1(), O_RDWR)) < 0)
+ {
+ if ( errno == EACCES )
+ return Mixer::ERR_PERM;
+ else {
+ if ((m_fd= ::open( deviceNameDevfs( m_devnum ).latin1(),
+ O_RDWR)) < 0)
+ {
+ if ( errno == EACCES )
+ return Mixer::ERR_PERM;
+ else
+ return Mixer::ERR_OPEN;
+ }
+ }
+ }
+
+ int devmask, recmask, i_recsrc, stereodevs;
+ // Mixer is open. Now define properties
+ if (ioctl(m_fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1)
+ return Mixer::ERR_READ;
+ if (ioctl(m_fd, SOUND_MIXER_READ_RECMASK, &recmask) == -1)
+ return Mixer::ERR_READ;
+ if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &i_recsrc) == -1)
+ return Mixer::ERR_READ;
+ if (ioctl(m_fd, SOUND_MIXER_READ_STEREODEVS, &stereodevs) == -1)
+ return Mixer::ERR_READ;
+ if (!devmask)
+ return Mixer::ERR_NODEV;
+ int maxVolume =100;
+
+ if( m_mixDevices.isEmpty() )
+ {
+ int idx = 0;
+ while( devmask && idx < MAX_MIXDEVS )
+ {
+ if( devmask & ( 1 << idx ) ) // device active?
+ {
+ Volume vol( stereodevs & ( 1 << idx ) ? 2 : 1, maxVolume);
+ readVolumeFromHW( idx, vol );
+ MixDevice* md =
+ new MixDevice( idx, vol, recmask & ( 1 << idx ), true,
+ i18n(MixerDevNames[idx]),
+ MixerChannelTypes[idx]);
+ md->setRecSource( isRecsrcHW( idx ) );
+ m_mixDevices.append( md );
+ }
+ idx++;
+ }
+ }
+ else
+ for( unsigned int idx = 0; idx < m_mixDevices.count(); idx++ )
+ {
+ MixDevice* md = m_mixDevices.at( idx );
+ if( !md )
+ return Mixer::ERR_INCOMPATIBLESET;
+ writeVolumeToHW( idx, md->getVolume() );
+ }
+
+#if !defined(__FreeBSD__)
+ struct mixer_info l_mix_info;
+ if (ioctl(m_fd, SOUND_MIXER_INFO, &l_mix_info) != -1)
+ {
+ m_mixerName = l_mix_info.name;
+ }
+ else
+#endif
+
+ m_mixerName = "OSS Audio Mixer";
+
+ m_isOpen = true;
+ return 0;
+}
+
+int Mixer_OSS::close()
+{
+ m_isOpen = false;
+ int l_i_ret = ::close(m_fd);
+ m_mixDevices.clear();
+ return l_i_ret;
+}
+
+
+QString Mixer_OSS::deviceName(int devnum)
+{
+ switch (devnum) {
+ case 0:
+ return QString("/dev/mixer");
+ break;
+
+ default:
+ QString devname("/dev/mixer");
+ devname += ('0'+devnum);
+ return devname;
+ }
+}
+
+QString Mixer_OSS::deviceNameDevfs(int devnum)
+{
+ switch (devnum) {
+ case 0:
+ return QString("/dev/sound/mixer");
+ break;
+
+ default:
+ QString devname("/dev/sound/mixer");
+ devname += ('0'+devnum);
+ return devname;
+ }
+}
+
+QString Mixer_OSS::errorText(int mixer_error)
+{
+ QString l_s_errmsg;
+ switch (mixer_error)
+ {
+ case Mixer::ERR_PERM:
+ l_s_errmsg = i18n("kmix: You do not have permission to access the mixer device.\n" \
+ "Login as root and do a 'chmod a+rw /dev/mixer*' to allow the access.");
+ break;
+ case Mixer::ERR_OPEN:
+ l_s_errmsg = i18n("kmix: Mixer cannot be found.\n" \
+ "Please check that the soundcard is installed and the\n" \
+ "soundcard driver is loaded.\n" \
+ "On Linux you might need to use 'insmod' to load the driver.\n" \
+ "Use 'soundon' when using commercial OSS.");
+ break;
+ default:
+ l_s_errmsg = Mixer_Backend::errorText(mixer_error);
+ }
+ return l_s_errmsg;
+}
+
+
+bool Mixer_OSS::setRecsrcHW( int devnum, bool on )
+{
+ int i_recsrc, oldrecsrc;
+ if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &i_recsrc) == -1)
+ errormsg(Mixer::ERR_READ);
+
+ oldrecsrc = i_recsrc = on ?
+ (i_recsrc | (1 << devnum )) :
+ (i_recsrc & ~(1 << devnum ));
+
+ // Change status of record source(s)
+ if (ioctl(m_fd, SOUND_MIXER_WRITE_RECSRC, &i_recsrc) == -1)
+ errormsg (Mixer::ERR_WRITE);
+ // Re-read status of record source(s). Just in case, OSS does not like
+ // my settings. And with this line mix->recsrc gets its new value. :-)
+ if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &i_recsrc) == -1)
+ errormsg(Mixer::ERR_READ);
+
+ /* The following if {} patch was submitted by Tim McCormick <tim@pcbsd.org>. */
+ /* Comment (cesken): This patch fixes an issue with mutual exclusive recording sources.
+ Actually the kernel soundcard driver *could* "do the right thing" by examining the change
+ (old-recsrc XOR new-recsrc), and knowing which sources are mutual exclusive.
+ The OSS v3 API docs indicate that the behaviour is undefined for this case, and it is not
+ clearly documented how and whether SOUND_MIXER_CAP_EXCL_INPUT is evaluated in the OSS driver.
+ Evaluating that in the application (KMix) could help, but the patch will work independent
+ on whether SOUND_MIXER_CAP_EXCL_INPUT ist set or not.
+
+ In any case this patch is a superb workaround for a shortcoming of the OSS v3 API.
+ */
+ // If the record source is supposed to be on, but wasn't set, explicitly
+ // set the record source. Not all cards support multiple record sources.
+ // As a result, we also need to do the read & write again.
+ if (((i_recsrc & ( 1<<devnum)) == 0) && on)
+ {
+ // Setting the new device failed => Try to enable it *exclusively*
+ oldrecsrc = i_recsrc = 1 << devnum;
+ if (ioctl(m_fd, SOUND_MIXER_WRITE_RECSRC, &i_recsrc) == -1)
+ errormsg (Mixer::ERR_WRITE);
+ if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &i_recsrc) == -1)
+ errormsg(Mixer::ERR_READ);
+ }
+
+ // PORTING: Hint: Do not forget to set i_recsrc to the new valid
+ // record source mask.
+
+ return i_recsrc == oldrecsrc;
+}
+
+bool Mixer_OSS::isRecsrcHW( int devnum )
+{
+ bool isRecsrc = false;
+ int recsrcMask;
+ if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &recsrcMask) == -1)
+ errormsg(Mixer::ERR_READ);
+ else {
+ // test if device bit is set in record bit mask
+ isRecsrc = ( (recsrcMask & ( 1<<devnum)) != 0 );
+ }
+ return isRecsrc;
+}
+
+int Mixer_OSS::readVolumeFromHW( int devnum, Volume &vol )
+{
+ if( vol.isMuted() ) return 0; // Don't alter volume when muted
+
+ int volume;
+ if (ioctl(m_fd, MIXER_READ( devnum ), &volume) == -1)
+ {
+ /* Oops, can't read mixer */
+ return(Mixer::ERR_READ);
+ }
+ else
+ {
+ vol.setVolume( Volume::LEFT, (volume & 0x7f));
+ if( vol.count() > 1 )
+ vol.setVolume( Volume::RIGHT, ((volume>>8) & 0x7f));
+ //fprintf(stderr, "Mixer_OSS::readVolumeFromHW(%i,vol) set vol %i %i\n", devnum, vol.getVolume(Volume::LEFT), vol.getVolume(Volume::RIGHT));
+ return 0;
+ }
+}
+
+
+
+int Mixer_OSS::writeVolumeToHW( int devnum, Volume &vol )
+{
+ int volume;
+ if( vol.isMuted() ) volume = 0;
+ else
+ if ( vol.count() > 1 )
+ volume = (vol[ Volume::LEFT ]) + ((vol[ Volume::RIGHT ])<<8);
+ else
+ volume = vol[ Volume::LEFT ];
+
+ if (ioctl(m_fd, MIXER_WRITE( devnum ), &volume) == -1)
+ return Mixer::ERR_WRITE;
+
+ return 0;
+}
+
+QString OSS_getDriverName() {
+ return "OSS";
+}
+
diff --git a/kmix/mixer_oss.h b/kmix/mixer_oss.h
new file mode 100644
index 00000000..592802ea
--- /dev/null
+++ b/kmix/mixer_oss.h
@@ -0,0 +1,33 @@
+//-*-C++-*-
+
+#ifndef MIXER_OSS_H
+#define MIXER_OSS_H
+
+#include <qstring.h>
+
+#include "mixer_backend.h"
+
+class Mixer_OSS : public Mixer_Backend
+{
+public:
+ Mixer_OSS(int device = -1);
+ virtual ~Mixer_OSS();
+
+ virtual QString errorText(int mixer_error);
+ virtual int readVolumeFromHW( int devnum, Volume &vol );
+ virtual int writeVolumeToHW( int devnum, Volume &vol );
+
+protected:
+ virtual bool setRecsrcHW( int devnum, bool on = true );
+ virtual bool isRecsrcHW( int devnum );
+
+ virtual int open();
+ virtual int close();
+
+ virtual QString deviceName( int );
+ virtual QString deviceNameDevfs( int );
+ int m_fd;
+ QString m_deviceName;
+};
+
+#endif
diff --git a/kmix/mixer_oss4.cpp b/kmix/mixer_oss4.cpp
new file mode 100644
index 00000000..dccea0be
--- /dev/null
+++ b/kmix/mixer_oss4.cpp
@@ -0,0 +1,670 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ * Copyright (C) 1996-2000 Christian Esken
+ * esken@kde.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+//OSS4 mixer backend for KMix by Yoper Team released under GPL v2 or later
+
+
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <qregexp.h>
+#include <kdebug.h>
+
+// Since we're guaranteed an OSS setup here, let's make life easier
+#if !defined(__NetBSD__) && !defined(__OpenBSD__)
+#include <sys/soundcard.h>
+#else
+#include <soundcard.h>
+#endif
+
+#include "mixer_oss4.h"
+#include <klocale.h>
+
+Mixer_Backend* OSS4_getMixer(int device)
+{
+ Mixer_Backend *l_mixer;
+ l_mixer = new Mixer_OSS4(device);
+ return l_mixer;
+}
+
+Mixer_OSS4::Mixer_OSS4(int device) : Mixer_Backend(device)
+{
+ if ( device == -1 ) m_devnum = 0;
+ m_numExtensions = 0;
+}
+
+Mixer_OSS4::~Mixer_OSS4()
+{
+ close();
+}
+
+bool Mixer_OSS4::setRecsrcHW(int ctrlnum, bool on)
+{
+ return true;
+}
+
+//dummy implementation only
+bool Mixer_OSS4::isRecsrcHW(int ctrlnum)
+{
+ return false;
+}
+
+//classifies mixexts according to their name, last classification wins
+MixDevice::ChannelType Mixer_OSS4::classifyAndRename(QString &name, int flags)
+{
+ MixDevice::ChannelType cType = MixDevice::UNKNOWN;
+ QStringList classes = QStringList::split ( QRegExp ( "[-,.]" ), name );
+
+
+ if ( flags & MIXF_PCMVOL ||
+ flags & MIXF_MONVOL ||
+ flags & MIXF_MAINVOL )
+ {
+ cType = MixDevice::VOLUME;
+ }
+
+ for ( QStringList::Iterator it = classes.begin(); it != classes.end(); ++it )
+ {
+ if ( *it == "line" )
+ {
+ *it = "Line";
+ cType = MixDevice::EXTERNAL;
+
+ } else
+ if ( *it == "mic" )
+ {
+ *it = "Microphone";
+ cType = MixDevice::MICROPHONE;
+ } else
+ if ( *it == "vol" )
+ {
+ *it = "Volume";
+ cType = MixDevice::VOLUME;
+ } else
+ if ( *it == "surr" )
+ {
+ *it = "Surround";
+ cType = MixDevice::SURROUND;
+ } else
+ if ( *it == "bass" )
+ {
+ *it = "Bass";
+ cType = MixDevice::BASS;
+ } else
+ if ( *it == "treble" )
+ {
+ *it = "Treble";
+ cType = MixDevice::TREBLE;
+ } else
+ if ( (*it).startsWith ( "pcm" ) )
+ {
+ (*it).replace ( "pcm","PCM" );
+ cType = MixDevice::AUDIO;
+ } else
+ if ( *it == "src" )
+ {
+ *it = "Source";
+ } else
+ if ( *it == "rec" )
+ {
+ *it = "Recording";
+ } else
+ if ( *it == "cd" )
+ {
+ *it = (*it).upper();
+ cType = MixDevice::CD;
+ }
+ if ( (*it).startsWith("vmix") )
+ {
+ (*it).replace("vmix","Virtual Mixer");
+ cType = MixDevice::VOLUME;
+ } else
+ if ( (*it).endsWith("vol") )
+ {
+ QChar &ref = (*it).ref(0);
+ ref = ref.upper();
+ cType = MixDevice::VOLUME;
+ }
+ else
+ {
+ QChar &ref = (*it).ref(0);
+ ref = ref.upper();
+ }
+ }
+ name = classes.join( " ");
+ return cType;
+}
+
+int Mixer_OSS4::open()
+{
+ if ( (m_fd= ::open("/dev/mixer", O_RDWR)) < 0 )
+ {
+ if ( errno == EACCES )
+ return Mixer::ERR_PERM;
+ else
+ return Mixer::ERR_OPEN;
+ }
+
+ if (wrapIoctl( ioctl (m_fd, OSS_GETVERSION, &m_ossVersion) ) < 0)
+ {
+ return Mixer::ERR_READ;
+ }
+ if (m_ossVersion < 0x040000)
+ {
+ return Mixer::ERR_NOTSUPP;
+ }
+
+
+ wrapIoctl( ioctl (m_fd, SNDCTL_MIX_NRMIX, &m_numMixers) );
+
+ if ( m_mixDevices.isEmpty() )
+ {
+ if ( m_devnum >= 0 && m_devnum < m_numMixers )
+ {
+ m_numExtensions = m_devnum;
+ bool masterChosen = false;
+ oss_mixext ext;
+ ext.dev = m_devnum;
+
+ if ( wrapIoctl( ioctl (m_fd, SNDCTL_MIX_NREXT, &m_numExtensions) ) < 0 )
+ {
+ //TODO: more specific error handling here
+ if ( errno == ENODEV ) return Mixer::ERR_NODEV;
+ return Mixer::ERR_READ;
+ }
+
+ if( m_numExtensions == 0 )
+ {
+ return Mixer::ERR_NODEV;
+ }
+
+ ext.ctrl = 0;
+
+ //read MIXT_DEVROOT, return Mixer::NODEV on error
+ if ( wrapIoctl ( ioctl( m_fd, SNDCTL_MIX_EXTINFO, &ext) ) < 0 )
+ {
+ return Mixer::ERR_NODEV;
+ }
+
+ oss_mixext_root *root = (oss_mixext_root *) ext.data;
+ m_mixerName = root->name;
+
+ for ( int i = 1; i < m_numExtensions; i++ )
+ {
+ bool isCapture = false;
+
+ ext.dev = m_devnum;
+ ext.ctrl = i;
+
+ //wrapIoctl handles reinitialization, cancel loading on EIDRM
+ if ( wrapIoctl( ioctl( m_fd, SNDCTL_MIX_EXTINFO, &ext) ) == EIDRM )
+ {
+ return 0;
+ }
+
+ QString name = ext.extname;
+
+ //skip vmix volume controls and mute controls
+ if ( (name.find("vmix") > -1 && name.find( "pcm") > -1) ||
+ name.find("mute") > -1
+#ifdef MIXT_MUTE
+ || (ext.type == MIXT_MUTE)
+#endif
+ )
+ {
+ continue;
+ }
+
+ //fix for old legacy names, according to Hannu's suggestions
+ if ( name.contains('_') )
+ {
+ name = name.section('_',1,1).lower();
+ }
+
+ if ( ext.flags & MIXF_RECVOL || ext.flags & MIXF_MONVOL || name.find(".in") > -1 )
+ {
+ isCapture = true;
+ }
+
+ Volume::ChannelMask chMask = Volume::MNONE;
+
+ MixDevice::ChannelType cType = classifyAndRename(name, ext.flags);
+
+ if ( ext.type == MIXT_STEREOSLIDER16 ||
+ ext.type == MIXT_STEREOSLIDER ||
+ ext.type == MIXT_MONOSLIDER16 ||
+ ext.type == MIXT_MONOSLIDER ||
+ ext.type == MIXT_SLIDER
+ )
+ {
+ if ( ext.type == MIXT_STEREOSLIDER16 ||
+ ext.type == MIXT_STEREOSLIDER
+ )
+ {
+ if ( isCapture )
+ {
+ chMask = Volume::ChannelMask(Volume::MLEFT|Volume::MRIGHT);
+ }
+ else
+ {
+ chMask = Volume::ChannelMask(Volume::MLEFT|Volume::MRIGHT );
+ }
+ }
+ else
+ {
+ if ( isCapture )
+ {
+ chMask = Volume::MLEFT;
+ }
+ else
+ {
+ chMask = Volume::MLEFT;
+ }
+ }
+
+ Volume vol (chMask, ext.maxvalue, ext.minvalue, isCapture);
+
+ MixDevice* md = new MixDevice(i, vol, isCapture, true,
+ name, cType, MixDevice::SLIDER);
+
+ m_mixDevices.append(md);
+
+ if ( !masterChosen && ext.flags & MIXF_MAINVOL )
+ {
+ m_recommendedMaster = md;
+ masterChosen = true;
+ }
+ }
+ else if ( ext.type == MIXT_ONOFF )
+ {
+ Volume vol;
+ vol.setMuted(true);
+ MixDevice* md = new MixDevice(i, vol, false, true, name, MixDevice::VOLUME, MixDevice::SWITCH);
+ m_mixDevices.append(md);
+ }
+ else if ( ext.type == MIXT_ENUM )
+ {
+ oss_mixer_enuminfo ei;
+ ei.dev = m_devnum;
+ ei.ctrl = i;
+
+ if ( wrapIoctl( ioctl (m_fd, SNDCTL_MIX_ENUMINFO, &ei) ) != -1 )
+ {
+ Volume vol(Volume::MLEFT, ext.maxvalue, ext.minvalue, false);
+
+ MixDevice* md = new MixDevice (i, vol, false, false,
+ name, MixDevice::UNKNOWN,
+ MixDevice::ENUM);
+
+ QPtrList<QString> &enumValuesRef = md->enumValues();
+ QString thisElement;
+
+ for ( int i = 0; i < ei.nvalues; i++ )
+ {
+ thisElement = &ei.strings[ ei.strindex[i] ];
+
+ if ( thisElement.isEmpty() )
+ {
+ thisElement = QString::number(i);
+ }
+ enumValuesRef.append( new QString(thisElement) );
+ }
+ m_mixDevices.append(md);
+ }
+ }
+
+ }
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ m_isOpen = true;
+ return 0;
+}
+
+int Mixer_OSS4::close()
+{
+ m_isOpen = false;
+ int l_i_ret = ::close(m_fd);
+ m_mixDevices.clear();
+ return l_i_ret;
+}
+
+QString Mixer_OSS4::errorText(int mixer_error)
+{
+ QString l_s_errmsg;
+
+ switch( mixer_error )
+ {
+ case Mixer::ERR_PERM:
+ l_s_errmsg = i18n("kmix: You do not have permission to access the mixer device.\n" \
+ "Login as root and do a 'chmod a+rw /dev/mixer*' to allow the access.");
+ break;
+ case Mixer::ERR_OPEN:
+ l_s_errmsg = i18n("kmix: Mixer cannot be found.\n" \
+ "Please check that the soundcard is installed and the\n" \
+ "soundcard driver is loaded.\n" \
+ "On Linux you might need to use 'insmod' to load the driver.\n" \
+ "Use 'soundon' when using OSS4 from 4front.");
+ break;
+ case Mixer::ERR_NOTSUPP:
+ l_s_errmsg = i18n("kmix expected an OSSv4 mixer module,\n" \
+ "but instead found an older version.");
+ break;
+ default:
+ l_s_errmsg = Mixer_Backend::errorText(mixer_error);
+ }
+ return l_s_errmsg;
+}
+
+int Mixer_OSS4::readVolumeFromHW(int ctrlnum, Volume &vol)
+{
+
+ oss_mixext extinfo;
+ oss_mixer_value mv;
+
+ extinfo.dev = m_devnum;
+ extinfo.ctrl = ctrlnum;
+
+ if ( wrapIoctl( ioctl(m_fd, SNDCTL_MIX_EXTINFO, &extinfo) ) < 0 )
+ {
+ //TODO: more specific error handling
+ return Mixer::ERR_READ;
+ }
+
+ mv.dev = extinfo.dev;
+ mv.ctrl = extinfo.ctrl;
+ mv.timestamp = extinfo.timestamp;
+
+ if ( wrapIoctl ( ioctl (m_fd, SNDCTL_MIX_READ, &mv) ) < 0 )
+ {
+ /* Oops, can't read mixer */
+ return Mixer::ERR_READ;
+ }
+ else
+ {
+ if ( vol.isMuted() && extinfo.type != MIXT_ONOFF )
+ {
+ return 0;
+ }
+
+ if ( vol.isCapture() )
+ {
+ switch ( extinfo.type )
+ {
+ case MIXT_ONOFF:
+ vol.setMuted(mv.value != extinfo.maxvalue);
+ break;
+
+ case MIXT_MONOSLIDER:
+ vol.setVolume(Volume::LEFT, mv.value & 0xff);
+ break;
+
+ case MIXT_STEREOSLIDER:
+ vol.setVolume(Volume::LEFT, mv.value & 0xff);
+ vol.setVolume(Volume::RIGHT, ( mv.value >> 8 ) & 0xff);
+ break;
+
+ case MIXT_SLIDER:
+ vol.setVolume(Volume::LEFT, mv.value);
+ break;
+
+ case MIXT_MONOSLIDER16:
+ vol.setVolume(Volume::LEFT, mv.value & 0xffff);
+ break;
+
+ case MIXT_STEREOSLIDER16:
+ vol.setVolume(Volume::LEFT, mv.value & 0xffff);
+ vol.setVolume(Volume::RIGHT, ( mv.value >> 16 ) & 0xffff);
+ break;
+ }
+ }
+ else
+ {
+ switch( extinfo.type )
+ {
+ case MIXT_ONOFF:
+ vol.setMuted(mv.value != extinfo.maxvalue);
+ break;
+ case MIXT_MONOSLIDER:
+ vol.setVolume(Volume::LEFT, mv.value & 0xff);
+ break;
+
+ case MIXT_STEREOSLIDER:
+ vol.setVolume(Volume::LEFT, mv.value & 0xff);
+ vol.setVolume(Volume::RIGHT, ( mv.value >> 8 ) & 0xff);
+ break;
+
+ case MIXT_SLIDER:
+ vol.setVolume(Volume::LEFT, mv.value);
+ break;
+
+ case MIXT_MONOSLIDER16:
+ vol.setVolume(Volume::LEFT, mv.value & 0xffff);
+ break;
+
+ case MIXT_STEREOSLIDER16:
+ vol.setVolume(Volume::LEFT, mv.value & 0xffff);
+ vol.setVolume(Volume::RIGHT, ( mv.value >> 16 ) & 0xffff);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+int Mixer_OSS4::writeVolumeToHW(int ctrlnum, Volume &vol)
+{
+ int volume = 0;
+
+ oss_mixext extinfo;
+ oss_mixer_value mv;
+
+ extinfo.dev = m_devnum;
+ extinfo.ctrl = ctrlnum;
+
+ if ( wrapIoctl( ioctl(m_fd, SNDCTL_MIX_EXTINFO, &extinfo) ) < 0 )
+ {
+ //TODO: more specific error handling
+ kdDebug ( 67100 ) << "failed to read info for control " << ctrlnum << endl;
+ return Mixer::ERR_READ;
+ }
+
+ if ( vol.isMuted() && extinfo.type != MIXT_ONOFF )
+ {
+ volume = 0;
+ }
+ else
+ {
+ switch ( extinfo.type )
+ {
+ case MIXT_ONOFF:
+ volume = (vol.isMuted()) ? (extinfo.minvalue) : (extinfo.maxvalue);
+ break;
+ case MIXT_MONOSLIDER:
+ volume = vol[Volume::LEFT];
+ break;
+
+ case MIXT_STEREOSLIDER:
+ volume = vol[Volume::LEFT] | ( vol[Volume::RIGHT] << 8 );
+ break;
+
+ case MIXT_SLIDER:
+ volume = vol[Volume::LEFT];
+ break;
+
+ case MIXT_MONOSLIDER16:
+ volume = vol[Volume::LEFT];
+ break;
+
+ case MIXT_STEREOSLIDER16:
+ volume = vol[Volume::LEFT] | ( vol[Volume::RIGHT] << 16 );
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ mv.dev = extinfo.dev;
+ mv.ctrl = extinfo.ctrl;
+ mv.timestamp = extinfo.timestamp;
+ mv.value = volume;
+
+ if ( wrapIoctl ( ioctl (m_fd, SNDCTL_MIX_WRITE, &mv) ) < 0 )
+ {
+ kdDebug ( 67100 ) << "error writing: " << endl;
+ return Mixer::ERR_WRITE;
+ }
+ return 0;
+}
+
+void Mixer_OSS4::setEnumIdHW(int ctrlnum, unsigned int idx)
+{
+ oss_mixext extinfo;
+ oss_mixer_value mv;
+
+ extinfo.dev = m_devnum;
+ extinfo.ctrl = ctrlnum;
+
+ if ( wrapIoctl ( ioctl(m_fd, SNDCTL_MIX_EXTINFO, &extinfo) ) < 0 )
+ {
+ //TODO: more specific error handling
+ kdDebug ( 67100 ) << "failed to read info for control " << ctrlnum << endl;
+ return;
+ }
+
+ if ( extinfo.type != MIXT_ENUM )
+ {
+ return;
+ }
+
+
+ //according to oss docs maxVal < minVal could be true - strange...
+ unsigned int maxVal = (unsigned int) extinfo.maxvalue;
+ unsigned int minVal = (unsigned int) extinfo.minvalue;
+
+ if ( maxVal < minVal )
+ {
+ int temp;
+ temp = maxVal;
+ maxVal = minVal;
+ minVal = temp;
+ }
+
+ if ( idx > maxVal || idx < minVal )
+ idx = minVal;
+
+ mv.dev = extinfo.dev;
+ mv.ctrl = extinfo.ctrl;
+ mv.timestamp = extinfo.timestamp;
+ mv.value = idx;
+
+ if ( wrapIoctl ( ioctl (m_fd, SNDCTL_MIX_WRITE, &mv) ) < 0 )
+ {
+ /* Oops, can't write to mixer */
+ kdDebug ( 67100 ) << "error writing: " << endl;
+ }
+}
+
+unsigned int Mixer_OSS4::enumIdHW(int ctrlnum)
+{
+ oss_mixext extinfo;
+ oss_mixer_value mv;
+
+ extinfo.dev = m_devnum;
+ extinfo.ctrl = ctrlnum;
+
+ if ( wrapIoctl ( ioctl(m_fd, SNDCTL_MIX_EXTINFO, &extinfo) ) < 0 )
+ {
+ //TODO: more specific error handling
+ //TODO: check whether those return values are actually possible
+ return Mixer::ERR_READ;
+ }
+
+ if ( extinfo.type != MIXT_ENUM )
+ {
+ return Mixer::ERR_READ;
+ }
+
+ mv.dev = extinfo.dev;
+ mv.ctrl = extinfo.ctrl;
+ mv.timestamp = extinfo.timestamp;
+
+ if ( wrapIoctl ( ioctl (m_fd, SNDCTL_MIX_READ, &mv) ) < 0 )
+ {
+ /* Oops, can't read mixer */
+ return Mixer::ERR_READ;
+ }
+ return mv.value;
+}
+
+int Mixer_OSS4::wrapIoctl(int ioctlRet)
+{
+ switch( ioctlRet )
+ {
+ case EIO:
+ {
+ kdDebug ( 67100 ) << "A hardware level error occured" << endl;
+ break;
+ }
+ case EINVAL:
+ {
+ kdDebug ( 67100 ) << "Operation caused an EINVAL. You may have found a bug in kmix OSS4 module or in OSS4 itself" << endl;
+ break;
+ }
+ case ENXIO:
+ {
+ kdDebug ( 67100 ) << "Operation index out of bounds or requested device does not exist - you likely found a bug in the kmix OSS4 module" << endl;
+ break;
+ }
+ case EPERM:
+ case EACCES:
+ {
+ kdDebug ( 67100 ) << errorText ( Mixer::ERR_PERM ) << endl;
+ break;
+ }
+ case ENODEV:
+ {
+ kdDebug ( 67100 ) << "kmix received an ENODEV errors - are the OSS4 drivers loaded?" << endl;
+ break;
+ }
+ case EPIPE:
+ case EIDRM:
+ {
+ reinitialize();
+ }
+
+ }
+ return ioctlRet;
+}
+
+
+QString OSS4_getDriverName()
+{
+ return "OSS4";
+}
+
diff --git a/kmix/mixer_oss4.h b/kmix/mixer_oss4.h
new file mode 100644
index 00000000..07a927d0
--- /dev/null
+++ b/kmix/mixer_oss4.h
@@ -0,0 +1,42 @@
+//-*-C++-*-
+
+#ifndef MIXER_OSS4_H
+#define MIXER_OSS4_H
+
+#include <qstring.h>
+
+#include "mixer_backend.h"
+#include <sys/soundcard.h>
+
+class Mixer_OSS4 : public Mixer_Backend
+{
+public:
+ Mixer_OSS4(int device = -1);
+ virtual ~Mixer_OSS4();
+
+ virtual QString errorText(int mixer_error);
+ virtual int readVolumeFromHW( int ctrlnum, Volume &vol );
+ virtual int writeVolumeToHW( int ctrlnum, Volume &vol );
+ virtual void setEnumIdHW(int ctrlnum, unsigned int idx);
+ virtual unsigned int enumIdHW(int ctrlnum);
+ virtual bool setRecsrcHW( int ctrlnum, bool on);
+ virtual bool isRecsrcHW( int ctrlnum );
+
+protected:
+
+ MixDevice::ChannelType classifyAndRename(QString &name, int flags);
+
+ int wrapIoctl(int ioctlRet);
+
+ void reinitialize() { open(); close(); };
+ virtual int open();
+ virtual int close();
+ virtual bool needsPolling() { return true; };
+
+ int m_ossVersion;
+ int m_fd;
+ int m_numExtensions;
+ int m_numMixers;
+ QString m_deviceName;
+};
+#endif
diff --git a/kmix/mixer_sun.cpp b/kmix/mixer_sun.cpp
new file mode 100644
index 00000000..542fdcc8
--- /dev/null
+++ b/kmix/mixer_sun.cpp
@@ -0,0 +1,473 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2000 Christian Esken <esken@kde.org>
+ * 2000 Brian Hanson <bhanson@hotmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <sys/audioio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "mixer_sun.h"
+
+
+//======================================================================
+// CONSTANT/ENUM DEFINITIONS
+//======================================================================
+
+//
+// Mixer Device Numbers
+//
+// Note: We can't just use the Sun port #defines because :
+// 1) Some logical devices don't correspond to ports (master&recmon)
+// 2) The play and record port definitions reuse the same values
+//
+enum MixerDevs
+{
+ MIXERDEV_MASTER_VOLUME,
+ MIXERDEV_INTERNAL_SPEAKER,
+ MIXERDEV_HEADPHONE,
+ MIXERDEV_LINE_OUT,
+ MIXERDEV_RECORD_MONITOR,
+ MIXERDEV_MICROPHONE,
+ MIXERDEV_LINE_IN,
+ MIXERDEV_CD,
+ // Insert new devices before this marker
+ MIXERDEV_END_MARKER
+};
+const int numDevs = MIXERDEV_END_MARKER;
+
+//
+// Device name strings
+//
+const char* MixerDevNames[] =
+{
+ I18N_NOOP("Master Volume"),
+ I18N_NOOP("Internal Speaker"),
+ I18N_NOOP("Headphone"),
+ I18N_NOOP("Line Out"),
+ I18N_NOOP("Record Monitor"),
+ I18N_NOOP("Microphone"),
+ I18N_NOOP("Line In"),
+ I18N_NOOP("CD")
+};
+
+//
+// Channel types (this specifies which icon to display)
+//
+const MixDevice::ChannelType MixerChannelTypes[] =
+{
+ MixDevice::VOLUME, // MASTER_VOLUME
+ MixDevice::AUDIO, // INTERNAL_SPEAKER
+ MixDevice::EXTERNAL, // HEADPHONE (we really need an icon for this)
+ MixDevice::EXTERNAL, // LINE_OUT
+ MixDevice::RECMONITOR, // RECORD_MONITOR
+ MixDevice::MICROPHONE, // MICROPHONE
+ MixDevice::EXTERNAL, // LINE_IN
+ MixDevice::CD // CD
+};
+
+//
+// Mapping from device numbers to Sun port mask values
+//
+const uint_t MixerSunPortMasks[] =
+{
+ 0, // MASTER_VOLUME - no associated port
+ AUDIO_SPEAKER,
+ AUDIO_HEADPHONE,
+ AUDIO_LINE_OUT,
+ 0, // RECORD_MONITOR - no associated port
+ AUDIO_MICROPHONE,
+ AUDIO_LINE_IN,
+ AUDIO_CD
+};
+
+
+//======================================================================
+// FUNCTION/METHOD DEFINITIONS
+//======================================================================
+
+
+//======================================================================
+// FUNCTION : SUN_getMixer
+// DESCRIPTION : Creates and returns a new mixer object.
+//======================================================================
+Mixer_Backend* SUN_getMixer( int devnum )
+{
+ Mixer_Backend *l_mixer;
+ l_mixer = new Mixer_SUN( devnum );
+ return l_mixer;
+}
+
+
+//======================================================================
+// FUNCTION : Mixer::Mixer
+// DESCRIPTION : Class constructor.
+//======================================================================
+Mixer_SUN::Mixer_SUN(int devnum) : Mixer_Backend(devnum)
+{
+ if ( devnum == -1 )
+ m_devnum = 0;
+}
+
+//======================================================================
+// FUNCTION : Mixer::Mixer
+// DESCRIPTION : Class destructor.
+//======================================================================
+Mixer_SUN::~Mixer_SUN()
+{
+ close();
+}
+
+//======================================================================
+// FUNCTION : Mixer::open
+// DESCRIPTION : Initialize the mixer and open the hardware driver.
+//======================================================================
+int Mixer_SUN::open()
+{
+ //
+ // We don't support multiple devices
+ //
+ if ( m_devnum !=0 )
+ return Mixer::ERR_OPEN;
+
+ //
+ // Open the mixer hardware driver
+ //
+ QCString audiodev(getenv("AUDIODEV"));
+ if(audiodev.isNull())
+ audiodev = "/dev/audio";
+ audiodev += "ctl";
+ if ( ( fd = ::open( audiodev.data(), O_RDWR ) ) < 0 )
+ {
+ if ( errno == EACCES )
+ return Mixer::ERR_PERM;
+ else
+ return Mixer::ERR_OPEN;
+ }
+ else
+ {
+ //
+ // Mixer is open. Now define all of the mix devices.
+ //
+
+ if( m_mixDevices.isEmpty() )
+ {
+ for ( int idx = 0; idx < numDevs; idx++ )
+ {
+ Volume vol( 2, AUDIO_MAX_GAIN );
+ readVolumeFromHW( idx, vol );
+ MixDevice* md = new MixDevice( idx, vol, false, true,
+ QString(MixerDevNames[idx]), MixerChannelTypes[idx]);
+ md->setRecSource( isRecsrcHW( idx ) );
+ m_mixDevices.append( md );
+ }
+ }
+ else
+ {
+ for( unsigned int idx = 0; idx < m_mixDevices.count(); idx++ )
+ {
+ MixDevice* md = m_mixDevices.at( idx );
+ if( !md )
+ return Mixer::ERR_INCOMPATIBLESET;
+ writeVolumeToHW( idx, md->getVolume() );
+ }
+ }
+
+ m_mixerName = "SUN Audio Mixer";
+ m_isOpen = true;
+
+ return 0;
+ }
+}
+
+//======================================================================
+// FUNCTION : Mixer::close
+// DESCRIPTION : Close the hardware driver.
+//======================================================================
+int Mixer_SUN::close()
+{
+ m_isOpen = false;
+ int l_i_ret = ::close( fd );
+ m_mixDevices.clear();
+ return l_i_ret;
+}
+
+//======================================================================
+// FUNCTION : Mixer::errorText
+// DESCRIPTION : Convert an error code enum to a text string.
+//======================================================================
+QString Mixer_SUN::errorText( int mixer_error )
+{
+ QString errmsg;
+ switch (mixer_error)
+ {
+ case Mixer::ERR_PERM:
+ errmsg = i18n(
+ "kmix: You do not have permission to access the mixer device.\n"
+ "Ask your system administrator to fix /dev/audioctl to allow access."
+ );
+ break;
+ default:
+ errmsg = Mixer_Backend::errorText( mixer_error );
+ }
+ return errmsg;
+}
+
+
+//======================================================================
+// FUNCTION : Mixer::readVolumeFrmoHW
+// DESCRIPTION : Read the audio information from the driver.
+//======================================================================
+int Mixer_SUN::readVolumeFromHW( int devnum, Volume& volume )
+{
+ audio_info_t audioinfo;
+ uint_t devMask = MixerSunPortMasks[devnum];
+
+ //
+ // Read the current audio information from the driver
+ //
+ if ( ioctl( fd, AUDIO_GETINFO, &audioinfo ) < 0 )
+ {
+ return( Mixer::ERR_READ );
+ }
+ else
+ {
+ //
+ // Extract the appropriate fields based on the requested device
+ //
+ switch ( devnum )
+ {
+ case MIXERDEV_MASTER_VOLUME :
+ volume.setMuted( audioinfo.output_muted );
+ GainBalanceToVolume( audioinfo.play.gain,
+ audioinfo.play.balance,
+ volume );
+ break;
+
+ case MIXERDEV_RECORD_MONITOR :
+ volume.setMuted(FALSE);
+ volume.setAllVolumes( audioinfo.monitor_gain );
+ break;
+
+ case MIXERDEV_INTERNAL_SPEAKER :
+ case MIXERDEV_HEADPHONE :
+ case MIXERDEV_LINE_OUT :
+ volume.setMuted( (audioinfo.play.port & devMask) ? FALSE : TRUE );
+ GainBalanceToVolume( audioinfo.play.gain,
+ audioinfo.play.balance,
+ volume );
+ break;
+
+ case MIXERDEV_MICROPHONE :
+ case MIXERDEV_LINE_IN :
+ case MIXERDEV_CD :
+ volume.setMuted( (audioinfo.record.port & devMask) ? FALSE : TRUE );
+ GainBalanceToVolume( audioinfo.record.gain,
+ audioinfo.record.balance,
+ volume );
+ break;
+
+ default :
+ return Mixer::ERR_NODEV;
+ }
+ return 0;
+ }
+}
+
+//======================================================================
+// FUNCTION : Mixer::writeVolumeToHW
+// DESCRIPTION : Write the specified audio settings to the hardware.
+//======================================================================
+int Mixer_SUN::writeVolumeToHW( int devnum, Volume &volume )
+{
+ uint_t gain;
+ uchar_t balance;
+ uchar_t mute;
+
+ //
+ // Convert the Volume(left vol, right vol) to the Gain/Balance Sun uses
+ //
+ VolumeToGainBalance( volume, gain, balance );
+ mute = volume.isMuted() ? 1 : 0;
+
+ //
+ // Read the current audio settings from the hardware
+ //
+ audio_info_t audioinfo;
+ if ( ioctl( fd, AUDIO_GETINFO, &audioinfo ) < 0 )
+ {
+ return( Mixer::ERR_READ );
+ }
+
+ //
+ // Now, based on the devnum that we are writing to, update the appropriate
+ // volume field and twiddle the appropriate bitmask to enable/mute the
+ // device as necessary.
+ //
+ switch ( devnum )
+ {
+ case MIXERDEV_MASTER_VOLUME :
+ audioinfo.play.gain = gain;
+ audioinfo.play.balance = balance;
+ audioinfo.output_muted = mute;
+ break;
+
+ case MIXERDEV_RECORD_MONITOR :
+ audioinfo.monitor_gain = gain;
+ // no mute or balance for record monitor
+ break;
+
+ case MIXERDEV_INTERNAL_SPEAKER :
+ case MIXERDEV_HEADPHONE :
+ case MIXERDEV_LINE_OUT :
+ audioinfo.play.gain = gain;
+ audioinfo.play.balance = balance;
+ if ( mute )
+ audioinfo.play.port &= ~MixerSunPortMasks[devnum];
+ else
+ audioinfo.play.port |= MixerSunPortMasks[devnum];
+ break;
+
+ case MIXERDEV_MICROPHONE :
+ case MIXERDEV_LINE_IN :
+ case MIXERDEV_CD :
+ audioinfo.record.gain = gain;
+ audioinfo.record.balance = balance;
+ if ( mute )
+ audioinfo.record.port &= ~MixerSunPortMasks[devnum];
+ else
+ audioinfo.record.port |= MixerSunPortMasks[devnum];
+ break;
+
+ default :
+ return Mixer::ERR_NODEV;
+ }
+
+ //
+ // Now that we've updated the audioinfo struct, write it back to the hardware
+ //
+ if ( ioctl( fd, AUDIO_SETINFO, &audioinfo ) < 0 )
+ {
+ return( Mixer::ERR_WRITE );
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+//======================================================================
+// FUNCTION : Mixer::setRecsrcHW
+// DESCRIPTION :
+//======================================================================
+bool Mixer_SUN::setRecsrcHW( int /* devnum */, bool /* on */ )
+{
+ return FALSE;
+}
+
+//======================================================================
+// FUNCTION : Mixer::isRecsrcHW
+// DESCRIPTION : Returns true if the specified device is a record source.
+//======================================================================
+bool Mixer_SUN::isRecsrcHW( int devnum )
+{
+ switch ( devnum )
+ {
+ case MIXERDEV_MICROPHONE :
+ case MIXERDEV_LINE_IN :
+ case MIXERDEV_CD :
+ return TRUE;
+
+ default :
+ return FALSE;
+ }
+}
+
+//======================================================================
+// FUNCTION : Mixer::VolumeToGainBalance
+// DESCRIPTION : Converts a Volume(left vol + right vol) into the
+// Gain/Balance values used by Sun.
+//======================================================================
+void Mixer_SUN::VolumeToGainBalance( Volume& volume, uint_t& gain, uchar_t& balance )
+{
+ if ( ( volume.count() == 1 ) ||
+ ( volume[Volume::LEFT] == volume[Volume::RIGHT] ) )
+ {
+ gain = volume[Volume::LEFT];
+ balance = AUDIO_MID_BALANCE;
+ }
+ else
+ {
+ if ( volume[Volume::LEFT] > volume[Volume::RIGHT] )
+ {
+ gain = volume[Volume::LEFT];
+ balance = AUDIO_LEFT_BALANCE +
+ ( AUDIO_MID_BALANCE - AUDIO_LEFT_BALANCE ) *
+ volume[Volume::RIGHT] / volume[Volume::LEFT];
+ }
+ else
+ {
+ gain = volume[Volume::RIGHT];
+ balance = AUDIO_RIGHT_BALANCE -
+ ( AUDIO_RIGHT_BALANCE - AUDIO_MID_BALANCE ) *
+ volume[Volume::LEFT] / volume[Volume::RIGHT];
+ }
+ }
+}
+
+//======================================================================
+// FUNCTION : Mixer::GainBalanceToVolume
+// DESCRIPTION : Converts Gain/Balance returned by Sun driver to the
+// Volume(left vol + right vol) format used by kmix.
+//======================================================================
+void Mixer_SUN::GainBalanceToVolume( uint_t& gain, uchar_t& balance, Volume& volume )
+{
+ if ( volume.count() == 1 )
+ {
+ volume.setVolume( Volume::LEFT, gain );
+ }
+ else
+ {
+ if ( balance <= AUDIO_MID_BALANCE )
+ {
+ volume.setVolume( Volume::LEFT, gain );
+ volume.setVolume( Volume::RIGHT, gain *
+ ( balance - AUDIO_LEFT_BALANCE ) /
+ ( AUDIO_MID_BALANCE - AUDIO_LEFT_BALANCE ) );
+ }
+ else
+ {
+ volume.setVolume( Volume::RIGHT, gain );
+ volume.setVolume( Volume::LEFT, gain *
+ ( AUDIO_RIGHT_BALANCE - balance ) /
+ ( AUDIO_RIGHT_BALANCE - AUDIO_MID_BALANCE ) );
+ }
+ }
+}
+
+QString SUN_getDriverName() {
+ return "SUNAudio";
+}
+
diff --git a/kmix/mixer_sun.h b/kmix/mixer_sun.h
new file mode 100644
index 00000000..cd1ef76a
--- /dev/null
+++ b/kmix/mixer_sun.h
@@ -0,0 +1,52 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2000 Christian Esken <esken@kde.org>
+ * 2000 Brian Hanson <bhanson@hotmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef MIXER_SUN_H
+#define MIXER_SUN_H
+
+#include <qstring.h>
+
+#include "mixer_backend.h"
+
+class Mixer_SUN : public Mixer_Backend
+{
+public:
+ Mixer_SUN(int devnum);
+ virtual ~Mixer_SUN();
+
+ virtual QString errorText(int mixer_error);
+ virtual int readVolumeFromHW( int devnum, Volume& volume );
+ virtual int writeVolumeToHW( int devnum, Volume& volume );
+ bool setRecsrcHW( int devnum, bool on );
+ bool isRecsrcHW( int devnum );
+
+protected:
+ virtual int open();
+ virtual int close();
+
+ void VolumeToGainBalance( Volume& volume, uint_t& gain, uchar_t& balance );
+ void GainBalanceToVolume( uint_t& gain, uchar_t& balance, Volume& volume );
+
+ int fd;
+};
+
+#endif
diff --git a/kmix/mixertoolbox.cpp b/kmix/mixertoolbox.cpp
new file mode 100644
index 00000000..9b6ee943
--- /dev/null
+++ b/kmix/mixertoolbox.cpp
@@ -0,0 +1,226 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#include "qwidget.h"
+#include "qstring.h"
+
+//#include <kdebug.h>
+#include <klocale.h>
+
+#include "mixdevice.h"
+#include "mixer.h"
+
+#include "mixertoolbox.h"
+
+/***********************************************************************************
+ Attention:
+ This MixerToolBox is linked to the KMix Main Program, the KMix Applet and kmixctrl.
+ As we do not want to link in more than neccesary to kmixctrl, you are asked
+ not to put any GUI classes in here.
+ In the case where it is unavoidable, please put them in KMixToolBox.
+ ***********************************************************************************/
+
+/**
+ * Scan for Mixers in the System. This is the method that implicitely fills the
+ * list of Mixer's, which is accesible via the static Mixer::mixer() method.
+ * @par mixers The list where to add the found Mixers. This parameter is superfluous
+ * nowadays, as it is now really trivial to get it - just call the static
+ * Mixer::mixer() method.
+ * @par multiDriverMode Whether the Mixer scan should try more all backendends.
+ * 'true' means to scan all backends. 'false' means: After scanning the
+ * current backend the next backend is only scanned if no Mixers were found yet.
+ */
+void MixerToolBox::initMixer(QPtrList<Mixer> &mixers, bool multiDriverMode, QString& ref_hwInfoString)
+{
+ //kdDebug(67100) << "IN MixerToolBox::initMixer()"<<endl;
+
+ // Find all mixers and initalize them
+ QMap<QString,int> mixerNums;
+ int drvNum = Mixer::numDrivers();
+
+ int driverWithMixer = -1;
+ bool multipleDriversActive = false;
+
+ QString driverInfo = "";
+ QString driverInfoUsed = "";
+
+ for( int drv1=0; drv1<drvNum; drv1++ )
+ {
+ QString driverName = Mixer::driverName(drv1);
+ if ( driverInfo.length() > 0 )
+ driverInfo += " + ";
+ driverInfo += driverName;
+ }
+ /* Run a loop over all drivers. The loop will terminate after the first driver which
+ has mixers. And here is the reason:
+ - If you run ALSA with ALSA-OSS-Emulation enabled, mixers will show up twice: once
+ as native ALSA mixer, once as OSS mixer (emulated by ALSA). This is bad and WILL
+ confuse users. So it is a design decision that we can compile in multiple drivers
+ but we can run only one driver.
+ - For special usage scenarios, people will still want to run both drivers at the
+ same time. We allow them to hack their Config-File, where they can enable a
+ multi-driver mode.
+ - Another remark: For KMix3.0 or so, we should allow multiple-driver, for allowing
+ addition of special-use drivers, e.g. an Jack-Mixer-Backend, or a CD-Rom volume Backend.
+ */
+
+ bool autodetectionFinished = false;
+ for( int drv=0; drv<drvNum; drv++ )
+ {
+ QString driverName = Mixer::driverName(drv);
+
+ if ( autodetectionFinished ) {
+ // sane exit from loop
+ break;
+ }
+
+ bool drvInfoAppended = false;
+ // The "19" below is just a "silly" number:
+ // (Old: The loop will break as soon as an error is detected - e.g. on 3rd loop when 2 soundcards are installed)
+ // New: We don't try be that clever anymore. We now blindly scan 20 cards, as the clever
+ // approach doesn't work for the one or other user.
+ int devNumMax = 19;
+ for( int dev=0; dev<=devNumMax; dev++ )
+ {
+ Mixer *mixer = new Mixer( drv, dev );
+ if ( mixer->isValid() ) {
+ mixer->open();
+ mixers.append( mixer );
+ // Count mixer nums for every mixer name to identify mixers with equal names.
+ // This is for creating persistent (reusable) primary keys, which can safely
+ // be referenced (especially for config file access, so it is meant to be persistent!).
+ mixerNums[mixer->mixerName()]++;
+ // Create a useful PK
+ /* As we use "::" and ":" as separators, the parts %1,%2 and %3 may not
+ * contain it.
+ * %1, the driver name is from the KMix backends, it does not contain colons.
+ * %2, the mixer name, is typically coming from an OS driver. It could contain colons.
+ * %3, the mixer number, is a number: it does not contain colons.
+ */
+ QString mixerName = mixer->mixerName();
+ mixerName.replace(":","_");
+ QString primaryKeyOfMixer = QString("%1::%2:%3")
+ .arg(driverName)
+ .arg(mixerName)
+ .arg(mixerNums[mixer->mixerName()]);
+ // The following 3 replaces are for not messing up the config file
+ primaryKeyOfMixer.replace("]","_");
+ primaryKeyOfMixer.replace("[","_"); // not strictly neccesary, but lets play safe
+ primaryKeyOfMixer.replace(" ","_");
+ primaryKeyOfMixer.replace("=","_");
+
+ mixer->setID(primaryKeyOfMixer);
+
+ } // valid
+ else
+ {
+ delete mixer;
+ mixer = 0;
+ } // invalid
+
+ /* Lets decide if we the autoprobing shall continue: */
+ if ( multiDriverMode ) {
+ // trivial case: In multiDriverMode, we scan ALL 20 devs of ALL drivers
+ // so we have to do "nothing" in this case
+ } // multiDriver
+ else {
+ // In No-multiDriver-mode we only need to check after we reached devNumMax
+ if ( dev == devNumMax ) {
+ if ( Mixer::mixers().count() != 0 ) {
+ // highest device number of driver and a Mixer => finished
+ autodetectionFinished = true;
+ }
+ }
+ } // !multiDriver
+
+ // append driverName (used drivers)
+ if ( !drvInfoAppended )
+ {
+ drvInfoAppended = true;
+ QString driverName = Mixer::driverName(drv);
+ if ( drv!= 0 && mixers.count() > 0) {
+ driverInfoUsed += " + ";
+ }
+ driverInfoUsed += driverName;
+ }
+
+ // Check whether there are mixers in different drivers, so that the user can be warned
+ if (!multipleDriversActive)
+ {
+ if ( driverWithMixer == -1 )
+ {
+ // Aha, this is the very first detected device
+ driverWithMixer = drv;
+ }
+ else
+ {
+ if ( driverWithMixer != drv )
+ {
+ // Got him: There are mixers in different drivers
+ multipleDriversActive = true;
+ }
+ }
+ } // !multipleDriversActive
+
+ } // loop over sound card devices of current driver
+ } // loop over soundcard drivers
+
+ if ( Mixer::masterCard() == 0 ) {
+ // We have no master card yet. This actually only happens when there was
+ // not one defined in the kmixrc.
+ // So lets just set the first card as master card.
+ if ( Mixer::mixers().count() > 0 ) {
+ Mixer::setMasterCard( Mixer::mixers().first()->id());
+ }
+ }
+
+ ref_hwInfoString = i18n("Sound drivers supported:");
+ ref_hwInfoString.append(" ").append( driverInfo ).append( "\n").append(i18n("Sound drivers used:")) .append(" ").append(driverInfoUsed);
+
+ if ( multipleDriversActive )
+ {
+ // this will only be possible by hacking the config-file, as it will not be officially supported
+ ref_hwInfoString += "\nExperimental multiple-Driver mode activated";
+ }
+
+ kdDebug(67100) << ref_hwInfoString << endl << "Total number of detected Mixers: " << Mixer::mixers().count() << endl;
+ //kdDebug(67100) << "OUT MixerToolBox::initMixer()"<<endl;
+
+}
+
+
+/*
+ * Clean up and free all ressources of all found Mixers, which were found in the initMixer() call
+ */
+void MixerToolBox::deinitMixer()
+{
+ //kdDebug(67100) << "IN MixerToolBox::deinitMixer()"<<endl;
+ Mixer *mixer;
+ while ( (mixer=Mixer::mixers().first()) != 0)
+ {
+ //kdDebug(67100) << "MixerToolBox::deinitMixer() Remove Mixer" << endl;
+ mixer->close();
+ Mixer::mixers().remove(mixer);
+ delete mixer;
+ }
+ // kdDebug(67100) << "OUT MixerToolBox::deinitMixer()"<<endl;
+}
diff --git a/kmix/mixertoolbox.h b/kmix/mixertoolbox.h
new file mode 100644
index 00000000..1af4f22c
--- /dev/null
+++ b/kmix/mixertoolbox.h
@@ -0,0 +1,22 @@
+#ifndef MIXERTOOLBOX_H
+#define MIXERTOOLBOX_H
+
+#include <qptrlist.h>
+#include <qstring.h>
+
+class Mixer;
+
+/**
+ * This toolbox contains various static methods that are shared throughout KMix.
+ * It only contains no-GUI code. The shared with-GUI code is in KMixToolBox
+ * The reason, why it is not put in a common base class is, that the classes are
+ * very different and cannot be changed (e.g. KPanelApplet) without major headache.
+ */
+class MixerToolBox {
+ public:
+ static void initMixer(QPtrList<Mixer>&, bool, QString&);
+ static void deinitMixer();
+};
+
+
+#endif
diff --git a/kmix/mixset.cpp b/kmix/mixset.cpp
new file mode 100644
index 00000000..c1b2a73f
--- /dev/null
+++ b/kmix/mixset.cpp
@@ -0,0 +1,71 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#include <qstring.h>
+
+#include <kdebug.h>
+#include <kconfig.h>
+
+#include "mixdevice.h"
+#include "mixset.h"
+
+void MixSet::clone( MixSet &set )
+{
+ clear();
+
+ for( MixDevice *md=set.first(); md!=0; md=set.next() )
+ {
+ append( new MixDevice( *md ) );
+ }
+}
+
+void MixSet::read( KConfig *config, const QString& grp )
+{
+ kdDebug(67100) << "MixSet::read() of group " << grp << endl;
+ config->setGroup(grp);
+ m_name = config->readEntry( "name", m_name );
+
+ MixDevice* md;
+ for( md=first(); md!=0; md=next() )
+ {
+ md->read( config, grp );
+ }
+}
+
+void MixSet::write( KConfig *config, const QString& grp )
+{
+ kdDebug(67100) << "MixSet::write() of group " << grp << endl;
+ config->setGroup(grp);
+ config->writeEntry( "name", m_name );
+
+ MixDevice* md;
+ for( md=first(); md!=0; md=next() )
+ {
+ md->write( config, grp );
+ }
+}
+
+void MixSet::setName( const QString &name )
+{
+ m_name = name;
+}
+
diff --git a/kmix/mixset.h b/kmix/mixset.h
new file mode 100644
index 00000000..24448883
--- /dev/null
+++ b/kmix/mixset.h
@@ -0,0 +1,21 @@
+#ifndef MixSet_h
+#define MixSet_h
+
+#include "mixdevice.h"
+
+class MixSet : public QPtrList<MixDevice>
+{
+ public:
+ void read( KConfig *config, const QString& grp );
+ void write( KConfig *config, const QString& grp );
+
+ void clone( MixSet &orig );
+
+ QString name() { return m_name; };
+ void setName( const QString &name );
+
+ private:
+ QString m_name;
+};
+
+#endif
diff --git a/kmix/pics/Listener.png b/kmix/pics/Listener.png
new file mode 100644
index 00000000..4f195914
--- /dev/null
+++ b/kmix/pics/Listener.png
Binary files differ
diff --git a/kmix/pics/Makefile.am b/kmix/pics/Makefile.am
new file mode 100644
index 00000000..bd1d3ee8
--- /dev/null
+++ b/kmix/pics/Makefile.am
@@ -0,0 +1,14 @@
+
+picsdir = $(kde_datadir)/kmix/pics
+pics_DATA = mix_audio.png mix_bass.png mix_cd.png mix_ext.png \
+ mix_microphone.png mix_midi.png mix_recmon.png mix_treble.png \
+ mix_unknown.png mix_volume.png mix_surround.png mix_video.png \
+ mix_headphone.png mix_digital.png mix_ac97.png \
+ kmixdocked.png kmixdocked_mute.png kmixdocked_error.png \
+ mix_record.png \
+ SpeakerFrontLeft.png SpeakerRearLeft.png SpeakerFrontRight.png SpeakerRearRight.png Listener.png
+
+
+EXTRA_DIST = $(pics_DATA)
+
+KDE_ICON=kmix
diff --git a/kmix/pics/SpeakerFrontLeft.png b/kmix/pics/SpeakerFrontLeft.png
new file mode 100644
index 00000000..21f7795e
--- /dev/null
+++ b/kmix/pics/SpeakerFrontLeft.png
Binary files differ
diff --git a/kmix/pics/SpeakerFrontRight.png b/kmix/pics/SpeakerFrontRight.png
new file mode 100644
index 00000000..33cee57e
--- /dev/null
+++ b/kmix/pics/SpeakerFrontRight.png
Binary files differ
diff --git a/kmix/pics/SpeakerRearLeft.png b/kmix/pics/SpeakerRearLeft.png
new file mode 100644
index 00000000..375fe8df
--- /dev/null
+++ b/kmix/pics/SpeakerRearLeft.png
Binary files differ
diff --git a/kmix/pics/SpeakerRearRight.png b/kmix/pics/SpeakerRearRight.png
new file mode 100644
index 00000000..e16af4c8
--- /dev/null
+++ b/kmix/pics/SpeakerRearRight.png
Binary files differ
diff --git a/kmix/pics/hi128-app-kmix.png b/kmix/pics/hi128-app-kmix.png
new file mode 100644
index 00000000..5543818b
--- /dev/null
+++ b/kmix/pics/hi128-app-kmix.png
Binary files differ
diff --git a/kmix/pics/hi16-app-kmix.png b/kmix/pics/hi16-app-kmix.png
new file mode 100644
index 00000000..a422967e
--- /dev/null
+++ b/kmix/pics/hi16-app-kmix.png
Binary files differ
diff --git a/kmix/pics/hi32-app-kmix.png b/kmix/pics/hi32-app-kmix.png
new file mode 100644
index 00000000..2036faa5
--- /dev/null
+++ b/kmix/pics/hi32-app-kmix.png
Binary files differ
diff --git a/kmix/pics/hi48-app-kmix.png b/kmix/pics/hi48-app-kmix.png
new file mode 100644
index 00000000..dc766a89
--- /dev/null
+++ b/kmix/pics/hi48-app-kmix.png
Binary files differ
diff --git a/kmix/pics/hi64-app-kmix.png b/kmix/pics/hi64-app-kmix.png
new file mode 100644
index 00000000..f7679b27
--- /dev/null
+++ b/kmix/pics/hi64-app-kmix.png
Binary files differ
diff --git a/kmix/pics/kmixdocked.png b/kmix/pics/kmixdocked.png
new file mode 100644
index 00000000..367ff42e
--- /dev/null
+++ b/kmix/pics/kmixdocked.png
Binary files differ
diff --git a/kmix/pics/kmixdocked_error.png b/kmix/pics/kmixdocked_error.png
new file mode 100644
index 00000000..e9b68f2e
--- /dev/null
+++ b/kmix/pics/kmixdocked_error.png
Binary files differ
diff --git a/kmix/pics/kmixdocked_mute.png b/kmix/pics/kmixdocked_mute.png
new file mode 100644
index 00000000..31f21541
--- /dev/null
+++ b/kmix/pics/kmixdocked_mute.png
Binary files differ
diff --git a/kmix/pics/mix_ac97.png b/kmix/pics/mix_ac97.png
new file mode 100644
index 00000000..ad4f3219
--- /dev/null
+++ b/kmix/pics/mix_ac97.png
Binary files differ
diff --git a/kmix/pics/mix_audio.png b/kmix/pics/mix_audio.png
new file mode 100644
index 00000000..74fc2764
--- /dev/null
+++ b/kmix/pics/mix_audio.png
Binary files differ
diff --git a/kmix/pics/mix_bass.png b/kmix/pics/mix_bass.png
new file mode 100644
index 00000000..5952d8b8
--- /dev/null
+++ b/kmix/pics/mix_bass.png
Binary files differ
diff --git a/kmix/pics/mix_cd.png b/kmix/pics/mix_cd.png
new file mode 100644
index 00000000..77ac81e9
--- /dev/null
+++ b/kmix/pics/mix_cd.png
Binary files differ
diff --git a/kmix/pics/mix_digital.png b/kmix/pics/mix_digital.png
new file mode 100644
index 00000000..739e60f5
--- /dev/null
+++ b/kmix/pics/mix_digital.png
Binary files differ
diff --git a/kmix/pics/mix_ext.png b/kmix/pics/mix_ext.png
new file mode 100644
index 00000000..c49f08ba
--- /dev/null
+++ b/kmix/pics/mix_ext.png
Binary files differ
diff --git a/kmix/pics/mix_headphone.png b/kmix/pics/mix_headphone.png
new file mode 100644
index 00000000..de62b2fd
--- /dev/null
+++ b/kmix/pics/mix_headphone.png
Binary files differ
diff --git a/kmix/pics/mix_microphone.png b/kmix/pics/mix_microphone.png
new file mode 100644
index 00000000..ca747403
--- /dev/null
+++ b/kmix/pics/mix_microphone.png
Binary files differ
diff --git a/kmix/pics/mix_midi.png b/kmix/pics/mix_midi.png
new file mode 100644
index 00000000..0b8477cc
--- /dev/null
+++ b/kmix/pics/mix_midi.png
Binary files differ
diff --git a/kmix/pics/mix_recmon.png b/kmix/pics/mix_recmon.png
new file mode 100644
index 00000000..dba5c968
--- /dev/null
+++ b/kmix/pics/mix_recmon.png
Binary files differ
diff --git a/kmix/pics/mix_record.png b/kmix/pics/mix_record.png
new file mode 100644
index 00000000..f4aa7401
--- /dev/null
+++ b/kmix/pics/mix_record.png
Binary files differ
diff --git a/kmix/pics/mix_surround.png b/kmix/pics/mix_surround.png
new file mode 100644
index 00000000..d02d02c6
--- /dev/null
+++ b/kmix/pics/mix_surround.png
Binary files differ
diff --git a/kmix/pics/mix_toslink.png b/kmix/pics/mix_toslink.png
new file mode 100644
index 00000000..6766e613
--- /dev/null
+++ b/kmix/pics/mix_toslink.png
Binary files differ
diff --git a/kmix/pics/mix_treble.png b/kmix/pics/mix_treble.png
new file mode 100644
index 00000000..9871ab72
--- /dev/null
+++ b/kmix/pics/mix_treble.png
Binary files differ
diff --git a/kmix/pics/mix_unknown.png b/kmix/pics/mix_unknown.png
new file mode 100644
index 00000000..7c904971
--- /dev/null
+++ b/kmix/pics/mix_unknown.png
Binary files differ
diff --git a/kmix/pics/mix_video.png b/kmix/pics/mix_video.png
new file mode 100644
index 00000000..59efde57
--- /dev/null
+++ b/kmix/pics/mix_video.png
Binary files differ
diff --git a/kmix/pics/mix_volume.png b/kmix/pics/mix_volume.png
new file mode 100644
index 00000000..d17a85d2
--- /dev/null
+++ b/kmix/pics/mix_volume.png
Binary files differ
diff --git a/kmix/resource.h b/kmix/resource.h
new file mode 100644
index 00000000..57461096
--- /dev/null
+++ b/kmix/resource.h
@@ -0,0 +1,30 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef RESOURCE_H
+#define RESOURCE_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+
+#endif // RESOURCE_H
diff --git a/kmix/restore_kmix_volumes.desktop b/kmix/restore_kmix_volumes.desktop
new file mode 100644
index 00000000..fed5e90d
--- /dev/null
+++ b/kmix/restore_kmix_volumes.desktop
@@ -0,0 +1,58 @@
+[Desktop Entry]
+X-KDE-autostart-phase=1
+X-KDE-autostart-condition=kmixrc::startkdeRestore:true
+Type=Application
+Exec=kmixctrl --restore
+OnlyShowIn=KDE;
+Name=Restore Mixer Volumes
+Name[bg]=Възстановяване на стойностите на миксера
+Name[bn]=মিক্সার ভলিউম পুনঃস্থাপন করে
+Name[br]=Assav pep tolzennad mesker
+Name[bs]=Vrati jačine miksera
+Name[ca]=Restaura els volums del mesclador
+Name[cs]=Obnovit nastavení hlasitosti
+Name[cy]=Adfer Lefelau Sain y Cymysgydd
+Name[da]=Genopret mikser lydstyrke
+Name[de]=Lautstärken wiederherstellen
+Name[el]=Επαναφορά των εντάσεων του μείκτη
+Name[eo]=Restarigu Miksilagordon
+Name[es]=Restaurar opciones del mezclador
+Name[et]=Mikseri helitugevuste taastamine
+Name[eu]=Nahasgailuaren bolumenak berreskuratu
+Name[fa]=باز‌گردانی حجم صداهای مخلوط‌کن
+Name[fi]=Palauta mikserin äänivoimakkuudet
+Name[fr]=Restaurer les volumes du mixage
+Name[gl]=Restaurar os Volumes do Mesturador
+Name[he]=שיחזור עוצמות הקול של המערבל
+Name[hu]=A hangkeverő hangerőinek visszaállítása
+Name[is]=Sækja aftur stillingar hljóðrása
+Name[it]=Ripristina i volumi del Mixer
+Name[ja]=ミキサーの音量設定を復元する
+Name[kk]=Микшер деңгейлерін қалпына келтіру
+Name[km]=ស្ដារ​សំឡេង​ឧបករណ៍​លាយ
+Name[ko]=믹서 음량 복원
+Name[lt]=Atstatyti maišytuvo garso lygius
+Name[mk]=Враќање на гласностите на миксетата
+Name[nb]=Gjenopprett lydstyrkene til mikser
+Name[nds]=Mischerluutstärken wedderherstellen
+Name[ne]=मिक्सर भोल्युम पूर्वावस्थामा ल्यानुहोस्
+Name[nl]=Mixervolumes herstellen
+Name[nn]=Gjenopprett miksarlydstyrkar
+Name[pl]=Odtwarzanie głośności miksera
+Name[pt]=Repor os Volumes
+Name[pt_BR]=Restaurar volumes do mixer
+Name[ro]=Reface volumele mixerului
+Name[ru]=Восстановление настроек микшера
+Name[sk]=Obnoviť nastavenia mixéra
+Name[sl]=Obnovi nastavitve mešalnika
+Name[sr]=Обнови јачине миксете
+Name[sr@Latn]=Obnovi jačine miksete
+Name[sv]=Återställ mixervolymer
+Name[ta]=ஒன்றுசேர்க்கும் ஒலியளவு மீட்கவும்
+Name[th]=เรียกคืนระดับเสียงของมิกเซอร์
+Name[tr]=Karıştırıcı Seslerini Yenile
+Name[uk]=Відновити параметри мікшера
+Name[zh_CN]=恢复混音器设置
+Name[zh_HK]=回復混音器音量
+Name[zh_TW]=回復混音器音量
+
diff --git a/kmix/version.h b/kmix/version.h
new file mode 100644
index 00000000..c80dd2f6
--- /dev/null
+++ b/kmix/version.h
@@ -0,0 +1,21 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#define APP_VERSION "2.6.1"
diff --git a/kmix/verticaltext.cpp b/kmix/verticaltext.cpp
new file mode 100644
index 00000000..490e4c91
--- /dev/null
+++ b/kmix/verticaltext.cpp
@@ -0,0 +1,57 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 2003-2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "verticaltext.h"
+#include <qpainter.h>
+#include <kdebug.h>
+
+
+VerticalText::VerticalText(QWidget * parent, const char * name, WFlags f) : QWidget(parent,name,f)
+{
+ resize(20,100 /*parent->height() */ );
+ setMinimumSize(20,10); // neccesary for smooth integration into layouts (we only care for the widths).
+}
+
+VerticalText::~VerticalText() {
+}
+
+
+void VerticalText::paintEvent ( QPaintEvent * /*event*/ ) {
+ //kdDebug(67100) << "paintEvent(). height()=" << height() << "\n";
+ QPainter paint(this);
+ paint.rotate(270);
+ paint.translate(0,-4); // Silly "solution" to make underlengths work
+
+ // Fix for bug 72520
+ //- paint.drawText(-height()+2,width(),name());
+ //+ paint.drawText( -height()+2, width(), QString::fromUtf8(name()) );
+ paint.drawText( -height()+2, width(), QString::fromUtf8(name()) );
+}
+
+QSize VerticalText::sizeHint() const {
+ return QSize(20,100); // !! UGLY. Should be reworked
+}
+
+QSizePolicy VerticalText::sizePolicy () const
+{
+ return QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
+}
+
diff --git a/kmix/verticaltext.h b/kmix/verticaltext.h
new file mode 100644
index 00000000..7b1c07e0
--- /dev/null
+++ b/kmix/verticaltext.h
@@ -0,0 +1,19 @@
+#ifndef VerticalText_h
+#define VerticalText_h
+
+#include <qwidget.h>
+
+class VerticalText : public QWidget
+{
+public:
+ VerticalText(QWidget * parent, const char * name, WFlags f = 0);
+ ~VerticalText();
+
+ QSize sizeHint() const;
+ QSizePolicy sizePolicy () const;
+
+protected:
+ void paintEvent ( QPaintEvent * event );
+};
+
+#endif
diff --git a/kmix/viewapplet.cpp b/kmix/viewapplet.cpp
new file mode 100644
index 00000000..23599190
--- /dev/null
+++ b/kmix/viewapplet.cpp
@@ -0,0 +1,242 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "viewapplet.h"
+
+// Qt
+#include <qwidget.h>
+#include <qlayout.h>
+#include <qsize.h>
+
+// KDE
+#include <kactioncollection.h>
+#include <kdebug.h>
+#include <kpanelapplet.h>
+#include <kstdaction.h>
+
+// KMix
+#include "kmixtoolbox.h"
+#include "mdwslider.h"
+#include "mixer.h"
+
+ViewApplet::ViewApplet(QWidget* parent, const char* name, Mixer* mixer, ViewBase::ViewFlags vflags, KPanelApplet::Position position )
+ : ViewBase(parent, name, QString::null, mixer, WStyle_Customize|WStyle_NoBorder, vflags)
+{
+ setBackgroundOrigin(AncestorOrigin);
+ // remove the menu bar action, that is put by the "ViewBase" constructor in _actions.
+ //KToggleAction *m = static_cast<KToggleAction*>(KStdAction::showMenubar( this, SLOT(toggleMenuBarSlot()), _actions ));
+ _actions->remove( KStdAction::showMenubar(this, SLOT(toggleMenuBarSlot()), _actions) );
+
+
+ if ( position == KPanelApplet::pLeft || position == KPanelApplet::pRight ) {
+ //kdDebug(67100) << "ViewApplet() isVertical" << "\n";
+ _viewOrientation = Qt::Vertical;
+ }
+ else {
+ //kdDebug(67100) << "ViewApplet() isHorizontal" << "\n";
+ _viewOrientation = Qt::Horizontal;
+ }
+
+ if ( _viewOrientation == Qt::Horizontal ) {
+ _layoutMDW = new QHBoxLayout( this );
+ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
+ }
+ else {
+ _layoutMDW = new QVBoxLayout( this );
+ setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
+ }
+
+
+ //_layoutMDW->setResizeMode(QLayout::Fixed);
+ init();
+}
+
+ViewApplet::~ViewApplet() {
+}
+
+void ViewApplet::setMixSet(MixSet *mixset)
+{
+ MixDevice* md;
+ for ( md = mixset->first(); md != 0; md = mixset->next() ) {
+ if ( (! md->isSwitch()) && ( ! md->isEnum() ) ) {
+ _mixSet->append(md);
+ }
+ }
+}
+
+int ViewApplet::count()
+{
+ return ( _mixSet->count() );
+}
+
+int ViewApplet::advice() {
+ if ( _mixSet->count() > 0 ) {
+ // The standard input and output views are always advised, if there are devices in it
+ return 100;
+ }
+ else {
+ return 0;
+ }
+}
+
+
+
+QWidget* ViewApplet::add(MixDevice *md)
+{
+ /**
+ Slider orientation is exactly the other way round. If the applet stretches horzontally,
+ the sliders must be vertical
+ */
+ Qt::Orientation sliderOrientation;
+ if (_viewOrientation == Qt::Horizontal )
+ sliderOrientation = Qt::Vertical;
+ else
+ sliderOrientation = Qt::Horizontal;
+
+ // kdDebug(67100) << "ViewApplet::add()\n";
+ MixDeviceWidget *mdw =
+ new MDWSlider(
+ _mixer, // the mixer for this device
+ md, // MixDevice (parameter)
+ false, // Show Mute LED
+ false, // Show Record LED
+ true, // Small
+ sliderOrientation, // Orientation
+ this, // parent
+ this, // View widget
+ md->name().latin1()
+ );
+ mdw->setBackgroundOrigin(AncestorOrigin);
+
+ static_cast<MDWSlider*>(mdw)->setValueStyle(MixDeviceWidget::NNONE);
+ static_cast<MDWSlider*>(mdw)->setIcons(shouldShowIcons( size()) ); // !!! This should use the panel size
+ _layoutMDW->add(mdw);
+ return mdw;
+}
+
+void ViewApplet::constructionFinished() {
+ _layoutMDW->activate();
+
+ KMixToolBox::setIcons ( _mdws, shouldShowIcons( size()) ); // !!! This should use the panel size
+ KMixToolBox::setValueStyle( _mdws, MixDeviceWidget::NNONE);
+}
+
+
+QSize ViewApplet::sizeHint() const {
+ // Basically out main layout knows very good what the sizes should be
+ QSize qsz = _layoutMDW->sizeHint();
+ //kdDebug(67100) << "ViewApplet::sizeHint(): NewSize is " << qsz << "\n";
+ return qsz;
+}
+
+QSizePolicy ViewApplet::sizePolicy() const {
+ if ( _viewOrientation == Qt::Horizontal ) {
+ //kdDebug(67100) << "ViewApplet::sizePolicy=(Fixed,Expanding)\n";
+ return QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
+ }
+ else {
+ //kdDebug(67100) << "ViewApplet::sizePolicy=(Expanding,Fixed)\n";
+ return QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ }
+}
+
+bool ViewApplet::shouldShowIcons(QSize qsz) {
+ bool showIcons = false;
+ if ( _viewOrientation == Qt::Horizontal ) {
+ if ( qsz.height() >= 32 ) {
+ //kdDebug(67100) << "ViewApplet::resizeEvent() hor >=32" << qre->size() << "\n";
+ showIcons = true;
+ }
+ }
+ else {
+ if ( qsz.width() >= 32 ) {
+ //kdDebug(67100) << "ViewApplet::resizeEvent() vert >=32" << qre->size() << "\n";
+ showIcons = true;
+ }
+ }
+ return showIcons;
+}
+
+void ViewApplet::resizeEvent(QResizeEvent *qre)
+{
+ //kdDebug(67100) << "ViewApplet::resizeEvent() size=" << qre->size() << "\n";
+ // decide whether we have to show or hide all icons
+ bool showIcons = shouldShowIcons(qre->size());
+
+ for ( QWidget *mdw = _mdws.first(); mdw != 0; mdw = _mdws.next() ) {
+ if ( mdw == 0 ) {
+ kdError(67100) << "ViewApplet::resizeEvent(): mdw == 0\n";
+ break; // sanity check (normally the lists are set up correctly)
+ }
+ else {
+ if ( mdw->inherits("MDWSlider")) {
+ static_cast<MDWSlider*>(mdw)->setIcons(showIcons);
+ static_cast<MDWSlider*>(mdw)->setValueStyle(MixDeviceWidget::NNONE);
+ //static_cast<MDWSlider*>(mdw)->resize(qre->size());
+ }
+ }
+ }
+ // kdDebug(67100) << "ViewApplet::resizeEvent(). SHOULD resize _layoutMDW to " << qre->size() << endl;
+ //QWidget::resizeEvent(qre);
+
+ // resizing changes our own sizeHint(), because we must take the new PanelSize in account.
+ // So updateGeometry() is amust for us.
+ updateGeometry();
+}
+
+
+void ViewApplet::refreshVolumeLevels() {
+ //kdDebug(67100) << "ViewApplet::refreshVolumeLevels()\n";
+
+ QWidget *mdw = _mdws.first();
+ MixDevice* md;
+ for ( md = _mixSet->first(); md != 0; md = _mixSet->next() ) {
+ if ( mdw == 0 ) {
+ kdError(67100) << "ViewApplet::refreshVolumeLevels(): mdw == 0\n";
+ break; // sanity check (normally the lists are set up correctly)
+ }
+ else {
+ if ( mdw->inherits("MDWSlider")) {
+ //kdDebug(67100) << "ViewApplet::refreshVolumeLevels(): updating\n";
+ // a slider, fine. Lets update its value
+ static_cast<MDWSlider*>(mdw)->update();
+ }
+ else {
+ kdError(67100) << "ViewApplet::refreshVolumeLevels(): mdw is not slider\n";
+ // no slider. Cannot happen in theory => skip it
+ }
+ }
+ mdw = _mdws.next();
+ }
+}
+
+void ViewApplet::configurationUpdate() {
+ updateGeometry();
+ //_layoutMDW->activate();
+ constructionFinished(); // contains "_layoutMDW->activate();"
+ emit appletContentChanged();
+ kdDebug(67100) << "ViewApplet::configurationUpdate()" << endl;
+ // the following "emit" is only here to be picked up by KMixApplet, because it has to
+ // - make sure the panel is informed about the size change
+ // - save the new configuration
+ //emit configurationUpdated();
+}
+
+#include "viewapplet.moc"
diff --git a/kmix/viewapplet.h b/kmix/viewapplet.h
new file mode 100644
index 00000000..a03d90e2
--- /dev/null
+++ b/kmix/viewapplet.h
@@ -0,0 +1,47 @@
+#ifndef ViewApplet_h
+#define ViewApplet_h
+
+#include "viewbase.h"
+#include <kpanelapplet.h>
+
+class QBoxLayout;
+class QHBox;
+class QSize;
+
+class Mixer;
+
+class ViewApplet : public ViewBase
+{
+ Q_OBJECT
+public:
+ ViewApplet(QWidget* parent, const char* name, Mixer* mixer, ViewBase::ViewFlags vflags, KPanelApplet::Position pos);
+ ~ViewApplet();
+
+ virtual int count();
+ virtual int advice();
+ virtual void setMixSet(MixSet *mixset);
+ virtual QWidget* add(MixDevice *mdw);
+ virtual void constructionFinished();
+ virtual void configurationUpdate();
+
+ QSize sizeHint() const;
+ QSizePolicy sizePolicy() const;
+ virtual void resizeEvent(QResizeEvent*);
+
+signals:
+ void appletContentChanged();
+
+public slots:
+ virtual void refreshVolumeLevels();
+
+private:
+ bool shouldShowIcons(QSize);
+ QBoxLayout* _layoutMDW;
+ // Position of the applet (pLeft, pRight, pTop, pBottom)
+ //KPanelApplet::Position _KMIXposition;
+ // Orientation of the applet (horizontal or vertical)
+ Qt::Orientation _viewOrientation;
+};
+
+#endif
+
diff --git a/kmix/viewbase.cpp b/kmix/viewbase.cpp
new file mode 100644
index 00000000..af4a9aa5
--- /dev/null
+++ b/kmix/viewbase.cpp
@@ -0,0 +1,187 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "viewbase.h"
+
+// QT
+#include <qlabel.h>
+#include <qcursor.h>
+
+// KDE
+#include <kaction.h>
+#include <kpopupmenu.h>
+#include <klocale.h>
+#include <kiconloader.h>
+
+// KMix
+#include "dialogviewconfiguration.h"
+#include "mixdevicewidget.h"
+#include "mixer.h"
+
+
+ViewBase::ViewBase(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, WFlags f, ViewBase::ViewFlags vflags)
+ : QWidget(parent, name, f), _vflags(vflags), _caption(caption)
+{
+ _mixer = mixer;
+ _mixSet = new MixSet();
+
+ /* Can't use the following construct:
+ setMixSet( & mixer->getMixSet());
+ C++ does not use overloaded methods like getMixSet() as long as the constructor has not completed :-(((
+ */
+ _actions = new KActionCollection( this );
+
+ // Plug in the "showMenubar" action, if the caller wants it. Typically this is only neccesary for views in the KMix main window.
+ if ( vflags & ViewBase::HasMenuBar ) {
+ KToggleAction *m = static_cast<KToggleAction*>(KStdAction::showMenubar( this, SLOT(toggleMenuBarSlot()), _actions ));
+ if ( vflags & ViewBase::MenuBarVisible ) {
+ m->setChecked(true);
+ }
+ else {
+ m->setChecked(false);
+ }
+ }
+ new KAction(i18n("&Channels"), 0, this, SLOT(configureView()), _actions, "toggle_channels");
+ connect ( _mixer, SIGNAL(newVolumeLevels()), this, SLOT(refreshVolumeLevels()) );
+}
+
+ViewBase::~ViewBase() {
+ delete _mixSet;
+}
+
+void ViewBase::init() {
+ const MixSet& mixset = _mixer->getMixSet();
+ setMixSet( const_cast<MixSet*>(&mixset)); // const_cast<>
+}
+
+void ViewBase::setMixSet(MixSet *)
+{
+ // do nothing. Subclasses can do something if they feel like it
+}
+
+/**
+ * Dummy implementation for add().
+ */
+QWidget* ViewBase::add(MixDevice* mdw) {
+ QWidget* label = new QLabel( mdw->name(), this, mdw->name().latin1());
+ label->move(0, mdw->num()*12);
+ return label;
+}
+
+void ViewBase::configurationUpdate() {
+}
+
+/**
+ * Create all widgets.
+ * This is a loop over all supported devices of the corresponding view.
+ * On each device add() is called - the derived class must implement add() for creating and placing
+ * the real MixDeviceWidget.
+ * The added MixDeviceWidget is appended to the _mdws list.
+ */
+void ViewBase::createDeviceWidgets()
+{
+ // create devices
+ MixDevice *mixDevice;
+ for ( mixDevice = _mixSet->first(); mixDevice != 0; mixDevice = _mixSet->next())
+ {
+ QWidget* mdw = add(mixDevice);
+ _mdws.append(mdw);
+ }
+ // allow view to "polish" itself
+ constructionFinished();
+}
+
+// ---------- Popup stuff START ---------------------
+void ViewBase::mousePressEvent( QMouseEvent *e )
+{
+ if ( e->button()==RightButton )
+ showContextMenu();
+}
+
+/**
+ * Return a popup menu. This contains basic entries.
+ * More can be added by the caller.
+ */
+KPopupMenu* ViewBase::getPopup()
+{
+ popupReset();
+ return _popMenu;
+}
+
+void ViewBase::popupReset()
+{
+ KAction *a;
+
+ _popMenu = new KPopupMenu( this );
+ _popMenu->insertTitle( SmallIcon( "kmix" ), i18n("Device Settings") );
+
+ a = _actions->action( "toggle_channels" );
+ if ( a ) a->plug( _popMenu );
+
+ a = _actions->action( "options_show_menubar" );
+ if ( a ) a->plug( _popMenu );
+}
+
+
+/**
+ This will only get executed, when the user has removed all items from the view.
+ Don't remove this method, because then the user cannot get a menu for getting his
+ channels back
+*/
+void ViewBase::showContextMenu()
+{
+ //kdDebug(67100) << "ViewBase::showContextMenu()" << endl;
+ popupReset();
+
+ QPoint pos = QCursor::pos();
+ _popMenu->popup( pos );
+}
+
+
+void ViewBase::refreshVolumeLevels()
+{
+ // is virtual
+}
+
+Mixer* ViewBase::getMixer() {
+ return _mixer;
+}
+
+/**
+ * Open the View configuration dialog. The user can select which channels he wants
+ * to see and which not.
+ */
+void ViewBase::configureView() {
+
+ DialogViewConfiguration* dvc = new DialogViewConfiguration(0, *this);
+ dvc->show();
+ // !! The dialog is modal. Does it delete itself?
+}
+
+void ViewBase::toggleMenuBarSlot() {
+ //kdDebug(67100) << "ViewBase::toggleMenuBarSlot() start\n";
+ emit toggleMenuBar();
+ //kdDebug(67100) << "ViewBase::toggleMenuBarSlot() done\n";
+}
+
+// ---------- Popup stuff END ---------------------
+
+#include "viewbase.moc"
diff --git a/kmix/viewbase.h b/kmix/viewbase.h
new file mode 100644
index 00000000..50d92b10
--- /dev/null
+++ b/kmix/viewbase.h
@@ -0,0 +1,121 @@
+#ifndef ViewBase_h
+#define ViewBase_h
+
+// QT
+#include "qwidget.h"
+
+// KDE
+class KActionCollection;
+class KPopupMenu;
+class MixSet;
+class Mixer;
+class MixDevice;
+
+/**
+ * The ViewBase is a virtual base class, to be used for subclassing the real Mixer Views.
+ */
+class ViewBase : public QWidget
+{
+ Q_OBJECT
+public:
+
+ typedef uint ViewFlags;
+ enum ViewFlagsEnum {
+ // Regular flags
+ HasMenuBar = 0x0001,
+ MenuBarVisible = 0x0002,
+ Horizontal = 0x0004,
+ Vertical = 0x0008,
+ // Experimental flags
+ Experimental_SurroundView = 0x1000,
+ Experimental_GridView = 0x2000
+ };
+
+ ViewBase(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, WFlags=0, ViewFlags vflags=0);
+ virtual ~ViewBase();
+
+ // Subclasses must define this method. It is called by the ViewBase() constuctor.
+ // The view class must initialize here the _mixSet. This will normally be a subset
+ // of the passed mixset.
+ // After that the subclass must be prepared for
+ // being fed MixDevice's via the add() method.
+ virtual void setMixSet(MixSet *mixset);
+
+ // Returns the number of accepted MixDevice's from setMixerSet(). This is
+ // normally smaller that mixset->count(), except when the class creates virtual
+ // devices
+ virtual int count() = 0;
+ // returns an advice about whether this view should be used at all. The returned
+ // value is a percentage (0-100). A view without accepted devices would return 0,
+ // a "3D sound View" would return 75, if all vital but some optional devices are
+ // not available.
+ virtual int advice() = 0;
+
+ // This method is called by ViewBase at the end of createDeviceWidgets(). The default
+ // implementation does nothing. Subclasses can override this method for doing final
+ // touches. This is very much like polish(), but called at an exactly well-known time.
+ // Also I do not want Views to interfere with polish()
+ virtual void constructionFinished() = 0;
+
+ // This method is called after a configuration update (in other words: after the user
+ // has clicked "OK" on the "show/hide" configuration dialog. The default implementation
+ // does nothing.
+ virtual void configurationUpdate();
+
+ /**
+ * Creates the widgets for all supported devices. The default implementation loops
+ * over the supported MixDevice's and calls add() for each of it.
+ */
+ virtual void createDeviceWidgets();
+
+ /**
+ * Creates a suitable representation for the given MixDevice.
+ * The default implementation creates a label
+ */
+ virtual QWidget* add(MixDevice *);
+
+ /**
+ * Popup stuff
+ */
+ virtual KPopupMenu* getPopup();
+ virtual void popupReset();
+ virtual void showContextMenu();
+
+ Mixer* getMixer();
+
+ /**
+ * Contains the widgets for the _mixSet. There is a 1:1 relationship, which means:
+ * _mdws[i] is the Widget for the MixDevice _mixSet[i].
+ * Hint: The new ViewSurround class shows that a 1:1 relationship does not work in a general scenario.
+ * I actually DID expect this. The solution is unclear yet, probably there will be a virtual mapper method.
+ */
+ QPtrList<QWidget> _mdws; // this obsoletes the former Channel class
+
+ QString caption() const { return _caption; }
+
+protected:
+ void init();
+
+ Mixer *_mixer;
+ MixSet *_mixSet;
+ KPopupMenu *_popMenu;
+ KActionCollection* _actions;
+ ViewFlags _vflags;
+
+public slots:
+ virtual void refreshVolumeLevels();
+ virtual void configureView();
+ void toggleMenuBarSlot();
+
+protected slots:
+ void mousePressEvent( QMouseEvent *e );
+
+signals:
+ void toggleMenuBar();
+
+private:
+ QString _caption;
+};
+
+#endif
+
diff --git a/kmix/viewdockareapopup.cpp b/kmix/viewdockareapopup.cpp
new file mode 100644
index 00000000..b08138f7
--- /dev/null
+++ b/kmix/viewdockareapopup.cpp
@@ -0,0 +1,206 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "viewdockareapopup.h"
+
+// Qt
+#include <qwidget.h>
+#include <qevent.h>
+#include <qlayout.h>
+#include <qframe.h>
+#include <qpushbutton.h>
+#include <qdatetime.h>
+
+// KDE
+#include <kdebug.h>
+#include <kaction.h>
+#include <kapplication.h>
+#include <klocale.h>
+
+// KMix
+#include "mdwslider.h"
+#include "mixer.h"
+#include "kmixdockwidget.h"
+
+// !! Do NOT remove or mask out "WType_Popup"
+// Users will not be able to close the Popup without opening the KMix main window then.
+// See Bug #93443, #96332 and #96404 for further details. -- esken
+ViewDockAreaPopup::ViewDockAreaPopup(QWidget* parent, const char* name, Mixer* mixer, ViewBase::ViewFlags vflags, KMixDockWidget *dockW )
+ : ViewBase(parent, name, QString::null, mixer, WStyle_Customize | WType_Popup | Qt::WStyle_DialogBorder, vflags), _mdw(0), _dock(dockW)
+{
+ QBoxLayout *layout = new QHBoxLayout( this );
+ _frame = new QFrame( this );
+ layout->addWidget( _frame );
+
+ _frame->setFrameStyle( QFrame::PopupPanel | QFrame::Raised );
+ _frame->setLineWidth( 1 );
+
+ _layoutMDW = new QGridLayout( _frame, 1, 1, 2, 1, "KmixPopupLayout" );
+ _hideTimer = new QTime();
+ init();
+}
+
+ViewDockAreaPopup::~ViewDockAreaPopup() {
+}
+
+
+
+void ViewDockAreaPopup::mousePressEvent(QMouseEvent *)
+{
+// kdDebug() << "Teste pres mouse" << endl;
+ /**
+ Hide the popup:
+ This should work automatically, when the user clicks outside the bounds of this popup:
+ Alas - it does not work.
+ Why it does not work, I do not know: this->isPopup() returns "true", so Qt should
+ properly take care of it in QWidget.
+ */
+ if ( ! this->hasMouse() ) {
+ _hideTimer->start();
+ hide(); // needed!
+ }
+ return;
+}
+
+bool ViewDockAreaPopup::justHidden()
+{
+ return _hideTimer->elapsed() < 300;
+}
+
+void ViewDockAreaPopup::wheelEvent ( QWheelEvent * e ) {
+ // Pass wheel event from "border widget" to child
+ if ( _mdw != 0 ) {
+ QApplication::sendEvent( _mdw, e);
+ }
+}
+
+MixDevice* ViewDockAreaPopup::dockDevice()
+{
+ return _dockDevice;
+}
+
+
+void ViewDockAreaPopup::showContextMenu()
+{
+ // no right-button-menu on "dock area popup"
+ return;
+}
+
+
+void ViewDockAreaPopup::setMixSet(MixSet *)
+{
+ // kdDebug(67100) << "ViewDockAreaPopup::setMixSet()\n";
+ // This implementation of setMixSet() is a bit "exotic". But I will leave it like this, until I implement
+ // a configuration option for "what device to show on the dock area"
+ _dockDevice = _mixer->masterDevice();
+ if ( _dockDevice == 0 ) {
+ // If we have no mixer device, we will take the first available mixer device
+ _dockDevice = (*_mixer)[0];
+ }
+ _mixSet->append(_dockDevice);
+}
+
+QWidget* ViewDockAreaPopup::add(MixDevice *md)
+{
+ _mdw =
+ new MDWSlider(
+ _mixer, // the mixer for this device
+ md, // only 1 device. This is actually _dockDevice
+ true, // Show Mute LED
+ false, // Show Record LED
+ false, // Small
+ Qt::Vertical, // Direction: only 1 device, so doesn't matter
+ _frame, // parent
+ 0, // Is "NULL", so that there is no RMB-popup
+ _dockDevice->name().latin1() );
+ _layoutMDW->addItem( new QSpacerItem( 5, 20 ), 0, 2 );
+ _layoutMDW->addItem( new QSpacerItem( 5, 20 ), 0, 0 );
+ _layoutMDW->addWidget( _mdw, 0, 1 );
+
+ // Add button to show main panel
+ _showPanelBox = new QPushButton( i18n("Mixer"), _frame, "MixerPanel" );
+ connect ( _showPanelBox, SIGNAL( clicked() ), SLOT( showPanelSlot() ) );
+ _layoutMDW->addMultiCellWidget( _showPanelBox, 1, 1, 0, 2 );
+
+ return _mdw;
+}
+
+int ViewDockAreaPopup::count()
+{
+ return ( _mixSet->count() );
+}
+
+int ViewDockAreaPopup::advice() {
+ if ( _dockDevice != 0 ) {
+ // I could also evaluate whether we have a "sensible" device available.
+ // For example
+ // 100 : "master volume"
+ // 100 : "PCM"
+ // 50 : "CD"
+ // 0 : all other devices
+ return 100;
+ }
+ else {
+ return 0;
+ }
+}
+
+QSize ViewDockAreaPopup::sizeHint() const {
+ // kdDebug(67100) << "ViewDockAreaPopup::sizeHint(): NewSize is " << _mdw->sizeHint() << "\n";
+ return( _mdw->sizeHint() );
+}
+
+void ViewDockAreaPopup::constructionFinished() {
+ // kdDebug(67100) << "ViewDockAreaPopup::constructionFinished()\n";
+
+ _mdw->move(0,0);
+ _mdw->show();
+ _mdw->resize(_mdw->sizeHint() );
+ resize(sizeHint());
+
+}
+
+
+void ViewDockAreaPopup::refreshVolumeLevels() {
+ // kdDebug(67100) << "ViewDockAreaPopup::refreshVolumeLevels()\n";
+ QWidget* mdw = _mdws.first();
+ if ( mdw == 0 ) {
+ kdError(67100) << "ViewDockAreaPopup::refreshVolumeLevels(): mdw == 0\n";
+ // sanity check (normally the lists are set up correctly)
+ }
+ else {
+ if ( mdw->inherits("MDWSlider")) {
+ static_cast<MDWSlider*>(mdw)->update();
+ }
+ else {
+ kdError(67100) << "ViewDockAreaPopup::refreshVolumeLevels(): mdw is not slider\n";
+ // no slider. Cannot happen in theory => skip it
+ }
+ }
+}
+
+void ViewDockAreaPopup::showPanelSlot() {
+ _dock->toggleActive();
+ _dock->_dockAreaPopup->hide();
+}
+
+#include "viewdockareapopup.moc"
+
diff --git a/kmix/viewdockareapopup.h b/kmix/viewdockareapopup.h
new file mode 100644
index 00000000..4205e831
--- /dev/null
+++ b/kmix/viewdockareapopup.h
@@ -0,0 +1,57 @@
+#ifndef ViewDockAreaPopup_h
+#define ViewDockAreaPopup_h
+
+#include "viewbase.h"
+
+class QMouseEvent;
+class QGridLayout;
+class QWidget;
+class QPushButton;
+
+class Mixer;
+class KMixDockWidget;
+class MixDeviceWidget;
+class MixDevice;
+class QFrame;
+class QTime;
+
+class ViewDockAreaPopup : public ViewBase
+{
+ Q_OBJECT
+public:
+ ViewDockAreaPopup(QWidget* parent, const char* name, Mixer* mixer, ViewBase::ViewFlags vflags, KMixDockWidget *dockW);
+ ~ViewDockAreaPopup();
+ MixDevice* dockDevice();
+
+ virtual int count();
+ virtual int advice();
+ virtual void setMixSet(MixSet *mixset);
+ virtual QWidget* add(MixDevice *mdw);
+ virtual void constructionFinished();
+ virtual void refreshVolumeLevels();
+ virtual void showContextMenu();
+
+ QSize sizeHint() const;
+ bool justHidden();
+
+protected:
+ MixDeviceWidget *_mdw;
+ KMixDockWidget *_dock;
+ MixDevice *_dockDevice;
+ QPushButton *_showPanelBox;
+
+ void mousePressEvent(QMouseEvent *e);
+ void wheelEvent ( QWheelEvent * e );
+
+private:
+ QGridLayout* _layoutMDW;
+ QFrame *_frame;
+ QTime *_hideTimer;
+
+private slots:
+ void showPanelSlot();
+
+};
+
+#endif
+
diff --git a/kmix/viewgrid.cpp b/kmix/viewgrid.cpp
new file mode 100644
index 00000000..0a995ac8
--- /dev/null
+++ b/kmix/viewgrid.cpp
@@ -0,0 +1,212 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "viewgrid.h"
+
+// Qt
+#include <qwidget.h>
+
+// KDE
+#include <kdebug.h>
+
+// KMix
+#include "mdwenum.h"
+#include "mdwslider.h"
+#include "mdwswitch.h"
+#include "mixer.h"
+
+/**
+ */
+ViewGrid::ViewGrid(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags)
+ : ViewBase(parent, name, caption, mixer, WStyle_Customize|WStyle_NoBorder, vflags)
+{
+ m_spacingHorizontal = 5;
+ m_spacingVertical = 5;
+
+ if ( _vflags & ViewBase::Vertical ) {
+ //_layoutMDW = new QVBoxLayout(this);
+ }
+ else {
+ //_layoutMDW = new QHBoxLayout(this);
+ }
+ init();
+}
+
+ViewGrid::~ViewGrid() {
+}
+
+void ViewGrid::setMixSet(MixSet *mixset)
+{
+ MixDevice* md;
+ int testCounter = 0;
+ for ( md = mixset->first(); md != 0; md = mixset->next() ) {
+ if (testCounter<8) {
+ _mixSet->append(md);
+ }
+ testCounter++;
+ }
+}
+
+int ViewGrid::count()
+{
+ return ( _mixSet->count() );
+}
+
+int ViewGrid::advice() {
+ if ( _mixSet->count() > 0 ) {
+ // The standard input and output views are always advised, if there are devices in it
+ return 100;
+ }
+ else {
+ return 0;
+ }
+}
+
+QWidget* ViewGrid::add(MixDevice *md)
+{
+ MixDeviceWidget *mdw = 0;
+ if ( md->isEnum() ) {
+ Qt::Orientation orientation = (_vflags & ViewBase::Vertical) ? Qt::Horizontal : Qt::Vertical;
+ mdw = new MDWEnum(
+ _mixer, // the mixer for this device
+ md, // MixDevice (parameter)
+ orientation, // Orientation
+ this, // parent
+ this, // View widget
+ md->name().latin1()
+ );
+ } // an enum
+ else if (md->isSwitch()) {
+ Qt::Orientation orientation = (_vflags & ViewBase::Vertical) ? Qt::Horizontal : Qt::Vertical;
+ mdw =
+ new MDWSwitch(
+ _mixer, // the mixer for this device
+ md, // MixDevice (parameter)
+ false, // Small
+ orientation, // Orientation
+ this, // parent
+ this, // View widget
+ md->name().latin1()
+ );
+ } // a switch
+
+ else { // must be a slider
+ Qt::Orientation orientation = (_vflags & ViewBase::Vertical) ? Qt::Horizontal : Qt::Vertical;
+ mdw =
+ new MDWSlider(
+ _mixer, // the mixer for this device
+ md, // MixDevice (parameter)
+ true, // Show Mute LED
+ true, // Show Record LED
+ false, // Small
+ orientation, // Orientation
+ this, // parent
+ this, // View widget
+ md->name().latin1()
+ );
+ }
+ return mdw;
+}
+
+QSize ViewGrid::sizeHint() const {
+ // kdDebug(67100) << "ViewGrid::sizeHint(): NewSize is " << _layoutMDW->sizeHint() << "\n";
+ return( m_sizeHint);
+}
+
+void ViewGrid::constructionFinished() {
+ //_layoutMDW->activate();
+
+ // do a manual layout
+ configurationUpdate();
+}
+
+void ViewGrid::refreshVolumeLevels() {
+ // kdDebug(67100) << "ViewGrid::refreshVolumeLevels()\n";
+
+ m_sizeHint.setWidth (0);
+ m_sizeHint.setHeight(0);
+
+ m_testingX = 0;
+ m_testingY = 0;
+
+ QWidget *mdw = _mdws.first();
+ MixDevice* md;
+ for ( md = _mixSet->first(); md != 0; md = _mixSet->next() ) {
+ if ( mdw == 0 ) {
+ kdError(67100) << "ViewGrid::refreshVolumeLevels(): mdw == 0\n";
+ break; // sanity check (normally the lists are set up correctly)
+ }
+ else {
+ if ( mdw->inherits("MDWSlider")) {
+ //kdDebug(67100) << "ViewGrid::refreshVolumeLevels(): updating\n";
+ // a slider, fine. Lets update its value
+ static_cast<MDWSlider*>(mdw)->update();
+ }
+ else if ( mdw->inherits("MDWSwitch")) {
+ //kdDebug(67100) << "ViewGrid::refreshVolumeLevels(): updating\n";
+ // a slider, fine. Lets update its value
+ static_cast<MDWSwitch*>(mdw)->update();
+ }
+ else if ( mdw->inherits("MDWEnum")) {
+ static_cast<MDWEnum*>(mdw)->update();
+ }
+ else {
+ kdError(67100) << "ViewGrid::refreshVolumeLevels(): mdw is unknown/unsupported type\n";
+ // no slider. Cannot happen in theory => skip it
+ }
+ }
+ mdw = _mdws.next();
+ }
+}
+
+/**
+ This implementation makes sure the Grid's geometry is updated
+ after hiding/showing channels.
+*/
+void ViewGrid::configurationUpdate() {
+ m_sizeHint.setWidth (0);
+ m_sizeHint.setHeight(0);
+
+ m_testingX = 0;
+ m_testingY = 0;
+
+ for (QWidget *qw = _mdws.first(); qw !=0; qw = _mdws.next() ) {
+
+ if ( qw->inherits("MixDeviceWidget")) {
+ MixDeviceWidget* mdw = static_cast<MixDeviceWidget*>(qw);
+ int xPos = m_testingX * m_spacingHorizontal;
+ int yPos = m_testingY * m_spacingVertical ;
+ mdw->move( xPos, yPos );
+ mdw->resize( mdw->sizeHint() );
+ int xMax = xPos + mdw->width() ; if ( xMax > m_sizeHint.width() ) m_sizeHint.setWidth(xMax);
+ int yMax = yPos + mdw->height(); if ( yMax > m_sizeHint.height() ) m_sizeHint.setHeight(yMax);
+
+ m_testingX += 5;
+ if ( m_testingX > 50 ) {
+ m_testingY += 10;
+ m_testingX = 0;
+ }
+ } // inherits MixDeviceWidget
+ } // for all MDW's
+}
+
+
+#include "viewgrid.moc"
diff --git a/kmix/viewgrid.h b/kmix/viewgrid.h
new file mode 100644
index 00000000..f57cdc59
--- /dev/null
+++ b/kmix/viewgrid.h
@@ -0,0 +1,42 @@
+#ifndef ViewGrid_h
+#define ViewGrid_h
+
+class QBoxLayout;
+#include "qsize.h"
+class QWidget;
+
+class Mixer;
+#include "viewbase.h"
+
+class ViewGrid : public ViewBase
+{
+ Q_OBJECT
+public:
+ ViewGrid(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags);
+ ~ViewGrid();
+
+ virtual int count();
+ virtual int advice();
+ virtual void setMixSet(MixSet *mixset);
+ virtual QWidget* add(MixDevice *mdw);
+ virtual void configurationUpdate();
+ virtual void constructionFinished();
+
+ QSize sizeHint() const;
+
+public slots:
+ virtual void refreshVolumeLevels();
+
+private:
+ unsigned int m_spacingHorizontal;
+ unsigned int m_spacingVertical;
+
+ // m_maxX and m_maxY are the highest coordiantes encountered
+ QSize m_sizeHint;
+
+ unsigned int m_testingX;
+ unsigned int m_testingY;
+};
+
+#endif
+
diff --git a/kmix/viewinput.cpp b/kmix/viewinput.cpp
new file mode 100644
index 00000000..646e94d6
--- /dev/null
+++ b/kmix/viewinput.cpp
@@ -0,0 +1,50 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "viewinput.h"
+#include <qwidget.h>
+
+#include "mixer.h"
+#include "mixdevicewidget.h"
+
+ViewInput::ViewInput(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags)
+ : ViewSliders(parent, name, caption, mixer, vflags)
+{
+ init();
+ connect ( _mixer, SIGNAL(newRecsrc()) , this, SLOT(refreshVolumeLevels()) ); // only the input widget has record sources
+}
+
+ViewInput::~ViewInput() {
+}
+
+void ViewInput::setMixSet(MixSet *mixset)
+{
+ MixDevice* md;
+ for ( md = mixset->first(); md != 0; md = mixset->next() ) {
+ if ( md->isRecordable() && ! md->isSwitch() && ! md->isEnum() ) {
+ _mixSet->append(md);
+ }
+ else {
+ }
+ }
+}
+
+#include "viewinput.moc"
diff --git a/kmix/viewinput.h b/kmix/viewinput.h
new file mode 100644
index 00000000..1b5d5be8
--- /dev/null
+++ b/kmix/viewinput.h
@@ -0,0 +1,19 @@
+#ifndef ViewInput_h
+#define ViewInput_h
+
+#include "viewsliders.h"
+class QWidget;
+class Mixer;
+
+class ViewInput : public ViewSliders
+{
+ Q_OBJECT
+public:
+ ViewInput(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags);
+ ~ViewInput();
+
+ virtual void setMixSet(MixSet *mixset);
+};
+
+#endif
+
diff --git a/kmix/viewoutput.cpp b/kmix/viewoutput.cpp
new file mode 100644
index 00000000..81c850b1
--- /dev/null
+++ b/kmix/viewoutput.cpp
@@ -0,0 +1,50 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "viewoutput.h"
+#include <qwidget.h>
+
+#include "mixer.h"
+#include "mixdevicewidget.h"
+
+ViewOutput::ViewOutput(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags)
+ : ViewSliders(parent, name, caption, mixer, vflags)
+{
+ init();
+}
+
+ViewOutput::~ViewOutput() {
+}
+
+void ViewOutput::setMixSet(MixSet *mixset)
+{
+ MixDevice* md;
+ for ( md = mixset->first(); md != 0; md = mixset->next() ) {
+ if ( ! md->isRecordable() && ! md->isSwitch() && ! md->isEnum()) {
+ _mixSet->append(md);
+ }
+ else {
+ }
+ }
+}
+
+
+#include "viewoutput.moc"
diff --git a/kmix/viewoutput.h b/kmix/viewoutput.h
new file mode 100644
index 00000000..439aa7d8
--- /dev/null
+++ b/kmix/viewoutput.h
@@ -0,0 +1,19 @@
+#ifndef ViewOutput_h
+#define ViewOutput_h
+
+#include "viewsliders.h"
+class QWidget;
+class Mixer;
+
+class ViewOutput : public ViewSliders
+{
+ Q_OBJECT
+public:
+ ViewOutput(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags);
+ ~ViewOutput();
+
+ virtual void setMixSet(MixSet *mixset);
+};
+
+#endif
+
diff --git a/kmix/viewsliders.cpp b/kmix/viewsliders.cpp
new file mode 100644
index 00000000..cb9b441e
--- /dev/null
+++ b/kmix/viewsliders.cpp
@@ -0,0 +1,140 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "viewsliders.h"
+
+// Qt
+#include <qlayout.h>
+#include <qwidget.h>
+
+// KDE
+#include <kdebug.h>
+
+// KMix
+#include "mdwslider.h"
+#include "mixer.h"
+
+/**
+ * Don't instanciate objects of this class directly. It won't work
+ * correctly because init() does not get called.
+ * See ViewInput and ViewOutput for "real" implementations.
+ */
+ViewSliders::ViewSliders(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags)
+ : ViewBase(parent, name, caption, mixer, WStyle_Customize|WStyle_NoBorder, vflags)
+{
+ if ( _vflags & ViewBase::Vertical ) {
+ _layoutMDW = new QVBoxLayout(this);
+ }
+ else {
+ _layoutMDW = new QHBoxLayout(this);
+ }
+ /*
+ * Do not call init(). Call init() only for "end usage" classes.
+ * Otherwise setMixSet() will be called multiple times.
+ * Yes, this is rotten ... I will think of something smart later !!
+ * Perhaps I can have a boolean "init-has-run" instance variable.
+ */
+ //init();
+}
+
+ViewSliders::~ViewSliders() {
+}
+
+void ViewSliders::setMixSet(MixSet *mixset)
+{
+ MixDevice* md;
+ for ( md = mixset->first(); md != 0; md = mixset->next() ) {
+ if ( (! md->isSwitch()) && ( ! md->isEnum() ) ) {
+ _mixSet->append(md);
+ }
+ }
+}
+
+int ViewSliders::count()
+{
+ return ( _mixSet->count() );
+}
+
+int ViewSliders::advice() {
+ if ( _mixSet->count() > 0 ) {
+ // The standard input and output views are always advised, if there are devices in it
+ return 100;
+ }
+ else {
+ return 0;
+ }
+}
+
+QWidget* ViewSliders::add(MixDevice *md)
+{
+ Qt::Orientation orientation = (_vflags & ViewBase::Vertical) ? Qt::Horizontal : Qt::Vertical;
+ MixDeviceWidget *mdw =
+ new MDWSlider(
+ _mixer, // the mixer for this device
+ md, // MixDevice (parameter)
+ true, // Show Mute LED
+ true, // Show Record LED
+ false, // Small
+ orientation, // Orientation
+ this, // parent
+ this, // View widget
+ md->name().latin1()
+ );
+ _layoutMDW->add(mdw);
+ return mdw;
+}
+
+QSize ViewSliders::sizeHint() const {
+ // kdDebug(67100) << "ViewSliders::sizeHint(): NewSize is " << _layoutMDW->sizeHint() << "\n";
+ return( _layoutMDW->sizeHint() );
+}
+
+void ViewSliders::constructionFinished() {
+ _layoutMDW->activate();
+}
+
+void ViewSliders::refreshVolumeLevels() {
+ // kdDebug(67100) << "ViewSliders::refreshVolumeLevels()\n";
+
+ QWidget *mdw = _mdws.first();
+ MixDevice* md;
+ for ( md = _mixSet->first(); md != 0; md = _mixSet->next() ) {
+ if ( mdw == 0 ) {
+ kdError(67100) << "ViewSliders::refreshVolumeLevels(): mdw == 0\n";
+ break; // sanity check (normally the lists are set up correctly)
+ }
+ else {
+ if ( mdw->inherits("MDWSlider")) {
+ //kdDebug(67100) << "ViewSliders::refreshVolumeLevels(): updating\n";
+ // a slider, fine. Lets update its value
+ static_cast<MDWSlider*>(mdw)->update();
+ }
+ else {
+ kdError(67100) << "ViewSliders::refreshVolumeLevels(): mdw is not slider\n";
+ // no slider. Cannot happen in theory => skip it
+ }
+ }
+ mdw = _mdws.next();
+ }
+}
+
+
+#include "viewsliders.moc"
diff --git a/kmix/viewsliders.h b/kmix/viewsliders.h
new file mode 100644
index 00000000..ea108ee3
--- /dev/null
+++ b/kmix/viewsliders.h
@@ -0,0 +1,33 @@
+#ifndef ViewSliders_h
+#define ViewSliders_h
+
+class QBoxLayout;
+class QWidget;
+
+class Mixer;
+#include "viewbase.h"
+
+class ViewSliders : public ViewBase
+{
+ Q_OBJECT
+public:
+ ViewSliders(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags);
+ ~ViewSliders();
+
+ virtual int count();
+ virtual int advice();
+ virtual void setMixSet(MixSet *mixset);
+ virtual QWidget* add(MixDevice *mdw);
+ virtual void constructionFinished();
+
+ QSize sizeHint() const;
+
+public slots:
+ virtual void refreshVolumeLevels();
+
+private:
+ QBoxLayout* _layoutMDW;
+};
+
+#endif
+
diff --git a/kmix/viewsurround.cpp b/kmix/viewsurround.cpp
new file mode 100644
index 00000000..a5eb7068
--- /dev/null
+++ b/kmix/viewsurround.cpp
@@ -0,0 +1,270 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "viewsurround.h"
+
+// Qt
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qwidget.h>
+
+// KDE
+#include <kdebug.h>
+#include <kiconloader.h>
+
+// KMix
+#include "kmixtoolbox.h"
+#include "mdwslider.h"
+#include "mixer.h"
+
+/**
+ * Demonstration verion of a "surround view"
+ * Not really usable right now.
+ */
+ViewSurround::ViewSurround(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags)
+ : ViewBase(parent, name, caption, mixer, WStyle_Customize|WStyle_NoBorder, vflags)
+{
+ _mdSurroundFront = 0;
+ _mdSurroundBack = 0;
+ _layoutMDW = new QHBoxLayout(this);
+ _layoutMDW->setMargin(8);
+ // Create switch buttonGroup
+ if ( _vflags & ViewBase::Vertical ) {
+ _layoutSliders = new QVBoxLayout(_layoutMDW);
+ }
+ else {
+ _layoutSliders = new QHBoxLayout(_layoutMDW);
+ }
+ _layoutSurround = new QGridLayout(_layoutMDW,3,5);
+ // _layoutMDW->setMargin(8);
+ init();
+}
+
+ViewSurround::~ViewSurround() {
+}
+
+void ViewSurround::setMixSet(MixSet *mixset)
+{
+ MixDevice* md;
+ for ( md = mixset->first(); md != 0; md = mixset->next() ) {
+ if ( ! md->isSwitch() ) {
+ switch ( md->type() ) {
+ case MixDevice::VOLUME:
+ case MixDevice::SURROUND:
+ case MixDevice::SURROUND_BACK:
+ case MixDevice::SURROUND_LFE:
+ case MixDevice::SURROUND_CENTERFRONT:
+ case MixDevice::SURROUND_CENTERBACK:
+ case MixDevice::AC97:
+ _mixSet->append(md);
+ break;
+ default:
+ // we are not interested in other channels
+ break;
+ } // switch(type)
+ } // !is_switch()
+ } // for
+}
+
+int ViewSurround::count()
+{
+ return ( _mixSet->count() );
+}
+
+int ViewSurround::advice() {
+ if ( _mixSet->count() > 0 ) {
+ // The standard input and output views are always advised, if there are devices in it
+ return 100;
+ }
+ else {
+ return 0;
+ }
+}
+
+QWidget* ViewSurround::add(MixDevice *md)
+{
+ bool small = false;
+ Qt::Orientation orientation = Qt::Vertical;
+ switch ( md->type() ) {
+ case MixDevice::VOLUME:
+ _mdSurroundFront = md;
+ small = true;
+ break;
+ case MixDevice::SURROUND_BACK:
+ _mdSurroundBack = md;
+ small = true;
+ break;
+ case MixDevice::SURROUND_LFE:
+ orientation = Qt::Horizontal;
+ small = true;
+ break;
+ case MixDevice::SURROUND_CENTERFRONT:
+ orientation = Qt::Horizontal;
+ small = true;
+ break;
+ case MixDevice::SURROUND_CENTERBACK:
+ orientation = Qt::Horizontal;
+ small = true;
+ break;
+
+ default:
+ small = false;
+ // these are the sliders on the left side of the surround View
+ orientation = (_vflags & ViewBase::Vertical) ? Qt::Horizontal : Qt::Vertical;
+ } // switch(type)
+
+ MixDeviceWidget *mdw = createMDW(md, small, orientation);
+
+ switch ( md->type() ) {
+ case MixDevice::VOLUME:
+ _layoutSurround->addWidget(mdw ,0,0, Qt::AlignBottom | Qt::AlignLeft);
+ break;
+
+ case MixDevice::SURROUND_BACK:
+ _layoutSurround->addWidget(mdw ,2,0, Qt::AlignTop | Qt::AlignLeft);
+ break;
+ case MixDevice::SURROUND_LFE:
+ _layoutSurround->addWidget(mdw,1,3, Qt::AlignVCenter | Qt::AlignRight ); break;
+ break;
+ case MixDevice::SURROUND_CENTERFRONT:
+ _layoutSurround->addWidget(mdw,0,2, Qt::AlignTop | Qt::AlignHCenter); break;
+ break;
+ case MixDevice::SURROUND_CENTERBACK:
+ _layoutSurround->addWidget(mdw,2,2, Qt::AlignBottom | Qt::AlignHCenter); break;
+ break;
+
+ case MixDevice::SURROUND:
+ case MixDevice::AC97:
+ default:
+ // Add as slider to the layout on the left side
+ _layoutSliders->add(mdw);
+ break;
+ } // switch(type)
+
+ return mdw;
+}
+
+QSize ViewSurround::sizeHint() const {
+ // kdDebug(67100) << "ViewSurround::sizeHint(): NewSize is " << _layoutMDW->sizeHint() << "\n";
+ return( _layoutMDW->sizeHint() );
+}
+
+void ViewSurround::constructionFinished() {
+ QLabel* personLabel = new QLabel("Listener", this);
+ QPixmap icon = UserIcon( "Listener" );
+ if ( ! icon.isNull()) personLabel->setPixmap(icon);
+ personLabel->setLineWidth( 4 );
+ personLabel->setMidLineWidth( 3 );
+ personLabel->setFrameStyle( QFrame::Panel | QFrame::Sunken );
+ int rowOfSpeaker = 0;
+ if ( _mdSurroundBack != 0 ) {
+ // let the speaker "sit" in the rear of the room, if there is
+ // rear speaker support in this sound card
+ rowOfSpeaker = 1;
+ }
+ _layoutSurround->addWidget(personLabel ,rowOfSpeaker, 2, Qt::AlignHCenter | Qt::AlignVCenter);
+
+ if ( _mdSurroundFront != 0 ) {
+ MixDeviceWidget *mdw = createMDW(_mdSurroundFront, true, Qt::Vertical);
+ _layoutSurround->addWidget(mdw,0,4, Qt::AlignBottom | Qt::AlignRight);
+ _mdws.append(mdw);
+
+ QLabel* speakerIcon = new QLabel("Speaker", this);
+ icon = UserIcon( "SpeakerFrontLeft" );
+ if ( ! icon.isNull()) speakerIcon->setPixmap(icon);
+ _layoutSurround->addWidget(speakerIcon,0,1, Qt::AlignTop | Qt::AlignLeft);
+
+ speakerIcon = new QLabel("Speaker", this);
+ icon = UserIcon( "SpeakerFrontRight" );
+ if ( ! icon.isNull()) speakerIcon->setPixmap(icon);
+ _layoutSurround->addWidget(speakerIcon,0,3, Qt::AlignTop | Qt::AlignRight);
+
+ }
+
+ if ( _mdSurroundBack != 0 ) {
+ MixDeviceWidget *mdw = createMDW(_mdSurroundBack, true, Qt::Vertical);
+ _layoutSurround->addWidget(mdw,2,4, Qt::AlignTop | Qt::AlignRight);
+ _mdws.append(mdw);
+
+ QLabel* speakerIcon = new QLabel("Speaker", this);
+ icon = UserIcon( "SpeakerRearLeft" );
+ if ( ! icon.isNull()) speakerIcon->setPixmap(icon);
+ _layoutSurround->addWidget(speakerIcon,2,1, Qt::AlignBottom | Qt::AlignLeft);
+
+ speakerIcon = new QLabel("Speaker", this);
+ icon = UserIcon( "SpeakerRearRight" );
+ if ( ! icon.isNull()) speakerIcon->setPixmap(icon);
+ _layoutSurround->addWidget(speakerIcon,2,3, Qt::AlignBottom | Qt::AlignRight);
+
+
+ }
+
+ // !! just for the demo version
+ KMixToolBox::setIcons (_mdws, true);
+ KMixToolBox::setLabels(_mdws, true);
+ KMixToolBox::setTicks (_mdws, true);
+
+ _layoutMDW->activate();
+}
+
+void ViewSurround::refreshVolumeLevels() {
+ // kdDebug(67100) << "ViewSurround::refreshVolumeLevels()\n";
+
+ QWidget *mdw = _mdws.first();
+ MixDevice* md;
+ for ( md = _mixSet->first(); md != 0; md = _mixSet->next() ) {
+ if ( mdw == 0 ) {
+ kdError(67100) << "ViewSurround::refreshVolumeLevels(): mdw == 0\n";
+ break; // sanity check (normally the lists are set up correctly)
+ }
+ else {
+ if ( mdw->inherits("MDWSlider")) {
+ //kdDebug(67100) << "ViewSurround::refreshVolumeLevels(): updating\n";
+ // a slider, fine. Lets update its value
+ static_cast<MDWSlider*>(mdw)->update();
+ }
+ else {
+ kdError(67100) << "ViewSurround::refreshVolumeLevels(): mdw is not slider\n";
+ // no slider. Cannot happen in theory => skip it
+ }
+ }
+ mdw = _mdws.next();
+ }
+}
+
+
+MixDeviceWidget* ViewSurround::createMDW(MixDevice *md, bool small, Qt::Orientation orientation)
+{
+ MixDeviceWidget* mdw = new MDWSlider(
+ _mixer, // the mixer for this device
+ md, // MixDevice (parameter)
+ false, // Show Mute LED
+ false, // Show Record LED
+ small, // Small
+ orientation, // Orientation
+ this, // parent
+ this, // View widget
+ md->name().latin1()
+ );
+ return mdw;
+}
+
+#include "viewsurround.moc"
diff --git a/kmix/viewsurround.h b/kmix/viewsurround.h
new file mode 100644
index 00000000..1427f41d
--- /dev/null
+++ b/kmix/viewsurround.h
@@ -0,0 +1,42 @@
+#ifndef ViewSurround_h
+#define ViewSurround_h
+
+class QBoxLayout;
+class QGridLayout;
+class QWidget;
+
+class MixDevice;
+class MixDeviceWidget;
+class Mixer;
+#include "viewbase.h"
+
+class ViewSurround : public ViewBase
+{
+ Q_OBJECT
+public:
+ ViewSurround(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags);
+ ~ViewSurround();
+
+ virtual int count();
+ virtual int advice();
+ virtual void setMixSet(MixSet *mixset);
+ virtual QWidget* add(MixDevice *mdw);
+ virtual void constructionFinished();
+
+ QSize sizeHint() const;
+
+public slots:
+ virtual void refreshVolumeLevels();
+
+private:
+ MixDeviceWidget* createMDW(MixDevice *md, bool small, Qt::Orientation orientation);
+ MixDevice *_mdSurroundFront;
+ MixDevice *_mdSurroundBack;
+
+ QBoxLayout* _layoutMDW;
+ QBoxLayout* _layoutSliders;
+ QGridLayout* _layoutSurround;
+};
+
+#endif
+
diff --git a/kmix/viewswitches.cpp b/kmix/viewswitches.cpp
new file mode 100644
index 00000000..d5a9b60c
--- /dev/null
+++ b/kmix/viewswitches.cpp
@@ -0,0 +1,189 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "viewswitches.h"
+
+#include <qlayout.h>
+#include <qwidget.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include "mdwswitch.h"
+#include "mdwenum.h"
+#include "mixer.h"
+
+ViewSwitches::ViewSwitches(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags)
+ : ViewBase(parent, name, caption, mixer, 0, vflags)
+{
+ // Create switch buttonGroup
+ if ( _vflags & ViewBase::Vertical ) {
+ _layoutMDW = new QVBoxLayout(this);
+ _layoutSwitch = new QVBoxLayout(_layoutMDW);
+ _layoutEnum = new QVBoxLayout(_layoutMDW); // always vertical!
+ }
+ else {
+ _layoutMDW = new QHBoxLayout(this);
+ _layoutSwitch = new QHBoxLayout(_layoutMDW);
+ // Place enums right from the switches: This is done, so that there will be no
+ // ugly space on the left side, when no Switch is shown.
+ // Actually it is not really clear yet, why there is empty space at all: There are 0 items in
+ // the _layoutEnum, so it might be a sizeHint() or some other subtle layout issue.
+ _layoutEnum = new QVBoxLayout(_layoutMDW);
+ }
+ init();
+}
+
+ViewSwitches::~ViewSwitches() {
+}
+
+void ViewSwitches::setMixSet(MixSet *mixset)
+{
+ MixDevice* md;
+ for ( md = mixset->first(); md != 0; md = mixset->next() ) {
+ if ( md->isSwitch() || md->isEnum() ) {
+ _mixSet->append(md);
+ }
+ else {
+ }
+ }
+}
+
+
+int ViewSwitches::count()
+{
+ return ( _mixSet->count() );
+}
+
+int ViewSwitches::advice() {
+ if ( _mixSet->count() > 0 ) {
+ // The Switch Views is always advised, if there are devices in it
+ return 100;
+ }
+ else {
+ return 0;
+ }
+}
+
+QWidget* ViewSwitches::add(MixDevice *md)
+{
+ MixDeviceWidget *mdw;
+
+ if ( md->isEnum() ) {
+ Qt::Orientation orientation = (_vflags & ViewBase::Vertical) ? Qt::Horizontal : Qt::Vertical;
+ mdw = new MDWEnum(
+ _mixer, // the mixer for this device
+ md, // MixDevice (parameter)
+ orientation, // Orientation
+ this, // parent
+ this, // View widget
+ md->name().latin1()
+ );
+ _layoutEnum->add(mdw);
+ } // an enum
+ else {
+ // must be a switch
+ Qt::Orientation orientation = (_vflags & ViewBase::Vertical) ? Qt::Horizontal : Qt::Vertical;
+ mdw =
+ new MDWSwitch(
+ _mixer, // the mixer for this device
+ md, // MixDevice (parameter)
+ false, // Small
+ orientation, // Orientation
+ this, // parent
+ this, // View widget
+ md->name().latin1()
+ );
+ _layoutSwitch->add(mdw);
+ } // a switch
+
+ return mdw;
+}
+
+QSize ViewSwitches::sizeHint() const {
+ //kdDebug(67100) << "ViewSwitches::sizeHint(): NewSize is " << _layoutMDW->sizeHint() << "\n";
+ return( _layoutMDW->sizeHint() );
+}
+
+void ViewSwitches::constructionFinished() {
+ configurationUpdate(); // also does _layoutMDW->activate();
+}
+
+void ViewSwitches::refreshVolumeLevels() {
+ //kdDebug(67100) << "ViewSwitches::refreshVolumeLevels()\n";
+ QWidget *mdw = _mdws.first();
+ MixDevice* md;
+ for ( md = _mixSet->first(); md != 0; md = _mixSet->next() ) {
+ if ( mdw == 0 ) {
+ kdError(67100) << "ViewSwitches::refreshVolumeLevels(): mdw == 0\n";
+ break; // sanity check (normally the lists are set up correctly)
+ }
+ else {
+ if ( mdw->inherits("MDWSwitch")) {
+ //kdDebug(67100) << "ViewSwitches::refreshVolumeLevels(): updating\n";
+ // a slider, fine. Lets update its value
+ static_cast<MDWSwitch*>(mdw)->update();
+ }
+ else if ( mdw->inherits("MDWEnum")) {
+ static_cast<MDWEnum*>(mdw)->update();
+ }
+ else {
+ kdError(67100) << "ViewSwitches::refreshVolumeLevels(): mdw is not slider\n";
+ // no switch. Cannot happen in theory => skip it
+ // If I start putting other stuff in the switch tab, I will get a nice warning.
+ }
+ }
+ mdw = _mdws.next();
+ }
+}
+
+
+/**
+ This implementation makes sure the BackgroundMode's are properly updated
+ with their alternating colors after hiding/showing channels.
+*/
+void ViewSwitches::configurationUpdate() {
+ bool backGoundModeToggler = true;
+ for (QWidget *qw = _mdws.first(); qw !=0; qw = _mdws.next() ) {
+ if ( qw->inherits("MDWSwitch")) {
+ MixDeviceWidget* mdw = static_cast<MDWSwitch*>(qw);
+ if ( ! mdw->isDisabled() ) {
+ if ( backGoundModeToggler ) {
+ mdw->setBackgroundMode(PaletteBackground);
+ }
+ else {
+ // !! Should use KGlobalSettings::alternateBackgroundColor()
+ // or KGlobalSettings::calculateAlternateBackgroundColor() instead.
+ mdw->setBackgroundMode( PaletteBase );
+ }
+ backGoundModeToggler = !backGoundModeToggler;
+ } // ! isDisabled()
+ else {
+ //kdDebug(67100) << "ViewSwitches::configurationUpdate() ignoring diabled switch\n";
+ }
+ } // inherits("MDWSwitch")
+ }
+ _layoutMDW->activate();
+}
+
+
+#include "viewswitches.moc"
+
diff --git a/kmix/viewswitches.h b/kmix/viewswitches.h
new file mode 100644
index 00000000..043fce07
--- /dev/null
+++ b/kmix/viewswitches.h
@@ -0,0 +1,36 @@
+#ifndef ViewSwitches_h
+#define ViewSwitches_h
+
+class QLayout;
+class QWidget;
+
+class Mixer;
+#include "viewbase.h"
+
+class ViewSwitches : public ViewBase
+{
+ Q_OBJECT
+public:
+ ViewSwitches(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags);
+ ~ViewSwitches();
+
+ virtual int count();
+ virtual int advice();
+ virtual void setMixSet(MixSet *mixset);
+ virtual QWidget* add(MixDevice *mdw);
+ virtual void constructionFinished();
+ virtual void configurationUpdate();
+
+ QSize sizeHint() const;
+
+public slots:
+ virtual void refreshVolumeLevels();
+
+private:
+ QLayout* _layoutMDW;
+ QLayout* _layoutEnum;
+ QLayout* _layoutSwitch;
+};
+
+#endif
+
diff --git a/kmix/volume.cpp b/kmix/volume.cpp
new file mode 100644
index 00000000..20d056a4
--- /dev/null
+++ b/kmix/volume.cpp
@@ -0,0 +1,266 @@
+/*
+ * KMix -- KDE's full featured mini mixer
+ *
+ *
+ * Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// for operator<<()
+#include <iostream>
+
+#include <kdebug.h>
+
+#include "volume.h"
+
+
+int Volume::_channelMaskEnum[10] =
+ { MLEFT, MRIGHT, MCENTER,
+ MREARLEFT, MREARRIGHT, MWOOFER,
+ MLEFTREC , MRIGHTREC ,
+ MCUSTOM1, MCUSTOM2
+ };
+
+Volume::Volume( ChannelMask chmask, long maxVolume, long minVolume, bool isCapture )
+{
+ init(chmask, maxVolume, minVolume, isCapture);
+}
+
+
+// @ compatiblity constructor
+Volume::Volume( int channels, long maxVolume ) {
+ if (channels == 1 ) {
+ init(Volume::MLEFT, maxVolume, 0, false);
+ }
+ else if (channels == 2) {
+ init(ChannelMask(Volume::MLEFT|Volume::MRIGHT), maxVolume, 0, false );
+ }
+ else {
+ init(ChannelMask(Volume::MLEFT|Volume::MRIGHT), maxVolume, 0, false );
+ kdError(67100) << "Warning: Multi-channel Volume object created with old constructor - this will not work fully\n";
+ }
+}
+
+Volume::Volume( const Volume &v )
+{
+ _chmask = v._chmask;
+ _maxVolume = v._maxVolume;
+ _minVolume = v._minVolume;
+ _muted = v._muted;
+ _isCapture = v._isCapture;
+ setVolume(v, (ChannelMask)v._chmask);
+
+ // kdDebug(67100) << "Volume::copy-constructor initialized " << v << "\n";
+}
+
+void Volume::init( ChannelMask chmask, long maxVolume, long minVolume, bool isCapture )
+{
+ for ( int i=0; i<= Volume::CHIDMAX; i++ ) {
+ _volumes[i] = 0;
+ }
+ _chmask = chmask;
+ _maxVolume = maxVolume;
+ _minVolume = minVolume;
+ _isCapture = isCapture;
+ _muted = false;
+}
+
+// @ compatibility
+void Volume::setAllVolumes(long vol)
+{
+ for ( int i=0; i<= Volume::CHIDMAX; i++ ) {
+ if ( (_channelMaskEnum[i]) & _chmask ) {
+ // we are supposed to set it
+ _volumes[i] = volrange(vol);
+ }
+ }
+}
+
+// @ compatibility
+void Volume::setVolume( ChannelID chid, long vol)
+{
+ if ( chid>=0 && chid<=Volume::CHIDMAX ) {
+ // accepted. we don't care if we support the channel,
+ // because there is NO good action we could take.
+ // Anyway: getVolume() on an unsupported channel will return 0 all the time
+ _volumes[chid] = volrange(vol);
+ }
+}
+
+/**
+ * Copy the volume elements contained in v to this Volume object.
+ * Only those elments are copied, that are supported in BOTH Volume objects.
+ */
+void Volume::setVolume(const Volume &v)
+{
+ setVolume(v, (ChannelMask)(v._chmask&_chmask) );
+}
+
+/**
+ * Copy the volume elements contained in v to this Volume object.
+ * Only those elments are copied, that are supported in BOTH Volume objects
+ * and match the ChannelMask given by chmask.
+ */
+void Volume::setVolume(const Volume &v, ChannelMask chmask) {
+ for ( int i=0; i<= Volume::CHIDMAX; i++ ) {
+ if ( _channelMaskEnum[i] & _chmask & (int)chmask ) {
+ // we are supposed to copy it
+ _volumes[i] = volrange(v._volumes[i]);
+ }
+ else {
+ // Safety first! Lets play safe here and put sane values in
+ _volumes[i] = 0;
+ }
+ }
+
+}
+
+long Volume::maxVolume() {
+ return _maxVolume;
+}
+
+long Volume::minVolume() {
+ return _minVolume;
+}
+
+// @ compatibility
+long Volume::operator[](int id) {
+ return getVolume( (Volume::ChannelID) id );
+}
+
+long Volume::getVolume(ChannelID chid) {
+ long vol = 0;
+
+ if ( chid < 0 || chid > (Volume::CHIDMAX) ) {
+ // should throw exception here. I will return 0 instead
+ }
+ else {
+ // check if channel is supported
+ int chmask = _channelMaskEnum[chid];
+ if ( (chmask & _chmask) != 0 ) {
+ // channel is supported
+ vol = _volumes[chid];
+ }
+ else {
+ // should throw exception here. I will return 0 instead
+ }
+ }
+
+ return vol;
+}
+
+long Volume::getAvgVolume(ChannelMask chmask) {
+ int avgVolumeCounter = 0;
+ long long sumOfActiveVolumes = 0;
+ for ( int i=0; i<= Volume::CHIDMAX; i++ ) {
+ if ( (_channelMaskEnum[i] & _chmask) & (int)chmask ) {
+ avgVolumeCounter++;
+ sumOfActiveVolumes += _volumes[i];
+ }
+ }
+ if (avgVolumeCounter != 0) {
+ sumOfActiveVolumes /= avgVolumeCounter;
+ }
+ else {
+ // just return 0;
+ }
+ return (long)sumOfActiveVolumes;
+}
+
+long Volume::getTopStereoVolume(ChannelMask chmask) {
+ long long topVolumeCount = 0;
+ for ( int i=0; i<= Volume::CHIDMAX; i++ ) {
+ if ( (_channelMaskEnum[i] & _chmask) & (int)chmask ) {
+ if ( topVolumeCount < _volumes[i] )
+ topVolumeCount = _volumes[i];
+ }
+ }
+ return (long)topVolumeCount;
+}
+
+int Volume::count() {
+ int counter = 0;
+ for ( int i=0; i<= Volume::CHIDMAX; i++ ) {
+ if ( _channelMaskEnum[i] & _chmask ) {
+ counter++;
+ }
+ }
+ return counter;
+}
+
+/**
+ * returns a "sane" volume level. This means, it is a volume level inside the
+ * valid bounds
+ */
+long Volume::volrange( int vol )
+{
+ if ( vol < _minVolume ) {
+ return _minVolume;
+ }
+ else if ( vol < _maxVolume ) {
+ return vol;
+ }
+ else {
+ return _maxVolume;
+ }
+}
+
+
+std::ostream& operator<<(std::ostream& os, const Volume& vol) {
+ os << "(";
+ for ( int i=0; i<= Volume::CHIDMAX; i++ ) {
+ if ( i != 0 ) {
+ os << ",";
+ }
+ if ( Volume::_channelMaskEnum[i] & vol._chmask ) {
+ // supported channel: Print Volume
+ os << vol._volumes[i];
+ }
+ else {
+ // unsupported channel: Print "x"
+ os << "x";
+ }
+ } // all channels
+ os << ")";
+
+ os << " [" << vol._minVolume << "-" << vol._maxVolume;
+ if ( vol._muted ) { os << " : muted ]"; } else { os << " : playing ]"; }
+
+ return os;
+}
+
+kdbgstream& operator<<(kdbgstream &os, const Volume& vol) {
+ os << "(";
+ for ( int i=0; i<= Volume::CHIDMAX; i++ ) {
+ if ( i != 0 ) {
+ os << ",";
+ }
+ if ( Volume::_channelMaskEnum[i] & vol._chmask ) {
+ // supported channel: Print Volume
+ os << vol._volumes[i];
+ }
+ else {
+ // unsupported channel: Print "x"
+ os << "x";
+ }
+ } // all channels
+ os << ")";
+
+ os << " [" << vol._minVolume << "-" << vol._maxVolume;
+ if ( vol._muted ) { os << " : muted ]"; } else { os << " : playing ]"; }
+
+ return os;
+}
diff --git a/kmix/volume.h b/kmix/volume.h
new file mode 100644
index 00000000..9051ed0c
--- /dev/null
+++ b/kmix/volume.h
@@ -0,0 +1,80 @@
+// -*-C++-*-
+#ifndef VOLUME_H
+#define VOLUME_H
+
+#include <fstream>
+
+#include <kdebug.h>
+
+class Volume
+{
+ public:
+ enum ChannelMask { MNONE = 0,
+ MLEFT = 1, MRIGHT = 2, MCENTER = 4,
+ MMAIN = 3, MFRONT = 7,
+ MREARLEFT = 8, MREARRIGHT = 16, MWOOFER = 32,
+ MREAR = 56,
+ MLEFTREC = 64, MRIGHTREC = 128,
+ MREC =192,
+ MCUSTOM1 =256, MCUSTOM2 = 512,
+ MALL=65535 };
+
+
+ enum ChannelID { CHIDMIN = 0,
+ LEFT = 0, RIGHT = 1, CENTER = 2,
+ REARLEFT = 3, REARRIGHT = 4, WOOFER = 5,
+ LEFTREC = 6, RIGHTREC = 7,
+ CUSTOM1 = 8, CUSTOM2 = 9, CHIDMAX = 9 };
+
+
+ Volume( ChannelMask chmask = MALL, long maxVolume = 100, long minVolume=0, bool isCapture=false );
+ Volume( const Volume &v );
+ Volume( int channels, long maxVolume );
+
+
+
+ // Set all volumes as given by vol
+ void setAllVolumes(long vol);
+ // Set all volumes to the ones given in vol
+ void setVolume(const Volume &vol );
+ // Set volumes as specified by the channel mask
+ void setVolume( const Volume &vol, ChannelMask chmask);
+ void setVolume( ChannelID chid, long volume);
+
+ long getVolume(ChannelID chid);
+ long getAvgVolume(ChannelMask chmask);
+ long getTopStereoVolume(ChannelMask chmask);
+ long operator[](int);
+ long maxVolume();
+ long minVolume();
+ int count();
+
+ void setMuted( bool val ) { _muted = val; };
+ bool isMuted() { return _muted; };
+ bool isCapture() { return _isCapture; };
+
+ friend std::ostream& operator<<(std::ostream& os, const Volume& vol);
+ friend kdbgstream& operator<<(kdbgstream& os, const Volume& vol);
+
+ // _channelMaskEnum[] and the following elements moved to public seection. operator<<() could not
+ // access it, when private. Strange, as operator<<() is declared friend.
+ static int _channelMaskEnum[10];
+ bool _muted;
+ bool _isCapture; // true, when the Volume represents capture/record levels
+ long _chmask;
+ long _volumes[CHIDMAX+1];
+ long _maxVolume;
+ long _minVolume;
+
+private:
+ void init( ChannelMask chmask, long, long, bool );
+
+ long volrange( int vol );
+ long volrangeRec( int vol );
+};
+
+std::ostream& operator<<(std::ostream& os, const Volume& vol);
+kdbgstream& operator<<(kdbgstream &os, const Volume& vol);
+
+#endif // VOLUME
+
diff --git a/krec/Makefile.am b/krec/Makefile.am
new file mode 100644
index 00000000..25e652e1
--- /dev/null
+++ b/krec/Makefile.am
@@ -0,0 +1,95 @@
+
+if compile_ogg_export
+OGGEXPORTDIR=ogg_export
+endif
+
+if compile_mp3_export
+MP3EXPORTDIR=mp3_export
+endif
+
+SUBDIRS = . pics $(OGGEXPORTDIR) $(MP3EXPORTDIR)
+
+INCLUDES= \
+ -I$(top_builddir)/arts/gui/common \
+ -I$(top_builddir)/arts/midi \
+ -I$(top_builddir)/arts/modules/common \
+ -I$(top_builddir)/arts/modules/synth \
+ -I$(top_builddir)/arts/modules/effects \
+ -I$(top_srcdir)/arts/gui/kde \
+ -I$(top_srcdir)/arts/tools \
+ -I$(top_builddir)/arts/modules/effects \
+ -I$(top_builddir)/arts/modules/common \
+ -I$(top_builddir)/arts/modules/synth \
+ -I$(top_builddir)/arts/midi \
+ -I$(arts_includes) $(all_includes)
+
+METASOURCES = AUTO
+
+bin_PROGRAMS =
+lib_LTLIBRARIES =
+kdeinit_LTLIBRARIES = krec.la
+
+kde_module_LTLIBRARIES = kcm_krec.la kcm_krec_files.la libkrecexport_wave.la
+
+noinst_LTLIBRARIES = lib_krec_common.la
+
+krec_la_SOURCES = \
+ krecnewproperties.cpp \
+ krecfile.cpp \
+ krecfileview.cpp \
+ krecfileviewhelpers.cpp \
+ krecfilewidgets.cpp \
+ main.cpp \
+ krecord.cpp
+
+krec_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
+krec_la_LIBADD = \
+ lib_krec_common.la \
+ $(top_builddir)/arts/gui/kde/libartsgui_kde.la \
+ $(top_builddir)/arts/tools/libartscontrolsupport.la \
+ -lartskde $(LIBDL) $(LIB_KIO) $(LIB_KDEUI) $(LIB_KUTILS)
+
+kcm_krec_la_SOURCES = krecconfigure.cpp
+kcm_krec_la_LDFLAGS = -module -avoid-version -no-undefined $(all_libraries)
+kcm_krec_la_LIBADD = lib_krec_common.la $(LIB_KUTILS)
+
+kcm_krec_files_la_SOURCES = krecconfig_files.cpp
+kcm_krec_files_la_LDFLAGS = -module -avoid-version -no-undefined $(all_libraries)
+kcm_krec_files_la_LIBADD = lib_krec_common.la $(LIB_KUTILS)
+
+lib_krec_common_la_SOURCES = \
+ krecglobal.cpp \
+ krecexport_template.cpp \
+ krecconfig_fileswidget.cpp
+
+lib_krec_common_la_LDFLAGS = -module -avoid-version -no-undefined $(all_libraries)
+lib_krec_common_la_LIBADD = $(LIB_KDEUI) $(LIB_KIO)
+
+libkrecexport_wave_la_SOURCES = krecexport_wave.cpp
+libkrecexport_wave_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+libkrecexport_wave_la_LIBADD = lib_krec_common.la
+
+rcdir = $(kde_datadir)/krec
+rc_DATA = krecui.rc
+
+xdg_apps_DATA = krec.desktop
+xdg_configdir = $(kde_servicesdir)
+xdg_config_DATA = kcm_krec.desktop kcm_krec_files.desktop
+
+messages: rc.cpp
+ rm -f tips.cpp
+ $(PREPARETIPS) > tips.cpp
+ $(XGETTEXT) */*.cpp *.cpp *.h -o $(podir)/krec.pot
+ rm -f tips.cpp
+
+KDE_ICON = krec
+
+appsdatadir=$(kde_datadir)/krec
+appsdata_DATA=tips
+
+kde_servicetypes_DATA=krec_exportitem.desktop
+
+kde_services_DATA=krec_exportwave.desktop
+
+krecord.lo: ../arts/gui/common/artsgui.h ../arts/modules/effects/artsmoduleseffects.h ../arts/modules/common/artsmodulescommon.h ../arts/midi/artsmidi.h ../arts/modules/synth/artsmodulessynth.h
+
diff --git a/krec/configure.in.in b/krec/configure.in.in
new file mode 100644
index 00000000..092ca0ca
--- /dev/null
+++ b/krec/configure.in.in
@@ -0,0 +1,3 @@
+if test "x$build_arts" = "xno"; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE krec"
+fi
diff --git a/krec/globals.h b/krec/globals.h
new file mode 100644
index 00000000..4c95768c
--- /dev/null
+++ b/krec/globals.h
@@ -0,0 +1,36 @@
+/*
+
+ Copyright (C) 2003 Nikolas Zimmermann <wildfox@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef GLOBALS_H
+#define GLOBALS_H
+
+// Global reader + writer settings
+// FIXME: Check endian!
+#define GLOBAL_SWAP false
+
+// Global reader settings
+#define GLOBAL_BUFFERSIZE 8192
+
+// Global writer settings
+#define GLOBAL_RATE 44100
+#define GLOBAL_CHANNELS 2
+
+#endif
diff --git a/krec/hi128-app-krec.png b/krec/hi128-app-krec.png
new file mode 100644
index 00000000..67b5bf79
--- /dev/null
+++ b/krec/hi128-app-krec.png
Binary files differ
diff --git a/krec/hi16-app-krec.png b/krec/hi16-app-krec.png
new file mode 100644
index 00000000..16635f55
--- /dev/null
+++ b/krec/hi16-app-krec.png
Binary files differ
diff --git a/krec/hi22-app-krec.png b/krec/hi22-app-krec.png
new file mode 100644
index 00000000..9585321e
--- /dev/null
+++ b/krec/hi22-app-krec.png
Binary files differ
diff --git a/krec/hi32-app-krec.png b/krec/hi32-app-krec.png
new file mode 100644
index 00000000..0aa41e11
--- /dev/null
+++ b/krec/hi32-app-krec.png
Binary files differ
diff --git a/krec/hi48-app-krec.png b/krec/hi48-app-krec.png
new file mode 100644
index 00000000..bf1afefa
--- /dev/null
+++ b/krec/hi48-app-krec.png
Binary files differ
diff --git a/krec/hi64-app-krec.png b/krec/hi64-app-krec.png
new file mode 100644
index 00000000..872cb4e9
--- /dev/null
+++ b/krec/hi64-app-krec.png
Binary files differ
diff --git a/krec/kcm_krec.desktop b/krec/kcm_krec.desktop
new file mode 100644
index 00000000..a4ae874c
--- /dev/null
+++ b/krec/kcm_krec.desktop
@@ -0,0 +1,108 @@
+[Desktop Entry]
+Icon=krec
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=krec
+X-KDE-FactoryName=KRecConfigGeneralFactory
+X-KDE-ParentApp=krec
+X-KDE-ParentComponents=krec
+
+Name=Global
+Name[bg]=Общи
+Name[bn]=গ্লোবাল
+Name[br]=Hollek
+Name[bs]=Globalno
+Name[cs]=Globální
+Name[cy]=Eang
+Name[el]=Καθολικές
+Name[eo]=Tutprograma
+Name[et]=Globaalne
+Name[eu]=Globala
+Name[fa]=سراسری
+Name[fi]=Yleiset
+Name[he]=כללי
+Name[hi]=वैश्विक
+Name[hu]=Globális
+Name[is]=Algilt
+Name[it]=Globale
+Name[ja]=グローバル
+Name[kk]=Жалпы
+Name[km]=សកល
+Name[ko]=전역
+Name[lt]=Globalinė
+Name[mk]=Општо
+Name[nds]=Globaal
+Name[ne]=विश्वव्यापी
+Name[nl]=Globaal
+Name[pa]=ਗਲੋਬਲ
+Name[pl]=Ogólne
+Name[ru]=Глобальные
+Name[sk]=Globálne
+Name[sl]=Splošno
+Name[sr]=Глобално
+Name[sr@Latn]=Globalno
+Name[ta]=உலகலாவிய
+Name[tg]=Саросарӣ
+Name[th]=โดยรวม
+Name[tr]=Evrensel
+Name[uk]=Глобальні
+Name[uz@cyrillic]=Глобал
+Name[zh_CN]=全局
+Name[zh_HK]=通用
+Name[zh_TW]=全域
+Comment=Global Configuration
+Comment[bg]=Общи настройки
+Comment[bn]=গ্লোবাল কনফিগারেশন
+Comment[br]=Kefluniadur hollek
+Comment[bs]=Globalne postavke
+Comment[ca]=Configuració global
+Comment[cs]=Globální nastavení
+Comment[cy]=Ffurfweddiad Eang
+Comment[da]=Global indstilling
+Comment[de]=Globale Einstellungen
+Comment[el]=Καθολική ρύθμιση
+Comment[eo]=Tutprograma Agordo
+Comment[es]=Configuración global
+Comment[et]=Globaalne seadistamine
+Comment[eu]=Konfigurazio globala
+Comment[fa]=پیکربندی سراسری
+Comment[fi]=Yleiset asetukset
+Comment[fr]=Configuration globale
+Comment[ga]=Cumraíocht Chomhchoiteann
+Comment[gl]=Configuración Global
+Comment[he]=הגדרות כלליות
+Comment[hu]=Globális beállítások
+Comment[is]=Algildar stillingar
+Comment[it]=Configurazione globale
+Comment[ja]=グローバル設定
+Comment[kk]=Жалпы баптаулары
+Comment[km]=ការ​កំណត់​រចនាសម្ព័ន្ធ​សកល
+Comment[ko]=전역 설정
+Comment[lt]=Globalinė konfigūracija
+Comment[mk]=Глобална конфигурација
+Comment[nb]=Globalt oppsett
+Comment[nds]=Globaal Instellen
+Comment[ne]=विश्वव्यापी कन्फिगरेसन
+Comment[nl]=Globale configuratie
+Comment[nn]=Globalt oppsett
+Comment[pa]=ਗਲੋਬਲ ਸੰਰਚਨਾ
+Comment[pl]=Globalna konfiguracja
+Comment[pt]=Configuração Global
+Comment[pt_BR]=Configuração global
+Comment[ro]=Configurare globală
+Comment[ru]=Общие параметры
+Comment[sk]=Globálne nastavenie
+Comment[sl]=Splošne nastavitve
+Comment[sr]=Опште подешавање
+Comment[sr@Latn]=Opšte podešavanje
+Comment[sv]=Global inställning
+Comment[ta]=KRec இன் உலகலாவிய வடிவமைப்பு
+Comment[th]=ปรับแต่งโดยรวม
+Comment[tr]=Genel Yapılandırma
+Comment[uk]=Глобальні налаштування
+Comment[zh_CN]=全局配置
+Comment[zh_HK]=通用設定
+Comment[zh_TW]=全域設定
+
diff --git a/krec/kcm_krec_files.desktop b/krec/kcm_krec_files.desktop
new file mode 100644
index 00000000..d946330f
--- /dev/null
+++ b/krec/kcm_krec_files.desktop
@@ -0,0 +1,127 @@
+[Desktop Entry]
+Icon=filenew
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=krec_files
+X-KDE-FactoryName=KRecConfigFileFactory
+X-KDE-ParentApp=krec
+X-KDE-ParentComponents=krec
+
+Name=New Files
+Name[ar]=الملفات الجديدة
+Name[bg]=Нови файлове
+Name[bn]=নতুন ফাইল
+Name[br]=Restroù nevez
+Name[bs]=Nove datoteke
+Name[ca]=Nous fitxers
+Name[cs]=Nové soubory
+Name[cy]=Ffeiliau Newydd
+Name[da]=Nye filer
+Name[de]=Neue Dateien
+Name[el]=Νέα αρχεία
+Name[eo]=Novaj dosieroj
+Name[es]=Nuevos archivos
+Name[et]=Uued failid
+Name[eu]=Fitxategi berriak
+Name[fa]=پرونده‌های جدید
+Name[fi]=Uudet tiedostot
+Name[fr]=Nouveaux fichiers
+Name[ga]=Comhaid Nua
+Name[gl]=Novos Ficheiros
+Name[he]=קבצים חדשים
+Name[hi]=नई फ़ाइलें
+Name[hu]=Új fájlok
+Name[is]=Nýjar skrár
+Name[it]=Nuovi file
+Name[ja]=新規ファイル
+Name[kk]=Жаңа файдар
+Name[km]=ឯកសារ​ថ្មី
+Name[ko]=새 파일
+Name[lt]=Naujos bylos
+Name[mk]=Нови датотеки
+Name[nb]=Nye filer
+Name[nds]=Niege Dateien
+Name[ne]=नयाँ फाइल
+Name[nl]=Nieuwe bestanden
+Name[nn]=Nye filer
+Name[pa]=ਨਵੀਆਂ ਫਾਇਲਾਂ
+Name[pl]=Nowe pliki
+Name[pt]=Novos Ficheiros
+Name[pt_BR]=Novos Arquivos
+Name[ro]=Fişiere noi
+Name[ru]=Новые файлы
+Name[sk]=Nové súbory
+Name[sl]=Nove datoteke
+Name[sr]=Нови фајлови
+Name[sr@Latn]=Novi fajlovi
+Name[sv]=Nya filer
+Name[ta]=புது கோப்புகள்
+Name[tg]=Файли Нав
+Name[th]=แฟ้มใหม่
+Name[tr]=Yeni Dosyalar
+Name[uk]=Нові файли
+Name[uz]=Yangi fayllar
+Name[uz@cyrillic]=Янги файллар
+Name[wa]=Noveas fitchîs
+Name[zh_CN]=新建文件
+Name[zh_HK]=新檔案
+Name[zh_TW]=新檔案
+Comment=Default Properties for New Files
+Comment[ar]=الخصائص الإفتراضية للملفات الجديدة
+Comment[bg]=Настройки на създаване на нови файлове
+Comment[br]=Dibaboù dre ziouer evit ar restroù nevez
+Comment[bs]=Podrazumijevane postavke za nove datoteke
+Comment[ca]=Propietats per omissió dels nous fitxers
+Comment[cs]=Výchozí nastavení pro nové soubory
+Comment[cy]=Priodweddau Rhagosod ar gyfer Ffeiliau Newydd
+Comment[da]=Standardegenskaber for nye filer
+Comment[de]=Standardeinstellungen für neue Dateien
+Comment[el]=Προκαθορισμένες ιδιότητες για νέα αρχεία
+Comment[eo]=Aprioraj ecoj por novaj dosieroj
+Comment[es]=Propiedades predeterminadas para nuevos archivos
+Comment[et]=Uute failide vaikimisi omadused
+Comment[eu]=Fitxategi berrientzako lehenetsitako propietateak.
+Comment[fa]=ویژگیهای پیش‌فرض برای پرونده‌های جدید
+Comment[fi]=Oletusasetukset uusille tiedostoille
+Comment[fr]=Propriétés par défaut pour les nouveaux fichiers
+Comment[gl]=Propiedades por Defecto para os Novos Ficheiros
+Comment[he]=מאפייני ברירת המחדל עבור קבצים חדשים
+Comment[hi]=नई फ़ाइलों के लिए डिफ़ॉल्ट गुण
+Comment[hu]=Az alapértelmezett tulajdonságok új fájloknál
+Comment[is]=Sjálfgefnir eiginleikar nýrra skráa
+Comment[it]=Impostazioni predefinite per i nuovi file
+Comment[ja]=新規ファイルのデフォルト設定
+Comment[kk]=Жаңа файлдардың әдеттегі қасиеттері
+Comment[km]=លក្ខណៈ​សម្បត្តិ​លំនាំដើម សម្រាប់​ឯកសារ​ថ្មី
+Comment[ko]=새 파일의 기본 속성
+Comment[lt]=Numatytieji naujų bylų nustatymai
+Comment[mk]=Почетни својства за новите датотеки
+Comment[nb]=Standard egenskaper for nye filer
+Comment[nds]=Standardegenschappen för niege Dateien
+Comment[ne]=नयाँ फाइलका लागि पूर्वनिर्धारित गुण
+Comment[nl]=Standaardeigenschappen voor nieuwe bestanden
+Comment[nn]=Standardeigenskapar for nye filer
+Comment[pa]=ਨਵੀਆਂ ਫਾਇਲਾਂ ਲਈ ਮੂਲ ਵਿਸ਼ੇਸ਼ਤਾ
+Comment[pl]=Domyślne ustawienia dla nowych plików
+Comment[pt]=Propriedades por Omissão para Novos Ficheiros
+Comment[pt_BR]=Propriedades padrão para novos arquivos
+Comment[ro]=Proprietăţi implicite pentru fişiere noi
+Comment[ru]=Параметры по умолчанию для новых файлов
+Comment[sk]=Štandardné vlastnosti pre nové súbory
+Comment[sl]=Privzete lastnosti za nove datoteke
+Comment[sr]=Подразумевана својства за нове фајлове
+Comment[sr@Latn]=Podrazumevana svojstva za nove fajlove
+Comment[sv]=Standardegenskaper för nya filer
+Comment[ta]=புது கோப்புகளுக்கான முன்னிருப்பு பண்புகள்
+Comment[tg]=Хусусиятҳо бо Нобаёнӣ барои Файлҳои Нав
+Comment[th]=ค่าคุณสมบัติโดยปริยายสำหรับแฟ้มใหม่
+Comment[tr]=Yeni Dosyalar İçin Öntanımlı Özellikler
+Comment[uk]=Типові властивості для нових файлів
+Comment[uz]=Yangi fayllarning andoza xossalari
+Comment[uz@cyrillic]=Янги файлларнинг андоза хоссалари
+Comment[zh_CN]=新文件的默认属性
+Comment[zh_HK]=新檔案的預設值
+Comment[zh_TW]=新檔案的預設內容
+
diff --git a/krec/krec.desktop b/krec/krec.desktop
new file mode 100644
index 00000000..047f95b5
--- /dev/null
+++ b/krec/krec.desktop
@@ -0,0 +1,80 @@
+[Desktop Entry]
+Type=Application
+Exec=krec %i %m -caption "%c"
+Icon=krec
+Path=
+DocPath=krec/index.html
+Terminal=false
+GenericName=Recording Tool
+GenericName[af]=Opneem Program
+GenericName[ar]=أداة التسجيل
+GenericName[bg]=Аудио запис
+GenericName[br]=Ostilh enrollañ
+GenericName[bs]=Alat za snimanje
+GenericName[ca]=Eina per gravar
+GenericName[cs]=Nástroj pro nahrávání
+GenericName[cy]=Erfyn Recordio
+GenericName[da]=Indspilningsværktøj
+GenericName[de]=Aufnahmeprogramm
+GenericName[el]=Εργαλείο εγγραφής
+GenericName[eo]=Sonregistrilo
+GenericName[es]=Herramienta de grabación
+GenericName[et]=Salvestamise rakendus
+GenericName[eu]=Grabaketa tresna
+GenericName[fa]=ابزار ضبط
+GenericName[fi]=Nauhoitustyökalu
+GenericName[fr]=Outil d'enregistrement
+GenericName[ga]=Uirlis Taifeadta
+GenericName[gl]=Ferramenta de Gravación
+GenericName[he]=כלי הקלטה
+GenericName[hi]=रेकॉर्डिंग औज़ार
+GenericName[hu]=Hangrögzítő
+GenericName[is]=Hljóðupptökutól
+GenericName[it]=Strumento di registrazione
+GenericName[ja]=録音ツール
+GenericName[kk]=Жазып алу құралы
+GenericName[km]=ឧបករណ៍​ថត
+GenericName[ko]=녹음 도구
+GenericName[lt]=Įrašinėjimo priemonė
+GenericName[mk]=Алатка за снимање
+GenericName[ms]=Alat Perekod
+GenericName[nb]=Opptaksverktøy
+GenericName[nds]=Opnehm-Warktüüch
+GenericName[ne]=रेकर्डिङ उपकरण
+GenericName[nl]=Opnameprogramma
+GenericName[nn]=Opptaksverktøy
+GenericName[pa]=ਰਿਕਾਰਡਿੰਗ ਸੰਦ
+GenericName[pl]=Narzędzie do nagrywania
+GenericName[pt]=Ferramenta de Gravação
+GenericName[pt_BR]=Ferramenta de Gravação
+GenericName[ro]=Utilitar de înregistrare sunet
+GenericName[ru]=Звукозапись
+GenericName[se]=Báddenreaidu
+GenericName[sk]=Nahrávací nástroj
+GenericName[sl]=Orodje za snemanje
+GenericName[sr]=Алат за снимање звука
+GenericName[sr@Latn]=Alat za snimanje zvuka
+GenericName[sv]=Inspelningsverktyg
+GenericName[ta]=பதிவுக் கருவி
+GenericName[tg]=Асбоби Сабткунӣ
+GenericName[th]=เครื่องมือบันทึกเสียง
+GenericName[tr]=Ses Kaydedici
+GenericName[uk]=Засіб для запису
+GenericName[uz]=Tovush yozish vositasi
+GenericName[uz@cyrillic]=Товуш ёзиш воситаси
+GenericName[ven]=Tshishumiswa tshau rekhoda
+GenericName[wa]=Usteye eredjîstresse di sons
+GenericName[xh]=Isixhobo Sokubhala phantsi
+GenericName[zh_CN]=录音工具
+GenericName[zh_HK]=錄製工具
+GenericName[zh_TW]=錄製工具
+GenericName[zu]=Ithuluzi Lokuqopha
+Name=KRec
+Name[bn]=কে-রেক
+Name[hi]=के-रेक
+Name[sv]=Krec
+Name[tg]=KСабт
+Name[zh_TW]=KRec 錄音機
+X-KDE-StartupNotify=true
+X-DCOP-ServiceType=Multi
+Categories=Qt;KDE;AudioVideo;
diff --git a/krec/krec_exportitem.desktop b/krec/krec_exportitem.desktop
new file mode 100644
index 00000000..042cb52c
--- /dev/null
+++ b/krec/krec_exportitem.desktop
@@ -0,0 +1,61 @@
+[Desktop Entry]
+Type=ServiceType
+X-KDE-ServiceType=KRec/exportplugin
+Comment=Exportplugin for KRec
+Comment[bg]=Приставка за KRec
+Comment[br]=Lugent ezporh evit KRec
+Comment[bs]=Exportplugin za KRec
+Comment[ca]=Connector d'exportació per a KRec
+Comment[cs]=Exportní modul pro KRec
+Comment[cy]=Ategyn Allforio ar gyfer KRec
+Comment[da]=Eksportplugin for KRec
+Comment[de]=Export-Modul für KRec
+Comment[el]=Πρόσθετο εξαγωγής για το KRec
+Comment[eo]=Eksportkromaĵo por KRec
+Comment[es]=Accesorio de exportación para KRec
+Comment[et]=KReci eksportplugin
+Comment[eu]=Esportazio plugin-a KRec-entzat
+Comment[fa]=وصلۀ صادرات برای KRec
+Comment[fi]=Vientiliitännäinen KRec-ohjelmalle
+Comment[fr]=Module externe d'exportation pour KRec
+Comment[ga]=Breiseán easpórtála le haghaidh KRec
+Comment[gl]=Extensión de exportacións para KRec
+Comment[he]=תוסף יצוא עבור KRec
+Comment[hi]=के-रेक के लिए निर्यात प्लगइन
+Comment[hu]=Exportálási bővítőmodul a KRec-hez
+Comment[is]=Útflutningsíforrit fyrir KRec
+Comment[it]=Plugin di esportazione per KRec
+Comment[ja]=KRec のエクスポートプラグイン
+Comment[kk]=KRec-тің экспорттау плагин модулі
+Comment[km]=កម្មវិធី​ជំនួយ​ការ​នាំចេញ​សម្រាប់ KRec
+Comment[ko]=KRec 내보내기 플러그인
+Comment[lt]=KRec eksporto priedas
+Comment[mk]=Приклучок за изнесување за KRec
+Comment[nb]=Programtillegg for eksport i KRec
+Comment[nds]=Export-Moduul för KRec
+Comment[ne]=KRec का लागि निर्यात प्लगइन
+Comment[nl]=Exportplugin voor KRec
+Comment[nn]=Eksporttillegg for KRec
+Comment[pl]=Wtyczka eksportu dla KRec
+Comment[pt]='Plugin' de exportação para o KRec
+Comment[pt_BR]=Um Plugin de exportação para o KRec
+Comment[ro]=Modul de export pentru KRec
+Comment[ru]=Модуль экспорта файла для KRec
+Comment[sk]=Exportný modul pre KRec
+Comment[sl]=Izvozni vstavek za KRec
+Comment[sr]=KRec-ов прикључак за извоз
+Comment[sr@Latn]=KRec-ov priključak za izvoz
+Comment[sv]=Exportinsticksmodul för Krec
+Comment[ta]=KRecற்கான ஏற்றுமதிசொருகுப்பொருள்
+Comment[tg]=Модули содирот барои KСабт
+Comment[th]=ปลั๊กอินส่งออกสำหรับ KRec
+Comment[tr]=KRec için aktarma eklentisi
+Comment[uk]=Втулок експорту для KRec
+Comment[uz]=KRec uchun eksport plagini
+Comment[uz@cyrillic]=KRec учун экспорт плагини
+Comment[zh_CN]=KRec 导出插件
+Comment[zh_HK]=用於 KRec 的 匯出插件
+Comment[zh_TW]=KRec 匯出外掛
+
+[PropertyDef::X-KDE-ExportSuffix]
+Type=QStringList
diff --git a/krec/krec_exportwave.desktop b/krec/krec_exportwave.desktop
new file mode 100644
index 00000000..5a486ad5
--- /dev/null
+++ b/krec/krec_exportwave.desktop
@@ -0,0 +1,57 @@
+[Desktop Entry]
+Type=Service
+Name=Wave-Export
+Name[bg]=Експортиране в Wave
+Name[bn]=ওয়েভ-রপ্তানি
+Name[br]=Ezporhz Wave
+Name[ca]=Exportació d'ones
+Name[cs]=Export do WAV
+Name[cy]=Allforio Ton
+Name[da]=Wave-Eksport
+Name[el]=Εξαγωγή wave
+Name[eo]=Wave-Eksporto
+Name[es]=Exportador de Wave
+Name[et]=Wave-eksport
+Name[eu]=Wave-esportazioa
+Name[fa]=صادرات موج
+Name[fi]=Wave-tiedoston vienti
+Name[fr]=Exportation en Wave
+Name[gl]=Exportación a Wav
+Name[he]=יצוא Wave
+Name[hi]=Wave-निर्यात
+Name[hu]=Exportálás - WAV
+Name[is]=Wave útflutningur
+Name[it]=Esporta-Wave
+Name[ja]=Wave エクスポート
+Name[kk]=Wave-экспорттау
+Name[km]=នាំចេញ-Wave
+Name[ko]=Wave 추출
+Name[lt]=Wave eksportas
+Name[mk]=Wave-изнесување
+Name[nb]=Wave-eksport
+Name[ne]=तरङ-निर्यात
+Name[nl]=Wave-export
+Name[nn]=Wave-eksport
+Name[pl]=Eksport wave
+Name[pt]=Exportação de Wave
+Name[pt_BR]=Exportar-Onda
+Name[ro]=Exportare WAV
+Name[ru]=Экспорт в wav
+Name[sk]=Export do Wave
+Name[sl]=Izvoz v WAV
+Name[sr]=Извоз у Wave
+Name[sr@Latn]=Izvoz u Wave
+Name[sv]=Wave-export
+Name[ta]=அலை-ஏற்றுமதி
+Name[tg]=Содироти Мавҷ
+Name[th]=ส่งออกแฟ้ม Wave
+Name[tr]=Wave Aktarma
+Name[uk]=Експорт Wave
+Name[uz]=Wave eksport
+Name[uz@cyrillic]=Wave экспорт
+Name[zh_CN]=声波-导出
+Name[zh_HK]=Wave-匯出
+Name[zh_TW]=Wave 匯出
+X-KDE-Library=libkrecexport_wave
+ServiceTypes=KRec/exportplugin
+X-KDE-ExportSuffix=wav,WAV
diff --git a/krec/krecconfig_files.cpp b/krec/krecconfig_files.cpp
new file mode 100644
index 00000000..a741c8e9
--- /dev/null
+++ b/krec/krecconfig_files.cpp
@@ -0,0 +1,85 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#include "krecconfig_files.h"
+#include "krecconfig_files.moc"
+
+#include "krecconfig_fileswidget.h"
+
+#include <kgenericfactory.h>
+#include <kdebug.h>
+#include <qlayout.h>
+#include <qbuttongroup.h>
+#include <qradiobutton.h>
+#include <qlineedit.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <klocale.h>
+
+typedef KGenericFactory<KRecConfigFiles, QWidget> KRecConfigFilesFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_krec_files, KRecConfigFilesFactory( "krec" ) )
+
+KRecConfigFiles::KRecConfigFiles( QWidget* p, const char*, const QStringList& s )
+ : KCModule( KRecConfigFilesFactory::instance(), p, s )
+ , _layout( 0 )
+ , _filewidget( 0 )
+{
+ _layout = new QBoxLayout( this, QBoxLayout::TopToBottom );
+
+ _layout->addSpacing( 10 );
+
+ _filewidget = new KRecConfigFilesWidget( this );
+ connect( _filewidget, SIGNAL( sRateChanged( int ) ), this, SLOT( ratechanged( int ) ) );
+ connect( _filewidget, SIGNAL( sChannelsChanged( int ) ), this, SLOT( channelschanged( int ) ) );
+ connect( _filewidget, SIGNAL( sBitsChanged( int ) ), this, SLOT( bitschanged( int ) ) );
+ connect( _filewidget, SIGNAL( sUseDefaultsChanged( bool ) ), this, SLOT( usedefaultschanged( bool ) ) );
+ _layout->addWidget( _filewidget );
+
+ _layout->addStretch( 100 );
+
+ load();
+}
+
+KRecConfigFiles::~KRecConfigFiles() {
+}
+
+void KRecConfigFiles::load() {
+kdDebug( 60005 ) << k_funcinfo << endl;
+ defaults();
+ _filewidget->load();
+}
+
+void KRecConfigFiles::save() {
+ _filewidget->save();
+ emit changed( false );
+}
+
+void KRecConfigFiles::defaults() {
+ _filewidget->defaults();
+}
+
+void KRecConfigFiles::ratechanged( int ) {
+ emit changed( true );
+}
+void KRecConfigFiles::channelschanged( int ) {
+ emit changed( true );
+}
+void KRecConfigFiles::bitschanged( int ) {
+ emit changed( true );
+}
+void KRecConfigFiles::usedefaultschanged( bool ) {
+ emit changed( true );
+}
+
diff --git a/krec/krecconfig_files.h b/krec/krecconfig_files.h
new file mode 100644
index 00000000..0ef74b49
--- /dev/null
+++ b/krec/krecconfig_files.h
@@ -0,0 +1,52 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#ifndef KREC_CONFIGURE_FILES_H
+#define KREC_CONFIGURE_FILES_H
+
+#include <kcmodule.h>
+#include <qstringlist.h>
+#include <qstring.h>
+
+class QBoxLayout;
+class QRadioButton;
+class QButtonGroup;
+class QLineEdit;
+class QHBox;
+class QLabel;
+
+class KRecConfigFilesWidget;
+
+class KRecConfigFiles : public KCModule {
+ Q_OBJECT
+public:
+ KRecConfigFiles( QWidget*, const char* =0, const QStringList& = QStringList() );
+ ~KRecConfigFiles();
+
+ void load();
+ void save();
+ void defaults();
+private slots:
+ void ratechanged( int );
+ void channelschanged( int );
+ void bitschanged( int );
+ void usedefaultschanged( bool );
+
+private:
+ QBoxLayout *_layout;
+ KRecConfigFilesWidget *_filewidget;
+};
+
+#endif
+
+// vim:sw=4:ts=4
diff --git a/krec/krecconfig_fileswidget.cpp b/krec/krecconfig_fileswidget.cpp
new file mode 100644
index 00000000..5df6693d
--- /dev/null
+++ b/krec/krecconfig_fileswidget.cpp
@@ -0,0 +1,147 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#include "krecconfig_fileswidget.h"
+#include "krecconfig_fileswidget.moc"
+
+#include "krecglobal.h"
+
+#include <kdebug.h>
+#include <qbuttongroup.h>
+#include <qradiobutton.h>
+#include <qcheckbox.h>
+#include <qlineedit.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <qlabel.h>
+
+KRecConfigFilesWidget::KRecConfigFilesWidget( QWidget* p, const char* n )
+ : QVBox( p,n )
+ , _hbox( new QHBox( this ) )
+ , _ratebox( 0 ), _channelsbox( 0 ), _bitsbox( 0 )
+ , _rate48( 0 ), _rate44( 0 ), _rate22( 0 ), _rate11( 0 ), _rateother( 0 )
+ , _rateotherbox( 0 ), _rateotherlabel( 0 ), _rateotherline( 0 )
+ , _channels2( 0 ), _channels1( 0 )
+ , _bits16( 0 ), _bits8( 0 )
+ , _samplingRate( 44100 ), _channels( 2 ), _bits( 16 )
+{
+ _ratebox = new QButtonGroup( 1, Qt::Horizontal, i18n( "Sampling Rate" ), _hbox );
+ connect( _ratebox, SIGNAL( clicked( int ) ), this, SLOT( ratechanged( int ) ) );
+ _rate48 = new QRadioButton( i18n( "48000 Hz" ), _ratebox );
+ _rate44 = new QRadioButton( i18n( "44100 Hz" ), _ratebox );
+ _rate22 = new QRadioButton( i18n( "22050 Hz" ), _ratebox );
+ _rate11 = new QRadioButton( i18n( "11025 Hz" ), _ratebox );
+ _rateother = new QRadioButton( i18n( "Other" ), _ratebox );
+ _rateotherbox = new QHBox( _ratebox );
+ _rateotherbox->setSpacing( 2 );
+ _rateotherlabel = new QLabel( i18n( "Other:" ), _rateotherbox );
+ _rateotherline = new QLineEdit( _rateotherbox );
+ _rateotherline->setMaxLength( 10 );
+ _rateotherline->setFrame( true );
+ _rateotherbox->setEnabled( false );
+ connect( _rateotherline, SIGNAL( textChanged( const QString& ) ), this, SLOT( rateotherchanged( const QString& ) ) );
+ _channelsbox = new QButtonGroup( 1, Qt::Horizontal, i18n( "Channels" ), _hbox );
+ connect( _channelsbox, SIGNAL( clicked( int ) ), this, SLOT( channelschanged( int ) ) );
+ _channels2 = new QRadioButton( i18n( "Stereo (2 channels)" ), _channelsbox );
+ _channels1 = new QRadioButton( i18n( "Mono (1 channel)" ), _channelsbox );
+ _bitsbox = new QButtonGroup( 1, Qt::Horizontal, i18n( "Bits" ), _hbox );
+ connect( _bitsbox, SIGNAL( clicked( int ) ), this, SLOT( bitschanged( int ) ) );
+ _bits16 = new QRadioButton( i18n( "16 bit" ), _bitsbox );
+ _bits8 = new QRadioButton( i18n( "8 bit" ), _bitsbox );
+
+ _usedefaults = new QCheckBox( i18n( "Use defaults for creating new files" ), this );
+ connect( _usedefaults, SIGNAL( toggled( bool ) ), this, SLOT( usedefaultschanged( bool ) ) );
+
+ setSpacing( 5 );
+
+ load();
+}
+
+KRecConfigFilesWidget::~KRecConfigFilesWidget() {
+}
+
+void KRecConfigFilesWidget::load() {
+kdDebug( 60005 ) << k_funcinfo << endl;
+ defaults();
+ kapp->config()->setGroup( "FileDefaults" );
+ _samplingRate = kapp->config()->readNumEntry( "SamplingRate", 44100 );
+ switch ( _samplingRate ) {
+ case 48000: _rate48->setChecked( true ); break;
+ case 44100: _rate44->setChecked( true ); break;
+ case 22050: _rate22->setChecked( true ); break;
+ case 11025: _rate11->setChecked( true ); break;
+ default:
+ _rateother->setChecked( true );
+ _rateotherbox->setEnabled( true );
+ _rateotherline->setText( QString::number( _samplingRate ) );
+ break;
+ };
+ _channels = kapp->config()->readNumEntry( "Channels", 2 );
+ switch ( _channels ) {
+ default:
+ case 2: _channels2->setChecked( true ); break;
+ case 1: _channels1->setChecked( true ); break;
+ };
+ _bits = kapp->config()->readNumEntry( "Bits", 16 );
+ switch ( _bits ) {
+ default:
+ case 16: _bits16->setChecked( true ); break;
+ case 8: _bits8->setChecked( true ); break;
+ };
+ _usedefaults->setChecked( kapp->config()->readBoolEntry( "UseDefaults", false ) );
+}
+
+void KRecConfigFilesWidget::save() {
+ kapp->config()->setGroup( "FileDefaults" );
+ kapp->config()->writeEntry( "SamplingRate", _samplingRate );
+ kapp->config()->writeEntry( "Channels", _channels );
+ kapp->config()->writeEntry( "Bits", _bits );
+ kapp->config()->writeEntry( "UseDefaults", _usedefaults->isOn() );
+
+ kapp->config()->sync();
+}
+
+void KRecConfigFilesWidget::defaults() {
+ _rate44->setChecked( true );
+ _channels2->setChecked( true );
+ _bits16->setChecked( true );
+}
+
+void KRecConfigFilesWidget::ratechanged( int index ) {
+ if ( _ratebox->find( index ) == _rateother ) _rateotherbox->setEnabled( true );
+ else _rateotherbox->setEnabled( false );
+ if ( _ratebox->find( index ) == _rate48 ) _samplingRate = 48000;
+ if ( _ratebox->find( index ) == _rate44 ) _samplingRate = 44100;
+ if ( _ratebox->find( index ) == _rate22 ) _samplingRate = 22050;
+ if ( _ratebox->find( index ) == _rate11 ) _samplingRate = 11025;
+ emit sRateChanged( _samplingRate );
+}
+void KRecConfigFilesWidget::rateotherchanged( const QString& text ) {
+ _samplingRate = text.toInt();
+ emit sRateChanged( _samplingRate );
+}
+void KRecConfigFilesWidget::channelschanged( int index ) {
+ if ( _channelsbox->find( index ) == _channels2 ) _channels = 2;
+ if ( _channelsbox->find( index ) == _channels1 ) _channels = 1;
+ emit sChannelsChanged( _channels );
+}
+void KRecConfigFilesWidget::bitschanged( int index ) {
+ if ( _bitsbox->find( index ) == _bits16 ) _bits = 16;
+ if ( _bitsbox->find( index ) == _bits8 ) _bits = 8;
+ emit sBitsChanged( _bits );
+}
+
+void KRecConfigFilesWidget::usedefaultschanged( bool n ) {
+ emit sUseDefaultsChanged( n );
+}
diff --git a/krec/krecconfig_fileswidget.h b/krec/krecconfig_fileswidget.h
new file mode 100644
index 00000000..2095ce33
--- /dev/null
+++ b/krec/krecconfig_fileswidget.h
@@ -0,0 +1,68 @@
+/***************************************************************************
+ krecconfig_fileswidget.h - description
+ -------------------
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#ifndef KREC_CONFIGURE_FILESWIDGET_H
+#define KREC_CONFIGURE_FILESWIDGET_H
+
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qstringlist.h>
+#include <qstring.h>
+
+class QRadioButton;
+class QCheckBox;
+class QButtonGroup;
+class QLineEdit;
+class QLabel;
+
+class KRecConfigFilesWidget : public QVBox {
+ Q_OBJECT
+public:
+ KRecConfigFilesWidget( QWidget*, const char* =0 );
+ ~KRecConfigFilesWidget();
+
+ void load();
+ void save();
+ void defaults();
+signals:
+ void sRateChanged( int );
+ void sChannelsChanged( int );
+ void sBitsChanged( int );
+ void sUseDefaultsChanged( bool );
+private slots:
+ void ratechanged( int );
+ void rateotherchanged( const QString& );
+ void channelschanged( int );
+ void bitschanged( int );
+ void usedefaultschanged( bool );
+
+private:
+ QHBox *_hbox;
+ QButtonGroup *_ratebox, *_channelsbox, *_bitsbox;
+ QRadioButton *_rate48, *_rate44, *_rate22, *_rate11, *_rateother;
+ QHBox *_rateotherbox;
+ QLabel *_rateotherlabel;
+ QLineEdit *_rateotherline;
+ QRadioButton *_channels2, *_channels1;
+ QRadioButton *_bits16, *_bits8;
+
+ QCheckBox *_usedefaults;
+
+ int _samplingRate, _channels, _bits;
+};
+
+#endif
+
+// vim:sw=4:ts=4
diff --git a/krec/krecconfigure.cpp b/krec/krecconfigure.cpp
new file mode 100644
index 00000000..76d949f4
--- /dev/null
+++ b/krec/krecconfigure.cpp
@@ -0,0 +1,192 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#include "krecconfigure.h"
+#include "krecconfigure.moc"
+
+#include "krecglobal.h"
+
+#include <kgenericfactory.h>
+#include <kdebug.h>
+#include <qlayout.h>
+#include <qbuttongroup.h>
+#include <qradiobutton.h>
+#include <qcheckbox.h>
+#include <qlineedit.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kpushbutton.h>
+
+typedef KGenericFactory<KRecConfigGeneral, QWidget> KRecConfigGeneralFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_krec, KRecConfigGeneralFactory( "krec" ) )
+
+KRecConfigGeneral::KRecConfigGeneral( QWidget* p, const char*, const QStringList& s )
+ : KCModule( KRecConfigGeneralFactory::instance(), p, s )
+ , _layout( 0 ), _layout_display( 0 )
+ , _display_title( 0 )
+ , _displaybox( 0 ), _framebasebox( 0 )
+ , _display0( 0 ), _display1( 0 ), _display2( 0 ), _display3( 0 )
+ , _framebase30( 0 ), _framebase25( 0 ), _framebase75( 0 ), _framebaseother( 0 )
+ , _framebaseotherbox( 0 ), _framebaseotherlabel( 0 ), _framebaseotherline( 0 )
+ , _displaymode( 0 ), _framebase( 25 )
+{
+ _layout = new QBoxLayout( this, QBoxLayout::TopToBottom );
+
+ _layout->addSpacing( 10 );
+ _display_title = new QLabel( i18n( "<qt><b>Timedisplay Related Settings</b></qt>" ), this );
+ _layout->addWidget( _display_title, -100 );
+
+ _layout_display = new QBoxLayout( _layout, QBoxLayout::LeftToRight );
+ _layout->setStretchFactor( _layout_display, -100 );
+
+ _displaybox = new QButtonGroup( 1, Qt::Horizontal, i18n( "Timedisplay Style" ), this );
+ _layout_display->addWidget( _displaybox, 100 );
+ connect( _displaybox, SIGNAL( clicked( int ) ), this, SLOT( displaychanged( int ) ) );
+ _display0 = new QRadioButton( i18n( "Plain samples" ), _displaybox );
+ _display1 = new QRadioButton( i18n( "[hours:]mins:secs:samples" ), _displaybox );
+ _display2 = new QRadioButton( i18n( "[hours:]mins:secs:frames" ), _displaybox );
+ _display3 = new QRadioButton( i18n( "MByte.KByte" ), _displaybox );
+ _framebasebox = new QButtonGroup( 1, Qt::Horizontal, i18n( "Framebase" ), this );
+ _layout_display->addWidget( _framebasebox, 100 );
+ connect( _framebasebox, SIGNAL( clicked( int ) ), this, SLOT( framebasechanged( int ) ) );
+ _framebase30 = new QRadioButton( i18n( "30 frames per second (American TV)" ), _framebasebox );
+ _framebase25 = new QRadioButton( i18n( "25 frames per second (European TV)" ), _framebasebox );
+ _framebase75 = new QRadioButton( i18n( "75 frames per second (CD)" ), _framebasebox );
+ _framebaseother = new QRadioButton( i18n( "Other" ), _framebasebox );
+ _framebaseotherbox = new QHBox( _framebasebox );
+ _framebaseotherbox->setSpacing( 2 );
+ _framebaseotherlabel = new QLabel( i18n( "Other" ), _framebaseotherbox );
+ _framebaseotherline = new QLineEdit( _framebaseotherbox );
+ _framebaseotherline->setMaxLength( 10 );
+ _framebaseotherbox->setEnabled( false );
+ connect( _framebaseotherline, SIGNAL( textChanged( const QString& ) ), this, SLOT( framebaseotherchanged( const QString& ) ) );
+
+ _layout->addSpacing( 5 );
+ _verboseDisplayMode = new QCheckBox( i18n( "Show verbose times ( XXmins:XXsecs:XXframes instead of XX:XX::XX )" ), this );
+ connect( _verboseDisplayMode, SIGNAL( toggled( bool ) ), this, SLOT( verboseDisplayChanged( bool ) ) );
+ _layout->addWidget( _verboseDisplayMode );
+
+ _layout->addSpacing( 10 );
+ _other_title = new QLabel( i18n( "<qt><b>Miscellaneous Settings</b></qt>" ), this );
+ _layout->addWidget( _other_title );
+
+ _tipofday = new QCheckBox( i18n( "Show tip of the day at startup" ), this );
+ connect( _tipofday, SIGNAL( toggled( bool ) ), this, SLOT( tipofdaychanged( bool ) ) );
+ _layout->addWidget( _tipofday );
+ QBoxLayout* _tmplayout = new QBoxLayout( this, QBoxLayout::LeftToRight );
+ _enableAllMessages = new KPushButton( i18n( "Enable All Hidden Messages" ), this );
+ connect( _enableAllMessages, SIGNAL( clicked() ), this, SLOT( enableallmessagesclicked() ) );
+ _tmplayout->addWidget( _enableAllMessages );
+ QLabel* _tmplbl = new QLabel( i18n( "<qt><i>All messages with the \"Don't show this message again\" option are shown again after selecting this button.</i></qt>" ), this );
+ _tmplayout->addWidget( _tmplbl );
+ _layout->addLayout( _tmplayout );
+
+ _layout->addStretch( 100 );
+
+ load();
+}
+
+KRecConfigGeneral::~KRecConfigGeneral() {
+}
+
+void KRecConfigGeneral::load() {
+kdDebug( 60005 ) << k_funcinfo << endl;
+ defaults();
+ _displaymode = KRecGlobal::the()->timeFormatMode();
+ switch ( _displaymode % 100 ) {
+ default:
+ case 0: _display0->setChecked( true ); break;
+ case 1: _display1->setChecked( true ); break;
+ case 2: _display2->setChecked( true ); break;
+ case 3: _display3->setChecked( true ); break;
+ };
+ _verboseDisplayMode->setChecked( ( _displaymode / 100 == 1 ) );
+
+ _framebase = KRecGlobal::the()->frameBase();
+ switch ( _framebase ) {
+ case 30: _framebase30->setChecked( true ); break;
+ case 25: _framebase25->setChecked( true ); break;
+ case 75: _framebase75->setChecked( true ); break;
+ default:
+ _framebaseother->setChecked( true );
+ _framebaseotherbox->setEnabled( true );
+ _framebaseotherline->setText( QString::number( _framebase ) );
+ break;
+ };
+ kapp->config()->setGroup( "TipOfDay" );
+ _tip = kapp->config()->readBoolEntry( "RunOnStart", true );
+ _tipofday->setChecked( _tip );
+}
+
+void KRecConfigGeneral::save() {
+ KRecGlobal::the()->setTimeFormatMode( _displaymode );
+ KRecGlobal::the()->setFrameBase( _framebase );
+kdDebug(60005) << k_funcinfo << "Framebase=" << _framebase << endl;
+
+ kapp->config()->setGroup( "TipOfDay" );
+ kapp->config()->writeEntry( "RunOnStart", _tip );
+
+ kapp->config()->sync();
+ emit changed( false );
+}
+
+void KRecConfigGeneral::defaults() {
+ _display0->setChecked( true );
+ _framebase25->setChecked( true );
+}
+
+void KRecConfigGeneral::displaychanged( int index ) {
+ int verbose = _displaymode / 100;
+ if ( _displaybox->find( index ) == _display0 ) _displaymode = 0 + verbose * 100;
+ if ( _displaybox->find( index ) == _display1 ) _displaymode = 1 + verbose * 100;
+ if ( _displaybox->find( index ) == _display2 ) _displaymode = 2 + verbose * 100;
+ if ( _displaybox->find( index ) == _display3 ) _displaymode = 3 + verbose * 100;
+ emit changed( true );
+}
+void KRecConfigGeneral::framebasechanged( int index ) {
+ if ( _framebasebox->find( index ) == _framebase30 ) _framebase = 30;
+ if ( _framebasebox->find( index ) == _framebase25 ) _framebase = 25;
+ if ( _framebasebox->find( index ) == _framebase75 ) _framebase = 75;
+ if ( _framebasebox->find( index ) == _framebaseother ) {
+ _framebaseotherbox->setEnabled( true );
+ _framebase = _framebaseotherline->text().toInt();
+kdDebug(60005) << k_funcinfo << "Framebase=" << _framebase << endl;
+ } else
+ _framebaseotherbox->setEnabled( false );
+ emit changed( true );
+}
+void KRecConfigGeneral::framebaseotherchanged( const QString& text ) {
+ _framebase = text.toInt();
+kdDebug(60005) << k_funcinfo << "Framebase=" << _framebase << endl;
+ emit changed( true );
+}
+void KRecConfigGeneral::verboseDisplayChanged( bool n ) {
+ if ( n && _displaymode < 100 ) _displaymode += 100;
+ if ( !n && _displaymode >= 100 ) _displaymode -= 100;
+ emit changed( true );
+}
+
+void KRecConfigGeneral::tipofdaychanged( bool n ) {
+ _tip = n;
+ emit changed( true );
+}
+
+void KRecConfigGeneral::enableallmessagesclicked() {
+kdDebug(60005) << k_funcinfo << endl;
+ KMessageBox::enableAllMessages();
+}
+
diff --git a/krec/krecconfigure.h b/krec/krecconfigure.h
new file mode 100644
index 00000000..13416c44
--- /dev/null
+++ b/krec/krecconfigure.h
@@ -0,0 +1,67 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#ifndef KREC_CONFIGURE_H
+#define KREC_CONFIGURE_H
+
+#include <kcmodule.h>
+#include <qstringlist.h>
+#include <qstring.h>
+
+class QBoxLayout;
+class QRadioButton;
+class QButtonGroup;
+class QLineEdit;
+class QHBox;
+class QLabel;
+class QCheckBox;
+class KPushButton;
+
+class KRecConfigGeneral : public KCModule {
+ Q_OBJECT
+public:
+ KRecConfigGeneral( QWidget*, const char* =0, const QStringList& = QStringList() );
+ ~KRecConfigGeneral();
+
+ void load();
+ void save();
+ void defaults();
+private slots:
+
+ void displaychanged( int );
+ void framebasechanged( int );
+ void framebaseotherchanged( const QString& );
+ void verboseDisplayChanged( bool );
+
+ void tipofdaychanged( bool );
+ void enableallmessagesclicked();
+private:
+ QBoxLayout *_layout, *_layout_display;
+ QLabel *_display_title, *_other_title;
+ QButtonGroup *_displaybox, *_framebasebox;
+ QRadioButton *_display0, *_display1, *_display2, *_display3;
+ QRadioButton *_framebase30, *_framebase25, *_framebase75, *_framebaseother;
+ QHBox *_framebaseotherbox;
+ QLabel *_framebaseotherlabel;
+ QLineEdit *_framebaseotherline;
+
+ QCheckBox *_tipofday, *_verboseDisplayMode;
+ KPushButton *_enableAllMessages;
+
+ int _displaymode, _framebase;
+ bool _tip;
+};
+
+#endif
+
+// vim:sw=4:ts=4
diff --git a/krec/krecexport_template.cpp b/krec/krecexport_template.cpp
new file mode 100644
index 00000000..43fa023c
--- /dev/null
+++ b/krec/krecexport_template.cpp
@@ -0,0 +1,89 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#include "krecexport_template.h"
+#include "krecexport_template.moc"
+
+#include "krecglobal.h"
+
+#include <qtimer.h>
+#include <kdebug.h>
+
+KRecExportItem::KRecExportItem( QObject* p, const char* n, const QStringList& ) : QObject( p,n ), _running( false ) {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+}
+KRecExportItem::~KRecExportItem() {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+}
+int KRecExportItem::samplingRate() const {
+ //kdDebug( 60005 ) << k_funcinfo << _samplingRate << endl;
+ return _samplingRate;
+}
+int KRecExportItem::bits() const {
+ //kdDebug( 60005 ) << k_funcinfo << _bits << endl;
+ return _bits;
+}
+int KRecExportItem::channels() const {
+ //kdDebug( 60005 ) << k_funcinfo << _channels << endl;
+ return _channels;
+}
+
+void KRecExportItem::registerAtGlobal( KRecExportItem* item ) {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+ bool registered = false;
+ if ( !registered ) registered = KRecGlobal::the()->registerExport( item );
+ //if ( registered ) kdDebug( 60005 ) << "Register successful!" << endl;
+ // else kdDebug( 60005 ) << "Register NOT successful!" << endl;
+}
+
+void KRecExportItem::initialize( int samplingRate, int bits, int channels ) {
+kdDebug( 60005 ) << k_funcinfo << "samplingRate:" << samplingRate << " bits:" << bits << " channels:" << channels << endl;
+ _samplingRate = samplingRate;
+ _bits = bits;
+ _channels = channels;
+}
+bool KRecExportItem::start() {
+kdDebug( 60005 ) << k_funcinfo << endl;
+ if ( !running() ) {
+ if ( process() ) {
+ _running = true;
+ QTimer::singleShot( 0, this, SLOT( process() ) );
+ emit running( running() );
+ }
+ return true;
+ } else return false;
+}
+
+void KRecExportItem::stop() {
+kdDebug( 60005 ) << k_funcinfo << endl;
+ _running = false;
+ emit running( running() );
+}
+
+Q_INT16 KRecExportItem::read16( char* array, int index ) {
+ Q_INT16 tmp;
+ tmp = array[ index ] + ( array[ index + 1 ] << 8 ) & 0xff;
+ return tmp;
+}
+/// Helper: writes an integer into an char* formated for wave-files
+void KRecExportItem::write16( char* array, Q_INT16 value, int index ) {
+ array[ index ] = ( value >> 0 ) & 0xff;
+ array[ index + 1 ] = ( value >> 8 ) & 0xff;
+}
+void KRecExportItem::write32( char* array, Q_INT32 value, int index ) {
+ write16( array, value, index );
+ array[ index + 2 ] = ( value >> 16 ) & 0xff;
+ array[ index + 3 ] = ( value >> 24 ) & 0xff;
+}
+
+// vim:sw=4:ts=4
diff --git a/krec/krecexport_template.h b/krec/krecexport_template.h
new file mode 100644
index 00000000..8a1c4a03
--- /dev/null
+++ b/krec/krecexport_template.h
@@ -0,0 +1,99 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#ifndef KREC_EXPORT_TEMPLATE_H
+#define KREC_EXPORT_TEMPLATE_H
+
+#include <qobject.h>
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qcstring.h>
+#include <arts/common.h>
+
+/**
+ * This class is the template for all exportitems in KRec.
+ *
+ * To register your ExportItem you have to do @code registerAtGlobal( this ); @code
+ * at the end of your constructor.
+ *
+ * @author Arnold Krille <arnold@arnoldarts.de>
+*/
+class KRecExportItem : public QObject {
+ Q_OBJECT
+private:
+ /// Controls wether an export is running, or not...
+ bool _running;
+ int _samplingRate, _bits, _channels;
+protected:
+ KRecExportItem( QObject*, const char* =0, const QStringList& =0 );
+
+ /**
+ * This registers this class at the KRecGlobal.
+ */
+ static void registerAtGlobal( KRecExportItem* );
+
+ int samplingRate() const;
+ int bits() const;
+ int channels() const;
+
+ /// Helpers for reading and writing to an char-array
+ Q_INT16 read16( char* array, int index );
+ void write16( char* array, Q_INT16 value, int index );
+ void write32( char* array, Q_INT32 value, int index );
+public:
+ ~KRecExportItem();
+
+ /**
+ * Returns a new Item for your export.
+ */
+ virtual KRecExportItem* newItem()=0;
+
+ /// Returns a list with the extensions.
+ virtual QStringList extensions()=0;
+ /// Returns the export format.
+ virtual QString exportFormat()=0;
+
+ bool running() const { return _running; }
+public slots:
+ /**
+ * Initializes the sound-settings.
+ * Has to be called before the initialize( const QString & ) function.
+ */
+ void initialize( int samplingRate, int bits, int channels );
+ /// Initializes the export.
+ virtual bool initialize( const QString &filename )=0;
+ /**
+ * Start the real export (including the emission of @see getData() while running=true).
+ * Best way to do this is to use @see QTimer::singleShot() since then the events are
+ * put at the end of the EventLoop not blocking the gui and everything else.
+ */
+ bool start();
+ virtual bool process() =0;
+
+ /// Stops the export by setting running=false.
+ void stop();
+ /// Finish the export.
+ virtual bool finalize()=0;
+ //virtual void data( QByteArray& );
+signals:
+ /// Is emitted when this Item wants data to export.
+ void getData( QByteArray& );
+
+ void running( bool );
+};
+
+
+#endif
+
+// vim:sw=4:ts=4
diff --git a/krec/krecexport_wave.cpp b/krec/krecexport_wave.cpp
new file mode 100644
index 00000000..80ea63d7
--- /dev/null
+++ b/krec/krecexport_wave.cpp
@@ -0,0 +1,135 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#include "krecexport_wave.h"
+#include "krecexport_wave.moc"
+
+#include "krecglobal.h"
+
+#include <kdebug.h>
+#include <ktempfile.h>
+#include <qfile.h>
+#include <qtimer.h>
+#include <kgenericfactory.h>
+
+K_EXPORT_COMPONENT_FACTORY( libkrecexport_wave, KGenericFactory<KRecExport_Wave> )
+
+KRecExport_Wave krecExportWave( 0 );
+
+KRecExport_Wave::KRecExport_Wave( QObject* p, const char* n, const QStringList& )
+ : KRecExportItem( p,n )
+ , _file( 0 )
+{
+kdDebug( 60005 ) << k_funcinfo << endl;
+ registerAtGlobal( this );
+kdDebug( 60005 ) << "Registered Exports: " << KRecGlobal::the()->exportFormats() << endl;
+}
+KRecExport_Wave::~KRecExport_Wave() {
+kdDebug( 60005 ) << k_funcinfo << endl;
+}
+
+KRecExport_Wave* KRecExport_Wave::newItem() {
+kdDebug( 60005 ) << k_funcinfo << endl;
+ return new KRecExport_Wave( 0 );
+}
+
+QStringList KRecExport_Wave::extensions() {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+ QStringList tmp;
+ tmp << "*.wav" << "*.WAV";
+ return tmp;
+}
+
+bool KRecExport_Wave::initialize( const QString &filename ) {
+kdDebug( 60005 ) << k_funcinfo << endl;
+ if ( !_file ) {
+ _file = new QFile( filename );
+ if ( _file->open( IO_Raw|IO_WriteOnly ) ) {
+ /// Write an empty Wave-header...
+ for ( int i=0; i<44; i++ )
+ _file->putch( 0 );
+ } else return false;
+ return true;
+ } else return false;
+}
+bool KRecExport_Wave::process() {
+ //kdDebug( 60005 ) << k_funcinfo << running << endl;
+ if ( _file ) {
+ if ( running() ) {
+ QByteArray bytearray( 4096 );
+ emit getData( bytearray );
+ _file->writeBlock( bytearray );
+ QTimer::singleShot( 10, this, SLOT( process() ) );
+ }
+ return true;
+ } else return false;
+}
+bool KRecExport_Wave::finalize() {
+kdDebug( 60005 ) << k_funcinfo << endl;
+ if ( _file ) {
+ // PCM-Compatible WAVE Header
+ // bytes variable description
+ // 0 - 3 'RIFF'
+ // 4 - 7 wRiffLength length of file minus the 8 byte riff header
+ // 8 - 11 'WAVE'
+ // 12 - 15 'fmt '
+ // 16 - 19 wFmtSize length of format chunk minus 8 byte header
+ // 20 - 21 wFormatTag identifies PCM, ULAW etc
+ // 22 - 23 wChannels number of channels
+ // 24 - 27 dwSamplesPerSecond samples per second per channel
+ // 28 - 31 dwAvgBytesPerSec non-trivial for compressed formats
+ // 32 - 33 wBlockAlign basic block size
+ // 34 - 35 wBitsPerSample non-trivial for compressed formats
+ // 36 - 39 'data'
+ // 40 - 43 dwDataLength length of data chunk minus 8 byte header
+ // 44 - (dwDataLength + 43) the data
+
+ // Basic 16b,2c,44kHz Wave-Header
+ char riffHeader[] =
+ {
+ 0x52, 0x49, 0x46, 0x46, // 0 "AIFF"
+ 0x00, 0x00, 0x00, 0x00, // 4 wavSize
+ 0x57, 0x41, 0x56, 0x45, // 8 "WAVE"
+ 0x66, 0x6d, 0x74, 0x20, // 12 "fmt "
+ 0x10, 0x00, 0x00, 0x00, // 16
+ 0x01, 0x00, 0x02, 0x00, // 20
+ 0x44, 0xac, 0x00, 0x00, // 24
+ 0x10, 0xb1, 0x02, 0x00, // 28
+ 0x04, 0x00, 0x10, 0x00, // 32
+ 0x64, 0x61, 0x74, 0x61, // 36 "data"
+ 0x00, 0x00, 0x00, 0x00 // 40 byteCount
+ };
+
+ long wavSize = _file->size() - 8;
+ write32( riffHeader, wavSize, 4 );
+
+ write16( riffHeader, channels(), 22 );
+ write32( riffHeader, samplingRate(), 24 );
+ write16( riffHeader, bits(), 34 );
+
+ long byteCount = wavSize - 44;
+ write32( riffHeader, byteCount, 40 );
+
+ _file->at( 0 );
+ _file->writeBlock( riffHeader, 44 );
+ _file->at( _file->size() );
+
+ _file->close();
+ delete _file;
+ _file = 0;
+
+ return true;
+ } else return false;
+}
+
+// vim:sw=4:ts=4
diff --git a/krec/krecexport_wave.h b/krec/krecexport_wave.h
new file mode 100644
index 00000000..f1336970
--- /dev/null
+++ b/krec/krecexport_wave.h
@@ -0,0 +1,43 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#ifndef KREC_EXPORT_WAVE_H
+#define KREC_EXPORT_WAVE_H
+
+#include "krecexport_template.h"
+
+class KTempFile;
+class QFile;
+
+class KRecExport_Wave : public KRecExportItem {
+ Q_OBJECT
+public:
+ KRecExport_Wave( QObject*, const char* =0, const QStringList& =0 );
+ ~KRecExport_Wave();
+
+ KRecExport_Wave* newItem();
+
+ QStringList extensions();
+ QString exportFormat() { return QString( "Wave" ); }
+public slots:
+ bool initialize( const QString & );
+ bool process();
+ bool finalize();
+
+private:
+ QFile* _file;
+};
+
+#endif
+
+// vim:sw=4:ts=4
diff --git a/krec/krecfile.cpp b/krec/krecfile.cpp
new file mode 100644
index 00000000..6e4b2b8f
--- /dev/null
+++ b/krec/krecfile.cpp
@@ -0,0 +1,457 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#include "krecfile.h"
+#include "krecfile.moc"
+
+#include "krecglobal.h"
+
+#include "krecnewproperties.h"
+
+#include <kdebug.h>
+#include <kapplication.h>
+#include <ktempdir.h>
+#include <ktempfile.h>
+#include <ktar.h>
+#include <kio/job.h>
+#include <klocale.h>
+#include <ksimpleconfig.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <kmessagebox.h>
+#include <qtimer.h>
+
+#include <qdatastream.h>
+#include <math.h>
+
+void KRecFile::init() {
+ _pos = 0;
+ _size = 0;
+ _filename = QString::null;
+ _currentBuffer = 0;
+ _dir = new KTempDir();
+ _config = new KSimpleConfig( _dir->name()+"project.rc", false );
+}
+
+KRecFile::KRecFile( QObject* p, const char* n )
+ : QObject( p,n )
+ , _saved( false )
+{
+ init();
+ kdDebug( 60005 ) << k_funcinfo << "_dir->name(): " << _dir->name() << endl;
+ _dir->setAutoDelete( true );
+ KRecNewProperties* dialog = new KRecNewProperties( KRecGlobal::the()->mainWidget() );
+
+ if ( dialog->usedefaults() )
+ KRecGlobal::the()->message( i18n( "Using default properties for the new file" ) );
+ else
+ dialog->exec();
+
+ _samplerate = dialog->samplerate();
+ _channels = dialog->channels();
+ _bits = dialog->bits();
+
+ saveProps();
+
+ delete dialog;
+}
+KRecFile::KRecFile( const QString &filename, QObject* p, const char* n )
+ : QObject( p,n )
+ , _saved( true )
+{
+ init();
+ _filename = filename;
+ kdDebug( 60005 ) << k_funcinfo << "_dir->name(): " << _dir->name() << endl;
+ _dir->setAutoDelete( true );
+
+ KTar *tar = new KTar( _filename, "application/x-gzip" );
+ tar->open( IO_ReadOnly );
+ int i=0; while ( _filename.find( '/', i ) != -1 ) i++; // Find last '/'
+ QString basename = _filename.right( _filename.length()-i );
+ basename = basename.left( basename.length()-5 );
+
+ const KArchiveDirectory *dir = dynamic_cast<const KArchiveDirectory*>( tar->directory()->entry( basename ) );
+
+ dir->copyTo( _dir->name() );
+
+ delete _config;
+ _config = new KSimpleConfig( _dir->name()+"project.rc", false );
+ loadProps();
+ int c = _config->readNumEntry( "Files" );
+ //kdDebug( 60005 ) << c << " Files to load" << endl;
+ for ( int i=0; i<c; i++ ) {
+ //kdDebug( 60005 ) << "Loading file " << i << endl;
+ _config->setGroup( "File-" + QString::number( i ) );
+ newBuffer( KRecBuffer::fromConfig( _config, _dir->qDir(), this ) );
+ }
+ KRecGlobal::the()->message( i18n( "\'%1\' loaded." ).arg( filename ) );
+
+ delete tar;
+
+ _saved = true;
+ //kdDebug( 60005 ) << "Buffers opened: " << _buffers.count() << endl;
+}
+KRecFile::~KRecFile() {
+ kdDebug( 60005 ) << k_funcinfo << endl;
+ QValueList<KRecBuffer*>::iterator it;
+ for ( it = _buffers.begin(); it != _buffers.end(); ++it )
+ delete ( *it );
+ _buffers.clear();
+ delete _dir;
+ delete _config;
+ //kdDebug( 60005 ) << k_funcinfo << "done." << endl;
+}
+
+QString KRecFile::filename() { return _filename; }
+void KRecFile::filename( const QString &n ) {
+ if ( _filename!=n ) {
+ _filename = n;
+ emit filenameChanged( _filename );
+ }
+}
+
+void KRecFile::writeData( Arts::mcopbyte* /*data*/, uint /*length*/ ) {
+ kdDebug( 60005 ) << k_funcinfo << endl;
+}
+void KRecFile::writeData( QByteArray* data ) {
+ kdDebug( 60005 ) << k_funcinfo << endl;
+ if ( _currentBuffer!=-1 ) _buffers[ _currentBuffer ]->writeData( data );
+ _saved = false;
+}
+void KRecFile::writeData( QByteArray& data ) {
+ if ( _currentBuffer!=-1 ) _buffers[ _currentBuffer ]->writeData( data );
+ _saved = false;
+}
+
+void KRecFile::save( const QString &fname ) {
+ QString filetosave = fname;
+
+ //kdDebug( 60005 ) << k_funcinfo << filename << endl;
+ if ( saved() ) {
+ KRecGlobal::the()->message( i18n( "No need to save." ) );
+ return;
+ }
+
+ KRecGlobal::the()->message( i18n( "Saving in progress..." ) );
+ filename( fname );
+ QString tmpname;
+ {
+ KTempFile *tmp = new KTempFile();
+ tmp->setAutoDelete( true );
+ tmpname = tmp->name();
+ delete tmp;
+ }
+ saveProps();
+
+ KTar *tar = new KTar( tmpname, "application/x-gzip" );
+ tar->open( IO_WriteOnly );
+ int i=0; while ( fname.find( '/', i ) != -1 ) i++; // Find last '/'
+ QString basename = fname.right( fname.length()-i );
+ if ( basename.endsWith( ".krec" ) )
+ basename = basename.left( basename.length()-5 );
+ else {
+ filetosave = fname + ".krec";
+ filename( filetosave );
+ }
+ tar->addLocalDirectory( _dir->name(), basename );
+
+ delete tar;
+ KIO::file_move( tmpname, filetosave, -1, true, false, true );
+
+ KRecGlobal::the()->message( i18n( "Saving \"%1\" was successful." ).arg( filename() ) );
+ _saved = true;
+}
+
+void KRecFile::exportwave( const QString &filename ) {
+ kdDebug( 60005 ) << k_funcinfo << filename << endl;
+}
+
+QByteArray* KRecFile::getData( int ) {
+ kdDebug( 60005 ) << k_funcinfo << endl;
+ return 0;
+}
+
+void KRecFile::getData( QByteArray& data ) {
+ KRecBuffer* current = getTopBuffer_buffer( _pos );
+ if ( current ) {
+ //kdDebug( 60005 ) << "_pos=" << _pos << "(" << samplesToOffset( _pos ) << ") current->startpos()=" << current->startpos() << "(" << samplesToOffset( current->startpos() ) << ") that is " << samplesToOffset( _pos - current->startpos() ) << endl;
+ current->setPos( samplesToOffset( _pos - current->startpos() ) );
+ current->getData( data );
+ } else {
+ for ( uint i=0; i<data.size(); i++ )
+ data[ i ] = 0;
+ }
+ newPos( _pos + offsetToSamples( data.size() ) );
+ if ( position() >= size() ) emit endReached();
+}
+
+
+// * * * Position * * *
+void KRecFile::newPos( int p ) {
+ if ( _pos != p ) {
+ _pos = p;
+ emit posChanged( _pos );
+ }
+}
+void KRecFile::newPos( KRecBuffer* buffer, QIODevice::Offset p ) {
+ newPos( buffer->startpos() + offsetToSamples( p ) );
+}
+void KRecFile::newSize( KRecBuffer* buffer, QIODevice::Offset p ) {
+ if ( buffer->startpos() + offsetToSamples( p ) > _size ) {
+ _size = buffer->startpos() + offsetToSamples( p );
+ }
+ emit sizeChanged( _size );
+}
+
+
+/// * * * Frames <-> Offset and more * * *
+int KRecFile::offsetToSamples( QIODevice::Offset n ) const {
+ int out = n / channels();
+ if ( bits() == 16 ) out /= 2;
+ return out;
+}
+QIODevice::Offset KRecFile::samplesToOffset( int n ) const {
+ QIODevice::Offset out = n * channels();
+ if ( bits() == 16 ) out *= 2;
+ return out;
+}
+
+/// * * * Properties <-> KConfig * * *
+void KRecFile::saveProps() {
+ kdDebug( 60005 ) << k_funcinfo << endl;
+ _config->setGroup( "General" );
+ _config->writeEntry( "Samplerate", _samplerate );
+ _config->writeEntry( "Bits", _bits );
+ _config->writeEntry( "Channels", _channels );
+
+ _config->writeEntry( "Files", _buffers.count() );
+ for ( uint i=0; i<_buffers.count(); i++ ) {
+ //kdDebug( 60005 ) << "Writing config " << i << endl;
+ _config->setGroup( "File-" + QString::number( i ) );
+ _buffers[ i ]->writeConfig( _config );
+ }
+ _config->sync();
+}
+void KRecFile::loadProps() {
+ kdDebug( 60005 ) << k_funcinfo << endl;
+ _config->setGroup( "General" );
+ _samplerate = _config->readNumEntry( "Samplerate", 44100 );
+ _bits = _config->readNumEntry( "Bits", 16 );
+ _channels = _config->readNumEntry( "Channels", 2 );
+}
+
+
+/// * * * NewBuffer * * *
+void KRecFile::newBuffer( const QString &filename ) {
+ kdDebug( 60005 ) << k_funcinfo << filename << endl;
+ newBuffer( new KRecBuffer( filename, _pos, true, this ) );
+}
+void KRecFile::newBuffer( KRecBuffer* buffer ) {
+ kdDebug( 60005 ) << k_funcinfo << endl;
+ connect( buffer, SIGNAL( posChanged( KRecBuffer*, QIODevice::Offset ) ), this, SLOT( newPos( KRecBuffer*, QIODevice::Offset ) ) );
+ connect( buffer, SIGNAL( sizeChanged( KRecBuffer*, QIODevice::Offset ) ), this, SLOT( newSize( KRecBuffer*, QIODevice::Offset ) ) );
+ connect( buffer, SIGNAL( deleteSelf( KRecBuffer* ) ), this, SLOT( deleteBuffer( KRecBuffer* ) ) );
+ _buffers.append( buffer );
+ newSize( buffer, buffer->size() );
+ _currentBuffer = _buffers.findIndex( buffer );
+ emit sNewBuffer( buffer );
+ _saved = false;
+}
+void KRecFile::newBuffer() {
+ kdDebug( 60005 ) << k_funcinfo << endl;
+ newBuffer( _dir->name() + "file" + QString::number( _buffers.count() ) + ".raw" );
+}
+
+void KRecFile::deleteBuffer( KRecBuffer* b ) {
+ kdDebug( 60005 ) << k_funcinfo << b << endl;
+ emit sDeleteBuffer( b );
+ delete b;
+ if ( _buffers.remove( b ) )
+ _currentBuffer = -1;
+ KRecGlobal::the()->message( i18n( "Part deleted." ) );
+ _saved = false;
+}
+
+/// * * * helper * * *
+KRecBuffer* KRecFile::getTopBuffer_buffer( int pos ) {
+ //kdDebug( 60005 ) << k_funcinfo << pos << endl;
+ QValueList<KRecBuffer*>::iterator it = _buffers.begin();
+ KRecBuffer* out = 0;
+ while ( it != _buffers.end() ) {
+ if ( ( *it )->startpos() <= pos && offsetToSamples( ( *it )->size() ) + ( *it )->startpos() > pos && ( *it )->active() )
+ out = ( *it );
+ ++it;
+ }
+ return out;
+}
+int KRecFile::getTopBuffer_int( int pos ) {
+ return _buffers.findIndex( getTopBuffer_buffer( pos ) );
+}
+
+
+/// * * * KRecBuffer * * *
+KRecBuffer::KRecBuffer( const QString &filename, int startpos, bool a, KRecFile* p, const char* n )
+ : QObject( p,n )
+ , _krecfile( p )
+ , _file( new QFile( filename ) )
+ , _stream( new QDataStream( _file ) )
+ , _fileinfo( new QFileInfo( filename ) )
+ , _active( a )
+ , _pos( 0 ), _start( startpos )
+ , _title( _fileinfo->fileName() )
+ , _comment( QString::null )
+{
+ kdDebug( 60005 ) << k_funcinfo << filename << " " << startpos << endl;
+ _open = _file->open( IO_Raw | IO_ReadWrite );
+ setPos( _file->at() );
+ if ( _open ) kdDebug( 60005 ) << k_funcinfo << "Open successfull!" << endl;
+ else kdDebug( 60005 ) << endl << k_funcinfo << "Could not open file!" << endl << endl;
+}
+KRecBuffer::~KRecBuffer() {
+ kdDebug( 60005 ) << k_funcinfo << endl;
+ if ( _open ) {
+ _file->close();
+ _open = false;
+ _file->remove();
+ }
+ //kdDebug( 60005 ) << k_funcinfo << "done." << endl;
+}
+
+void KRecBuffer::writeConfig( KConfig* config ) {
+ //kdDebug( 60005 ) << k_funcinfo << config << endl;
+ config->writeEntry( "Filename", _fileinfo->fileName() );
+ config->writeEntry( "StartPos", _start );
+ config->writeEntry( "Activated", _active );
+ config->writeEntry( "Title", _title );
+ config->writeEntry( "Comment", _comment );
+}
+
+KRecBuffer* KRecBuffer::fromConfig( KConfig* config, QDir* dir, KRecFile* p, const char* n ) {
+ kdDebug( 60005 ) << k_funcinfo << config << endl;
+ KRecBuffer* tmp = new KRecBuffer( dir->path() + "/" + config->readEntry( "Filename" ),
+ config->readNumEntry( "StartPos" ),
+ config->readBoolEntry( "Activated", true ),
+ p, n );
+ tmp->setTitle( config->readEntry( "Title", tmp->filename() ) );
+ tmp->setComment( config->readEntry( "Comment", QString::null ) );
+ return tmp;
+}
+
+void KRecBuffer::writeData( Arts::mcopbyte* /*data*/, uint /*length*/ ) {
+ kdDebug( 60005 ) << k_funcinfo << endl;
+}
+void KRecBuffer::writeData( QByteArray* data ) {
+kdDebug( 60005 ) << k_funcinfo << endl;
+ writeData( *data );
+/* if ( _open ) {
+ _file->at( _pos );
+ _file->writeBlock( *data );
+ setPos( _file->at() );
+ emit sizeChanged( this, size() );
+ }*/
+}
+void KRecBuffer::writeData( QByteArray& data ) {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+ if ( _open ) {
+ _file->at( _pos );
+ _file->writeBlock( data );
+ setPos( _file->at() );
+ emit sizeChanged( this, size() );
+ }
+}
+
+void KRecBuffer::getData( QByteArray& data ) {
+ //kdDebug( 60005 ) << k_funcinfo << "data.size()" << data.size() << " _pos" << _pos << endl;
+ if ( _pos > _file->size() )
+ kdWarning() << "Trying to access behind file!" << endl;
+ else {
+ if ( _open ) {
+ _file->at( _pos );
+ //kdDebug( 60005 ) << "Reading " << _file->readBlock( ( char* )data.data(), data.size() ) << "bytes into data." << endl;
+ //kdDebug( 60005 ) << "data.size()" << data.size() << endl;
+ for ( uint i=0; i<data.size(); i++ ) {
+ if ( !_file->atEnd() )
+ data.data()[ i ] = _file->getch();
+ else
+ data.data()[ i ] = 0;
+ }
+ //setPos( _file->at() );
+ }
+ }
+}
+
+void KRecBuffer::setPos( QIODevice::Offset p ) {
+ if ( p!=_pos ) {
+ _pos = p;
+ emit posChanged( this, _pos );
+//kdDebug( 60005 ) << k_funcinfo << _pos << endl;
+ }
+}
+
+int KRecBuffer::startpos() const { return _start; }
+QIODevice::Offset KRecBuffer::size() const { return _file->size(); }
+
+QString KRecBuffer::filename() const { return _fileinfo->fileName(); }
+QString KRecBuffer::title() const { return _title; }
+QString KRecBuffer::comment() const { return _comment; }
+
+void KRecBuffer::setTitle( const QString &n ) {
+ if ( _title != n ) {
+ _title = n;
+ emit somethingChanged();
+ }
+}
+
+void KRecBuffer::setComment( const QString &n ) {
+ if ( _comment != n ) {
+ _comment = n;
+ emit somethingChanged();
+ }
+}
+
+bool KRecBuffer::active() const { return _active; }
+void KRecBuffer::setActive( bool n ) {
+ if ( _active != n ) {
+ _active = n;
+ emit activeChanged( _active );
+ emit somethingChanged();
+ }
+}
+
+void KRecBuffer::deleteBuffer() {
+ if ( KMessageBox::warningContinueCancel( KRecGlobal::the()->mainWidget(), i18n( "Do you really want to delete the selected part '%1'?" ).arg( filename() ), i18n("Delete Part?"), KStdGuiItem::del() ) == KMessageBox::Continue )
+ _krecfile->deleteBuffer( this );
+}
+
+float KRecBuffer::getSample( int pos, int /*channel*/ ) {
+ Q_INT16 tmp16;
+ Q_INT8 tmp8;
+ float out;
+ _file->at( _krecfile->samplesToOffset( pos ) );
+ if ( _krecfile->bits() == 16 ) {
+ *_stream >> tmp16;
+ out = tmp16 / 65535.0;
+ }
+ else {
+ *_stream >> tmp8;
+ out = tmp8 / 65535.0;
+ }
+ return out;
+}
+float* KRecBuffer::getsamples( int start, int end, int channel ) {
+ float* tmp = new float[ end-start ];
+ for ( int i=start; i<end; ++i )
+ tmp[ i ] = getSample( i, channel );
+ return tmp;
+}
+
diff --git a/krec/krecfile.h b/krec/krecfile.h
new file mode 100644
index 00000000..ba307838
--- /dev/null
+++ b/krec/krecfile.h
@@ -0,0 +1,201 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#ifndef KRECFILE_H
+#define KRECFILE_H
+
+#include <qobject.h>
+#include <qstring.h>
+#include <qvaluelist.h>
+
+#include <arts/common.h>
+
+class KRecBuffer;
+class KTempDir;
+class KSimpleConfig;
+class QFile;
+class KRecFileViewWidget;
+
+class KRecFile : virtual public QObject {
+ Q_OBJECT
+ friend class KRecFileWidget;
+public:
+ KRecFile( QObject*, const char* =0 );
+ KRecFile( const QString &, QObject*, const char* =0 );
+ ~KRecFile();
+
+ QString filename();
+ void filename( const QString &);
+
+ bool saved() const { return _saved; }
+ int samplerate() const { return _samplerate; }
+ int channels() const { return _channels; }
+ int bits() const { return _bits; }
+
+ QIODevice::Offset offsetSize() const { return samplesToOffset( _size ); }
+ QIODevice::Offset offsetPosition() const { return samplesToOffset( _pos ); }
+
+ int size() const { return _size; }
+ int position() const { return _pos; }
+
+ /// Calculates an offset into a frames-value depending on the files settings (channels and bits)
+ int offsetToSamples( QIODevice::Offset ) const;
+ QIODevice::Offset samplesToOffset( int ) const;
+public slots:
+ /// Creates a new empty buffer and marks it for recording.
+ void newBuffer();
+
+ /// Deletes the buffer from the file.
+ void deleteBuffer( KRecBuffer* =0 );
+
+ void writeData( Arts::mcopbyte* data, uint length );
+ void writeData( QByteArray* );
+ void writeData( QByteArray& );
+
+ void save( const QString & );
+ void exportwave( const QString & );
+
+ /// Fills a QByteArray with values from the specified Offset.
+ QByteArray* getData( int );
+
+ void getData( QByteArray& );
+
+ void newPos( int );
+
+private slots:
+ void newPos( KRecBuffer*, QIODevice::Offset );
+ void newSize( KRecBuffer*, QIODevice::Offset );
+signals:
+ /// Position signals
+ void posChanged( int );
+ void sizeChanged( int );
+ /// The end of the file is reached, you should stop reading.
+ void endReached();
+
+ /// GUI-Signals
+ void sNewBuffer( KRecBuffer* );
+ void sDeleteBuffer( KRecBuffer* );
+
+ void filenameChanged( const QString & );
+private:
+ void saveProps();
+ void loadProps();
+ /**
+ Creates a new buffer for a given file
+ @param filename Name of the file
+ */
+ void newBuffer( const QString &filename );
+ /**
+ Adds buffer to the buffers.
+ @param buffer The to be added buffer
+ */
+ void newBuffer( KRecBuffer* buffer );
+
+ /// Get the top-most-buffer at the specified position.
+ int getTopBuffer_int( int );
+ KRecBuffer* getTopBuffer_buffer( int );
+
+ bool _saved;
+ QString _filename;
+ int _samplerate, _channels, _bits;
+ int _currentBuffer;
+ QValueList<KRecBuffer*> _buffers;
+ KTempDir *_dir;
+ KSimpleConfig *_config;
+ int _pos, _size;
+
+ void init();
+};
+
+class QFile;
+class QDir;
+class QFileInfo;
+class KConfig;
+class QDataStream;
+
+class KRecBuffer : virtual public QObject {
+ Q_OBJECT
+public:
+ KRecBuffer( const QString &, int, bool, KRecFile*, const char* =0 );
+ ~KRecBuffer();
+
+ // After how many samples in the File this Buffer starts...
+ int startpos() const;
+
+ QIODevice::Offset size() const;
+ int sizeInSamples() const { return _krecfile->offsetToSamples( size() ); }
+
+ /**
+ Writes out its Config
+ It doesn't set the group.
+ @param config the KConfig where the data gets written
+ */
+ void writeConfig( KConfig* config );
+
+ /**
+ restores a KRecBuffer from Config
+ The group has to be set the right way.
+ @param config The configuration thats read
+ @param dir The directory where the file is saved
+ */
+ static KRecBuffer* fromConfig( KConfig* config, QDir* dir, KRecFile* p, const char* n=0 );
+
+ QString filename() const;
+
+ bool active() const;
+
+ QString title() const;
+ QString comment() const;
+public slots:
+ /// writes the data into the buffer
+ void writeData( Arts::mcopbyte* data, uint length );
+ void writeData( QByteArray* );
+ void writeData( QByteArray& );
+ /// gets the data from the stream
+ void getData( QByteArray& );
+
+ void setPos( QIODevice::Offset );
+
+ void setActive( bool );
+
+ void deleteBuffer();
+
+ void setTitle( const QString & );
+ void setComment( const QString & );
+
+ /// Returns the sample at the specified position and channel.
+ float getSample( int pos, int channel );
+ float* getsamples( int start, int end, int channel );
+signals:
+ void posChanged( KRecBuffer*, QIODevice::Offset );
+ void sizeChanged( KRecBuffer*, QIODevice::Offset );
+ void activeChanged( bool );
+
+ /// Is emitted when something has changed.
+ void somethingChanged();
+
+ void deleteSelf( KRecBuffer* );
+private:
+ KRecFile* _krecfile;
+ QFile* _file;
+ QDataStream* _stream;
+ QFileInfo* _fileinfo;
+ bool _open, _active;
+ QIODevice::Offset _pos;
+ int _start;
+ QString _title, _comment;
+};
+
+#endif
+
+// vim:sw=4:ts=4
diff --git a/krec/krecfileview.cpp b/krec/krecfileview.cpp
new file mode 100644
index 00000000..b3604412
--- /dev/null
+++ b/krec/krecfileview.cpp
@@ -0,0 +1,89 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#include "krecfileview.h"
+#include "krecfileview.moc"
+
+#include "krecfilewidgets.h"
+#include "krecfileviewhelpers.h"
+#include "krecfile.h"
+#include "krecglobal.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qpainter.h>
+#include <qpointarray.h>
+#include <qlineedit.h>
+
+KRecFileView::KRecFileView( QWidget* p, const char* n )
+ : QWidget( p,n )
+{
+kdDebug( 60005 ) << k_funcinfo << endl;
+ _layout_td = new QBoxLayout( this, QBoxLayout::TopToBottom, 5, 5 );
+ _filename = new QLabel( i18n( "<no file>" ), this );
+ _layout_td->addWidget( _filename, 1 );
+ _fileview = new KRecFileWidget( 0, this );
+ _layout_td->addWidget( _fileview, 100 );
+ _timebar = new KRecTimeBar( this );
+ _layout_td->addWidget( _timebar, 50 );
+ _layout_lr = new QBoxLayout( this, QBoxLayout::LeftToRight, 5, 5 );
+ _layout_td->addLayout( _layout_lr, 1 );
+ _layout_lr->addStretch( 20 );
+ _timedisplay = new KRecTimeDisplay( this );
+ _layout_td->addWidget( _timedisplay, 1 );
+ _file = 0;
+}
+KRecFileView::~KRecFileView() {
+kdDebug( 60005 ) << k_funcinfo << endl;
+}
+
+void KRecFileView::setFile( KRecFile* file ) {
+kdDebug( 60005 ) << k_funcinfo << file << endl;
+ if ( _file != file ) {
+ _file = file;
+ _fileview->setFile( _file );
+ if ( _file ) {
+ if ( !_file->filename().isNull() ) setFilename( _file->filename() );
+ else _filename->setText( i18n( "file with no name" ) );
+ connect( _file, SIGNAL( posChanged( int ) ), this, SLOT( setPos( int ) ) );
+ connect( _file, SIGNAL( posChanged( int ) ), _timebar, SLOT( newPos( int ) ) );
+ connect( _file, SIGNAL( posChanged( int ) ), _timedisplay, SLOT( newPos( int ) ) );
+ connect( _file, SIGNAL( sizeChanged( int ) ), this, SLOT( setSize( int ) ) );
+ connect( _file, SIGNAL( sizeChanged( int ) ), _timebar, SLOT( newSize( int ) ) );
+ connect( _file, SIGNAL( sizeChanged( int ) ), _timedisplay, SLOT( newSize( int ) ) );
+ connect( _file, SIGNAL( filenameChanged( const QString &) ), this, SLOT( setFilename( const QString &) ) );
+ connect( _file, SIGNAL( filenameChanged( const QString &) ), _timedisplay, SLOT( newFilename( const QString &) ) );
+ connect( _timebar, SIGNAL( sNewPos( int ) ), _file, SLOT( newPos( int ) ) );
+ _timebar->newPos( _file->position() );
+ _timebar->newSize( _file->size() );
+ _timedisplay->newSamplingRate( _file->samplerate() );
+ _timedisplay->newChannels( _file->channels() );
+ _timedisplay->newBits( _file->bits() );
+ _timedisplay->newFilename( _file->filename() );
+ _timedisplay->newPos( _file->position() );
+ _timedisplay->newSize( _file->size() );
+ } else {
+ disconnect( this, SLOT( setPos( QIODevice::Offset ) ) );
+ _filename->setText( i18n( "<no file>" ) );
+ _timedisplay->newFilename( QString::null );
+ }
+ }
+}
+
+void KRecFileView::updateGUI() { _fileview->resizeEvent(); }
+void KRecFileView::setPos( int ) {}
+void KRecFileView::setSize( int ) {}
+void KRecFileView::setFilename( const QString &n ) { _filename->setText( n );}
+
diff --git a/krec/krecfileview.h b/krec/krecfileview.h
new file mode 100644
index 00000000..a445316f
--- /dev/null
+++ b/krec/krecfileview.h
@@ -0,0 +1,56 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#ifndef KRECFILEVIEW_H
+#define KRECFILEVIEW_H
+
+#include <qwidget.h>
+
+class KRecFile;
+class KRecBuffer;
+class QBoxLayout;
+class QLabel;
+class QLineEdit;
+
+class KRecFileWidget;
+class KRecBufferWidget;
+class KRecTimeBar;
+class KRecTimeDisplay;
+
+class KRecFileView : virtual public QWidget {
+ Q_OBJECT
+public:
+ KRecFileView( QWidget*, const char* =0 );
+ ~KRecFileView();
+
+ void setFile( KRecFile* );
+public slots:
+ /// Mainly calls resize() on the KRecFileWidget.
+ void updateGUI();
+private slots:
+ void setPos( int );
+ void setSize( int );
+ void setFilename( const QString & );
+private:
+ QBoxLayout *_layout_td, *_layout_lr;
+ QLabel *_filename;
+
+ KRecFile *_file;
+ KRecFileWidget *_fileview;
+ KRecTimeBar *_timebar;
+ KRecTimeDisplay *_timedisplay;
+};
+
+#endif
+
+// vim:sw=4:ts=4
diff --git a/krec/krecfileviewhelpers.cpp b/krec/krecfileviewhelpers.cpp
new file mode 100644
index 00000000..8025f576
--- /dev/null
+++ b/krec/krecfileviewhelpers.cpp
@@ -0,0 +1,268 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#include "krecfileviewhelpers.h"
+#include "krecfileviewhelpers.moc"
+
+#include "krecglobal.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qpainter.h>
+#include <qpointarray.h>
+#include <qpopupmenu.h>
+#include <kpopupmenu.h>
+
+KRecTimeBar::KRecTimeBar( QWidget* p, const char* n ) : QFrame( p,n )
+ ,_size(10), _pos(0)
+ {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+ setMinimumSize( 10,10 );
+ setFrameStyle( QFrame::Panel|QFrame::Plain );
+ setLineWidth( 1 );
+}
+
+KRecTimeBar::~KRecTimeBar() {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+}
+
+void KRecTimeBar::drawContents( QPainter* p ) {
+ int w = contentsRect().width();
+ int h = contentsRect().height();
+ int t = contentsRect().top();
+ int l = contentsRect().left();
+ p->setPen( QColor( 255,0,0 ) );
+ if ( _pos < _size ) {
+ int x = int( l + w * float( _pos ) / _size );
+ p->drawLine( x, t, x, int( t+h/2 ) );
+ } else {
+ QPointArray tmp;
+ tmp.putPoints( 0, 4, l+w-3,t+h/4, l+w-3,t+h/4*3, l+w,t+h/2, l+w-3,t+h/4 );
+ p->drawPolyline( tmp );
+ }
+}
+
+void KRecTimeBar::mouseReleaseEvent( QMouseEvent* qme ) {
+ QIODevice::Offset pos = 0;
+ if ( _size > 0 )
+ pos = int( float( qme->x() - contentsRect().left() ) / contentsRect().width() * _size );
+ //kdDebug( 60005 ) << k_funcinfo << "New Position: " << pos << endl;
+ emit sNewPos( pos );
+}
+
+void KRecTimeBar::newPos( int n ) {
+ _pos = n;
+ repaint();
+}
+
+void KRecTimeBar::newSize( int n ) {
+ _size = n;
+ repaint();
+}
+
+
+KRecTimeDisplay::KRecTimeDisplay( QWidget* p, const char* n )
+ : QFrame( p,n )
+ , _posmenu( 0 ), _sizemenu( 0 )
+ , _sizevalue( 0 ), _posvalue( 0 )
+ , _samplingRate( 44100 ), _bits( 16 ), _channels( 2 )
+{
+ _position = new AKLabel( this );
+ connect( _position, SIGNAL( showContextMenu( const QPoint & ) ), this, SLOT( timeContextMenu( const QPoint &) ) );
+ _size = new AKLabel( this );
+ connect( _size, SIGNAL( showContextMenu( const QPoint &) ), this, SLOT( sizeContextMenu( const QPoint &) ) );
+
+ _layout = new QBoxLayout( this, QBoxLayout::LeftToRight, 0, 2 );
+ _layout->addStretch( 100 );
+ _layout->addWidget( _position );
+ _layout->addSpacing( 2 );
+ _layout->addWidget( _size );
+
+ reset();
+}
+KRecTimeDisplay::~KRecTimeDisplay() {
+}
+
+void KRecTimeDisplay::reset() {
+ _position->setText( positionText( 0, 0 ) );
+ _size->setText( sizeText( 0, 0 ) );
+ _position->setFrameStyle( QFrame::Panel|QFrame::Sunken );
+ _position->setLineWidth( 1 );
+ _position->setMargin( 2 );
+ _size->setFrameStyle( QFrame::Panel|QFrame::Sunken );
+ _size->setLineWidth( 1 );
+ _size->setMargin( 2 );
+}
+
+void KRecTimeDisplay::newPos( int n ) {
+ _posvalue = n;
+ _position->setText( positionText( KRecGlobal::the()->timeFormatMode(), _posvalue ) );
+ static int timeformat = KRecGlobal::the()->timeFormatMode();
+ if ( timeformat != KRecGlobal::the()->timeFormatMode() ) {
+ timeformat = KRecGlobal::the()->timeFormatMode();
+ newSize( _sizevalue );
+ }
+}
+void KRecTimeDisplay::newSize( int n ) {
+ _sizevalue = n;
+ _size->setText( sizeText( KRecGlobal::the()->timeFormatMode(), n ) );
+}
+void KRecTimeDisplay::newFilename( const QString &n ) {
+ _filename = n;
+ if ( _filename.isNull() ) {
+ newPos( 0 );
+ newSize( 0 );
+ }
+}
+
+QString KRecTimeDisplay::formatTime( const int mode, const int sample ) const {
+ QString text;
+ bool verbose = mode / 100;
+ switch ( mode % 100 ) {
+ case 3:
+ {
+ int bytes = sample * ( _bits/8 ) * _channels / 1024;
+ int kbyte = bytes % 1024;
+ bytes /= 1024;
+ int mbyte = bytes % 1024;
+ text += QString::number( mbyte );
+ if ( verbose ) text += i18n( "MB" );
+ text += ".";
+ if ( kbyte < 1000 ) text += "0";
+ if ( kbyte < 100 ) text += "0";
+ if ( kbyte < 10 ) text += "0";
+ text += QString::number( kbyte );
+ if ( verbose ) text += i18n( "kB" );
+ }
+ break;
+ case 2:
+ {
+ int value = sample / ( _samplingRate / KRecGlobal::the()->frameBase() ); // overall frames
+ int frames = value % KRecGlobal::the()->frameBase();
+ value = value / KRecGlobal::the()->frameBase();
+ int secs = value % 60;
+ value = value / 60;
+ int mins = value % 60;
+ value = value / 60;
+ if ( value ) {
+ text += QString::number( value );
+ if ( verbose ) text += i18n( "hours" );
+ text += ":";
+ }
+ if ( mins<10 ) text += "0";
+ text += QString::number( mins );
+ if ( verbose ) text += i18n( "mins" );
+ text += ":";
+ if ( secs<10 ) text += "0";
+ text += QString::number( secs );
+ if ( verbose ) text += i18n( "secs" );
+ text += ".";
+ if ( frames < 10 ) text += "0";
+ text += QString::number( frames );
+ if ( verbose ) text += i18n( "frames" );
+ }
+ break;
+ case 1:
+ {
+ int value = sample; // overall samples
+ int samples = value % ( _samplingRate / 60 );
+ value = value / ( _samplingRate / 60 );
+ int secs = value % 60;
+ value = value / 60;
+ int mins = value % 60;
+ value = value / 60;
+ if ( value ) {
+ text += QString::number( value );
+ if ( verbose ) text += i18n( "hours" );
+ text += ":";
+ }
+ if ( mins<10 ) text += "0";
+ text += QString::number( mins );
+ if ( verbose ) text += i18n( "mins" );
+ text += ":";
+ if ( secs<10 ) text += "0";
+ text += QString::number( secs );
+ if ( verbose ) text += i18n( "secs" );
+ text += ".";
+ if ( samples < 10000 && ( _samplingRate / 60 > 10000 ) ) text += "0";
+ if ( samples < 1000 ) text += "0";
+ if ( samples < 100 ) text += "0";
+ if ( samples < 10 ) text += "0";
+ text += QString::number( samples );
+ if ( verbose ) text += i18n( "samples" );
+ }
+ break;
+ case 0:
+ default:
+ text = QString::number( sample );
+ if ( verbose ) text += i18n( "samples" );
+ break;
+ };
+ return text;
+}
+
+void KRecTimeDisplay::timeContextMenu( QPopupMenu* menu ) {
+ if ( !_filename.isNull() ) {
+ menu->insertSeparator( 0 );
+ menu->insertItem( i18n( "kByte: %1" ).arg( formatTime( 3, _posvalue ) ), -1, 0 );
+ menu->insertItem( i18n( "[h:]m:s.f %1" ).arg( formatTime( 2, _posvalue ) ), -1, 0 );
+ menu->insertItem( i18n( "[h:]m:s.s %1" ).arg( formatTime( 1, _posvalue ) ), -1, 0 );
+ menu->insertItem( i18n( "%1 Samples" ).arg( formatTime( 0, _posvalue ) ), -1, 0 );
+ KPopupTitle *tmp = new KPopupTitle( menu );
+ tmp->setTitle( i18n( "Position" ) );
+ menu->insertItem( tmp, -1, 0 );
+ } else
+ menu->insertItem( i18n( "<no file>" ), -1, 0 );
+}
+void KRecTimeDisplay::timeContextMenu( const QPoint &point ) {
+ if ( _posmenu ) delete _posmenu;
+ _posmenu = new KPopupMenu( this );
+ timeContextMenu( _posmenu );
+ _posmenu->exec( point );
+}
+void KRecTimeDisplay::sizeContextMenu( QPopupMenu* menu ) {
+ if ( !_filename.isNull() ) {
+ menu->insertSeparator( 0 );
+ menu->insertItem( i18n( "kByte: %1" ).arg( formatTime( 3, _sizevalue ) ), -1, 0 );
+ menu->insertItem( i18n( "[h:]m:s.f %1" ).arg( formatTime( 2, _sizevalue ) ), -1, 0 );
+ menu->insertItem( i18n( "[h:]m:s.s %1" ).arg( formatTime( 1, _sizevalue ) ), -1, 0 );
+ menu->insertItem( i18n( "%1 Samples" ).arg( formatTime( 0, _sizevalue ) ), -1, 0 );
+ KPopupTitle *tmp = new KPopupTitle( menu );
+ tmp->setTitle( i18n( "Size" ) );
+ menu->insertItem( tmp, -1, 0 );
+ } else
+ menu->insertItem( i18n( "<no file>" ), -1, 0 );
+}
+void KRecTimeDisplay::sizeContextMenu( const QPoint &point ) {
+ if ( _sizemenu ) delete _sizemenu;
+ _sizemenu = new KPopupMenu( this );
+ sizeContextMenu( _sizemenu );
+ _sizemenu->exec( point );
+}
+void KRecTimeDisplay::jumpToTime() {
+}
+
+QString KRecTimeDisplay::positionText( int m, int n ) {
+ return i18n( "Position: %1" ).arg( formatTime( m,n ) );
+}
+QString KRecTimeDisplay::sizeText( int m, int n ) {
+ return i18n( "Size: %1" ).arg( formatTime( m,n ) );
+}
+
+void AKLabel::mousePressEvent( QMouseEvent* qme ) {
+ if ( qme->button() == Qt::RightButton )
+ emit showContextMenu( qme->globalPos() );
+}
+
diff --git a/krec/krecfileviewhelpers.h b/krec/krecfileviewhelpers.h
new file mode 100644
index 00000000..71881f1e
--- /dev/null
+++ b/krec/krecfileviewhelpers.h
@@ -0,0 +1,110 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#ifndef KRECFILEVIEWHELPERS_H
+#define KRECFILEVIEWHELPERS_H
+
+#include <qframe.h>
+#include <qlabel.h>
+#include <qstring.h>
+#include <qpoint.h>
+#include <kglobalsettings.h>
+
+class QBoxLayout;
+class AKLabel;
+class QPopupMenu;
+class KPopupMenu;
+
+class KRecTimeBar : public QFrame {
+ Q_OBJECT
+public:
+ KRecTimeBar( QWidget*, const char* =0 );
+ ~KRecTimeBar();
+
+ void drawContents( QPainter* );
+
+ void mouseReleaseEvent( QMouseEvent* );
+
+public slots:
+ void newPos( int );
+ void newSize( int );
+signals:
+ void sNewPos( int );
+private:
+ int _pos, _size;
+};
+
+/**
+ * Modes are:
+ * 0 - Just samples
+ * 1 - [hours:]mins:secs:samples
+ * 2 - [hours:]mins:secs:frames ( framebase from global )
+ * 3 - [hours:]mins:secs:msecs
+ *
+ * +100 - verbose ( [XXhours:]XXmins:XXsecs:... )
+*/
+
+class KRecTimeDisplay : public QFrame {
+ Q_OBJECT
+public:
+ KRecTimeDisplay( QWidget*, const char* =0 );
+ ~KRecTimeDisplay();
+
+ /// Resets the display to its defaultvalues
+ void reset();
+public slots:
+ void newPos( int );
+ void newSize( int );
+ void newFilename( const QString & );
+ void newSamplingRate( int n ) { _samplingRate = n; }
+ void newChannels( int n ) { _channels = n; }
+ void newBits( int n ) { _bits = n; }
+signals:
+ void sNewPos( int );
+private slots:
+ void timeContextMenu( QPopupMenu* );
+ void timeContextMenu( const QPoint &);
+ void sizeContextMenu( QPopupMenu* );
+ void sizeContextMenu( const QPoint &);
+ void jumpToTime();
+private:
+ QString positionText( int, int );
+ QString sizeText( int, int );
+ QString formatTime( const int mode, const int samples ) const;
+ QString _filename;
+ QBoxLayout *_layout;
+ AKLabel *_position, *_size;
+ KPopupMenu *_posmenu, *_sizemenu;
+ int _sizevalue, _posvalue;
+ int _samplingRate, _bits, _channels;
+};
+
+
+class AKLabel : public QLabel {
+ Q_OBJECT
+public:
+ AKLabel( QWidget* p, const char* n=0, WFlags f=0 ) : QLabel( p, n, f ) { init(); }
+ AKLabel( const QString& s, QWidget* p, const char* n=0, WFlags f=0 ) : QLabel( s, p, n, f ) { init(); }
+ AKLabel( QWidget* w, const QString& s, QWidget* p, const char* n=0, WFlags f=0 ) : QLabel( w,s,p,n,f ) { init(); }
+
+ void mousePressEvent( QMouseEvent* );
+signals:
+ void showContextMenu( const QPoint & );
+private:
+ void init() {
+ setFont( KGlobalSettings::fixedFont() );
+ }
+};
+#endif
+
+// vim:sw=4:ts=4
diff --git a/krec/krecfilewidgets.cpp b/krec/krecfilewidgets.cpp
new file mode 100644
index 00000000..44fd6a4e
--- /dev/null
+++ b/krec/krecfilewidgets.cpp
@@ -0,0 +1,313 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#include "krecfilewidgets.h"
+#include "krecfilewidgets.moc"
+
+#include "krecfile.h"
+#include "krecglobal.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qpainter.h>
+#include <qpointarray.h>
+#include <qlineedit.h>
+#include <kinputdialog.h>
+#include <qregion.h>
+#include <qrect.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <kpopupmenu.h>
+#include <kactionclasses.h>
+#include <kaction.h>
+
+KRecFileWidget::KRecFileWidget( KRecFile* file, QWidget* p, const char* n )
+ : QFrame( p,n )
+ , _file( 0 )
+{
+ kdDebug( 60005 ) << k_funcinfo << file << endl;
+ setFrameStyle( QFrame::Panel|QFrame::Sunken );
+ setLineWidth( 1 );
+ setMinimumHeight( 20 );
+
+ setFile( file );
+}
+KRecFileWidget::~KRecFileWidget() {
+ kdDebug( 60005 ) << k_funcinfo << endl;
+}
+
+void KRecFileWidget::setFile( KRecFile* file ) {
+kdDebug( 60005 ) << k_funcinfo << file << endl;
+ if ( _file != file ) {
+ _file = file;
+ //kdDebug( 60005 ) << "Removing widgets" << endl;
+ QValueList<KRecBufferWidget*>::iterator it = bufferwidgets.begin();
+ while ( it != bufferwidgets.end() ) {
+ delete ( *it );
+ ++it;
+ }
+ bufferwidgets.clear();
+ resizeEvent();
+ if ( _file ) {
+ for ( QValueList<KRecBuffer*>::iterator it = _file->_buffers.begin(); it != _file->_buffers.end(); ++it ) {
+ newBuffer( ( *it ) );
+ }
+ connect( _file, SIGNAL( sNewBuffer( KRecBuffer* ) ), this, SLOT( newBuffer( KRecBuffer* ) ) );
+ connect( _file, SIGNAL( sDeleteBuffer( KRecBuffer* ) ), this, SLOT( deleteBuffer( KRecBuffer* ) ) );
+ //kdDebug( 60005 ) << _file->_currentBuffer << endl;
+ }
+ }
+}
+
+void KRecFileWidget::resizeEvent( QResizeEvent* /*qre*/ ) {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+ if ( _file ) {
+ int w = contentsRect().width();
+ int h = contentsRect().height();
+ QValueList<KRecBufferWidget*>::iterator it;
+ for ( it = bufferwidgets.begin(); it != bufferwidgets.end(); ++it ) {
+ int width, x;
+ if ( !_file->offsetSize()==0 && !( *it )->buffer()->size()==0 ) {
+ width = int ( float( ( *it )->buffer()->size() ) / _file->offsetSize() * w );
+ x = int( float( ( *it )->buffer()->startpos() ) / _file->size() * w ) + contentsRect().left() ;
+ } else {
+ width = 5;
+ x = contentsRect().left();
+ }
+ ( *it )->setGeometry( x, contentsRect().top(), width, h );
+ }
+ }
+}
+
+void KRecFileWidget::mouseReleaseEvent( QMouseEvent* qme ) {
+ kdDebug( 60005 ) << k_funcinfo << "(" << qme->x() << "|" << qme->y() << ")" << endl;
+}
+
+void KRecFileWidget::newBuffer( KRecBuffer* buffer ) {
+ //kdDebug( 60005 ) << k_funcinfo << buffer << endl;
+ KRecBufferWidget *tmp = new KRecBufferWidget( buffer, this );
+ connect( tmp, SIGNAL( popupMenu( KRecBufferWidget*, QPoint ) ), this, SLOT( popupMenu( KRecBufferWidget*, QPoint ) ) );
+ bufferwidgets.append( tmp );
+ tmp->show();
+ resizeEvent();
+}
+void KRecFileWidget::deleteBuffer( KRecBuffer* buffer ) {
+ //kdDebug( 60005 ) << k_funcinfo << buffer << endl;
+ QValueList<KRecBufferWidget*>::iterator it = bufferwidgets.begin();
+ KRecBufferWidget* tmp = 0;
+ while ( it != bufferwidgets.end() ) {
+ if ( ( *it )->buffer() == buffer ) tmp = ( *it );
+ ++it;
+ }
+ if ( tmp != 0 ) {
+ delete tmp;
+ bufferwidgets.remove( tmp );
+ }
+}
+
+void KRecFileWidget::popupMenu( KRecBufferWidget* bw, QPoint pos ) {
+ KPopupMenu tmp( this );
+ KToggleAction* _activeaction = new KToggleAction( i18n( "Toggle Active/Disabled State" ), KShortcut(), this );
+ _activeaction->setChecked( bw->buffer()->active() );
+ connect( _activeaction, SIGNAL( toggled( bool ) ), bw->buffer(), SLOT( setActive( bool ) ) );
+ KAction* _removeaction = new KAction( i18n( "Remove This Part" ), "fileremove", KShortcut(), bw->buffer(), SLOT( deleteBuffer() ), this );
+ KAction* _changetitle = new KAction( i18n( "Change Title of This Part" ), KShortcut(), bw, SLOT( changeTitle() ), this );
+ KAction* _changecomment = new KAction( i18n( "Change Comment of This Part" ), KShortcut(), bw, SLOT( changeComment() ), this );
+ _activeaction->plug( &tmp );
+ _changetitle->plug( &tmp );
+ _changecomment->plug( &tmp );
+ tmp.insertSeparator();
+ _removeaction->plug( &tmp );
+
+ tmp.exec( pos );
+
+ delete _removeaction;
+ delete _changecomment;
+ delete _changetitle;
+ delete _activeaction;
+}
+
+
+
+KRecBufferWidget::KRecBufferWidget( KRecBuffer* buffer, QWidget* p, const char* n )
+ : QFrame( p,n )
+ , _buffer( buffer )
+ , _main_region( 0 ), _title_region( 0 ), _fileend_region( 0 )
+ , alreadyreadsize( 0 )
+{
+ connect( _buffer, SIGNAL( somethingChanged() ), this, SLOT( update() ) );
+ kdDebug( 60005 ) << k_funcinfo << endl;
+}
+KRecBufferWidget::~KRecBufferWidget() {
+ kdDebug( 60005 ) << k_funcinfo << endl;
+}
+
+void KRecBufferWidget::resizeEvent( QResizeEvent* ) {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+}
+
+void KRecBufferWidget::initLayout() {
+ _title_height = fontMetrics().boundingRect( _buffer->filename() ).height() + 4;
+ if( height()/5 > _title_height ) _title_height = height()/5;
+ int _title_width = fontMetrics().boundingRect( _buffer->filename() ).width() + 10;
+ if ( _title_width > width() ) _title_width = width();
+ if ( _main_region ) delete _main_region;
+ _main_region = new QRegion( QRect( 0, _title_height, width(), height()-_title_height ) );
+ if ( _title_region ) delete _title_region;
+ if ( _buffer->active() )
+ _title_region = new QRegion( QRect( 0, 0, _title_width, _title_height ) );
+ else
+ _title_region = new QRegion( QRect( 0, _title_height/2, _title_width, _title_height/2 ) );
+ if ( _fileend_region ) { delete _fileend_region; _fileend_region=0; }
+ if ( _buffer->active() )
+ _fileend_region = new QRegion( QRect( width()-4, _title_height/2, 4, _title_height/2 ) );
+ else
+ _fileend_region = new QRegion( QRect( width()-4, _title_height/4*3, 4, _title_height/4 ) );
+
+ setMask( _main_region->unite( *_title_region ).unite( *_fileend_region ) );
+
+ _topleft = _title_region->boundingRect().topLeft();
+ _bottomleft = _main_region->boundingRect().bottomLeft();
+ _bottomright = _main_region->boundingRect().bottomRight();
+ _topright = _main_region->boundingRect().topRight();
+ _bottommiddle = _title_region->boundingRect().bottomRight();
+ _bottommiddle += QPoint( 0, 1 );
+ _topmiddle = _title_region->boundingRect().topRight();
+}
+void KRecBufferWidget::drawFrame( QPainter* p ) {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+ if ( _buffer->active() )
+ p->setBrush( colorGroup().highlight() );
+ else
+ p->setBrush( colorGroup().highlight().dark() );
+ p->setPen( QPen( colorGroup().dark(), 1 ) );
+ p->drawRect( _title_region->boundingRect() );
+ p->drawRect( _fileend_region->boundingRect() );
+
+ p->setBrush( QBrush() );
+ p->setPen( QPen( colorGroup().dark(), 1 ) );
+ p->drawRect( _main_region->boundingRect() );
+
+ p->setPen( QPen( colorGroup().highlightedText() ) );
+ p->drawText( _title_region->boundingRect(), Qt::AlignCenter, _buffer->title() );
+
+}
+void KRecBufferWidget::drawContents( QPainter* p ) {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+ initSamples();
+ int space = ( _main_region->boundingRect().height() - 2 ) / 2;
+ int xoffset = _main_region->boundingRect().top() + space + 1;
+ p->setPen( QPen( QColor( 0,0,255 ) ) );
+ for ( uint i=0; i<samples1.count(); i++ )
+ p->drawPoint( i, int( samples1[ i ]->getMax() * space + xoffset ) );
+ for ( uint i=0; i<samples1.count(); i++ )
+ p->drawPoint( i, int( samples1[ i ]->getMin() * space + xoffset ) );
+ p->setPen( QPen( QColor( 255,0,0 ) ) );
+ for ( uint i=0; i<samples1.count(); i++ )
+ p->drawPoint( i, int( samples1[ i ]->getValue() * space + xoffset ) );
+ p->setPen( QPen( QColor( 0,0,0 ) ) );
+ QString comment = _buffer->comment();
+ if ( comment.isNull() ) comment = i18n( "Lots of Data" );
+ p->drawText( _main_region->boundingRect(), Qt::AlignCenter, comment );
+}
+void KRecBufferWidget::paintEvent( QPaintEvent* ) {
+ initLayout();
+ QPainter *p = new QPainter( this );
+ drawFrame( p );
+ drawContents( p );
+ delete p;
+}
+
+void KRecBufferWidget::mousePressEvent( QMouseEvent* qme ) {
+ kdDebug( 60005 ) << k_funcinfo << endl;
+ if ( _main_region->contains( qme->pos() ) || _title_region->contains( qme->pos() ) )
+ if ( qme->button() == Qt::RightButton )
+ emit popupMenu( this, qme->globalPos() );
+}
+void KRecBufferWidget::mouseDoubleClickEvent( QMouseEvent* qme ) {
+ if ( _title_region->contains( qme->pos() ) )
+ _buffer->setActive( !_buffer->active() );
+}
+
+void KRecBufferWidget::initSamples() {
+#if 0
+ /**
+ * Experimental.
+ *
+ * Doesn't really work since the conversion from char8/char16 to float in
+ * krecfile.cpp isn't working.
+ */
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+ if ( alreadyreadsize != _buffer->size() ) {
+ kdDebug( 60005 ) << k_funcinfo << "something to do" << endl;
+ while ( samples1.count() > 0 ) {
+ delete samples1.first();
+ samples1.pop_front();
+ }
+ while ( samples2.count() > 0 ) {
+ delete samples2.first();
+ samples2.pop_front();
+ }
+ Sample* tmp = new Sample();
+ samples1.append( tmp );
+ int j=0;
+ for ( int i=0; i<= _buffer->sizeInSamples(); ++i ) {
+ //for ( int i=0; i<= 10000; ++i ) {
+ if ( j/128 ) {
+ //kdDebug(60005) << "sample " << i << ": creating new Sample" << endl;
+ tmp = new Sample();
+ samples1.append( tmp );
+ j=0;
+ }
+ tmp->addValue( _buffer->getSample( i, 0 ) );
+ //kdDebug(60005) << "Getting value " << _buffer->getSample( i,0 ) << endl;
+ ++j;
+ }
+
+ tmp = new Sample;
+ samples2.append( tmp );
+ j=0;
+ for ( uint i=0; i<samples1.count(); ++i ) {
+ if ( j/128 ) {
+ tmp = new Sample();
+ samples2.append( tmp );
+ j=0;
+ }
+ tmp->addValue( samples1[ i ]->getValue() );
+ ++j;
+ }
+
+// for ( uint i=0; i<samples1.count(); ++i )
+// kdDebug(60005) << i << ": " << samples1[ i ]->getMin() << " < " << samples1[ i ]->getValue() << " < " << samples1[ i ]->getMax() << endl;
+ for ( uint i=0; i<samples2.count(); ++i )
+ kdDebug(60005) << i << ": " << samples2[ i ]->getMin() << " < " << samples2[ i ]->getValue() << " < " << samples2[ i ]->getMax() << endl;
+
+ alreadyreadsize = _buffer->size();
+ }
+#endif
+}
+
+
+void KRecBufferWidget::changeTitle() {
+ QString tmp = KInputDialog::getText( i18n( "New Title" ), i18n( "Enter new part title:" ), _buffer->title() );
+ if ( !tmp.isNull() )
+ _buffer->setTitle( tmp );
+}
+void KRecBufferWidget::changeComment() {
+ QString tmp = KInputDialog::getText( i18n( "New Comment" ), i18n( "Enter new part comment:" ), _buffer->comment() );
+ if ( !tmp.isNull() )
+ _buffer->setComment( tmp );
+}
+
diff --git a/krec/krecfilewidgets.h b/krec/krecfilewidgets.h
new file mode 100644
index 00000000..cca78907
--- /dev/null
+++ b/krec/krecfilewidgets.h
@@ -0,0 +1,110 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#ifndef KRECFILEWIDGETS_H
+#define KRECFILEWIDGETS_H
+
+#include <qframe.h>
+#include <qvaluelist.h>
+#include <qpoint.h>
+
+class KRecFile;
+class KRecBuffer;
+class TimeDisplay;
+class QBoxLayout;
+class QLabel;
+class QLineEdit;
+
+class KRecFileWidget;
+class KRecBufferWidget;
+class KRecTimeBar;
+class KRecTimeDisplay;
+
+class QRegion;
+class QPainter;
+class KAction;
+class KToggleAction;
+
+class KRecFileWidget : public QFrame {
+ Q_OBJECT
+public:
+ KRecFileWidget( KRecFile*, QWidget*, const char* =0 );
+ ~KRecFileWidget();
+
+ void setFile( KRecFile* );
+
+ void resizeEvent( QResizeEvent* =0 );
+ void mouseReleaseEvent( QMouseEvent* );
+public slots:
+ void newBuffer( KRecBuffer* );
+ void deleteBuffer( KRecBuffer* );
+private slots:
+ void popupMenu( KRecBufferWidget*, QPoint );
+private:
+ KRecFile *_file;
+ QValueList<KRecBufferWidget*> bufferwidgets;
+};
+
+class Sample : public QObject {
+ Q_OBJECT
+public:
+ Sample() : _values( 0 ), _min( 0 ), _max( 0 ) {}
+
+ void addValue( float n ) { _values += n; ++_count; addMin( n ); addMax( n ); }
+ void addMin( float n ) { if ( n < _min ) _min = n; }
+ void addMax( float n ) { if ( n > _max ) _max = n; }
+
+ float getValue() const { return _values / _count; }
+ float getMax() const { return ( _max>1 )?1:_max; }
+ float getMin() const { return ( _min<-1 )?-1:_min; }
+ int getCount() const { return _count; }
+private:
+ float _values, _min, _max;
+ int _count;
+};
+
+class KRecBufferWidget : public QFrame {
+ Q_OBJECT
+public:
+ KRecBufferWidget( KRecBuffer*, QWidget*, const char* =0 );
+ ~KRecBufferWidget();
+
+ const KRecBuffer* buffer() { return _buffer; }
+
+ void resizeEvent( QResizeEvent* );
+
+ void drawFrame( QPainter* );
+ void drawContents( QPainter* );
+ void paintEvent( QPaintEvent* );
+
+ void mousePressEvent( QMouseEvent* );
+ void mouseDoubleClickEvent( QMouseEvent* );
+signals:
+ void popupMenu( KRecBufferWidget*, QPoint );
+private slots:
+ void initSamples();
+ void changeTitle();
+ void changeComment();
+private:
+ void initLayout();
+ KRecBuffer *_buffer;
+ QRegion *_main_region, *_title_region, *_fileend_region;
+ QPoint _topleft, _bottomleft, _bottomright, _topright, _topmiddle, _bottommiddle;
+ int _title_height;
+ QValueList <Sample*> samples1, samples2;
+ uint alreadyreadsize;
+};
+
+#endif
+
+// vim:sw=4:ts=4
diff --git a/krec/krecglobal.cpp b/krec/krecglobal.cpp
new file mode 100644
index 00000000..94683995
--- /dev/null
+++ b/krec/krecglobal.cpp
@@ -0,0 +1,130 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#include "krecglobal.h"
+#include "krecglobal.moc"
+
+#include "krecexport_template.h"
+
+#include <kconfig.h>
+#include <qwidget.h>
+#include <kapplication.h>
+#include <kstatusbar.h>
+
+#include <ktrader.h>
+#include <kparts/componentfactory.h>
+#include <qvaluelist.h>
+#include <kservice.h>
+
+#include <kdebug.h>
+
+KRecGlobal::KRecGlobal( QObject* p, const char* n )
+ : QObject( p,n )
+ , _qwidget( 0 )
+ , _statusbar( 0 )
+ , _timeformatcache( -1 )
+ , _framebasecache( -1 )
+{
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+ _exports = new QDict<KRecExportItem>;
+}
+KRecGlobal::~KRecGlobal() {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+}
+
+KRecGlobal* KRecGlobal::the() {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+ static KRecGlobal* object = new KRecGlobal();
+ return object;
+}
+
+void KRecGlobal::setMainWidget( QWidget* n ) { _qwidget = n; }
+QWidget* KRecGlobal::mainWidget() { return _qwidget; }
+
+KConfig* KRecGlobal::kconfig() { return kapp->config(); }
+
+void KRecGlobal::setStatusBar( KStatusBar *bar ) { _statusbar = bar; }
+void KRecGlobal::message( const QString &text ) { if ( _statusbar ) _statusbar->message( text, 2000 ); }
+
+bool KRecGlobal::registerExport( KRecExportItem* item ) {
+ //kdDebug( 60005 ) << k_funcinfo << "About to register ExportItem for \"" << item->exportFormat() << "\" extensions:\"" << item->extensions() << "\"" << endl;
+ if ( !the()->_exports->find( item->exportFormat() ) ) {
+ the()->_exports->insert( item->exportFormat(), item );
+ the()->_exportformats << item->exportFormat();
+ }
+ return true;
+}
+
+KRecExportItem* KRecGlobal::getExportItem( const QString &exportFormat ) {
+ KRecExportItem *tmp = _exports->find( exportFormat );
+ return tmp->newItem();
+}
+
+QStringList KRecGlobal::exportFormats() const {
+ return _exportformats;
+}
+
+QString KRecGlobal::exportFormatEndings() const {
+ QString out;
+
+ KTrader::OfferList offers = KTrader::self()->query( "KRec/exportplugin" );
+ KTrader::OfferList::iterator it = offers.begin();
+ while ( it!=offers.end() ) {
+ out += " *.";
+ out += ( *it )->property( "X-KDE-ExportSuffix" ).toStringList().join( " *." );
+ ++it;
+ }
+
+ return out;
+}
+
+KRecExportItem* KRecGlobal::getExportItemForEnding( const QString &ending ) {
+ KTrader::OfferList offers = KTrader::self()->query( "KRec/exportplugin" );
+ KTrader::OfferList::iterator it = offers.begin();
+ while ( it!=offers.end() ) {
+ kdDebug(60005) << ( *it )->property( "X-KDE-ExportSuffix" ).toStringList() << endl;
+ if ( ( *it )->property( "X-KDE-ExportSuffix" ).toStringList().grep( ending ).count() ) {
+ kdDebug(60005) << "Ending(" << ending << ") found" << endl;
+ return KParts::ComponentFactory::createInstanceFromService<KRecExportItem>( ( *it ), this, "exportplugin" );
+ } else kdDebug(60005) << "Doesn't contain " << ending << endl;
+ ++it;
+ }
+ return 0;
+}
+
+int KRecGlobal::timeFormatMode() {
+ if ( _timeformatcache < 0 ) {
+ kapp->config()->setGroup( "General" );
+ _timeformatcache = kapp->config()->readNumEntry( "TimeFormat", 0 );
+ }
+ return _timeformatcache;
+}
+void KRecGlobal::setTimeFormatMode( int n ) {
+ kapp->config()->setGroup( "General" );
+ kapp->config()->writeEntry( "TimeFormat", n );
+ _timeformatcache = n;
+}
+
+int KRecGlobal::frameBase() {
+ if ( _framebasecache < 0 ) {
+ kapp->config()->setGroup( "General" );
+ _framebasecache = kapp->config()->readNumEntry( "FrameBase", 25 );
+ }
+ return _framebasecache;
+}
+void KRecGlobal::setFrameBase( int n ) {
+ kapp->config()->setGroup( "General" );
+ kapp->config()->writeEntry( "FrameBase", n );
+ _framebasecache = n;
+}
+
diff --git a/krec/krecglobal.h b/krec/krecglobal.h
new file mode 100644
index 00000000..6955eb14
--- /dev/null
+++ b/krec/krecglobal.h
@@ -0,0 +1,97 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#ifndef KREC_GLOBAL_H
+#define KREC_GLOBAL_H
+
+#include <qobject.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qdict.h>
+
+class KConfig;
+class QWidget;
+class KStatusBar;
+
+class KRecExportItem;
+
+class KRecGlobal : public QObject {
+ Q_OBJECT
+private:
+ KRecGlobal( QObject* =0, const char* =0 );
+ ~KRecGlobal();
+
+public:
+
+ /**
+ * @return Returns a pointer to the global KRecGlobal.
+ */
+ static KRecGlobal* the();
+
+ /**
+ * Sets the MainWidget.
+ */
+ void setMainWidget( QWidget* );
+ /**
+ * Returns a pointer to the mainwidget.
+ * Usefull to display Messageboxes, etc correctly without beeing a
+ * QWidget or knowing about a parent QWidget.
+ */
+ QWidget* mainWidget();
+
+ /// @return kapp->config()
+ static KConfig* kconfig();
+
+ /// Sets the Statusbar.
+ void setStatusBar( KStatusBar* );
+ /**
+ * Puts a message into the statusbar.
+ * Usefull for showing messages without knowing about the Statusbar.
+ */
+ void message( const QString & );
+
+ /// Registers a KRecExportItem
+ static bool registerExport( KRecExportItem* );
+ /**
+ * Returns a new KRecExportItem for the specified exportFormat.
+ * If now Item can be found the return value is 0.
+ */
+ KRecExportItem* getExportItem( const QString &exportFormat );
+ ///
+ KRecExportItem* getExportItemForEnding( const QString & );
+ /// Returns a list of exportFormats.
+ QStringList exportFormats() const;
+ /// Returns a list of fileendings.
+ QString exportFormatEndings() const;
+
+ /**
+ * Gets/Sets the actual mode for formating time values.
+ *
+ * For description see krecfileview.h
+ */
+ int timeFormatMode();
+ void setTimeFormatMode( int );
+ /// The framebase (how many frames per second) (25/30 for movies, 75 for CDs)
+ int frameBase();
+ void setFrameBase( int );
+private:
+ QWidget *_qwidget;
+ KStatusBar *_statusbar;
+ QDict <KRecExportItem> *_exports;
+ QStringList _exportformats;
+ int _timeformatcache, _framebasecache;
+};
+
+#endif
+
+// vim:sw=4:ts=4
diff --git a/krec/krecnewproperties.cpp b/krec/krecnewproperties.cpp
new file mode 100644
index 00000000..b63cd032
--- /dev/null
+++ b/krec/krecnewproperties.cpp
@@ -0,0 +1,102 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#include "krecnewproperties.h"
+#include "krecnewproperties.moc"
+
+#include "krecglobal.h"
+#include "krecconfig_fileswidget.h"
+
+#include <kconfig.h>
+#include <qbuttongroup.h>
+#include <qvbuttongroup.h>
+#include <qradiobutton.h>
+#include <qlayout.h>
+#include <kseparator.h>
+#include <kpushbutton.h>
+#include <kstdguiitem.h>
+#include <qlabel.h>
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qfont.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+KRecNewProperties::KRecNewProperties( QWidget* p, const char* n )
+ : QDialog( p,n, 0, Qt::WType_Dialog|Qt::WStyle_Customize|Qt::WStyle_DialogBorder )
+ , _filename( QString::null )
+ , _samplerate( 44100 ), _channels( 2 ), _bits( 16 )
+{
+kdDebug( 60005 ) << k_funcinfo << endl;
+ KConfig *config = KRecGlobal::kconfig();
+ config->setGroup( "FileDefaults" );
+ _samplerate = config->readNumEntry( "SamplingRate", 44100 );
+ _channels = config->readNumEntry( "Channels", 2 );
+ _bits = config->readNumEntry( "Bits", 16 );
+ _usedefaults = config->readBoolEntry( "UseDefaults", false );
+
+ _layout = new QVBoxLayout( this, 5, 5 );
+
+ QLabel *captionlabel = new QLabel( this );
+ QFont labelfont( captionlabel->font() );
+ labelfont.setPointSize( labelfont.pointSize()*3/2 );
+ captionlabel->setFont( labelfont );
+ captionlabel->setText( i18n( "Properties for the new File" ) );
+ captionlabel->setAlignment( AlignCenter );
+ _layout->addWidget( captionlabel );
+
+ _filewidget = new KRecConfigFilesWidget( this );
+ connect( _filewidget, SIGNAL( sRateChanged( int ) ), this, SLOT( ratechanged( int ) ) );
+ connect( _filewidget, SIGNAL( sChannelsChanged( int ) ), this, SLOT( channelschanged( int ) ) );
+ connect( _filewidget, SIGNAL( sBitsChanged( int ) ), this, SLOT( bitschanged( int ) ) );
+ connect( _filewidget, SIGNAL( sUseDefaultsChanged( bool ) ), this, SLOT( usedefaultschanged( bool ) ) );
+
+ QWidget *_btnWidget = new QWidget( this );
+ _layoutbuttons = new QHBoxLayout( _btnWidget );
+ _layoutbuttons->addStretch( 100 );
+ _btnok = new KPushButton( KStdGuiItem::ok(), _btnWidget );
+ connect( _btnok, SIGNAL( clicked() ), this, SLOT( accept() ) );
+ _layoutbuttons->addWidget( _btnok, 0 );
+
+ _layout->addWidget( new KSeparator( KSeparator::HLine, this ) );
+ _layout->addWidget( _filewidget );
+ _layout->addWidget( new KSeparator( KSeparator::HLine, this ) );
+ _layout->addWidget( _btnWidget );
+
+ setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum );
+}
+KRecNewProperties::~KRecNewProperties() {
+kdDebug( 60005 ) << k_funcinfo << endl;
+}
+
+QString KRecNewProperties::filename() { return _filename; }
+int KRecNewProperties::samplerate() { return _samplerate; }
+int KRecNewProperties::channels() { return _channels; }
+int KRecNewProperties::bits() { return _bits; }
+bool KRecNewProperties::usedefaults() { return _usedefaults; }
+
+void KRecNewProperties::ratechanged( int rate ) { _samplerate = rate; }
+void KRecNewProperties::channelschanged( int channels ) { _channels = channels; }
+void KRecNewProperties::bitschanged( int bits ) { _bits = bits; }
+void KRecNewProperties::usedefaultschanged( bool n ) {
+ _usedefaults = n;
+ KRecGlobal::kconfig()->setGroup( "FileDefaults" );
+ KRecGlobal::kconfig()->writeEntry( "UseDefaults", _usedefaults );
+}
+
+void KRecNewProperties::done( int r ) {
+kdDebug( 60005 ) << k_funcinfo << endl;
+ QDialog::done( r );
+}
+
diff --git a/krec/krecnewproperties.h b/krec/krecnewproperties.h
new file mode 100644
index 00000000..f398033b
--- /dev/null
+++ b/krec/krecnewproperties.h
@@ -0,0 +1,60 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#ifndef KREC_NEW_PROPERTIES_H
+#define KREC_NEW_PROPERTIES_H
+
+#include <qdialog.h>
+
+class KConfig;
+class QBoxLayout;
+class QButtonGroup;
+class QRadioButton;
+class KPushButton;
+class QHBox;
+class QVBox;
+
+class KRecConfigFilesWidget;
+
+class KRecNewProperties : public QDialog {
+ Q_OBJECT
+public:
+ KRecNewProperties( QWidget*, const char* =0 );
+ ~KRecNewProperties();
+
+ QString filename();
+ int samplerate();
+ int channels();
+ int bits();
+ bool usedefaults();
+
+ void done( int );
+private slots:
+ void ratechanged( int );
+ void channelschanged( int );
+ void bitschanged( int );
+ void usedefaultschanged( bool );
+private:
+ QString _filename;
+ KRecConfigFilesWidget *_filewidget;
+
+ int _samplerate, _channels, _bits;
+ bool _usedefaults;
+
+ QBoxLayout *_layout, *_layoutbuttons;
+ KPushButton *_btnok;
+};
+
+#endif
+
+// vim:sw=4:ts=4
diff --git a/krec/krecord.cpp b/krec/krecord.cpp
new file mode 100644
index 00000000..72fbde7c
--- /dev/null
+++ b/krec/krecord.cpp
@@ -0,0 +1,407 @@
+/***************************************************************************
+ begin : Mon Jun 17 2002
+ copyright : (C) 2002 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#include "krecord.h"
+#include "krecord_private.h"
+
+#include "artsactions.h"
+// VolumeControl
+#include "artsmoduleseffects.h"
+
+#include "krecfile.h"
+#include "krecglobal.h"
+#include "krecexport_template.h"
+
+#include <arts/kaudioplaystream.h>
+#include <arts/kaudiorecordstream.h>
+#include <kfiledialog.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kaction.h>
+#include <kconfig.h>
+#include <qlayout.h>
+#include <qtimer.h>
+#include <ksettings/dialog.h>
+#include <ktip.h>
+
+KRecPrivate::KRecPrivate( KRecord* p, const char* n )
+ : QObject( p,n )
+ , _confdlg( 0 )
+ , server( new KArtsServer( 0 ) )
+ , dispatcher( new KArtsDispatcher( 0 ) )
+ , m_playStream( new KAudioPlayStream( server, "KRec::Out", server ) )
+ , m_recStream( new KAudioRecordStream( server, "KRec::In", server ) )
+ , w( 0 )
+ , b_arts( true ), b_comp( true )
+ , _impl( p )
+ , mainwidget( new KRecMainWidget( p ) )
+ , _currentFile( 0 )
+ , _exportitem( 0 )
+{
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+ m_recStream->usePolling( false );
+ if( m_recStream->effectStack().isNull() )
+ kdFatal() << "Couldn't get Object 'StereoEffectStack' from KAudioRecordStream!!!" << endl;
+ volumecontrol = Arts::DynamicCast( server->server().createObject( "Arts::StereoVolumeControl" ) );
+ if( volumecontrol.isNull() )
+ kdFatal()<<"Couldn't create Object 'StereoVolumeControl' !!!"<<endl;
+ comp = Arts::DynamicCast( server->server().createObject( "Arts::Synth_STEREO_COMPRESSOR" ) );
+ if( comp.isNull() )
+ b_comp = false;
+
+ volumecontrol.start();
+ if( b_comp ) comp.start();
+ vc_id = m_recStream->effectStack().insertBottom( volumecontrol, "VolumeControl" );
+ if( b_comp ) comp_id = m_recStream->effectStack().insertTop( comp, "Compressor" );
+
+ Arts::StereoVolumeControlGui gui = Arts::StereoVolumeControlGui( volumecontrol );
+ gui.title( i18n( "Recording level" ).utf8().data() );
+ mainwidget->_artswidget = gui;
+ mainwidget->_kaw_volumecontrol->setContent( mainwidget->_artswidget );
+
+}
+
+KRecPrivate::~KRecPrivate() {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+ mainwidget->_artswidget = Arts::StereoVolumeControlGui::null();
+ delete _currentFile;
+ _currentFile = 0;
+ //kdDebug( 60005 ) << k_funcinfo << "done." << endl;
+}
+
+void KRecPrivate::showConfDialog() {
+ if ( !_confdlg )
+ _confdlg = new KSettings::Dialog( _impl );
+ _confdlg->show();
+}
+
+void KRecPrivate::newFile() {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+ if ( _currentFile ) closeFile();
+ if ( !_currentFile ) {
+ pNewFile( new KRecFile( this ) );
+ }
+}
+void KRecPrivate::openFile() {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+ if ( _currentFile ) closeFile();
+ if ( !_currentFile ) {
+ QString filename = KFileDialog::getOpenFileName( "", "*.krec", _impl );
+ //kdDebug( 60005 ) << k_funcinfo << filename << endl;
+ if ( !filename.isNull() )
+ pNewFile( new KRecFile( filename, this ) );
+ }
+}
+void KRecPrivate::pNewFile( KRecFile* file ) {
+ _currentFile = file;
+ connect( m_recStream, SIGNAL( data( QByteArray& ) ), _currentFile, SLOT( writeData( QByteArray& ) ) );
+ connect( m_playStream, SIGNAL( requestData( QByteArray& ) ), _currentFile, SLOT( getData( QByteArray& ) ) );
+ mainwidget->_fileview->setFile( _currentFile );
+ checkActions();
+}
+
+void KRecPrivate::saveFile() {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+ if ( _currentFile ) pSaveFile( _currentFile->filename() );
+}
+void KRecPrivate::saveAsFile() {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+ if ( _currentFile ) pSaveFile( QString::null );
+}
+void KRecPrivate::pSaveFile( const QString &filename ) {
+ if ( !_currentFile )
+ return;
+
+ if ( !filename.isNull() )
+ _currentFile->save( filename );
+ else {
+ QString userFilename = KFileDialog::getSaveFileName( "", "*.krec", _impl, i18n( "Save File As" ) );
+ if ( !userFilename.isNull() )
+ _currentFile->save( userFilename );
+ }
+}
+
+bool KRecPrivate::closeFile() {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+ if ( _currentFile ) {
+ if ( !_currentFile->saved() ) {
+ int choice = KMessageBox::questionYesNoCancel( _impl, i18n( "The document \"%1\" has been modified.\nDo you want to save it?" ).arg( _currentFile->filename() ), QString::null, KStdGuiItem::save(), KStdGuiItem::discard() );
+ if ( choice == KMessageBox::Yes ) saveFile();
+ if ( choice == KMessageBox::Cancel ) return false;
+ // go on if KMessageBox::No
+ }
+ delete _currentFile;
+ _currentFile = 0;
+ mainwidget->_fileview->setFile( _currentFile );
+ }
+ checkActions();
+ return true;
+}
+
+void KRecPrivate::exportFile() {
+ kdDebug( 60005 ) << k_funcinfo << endl;
+ if ( _currentFile ) {
+ QString filename = KFileDialog::getSaveFileName( "", KRecGlobal::the()->exportFormatEndings(), _impl, "Export File As" );
+ if ( !filename.isNull() ) {
+ int lastdot = filename.find( '.', -5 );
+ QString ending = filename.right( filename.length()-lastdot-1 );
+ _exportitem = KRecGlobal::the()->getExportItemForEnding( ending );
+ if ( _exportitem ) {
+ _exportitem->initialize( _currentFile->samplerate(), _currentFile->bits(), _currentFile->channels() );
+ if ( _exportitem->initialize( filename ) ) {
+ connect( _exportitem, SIGNAL( getData( QByteArray& ) ), _currentFile, SLOT( getData( QByteArray& ) ) );
+ connect( _currentFile, SIGNAL( endReached() ), _exportitem, SLOT( stop() ) );
+ connect( _currentFile, SIGNAL( endReached() ), this, SLOT( endExportFile() ) );
+ _exportitem->start();
+ }
+ } else
+ KMessageBox::detailedSorry( _impl,
+ i18n( "Sorry, an encoding method could not be determined." ),
+ i18n( "<qt>This can have several reasons:<ul>" \
+ "<li>You did not specify an ending.</li>" \
+ "<li>You specified an ending but there is no plugin available " \
+ "for this ending. In both cases be sure to choose an ending of " \
+ "the list presented in the previous dialog.</li>" \
+ "<li>The plugin loading mechanism isn't working. If you are sure " \
+ "you did everything right, please file a bugreport saying what " \
+ "you where about to do and please quote the following line:<br />" \
+ "%1</li>" \
+ "</ul></qt>" ).arg( KRecGlobal::the()->exportFormatEndings() ),
+ i18n( "Could not determine encodingmethod" ) );
+ }
+ } else KRecGlobal::the()->message( i18n( "There is nothing to export." ) );
+ checkActions();
+}
+void KRecPrivate::endExportFile() { QTimer::singleShot( 20, this, SLOT( endExportFile2() ) ); }
+void KRecPrivate::endExportFile2() {
+ _exportitem->finalize();
+ disconnect( _currentFile, 0, _exportitem, 0 );
+ disconnect( _exportitem, 0, 0, 0 );
+ checkActions();
+}
+
+void KRecPrivate::playthru( bool yes ){
+ kdDebug( 60005 ) << k_funcinfo << yes << endl;
+ if(yes) {
+ Arts::connect( m_recStream->effectStack(), m_playStream->effectStack() );
+ kdDebug( 60005 ) << "Making a connection between recStream and playStream :-)" << endl;
+ } else {
+ Arts::disconnect( m_recStream->effectStack(), m_playStream->effectStack() );
+ kdDebug( 60005 ) << "Disconnecting recStream and playStream :-(" << endl;
+ }
+}
+
+void KRecPrivate::checkActions(){
+ _impl->actionCollection()->action( "player_record" )->setEnabled( false );
+ _impl->actionCollection()->action( "player_play" )->setEnabled( false );
+ _impl->actionCollection()->action( "player_stop" )->setEnabled( false );
+ _impl->actionCollection()->action( "player_gobegin" )->setEnabled( false );
+ _impl->actionCollection()->action( "player_goend" )->setEnabled( false );
+ _impl->actionCollection()->action( "export_file" )->setEnabled( false );
+ _impl->actionCollection()->action( "file_save" )->setEnabled( false );
+ _impl->actionCollection()->action( "file_save_as" )->setEnabled( false );
+ _impl->actionCollection()->action( "file_close" )->setEnabled( false );
+ if ( _currentFile ) {
+ if ( !_exportitem || ( _exportitem && !_exportitem->running() ) ) {
+ if ( !m_recStream->running() && !m_playStream->running() ) {
+ _impl->actionCollection()->action( "player_record" )->setEnabled( true );
+ _impl->actionCollection()->action( "player_play" )->setEnabled( true );
+ }
+ if ( m_playStream->running() || m_recStream->running() )
+ _impl->actionCollection()->action( "player_stop" )->setEnabled( true );
+ if ( _currentFile->position() != 0 )
+ _impl->actionCollection()->action( "player_gobegin" )->setEnabled( true );
+ if ( _currentFile->position() != _currentFile->size() )
+ _impl->actionCollection()->action( "player_goend" )->setEnabled( true );
+ }
+ _impl->actionCollection()->action( "export_file" )->setEnabled( true );
+ _impl->actionCollection()->action( "file_close" )->setEnabled( true );
+ //if ( !_currentFile->saved() ) {
+ _impl->actionCollection()->action( "file_save" )->setEnabled( true );
+ _impl->actionCollection()->action( "file_save_as" )->setEnabled( true );
+ //}
+ }
+}
+
+void KRecPrivate::toBegin() {
+ if ( _currentFile )
+ _currentFile->newPos( 0 );
+ checkActions();
+}
+void KRecPrivate::toEnd() {
+ if ( _currentFile )
+ _currentFile->newPos( _currentFile->size() );
+ checkActions();
+}
+
+void KRecPrivate::forceTipOfDay() {
+ KTipDialog::showTip( _impl, QString::null, true );
+}
+
+void KRecPrivate::execaRtsControl() {
+ kapp->kdeinitExec( "artscontrol" );
+}
+void KRecPrivate::execKMix() {
+ kapp->kdeinitExec( "kmix" );
+}
+
+/**
+
+ KRecord:
+
+*/
+
+KRecord::KRecord(QWidget *parent, const char *name )
+ : KMainWindow(parent,name)
+ , d( new KRecPrivate( this ) )
+{
+ kdDebug( 60005 ) << k_funcinfo << endl;
+ //kdDebug( 60005 )<<"KRecord::KRecord( QWidget *"<<parent<<", const char* "<<name<<" )"<<endl; // (DEBUG)
+
+ KRecGlobal::the()->setMainWidget( this );
+ KRecGlobal::the()->setStatusBar( statusBar() );
+
+ // * * * Startup-Window * * *
+ KTipDialog::showTip( this );
+
+ // * * * Mainwidget * * *
+ setCentralWidget( d->mainwidget );
+
+ // * * * Actions * * *
+ d->artsactions = new ArtsActions( d->server, actionCollection(), this );
+
+ KStdAction::preferences( d, SLOT( showConfDialog() ), actionCollection() );
+
+ KStdAction::openNew( d, SLOT( newFile() ), actionCollection() );
+ KStdAction::open( d, SLOT( openFile() ), actionCollection() );
+ KStdAction::save( d, SLOT( saveFile() ), actionCollection() );
+ KStdAction::saveAs( d, SLOT( saveAsFile() ), actionCollection() );
+ KStdAction::close( d, SLOT( closeFile() ), actionCollection() );
+ KStdAction::quit( this, SLOT( close() ), actionCollection() );
+
+ KStdAction::tipOfDay( d, SLOT( forceTipOfDay() ), actionCollection() );
+
+ d->aExportFile = new KAction( i18n( "Export..." ), KShortcut(),
+ d, SLOT( exportFile() ), actionCollection(), "export_file" );
+
+ d->aRecord = new KAction( i18n( "&Record" ), KShortcut( Key_R ),
+ this, SLOT( startRec() ), actionCollection(), "player_record" );
+ d->aPlay = new KAction( i18n( "&Play" ), KShortcut( Key_P ),
+ this, SLOT( startPlay() ), actionCollection(), "player_play" );
+ d->aStop = new KAction( i18n( "&Stop" ), KShortcut( Key_S ),
+ this, SLOT( stopRec() ), actionCollection(), "player_stop" );
+ d->aThru = new KToggleAction( i18n( "Play Through" ), KShortcut( CTRL + Key_P), actionCollection(), "play_thru" );
+ connect( d->aThru, SIGNAL( toggled( bool ) ), d, SLOT( playthru( bool ) ) );
+
+ d->aBegin = new KAction( i18n( "Go to &Beginning" ), KShortcut( SHIFT + Key_Left ),
+ d, SLOT( toBegin() ), actionCollection(), "player_gobegin" );
+ d->aEnd = new KAction( i18n( "Go to &End" ), KShortcut( SHIFT + Key_Right ),
+ d, SLOT( toEnd() ), actionCollection(), "player_goend" );
+
+ ( void* ) d->artsactions->actionAudioManager();
+
+ d->aExecaRtsControl = new KAction( i18n( "Start aRts Control Tool" ), KShortcut(),
+ d, SLOT( execaRtsControl() ), actionCollection(), "exec_artscontrol" );
+ d->aExecKMix = new KAction( i18n( "Start KMix" ), KShortcut(),
+ d, SLOT( execKMix() ), actionCollection(), "exec_kmix" );
+
+ // * * * GUI * * *
+ // TODO Fix toolbar config so this line can just be setupGUI()
+ setupGUI(KMainWindow::Keys | StatusBar | Save | Create );
+ setStandardToolBarMenuEnabled( true );
+ // TODO Fix the arts toolbar to know it own minium size, this app shouldn't care!
+ setMinimumWidth( 400 );
+
+ if( d->b_comp )
+ {
+ Arts::GenericGuiFactory factory;
+ Arts::Widget aw = factory.createGui( d->comp );
+ d->w = new KArtsWidget( aw, toolBar("compressor") );
+ d->w->setName( "kde toolbar widget" );
+
+ toolBar( "compressor" )->insertWidget( 1, 400, d->w );
+ toolBar( "compressor" )->setBarPos( KToolBar::Bottom );
+ }
+ else
+ {
+ toolBar( "compressor" )->close();
+ KMessageBox::detailedSorry( this,
+ i18n( "Your system is missing the Synth_STEREO_COMPRESSOR aRts module.\nYou will be able to use KRec but without the great functions of the compressor." ),
+ i18n( "Possible reasons are:\n- You installed KRec on its own without the rest of kdemultimedia.\n- You installed everything correctly, but did not restart the aRts daemon\n and therefore it is not aware of the new effects.\n- This is a bug." ),
+ i18n( "Unable to Find Compressor" ) );
+ }
+
+ d->checkActions();
+}
+
+KRecord::~KRecord(){
+ kdDebug( 60005 ) << k_funcinfo << endl; // (DEBUG)
+ stopRec();
+
+ d->m_recStream->effectStack().remove( d->vc_id );
+ if( d->b_comp ) d->m_recStream->effectStack().remove( d->comp_id );
+ d->volumecontrol.stop();
+ if( d->b_comp ) d->comp.stop();
+
+ d->volumecontrol = Arts::StereoVolumeControl::null();
+ if( d->b_comp ) d->comp = Arts::StereoEffect::null();
+
+ KRecGlobal::kconfig()->sync();
+ delete d;
+ kdDebug( 60005 ) << k_funcinfo << "done. Bye!" << endl;
+}
+
+void KRecord::startRec(){
+ if( !d->m_recStream->running() && d->_currentFile ) {
+ //kdDebug( 60005 )<<"KRecord::startRec()"<<endl; // (DEBUG)
+ d->_currentFile->newBuffer();
+ d->m_recStream->start( d->_currentFile->samplerate(),
+ d->_currentFile->bits(),
+ d->_currentFile->channels() );
+ }
+ d->checkActions();
+ d->mainwidget->_fileview->updateGUI();
+}
+
+void KRecord::startPlay(){
+ if ( !d->m_playStream->running() && d->_currentFile ) {
+ d->m_playStream->start( d->_currentFile->samplerate(),
+ d->_currentFile->bits(),
+ d->_currentFile->channels() );
+ }
+ d->checkActions();
+ d->mainwidget->_fileview->updateGUI();
+}
+
+void KRecord::stopRec(){
+ if ( d->m_recStream->running() )
+ d->m_recStream->stop();
+ if ( d->m_playStream->running() )
+ d->m_playStream->stop();
+ d->checkActions();
+ d->mainwidget->_fileview->updateGUI();
+}
+
+
+bool KRecord::queryClose() {
+ kdDebug( 60005 ) << k_funcinfo << endl;
+ return d->closeFile();
+}
+
+#include "krecord.moc"
+#include "krecord_private.moc"
+
+// vim:sw=4:ts=4
diff --git a/krec/krecord.h b/krec/krecord.h
new file mode 100644
index 00000000..3e127cc4
--- /dev/null
+++ b/krec/krecord.h
@@ -0,0 +1,60 @@
+/***************************************************************************
+ begin : Mon Jun 17 2002
+ copyright : (C) 2002 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#ifndef KRECORD_H
+#define KRECORD_H
+
+#include <kmainwindow.h>
+
+class KRecPrivate;
+
+/// KDE Recording GUI.
+
+/**
+ Presents a GUI for Recording.
+
+ Supports various takes, Play-Thru and Playing of previous takes.
+
+ @author Arnold Krille
+ */
+
+class KRecord : public KMainWindow {
+ Q_OBJECT
+public:
+ /// Constructor
+ KRecord( QWidget* /*parent*/=0, const char* /*name*/=0 );
+ /// Destructor
+ ~KRecord();
+public slots:
+ /// Does nothing.
+ void nothing() { }
+ /// Starts Recording.
+ void startRec();
+ /// Starts Playback.
+ void startPlay();
+ /// Stops Recording.
+ void stopRec();
+
+protected slots:
+ /// Checks all the Actions and sets Enabled/Disabled.
+ //void checkActions();
+protected:
+ bool queryClose();
+private:
+ KRecPrivate *d;
+};
+
+#endif
+
+// vim:sw=4:ts=4
diff --git a/krec/krecord_private.h b/krec/krecord_private.h
new file mode 100644
index 00000000..8c157f41
--- /dev/null
+++ b/krec/krecord_private.h
@@ -0,0 +1,148 @@
+/***************************************************************************
+ begin : Mon Jun 17 2002
+ copyright : (C) 2002 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#ifndef KRECORD_PRIVATE_H
+#define KRECORD_PRIVATE_H
+
+#include <qobject.h>
+
+#include <kartsserver.h>
+#include <kartsdispatcher.h>
+#include <artsflow.h>
+#include <artsgui.h>
+#include <kartswidget.h>
+#include <kurl.h>
+#include <qlayout.h>
+
+#include "krecfileview.h"
+
+class KRecord;
+class KRecMainWidget;
+class KRecFile;
+class KRecExportItem;
+
+class KAudioRecordStream;
+class KAudioPlayStream;
+class KConfig;
+class ArtsActions;
+class KAction;
+class KActionMenu;
+class KToggleAction;
+class KRecentFilesAction;
+
+namespace KSettings { class Dialog; }
+
+class KRecPrivate : public QObject {
+ Q_OBJECT
+public:
+ /// Constructor
+ KRecPrivate( KRecord*, const char* =0 );
+ /// Destructor
+ ~KRecPrivate();
+public slots:
+/* /// Does nothing.
+ void nothing() { }
+ /// Starts Recording.
+ void startRec();
+ /// Starts Playback.
+ void startPlay();
+ /// ByteStreamSender informs of his playing.
+ void playIsRunning() { playing=true; checkActions(); }
+ /// ByteStreamSender informs of his playing.
+ void playStops() { playing=false; checkActions(); }
+ /// Stops Recording.
+ void stopRec();
+
+ /// If we can play or not.
+ void canPlay( bool b ) { _canplay = b; checkActions(); }
+ /// If we can record or not.
+ void canRecord( bool b ) { _canrecord = b; checkActions(); }
+*/
+
+// void startUpWindow( bool on );
+
+ void showConfDialog();
+
+ /// Checks all the Actions and sets Enabled/Disabled.
+ void checkActions();
+
+ /// Enables Play-thru from the AMAN_RECORD to the AMAN_PLAY.
+ void playthru( bool );
+
+ void newFile();
+ void openFile();
+ void saveFile();
+ void saveAsFile();
+ bool closeFile();
+ void exportFile();
+ void endExportFile();
+ void endExportFile2();
+
+ void toBegin();
+ void toEnd();
+
+ void forceTipOfDay();
+ void execaRtsControl();
+ void execKMix();
+private:
+ void pNewFile( KRecFile* );
+ void pSaveFile( const QString &);
+public:
+ KAction *aRecord, *aPlay, *aStop, *aExportFile;
+ KAction *aBegin, *aEnd;
+ KToggleAction *aThru, *aStartUpWindow;
+ KAction *aExecaRtsControl, *aExecKMix;
+
+ KSettings::Dialog *_confdlg;
+
+ KArtsServer *server;
+ KArtsDispatcher *dispatcher;
+ KAudioPlayStream *m_playStream;
+ KAudioRecordStream * m_recStream;
+
+ Arts::StereoVolumeControl volumecontrol;
+ Arts::StereoEffect comp;
+ KArtsWidget *w;
+ long vc_id, comp_id;
+ ArtsActions *artsactions;
+
+ bool b_arts, b_comp;
+
+ KRecord *_impl;
+ KRecMainWidget *mainwidget;
+
+ KRecFile *_currentFile;
+
+ KRecExportItem *_exportitem;
+};
+
+class KRecMainWidget : public QWidget {
+ Q_OBJECT
+public:
+ KRecMainWidget( QWidget* p, const char* n=0 ) : QWidget( p,n ) {
+ _layout = new QBoxLayout( this, QBoxLayout::LeftToRight, 2 );
+ _kaw_volumecontrol = new KArtsWidget( this );
+ _layout->addWidget( _kaw_volumecontrol, 0 );
+ _fileview = new KRecFileView( this );
+ _layout->addWidget( _fileview, 500 );
+ }
+ QBoxLayout *_layout;
+ KArtsWidget *_kaw_volumecontrol;
+ Arts::Widget _artswidget;
+ KRecFileView *_fileview;
+};
+
+#endif
+
+// vim:sw=4:ts=4
diff --git a/krec/krecui.rc b/krec/krecui.rc
new file mode 100644
index 00000000..169670bd
--- /dev/null
+++ b/krec/krecui.rc
@@ -0,0 +1,57 @@
+<!DOCTYPE kpartgui ><kpartgui name="krec" version="1">
+<MenuBar>
+ <Menu name="file">
+ <Action name="export_file" />
+ <Separator />
+ </Menu>
+ <Menu name="play">
+ <text>&amp;Play</text>
+ <Action name="player_play" />
+ <Action name="player_record" />
+ <Action name="player_stop" />
+ <Separator />
+ <Action name="player_gobegin" />
+ <Action name="player_goend" />
+ <Separator />
+ <Action name="play_thru" />
+ </Menu>
+ <Menu name="tools">
+ <Action name="artssupport_view_audiomanager" />
+ <Separator />
+ <Action name="exec_artscontrol" />
+ <Action name="exec_kmix" />
+ </Menu>
+ <Menu name="settings">
+ </Menu>
+</MenuBar>
+<ToolBar name="mainToolBar">
+ <text>Main Toolbar</text>
+ <Action name="file_save_as" />
+ <Action name="export_file" />
+ <Separator />
+ <Action name="file_close" />
+</ToolBar>
+<ToolBar name="play">
+ <text>Play</text>
+ <Action name="player_play" />
+ <Action name="player_record" />
+ <Action name="player_stop" />
+ <Action name="player_gobegin" />
+ <Action name="player_goend" />
+</ToolBar>
+<ToolBar name="compressor">
+ <text>Compressor</text>
+</ToolBar>
+<ActionProperties>
+ <Action name="export_file" icon="fileexport" />
+ <Action name="player_play" icon="player_play" />
+ <Action name="player_record" icon="krec_record" />
+ <Action name="player_stop" icon="player_stop" />
+ <Action name="play_thru" icon="kmix" />
+ <Action name="player_gobegin" icon="player_start" />
+ <Action name="player_goend" icon="player_end" />
+ <Action name="settings_startup" icon="wizard" />
+ <Action name="exec_artscontrol" icon="artscontrol" />
+ <Action name="exec_kmix" icon="kmix" />
+</ActionProperties>
+</kpartgui>
diff --git a/krec/main.cpp b/krec/main.cpp
new file mode 100644
index 00000000..6e752817
--- /dev/null
+++ b/krec/main.cpp
@@ -0,0 +1,68 @@
+/***************************************************************************
+ begin : Mon Jun 17 2002
+ copyright : (C) 2002 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+// Should stay here because I use it for Start-/Stop-Info (see below)
+//#include <iostream>
+
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kaboutdata.h>
+#include <klocale.h>
+
+#include "krecord.h"
+
+#include <qobject.h>
+
+using namespace std;
+
+static const char description[] =
+ I18N_NOOP("\
+This is a recording tool for KDE.\n\
+It uses aRts, just look at the audiomanager\n\
+and you will find it there accepting sound\n\
+for recording."
+ );
+
+static KCmdLineOptions options[] =
+{
+ KCmdLineLastOption
+ // INSERT YOUR COMMANDLINE OPTIONS HERE
+};
+
+extern "C" KDE_EXPORT int kdemain( int argc, char* argv[] )
+{
+ //cout<<endl<<"Starting kRec..."<<endl;
+
+ KAboutData aboutData( "krec", I18N_NOOP("KRec"),
+ "0.5.1", description, KAboutData::License_GPL,
+ "(c) 2002, 2003 Arnold Krille" );
+ aboutData.addAuthor("Arnold Krille", I18N_NOOP( "Creator \nLook at the website www.arnoldarts.de \nfor other good stuff." ), "arnold@arnoldarts.de");
+ aboutData.addCredit( "Matthias Kretz", I18N_NOOP( "Helped where he was asked" ), "kretz@kde.org" );
+ aboutData.addCredit( "Stefan Asserhaell", I18N_NOOP( "Made some minor improvements" ) );
+ aboutData.addCredit( "Stefan Asserhaell & Nikolas Zimmermann", I18N_NOOP( "They indirectly wrote the exports. At least I learned from their files and patches." ) );
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.
+
+ KApplication app;
+ KRecord *w = new KRecord();
+ w->show();
+ app.setMainWidget(w);
+
+ int quit = app.exec();
+ //cout<<"Quitting with "<<quit<<endl;
+ return quit;
+}
+
+// vim:sw=4:ts=4
+
diff --git a/krec/mp3_export/Makefile.am b/krec/mp3_export/Makefile.am
new file mode 100644
index 00000000..191d7db3
--- /dev/null
+++ b/krec/mp3_export/Makefile.am
@@ -0,0 +1,22 @@
+
+INCLUDES= \
+ -I$(top_builddir)/arts/gui/common \
+ -I$(top_builddir)/arts/midi \
+ -I$(top_builddir)/arts/modules/common \
+ -I$(top_builddir)/arts/modules/synth \
+ -I$(top_builddir)/arts/modules/effects \
+ -I$(top_srcdir)/arts/gui/kde \
+ -I$(top_srcdir)/arts/tools \
+ -I$(top_srcdir)/krec \
+ -I$(arts_includes) $(all_includes)
+
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = libkrecexport_mp3.la
+
+libkrecexport_mp3_la_SOURCES = krecexport_mp3.cpp
+libkrecexport_mp3_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) $(KDE_RPATH)
+libkrecexport_mp3_la_LIBADD = ../lib_krec_common.la $(LAME_LIBS)
+
+kde_services_DATA = krec_exportmp3.desktop
+
diff --git a/krec/mp3_export/krec_exportmp3.desktop b/krec/mp3_export/krec_exportmp3.desktop
new file mode 100644
index 00000000..18d097c5
--- /dev/null
+++ b/krec/mp3_export/krec_exportmp3.desktop
@@ -0,0 +1,59 @@
+[Desktop Entry]
+Type=Service
+Name=MP3-Export
+Name[bg]=Експортиране в MP3
+Name[bn]=এম-পি-৩ রপ্তানি
+Name[br]=Ezporzh MP3
+Name[ca]=Exportació MP3
+Name[cs]=Export do MP3
+Name[cy]=Allforio MP3
+Name[da]=MP3-Eksport
+Name[el]=Εξαγωγή MP3
+Name[eo]=MP3-Eksporto
+Name[es]=Exportador de MP3
+Name[et]=MP3-eksport
+Name[eu]=MP3-esportazioa
+Name[fa]=صادرات MP3
+Name[fi]=MP3-vienti
+Name[fr]=Exportation en MP3
+Name[ga]=Easpórtáil MP3
+Name[gl]=Exportación a MP3
+Name[he]=יצוא MP3
+Name[hi]=MP3-निर्यात
+Name[hu]=Exportálás - MP3
+Name[is]=MP3 útflutningur
+Name[it]=Esporta-MP3
+Name[ja]=MP3 エクスポート
+Name[kk]=MP3-экспорттау
+Name[km]=នាំចេញ-MP3
+Name[ko]=MP3 추출
+Name[lt]=MP3 eksportas
+Name[mk]=MP3-изнесување
+Name[nb]=MP3-eksport
+Name[ne]=MP3-निर्यात
+Name[nl]=MP3-export
+Name[nn]=MP3-eksport
+Name[pa]=MP3-ਨਿਰਯਾਤ
+Name[pl]=Eksport MP3
+Name[pt]=Exportação de MP3
+Name[pt_BR]=Exportar-MP3
+Name[ro]=Exportare MP3
+Name[ru]=Экспорт в mp3
+Name[sk]=Export do MP3
+Name[sl]=Izvoz v MP3
+Name[sr]=Извоз у MP3
+Name[sr@Latn]=Izvoz u MP3
+Name[sv]=MP3-export
+Name[ta]=MP3-ஏற்றுமதி
+Name[tg]=Содироти MP3
+Name[th]=ส่งออกเป็น MP3
+Name[tr]=MP3 Aktarımı
+Name[uk]=Експорт MP3
+Name[uz]=MP3 eksport
+Name[uz@cyrillic]=MP3 экспорт
+Name[zh_CN]=MP3-导出
+Name[zh_HK]=MP3-匯出
+Name[zh_TW]=MP3 匯出
+X-KDE-Library=libkrecexport_mp3
+ServiceTypes=KRec/exportplugin
+X-KDE-ExportSuffix=mp3,MP3
diff --git a/krec/mp3_export/krecexport_mp3.cpp b/krec/mp3_export/krecexport_mp3.cpp
new file mode 100644
index 00000000..85418688
--- /dev/null
+++ b/krec/mp3_export/krecexport_mp3.cpp
@@ -0,0 +1,234 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#include "krecexport_mp3.h"
+#include "krecexport_mp3.moc"
+
+#include "krecglobal.h"
+
+#include <kdebug.h>
+#include <ktempfile.h>
+#include <qfile.h>
+#include <qtimer.h>
+#include <kgenericfactory.h>
+#include <stdlib.h>
+#include <time.h>
+#include <qdatetime.h>
+#include <kmessagebox.h>
+#include <kconfig.h>
+#include <kmessagebox.h>
+
+// Error code definitions from lame.h
+static const char *lame_error[] =
+{
+ I18N_NOOP( "Unknown encoding error." ),
+ I18N_NOOP( "Buffer was too small." ),
+ I18N_NOOP( "Memory allocation problem." ),
+ I18N_NOOP( "Parameter initialisation not performed." ),
+ I18N_NOOP( "Psycho acoustic problems." ),
+ I18N_NOOP( "OGG cleanup encoding error." ),
+ I18N_NOOP( "OGG frame encoding error" )
+};
+
+K_EXPORT_COMPONENT_FACTORY( libkrecexport_mp3, KGenericFactory<KRecExport_MP3> )
+
+KRecExport_MP3 krecExportMP3( 0 );
+
+KRecExport_MP3::KRecExport_MP3( QObject* p, const char* n, const QStringList& )
+ : KRecExportItem( p,n )
+ , _file( 0 )
+ , error_occurred( false )
+ , init_done( false )
+{
+kdDebug( 60005 ) << k_funcinfo << endl;
+ registerAtGlobal( this );
+kdDebug( 60005 ) << "Registered Exports: " << KRecGlobal::the()->exportFormats() << endl;
+}
+KRecExport_MP3::~KRecExport_MP3() {
+kdDebug( 60005 ) << k_funcinfo << endl;
+}
+
+KRecExport_MP3* KRecExport_MP3::newItem() {
+kdDebug( 60005 ) << k_funcinfo << endl;
+ return new KRecExport_MP3( 0 );
+}
+
+QStringList KRecExport_MP3::extensions() {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+ QStringList tmp;
+ tmp << "*.wav" << "*.WAV";
+ return tmp;
+}
+
+bool KRecExport_MP3::initialize( const QString &filename ) {
+kdDebug( 60005 ) << k_funcinfo << endl;
+ if ( !_file &&
+ !( bits()!=16 && channels()!=2 &&
+ KMessageBox::warningContinueCancel( KRecGlobal::the()->mainWidget(),
+ i18n( "At this time MP3-Export only supports files in stereo and 16bit." )
+ ) == KMessageBox::Cancel
+ )
+ ) {
+ KMessageBox::information( KRecGlobal::the()->mainWidget(),
+ i18n( "Please note that this plugin takes its qualitysettings from" \
+ " the corresponding section of the Audio CDs Control Center module" \
+ " configuration. Make use" \
+ " of the Control Center to configure these settings." ),
+ i18n( "Quality Configuration" ), "qualityinfo_mp3" );
+ _file = new QFile( filename );
+ if ( _file->open( IO_Raw|IO_WriteOnly ) ) {
+ if ( ! init_done ) {
+ gfp = lame_init();
+ setLameParameters();
+ if ( write_id3 ) {
+ id3tag_init( gfp );
+ id3tag_v1_only ( gfp );
+ id3tag_set_album ( gfp, "" );
+ id3tag_set_artist ( gfp, "" );
+ id3tag_set_title ( gfp, "" );
+ id3tag_set_comment( gfp, "krec" );
+ }
+ /// Set input parameters right
+ lame_set_in_samplerate( gfp, this->samplingRate() );
+ lame_set_num_channels( gfp, this->channels() );
+ lame_init_params( gfp );
+ }
+ if ( _file->size() >= 128 )
+ _file->at( _file->size() - 128 );
+ else
+ _file->at( _file->size() );
+ } else return false;
+ return true;
+ } else return false;
+}
+bool KRecExport_MP3::process() {
+ //kdDebug( 60005 ) << k_funcinfo << running << endl;
+ if ( _file ) {
+ if ( running() ) {
+ QByteArray bytearray( 4096 );
+ emit getData( bytearray );
+ int mp3bytes = lame_encode_buffer_interleaved( gfp,
+ reinterpret_cast<short int *>( bytearray.data() ),
+ bytearray.size() >> 2, mp3buf, sizeof( mp3buf ) );
+ if ( mp3bytes > 0 )
+ _file->writeBlock( reinterpret_cast<char *>( mp3buf ), mp3bytes );
+ if ( mp3bytes < 0 && ! error_occurred ) {
+ int code = ( mp3bytes < -6 ) ? 0 : -mp3bytes;
+ KMessageBox::detailedError( 0, i18n( "MP3 encoding error." ),
+ i18n( lame_error[ code ] ) );
+ error_occurred = true;
+ }
+ QTimer::singleShot( 10, this, SLOT( process() ) );
+ }
+ return true;
+ } else return false;
+}
+bool KRecExport_MP3::finalize() {
+kdDebug( 60005 ) << k_funcinfo << endl;
+ if ( _file ) {
+ int mp3bytes = lame_encode_flush( gfp, mp3buf, sizeof( mp3buf ) );
+ if ( mp3bytes > 0 )
+ _file->writeBlock( reinterpret_cast<char *>( mp3buf ), mp3bytes );
+ if ( mp3bytes < 0 && ! error_occurred ) {
+ int code = ( mp3bytes < -6 ) ? 0 : -mp3bytes;
+ KMessageBox::detailedError( 0, i18n( "MP3 encoding error." ),
+ i18n( lame_error[ code ] ) );
+ error_occurred = true;
+ }
+ lame_close( gfp );
+
+ _file->close();
+ delete _file;
+ _file = 0;
+
+ return true;
+ } else return false;
+}
+
+// Derived from kdemultimedia/kioslave/audiocd/audiocd.cpp.
+// We use the encoding settings from kcmaudiocd.
+void KRecExport_MP3::setLameParameters() {
+ KConfig *config;
+ config = new KConfig( "kcmaudiocdrc" );
+
+ config->setGroup( "MP3" );
+
+ int quality = config->readNumEntry( "quality",2 );
+
+ if ( quality < 0 ) quality = 0;
+ if ( quality > 9 ) quality = 9;
+
+ int method = config->readNumEntry( "encmethod",0 );
+ if ( method == 0 ) {
+ // Constant Bitrate Encoding
+ lame_set_VBR( gfp, vbr_off );
+ lame_set_brate( gfp,config->readNumEntry( "cbrbitrate",160 ) );
+ lame_set_quality( gfp, quality );
+ } else {
+ // Variable Bitrate Encoding
+ if ( config->readBoolEntry( "set_vbr_avr",true ) ) {
+ lame_set_VBR( gfp,vbr_abr );
+ lame_set_VBR_mean_bitrate_kbps( gfp,
+ config->readNumEntry( "vbr_average_bitrate",0 ) );
+ } else {
+ if ( lame_get_VBR( gfp ) == vbr_off ) lame_set_VBR( gfp, vbr_default );
+ if ( config->readBoolEntry( "set_vbr_min",true ) )
+ lame_set_VBR_min_bitrate_kbps( gfp,
+ config->readNumEntry( "vbr_min_bitrate",0 ) );
+ if ( config->readBoolEntry( "vbr_min_hard",true ) )
+ lame_set_VBR_hard_min( gfp, 1 );
+ if ( config->readBoolEntry( "set_vbr_max",true ) )
+ lame_set_VBR_max_bitrate_kbps( gfp,
+ config->readNumEntry( "vbr_max_bitrate",0 ) );
+ lame_set_VBR_q( gfp, quality );
+ }
+
+ if ( config->readBoolEntry( "write_xing_tag",true ) )
+ lame_set_bWriteVbrTag( gfp, 1 );
+ }
+
+ switch ( config->readNumEntry( "mode",0 ) ) {
+ case 0: lame_set_mode( gfp, STEREO ); break;
+ case 1: lame_set_mode( gfp, JOINT_STEREO ); break;
+ case 2: lame_set_mode( gfp, DUAL_CHANNEL ); break;
+ case 3: lame_set_mode( gfp, MONO ); break;
+ default: lame_set_mode( gfp,STEREO ); break;
+ }
+
+ lame_set_copyright( gfp, config->readBoolEntry( "copyright",false ) );
+ lame_set_original( gfp, config->readBoolEntry( "original",true ) );
+ lame_set_strict_ISO( gfp, config->readBoolEntry( "iso",false ) );
+ lame_set_error_protection( gfp, config->readBoolEntry( "crc",false ) );
+
+ write_id3 = config->readBoolEntry( "id3",true );
+
+ if ( config->readBoolEntry( "enable_lowpassfilter",false ) ) {
+ lame_set_lowpassfreq( gfp,
+ config->readNumEntry( "lowpassfilter_freq",0 ) );
+ if ( config->readBoolEntry( "set_lowpassfilter_width",false ) )
+ lame_set_lowpasswidth( gfp,
+ config->readNumEntry( "lowpassfilter_width",0 ) );
+ }
+
+ if ( config->readBoolEntry( "enable_highpassfilter",false ) ) {
+ lame_set_highpassfreq( gfp,
+ config->readNumEntry( "highpassfilter_freq",0 ) );
+ if ( config->readBoolEntry( "set_highpassfilter_width",false ) )
+ lame_set_highpasswidth( gfp,
+ config->readNumEntry( "highpassfilter_width",0 ) );
+ }
+
+ delete config;
+}
+
+// vim:sw=4:ts=4
diff --git a/krec/mp3_export/krecexport_mp3.h b/krec/mp3_export/krecexport_mp3.h
new file mode 100644
index 00000000..8bf243b3
--- /dev/null
+++ b/krec/mp3_export/krecexport_mp3.h
@@ -0,0 +1,52 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#ifndef KREC_EXPORT_MP3_H
+#define KREC_EXPORT_MP3_H
+
+#include "krecexport_template.h"
+
+#include <lame/lame.h>
+
+class KTempFile;
+class QFile;
+
+class KRecExport_MP3 : public KRecExportItem {
+ Q_OBJECT
+public:
+ KRecExport_MP3( QObject*, const char* =0, const QStringList& =0 );
+ ~KRecExport_MP3();
+
+ KRecExport_MP3* newItem();
+
+ QStringList extensions();
+ QString exportFormat() { return QString( "Wave" ); }
+public slots:
+ bool initialize( const QString & );
+ bool process();
+ bool finalize();
+
+private:
+ QFile* _file;
+ // Lame MP3 encoder
+ void setLameParameters();
+ lame_global_flags *gfp;
+ unsigned char mp3buf[ LAME_MAXMP3BUFFER ];
+ bool error_occurred;
+ bool write_id3;
+ bool init_done;
+};
+
+#endif
+
+// vim:sw=4:ts=4
diff --git a/krec/ogg_export/Makefile.am b/krec/ogg_export/Makefile.am
new file mode 100644
index 00000000..a54940c7
--- /dev/null
+++ b/krec/ogg_export/Makefile.am
@@ -0,0 +1,22 @@
+
+INCLUDES= \
+ -I$(top_builddir)/arts/gui/common \
+ -I$(top_builddir)/arts/midi \
+ -I$(top_builddir)/arts/modules/common \
+ -I$(top_builddir)/arts/modules/synth \
+ -I$(top_builddir)/arts/modules/effects \
+ -I$(top_srcdir)/arts/gui/kde \
+ -I$(top_srcdir)/arts/tools \
+ -I$(top_srcdir)/krec \
+ -I$(arts_includes) $(all_includes)
+
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = libkrecexport_ogg.la
+
+libkrecexport_ogg_la_SOURCES = krecexport_ogg.cpp
+libkrecexport_ogg_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) $(KDE_RPATH)
+libkrecexport_ogg_la_LIBADD = ../lib_krec_common.la $(VORBISENC_LIBS) $(VORBIS_LIBS)
+
+kde_services_DATA = krec_exportogg.desktop
+
diff --git a/krec/ogg_export/krec_exportogg.desktop b/krec/ogg_export/krec_exportogg.desktop
new file mode 100644
index 00000000..5c2baf90
--- /dev/null
+++ b/krec/ogg_export/krec_exportogg.desktop
@@ -0,0 +1,60 @@
+[Desktop Entry]
+Type=Service
+Name=OGG-Export
+Name[bg]=Експортиране в OGG
+Name[bn]=অগ-রপ্তানি
+Name[br]=Ezporzh OGG
+Name[ca]=Exportació OGG
+Name[cs]=Export do OGG
+Name[cy]=Allforio OGG
+Name[da]=OGG-Eksport
+Name[el]=Εξαγωγή OGG
+Name[eo]=OGG-Eksporto
+Name[es]=Exportador de OGG
+Name[et]=OGG-eksport
+Name[eu]=OGG-esportazioa
+Name[fa]=صادرات OGG
+Name[fi]=OGG-vienti
+Name[fr]=Exportation en OGG
+Name[ga]=Easpórtáil OGG
+Name[gl]=Exportación a OGG
+Name[he]=יצוא OGG
+Name[hi]=OGG-निर्यात
+Name[hu]=Exportálás - OGG
+Name[is]=OGG útflutningur
+Name[it]=Esporta-OGG
+Name[ja]=OGG エクスポート
+Name[kk]=OGG-экспорттау
+Name[km]=នាំចេញ-OGG
+Name[ko]=OGG 추출
+Name[lt]=OGG eksportavimas
+Name[mk]=OGG-изнесување
+Name[nb]=OGG-eksport
+Name[nds]=Ogg-Export
+Name[ne]=OGG-निर्यात
+Name[nl]=OGG-export
+Name[nn]=OGG-eksport
+Name[pa]=OGG-ਨਿਰਯਾਤ
+Name[pl]=Eksport OGG
+Name[pt]=Exportação de OGG
+Name[pt_BR]=Exportar-OGG
+Name[ro]=Exportare OGG
+Name[ru]=Экспорт в ogg
+Name[sk]=Export do OGG
+Name[sl]=Izvoz v OGG
+Name[sr]=Извоз у OGG
+Name[sr@Latn]=Izvoz u OGG
+Name[sv]=Ogg-export
+Name[ta]=OGG-ஏற்றுமதி
+Name[tg]=Содироти OGG
+Name[th]=ส่งออกเป็น OGG
+Name[tr]=OGG Aktarımı
+Name[uk]=Експорт OGG
+Name[uz]=OGG eksport
+Name[uz@cyrillic]=OGG экспорт
+Name[zh_CN]=OGG 导出
+Name[zh_HK]=OGG-匯出
+Name[zh_TW]=OGG 匯出
+X-KDE-Library=libkrecexport_ogg
+ServiceTypes=KRec/exportplugin
+X-KDE-ExportSuffix=ogg,OGG
diff --git a/krec/ogg_export/krecexport_ogg.cpp b/krec/ogg_export/krecexport_ogg.cpp
new file mode 100644
index 00000000..6a5d5735
--- /dev/null
+++ b/krec/ogg_export/krecexport_ogg.cpp
@@ -0,0 +1,254 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#include <config.h>
+
+#ifdef HAVE_VORBIS
+
+#include "krecexport_ogg.h"
+#include "krecexport_ogg.moc"
+
+#include "krecglobal.h"
+
+#include <kdebug.h>
+#include <ktempfile.h>
+#include <qfile.h>
+#include <qtimer.h>
+#include <kconfig.h>
+#include <stdlib.h>
+#include <time.h>
+#include <qdatetime.h>
+#include <kgenericfactory.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+K_EXPORT_COMPONENT_FACTORY( libkrecexport_ogg, KGenericFactory<KRecExport_OGG> )
+
+KRecExport_OGG krecExportOGG( 0 );
+
+KRecExport_OGG::KRecExport_OGG( QObject* p, const char* n, const QStringList& )
+ : KRecExportItem( p,n )
+ , _file( 0 )
+ , init_done( false )
+{
+kdDebug( 60005 ) << k_funcinfo << endl;
+ registerAtGlobal( this );
+kdDebug( 60005 ) << "Registered Exports: " << KRecGlobal::the()->exportFormats() << endl;
+}
+KRecExport_OGG::~KRecExport_OGG() {
+kdDebug( 60005 ) << k_funcinfo << endl;
+}
+
+KRecExport_OGG* KRecExport_OGG::newItem() {
+kdDebug( 60005 ) << k_funcinfo << endl;
+ return new KRecExport_OGG( 0 );
+}
+
+QStringList KRecExport_OGG::extensions() {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+ QStringList tmp;
+ tmp << "*.ogg" << "*.OGG";
+ return tmp;
+}
+
+bool KRecExport_OGG::initialize( const QString &filename ) {
+kdDebug( 60005 ) << k_funcinfo << endl;
+ if ( !_file &&
+ !( samplingRate()!=44100 && bits()!=16 && channels()!=2 &&
+ KMessageBox::warningContinueCancel( KRecGlobal::the()->mainWidget(),
+ i18n( "At this time OGG-export only supports files in 44kHz " \
+ "samplingrate, 16bit and 2 channels." )
+ ) == KMessageBox::Cancel
+ )
+ ) {
+ KMessageBox::information( KRecGlobal::the()->mainWidget(),
+ i18n( "Please note that this plugin takes its qualitysettings from" \
+ " the corresponding section of the audiocd:/ configuration. Make use" \
+ " of the Control Center to configure these settings." ),
+ i18n( "Quality Configuration" ), "qualityinfo_ogg" );
+ _file = new QFile( filename );
+ if ( _file->open( IO_Raw|IO_WriteOnly ) ) {
+ if ( ! init_done ) {
+ setOggParameters();
+ vorbis_analysis_init( &vd, &vi );
+ vorbis_block_init( &vd, &vb );
+
+ srand( time( NULL ) );
+ ogg_stream_init( &os, rand() );
+ }
+ if ( _file->size() == 0 ) {
+ ogg_packet header;
+ ogg_packet header_comm;
+ ogg_packet header_code;
+
+ vorbis_comment_init( &vc );
+ vorbis_comment_add_tag ( &vc, const_cast<char *>( "kde-encoder" ),
+ const_cast<char *>( "KRec" ) );
+ if ( write_vorbis_comments ) {
+ QDateTime dt = QDateTime::currentDateTime();
+ vorbis_comment_add_tag ( &vc, const_cast<char *>( "title" ),
+ const_cast<char *>( "" ) );
+ vorbis_comment_add_tag ( &vc, const_cast<char *>( "artist" ),
+ const_cast<char *>( "" ) );
+ vorbis_comment_add_tag ( &vc, const_cast<char *>( "album" ),
+ const_cast<char *>( "" ) );
+ vorbis_comment_add_tag ( &vc, const_cast<char *>( "genre" ),
+ const_cast<char *>( "" ) );
+ vorbis_comment_add_tag ( &vc, const_cast<char *>( "tracknumber" ),
+ const_cast<char *>( "" ) );
+ vorbis_comment_add_tag ( &vc, const_cast<char *>( "date" ),
+ const_cast<char *>( dt.toString( "yyyy-MM-dd hh:mm:ss" ).utf8().data() ) );
+ }
+ vorbis_analysis_headerout( &vd, &vc, &header, &header_comm, &header_code );
+
+ ogg_stream_packetin( &os, &header );
+ ogg_stream_packetin( &os, &header_comm );
+ ogg_stream_packetin( &os, &header_code );
+
+ while ( ogg_stream_flush( &os, &og ) ) {
+ //kdDebug( 60005 ) << "Writing Ogg/Vorbis Header." << endl;
+ _file->writeBlock( reinterpret_cast<char *>( og.header ), og.header_len );
+ _file->writeBlock( reinterpret_cast<char *>( og.body ), og.body_len );
+ }
+ } else
+ _file->at( _file->size() );
+ init_done = true;
+ } else return false;
+ return true;
+ }
+ return false;
+}
+bool KRecExport_OGG::process() {
+ //kdDebug( 60005 ) << k_funcinfo << endl;
+ if ( _file ) {
+ if ( running() ) {
+ QByteArray bytearray( 4096 );
+ emit getData( bytearray );
+ float **buffer = vorbis_analysis_buffer( &vd, bytearray.size() >> 2 );
+
+ // uninterleave samples
+ unsigned int i;
+ int index = 0;
+ int16_t sample;
+ unsigned char *packet = reinterpret_cast<unsigned char *>( bytearray.data() );
+ for ( i = 0; i < ( bytearray.size() >> 2 ); i++ ) {
+ sample = packet[ index ] | ( packet[ index + 1 ] << 8 );
+ index += 2;
+ buffer[ 0 ][ i ] = sample / 32768.0;
+ sample = packet[ index ] | ( packet[ index + 1 ] << 8 );
+ index += 2;
+ buffer[ 1 ][ i ] = sample / 32768.0;
+ }
+
+ vorbis_analysis_wrote( &vd, i );
+
+ while ( vorbis_analysis_blockout( &vd, &vb ) == 1 ) {
+#if HAVE_VORBIS >= 2
+ vorbis_analysis( &vb, NULL );
+ vorbis_bitrate_addblock( &vb );
+ while ( vorbis_bitrate_flushpacket( &vd, &op ) ) {
+#else
+ // Support for very old libvorbis
+ vorbis_analysis( &vb, &op );
+ {
+#endif
+ ogg_stream_packetin( &os, &op );
+ while ( ogg_stream_pageout( &os, &og ) ) {
+ _file->writeBlock( reinterpret_cast<char *>( og.header ), og.header_len );
+ _file->writeBlock( reinterpret_cast<char *>( og.body ), og.body_len );
+ }
+ }
+ }
+ QTimer::singleShot( 10, this, SLOT( process() ) );
+ }
+ return true;
+ } else return false;
+}
+bool KRecExport_OGG::finalize() {
+kdDebug( 60005 ) << k_funcinfo << endl;
+ if ( _file ) {
+ ogg_stream_clear( &os );
+ vorbis_block_clear( &vb );
+ vorbis_dsp_clear( &vd );
+ vorbis_info_clear( &vi );
+
+ _file->close();
+ delete _file;
+ _file = 0;
+
+ return true;
+ } else return false;
+}
+
+
+// Derived from kdemultimedia/kioslave/audiocd/audiocd.cpp.
+// We use the encoding settings from kcmaudiocd.
+void KRecExport_OGG::setOggParameters() {
+kdDebug( 60005 ) << k_funcinfo << endl;
+ KConfig *config;
+ config = new KConfig( "kcmaudiocdrc" );
+
+ config->setGroup( "Vorbis" );
+
+ // 0 for quality, 1 for managed bitrate
+ int vorbis_encode_method = config->readNumEntry( "encmethod", 0 );
+ // default quality level of 3, to match oggenc
+ double vorbis_quality = config->readDoubleNumEntry( "quality",3.0 );
+
+ int vorbis_bitrate_lower = -1;
+ if ( config->readBoolEntry( "set_vorbis_min_bitrate",false ) )
+ vorbis_bitrate_lower = config->readNumEntry( "vorbis_min_bitrate",40 ) * 1000;
+
+ int vorbis_bitrate_upper = -1;
+ if ( config->readBoolEntry( "set_vorbis_max_bitrate",false ) )
+ vorbis_bitrate_upper = config->readNumEntry( "vorbis_max_bitrate",350 ) * 1000;
+
+ // this is such a hack!
+ int vorbis_bitrate;
+ if ( vorbis_bitrate_upper != -1 && vorbis_bitrate_lower != -1 )
+ vorbis_bitrate = 104000; // empirically determined ...?!
+ else
+ vorbis_bitrate = 160 * 1000;
+
+ int vorbis_bitrate_nominal = -1;
+ if ( config->readBoolEntry( "set_vorbis_nominal_bitrate",true ) ) {
+ vorbis_bitrate_nominal = config->readNumEntry( "vorbis_nominal_bitrate",160 ) * 1000;
+ vorbis_bitrate = vorbis_bitrate_nominal;
+ }
+
+ write_vorbis_comments = config->readBoolEntry( "vorbis_comments",true );
+
+ vorbis_info_init( &vi );
+ switch ( vorbis_encode_method ) {
+ case 0:
+ // Support very old libvorbis by simply falling through
+#if HAVE_VORBIS >= 2
+ vorbis_encode_init_vbr( &vi, 2/*this->channels()*/,
+ 44100/*this->samplingRate()*/,
+ vorbis_quality/10.0 );
+ break;
+#endif
+ case 1:
+ vorbis_encode_init( &vi, 2/*this->channels()*/,
+ 44100/*this->samplingRate()*/,
+ vorbis_bitrate_upper, vorbis_bitrate_nominal,
+ vorbis_bitrate_lower );
+ break;
+ }
+
+ delete config;
+}
+
+#endif // vorbis
+
+// vim:sw=4:ts=4
diff --git a/krec/ogg_export/krecexport_ogg.h b/krec/ogg_export/krecexport_ogg.h
new file mode 100644
index 00000000..43b79d54
--- /dev/null
+++ b/krec/ogg_export/krecexport_ogg.h
@@ -0,0 +1,63 @@
+/***************************************************************************
+ copyright : (C) 2003 by Arnold Krille
+ email : arnold@arnoldarts.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#ifndef KREC_EXPORT_OGG_H
+#define KREC_EXPORT_OGG_H
+
+#include <config.h>
+
+#ifdef HAVE_VORBIS
+
+#include "krecexport_template.h"
+
+#include <vorbis/vorbisenc.h>
+
+class KTempFile;
+class QFile;
+
+class KRecExport_OGG : public KRecExportItem {
+ Q_OBJECT
+public:
+ KRecExport_OGG( QObject*, const char* =0, const QStringList& =0 );
+ ~KRecExport_OGG();
+
+ KRecExport_OGG* newItem();
+
+ QStringList extensions();
+ QString exportFormat() { return QString( "OGG-Vorbis" ); }
+public slots:
+ bool initialize( const QString & );
+ bool process();
+ bool finalize();
+
+private:
+ QFile* _file;
+
+ bool init_done;
+
+ void setOggParameters();
+ ogg_stream_state os; // take physical pages, weld into a logical stream of packets
+ ogg_page og; // one Ogg bitstream page. Vorbis packets are inside
+ ogg_packet op; // one raw packet of data for decode
+ vorbis_info vi; // struct that stores all the static vorbis bitstream settings
+ vorbis_comment vc; // struct that stores all the user comments
+ vorbis_dsp_state vd; // central working state for the packet->PCM decoder
+ vorbis_block vb; // local working space for packet->PCM decode
+ bool write_vorbis_comments;
+};
+
+#endif
+
+#endif
+
+// vim:sw=4:ts=4
diff --git a/krec/pics/Makefile.am b/krec/pics/Makefile.am
new file mode 100644
index 00000000..e92a80f3
--- /dev/null
+++ b/krec/pics/Makefile.am
@@ -0,0 +1,2 @@
+kreciconsdir = $(kde_datadir)/krec/icons
+krecicons_ICON = AUTO
diff --git a/krec/pics/cr16-action-krec_record.png b/krec/pics/cr16-action-krec_record.png
new file mode 100644
index 00000000..b808b652
--- /dev/null
+++ b/krec/pics/cr16-action-krec_record.png
Binary files differ
diff --git a/krec/pics/cr22-action-krec_record.png b/krec/pics/cr22-action-krec_record.png
new file mode 100644
index 00000000..6adff511
--- /dev/null
+++ b/krec/pics/cr22-action-krec_record.png
Binary files differ
diff --git a/krec/pics/cr32-action-krec_record.png b/krec/pics/cr32-action-krec_record.png
new file mode 100644
index 00000000..831561fc
--- /dev/null
+++ b/krec/pics/cr32-action-krec_record.png
Binary files differ
diff --git a/krec/pics/cr32-mime-krec_fileempty.png b/krec/pics/cr32-mime-krec_fileempty.png
new file mode 100644
index 00000000..0d5c334f
--- /dev/null
+++ b/krec/pics/cr32-mime-krec_fileempty.png
Binary files differ
diff --git a/krec/pics/cr32-mime-krec_fileplay.png b/krec/pics/cr32-mime-krec_fileplay.png
new file mode 100644
index 00000000..6bb24c65
--- /dev/null
+++ b/krec/pics/cr32-mime-krec_fileplay.png
Binary files differ
diff --git a/krec/pics/cr32-mime-krec_filerec.png b/krec/pics/cr32-mime-krec_filerec.png
new file mode 100644
index 00000000..e26cd05c
--- /dev/null
+++ b/krec/pics/cr32-mime-krec_filerec.png
Binary files differ
diff --git a/krec/tips b/krec/tips
new file mode 100644
index 00000000..d376a743
--- /dev/null
+++ b/krec/tips
@@ -0,0 +1,44 @@
+<tip category="KRec">
+<html>
+<h4>...that KRec does non-destructive Recording?</h4>
+<p>
+That means if you have a recording and want to record only a part for a second (third or more) time, your first (and second and later) version is still on disc and can still be restored. Only for Playback/Export the old version is overlayed by the newer one.
+</p>
+</html>
+</tip>
+
+<tip category="KRec">
+<html>
+<h4>...which event caused the first version of KRec?</h4>
+<p>
+Some friends of mine asked me whether I could do the recordings for a radioplay for them. So I was searching for an easy-to-use recording tool running on my favorite OS. After some searching (without finding something suitable) I started a first version of KRec.
+</p>
+</html>
+</tip>
+
+<tip category="KRec">
+<html>
+<h4>...that developers are very happy to hear from the users?</h4>
+<p>
+Most developers are very happy to see their applications used by other people. So if you want to say "Thank you" or you have some problems, don't hesitate to mail us/me. You can find the email addresses of the author in the "Help"-menu under "About KRec".
+</p>
+</html>
+</tip>
+
+<tip category="KRec">
+<html>
+<h4>...that you are invited to report bugs?</h4>
+<p>
+Altough a lot of testing is done, our capabilities of catching every possible event/configuration are limited. So if you find a bug use "Report Bug" in the "Help"-menu or go directly to http://bugs.kde.org.
+</p>
+</html>
+</tip>
+
+<tip category="KRec">
+<html>
+<h4>...that KRec is far from complete?</h4>
+<p>
+So if you have a nice feature you think KRec should incorporate please tell us! To avoid duplicates and improve productivity please do it via bugs.kde.org or the bug reporting tools and us wishlist as severity.
+</p>
+</html>
+</tip>
diff --git a/kscd/CDQuery.txt b/kscd/CDQuery.txt
new file mode 100644
index 00000000..51ae2ecc
--- /dev/null
+++ b/kscd/CDQuery.txt
@@ -0,0 +1,21 @@
+
+Purchases:
+http://cdnow.com/switch/from=sr-288025/target=buyweb_products/artfs=Seal
+http://www.cduniverse.com/cgi-bin/cdubin.exe/rlinka/ean=Seal/frm=lk_DiscPlay/
+
+Information:
+http://musiccentral.msn.com/MCGen/ReEntry?theurl=http%3A//musiccentral.msn.com/Search/DoFullSearch%3FArtists%3Don%26Albums%3D%26Songs%3D%26ArticleTitles%3D%26FullText%3D%26strInput%3DSeal
+http://www.ubl.com/find/form?SEARCH=Seal
+http://www.excite.com/search.gw?c=web&search=Seal&trace=a
+http://www.search.hotbot.com/hResult.html?SW=web&SM=MC&MT=Seal&DC=10&DE=2&RG=NA&_v=2
+http://www.infoseek.com/Titles?qt=Seal&col=WW&sv=IS&lk=ip-noframes&nh=10
+http://www.lycos.com/cgi-bin/pursuit?cat=lycos&query=Seal
+http://www.mckinley.com/search.gw?search=Seal&c=web&look=magellan
+http://search.yahoo.com/bin/search?p=Seal
+
+Performances:
+http://www.tourdates.com/cgi-bin/search.cgi?type=Artist&search=Seal
+
+
+
+
diff --git a/kscd/ChangeLog b/kscd/ChangeLog
new file mode 100644
index 00000000..24eeb3ee
--- /dev/null
+++ b/kscd/ChangeLog
@@ -0,0 +1,266 @@
+Wed May 31 06:18:21 2000 Dirk Frsterling <milliByte@gmx.de>
+
+ * Switched from stand alone WorkMan code to libworkman.
+ * Fixed Bugs 296, 298, 299, 301, 371, 815, 1186, 2278, 2354,
+ * 2429, 2559 and 2586 or verified that they were already fixed.
+ * updated ChangeLog.
+
+Sat May 27 09:43:58 2000 Dirk Frsterling <milliByte@gmx.de>
+
+ * Updated Kscd Magic from Synaesthesia 1.3 to Synaesthesia 2.0
+ * Fixed missing slot in CDDB configuration
+ * Added (and fixed) loop mode display.
+ * updated and sorted libWorkMan API
+ * addressed the 100dpi font problem (Thanks to John R
+ MacMillan <john@weirdways.com> for sending an idea for a fix).
+
+Sun Mar 5 10:54:40 2000 Dirk Frsterling <milliByte@gmx.de>
+
+ * Completed shuffle mode (some patch data was missing) and
+ added corresponding "shuffle" Display string.
+ * Slightly changed "loop" semantics: "loop" is no longer a single
+ playing mode but a modifier. You can now loop "playing" mode and
+ "shuffle" mode.
+
+Mon Feb 28 07:27:04 2000 Dirk Frsterling <milliByte@gmx.de>
+
+ * Added "random is shuffle" patch from
+ Bernard Kozioziemski <koz@tdl.com>
+ * Started some source formatting my better understanding. Sorry,
+ but I'm a Qt/KDE-programming-newbie and need some visual hints yet
+ to understand the code. (And I _love_ source documentation
+ (comments) ...)
+
+Tue Jan 18 17:27:26 2000 Dirk Frsterling <milliByte@DeathsDoor.com>
+
+ * Added the libWorkMan directory and inserted it into the make
+ process. Please note, that libWorkMan will be built and installed,
+ but is not currently used.
+
+1999-03-17 Harri Porten <porten@kde.org>
+
+ * kscd.cpp: applied patch from Petter Reinholdtsen <pere@hungry.com>
+ (bug#302) that prevents a segfault if cddb directory is invalid.
+ * CDDialog.cpp: fixed length of last title (bug#304).
+
+1999-03-09 Harri Porten <porten@kde.org>
+
+ * ported to Qt 2.0 strings. Haven't tested if everything still works.
+
+1999-02-18 Harri Porten <porten@kde.org>
+
+ * cddb.cpp: work around snprintf() for non-GNUish systems
+
+1998-12-22 Sam Maloney <thufir@illogic.ml.org>
+
+ * Added to last commit, as so the 'autodock' checkbox would be
+ disabled when 'docking' was unchecked, and vice versa.
+
+1998-12-20 Sam Maloney <thufir@illogic.ml.org>
+
+ * Just simple bugfixes to make more stable like...:
+ * Fixed so that when one turns on or off 'enable docking' it
+ adds/removes the dockwidget in the panel (instead of before one
+ would have to restart kscd).
+ * Fixed so that if one turned off 'enable docking' but still had
+ 'autodock enabled' on, then it would NOT dock (and remove the
+ widget).
+
+1998-12-12 Christian Esken <esken@kde.org>
+
+ * Tweaked docking a bit more. Under certain circumstances,
+ dock_widget->dock() correctly does not get called at the start
+ of the program. So if you docked, the window just vanished.
+ I added a call to dock_widget->dock() at "appropiate" places.
+
+1998-11-28 Bernd Wuebben <wuebben@petit.cornell.edu>
+
+ * kscd.cpp: resinstated old look and behavior
+ Sam, we should talk ...
+
+1998-11-20 Sam Maloney <thufir@illogic.ml.org>
+
+ * Fixed bug/"missing thing" were it would just crash when loading
+ up if it couldn't find the cddb base dir. Now it will use
+ InexactDialog to prompt the user for the directory. (cancel button
+ will force it to get from remote).
+ * NOTE: Im just going fix the inexact dialog box so it can be
+ single line input, (for the cddb dialog), then ill probably
+ release 1.2.4, unless anyone has any objections. (to lazy to fix
+ tonite)
+
+1998-11-11 Sam Maloney <thufir@illogic.ml.org>
+
+ * Fixed bug with debug flag being messed up when docking
+ (incorrect extern statement)
+ * Worked around a wierd bug thing with docking. (consult
+ docking.cpp in toggleState() )
+
+1998-11-08 Robert Bossecker <Robert.Bossecker@fresenius.de>
+
+ * Fixed problem with editing EXT info, where if you edited track
+ x, it would (stupidly) modify x-1. If editing track 1, it would
+ modify the extinfo for the Title.
+
+1998-11-06 Sam Maloney <thufir@illogic.ml.org>
+
+ * Made smtpMailer a global widget, so it could mail in background.
+ * Fixed problem were after submitting a record via SMTP, and
+ having an error, if one tried to exit Kscd, it would close all
+ widgets, but be stuck in while loop.
+ * Added error/success reporting of SMTP submissions.
+ * Fixed problem with Local Cache, where it would ignore leading
+ 0's in record file names, thus not finding them when looking for
+ that record.
+ * Added a timeout when waiting for server interaction.
+
+1998-11-04 Sam Maloney <thufir@illogic.ml.org>
+
+ * Fixed problem with 'dock' button, when AutoDock is turned on.
+ * Implimented SMTP code, so Kscd no longer requires sendmail/unix
+ mail program(s) to submit CDDB records.
+ * Added VERY requested feature, of having it retract the tray if
+ its ejected, when one hits the eject button.
+
+1998-10-14 Alexei Dets <dets@services.ru>
+
+ * Fixed CD Database Editor close handling.
+
+1998-10-13 Sam Maloney <thufir@illogic.ml.org>
+
+ * Fixed compilation problems some compilers will notice related to
+ last CDDB setup modifs
+
+1998-10-09 Sam Maloney <thufir@illogic.ml.org>
+
+ * Added "play on tray close" feature
+ * Many little fixes
+
+1998-09-23 Sam Maloney <thufir@illogic.ml.org>
+
+ * Fixed submission of CDDB records, where it would not include a
+ 'PLAYORDER=' at the bottom of the email.
+ * Switched it over from using 'mail -s <subject> < record', to
+ using sendmail. (currently adding SMTP suppport, so it uses
+ no external program for this)
+
+1998-09-21 Sam Maloney <thufir@illogic.ml.org>
+
+ * Added automatic storing of CDDB records downloaded from CDDB
+ server to local harddrive.
+ * Fixed bug with routine connecting signals, over and over again,
+ causing routines to be called too many time, annoying CDDB
+ server admins. Happens when a new CDDB (remote) request was
+ issued.
+ * Added snazyness of having the pulldown track selection box
+ display ("%02d: %s", tracknumber, trackname) for each item,
+ instead of just the number.
+
+Fri May 15 18:28:40 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu>
+
+ * docking.cpp: fixed up the docking behaviour
+
+ * Some Layout improvements. -- still not 100% satisfied with the
+ way the config dialogs look now.
+
+1998-05-12 Vadim Zaliva <lord@crocodile.org>
+
+ * Options dialog made wider
+ * In CDDB setup dialog added list of email serves.
+ * Getting SMTP servers from list of servers.
+
+1998-04-29 Vadim Zaliva <lord@crocodile.org>
+
+ * Corrected problem with not saving "UnixMailCommand" option
+ which was disabling CDDB submissions.
+
+1998-04-28 Vadim Zaliva <lord@crocodile.org>
+
+ * Corrected bug in cddb with current CD information disappearing
+ after some time of playing.
+
+1998-04-26 Vadim Zaliva <lord@crocodile.org>
+
+ * Removed potetial concurency problem in CDDB connection procedure.
+ * Added support for protocol level 3 in CDDB connections.
+
+1998-04-17 Vadim Zaliva <lord@crocodile.org>
+
+ * User and domain name now calculated once, not per each request.
+
+1998-04-16 Vadim Zaliva <lord@crocodile.org>
+
+ * Tested with Netscape Proxy
+
+ * Tested with Squid-1.1.18 HTTP proxy
+ * Tested with HTTP proxy, w/o HTTP proxy, using CDDBP and HTTP.
+ * Small bug in parsing corrected.
+
+ * State automata and parsing reimplemented
+ * Works over HTTP with and without HTTP proxy.
+ * Tested with M>Wall HTTP proxy
+
+ * Implemented more sophisticated HTTP encoding for parameters.
+ * kscd.cpp reformated.
+
+ * CDDB over HTTP works, w/o proxy now. Proxy support to be implemeted to-morrow.
+
+1998-04-15 Vadim Zaliva <lord@crocodile.org>
+
+ * cddb.h, cddb.cpp reformated
+ * tested to work with and without HTTP proxy
+ * tested with squid 1.1.18
+
+ * Added one more default server, with HTTP protocol
+ * Code for getting servers list via HTTP done and works.
+ * Changed 'cddb' object allocation.
+ * (this is intermediate commit, HTTP only partialy works)
+
+1998-04-14 Vadim Zaliva <lord@crocodile.org>
+
+ * GUI added for setting HTTP proxy to access CDDB.
+ * Config options added for HTTP proxy stuff
+ * most includes from cddb.h moved to cddb.cpp,kscd.cpp
+ * new version #1.1.1
+
+Tue Apr 14 05:17:36 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu>
+
+ * layout corrections for kscd magic dialog
+
+Sat Jan 17 21:36:27 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu>
+
+ * Enabled CDDB support. A couple of clean ups and fixes.
+
+Tue Jan 13 03:24:49 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu>
+
+ * applied Sebestyen Zoltan <szoli@digo.inf.elte.hu> FreeBSD patch.
+
+Thu Jan 1 19:08:22 1998 Bernd Johannes Wuebben <wuebben@petit.cornell.edu>
+
+ * Monster commit: Commit cddb and web integration additions
+ Let the games begin!
+
+Fri Dec 26 13:48:56 1997 Bernd Johannes Wuebben <wuebben@petit.cornell.edu>
+
+ * headerfile changes in plat_bsd.c
+
+Sat Oct 25 11:13:40 1997 Bernd Johannes Wuebben <wuebben@petit.cornell.edu>
+
+ * fixed the segfault problem when you didn't have acess permissions
+ to the cdrom device
+
+Thu Aug 14 19:49:18 1997 Bernd Johannes Wuebben <wuebben@petit.cornell.edu>
+
+ * added irix support
+ * added KSCD configuration script
+
+Wed Jun 25 01:24:39 1997 Bernd Johannes Wuebben <wuebben@petit.cornell.edu>
+
+ * added Marc van Kempen's FreeBSD patches
+ He reports also strange behaviour of kscd when reloading a CD
+ Is this with the code or with his CD drive ?
+
+ * Set proper defines that should allow for a smooth compile
+ on a SUN/ SUN SOLARIS
+
+
diff --git a/kscd/Makefile.am b/kscd/Makefile.am
new file mode 100644
index 00000000..f0b8d50b
--- /dev/null
+++ b/kscd/Makefile.am
@@ -0,0 +1,83 @@
+# $Id$
+
+mimedir = $(kde_mimedir)/text
+picsdir = $(kde_datadir)/kscd/pics
+
+INCLUDES= -I$(top_srcdir) -I$(top_builddir)/libkcddb $(all_includes)
+METASOURCES = AUTO
+
+####### Files
+
+noinst_HEADERS = kscd.h bwlednum.h ledlamp.h \
+ version.h docking.h configWidget.h
+
+kscd_SOURCES = panel.ui kscd.cpp kscd.skel ledlamp.cpp \
+ docking.cpp bwlednum.cpp \
+ configWidget.cpp configWidgetUI.ui prefs.kcfgc cddbdlg.cpp
+
+noinst_LTLIBRARIES = libkcompactdisc.la
+libkcompactdisc_la_SOURCES = kcompactdisc.cpp
+
+libkcompactdisc_la_LIBADD = $(top_builddir)/kscd/libwm/libworkman.la
+
+kde_kcfg_DATA = kscd.kcfg
+
+kscd_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+kscd_LDADD = $(LIBCDROM) $(LIBCDAUDIO) \
+ $(LIBFPE) $(LIB_KIO) -lkutils $(top_builddir)/libkcddb/libkcddb.la \
+ libkcompactdisc.la
+
+profiledata_DATA = kscd.profile.xml
+profiledatadir = $(kde_datadir)/profiles
+
+servicedata_DATA = audiocd_play.desktop
+servicedatadir = $(kde_datadir)/konqueror/servicemenus
+
+DISTCLEANFILES = config.h
+
+bin_PROGRAMS = kscd
+# Don't Build cddaslave. It doesn't work.
+# cddaslave
+
+#config.h: kscd-script
+#$(SHELL) $(srcdir)/kscd-script
+
+SUBDIRS = bitmaps libwm
+
+bin_SCRIPTS = workman2cddb.pl
+
+kscdicondir = $(kde_datadir)/kscd/icons
+kscdicon_ICON = cdsmall
+KDE_ICON = kscd
+
+xdg_apps_DATA = kscd.desktop
+
+mime_DATA = xmcd.desktop
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/kscd.pot
+
+cdrom.c:config.h
+plat_bsd386.c:config.h
+plat_freebsd.c:config.h
+plat_hpux.c:config.h
+plat_irix.c:config.h
+plat_linux.c:config.h
+plat_news.c:config.h
+plat_osf1.c:config.h
+plat_scor5.c:config.h
+plat_sun.c:config.h
+plat_sun_audio.c:config.h
+plat_sun_cdda.c:config.h
+plat_svr4.c:config.h
+plat_ultrix.c:config.h
+
+KDE_OPTIONS = nofinal
+
+
+cddbdlg.o: ../libkcddb/configbase.h ../libkcddb/cdinfodialogbase.h
+configWidget.o: ../libkcddb/configbase.h
+docking.o: ../libkcddb/configbase.h
+kscd.o: ../libkcddb/configbase.h
+kscd_skel.o: ../libkcddb/configbase.h panel.h prefs.h configWidgetUI.h
+
diff --git a/kscd/README b/kscd/README
new file mode 100644
index 00000000..e1366867
--- /dev/null
+++ b/kscd/README
@@ -0,0 +1,59 @@
+KSCD 1.2.3
+==========
+
+ * Fixed DOCKING!
+
+Basically, this version finishes a whole lot more that has been
+missing/broken since the last version that bernd did, (1.2.0).
+
+ Sam Maloney, <thufir@illogic.ml.org>
+
+KSCD 1.2.0t1
+============
+
+ * Fixed CDDB code big time.
+ * Submissions now fixed.
+ * Server requests fixed.
+ * Local storing of CDDB records. (automatic)
+
+KSCD have been broken and NOT updated, since the release of KDE 1.0, and
+that is a long time ago, so i decided to fix MAJOR problems with the CDDB
+code, now it is permitted to use and upload to the CDDB database.
+
+Sam Maloney, <thufir@illogic.ml.org>
+
+KSCD 0.8
+========
+
+Sat Jan 17 17:50:37 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu>
+
+ * KSCD 0.8
+ * CDDB submissions enabled.
+
+
+Remember if kscd seems to have trouble with your cd drive, do a 'make clean'
+then 'make' and answer the questions posed by the kscd-script to configure
+kscd suitable for your cdrom drive.
+
+Bernd
+
+KSCD 0.6
+========
+
+ CDDB support, web integration
+
+Bernd
+
+KSCD 0.4
+========
+
+This is version 0.4 of kscd. Kscd is now easier to build than
+ever with its new configuration script. Supprt was added
+for Irix and Sun/Solaris cdda.
+
+Please see the html documentation in the docs directory for
+information about kscd.
+
+Thank you,
+Bernd Wuebben
+wuebben@kde.org
diff --git a/kscd/TODO b/kscd/TODO
new file mode 100644
index 00000000..e10c01c8
--- /dev/null
+++ b/kscd/TODO
@@ -0,0 +1,9 @@
+* fix html encoding
+* Session Management
+* change the GUI
+
+AJS:
+o fix eject button
+o don't show "no disc" error on startup
+o direct digital playback
+o better GUI layout for LCD panel
diff --git a/kscd/audiocd_play.desktop b/kscd/audiocd_play.desktop
new file mode 100644
index 00000000..4200b488
--- /dev/null
+++ b/kscd/audiocd_play.desktop
@@ -0,0 +1,55 @@
+[Desktop Entry]
+ServiceTypes=media/audiocd
+Actions=Play;
+X-KDE-Priority=TopLevel
+
+[Desktop Action Play]
+Name=Play
+Name[bg]=Старт
+Name[bn]=চালাও
+Name[br]=Seniñ
+Name[bs]=Sviraj
+Name[ca]=Reproducció
+Name[cs]=Přehrát
+Name[cy]=Chwarae
+Name[da]=Spil
+Name[de]=Abspielen
+Name[el]=Αναπαραγωγή
+Name[eo]=Ludi
+Name[et]=Esitus
+Name[fa]=پخش
+Name[fi]=Soita
+Name[fr]=Lecture
+Name[ga]=Seinn
+Name[gl]=Reproducir
+Name[he]=נגן
+Name[hu]=Lejátszás
+Name[is]=Spila
+Name[it]=Riproduci
+Name[kk]=Ойнату
+Name[km]=ចាក់
+Name[ko]=재생
+Name[lt]=Groti
+Name[mk]=Пушти
+Name[nb]=Spill
+Name[nds]=Afspelen
+Name[ne]=प्ले
+Name[nl]=Afspelen
+Name[nn]=Spel
+Name[pa]=ਵਜਾਓ
+Name[pl]=Odtwarzaj
+Name[pt]=Tocar
+Name[pt_BR]=DjPlay
+Name[ru]=Воспроизведение
+Name[sl]=Predvajaj
+Name[sv]=Spela
+Name[ta]=தொடங்கு
+Name[th]=เล่น
+Name[tr]=Çal
+Name[uk]=Пуск
+Name[wa]=Djouwer
+Name[zh_CN]=播放
+Name[zh_HK]=播放
+Name[zh_TW]=播放
+Exec=kscd -s -caption "%c" %i %m %u
+Icon=kscd
diff --git a/kscd/bitmaps/AUTHORS b/kscd/bitmaps/AUTHORS
new file mode 100644
index 00000000..3db9dacb
--- /dev/null
+++ b/kscd/bitmaps/AUTHORS
@@ -0,0 +1,37 @@
+The following copyright notice applies to the following files found in
+this directory:
+ eject.xbm
+ ff.xbm
+ lock.xbm
+ logo.xbm
+ nexttrk.xbm
+ options.xbm
+ playpaus.xbm
+ poweroff.xbm
+ prevtrk.xbm
+ repeat.xbm
+ rew.xbm
+ shuffle.xbm
+ stop.xbm
+
+/*
+ * xmcd - Motif(tm) CD Audio Player
+ *
+ * Copyright (C) 1993-1996 Ti Kan
+ * E-mail: ti@amb.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
diff --git a/kscd/bitmaps/CompactDisc.xpm b/kscd/bitmaps/CompactDisc.xpm
new file mode 100644
index 00000000..cf7e4c00
--- /dev/null
+++ b/kscd/bitmaps/CompactDisc.xpm
@@ -0,0 +1,68 @@
+/* XPM */
+static char * image_name[] = {
+"56 46 19 1",
+" c #F7F7F3F3F7F7",
+". c #B6B6B2B2B6B6",
+"X c #000000000000",
+"o c #A6A6A2A2A6A6",
+"O c #969692929696",
+"+ c #61618282B6B6",
+"@ c #EFEFAEAEA6A6",
+"# c #B6B6B6B6B6B6",
+"$ c #F7F7F7F7F7F7",
+"% c #9E9E96969E9E",
+"& c #FFFFFBFBFFFF",
+"* c #696971718E8E",
+"= c #B6B6AEAEB6B6",
+"- c #AEAEAAAA8E8E",
+"; c #000000001010",
+": c #080804040808",
+"> c #A6A69E9EA6A6",
+", c #B6B6BABAC7C7",
+"< c #96969A9AAEAE",
+" .",
+" ......................................................X",
+" ......................................................X",
+" ......................................................X",
+" o...o...o...o...o...o...o...o...o...o...o...o...o...o.X",
+" ......................................................X",
+" o.o.o.o.o.o.o.o...........................o.o.o.o.o.o.X",
+" .....o.......o...OXO.o+X+OXOXOX++X+OXO+o.....o.......oX",
+" o.o.o.o.o.o.o.o..X X.oOOOOOOOOOOOOOOOOOo..o.o.o.o.o.o.X",
+" ...o...o...o.....X X.......................o...o...o..X",
+" o.o.o.o.o.o..OXXXX Xo+X+oOXXXXXOoOXXXXXO..o.o.o.o.o.o.X",
+" .o...o.o.o...X. X.X X.X. X.X. .X.o...o.o.o...oX",
+" o.o.o.o.o.o..X +X+ XoX XoX XXXXOoX XXX X..o.o.o.o.o.o.X",
+" .o.o.o.o.o.o.X X.X X.X X.X X+++..X X.XXX.o.o.o.o.o.o.oX",
+" o.o.o.o.o.o..X X.X XoX XoX. .XoX X......o.o.o.o.o.o.X",
+" oo.ooo.ooo.o.X X.X XoX Xo.++XX XoX X.XXX.o.ooo.ooo.oooX",
+" o.o.o.o.o.o..X +X+ XoX XoOXXXX XoX XXX X..o.o.o.o.o.o.X",
+" oooooooooooo.X. XoX XoX .XoX. .X.oooooooooooooX",
+" o.o.o.o.o.o..OXXXXXOo+X+oOXXXXXOoOXXXXXO..o.o.o.o.o.o.X",
+" ooooooooooooo...........................ooooooooooooooX",
+" ooo.ooo.ooooooooooooooooooooooooooooooooooo.ooo.ooo.ooX",
+" ooooooooooooooooooooooooooooooooooooooooooooooooooooooX",
+" ooooooooooooooooo...@.@#@.@@$@$@$@..oo.ooo.ooo.oooooooX",
+" ooooooooooooooo#@@. O$O + %@@$@ #$@$@.o@oo.oo@.oooooooX",
+" OoooOoooOoooO.@$$$ + + + + @&@$# + # . .@. .ooOoooOoX",
+" oooooooooooo@ . + + %@@$# + O # .$@ .oooooooooX",
+" ooOoOoOoooO@$@$@$ . + @$.$*$O . . @$ @#..ooOoOoX",
+" oooooooooo@$@$@$@$@ @ .$O@# +$# .$@$@$@ #oooooooooX",
+" OoOoOoOoO.$@$@$@$@$@$@$ $$$@$# @$@$$$$$$ oooOoOoOoX",
+" ooooooooo@$#$#$#$#$#$# .XXX#$@$@ $ #@ooooooooX",
+" OoOoOoOoO O$O$O$O$=$.$ .+ooo+# @$@$@$@$@$@$@#o#oOoOoOoX",
+" oOoooOooO@ +$*$=$. .++*# #@#@#$#$.$.$#$+OoooOoooOX",
+" OoOoOoOoO@$ +$# . - @$ $$$@$#@##=....@#$=+OOoOoOoOoX",
+" oOoOoOoOOO . +$*$*$+#@$o$@$#@#.....=$.++OoOoOoOoOX",
+" OoOoOoOoOo+ . + + +$+$+..$o o$@$#@#...@$.*;oOOoOoOoOoX",
+" OOoOOOoOOOo+ . + + +$+.. O o o$@$#@#@$$O+:oOOoOOOoOOOX",
+" OoOoOoOoOoOo+. . + +## O o o o$@$$$$=+X+oOOoOoOoOoOoX",
+" OOOOOOOOOOOOoO+.. @$@ @ . . o o$>$@,*XX+OoOOOOOOOOOOOOX",
+" OoOoOoOoOoOoOooO+++..o.o.o.o.o#>#+XX+<>ooOOoOoOoOoOoOoX",
+" OOOOOOOOOOOOOOOoooO++X+XXXXXXXX+X+OoooOOOOOOOOOOOOOOOOX",
+" OOOoOOOoOOOoOOOoOOoooooooooooooooooOOOOoOOOoOOOoOOOoOOX",
+" OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOX",
+" OoOOOOOOOoOOOOOOOoOOOOOOOoOOOOOOOoOOOOOOOoOOOOOOOoOOOOX",
+" OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOX",
+" OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOX",
+".XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"};
diff --git a/kscd/bitmaps/Image3.gif b/kscd/bitmaps/Image3.gif
new file mode 100644
index 00000000..a2a72c4c
--- /dev/null
+++ b/kscd/bitmaps/Image3.gif
Binary files differ
diff --git a/kscd/bitmaps/Image3.tif b/kscd/bitmaps/Image3.tif
new file mode 100644
index 00000000..4fd8fcb9
--- /dev/null
+++ b/kscd/bitmaps/Image3.tif
Binary files differ
diff --git a/kscd/bitmaps/Image3.xpm b/kscd/bitmaps/Image3.xpm
new file mode 100644
index 00000000..488cb3b0
--- /dev/null
+++ b/kscd/bitmaps/Image3.xpm
Binary files differ
diff --git a/kscd/bitmaps/Image4.gif b/kscd/bitmaps/Image4.gif
new file mode 100644
index 00000000..7fcf837f
--- /dev/null
+++ b/kscd/bitmaps/Image4.gif
Binary files differ
diff --git a/kscd/bitmaps/Image4.xpm b/kscd/bitmaps/Image4.xpm
new file mode 100644
index 00000000..611290ec
--- /dev/null
+++ b/kscd/bitmaps/Image4.xpm
@@ -0,0 +1,31 @@
+/* XPM */
+static char * Image4_xpm[] = {
+"20 20 8 1",
+" c #C71BC30BC71B",
+". c #000000000000",
+"X c #79E779E779E7",
+"o c #FFFFFFFF0000",
+"O c #BEFBBEFBBEFB",
+"+ c #0000FFFFFFFF",
+"@ c #0000FFFF0000",
+"# c #FFFFFFFFFFFF",
+" .. ",
+" ... ",
+" XXXXXX .... ",
+" XXooOOOOXX.O.. ",
+" X+oooOOOOOO.OO. ",
+" X@++ooOOOOO..XO. ",
+" X@@+ooOOOO...XO. ",
+" XOO@@+oOOO#...O.. ",
+" XOOO@+XXXXOOOO... ",
+" XOOOO@X##XOOOO... ",
+" XOOOO@X##XOOOO X ",
+" XOOOO#XXXX+OOOOX ",
+" XOOO#OO#@@o+OOOX ",
+" XO#OOOO@@o++OX ",
+" X#OOOOO@@oo++X ",
+" XOOOOO@@@ooX ",
+" XXOOO@@@XX ",
+" XXXXXX ",
+" ",
+" "};
diff --git a/kscd/bitmaps/Makefile.am b/kscd/bitmaps/Makefile.am
new file mode 100644
index 00000000..4785fec0
--- /dev/null
+++ b/kscd/bitmaps/Makefile.am
@@ -0,0 +1,2 @@
+EXTRA_DIST = *.xpm *.gif *.xbm
+
diff --git a/kscd/bitmaps/cd3d.xpm b/kscd/bitmaps/cd3d.xpm
new file mode 100644
index 00000000..627e76a0
--- /dev/null
+++ b/kscd/bitmaps/cd3d.xpm
@@ -0,0 +1,62 @@
+/* XPM */
+static char * image_name[] = {
+"56 46 13 1",
+" c #F7F7F7F7F7F7",
+". c #B6B6B2B2B6B6",
+"X c #B6B6B6B6B6B6",
+"o c #000000001010",
+"O c #A6A6A2A2A6A6",
+"+ c #969692929696",
+"@ c #3838FBFB3030",
+"# c #717182829696",
+"$ c #FFFFFFFF0000",
+"% c #B6B620202020",
+"& c #51515151FFFF",
+"* c #FFFF0000FFFF",
+"= c #FFFF14144141",
+" .",
+" .X..X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.o",
+" ......................................................o",
+" .X..X.................................................o",
+" O...O.X.OX.XO.X.OX.XO.X.OX.XO.X.OX.XO.X.OX.XO.X.OX.XO.o",
+" X..X..................................................o",
+" O.+.O.+XO.+.OX+.O.+.OX+.O.+.OX+.O.+.OX+.O.+.OX+.O.O.OXo",
+" .X...O....X..O.X..X..O.X..X..O.X..X..O.X..X..O.X..X..Oo",
+" O.O.OXO.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.o",
+" ...O...OX..O...O...OX..O...OX..O...OX..O...OX..O...O..o",
+" OXO.O.O.O.OXO.OXO.O.O.O.OXO.O.O.OXO.O.O.OXO.O.O.OXO.OXo",
+" .O..XO.O.O...O.O.OX..O.O@#@####O.O...OXO.O...OXO.O...Oo",
+" O.O.O.O.O.O.OXO.O.O.O$#@#@########O.O.O.O.O.O.O.O.OXO.o",
+" .OXO.OXO.OXO.O.O.O.%&#$@@###########.O.O.OXO.O.O.O.O.Oo",
+" O.O.O.O.O.O.O.O.O.#*%&@$@@##O.#######.O.O.O.O.OXO.O.O.o",
+" OO.OOO.OOO.OOO.OO@@#*%&$@##OO##OO#####XOOO.OOO.OOO.OOOo",
+" OXO.O.OXO.OXO.OX&$$@#*%$@@#.O##.#.#####.O.OXO.O.O.OXO.o",
+" OOOOOOOOOOOOOOOO%%&$$#**@@##OOOO#OO####OOOOOOOOOOOOOOOo",
+" O.O.O.O.O.O.O.O*=*%%&$@$=@#####.#X######O.O.O.O.O.O.O.o",
+" OOOOOOOOOOOOOOO#*#=*%&$$&@OOO##OO#######OOOOOOOOOOOOOOo",
+" OOOXOOO.OOO.OO######=*%&$O O###########OO.OOOXOOO.OOo",
+" OOOOOOOOOOOOOO@@@@@@@@@@O O##########OOOOOOOOOOOOOo",
+" OOOOOOOOOOOOOO#########O #XX O#########OOOOOOOOOOOOOo",
+" OOOOOOOOOOOOOO#########O #. +X O#########OOOOOOOOOOOOOo",
+" +OOO+OOO+OOO+O#########O #X. O#########OOO+OOO+OOO+Oo",
+" OOOOOOOOOOOOOO##########O O**########OOOOOOOOOOOOOo",
+" OO+O+O+OOO+O+O####+######O O@&%***#####O+O+O+OOO+O+Oo",
+" OOOOOOOOOOOOOOO##OO#######OOO#$@@&%%**##OOOOOOOOOOOOOOo",
+" +O+O+O+O+O+O+O+####O##########=$@@$&%%**+O+O+O+O+O+O+Oo",
+" OOOOOOOOOOOOOOOO##O#O##########$$@@$&&&OOOOOOOOOOOOOOOo",
+" +O+O+O+O+O+O+O+O####+##########$=$@@@$&O+O+O+O+O+O+O+Oo",
+" O+OOO+OOO+OOO+OOO####+O#########$#$@$@OOO+OOO+OOO+OOO+o",
+" +O+O+O+O+O+O+O+O+O####+#+#######$=#$$O+O+O.O+O+O+O+O+Oo",
+" O+O+O+O+O+O+O+O+O+O####+#########$=#O+O+..O+O+O+O+O+O+o",
+" +O+O+O+O+O+O+O+O+O+O+############$+O+#...++O+O+O+O+O+Oo",
+" ++O+++O+++O+++O+++O+++O+#######+++##...X#+O+++O+++O+++o",
+" +O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+#........+O+O+O+O+O+O+Oo",
+" ++++++++++++++++++++++++++++++++++OOOO++++++++++++++++o",
+" +O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+Oo",
+" ++++++++++++++++++++++++++++++++++++++++++++++++++++++o",
+" +++O+++O+++O+++O+++O+++O+++O+++O+++O+++O+++O+++O+++O++o",
+" ++++++++++++++++++++++++++++++++++++++++++++++++++++++o",
+" +O+++++++O+++++++O+++++++O+++++++O+++++++O+++++++O++++o",
+" ++++++++++++++++++++++++++++++++++++++++++++++++++++++o",
+" ++++++++++++++++++++++++++++++++++++++++++++++++++++++o",
+"Xooooooooooooooooooooooooooooooooooooooooooooooooooooooo"};
diff --git a/kscd/bitmaps/cdsmallpause.xpm b/kscd/bitmaps/cdsmallpause.xpm
new file mode 100644
index 00000000..7ade7d0b
--- /dev/null
+++ b/kscd/bitmaps/cdsmallpause.xpm
@@ -0,0 +1,34 @@
+/* XPM */
+static char * cdsmallpause_xpm[] = {
+"15 24 7 1",
+" c none",
+". c #FFFFFFFF0000",
+"X c #C71BC30BC71B",
+"o c #0000FFFFFFFF",
+"O c #0000FFFF0000",
+"+ c #FFFFFFFFFFFF",
+"@ c #000000000000",
+" ",
+" ..XXX ",
+" o...XXXXX ",
+" Ooo..XXXX+X ",
+" OOo..XXX+XX ",
+" XXOOo.XX+XXXX ",
+" XXXOo @ XXXXX ",
+" XXXXO@ @XXXXX ",
+" XXXX+ @ oXXXX ",
+" XXX+XX+O.oXXX ",
+" X+XXXXO.ooX ",
+" +XXXXXO..oo ",
+" XXXXXOO.. ",
+" XXXOO ",
+" ",
+" ",
+" @@ @@ ",
+" @@ @@ ",
+" @@ @@ ",
+" @@ @@ ",
+" @@ @@ ",
+" @@ @@ ",
+" @@ @@ ",
+" "};
diff --git a/kscd/bitmaps/cdsmallplay.xpm b/kscd/bitmaps/cdsmallplay.xpm
new file mode 100644
index 00000000..1622ebce
--- /dev/null
+++ b/kscd/bitmaps/cdsmallplay.xpm
@@ -0,0 +1,33 @@
+/* XPM */
+static char * cdsmallplay_xpm[] = {
+"15 24 6 1",
+" c #FFFFFFFF0000",
+". c #C71BC30BC71B",
+"X c #0000FFFFFFFF",
+"o c #0000FFFF0000",
+"O c #FFFFFFFFFFFF",
+"+ c #000000000000",
+" ",
+" ... ",
+" X ..... ",
+" oXX ....O. ",
+" ooX ...O.. ",
+" ..ooX ..O.... ",
+" ...oX + ..... ",
+" ....o+ +..... ",
+" ....O + X.... ",
+" ...O..Oo X... ",
+" .O....o XX. ",
+" O.....o XX ",
+" .....oo ",
+" ...oo ",
+" ",
+" ",
+" ++ ",
+" ++++ ",
+" ++++++ ",
+" ++++++++ ",
+" ++++++ ",
+" ++++ ",
+" ++ ",
+" "};
diff --git a/kscd/bitmaps/cdsmallstop.xpm b/kscd/bitmaps/cdsmallstop.xpm
new file mode 100644
index 00000000..9bbc477a
--- /dev/null
+++ b/kscd/bitmaps/cdsmallstop.xpm
@@ -0,0 +1,34 @@
+/* XPM */
+static char * cdsmallstop_xpm[] = {
+"15 24 7 1",
+" c none",
+". c #FFFFFFFF0000",
+"X c #C71BC30BC71B",
+"o c #0000FFFFFFFF",
+"O c #0000FFFF0000",
+"+ c #FFFFFFFFFFFF",
+"@ c #000000000000",
+" ",
+" ..XXX ",
+" o...XXXXX ",
+" Ooo..XXXX+X ",
+" OOo..XXX+XX ",
+" XXOOo.XX+XXXX ",
+" XXXOo @ XXXXX ",
+" XXXXO@ @XXXXX ",
+" XXXX+ @ oXXXX ",
+" XXX+XX+O.oXXX ",
+" X+XXXXO.ooX ",
+" +XXXXXO..oo ",
+" XXXXXOO.. ",
+" XXXOO ",
+" ",
+" ",
+" @@@@@@@ ",
+" @@@@@@@ ",
+" @@@@@@@ ",
+" @@@@@@@ ",
+" @@@@@@@ ",
+" @@@@@@@ ",
+" @@@@@@@ ",
+" "};
diff --git a/kscd/bitmaps/db.xbm b/kscd/bitmaps/db.xbm
new file mode 100644
index 00000000..57f8514a
--- /dev/null
+++ b/kscd/bitmaps/db.xbm
@@ -0,0 +1,8 @@
+#define db_width 20
+#define db_height 16
+static unsigned char db_bits[] = {
+ 0xff, 0xff, 0x0f, 0x01, 0x00, 0x08, 0xfd, 0xff, 0x0b, 0x01, 0x00, 0x08,
+ 0x01, 0x0f, 0x08, 0x01, 0x09, 0x08, 0x01, 0x0f, 0x08, 0x01, 0x00, 0x08,
+ 0x01, 0x00, 0x08, 0x81, 0x1f, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08,
+ 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0xff, 0xff, 0x0f,
+ };
diff --git a/kscd/bitmaps/db.xpm b/kscd/bitmaps/db.xpm
new file mode 100644
index 00000000..56f82078
--- /dev/null
+++ b/kscd/bitmaps/db.xpm
Binary files differ
diff --git a/kscd/bitmaps/eject.xbm b/kscd/bitmaps/eject.xbm
new file mode 100644
index 00000000..709fc709
--- /dev/null
+++ b/kscd/bitmaps/eject.xbm
@@ -0,0 +1,7 @@
+#define eject_width 16
+#define eject_height 16
+static unsigned char eject_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07,
+ 0xf0, 0x0f, 0xf8, 0x1f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f,
+ 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/ff.xbm b/kscd/bitmaps/ff.xbm
new file mode 100644
index 00000000..76fdcaa4
--- /dev/null
+++ b/kscd/bitmaps/ff.xbm
@@ -0,0 +1,7 @@
+#define ff_width 16
+#define ff_height 16
+static unsigned char ff_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x18, 0x06, 0x38, 0x0e,
+ 0x78, 0x1e, 0xf8, 0x3e, 0xf8, 0x3e, 0x78, 0x1e, 0x38, 0x0e, 0x18, 0x06,
+ 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/info.xbm b/kscd/bitmaps/info.xbm
new file mode 100644
index 00000000..7359938e
--- /dev/null
+++ b/kscd/bitmaps/info.xbm
@@ -0,0 +1,6 @@
+#define info_width 16
+#define info_height 16
+static unsigned char info_bits[] = {
+ 0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0xe0, 0x01, 0xe0, 0x01, 0x80, 0x01,
+ 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
+ 0xe0, 0x07, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, };
diff --git a/kscd/bitmaps/kscdlogo.xpm b/kscd/bitmaps/kscdlogo.xpm
new file mode 100644
index 00000000..559994e5
--- /dev/null
+++ b/kscd/bitmaps/kscdlogo.xpm
@@ -0,0 +1,137 @@
+/* XPM */
+static char *kscdlogo[] = {
+/* width height num_colors chars_per_pixel */
+" 90 120 10 1",
+/* colors */
+". c #000000",
+"# c #1d1d1d",
+"a c #393939",
+"b c #555555",
+"c c #727272",
+"d c #8e8e8e",
+"e c #aaaaaa",
+"f c #c7c7c7",
+"g c #e3e3e3",
+"h c #ffffff",
+/* pixels */
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhbbbbbdhhhhcbbbbbhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhdbbbbfhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhhhf.....dhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhhha....#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhhd.....ehhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhh#....ahhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhd.....fhhhhhhhhheeeghhhhhebbbehhhhhhhhhhgdbbdb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bg#....bhhhhhhhhc....ehhhe#.....#ehhhhhhhc....bb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bc.....ghhhhhhhb.....ehhe.........bhhhhg#.....bb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....a.....chhhhhhhf......ehe...........ehhhb......bb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....#.....ehhhhhhhb......eh#...........#hhf.......bb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....ba....ahhhhhhh#......ee.....beb.....ehb.....#bbb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bd.....fhhhhhh.....#dgb....bhhheeeeghh.....ahhgb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bh.....ahhhhhh.....ehhb....ehhhhhhhhhh.....ehhhb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhc.....fhhhhd.....fhhb....bhhha....ch.....chhh#....hhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhf.....ahhda......hhhe.....#b#.....ehb.....cea....#hhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhha.....fhb......ahhhg............#hhf............chhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhhe.....ahb......fhhhhd...........dhhhb..........#ghhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhhh#.....fb.....bhhhhhhb.........bhhhhg#.........ehhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhhhc.....cb....chhhhhhhhd#.....#dhhhhhhhc......aghhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhheeeeefhhhgeeeeeedbbdghhhhhhhhhhhebbcehhhhhhhhhhgebbcfhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"
+};
diff --git a/kscd/bitmaps/lock.xbm b/kscd/bitmaps/lock.xbm
new file mode 100644
index 00000000..0f2e6e80
--- /dev/null
+++ b/kscd/bitmaps/lock.xbm
@@ -0,0 +1,7 @@
+#define lock_width 16
+#define lock_height 16
+static unsigned char lock_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x20, 0x04, 0x20, 0x04,
+ 0x20, 0x04, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/logo.xbm b/kscd/bitmaps/logo.xbm
new file mode 100644
index 00000000..16c293bb
--- /dev/null
+++ b/kscd/bitmaps/logo.xbm
@@ -0,0 +1,28 @@
+#define logo_width 48
+#define logo_height 48
+static unsigned char logo_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x63, 0xa2, 0x23, 0xe6, 0x00, 0x9f, 0x94, 0xb6, 0x54, 0x49,
+ 0x00, 0x91, 0x90, 0xaa, 0x54, 0x41, 0x00, 0x91, 0x94, 0xaa, 0x73, 0x49,
+ 0x00, 0x11, 0x63, 0xa2, 0x50, 0x46, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00,
+ 0xfc, 0x91, 0x0f, 0xff, 0xc3, 0x3f, 0x02, 0x90, 0x88, 0x00, 0x22, 0x40,
+ 0x01, 0x90, 0x48, 0x00, 0x12, 0x80, 0xe1, 0x90, 0x48, 0x00, 0x12, 0x86,
+ 0x11, 0x91, 0x48, 0xfc, 0x13, 0x89, 0x11, 0x91, 0x48, 0x02, 0x10, 0x89,
+ 0x11, 0x91, 0x48, 0xfc, 0x10, 0x89, 0x11, 0x91, 0x48, 0x00, 0x11, 0xf9,
+ 0x11, 0x91, 0x48, 0x00, 0x12, 0x01, 0x11, 0x91, 0x88, 0x00, 0x12, 0xf9,
+ 0x11, 0x91, 0x08, 0x3f, 0x12, 0x89, 0x11, 0x91, 0x08, 0x40, 0x12, 0x89,
+ 0x11, 0x91, 0xc8, 0x3f, 0x12, 0x89, 0xe1, 0x90, 0x48, 0x00, 0x12, 0x86,
+ 0x01, 0x90, 0x48, 0x00, 0x12, 0x80, 0x02, 0x90, 0x48, 0x00, 0x21, 0x40,
+ 0xfc, 0x9f, 0xcf, 0xff, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x27, 0xd7, 0x49, 0x20, 0xe9, 0x64, 0xa9, 0x90, 0x54, 0x50, 0x29, 0x95,
+ 0xa9, 0x96, 0x54, 0x50, 0x29, 0x95, 0xa9, 0x94, 0x5c, 0x70, 0x29, 0x95,
+ 0x27, 0x97, 0xd4, 0x51, 0xe6, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/magic.xbm b/kscd/bitmaps/magic.xbm
new file mode 100644
index 00000000..9b19811e
--- /dev/null
+++ b/kscd/bitmaps/magic.xbm
@@ -0,0 +1,7 @@
+/* Created with The GIMP */
+#define magicxbm_width 16
+#define magicxbm_height 16
+static unsigned char magicxbm_bits[] = {
+ 0x00, 0x10, 0x00, 0x38, 0x80, 0xfe, 0x80, 0x38, 0x80, 0x10, 0xc0, 0x11,
+ 0xc0, 0x01, 0xf0, 0x07, 0xfe, 0x3f, 0xf0, 0x07, 0xc0, 0x01, 0xc0, 0x01,
+ 0x84, 0x00, 0x8e, 0x00, 0x84, 0x00, 0x00, 0x00 };
diff --git a/kscd/bitmaps/nexttrk.xbm b/kscd/bitmaps/nexttrk.xbm
new file mode 100644
index 00000000..6e36d666
--- /dev/null
+++ b/kscd/bitmaps/nexttrk.xbm
@@ -0,0 +1,7 @@
+#define nexttrk_width 16
+#define nexttrk_height 16
+static unsigned char nexttrk_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x86, 0x61, 0x8e, 0x63,
+ 0x9e, 0x67, 0xbe, 0x6f, 0xbe, 0x6f, 0x9e, 0x67, 0x8e, 0x63, 0x86, 0x61,
+ 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/options.xbm b/kscd/bitmaps/options.xbm
new file mode 100644
index 00000000..760ac205
--- /dev/null
+++ b/kscd/bitmaps/options.xbm
@@ -0,0 +1,7 @@
+#define options_width 16
+#define options_height 16
+static unsigned char options_bits[] = {
+ 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x04, 0x00, 0x8e, 0x7f, 0xfe, 0x7f,
+ 0x8e, 0x7f, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x7d, 0xfc, 0x7f,
+ 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/playpaus.xbm b/kscd/bitmaps/playpaus.xbm
new file mode 100644
index 00000000..a2494705
--- /dev/null
+++ b/kscd/bitmaps/playpaus.xbm
@@ -0,0 +1,10 @@
+#define playpause_width 32
+#define playpause_height 16
+static unsigned char playpause_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0xc0, 0x71, 0x1e, 0x00, 0xc0, 0x71, 0x7e, 0x00, 0xc0, 0x71,
+ 0xfe, 0x01, 0xc0, 0x71, 0xfe, 0x07, 0xc0, 0x71, 0xfe, 0x07, 0xc0, 0x71,
+ 0xfe, 0x01, 0xc0, 0x71, 0x7e, 0x00, 0xc0, 0x71, 0x1e, 0x00, 0xc0, 0x71,
+ 0x06, 0x00, 0xc0, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/poweroff.xbm b/kscd/bitmaps/poweroff.xbm
new file mode 100644
index 00000000..f332ef22
--- /dev/null
+++ b/kscd/bitmaps/poweroff.xbm
@@ -0,0 +1,7 @@
+#define poweroff_width 16
+#define poweroff_height 16
+static unsigned char poweroff_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x18, 0xfe, 0x10, 0xfe, 0x10, 0xfe, 0x10,
+ 0xfe, 0x10, 0x82, 0x00, 0x82, 0x38, 0x82, 0x44, 0x82, 0x44, 0x82, 0x44,
+ 0xfe, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/prevtrk.xbm b/kscd/bitmaps/prevtrk.xbm
new file mode 100644
index 00000000..1cf3b803
--- /dev/null
+++ b/kscd/bitmaps/prevtrk.xbm
@@ -0,0 +1,7 @@
+#define prevtrk_width 16
+#define prevtrk_height 16
+static unsigned char prevtrk_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x86, 0x61, 0xc6, 0x71,
+ 0xe6, 0x79, 0xf6, 0x7d, 0xf6, 0x7d, 0xe6, 0x79, 0xc6, 0x71, 0x86, 0x61,
+ 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/repeat.xbm b/kscd/bitmaps/repeat.xbm
new file mode 100644
index 00000000..3e6272cf
--- /dev/null
+++ b/kscd/bitmaps/repeat.xbm
@@ -0,0 +1,7 @@
+#define repeat_width 16
+#define repeat_height 16
+static unsigned char repeat_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x13,
+ 0x0c, 0x31, 0x04, 0x20, 0x04, 0x20, 0x8c, 0x30, 0xc8, 0x1f, 0x80, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/rew.xbm b/kscd/bitmaps/rew.xbm
new file mode 100644
index 00000000..79aa0db3
--- /dev/null
+++ b/kscd/bitmaps/rew.xbm
@@ -0,0 +1,7 @@
+#define rew_width 16
+#define rew_height 16
+static unsigned char rew_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x60, 0x18, 0x70, 0x1c,
+ 0x78, 0x1e, 0x7c, 0x1f, 0x7c, 0x1f, 0x78, 0x1e, 0x70, 0x1c, 0x60, 0x18,
+ 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/shuffle.xbm b/kscd/bitmaps/shuffle.xbm
new file mode 100644
index 00000000..7214cd0a
--- /dev/null
+++ b/kscd/bitmaps/shuffle.xbm
@@ -0,0 +1,7 @@
+#define shuffle_width 16
+#define shuffle_height 16
+static unsigned char shuffle_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xcc, 0x00,
+ 0x84, 0x0c, 0xc0, 0x3c, 0x60, 0x3c, 0x30, 0x0c, 0x00, 0x00, 0x30, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/stop.xbm b/kscd/bitmaps/stop.xbm
new file mode 100644
index 00000000..1f176d13
--- /dev/null
+++ b/kscd/bitmaps/stop.xbm
@@ -0,0 +1,7 @@
+#define stop_width 16
+#define stop_height 16
+static unsigned char stop_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0xf0, 0x0f,
+ 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bwlednum.cpp b/kscd/bwlednum.cpp
new file mode 100644
index 00000000..a8f828cc
--- /dev/null
+++ b/kscd/bwlednum.cpp
@@ -0,0 +1,591 @@
+/*
+ * BW_LED_Number a very very primitive LED
+ *
+ * Copyright: Bernd Johannes Wuebben, wuebben@math.cornell.edu
+ *
+ * $Id$
+ *
+ */
+
+
+#include "bwlednum.h"
+#include "qbitarray.h"
+#include "qpainter.h"
+#include <stdio.h>
+
+#include "bwlednum.moc"
+
+#define NUM_OF_SEGMENTS 8
+#define STOP_CHAR 25
+
+
+static char segs[14][8] =
+{ { 0, 1, 2, 4, 5, 6,25, 0}, // 0
+ { 2, 5,25, 0, 0, 0, 0, 0}, // 1
+ { 1, 2, 3, 4, 6,25 ,0, 0}, // 2
+ { 1, 2, 3, 5, 6,25, 0, 0}, // 3
+ { 0, 3, 2, 5 ,25, 0, 0, 0}, // 4
+ { 0, 1, 3, 5, 6,25, 0, 0}, // 5
+ { 0, 1, 3, 4, 5, 6,25, 0}, // 6
+ { 1,2 , 5,25, 0, 0, 0, 0}, // 7
+ { 0, 1, 2, 3, 4, 5, 6,25}, // 8
+ { 0, 1, 2, 3, 5, 6,25, 0}, // 9
+ { 3,25, 0, 0, 0, 0, 0, 0}, // -
+ { 7,25, 0, 0, 0, 0, 0, 0}, // .
+ { 8, 9,25, 0, 0, 0, 0, 0}, // :
+ {25, 0, 0, 0, 0, 0, 0, 0} }; // blank
+
+
+BW_LED_Number::BW_LED_Number( QWidget *parent, const char *name )
+ : QFrame( parent, name ){
+
+
+ offcolor = QColor(100,0,0);
+ showOffColon(FALSE);
+ smallLED = false;
+ current_symbol = ' ';
+ old_segments = &segs[13][0]; // nothing
+ current_segments = &segs[13][0]; // nothing
+ setLEDColor(yellow,black);
+
+}
+
+void dump_segments(char * segs){
+
+ printf("dumping segments:");
+ for (int i = 0; i <=7; i++){
+ printf("%d:",segs[i]);
+ }
+ printf("\n");
+}
+
+
+BW_LED_Number::~BW_LED_Number(){
+
+}
+
+void BW_LED_Number::resizeEvent( QResizeEvent * ){
+
+}
+
+void BW_LED_Number::mouseReleaseEvent( QMouseEvent * /* e */ )
+{
+ emit(clicked());
+}
+
+void BW_LED_Number::showOffColon(bool off){
+
+ show_off_colon = off;
+
+}
+
+void BW_LED_Number::setLEDColor( const QColor& fgColor, const QColor& bgColor ){
+
+ fgcolor = fgColor;
+ bgcolor = bgColor;
+
+ QColorGroup old_cg = this->colorGroup();
+
+
+ QColorGroup new_cg( fgColor, bgColor,
+ fgColor, fgColor, fgColor,
+ fgColor, fgColor );
+
+
+ this->setPalette(QPalette(new_cg, new_cg, new_cg));
+
+}
+
+
+
+
+static char *getSegments( char s)
+
+{
+ if (s >= '0' && s <= '9'){
+ return segs[s - '0'];
+ }
+
+ int j;
+
+ switch ( s ) {
+ case '-':
+ j = 10;
+ break;
+ case 'O':
+ j = 0;
+ break;
+ case '.':
+ j = 11;
+ break;
+ case ':':
+ j = 12;
+ break;
+ default:
+ j = 13;
+ break;
+ }
+
+ return segs[j];
+}
+
+void BW_LED_Number::drawContents( QPainter *p ){
+
+ drawSymbol( p, current_symbol,TRUE );
+
+}
+
+
+void BW_LED_Number::display(int i ){
+
+ if( (i<0) || (i> 9))
+ return;
+ display( (char)('0'+ i));
+
+}
+
+
+void BW_LED_Number::display(char s){
+
+ QPainter p;
+
+ p.begin( this );
+
+ old_segments = current_segments;
+ current_symbol = s;
+ current_segments = getSegments(s);
+
+ drawSymbol(&p,s,FALSE);
+
+ p.end();
+
+}
+
+void BW_LED_Number::setSmallLED(bool a_boolean){
+
+ smallLED = a_boolean;
+
+}
+
+
+void BW_LED_Number::drawSymbol( QPainter *p,char ,bool repaint ){
+
+ // printf("drawSymbol repaint = %d\n",repaint);
+
+ QPoint pos;
+
+ int xSegment_Length, ySegment_Length, Segment_Length, xAdvance;
+ int Xoffset, Yoffset, space;
+
+ space = 1;
+
+ xSegment_Length = width()*5/((5 + space) + space) ;
+
+ ySegment_Length = height()*5/12;
+
+ Segment_Length = ySegment_Length > xSegment_Length ? xSegment_Length : ySegment_Length;
+
+ xAdvance = Segment_Length*( 5 + space )/5 +1 ;
+
+ // Xoffset = ( width() - xAdvance + Segment_Length/5 )/2; // origininal
+ Xoffset = ( width() - xAdvance + Segment_Length/4 )/2;
+ Yoffset = ( height() - Segment_Length*2 )/2;
+
+ pos = QPoint( Xoffset , Yoffset );
+
+
+ if(repaint){
+
+ // this draw the non-illumintated segments
+
+ if(show_off_colon){// we want to show the colon which is actually ugly and
+ // by default not shown.
+
+ for(int l = 0; l <= NUM_OF_SEGMENTS +1; l++){
+ drawSegment(pos,(char) l,*p,Segment_Length,TRUE); //erase segment
+ }
+ }
+ else{
+ for(int l = 0; l <= NUM_OF_SEGMENTS -1; l++){
+ drawSegment(pos,(char) l,*p,Segment_Length,TRUE); //erase segment
+ }
+ }
+
+ // now draw the illuminated segments
+
+ for(int l = 0; l <= NUM_OF_SEGMENTS -1; l++){
+ if(current_segments[l] != STOP_CHAR){
+ drawSegment(pos,current_segments[l],*p,Segment_Length,FALSE); // draw segment
+ }
+ else{
+ break;
+ }
+ }
+ }
+ else{ // we are not repainting ourselves due to a repaint event but rather
+ // genuinely changing the symbol that is to be displayed
+
+ for(int l = 0; l <= NUM_OF_SEGMENTS -1; l++){
+
+ if(current_segments[l] != STOP_CHAR){
+ if(!seg_contained_in(current_segments[l],old_segments))
+ drawSegment(pos,current_segments[l],*p,Segment_Length,FALSE); // draw segment
+ }
+ else{
+ break;
+ }
+ }
+
+
+ for(int k = 0; k <= NUM_OF_SEGMENTS -1; k++){
+
+ if(old_segments[k] != STOP_CHAR){
+ if(!seg_contained_in(old_segments[k],current_segments))
+ drawSegment(pos,old_segments[k],*p,Segment_Length,TRUE); //erase segment
+ }
+ else{
+ break;
+ }
+ }
+ }
+}
+
+
+
+bool BW_LED_Number::seg_contained_in( char c, char* seg){
+
+ bool result = FALSE;
+
+ while ( *seg != STOP_CHAR){
+ // printf("Comparing %d with %d\n",c,*seg);
+ if ( c == *seg )
+ result = TRUE;
+ seg++;
+ }
+
+ return result;
+}
+
+void BW_LED_Number::setLEDoffColor(QColor color){
+
+ offcolor = color;
+}
+
+
+void BW_LED_Number::drawSegment( const QPoint &pos, char seg_number, QPainter &p,
+ int Segment_Length, bool erase){
+
+
+ QPoint pt = pos;
+ QColorGroup g = colorGroup();
+ QColor lightColor,darkColor;
+ if ( erase ){
+
+ lightColor = offcolor;
+ darkColor = offcolor;
+
+ } else {
+ lightColor = g.light();
+ darkColor = g.dark();
+ }
+
+ // int Width = (int) Segment_Length/5 ; // original
+ int Width = (int) Segment_Length/4;
+
+
+ QBrush brush(g.light());
+ QPointArray pts;
+
+
+ pt.ry() += (QCOORD)Width/2;
+
+
+ if (erase){
+
+ p.setBrush(offcolor);
+ brush.setColor(offcolor);
+
+ }
+ else
+ p.setBrush(g.light());
+
+ if(!smallLED){
+
+ switch ( seg_number ) {
+ case 0 :
+
+
+ if (erase)
+ p.setPen(offcolor);
+
+ pts.setPoints(3,pt.x(), pt.y() ,
+ pt.x(), pt.y()-Width +1,
+ pt.x() + Width-1, pt.y());
+ p.drawPolygon(pts);
+ pts.setPoints(3,pt.x(), pt.y() + Segment_Length -Width - Width/2 -1 ,
+ pt.x() + Width -1 , pt.y() -Width +Segment_Length - Width/2 -1,
+ pt.x() , pt.y() + Segment_Length - 3*Width/4 -1);
+ p.drawPolygon(pts);
+
+ if (erase)
+ p.setPen(g.light());
+
+ p.fillRect(pt.x(),pt.y()+ Width/2 -1, Width ,
+ Segment_Length - Width -Width +1 ,brush);
+
+ break;
+ case 1 :
+
+ p.fillRect(pt.x()+Width,pt.y()- Width , Segment_Length -2* Width, Width ,brush);
+
+ if (erase)
+ p.setPen(offcolor);
+
+ pts.setPoints(3,pt.x()+1, pt.y()-Width ,
+ pt.x()+Width, pt.y()-Width ,
+ pt.x() + Width, pt.y() -1 );
+ p.drawPolygon(pts);
+
+ pts.setPoints(3,pt.x()+ Segment_Length - Width , pt.y() - Width,
+ pt.x()+ Segment_Length -1, pt.y() - Width,
+ pt.x() + Segment_Length - Width , pt.y() -1 );
+ p.drawPolygon(pts);
+
+ if (erase)
+ p.setPen(g.light());
+ break;
+ case 2 :
+ pt.rx() += (QCOORD)(Segment_Length);
+
+
+ if (erase)
+ p.setPen(offcolor);
+
+ pts.setPoints(3,pt.x() , pt.y() ,
+ pt.x() , pt.y() - Width + 1, // changes from 1 to 2
+ pt.x() - Width +1, pt.y() );
+
+ p.drawPolygon(pts);
+
+ pts.setPoints(3,pt.x() , pt.y() + Segment_Length - Width - Width/2 -1,
+ pt.x() , pt.y() + Segment_Length - 3*Width/4 - 1,
+ pt.x() - Width +1, pt.y() + Segment_Length - Width - Width/2 -1);
+
+ p.drawPolygon(pts);
+
+ if (erase)
+ p.setPen(g.light());
+
+ p.fillRect(pt.x() - Width+1 ,pt.y() + Width/2- 1, Width ,
+ Segment_Length - Width - Width + 1 ,brush);
+
+ break;
+ case 3 :
+
+ pt.ry() += (QCOORD)Segment_Length;
+
+ p.setPen(g.background());
+
+ pts.setPoints(3,pt.x()-1 , pt.y() - Width/2 -1,
+ pt.x() + Width+2, pt.y()-Width -1 ,//
+ pt.x() + Width+2, pt.y() );
+ p.drawPolygon(pts);
+ pts.setPoints(3,pt.x() + Segment_Length + 1, pt.y() - Width/2 -1 ,
+ pt.x() + Segment_Length - Width - 2 ,
+ pt.y() - Width -1,
+ pt.x() + Segment_Length - Width - 2, pt.y() );
+ p.drawPolygon(pts);
+
+ p.setPen(g.light());
+ p.fillRect(pt.x() + Width -1 ,pt.y() - Width, Segment_Length- 2* Width + 3,
+ Width ,brush);
+
+ break;
+ case 4 :
+ pt.ry() += (QCOORD)(Segment_Length +1);
+ p.fillRect(pt.x(), pt.y(), Width , Segment_Length - Width - Width/2 ,brush);
+ if (erase)
+ p.setPen(offcolor);
+
+ pts.setPoints(3,pt.x(), pt.y(),
+ pt.x(), pt.y()-Width+1,
+ pt.x() + Width-1, pt.y());
+ p.drawPolygon(pts);
+ pts.setPoints(3,pt.x(), pt.y() + Segment_Length -Width - Width/2 -1 ,
+ pt.x() + Width -1 , pt.y() -Width +Segment_Length - Width/2 -1 ,
+ pt.x() , pt.y() + Segment_Length - 3*Width/4 -1);
+ p.drawPolygon(pts);
+
+ if (erase)
+ p.setPen(g.light());
+
+ break;
+ case 5 :
+ pt.rx() += (QCOORD)(Segment_Length );
+ pt.ry() += (QCOORD)(Segment_Length +1);
+ p.fillRect(pt.x() - Width +1 ,pt.y(), Width ,
+ Segment_Length - Width - Width/2 ,brush);
+
+ if (erase)
+ p.setPen(offcolor);
+
+ pts.setPoints(3,pt.x() , pt.y(),
+ pt.x() , pt.y() - Width +1,
+ pt.x() - Width +1, pt.y());
+
+ p.drawPolygon(pts);
+
+ pts.setPoints(3,pt.x() , pt.y() + Segment_Length - Width - Width/2 -1,
+ pt.x() , pt.y() + Segment_Length - 3*Width/4 -1,
+ pt.x() - Width +1, pt.y() + Segment_Length - Width - Width/2 -1);
+
+ p.drawPolygon(pts);
+
+ if (erase)
+ p.setPen(g.light());
+
+ break;
+ case 6 :
+ pt.ry() += (QCOORD)(Segment_Length*2 );
+ p.fillRect(pt.x() + Width ,pt.y() -Width , Segment_Length -2* Width ,
+ Width ,brush);
+
+ if (erase)
+ p.setPen(offcolor);
+
+ pts.setPoints(3,pt.x()+1, pt.y()-1,
+ pt.x() + Width, pt.y() - Width,
+ pt.x() + Width, pt.y() - 1 );
+ p.drawPolygon(pts);
+
+ pts.setPoints(3, pt.x() + Segment_Length - 1, pt.y()-1,
+ pt.x() + Segment_Length - Width , pt.y() - Width,
+ pt.x() + Segment_Length - Width , pt.y() - 1 );
+
+ p.drawPolygon(pts);
+
+ if (erase)
+ p.setPen(g.light());
+
+
+
+ break;
+ case 7 :
+ pt.rx() += (QCOORD)(Segment_Length/2);
+ pt.ry() += (QCOORD)(Segment_Length*2);
+ p.fillRect(pt.x() ,pt.y() - Width , Width , Width ,brush);
+ break;
+ case 8 :
+ pt.ry() += (QCOORD)(Segment_Length/2 + Width/2);
+ pt.rx() += (QCOORD)(Segment_Length/2 - Width/2 + 1);
+
+ if (!show_off_colon && erase) {
+ p.setBrush(bgcolor);
+ brush.setColor(bgcolor);
+ }
+
+ p.fillRect(pt.x() ,pt.y() - Width , Width , Width ,brush);
+ p.moveTo(pt);
+
+ if (!show_off_colon && erase) {
+ p.setBrush(fgcolor);
+ brush.setColor(fgcolor);
+ }
+
+ break;
+ case 9 :
+ pt.ry() += (QCOORD)(3*Segment_Length/2 + Width/2);
+ pt.rx() += (QCOORD)(Segment_Length/2 - Width/2 + 1);
+
+ if (!show_off_colon && erase) {
+ p.setBrush(bgcolor);
+ brush.setColor(bgcolor);
+ }
+ p.fillRect(pt.x() ,pt.y() - Width , Width , Width ,brush);
+
+ if (!show_off_colon && erase) {
+ p.setBrush(fgcolor);
+ brush.setColor(fgcolor);
+ }
+ break;
+ }
+
+ } /* if (!smallLED) */
+
+ else{
+
+ pt.ry() += (QCOORD)Width/2;
+
+ switch ( seg_number ) {
+ case 0 :
+ p.fillRect(pt.x(),pt.y()+ Width /2, Width , Segment_Length - Width -Width/2 ,brush);
+ break;
+ case 1 :
+ p.fillRect(pt.x()+Width,pt.y()- Width , Segment_Length -2* Width, Width ,brush);
+ break;
+ case 2 :
+ pt.rx() += (QCOORD)(Segment_Length);
+ p.fillRect(pt.x()-Width,pt.y()+ Width/2, Width ,
+ Segment_Length - Width -Width/2 ,brush);
+ break;
+ case 3 :
+ pt.ry() += (QCOORD)Segment_Length;
+ p.fillRect(pt.x() + Width ,pt.y() - Width, Segment_Length- 2* Width, Width ,brush);
+ break;
+ case 4 :
+ pt.ry() += (QCOORD)(Segment_Length );
+ p.fillRect(pt.x(), pt.y(), Width , Segment_Length - Width - Width/2 ,brush);
+ break;
+ case 5 :
+ pt.rx() += (QCOORD)(Segment_Length );
+ pt.ry() += (QCOORD)(Segment_Length );
+ p.fillRect(pt.x() - Width ,pt.y(), Width ,
+ Segment_Length - Width - Width/2 ,brush);
+ break;
+ case 6 :
+ pt.ry() += (QCOORD)(Segment_Length*2);
+ p.fillRect(pt.x() + Width ,pt.y() -Width , Segment_Length -2* Width ,
+ Width ,brush);
+ break;
+ case 7 :
+ pt.rx() += (QCOORD)(Segment_Length/2);
+ pt.ry() += (QCOORD)(Segment_Length*2);
+ p.fillRect(pt.x() ,pt.y() - Width , Width , Width ,brush);
+ break;
+ case 8 :
+ pt.ry() += (QCOORD)(Segment_Length/2 + Width/2);
+ pt.rx() += (QCOORD)(Segment_Length/2 - Width/2 + 1);
+ if (!show_off_colon && erase) {
+ p.setBrush(bgcolor);
+ brush.setColor(bgcolor);
+ }
+
+ p.fillRect(pt.x() ,pt.y() - Width , Width , Width ,brush);
+ p.moveTo(pt);
+ if (!show_off_colon && erase) {
+ p.setBrush(fgcolor);
+ brush.setColor(fgcolor);
+ }
+
+ break;
+ case 9 :
+ pt.ry() += (QCOORD)(3*Segment_Length/2 + Width/2);
+ pt.rx() += (QCOORD)(Segment_Length/2 - Width/2 + 1);
+
+ if (!show_off_colon && erase) {
+ p.setBrush(bgcolor);
+ brush.setColor(bgcolor);
+ }
+ p.fillRect(pt.x() ,pt.y() - Width , Width , Width ,brush);
+ if (!show_off_colon && erase) {
+ p.setBrush(fgcolor);
+ brush.setColor(fgcolor);
+ }
+
+ break;
+ }
+
+ } /* end smallLED */
+
+
+}
diff --git a/kscd/bwlednum.h b/kscd/bwlednum.h
new file mode 100644
index 00000000..f3d66f8e
--- /dev/null
+++ b/kscd/bwlednum.h
@@ -0,0 +1,93 @@
+/*
+ *
+ * BW_LED_Number a very very primitive LED
+ *
+ * Copyright: Bernd Johannes Wuebben, wuebben@math.cornell.edu
+ *
+ *
+ * $Id$
+ *
+ */
+
+
+#ifndef BW_LED_NUM_H
+#define BW_LED_NUM_H
+
+#include "qframe.h"
+#include "qbitarray.h"
+
+
+class BW_LED_Number : public QFrame
+{
+ Q_OBJECT
+
+public:
+
+ BW_LED_Number( QWidget *parent=0, const char *name=0 );
+ ~BW_LED_Number();
+
+ void setSmallLED(bool ); // if you LED is small it might look better
+ // if you call setSmallLED(TRUE)
+
+ // this sets the fore and background color of the LED
+ // the forground defaults to yellow, the background defaults
+ // to black
+
+ void setLEDColor( const QColor& foregroundColor, const QColor& backgroundColor );
+
+
+ // this sets the color of the segments that are not iluminated
+ // the default is a rather dark red.
+
+ void setLEDoffColor(QColor color);
+
+ // calling showOffColon(TRUE) will show the colon if not illuminated
+ // this is rather ugly -- the default is that they are not shown.
+
+ void showOffColon(bool off);
+
+signals:
+ void clicked();
+
+public slots:
+
+ // display one of the characters " 0 1 2 3 4 5 6 7 8 9 . : - "
+ void display( char c );
+
+ // display on e of the numbers " 0 1 2 3 4 5 6 7 8 9"
+ void display( int i );
+
+protected:
+
+ void resizeEvent( QResizeEvent * );
+ void mouseReleaseEvent ( QMouseEvent * e );
+ void drawContents( QPainter * );
+
+private:
+
+ bool seg_contained_in( char c, char* seg);
+ void drawSegment( const QPoint &, char, QPainter &, int, bool = FALSE );
+ void drawSymbol( QPainter *p,char s ,bool repaint);
+
+ char* old_segments;
+ char* current_segments;
+
+ char current_symbol;
+ char old_symbol;
+ QColor offcolor;
+ QColor fgcolor;
+ QColor bgcolor;
+
+
+ bool smallLED;
+ bool show_off_colon;
+
+private: // Disabled copy constructor and operator=
+
+ BW_LED_Number( const BW_LED_Number & );
+ BW_LED_Number &operator=( const BW_LED_Number & );
+
+};
+
+
+#endif // BW_LED_NUM_H
diff --git a/kscd/cddaslave.c b/kscd/cddaslave.c
new file mode 100644
index 00000000..6b65c640
--- /dev/null
+++ b/kscd/cddaslave.c
@@ -0,0 +1,532 @@
+/*
+ * @(#)cddaslave.c 1.11 13 Sep 1995
+ *
+ * Digital audio manipulator for WorkMan.
+ *
+ * The CDDA architecture looks like this:
+ *
+ * WorkMan (or another UI!)
+ * ^^^
+ * ||| (separate processes connected by pipe)
+ * vvv
+ * +------------- cddaslave -------------+
+ * | | |
+ * command module CDDA reader audio output
+ * (portable) (per platform) (per platform)
+ *
+ * This source file has the command module and some of the scaffolding
+ * to hold cddaslave together, plus some non-system-dependent audio
+ * processing code. Look in plat_*_cdda.c for system-specific stuff.
+ */
+
+
+#include "libwm/include/wm_config.h"
+#include "libwm/include/wm_cdda.h"
+
+#ifdef BUILD_CDDA /* { */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#ifndef timerclear
+#define timerclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0)
+#endif
+
+int playing = 0; /* Should the CD be playing now? */
+
+/*
+ * Loudness setting, plus the floating volume multiplier and decaying-average
+ * volume level.
+ */
+int loudness = 0;
+unsigned int volume = 32768;
+unsigned int level;
+
+/*
+ * Playback speed (0 = slow)
+ */
+int speed = 128;
+
+/*
+ * This is non-null if we're saving audio to a file.
+ */
+FILE *output = NULL;
+
+/*
+ * Audio file header format.
+ */
+typedef unsigned long u_32;
+struct auheader {
+ u_32 magic;
+ u_32 hdr_size;
+ u_32 data_size;
+ u_32 encoding;
+ u_32 sample_rate;
+ u_32 channels;
+};
+
+#ifdef BIG_ENDIAN
+# ifndef htonl
+# define htonl(x) (x)
+# endif
+#else
+extern unsigned long htonl(x);
+#endif
+
+void *malloc();
+long cdda_transform();
+
+/*
+ * Send status information upstream.
+ */
+void
+send_status(struct cdda_block *blk)
+{
+ write(1, blk, sizeof(*blk));
+}
+
+/*
+ * Accept a command from our master.
+ *
+ * The protocol is byte-oriented:
+ * PmsfMSFxyz Play from msf to MSF (MSF can be 0,0,0 to play to end)
+ * xyz is the msf of the start of this chunk, i.e., the
+ * ending boundary for reverse play.
+ * S Stop.
+ * Q Quit.
+ * Vn Set volume level (0-255).
+ * Bn Set balance level (0-255).
+ * EnL Set an equalizer level (n = 0 for bass, 255 for treble)
+ * G Get current status.
+ * sn Set speed multiplier to n.
+ * dn Set direction to forward (n = 0) or reverse.
+ * Fllllx... Start saving to a file (length = l, followed by name)
+ * F0000 Stop saving.
+ * Ln Set loudness level (0-255).
+ */
+void
+command(int cd_fd, struct cdda_block *blk)
+{
+ unsigned char inbuf[10];
+ char *filename;
+ int namelen;
+ struct auheader hdr;
+
+ if (read(0, inbuf, 1) <= 0) /* Parent died. */
+ {
+ wmcdda_close();
+ wmaudio_close();
+ exit(0);
+ }
+
+ switch (inbuf[0]) {
+ case 'P':
+ read(0, inbuf, 9);
+ playing = 1;
+
+ wmaudio_stop();
+
+ wmcdda_setup(inbuf[0] * 60 * 75 + inbuf[1] * 75 + inbuf[2],
+ inbuf[3] * 60 * 75 + inbuf[4] * 75 + inbuf[5],
+ inbuf[6] * 60 * 75 + inbuf[7] * 75 + inbuf[8]);
+
+ wmaudio_ready();
+
+ level = 2500;
+ volume = 1 << 15;
+
+ blk->status = WMCDDA_ACK;
+ send_status(blk);
+ break;
+
+ case 'S':
+ playing = 0;
+ wmaudio_stop();
+ blk->status = WMCDDA_ACK;
+ send_status(blk);
+ blk->status = WMCDDA_STOPPED;
+ send_status(blk);
+ break;
+
+ case 'B':
+ read(0, inbuf, 1);
+ wmaudio_balance(inbuf[0]);
+ blk->status = WMCDDA_ACK;
+ send_status(blk);
+ break;
+
+ case 'V':
+ read(0, inbuf, 1);
+ wmaudio_volume(inbuf[0]);
+ blk->status = WMCDDA_ACK;
+ send_status(blk);
+ break;
+
+ case 'G':
+ blk->status = WMCDDA_ACK;
+ send_status(blk);
+
+ if (playing)
+ blk->status = WMCDDA_PLAYED;
+ else
+ blk->status = WMCDDA_STOPPED;
+ wmaudio_state(blk);
+ send_status(blk);
+ break;
+
+ case 'Q':
+ blk->status = WMCDDA_ACK;
+ send_status(blk);
+ wmcdda_close();
+ wmaudio_close();
+ exit(0);
+
+ case 's':
+ read(0, inbuf, 1);
+ speed = inbuf[0];
+ wmcdda_speed(speed);
+ blk->status = WMCDDA_ACK;
+ send_status(blk);
+ break;
+
+ case 'd':
+ read(0, inbuf, 1);
+ wmcdda_direction(inbuf[0]);
+ blk->status = WMCDDA_ACK;
+ send_status(blk);
+ break;
+
+ case 'L':
+ read(0, inbuf, 1);
+ loudness = inbuf[0];
+ blk->status = WMCDDA_ACK;
+ send_status(blk);
+ break;
+
+ case 'F':
+ read(0, &namelen, sizeof(namelen));
+ if (output != NULL)
+ {
+ fclose(output);
+ output = NULL;
+ }
+ if (namelen)
+ {
+ filename = malloc(namelen + 1);
+ if (filename == NULL)
+ {
+ perror("cddaslave");
+ wmcdda_close();
+ wmaudio_close();
+ exit(1);
+ }
+
+ read(0, filename, namelen);
+ filename[namelen] = '\0';
+ output = fopen(filename, "w");
+ if (output == NULL)
+ perror(filename);
+ else
+ {
+ /* Write an .au file header. */
+ hdr.magic = htonl(0x2e736e64);
+ hdr.hdr_size = htonl(sizeof(hdr) + 28);
+ hdr.data_size = htonl(~0);
+ hdr.encoding = htonl(3); /* linear-16 */
+ hdr.sample_rate = htonl(44100);
+ hdr.channels = htonl(2);
+
+ fwrite(&hdr, sizeof(hdr), 1, output);
+ fwrite("Recorded from CD by WorkMan", 28, 1,
+ output);
+ }
+
+ free(filename);
+ }
+
+ blk->status = WMCDDA_ACK;
+ send_status(blk);
+ }
+}
+
+/*
+ * Transform some CDDA data.
+ */
+long
+wmcdda_transform(unsigned char *rawbuf, long buflen, struct cdda_block *block)
+{
+ long i;
+ long *buf32 = (long *)rawbuf;
+ register short *buf16 = (short *)rawbuf;
+ register int aval;
+
+ /*
+ * Loudness transformation. Basically this is a self-adjusting
+ * volume control; our goal is to keep the average output level
+ * around a certain value (2500 seems to be pleasing.) We do this
+ * by maintaining a decaying average of the recent output levels
+ * (where "recent" is some fraction of a second.) All output levels
+ * are multiplied by the inverse of the decaying average; this has
+ * the volume-leveling effect we desire, and isn't too CPU-intensive.
+ *
+ * This is done by modifying the digital data, rather than adjusting
+ * the system volume control, because (at least on some systems)
+ * tweaking the system volume can generate little pops and clicks.
+ *
+ * There's probably a more elegant way to achieve this effect, but
+ * what the heck, I never took a DSP class and am making this up as
+ * I go along, with a little help from some friends.
+ *
+ * This is all done with fixed-point math, oriented around powers
+ * of two, which with luck will keep the CPU usage to a minimum.
+ * More could probably be done, for example using lookup tables to
+ * replace multiplies and divides; whether the memory hit (128K
+ * for each table) is worthwhile is unclear.
+ */
+ if (loudness)
+ {
+ /* We aren't really going backwards, but i > 0 is fast */
+ for (i = buflen / 2; i > 0; i--)
+ {
+ /*
+ * Adjust this sample to the current level.
+ */
+ aval = (*buf16 = (((long)*buf16) * volume) >> 15);
+ buf16++;
+
+ /*
+ * Don't adjust the decaying average for each sample;
+ * that just spends CPU time for very little benefit.
+ */
+ if (i & 127)
+ continue;
+
+ /*
+ * We want to use absolute values to compute the
+ * decaying average; otherwise it'd sit around 0.
+ */
+ if (aval < 0)
+ aval = -aval;
+
+ /*
+ * Adjust more quickly when we start hitting peaks,
+ * or we'll get clipping when there's a sudden loud
+ * section after lots of quiet.
+ */
+ if (aval & ~8191)
+ aval <<= 3;
+
+ /*
+ * Adjust the decaying average.
+ */
+ level = ((level << 11) - level + aval) >> 11;
+
+ /*
+ * Let *really* quiet sounds play softly, or we'll
+ * amplify background hiss to full volume and blast
+ * the user's speakers when real sound starts up.
+ */
+ if (! (level & ~511))
+ level = 512;
+
+ /*
+ * And adjust the volume setting using the inverse
+ * of the decaying average.
+ */
+ volume = (2500 << 15) / level;
+ }
+ }
+
+ if (speed == 128)
+ return (buflen);
+
+ /*
+ * Half-speed play. Stretch things out.
+ */
+ if (speed == 0)
+ {
+ buflen /= 2; /* Since we're moving 32 bits at a time */
+
+ for (i = buflen - 1; i > 0; i--)
+ {
+ buf32[i] = buf32[i / 2];
+ }
+
+ buflen *= 4; /* 2 for doubling the buffer, 2 from above */
+ }
+
+ /*
+ * Slow play; can't optimize it as well as half-speed.
+ */
+ if (speed && speed < 128)
+ {
+ int multiplier = ((speed + 128) * 128) / 256;
+ int newlen;
+ int tally = 0, pos;
+
+ buflen /= 4; /* Get the number of 32-bit values */
+
+ /*
+ * Buffer length doubles when speed is 0, stays the same
+ * when speed is 128.
+ */
+ newlen = (buflen * 128) / multiplier;
+
+ pos = buflen - 1;
+ for (i = newlen - 1; i > 0; i--)
+ {
+ buf32[i] = buf32[pos];
+ tally += multiplier;
+ if (tally & 128)
+ {
+ pos--;
+ tally ^= 128;
+ }
+ }
+
+ buflen = newlen * 4;
+ }
+
+ return (buflen);
+}
+
+
+main(argc, argv)
+ char **argv;
+{
+ int cd_fd = 3;
+ fd_set readfd, dummyfd;
+ struct timeval timeout;
+ char *cddabuf;
+ long cddabuflen;
+ struct cdda_block blockinfo;
+ long result;
+ int nfds;
+ char *devname;
+
+ /*
+ * Device name should be the command-line argument.
+ */
+ if (argc < 2)
+ devname = "";
+ else
+ devname = argv[1];
+
+ /*
+ * If we're running setuid root, bump up our priority then lose
+ * superuser access.
+ */
+ nice(-14);
+ setgid(getgid());
+ setuid(getuid());
+ if (getuid() != geteuid())
+ return 255;
+
+ FD_ZERO(&dummyfd);
+ FD_ZERO(&readfd);
+
+ timerclear(&timeout);
+
+ cd_fd = wmcdda_init(&cddabuf, &cddabuflen, cd_fd, devname);
+ if (cd_fd < 0)
+ exit(1);
+ wmaudio_init();
+
+ blockinfo.status = WMCDDA_ACK;
+ send_status(&blockinfo);
+ blockinfo.status = WMCDDA_STOPPED;
+
+fprintf(stderr,"cddaslave: done init.");
+ /*
+ * Accept commands as they come in, and play some sound if we're
+ * supposed to be doing that.
+ */
+ while (1)
+ {
+ FD_SET(0, &readfd);
+
+ /*
+ * If we're playing, we don't want select to block. Otherwise,
+ * wait a little while for the next command.
+ */
+ if (playing)
+ timeout.tv_usec = 0;
+ else
+ timeout.tv_usec = 500000;
+
+ nfds = select(1, &readfd, &dummyfd, &dummyfd, &timeout);
+
+ if (nfds < 0) /* Broken pipe; our GUI exited. */
+ {
+ wmcdda_close(cd_fd);
+ wmaudio_close();
+ fprintf(stderr,"cddaslave: Borken pipe; GUI must have exited.");
+ exit(0);
+ }
+
+ if (FD_ISSET(0, &readfd))
+ {
+ command(cd_fd, &blockinfo);
+ /*
+ * Process all commands in rapid succession, rather
+ * than possibly waiting for a CDDA read.
+ */
+ continue;
+ }
+
+ if (playing)
+ {
+ result = wmcdda_read(cd_fd, cddabuf, cddabuflen,
+ &blockinfo);
+ if (result <= 0)
+ {
+ /* Let the output queue drain. */
+ if (blockinfo.status == WMCDDA_DONE)
+ {
+ wmaudio_mark_last();
+ if (wmaudio_send_status())
+ {
+ /* queue drained, stop polling*/
+ playing = 0;
+ }
+ }
+ else
+ {
+ playing = 0;
+ send_status(&blockinfo);
+ }
+ }
+ else
+ {
+ result = wmcdda_normalize(cddabuf, result,
+ &blockinfo);
+ result = wmcdda_transform(cddabuf, result,
+ &blockinfo);
+ if (output)
+ fwrite(cddabuf, result, 1, output);
+ result = wmaudio_convert(cddabuf, result,
+ &blockinfo);
+ if (wmaudio_play(cddabuf, result, &blockinfo))
+ {
+ playing = 0;
+ wmaudio_stop();
+ send_status(&blockinfo);
+ }
+ }
+ }
+ else
+ send_status(&blockinfo);
+ }
+}
+
+#else /* BUILD_CDDA } { */
+
+#include <stdio.h>
+int main()
+{
+ printf("cddaslave: will work only on Solaris 2.4 or newer.");
+ exit(0);
+}
+
+#endif /* } */
diff --git a/kscd/cddbdlg.cpp b/kscd/cddbdlg.cpp
new file mode 100644
index 00000000..cc0e9669
--- /dev/null
+++ b/kscd/cddbdlg.cpp
@@ -0,0 +1,198 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <qkeycode.h>
+#include <qdatetime.h>
+#include <qtextstream.h>
+#include <qfile.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <klistview.h>
+#include <klineedit.h>
+#include <knuminput.h>
+
+#include <kglobal.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kinputdialog.h>
+#include <klocale.h>
+#include <kcombobox.h>
+#include <kmessagebox.h>
+
+#include <stdio.h>
+#include <math.h>
+
+#include "version.h"
+#include "kscd.h"
+#include "cddbdlg.h"
+#include "libkcddb/cdinfodialogbase.h"
+
+struct mytoc
+{
+ unsigned absframe;
+};
+
+CDDBDlg::CDDBDlg( QWidget* parent, const char* name )
+ : KDialogBase( parent, name, false, i18n( "CD Editor" ),
+ Ok|Cancel|User1|User2, Ok, true )
+{
+ KGlobal::locale()->insertCatalogue("libkcddb");
+
+ m_dlgBase = new CDInfoDialogBase( this, "m_dlgBase" );
+
+ setMainWidget( m_dlgBase );
+
+ setButtonText( User1, i18n( "Upload" ) );
+ setButtonText( User2, i18n( "Fetch Info" ) );
+
+ connect( this, SIGNAL( okClicked() ), SLOT( save() ) );
+ connect( this, SIGNAL( user1Clicked() ), SLOT( upload() ) );
+ connect( this, SIGNAL( user2Clicked() ), SIGNAL( cddbQuery() ) );
+ connect( m_dlgBase, SIGNAL( play( int ) ), SIGNAL( play( int ) ) );
+
+ cddbClient = new KCDDB::Client();
+ cddbClient->setBlockingMode(false);
+ connect (cddbClient, SIGNAL(finished(CDDB::Result)),
+ SLOT(submitFinished(CDDB::Result)));
+}
+
+
+CDDBDlg::~CDDBDlg()
+{
+ delete cddbClient;
+}
+
+void CDDBDlg::setData(
+ const KCDDB::CDInfo &_cddbInfo,
+ const KCDDB::TrackOffsetList &_trackStartFrames,
+ const QStringList &_playlist)
+{
+ // Let's make a deep copy of the cd struct info so that the data won't
+ // change the cd changes while we are playing with the dialog.
+ cddbInfo = _cddbInfo;
+ trackStartFrames = _trackStartFrames;
+ playlist = _playlist;
+
+ // Write the complete record to the dialog.
+ m_dlgBase->setInfo(cddbInfo, trackStartFrames);
+ // FIXME: KDE4, move this logic into m_dlgBase->setInfo() once KCDDB:CDInfo is updated.
+ m_dlgBase->m_playOrder->setText( playlist.join( "," ) );
+} // setData
+
+void CDDBDlg::submitFinished(KCDDB::CDDB::Result r)
+{
+ if (r == KCDDB::CDDB::Success)
+ {
+ KMessageBox::information(this, i18n("Record submitted successfully."),
+ i18n("Record Submission"));
+ }
+ else
+ {
+ QString str = i18n("Error sending record.\n\n%1")
+ .arg(KCDDB::CDDB::resultToString(r));
+ KMessageBox::error(this, str, i18n("Record Submission"));
+ }
+} // submitFinished()
+
+void CDDBDlg::upload()
+{
+ if (!validInfo())
+ return;
+
+ updateFromDialog();
+
+ // Create a copy with a bumped revision number.
+ KCDDB::CDInfo copyInfo = cddbInfo;
+ copyInfo.revision++;
+ cddbClient->submit(copyInfo, trackStartFrames);
+} // upload
+
+void CDDBDlg::save()
+{
+ updateFromDialog();
+
+ KCDDB::Cache::store(cddbInfo);
+
+ emit newCDInfoStored(cddbInfo);
+} // save
+
+bool CDDBDlg::validInfo()
+{
+ KCDDB::CDInfo copy = m_dlgBase->info();
+
+ if (copy.artist.isEmpty())
+ {
+ KMessageBox::sorry(this,
+ i18n("The artist name of the disc has to be entered.\n"
+ "Please correct the entry and try again."),
+ i18n("Invalid Database Entry"));
+ return false;
+ }
+
+ if (copy.title.isEmpty())
+ {
+ KMessageBox::sorry(this,
+ i18n("The title of the disc has to be entered.\n"
+ "Please correct the entry and try again."),
+ i18n("Invalid Database Entry"));
+ return false;
+ }
+
+ bool have_nonempty_title = false;
+ for (unsigned i = 0; i < copy.trackInfoList.count(); i++)
+ {
+ if (!copy.trackInfoList[i].title.isEmpty())
+ {
+ have_nonempty_title = true;
+ break;
+ }
+ }
+
+ if (!have_nonempty_title)
+ {
+ KMessageBox::sorry(this,
+ i18n("At least one track title must be entered.\n"\
+ "Please correct the entry and try again."),
+ i18n("Invalid Database Entry"));
+ return false;
+ }
+
+ return true;
+}
+
+void CDDBDlg::updateFromDialog()
+{
+ KCDDB::CDInfo copy = m_dlgBase->info();
+
+ // Playorder...
+ QStringList strlist = QStringList::split( ',', m_dlgBase->m_playOrder->text() );
+
+ bool ret = true;
+
+ QString teststr;
+ bool ok;
+ unsigned num;
+
+ for ( QStringList::Iterator it = strlist.begin();
+ it != strlist.end();
+ ++it )
+ {
+ teststr = *it;
+ num = teststr.toInt(&ok);
+
+ if( !ok || num > cddbInfo.trackInfoList.count() )
+ ret = false;
+ }
+
+ if(!ret)
+ {
+ KMessageBox::sorry(this,
+ i18n("Invalid Playlist\nPlease use valid track numbers, "
+ "separated by commas."));
+ }
+
+ cddbInfo = copy;
+} // updateFromDialog
+
+#include "cddbdlg.moc"
diff --git a/kscd/cddbdlg.h b/kscd/cddbdlg.h
new file mode 100644
index 00000000..bbef6e4e
--- /dev/null
+++ b/kscd/cddbdlg.h
@@ -0,0 +1,46 @@
+#ifndef CDDBDLG_H
+#define CDDBDLG_H
+
+#include <kdialogbase.h>
+
+#include "libkcddb/cdinfo.h"
+#include "libkcddb/cddb.h"
+#include "libkcddb/client.h"
+
+class CDInfoDialogBase;
+
+class CDDBDlg : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ CDDBDlg(QWidget* parent, const char* name = 0);
+ ~CDDBDlg();
+
+ void setData(
+ const KCDDB::CDInfo &_cddbInfo,
+ const KCDDB::TrackOffsetList &_trackStartFrames,
+ const QStringList &_playlist);
+
+ private slots:
+ void save();
+ void upload();
+ void submitFinished(CDDB::Result);
+
+ signals:
+ void cddbQuery();
+ void newCDInfoStored(KCDDB::CDInfo);
+ void play(int i);
+
+ private:
+ bool validInfo();
+ void updateFromDialog();
+ QString framesTime(unsigned frames);
+
+ CDInfoDialogBase *m_dlgBase;
+ KCDDB::CDInfo cddbInfo;
+ KCDDB::TrackOffsetList trackStartFrames;
+ QStringList playlist;
+ KCDDB::Client *cddbClient;
+};
+#endif // CDDBDLG_H
diff --git a/kscd/configWidget.cpp b/kscd/configWidget.cpp
new file mode 100644
index 00000000..84119c6b
--- /dev/null
+++ b/kscd/configWidget.cpp
@@ -0,0 +1,143 @@
+/*
+ * configWidget - the config dialog page for KSCD settings
+ *
+ * $Id:
+ *
+ * Copyright (c) 2002 Aaron J. Seigo <aseigo@kde.org>
+ * Copyright (c) 2004 Alexander Kern <alex.kern@gmx.de>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <kdebug.h>
+#include <klineedit.h>
+#include <dcopref.h>
+#include <kurlrequester.h>
+#include <qcheckbox.h>
+#include <kcombobox.h>
+#include <qlayout.h>
+
+#include <config.h>
+extern "C" {
+ // We don't have libWorkMan installed already, so get everything
+ // from within our own directory
+#include "libwm/include/wm_config.h"
+}
+
+
+#include "configWidget.h"
+#include "kscd.h"
+#include "prefs.h"
+
+class SpecialComboBox : public KComboBox
+{
+public:
+ SpecialComboBox(QWidget* parent, const char* name)
+ : KComboBox(parent, name)
+ {}
+
+ // QComboBox::setCurrentText replaces the current text if
+ // the list doesn't contain text, while
+ // KComboBox::setCurrentItem doesn't
+ void setCurrentText(const QString& text)
+ {
+ setCurrentItem(text);
+ }
+} ;
+
+/*
+ * Constructs a configWidget which is a child of 'parent', with the
+ * name 'name' and widget flags set to 'f'.
+ *
+ * The dialog will by default be modeless, unless you set 'modal' to
+ * TRUE to construct a modal dialog.
+ */
+configWidget::configWidget(KSCD* player, QWidget* parent, const char* name)
+ : configWidgetUI(parent, name),
+ mPlayer(player)
+{
+ if (!name)
+ {
+ setName("configWidget");
+ }
+
+ kcfg_cdDevice->comboBox()->setEditable(true);
+ kcfg_cdDevice->comboBox()->insertItem(DEFAULT_CD_DEVICE);
+ getMediaDevices();
+
+ (new QVBoxLayout(audioSystemFrame))->setAutoAdd(true);
+ kcfg_AudioSystem = new SpecialComboBox(audioSystemFrame, "kcfg_AudioSystem");
+ textLabel4->setBuddy(kcfg_AudioSystem);
+
+#if defined(BUILD_CDDA)
+ kcfg_DigitalPlayback_toggled(Prefs::digitalPlayback());
+
+ // fill ComboBox audioBackend
+ kcfg_AudioSystem->insertStringList(mPlayer->audioSystems());
+#else
+ kcfg_DigitalPlayback_toggled(false);
+
+ kcfg_DigitalPlayback->setChecked(false);
+ kcfg_DigitalPlayback->hide();
+#endif
+ kcfg_SelectEncoding_toggled(Prefs::selectEncoding());
+}
+
+configWidget::~configWidget()
+{
+}
+
+void configWidget::kcfg_DigitalPlayback_toggled(bool toggle)
+{
+ kcfg_AudioSystem->setEnabled(toggle);
+ textLabel4->setEnabled(toggle);
+ kcfg_AudioDevice->setEnabled(toggle);
+ textLabel5->setEnabled(toggle);
+}
+
+void configWidget::getMediaDevices()
+{
+ DCOPRef ref("kded","mediamanager");
+ DCOPReply rep = ref.call("fullList()");
+ if (!rep.isValid()) {
+ return;
+ }
+ QStringList list = rep;
+ QStringList::const_iterator it = list.begin();
+ QStringList::const_iterator itEnd = list.end();
+ // it would be much better if libmediacommon was in kdelibs
+ while (it != itEnd) {
+ it++;
+ if (it == itEnd) break;
+ QString url="media:/"+(*it); // is it always right? ervin?
+ kdDebug() << "checking " << url << endl;
+ for (int i=0;i<9;i++) ++it; // go to mimetype (MIME_TYPE-NAME from medium.h)
+ kdDebug() << "Mime: " << *it << endl;
+ if (it!=itEnd && (*it)=="media/audiocd") {
+ kcfg_cdDevice->comboBox()->insertItem(url);
+ }
+ while (it !=itEnd && (*it)!="---") ++it; // go to end of current device's properties
+ ++it;
+ }
+}
+
+
+void configWidget::kcfg_SelectEncoding_toggled(bool toggle)
+{
+ kcfg_SelectedEncoding->setEnabled(toggle);
+}
+
+#include "configWidget.moc"
diff --git a/kscd/configWidget.h b/kscd/configWidget.h
new file mode 100644
index 00000000..bd0ea70f
--- /dev/null
+++ b/kscd/configWidget.h
@@ -0,0 +1,50 @@
+/*
+ * configWidget - the config dialog page for KSCD settings
+ *
+ * Copyright (c) 2002 Aaron J. Seigo <aseigo@kde.org>
+ * Copyright (c) 2004 Alexander Kern <alex.kern@gmx.de>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef CONFIGWIDGET_H
+#define CONFIGWIDGET_H
+
+#include "configWidgetUI.h"
+
+class KSCD;
+class SpecialComboBox;
+
+class configWidget : public configWidgetUI
+{
+ Q_OBJECT
+
+ public:
+ configWidget(KSCD* player, QWidget* parent = 0, const char* name = 0);
+ ~configWidget();
+
+ protected:
+ KSCD* mPlayer;
+ SpecialComboBox* kcfg_AudioSystem;
+
+ public slots:
+ virtual void kcfg_DigitalPlayback_toggled(bool);
+ virtual void kcfg_SelectEncoding_toggled(bool);
+ private:
+ void getMediaDevices();
+};
+
+#endif // CONFIGWIDGET_H
diff --git a/kscd/configWidgetUI.ui b/kscd/configWidgetUI.ui
new file mode 100644
index 00000000..83f9aca0
--- /dev/null
+++ b/kscd/configWidgetUI.ui
@@ -0,0 +1,462 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>configWidgetUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>configWidgetUI</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>539</width>
+ <height>605</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Interface</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Background color:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_BackColor</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="2" column="1">
+ <property name="name">
+ <cstring>kcfg_BackColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The background color that will be used for the LCD display.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_Docking</cstring>
+ </property>
+ <property name="text">
+ <string>Show icon in &amp;system tray</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>When this option is selected an icon will appear in the system tray. Note that KsCD will &lt;i&gt;not&lt;/i&gt; quit when the window is closed if a system tray icon is displayed. You may quit KsCD by clicking the Quit button or right-clicking on the system tray icon and selecting the appropriate entry.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_TrackAnnouncement</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Show &amp;track announcement</string>
+ </property>
+ </widget>
+ <spacer row="1" column="2" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>292</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;LCD color:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_ledColor</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="1">
+ <property name="name">
+ <cstring>kcfg_ledColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The foreground color that will be used in the LCD display.</string>
+ </property>
+ </widget>
+ <widget class="KFontRequester" row="0" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_ledFont</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>LCD &amp;font:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_ledFont</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>Play Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KIntNumInput" row="0" column="1">
+ <property name="name">
+ <cstring>kcfg_SkipDelta</cstring>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="maxValue">
+ <number>1000</number>
+ </property>
+ <property name="suffix">
+ <string> seconds</string>
+ </property>
+ <property name="specialValueText">
+ <string>1 second</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This option controls the number of seconds KsCD will skip when the skip forwards or backwards buttons are pressed.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Skip &amp;interval:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_SkipDelta</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_Autoplay</cstring>
+ </property>
+ <property name="text">
+ <string>Auto&amp;play when CD inserted</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>When this option is selected the CD will start playing automatically upon being inserted into the CD-ROM.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_EjectOnFinish</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Eject CD when finished playing</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>When this option is selected the CD will automatically eject when it is finished.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_StopExit</cstring>
+ </property>
+ <property name="text">
+ <string>Stop playing CD on e&amp;xit</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>When this option is selected the CD will automatically stop playing when quitting KsCD.</string>
+ </property>
+ </widget>
+ <spacer row="0" column="2" rowspan="4" colspan="1">
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>171</width>
+ <height>81</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox3</cstring>
+ </property>
+ <property name="title">
+ <string>CD-ROM &amp;Device</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KURLComboRequester" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_cdDevice</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The CD-ROM device to use when playing CDs. This will typically look something like "/dev/cdrom". To have KsCD autodetect your CD-ROM, leave this field empty.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Select audio de&amp;vice:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_AudioDevice</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_DigitalPlayback</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Use direct digital playback</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>When this option is selected KsCD will attempt to play the CD using direct digital playback. This option is useful if the CD-ROM is not connected directly to the sound output on the computer. Note that digital playback consumes more system resources than the normal method of playback.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Select &amp;audio backend:</string>
+ </property>
+ <property name="indent">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="3" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_AudioDevice</cstring>
+ </property>
+ </widget>
+ <widget class="QFrame" row="2" column="2">
+ <property name="name">
+ <cstring>audioSystemFrame</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox9</cstring>
+ </property>
+ <property name="title">
+ <string> Music Information Services </string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_SelectEncoding</cstring>
+ </property>
+ <property name="text">
+ <string>Allow en&amp;coding selection:</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>When this option is selected, you have the ability to select encoding for the results of a CDDB request. The standard describes CDDB results as being strictly Latin1. This is not true, as non-English speaking users often use other 8-bit encodings.</string>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>AUTO</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>UTF-8</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>CP1250</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>CP1251</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>CP1252</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>CP1253</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>CP1254</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>CP1255</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>CP1256</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>CP1257</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>kcfg_SelectedEncoding</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="duplicatesEnabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>45</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>kcfg_DigitalPlayback</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>configWidgetUI</receiver>
+ <slot>kcfg_DigitalPlayback_toggled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_Docking</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_TrackAnnouncement</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_SelectEncoding</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>configWidgetUI</receiver>
+ <slot>kcfg_SelectEncoding_toggled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>kcfg_ledColor</tabstop>
+ <tabstop>kcfg_BackColor</tabstop>
+ <tabstop>kcfg_Docking</tabstop>
+ <tabstop>kcfg_TrackAnnouncement</tabstop>
+ <tabstop>kcfg_SkipDelta</tabstop>
+ <tabstop>kcfg_Autoplay</tabstop>
+ <tabstop>kcfg_EjectOnFinish</tabstop>
+ <tabstop>kcfg_StopExit</tabstop>
+ <tabstop>kcfg_cdDevice</tabstop>
+ <tabstop>kcfg_DigitalPlayback</tabstop>
+ <tabstop>kcfg_AudioDevice</tabstop>
+</tabstops>
+<slots>
+ <slot>kcfg_DigitalPlayback_toggled( bool )</slot>
+ <slot>kcfg_SelectEncoding_toggled(bool)</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kfontrequester.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kscd/configure.in.in b/kscd/configure.in.in
new file mode 100644
index 00000000..9f92c1a7
--- /dev/null
+++ b/kscd/configure.in.in
@@ -0,0 +1,68 @@
+dnl +-------------------------+
+dnl | Checks for KSCD |
+dnl +-------------------------+
+AC_CHECK_LIB(cdaudio, main, [LIBCDAUDIO="-lcdaudio -lmediad -lds"],[],[-lmedia -lds]) dnl for Irix
+AC_SUBST(LIBCDAUDIO)
+AC_CHECK_LIB(fpe, main, [LIBFPE="-lfpe"]) dnl for Irix CDDA
+AC_SUBST(LIBFPE) dnl for Irix CDDA
+AC_CHECK_LIB(cdrom, main, [LIBCDROM="-lcdrom"],[],[-lcdrom]) dnl for kscd FreeBSD
+AC_SUBST(LIBCDROM)
+
+dnl Checks for libraries.
+dnl added by wuebben Nov 27 1997
+AC_CHECK_LIB(Alib, main, [LIBALIB="-lAlib"]) dnl HP-UX network audio server
+AC_SUBST(LIBALIB)
+AC_CHECK_LIB(audio,main,[LIBAUDIO="-laudio"]) dnl SunOS audio driver
+AC_SUBST(LIBAUDIO)
+AC_CHECK_LIB(mme, main, [LIBMME="-lmme";EXTRAINCS="-I/usr/include/mme"]) dnl DEC MMS audio server
+AC_SUBST(LIBMME)
+
+AC_SUBST(EXTRAINCS)
+AC_SUBST(EXTRALIBS)
+
+AC_ARG_ENABLE(kscd-defaults,[ --enable-kscd-defaults use kscd default configs [default=yes]],
+[
+if test $enableval = "no"; dnl
+ then use_kscd_defaults="no"
+ else use_kscd_defaults="yes"
+fi
+], use_kscd_defaults="yes"
+)
+
+AC_DEFUN([KDE_COPY_KSCD_DEFAULTS],
+[
+if echo $TOPSUBDIRS | grep "kscd" > /dev/null 2> /dev/null; then
+ if test "$use_kscd_defaults" = "yes"; then
+ test -d kscd || mkdir kscd
+ cp $srcdir/kscd/config.h.std kscd/config.h
+ else
+ cd kscd && $srcdir/kscd-script
+ cd $topdir
+ fi
+fi
+])
+
+AC_OUTPUT_COMMANDS(KDE_COPY_KSCD_DEFAULTS)
+
+dnl +-------------------------+
+dnl | End KSCD checks |
+dnl +-------------------------+
+
+AC_MSG_CHECKING(if kscd can be compiled)
+case "$host" in
+ *-*-linux*) kscd_compile=yes;;
+ *-*-*bsdi*) kscd_compile=yes;;
+ *-*-*freebsd*) kscd_compile=yes;;
+ *-*-*netbsd*) kscd_compile=yes;;
+ *-*-*openbsd*) kscd_compile=yes;;
+ *) kscd_compile=yes;;
+esac
+AC_MSG_RESULT($kscd_compile)
+if test "$kscd_compile" = "no"; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE kscd"
+fi
+
+if test "x$build_arts" != "xno"; then
+ AC_DEFINE_UNQUOTED(USE_ARTS, 1, [Define if aRts is available])
+fi
+
diff --git a/kscd/cr22-action-cdsmall.png b/kscd/cr22-action-cdsmall.png
new file mode 100644
index 00000000..d9f8f05e
--- /dev/null
+++ b/kscd/cr22-action-cdsmall.png
Binary files differ
diff --git a/kscd/docking.cpp b/kscd/docking.cpp
new file mode 100644
index 00000000..9792ba73
--- /dev/null
+++ b/kscd/docking.cpp
@@ -0,0 +1,158 @@
+/*
+ * KSCD -- a simpole cd player for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "docking.h"
+#include "kscd.h"
+
+#include <qhbox.h>
+#include <qtooltip.h>
+
+#include <kaboutdata.h>
+#include <kactioncollection.h>
+#include <kapplication.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <kpopupmenu.h>
+#include <kpassivepopup.h>
+
+#include <kdebug.h>
+
+DockWidget::DockWidget( KSCD* parent, const char *name)
+ : KSystemTray( parent, name )
+{
+ m_popup = 0;
+ setPixmap( loadIcon("cdsmall") );
+
+ KActionCollection* actionCollection = parent->actionCollection();
+ m_backAction = actionCollection->action("Previous");
+ m_forwardAction = actionCollection->action("Next");
+ m_backPix = loadIcon("player_start");
+ m_forwardPix = loadIcon("player_end");
+
+ // popup menu for right mouse button
+ QPopupMenu* popup = contextMenu();
+
+ popup->insertItem(KGlobal::iconLoader()->loadIconSet("player_play", KIcon::Small), i18n("Play/Pause"), parent, SLOT(playClicked()));
+ popup->insertItem(KGlobal::iconLoader()->loadIconSet("player_stop", KIcon::Small), i18n("Stop"), parent, SLOT(stopClicked()));
+ popup->insertItem(KGlobal::iconLoader()->loadIconSet("player_end", KIcon::Small), i18n("Next"), parent, SLOT(nextClicked()));
+ popup->insertItem(KGlobal::iconLoader()->loadIconSet("player_start", KIcon::Small), i18n("Previous"), parent, SLOT(prevClicked()));
+ popup->insertItem(KGlobal::iconLoader()->loadIconSet("player_eject", KIcon::Small), i18n("Eject"), parent, SLOT(ejectClicked()));
+
+ QToolTip::add(this, kapp->aboutData()->programName());
+}
+
+DockWidget::~DockWidget()
+{
+}
+
+void DockWidget::createPopup(const QString &songName, bool addButtons)
+{
+ if (!Prefs::trackAnnouncement())
+ return;
+
+ delete m_popup;
+ m_popup = new KPassivePopup(this);
+
+ QHBox* box = new QHBox(m_popup);
+
+ if (addButtons)
+ {
+ QPushButton* backButton = new QPushButton(m_backPix, 0, box, "popup_back");
+ backButton->setFlat(true);
+ connect(backButton, SIGNAL(clicked()), m_backAction, SLOT(activate()));
+ }
+
+ QLabel* l = new QLabel(songName, box);
+ l->setMargin(3);
+
+ if (addButtons)
+ {
+ QPushButton* forwardButton = new QPushButton(m_forwardPix, 0, box, "popup_forward");
+ forwardButton->setFlat(true);
+ connect(forwardButton, SIGNAL(clicked()), m_forwardAction, SLOT(activate()));
+ }
+
+ m_popup->setView(box);
+ m_popup->setAutoDelete(false);
+ m_popup->show();
+}
+
+void DockWidget::setToolTip(const QString& text)
+{
+ if (tip == text)
+ {
+ return;
+ }
+
+ tip = text;
+ QToolTip::remove(this);
+
+ if (text.isEmpty())
+ {
+ QToolTip::add(this, kapp->aboutData()->programName());
+ }
+ else
+ {
+ QToolTip::add(this, text);
+ }
+}
+
+void DockWidget::wheelEvent(QWheelEvent *e)
+{
+ if (e->orientation() == Horizontal)
+ return;
+
+ KSCD* kscd = dynamic_cast<KSCD*>(parent());
+ if (kscd == 0)
+ return;
+
+ switch (e->state())
+ {
+ case ShiftButton:
+ {
+ if (e->delta() > 0)
+ {
+ kscd->incVolume();
+ }
+ else
+ {
+ kscd->decVolume();
+ }
+ break;
+ }
+ default:
+ {
+ if (e->delta() > 0)
+ {
+ kscd->nextClicked();
+ }
+ else
+ {
+ kscd->prevClicked();
+ }
+ }
+ }
+}
+
+#include "docking.moc"
diff --git a/kscd/docking.h b/kscd/docking.h
new file mode 100644
index 00000000..ff946c15
--- /dev/null
+++ b/kscd/docking.h
@@ -0,0 +1,75 @@
+/*
+ * kPPP: A pppd Front End for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * This file was contributed by Harri Porten <porten@tu-harburg.de>
+ * Latest changes (dynamic tooltips) by Cristian Tibirna <tibirna@kde.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef _DOCKING_H_
+#define _DOCKING_H_
+
+#include <stdio.h>
+#include <qapplication.h>
+#include <qpixmap.h>
+#include <qtimer.h>
+#include <qpopupmenu.h>
+#include <qpoint.h>
+#include <kdockwindow.h>
+
+class KSCD;
+
+class KAction;
+class KToggleAction;
+class KPassivePopup;
+
+class DockWidget : public KSystemTray
+{
+ Q_OBJECT
+
+public:
+ DockWidget( KSCD* parent, const char *name=0);
+ ~DockWidget();
+
+public slots:
+ void setToolTip(const QString& text);
+ void createPopup(const QString& songName, bool addButtons = true);
+
+private:
+ virtual void wheelEvent( QWheelEvent *e);
+
+ KPassivePopup* m_popup;
+
+ KAction* m_forwardAction;
+ KAction* m_backAction;
+
+ QPixmap m_backPix;
+ QPixmap m_forwardPix;
+
+ QString tip;
+};
+
+#endif
+
+
+
diff --git a/kscd/hi128-app-kscd.png b/kscd/hi128-app-kscd.png
new file mode 100644
index 00000000..25496a41
--- /dev/null
+++ b/kscd/hi128-app-kscd.png
Binary files differ
diff --git a/kscd/hi16-app-kscd.png b/kscd/hi16-app-kscd.png
new file mode 100644
index 00000000..e59a04eb
--- /dev/null
+++ b/kscd/hi16-app-kscd.png
Binary files differ
diff --git a/kscd/hi32-app-kscd.png b/kscd/hi32-app-kscd.png
new file mode 100644
index 00000000..0586fee8
--- /dev/null
+++ b/kscd/hi32-app-kscd.png
Binary files differ
diff --git a/kscd/hi48-app-kscd.png b/kscd/hi48-app-kscd.png
new file mode 100644
index 00000000..4d001825
--- /dev/null
+++ b/kscd/hi48-app-kscd.png
Binary files differ
diff --git a/kscd/hi64-app-kscd.png b/kscd/hi64-app-kscd.png
new file mode 100644
index 00000000..1769ca22
--- /dev/null
+++ b/kscd/hi64-app-kscd.png
Binary files differ
diff --git a/kscd/kcompactdisc.cpp b/kscd/kcompactdisc.cpp
new file mode 100644
index 00000000..b69325a3
--- /dev/null
+++ b/kscd/kcompactdisc.cpp
@@ -0,0 +1,486 @@
+/*
+ * KCompactDisc - A CD drive interface for the KDE Project.
+ *
+ * Copyright (c) 2005 Shaheedur R. Haque <srhaque@iee.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <dcopclient.h>
+#include <dcopref.h>
+#include <qfile.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kprotocolmanager.h>
+#include <krun.h>
+#include "kcompactdisc.h"
+#include <netwm.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+/* this is for glibc 2.x which the ust structure in ustat.h not stat.h */
+#ifdef __GLIBC__
+#include <sys/ustat.h>
+#endif
+
+#ifdef __FreeBSD__
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#endif
+
+#ifdef __linux__
+#include <mntent.h>
+#define KSCDMAGIC 0
+#endif
+
+#include <kprocess.h>
+#include <config.h>
+
+extern "C"
+{
+// We don't have libWorkMan installed already, so get everything
+// from within our own directory
+#include "libwm/include/wm_cddb.h"
+#include "libwm/include/wm_cdrom.h"
+#include "libwm/include/wm_cdtext.h"
+#include "libwm/include/wm_config.h"
+#include "libwm/include/wm_cdinfo.h"
+#include "libwm/include/wm_helpers.h"
+
+// Sun, Ultrix etc. have a canonical CD device specified in the
+// respective plat_xxx.c file. On those platforms you need not
+// specify the CD device and DEFAULT_CD_DEVICE is not defined
+// in config.h.
+#ifndef DEFAULT_CD_DEVICE
+#define DEFAULT_CD_DEVICE "/dev/cdrom"
+#endif
+}
+
+#include <qtextcodec.h>
+#include <fixx11h.h>
+
+// Our internal definition of when we have no disc. Used to guard some
+// internal arrays.
+#define NO_DISC ((m_discId == missingDisc) && (m_previousDiscId == 0))
+
+#define FRAMES_TO_MS(frames) \
+((frames) * 1000 / 75)
+
+#define TRACK_VALID(track) \
+((track) && (track <= m_tracks))
+
+const QString KCompactDisc::defaultDevice = DEFAULT_CD_DEVICE;
+const unsigned KCompactDisc::missingDisc = (unsigned)-1;
+
+KCompactDisc::KCompactDisc(InformationMode infoMode) :
+ m_device(QString::null),
+ m_status(0),
+ m_previousStatus(123456),
+ m_discId(missingDisc),
+ m_previousDiscId(0),
+ m_artist(QString::null),
+ m_title(QString::null),
+ m_track(0),
+ m_previousTrack(99999999),
+ m_infoMode(infoMode)
+{
+ // Debug.
+ // wm_cd_set_verbosity(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS_ALL );
+ m_trackArtists.clear();
+ m_trackTitles.clear();
+ m_trackStartFrames.clear();
+ connect(&timer, SIGNAL(timeout()), SLOT(timerExpired()));
+}
+
+KCompactDisc::~KCompactDisc()
+{
+ // Ensure nothing else starts happening.
+ timer.stop();
+ wm_cd_stop();
+ wm_cd_set_verbosity(0x0);
+ wm_cd_destroy();
+}
+
+const QString &KCompactDisc::device() const
+{
+ return m_device;
+}
+
+unsigned KCompactDisc::discLength() const
+{
+ if (NO_DISC || !m_tracks)
+ return 0;
+ return FRAMES_TO_MS(m_trackStartFrames[m_tracks+1] - m_trackStartFrames[0]);
+}
+
+unsigned KCompactDisc::discPosition() const
+{
+ return cur_pos_abs * 1000 - FRAMES_TO_MS(m_trackStartFrames[0]);
+}
+
+QString KCompactDisc::discStatus(int status)
+{
+ QString message;
+
+ switch (status)
+ {
+ case WM_CDM_TRACK_DONE: // == WM_CDM_BACK
+ message = i18n("Back/Track Done");
+ break;
+ case WM_CDM_PLAYING:
+ message = i18n("Playing");
+ break;
+ case WM_CDM_FORWARD:
+ message = i18n("Forward");
+ break;
+ case WM_CDM_PAUSED:
+ message = i18n("Paused");
+ break;
+ case WM_CDM_STOPPED:
+ message = i18n("Stopped");
+ break;
+ case WM_CDM_EJECTED:
+ message = i18n("Ejected");
+ break;
+ case WM_CDM_NO_DISC:
+ message = i18n("No Disc");
+ break;
+ case WM_CDM_UNKNOWN:
+ message = i18n("Unknown");
+ break;
+ case WM_CDM_CDDAERROR:
+ message = i18n("CDDA Error");
+ break;
+ case WM_CDM_CDDAACK:
+ message = i18n("CDDA Ack");
+ break;
+ default:
+ if (status <= 0)
+ message = strerror(-status);
+ else
+ message = QString::number(status);
+ break;
+ }
+ return message;
+}
+
+/**
+ * Do everything needed if the user requested to eject the disc.
+ */
+void KCompactDisc::eject()
+{
+ if (m_status == WM_CDM_EJECTED)
+ {
+ emit trayClosing();
+ wm_cd_closetray();
+ }
+ else
+ {
+ wm_cd_stop();
+ wm_cd_eject();
+ }
+}
+
+unsigned KCompactDisc::track() const
+{
+ return m_track;
+}
+
+bool KCompactDisc::isPaused() const
+{
+ return (m_status == WM_CDM_PAUSED);
+}
+
+bool KCompactDisc::isPlaying() const
+{
+ return WM_CDS_DISC_PLAYING(m_status) && (m_status != WM_CDM_PAUSED) && (m_status != WM_CDM_TRACK_DONE);
+}
+
+void KCompactDisc::pause()
+{
+ // wm_cd_pause "does the right thing" by flipping between pause and resume.
+ wm_cd_pause();
+}
+
+void KCompactDisc::play(unsigned startTrack, unsigned startTrackPosition, unsigned endTrack)
+{
+ wm_cd_play(TRACK_VALID(startTrack) ? startTrack : 1, startTrackPosition / 1000, TRACK_VALID(endTrack) ? endTrack : WM_ENDTRACK );
+}
+
+QString KCompactDisc::urlToDevice(const QString& device)
+{
+ KURL deviceUrl(device);
+ if (deviceUrl.protocol() == "media" || deviceUrl.protocol() == "system")
+ {
+ kdDebug() << "Asking mediamanager for " << deviceUrl.fileName() << endl;
+ DCOPRef mediamanager("kded", "mediamanager");
+ DCOPReply reply = mediamanager.call("properties(QString)", deviceUrl.fileName());
+ QStringList properties = reply;
+ if (!reply.isValid() || properties.count() < 6)
+ {
+ kdError() << "Invalid reply from mediamanager" << endl;
+ return defaultDevice;
+ }
+ else
+ {
+ kdDebug() << "Reply from mediamanager " << properties[5] << endl;
+ return properties[5];
+ }
+ }
+
+ return device;
+}
+
+bool KCompactDisc::setDevice(
+ const QString &device_,
+ unsigned volume,
+ bool digitalPlayback,
+ const QString &audioSystem,
+ const QString &audioDevice)
+{
+ timer.stop();
+
+ QString device = urlToDevice(device_);
+
+#if !defined(BUILD_CDDA)
+ digitalPlayback = false;
+#endif
+ int status = wm_cd_init(
+ digitalPlayback ? WM_CDDA : WM_CDIN,
+ QFile::encodeName(device),
+ digitalPlayback ? audioSystem.ascii() : 0,
+ digitalPlayback ? audioDevice.ascii() : 0,
+ 0);
+ m_device = wm_drive_device();
+ kdDebug() << "Device change: "
+ << (digitalPlayback ? "WM_CDDA, " : "WM_CDIN, ")
+ << m_device << ", "
+ << (digitalPlayback ? audioSystem : QString::null) << ", "
+ << (digitalPlayback ? audioDevice : QString::null) << ", status: "
+ << discStatus(status) << endl;
+
+ if (status < 0)
+ {
+ // Severe (OS-level) error.
+ m_device = QString::null;
+ }
+ else
+ {
+ // Init CD-ROM and display.
+ setVolume(volume);
+ }
+
+ m_previousStatus = m_status = wm_cd_status();
+
+ if (m_infoMode == Asynchronous)
+ timerExpired();
+ else
+ timer.start(1000, true);
+ return m_device != QString::null;
+}
+
+void KCompactDisc::setVolume(unsigned volume)
+{
+ int status = wm_cd_volume(volume, WM_BALANCE_SYMMETRED);
+ kdDebug() << "Volume change: " << volume << ", status: " << discStatus(status) << endl;
+}
+
+void KCompactDisc::stop()
+{
+ wm_cd_stop();
+}
+
+const QString &KCompactDisc::trackArtist() const
+{
+ return trackArtist(m_track);
+}
+
+const QString &KCompactDisc::trackArtist(unsigned track) const
+{
+ if (NO_DISC || !TRACK_VALID(track))
+ return QString::null;
+ return m_trackArtists[track - 1];
+}
+
+unsigned KCompactDisc::trackLength() const
+{
+ return trackLength(m_track);
+}
+
+unsigned KCompactDisc::trackLength(unsigned track) const
+{
+ if (NO_DISC || !TRACK_VALID(track))
+ return 0;
+ return cd->trk[track - 1].length * 1000;
+}
+
+unsigned KCompactDisc::trackPosition() const
+{
+ return cur_pos_rel * 1000;
+}
+
+unsigned KCompactDisc::tracks() const
+{
+ return m_tracks;
+}
+
+const QString &KCompactDisc::trackTitle() const
+{
+ return trackTitle(m_track);
+}
+
+const QString &KCompactDisc::trackTitle(unsigned track) const
+{
+ if (NO_DISC || !TRACK_VALID(track))
+ return QString::null;
+ return m_trackTitles[track - 1];
+}
+
+bool KCompactDisc::isAudio(unsigned track) const
+{
+ if (NO_DISC || !TRACK_VALID(track))
+ return 0;
+ return !(cd->trk[track - 1].data);
+}
+
+/*
+ * timerExpired
+ *
+ * - Data discs not recognized as data discs.
+ *
+ */
+void KCompactDisc::timerExpired()
+{
+ m_status = wm_cd_status();
+
+ if (WM_CDS_NO_DISC(m_status) || (m_device == QString::null))
+ {
+ if (m_previousStatus != m_status)
+ {
+ m_previousStatus = m_status;
+ m_discId = missingDisc;
+ m_previousDiscId = 0;
+ m_trackArtists.clear();
+ m_trackTitles.clear();
+ m_trackStartFrames.clear();
+ m_tracks = 0;
+ m_track = 0;
+ emit discChanged(m_discId);
+ }
+ }
+ else
+ {
+ m_discId = cddb_discid();
+ if (m_previousDiscId != m_discId)
+ {
+ m_previousDiscId = m_discId;
+ kdDebug() << "New discId=" << m_discId << endl;
+ // Initialise the album and its signature from the CD.
+ struct cdtext_info *info = wm_cd_get_cdtext();
+ if (info && info->valid)
+ {
+ m_artist = reinterpret_cast<char*>(info->blocks[0]->performer[0]);
+ m_title = reinterpret_cast<char*>(info->blocks[0]->name[0]);
+ }
+ else
+ {
+ m_artist = i18n("Unknown Artist");
+ m_title = i18n("Unknown Title");
+ }
+
+ // Read or default CD data.
+ m_trackArtists.clear();
+ m_trackTitles.clear();
+ m_trackStartFrames.clear();
+ m_tracks = wm_cd_getcountoftracks();
+ for (unsigned i = 1; i <= m_tracks; i++)
+ {
+ if (info && info->valid)
+ {
+ m_trackArtists.append(reinterpret_cast<char*>(info->blocks[0]->performer[i]));
+ m_trackTitles.append(reinterpret_cast<char*>(info->blocks[0]->name[i]));
+ }
+ else
+ {
+ m_trackArtists.append(i18n("Unknown Artist"));
+ m_trackTitles.append(i18n("Track %1").arg(QString::number(i).rightJustify(2, '0')));
+ }
+ // FIXME: KDE4
+ // track.length = cd->trk[i - 1].length;
+ m_trackStartFrames.append(cd->trk[i - 1].start);
+ }
+ m_trackStartFrames.append(cd->trk[0].start);
+ m_trackStartFrames.append(cd->trk[m_tracks].start);
+ emit discChanged(m_discId);
+ }
+
+ // Per-event processing.
+ m_track = wm_cd_getcurtrack();
+ if (m_previousTrack != m_track)
+ {
+ m_previousTrack = m_track;
+
+ // Update the current track and its length.
+ emit trackChanged(m_track, trackLength());
+ }
+ if (isPlaying())
+ {
+ m_previousStatus = m_status;
+ // Update the current playing position.
+ emit trackPlaying(m_track, trackPosition());
+ }
+ else
+ if (m_previousStatus != m_status)
+ {
+ // If we are not playing, then we are either paused, or stopped.
+ switch (m_status)
+ {
+ case WM_CDM_PAUSED:
+ emit trackPaused(m_track, trackPosition());
+ break;
+ case WM_CDM_EJECTED:
+ emit trayOpening();
+ break;
+ default:
+ if (m_previousStatus == WM_CDM_PLAYING || m_previousStatus == WM_CDM_PAUSED
+ && m_status == WM_CDM_STOPPED)
+ {
+ emit discStopped();
+ }
+ break;
+ }
+
+ m_previousStatus = m_status;
+ }
+ }
+
+ // Now that we have incurred any delays caused by the signals, we'll start the timer.
+ timer.start(1000, true);
+}
+
+#include "kcompactdisc.moc"
diff --git a/kscd/kcompactdisc.h b/kscd/kcompactdisc.h
new file mode 100644
index 00000000..595567f4
--- /dev/null
+++ b/kscd/kcompactdisc.h
@@ -0,0 +1,303 @@
+/*
+ * KCompactDisc - A CD drive interface for the KDE Project.
+ *
+ * Copyright (c) 2005 Shaheedur R. Haque <srhaque@iee.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef KCOMPACTDISC_H
+#define KCOMPACTDISC_H
+
+#include <qobject.h>
+#include <qtimer.h>
+#include <qvaluelist.h>
+
+/**
+ * KCompactDisc - A CD drive interface for the KDE Project.
+ *
+ * The disc lifecycle is modelled by these signals:
+ *
+ * @see #trayClosing(): A disc is being inserted.
+ * @see #discChanged(): A disc was inserted or removed.
+ * @see #trayOpening(): A disc is being removed.
+ *
+ * The progress of playout is modelled by these signals:
+ *
+ * @see #trackPlaying(): A track started playing, or is still playing.
+ * @see #trackPaused(): A track was paused.
+ * @see #discStopped(): The disc stopped.
+ *
+ * All times in this interface are in milliseconds. Valid track numbers are
+ * positive numbers; zero is not a valid track number.
+ */
+class KCompactDisc :
+ public QObject
+{
+ Q_OBJECT
+public:
+ enum InformationMode
+ {
+ Synchronous, // Return and emit signal when cdrom and cddb information arrives.
+ Asynchronous // Block until cdrom and cddb infromation has been obtained
+ };
+
+ KCompactDisc(InformationMode=Synchronous);
+ virtual ~KCompactDisc();
+
+ /**
+ * Open/close tray.
+ */
+ void eject();
+
+ /**
+ * Start playout at given position of track.
+ */
+ void play(unsigned startTrack = 0, unsigned startTrackPosition = 0, unsigned endTrack = 0);
+
+ /**
+ * Pause/resume playout.
+ */
+ void pause();
+
+ /**
+ * If the url is a media:/ or system:/ URL returns
+ * the device it represents, otherwise returns device
+ */
+ static QString urlToDevice(const QString& device);
+
+ /**
+ * @param device Name of CD device, e.g. /dev/cdrom.
+ * @param digitalPlayback Select digial or analogue playback.
+ * @param audioSystem For analogue playback, system to use, e.g. "arts".
+ * @param audioDevice For analogue playback, device to use.
+ * @return true if the device seemed usable.
+ */
+ bool setDevice(
+ const QString &device = defaultDevice,
+ unsigned volume = 50,
+ bool digitalPlayback = true,
+ const QString &audioSystem = QString::null,
+ const QString &audioDevice = QString::null);
+
+ void setVolume(unsigned volume);
+
+ /**
+ * Stop playout.
+ */
+ void stop();
+
+ /**
+ * The default CD for this system.
+ */
+ static const QString defaultDevice;
+
+ /**
+ * Current device.
+ *
+ * @return Null string if no usable device set.
+ */
+ const QString &device() const;
+
+ /**
+ * The discId for a missing disc.
+ */
+ static const unsigned missingDisc;
+
+ /**
+ * Current disc, missingDisc if no disc.
+ */
+ unsigned discId() const { return m_discId; }
+
+ /**
+ * CDDB signature of disc.
+ */
+ const QValueList<unsigned> &discSignature() const { return m_trackStartFrames; }
+
+ /**
+ * Artist for whole disc.
+ *
+ * @return Disc artist or null string.
+ */
+ const QString &discArtist() const { return m_artist; }
+
+ /**
+ * Title of disc.
+ *
+ * @return Disc title or null string.
+ */
+ const QString &discTitle() const { return m_title; }
+
+ /**
+ * Length of disc.
+ *
+ * @return Disc length in milliseconds.
+ */
+ unsigned discLength() const;
+
+ /**
+ * Position in disc.
+ *
+ * @return Position in milliseconds.
+ */
+ unsigned discPosition() const;
+ /**
+ * Artist of current track.
+ *
+ * @return Track artist or null string.
+ */
+ const QString &trackArtist() const;
+
+ /**
+ * Artist of given track.
+ *
+ * @return Track artist or null string.
+ */
+ const QString &trackArtist(unsigned track) const;
+
+ /**
+ * Title of current track.
+ *
+ * @return Track title or null string.
+ */
+ const QString &trackTitle() const;
+
+ /**
+ * Title of given track.
+ *
+ * @return Track title or null string.
+ */
+ const QString &trackTitle(unsigned track) const;
+
+ /**
+ * Current track.
+ *
+ * @return Track number.
+ */
+ unsigned track() const;
+
+ /**
+ * Number of tracks.
+ */
+ unsigned tracks() const;
+
+ /**
+ * @return if the track is actually an audio track.
+ */
+ bool isAudio(unsigned track) const;
+
+ /**
+ * Length of current track.
+ *
+ * @return Track length in milliseconds.
+ */
+ unsigned trackLength() const;
+
+ /**
+ * Length of given track.
+ *
+ * @param track Track number.
+ * @return Track length in milliseconds.
+ */
+ unsigned trackLength(unsigned track) const;
+
+ /**
+ * Position in current track.
+ *
+ * @return Position in milliseconds.
+ */
+ unsigned trackPosition() const;
+
+ bool isPaused() const;
+
+ bool isPlaying() const;
+
+signals:
+
+ /**
+ * A disc is being inserted.
+ */
+ void trayClosing();
+
+ /**
+ * A disc is being removed.
+ */
+ void trayOpening();
+
+ /**
+ * A disc was inserted or removed.
+ *
+ * @param discId Current disc, missingDisc if no disc.
+ */
+ void discChanged(unsigned discId);
+
+ /**
+ * Disc stopped. See @see #trackPaused.
+ */
+ void discStopped();
+
+ /**
+ * The current track changed.
+ *
+ * @param track Track number.
+ * @param trackLength Length within track in milliseconds.
+ */
+ void trackChanged(unsigned track, unsigned trackLength);
+
+ /**
+ * A track started playing, or is still playing. This signal is delivered at
+ * approximately 1 second intervals while a track is playing. At first sight,
+ * this might seem overzealous, but its likely that any CD player UI will use
+ * this to track the second-by-second position, so we may as well do it for
+ * them.
+ *
+ * @param track Track number.
+ * @param trackPosition Position within track in milliseconds.
+ */
+ void trackPlaying(unsigned track, unsigned trackPosition);
+
+ /**
+ * A track paused playing.
+ *
+ * @param track Track number.
+ * @param trackPosition Position within track in milliseconds.
+ */
+ void trackPaused(unsigned track, unsigned trackPosition);
+
+private:
+ QTimer timer;
+ QString m_device;
+ int m_status;
+ int m_previousStatus;
+ unsigned m_discId;
+ unsigned m_previousDiscId;
+ QString m_artist;
+ QString m_title;
+ unsigned m_tracks;
+ QValueList<unsigned> m_trackStartFrames;
+ QStringList m_trackArtists;
+ QStringList m_trackTitles;
+ unsigned m_track;
+ unsigned m_previousTrack;
+ void checkDeviceStatus();
+ QString discStatus(int status);
+ class KCompactDiscPrivate *d;
+ InformationMode m_infoMode;
+
+private slots:
+ void timerExpired();
+};
+
+#endif
diff --git a/kscd/kscd-script b/kscd/kscd-script
new file mode 100755
index 00000000..8e54bc9e
--- /dev/null
+++ b/kscd/kscd-script
@@ -0,0 +1,782 @@
+#!/bin/sh
+#
+# Kscd - A simple cd player for the KDE Project
+#
+# $Id$
+#
+# Copyright (c) 1997 Bernd Johannes Wuebben math.cornell.edu
+#
+# 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, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+#
+
+# the following is borrowed from configure
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+ # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+ if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+ ac_n= ac_c='
+' ac_t=' '
+ else
+ ac_n=-n ac_c= ac_t=
+ fi
+else
+ ac_n= ac_c='\c' ac_t=
+fi
+
+config_file="config.h"
+ORG_CONFIG="config.h.std"
+
+display_main_menu(){
+
+clear
+echo
+echo " KSCD -- Configuration"
+echo "==================================================================="
+echo
+echo " Linux ...................... L"
+echo " FreeBSD/NetBSD ............. F"
+echo " BSD386 ..................... B"
+echo " Sun ........................ S"
+echo " HPUX ....................... H"
+echo " Irix ....................... I"
+echo " Sony NEWS .................. N"
+echo " OSF/1 ...................... O"
+echo " Ultrix ..................... U"
+echo " Generic SVR4 (not Sun) ..... V"
+echo
+echo " Quit ....................... Q"
+echo
+echo
+echo " Bernd Johannes Wuebben <wuebben@kde.org>"
+echo "=================================================================="
+echo
+echo $ac_n " Please chose acccording to your platform:$ac_c"
+
+read platform
+
+}
+
+give_instructions(){
+
+echo "Please answer all questions. Simply hit [Enter] to select the default"
+echo "values which are displayed in square brackets."
+echo
+
+}
+
+write_config_header(){
+
+echo "" >> $config_file
+echo "/* This header file was automatically generated by the kscd" >> $config_file
+echo " installation script. In case of trouble please edit" >> $config_file
+echo " $ORG_CONFIG in the source directory of the kscd distribution" >> $config_file
+echo " and rename it to $config_file .*/" >> $config_file
+echo "" >> $config_file
+
+}
+
+linux_conf(){
+
+clear
+echo "KSCD configuration for Linux systems"
+echo "------------------------------------"
+echo
+
+give_instructions
+write_config_header
+
+echo "#ifdef linux " >> $config_file
+echo "" >> $config_file
+
+echo $ac_n "Please specify your cdrom device [/dev/cdrom]:$ac_c"
+
+read device
+if [ "$device" = "" ]
+then
+ device="/dev/cdrom"
+fi
+
+echo "Your choice was: $device"
+echo "#define DEFAULT_CD_DEVICE \"$device\" " >> $config_file
+
+
+
+echo
+echo "I have made modifications to the workman engine which cause kscd to be"
+echo "a very fast and responsive cd player for an average IDE CDROM drive."
+echo "Answer NO to the next question unless you have problems getting kscd"
+echo "to work."
+echo
+echo $ac_n "Revert to original Workman behavior? (y/n) [n]:$ac_c"
+
+read workman
+
+case "$workman" in
+ "yes" | "y" | "Yes" | "Y") echo "#define WORKMAN_ORIGINAL " >> $config_file ;;
+esac
+
+
+echo
+echo "I have incorporated modifications from Dirk (milliByte@DeathsDoor.com)"
+echo "which will make kscd perform better on any drive using the sbpcd or"
+echo "mcdx drivers. If and only if your kernel uses one of these cd drivers you"
+echo "should try answering YES to the next question. Please peruse the "
+echo "documents in the spbcd directory of this distribution for more "
+echo "information about theses modifications."
+echo
+echo $ac_n "Enable SBPCD_HACK? (y/n) [n]:$ac_c"
+
+read sbpcd
+
+case "$sbpcd" in
+ "yes" | "y" | "Yes" | "Y") echo "#define SBPCD_HACK " >> $config_file ;;
+esac
+
+
+echo
+echo "If and only if you have an SCSI cdrom drive you should probably"
+echo "answer YES to the next question"
+echo
+echo $ac_n "Enable LINUX_SCSI_PASSTHROUGH? (y/n) [n]:$ac_c"
+
+read scsi
+
+case "$scsi" in
+ "yes" | "y" | "Yes" | "Y") echo "#define LINUX_SCSI_PASSTHROUGH " >> $config_file ;;
+esac
+
+echo
+echo "Most cdrom devices have a volume range from 0 to 255."
+echo "Feel free to play with the parameters MINVOLUME and MAXVOLUME"
+echo "until volume adjustment works to your satisfaction."
+echo "Note: Some CDROM device only support a range from 128 to 255."
+echo
+echo $ac_n "MIN_VOLUME [0]:$ac_c"
+
+read MINVOLUME
+if [ "$MINVOLUME" = "" ]
+then
+ MINVOLUME="0"
+fi
+
+echo "You entered $MINVOLUME "
+echo "#define MIN_VOLUME $MINVOLUME " >> $config_file
+
+echo
+echo $ac_n "MAX_VOLUME [255]:$ac_c"
+
+read MAXVOLUME
+if [ "$MAXVOLUME" = "" ]
+then
+ MAXVOLUME="255"
+fi
+
+echo "You entered $MAXVOLUME "
+echo "#define MAX_VOLUME $MAXVOLUME " >> $config_file
+
+echo
+echo "If you own one a cdrom player whose volume range is 128 to 255,"
+echo "you might want to try answering YES to the next question."
+echo "Enabling 'Curved Volume' will give you a square root like"
+echo "volume slider - volume curve."
+echo
+echo $ac_n "Enable CURVED_VOLUME? (y/n) [n]:$ac_c"
+
+read curved
+
+case "$curved" in
+ "yes" | "y" | "Yes" | "Y") echo "#define CURVED_VOLUME " >> $config_file ;;
+esac
+
+echo "" >> $config_file
+echo "#endif /* linux */" >> $config_file
+
+}
+
+irix_conf(){
+
+clear
+echo "KSCD configuration for Irix"
+echo "---------------------------"
+echo
+
+give_instructions
+write_config_header
+
+echo "" >> $config_file
+echo "#if defined(sgi)" >> $config_file
+echo "" >> $config_file
+
+echo $ac_n "Please specify your cdrom device [/dev/scsi/sc0d6l0]:$ac_c"
+
+read device
+if [ "$device" = "" ]
+then
+ device="/dev/scsi/sc0d6l0"
+fi
+
+echo "Your choice was: $device"
+echo "#define DEFAULT_CD_DEVICE \"$device\" " >> $config_file
+
+echo $ac_n "Enable CDDA? (y/n) [y]:$ac_c"
+
+read curved
+
+case "$curved" in
+ "yes" | "y" | "Yes" | "Y" | "") echo "#define CDDA " >> $config_file ;;
+esac
+echo ""
+echo "That's all for sgi ..."
+
+echo "#endif /* sgi */" >> $config_file
+
+}
+
+hp_conf(){
+
+clear
+echo "KSCD configuration for HPUX systems"
+echo "-----------------------------------"
+echo
+
+give_instructions
+write_config_header
+
+echo "" >> $config_file
+echo "#if defined(hpux) || defined (__hpux)" >> $config_file
+echo "" >> $config_file
+
+echo $ac_n "Please specify your cdrom device [/dev/rscsi]:$ac_c"
+
+read device
+if [ "$device" = "" ]
+then
+ device="/dev/rcsci"
+fi
+
+echo "Your choice was: $device"
+echo "#define DEFAULT_CD_DEVICE \"$device\" " >> $config_file
+echo ""
+echo "That's all for HPUX ..."
+
+echo "#endif /* hpux */" >> $config_file
+
+
+}
+
+bsd386_conf(){
+
+clear
+echo "KSCD configuration for BSD386 systems"
+echo "-------------------------------------"
+echo
+
+give_instructions
+write_config_header
+
+echo "#ifdef __bsdi__ " >> $config_file
+
+echo
+echo "Please answer YES to the next question if you have a Sound Blaster cdrom."
+echo
+echo $ac_n "Define SOUNDBALSTER? (y/n) [n]:$ac_c"
+
+read soundblaster
+
+case "$soundblaster" in
+ "yes" | "y" | "Yes" | "Y") echo "#define SOUNDBLASTER " >> $config_file ;;
+esac
+
+echo "#endif /* __bsdi__ */" >> $config_file
+
+}
+
+freebsd_netbsd_conf(){
+
+clear
+echo "KSCD configuration for FreeBSD/NetBSD systems"
+echo "---------------------------------------------"
+echo
+
+give_instructions
+write_config_header
+
+echo "" >> $config_file
+echo "#if defined(__FreeBSD__) || defined(__NetBSD__)" >> $config_file
+echo "" >> $config_file
+
+# get default device
+if [ `/usr/bin/uname -s` = "NetBSD" ]; then
+ if [ `/usr/bin/uname -m` = "i386" ]; then
+ def_device=/dev/rcd0d
+ else
+ def_device=/dev/rcd0c
+ fi
+else
+ # FreeBSD
+ def_device=/dev/acd0c
+fi
+
+echo $ac_n "Please specify your cdrom device [$def_device]:$ac_c"
+
+read device
+if [ "$device" = "" ]
+then
+ device="$def_device"
+fi
+
+echo "Your choice was:$device"
+echo "#define DEFAULT_CD_DEVICE \"$device\" " >> $config_file
+
+echo
+echo "Some experimental changes to the FreeBSD code were made."
+echo "Please let me know whether answering yes or no to the next"
+echo "question will give you a better functioning cd player."
+echo "Users of NetBSD might want to experiment with this too."
+echo
+echo $ac_n "Define NEW_BSD_PLAYCLICKED? (y/n) [y]:$ac_c"
+
+read free_play_clicked
+
+case "$free_play_clicked" in
+ "yes" | "y" | "Yes" | "Y"| "") echo "#define NEW_BSD_PLAYCLICKED" >> $config_file ;;
+esac
+
+echo "" >> $config_file
+echo "#endif /* FreeBSD/NetBSD */" >> $config_file
+
+}
+
+sun_conf(){
+
+clear
+echo "KSCD configuration for Sun systems"
+echo "----------------------------------"
+echo
+
+give_instructions
+write_config_header
+
+echo "" >> $config_file
+echo "#if defined(sun) || defined(__sun__) " >> $config_file
+echo "" >> $config_file
+
+echo $ac_n "Are you compiling on Solaris 2.x? (y/n) [y]:$ac_c"
+read solaris
+
+case "$solaris" in
+
+ n | N)
+ echo
+ ;;
+ *)
+ echo "#define SYSV" >> $config_file
+ solaris="y"
+ ;;
+esac
+
+echo ""
+echo "You need to enable the internal audio device if you wish"
+echo "to get sound from your workstation, as opposed to only from"
+echo "the head-phone jack of your CDROM."
+echo ""
+echo $ac_n "Activate internal audio device (CODEC)? (y/n) [y]:$ac_c"
+
+read codec
+
+case "$code" in
+
+ n | N)
+ ;;
+ *)
+ echo "#define CODEC" >> $config_file
+ ;;
+esac
+
+if [ "$solaris" = "y" ]
+then
+ echo
+ echo "Do you wish to enable digital audio capablilites on drives that"
+ echo $ac_n "support it? (y/n) [y]:$ac_c"
+ read cdda
+
+ case "$cdda" in
+
+ n | N)
+ ;;
+ *)
+ echo "#define BUILD_CDDA" >> $config_file
+ ;;
+ esac
+fi
+
+
+echo
+echo "Most cdrom devices have a volume range from 0 to 255."
+echo "Feel free to play with the parameters MINVOLUME and MAXVOLUME"
+echo "until volume adjustment works to your satisfaction."
+echo "Note: Some CDROM device only support a range from 128 to 255."
+echo
+echo $ac_n "MIN_VOLUME [128]:$ac_c"
+
+read MINVOLUME
+if [ "$MINVOLUME" = "" ]
+then
+ MINVOLUME="128"
+fi
+
+echo "You entered $MINVOLUME "
+echo "#define MIN_VOLUME $MINVOLUME " >> $config_file
+
+echo
+echo $ac_n "MAX_VOLUME [255]:$ac_c"
+
+read MAXVOLUME
+if [ "$MAXVOLUME" = "" ]
+then
+ MAXVOLUME="255"
+fi
+
+echo "You entered $MAXVOLUME "
+echo "#define MAX_VOLUME $MAXVOLUME " >> $config_file
+
+echo
+echo "If you own one a cdrom player whose volume range is 128 to 255,"
+echo "you might want to try answering YES to the next question."
+echo "Enabling 'Curved Volume' will give you a square root like"
+echo "volume slider - volume curve."
+echo
+echo $ac_n "Enable CURVED_VOLUME? (y/n) [n]:$ac_c"
+
+read curved
+
+case "$curved" in
+ "yes" | "y" | "Yes" | "Y") echo "#define CURVED_VOLUME " >> $config_file ;;
+esac
+
+echo "#endif /* Sun*/" >> $config_file
+
+}
+
+sony_conf(){
+
+clear
+echo "KSCD configuration for Sony NEWS systems"
+echo "---------------------------------------------"
+echo
+
+give_instructions
+write_config_header
+
+echo "" >> $config_file
+echo "#if defined(__sony_news) || defined(sony_news)" >> $config_file
+echo "" >> $config_file
+
+echo "Unfortunately, I have conflicting reports:"
+echo "You might need a,b or c below."
+echo "Please let me know what is required on Sony NEWS."
+echo
+echo "a) #include <CD.h> "
+echo
+echo "b) #include <newsiodev/scu.h>"
+echo " #include <newsiodev/scsireg.h>"
+echo
+echo "c) both of the above"
+echo ""
+echo $ac_n "Please choose a,b or c. [c]:$ac_c"
+
+read sony_include
+
+case "$sony_include" in
+ "a" | "A" )
+ echo "Your choice was a"
+ echo "#include <CD.h>" >> $config_file
+ ;;
+ "b" | "B" )
+ echo "Your choice was b"
+ echo "#include <newsiodev/scu.h>" >> $config_file
+ echo "#include <newsiodev/scsireg.h>" >> $config_file
+ ;;
+ * )
+ echo "Your choice was c"
+ echo "#include <CD.h>" >> $config_file
+ echo "#include <newsiodev/scu.h>" >> $config_file
+ echo "#include <newsiodev/scsireg.h>" >> $config_file
+ ;;
+esac
+
+echo
+echo $ac_n "Please specify your cdrom device [/dev/rsd/b0i6u0p2\0]:$ac_c"
+
+read device
+if [ "$device" = "" ]
+then
+ device="/dev/rsd/b0i6u0p2\0"
+fi
+
+echo "Your choice was:$device"
+echo "#define DEFAULT_CD_DEVICE \"$device\" " >> $config_file
+
+echo
+echo "While most CDROM devices have a volume range from 0 to 255,"
+echo "it appears that 128 to 255 is more appropriate for Sony NEWS."
+echo "Feel free to play with the parameters MINVOLUME and MAXVOLUME"
+echo "until volume adjustment works to your satisfaction."
+echo "Don't forget to let me know the result of your experiments."
+echo
+echo $ac_n "MIN_VOLUME [128]:$ac_c"
+
+read MINVOLUME
+if [ "$MINVOLUME" = "" ]
+then
+ MINVOLUME="128"
+fi
+
+echo "You entered $MINVOLUME "
+echo "#define MIN_VOLUME $MINVOLUME " >> $config_file
+
+echo
+echo $ac_n "MAX_VOLUME [255]:$ac_c"
+
+read MAXVOLUME
+if [ "$MAXVOLUME" = "" ]
+then
+ MAXVOLUME="255"
+fi
+
+echo "You entered $MAXVOLUME "
+echo "#define MAX_VOLUME $MAXVOLUME " >> $config_file
+
+echo "" >> $config_file
+echo "#endif /* sony_news */" >> $config_file
+
+}
+
+ultrix_conf(){
+
+clear
+echo "KSCD configuration for Ultrix systems"
+echo "---------------------------------------------"
+echo
+
+give_instructions
+write_config_header
+
+echo "" >> $config_file
+echo "#if defined(ultrix) || defined(__ultrix)" >> $config_file
+echo "" >> $config_file
+
+echo "Unfortunately, I have conflicting reports:"
+echo "You might need a,b or c below."
+echo "Please let me know what is required on Ultrix."
+echo
+echo "a) #include <sys/rzdisk.h>"
+echo " #include <sys/cdrom.h>"
+echo
+echo "b) #include <sys/devio.h>"
+echo " #include <io/cam/cam.h>"
+echo " #include <io/cam/uagt.h>"
+echo " #include <io/cam/dec_cam.h>"
+echo " #include <io/cam/scsi_all.h>"
+echo ""
+echo "c) both of the above"
+echo ""
+echo $ac_n "Please choose a,b or c. [a]:$ac_c"
+
+read ultrix_include
+
+case "$ultrix_include" in
+ "c" | "C" )
+ echo "Your choice was c)"
+ echo ""
+ echo "#include <sys/rzdisk.h>" >> $config_file
+ echo "#include <sys/cdrom.h>">> $config_file
+ echo ""
+ echo "#include <sys/devio.h>" >> $config_file
+ echo "#include <io/cam/cam.h>" >> $config_file
+ echo "#include <io/cam/uagt.h>" >> $config_file
+ echo "#include <io/cam/dec_cam.h>" >> $config_file
+ echo "#include <io/cam/scsi_all.h>" >> $config_file
+ echo "" >> $config_file
+ ;;
+
+ "b" | "B" )
+ echo "Your choice was b"
+ echo "#include <sys/devio.h>" >> $config_file
+ echo "#include <io/cam/cam.h>" >> $config_file
+ echo "#include <io/cam/uagt.h>" >> $config_file
+ echo "#include <io/cam/dec_cam.h>" >> $config_file
+ echo "#include <io/cam/scsi_all.h>" >> $config_file
+ ;;
+ * )
+ echo "Your choice was a"
+ echo "#include <sys/rzdisk.h>" >> $config_file
+ echo "#include <sys/cdrom.h>">> $config_file
+ ;;
+esac
+
+echo
+echo "While most CDROM devices have a volume range from 0 to 255,"
+echo "it appears that 128 to 255 is more appropriate for Ultrix"
+echo "Feel free to play with the parameters MINVOLUME and MAXVOLUME"
+echo "until volume adjustment works to your satisfaction."
+echo "Don't forget to let me know the result of your experiments."
+echo
+echo $ac_n "MIN_VOLUME [128]:$ac_c"
+
+read MINVOLUME
+if [ "$MINVOLUME" = "" ]
+then
+ MINVOLUME="128"
+fi
+
+echo "You entered $MINVOLUME "
+echo "#define MIN_VOLUME $MINVOLUME " >> $config_file
+
+echo
+echo $ac_n "MAX_VOLUME [255]:$ac_c"
+
+read MAXVOLUME
+if [ "$MAXVOLUME" = "" ]
+then
+ MAXVOLUME="255"
+fi
+
+echo "You entered $MAXVOLUME "
+echo "#define MAX_VOLUME $MAXVOLUME " >> $config_file
+
+echo "" >> $config_file
+echo "#endif /* ultrix */" >> $config_file
+
+}
+
+osf_conf(){
+
+clear
+echo "KSCD configuration for OSF systems"
+echo "----------------------------------"
+echo
+
+give_instructions
+write_config_header
+
+echo "" >> config_file
+echo "#if defined(__osf__)" >> $config_file
+
+echo "Nothing to be done for OSF. The CD device should be automatically detected."
+echo "If you have trouble look at plat_osf1.c and let me know what needs to be"
+echo "changed."
+
+echo ""
+echo $ac_n "Press [Enter] to continue.$ac_c"
+read dummy
+
+echo "" >> $config_file
+echo "#endif /* osf */" >> $config_file
+echo "" >> $config_file
+
+#endif
+
+
+}
+
+generic_svr_conf(){
+
+clear
+echo "KSCD configuration for a generic SVR4 system"
+echo "--------------------------------------------"
+echo
+
+give_instructions
+write_config_header
+
+echo "" >> $config_file
+echo "#if defined(SVR4) && !defined(sun) && !defined(__sun__)" >> $config_file
+
+echo $ac_n "Please specify your cdrom device [/dev/rcdrom/cd0]:$ac_c"
+
+read device
+if [ "$device" = "" ]
+then
+ device="/dev/rcdrom/cd0"
+fi
+
+echo "Your choice was:$device"
+echo "#define DEFAULT_CD_DEVICE \"$device\" " >> $config_file
+
+echo "" >> $config_file
+echo "#endif /* SVR4 */" >> $config_file
+echo "" >> $config_file
+
+}
+
+########################################################################
+#
+# main
+#
+########################################################################
+
+if [ -f $config_file ]
+then
+ echo "Found old config file. Will clean up first ...."
+ rm $config_file
+ sleep 1
+fi
+
+display_main_menu
+
+if [ "$platform" = "q" ]
+then
+ clear
+ echo "Good bye!"
+ exit 1
+fi
+
+case "$platform" in
+l|L) linux_conf;;
+f|F) freebsd_netbsd_conf;;
+b|B) bsd386_conf;;
+s|S) sun_conf;;
+h|H) hp_conf;;
+i|I) irix_conf;;
+n|N) sony_conf;;
+o|O) osf_conf;;
+u|U) ultrix_conf;;
+v|V) generic_svr_conf;;
+*) linux_conf;;
+esac
+
+sleep 1
+clear
+echo ""
+echo "I have written your configuration choices to $config_file."
+echo "Should you have trouble with the configuration file this"
+echo "script generates, you can rename $ORG_CONFIG to $config_file"
+echo "and edit it manually to suit your configuration."
+echo ""
+echo "Have fun with kscd!"
+echo ""
+echo "Bernd Johannes Wuebben"
+echo "wuebben@kde.org"
+echo "wuebben@math.cornell.edu"
+echo ""
+echo "Press [Enter] to start the compilation."
+read key
+exit
+
+
+
+
+
+
diff --git a/kscd/kscd.cpp b/kscd/kscd.cpp
new file mode 100644
index 00000000..b462eb2a
--- /dev/null
+++ b/kscd/kscd.cpp
@@ -0,0 +1,1677 @@
+/*
+ * Kscd - A simple cd player for the KDE Project
+ *
+ * Copyright (c) 1997 Bernd Johannes wuebben@math.cornell.edu
+ * Copyright (c) 2002-2003 Aaron J. Seigo <aseigo@kde.org>
+ * Copyright (c) 2004 Alexander Kern <alex.kern@gmx.de>
+ * Copyright (c) 2003-2006 Richard Lärkäng <nouseforaname@home.se>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <qdir.h>
+#include <qregexp.h>
+#include <qtextstream.h>
+#include <qlayout.h>
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qapplication.h>
+#include <qgroupbox.h>
+#include <qsqlpropertymap.h>
+
+#include <dcopclient.h>
+#include <kaboutdata.h>
+#include <kaccel.h>
+#include <kaction.h>
+#include <dcopref.h>
+#include <kcharsets.h>
+#include <kcmdlineargs.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kdialogbase.h>
+#include <kemailsettings.h>
+#include <kglobal.h>
+#include <khelpmenu.h>
+#include <kkeydialog.h>
+#include <kiconloader.h>
+#include <kinputdialog.h>
+#include <klocale.h>
+#include <kmainwindow.h>
+#include <kmessagebox.h>
+#include <kpopupmenu.h>
+#include <kprotocolmanager.h>
+#include <krun.h>
+#include <kstandarddirs.h>
+#include <kstdaction.h>
+#include <kstringhandler.h>
+#include <kurl.h>
+#include <kuniqueapplication.h>
+#include <kglobalsettings.h>
+#include <kcmoduleloader.h>
+#include <kconfigdialog.h>
+
+#include "docking.h"
+#include "kscd.h"
+#include "version.h"
+#include "prefs.h"
+
+#include <kwin.h>
+#include <netwm.h>
+#include <stdlib.h>
+
+#include <config.h>
+
+#include "cddbdlg.h"
+#include "configWidget.h"
+#include <qtextcodec.h>
+#include <kcompactdisc.h>
+#include <fixx11h.h>
+
+static const char description[] = I18N_NOOP("KDE CD player");
+
+bool stoppedByUser = false;
+
+
+/****************************************************************************
+ The GUI part
+*****************************************************************************/
+
+KSCD::KSCD( QWidget *parent, const char *name )
+ : DCOPObject("CDPlayer"),
+ kscdPanelDlg( parent, name, Qt::WDestructiveClose ),
+ configDialog(0L),
+ cddialog(0L), //!!!!
+ jumpToTrack(0L),
+ updateTime(true),
+ m_dockWidget(0)
+{
+ m_cd = new KCompactDisc();
+ cddbInfo.clear(); // The first freedb revision is "0" //!!!!
+ random_current = random_list.begin();
+
+ cddb = new KCDDB::Client();
+ connect(cddb, SIGNAL(finished(CDDB::Result)), this, SLOT(lookupCDDBDone(CDDB::Result)));
+
+#if defined(BUILD_CDDA)
+ audio_systems_list
+ << "arts"
+#if defined(HAVE_ARTS_LIBASOUND2)
+ << "alsa"
+#endif
+#ifdef USE_SUN_AUDIO
+ << "sun"
+#endif
+ ;
+#endif
+
+ readSettings();
+ initFont();
+ drawPanel();
+ setColors();
+
+ // the time slider
+ timeIcon->setPixmap(SmallIcon("player_time"));
+ connect(timeSlider, SIGNAL(sliderPressed()), SLOT(timeSliderPressed()));
+ connect(timeSlider, SIGNAL(sliderReleased()), SLOT(timeSliderReleased()));
+ connect(timeSlider, SIGNAL(sliderMoved(int)), SLOT(timeSliderMoved(int)));
+ connect(timeSlider, SIGNAL(valueChanged(int)), SLOT(jumpToTime(int)));
+
+ // the volume slider
+ volumeIcon->setPixmap(SmallIcon("player_volume"));
+ volumeSlider->setValue(Prefs::volume());
+ QString str;
+ str = QString::fromUtf8( QCString().sprintf(i18n("Vol: %02d%%").utf8(), Prefs::volume()) );
+ volumelabel->setText(str);
+ connect(volumeSlider, SIGNAL(valueChanged(int)), SLOT(volChanged(int)));
+
+ /* FIXME check for return value */
+ setDevicePaths(/*Prefs::cdDevice(), Prefs::audioSystem(), Prefs::audioDevice()*/);
+ connect(m_cd, SIGNAL(trackPlaying(unsigned, unsigned)), this, SLOT(trackUpdate(unsigned, unsigned)));
+ connect(m_cd, SIGNAL(trackPaused(unsigned, unsigned)), this, SLOT(trackUpdate(unsigned, unsigned)));
+ connect(m_cd, SIGNAL(trackChanged(unsigned, unsigned)), this, SLOT(trackChanged(unsigned, unsigned)));
+ connect(m_cd, SIGNAL(discStopped()), this, SLOT(discStopped()));
+ connect(m_cd, SIGNAL(discChanged(unsigned)), this, SLOT(discChanged(unsigned)));
+ connect( &queryledtimer, SIGNAL(timeout()), SLOT(togglequeryled()) );
+ connect( &titlelabeltimer, SIGNAL(timeout()), SLOT(titlelabeltimeout()) );
+ connect( &cycletimer, SIGNAL(timeout()), SLOT(cycletimeout()) );
+ connect( &jumpTrackTimer, SIGNAL(timeout()), SLOT(jumpTracks()) );
+/*
+ these are always connected in base class
+ connect( playPB, SIGNAL(clicked()), SLOT(playClicked()) );
+ connect( nextPB, SIGNAL(clicked()), SLOT(nextClicked()) );
+ connect( prevPB, SIGNAL(clicked()), SLOT(prevClicked()) );
+ connect( stopPB, SIGNAL(clicked()), SLOT(stopClicked()) );
+ connect( ejectPB, SIGNAL(clicked()), SLOT(ejectClicked()) );
+*/
+ connect( repeatPB, SIGNAL(clicked()), SLOT(loopClicked()) );
+ connect( songListCB, SIGNAL(activated(int)), SLOT(trackSelected(int)));
+ connect( shufflePB, SIGNAL(clicked()), SLOT(randomSelected()));
+ connect( cddbPB, SIGNAL(clicked()), SLOT(CDDialogSelected()));
+ connect(kapp, SIGNAL(kdisplayPaletteChanged()), this, SLOT(setColors()));
+ connect(kapp, SIGNAL(iconChanged(int)), this, SLOT(setIcons()));
+ QToolTip::remove(songListCB);
+ QToolTip::add(songListCB, i18n("Track list"));
+
+
+ // set up the actions and keyboard accels
+ m_actions = new KActionCollection(this);
+
+ KAction* action;
+ action = new KAction(i18n("Play/Pause"), Key_P, this, SLOT(playClicked()), m_actions, "Play/Pause");
+ action = new KAction(i18n("Stop"), Key_S, this, SLOT(stopClicked()), m_actions, "Stop");
+ action = new KAction(i18n("Previous"), Key_B, this, SLOT(prevClicked()), m_actions, "Previous");
+ action = new KAction(i18n("Next"), Key_N, this, SLOT(nextClicked()), m_actions, "Next");
+ action = KStdAction::quit(this, SLOT(quitClicked()), m_actions);
+ action = KStdAction::keyBindings(this, SLOT(configureKeys()), m_actions, "options_configure_shortcuts");
+ action = KStdAction::keyBindings(this, SLOT(configureGlobalKeys()), m_actions, "options_configure_globals");
+ action = KStdAction::preferences(this, SLOT(showConfig()), m_actions);
+ action = new KAction(i18n("Loop"), Key_L, this, SLOT(loopClicked()), m_actions, "Loop");
+ action = new KAction(i18n("Eject"), CTRL + Key_E, this, SLOT(ejectClicked()), m_actions, "Eject");
+ action = new KAction(i18n("Increase Volume"), Key_Plus, this, SLOT(incVolume()), m_actions, "IncVolume");
+ KShortcut increaseVolume = action->shortcut();
+ increaseVolume.append( KKey( Key_Equal ) );
+ action->setShortcut( increaseVolume );
+ action = new KAction(i18n("Decrease Volume"), Key_Minus, this, SLOT(decVolume()), m_actions, "DecVolume");
+ action = new KAction(i18n("Options"), CTRL + Key_T, this, SLOT(showConfig()), m_actions, "Options");
+ action = new KAction(i18n("Shuffle"), Key_R, this, SLOT(randomSelected()), m_actions, "Shuffle");
+ action = new KAction(i18n("CDDB"), CTRL + Key_D, this, SLOT(CDDialogSelected()), m_actions, "CDDB");
+
+ m_actions->readShortcutSettings("Shortcuts");
+
+ m_actions->action( "options_configure_globals" )->setText( i18n( "Configure &Global Shortcuts..." ) );
+
+ kapp->installKDEPropertyMap();
+ QSqlPropertyMap *map = QSqlPropertyMap::defaultMap();
+ map->insert("KComboBox", "currentText");
+
+ initGlobalShortcuts();
+
+ setupPopups();
+
+ if (Prefs::looping())
+ {
+ loopled->on();
+ loopled->show();
+ repeatPB->setOn(true);
+ }
+
+ setDocking(Prefs::docking());
+
+ setFocusPolicy(QWidget::NoFocus);
+
+ songListCB->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
+ adjustSize();
+ setFixedHeight(this->height());
+} // KSCD
+
+
+KSCD::~KSCD()
+{
+ delete cddb;
+ delete m_cd;
+} // ~KSCD
+
+
+void KSCD::initGlobalShortcuts() {
+
+ m_globalAccel = new KGlobalAccel( this );
+
+ //Definition of global shortcuts is based on 'local' shortcuts which follow
+ //the WIN key.
+ m_globalAccel->insert("Next", i18n("Next"), 0, KKey("WIN+N"), KKey("WIN+Right"),
+ this, SLOT(nextClicked()));
+ //NOTE: WIN+B collidates with amarok's default global shortcut.
+ m_globalAccel->insert("Previous", i18n("Previous"), 0, KKey("WIN+B"), KKey("WIN+Left"),
+ this, SLOT(prevClicked()));
+ m_globalAccel->insert("Play/Pause", i18n("Play/Pause"), 0, KKey("WIN+P"), 0,
+ this, SLOT(playClicked()));
+ m_globalAccel->insert("Stop", i18n("Stop"), 0, KKey("WIN+S"), 0,
+ this, SLOT(stopClicked()));
+ m_globalAccel->insert("IncVolume", i18n("Increase Volume"), 0, KKey("WIN+Plus"), KKey("WIN+Up"),
+ this, SLOT(incVolume()));
+ m_globalAccel->insert("DecVolume", i18n("Decrease Volume"), 0, KKey("WIN+Minus"), KKey("WIN+Down"),
+ this, SLOT(decVolume()));
+ m_globalAccel->insert("Shuffle", i18n("Shuffle"), 0, KKey("WIN+R"), 0,
+ this, SLOT(incVolume()));
+
+ m_globalAccel->setConfigGroup( "GlobalShortcuts" );
+ m_globalAccel->readSettings( kapp->config() );
+ m_globalAccel->updateConnections();
+}
+
+bool KSCD::digitalPlayback() {
+#if defined(BUILD_CDDA)
+ return !(Prefs::audioSystem().isEmpty());
+#else
+ return false;
+#endif
+}
+
+void KSCD::setVolume(int v)
+{
+ volChanged(v);
+ volumeSlider->setValue(v);
+}
+
+void KSCD::setDevice(const QString& dev)
+{
+ Prefs::self()->setCdDevice(dev);
+ setDevicePaths();
+}
+
+/**
+ * Initialize smallfont which fits into the 13 and 14 pixel widgets.
+ */
+void KSCD::initFont()
+{
+/* int theSmallPtSize = 10;
+
+ // Find a font that fits the 13 and 14 pixel widgets
+ QFont fn( KGlobalSettings::generalFont().family(), theSmallPtSize, QFont::Bold );
+ bool fits = false;
+ while (!fits && theSmallPtSize > 1)
+ {
+ QFontMetrics metrics(fn);
+ if(metrics.height() > 13)
+ {
+ --theSmallPtSize;
+ fn.setPointSize(theSmallPtSize);
+ } else {
+ fits = true;
+ }
+ }
+ smallfont = QFont(KGlobalSettings::generalFont().family(), theSmallPtSize, QFont::Bold);
+*/
+} // initFont()
+
+/**
+ * drawPanel() constructs KSCD's little black LED area
+ * all settings are made via panel.ui
+ */
+void KSCD::drawPanel()
+{
+ setIcons();
+ adjustSize();
+
+ const int D = 6;
+ for (int u = 0; u < 5; u++) {
+ trackTimeLED[u] = new BW_LED_Number(frameleds);
+ trackTimeLED[u]->setLEDoffColor(Prefs::backColor());
+ trackTimeLED[u]->setLEDColor(Prefs::ledColor(), Prefs::backColor());
+ trackTimeLED[u]->setGeometry(2 + u * 18, D, 23, 30);
+ connect(trackTimeLED[u], SIGNAL(clicked()), this, SLOT(cycleplaytimemode()));
+ }
+
+ setLEDs(-1);
+
+ queryled = new LedLamp(symbols);
+ queryled->move(+10, D + 1);
+ queryled->off();
+ queryled->hide();
+
+ loopled = new LedLamp(symbols, LedLamp::Loop);
+ loopled->move(+10, D + 18);
+ loopled->off();
+
+ totaltimelabel->hide();
+} // drawPanel
+
+void KSCD::setIcons()
+{
+ playPB->setIconSet(SmallIconSet("player_play"));
+ stopPB->setIconSet(SmallIconSet("player_stop"));
+ ejectPB->setIconSet(SmallIconSet("player_eject"));
+ prevPB->setIconSet(SmallIconSet("player_start"));
+ nextPB->setIconSet(SmallIconSet("player_end"));
+ cddbPB->setIconSet(SmallIconSet("view_text"));
+ infoPB->setIconSet(SmallIconSet("run"));
+}
+
+void KSCD::setupPopups()
+{
+ QPopupMenu* mainPopup = new QPopupMenu(this);
+ infoPB->setPopup(mainPopup);
+ infoPopup = new QPopupMenu (this);
+
+
+ infoPopup->insertItem("MusicMoz", 0);
+ infoPopup->insertItem("Ultimate Bandlist", 1);
+ infoPopup->insertItem("CD Universe", 2);
+ infoPopup->insertSeparator();
+ infoPopup->insertItem("AlltheWeb", 3);
+ infoPopup->insertItem("Altavista", 4);
+ infoPopup->insertItem("Excite", 5);
+ infoPopup->insertItem("Google", 6);
+ infoPopup->insertItem("Google Groups", 7);
+ infoPopup->insertItem("HotBot", 8);
+ infoPopup->insertItem("Lycos", 9);
+ infoPopup->insertItem("Open Directory", 10);
+ infoPopup->insertItem("Yahoo!", 11);
+
+ m_actions->action(KStdAction::name(KStdAction::Preferences))->plug(mainPopup);
+ //NEW add the shortcut dialogs
+ m_actions->action("options_configure_globals")->plug(mainPopup);
+ m_actions->action("options_configure_shortcuts")->plug(mainPopup);
+ mainPopup->insertSeparator();
+
+ mainPopup->insertItem(i18n("Artist Information"), infoPopup);
+
+ connect( infoPopup, SIGNAL(activated(int)), SLOT(information(int)) );
+
+ KHelpMenu* helpMenu = new KHelpMenu(this, KGlobal::instance()->aboutData(), false);
+ mainPopup->insertItem(SmallIcon("help"),i18n("&Help"), helpMenu->menu());
+ mainPopup->insertSeparator();
+ m_actions->action(KStdAction::name(KStdAction::Quit))->plug(mainPopup);
+} // setupPopups
+
+void KSCD::playClicked()
+{
+ if (m_cd->discId() == KCompactDisc::missingDisc)
+ return;
+
+ kapp->processEvents();
+ kapp->flushX();
+
+ if (!m_cd->isPlaying())
+ {
+ kapp->processEvents();
+ kapp->flushX();
+
+ if (m_cd->isPaused())
+ {
+ // Unpause (!!).
+ m_cd->pause();
+ }
+ else
+ {
+ setLEDs(0);
+ resetTimeSlider(true);
+
+ if(Prefs::randomPlay())
+ {
+ make_random_list();
+ // next clicked handles updating the play button, etc.
+ nextClicked();
+ }
+ else
+ {
+ m_cd->play(0, 0, playlist.isEmpty() ? 0 : 1);
+ }
+ }
+
+ // Update UI to allow a subsequent pause.
+ statuslabel->setText(i18n("Play"));
+ playPB->setIconSet(SmallIconSet("player_pause"));
+ playPB->setText(i18n("Pause"));
+ }
+ else
+ {
+ m_cd->pause();
+
+ // Update UI to allow a subsequent play.
+ statuslabel->setText(i18n("Pause"));
+ playPB->setIconSet(SmallIconSet("player_play"));
+ playPB->setText(i18n("Play"));
+ }
+
+ kapp->processEvents();
+ kapp->flushX();
+} // playClicked()
+
+void KSCD::setShuffle(int shuffle)
+{
+ if (shuffle == 2) {
+ if(Prefs::randomPlay() && m_cd->tracks() > 0) {
+ shufflePB->blockSignals(true);
+ shufflePB->setOn(true);
+ shufflePB->blockSignals(false);
+ make_random_list(); /* koz: Build a unique, once, random list */
+ if(m_cd->isPlaying())
+ nextClicked();
+ }
+
+ return;
+ }
+
+ Prefs::setRandomPlay(shuffle);
+ shufflePB->blockSignals(true);
+ shufflePB->setOn(shuffle);
+ shufflePB->blockSignals(false);
+
+ if (Prefs::randomPlay() && m_cd->tracks() > 0) {
+ make_random_list(); /* koz: Build a unique, once, random list */
+ if(m_cd->isPlaying())
+ nextClicked();
+ }
+}
+
+void KSCD::stopClicked()
+{
+ stoppedByUser = true;
+
+ kapp->processEvents();
+ kapp->flushX();
+ m_cd->stop();
+} // stopClicked()
+
+void KSCD::prevClicked()
+{
+ int track = m_cd->track();
+
+ if (Prefs::randomPlay()) {
+ track = prev_randomtrack();
+ if (track == -1) {
+ return;
+ }
+ } else {
+ if (track <= 1) {
+ if (Prefs::looping()) {
+ track = m_cd->tracks();
+ } else {
+ return;
+ }
+ } else {
+ track--;
+ }
+ }
+
+ kapp->processEvents();
+ kapp->flushX();
+ m_cd->play(track, 0, playlist.isEmpty() ? 0 : track);
+} // prevClicked()
+
+bool KSCD::nextClicked()
+{
+ unsigned track = m_cd->track();
+
+ if (Prefs::randomPlay()) {
+ track = next_randomtrack();
+ if(track == 0) {
+ return false;
+ }
+ } else {
+ if(track < 1) {
+ track = 1;
+ } else if (track >= m_cd->tracks()) {
+ if (Prefs::looping()) {
+ track = 1;
+ } else {
+ return true;
+ }
+ } else {
+ track++;
+ }
+ }
+
+ kapp->processEvents();
+ kapp->flushX();
+ m_cd->play(track, 0, Prefs::randomPlay() || !playlist.isEmpty() ? track + 1 : 0);
+ return true;
+} // nextClicked()
+
+void KSCD::trackChanged(unsigned track, unsigned trackLength)
+{
+ QString tooltip = artistlabel->text();
+ if (track < 1)
+ {
+ setLEDs(-1);
+ resetTimeSlider(true);
+ tracklabel->setText("--/--");
+ titlelabel->clear();
+ }
+ else
+ {
+// if (!nextClicked())
+// {
+// statuslabel->setText(i18n("Disc Finished"));
+// m_cd->stop();
+// }
+// break;
+
+ if (songListCB->count())
+ {
+ songListCB->setCurrentItem(track - 1);
+ // drop the number.
+ // for Mahlah, a picky though otherwise wonderful person - AJS
+ QString justTheName = songListCB->currentText();
+ justTheName = justTheName.right(justTheName.length() - 4);
+
+ QToolTip::remove(songListCB);
+ QToolTip::add(songListCB, i18n("Current track: %1").arg(justTheName));
+ }
+ timeSlider->blockSignals(true);
+ timeSlider->setRange(0, trackLength ? trackLength - 1 : 0);
+ timeSlider->blockSignals(false);
+ QString str;
+ str.sprintf("%02d/%02d", track, m_cd->tracks());
+ tracklabel->setText(str);
+
+ QString title = cddbInfo.trackInfoList[track-1].title;
+ titlelabel->setText(title);
+ tooltip += "/";
+ tooltip += KStringHandler::rsqueeze(title, 30);
+ }
+ emit trackChanged(tooltip);
+} //trackChanged(int track)
+
+
+void KSCD::jumpToTime(int ms, bool forcePlay)
+{
+ kapp->processEvents();
+ kapp->flushX();
+
+ int track = m_cd->track();
+ if ((m_cd->isPlaying() || forcePlay) &&
+ ms < (int)m_cd->trackLength())
+ {
+ if(Prefs::randomPlay() || !playlist.isEmpty())
+ {
+ m_cd->play(track, ms, track + 1);
+ }
+ else
+ {
+ m_cd->play(track, ms);
+ }
+ }
+} // jumpToTime(int ms)
+
+void KSCD::timeSliderPressed()
+{
+ updateTime = false;
+} // timeSliderPressed()
+
+void KSCD::timeSliderMoved(int milliseconds)
+{
+ setLEDs(milliseconds);
+} // timeSliderMoved(int seconds)
+
+void KSCD::timeSliderReleased()
+{
+ updateTime = true;
+} // timeSliderReleased()
+
+void KSCD::quitClicked()
+{
+ // ensure nothing else starts happening
+ queryledtimer.stop();
+ titlelabeltimer.stop();
+ cycletimer.stop();
+ jumpTrackTimer.stop();
+
+ writeSettings();
+ //setShuffle(0);
+ statuslabel->clear();
+ setLEDs(-1);
+
+ // Good GOD this is evil
+ kapp->processEvents();
+ kapp->flushX();
+
+ if(Prefs::stopExit())
+ m_cd->stop();
+
+ delete m_cd;
+
+ kapp->quit();
+} // quitClicked()
+
+bool KSCD::event( QEvent *e )
+{
+ return QWidget::event(e);
+} // event
+
+
+void KSCD::loopOn()
+{
+ Prefs::setLooping(true);
+ loopled->on();
+ loopled->show();
+ kapp->processEvents();
+ kapp->flushX();
+} // loopOn;
+
+void KSCD::loopOff()
+{
+ Prefs::setLooping(false);
+ loopled->off();
+ loopled->show();
+ kapp->processEvents();
+ kapp->flushX();
+} // loopOff;
+
+void KSCD::loopClicked()
+{
+ if(Prefs::looping())
+ {
+ loopOff();
+ }
+ else
+ {
+ loopOn();
+ }
+} // loopClicked
+
+/**
+ * Do everything needed if the user requested to eject the disc.
+ *
+ */
+void KSCD::ejectClicked()
+{
+ m_cd->eject();
+} // ejectClicked
+
+void KSCD::closeEvent(QCloseEvent *e)
+{
+ if (Prefs::docking() && !kapp->sessionSaving())
+ {
+ hide();
+ e->ignore();
+ return;
+ }
+ e->accept();
+}
+
+void KSCD::randomSelected()
+{
+ setShuffle(Prefs::randomPlay()?0:1);
+
+ /* FIXME this helps us to display "Random" in Status line
+ should it maybe to be replaced with symbol "RAND" or something others */
+ statuslabel->setText(Prefs::randomPlay()?i18n("Random"):i18n("Play"));
+} // randomSelected
+
+/**
+ * A Track was selected for playback from the drop down box.
+ *
+ */
+void KSCD::trackSelected( int cb_index )
+{
+ if (cb_index < 0)
+ return;
+
+ unsigned int track = cb_index + 1;
+ setShuffle(0);
+
+ m_cd->play(track, 0);
+} // trackSelected
+
+void KSCD::updateConfigDialog(configWidget* widget)
+{
+ if(!widget)
+ return;
+
+ static QString originalTitleOfGroupBox = widget->groupBox3->title();
+ if(m_cd->isPlaying()) {
+ widget->groupBox3->setEnabled(false);
+ widget->groupBox3->setTitle( i18n( "CD Drive (you must stop playing to change this)" ) );
+ } else {
+ widget->groupBox3->setEnabled(true);
+ widget->groupBox3->setTitle(originalTitleOfGroupBox);
+ }
+}
+
+void KSCD::showConfig()
+{
+ static configWidget* confWidget = 0;
+
+ if (KConfigDialog::showDialog("settings")) {
+ updateConfigDialog(confWidget);
+ return;
+ }
+
+ configDialog = new KConfigDialog(this, "settings", Prefs::self());
+
+ configDialog->setHelp(QString::null);
+
+ confWidget = new configWidget(this, 0, "Kscd");
+
+ // kscd config page
+ configDialog->addPage(confWidget, i18n("CD Player"), "kscd", i18n("Settings & Behavior"));
+
+ // libkcddb page
+ KService::Ptr libkcddb = KService::serviceByDesktopName("libkcddb");
+ if (libkcddb && libkcddb->isValid())
+ {
+ KCModuleInfo info(libkcddb->desktopEntryPath());
+ if (info.service()->isValid())
+ {
+ KCModule *m = KCModuleLoader::loadModule(info, KCModuleLoader::Inline);
+ if (m)
+ {
+ m->load();
+ KCDDB::Config* cfg = new KCDDB::Config();
+ cfg->readConfig();
+ configDialog -> addPage(m, cfg, QString("CDDB"), "cdtrack", i18n("Configure Fetching Items"));
+
+ connect(configDialog, SIGNAL(okClicked()), m, SLOT(save()));
+ connect(configDialog, SIGNAL(applyClicked()), m, SLOT(save()));
+ connect(configDialog, SIGNAL(defaultClicked()), m, SLOT(defaults()));
+ }
+ }
+ }
+
+ updateConfigDialog(confWidget);
+
+ connect(configDialog, SIGNAL(settingsChanged()), this, SLOT(configDone()));
+ configDialog -> show();
+} // showConfig()
+
+void KSCD::configDone()
+{
+ setColors();
+ setDocking(Prefs::docking());
+
+ setDevicePaths();
+
+ volumeIcon->setEnabled(!Prefs::digitalPlayback());
+ volumeSlider->setEnabled(!Prefs::digitalPlayback());
+
+ // dialog deletes itself
+ configDialog = 0L;
+}
+
+void KSCD::configureKeys()
+{
+ KKeyDialog::configure(m_actions, this);
+}
+
+void KSCD::configureGlobalKeys()
+{
+ KKeyDialog::configure(m_globalAccel, true, this, true);
+}
+
+void KSCD::setDevicePaths()
+{
+ if (!m_cd->setDevice(Prefs::cdDevice(), Prefs::volume(), Prefs::digitalPlayback(),
+ Prefs::audioSystem(), Prefs::audioDevice()))
+ {
+ // This device did not seem usable.
+ QString str = i18n("CD-ROM read or access error (or no audio disc in drive).\n"\
+ "Please make sure you have access permissions to:\n%1").arg(
+ KCompactDisc::urlToDevice(Prefs::cdDevice()));
+ KMessageBox::error(this, str, i18n("Error"));
+ }
+} // setDevicePath()
+
+void KSCD::setDocking(bool dock)
+{
+ Prefs::setDocking(dock);
+ if (Prefs::docking())
+ {
+ if (!m_dockWidget)
+ {
+ m_dockWidget = new DockWidget(this, "dockw");
+ connect(m_dockWidget, SIGNAL(quitSelected()), this, SLOT(quitClicked()));
+ }
+
+ m_dockWidget->show();
+ connect(this, SIGNAL(trackChanged(const QString&)),
+ m_dockWidget, SLOT(setToolTip(const QString&)));
+ connect(this, SIGNAL(trackChanged(const QString&)),
+ m_dockWidget, SLOT(createPopup(const QString&)));
+ }
+ else
+ {
+ show();
+ delete m_dockWidget;
+ m_dockWidget = 0;
+ }
+}
+
+void KSCD::incVolume()
+{
+ int v = Prefs::volume() + 5;
+
+ if (v > 100)
+ {
+ v = 100;
+ }
+
+ volChanged(v);
+ volumeSlider->setValue(v);
+} // incVolume
+
+void KSCD::decVolume()
+{
+ int v = Prefs::volume() - 5;
+
+ if (v < 0)
+ {
+ v = 0;
+ }
+
+ volChanged(v);
+ volumeSlider->setValue(v);
+} // decVolume
+
+void KSCD::volChanged( int vol )
+{
+ QString str;
+ str = QString::fromUtf8( QCString().sprintf(i18n("Vol: %02d%%").utf8(), vol) );
+ volumelabel->setText(str);
+ m_cd->setVolume(vol);
+ Prefs::setVolume(vol);
+} // volChanged
+
+void KSCD::make_random_list()
+{
+ /* koz: 15/01/00. I want a random list that does not repeat tracks. Ie, */
+ /* a list is created in which each track is listed only once. The tracks */
+ /* are picked off one by one until the end of the list */
+
+ int selected = 0;
+ bool rejected = false;
+
+ //kdDebug(67000) << "Playlist has " << size << " entries\n" << endl;
+ random_list.clear();
+ for(unsigned i = 0; i < m_cd->tracks(); i++)
+ {
+ do {
+ selected = 1 + (int) randSequence.getLong(m_cd->tracks());
+ rejected = (random_list.find(selected) != random_list.end());
+ } while(rejected == true);
+ random_list.append(selected);
+ }
+/*
+ printf("debug: dump random list\n");
+ RandomList::iterator it;
+ for(it = random_list.begin(); it != random_list.end(); it++) {
+ printf("%i ", *it);
+ }
+ printf("\n");
+*/
+ random_current = random_list.end();
+} // make_random_list()
+
+int KSCD::next_randomtrack()
+{
+ /* Check to see if we are at invalid state */
+ if(random_current == random_list.end())
+ {
+ random_current = random_list.begin();
+ }
+ else if(random_current == random_list.fromLast())
+ {
+ if(!Prefs::looping())
+ {
+ m_cd->stop();
+ return 0;
+ }
+ else
+ {
+ // playing the same random list isn't very random, is it?
+ make_random_list();
+ return next_randomtrack();
+ }
+ }
+ else
+ {
+ ++random_current;
+ }
+
+ return *random_current;
+} // next_randomtrack
+
+int KSCD::prev_randomtrack()
+{
+ /* Check to see if we are at invalid state */
+ if(random_current == random_list.end())
+ {
+ random_current = random_list.fromLast();
+ }
+ else if(random_current == random_list.begin())
+ {
+ if(!Prefs::looping())
+ {
+ return -1;
+ }
+ else
+ {
+ // playing the same random list isn't very random, is it?
+ make_random_list();
+ return prev_randomtrack();
+ }
+ }
+ else
+ {
+ --random_current;
+ }
+
+ return *random_current;
+} // prev_randomtrack
+
+void KSCD::discChanged(unsigned discId)
+{
+ cddbInfo.clear();
+ if (discId == KCompactDisc::missingDisc)
+ {
+ statuslabel->setText(i18n("No disc"));
+ }
+ else
+ {
+ cddbInfo.id = QString::number(discId, 16).rightJustify(8,'0');
+ cddbInfo.length = m_cd->discLength() / 1000;
+ cddbInfo.artist = m_cd->discArtist();
+ cddbInfo.title = m_cd->discTitle();
+
+ // If it's a sampler, we'll do artist/title.
+ bool isSampler = (cddbInfo.title.compare("Various") == 0);
+ KCDDB::TrackInfo track;
+ for (unsigned i = 1; i <= m_cd->tracks(); i++)
+ {
+ if (isSampler)
+ {
+ track.title = m_cd->trackArtist(i);
+ track.title.append("/");
+ track.title.append(m_cd->trackTitle(i));
+ }
+ else
+ {
+ track.title = m_cd->trackTitle(i);
+ }
+
+ // FIXME: KDE4
+ // track.length = cd->trk[i - 1].length;
+ cddbInfo.trackInfoList.append(track);
+ }
+ }
+
+ // Set the total time.
+ QTime dml;
+ dml = dml.addSecs(m_cd->discLength() / 1000);
+
+ QString fmt;
+ if(dml.hour() > 0)
+ fmt.sprintf("%02d:%02d:%02d",dml.hour(),dml.minute(),dml.second());
+ else
+ fmt.sprintf("%02d:%02d",dml.minute(),dml.second());
+ totaltimelabel->setText(fmt);
+
+ trackChanged(0, 0);
+ populateSongList("");
+ //totaltimelabel->clear();
+ totaltimelabel->lower();
+
+ if ((Prefs::autoplay() || KCmdLineArgs::parsedArgs()->isSet("start"))
+ && !m_cd->isPlaying())
+ {
+ playClicked();
+ }
+
+ // We just populated the GUI with what we got from the CD. Now look for
+ // more from the Internet...
+ lookupCDDB();
+}
+
+void KSCD::discStopped()
+{
+ if (Prefs::ejectOnFinish() && !stoppedByUser && (m_cd->discId() != KCompactDisc::missingDisc))
+ {
+ ejectClicked();
+ }
+
+ if (!stoppedByUser)
+ {
+ if (Prefs::randomPlay())
+ {
+ // If nextClicked returns false, it was the last
+ // random track, and the player should be stopped
+ if (nextClicked())
+ return;
+ }
+ else if (Prefs::looping())
+ {
+ playClicked();
+ return;
+ }
+ }
+
+ statuslabel->setText(i18n("Stopped"));
+ playPB->setText(i18n("Play"));
+ playPB->setIconSet(SmallIconSet("player_play"));
+
+ /* reset to initial value, only stopclicked() sets this to true */
+ stoppedByUser = false;
+
+ trackChanged(0, 0);
+ populateSongList("");
+ totaltimelabel->clear();
+ totaltimelabel->lower();
+}
+
+void KSCD::setLEDs(int milliseconds)
+{
+ QString symbols;
+
+ if (milliseconds < 0)
+ {
+ symbols = "--:--";
+ }
+ else
+ {
+ unsigned mymin;
+ unsigned mysec;
+ mymin = milliseconds / 60000;
+ mysec = (milliseconds % 60000) / 1000;
+ symbols.sprintf("%02d:%02d", mymin, mysec);
+ }
+
+ for (int i = 0; i < 5; i++)
+ {
+ trackTimeLED[i]->display((char)symbols.local8Bit().at(i));
+ }
+}
+
+void KSCD::resetTimeSlider(bool enabled)
+{
+ timeSlider->setEnabled(enabled);
+ timeSlider->blockSignals(true);
+ timeSlider->setValue(0);
+ timeSlider->blockSignals(false);
+} // resetTimeSlider(bool enabled);
+
+void KSCD::setColors()
+{
+ QColor led_color = Prefs::ledColor();
+ QColor background_color = Prefs::backColor();
+
+ backdrop->setBackgroundColor(background_color);
+
+ QColorGroup colgrp( led_color, background_color, led_color,led_color , led_color,
+ led_color, white );
+
+ titlelabel ->setPalette( QPalette(colgrp,colgrp,colgrp) );
+ artistlabel->setPalette( QPalette(colgrp,colgrp,colgrp) );
+ volumelabel->setPalette( QPalette(colgrp,colgrp,colgrp) );
+ statuslabel->setPalette( QPalette(colgrp,colgrp,colgrp) );
+ tracklabel ->setPalette( QPalette(colgrp,colgrp,colgrp) );
+ totaltimelabel->setPalette( QPalette(colgrp,colgrp,colgrp) );
+
+ queryled->setPalette( QPalette(colgrp,colgrp,colgrp) );
+ loopled->setPalette( QPalette(colgrp,colgrp,colgrp) );
+
+ for (int u = 0; u< 5;u++){
+ trackTimeLED[u]->setLEDoffColor(background_color);
+ trackTimeLED[u]->setLEDColor(led_color,background_color);
+ }
+
+ titlelabel ->setFont( Prefs::ledFont() );
+ artistlabel->setFont( Prefs::ledFont() );
+ volumelabel->setFont( Prefs::ledFont() );
+ statuslabel->setFont( Prefs::ledFont() );
+ tracklabel ->setFont( Prefs::ledFont() );
+ totaltimelabel->setFont( Prefs::ledFont() );
+}
+
+void KSCD::readSettings()
+{
+/*
+ time_display_mode = config->readNumEntry("TimeDisplay", TRACK_SEC);
+*/
+
+#ifndef DEFAULT_CD_DEVICE
+#define DEFAULT_CD_DEVICE "/dev/cdrom"
+ // sun ultrix etc have a canonical cd rom device specified in the
+ // respective plat_xxx.c file. On those platforms you need nnot
+ // specify the cd rom device and DEFAULT_CD_DEVICE is not defined
+ // in config.h
+#endif
+
+ if (Prefs::cdDevice().isEmpty())
+ {
+ Prefs::setCdDevice(DEFAULT_CD_DEVICE);
+ }
+
+ volumeIcon->setEnabled(!Prefs::digitalPlayback());
+ volumeSlider->setEnabled(!Prefs::digitalPlayback());
+}
+
+/**
+ * Write KSCD's Configuration into the kderc file.
+ *
+ */
+void KSCD::writeSettings()
+{
+ Prefs::writeConfig();
+} // writeSettings()
+
+void KSCD::CDDialogSelected()
+{
+ if (!cddialog)
+ {
+ cddialog = new CDDBDlg(this);
+
+ cddialog->setData(cddbInfo, m_cd->discSignature(), playlist);
+ connect(cddialog,SIGNAL(cddbQuery()),SLOT(lookupCDDB()));
+ connect(cddialog,SIGNAL(newCDInfoStored(KCDDB::CDInfo)),
+ SLOT(setCDInfo(KCDDB::CDInfo)));
+ connect(cddialog,SIGNAL(finished()),SLOT(CDDialogDone()));
+ connect(cddialog,SIGNAL(play(int)),SLOT(trackSelected(int)));
+ }
+
+ cddialog->show();
+ cddialog->raise();
+}
+
+void KSCD::CDDialogDone()
+{
+ cddialog->delayedDestruct();
+ cddialog = 0L;
+}
+
+void KSCD::lookupCDDB()
+{
+ if (m_cd->discId() == KCompactDisc::missingDisc)
+ return;
+ kdDebug(67000) << "lookupCDDB() called" << endl;
+
+ populateSongList(i18n("Start freedb lookup."));
+
+ setShuffle(2);
+
+ led_on();
+
+ cddb->config().reparse();
+ cddb->setBlockingMode(false);
+ cddb->lookup(m_cd->discSignature());
+} // lookupCDDB
+
+void KSCD::lookupCDDBDone(CDDB::Result result)
+{
+ led_off();
+ if ((result != KCDDB::CDDB::Success) &&
+ (result != KCDDB::CDDB::MultipleRecordFound))
+ {
+ populateSongList(result == CDDB::NoRecordFound ? i18n("No matching freedb entry found.") : i18n("Error getting freedb entry."));
+ return;
+ }
+
+ // The intent of the original code here seems to have been to perform the
+ // lookup, and then to convert all the string data within the CDDB response
+ // using the use Prefs::selectedEncoding() and a QTextCodec. However, that
+ // seems to be irrelevant these days.
+ KCDDB::CDInfo info = cddb->bestLookupResponse();
+ // TODO Why doesn't libcddb not return MultipleRecordFound?
+ //if( result == KCDDB::CDDB::MultipleRecordFound ) {
+ if( cddb->lookupResponse().count() > 1 ) {
+ CDInfoList cddb_info = cddb->lookupResponse();
+ CDInfoList::iterator it;
+ QStringList list;
+ for ( it = cddb_info.begin(); it != cddb_info.end(); ++it ) {
+ list.append( QString("%1, %2, %3").arg((*it).artist).arg((*it).title)
+ .arg((*it).genre));
+ }
+
+ bool ok(false);
+ QString res = KInputDialog::getItem(
+ i18n("Select CDDB Entry"),
+ i18n("Select a CDDB entry:"), list, 0, false, &ok,
+ this );
+ if ( ok ) {
+ // The user selected and item and pressed OK
+ uint c = 0;
+ for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
+ if( *it == res) break;
+ c++;
+ }
+ if( c < cddb_info.size() )
+ info = cddb_info[c];
+ } else {
+ return;
+ // user pressed Cancel
+ }
+ }
+
+ setCDInfo(info);
+
+ // In case the cddb dialog is open, update it
+ if (cddialog)
+ cddialog->setData(cddbInfo, m_cd->discSignature(), playlist);
+} // lookupCDDBDone
+
+void KSCD::setCDInfo(KCDDB::CDInfo info)
+{
+ // Some sanity provisions to ensure that the number of records matches what
+ // the CD actually contains.
+ while (info.trackInfoList.count() < cddbInfo.trackInfoList.count())
+ {
+ info.trackInfoList.append(KCDDB::TrackInfo());
+ }
+ while (info.trackInfoList.count() > cddbInfo.trackInfoList.count())
+ {
+ info.trackInfoList.pop_back();
+ }
+ cddbInfo = info;
+ populateSongList("");
+}
+
+void KSCD::led_off()
+{
+ queryledtimer.stop();
+ queryled->off();
+ queryled->hide();
+ totaltimelabel->raise();
+ totaltimelabel->show();
+} // led_off
+
+void KSCD::led_on()
+{
+ totaltimelabel->hide();
+ totaltimelabel->lower();
+ queryledtimer.start(800);
+ queryled->off();
+ queryled->show();
+ kapp->processEvents();
+ kapp->flushX();
+} // led_on
+
+void KSCD::togglequeryled()
+{
+ queryled->show();
+ queryled->toggle();
+} // togglequeryled
+
+void KSCD::titlelabeltimeout()
+{
+ // clear the cddb error message on the title label.
+ titlelabeltimer.stop();
+ titlelabel->clear();
+
+} // titlelabeltimeout
+
+void KSCD::trayOpening()
+{
+ statuslabel->setText(i18n("Ejected"));
+ trackChanged(0, 0);
+}
+
+int KSCD::currentTrack()
+{
+ return m_cd->track();
+}
+
+int KSCD::currentTrackLength()
+{
+ return m_cd->trackLength();
+}
+
+int KSCD::currentPosition()
+{
+ return m_cd->trackPosition();
+}
+
+int KSCD::getStatus()
+{
+ if (m_cd->isPlaying())
+ return 2;
+ else if (m_cd->isPaused())
+ return 4;
+ else if (m_cd->discId() != KCompactDisc::missingDisc)
+ return 5;
+ else
+ return 6;
+}
+
+bool KSCD::playing()
+{
+ return m_cd->isPlaying();
+}
+
+void KSCD::trackUpdate(unsigned /*track*/, unsigned trackPosition)
+{
+ unsigned tmp;
+
+ switch (Prefs::timeDisplayMode())
+ {
+ case TRACK_REM:
+ tmp = m_cd->trackLength() - trackPosition;
+ break;
+
+ case TOTAL_SEC:
+ tmp = m_cd->discPosition();
+ break;
+
+ case TOTAL_REM:
+ tmp = m_cd->discLength() - m_cd->discPosition();
+ break;
+
+ case TRACK_SEC:
+ default:
+ tmp = trackPosition;
+ break;
+ }
+ if (updateTime)
+ {
+ setLEDs(tmp);
+ timeSlider->blockSignals(true);
+ timeSlider->setValue(trackPosition);
+ timeSlider->blockSignals(false);
+ }
+}
+
+void KSCD::cycleplaytimemode()
+{
+ cycletimer.stop();
+
+ if (Prefs::timeDisplayMode() > 2) {
+ Prefs::setTimeDisplayMode(TRACK_SEC);
+ } else {
+ Prefs::setTimeDisplayMode(Prefs::timeDisplayMode() + 1);
+ }
+
+ switch(Prefs::timeDisplayMode()) {
+
+ case TRACK_REM:
+ volumelabel->setText(i18n("Tra Rem"));
+ break;
+
+ case TOTAL_SEC:
+ volumelabel->setText(i18n("Tot Sec"));
+ break;
+
+ case TOTAL_REM:
+ volumelabel->setText(i18n("Tot Rem"));
+ break;
+
+ case TRACK_SEC:
+ default:
+ volumelabel->setText(i18n("Tra Sec"));
+ break;
+ }
+
+ cycletimer.start(3000, true);
+} // cycleplaymode
+
+void KSCD::cycletimeout()
+{
+ cycletimer.stop();
+ QString str;
+ str = QString::fromUtf8( QCString().sprintf(i18n("Vol: %02d%%").utf8(), Prefs::volume()) );
+ volumelabel->setText(str);
+} // cycletimeout
+
+
+void KSCD::information(int i)
+{
+ //kdDebug(67000) << "Information " << i << "\n" << endl;
+
+ if(cddbInfo.artist.isEmpty())
+ return;
+
+ QString encodedArtist = KURL::encode_string_no_slash(cddbInfo.artist);
+
+ QString str;
+
+ switch(i)
+ {
+ case 0:
+ str = QString("http://musicmoz.org/cgi-bin/ext.cgi?artist=%1")
+ .arg(encodedArtist);
+ break;
+
+ case 1:
+ str = QString("http://ubl.artistdirect.com/cgi-bin/gx.cgi/AppLogic+Search?select=MusicArtist&searchstr=%1&searchtype=NormalSearch")
+ .arg(encodedArtist);
+ break;
+
+ case 2:
+ str = QString("http://www.cduniverse.com/cgi-bin/cdubin.exe/rlinka/ean=%1")
+ .arg(encodedArtist);
+ break;
+
+ case 3:
+ str = QString("http://www.alltheweb.com/search?cat=web&q=%1")
+ .arg(encodedArtist);
+ break;
+
+ case 4:
+ str = QString("http://altavista.com/web/results?q=%1&kgs=0&kls=1&avkw=xytx")
+ .arg(encodedArtist);
+ break;
+
+ case 5:
+ str = QString("http://msxml.excite.com/_1_2UDOUB70SVHVHR__info.xcite/dog/results?otmpl=dog/webresults.htm&qkw=%1&qcat=web&qk=20&top=1&start=&ver=14060")
+ .arg(encodedArtist);
+ break;
+
+ case 6:
+ str = QString("http://www.google.com/search?q=%1")
+ .arg(encodedArtist);
+ break;
+
+ case 7:
+ str = QString("http://groups.google.com/groups?oi=djq&as_q=%1&num=20")
+ .arg(encodedArtist);
+ break;
+
+ case 8:
+ str = QString("http://www.hotbot.com/default.asp?prov=Inktomi&query=%1&ps=&loc=searchbox&tab=web")
+ .arg(encodedArtist);
+ break;
+
+ case 9:
+ str = QString("http://search.lycos.com/default.asp?lpv=1&loc=searchhp&tab=web&query=%1")
+ .arg(encodedArtist);
+ break;
+
+ case 10:
+ str = QString("http://search.dmoz.org/cgi-bin/search?search=%1")
+ .arg(encodedArtist);
+ break;
+
+ case 11:
+ str = QString("http://search.yahoo.com/bin/search?p=%1")
+ .arg(encodedArtist);
+ break;
+
+ default:
+ return;
+ break;
+ } // switch()
+
+ KRun::runURL(KURL( str ), "text/html");
+} // information
+
+/**
+ * Save state on session termination
+ */
+bool KSCD::saveState(QSessionManager& /*sm*/)
+{
+ writeSettings();
+ KConfig* config = KApplication::kApplication()->sessionConfig();
+ config->setGroup("General");
+ config->writeEntry("Show", isVisible());
+ return true;
+} // saveState
+
+
+/**
+ * Allow the user to type in the number of the track
+ */
+void KSCD::keyPressEvent(QKeyEvent* e)
+{
+ bool isNum;
+ int value = e->text().toInt(&isNum);
+
+ if (e->key() == Qt::Key_F1)
+ {
+ kapp->invokeHelp();
+ }
+ else if (isNum)
+ {
+ value = (jumpToTrack * 10) + value;
+
+ if (value <= (int)cddbInfo.trackInfoList.count())
+ {
+ jumpToTrack = value;
+ jumpTrackTimer.stop();
+ jumpTrackTimer.start(333);
+ }
+ }
+ else
+ {
+ QWidget::keyPressEvent(e);
+ }
+} //keyPressEvent
+
+void KSCD::jumpTracks()
+{
+ if (jumpToTrack > 0 && jumpToTrack <= (int)cddbInfo.trackInfoList.count())
+ {
+ m_cd->play(jumpToTrack, 0, jumpToTrack + 1);
+ }
+
+ jumpToTrack = 0;
+} // jumpTracks
+
+QString KSCD::currentTrackTitle()
+{
+ int track = m_cd->track();
+ return (track > -1) ? cddbInfo.trackInfoList[track-1].title : QString::null;
+}
+
+QString KSCD::currentAlbum()
+{
+ return cddbInfo.title;
+}
+
+QString KSCD::currentArtist()
+{
+ return cddbInfo.artist;
+}
+
+QStringList KSCD::trackList()
+{
+ QStringList tracks;
+ for (TrackInfoList::const_iterator it = cddbInfo.trackInfoList.begin();
+ it != cddbInfo.trackInfoList.end(); ++it)
+ tracks << (*it).title;
+
+ return tracks;
+}
+
+void KSCD::populateSongList(QString infoStatus)
+{
+ // set the artist and title labels as well as the dock tooltip.
+ if (!infoStatus.isEmpty())
+ artistlabel->setText(infoStatus);
+ else
+ artistlabel->setText(QString("%1 - %2").arg(cddbInfo.artist, cddbInfo.title));
+
+ songListCB->clear();
+ for (unsigned i = 0; i < cddbInfo.trackInfoList.count(); i++)
+ {
+ unsigned tmp = m_cd->trackLength(i + 1);
+ unsigned mymin;
+ unsigned mysec;
+ mymin = tmp / 60000;
+ mysec = (tmp % 60000) / 1000;
+ QString str1;
+ str1.sprintf("%02d: ", i + 1);
+ QString str2;
+ str2.sprintf(" (%02d:%02d) ", mymin, mysec);
+ str1.append(cddbInfo.trackInfoList[i].title);
+ str1.append(str2);
+ songListCB->insertItem(str1);
+ }
+
+ emit trackChanged(m_cd->track(), m_cd->trackLength());
+}
+
+static const KCmdLineOptions options[] =
+{
+ {"s",0,0},
+ {"start",I18N_NOOP("Start playing"),0},
+ {"+[device]",I18N_NOOP("CD device, can be a path or a media:/ URL"),0},
+ KCmdLineLastOption
+};
+
+
+/**
+ * main()
+ */
+int main( int argc, char *argv[] )
+{
+ KAboutData aboutData("kscd", I18N_NOOP("KsCD"),
+ KSCDVERSION, description,
+ KAboutData::License_GPL,
+ "(c) 2001, Dirk Försterling\n(c) 2003, Aaron J. Seigo");
+ aboutData.addAuthor("Aaron J. Seigo", I18N_NOOP("Current maintainer"), "aseigo@kde.org");
+ aboutData.addAuthor("Alexander Kern",I18N_NOOP("Workman library update, CDTEXT, CDDA"), "kernalex@kde.org");
+ aboutData.addAuthor("Bernd Johannes Wuebben",0, "wuebben@kde.org");
+ aboutData.addAuthor("Dirk Försterling", I18N_NOOP("Workman library, previous maintainer"), "milliByte@gmx.net");
+ aboutData.addCredit("Wilfried Huss", I18N_NOOP("Patches galore"));
+ aboutData.addCredit("Steven Grimm", I18N_NOOP("Workman library"));
+ aboutData.addCredit("Sven Lueppken", I18N_NOOP("UI Work"));
+ aboutData.addCredit("freedb.org", I18N_NOOP("Special thanks to freedb.org for providing a free CDDB-like CD database"), 0, "http://freedb.org");
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineArgs::addCmdLineOptions(options);
+ KUniqueApplication::addCmdLineOptions();
+
+ KCmdLineArgs* args = KCmdLineArgs::parsedArgs();
+ if (!KUniqueApplication::start())
+ {
+ fprintf(stderr, "kscd is already running\n");
+ if (args->count()>0 || args->isSet("start"))
+ {
+ DCOPClient client;
+ if (client.attach())
+ {
+ // Forward the command line args to the running instance.
+ DCOPRef ref("kscd", "CDPlayer");
+ if (args->count() > 0)
+ {
+ ref.send("setDevice(QString)", QString(args->arg(0)));
+ }
+ if (args->isSet("start"))
+ {
+ ref.send("play()");
+ }
+ }
+ }
+ exit(0);
+ }
+
+ KUniqueApplication a;
+
+ kapp->dcopClient()->setDefaultObject("CDPlayer");
+
+ KSCD *k = new KSCD();
+
+ a.setTopWidget( k );
+ a.setMainWidget( k );
+
+ k->setCaption(a.caption());
+
+ if (kapp->isRestored())
+ {
+ KConfig* config = KApplication::kApplication()->sessionConfig();
+ config->setGroup("General");
+ if (config->readBoolEntry("Show"))
+ k->show();
+ }
+ else
+ {
+ k->show();
+ }
+
+ if (args->count()>0) Prefs::self()->setCdDevice(args->arg(0));
+
+ return a.exec();
+}
+
+#include "kscd.moc"
diff --git a/kscd/kscd.desktop b/kscd/kscd.desktop
new file mode 100644
index 00000000..cf0535e3
--- /dev/null
+++ b/kscd/kscd.desktop
@@ -0,0 +1,85 @@
+[Desktop Entry]
+GenericName=CD Player
+GenericName[af]=Cd Speler
+GenericName[ar]=مشغل الأقراص المدمجة
+GenericName[bg]=Плеър за аудио дискове
+GenericName[bn]=সিডি প্লেয়ার
+GenericName[br]=Ur c'hoarier CD
+GenericName[ca]=Reproductor de CD
+GenericName[cs]=CD přehrávač
+GenericName[cy]=Chwaraewr CD
+GenericName[da]=Cd-afspiller
+GenericName[de]=CD-Wiedergabe
+GenericName[el]=Αναπαραγωγέας CD
+GenericName[eo]=Ludilo por muzikaj lumdiskoj
+GenericName[es]=Reproductor de CDs
+GenericName[et]=CD mängija
+GenericName[eu]=CD erreproduzigailua
+GenericName[fa]=پخش‌کنندۀ دیسک فشرده
+GenericName[fi]=CD-soitin
+GenericName[fr]=Lecteur de CD audio
+GenericName[ga]=Seinnteoir Dlúthdhioscaí
+GenericName[gl]=Reproductor de CD
+GenericName[he]=נגן תקליטורים
+GenericName[hi]=सीडी प्लेयर
+GenericName[hu]=CD-lejátszó
+GenericName[is]=CD spilari
+GenericName[it]=Lettore CD
+GenericName[ja]=CD プレーヤ
+GenericName[kk]=CD ойнатқышы
+GenericName[km]=កម្មវិធី​ចាក់​ស៊ីឌី
+GenericName[ko]=CD 재생기
+GenericName[lt]=CD grotuvas
+GenericName[lv]=CD Atskaņotājs
+GenericName[mk]=CD плеер
+GenericName[ms]=Pemain CD
+GenericName[nb]=CD-spiller
+GenericName[nds]=CD-Afspeler
+GenericName[ne]=सीडी प्लेयर
+GenericName[nl]=CD-speler
+GenericName[nn]=CD-spelar
+GenericName[pa]=CD ਪਲੇਅਰ
+GenericName[pl]=Odtwarzacz CD
+GenericName[pt]=Leitor de CDs
+GenericName[pt_BR]=Reprodutor de CDs
+GenericName[ro]=Redare CD audio
+GenericName[ru]=Проигрыватель аудиодисков
+GenericName[se]=CD-čuojaheaddji
+GenericName[sk]=CD prehrávač
+GenericName[sl]=Predvajalnik CD-jev
+GenericName[sr]=CD плејер
+GenericName[sr@Latn]=CD plejer
+GenericName[sv]=Cd-spelare
+GenericName[ta]=குறுந்தகடு இயக்கி
+GenericName[tg]=Бозингари Диски Фишурда
+GenericName[th]=โปรแกรมเล่นซีดี
+GenericName[tr]=CD Çalar
+GenericName[uk]=Програвач КД
+GenericName[uz]=Kompakt-disk pleyer
+GenericName[uz@cyrillic]=Компакт-диск плейер
+GenericName[ven]=Tshitambi tsha CD
+GenericName[wa]=Djoweu di plakes lazer
+GenericName[xh]=Umdlali We CD
+GenericName[zh_CN]=CD 播放器
+GenericName[zh_HK]=CD 播放器
+GenericName[zh_TW]=CD 播放器
+GenericName[zu]=Umdlali we CD
+Name=KsCD
+Name[af]=Kscd
+Name[bn]=কে-এস-সিডি
+Name[eo]=Lumdiskludilo
+Name[hi]=के-एससीडी
+Name[sv]=Kscd
+Name[ta]=Ksகுறுந்தகடு
+Name[tg]=KsДиски Фишурда
+Name[tr]=Kscd
+Name[zh_TW]=KsCD 播放器
+Exec=kscd -caption "%c" %i %m
+DocPath=kscd/index.html
+Icon=kscd
+Path=
+Type=Application
+Terminal=false
+X-KDE-StartupNotify=true
+X-DCOP-ServiceType=Multi
+Categories=Qt;KDE;AudioVideo;
diff --git a/kscd/kscd.h b/kscd/kscd.h
new file mode 100644
index 00000000..6c94c09d
--- /dev/null
+++ b/kscd/kscd.h
@@ -0,0 +1,258 @@
+/*
+ Kscd - A simple cd player for the KDE Project
+
+ $Id$
+
+ Copyright (c) 1997 Bernd Johannes Wuebben math.cornell.edu
+ Copyright (c) 2002 Aaron J. Seigo <aseigo@kde.org>
+ Copyright (c) 2004 Alexander Kern <alex.kern@gmx.de>
+
+ 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, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __KSCD__
+#define __KSCD__
+
+#include "bwlednum.h"
+
+// CD support.
+class KCompactDisc;
+
+// CDDB support via libkcddb
+#include <libkcddb/cddb.h>
+#include <libkcddb/client.h>
+
+
+#include <qpushbutton.h>
+#include <qlabel.h>
+#include <qdialog.h>
+#include <qapplication.h>
+#include <qtimer.h>
+#include <qcombobox.h>
+#include <qscrollbar.h>
+#include <qslider.h>
+#include <qtabdialog.h>
+#include <qtooltip.h>
+#include <qpopupmenu.h>
+#include <qvaluelist.h>
+
+#include "ledlamp.h"
+#include "panel.h"
+#include "prefs.h"
+#include "configWidget.h"
+#include <kapplication.h>
+#include <kconfigdialog.h>
+#include <kprocess.h>
+#include <krandomsequence.h>
+#include <dcopobject.h>
+#include <kglobalaccel.h>
+
+class CDDBDlg;
+class DockWidget;
+class QGridLayout;
+class KActionCollection;
+class KToggleAction;
+
+using namespace KCDDB;
+
+typedef QValueList<int> RandomList;
+
+class KSCD : public kscdPanelDlg, public KSessionManaged, virtual public DCOPObject {
+
+ Q_OBJECT
+ K_DCOP
+
+ // time display modes
+ enum time_display { TRACK_SEC = 0, TRACK_REM = 1, TOTAL_SEC = 2, TOTAL_REM = 3 };
+
+
+k_dcop:
+ bool playing();
+ void play() { playClicked(); }
+ void stop() { stopClicked(); }
+ void previous() { prevClicked(); }
+ void next() { nextClicked(); }
+ void jumpTo(int seconds) { jumpToTime(seconds); }
+ void eject() { ejectClicked(); }
+ void quit() { quitClicked(); }
+ void toggleLoop() { loopClicked(); }
+ void toggleShuffle() { randomSelected(); }
+ void toggleTimeDisplay() { cycleplaytimemode(); }
+ void cddbDialog() { CDDialogSelected(); }
+ void optionDialog() { showConfig(); }
+ void setTrack(int t) { trackSelected(t > 0 ? t - 1 : 0); }
+ void volumeDown() { decVolume(); }
+ void volumeUp() { incVolume(); }
+ void setVolume(int v);
+ void setDevice(const QString& dev);
+ int getVolume() { return Prefs::volume(); }
+ int currentTrack();
+ int currentTrackLength();
+ int currentPosition();
+ int getStatus();
+ QString currentTrackTitle();
+ QString currentAlbum();
+ QString currentArtist();
+ QStringList trackList();
+
+public:
+ KSCD( QWidget *parent = 0, const char *name = 0 );
+ ~KSCD();
+ virtual bool saveState(QSessionManager& sm);
+
+ void setDocking(bool dock);
+ bool digitalPlayback();
+ void setDevicePaths();
+ QStringList audioSystems() { return audio_systems_list; }
+
+ KActionCollection* actionCollection() { return m_actions; }
+
+signals:
+ void trackChanged(const QString&);
+
+public slots:
+ void setColors();
+ void togglequeryled();
+ void randomSelected();
+ void setShuffle(int shuffle); /* 0 -off, 1 - on, 2 - remake random list */
+ void writeSettings();
+ void playClicked();
+ bool nextClicked();
+ void prevClicked();
+ void stopClicked();
+ void ejectClicked();
+ void jumpToTime(int seconds, bool forcePlay = false);
+ void quitClicked();
+ void loopOn();
+ void loopOff();
+ void loopClicked();
+ void trackSelected(int);
+ void showConfig();
+ void incVolume();
+ void decVolume();
+ void volChanged(int);
+ void led_on();
+ void led_off();
+ void titlelabeltimeout();
+ void cycleplaytimemode();
+ void cycletimeout();
+
+ void information(int);
+ void jumpTracks();
+
+ void make_random_list(); /* koz: 15/01/00 */
+
+protected:
+ // mostly start up stuff
+ void readSettings();
+ void initFont();
+ void drawPanel();
+ void setupPopups();
+ void setLEDs(int milliseconds);
+ void resetTimeSlider(bool enabled);
+
+ void dragTime(int sec);
+
+ void closeEvent(QCloseEvent *e);
+ void keyPressEvent( QKeyEvent* e );
+ bool event( QEvent *e );
+ // void focusOutEvent(QFocusEvent *e);
+ void playtime();
+ void playtime(int seconds);
+ void calculateDisplayedTime();
+ void calculateDisplayedTime(int sec);
+ void setSongListTo(int whichTrack);
+ void populateSongList(QString infoStatus);
+ void updatePlayPB(bool playing);
+
+ void updateConfigDialog(configWidget* widget);
+
+private:
+ KConfigDialog *configDialog;
+ CDDBDlg *cddialog;
+ QPopupMenu *mainPopup;
+ QPopupMenu *infoPopup;
+
+ BW_LED_Number *trackTimeLED[6];
+
+ KCompactDisc *m_cd;
+ QTimer titlelabeltimer;
+ QTimer queryledtimer;
+ QTimer cycletimer;
+ QTimer jumpTrackTimer;
+
+ // random playlists
+ KRandomSequence randSequence;
+ RandomList random_list;
+ RandomList::iterator random_current;
+
+
+ int jumpToTrack;
+ LedLamp *queryled;
+ LedLamp *loopled;
+ bool randomplay_pending;
+ bool updateTime;
+ QStringList audio_systems_list;
+
+ /**
+ * select a random track from the current disc.
+ *
+ */
+ int next_randomtrack();
+ int prev_randomtrack();
+ int real_randomtrack();
+
+ void setTitle(int track);
+
+ /**
+ * Info from CDDB, and exploded versions thereof.
+ */
+ KCDDB::CDInfo cddbInfo;
+ QStringList playlist;
+ KCDDB::Client* cddb;
+ KActionCollection* m_actions;
+ KGlobalAccel* m_globalAccel;
+ KToggleAction* m_togglePopupsAction;
+ DockWidget* m_dockWidget;
+ void lookupDevice();
+ void initGlobalShortcuts();
+public slots:
+ void lookupCDDB();
+
+private slots:
+ void CDDialogSelected();
+ void CDDialogDone();
+ void setCDInfo(KCDDB::CDInfo);
+ void lookupCDDBDone(CDDB::Result);
+ void discStopped();
+ void trackUpdate(unsigned track, unsigned trackPosition);
+ void trackChanged(unsigned track, unsigned trackLength);
+ void discChanged(unsigned discId);
+// void trayClosing();
+ void trayOpening();
+ void configDone();
+ void configureKeys();
+ void configureGlobalKeys();
+ void setIcons();
+
+ void timeSliderPressed();
+ void timeSliderReleased();
+ void timeSliderMoved(int milliseconds);
+};
+
+
+
+#endif
+
diff --git a/kscd/kscd.kcfg b/kscd/kscd.kcfg
new file mode 100644
index 00000000..62a04984
--- /dev/null
+++ b/kscd/kscd.kcfg
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+ <kcfgfile name="kscdrc"/>
+ <group name="GENERAL">
+ <entry name="Volume" type="UInt">
+ <label>Output Volume</label>
+ <default>40</default>
+ </entry>
+ <entry name="RandomPlay" type="Bool">
+ <label>Play random tracks.</label>
+ <whatsthis>When this option is selected the playing order
+ of the CD tracks is chosen at random.</whatsthis>
+ <default>false</default>
+ </entry>
+ <entry name="Docking" type="Bool" key="DOCKING">
+ <label>Show an icon in the system tray.</label>
+ <whatsthis>When this option is selected an icon will appear in the system tray. Note that KsCD will &lt;i&gt;not&lt;/i&gt; quit when the window is closed if a system tray icon is displayed. You may quit KsCD by clicking the Quit button or right-clicking on the system tray icon and selecting the appropriate entry.</whatsthis>
+ <default>true</default>
+ </entry>
+ <entry name="TrackAnnouncement" type="Bool">
+ <default>false</default>
+ </entry>
+ <entry name="Autoplay" type="Bool" key="AUTOPLAY">
+ <label>Start playing when a CD is inserted.</label>
+ <whatsthis>When this option is selected the CD will start playing automatically upon being inserted into the CD-ROM.</whatsthis>
+ <default>false</default>
+ </entry>
+ <entry name="StopExit" type="Bool" key="STOPEXIT">
+ <label>Stop playing the CD on program exit.</label>
+ <whatsthis>When this option is selected the CD will automatically stop playing when quitting KsCD.</whatsthis>
+ <default>true</default>
+ </entry>
+ <entry name="EjectOnFinish" type="Bool" key="EJECTONFINISH">
+ <label>Eject CD when playing is finished.</label>
+ <whatsthis>When this option is selected the CD will automatically eject when it is finished.</whatsthis>
+ <default>false</default>
+ </entry>
+ <entry name="Looping" type="Bool">
+ <label>Loop tracks.</label>
+ <default>false</default>
+ </entry>
+ <entry name="SkipDelta" type="UInt">
+ <label>Skip interval.</label>
+ <whatsthis>This option controls the number of seconds KsCD will skip when the skip forwards or backwards buttons are pressed.</whatsthis>
+ <default>30</default>
+ </entry>
+ <entry name="TimeDisplayMode" type="Enum" key="TimeDisplay">
+ <choices>
+ <choice name="TRACK_SEC"/>
+ <choice name="TRACK_REM"/>
+ <choice name="TOTAL_SEC"/>
+ <choice name="TOTAL_REM"/>
+ </choices>
+ <default>TRACK_SEC</default>
+ </entry>
+ <entry name="cdDevice" type="String" key="CDDevice">
+ <label>Name of the CD-ROM device.</label>
+ <whatsthis>The CD-ROM device to use when playing CDs. This will typically look something like "/dev/cdrom". To have KsCD autodetect your CD-ROM, leave this field empty.</whatsthis>
+ </entry>
+ <entry name="AudioSystem" type="String">
+ <label>The audio backend KsCD uses.</label>
+ </entry>
+ <entry name="AudioDevice" type="String">
+ <label>The audio device KsCD uses.</label>
+ </entry>
+ <entry name="DigitalPlayback" type="Bool">
+ <label>Use direct digital playback.</label>
+ <whatsthis>When this option is selected KsCD will attempt to play the CD using direct digital playback. This option is useful if the CD-ROM is not connected directly to the sound output on the computer. Note that digital playback is slower than the normal method of playback.</whatsthis>
+ <default>false</default>
+ </entry>
+ <entry name="ledColor" type="Color">
+ <label>The foreground color that will be used in the LCD display.</label>
+ <default>#e2e0ff</default>
+ </entry>
+ <entry name="ledFont" type="Font">
+ <label>The font that will be used for the LCD display.</label>
+ <default><code>QFont(KGlobalSettings::generalFont().family(), 10, QFont::Bold)</code></default>
+ </entry>
+ <entry name="BackColor" type="Color">
+ <label>The background color that will be used for the LCD display.</label>
+ <default>black</default>
+ </entry>
+ <entry name="SelectEncoding" type="Bool">
+ <label>Allow encoding selection.</label>
+ <whatsthis>When this option is selected, you have the ability to select encoding for the results of a CDDB request. The standard describes CDDB results as being strictly Latin1. This is not true, as non-English speaking users often use other 8-bit encodings.</whatsthis>
+ <default>false</default>
+ </entry>
+ <entry name="SelectedEncoding" type="Enum">
+ <choices>
+ <choice name="AUTO"/>
+ <choice name="utf8"/>
+ <choice name="CP1250"/>
+ <choice name="CP1251"/>
+ <choice name="CP1252"/>
+ <choice name="CP1253"/>
+ <choice name="CP1254"/>
+ <choice name="CP1255"/>
+ <choice name="CP1256"/>
+ <choice name="CP1257"/>
+ </choices>
+ <default>utf8</default>
+ </entry>
+ </group>
+</kcfg>
diff --git a/kscd/kscd.lsm b/kscd/kscd.lsm
new file mode 100644
index 00000000..27cdb0dc
--- /dev/null
+++ b/kscd/kscd.lsm
@@ -0,0 +1,15 @@
+Begin3
+Title: Kscd
+Version: 0.4
+Entered-date: 08/14/97
+Description: A simple CD Player for the KDE Project
+Keywords: KDE, Qt, gui, X11, CD player, sound
+Author: <wuebben@math.cornell.edu> Bernd Johannes Wuebben
+Maintained-by: <wuebben@math.cornell.edu> Bernd Johannes Wuebben
+Primary-site: ftp://fiwi02.wiwi.uni-tuebingen.de/pub/kde/apps/multimedia
+Home-Page: http://math.cornell.edu/~wuebben/kde.html
+Alternate-site: ftp://math.cornell.edu/pub/wuebben/kscd
+Original-site: ftp://fiwi02.wiwi.uni-tuebingen.de/pub/kde/apps/multimedia
+Platform: Unix, Qt
+Copying-policy: GPL
+End
diff --git a/kscd/kscd.profile.xml b/kscd/kscd.profile.xml
new file mode 100644
index 00000000..542c5911
--- /dev/null
+++ b/kscd/kscd.profile.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" ?>
+<!DOCTYPE profile SYSTEM "profile.dtd">
+
+<profile id="kscd" servicename="KsCD">
+ <name>KsCD</name>
+ <author>Aaron J. Seigo</author>
+ <action objid="CDPlayer" prototype="void play()" class="play" repeat="0" autostart="1">
+ <name>Play/Pause</name>
+ <comment>Toggles between playing and pausing.</comment>
+ </action>
+ <action objid="CDPlayer" prototype="void stop()" class="stop" repeat="0" autostart="0">
+ <name>Stop</name>
+ <comment>Stops the CD.</comment>
+ </action>
+ <action objid="CDPlayer" prototype="void eject()" class="eject" repeat="0" autostart="0">
+ <name>Eject</name>
+ <comment>Ejects the CD.</comment>
+ </action>
+ <action objid="CDPlayer" prototype="void forward()" class="forward" repeat="1" autostart="0">
+ <name>Fast Forward</name>
+ <comment>Skips forwards in the current track.</comment>
+ </action>
+ <action objid="CDPlayer" prototype="void backward()" class="rewind" repeat="1" autostart="0">
+ <name>Rewind</name>
+ <comment>Skips backwards in the current track.</comment>
+ </action>
+ <action objid="CDPlayer" prototype="void previous()" class="previous" repeat="0" autostart="0">
+ <name>Previous</name>
+ <comment>Skips to the previous track on the CD.</comment>
+ </action>
+ <action objid="CDPlayer" prototype="void next()" class="next" repeat="0" autostart="0">
+ <name>Next</name>
+ <comment>Skips to the next track on the CD.</comment>
+ </action>
+ <action objid="CDPlayer" prototype="void setTrack(int)" class="number" repeat="0" autostart="1">
+ <name>Set Track</name>
+ <comment>Skips to a specific track on the CD.</comment>
+ <argument type="int">
+ <comment>The number of the track to skip to.</comment>
+ <range min="0" max="100"/>
+ </argument>
+ </action>
+ <action objid="CDPlayer" prototype="void setVolume(int)" repeat="0" autostart="0">
+ <name>Set Volume</name>
+ <comment>Sets the play volume.</comment>
+ <argument type="int">
+ <comment>The volume to set the CD player to.</comment>
+ <range min="0" max="100"/>
+ </argument>
+ </action>
+ <action objid="CDPlayer" prototype="void toggleLoop()" repeat="0" autostart="0">
+ <name>Loop</name>
+ <comment>Toggles looping on and off.</comment>
+ </action>
+ <action objid="CDPlayer" prototype="void toggleShuffle()" repeat="0" autostart="0">
+ <name>Shffule</name>
+ <comment>Toggles track shuffling on and off.</comment>
+ </action>
+</profile>
diff --git a/kscd/kscdmagic/Makefile.am b/kscd/kscdmagic/Makefile.am
new file mode 100644
index 00000000..c5cc3a21
--- /dev/null
+++ b/kscd/kscdmagic/Makefile.am
@@ -0,0 +1,40 @@
+
+# this 10 paths are KDE specific. Use them:
+# kde_htmldir Where your docs should go to. (contains lang subdirs)
+# kde_appsdir Where your application file (.kdelnk) should go to.
+# kde_icondir Where your icon should go to.
+# kde_minidir Where your mini icon should go to.
+# kde_datadir Where you install application data. (Use a subdir)
+# kde_locale Where translation files should go to.(contains lang subdirs)
+# kde_cgidir Where cgi-bin executables should go to.
+# kde_confdir Where config files should go to.
+# kde_mimedir Where mimetypes should go to.
+# kde_toolbardir Where general toolbar icons should go to.
+# kde_wallpaperdir Where general wallpapers should go to.
+
+# just set the variable
+APPSDIR = $(kde_appsdir)/Multimedia
+
+# set the include path for X, qt and KDE
+INCLUDES= $(all_includes)
+
+####### This part is very kscdmagic specific
+# you can add here more. This one gets installed
+bin_PROGRAMS = kscdmagic
+
+# Which sources should be compiled for kscdmagic
+kscdmagic_SOURCES = core.cpp main.cpp sound.cpp xlibwrap.cpp xlib.c
+
+# the library search path.
+kscdmagic_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+# the libraries to link against. Be aware of the order. First the libraries,
+# that depend on the following ones.
+kscdmagic_LDADD = -lm $(LIB_KDEUI)
+
+# this option you can leave out. Just, if you use "make dist", you need it
+noinst_HEADERS = symbol.h syna.h xlib.h magicconf.h
+
+messages:
+# $(XGETTEXT) -kTRANS $(kscdmagic_SOURCES)
+
diff --git a/kscd/kscdmagic/README b/kscd/kscdmagic/README
new file mode 100644
index 00000000..64af4f6f
--- /dev/null
+++ b/kscd/kscdmagic/README
@@ -0,0 +1,146 @@
+
+kscdmagic is based on synaesthesia
+by Paul Harrison <pfh@yoyo.cc.monash.edu.au>.
+
+ -dirk <milliByte@gmx.net>
+
+
+
+
+SYNAESTHESIA v2.0
+
+Introduction
+============
+
+This is a program for representing sounds visually from a CD or line
+input or piped from another program. It goes beyond the usual oscilliscope
+style program by combining an FFT and stereo positioning information to
+give a two dimensional display. Some of the shapes I have observed are:
+ * Drums: clouds of color, fairly high
+ * Clean guitar: several horizontal lines, low down
+ * Rough guitar: a cloud, low down
+ * Trumpet: Lots of horizontal lines everywhere
+ * Flute: A single horizontal line, low down
+ * Voice: A vertical line with some internal structure
+ * Synthesizer: All kinds of weird shapes!
+
+Synaesthesia can run in a window in X or full screen using SVGAlib.
+
+The display represents frequency as vertical position on screen,
+left-right position as left-right position on screen. It can also
+understand surround sound encoded music, and shows ambient noise
+in orange.
+
+X-Windows support was added in version 1.3, as well as a major redesign
+of the interface. You can use Synaesthesia as a fully functional
+CD player, suitable for use while working.
+
+There is command line support for play lists and piping from another
+program (such as an mp3 player).
+
+Usage
+=====
+
+Synaesthesia should work on Linux and BSD systems. (Note: I don't
+have access to a BSD system myself, I have to rely on patches -- if it
+doesn't work, please tell me!) LinuxPPC users may have to use the pipe
+mode rather than taking sound input from the CD player, as I believe
+sound recording is not yet implemented.
+
+Compile Synaesthesia by typing
+
+ make
+
+then install it by typing
+
+ make install
+
+This will create three versions of Synaesthesia:
+
+ synaesthesia - full screen SVGAlib version (Linux only)
+ xsynaesthesia - Version that runs as a window in X
+ sdlsynaesthesia - Version that uses the SDL graphics library
+
+If you want to use the SDL version, you need to get SDL from
+http://www.devolution.com/~slouken/SDL.
+
+You will need to run Synaesthesia as root to run it full screen
+with SVGAlib. Other varieties can be run by any user providing you
+provide permissions on /dev/dsp, /dev/cdrom, and /dev/mixer.
+
+Synaesthesia creates a configuration file, named ~/.synaesthesia,
+to store settings such as brightness, color, and window size, as
+well as which devices to use to control sound input.
+
+BSD users will have to edit this file to set the CD-ROM device name
+before using Synaesthesia in order to control the CD.
+
+Run Synaesthesia with no parameters for further information on how to
+use it.
+
+Notes for code rippers
+======================
+
+This program contains code that you may wish to use in your own projects.
+If you want to, please do. (For example, you might want to add some
+snazzy visual effects to your favorite MP3 or CD player)
+
+The actual code to do the mapping from sound to visual display is
+all in core.cpp, it should be fairly easy to disentangle from other
+parts of the program. It does make reference to some globals defined
+in syna.h, namely the #defines m (log2 of the sample size for each
+frame) and brightness, data (which stores the sound input), outputBmp,
+lastOutputBmp and lastLastOutputBmp (which hold the output), outWidth
+and outHeight (size of the bitmaps), and fadeMode, brightnessTwiddler,
+starSize and pointsAreDiamonds (various parameters affecting the display).
+
+The normal way to use it would be:
+
+ Call coreInit() to set up some look-up tables
+ Call setStarSize(starSize) to set up some more look-up tables
+ Loop
+ Put data into the data array
+ Call fade() to apply the fade/wave/heat effect to the output
+ Call coreGo() to add the next fragment of sound input to the output
+ Display contents of outputBmp to screen
+
+There is a simple anti-aliased polygon drawing engine in the file
+polygon.h. sound.cpp contains code for driving the CD. xlib.c and
+xlibwrap.cpp contain code for setting up a window under X (originally
+ripped from the Xaos fractal viewer program :-) ).
+
+Authors
+=======
+
+This program is free. If you like it, or have any suggestions, please
+send me (Paul Harrison) an email (pfh@yoyo.cc.monash.edu.au).
+
+Thanks to Asger Alstrup Nielsen for many great suggestions, and for
+writing optimized 32 bit loops for fading and drawing to screen.
+
+Thanks to Roger Knobbe for porting Synaesthesia to FreeBSD.
+
+Thanks to Ben Gertzfield and Martin Mitchell for some small fixes to the
+CD controlling code.
+
+Thanks to Simon Budig for an improvement to the X code.
+
+Changes
+=======
+
+1.1 - Added surround sound decoding.
+1.2 - Fixed a bug in the ioctl calls to /dev/dsp.
+1.3 - Asger Alstrup Nielsen's optimizations added.
+ Added X-Windows support.
+ More options, redesigned interface.
+1.4 - Bug fixes, including a great reduction in
+ "Sound: Recording overrun" warnings.
+ New command line options: play lists and piping.
+ Support for SDL.
+2.0 - Bug fixes: Fixed problem in xlib.c that caused occasional segfaults,
+ several endianness problems fixed.
+ New effects: Wave, heat, diamond shaped points.
+ Piping sound now longer requires the twiddle factor.
+ Yet another interface redesign.
+ Partial support for LinuxPPC (pipe mode only)
+
diff --git a/kscd/kscdmagic/core.cpp b/kscd/kscdmagic/core.cpp
new file mode 100644
index 00000000..49fce661
--- /dev/null
+++ b/kscd/kscdmagic/core.cpp
@@ -0,0 +1,410 @@
+/* Synaesthesia - program to display sound graphically
+ Copyright (C) 1997 Paul Francis Harrison
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The author may be contacted at:
+ pfh@yoyo.cc.monash.edu.au
+ or
+ 27 Bond St., Mt. Waverley, 3149, Melbourne, Australia
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include "syna.h"
+
+double cosTable[n], negSinTable[n];
+int bitReverse[n];
+int scaleDown[256];
+int maxStarRadius;
+
+int bitReverser(int i) {
+ int sum=0,j;
+ for(j=0;j<m;j++) {
+ sum = (i&1)+sum*2;
+ i >>= 1;
+ }
+ return sum;
+}
+
+void fft(double *x,double *y) {
+ int n2 = n, n1;
+ int twoToTheK;
+ for(twoToTheK=1;twoToTheK<n;twoToTheK*=2) {
+ n1 = n2;
+ n2 /= 2;
+ for(int j=0;j<n2;j++) {
+ double c = cosTable[j*twoToTheK&(n-1)],
+ s = negSinTable[j*twoToTheK&(n-1)];
+ for(int i=j;i<n;i+=n1) {
+ int l = i+n2;
+ double xt = x[i] - x[l];
+ x[i] = (x[i] + x[l]);
+ double yt = y[i] - y[l];
+ y[i] = (y[i] + y[l]);
+ x[l] = xt*c - yt*s;
+ y[l] = xt*s + yt*c;
+ }
+ }
+ }
+}
+
+void coreInit() {
+ int i;
+
+ for(i=0;i<n;i++) {
+ negSinTable[i] = -sin(3.141592*2.0/n*i);
+ cosTable[i] = cos(3.141592*2.0/n*i);
+ bitReverse[i] = bitReverser(i);
+ }
+}
+
+void setStarSize(double size) {
+ //int factor = (fadeMode == Flame ? 100 :
+ // (fadeMode == Wave ? 150 : 200));
+
+ double fadeModeFudge = (fadeMode == Wave ? 0.4 :
+ (fadeMode == Flame ? 0.6 : 0.78));
+
+ int factor;
+ if (size > 0.0)
+ factor = int(exp(log(fadeModeFudge) / (size*8.0))*255);
+ else
+ factor = 0;
+
+ if (factor > 255) factor = 255;
+
+ for(int i=0;i<256;i++)
+ scaleDown[i] = i*factor>>8;
+
+ maxStarRadius = 1;
+ for(int i=255;i;i = scaleDown[i])
+ maxStarRadius++;
+}
+
+inline void addPixel(int x,int y,int br1,int br2) {
+ if (x < 0 || x >= outWidth || y < 0 || y >= outHeight) return;
+
+ unsigned char *p = ucoutput+x*2+y*outWidth*2;
+ if (p[0] < 255-br1) p[0] += br1; else p[0] = 255;
+ if (p[1] < 255-br2) p[1] += br2; else p[1] = 255;
+ //p += lastOutput-output;
+ //if (p[0] < 255-br1) p[0] += br1; else p[0] = 255;
+ //if (p[1] < 255-br2) p[1] += br2; else p[1] = 255;
+}
+
+inline void addPixelFast(unsigned char *p,int br1,int br2) {
+ if (p[0] < 255-br1) p[0] += br1; else p[0] = 255;
+ if (p[1] < 255-br2) p[1] += br2; else p[1] = 255;
+ //p += lastOutput-output;
+ //if (p[0] < 255-br1) p[0] += br1; else p[0] = 255;
+ //if (p[1] < 255-br2) p[1] += br2; else p[1] = 255;
+}
+
+void fadeFade() {
+ register unsigned long *ptr = (unsigned long*)ucoutput;
+ int i = outWidth*outHeight*2/4;
+ do {
+ //Bytewize version was: *(ptr++) -= *ptr+(*ptr>>1)>>4;
+ if (*ptr)
+ //if (*ptr & 0xf0f0f0f0ul)
+ *(ptr++) -= ((*ptr & 0xf0f0f0f0ul) >> 4) + ((*ptr & 0xe0e0e0e0ul) >> 5);
+ //else {
+ // *(ptr++) = (*ptr * 14 >> 4) & 0x0f0f0f0ful;
+ //}
+ else
+ ptr++;
+ } while(--i > 0);
+}
+
+inline unsigned char getPixel(int x,int y,int where) {
+ if (x < 0 || y < 0 || x >= outWidth || y >= outHeight) return 0;
+ return lastOutput[where];
+}
+
+inline void fadePixelWave(int x,int y,int where,int step) {
+ short j =
+ ( short(getPixel(x-1,y,where-2))+
+ getPixel(x+1,y,where+2)+
+ getPixel(x,y-1,where-step)+
+ getPixel(x,y+1,where+step)
+ >> 2)
+ +lastOutput[where];
+ if (!j) { ucoutput[where] = 0; return; }
+ j = j
+ -lastLastOutput[where]
+ -1;
+ if (j < 0) ucoutput[where] = 0;
+ else if (j & (255*256)) ucoutput[where] = 255;
+ else ucoutput[where] = j;
+}
+
+void fadeWave() {
+ unsigned short *t = lastLastOutputBmp.data;
+ lastLastOutputBmp.data = lastOutputBmp.data;
+ lastOutputBmp.data = outputBmp.data;
+ outputBmp.data = t;
+
+ int x,y,i,j,start,end;
+ int step = outWidth*2;
+ for(x=0,i=0,j=outWidth*(outHeight-1)*2;x<outWidth;x++,i+=2,j+=2) {
+ fadePixelWave(x,0,i,step);
+ fadePixelWave(x,0,i+1,step);
+ fadePixelWave(x,outHeight-1,j,step);
+ fadePixelWave(x,outHeight-1,j+1,step);
+ }
+
+ for(y=1,i=outWidth*2,j=outWidth*4-2;y<outHeight;y++,i+=step,j+=step) {
+ fadePixelWave(0,y,i,step);
+ fadePixelWave(0,y,i+1,step);
+ fadePixelWave(outWidth-1,y,j,step);
+ fadePixelWave(outWidth-1,y,j+1,step);
+ }
+
+ for(y=1,
+ start=outWidth*2+2,
+ end=outWidth*4-2; y<outHeight-1; y++,start+=step,end+=step) {
+ int i = start;
+ do {
+ short j =
+ ( short(lastOutput[i-2])+
+ lastOutput[i+2]+
+ lastOutput[i-step]+
+ lastOutput[i+step]
+ >> 2)
+ +lastOutput[i];
+ if (!j) {
+ ucoutput[i] = 0;
+ } else {
+ j = j
+ -lastLastOutput[i]
+ -1;
+ if (j < 0) ucoutput[i] = 0;
+ else if (j & (255*256)) ucoutput[i] = 255;
+ else ucoutput[i] = j;
+ }
+ } while(++i < end);
+ }
+}
+
+inline void fadePixelHeat(int x,int y,int where,int step) {
+ short j =
+ ( short(getPixel(x-1,y,where-2))+
+ getPixel(x+1,y,where+2)+
+ getPixel(x,y-1,where-step)+
+ getPixel(x,y+1,where+step)
+ >> 2)
+ +lastOutput[where];
+ if (!j) { ucoutput[where] = 0; return; }
+ j = j
+ -lastLastOutput[where]
+ -1;
+ if (j < 0) ucoutput[where] = 0;
+ else if (j & (255*256)) ucoutput[where] = 255;
+ else ucoutput[where] = j;
+}
+
+void fadeHeat() {
+ unsigned short *t = lastLastOutputBmp.data;
+ lastLastOutputBmp.data = lastOutputBmp.data;
+ lastOutputBmp.data = outputBmp.data;
+ outputBmp.data = t;
+
+ int x,y,i,j,start,end;
+ int step = outWidth*2;
+ for(x=0,i=0,j=outWidth*(outHeight-1)*2;x<outWidth;x++,i+=2,j+=2) {
+ fadePixelHeat(x,0,i,step);
+ fadePixelHeat(x,0,i+1,step);
+ fadePixelHeat(x,outHeight-1,j,step);
+ fadePixelHeat(x,outHeight-1,j+1,step);
+ }
+
+ for(y=1,i=outWidth*2,j=outWidth*4-2;y<outHeight;y++,i+=step,j+=step) {
+ fadePixelHeat(0,y,i,step);
+ fadePixelHeat(0,y,i+1,step);
+ fadePixelHeat(outWidth-1,y,j,step);
+ fadePixelHeat(outWidth-1,y,j+1,step);
+ }
+
+ for(y=1,
+ start=outWidth*2+2,
+ end=outWidth*4-2; y<outHeight-1; y++,start+=step,end+=step) {
+ int i = start;
+ do {
+ short j =
+ ( short(lastOutput[i-2])+
+ lastOutput[i+2]+
+ +lastOutput[i-step]
+ +lastOutput[i+step]
+ >> 2)
+ +lastOutput[i];
+ if (!j) {
+ ucoutput[i] = 0;
+ } else {
+ j = j
+ -lastLastOutput[i]
+ +(lastLastOutput[i]
+ -lastOutput[i]>>2)
+ -1;
+ if (j < 0) ucoutput[i] = 0;
+ else if (j & (255*256)) ucoutput[i] = 255;
+ else ucoutput[i] = j;
+ }
+ } while(++i < end);
+ }
+}
+
+void fade() {
+ switch(fadeMode) {
+ case Stars :
+ fadeFade();
+ break;
+ case Flame :
+ fadeHeat();
+ break;
+ case Wave :
+ fadeWave();
+ break;
+ default:
+ break;
+ }
+}
+
+int coreGo() {
+ double x[n], y[n];
+ double a[n], b[n];
+ int clarity[n]; //Surround sound
+ int i,j,k;
+
+ int brightFactor = int(brightness * brightnessTwiddler /(starSize+0.01));
+
+ if (-1 == getNextFragment())
+ {
+ fprintf(stderr, "no frag\n" );
+ return -1;
+ }
+
+ for(i=0;i<n;i++) {
+ x[i] = data[i*2];
+ y[i] = data[i*2+1];
+ }
+
+ fft(x,y);
+
+ for(i=0 +1;i<n;i++) {
+ double x1 = x[bitReverse[i]],
+ y1 = y[bitReverse[i]],
+ x2 = x[bitReverse[n-i]],
+ y2 = y[bitReverse[n-i]],
+ aa,bb;
+ a[i] = sqrt(aa= (x1+x2)*(x1+x2) + (y1-y2)*(y1-y2) );
+ b[i] = sqrt(bb= (x1-x2)*(x1-x2) + (y1+y2)*(y1+y2) );
+ if (aa+bb != 0.0)
+ clarity[i] = (int)(
+ ( (x1+x2) * (x1-x2) + (y1+y2) * (y1-y2) )/(aa+bb) * 256 );
+ else
+ clarity[i] = 0;
+ }
+
+ // Asger Alstrupt's optimized 32 bit fade
+ // (alstrup@diku.dk)
+ /*register unsigned long *ptr = (unsigned long*)output;
+ i = outWidth*outHeight*2/4;
+ do {
+ //Bytewize version was: *(ptr++) -= *ptr+(*ptr>>1)>>4;
+ if (*ptr)
+ if (*ptr & 0xf0f0f0f0ul)
+ *(ptr++) -= ((*ptr & 0xf0f0f0f0ul) >> 4) + ((*ptr & 0xe0e0e0e0ul) >> 5);
+ else {
+ *(ptr++) = (*ptr * 14 >> 4) & 0x0f0f0f0ful;
+ //Should be 29/32 to be consistent. Who cares. This is totally
+ // hacked anyway.
+ //unsigned char *subptr = (unsigned char*)(ptr++);
+ //subptr[0] = (int)subptr[0] * 29 / 32;
+ //subptr[1] = (int)subptr[0] * 29 / 32;
+ //subptr[2] = (int)subptr[0] * 29 / 32;
+ //subptr[3] = (int)subptr[0] * 29 / 32;
+ }
+ else
+ ptr++;
+ } while(--i > 0);
+ */
+
+ int heightFactor = n/2 / outHeight + 1;
+ int actualHeight = n/2/heightFactor;
+ int heightAdd = outHeight + actualHeight >> 1;
+
+ /* Correct for window size */
+ double brightFactor2 = (brightFactor/65536.0/n)*
+ sqrt(actualHeight*outWidth/(320.0*200.0));
+
+ for(i=1;i<n/2;i++) {
+ //int h = (int)( b[i]*280 / (a[i]+b[i]+0.0001)+20 );
+ if (a[i] > 0 || b[i] > 0) {
+ int h = (int)( b[i]*outWidth / (a[i]+b[i]) );
+ int br1, br2, br = (int)(
+ (a[i]+b[i])*i*brightFactor2 );
+ br1 = br*(clarity[i]+128)>>8;
+ br2 = br*(128-clarity[i])>>8;
+ if (br1 < 0) br1 = 0; else if (br1 > 255) br1 = 255;
+ if (br2 < 0) br2 = 0; else if (br2 > 255) br2 = 255;
+ //unsigned char *p = output+ h*2+(164-((i<<8)>>m))*(outWidth*2);
+ int px = h,
+ py = heightAdd - i / heightFactor;
+
+ if (pointsAreDiamonds) {
+ addPixel(px,py,br1,br2);
+ br1=scaleDown[br1];br2=scaleDown[br2];
+
+ //TODO: Use addpixelfast
+ for(j=1;br1>0||br2>0;j++,br1=scaleDown[br1],br2=scaleDown[br2]) {
+ for(k=0;k<j;k++) {
+ addPixel(px-j+k,py-k,br1,br2);
+ addPixel(px+k,py-j+k,br1,br2);
+ addPixel(px+j-k,py+k,br1,br2);
+ addPixel(px-k,py+j-k,br1,br2);
+ }
+ }
+ } else {
+ if (px < maxStarRadius || py < maxStarRadius ||
+ px > outWidth-maxStarRadius || py > outHeight-maxStarRadius) {
+ addPixel(px,py,br1,br2);
+ for(j=1;br1>0||br2>0;j++,br1=scaleDown[br1],br2=scaleDown[br2]) {
+ addPixel(px+j,py,br1,br2);
+ addPixel(px,py+j,br1,br2);
+ addPixel(px-j,py,br1,br2);
+ addPixel(px,py-j,br1,br2);
+ }
+ } else {
+ unsigned char *p = ucoutput+px*2+py*outWidth*2, *p1=p, *p2=p, *p3=p, *p4=p;
+ addPixelFast(p,br1,br2);
+ for(;br1>0||br2>0;br1=scaleDown[br1],br2=scaleDown[br2]) {
+ p1 += 2;
+ addPixelFast(p1,br1,br2);
+ p2 -= 2;
+ addPixelFast(p2,br1,br2);
+ p3 += outWidth*2;
+ addPixelFast(p3,br1,br2);
+ p4 -= outWidth*2;
+ addPixelFast(p4,br1,br2);
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
diff --git a/kscd/kscdmagic/logo.h b/kscd/kscdmagic/logo.h
new file mode 100644
index 00000000..478f8c70
--- /dev/null
+++ b/kscd/kscdmagic/logo.h
@@ -0,0 +1,50 @@
+int logo[48][48] = {
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,17,0,0,0,17,17,0,0,17,0,0,0,17,0,17,17,17,0,0,0,17,0,0,0,17,17,0,0,17,17,17},
+{0,0,0,0,0,0,0,0,17,17,17,17,17,0,0,17,0,0,17,0,17,0,0,17,0,17,17,0,17,17,0,17,0,0,17,0,17,0,17,0,17,0,0,17,0,0,17,0},
+{0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,17,0,0,0,0,17,0,0,17,0,17,0,17,0,17,0,17,0,0,17,0,17,0,17,0,17,0,0,0,0,0,17,0},
+{0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,17,0,0,17,0,17,0,0,17,0,17,0,17,0,17,0,17,17,17,0,0,17,17,17,0,17,0,0,17,0,0,17,0},
+{0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,0,17,17,0,0,0,17,17,0,0,17,0,0,0,17,0,17,0,0,0,0,17,0,17,0,0,17,17,0,0,0,17,0},
+{0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,17,17,17,17,17,17,17,0,0,0,17,0,0,17,17,17,17,17,0,0,0,0,17,17,17,17,17,17,17,17,17,17,0,0,0,0,17,17,17,17,17,17,17,17,0,0},
+{0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,17,0,0,0,17,0,0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,0,0,0,0,0,0,17,0},
+{17,0,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17},
+{17,0,0,0,0,17,17,17,0,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,0,17,17,0,0,0,0,17},
+{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,17,17,17,17,17,17,17,17,0,0,17,0,0,0,17,0,0,17,0,0,0,17},
+{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17},
+{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,17,17,17,17,17,17,0,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17},
+{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,17,17,17,17},
+{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,17,0,0,0,0,0,0,0},
+{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,0,17,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,17,0,0,17,17,17,17,17},
+{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,0,0,17,17,17,17,17,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,17},
+{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,17},
+{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,17,17,17,17,17,17,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,17},
+{17,0,0,0,0,17,17,17,0,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,0,17,17,0,0,0,0,17},
+{17,0,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17},
+{0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,17,0,0,0,0,17,0,0,0,0,0,0,0,0,17,0},
+{0,0,17,17,17,17,17,17,17,17,17,17,17,0,0,17,17,17,17,17,0,0,17,17,17,17,17,17,17,17,17,17,0,0,0,0,0,0,17,17,17,17,17,17,17,17,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{17,17,17,0,0,17,0,0,17,17,17,0,17,0,17,17,17,0,0,17,0,0,17,0,0,0,0,0,0,17,0,0,17,0,0,17,0,17,17,17,0,0,17,0,0,17,17,0},
+{17,0,0,17,0,17,0,17,0,0,0,0,17,0,0,17,0,0,17,0,17,0,17,0,0,0,0,0,17,0,17,0,17,0,0,17,0,17,0,0,17,0,17,0,17,0,0,17},
+{17,0,0,17,0,17,0,17,0,17,17,0,17,0,0,17,0,0,17,0,17,0,17,0,0,0,0,0,17,0,17,0,17,0,0,17,0,17,0,0,17,0,17,0,17,0,0,17},
+{17,0,0,17,0,17,0,17,0,0,17,0,17,0,0,17,0,0,17,17,17,0,17,0,0,0,0,0,17,17,17,0,17,0,0,17,0,17,0,0,17,0,17,0,17,0,0,17},
+{17,17,17,0,0,17,0,0,17,17,17,0,17,0,0,17,0,0,17,0,17,0,17,17,17,0,0,0,17,0,17,0,0,17,17,0,0,17,17,17,0,0,17,0,0,17,17,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
+};
diff --git a/kscd/kscdmagic/magicconf.h b/kscd/kscdmagic/magicconf.h
new file mode 100644
index 00000000..86ed42de
--- /dev/null
+++ b/kscd/kscdmagic/magicconf.h
@@ -0,0 +1,23 @@
+
+#ifndef __MAGIC_CONF_H_
+#define __MAGIC_CONF_H_
+
+/***************************************/
+/* For the incurably fiddle prone: */
+
+/* log2 of sample size */
+#define m 8
+
+/* overlap amount between samples. Set to 1 or 2 if you have a fast computer */
+#define overlap 0
+
+/* Brightness */
+#define brightness 150
+
+/* Sample frequency*/
+#define frequency 22050
+
+#define MITSHM 1 /* use MIT X11 shared memory*/
+
+/***************************************/
+#endif
diff --git a/kscd/kscdmagic/main.cpp b/kscd/kscdmagic/main.cpp
new file mode 100644
index 00000000..7196d3a2
--- /dev/null
+++ b/kscd/kscdmagic/main.cpp
@@ -0,0 +1,300 @@
+/*
+
+ $Id$
+
+ kscdmagic 2.0 Dirk Frsterling <milliByte@gmx.de>
+
+ based on:
+
+ kscdmagic 1.0 Bernd Johannes Wuebben <wuebben@kde.org>
+
+ based on:
+
+ Synaesthesia - program to display sound graphically
+ Copyright (C) 1997 Paul Francis Harrison
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The author may be contacted at:
+ pfh@yoyo.cc.monash.edu.au
+ 27 Bond St., Mt. Waverley, 3149, Melbourne, Australia
+
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#if defined(__linux__) || defined(__svr4__) || defined(__osf__)
+
+#include <signal.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <string.h>
+#if defined(__linux__)
+#include <getopt.h>
+#endif
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <kapplication.h>
+#include <kaboutdata.h>
+#include <dcopclient.h>
+#include <kcmdlineargs.h>
+
+#include "logo.h"
+#include "magicconf.h"
+#include "syna.h"
+#include "version.h"
+
+volatile short *data;
+
+int outWidth, outHeight;
+double brightnessTwiddler;
+SymbolID fadeMode = Stars;
+double starSize = 0.125;
+bool pointsAreDiamonds = true;
+
+
+const int numRows = 4;
+const int rowHeight = 50;
+const int leftColWidth = 40;
+const int rowMaxWidth = 310;
+const int sliderBorder = 20;
+const int sliderWidth = rowMaxWidth - leftColWidth - sliderBorder*2;
+const int numberSpacing = 15;
+const int uiWidth = 330;
+const int uiHeight = 135;
+
+
+static int isExpanded = 0;
+static double bright = 1.0;
+
+Bitmap<unsigned short> outputBmp, lastOutputBmp, lastLastOutputBmp;
+PolygonEngine<unsigned short,combiner,2> polygonEngine;
+
+void
+allocOutput(int w,int h)
+{
+ outputBmp.size(w,h);
+ lastOutputBmp.size(w,h);
+ lastLastOutputBmp.size(w,h);
+ polygonEngine.size(w,h);
+ outWidth = w;
+ outHeight = h;
+} // allocOutput()
+
+void
+setBrightness(double bright)
+{
+ brightnessTwiddler = bright;
+} // setBrightness()
+
+
+
+static void
+cleanup( int sig )
+{
+ (void) sig;
+ closeSound();
+ exit(0);
+} // cleanup()
+
+
+// make sure the pid file is cleaned up when exiting unexpectedly.
+
+void
+catchSignals()
+{
+ signal(SIGHUP, cleanup); /* Hangup */
+ signal(SIGINT, cleanup); /* Interrupt */
+ signal(SIGTERM, cleanup); /* Terminate */
+ signal(SIGPIPE, cleanup);
+ signal(SIGQUIT, cleanup);
+} // catchSignals()
+
+void
+usage(char*)
+{
+ fprintf(stderr, "Valid command line options:\n");
+ fprintf(stderr, " -b set brightness (1 - 10)\n");
+ fprintf(stderr, " -w set width\n");
+ fprintf(stderr, " -h set height\n");
+ exit(1);
+} // usage()
+
+void
+error(const char *str, bool syscall) {
+ fprintf(stderr, PROGNAME ": Error %s\n",str);
+ if (syscall)
+ fprintf(stderr,"(reason for error: %s)\n",strerror(errno));
+ exit(1);
+} // error()
+
+void
+warning(const char *str, bool syscall) {
+ fprintf(stderr, PROGNAME ": Possible error %s\n",str);
+ if (syscall)
+ fprintf(stderr,"(reason for error: %s)\n",strerror(errno));
+} // warning()
+
+
+
+int
+processUserInput()
+{
+
+ int mouseX, mouseY, mouseButtons;
+ char keyHit;
+
+ inputUpdate(mouseX,mouseY,mouseButtons,keyHit);
+
+ if( keyHit == 'q' )
+ return -1;
+
+
+ if (sizeUpdate())
+ {
+ isExpanded = 0;
+ }
+
+ return 0;
+} // processUserInput()
+
+int
+main(int argc, char **argv)
+{
+ int windX=10;
+ int windY=30;
+ int windWidth=uiWidth;
+ int windHeight=uiHeight;
+ int c;
+ int xx, xy;
+ opterr = 0;
+
+ /*
+ KAboutData aboutData( "kscdmagic", I18N_NOOP("kscdmagic"),
+ KSCDMAGICVERSION, I18N_NOOP("sound visualisation"),
+ KAboutData::License_GPL,
+ "(c) 2000, Dirk Frsterling");
+ aboutData.addAuthor("Paul Harrison",0, "pfh@yoyo.cc.monash.edu.au");
+ aboutData.addAuthor("Dirk Frsterling",0, "milliByte@gmx.net");
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+
+ KApplication magicApp;
+ */
+
+
+ openSound(SourceCD, 44100, "/dev/dsp", NULL);
+
+ catchSignals();
+
+ while ((c = getopt(argc, argv, "b:h:w:")) != -1){
+ switch (c)
+ {
+ case '?':
+ fprintf(stderr, "%s: unknown option \"%s\"\n",
+ argv[0], argv[optind-1]);
+ usage(argv[0]);
+ exit(1);
+ case 'b':
+ bright = (double) atoi(optarg);
+ bright = bright/10;
+ break;
+ case 'w':
+ windWidth = atoi(optarg);
+ break;
+ case 'h':
+ windHeight = atoi(optarg);
+ break;
+ }
+ }
+
+ if (bright > 1.0)
+ bright = 1.0;
+ else if (bright < 0.0)
+ bright = 0.0;
+
+ if (windWidth < 1)
+ windWidth = uiWidth;
+ if (windHeight < 1)
+ windHeight = uiHeight;
+
+ screenInit(windX,windY,windWidth,windHeight);
+
+ allocOutput(outWidth,outHeight);
+
+ coreInit();
+
+
+ setStarSize(starSize);
+ setBrightness(bright);
+
+ time_t timer = time(NULL);
+
+ int frames = 0;
+
+ for(;;) {
+ fade();
+ if (-1 == coreGo())
+ break;
+
+ polygonEngine.clear();
+
+ for( xy = 0; xy < 48; xy++)
+ {
+ for( xx = 0; xx < 48; xx++)
+ {
+ if ( logo[xy][xx] != 0)
+ {
+ polygonEngine.add(32769, xx+10, xy+3);
+ }
+ }
+ }
+ polygonEngine.apply(outputBmp.data);
+ screenShow();
+
+
+
+ frames++;
+ if(processUserInput() == -1)
+ break;
+ }
+
+
+ timer = time(NULL) - timer;
+ delete ucoutput;
+ closeSound();
+
+ if (timer > 10)
+ fprintf(stderr,"Frames per second: %f\n", double(frames)/timer);
+
+ return 0;
+} // main() /* linux */
+
+#else
+
+int main()
+{
+ fprintf(stderr,"KSCD Magic works currently only on Linux.\n"\
+ "It should however be trivial to port it to other platforms ...\n");
+} // main() /* non-linux */
+
+#endif
+
diff --git a/kscd/kscdmagic/polygon.h b/kscd/kscdmagic/polygon.h
new file mode 100644
index 00000000..4337ab10
--- /dev/null
+++ b/kscd/kscdmagic/polygon.h
@@ -0,0 +1,106 @@
+#include <string.h>
+
+template<class Pixel,int extra=0>
+struct Bitmap {
+ Pixel *data;
+ int width, height;
+
+ Bitmap() : data(0) { };
+ ~Bitmap() { delete[] data; };
+
+ void size(int w,int h) {
+ delete[] data;
+ width = w;
+ height = h;
+ data = new Pixel[w*h+extra];
+ clear();
+ }
+
+ void clear() {
+ memset(data,0,sizeof(Pixel)*(width*height+extra));
+ }
+};
+
+template<class Pixel, Pixel combine(Pixel a,Pixel b), int superSampleShift>
+struct PolygonEngine : public Bitmap<Pixel,1> {
+#define super (1<<superSampleShift)
+ void apply(Pixel *dest) {
+ /* Pixel sum=0; */
+ int count = width*height;
+ Pixel *src = data;
+ /*
+ * I don't really want this.
+ *
+ while(count--) {
+ sum += *(src++);
+ if (sum)
+ *dest = combine(sum,*dest);
+ dest++;
+ }
+ */
+ while(count--)
+ {
+ *dest = combine(*src, *dest);
+ src++;
+ dest++;
+ }
+ }
+
+ void add(Pixel color,int x,int y) {
+ if (y < 0) return;
+ if (y >= height) return;
+ if (x < 0) x = 0;
+ if (x > width) x = width;
+ data[x+y*width] += color;
+ }
+
+ /* Color is char[layers] */
+
+ // zwoosh, yknow, it goes... zwoosh an all these bars and lines and
+ // crap intersect.
+ Pixel colorTable[2][super+1];
+ void pen(Pixel color) {
+ for(int i=0;i<super+1;i++) {
+ colorTable[0][i] = color*i;
+ colorTable[1][i] = -(color*i);
+ }
+ }
+
+ void line(int x1,int y1,int x2,int y2) {
+ Pixel *colors;
+ if (y2 < y1) {
+ int temp;
+ temp = x2; x2 = x1; x1 = temp;
+ temp = y2; y2 = y1; y1 = temp;
+ colors = colorTable[1];
+ } else {
+ if (y1 == y2) return;
+
+ colors= colorTable[0];
+ }
+
+ int slope = (x1-x2 << 16)/(y1-y2);
+ int x = x1<<16, y = y1;
+ while(y < y2) {
+ add(colors[super-((x>>16)&(super-1))],
+ x>>(16+superSampleShift),y>>superSampleShift);
+ add(colors[(x>>16)&(super-1)],
+ 1+(x>>(16+superSampleShift)),y>>superSampleShift);
+ x += slope;
+ y++;
+ }
+ }
+
+ void icon(double icon[][4],Pixel color,double x,double y,
+ double scaleX, double scaleY) {
+ pen(color);
+ x *= super;
+ y *= super;
+ scaleX *= super;
+ scaleY *= super;
+ for(int i=0;icon[i][1] != icon[i][3];i++)
+ line(int(icon[i][0]*scaleX+x),int(icon[i][1]*scaleY+y),
+ int(icon[i][2]*scaleX+x),int(icon[i][3]*scaleY+y));
+ }
+#undef super
+};
diff --git a/kscd/kscdmagic/sound.cpp b/kscd/kscdmagic/sound.cpp
new file mode 100644
index 00000000..0bcb8d8d
--- /dev/null
+++ b/kscd/kscdmagic/sound.cpp
@@ -0,0 +1,252 @@
+/* Synaesthesia - program to display sound graphically
+ Copyright (C) 1997 Paul Francis Harrison
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The author may be contacted at:
+ pfh@yoyo.cc.monash.edu.au
+ or
+ 27 Bond St., Mt. Waverley, 3149, Melbourne, Australia
+*/
+
+#if defined(__linux__) || defined(__svr4__)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+
+#if defined (__linux__)
+#include <linux/soundcard.h>
+#ifndef __GNUC__
+#define __GNUC__ 1
+#endif
+#undef __STRICT_ANSI__
+#include <asm/types.h>
+#include <linux/cdrom.h>
+#endif
+
+#if defined (__svr4__)
+#include <sys/soundcard.h>
+#endif
+
+
+// who knows when we'll need that...
+#if defined (FreeBSD)
+#include <sys/soundcard.h>
+#include <sys/cdio.h>
+#define CDROM_LEADOUT 0xAA
+#define CD_FRAMES 75 /* frames per second */
+#define CDROM_DATA_TRACK 0x4
+#endif
+
+#include <time.h>
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include "syna.h"
+#include "magicconf.h"
+
+
+/* Sound Recording ================================================= */
+
+#ifdef LITTLEENDIAN
+#define SOUNDFORMAT AFMT_S16_LE
+#else
+#define SOUNDFORMAT AFMT_S16_BE
+#endif
+
+//If kernel starts running out of sound memory playing mp3s, this could
+//be the problem. OTOH if it is too small, it will start ticking on slow
+//computers
+#define MAXWINDOWSIZE 32
+
+static SoundSource source;
+static int inFrequency, downFactor, windowSize, pipeIn, device;
+static short *dataIn;
+static char *mixer;
+
+void
+openSound(SoundSource source, int inFrequency, const char *dspName,
+ char *mixerName)
+{
+ ::source = source;
+ ::inFrequency = inFrequency;
+ ::windowSize = 1;
+ mixer = mixerName;
+ downFactor = inFrequency / frequency;
+ if (downFactor <= 0)
+ downFactor = 1;
+
+ int format, stereo, fragment, fqc;
+
+#ifdef __FreeBSD__
+ attempt(device = open(dspName,O_WRONLY),"opening dsp device",true);
+ format = SOUNDFORMAT;
+ attempt(ioctl(device,SNDCTL_DSP_SETFMT,&format),"setting format",true);
+ if (format != SOUNDFORMAT) error("setting format (2)");
+ close(device);
+#endif
+ if (source == SourcePipe)
+ attempt(device = open(dspName,O_WRONLY),"opening dsp device",true);
+ else
+ attempt(device = open(dspName,O_RDONLY),"opening dsp device",true);
+
+ //Probably not needed
+ //attemptNoDie(ioctl(device,SNDCTL_DSP_RESET,0),"reseting dsp");
+ format = SOUNDFORMAT;
+ fqc = (source == SourcePipe ? inFrequency : frequency);
+ stereo = 1;
+
+ //int logWindowSize = -1, tmp = windowSize*downFactor;
+ //while(tmp) {
+ // tmp /= 2;
+ // logWindowSize++;
+ //}
+
+ if (source == SourcePipe)
+ //fragment = 0x00020000 + (m-overlap+1)+logWindowSize;
+ fragment = 0x00010000*(MAXWINDOWSIZE+1) + (m-overlap+1);//+logWindowSize;
+ //Soundcard should read in windowSize
+ // blocks of sound before blocking
+ else
+ //fragment = 0x00020000 + (m-overlap+1); //2 fragments of size 2*(2^(m-overlap+1)) bytes
+
+ //Added extra fragments to allow recording overrun (9/7/98)
+ fragment = 0x00080000 + (m-overlap+1); //8 fragments of size 2*(2^(m-overlap+1)) bytes
+
+
+
+
+ //Was 0x00010000 + m;
+
+ attemptNoDie(ioctl(device,SNDCTL_DSP_SETFRAGMENT,&fragment),"setting fragment",true);
+#ifndef __FreeBSD__
+ attempt(ioctl(device,SNDCTL_DSP_SETFMT,&format),"setting format",true);
+ if (format != SOUNDFORMAT) error("setting format (2)");
+#endif
+ attempt(ioctl(device,SNDCTL_DSP_STEREO,&stereo),"setting stereo",true);
+ attemptNoDie(ioctl(device,SNDCTL_DSP_SPEED,&fqc),"setting frequency",true);
+
+ data = new short[n*2];
+
+ if (source == SourcePipe) {
+ dataIn = new short[n*2*downFactor*MAXWINDOWSIZE];
+ memset(dataIn,0,n*4*downFactor*MAXWINDOWSIZE);
+ pipeIn = dup(0);
+ close(0);
+ }
+}
+
+void closeSound() {
+ delete data;
+ if (source == SourcePipe) {
+ delete dataIn;
+ close(pipeIn);
+ }
+ close(device);
+}
+
+int readWholeBlock(int pipe,char *dest,int length) {
+ while(length > 0) {
+ int result = read(pipe,dest,length);
+ if (result < 1)
+ return -1;
+ dest += result;
+ length -= result;
+ }
+ return 0;
+}
+
+int getNextFragment(void) {
+ if (source == SourcePipe) {
+ static int lastTime = 0;
+ int nowTime;
+ timeval timeVal1, timeVal2;
+
+ gettimeofday(&timeVal1,0);
+ write(device, (char*)dataIn, n*4*downFactor*windowSize);
+ gettimeofday(&timeVal2,0);
+
+ nowTime = timeVal1.tv_usec + timeVal1.tv_sec * 1000000;
+ if (nowTime > lastTime) {
+ int optimumFrags =
+ int(double(nowTime-lastTime)*inFrequency/1000000.0/(n*downFactor))
+ +1;
+ if (optimumFrags > MAXWINDOWSIZE)
+ optimumFrags = MAXWINDOWSIZE;
+
+ windowSize = optimumFrags;
+ }
+
+ lastTime = timeVal2.tv_usec + timeVal2.tv_sec * 1000000;
+
+ if (readWholeBlock(pipeIn, ((char*)dataIn), n*4*downFactor*windowSize) == -1)
+ return -1;
+
+ int i,j;
+ for(i=0,j=0;i<n;i++,j+=downFactor)
+ ((long*)data)[i] = ((long*)dataIn)[j];
+ } else {
+ int i;
+ count_info info;
+ if (-1 == ioctl(device,SNDCTL_DSP_GETIPTR,&info))
+ info.blocks = 1;
+ if (info.blocks > 8 || info.blocks < 1) /* Sanity check */
+ info.blocks = 1;
+
+ for(i=0;i<info.blocks;i++) {
+ if (recSize != n)
+ memmove((char*)data,(char*)data+recSize*4,(n-recSize)*4);
+
+ attemptNoDie(
+ readWholeBlock(device,(char*)data+n*4-recSize*4, recSize*4),
+ "reading from soundcard", true);
+ }
+ }
+ return 0;
+}
+
+#else
+
+// generic dummy implementation
+
+#include "syna.h"
+
+int getNextFragment(void) {
+ return 0;
+}
+
+void openSound(SoundSource source, int inFrequency, const char *dspName,
+ char *mixerName)
+{
+}
+
+void closeSound()
+{
+}
+
+#endif // linux || svr4
+
diff --git a/kscd/kscdmagic/symbol.h b/kscd/kscdmagic/symbol.h
new file mode 100644
index 00000000..aad04f47
--- /dev/null
+++ b/kscd/kscdmagic/symbol.h
@@ -0,0 +1,1028 @@
+#ifndef __SYNAES_SYMBOL_H__
+#define __SYNAES_SYMBOL_H__
+#if defined(__linux__) || defined(__svr4__)
+
+#define SYMBOLSWIDTH 586
+#define SYMBOLSHEIGHT 50
+unsigned char Symbols[586*50] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,241,72,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,13,30,37,31,24,5,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,132,254,237,72,0,0,0,0,0,0,0,0,0,0,0,1,7,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,46,0,0,0,0,0,0,0,0,0,0,0,0,0,10,87,
+ 176,225,244,246,244,238,206,135,39,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,
+ 254,236,73,0,0,0,0,0,0,0,0,0,0,3,154,138,58,9,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,36,211,80,0,0,0,0,0,0,0,0,0,0,0,2,77,204,252,254,254,254,
+ 254,254,254,254,254,239,145,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255,245,
+ 215,72,0,0,0,0,0,0,0,0,0,0,115,254,249,213,138,58,8,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,36,212,253,80,0,0,0,0,0,0,0,0,0,0,7,136,247,254,254,254,254,
+ 254,254,254,254,254,254,254,254,208,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,
+ 255,225,66,210,72,0,0,0,0,0,0,0,0,0,40,244,254,254,254,249,214,137,57,8,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,37,212,254,254,80,0,0,0,0,0,0,0,0,0,4,150,253,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,227,50,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,132,254,255,225,10,55,217,71,0,0,0,0,0,0,0,0,4,194,254,255,255,
+ 255,255,254,250,214,137,57,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,211,254,254,254,80,0,0,
+ 0,0,0,0,0,0,0,100,250,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,203,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255,225,10,1,196,187,1,
+ 0,0,0,0,0,0,0,0,115,254,255,255,255,255,255,254,254,254,249,212,137,57,
+ 10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,36,211,254,254,254,254,80,0,0,0,0,0,0,0,0,29,230,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,255,255,254,130,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,41,244,255,255,
+ 255,255,255,255,255,255,254,254,254,249,212,137,58,8,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,212,254,254,254,
+ 254,254,80,0,0,0,0,0,0,0,0,134,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,255,255,255,232,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255,
+ 225,10,1,196,190,1,0,0,0,0,0,0,0,0,4,196,254,255,255,255,255,255,255,255,
+ 255,255,255,255,254,254,249,212,137,57,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,212,254,254,254,254,254,254,80,0,0,
+ 0,0,0,0,0,16,225,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,255,255,255,254,112,0,0,0,0,0,0,0,3,12,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,
+ 0,0,0,0,0,0,7,9,0,0,0,0,0,0,0,0,0,0,0,0,111,155,155,111,0,0,0,16,148,155,
+ 155,58,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,132,254,
+ 255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,115,253,254,255,255,255,255,255,
+ 255,255,255,255,255,255,255,254,254,254,250,194,15,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,7,147,99,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,38,179,
+ 38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,211,254,254,254,254,254,
+ 254,254,80,0,0,0,0,0,0,0,62,251,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,255,255,255,254,182,1,0,0,0,0,0,0,18,171,19,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,1,162,44,0,0,0,0,0,37,159,11,0,0,0,0,0,0,0,0,0,0,1,182,254,
+ 254,182,1,0,0,27,243,254,254,96,0,0,0,0,0,0,0,0,0,0,0,23,136,1,0,0,0,0,
+ 0,68,92,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,
+ 40,244,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,254,
+ 232,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,151,253,245,99,1,0,0,
+ 6,50,106,155,166,174,166,162,115,52,11,0,0,0,38,213,254,213,39,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,210,254,254,254,254,254,254,254,254,80,
+ 0,0,0,0,0,0,0,108,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,255,255,255,254,223,9,0,0,0,0,0,0,18,239,185,19,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,1,197,219,45,0,0,0,0,38,246,166,11,0,0,0,0,0,0,0,0,0,1,182,254,254,
+ 182,1,0,0,27,243,254,254,96,0,0,0,0,0,0,0,0,0,0,23,192,196,1,0,0,0,0,68,
+ 233,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,
+ 4,196,254,255,255,255,255,255,255,255,255,255,255,255,255,255,254,232,65,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,180,252,255,254,244,95,10,97,
+ 197,248,254,254,255,255,255,254,254,248,210,118,20,36,212,254,255,254,213,
+ 39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,211,254,254,254,254,254,254,254,
+ 254,254,80,0,0,0,0,0,0,0,117,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,255,255,255,255,228,11,0,0,0,0,0,0,18,240,254,185,
+ 19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,0,0,0,0,0,0,0,1,197,254,218,44,0,0,0,38,247,253,165,11,0,0,0,0,0,0,
+ 0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,0,0,0,0,0,0,0,0,23,192,
+ 254,196,1,0,0,0,67,234,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,
+ 1,0,0,0,0,0,0,0,0,0,0,116,254,255,255,255,255,255,255,255,255,255,255,255,
+ 255,254,233,65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,186,254,255,
+ 254,254,225,74,34,220,254,254,255,255,255,255,255,255,255,254,254,248,95,
+ 44,205,254,254,255,254,213,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,211,254,
+ 254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,122,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,255,255,255,255,228,
+ 11,0,0,0,0,0,0,18,240,254,254,186,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 17,187,197,197,197,197,197,195,46,0,0,0,0,0,0,0,142,197,197,197,197,197,
+ 197,107,0,0,0,0,0,0,0,81,196,197,197,197,197,197,197,197,197,197,197,197,
+ 197,197,197,197,197,197,197,197,197,194,37,0,0,0,0,0,0,1,197,254,254,219,
+ 44,0,0,38,247,254,253,165,11,0,0,0,0,0,0,0,1,182,254,254,182,1,0,0,27,243,
+ 254,254,96,0,0,0,0,0,0,0,0,22,192,254,254,196,1,0,0,66,235,254,254,111,
+ 0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,39,246,
+ 254,255,255,255,255,255,255,255,255,255,255,254,234,64,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,17,187,254,255,254,254,204,34,0,0,47,222,254,254,
+ 255,255,255,255,255,255,254,248,113,2,0,18,175,254,254,255,254,214,36,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,36,211,254,254,254,254,254,254,254,254,254,254,
+ 254,80,0,0,0,0,0,0,0,114,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,255,255,255,254,227,11,0,0,0,0,0,0,18,240,255,254,254,
+ 185,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,
+ 0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,251,47,0,0,0,0,0,0,1,197,254,254,254,220,43,0,38,247,254,254,253,166,
+ 11,0,0,0,0,0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,0,0,0,0,0,0,
+ 22,193,254,254,254,196,1,0,67,234,254,255,254,111,0,0,0,0,0,0,0,132,254,
+ 255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,3,196,254,255,255,255,255,255,
+ 255,255,255,255,255,236,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,
+ 211,254,255,254,204,30,0,0,0,0,47,221,254,255,255,255,255,255,254,243,112,
+ 2,0,0,0,14,175,253,255,254,231,54,0,0,0,0,0,0,1,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,5,5,5,1,
+ 0,0,0,0,0,0,0,0,0,0,0,5,5,5,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,210,254,255,
+ 254,254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,74,253,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,255,255,255,254,
+ 194,2,0,0,0,0,0,0,18,240,255,255,255,254,184,19,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,23,242,255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,
+ 254,254,138,0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,
+ 254,254,219,44,38,247,254,254,254,253,166,11,0,0,0,0,0,1,182,254,254,182,
+ 1,0,0,27,243,254,254,96,0,0,0,0,0,0,23,192,254,254,254,254,196,1,68,233,
+ 254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,
+ 0,0,0,0,0,0,0,0,116,254,255,255,255,255,255,255,255,255,255,255,231,53,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,210,254,225,33,0,0,0,0,0,
+ 0,48,221,254,254,255,255,254,237,78,1,0,0,0,0,0,15,198,254,231,61,0,0,0,
+ 0,0,0,0,73,213,214,214,214,214,214,214,214,213,214,214,214,214,214,214,
+ 214,214,214,214,214,214,214,214,213,214,214,214,214,214,214,214,214,214,
+ 156,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,8,145,213,214,213,133,5,0,0,0,0,0,0,0,0,0,88,211,213,
+ 214,181,24,0,0,0,0,3,11,11,11,11,11,11,11,40,210,254,255,255,254,254,254,
+ 254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,26,235,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,255,255,255,254,131,0,0,0,0,
+ 0,0,0,18,240,255,255,255,254,254,184,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,
+ 242,255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,
+ 138,0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,
+ 254,218,83,247,254,254,254,254,253,165,11,0,0,0,0,1,182,254,254,182,1,0,
+ 0,27,243,254,254,96,0,0,0,0,0,23,191,254,254,254,254,254,197,69,233,254,
+ 254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,
+ 0,0,0,0,0,0,0,0,40,245,255,255,255,255,255,255,255,255,255,255,254,226,
+ 52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,205,79,0,0,0,0,0,0,0,
+ 0,47,222,254,254,254,238,76,0,0,0,0,0,0,0,0,45,212,62,0,0,0,0,0,0,0,0,87,
+ 253,185,168,168,168,168,168,171,243,249,175,168,204,254,255,254,220,168,
+ 170,239,243,171,168,220,254,195,168,168,168,168,168,169,236,185,1,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,11,11,11,5,
+ 0,0,0,0,0,0,0,0,0,0,7,11,11,11,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,11,11,11,11,4,0,0,0,0,0,0,0,0,0,0,8,11,11,11,8,0,0,0,0,0,
+ 0,0,0,0,0,4,11,11,11,11,1,0,0,0,0,0,0,0,0,0,1,11,11,11,11,4,0,0,0,0,0,0,
+ 0,0,0,0,8,11,11,11,7,0,0,0,0,0,0,0,0,0,0,5,11,11,11,10,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,151,252,254,254,254,251,136,2,0,
+ 0,0,0,0,0,0,88,242,254,224,246,254,194,22,0,0,0,64,228,229,229,229,229,
+ 229,229,239,254,254,254,254,254,254,254,254,254,254,254,254,254,254,80,
+ 0,0,0,0,0,0,0,1,163,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,255,255,255,244,45,0,0,0,0,0,0,0,18,240,255,255,255,255,254,
+ 254,185,18,0,0,0,0,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,
+ 0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,237,253,254,254,254,
+ 254,254,253,165,11,0,0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,0,
+ 0,0,22,191,254,254,254,254,254,254,233,235,254,254,254,255,255,254,111,
+ 0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,4,196,
+ 255,255,255,255,255,255,255,255,255,255,255,254,225,52,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,4,0,0,0,0,0,0,0,0,0,48,222,254,239,77,0,
+ 0,0,0,0,0,0,0,0,1,24,0,0,0,0,0,0,0,0,0,87,251,50,0,0,0,0,0,10,223,251,67,
+ 0,53,246,255,253,99,0,31,241,222,9,0,153,253,80,0,0,0,0,0,2,200,185,1,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,87,224,229,
+ 229,229,179,18,0,0,0,0,0,0,0,0,37,203,229,229,229,214,55,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,98,226,229,229,229,170,14,0,0,0,0,0,0,
+ 0,0,45,208,229,229,229,209,47,0,0,0,0,0,0,0,0,13,167,229,229,229,227,101,
+ 1,0,0,0,0,0,0,0,1,109,227,229,229,229,161,11,0,0,0,0,0,0,0,0,52,213,229,
+ 229,229,205,39,0,0,0,0,0,0,0,0,16,177,229,229,229,225,90,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65,251,254,255,255,255,255,238,15,
+ 0,0,0,0,0,0,11,225,254,207,34,104,247,253,80,0,0,0,71,253,255,255,255,255,
+ 254,254,254,255,255,255,255,254,254,254,254,254,254,254,254,254,254,80,
+ 0,0,0,0,0,0,0,0,49,243,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,255,255,254,161,1,0,0,0,0,0,0,0,18,240,255,255,255,255,254,
+ 254,254,185,18,0,0,0,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,
+ 0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,253,165,11,0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,
+ 0,0,22,192,254,254,254,254,254,254,254,254,254,254,254,254,255,255,254,
+ 111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,
+ 0,116,253,254,255,255,255,254,235,219,254,255,255,255,254,226,53,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,21,21,21,21,21,21,21,21,21,
+ 21,21,21,21,21,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,95,114,2,0,0,0,0,0,
+ 0,0,0,0,47,207,76,0,0,0,0,0,0,0,0,0,0,80,130,0,0,0,0,0,0,0,0,0,87,251,50,
+ 0,0,0,0,0,9,223,254,161,0,5,193,254,235,28,0,116,254,222,9,0,153,254,79,
+ 0,0,0,0,0,1,200,185,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,88,242,254,254,255,255,254,185,18,0,0,0,0,0,0,37,214,254,254,
+ 255,255,254,228,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,98,246,254,
+ 254,255,255,254,175,14,0,0,0,0,0,0,44,220,254,254,255,255,254,222,47,0,
+ 0,0,0,0,0,13,172,254,254,255,255,254,246,101,1,0,0,0,0,0,1,109,248,254,
+ 254,255,255,253,165,11,0,0,0,0,0,0,53,226,254,254,255,255,254,216,39,0,
+ 0,0,0,0,0,17,182,254,254,255,255,254,243,91,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,69,253,254,255,255,255,255,239,16,0,0,0,0,0,0,12,
+ 230,212,32,0,1,113,253,81,0,0,0,71,253,254,255,255,255,254,255,255,255,
+ 255,254,255,254,254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,0,
+ 2,141,253,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 229,36,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,255,254,254,184,19,0,
+ 0,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,
+ 254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,251,47,0,0,0,0,
+ 0,0,1,197,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,253,
+ 165,11,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,0,22,192,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,255,255,254,111,0,0,0,0,0,0,0,132,
+ 254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,41,244,254,255,255,254,
+ 233,66,37,212,254,255,255,255,254,226,53,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,108,240,241,241,241,241,241,241,241,241,241,241,241,241,
+ 241,224,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,199,249,115,2,0,0,0,0,0,0,0,
+ 29,99,119,113,55,4,0,0,0,0,0,0,0,80,240,230,22,0,0,0,0,0,0,0,0,87,251,50,
+ 0,0,0,0,0,9,223,255,228,18,0,97,253,165,1,4,196,254,222,9,0,153,254,79,
+ 0,0,0,0,0,1,200,185,1,0,0,0,0,0,0,0,0,0,0,0,0,0,8,53,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,6,176,254,255,255,255,255,254,242,110,18,0,0,0,0,0,106,250,255,
+ 255,255,255,254,252,154,44,0,0,0,0,10,51,0,0,0,0,0,0,0,3,56,2,0,0,0,1,64,
+ 193,254,255,255,255,255,254,237,57,0,0,0,0,0,39,143,251,255,255,255,255,
+ 254,251,123,0,0,0,0,0,0,54,236,254,255,255,255,254,254,196,66,1,0,0,0,2,
+ 69,202,254,255,255,255,255,254,234,98,11,0,0,0,0,43,151,252,255,255,255,
+ 255,254,251,137,35,0,0,0,0,17,108,241,254,255,255,255,254,254,187,61,1,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,69,253,255,255,255,255,255,
+ 239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,71,253,254,255,255,
+ 255,254,255,255,254,255,254,255,254,254,254,254,254,254,254,254,254,254,
+ 80,0,0,0,0,0,0,0,0,0,13,192,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,244,84,0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,255,
+ 255,254,254,184,19,0,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,
+ 0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,253,165,11,1,182,254,254,182,1,0,0,27,243,254,254,96,
+ 0,23,191,254,254,254,254,254,254,254,254,254,254,254,254,254,254,255,255,
+ 254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,
+ 0,0,0,4,196,254,255,254,233,66,0,0,37,211,254,255,255,255,254,225,34,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,219,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,254,135,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,53,
+ 248,254,248,115,2,0,0,0,0,3,112,186,152,158,153,173,163,23,0,0,0,0,0,80,
+ 240,254,253,85,0,0,0,0,0,0,0,0,87,251,50,0,5,36,37,37,45,228,255,252,79,
+ 0,20,226,73,0,40,244,254,222,9,0,153,253,105,37,15,0,1,32,38,208,185,1,
+ 0,0,0,0,0,0,0,0,0,0,0,0,8,150,240,86,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,186,
+ 253,253,253,253,242,144,228,183,19,0,0,0,0,3,126,249,253,253,253,251,170,
+ 192,227,55,0,0,10,161,237,76,0,0,0,0,0,3,123,243,112,2,0,1,98,241,163,201,
+ 253,253,253,253,238,78,0,0,0,0,0,45,219,201,162,250,253,253,253,250,140,
+ 6,0,0,0,0,0,0,0,75,237,253,253,253,253,203,161,242,102,1,0,2,109,243,157,
+ 207,253,253,253,253,234,143,236,164,11,0,0,52,225,194,169,251,253,253,253,
+ 250,158,206,214,40,0,0,17,181,229,143,241,253,253,253,253,196,167,240,91,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,69,253,255,255,255,255,255,
+ 239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,71,253,254,255,255,
+ 255,254,254,255,254,255,254,254,254,254,254,254,254,254,254,254,254,254,
+ 80,0,0,0,0,0,0,0,0,0,0,24,188,253,254,254,254,254,254,254,254,254,254,254,
+ 254,254,237,97,1,0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,255,255,254,
+ 254,254,184,18,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,0,0,
+ 0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,253,164,12,182,254,254,182,1,0,0,27,243,254,254,96,
+ 22,191,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,255,
+ 255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,116,254,254,233,66,0,0,0,0,37,212,254,255,254,253,161,9,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109,253,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,234,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109,
+ 254,254,254,248,115,2,0,0,0,113,161,193,249,253,252,224,149,171,14,0,0,
+ 0,80,239,254,255,254,154,0,0,0,0,0,0,0,0,87,251,50,0,38,240,247,247,247,
+ 253,255,254,165,1,0,115,12,0,121,254,255,222,9,0,153,254,249,247,102,0,
+ 8,214,247,253,185,1,0,0,0,0,0,0,0,0,0,0,0,2,145,252,254,241,58,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,17,67,68,68,68,93,228,254,254,171,2,0,0,0,0,3,59,68,
+ 68,68,73,190,254,254,226,27,3,156,253,254,237,48,0,0,0,0,115,250,254,247,
+ 86,0,87,245,254,251,135,67,68,68,68,46,0,0,0,0,0,34,219,254,254,192,67,
+ 68,68,68,62,6,0,0,0,0,0,0,0,0,0,45,68,68,68,68,149,251,254,245,74,0,99,
+ 247,254,250,126,67,68,68,68,105,237,254,253,146,0,41,225,254,254,182,66,
+ 68,68,68,78,205,254,254,211,15,7,179,254,254,229,78,68,68,68,68,158,252,
+ 254,243,63,0,0,0,0,0,0,0,0,0,0,1,3,0,0,0,0,0,0,0,0,0,0,69,253,254,254,254,
+ 254,254,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,71,253,254,
+ 255,255,255,254,255,255,255,255,255,255,254,254,254,254,254,254,254,254,
+ 254,254,80,0,0,0,0,0,0,0,0,0,0,0,11,133,239,254,254,254,254,254,254,254,
+ 254,254,252,200,55,0,0,0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,255,
+ 255,255,255,254,254,185,17,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,
+ 60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,253,165,193,254,254,182,1,0,0,27,243,254,
+ 254,118,191,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,41,244,234,65,0,0,0,0,0,0,36,213,254,253,161,10,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,219,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,254,135,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 159,254,255,255,254,248,115,2,0,31,186,194,254,255,255,254,254,238,162,
+ 105,0,0,80,239,254,255,255,255,201,3,0,0,0,0,0,0,0,87,251,50,0,16,102,116,
+ 246,254,255,255,255,236,26,0,10,0,7,208,254,255,222,9,0,153,255,255,254,
+ 106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,5,213,254,255,254,88,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,91,254,255,255,211,4,0,0,0,0,0,0,0,
+ 0,0,23,241,254,255,250,46,9,223,254,255,253,75,0,0,0,1,184,254,255,254,
+ 122,0,152,254,255,255,154,0,0,0,0,0,0,0,0,0,0,73,253,254,255,224,10,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,197,254,255,254,108,0,166,254,255,254,
+ 140,0,0,0,0,118,254,255,255,187,1,87,254,255,255,214,6,0,0,0,43,249,254,
+ 255,243,26,20,240,254,255,252,49,0,0,0,3,208,254,255,254,95,0,0,0,0,0,0,
+ 0,0,0,68,189,198,107,5,0,0,0,0,0,0,0,0,69,252,144,104,105,104,183,239,16,
+ 0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,71,253,254,255,255,255,254,
+ 255,255,255,255,255,255,254,254,254,254,254,254,254,254,254,254,80,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,37,147,225,249,254,254,253,252,242,193,87,8,0,0,0,
+ 0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,255,255,255,255,255,254,254,
+ 184,18,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,
+ 254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,251,47,0,0,0,0,
+ 0,0,1,197,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,252,254,254,182,1,0,0,27,243,254,254,242,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,255,255,254,111,0,0,
+ 0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,4,176,
+ 65,0,0,0,0,0,0,0,0,36,211,160,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,109,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,233,29,0,0,0,0,0,0,0,0,0,0,0,0,0,170,255,255,255,255,255,249,115,
+ 2,103,151,250,255,255,255,255,255,254,169,172,3,80,240,254,255,255,255,
+ 255,211,5,0,0,0,0,0,0,0,87,251,50,0,0,0,19,240,255,255,255,255,253,94,0,
+ 0,0,53,248,255,255,222,9,0,153,255,255,254,106,0,8,220,255,255,185,1,0,
+ 0,0,0,0,0,0,0,0,0,0,5,213,255,255,254,88,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,91,254,255,255,211,4,0,0,0,0,0,0,0,0,0,23,242,255,255,250,46,9,
+ 223,255,255,253,75,0,0,0,1,184,255,255,254,122,0,153,255,255,255,154,0,
+ 0,0,0,0,0,0,0,0,0,74,253,255,255,224,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,1,197,255,255,254,108,0,166,255,255,254,140,0,0,0,0,118,254,255,255,
+ 187,1,87,254,255,255,214,6,0,0,0,43,249,255,255,243,26,20,240,255,255,252,
+ 49,0,0,0,3,209,255,255,254,95,0,0,0,0,0,0,0,0,34,233,254,254,251,82,0,0,
+ 0,0,0,0,0,0,69,252,67,0,0,0,133,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,
+ 252,81,0,0,0,71,253,254,255,255,254,254,254,255,254,255,254,254,254,254,
+ 254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,52,
+ 83,84,84,73,32,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,
+ 255,255,255,255,255,254,254,254,156,2,0,0,0,0,0,0,23,242,255,255,255,255,
+ 255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,
+ 104,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,182,1,0,0,27,
+ 243,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,26,10,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,218,254,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,254,135,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,178,255,255,255,255,255,255,249,112,123,158,254,255,255,255,255,255,254,
+ 187,177,81,240,254,255,255,255,255,255,211,5,0,0,0,0,0,0,0,87,251,50,0,
+ 0,0,19,240,255,255,255,255,254,161,0,0,0,116,254,255,255,222,9,0,153,255,
+ 255,254,106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,5,213,255,255,254,
+ 88,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,91,254,255,255,211,4,0,0,0,0,0,
+ 0,0,0,0,23,242,255,255,250,46,9,223,255,255,253,75,0,0,0,1,184,255,255,
+ 254,122,0,153,255,255,255,154,0,0,0,0,0,0,0,0,0,0,74,253,255,255,224,10,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,197,255,255,254,108,0,166,255,255,
+ 254,140,0,0,0,0,118,254,255,255,187,1,87,254,255,255,214,6,0,0,0,43,249,
+ 255,255,243,26,20,240,255,255,252,49,0,0,0,3,209,255,255,254,95,0,0,0,0,
+ 0,0,0,0,72,253,255,254,254,136,0,0,0,0,0,0,0,0,69,252,121,73,73,73,168,
+ 239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,71,253,255,255,255,
+ 255,254,255,255,255,255,255,255,254,254,254,254,254,254,254,254,254,254,
+ 80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,18,240,255,255,255,255,255,255,255,255,255,255,255,254,218,43,0,0,
+ 0,0,0,0,0,23,242,255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,
+ 254,254,254,138,0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,182,1,0,0,27,243,254,254,252,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,255,255,254,111,0,0,0,0,0,0,0,132,
+ 254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,108,251,253,253,253,
+ 253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,230,29,
+ 0,0,0,0,0,0,0,0,0,0,0,0,170,255,255,255,255,255,254,248,112,120,153,253,
+ 255,255,255,255,255,254,181,176,81,240,254,255,255,255,255,255,211,5,0,
+ 0,0,0,0,0,0,87,251,50,0,0,0,19,240,255,255,255,255,254,113,0,0,0,69,251,
+ 255,255,222,9,0,153,255,255,254,106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,
+ 0,0,0,0,5,211,254,255,254,88,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,89,254,
+ 255,255,211,4,0,0,0,0,0,0,1,1,1,23,240,255,255,250,45,9,221,254,255,253,
+ 75,1,1,1,2,181,254,255,254,122,0,150,254,255,254,154,1,1,1,1,0,0,0,0,0,
+ 0,71,253,255,255,224,10,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,194,254,255,
+ 254,108,0,163,254,255,254,140,1,1,1,1,116,254,255,254,187,1,84,253,255,
+ 255,214,7,1,1,1,42,248,255,255,242,26,19,238,255,255,252,49,0,0,0,3,206,
+ 254,255,254,94,0,0,0,0,0,0,0,0,47,244,254,254,253,104,0,0,0,0,0,0,0,0,69,
+ 253,252,252,252,252,253,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,
+ 0,0,71,253,254,255,255,255,254,255,255,255,255,255,255,254,254,254,254,
+ 254,254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,3,81,94,94,94,94,94,
+ 94,94,94,94,93,19,0,0,0,0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,255,
+ 255,255,255,255,254,219,43,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,
+ 60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,205,208,254,254,182,1,0,0,27,243,254,
+ 254,145,224,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,11,134,158,158,158,158,158,158,158,158,158,158,158,158,
+ 158,158,158,158,158,158,158,158,158,66,0,0,0,0,0,0,0,0,0,0,0,0,167,254,
+ 255,255,255,254,248,115,2,62,170,229,254,254,255,255,255,251,159,145,1,
+ 80,240,254,255,255,255,255,208,4,0,0,0,0,0,0,0,87,251,50,0,14,91,106,245,
+ 254,255,255,255,243,39,0,2,0,14,222,255,255,222,9,0,153,255,255,254,106,
+ 0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,0,90,243,254,214,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,54,184,190,190,190,155,195,254,250,124,1,0,0,0,0,17,
+ 161,190,190,190,176,157,250,254,187,14,1,101,246,254,210,142,190,190,190,
+ 188,140,235,254,229,52,0,46,222,254,240,136,187,190,190,190,127,4,0,0,0,
+ 0,14,174,254,252,162,169,190,190,190,167,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 75,239,254,224,43,0,54,227,254,236,135,187,190,190,190,148,208,254,247,
+ 100,0,17,184,254,251,156,173,190,190,190,169,168,253,253,167,7,2,123,250,
+ 254,190,15,0,0,0,0,85,242,254,218,35,0,0,0,0,0,0,0,0,1,115,233,237,163,
+ 14,0,0,0,0,0,0,0,0,69,252,114,63,63,63,164,239,16,0,0,0,0,0,0,12,230,146,
+ 0,0,0,55,252,81,0,0,0,71,253,254,255,255,255,254,255,255,254,255,254,255,
+ 254,254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,8,217,
+ 250,250,250,250,250,250,250,250,250,248,51,0,0,0,0,0,0,0,0,0,0,0,0,18,240,
+ 255,255,255,255,255,255,255,255,255,254,218,43,0,0,0,0,0,0,0,0,0,23,242,
+ 255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,
+ 0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,204,31,182,254,254,
+ 182,1,0,0,27,243,254,254,96,49,223,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,
+ 1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,245,252,250,250,250,250,250,250,
+ 250,250,250,250,250,250,250,250,250,250,250,250,250,254,127,0,0,0,0,0,0,
+ 0,0,0,0,0,0,121,254,255,255,254,248,115,2,0,5,170,149,242,254,254,254,252,
+ 181,184,44,0,0,80,239,254,254,255,254,166,0,0,0,0,0,0,0,0,87,251,50,0,38,
+ 243,250,250,250,254,254,254,187,4,0,82,6,0,144,254,254,222,9,0,153,255,
+ 255,254,106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,0,0,90,204,37,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,55,227,254,254,255,255,253,168,191,126,3,0,0,
+ 0,0,17,184,254,254,255,255,254,207,157,184,19,0,0,1,101,203,155,250,254,
+ 254,255,254,238,141,211,57,0,0,0,47,207,142,233,254,254,255,255,252,137,
+ 5,0,0,0,0,14,173,164,199,254,254,255,255,254,195,24,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,76,208,49,0,0,0,55,210,141,237,254,254,255,254,250,157,202,104,
+ 1,0,0,17,182,159,205,254,254,255,255,254,194,168,166,12,0,0,3,123,186,21,
+ 0,0,0,0,0,0,86,205,41,0,0,0,0,0,0,0,0,0,0,0,15,16,1,0,0,0,0,0,0,0,0,0,69,
+ 252,67,0,0,0,133,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,71,
+ 253,254,255,254,254,254,254,254,254,255,254,254,254,254,254,254,254,254,
+ 254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,1,38,44,44,44,44,44,44,44,44,
+ 44,44,9,0,0,0,0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,255,255,255,
+ 254,218,44,0,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,0,0,0,
+ 0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,251,
+ 47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,204,30,1,182,254,254,182,1,0,0,27,243,254,254,96,0,49,223,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,255,255,254,111,
+ 0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,
+ 245,137,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,62,243,127,
+ 0,0,0,0,0,0,0,0,0,0,0,0,65,251,254,254,248,115,2,0,0,0,27,177,159,174,192,
+ 185,159,183,80,0,0,0,0,80,239,254,254,254,99,0,0,0,0,0,0,0,0,87,251,50,
+ 0,6,43,44,44,52,229,254,253,98,0,11,209,53,0,56,249,254,222,9,0,153,255,
+ 255,254,106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,0,0,3,37,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,31,192,254,255,255,255,255,254,248,85,3,0,0,0,0,0,
+ 118,253,255,255,255,255,254,254,159,31,0,0,0,0,1,63,241,254,255,255,255,
+ 254,254,215,54,0,0,0,0,0,24,201,254,255,255,255,255,254,245,85,5,0,0,0,
+ 0,26,146,253,255,255,255,255,254,253,144,18,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 38,0,0,0,0,0,53,212,254,255,255,255,255,254,242,89,5,0,0,0,0,11,148,254,
+ 255,255,255,255,254,253,139,24,0,0,0,0,9,31,0,0,0,0,0,0,0,2,37,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,69,252,67,0,0,0,133,239,16,0,0,
+ 0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,71,253,254,255,255,255,254,255,
+ 255,255,255,255,255,254,254,254,254,254,254,254,254,254,254,80,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,240,
+ 255,255,255,255,255,255,255,254,218,44,0,0,0,0,0,0,0,0,0,0,0,23,242,255,
+ 255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,
+ 0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,204,30,0,1,182,254,254,182,
+ 1,0,0,27,243,254,254,96,0,0,49,223,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,
+ 190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,245,112,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,21,241,127,0,0,0,0,0,0,0,0,0,0,0,0,14,216,254,248,115,2,0,0,0,0,0,
+ 18,115,175,181,180,148,47,0,0,0,0,0,0,80,239,254,239,36,0,0,0,0,0,0,0,0,
+ 87,251,50,0,0,0,0,0,9,223,255,238,28,0,75,252,146,0,8,212,254,222,9,0,153,
+ 255,255,254,106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,0,2,111,220,
+ 52,0,0,0,0,0,0,0,0,0,0,0,0,54,220,151,217,254,254,254,254,250,127,3,0,0,
+ 0,0,0,0,11,165,253,254,254,254,254,194,168,201,29,0,0,0,0,1,102,245,254,
+ 254,254,254,230,143,225,75,0,0,0,0,0,47,221,254,254,254,254,248,153,210,
+ 137,5,0,0,23,191,177,186,254,254,254,254,254,188,175,194,24,0,0,0,0,0,0,
+ 0,0,0,0,0,1,96,223,66,0,0,0,72,224,144,228,254,254,254,254,246,149,215,
+ 126,3,0,0,0,0,19,184,254,254,254,254,253,181,181,185,20,0,0,6,144,203,32,
+ 0,0,0,0,0,2,106,221,57,0,0,0,0,0,0,0,0,0,0,2,34,41,5,0,0,0,0,0,0,0,0,0,
+ 69,252,67,0,0,0,133,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,
+ 71,253,254,255,255,255,254,255,255,255,255,255,255,254,254,254,254,254,
+ 254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,4,118,137,137,137,137,137,
+ 137,137,137,137,135,28,0,0,0,0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,
+ 255,254,219,43,0,0,0,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,
+ 0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,204,31,0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,
+ 0,0,50,224,254,254,254,254,254,254,254,254,254,254,254,254,255,255,254,
+ 111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,31,245,112,0,44,136,137,137,137,137,137,137,137,137,137,137,137,137,137,
+ 120,6,20,241,127,0,0,0,0,0,0,0,0,0,0,0,0,0,126,248,115,2,0,0,0,0,0,0,0,
+ 0,5,43,9,0,0,0,0,0,0,0,0,0,80,240,169,2,0,0,0,0,0,0,0,0,87,251,50,0,0,0,
+ 0,0,9,223,254,179,2,2,172,254,222,17,0,135,254,222,9,0,153,255,255,254,
+ 106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,1,109,247,254,225,41,0,
+ 0,0,0,0,0,0,0,0,0,51,226,254,247,138,115,116,116,116,90,3,0,0,0,0,0,0,0,
+ 0,11,105,116,116,116,116,167,252,254,202,18,0,0,0,0,1,78,116,116,116,116,
+ 134,241,254,237,64,0,0,0,0,0,43,116,116,116,116,118,214,254,251,129,0,20,
+ 191,254,253,172,111,116,116,116,116,174,253,254,193,14,0,0,0,0,0,0,0,0,
+ 0,0,93,244,254,233,54,0,69,236,254,242,127,116,116,116,116,120,219,254,
+ 250,117,0,0,0,0,0,19,110,116,116,116,116,181,254,254,184,10,4,143,252,254,
+ 205,20,0,0,0,0,104,246,254,228,45,0,0,0,0,0,0,0,0,3,136,242,246,185,20,
+ 0,0,0,0,0,0,0,0,69,252,168,137,137,137,198,239,16,0,0,0,0,0,0,12,230,146,
+ 0,0,0,55,252,81,0,0,0,66,236,237,237,237,237,237,237,247,254,255,254,254,
+ 254,254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,8,206,
+ 237,237,237,237,237,237,237,237,237,235,49,0,0,0,0,0,0,0,0,0,0,0,0,18,240,
+ 255,255,255,255,255,254,219,43,0,0,0,0,0,0,0,0,0,0,0,0,0,23,242,255,255,
+ 255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,
+ 0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,
+ 251,254,254,254,254,254,254,254,205,31,0,0,0,1,182,254,254,182,1,0,0,27,
+ 243,254,254,96,0,0,0,0,50,224,254,254,254,254,254,254,245,249,254,254,254,
+ 255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,29,228,104,2,170,254,254,254,254,253,252,251,249,251,252,
+ 254,254,254,254,250,76,19,224,118,0,0,0,0,0,0,0,0,0,0,0,0,0,22,76,2,0,0,
+ 0,0,0,0,0,0,0,78,233,114,2,0,0,0,0,0,0,0,0,0,57,43,0,0,0,0,0,0,0,0,0,87,
+ 251,50,0,0,0,0,0,9,223,253,89,0,36,240,254,252,80,0,48,247,222,9,0,153,
+ 254,255,254,106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,5,212,254,255,
+ 254,88,0,0,0,0,0,0,0,0,0,0,138,254,255,254,167,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,23,241,254,255,250,46,0,0,0,0,0,0,0,0,0,1,183,254,255,254,
+ 122,0,0,0,0,0,0,0,0,0,0,104,254,255,255,200,1,73,253,254,255,224,10,0,0,
+ 0,32,245,254,255,247,36,0,0,0,0,0,0,0,0,0,1,196,254,255,254,108,0,165,254,
+ 255,254,140,0,0,0,0,117,254,255,254,187,1,0,0,0,0,0,0,0,0,0,42,249,254,
+ 255,243,26,19,239,254,255,252,49,0,0,0,3,207,254,255,254,94,0,0,0,0,0,0,
+ 0,0,53,248,255,254,254,112,0,0,0,0,0,0,0,0,69,253,241,237,237,237,246,239,
+ 16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,4,14,14,14,14,14,14,14,
+ 72,235,254,254,254,254,254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,12,14,14,14,14,14,14,14,14,14,14,2,0,0,0,0,0,0,0,0,0,0,
+ 0,0,18,240,255,255,255,255,254,218,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,242,
+ 255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,
+ 0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,
+ 240,119,248,254,254,254,254,254,204,30,0,0,0,0,1,182,254,254,182,1,0,0,
+ 27,243,254,254,96,0,0,0,0,0,50,223,254,254,254,254,254,198,112,247,254,
+ 254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,1,14,6,54,246,255,253,219,141,82,49,48,42,49,55,102,172,
+ 241,254,254,194,7,14,7,0,0,0,0,0,0,0,0,0,0,0,0,0,59,43,0,0,0,0,0,0,0,0,
+ 0,79,240,254,249,114,2,0,0,0,0,0,0,0,0,23,78,1,0,0,0,0,0,0,0,0,87,253,177,
+ 158,158,158,158,158,161,242,251,170,158,189,254,254,254,206,158,162,242,
+ 242,161,158,216,254,255,254,198,158,161,241,255,255,185,1,0,0,0,0,0,0,0,
+ 0,0,0,0,5,213,255,255,254,88,0,0,0,0,0,0,0,0,0,0,139,254,255,255,167,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,242,255,255,250,46,0,0,0,0,0,0,0,
+ 0,0,1,184,255,255,254,122,0,0,0,0,0,0,0,0,0,0,105,254,255,255,201,1,74,
+ 253,255,255,224,10,0,0,0,33,246,255,255,247,36,0,0,0,0,0,0,0,0,0,1,197,
+ 255,255,254,108,0,166,255,255,254,140,0,0,0,0,118,254,255,255,187,1,0,0,
+ 0,0,0,0,0,0,0,43,249,255,255,243,26,20,240,255,255,252,49,0,0,0,3,209,255,
+ 255,254,95,0,0,0,0,0,0,0,0,72,253,255,254,254,136,0,0,0,0,0,0,0,0,69,252,
+ 78,14,14,14,140,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,0,0,
+ 0,0,0,0,0,0,0,70,235,254,255,254,254,254,254,254,254,254,254,254,254,80,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,18,240,255,255,255,254,218,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,242,255,
+ 255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,
+ 0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,240,
+ 81,38,247,254,254,254,254,205,30,0,0,0,0,0,1,182,254,254,182,1,0,0,27,243,
+ 254,254,96,0,0,0,0,0,0,50,223,254,254,254,254,196,4,111,247,254,255,255,
+ 254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,2,171,254,254,177,18,0,0,0,0,0,0,0,0,1,59,238,255,251,76,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,64,232,199,14,0,0,0,0,0,0,0,78,239,254,255,255,
+ 248,113,2,0,0,0,0,0,0,4,163,244,96,1,0,0,0,0,0,0,0,75,221,221,221,221,221,
+ 221,221,221,221,221,221,221,221,221,221,221,221,221,221,221,221,221,221,
+ 221,221,221,221,221,221,221,221,221,221,161,1,0,0,0,0,0,0,0,0,0,0,0,5,213,
+ 255,255,254,88,0,0,0,0,0,0,0,0,0,0,139,254,255,255,167,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,23,242,255,255,250,46,0,0,0,0,0,0,0,0,0,1,184,255,
+ 255,254,122,0,0,0,0,0,0,0,0,0,0,105,254,255,255,201,1,74,253,255,255,224,
+ 10,0,0,0,33,246,255,255,247,36,0,0,0,0,0,0,0,0,0,1,197,255,255,254,108,
+ 0,166,255,255,254,140,0,0,0,0,118,254,255,255,187,1,0,0,0,0,0,0,0,0,0,43,
+ 249,255,255,243,26,20,240,255,255,252,49,0,0,0,3,209,255,255,254,95,0,0,
+ 0,0,0,0,0,0,27,224,254,254,248,71,0,0,0,0,0,0,0,0,69,252,68,0,0,0,134,239,
+ 16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,0,0,0,0,0,0,0,0,0,0,70,
+ 235,254,254,254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,
+ 0,0,5,155,179,179,179,179,179,179,179,179,179,177,36,0,0,0,0,0,0,0,0,0,
+ 0,0,0,18,240,255,255,254,219,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,242,
+ 255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,
+ 0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,241,81,
+ 0,38,247,254,254,254,204,31,0,0,0,0,0,0,1,182,254,254,182,1,0,0,27,243,
+ 254,254,96,0,0,0,0,0,0,0,50,224,254,254,254,196,1,2,111,248,254,255,254,
+ 111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,54,246,254,254,129,1,0,0,0,0,0,0,0,0,0,15,223,255,254,193,6,0,0,0,
+ 0,0,0,0,0,0,0,0,0,64,232,254,253,165,11,0,0,0,0,0,78,239,254,255,255,255,
+ 254,248,113,2,0,0,0,0,4,129,251,254,244,96,1,0,0,0,0,0,0,2,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,0,0,0,0,0,0,0,0,
+ 0,0,0,0,5,213,255,255,254,88,0,0,0,0,0,0,0,0,0,0,139,254,255,255,167,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,241,255,255,250,46,0,0,0,0,0,0,0,
+ 0,0,1,183,254,255,254,122,0,0,0,0,0,0,0,0,0,0,104,254,255,255,201,1,73,
+ 253,255,255,224,10,0,0,0,33,245,255,255,247,36,0,0,0,0,0,0,0,0,0,1,197,
+ 255,255,254,108,0,166,254,255,254,140,0,0,0,0,118,254,255,255,187,1,0,0,
+ 0,0,0,0,0,0,0,43,249,255,255,243,26,20,240,255,255,252,49,0,0,0,3,208,255,
+ 255,254,95,0,0,0,0,0,0,0,0,0,51,162,167,84,2,0,0,0,0,0,0,0,0,69,253,199,
+ 179,179,179,218,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,70,236,254,254,254,254,254,254,254,254,254,254,80,0,0,
+ 0,0,0,0,0,0,0,0,0,0,6,179,206,206,206,206,206,206,206,206,206,204,42,0,
+ 0,0,0,0,0,0,0,0,0,0,0,18,240,255,254,220,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,18,196,206,206,206,206,206,204,48,0,0,0,0,0,0,0,148,206,206,206,206,
+ 206,206,112,0,0,0,0,0,0,0,84,206,206,206,206,206,206,206,206,206,206,206,
+ 206,206,206,206,206,206,206,206,206,206,203,38,0,0,0,0,0,0,1,197,254,254,
+ 241,82,0,0,38,247,254,254,205,31,0,0,0,0,0,0,0,1,182,254,254,182,1,0,0,
+ 27,243,254,254,96,0,0,0,0,0,0,0,0,50,224,254,254,196,1,0,2,111,248,254,
+ 254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,2,170,254,255,255,242,137,48,10,1,1,1,1,3,19,76,187,253,255,255,
+ 250,75,0,0,0,0,0,0,0,0,0,0,0,39,231,254,254,254,253,164,11,0,0,0,78,238,
+ 254,255,255,255,255,255,254,248,113,2,0,0,4,129,250,255,255,254,244,72,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,127,251,254,234,49,0,0,0,0,0,0,0,0,
+ 0,0,63,236,254,250,143,136,142,142,141,91,1,0,0,0,0,0,0,0,0,6,113,142,142,
+ 142,131,178,253,254,215,23,0,0,0,0,0,0,0,0,0,0,98,246,254,243,75,0,0,0,
+ 0,0,34,138,142,142,141,126,224,254,253,145,0,26,206,254,254,182,123,142,
+ 142,142,130,185,254,254,207,17,0,0,0,0,0,0,0,0,0,0,109,249,254,240,64,0,
+ 84,242,254,247,130,139,142,142,141,128,229,254,252,132,0,0,0,0,0,0,0,0,
+ 0,0,13,185,254,254,198,12,5,161,253,254,220,114,141,142,142,137,152,250,
+ 254,237,53,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,69,253,255,255,255,
+ 255,255,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,70,236,254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,2,3,3,3,3,3,3,3,3,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,18,240,254,
+ 219,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,3,3,3,3,2,0,0,0,0,0,0,
+ 0,0,2,3,3,3,3,3,2,1,0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 3,3,3,2,0,0,0,0,0,0,0,1,197,254,240,82,0,0,0,38,247,254,205,30,0,0,0,0,
+ 0,0,0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,0,0,0,0,0,0,0,0,50,
+ 224,254,196,1,0,0,1,111,248,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,
+ 196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,54,245,255,255,255,254,254,247,225,
+ 200,199,199,199,204,237,252,254,255,255,255,254,192,6,0,0,0,0,0,0,0,0,0,
+ 0,7,151,252,254,255,254,253,188,37,0,75,239,254,255,255,255,255,255,255,
+ 255,254,248,113,2,22,160,251,254,255,255,254,185,17,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,4,130,231,66,0,0,0,0,0,0,0,0,0,0,0,0,68,232,158,207,254,
+ 254,254,254,248,108,1,0,0,0,0,0,0,6,147,252,254,254,254,254,183,179,215,
+ 40,0,0,0,0,0,0,0,0,0,0,0,1,102,236,92,1,0,0,0,0,35,211,254,254,254,254,
+ 245,146,222,155,8,0,0,31,206,188,174,253,254,254,254,253,176,186,208,33,
+ 0,0,0,0,0,0,0,0,0,0,0,2,113,234,81,0,0,0,88,235,148,220,254,254,254,254,
+ 242,144,227,144,6,0,0,0,0,0,0,0,0,0,0,0,18,186,200,28,0,0,10,162,219,148,
+ 247,254,254,254,254,209,156,232,71,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,69,253,255,255,255,255,255,239,16,0,0,0,0,0,0,12,230,146,0,0,
+ 0,55,252,81,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,235,254,254,254,254,254,
+ 254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,6,6,6,6,6,6,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,18,240,218,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,197,240,82,0,0,0,0,38,
+ 247,205,30,0,0,0,0,0,0,0,0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,
+ 0,0,0,0,0,0,0,0,0,0,50,224,196,1,0,0,0,2,111,247,111,0,0,0,0,0,0,0,132,
+ 254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,117,193,193,193,193,
+ 193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,189,44,
+ 0,0,0,0,0,0,0,0,0,0,0,7,151,252,254,255,255,254,223,38,128,235,254,254,
+ 255,255,255,255,255,254,254,241,169,19,178,254,254,255,255,254,190,18,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 43,190,254,255,255,255,255,254,246,72,0,0,0,0,0,0,114,252,255,255,255,255,
+ 254,253,155,30,0,0,0,0,0,0,0,0,0,0,0,0,0,1,37,0,0,0,0,0,10,196,254,255,
+ 255,255,255,254,243,93,8,0,0,0,0,25,142,253,255,255,255,255,254,253,145,
+ 26,0,0,0,0,0,0,0,0,0,0,0,0,0,2,37,0,0,0,0,0,51,209,254,255,255,255,255,
+ 254,240,85,6,0,0,0,0,0,0,0,0,0,0,0,0,0,17,23,0,0,0,0,10,99,245,255,255,
+ 255,255,254,254,193,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 69,253,255,255,255,255,255,239,16,0,0,0,0,0,0,12,230,147,0,0,0,55,252,81,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,235,254,254,254,254,254,254,254,80,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,205,216,216,216,215,147,7,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,18,204,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,183,82,0,0,0,0,0,38,198,31,
+ 0,0,0,0,0,0,0,0,0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,0,0,0,0,
+ 0,0,0,0,0,0,50,167,1,0,0,0,0,2,112,104,0,0,0,0,0,0,0,132,254,255,225,10,
+ 1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,209,217,217,217,217,217,217,217,
+ 217,217,217,217,217,217,217,217,217,217,217,217,217,217,108,0,0,0,0,0,0,
+ 0,0,0,0,0,0,8,151,252,254,254,245,99,1,1,28,104,164,209,218,218,218,214,
+ 173,106,40,2,0,38,212,254,254,254,213,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,234,254,255,255,255,253,
+ 166,11,0,0,0,0,0,0,26,198,254,255,255,255,254,215,41,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,79,239,254,255,255,255,253,155,9,0,0,0,0,0,0,
+ 32,206,254,255,255,255,254,208,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,89,243,254,255,255,255,252,144,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,10,162,253,255,255,255,254,236,73,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,69,253,255,255,255,255,255,239,16,0,0,0,0,0,0,12,
+ 230,222,45,0,3,131,253,81,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,236,254,
+ 254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,86,165,166,166,147,
+ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,0,0,0,0,
+ 0,0,15,27,0,0,0,0,0,0,0,0,0,0,0,0,119,166,166,118,0,0,0,17,158,166,165,
+ 62,0,0,0,0,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,2,17,0,0,0,0,0,0,0,132,254,255,
+ 225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,159,166,166,166,166,166,
+ 166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,82,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,8,151,252,245,99,1,0,0,0,0,0,6,7,7,7,7,1,0,0,0,0,
+ 0,38,213,254,213,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,67,164,166,166,166,137,11,0,0,0,0,0,0,0,0,26,
+ 153,166,166,166,160,41,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 75,165,166,166,166,130,9,0,0,0,0,0,0,0,0,32,156,166,166,166,157,34,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,84,165,166,166,166,123,7,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,135,166,166,166,165,70,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,61,250,255,255,255,255,
+ 255,237,15,0,0,0,0,0,0,10,221,254,219,49,124,250,253,79,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,70,237,254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255,225,
+ 10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,147,99,1,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,38,179,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,131,251,
+ 254,254,254,249,116,1,0,0,0,0,0,0,0,69,237,254,236,250,254,178,15,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,236,254,254,254,254,80,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,
+ 255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,105,144,
+ 145,144,96,2,0,0,0,0,0,0,0,0,0,64,144,145,145,129,14,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,71,235,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255,225,10,
+ 1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,
+ 236,254,253,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,132,254,255,225,10,12,209,141,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,237,254,80,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255,
+ 224,21,163,155,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,71,236,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255,233,173,155,8,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,63,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255,
+ 252,155,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,132,254,253,155,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,253,156,9,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 132,160,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,};
+
+#endif // linux || svr4
+#endif // __SYNAES_SYMBOL_H__
diff --git a/kscd/kscdmagic/syna.h b/kscd/kscdmagic/syna.h
new file mode 100644
index 00000000..40128532
--- /dev/null
+++ b/kscd/kscdmagic/syna.h
@@ -0,0 +1,180 @@
+/*
+ $Id$
+
+ Synaesthesia - program to display sound graphically
+ Copyright (C) 1997 Paul Francis Harrison
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The author may be contacted at:
+ phar6@student.monash.edu.au
+ or
+ 27 Bond St., Mt. Waverley, 3149, Melbourne, Australia
+*/
+
+#ifndef __SYNA_H__
+#define __SYNA_H__
+
+
+#if defined(__linux__) || defined(__svr4__) || defined(__osf__)
+
+void error(const char *str, bool syscall=false); //Display error and exit
+void warning(const char *str, bool syscall=false); //Display error
+
+//void error(char *str,bool syscall=false);
+inline void attempt(int x, const char *y, bool syscall=false) { if (x == -1) error(y,syscall); }
+
+//void warning(char *str,bool syscall=false);
+inline void attemptNoDie(int x, const char *y, bool syscall=false) { if (x == -1) warning(y,syscall); }
+
+#include "polygon.h"
+#include "magicconf.h"
+
+/***************************************/
+
+
+#define PROGNAME "kscdmagic"
+
+
+#ifdef __FreeBSD__
+
+
+typedef unsigned short sampleType;
+
+#else
+
+typedef short sampleType;
+
+#ifndef __linux__
+
+#warning This target has not been tested!
+
+#endif
+#endif
+
+#ifdef __osf__
+#include <machine/endian.h>
+#else
+#include <endian.h>
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+#define BIGENDIAN
+#else
+#define LITTLEENDIAN
+#endif
+
+
+#define n (1<<m)
+#define recSize (1<<m-overlap)
+
+/* core */
+extern volatile sampleType *data;
+//extern unsigned char *output, *lastOutput, *lastLastOutput;
+extern Bitmap<unsigned short> outputBmp, lastOutputBmp, lastLastOutputBmp;
+#define ucoutput ((unsigned char*)outputBmp.data)
+#define lastOutput ((unsigned char*)lastOutputBmp.data)
+#define lastLastOutput ((unsigned char*)lastLastOutputBmp.data)
+
+inline unsigned short combiner(unsigned short a,unsigned short b) {
+ //Not that i want to give the compiler a hint or anything...
+ unsigned char ah = a>>8, al = a&255, bh = b>>8, bl = b&255;
+ if (ah < 64) ah *= 4; else ah = 255;
+ if (al < 64) al *= 4; else al = 255;
+ if (bh > ah) ah = bh;
+ if (bl > al) al = bl;
+ return ah*256+al;
+}
+
+extern PolygonEngine<unsigned short,combiner,2> polygonEngine;
+
+extern int outWidth, outHeight;
+
+void allocOutput(int w,int h);
+
+void coreInit();
+void setStarSize(double size);
+int coreGo();
+void fade();
+
+/* *wrap */
+void screenInit(int xHint,int yHint,int widthHint,int heightHint);
+void screenSetPalette(unsigned char *palette);
+void screenEnd(void);
+void screenShow(void);
+int sizeUpdate(void);
+
+void inputUpdate(int &mouseX,int &mouseY,int &mouseButtons,char &keyHit);
+
+/* ui */
+void interfaceInit();
+void interfaceSyncToState();
+void interfaceEnd();
+bool interfaceGo();
+
+enum SymbolID {
+ Speaker, Bulb,
+ Play, Pause, Stop, SkipFwd, SkipBack,
+ Handle, Pointer, Open, NoCD, Exit,
+ Zero, One, Two, Three, Four,
+ Five, Six, Seven, Eight, Nine,
+ Slider, Selector, Plug, Loop, Box, Bar,
+ Flame, Wave, Stars, Star, Diamond, Size, FgColor, BgColor,
+ Save, Reset, TrackSelect,
+ NotASymbol
+};
+
+/* State information */
+
+extern SymbolID state;
+extern int track, frames;
+extern double trackProgress;
+extern char **playList;
+extern int playListLength, playListPosition;
+extern SymbolID fadeMode;
+extern bool pointsAreDiamonds;
+extern double brightnessTwiddler;
+extern double starSize;
+extern double fgRedSlider, fgGreenSlider, bgRedSlider, bgGreenSlider;
+
+extern double volume;
+
+void setStateToDefaults();
+void saveConfig();
+
+void putString(char *string,int x,int y,int red,int blue);
+
+/* sound */
+enum SoundSource { SourceLine, SourceCD, SourcePipe };
+
+void cdOpen(char *cdromName);
+void cdClose(void);
+void cdGetStatus(int &track, int &frames, SymbolID &state);
+void cdPlay(int trackFrame, int endFrame=-1);
+void cdStop(void);
+void cdPause(void);
+void cdResume(void);
+void cdEject(void);
+void cdCloseTray(void);
+int cdGetTrackCount(void);
+int cdGetTrackFrame(int track);
+void openSound(SoundSource sound, int downFactor, const char *dspName, char *mixerName);
+void closeSound();
+void setupMixer(double &loudness);
+void setVolume(double loudness);
+int getNextFragment(void);
+
+
+#endif // linux || svr4
+#endif // __SYNA_H__
diff --git a/kscd/kscdmagic/version.h b/kscd/kscdmagic/version.h
new file mode 100644
index 00000000..aa5df67c
--- /dev/null
+++ b/kscd/kscdmagic/version.h
@@ -0,0 +1 @@
+#define KSCDMAGICVERSION "2.0"
diff --git a/kscd/kscdmagic/xlib.c b/kscd/kscdmagic/xlib.c
new file mode 100644
index 00000000..2fb15c19
--- /dev/null
+++ b/kscd/kscdmagic/xlib.c
@@ -0,0 +1,781 @@
+/*
+ * XaoS, a fast portable realtime fractal zoomer
+ * Copyright (C) 1996,1997 by
+ *
+ * Jan Hubicka (hubicka@paru.cas.cz)
+ * Thomas Marsh (tmarsh@austin.ibm.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ * Shamelessly ripped for use in xsynaesthesia
+ */
+/*#include "aconfig.h"*/
+#define X11_DRIVER
+/*#define MITSHM*/
+
+#ifdef X11_DRIVER
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/cursorfont.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef __FreeBSD__
+#include <malloc.h>
+#else
+#include <stdlib.h>
+#endif
+#include "xlib.h"
+#ifdef AMIGA
+#define XFlush(x) while(0)
+#endif
+
+#undef PIXMAP
+
+#define chkalloc(n) if (!n) fprintf(stderr, "out of memory\n"), exit(-1)
+
+int xupdate_size(xdisplay * d)
+{
+ int tmp;
+ Window wtmp;
+ int width = d->width, height = d->height;
+ XGetGeometry(d->display, d->window, &wtmp, &tmp, &tmp, &d->width, &d->height, (unsigned int *) &tmp, (unsigned int *) &tmp);
+ if ((int)d->width != width || (int)d->height != height)
+ return 1;
+ return 0;
+}
+
+void xflip_buffers(xdisplay * d)
+{
+ d->back = d->vbuffs[d->current];
+ d->current ^= 1;
+ d->vbuff = d->vbuffs[d->current];
+}
+
+void draw_screen(xdisplay * d)
+{
+ switch (d->image[0]->bits_per_pixel) {
+ case 16:{
+ unsigned short *de;
+ unsigned char *s;
+ unsigned char *e;
+ for (s = (unsigned char *) d->vbuffs[d->current],
+ e = (unsigned char *) d->vbuffs[d->current] + (d->linewidth * d->height),
+ de = (unsigned short *) d->data[d->current]; s < e; s += 8, de += 8)
+ *de = d->pixels[*s],
+ *(de + 1) = d->pixels[*(s + 1)],
+ *(de + 2) = d->pixels[*(s + 2)],
+ *(de + 3) = d->pixels[*(s + 3)],
+ *(de + 4) = d->pixels[*(s + 4)],
+ *(de + 5) = d->pixels[*(s + 5)],
+ *(de + 6) = d->pixels[*(s + 6)],
+ *(de + 7) = d->pixels[*(s + 7)];
+ s -= 8;
+ de -= 8;
+ for (; s < e; s++, de++)
+ *de = d->pixels[*s];
+ break;
+ }
+ case 24:{
+ unsigned char *de;
+ unsigned char *s;
+ unsigned char *e;
+ for (s = (unsigned char *) d->vbuffs[d->current],
+ e = (unsigned char *) d->vbuffs[d->current] + (d->linewidth * d->height),
+ de = (unsigned char *) d->data[d->current]; s < e; s++, de+=3)
+ de[0] = d->pixels[*s],
+ de[1] = d->pixels[*s]>>8,
+ de[2] = d->pixels[*s]>>16;
+
+ break;
+ }
+ case 32:{
+ unsigned long *de;
+ unsigned char *s;
+ unsigned char *e;
+ for (s = (unsigned char *) d->vbuffs[d->current],
+ e = (unsigned char *) d->vbuffs[d->current] + (d->linewidth * d->height),
+ de = (unsigned long *) d->data[d->current]; s < e; s += 8, de += 8)
+ *de = d->pixels[*s],
+ *(de + 1) = d->pixels[*(s + 1)],
+ *(de + 2) = d->pixels[*(s + 2)],
+ *(de + 3) = d->pixels[*(s + 3)],
+ *(de + 4) = d->pixels[*(s + 4)],
+ *(de + 5) = d->pixels[*(s + 5)],
+ *(de + 6) = d->pixels[*(s + 6)],
+ *(de + 7) = d->pixels[*(s + 7)];
+ s -= 8;
+ de -= 8;
+ for (; s < e; s++, de++)
+ *de = d->pixels[*s];
+ break;
+ }
+ }
+#ifdef MITSHM
+ if (d->SharedMemFlag) {
+ XShmPutImage(d->display, d->window, d->gc, d->image[d->current], 0, 0, 0,
+ 0, d->width, d->height, True);
+ XFlush(d->display);
+ } else
+#endif
+ {
+ XPutImage(d->display, d->window, d->gc, d->image[d->current], 0, 0, 0, 0, d->width, d->height);
+ XFlush(d->display);
+ }
+ d->screen_changed = 0;
+}
+
+#ifdef MITSHM
+int alloc_shm_image(xdisplay * new)
+{
+ register char *ptr;
+ int temp, size = 0, i;
+ ptr = DisplayString(new->display);
+ if (!ptr || (*ptr == ':') || !strncmp(ptr, "localhost:", 10) ||
+ !strncmp(ptr, "unix:", 5) || !strncmp(ptr, "local:", 6)) {
+ new->SharedMemOption = XQueryExtension(new->display, "MIT-SHM", &temp, &temp, &temp);
+ } else {
+ new->SharedMemOption = False;
+ return 0;
+ }
+ new->SharedMemFlag = False;
+#if 0
+ new->SharedMemOption = True;
+ new->SharedMemFlag = False;
+#endif
+
+ if (new->SharedMemFlag) {
+ XShmDetach(new->display, &new->xshminfo[0]);
+ XShmDetach(new->display, &new->xshminfo[1]);
+ new->image[0]->data = (char *) NULL;
+ new->image[1]->data = (char *) NULL;
+ shmdt(new->xshminfo[0].shmaddr);
+ shmdt(new->xshminfo[1].shmaddr);
+ }
+ for (i = 0; i < 2; i++) {
+ if (new->SharedMemOption) {
+ int mul;
+ if (new->depth == 8)
+ mul = 1;
+ else if (new->depth <= 24)
+ mul = 2;
+ else
+ mul = 4;
+ new->SharedMemFlag = False;
+ new->image[i] = XShmCreateImage(new->display, new->visual, new->depth, ZPixmap,
+ NULL, &new->xshminfo[i], new->width, new->height * mul);
+ if (new->image[i]) {
+ temp = new->image[i]->bytes_per_line * new->image[i]->height;
+ new->linewidth = new->image[i]->bytes_per_line * 8 / new->image[i]->bits_per_pixel;
+ if (temp > size)
+ size = temp;
+ new->xshminfo[i].shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777);
+ if (new->xshminfo[i].shmid != -1) {
+ new->xshminfo[i].shmaddr = (char *) shmat(new->xshminfo[i].shmid, 0, 0);
+ if (new->xshminfo[i].shmaddr != (char *) -1) {
+ new->image[i]->data = new->xshminfo[i].shmaddr;
+ new->data[i] = new->vbuffs[i] = (char *) new->image[i]->data;
+ new->xshminfo[i].readOnly = True;
+
+ new->SharedMemFlag = XShmAttach(new->display, &new->xshminfo[i]);
+ XSync(new->display, False);
+ if (!new->SharedMemFlag) {
+ XDestroyImage(new->image[i]);
+ new->image[i] = (XImage *) NULL;
+ new->SharedMemFlag = 0;
+ return 0;
+ }
+ }
+ /* Always Destroy Shared Memory Ident */
+ shmctl(new->xshminfo[i].shmid, IPC_RMID, 0);
+ }
+ if (!new->SharedMemFlag) {
+ XDestroyImage(new->image[i]);
+ new->image[i] = (XImage *) NULL;
+ new->SharedMemFlag = 0;
+ return 0;
+ }
+ } else {
+ new->SharedMemFlag = 0;
+ return 0;
+ }
+ } else {
+ new->SharedMemFlag = 0;
+ return 0;
+ }
+ }
+ new->current = 0;
+ xflip_buffers(new);
+ return 1;
+}
+
+void free_shm_image(xdisplay * d)
+{
+ if (d->SharedMemFlag) {
+ XDestroyImage(d->image[0]);
+ XDestroyImage(d->image[1]);
+ XShmDetach(d->display, &d->xshminfo[0]);
+ XShmDetach(d->display, &d->xshminfo[1]);
+ shmdt(d->xshminfo[0].shmaddr);
+ shmdt(d->xshminfo[1].shmaddr);
+ }
+}
+
+#endif
+
+int alloc_image(xdisplay * d)
+{
+ int i;
+#ifdef MITSHM
+ if (!d->params->nomitshm && alloc_shm_image(d)) {
+ if (d->depth != 8) {
+ for (i = 0; i < 2; i++)
+ d->vbuffs[i] = malloc(d->linewidth * d->height);
+ }
+ return 1;
+ }
+#endif
+ for (i = 0; i < 2; i++) {
+ d->image[i] = XCreateImage(d->display, d->visual, d->depth, ZPixmap, 0,
+ NULL, d->width, d->height, 8, 0);
+ if (d->image[i] == NULL) {
+ printf("Out of memory for image..exiting\n");
+ exit(3);
+ }
+ /*Add a little extra memory to catch overruns when dumping image to buffer in draw_screen*/
+ d->image[i]->data = malloc(d->image[i]->bytes_per_line * d->height + 32);
+ memset(d->image[i]->data,0,d->image[i]->bytes_per_line * d->height);
+
+ if (d->image[i]->data == NULL) {
+ printf("Out of memory for image buffers..exiting\n");
+ exit(3);
+ }
+ d->data[i] = d->vbuffs[i] = (char *) d->image[i]->data;
+ d->linewidth = d->image[i]->bytes_per_line * 8 / d->image[i]->bits_per_pixel;
+ }
+ if (d->depth != 8) {
+ for (i = 0; i < 2; i++) {
+ /* Add a little extra memory to catch overruns */
+ /* when dumping image to buffer in draw_screen */
+ d->vbuffs[i] = malloc(d->linewidth * d->height + 32);
+ memset(d->vbuffs[i],0,d->linewidth * d->height);
+
+ if (d->vbuffs[i] == NULL) {
+ printf("Out of memory for image buffers2..exiting\n");
+ exit(3);
+ }
+ }
+ }
+ xflip_buffers(d);
+ return 1;
+}
+
+void free_image(xdisplay * d)
+{
+ if (d->depth != 8)
+ free(d->vbuffs[0]), free(d->vbuffs[1]);
+#ifdef MITSHM
+ if (d->SharedMemFlag) {
+ free_shm_image(d);
+ return;
+ }
+#endif
+ XDestroyImage(d->image[0]);
+ XDestroyImage(d->image[1]);
+}
+#define MAX(x,y) ((x)>(y)?(x):(y))
+
+
+xdisplay *xalloc_display(const char *s, int xHint, int yHint, int x, int y, xlibparam * params)
+{
+ xdisplay *xd;
+ Visual *defaultvisual;
+ XVisualInfo vis;
+
+
+ xd = (xdisplay *) calloc(sizeof(xdisplay), 1);
+ chkalloc(xd);
+ xd->display = XOpenDisplay((char *) NULL);
+ if (!xd->display) {
+ free((void *) xd);
+ return NULL;
+ }
+ xd->screen = DefaultScreen(xd->display);
+ xd->attributes = (XSetWindowAttributes *)
+ malloc(sizeof(XSetWindowAttributes));
+ chkalloc(xd->attributes);
+ xd->attributes->background_pixel = BlackPixel(xd->display,
+ xd->screen);
+ xd->attributes->border_pixel = BlackPixel(xd->display, xd->screen);
+ xd->attributes->event_mask = ButtonPressMask | StructureNotifyMask | ButtonReleaseMask | ButtonMotionMask | KeyPressMask | ExposureMask | KeyReleaseMask;
+ xd->attributes->override_redirect = False;
+ xd->attr_mask = CWBackPixel | CWBorderPixel | CWEventMask;
+ xd->classX = InputOutput;
+ xd->xcolor.n = 0;
+ xd->parent_window = RootWindow(xd->display, xd->screen);
+ defaultvisual = DefaultVisual(xd->display, xd->screen);
+ xd->params = params;
+ if (!params->usedefault) {
+ if (defaultvisual->class != PseudoColor || (!XMatchVisualInfo(xd->display, xd->screen, 8, PseudoColor, &vis) && vis.colormap_size > 128)) {
+ xd->fixedcolormap = 1;
+ if (!XMatchVisualInfo(xd->display, xd->screen, 15, TrueColor, &vis)) {
+ if (!XMatchVisualInfo(xd->display, xd->screen, 16, TrueColor, &vis)) {
+ if (!XMatchVisualInfo(xd->display, xd->screen, 32, TrueColor, &vis) &&
+ !XMatchVisualInfo(xd->display, xd->screen, 24, TrueColor, &vis)) {
+ if (!XMatchVisualInfo(xd->display, xd->screen, 8, PseudoColor, &vis) &&
+ !XMatchVisualInfo(xd->display, xd->screen, 7, PseudoColor, &vis)) {
+ if (!XMatchVisualInfo(xd->display, xd->screen, 8, TrueColor, &vis) &&
+ !XMatchVisualInfo(xd->display, xd->screen, 8, StaticColor, &vis) &&
+ !XMatchVisualInfo(xd->display, xd->screen, 8, StaticGray, &vis)) {
+ printf("Display does not support PseudoColor depth 7,8,StaticColor depth 8, StaticGray depth 8, Truecolor depth 8,15,16,24 nor 32!\n");
+ return NULL;
+ } else
+ xd->truecolor = 1;
+ } else
+ xd->fixedcolormap = 0, xd->truecolor = 0;
+ } else
+ xd->truecolor = 1;
+ } else
+ xd->truecolor = 1;
+ } else
+ xd->truecolor = 1;
+ } else {
+ xd->truecolor = 0;
+ }
+ xd->depth = vis.depth;
+ xd->visual = vis.visual;
+ } else { /*usedefault */
+ vis.depth = xd->depth = DefaultDepth(xd->display, xd->screen);
+ xd->visual = defaultvisual;
+ switch (defaultvisual->class) {
+ case PseudoColor:
+ if (xd->depth <= 8) {
+ xd->depth = 8;
+ xd->truecolor = 0;
+ xd->fixedcolormap = 0;
+ } else {
+ printf("Pseudocolor visual on unsuported depth\n");
+ return NULL;
+ }
+ break;
+ case TrueColor:
+ case StaticColor:
+ case StaticGray:
+ xd->truecolor = 1;
+ xd->fixedcolormap = 1;
+ if (xd->depth <= 8)
+ xd->depth = 8;
+ else if (xd->depth <= 16)
+ xd->depth = 16;
+ else if (xd->depth <= 32)
+ xd->depth = 32;
+ else {
+ printf("Truecolor visual on unsuported depth\n");
+ return NULL;
+ }
+ break;
+ default:
+ printf("Unusuported visual\n");
+ break;
+ }
+ }
+ /*xd->visual->map_entries = 256; */
+ xd->colormap = xd->defaultcolormap = DefaultColormap(xd->display, xd->screen);
+
+ xd->window_name = s;
+ xd->height = y;
+ xd->width = x;
+ xd->border_width = 2;
+ xd->lastx = 0;
+ xd->lasty = 0;
+ xd->font_struct = (XFontStruct *) NULL;
+
+ xd->window = XCreateWindow(xd->display, xd->parent_window, xHint, yHint,
+ xd->width, xd->height, xd->border_width,
+ vis.depth, xd->classX, xd->visual,
+ xd->attr_mask, xd->attributes);
+ if (!xd->fixedcolormap && params->privatecolormap) {
+ unsigned long pixels[256];
+ int i;
+ xd->colormap = XCreateColormap(xd->display, xd->window, xd->visual, AllocNone);
+ XAllocColorCells(xd->display, xd->colormap, 1, 0, 0, pixels, MAX(xd->visual->map_entries, 256));
+ for (i = 0; i < 16; i++) {
+ xd->xcolor.c[i].pixel = pixels[i];
+ }
+ XQueryColors(xd->display, xd->defaultcolormap, xd->xcolor.c, 16);
+ XStoreColors(xd->display, xd->colormap, xd->xcolor.c, 16);
+ xd->privatecolormap = 1;
+ }
+ if (!xd->fixedcolormap)
+ XSetWindowColormap(xd->display, xd->window, xd->colormap);
+ xd->gc = XCreateGC(xd->display, xd->window, 0L, &(xd->xgcvalues));
+ XSetBackground(xd->display, xd->gc,
+ BlackPixel(xd->display, xd->screen));
+ XSetForeground(xd->display, xd->gc,
+ WhitePixel(xd->display, xd->screen));
+ XStoreName(xd->display, xd->window, xd->window_name);
+ XMapWindow(xd->display, xd->window);
+#if 1
+ XSelectInput(xd->display, xd->window,
+ /* ExposureMask | */
+ KeyPress |
+ /* KeyRelease | */
+ /* ConfigureRequest | */
+ /* FocusChangeMask | */
+ StructureNotifyMask |
+ ButtonPressMask | ButtonReleaseMask);
+#endif
+#ifdef PIXAMP
+ xd->pixmap = XCreatePixmap(xd->display, xd->window, xd->width,
+ xd->height, xd->depth);
+#endif
+
+ {
+ XColor c;
+ Pixmap p = XCreatePixmap(xd->display, xd->window, 1,1,1);
+ memset(&c,0,sizeof(c));
+ xd->cursor = XCreatePixmapCursor(xd->display, p,p,
+ &c,&c, 0,0);
+ /* We don't need no fancy cursor
+ XDefineCursor(xd->display,xd->window,xd->cursor);
+ */
+ XFreePixmap(xd->display, p);
+ }
+
+ return (xd);
+}
+
+void xsetcolor(xdisplay * d, int col)
+{
+ switch (col) {
+ case 0:
+ XSetForeground(d->display, d->gc,
+ BlackPixel(d->display, d->screen));
+ break;
+ case 1:
+ XSetForeground(d->display, d->gc,
+ WhitePixel(d->display, d->screen));
+ break;
+ default:
+ if ((col - 2) > d->xcolor.n) {
+ fprintf(stderr, "color error\n");
+ exit(3);
+ }
+ XSetForeground(d->display, d->gc,
+ d->xcolor.c[col - 2].pixel);
+ break;
+ }
+}
+void xrotate_palette(xdisplay * d, int direction, unsigned char co[3][256], int ncolors)
+{
+ int i, p;
+
+ if (d->privatecolormap) {
+ for (i = 0; i < d->xcolor.n; i++) {
+ p = d->xcolor.c[i].pixel;
+ d->xcolor.c[i].red = (int) co[0][p] * 256;
+ d->xcolor.c[i].green = (int) co[1][p] * 256;
+ d->xcolor.c[i].blue = (int) co[2][p] * 256;
+ }
+ XStoreColors(d->display, d->colormap, d->xcolor.c, d->xcolor.n);
+ }
+ if (d->truecolor) {
+ unsigned long oldpixels[256];
+ memcpy(oldpixels, d->pixels, sizeof(oldpixels));
+ p = (ncolors - 1 + direction) % (ncolors - 1) + 1;
+ for (i = 1; i < ncolors; i++) { /*this is ugly..I know */
+ d->pixels[i] = oldpixels[p];
+ p++;
+ if (p >= ncolors)
+ p = 1;
+ }
+ draw_screen(d);
+ }
+}
+int xalloc_color(xdisplay * d, int r, int g, int b, int readwrite)
+{
+ d->xcolor.n++;
+ d->xcolor.c[d->xcolor.n - 1].flags = DoRed | DoGreen | DoBlue;
+ d->xcolor.c[d->xcolor.n - 1].red = r;
+ d->xcolor.c[d->xcolor.n - 1].green = g;
+ d->xcolor.c[d->xcolor.n - 1].blue = b;
+ d->xcolor.c[d->xcolor.n - 1].pixel = d->xcolor.n - 1;
+ if ((readwrite && !d->fixedcolormap) || d->privatecolormap) {
+ unsigned long cell;
+ if (d->privatecolormap) {
+ cell = d->xcolor.c[d->xcolor.n - 1].pixel += 16;
+ if (d->xcolor.c[d->xcolor.n - 1].pixel >= d->visual->map_entries) {
+ d->xcolor.n--;
+ return (-1);
+ }
+ } else {
+ if (!XAllocColorCells(d->display, d->colormap, 0, 0, 0, &cell, 1)) {
+ d->xcolor.n--;
+ if (d->xcolor.n <= 32)
+ printf("Colormap is too full! close some colorfull aplications or use -private\n");
+ return (-1);
+ }
+ d->xcolor.c[d->xcolor.n - 1].pixel = cell;
+ }
+ XStoreColor(d->display, d->colormap, &(d->xcolor.c[d->xcolor.n - 1]));
+ return (cell);
+ }
+ if (!XAllocColor(d->display, d->colormap, &(d->xcolor.c[d->xcolor.n - 1]))) {
+ d->xcolor.n--;
+ if (d->xcolor.n <= 32)
+ printf("Colormap is too full! close some colorfull aplications or use -private\n");
+ return (-1);
+ }
+ d->pixels[d->xcolor.n - 1] = d->xcolor.c[d->xcolor.n - 1].pixel;
+ return (d->depth != 8 ? d->xcolor.n - 1 : d->xcolor.c[d->xcolor.n - 1].pixel);
+}
+
+void xfree_colors(xdisplay * d)
+{
+ unsigned long pixels[256];
+ int i;
+ for (i = 0; i < d->xcolor.n; i++)
+ pixels[i] = d->xcolor.c[i].pixel;
+ if (!d->privatecolormap)
+ XFreeColors(d->display, d->colormap, pixels, d->xcolor.n, 0);
+ d->xcolor.n = 0;
+}
+
+void xfree_display(xdisplay * d)
+{
+ XSync(d->display, 0);
+ if (d->font_struct != (XFontStruct *) NULL) {
+ XFreeFont(d->display, d->font_struct);
+ }
+ XUnmapWindow(d->display, d->window);
+#ifdef PIXMAP
+ XFreePixmap(d->display, d->pixmap);
+#endif
+ XDestroyWindow(d->display, d->window);
+ XFreeCursor(d->display, d->cursor);
+ XCloseDisplay(d->display);
+ free((void *) d->attributes);
+ free((void *) d);
+}
+
+#ifdef PIXMAP
+void xline(xdisplay * d, int x1, int y1, int x2, int y2)
+{
+ XDrawLine(d->display, d->pixmap, d->gc, x1, y1, x2, y2);
+ d->lastx = x2, d->lasty = y2;
+ d->screen_changed = 1;
+} void xlineto(xdisplay * d, int x, int y)
+{
+
+ XDrawLine(d->display, d->pixmap, d->gc, d->lastx, d->lasty, x, y);
+ d->lastx = x, d->lasty = y;
+ d->screen_changed = 1;
+} void xrect(xdisplay * d, int x1, int y1, int x2, int y2)
+{
+
+ XDrawRectangle(d->display, d->pixmap, d->gc, x1, y1,
+ (x2 - x1), (y2 - y1));
+ d->lastx = x2, d->lasty = y2;
+ d->screen_changed = 1;
+} void xfillrect(xdisplay * d, int x1, int y1, int x2, int y2)
+{
+
+ XFillRectangle(d->display, d->pixmap, d->gc, x1, y1,
+ (x2 - x1), (y2 - y1));
+ d->lastx = x2, d->lasty = y2;
+ d->screen_changed = 1;
+} void xpoint(xdisplay * d, int x, int y)
+{
+
+ XDrawPoint(d->display, d->pixmap, d->gc, x, y);
+ d->lastx = x, d->lasty = y;
+ d->screen_changed = 1;
+} void xflush(xdisplay * d)
+{
+
+ draw_screen(d);
+ XFlush(d->display);
+}
+
+void xclear_screen(xdisplay * d)
+{
+ xfillrect(d, 0, 0, d->width, d->height);
+ d->screen_changed = 1;
+}
+
+#endif
+void xmoveto(xdisplay * d, int x, int y)
+{
+ d->lastx = x, d->lasty = y;
+} int xsetfont(xdisplay * d, char *font_name)
+{
+
+ if (d->font_struct != (XFontStruct *) NULL) {
+ XFreeFont(d->display, d->font_struct);
+ }
+ d->font_struct = XLoadQueryFont(d->display, font_name);
+ if (!d->font_struct) {
+ fprintf(stderr, "could not load font: %s\n", font_name);
+ exit(3);
+ }
+ return (d->font_struct->max_bounds.ascent + d->font_struct->max_bounds.descent);
+}
+
+void xouttext(xdisplay * d, char *string)
+{
+ int sz;
+
+ sz = strlen(string);
+ XDrawImageString(d->display, d->window, d->gc, d->lastx, d->lasty,
+ string, sz);
+#if 0
+ d->lastx += XTextWidth(d->font_struct, string, sz);
+ d->screen_changed = 1;
+#endif
+} void xresize(xdisplay * d, XEvent * ev)
+{
+
+#ifdef PIXMAP
+ XFreePixmap(d->display, d->pixmap);
+#endif
+ d->width = ev->xconfigure.width;
+ d->height = ev->xconfigure.height;
+#ifdef PIXMAP
+ d->pixmap = XCreatePixmap(d->display, d->window, d->width,
+ d->height, d->depth);
+#endif
+}
+
+#ifdef PIXMAP
+void xarc(xdisplay * d, int x, int y, unsigned int w,
+ unsigned int h, int a1, int a2)
+{
+ XDrawArc(d->display, d->pixmap, d->gc, x, y, w, h, a1, a2);
+} void xfillarc(xdisplay * d, int x, int y, unsigned int w,
+ unsigned int h, int a1, int a2)
+{
+ XFillArc(d->display, d->pixmap, d->gc, x, y, w, h, a1, a2);
+}
+#endif
+
+void xsize_set(xdisplay *d, int width, int height)
+{
+ XResizeWindow(d->display, d->window, width, height);
+}
+
+int xmouse_x(xdisplay * d)
+{
+
+ return d->mouse_x;
+}
+
+int xmouse_y(xdisplay * d)
+{
+ return d->mouse_y;
+}
+
+void xmouse_update(xdisplay * d)
+{
+ Window rootreturn, childreturn;
+ int rootx = 0, rooty = 0, buttons = 0;
+
+ XEvent event;
+
+ if (XCheckMaskEvent(d->display,ButtonPressMask | ButtonReleaseMask, &event)) {
+ if (event.type == ButtonPress)
+ d->mouse_buttons |= 1 << ((XButtonEvent*)(&event))->button;
+ else
+ d->mouse_buttons &= ~( 1 << ((XButtonEvent*)(&event))->button );
+ }
+
+ XQueryPointer(d->display, d->window, &rootreturn, &childreturn,
+ &rootx, &rooty, &(d->mouse_x), &(d->mouse_y),
+ &buttons);
+}
+
+char xkeyboard_query(xdisplay * d) {
+ XEvent event;
+
+ if (XCheckMaskEvent(d->display,KeyPressMask | KeyReleaseMask, &event)) {
+ char *str =
+ XKeysymToString(XLookupKeysym((XKeyPressedEvent*)(&event),0));
+
+ if ( ((XKeyPressedEvent*)(&event))->state &
+ (ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask) )
+ return 0;
+
+ if (str) {
+ char key;
+
+ if (strlen(str) == 1)
+ key = str[0];
+ else if (strcmp(str,"equal") == 0)
+ key = '=';
+ else if (strcmp(str,"minus") == 0)
+ key = '-';
+ else if (strcmp(str,"bracketleft") == 0)
+ key = '[';
+ else if (strcmp(str,"bracketright") == 0)
+ key = ']';
+ else if (strcmp(str,"comma") == 0)
+ key = ',';
+ else if (strcmp(str,"period") == 0)
+ key = '.';
+ else if (strcmp(str,"slash") == 0)
+ key = '/';
+ else return 0;
+
+ if ( ((XKeyPressedEvent*)(&event))->state & ShiftMask )
+ switch(key) {
+ case '=' : key = '+'; break;
+ case '[' : key = '{'; break;
+ case ']' : key = '}'; break;
+ case ',' : key = '<'; break;
+ case '/' : key = '?'; break;
+ default :
+ if (key >= 'a' && key <= 'z')
+ key = key+'A'-'a';
+ break;
+ }
+ return key;
+ }
+ }
+
+ return 0;
+}
+
+int xsize_update(xdisplay *d,int *width,int *height) {
+ XEvent event;
+
+ if (XCheckMaskEvent(d->display,StructureNotifyMask, &event)) {
+ if (event.type == ConfigureNotify) {
+ xupdate_size(d);
+ free_image(d);
+ alloc_image(d);
+ *width = d->linewidth;
+ *height = d->height;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+unsigned int xmouse_buttons(xdisplay * d)
+{
+ return d->mouse_buttons;
+}
+#endif
diff --git a/kscd/kscdmagic/xlib.h b/kscd/kscdmagic/xlib.h
new file mode 100644
index 00000000..077a9052
--- /dev/null
+++ b/kscd/kscdmagic/xlib.h
@@ -0,0 +1,139 @@
+/*
+ * XaoS, a fast portable realtime fractal zoomer
+ * Copyright (C) 1996,1997 by
+ *
+ * Jan Hubicka (hubicka@paru.cas.cz)
+ * Thomas Marsh (tmarsh@austin.ibm.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef XAOS_X11_H
+#define XAOS_X11_H
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+#include <X11/keysymdef.h>
+/*#include "config.h"*/
+/*#define MITSHM*/
+
+#ifdef MITSHM
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/extensions/XShm.h>
+#endif /* MITSHM */
+
+#ifdef n
+#undef n
+#endif
+
+typedef struct {
+ int n;
+ XColor c[256];
+} xcol_t;
+
+typedef struct {
+ int privatecolormap;
+ int usedefault;
+ int nomitshm;
+} xlibparam;
+
+typedef struct {
+ Colormap colormap;
+ Colormap defaultcolormap;
+ int fixedcolormap;
+ int privatecolormap;
+ xlibparam *params;
+ Display *display;
+ Window parent_window;
+ Window window;
+ unsigned int width, height;
+ unsigned int border_width;
+ unsigned long background;
+ int depth;
+ unsigned int classX;
+ Visual *visual;
+ unsigned long valuemask;
+ XSetWindowAttributes *attributes;
+ unsigned long attr_mask;
+ XSizeHints sizehints;
+ int screen;
+ const char *window_name;
+ int status;
+ GC gc;
+ XGCValues xgcvalues;
+ xcol_t xcolor;
+ Pixmap pixmap;
+ XFontStruct *font_struct;
+ int screen_changed;
+ int lastx, lasty;
+ int mouse_x, mouse_y;
+ unsigned int mouse_buttons;
+ int current;
+ XImage *image[2];
+#ifdef MITSHM
+ XShmSegmentInfo xshminfo[2];
+ int SharedMemOption;
+ int SharedMemFlag;
+#endif /* MITSHM */
+ unsigned long pixels[256];
+ char *vbuffs[2];
+ char *data[2];
+ char *vbuff;
+ char *back;
+ int truecolor;
+ int linewidth;
+
+ Cursor cursor;
+} xdisplay;
+
+extern int alloc_shm_image(xdisplay * d);
+extern void free_shm_image(xdisplay * d);
+extern int alloc_image(xdisplay * d);
+extern void free_image(xdisplay * d);
+extern int xupdate_size(xdisplay * d);
+extern void xflip_buffers(xdisplay * d);
+extern xdisplay *xalloc_display(const char *n, int x, int y, int width,int height, xlibparam * p);
+extern void xfree_display(xdisplay * d);
+extern void xsetcolor(xdisplay * d, int col);
+extern int xsetfont(xdisplay * d, char *font_name);
+extern int xalloc_color(xdisplay * d, int r, int g, int b, int readwrite);
+extern void xfree_colors(xdisplay * d);
+extern void xline(xdisplay * d, int x1, int y1, int x2, int y2);
+extern void xmoveto(xdisplay * d, int x, int y);
+extern void xlineto(xdisplay * d, int x, int y);
+extern void xrect(xdisplay * d, int x1, int y1, int x2, int y2);
+extern void xfillrect(xdisplay * d, int x1, int y1, int x2, int y2);
+extern void xarc(xdisplay * d, int x, int y, unsigned int w,
+ unsigned int h, int a1, int a2);
+extern void xfillarc(xdisplay * d, int x, int y, unsigned int w,
+ unsigned int h, int a1, int a2);
+extern void xpoint(xdisplay * d, int x, int y);
+extern void xflush(xdisplay * d);
+extern void xclear_screen(xdisplay * d);
+extern void xrotate_palette(xdisplay * d, int direction, unsigned char c[3][256], int ncolors);
+extern void draw_screen(xdisplay * d);
+extern void xouttext(xdisplay * d, char *string);
+extern void xresize(xdisplay * d, XEvent * ev);
+extern void xsize_set(xdisplay *d, int width, int height);
+extern int xsize_update(xdisplay *d,int *width,int *height);
+extern int xmouse_x(xdisplay * d);
+extern int xmouse_y(xdisplay * d);
+extern void xmouse_update(xdisplay * d);
+extern char xkeyboard_query(xdisplay * d);
+extern unsigned int xmouse_buttons(xdisplay * d);
+
+#endif /* XAOS_X11_H */
diff --git a/kscd/kscdmagic/xlibwrap.cpp b/kscd/kscdmagic/xlibwrap.cpp
new file mode 100644
index 00000000..b5241326
--- /dev/null
+++ b/kscd/kscdmagic/xlibwrap.cpp
@@ -0,0 +1,209 @@
+/* Synaesthesia - program to display sound graphically
+ Copyright (C) 1997 Paul Francis Harrison
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The author may be contacted at:
+ pfh@yoyo.cc.monash.edu.au
+ or
+ 27 Bond St., Mt. Waverley, 3149, Melbourne, Australia
+*/
+
+#if defined(__linux__) || defined (__svr4__) || defined(__osf__)
+
+#include <string.h>
+
+extern "C" {
+#include "xlib.h"
+}
+
+#include "magicconf.h"
+#include "syna.h"
+
+static xlibparam xparams = { 0, 0, 0 };
+static xdisplay *d;
+
+static bool lowColor, paletteInstalled;
+static unsigned char mapping[64];
+
+int setPalette(int ,int r,int g,int b) {
+ return xalloc_color(d,r*257,g*257,b*257,0);
+}
+
+void screenInit(int xHint,int yHint,int widthHint,int heightHint)
+{
+
+ int i;
+
+ d = xalloc_display("KSCD Magic",xHint,yHint,widthHint,heightHint,&xparams);
+
+ if (d == 0)
+ error("setting up a window");
+
+ if (!alloc_image(d))
+ error("allocating window buffer");
+ outWidth = widthHint;
+ outHeight = heightHint;
+
+ //XMoveWindow(d->display,d->window,xHint,yHint);
+
+#define BOUND(x) ((x) > 255 ? 255 : (x))
+#define PEAKIFY(x) BOUND((x) - (x)*(255-(x))/255/2)
+
+ lowColor = (d->depth <= 8);
+
+ /* Set up palette immediately.
+ * Original code has the following commented out.
+ */
+ if (!lowColor) {
+ for(i=0;i<256;i++)
+ attempt(setPalette(i,PEAKIFY((i&15*16)),
+ PEAKIFY((i&15)*16+(i&15*16)/4),
+ PEAKIFY((i&15)*16)),
+ " in X: could not allocate sufficient palette entries");
+ } else {
+ for(i=0;i<64;i++)
+ attempt(mapping[i] = setPalette(i,PEAKIFY((i&7*8)*4),
+ PEAKIFY((i&7)*32+(i&7*8)*2),
+ PEAKIFY((i&7)*32)),
+ " in X: could not allocate sufficient palette entries");
+ }
+}
+
+void screenSetPalette(unsigned char *palette) {
+ int i;
+
+ if (paletteInstalled)
+ xfree_colors(d);
+
+ if (!lowColor) {
+ for(i=0;i<256;i++)
+ attempt(setPalette(i,palette[i*3],palette[i*3+1],palette[i*3+2]),
+ " in X: could not allocate sufficient palette entries");
+ } else {
+ const int array[8] = {0,2,5,7,9,11,13,15};
+ for(i=0;i<64;i++) {
+ int p = (array[i&7]+array[i/8]*16) *3;
+ attempt(mapping[i] = setPalette(i,palette[p],palette[p+1],palette[p+2]),
+ " in X: could not allocate sufficient palette entries");
+ }
+ }
+
+ paletteInstalled = true;
+}
+
+void screenEnd() {
+ if (paletteInstalled)
+ xfree_colors(d);
+ xfree_display(d);
+}
+
+int sizeUpdate()
+{
+ int newWidth,newHeight;
+
+ if (xsize_update(d,&newWidth,&newHeight))
+ {
+ if (newWidth == outWidth && newHeight == outHeight)
+ return 0;
+ //delete[] output;
+ //outWidth = newWidth;
+ //outHeight = newHeight;
+ //output = new unsigned char [outWidth*outHeight*2];
+ //memset(output,32,outWidth*outHeight*2);
+ allocOutput(newWidth,newHeight);
+ return 1;
+ }
+ return 0;
+}
+
+void inputUpdate(int &mouseX,int &mouseY,int &mouseButtons,char &keyHit) {
+ xmouse_update(d);
+ mouseX = xmouse_x(d);
+ mouseY = xmouse_y(d);
+ mouseButtons = xmouse_buttons(d);
+ keyHit = xkeyboard_query(d);
+}
+
+void screenShow(void) {
+ register unsigned long *ptr2 = (unsigned long*)ucoutput;
+ unsigned long *ptr1 = (unsigned long*)d->back;
+ int i = outWidth*outHeight/4;
+ if (lowColor)
+ do {
+ register unsigned int const r1 = *(ptr2++);
+ register unsigned int const r2 = *(ptr2++);
+
+ //if (r1 || r2) {
+#ifdef LITTLEENDIAN
+ register unsigned int const v =
+ mapping[((r1&0xe0ul)>>5)|((r1&0xe000ul)>>10)]
+ |mapping[((r1&0xe00000ul)>>21)|((r1&0xe0000000ul)>>26)]*256U;
+ *(ptr1++) = v |
+ mapping[((r2&0xe0ul)>>5)|((r2&0xe000ul)>>10)]*65536U
+ |mapping[((r2&0xe00000ul)>>21)|((r2&0xe0000000ul)>>26)]*16777216U;
+#else
+ register unsigned int const v =
+ mapping[((r2&0xe0ul)>>5)|((r2&0xe000ul)>>10)]
+ |mapping[((r2&0xe00000ul)>>21)|((r2&0xe0000000ul)>>26)]*256U;
+ *(ptr1++) = v |
+ mapping[((r1&0xe0ul)>>5)|((r1&0xe000ul)>>10)]*65536U
+ |mapping[((r1&0xe00000ul)>>21)|((r1&0xe0000000ul)>>26)]*16777216U;
+#endif
+ //} else ptr1++;
+ } while (--i);
+ else
+ do {
+ // Asger Alstrup Nielsen's (alstrup@diku.dk)
+ // optimized 32 bit screen loop
+ register unsigned int const r1 = *(ptr2++);
+ register unsigned int const r2 = *(ptr2++);
+
+ //if (r1 || r2) {
+#ifdef LITTLEENDIAN
+ register unsigned int const v =
+ ((r1 & 0x000000f0ul) >> 4)
+ | ((r1 & 0x0000f000ul) >> 8)
+ | ((r1 & 0x00f00000ul) >> 12)
+ | ((r1 & 0xf0000000ul) >> 16);
+ *(ptr1++) = v |
+ ((r2 & 0x000000f0ul) << 16 -4)
+ | ((r2 & 0x0000f000ul) << 16 -8)
+ | ((r2 & 0x00f00000ul) << 16 -12)
+ | ((r2 & 0xf0000000ul) << 16 -16);
+#else
+ register unsigned int const v =
+ ((r2 & 0x000000f0ul) >> 4)
+ | ((r2 & 0x0000f000ul) >> 8)
+ | ((r2 & 0x00f00000ul) >> 12)
+ | ((r2 & 0xf0000000ul) >> 16);
+ *(ptr1++) = v |
+ ((r1 & 0x000000f0ul) << 16 -4)
+ | ((r1 & 0x0000f000ul) << 16 -8)
+ | ((r1 & 0x00f00000ul) << 16 -12)
+ | ((r1 & 0xf0000000ul) << 16 -16);
+#endif
+ //} else ptr1++;
+ } while (--i);
+
+ xflip_buffers(d);
+ draw_screen(d);
+ XFlush(d->display);
+}
+
+
+
+#endif // linux || svr4
+
diff --git a/kscd/ledlamp.cpp b/kscd/ledlamp.cpp
new file mode 100644
index 00000000..706c16cf
--- /dev/null
+++ b/kscd/ledlamp.cpp
@@ -0,0 +1,103 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 1997 Richard Moore (moorer@cs.man.ac.uk)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+
+#include <stdio.h>
+#include <qpainter.h>
+#include <qbrush.h>
+#include <qpen.h>
+#include <qcolor.h>
+#include "ledlamp.h"
+#include "ledlamp.moc"
+
+LedLamp::LedLamp(QWidget *parent, Type t) : QFrame(parent),
+ w( 10 ), h( 7 ), dx( 4 )
+{
+ // Make sure we're in a sane state
+ s = Off;
+
+ // Set the frame style
+ // setFrameStyle(Sunken | Box);
+ setGeometry(0,0,h+1,w+1);
+ ledtype = t;
+} // LedLamp
+
+void
+LedLamp::drawContents(QPainter *painter)
+{
+
+
+ QBrush lightBrush(this->foregroundColor());
+ QBrush darkBrush(this->backgroundColor());
+
+ QPen darkPen(this->backgroundColor(),1);
+ QPen lightPen(this->foregroundColor(), 1);
+
+ switch(s)
+ {
+ case On:
+ painter->setBrush(lightBrush);
+ switch (ledtype)
+ {
+ case Rect:
+ painter->drawRect(1,1,w-3, h-2);
+ break;
+ case Loop:
+ painter->setBrush(lightBrush);
+ painter->setPen(lightPen);
+
+ painter->drawLine(0, 2, 0, h-2); // |
+ painter->drawLine(1, 1, w-4, 1); // ~
+ painter->drawLine(w-3, 2, w-3, h-2); // |
+ painter->drawLine(2, h-2, w-3, h-2); //_
+ painter->drawLine(w-6,0,w-6,2); // ---+
+ painter->drawLine(3,h-2,3,h); // +---
+ break;
+ }
+ break;
+
+ case Off:
+ painter->setBrush(darkBrush);
+ switch (ledtype)
+ {
+ case Rect:
+ painter->drawRect(1,1,w-3, h-2);
+ break;
+ case Loop:
+ painter->setBrush(darkBrush);
+ painter->setPen(darkPen);
+
+ painter->fillRect(0,0,w,h, darkBrush);
+ /*
+ painter->drawLine(0, 2, 0, h-2); // |
+ painter->drawLine(1, 1, w-4, 1); // ~
+ painter->drawLine(w-3, 2, w-3, h-2); // |
+ painter->drawLine(2, h-2, w-3, h-2); //_
+ painter->drawLine(w-6,0,w-6,2); // ---+
+ painter->drawLine(3,h-2,3,h); // +---
+ */
+ break;
+ }
+ break;
+
+ default:
+ fprintf(stderr, "LedLamp: INVALID State (%d)\n", s);
+ }
+} // drawContents
+
diff --git a/kscd/ledlamp.h b/kscd/ledlamp.h
new file mode 100644
index 00000000..15d4b8b4
--- /dev/null
+++ b/kscd/ledlamp.h
@@ -0,0 +1,63 @@
+
+/*
+ This file is part of the KDE libraries
+ Copyright (C) 1997 Richard Moore (moorer@cs.man.ac.uk)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef LED_LAMP_H
+#define LED_LAMP_H
+
+#include <qframe.h>
+
+
+class LedLamp : public QFrame
+{
+ Q_OBJECT
+public:
+
+ enum Type { Rect, Loop };
+
+ LedLamp(QWidget *parent=0, Type t=Rect);
+
+ enum State { On, Off };
+
+
+ State state() const { return s; }
+
+
+ void setState(State state) { s= state; repaint(false); }
+
+
+ void toggleState() { if (s == On) s= Off; else if (s == Off) s= On; repaint(false); }
+public slots:
+ void toggle() { toggleState(); };
+ void on() { setState(On); };
+ void off() { setState(Off); };
+protected:
+ void drawContents(QPainter *);
+private:
+ const int w;
+ const int h;
+ const int dx;
+ State s;
+ int ledtype;
+};
+
+
+#endif
diff --git a/kscd/libwm/Makefile.am b/kscd/libwm/Makefile.am
new file mode 100644
index 00000000..ae2a9144
--- /dev/null
+++ b/kscd/libwm/Makefile.am
@@ -0,0 +1,23 @@
+#
+# Makefile.am for libworkman. Based on the example Makefile.am for a
+# shared library.
+#
+
+subdirs = include
+SUBDIRS = audio
+INCLUDES = $(all_includes)
+
+noinst_LTLIBRARIES = libworkman.la
+
+libworkman_la_LDFLAGS = $(ARTSC_LIBS) $(all_libraries)
+libworkman_la_LIBADD = audio/libworkmanaudio.la $(ARTS_LIBASOUND)
+
+libworkman_la_SOURCES = cddb.c cdinfo.c cdrom.c wm_helpers.c cdtext.c\
+database.c index.c scsi.c cdda.c plat_linux_cdda.c plat_sun_cdda.c\
+plat_aix.c plat_bsd386.c plat_freebsd.c plat_hpux.c plat_irix.c \
+plat_linux.c plat_svr4.c plat_ultrix.c plat_news.c plat_openbsd.c \
+plat_osf1.c plat_sun.c plat_scor5.c \
+drv_sony.c drv_toshiba.c
+
+#libworkmanincludedir = $(includedir)/libwm
+#libworkmaninclude_HEADERS = include/wm_cdrom.h include/wm_cdtext.h
diff --git a/kscd/libwm/PLAT_IMPL_STATUS b/kscd/libwm/PLAT_IMPL_STATUS
new file mode 100644
index 00000000..d1919728
--- /dev/null
+++ b/kscd/libwm/PLAT_IMPL_STATUS
@@ -0,0 +1,48 @@
+LIBRARY IMPLEMENTATION STATUS
+-----------------------------
+
+This table should show up where the platform codes differ.
+
+- function not available
+X function available
+\ function dummy, null implementation or commented out
+(X) function available in special configurations
+
+function |aix |bsd386|freebsd|hpux |irix |linux|news |openbsd|osf1 |scor5|sun |svr4 |ultrix|
+------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+
+find_cdrom() | - | - | - | - | - | - | - | - | X | - | X | X | X |
+gen_init() | \ | \ | \ | \ | (X) | \ | \ | \ | \ | \ | X | \ | \ |
+wmcd_open() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+wmcd_close() | - | - | - | - | - | - | X | - | - | - | - | - | - |
+wmcd_reopen() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+wm_scsi() | \ | \ | \ | X | \ | X | \ | \ | \ | X | X | X | \ |
+keep_cd_open() | - | - | - | - | - | \ | - | - | - | \ | - | - | - |
+gen_get_drive_status() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+
+gen_get_trackcount() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_get_trackinfo() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_get_cdlen() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+
+gen_play() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_pause() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_resume() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_stop() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_eject() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_closetray() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+
+scale_volume() | X | X | X | - | - | X | - | X | X | - | - | - | X |
+unscale_volume() | X | X | X | - | - | - | - | X | X | - | - | - | X |
+gen_set_volume() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_get_volume() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+
+cdda_init() | - | - | - | - | - | \ | - | - | - | - | X | - | - |
+get_ack() | - | - | - | - | - | \ | - | - | - | - | X | - | - |
+cdda_kill() | - | - | - | - | - | \ | - | - | - | - | X | - | - |
+gen_set_direction() | - | - | - | - | - | \ | - | - | - | - | X | - | - |
+gen_set_speed() | - | - | - | - | - | \ | - | - | - | - | X | - | - |
+gen_set_loudness() | - | - | - | - | - | \ | - | - | - | - | X | - | - |
+gen_save() | - | - | - | - | - | \ | - | - | - | - | X | - | - |
+------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+
+
+
+
diff --git a/kscd/libwm/README b/kscd/libwm/README
new file mode 100644
index 00000000..69b4ae65
--- /dev/null
+++ b/kscd/libwm/README
@@ -0,0 +1,20 @@
+$Id$
+
+This directory contains the WorkMan library.
+
+libworkman is a multi-plaform CD-Player library for creating various
+CD-Player-UIs.
+
+06.03.2003 Alex Kern <alex.kern@gmx.de>
+ I have reworked most part of libworkman, get rid of most of externals
+ add cdda support for linux and, I hope not breaked it for SUN ;-)
+
+ Interface was cleaned, that means for a potentialy application developers
+ new rules:
+ 1. please include only wm_cdrom.h it's all defines and functions, what
+ you need. You have any exceptions.
+ 2. for cdtext, include wm_cdtext.h
+ 3. for cddb, include wm_cddb.h (think, it's not so powerfull and can be replaced with
+ any new external library.
+ 4. wm_cdinfo declares any externals now, I will change it in the future
+ \ No newline at end of file
diff --git a/kscd/libwm/audio/Makefile.am b/kscd/libwm/audio/Makefile.am
new file mode 100644
index 00000000..318cb0a3
--- /dev/null
+++ b/kscd/libwm/audio/Makefile.am
@@ -0,0 +1,5 @@
+INCLUDES = $(all_includes) $(ARTSC_INCLUDE)
+
+noinst_LTLIBRARIES = libworkmanaudio.la
+
+libworkmanaudio_la_SOURCES = audio.c audio_alsa.c audio_arts.c audio_sun.c
diff --git a/kscd/libwm/audio/audio.c b/kscd/libwm/audio/audio.c
new file mode 100644
index 00000000..39c8330d
--- /dev/null
+++ b/kscd/libwm/audio/audio.c
@@ -0,0 +1,29 @@
+#include "audio.h"
+
+#include <string.h>
+
+struct audio_oops* setup_arts(const char *dev, const char *ctl);
+struct audio_oops* setup_alsa(const char *dev, const char *ctl);
+
+struct audio_oops* setup_soundsystem(const char* ss, const char* dev, const char* ctl)
+{
+ if(!ss) {
+ ERRORLOG("audio: Internal error, trying to setup a NULL soundsystem.\n");
+ return NULL;
+ }
+
+#ifdef USE_ARTS
+ if(!strcmp(ss, "arts"))
+ return setup_arts(dev, ctl);
+#endif
+#if defined(HAVE_ARTS_LIBASOUND2)
+ if(!strcmp(ss, "alsa"))
+ return setup_alsa(dev, ctl);
+#endif
+#ifdef USE_SUN_AUDIO
+ if(!strcmp(ss, "sun"))
+ return setup_sun_audio(dev, ctl);
+#endif
+ ERRORLOG("audio: unknown soundsystem '%s'\n", ss);
+ return NULL;
+}
diff --git a/kscd/libwm/audio/audio.h b/kscd/libwm/audio/audio.h
new file mode 100644
index 00000000..089ea116
--- /dev/null
+++ b/kscd/libwm/audio/audio.h
@@ -0,0 +1,21 @@
+/*
+ * Audio 'LIB' defines
+ */
+#include "../include/wm_cdda.h"
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+struct audio_oops {
+ int (*wmaudio_open)(void);
+ int (*wmaudio_close)(void);
+ int (*wmaudio_play)(struct cdda_block*);
+ int (*wmaudio_stop)(void);
+ int (*wmaudio_state)(struct cdda_block*);
+ int (*wmaudio_balance)(int);
+ int (*wmaudio_volume)(int);
+};
+
+extern struct audio_oops* setup_soundsystem(const char*, const char*, const char*);
+
diff --git a/kscd/libwm/audio/audio_alsa.c b/kscd/libwm/audio/audio_alsa.c
new file mode 100644
index 00000000..b1d4e938
--- /dev/null
+++ b/kscd/libwm/audio/audio_alsa.c
@@ -0,0 +1,334 @@
+/*
+ * Driver for Advanced Linux Sound Architecture, http://alsa.jcu.cz
+ *
+ * mpg123 comments:
+ * Code by Anders Semb Hermansen <ahermans@vf.telia.no>
+ * Cleanups by Jaroslav Kysela <perex@jcu.cz>
+ * Ville Syrjala <syrjala@sci.fi>
+ *
+ * adopted for libworkman cdda audio backend from Alexander Kern alex.kern@gmx.de
+ *
+ * Adapted to support both ALSA V0.x and V1.x APIs for PCM calls
+ * (Philip Nelson <teamdba@scotdb.com> 2004-03-15)
+ *
+ * This file comes under GPL license.
+ */
+
+
+
+#include <config.h>
+
+#if defined(HAVE_ARTS_LIBASOUND2)
+
+#include <alsa/asoundlib.h>
+#include "audio.h"
+
+char* device = NULL;
+snd_pcm_t *handle;
+
+snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */
+int rate = 44100; /* stream rate */
+int channels = 2; /* count of channels */
+int buffer_time = 2000000; /* ring buffer length in us */
+int period_time = 100000; /* period time in us */
+
+snd_pcm_sframes_t buffer_size;
+snd_pcm_sframes_t period_size;
+
+int alsa_open(void);
+int alsa_close(void);
+int alsa_stop(void);
+int alsa_play(struct cdda_block *blk);
+int alsa_state(struct cdda_block *blk);
+struct audio_oops* setup_alsa(const char *dev, const char *ctl);
+
+static int set_hwparams(snd_pcm_hw_params_t *params,
+ snd_pcm_access_t accesspar)
+{
+ int err, dir, new_rate;
+
+ /* choose all parameters */
+ err = snd_pcm_hw_params_any(handle, params);
+ if (err < 0) {
+ ERRORLOG("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* set the interleaved read/write format */
+ err = snd_pcm_hw_params_set_access(handle, params, accesspar);
+ if (err < 0) {
+ ERRORLOG("Access type not available for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* set the sample format */
+ err = snd_pcm_hw_params_set_format(handle, params, format);
+ if (err < 0) {
+ ERRORLOG("Sample format not available for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* set the count of channels */
+ err = snd_pcm_hw_params_set_channels(handle, params, channels);
+ if (err < 0) {
+ ERRORLOG("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err));
+ return err;
+ }
+ /* set the stream rate */
+#if (SND_LIB_MAJOR < 1)
+ err = new_rate = snd_pcm_hw_params_set_rate_near(handle, params, rate, 0);
+#else
+ new_rate = rate;
+ err = snd_pcm_hw_params_set_rate_near(handle, params, &new_rate, 0);
+#endif
+ if (err < 0) {
+ ERRORLOG("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
+ return err;
+ }
+ if (new_rate != rate) {
+ ERRORLOG("Rate doesn't match (requested %iHz, get %iHz)\n", rate, new_rate);
+ return -EINVAL;
+ }
+ /* set the buffer time */
+#if (SND_LIB_MAJOR < 1)
+ err = snd_pcm_hw_params_set_buffer_time_near(handle, params, buffer_time, &dir);
+#else
+ err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);
+#endif
+ if (err < 0) {
+ ERRORLOG("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err));
+ return err;
+ }
+#if (SND_LIB_MAJOR < 1)
+ buffer_size = snd_pcm_hw_params_get_buffer_size(params);
+#else
+ err = snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
+ if (err < 0) {
+ ERRORLOG("Unable to get buffer size : %s\n", snd_strerror(err));
+ return err;
+ }
+#endif
+ DEBUGLOG("buffersize %i\n", buffer_size);
+
+ /* set the period time */
+#if (SND_LIB_MAJOR < 1)
+ err = snd_pcm_hw_params_set_period_time_near(handle, params, period_time, &dir);
+#else
+ err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);
+#endif
+ if (err < 0) {
+ ERRORLOG("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err));
+ return err;
+ }
+
+#if (SND_LIB_MAJOR < 1)
+ period_size = snd_pcm_hw_params_get_period_size(params, &dir);
+#else
+ err = snd_pcm_hw_params_get_period_size(params, &period_size, &dir);
+ if (err < 0) {
+ ERRORLOG("Unable to get hw period size: %s\n", snd_strerror(err));
+ }
+#endif
+
+ DEBUGLOG("period_size %i\n", period_size);
+
+ /* write the parameters to device */
+ err = snd_pcm_hw_params(handle, params);
+ if (err < 0) {
+ ERRORLOG("Unable to set hw params for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ return 0;
+}
+
+static int set_swparams(snd_pcm_sw_params_t *swparams)
+{
+ int err;
+
+ /* get the current swparams */
+ err = snd_pcm_sw_params_current(handle, swparams);
+ if (err < 0) {
+ ERRORLOG("Unable to determine current swparams for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* start the transfer when the buffer is full */
+ err = snd_pcm_sw_params_set_start_threshold(handle, swparams, buffer_size);
+ if (err < 0) {
+ ERRORLOG("Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* allow the transfer when at least period_size samples can be processed */
+ err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size);
+ if (err < 0) {
+ ERRORLOG("Unable to set avail min for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* align all transfers to 1 sample */
+ err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1);
+ if (err < 0) {
+ ERRORLOG("Unable to set transfer align for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* write the parameters to the playback device */
+ err = snd_pcm_sw_params(handle, swparams);
+ if (err < 0) {
+ ERRORLOG("Unable to set sw params for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ return 0;
+}
+
+int alsa_open( void )
+{
+ int err;
+
+ snd_pcm_hw_params_t *hwparams;
+ snd_pcm_sw_params_t *swparams;
+
+ DEBUGLOG("alsa_open\n");
+
+ snd_pcm_hw_params_alloca(&hwparams);
+ snd_pcm_sw_params_alloca(&swparams);
+
+ if((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0/*SND_PCM_NONBLOCK*/)) < 0 ) {
+ ERRORLOG("open failed: %s\n", snd_strerror(err));
+ return -1;
+ }
+
+ if((err = set_hwparams(hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
+ ERRORLOG("Setting of hwparams failed: %s\n", snd_strerror(err));
+ return -1;
+ }
+ if((err = set_swparams(swparams)) < 0) {
+ ERRORLOG("Setting of swparams failed: %s\n", snd_strerror(err));
+ return -1;
+ }
+
+ return 0;
+}
+
+int alsa_close( void )
+{
+ int err;
+
+ DEBUGLOG("alsa_close\n");
+
+ err = alsa_stop();
+
+#if (SND_LIB_MAJOR < 1)
+ err = snd_pcm_close(handle);
+#else
+ err = snd_pcm_close(handle);
+#endif
+
+ free(device);
+
+ return err;
+}
+
+/*
+ * Play some audio and pass a status message upstream, if applicable.
+ * Returns 0 on success.
+ */
+int
+alsa_play(struct cdda_block *blk)
+{
+ signed short *ptr;
+ int err = 0, frames;
+
+ ptr = (signed short *)blk->buf;
+ frames = blk->buflen / (channels * 2);
+ DEBUGLOG("play %i frames, %i bytes\n", frames, blk->buflen);
+ while (frames > 0) {
+ err = snd_pcm_writei(handle, ptr, frames);
+
+ if (err == -EAGAIN)
+ continue;
+ if(err == -EPIPE) {
+ err = snd_pcm_prepare(handle);
+ continue;
+ } else if (err < 0)
+ break;
+
+ ptr += err * channels;
+ frames -= err;
+ DEBUGLOG("played %i, rest %i\n", err / channels, frames);
+ }
+
+ if (err < 0) {
+ ERRORLOG("alsa_write failed: %s\n", snd_strerror(err));
+ err = snd_pcm_prepare(handle);
+
+ if (err < 0) {
+ ERRORLOG("Unable to snd_pcm_prepare pcm stream: %s\n", snd_strerror(err));
+ }
+ blk->status = WM_CDM_CDDAERROR;
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * Stop the audio immediately.
+ */
+int
+alsa_stop( void )
+{
+ int err;
+
+ DEBUGLOG("alsa_stop\n");
+
+ err = snd_pcm_drop(handle);
+ if (err < 0) {
+ ERRORLOG("Unable to drop pcm stream: %s\n", snd_strerror(err));
+ }
+
+ err = snd_pcm_prepare(handle);
+ if (err < 0) {
+ ERRORLOG("Unable to snd_pcm_prepare pcm stream: %s\n", snd_strerror(err));
+ }
+
+ return err;
+}
+
+/*
+ * Get the current audio state.
+ */
+int
+alsa_state(struct cdda_block *blk)
+{
+ return -1; /* not implemented yet for ALSA */
+}
+
+static struct audio_oops alsa_oops = {
+ .wmaudio_open = alsa_open,
+ .wmaudio_close = alsa_close,
+ .wmaudio_play = alsa_play,
+ .wmaudio_stop = alsa_stop,
+ .wmaudio_state = alsa_state,
+ .wmaudio_balance = NULL,
+ .wmaudio_volume = NULL
+};
+
+struct audio_oops*
+setup_alsa(const char *dev, const char *ctl)
+{
+ static int init_complete = 0;
+
+ if(dev && strlen(dev) > 0) {
+ device = strdup(dev);
+ } else {
+ device = strdup("plughw:0,0"); /* playback device */
+ }
+
+ if(init_complete) {
+ ERRORLOG("already initialized\n");
+ return NULL;
+ }
+ if(!alsa_open())
+ init_complete = 1;
+ else
+ return NULL;
+
+ return &alsa_oops;
+}
+
+#endif /* ALSA */
diff --git a/kscd/libwm/audio/audio_arts.c b/kscd/libwm/audio/audio_arts.c
new file mode 100644
index 00000000..a7b033c9
--- /dev/null
+++ b/kscd/libwm/audio/audio_arts.c
@@ -0,0 +1,141 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Linux digital audio functions.
+ *
+ *
+ * Forget /dev/audio
+ * most modern soundcards accept 16LE with 44.1kHz
+ * Alexander Kern alex.kern@gmx.de
+ */
+
+#include <config.h>
+
+#ifdef USE_ARTS
+
+#include <artsc.h>
+
+#include "audio.h"
+
+arts_stream_t arts_stream = NULL;
+
+int arts_open(void);
+int arts_close(void);
+int arts_stop(void);
+int arts_play(struct cdda_block *blk);
+int arts_state(struct cdda_block *blk);
+struct audio_oops* setup_arts(const char *dev, const char *ctl);
+
+/*
+ * Initialize the audio device.
+ */
+int
+arts_open(void)
+{
+ int err;
+
+ DEBUGLOG("arts_open\n");
+
+ if(!(arts_stream = arts_play_stream(44100, 16, 2, "cddaslave"))) {
+ ERRORLOG("cannot open ARTS stream for playback\n");
+ return -1;
+ }
+ /* 1000 ms because we read 75 frames = 1 sec */
+ if((err = arts_stream_set(arts_stream, ARTS_P_BUFFER_TIME, 1000)) < 0) {
+ ERRORLOG("arts_stream_set failed (%s)\n", arts_error_text(err));
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Close the audio device.
+ */
+int
+arts_close(void)
+{
+ arts_stop();
+
+ DEBUGLOG("arts_close\n");
+ arts_close_stream(arts_stream);
+
+ arts_free();
+
+ return 0;
+}
+
+/*
+ * Play some audio and pass a status message upstream, if applicable.
+ * Returns 0 on success.
+ */
+int
+arts_play(struct cdda_block *blk)
+{
+ int err;
+
+ if((err = arts_write(arts_stream, blk->buf, blk->buflen)) < 0) {
+ ERRORLOG("arts_write failed (%s)\n", arts_error_text(err));
+ blk->status = WM_CDM_CDDAERROR;
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Stop the audio immediately.
+ */
+int
+arts_stop(void)
+{
+ DEBUGLOG("arts_stop\n");
+ return 0;
+}
+
+/*
+ * Get the current audio state.
+ */
+int
+arts_state(struct cdda_block *blk)
+{
+ return -1; /* not implemented yet for ARTS */
+}
+
+static struct audio_oops arts_oops = {
+ .wmaudio_open = arts_open,
+ .wmaudio_close = arts_close,
+ .wmaudio_play = arts_play,
+ .wmaudio_stop = arts_stop,
+ .wmaudio_state = arts_state,
+ .wmaudio_balance = NULL,
+ .wmaudio_volume = NULL
+};
+
+struct audio_oops*
+setup_arts(const char *dev, const char *ctl)
+{
+ int err;
+
+ if((err = arts_init())) {
+ ERRORLOG("cannot initialize ARTS audio subsystem (%s)\n", arts_error_text(err));
+ return NULL;
+ }
+
+ arts_open();
+
+ return &arts_oops;
+}
+#endif
diff --git a/kscd/libwm/audio/audio_sun.c b/kscd/libwm/audio/audio_sun.c
new file mode 100644
index 00000000..78b1ab21
--- /dev/null
+++ b/kscd/libwm/audio/audio_sun.c
@@ -0,0 +1,525 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Sun (really Solaris) digital audio functions.
+ */
+
+#include <config.h>
+
+#ifdef USE_SUN_AUDIO
+
+#include <stdio.h>
+#include <malloc.h>
+#include <sys/ioctl.h>
+#include <sys/audioio.h>
+#include <sys/stropts.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include "audio.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * Since there's a lag time between writing audio to the audio device and
+ * hearing it, we need to make sure the status indicators correlate to what's
+ * playing out the speaker. Luckily, Solaris gives us some audio
+ * synchronization facilities that make this pretty easy.
+ *
+ * We maintain a circular queue of status information. When we write some
+ * sound to the audio device, we put its status info into the queue. We write
+ * a marker into the audio stream; when the audio device driver encounters the
+ * marker, it increments a field in a status structure. When we see that
+ * field go up, we grab the next status structure from the queue and send it
+ * to the parent process.
+ *
+ * The minimum size of the queue depends on the latency of the audio stream.
+ */
+#define QSIZE 500
+
+struct cdda_block queue[QSIZE];
+int qtail;
+int qstart;
+
+/*
+ * We only send WM_CDM_PLAYING status messages upstream when the CD is supposed
+ * to be playing; this is used to keep track.
+ */
+extern int playing;
+
+static int aufd, aucfd;
+static int raw_audio = 1; /* Can /dev/audio take 44.1KHz stereo? */
+
+/*
+ * For fast linear-to-ulaw mapping, we use a lookup table that's generated
+ * at startup.
+ */
+unsigned char *ulawmap, linear_to_ulaw();
+
+char *getenv();
+
+/*
+ * Dummy signal handler so writes to /dev/audio will interrupt.
+ */
+static void
+dummy( void )
+{
+ signal(SIGALRM, dummy);
+}
+
+/*
+ * Initialize the audio device.
+ */
+void
+sun_audio_init( void )
+{
+ audio_info_t info;
+ char *audiodev, *acdev;
+ int linval;
+
+ audiodev = getenv("AUDIODEV");
+ if (audiodev == NULL ||
+ strncmp("/dev/", audiodev, 5) ||
+ strstr(audiodev, "/../") )
+ audiodev = "/dev/audio";
+
+ acdev = malloc(strlen(audiodev) + 4);
+ if (acdev == NULL)
+ {
+ perror("Can't allocate audio control filename");
+ exit(1);
+ }
+ strcpy(acdev, audiodev);
+ strcat(acdev, "ctl");
+
+ aucfd = open(acdev, O_WRONLY, 0);
+ if (aucfd < 0)
+ {
+ perror(acdev);
+ exit(1);
+ }
+ free(acdev);
+
+ aufd = open(audiodev, O_WRONLY, 0);
+ if (aufd < 0)
+ {
+ perror(audiodev);
+ exit(1);
+ }
+
+ signal(SIGALRM, dummy);
+
+ /*
+ * Try to set the device to CD-style audio; we can process it
+ * with the least CPU overhead.
+ */
+ AUDIO_INITINFO(&info);
+ info.play.sample_rate = 44100;
+ info.play.channels = 2;
+ info.play.precision = 16;
+ info.play.encoding = AUDIO_ENCODING_LINEAR;
+ info.play.pause = 0;
+ info.record.pause = 0;
+ info.monitor_gain = 0;
+
+ if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+ if (errno == EINVAL)
+ {
+ /*
+ * Oh well, so much for that idea.
+ */
+ AUDIO_INITINFO(&info);
+ info.play.sample_rate = 8000;
+ info.play.channels = 1;
+ info.play.precision = 8;
+ info.play.encoding = AUDIO_ENCODING_ULAW;
+ info.play.pause = 0;
+ info.record.pause = 0;
+ info.monitor_gain = 0;
+ if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+ {
+ perror("Can't set up audio device");
+ exit(1);
+ }
+
+ /*
+ * Initialize the linear-to-ulaw mapping table.
+ */
+ if (ulawmap == NULL)
+ ulawmap = malloc(65536);
+ if (ulawmap == NULL)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ for (linval = 0; linval < 65536; linval++)
+ ulawmap[linval] = linear_to_ulaw(linval-32768);
+ ulawmap += 32768;
+ raw_audio = 0;
+ }
+ else
+ {
+ perror(audiodev);
+ exit(1);
+ }
+}
+
+/*
+ * Get ready to play some sound.
+ */
+void
+sun_audio_ready( void )
+{
+ audio_info_t info;
+
+ /*
+ * Start at the correct queue position.
+ */
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ qtail = info.play.eof % QSIZE;
+ qstart = qtail;
+
+ queue[qtail].status = WM_CDM_PLAYING;
+}
+
+/*
+ * Stop the audio immediately.
+ */
+int
+sun_audio_stop( void )
+{
+ if (ioctl(aufd, I_FLUSH, FLUSHRW) < 0)
+ perror("flush");
+ return 0;
+}
+
+/*
+ * Close the audio device.
+ */
+int
+sun_audio_close( void )
+{
+ wmaudio_stop();
+ close(aufd);
+ close(aucfd);
+ return 0;
+}
+
+/*
+ * Set the volume level.
+ */
+int
+sun_audio_volume(int level)
+{
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ info.play.gain = level;
+ if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) {
+ perror("AUDIO_SETINFO");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Set the balance level.
+ */
+int
+sun_audio_balance(int level)
+{
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ level *= AUDIO_RIGHT_BALANCE;
+ info.play.balance = level / 255;
+ if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) {
+ perror("AUDIO_SETINFO");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Mark the most recent audio block on the queue as the last one.
+ */
+void
+sun_audio_mark_last( void )
+{
+ queue[qtail].status = WM_CDM_TRACK_DONE;
+}
+
+/*
+ * Figure out the most recent status information and send it upstream.
+ */
+int
+sun_audio_send_status( void )
+{
+ audio_info_t info;
+ int qhead;
+
+ /*
+ * Now send the most current status information to our parent.
+ */
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+ perror("AUDIO_GETINFO");
+ qhead = info.play.eof % QSIZE;
+
+ if (qhead != qstart && playing)
+ {
+ int balance;
+
+ if (queue[qhead].status != WM_CDM_TRACK_DONE)
+ queue[qhead].status = WM_CDM_PLAYING;
+ queue[qhead].volume = info.play.gain;
+ queue[qhead].balance = (info.play.balance * 255) /
+ AUDIO_RIGHT_BALANCE;
+
+ send_status(queue + qhead);
+ qstart = -1;
+ }
+
+ return (queue[qhead].status == WM_CDM_TRACK_DONE);
+}
+
+/*
+ * Play some audio and pass a status message upstream, if applicable.
+ * Returns 0 on success.
+ */
+int
+sun_audio_play(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+ int i;
+ short *buf16;
+ int alarmcount = 0;
+ struct itimerval it;
+ long playablelen;
+
+ alarm(1);
+ playablelen = dev_audio_convert(rawbuf, buflen, blk);
+ while (write(aufd, rawbuf, playablelen) <= 0)
+ if (errno == EINTR)
+ {
+ if (! raw_audio && alarmcount++ < 5)
+ {
+ /*
+ * 8KHz /dev/audio blocks for several seconds
+ * waiting for its queue to drop below a low
+ * water mark.
+ */
+ wmaudio_send_status();
+ timerclear(&it.it_interval);
+ timerclear(&it.it_value);
+ it.it_value.tv_usec = 500000;
+ setitimer(ITIMER_REAL, &it, NULL);
+ continue;
+ }
+
+/* close(aufd);
+ close(aucfd);
+ wmaudio_init();
+*/ sun_audio_stop();
+ alarm(2);
+ continue;
+ }
+ else
+ {
+ blk->status = WM_CDM_CDDAERROR;
+ return (-1);
+ }
+ alarm(0);
+
+ /*
+ * Mark this spot in the audio stream.
+ *
+ * Marks don't always succeed (if the audio buffer is empty
+ * this call will block forever) so do it asynchronously.
+ */
+ fcntl(aufd, F_SETFL, O_NONBLOCK);
+ if (write(aufd, rawbuf, 0) < 0)
+ {
+ if (errno != EAGAIN)
+ perror("audio mark");
+ }
+ else
+ qtail = (qtail + 1) % QSIZE;
+
+ fcntl(aufd, F_SETFL, 0);
+
+ queue[qtail] = *blk;
+
+ if (wmaudio_send_status() < 0)
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * Get the current audio state.
+ */
+int
+sun_audio_state(struct cdda_block *blk)
+{
+ audio_info_t info;
+ int balance;
+
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+ perror("AUDIO_GETINFO");
+ blk->volume = info.play.gain;
+ blk->balance = (info.play.balance * 255) / AUDIO_RIGHT_BALANCE;
+ return 0;
+}
+
+/*
+** This routine converts from linear to ulaw.
+**
+** Craig Reese: IDA/Supercomputing Research Center
+** Joe Campbell: Department of Defense
+** 29 September 1989
+**
+** References:
+** 1) CCITT Recommendation G.711 (very difficult to follow)
+** 2) "A New Digital Technique for Implementation of Any
+** Continuous PCM Companding Law," Villeret, Michel,
+** et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
+** 1973, pg. 11.12-11.17
+** 3) MIL-STD-188-113,"Interoperability and Performance Standards
+** for Analog-to_Digital Conversion Techniques,"
+** 17 February 1987
+**
+** Input: Signed 16 bit linear sample
+** Output: 8 bit ulaw sample
+*/
+#define ZEROTRAP /* turn on the trap as per the MIL-STD */
+#define BIAS 0x84 /* define the add-in bias for 16 bit samples */
+#define CLIP 32635
+
+unsigned char
+linear_to_ulaw( sample )
+int sample;
+{
+ static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
+ int sign, exponent, mantissa;
+ unsigned char ulawbyte;
+
+ /* Get the sample into sign-magnitude. */
+ sign = (sample >> 8) & 0x80; /* set aside the sign */
+ if ( sign != 0 ) sample = -sample; /* get magnitude */
+ if ( sample > CLIP ) sample = CLIP; /* clip the magnitude */
+
+ /* Convert from 16 bit linear to ulaw. */
+ sample = sample + BIAS;
+ exponent = exp_lut[( sample >> 7 ) & 0xFF];
+ mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
+ ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa );
+#ifdef ZEROTRAP
+ if ( ulawbyte == 0 ) ulawbyte = 0x02; /* optional CCITT trap */
+#endif
+
+ return ulawbyte;
+}
+
+/*
+ * Downsample a block of CDDA data, if necessary, for playing out an old-style
+ * audio device.
+ */
+long
+dev_audio_convert(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+ short *buf16 = (short *)rawbuf;
+ int i, j, samples;
+ int mono_value;
+ unsigned char *rbend = rawbuf + buflen;
+
+ /* Don't do anything if the audio device can take the raw values. */
+ if (raw_audio)
+ return (buflen);
+
+ for (i = 0; buf16 < (short *)(rbend); i++)
+ {
+ /* Downsampling to 8KHz is a little irregular. */
+ samples = (i & 1) ? ((i % 20) ? 10 : 12) : 12;
+
+ /* And unfortunately, we don't always end on a nice boundary. */
+ if (buf16 + samples > (short *)(rbend))
+ samples = ((short *)rbend) - buf16;
+
+ /*
+ * No need to average all the values; taking the first one
+ * is sufficient and less CPU-intensive. But we do need to
+ * do both channels.
+ */
+ mono_value = (buf16[0] + buf16[1]) / 2;
+ buf16 += samples;
+ rawbuf[i] = ulawmap[mono_value];
+ }
+
+ return (i);
+}
+
+static struct audio_oops sun_audio_oops = {
+ .wmaudio_open = sun_audio_open,
+ .wmaudio_close = sun_audio_close,
+ .wmaudio_play = sun_audio_play,
+ .wmaudio_stop = sun_audio_stop,
+ .wmaudio_state = sun_audio_state,
+ .wmaudio_balance = sun_audio_balance,
+ .wmaudio_volume = sun_audio_volume
+};
+
+struct audio_oops*
+setup_sun_audio(const char *dev, const char *ctl)
+{
+ int err;
+
+ if((err = sun_audio_init())) {
+ ERRORLOG("cannot initialize SUN /dev/audio subsystem \n");
+ return NULL;
+ }
+
+ sun_audio_open();
+
+ return &sun_audio_oops;
+}
+
+#endif /* USE_SUN_AUDIO */
diff --git a/kscd/libwm/buildindex.c b/kscd/libwm/buildindex.c
new file mode 100644
index 00000000..8edc3684
--- /dev/null
+++ b/kscd/libwm/buildindex.c
@@ -0,0 +1,221 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Build a WorkMan database index file from a flat text file. Requires
+ * 4.4BSD libdb library.
+ */
+
+static char buildindex_id[] = "$Id$";
+
+#include <stdio.h>
+#include <db.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netinet/in.h> /* for htonl() */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+char *strrchr();
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ DB *db;
+ DBT key, data;
+ FILE *fp;
+ int lock = 1, i = 0, locked, frame;
+ char buf[1000], indname[MAXPATHLEN + 100], framebuf[8], *c;
+ unsigned long pos;
+ BTREEINFO bt;
+ struct stat st;
+
+ if (argc > 2 && !strcmp(argv[1], "-n"))
+ {
+ lock = 0;
+ i++;
+ }
+
+ if (argc < i + 2)
+ {
+ fprintf(stderr, "Usage: %s [-n] dbfile [dbfile ...]\n",
+ argv[0]);
+ exit(1);
+ }
+
+ data.data = &pos;
+ data.size = sizeof(pos);
+ key.data = framebuf;
+ key.size = 7; /* %07d */
+
+ while (++i < argc)
+ {
+ fprintf(stderr, "Building index for %s\n", argv[i]);
+
+ if ((fp = fopen(argv[i], "r")) == NULL)
+ {
+ perror(argv[i]);
+ continue;
+ }
+
+ /*
+ * Figure out the file's mode, uid, gid, so we can set the
+ * permissions on the index file to the same thing.
+ */
+ if (fstat(fileno(fp), &st))
+ {
+ sprintf(indname, "%s: fstat", argv[i]);
+ perror(indname);
+ fclose(fp);
+ continue;
+ }
+
+ if (lock && lockit(fileno(fp), F_WRLCK))
+ {
+ sprintf(indname, "%s: Warning: Couldn't lock", argv[i]);
+ perror(indname);
+ locked = 0;
+ }
+ else
+ locked = 1;
+
+ /*
+ * Create a database file.
+ */
+ bt.flags = R_DUP; /* allow duplicate keys */
+ bt.cachesize = 0;
+ bt.psize = 0;
+ bt.lorder = 4321;
+ bt.minkeypage = 0;
+ bt.compare = NULL; /* use lexical comparison */
+ bt.prefix = NULL; /* no prefix comparisons */
+
+ /* Index files have ".ind" extensions */
+ sprintf(indname, "%s.ind", argv[i]);
+ if ((db = dbopen(indname, O_CREAT | O_RDWR | O_TRUNC,
+ st.st_mode, DB_BTREE, &bt)) == NULL)
+ {
+ perror(indname);
+ if (locked)
+ lockit(fileno(fp), F_UNLCK);
+ fclose(fp);
+ continue;
+ }
+
+ /*
+ * Now loop through the text file, inserting a record into
+ * the index file for each "tracks" line.
+ */
+ while (! feof(fp))
+ {
+ pos = ftell(fp);
+ buf[0] = '\0';
+ if (fgets(buf, sizeof(buf), fp) == NULL || ! buf[0])
+ {
+ /* End of file? */
+ if (feof(fp))
+ break;
+
+ /* Nope. A read error. Unlink the database. */
+ perror(argv[i]);
+ (void) unlink(indname);
+ break;
+ }
+
+ if (strncmp(buf, "tracks ", 7))
+ continue;
+
+ /*
+ * Found the start of a record. Figure out the start
+ * time of the last track and put an entry in the
+ * index file with that as the key.
+ */
+ c = strrchr(buf, ' '); /* this will always succeed */
+ *c = '\0';
+ c = strrchr(buf, ' '); /* this should too, but... */
+ if (c == NULL)
+ {
+ fprintf(stderr,
+ "%s: Malformed tracks line at %lu\n",
+ argv[i], pos);
+ continue;
+ }
+ sscanf(c+1, "%d", &frame);
+ sprintf(framebuf, "%07d", frame);
+ pos = htonl(pos);
+
+ if ((db->put)(db, &key, &data, 0))
+ {
+ perror(indname);
+ unlink(indname);
+ break;
+ }
+ }
+
+ /*
+ * Clean up.
+ */
+ (void) (db->close)(db);
+ if (locked)
+ lockit(fileno(fp), F_UNLCK);
+ }
+}
+
+/*
+ * Lock a file. Time out after a little while if we can't get a lock;
+ * this usually means the locking system is broken.
+ *
+ * Unfortunately, if there are lots of people contending for a lock,
+ * this can result in the file not getting locked when it probably should.
+ */
+int
+lockit(fd, type)
+ int fd;
+ int type;
+{
+ struct flock fl;
+ int result, timer = 0;
+
+ fl.l_type = type;
+ fl.l_whence = 0;
+ fl.l_start = 0;
+ fl.l_len = 0;
+
+ while ((result = fcntl(fd, F_SETLK, &fl)) < 0)
+ {
+ if (errno != EACCES || errno != EAGAIN)
+ break;
+ if (timer++ == 30)
+ {
+ errno = ETIMEDOUT;
+ break;
+ }
+
+ sleep(1);
+ }
+
+ return (result);
+}
diff --git a/kscd/libwm/cdda.c b/kscd/libwm/cdda.c
new file mode 100644
index 00000000..ca8d76ba
--- /dev/null
+++ b/kscd/libwm/cdda.c
@@ -0,0 +1,458 @@
+/***************************************************************************
+ cdda.c - description
+ -------------------
+ begin : Mon Jan 27 2003
+ copyright : (C) 2003 by Alex Kern
+ email : alex.kern@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 is a common cddamaster piece of code *
+ * *
+ ***************************************************************************/
+
+#include <string.h>
+#include <sys/poll.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdda.h"
+#include "include/wm_cdrom.h"
+#include "audio/audio.h"
+#include <pthread.h>
+
+#if defined(BUILD_CDDA)
+
+static pthread_t thread_read;
+static pthread_t thread_play;
+
+int get_next_block(int x);
+void *cdda_fct_read(void* arg);
+void *cdda_fct_play(void* arg);
+
+#define NUMBLOCKS 2
+#define NUMFRAMES 10
+
+static struct cdda_block blks[NUMBLOCKS];
+static pthread_mutex_t blks_mutex[NUMBLOCKS];
+
+static struct cdda_device dev;
+static pthread_cond_t wakeup_audio;
+
+/*
+ * Loudness setting, plus the floating volume multiplier and decaying-average
+ * volume level.
+ */
+static unsigned int loudness = 0, volume = 32768, level;
+
+/*
+ * This is non-null if we're saving audio to a file.
+ */
+static FILE *output = NULL;
+
+/*
+ * These are driverdependent oops
+ *
+ */
+static struct audio_oops *oops = NULL;
+
+/*
+ * Audio file header format.
+ */
+typedef unsigned long u_32;
+struct auheader {
+ u_32 magic;
+ u_32 hdr_size;
+ u_32 data_size;
+ u_32 encoding;
+ u_32 sample_rate;
+ u_32 channels;
+};
+
+/* had to change #ifdef to #if -> see wm_cdda.h */
+#ifdef __FreeBSD__
+/* Phungus not with htonl on FreeBSD */
+#include <sys/param.h>
+#else
+#if WM_BIG_ENDIAN
+# ifndef htonl
+# define htonl(x) (x)
+# endif
+#else
+extern unsigned long htonl(unsigned long);
+#endif
+#endif
+
+/*
+ * Try to initialize the CDDA slave. Returns 0 on success.
+ */
+int
+gen_cdda_init( struct wm_drive *d )
+{
+ int ret = 0;
+
+ if (d->cdda_slave > -1)
+ return 0;
+
+ memset(&blks, 0, sizeof(blks));
+
+ dev.fd = -1;
+ dev.frames_at_once = NUMFRAMES;
+ dev.blocks = blks;
+ dev.numblocks = NUMBLOCKS;
+ dev.status = WM_CDM_UNKNOWN;
+ dev.devname = d->cd_device;
+
+ if ((ret = wmcdda_init(&dev)))
+ return ret;
+
+ oops = setup_soundsystem(d->soundsystem, d->sounddevice, d->ctldevice);
+ if (!oops) {
+ ERRORLOG("cdda: setup_soundsystem failed\n");
+ wmcdda_close(&dev);
+ return -1;
+ }
+
+ if(pthread_create(&thread_read, NULL, cdda_fct_read, &dev)) {
+ ERRORLOG("error by create pthread");
+ oops->wmaudio_close();
+ wmcdda_close(&dev);
+ return -1;
+ }
+
+ if(pthread_create(&thread_play, NULL, cdda_fct_play, &dev)) {
+ ERRORLOG("error by create pthread");
+ oops->wmaudio_close();
+ wmcdda_close(&dev);
+ return -1;
+ }
+ d->cdda_slave = 0;
+ return 0;
+}
+
+int
+cdda_get_drive_status( struct wm_drive *d, int oldmode,
+ int *mode, int *frame, int *track, int *ind )
+{
+ if (d->cdda_slave > -1) {
+ if(dev.status)
+ *mode = dev.status;
+ else
+ *mode = oldmode;
+
+ if (*mode == WM_CDM_PLAYING) {
+ *track = dev.track;
+ *ind = dev.index;
+ *frame = dev.frame;
+ } else if (*mode == WM_CDM_CDDAERROR) {
+ /*
+ * An error near the end of the CD probably
+ * just means we hit the end.
+ */
+ *mode = WM_CDM_TRACK_DONE;
+ }
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+cdda_play( struct wm_drive *d, int start, int end, int realstart )
+{
+ if (d->cdda_slave > -1) {
+ dev.command = WM_CDM_STOPPED;
+
+ wmcdda_setup(start, end, realstart);
+
+ level = 2500;
+ volume = 1 << 15;
+
+ dev.track = -1;
+ dev.index = 0;
+ dev.frame = start;
+ dev.command = WM_CDM_PLAYING;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+cdda_pause( struct wm_drive *d )
+{
+ if (d->cdda_slave > -1) {
+ if(WM_CDM_PLAYING == dev.command) {
+ dev.command = WM_CDM_PAUSED;
+ } else {
+ dev.command = WM_CDM_PLAYING;
+ }
+
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+cdda_stop( struct wm_drive *d )
+{
+ if (d->cdda_slave > -1) {
+ dev.command = WM_CDM_STOPPED;
+ oops->wmaudio_stop();
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+cdda_eject( struct wm_drive *d )
+{
+ if (d->cdda_slave > -1) {
+ dev.command = WM_CDM_EJECTED;
+ oops->wmaudio_stop();
+ /*wmcdda_close(&dev);*/
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+cdda_set_volume( struct wm_drive *d, int left, int right )
+{
+ if (d->cdda_slave > -1) {
+ int bal, vol;
+
+ bal = (right - left) + 100;
+ bal *= 255;
+ bal /= 200;
+ if (right > left)
+ vol = right;
+ else
+ vol = left;
+ vol *= 255;
+ vol /= 100;
+
+ if(oops->wmaudio_balance)
+ oops->wmaudio_balance(bal);
+ if(oops->wmaudio_volume)
+ oops->wmaudio_volume(vol);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+cdda_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ if (d->cdda_slave > -1) {
+ if(!oops->wmaudio_state) {
+ dev.volume = -1;
+ dev.balance = 128;
+ }
+
+ *left = *right = (dev.volume * 100 + 254) / 255;
+
+ if (dev.balance < 110)
+ *right = (((dev.volume * dev.balance + 127) / 128) * 100 + 254) / 255;
+ else if (dev.balance > 146)
+ *left = (((dev.volume * (255 - dev.balance) + 127) / 128) * 100 + 254) / 255;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * Turn off the CDDA slave.
+ */
+void
+cdda_kill( struct wm_drive *d )
+{
+ if (d->cdda_slave > -1) {
+ dev.command = WM_CDM_STOPPED;
+ oops->wmaudio_stop();
+ sleep(1);
+ wmcdda_close(&dev);
+ oops->wmaudio_close();
+
+ dev.blocks = NULL;
+ wait(NULL);
+ d->cdda_slave = -1;
+ }
+}
+
+/*
+ * Tell the CDDA slave to set the loudness level.
+ */
+void
+cdda_set_loudness( struct wm_drive *d, int loud )
+{
+ if (d->cdda_slave > -1) {
+ loudness = loud;
+ }
+}
+
+/*
+ * Tell the CDDA slave to start (or stop) saving to a file.
+ */
+void
+cdda_save( struct wm_drive *d, char *filename )
+{
+ #if 0
+ int len;
+
+ if (filename == NULL || filename[0] == '\0')
+ len = 0;
+ else
+ len = strlen(filename);
+ write(d->cdda_slave, "F", 1);
+ write(d->cdda_slave, &len, sizeof(len));
+ if (len)
+ write(d->cdda_slave, filename, len);
+
+
+ read(0, &namelen, sizeof(namelen));
+ if (output != NULL) {
+ fclose(output);
+ output = NULL;
+ }
+ if (namelen) {
+ filename = malloc(namelen + 1);
+ if (filename == NULL) {
+ perror("cddas");
+ wmcdda_close(dev);
+ oops->wmaudio_close();
+ exit(1);
+ }
+
+ read(0, filename, namelen);
+ filename[namelen] = '\0';
+ output = fopen(filename, "w");
+ if (output == NULL) {
+ perror(filename);
+ } else {
+ /* Write an .au file header. */
+ hdr.magic = htonl(0x2e736e64);
+ hdr.hdr_size = htonl(sizeof(hdr) + 28);
+ hdr.data_size = htonl(~0);
+ hdr.encoding = htonl(3); /* linear-16 */
+ hdr.sample_rate = htonl(44100);
+ hdr.channels = htonl(2);
+
+ fwrite(&hdr, sizeof(hdr), 1, output);
+ fwrite("Recorded from CD by WorkMan", 28, 1, output);
+ }
+ free(filename);
+
+#endif
+}
+
+int get_next_block(int x)
+{
+ int y = ++x;
+ return (y < NUMBLOCKS)?y:0;
+}
+
+void *cdda_fct_read(void* arg)
+{
+ struct cdda_device *cddadev = (struct cdda_device*)arg;
+ int i, j, wakeup;
+ long result;
+
+ while (cddadev->blocks) {
+ while(cddadev->command != WM_CDM_PLAYING) {
+ cddadev->status = cddadev->command;
+ sleep(1);
+ }
+
+ i = 0;
+ pthread_mutex_lock(&blks_mutex[i]);
+ wakeup = 1;
+
+ while(cddadev->command == WM_CDM_PLAYING) {
+
+ result = wmcdda_read(cddadev, &blks[i]);
+
+ if (result <= 0 && blks[i].status != WM_CDM_TRACK_DONE) {
+ ERRORLOG("cdda: wmcdda_read failed, stop playing\n");
+ cddadev->command = WM_CDM_STOPPED;
+ break;
+ } else {
+ if (output)
+ fwrite(blks[i].buf, blks[i].buflen, 1, output);
+ }
+
+ j = get_next_block(i);
+ pthread_mutex_lock(&blks_mutex[j]);
+
+ if(wakeup) {
+ wakeup = 0;
+ pthread_cond_signal(&wakeup_audio);
+ }
+
+ pthread_mutex_unlock(&blks_mutex[i]);
+ /* audio can start here */
+
+ i = j;
+ }
+
+ pthread_mutex_unlock(&blks_mutex[i]);
+ }
+
+ return 0;
+}
+
+void *cdda_fct_play(void* arg)
+{
+ struct cdda_device *cddadev = (struct cdda_device*)arg;
+ int i = 0;
+
+ while (cddadev->blocks) {
+ if(cddadev->command != WM_CDM_PLAYING) {
+ i = 0;
+ pthread_mutex_lock(&blks_mutex[i]);
+ pthread_cond_wait(&wakeup_audio, &blks_mutex[i]);
+ } else {
+ i = get_next_block(i);
+ pthread_mutex_lock(&blks_mutex[i]);
+ }
+
+ if (oops->wmaudio_play(&blks[i])) {
+ oops->wmaudio_stop();
+ ERRORLOG("cdda: wmaudio_play failed\n");
+ cddadev->command = WM_CDM_STOPPED;
+ }
+ cddadev->frame = blks[i].frame;
+ cddadev->track = blks[i].track;
+ cddadev->index = blks[i].index;
+ cddadev->status = blks[i].status;
+
+ pthread_mutex_unlock(&blks_mutex[i]);
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/kscd/libwm/cddaslave.c b/kscd/libwm/cddaslave.c
new file mode 100644
index 00000000..9a008b84
--- /dev/null
+++ b/kscd/libwm/cddaslave.c
@@ -0,0 +1,572 @@
+/*
+ * $id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ ******************************************************************
+ *
+ * Digital audio manipulator for WorkMan.
+ *
+ * The CDDA architecture looks like this:
+ *
+ * WorkMan (or another UI!)
+ * ^^^
+ * ||| (separate processes connected by pipe)
+ * vvv
+ * +------------- cddaslave -------------+
+ * | | |
+ * command module CDDA reader audio output
+ * (portable) (per platform) (per platform)
+ *
+ * This source file has the command module and some of the scaffolding
+ * to hold cddaslave together, plus some non-system-dependent audio
+ * processing code. Look in plat_*_cdda.c for system-specific stuff.
+ *
+ */
+
+#include "include/wm_cdda.h"
+
+#ifdef BUILD_CDDA
+
+static char cddaslave_id[] = "$Id$";
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include "include/wm_struct.h"
+#include "include/wm_cdda.h"
+#include "include/wm_platform.h"
+#include "audio/audio.h"
+
+void send_status(struct cdda_block *);
+
+#define SEND_ACK(b); { (b)->status |= WMCDDA_ACK; send_status(b); }
+#define SEND_STATUS(b); { (b)->status &= ~WMCDDA_ACK; send_status(b); }
+#define SEND_STATUS_ACK(b, s); { (b)->status = ((s) | WMCDDA_ACK); send_status(b); }
+
+int receive_command(struct cdda_device *, struct cdda_block *);
+
+int playing = 0; /* Should the CD be playing now? */
+int pausing = 0;
+
+/*
+ * Loudness setting, plus the floating volume multiplier and decaying-average
+ * volume level.
+ */
+int loudness = 0;
+unsigned int volume = 32768;
+unsigned int level;
+
+/*
+ * Playback speed (0 = slow)
+ */
+int speed = 128;
+
+/*
+ * This is non-null if we're saving audio to a file.
+ */
+FILE *output = NULL;
+
+/*
+ * These are driverdependent oops
+ *
+ */
+struct audio_oops *oops = NULL;
+
+/*
+ * Audio file header format.
+ */
+typedef unsigned long u_32;
+struct auheader {
+ u_32 magic;
+ u_32 hdr_size;
+ u_32 data_size;
+ u_32 encoding;
+ u_32 sample_rate;
+ u_32 channels;
+};
+
+/* had to change #ifdef to #if -> see wm_cdda.h */
+#ifdef __FreeBSD__
+/* Phungus not with htonl on FreeBSD */
+#include <sys/param.h>
+#else
+#if WM_BIG_ENDIAN
+# ifndef htonl
+# define htonl(x) (x)
+# endif
+#else
+extern unsigned long htonl(x);
+#endif
+#endif
+
+void *malloc();
+long cdda_transform();
+
+/*
+ * Send status information upstream.
+ */
+void
+send_status(struct cdda_block *blk)
+{
+ DEBUGLOG("send_status, send %i(%s | %s)\n", blk->status,
+ gen_status(blk->status & WMCDDA_ACK), gen_status(blk->status & ~WMCDDA_ACK));
+ write(1, blk, sizeof(*blk));
+}
+
+/*
+ * Accept a command from our master.
+ *
+ * The protocol is byte-oriented:
+ * PmsfMSFxyz Play from msf to MSF (MSF can be 0,0,0 to play to end)
+ * xyz is the msf of the start of this chunk, i.e., the
+ * ending boundary for reverse play.
+ * S Stop.
+ * E Eject. This means we just close the CD device and
+ * open it again later.
+ * Q Quit.
+ * Vn Set volume level (0-255).
+ * Bn Set balance level (0-255).
+ * EnL Set an equalizer level (n = 0 for bass, 255 for treble)
+ * G Get current status.
+ * sn Set speed multiplier to n.
+ * dn Set direction to forward (n = 0) or reverse.
+ * Fllllx... Start saving to a file (length = l, followed by name)
+ * F0000 Stop saving.
+ * Ln Set loudness level (0-255).
+ * A Pause/Resume
+ * I Get status, current frame
+ */
+int
+receive_command(struct cdda_device *dev, struct cdda_block* blk)
+{
+ unsigned char inbuf[10];
+ char *filename;
+ int namelen;
+ struct auheader hdr;
+
+ if (read(0, inbuf, 1) <= 0) {
+ wmcdda_close(dev);
+ oops->wmaudio_close();
+/* ERRORLOG("cddaslave: parent died, exit\n");*/
+ exit(0);
+ }
+
+ DEBUGLOG("cddaslave: CMD %c\n", inbuf[0]);
+
+ switch (inbuf[0]) {
+ case 'I':
+ if(dev->fd < 0) wmcdda_init(dev, blk);
+ SEND_ACK(blk);
+ break;
+ case 'A': /* pause/resume */
+ if(WMCDDA_PLAYING & blk->status) {
+ oops->wmaudio_stop();
+ SEND_STATUS_ACK(blk, WMCDDA_PAUSED);
+ } else if (WMCDDA_PAUSED & blk->status) {
+ SEND_STATUS_ACK(blk, WMCDDA_PLAYING);
+ } else {
+ SEND_ACK(blk);
+ }
+ break;
+ case 'E':
+ oops->wmaudio_stop();
+ wmcdda_close(dev);
+ SEND_ACK(blk);
+ break;
+ case 'P':
+ read(0, inbuf, 9);
+
+ wmcdda_setup(inbuf[0] * 60 * 75 + inbuf[1] * 75 + inbuf[2],
+ inbuf[3] * 60 * 75 + inbuf[4] * 75 + inbuf[5],
+ inbuf[6] * 60 * 75 + inbuf[7] * 75 + inbuf[8]);
+
+ level = 2500;
+ volume = 1 << 15;
+
+ blk->track = -1;
+ blk->index = 0;
+ blk->minute = inbuf[6];
+ blk->second = inbuf[7];
+ blk->frame = inbuf[8];
+ SEND_STATUS_ACK(blk, WMCDDA_PLAYING);
+ break;
+ case 'S':
+ oops->wmaudio_stop();
+ SEND_STATUS_ACK(blk, WMCDDA_STOPPED);
+ break;
+ case 'B':
+ read(0, inbuf, 1);
+ if(oops->wmaudio_balance)
+ oops->wmaudio_balance(inbuf[0]);
+ break;
+ case 'V':
+ read(0, inbuf, 1);
+ if(oops->wmaudio_balance)
+ oops->wmaudio_volume(inbuf[0]);
+ break;
+ case 'G':
+ SEND_ACK(blk);
+ if(!oops->wmaudio_state || oops->wmaudio_state(blk) == -1) {
+ blk->volume = -1;
+ blk->balance = 128;
+ }
+ send_status(blk);
+ break;
+ case 'Q':
+ SEND_ACK(blk);
+ wmcdda_close(dev);
+ oops->wmaudio_close();
+ exit(0);
+/*
+ case 's':
+ read(0, inbuf, 1);
+ speed = inbuf[0];
+ wmcdda_speed(speed);
+ SEND_STATUS(blk, WMCDDA_ACK);
+ break;
+
+ case 'd':
+ read(0, inbuf, 1);
+ wmcdda_direction(inbuf[0]);
+ SEND_STATUS(blk, WMCDDA_ACK);
+ break;
+*/
+ case 'L':
+ read(0, inbuf, 1);
+ loudness = inbuf[0];
+ SEND_ACK(blk);
+ break;
+ case 'F':
+ read(0, &namelen, sizeof(namelen));
+ if (output != NULL) {
+ fclose(output);
+ output = NULL;
+ }
+ if (namelen) {
+ filename = malloc(namelen + 1);
+ if (filename == NULL) {
+ perror("cddaslave");
+ wmcdda_close(dev);
+ oops->wmaudio_close();
+ exit(1);
+ }
+
+ read(0, filename, namelen);
+ filename[namelen] = '\0';
+ output = fopen(filename, "w");
+ if (output == NULL)
+ perror(filename);
+ else {
+ /* Write an .au file header. */
+ hdr.magic = htonl(0x2e736e64);
+ hdr.hdr_size = htonl(sizeof(hdr) + 28);
+ hdr.data_size = htonl(~0);
+ hdr.encoding = htonl(3); /* linear-16 */
+ hdr.sample_rate = htonl(44100);
+ hdr.channels = htonl(2);
+
+ fwrite(&hdr, sizeof(hdr), 1, output);
+ fwrite("Recorded from CD by WorkMan", 28, 1, output);
+ }
+ free(filename);
+ }
+ SEND_ACK(blk);
+ }
+
+ return(dev->fd);
+}
+
+
+/*
+ * Transform some CDDA data.
+ */
+long
+wmcdda_transform(unsigned char *rawbuf, long buflen, struct cdda_block *block)
+{
+ long i;
+ long *buf32 = (long *)rawbuf;
+ register short *buf16 = (short *)rawbuf;
+ register int aval;
+
+ /*
+ * Loudness transformation. Basically this is a self-adjusting
+ * volume control; our goal is to keep the average output level
+ * around a certain value (2500 seems to be pleasing.) We do this
+ * by maintaining a decaying average of the recent output levels
+ * (where "recent" is some fraction of a second.) All output levels
+ * are multiplied by the inverse of the decaying average; this has
+ * the volume-leveling effect we desire, and isn't too CPU-intensive.
+ *
+ * This is done by modifying the digital data, rather than adjusting
+ * the system volume control, because (at least on some systems)
+ * tweaking the system volume can generate little pops and clicks.
+ *
+ * There's probably a more elegant way to achieve this effect, but
+ * what the heck, I never took a DSP class and am making this up as
+ * I go along, with a little help from some friends.
+ *
+ * This is all done with fixed-point math, oriented around powers
+ * of two, which with luck will keep the CPU usage to a minimum.
+ * More could probably be done, for example using lookup tables to
+ * replace multiplies and divides; whether the memory hit (128K
+ * for each table) is worthwhile is unclear.
+ */
+ if (loudness)
+ {
+ /* We aren't really going backwards, but i > 0 is fast */
+ for (i = buflen / 2; i > 0; i--)
+ {
+ /*
+ * Adjust this sample to the current level.
+ */
+ aval = (*buf16 = (((long)*buf16) * volume) >> 15);
+ buf16++;
+
+ /*
+ * Don't adjust the decaying average for each sample;
+ * that just spends CPU time for very little benefit.
+ */
+ if (i & 127)
+ continue;
+
+ /*
+ * We want to use absolute values to compute the
+ * decaying average; otherwise it'd sit around 0.
+ */
+ if (aval < 0)
+ aval = -aval;
+
+ /*
+ * Adjust more quickly when we start hitting peaks,
+ * or we'll get clipping when there's a sudden loud
+ * section after lots of quiet.
+ */
+ if (aval & ~8191)
+ aval <<= 3;
+
+ /*
+ * Adjust the decaying average.
+ */
+ level = ((level << 11) - level + aval) >> 11;
+
+ /*
+ * Let *really* quiet sounds play softly, or we'll
+ * amplify background hiss to full volume and blast
+ * the user's speakers when real sound starts up.
+ */
+ if (! (level & ~511))
+ level = 512;
+
+ /*
+ * And adjust the volume setting using the inverse
+ * of the decaying average.
+ */
+ volume = (2500 << 15) / level;
+ }
+ }
+
+ if (speed == 128)
+ return (buflen);
+
+ /*
+ * Half-speed play. Stretch things out.
+ */
+ if (speed == 0)
+ {
+ buflen /= 2; /* Since we're moving 32 bits at a time */
+
+ for (i = buflen - 1; i > 0; i--)
+ {
+ buf32[i] = buf32[i / 2];
+ }
+
+ buflen *= 4; /* 2 for doubling the buffer, 2 from above */
+ }
+
+ /*
+ * Slow play; can't optimize it as well as half-speed.
+ */
+ if (speed && speed < 128)
+ {
+ int multiplier = ((speed + 128) * 128) / 256;
+ int newlen;
+ int tally = 0, pos;
+
+ buflen /= 4; /* Get the number of 32-bit values */
+
+ /*
+ * Buffer length doubles when speed is 0, stays the same
+ * when speed is 128.
+ */
+ newlen = (buflen * 128) / multiplier;
+
+ pos = buflen - 1;
+ for (i = newlen - 1; i > 0; i--)
+ {
+ buf32[i] = buf32[pos];
+ tally += multiplier;
+ if (tally & 128)
+ {
+ pos--;
+ tally ^= 128;
+ }
+ }
+
+ buflen = newlen * 4;
+ }
+
+ return (buflen);
+}
+
+
+int main(int argc, char **argv)
+{
+ fd_set readfd, dummyfd;
+ struct timeval timeout;
+ struct cdda_block blockinfo;
+ long result;
+ int nfds;
+ struct cdda_device dev;
+ const char *sondsystem;
+ const char *sounddevice;
+ const char *sounddevicectl;
+
+ memset(&blockinfo, 0, sizeof(struct cdda_block));
+
+ /*
+ * Device name should be the command-line argument.
+ */
+ if (argc < 2)
+ dev.devname = NULL;
+ else
+ dev.devname = argv[1];
+
+ if (argc < 3)
+ sondsystem = "arts";
+ else
+ sondsystem = argv[2];
+
+ if (argc < 4)
+ sounddevice = NULL;
+ else
+ sounddevice = argv[3];
+
+ if (argc < 5)
+ sounddevicectl = NULL;
+ else
+ sounddevicectl = argv[3];
+
+ DEBUGLOG("cddaslave: called with %s %s %s %s\n",
+ dev.devname?dev.devname:"",
+ sondsystem?sondsystem:"",
+ sounddevice?sounddevice:"",
+ sounddevicectl?sounddevicectl:"");
+
+ /*
+ * If we're running setuid root, bump up our priority then lose
+ * superuser access.
+ */
+ nice(-14);
+ setgid(getgid());
+ setuid(getuid());
+ if (geteuid() != getuid())
+ return 255;
+
+ FD_ZERO(&dummyfd);
+ FD_ZERO(&readfd);
+
+ timerclear(&timeout);
+
+ dev.fd = -1;
+ wmcdda_init(&dev, &blockinfo);
+
+ oops = setup_soundsystem(sondsystem, sounddevice, sounddevicectl);
+ if (!oops) {
+ ERRORLOG("cddaslave: setup_soundsystem failed\n");
+ exit(1);
+ }
+
+ DEBUGLOG("cddaslave: sent first ACK\n");
+ SEND_ACK(&blockinfo);
+
+ /*
+ * Accept commands as they come in, and play some sound if we're
+ * supposed to be doing that.
+ */
+ while (1) {
+ FD_SET(0, &readfd);
+
+ /*
+ * If we're playing, we don't want select to block. Otherwise,
+ * wait a little while for the next command.
+ */
+ if (playing)
+ timeout.tv_usec = 0;
+ else
+ timeout.tv_usec = 500000;
+
+ nfds = select(1, &readfd, &dummyfd, &dummyfd, &timeout);
+
+ if (nfds < 0) { /* Broken pipe; our GUI exited. */
+ wmcdda_close(&dev);
+ oops->wmaudio_close();
+ exit(0);
+ }
+
+ if (FD_ISSET(0, &readfd)) {
+ receive_command(&dev, &blockinfo);
+ /*
+ * Process all commands in rapid succession, rather
+ * than possibly waiting for a CDDA read.
+ */
+ continue;
+ }
+
+ if ((blockinfo.status & ~WMCDDA_ACK) == WMCDDA_PLAYING) {
+ result = wmcdda_read(&dev, &blockinfo);
+ if (result <= 0 && blockinfo.status != WMCDDA_DONE) {
+ ERRORLOG("cddaslave: wmcdda_read failed\n");
+ blockinfo.status = WMCDDA_STOPPED;
+ send_status(&blockinfo);
+ } else {
+ result = wmcdda_normalize(&dev, &blockinfo);
+ if (output)
+ fwrite(dev.buf, result, 1, output);
+
+ if (oops->wmaudio_play(dev.buf, dev.buflen, &blockinfo)) {
+ oops->wmaudio_stop();
+ ERRORLOG("cddaslave: wmaudio_play failed\n");
+ blockinfo.status = WMCDDA_STOPPED;
+ send_status(&blockinfo);
+ }
+ }
+ } else
+ send_status(&blockinfo);
+ }
+
+ return 0;
+}
+
+#endif /* BUILD_CDDA */
diff --git a/kscd/libwm/cddb.c b/kscd/libwm/cddb.c
new file mode 100644
index 00000000..657ab6a4
--- /dev/null
+++ b/kscd/libwm/cddb.c
@@ -0,0 +1,588 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * establish connection to cddb server and get data
+ * socket stuff gotten from gnu port of the finger command
+ *
+ * provided by Sven Oliver Moll
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdinfo.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cddb.h"
+
+struct wm_cddb cddb;
+
+extern struct wm_cdinfo thiscd;
+static int cur_cddb_protocol;
+static char *cur_cddb_server;
+static char *cur_cddb_mail_adress;
+static char *cur_cddb_path_to_cgi;
+static char *cur_cddb_proxy_server;
+
+/* local prototypes */
+int cddb_sum(int);
+char *string_split(char *line, char delim);
+void string_makehello(char *line, char delim);
+int connect_open(void);
+void connect_close(void);
+void connect_getline(char *line);
+void connect_read_entry(void);
+void cddbp_send(const char *line);
+void cddbp_read(char *category, unsigned int id);
+void http_send(char* line);
+void http_read(char *category, unsigned int id);
+void cddb_request(void);
+/* local prototypes END */
+
+static int Socket;
+static FILE *Connection;
+
+/*
+ *
+ */
+void
+cddb_cur2struct(void)
+{
+ cddb.protocol = cur_cddb_protocol;
+ strcpy(cddb.cddb_server, cur_cddb_server);
+ strcpy(cddb.mail_adress, cur_cddb_mail_adress);
+ strcpy(cddb.path_to_cgi, cur_cddb_path_to_cgi);
+ strcpy(cddb.proxy_server, cur_cddb_proxy_server);
+} /* cddb_cur2struct() */
+
+/*
+ *
+ */
+void
+cddb_struct2cur(void)
+{
+ cur_cddb_protocol = cddb.protocol;
+ cur_cddb_server = (cddb.cddb_server);
+ cur_cddb_mail_adress = (cddb.mail_adress);
+ cur_cddb_path_to_cgi = (cddb.path_to_cgi);
+ cur_cddb_proxy_server = (cddb.proxy_server);
+} /* cddb_struct2cur() */
+
+
+/*
+ * Subroutine from cddb_discid
+ */
+int
+cddb_sum(int n)
+{
+ char buf[12],
+ *p;
+ int ret = 0;
+
+ /* For backward compatibility this algorithm must not change */
+ sprintf(buf, "%lu", (unsigned long)n);
+ for (p = buf; *p != '\0'; p++)
+ ret += (*p - '0');
+
+ return (ret);
+} /* cddb_sum() */
+
+
+/*
+ * Calculate the discid of a CD according to cddb
+ */
+unsigned long
+cddb_discid(void)
+{
+ int i,
+ t,
+ n = 0;
+
+ /* For backward compatibility this algorithm must not change */
+ for (i = 0; i < thiscd.ntracks; i++) {
+
+ n += cddb_sum(thiscd.trk[i].start / 75);
+ /*
+ * Just for demonstration (See below)
+ *
+ * t += (thiscd.trk[i+1].start / 75) -
+ * (thiscd.trk[i ].start / 75);
+ */
+ }
+
+ /*
+ * Mathematics can be fun. Example: How to reduce a full loop to
+ * a simple statement. The discid algorhythm is so half-hearted
+ * developed that it doesn't even use the full 32bit range.
+ * But it seems to be always this way: The bad standards will be
+ * accepted, the good ones turned down.
+ * Boy, you pulled out the /75. This is not correct here, because
+ * this calculation needs the integer division for both .start
+ * fields.
+ */
+
+ t = (thiscd.trk[thiscd.ntracks].start / 75)
+ - (thiscd.trk[0].start / 75);
+ return ((n % 0xff) << 24 | t << 8 | thiscd.ntracks);
+} /* cddb_discid() */
+
+/*
+ * Split a string into two components according to the first occurrence of
+ * the delimiter.
+ */
+char *
+string_split(char *line, char delim)
+{
+ char *p1;
+
+ for (p1=line;*p1;p1++)
+ {
+ if(*p1 == delim)
+ {
+ *p1 = 0;
+ return ++p1;
+ }
+ }
+ return (NULL);
+} /* string_split() */
+
+/*
+ * Generate the hello string according to the cddb protocol
+ * delimiter is either ' ' (cddbp) or '+' (http)
+ */
+void
+string_makehello(char *line,char delim)
+{
+ char mail[84],*host;
+
+ strcpy(mail,cddb.mail_adress);
+ host=string_split(mail,'@');
+
+ sprintf(line,"%shello%c%s%c%s%c%s%c%s",
+ delim == ' ' ? "cddb " : "&",
+ delim == ' ' ? ' ' : '=',
+ mail,delim,
+ host,delim,
+ WORKMAN_NAME,delim,
+ WORKMAN_VERSION);
+} /* string_makehello() */
+
+/*
+ * Open the TCP connection to the cddb/proxy server
+ */
+int
+connect_open(void)
+{
+ char *host;
+ struct hostent *hp;
+ struct sockaddr_in soc_in;
+ int port;
+
+ if(cddb.protocol == 3) /* http proxy */
+ host = wm_strdup(cddb.proxy_server);
+ else
+ host = wm_strdup(cddb.cddb_server);
+ /*
+ * t=string_split(host,':');
+ */
+ port=atoi(string_split(host,':'));
+ if(!port)
+ port=8880;
+
+#ifndef NDEBUG
+ printf("%s:%d\n",host,port);
+#endif
+ hp =gethostbyname(host);
+
+ if (hp == NULL)
+ {
+ static struct hostent def;
+ static struct in_addr defaddr;
+ static char *alist[1];
+ static char namebuf[128];
+ int inet_addr();
+
+ defaddr.s_addr = inet_addr(host);
+ if ((int) defaddr.s_addr == -1)
+ {
+#ifndef NDEBUG
+ printf("unknown host: %s\n", host);
+#endif
+ return (-1);
+ }
+ strcpy(namebuf, host);
+ def.h_name = namebuf;
+ def.h_addr_list = alist, def.h_addr = (char *)&defaddr;
+ def.h_length = sizeof (struct in_addr);
+ def.h_addrtype = AF_INET;
+ def.h_aliases = 0;
+ hp = &def;
+ }
+ soc_in.sin_family = hp->h_addrtype;
+ bcopy(hp->h_addr, (char *)&soc_in.sin_addr, hp->h_length);
+ soc_in.sin_port = htons(port);
+ Socket = socket(hp->h_addrtype, SOCK_STREAM, 0);
+ if (Socket < 0)
+ {
+ perror("socket");
+ return (-1);
+ }
+ fflush(stdout);
+ if (connect(Socket, (struct sockaddr *)&soc_in, sizeof (soc_in)) < 0)
+ {
+ perror("connect");
+ close(Socket);
+ return (-1);
+ }
+
+ Connection = fdopen(Socket, "r");
+ return (0);
+} /* connect_open() */
+
+
+/*
+ * Close the connection
+ */
+void
+connect_close(void)
+{
+ (void)fclose(Connection);
+ close(Socket);
+} /* connect_close() */
+
+/*
+ * Get a line from the connection with CR and LF stripped
+ */
+void
+connect_getline(char *line)
+{
+ char c;
+
+ while ((c = getc(Connection)) != '\n')
+ {
+ *line = c;
+ if ((c != '\r') && (c != (char)0xff))
+ line++;
+ }
+ *line=0;
+} /* connect_getline() */
+
+/*
+ * Read the CD data from the server and place them into the cd struct
+ */
+void
+connect_read_entry(void)
+{
+ char type;
+ int trknr;
+
+ char *t,*t2,tempbuf[2000];
+
+ while(strcmp(tempbuf,"."))
+ {
+ connect_getline(tempbuf);
+
+ t=string_split(tempbuf,'=');
+ if(t != NULL)
+ {
+ type=tempbuf[0];
+
+ if(strncmp("TITLE",tempbuf+1,5))
+ continue;
+
+ if('D' == type)
+ {
+ /*
+ * Annahme: "Interpret / Titel" ist falsch.
+ * Daher: NULL-String erwarten.
+ */
+ t2=string_split(t,'/');
+ if(t2 == NULL)
+ t2 = t;
+ if(*t2 == ' ')
+ t2++;
+ strncpy(cd->cdname,t2,sizeof(cd->cdname)-1);
+ cd->cdname[sizeof(cd->cdname)-1]='\0';
+
+ for(t2=t;*t2;t2++)
+ {
+ if((*t2 == ' ') && (*(t2+1) == 0))
+ *t2=0;
+ }
+ strncpy(cd->artist,t,sizeof(cd->artist)-1);
+ cd->artist[sizeof(cd->artist)-1]='\0';
+ }
+
+ if('T' == type)
+ {
+ trknr=atoi(tempbuf+6);
+ /*
+ * printf("Track %d:%s\n",trknr,t);
+ */
+ wm_strmcpy(&cd->trk[trknr].songname,t);
+ }
+ /*
+ * fprintf(stderr, "%s %s\n",tempbuf,t);
+ */
+ }
+ }
+} /* connect_read_entry() */
+
+/*
+ * Send a command to the server using cddbp
+ */
+void
+cddbp_send(const char *line)
+{
+ write(Socket, line, strlen(line));
+ write(Socket, "\n", 1);
+} /* cddbp_send() */
+
+/*
+ * Send the "read from cddb" command to the server using cddbp
+ */
+void
+cddbp_read(char *category, unsigned int id)
+{
+ char tempbuf[84];
+ sprintf(tempbuf, "cddb read %s %08x", category, id);
+ cddbp_send(tempbuf);
+} /* cddbp_read() */
+
+/*
+ * Send a command to the server using http
+ */
+void
+http_send(char* line)
+{
+ char tempbuf[2000];
+
+ write(Socket, "GET ", 4);
+#ifndef NDEBUG
+ printf("GET ");
+#endif
+ if(cddb.protocol == 3)
+ {
+ write(Socket, "http://", 7);
+ write(Socket, cddb.cddb_server, strlen(cddb.cddb_server));
+#ifndef NDEBUG
+ printf("http://%s",cddb.cddb_server);
+#endif
+ }
+ write(Socket, cddb.path_to_cgi, strlen(cddb.path_to_cgi));
+ write(Socket, "?cmd=" ,5);
+ write(Socket, line, strlen(line));
+#ifndef NDEBUG
+ printf("%s?cmd=%s",cddb.path_to_cgi,line);
+#endif
+ string_makehello(tempbuf,'+');
+ write(Socket, tempbuf, strlen(tempbuf));
+#ifndef NDEBUG
+ printf("%s",tempbuf);
+#endif
+ write(Socket, "&proto=1 HTTP/1.0\n\n", 19);
+#ifndef NDEBUG
+ printf("&proto=1 HTTP/1.0\n");
+#endif
+ do
+ connect_getline(tempbuf);
+ while(strcmp(tempbuf,""));
+} /* http_send() */
+
+/*
+ * Send the "read from cddb" command to the server using http
+ */
+void
+http_read(char *category, unsigned int id)
+{
+ char tempbuf[84];
+ sprintf(tempbuf, "cddb+read+%s+%08x", category, id);
+ http_send(tempbuf);
+} /* http_read() */
+
+/*
+ * The main routine called from the ui
+ */
+void
+cddb_request(void)
+{
+ int i;
+ char tempbuf[2000];
+ extern int cur_ntracks;
+
+ int status;
+ char category[21];
+ unsigned int id;
+
+ strcpy(cddb.cddb_server,"localhost:888");
+ strcpy(cddb.mail_adress,"svolli@bigfoot.com");
+ /*
+ * cddb.protocol = 1;
+ */
+ wipe_cdinfo();
+
+ switch(cddb.protocol)
+ {
+ case 1: /* cddbp */
+#ifndef NDEBUG
+ printf("USING CDDBP\n");
+ printf("open\n");
+#endif
+ connect_open();
+ connect_getline(tempbuf);
+#ifndef NDEBUG
+ printf("[%s]\n",tempbuf);
+#endif
+ /*
+ * if(atoi(tempbuf) == 201) return;
+ */
+
+ /*
+ * strcpy(tempbuf,"cddb hello svolli bigfoot.com Eierkratzer eins");
+ */
+ string_makehello(tempbuf,' ');
+#ifndef NDEBUG
+ fprintf(stderr, "%s\n", tempbuf);
+#endif
+ cddbp_send(tempbuf);
+ connect_getline(tempbuf);
+#ifndef NDEBUG
+ printf("[%s]\n",tempbuf);
+ printf("query\n");
+#endif
+ sprintf(tempbuf, "cddb query %08x %d",thiscd.cddbid,thiscd.ntracks);
+ for (i = 0; i < cur_ntracks; i++)
+ if (thiscd.trk[i].section < 2)
+ sprintf(tempbuf + strlen(tempbuf), " %d",
+ thiscd.trk[i].start);
+ sprintf(tempbuf + strlen(tempbuf), " %d\n", thiscd.length);
+#ifndef NDEBUG
+ printf(">%s<\n",tempbuf);
+#endif
+ cddbp_send(tempbuf);
+ connect_getline(tempbuf);
+#ifndef NDEBUG
+ printf("[%s]\n",tempbuf);
+#endif
+ status=atoi(tempbuf);
+ /*
+ * fprintf(stderr, "status:%d\n",status);
+ * fprintf(stderr,"category:%s\n",category);
+ * fprintf(stderr,"id:%s\n",id);
+ */
+ if(status == 200) /* Exact match */
+ {
+ sscanf(tempbuf,"%d %20s %08x",&status,category,&id);
+ cddbp_read(category,id);
+ connect_read_entry();
+ }
+
+ if(status == 211) /* Unexact match, multiple possible
+ * Hack: always use first. */
+ {
+ connect_getline(tempbuf);
+ sscanf(tempbuf,"%20s %08x",category,&id);
+ while(strcmp(tempbuf,"."))
+ connect_getline(tempbuf);
+ cddbp_read(category,id);
+ connect_read_entry();
+ }
+
+ cddbp_send("quit");
+ connect_close();
+#ifndef NDEBUG
+ printf("close\n");
+#endif
+ break;
+ case 2: /* http */
+ case 3: /* http proxy */
+#ifndef NDEBUG
+ printf("USING HTTP%s\n",
+ (cddb.protocol == 3) ? " WITH PROXY" : "");
+ printf("query\n");
+#endif
+ sprintf(tempbuf, "cddb+query+%08x+%d",thiscd.cddbid,thiscd.ntracks);
+ for (i = 0; i < cur_ntracks; i++)
+ if (thiscd.trk[i].section < 2)
+ sprintf(tempbuf + strlen(tempbuf), "+%d",
+ thiscd.trk[i].start);
+ sprintf(tempbuf + strlen(tempbuf), "+%d", thiscd.length);
+#ifndef NDEBUG
+ printf(">%s<\n",tempbuf);
+#endif
+ connect_open();
+ http_send(tempbuf);
+ connect_getline(tempbuf);
+#ifndef NDEBUG
+ printf("[%s]\n",tempbuf);
+#endif
+ status=atoi(tempbuf);
+ /*
+ * fprintf(stderr, "status:%d\n",status);
+ * fprintf(stderr, "category:%s\n",category);
+ * fprintf(stderr, "id:%s\n",id);
+ */
+
+ if(status == 200) /* Exact match */
+ {
+ connect_close();
+ connect_open();
+ sscanf(tempbuf,"%d %20s %08x",&status,category,&id);
+ http_read(category,id);
+ connect_read_entry();
+ }
+
+ if(status == 211) /* Unexact match, multiple possible
+ * Hack: always use first. */
+ {
+ connect_getline(tempbuf);
+ sscanf(tempbuf,"%20s %08x",category,&id);
+ while(strcmp(tempbuf,"."))
+ connect_getline(tempbuf);
+ connect_close();
+ connect_open();
+ http_read(category,id);
+ connect_read_entry();
+ }
+ /* moved close above break */
+ connect_close();
+ break;
+ default: /* off */
+ break;
+ }
+} /* cddb_request() */
+
diff --git a/kscd/libwm/cdinfo.c b/kscd/libwm/cdinfo.c
new file mode 100644
index 00000000..b04dec69
--- /dev/null
+++ b/kscd/libwm/cdinfo.c
@@ -0,0 +1,890 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Get information about a CD.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "include/wm_config.h"
+
+#include "include/wm_struct.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_cdinfo.h"
+#include "include/wm_database.h"
+#include "include/wm_helpers.h"
+
+struct wm_play *playlist = NULL;
+struct wm_cdinfo thiscd, *cd = &thiscd;
+
+int cur_track = -1; /* Current track number, starting at 1 */
+int cur_index = 0; /* Current index mark */
+int cur_lasttrack = 999; /* Last track to play in current chunk */
+int cur_firsttrack = 0; /* First track of current chunk */
+int cur_pos_abs; /* Current absolute position in seconds */
+int cur_frame; /* Current frame number */
+int cur_pos_rel; /* Current track-relative position in seconds */
+int cur_tracklen; /* Length in seconds of current track */
+int cur_cdlen; /* Length in seconds of entire CD */
+int cur_ntracks; /* Number of tracks on CD (= tracks + sections) */
+int cur_nsections; /* Number of sections currently defined */
+
+int cur_listno; /* Current index into the play list, if playing */
+char * cur_artist; /* Name of current CD's artist */
+char * cur_cdname; /* Album name */
+char * cur_trackname; /* Take a guess */
+char cur_contd; /* Continued flag */
+char cur_avoid; /* Avoid flag */
+
+int exit_on_eject = 0;
+
+int cur_stopmode = -1;
+int info_modified;
+
+/*
+ * insert_trackinfo()
+ *
+ * Add a new track to the CD info structure. Pass the position of the new
+ * entry in the track list -- 0 will make this the first track, 1 the second,
+ * etc. The new entry will be zeroed out.
+ */
+static void
+insert_trackinfo(int num)
+{
+ struct wm_trackinfo *newtrk;
+
+ /* Easy case: the list is empty */
+ if (cd->trk == NULL) {
+ if ((cd->trk = (struct wm_trackinfo *) calloc(1,
+ sizeof(*newtrk))) == NULL)
+ {
+ perror("insert_trackinfo");
+ exit(1);
+ } else {
+ return;
+ } /* if() else */
+ } /* if() */
+ /* Stick the new entry in cd->trk[]. */
+ if ((newtrk = (struct wm_trackinfo *) malloc(sizeof(*newtrk) *
+ (cur_ntracks + 1))) == NULL)
+ {
+ perror("insert_trackinfo");
+ exit(1);
+ }
+
+ if (num)
+ memcpy(newtrk, cd->trk, sizeof(*newtrk) * num);
+ memset(&newtrk[num], 0, sizeof(*newtrk));
+ if (num < cur_ntracks)
+ memcpy(&newtrk[num + 1], &cd->trk[num], sizeof(*newtrk) *
+ (cur_ntracks - num));
+
+ free(cd->trk);
+ cd->trk = newtrk;
+}
+
+/*
+ * split_trackinfo()
+ *
+ * Split a track in two at a particular position (absolute, in frames). All
+ * internal data structures and variables will be adjusted to the new
+ * numbering scheme. Pass in the track number (>=1) to split, which is also
+ * the index into cd->trk[] of the new entry.
+ *
+ * If pos is within 1 second of the start of another track, the split fails.
+ *
+ * Returns 1 on success, 0 if the track couldn't be inserted.
+ *
+ * Note: updating user interface elements is up to the caller.
+ */
+int
+split_trackinfo( int pos )
+{
+ int i, l, num;
+
+ if (pos < cd->trk[0].start)
+ return (0);
+
+ /* First find the appropriate track. */
+ for (num = 0; num < cur_ntracks; num++)
+ if (cd->trk[num].start - 75 < pos &&
+ cd->trk[num].start + 75 > pos)
+ return (0);
+ else if (cd->trk[num].start > pos)
+ break;
+ if (num == 0)
+ return (0);
+
+ /* Insert the new entry into the track array. */
+ insert_trackinfo(num);
+
+ /* Update the easy variables. */
+ if (cur_track > num)
+ cur_track++;
+ if (cur_firsttrack > num)
+ cur_firsttrack++;
+ if (cur_lasttrack > num)
+ cur_lasttrack++;
+
+ /* Update the user-defined playlists. */
+ if (cd->lists != NULL)
+ for (l = 0; cd->lists[l].name != NULL; l++)
+ if (cd->lists[l].list != NULL)
+ for (i = 0; cd->lists[l].list[i]; i++)
+ if (cd->lists[l].list[i] > num)
+ cd->lists[l].list[i]++;
+
+ /* Update the internal playlist. */
+ if (playlist != NULL)
+ for (i = 0; playlist[i].start; i++)
+ {
+ if (playlist[i].start > num)
+ playlist[i].start++;
+ if (playlist[i].end > num)
+ playlist[i].end++;
+ }
+
+ /* Now adjust the information in cd->trk[]. */
+ cd->trk[num].start = pos;
+ if (num == cur_ntracks)
+ cd->trk[num].length = cur_cdlen - pos / 75;
+ else
+ cd->trk[num].length = (cd->trk[num + 1].start - pos) / 75;
+ cd->trk[num - 1].length -= cd->trk[num].length;
+ if (cur_track == num)
+ cur_tracklen -= cd->trk[num].length;
+ cd->trk[num].track = cd->trk[num - 1].track;
+ cd->trk[num].data = cd->trk[num - 1].data;
+ cd->trk[num].contd = 1;
+ cd->trk[num].volume = cd->trk[num - 1].volume;
+
+ if (cd->trk[num - 1].section == 0)
+ cd->trk[num - 1].section = 1;
+ cd->trk[num].section = cd->trk[num - 1].section + 1;
+
+ cur_ntracks++;
+ cur_nsections++;
+
+ for (i = num + 1; i < cur_ntracks; i++)
+ if (cd->trk[i].track == cd->trk[num].track)
+ cd->trk[i].section++;
+
+ return (1);
+}
+
+/*
+ * remove_trackinfo()
+ *
+ * Remove a track's internal data. This is similar to split_trackinfo()
+ * above, but simpler. A track's initial section can't be removed. Track
+ * numbers start at 0.
+ *
+ * Returns 1 on success, 0 on failure.
+ */
+int
+remove_trackinfo( int num )
+{
+ int i, l;
+
+ if (num < 1 || num >= cur_ntracks || cd->trk[num].section < 2)
+ return (0);
+
+ cd->trk[num - 1].length += cd->trk[num].length;
+
+ for (i = num; i < cur_ntracks - 1; i++)
+ memcpy(&cd->trk[i], &cd->trk[i + 1], sizeof(cd->trk[0]));
+
+ if (cur_track > num)
+ cur_track--;
+ if (cur_firsttrack > num)
+ cur_firsttrack--;
+ if (cur_lasttrack > num)
+ cur_lasttrack--;
+
+ /* Update the user-defined playlists. */
+ if (cd->lists != NULL)
+ for (l = 0; cd->lists[l].name != NULL; l++)
+ if (cd->lists[l].list != NULL)
+ for (i = 0; cd->lists[l].list[i]; i++)
+ if (cd->lists[l].list[i] > num)
+ cd->lists[l].list[i]--;
+
+ /* Update the internal playlist. */
+ if (playlist != NULL)
+ for (i = 0; playlist[i].start; i++)
+ {
+ if (playlist[i].start > num)
+ playlist[i].start--;
+ if (playlist[i].end > num)
+ playlist[i].end--;
+ }
+
+ cur_ntracks--;
+ cur_nsections--;
+
+ /*
+ * Update the section numbers for this track. If this is the only
+ * user-created section in a track, get rid of the section number
+ * in the track's entry.
+ */
+ if (num == cur_ntracks || cd->trk[num - 1].track != cd->trk[num].track)
+ {
+ if (cd->trk[num - 1].section == 1)
+ cd->trk[num - 1].section = 0;
+ }
+ else
+ for (i = num; i < cur_ntracks; i++)
+ if (cd->trk[i].track == cd->trk[num - 1].track)
+ cd->trk[i].section--;
+
+ return (1);
+}
+
+/*
+ * listentry()
+ *
+ * Return a scrolling list entry.
+ */
+char *
+listentry( int num )
+{
+ static char buf[600];
+ const char *name;
+ char tracknum[20];
+ int digits;
+ int sdigits;
+
+ if (num >= 0 && num < cur_ntracks)
+ {
+
+/*
+ if (big_spaces)
+ {
+ digits = 2;
+ sdigits = cur_nsections < 9 ? -1 : -2;
+ }
+ else
+ {
+ digits = cd->trk[num].track < 10 ? 3 : 2;
+ sdigits = cur_nsections < 9 ? -1 : -3;
+ }
+*/
+
+ digits = 2;
+ sdigits = cur_nsections < 9 ? -1 : -2;
+
+ name = cd->trk[num].songname ? cd->trk[num].songname : "";
+
+ if (cur_nsections)
+ {
+ if (cd->trk[num].section > 9)
+ {
+ sprintf(tracknum, "%*d.%d", digits,
+ cd->trk[num].track,
+ cd->trk[num].section);
+ } else {
+ if (cd->trk[num].section)
+ {
+ sprintf(tracknum, "%*d.%*d", digits,
+ cd->trk[num].track, sdigits,
+ cd->trk[num].section);
+ } else {
+ sprintf(tracknum, "%*d%*s", digits,
+ cd->trk[num].track,
+ 2 - sdigits, " ");
+/* 2 - sdigits - big_spaces, " ");*/
+ }
+ }
+ } else {
+ sprintf(tracknum, "%*d", digits, cd->trk[num].track);
+ }
+
+ if (cd->trk[num].data)
+ {
+ sprintf(buf, "%s) %3dMB %s", tracknum,
+ cd->trk[num].length / 1024, name);
+ } else {
+ sprintf(buf, "%s) %02d:%02d %s", tracknum,
+ cd->trk[num].length / 60,
+ cd->trk[num].length % 60, name);
+ }
+
+ return (buf);
+ } else {
+ return (NULL);
+ }
+} /* listentry() */
+
+/*
+ * trackname()
+ *
+ * Return a track's name.
+ */
+const char *
+trackname( int num )
+{
+ if (num >= 0 && num < cur_ntracks)
+ {
+ if (cd->trk[num].songname)
+ {
+ return (cd->trk[num].songname);
+ } else {
+ return ("");
+ }
+ } else {
+ return (NULL);
+ }
+} /* trackname() */
+
+/*
+ * tracklen()
+ *
+ * Return a track's length in seconds.
+ */
+int
+tracklen( int num )
+{
+ if (cd != NULL && num >= 0 && num < cur_ntracks)
+ return (cd->trk[num].length);
+ else
+ return (0);
+}
+
+/*
+ * get_default_volume()
+ *
+ * Return the default volume (0-32, 0=none) for the CD or a track.
+ */
+int
+get_default_volume( int track )
+{
+ if (! track)
+ return (cd->volume);
+ else if (track <= cur_ntracks)
+ return (cd->trk[track - 1].volume);
+ else
+ return (0);
+}
+
+/*
+ * get_contd()
+ *
+ * Return the contd value for a track.
+ */
+int
+get_contd( int num )
+{
+ if (num >= 0 && num < cur_ntracks)
+ return (cd->trk[num].contd);
+ else
+ return (0);
+}
+
+/*
+ * get_avoid()
+ *
+ * Return the avoid value for a track.
+ */
+int
+get_avoid( int num )
+{
+ if (num >= 0 && num < cur_ntracks)
+ return (cd->trk[num].avoid);
+ else
+ return (0);
+}
+
+/*
+ * get_autoplay()
+ *
+ * Is autoplay set on this disc?
+ */
+int
+get_autoplay( void )
+{
+ return ( cd->autoplay );
+}
+
+/*
+ * get_playmode()
+ *
+ * Return the default playmode for the CD.
+ */
+int
+get_playmode( void )
+{
+ return ( cd->playmode );
+}
+
+/*
+ * get_runtime()
+ *
+ * Return the total running time for the current playlist in seconds.
+ */
+int
+get_runtime( void )
+{
+ int i;
+
+ if (playlist == NULL || playlist[0].start == 0 || cur_firsttrack == -1)
+ return (cd == NULL ? 0 : cd->length);
+
+ for (i = 0; playlist[i].start; i++)
+ ;
+
+ return (playlist[i].starttime);
+}
+
+/*
+ * default_volume()
+ *
+ * Set the default volume for the CD or a track.
+ */
+void
+default_volume( int track, int vol )
+{
+ if (track == 0)
+ cd->volume = vol;
+ else if (track <= cur_ntracks)
+ cd->trk[track - 1].volume = vol;
+}
+
+/*
+ * Play the next thing on the playlist, if any.
+ */
+void
+play_next_entry( int forward )
+{
+ if (cd == NULL)
+ return;
+ if (playlist != NULL && playlist[cur_listno].start)
+ {
+ wm_cd_play(playlist[cur_listno].start, 0,
+ playlist[cur_listno].end);
+ cur_listno++;
+ }
+ else
+ wm_cd_stop();
+}
+
+/*
+ * Play the next track, following playlists as necessary.
+ */
+void
+play_next_track( int forward )
+{
+ if (cd == NULL)
+ return;
+
+ /* Is the current playlist entry done? Move on, if so. */
+ if (playlist == NULL || cur_track + 1 == playlist[cur_listno - 1].end)
+ play_next_entry( forward );
+ else
+ wm_cd_play(cur_track + 1, 0, playlist[cur_listno - 1].end);
+}
+
+/*
+ * Play the previous track, hopping around the playlist as necessary.
+ */
+void
+play_prev_track( int forward )
+{
+ if (cd == NULL)
+ return;
+
+ if (playlist == NULL)
+ return;
+
+ /* If we're in the middle of this playlist entry, go back one track */
+ if (cur_track > playlist[cur_listno - 1].start)
+ wm_cd_play(cur_track - 1, 0, playlist[cur_listno - 1].end);
+ else
+ if (cur_listno > 1)
+ {
+ cur_listno--;
+ wm_cd_play(playlist[cur_listno - 1].end - 1, 0,
+ playlist[cur_listno - 1].end);
+ }
+ else
+ wm_cd_play(playlist[0].start, 0, playlist[0].end);
+}
+
+/*
+ * stash_cdinfo(artist, cdname)
+ */
+void
+stash_cdinfo(char *artist, char *cdname, int autoplay, int playmode )
+{
+ if (cd != NULL)
+ {
+ if (strcmp(cd->artist, artist))
+ info_modified = 1;
+ strncpy(cd->artist, artist,sizeof(cd->artist)-1);
+ cd->artist[sizeof(cd->artist)-1]='\0';
+
+ if (strcmp(cd->cdname, cdname))
+ info_modified = 1;
+ strncpy(cd->cdname, cdname,sizeof(cd->cdname)-1);
+ cd->cdname[sizeof(cd->cdname)-1]='\0';
+
+ if (!!cd->autoplay != !!autoplay)
+ info_modified = 1;
+ cd->autoplay = autoplay;
+
+ if (!!cd->playmode != !!playmode)
+ info_modified = 1;
+ cd->playmode = playmode;
+ }
+} /* stash_cdinfo() */
+
+/*
+ * wipe_cdinfo()
+ *
+ * Clear out all a CD's soft information (presumably in preparation for
+ * reloading from the database.)
+ */
+void
+wipe_cdinfo( void )
+{
+ struct wm_playlist *l;
+ int i;
+
+ if (cd != NULL)
+ {
+ cd->artist[0] = cd->cdname[0] = '\0';
+ cd->autoplay = cd->playmode = cd->volume = 0;
+ cd->whichdb = NULL;
+ freeup(&cd->otherrc);
+ freeup(&cd->otherdb);
+
+ if (thiscd.lists != NULL)
+ {
+ for (l = thiscd.lists; l->name != NULL; l++)
+ {
+ free(l->name);
+ free(l->list);
+ }
+ free(thiscd.lists);
+ thiscd.lists = NULL;
+ }
+
+ for (i = 0; i < cur_ntracks; i++)
+ {
+ freeup(&cd->trk[i].songname);
+ freeup(&cd->trk[i].otherrc);
+ freeup(&cd->trk[i].otherdb);
+ cd->trk[i].avoid = cd->trk[i].contd = 0;
+ cd->trk[i].volume = 0;
+ if (cd->trk[i].section > 1)
+ remove_trackinfo(i--);
+ }
+ }
+}
+
+/*
+ * stash_trkinfo(track, songname, contd, avoid)
+ *
+ * Update information about a track on the current CD.
+ */
+void
+stash_trkinfo( int track, char *songname, int contd, int avoid )
+{
+ if (cd != NULL)
+ {
+ track--;
+ if (!!cd->trk[track].contd != !!contd)
+ info_modified = 1;
+ cd->trk[track].contd = track ? contd : 0;
+
+ if (!!cd->trk[track].avoid != !!avoid)
+ info_modified = 1;
+ cd->trk[track].avoid = avoid;
+
+ if ((cd->trk[track].songname == NULL && songname[0]) ||
+ (cd->trk[track].songname != NULL &&
+ strcmp(cd->trk[track].songname, songname)))
+ {
+ info_modified = 1;
+ wm_strmcpy(&cd->trk[track].songname, songname);
+ }
+ }
+}
+
+/*
+ * new_playlist()
+ *
+ * Add a playlist to a CD.
+ */
+struct wm_playlist *
+new_playlist(struct wm_cdinfo* cdinfo, char* listname)
+{
+ int nlists = 0;
+ struct wm_playlist *l;
+
+ if (cdinfo->lists != NULL)
+ {
+ for (nlists = 0; cdinfo->lists[nlists].name != NULL; nlists++)
+ ;
+ l = (struct wm_playlist *)realloc(cdinfo->lists, (nlists + 2) *
+ sizeof (struct wm_playlist));
+ }
+ else
+ l = (struct wm_playlist *)malloc(2 * sizeof (struct wm_playlist));
+
+ if (l == NULL)
+ return (NULL);
+
+ l[nlists + 1].name = NULL;
+ l[nlists].name = NULL; /* so wm_strmcpy doesn't free() it */
+ wm_strmcpy(&l[nlists].name, listname);
+ l[nlists].list = NULL;
+ cdinfo->lists = l;
+
+ return (&l[nlists]);
+}
+
+/*
+ * make_playlist()
+ *
+ * Construct a playlist for the current CD. If we're in shuffle mode, play
+ * each non-avoided track once, keeping continued tracks in the right order.
+ *
+ * If playmode is 2, use playlist number (playmode-2). XXX should do
+ * bounds checking on this, probably.
+ *
+ * If consecutive tracks are being played, only make one playlist entry for
+ * them, so the CD player won't pause between tracks while we wake up.
+ */
+void
+make_playlist( int playmode, int starttrack )
+{
+ int i, avoiding = 1, entry = 0, count, track,
+ *thislist;
+
+ cur_listno = 0;
+ if (playlist != NULL)
+ free(playlist);
+ playlist = malloc(sizeof (*playlist) * (cur_ntracks + 1));
+ if (playlist == NULL)
+ {
+ perror("playlist");
+ exit(1);
+ }
+
+ /* If this is a data-only CD, we can't play it. */
+ if ((starttrack && cd->trk[starttrack - 1].data) ||
+ (cur_ntracks == 1 && cd->trk[0].data))
+ {
+ playlist[0].start = 0;
+ playlist[0].end = 0;
+ playlist[1].start = 0;
+ return;
+ }
+
+ if (playmode == 1)
+ {
+ char *done = malloc(cur_ntracks);
+
+ if (done == NULL)
+ {
+ perror("randomizer");
+ exit(1);
+ }
+
+ count = cur_ntracks;
+ if (starttrack && cd->trk[starttrack - 1].avoid)
+ count++;
+ for (i = 0; i < cur_ntracks; i++)
+ if (cd->trk[i].contd || cd->trk[i].avoid ||
+ cd->trk[i].data)
+ {
+ done[i] = 1;
+ count--;
+ }
+ else
+ done[i] = 0;
+
+ for (i = 0; i < count; i++)
+ {
+ int end; /* for readability */
+ if (starttrack)
+ {
+ track = starttrack - 1;
+ starttrack = 0;
+ }
+ else
+ while (done[track = rand() % cur_ntracks])
+ ;
+
+ playlist[i].start = track + 1;
+
+ /* play all subsequent continuation tracks too */
+ for (end = track + 1; end < cur_ntracks + 1; end++)
+ if (! cd->trk[end].contd ||
+ cd->trk[end].avoid ||
+ cd->trk[end].data)
+ break;
+ playlist[i].end = end + 1;
+
+ done[track]++;
+ }
+ playlist[i].start = 0;
+
+ free(done);
+ }
+ else if (playmode >= 2 && cd->lists && cd->lists[playmode - 2].name)
+ {
+ count = 2; /* one terminating entry, and one for start */
+ thislist = cd->lists[playmode - 2].list;
+
+ for (i = 0; thislist[i]; i++)
+ if (thislist[i + 1] != thislist[i] + 1)
+ count++;
+
+ if (playlist != NULL)
+ free(playlist);
+ playlist = malloc(sizeof (*playlist) * count);
+ if (playlist == NULL)
+ {
+ perror("playlist");
+ exit(1);
+ }
+
+ count = 0;
+ if (starttrack)
+ {
+ playlist[0].start = starttrack;
+ for (track = 0; thislist[track]; track++)
+ if (starttrack == thislist[track])
+ break;
+ if (! thislist[track])
+ {
+ playlist[0].end = starttrack + 1;
+ playlist[1].start = thislist[0];
+ count = 1;
+ track = 0;
+ }
+ }
+ else
+ {
+ playlist[0].start = thislist[0];
+ track = 0;
+ }
+
+ for (i = track; thislist[i]; i++)
+ if (thislist[i + 1] != thislist[i] + 1)
+ {
+ playlist[count].end = thislist[i] + 1;
+ count++;
+ playlist[count].start = thislist[i + 1];
+ }
+ }
+ else
+ {
+ for (i = starttrack ? starttrack - 1 : 0; i < cur_ntracks; i++)
+ if (avoiding && ! (cd->trk[i].avoid || cd->trk[i].data))
+ {
+ playlist[entry].start = i + 1;
+ avoiding = 0;
+ }
+ else if (! avoiding && (cd->trk[i].avoid ||
+ cd->trk[i].data))
+ {
+ playlist[entry].end = i + 1;
+ avoiding = 1;
+ entry++;
+ }
+ if (! avoiding)
+ playlist[entry].end = i + 1;
+ playlist[entry + !avoiding].start = 0;
+ }
+
+ /*
+ * Now go through the list, whatever its source, and figure out
+ * cumulative starting times for each entry.
+ */
+ entry = count = 0;
+ do {
+ playlist[entry].starttime = count;
+
+ if (playlist[entry].start)
+ for (i = playlist[entry].start; i <
+ playlist[entry].end; i++)
+ count += cd->trk[i - 1].length;
+ } while (playlist[entry++].start);
+}
+
+/*
+ * Find a particular track's location in the current playlist. Sets the
+ * appropriate variables (cur_listno, cur_firsttrack, cur_lasttrack).
+ */
+void
+pl_find_track( int track )
+{
+ int i;
+
+ if (playlist == NULL)
+ {
+#ifndef NDEBUG
+ fprintf(stderr, "Null playlist! Huh?\n");
+#endif
+ return;
+ }
+
+ for (i = 0; playlist[i].start; i++)
+ if (track >= playlist[i].start && track < playlist[i].end)
+ {
+ cur_listno = i + 1;
+ cur_firsttrack = playlist[i].start;
+ cur_lasttrack = playlist[i].end - 1;
+ return;
+ }
+
+ /*
+ * Couldn't find the track in question. Make a special entry with
+ * just that track.
+ */
+ if (! playlist[i].start)
+ {
+ playlist = realloc(playlist, (i + 2) * sizeof(*playlist));
+ if (playlist == NULL)
+ {
+ perror("playlist realloc");
+ exit(1);
+ }
+
+ playlist[i + 1].start = playlist[i + 1].end = 0;
+ playlist[i + 1].starttime = playlist[i].starttime +
+ cd->trk[track - 1].length;
+ playlist[i].start = track;
+ playlist[i].end = track + 1;
+ cur_listno = i + 1;
+ cur_firsttrack = track;
+ cur_lasttrack = track;
+ }
+}
diff --git a/kscd/libwm/cdrom.c b/kscd/libwm/cdrom.c
new file mode 100644
index 00000000..980ce3ce
--- /dev/null
+++ b/kscd/libwm/cdrom.c
@@ -0,0 +1,899 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Interface between most of WorkMan and the low-level CD-ROM library
+ * routines defined in plat_*.c and drv_*.c. The goal is to have no
+ * platform- or drive-dependent code here.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+/* #include <sys/time.h> */
+
+#include "config.h"
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cddb.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_database.h"
+#include "include/wm_platform.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdinfo.h"
+#include "include/wm_cdtext.h"
+
+#ifdef CAN_CLOSE
+#include <fcntl.h>
+#endif
+
+/* local prototypes */
+int read_toc(void);
+
+#define WM_MSG_CLASS WM_MSG_CLASS_CDROM
+
+/* extern struct wm_drive generic_proto, toshiba_proto, sony_proto; */
+/* toshiba33_proto; <=== Somehow, this got lost */
+
+/*
+ * The supported drive types are listed here. NULL means match anything.
+ * The first match in the list is used, and substring matches are done (so
+ * put long names before their shorter prefixes.)
+ */
+struct drivelist {
+ const char *ven;
+ const char *mod;
+ const char *rev;
+ struct wm_drive_proto *proto;
+} drives[] = {
+{ "TOSHIBA", "XM-3501", NULL, &toshiba_proto },
+{ "TOSHIBA", "XM-3401", NULL, &toshiba_proto },
+{ "TOSHIBA", "XM-3301", NULL, &toshiba_proto },
+{ "SONY", "CDU-8012", NULL, &sony_proto },
+{ "SONY", "CDU 561", NULL, &sony_proto },
+{ "SONY", "CDU-76S", NULL, &sony_proto },
+{ WM_STR_GENVENDOR, WM_STR_GENMODEL, WM_STR_GENREV, &generic_proto },
+{ NULL, NULL, NULL, &generic_proto }
+};
+
+/*
+ * Solaris 2.2 will remove the device out from under us. Getting an ENOENT
+ * is therefore sometimes not a problem.
+ */
+int intermittent_dev = 0;
+
+static int wm_cd_cur_balance = 10;
+static int wm_cur_cdmode = WM_CDM_UNKNOWN;
+static char *wm_cd_device = NULL; /* do not use this extern */
+
+static struct wm_drive drive = {
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ -1,
+ -1,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ NULL
+};
+
+extern struct wm_cdinfo thiscd;
+
+/*
+ * Macro magic
+ *
+ */
+#define FREE(x); if(x) free(x); x = NULL;
+
+#define STRDUP(old, neu); \
+ if(old) free(old); old = NULL;\
+ if(neu) old = strdup(neu);
+
+#define CARRAY(id) ((id)-1)
+
+#define DATATRACK 1
+/*
+ * init the workmanlib
+ */
+int wm_cd_init( int cdin, const char *cd_device, const char *soundsystem,
+ const char *sounddevice, const char *ctldevice )
+{
+ drive.cdda = (WM_CDDA == cdin);
+#if !defined(BUILD_CDDA)
+ if(drive.cdda) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "Libworkman library was compiled without cdda support\n");
+ return -1;
+ }
+#endif
+ wm_cd_destroy();
+
+ STRDUP(wm_cd_device, cd_device);
+ drive.cd_device = wm_cd_device;
+ STRDUP(drive.soundsystem, soundsystem);
+ STRDUP(drive.sounddevice, sounddevice);
+ STRDUP(drive.ctldevice, ctldevice);
+
+ return wm_cd_status();
+}
+
+int wm_cd_destroy( void )
+{
+ free_cdtext();
+
+ if(drive.fd != -1) {
+ /* first free one old */
+ if(drive.proto && drive.proto->gen_close)
+ drive.proto->gen_close(&drive);
+ else
+ close(drive.fd);
+ }
+ drive.fd = -1;
+ FREE(wm_cd_device);
+ FREE(drive.soundsystem);
+ FREE(drive.sounddevice);
+ FREE(drive.ctldevice);
+ FREE(drive.vendor);
+ FREE(drive.model);
+ FREE(drive.revision);
+ drive.proto = NULL;
+
+ return 0;
+}
+/*
+ * Give information about the drive we found during wmcd_open()
+ */
+const char *wm_drive_vendor( void )
+{
+ return drive.vendor?drive.vendor:"";
+}
+
+const char *wm_drive_model( void )
+{
+ return drive.model?drive.model:"";
+}
+
+const char *wm_drive_revision( void )
+{
+ return drive.revision?drive.revision:"";
+}
+
+const char *wm_drive_device( void )
+{
+ return drive.cd_device ? drive.cd_device : "";
+}
+
+/*
+ * Figure out which prototype drive structure we should be using based
+ * on the vendor, model, and revision of the current drive.
+ */
+int
+find_drive_struct(const char *vendor, const char *model, const char *rev)
+{
+ struct drivelist *d;
+
+ for (d = drives; d; d++) {
+ if(((d->ven != NULL) && strncmp(d->ven, vendor, strlen(d->ven))) ||
+ ((d->mod != NULL) && strncmp(d->mod, model, strlen(d->mod))) ||
+ ((d->rev != NULL) && strncmp(d->rev, rev, strlen(d->rev))))
+ continue;
+
+ if(!(d->proto))
+ goto fail;
+
+ STRDUP(drive.vendor, vendor);
+ STRDUP(drive.model, model);
+ STRDUP(drive.revision, rev);
+
+ drive.proto = d->proto;
+ return 0;
+ }
+
+fail:
+ return -1;
+} /* find_drive_struct() */
+
+/*
+ * read_toc()
+ *
+ * Read the table of contents from the CD. Return a pointer to a wm_cdinfo
+ * struct containing the relevant information (minus artist/cdname/etc.)
+ * This is a static struct. Returns NULL if there was an error.
+ *
+ * XXX allocates one trackinfo too many.
+ */
+int
+read_toc( void )
+{
+ struct wm_playlist *l;
+ int i;
+ int pos;
+
+ if(!drive.proto)
+ return -1;
+
+ if(drive.proto && drive.proto->gen_get_trackcount &&
+ (drive.proto->gen_get_trackcount)(&drive, &thiscd.ntracks) < 0) {
+ return -1 ;
+ }
+
+ thiscd.artist[0] = thiscd.cdname[0] = '\0';
+ thiscd.whichdb = thiscd.otherrc = thiscd.otherdb = thiscd.user = NULL;
+ thiscd.length = 0;
+ thiscd.autoplay = thiscd.playmode = thiscd.volume = 0;
+
+ /* Free up any left-over playlists. */
+ if (thiscd.lists != NULL) {
+ for (l = thiscd.lists; l->name != NULL; l++) {
+ free(l->name);
+ free(l->list);
+ }
+ free(thiscd.lists);
+ thiscd.lists = NULL;
+ }
+
+ if (thiscd.trk != NULL)
+ free(thiscd.trk);
+
+ thiscd.trk = malloc((thiscd.ntracks + 1) * sizeof(struct wm_trackinfo));
+ if (thiscd.trk == NULL) {
+ perror("malloc");
+ return -1;
+ }
+
+ for (i = 0; i < thiscd.ntracks; i++) {
+ if(drive.proto && drive.proto->gen_get_trackinfo &&
+ (drive.proto->gen_get_trackinfo)(&drive, i + 1, &thiscd.trk[i].data,
+ &thiscd.trk[i].start) < 0) {
+ return -1;
+ }
+
+ thiscd.trk[i].avoid = thiscd.trk[i].data;
+ thiscd.trk[i].length = thiscd.trk[i].start / 75;
+
+ thiscd.trk[i].songname = thiscd.trk[i].otherrc =
+ thiscd.trk[i].otherdb = NULL;
+ thiscd.trk[i].contd = 0;
+ thiscd.trk[i].volume = 0;
+ thiscd.trk[i].track = i + 1;
+ thiscd.trk[i].section = 0;
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "track %i, start frame %i\n",
+ thiscd.trk[i].track, thiscd.trk[i].start);
+ }
+
+ if(drive.proto && drive.proto->gen_get_cdlen &&
+ (drive.proto->gen_get_cdlen)(&drive, &thiscd.trk[i].start) < 0) {
+ return -1;
+ }
+ thiscd.trk[i].length = thiscd.trk[i].start / 75;
+
+/* Now compute actual track lengths. */
+ pos = thiscd.trk[0].length;
+ for (i = 0; i < thiscd.ntracks; i++) {
+ thiscd.trk[i].length = thiscd.trk[i+1].length - pos;
+ pos = thiscd.trk[i+1].length;
+ if (thiscd.trk[i].data)
+ thiscd.trk[i].length = (thiscd.trk[i + 1].start - thiscd.trk[i].start) * 2;
+ if (thiscd.trk[i].avoid)
+ wm_strmcpy(&thiscd.trk[i].songname, "DATA TRACK");
+ }
+
+ thiscd.length = thiscd.trk[thiscd.ntracks].length;
+ thiscd.cddbid = cddb_discid();
+
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "read_toc() successful\n");
+ return 0;
+} /* read_toc() */
+
+/*
+ * wm_cd_status()
+ *
+ * Return values:
+ * see wm_cdrom.h
+ *
+ * Updates variables.
+ */
+int
+wm_cd_status( void )
+{
+ static int oldmode = WM_CDM_UNKNOWN;
+ int mode, err, tmp;
+
+ if(!drive.proto) {
+ oldmode = WM_CDM_UNKNOWN;
+ err = wmcd_open( &drive );
+ if (err < 0) {
+ wm_cur_cdmode = WM_CDM_UNKNOWN;
+ return err;
+ }
+ }
+
+ if(drive.proto && drive.proto->gen_get_drive_status &&
+ (drive.proto->gen_get_drive_status)(&drive, oldmode, &mode, &cur_frame,
+ &(thiscd.curtrack), &cur_index) < 0) {
+ perror("WM gen_get_drive_status");
+ return -1;
+ } else {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS,
+ "gen_get_drive_status returns status %s, track %i, frame %i\n",
+ gen_status(mode), thiscd.curtrack, cur_frame);
+ }
+
+ if(WM_CDS_NO_DISC(oldmode) && WM_CDS_DISC_READY(mode)) {
+ /* device changed */
+ thiscd.ntracks = 0;
+ if(read_toc() || 0 == thiscd.ntracks)
+ {
+ close(drive.fd);
+ drive.fd = -1;
+ mode = WM_CDM_NO_DISC;
+ }
+ else /* refresh cdtext info */
+ get_glob_cdtext(&drive, 1);
+
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "device status changed() from %s to %s\n",
+ gen_status(oldmode), gen_status(mode));
+ }
+ oldmode = mode;
+
+ /*
+ * it seems all driver have'nt state for stop
+ */
+ if(WM_CDM_PAUSED == mode && 0 == cur_frame) {
+ mode = WM_CDM_STOPPED;
+ thiscd.curtrack = 0;
+ }
+
+ switch (mode) {
+ case WM_CDM_PLAYING:
+ case WM_CDM_PAUSED:
+ cur_pos_abs = cur_frame / 75;
+ /* search for right track */
+ for(tmp = thiscd.ntracks;
+ tmp > 1 && cur_frame < thiscd.trk[CARRAY(tmp)].start;
+ tmp--)
+ ;
+ thiscd.curtrack = tmp;
+ /* Fall through */
+
+
+ case WM_CDM_UNKNOWN:
+ if (mode == WM_CDM_UNKNOWN)
+ {
+ mode = WM_CDM_NO_DISC;
+ cur_lasttrack = cur_firsttrack = -1;
+ }
+ /* Fall through */
+
+ case WM_CDM_STOPPED:
+ if(thiscd.curtrack >= 1 && thiscd.curtrack <= thiscd.ntracks && thiscd.trk != NULL) {
+ cur_trackname = thiscd.trk[CARRAY(thiscd.curtrack)].songname;
+ cur_avoid = thiscd.trk[CARRAY(thiscd.curtrack)].avoid;
+ cur_contd = thiscd.trk[CARRAY(thiscd.curtrack)].contd;
+ cur_pos_rel = (cur_frame - thiscd.trk[CARRAY(thiscd.curtrack)].start) / 75;
+ if (cur_pos_rel < 0)
+ cur_pos_rel = -cur_pos_rel;
+ }
+ if((playlist != NULL) && playlist[0].start & (cur_listno > 0)) {
+ cur_pos_abs -= thiscd.trk[playlist[CARRAY(cur_listno)].start - 1].start / 75;
+ cur_pos_abs += playlist[CARRAY(cur_listno)].starttime;
+ }
+ if (cur_pos_abs < 0)
+ cur_pos_abs = cur_frame = 0;
+
+ if (thiscd.curtrack < 1)
+ thiscd.curtracklen = thiscd.length;
+ else
+ thiscd.curtracklen = thiscd.trk[CARRAY(thiscd.curtrack)].length;
+ /* Fall through */
+
+ case WM_CDM_TRACK_DONE:
+ wm_cur_cdmode = mode;
+ break;
+ case WM_CDM_FORWARD:
+ case WM_CDM_EJECTED:
+ wm_cur_cdmode = mode;
+ break;
+ }
+
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS,
+ "wm_cd_status returns %s\n", gen_status(wm_cur_cdmode));
+ return wm_cur_cdmode;
+}
+
+int
+wm_cd_getcurtrack( void )
+{
+ if(WM_CDS_NO_DISC(wm_cur_cdmode))
+ return 0;
+ return thiscd.curtrack;
+}
+
+int
+wm_cd_getcurtracklen( void )
+{
+ if(WM_CDS_NO_DISC(wm_cur_cdmode))
+ return 0;
+
+ return thiscd.curtracklen;
+}
+
+int
+wm_cd_getcountoftracks( void )
+{
+ if(WM_CDS_NO_DISC(wm_cur_cdmode))
+ return 0;
+
+ return thiscd.ntracks;
+}
+
+int wm_cd_gettracklen( int track )
+{
+ if (track < 1 ||
+ track > thiscd.ntracks ||
+ thiscd.trk == NULL)
+ return 0;
+
+ return thiscd.trk[CARRAY(track)].length;
+}
+
+/*
+ * wm_cd_play(starttrack, pos, endtrack)
+ *
+ * Start playing the CD or jump to a new position. "pos" is in seconds,
+ * relative to start of track.
+ */
+int
+wm_cd_play( int start, int pos, int end )
+{
+ int real_start, real_end, status;
+
+ status = wm_cd_status();
+ if(WM_CDS_NO_DISC(status) || thiscd.ntracks < 1)
+ return -1;
+
+ /*
+ * check ranges
+ */
+ for(real_end = thiscd.ntracks; (thiscd.trk[CARRAY(real_end)].data == DATATRACK); real_end--)
+ ;
+ for(real_start = 1; (thiscd.trk[CARRAY(real_start)].data == DATATRACK); real_start++)
+ ;
+
+ if(end == WM_ENDTRACK) end = real_end;
+ if(end > real_end) end = real_end;
+
+ /*
+ * handle as overrun
+ */
+ if(start < real_start) start = real_start;
+ if(start > real_end) start = real_end;
+
+ /*
+ * Try to avoid mixed mode and CD-EXTRA data tracks
+ */
+ if(start > end || thiscd.trk[CARRAY(start)].data == DATATRACK) {
+ wm_cd_stop();
+ return -1;
+ }
+
+ cur_firsttrack = start;
+ cur_lasttrack = end;
+
+ wm_cd_play_chunk(thiscd.trk[CARRAY(start)].start + pos * 75, end == thiscd.ntracks ?
+ thiscd.length * 75 : thiscd.trk[CARRAY(end)].start - 1, thiscd.trk[CARRAY(start)].start);
+ /* So we don't update the display with the old frame number */
+ wm_cd_status();
+
+ return thiscd.curtrack;
+}
+
+/*
+ * wm_cd_play_chunk(start, end)
+ *
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+wm_cd_play_chunk( int start, int end, int realstart )
+{
+ int status;
+
+ status = wm_cd_status();
+ if(WM_CDS_NO_DISC(status))
+ return -1;
+
+ end--;
+ if (start >= end)
+ start = end-1;
+
+ if(!(drive.proto) || !(drive.proto->gen_play)) {
+ perror("WM gen_play: function pointer NULL");
+ return -1;
+ }
+
+ return (drive.proto->gen_play)(&drive, start, end, realstart);
+}
+
+/*
+ * Set the offset into the current track and play. -1 means end of track
+ * (i.e., go to next track.)
+ */
+int
+wm_cd_play_from_pos( int pos )
+{
+ int status;
+
+ status = wm_cd_status();
+ if(WM_CDS_NO_DISC(status))
+ return -1;
+
+ if (pos == -1)
+ pos = thiscd.trk[thiscd.curtrack - 1].length - 1;
+
+ if (wm_cur_cdmode == WM_CDM_PLAYING)
+ return wm_cd_play(thiscd.curtrack, pos, playlist[cur_listno-1].end);
+ else
+ return -1;
+} /* wm_cd_play_from_pos() */
+
+/*
+ * wm_cd_pause()
+ *
+ * Pause the CD, if it's in play mode. If it's already paused, go back to
+ * play mode.
+ */
+int
+wm_cd_pause( void )
+{
+ static int paused_pos;
+ int status;
+
+ status = wm_cd_status();
+ if(WM_CDS_NO_DISC(status))
+ return -1;
+
+ if(WM_CDM_PLAYING == wm_cur_cdmode) {
+ if(drive.proto && drive.proto->gen_pause)
+ (drive.proto->gen_pause)(&drive);
+
+ paused_pos = cur_pos_rel;
+ } else if(WM_CDM_PAUSED == status) {
+ if(!(drive.proto->gen_resume) || (drive.proto->gen_resume(&drive) > 0)) {
+ wm_cd_play(thiscd.curtrack, paused_pos, playlist[cur_listno-1].end);
+ }
+ } else {
+ return -1;
+ }
+ wm_cd_status();
+ return 0;
+} /* wm_cd_pause() */
+
+/*
+ * wm_cd_stop()
+ *
+ * Stop the CD if it's not already stopped.
+ */
+int
+wm_cd_stop( void )
+{
+ int status;
+
+ status = wm_cd_status();
+ if(WM_CDS_NO_DISC(status))
+ return -1;
+
+ if (status != WM_CDM_STOPPED) {
+
+ if(drive.proto && drive.proto->gen_stop)
+ (drive.proto->gen_stop)(&drive);
+
+ status = wm_cd_status();
+ }
+
+ return (status != WM_CDM_STOPPED);
+} /* wm_cd_stop() */
+
+/*
+ * Eject the current CD, if there is one, and set the mode to 5.
+ *
+ * Returns 0 on success, 1 if the CD couldn't be ejected, or 2 if the
+ * CD contains a mounted filesystem.
+ */
+int
+wm_cd_eject( void )
+{
+ int err = -1;
+
+ wm_cd_stop();
+
+ if(drive.proto && drive.proto->gen_eject)
+ err = (drive.proto->gen_eject)(&drive);
+
+ if (err < 0) {
+ if (err == -3) {
+ return 2;
+ } else {
+ return 1;
+ }
+ }
+
+ wm_cd_status();
+
+ return 0;
+}
+
+int wm_cd_closetray(void)
+{
+ int status, err = -1;
+
+ status = wm_cd_status();
+ if (status == WM_CDM_UNKNOWN || status == WM_CDM_NO_DISC)
+ return -1;
+
+ if(drive.proto->gen_closetray)
+ err = (drive.proto->gen_closetray)(&drive);
+
+ return(err ? 0 : wm_cd_status()==2 ? 1 : 0);
+} /* wm_cd_closetray() */
+
+struct cdtext_info*
+wm_cd_get_cdtext( void )
+{
+ int status;
+
+ status = wm_cd_status();
+
+ if(WM_CDS_NO_DISC(status))
+ return NULL;
+
+ return get_glob_cdtext(&drive, 0);
+}
+
+/*
+ * find_trkind(track, index, start)
+ *
+ * Start playing at a particular track and index, optionally using a particular
+ * frame as a starting position. Returns a frame number near the start of the
+ * index mark if successful, 0 if the track/index didn't exist.
+ *
+ * This is made significantly more tedious (though probably easier to port)
+ * by the fact that CDROMPLAYTRKIND doesn't work as advertised. The routine
+ * does a binary search of the track, terminating when the interval gets to
+ * around 10 frames or when the next track is encountered, at which point
+ * it's a fair bet the index in question doesn't exist.
+ */
+int
+wm_find_trkind( int track, int ind, int start )
+{
+ int top = 0, bottom, current, interval, ret = 0, i, status;
+
+ status = wm_cd_status();
+ if(WM_CDS_NO_DISC(status))
+ return 0;
+
+ for (i = 0; i < thiscd.ntracks; i++) {
+ if (thiscd.trk[i].track == track)
+ break;
+ }
+
+ bottom = thiscd.trk[i].start;
+
+ for (; i < thiscd.ntracks; i++) {
+ if (thiscd.trk[i].track > track)
+ break;
+ }
+
+ top = i == thiscd.ntracks ? (thiscd.length - 1) * 75 : thiscd.trk[i].start;
+ if (start > bottom && start < top)
+ bottom = start;
+
+ current = (top + bottom) / 2;
+ interval = (top - bottom) / 4;
+
+ do {
+ wm_cd_play_chunk(current, current + 75, current);
+
+ if (wm_cd_status() != 1)
+ return 0;
+ while (cur_frame < current) {
+ if(wm_cd_status() != 1 || wm_cur_cdmode != WM_CDM_PLAYING)
+ return 0;
+ else
+ wm_susleep(1);
+ }
+
+ if (thiscd.trk[thiscd.curtrack - 1].track > track)
+ break;
+
+ if(cur_index >= ind) {
+ ret = current;
+ current -= interval;
+ } else {
+ current += interval;
+ }
+
+ interval /= 2;
+
+ } while (interval > 2);
+
+ return ret;
+} /* find_trkind() */
+
+int
+wm_cd_set_verbosity( int level )
+{
+ wm_lib_set_verbosity(level);
+ return wm_lib_get_verbosity();
+}
+
+/*
+ * volume is valid WM_VOLUME_MUTE <= vol <= WM_VOLUME_MAXIMAL,
+ * balance is valid WM_BALANCE_ALL_LEFTS <= balance <= WM_BALANCE_ALL_RIGHTS
+ */
+
+int
+wm_cd_volume( int vol, int bal )
+{
+ int left, right;
+ const int bal1 = (vol - WM_VOLUME_MUTE)/(WM_BALANCE_ALL_RIGHTS - WM_BALANCE_SYMMETRED);
+
+/*
+ * Set "left" and "right" to volume-slider values accounting for the
+ * balance setting.
+ *
+ */
+ if(vol < WM_VOLUME_MUTE) vol = WM_VOLUME_MUTE;
+ if(vol > WM_VOLUME_MAXIMAL) vol = WM_VOLUME_MAXIMAL;
+ if(bal < WM_BALANCE_ALL_LEFTS) bal = WM_BALANCE_ALL_LEFTS;
+ if(bal > WM_BALANCE_ALL_RIGHTS) bal = WM_BALANCE_ALL_RIGHTS;
+
+ left = vol - (bal * bal1);
+ right = vol + (bal * bal1);
+
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calculate volume left %i, right %i\n", left, right);
+
+ if (left > WM_VOLUME_MAXIMAL)
+ left = WM_VOLUME_MAXIMAL;
+ if (right > WM_VOLUME_MAXIMAL)
+ right = WM_VOLUME_MAXIMAL;
+
+ if(!(drive.proto) || !(drive.proto->gen_set_volume))
+ return -1;
+ else
+ return (drive.proto->gen_set_volume)(&drive, left, right);
+} /* cd_volume() */
+
+int
+wm_cd_getvolume( void )
+{
+ int left, right;
+
+ if(!(drive.proto) || !(drive.proto->gen_get_volume) ||
+ (drive.proto->gen_get_volume)(&drive, &left, &right) < 0 || left == -1)
+ return -1;
+
+ if (left < right) {
+ wm_cd_cur_balance = (right - left) / 2;
+ if (wm_cd_cur_balance > WM_BALANCE_ALL_RIGHTS)
+ wm_cd_cur_balance = WM_BALANCE_ALL_RIGHTS;
+ return right;
+ } else if (left == right) {
+ wm_cd_cur_balance = WM_BALANCE_SYMMETRED;
+ return left;
+ } else {
+ wm_cd_cur_balance = (right - left) / 2;
+ if (wm_cd_cur_balance < WM_BALANCE_ALL_LEFTS)
+ wm_cd_cur_balance = WM_BALANCE_ALL_LEFTS;
+ return left;
+ }
+}
+
+int
+wm_cd_getbalance( void )
+{
+ int left, right;
+
+ if(!(drive.proto) || !(drive.proto->gen_get_volume) ||
+ (drive.proto->gen_get_volume)(&drive, &left, &right) < 0 || left == -1)
+ return WM_BALANCE_SYMMETRED;
+
+ if (left < right) {
+ wm_cd_cur_balance = (right - left) / 2;
+ if (wm_cd_cur_balance > WM_BALANCE_ALL_RIGHTS)
+ wm_cd_cur_balance = WM_BALANCE_ALL_RIGHTS;
+ } else if (left == right) {
+ wm_cd_cur_balance = WM_BALANCE_SYMMETRED;
+ } else {
+ wm_cd_cur_balance = (right - left) / 2;
+ if (wm_cd_cur_balance < WM_BALANCE_ALL_LEFTS)
+ wm_cd_cur_balance = WM_BALANCE_ALL_LEFTS;
+ }
+ return wm_cd_cur_balance;
+}
+
+/*
+ * Prototype wm_drive structure, with generic functions. The generic functions
+ * will be replaced with drive-specific functions as appropriate once the drive
+ * type has been sensed.
+ */
+struct wm_drive_proto generic_proto = {
+ gen_init, /* functions... */
+ gen_close,
+ gen_get_trackcount,
+ gen_get_cdlen,
+ gen_get_trackinfo,
+ gen_get_drive_status,
+ gen_get_volume,
+ gen_set_volume,
+ gen_pause,
+ gen_resume,
+ gen_stop,
+ gen_play,
+ gen_eject,
+ gen_closetray,
+ gen_get_cdtext
+};
+
+const char*
+gen_status(int status)
+{
+ static char tmp[250];
+ switch(status) {
+ case WM_CDM_TRACK_DONE:
+ return "WM_CDM_TRACK_DONE";
+ case WM_CDM_PLAYING:
+ return "WM_CDM_PLAYING";
+ case WM_CDM_FORWARD:
+ return "WM_CDM_FORWARD";
+ case WM_CDM_PAUSED:
+ return "WM_CDM_PAUSED";
+ case WM_CDM_STOPPED:
+ return "WM_CDM_STOPPED";
+ case WM_CDM_EJECTED:
+ return "WM_CDM_EJECTED";
+ case WM_CDM_DEVICECHANGED:
+ return "WM_CDM_DEVICECHANGED";
+ case WM_CDM_NO_DISC:
+ return "WM_CDM_NO_DISC";
+ case WM_CDM_UNKNOWN:
+ return "WM_CDM_UNKNOWN";
+ case WM_CDM_CDDAERROR:
+ return "WM_CDM_CDDAERROR";
+ case WM_CDM_CDDAACK:
+ return "WM_CDM_CDDAACK";
+ default:
+ {
+ sprintf(tmp, "unexpected status %i", status);
+ return tmp;
+ }
+ }
+}
diff --git a/kscd/libwm/cdtext.c b/kscd/libwm/cdtext.c
new file mode 100644
index 00000000..bb4e19b8
--- /dev/null
+++ b/kscd/libwm/cdtext.c
@@ -0,0 +1,428 @@
+/***************************************************************************
+ cdtext.c - description
+ -------------------
+ begin : Mon Feb 12 2001
+ copyright : (C) 2001,2003 by Alex Kern
+ email : alex.kern@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#ifdef libunicode
+ #include <unicode.h>
+#endif
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_database.h"
+#include "include/wm_platform.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdinfo.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_MISC
+
+/* local prototypes */
+int free_cdtext_info_block(struct cdtext_info_block* cdtextinfoblock);
+int free_cdtext_info(struct cdtext_info* cdtextinfo);
+struct cdtext_info_block* malloc_cdtext_info_block(int count_of_tracks);
+void get_data_from_cdtext_pack(
+ const struct cdtext_pack_data_header *pack,
+ const struct cdtext_pack_data_header *pack_previous,
+ cdtext_string *p_componente);
+
+struct cdtext_info wm_cdtext_info = { 0, 0, 0, 0 };
+
+int free_cdtext_info_block(struct cdtext_info_block* cdtextinfoblock)
+{
+ if(cdtextinfoblock)
+ {
+ if(cdtextinfoblock->name)
+ free(cdtextinfoblock->name);
+ if(cdtextinfoblock->performer)
+ free(cdtextinfoblock->performer);
+ if(cdtextinfoblock->songwriter)
+ free(cdtextinfoblock->songwriter);
+ if(cdtextinfoblock->composer)
+ free(cdtextinfoblock->composer);
+ if(cdtextinfoblock->arranger)
+ free(cdtextinfoblock->arranger);
+ if(cdtextinfoblock->message)
+ free(cdtextinfoblock->message);
+ if(cdtextinfoblock->UPC_EAN_ISRC_code)
+ free(cdtextinfoblock->UPC_EAN_ISRC_code);
+
+ if(cdtextinfoblock->block_encoding_text)
+ free(cdtextinfoblock->block_encoding_text);
+ }
+
+ return 0;
+}
+
+int free_cdtext_info(struct cdtext_info* cdtextinfo)
+{
+ int i;
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT INFO: free_cdtext_info() called\n");
+
+ if(cdtextinfo)
+ {
+ for(i = 0; i < 8; i++)
+ {
+ if(cdtextinfo->blocks[i])
+ {
+ free_cdtext_info_block(cdtextinfo->blocks[i]);
+ }
+ }
+ memset(cdtextinfo, 0, sizeof(struct cdtext_info));
+ }
+
+ return 0;
+}
+
+struct cdtext_info_block* malloc_cdtext_info_block(int count_of_tracks)
+{
+ int memamount;
+ struct cdtext_info_block* lp_block;
+
+ lp_block = malloc(sizeof(struct cdtext_info_block));
+ if(!lp_block)
+ {
+ return (struct cdtext_info_block*)0;
+ }
+ memset(lp_block, 0, sizeof(struct cdtext_info_block));
+
+ memamount = count_of_tracks * sizeof(cdtext_string);
+
+ lp_block->name = malloc(memamount);
+ if(!lp_block->name)
+ return (struct cdtext_info_block*)free_cdtext_info_block(lp_block);
+ memset(lp_block->name, 0, memamount);
+
+ lp_block->performer = malloc(memamount);
+ if(!lp_block->performer)
+ return (struct cdtext_info_block*)free_cdtext_info_block(lp_block);
+ memset(lp_block->performer, 0, memamount);
+
+ lp_block->songwriter = malloc(memamount);
+ if(!lp_block->songwriter)
+ return (struct cdtext_info_block*)free_cdtext_info_block(lp_block);
+ memset(lp_block->songwriter, 0, memamount);
+
+ lp_block->composer = malloc(memamount);
+ if(!lp_block->composer)
+ return (struct cdtext_info_block*)free_cdtext_info_block(lp_block);
+ memset(lp_block->composer, 0, memamount);
+
+ lp_block->arranger = malloc(memamount);
+ if(!lp_block->arranger)
+ return (struct cdtext_info_block*)free_cdtext_info_block(lp_block);
+ memset(lp_block->arranger, 0, memamount);
+
+ lp_block->message = malloc(memamount);
+ if(!lp_block->message)
+ return (struct cdtext_info_block*)free_cdtext_info_block(lp_block);
+ memset(lp_block->message, 0, memamount);
+
+ lp_block->UPC_EAN_ISRC_code = malloc(memamount);
+ if(!lp_block->UPC_EAN_ISRC_code)
+ return (struct cdtext_info_block*)free_cdtext_info_block(lp_block);
+ memset(lp_block->UPC_EAN_ISRC_code, 0, memamount);
+
+ return lp_block;
+}
+
+void get_data_from_cdtext_pack(
+ const struct cdtext_pack_data_header *pack,
+ const struct cdtext_pack_data_header *pack_previous,
+ cdtext_string *p_componente)
+{
+
+ int arr = pack->header_field_id2_tracknumber;
+ int i;
+ int language_block;
+ int unicode;
+
+ language_block = (pack->header_field_id4_block_no >> 4) & 0x07;
+ unicode = pack->header_field_id4_block_no & 0x80;
+
+ if(!unicode)
+ {
+ for(i = 0; i < DATAFIELD_LENGHT_IN_PACK; i++)
+ {
+ if(pack->text_data_field[i] == 0x00) /* end marker */
+ {
+ arr++;
+ }
+ else if(pack->text_data_field[i] == 0x09) /* repeat last marker */
+ {
+ /* ASSERT(arr > 0) */
+ strcat((char*)(p_componente[arr]), (char*)(p_componente[arr-1]));
+ arr++;
+ }
+ else
+ {
+ strncat((char*)(p_componente[arr]), (char*)(&(pack->text_data_field[i])), 1);
+ }
+ }
+ }
+#ifdef libunicode
+ else /* doublebytes ;-) */
+ {
+ for(i = 0; i < DATAFIELD_LENGHT_IN_PACK; i += 2)
+ {
+ if((Uchar)(pack->text_data_field[i]) == 0x0000) /* end marker */
+ {
+ arr++;
+ }
+ else if((Uchar)(pack->text_data_field[i]) == 0x0909) /* repeat last marker */
+ {
+ /* ASSERT(arr > 0) */
+ strcat((char*)(p_componente[arr]), (char*)(p_componente[arr-1]));
+ arr++;
+ }
+ else
+ {
+ strncat((char*)(p_componente[arr]), u_uc_to_utf8((Uchar*)(&(pack->text_data_field[i]))), 1);
+ }
+ }
+ }
+#else
+ else {
+ wm_lib_message(WM_MSG_LEVEL_ERROR | WM_MSG_CLASS, "can't handle unicode");
+ }
+#endif
+}
+
+struct cdtext_info*
+get_glob_cdtext(struct wm_drive *d, int redo)
+{
+ /* alloc cdtext_info */
+
+ unsigned char *buffer;
+ int buffer_length;
+ int ret;
+ int i;
+ struct cdtext_pack_data_header *pack, *pack_previous;
+ cdtext_string *p_componente;
+ struct cdtext_info_block *lp_block;
+ if(!d->proto || d->proto->gen_get_cdtext == NULL || d->proto->gen_get_trackcount == NULL) {
+ return NULL;
+ }
+
+ if(!redo && wm_cdtext_info.valid) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS, "CDTEXT DEBUG: recycle cdtext\n");
+ return &wm_cdtext_info;
+ } else
+ free_cdtext_info(&wm_cdtext_info);
+
+ lp_block = 0;
+ p_componente = 0;
+ buffer = 0;
+ buffer_length = 0;
+
+ ret = (d->proto->gen_get_cdtext)(d, &buffer, &buffer_length);
+ if(!ret)
+ {
+ (d->proto->gen_get_trackcount)(d, &(wm_cdtext_info.count_of_entries));
+ if( wm_cdtext_info.count_of_entries < 0 )
+ wm_cdtext_info.count_of_entries = 1;
+ else
+ wm_cdtext_info.count_of_entries++;
+
+ i = 0;
+
+ pack = 0; /* NULL pointer*/
+ while(i < buffer_length)
+ {
+ pack_previous = pack;
+ pack = (struct cdtext_pack_data_header*)(buffer+i);
+ /* to implement: check_crc(pack); */
+
+ /* only for valid packs */
+ if(pack->header_field_id1_typ_of_pack >= 0x80 && pack->header_field_id1_typ_of_pack < 0x90)
+ {
+ int code, j;
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT DEBUG: valid packet at 0x%08X: 0x %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
+ i,
+ pack->header_field_id1_typ_of_pack,
+ pack->header_field_id2_tracknumber,
+ pack->header_field_id3_sequence,
+ pack->header_field_id4_block_no,
+ pack->text_data_field[0],
+ pack->text_data_field[1],
+ pack->text_data_field[2],
+ pack->text_data_field[3],
+ pack->text_data_field[4],
+ pack->text_data_field[5],
+ pack->text_data_field[6],
+ pack->text_data_field[7],
+ pack->text_data_field[8],
+ pack->text_data_field[9],
+ pack->text_data_field[10],
+ pack->text_data_field[11],
+ pack->crc_byte1,
+ pack->crc_byte2);
+ wm_cdtext_info.count_of_valid_packs++;
+
+ code = (pack->header_field_id4_block_no >> 4) & 0x07;
+ if(0 == lp_block || lp_block->block_code != code) /* find or create one new block */
+ {
+ lp_block = 0;
+ for(j = 0; j < MAX_LANGUAGE_BLOCKS && wm_cdtext_info.blocks[j] != 0 && 0 == lp_block; j++)
+ {
+ if(wm_cdtext_info.blocks[j]->block_code == code)
+ {
+ lp_block = wm_cdtext_info.blocks[j];
+ }
+ }
+
+ if(MAX_LANGUAGE_BLOCKS <= j)
+ {
+ free_cdtext_info(&wm_cdtext_info);
+ wm_lib_message(WM_MSG_LEVEL_ERROR | WM_MSG_CLASS,
+ "CDTEXT ERROR: more as 8 languageblocks defined\n");
+ return NULL;
+ }
+
+ if(0 == lp_block)
+ {
+ /* make next new block */
+ lp_block = malloc_cdtext_info_block(wm_cdtext_info.count_of_entries);
+ if(0 == lp_block)
+ {
+ wm_lib_message(WM_MSG_LEVEL_ERROR | WM_MSG_CLASS,
+ "CDTEXT ERROR: out of memory, can't create a new language block\n");
+ free_cdtext_info(&wm_cdtext_info);
+ return NULL /*ENOMEM*/;
+ }
+ else
+ {
+ wm_cdtext_info.blocks[j] = lp_block;
+ wm_cdtext_info.blocks[j]->block_code = code;
+ wm_cdtext_info.blocks[j]->block_unicode = pack->header_field_id4_block_no & 0x80;
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT INFO: created a new language block; code %i, %s characters\n", code, lp_block->block_unicode?"doublebyte":"singlebyte");
+/*
+ unsigned char block_encoding; not jet!
+ cdtext_string* block_encoding_text;
+*/
+ }
+ }
+ }
+ }
+
+ switch(pack->header_field_id1_typ_of_pack)
+ {
+ case 0x80:
+ p_componente = (lp_block->name);
+ get_data_from_cdtext_pack(pack, pack_previous, p_componente);
+ break;
+ case 0x81:
+ p_componente = (lp_block->performer);
+ get_data_from_cdtext_pack(pack, pack_previous, p_componente);
+ break;
+ case 0x82:
+ p_componente = (lp_block->songwriter);
+ get_data_from_cdtext_pack(pack, pack_previous, p_componente);
+ break;
+ case 0x83:
+ p_componente = (lp_block->composer);
+ get_data_from_cdtext_pack(pack, pack_previous, p_componente);
+ break;
+ case 0x84:
+ p_componente = (lp_block->arranger);
+ get_data_from_cdtext_pack(pack, pack_previous, p_componente);
+ break;
+ case 0x85:
+ p_componente = (lp_block->message);
+ get_data_from_cdtext_pack(pack, pack_previous, p_componente);
+ break;
+ case 0x86:
+ memcpy((char*)(lp_block->binary_disc_identification_info),
+ (char*)(pack->text_data_field), DATAFIELD_LENGHT_IN_PACK);
+ break;
+ case 0x87:
+ memcpy((char*)(lp_block->binary_genreidentification_info),
+ (char*)(pack->text_data_field), DATAFIELD_LENGHT_IN_PACK);
+ break;
+ case 0x88:
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT INFO: PACK with code 0x88 (TOC)\n");
+ break;
+ case 0x89:
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT INFO: PACK with code 0x89 (second TOC)\n");
+ break;
+ case 0x8A:
+ case 0x8B:
+ case 0x8C:
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT INFO: PACK with code 0x%02X (reserved)\n", pack->header_field_id1_typ_of_pack);
+ break;
+ case 0x8D:
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT INFO: PACK with code 0x8D (for content provider only)\n");
+ break;
+ case 0x8E:
+ p_componente = (lp_block->UPC_EAN_ISRC_code);
+ get_data_from_cdtext_pack(pack, pack_previous, p_componente);
+ break;
+ case 0x8F:
+ memcpy((char*)(lp_block->binary_size_information),
+ (char*)(pack->text_data_field), DATAFIELD_LENGHT_IN_PACK);
+ break;
+ default:
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT ERROR: invalid packet at 0x%08X: 0x %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
+ i,
+ pack->header_field_id1_typ_of_pack,
+ pack->header_field_id2_tracknumber,
+ pack->header_field_id3_sequence,
+ pack->header_field_id4_block_no,
+ pack->text_data_field[0],
+ pack->text_data_field[1],
+ pack->text_data_field[2],
+ pack->text_data_field[3],
+ pack->text_data_field[4],
+ pack->text_data_field[5],
+ pack->text_data_field[6],
+ pack->text_data_field[7],
+ pack->text_data_field[8],
+ pack->text_data_field[9],
+ pack->text_data_field[10],
+ pack->text_data_field[11],
+ pack->crc_byte1,
+ pack->crc_byte2);
+ wm_cdtext_info.count_of_invalid_packs++;
+ }
+ i += sizeof(struct cdtext_pack_data_header);
+ } /* while */
+ }
+
+ if(0 == ret && wm_cdtext_info.count_of_valid_packs > 0)
+ wm_cdtext_info.valid = 1;
+
+ return &wm_cdtext_info;
+}
+
+void free_cdtext(void)
+{
+ if (wm_cdtext_info.valid)
+ free_cdtext_info(&wm_cdtext_info);
+}
diff --git a/kscd/libwm/configure.in.in b/kscd/libwm/configure.in.in
new file mode 100644
index 00000000..a0ce3ab7
--- /dev/null
+++ b/kscd/libwm/configure.in.in
@@ -0,0 +1,72 @@
+dnl +-------------------------------+
+dnl | Checks for LIBWORKMAN |
+dnl +-------------------------------+
+AC_MSG_CHECKING(for CDDA)
+
+AC_ARG_WITH(kscd-cdda, [ --with-kscd-cdda build CDDA support in kscd [default=yes]],
+[
+ if test $withval = yes; then
+ libwm_with_cdda=yes
+ else
+ libwm_with_cdda=no
+ fi
+],libwm_with_cdda=yes)
+
+if test "$libwm_with_cdda" = "yes"; then
+case $host in
+ *-*-linux*)
+ AC_CHECK_HEADERS(pthread.h)
+ AC_TRY_COMPILE(
+ [
+#ifndef __GNUC__
+#define __GNUC__ 1
+#endif
+/* needed for vanilla kernel headers, which do provide __u64 only
+ for ansi */
+#undef __STRICT_ANSI__
+/* needed for non-ansi kernel headers */
+#define asm __asm__
+#define inline __inline__
+#include <linux/types.h>
+#include <linux/cdrom.h>
+#undef asm
+#undef inline
+ ],[
+#if defined(__linux__)
+ioctl(1, CDROMREADAUDIO, 0);
+#else
+ #error platform?
+#endif
+ ],, libwm_with_cdda=no)
+ ;;
+ *-*-sunos*)
+ AC_CHECK_HEADERS(pthread.h)
+ AC_TRY_COMPILE(
+ [
+#include <sys/types.h>
+#include <sys/cdio.h>
+ ],[
+#if defined(__sun) || defined(sun)
+ioctl(1, CDROMCDDA, 0);
+#else
+ #error platform?
+#endif
+ ],, libwm_with_cdda=no)
+ ;;
+ *)
+ libwm_with_cdda=no
+ ;;
+esac
+fi
+
+if test "$libwm_with_cdda" = "yes"; then
+ AC_DEFINE(BUILD_CDDA, 1, [Define if you will CDDA support in kscd])
+fi
+AM_CONDITIONAL(libwm_with_cdda, test "$libwm_with_cdda" = "yes")
+
+AC_MSG_RESULT($libwm_with_cdda)
+
+
+dnl +-------------------------------+
+dnl | End LIBWORKMAN checks |
+dnl +-------------------------------+
diff --git a/kscd/libwm/database.c b/kscd/libwm/database.c
new file mode 100644
index 00000000..f7f4e7cc
--- /dev/null
+++ b/kscd/libwm/database.c
@@ -0,0 +1,1543 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Manage the CD database. All these routines assume that the "cd" global
+ * structure contains current information (as far as the outside world knows;
+ * obviously it won't contain track titles just after a CD is inserted.)
+ */
+
+#define RCFILE "/.workmanrc"
+#define DBFILE "/.workmandb"
+#define FUZZFRAMES 75
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <time.h>
+#include "include/wm_config.h"
+#include "include/wm_helpers.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdinfo.h"
+#include "include/wm_cddb.h"
+#include "include/wm_index.h"
+#include "include/wm_database.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_DB
+
+#define SWALLOW_LINE(fp) { int _c; while ((_c = getc(fp)) != '\n' && _c != EOF); }
+
+/* local prototypes */
+char *print_cdinfo(struct wm_cdinfo *pcd, int prefs);
+FILE *open_rcfile(const char *name, const char *mode);
+int *reset_tracks(void);
+int search_db( FILE *fp, int prefs, int scan, int holesize_wanted );
+void spinwheels(int secs);
+int lockit(int fd, int type);
+void save_globals(FILE *fp);
+int save_entry(char *filename, int pref);
+/* local prototypes END */
+
+static int suppress_locking = 0; /* Turn off locking of datafile (dangerous) */
+
+static char *rcfile = NULL; /* Personal rcfile */
+static char *dbfiles = NULL; /* Colon-separated list of databases */
+static char **databases = NULL; /* NULL-terminated list of databases */
+
+static char *otherrc = NULL; /* Unrecognized cruft from start of rcfile */
+
+static long rcpos, rclen; /* XXX */
+
+static int found_in_db, found_in_rc;
+static long holepos, firstpos;
+
+static int fuzz_frames = FUZZFRAMES;
+
+static int wm_db_save_disabled = FALSE;
+
+static int cur_playnew = -1;
+
+extern int cur_ntracks, cur_nsections;
+
+int mark_a = 0;
+int mark_b = 0;
+
+
+/*
+ *
+ */
+int wm_db_get_playnew( void )
+{
+ return 0;
+}
+
+/*
+ * split_workmandb()
+ *
+ * Split the WORKMANDB environment variable, if any, into a list of database
+ * files in the global "databases". If WORKMANDB is not available, make
+ * a single entry with $HOME/DBFILE.
+ *
+ * Also, fill the "rcfile" global with the personal preferences filename.
+ *
+ * The environment variables should have already been read and placed in the
+ * "rcfile" and "dbfiles" globals, respectively.
+ */
+void
+split_workmandb( void )
+{
+ int ndbs, i;
+ char *home, *wmdb;
+ int no_rc = 0, no_db = 0;
+
+ if (rcfile == NULL)
+ {
+ if ((home = getenv("HOME")) != NULL)
+ {
+ rcfile = malloc(strlen(home) + sizeof(RCFILE));
+ if (rcfile == NULL)
+ {
+
+nomem:
+ perror("split_workmandb()");
+ exit(1);
+ }
+
+ strcpy(rcfile, home);
+ strcat(rcfile, RCFILE);
+ }
+ else
+ no_rc = 1;
+
+ }
+
+ if ((wmdb = dbfiles) == NULL)
+ {
+ if ((home = getenv("HOME")) != NULL)
+ {
+ wmdb = malloc(strlen(home) + sizeof(DBFILE));
+ if (wmdb == NULL)
+ goto nomem;
+
+ databases = malloc(2 * sizeof (databases[0]));
+ if (databases == NULL)
+ goto nomem;
+
+ strcpy(wmdb, home);
+ strcat(wmdb, DBFILE);
+ databases[0] = wmdb;
+ databases[1] = NULL;
+ }
+ else
+ {
+ static char *emptydb = NULL;
+
+ databases = &emptydb;
+ no_db = 1;
+ }
+ }
+ else
+ {
+ ndbs = 1;
+ for (home = wmdb; *home; home++)
+ if (*home == ':')
+ {
+ *home = '\0';
+ ndbs++;
+ }
+
+ databases = malloc((ndbs + 1) * sizeof(databases[0]));
+ if (databases == NULL)
+ goto nomem;
+
+ for (i = 0; i < ndbs; i++)
+ {
+ databases[i] = wmdb;
+ wmdb += strlen(wmdb) + 1;
+ }
+
+ databases[i] = NULL;
+ }
+
+ if (no_db || no_rc)
+ {
+#ifndef NDEBUG
+ fprintf(stderr,
+"WorkMan was run without a home directory, probably by a system daemon.\n");
+ fprintf(stderr, "It doesn't know where to find ");
+ if (no_rc)
+ {
+ fprintf(stderr, "your personal preferences file ");
+ if (no_db)
+ fprintf(stderr, "or the\ndatabase of CD descriptions");
+ }
+ else
+ fprintf(stderr, "the database of CD descriptions");
+
+ fprintf(stderr,
+".\nYou can use the X resources \"workman.db.shared\" and \"workman.db.personal\"\nto tell WorkMan where to look.\n");
+#endif
+ wm_db_save_disabled = TRUE;
+ }
+}
+
+/*
+ * print_cdinfo(cd, prefs)
+ *
+ * cd A pointer to a cdinfo struct.
+ * prefs Flag: write personal preferences?
+ *
+ * Print a CD's information (in more or less readable form) to a buffer.
+ * Returns a pointer to the buffer.
+ *
+ * XXX - could be more efficient about calling wm_strmcat() and strlen().
+ */
+char *
+print_cdinfo(struct wm_cdinfo *cd, int prefs)
+{
+ int i;
+ char tempbuf[2000]; /* XXX - is this always big enough? */
+ static char *cdibuf = NULL;
+ struct wm_playlist *l;
+
+ sprintf(tempbuf, "\ntracks %d", cd->ntracks);
+ for (i = 0; i < cur_ntracks; i++)
+ if (cd->trk[i].section < 2)
+ sprintf(tempbuf + strlen(tempbuf), " %d",
+ cd->trk[i].start);
+ sprintf(tempbuf + strlen(tempbuf), " %d\n", cd->length);
+
+ wm_strmcpy(&cdibuf, tempbuf);
+
+ if (cur_nsections)
+ {
+ sprintf(tempbuf, "sections %d", cur_nsections);
+ /* fixed a bug here */
+ for (i = 0; i < cur_ntracks; i++)
+ if (cd->trk[i].section > 1)
+ sprintf(tempbuf + strlen(tempbuf), " %d",
+ cd->trk[i].start);
+ sprintf(tempbuf + strlen(tempbuf), "\n");
+
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+
+ if (prefs)
+ {
+ if (cd->autoplay)
+ wm_strmcat(&cdibuf, "autoplay\n");
+ for (l = cd->lists; l != NULL && l->name != NULL; l++)
+ {
+ wm_strmcat(&cdibuf, "playlist ");
+
+ i = strlen(cdibuf) - 1;
+ wm_strmcat(&cdibuf, l->name);
+ while (cdibuf[++i])
+ if (cdibuf[i] == ' ' || cdibuf[i] == '\t')
+ cdibuf[i] = '_';
+
+ if (l->list != NULL)
+ {
+ for (i = 0; l->list[i]; i++)
+ ;
+ sprintf(tempbuf, " %d", i);
+ wm_strmcat(&cdibuf, tempbuf);
+ for (i = 0; l->list[i]; i++)
+ {
+ sprintf(tempbuf, " %d", l->list[i]);
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+ wm_strmcat(&cdibuf, "\n");
+ }
+ else
+ wm_strmcat(&cdibuf, " 0\n");
+ }
+
+ if (cd->volume)
+ {
+ /*
+ * Have to maintain compatibility with old versions,
+ * where volume was 0-32.
+ */
+ sprintf(tempbuf, "cdvolume %d\n", (cd->volume * 32) / 100);
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+
+ if (cd->playmode)
+ {
+ sprintf(tempbuf, "playmode %d\n", cd->playmode);
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+
+ if (mark_a)
+ {
+ sprintf(tempbuf, "mark %d START\n", mark_a);
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+ if (mark_b)
+ {
+ sprintf(tempbuf, "mark %d END\n", mark_b);
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+
+ if (cd->otherrc)
+ wm_strmcat(&cdibuf, cd->otherrc);
+
+ for (i = 0; i < cur_ntracks; i++)
+ {
+ if (cd->trk[i].avoid)
+ {
+ sprintf(tempbuf, "dontplay %d\n", i + 1);
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+ if (cd->trk[i].volume)
+ {
+ sprintf(tempbuf, "volume %d %d\n", i + 1,
+ (cd->trk[i].volume * 32) / 100);
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+ if (cd->trk[i].otherrc)
+ wm_strmcat(&cdibuf, cd->trk[i].otherrc);
+ }
+ }
+ else
+ {
+ if (cd->cdname[0])
+ {
+ wm_strmcat(&cdibuf, "cdname ");
+ wm_strmcat(&cdibuf, cd->cdname);
+ wm_strmcat(&cdibuf, "\n");
+ }
+
+ if (cd->artist[0])
+ {
+ wm_strmcat(&cdibuf, "artist ");
+ wm_strmcat(&cdibuf, cd->artist);
+ wm_strmcat(&cdibuf, "\n");
+ }
+
+ if (cd->otherdb)
+ wm_strmcat(&cdibuf, cd->otherdb);
+
+ for (i = 0; i < cur_ntracks; i++)
+ {
+ if (cd->trk[i].section > 1)
+ wm_strmcat(&cdibuf, "s-");
+ wm_strmcat(&cdibuf, "track ");
+ if (cd->trk[i].songname != NULL)
+ wm_strmcat(&cdibuf, cd->trk[i].songname);
+ wm_strmcat(&cdibuf, "\n");
+ if (cd->trk[i].contd)
+ {
+ if (cd->trk[i].section > 1)
+ wm_strmcat(&cdibuf, "s-");
+ wm_strmcat(&cdibuf, "continue\n");
+ }
+ if (cd->trk[i].otherdb)
+ wm_strmcat(&cdibuf, cd->trk[i].otherdb);
+ }
+ }
+
+ return (cdibuf);
+} /* print_cdinfo() */
+
+/*
+ * Open the rcfile for reading or writing.
+ *
+ * name Filename
+ * mode "r" or "w"
+ */
+FILE *
+open_rcfile(const char *name, const char *mode)
+{
+ FILE *fp;
+ struct stat st;
+
+ fp = fopen(name, mode);
+ if (fp == NULL)
+ {
+ if (errno != ENOENT || mode[0] == 'w')
+ perror(name);
+ }
+ else
+ {
+ /* Don't let people open directories or devices */
+ if (fstat(fileno(fp), &st) < 0)
+ {
+ perror(name);
+ fclose(fp);
+ return (NULL);
+ }
+
+#ifdef S_ISREG
+ if (! S_ISREG(st.st_mode))
+#else
+ if ((st.st_mode & S_IFMT) != S_IFREG)
+#endif
+ {
+ errno = EISDIR;
+ perror(name);
+ fclose(fp);
+ return (NULL);
+ }
+
+ if (mode[0] == 'w') /* create -- put data in so locks work */
+ {
+ fputs("# WorkMan database file\n", fp);
+ fclose(fp);
+ fp = fopen(name, "r+");
+ if (fp == NULL)
+ if (errno != ENOENT)
+ perror(name);
+ }
+ }
+
+ return (fp);
+}
+
+/*
+ *
+ * allocate and clear "trackmap".
+ *
+ */
+int *reset_tracks(void)
+{
+ int i, j;
+ int *trackmap;
+ /*
+ * Since we access track numbers indirectly (to handle sections
+ * with at least a little elegance), the track mapping needs to be
+ * set up before we read anything. Initially it must assume that
+ * no sections will appear in this datafile.
+ */
+ trackmap = malloc(sizeof(int) * cur_ntracks);
+ if (trackmap == NULL)
+ {
+ perror("trackmap");
+ exit(1);
+ }
+ j = 0;
+ for (i = 0; i < cd->ntracks; i++)
+ {
+ trackmap[i] = j;
+ while (cd->trk[++j].section > 1)
+ ;
+ }
+ return trackmap;
+} /* reset_tracks() */
+
+/*
+ * Load a new-format database file, searching for a match with the currently
+ * inserted CD. Modify the in-core copy of the CD info based on what's found
+ * in the database.
+ *
+ * Returns 1 if there was a match or 0 if not.
+ *
+ * fp FILE* of database or rcfile.
+ * prefs 1 if we're searching .workmanrc, 0 for .workmandb,
+ * 2 if just reading settings
+ * scan Scan for "tracks" location and entry size only
+ * holesize_wanted How big a hole we're looking for, if any.
+ *
+ * If a hole was found along the way, update the global "holepos" with its
+ * starting offset in the file. A hole is defined as a bunch of blank lines
+ * preceding a "tracks" line. Holepos will contain the best match.
+ *
+ * In addition, "firstpos" will be filled with the position of the first
+ * "tracks" keyword, so we know how much room is available for global
+ * settings at the rcfile's start.
+ */
+int
+search_db( FILE *fp, int prefs, int scan, int holesize_wanted )
+
+{
+ char keyword[64], listname[64], *c;
+ int b, i, j, track = 0, listsize, ntracks, scratch, searching = 1;
+ int *trackmap = 0, gotsections = 0;
+ int fudge, maxfudge, sizediff, bestfudge = 0;
+ long pos = 0, thisholepos = -1, holesize = 99991239;
+ struct wm_playlist *l;
+
+ rclen = 0;
+
+ wm_lib_message(WM_MSG_CLASS_DB|WM_MSG_LEVEL_DEBUG , "db: Reached search_db()\n" );
+
+ /* We may not find any holes at all! */
+ if (holesize_wanted)
+ holepos = -1;
+
+ if( prefs != 2 )
+ trackmap = reset_tracks();
+
+ if (prefs)
+ freeup(&otherrc);
+ firstpos = -1;
+ while (! feof(fp))
+ {
+ pos = ftell(fp);
+ keyword[0] = '\0';
+ do
+ b = getc(fp);
+ while (b != EOF && b != '\n' && isspace(b));
+
+ if (b == EOF || feof(fp))
+ break;
+
+ if (b != '\n')
+ {
+ keyword[0] = b;
+ fscanf(fp, "%62s", &keyword[1]);
+ }
+ if (keyword[0] == '\0') /* Blank line. */
+ {
+ if (thisholepos < 0)
+ thisholepos = pos;
+ continue;
+ }
+
+ /* Strip off "s-" if we've seen a "sections" keyword */
+ if (gotsections && keyword[0] == 's' && keyword[1] == '-')
+ {
+ for (c = &keyword[2]; (c[-2] = *c) != '\0'; c++)
+ ;
+ wm_lib_message(WM_MSG_CLASS_DB|WM_MSG_LEVEL_DEBUG , "db: stripped off the 's-'. Result is %s\n", keyword);
+ }
+
+ /* If this is the start of a CD entry, see if it matches. */
+ if (! strcmp(keyword, "tracks"))
+ {
+ if (prefs == 2)
+ break;
+
+ /* Is this the end of a hole? */
+ if (holesize_wanted && (thisholepos >= 0))
+ {
+ /* Yep. Is it better than the last one? */
+ if (pos - thisholepos < holesize && pos -
+ thisholepos >= holesize_wanted)
+ {
+ holepos = thisholepos;
+ holesize = pos - thisholepos;
+ }
+ thisholepos = -1;
+ }
+
+ /* Is it the start of the CD entries? */
+ if (firstpos == -1)
+ firstpos = pos;
+
+ /* Is this the end of the entry we really wanted? */
+ if (! searching)
+ {
+ rclen = pos - rcpos;
+ break;
+ }
+
+ /* If we have a near match, indicate that we
+ should stop reading tracks, etc now */
+ if (searching == 2)
+ {
+ searching = 3;
+ continue;
+ }
+
+ fscanf(fp, "%d", &ntracks);
+
+ if (ntracks != cd->ntracks)
+ {
+chomp:
+ SWALLOW_LINE(fp);
+ continue;
+ }
+
+ fudge = 0;
+ maxfudge = (ntracks * fuzz_frames) >> 1;
+ track = 0;
+ for (i = 0; i < ntracks; i++)
+ {
+ fscanf(fp, "%d", &scratch);
+ if (scratch != cd->trk[track].start)
+ {
+ sizediff = abs(scratch - cd->trk[track].start);
+ if (sizediff > fuzz_frames ||
+ (sizediff && scan))
+ break;
+ fudge += sizediff;
+ }
+ while (cd->trk[++track].section > 1)
+ ;
+ }
+ if (i != ntracks)
+ goto chomp;
+
+ if (fudge > 0) /* best near match? */
+ {
+ if (fudge > maxfudge)
+ goto chomp;
+ if (bestfudge == 0 || fudge < bestfudge)
+ bestfudge = fudge;
+ else
+ goto chomp;
+ rcpos = pos;
+ track = 0;
+ searching = 2;
+ }
+ else /* probably exact match */
+ {
+ fscanf(fp, "%d", &scratch);
+
+ if (scratch != -1 && scratch != cd->length)
+ goto chomp;
+
+ /* Found it! */
+ rcpos = pos;
+ track = 0;
+ searching = 0;
+ }
+
+ SWALLOW_LINE(fp); /* Get rid of newline */
+ }
+
+ /* Global mode stuff goes here */
+ else if (! strcmp(keyword, "cddbprotocol"))
+ {
+ getc(fp);
+ i = getc(fp); /* only first letter is used */
+ cddb.protocol = i == 'c' ? 1 :
+ i == 'h' ? 2 : 3 ;
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ }
+
+ else if (! strcmp(keyword, "cddbserver"))
+ {
+ getc(fp); /* lose the space */
+ if (cddb.cddb_server[0])
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ else
+ {
+ fgets(cddb.cddb_server,
+ sizeof(cddb.cddb_server), fp);
+ if ((i = strlen(cddb.cddb_server)))
+ cddb.cddb_server[i - 1] = '\0';
+ }
+ }
+
+ else if (! strcmp(keyword, "cddbmailadress"))
+ {
+ getc(fp); /* lose the space */
+ if (cddb.mail_adress[0])
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ else
+ {
+ fgets(cddb.mail_adress,
+ sizeof(cddb.mail_adress), fp);
+ if ((i = strlen(cddb.mail_adress)))
+ cddb.mail_adress[i - 1] = '\0';
+ }
+ }
+
+ else if (! strcmp(keyword, "cddbpathtocgi"))
+ {
+ getc(fp); /* lose the space */
+ if (cddb.path_to_cgi[0])
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ else
+ {
+ fgets(cddb.path_to_cgi,
+ sizeof(cddb.path_to_cgi), fp);
+ if ((i = strlen(cddb.path_to_cgi)))
+ cddb.path_to_cgi[i - 1] = '\0';
+ }
+ }
+
+ else if (! strcmp(keyword, "cddbproxy"))
+ {
+ getc(fp); /* lose the space */
+ if (cddb.proxy_server[0])
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ else
+ {
+ fgets(cddb.proxy_server,
+ sizeof(cddb.proxy_server), fp);
+ if ((i = strlen(cddb.proxy_server)))
+ cddb.proxy_server[i - 1] = '\0';
+ }
+ }
+
+ else if (! strcmp(keyword, "whendone"))
+ {
+ getc(fp);
+ i = getc(fp); /* only first letter is used */
+ if (cur_stopmode == -1) /* user's setting preferred */
+ cur_stopmode = i == 's' ? 0 : i == 'r' ? 1 : 2;
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ }
+
+ else if (! strcmp(keyword, "playnew"))
+ {
+ if (cur_playnew == -1)
+ cur_playnew = 1;
+ SWALLOW_LINE(fp);
+ }
+
+ /* If we're searching, skip to the next "tracks" line. */
+ else if (((searching & 1)|| scan)
+ && !(prefs && firstpos == -1))
+ SWALLOW_LINE(fp)
+
+ else if (! strcmp(keyword, "sections"))
+ {
+ gotsections = 1;
+ fscanf(fp, "%d", &ntracks);
+
+ free(trackmap);
+ trackmap = (int *) malloc(sizeof(int) *
+ (cur_ntracks + ntracks));
+ if (trackmap == NULL)
+ {
+ perror("section mapping");
+ exit(1);
+ }
+
+ /*
+ * If sections are already defined, use these as a
+ * reference, mapping this CD entry's section numbers
+ * to the ones in core.
+ *
+ * Otherwise, split the CD up according to the sections
+ * listed here.
+ */
+ if (cur_nsections)
+ {
+ track = 0;
+ i = 0;
+ while (ntracks)
+ {
+ ntracks--;
+ fscanf(fp, "%d", &scratch);
+ while (scratch > cd->trk[track].start)
+ {
+ if (cd->trk[track].section < 2)
+ trackmap[i++] = track;
+ ++track;
+
+ if (track == cur_ntracks)
+ break;
+ }
+
+ /* rc has later sections than db... */
+ if (track == cur_ntracks)
+ break;
+
+ /* Matches can be approximate */
+ if (scratch+75 > cd->trk[track].start &&
+ scratch-75 < cd->trk[track].start)
+ trackmap[i++] = track++;
+ else
+ trackmap[i++] = -1;
+
+ if (track == cur_ntracks)
+ break;
+ }
+
+ /* This only happens if track == cur_ntracks */
+ while (ntracks--)
+ trackmap[i++] = -1;
+
+ while (track < cur_ntracks)
+ {
+ if (cd->trk[track].section < 2)
+ trackmap[i++] = track;
+ track++;
+ }
+
+ track = 0;
+ SWALLOW_LINE(fp);
+ }
+ else
+ {
+ while (ntracks--)
+ {
+ fscanf(fp, "%d", &scratch);
+ split_trackinfo(scratch);
+ }
+
+ for (i = 0; i < cur_ntracks; i++)
+ {
+ trackmap[i] = i;
+ /* split_trackinfo() sets this */
+ cd->trk[i].contd = 0;
+ }
+
+ SWALLOW_LINE(fp);
+ }
+ }
+
+ else if (! strcmp(keyword, "track"))
+ {
+ char buf[502];
+
+ getc(fp); /* lose the space */
+
+ /* don't overwrite existing track names. */
+ /* However, overwrite them if there was a "bad" fuzzy match before */
+ if ((trackmap[track] == -1 || track > (cd->ntracks + cur_nsections)) && (searching == 2))
+ SWALLOW_LINE(fp)
+ else if (cd->trk[trackmap[track]].songname &&
+ cd->trk[trackmap[track]].songname[0])
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ else
+ {
+ fgets(buf, sizeof(buf), fp);
+ wm_lib_message(WM_MSG_CLASS_DB|WM_MSG_LEVEL_DEBUG, "found track %s\n", buf);
+ if( (i = strlen(buf)) )
+ buf[i - 1] = '\0';
+ wm_strmcpy(&cd->trk[trackmap[track]].songname, buf);
+ }
+ track++;
+ }
+
+ else if (! strcmp(keyword, "playmode"))
+ fscanf(fp, "%d", &cd->playmode);
+
+ else if (! strcmp(keyword, "autoplay"))
+ cd->autoplay = 1;
+
+ else if (! strcmp(keyword, "cdname"))
+ {
+ /* because of fuzzy matching that may change
+ the disk contents, we reset everything when
+ we find the name, in hopes that we will recover
+ most, if not all, of the information from the
+ file. */
+/*
+ * nasty bug was here. Was it? BUGBUGBUG
+ *
+ * wipe_cdinfo();
+ * trackmap = reset_tracks();
+ */
+
+ getc(fp); /* lose the space */
+ /* don't overwrite existing cd name. */
+ if (cd->cdname[0] && (searching == 2))
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ else
+ {
+ if (searching > 1)
+ {
+ strcpy(cd->cdname, "Probably://");
+ fgets(cd->cdname + strlen(cd->cdname), sizeof(cd->cdname), fp);
+ }
+ else
+ {
+ fgets(cd->cdname, sizeof(cd->cdname), fp);
+ }
+ if ( (i = strlen(cd->cdname)) )
+ cd->cdname[i - 1] = '\0';
+ }
+ }
+
+ else if (! strcmp(keyword, "artist"))
+ {
+ getc(fp); /* lose the space */
+ /* don't overwrite existing artist names. */
+ if (cd->artist[0])
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ else
+ {
+ fgets(cd->artist, sizeof(cd->artist), fp);
+ if( (i = strlen(cd->artist)) )
+ cd->artist[i - 1] = '\0';
+ }
+ }
+
+ else if (! strcmp(keyword, "cdvolume"))
+ {
+ fscanf(fp, "%d", &cd->volume);
+ cd->volume = (cd->volume * 100) / 32;
+ }
+
+ else if (! strcmp(keyword, "dontplay"))
+ {
+ fscanf(fp, "%d", &i);
+ if (trackmap[i - 1] != -1)
+ cd->trk[trackmap[i - 1]].avoid = 1;
+ }
+
+ else if (! strcmp(keyword, "continue"))
+ {
+ if (trackmap[track - 1] != -1)
+ cd->trk[trackmap[track - 1]].contd = 1;
+ }
+
+ else if (! strcmp(keyword, "volume"))
+ {
+ fscanf(fp, "%d", &i);
+ if (trackmap[i - 1] == -1)
+ SWALLOW_LINE(fp)
+ else
+ {
+ i = trackmap[i - 1];
+ fscanf(fp, "%d", &cd->trk[i].volume);
+ cd->trk[i].volume = (cd->trk[i].volume*100)/32;
+ if (cd->trk[i].volume > 32)
+ cd->trk[i].volume = 0;
+ }
+ }
+
+ else if (! strcmp(keyword, "playlist"))
+ {
+ getc(fp);
+ fscanf(fp, "%63s", listname);
+
+/* XXX take this out at some point */
+ if (! strcmp(listname, "Default"))
+ strcpy(listname, "List A");
+
+ for (i = 0; listname[i]; i++)
+ if (listname[i] == '_')
+ listname[i] = ' ';
+
+ l = new_playlist(cd, listname);
+ if (l == NULL)
+ {
+plnomem:
+ perror("playlist read");
+ exit(1);
+ }
+
+ fscanf(fp, "%d", &listsize);
+
+ l->list = malloc(sizeof(int) * (listsize + 1));
+ if (l->list == NULL)
+ goto plnomem;
+
+ /* Leave out tracks that weren't in .workmandb. */
+ j = 0;
+ for (i = 0; i < listsize; i++)
+ {
+ fscanf(fp, "%d", &scratch);
+ scratch = trackmap[scratch - 1];
+ if (scratch != -1)
+ l->list[j++] = scratch + 1;
+ }
+
+ l->list[j] = 0;
+ }
+
+ else if (! strcmp(keyword, "mark"))
+ {
+ int mark_val = -1, mark_namelen;
+ char mark_name[32];
+
+ fscanf(fp, "%d", &mark_val);
+ if (mark_val == -1)
+ goto chomp;
+
+ if (getc(fp) != ' ')
+ continue;
+
+ fgets(mark_name, sizeof(mark_name), fp);
+ if( ( mark_namelen = strlen(mark_name)) )
+ mark_name[mark_namelen - 1] = '\0';
+
+/*
+ if (! strcmp(mark_name, "START"))
+ set_abtimer(0, mark_val);
+ else if (! strcmp(mark_name, "END"))
+ set_abtimer(1, mark_val);
+
+*/
+ }
+
+ /* Unrecognized keyword. Put it in the right place. */
+ else
+ {
+ char **buf, input[BUFSIZ];
+
+ if (track && trackmap[track - 1] == -1)
+ {
+ SWALLOW_LINE(fp);
+ continue;
+ }
+
+ i = track ? trackmap[track - 1] : 0;
+ buf = prefs ? i ? &cd->trk[i].otherrc : &cd->otherrc :
+ i ? &cd->trk[i].otherdb : &cd->otherdb;
+ if (firstpos == -1) {
+ if (prefs) {
+ buf = &otherrc;
+ } else {
+ goto chomp;
+ } /* if() else */
+ } /* if() */
+ wm_strmcat(buf, keyword);
+ do {
+ input[sizeof(input) - 1] = 'x';
+ fgets(input, sizeof(input), fp);
+ wm_strmcat(buf, input);
+ } while (input[sizeof(input) - 1] != 'x');
+ }
+ }
+
+ if (rclen == 0 && !searching)
+ rclen = pos - rcpos;
+
+ if (searching > 1) /* A near match has been found. Good enough. */
+ searching = 0;
+
+ cddb_struct2cur();
+ return (! searching);
+
+} /* search_db() */
+
+/*
+ * Delay some amount of time without using interval timers.
+ */
+void
+spinwheels(int secs) {
+ struct timeval tv;
+
+ tv.tv_usec = 0;
+ tv.tv_sec = secs;
+ select(0, NULL, NULL, NULL, &tv);
+} /* spinwheels() */
+
+/*
+ * lockit(fd, type)
+ *
+ * fd file descriptor
+ * type lock type
+ *
+ * Lock a file. Time out after a little while if we can't get a lock;
+ * this usually means the locking system is broken.
+ *
+ * Unfortunately, if there are lots of people contending for a lock,
+ * this can result in the file not getting locked when it probably should.
+ */
+int
+lockit(int fd, int type)
+{
+ struct flock fl;
+ int result, timer = 0;
+
+ if (suppress_locking)
+ return (0);
+
+ fl.l_type = type;
+ fl.l_whence = 0;
+ fl.l_start = 0;
+ fl.l_len = 0;
+
+ while ((result = fcntl(fd, F_SETLK, &fl)) < 0)
+ {
+ if (errno != EACCES || errno != EAGAIN)
+ break;
+ if (timer++ == 30)
+ {
+ errno = ETIMEDOUT;
+ break;
+ }
+
+ spinwheels(1);
+ }
+
+ return (result);
+} /* lockit() */
+
+/*
+ * Search all the database files and our personal preference file for
+ * more information about the current CD.
+ */
+void
+load( void )
+{
+ FILE *fp;
+ char **dbfile;
+ int locked = 0;
+ int dbfound = 0, *trklist, i;
+ unsigned long dbpos;
+
+/* This is some kind of profiling code. I don't change it
+ to wm_lib_message() for now... */
+#ifndef NDEBUG
+ long t1, t2;
+ if( getenv( "WORKMAN_DEBUG" ) != NULL )
+ {
+ time(&t1);
+ printf("%s (%d): search start = %ld\n", __FILE__, __LINE__, t1);
+ fflush(stdout);
+ }
+#endif
+
+ dbfile = databases;
+
+ found_in_db = 0;
+
+ /* Turn the cd->trk array into a simple array of ints. */
+ trklist = (int *)malloc(sizeof(int) * cd->ntracks);
+ for (i = 0; i < cd->ntracks; i++)
+ trklist[i] = cd->trk[i].start;
+
+ do {
+ if (*dbfile && idx_find_entry(*dbfile, cd->ntracks, trklist,
+ cd->length * 75, 0, &dbpos) == 0)
+ dbfound = 1;
+
+ fp = *dbfile ? open_rcfile(*dbfile, "r") : NULL;
+ if (fp != NULL)
+ {
+ if (lockit(fileno(fp), F_RDLCK))
+ perror("Couldn't get read (db) lock");
+ else
+ locked = 1;
+
+ if (dbfound)
+ fseek(fp, dbpos, 0);
+
+ if (search_db(fp, 0, 0, 0))
+ {
+ found_in_db = 1;
+ cd->whichdb = *dbfile;
+ }
+
+ if (locked && lockit(fileno(fp), F_UNLCK))
+ perror("Couldn't relinquish (db) lock");
+
+ fclose(fp);
+ }
+ } while (*++dbfile != NULL && cd->whichdb == NULL);
+
+#ifndef NDEBUG
+ if( getenv( "WORKMAN_DEBUG" ) != NULL )
+ {
+ time(&t2);
+ printf("%s (%d): db search end = %ld, elapsed = %ld\n", __FILE__, __LINE__, t2, t2 - t1);
+ fflush(stdout);
+ }
+#endif
+
+ fp = rcfile ? open_rcfile(rcfile, "r") : NULL;
+ if (fp != NULL)
+ {
+ locked = 0;
+ if (lockit(fileno(fp), F_RDLCK))
+ perror("Couldn't get read (rc) lock");
+ else
+ locked = 1;
+
+ rcpos = 0;
+ found_in_rc = search_db(fp, 1, 0, 0);
+ if (! found_in_rc)
+ cd->autoplay = wm_db_get_playnew();
+
+ if (locked && lockit(fileno(fp), F_UNLCK))
+ perror("Couldn't relinquish (rc) lock");
+
+ fclose(fp);
+ }
+
+ free(trklist);
+
+ if (cur_playnew == -1)
+ cur_playnew = 0;
+
+#ifndef NDEBUG
+ if( getenv( "WORKMAN_DEBUG" ) != NULL )
+ {
+ time(&t2);
+ printf("%s (%d): search end = %ld, elapsed = %ld\n", __FILE__, __LINE__, t2, t2 - t1);
+ fflush(stdout);
+ }
+#endif
+} /* load() */
+
+/*
+ * Load program settings from the rcfile.
+ */
+void
+load_settings( void )
+{
+ FILE *fp;
+ int locked;
+
+ fp = rcfile ? open_rcfile(rcfile, "r") : NULL;
+ if (fp != NULL)
+ {
+ locked = 0;
+ if (lockit(fileno(fp), F_RDLCK))
+ perror("Couldn't get read (rc) lock");
+ else
+ locked = 1;
+
+ rcpos = 0;
+ found_in_rc = search_db(fp, 2, 0, 0);
+ if (! found_in_rc)
+ cd->autoplay = wm_db_get_playnew();
+
+ if (locked && lockit(fileno(fp), F_UNLCK))
+ perror("Couldn't relinquish (rc) lock");
+
+ fclose(fp);
+ }
+} /* load_settings() */
+
+/*
+ * save_globals()
+ *
+ * Save the global preferences, scooting CD entries to the end if needed.
+ * The assumption here is that the rcfile is locked, and that firstpos has
+ * been set by a previous scan.
+ */
+void
+save_globals(FILE *fp)
+{
+ char *globes = NULL, *cdentry = NULL, temp[100];
+ long curpos;
+ int globesize, hit_cdent = 0, c = 0;
+
+ if (otherrc)
+ wm_strmcpy(&globes, otherrc);
+
+ if (cddb.protocol)
+ {
+ sprintf(temp, "cddbprotocol ");
+ switch(cddb.protocol)
+ {
+ case 1: /* cddbp */
+ sprintf(temp + strlen(temp), "cddbp\n");
+ break;
+ case 2: /* http */
+ sprintf(temp + strlen(temp), "http\n");
+ break;
+ case 3: /* proxy */
+ sprintf(temp + strlen(temp), "proxy\n");
+ break;
+ default:
+ break;
+ }
+ wm_strmcat(&globes, temp);
+
+ if(cddb.mail_adress[0])
+ {
+ sprintf(temp,"cddbmailadress %s\n",
+ cddb.mail_adress);
+ wm_strmcat(&globes, temp);
+ }
+
+ if(cddb.cddb_server[0])
+ {
+ sprintf(temp,"cddbserver %s\n",
+ cddb.cddb_server);
+ wm_strmcat(&globes, temp);
+ }
+
+ if(cddb.path_to_cgi[0])
+ {
+ sprintf(temp,"cddbpathtocgi %s\n",
+ cddb.mail_adress);
+ wm_strmcat(&globes, temp);
+ }
+
+ if(cddb.proxy_server[0])
+ {
+ sprintf(temp,"cddbproxy %s\n",
+ cddb.mail_adress);
+ wm_strmcat(&globes, temp);
+ }
+ }
+
+ if (cur_stopmode == 1 || cur_stopmode == 2)
+ {
+ sprintf(temp, "whendone %s\n", cur_stopmode == 1 ? "repeat" :
+ "eject");
+ wm_strmcat(&globes, temp);
+ }
+
+ if (cur_playnew == 1)
+ wm_strmcat(&globes, "playnew\n");
+
+ curpos = firstpos;
+ if (curpos < 0)
+ curpos = 0;
+
+ fseek(fp, curpos, SEEK_SET);
+
+ if (firstpos < (globesize = globes != NULL ? strlen(globes) : 0))
+ {
+ while (1)
+ {
+ temp[sizeof(temp)-1] = 'x';
+
+ if (fgets(temp, sizeof(temp), fp) == NULL)
+ {
+ fseek(fp, 0, SEEK_SET);
+ if (globes != NULL)
+ {
+ fwrite(globes, globesize, 1, fp);
+ free(globes);
+ }
+ if (cdentry != NULL)
+ {
+ fwrite(cdentry, strlen(cdentry), 1, fp);
+ free(cdentry);
+ }
+ return;
+ }
+
+ if (! strncmp(temp, "tracks ", 7))
+ {
+ hit_cdent = 1;
+ if (curpos >= globesize)
+ break;
+ }
+
+ if (! hit_cdent)
+ {
+ curpos += strlen(temp);
+ if (temp[sizeof(temp)-1] == '\0')
+ while ((c = getc(fp)) != '\n' &&
+ c != EOF)
+ curpos++;
+ if (c == '\n')
+ curpos++;
+
+ continue;
+ }
+
+ wm_strmcat(&cdentry, temp);
+ curpos += strlen(temp);
+ while (temp[sizeof(temp)-1] == '\0')
+ {
+ temp[sizeof(temp)-1] = 'x';
+ if (fgets(temp, sizeof(temp), fp) == NULL)
+ break;
+ wm_strmcat(&cdentry, temp);
+ curpos += strlen(temp);
+ }
+ }
+
+ if (cdentry != NULL)
+ {
+ fseek(fp, 0, SEEK_END);
+ fwrite(cdentry, strlen(cdentry), 1, fp);
+ free(cdentry);
+ }
+ }
+
+ if (globes != NULL)
+ {
+ fseek(fp, 0, SEEK_SET);
+ fwrite(globes, globesize, 1, fp);
+ free(globes);
+ }
+
+ while (globesize++ < curpos)
+ putc('\n', fp);
+} /* save_globals() */
+
+/*
+ * save_entry()
+ *
+ * Save the CD information to one database.
+ *
+ * filename Database to save to.
+ * pref 0 for hard data, 1 for preferences.
+ *
+ * If an entry for this CD exists already, overwrite it with the new entry
+ * if the new entry is the same size or smaller, or with newlines if the new
+ * entry is larger (in which case the new entry is appended to the file.)
+ *
+ * Also, if the preference information is being updated, save it to the
+ * file while we've got it locked. Scoot stuff from the beginning of
+ * the file to the end as needed to facilitate this.
+ *
+ * XXX Preference-saving should probably be done elsewhere, like in an
+ * Apply button on the Goodies popup, and in any case needs to go to a
+ * different file (.Xdefaults?)
+ *
+ * Returns 0 on success.
+ */
+int
+save_entry(char *filename, int pref)
+{
+ FILE *fp;
+ char *buf;
+ int len, i, locked = 0;
+
+
+ if( filename == NULL )
+ return (-1);
+
+ fp = open_rcfile(filename, "r+");
+ if (fp == NULL)
+ {
+ if (errno == ENOENT) /* doesn't exist already */
+ fp = open_rcfile(filename, "w");
+ if (fp == NULL)
+ return (-1);
+ }
+
+ if (lockit(fileno(fp), F_WRLCK))
+ perror("Warning: Couldn't get write lock");
+ else
+ locked = 1;
+
+ buf = print_cdinfo(cd, pref);
+ len = strlen(buf); /* doesn't return if there's an error */
+
+ rcpos = -1;
+ search_db(fp, pref, 1, len);
+ if (rcpos != -1) /* XXX */
+ {
+ /*
+ * Jump to the entry's position in the database file, if
+ * it was found.
+ */
+ fseek(fp, rcpos, SEEK_SET);
+
+ if (rclen >= len && holepos == -1)
+ {
+ /*
+ * If the new entry will fit in the space occupied by
+ * the old one, overwrite the old one and make a hole
+ * of the appropriate size at its end.
+ *
+ * No need to update the index file in this case, as
+ * the entry's position hasn't changed.
+ */
+ fputs(buf, fp);
+ for (i = len; i < rclen; i++)
+ fputc('\n', fp);
+ }
+ else
+ {
+ /*
+ * Overwrite the old entry with a hole and delete
+ * its pointer in the index file.
+ */
+ for (i = 0; i < rclen; i++)
+ fputc('\n', fp);
+ idx_delete_entry(filename, cd->trk[cd->ntracks-1].start,
+ 0, rcpos);
+
+ rcpos = -1;
+ }
+ }
+
+ /*
+ * Old entry wasn't found, or its new version wouldn't fit where
+ * the old one was.
+ */
+ if (rcpos == -1)
+ {
+ /*
+ * Write the new entry in a hole, if there is one,
+ * or at the end of the file.
+ */
+ if (holepos >= 0)
+ {
+ fseek(fp, holepos, SEEK_SET);
+ if (holepos < firstpos)
+ firstpos = holepos;
+ }
+ else
+ {
+ fseek(fp, 0, SEEK_END);
+ holepos = ftell(fp);
+ }
+ fputs(buf, fp);
+
+ /*
+ * Write a new index entry for this CD.
+ */
+ idx_write_entry(filename, cd->trk[cd->ntracks - 1].start,
+ holepos);
+ }
+
+ if (pref)
+ save_globals(fp);
+
+ fflush(fp);
+
+ if (locked && lockit(fileno(fp), F_UNLCK))
+ perror("Warning: Couldn't relinquish write lock");
+
+ fclose(fp);
+
+ return (0);
+} /* save_entry() */
+
+/*
+ * save()
+ *
+ * Save CD information to the appropriate datafile (the first file in the
+ * list, unless the entry came from another database file) and to the
+ * personal prefs file.
+ */
+int
+save( void )
+{
+
+ if( wm_db_save_disabled == FALSE )
+ {
+ if (save_entry(rcfile, 1))
+ return (0);
+
+ if (cd->whichdb == NULL || access(cd->whichdb, W_OK))
+ cd->whichdb = databases[0];
+
+ if (save_entry(cd->whichdb, 0))
+ return (0);
+
+ return( WM_DB_SAVE_ERROR );
+ } else {
+ return( WM_DB_SAVE_DISABLED );
+ }
+} /* save() */
diff --git a/kscd/libwm/drv_sony.c b/kscd/libwm/drv_sony.c
new file mode 100644
index 00000000..a9db5d73
--- /dev/null
+++ b/kscd/libwm/drv_sony.c
@@ -0,0 +1,166 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Vendor-specific drive control routines for Sony CDU-8012 series.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_scsi.h"
+
+#define PAGE_AUDIO 0x0e
+
+/* local prototypes */
+static int sony_init( struct wm_drive *d );
+static int sony_set_volume(struct wm_drive *d, int left, int right );
+static int sony_get_volume( struct wm_drive *d, int *left, int *right );
+
+
+extern int min_volume, max_volume;
+
+struct wm_drive_proto sony_proto = {
+ sony_init, /* functions... */
+ gen_close,
+ gen_get_trackcount,
+ gen_get_cdlen,
+ gen_get_trackinfo,
+ gen_get_drive_status,
+ sony_get_volume,
+ sony_set_volume,
+ gen_pause,
+ gen_resume,
+ gen_stop,
+ gen_play,
+ gen_eject,
+ gen_closetray,
+ NULL /* gen_get_cdtext */
+};
+
+/*
+ * Initialize the driver.
+ */
+static int sony_init( struct wm_drive *d ) {
+
+/* Sun use Sony drives as well */
+#if defined(SYSV) && defined(CODEC)
+ codec_init();
+#endif
+ min_volume = 128;
+ max_volume = 255;
+ return( 0 );
+} /* sony_init() */
+
+/*
+ * On the Sony CDU-8012 drive, the amount of sound coming out the jack
+ * increases much faster toward the top end of the volume scale than it
+ * does at the bottom. To make up for this, we make the volume scale look
+ * sort of logarithmic (actually an upside-down inverse square curve) so
+ * that the volume value passed to the drive changes less and less as you
+ * approach the maximum slider setting. Additionally, only the top half
+ * of the volume scale is valid; the bottom half is all silent. The actual
+ * formula looks like
+ *
+ * max^2 - (max - vol)^2 max
+ * v = --------------------- + ---
+ * max * 2 2
+ *
+ * Where "max" is the maximum value of the volume scale, usually 100.
+ */
+static int
+scale_volume(vol, max)
+ int vol, max;
+{
+ vol = (max*max - (max - vol) * (max - vol)) / max;
+ return ((vol + max) / 2);
+}
+
+/*
+ * Given a value between min_volume and max_volume, return the standard-scale
+ * volume value needed to achieve that hardware value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ */
+static int
+unscale_volume(cd_vol, max)
+ int cd_vol, max;
+{
+ int vol = 0, top = max, bot = 0, scaled = 0;
+
+ cd_vol = (cd_vol * 100 + (max_volume - 1)) / max_volume;
+
+ while (bot <= top)
+ {
+ vol = (top + bot) / 2;
+ scaled = scale_volume(vol, max);
+ if (cd_vol <= scaled)
+ top = vol - 1;
+ else
+ bot = vol + 1;
+ }
+
+ /* Might have looked down too far for repeated scaled values */
+ if (cd_vol < scaled)
+ vol++;
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > max)
+ vol = max;
+
+ return (vol);
+}
+
+/*
+ * Get the volume. Sun's CD-ROM driver doesn't support this operation, even
+ * though their drive does. Dumb.
+ */
+static int
+sony_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ unsigned char mode[16];
+
+ /* Get the current audio parameters first. */
+ if (wm_scsi_mode_sense(d, PAGE_AUDIO, mode))
+ return (-1);
+
+ *left = unscale_volume(mode[9], 100);
+ *right = unscale_volume(mode[11], 100);
+
+ return (0);
+}
+
+/*
+ * Set the volume using the wacky scale outlined above. The Sony drive
+ * responds to the standard set-volume command.
+ */
+static int
+sony_set_volume(struct wm_drive *d, int left, int right )
+{
+ left = scale_volume(left, 100);
+ right = scale_volume(right, 100);
+ return (gen_set_volume(d, left, right));
+}
diff --git a/kscd/libwm/drv_toshiba.c b/kscd/libwm/drv_toshiba.c
new file mode 100644
index 00000000..ef009867
--- /dev/null
+++ b/kscd/libwm/drv_toshiba.c
@@ -0,0 +1,149 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Vendor-specific drive control routines for Toshiba XM-3401 series.
+ */
+
+static char drv_toshiba_id[] = "$Id$";
+
+#include <stdio.h>
+#include <errno.h>
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_scsi.h"
+
+#define SCMD_TOSH_EJECT 0xc4
+
+/* local prototypes */
+static int tosh_init( struct wm_drive *d );
+static int tosh_eject( struct wm_drive *d );
+static int tosh_set_volume( struct wm_drive *d, int left, int right );
+static int tosh_get_volume( struct wm_drive *d, int *left, int *right );
+
+struct wm_drive_proto toshiba_proto = {
+ tosh_init, /* functions... */
+ gen_close,
+ gen_get_trackcount,
+ gen_get_cdlen,
+ gen_get_trackinfo,
+ gen_get_drive_status,
+ tosh_get_volume,
+ tosh_set_volume,
+ gen_pause,
+ gen_resume,
+ gen_stop,
+ gen_play,
+ tosh_eject,
+ gen_closetray,
+ NULL /* gen_get_cdtext */
+};
+
+/*
+ * Initialize the driver.
+ */
+static int
+tosh_init( struct wm_drive *d )
+{
+ extern int min_volume;
+
+/* Sun use Toshiba drives as well */
+#if defined(SYSV) && defined(CODEC)
+ codec_init();
+#endif
+ min_volume = 0;
+ return ( 0 );
+}
+
+/*
+ * Send the Toshiba code to eject the CD.
+ */
+static int
+tosh_eject( struct wm_drive *d )
+{
+ return (sendscsi(d, NULL, 0, 0, SCMD_TOSH_EJECT, 1, 0,0,0,0,0,0,0,0,0,0));
+}
+
+/*
+ * Set the volume. The low end of the scale is more sensitive than the high
+ * end, so make up for that by transforming the volume parameters to a square
+ * curve.
+ */
+static int
+tosh_set_volume( struct wm_drive *d, int left, int right )
+{
+ left = (left * left * left) / 10000;
+ right = (right * right * right) / 10000;
+ return (gen_set_volume(d, left, right));
+}
+
+/*
+ * Undo the transformation above using a binary search (so no floating-point
+ * math is required.)
+ */
+static int
+unscale_volume(cd_vol, max)
+ int cd_vol, max;
+{
+ int vol = 0, top = max, bot = 0, scaled = 0;
+
+ /*cd_vol = (cd_vol * 100 + (max_volume - 1)) / max_volume;*/
+
+ while (bot <= top)
+ {
+ vol = (top + bot) / 2;
+ scaled = (vol * vol) / max;
+ if (cd_vol <= scaled)
+ top = vol - 1;
+ else
+ bot = vol + 1;
+ }
+
+ /* Might have looked down too far for repeated scaled values */
+ if (cd_vol < scaled)
+ vol++;
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > max)
+ vol = max;
+
+ return (vol);
+}
+
+/*
+ * Get the volume.
+ */
+static int
+tosh_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ int status;
+
+ status = gen_get_volume(d, left, right);
+ if (status < 0)
+ return (status);
+ *left = unscale_volume(*left, 100);
+ *right = unscale_volume(*right, 100);
+
+ return (0);
+}
diff --git a/kscd/libwm/include/wm_cdda.h b/kscd/libwm/include/wm_cdda.h
new file mode 100644
index 00000000..11c8eb3e
--- /dev/null
+++ b/kscd/libwm/include/wm_cdda.h
@@ -0,0 +1,203 @@
+#ifndef WM_CDDA_H
+#define WM_CDDA_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * Information about a particular block of CDDA data.
+ */
+struct cdda_block {
+ unsigned char status;
+ unsigned char track;
+ unsigned char index;
+ unsigned char reserved;
+
+ int frame;
+ char *buf;
+ long buflen;
+};
+
+struct cdda_device {
+ int fd;
+ const char *devname;
+
+ unsigned char status;
+ unsigned char track;
+ unsigned char index;
+ unsigned char command;
+
+ int frame;
+ int frames_at_once;
+
+ /* Average volume levels, for level meters */
+ unsigned char lev_chan0;
+ unsigned char lev_chan1;
+
+ /* Current volume setting (0-255) */
+ unsigned char volume;
+
+ /* Current balance setting (0-255, 128 = balanced) */
+ unsigned char balance;
+
+ struct cdda_block *blocks;
+ int numblocks;
+};
+
+#include "wm_cdrom.h"
+#include "wm_config.h"
+#include "wm_struct.h"
+/*
+ * cdda_block status codes.
+ */
+
+/*
+ * Enable or disable CDDA building depending on platform capabilities, and
+ * determine endianness based on architecture. (Gross!)
+ *
+ * For header-comfort, the macros LITTLE_ENDIAN and BIG_ENDIAN had to be
+ * renamed. At least Linux does have bytesex.h and endian.h for easy
+ * byte-order examination.
+ */
+
+#ifdef HAVE_MACHINE_ENDIAN_H
+ #include <machine/endian.h>
+ #if BYTE_ORDER == LITTLE_ENDIAN
+ #define WM_LITTLE_ENDIAN 1
+ #define WM_BIG_ENDIAN 0
+ #else
+ #define WM_LITTLE_ENDIAN 0
+ #define WM_BIG_ENDIAN 1
+ #endif
+#elif defined(__sun) || defined(sun)
+# ifdef SYSV
+# include <sys/types.h>
+# include <sys/cdio.h>
+# ifndef CDROMCDDA
+# undef BUILD_CDDA
+# endif
+# ifdef i386
+# define WM_LITTLE_ENDIAN 1
+# define WM_BIG_ENDIAN 0
+# else
+# define WM_BIG_ENDIAN 1
+# define WM_LITTLE_ENDIAN 0
+# endif
+# else
+# undef BUILD_CDDA
+# endif
+
+/* Linux only allows definition of endianness, because there's no
+ * standard interface for CDROM CDDA functions that aren't available
+ * if there is no support.
+ */
+#elif defined(__linux__)
+/*# include <bytesex.h>*/
+# include <endian.h>
+/*
+ * XXX could this be a problem? The results are only 0 and 1 because
+ * of the ! operator. How about other linux compilers than gcc ?
+ */
+# define WM_LITTLE_ENDIAN !(__BYTE_ORDER - __LITTLE_ENDIAN)
+# define WM_BIG_ENDIAN !(__BYTE_ORDER - __BIG_ENDIAN)
+#elif defined WORDS_BIGENDIAN
+ #define WM_LITTLE_ENDIAN 0
+ #define WM_BIG_ENDIAN 1
+#else
+ #define WM_LITTLE_ENDIAN 1
+ #define WM_BIG_ENDIAN 0
+#endif
+
+/*
+ * The following code shouldn't take effect now.
+ * In 1998, the WorkMan platforms don't support __PDP_ENDIAN
+ * architectures.
+ *
+ */
+
+#if !defined(WM_LITTLE_ENDIAN)
+# if !defined(WM_BIG_ENDIAN)
+# error yet unsupported architecture
+ foo bar this is to stop the compiler.
+# endif
+#endif
+
+#if defined(BUILD_CDDA)
+/*
+ * The following code support us by optimize cdda operations
+ */
+#define CDDARETURN(x) if(x && x->cdda == 1) return
+#define IFCDDA(x) if(x && x->cdda == 1)
+int cdda_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *ind);
+int cdda_play(struct wm_drive *d, int start, int end, int realstart);
+int cdda_pause(struct wm_drive *d);
+int cdda_stop(struct wm_drive *d);
+int cdda_eject(struct wm_drive *d);
+int cdda_set_volume(struct wm_drive *d, int left, int right);
+int cdda_get_volume(struct wm_drive *d, int *left, int *right);
+void cdda_kill(struct wm_drive *d);
+void cdda_save(struct wm_drive *d, char *filename);
+int cdda_get_ack(int);
+int gen_cdda_init(struct wm_drive *d );
+
+void cdda_set_direction(struct wm_drive *d, int newdir);
+void cdda_set_speed(struct wm_drive *d, int speed);
+void cdda_set_loudness(struct wm_drive *d, int loud);
+
+
+int wmcdda_init(struct cdda_device*);
+int wmcdda_open(const char*);
+int wmcdda_close(struct cdda_device*);
+int wmcdda_setup(int start, int end, int realstart);
+long wmcdda_read(struct cdda_device*, struct cdda_block *block);
+void wmcdda_speed(int speed);
+void wmcdda_direction(int newdir);
+
+#else
+ #define CDDARETURN(x)
+ #define IFCDDA(x)
+ #define cdda_get_drive_status
+ #define cdda_play
+ #define cdda_pause
+ #define cdda_resume
+ #define cdda_stop
+ #define cdda_eject
+ #define cdda_set_volume
+ #define cdda_get_volume
+ #define cdda_kill
+ #define cdda_save
+ #define cdda_get_ack
+#endif /* defined(BUILD_CDDA) */
+
+#include <stdio.h>
+
+#ifdef DEBUG
+ #define DEBUGLOG(fmt, args...) fprintf(stderr, fmt, ##args)
+#else
+ #define DEBUGLOG(fmt, args...)
+#endif
+#define ERRORLOG(fmt, args...) fprintf(stderr, fmt, ##args)
+
+#endif /* WM_CDDA_H */
diff --git a/kscd/libwm/include/wm_cddb.h b/kscd/libwm/include/wm_cddb.h
new file mode 100644
index 00000000..26a0c2d9
--- /dev/null
+++ b/kscd/libwm/include/wm_cddb.h
@@ -0,0 +1,36 @@
+#ifndef WM_CDDB_H
+#define WM_CDDB_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+unsigned long cddb_discid(void);
+void cddb_struct2cur(void);
+void cddb_cur2struct(void);
+void cddb_select(void);
+void connect_cddb(void);
+void update_cddbserver(void);
+void cddb_request(void);
+
+#endif /* WM_CDDB_H */
diff --git a/kscd/libwm/include/wm_cdinfo.h b/kscd/libwm/include/wm_cdinfo.h
new file mode 100644
index 00000000..dbb8e949
--- /dev/null
+++ b/kscd/libwm/include/wm_cdinfo.h
@@ -0,0 +1,76 @@
+#ifndef WM_CDINFO_H
+#define WM_CDINFO_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Prototypes from cdinfo.c
+ *
+ * This is just one more step to a more modular and understandable code.
+ */
+
+#include "wm_struct.h"
+extern char *cur_trackname; /* Take a guess */
+extern int cur_index; /* Current index mark */
+extern int cur_frame; /* Current frame number */
+extern struct wm_play *playlist; /* = NULL */
+
+/*extern int cur_track;*/ /* Current track number, starting at 1 */
+extern char *cur_artist; /* Name of current CD's artist */
+extern char cur_avoid; /* Avoid flag */
+extern char cur_contd; /* Continued flag */
+extern char *cur_cdname; /* Album name */
+extern int cur_nsections; /* Number of sections currently defined */
+extern int exit_on_eject;
+extern int cur_pos_abs;
+extern int cur_pos_rel;
+extern int cur_cdlen;
+
+extern int cur_ntracks;
+extern int cur_lasttrack;
+extern int cur_firsttrack;
+extern int cur_listno;
+extern int cur_stopmode;
+
+void wipe_cdinfo( void );
+void play_next_entry( int forward );
+void make_playlist( int playmode, int starttrack );
+int get_autoplay( void );
+int get_playmode( void );
+void pl_find_track( int track );
+void play_prev_track( int forward );
+void play_next_track( int forward );
+int tracklen( int num );
+int get_default_volume( int track );
+int split_trackinfo( int pos );
+int remove_trackinfo( int num );
+void freeup( char **x );
+int get_runtime( void );
+const char *trackname( int num );
+void stash_cdinfo( char *artist, char *cdname, int autoplay, int playmode );
+void stash_trkinfo( int track, char *songname, int contd, int avoid );
+int get_avoid( int num );
+int get_contd( int num );
+void default_volume( int track, int vol );
+char *listentry( int num );
+
+#endif /* WM_CDINFO_H */
diff --git a/kscd/libwm/include/wm_cdrom.h b/kscd/libwm/include/wm_cdrom.h
new file mode 100644
index 00000000..d38f8b68
--- /dev/null
+++ b/kscd/libwm/include/wm_cdrom.h
@@ -0,0 +1,114 @@
+#ifndef WM_CDROM_H
+#define WM_CDROM_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Prototypes from cdrom.c
+ *
+ * This is just one more step to a more modular and understandable code.
+ */
+#define WM_CDS_NO_DISC(status) (status == WM_CDM_UNKNOWN ||\
+ status == WM_CDM_EJECTED ||\
+ status == WM_CDM_NO_DISC)
+
+#define WM_CDS_DISC_READY(status)(status == WM_CDM_TRACK_DONE ||\
+ status == WM_CDM_PLAYING ||\
+ status == WM_CDM_FORWARD ||\
+ status == WM_CDM_PAUSED ||\
+ status == WM_CDM_STOPPED)
+
+#define WM_CDS_DISC_PLAYING(status)(status == WM_CDM_TRACK_DONE ||\
+ status == WM_CDM_PLAYING ||\
+ status == WM_CDM_FORWARD ||\
+ status == WM_CDM_PAUSED)
+#define WM_CDM_BACK 1
+#define WM_CDM_TRACK_DONE 1
+#define WM_CDM_PLAYING 2
+#define WM_CDM_FORWARD 3
+#define WM_CDM_PAUSED 4
+#define WM_CDM_STOPPED 5
+#define WM_CDM_EJECTED 6
+#define WM_CDM_DEVICECHANGED 9 /* deprecated */
+#define WM_CDM_NO_DISC 10
+#define WM_CDM_UNKNOWN 11
+#define WM_CDM_CDDAERROR 12
+#define WM_CDM_CDDAACK 0xF0
+
+#define WM_CDIN 0
+#define WM_CDDA 1
+
+#define WM_ENDTRACK 0
+
+#define WM_BALANCE_SYMMETRED 0
+#define WM_BALANCE_ALL_LEFTS -10
+#define WM_BALANCE_ALL_RIGHTS 10
+
+#define WM_VOLUME_MUTE 0
+#define WM_VOLUME_MAXIMAL 100
+
+int wm_cd_init( int cdin, const char *cd_device, const char *soundsystem,
+ const char *sounddevice, const char *ctldevice );
+int wm_cd_destroy( void );
+
+int wm_cd_status( void );
+
+int wm_cd_getcurtrack( void );
+int wm_cd_getcurtracklen( void );
+int wm_cd_getcountoftracks( void );
+
+int wm_cd_gettracklen( int track );
+
+int wm_cd_play( int start, int pos, int end );
+int wm_cd_play_chunk( int start, int end, int realstart );
+int wm_cd_play_from_pos( int pos );
+int wm_cd_pause( void );
+int wm_cd_stop( void );
+int wm_cd_eject( void );
+int wm_cd_closetray( void );
+
+int wm_find_trkind( int, int, int );
+
+/*
+ * for vaild values see wm_helpers.h
+ */
+int wm_cd_set_verbosity( int );
+
+const char * wm_drive_vendor( void );
+const char * wm_drive_model( void );
+const char * wm_drive_revision( void );
+const char * wm_drive_device( void );
+
+/*
+ * volume is valid WM_VOLUME_MUTE <= vol <= WM_VOLUME_MAXIMAL,
+ * balance is valid WM_BALANCE_ALL_LEFTS <= balance <= WM_BALANCE_ALL_RIGHTS
+ */
+int wm_cd_volume( int volume, int balance);
+
+/*
+ * please notice, that more OSs don't allow to read balance and volume
+ * in this case you get -1 for volume and WM_BALANCE_SYMMETRED for balance
+ */
+int wm_cd_getvolume( void );
+int wm_cd_getbalance( void );
+
+#endif /* WM_CDROM_H */
diff --git a/kscd/libwm/include/wm_cdtext.h b/kscd/libwm/include/wm_cdtext.h
new file mode 100644
index 00000000..e843ee95
--- /dev/null
+++ b/kscd/libwm/include/wm_cdtext.h
@@ -0,0 +1,105 @@
+#ifndef WM_CDTEXT_H
+#define WM_CDTEXT_H
+
+/***************************************************************************
+ wm_cdtext.h - description
+ -------------------
+ begin : Mon Feb 12 2001
+ copyright : (C) 2001 by Alex Kern
+ email : alex.kern@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+/*
+ * cdtext base structure and defines
+ */
+
+#define MAX_LENGHT_OF_CDTEXT_STRING 162 /* max 160 bytes + 2 * 0x00 by UNICODES */
+#define DATAFIELD_LENGHT_IN_PACK 12
+#define MAX_LANGUAGE_BLOCKS 8
+
+struct cdtext_pack_data_header {
+ unsigned char header_field_id1_typ_of_pack;
+ unsigned char header_field_id2_tracknumber;
+ unsigned char header_field_id3_sequence;
+ unsigned char header_field_id4_block_no;
+ unsigned char text_data_field[DATAFIELD_LENGHT_IN_PACK];
+ unsigned char crc_byte1;
+ unsigned char crc_byte2;
+};
+
+typedef unsigned char cdtext_string[MAX_LENGHT_OF_CDTEXT_STRING];
+
+/* meke it more generic
+ it can be up to 8 blocks with different encoding */
+
+struct cdtext_info_block {
+ /* management */
+ unsigned char block_code;
+ unsigned char block_unicode; /* 0 - single chars, 1 - doublebytes */
+ unsigned char block_encoding; /* orange book -? */
+ cdtext_string* block_encoding_text;
+
+ /* variable part of cdtext */
+ cdtext_string* name;
+ cdtext_string* performer;
+ cdtext_string* songwriter;
+ cdtext_string* composer;
+ cdtext_string* arranger;
+ cdtext_string* message;
+ cdtext_string* UPC_EAN_ISRC_code;
+
+ /* fix part of cdtext */
+ unsigned char binary_disc_identification_info[DATAFIELD_LENGHT_IN_PACK];
+ unsigned char binary_genreidentification_info[DATAFIELD_LENGHT_IN_PACK];
+ unsigned char binary_size_information[DATAFIELD_LENGHT_IN_PACK];
+};
+
+struct cdtext_info {
+ /* somethimes i get hunderts of bytes, without anyone valid pack
+ my CDU-561 for example */
+ int count_of_entries; /* one more because album need one too */
+ int count_of_valid_packs;
+ int count_of_invalid_packs;
+ int valid;
+
+ struct cdtext_info_block *blocks[MAX_LANGUAGE_BLOCKS];
+};
+
+#ifndef IGNORE_FEATURE_LIST
+
+struct feature_list_header {
+ unsigned char lenght_msb;
+ unsigned char lenght_1sb;
+ unsigned char lenght_2sb;
+ unsigned char lenght_lsb;
+ unsigned char reserved1;
+ unsigned char reserved2;
+ unsigned char profile_msb;
+ unsigned char profile_lsb;
+};
+
+struct feature_descriptor_cdread {
+ unsigned char feature_code_msb;
+ unsigned char feature_code_lsb;
+ unsigned char settings;
+ unsigned char add_lenght;
+ unsigned char add_settings;
+ unsigned char reserved1;
+ unsigned char reserved2;
+ unsigned char reserved3;
+};
+
+#endif /* IGNORE_FEATURE_LIST */
+
+struct cdtext_info* wm_cd_get_cdtext( void );
+
+#endif /* WM_CDTEXT_H */
diff --git a/kscd/libwm/include/wm_config.h b/kscd/libwm/include/wm_config.h
new file mode 100644
index 00000000..b04027f4
--- /dev/null
+++ b/kscd/libwm/include/wm_config.h
@@ -0,0 +1,377 @@
+#ifndef WM_CONFIG_H
+#define WM_CONFIG_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ **********************************************************************
+ *
+ * This file consists of several parts. First, there's a generic,
+ * platform independent part. Set needed options there.
+ * The following parts are platform dependent. You may search for the
+ * names listed below and then set your OS specific options there.
+ * Don't be surprised, if there are no options for your OS. They aren't
+ * needed in any case.
+ *
+ * The default values should produce a functional WorkMan on every
+ * platform.
+ *
+ *********************
+ * Current platforms:
+ *********************
+ * BSD386
+ * FreeBSD
+ * HP-UX
+ * Irix (SGI)
+ * Linux
+ * News (Sony NewsOS)
+ * OpenBSD
+ * OSF1
+ * Sun (SunOS/Solaris, Sparc or x86)
+ * SVR4
+ * Ultrix
+ * AIX
+ *
+ * The above order corresponds with the order of the platform specific
+ * options below.
+ */
+
+#include <config.h>
+/******************************************************************
+ * generic options
+ ******************************************************************
+ ** ** *** **** ** **** ** * ** ** ** * **
+ * * * *** **** * * *** * ** ** *** * * * * * * **
+ * * *** **** ** *** ** ** ** * * ** * * **
+ * * * *** **** **** *** * ** ** *** * * * * *** **
+ * * * * ** **** * * ** ** **** ** * * *** **
+ ******************************************************************/
+
+/*
+ * This option is obvious. But please do not forget the original
+ * WorkMan version string if you need support.
+ */
+
+#define WORKMAN_NAME "LibWorkMan"
+#define WORKMAN_VERSION "1.4.0"
+
+/*
+ * If your CD-ROM drive closes its tray if the device is opened, then
+ * the next define can make WorkMans "Eject" button an "open/close"
+ * button. If it disturbs you, just comment it out.
+ *
+ * ### this is preliminary. It may have no effect for you ###
+ */
+#define CAN_CLOSE 1
+
+/*
+ * Define the following if you want the balance slider to
+ * decrease one channel's volume while increasing the other's
+ */
+/* #define SYMETRIC_BALANCE 1 */
+
+
+/*
+ * Define this if you want CDDA support. Supported systems are:
+ *
+ * - Solaris (2.4 or newer)
+ * --> Linux is on the way. Don't define it now. It would lead to errors only.
+ */
+/*#define BUILD_CDDA 1*/
+
+
+
+/******************************************************************
+ * BSD386
+ ******************************************************************
+ *** **** *** ******* ** **** **** ************
+ *** ** ** ****** * ***** ****** ** ** ** ***************
+ *** ****** **** ** *** ***** ***** *** ************
+ *** ** ****** ** * *** ******** ** ** ** ** ***********
+ *** **** *** *** ****** **** **** ************
+ ******************************************************************/
+#if defined(__bsdi__) || defined(__bsdi)
+
+/*
+ * This lets you use the SoundBlaster Mixer on BSD/386
+ */
+#define SOUNDBLASTER 1
+
+#define DEFAULT_CD_DEVICE "/dev/rsr2c"
+
+#endif /* __bsdi__ (BSD/386) */
+
+/******************************************************************
+ * FreeBSD
+ ******************************************************************
+ *** ** *** ** ** **** *** ***********
+ *** ****** ** ** ****** ****** ** ** ****** * **********
+ *** **** *** **** **** ****** **** ** *********
+ *** ****** ** ** ****** ****** ** ****** ** * **********
+ *** ****** ** ** ** ** **** *** ***********
+ ******************************************************************/
+#if defined(__FreeBSD__) || defined(__FreeBSD)
+
+#if __FreeBSD_version >= 500100
+#define DEFAULT_CD_DEVICE "/dev/acd0"
+#else
+#define DEFAULT_CD_DEVICE "/dev/acd0c"
+#endif
+
+#endif /* freebsd */
+
+/* DragonFly */
+#if defined(__DragonFly__)
+#define DEFAULT_CD_DEVICE "/dev/acd0c"
+#endif
+
+/******************************************************************
+ * NetBSD
+ ******************************************************************
+ *** *** ** ** ** **** *** *****************
+ *** ** ** ******** **** ** ** ****** * ****************
+ *** * * ** ****** **** ****** **** ** ***************
+ *** ** ** ******** **** ** ****** ** * ****************
+ *** *** ** **** **** **** *** *****************
+ ******************************************************************/
+#if defined(__NetBSD__) || defined(__NetBSD)
+
+#if defined(__i386__)
+ #define DEFAULT_CD_DEVICE "/dev/rcd0d"
+#else
+ #define DEFAULT_CD_DEVICE "/dev/rcd0c"
+#endif
+
+#endif /* netbsd */
+
+/******************************************************************
+ * HP-UX
+ ******************************************************************
+ *** ** ** ********* ** ** ** ***************************
+ *** ** ** ** ******** ** *** ****************************
+ *** ** *** ** ** **** *****************************
+ *** ** ** ************ ** *** ****************************
+ *** ** ** ************* *** ** ***************************
+ ******************************************************************/
+#if defined(hpux) || defined (__hpux)
+
+#define DEFAULT_CD_DEVICE "/dev/rscsi"
+
+#endif /* hpux */
+
+/******************************************************************
+ * Irix
+ ******************************************************************
+ *** ** *** ** ** *********************************
+ ***** **** ** **** ***** **********************************
+ ***** **** ***** ****** ***********************************
+ ***** **** ** **** ***** **********************************
+ *** ** ** ** ** ** *********************************
+ ******************************************************************/
+#if defined(sgi) || defined(__sgi)
+
+#define DEFAULT_CD_DEVICE "/dev/scsi/sc0d6l0"
+
+#endif /* sgi IRIX */
+
+/******************************************************************
+ * Linux
+ ******************************************************************
+ *** ****** ** *** ** ** ** ** ***********************
+ *** ******** **** ** ** ** *** ************************
+ *** ******** **** * * ** ** **** *************************
+ *** ******** **** ** ** ** *** ************************
+ *** ** ** *** *** *** ** ***********************
+ ******************************************************************/
+#if defined(__linux__)
+
+/*
+ * Uncomment the following line to have WorkMan send SCSI commands
+ * directly to the CD-ROM drive. If you have a SCSI drive you
+ * probably want this, but it will cause WorkMan to not work on IDE
+ * drives.
+ */
+/*#define LINUX_SCSI_PASSTHROUGH 1*/
+
+/*
+ * Which device should be opened by WorkMan at default?
+ */
+#define DEFAULT_CD_DEVICE "/dev/cdrom"
+
+/*
+ * Uncomment the following if you use the sbpcd or mcdx device driver.
+ * It shouldn't hurt if you use it on other devices. It'll be nice to
+ * hear from non-sbpcd (or mcdx) users if this is right.
+ */
+/*#define SBPCD_HACK 1*/
+
+/*
+ * Linux Soundcard support
+ * Disabled by default, because some people rely on it
+ */
+/* #define OSS_SUPPORT 1 */
+
+/*
+ * This has nothing to do with the above.
+ */
+
+/* #define CURVED_VOLUME */
+
+/*
+ * Uncomment the following if you want to try out a better responding
+ * WorkMan, especially with IDE drives. This may work with non-IDE
+ * drives as well. But it may be possible, that it doesn't work at all.
+ * If your drive/driver combination cannot handle the faster access,
+ * the driver will usually hang and you have to reboot your machine.
+ */
+/* #define FAST_IDE 1 */
+
+/*
+ * There are two alternative ways of checking a device containing a
+ * mounted filesystem. Define BSD_MOUNTTEST for the test using
+ * getmntent(). Undefine it for using the SVR4 ustat().
+ * I built in the choice, because it's not clear which method should
+ * be used in Linux. The ustat manpage tells us since 1995, that
+ * fstat() should be used, but I'm too dumb to do so.
+ */
+
+#define BSD_MOUNTTEST
+
+#endif /* __linux */
+
+/******************************************************************
+ * Sony NewsOS
+ ******************************************************************
+ *** *** ** ** ***** *** *****************************
+ *** ** ** ****** ***** ** ********************************
+ *** * * ** **** ** ** **** ******************************
+ *** ** ** ****** * * ****** ****************************
+ *** *** ** ** * *** *****************************
+ ******************************************************************/
+#if defined(__sony_news) || defined(sony_news)
+
+#define DEFAULT_CD_DEVICE "/dev/rsd/b0i6u0p2\0"
+
+#endif
+
+/******************************************************************
+ * OSF1
+ ******************************************************************
+ **** **** *** *** *** *******************************
+ *** ** ** ****** ****** ** *******************************
+ *** ** **** **** *** ***** *******************************
+ *** ** ****** ** **** ****** *******************************
+ **** **** *** *** ***** *****************************
+ ******************************************************************/
+#if defined(__osf__) || defined(__osf)
+
+/* not needed here, it's figured out by plat_osf1.c */
+/* #define DEFAULT_CD_DEVICE "/dev/rcdrom/cd0" */
+
+#endif
+
+/******************************************************************
+ * SunOS/Solaris
+ ******************************************************************
+ **** *** ** ** *** ***************************************
+ *** ****** ** ** ** ***************************************
+ ***** **** ** ** * * ***************************************
+ ******* ** ** ** ** ***************************************
+ **** **** *** *** ***************************************
+ ******************************************************************/
+#if defined(sun) || defined(__sun)
+
+/*
+ * Define the following for Solaris 2.x
+ * If you don't want WorkMan to try to activate the SPARCstation 5
+ * internal audio input so you get sound from the workstation, comment
+ * out the CODEC define.
+ */
+
+#define SYSV 1
+#define CODEC 1
+#define DEFAULT_CD_DEVICE "/vol/dev/aliases/cdrom0"
+
+/*
+ * set the following to "SUNW,CS4231" for Sun and to "SUNW,sb16"
+ * for PC (with SoundBlaster 16) running Solaris x86
+ * (only important if you define CODEC above)
+ */
+#define SUN_AUD_DEV "SUNW,CS4231"
+/*#define SUN_AUD_DEV "SUNW,sbpro"*/
+
+
+#endif
+
+/******************************************************************
+ * SVR4
+ ******************************************************************
+ **** *** **** ** *** * ********************************
+ *** ****** **** ** ** ** * ********************************
+ ***** ***** ** *** *** *******************************
+ ******* *** ** *** ** ***** ********************************
+ **** ***** **** ** ***** ********************************
+ ******************************************************************/
+#if (defined(SVR4) || defined(__SVR4)) && !defined(sun) && !defined(__sun) && !defined(sony_news) && !defined(__sony_news)
+
+#define DEFAULT_CD_DEVICE "/dev/rcdrom/cd0"
+
+#endif
+
+/******************************************************************
+ * Ultrix
+ ******************************************************************
+ *** ** ** ***** ** *** ** ** ******************
+ *** ** ** ******* **** ** **** ***** *******************
+ *** ** ** ******* **** ***** ****** ********************
+ *** ** ** ******* **** ** **** ***** *******************
+ **** *** *** **** ** ** ** ** ******************
+ ******************************************************************/
+#if defined(ultrix) || defined(__ultrix)
+
+#endif
+
+/******************************************************************
+ * IBM AIX
+ ******************************************************************
+ **** *** ** ** *****************************************
+ *** ** **** ***** ******************************************
+ *** **** ****** *******************************************
+ *** ** **** ***** ******************************************
+ *** ** ** ** ** *****************************************
+ ******************************************************************/
+#if defined(AIXV3) || defined(__AIXV3)
+
+#define DEFAULT_CD_DEVICE "/dev/cd0"
+
+#endif /* IBM AIX */
+
+/******************************************************************/
+#endif /* WM_CONFIG_H */
+
+
+
+
+
+
+
+
diff --git a/kscd/libwm/include/wm_database.h b/kscd/libwm/include/wm_database.h
new file mode 100644
index 00000000..401f12bc
--- /dev/null
+++ b/kscd/libwm/include/wm_database.h
@@ -0,0 +1,42 @@
+#ifndef WM_DATABASE_H
+#define WM_DATABASE_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Prototypes for WorkMan database
+ *
+ * This is just one more step to a more modular and understandable code.
+ */
+
+
+#define WM_DB_SAVE_ERROR 1
+#define WM_DB_SAVE_DISABLED 2
+
+int wm_db_get_playnew( void );
+void split_workmandb( void );
+int save( void );
+void load( void );
+void load_settings( void );
+
+
+#endif /* WM_DATABASE_H */
diff --git a/kscd/libwm/include/wm_helpers.h b/kscd/libwm/include/wm_helpers.h
new file mode 100644
index 00000000..ad98a365
--- /dev/null
+++ b/kscd/libwm/include/wm_helpers.h
@@ -0,0 +1,109 @@
+#ifndef WM_HELPERS_H
+#define WM_HELPERS_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Here to be found: Prototypes. Including variable names to be easier
+ * to read.
+ * This is just one more step to a more modular and understandable code.
+ *
+ */
+
+/*
+ * LibWorkMan message levels. I'm not sure how to call them all and which
+ * use they should fulfill. This is not very urgent now, because there
+ * aren't many messages in LibWorkMan now.
+ */
+#define WM_MSG_LEVEL_NONE 0 /**/
+#define WM_MSG_LEVEL_ERROR 1 /**/
+#define WM_MSG_LEVEL_TWO 2
+#define WM_MSG_LEVEL_THREE 3
+#define WM_MSG_LEVEL_FOUR 4
+#define WM_MSG_LEVEL_INFO 5 /**/
+#define WM_MSG_LEVEL_SIX 6
+#define WM_MSG_LEVEL_VERB 7 /**/
+#define WM_MSG_LEVEL_EIGHT 8
+#define WM_MSG_LEVEL_DEBUG 9 /**/
+
+/*
+ * Message classes. This is somehow a definition of
+ * the message's source.
+ */
+
+#define WM_MSG_CLASS_PLATFORM 0x010
+#define WM_MSG_CLASS_SCSI 0x020
+#define WM_MSG_CLASS_CDROM 0x040
+#define WM_MSG_CLASS_DB 0x080
+#define WM_MSG_CLASS_MISC 0x100
+
+#define WM_MSG_CLASS_ALL 0xff0
+
+extern int wm_lib_verbosity;
+
+/*
+ * I did not know any better place...
+ */
+#ifdef DEBUG
+#define CHECKPOINT(t) fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, t );
+#else
+#define CHECKPOINT(t)
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifdef __linux__
+#include <signal.h>
+/* Linux doesn't have a SIGEMT */
+#if !defined( SIGEMT )
+# define SIGEMT SIGUNUSED
+#endif
+#endif /* linux */
+
+void freeup( char **x );
+void wm_strmcat( char **t, const char *s);
+void wm_strmcpy( char **t, const char *s );
+char * wm_strdup( char *s );
+/* Somebody's version query unsatisfied? */
+int wm_libver_major( void ); /* return major internal version number */
+int wm_libver_minor( void ); /* return minor internal version number */
+int wm_libver_pl( void ); /* return internal patchlevel number */
+char * wm_libver_name( void ); /* return internal name (LibWorkMan) */
+char * wm_libver_number( void ); /* returns string: "<major>.<minor>.<pl>" */
+char * wm_libver_string( void ); /* returns string: "<name> <number>" */
+char * wm_libver_date( void ); /* returns string: date of compilation */
+void wm_lib_set_verbosity( int level ); /* set verbosity level */
+int wm_lib_get_verbosity( void ); /* get verbosity level */
+void wm_lib_message( unsigned int level, const char *format, ... )
+#ifdef __GNUC__
+ __attribute__ ((format(printf,2,3)))
+#endif
+ ; /* put out a message on stderr */
+int wm_susleep( int usec );
+
+#endif /* WM_HELPERS_H */
diff --git a/kscd/libwm/include/wm_index.h b/kscd/libwm/include/wm_index.h
new file mode 100644
index 00000000..7c348b75
--- /dev/null
+++ b/kscd/libwm/include/wm_index.h
@@ -0,0 +1,36 @@
+#ifndef WM_INDEX_H
+#define WM_INDEX_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Prototypes for wm_index.c
+ *
+ * This is just one more step to a more modular and understandable code.
+ */
+
+int idx_find_entry( char *file, int ntracks, int *tracks,
+ int len, int fuzz, unsigned long *pos );
+int idx_delete_entry(char *file, int track, int fuzz, unsigned long pos );
+int idx_write_entry( char *file, int track, unsigned long pos );
+
+#endif /* WM_INDEX_H */
diff --git a/kscd/libwm/include/wm_platform.h b/kscd/libwm/include/wm_platform.h
new file mode 100644
index 00000000..edce8119
--- /dev/null
+++ b/kscd/libwm/include/wm_platform.h
@@ -0,0 +1,51 @@
+#ifndef WM_PLATFORM_H
+#define WM_PLATFORM_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * The platform interface
+ *
+ * This is just one more step to a more modular and understandable code.
+ */
+
+#include "wm_struct.h"
+
+int wmcd_open( struct wm_drive *d );
+int wmcd_reopen( struct wm_drive *d );
+
+/*
+ * void keep_cd_open( void );
+ */
+int wm_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen,
+ void *retbuf, int retbuflen, int getreply );
+
+/****************************************
+ *
+ * The drive prototypes.
+ *
+ */
+extern struct wm_drive_proto generic_proto;
+extern struct wm_drive_proto sony_proto;
+extern struct wm_drive_proto toshiba_proto;
+
+#endif /* WM_PLATFORM_H */
diff --git a/kscd/libwm/include/wm_scsi.h b/kscd/libwm/include/wm_scsi.h
new file mode 100644
index 00000000..0acc62ef
--- /dev/null
+++ b/kscd/libwm/include/wm_scsi.h
@@ -0,0 +1,50 @@
+#ifndef WM_SCSI_H
+#define WM_SCSI_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * SCSI prototypes (scsi.c)
+ *
+ * This is just one more step to a more modular and understandable code.
+ */
+
+#include "wm_struct.h"
+
+#define WM_ERR_SCSI_INQUIRY_FAILED -1
+
+int wm_scsi_mode_sense( struct wm_drive *d, unsigned char page,
+ unsigned char *buf );
+int sendscsi( struct wm_drive *d, void *buf,
+ unsigned int len, int dir,
+ unsigned char a0, unsigned char a1,
+ unsigned char a2, unsigned char a3,
+ unsigned char a4, unsigned char a5,
+ unsigned char a6, unsigned char a7,
+ unsigned char a8, unsigned char a9,
+ unsigned char a10, unsigned char a11 );
+ int wm_scsi_get_drive_type( struct wm_drive *d, char *vendor,
+ char *model, char *rev );
+int wm_scsi_get_cdtext( struct wm_drive *d,
+ unsigned char **pp_buffer, int *p_buffer_length );
+
+#endif /* WM_SCSI_H */
diff --git a/kscd/libwm/include/wm_struct.h b/kscd/libwm/include/wm_struct.h
new file mode 100644
index 00000000..790de3b0
--- /dev/null
+++ b/kscd/libwm/include/wm_struct.h
@@ -0,0 +1,198 @@
+#ifndef WM_STRUCT_H
+#define WM_STRUCT_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+
+/*
+ * Structure for a single track. This is pretty much self-explanatory --
+ * one of these exists for each track on the current CD.
+ */
+struct wm_trackinfo
+{
+ char *songname; /* Name of song, dynamically allocated */
+ char *otherdb; /* Unrecognized info for this track */
+ char *otherrc;
+ int length; /* Length of track in seconds or Kbytes */
+ int start; /* Starting position (f+s*75+m*60*75) */
+ int volume; /* Per-track volume (1-32, 0 to disable) */
+ int track; /* Physical track number */
+ int section; /* Section number (0 if track not split) */
+ int contd; /* Flag: continuation of previous track */
+ int avoid; /* Flag: don't play this track. */
+ int data; /* Flag: data track */
+};
+
+/*
+ * Structure for internal playlist management. The internal playlist is
+ * simply the list of track ranges that are being played currently. This
+ * is built whenever the CD starts playing; it's used in normal and shuffle
+ * modes as well as playlist mode.
+ *
+ * The "starttime" element represents how much time has elapsed by the time
+ * we get to this entry. For instance, if the list begins with a 5-minute
+ * track and a 3-minute track, the third entry would have a starttime of 8
+ * minutes. This is used so that the elapsed play time can be displayed
+ * even in shuffle or playlist modes.
+ *
+ * The last member of the list has a start track of 0, and its starttime is
+ * the total playing time of the playlist (which will usually be overestimated,
+ * since we don't play leadouts in some cases.)
+ */
+struct wm_play
+{
+ int start; /* Start track, or 0 if end of list */
+ int end; /* last track plus 1 */
+ int starttime; /* Number of seconds elapsed previously */
+};
+
+/*
+ * Structure for playlists (as seen by the user.) This is simply a name
+ * followed by a zero-terminated list of track numbers to play. The list
+ * is terminated by a NULL name.
+ */
+struct wm_playlist
+{
+ char *name; /* Name of this playlist */
+ int *list; /* List of tracks */
+};
+
+struct wm_cdinfo
+{
+ char artist[84]; /* Artist's name */
+ char cdname[84]; /* Disc's name */
+ int ntracks; /* Number of tracks on the disc */
+ int curtrack;
+ int curtracklen;
+ int length; /* Total running time in seconds */
+ int autoplay; /* Start playing CD immediately */
+ int playmode; /* How to play the CD */
+ int volume; /* Default volume (1-32, 0 for none) */
+ struct wm_trackinfo *trk; /* struct wm_trackinfo[ntracks] */
+ struct wm_playlist *lists; /* User-specified playlists */
+ char *whichdb; /* Which database is this entry from? */
+ char *otherdb; /* Unrecognized lines from this entry */
+ char *otherrc;
+ char *user; /* Name of originating user */
+ unsigned int cddbid; /* CDDB-ID of the current disc */
+ struct cdinfo *next; /* For browsers, etc. */
+};
+
+/* The global variable "cd" points to the struct for the CD that's playing. */
+extern struct wm_cdinfo *cd;
+
+extern struct wm_playlist *new_playlist();
+
+#define WM_STR_GENVENDOR "Generic"
+#define WM_STR_GENMODEL "drive"
+#define WM_STR_GENREV "type"
+
+
+/*
+ * Drive descriptor structure. Used for access to low-level routines.
+ */
+struct wm_drive_proto
+{
+ int (*gen_init)();
+ int (*gen_close)();
+ int (*gen_get_trackcount)();
+ int (*gen_get_cdlen)();
+ int (*gen_get_trackinfo)();
+ int (*gen_get_drive_status)();
+ int (*gen_get_volume)();
+ int (*gen_set_volume)();
+ int (*gen_pause)();
+ int (*gen_resume)();
+ int (*gen_stop)();
+ int (*gen_play)();
+ int (*gen_eject)();
+ int (*gen_closetray)();
+ int (*gen_get_cdtext)();
+};
+
+struct wm_drive
+{
+ int cdda; /* cdda 1, cdin 0 */
+ const char *cd_device;
+ char *soundsystem;
+ char *sounddevice;
+ char *ctldevice;
+ int fd; /* File descriptor, if used by platform */
+ int cdda_slave; /* File descriptor for CDDA */
+
+ char *vendor; /* Vendor name */
+ char *model; /* Drive model */
+ char *revision; /* Revision of the drive */
+ void *aux; /* Pointer to optional platform-specific info */
+ void *daux; /* Pointer to optional drive-specific info */
+
+ struct wm_drive_proto *proto;
+};
+
+/*
+ * Structure for information of the usage of cddb.
+ */
+struct wm_cddb {
+ int protocol; /* 0-off, 1-cddbp, 2-http, 3-htproxy */
+ char cddb_server[84]; /* host.domain.name:port */
+ char mail_adress[84]; /* user@domain.name */
+ char path_to_cgi[84]; /* (/)path/to/cddb.cgi */
+ char proxy_server[84]; /* host.domain.name:port */
+};
+extern struct wm_cddb cddb;
+
+
+/*
+ * Each platform has to define generic functions, so may as well declare
+ * them all here to save space.
+ * These functions should never be seen outside libworkman. So I don't care
+ * about the wm_ naming convention here.
+ */
+int gen_init( struct wm_drive *d );
+int gen_close( struct wm_drive *d );
+int gen_get_trackcount(struct wm_drive *d, int *tracks);
+int gen_get_cdlen(struct wm_drive *d, int *frames);
+int gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe);
+int gen_get_drive_status( struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *ind );
+int gen_set_volume( struct wm_drive *d, int left, int right );
+int gen_get_volume( struct wm_drive *d, int *left, int *right );
+int gen_pause(struct wm_drive *d);
+int gen_resume(struct wm_drive *d);
+int gen_stop(struct wm_drive *d);
+int gen_play(struct wm_drive *d, int start, int end, int realstart);
+int gen_eject(struct wm_drive *d);
+int gen_closetray(struct wm_drive *d);
+int gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght);
+
+int find_drive_struct(const char *vendor, const char *model, const char *rev);
+
+
+struct cdtext_info* get_glob_cdtext(struct wm_drive*, int);
+void free_cdtext(void);
+const char* gen_status(int);
+
+#endif /* WM_STRUCT_H */
diff --git a/kscd/libwm/include/wm_version.h b/kscd/libwm/include/wm_version.h
new file mode 100644
index 00000000..82e500c5
--- /dev/null
+++ b/kscd/libwm/include/wm_version.h
@@ -0,0 +1,35 @@
+#ifndef WM_VERSION_H
+#define WM_VERSION_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Version information
+ *
+ */
+
+#define WM_LIBVER_MAJOR 1
+#define WM_LIBVER_MINOR 4
+#define WM_LIBVER_PL 3
+#define WM_LIBVER_NAME "LibWorkMan"
+
+#endif /* WM_VERSION_H */
diff --git a/kscd/libwm/include/workman.h b/kscd/libwm/include/workman.h
new file mode 100644
index 00000000..e7c3807d
--- /dev/null
+++ b/kscd/libwm/include/workman.h
@@ -0,0 +1,52 @@
+#ifndef WORKMAN_H
+#define WORKMAN_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * all-in-one libworkman include file.
+ *
+ */
+
+
+/*
+ * wm_config should always be included first
+ */
+#include "wm_config.h"
+
+#include "workman_defs.h"
+#ifdef BUILD_CDDA
+#include "wm_cdda.h"
+#endif
+#include "wm_cddb.h"
+#include "wm_cdinfo.h"
+#include "wm_cdrom.h"
+#include "wm_database.h"
+#include "wm_helpers.h"
+#include "wm_index.h"
+#include "wm_platform.h"
+#include "wm_scsi.h"
+#include "wm_struct.h"
+#include "wm_cdtext.h"
+
+#endif /* WORKMAN_H */
+
diff --git a/kscd/libwm/include/workman_defs.h b/kscd/libwm/include/workman_defs.h
new file mode 100644
index 00000000..9da47f8f
--- /dev/null
+++ b/kscd/libwm/include/workman_defs.h
@@ -0,0 +1,30 @@
+#ifndef WORKMAN_DEFS_H
+#define WORKMAN_DEFS_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player program
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * #defined CONSTANTS
+ *
+ * Too bad this file seems to be so empty...
+ *
+ */
+
+#include "wm_version.h"
+
+#endif /* WORKMAN_DEFS_H */
diff --git a/kscd/libwm/index.c b/kscd/libwm/index.c
new file mode 100644
index 00000000..e15312b7
--- /dev/null
+++ b/kscd/libwm/index.c
@@ -0,0 +1,383 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Maintain an external index file for a WorkMan CD database.
+ * Uses the Berkeley libdb library, available from ftp.cs.berkeley.edu.
+ */
+
+#ifdef LIBDB
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <db.h>
+#include <fcntl.h>
+#include <string.h>
+#include <netinet/in.h> /* for htonl() */
+#include "include/wm_config.h"
+#include "include/wm_index.h"
+
+extern int suppress_locking;
+
+/*
+ * idx_find_entry()
+ *
+ * Find an entry in the index file.
+ *
+ * Input:
+ * file Name of database file (text version).
+ * ntracks Number of tracks on the CD we're looking for.
+ * tracks Array of track start times.
+ * len CD length in frames.
+ * fuzz Fuzz factor (tolerance value).
+ * pos Pointer to return value.
+ *
+ * Output:
+ * 1 No matching record found.
+ * 0 Record found; *pos contains offset into text file.
+ * -1 Index file out of date or inaccessible, or another error.
+ */
+int
+idx_find_entry( char *file, int ntracks, int *tracks,
+ int len, int fuzz, unsigned long *pos )
+{
+ unsigned long dbpos;
+ char *indexname = NULL, keyval[8];
+ int c;
+ FILE *text;
+ DB *index;
+ DBT key, data;
+ BTREEINFO bti;
+
+ /*
+ * First, see if the text file is accessible. Lock it if so.
+ */
+ text = fopen(file, "r");
+ if (text == NULL)
+ return (-1);
+ if ((c = getc(text)) == EOF)
+ {
+ fclose(text);
+ return (-1);
+ }
+ if (! suppress_locking)
+ if (lockit(fileno(text), F_RDLCK))
+ {
+ fclose(text);
+ return (-1);
+ }
+
+ /*
+ * Open the index file.
+ */
+ indexname = malloc(strlen(file) + sizeof(".ind"));
+ if (indexname == NULL)
+ {
+ fclose(text);
+ return (-1);
+ }
+ strcpy(indexname, file);
+ strcat(indexname, ".ind");
+ bti.flags = 0;
+ bti.cachesize = 0;
+ bti.minkeypage = bti.maxkeypage = 0;
+ bti.psize = bti.lorder = 0;
+ bti.compare = NULL;
+ bti.prefix = NULL;
+ index = dbopen(indexname, O_RDONLY, 0666, DB_BTREE, &bti);
+ free(indexname);
+ if (index == NULL)
+ {
+ fclose(text);
+ return (-1);
+ }
+
+ /*
+ * Search for the first matching entry.
+ */
+ sprintf(keyval, "%07d", tracks[ntracks - 1] - fuzz);
+ key.data = keyval;
+ key.size = 7;
+ if (c = (index->seq)(index, &key, &data, R_CURSOR))
+ {
+ (index->close)(index);
+ fclose(text);
+ return (c);
+ }
+
+ /*
+ * Now loop through all the possible matches, collecting them into
+ * memory.
+ */
+ do {
+ char tracksline[750], *s;
+ int i, val;
+
+ /* Hit the end of the valid entries? */
+ sscanf(key.data, "%d", &val);
+ if (val > tracks[ntracks - 1] + fuzz)
+ break;
+
+ dbpos = ntohl(*((unsigned long *) data.data));
+ if (fseek(text, dbpos, 0))
+ break;
+
+ fgets(tracksline, sizeof(tracksline), text);
+ if (strncmp(tracksline, "tracks ", 7))
+ break;
+ (void) strtok(tracksline, " \t");
+
+ /* Got a valid tracks line. See if it matches the CD. */
+ s = strtok(NULL, " \t");
+ if (s == NULL)
+ break;
+ if (atoi(s) != ntracks)
+ continue;
+
+ for (i = 0; i < ntracks; i++)
+ {
+ s = strtok(NULL, " \t");
+ if (s == NULL)
+ break;
+ val = atoi(s);
+ if (val + fuzz < tracks[i] || val - fuzz > tracks[i])
+ break;
+ }
+ if (i != ntracks)
+ continue;
+
+ s = strtok(NULL, " \t");
+ if (s == NULL)
+ continue;
+ val = atoi(s);
+ if (val + fuzz / 75 < len / 75 || val + fuzz / 75 > len / 75)
+ continue;
+
+ /* XXX - add to sorted list! */
+ *pos = dbpos;
+ (index->close)(index);
+ fclose(text);
+ return (0);
+ } while ((c = (index->seq)(index, &key, &data, R_NEXT)) == 0);
+
+ if (c == 0)
+ {
+ /* An error. */
+ (index->close)(index);
+ fclose(text);
+ return (-1);
+ }
+
+ (index->close)(index);
+ fclose(text);
+ return (1);
+}
+
+/*
+ * idx_delete_entry()
+ *
+ * Delete an entry from the index file.
+ *
+ * Input:
+ * file Name of database file (text version).
+ * track Last track's start time (database key).
+ * fuzz Fuzz factor (tolerance value).
+ * pos Position of CD in database file.
+ *
+ * Output:
+ * 1 No matching record found.
+ * 0 Record deleted.
+ * -1 Index file out of date or inaccessible, or another error.
+ *
+ * Note: it is the caller's responsibility to do locking, as it's assumed
+ * that this operation will accompany a modification of the main database
+ * file and will need to be atomic with that modification.
+ */
+int
+idx_delete_entry(char *file, int track, int fuzz, unsigned long pos )
+{
+ unsigned long dbpos;
+ char *indexname = NULL, keyval[8];
+ int c, status;
+ DB *index;
+ DBT key, data;
+ BTREEINFO bti;
+
+ /*
+ * Open the index file.
+ */
+ indexname = malloc(strlen(file) + sizeof(".ind"));
+ if (indexname == NULL)
+ return (-1);
+
+ strcpy(indexname, file);
+ strcat(indexname, ".ind");
+
+ bti.flags = 0;
+ bti.cachesize = 0;
+ bti.minkeypage = bti.maxkeypage = 0;
+ bti.psize = bti.lorder = 0;
+ bti.compare = NULL;
+ bti.prefix = NULL;
+ index = dbopen(indexname, O_RDWR, 0666, DB_BTREE, &bti);
+ free(indexname);
+ if (index == NULL)
+ return (-1);
+
+ /*
+ * Search for the first matching entry.
+ */
+ sprintf(keyval, "%07d", track - fuzz);
+ key.data = keyval;
+ key.size = 7;
+ if (c = (index->seq)(index, &key, &data, R_CURSOR))
+ {
+ /*
+ * Nothing matched!
+ */
+ (index->close)(index);
+ return (c);
+ }
+
+ /*
+ * Look for the entry the user wants to delete.
+ */
+ do {
+ int val;
+
+ /* Hit the end of the valid entries? */
+ sscanf(key.data, "%d", &val);
+ if (val > track + fuzz)
+ break;
+
+ /* Is this the entry we want? */
+ if (pos == ntohl(*((unsigned long *) data.data)))
+ {
+ /*
+ * Yep! Delete it.
+ */
+ status = (index->del)(index, &key, R_CURSOR);
+ (index->close)(index);
+ return (status);
+ }
+ } while ((c = (index->seq)(index, &key, &data, R_NEXT)) == 0);
+
+ if (c == 0)
+ {
+ /* An error. */
+ (index->close)(index);
+ return (-1);
+ }
+
+ (index->close)(index);
+ return (1);
+}
+
+/*
+ * idx_write_entry()
+ *
+ * Write out an index file entry.
+ *
+ * Input:
+ * file Name of database file (text version).
+ * track Start time of last track (database key).
+ * pos Position of entry in text file.
+ *
+ * Output:
+ * 0 Record written.
+ * -1 Index file inaccessible, or another error.
+ *
+ * Note: it is the caller's responsibility to do locking, as it's assumed
+ * that this operation will accompany a modification of the main database
+ * file and will need to be atomic with that modification.
+ */
+int
+idx_write_entry( char *file, int track, unsigned long pos )
+{
+ char *indexname, keyval[8];
+ int status;
+ DB *index;
+ DBT key, data;
+ BTREEINFO bti;
+
+ /*
+ * Open the index file.
+ */
+ indexname = malloc(strlen(file) + sizeof(".ind"));
+ if (indexname == NULL)
+ return (-1);
+
+ strcpy(indexname, file);
+ strcat(indexname, ".ind");
+
+ bti.flags = R_DUP;
+ bti.cachesize = 0;
+ bti.minkeypage = 0;
+ bti.maxkeypage = 0;
+ bti.psize = 0;
+ bti.lorder = 4321; /* network byte order */
+ bti.compare = NULL;
+ bti.prefix = NULL;
+ index = dbopen(indexname, O_RDWR, 0666, DB_BTREE, &bti);
+ free(indexname);
+ if (index == NULL)
+ return (-1);
+
+ /*
+ * Create a new key and value.
+ */
+ pos = htonl(pos);
+ data.data = &pos;
+ data.size = sizeof(pos);
+ key.data = keyval;
+ key.size = 7;
+
+ sprintf(keyval, "%07d", track);
+
+ status = (index->put)(index, &key, &data, 0);
+
+ (index->close)(index);
+ return (status);
+}
+
+#else /* LIBDB */
+
+int
+idx_find_entry()
+{
+ return (1); /* no record found; text file will be searched. */
+}
+
+int
+idx_delete_entry()
+{
+ return (0);
+}
+
+int
+idx_write_entry()
+{
+ return (0);
+}
+
+#endif /* LIBDB */
diff --git a/kscd/libwm/plat_aix.c b/kscd/libwm/plat_aix.c
new file mode 100644
index 00000000..93b596a1
--- /dev/null
+++ b/kscd/libwm/plat_aix.c
@@ -0,0 +1,489 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * plat_aix - AIX 4.x IDE and SCSI support 16 Dec 1998
+ *
+ * AIX 4.x Port: Erik O'Shaughnessy
+ * Original AIX IDE Code: Cloyce Spradling (xmcd libdi_d/aixioc.c )
+ *
+ * Taken from the ascd distribution.
+ *
+ */
+
+
+#if defined(AIXV3) || defined(__AIXV3)
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/cdrom.h>
+#include <sys/devinfo.h>
+#include <sys/scsi.h>
+#include <sys/scdisk.h>
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+#define LEADOUT 0xaa
+
+int min_volume = 128;
+int max_volume = 255;
+
+
+
+/* NAME: gen_init
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_init(struct wm_drive *d)
+{
+ return 0;
+} /* gen_init() */
+
+
+/* NAME: wmcd_open
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+wmcd_open(struct wm_drive *d)
+{
+ char vendor[32] = WM_STR_GENVENDOR;
+ char model[32] = WM_STR_GENMODEL;
+ char rev[32] = WM_STR_GENREV;
+
+ int fd;
+
+ if( ! d )
+ {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if(d->fd > -1) /* device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return 0;
+ }
+
+ if( d->cd_device == (char *)NULL )
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+ if( (fd = openx(d->cd_device,O_RDONLY,NULL,SC_SINGLE)) < 0 )
+ {
+ perror("openx");
+ return -6;
+ /* return 1 */
+ }
+
+ find_drive_struct(vendor, model, rev);
+
+ d->fd = fd;
+ d->init(d);
+ return 0;
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/* NAME: wm_scsi
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+wm_scsi(struct wm_drive *d,
+ uchar_t *cdb, int cdblen,
+ void *retbuf, int retbuflen,
+ int getreply)
+{
+ return 0;
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/* NAME: gen_get_drive_status
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_get_drive_status(struct wm_drive *d,
+ int oldmode,
+ int *mode,
+ int *pos,
+ int *track,
+ int *index)
+{
+ struct cd_audio_cmd cmd;
+
+ *mode = WM_CDM_EJECTED;
+
+ if(d->fd < 0)
+ switch( wmcd_open(d) )
+ {
+ case -1:
+ return -1;
+ case 1:
+ return 0;
+ }
+
+ cmd.audio_cmds = CD_INFO_AUDIO;
+
+ if( ioctl(d->fd,DKAUDIO,&cmd) < 0)
+ return -1;
+
+ switch(cmd.status)
+ {
+ case CD_PLAY_AUDIO:
+ *mode = WM_CDM_PLAYING;
+ *track = cmd.indexing.info_audio.current_track;
+ *index = cmd.indexing.info_audio.current_index;
+ *pos = cmd.indexing.info_audio.current_mins * 60 * 75 +
+ cmd.indexing.info_audio.current_secs * 75 +
+ cmd.indexing.info_audio.current_frames;
+ break;
+
+ case CD_PAUSE_AUDIO:
+ *mode = WM_CDM_PAUSED;
+ *track = cmd.indexing.info_audio.current_track;
+ *index = cmd.indexing.info_audio.current_index;
+ *pos = cmd.indexing.info_audio.current_mins * 60 * 75 +
+ cmd.indexing.info_audio.current_secs * 75 +
+ cmd.indexing.info_audio.current_frames;
+
+ break;
+ case CD_NO_AUDIO: /* no play audio in progress */
+ case CD_COMPLETED: /* play operation completed successfully */
+ case CD_STATUS_ERROR: /* invalid status or play stopped due to err */
+ case CD_NOT_VALID: /* audio status is invalid or not supported */
+ *mode = WM_CDM_STOPPED;
+ break;
+ default:
+ *mode = WM_CDM_UNKNOWN;
+ break;
+ }
+
+ return 0;
+} /* gen_get_drive_status() */
+
+
+
+/* NAME: gen_get_trackcount
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_get_trackcount(struct wm_drive *d,int *tracks)
+{
+ struct cd_audio_cmd cmd;
+
+ cmd.audio_cmds = CD_TRK_INFO_AUDIO;
+ cmd.msf_flag = 0;
+
+ if( ioctl(d->fd,DKAUDIO,&cmd) < 0)
+ {
+ perror("DKAUDIO");
+ return -1;
+ }
+
+ *tracks = cmd.indexing.track_index.last_track;
+
+ return 0;
+} /* gen_get_trackcount() */
+
+/* NAME: gen_get_trackinfo
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_get_trackinfo(struct wm_drive *d,int track,int *data,int *startframe)
+{
+ struct cd_audio_cmd cmd;
+
+ cmd.audio_cmds = CD_GET_TRK_MSF;
+ cmd.msf_flag = 1;
+
+ cmd.indexing.track_msf.track = track;
+
+ if( ioctl(d->fd,DKAUDIO,&cmd) < 0)
+ return -1;
+
+ *startframe = cmd.indexing.track_msf.mins * 60 * 75 +
+ cmd.indexing.track_msf.secs * 75 +
+ cmd.indexing.track_msf.frames;
+
+ *data = 0;
+
+ return 0;
+} /* gen_get_trackinfo() */
+
+/* NAME: gen_get_cdlen
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_get_cdlen(struct wm_drive *d,int *frames)
+{
+ int tmp;
+
+ return gen_get_trackinfo(d,LEADOUT,&tmp,frames);
+} /* gen_get_cdlen() */
+
+
+/* NAME: gen_play
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_play(struct wm_drive *d,int start,int end)
+{
+ struct cd_audio_cmd cmd;
+
+ cmd.audio_cmds = CD_PLAY_AUDIO;
+ cmd.msf_flag = 1;
+
+ cmd.indexing.msf.first_mins = start / (60*75);
+ cmd.indexing.msf.first_secs = (start % (60*75)) / 75;
+ cmd.indexing.msf.first_frames = start % 75;
+
+ cmd.indexing.msf.last_mins = end / (60*75);
+ cmd.indexing.msf.last_secs = (end % (60*75)) / 75;
+ cmd.indexing.msf.last_frames = end % 75;
+
+ if( ioctl(d->fd,DKAUDIO,&cmd) < 0)
+ {
+ perror("DKAUDIO:CD_PLAY_AUDIO");
+ return -1;
+ }
+ return 0;
+} /* gen_play() */
+
+/* NAME: gen_pause
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_pause(struct wm_drive *d)
+{
+ struct cd_audio_cmd cmd;
+
+ cmd.audio_cmds = CD_PAUSE_AUDIO;
+
+ return ioctl(d->fd,DKAUDIO,&cmd);
+} /* gen_pause() */
+
+
+/* NAME: gen_resume
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_resume(struct wm_drive *d)
+{
+ struct cd_audio_cmd cmd;
+
+ cmd.audio_cmds = CD_RESUME_AUDIO;
+ return ioctl(d->fd,DKAUDIO,&cmd);
+} /* gen_resume() */
+
+/* NAME: gen_stop
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_stop(struct wm_drive *d)
+{
+ struct cd_audio_cmd cmd;
+
+ cmd.audio_cmds = CD_STOP_AUDIO;
+ return ioctl(d->fd,DKAUDIO,&cmd);
+} /* gen_stop() */
+
+/* NAME: gen_eject
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+ return ioctl(d->fd,DKEJECT,NULL);
+}
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+int
+scale_volume(int vol,int max)
+{
+ return ((vol * (max_volume - min_volume)) / max + min_volume);
+}
+
+int
+unscale_volume(int vol,int max)
+{
+ int n;
+ n = ( vol - min_volume ) * max_volume / (max - min_volume);
+ return (n <0)?0:n;
+}
+
+/* NAME: gen_set_volume
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_set_volume(struct wm_drive *d,int left,int right)
+{
+ struct cd_audio_cmd cmd;
+
+ cmd.audio_cmds = CD_SET_VOLUME;
+ cmd.volume_type = CD_VOLUME_CHNLS;
+
+ cmd.out_port_0_vol = scale_volume(left,100);
+ cmd.out_port_1_vol = scale_volume(right,100);
+
+ if( ioctl(d->fd,DKAUDIO,&cmd) < 0)
+ {
+ perror("CD_SET_VOLUME");
+ return -1;
+ }
+
+ return 0;
+} /* gen_set_volume() */
+
+/* NAME: gen_get_volume
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_get_volume(struct wm_drive *d,int *left,int *right)
+{
+ struct cd_audio_cmd cmd;
+ int l,r;
+
+ fprintf(stderr,"gen_get_volume\n");
+
+ cmd.audio_cmds = CD_INFO_AUDIO;
+ if( ioctl(d->fd,DKAUDIO,&cmd) < 0)
+ return -1;
+
+ *left = unscale_volume(cmd.out_port_0_vol,100);
+ *right = unscale_volume(cmd.out_port_1_vol,100);
+
+ return 0;
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* no SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+
+
+#endif /* _AIX */
+
+
+
+
+
diff --git a/kscd/libwm/plat_bsd386.c b/kscd/libwm/plat_bsd386.c
new file mode 100644
index 00000000..492d394c
--- /dev/null
+++ b/kscd/libwm/plat_bsd386.c
@@ -0,0 +1,510 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * BSD/386-specific drive control routines.
+ */
+
+static char plat_bsd386_id[] = "$Id$";
+
+#if defined(__bsdi__) || defined(__bsdi)
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include "include/wm_config.h"
+
+/*
+ * The following is included from the Linux module. However, I didn't
+ * see a check here if the CD to be ejected is mounted...
+ */
+#if defined(BSD_MOUNTTEST)
+ #include <mntent.h>
+#endif
+
+
+#include <sys/time.h>
+#include <string.h>
+#include <cdrom.h>
+#ifdef SOUNDBLASTER
+# include <sys/soundcard.h>
+#endif
+
+#include "include/wm_struct.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+
+/*
+ * Since we can't sense the drive type with libcdrom anyway, and since the
+ * library doesn't provide "pause" or "resume" functions, use the daux field
+ * to point to the frame number at which we paused.
+ */
+struct pause_info
+{
+ int frame;
+ int endframe;
+};
+
+#define PAUSE_FRAME (((struct pause_info *) d->daux)->frame)
+#define END_FRAME (((struct pause_info *) d->daux)->endframe)
+#define CUR_CD ((struct cdinfo *) d->aux)
+
+void *malloc();
+
+#ifdef SOUNDBLASTER
+ int min_volume = 0;
+ int max_volume = 100;
+ int min_volume_drive = 10; /* Toshiba drive does low values. */
+ int max_volume_drive = 255;
+#else
+ int min_volume = 10;
+ int max_volume = 255;
+#endif
+
+/*--------------------------------------------------------*
+ * Initialize the drive. A no-op for the generic driver.
+ *--------------------------------------------------------*/
+int
+gen_init(struct wm_drive *d)
+{
+ return (0);
+} /* gen_init() */
+
+/*-----------------------------------------------------------------------*
+ * Open the CD device. We can't determine the drive type under BSD/386.
+ *-----------------------------------------------------------------------*/
+int
+wmcd_open(struct wm_drvie *d)
+{
+ void *aux = NULL, *daux = NULL;
+ int fd = -1;
+
+ if (d->aux) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (aux=%d)]\n", d->aux);
+ return (0);
+ }
+
+ if ((aux = cdopen(d->cd_device)) == NULL)
+ {
+ fprintf(stderr, "No cdrom found by libcdrom\n");
+ return (-6);
+ }
+
+ if ((daux = malloc(sizeof(struct pause_info))) == NULL)
+ return (-1);
+
+#ifdef SOUNDBLASTER
+ fd = open("/dev/mixer", O_RDWR, 0);
+#endif
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ find_drive_struct("", "", "");
+ d->aux = aux;
+ d->daux = daux;
+ d->fd = fd;
+ PAUSE_FRAME = 0;
+ END_FRAME = 0;
+
+ (d->init)(d);
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+ int tries = 0;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ tries++;
+ } while ( (status != 0) && (tries < 10) );
+ return status;
+} /* wmcd_reopen() */
+
+
+/*---------------------------------------------*
+ * Send an arbitrary SCSI command to a device.
+ *---------------------------------------------*/
+int
+wm_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen,
+ void *retbuf, int retbuflen, int getreply)
+{
+ /* Don't know how to do SCSI passthrough... */
+ return (-1);
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd); /* close mixer if open */
+ d->fd = -1;
+ cdclose( d->aux ); /* close the cdrom drive! */
+ d->aux = NULL;
+ free(d->daux);
+ d->daux = NULL;
+ }
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ *--------------------------------------------------------------------------*/
+#define DOPOS \
+ *pos = status.abs_frame; \
+*track = status.track_num; \
+*index = status.index_num
+
+int
+gen_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *index)
+{
+ struct cdstatus status;
+ extern enum wm_cd_modes cur_cdmode;
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ /* Is the device open? */
+ if (d->aux == NULL)
+ {
+ switch (wmcd_open(d))
+ {
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+ if (cdstatus (CUR_CD, &status) < 0)
+ {
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ return (0);
+ }
+
+ switch (status.state)
+ {
+ case cdstate_playing:
+ *mode = WM_CDM_PLAYING;
+ DOPOS;
+ break;
+
+ case cdstate_stopped:
+ /* the MITSUMI drive doesn't have a "paused" state,
+ so it always comes here and not to the paused section.
+ The PAUSE_FRAME stuff below (in gen_pause())
+ fakes out the paused state. */
+ if (oldmode == WM_CDM_PLAYING)
+ {
+ *mode = WM_CDM_TRACK_DONE;
+ break;
+ } else if (cur_cdmode != WM_CDM_PAUSED) {
+ *mode = WM_CDM_STOPPED;
+ DOPOS;
+ break;
+ }
+ /* fall through if paused */
+
+ case cdstate_paused:
+ /* the SCSI2 code in the cdrom library only pauses with
+ cdstop(); it never truly stops a disc (until an in-progress
+ play reaches the end). So it always comes here. */
+ if (cur_cdmode == WM_CDM_STOPPED)
+ {
+ *mode = WM_CDM_STOPPED;
+ DOPOS;
+ break;
+ }
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ DOPOS;
+ } else {
+ *mode = WM_CDM_STOPPED;
+ DOPOS;
+ }
+ break;
+
+ default:
+ *mode = WM_CDM_STOPPED;
+ }
+
+ return (0);
+} /* gen_get_drive_status() */
+
+
+/*-------------------------------------*
+ * Get the number of tracks on the CD.
+ *-------------------------------------*/
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ *tracks = CUR_CD->ntracks;
+
+ return (0);
+} /* gen_get_trackcount() */
+
+/*---------------------------------------------------------*
+ * Get the start time and mode (data or audio) of a track.
+ *---------------------------------------------------------*/
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ *data = (CUR_CD->tracks[track - 1].control & 4) ? 1 : 0;
+ *startframe = CUR_CD->tracks[track - 1].start_frame;
+
+ return (0);
+} /* gen_get_trackinfo() */
+
+/*-------------------------------------*
+ * Get the number of frames on the CD.
+ *-------------------------------------*/
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ *frames = CUR_CD->total_frames;
+
+ return (0);
+} /* gen_get_cdlen() */
+
+/*------------------------------------------------------------*
+ * Play the CD from one position to another (both in frames.)
+ *------------------------------------------------------------*/
+int
+gen_play(struct wm_drive *d, int start, int end)
+{
+ END_FRAME = end;
+ if (cdplay(d->aux, start, end) < 0)
+ return (-1);
+ else
+ return (0);
+} /* gen_play() */
+
+/*--------------------------------------------------------------------*
+ * Pause the CD. This is a bit of a trick since there's no cdpause()
+ * function in the library. We fake it by saving the frame number
+ * and stopping.
+ *--------------------------------------------------------------------*/
+int
+gen_pause(struct wm_drive *d)
+{
+ struct cdstatus status;
+
+ if (cdstatus(d->aux, &status) < 0)
+ return (-1);
+ if (status.state != cdstate_playing)
+ PAUSE_FRAME = CUR_CD->tracks[0].start_frame;
+ else
+ PAUSE_FRAME = status.abs_frame;
+ if (cdstop(d->aux) < 0)
+ return (-1);
+
+ return (0);
+} /* gen_pause() */
+
+/*-------------------------------------------------*
+ * Resume playing the CD (assuming it was paused.)
+ *-------------------------------------------------*/
+int
+gen_resume(struct wm_drive *d)
+{
+ int status;
+
+ status = (d->play)(d, PAUSE_FRAME, END_FRAME);
+ PAUSE_FRAME = 0;
+ return (status);
+} /* gen_resume() */
+
+/*--------------*
+ * Stop the CD.
+ *--------------*/
+int
+gen_stop(struct wm_drive *d)
+{
+ return cdstop(d->aux);
+} /* gen_stop() */
+
+/*----------------------------------------*
+ * Eject the current CD, if there is one.
+ *----------------------------------------*/
+int
+gen_eject(struct wm_drive *d)
+{
+ cdeject(d->aux);
+ cdclose(d->aux);
+ d->aux = NULL;
+ free(d->daux);
+ d->daux = NULL;
+
+ if (d->fd >= 0)
+ close(d->fd); /* close mixer */
+ d->fd = -1;
+ return (0);
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if (!cdload(d->aux))
+ return(0);
+ return(-1);
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*------------------------------------------------------------------------*
+ * Return a volume value suitable for passing to the CD-ROM drive. "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ *------------------------------------------------------------------------*/
+static int
+scale_volume(int vol, int max)
+{
+ /* on Toshiba XM-3401B drive, and on soundblaster, this works fine. */
+ return ((vol * (max_volume - min_volume)) / max + min_volume);
+} /* scale_volume() */
+
+/*---------------------------------------------------------------------------*
+ * unscale_volume(cd_vol, max)
+ *
+ * Given a value between min_volume and max_volume, return the volume slider
+ * value needed to achieve that value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ *--------------------------------------------------------------------------*/
+static int
+unscale_volume(int cd_vol, int max)
+{
+ int vol = 0, top = max, bot = 0, scaled;
+
+ while (bot <= top)
+ {
+ vol = (top + bot) / 2;
+ scaled = scale_volume(vol, max);
+ if (cd_vol == scaled)
+ break;
+ if (cd_vol < scaled)
+ top = vol - 1;
+ else
+ bot = vol + 1;
+ }
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > max)
+ vol = max;
+
+ return (vol);
+} /* unscale_volume() */
+
+/*---------------------------------------------------------------------*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ *---------------------------------------------------------------------*/
+int
+gen_set_volume(struct wm_drive *d, int left, int right)
+{
+ int level;
+
+ left = scale_volume(left, 100);
+ right = scale_volume(right, 100);
+ level = right << 8 | left;
+
+ /* Send a Mixer IOCTL */
+ if (d->fd >= 0)
+ (void) ioctl(d->fd, MIXER_WRITE(SOUND_MIXER_VOLUME), &level);
+#ifdef notnow
+ /* NOTE: the cdvolume2() call is an addition to the cdrom library.
+ Pick it up from the archives on bsdi.com */
+ cdvolume2 (CUR_CD, left < 0 ? 0 : left > 255 ? 255 : left,
+ right < 0 ? 0 : right > 255 ? 255 : right);
+
+#endif
+ return (0);
+}
+
+/*---------------------------------------------------------------------*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ *---------------------------------------------------------------------*/
+int
+gen_get_volume(struct wm_drive *d, int *left, int *right)
+{
+ int level;
+
+ /* Most systems can't seem to do this... */
+ *left = *right = -1;
+
+ /* Send a Mixer IOCTL */
+ if (d->fd >= 0) {
+ if (ioctl(d->fd, MIXER_READ(SOUND_MIXER_VOLUME), &level) == 0) {
+ *left = unscale_volume((level & 0xff) & 0xff, 100);
+ *right = unscale_volume((level >> 8) & 0xff, 100);
+ }
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* no SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+
+#endif /* __bsdi__ */
diff --git a/kscd/libwm/plat_freebsd.c b/kscd/libwm/plat_freebsd.c
new file mode 100644
index 00000000..b1028a75
--- /dev/null
+++ b/kscd/libwm/plat_freebsd.c
@@ -0,0 +1,560 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * plat_freebsd.c
+ *
+ * FreeBSD-specific drive control routines.
+ *
+ * Todd Pfaff, 3/20/94
+ *
+ */
+
+#if defined(__FreeBSD__) || defined(__FreeBSD) || defined(__NetBSD__) || defined (__NetBSD) || defined(__DragonFly__)
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include "include/wm_config.h"
+
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/cdio.h>
+
+#if defined(__OpenBSD__)
+# define MSF_MINUTES 1
+# define MSF_SECONDS 2
+# define MSF_FRAMES 3
+# include <sys/scsiio.h>
+# include "/sys/scsi/scsi_all.h"
+# include "/sys/scsi/scsi_cd.h"
+#elif defined(__NetBSD__)
+#include <sys/scsiio.h>
+#include <dev/scsipi/scsipi_cd.h>
+#else
+# define LEFT_PORT 0
+# define RIGHT_PORT 1
+# if defined(__FreeBSD_version) && __FreeBSD_version < 300000
+# include <scsi.h>
+# endif
+#endif
+
+#include "include/wm_struct.h"
+#include "include/wm_platform.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_scsi.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+void *malloc();
+
+int min_volume = 10;
+int max_volume = 255;
+
+
+/*--------------------------------------------------------*
+ * Initialize the drive. A no-op for the generic driver.
+ *--------------------------------------------------------*/
+int
+gen_init(struct wm_drive *d)
+{
+ return (0);
+} /* gen_init() */
+
+
+/*-------------------------------------------------------------------*
+ * Open the CD device and figure out what kind of drive is attached.
+ *-------------------------------------------------------------------*/
+int
+wmcd_open( struct wm_drive *d )
+{
+ int fd;
+ static int warned = 0;
+ char vendor[32] = WM_STR_GENVENDOR;
+ char model[32] = WM_STR_GENMODEL;
+ char rev[32] = WM_STR_GENREV;
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+ d->fd = open(d->cd_device, 0);
+ if (d->fd < 0)
+ {
+ if (errno == EACCES)
+ {
+ return -EACCES;
+ }
+
+ /* No CD in drive. */
+ return (1);
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ fd = d->fd;
+
+ find_drive_struct(vendor, model, rev);
+
+ /*(d->init)(d); */
+
+ d->fd = fd;
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ if(status == -EACCES || status == 1)
+ return status;
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/*---------------------------------------------*
+ * Send an arbitrary SCSI command to a device.
+ *
+ *---------------------------------------------*/
+int
+wm_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen,
+ void *retbuf, int retbuflen, int getreply)
+{
+ return (-1);
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ *--------------------------------------------------------------------------*/
+int
+gen_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *index)
+{
+ struct ioc_read_subchannel sc;
+ struct cd_sub_channel_info scd;
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ sc.address_format = CD_MSF_FORMAT;
+ sc.data_format = CD_CURRENT_POSITION;
+ sc.track = 0;
+ sc.data_len = sizeof(scd);
+ sc.data = (struct cd_sub_channel_info *)&scd;
+
+ /* Is the device open? */
+ if (d->fd < 0)
+ {
+ switch (wmcd_open(d))
+ {
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+ if (ioctl(d->fd, CDIOCREADSUBCHANNEL, &sc))
+ {
+ /*
+ * #ifdef __NetBSD__
+ *
+ * Denis Bourez <denis@rsn.fdn.fr> told me, that closing the
+ * device is mandatory for FreeBSD, too.
+ */
+ /* we need to release the device so the kernel will notice
+ reloaded media */
+ (void) close(d->fd);
+ d->fd = -1;
+ /*
+ * #endif
+ */
+ return (0); /* ejected */
+ }
+
+ switch (scd.header.audio_status)
+ {
+ case CD_AS_PLAY_IN_PROGRESS:
+ *mode = WM_CDM_PLAYING;
+ dopos:
+ *pos = scd.what.position.absaddr.msf.minute * 60 * 75 +
+ scd.what.position.absaddr.msf.second * 75 +
+ scd.what.position.absaddr.msf.frame;
+ *track = scd.what.position.track_number;
+ *index = scd.what.position.index_number;
+ break;
+
+ case CD_AS_PLAY_PAUSED:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ goto dopos;
+ }
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ case CD_AS_PLAY_COMPLETED:
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ break;
+
+ case CD_AS_NO_STATUS:
+ case 0:
+ *mode = WM_CDM_STOPPED;
+ break;
+ }
+
+ return (0);
+} /* gen_get_drive_status() */
+
+
+/*-------------------------------------*
+ * Get the number of tracks on the CD.
+ *-------------------------------------*/
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ struct ioc_toc_header hdr;
+
+ if (ioctl(d->fd, CDIOREADTOCHEADER, &hdr) == -1)
+ return (-1);
+
+ *tracks = hdr.ending_track - hdr.starting_track + 1;
+
+ return (0);
+} /* gen_get_trackcount() */
+
+/*-----------------------------------------------------------------------*
+ * Get the start time and mode (data or audio) of a track.
+ *
+ * XXX - this should get cached, but that means keeping track of ejects.
+ *-----------------------------------------------------------------------*/
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ struct ioc_read_toc_entry toc;
+ struct cd_toc_entry toc_buffer;
+
+ bzero((char *)&toc_buffer, sizeof(toc_buffer));
+ toc.address_format = CD_MSF_FORMAT;
+ toc.starting_track = track;
+ toc.data_len = sizeof(toc_buffer);
+ toc.data = &toc_buffer;
+
+ if (ioctl(d->fd, CDIOREADTOCENTRYS, &toc))
+ return (-1);
+
+ *data = ((toc_buffer.control & 0x4) != 0);
+
+ *startframe = toc_buffer.addr.msf.minute*60*75 +
+ toc_buffer.addr.msf.second * 75 +
+ toc_buffer.addr.msf.frame;
+
+ return (0);
+} /* gen_get_trackinfo() */
+
+/*-------------------------------------*
+ * Get the number of frames on the CD.
+ *-------------------------------------*/
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ int tmp;
+ struct ioc_toc_header hdr;
+ int status;
+
+#define LEADOUT 0xaa /* see scsi.c. what a hack! */
+ return gen_get_trackinfo(d, LEADOUT, &tmp, frames);
+} /* gen_get_cdlen() */
+
+
+/*------------------------------------------------------------*
+ * Play the CD from one position to another (both in frames.)
+ *------------------------------------------------------------*/
+int
+gen_play(struct wm_drive *d, int start, int end, int realstart)
+{
+ struct ioc_play_msf msf;
+
+ msf.start_m = start / (60*75);
+ msf.start_s = (start % (60*75)) / 75;
+ msf.start_f = start % 75;
+ msf.end_m = end / (60*75);
+ msf.end_s = (end % (60*75)) / 75;
+ msf.end_f = end % 75;
+
+ if (ioctl(d->fd, CDIOCSTART))
+ return (-1);
+
+ if (ioctl(d->fd, CDIOCPLAYMSF, &msf))
+ return (-2);
+
+ return (0);
+} /* gen_play() */
+
+/*---------------*
+ * Pause the CD.
+ *---------------*/
+int
+gen_pause( struct wm_drive *d )
+{
+ return (ioctl(d->fd, CDIOCPAUSE));
+} /* gen_pause() */
+
+/*-------------------------------------------------*
+ * Resume playing the CD (assuming it was paused.)
+ *-------------------------------------------------*/
+int
+gen_resume( struct wm_drive *d )
+{
+ return (ioctl(d->fd, CDIOCRESUME));
+} /* gen_resume() */
+
+/*--------------*
+ * Stop the CD.
+ *--------------*/
+int
+gen_stop( struct wm_drive *d)
+{
+ return (ioctl(d->fd, CDIOCSTOP));
+} /* gen_stop() */
+
+/*----------------------------------------*
+ * Eject the current CD, if there is one.
+ *----------------------------------------*/
+int
+gen_eject( struct wm_drive *d )
+{
+ /* On some systems, we can check to see if the CD is mounted. */
+ struct stat stbuf;
+ struct statfs buf;
+ int rval;
+
+ if (fstat(d->fd, &stbuf) != 0)
+ return (-2);
+
+ /* Is this a mounted filesystem? */
+ if (fstatfs(stbuf.st_rdev, &buf) == 0)
+ return (-3);
+
+ rval = ioctl(d->fd, CDIOCALLOW);
+
+ if (rval == 0)
+ rval = ioctl(d->fd, CDIOCEJECT);
+
+ if (rval == 0)
+ rval = ioctl(d->fd, CDIOCPREVENT);
+
+ (void) close(d->fd);
+
+ return rval;
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*---------------------------------------------------------------------------*
+ * scale_volume(vol, max)
+ *
+ * Return a volume value suitable for passing to the CD-ROM drive. "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ *
+ * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
+ * increases much faster toward the top end of the volume scale than it
+ * does at the bottom. To make up for this, we make the volume scale look
+ * sort of logarithmic (actually an upside-down inverse square curve) so
+ * that the volume value passed to the drive changes less and less as you
+ * approach the maximum slider setting. The actual formula looks like
+ *
+ * (max^2 - (max - vol)^2) * (max_volume - min_volume)
+ * v = --------------------------------------------------- + min_volume
+ * max^2
+ *
+ * If your system's volume settings aren't broken in this way, something
+ * like the following should work:
+ *
+ * return ((vol * (max_volume - min_volume)) / max + min_volume);
+ *---------------------------------------------------------------------------*/
+static int
+scale_volume(int vol, int max)
+{
+ return ((vol * (max_volume - min_volume)) / max + min_volume);
+} /* scale_volume() */
+
+/*---------------------------------------------------------------------------*
+ * unscale_volume(cd_vol, max)
+ *
+ * Given a value between min_volume and max_volume, return the volume slider
+ * value needed to achieve that value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ *--------------------------------------------------------------------------*/
+static int
+unscale_volume( int cd_vol, int max )
+{
+ int vol = 0, top = max, bot = 0, scaled;
+
+ while (bot <= top)
+ {
+ vol = (top + bot) / 2;
+ scaled = scale_volume(vol, max);
+ if (cd_vol == scaled)
+ break;
+ if (cd_vol < scaled)
+ top = vol - 1;
+ else
+ bot = vol + 1;
+ }
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > max)
+ vol = max;
+
+ return (vol);
+} /* unscale_volume() */
+
+/*---------------------------------------------------------------------*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ *---------------------------------------------------------------------*/
+int
+gen_set_volume(struct wm_drive *d, int left, int right)
+{
+ struct ioc_vol vol;
+
+ if (left < 0) /* don't laugh, I saw this happen once! */
+ left = 0;
+ if (right < 0)
+ right = 0;
+ left = scale_volume(left, 100);
+ right = scale_volume(right, 100);
+
+ bzero((char *)&vol, sizeof(vol));
+
+ vol.vol[LEFT_PORT] = left;
+ vol.vol[RIGHT_PORT] = right;
+
+ if (ioctl(d->fd, CDIOCSETVOL, &vol))
+ return (-1);
+
+ return (0);
+} /* gen_set_volume() */
+
+/*---------------------------------------------------------------------*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ *---------------------------------------------------------------------*/
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ struct ioc_vol vol;
+
+ if (d->fd >= 0)
+ {
+ bzero((char *)&vol, sizeof(vol));
+
+ if (ioctl(d->fd, CDIOCGETVOL, &vol))
+ *left = *right = -1;
+ else
+ {
+ *left = unscale_volume(vol.vol[LEFT_PORT], 100);
+ *right = unscale_volume(vol.vol[RIGHT_PORT], 100);
+ }
+ } else {
+ *left = *right = -1;
+ }
+ return (0);
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* no SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+
+#endif
diff --git a/kscd/libwm/plat_hpux.c b/kscd/libwm/plat_hpux.c
new file mode 100644
index 00000000..53aaee40
--- /dev/null
+++ b/kscd/libwm/plat_hpux.c
@@ -0,0 +1,358 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * HP/UX-specific drive control routines.
+ */
+
+#if defined(hpux) || defined(__hpux)
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <ustat.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include "include/wm_config.h"
+
+/*
+ * this is for glibc 2.x which the ust structure in
+ * ustat.h not stat.h
+ */
+#ifdef __GLIBC__
+#include <sys/ustat.h>
+#endif
+
+#include <sys/time.h>
+#include <sys/scsi.h>
+
+#include "include/wm_struct.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+void *malloc();
+char *strchr();
+
+int min_volume = 0;
+int max_volume = 255;
+
+/*--------------------------------------------------------*
+ * Initialize the drive. A no-op for the generic driver.
+ *--------------------------------------------------------*/
+int
+gen_init( struct wm_drive *d )
+{
+ return (0);
+} /* gen_init() */
+
+
+/*-------------------------------------------------------------*
+ * Open the CD and figure out which kind of drive is attached.
+ *-------------------------------------------------------------*/
+int
+wmcd_open( struct wm_drive *d )
+{
+ int fd, flag = 1;
+ static int warned = 0;
+ /* unsigned ? */
+ char vendor[32], model[32], rev[32];
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+ d->fd = open(d->cd_device, O_RDWR);
+ if (d->fd < 0)
+ {
+ if (errno == EACCES)
+ {
+ return -EACCES;
+ }
+ else if (errno != EINTR)
+ {
+ return (-6);
+ }
+
+ /* No CD in drive. */
+ return (1);
+ }
+
+ /* Prepare the device to receive raw SCSI commands. */
+ if (ioctl(d->fd, SIOC_CMD_MODE, &flag) < 0)
+ {
+ fprintf(stderr, "%s: SIOC_CMD_MODE: true: %s\n",
+ d->cd_device, strerror(errno));
+ /*exit(1);*/
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ fd = d->fd;
+
+ /* Default drive is the HP one, which might not respond to INQUIRY */
+ strcpy(&vendor, "TOSHIBA");
+ strcpy(&model, "XM-3301");
+ rev[0] = '\0';
+ wm_scsi_get_drive_type(d, vendor, model, rev);
+ find_drive_struct(vendor, model, rev);
+
+ d->fd = fd;
+
+ (d->init)(d);
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/*----------------------------------*
+ * Send a SCSI command out the bus.
+ *----------------------------------*/
+int
+wm_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen,
+ void *retbuf, int retbuflen, int getreply )
+{
+#ifdef SIOC_IO
+ struct sctl_io cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cdb_length = cdblen;
+ cmd.data = retbuf;
+ cmd.data_length = retbuflen;
+ cmd.max_msecs = 1000;
+ cmd.flags = getreply ? SCTL_READ : 0;
+ memcpy(cmd.cdb, cdb, cdblen);
+
+ return (ioctl(d->fd, SIOC_IO, &cmd));
+#else
+ /* this code, for pre-9.0, is BROKEN! ugh. */
+ char reply_buf[12];
+ struct scsi_cmd_parms cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.clock_ticks = 500;
+ cmd.cmd_mode = 1;
+ cmd.cmd_type = cdblen;
+ memcpy(cmd.command, cdb, cdblen);
+ if (ioctl(d->fd, SIOC_SET_CMD, &cmd) < 0)
+ return (-1);
+
+ if (! retbuf || ! retbuflen)
+ (void) read(d->fd, reply_buf, sizeof(reply_buf));
+ else if (getreply)
+ {
+ if (read(d->fd, retbuf, retbuflen) < 0)
+ return (-1);
+ }
+ else
+ if (write(d->fd, retbuf, retbuflen) < 0)
+ return (-1);
+
+ return (0);
+#endif
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ *--------------------------------------------------------------------------*/
+int
+gen_get_drive_status( struct wm_drive *d, int oldmode, int *mode,
+ int *pos, int *track, int *index )
+{
+ return (wm_scsi2_get_drive_status(d, oldmode, mode, pos, track, index));
+} /* gen_get_drive_status() */
+
+/*-------------------------------------*
+ * Get the number of tracks on the CD.
+ *-------------------------------------*/
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks )
+{
+ return (wm_scsi2_get_trackcount(d, tracks));
+} /* gen_get_trackcount() */
+
+/*---------------------------------------------------------*
+ * Get the start time and mode (data or audio) of a track.
+ *---------------------------------------------------------*/
+int
+gen_get_trackinfo( struct wm_drive *d, int *track, int *data, int *startframe)
+{
+ return (wm_scsi2_get_trackinfo(d, track, data, startframe));
+} /* gen_get_trackinfo() */
+
+/*-------------------------------------*
+ * Get the number of frames on the CD.
+ *-------------------------------------*/
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ int tmp;
+
+ return (wm_scsi2_get_cdlen(d, frames));
+} /* gen_get_cdlen() */
+
+/*------------------------------------------------------------*
+ * Play the CD from one position to another (both in frames.)
+ *------------------------------------------------------------*/
+int
+gen_play( struct wm_drive *d, int start, int end )
+{
+ return (wm_scsi2_play(d, start, end));
+} /* gen_play() */
+
+/*---------------*
+ * Pause the CD.
+ *---------------*/
+int
+gen_pause( struct wm_drive *d )
+{
+ return (wm_scsi2_pause(d));
+} /* gen_pause() */
+
+/*-------------------------------------------------*
+ * Resume playing the CD (assuming it was paused.)
+ *-------------------------------------------------*/
+int
+gen_resume( struct wm_drive *d )
+{
+ return (wm_scsi2_resume(d));
+} /* gen_resume() */
+
+/*--------------*
+ * Stop the CD.
+ *--------------*/
+int
+gen_stop( struct wm_drive *d )
+{
+ return (wm_scsi2_stop(d));
+} /* gen_stop() */
+
+
+/*----------------------------------------*
+ * Eject the current CD, if there is one.
+ *----------------------------------------*/
+int
+gen_eject( struct wm_drive *d )
+{
+ struct stat stbuf;
+ struct ustat ust;
+
+ if (fstat(d->fd, &stbuf) != 0)
+ return (-2);
+
+ /* Is this a mounted filesystem? */
+ if (ustat(stbuf.st_rdev, &ust) == 0)
+ return (-3);
+
+ return (wm_scsi2_eject(d));
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ return (wm_scsi2_closetray(d));
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*---------------------------------------------------------------------*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ *---------------------------------------------------------------------*/
+int
+gen_set_volume( struct wm_drive *d, int left, int right )
+{
+ return (wm_scsi2_set_volume(d, left, right));
+} /* gen_set_volume() */
+
+/*---------------------------------------------------------------------*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ *---------------------------------------------------------------------*/
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ return (wm_scsi2_get_volume(d, left, right));
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ /* This needs to be tested ! */
+ return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght);
+} /* gen_get_cdtext() */
+
+
+#endif
+
+
diff --git a/kscd/libwm/plat_irix.c b/kscd/libwm/plat_irix.c
new file mode 100644
index 00000000..7f1e3fc7
--- /dev/null
+++ b/kscd/libwm/plat_irix.c
@@ -0,0 +1,474 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * IRIX specific.
+ *
+ * Taken from the kscd distribution
+ *
+ * Paul Kendall
+ * paul@orion.co.nz, or
+ * paul@kcbbs.gen.nz
+ */
+
+#if defined(sgi) || defined(__sgi)
+
+#include "include/wm_config.h"
+
+/*
+ * Yes, it was designed for WorkMan 1.4b3
+ * Because I did start over from 1.3a, I disable it here.
+ * There is no guarantee of getting working code by defining
+ * CDDA yourself.
+ *
+ */
+#undef CDDA
+/*#define CDDA*/
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sigfpe.h>
+#include <dmedia/cdaudio.h>
+#include <dmedia/audio.h>
+#include <errno.h>
+
+#include "include/wm_struct.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+void *malloc();
+char *strchr();
+
+int min_volume = 0;
+int max_volume = 255;
+
+#ifdef CDDA
+static int playing = STOPPED;
+static CDPLAYER *icd;
+static CDPARSER *icdp;
+static CDFRAME cdbuf[12];
+static ALport audioport;
+static ALconfig aconfig;
+static struct itimerval audiotimer = { {0,0}, {0,25000} };
+static int cdtrack=0;
+static int cdframe=0;
+static int cdstopframe=0;
+
+/*
+ * Platform specific internal functions for CDDA
+ */
+void
+cbprognum(void *arg, CDDATATYPES type, CDPROGNUM* prognum)
+{
+ cdtrack = prognum->value;
+} /* cbprognum() */
+
+void
+cbabstime(void *arg, CDDATATYPES type, struct cdtimecode* atime)
+{
+ cdframe = CDtctoframe(atime);
+ if( cdframe == cdstopframe )
+ playing = STOPPED;
+} /* cbabstime() */
+
+void
+cbplayaudio(void *arg, CDDATATYPES type, short* audio)
+{
+ if(playing != PLAYING) return;
+ ALwritesamps(audioport, audio, CDDA_NUMSAMPLES);
+} /* cbplayaudio() */
+
+static void
+alarmsignal()
+{
+ int n, i;
+ if(playing != PLAYING) return;
+ if( ALgetfilled(audioport) < CDDA_NUMSAMPLES*8 )
+ {
+ /* Only get more samples and play them if we're getting low
+ * this ensures that the CD stays close to the sound
+ */
+ n = CDreadda(icd, cdbuf, 12);
+ if( n == 0 ) return;
+ for( i=0 ; i<12 ; i++ )
+ CDparseframe(icdp, &cdbuf[i]);
+ }
+ signal(SIGALRM, alarmsignal);
+ setitimer(ITIMER_REAL, &audiotimer, NULL);
+} /* alarmsignal() */
+#endif
+
+/*--------------------------------------------------------*
+ * Initialize the drive. A no-op for the generic driver.
+ *--------------------------------------------------------*/
+int
+gen_init( struct wm_drive *d )
+{
+#ifdef CDDA
+ long Param[4];
+ /* Set the audio rate to 44100Hz 16bit 2s-comp stereo */
+ aconfig = ALnewconfig();
+ ALsetwidth(aconfig, AL_SAMPLE_16);
+ ALsetsampfmt(aconfig, AL_SAMPFMT_TWOSCOMP);
+ ALsetchannels(aconfig, 2);
+ Param[0] = AL_OUTPUT_RATE; Param[1] = AL_RATE_44100;
+ Param[2] = AL_CHANNEL_MODE; Param[3] = AL_STEREO;
+ ALsetparams(AL_DEFAULT_DEVICE, Param, 4);
+ audioport = ALopenport("KDE KSCD Audio", "w", aconfig);
+
+ /* setup cdparser */
+ icdp = CDcreateparser();
+ CDaddcallback(icdp, cd_audio, (CDCALLBACKFUNC)cbplayaudio, 0);
+ CDaddcallback(icdp, cd_pnum, (CDCALLBACKFUNC)cbprognum, 0);
+ CDaddcallback(icdp, cd_atime, (CDCALLBACKFUNC)cbabstime, 0);
+
+ /* Lets handle those floating point exceptions expeditiously. */
+ sigfpe_[_UNDERFL].repls = _ZERO;
+ handle_sigfpes(_ON, _EN_UNDERFL, NULL, _ABORT_ON_ERROR, NULL);
+#endif
+ return 0;
+} /* gen_init() */
+
+/*-------------------------------------------------------------*
+ * Open the CD and figure out which kind of drive is attached.
+ *-------------------------------------------------------------*/
+int
+wmcd_open( struct wm_drive *d )
+{
+ int fd;
+ CDSTATUS s;
+
+ if (d->fd < 0) /* Device already open? */
+ {
+ if (d->cd_device == NULL)
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+ d->fd = 1;
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ fd = d->fd;
+ find_drive_struct("", "", "");
+ d->fd = fd;
+ (d->init)(d);
+
+ d->daux = CDopen(d->cd_device,"r");
+ if (d->daux == 0)
+ {
+ return (-6);
+ }
+#ifdef CDDA
+ icd = d->daux;
+#endif
+ } else {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ }
+
+ CDgetstatus(d->daux, &s);
+ if( s.state==CD_NODISC || s.state==CD_ERROR )
+ return 1;
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+
+/*----------------------------------*
+ * Send a SCSI command out the bus.
+ *----------------------------------*/
+int
+wm_scsi( struct wm_drive *d, unsigned char *xcdb, int cdblen,
+ char *retbuf, int retbuflen, int getreply)
+{
+ return -1;
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ *--------------------------------------------------------------------------*/
+int
+gen_get_drive_status( struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track,
+ int *index )
+{
+#ifdef CDDA
+ *mode = playing;
+ *track = cdtrack;
+ *pos = cdframe;
+ *index = 0;
+#else
+ CDSTATUS s;
+ if( CDgetstatus(d->daux, &s)==0 )
+ return -1;
+ *pos = CDmsftoframe(s.min,s.sec,s.frame);
+ *track = s.track;
+ *index = 0;
+ switch( s.state )
+ {
+ case CD_READY: *mode = WM_CDM_STOPPED;
+ break;
+ case CD_STILL:
+ case CD_PAUSED: *mode = WM_CDM_PAUSED;
+ break;
+ case CD_PLAYING: *mode = WM_CDM_PLAYING;
+ break;
+ default: *mode = WM_CDM_UNKNOWN;
+ }
+#endif
+ return 0;
+} /* gen_get_drive_status() */
+
+/*-------------------------------------*
+ * Get the number of tracks on the CD.
+ *-------------------------------------*/
+int
+gen_get_trackcount( struct wm_drive *d, int *tracks )
+{
+ CDSTATUS s;
+ if( CDgetstatus(d->daux, &s)==0 )
+ return -1;
+ *tracks = s.last;
+ return 0;
+} /* gen_get_trackcount() */
+
+/*---------------------------------------------------------*
+ * Get the start time and mode (data or audio) of a track.
+ *---------------------------------------------------------*/
+int
+gen_get_trackinfo( struct wm_drive *d, int track, int *data, int *startframe)
+{
+ CDTRACKINFO i;
+ int ret = CDgettrackinfo(d->daux, track, &i);
+ if( ret == 0 )
+ return -1;
+ *data = 0;
+ *startframe = CDmsftoframe(i.start_min,i.start_sec,i.start_frame);
+ return 0;
+} /* gen_get_trackinfo() */
+
+/*-------------------------------------*
+ * Get the number of frames on the CD.
+ *-------------------------------------*/
+int
+gen_get_cdlen( struct wm_drive *d, int *frames )
+{
+ CDSTATUS s;
+ if( CDgetstatus(d->daux, &s)==0 )
+ return -1;
+ *frames = CDmsftoframe(s.total_min,s.total_sec,s.total_frame);
+ return 0;
+} /* gen_get_cdlen() */
+
+/*------------------------------------------------------------*
+ * Play the CD from one position to another (both in frames.)
+ *------------------------------------------------------------*/
+int
+gen_play( struct wm_drive *d, int start, int end )
+{
+#ifdef CDDA
+ int m, s, f;
+ CDframetomsf(start, &m, &s, &f);
+ CDseek(icd, m, s, f);
+ cdstopframe = end;
+ playing = PLAYING;
+ signal(SIGALRM, alarmsignal);
+ setitimer(ITIMER_REAL, &audiotimer, NULL);
+#else
+ int m, s, f;
+ CDframetomsf(start, &m, &s, &f);
+ CDplayabs(d->daux, m, s, f, 1);
+#endif
+ return 0;
+} /* gen_play() */
+
+/*---------------*
+ * Pause the CD.
+ *---------------*/
+int
+gen_pause( struct wm_drive *d )
+{
+#ifdef CDDA
+ playing = WM_CDM_PAUSED;
+#else
+ CDSTATUS s;
+ if( CDgetstatus(d->daux, &s)==0 )
+ return -1;
+ if(s.state == CD_PLAYING)
+ CDtogglepause(d->daux);
+#endif
+ return 0;
+} /* gen_pause() */
+
+/*-------------------------------------------------*
+ * Resume playing the CD (assuming it was paused.)
+ *-------------------------------------------------*/
+int
+gen_resume( struct wm_drive *d )
+{
+#ifdef CDDA
+ playing = WM_CDM_PLAYING;
+ signal(SIGALRM, alarmsignal);
+ setitimer(ITIMER_REAL, &audiotimer, NULL);
+#else
+ CDSTATUS s;
+ if( CDgetstatus(d->daux, &s)==0 )
+ return -1;
+ if(s.state == CD_PAUSED)
+ CDtogglepause(d->daux);
+#endif
+ return 0;
+} /* gen_resume() */
+
+/*--------------*
+ * Stop the CD.
+ *--------------*/
+int
+gen_stop( struct wm_drive *d )
+{
+#ifdef CDDA
+ playing = WM_CDM_STOPPED;
+#else
+ CDstop(d->daux);
+#endif
+ return 0;
+} /* gen_stop() */
+
+/*----------------------------------------*
+ * Eject the current CD, if there is one.
+ *----------------------------------------*/
+int
+gen_eject( struct wm_drive *d )
+{
+#ifdef CDDA
+ playing = WM_CDM_STOPPED;
+#endif
+ CDeject(d->daux);
+ return 0;
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * milliByte@Deathsdoor.com
+ *----------------------------------------*/
+
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+/*---------------------------------------------------------------------*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ *---------------------------------------------------------------------*/
+int
+gen_set_volume( struct wm_drive *d, int left, int right )
+{
+ long Param[4];
+ Param[0] = AL_LEFT_SPEAKER_GAIN; Param[1] = left*255/100;
+ Param[2] = AL_RIGHT_SPEAKER_GAIN; Param[3] = right*255/100;
+ ALsetparams(AL_DEFAULT_DEVICE, Param, 4);
+ return 0;
+} /* gen_set_volume() */
+
+/*---------------------------------------------------------------------*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ *---------------------------------------------------------------------*/
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ long Param[4];
+ Param[0] = AL_LEFT_SPEAKER_GAIN; Param[1] = 0;
+ Param[2] = AL_RIGHT_SPEAKER_GAIN; Param[3] = 0;
+ ALgetparams(AL_DEFAULT_DEVICE, Param, 4);
+ *left = Param[1] * 100 / 255;
+ *right = Param[3] * 100 / 255;
+ return 0;
+} /* gen_get_volume() */
+
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* no SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+
+#endif
+
diff --git a/kscd/libwm/plat_linux.c b/kscd/libwm/plat_linux.c
new file mode 100644
index 00000000..58768a40
--- /dev/null
+++ b/kscd/libwm/plat_linux.c
@@ -0,0 +1,803 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Linux-specific drive control routines. Very similar to the Sun module.
+ */
+
+#if defined(__linux__)
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+/* Try to get around bug #29274 */
+#include <linux/version.h>
+#if 0
+/* this breaks the build on ia64 and s390 for example.
+ sys/types.h is already included and should provide __u64.
+ please tell where we really need this and let's try to find
+ a working #if case for everyone ... adrian@suse.de */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,50)) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,21) && LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+#undef __GNUC__
+typedef unsigned long long __u64;
+#endif
+#endif
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdtext.h"
+
+#if defined(BSD_MOUNTTEST)
+ #include <mntent.h>
+#else
+ /*
+ * this is for glibc 2.x which defines ust structure in
+ * ustat.h not stat.h
+ */
+ #ifdef __GLIBC__
+ #include <sys/ustat.h>
+ #endif
+#endif
+
+
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+#ifndef __GNUC__
+#define __GNUC__ 1
+#endif
+/* needed for vanilla kernel headers, which do provide __u64 only
+ for ansi */
+#undef __STRICT_ANSI__
+/* needed for non-ansi kernel headers */
+#define asm __asm__
+#define inline __inline__
+#include <asm/types.h>
+#include <linux/cdrom.h>
+#undef asm
+#undef inline
+
+#include "include/wm_cdda.h"
+#include "include/wm_struct.h"
+#include "include/wm_platform.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_scsi.h"
+#include "include/wm_helpers.h"
+
+#ifdef OSS_SUPPORT
+#include <linux/soundcard.h>
+#define CD_CHANNEL SOUND_MIXER_CD
+#endif
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+#define max(a,b) ((a) > (b) ? (a) : (b))
+
+#ifdef LINUX_SCSI_PASSTHROUGH
+/* this is from <scsi/scsi_ioctl.h> */
+# define SCSI_IOCTL_SEND_COMMAND 1
+#endif
+
+#ifdef BUILD_CDDA
+int gen_cdda_init( struct wm_drive *d );
+#endif
+
+int min_volume = 0;
+int max_volume = 255;
+
+#ifdef OSS_SUPPORT
+int mixer;
+char mixer_dev_name[20] = "/dev/mixer";
+#endif
+
+/*-------------------------------------------------------*
+ *
+ *
+ * CD-ROM drive functions.
+ *
+ *
+ *-------------------------------------------------------*/
+
+/*--------------------------------------------------------*
+ * Initialize the drive. A no-op for the generic driver.
+ *--------------------------------------------------------*/
+int
+gen_init( struct wm_drive *d )
+{
+ return (0);
+} /* gen_init() */
+
+/*---------------------------------------------------------------------------*
+ * Open the CD device and figure out what kind of drive is attached.
+ *---------------------------------------------------------------------------*/
+int
+wmcd_open( struct wm_drive *d )
+{
+ int fd;
+ char vendor[32], model[32], rev[32];
+
+ if (d->cd_device == NULL)
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+
+ if (d->fd >= 0) { /* Device already open? */
+/* wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);*/
+ return (0);
+ }
+
+ fd = open(d->cd_device, O_RDONLY | O_NONBLOCK);
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): device=%s fd=%d\n", d->cd_device, fd);
+
+ if (fd < 0)
+ return -errno;
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ d->fd = fd;
+
+ /*
+ * See if we can do digital audio.
+ */
+#if defined(BUILD_CDDA)
+ if(d->cdda && gen_cdda_init(d)) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): failed in gen_cdda_init\n");
+ gen_close(d);
+ return -1;
+ }
+#endif
+
+ /* Can we figure out the drive type? */
+ if (wm_scsi_get_drive_type(d, vendor, model, rev)) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): inquiry failed\n");
+ strcpy(vendor, "Generic");
+ strcpy(model, "drive type");
+ strcpy(rev, "");
+ }
+
+ if(find_drive_struct(vendor, model, rev) < 0) {
+ gen_close(d);
+ return -1;
+ }
+
+ if(d->proto->gen_init)
+ return (d->proto->gen_init)(d);
+
+ return 0;
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+ int tries = 0;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ gen_close(d);
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calls wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ tries++;
+ } while ( (status != 0) && (tries < 10) );
+ return status;
+} /* wmcd_reopen() */
+
+/*---------------------------------------------*
+ * Send an arbitrary SCSI command to a device.
+ *---------------------------------------------*/
+int
+wm_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen,
+ void *retbuf, int retbuflen, int getreply )
+{
+#ifdef LINUX_SCSI_PASSTHROUGH
+
+ char *cmd;
+ int cmdsize;
+
+ cmdsize = 2 * sizeof(int);
+ if (retbuf)
+ {
+ if (getreply) cmdsize += max(cdblen, retbuflen);
+ else cmdsize += (cdblen + retbuflen);
+ }
+ else cmdsize += cdblen;
+
+ cmd = malloc(cmdsize);
+ if (cmd == NULL)
+ return (-1);
+
+ ((int*)cmd)[0] = cdblen + ((retbuf && !getreply) ? retbuflen : 0);
+ ((int*)cmd)[1] = ((retbuf && getreply) ? retbuflen : 0);
+
+ memcpy(cmd + 2*sizeof(int), cdb, cdblen);
+ if (retbuf && !getreply)
+ memcpy(cmd + 2*sizeof(int) + cdblen, retbuf, retbuflen);
+
+ if (ioctl(d->fd, SCSI_IOCTL_SEND_COMMAND, cmd))
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "%s: ioctl() failure\n", __FILE__);
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "command buffer is:\n");
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "%02x %02x %02x %02x %02x %02x\n",
+ cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5]);
+ free(cmd);
+ return (-1);
+ }
+
+ if (retbuf && getreply)
+ memcpy(retbuf, cmd + 2*sizeof(int), retbuflen);
+
+ free(cmd);
+ return 0;
+
+#else /* Linux SCSI passthrough*/
+/*----------------------------------------*
+ * send packet over cdrom interface
+ * kernel >= 2.2.16
+ *----------------------------------------*/
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,2,15))
+
+ struct cdrom_generic_command cdc;
+ struct request_sense sense;
+ int capability;
+
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wm_scsi over CDROM_SEND_PACKET entered\n");
+
+ capability = ioctl(d->fd, CDROM_GET_CAPABILITY);
+
+ if(!(capability & CDC_GENERIC_PACKET))
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "your CDROM or/and kernel don't support CDC_GENERIC_PACKET ...\n");
+ return -1;
+ }
+
+ memset(&cdc, 0, sizeof(struct cdrom_generic_command));
+ memset(&sense, 0, sizeof(struct request_sense));
+
+ memcpy(cdc.cmd, cdb, cdblen);
+
+ cdc.buffer = retbuf;
+ cdc.buflen = retbuflen;
+ cdc.stat = 0;
+ cdc.sense = &sense;
+ cdc.data_direction = getreply?CGC_DATA_READ:CGC_DATA_WRITE;
+
+ /* sendpacket_over_cdrom_interface() */
+ return ioctl(d->fd, CDROM_SEND_PACKET, &cdc);
+#endif /* CDROM_SEND_PACKET */
+ printf("ERROR: this binary was compiled without CDROM GENERIC PACKET SUPPORT. kernel version < 2.2.16?\n");
+ printf("ERROR: if you have a SCSI CDROM, rebuild it with a #define LINUX_SCSI_PASSTHROUGH\n");
+ return (-1);
+#endif
+} /* wm_scsi */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/*--------------------------------*
+ * Keep the CD open all the time.
+ * disabled, analogous to 1.4b3
+ *--------------------------------*
+void
+keep_cd_open( void )
+{
+ int fd;
+ struct flock fl;
+ extern end;
+
+
+ for (fd = 0; fd < 256; fd++)
+ close(fd);
+
+ if (fork())
+ exit(0);
+
+#if defined(O_NOFOLLOW)
+ if ((fd = open("/tmp/cd.lock", O_RDWR | O_CREAT | O_NOFOLLOW, 0666)) < 0)
+#else
+ if ((fd = open("/tmp/cd.lock", O_RDWR | O_CREAT, 0666)) < 0)
+#endif
+ exit(0);
+ fl.l_type = F_WRLCK;
+ fl.l_whence = 0;
+ fl.l_start = 0;
+ fl.l_len = 0;
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ exit(0);
+
+ if (open(cd_device, 0) >= 0)
+ {
+ brk(&end);
+ pause();
+ }
+
+ exit(0);
+}
+*/
+
+/*--------------------------------------------------------------------------*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ *--------------------------------------------------------------------------*/
+int
+gen_get_drive_status( struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *ind )
+{
+ struct cdrom_subchnl sc;
+ int ret;
+
+#ifdef SBPCD_HACK
+ static int prevpos = 0;
+#endif
+
+
+ /* Is the device open? */
+ if (d->fd < 0) {
+ ret = wmcd_open(d);
+ if(ret < 0) /* error */
+ return ret;
+
+ if(ret == 1) {
+ /* retry */
+ *mode = WM_CDM_UNKNOWN;
+ return 0;
+ }
+ }
+
+ /* Try to get rid of the door locking */
+ /* Don't care about return value. If it */
+ /* works - fine. If not - ... */
+ ioctl(d->fd, CDROM_LOCKDOOR, 0);
+
+ *mode = WM_CDM_UNKNOWN;
+
+ sc.cdsc_format = CDROM_MSF;
+
+#if defined(BUILD_CDDA)
+ IFCDDA(d) {
+ if(!cdda_get_drive_status(d, oldmode, mode, pos, track, ind)) {
+ if(*mode == WM_CDM_STOPPED)
+ *mode = WM_CDM_UNKNOWN; /* dont believe */
+ }
+ } else
+#endif
+ if(!ioctl(d->fd, CDROMSUBCHNL, &sc)) {
+ switch (sc.cdsc_audiostatus) {
+ case CDROM_AUDIO_PLAY:
+ *mode = WM_CDM_PLAYING;
+ *track = sc.cdsc_trk;
+ *ind = sc.cdsc_ind;
+ *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
+ sc.cdsc_absaddr.msf.second * 75 +
+ sc.cdsc_absaddr.msf.frame;
+#ifdef SBPCD_HACK
+ if( *pos < prevpos ) {
+ if( (prevpos - *pos) < 75 ) {
+ *mode = WM_CDM_TRACK_DONE;
+ }
+ }
+
+ prevpos = *pos;
+#endif
+ break;
+
+ case CDROM_AUDIO_PAUSED:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED) {
+ *mode = WM_CDM_PAUSED;
+ *track = sc.cdsc_trk;
+ *ind = sc.cdsc_ind;
+ *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
+ sc.cdsc_absaddr.msf.second * 75 +
+ sc.cdsc_absaddr.msf.frame;
+ } else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ case CDROM_AUDIO_NO_STATUS:
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ case CDROM_AUDIO_COMPLETED:
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ break;
+
+ case CDROM_AUDIO_INVALID: /**/
+ default:
+ *mode = WM_CDM_UNKNOWN;
+ break;
+ }
+ }
+
+ if(WM_CDS_NO_DISC(*mode)) {
+ /* verify status of drive */
+ ret = ioctl(d->fd, CDROM_DRIVE_STATUS, 0/* slot */);
+ if(ret == CDS_DISC_OK)
+ ret = ioctl(d->fd, CDROM_DISC_STATUS, 0);
+ switch(ret) {
+ case CDS_NO_DISC:
+ *mode = WM_CDM_NO_DISC;
+ break;
+ case CDS_TRAY_OPEN:
+ *mode = WM_CDM_EJECTED;
+ break;
+ case CDS_AUDIO:
+ case CDS_MIXED:
+ *mode = WM_CDM_STOPPED;
+ break;
+ case CDS_DRIVE_NOT_READY:
+ case CDS_NO_INFO:
+ case CDS_DATA_1:
+ case CDS_DATA_2:
+ case CDS_XA_2_1:
+ case CDS_XA_2_2:
+ default:
+ *mode = WM_CDM_UNKNOWN;
+ }
+ }
+
+ return (0);
+} /* gen_get_drive_status */
+
+/*-------------------------------------*
+ * Get the number of tracks on the CD.
+ *-------------------------------------*/
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ struct cdrom_tochdr hdr;
+
+ if (ioctl(d->fd, CDROMREADTOCHDR, &hdr))
+ return (-1);
+
+ *tracks = hdr.cdth_trk1;
+ return (0);
+} /* gen_get_trackcount() */
+
+/*---------------------------------------------------------*
+ * Get the start time and mode (data or audio) of a track.
+ *---------------------------------------------------------*/
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ struct cdrom_tocentry entry;
+
+ entry.cdte_track = track;
+ entry.cdte_format = CDROM_MSF;
+
+ if (ioctl(d->fd, CDROMREADTOCENTRY, &entry))
+ return (-1);
+
+ *startframe = entry.cdte_addr.msf.minute * 60 * 75 +
+ entry.cdte_addr.msf.second * 75 +
+ entry.cdte_addr.msf.frame;
+ *data = entry.cdte_ctrl & CDROM_DATA_TRACK ? 1 : 0;
+
+ return (0);
+}
+
+/*-------------------------------------*
+ * Get the number of frames on the CD.
+ *-------------------------------------*/
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ int tmp;
+
+ return gen_get_trackinfo( d, CDROM_LEADOUT, &tmp, frames);
+} /* gen_get_cdlen() */
+
+
+/*------------------------------------------------------------*
+ * Play the CD from one position to another (both in frames.)
+ *------------------------------------------------------------*/
+int
+gen_play(struct wm_drive *d, int start, int end, int realstart)
+{
+ struct cdrom_msf msf;
+
+ CDDARETURN(d) cdda_play(d, start, end, realstart);
+
+ msf.cdmsf_min0 = start / (60*75);
+ msf.cdmsf_sec0 = (start % (60*75)) / 75;
+ msf.cdmsf_frame0 = start % 75;
+ msf.cdmsf_min1 = end / (60*75);
+ msf.cdmsf_sec1 = (end % (60*75)) / 75;
+ msf.cdmsf_frame1 = end % 75;
+
+ if (ioctl(d->fd, CDROMPLAYMSF, &msf)) {
+ if (ioctl(d->fd, CDROMSTART))
+ return (-1);
+ if (ioctl(d->fd, CDROMPLAYMSF, &msf))
+ return (-2);
+ }
+
+ /*
+ * I hope no drive gets really confused after CDROMSTART
+ * If so, I need to make this run-time configurable.
+ *
+#ifndef FAST_IDE
+ if (ioctl( d->fd, CDROMSTART))
+ return (-1);
+#endif
+ if (ioctl( d->fd, CDROMPLAYMSF, &msf ))
+ return (-2);
+ */
+
+ return (0);
+} /* gen_play() */
+
+/*---------------*
+ * Pause the CD.
+ *---------------*/
+int
+gen_pause(struct wm_drive *d)
+{
+ CDDARETURN(d) cdda_pause(d);
+ return (ioctl(d->fd, CDROMPAUSE));
+}
+
+/*-------------------------------------------------*
+ * Resume playing the CD (assuming it was paused.)
+ *-------------------------------------------------*/
+int
+gen_resume(struct wm_drive *d)
+{
+ CDDARETURN(d) cdda_pause(d);
+ return (ioctl(d->fd, CDROMRESUME));
+}
+
+/*--------------*
+ * Stop the CD.
+ *--------------*/
+int
+gen_stop(struct wm_drive *d)
+{
+ CDDARETURN(d) cdda_stop(d);
+ return (ioctl(d->fd, CDROMSTOP));
+}
+
+
+/*----------------------------------------*
+ * Eject the current CD, if there is one.
+ *----------------------------------------*/
+int
+gen_eject(struct wm_drive *d)
+{
+ struct stat stbuf;
+#if !defined(BSD_MOUNTTEST)
+ struct ustat ust;
+#else
+ struct mntent *mnt;
+ FILE *fp;
+#endif
+
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "ejecting?\n");
+
+ if (fstat(d->fd, &stbuf) != 0) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "that weird fstat() thingy\n");
+ return (-2);
+ }
+
+ /* Is this a mounted filesystem? */
+#if !defined(BSD_MOUNTTEST)
+ if (ustat(stbuf.st_rdev, &ust) == 0)
+ return (-3);
+#else
+ /*
+ * This is the same test as in the WorkBone interface.
+ * I should eliminate it there, because there is no need
+ * for it in the UI
+ */
+ /* check if drive is mounted (from Mark Buckaway's cdplayer code) */
+ /* Changed it again (look at XPLAYCD from ???? */
+ /* It's better to check the device name rather than one device is */
+ /* mounted as iso9660. That prevents "no playing" if you have more*/
+ /* than one CD-ROM, and one of them is mounted, but it's not the */
+ /* audio CD -dirk */
+ if ((fp = setmntent (MOUNTED, "r")) == NULL)
+ {
+ wm_lib_message(WM_MSG_LEVEL_ERROR|WM_MSG_CLASS, "Could not open %s: %s\n", MOUNTED, strerror (errno));
+ return(-3);
+ }
+ while ((mnt = getmntent (fp)) != NULL)
+ {
+ if (strcmp (mnt->mnt_fsname, d->cd_device) == 0)
+ {
+ wm_lib_message(WM_MSG_LEVEL_ERROR|WM_MSG_CLASS, "CDROM already mounted (according to mtab). Operation aborted.\n");
+ endmntent (fp);
+ return(-3);
+ }
+ }
+ endmntent (fp);
+#endif /* BSD_MOUNTTEST */
+
+ IFCDDA(d) {
+ cdda_eject(d);
+ }
+
+ ioctl( d->fd, CDROM_LOCKDOOR, 0 );
+
+ if (ioctl(d->fd, CDROMEJECT))
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "eject failed (%s).\n", strerror(errno));
+ return (-1);
+ }
+
+ /*------------------
+ * Things in "foobar_one" are left over from 1.4b3
+ * I put them here for further observation. In 1.4b3, however,
+ * that workaround didn't help at least for /dev/sbpcd
+ * (The tray closed just after ejecting because re-opening the
+ * device causes the tray to close)
+ *------------------*/
+#ifdef foobar_one
+ extern int intermittent_dev
+ /*
+ * Some drives (drivers?) won't recognize a new CD if we leave the
+ * device open.
+ */
+ if (intermittent_dev)
+ gen_close(d);
+#endif
+
+ return (0);
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+#ifdef CDROMCLOSETRAY
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "CDROMCLOSETRAY closing tray...\n");
+ if (ioctl(d->fd, CDROMCLOSETRAY))
+ return (-1);
+#else
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen() closing tray...\n");
+ if(!gen_close(d))
+ {
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#endif /* CDROMCLOSETRAY */
+#endif /* CAN_CLOSE */
+ /* Always succeed if the drive can't close. */
+ return(0);
+} /* gen_closetray() */
+
+
+/*------------------------------------------------------------------------*
+ * scale_volume(vol, max)
+ *
+ * Return a volume value suitable for passing to the CD-ROM drive. "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ * This is not used if sound card support is enabled.
+ *
+ *------------------------------------------------------------------------*/
+static int
+scale_volume( int vol, int max )
+{
+#ifdef CURVED_VOLUME
+ return ((max * max - (max - vol) * (max - vol)) *
+ (max_volume - min_volume) / (max * max) + min_volume);
+#else
+ return ((vol * (max_volume - min_volume)) / max + min_volume);
+#endif
+} /* scale_volume() */
+
+static int
+unscale_volume( int vol, int max )
+{
+#ifdef CURVED_VOLUME
+ /* FIXME do it simpler */
+ int tmp = (((max_volume - min_volume - vol) * max * max) - (vol + min_volume));
+ return max - sqrt((tmp/(max_volume - min_volume)));
+#else
+ return (((vol - min_volume) * max) / (max_volume - min_volume));
+#endif
+} /* unscale_volume() */
+
+/*---------------------------------------------------------------------*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ *---------------------------------------------------------------------*/
+int
+gen_set_volume( struct wm_drive *d, int left, int right )
+{
+ struct cdrom_volctrl v;
+
+ CDDARETURN(d) cdda_set_volume(d, left, right);
+
+ /* Adjust the volume to make up for the CD-ROM drive's weirdness. */
+ left = scale_volume(left, 100);
+ right = scale_volume(right, 100);
+
+ v.channel0 = v.channel2 = left < 0 ? 0 : left > 255 ? 255 : left;
+ v.channel1 = v.channel3 = right < 0 ? 0 : right > 255 ? 255 : right;
+
+ return (ioctl(d->fd, CDROMVOLCTRL, &v));
+} /* gen_set_volume() */
+
+/*---------------------------------------------------------------------*
+ * Read the volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ *---------------------------------------------------------------------*/
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ struct cdrom_volctrl v;
+
+ CDDARETURN(d) cdda_get_volume(d, left, right);
+
+#if defined(CDROMVOLREAD)
+ if(!ioctl(d->fd, CDROMVOLREAD, &v)) {
+ *left = unscale_volume((v.channel0 + v.channel2)/2, 100);
+ *right = unscale_volume((v.channel1 + v.channel3)/2, 100);
+ } else
+#endif
+ /* Suns, HPs, Linux, NEWS can't read the volume; oh well */
+ *left = *right = -1;
+
+ return 0;
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *
+ * Return a buffer with cdtext-stream. buffer will be allocated and filled
+ *
+ * needs send packet interface -> for IDE, linux at 2.2.16
+ * depends on scsi.c which depends on wm_scsi defined in here
+ * (which also takes care of IDE drives)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght);
+} /* gen_get_cdtext() */
+
+#endif /* __linux__ */
diff --git a/kscd/libwm/plat_linux_audio.c b/kscd/libwm/plat_linux_audio.c
new file mode 100644
index 00000000..7316a691
--- /dev/null
+++ b/kscd/libwm/plat_linux_audio.c
@@ -0,0 +1,489 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Linux digital audio functions.
+ */
+
+#include "include/wm_config.h"
+
+static char plat_linux_audio_id[] = "$Id$";
+
+#if defined(__linux__) && defined(BUILD_CDDA) /* { */
+
+#include "include/wm_cdda.h"
+
+/* types.h included by wm_cdda.h */
+
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/audioio.h>
+#include <sys/stropts.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * Since there's a lag time between writing audio to the audio device and
+ * hearing it, we need to make sure the status indicators correlate to what's
+ * playing out the speaker. Luckily, Solaris gives us some audio
+ * synchronization facilities that make this pretty easy.
+ *
+ * We maintain a circular queue of status information. When we write some
+ * sound to the audio device, we put its status info into the queue. We write
+ * a marker into the audio stream; when the audio device driver encounters the
+ * marker, it increments a field in a status structure. When we see that
+ * field go up, we grab the next status structure from the queue and send it
+ * to the parent process.
+ *
+ * The minimum size of the queue depends on the latency of the audio stream.
+ */
+#define QSIZE 500
+
+struct cdda_block queue[QSIZE];
+int qtail;
+int qstart;
+
+/*
+ * We only send WMCDDA_PLAYED status messages upstream when the CD is supposed
+ * to be playing; this is used to keep track.
+ */
+extern int playing;
+
+static int aufd, aucfd;
+static int raw_audio = 1; /* Can /dev/audio take 44.1KHz stereo? */
+
+/*
+ * For fast linear-to-ulaw mapping, we use a lookup table that's generated
+ * at startup.
+ */
+unsigned char *ulawmap, linear_to_ulaw();
+
+char *getenv();
+
+/*
+ * Dummy signal handler so writes to /dev/audio will interrupt.
+ */
+static void
+dummy( void )
+{
+ signal(SIGALRM, dummy);
+}
+
+/*
+ * Initialize the audio device.
+ */
+void
+wmaudio_init( void )
+{
+ audio_info_t info;
+ char *audiodev, *acdev;
+ int linval;
+
+ audiodev = getenv("AUDIODEV");
+ if (audiodev == NULL)
+ audiodev = "/dev/audio";
+
+ acdev = malloc(strlen(audiodev) + 4);
+ if (acdev == NULL)
+ {
+ perror("Can't allocate audio control filename");
+ exit(1);
+ }
+ strcpy(acdev, audiodev);
+ strcat(acdev, "ctl");
+
+ aucfd = open(acdev, O_WRONLY, 0);
+ if (aucfd < 0)
+ {
+ perror(acdev);
+ exit(1);
+ }
+ free(acdev);
+
+ aufd = open(audiodev, O_WRONLY, 0);
+ if (aufd < 0)
+ {
+ perror(audiodev);
+ exit(1);
+ }
+
+ signal(SIGALRM, dummy);
+
+ /*
+ * Try to set the device to CD-style audio; we can process it
+ * with the least CPU overhead.
+ */
+ AUDIO_INITINFO(&info);
+ info.play.sample_rate = 44100;
+ info.play.channels = 2;
+ info.play.precision = 16;
+ info.play.encoding = AUDIO_ENCODING_LINEAR;
+ info.play.pause = 0;
+ info.record.pause = 0;
+ info.monitor_gain = 0;
+
+ if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+ if (errno == EINVAL)
+ {
+ /*
+ * Oh well, so much for that idea.
+ */
+ AUDIO_INITINFO(&info);
+ info.play.sample_rate = 8000;
+ info.play.channels = 1;
+ info.play.precision = 8;
+ info.play.encoding = AUDIO_ENCODING_ULAW;
+ info.play.pause = 0;
+ info.record.pause = 0;
+ info.monitor_gain = 0;
+ if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+ {
+ perror("Can't set up audio device");
+ exit(1);
+ }
+
+ /*
+ * Initialize the linear-to-ulaw mapping table.
+ */
+ if (ulawmap == NULL)
+ ulawmap = malloc(65536);
+ if (ulawmap == NULL)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ for (linval = 0; linval < 65536; linval++)
+ ulawmap[linval] = linear_to_ulaw(linval-32768);
+ ulawmap += 32768;
+ raw_audio = 0;
+ }
+ else
+ {
+ perror(audiodev);
+ exit(1);
+ }
+}
+
+/*
+ * Get ready to play some sound.
+ */
+void
+wmaudio_ready( void )
+{
+ audio_info_t info;
+
+ /*
+ * Start at the correct queue position.
+ */
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ qtail = info.play.eof % QSIZE;
+ qstart = qtail;
+
+ queue[qtail].status = WMCDDA_OK;
+}
+
+/*
+ * Stop the audio immediately.
+ */
+void
+wmaudio_stop( void )
+{
+ if (ioctl(aufd, I_FLUSH, FLUSHRW) < 0)
+ perror("flush");
+}
+
+/*
+ * Close the audio device.
+ */
+void
+wmaudio_close( void )
+{
+ wmaudio_stop();
+ close(aufd);
+ close(aucfd);
+}
+
+/*
+ * Set the volume level.
+ */
+void
+wmaudio_volume(int level)
+{
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ info.play.gain = level;
+ if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO");
+}
+
+/*
+ * Set the balance level.
+ */
+void
+wmaudio_balance(int level)
+{
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ level *= AUDIO_RIGHT_BALANCE;
+ info.play.balance = level / 255;
+ if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO");
+}
+
+/*
+ * Mark the most recent audio block on the queue as the last one.
+ */
+void
+wmaudio_mark_last( void )
+{
+ queue[qtail].status = WMCDDA_DONE;
+}
+
+/*
+ * Figure out the most recent status information and send it upstream.
+ */
+int
+wmaudio_send_status( void )
+{
+ audio_info_t info;
+ int qhead;
+
+ /*
+ * Now send the most current status information to our parent.
+ */
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+ perror("AUDIO_GETINFO");
+ qhead = info.play.eof % QSIZE;
+
+ if (qhead != qstart && playing)
+ {
+ int balance;
+
+ if (queue[qhead].status != WMCDDA_DONE)
+ queue[qhead].status = WMCDDA_PLAYED;
+ queue[qhead].volume = info.play.gain;
+ queue[qhead].balance = (info.play.balance * 255) /
+ AUDIO_RIGHT_BALANCE;
+
+ send_status(queue + qhead);
+ qstart = -1;
+ }
+
+ return (queue[qhead].status == WMCDDA_DONE);
+}
+
+/*
+ * Play some audio and pass a status message upstream, if applicable.
+ * Returns 0 on success.
+ */
+int
+wmaudio_play(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+ int i;
+ short *buf16;
+ int alarmcount = 0;
+ struct itimerval it;
+
+ alarm(1);
+
+ while (write(aufd, rawbuf, buflen) <= 0)
+ if (errno == EINTR)
+ {
+ if (! raw_audio && alarmcount++ < 5)
+ {
+ /*
+ * 8KHz /dev/audio blocks for several seconds
+ * waiting for its queue to drop below a low
+ * water mark.
+ */
+ wmaudio_send_status();
+ timerclear(&it.it_interval);
+ timerclear(&it.it_value);
+ it.it_value.tv_usec = 500000;
+ setitimer(ITIMER_REAL, &it, NULL);
+ continue;
+ }
+
+/* close(aufd);
+ close(aucfd);
+ wmaudio_init();
+*/ wmaudio_stop( void );
+ alarm(2);
+ continue;
+ }
+ else
+ {
+ blk->status = WMCDDA_ERROR;
+ return (-1);
+ }
+ alarm(0);
+
+ /*
+ * Mark this spot in the audio stream.
+ *
+ * Marks don't always succeed (if the audio buffer is empty
+ * this call will block forever) so do it asynchronously.
+ */
+ fcntl(aufd, F_SETFL, O_NONBLOCK);
+ if (write(aufd, rawbuf, 0) < 0)
+ {
+ if (errno != EAGAIN)
+ perror("audio mark");
+ }
+ else
+ qtail = (qtail + 1) % QSIZE;
+
+ fcntl(aufd, F_SETFL, 0);
+
+ queue[qtail] = *blk;
+
+ if (wmaudio_send_status() < 0)
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * Get the current audio state.
+ */
+void
+wmaudio_state(struct cdda_block *blk)
+{
+ audio_info_t info;
+ int balance;
+
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+ perror("AUDIO_GETINFO");
+ blk->volume = info.play.gain;
+ blk->balance = (info.play.balance * 255) / AUDIO_RIGHT_BALANCE;
+}
+
+/*
+** This routine converts from linear to ulaw.
+**
+** Craig Reese: IDA/Supercomputing Research Center
+** Joe Campbell: Department of Defense
+** 29 September 1989
+**
+** References:
+** 1) CCITT Recommendation G.711 (very difficult to follow)
+** 2) "A New Digital Technique for Implementation of Any
+** Continuous PCM Companding Law," Villeret, Michel,
+** et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
+** 1973, pg. 11.12-11.17
+** 3) MIL-STD-188-113,"Interoperability and Performance Standards
+** for Analog-to_Digital Conversion Techniques,"
+** 17 February 1987
+**
+** Input: Signed 16 bit linear sample
+** Output: 8 bit ulaw sample
+*/
+#define ZEROTRAP /* turn on the trap as per the MIL-STD */
+#define BIAS 0x84 /* define the add-in bias for 16 bit samples */
+#define CLIP 32635
+
+unsigned char
+linear_to_ulaw( sample )
+int sample;
+{
+ static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
+ int sign, exponent, mantissa;
+ unsigned char ulawbyte;
+
+ /* Get the sample into sign-magnitude. */
+ sign = (sample >> 8) & 0x80; /* set aside the sign */
+ if ( sign != 0 ) sample = -sample; /* get magnitude */
+ if ( sample > CLIP ) sample = CLIP; /* clip the magnitude */
+
+ /* Convert from 16 bit linear to ulaw. */
+ sample = sample + BIAS;
+ exponent = exp_lut[( sample >> 7 ) & 0xFF];
+ mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
+ ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa );
+#ifdef ZEROTRAP
+ if ( ulawbyte == 0 ) ulawbyte = 0x02; /* optional CCITT trap */
+#endif
+
+ return ulawbyte;
+}
+
+/*
+ * Downsample a block of CDDA data, if necessary, for playing out an old-style
+ * audio device.
+ */
+long
+wmaudio_convert(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+ short *buf16 = (short *)rawbuf;
+ int i, j, samples;
+ int mono_value;
+ unsigned char *rbend = rawbuf + buflen;
+
+ /* Don't do anything if the audio device can take the raw values. */
+ if (raw_audio)
+ return (buflen);
+
+ for (i = 0; buf16 < (short *)(rbend); i++)
+ {
+ /* Downsampling to 8KHz is a little irregular. */
+ samples = (i & 1) ? ((i % 20) ? 10 : 12) : 12;
+
+ /* And unfortunately, we don't always end on a nice boundary. */
+ if (buf16 + samples > (short *)(rbend))
+ samples = ((short *)rbend) - buf16;
+
+ /*
+ * No need to average all the values; taking the first one
+ * is sufficient and less CPU-intensive. But we do need to
+ * do both channels.
+ */
+ mono_value = (buf16[0] + buf16[1]) / 2;
+ buf16 += samples;
+ rawbuf[i] = ulawmap[mono_value];
+ }
+
+ return (i);
+}
+
+#endif /* ... && BUILD_CDDA */
diff --git a/kscd/libwm/plat_linux_cdda.c b/kscd/libwm/plat_linux_cdda.c
new file mode 100644
index 00000000..6456e10a
--- /dev/null
+++ b/kscd/libwm/plat_linux_cdda.c
@@ -0,0 +1,253 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk F�sterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Linux CDDA functions. Derived from the SUN module.
+ */
+
+#include "include/wm_cdda.h"
+
+#if defined(__linux__) && defined(BUILD_CDDA)
+
+#ifndef __GNUC__
+#define __GNUC__ 1
+#endif
+/* needed for vanilla kernel headers, which do provide __u64 only
+ for ansi */
+#undef __STRICT_ANSI__
+/* needed for non-ansi kernel headers */
+#define asm __asm__
+#define inline __inline__
+#include <asm/types.h>
+#include <linux/cdrom.h>
+#undef asm
+#undef inline
+
+#include <stdio.h>
+#include <math.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include "include/wm_struct.h"
+#include "include/wm_cdda.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+#define CDDABLKSIZE 2352
+
+/* Address of next block to read. */
+static int current_position;
+
+/* Address of last block to read. */
+static int ending_position;
+
+static struct cdrom_read_audio cdda;
+static long wmcdda_normalize(struct cdda_block *block);
+
+
+/*
+ * Initialize the CDDA data buffer and open the appropriate device.
+ *
+ */
+int
+wmcdda_init(struct cdda_device* pdev)
+{
+ int i;
+
+ if (pdev->fd > -1)
+ return -1;
+
+ if(!pdev->devname)
+ return -1;
+
+ for (i = 0; i < pdev->numblocks; i++) {
+ /* in Linux const */
+ pdev->blocks[i].buflen = pdev->frames_at_once * CDDABLKSIZE;
+ pdev->blocks[i].buf = malloc(pdev->blocks[i].buflen);
+ if (!pdev->blocks[i].buf) {
+ ERRORLOG("wmcdda_init ENOMEM\n");
+ return -ENOMEM;
+ }
+ }
+
+ pdev->fd = open(pdev->devname, O_RDONLY | O_NONBLOCK);
+
+ if (pdev->fd > -1) {
+ cdda.addr_format = CDROM_LBA;
+ cdda.addr.lba = 200;
+ cdda.nframes = 1;
+ cdda.buf = (unsigned char*)pdev->blocks[0].buf;
+
+ pdev->status = WM_CDM_STOPPED;
+ if((ioctl(pdev->fd, CDROMREADAUDIO, &cdda) < 0)) {
+ if (errno == ENXIO) {
+ /* CD ejected! */
+ pdev->status = WM_CDM_EJECTED;
+ return 0;
+ } else {
+ /* Sometimes it fails once, dunno why */
+ pdev->status = WM_CDM_CDDAERROR;
+ return 0;
+ }
+ }
+ } else {
+ ERRORLOG("canot open device, errno %i\n", errno);
+ pdev->status = WM_CDM_UNKNOWN;
+ return -1;
+ }
+
+ pdev->status = WM_CDM_UNKNOWN;
+ return 0;
+}
+
+/*
+ * Close the CD-ROM device in preparation for exiting.
+ */
+int
+wmcdda_close(struct cdda_device* pdev)
+{
+ int i;
+
+ if(-1 == pdev->fd)
+ return -1;
+
+ close(pdev->fd);
+ pdev->fd = -1;
+
+ for (i = 0; i < pdev->numblocks; i++) {
+ free(pdev->blocks[i].buf);
+ pdev->blocks[i].buf = 0;
+ pdev->blocks[i].buflen = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Set up for playing the CD. Actually this doesn't play a thing, just sets a
+ * couple variables so we'll know what to do when we're called.
+ */
+int
+wmcdda_setup(int start, int end, int realstart)
+{
+ current_position = start;
+ ending_position = end;
+
+ return 0;
+}
+
+/*
+ * Read some blocks from the CD. Stop if we hit the end of the current region.
+ *
+ * Returns number of bytes read, -1 on error, 0 if stopped for a benign reason.
+ */
+long
+wmcdda_read(struct cdda_device* pdev, struct cdda_block *block)
+{
+ if (pdev->fd < 0 && wmcdda_init(pdev)) {
+ return -1;
+ }
+
+ /* Hit the end of the CD, probably. */
+ if (current_position >= ending_position) {
+ block->status = WM_CDM_TRACK_DONE;
+ return 0;
+ }
+
+ cdda.addr_format = CDROM_LBA;
+ cdda.addr.lba = current_position - CD_MSF_OFFSET;
+ if (ending_position && current_position + pdev->frames_at_once > ending_position)
+ cdda.nframes = ending_position - current_position;
+ else
+ cdda.nframes = pdev->frames_at_once;
+
+ cdda.buf = (unsigned char*)block->buf;
+
+ if (ioctl(pdev->fd, CDROMREADAUDIO, &cdda) < 0) {
+ if (errno == ENXIO) {
+ /* CD ejected! */
+ block->status = WM_CDM_EJECTED;
+ return 0;
+ } else {
+ /* Sometimes it fails once, dunno why */
+ block->status = WM_CDM_CDDAERROR;
+ return 0;
+ }
+ }
+
+ block->track = -1;
+ block->index = 0;
+ block->frame = current_position;
+ block->status = WM_CDM_PLAYING;
+ block->buflen = cdda.nframes * CDDABLKSIZE;
+
+ current_position = current_position + cdda.nframes;
+
+ return wmcdda_normalize(block);
+}
+
+/*
+ * Normalize a bunch of CDDA data. Basically this means doing byte-swapping, since the CD audio is in
+ * littleendian format.
+ */
+long
+wmcdda_normalize(struct cdda_block *block)
+{
+#if WM_BIG_ENDIAN
+ int i;
+ int blocks = block->buflen / CDDABLKSIZE;
+ char *rawbuf = block->buf;
+ char *dest = block->buf;
+
+ while (blocks--) {
+ for (i = 0; i < CDDABLKSIZE / 2; i++) {
+ *dest++ = rawbuf[1];
+ *dest++ = rawbuf[0];
+ rawbuf += 2;
+ }
+ }
+#endif
+ return block->buflen;
+}
+
+/*
+ * Set the playback direction.
+ */
+void
+wmcdda_direction(int newdir)
+{
+}
+
+/*
+ * Do system-specific stuff to get ready to play at a particular speed.
+ */
+void
+wmcdda_speed(int speed)
+{
+}
+
+#endif
diff --git a/kscd/libwm/plat_news.c b/kscd/libwm/plat_news.c
new file mode 100644
index 00000000..0e95b01d
--- /dev/null
+++ b/kscd/libwm/plat_news.c
@@ -0,0 +1,442 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Sony NEWS-specific drive control routines.
+ */
+
+static char plat_news_id[] = "$Id$";
+
+#if defined( __sony_news) || defined(sony_news)
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <ustat.h>
+#include <CD.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+void *malloc();
+char *strchr();
+
+extern int intermittent_dev;
+
+int min_volume = 128;
+int max_volume = 255;
+
+/*
+ * Initialize the drive. A no-op for the generic driver.
+ */
+int
+gen_init( struct wm_drive *d )
+{
+ return (0);
+} /* gen_init() */
+
+/*
+ * Open the CD device and figure out what kind of drive is attached.
+ */
+int
+wmcd_open( struct wm_drive *d )
+{
+ int fd;
+ static int warned = 0;
+ char vendor[32] = WM_STR_GENVENDOR;
+ char model[32] = WM_STR_GENMODEL;
+ char rev[32] = WM_STR_GENREV;
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ intermittent_dev = 1;
+ if (d->cd_device == NULL)
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+ if ((d->fd = CD_Open(d->cd_device, 0)) < 0)
+ {
+ /* Solaris 2.2 volume manager moves links around */
+ if (errno == ENOENT && intermittent_dev)
+ return (0);
+
+ if (errno == EACCES)
+ {
+ if (!warned)
+ {
+ /*
+ char realname[MAXPATHLEN];
+
+ if (realpath(cd_device, realname) == NULL)
+ {
+ perror("realpath");
+ return 1;
+ }
+ */
+ return -EACCES;
+ }
+ }
+ else if (errno != EIO) /* defined at top */
+ {
+ return (-6);
+ }
+
+ /* No CD in drive. */
+ return (1);
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ fd = d->fd;
+
+ /* Figure out the drive type, if possible */
+ wm_scsi_get_drive_type(d, vendor, model, rev);
+ find_drive_struct(vendor, model, rev);
+
+ d->fd = fd;
+
+ (d->init)(d);
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/*
+ * Pass SCSI commands to the device.
+ */
+int
+wm_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen,
+ unsigned char *buf, int buflen, int getreply)
+{
+ /* NEWS can't do SCSI passthrough... or can it? */
+ return (-1);
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ int ret = 0;
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device.\n");
+ ret = CD_Close(d->fd);
+ d->fd = -1;
+ wm_susleep(3000000);
+ }
+ return (ret);
+}
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status( struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *index)
+{
+ struct CD_Status sc;
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ /* Is the device open? */
+ if (d->fd < 0)
+ {
+ switch (wmcd_open(d)) {
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+ /* Disc is ejected. Close the device. */
+ if (CD_GetStatus(d->fd, &sc))
+ {
+ gen_close(d);
+ return (0);
+ }
+
+ switch (sc.status) {
+ case CDSTAT_PLAY:
+ *mode = PLAYING;
+ *track = sc.tno;
+ *index = sc.index;
+ *pos = sc.baddr;
+ break;
+
+ case CDSTAT_PAUSE:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ *track = sc.tno;
+ *index = sc.index;
+ *pos = sc.baddr;
+ }
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ case CDSTAT_STOP:
+ if (oldmode == WM_CDM_PLAYING)
+ {
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ break;
+ }
+ /* fall through */
+
+ default:
+ *mode = WM_CDM_STOPPED;
+ break;
+ }
+
+ return (0);
+} /* gen_get_drive_status() */
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ struct CD_Capacity cc;
+
+ if (CD_GetCapacity(d->fd, &cc))
+ return (-1);
+
+ *tracks = cc.etrack - 1;
+ return (0);
+} /* gen_get_trackcount() */
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ */
+int
+gen_get_trackinfo( struct wm_drive *d, int track, int *data, int *startframe )
+{
+ struct CD_TOCinfo hdr;
+ struct CD_TOCdata ent;
+
+ hdr.strack = track;
+ hdr.ntrack = 1;
+ hdr.data = &ent;
+ if (CD_ReadTOC(d->fd, &hdr))
+ return (-1);
+
+ *data = (ent.control & 4) ? 1 : 0;
+ *startframe = ent.baddr;
+
+ return (0);
+} /* gen_get_trackinfo */
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen( struct wm_drive *d, int *frames )
+{
+ int tmp;
+
+ if ((d->get_trackcount)(d, &tmp))
+ return (-1);
+
+ return (gen_get_trackinfo(d, tmp + 1, &tmp, frames));
+} /* gen_get_cdlen() */
+
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play( struct wm_drive *d, int start, int end )
+{
+ struct CD_PlayAddr msf;
+
+ msf.addrmode = CD_MSF;
+ msf.addr.msf.startmsf.min = start / (60*75);
+ msf.addr.msf.startmsf.sec = (start % (60*75)) / 75;
+ msf.addr.msf.startmsf.frame = start % 75;
+ msf.addr.msf.endmsf.min = end / (60*75);
+ msf.addr.msf.endmsf.sec = (end % (60*75)) / 75;
+ msf.addr.msf.endmsf.frame = end % 75;
+
+ if (CD_Play(d->fd, &msf))
+ {
+ printf("wm_cd_play_chunk(%d,%d)\n",start,end);
+ printf("msf = %d:%d:%d %d:%d:%d\n",
+ msf.addr.msf.startmsf.min,
+ msf.addr.msf.startmsf.sec,
+ msf.addr.msf.startmsf.frame,
+ msf.addr.msf.endmsf.min,
+ msf.addr.msf.endmsf.sec,
+ msf.addr.msf.endmsf.frame);
+ perror("CD_Play");
+ return (-1);
+ }
+
+ return (0);
+} /* gen_play() */
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause( struct wm_drive *d )
+{
+ CD_Pause(d->fd);
+ return (0);
+} /* gen_pause() */
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume( struct wm_drive *d )
+{
+ CD_Restart(d->fd);
+ return (0);
+} /* gen_resume() */
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop( struct wm_drive *d )
+{
+ CD_Stop(d->fd);
+ return (0);
+} /* gen_stop() */
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject( struct wm_drive *d )
+{
+ struct stat stbuf;
+ struct ustat ust;
+
+ if (fstat(d->fd, &stbuf) != 0)
+ return (-2);
+
+ /* Is this a mounted filesystem? */
+ if (ustat(stbuf.st_rdev, &ust) == 0)
+ return (-3);
+
+ if (CD_AutoEject(d->fd))
+ return (-1);
+
+ /* Close the device if it needs to vanish. */
+ if (intermittent_dev)
+ {
+ gen_close(d);
+ }
+
+ return (0);
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * milliByte@DeathsDoor.com
+ *----------------------------------------*/
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!gen_close(d))
+ {
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume( struct wm_drive *d, int left, int right)
+{
+ /* NEWS can't adjust volume! */
+ return (0);
+}
+
+/*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume( struct wm_drive *d, omt *left, int *right)
+{
+ /* Suns, HPs, Linux, NEWS can't read the volume; oh well */
+ *left = *right = -1;
+ return (0);
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* no SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+#endif
diff --git a/kscd/libwm/plat_openbsd.c b/kscd/libwm/plat_openbsd.c
new file mode 100644
index 00000000..a3b0fd47
--- /dev/null
+++ b/kscd/libwm/plat_openbsd.c
@@ -0,0 +1,545 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * OpenBSD-specific drive control routines. (Based on plat_freebsd.c)
+ *
+ * Michael Shalayeff, 7/24/96
+ * Todd Pfaff, 3/20/94
+ *
+ */
+
+#if defined(__OpenBSD__) || defined(__OpenBSD)
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include "include/wm_config.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_helpers.h"
+
+/* this is for glibc 2.x which defines the ust structure in ustat.h not stat.h */
+#ifdef __GLIBC__
+#include <sys/ustat.h>
+#endif
+
+#include <sys/time.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/cdio.h>
+#include <sys/scsiio.h>
+#include <scsi/scsi_all.h>
+#include <scsi/cd.h>
+#include <scsi/scsi_cd.h>
+
+#include "include/wm_struct.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+void *malloc();
+
+int min_volume = 10;
+int max_volume = 255;
+
+/*
+ * Initialize the drive. A no-op for the generic driver.
+ */
+int
+gen_init(struct wm_drive *d)
+{
+ return (0);
+} /* gen_init() */
+
+
+char *cds[] = {"/dev/rcd0c", "/dev/rcd1c", "/dev/acd0c", NULL};
+
+
+/*
+ * Open the CD device and figure out what kind of drive is attached.
+ */
+int
+wmcd_open(struct wm_drive *d)
+{
+ int fd;
+ static int warned = 0;
+ char vendor[32] = WM_STR_GENVENDOR;
+ char model[32] = WM_STR_GENMODEL;
+ char rev[32] = WM_STR_GENREV;
+ int i;
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ {
+ for (i = 0; cds[i] != NULL; i++)
+ {
+ d->cd_device = cds[i];
+ d->fd = open(d->cd_device, O_RDONLY);
+ if (d->fd >= 0)
+ break;
+ }
+ }
+ else
+ d->fd = open(d->cd_device, O_RDONLY);
+ if (d->fd < 0)
+ {
+ if (errno == EIO)
+ /* No CD in drive. */
+ return 1;
+ else
+ return -errno;
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ fd = d->fd;
+
+ find_drive_struct(vendor, model, rev);
+
+ (d->init)(d);
+
+ d->fd = fd;
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/*
+ * Send an arbitrary SCSI command to a device.
+ *
+ */
+int
+wm_scsi(struct wm_drive *d, unsigned char *cdb,
+ int cdblen, void *retbuf, int retbuflen, int getreply)
+{
+ return (-1);
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ }
+ d->fd = -1;
+ return 0;
+}
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *index)
+{
+ struct ioc_read_subchannel sc;
+ struct cd_sub_channel_info scd;
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ sc.address_format = CD_MSF_FORMAT;
+ sc.data_format = CD_CURRENT_POSITION;
+ sc.track = 0;
+ sc.data_len = sizeof(scd);
+ sc.data = (struct cd_sub_channel_info *)&scd;
+
+ /* Is the device open? */
+ if (d->fd < 0)
+ {
+ switch (wmcd_open(d)) {
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+ if (ioctl(d->fd, CDIOCREADSUBCHANNEL, &sc))
+ {
+ /* we need to release the device so the kernel will notice
+ reloaded media */
+ (void) close(d->fd);
+ d->fd = -1;
+ return (0); /* ejected */
+ }
+
+ switch (scd.header.audio_status)
+ {
+ case CD_AS_PLAY_IN_PROGRESS:
+ *mode = WM_CDM_PLAYING;
+ dopos:
+ *pos = scd.what.position.absaddr.msf.minute * 60 * 75 +
+ scd.what.position.absaddr.msf.second * 75 +
+ scd.what.position.absaddr.msf.frame;
+ *track = scd.what.position.track_number;
+ *index = scd.what.position.index_number;
+ break;
+
+ case CD_AS_PLAY_PAUSED:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ goto dopos;
+ }
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ case CD_AS_PLAY_COMPLETED:
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ break;
+
+ case CD_AS_NO_STATUS:
+ case 0:
+ *mode = WM_CDM_STOPPED;
+ break;
+ }
+
+ return (0);
+} /* gen_get_drive_status() */
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ struct ioc_toc_header hdr;
+
+ if (ioctl(d->fd, CDIOREADTOCHEADER, &hdr) == -1)
+ return (-1);
+
+ *tracks = hdr.ending_track - hdr.starting_track + 1;
+ return (0);
+} /* gen_get_trackcount() */
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ *
+ * XXX - this should get cached, but that means keeping track of ejects.
+ */
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ struct ioc_read_toc_entry toc;
+ struct cd_toc_entry toc_buffer;
+
+ bzero((char *)&toc_buffer, sizeof(toc_buffer));
+ toc.address_format = CD_MSF_FORMAT;
+ toc.starting_track = track;
+ toc.data_len = sizeof(toc_buffer);
+ toc.data = &toc_buffer;
+
+ if (ioctl(d->fd, CDIOREADTOCENTRYS, &toc))
+ return (-1);
+
+ *data = ((toc_buffer.control & 0x4) != 0);
+
+ *startframe = toc_buffer.addr.msf.minute*60*75 +
+ toc_buffer.addr.msf.second * 75 +
+ toc_buffer.addr.msf.frame;
+
+ return (0);
+} /* gen_get_trackinfo() */
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ int tmp;
+ struct ioc_toc_header hdr;
+ int status;
+
+#define LEADOUT 0xaa /* see scsi.c. what a hack! */
+ return gen_get_trackinfo(d, LEADOUT, &tmp, frames);
+} /* gen_get_cdlen() */
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play(struct wm_drive *d, int start, int end)
+{
+ struct ioc_play_msf msf;
+
+ msf.start_m = start / (60*75);
+ msf.start_s = (start % (60*75)) / 75;
+ msf.start_f = start % 75;
+ msf.end_m = end / (60*75);
+ msf.end_s = (end % (60*75)) / 75;
+ msf.end_f = end % 75;
+
+ if (ioctl(d->fd, CDIOCSTART))
+ return (-1);
+
+ if (ioctl(d->fd, CDIOCPLAYMSF, &msf))
+ return (-2);
+
+ return (0);
+} /* gen_play() */
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause(struct wm_drive *d)
+{
+ return (ioctl(d->fd, CDIOCPAUSE));
+} /* gen_pause() */
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume(struct wm_drive *d)
+{
+ return (ioctl(d->fd, CDIOCRESUME));
+} /* gen_resume() */
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop(struct wm_drive *d)
+{
+ return (ioctl(d->fd, CDIOCSTOP));
+} /* gen_stop() */
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+ /* On some systems, we can check to see if the CD is mounted. */
+ struct stat stbuf;
+ struct statfs buf;
+ int rval;
+
+ if (fstat(d->fd, &stbuf) != 0)
+ return (-2);
+
+ /* Is this a mounted filesystem? */
+ if (fstatfs(stbuf.st_rdev, &buf) == 0)
+ return (-3);
+
+ rval = ioctl(d->fd, CDIOCALLOW);
+ if (rval == 0)
+ rval = ioctl(d->fd, CDIOCEJECT);
+ if (rval == 0)
+ rval = ioctl(d->fd, CDIOCPREVENT);
+ if (rval == 0)
+ rval = close(d->fd);
+ if (rval == 0)
+ d->fd = -1;
+ return rval;
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * milliByte@DeathsDoor.com
+ *----------------------------------------*/
+
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+/*
+ * scale_volume(vol, max)
+ *
+ * Return a volume value suitable for passing to the CD-ROM drive. "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ *
+ * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
+ * increases much faster toward the top end of the volume scale than it
+ * does at the bottom. To make up for this, we make the volume scale look
+ * sort of logarithmic (actually an upside-down inverse square curve) so
+ * that the volume value passed to the drive changes less and less as you
+ * approach the maximum slider setting. The actual formula looks like
+ *
+ * (max^2 - (max - vol)^2) * (max_volume - min_volume)
+ * v = --------------------------------------------------- + min_volume
+ * max^2
+ *
+ * If your system's volume settings aren't broken in this way, something
+ * like the following should work:
+ *
+ * return ((vol * (max_volume - min_volume)) / max + min_volume);
+ */
+static int
+scale_volume(int vol, int max)
+{
+ return ((vol * (max_volume - min_volume)) / max + min_volume);
+} /* scale_volume() */
+
+/*
+ * unscale_volume(cd_vol, max)
+ *
+ * Given a value between min_volume and max_volume, return the volume slider
+ * value needed to achieve that value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ */
+static int
+unscale_volume(int cd_vol, int max)
+{
+ int vol = 0, top = max, bot = 0, scaled;
+
+ while (bot <= top)
+ {
+ vol = (top + bot) / 2;
+ scaled = scale_volume(vol, max);
+ if (cd_vol == scaled)
+ break;
+ if (cd_vol < scaled)
+ top = vol - 1;
+ else
+ bot = vol + 1;
+ }
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > max)
+ vol = max;
+
+ return (vol);
+} /* unscale_volume() */
+
+/*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume(struct wm_drive *d, int left, int right)
+{
+ struct ioc_vol vol;
+
+ if (left < 0) /* don't laugh, I saw this happen once! */
+ left = 0;
+ if (right < 0)
+ right = 0;
+ left = scale_volume(left, 100);
+ right = scale_volume(right, 100);
+
+ bzero((char *)&vol, sizeof(vol));
+
+ vol.vol[LEFT_PORT] = left;
+ vol.vol[RIGHT_PORT] = right;
+
+ if (ioctl(d->fd, CDIOCSETVOL, &vol))
+ return (-1);
+
+ return (0);
+} /* gen_set_volume() */
+
+
+/*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume(struct wm_drive *d, int *left, int *right)
+{
+ struct ioc_vol vol;
+
+ if (d->fd >= 0)
+ {
+ bzero((char *)&vol, sizeof(vol));
+
+ if (ioctl(d->fd, CDIOCGETVOL, &vol))
+ *left = *right = -1;
+ else
+ {
+ *left = unscale_volume(vol.vol[LEFT_PORT], 100);
+ *right = unscale_volume(vol.vol[RIGHT_PORT], 100);
+ }
+ }
+ else
+ *left = *right = -1;
+
+ return (0);
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* no SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+#endif
diff --git a/kscd/libwm/plat_osf1.c b/kscd/libwm/plat_osf1.c
new file mode 100644
index 00000000..a2cc0afb
--- /dev/null
+++ b/kscd/libwm/plat_osf1.c
@@ -0,0 +1,674 @@
+/*
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * OSF drive control routines.
+ */
+
+
+#if defined(__osf__) || defined(__osf)
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ustat.h>
+#include <string.h>
+/* #include <sys/rzdisk.h>
+#include <sys/cdrom.h> */
+#include <io/cam/rzdisk.h>
+#include <io/cam/cdrom.h>
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdtext.h"
+
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * This structure will be filled with the TOC header and all entries.
+ * Ultrix doesn't seem to allow getting single TOC entries.
+ * - Chris Ross (cross@eng.umd.edu)
+ */
+struct cd_toc_header_and_entries
+{
+ struct cd_toc_header cdth;
+ struct cd_toc_entry cdte[CDROM_MAX_TRACK+1];
+};
+
+void *malloc();
+char *strchr();
+
+int min_volume = 128;
+int max_volume = 255;
+static char* osf_cd_device = NULL;
+
+/*
+ * fgetline()
+ *
+ * Simulate fgets, but joining continued lines in the output of uerf.
+ *
+ * platform specific internal function
+ *
+ */
+
+#define BUF_SIZE 85 /* Max length of a (real) line */
+
+char *
+fgetline( FILE *fp )
+{
+ static char *retval = NULL;
+ static char holdbuf[BUF_SIZE + 1];
+ char tmp[BUF_SIZE + 1];
+ char *stmp;
+
+ if (!retval)
+ {
+ retval = malloc(BUF_SIZE * 3); /* 3 lines can be joined */
+ if (!retval)
+ return(NULL);
+ else
+ *retval = '\0';
+ }
+
+ if (*holdbuf)
+ {
+ strcpy(retval, holdbuf);
+ retval[strlen(retval)-1] = '\0';
+ memset(holdbuf, 0, BUF_SIZE+1);
+ }
+
+ while (fgets(tmp, BUF_SIZE, fp))
+ {
+ stmp = tmp + strspn(tmp, " \t");
+ if (*stmp == '_') { /* Continuation line */
+ retval[strlen(retval)-1] = '\0'; /* Trim off C/R */
+ strcat(retval, stmp+1);
+ } else {
+ if (*retval)
+ {
+ strcpy(holdbuf, tmp);
+ holdbuf[strlen(holdbuf)-1] = -1;
+ return retval;
+ } else { /* First line read, keep reading */
+ strcat(retval, stmp);
+ retval[strlen(retval)-1] = '\0';
+ }
+ }
+ }
+ return NULL;
+} /* fgetline() */
+
+
+/*
+ * find_cdrom
+ *
+ * Determine the name of the CD-ROM device.
+ *
+ * Read through the boot records (via a call to uerf) and find the SCSI
+ * address of the CD-ROM.
+ */
+const char*
+find_cdrom()
+{
+ char *data;
+ FILE *uerf;
+ int fds[2];
+ int pid;
+ extern char *getenv();
+ const char *device = NULL;
+
+ pipe(fds);
+
+ device = getenv("CDROM");
+ /*
+ ** the path of the device has to start w/ /dev
+ ** otherwise we are vulnerable to race conditions
+ ** Thomas Biege <thomas@suse.de>
+ */
+ if ( device == NULL ||
+ strncmp("/dev/", device, 5) ||
+ strstr(device, "/../")
+ )
+ return NULL;
+
+ if ((pid = fork()) == 0)
+ {
+ close(fds[0]);
+ dup2(fds[1], 1);
+ execl("/etc/uerf", "uerf", "-R", "-r", "300", (void *)0);
+ execl("/usr/sbin/uerf", "uerf", "-R", "-r", "300", (void *)0);
+ return NULL; /* _exit(1); */
+ } else if (pid < 0) {
+ perror("fork");
+ return NULL;
+ }
+
+ close(fds[1]);
+ uerf = fdopen(fds[0], "r");
+
+ while (data = fgetline(uerf))
+ if (strstr(data, "RRD42"))
+ {
+ char *device_p;
+
+ osf_cd_device = (char *)malloc(sizeof("/dev/rrz##c"));
+ strcpy(osf_cd_device, "/dev/r");
+ device_p = strstr(data, "rz");
+ device_p[(int)(strchr(device_p, ' ') - device_p)] = '\0';
+ strcat(osf_cd_device, device_p);
+ strcat(osf_cd_device, "c");
+ device = osf_cd_device;
+ break;
+ }
+
+ fclose(uerf);
+
+ if (device == NULL)
+ {
+ fprintf(stderr,
+ "No cdrom (RRD42) is installed on this system\n");
+ return NULL;
+ }
+
+ kill(pid, 15);
+ (void)wait((int *)NULL);
+ return device;
+} /* find_cdrom() */
+
+/*
+ * Initialize the drive. A no-op for the generic driver.
+ */
+int
+gen_init( struct wm_drive *d )
+{
+ return (0);
+} /* gen_init() */
+
+/*
+ * Open the CD device and figure out what kind of drive is attached.
+ */
+int
+wmcd_open( struct wm_drive *d )
+{
+ int fd;
+ static int warned = 0;
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ d->cd_device = find_cdrom();
+
+ d->fd = open(d->cd_device, O_RDWR);
+ if (d->fd < 0)
+ {
+ if (errno == EACCES)
+ {
+ return -EACCES;
+ }
+ else if (errno != EINTR)
+ {
+ return (-6);
+ }
+
+ /* No CD in drive. */
+ return (1);
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ find_drive_struct("", "", "");
+ d->fd = fd;
+
+ (d->init)(d);
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/*
+ * Send an arbitrary SCSI command to a device.
+ */
+int
+wm_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen,
+ void *retbuf, int retbuflen, int getreply)
+{
+ /* OSF1 doesn't have a SCSI passthrough interface, does it? */
+ return (-1);
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *index)
+{
+ struct cd_sub_channel sc;
+ struct cd_subc_channel_data scd;
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ sc.sch_address_format = CDROM_MSF_FORMAT;
+ sc.sch_data_format = CDROM_CURRENT_POSITION;
+ sc.sch_track_number = 0;
+ sc.sch_alloc_length = sizeof(scd);
+ sc.sch_buffer = (caddr_t)&scd;
+
+ /* Is the device open? */
+ if (d->fd < 0)
+ {
+ switch (wmcd_open(d))
+ {
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+ if (ioctl(d->fd, CDROM_READ_SUBCHANNEL, &sc))
+ return (0); /* ejected */
+
+ switch (scd.scd_header.sh_audio_status)
+ {
+ case AS_PLAY_IN_PROGRESS:
+ *mode = WM_CDM_PLAYING;
+ dopos:
+ *pos = scd.scd_position_data.scp_absaddr.msf.m_units * 60 * 75 +
+ scd.scd_position_data.scp_absaddr.msf.s_units * 75 +
+ scd.scd_position_data.scp_absaddr.msf.f_units;
+ *track = scd.scd_position_data.scp_track_number;
+ *index = scd.scd_position_data.scp_index_number;
+ break;
+
+ case AS_PLAY_PAUSED:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ goto dopos;
+ }
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ case AS_PLAY_COMPLETED:
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ break;
+
+ case AS_NO_STATUS:
+ *mode = WM_CDM_STOPPED;
+ break;
+ default:
+ abort();
+ }
+
+ return (0);
+} /* gen_get_drive_status() */
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play( struct wm_drive *d, int start, int end )
+{
+ struct cd_play_audio_msf msf;
+
+ msf.msf_starting_M_unit = start / (60*75);
+ msf.msf_starting_S_unit = (start % (60*75)) / 75;
+ msf.msf_starting_F_unit = start % 75;
+ msf.msf_ending_M_unit = end / (60*75);
+ msf.msf_ending_S_unit = (end % (60*75)) / 75;
+ msf.msf_ending_F_unit = end % 75;
+
+ if (ioctl(d->fd, SCSI_START_UNIT))
+ return (-1);
+ if (ioctl(d->fd, CDROM_PLAY_AUDIO_MSF, &msf))
+ return (-2);
+
+ return (0);
+} /* gen_play() */
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause( struct wm_drive *d )
+{
+ return (ioctl(d->fd, CDROM_PAUSE_PLAY, 0));
+} /* gen_pause() */
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume( struct wm_drive *d )
+{
+ return (ioctl(d->fd, CDROM_RESUME_PLAY, 0));
+} /* gen_resume() */
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop( struct wm_drive *d )
+{
+ return (ioctl(d->fd, SCSI_STOP_UNIT, 0));
+} /* gen_stop() */
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+ /* On some systems, we can check to see if the CD is mounted. */
+ struct stat stbuf;
+ struct ustat ust;
+
+ if (fstat(d->fd, &stbuf) != 0)
+ return (-2);
+
+ /* Is this a mounted filesystem? */
+ if (ustat(stbuf.st_rdev, &ust) == 0)
+ return (-3);
+
+ return (ioctl(d->fd, CDROM_EJECT_CADDY, 0));
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * milliByte@DeathsDoor.com
+ *----------------------------------------*/
+
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ struct cd_toc_header hdr;
+
+ if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr))
+ return (-1);
+
+ *tracks = hdr.th_ending_track;
+
+ return (0);
+} /* gen_get_trackcount() */
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ *
+ * XXX - this should get cached, but that means keeping track of ejects.
+ */
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ struct cd_toc toc;
+ struct cd_toc_header hdr;
+ struct cd_toc_header_and_entries toc_buffer;
+
+ if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr))
+ return (-1);
+
+ bzero((char *)&toc_buffer, sizeof(toc_buffer));
+ toc.toc_address_format = CDROM_MSF_FORMAT;
+ toc.toc_starting_track = 0;
+ toc.toc_alloc_length = (u_short)(((hdr.th_data_len1 << 8) +
+ hdr.th_data_len0) & 0xfff) + 2;
+ toc.toc_buffer = (caddr_t)&toc_buffer;
+
+ if (ioctl(d->fd, CDROM_TOC_ENTRYS, &toc))
+ return (-1);
+
+ if (track == 0)
+ track = hdr.th_ending_track + 1;
+
+ *data = (toc_buffer.cdte[track-1].te_control & CDROM_DATA_TRACK) ? 1:0;
+ *startframe = toc_buffer.cdte[track - 1].te_absaddr.msf.m_units*60*75 +
+ toc_buffer.cdte[track - 1].te_absaddr.msf.s_units * 75 +
+ toc_buffer.cdte[track - 1].te_absaddr.msf.f_units;
+
+ return (0);
+} /* gen_get_trackinfo() */
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen( struct wm_drive *d, int *frames )
+{
+ int tmp;
+
+ return (gen_get_trackinfo(d, 0, &tmp, frames));
+} /* gen_get_cdlen() */
+
+/*
+ * scale_volume(vol, max)
+ *
+ * Return a volume value suitable for passing to the CD-ROM drive. "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ *
+ * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
+ * increases much faster toward the top end of the volume scale than it
+ * does at the bottom. To make up for this, we make the volume scale look
+ * sort of logarithmic (actually an upside-down inverse square curve) so
+ * that the volume value passed to the drive changes less and less as you
+ * approach the maximum slider setting. The actual formula looks like
+ *
+ * (max^2 - (max - vol)^2) * (max_volume - min_volume)
+ * v = --------------------------------------------------- + min_volume
+ * max^2
+ *
+ * If your system's volume settings aren't broken in this way, something
+ * like the following should work:
+ *
+ * return ((vol * (max_volume - min_volume)) / max + min_volume);
+ */
+scale_volume(int vol, int max)
+{
+ return ((max * max - (max - vol) * (max - vol)) *
+ (max_volume - min_volume) / (max * max) + min_volume);
+} /* scale_volume() */
+
+/*
+ * unscale_volume(cd_vol, max)
+ *
+ * Given a value between min_volume and max_volume, return the volume slider
+ * value needed to achieve that value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ */
+static int
+unscale_volume( int cd_vol, int max )
+{
+ int vol = 0, top = max, bot = 0, scaled;
+
+ while (bot <= top)
+ {
+ vol = (top + bot) / 2;
+ scaled = scale_volume(vol, max);
+ if (cd_vol == scaled)
+ break;
+ if (cd_vol < scaled)
+ top = vol - 1;
+ else
+ bot = vol + 1;
+ }
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > max)
+ vol = max;
+
+ return (vol);
+} /* unscale_volume() */
+
+/*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume(struct wm_drive *d, int left, int right)
+{
+ struct cd_playback pb;
+ struct cd_playback_status ps;
+ struct cd_playback_control pc;
+
+ left = scale_volume(left, 100);
+ right = scale_volume(right, 100);
+
+ bzero((char *)&pb, sizeof(pb));
+ bzero((char *)&ps, sizeof(ps));
+ bzero((char *)&pc, sizeof(pc));
+
+ pb.pb_alloc_length = sizeof(ps);
+ pb.pb_buffer = (caddr_t)&ps;
+
+ if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb))
+ return (-1);
+
+ pc.pc_chan0_select = ps.ps_chan0_select;
+ pc.pc_chan0_volume = (left < CDROM_MIN_VOLUME) ?
+ CDROM_MIN_VOLUME : (left > CDROM_MAX_VOLUME) ?
+ CDROM_MAX_VOLUME : left;
+ pc.pc_chan1_select = ps.ps_chan1_select;
+ pc.pc_chan1_volume = (right < CDROM_MIN_VOLUME) ?
+ CDROM_MIN_VOLUME : (right > CDROM_MAX_VOLUME) ?
+ CDROM_MAX_VOLUME : right;
+
+ pb.pb_alloc_length = sizeof(pc);
+ pb.pb_buffer = (caddr_t)&pc;
+
+ if (ioctl(d->fd, CDROM_PLAYBACK_CONTROL, &pb))
+ return (-1);
+
+ return (0);
+} /* gen_set_volume() */
+
+
+/*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ struct cd_playback pb;
+ struct cd_playback_status ps;
+
+ bzero((char *)&pb, sizeof(pb));
+ bzero((char *)&ps, sizeof(ps));
+
+ pb.pb_alloc_length = sizeof(ps);
+ pb.pb_buffer = (caddr_t)&ps;
+
+ if (d->fd >= 0)
+ {
+ if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb))
+ *left = *right = -1;
+ else
+ {
+ *left = unscale_volume(ps.ps_chan0_volume, 100);
+ *right = unscale_volume(ps.ps_chan1_volume, 100);
+ }
+ }
+ else
+ *left = *right = -1;
+
+ return (0);
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* no SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+
+#endif
diff --git a/kscd/libwm/plat_scor5.c b/kscd/libwm/plat_scor5.c
new file mode 100644
index 00000000..1e5be6e2
--- /dev/null
+++ b/kscd/libwm/plat_scor5.c
@@ -0,0 +1,426 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * SCO Openserver R5 specific. Derived from the KSCD plat_scor5.c
+ *
+ */
+
+
+static char plat_linux_id[] = "$Id$";
+
+#if defined(M_UNIX) || defined(__M_UNIX)
+
+
+#include "include/wm_config.h"
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/scsi.h>
+#include <sys/scsicmd.h>
+#include <errno.h>
+#include <macros.h>
+
+#include "include/wm_struct.h"
+#include "include/wm_scsi.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+#define SENSE_SZ EXTD_SENSE_LEN
+
+void *malloc();
+char *strchr();
+
+int min_volume = 0;
+int max_volume = 255;
+
+/*
+ * platformspecific internal function
+ */
+static int
+create_cdrom_node(char *dev_name)
+{
+ char pass_through[100];
+ int file_des;
+ struct stat sbuf;
+ int err;
+ int ccode;
+
+
+ strncpy(pass_through, dev_name, sizeof(pass_through)-2);
+ strcat(pass_through, "p" );
+
+ if (setreuid(-1,0) < 0)
+ {
+ perror("setregid/setreuid/access");
+ return -1;
+ }
+
+ ccode = access(pass_through, F_OK);
+
+ if (ccode < 0)
+ {
+
+ if (stat(dev_name, &sbuf) < 0)
+ {
+ perror("Call to get pass-through device number failed");
+ return -1;
+ }
+
+ if (mknod(pass_through, (S_IFCHR | S_IREAD | S_IWRITE),
+ sbuf.st_rdev) < 0)
+ {
+ perror("Unable to make pass-through node");
+ return -1;
+ }
+
+ if (chown(pass_through, 0 , 0) < 0)
+ {
+ perror("chown");
+ return -1;
+ }
+
+ if (chmod(pass_through, 0660 ) < 0)
+ {
+ perror("chmod");
+ return -1;
+ }
+ }
+
+ file_des = open( pass_through, O_RDONLY);
+ err = errno;
+
+ /*
+ if ( (setreuid(-1,getuid()) < 0) || (setregid(-1,getgid()) < 0) )
+ {
+ perror("setreuid/setregid");
+ exit(1);
+ }
+
+ */
+
+ errno = err;
+ return file_des;
+} /* create_cdrom_node() */
+
+
+/*
+ * Initialize the drive. A no-op for the generic driver.
+ */
+int
+gen_init( struct wm_drive *d )
+{
+ return (0);
+} /* gen_init() */
+
+/*
+ * Open the CD and figure out which kind of drive is attached.
+ */
+int
+wmcd_open( struct wm_drive *d)
+{
+ int fd;
+ static int warned = 0;
+ char vendor[9], model[17], rev[5];
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ {
+ fprintf(stderr,"cd_device string empty\n");
+ return (-1);
+ }
+
+
+ d->fd = create_cdrom_node(d->cd_device); /* this will do open */
+
+ if (d->fd < 0)
+ {
+ if (errno == EACCES)
+ {
+ if (! warned)
+ {
+ fprintf(stderr,"Cannot access %s\n",d->cd_device);
+ warned++;
+ }
+ }
+ else if (errno != EINTR)
+ {
+ perror(d->cd_device);
+ return( -6 );
+ }
+
+ /* can not acces CDROM device */
+ return (-1);
+ }
+
+ if (warned)
+ {
+ warned = 0;
+ fprintf(stderr, "Thank you.\n");
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+
+ fd = d->fd;
+
+ if (wm_scsi_get_drive_type(d, vendor, model, rev) < 0)
+ {
+ perror("Cannot inquiry drive for it's type");
+ return (-1);
+ }
+ find_drive_struct(vendor, model, rev);
+
+ d->fd = fd;
+
+ return (0);
+} /* wmcd_open */
+
+/*
+ * Re-Open the device
+ */
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+ int tries = 0;
+ do {
+ gen_close(d);
+ susleep( 1000 );
+ status = wmcd_open( d );
+ susleep( 1000 );
+ tries++;
+ } while ( (status != 0) && (tries < 10) );
+ return status;
+} /* wmcd_reopen() */
+
+/*
+ * Send a SCSI command out the bus.
+ */
+int
+wm_scsi(struct wm_drive *d, unsigned char *xcdb, int cdblen,
+ char *retbuf, int retbuflen, int getreply)
+{
+ int ccode;
+ int file_des = d->fd;
+ unsigned char sense_buffer[ SENSE_SZ ];
+
+ /* getreply == 1 is read, == 0 is write */
+
+ struct scsicmd2 sb; /* Use command with automatic sense */
+
+ if (cdblen > SCSICMDLEN)
+ {
+ fprintf(stderr,"Cannot handle longer commands than %d bytes.\n", SCSICMDLEN);
+ exit(-1);
+ }
+
+ /* Get the command */
+ memcpy(sb.cmd.cdb, xcdb, cdblen);
+ sb.cmd.cdb_len = cdblen;
+
+ /* Point to data buffer */
+ sb.cmd.data_ptr = retbuf;
+ sb.cmd.data_len = retbuflen;
+
+ /* Is this write or read ? */
+ sb.cmd.is_write = (getreply==1) ? 0 : 1;
+
+ /* Zero out return status fields */
+ sb.cmd.host_sts = 0;
+ sb.cmd.target_sts = 0;
+
+ /* Set up for possible sense info */
+
+ sb.sense_ptr = sense_buffer;
+ sb.sense_len = sizeof(sense_buffer);
+
+ ccode = ioctl(file_des, SCSIUSERCMD2, &sb);
+
+ if ( sb.cmd.target_sts != 02 )
+ return ccode;
+
+ return 0;
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+void
+keep_cd_open() { }
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status(wm_drive *d, int oldmode, int *mode,
+ int *pos, int *track, int *index)
+{
+ return (wm_scsi2_get_drive_status(d, oldmode, mode, pos, track, index));
+} /* gen_get_drive_status() */
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ return (wm_scsi2_get_trackcount(d, tracks));
+} /* gen_get_trackcount() */
+
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ */
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ return (wm_scsi2_get_trackinfo(d, track, data, startframe));
+} /* gen_get_trackinfo() */
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ return (wm_scsi2_get_cdlen(d, frames));
+} /* gen_get_cdlen() */
+
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play(struct wm_drive *d, int start, int end)
+{
+ return (wm_scsi2_play(d, start, end));
+} /* gen_play() */
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause( struct wm_drive *d )
+{
+ return (wm_scsi2_pause(d));
+} /* gen_pause() */
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume( struct wm_drive *d )
+{
+ return (wm_scsi2_resume(d));
+} /* gen_resume() */
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop(struct wm_drive *d)
+{
+ return (wm_scsi2_stop(d));
+} /* gen_stop() */
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+ int stat;
+
+ stat = wm_scsi2_eject(d);
+ sleep(2);
+ return (stat);
+} /* gen_eject() */
+
+
+int
+gen_closetray(struct wm_drive *d)
+{
+ return(wm_scsi2_closetray(d));
+} /* gen_closetray() */
+
+
+/*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume(struct wm_drive *d, int left, int right)
+{
+ return (wm_scsi2_set_volume(d, left, right));
+} /* gen_set_volume() */
+
+/*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume(struct wm_drive *d, int *left, int *right)
+{
+ return (wm_scsi2_get_volume(d, left, right));
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ /* This needs to be tested! */
+ return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght);
+} /* gen_get_cdtext() */
+
+
+
+#endif /* M_UNIX */
+
+
+
+
diff --git a/kscd/libwm/plat_sun.c b/kscd/libwm/plat_sun.c
new file mode 100644
index 00000000..f83a6cd8
--- /dev/null
+++ b/kscd/libwm/plat_sun.c
@@ -0,0 +1,972 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Sun-specific drive control routines.
+ */
+
+static char plat_sun_id[] = "$Id$";
+
+#if defined(sun) || defined(__sun)
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+#include "include/wm_config.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_cdtext.h"
+
+#include <ustat.h>
+#include <unistd.h>
+#include <signal.h>
+#ifdef solbourne
+# include <mfg/dklabel.h>
+# include <mfg/dkio.h>
+# include <sys/unistd.h>
+# include <dev/srvar.h>
+#else /* A real Sun */
+# ifdef SYSV
+# include <poll.h>
+# include <stdlib.h>
+# include <sys/cdio.h>
+# include <sys/socket.h>
+# include <sys/scsi/impl/uscsi.h>
+# include "include/wm_cdda.h"
+# else
+# include <sys/buf.h>
+# include <sun/dkio.h>
+# include <scsi/targets/srdef.h>
+# include <scsi/impl/uscsi.h>
+# include <scsi/generic/commands.h>
+# endif
+#endif
+
+#include "include/wm_struct.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+int min_volume = 0;
+int max_volume = 255;
+
+static const char *sun_cd_device = NULL;
+extern int intermittent_dev;
+
+int current_end;
+
+#if defined(SYSV) && defined(SIGTHAW)
+#ifdef __GNUC__
+void sigthawinit(void) __attribute__ ((constructor));
+#else
+#pragma init(sigthawinit)
+#endif /* GNUC */
+
+static int last_left, last_right;
+static struct wm_drive *thecd = NULL;
+
+/*
+ * Handling for Sun's Suspend functionality
+ */
+static void
+thawme(int sig)
+{
+// Just leave this line in as a reminder for a missing
+// functionality in the GUI.
+// change_mode(NULL, WM_CDM_STOPPED, NULL);
+ codec_init();
+ if( thecd )
+ gen_set_volume(thecd, last_left, last_right);
+} /* thawme() */
+
+void
+sigthawinit( void )
+{
+ struct sigaction sa;
+
+ sa.sa_handler = thawme;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+
+ sigaction(SIGTHAW, &sa, NULL);
+} /* sigthawinit() */
+
+#endif /* SYSV && SIGTHAW */
+
+/*
+ * find_cdrom
+ *
+ * Determine the name of the CD-ROM device.
+ *
+ * Use the first of /vol/dev/aliases/cdrom0, /dev/rdsk/c0t6d0s2, and /dev/rsr0
+ * that exists. (Check for /vol/dev/aliases, not cdrom0, since it won't be
+ * there if there's no CD in the drive.) This is done so a single SunOS 4.x
+ * binary can be used on any 4.x or higher Sun system.
+ */
+const char*
+find_cdrom( void )
+{
+ if (access("/vol/dev/aliases", X_OK) == 0)
+ {
+ /* Volume manager. Device might not be there. */
+ intermittent_dev = 1;
+
+ /* If vold is running us, it'll tell us the device name. */
+ sun_cd_device = getenv("VOLUME_DEVICE");
+ /*
+ ** the path of the device has to include /dev
+ ** otherwise we are vulnerable to race conditions
+ ** Thomas Biege <thomas@suse.de>
+ */
+ if (sun_cd_device == NULL ||
+ strncmp("/vol/dev/", sun_cd_device, 9) ||
+ strstr(sun_cd_device, "/../") )
+ return "/vol/dev/aliases/cdrom0";
+ else
+ return sun_cd_device;
+ }
+ else if (access("/dev/rdsk/c0t6d0s2", F_OK) == 0)
+ {
+ /* Solaris 2.x w/o volume manager. */
+ return "/dev/rdsk/c0t6d0s2";
+ }
+ else if (access("/dev/rcd0", F_OK) == 0)
+ {
+ return "/dev/rcd0";
+ }
+ else if (access("/dev/rsr0", F_OK) == 0)
+ return "/dev/rsr0";
+ else
+ {
+ fprintf(stderr, "Couldn't find a CD device!\n");
+ return NULL;
+ }
+} /* find_cdrom() */
+
+/*
+ * Initialize the drive. A no-op for the generic driver.
+ */
+int
+gen_init( struct wm_drive *d )
+{
+ codec_init();
+ return (0);
+} /* gen_init() */
+
+
+/*
+ * Open the CD device and figure out what kind of drive is attached.
+ */
+int
+wmcd_open( struct wm_drive *d )
+{
+ static int warned = 0;
+ char vendor[32] = WM_STR_GENVENDOR;
+ char model[32] = WM_STR_GENMODEL;
+ char rev[32] = WM_STR_GENREV;
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ d->cd_device = find_cdrom();
+
+ d->fd = open(d->cd_device, 0);
+ if (d->fd < 0)
+ {
+ /* Solaris 2.2 volume manager moves links around */
+ if (errno == ENOENT && intermittent_dev)
+ return (1);
+
+ if (errno == EACCES)
+ {
+ if (!warned)
+ {
+ /*
+ char realname[MAXPATHLEN];
+
+ if (realpath(cd_device, realname) == NULL)
+ {
+ perror("realpath");
+ return (1);
+ }
+ */
+ return -EACCES;
+ }
+ }
+ else if (errno != ENXIO)
+ {
+ return( -6 );
+ }
+
+ /* No CD in drive. */
+ return (1);
+ }
+
+ /*
+ * See if we can do digital audio.
+ */
+#if defined(BUILD_CDDA)
+ if(d->cdda) {
+ if (!gen_cdda_init(d))
+ /* WARNING: Old GUI call. How could this survive? */
+ enable_cdda_controls(1);
+ else {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): failed in gen_cdda_init\n");
+ gen_close(d);
+ return -1;
+ }
+#endif
+
+ /* Can we figure out the drive type? */
+ if (wm_scsi_get_drive_type(d, vendor, model, rev))
+ {
+ if (errno == EPERM)
+ {
+ /*
+ * Solaris 2.4 seems to refuse to do USCSICMD ioctls
+ * when not running as root. SunOS 4.x allows it
+ * as an unprivileged user, though.
+ */
+ fprintf(stderr, "Warning: WorkMan can't adapt itself to your drive unless it runs as root.\n");
+ } else {
+ fprintf(stderr, "Warning: WorkMan couldn't determine drive type\n");
+ }
+ strcpy(vendor, "Generic");
+ strcpy(model, "drive type");
+ strcpy(rev, "");
+ }
+
+ find_drive_struct(vendor, model, rev);
+
+ (d->proto->gen_init)(d);
+ thecd = d;
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ gen_close(d);
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+
+#ifndef solbourne
+/*
+ * Send an arbitrary SCSI command out the bus and optionally wait for
+ * a reply if "retbuf" isn't NULL.
+ */
+int
+wm_scsi( struct wm_drive *d,
+ unsigned char *cdb,
+ int cdblen, void *retbuf,
+ int retbuflen, int getreply )
+{
+ char x;
+ struct uscsi_cmd cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.uscsi_cdb = (void *) cdb;
+ cmd.uscsi_cdblen = cdblen;
+ cmd.uscsi_bufaddr = retbuf ? retbuf : (void *)&x;
+ cmd.uscsi_buflen = retbuf ? retbuflen : 0;
+ cmd.uscsi_flags = USCSI_ISOLATE | USCSI_SILENT;
+ if (getreply)
+ cmd.uscsi_flags |= USCSI_READ;
+
+ if (ioctl(d->fd, USCSICMD, &cmd))
+ return (-1);
+
+ if (cmd.uscsi_status)
+ return (-1);
+
+ return (0);
+}
+#else
+int wm_scsi() { return (-1); }
+#endif
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/* Alarm signal handler. */
+static void do_nothing( int x ) { x++; }
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status( struct wm_drive *d,
+ int oldmode,
+ int *mode,
+ int *pos, int *track, int *index )
+{
+ struct cdrom_subchnl sc;
+ struct itimerval old_timer, new_timer;
+ struct sigaction old_sig, new_sig;
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ /* Is the device open? */
+ if (d->fd < 0)
+ {
+ switch (wmcd_open(d))
+ {
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+#if defined(BUILD_CDDA)
+ if (oldmode == WM_CDM_PAUSED || oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_STOPPED) {
+ CDDARETURN(d) cdda_get_drive_status(d, oldmode, mode, pos, track, index);
+ }
+#endif
+
+ /*
+ * Solaris 2.2 hangs on this ioctl if someone else ejects the CD.
+ * So we schedule a signal to break out of the hang if the call
+ * takes an unreasonable amount of time. The signal handler and
+ * timer are restored immediately to avoid interfering with XView.
+ */
+ if (intermittent_dev)
+ {
+ /*
+ * First clear out the timer so XView's signal doesn't happen
+ * while we're diddling with the signal handler.
+ */
+ timerclear(&new_timer.it_interval);
+ timerclear(&new_timer.it_value);
+ setitimer(ITIMER_REAL, &new_timer, &old_timer);
+
+ /*
+ * Now install the no-op signal handler.
+ */
+ new_sig.sa_handler = do_nothing;
+ memset(&new_sig.sa_mask, 0, sizeof(new_sig.sa_mask));
+ new_sig.sa_flags = 0;
+ if (sigaction(SIGALRM, &new_sig, &old_sig))
+ perror("sigaction");
+
+ /*
+ * And finally, set the timer.
+ */
+ new_timer.it_value.tv_sec = 2;
+ setitimer(ITIMER_REAL, &new_timer, NULL);
+ }
+
+ sc.cdsc_format = CDROM_MSF;
+
+ if (ioctl(d->fd, CDROMSUBCHNL, &sc))
+ {
+ if (intermittent_dev)
+ {
+ sigaction(SIGALRM, &old_sig, NULL);
+ setitimer(ITIMER_REAL, &old_timer, NULL);
+
+ /* If the device can disappear, let it do so. */
+ close(d->fd);
+ d->fd = -1;
+ }
+
+ return (0);
+ }
+
+ if (intermittent_dev)
+ {
+ sigaction(SIGALRM, &old_sig, NULL);
+ setitimer(ITIMER_REAL, &old_timer, NULL);
+ }
+
+ switch (sc.cdsc_audiostatus) {
+ case CDROM_AUDIO_PLAY:
+ *mode = WM_CDM_PLAYING;
+ *track = sc.cdsc_trk;
+ *index = sc.cdsc_ind;
+ *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
+ sc.cdsc_absaddr.msf.second * 75 +
+ sc.cdsc_absaddr.msf.frame;
+ break;
+
+ case CDROM_AUDIO_PAUSED:
+ case CDROM_AUDIO_INVALID:
+ case CDROM_AUDIO_NO_STATUS:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ *track = sc.cdsc_trk;
+ *index = sc.cdsc_ind;
+ *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
+ sc.cdsc_absaddr.msf.second * 75 +
+ sc.cdsc_absaddr.msf.frame;
+ }
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ /* CD ejected manually during play. */
+ case CDROM_AUDIO_ERROR:
+ break;
+
+ case CDROM_AUDIO_COMPLETED:
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ break;
+
+ default:
+ *mode = WM_CDM_UNKNOWN;
+ break;
+ }
+
+ return (0);
+} /* gen_get_drive_status() */
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount( struct wm_drive *d, int *tracks )
+{
+ struct cdrom_tochdr hdr;
+
+ if (ioctl(d->fd, CDROMREADTOCHDR, &hdr))
+ return (-1);
+
+ *tracks = hdr.cdth_trk1;
+ return (0);
+} /* gen_get_trackcount() */
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ */
+int
+gen_get_trackinfo( struct wm_drive *d, int track, int *data, int *startframe)
+{
+ struct cdrom_tocentry entry;
+
+ entry.cdte_track = track;
+ entry.cdte_format = CDROM_MSF;
+
+ if (ioctl(d->fd, CDROMREADTOCENTRY, &entry))
+ return (-1);
+
+ *startframe = entry.cdte_addr.msf.minute * 60 * 75 +
+ entry.cdte_addr.msf.second * 75 +
+ entry.cdte_addr.msf.frame;
+ *data = entry.cdte_ctrl & CDROM_DATA_TRACK ? 1 : 0;
+
+ return (0);
+} /* gen_get_trackinfo() */
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen(struct wm_drive *d, int *frames )
+{
+ int tmp;
+
+ return (gen_get_trackinfo(d, CDROM_LEADOUT, &tmp, frames));
+} /* gen_get_cdlen() */
+
+/*
+ * Play the CD from one position to another.
+ *
+ * d Drive structure.
+ * start Frame to start playing at.
+ * end End of this chunk.
+ * realstart Beginning of this chunk (<= start)
+ */
+int
+gen_play( struct wm_drive *d, int start, int end, int realstart)
+{
+ struct cdrom_msf msf;
+ unsigned char cmdbuf[10];
+
+ current_end = end;
+
+ CDDARETURN(d) cdda_play(d, start, end, realstart);
+
+ msf.cdmsf_min0 = start / (60*75);
+ msf.cdmsf_sec0 = (start % (60*75)) / 75;
+ msf.cdmsf_frame0 = start % 75;
+ msf.cdmsf_min1 = end / (60*75);
+ msf.cdmsf_sec1 = (end % (60*75)) / 75;
+ msf.cdmsf_frame1 = end % 75;
+
+ codec_start();
+ if (ioctl(d->fd, CDROMSTART))
+ return (-1);
+ if (ioctl(d->fd, CDROMPLAYMSF, &msf))
+ return (-2);
+
+ return (0);
+} /* gen_play() */
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause( struct wm_drive *d )
+{
+ CDDARETURN(d) cdda_pause(d);
+
+ codec_stop();
+ return (ioctl(d->fd, CDROMPAUSE));
+} /* gen_pause() */
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume( struct wm_drive *d )
+{
+ CDDARETURN(d) cdda_pause(d);
+
+ codec_start();
+ return (ioctl(d->fd, CDROMRESUME));
+} /* gen_resume() */
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop( struct wm_drive *d )
+{
+ CDDARETURN(d) cdda_stop(d);
+
+ codec_stop();
+ return (ioctl(d->fd, CDROMSTOP));
+} /* gen_stop() */
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject( struct wm_drive *d )
+{
+ struct stat stbuf;
+ struct ustat ust;
+
+ if (fstat(d->fd, &stbuf) != 0)
+ return (-2);
+
+ /* Is this a mounted filesystem? */
+ if (ustat(stbuf.st_rdev, &ust) == 0)
+ return (-3);
+
+ IFCDDA(d) {
+ cdda_eject(d);
+ }
+
+ if (ioctl(d->fd, CDROMEJECT))
+ return (-1);
+
+ /* Close the device if it needs to vanish. */
+ if (intermittent_dev)
+ {
+ close(d->fd);
+ d->fd = -1;
+ /* Also remember to tell the cddaslave since volume
+ manager switches links around on us */
+ if (d->cdda_slave > -1)
+ {
+ write(d->cdda_slave, "E", 1);
+ cdda_get_ack(d->cdda_slave);
+ }
+ }
+
+ return (0);
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * milliByte@DeathsDoor.com
+ *----------------------------------------*/
+
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+/*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume( struct wm_drive *d, int left, int right )
+{
+ struct cdrom_volctrl v;
+
+#if defined(SIGTHAW) && defined(SYSV)
+ last_left = left;
+ last_right = right;
+ thecd = d;
+#endif
+
+ CDDARETURN(d) cdda_set_volume(d, left, right);
+
+ left = (left * (max_volume - min_volume)) / 100 + min_volume;
+ right = (right * (max_volume - min_volume)) / 100 + min_volume;
+
+ v.channel0 = left < 0 ? 0 : left > 255 ? 255 : left;
+ v.channel1 = right < 0 ? 0 : right > 255 ? 255 : right;
+
+ return (ioctl(d->fd, CDROMVOLCTRL, &v));
+} /* gen_set_volume() */
+
+/*
+ * Read the volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ CDDARETURN(d) cdda_get_volume(d, left, right);
+
+ *left = *right = -1;
+
+ return (wm_scsi2_get_volume(d, left, right));
+} /* gen_get_volume() */
+
+#ifdef BUILD_CDDA
+
+/*
+ * Try to initialize the CDDA slave. Returns 0 on success.
+ */
+int
+gen_cdda_init( struct wm_drive *d )
+{
+ int slavefds[2];
+
+ if (d->cdda_slave > -1)
+ return (0);
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, slavefds))
+ {
+ perror("socketpair");
+ return (-1);
+ }
+
+ switch (fork())
+ {
+ case 0:
+ close(slavefds[0]);
+ dup2(slavefds[1], 1);
+ dup2(slavefds[1], 0);
+ close(slavefds[1]);
+ close(d->fd);
+ /* Try the default path first. */
+ execl(cddaslave_path, cddaslave_path, d->cd_device, (void *)0);
+ /* Search $PATH if that didn't work. */
+ execlp("cddaslave", "cddaslave", d->cd_device, (void *)0);
+ perror(cddaslave_path);
+ exit(1);
+
+ case -1:
+ close(slavefds[0]);
+ close(slavefds[1]);
+ perror("fork");
+ return (-2);
+ }
+
+ close(slavefds[1]);
+ d->cdda_slave = slavefds[0];
+
+ if (!cdda_get_ack(d->cdda_slave))
+ {
+ d->cdda_slave = -1;
+ codec_start();
+ return (-3);
+ }
+
+ return (0);
+}
+
+#endif /* BUILD_CDDA */
+
+/*
+ * The following code activates the internal CD audio passthrough on
+ * SPARCstation 5 systems (and possibly others.)
+ *
+ * Thanks to <stevep@ctc.ih.att.com>, Roger Oscarsson <roger@cs.umu.se>
+ * and Steve McKinty <>
+ *
+ * Most CD drives have a headphone socket on the front, but it
+ * is often more convenient to route the audio though the
+ * built-in audio device. That way the user can leave their
+ * headphones plugged-in to the base system, for use with
+ * other audio stuff like ShowMeTV
+ */
+
+#ifdef CODEC /* { */
+#ifdef SYSV /* { */
+
+# include <sys/ioctl.h>
+# include <sys/audioio.h>
+# include <stdlib.h>
+
+#else /* } { */
+
+# include <sun/audioio.h>
+# define AUDIO_DEV_SS5STYLE 5
+typedef int audio_device_t;
+
+#endif /* } */
+#endif /* } */
+
+/*
+ * Don't do anything with /dev/audio if we can't set it to high quality.
+ * Also, don't do anything real if it's not Solaris.
+ */
+#if !defined(AUDIO_ENCODING_LINEAR) || !defined(CODEC) || !defined(SYSV) /* { */
+codec_init() { return 0; }
+codec_start() { return 0; }
+codec_stop() { return 0; }
+#else
+
+#ifndef AUDIO_INTERNAL_CD_IN
+#define AUDIO_INTERNAL_CD_IN 0x4
+#endif
+
+static char* devname = 0;
+static char* ctlname = 0;
+static int ctl_fd = -1;
+static int port = AUDIO_LINE_IN;
+int internal_audio = 1;
+
+codec_init( void )
+{
+ register int i;
+ char* ctlname;
+ audio_info_t foo;
+ audio_device_t aud_dev;
+
+ if (internal_audio == 0)
+ {
+ ctl_fd = -1;
+ return(0);
+ }
+
+ if (!(devname = getenv("AUDIODEV"))) devname = "/dev/audio";
+ ctlname = strcat(strcpy(malloc(strlen(devname) + 4), devname), "ctl");
+ if ((ctl_fd = open(ctlname, O_WRONLY, 0)) < 0)
+ {
+ perror(ctlname);
+ return -1;
+ }
+ if (ioctl(ctl_fd, AUDIO_GETDEV, &aud_dev) < 0)
+ {
+ close(ctl_fd);
+ ctl_fd = -1;
+ return -1;
+ }
+ /*
+ * Instead of filtering the "OLD_SUN_AUDIO", try to find the new ones.
+ * Not sure if this is all correct.
+ */
+#ifdef SYSV
+ if (strcmp(aud_dev.name, "SUNW,CS4231") &&
+ strcmp(aud_dev.name, "SUNW,sb16") &&
+ strcmp(aud_dev.name, "SUNW,sbpro"))
+#else
+ if (aud_dev != AUDIO_DEV_SS5STYLE)
+#endif
+ {
+ close(ctl_fd);
+ ctl_fd = -1;
+ return 0; /* but it's okay */
+ }
+
+ /*
+ * Does the chosen device have an internal CD port?
+ * If so, use it. If not then try and use the
+ * Line In port.
+ */
+ if (ioctl(ctl_fd, AUDIO_GETINFO, &foo) < 0)
+ {
+ perror("AUDIO_GETINFO");
+ close(ctl_fd);
+ ctl_fd = -1;
+ return(-1);
+ }
+ if (foo.record.avail_ports & AUDIO_INTERNAL_CD_IN)
+ port = AUDIO_INTERNAL_CD_IN;
+ else
+ port = AUDIO_LINE_IN;
+
+ /*
+ * now set it up to use it. See audio(7I)
+ */
+
+ AUDIO_INITINFO(&foo);
+ foo.record.port = port;
+ foo.record.balance = foo.play.balance = AUDIO_MID_BALANCE;
+#ifdef BUILD_CDDA
+ if (d->cdda_slave > -1)
+ foo.monitor_gain = 0;
+ else
+#endif
+ foo.monitor_gain = AUDIO_MAX_GAIN;
+ /*
+ * These next ones are tricky. The voulme will depend on the CD drive
+ * volume (set by the knob on the drive and/or by workman's volume
+ * control), the audio device record gain and the audio device
+ * play gain. For simplicity we set the latter two to something
+ * reasonable, but we don't force them to be reset if the user
+ * wants to change them.
+ */
+ foo.record.gain = (AUDIO_MAX_GAIN * 80) / 100;
+ foo.play.gain = (AUDIO_MAX_GAIN * 40) / 100;
+
+ ioctl(ctl_fd, AUDIO_SETINFO, &foo);
+ return 0;
+}
+
+static int
+kick_codec( void )
+{
+ audio_info_t foo;
+ int dev_fd;
+ int retval = 0;
+
+ /*
+ * Open the audio device, not the control device. This
+ * will fail if someone else has taken it.
+ */
+
+ if ((dev_fd = open(devname, O_WRONLY|O_NDELAY, 0)) < 0)
+ {
+ perror(devname);
+ return -1;
+ }
+
+ AUDIO_INITINFO(&foo);
+ foo.record.port = port;
+ foo.monitor_gain = AUDIO_MAX_GAIN;
+
+ /* These can only be set on the real device */
+ foo.play.sample_rate = 44100;
+ foo.play.channels = 2;
+ foo.play.precision = 16;
+ foo.play.encoding = AUDIO_ENCODING_LINEAR;
+
+ if ((retval = ioctl(dev_fd, AUDIO_SETINFO, &foo)) < 0)
+ perror(devname);
+
+ close(dev_fd);
+ return retval;
+} /* kick_codec() */
+
+codec_start( void )
+{
+ audio_info_t foo;
+
+ if (ctl_fd < 0)
+ return 0;
+
+ if (ioctl(ctl_fd, AUDIO_GETINFO, &foo) < 0)
+ return -1;
+
+ if (foo.play.channels != 2) return kick_codec();
+ if (foo.play.encoding != AUDIO_ENCODING_LINEAR) return kick_codec();
+ if (foo.play.precision != 16) return kick_codec();
+ if (foo.play.sample_rate != 44100) return kick_codec();
+
+ if (foo.record.channels != 2) return kick_codec();
+ if (foo.record.encoding != AUDIO_ENCODING_LINEAR) return kick_codec();
+ if (foo.record.precision != 16) return kick_codec();
+ if (foo.record.sample_rate != 44100) return kick_codec();
+
+ if (foo.monitor_gain != AUDIO_MAX_GAIN) return kick_codec();
+ if (foo.record.port != port) return kick_codec();
+
+ return 0;
+} /* codec_start() */
+
+codec_stop( void ) { return 0; }
+
+#endif /* CODEC } */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ /* This needs to be tested */
+ return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght);
+} /* gen_get_cdtext() */
+
+#endif /* sun */
diff --git a/kscd/libwm/plat_sun_audio.c b/kscd/libwm/plat_sun_audio.c
new file mode 100644
index 00000000..da9fe12f
--- /dev/null
+++ b/kscd/libwm/plat_sun_audio.c
@@ -0,0 +1,493 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Sun (really Solaris) digital audio functions.
+ */
+
+#include "include/wm_config.h"
+
+static char plat_sun_audio_id[] = "$Id$";
+
+#if defined(sun) || defined(__sun) && defined(SYSV) && defined(BUILD_CDDA) && defined(WMCDDA_DONE)
+
+
+#include "include/wm_cdda.h"
+
+/* types.h included by wmcdda.h */
+
+#include <stdio.h>
+#include <malloc.h>
+#include <sys/ioctl.h>
+#include <sys/audioio.h>
+#include <sys/stropts.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * Since there's a lag time between writing audio to the audio device and
+ * hearing it, we need to make sure the status indicators correlate to what's
+ * playing out the speaker. Luckily, Solaris gives us some audio
+ * synchronization facilities that make this pretty easy.
+ *
+ * We maintain a circular queue of status information. When we write some
+ * sound to the audio device, we put its status info into the queue. We write
+ * a marker into the audio stream; when the audio device driver encounters the
+ * marker, it increments a field in a status structure. When we see that
+ * field go up, we grab the next status structure from the queue and send it
+ * to the parent process.
+ *
+ * The minimum size of the queue depends on the latency of the audio stream.
+ */
+#define QSIZE 500
+
+struct cdda_block queue[QSIZE];
+int qtail;
+int qstart;
+
+/*
+ * We only send WMCDDA_PLAYED status messages upstream when the CD is supposed
+ * to be playing; this is used to keep track.
+ */
+extern int playing;
+
+static int aufd, aucfd;
+static int raw_audio = 1; /* Can /dev/audio take 44.1KHz stereo? */
+
+/*
+ * For fast linear-to-ulaw mapping, we use a lookup table that's generated
+ * at startup.
+ */
+unsigned char *ulawmap, linear_to_ulaw();
+
+char *getenv();
+
+/*
+ * Dummy signal handler so writes to /dev/audio will interrupt.
+ */
+static void
+dummy( void )
+{
+ signal(SIGALRM, dummy);
+}
+
+/*
+ * Initialize the audio device.
+ */
+void
+wmaudio_init( void )
+{
+ audio_info_t info;
+ char *audiodev, *acdev;
+ int linval;
+
+ audiodev = getenv("AUDIODEV");
+ if (audiodev == NULL ||
+ strncmp("/dev/", audiodev, 5) ||
+ strstr(audiodev, "/../") )
+ audiodev = "/dev/audio";
+
+ acdev = malloc(strlen(audiodev) + 4);
+ if (acdev == NULL)
+ {
+ perror("Can't allocate audio control filename");
+ exit(1);
+ }
+ strcpy(acdev, audiodev);
+ strcat(acdev, "ctl");
+
+ aucfd = open(acdev, O_WRONLY, 0);
+ if (aucfd < 0)
+ {
+ perror(acdev);
+ exit(1);
+ }
+ free(acdev);
+
+ aufd = open(audiodev, O_WRONLY, 0);
+ if (aufd < 0)
+ {
+ perror(audiodev);
+ exit(1);
+ }
+
+ signal(SIGALRM, dummy);
+
+ /*
+ * Try to set the device to CD-style audio; we can process it
+ * with the least CPU overhead.
+ */
+ AUDIO_INITINFO(&info);
+ info.play.sample_rate = 44100;
+ info.play.channels = 2;
+ info.play.precision = 16;
+ info.play.encoding = AUDIO_ENCODING_LINEAR;
+ info.play.pause = 0;
+ info.record.pause = 0;
+ info.monitor_gain = 0;
+
+ if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+ if (errno == EINVAL)
+ {
+ /*
+ * Oh well, so much for that idea.
+ */
+ AUDIO_INITINFO(&info);
+ info.play.sample_rate = 8000;
+ info.play.channels = 1;
+ info.play.precision = 8;
+ info.play.encoding = AUDIO_ENCODING_ULAW;
+ info.play.pause = 0;
+ info.record.pause = 0;
+ info.monitor_gain = 0;
+ if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+ {
+ perror("Can't set up audio device");
+ exit(1);
+ }
+
+ /*
+ * Initialize the linear-to-ulaw mapping table.
+ */
+ if (ulawmap == NULL)
+ ulawmap = malloc(65536);
+ if (ulawmap == NULL)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ for (linval = 0; linval < 65536; linval++)
+ ulawmap[linval] = linear_to_ulaw(linval-32768);
+ ulawmap += 32768;
+ raw_audio = 0;
+ }
+ else
+ {
+ perror(audiodev);
+ exit(1);
+ }
+}
+
+/*
+ * Get ready to play some sound.
+ */
+void
+wmaudio_ready( void )
+{
+ audio_info_t info;
+
+ /*
+ * Start at the correct queue position.
+ */
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ qtail = info.play.eof % QSIZE;
+ qstart = qtail;
+
+ queue[qtail].status = WMCDDA_OK;
+}
+
+/*
+ * Stop the audio immediately.
+ */
+void
+wmaudio_stop( void )
+{
+ if (ioctl(aufd, I_FLUSH, FLUSHRW) < 0)
+ perror("flush");
+}
+
+/*
+ * Close the audio device.
+ */
+void
+wmaudio_close( void )
+{
+ wmaudio_stop();
+ close(aufd);
+ close(aucfd);
+}
+
+/*
+ * Set the volume level.
+ */
+void
+wmaudio_volume(int level)
+{
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ info.play.gain = level;
+ if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO");
+}
+
+/*
+ * Set the balance level.
+ */
+void
+wmaudio_balance(int level)
+{
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ level *= AUDIO_RIGHT_BALANCE;
+ info.play.balance = level / 255;
+ if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO");
+}
+
+/*
+ * Mark the most recent audio block on the queue as the last one.
+ */
+void
+wmaudio_mark_last( void )
+{
+ queue[qtail].status = WMCDDA_DONE;
+}
+
+/*
+ * Figure out the most recent status information and send it upstream.
+ */
+int
+wmaudio_send_status( void )
+{
+ audio_info_t info;
+ int qhead;
+
+ /*
+ * Now send the most current status information to our parent.
+ */
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+ perror("AUDIO_GETINFO");
+ qhead = info.play.eof % QSIZE;
+
+ if (qhead != qstart && playing)
+ {
+ int balance;
+
+ if (queue[qhead].status != WMCDDA_DONE)
+ queue[qhead].status = WMCDDA_PLAYED;
+ queue[qhead].volume = info.play.gain;
+ queue[qhead].balance = (info.play.balance * 255) /
+ AUDIO_RIGHT_BALANCE;
+
+ send_status(queue + qhead);
+ qstart = -1;
+ }
+
+ return (queue[qhead].status == WMCDDA_DONE);
+}
+
+/*
+ * Play some audio and pass a status message upstream, if applicable.
+ * Returns 0 on success.
+ */
+int
+wmaudio_play(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+ int i;
+ short *buf16;
+ int alarmcount = 0;
+ struct itimerval it;
+
+ alarm(1);
+
+ while (write(aufd, rawbuf, buflen) <= 0)
+ if (errno == EINTR)
+ {
+ if (! raw_audio && alarmcount++ < 5)
+ {
+ /*
+ * 8KHz /dev/audio blocks for several seconds
+ * waiting for its queue to drop below a low
+ * water mark.
+ */
+ wmaudio_send_status();
+ timerclear(&it.it_interval);
+ timerclear(&it.it_value);
+ it.it_value.tv_usec = 500000;
+ setitimer(ITIMER_REAL, &it, NULL);
+ continue;
+ }
+
+/* close(aufd);
+ close(aucfd);
+ wmaudio_init();
+*/ wmaudio_stop();
+ alarm(2);
+ continue;
+ }
+ else
+ {
+ blk->status = WMCDDA_ERROR;
+ return (-1);
+ }
+ alarm(0);
+
+ /*
+ * Mark this spot in the audio stream.
+ *
+ * Marks don't always succeed (if the audio buffer is empty
+ * this call will block forever) so do it asynchronously.
+ */
+ fcntl(aufd, F_SETFL, O_NONBLOCK);
+ if (write(aufd, rawbuf, 0) < 0)
+ {
+ if (errno != EAGAIN)
+ perror("audio mark");
+ }
+ else
+ qtail = (qtail + 1) % QSIZE;
+
+ fcntl(aufd, F_SETFL, 0);
+
+ queue[qtail] = *blk;
+
+ if (wmaudio_send_status() < 0)
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * Get the current audio state.
+ */
+void
+wmaudio_state(struct cdda_block *blk)
+{
+ audio_info_t info;
+ int balance;
+
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+ perror("AUDIO_GETINFO");
+ blk->volume = info.play.gain;
+ blk->balance = (info.play.balance * 255) / AUDIO_RIGHT_BALANCE;
+}
+
+/*
+** This routine converts from linear to ulaw.
+**
+** Craig Reese: IDA/Supercomputing Research Center
+** Joe Campbell: Department of Defense
+** 29 September 1989
+**
+** References:
+** 1) CCITT Recommendation G.711 (very difficult to follow)
+** 2) "A New Digital Technique for Implementation of Any
+** Continuous PCM Companding Law," Villeret, Michel,
+** et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
+** 1973, pg. 11.12-11.17
+** 3) MIL-STD-188-113,"Interoperability and Performance Standards
+** for Analog-to_Digital Conversion Techniques,"
+** 17 February 1987
+**
+** Input: Signed 16 bit linear sample
+** Output: 8 bit ulaw sample
+*/
+#define ZEROTRAP /* turn on the trap as per the MIL-STD */
+#define BIAS 0x84 /* define the add-in bias for 16 bit samples */
+#define CLIP 32635
+
+unsigned char
+linear_to_ulaw( sample )
+int sample;
+{
+ static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
+ int sign, exponent, mantissa;
+ unsigned char ulawbyte;
+
+ /* Get the sample into sign-magnitude. */
+ sign = (sample >> 8) & 0x80; /* set aside the sign */
+ if ( sign != 0 ) sample = -sample; /* get magnitude */
+ if ( sample > CLIP ) sample = CLIP; /* clip the magnitude */
+
+ /* Convert from 16 bit linear to ulaw. */
+ sample = sample + BIAS;
+ exponent = exp_lut[( sample >> 7 ) & 0xFF];
+ mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
+ ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa );
+#ifdef ZEROTRAP
+ if ( ulawbyte == 0 ) ulawbyte = 0x02; /* optional CCITT trap */
+#endif
+
+ return ulawbyte;
+}
+
+/*
+ * Downsample a block of CDDA data, if necessary, for playing out an old-style
+ * audio device.
+ */
+long
+wmaudio_convert(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+ short *buf16 = (short *)rawbuf;
+ int i, j, samples;
+ int mono_value;
+ unsigned char *rbend = rawbuf + buflen;
+
+ /* Don't do anything if the audio device can take the raw values. */
+ if (raw_audio)
+ return (buflen);
+
+ for (i = 0; buf16 < (short *)(rbend); i++)
+ {
+ /* Downsampling to 8KHz is a little irregular. */
+ samples = (i & 1) ? ((i % 20) ? 10 : 12) : 12;
+
+ /* And unfortunately, we don't always end on a nice boundary. */
+ if (buf16 + samples > (short *)(rbend))
+ samples = ((short *)rbend) - buf16;
+
+ /*
+ * No need to average all the values; taking the first one
+ * is sufficient and less CPU-intensive. But we do need to
+ * do both channels.
+ */
+ mono_value = (buf16[0] + buf16[1]) / 2;
+ buf16 += samples;
+ rawbuf[i] = ulawmap[mono_value];
+ }
+
+ return (i);
+}
+
+#endif /* ... && BUILD_CDDA */
diff --git a/kscd/libwm/plat_sun_cdda.c b/kscd/libwm/plat_sun_cdda.c
new file mode 100644
index 00000000..3f669a8b
--- /dev/null
+++ b/kscd/libwm/plat_sun_cdda.c
@@ -0,0 +1,380 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Sun (really Solaris) CDDA functions.
+ */
+
+#include "include/wm_cdda.h"
+
+#if defined(sun) || defined(__sun__) && defined(SYSV) && defined(BUILD_CDDA)
+
+
+#include "include/wm_struct.h"
+#include "include/wm_cdda.h"
+/* types.h and cdio.h are included by wm_cdda.h */
+
+#include <stdio.h>
+#include <math.h>
+#include <sys/ioctl.h>
+#include <malloc.h>
+#include <errno.h>
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+#define CDDABLKSIZE 2368
+#define SAMPLES_PER_BLK 588
+
+/* Address of next block to read. */
+int current_position = 0;
+
+/* Address of first and last blocks to read. */
+int starting_position = 0;
+int ending_position = 0;
+
+/* Playback direction. */
+int direction = 1;
+
+/* Number of blocks to read at once; initialize to the maximum. */
+/* (was 30. Set to 15 for INTeL. Maybe config option? */
+int numblocks = 15;
+
+/*
+ * This is the fastest way to convert from BCD to 8-bit.
+ */
+unsigned char unbcd[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,0,0,0,0,0,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0,0,0,0,0,0,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 0,0,0,0,0,0,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 0,0,0,0,0,0,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 0,0,0,0,0,0,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 0,0,0,0,0,0,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 0,0,0,0,0,0,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 0,0,0,0,0,0,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 0,0,0,0,0,0,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+
+static long wmcdda_normalize(struct cdda_block *block);
+
+/*
+ * Initialize the CDDA data buffer and open the appropriate device.
+ *
+ * NOTE: We allocate twice as much space as we need to actually read a block;
+ * this lets us do audio manipulations without bothering to malloc a second
+ * buffer.
+ *
+ * Also, test to see if we can actually *do* CDDA on this drive; if not, we
+ * need to exit right away so the UI doesn't show the user any CDDA controls.
+ */
+int
+wmcdda_init(struct cdda_device* pdev, struct cdda_block *block)
+{
+ struct cdrom_cdda cdda;
+ int i;
+
+ if (pdev->fd > -1)
+ return -1;
+
+ for (i = 0; i < pdev->numblocks; i++) {
+ /* in Linux const */
+ pdev->blocks[i].buflen = pdev->frames_at_once * CDDABLKSIZE;
+ pdev->blocks[i].buf = malloc(pdev->blocks[i].buflen);
+ if (!pdev->blocks[i].buf)
+ return -ENOMEM;
+ }
+
+ pdev->fd = open(pdev->devname, 0);
+ if (pdev->fd == -1)
+ pdev->fd = open("/dev/rdsk/c0t6d0s2", 0);
+
+ if (pdev->fd > -1)
+ {
+ cdda.cdda_addr = 200;
+ cdda.cdda_length = 1;
+ cdda.cdda_data = pdev->blocks[0].buf;
+ cdda.cdda_subcode = CDROM_DA_SUBQ;
+
+ if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0)
+ {
+ block->status = WM_CDM_STOPPED;
+ return -1;
+ } else {
+ block->status = WM_CDM_STOPPED;
+ return 0;
+ }
+ } else {
+ block->status = WM_CDM_EJECTED;
+ return -1;
+ }
+}
+
+/*
+ * Close the CD-ROM device in preparation for exiting.
+ */
+int
+wmcdda_close(struct cdda_device* pdev)
+{
+ int i;
+
+ if(-1 == pdev->fd)
+ return -1;
+
+ close(pdev->fd);
+ pdev->fd = -1;
+
+ for (i = 0; i < pdev->numblocks; i++) {
+ free(pdev->blocks[i].buf);
+ pdev->blocks[i].buf = 0;
+ pdev->blocks[i].buflen = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Set up for playing the CD. Actually this doesn't play a thing, just sets a
+ * couple variables so we'll know what to do when we're called.
+ */
+int
+wmcdda_setup(int start, int end, int realstart)
+{
+ current_position = start - 150;
+ ending_position = end - 150;
+ starting_position = realstart - 150;
+
+ /*
+ * Special case: don't start at the "end" of a track if we're
+ * playing backwards!
+ */
+ if (direction == -1 && start == realstart)
+ current_position = ending_position - numblocks;
+ return 0;
+}
+
+/*
+ * Read some blocks from the CD. Stop if we hit the end of the current region.
+ *
+ * Returns number of bytes read, -1 on error, 0 if stopped for a benign reason.
+ */
+long
+wmcdda_read(struct cdda_device* pdev, struct cdda_block *block)
+{
+ struct cdrom_cdda cdda;
+ int blk;
+ unsigned char *q;
+ extern int speed;
+ unsigned char* rawbuf = block->buf;
+
+ if(pdev->fd < 0 && (wmcdda_init(pdev, block) < 0)) {
+ return -1;
+ }
+
+ /*
+ * Hit the end of the CD, probably.
+ */
+ if ((direction > 0 && current_position >= ending_position) ||
+ (direction < 0 && current_position < starting_position))
+ {
+ block->status = WM_CDM_TRACK_DONE;
+ return (0);
+ }
+
+ cdda.cdda_addr = current_position;
+ if (ending_position && current_position + pdev->frames_at_once > ending_position)
+ cdda.cdda_length = ending_position - current_position;
+ else
+ cdda.cdda_length = pdev->frames_at_once;
+ cdda.cdda_data = (unsigned char*)block->buf;
+ cdda.cdda_subcode = CDROM_DA_SUBQ;
+
+ if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0)
+ {
+ if (errno == ENXIO) /* CD ejected! */
+ {
+ block->status = WM_CDM_EJECTED;
+ return (-1);
+ }
+
+ /* Sometimes it fails once, dunno why */
+ if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0)
+ {
+ if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0)
+ {
+ if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0)
+ {
+ perror("CDROMCDDA");
+ block->status = WM_CDM_CDDAERROR;
+ return (-1);
+ }
+ }
+ }
+ }
+
+ if (speed > 148)
+ {
+ /*
+ * We want speed=148 to advance by cdda_length, but
+ * speed=256 to advance cdda_length * 4.
+ */
+ current_position = current_position +
+ (cdda.cdda_length * direction * (speed - 112)) / 36;
+ }
+ else
+ current_position = current_position + cdda.cdda_length * direction;
+
+ for (blk = 0; blk < numblocks; blk++)
+ {
+ /*
+ * New valid Q-subchannel information? Update the block
+ * status.
+ */
+ q = &rawbuf[blk * CDDABLKSIZE + SAMPLES_PER_BLK * 4];
+ if (*q == 1)
+ {
+ block->track = unbcd[q[1]];
+ block->index = unbcd[q[2]];
+ /*block->minute = unbcd[q[7]];
+ block->second = unbcd[q[8]];*/
+ block->frame = unbcd[q[9]];
+ block->status = WM_CDM_PLAYING;
+ block->buflen = cdda.cdda_length;
+ }
+ }
+
+ return wmcdda_normalize(block);
+}
+
+/*
+ * Normalize a bunch of CDDA data. Basically this means ripping out the
+ * Q subchannel data and doing byte-swapping, since the CD audio is in
+ * littleendian format.
+ *
+ * Scanning is handled here too.
+ *
+ * XXX - do byte swapping on Intel boxes?
+ */
+long
+wmcdda_normalize(struct cdda_block *block)
+{
+ int i, nextq;
+ long buflen = block->buflen;
+ int blocks = buflen / CDDABLKSIZE;
+ unsigned char *rawbuf = block->buf;
+ unsigned char *dest = rawbuf;
+ unsigned char tmp;
+ long *buf32 = (long *)rawbuf, tmp32;
+
+/*
+ * this was #ifndef LITTLEENDIAN
+ * in wmcdda it was called LITTLE_ENDIAN. Was this a flaw?
+ */
+#if WM_BIG_ENDIAN
+ if (blocks--)
+ for (i = 0; i < SAMPLES_PER_BLK * 2; i++)
+ {
+ /* Only need to use temp buffer on first block. */
+ tmp = *rawbuf++;
+ *dest++ = *rawbuf++;
+ *dest++ = tmp;
+ }
+#endif
+
+ while (blocks--)
+ {
+ /* Skip over Q data. */
+ rawbuf += 16;
+
+ for (i = 0; i < SAMPLES_PER_BLK * 2; i++)
+ {
+#if WM_LITTLE_ENDIAN
+ *dest++ = *rawbuf++;
+ *dest++ = *rawbuf++;
+#else
+ *dest++ = rawbuf[1];
+ *dest++ = rawbuf[0];
+ rawbuf += 2;
+#endif
+ }
+ }
+
+ buflen -= ((buflen / CDDABLKSIZE) * 16);
+
+ /*
+ * Reverse the data here if we're playing backwards.
+ * XXX - ideally this should be done above.
+ */
+ if (direction < 0)
+ {
+ buflen /= 4; /* we can move 32 bits at a time. */
+
+ for (i = 0; i < buflen / 2; i++)
+ {
+ tmp32 = buf32[i];
+ buf32[i] = buf32[buflen - i - 1];
+ buf32[buflen - i - 1] = tmp32;
+ }
+
+ buflen *= 4;
+ }
+
+ return (buflen);
+}
+
+/*
+ * Set the playback direction.
+ */
+void
+wmcdda_direction(int newdir)
+{
+ if (newdir == 0)
+ {
+ numblocks = 20;
+ direction = 1;
+ }
+ else
+ {
+ numblocks = 30;
+ direction = -1;
+ }
+}
+
+/*
+ * Do system-specific stuff to get ready to play at a particular speed.
+ */
+void
+wmcdda_speed(int speed)
+{
+ if (speed > 128)
+ numblocks = 12;
+ else
+ numblocks = direction > 0 ? 20 : 30;
+}
+
+#endif /* } */
diff --git a/kscd/libwm/plat_svr4.c b/kscd/libwm/plat_svr4.c
new file mode 100644
index 00000000..ad6e488c
--- /dev/null
+++ b/kscd/libwm/plat_svr4.c
@@ -0,0 +1,482 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * SVR4 specific. Much of this is similar to plat_hpux.c.
+ */
+
+static char plat_svr4_id[] = "$Id$";
+
+#if (defined(SVR4) || defined(__SVR4)) && !defined(sun) && !defined(__sun) && !defined(sony_news) && !defined(__sony_news)
+
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/mkdev.h>
+#include <sys/stat.h>
+#include <sys/sdi.h>
+#include <sys/sdi_edt.h>
+#include <sys/scsi.h>
+#include <errno.h>
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+
+void *malloc();
+char *strchr();
+
+int min_volume = 0;
+int max_volume = 255;
+
+static int
+create_cdrom_node(char *dev_name)
+{
+ char pass_through[100];
+ int file_des;
+ dev_t pass_thru_device;
+ int err;
+ int ccode;
+
+
+ strncpy(pass_through, dev_name, sizeof(pass_through) - 2);
+ strcat(pass_through, "p" );
+
+ if (setreuid(-1,0) < 0)
+ {
+ perror("setregid/setreuid/access");
+ return -1;
+ }
+
+ ccode = access(pass_through, F_OK);
+
+ if (ccode < 0)
+ {
+ if ((file_des = open(dev_name, O_RDONLY)) < 0)
+ {
+ perror("open cdrom devices failed");
+ return -1;
+ }
+
+ if (ioctl(file_des, B_GETDEV, &pass_thru_device) < 0)
+ {
+ perror("Call to get pass-through device number failed");
+ return -1;
+ }
+
+ (void)close(file_des);
+
+ if (mknod(pass_through, (S_IFCHR | S_IREAD | S_IWRITE),
+ pass_thru_device) < 0)
+ {
+ perror("Unable to make pass-through node");
+ return -1;
+ }
+
+ if (chown(pass_through, 0 , 0) < 0)
+ {
+ perror("chown");
+ return -1;
+ }
+
+ if (chmod(pass_through, 0660 ) < 0)
+ {
+ perror("chmod");
+ return -1;
+ }
+ }
+
+ file_des = open( pass_through, O_RDWR);
+ err = errno;
+
+ if ( (setreuid(-1,getuid()) < 0) || (setregid(-1,getgid()) < 0) )
+ {
+ perror("setreuid/setregid");
+ return -1;
+ }
+ errno = err;
+ return file_des;
+} /* create_cdrom_node() */
+
+const char*
+find_cdrom()
+{
+ /*
+ ** the path of the device has to start w/ /dev
+ ** otherwise we are vulnerable to race conditions
+ ** Thomas Biege <thomas@suse.de>
+ */
+ const char* device = NULL;
+
+ device = getenv("CDROM");
+ if ( (device != NULL) &&
+ !(strncmp("/dev/", device, 5) ||
+ strstr(_device, "/../") ))
+ return device;
+
+ if (access("/dev/cdrom/cdrom1", F_OK) == 0)
+ {
+ return "/dev/cdrom/cdrom1";
+ }
+ else if (access("/dev/cdrom/cdrom2", F_OK) == 0)
+ {
+ return "/dev/cdrom/cdrom2";
+ }
+ else
+ {
+ fprintf(stderr, "Couldn't find a CD device!\n");
+ return NULL;
+ }
+} /* find_cdrom() */
+
+/*
+ * Initialize the drive. A no-op for the generic driver.
+ */
+int
+gen_init(struct wm_drive *d)
+{
+ return (0);
+} /* gen_init() */
+
+/*
+ * Open the CD and figure out which kind of drive is attached.
+ */
+int
+wmcd_open(struct wm_drive *d)
+{
+ int fd, flag = 1;
+ static int warned = 0;
+ char vendor[32] = WM_STR_GENVENDOR;
+ char model[32] = WM_STR_GENMODEL;
+ char rev[32] = WM_STR_GENREV;
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+ d->fd = create_cdrom_node(d->cd_device); /* this will do open */
+
+ if (d->fd < 0)
+ {
+ if (errno == EACCES)
+ {
+ if (! warned)
+ {
+ fprintf(stderr,"Cannot access %s\n",d->cd_device);
+ warned++;
+ }
+ }
+ else if (errno != EINTR)
+ {
+ return ( -6 );
+ }
+
+ /* No CD in drive. (Is this true also for svr4 ? XXX ) */
+ return (1);
+ }
+
+ if (warned)
+ {
+ warned = 0;
+ fprintf(stderr, "Thank you.\n");
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+
+ fd = d->fd;
+
+ if (wm_scsi_get_drive_type(d, vendor, model, rev) < 0)
+ {
+ perror("Cannot inquiry drive for it's type");
+ exit(1);
+ }
+ find_drive_struct(vendor, model, rev);
+
+ d->fd = fd;
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+
+/*
+ * Send a SCSI command out the bus.
+ */
+int
+wm_scsi( struct wm_drive *d, unsigned char *xcdb, int cdblen,
+ char *retbuf, int retbuflen, int getreply)
+{
+ int ccode;
+ int file_des = d->fd;
+ int i,j;
+ unsigned char sense_buffer[ SENSE_SZ ];
+ int errno_save;
+
+ /* getreply == 1 is read, == 0 is write */
+
+ struct sb sb;
+ struct scs scs;
+
+ sb.sb_type = ISCB_TYPE;
+
+ sb.SCB.sc_comp_code = SDI_PROGRES;
+ sb.SCB.sc_int = NULL;
+ sb.SCB.sc_wd = 0;
+ sb.SCB.sc_dev.sa_major = 0;
+ sb.SCB.sc_dev.sa_minor = 0;
+ sb.SCB.sc_dev.sa_lun = 0;
+ sb.SCB.sc_dev.sa_exlun = 0;
+ sb.SCB.sc_status = 0;
+ sb.SCB.sc_link = (struct sb *) NULL;
+ sb.SCB.sc_resid = 0;
+
+ sb.SCB.sc_cmdpt = (void *)xcdb;
+ sb.SCB.sc_cmdsz = cdblen;
+
+ sb.SCB.sc_datapt = retbuf ;
+ sb.SCB.sc_datasz = retbuflen ;
+
+ if (getreply == 1)
+ sb.SCB.sc_mode = SCB_READ;
+ else
+ sb.SCB.sc_mode = SCB_WRITE;
+
+ sb.SCB.sc_time = 500;
+
+ ccode = ioctl(file_des, SDI_SEND, &sb);
+
+ if ( (sb.SCB.sc_comp_code != 0xd000000e ) ||
+ ( sb.SCB.sc_status != 02) )
+ return ccode;
+
+ errno_save = errno;
+
+ sb.SCB.sc_comp_code = SDI_PROGRES;
+ sb.SCB.sc_int = NULL;
+ sb.SCB.sc_wd = 0;
+ sb.SCB.sc_dev.sa_major = 0;
+ sb.SCB.sc_dev.sa_minor = 0;
+ sb.SCB.sc_dev.sa_lun = 0;
+ sb.SCB.sc_dev.sa_exlun = 0;
+ sb.SCB.sc_status = 0;
+ sb.SCB.sc_link = (struct sb *) NULL;
+ sb.SCB.sc_resid = 0;
+
+ scs.ss_op = SS_REQSEN;
+ scs.ss_lun = 0;
+ scs.ss_addr1 = 0;
+ scs.ss_addr = 0;
+ scs.ss_len = SENSE_SZ;
+ scs.ss_cont = 0;
+
+ sb.SCB.sc_cmdpt = SCS_AD(&scs);
+ sb.SCB.sc_cmdsz = SCS_SZ;
+ sb.SCB.sc_datapt = sense_buffer;
+ sb.SCB.sc_datasz = 18;
+ sb.SCB.sc_mode = SCB_READ;
+ sb.SCB.sc_time = 5000;
+
+ if (ioctl(file_des, SDI_SEND, &sb) < 0)
+ {
+ fprintf(stderr,"Cannot read sense.\n");
+ exit(-1);
+ }
+
+ errno=errno_save;
+ return -1;
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *index)
+{
+ return (wm_scsi2_get_drive_status(d, oldmode, mode, pos, track, index));
+} /* gen_get_drive_status() */
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ return (wm_scsi2_get_trackcount(d, tracks));
+} /* gen_get_trackcount() */
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ */
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ return (wm_scsi2_get_trackinfo(d, track, data, startframe));
+} /* gen_get_trackinfo() */
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ int tmp;
+
+ return (wm_scsi2_get_cdlen(d, frames));
+} /* gen_get_cdlen() */
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play(struct wm_drive *d, int start, int end)
+{
+ return (wm_scsi2_play(d, start, end));
+} /* gen_play() */
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause(struct wm_drive *d)
+{
+ return (wm_scsi2_pause(d));
+} /* gen_pause() */
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume(struct wm_drive *d)
+{
+ return (wm_scsi2_resume(d));
+} /* gen_resume() */
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop(struct wm_drive *d)
+{
+ return (wm_scsi2_stop(d));
+} /* gen_stop() */
+
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+ return (wm_scsi2_eject(d));
+} /* gen_eject() */
+
+/*
+ * Close the tray.
+ * please review scsi.c / wm_scsi2_closetray()
+ * and send changes to milliByte@DeathsDoor.com
+ */
+int
+gen_closetray( struct wm_drive *d )
+{
+ return(wm_scsi2_closetray(d));
+} /* gen_closetray() */
+
+
+/*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume(struct wm_drive *d, int left, int right)
+{
+ return (wm_scsi2_set_volume(d, left, right));
+} /* gen_set_volume() */
+
+/*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume(struct wm_drive *d, int *left, int *right)
+{
+ return (wm_scsi2_get_volume(d, left, right));
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ /* This needs to be tested */
+ return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght);
+} /* gen_get_cdtext() */
+
+#endif
diff --git a/kscd/libwm/plat_template.c b/kscd/libwm/plat_template.c
new file mode 100644
index 00000000..bd47c20b
--- /dev/null
+++ b/kscd/libwm/plat_template.c
@@ -0,0 +1,295 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * This file surely contains nonsense. It's the porter's part to fill
+ * the gaps and assure that the resulting code makes sense.
+ *
+ */
+
+
+static char plat_template_id[] = "$Id$"
+
+#if [TEMPLATESYSTEM]
+
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * gen_init();
+ *
+ */
+int
+gen_init(struct wm_drive *d)
+{
+ return (0);
+} /* gen_init() */
+
+/*
+ * wmcd_open()
+ *
+ */
+int
+wmcd_open(struct wm_drive *d)
+{
+ char vendor[32] = WM_STR_GENVENDOR;
+ char model[32] = WM_STR_GENMODEL;
+ char rev[32] = WM_STR_GENREV;
+
+ if( ! d )
+ {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if(d->fd > -1) /* device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return 0;
+ }
+
+ if( d->cd_device == (char *)NULL )
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+ /* open() goes here */
+
+ if(find_drive_struct(vendor, model, rev)) {
+ gen_close(d);
+ return -1;
+ }
+
+ d->init(d);
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/*
+ * wm_scsi()
+ *
+ */
+int
+wm_scsi(struct wm_drive *d,
+ uchar_t *cdb, int cdblen,void *retbuf,int retbuflen,int getreply)
+{
+ return (0);
+} /* wm_scsi() */
+
+/*
+ * close the CD device
+ */
+
+int
+gen_close(struct wm_drive *d)
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+} /* gen_close() */
+
+/*
+ * gen_get_drive_status()
+ *
+ */
+int
+gen_get_drive_status(struct wm_drive *d,
+ int oldmode,
+ int *mode,
+ int *pos,
+ int *track,
+ int *index)
+{
+ return (0);
+} /* gen_get_drive_status() */
+
+/*
+ * gen_get_trackcount()
+ *
+ */
+int
+gen_get_trackcount(struct wm_drive *d,int *tracks)
+{
+ return (0);
+} /* gen_get_trackcount() */
+
+/*
+ * gen_get_trackinfo()
+ *
+ */
+int
+gen_get_trackinfo(struct wm_drive *d,int track,int *data,int *startframe)
+{
+ return (0);
+} /* gen_get_trackinfo() */
+
+/*
+ * gen_get_cdlen()
+ *
+ */
+int
+gen_get_cdlen(struct wm_drive *d,int *frames)
+{
+ return (0);
+} /* gen_get_cdlen() */
+
+/*
+ * gen_play()
+ *
+ */
+int
+gen_play(struct wm_drive *d,int start,int end)
+{
+ return (0);
+} /* gen_play() */
+
+/*
+ * gen_pause()
+ *
+ */
+int
+gen_pause(struct wm_drive *d)
+{
+ return ioctl( 0 );
+} /* gen_pause() */
+
+/*
+ * gen_resume
+ *
+ */
+int
+gen_resume(struct wm_drive *d)
+{
+ return (0);
+} /* gen_resume() */
+
+/*
+ * gen_stop()
+ *
+ */
+int
+gen_stop(struct wm_drive *d)
+{
+ return (0);
+} /* gen_stop() */
+
+/*
+ * gen_eject()
+ *
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+ return (0);
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+int gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!wmcd_close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+int
+scale_volume(int vol,int max)
+{
+ return ((vol * (max_volume - min_volume)) / max + min_volume);
+} /* scale_volume() */
+
+int
+unscale_volume(int vol,int max)
+{
+ int n;
+ n = ( vol - min_volume ) * max_volume / (max - min_volume);
+ return (n <0)?0:n;
+} /* unscale_volume() */
+
+/*
+ * gen_set_volume()
+ *
+ */
+int
+gen_set_volume(struct wm_drive *d,int left,int right)
+{
+ return (0);
+} /* gen_set_volume() */
+
+/*
+ * gen_get_volume()
+ *
+ */
+int
+gen_get_volume(struct wm_drive *d,int *left,int *right)
+{
+ return (0);
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *
+ * For systems without working wm_scsi(), this should return -1
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght);
+} /* gen_get_cdtext() */
+
+#endif /* TEMPLATESYSTEM */
diff --git a/kscd/libwm/plat_ultrix.c b/kscd/libwm/plat_ultrix.c
new file mode 100644
index 00000000..2685b0f5
--- /dev/null
+++ b/kscd/libwm/plat_ultrix.c
@@ -0,0 +1,663 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * ULTRIX 4.2 drive control routines.
+ */
+
+static char plat_ultrix_id[] = "$Id$";
+
+#if defined(ultrix) || defined(__ultrix)
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ustat.h>
+#include <string.h>
+#include <sys/rzdisk.h>
+#include <sys/cdrom.h>
+
+#include "include/wm_config.h"
+#include "include/wm_cdtext.h"
+#include "include/wm_struct.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * This structure will be filled with the TOC header and all entries.
+ * Ultrix doesn't seem to allow getting single TOC entries.
+ * - Chris Ross (cross@eng.umd.edu)
+ */
+struct cd_toc_header_and_entries
+{
+ struct cd_toc_header cdth;
+ struct cd_toc_entry cdte[CDROM_MAX_TRACK+1];
+};
+
+void *malloc();
+char *strchr();
+
+int min_volume = 128;
+int max_volume = 255;
+
+char * ultrix_cd_device = NULL;
+/*
+ * fgetline()
+ *
+ * Simulate fgets, but joining continued lines in the output of uerf.
+ */
+
+#define BUF_SIZE 85 /* Max length of a (real) line */
+
+char *
+fgetline( FILE *fp )
+{
+ static char *retval = NULL;
+ static char holdbuf[BUF_SIZE + 1];
+ char tmp[BUF_SIZE + 1];
+ char *stmp;
+
+ if (!retval)
+ {
+ retval = malloc(BUF_SIZE * 3); /* 3 lines can be joined */
+ if (!retval)
+ return(NULL);
+ else
+ *retval = '\0';
+ }
+
+ if (*holdbuf)
+ {
+ strcpy(retval, holdbuf);
+ retval[strlen(retval)-1] = '\0';
+ memset(holdbuf, 0, BUF_SIZE+1);
+ }
+
+ while (fgets(tmp, BUF_SIZE, fp))
+ {
+ stmp = tmp + strspn(tmp, " \t");
+ if (*stmp == '_')
+ { /* Continuation line */
+ retval[strlen(retval)-1] = '\0'; /* Trim off C/R */
+ strcat(retval, stmp+1);
+ } else {
+ if (*retval)
+ {
+ strcpy(holdbuf, tmp);
+ holdbuf[strlen(holdbuf)-1] = -1;
+ return retval;
+ } else { /* First line read, keep reading */
+ strcat(retval, stmp);
+ retval[strlen(retval)-1] = '\0';
+ }
+ }
+ }
+ return NULL;
+} /* fgetline() */
+
+/*
+ * find_cdrom
+ *
+ * Determine the name of the CD-ROM device.
+ *
+ * Read through the boot records (via a call to uerf) and find the SCSI
+ * address of the CD-ROM. If the "CDROM" environment variable is set,
+ * use that instead.
+ */
+const char*
+find_cdrom()
+{
+ char *data;
+ FILE *uerf;
+ int fds[2];
+ int pid;
+ const char* device = NULL;
+
+ device = getenv("CDROM");
+
+ if (device != NULL)
+ {
+ if(strncmp("/dev/", device, 5) || strstr(device, "/../"))
+ return NULL;
+ }
+
+ pipe(fds);
+
+ if ((pid = fork()) == 0)
+ {
+ close(fds[0]);
+ dup2(fds[1], 1);
+ execl("/etc/uerf", "uerf", "-R", "-r", "300", (void *)0);
+ execl("/usr/sbin/uerf", "uerf", "-R", "-r", "300", (void *)0);
+ return NULL; /* _exit(1); */
+ } else if (pid < 0) {
+ perror("fork");
+ return NULL; /* exit(1); */
+ }
+
+ close(fds[1]);
+ uerf = fdopen(fds[0], "r");
+
+ while (data = fgetline(uerf))
+ if (strstr(data, "RRD42"))
+ {
+ char *device_p;
+
+ ultrix_cd_device = (char *)malloc(sizeof("/dev/rrz##c"));
+ strcpy(ultrix_cd_device, "/dev/r");
+ device_p = strstr(data, "rz");
+ device_p[(int)(strchr(device_p, ' ') - device_p)] = '\0';
+ strcat(ultrix_cd_device, device_p);
+ strcat(ultrix_cd_device, "c");
+ device = ultrix_cd_device;
+ break;
+ }
+
+ fclose(uerf);
+
+ if (device == NULL)
+ {
+ fprintf(stderr, "No cdrom (RRD42) is installed on this system\n");
+ return NULL; /* exit(1); */
+ }
+
+ kill(pid, 15);
+ (void)wait((int *)NULL);
+ return device;
+} /* find_cdrom() */
+
+/*
+ * initialize the drive. a no-op for the generic driver.
+ */
+int
+gen_init( struct wm_drive *d )
+{
+ return (0);
+} /* gen_init() */
+
+
+/*
+ * Open the CD device and figure out what kind of drive is attached.
+ */
+int
+wmcd_open( struct wm_drive *d )
+{
+ int fd;
+ static int warned = 0;
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ d->cd_device = find_cdrom();
+
+ d->fd = open(d->cd_device, 0);
+ if (d->fd < 0)
+ {
+ if (errno == EACCES)
+ {
+ return -EACCES;
+ }
+ else if (errno != EINTR)
+ {
+ return( -6 );
+ }
+
+ /* No CD in drive. */
+ return (1);
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ find_drive_struct("", "", "");
+ d->fd = fd;
+
+ (d->init)(d);
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/*
+ * Send an arbitrary SCSI command to a device.
+ */
+int
+wm_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen,
+ void *retbuf, int retbuflen, int getreply )
+{
+ /* ULTRIX doesn't have a SCSI passthrough interface, does it? */
+ return (-1);
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status( struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *index)
+{
+ struct cd_sub_channel sc;
+ struct cd_subc_channel_data scd;
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ sc.sch_address_format = CDROM_MSF_FORMAT;
+ sc.sch_data_format = CDROM_CURRENT_POSITION;
+ sc.sch_track_number = 0;
+ sc.sch_alloc_length = sizeof(scd);
+ sc.sch_buffer = (caddr_t)&scd;
+
+ /* Is the device open? */
+ if (d->fd < 0)
+ {
+ switch (wmcd_open(d))
+ {
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+ if (ioctl(d->fd, CDROM_READ_SUBCHANNEL, &sc))
+ return (0); /* ejected */
+
+ switch (scd.scd_header.sh_audio_status)
+ {
+ case AS_PLAY_IN_PROGRESS:
+ *mode = WM_CDM_PLAYING;
+dopos:
+ *pos = scd.scd_position_data.scp_absaddr.msf.m_units * 60 * 75 +
+ scd.scd_position_data.scp_absaddr.msf.s_units * 75 +
+ scd.scd_position_data.scp_absaddr.msf.f_units;
+ *track = scd.scd_position_data.scp_track_number;
+ *index = scd.scd_position_data.scp_index_number;
+ break;
+
+ case AS_PLAY_PAUSED:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ goto dopos;
+ }
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ case AS_PLAY_COMPLETED:
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ break;
+
+ case AS_NO_STATUS:
+ *mode = WM_CDM_STOPPED;
+ break;
+ }
+ return (0);
+} /* gen_get_drive_status() */
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount( struct wm_drive *d, int *tracks )
+{
+ struct cd_toc_header hdr;
+
+ if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr))
+ return (-1);
+
+ *tracks = hdr.th_ending_track;
+
+ return (0);
+} /* gen_get_trackcount() */
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ *
+ * XXX - this should get cached, but that means keeping track of ejects.
+ */
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ struct cd_toc toc;
+ struct cd_toc_header hdr;
+ struct cd_toc_header_and_entries toc_buffer;
+
+ if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr))
+ return (-1);
+
+ bzero((char *)&toc_buffer, sizeof(toc_buffer));
+ toc.toc_address_format = CDROM_MSF_FORMAT;
+ toc.toc_starting_track = 0;
+ toc.toc_alloc_length = (u_short)(((hdr.th_data_len1 << 8) +
+ hdr.th_data_len0) & 0xfff) + 2;
+ toc.toc_buffer = (caddr_t)&toc_buffer;
+
+ if (ioctl(d->fd, CDROM_TOC_ENTRYS, &toc))
+ return (-1);
+
+ if (track == 0)
+ track = hdr.th_ending_track + 1;
+
+ *data = (toc_buffer.cdte[track-1].te_control & CDROM_DATA_TRACK) ? 1:0;
+ *startframe = toc_buffer.cdte[track - 1].te_absaddr.msf.m_units*60*75 +
+ toc_buffer.cdte[track - 1].te_absaddr.msf.s_units * 75 +
+ toc_buffer.cdte[track - 1].te_absaddr.msf.f_units;
+
+ return (0);
+} /* gen_get_trackinfo() */
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ int tmp;
+ return (gen_get_trackinfo(d, 0, &tmp, frames));
+} /* gen_get_cdlen() */
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play( struct wm_drive *d, int start, int end )
+{
+ struct cd_play_audio_msf msf;
+
+ msf.msf_starting_M_unit = start / (60*75);
+ msf.msf_starting_S_unit = (start % (60*75)) / 75;
+ msf.msf_starting_F_unit = start % 75;
+ msf.msf_ending_M_unit = end / (60*75);
+ msf.msf_ending_S_unit = (end % (60*75)) / 75;
+ msf.msf_ending_F_unit = end % 75;
+
+ if (ioctl(d->fd, SCSI_START_UNIT))
+ return (-1);
+ if (ioctl(d->fd, CDROM_PLAY_MSF, &msf))
+ return (-2);
+
+ return (0);
+} /* gen_play() */
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause( struct wm_drive *d )
+{
+ return (ioctl(d->fd, CDROM_PAUSE_PLAY));
+} /* gen_pause() */
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume( struct wm_drive *d )
+{
+ return (ioctl(d->fd, CDROM_RESUME_PLAY));
+} /* gen_resume() */
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop( struct wm_drive *d )
+{
+ return (ioctl(d->fd, SCSI_STOP_UNIT));
+} /* gen_stop() */
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+ /* On some systems, we can check to see if the CD is mounted. */
+ struct stat stbuf;
+ struct ustat ust;
+
+ if (fstat(d->fd, &stbuf) != 0)
+ return (-2);
+
+ /* Is this a mounted filesystem? */
+ if (ustat(stbuf.st_rdev, &ust) == 0)
+ return (-3);
+
+ return (ioctl(d->fd, CDROM_EJECT_CADDY));
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * milliByte@DeathsDoor.com
+ *----------------------------------------*/
+
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*
+ * scale_volume(vol, max)
+ *
+ * Return a volume value suitable for passing to the CD-ROM drive. "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ *
+ * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
+ * increases much faster toward the top end of the volume scale than it
+ * does at the bottom. To make up for this, we make the volume scale look
+ * sort of logarithmic (actually an upside-down inverse square curve) so
+ * that the volume value passed to the drive changes less and less as you
+ * approach the maximum slider setting. The actual formula looks like
+ *
+ * (max^2 - (max - vol)^2) * (max_volume - min_volume)
+ * v = --------------------------------------------------- + min_volume
+ * max^2
+ *
+ * If your system's volume settings aren't broken in this way, something
+ * like the following should work:
+ *
+ * return ((vol * (max_volume - min_volume)) / max + min_volume);
+ */
+scale_volume( int vol, int max )
+{
+ return ((max * max - (max - vol) * (max - vol)) *
+ (max_volume - min_volume) / (max * max) + min_volume);
+} /* scale_volume() */
+
+
+/*
+ * unscale_volume(cd_vol, max)
+ *
+ * Given a value between min_volume and max_volume, return the volume slider
+ * value needed to achieve that value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ */
+static int
+unscale_volume( int cd_vol, int max )
+{
+ int vol = 0, top = max, bot = 0, scaled;
+
+ while (bot <= top)
+ {
+ vol = (top + bot) / 2;
+ scaled = scale_volume(vol, max);
+ if (cd_vol == scaled)
+ break;
+ if (cd_vol < scaled)
+ top = vol - 1;
+ else
+ bot = vol + 1;
+ }
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > max)
+ vol = max;
+
+ return (vol);
+} /* unscale_volume() */
+
+/*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume( struct wm_drive *d, int left, int right )
+{
+ struct cd_playback pb;
+ struct cd_playback_status ps;
+ struct cd_playback_control pc;
+
+ left = scale_volume(left, 100);
+ right = scale_volume(right, 100);
+
+ bzero((char *)&pb, sizeof(pb));
+ bzero((char *)&ps, sizeof(ps));
+ bzero((char *)&pc, sizeof(pc));
+
+ pb.pb_alloc_length = sizeof(ps);
+ pb.pb_buffer = (caddr_t)&ps;
+
+ if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb))
+ return (-1);
+
+ pc.pc_chan0_select = ps.ps_chan0_select;
+ pc.pc_chan0_volume = (left < CDROM_MIN_VOLUME) ?
+ CDROM_MIN_VOLUME : (left > CDROM_MAX_VOLUME) ?
+ CDROM_MAX_VOLUME : left;
+ pc.pc_chan1_select = ps.ps_chan1_select;
+ pc.pc_chan1_volume = (right < CDROM_MIN_VOLUME) ?
+ CDROM_MIN_VOLUME : (right > CDROM_MAX_VOLUME) ?
+ CDROM_MAX_VOLUME : right;
+
+ pb.pb_alloc_length = sizeof(pc);
+ pb.pb_buffer = (caddr_t)&pc;
+
+ if (ioctl(d->fd, CDROM_PLAYBACK_CONTROL, &pb))
+ return (-1);
+
+ return (0);
+} /* gen_set_volume() */
+
+/*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume(struct wm_drive *d, int *left, int *right)
+{
+ struct cd_playback pb;
+ struct cd_playback_status ps;
+
+ bzero((char *)&pb, sizeof(pb));
+ bzero((char *)&ps, sizeof(ps));
+
+ pb.pb_alloc_length = sizeof(ps);
+ pb.pb_buffer = (caddr_t)&ps;
+
+ if (d->fd >= 0)
+ {
+ if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb))
+ *left = *right = -1;
+ else
+ {
+ *left = unscale_volume(ps.ps_chan0_volume, 100);
+ *right = unscale_volume(ps.ps_chan1_volume, 100);
+ }
+ }
+ else
+ *left = *right = -1;
+
+ return (0);
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* No SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+
+#endif
diff --git a/kscd/libwm/scsi.c b/kscd/libwm/scsi.c
new file mode 100644
index 00000000..4e0dd759
--- /dev/null
+++ b/kscd/libwm/scsi.c
@@ -0,0 +1,667 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Frontend functions for sending raw SCSI commands to the CD-ROM drive.
+ * These depend on wm_scsi(), which should be defined in each platform
+ * module.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_scsi.h"
+#include "include/wm_platform.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_cdtext.h"
+
+#define SCMD_INQUIRY 0x12
+#define SCMD_MODE_SELECT 0x15
+#define SCMD_MODE_SENSE 0x1a
+#define SCMD_START_STOP 0x1b
+#define SCMD_PREVENT 0x1e
+#define SCMD_READ_SUBCHANNEL 0x42
+#define SCMD_READ_TOC 0x43
+#define SCMD_PLAY_AUDIO_MSF 0x47
+#define SCMD_PAUSE_RESUME 0x4b
+
+#define SUBQ_STATUS_INVALID 0x00
+#define SUBQ_STATUS_PLAY 0x11
+#define SUBQ_STATUS_PAUSE 0x12
+#define SUBQ_STATUS_DONE 0x13
+#define SUBQ_STATUS_ERROR 0x14
+#define SUBQ_STATUS_NONE 0x15
+#define SUBQ_STATUS_NO_DISC 0x17 /* Illegal, but Toshiba returns it. */
+#define SUBQ_ILLEGAL 0xff
+
+#define PAGE_AUDIO 0x0e
+#define LEADOUT 0xaa
+
+#define WM_MSG_CLASS WM_MSG_CLASS_SCSI
+
+/* local prototypes */
+int wm_scsi_mode_select( struct wm_drive *d, unsigned char *buf, unsigned char len );
+int wm_scsi2_pause_resume(struct wm_drive *d, int resume);
+int wm_scsi2_prevent(struct wm_drive *d, int prevent);
+int wm_scsi2_play(struct wm_drive *d, int sframe, int eframe);
+int wm_scsi2_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe);
+int wm_scsi2_get_cdlen(struct wm_drive *d, int frames);
+int wm_scsi2_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *ind);
+int wm_scsi2_get_trackcount(struct wm_drive *d, int *tracks);
+int wm_scsi2_pause(struct wm_drive *d);
+int wm_scsi2_resume(struct wm_drive *d);
+int wm_scsi2_stop(struct wm_drive *d);
+int wm_scsi2_eject(struct wm_drive *d);
+int wm_scsi2_closetray(struct wm_drive *d);
+int wm_scsi2_get_volume(struct wm_drive *d, int *left, int *right);
+int wm_scsi2_set_volume(struct wm_drive *d, int left, int right);
+/* local prototypes END */
+
+/*
+ * Send a SCSI command over the bus, with all the CDB bytes specified
+ * as unsigned char parameters. This doesn't use varargs because some
+ * systems have stdargs instead and the number of bytes in a CDB is
+ * limited to 12 anyway.
+ *
+ * d Drive structure
+ * buf Buffer for data, both sending and receiving
+ * len Size of buffer
+ * dir TRUE if the command expects data to be returned in the buffer.
+ * a0- CDB bytes. Either 6, 10, or 12 of them, depending on the command.
+ */
+/*VARARGS4*/
+int
+sendscsi( struct wm_drive *d, void *buf,
+ unsigned int len, int dir,
+ unsigned char a0, unsigned char a1,
+ unsigned char a2, unsigned char a3,
+ unsigned char a4, unsigned char a5,
+ unsigned char a6, unsigned char a7,
+ unsigned char a8, unsigned char a9,
+ unsigned char a10, unsigned char a11 )
+
+{
+ int cdblen = 0;
+ unsigned char cdb[12];
+
+ cdb[0] = a0;
+ cdb[1] = a1;
+ cdb[2] = a2;
+ cdb[3] = a3;
+ cdb[4] = a4;
+ cdb[5] = a5;
+
+ switch ((a0 >> 5) & 7) {
+ case 0:
+ cdblen = 6;
+ break;
+
+ case 5:
+ cdb[10] = a10;
+ cdb[11] = a11;
+ cdblen = 12;
+
+ case 1:
+ case 2:
+ case 6: /* assume 10-byte vendor-specific codes for now */
+ cdb[6] = a6;
+ cdb[7] = a7;
+ cdb[8] = a8;
+ cdb[9] = a9;
+ if (! cdblen)
+ cdblen = 10;
+ break;
+ }
+
+ return (wm_scsi(d, cdb, cdblen, buf, len, dir));
+}
+
+/*
+ * Send a MODE SENSE command and return the results (minus header cruft)
+ * in a user buffer.
+ *
+ * d Drive structure
+ * page Number of page to query (plus page control bits, if any)
+ * buf Result buffer
+ */
+int
+wm_scsi_mode_sense( struct wm_drive *d, unsigned char page, unsigned char *buf )
+{
+ unsigned char pagebuf[255];
+ int status, i, len, offset;
+
+ status = sendscsi(d, pagebuf, sizeof(pagebuf), 1, SCMD_MODE_SENSE, 0,
+ page, 0, sizeof(pagebuf), 0,0,0,0,0,0,0);
+ if (status < 0)
+ return (status);
+
+ /*
+ * The first byte of the returned data is the transfer length. Then
+ * two more bytes and the length of whatever header blocks are in
+ * front of the page we want.
+ */
+ len = pagebuf[0] - pagebuf[3] - 3;
+ offset = pagebuf[3] + 4;
+ for (i = 0; i < len; i++)
+ buf[i] = pagebuf[offset + i];
+
+ return (0);
+}
+
+/*
+ * Send a MODE SELECT command.
+ *
+ * d Drive structure
+ * buf Page buffer (no need to put on block descriptors)
+ * len Size of page
+ */
+int
+wm_scsi_mode_select( struct wm_drive *d, unsigned char *buf, unsigned char len )
+{
+ unsigned char pagebuf[255];
+ int i;
+
+ pagebuf[0] = pagebuf[1] = pagebuf[2] = pagebuf[3] = 0;
+ for (i = 0; i < (int) len; i++)
+ pagebuf[i + 4] = buf[i];
+
+ return (sendscsi(d, pagebuf, len + 4, 0, SCMD_MODE_SELECT, 0x10, 0,
+ 0, len + 4, 0,0,0,0,0,0,0));
+}
+
+/*
+ * Send an INQUIRY command to get the drive type.
+ *
+ * d Drive structure
+ * vendor Buffer for vendor name (8 bytes + null)
+ * model Buffer for model name (16 bytes + null)
+ * rev Buffer for revision level (4 bytes + null)
+ *
+ * The above string lengths apply to the SCSI INQUIRY command. The
+ * actual WorkMan drive structure reserves 31 bytes + NULL per entry.
+ *
+ * If the model name begins with "CD-ROM" and zero or more spaces, that will
+ * all be stripped off since it's just extra junk to WorkMan.
+ */
+int
+wm_scsi_get_drive_type( struct wm_drive *d, char *vendor,
+ char *model, char *rev )
+{
+/* removed unsigned*/
+ char *s, *t, buf[36];
+ memset(buf, 0, 36);
+
+ wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_INFO, "Sending SCSI inquiry command...\n");
+ if (sendscsi(d, buf, 36, 1, SCMD_INQUIRY, 0, 0, 0, 36, 0,0,0,0,0,0,0))
+ {
+ sprintf( vendor, WM_STR_GENVENDOR);
+ sprintf( model, WM_STR_GENMODEL);
+ sprintf( rev, WM_STR_GENREV);
+ wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_ERROR, "SCSI Inquiry command not supported in this context\n");
+ return -1;
+ }
+
+ wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_DEBUG, "sent.\n");
+
+ memcpy(vendor, buf + 8, 8);
+ vendor[8] = '\0';
+ memcpy(model, buf + 16, 16);
+ model[16] = '\0';
+ memcpy(rev, buf + 32, 4);
+ rev[4] = '\0';
+ wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_VERB, "SCSI Inquiry result: [%s|%s|%s]\n", vendor, model, rev);
+
+
+ /* Remove "CD-ROM " from the model. */
+ if (! strncmp(model, "CD-ROM", 6))
+ {
+ s = model + 6;
+ t = model;
+ while (*s == ' ' || *s == '\t')
+ s++;
+ while( (*t++ = *s++) )
+ ;
+ }
+ wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_INFO, "scsi: Cooked data: %s %s rev. %s\n", vendor, model,rev);
+ return (0);
+} /* wm_scsi_get_drive_type() */
+
+/*
+ * Send a SCSI-2 PAUSE/RESUME command. "resume" is 1 to resume, 0 to pause.
+ */
+int
+wm_scsi2_pause_resume(struct wm_drive *d, int resume)
+{
+ return (sendscsi(d, NULL, 0, 0, SCMD_PAUSE_RESUME, 0, 0, 0, 0, 0, 0,
+ 0, resume ? 1 : 0, 0,0,0));
+}
+
+/*
+ * Send a SCSI-2 "prevent media removal" command. "prevent" is 1 to lock
+ * caddy in.
+ */
+int
+wm_scsi2_prevent(struct wm_drive *d, int prevent)
+{
+ return (sendscsi(d, NULL, 0, 0, SCMD_PREVENT, 0, 0, 0, 0, 0, 0,
+ 0, prevent ? 1 : 0, 0,0,0));
+}
+
+/*
+ * Send a SCSI-2 PLAY AUDIO MSF command. Pass the starting and ending
+ * frame numbers.
+ */
+int
+wm_scsi2_play(struct wm_drive *d, int sframe, int eframe)
+{
+ return (sendscsi(d, NULL, 0, 0, SCMD_PLAY_AUDIO_MSF, 0, 0,
+ sframe / (60 * 75), (sframe / 75) % 60, sframe % 75,
+ eframe / (60 * 75), (eframe / 75) % 60, eframe % 75,
+ 0,0,0));
+}
+
+/*
+ * Send a SCSI-2 READ TOC command to get the data for a particular track.
+ * Fill in track information from the returned data.
+ */
+int
+wm_scsi2_get_trackinfo(struct wm_drive *d, int track,
+ int *data, int *startframe)
+{
+ unsigned char buf[12]; /* one track's worth of info */
+
+ if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_TOC, 2,
+ 0, 0, 0, 0, track, sizeof(buf) / 256,
+ sizeof(buf) % 256, 0,0,0))
+ return (-1);
+
+ *data = buf[5] & 4 ? 1 : 0;
+ *startframe = buf[9] * 60 * 75 + buf[10] * 75 + buf[11];
+
+ return (0);
+}
+
+/*
+ * Get the starting frame for the leadout area (which should be the same as
+ * the length of the disc as far as WorkMan is concerned).
+ */
+int
+wm_scsi2_get_cdlen(struct wm_drive *d, int frames)
+{
+ int tmp;
+ return (wm_scsi2_get_trackinfo(d, LEADOUT, &tmp, &frames));
+}
+
+/*
+ * Get the current status of the drive by sending the appropriate SCSI-2
+ * READ SUB-CHANNEL command.
+ */
+int
+wm_scsi2_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *ind)
+{
+ unsigned char buf[48];
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ /* Is the device open? */
+ if (d->fd < 0)
+ {
+/*
+ * stupid somehow, but necessary this time
+ */
+ switch( wmcd_open( d ) ) {
+
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+ /* If we can't read status, the CD has been ejected. */
+ buf[1] = SUBQ_ILLEGAL;
+ if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_SUBCHANNEL, 2, 64, 1,
+ 0, 0, 0, sizeof(buf) / 256, sizeof(buf) % 256, 0,0,0))
+ return (0);
+
+ switch (buf[1]) {
+ case SUBQ_STATUS_PLAY:
+ *mode = WM_CDM_PLAYING;
+ *track = buf[6];
+ *ind = buf[7];
+ *pos = buf[9] * 60 * 75 + buf[10] * 75 + buf[11];
+ break;
+
+ case SUBQ_STATUS_PAUSE:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ *track = buf[6];
+ *ind = buf[7];
+ *pos = buf[9] * 60 * 75 +
+ buf[10] * 75 +
+ buf[11];
+ }
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ /*
+ * SUBQ_STATUS_DONE is sometimes returned when the CD is idle,
+ * even though the spec says it should only be returned when an
+ * audio play operation finishes.
+ */
+ case SUBQ_STATUS_DONE:
+ case SUBQ_STATUS_NONE:
+ case SUBQ_STATUS_INVALID:
+ if (oldmode == WM_CDM_PLAYING)
+ *mode = WM_CDM_TRACK_DONE;
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ /*
+ * This usually means there's no disc in the drive.
+ */
+ case SUBQ_STATUS_NO_DISC:
+ break;
+
+ /*
+ * This usually means the user ejected the CD manually.
+ */
+ case SUBQ_STATUS_ERROR:
+ break;
+
+ case SUBQ_ILLEGAL: /* call didn't really succeed */
+ break;
+
+ default:
+ *mode = WM_CDM_UNKNOWN;
+#ifndef NDEBUG
+ if( getenv( "WORKMAN_DEBUG" ) != NULL )
+ printf("wm_scsi2_get_drive_status: status is 0x%x\n",
+ buf[1]);
+#endif
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * Get the number of tracks on the CD using the SCSI-2 READ TOC command.
+ */
+int
+wm_scsi2_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ unsigned char buf[4];
+
+ if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_TOC, 0,
+ 0, 0, 0, 0, 0, sizeof(buf) / 256,
+ sizeof(buf) % 256, 0,0,0))
+ return (-1);
+
+ *tracks = buf[3] - buf[2] + 1;
+ return (0);
+}
+
+/*
+ * Pause the CD.
+ */
+int
+wm_scsi2_pause(struct wm_drive *d)
+{
+ return (wm_scsi2_pause_resume(d, 0));
+}
+
+/*
+ * Resume playing after a pause.
+ */
+int
+wm_scsi2_resume(struct wm_drive *d)
+{
+ return (wm_scsi2_pause_resume(d, 1));
+}
+
+/*
+ * Stop playing the CD by sending a START STOP UNIT command.
+ */
+int
+wm_scsi2_stop(struct wm_drive *d)
+{
+ return (sendscsi(d, NULL, 0, 0, SCMD_START_STOP, 0, 0,0,0,0,0,0,0,0,0,0));
+}
+
+/*
+ * Eject the CD by sending a START STOP UNIT command.
+ */
+int
+wm_scsi2_eject(struct wm_drive *d)
+{
+ /* Unlock the disc (possibly unnecessary). */
+ if (wm_scsi2_prevent(d, 0))
+ return (-1);
+ wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_VERB, "Issuing START_STOP for ejecting...\n");
+ return (sendscsi(d, NULL, 0, 0, SCMD_START_STOP, 2, 0,0,0,0,0,0,0,0,0,0));
+}
+
+/*
+ * Something like a dummy. The SCSI-2 specs are too hard for me to
+ * understand here...
+ *
+ * If you have the correct command handy, please send the code to
+ * milliByte@DeathsDoor.com
+ */
+int
+wm_scsi2_closetray(struct wm_drive *d)
+{
+ wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_VERB, "Issuing START_STOP for closing...\n");
+ return (sendscsi(d, NULL, 0,0, SCMD_START_STOP, 2, 0,0,0,0,0,0,0,0,0,0));
+}
+
+/*
+ * Get the volume by doing a MODE SENSE command.
+ */
+int
+wm_scsi2_get_volume(struct wm_drive *d, int *left, int *right)
+{
+ unsigned char mode[16];
+
+ *left = *right = -1;
+
+ /* Get the current audio parameters first. */
+ if (wm_scsi_mode_sense(d, PAGE_AUDIO, mode))
+ return (-1);
+
+ *left = ((int) mode[9] * 100) / 255;
+ *right = ((int) mode[11] * 100) / 255;
+
+ return (0);
+}
+
+/*
+ * Set the volume by doing a MODE SELECT command.
+ */
+int
+wm_scsi2_set_volume(struct wm_drive *d, int left, int right)
+{
+ unsigned char mode[16];
+
+ /* Get the current audio parameters first. */
+ if (wm_scsi_mode_sense(d, PAGE_AUDIO, mode))
+ return (-1);
+
+ /* Tweak the volume part of the parameters. */
+ mode[9] = (left * 255) / 100;
+ mode[11] = (right * 255) / 100;
+
+ /* And send them back to the drive. */
+ return (wm_scsi_mode_select(d, mode, sizeof(mode)));
+}
+
+/*------------------------------------------------------------------------*
+ * wm_scsi_get_cdtext(drive, buffer, lenght)
+ *
+ * Return a buffer with cdtext-stream. buffer mus be allocated and filled
+ *
+ *
+ *------------------------------------------------------------------------*/
+
+int
+wm_scsi_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_length)
+{
+ int ret;
+ unsigned char temp[8];
+ unsigned char *dynamic_temp;
+ int cdtext_possible;
+ unsigned short cdtext_data_length;
+ unsigned long feature_list_length;
+#define IGNORE_FEATURE_LIST
+#ifndef IGNORE_FEATURE_LIST
+ struct feature_list_header *pHeader;
+ struct feature_descriptor_cdread *pDescriptor;
+#endif /* IGNORE_FEATURE_LIST */
+
+ dynamic_temp = NULL;
+ cdtext_possible = 0;
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wm_scsi_get_cdtext entered\n");
+
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: use GET_FEATURY_LIST(0x46)...\n");
+ ret = sendscsi(d, temp, 8, 1,
+ 0x46, 0x02, 0x00, 0x1E, 0,
+ 0, 0, 0, 8, 0, 0, 0);
+
+ if(ret)
+ {
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT ERROR: GET_FEATURY_LIST(0x46) not implemented or broken. ret = %i!\n", ret);
+#ifndef IGNORE_FEATURE_LIST
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT ERROR: Try #define IGNORE_FEATURE_LIST in libwm/scsi.c\n");
+#else
+ cdtext_possible = 1;
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: GET_FEATURY_LIST(0x46) ignored. It's OK, because many CDROMS don't implement this feature\n");
+#endif /* IGNORE_FEATURE_LIST */
+ }
+ else
+ {
+ feature_list_length = temp[0]*0xFFFFFF + temp[1]*0xFFFF + temp[2]*0xFF + temp[3] + 4;
+
+ dynamic_temp = malloc(feature_list_length);
+
+ if(!dynamic_temp)
+ return -1;
+
+ memset(dynamic_temp, 0, feature_list_length);
+ ret = sendscsi(d, dynamic_temp, feature_list_length, 1,
+ 0x46, 0x02, 0x00, 0x1E, 0, 0,
+ 0, (feature_list_length>>8) & 0xFF, feature_list_length & 0xFF, 0, 0, 0);
+
+
+#ifndef IGNORE_FEATURE_LIST
+ if(!ret)
+ {
+ pHeader = (struct feature_list_header*)dynamic_temp;
+/* printf("length = %i, profile = 0x%02X%02X\n", pHeader->lenght_lsb, pHeader->profile_msb, pHeader->profile_lsb);*/
+ pDescriptor = (struct feature_descriptor_cdread*)(dynamic_temp + sizeof(struct feature_list_header));
+/* printf("code = 0x%02X%02X, settings = 0x%02X, add_length = %i, add_settings = 0x%02X \n",
+ pDescriptor->feature_code_msb, pDescriptor->feature_code_lsb, pDescriptor->settings,
+ pDescriptor->add_lenght, pDescriptor->add_settings);*/
+
+ cdtext_possible = pDescriptor->add_settings & 0x01;
+ }
+ else
+ {
+ cdtext_possible = 0;
+ }
+
+#else
+ cdtext_possible = 1;
+#endif /* IGNORE_FEATURE_LIST */
+
+ free (dynamic_temp);
+ dynamic_temp = 0;
+ }
+
+ if(!cdtext_possible)
+ {
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: GET_FEATURY_LIST(0x46) says, CDTEXT is not present!\n");
+ return EXIT_SUCCESS;
+ }
+
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: try to read, how long CDTEXT is?\n");
+ ret = sendscsi(d, temp, 4, 1,
+ SCMD_READ_TOC, 0x00, 0x05, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0);
+
+ if(ret)
+ {
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS,
+ "CDTEXT ERROR: READ_TOC(0x43) with format code 0x05 not implemented or broken. ret = %i!\n", ret);
+ }
+ else
+ {
+ cdtext_data_length = temp[0]*0xFF + temp[1] + 4 + 1; /* divide by 18 + 4 ? */
+ /* cdtext_data_length%18 == 0;? */
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: CDTEXT is %i byte(s) long\n", cdtext_data_length);
+ /* cdc_buffer[2]; cdc_buffer[3]; reserwed */
+ dynamic_temp = malloc(cdtext_data_length);
+ if(!dynamic_temp)
+ return -1;
+
+ memset(dynamic_temp, 0, cdtext_data_length);
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: try to read CDTEXT\n");
+ ret = sendscsi(d, dynamic_temp, cdtext_data_length, 1,
+ SCMD_READ_TOC, 0x00, 0x05, 0, 0, 0,
+ 0, (cdtext_data_length>>8) & 0xFF, cdtext_data_length & 0xFF, 0, 0, 0);
+
+ if(ret)
+ {
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS,
+ "CDTEXT ERROR: READ_TOC(0x43) with format code 0x05 not implemented or broken. ret = %i!\n", ret);
+ }
+ else
+ {
+ cdtext_data_length = temp[0]*0xFF + temp[1] + 4 + 1; /* divide by 18 + 4 ? */
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: read %i byte(s) of CDTEXT\n", cdtext_data_length);
+
+ /* send cdtext only 18 bytes packs * ? */
+ *(p_buffer_length) = cdtext_data_length - 4;
+ *pp_buffer = malloc(*p_buffer_length);
+ if(!(*pp_buffer))
+ {
+ return -1;
+ }
+ memcpy(*pp_buffer, dynamic_temp + 4, *p_buffer_length);
+ }
+ free(dynamic_temp);
+ dynamic_temp = 0;
+ }
+
+ return ret;
+} /* wm_scsi_get_cdtext() */
diff --git a/kscd/libwm/wm_helpers.c b/kscd/libwm/wm_helpers.c
new file mode 100644
index 00000000..109efe1b
--- /dev/null
+++ b/kscd/libwm/wm_helpers.c
@@ -0,0 +1,238 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Some helpful functions...
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <sys/time.h>
+#include "include/workman_defs.h"
+#include "include/wm_config.h"
+#include "include/wm_helpers.h"
+#include "include/wm_struct.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_MISC
+
+int wm_lib_verbosity = WM_MSG_LEVEL_NONE;
+
+/*
+ * Some seleced functions of version reporting follow...
+ */
+
+int wm_libver_major( void ){return WM_LIBVER_MAJOR;}
+int wm_libver_minor( void ){return WM_LIBVER_MINOR;}
+int wm_libver_pl( void ){return WM_LIBVER_PL;}
+
+char *wm_libver_name( void )
+{
+ char *s = NULL;
+
+ wm_strmcat(&s, WM_LIBVER_NAME);
+ return s;
+} /* wm_libver_name() */
+
+char *wm_libver_number( void )
+{
+ char *s = NULL;
+
+ s = malloc(10);
+ /* this is not used very often, so don't care about speed...*/
+ sprintf(s, "%d.%d.%d", wm_libver_major(), wm_libver_minor(), wm_libver_pl());
+ return s;
+} /* wm_libver_number() */
+
+char *wm_libver_date( void )
+{
+ char *s = NULL;
+ wm_strmcat(&s, __DATE__);
+ return s;
+} /* wm_libver_date() */
+
+char *wm_libver_string( void )
+{
+ char *s = NULL;
+
+ wm_strmcat( &s, wm_libver_name() );
+ wm_strmcat( &s, " " );
+ wm_strmcat( &s, wm_libver_number() );
+ return s;
+} /* wm_libver_string() */
+
+
+/*
+ *
+ * Now for some memory management...
+ *
+ */
+
+/* Free some memory and set a pointer to null. */
+void freeup( char **x )
+{
+ if (*x != NULL)
+ {
+ free(*x);
+ *x = NULL;
+ }
+} /* freeup() */
+
+/* Copy into a malloced string. */
+void
+wm_strmcpy( char **t, const char *s )
+{
+ wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "wm_strmcpy(%s, '%s')\n", *t, s);
+ if (*t != NULL)
+ {
+ wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "wm_strmcpy freeing pointer %p\n", *t);
+ free(*t);
+ }
+
+ *t = malloc(strlen(s) + 1);
+ if (*t == NULL)
+ {
+ perror("wm_strmcpy");
+ exit(1);
+ }
+
+ wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "wm_strmcpy finally copying (%p, '%s')\n", *t, s);
+ strncpy(*t, s, strlen(s));
+} /* wm_strmcpy() */
+
+/* Add to a malloced string. */
+void
+wm_strmcat( char **t, const char *s)
+{
+ int len = strlen(s) + 1;
+
+ wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "wm_strmcat(%s, %s)\n", *t, s);
+
+ if (*s == '\0')
+ return;
+
+ if (*t != NULL)
+ {
+ len += strlen(*t);
+ *t = realloc(*t, len);
+ if (*t == NULL)
+ {
+ perror("wm_strmcat");
+ exit(1);
+ }
+ strcat(*t, s);
+ }
+ else
+ wm_strmcpy(t, s);
+} /* wm_strmcat() */
+
+/* Duplicate a string. Some systems have this in libc, but not all. */
+char *
+wm_strdup( char *s )
+{
+ char *new;
+
+ new = malloc(strlen(s) + 1);
+ if (new)
+ strcpy(new, s);
+ return (new);
+} /* wm_strdup() */
+
+
+/*
+ * set and get verbosity level.
+ */
+void wm_lib_set_verbosity( int level )
+{
+ if( WM_MSG_LEVEL_NONE <= level && level <= WM_MSG_LEVEL_DEBUG )
+ {
+ wm_lib_verbosity = level;
+ wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "Verbosity set to %d|%d\n", WM_MSG_LEVEL_DEBUG, level & WM_MSG_CLASS_ALL);
+ }
+} /* wm_lib_set_verbosity */
+
+int wm_lib_get_verbosity( void )
+{
+ return wm_lib_verbosity;
+}
+
+/*
+ * wm_lib_message().
+ *
+ * any message that falls into allowed classes and has at least
+ * verbosity level wm_lib_verbosity & 0xf will be printed.
+ *
+ * Usage:
+ *
+ * wm_lib_message( WM_MSG_LEVEL | WM_MSG_CLASS, "format", contents);
+ *
+ * To simplify the usage, you may simply use WM_MSG_CLASS. It should be
+ * defined in each module to reflect the correct message class.
+ *
+ */
+void wm_lib_message( unsigned int level, const char *fmt, ... )
+{
+ va_list ap;
+ /* verbosity level */
+ unsigned int vlevel = wm_lib_verbosity & 0xf;
+ /* allowed classes */
+ unsigned int vclass = (level & WM_MSG_CLASS_ALL) & (wm_lib_verbosity & WM_MSG_CLASS_ALL);
+
+ /*
+ * just give me the level
+ */
+ level &= 0xf;
+ if(level <= WM_MSG_LEVEL_NONE)
+ {
+ fprintf(stderr, "LibWorkMan warning: A LibWorkMan programmer specified an invalid message level.\n");
+ }
+ /*
+ * print it only if level and class are allowed.
+ */
+ if( (level <= vlevel) && (vclass != 0) )
+ {
+ fprintf(stderr, "libWorkMan: ");
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+} /* wm_lib_message() */
+
+/*
+ * Simulate usleep() using select().
+ */
+int
+wm_susleep( int usec )
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = usec / 1000000;
+ tv.tv_usec = usec % 1000000;
+ return (select(0, NULL, NULL, NULL, &tv));
+} /* wm_susleep() */
+
+
diff --git a/kscd/panel.ui b/kscd/panel.ui
new file mode 100644
index 00000000..3b6b218a
--- /dev/null
+++ b/kscd/panel.ui
@@ -0,0 +1,468 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>kscdPanelDlg</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>kscdPanelDlg</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>516</width>
+ <height>198</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>KsCD</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>songListCB</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>timeIcon</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Track progress</string>
+ </property>
+ </widget>
+ <widget class="QSlider">
+ <property name="name">
+ <cstring>timeSlider</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>100</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="tracking">
+ <bool>false</bool>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Track progress</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer7</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>volumeIcon</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Volume control</string>
+ </property>
+ </widget>
+ <widget class="QSlider">
+ <property name="name">
+ <cstring>volumeSlider</cstring>
+ </property>
+ <property name="maxValue">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Volume control</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="spacing">
+ <number>1</number>
+ </property>
+ <widget class="QPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>ejectPB</cstring>
+ </property>
+ <property name="text">
+ <string>E&amp;ject</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QFrame" row="0" column="2" rowspan="3" colspan="2">
+ <property name="name">
+ <cstring>backdrop</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>270</width>
+ <height>80</height>
+ </size>
+ </property>
+ <property name="paletteForegroundColor">
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ <property name="paletteBackgroundColor">
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>9</pointsize>
+ </font>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>2</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QFrame" row="0" column="0" rowspan="2" colspan="2">
+ <property name="name">
+ <cstring>frameleds</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>35</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>statuslabel</cstring>
+ </property>
+ <property name="text">
+ <string>Status</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>volumelabel</cstring>
+ </property>
+ <property name="text">
+ <string>Vol: --</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="3">
+ <property name="name">
+ <cstring>totaltimelabel</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="3">
+ <property name="name">
+ <cstring>tracklabel</cstring>
+ </property>
+ <property name="text">
+ <string>--/--</string>
+ </property>
+ </widget>
+ <widget class="QWidget" row="0" column="4" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>symbols</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>30</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text" stdset="0">
+ <string>Vol: --</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>artistlabel</cstring>
+ </property>
+ <property name="text">
+ <string>Artist</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>titlelabel</cstring>
+ </property>
+ <property name="text">
+ <string>Title</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QPushButton" row="3" column="0">
+ <property name="name">
+ <cstring>shufflePB</cstring>
+ </property>
+ <property name="text">
+ <string>R&amp;andom</string>
+ </property>
+ <property name="toggleButton">
+ <bool>true</bool>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="3" column="3">
+ <property name="name">
+ <cstring>infoPB</cstring>
+ </property>
+ <property name="text">
+ <string>E&amp;xtras</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>playPB</cstring>
+ </property>
+ <property name="text">
+ <string>Pla&amp;y</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="0">
+ <property name="name">
+ <cstring>stopPB</cstring>
+ </property>
+ <property name="text">
+ <string>Stop</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="3" column="2">
+ <property name="name">
+ <cstring>cddbPB</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;CDDB</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="3" column="1">
+ <property name="name">
+ <cstring>repeatPB</cstring>
+ </property>
+ <property name="text">
+ <string>Loop</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="toggleButton">
+ <bool>true</bool>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="1">
+ <property name="name">
+ <cstring>nextPB</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Next</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="0">
+ <property name="name">
+ <cstring>prevPB</cstring>
+ </property>
+ <property name="text">
+ <string>Pre&amp;vious</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>playPB</sender>
+ <signal>clicked()</signal>
+ <receiver>kscdPanelDlg</receiver>
+ <slot>playClicked()</slot>
+ </connection>
+ <connection>
+ <sender>nextPB</sender>
+ <signal>clicked()</signal>
+ <receiver>kscdPanelDlg</receiver>
+ <slot>nextClicked()</slot>
+ </connection>
+ <connection>
+ <sender>prevPB</sender>
+ <signal>clicked()</signal>
+ <receiver>kscdPanelDlg</receiver>
+ <slot>prevClicked()</slot>
+ </connection>
+ <connection>
+ <sender>stopPB</sender>
+ <signal>clicked()</signal>
+ <receiver>kscdPanelDlg</receiver>
+ <slot>stopClicked()</slot>
+ </connection>
+ <connection>
+ <sender>ejectPB</sender>
+ <signal>clicked()</signal>
+ <receiver>kscdPanelDlg</receiver>
+ <slot>ejectClicked()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>songListCB</tabstop>
+ <tabstop>timeSlider</tabstop>
+ <tabstop>volumeSlider</tabstop>
+ <tabstop>playPB</tabstop>
+ <tabstop>stopPB</tabstop>
+ <tabstop>ejectPB</tabstop>
+ <tabstop>prevPB</tabstop>
+ <tabstop>nextPB</tabstop>
+ <tabstop>shufflePB</tabstop>
+ <tabstop>repeatPB</tabstop>
+ <tabstop>cddbPB</tabstop>
+ <tabstop>infoPB</tabstop>
+</tabstops>
+<slots>
+ <slot>playClicked()</slot>
+ <slot>stopClicked()</slot>
+ <slot>prevClicked()</slot>
+ <slot>ejectClicked()</slot>
+ <slot returnType="bool">nextClicked()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kscd/prefs.kcfgc b/kscd/prefs.kcfgc
new file mode 100644
index 00000000..89d94319
--- /dev/null
+++ b/kscd/prefs.kcfgc
@@ -0,0 +1,5 @@
+# Code generation options for kconfig_compiler
+File=kscd.kcfg
+ClassName=Prefs
+Singleton=true
+Mutators=true
diff --git a/kscd/version.h b/kscd/version.h
new file mode 100644
index 00000000..9a87fa75
--- /dev/null
+++ b/kscd/version.h
@@ -0,0 +1 @@
+#define KSCDVERSION "1.6"
diff --git a/kscd/workman2cddb.pl b/kscd/workman2cddb.pl
new file mode 100755
index 00000000..539ce929
--- /dev/null
+++ b/kscd/workman2cddb.pl
@@ -0,0 +1,86 @@
+#!/usr/bin/perl
+#
+# usage: just start it. Then copy the files to the local database.
+#
+# I have a extra cddb category (x-converted) for them, becauce I'm to
+# lazy to sort them all into the right category...
+#
+# make sure the output directory does *not* exist, the script refuses
+# to work if it does (just to make sure that it does not overwrite
+# something important)
+#
+# (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de> / Copying: GNU GPL
+#
+
+# config: output directory
+$dir="/tmp/cddb";
+
+#############################################################################
+
+$out=0;
+open(IN,"$ENV{'HOME'}/.workmandb") ||\
+ die "open $ENV{'HOME'}/.workmandb: $!";
+mkdir("$dir",0777) || die "mkdir $dir: $!";
+
+while (<IN>) {
+ if (/^tracks/) {
+ if ($out) {
+ print OUT "EXTD=\n";
+ for ($i = 2, $n = 0; $i < $t[1]+2; $i++) {
+ printf OUT "EXTT%d=\n",$i-2;
+ }
+ print OUT "PLAYORDER=\n";
+ close OUT;
+ }
+ @t = split;
+ for ($i = 2, $n = 0; $i < $t[1]+2; $i++) {
+ $len = $t[$i]/75;
+ for ($quer = 0; $len > 0; $quer += $len % 10, $len /= 10) {};
+ $n += $quer + ($len % 60);
+ }
+ $m = ($t[$t[1]+2] - int($t[2]/75));
+ $id = (($n % 0xff) << 24) | ($m << 8) | $t[1];
+ $magic = sprintf("%08x",$id);
+ open(OUT,">$dir/$magic") || die "open $dir/$magic: $!";
+ $out=1;
+ }
+
+ $cdname = $1 if (/^cdname (.*)/);
+ if (/^artist (.*)/) {
+ $artist = $1;
+
+ print OUT "# xmcd 2.0 CD database file\n";
+ print OUT "#\n";
+ print OUT "# Track frame offsets:\n";
+ for ($i = 2, $n = 0; $i < $t[1]+2; $i++) {
+ print OUT "#\t$t[$i]\n";
+ }
+ print OUT "#\n";
+ print OUT "# Disc length: $t[$t[1]+2] seconds\n";
+ print OUT "#\n";
+ print OUT "# Revision: 1\n";
+ print OUT "# Submitted via: wdb2cddb 1.0\n";
+ print OUT "#\n";
+
+ print OUT "DISCID=$magic\n";
+ print OUT "DTITLE=$artist / $cdname\n";
+ print "$magic - $artist / $cdname\n";
+ $track=0;
+ }
+ if (/^track (.*)/) {
+ print OUT "TTITLE$track=$1\n";
+ $track++;
+ }
+}
+
+close IN;
+
+print OUT "EXTD=\n";
+for ($i = 2, $n = 0; $i < $t[1]+2; $i++) {
+ printf OUT "EXTT%d=\n",$i-2;
+}
+print OUT "PLAYORDER=\n";
+close OUT;
+
+print "\n*** The CDDB files are in $dir ***\n\n";
+
diff --git a/kscd/xmcd.desktop b/kscd/xmcd.desktop
new file mode 100644
index 00000000..a89db6f2
--- /dev/null
+++ b/kscd/xmcd.desktop
@@ -0,0 +1,75 @@
+[Desktop Entry]
+Comment=CD Database File
+Comment[af]=Cd Databasis Lêer
+Comment[az]=CD Databeyzi Faylı
+Comment[bg]=Файл с база от данни за КД
+Comment[bn]=সিডি ডেটাবেস ফাইল
+Comment[br]=Restr Stlennvon CD
+Comment[bs]=CD baza podataka
+Comment[ca]=Fitxer de base de dades de CD
+Comment[cs]=Soubor CD databáze
+Comment[cy]=Ffeil Cronfa Ddata CD
+Comment[da]=Cd-databasefil
+Comment[de]=CD-Datenbank-Datei
+Comment[el]=Αρχείο βάσης δεδομένων CD
+Comment[eo]=Lumdiskdatumbazo-dosiero
+Comment[es]=Base de datos de CDs
+Comment[et]=CD andmebaasi fail
+Comment[eu]=CD datubase fitxategia
+Comment[fa]=پروندۀ دادگان دیسک فشرده
+Comment[fi]=CD-tietokantatiedosto
+Comment[fr]=Banque de données pour CD
+Comment[gl]=Ficheiro de base de datos de CD
+Comment[he]=קובץ מסד נתוני תקליטור
+Comment[hi]=सीडी डाटाबेस फ़ाइल
+Comment[hr]=CD baza datoteka
+Comment[hu]=CD-adatbázis fájl
+Comment[id]=File Database CD
+Comment[is]=CD gagnasafnsskrá
+Comment[it]=File di database CD
+Comment[ja]=CD データベースファイル
+Comment[kk]=CD деректер қоры файлы
+Comment[km]=ឯកសារ​មូលដ្ឋាន​ទិន្នន័យ​ស៊ីឌី
+Comment[ko]=CD 데이터베이스 파일
+Comment[lt]=CD duomenų bazės Byla
+Comment[lv]=CD Datubāzes Fails
+Comment[mk]=Датотека за база на податоци за CD
+Comment[ms]=Fail Pangkalan Data CD
+Comment[mt]=Fajl Database tas-CDs
+Comment[nb]=CD-databasefil
+Comment[nds]=CD-Datenbankdatei
+Comment[ne]=CD डाटाबेस फाइल
+Comment[nl]=Bestand met cd-database
+Comment[nn]=CD-databasefil
+Comment[pa]=CD ਡਾਟਾਬੇਸ ਫਾਇਲ
+Comment[pl]=Baza danych CD
+Comment[pt]=Ficheiro de dados de CDs
+Comment[pt_BR]=Arquivo de Dados de CDs
+Comment[ro]=Fişier bază de date CD
+Comment[ru]=Файл базы данных CD
+Comment[se]=CD-diehtovuođđofiila
+Comment[sk]=Databázový súbor CD
+Comment[sl]=Zbirka podatkov o CD
+Comment[sr]=Фајл CD базе података
+Comment[sr@Latn]=Fajl CD baze podataka
+Comment[sv]=Cd-databasfil
+Comment[ta]=குறுந்தகடு தரவுத்தளக் கோப்பு
+Comment[tg]=Файли Манбаъи Додаҳои Диски Фишурда
+Comment[th]=แฟ้มฐานข้อมูลซีดี
+Comment[tr]=CD Veritabanı Dosyası
+Comment[uk]=Файл бази даних CD
+Comment[uz]=CD maʼlumot baza fayli
+Comment[uz@cyrillic]=CD маълумот база файли
+Comment[ven]=CD ya databeizi ya faela
+Comment[wa]=Båze di dnêyes des plakes
+Comment[xh]=Ifayile yesiseko sedata ye CD
+Comment[zh_CN]=CD 数据库文件
+Comment[zh_HK]=CD 資料庫檔案
+Comment[zh_TW]=CD 資料庫檔案
+Comment[zu]=Ifayela yesiseko sedata ye CD
+DefaultApp=kedit
+Icon=cdtrack
+Type=MimeType
+MimeType=text/xmcd
+Patterns=
+
diff --git a/libkcddb/Makefile.am b/libkcddb/Makefile.am
new file mode 100644
index 00000000..e5b88c90
--- /dev/null
+++ b/libkcddb/Makefile.am
@@ -0,0 +1,33 @@
+SUBDIRS = . kcmcddb test
+
+INCLUDES = -I$(srcdir)/.. $(all_includes)
+
+lib_LTLIBRARIES = libkcddb.la
+
+libkcddb_la_SOURCES = \
+ cache.cpp cdinfo.cpp config.cpp client.cpp cddb.cpp lookup.cpp \
+ cddbplookup.cpp synccddbplookup.cpp asynccddbplookup.cpp httplookup.cpp \
+ synchttplookup.cpp asynchttplookup.cpp smtpsubmit.cpp \
+ asyncsmtpsubmit.cpp syncsmtpsubmit.cpp configbase.kcfgc \
+ submit.cpp sites.cpp httpsubmit.cpp asynchttpsubmit.cpp \
+ synchttpsubmit.cpp cdinfodialogbase.ui categories.cpp genres.cpp \
+ cdinfoencodingwidget.cpp cdinfoencodingwidgetbase.ui
+
+libkcddb_la_LDFLAGS = $(all_libraries) -version-info 1:0:0
+libkcddb_la_LIBADD = $(LIB_KDECORE) $(LIB_KIO)
+
+METASOURCES = AUTO
+
+kde_kcfg_DATA = libkcddb.kcfg
+
+kcddbincludedir = $(includedir)/libkcddb
+kcddbinclude_HEADERS = \
+ cache.h cdinfo.h client.h config.h cddb.h configbase.h
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/libkcddb.pot
+
+config.lo: configbase.h
+
+include $(top_srcdir)/admin/Doxyfile.am
+
diff --git a/libkcddb/TODO b/libkcddb/TODO
new file mode 100644
index 00000000..a556d761
--- /dev/null
+++ b/libkcddb/TODO
@@ -0,0 +1,12 @@
+Better error checking, for example checking if the config is empty
+
+Try to comply with freedb's: "Guidelines for optimal freedb support"
+http://www.freedb.org/modules.php?name=Sections&sop=viewarticle&artid=38
+
+Saving playlist
+
+Make it possible for a program to in some way mark that
+an entry is not coming from freedb, but created from the
+program. Now it's not possible to know the difference, so
+the revision of a new entry is always 1 instead of 0 as
+it should be
diff --git a/libkcddb/asynccddbplookup.cpp b/libkcddb/asynccddbplookup.cpp
new file mode 100644
index 00000000..f1ebe528
--- /dev/null
+++ b/libkcddb/asynccddbplookup.cpp
@@ -0,0 +1,352 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ Copyright (C) 2005 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <kdebug.h>
+
+#include "asynccddbplookup.h"
+
+namespace KCDDB
+{
+ AsyncCDDBPLookup::AsyncCDDBPLookup()
+ : CDDBPLookup(),
+ state_(Idle)
+ {
+
+ }
+
+ AsyncCDDBPLookup::~AsyncCDDBPLookup()
+ {
+ }
+
+ CDDB::Result
+ AsyncCDDBPLookup::lookup
+ (
+ const QString & hostname,
+ uint port,
+ const TrackOffsetList & trackOffsetList
+ )
+ {
+ socket_ = new KNetwork::KBufferedSocket(hostname,QString::number(port));
+
+ socket_->setBlocking( false );
+
+ connect (socket_, SIGNAL(gotError(int)), SLOT(slotGotError(int)));
+
+ connect (socket_, SIGNAL( connected(const KResolverEntry &) ),
+ SLOT( slotConnectionSuccess() ) );
+
+ connect (socket_, SIGNAL( readyRead() ), SLOT( slotReadyRead() ) );
+
+ if ( trackOffsetList.count() < 3 )
+ return UnknownError;
+
+ trackOffsetList_ = trackOffsetList;
+
+ state_ = WaitingForConnection;
+
+ if ( !socket_->connect(hostname, QString::number(port)) )
+ {
+ state_ = Idle;
+ emit finished( NoResponse );
+ return NoResponse;
+ }
+
+ return Success;
+ }
+
+ void
+ AsyncCDDBPLookup::slotGotError(int error)
+ {
+ state_ = Idle;
+
+ if ( error == KNetwork::KSocketBase::LookupFailure )
+ emit finished( HostNotFound );
+ else if ( error == KNetwork::KSocketBase::ConnectionTimedOut ||
+ error == KNetwork::KSocketBase::NetFailure )
+ emit finished( NoResponse );
+ else
+ emit finished( UnknownError );
+ }
+
+ void
+ AsyncCDDBPLookup::slotConnectionSuccess()
+ {
+ kdDebug(60010) << "Connection successful" << endl;
+ state_ = WaitingForGreeting;
+ }
+
+ void
+ AsyncCDDBPLookup::slotReadyRead()
+ {
+ kdDebug(60010) << "Ready to read. State: " << stateToString() << endl;
+
+ while ( Idle != state_ && isConnected() && socket_->canReadLine() )
+ read();
+ }
+
+ void
+ AsyncCDDBPLookup::read()
+ {
+ switch ( state_ )
+ {
+ case WaitingForGreeting:
+
+ if ( !parseGreeting( readLine() ) )
+ {
+ result_ = ServerError;
+ doQuit();
+ return;
+ }
+
+ doHandshake();
+
+ break;
+
+ case WaitingForHandshake:
+
+ if ( !parseHandshake( readLine() ) )
+ {
+ result_ = ServerError;
+ doQuit();
+ return;
+ }
+
+ doProto();
+
+ break;
+
+ case WaitingForProtoResponse:
+
+ // Ignore the response for now
+ readLine();
+
+ doQuery();
+
+ break;
+
+ case WaitingForQueryResponse:
+ result_ = parseQuery( readLine() );
+
+ switch ( result_ )
+ {
+ case Success:
+ requestCDInfoForMatch();
+ break;
+
+ case MultipleRecordFound:
+ state_ = WaitingForMoreMatches;
+ break;
+
+ default: // Error :(
+ doQuit();
+ return;
+ }
+
+ break;
+
+ case WaitingForMoreMatches:
+ {
+ QString line = readLine();
+
+ if (line.startsWith("."))
+ requestCDInfoForMatch();
+ else
+ parseExtraMatch( line );
+ }
+
+ break;
+
+ case WaitingForCDInfoResponse:
+ {
+ Result result = parseRead( readLine() );
+
+ if ( Success != result )
+ {
+ result_ = result;
+ doQuit();
+ return;
+ }
+
+ state_ = WaitingForCDInfoData;
+ }
+
+ break;
+
+ case WaitingForCDInfoData:
+ {
+ QString line = readLine();
+
+ if (line.startsWith("."))
+ {
+ parseCDInfoData();
+ requestCDInfoForMatch();
+ }
+ else
+ cdInfoBuffer_ << line;
+ }
+
+ break;
+
+ case WaitingForQuitResponse:
+
+ state_ = Idle;
+
+ while ( socket_->bytesAvailable() )
+ socket_->getch();
+
+ close();
+
+ emit finished( result_ );
+
+ break;
+
+ default:
+
+ break;
+ }
+ }
+
+ QString
+ AsyncCDDBPLookup::readLine()
+ {
+ return QString::fromUtf8(socket_->readLine());
+ }
+
+ void
+ AsyncCDDBPLookup::doHandshake()
+ {
+ sendHandshake();
+
+ state_ = WaitingForHandshake;
+ }
+
+ void
+ AsyncCDDBPLookup::doProto()
+ {
+ sendProto();
+
+ state_ = WaitingForProtoResponse;
+ }
+
+ void
+ AsyncCDDBPLookup::doQuery()
+ {
+ sendQuery();
+
+ state_ = WaitingForQueryResponse;
+ }
+
+ void
+ AsyncCDDBPLookup::requestCDInfoForMatch()
+ {
+ if (matchList_.isEmpty())
+ {
+ result_ = cdInfoList_.isEmpty()? NoRecordFound : Success;
+ doQuit();
+ return;
+ }
+
+ CDDBMatch match = matchList_.first();
+ matchList_.remove( match );
+
+ sendRead( match );
+
+ state_ = WaitingForCDInfoResponse;
+ }
+
+ void
+ AsyncCDDBPLookup::parseCDInfoData()
+ {
+ CDInfo info;
+
+ if (info.load( cdInfoBuffer_ ))
+ {
+ info.category = category_;
+ cdInfoList_.append( info );
+ }
+
+ cdInfoBuffer_.clear();
+ }
+
+ void
+ AsyncCDDBPLookup::doQuit()
+ {
+ state_ = WaitingForQuitResponse;
+
+ sendQuit();
+ }
+
+ QString
+ AsyncCDDBPLookup::stateToString() const
+ {
+ switch (state_)
+ {
+ case Idle:
+ return "Idle";
+ break;
+
+ case WaitingForConnection:
+ return "WaitingForConnection";
+ break;
+
+ case WaitingForGreeting:
+ return "WaitingForGreeting";
+ break;
+
+ case WaitingForProtoResponse:
+ return "WaitingForProtoResponse";
+ break;
+
+ case WaitingForHandshake:
+ return "WaitingForHandshake";
+ break;
+
+ case WaitingForQueryResponse:
+ return "WaitingForQueryResponse";
+ break;
+
+ case WaitingForMoreMatches:
+ return "WaitingForMoreMatches";
+ break;
+
+ case WaitingForCDInfoResponse:
+ return "WaitingForCDInfoResponse";
+ break;
+
+ case WaitingForCDInfoData:
+ return "WaitingForCDInfoData";
+ break;
+
+ case WaitingForQuitResponse:
+ return "WaitingForQuitResponse";
+ break;
+
+ default:
+ return "Unknown";
+ break;
+ }
+ }
+}
+
+
+#include "asynccddbplookup.moc"
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/asynccddbplookup.h b/libkcddb/asynccddbplookup.h
new file mode 100644
index 00000000..f0a39c84
--- /dev/null
+++ b/libkcddb/asynccddbplookup.h
@@ -0,0 +1,94 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KCDDB_ASYNC_CDDBP_LOOKUP_H
+#define KCDDB_ASYNC_CDDBP_LOOKUP_H
+
+#include <qobject.h>
+
+#include "cddbplookup.h"
+
+namespace KCDDB
+{
+ class AsyncCDDBPLookup : public CDDBPLookup
+ {
+ Q_OBJECT
+
+ public:
+
+ enum State
+ {
+ Idle,
+ WaitingForConnection,
+ WaitingForGreeting,
+ WaitingForHandshake,
+ WaitingForProtoResponse,
+ WaitingForQueryResponse,
+ WaitingForMoreMatches,
+ WaitingForCDInfoResponse,
+ WaitingForCDInfoData,
+ WaitingForQuitResponse
+ };
+
+ AsyncCDDBPLookup();
+
+ virtual ~AsyncCDDBPLookup();
+
+ Result lookup( const QString &, uint, const TrackOffsetList & );
+
+ signals:
+
+ void finished( CDDB::Result );
+ void quit( CDDB::Result );
+
+ protected slots:
+
+ void slotGotError(int error);
+ void slotConnectionSuccess();
+ void slotReadyRead();
+
+ protected:
+
+ void doHandshake();
+ void doProto();
+ void doQuery();
+ void doQuit();
+
+ bool parseQueryResponse( const QString & );
+ void requestCDInfoForMatch();
+ bool parseCDInfoResponse( const QString & );
+ void parseCDInfoData();
+
+ void read();
+
+ QString readLine();
+
+ QString stateToString() const;
+
+ private:
+
+ State state_;
+ Result result_;
+ QStringList cdInfoBuffer_;
+ };
+}
+
+#endif // KCDDB_ASYNC_CDDBP_LOOKUP_H
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/asynchttplookup.cpp b/libkcddb/asynchttplookup.cpp
new file mode 100644
index 00000000..a183b3d5
--- /dev/null
+++ b/libkcddb/asynchttplookup.cpp
@@ -0,0 +1,159 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <qstringlist.h>
+#include <qapplication.h>
+
+#include <kdebug.h>
+#include <kio/job.h>
+
+#include "asynchttplookup.h"
+
+namespace KCDDB
+{
+ AsyncHTTPLookup::AsyncHTTPLookup()
+ : HTTPLookup()
+ {
+ block_ = false;
+ }
+
+ AsyncHTTPLookup::~AsyncHTTPLookup()
+ {
+ // Empty.
+ }
+
+ CDDB::Result
+ AsyncHTTPLookup::lookup
+ (
+ const QString & hostName,
+ uint port,
+ const TrackOffsetList & trackOffsetList
+ )
+ {
+ if ( trackOffsetList.count() < 3 )
+ return UnknownError;
+
+ trackOffsetList_ = trackOffsetList;
+
+ connect( this, SIGNAL( queryReady() ), SLOT( slotQueryReady() ) );
+ connect( this, SIGNAL( readReady() ), SLOT( requestCDInfoForMatch() ) );
+
+ initURL( hostName, port );
+
+ // Run a query.
+ result_ = runQuery();
+
+ return result_;
+ }
+
+ CDDB::Result
+ AsyncHTTPLookup::runQuery()
+ {
+ data_ = QByteArray();
+ state_ = WaitingForQueryResponse;
+
+ result_ = sendQuery();
+
+ return result_;
+ }
+
+ void
+ AsyncHTTPLookup::slotQueryReady()
+ {
+ kdDebug(60010) << "Matches Found: " << matchList_.count() << endl;
+
+ if ( Success != result_ )
+ {
+ emit finished( result_ );
+ return;
+ }
+
+ requestCDInfoForMatch();
+ }
+
+ void
+ AsyncHTTPLookup::requestCDInfoForMatch()
+ {
+ if ( matchList_.isEmpty() )
+ {
+ result_ = cdInfoList_.isEmpty()? NoRecordFound : Success;
+ emit finished( result_ );
+ return;
+ }
+
+ CDDBMatch match = matchList_.first();
+ matchList_.remove( match );
+
+ data_ = QByteArray();
+ state_ = WaitingForReadResponse;
+
+ result_ = sendRead( match );
+
+ if ( Success != result_ )
+ emit finished( result_ );
+ }
+
+ void
+ AsyncHTTPLookup::slotData( KIO::Job *, const QByteArray &data )
+ {
+ if (data.size() > 0)
+ {
+ QDataStream stream(data_, IO_WriteOnly | IO_Append);
+ stream.writeRawBytes(data.data(), data.size());
+ }
+ }
+
+ void
+ AsyncHTTPLookup::slotResult( KIO::Job *job )
+ {
+ if ( 0 != job->error() )
+ {
+ result_ = ServerError;
+ if ( !block_ )
+ emit queryReady();
+ return;
+ }
+
+ jobFinished();
+ }
+
+ CDDB::Result
+ AsyncHTTPLookup::fetchURL()
+ {
+ kdDebug(60010) << "About to fetch: " << cgiURL_.url() << endl;
+
+ KIO::TransferJob* job = KIO::get( cgiURL_, false, false );
+
+ if ( 0 == job )
+ return ServerError;
+
+ connect( job, SIGNAL( data( KIO::Job *, const QByteArray & ) ),
+ SLOT( slotData( KIO::Job *, const QByteArray & ) ) );
+ connect( job, SIGNAL( result( KIO::Job * ) ),
+ SLOT( slotResult( KIO::Job * ) ) );
+
+ return Success;
+ }
+
+}
+
+#include "asynchttplookup.moc"
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/asynchttplookup.h b/libkcddb/asynchttplookup.h
new file mode 100644
index 00000000..2c4ede05
--- /dev/null
+++ b/libkcddb/asynchttplookup.h
@@ -0,0 +1,62 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KCDDB_ASYNC_HTTP_LOOKUP_H
+#define KCDDB_ASYNC_HTTP_LOOKUP_H
+
+#include "httplookup.h"
+
+namespace KCDDB
+{
+ class AsyncHTTPLookup : public HTTPLookup
+ {
+
+ Q_OBJECT
+
+ public:
+
+ AsyncHTTPLookup();
+ virtual ~AsyncHTTPLookup();
+
+ Result lookup( const QString &, uint, const TrackOffsetList & );
+
+ CDInfoList lookupResponse() const;
+
+ signals:
+
+ void finished( CDDB::Result );
+
+ protected slots:
+ void slotQueryReady();
+ void requestCDInfoForMatch();
+
+ void slotData( KIO::Job *, const QByteArray & );
+ void slotResult( KIO::Job * );
+
+ protected:
+ virtual Result fetchURL();
+
+ Result runQuery();
+ };
+}
+
+#endif // KCDDB_ASYNC_HTTP_LOOKUP_H
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/asynchttpsubmit.cpp b/libkcddb/asynchttpsubmit.cpp
new file mode 100644
index 00000000..ffabc2db
--- /dev/null
+++ b/libkcddb/asynchttpsubmit.cpp
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 2003 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "asynchttpsubmit.h"
+#include <kio/job.h>
+#include <kdebug.h>
+
+namespace KCDDB
+{
+ AsyncHTTPSubmit::AsyncHTTPSubmit(const QString& from, const QString& hostname, uint port)
+ : HTTPSubmit(from, hostname, port)
+ {
+
+ }
+
+ AsyncHTTPSubmit::~AsyncHTTPSubmit()
+ {
+
+ }
+
+ CDDB::Result AsyncHTTPSubmit::runJob(KIO::Job* job)
+ {
+ connect(job, SIGNAL(result(KIO::Job *)), SLOT(slotFinished(KIO::Job *)));
+
+ return CDDB::Success;
+ }
+
+ void AsyncHTTPSubmit::slotFinished(KIO::Job* job)
+ {
+ kdDebug() << "Finished" << endl;
+
+ if ( job->error()==0 )
+ emit finished( Success );
+ else
+ emit finished( UnknownError );
+ }
+}
+
+#include "asynchttpsubmit.moc"
diff --git a/libkcddb/asynchttpsubmit.h b/libkcddb/asynchttpsubmit.h
new file mode 100644
index 00000000..e40f243d
--- /dev/null
+++ b/libkcddb/asynchttpsubmit.h
@@ -0,0 +1,44 @@
+#ifndef ASYNCHTTPSUBMIT_H
+#define ASYNCHTTPSUBMIT_H
+/*
+ Copyright (C) 2003 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "httpsubmit.h"
+
+namespace KCDDB
+{
+ class AsyncHTTPSubmit : public HTTPSubmit
+ {
+ Q_OBJECT
+ public:
+ AsyncHTTPSubmit(const QString& from, const QString& hostname, uint port);
+ virtual ~AsyncHTTPSubmit();
+
+ signals:
+ void finished( CDDB::Result );
+ protected:
+ virtual Result runJob(KIO::Job* job);
+ private slots:
+ void slotFinished(KIO::Job*);
+ } ;
+}
+
+
+#endif // ASYNCHTTPSUBMIT_H
+
diff --git a/libkcddb/asyncsmtpsubmit.cpp b/libkcddb/asyncsmtpsubmit.cpp
new file mode 100644
index 00000000..a51d9eb5
--- /dev/null
+++ b/libkcddb/asyncsmtpsubmit.cpp
@@ -0,0 +1,57 @@
+/*
+ Copyright (C) 2003 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "asyncsmtpsubmit.h"
+#include "cdinfo.h"
+#include <qdatastream.h>
+#include <kdebug.h>
+
+namespace KCDDB
+{
+ AsyncSMTPSubmit::AsyncSMTPSubmit(const QString& hostname, uint port,
+ const QString& username, const QString& from, const QString& to )
+ : SMTPSubmit( hostname, port, username, from, to )
+ {
+
+ }
+
+ AsyncSMTPSubmit::~AsyncSMTPSubmit()
+ {
+
+ }
+
+ CDDB::Result AsyncSMTPSubmit::runJob(KIO::Job* job)
+ {
+ connect( job, SIGNAL( result( KIO::Job* ) ),
+ this, SLOT(slotDone( KIO::Job* ) ) );
+
+ return Success;
+ }
+
+ void AsyncSMTPSubmit::slotDone( KIO::Job* job )
+ {
+ kdDebug(60010) << k_funcinfo << endl;
+ if ( job->error()==0 )
+ emit finished( Success );
+ else
+ emit finished( UnknownError );
+ }
+}
+
+#include "asyncsmtpsubmit.moc"
diff --git a/libkcddb/asyncsmtpsubmit.h b/libkcddb/asyncsmtpsubmit.h
new file mode 100644
index 00000000..7a76f25d
--- /dev/null
+++ b/libkcddb/asyncsmtpsubmit.h
@@ -0,0 +1,46 @@
+#ifndef ASYNCSMTPSUBMIT_H
+#define ASYNCSMTPSUBMIT_H
+/*
+ Copyright (C) 2003-2004 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "smtpsubmit.h"
+#include <kio/job.h>
+
+namespace KCDDB
+{
+ class AsyncSMTPSubmit : public SMTPSubmit
+ {
+ Q_OBJECT
+
+ public:
+ AsyncSMTPSubmit(const QString& hostname, uint port, const QString& username,
+ const QString& from, const QString& to);
+ virtual ~AsyncSMTPSubmit();
+
+ signals:
+ void finished( CDDB::Result );
+ protected slots:
+ void slotDone( KIO::Job * );
+ protected:
+ virtual Result runJob(KIO::Job* job);
+ } ;
+}
+
+#endif // ASYNCSMTPSUBMIT_H
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/cache.cpp b/libkcddb/cache.cpp
new file mode 100644
index 00000000..1dda49e4
--- /dev/null
+++ b/libkcddb/cache.cpp
@@ -0,0 +1,131 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ Copyright (C) 2002 Nadeem Hasan <nhasan@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <kdebug.h>
+#include <kstandarddirs.h>
+
+#include <qfile.h>
+#include <qdir.h>
+
+#include "cache.h"
+#include "config.h"
+
+namespace KCDDB
+{
+ QString
+ Cache::fileName( const QString &category, const QString &discid, const QString &cacheDir )
+ {
+ QDir dir( cacheDir );
+ if ( !dir.exists( category ) )
+ dir.mkdir( category );
+
+ return cacheDir + "/" + category + "/" + discid;
+ }
+
+ CDInfoList
+ Cache::lookup( const QString &cddbId )
+ {
+ kdDebug(60010) << "Looking up " << cddbId << " in CDDB cache" << endl;
+
+ CDInfoList infoList;
+ Config c;
+ c.readConfig();
+ QStringList cddbCacheDirs = c.cacheLocations();
+
+ for (QStringList::Iterator cddbCacheDir = cddbCacheDirs.begin();
+ cddbCacheDir != cddbCacheDirs.end(); ++cddbCacheDir)
+ {
+ QDir dir( *cddbCacheDir );
+ QStringList dirList = dir.entryList( QDir::Dirs );
+
+ QStringList::ConstIterator it = dirList.begin();
+
+ while ( it != dirList.end() )
+ {
+ QString category( *it );
+ if ( category[ 0 ] != '.' )
+ {
+ QFile f( *cddbCacheDir + "/" + category + "/" + cddbId );
+ if ( f.exists() && f.open(IO_ReadOnly) )
+ {
+ QTextStream ts(&f);
+ ts.setEncoding(QTextStream::UnicodeUTF8);
+ QString cddbData = ts.read();
+ f.close();
+ CDInfo info;
+ info.load(cddbData);
+ info.category = category;
+
+ infoList.append( info );
+ }
+ }
+ ++it;
+ }
+ }
+
+ return infoList;
+ }
+
+ void
+ Cache::store(const CDInfoList& list)
+ {
+ CDInfoList::ConstIterator it=list.begin();
+ while (it!=list.end())
+ {
+ CDInfo info( *it );
+ store(info);
+ ++it;
+ }
+ }
+
+ void
+ Cache::store(const CDInfo& info)
+ {
+ Config c;
+ c.readConfig();
+
+ QString cacheDir = c.cacheLocations().first();
+ QDir d(cacheDir);
+ if (!d.exists())
+ d.mkdir(cacheDir);
+
+ // The same entry can contain several discids (separated by a ','),
+ // so we save the entry to all of them
+ QStringList discids = QStringList::split(',', info.id);
+ for (QStringList::Iterator it = discids.begin(); it != discids.end(); ++it)
+ {
+ QString cacheFile = fileName(info.category, *it, cacheDir);
+
+ kdDebug(60010) << "Storing " << cacheFile << " in CDDB cache" << endl;
+
+ QFile f(cacheFile);
+ if ( f.open(IO_WriteOnly) )
+ {
+ QTextStream ts(&f);
+ ts.setEncoding(QTextStream::UnicodeUTF8);
+ ts << info.toString();
+ f.close();
+ }
+ }
+ }
+}
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/cache.h b/libkcddb/cache.h
new file mode 100644
index 00000000..f7b861b2
--- /dev/null
+++ b/libkcddb/cache.h
@@ -0,0 +1,57 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ Copyright (C) 2002 Nadeem Hasan <nhasan@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KCDDB_CACHE_H
+#define KCDDB_CACHE_H
+
+#include <qpair.h>
+#include <qstring.h>
+
+#include "cdinfo.h"
+#include <kdemacros.h>
+namespace KCDDB
+{
+ class KDE_EXPORT Cache
+ {
+ public:
+
+ enum Policy
+ {
+ Only,
+ Use,
+ Ignore
+ };
+
+ static CDInfoList lookup( const QString & );
+ static void store( const CDInfoList & );
+ // KDE4: Should probably take a TrackOffsetList too, so
+ // the list can be stored in the file, and we can make
+ // sure the discid is correct (had to do the same fix in
+ // both kscd and kaudiocreator)
+ static void store( const CDInfo & );
+
+ private:
+ static QString fileName( const QString &category, const QString& discid, const QString &cacheDir );
+ };
+}
+
+#endif // KCDDB_CACHE_H
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/categories.cpp b/libkcddb/categories.cpp
new file mode 100644
index 00000000..522da81b
--- /dev/null
+++ b/libkcddb/categories.cpp
@@ -0,0 +1,50 @@
+// Copyright (C) 2005 by Shaheed Haque (srhaque@iee.org). All rights reserved.
+//
+// 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 <categories.h>
+#include <klocale.h>
+
+KCDDB::Categories::Categories()
+{
+ // These are only 11 Category values defined by CDDB. See
+ //
+ // http://www.freedb.org/modules.php?name=Sections&sop=viewarticle&artid=26
+ //
+ m_cddb << "blues" << "classical" << "country" <<
+ "data" << "folk" << "jazz" << "misc" <<
+ "newage" << "reggae" << "rock" << "soundtrack";
+ m_i18n << i18n("Blues") << i18n("Classical") << i18n("music genre", "Country") <<
+ i18n("Data") << i18n("Folk") << i18n("Jazz") << i18n("Miscellaneous") <<
+ i18n("New Age") << i18n("Reggae") << i18n("Rock") << i18n("Soundtrack");
+}
+
+const QString KCDDB::Categories::cddb2i18n(const QString &category) const
+{
+ int index = m_cddb.findIndex(category.stripWhiteSpace());
+ if (index != -1)
+ {
+ return m_i18n[index];
+ }
+ else
+ {
+ return cddb2i18n("misc");
+ }
+}
+
+const QString KCDDB::Categories::i18n2cddb(const QString &category) const
+{
+ int index = m_i18n.findIndex(category.stripWhiteSpace());
+ if (index != -1)
+ {
+ return m_cddb[index];
+ }
+ else
+ {
+ return "misc";
+ }
+}
diff --git a/libkcddb/categories.h b/libkcddb/categories.h
new file mode 100644
index 00000000..01147111
--- /dev/null
+++ b/libkcddb/categories.h
@@ -0,0 +1,42 @@
+// Copyright (C) 2005 by Shaheed Haque (srhaque@iee.org). All rights reserved.
+//
+// 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 KCDDB_CATEGORIES_H
+#define KCDDB_CATEGORIES_H
+
+#include <qstring.h>
+#include <qstringlist.h>
+
+namespace KCDDB
+{
+ /**
+ * Category values defined by CDDB.
+ */
+ class Categories
+ {
+ public:
+ Categories();
+
+ const QStringList &cddbList() const { return m_cddb; };
+ const QStringList &i18nList() const { return m_i18n; };
+
+ /**
+ * Lookup the CDDB category, and return the i18n'd version.
+ */
+ const QString cddb2i18n(const QString &category) const;
+
+ /**
+ * Lookup the i18n category, and return the CDDB version.
+ */
+ const QString i18n2cddb(const QString &category) const;
+ private:
+ QStringList m_cddb;
+ QStringList m_i18n;
+ };
+}
+
+#endif
diff --git a/libkcddb/cddb.cpp b/libkcddb/cddb.cpp
new file mode 100644
index 00000000..3e5cb893
--- /dev/null
+++ b/libkcddb/cddb.cpp
@@ -0,0 +1,227 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ CopyRight (C) 2002 Nadeem Hasan <nhasan@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <qregexp.h>
+#include <qstringlist.h>
+
+#include <kdebug.h>
+#include <kstringhandler.h>
+#include <klocale.h>
+
+#include "cddb.h"
+
+namespace KCDDB
+{
+ CDDB::CDDB()
+ : user_( "libkcddb-user" ),
+ localHostName_( "localHost" ),
+ readOnly_( false )
+ {
+
+ }
+
+ CDDB::~CDDB()
+ {
+ // Empty.
+ }
+
+ QString
+ CDDB::trackOffsetListToId()
+ {
+ return trackOffsetListToId( trackOffsetList_ );
+ }
+ QString
+ CDDB::trackOffsetListToId( const TrackOffsetList & list )
+ {
+ // Taken from version by Michael Matz in kio_audiocd.
+ unsigned int id = 0;
+ int numTracks = list.count() - 2;
+
+ // The last two in the list are disc begin and disc end.
+ for ( int i = numTracks-1; i >= 0; i-- )
+ {
+ int n = list[ i ]/75;
+ while ( n > 0 )
+ {
+ id += n % 10;
+ n /= 10;
+ }
+ }
+
+ unsigned int l = list[numTracks + 1] / 75;
+ l -= list[0] / 75;
+
+ id = ( ( id % 255 ) << 24 ) | ( l << 8 ) | numTracks;
+
+ return QString::number( id, 16 ).rightJustify( 8, '0' );
+ }
+
+ QString
+ CDDB::trackOffsetListToString()
+ {
+ QString ret;
+ uint numTracks = trackOffsetList_.count()-2;
+
+ // Disc start.
+ ret.append( QString::number( numTracks ) );
+ ret.append( " " );
+
+ for ( uint i = 0; i < numTracks; i++ )
+ {
+ ret.append( QString::number( trackOffsetList_[ i ] ) );
+ ret.append( " " );
+ }
+
+ unsigned int discLengthInSec = ( trackOffsetList_[ numTracks+1 ] ) / 75;
+
+ ret.append( QString::number( discLengthInSec ) );
+
+ return ret;
+ }
+
+ bool
+ CDDB::parseGreeting( const QString & line )
+ {
+ uint serverStatus = statusCode( line );
+
+ if ( 200 == serverStatus )
+ {
+ kdDebug(60010) << "Server response: read-only" << endl;
+ readOnly_ = true;
+ }
+ else if ( 201 == serverStatus )
+ {
+ kdDebug(60010) << "Server response: read-write" << endl;
+ }
+ else
+ {
+ kdDebug(60010) << "Server response: bugger off" << endl;
+ return false;
+ }
+
+ return true;
+ }
+
+ bool
+ CDDB::parseHandshake( const QString & line )
+ {
+ uint serverStatus = statusCode( line );
+
+ if ( ( 200 != serverStatus ) && ( 402 != serverStatus ) )
+ {
+ kdDebug(60010) << "Handshake was too tight. Letting go." << endl;
+ return false;
+ }
+
+ kdDebug(60010) << "Handshake was warm and firm" << endl;
+
+ return true;
+ }
+
+ uint
+ CDDB::statusCode( const QString & line )
+ {
+ QStringList tokenList = QStringList::split( ' ', line );
+
+ uint serverStatus = tokenList[ 0 ].toUInt();
+
+ return serverStatus;
+ }
+
+/* CDDB::Transport
+ CDDB::stringToTransport(const QString & s)
+ {
+ if ("HTTP" == s )
+ return HTTP;
+ else if ( "CDDBP" == s )
+ return CDDBP;
+ else
+ return SMTP;
+ }*/
+
+ QString
+ CDDB::resultToString(Result r)
+ {
+ switch (r)
+ {
+ case Success:
+ return i18n("Success");
+ break;
+
+ case ServerError:
+ return i18n("Server error");
+ break;
+
+ case HostNotFound:
+ return i18n("Host not found");
+ break;
+
+ case NoResponse:
+ return i18n("No response");
+ break;
+
+ case NoRecordFound:
+ return i18n("No record found");
+ break;
+
+ case MultipleRecordFound:
+ return i18n("Multiple records found");
+ break;
+
+ case CannotSave:
+ return i18n("Cannot save");
+ break;
+
+ case InvalidCategory:
+ return i18n("Invalid category");
+ break;
+
+ default:
+ return i18n("Unknown error");
+ break;
+ }
+ }
+
+/* QString
+ CDDB::transportToString(uint t)
+ {
+ switch (Transport(t))
+ {
+ case HTTP:
+ return "HTTP";
+ break;
+
+ case CDDBP:
+ return "CDDBP";
+ break;
+
+ case SMTP:
+ return "SMTP";
+ break;
+
+ default:
+ return "UnknownTransport";
+ break;
+ }
+ }*/
+}
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/cddb.h b/libkcddb/cddb.h
new file mode 100644
index 00000000..b32821de
--- /dev/null
+++ b/libkcddb/cddb.h
@@ -0,0 +1,92 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ CopyRight (C) 2002 Nadeem Hasan <nhasan@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KCDDB_CDDB_H
+#define KCDDB_CDDB_H
+
+#include <qstring.h>
+#include <qpair.h>
+#include <qvaluelist.h>
+
+#include <kdelibs_export.h>
+
+/** The KCDDB namespace collects all the CDDB-related classes and methods. */
+namespace KCDDB
+{
+ /** This list is used to calculate the CDDB disc id.
+ Insert the start frames ((minute*60 + seconds)*75+frames)
+ of all tracks, followed by the first frame of the disc and the last
+ frame of the disc. The first frame is for most audio CD's the same
+ as the first frame of the first track, the
+ last one is the start frame of the leadout track.
+ */
+ typedef QValueList<uint> TrackOffsetList;
+
+ /** This is just a container class used for interpreting results
+ of CDDB queries.
+ */
+ class KDE_EXPORT CDDB
+ {
+ public:
+
+ enum Result
+ {
+ Success,
+ ServerError,
+ HostNotFound,
+ NoResponse,
+ NoRecordFound,
+ MultipleRecordFound,
+ CannotSave,
+ InvalidCategory,
+ UnknownError
+ };
+
+ CDDB();
+ virtual ~CDDB();
+
+ static QString resultToString(Result);
+ static QString trackOffsetListToId( const TrackOffsetList & );
+
+ static QString clientName() { return QString::fromLatin1("libkcddb"); }
+ static QString clientVersion() { return QString::fromLatin1("0.31"); }
+
+ protected:
+ bool parseGreeting( const QString & );
+ bool parseHandshake( const QString & );
+
+ uint statusCode( const QString & );
+
+ QString trackOffsetListToId();
+ QString trackOffsetListToString();
+
+ QString user_;
+ QString localHostName_;
+
+ bool readOnly_;
+
+ TrackOffsetList trackOffsetList_;
+ };
+}
+
+#endif // KCDDB_CDDB_H
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/cddbplookup.cpp b/libkcddb/cddbplookup.cpp
new file mode 100644
index 00000000..d2ec1b26
--- /dev/null
+++ b/libkcddb/cddbplookup.cpp
@@ -0,0 +1,113 @@
+/*
+ Copyright ( C ) 2002 Rik Hemsley ( rikkus ) <rik@kde.org>
+ Copyright ( C ) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ Copyright ( C ) 2002 Nadeem Hasan <nhasan@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or ( at your option ) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <kdebug.h>
+
+#include "cddbplookup.h"
+
+namespace KCDDB
+{
+ CDDBPLookup::CDDBPLookup()
+ : Lookup()
+ {
+
+ }
+
+ CDDBPLookup::~CDDBPLookup()
+ {
+ delete socket_;
+ }
+
+ void
+ CDDBPLookup::sendHandshake()
+ {
+ QString handshake = QString( "cddb hello %1 %2 %3 %4" )
+ .arg( user_ )
+ .arg( localHostName_ )
+ .arg( clientName() )
+ .arg( clientVersion() );
+
+ writeLine( handshake );
+ }
+
+ void
+ CDDBPLookup::sendProto()
+ {
+ writeLine( "proto 6" );
+ }
+
+ void
+ CDDBPLookup::sendQuery()
+ {
+ QString query = QString( "cddb query %1 %2" )
+ .arg( trackOffsetListToId() )
+ .arg( trackOffsetListToString() );
+
+ writeLine( query );
+ }
+
+ void
+ CDDBPLookup::sendRead( const CDDBMatch & match )
+ {
+ category_ = match.first;
+ QString discid = match.second;
+
+ QString readRequest = QString( "cddb read %1 %2" )
+ .arg( category_ )
+ .arg( discid );
+
+ writeLine( readRequest );
+ }
+
+ void
+ CDDBPLookup::sendQuit()
+ {
+ writeLine( "quit" );
+ }
+
+ void
+ CDDBPLookup::close()
+ {
+ kdDebug(60010) << "Disconnect from server..." << endl;
+ if ( isConnected() )
+ {
+ socket_->close();
+ }
+ }
+
+ Q_LONG
+ CDDBPLookup::writeLine( const QString & line )
+ {
+ if ( !isConnected() )
+ {
+ kdDebug(60010) << "socket status: " << socket_->state() << endl;
+ return -1;
+ }
+
+ kdDebug(60010) << "WRITE: [" << line << "]" << endl;
+ QCString buf = line.utf8();
+ buf.append( "\n" );
+
+ return socket_->writeBlock( buf.data(), buf.length() );
+ }
+}
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/cddbplookup.h b/libkcddb/cddbplookup.h
new file mode 100644
index 00000000..da263b95
--- /dev/null
+++ b/libkcddb/cddbplookup.h
@@ -0,0 +1,56 @@
+/*
+ Copyright (C) 2002 Rik Hemsley ( rikkus ) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ Copyright (C) 2002 Nadeem Hasan <nhasan@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or ( at your option ) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KCDDB_CDDBP_LOOKUP_H
+#define KCDDB_CDDBP_LOOKUP_H
+
+#include <kbufferedsocket.h>
+
+#include "lookup.h"
+
+namespace KCDDB
+{
+ class CDDBPLookup : public Lookup
+ {
+ public:
+ CDDBPLookup();
+ virtual ~CDDBPLookup();
+
+ void sendHandshake();
+ void sendProto();
+ void sendQuery();
+ void sendRead( const CDDBMatch & );
+ void sendQuit();
+
+ void close();
+ protected:
+ Q_LONG writeLine( const QString & );
+
+ bool isConnected()
+ { return KNetwork::KClientSocketBase::Connected == socket_->state(); }
+
+ KNetwork::KBufferedSocket* socket_;
+ };
+}
+
+#endif
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/cdinfo.cpp b/libkcddb/cdinfo.cpp
new file mode 100644
index 00000000..a08e9b8f
--- /dev/null
+++ b/libkcddb/cdinfo.cpp
@@ -0,0 +1,339 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ Copyright (C) 2002-2004 Nadeem Hasan <nhasan@nadmm.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <kdebug.h>
+#include <kstringhandler.h>
+
+#include "cdinfo.h"
+#include "cddb.h"
+
+namespace KCDDB
+{
+ TrackInfo::TrackInfo()
+ {
+ }
+
+ TrackInfo::TrackInfo(const TrackInfo& clone)
+ : title(clone.title),
+ extt(clone.extt)
+ {
+ }
+
+ TrackInfo::~TrackInfo()
+ {
+ }
+
+ TrackInfo& TrackInfo::operator=(const TrackInfo& clone)
+ {
+ title = clone.title;
+ extt = clone.extt;
+ return *this;
+ }
+
+
+ CDInfo::CDInfo()
+ : year(0),
+ length(0),
+ revision(0)
+ {
+ }
+
+ CDInfo::CDInfo(const CDInfo& clone)
+ : id(clone.id),
+ artist(clone.artist),
+ title(clone.title),
+ genre(clone.genre),
+ category(clone.category),
+ extd(clone.extd),
+ year(clone.year),
+ length(clone.length),
+ revision(clone.revision),
+ trackInfoList(clone.trackInfoList)
+
+ {
+ }
+
+ CDInfo::~CDInfo()
+ {
+ }
+
+ CDInfo& CDInfo::operator=(const CDInfo& clone)
+ {
+ id = clone.id;
+ artist = clone.artist;
+ title = clone.title;
+ genre = clone.genre;
+ category = clone.category;
+ extd = clone.extd;
+ year = clone.year;
+ length = clone.length;
+ revision = clone.revision;
+ trackInfoList = clone.trackInfoList;
+ return *this;
+ }
+
+ QVariant TrackInfo::get(const QString &type) const {
+ if(type == "title")
+ return title;
+ if(type == "extt")
+ return extt;
+ return QVariant();
+ }
+
+ bool
+ CDInfo::load(const QString & s)
+ {
+ return load(QStringList::split('\n', s));
+ }
+
+ bool
+ CDInfo::load(const QStringList & lineList)
+ {
+ clear();
+
+ // We'll append to this until we've seen all the lines, then parse it after.
+ QString dtitle;
+
+ QStringList::ConstIterator it = lineList.begin();
+
+ QRegExp rev("# Revision: (\\d+)");
+
+ while ( it != lineList.end() )
+ {
+ QString line(*it);
+ ++it;
+
+ QStringList tokenList = KStringHandler::perlSplit('=', line, 2);
+
+ if (rev.search(line) != -1)
+ {
+ revision = rev.cap(1).toUInt();;
+ continue;
+ }
+
+ QString key = tokenList[0].stripWhiteSpace();
+ QString value;
+ if (2 != tokenList.count())
+ {
+ if (!key.startsWith("EXT"))
+ continue;
+ }
+ else
+ value = unescape ( tokenList[1].stripWhiteSpace() );
+
+ if ( "DISCID" == key )
+ {
+ id = value;
+ }
+ else if ( "DTITLE" == key )
+ {
+ dtitle += value;
+ }
+ else if ( "DYEAR" == key )
+ {
+ year = value.toUInt();
+ }
+ else if ( "DGENRE" == key )
+ {
+ genre += value;
+ }
+ else if ( "TTITLE" == key.left( 6 ) )
+ {
+ uint trackNumber = key.mid(6).toUInt();
+
+ checkTrack( trackNumber );
+
+ trackInfoList[ trackNumber ].title.append( value );
+ }
+ else if ( "EXTD" == key )
+ {
+ extd.append( value );
+ }
+ else if ( "EXTT" == key.left( 4 ) )
+ {
+ uint trackNumber = key.mid( 4 ).toUInt();
+
+ checkTrack( trackNumber );
+
+ trackInfoList[ trackNumber ].extt.append( value );
+ }
+ }
+
+ int slashPos = dtitle.find('/');
+
+ if (-1 == slashPos)
+ {
+ // Use string for title _and_ artist.
+ artist = title = dtitle;
+ }
+ else
+ {
+ artist = dtitle.left(slashPos).stripWhiteSpace();
+ title = dtitle.mid(slashPos + 1).stripWhiteSpace();
+ }
+
+ if ( genre.isEmpty() )
+ genre = "Unknown";
+
+ kdDebug(60010) << "Loaded CDInfo for " << id << endl;
+
+ return true;
+ }
+
+ QString
+ CDInfo::toString(bool submit) const
+ {
+ QString s;
+
+ if (revision != 0)
+ s += "# Revision: " + QString::number(revision) + "\n";
+
+ if (submit)
+ {
+ s += "#\n";
+ s += QString("# Submitted via: %1 %2\n").arg(CDDB::clientName(),
+ CDDB::clientVersion());
+ }
+
+ s += "DISCID=" + escape( id ) + "\n";
+ s += createLine("DTITLE",escape( artist ) + " / " + escape( title ));
+ s += "DYEAR=" + (0 == year ? QString::null : QString::number(year)) + "\n";
+ s += createLine("DGENRE",escape( genre ));
+
+ for (uint i = 0; i < trackInfoList.count(); ++i)
+ {
+ s += createLine(QString("TTITLE%1").arg(i),
+ escape( trackInfoList[ i ].title));
+ }
+
+ s += createLine("EXTD", escape( extd ));
+
+ for (uint i = 0; i < trackInfoList.count(); ++i)
+ {
+ s += createLine(QString("EXTT%1").arg(i), escape(trackInfoList[i].extt));
+ }
+
+ s +="PLAYORDER=\n";
+
+ return s;
+ }
+
+ // Creates a line in the form NAME=VALUE, and splits it into several
+ // lines if the line gets longer than 256 chars
+ QString
+ CDInfo::createLine(const QString& name, const QString& value) const
+ {
+ Q_ASSERT(name.length() < 254);
+
+ uint maxLength = 256 - name.length() - 2;
+
+ QString tmpValue = value;
+
+ QString lines;
+
+ while (tmpValue.length() > maxLength)
+ {
+ lines += QString("%1=%2\n").arg(name,tmpValue.left(maxLength));
+ tmpValue = tmpValue.mid(maxLength);
+ }
+
+ lines += QString("%1=%2\n").arg(name,tmpValue);
+
+ return lines;
+ }
+
+ void
+ CDInfo::checkTrack( uint trackNumber )
+ {
+ if ( trackInfoList.count() < trackNumber + 1 )
+ {
+ while ( trackInfoList.count() < trackNumber + 1 )
+ trackInfoList.append(TrackInfo());
+ }
+ }
+
+ QString
+ CDInfo::escape( const QString& value )
+ {
+ QString s = value;
+ s.replace( "\\", "\\\\" );
+ s.replace( "\n", "\\n" );
+ s.replace( "\t", "\\t" );
+
+ return s;
+ }
+
+ QString
+ CDInfo::unescape( const QString& value )
+ {
+ QString s = value;
+
+ s.replace( "\\n", "\n" );
+ s.replace( "\\t", "\t" );
+ s.replace( "\\\\", "\\" );
+
+ return s;
+ }
+
+ void
+ CDInfo::clear()
+ {
+ id = artist = title = genre = extd = QString::null;
+ length = year = revision = 0;
+ trackInfoList.clear();
+ }
+
+ bool
+ CDInfo::isValid() const
+ {
+ if (id.isEmpty())
+ return false;
+
+ if (id == "0")
+ return false;
+
+ return true;
+ }
+
+ QVariant CDInfo::get(const QString &type) const {
+ if(type == "id")
+ return id;
+ if(type == "artist")
+ return artist;
+ if(type == "title")
+ return title;
+ if(type == "genre")
+ return genre;
+ if(type == "category")
+ return category;
+ if(type == "extd")
+ return extd;
+ if(type == "year")
+ return year;
+ if(type == "length")
+ return length;
+ if(type == "revision")
+ return revision;
+ return QVariant();
+ }
+}
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/cdinfo.h b/libkcddb/cdinfo.h
new file mode 100644
index 00000000..554e2ff6
--- /dev/null
+++ b/libkcddb/cdinfo.h
@@ -0,0 +1,146 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ Copyright (C) 2002-2004 Nadeem Hasan <nhasan@nadmm.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KCDDB_CDINFO_H
+#define KCDDB_CDINFO_H
+
+#include <qstringlist.h>
+#include <qvaluelist.h>
+#include <kdemacros.h>
+#include <qvariant.h>
+
+namespace KCDDB
+{
+ /**
+ * Information about a sepecific track in a cd.
+ */
+ class KDE_EXPORT TrackInfo
+ {
+ public:
+
+ TrackInfo();
+ ~TrackInfo();
+ TrackInfo(const TrackInfo& clone);
+ TrackInfo& operator=(const TrackInfo& clone);
+
+ /**
+ * Get data for type that has been assigned to this track.
+ * @p type is case insensitive.
+ * For example <code>get("title")</code>
+ */
+ QVariant get(const QString &type) const;
+#ifndef KDE_NO_COMPAT
+ // Use get("title");
+ QString title;
+ // Use get("extt");
+ QString extt;
+#endif
+ };
+
+ typedef QValueList<TrackInfo> TrackInfoList;
+
+ /**
+ * Information about a CD
+ *
+ * Typically CDInfo is obtained from the client such as:
+ * <code>KCDDB::Client *cddb = new KCDDB::Client();
+ * cddb->lookup(discSignature);
+ * CDInfo info = cddb->bestLookupResponse();</code>
+ */
+ class KDE_EXPORT CDInfo
+ {
+ public:
+
+ CDInfo();
+ ~CDInfo();
+ CDInfo(const CDInfo& clone);
+ CDInfo& operator=(const CDInfo& clone);
+
+ /**
+ * Load CDInfo from a string that is CDDB compatible
+ * @return true if successful
+ */
+ bool load(const QString &);
+ /**
+ * Load CDInfo from a stringList that is CDDB compatible
+ * @return true if successful
+ */
+ bool load(const QStringList &);
+
+ /**
+ * Clear all information, setting this to invalid
+ * internal
+ */
+ void clear();
+
+ /**
+ * @return true if the cd information is valid
+ */
+ bool isValid() const;
+ /**
+ * @param submit If submit is true only returns CDDB compatible information
+ * @return a string containing all of the CD's information.
+ */
+ QString toString(bool submit=false) const;
+
+ /**
+ * Get data for type that has been assigned to this disc.
+ * @p type is case insensitive.
+ * For example <code>get("title")</code>
+ */
+ QVariant get(const QString &type) const;
+ // Use get(...)
+ QString id;
+ QString artist;
+ QString title;
+ QString genre;
+ QString category;
+ QString extd;
+ uint year;
+ uint length; // in milliseconds
+ uint revision;
+
+ TrackInfoList trackInfoList;
+
+ protected:
+ /**
+ * @returns a valid CDDB line made up of name and value
+ */
+ QString createLine(const QString& name, const QString& value) const;
+ /**
+ * Checks to make sure that trackNumber exists
+ */
+ void checkTrack( uint );
+ /**
+ * escape's string for CDDB processing
+ */
+ static QString escape( const QString & );
+ /**
+ * fixes an escaped string that has been CDDB processed
+ */
+ static QString unescape( const QString & );
+ };
+
+ typedef QValueList<CDInfo> CDInfoList;
+}
+
+#endif // KCDDB_CDINFO_H
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/cdinfodialogbase.ui b/libkcddb/cdinfodialogbase.ui
new file mode 100644
index 00000000..76ee8141
--- /dev/null
+++ b/libkcddb/cdinfodialogbase.ui
@@ -0,0 +1,429 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>CDInfoDialogBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>CDInfoDialogBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>501</width>
+ <height>600</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="5" column="2">
+ <property name="name">
+ <cstring>lb_revision</cstring>
+ </property>
+ <property name="text">
+ <string>Revision:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>m_title</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Use the name of the artist if there is no title.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>lb_category</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Category:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_category</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="1">
+ <property name="name">
+ <cstring>m_id</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="0" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>m_artist</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Write names as "first last", not "last, first". Omit any leading "The". Use "Various" for compilations.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>lb_artist</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Artist:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_artist</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>lb_year</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Year:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_year</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="2">
+ <property name="name">
+ <cstring>lb_genre</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Genre:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_genre</cstring>
+ </property>
+ </widget>
+ <widget class="KComboBox" row="4" column="1">
+ <property name="name">
+ <cstring>m_category</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Disc Id values must be unique within a category.</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="3" column="1">
+ <property name="name">
+ <cstring>m_year</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2100</number>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="6" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>m_multiple</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Multiple artists</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="2" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>m_comment</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>lb_comment</cstring>
+ </property>
+ <property name="text">
+ <string>Comment:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>le_discInfo</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>lb_title</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Title:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_title</cstring>
+ </property>
+ </widget>
+ <widget class="KComboBox" row="4" column="3">
+ <property name="name">
+ <cstring>m_genre</cstring>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ <property name="duplicatesEnabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Avoid custom values, as they will be written to CDDB as-is.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>lb_id</cstring>
+ </property>
+ <property name="text">
+ <string>Disc Id:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="2">
+ <property name="name">
+ <cstring>lb_length</cstring>
+ </property>
+ <property name="text">
+ <string>Length:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="3">
+ <property name="name">
+ <cstring>m_revision</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="3">
+ <property name="name">
+ <cstring>m_length</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Track</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Length</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Title</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Comment</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Artist</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>m_trackList</cstring>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="defaultRenameAction">
+ <enum>Accept</enum>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>For a CD-Extra, set title to "Data".</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>231</width>
+ <height>41</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_changeEncoding</cstring>
+ </property>
+ <property name="text">
+ <string>Change Encoding...</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lb_playingOrder</cstring>
+ </property>
+ <property name="text">
+ <string>Playing order:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>m_playOrder</cstring>
+ </property>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>m_trackList</sender>
+ <signal>selectionChanged(QListViewItem*)</signal>
+ <receiver>CDInfoDialogBase</receiver>
+ <slot>slotTrackSelected(QListViewItem*)</slot>
+ </connection>
+ <connection>
+ <sender>m_trackList</sender>
+ <signal>doubleClicked(QListViewItem*,const QPoint&amp;,int)</signal>
+ <receiver>CDInfoDialogBase</receiver>
+ <slot>slotTrackDoubleClicked(QListViewItem*,const QPoint&amp;,int)</slot>
+ </connection>
+ <connection>
+ <sender>m_artist</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>CDInfoDialogBase</receiver>
+ <slot>artistChanged(const QString&amp;)</slot>
+ </connection>
+ <connection>
+ <sender>m_genre</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>CDInfoDialogBase</receiver>
+ <slot>genreChanged(const QString&amp;)</slot>
+ </connection>
+ <connection>
+ <sender>m_multiple</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>CDInfoDialogBase</receiver>
+ <slot>slotMultipleArtists(bool)</slot>
+ </connection>
+ <connection>
+ <sender>m_changeEncoding</sender>
+ <signal>clicked()</signal>
+ <receiver>CDInfoDialogBase</receiver>
+ <slot>slotChangeEncoding()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>m_artist</tabstop>
+ <tabstop>m_title</tabstop>
+ <tabstop>m_comment</tabstop>
+ <tabstop>m_year</tabstop>
+ <tabstop>m_category</tabstop>
+ <tabstop>m_genre</tabstop>
+ <tabstop>m_trackList</tabstop>
+ <tabstop>m_playOrder</tabstop>
+</tabstops>
+<includes>
+ <include location="local" impldecl="in implementation">kdialog.h</include>
+ <include location="local" impldecl="in implementation">qdatetime.h</include>
+ <include location="local" impldecl="in declaration">libkcddb/cdinfo.h</include>
+ <include location="local" impldecl="in declaration">libkcddb/cddb.h</include>
+ <include location="local" impldecl="in declaration">libkcddb/categories.h</include>
+ <include location="local" impldecl="in declaration">libkcddb/genres.h</include>
+ <include location="local" impldecl="in implementation">cdinfodialogbase.ui.h</include>
+</includes>
+<forwards>
+ <forward>class KCDDBDlgBasePrivate</forward>
+</forwards>
+<variables>
+ <variable access="private">KCDDB::Genres m_genres;</variable>
+ <variable access="private">KCDDB::Categories m_categories;</variable>
+ <variable access="private">static const char *SEPARATOR;</variable>
+ <variable access="public">static const unsigned TRACK_ARTIST = 4;</variable>
+ <variable access="private">KCDDBDlgBasePrivate *d;</variable>
+ <variable access="public">static const unsigned TRACK_TIME = 1;</variable>
+ <variable access="public">static const unsigned TRACK_NUMBER = 0;</variable>
+ <variable access="public">static const unsigned TRACK_TITLE = 2;</variable>
+ <variable access="public">static const unsigned TRACK_COMMENT = 3;</variable>
+</variables>
+<signals>
+ <signal>play(int i)</signal>
+ <signal>discInfoClicked()</signal>
+ <signal>trackInfoClicked(unsigned)</signal>
+</signals>
+<slots>
+ <slot access="protected">slotTrackSelected( QListViewItem * item )</slot>
+ <slot access="protected">slotNextTrack()</slot>
+ <slot access="protected">slotTrackDoubleClicked( QListViewItem * item, const QPoint &amp;, int column )</slot>
+ <slot>setInfo( const KCDDB::CDInfo &amp; info, KCDDB::TrackOffsetList &amp; trackStartFrames )</slot>
+ <slot>artistChanged( const QString &amp; newArtist )</slot>
+ <slot>genreChanged( const QString &amp; newGenre )</slot>
+ <slot>slotMultipleArtists( bool hasMultipleArtist )</slot>
+ <slot access="private">slotChangeEncoding()</slot>
+</slots>
+<functions>
+ <function access="private" specifier="non virtual">init()</function>
+ <function access="private" specifier="non virtual">destroy()</function>
+ <function returnType="QString">framesTime( unsigned frames )</function>
+ <function returnType="KCDDB::CDInfo">info() const</function>
+</functions>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>klistview.h</includehint>
+</includehints>
+</UI>
diff --git a/libkcddb/cdinfodialogbase.ui.h b/libkcddb/cdinfodialogbase.ui.h
new file mode 100644
index 00000000..1291010a
--- /dev/null
+++ b/libkcddb/cdinfodialogbase.ui.h
@@ -0,0 +1,248 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+
+#include <qtextcodec.h>
+#include <kdebug.h>
+#include <kdialogbase.h>
+#include <kglobal.h>
+#include <kcharsets.h>
+
+#include "cdinfoencodingwidget.h"
+
+const char *CDInfoDialogBase::SEPARATOR = " / ";
+
+void CDInfoDialogBase::init()
+{
+ m_categories = KCDDB::Categories();
+ m_category->insertStringList(m_categories.i18nList());
+ m_genres = KCDDB::Genres();
+ m_genre->insertStringList(m_genres.i18nList());
+
+ // We want control over the visibility of this column. See artistChanged().
+ m_trackList->setColumnWidthMode(TRACK_ARTIST, QListView::Manual);
+
+ // Make the user-definable values in-place editable.
+ m_trackList->setRenameable(TRACK_NUMBER, false);
+ m_trackList->setRenameable(TRACK_TIME, false);
+ m_trackList->setRenameable(TRACK_TITLE, true);
+ m_trackList->setRenameable(TRACK_COMMENT, true);
+ m_trackList->setRenameable(TRACK_ARTIST, true);
+}
+
+void CDInfoDialogBase::destroy()
+{
+}
+
+void CDInfoDialogBase::slotTrackSelected( QListViewItem *item )
+{
+ emit play(item->text(0).toUInt()-1);
+}
+
+void CDInfoDialogBase::slotNextTrack()
+{
+ if (m_trackList->currentItem())
+ {
+ QListViewItem *item = m_trackList->currentItem()->nextSibling();
+ m_trackList->setSelected(item, true);
+ m_trackList->ensureItemVisible(item);
+ }
+}
+
+void CDInfoDialogBase::slotTrackDoubleClicked( QListViewItem *item, const QPoint &, int column)
+{
+ m_trackList->rename(item, column);
+}
+
+void CDInfoDialogBase::setInfo( const KCDDB::CDInfo &info, KCDDB::TrackOffsetList &trackStartFrames )
+{
+ m_artist->setText(info.artist.stripWhiteSpace());
+ m_title->setText(info.title.stripWhiteSpace());
+ m_category->setCurrentText(m_categories.cddb2i18n(info.category));
+
+ // Make sure the revision is set before the genre to allow the genreChanged() handler to fire.
+ m_revision->setText(QString::number(info.revision));
+ m_genre->setCurrentText(m_genres.cddb2i18n(info.genre));
+ m_year->setValue(info.year);
+ m_comment->setText(info.extd.stripWhiteSpace());
+ m_id->setText(info.id.stripWhiteSpace());
+
+ // Now do the individual tracks.
+ unsigned tracks = info.trackInfoList.count();
+ m_length->setText(framesTime(trackStartFrames[tracks + 1] - trackStartFrames[0]));
+ m_trackList->clear();
+ for (unsigned i = 0; i < tracks; i++)
+ {
+ QListViewItem *item = new QListViewItem(m_trackList, 0);
+
+ item->setText(TRACK_NUMBER, QString().sprintf("%02d", i + 1));
+ item->setText(TRACK_TIME, framesTime(trackStartFrames[i + ((i + 1 < tracks) ? 1 : 2)] - trackStartFrames[i]));
+ QString title = info.trackInfoList[i].title;
+ int separator = title.find(SEPARATOR);
+ if (info.artist != "Various" || separator == -1 || !m_multiple->isChecked() )
+ {
+ item->setText(TRACK_ARTIST, "");
+ item->setText(TRACK_TITLE, title);
+ }
+ else
+ {
+ // We seem to have a compilation.
+ item->setText(TRACK_ARTIST, title.left(separator));
+ item->setText(TRACK_TITLE, title.mid(separator + 3));
+ }
+ item->setText(TRACK_COMMENT, info.trackInfoList[i].extt);
+ }
+ // FIXME KDE4: handle playorder here too, once KCDDBInfo::CDInfo is updated.
+
+ if (info.artist == "Various" || m_multiple->isChecked()){
+ m_trackList->adjustColumn(TRACK_ARTIST);
+ }
+}
+
+QString CDInfoDialogBase::framesTime(unsigned frames)
+{
+ QTime time;
+ double ms;
+
+ ms = frames * 1000 / 75.0;
+ time = time.addMSecs((int)ms);
+
+ // Use ".zzz" for milliseconds...
+ QString temp2;
+ if (time.hour() > 0)
+ temp2 = time.toString("hh:mm:ss");
+ else
+ temp2 = time.toString("mm:ss");
+ return temp2;
+} // framesTime
+
+KCDDB::CDInfo CDInfoDialogBase::info() const
+{
+ KCDDB::CDInfo info;
+ KCDDB::TrackInfo track;
+
+ info.artist = m_artist->text().stripWhiteSpace();
+ info.title = m_title->text().stripWhiteSpace();
+ info.category = m_categories.i18n2cddb(m_category->currentText());
+ info.genre = m_genres.i18n2cddb(m_genre->currentText());
+ info.year = m_year->value();
+ info.extd = m_comment->text().stripWhiteSpace();
+ info.revision = m_revision->text().stripWhiteSpace().toUInt();
+ info.id = m_id->text().stripWhiteSpace();
+ for (QListViewItem *item = m_trackList->firstChild(); item; item=item->nextSibling())
+ {
+ // Combine the track artist if present with the title.
+ QString trackArtist = item->text(TRACK_ARTIST).stripWhiteSpace();
+ track.title = trackArtist;
+ if (!trackArtist.isEmpty())
+ {
+ track.title.append(SEPARATOR);
+ }
+ track.title.append(item->text(TRACK_TITLE).stripWhiteSpace());
+ track.extt = item->text(TRACK_COMMENT).stripWhiteSpace();
+ info.trackInfoList.append(track);
+ // FIXME KDE4: handle track lengths here too, once KCDDBInfo::CDInfo is updated.
+ }
+ // FIXME KDE4: handle playorder here too, once KCDDBInfo::CDInfo is updated.
+ return info;
+}
+
+
+void CDInfoDialogBase::artistChanged( const QString &newArtist )
+{
+ // Enable special handling of compilations.
+ if (newArtist.stripWhiteSpace().compare("Various")) {
+ m_multiple->setChecked(false);
+ } else {
+ m_multiple->setChecked(true);
+ }
+}
+
+void CDInfoDialogBase::genreChanged( const QString &newGenre )
+{
+ // Disable changes to category if the version number indicates that a record
+ // is already in the database, or if the genre is poorly set. The latter
+ // condition also provides a "back-door" override.
+ m_category->setEnabled((m_revision->text().stripWhiteSpace().toUInt() < 1) ||
+ (newGenre.compare("Unknown") == 0));
+}
+
+
+void CDInfoDialogBase::slotMultipleArtists( bool hasMultipleArtist)
+{
+ if(hasMultipleArtist){
+ for (QListViewItem *item = m_trackList->firstChild(); item; item=item->nextSibling())
+ {
+ QString title = item->text(TRACK_TITLE);
+ int separator = title.find(SEPARATOR);
+ if (separator != -1)
+ {
+ // Artists probably entered already
+ item->setText(TRACK_ARTIST, title.left(separator));
+ item->setText(TRACK_TITLE, title.mid(separator + 3));
+ }
+ }
+ m_trackList->adjustColumn(TRACK_ARTIST);
+ m_trackList->adjustColumn(TRACK_TITLE);
+ }
+ else{
+ for (QListViewItem *item = m_trackList->firstChild(); item; item=item->nextSibling())
+ {
+ QString artist = item->text(TRACK_ARTIST);
+ if (!artist.isEmpty())
+ {
+ item->setText(TRACK_ARTIST, QString::null);
+ item->setText(TRACK_TITLE, artist + SEPARATOR + item->text(TRACK_TITLE));
+ }
+ }
+ m_trackList->hideColumn(TRACK_ARTIST);
+ m_trackList->adjustColumn(TRACK_TITLE);
+ }
+}
+
+
+void CDInfoDialogBase::slotChangeEncoding()
+{
+ kdDebug() << k_funcinfo << endl;
+
+ KDialogBase* dialog = new KDialogBase(this, 0, true, i18n("Change Encoding"),
+ KDialogBase::Ok | KDialogBase::Cancel);
+
+ QStringList songTitles;
+ for (QListViewItem *item = m_trackList->firstChild(); item; item=item->nextSibling())
+ {
+ QString title = item->text(TRACK_ARTIST).stripWhiteSpace();
+ if (!title.isEmpty())
+ title.append(SEPARATOR);
+ title.append(item->text(TRACK_TITLE).stripWhiteSpace());
+ songTitles << title;
+ }
+
+ KCDDB::CDInfoEncodingWidget* encWidget = new KCDDB::CDInfoEncodingWidget(
+ dialog, m_artist->text(),m_title->text(), songTitles);
+
+ dialog->setMainWidget(encWidget);
+
+ if (dialog->exec())
+ {
+ KCharsets* charsets = KGlobal::charsets();
+ QTextCodec* codec = charsets->codecForName(charsets->encodingForName(encWidget->selectedEncoding()));
+
+ m_artist->setText(codec->toUnicode(m_artist->text().latin1()));
+ m_title->setText(codec->toUnicode(m_title->text().latin1()));
+ m_genre->setCurrentText(codec->toUnicode(m_genre->currentText().latin1()));
+ m_comment->setText(codec->toUnicode(m_comment->text().latin1()));
+
+ for (QListViewItem *item = m_trackList->firstChild(); item; item=item->nextSibling())
+ {
+ item->setText(TRACK_ARTIST,codec->toUnicode(item->text(TRACK_ARTIST).latin1()));
+ item->setText(TRACK_TITLE,codec->toUnicode(item->text(TRACK_TITLE).latin1()));
+ item->setText(TRACK_COMMENT,codec->toUnicode(item->text(TRACK_COMMENT).latin1()));
+ }
+ }
+}
diff --git a/libkcddb/cdinfoencodingwidget.cpp b/libkcddb/cdinfoencodingwidget.cpp
new file mode 100644
index 00000000..eb59be32
--- /dev/null
+++ b/libkcddb/cdinfoencodingwidget.cpp
@@ -0,0 +1,70 @@
+/*
+ Copyright (C) 2005 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <qcombobox.h>
+#include <qlistbox.h>
+#include <qlabel.h>
+#include <qtextcodec.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kcharsets.h>
+#include "cdinfoencodingwidget.h"
+
+namespace KCDDB
+{
+ CDInfoEncodingWidget::CDInfoEncodingWidget(QWidget* parent, const QString& artist,
+ const QString& title, const QStringList& songTitles)
+ : CDInfoEncodingWidgetBase(parent), m_artist(artist), m_title(title),
+ m_songTitles(songTitles)
+ {
+ encodingCombo->insertStringList(KGlobal::charsets()->descriptiveEncodingNames());
+
+ slotEncodingChanged(encodingCombo->currentText());
+
+ connect(encodingCombo,SIGNAL(activated(const QString&)),
+ this,SLOT(slotEncodingChanged(const QString&)));
+ }
+
+ QString CDInfoEncodingWidget::selectedEncoding()
+ {
+ return encodingCombo->currentText();
+ }
+
+ void CDInfoEncodingWidget::slotEncodingChanged(const QString& encoding)
+ {
+ KCharsets* charsets = KGlobal::charsets();
+
+ QTextCodec* codec = charsets->codecForName(charsets->encodingForName(encoding));
+
+ songsBox->clear();
+ QStringList newTitles;
+
+ for (QStringList::const_iterator it = m_songTitles.begin();
+ it != m_songTitles.end(); ++it)
+ newTitles << codec->toUnicode((*it).latin1());
+
+ songsBox->clear();
+ songsBox->insertStringList(newTitles);
+
+ titleLabel->setText(i18n("artist - cdtitle", "%1 - %2").arg(
+ codec->toUnicode(m_artist.latin1()), codec->toUnicode(m_title.latin1())));
+ }
+}
+
+#include "cdinfoencodingwidget.moc"
diff --git a/libkcddb/cdinfoencodingwidget.h b/libkcddb/cdinfoencodingwidget.h
new file mode 100644
index 00000000..87f9b7a3
--- /dev/null
+++ b/libkcddb/cdinfoencodingwidget.h
@@ -0,0 +1,45 @@
+/*
+ Copyright (C) 2005 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KCDDB_CDINFOENCODINGWIDGET_H
+#define KCDDB_CDINFOENCODINGWIDGET_H
+
+#include "cdinfoencodingwidgetbase.h"
+
+namespace KCDDB
+{
+ class CDInfoEncodingWidget : public CDInfoEncodingWidgetBase
+ {
+ Q_OBJECT
+ public:
+ CDInfoEncodingWidget(QWidget* parent, const QString& artist, const QString& title,
+ const QStringList& songTitles);
+
+ QString selectedEncoding();
+
+ private slots:
+ void slotEncodingChanged(const QString &);
+
+ private:
+ QString m_artist, m_title;
+ QStringList m_songTitles;
+ } ;
+}
+
+#endif // KCDDB_CDINFOENCODINGWIDGET_H
diff --git a/libkcddb/cdinfoencodingwidgetbase.ui b/libkcddb/cdinfoencodingwidgetbase.ui
new file mode 100644
index 00000000..e5156908
--- /dev/null
+++ b/libkcddb/cdinfoencodingwidgetbase.ui
@@ -0,0 +1,70 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>CDInfoEncodingWidgetBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>CDInfoEncodingWidgetBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>344</width>
+ <height>369</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>encodingLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Encoding:</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="0" column="1">
+ <property name="name">
+ <cstring>encodingCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Preview</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>titleLabel</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>LineEditPanel</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QListBox">
+ <property name="name">
+ <cstring>songsBox</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>300</width>
+ <height>250</height>
+ </size>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/libkcddb/client.cpp b/libkcddb/client.cpp
new file mode 100644
index 00000000..07e5e089
--- /dev/null
+++ b/libkcddb/client.cpp
@@ -0,0 +1,315 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ Copyright (C) 2003 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "client.h"
+#include "synccddbplookup.h"
+#include "asynccddbplookup.h"
+#include "synchttplookup.h"
+#include "asynchttplookup.h"
+#include "syncsmtpsubmit.h"
+#include "asyncsmtpsubmit.h"
+#include "synchttpsubmit.h"
+#include "asynchttpsubmit.h"
+#include "cache.h"
+#include "lookup.h"
+
+#include <kdebug.h>
+
+namespace KCDDB
+{
+ class Client::Private
+ {
+ public:
+
+ Private()
+ : block( true )
+ {}
+
+ Config config;
+ CDInfoList cdInfoList;
+ bool block;
+ };
+
+ Client::Client()
+ : QObject(),
+ cdInfoLookup(0),
+ cdInfoSubmit(0)
+ {
+ d = new Private;
+ d->config.readConfig();
+ }
+
+ Client::~Client()
+ {
+ delete d;
+ delete cdInfoLookup;
+ delete cdInfoSubmit;
+ }
+
+ Config &
+ Client::config() const
+ {
+ return d->config;
+ }
+
+ void
+ Client::setBlockingMode( bool enable )
+ {
+ d->block = enable;
+ }
+
+ bool
+ Client::blockingMode() const
+ {
+ return d->block;
+ }
+
+ CDInfoList
+ Client::lookupResponse() const
+ {
+ return d->cdInfoList;
+ }
+
+ CDInfo
+ Client::bestLookupResponse() const
+ {
+ CDInfo info;
+
+ uint maxrev = 0;
+
+ for ( CDInfoList::Iterator it = d->cdInfoList.begin();
+ it != d->cdInfoList.end(); ++it )
+ {
+ if ( ( *it ).revision >= maxrev )
+ {
+ info = *it;
+ maxrev = info.revision;
+ }
+ }
+
+ return info;
+ }
+
+ CDDB::Result
+ Client::lookup(const TrackOffsetList & trackOffsetList)
+ {
+ d->cdInfoList.clear();
+
+ QString cddbId = Lookup::trackOffsetListToId( trackOffsetList );
+
+ if ( cddbId.isNull() )
+ {
+ kdDebug(60010) << "Can't create cddbid from offset list" << endl;
+ return Lookup::NoRecordFound;
+ }
+
+ if ( Cache::Ignore != d->config.cachePolicy() )
+ {
+ d->cdInfoList = Cache::lookup( cddbId );
+
+ kdDebug(60010) << "Found " << d->cdInfoList.count() << " hit(s)" << endl;
+
+ if ( !d->cdInfoList.isEmpty() )
+ {
+ if ( !blockingMode() )
+ emit finished( Lookup::Success );
+
+ return CDDB::Success;
+ }
+ }
+
+ if ( Cache::Only == d->config.cachePolicy() )
+ {
+ kdDebug(60010) << "Only trying cache. Give up now." << endl;
+ if ( !blockingMode() )
+ emit finished( Lookup::NoRecordFound );
+ return CDDB::NoRecordFound;
+ }
+
+ CDDB::Result r;
+ Lookup::Transport t = ( Lookup::Transport )d->config.lookupTransport();
+
+ // just in case we have an info lookup hanging around, prevent mem leakage
+ delete cdInfoLookup;
+
+ if ( blockingMode() )
+ {
+
+ if( Lookup::CDDBP == t )
+ cdInfoLookup = new SyncCDDBPLookup();
+ else
+ cdInfoLookup = new SyncHTTPLookup();
+
+ r = cdInfoLookup->lookup( d->config.hostname(),
+ d->config.port(), trackOffsetList );
+
+ if ( CDDB::Success == r )
+ {
+ d->cdInfoList = cdInfoLookup->lookupResponse();
+ Cache::store( d->cdInfoList );
+ }
+
+ delete cdInfoLookup;
+ cdInfoLookup = 0L;
+ }
+ else
+ {
+ if( Lookup::CDDBP == t )
+ {
+ cdInfoLookup = new AsyncCDDBPLookup();
+
+ connect( static_cast<AsyncCDDBPLookup *>( cdInfoLookup ),
+ SIGNAL( finished( CDDB::Result ) ),
+ SLOT( slotFinished( CDDB::Result ) ) );
+ }
+ else
+ {
+ cdInfoLookup = new AsyncHTTPLookup();
+
+ connect( static_cast<AsyncHTTPLookup *>( cdInfoLookup ),
+ SIGNAL( finished( CDDB::Result ) ),
+ SLOT( slotFinished( CDDB::Result ) ) );
+ }
+
+ r = cdInfoLookup->lookup( d->config.hostname(),
+ d->config.port(), trackOffsetList );
+
+ if ( Lookup::Success != r )
+ {
+ delete cdInfoLookup;
+ cdInfoLookup = 0L;
+ }
+ }
+
+ return r;
+ }
+
+ void
+ Client::slotFinished( CDDB::Result r )
+ {
+ if ( cdInfoLookup && CDDB::Success == r )
+ {
+ d->cdInfoList = cdInfoLookup->lookupResponse();
+ Cache::store( d->cdInfoList );
+ }
+ else
+ d->cdInfoList.clear();
+
+ emit finished( r );
+
+ if ( cdInfoLookup ) // in case someone called lookup() while finished() was being processed, and deleted cdInfoLookup.
+ {
+ cdInfoLookup->deleteLater();
+ cdInfoLookup = 0L;
+ }
+ }
+
+ void
+ Client::slotSubmitFinished( CDDB::Result r )
+ {
+ emit finished( r );
+
+ cdInfoSubmit->deleteLater();
+ cdInfoSubmit=0L;
+ }
+
+ CDDB::Result
+ Client::submit(const CDInfo &cdInfo, const TrackOffsetList& offsetList)
+ {
+ // Check if it's valid
+
+ if (!cdInfo.isValid())
+ return CDDB::CannotSave;
+
+ uint last=0;
+ for (uint i=0; i < (offsetList.count()-2); i++)
+ {
+ if(last >= offsetList[i])
+ return CDDB::CannotSave;
+ last = offsetList[i];
+ }
+
+ //TODO Check that it is edited
+
+ // just in case we have a cdInfoSubmit, prevent memory leakage
+ delete cdInfoSubmit;
+
+ QString from = d->config.emailAddress();
+
+ switch (d->config.submitTransport())
+ {
+ case Submit::HTTP:
+ {
+ QString hostname = d->config.httpSubmitServer();
+ uint port = d->config.httpSubmitPort();
+
+ if ( blockingMode() )
+ cdInfoSubmit = new SyncHTTPSubmit(from, hostname, port);
+ else
+ {
+ cdInfoSubmit = new AsyncHTTPSubmit(from, hostname, port);
+ connect( static_cast<AsyncHTTPSubmit *>( cdInfoSubmit ),
+ SIGNAL(finished( CDDB::Result ) ),
+ SLOT( slotSubmitFinished( CDDB::Result ) ) );
+ }
+
+ break;
+ }
+ case Submit::SMTP:
+ {
+ QString hostname = d->config.smtpHostname();
+ uint port = d->config.smtpPort();
+ QString username = d->config.smtpUsername();
+
+ if ( blockingMode() )
+ cdInfoSubmit = new SyncSMTPSubmit( hostname, port, username, from, d->config.submitAddress() );
+ else
+ {
+ cdInfoSubmit = new AsyncSMTPSubmit( hostname, port, username, from, d->config.submitAddress() );
+ connect( static_cast<AsyncSMTPSubmit *>( cdInfoSubmit ),
+ SIGNAL( finished( CDDB::Result ) ),
+ SLOT( slotSubmitFinished( CDDB::Result ) ) );
+ }
+ break;
+ }
+ default:
+ kdDebug(60010) << k_funcinfo << "Unsupported transport: " << endl;
+// << CDDB::transportToString(d->config.submitTransport()) << endl;
+ return CDDB::UnknownError;
+ break;
+ }
+
+ CDDB::Result r = cdInfoSubmit->submit( cdInfo, offsetList );
+
+ if ( blockingMode() )
+ {
+ delete cdInfoSubmit;
+ cdInfoSubmit = 0L;
+ }
+
+ return r;
+ }
+}
+
+#include "client.moc"
+
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/client.h b/libkcddb/client.h
new file mode 100644
index 00000000..08188bb9
--- /dev/null
+++ b/libkcddb/client.h
@@ -0,0 +1,104 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KCDDB_CLIENT_H
+#define KCDDB_CLIENT_H
+
+#include "config.h"
+#include "cdinfo.h"
+#include <qobject.h>
+#include "cddb.h"
+#include <kdemacros.h>
+
+namespace KCDDB
+{
+ class Lookup;
+ class Submit;
+
+ /**
+ * Class used to obtain CDDB information about a CD
+ *
+ * Example:
+ * <code>KCDDB::Client *cddb = new KCDDB::Client();
+ * cddb->lookup(discSignature);
+ * CDInfo info = cddb->bestLookupResponse();</code>
+ */
+ class KDE_EXPORT Client : public QObject
+ {
+ Q_OBJECT
+
+ public:
+
+ /**
+ * Uses settings read from config.
+ */
+ Client();
+
+ virtual ~Client();
+
+ Config & config() const;
+
+ /**
+ * @return a list of CDDB entries that match the disc signature
+ */
+ CDInfoList lookupResponse() const;
+ /**
+ * @return a the CDDB entries that is the best match to the disc signature
+ */
+ CDInfo bestLookupResponse() const;
+
+ /**
+ * @return if the results of the lookup: Success, NoRecordFound, etc
+ */
+ CDDB::Result lookup(const TrackOffsetList &trackOffsetList);
+ /**
+ * @returns the results of trying to submit
+ */
+ CDDB::Result submit(const CDInfo &cdInfo, const TrackOffsetList &trackOffsetList);
+
+ void setBlockingMode( bool );
+ bool blockingMode() const;
+
+ signals:
+ /**
+ * emited when not blocking and lookup() finished.
+ */
+ void finished( CDDB::Result result );
+
+ protected slots:
+ /**
+ * Called when the lookup is finished with the result
+ */
+ void slotFinished( CDDB::Result result );
+ /**
+ * Called when the submit is finished with the result
+ */
+ void slotSubmitFinished( CDDB::Result result );
+
+ private:
+ class Private;
+ Private * d;
+ Lookup * cdInfoLookup;
+ Submit * cdInfoSubmit;
+ };
+}
+
+#endif // KCDDB_CLIENT_H
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/config.cpp b/libkcddb/config.cpp
new file mode 100644
index 00000000..54e61a2a
--- /dev/null
+++ b/libkcddb/config.cpp
@@ -0,0 +1,56 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ Copyright (C) 2002 Nadeem Hasan <nhasan@kde.org>
+ Copyright (C) 2003 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <kemailsettings.h>
+
+#include <libkcddb/config.h>
+
+namespace KCDDB
+{
+ Config::Config()
+ : ConfigBase()
+ {
+ loadEmailSettings();
+ }
+
+ void Config::loadEmailSettings()
+ {
+ KEMailSettings kes;
+ kes.setProfile( kes.defaultProfileName() );
+
+ static_cast<KConfigSkeleton::ItemString *>(findItem("emailAddress"))
+ ->setDefaultValue(kes.getSetting( KEMailSettings::EmailAddress ));
+ static_cast<KConfigSkeleton::ItemString *>(findItem("replyTo"))
+ ->setDefaultValue(kes.getSetting( KEMailSettings::ReplyToAddress ));
+ static_cast<KConfigSkeleton::ItemString *>(findItem("smtpHostname"))
+ ->setDefaultValue(kes.getSetting( KEMailSettings::OutServer ));
+ }
+
+ void Config::reparse()
+ {
+ loadEmailSettings();
+
+ readConfig();
+ }
+}
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/config.h b/libkcddb/config.h
new file mode 100644
index 00000000..ac16da6f
--- /dev/null
+++ b/libkcddb/config.h
@@ -0,0 +1,45 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ Copyright (C) 2002 Nadeem Hasan <nhasan@kde.org>
+ Copyright (C) 2003 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KCDDB_CONFIG_H
+#define KCDDB_CONFIG_H
+
+#include "cache.h"
+#include "cddb.h"
+#include "configbase.h"
+#include <qstring.h>
+#include <kdelibs_export.h>
+namespace KCDDB
+{
+ class KDE_EXPORT Config : public ConfigBase
+ {
+ public:
+ Config();
+
+ void reparse();
+ private:
+ void loadEmailSettings();
+ };
+}
+
+#endif // KCDDB_CONFIG_H
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/configbase.kcfgc b/libkcddb/configbase.kcfgc
new file mode 100644
index 00000000..fe089fc8
--- /dev/null
+++ b/libkcddb/configbase.kcfgc
@@ -0,0 +1,4 @@
+ClassName=ConfigBase
+File=libkcddb.kcfg
+Mutators=true
+Visibility=KDE_EXPORT
diff --git a/libkcddb/genres.cpp b/libkcddb/genres.cpp
new file mode 100644
index 00000000..1eeee324
--- /dev/null
+++ b/libkcddb/genres.cpp
@@ -0,0 +1,123 @@
+// Copyright (C) 2005 by Shaheed Haque (srhaque@iee.org). All rights reserved.
+//
+// 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 <genres.h>
+#include <klocale.h>
+
+KCDDB::Genres::Genres()
+{
+ // The Genre is completely arbitrary. But we follow kaudiocreator's cue
+ // and make life easy for people.
+ //
+ // To cope with preexisting records which don't match an entry, we will
+ // add one later if needed.
+ m_cddb << "Unknown" << "A Cappella" << "Acid Jazz" <<
+ "Acid Punk" << "Acid" << "Acoustic" << "Alternative" <<
+ "Alt. Rock" << "Ambient" << "Anime" << "Avantgarde" <<
+ "Ballad" << "Bass" << "Beat" << "Bebop" <<
+ "Big Band" << "Black Metal" << "Bluegrass" << "Blues" <<
+ "Booty Bass" << "BritPop" << "Cabaret" << "Celtic" <<
+ "Chamber Music" << "Chanson" << "Chorus" << "Christian Gangsta Rap" <<
+ "Christian Rap" << "Christian Rock" << "Classical" << "Classic Rock" <<
+ "Club-house" << "Club" << "Comedy" << "Contemporary Christian" <<
+ "Country" << "Crossover" << "Cult" << "Dance Hall" <<
+ "Dance" << "Darkwave" << "Death Metal" << "Disco" <<
+ "Dream" << "Drum & Bass" << "Drum Solo" << "Duet" <<
+ "Easy Listening" << "Electronic" << "Ethnic" << "Eurodance" <<
+ "Euro-House" << "Euro-Techno" << "Fast-Fusion" << "Folklore" <<
+ "Folk/Rock" << "Folk" << "Freestyle" << "Funk" <<
+ "Fusion" << "Game" << "Gangsta Rap" << "Goa" <<
+ "Gospel" << "Gothic Rock" << "Gothic" << "Grunge" <<
+ "Hardcore" << "Hard Rock" << "Heavy Metal" << "Hip-Hop" <<
+ "House" << "Humor" << "Indie" << "Industrial" <<
+ "Instrumental Pop" << "Instrumental Rock" << "Instrumental" << "Jazz+Funk" <<
+ "Jazz" << "JPop" << "Jungle" << "Latin" << "Lo-Fi" <<
+ "Meditative" << "Merengue" << "Metal" << "Musical" <<
+ "National Folk" << "Native American" << "Negerpunk" << "New Age" <<
+ "New Wave" << "Noise" << "Oldies" << "Opera" <<
+ "Other" << "Polka" << "Polsk Punk" << "Pop-Funk" <<
+ "Pop/Funk" << "Pop" << "Porn Groove" << "Power Ballad" <<
+ "Pranks" << "Primus" << "Progressive Rock" << "Psychedelic Rock" <<
+ "Psychedelic" << "Punk Rock" << "Punk" << "R&B" <<
+ "Rap" << "Rave" << "Reggae" << "Retro" <<
+ "Revival" << "Rhythmic Soul" << "Rock & Roll" << "Rock" <<
+ "Salsa" << "Samba" << "Satire" << "Showtunes" <<
+ "Ska" << "Slow Jam" << "Slow Rock" << "Sonata" <<
+ "Soul" << "Sound Clip" << "Soundtrack" << "Southern Rock" <<
+ "Space" << "Speech" << "Swing" << "Symphonic Rock" <<
+ "Symphony" << "Synthpop" << "Tango" << "Techno-Industrial" <<
+ "Techno" << "Terror" << "Thrash Metal" << "Top 40" <<
+ "Trailer" << "Trance" << "Tribal" << "Trip-Hop" <<
+ "Vocal";
+ m_i18n << i18n("Unknown") << i18n("A Cappella") << i18n("Acid Jazz") <<
+ i18n("Acid Punk") << i18n("Acid") << i18n("Acoustic") << i18n("Alternative") <<
+ i18n("Alt. Rock") << i18n("Ambient") << i18n("Anime") << i18n("Avantgarde") <<
+ i18n("Ballad") << i18n("Bass") << i18n("Beat") << i18n("Bebop") <<
+ i18n("Big Band") << i18n("Black Metal") << i18n("Bluegrass") << i18n("Blues") <<
+ i18n("Booty Bass") << i18n("BritPop") << i18n("Cabaret") << i18n("Celtic") <<
+ i18n("Chamber Music") << i18n("Chanson") << i18n("Chorus") << i18n("Christian Gangsta Rap") <<
+ i18n("Christian Rap") << i18n("Christian Rock") << i18n("Classical") << i18n("Classic Rock") <<
+ i18n("Club-house") << i18n("Club") << i18n("Comedy") << i18n("Contemporary Christian") <<
+ i18n("music genre", "Country") << i18n("Crossover") << i18n("Cult") << i18n("Dance Hall") <<
+ i18n("Dance") << i18n("Darkwave") << i18n("Death Metal") << i18n("Disco") <<
+ i18n("Dream") << i18n("Drum & Bass") << i18n("Drum Solo") << i18n("Duet") <<
+ i18n("Easy Listening") << i18n("Electronic") << i18n("Ethnic") << i18n("Eurodance") <<
+ i18n("Euro-House") << i18n("Euro-Techno") << i18n("Fast-Fusion") << i18n("Folklore") <<
+ i18n("Folk/Rock") << i18n("Folk") << i18n("Freestyle") << i18n("Funk") <<
+ i18n("Fusion") << i18n("Game") << i18n("Gangsta Rap") << i18n("Goa") <<
+ i18n("Gospel") << i18n("Gothic Rock") << i18n("Gothic") << i18n("Grunge") <<
+ i18n("Hardcore") << i18n("Hard Rock") << i18n("Heavy Metal") << i18n("Hip-Hop") <<
+ i18n("House") << i18n("Humor") << i18n("Indie") << i18n("Industrial") <<
+ i18n("Instrumental Pop") << i18n("Instrumental Rock") << i18n("Instrumental") << i18n("Jazz+Funk") <<
+ i18n("Jazz") << i18n("JPop") << i18n("Jungle") << i18n("Latin") << i18n("Lo-Fi") <<
+ i18n("Meditative") << i18n("Merengue") << i18n("Metal") << i18n("Musical") <<
+ i18n("National Folk") << i18n("Native American") << i18n("Negerpunk") << i18n("New Age") <<
+ i18n("New Wave") << i18n("Noise") << i18n("Oldies") << i18n("Opera") <<
+ i18n("Other") << i18n("Polka") << i18n("Polsk Punk") << i18n("Pop-Funk") <<
+ i18n("Pop/Funk") << i18n("Pop") << i18n("Porn Groove") << i18n("Power Ballad") <<
+ i18n("Pranks") << i18n("Primus") << i18n("Progressive Rock") << i18n("Psychedelic Rock") <<
+ i18n("Psychedelic") << i18n("Punk Rock") << i18n("Punk") << i18n("R&B") <<
+ i18n("Rap") << i18n("Rave") << i18n("Reggae") << i18n("Retro") <<
+ i18n("Revival") << i18n("Rhythmic Soul") << i18n("Rock & Roll") << i18n("Rock") <<
+ i18n("Salsa") << i18n("Samba") << i18n("Satire") << i18n("Showtunes") <<
+ i18n("Ska") << i18n("Slow Jam") << i18n("Slow Rock") << i18n("Sonata") <<
+ i18n("Soul") << i18n("Sound Clip") << i18n("Soundtrack") << i18n("Southern Rock") <<
+ i18n("Space") << i18n("Speech") << i18n("Swing") << i18n("Symphonic Rock") <<
+ i18n("Symphony") << i18n("Synthpop") << i18n("Tango") << i18n("Techno-Industrial") <<
+ i18n("Techno") << i18n("Terror") << i18n("Thrash Metal") << i18n("Top 40") <<
+ i18n("Trailer") << i18n("Trance") << i18n("Tribal") << i18n("Trip-Hop") <<
+ i18n("Vocal");
+}
+
+const QString KCDDB::Genres::cddb2i18n(const QString &genre) const
+{
+ QString userDefinedGenre = genre.stripWhiteSpace();
+ int index = m_cddb.findIndex(userDefinedGenre);
+ if (index != -1)
+ {
+ return m_i18n[index];
+ }
+ else
+ {
+ return userDefinedGenre;
+ }
+}
+
+const QString KCDDB::Genres::i18n2cddb(const QString &genre) const
+{
+ QString userDefinedGenre = genre.stripWhiteSpace();
+ int index = m_i18n.findIndex(userDefinedGenre);
+ if (index != -1)
+ {
+ return m_cddb[index];
+ }
+ else
+ {
+ return userDefinedGenre;
+ }
+}
diff --git a/libkcddb/genres.h b/libkcddb/genres.h
new file mode 100644
index 00000000..4456f894
--- /dev/null
+++ b/libkcddb/genres.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2005 by Shaheed Haque (srhaque@iee.org). All rights reserved.
+//
+// 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 KCDDB_GENRES_H
+#define KCDDB_GENRES_H
+
+#include <qstring.h>
+#include <qstringlist.h>
+
+namespace KCDDB
+{
+ /**
+ * The Genre is completely arbitrary. But we follow kaudiocreator's cue
+ * and make life easy for people.
+ */
+ class Genres
+ {
+ public:
+ Genres();
+
+ const QStringList &cddbList() const { return m_cddb; };
+ const QStringList &i18nList() const { return m_i18n; };
+
+ /**
+ * Lookup the CDDB genre, and return the i18n'd version.
+ */
+ const QString cddb2i18n(const QString &genre) const;
+
+ /**
+ * Lookup the i18n genre, and return the CDDB version if we can.
+ */
+ const QString i18n2cddb(const QString &genre) const;
+ private:
+ QStringList m_cddb;
+ QStringList m_i18n;
+ };
+}
+
+#endif
diff --git a/libkcddb/httplookup.cpp b/libkcddb/httplookup.cpp
new file mode 100644
index 00000000..f85da894
--- /dev/null
+++ b/libkcddb/httplookup.cpp
@@ -0,0 +1,186 @@
+/*
+ Copyright ( C ) 2002 Rik Hemsley ( rikkus ) <rik@kde.org>
+ Copyright ( C ) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ Copyright ( C ) 2002 Nadeem Hasan <nhasan@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or ( at your option ) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <kio/job.h>
+#include <kdebug.h>
+
+#include "httplookup.h"
+
+namespace KCDDB
+{
+ HTTPLookup::HTTPLookup()
+ : Lookup(),
+ block_( true ), state_( Idle ), result_( Success )
+ {
+ }
+
+ HTTPLookup::~HTTPLookup()
+ {
+ }
+
+ CDDB::Result
+ HTTPLookup::sendQuery()
+ {
+ QString cmd = QString( "cddb query %1 %2" )
+ .arg( trackOffsetListToId(), trackOffsetListToString() ) ;
+
+ makeURL( cmd );
+ Result result = fetchURL();
+
+ return result;
+ }
+
+ CDDB::Result
+ HTTPLookup::sendRead( const CDDBMatch & match )
+ {
+ category_ = match.first;
+ QString discid = match.second;
+
+ QString cmd = QString( "cddb read %1 %2" )
+ .arg( category_, discid );
+
+ makeURL( cmd );
+ Result result = fetchURL();
+
+ return result;
+ }
+
+ void
+ HTTPLookup::initURL( const QString & hostName, uint port )
+ {
+ cgiURL_.setProtocol( "http" );
+ cgiURL_.setHost( hostName );
+ cgiURL_.setPort( port );
+ cgiURL_.setPath( "/~cddb/cddb.cgi" );
+
+ return;
+ }
+
+ void
+ HTTPLookup::makeURL( const QString & cmd )
+ {
+ // The whole query has to constructed each time as the
+ // CDDB CGI script expects the parameters in strict order
+
+ cgiURL_.setQuery( QString::null );
+
+ QString hello = QString("%1 %2 %3 %4")
+ .arg(user_, localHostName_, clientName(), clientVersion());
+
+ cgiURL_.addQueryItem( "cmd", cmd );
+ cgiURL_.addQueryItem( "hello", hello );
+ cgiURL_.addQueryItem( "proto", "6" );
+ }
+
+ void
+ HTTPLookup::jobFinished()
+ {
+ QStringList lineList = QStringList::split( "\n", QString::fromUtf8(data_, data_.size()) );
+ QStringList::ConstIterator it = lineList.begin();
+
+ switch ( state_ )
+ {
+ case WaitingForQueryResponse:
+
+ if ( it != lineList.end() )
+ {
+ QString line( *it );
+
+ result_ = parseQuery( line );
+
+ switch ( result_ )
+ {
+ case Success:
+
+ if ( !block_ )
+ emit queryReady();
+ break;
+
+ case MultipleRecordFound:
+
+ ++it;
+ while ( it != lineList.end() )
+ {
+ QString line( *it );
+
+ if ( '.' == line[ 0 ] )
+ {
+ result_ = Success;
+
+ if ( !block_ )
+ emit queryReady();
+ break;
+ }
+
+ parseExtraMatch( line );
+ ++it;
+ }
+
+ break;
+
+ case ServerError:
+ case NoRecordFound:
+ if ( !block_ )
+ emit queryReady();
+
+ return;
+ break;
+
+ default:
+
+ break;
+ }
+
+ }
+
+ break;
+
+ case WaitingForReadResponse:
+
+ {
+ CDInfo info;
+
+ if ( info.load( QString::fromUtf8(data_,data_.size()) ) )
+ {
+ info.category = category_;
+ cdInfoList_.append( info );
+ }
+
+ if ( !block_ )
+ emit readReady();
+ }
+
+ return;
+
+ break;
+
+ default:
+
+ break;
+ }
+
+ result_ = Success;
+ }
+}
+
+#include "httplookup.moc"
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/httplookup.h b/libkcddb/httplookup.h
new file mode 100644
index 00000000..7404accd
--- /dev/null
+++ b/libkcddb/httplookup.h
@@ -0,0 +1,82 @@
+/*
+ Copyright (C) 2002 Rik Hemsley ( rikkus ) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ Copyright (C) 2002 Nadeem Hasan <nhasan@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or ( at your option ) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KCDDB_HTTP_LOOKUP_H
+#define KCDDB_HTTP_LOOKUP_H
+
+#include <kurl.h>
+
+#include "lookup.h"
+
+namespace KIO
+{
+ class TransferJob;
+ class Job;
+}
+
+namespace KCDDB
+{
+ class HTTPLookup : public Lookup
+ {
+
+ Q_OBJECT
+
+ public:
+
+ enum State
+ {
+ Idle,
+ WaitingForQueryResponse,
+ WaitingForReadResponse
+ };
+
+ HTTPLookup();
+ virtual ~HTTPLookup();
+
+ protected:
+
+ void initURL( const QString &, uint );
+ void makeURL( const QString & );
+ virtual Result fetchURL() = 0;
+
+ void jobFinished();
+
+ Result sendQuery();
+ Result sendRead( const CDDBMatch & );
+
+ signals:
+
+ void queryReady();
+ void readReady();
+
+ protected:
+
+ bool block_;
+ KURL cgiURL_;
+ QByteArray data_;
+ State state_;
+ Result result_;
+ };
+}
+
+#endif
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/httpsubmit.cpp b/libkcddb/httpsubmit.cpp
new file mode 100644
index 00000000..9b06fcb2
--- /dev/null
+++ b/libkcddb/httpsubmit.cpp
@@ -0,0 +1,60 @@
+/*
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ Copyright (C) 2003 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+#include "httpsubmit.h"
+#include <kdebug.h>
+#include <kio/job.h>
+
+namespace KCDDB
+{
+ HTTPSubmit::HTTPSubmit(const QString& from, const QString& hostname, uint port)
+ : Submit(), from_(from)
+ {
+ url_.setProtocol("http");
+ url_.setHost(hostname);
+ url_.setPort(port);
+ url_.setPath("/~cddb/submit.cgi");
+ }
+
+ HTTPSubmit::~HTTPSubmit()
+ {
+
+ }
+
+ KIO::Job* HTTPSubmit::createJob(const CDInfo& cdInfo)
+ {
+ KIO::TransferJob* job = KIO::http_post(url_, diskData_.utf8(), false);
+
+ job->addMetaData("content-type", "Content-Type: text/plain");
+ QString header;
+
+ header += "Content-Type: text/plain\n";
+
+ header += "Category: " + cdInfo.category + "\n";
+ header += "Discid: " + cdInfo.id + "\n";
+ header += "User-Email: " + from_ + "\n";
+ // Change to test for testing
+ header += "Submit-Mode: submit\n";
+ header += "Charset: UTF-8";
+
+ job->addMetaData("customHTTPHeader", header);
+
+ return job;
+ }
+}
diff --git a/libkcddb/httpsubmit.h b/libkcddb/httpsubmit.h
new file mode 100644
index 00000000..0ab3c72a
--- /dev/null
+++ b/libkcddb/httpsubmit.h
@@ -0,0 +1,41 @@
+#ifndef HTTPSUBMIT_H
+#define HTTPSUBMIT_H
+/*
+ Copyright (C) 2003 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "submit.h"
+#include <kurl.h>
+
+namespace KCDDB
+{
+ class HTTPSubmit : public Submit
+ {
+ public:
+ HTTPSubmit(const QString& from, const QString& hostname, uint port);
+ virtual ~HTTPSubmit();
+
+ protected:
+ virtual KIO::Job* createJob(const CDInfo& cdInfo);
+
+ KURL url_;
+ QString from_;
+ } ;
+}
+
+#endif // HTTPSUBMIT_H
diff --git a/libkcddb/kcmcddb/Makefile.am b/libkcddb/kcmcddb/Makefile.am
new file mode 100644
index 00000000..cc638047
--- /dev/null
+++ b/libkcddb/kcmcddb/Makefile.am
@@ -0,0 +1,23 @@
+INCLUDES = -I$(srcdir)/../.. -I.. $(all_includes)
+
+kde_module_LTLIBRARIES = kcm_cddb.la
+
+kcm_cddb_la_SOURCES = \
+ cddbconfigwidgetbase.ui cddbconfigwidget.cpp kcmcddb.cpp
+
+kcm_cddb_la_LDFLAGS = \
+ $(all_libraries) -module -avoid-version -no-undefined
+
+kcm_cddb_la_LIBADD = ../libkcddb.la $(LIB_KDEUI)
+
+kcm_cddb_la_COMPILE_FIRST = ../configbase.h
+
+METASOURCES = AUTO
+
+xdg_apps_DATA = libkcddb.desktop
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/kcmcddb.pot
+
+updatedir = $(kde_datadir)/kconf_update
+update_DATA = kcmcddb-emailsettings.upd
diff --git a/libkcddb/kcmcddb/cddbconfigwidget.cpp b/libkcddb/kcmcddb/cddbconfigwidget.cpp
new file mode 100644
index 00000000..067af2e0
--- /dev/null
+++ b/libkcddb/kcmcddb/cddbconfigwidget.cpp
@@ -0,0 +1,108 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ Copyright (C) 2004 Richard Lärkäng <nouseforaname@home.se>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "cddbconfigwidget.h"
+
+#include "libkcddb/sites.h"
+#include "libkcddb/lookup.h"
+
+#include <qlistbox.h>
+#include <qcombobox.h>
+#include <qspinbox.h>
+#include <qlineedit.h>
+#include <kfiledialog.h>
+#include <kapplication.h>
+#include <klocale.h>
+#include <kinputdialog.h>
+#include <kmessagebox.h>
+#include <keditlistbox.h>
+#include <qwidgetstack.h>
+#include <kurlrequester.h>
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+
+CDDBConfigWidget::CDDBConfigWidget(QWidget * parent, const char * name)
+ : CDDBConfigWidgetBase(parent, name)
+{
+ // Connections from widgets are made in designer.
+
+ KURLRequester* urlReq = new KURLRequester(this);
+ urlReq->setMode(KFile::Directory);
+
+ KEditListBox* editListBox = new KEditListBox(i18n("Cache Locations"), urlReq->customEditor(), cacheLocationsParent, "kcfg_cacheLocations");
+ cacheLocationsParent->raiseWidget(editListBox);
+
+ kcfg_submitTransport->remove(needsAuthenticationBox);
+}
+
+void CDDBConfigWidget::showMirrorList()
+{
+ KCDDB::Sites s;
+
+ QValueList<KCDDB::Mirror> sites = s.siteList();
+ QMap<QString, KCDDB::Mirror> keys;
+ for (QValueList<KCDDB::Mirror>::Iterator it = sites.begin(); it != sites.end(); ++it)
+ if ((*it).transport == KCDDB::Lookup::CDDBP)
+ keys[(*it).address + "(CDDBP, " + QString::number((*it).port) + ") " + (*it).description] = *it;
+ else
+ keys[(*it).address + "(HTTP, " + QString::number((*it).port) + ") " + (*it).description] = *it;
+
+ bool ok;
+
+ if (keys.isEmpty())
+ {
+ KMessageBox::information(this, i18n("Could not fetch mirror list."), i18n("Could Not Fetch"));
+ return;
+ }
+
+ QStringList result = KInputDialog::getItemList(i18n("Select mirror"),
+ i18n("Select one of these mirrors"), keys.keys(),
+ QStringList(), false, &ok, this);
+
+ if (ok && result.count() == 1)
+ {
+ KCDDB::Mirror m = keys[*(result.begin())];
+
+ kcfg_lookupTransport->setCurrentItem(m.transport == KCDDB::Lookup::CDDBP ? 0 : 1);
+ kcfg_hostname->setText(m.address);
+ kcfg_port->setValue(m.port);
+ }
+}
+
+void CDDBConfigWidget::protocolChanged()
+{
+ // Change the port if the port is the default-value for the old protocol
+
+ if (kcfg_lookupTransport->currentText() == i18n("HTTP") && kcfg_port->value() == 8880)
+ kcfg_port->setValue(80);
+ else if (kcfg_lookupTransport->currentText() == i18n("CDDB") && kcfg_port->value() == 80)
+ kcfg_port->setValue(8880);
+}
+
+void CDDBConfigWidget::needAuthenticationChanged(bool needsAuth)
+{
+ kcfg_smtpUsername->setEnabled(needsAuth);
+ if (!needsAuth)
+ kcfg_smtpUsername->clear();
+}
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
+
+#include "cddbconfigwidget.moc"
diff --git a/libkcddb/kcmcddb/cddbconfigwidget.h b/libkcddb/kcmcddb/cddbconfigwidget.h
new file mode 100644
index 00000000..3d9136e6
--- /dev/null
+++ b/libkcddb/kcmcddb/cddbconfigwidget.h
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef CDDB_CONFIG_WIDGET_H
+#define CDDB_CONFIG_WIDGET_H
+
+#include "cddbconfigwidgetbase.h"
+
+class CDDBConfigWidget : public CDDBConfigWidgetBase
+{
+ Q_OBJECT
+
+ public:
+
+ CDDBConfigWidget(QWidget * parent = 0, const char * name = 0);
+
+ protected slots:
+
+ virtual void showMirrorList();
+
+ virtual void protocolChanged();
+
+ virtual void needAuthenticationChanged(bool);
+};
+
+#endif // CDDB_CONFIG_WIDGET_H
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/kcmcddb/cddbconfigwidgetbase.ui b/libkcddb/kcmcddb/cddbconfigwidgetbase.ui
new file mode 100644
index 00000000..2158159a
--- /dev/null
+++ b/libkcddb/kcmcddb/cddbconfigwidgetbase.ui
@@ -0,0 +1,594 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>CDDBConfigWidgetBase</class>
+<comment>Used for configuring libkcddb.</comment>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>CDDBConfigWidgetBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>452</width>
+ <height>573</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>CDDB Settings</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>tabWidget2</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Lookup</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>kcfg_cachePolicy</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Mode</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>6</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>cacheOnly</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Cache only</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Only check in the local cache for CD information.</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>cacheAndRemote</cstring>
+ </property>
+ <property name="text">
+ <string>Cache &amp;and remote</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Check for locally cached CD information before trying to look up at remote CDDB server.</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>remoteOnly</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Remote only</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Only try to look up at remote CDDB server.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>serverBox</cstring>
+ </property>
+ <property name="title">
+ <string>CDDB Server</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel7</cstring>
+ </property>
+ <property name="text">
+ <string>CDD&amp;B server:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_hostname</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel9</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Transport:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_lookupTransport</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="1" column="1">
+ <item>
+ <property name="text">
+ <string>CDDB</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>HTTP</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>kcfg_lookupTransport</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Type of lookup which should be tried at the CDDB server.</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="1" column="2" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>mirrorListButton</cstring>
+ </property>
+ <property name="text">
+ <string>Show &amp;Mirror List</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="0" column="4">
+ <property name="name">
+ <cstring>kcfg_port</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maxValue">
+ <number>64000</number>
+ </property>
+ <property name="value">
+ <number>8880</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Port to connect to on CDDB server.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="3">
+ <property name="name">
+ <cstring>TextLabel8</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Port:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_port</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_hostname</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>freedb.freedb.org</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Name of CDDB server which will be used to look up CD information.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidgetStack">
+ <property name="name">
+ <cstring>cacheLocationsParent</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>WStackPage</cstring>
+ </property>
+ <attribute name="id">
+ <number>0</number>
+ </attribute>
+ </widget>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>41</width>
+ <height>260</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Submit</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>kcfg_emailAddress</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Email address:</string>
+ </property>
+ </widget>
+ <spacer row="2" column="1">
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>240</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QButtonGroup" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_submitTransport</cstring>
+ </property>
+ <property name="title">
+ <string>Submit Method</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="7" column="1">
+ <property name="name">
+ <cstring>spacer9</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>spacer10</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="1" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Server:</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="2" column="0" rowspan="1" colspan="6">
+ <property name="name">
+ <cstring>radioButton8</cstring>
+ </property>
+ <property name="text">
+ <string>SMTP (Email)</string>
+ </property>
+ <property name="buttonGroupId">
+ <number>1</number>
+ </property>
+ </widget>
+ <spacer row="3" column="0" rowspan="5" colspan="1">
+ <property name="name">
+ <cstring>spacer7</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>140</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QRadioButton" row="0" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>radioButton6</cstring>
+ </property>
+ <property name="text">
+ <string>HTTP</string>
+ </property>
+ <property name="buttonGroupId">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="4" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_httpSubmitServer</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="6">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>Port:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="1" column="7">
+ <property name="name">
+ <cstring>kcfg_httpSubmitPort</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="1" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>textLabel9_2</cstring>
+ </property>
+ <property name="text">
+ <string>Reply-To:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="1" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>textLabel2_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>SMTP server:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Port:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="5" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_replyTo</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="6" column="1" rowspan="1" colspan="7">
+ <property name="name">
+ <cstring>needsAuthenticationBox</cstring>
+ </property>
+ <property name="text">
+ <string>Server needs authentication</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="4" column="5" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_smtpHostname</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="7" column="2" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Username:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="7" column="5" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_smtpUsername</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="5" column="3" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_smtpPort</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="value">
+ <number>25</number>
+ </property>
+ </widget>
+ <spacer row="5" column="5" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>260</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>cacheOnly</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>serverBox</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>cacheAndRemote</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>serverBox</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>remoteOnly</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>serverBox</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>needsAuthenticationBox</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>CDDBConfigWidgetBase</receiver>
+ <slot>needAuthenticationChanged(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_lookupTransport</sender>
+ <signal>activated(int)</signal>
+ <receiver>CDDBConfigWidgetBase</receiver>
+ <slot>protocolChanged()</slot>
+ </connection>
+ <connection>
+ <sender>mirrorListButton</sender>
+ <signal>clicked()</signal>
+ <receiver>CDDBConfigWidgetBase</receiver>
+ <slot>showMirrorList()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>tabWidget2</tabstop>
+ <tabstop>cacheOnly</tabstop>
+ <tabstop>cacheAndRemote</tabstop>
+ <tabstop>remoteOnly</tabstop>
+ <tabstop>kcfg_hostname</tabstop>
+ <tabstop>kcfg_port</tabstop>
+ <tabstop>kcfg_lookupTransport</tabstop>
+</tabstops>
+<slots>
+ <slot access="protected">protocolChanged()</slot>
+ <slot access="protected">showMirrorList()</slot>
+ <slot access="protected">needAuthenticationChanged(bool)</slot>
+</slots>
+<layoutdefaults spacing="11" margin="6"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/libkcddb/kcmcddb/kcmcddb-emailsettings.upd b/libkcddb/kcmcddb/kcmcddb-emailsettings.upd
new file mode 100644
index 00000000..4fc6c111
--- /dev/null
+++ b/libkcddb/kcmcddb/kcmcddb-emailsettings.upd
@@ -0,0 +1,6 @@
+Id=kcmcddb_emailsettings
+File=kcmcddbrc
+Group=Submit
+Key=ownEmail,emailAddress
+Key=ownReplyTo,replyTo
+Key=ownSmtpHost,smtpHostname
diff --git a/libkcddb/kcmcddb/kcmcddb.cpp b/libkcddb/kcmcddb/kcmcddb.cpp
new file mode 100644
index 00000000..d5ca04a7
--- /dev/null
+++ b/libkcddb/kcmcddb/kcmcddb.cpp
@@ -0,0 +1,132 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ Copyright (C) 2003 Richard Lärkäng <nouseforaname@home.se>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <qlayout.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qspinbox.h>
+#include <qlineedit.h>
+#include <qradiobutton.h>
+#include <qlistbox.h>
+#include <qlabel.h>
+#include <qbuttongroup.h>
+
+#include <kconfig.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kgenericfactory.h>
+#include <kmessagebox.h>
+#include <kconfigdialogmanager.h>
+
+#include "cddbconfigwidget.h"
+
+#include "kcmcddb.h"
+#include "libkcddb/lookup.h"
+#include "libkcddb/cache.h"
+#include "libkcddb/submit.h"
+
+typedef KGenericFactory<CDDBModule, QWidget> KCDDBFactory;
+K_EXPORT_COMPONENT_FACTORY ( kcm_cddb, KCDDBFactory( "kcmcddb" ) )
+
+CDDBModule::CDDBModule(QWidget *parent, const char *name, const QStringList &)
+ : KCModule(parent, name)
+{
+ KGlobal::locale()->insertCatalogue("libkcddb");
+ setButtons(Default | Apply);
+
+ widget_ = new CDDBConfigWidget(this);
+
+ KCDDB::Config* cfg = new KCDDB::Config();
+ cfg->readConfig();
+
+ addConfig(cfg, widget_);
+
+ QVBoxLayout * layout = new QVBoxLayout(this, 0);
+
+ layout->addWidget(widget_);
+ layout->addStretch();
+
+ setQuickHelp(i18n("CDDB is used to get information like artist, title and song-names in CD's"));
+
+ load();
+}
+
+ void
+CDDBModule::defaults()
+{
+ KCModule::defaults();
+
+ updateWidgetsFromConfig(KCDDB::Config());
+}
+
+ void
+CDDBModule::checkSettings() const
+{
+ KCDDB::Config config;
+
+ config.readConfig();
+
+ if (config.smtpHostname().isEmpty() || config.emailAddress().isEmpty()
+ || !config.emailAddress().contains("@") ||
+ (!config.replyTo().isEmpty() && !config.replyTo().contains("@")))
+
+ {
+ if (config.submitTransport() == KCDDB::Submit::SMTP)
+ {
+ KMessageBox::sorry(widget_, i18n("freedb has been set to use HTTP for submissions "
+ "because the email details you have entered are "
+ "incomplete. Please review your email settings "
+ "and try again."), i18n("Incorrect Email Settings"));
+ config.setSubmitTransport(KCDDB::Submit::HTTP);
+
+ config.writeConfig();
+ }
+ }
+}
+
+ void
+CDDBModule::updateWidgetsFromConfig(const KCDDB::Config & config)
+{
+ bool smtpUserIsEmpty = config.smtpUsername().isEmpty();
+ widget_->needsAuthenticationBox->setChecked(!smtpUserIsEmpty);
+ widget_->kcfg_smtpUsername->setEnabled(!smtpUserIsEmpty);
+}
+
+ void
+CDDBModule::save()
+{
+ KCModule::save();
+
+ checkSettings();
+}
+
+ void
+CDDBModule::load()
+{
+ KCModule::load();
+
+ KCDDB::Config config;
+ config.readConfig();
+ updateWidgetsFromConfig(config);
+}
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
+
+#include "kcmcddb.moc"
diff --git a/libkcddb/kcmcddb/kcmcddb.h b/libkcddb/kcmcddb/kcmcddb.h
new file mode 100644
index 00000000..dfb7c4ef
--- /dev/null
+++ b/libkcddb/kcmcddb/kcmcddb.h
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KCMCDDB_H
+#define KCMCDDB_H
+
+#include <kcmodule.h>
+
+#include "libkcddb/config.h"
+
+class CDDBConfigWidgetBase;
+class KConfigDialogManager;
+
+class CDDBModule : public KCModule
+{
+ Q_OBJECT
+
+ public:
+
+ CDDBModule(QWidget * parent, const char *name, const QStringList &);
+
+ public slots:
+
+ void defaults();
+ void save();
+ void load();
+
+ protected:
+
+ void checkSettings() const;
+ void updateWidgetsFromConfig(const KCDDB::Config &);
+
+ private:
+
+ CDDBConfigWidgetBase * widget_;
+};
+
+#endif // KCMCDDB_H
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/kcmcddb/libkcddb.desktop b/libkcddb/kcmcddb/libkcddb.desktop
new file mode 100644
index 00000000..0c6e659c
--- /dev/null
+++ b/libkcddb/kcmcddb/libkcddb.desktop
@@ -0,0 +1,187 @@
+[Desktop Entry]
+Exec=kcmshell libkcddb
+Icon=cdaudio_unmount
+Type=Application
+X-KDE-ModuleType=Library
+X-KDE-Library=cddb
+
+Name=CDDB Retrieval
+Name[bg]=Извличане от CDDB
+Name[bs]=CDDB dobavljanje
+Name[ca]=Recuperació CDDB
+Name[cs]=Získání CDDB
+Name[cy]=Cyrchu CDDB
+Name[da]=CDDB-Hentning
+Name[de]=CDDB-Abfrage
+Name[el]=Ανάκτηση CDDB
+Name[eo]=CDDB-serĉo
+Name[es]=Recuperador de CDDB
+Name[et]=CDDB ülekanded
+Name[eu]=CDDB berreskuraketa
+Name[fa]=بازیابی CDDB
+Name[fi]=CDDB-haku
+Name[fr]=Recherche CDDB
+Name[ga]=Aisghabháil CDDB
+Name[gl]=Obtención CDDB
+Name[he]=אחזור CDDB
+Name[hi]=सीडीडीबी रिट्राइवल
+Name[hr]=Dohvat iz CDDB
+Name[hu]=CDDB-lekérdezés
+Name[is]=CDDB stillingar
+Name[it]=Recupero CDDB
+Name[ja]=CDDB 検索
+Name[kk]=CDDB деректерді алу
+Name[ko]=CDDB 가져오기
+Name[lt]=CDDB įrašo atsisiuntimas
+Name[mk]=Пребарување на CDDB
+Name[ms]=Pembuka CDDB
+Name[nb]=CDDB-henting
+Name[nds]=CDDB-Affraag
+Name[ne]=CDDB पुनः प्राप्ति
+Name[nl]=CDDB-informatie
+Name[nn]=CDDB-henting
+Name[pa]=CDDB ਪਰਾਪਤੀ
+Name[pl]=Pobieranie z CDDB
+Name[pt]=Transferência de CDDB
+Name[pt_BR]=Recuperação do CDDB
+Name[ro]=Căutare CDDB
+Name[ru]=Доступ к CDDB
+Name[sk]=Informácie z CDDB
+Name[sl]=Pridobivanje CDDB
+Name[sr]=Добављање из CDDB-а
+Name[sr@Latn]=Dobavljanje iz CDDB-a
+Name[sv]=Hämta från CDDB
+Name[ta]=குறுந்தகடு தகவல்தளம் மீட்டெடுப்பு
+Name[tg]=Бозёбии CDDB
+Name[th]=ดึงข้อมูล CDDB
+Name[tr]=CDDB Erişimi
+Name[uk]=Звантаження CDDB
+Name[ven]=U humbula ha CDDB
+Name[xh]=CDDB Ukukangela
+Name[zh_CN]=CDDB 查询
+Name[zh_HK]=CDDB 資訊擷取
+Name[zh_TW]=CDDB 取得資訊
+
+GenericName=CDDB Configuration
+GenericName[ar]=اعدادت CDDB
+GenericName[bg]=Настройки на CDDB
+GenericName[br]=Kefluniadur CDDB
+GenericName[bs]=Podešavanje CDDBa
+GenericName[ca]=Configuració CDDB
+GenericName[cs]=Nastavení CDDB
+GenericName[cy]=Ffurfweddu CDDB
+GenericName[da]=CDDB-Indstilling
+GenericName[de]=CDDB-Einrichtung
+GenericName[el]=Ρύθμιση CDDB
+GenericName[eo]=LDDB-agordo
+GenericName[es]=Configuración de CDDB
+GenericName[et]=CDDB seadistamine
+GenericName[eu]=CDDB konfigurazioa
+GenericName[fa]=پیکربندی CDDB
+GenericName[fi]=CDDB-asetukset
+GenericName[fr]=Configuration CDDB
+GenericName[ga]=Cumraíocht CDDB
+GenericName[gl]=Configuración da CDDB
+GenericName[he]=הגדרות CDDB
+GenericName[hi]=सीडीडीबी कॉन्फ़िगरेशन
+GenericName[hr]=Podešavanje CDDB-a
+GenericName[hu]=CDDB-beállító
+GenericName[is]=Stillingar CDDB
+GenericName[it]=Configurazione CDDB
+GenericName[ja]=CDDB の設定
+GenericName[kk]=CDDB баптауы
+GenericName[km]=ការ​កំណត់​រចនាសម្ព័ន្ធ CDDB
+GenericName[ko]=CDDB 설정
+GenericName[lt]=CDDB konfigūravimas
+GenericName[lv]=CDDB Konfigurācija
+GenericName[mk]=Конфигурација на CDDB
+GenericName[ms]=Penyelarasan CDDB
+GenericName[nb]=CDDB-oppsett
+GenericName[nds]=CDDB-Instellen
+GenericName[ne]=CDDB कन्फिगरेसन
+GenericName[nl]=CDDB instellingen
+GenericName[nn]=CDDB-oppsett
+GenericName[pa]=CDDB ਸੰਰਚਨਾ
+GenericName[pl]=Konfiguracja CDDB
+GenericName[pt]=Configuração do CDDB
+GenericName[pt_BR]=Configuração do CDDB
+GenericName[ro]=Configurează CDDB
+GenericName[ru]=Настройка CDDB
+GenericName[sk]=Nastavenie CDDB
+GenericName[sl]=Nastavitve CDDB
+GenericName[sr]=Подешавање CDDB-а
+GenericName[sr@Latn]=Podešavanje CDDB-a
+GenericName[sv]=CDDB-inställning
+GenericName[ta]=குறுந்தகடு தகவல்தள வடிவமைப்பு
+GenericName[tg]=Батанзимдарории CDDB
+GenericName[th]=ปรับแต่ง CDDB
+GenericName[tr]=CDDB Yapılandırması
+GenericName[uk]=Налаштування CDDB
+GenericName[uz]=CDDB moslamasi
+GenericName[uz@cyrillic]=CDDB мосламаси
+GenericName[ven]=Nzudzanyo ya CDDB
+GenericName[wa]=Apontiaedje di CDDB
+GenericName[zh_CN]=CDDB 配置
+GenericName[zh_HK]=CDDB 設定
+GenericName[zh_TW]=CDDB 設定
+GenericName[zu]=Uhlanganiso lwe CDDB
+
+Comment=Configure the CDDB Retrieval
+Comment[bg]=Настройване на извличането от CDDB
+Comment[bs]=Podesite CDDB dobavljanje
+Comment[ca]=Configura la recuperació CDDB
+Comment[cs]=Nastavení stahování z CDDB
+Comment[cy]=Ffurfweddu Nôl o'r CDDB
+Comment[da]=Indstil at hente via CDDB
+Comment[de]=CDDB-Abfrage einrichten
+Comment[el]=Ρύθμιση της ανάκτησης CDDB
+Comment[eo]=Agordi la CDDB-serĉon
+Comment[es]=Configurar el recuperador de CDDB
+Comment[et]=CDDB ülekannete seadistamine
+Comment[eu]=Konfiguratu CDDB berreskuraketa
+Comment[fa]=پیکربندی بازیابی CDDB
+Comment[fi]=Aseta CDDB-haku
+Comment[fr]=Configurer la recherche CDDB
+Comment[ga]=Cumraigh aisghabháil CDDB
+Comment[gl]=Configurar as solicitudes á CDDB
+Comment[he]=הגדר אחזור של CDDB
+Comment[hu]=A CDDB-lekérdezés beállításai
+Comment[is]=Stilling CDDB
+Comment[it]=Configura il recupero CDDB
+Comment[ja]=CDDB 検索の設定
+Comment[kk]=CDDB деректер алуды баптау
+Comment[km]=កំណត់​រចនាសម្ព័ន្ធ CDDB Retrieval
+Comment[ko]=CDDB 가져오기 설정
+Comment[lt]=Čia galite konfigūruoti CDDB įrašų atsisiuntimą
+Comment[mk]=Конфигурирање на пребарувањето на CDDB
+Comment[nb]=Oppsett av CDDB-henting
+Comment[nds]=CDDB-Affraag instellen
+Comment[ne]=CDDB पुनः प्राप्ति कन्फिगर गर्नुहोस्
+Comment[nl]=Ophalen van CDDB-informatie instellen
+Comment[nn]=Oppsett av CDDB-henting
+Comment[pa]=CDDB ਪਰਾਪਤੀ ਦੀ ਸੰਰਚਨਾ
+Comment[pl]=Konfiguracja pobierania danych z CDDB
+Comment[pt]=Configurar a Transferência de CDDB
+Comment[pt_BR]=Configurar a recuperação do CDDB
+Comment[ru]=Настройка CDDB
+Comment[sk]=Nastavenie CDDB
+Comment[sl]=Nastavi pridobivanje CDDB
+Comment[sr]=Подешавање добављања CDDB-а
+Comment[sr@Latn]=Podešavanje dobavljanja CDDB-a
+Comment[sv]=Anpassa hämtning från CDDB
+Comment[ta]=CDDB மீட்டெடுப்பை உள்ளமை
+Comment[tg]=Танзими Бозёбии CDDB
+Comment[th]=ปรับแต่งการดึงข้อมูลจาก CDDB
+Comment[tr]=CDDB Erişimini Yapılandır
+Comment[uk]=Налаштування звантаження CDDB
+Comment[zh_CN]=配置 CDDB 获取
+Comment[zh_HK]=設定 CDDB 資訊擷取
+Comment[zh_TW]=CDDB 取得資訊設定
+
+Keywords=cddb
+Keywords[bg]=компактдиск, диск, извличане, информация, песен, база, данни, cddb
+Keywords[hi]=सीडीडीबी
+Keywords[nds]=CDDB
+Keywords[ta]=குறுந்தகடு தகவல்தளம்
+
+Categories=Qt;KDE;Settings;X-KDE-settings-sound;
diff --git a/libkcddb/libkcddb.kcfg b/libkcddb/libkcddb.kcfg
new file mode 100644
index 00000000..cbd123c7
--- /dev/null
+++ b/libkcddb/libkcddb.kcfg
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+ <include>qdir.h</include>
+ <kcfgfile name="kcmcddbrc"/>
+ <group name="Lookup">
+ <entry name="hostname" type="String">
+ <default>freedb.freedb.org</default>
+ </entry>
+ <entry name="port" type="Int">
+ <default>80</default>
+ </entry>
+ <entry name="cachePolicy" type="Enum">
+ <choices>
+ <choice name="OnlyCache"></choice>
+ <choice name="UseCache"></choice>
+ <choice name="IgnoreCache"></choice>
+ </choices>
+ <default>UseCache</default>
+ </entry>
+ <entry name="lookupTransport" type="Enum">
+ <choices>
+ <choice name="CDDBP"></choice>
+ <choice name="HTTP"></choice>
+ </choices>
+ <default>HTTP</default>
+ </entry>
+ <entry name="cacheLocations" type="PathList">
+ <default code="true">QDir::homeDirPath()+"/.cddb/"</default>
+ </entry>
+ </group>
+ <group name="Submit">
+ <entry name="submitTransport" type="Enum">
+ <choices>
+ <choice name="HTTP"></choice>
+ <choice name="SMTP"></choice>
+ </choices>
+ <default>HTTP</default>
+ </entry>
+ <entry name="emailAddress" type="String">
+ </entry>
+ <entry name="httpSubmitServer" type="String">
+ <default>freedb.freedb.org</default>
+ </entry>
+ <entry name="httpSubmitPort" type="Int">
+ <default>80</default>
+ </entry>
+ <entry name="smtpPort" type="Int">
+ <default>25</default>
+ </entry>
+ <entry name="smtpUsername" type="String">
+ </entry>
+ <entry name="useGlobalEmail" type="Bool">
+ <default>true</default>
+ </entry>
+ <entry name="replyTo" type="String">
+ </entry>
+ <entry name="smtpHostname" type="String">
+ </entry>
+ <entry name="submitAddress" type="String">
+ <default>freedb-submit@freedb.org</default>
+ </entry>
+ </group>
+</kcfg>
diff --git a/libkcddb/lookup.cpp b/libkcddb/lookup.cpp
new file mode 100644
index 00000000..fb1858a3
--- /dev/null
+++ b/libkcddb/lookup.cpp
@@ -0,0 +1,87 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ CopyRight (C) 2002 Nadeem Hasan <nhasan@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <kdebug.h>
+
+#include "lookup.h"
+
+namespace KCDDB
+{
+ Lookup::Lookup()
+ : CDDB()
+ {
+ }
+
+ Lookup::~Lookup()
+ {
+ // Empty.
+ }
+
+ CDDB::Result
+ Lookup::parseQuery( const QString & line )
+ {
+ uint serverStatus = statusCode( line );
+
+ if ( 200 == serverStatus )
+ {
+ QStringList tokenList = QStringList::split( ' ', line );
+ matchList_.append( qMakePair( tokenList[ 1 ], tokenList[ 2 ] ) );
+ return Success;
+ }
+ else if ( ( 211 == serverStatus ) || ( 210 == serverStatus ) )
+ {
+ return MultipleRecordFound;
+ }
+ else if ( 202 == serverStatus )
+ {
+ return NoRecordFound;
+ }
+
+ return ServerError;
+ }
+
+ void
+ Lookup::parseExtraMatch( const QString & line )
+ {
+ QStringList tokenList = QStringList::split( ' ', line );
+ matchList_.append( qMakePair( tokenList[ 0 ], tokenList[ 1 ] ) );
+ }
+
+ CDDB::Result
+ Lookup::parseRead( const QString & line )
+ {
+ uint serverStatus = statusCode( line );
+
+ if ( 210 != serverStatus )
+ return ServerError;
+
+ return Success;
+ }
+
+ CDInfoList
+ Lookup::lookupResponse() const
+ {
+ return cdInfoList_;
+ }
+
+}
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/lookup.h b/libkcddb/lookup.h
new file mode 100644
index 00000000..03761a55
--- /dev/null
+++ b/libkcddb/lookup.h
@@ -0,0 +1,65 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ CopyRight (C) 2002 Nadeem Hasan <nhasan@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KCDDB_LOOKUP_H
+#define KCDDB_LOOKUP_H
+
+#include "cddb.h"
+#include "cdinfo.h"
+#include <qobject.h>
+
+namespace KCDDB
+{
+ typedef QPair<QString, QString> CDDBMatch;
+ typedef QValueList<CDDBMatch> CDDBMatchList;
+
+ class Lookup : public CDDB, public QObject
+ {
+ public:
+
+ enum Transport
+ {
+ CDDBP,
+ HTTP
+ };
+
+
+ Lookup();
+ virtual ~Lookup();
+
+ virtual Result lookup( const QString &, uint, const TrackOffsetList & ) = 0;
+
+ CDInfoList lookupResponse() const;
+
+ protected:
+
+ void parseExtraMatch( const QString & );
+ Result parseQuery( const QString & );
+ Result parseRead( const QString & );
+
+ CDInfoList cdInfoList_;
+ CDDBMatchList matchList_;
+ QString category_;
+ };
+}
+
+#endif // KCDDB_LOOKUP_H
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/sites.cpp b/libkcddb/sites.cpp
new file mode 100644
index 00000000..56eba65a
--- /dev/null
+++ b/libkcddb/sites.cpp
@@ -0,0 +1,119 @@
+/*
+ Copyright ( C ) 2004 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or ( at your option ) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "sites.h"
+#include <kurl.h>
+#include <kio/netaccess.h>
+#include <qfile.h>
+#include <kdebug.h>
+#include <qregexp.h>
+
+namespace KCDDB
+{
+ Sites::Sites()
+ : CDDB()
+ {
+
+ }
+
+ QValueList<Mirror>
+ Sites::siteList()
+ {
+ KURL url;
+ url.setProtocol( "http" );
+ url.setHost( "freedb.freedb.org" );
+ url.setPort( 80 );
+ url.setPath( "/~cddb/cddb.cgi" );
+
+ url.setQuery( QString::null );
+
+ QString hello = QString("%1 %2 %3 %4")
+ .arg(user_, localHostName_, clientName(), clientVersion());
+
+ url.addQueryItem( "cmd", "sites" );
+ url.addQueryItem( "hello", hello );
+ url.addQueryItem( "proto", "5" );
+
+ QValueList<Mirror> result;
+
+ QString tmpFile;
+ if( KIO::NetAccess::download( url, tmpFile, 0 ) )
+ {
+ result = readFile( tmpFile );
+ KIO::NetAccess::removeTempFile( tmpFile );
+ }
+
+ return result;
+ }
+
+ QValueList<Mirror>
+ Sites::readFile(const QString& fileName)
+ {
+ QValueList<Mirror> result;
+
+ QFile f(fileName);
+ if (!f.open(IO_ReadOnly))
+ {
+ kdDebug(60010) << "Couldn't read: " << fileName << endl;
+ return result;
+ }
+
+ QTextStream ts(&f);
+
+ if (statusCode(ts.readLine()) != 210)
+ return result;
+
+ while (!ts.atEnd())
+ {
+ QString line = ts.readLine();
+ if (line == ".")
+ break;
+ result << parseLine(line);
+ }
+
+ return result;
+ }
+
+ Mirror
+ Sites::parseLine(const QString& line)
+ {
+ Mirror m;
+
+ QRegExp rexp("([^ ]+) (cddbp|http) (\\d+) ([^ ]+) [N|S]\\d{3}.\\d{2} [E|W]\\d{3}.\\d{2} (.*)");
+
+ if (rexp.search(line) != -1)
+ {
+ m.address = rexp.cap(1);
+
+ if (rexp.cap(2) == "cddbp")
+ m.transport = Lookup::CDDBP;
+ else
+ m.transport = Lookup::HTTP;
+
+ m.port = rexp.cap(3).toUInt();
+
+ if (m.transport == Lookup::HTTP && rexp.cap(4) != "/~cddb/cddb.cgi")
+ kdWarning() << "Non default urls are not supported for http" << endl;
+
+ m.description = rexp.cap(5);
+ }
+
+ return m;
+ }
+}
diff --git a/libkcddb/sites.h b/libkcddb/sites.h
new file mode 100644
index 00000000..3ef8ea05
--- /dev/null
+++ b/libkcddb/sites.h
@@ -0,0 +1,53 @@
+/*
+ Copyright ( C ) 2004 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or ( at your option ) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KCDDB_SITES_H
+#define KCDDB_SITES_H
+
+#include <qvaluelist.h>
+#include "cddb.h"
+#include "lookup.h"
+#include <kdelibs_export.h>
+
+namespace KCDDB
+{
+ class Mirror
+ {
+ public:
+ QString address;
+ Lookup::Transport transport;
+ uint port;
+ QString description;
+ } ;
+
+ class KDE_EXPORT Sites : public CDDB
+ {
+ public:
+ Sites();
+
+ QValueList<Mirror> siteList();
+ private:
+ QValueList<Mirror> readFile(const QString& fileName);
+ Mirror parseLine(const QString& line);
+ } ;
+}
+
+#endif
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/smtpsubmit.cpp b/libkcddb/smtpsubmit.cpp
new file mode 100644
index 00000000..463e53ee
--- /dev/null
+++ b/libkcddb/smtpsubmit.cpp
@@ -0,0 +1,60 @@
+/*
+ Copyright (C) 2003 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "smtpsubmit.h"
+#include <kdebug.h>
+#include <kio/job.h>
+
+namespace KCDDB
+{
+ SMTPSubmit::SMTPSubmit(const QString& hostname, uint port, const QString& username,
+ const QString& from, const QString& to)
+ : Submit(), from_(from), to_(to)
+ {
+ url_.setProtocol("smtp");
+ url_.setHost(hostname);
+ url_.setPort(port);
+ if (!username.isEmpty())
+ url_.setUser(username);
+ url_.setPath("/send");
+ }
+
+ SMTPSubmit::~SMTPSubmit()
+ {
+
+ }
+
+ KIO::Job* SMTPSubmit::createJob(const CDInfo& cdInfo)
+ {
+ url_.setQuery(QString("to=%1&subject=cddb %2 %3&from=%4")
+ .arg(to_, cdInfo.category, cdInfo.id, from_));
+ kdDebug(60010) << "Url is: " << url_.prettyURL() << endl;
+
+ return KIO::storedPut(diskData_.utf8(), url_, -1, false, false, false);
+ }
+
+ void SMTPSubmit::makeDiskData( const CDInfo& cdInfo, const TrackOffsetList& offsetList )
+ {
+ diskData_ = "Content-Type: text/plain; charset=\"utf-8\";";
+ diskData_ += "\n\n";
+
+ Submit::makeDiskData(cdInfo, offsetList);
+ }
+}
+
diff --git a/libkcddb/smtpsubmit.h b/libkcddb/smtpsubmit.h
new file mode 100644
index 00000000..ce126bbf
--- /dev/null
+++ b/libkcddb/smtpsubmit.h
@@ -0,0 +1,43 @@
+#ifndef SMTPSUBMIT_H
+#define SMTPSUBMIT_H
+/*
+ Copyright (C) 2003 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "submit.h"
+#include <kurl.h>
+
+namespace KCDDB
+{
+ class SMTPSubmit : public Submit
+ {
+ public:
+ SMTPSubmit(const QString& hostname, uint port, const QString& username, const QString& from, const QString& to);
+ virtual ~SMTPSubmit();
+
+ protected:
+ virtual void makeDiskData( const CDInfo&, const TrackOffsetList& );
+
+ virtual KIO::Job* createJob(const CDInfo& cdInfo);
+
+ KURL url_;
+ QString from_, to_;
+ } ;
+}
+
+#endif // SMTPSUBMIT_H
diff --git a/libkcddb/submit.cpp b/libkcddb/submit.cpp
new file mode 100644
index 00000000..8e5f38f0
--- /dev/null
+++ b/libkcddb/submit.cpp
@@ -0,0 +1,101 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ Copyright (C) 2003-2005 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "submit.h"
+#include <kdebug.h>
+
+namespace KCDDB
+{
+ Submit::Submit()
+ : CDDB()
+ {
+ // Empty.
+ }
+
+ Submit::~Submit()
+ {
+ // Empty.
+ }
+
+ CDDB::Result Submit::submit( CDInfo cdInfo, const TrackOffsetList& offsetList)
+ {
+ // If it was an inexact math from the server the discid might
+ // be different, so recalculate it
+ cdInfo.id = trackOffsetListToId(offsetList);
+
+ makeDiskData( cdInfo, offsetList );
+
+ if (!validCategory(cdInfo.category))
+ return InvalidCategory;
+
+ KIO::Job* job = createJob(cdInfo);
+
+ if (!job)
+ return UnknownError;
+
+ return runJob(job);
+ }
+
+ CDDB::Result
+ Submit::parseWrite( const QString & line )
+ {
+ uint serverStatus = statusCode( line );
+
+ if ( 320 != serverStatus )
+ return ServerError;
+
+ return Success;
+ }
+
+ void Submit::makeDiskData( const CDInfo& cdInfo, const TrackOffsetList& offsetList )
+ {
+ unsigned numTracks = cdInfo.trackInfoList.count();
+
+ diskData_ += "# xmcd\n";
+ diskData_ += "#\n";
+ diskData_ += "# Track frame offsets:\n";
+
+ for (uint i=0; i < numTracks; i++)
+ diskData_ += QString("#\t%1\n").arg(offsetList[i]);
+
+ int l = offsetList[numTracks+1]/75;
+ diskData_ += QString("# Disc length: %1 seconds\n").arg(l);
+
+ diskData_ += cdInfo.toString(true);
+
+ kdDebug(60010) << "diskData_ == " << diskData_ << endl;
+ }
+
+ bool Submit::validCategory( const QString& c )
+ {
+ QStringList validCategories;
+ validCategories << "blues" << "classical" << "country"
+ << "data" << "folk" << "jazz" << "misc" << "newage" << "reggae"
+ << "rock" << "soundtrack";
+
+ if (validCategories.contains(c))
+ return true;
+ else
+ return false;
+ }
+}
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/submit.h b/libkcddb/submit.h
new file mode 100644
index 00000000..8ec51056
--- /dev/null
+++ b/libkcddb/submit.h
@@ -0,0 +1,65 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ Copyright (C) 2002 Nadeem Hasan <nhasan@kde.org>
+ Copyright (C) 2005 Richard Lärkäng <nouseforaname@home.se>e>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KCDDB_SUBMIT_H
+#define KCDDB_SUBMIT_H
+
+#include "cddb.h"
+#include "cdinfo.h"
+#include <qobject.h>
+
+namespace KIO
+{
+ class Job;
+}
+
+namespace KCDDB
+{
+ class Submit : public CDDB, public QObject
+ {
+ public:
+
+ enum Transport
+ {
+ HTTP,
+ SMTP
+ };
+
+ Submit();
+ virtual ~Submit();
+
+ Result submit( CDInfo cdInfo, const TrackOffsetList &offsetList);
+
+ protected:
+ virtual KIO::Job* createJob(const CDInfo& cdInfo) = 0;
+ virtual Result runJob(KIO::Job* job) = 0;
+
+ bool validCategory(const QString&);
+
+ Result parseWrite( const QString & );
+ virtual void makeDiskData( const CDInfo&, const TrackOffsetList& );
+ QString diskData_;
+ };
+}
+
+#endif // KCDDB_SUBMIT_H
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/synccddbplookup.cpp b/libkcddb/synccddbplookup.cpp
new file mode 100644
index 00000000..30d28a87
--- /dev/null
+++ b/libkcddb/synccddbplookup.cpp
@@ -0,0 +1,222 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ Copyright (C) 2005 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <qstringlist.h>
+#include <kdebug.h>
+
+#include "synccddbplookup.h"
+
+namespace KCDDB
+{
+ SyncCDDBPLookup::SyncCDDBPLookup()
+ : CDDBPLookup()
+ {
+ }
+
+ SyncCDDBPLookup::~SyncCDDBPLookup()
+ {
+ // Empty.
+ }
+
+ CDDB::Result
+ SyncCDDBPLookup::lookup
+ (
+ const QString & hostName,
+ uint port,
+ const TrackOffsetList & trackOffsetList
+ )
+ {
+ if ( trackOffsetList.count() < 3 )
+ return UnknownError;
+
+ trackOffsetList_ = trackOffsetList;
+
+ socket_ = new KNetwork::KBufferedSocket(hostName, QString::number(port));
+ socket_->setTimeout( 30000 );
+ socket_->setOutputBuffering(false);
+
+ Result result;
+
+ // Connect to server.
+ result = connect();
+ if ( Success != result )
+ return result;
+
+ // Try a handshake.
+ result = shakeHands();
+ if ( Success != result )
+ return result;
+
+ // Run a query.
+ result = runQuery();
+ if ( Success != result )
+ return result;
+
+ if (matchList_.isEmpty())
+ return NoRecordFound;
+
+ kdDebug(60010) << matchList_.count() << " matches found." << endl;
+
+ // For each match, read the cd info from the server and save it to
+ // cdInfoList.
+ CDDBMatchList::ConstIterator matchIt = matchList_.begin();
+
+ while ( matchIt != matchList_.end() )
+ {
+ CDDBMatch match( *matchIt );
+ result = matchToCDInfo( match );
+ ++matchIt;
+ }
+
+ sendQuit();
+
+ close();
+
+ return Success;
+ }
+
+ CDDB::Result
+ SyncCDDBPLookup::connect()
+ {
+ kdDebug(60010) << "Trying to connect to " << endl;
+
+ if ( !socket_->lookup() )
+ return HostNotFound;
+
+ socket_->peerResolver().wait();
+
+ if ( socket_->state() != KNetwork::KClientSocketBase::HostFound )
+ return HostNotFound;
+
+ if ( !socket_->connect() )
+ return ServerError;
+
+ socket_->waitForConnect();
+
+ return isConnected() ? Success : ServerError;
+ }
+
+ CDDB::Result
+ SyncCDDBPLookup::shakeHands()
+ {
+ QString line = readLine();
+
+ if ( !parseGreeting( line ) )
+ return ServerError;
+
+ sendHandshake();
+
+ line = readLine();
+
+ if ( !parseHandshake( line ) )
+ return ServerError;
+
+ sendProto();
+
+ // Ignore the response for now
+ readLine();
+
+ return Success;
+ }
+
+ CDDB::Result
+ SyncCDDBPLookup::runQuery()
+ {
+ Result result;
+
+ sendQuery();
+
+ QString line = readLine();
+ result = parseQuery( line );
+
+ if ( ServerError == result )
+ return ServerError;
+
+ if ( MultipleRecordFound == result )
+ {
+ // We have multiple matches
+ line = readLine();
+
+ while (!line.startsWith(".") && !line.isNull() )
+ {
+ parseExtraMatch( line );
+ line = readLine();
+ }
+ }
+
+ return Success;
+ }
+
+ CDDB::Result
+ SyncCDDBPLookup::matchToCDInfo( const CDDBMatch & match )
+ {
+ sendRead( match );
+
+ QString line = readLine();
+
+ Result result = parseRead( line );
+ if ( Success != result )
+ return result;
+
+ QStringList lineList;
+ line = readLine();
+
+ while ( !line.startsWith(".") && !line.isNull() )
+ {
+ lineList.append( line );
+ line = readLine();
+ }
+
+ CDInfo info;
+
+ if ( info.load( lineList ) )
+ {
+ info.category = category_;
+ cdInfoList_.append( info );
+ }
+
+ return Success;
+ }
+
+ QString
+ SyncCDDBPLookup::readLine()
+ {
+ if ( !isConnected() )
+ {
+ kdDebug(60010) << "socket status: " << socket_->state() << endl;
+ return QString::null;
+ }
+
+ if (!socket_->canReadLine())
+ {
+ bool timeout;
+
+ socket_->waitForMore(-1,&timeout);
+
+ if (timeout)
+ return QString::null;
+ }
+
+ return QString::fromUtf8(socket_->readLine());
+ }
+}
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/synccddbplookup.h b/libkcddb/synccddbplookup.h
new file mode 100644
index 00000000..cef5cec9
--- /dev/null
+++ b/libkcddb/synccddbplookup.h
@@ -0,0 +1,51 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+ Copyright (C) 2002 Nadeem Hasan <nhasan@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KCDDB_SYNC_CDDBP_LOOKUP_H
+#define KCDDB_SYNC_CDDBP_LOOKUP_H
+
+#include "cddbplookup.h"
+
+namespace KCDDB
+{
+ class SyncCDDBPLookup : public CDDBPLookup
+ {
+ public:
+
+ SyncCDDBPLookup();
+ virtual ~SyncCDDBPLookup();
+
+ Result lookup( const QString &, uint, const TrackOffsetList & );
+
+ CDInfoList lookupResponse() const;
+
+ protected:
+ Result connect();
+ Result shakeHands();
+ Result runQuery();
+ Result matchToCDInfo( const CDDBMatch & );
+
+ QString readLine();
+ };
+}
+
+#endif // KCDDB_SYNC_CDDBP_LOOKUP_H
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/synchttplookup.cpp b/libkcddb/synchttplookup.cpp
new file mode 100644
index 00000000..ff0de12a
--- /dev/null
+++ b/libkcddb/synchttplookup.cpp
@@ -0,0 +1,131 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <qstringlist.h>
+#include <qapplication.h>
+
+#include <kdebug.h>
+#include <kio/job.h>
+#include <kio/netaccess.h>
+
+#include "synchttplookup.h"
+
+namespace KCDDB
+{
+ SyncHTTPLookup::SyncHTTPLookup()
+ : HTTPLookup()
+ {
+ }
+
+ SyncHTTPLookup::~SyncHTTPLookup()
+ {
+ // Empty.
+ }
+
+ CDDB::Result
+ SyncHTTPLookup::lookup
+ (
+ const QString & hostName,
+ uint port,
+ const TrackOffsetList & trackOffsetList
+ )
+ {
+ if ( trackOffsetList.count() < 3 )
+ return UnknownError;
+
+ trackOffsetList_ = trackOffsetList;
+
+ initURL( hostName, port );
+
+ // Run a query.
+ result_ = runQuery();
+
+ if ( Success != result_ )
+ return result_;
+
+ kdDebug(60010) << matchList_.count() << " matches found." << endl;
+
+ if (matchList_.isEmpty())
+ return NoRecordFound;
+
+ // For each match, read the cd info from the server and save it to
+ // cdInfoList.
+ CDDBMatchList::ConstIterator matchIt = matchList_.begin();
+
+ while ( matchIt != matchList_.end() )
+ {
+ CDDBMatch match( *matchIt );
+ result_ = matchToCDInfo( match );
+ ++matchIt;
+ }
+
+ return result_;
+ }
+
+ CDDB::Result
+ SyncHTTPLookup::runQuery()
+ {
+ data_ = QByteArray();
+ state_ = WaitingForQueryResponse;
+
+ result_ = sendQuery();
+
+ if ( Success != result_ )
+ return result_;
+
+ kdDebug(60010) << "runQuery() Result: " << resultToString(result_) << endl;
+
+ return result_;
+ }
+
+ CDDB::Result
+ SyncHTTPLookup::matchToCDInfo( const CDDBMatch & match )
+ {
+ data_ = QByteArray();
+ state_ = WaitingForReadResponse;
+
+ result_ = sendRead( match );
+
+ if ( Success != result_ )
+ return result_;
+
+ return result_;
+ }
+
+ CDDB::Result
+ SyncHTTPLookup::fetchURL()
+ {
+ kdDebug(60010) << "About to fetch: " << cgiURL_.url() << endl;
+
+ KIO::TransferJob* job = KIO::get( cgiURL_, false, false );
+
+ if ( 0 == job )
+ return ServerError;
+
+ if (!KIO::NetAccess::synchronousRun(job, 0, &data_))
+ return ServerError;
+
+ jobFinished();
+
+ return Success;
+ }
+}
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/synchttplookup.h b/libkcddb/synchttplookup.h
new file mode 100644
index 00000000..730577e4
--- /dev/null
+++ b/libkcddb/synchttplookup.h
@@ -0,0 +1,50 @@
+/*
+ Copyright (C) 2002 Rik Hemsley (rikkus) <rik@kde.org>
+ Copyright (C) 2002 Benjamin Meyer <ben-devel@meyerhome.net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KCDDB_SYNC_HTTP_LOOKUP_H
+#define KCDDB_SYNC_HTTP_LOOKUP_H
+
+#include "httplookup.h"
+
+namespace KCDDB
+{
+ class SyncHTTPLookup : public HTTPLookup
+ {
+ public:
+
+ SyncHTTPLookup();
+ virtual ~SyncHTTPLookup();
+
+ Result lookup( const QString &, uint, const TrackOffsetList & );
+
+ CDInfoList lookupResponse() const;
+
+ protected:
+
+ virtual Result fetchURL();
+
+ Result runQuery();
+ Result matchToCDInfo( const CDDBMatch & );
+ };
+}
+
+#endif // KCDDB_SYNC_HTTP_LOOKUP_H
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/synchttpsubmit.cpp b/libkcddb/synchttpsubmit.cpp
new file mode 100644
index 00000000..b6e347fe
--- /dev/null
+++ b/libkcddb/synchttpsubmit.cpp
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 2003 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "synchttpsubmit.h"
+#include <kio/netaccess.h>
+#include <kio/job.h>
+
+namespace KCDDB
+{
+ SyncHTTPSubmit::SyncHTTPSubmit(const QString& from, const QString& hostname, uint port)
+ : HTTPSubmit(from, hostname, port)
+ {
+
+ }
+
+ SyncHTTPSubmit::~SyncHTTPSubmit()
+ {
+
+ }
+
+ CDDB::Result SyncHTTPSubmit::runJob(KIO::Job* job)
+ {
+ bool success = KIO::NetAccess::synchronousRun(job, 0);
+
+ if (success)
+ return CDDB::Success;
+ else
+ return CDDB::UnknownError;
+ }
+}
diff --git a/libkcddb/synchttpsubmit.h b/libkcddb/synchttpsubmit.h
new file mode 100644
index 00000000..5dbb992e
--- /dev/null
+++ b/libkcddb/synchttpsubmit.h
@@ -0,0 +1,37 @@
+#ifndef SYNCHTTPSUBMIT_H
+#define SYNCHTTPSUBMIT_H
+/*
+ Copyright (C) 2003 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "httpsubmit.h"
+
+namespace KCDDB
+{
+ class SyncHTTPSubmit : public HTTPSubmit
+ {
+ public:
+ SyncHTTPSubmit(const QString& from, const QString& hostname, uint port);
+ virtual ~SyncHTTPSubmit();
+
+ protected:
+ virtual Result runJob(KIO::Job* job);
+ } ;
+}
+
+#endif // SYNCHTTPSUBMIT_H
diff --git a/libkcddb/syncsmtpsubmit.cpp b/libkcddb/syncsmtpsubmit.cpp
new file mode 100644
index 00000000..f5761277
--- /dev/null
+++ b/libkcddb/syncsmtpsubmit.cpp
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 2003-2004 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "syncsmtpsubmit.h"
+#include "cdinfo.h"
+#include <kio/netaccess.h>
+#include <kio/job.h>
+
+namespace KCDDB
+{
+ SyncSMTPSubmit::SyncSMTPSubmit(const QString& hostname, uint port,
+ const QString& username, const QString& from, const QString& to)
+ : SMTPSubmit( hostname, port, username, from, to )
+ {
+
+ }
+
+ SyncSMTPSubmit::~SyncSMTPSubmit()
+ {
+
+ }
+
+ CDDB::Result SyncSMTPSubmit::runJob(KIO::Job* job)
+ {
+ if ( KIO::NetAccess::synchronousRun(job, 0) )
+ return Success;
+
+ return UnknownError;
+ }
+}
diff --git a/libkcddb/syncsmtpsubmit.h b/libkcddb/syncsmtpsubmit.h
new file mode 100644
index 00000000..959d7d94
--- /dev/null
+++ b/libkcddb/syncsmtpsubmit.h
@@ -0,0 +1,38 @@
+#ifndef SYNCSMTPSUBMIT_H
+#define SYNCSMTPSUBMIT_H
+/*
+ Copyright (C) 2003 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "smtpsubmit.h"
+
+namespace KCDDB
+{
+ class SyncSMTPSubmit : public SMTPSubmit
+ {
+ public:
+ SyncSMTPSubmit(const QString& hostname, uint port, const QString& username,
+ const QString& from, const QString& to);
+ virtual ~SyncSMTPSubmit();
+ protected:
+ virtual Result runJob(KIO::Job* job);
+ } ;
+}
+
+#endif // SYNCSMTPSUBMIT_H
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
diff --git a/libkcddb/test/Makefile.am b/libkcddb/test/Makefile.am
new file mode 100644
index 00000000..ab814db4
--- /dev/null
+++ b/libkcddb/test/Makefile.am
@@ -0,0 +1,48 @@
+INCLUDES = -I$(top_srcdir) -I$(top_builddir)/libkcddb $(all_includes)
+
+check_PROGRAMS = syncsmtpsubmittest asyncsmtpsubmittest asynchttplookuptest \
+ asynccddblookuptest synccddblookuptest synchttplookuptest \
+ asynchttpsubmittest synchttpsubmittest sitestest utf8test
+
+syncsmtpsubmittest_SOURCES = syncsmtpsubmittest.cpp
+syncsmtpsubmittest_LDFLAGS = $(all_libraries)
+syncsmtpsubmittest_LDADD = $(top_builddir)/libkcddb/libkcddb.la
+
+asyncsmtpsubmittest_SOURCES = asyncsmtpsubmittest.cpp
+asyncsmtpsubmittest_LDFLAGS = $(all_libraries)
+asyncsmtpsubmittest_LDADD = $(top_builddir)/libkcddb/libkcddb.la
+
+asynchttplookuptest_SOURCES = asynchttplookuptest.cpp
+asynchttplookuptest_LDFLAGS = $(all_libraries)
+asynchttplookuptest_LDADD = $(top_builddir)/libkcddb/libkcddb.la
+
+asynccddblookuptest_SOURCES = asynccddblookuptest.cpp
+asynccddblookuptest_LDFLAGS = $(all_libraries)
+asynccddblookuptest_LDADD = $(top_builddir)/libkcddb/libkcddb.la
+
+synchttplookuptest_SOURCES = synchttplookuptest.cpp
+synchttplookuptest_LDFLAGS = $(all_libraries)
+synchttplookuptest_LDADD = $(top_builddir)/libkcddb/libkcddb.la
+
+synccddblookuptest_SOURCES = synccddblookuptest.cpp
+synccddblookuptest_LDFLAGS = $(all_libraries)
+synccddblookuptest_LDADD = $(top_builddir)/libkcddb/libkcddb.la
+
+synchttpsubmittest_SOURCES = synchttpsubmittest.cpp
+synchttpsubmittest_LDFLAGS = $(all_libraries)
+synchttpsubmittest_LDADD = $(top_builddir)/libkcddb/libkcddb.la
+
+asynchttpsubmittest_SOURCES = asynchttpsubmittest.cpp
+asynchttpsubmittest_LDFLAGS = $(all_libraries)
+asynchttpsubmittest_LDADD = $(top_builddir)/libkcddb/libkcddb.la
+
+utf8test_SOURCES = utf8test.cpp
+utf8test_LDFLAGS = $(all_libraries)
+utf8test_LDADD = $(top_builddir)/libkcddb/libkcddb.la
+
+sitestest_SOURCES = sitestest.cpp
+sitestest_LDFLAGS = $(all_libraries)
+sitestest_LDADD = $(top_builddir)/libkcddb/libkcddb.la
+
+METASOURCES = AUTO
+
diff --git a/libkcddb/test/asynccddblookuptest.cpp b/libkcddb/test/asynccddblookuptest.cpp
new file mode 100644
index 00000000..afbd326a
--- /dev/null
+++ b/libkcddb/test/asynccddblookuptest.cpp
@@ -0,0 +1,113 @@
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+
+#include "asynccddblookuptest.h"
+
+#include "libkcddb/cache.h"
+#include "libkcddb/lookup.h"
+
+AsyncCDDBLookupTest::AsyncCDDBLookupTest()
+ : QObject()
+{
+ using namespace KCDDB;
+
+ client_ = new Client;
+ client_->config().setHostname("freedb.freedb.org");
+ client_->config().setPort(8880);
+ client_->config().setCachePolicy(Cache::Ignore);
+ client_->config().setLookupTransport(Lookup::CDDBP);
+ client_->setBlockingMode( false );
+
+ connect
+ (
+ client_,
+ SIGNAL(finished(CDDB::Result)),
+ SLOT(slotFinished(CDDB::Result))
+ );
+
+ TrackOffsetList list;
+
+ // a1107d0a - Kruder & Dorfmeister - The K&D Sessions - Disc One.
+ list
+ << 150 // First track start.
+ << 29462
+ << 66983
+ << 96785
+ << 135628
+ << 168676
+ << 194147
+ << 222158
+ << 247076
+ << 278203 // Last track start.
+ << 10 // Disc start.
+ << 316732; // Disc end.
+
+ client_->lookup(list);
+}
+
+AsyncCDDBLookupTest::~AsyncCDDBLookupTest()
+{
+ delete client_;
+}
+
+ void
+AsyncCDDBLookupTest::slotFinished(CDDB::Result r)
+{
+ kdDebug() << "AsyncCDDBLookupTest::slotResult: Got " << KCDDB::CDDB::resultToString(r) << endl;
+
+ CDInfoList l = client_->lookupResponse();
+
+ kdDebug() << "AsyncCDDBLookupTest::slotResult: Item count: " << l.count() << endl;
+
+ for (CDInfoList::ConstIterator it(l.begin()); it != l.end(); ++it)
+ {
+ CDInfo i(*it);
+
+ kdDebug() << "Disc artist: `" << i.artist << "'" << endl;
+ kdDebug() << "Disc title: `" << i.title << "'" << endl;
+ kdDebug() << "Disc revision: `" << i.revision << "'" << endl;
+ }
+
+ if (!l.isEmpty())
+ {
+ kdDebug() << "---------------------------------------" << endl;
+ kdDebug() << "Showing first item" << endl;
+
+ CDInfo i(l.first());
+
+ kdDebug() << "Disc artist: `" << i.artist << "'" << endl;
+ kdDebug() << "Disc title: `" << i.title << "'" << endl;
+ kdDebug() << "Disc genre: `" << i.genre << "'" << endl;
+ kdDebug() << "Disc year: `" << i.year << "'" << endl;
+ kdDebug() << "Disc length: `" << i.length << "'" << endl;
+ kdDebug() << "Disc id: `" << i.id << "'" << endl;
+ kdDebug() << "Tracks........" << endl;
+
+ for (TrackInfoList::ConstIterator it(i.trackInfoList.begin()); it != i.trackInfoList.end(); ++it)
+ {
+ kdDebug() << " Track: `" << (*it).title << "'" << endl;
+ }
+ kdDebug() << "---------------------------------------" << endl;
+ }
+
+ CDInfo i(client_->bestLookupResponse());
+
+ kdDebug() << "Best CDInfo had title: " << i.title << endl;
+ kdDebug() << "and revision: " << i.revision << endl;
+
+ kapp->quit();
+}
+
+int main(int argc, char ** argv)
+{
+ KCmdLineArgs::init(argc, argv, "libkcddb_test", "", "", "");
+
+ KApplication app(false /* No styles */, false /* No GUI */);
+
+ AsyncCDDBLookupTest test;
+
+ return app.exec();
+}
+
+#include "asynccddblookuptest.moc"
diff --git a/libkcddb/test/asynccddblookuptest.h b/libkcddb/test/asynccddblookuptest.h
new file mode 100644
index 00000000..5e0a3efb
--- /dev/null
+++ b/libkcddb/test/asynccddblookuptest.h
@@ -0,0 +1,27 @@
+#ifndef TEST_H
+#define TEST_H
+
+#include <qobject.h>
+#include <libkcddb/client.h>
+
+using namespace KCDDB;
+
+class AsyncCDDBLookupTest : public QObject
+{
+ Q_OBJECT
+
+ public:
+
+ AsyncCDDBLookupTest();
+ ~AsyncCDDBLookupTest();
+
+ public slots:
+
+ void slotFinished(CDDB::Result);
+
+ private:
+
+ KCDDB::Client * client_;
+};
+
+#endif
diff --git a/libkcddb/test/asynchttplookuptest.cpp b/libkcddb/test/asynchttplookuptest.cpp
new file mode 100644
index 00000000..610d9fab
--- /dev/null
+++ b/libkcddb/test/asynchttplookuptest.cpp
@@ -0,0 +1,111 @@
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+
+#include "asynchttplookuptest.h"
+#include "libkcddb/cache.h"
+#include "libkcddb/lookup.h"
+
+AsyncHTTPLookupTest::AsyncHTTPLookupTest()
+ : QObject()
+{
+ using namespace KCDDB;
+
+ client_ = new Client;
+ client_->config().setHostname("freedb.freedb.org");
+ client_->config().setPort(80);
+ client_->config().setCachePolicy(Cache::Ignore);
+ client_->config().setLookupTransport(Lookup::HTTP);
+ client_->setBlockingMode( false );
+
+ connect
+ (
+ client_,
+ SIGNAL(finished(CDDB::Result)),
+ SLOT(slotFinished(CDDB::Result))
+ );
+
+ TrackOffsetList list;
+
+ // a1107d0a - Kruder & Dorfmeister - The K&D Sessions - Disc One.
+ list
+ << 150 // First track start.
+ << 29462
+ << 66983
+ << 96785
+ << 135628
+ << 168676
+ << 194147
+ << 222158
+ << 247076
+ << 278203 // Last track start.
+ << 10 // Disc start.
+ << 316732; // Disc end.
+
+ client_->lookup(list);
+}
+
+AsyncHTTPLookupTest::~AsyncHTTPLookupTest()
+{
+ delete client_;
+}
+
+ void
+AsyncHTTPLookupTest::slotFinished(CDDB::Result r)
+{
+ kdDebug() << "AsyncHTTPLookupTest::slotFinished: Got " << KCDDB::CDDB::resultToString(r) << endl;
+
+ CDInfoList l = client_->lookupResponse();
+
+ kdDebug() << "AsyncHTTPLookupTest::slotFinished: Item count: " << l.count() << endl;
+
+ for (CDInfoList::ConstIterator it(l.begin()); it != l.end(); ++it)
+ {
+ CDInfo i(*it);
+
+ kdDebug() << "Disc artist: `" << i.artist << "'" << endl;
+ kdDebug() << "Disc title: `" << i.title << "'" << endl;
+ kdDebug() << "Disc revision: `" << i.revision << "'" << endl;
+ }
+
+ if (!l.isEmpty())
+ {
+ kdDebug() << "---------------------------------------" << endl;
+ kdDebug() << "Showing first item" << endl;
+
+ CDInfo i(l.first());
+
+ kdDebug() << "Disc artist: `" << i.artist << "'" << endl;
+ kdDebug() << "Disc title: `" << i.title << "'" << endl;
+ kdDebug() << "Disc genre: `" << i.genre << "'" << endl;
+ kdDebug() << "Disc year: `" << i.year << "'" << endl;
+ kdDebug() << "Disc length: `" << i.length << "'" << endl;
+ kdDebug() << "Disc id: `" << i.id << "'" << endl;
+ kdDebug() << "Tracks........" << endl;
+
+ for (TrackInfoList::ConstIterator it(i.trackInfoList.begin()); it != i.trackInfoList.end(); ++it)
+ {
+ kdDebug() << " Track: `" << (*it).title << "'" << endl;
+ }
+ kdDebug() << "---------------------------------------" << endl;
+ }
+ CDInfo i( client_->bestLookupResponse() );
+
+ kdDebug() << "Best CDInfo had title: " << i.title << endl;
+ kdDebug() << "and revision: " << i.revision << endl;
+
+ kapp->quit();
+}
+
+int main(int argc, char ** argv)
+{
+ KCmdLineArgs::init(argc, argv, "libkcddb_test", "", "", "");
+
+ KApplication app(false /* No styles */, false /* No GUI */);
+
+ AsyncHTTPLookupTest a;
+
+ return app.exec();
+}
+
+#include "asynchttplookuptest.moc"
diff --git a/libkcddb/test/asynchttplookuptest.h b/libkcddb/test/asynchttplookuptest.h
new file mode 100644
index 00000000..c8e3e86c
--- /dev/null
+++ b/libkcddb/test/asynchttplookuptest.h
@@ -0,0 +1,27 @@
+#ifndef TEST_H
+#define TEST_H
+
+#include <qobject.h>
+#include <libkcddb/client.h>
+
+using namespace KCDDB;
+
+class AsyncHTTPLookupTest : public QObject
+{
+ Q_OBJECT
+
+ public:
+
+ AsyncHTTPLookupTest();
+ ~AsyncHTTPLookupTest();
+
+ public slots:
+
+ void slotFinished(CDDB::Result);
+
+ private:
+
+ KCDDB::Client * client_;
+};
+
+#endif
diff --git a/libkcddb/test/asynchttpsubmittest.cpp b/libkcddb/test/asynchttpsubmittest.cpp
new file mode 100644
index 00000000..9981f8a7
--- /dev/null
+++ b/libkcddb/test/asynchttpsubmittest.cpp
@@ -0,0 +1,80 @@
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+
+#include "asynchttpsubmittest.h"
+#include "libkcddb/submit.h"
+
+AsyncHTTPSubmitTest::AsyncHTTPSubmitTest()
+ : QObject()
+{
+ using namespace KCDDB;
+
+ client_ = new Client;
+ client_->config().setSubmitTransport(Submit::HTTP);
+ client_->setBlockingMode( false );
+
+ TrackOffsetList list;
+
+ list
+ << 150 // First track start.
+ << 2592
+ << 35472
+ << 47891
+ << 123310
+ << 150 // Disc start.
+ << 133125; // Disc end.
+
+ CDInfo cdInfo;
+
+ cdInfo.id = "3606ed05";
+ cdInfo.revision = 4;
+ cdInfo.title = "Bamse och Bronto";
+ cdInfo.artist = "Musiksage";
+ cdInfo.year = 2001;
+ cdInfo.category = "misc";
+ cdInfo.genre = "Barnsaga";
+ cdInfo.extd = QString::fromUtf8("Berättare: Olof Thunberg");
+
+ TrackInfo info;
+ info.title = "Bamses signaturmelodi";
+ cdInfo.trackInfoList.append(info);
+ info.title = "*";
+ cdInfo.trackInfoList.append(info);
+ info.title = "Brummavisan";
+ cdInfo.trackInfoList.append(info);
+ info.title = "*";
+ cdInfo.trackInfoList.append(info);
+ info.title = QString::fromUtf8("Jätteödlan Bronto");
+ cdInfo.trackInfoList.append(info);
+
+ connect
+ (
+ client_,
+ SIGNAL(finished(CDDB::Result)),
+ SLOT(slotFinished(CDDB::Result))
+ );
+
+ client_->submit(cdInfo, list);
+}
+
+ void
+AsyncHTTPSubmitTest::slotFinished(CDDB::Result r)
+{
+ kdDebug() << "AsyncHTTPSubmitTest::slotFinished: Got " << KCDDB::CDDB::resultToString(r) << endl;
+
+ kapp->quit();
+}
+
+int main(int argc, char ** argv)
+{
+ KCmdLineArgs::init(argc, argv, "libkcddb_test", "", "", "");
+
+ KApplication app(false /* No styles */, false /* No GUI */);
+
+ new AsyncHTTPSubmitTest;
+
+ return app.exec();
+}
+
+#include "asynchttpsubmittest.moc"
diff --git a/libkcddb/test/asynchttpsubmittest.h b/libkcddb/test/asynchttpsubmittest.h
new file mode 100644
index 00000000..27053836
--- /dev/null
+++ b/libkcddb/test/asynchttpsubmittest.h
@@ -0,0 +1,25 @@
+#ifndef TEST_H
+#define TEST_H
+
+#include <qobject.h>
+#include <libkcddb/client.h>
+
+using namespace KCDDB;
+
+class AsyncHTTPSubmitTest : public QObject
+{
+ Q_OBJECT
+
+ public:
+ AsyncHTTPSubmitTest();
+
+ public slots:
+
+ void slotFinished(CDDB::Result);
+
+ private:
+
+ KCDDB::Client * client_;
+};
+
+#endif
diff --git a/libkcddb/test/asyncsmtpsubmittest.cpp b/libkcddb/test/asyncsmtpsubmittest.cpp
new file mode 100644
index 00000000..224288e3
--- /dev/null
+++ b/libkcddb/test/asyncsmtpsubmittest.cpp
@@ -0,0 +1,81 @@
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+
+#include "asyncsmtpsubmittest.h"
+#include "libkcddb/submit.h"
+
+AsyncSMTPSubmitTest::AsyncSMTPSubmitTest()
+ : QObject()
+{
+ using namespace KCDDB;
+
+ client_ = new Client;
+ client_->config().setSubmitTransport(Submit::SMTP);
+ client_->config().setSubmitAddress("test-submit@freedb.org");
+ client_->setBlockingMode( false );
+
+ TrackOffsetList list;
+
+ list
+ << 150 // First track start.
+ << 2592
+ << 35472
+ << 47891
+ << 123310
+ << 150 // Disc start.
+ << 133125; // Disc end.
+
+ CDInfo cdInfo;
+
+ cdInfo.id = "3606ed05";
+ cdInfo.revision = 4;
+ cdInfo.title = "Bamse och Bronto";
+ cdInfo.artist = "Musiksage";
+ cdInfo.year = 2001;
+ cdInfo.category = "misc";
+ cdInfo.genre = "Barnsaga";
+ cdInfo.extd = QString::fromUtf8("Berättare: Olof Thunberg");
+
+ TrackInfo info;
+ info.title = "Bamses signaturmelodi";
+ cdInfo.trackInfoList.append(info);
+ info.title = "*";
+ cdInfo.trackInfoList.append(info);
+ info.title = "Brummavisan";
+ cdInfo.trackInfoList.append(info);
+ info.title = "*";
+ cdInfo.trackInfoList.append(info);
+ info.title = QString::fromUtf8("Jätteödlan Bronto");
+ cdInfo.trackInfoList.append(info);
+
+ connect
+ (
+ client_,
+ SIGNAL(finished(CDDB::Result)),
+ SLOT(slotFinished(CDDB::Result))
+ );
+
+ client_->submit(cdInfo, list);
+}
+
+ void
+AsyncSMTPSubmitTest::slotFinished(CDDB::Result r)
+{
+ kdDebug() << "AsyncSMTPSubmitTest::slotFinished: Got " << KCDDB::CDDB::resultToString(r) << endl;
+
+ kapp->quit();
+}
+
+int main(int argc, char ** argv)
+{
+ KCmdLineArgs::init(argc, argv, "libkcddb_test", "", "", "");
+
+ KApplication app(false /* No styles */, false /* No GUI */);
+
+ new AsyncSMTPSubmitTest;
+
+ return app.exec();
+}
+
+#include "asyncsmtpsubmittest.moc"
diff --git a/libkcddb/test/asyncsmtpsubmittest.h b/libkcddb/test/asyncsmtpsubmittest.h
new file mode 100644
index 00000000..3528798d
--- /dev/null
+++ b/libkcddb/test/asyncsmtpsubmittest.h
@@ -0,0 +1,25 @@
+#ifndef TEST_H
+#define TEST_H
+
+#include <qobject.h>
+#include <libkcddb/client.h>
+
+using namespace KCDDB;
+
+class AsyncSMTPSubmitTest : public QObject
+{
+ Q_OBJECT
+
+ public:
+ AsyncSMTPSubmitTest();
+
+ public slots:
+
+ void slotFinished(CDDB::Result);
+
+ private:
+
+ KCDDB::Client * client_;
+};
+
+#endif
diff --git a/libkcddb/test/sitestest.cpp b/libkcddb/test/sitestest.cpp
new file mode 100644
index 00000000..3876ae2c
--- /dev/null
+++ b/libkcddb/test/sitestest.cpp
@@ -0,0 +1,47 @@
+/*
+ Copyright (C) 2003 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+
+#include "libkcddb/sites.h"
+
+ int
+main(int argc, char ** argv)
+{
+ KCmdLineArgs::init(argc, argv, "libkcddb_test", "", "", "");
+
+ KApplication app(false /* No styles */);
+
+ using namespace KCDDB;
+
+ Sites s;
+
+ kdDebug() << "Sites: " << endl;
+
+ QValueList<Mirror> sites = s.siteList();
+ for (QValueList<Mirror>::Iterator it = sites.begin(); it != sites.end(); ++it)
+ if ((*it).transport == Lookup::CDDBP)
+ kdDebug() << (*it).address << " CDDBP " << (*it).port << " " << (*it).description << endl;
+ else
+ kdDebug() << (*it).address << " HTTP " << (*it).port << " " << (*it).description << endl;
+
+ return 0;
+}
diff --git a/libkcddb/test/synccddblookuptest.cpp b/libkcddb/test/synccddblookuptest.cpp
new file mode 100644
index 00000000..0b71e864
--- /dev/null
+++ b/libkcddb/test/synccddblookuptest.cpp
@@ -0,0 +1,84 @@
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kdebug.h>
+
+#include "libkcddb/client.h"
+#include "libkcddb/cache.h"
+#include "libkcddb/lookup.h"
+
+
+ int
+main(int argc, char ** argv)
+{
+ KCmdLineArgs::init(argc, argv, "libkcddb_test", "", "", "");
+
+ KApplication app(false /* No styles */, false /* No GUI */);
+
+ using namespace KCDDB;
+
+ Client c;
+ c.config().setHostname("freedb.freedb.org");
+ c.config().setPort(8880);
+ c.config().setCachePolicy(Cache::Ignore);
+ c.config().setLookupTransport(Lookup::CDDBP);
+
+ TrackOffsetList list;
+
+ // a1107d0a - Kruder & Dorfmeister - The K&D Sessions - Disc One.
+// list
+// << 150 // First track start.
+// << 29462
+// << 66983
+// << 96785
+// << 135628
+// << 168676
+// << 194147
+// << 222158
+// << 247076
+// << 278203 // Last track start.
+// << 10 // Disc start.
+// << 316732; // Disc end.
+ list
+ << 150
+ << 106965
+ << 127220
+ << 151925
+ << 176085
+ << 5
+ << 234500;
+
+ kdDebug() << "Stuff to send to server:" << endl;
+
+ kdDebug()
+ << CDDB::trackOffsetListToId(list)
+ << " "
+ //<< trackOffsetListToString(list)
+ << endl;
+
+ CDDB::Result r = c.lookup(list);
+
+ kdDebug() << "Client::lookup gave : " << CDDB::resultToString(r) << endl;
+
+ CDInfoList response = c.lookupResponse();
+
+ kdDebug() << "Client::lookup returned : " << response.count() << " entries"
+ << endl;
+
+ CDInfoList::ConstIterator it;
+
+ for (it = response.begin(); it != response.end(); ++it)
+ {
+ CDInfo i(*it);
+
+ kdDebug() << "Disc title: " << i.title << endl;
+ kdDebug() << "Total tracks: " << i.trackInfoList.count() << endl;
+ kdDebug() << "Disc revision: `" << i.revision << "'" << endl;
+ }
+
+ CDInfo i( c.bestLookupResponse() );
+
+ kdDebug() << "Best CDInfo had title: " << i.title << endl;
+ kdDebug() << "and revision: " << i.revision << endl;
+
+ return 0;
+}
diff --git a/libkcddb/test/synchttplookuptest.cpp b/libkcddb/test/synchttplookuptest.cpp
new file mode 100644
index 00000000..8bc05c18
--- /dev/null
+++ b/libkcddb/test/synchttplookuptest.cpp
@@ -0,0 +1,84 @@
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kdebug.h>
+
+#include "libkcddb/client.h"
+#include "libkcddb/cache.h"
+#include "libkcddb/lookup.h"
+
+ int
+main(int argc, char ** argv)
+{
+ KCmdLineArgs::init(argc, argv, "libkcddb_test", "", "", "");
+
+ KApplication app(false /* No styles */);
+
+ using namespace KCDDB;
+
+ Client c;
+ c.config().setHostname("freedb.freedb.org");
+ c.config().setPort(80);
+ c.config().setCachePolicy(Cache::Ignore);
+ c.config().setLookupTransport(Lookup::HTTP);
+
+
+ TrackOffsetList list;
+
+ // a1107d0a - Kruder & Dorfmeister - The K&D Sessions - Disc One.
+// list
+// << 150 // First track start.
+// << 29462
+// << 66983
+// << 96785
+// << 135628
+// << 168676
+// << 194147
+// << 222158
+// << 247076
+// << 278203 // Last track start.
+// << 10 // Disc start.
+// << 316732; // Disc end.
+ list
+ << 150
+ << 106965
+ << 127220
+ << 151925
+ << 176085
+ << 5
+ << 234500;
+
+ kdDebug() << "Stuff to send to server:" << endl;
+
+ kdDebug()
+ << CDDB::trackOffsetListToId(list)
+ << " "
+ //<< trackOffsetListToString(list)
+ << endl;
+
+ CDDB::Result r = c.lookup(list);
+
+ kdDebug() << "Client::lookup gave : " << CDDB::resultToString(r) << endl;
+
+ CDInfoList response = c.lookupResponse();
+
+ kdDebug() << "Client::lookup returned : " << response.count() << " entries"
+ << endl;
+
+ CDInfoList::ConstIterator it;
+
+ for (it = response.begin(); it != response.end(); ++it)
+ {
+ CDInfo i(*it);
+
+ kdDebug() << "Disc title: " << i.title << endl;
+ kdDebug() << "Total tracks: " << i.trackInfoList.count() << endl;
+ kdDebug() << "Disc revision: `" << i.revision << "'" << endl;
+ }
+
+ CDInfo i( c.bestLookupResponse() );
+
+ kdDebug() << "Best CDInfo had title: " << i.title << endl;
+ kdDebug() << "and revision: " << i.revision << endl;
+
+ return 0;
+}
diff --git a/libkcddb/test/synchttpsubmittest.cpp b/libkcddb/test/synchttpsubmittest.cpp
new file mode 100644
index 00000000..67c42dac
--- /dev/null
+++ b/libkcddb/test/synchttpsubmittest.cpp
@@ -0,0 +1,79 @@
+/*
+ Copyright (C) 2003 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+
+#include "libkcddb/client.h"
+#include "libkcddb/config.h"
+#include "libkcddb/submit.h"
+
+ int
+main(int argc, char ** argv)
+{
+ KCmdLineArgs::init(argc, argv, "libkcddb_test", "", "", "");
+
+ KApplication app(false /* No styles */);
+
+ using namespace KCDDB;
+
+ TrackOffsetList list;
+
+ list
+ << 150 // First track start.
+ << 2592
+ << 35472
+ << 47891
+ << 123310
+ << 150 // Disc start.
+ << 133125; // Disc end.
+
+ CDInfo cdInfo;
+
+ cdInfo.id = "3606ed05";
+ cdInfo.revision = 4;
+ cdInfo.title = "Bamse och Bronto";
+ cdInfo.artist = "Musiksage";
+ cdInfo.year = 2001;
+ cdInfo.category = "misc";
+ cdInfo.genre = "Barnsaga";
+ cdInfo.extd = QString::fromUtf8("Berättare: Olof Thunberg");
+
+ TrackInfo info;
+ info.title = "Bamses signaturmelodi";
+ cdInfo.trackInfoList.append(info);
+ info.title = "*";
+ cdInfo.trackInfoList.append(info);
+ info.title = "Brummavisan";
+ cdInfo.trackInfoList.append(info);
+ info.title = "*";
+ cdInfo.trackInfoList.append(info);
+ info.title = QString::fromUtf8("Jätteödlan Bronto");
+ cdInfo.trackInfoList.append(info);
+
+ Client c;
+ c.config().setSubmitTransport(Submit::HTTP);
+ c.setBlockingMode( true );
+
+ CDDB::Result r = c.submit(cdInfo, list);
+
+ kdDebug() << "Result: " << CDDB::resultToString(r) << endl;
+}
+
diff --git a/libkcddb/test/syncsmtpsubmittest.cpp b/libkcddb/test/syncsmtpsubmittest.cpp
new file mode 100644
index 00000000..1d608dda
--- /dev/null
+++ b/libkcddb/test/syncsmtpsubmittest.cpp
@@ -0,0 +1,81 @@
+/*
+ Copyright (C) 2003 Richard Lärkäng <nouseforaname@home.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+
+#include "libkcddb/client.h"
+#include "libkcddb/config.h"
+#include "libkcddb/submit.h"
+
+ int
+main(int argc, char ** argv)
+{
+ KCmdLineArgs::init(argc, argv, "libkcddb_test", "", "", "");
+
+ KApplication app(false /* No styles */);
+
+ using namespace KCDDB;
+
+ TrackOffsetList list;
+
+ list
+ << 150 // First track start.
+ << 2592
+ << 35472
+ << 47891
+ << 123310
+ << 150 // Disc start.
+ << 133125; // Disc end.
+
+ CDInfo cdInfo;
+
+ cdInfo.id = "3606ed05";
+ cdInfo.revision = 4;
+ cdInfo.title = "Bamse och Bronto";
+ cdInfo.artist = "Musiksage";
+ cdInfo.year = 2001;
+ cdInfo.category = "misc";
+ cdInfo.genre = "Barnsaga";
+ cdInfo.extd = QString::fromUtf8("Berättare: Olof Thunberg");
+
+ TrackInfo info;
+ info.title = "Bamses signaturmelodi";
+ cdInfo.trackInfoList.append(info);
+ info.title = "*";
+ cdInfo.trackInfoList.append(info);
+ info.title = "Brummavisan";
+ cdInfo.trackInfoList.append(info);
+ info.title = "*";
+ cdInfo.trackInfoList.append(info);
+ info.title = QString::fromUtf8("Jätteödlan Bronto");
+ cdInfo.trackInfoList.append(info);
+
+ Client c;
+ c.config().setSubmitTransport(Submit::SMTP);
+ c.config().setSubmitAddress("test-submit@freedb.org");
+
+ c.setBlockingMode( true );
+
+ CDDB::Result r = c.submit(cdInfo, list);
+
+ kdDebug() << "Result: " << CDDB::resultToString(r) << endl;
+}
+
diff --git a/libkcddb/test/utf8test.cpp b/libkcddb/test/utf8test.cpp
new file mode 100644
index 00000000..05d8b83e
--- /dev/null
+++ b/libkcddb/test/utf8test.cpp
@@ -0,0 +1,50 @@
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kdebug.h>
+
+#include "libkcddb/client.h"
+#include "libkcddb/cache.h"
+#include "libkcddb/lookup.h"
+
+ int
+main(int argc, char ** argv)
+{
+ KCmdLineArgs::init(argc, argv, "libkcddb_test", "", "", "");
+
+ KApplication app;
+
+ using namespace KCDDB;
+
+ Client c;
+
+ TrackOffsetList list;
+
+ list
+ << 150
+ << 61408
+ << 0
+ << 177675;
+
+ kdDebug() << "Stuff to send to server:" << endl;
+
+ kdDebug()
+ << CDDB::trackOffsetListToId(list)
+ << " "
+ //<< trackOffsetListToString(list)
+ << endl;
+
+ CDDB::Result r = c.lookup(list);
+
+ kdDebug() << "Client::lookup gave : " << CDDB::resultToString(r) << endl;
+
+ CDInfoList response = c.lookupResponse();
+
+ kdDebug() << "Client::lookup returned : " << response.count() << " entries"
+ << endl;
+
+ CDInfo i( c.bestLookupResponse() );
+
+ kdDebug() << "Best CDInfo had title: " << i.title << endl;
+
+ return 0;
+}
diff --git a/mpeglib/CHANGES b/mpeglib/CHANGES
new file mode 100644
index 00000000..aefda7fd
--- /dev/null
+++ b/mpeglib/CHANGES
@@ -0,0 +1,24 @@
+* seek on paused streams works
+* close on paused streams works
+* added shutdown mutex. This mutex is locked during creation and
+ shutdown of the decoder_loop thread, but not when
+ the while(runCheck()) loop is running. You should lock this
+ mutex if another thread calls methods on variables which are
+ constructed/destructed by the decode_loop.
+* mpeglib now installs under /usr per default
+* Many thanks to Ranty for his patches. Applied patches
+ for playtime(absolute/current),compile fixes, mpegstream dectection
+ and realtive seek (+/-) time
+* added pts based sync to mpeg2 player (sync is still horrible)
+* added a syncClock class. Now the sync is based on scr stamps
+* hacked ac3dec/mpeg2dec so they no longer share the same (globally)
+* added yaf frontends for ac3,mpeg2
+* added ac3 and mpeg2 player (an mpeg2 demuxer is still missing)
+* moved the picture construction in the outputstream
+* images are now "smoother" Its a good balance between fast/nice images
+* SDL sound now works
+* wrapper for video and sound. sound over SDL does not work->bug?
+* wrapper for threads (SDL does not support try-lock and condition variables)
+* directories restructured lib/* now installs as library
+* added yaf backends for testing the decoders
+* first release
diff --git a/mpeglib/COPYING b/mpeglib/COPYING
new file mode 100644
index 00000000..c0da6a32
--- /dev/null
+++ b/mpeglib/COPYING
@@ -0,0 +1,498 @@
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/mpeglib/INSTALL b/mpeglib/INSTALL
new file mode 100644
index 00000000..299bb7a5
--- /dev/null
+++ b/mpeglib/INSTALL
@@ -0,0 +1,23 @@
+
+
+mpeglib is currently automatically compiled for Linux and AIX.
+If you want to compile it on a different platform you
+must manually compile/install
+
+* mpeglib and
+* mpeglib_artsplug
+
+Please send patches for currently unsupported platforms to:
+
+kde-multimedia@master.kde.org
+
+The homepage of mpeglib is:
+
+http://mpeglib.sourceforge.net/
+
+
+
+
+
+
+
diff --git a/mpeglib/Makefile.am b/mpeglib/Makefile.am
new file mode 100644
index 00000000..d3212dbb
--- /dev/null
+++ b/mpeglib/Makefile.am
@@ -0,0 +1,9 @@
+# This file you have to edit. Change the name here
+
+
+SUBDIRS = lib example
+
+
+EXTRA_DIST = CHANGES doemacs TODO \
+ PROBLEMS INSTALL configure.in.in README-WIN32.TXT
+
diff --git a/mpeglib/PROBLEMS b/mpeglib/PROBLEMS
new file mode 100644
index 00000000..5534963e
--- /dev/null
+++ b/mpeglib/PROBLEMS
@@ -0,0 +1,20 @@
+
+
+* SDL build:
+
+ ./configure --with-extra-includes=/usr/local/include --enable-sdl
+
+
+* XFree 4.0
+
+ You have installed XFree 4.0 from source?
+
+ please make sure you have the following link:
+
+ ln -s /usr/X11R6/include/X11/ /usr/include/X11
+
+ and then look for : (during ./configure)
+ XV extension -> yes
+ DGA 2.0 extension -> yes
+
+ \ No newline at end of file
diff --git a/mpeglib/README b/mpeglib/README
new file mode 100644
index 00000000..6e72c9b8
--- /dev/null
+++ b/mpeglib/README
@@ -0,0 +1,51 @@
+
+PORTS
+=====
+- Unix
+- Windows (read README-WIN32.TXT)
+
+
+MPEG I library.
+===============
+
+This library contains:
+
+* mpeg I audio player (layer I,II,III (mp3))
+* mpeg I video player
+* mpeg I system layer player
+* wav player
+
+
+Supported features:
+-------------------
+
+* seek in all players
+* length detection
+* video synchronisation, based on timestamps
+* mmx Support where necessary
+
+
+Supported Outputs:
+------------------
+
+Audio
+
+Support for OSS/Linux, Sun
+
+
+Video
+
+X11 standard calls (fallback)
+X11 Shared mem
+X11 XFree86 4.0 DGA 2.0 (needs root)
+X11 XFree86 4.0 XVideo Extension (hardware yuv->rgb renderer)
+
+Supported Inputs
+----------------
+
+
+* file,http.
+* Supports on Linux Video CDs (vcd,cdi)
+
+
+
diff --git a/mpeglib/README-WIN32.TXT b/mpeglib/README-WIN32.TXT
new file mode 100644
index 00000000..1d17de00
--- /dev/null
+++ b/mpeglib/README-WIN32.TXT
@@ -0,0 +1,16 @@
+README MPEGLIB-SDL for Win32
+
+please unpack VisualC.zip in the root directory
+and read with a browser the file:
+
+VisualC.html
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/TODO b/mpeglib/TODO
new file mode 100644
index 00000000..1e050165
--- /dev/null
+++ b/mpeglib/TODO
@@ -0,0 +1,2 @@
+
+make this library arts aware.
diff --git a/mpeglib/configure.in.in b/mpeglib/configure.in.in
new file mode 100644
index 00000000..1435874b
--- /dev/null
+++ b/mpeglib/configure.in.in
@@ -0,0 +1,241 @@
+dnl ***** end of KDE specific configure things *********
+dnl [START] for mpeglib checks
+
+
+THIS_LIB_MAJOR_VERSION=0
+THIS_LIB_MINOR_VERSION=3
+THIS_LIB_MICRO_VERSION=0
+THIS_LIB_NAME=mpeglib
+THIS_LIB_VERSION=$THIS_LIB_MAJOR_VERSION.$THIS_LIB_MINOR_VERSION.$THIS_LIB_MICRO_VERSION
+
+AC_SUBST(THIS_LIB_MAJOR_VERSION)
+AC_SUBST(THIS_LIB_MINOR_VERSION)
+AC_SUBST(THIS_LIB_MICRO_VERSION)
+AC_SUBST(THIS_LIB_VERSION)
+AC_SUBST(THIS_LIB_NAME)
+dnl this does not work, but to have no errors we subst it
+
+
+
+dnl Checks for header files.
+AC_CHECK_HEADERS(pthread.h pthread/mit/pthread.h)
+AC_CHECK_HEADERS(sys/soundcard.h soundcard.h)
+
+
+AC_C_BIGENDIAN
+
+
+
+
+dnl AC_C_ATTRIBUTE_ALIGNED
+dnl define ATTRIBUTE_ALIGNED_MAX to the maximum alignment if this is supported
+AC_DEFUN([AC_C_ATTRIBUTE_ALIGNED],
+ [AC_CACHE_CHECK([__attribute__ ((aligned ())) support],
+ [ac_cv_c_attribute_aligned],
+ [ac_cv_c_attribute_aligned=0
+ for ac_cv_c_attr_align_try in 2 4 8 16 32 64; do
+ AC_TRY_COMPILE([],
+ [static char c __attribute__ ((aligned($ac_cv_c_attr_align_try))) = 0; return c;],
+ [ac_cv_c_attribute_aligned=$ac_cv_c_attr_align_try])
+ done])
+ if test x"$ac_cv_c_attribute_aligned" != x"0"; then
+ AC_DEFINE_UNQUOTED([ATTRIBUTE_ALIGNED_MAX],
+ [$ac_cv_c_attribute_aligned],[maximum supported data alignment])
+ fi])
+
+AC_C_ATTRIBUTE_ALIGNED
+
+
+
+
+
+
+# later AC_CHECK_LIB is used, which uses ac_save_*, so don't use them
+# here
+mpg_save_CPPFLAGS="$CPPFLAGS"
+mpg_save_LDFLAGS="$LDFLAGS"
+mpg_save_LIBS="$LIBS"
+CPPFLAGS="$CPPFLAGS $X_INCLUDES $USER_INCLUDES"
+LDFLAGS="$LDFLAGS $X_LDFLAGS $USER_LDFLAGS"
+LIBS="$LIBS $LIB_XEXT -lX11 $X_EXTRA_LIBS"
+
+AC_MSG_CHECKING(for X11 shared mem extension)
+x11_shared_mem=no
+AC_TRY_COMPILE([
+ #include <X11/Xlib.h>
+ #include <X11/extensions/XShm.h>
+ #include <sys/ipc.h>
+ #include <sys/shm.h>
+ ],[
+ ],[
+ x11_shared_mem=yes
+ ])
+AC_MSG_RESULT($x11_shared_mem)
+if test x$x11_shared_mem = xyes; then
+ AC_DEFINE(X11_SHARED_MEM,1,[Define if you have X11 shared mem])
+fi
+
+
+AC_MSG_CHECKING(for X11 DGA2.0 extension)
+x11_dga2=no
+AC_TRY_COMPILE([
+ #include <X11/Xlib.h>
+ #include <X11/extensions/xf86dga1.h>
+ ],[
+ ],[
+ x11_dga2=yes
+ ])
+AC_MSG_RESULT($x11_dga2)
+if test x$x11_dga2 = xyes; then
+ AC_DEFINE(X11_DGA2,1,[Define if you have XFree4.0 DGA 2.0])
+ DGALIBS="-lXxf86dga"
+fi
+
+AC_MSG_CHECKING(for X11 video extension)
+x11_xv=no
+AC_TRY_COMPILE([
+ #include <X11/Xlib.h>
+ #include <X11/extensions/Xvlib.h>
+ ],[
+ ],[
+ x11_xv=yes
+ ])
+AC_MSG_RESULT($x11_xv)
+if test x$x11_xv = xyes; then
+ AC_DEFINE(X11_XV,1,[Define if you have XFree4.0 XV extension])
+ XVLIBS="-lXv"
+fi
+
+AC_MSG_CHECKING(for X11 videomode extension)
+x11_xvidmode=no
+AC_TRY_COMPILE([
+ #include <X11/Xlib.h>
+ #include <X11/extensions/xf86vmode.h>
+ ],[
+ ],[
+ x11_xvidmode=yes
+ ])
+AC_MSG_RESULT($x11_xvidmode)
+if test x$x11_xvidmode = xyes; then
+ AC_DEFINE(X11_XVIDMODE,1,[Define if you have XVidModeextension])
+ XVIDMODELIBS="-lXxf86vm"
+fi
+
+
+dnl
+dnl if we do an SDL build we remove now all determined X11 dependencies
+dnl
+
+X11_LIBS="-lX11 -lXext"
+
+if test x$disable_x11 = xyes; then
+ DGALIBS=""
+ XVLIBS=""
+ X11_LIBS=""
+fi
+
+LIBS="$mpg_save_LIBS"
+LDFLAGS="$mpg_save_LDFLAGS"
+CPPFLAGS="$mpg_save_CPPFLAGS"
+
+AC_MSG_CHECKING(checking OS)
+AC_SUBST(OS_TYPE)
+OS_TYPE=`uname -s`
+AC_MSG_RESULT($OS_TYPE)
+
+INTELCPPFLAG=
+dnl MMX_SUPPORT is set as variable in main configure.in.in
+dnl now make it a config.h entry
+if test x$MMX_SUPPORT = xyes; then
+ AC_DEFINE(INTEL,1,[Define if you have MMX support on x86 with gcc])
+ INTELCPPFLAG="-DINTEL"
+fi
+AC_SUBST(INTELCPPFLAG)
+
+AC_DEFUN([KDE_MPEGLIB_COMPILES],
+[
+dnl disable everything which is not supported
+kde_mpeglib_compiles=no
+
+case "$OS_TYPE" in
+ FreeBSD)
+ kde_mpeglib_compiles=yes
+ ;;
+ NetBSD)
+ kde_mpeglib_compiles=yes
+ ;;
+ Linux)
+ kde_mpeglib_compiles=yes
+ ;;
+ AIX)
+ kde_mpeglib_compiles=yes
+ ;;
+ SunOS)
+ kde_mpeglib_compiles=yes
+ ;;
+ DragonFly)
+ kde_mpeglib_compiles=yes
+ ;;
+ *)
+ AC_MSG_RESULT([*** We disable mpeglib for this platform ***])
+ DO_NOT_COMPILE="$DO_NOT_COMPILE mpeglib mpeglib_artsplug"
+ ;;
+esac
+
+
+KDE_CHECK_LIBPTHREAD
+case "$OS_TYPE" in
+ AIX)
+ AC_MSG_RESULT([AIX :Unknown hardware. You will have problems!!])
+ AC_DEFINE(OS_AIX,1,[Define if you have AIX for sound])
+ ;;
+
+ Linux)
+ AC_DEFINE(OS_Linux,1,[Define if you have linux pthread])
+ ;;
+
+ FreeBSD | DragonFly)
+ OS_TYPE="BSD"
+ AC_DEFINE(OS_BSD,1,[Define if you have BSD pthread])
+ ;;
+
+ BSD/OS)
+ OS_TYPE="BSD"
+ AC_DEFINE(OS_BSD,1,[Define if you have BSD pthread in -lc])
+ ;;
+
+ NetBSD)
+ AC_MSG_RESULT([NetBSD :Unknown hardware. You will have problems!!])
+ OS_TYPE="BSD"
+ AC_DEFINE(OS_BSD,1,[Define if you have BSD pthread in -lc_r])
+ ;;
+
+ SunOS)
+ OS_TYPE="SunOS"
+ AC_DEFINE(OS_SunOS,1,[Define if you have solaris -lpthread])
+ ;;
+
+ IRIX)
+ AC_MSG_RESULT([IRIX :Unknown hardware. You will have problems!!])
+ AC_DEFINE(OS_IRIX,1,[Define if you have iris])
+ ;;
+
+ HPUX)
+ AC_MSG_RESULT([HPUX :Unknown hardware. You will have problems!!])
+ AC_DEFINE(OS_HPUX,1,[Define if you have HP_UX])
+ ;;
+
+ *)
+ AC_MSG_RESULT([Very Unknown hardware. **Better give up!**])
+ ;;
+
+esac
+
+])
+
+KDE_MPEGLIB_COMPILES
+
+THIS_LIB_LIBS="-lX11 -lXext -lm ${XVLIBS} ${DGALIBS} "
+THIS_LIB_LIBS="${THIS_LIB_LIBS} ${XVIDMODELIBS} ${SDLLIB} ${CDDALIBS} "
+THIS_LIB_LIBS="${THIS_LIB_LIBS} ${OGG_VORBISLIBS} ${LIBPTHREAD}"
+AC_SUBST(THIS_LIB_LIBS)
diff --git a/mpeglib/example/Makefile.am b/mpeglib/example/Makefile.am
new file mode 100644
index 00000000..f5fcf06d
--- /dev/null
+++ b/mpeglib/example/Makefile.am
@@ -0,0 +1,3 @@
+
+SUBDIRS = mpgplay splay tplay cddaplay yaf
+
diff --git a/mpeglib/example/cddaplay/Makefile.am b/mpeglib/example/cddaplay/Makefile.am
new file mode 100644
index 00000000..49f42a2a
--- /dev/null
+++ b/mpeglib/example/cddaplay/Makefile.am
@@ -0,0 +1,33 @@
+# splay-yaf - Makefile.am
+
+INCLUDES = -I../../include $(all_includes)
+
+# EXTRA_DIST = stresstest
+
+noinst_PROGRAMS = cddaplay
+
+cddaplay_SOURCES = cddaplay.cpp
+
+noinst_HEADERS =
+
+cddaplay_LDFLAGS = $(all_libraries)
+
+
+cddaplay_LDADD = ../../lib/libmpeg.la \
+ $(THIS_LIB_LIBS)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/example/cddaplay/cddaplay.cpp b/mpeglib/example/cddaplay/cddaplay.cpp
new file mode 100644
index 00000000..ecf46e74
--- /dev/null
+++ b/mpeglib/example/cddaplay/cddaplay.cpp
@@ -0,0 +1,75 @@
+/*
+ * main.c --
+ *
+ * Example program for mpegplay library.
+
+
+ */
+
+
+#include "../../lib/decoder/cddaPlugin.h"
+#include "../../lib/input/inputPlugin.h"
+
+#include <iostream>
+
+using namespace std;
+
+int main(int argc, char** argv) {
+
+
+ if (argc <= 1) {
+ printf("Usage:\n\n");
+ printf("%s \"cdda://dev/<device> track:<nr>.cdda\n\n",argv[0]);
+ exit(0);
+ }
+ //
+ // The order is important !!!!
+ // 1. construct
+ // 2. set Output
+ // 3. open input
+ // 4. set input
+ //
+ // you cannot set the input _before_ the output
+ // in fact you can, but this gives you a segfault!
+
+ CDDAPlugin* plugin=new CDDAPlugin();
+ OutputStream* out=OutPlugin::createOutputStream(_OUTPUT_LOCAL);
+ InputStream* in= InputPlugin::createInputStream(argv[1]);
+
+
+
+ // The plugin does not do "open"
+ in->open(argv[1]);
+
+
+
+
+
+ // watch the order!
+ plugin->setOutputPlugin(out);
+ plugin->setInputPlugin(in);
+ //plugin->seek(1950);
+ cout << "hello 1 -s"<<endl;
+
+
+ plugin->play();
+ cout << "hello 1 -e"<<endl;
+
+
+ while(1) {
+ if (plugin->getStreamState() != _STREAM_STATE_EOF) {
+ cout << "******* plugin->getStreamState() continue"<<endl;
+ sleep(3);
+ continue;
+ }
+ break;
+ }
+ cout << "********************plugin eof"<<endl;
+ plugin->close();
+
+ delete plugin;
+ delete in;
+ delete out;
+
+}
+
diff --git a/mpeglib/example/mpgplay/Makefile.am b/mpeglib/example/mpgplay/Makefile.am
new file mode 100644
index 00000000..c76bd4d1
--- /dev/null
+++ b/mpeglib/example/mpgplay/Makefile.am
@@ -0,0 +1,32 @@
+# mpegplay-yaf - Makefile.am
+
+INCLUDES = $(all_includes)
+
+EXTRA_DIST =
+
+noinst_PROGRAMS = mpgplay
+
+mpgplay_SOURCES = mpgplay.cpp
+
+noinst_HEADERS =
+
+mpgplay_LDFLAGS = $(all_libraries)
+
+mpgplay_LDADD = ../../lib/libmpeg.la \
+ $(THIS_LIB_LIBS)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/example/mpgplay/mpgplay.cpp b/mpeglib/example/mpgplay/mpgplay.cpp
new file mode 100644
index 00000000..c5c1d82c
--- /dev/null
+++ b/mpeglib/example/mpgplay/mpgplay.cpp
@@ -0,0 +1,74 @@
+/*
+ * main.c --
+ *
+ * Example program for mpegplay library.
+
+
+ */
+
+#include "../../lib/decoder/mpgPlugin.h"
+#include "../../lib/util/timeWrapper.h"
+
+#include <iostream>
+
+using namespace std;
+
+int main(int argc, char** argv) {
+
+
+ if (argc <= 1) {
+ printf("Usage:\n\n");
+ printf("%s filename\n\n",argv[0]);
+ exit(0);
+ }
+ //
+ // The order is important !!!!
+ // 1. construct
+ // 2. set Output
+ // 3. open input
+ // 4. set input
+ //
+ // you cannot set the input _before_ the output
+ // in fact you can, but this gives you a segfault!
+
+ MpgPlugin* plugin=new MpgPlugin();
+ OutputStream* out=OutPlugin::createOutputStream(_OUTPUT_LOCAL,true);
+ InputStream* in=InputPlugin::createInputStream(argv[1],true);
+
+
+
+ // The plugin does not do "open"
+ in->open(argv[1]);
+
+
+
+
+
+ // watch the order!
+ plugin->setOutputPlugin(out);
+ plugin->setInputPlugin(in);
+ //plugin->seek(1950);
+ cout << "hello 1 -s"<<endl;
+
+
+ plugin->play();
+ cout << "hello 1 -e"<<endl;
+
+
+ while(1) {
+ if (plugin->getStreamState() != _STREAM_STATE_EOF) {
+ cout << "******* plugin->getStreamState() continue"<<endl;
+ TimeWrapper::sleep(3);
+ continue;
+ }
+ break;
+ }
+ cout << "********************plugin eof"<<endl;
+ plugin->close();
+
+ delete plugin;
+ delete in;
+ delete out;
+ return(0);
+}
+
diff --git a/mpeglib/example/splay/Makefile.am b/mpeglib/example/splay/Makefile.am
new file mode 100644
index 00000000..76e04cff
--- /dev/null
+++ b/mpeglib/example/splay/Makefile.am
@@ -0,0 +1,41 @@
+# splay-yaf - Makefile.am
+
+INCLUDES = -I../../include $(all_includes)
+
+EXTRA_DIST =
+
+noinst_PROGRAMS = splay mp3framing
+
+splay_SOURCES = splay.cpp
+
+mp3framing_SOURCES = mp3framing.cpp
+
+noinst_HEADERS =
+
+splay_LDFLAGS = $(all_libraries)
+
+splay_LDADD = ../../lib/libmpeg.la \
+ $(THIS_LIB_LIBS)
+
+mp3framing_LDFLAGS = $(all_libraries)
+mp3framing_LDADD = ../../lib/libmpeg.la \
+ $(THIS_LIB_LIBS)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/example/splay/mp3framing.cpp b/mpeglib/example/splay/mp3framing.cpp
new file mode 100644
index 00000000..586899a3
--- /dev/null
+++ b/mpeglib/example/splay/mp3framing.cpp
@@ -0,0 +1,274 @@
+/*
+
+ Example how frame creation works.
+
+ For the input we have std. file input.
+ We convert it to mpeg I encoded frames (mpegAudioFrame's job)
+
+ The decoded pcmFrames are stores in a queue.
+
+ If the queue if full, we "stream" the packets to /dev/dsp.
+ (The difference between directly playing the packets and "streaming"
+ is, that while streaming you can say: I want x bytes I don't care
+ about frame boundaries)
+ This is audioFrameQueue's job.
+
+
+ */
+
+
+#include "../../lib/splay/mpegAudioFrame.h"
+#include "../../lib/splay/splayDecoder.h"
+#include "../../lib/util/audio/dspWrapper.h"
+#include "../../lib/frame/audioFrameQueue.h"
+
+#include <iostream>
+
+using namespace std;
+
+// used for getopt
+#include <unistd.h>
+
+#define INPUT_SIZE 8192
+#define _QUEUE_FLOAT 1
+#define _QUEUE_INT 2
+
+
+void usage() {
+ printf("Usage:\n\n");
+ printf("mp3framing [hf] filename\n\n");
+ printf(" -f use float queue\n");
+ printf(" -c [samples] checkout samples\n");
+}
+
+
+void initDSP(DSPWrapper* dsp) {
+ if (dsp->openDevice()==false) {
+ cout << "cannot open /dev/dsp"<<endl;
+ exit(-1);
+ }
+}
+
+
+
+int main(int argc, char** argv) {
+ int queueType=_QUEUE_INT;
+ int samples=8192;
+
+ PCMFrame* outFrame;
+
+ while(1) {
+ int c = getopt (argc, argv, "hfc:");
+ if (c == -1) break;
+ switch(c) {
+ case 'h': {
+ usage();
+ exit(-1);
+ break;
+ }
+ case 'f': {
+ queueType=_QUEUE_FLOAT;
+ break;
+ }
+ case 'c': {
+ samples=atoi(optarg);
+ cout << "checking out samples:samples"<<endl;
+ break;
+ }
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ exit(-1);
+ }
+ }
+ // if we have nothing left, there is no filename
+ if (optind >= argc ) {
+ usage();
+ exit(-1);
+ }
+ FILE* file=fopen(argv[optind],"r");
+ if (file == NULL) {
+ cout << "cannot open:"<<argv[1]<<endl;
+ exit(-1);
+ }
+ DSPWrapper* dsp=new DSPWrapper();
+ outFrame=new PCMFrame(samples);
+
+ float* left=new float[samples];
+ float* right=new float[samples];
+
+
+ // create queue with 500 frames. We want output to dsp(later)
+ // so we need a _FRAME_AUDIO_PCM type and we need
+ // for splay at least MP3FRAMESIZE
+ AudioFrameQueue* frameQueue;
+ if (queueType == _QUEUE_INT) {
+ frameQueue=new AudioFrameQueue(500,MP3FRAMESIZE,_FRAME_AUDIO_PCM);
+ }
+ if (queueType == _QUEUE_FLOAT) {
+ frameQueue=new AudioFrameQueue(100,MP3FRAMESIZE,_FRAME_AUDIO_FLOAT);
+ }
+
+
+ initDSP(dsp);
+ SplayDecoder* splay=new SplayDecoder();
+
+
+ //
+ // MpegAudioFrame has the ability to convert "raw" file
+ // data into mpeg I frames. These are "valid" frames,
+ // which means: header is ok, and the packet has the
+ // correct length. splay decodes these frames _at once_
+ // so we need no callbacks for the decoder.
+ MpegAudioFrame* frame=new MpegAudioFrame();
+
+
+ // Note: The inputbuffer must NOT NOT NOT be on the stack!
+ // correction: it should not be corrupted by allocation
+ // on the stack. This is true, if allocation+decoding
+ // or in the _same_ function.
+ unsigned char inputbuffer[INPUT_SIZE];
+
+
+ int cnt=0;
+ // we proceed until eof, or our "queue" is full.
+ while( (feof(file) == false) && (frameQueue->dataQueueCanWrite() == true) ) {
+
+ //
+ // This switch/case statement "partitions" the fileinput
+ // into mp3frames. We directly decode them to pcmFrames.
+ //
+
+ //
+ // There are three states. One is "I want Input, give me input"
+ // The other is "I have enough input, dont' bother me and let
+ // me do my work"
+ // The last ist "I have a frame here, take care of this, or
+ // I will continue with my work"
+
+ int state=frame->getState();
+ switch(state) {
+ case FRAME_NEED: {
+ int bytes=frame->canStore();
+ int read=fread(inputbuffer,1,bytes,file);
+ if (read != bytes) {
+ // read error. reset framer
+ frame->reset();
+ continue;
+ }
+ frame->store(inputbuffer,bytes);
+ break;
+ }
+ case FRAME_WORK:
+ frame->work();
+ break;
+ case FRAME_HAS:{
+ AudioFrame* emptyFrame= frameQueue->emptyQueueDequeue();
+ splay->decode(frame->outdata(),frame->len(),emptyFrame);
+ frameQueue->dataQueueEnqueue(emptyFrame);
+ cout << "storing decodec frame:"<<cnt<<endl;
+ cnt++;
+ break;
+ }
+ default:
+ cout << "unknown state in mpeg audio framing"<<endl;
+ exit(0);
+ }
+ }
+
+
+ //
+ // Fine we put all decoded frames in the buffer.
+ // Now we want to "stream" from this buffer.
+ //
+
+ //
+ // create our "local" stream
+ short int playbuf[INPUT_SIZE];
+
+ //
+ // we need to setup the dsp manually, when we do not play pcmFrames directly
+ //
+ if (queueType == _QUEUE_INT) {
+ AudioFrame* audioFrame=frameQueue->getCurrent();
+ dsp->audioSetup(audioFrame);
+ }
+
+ if (queueType == _QUEUE_FLOAT) {
+ AudioFrame* audioFrame=frameQueue->getCurrent();
+ dsp->audioSetup(audioFrame->getStereo(),16,
+ audioFrame->getSigned(),
+ audioFrame->getBigEndian(),
+ audioFrame->getFrequenceHZ());
+ outFrame->setFrameFormat(audioFrame->getStereo(),
+ audioFrame->getFrequenceHZ());
+ }
+
+
+
+ //
+ // read from stream demo:
+ //
+
+ while(frameQueue->getLen() > 0) {
+ int hasLen= frameQueue->getLen();
+
+ cout << "hasLen:"<<hasLen<<endl;
+ int hasRead;
+ if (hasLen < samples) {
+ samples=hasLen;
+ }
+
+
+
+ if (queueType == _QUEUE_INT) {
+ cout << "reading :"<<samples<<" data"<<endl;
+ hasRead=frameQueue->copy(outFrame->getData(),samples);
+ dsp->audioPlay((char*)outFrame->getData(),hasRead*sizeof(short int));
+ frameQueue->forwardStreamSingle(hasRead);
+ }
+
+
+ if (queueType == _QUEUE_FLOAT) {
+ int n;
+
+ hasRead=frameQueue->copy(left,right,samples);
+ frameQueue->forwardStreamDouble(hasRead);
+ n=hasRead;
+
+ /*
+ FloatFrame* floatFrame=( FloatFrame*) frameQueue->dataQueueDequeue();
+ float* left=floatFrame->getData();
+ n=floatFrame->getLen();
+ */
+
+ int i=0;
+ while(i<n) {
+ int val=(int)(32768.0*left[i]);
+ if (val > 32767) val=32767;
+ if (val < -32768) val=-32768;
+ dsp->audioPlay((char*)&val,2);
+
+ if (outFrame->getStereo()) {
+ val=(int)(32768.0*right[i]);
+ if (val > 32767) val=32767;
+ if (val < -32768) val=-32768;
+ dsp->audioPlay((char*)&val,2);
+ }
+
+ i++;
+ }
+
+ }
+ }
+
+
+ delete frameQueue;
+ delete splay;
+ delete frame;
+ delete dsp;
+ delete left;
+ delete right;
+ delete outFrame;
+ return(0);
+}
+
diff --git a/mpeglib/example/splay/splay.cpp b/mpeglib/example/splay/splay.cpp
new file mode 100644
index 00000000..75254360
--- /dev/null
+++ b/mpeglib/example/splay/splay.cpp
@@ -0,0 +1,64 @@
+/*
+
+ Example how the plugin interface works.(LINUX Open Sound System only!)
+
+
+ */
+
+
+#include "../../lib/decoder/splayPlugin.h"
+#include "../../lib/decoder/tplayPlugin.h"
+#include "../../lib/decoder/vorbisPlugin.h"
+#include "../../lib/input/bufferInputStream.h"
+#include "../../lib/output/avSyncer.h"
+
+#include <iostream>
+
+using namespace std;
+
+int main(int argc, char** argv) {
+
+
+
+ if (argc <= 1) {
+ printf("Usage:\n\n");
+ printf("%s filename\n\n",argv[0]);
+ exit(0);
+ }
+
+ //
+ // The order is important !!!!
+ // 1. construct
+ // 2. set Output
+ // 3. open input
+ // 4. set input
+ //
+ // you cannot set the input _before_ the output
+ // in fact you can, but this gives you a segfault!
+
+ SplayPlugin* plugin=new SplayPlugin();
+ OutputStream* out=OutPlugin::createOutputStream(_OUTPUT_LOCAL,true);
+ InputStream* in=InputPlugin::createInputStream(argv[1],true);
+
+ in->open(argv[1]);
+ // watch the order!
+ plugin->setOutputPlugin(out);
+ plugin->setInputPlugin(in);
+ plugin->play();
+
+ while(plugin->getStreamState() != _STREAM_STATE_EOF) {
+ cout << "check state"<<endl;
+ TimeWrapper::sleep(1);
+
+ }
+
+ cout << "****************plugin eof"<<endl;
+ //out->audioClose();
+ plugin->close();
+
+ delete plugin;
+ delete in;
+ delete out;
+ return(0);
+}
+
diff --git a/mpeglib/example/tplay/Makefile.am b/mpeglib/example/tplay/Makefile.am
new file mode 100644
index 00000000..c79a2805
--- /dev/null
+++ b/mpeglib/example/tplay/Makefile.am
@@ -0,0 +1,32 @@
+# tplay-yaf - Makefile.am
+
+INCLUDES = -I../../include $(all_includes)
+
+EXTRA_DIST =
+
+noinst_PROGRAMS = tplay
+
+tplay_SOURCES = tplay.cpp
+
+noinst_HEADERS =
+
+tplay_LDFLAGS = $(all_libraries)
+
+
+tplay_LDADD = ../../lib/libmpeg.la \
+ $(THIS_LIB_LIBS)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/example/tplay/tplay.cpp b/mpeglib/example/tplay/tplay.cpp
new file mode 100644
index 00000000..324c6c17
--- /dev/null
+++ b/mpeglib/example/tplay/tplay.cpp
@@ -0,0 +1,66 @@
+/*
+
+ Example how the plugin interface works.(LINUX Open Sound System only!)
+
+
+
+
+
+ */
+
+
+#include "../../lib/decoder/tplayPlugin.h"
+#include "../../lib/decoder/nukePlugin.h"
+
+
+#include <iostream>
+
+using namespace std;
+
+int main(int argc, char** argv) {
+
+
+ if (argc <= 1) {
+ printf("Usage:\n\n");
+ printf("%s filename\n\n",argv[0]);
+ exit(0);
+ }
+
+ //
+ // The order is important !!!!
+ // 1. construct
+ // 2. set Output
+ // 3. open input
+ // 4. set input
+ //
+ // you cannot set the input _before_ the output
+ // in fact you can, but this gives you a segfault!
+
+ DecoderPlugin* plugin=new TplayPlugin();
+ //DecoderPlugin* plugin=new NukePlugin();
+ OutputStream* out=OutPlugin::createOutputStream(_OUTPUT_LOCAL,true);
+ InputStream* in=InputPlugin::createInputStream(argv[1],true);
+
+ // The plugin does not do "open"
+ in->open(argv[1]);
+
+ // watch the order!
+ plugin->setOutputPlugin(out);
+ plugin->setInputPlugin(in);
+
+
+
+ plugin->play();
+
+ while(plugin->getStreamState() != _STREAM_STATE_EOF) {
+ TimeWrapper::sleep(1);
+ }
+ cout << "plugin eof"<<endl;
+ plugin->close();
+
+ delete plugin;
+ delete in;
+ delete out;
+ return(0);
+}
+
diff --git a/mpeglib/example/yaf/Makefile.am b/mpeglib/example/yaf/Makefile.am
new file mode 100644
index 00000000..7144955a
--- /dev/null
+++ b/mpeglib/example/yaf/Makefile.am
@@ -0,0 +1,15 @@
+# yaflib - Makefile.am
+
+SUBDIRS = yafcore yafxplayer yafsplay yafmpgplay \
+ yaftplay yafvorbis yafyuv yafcdda
+
+
+
+EXTRA_DIST = README
+
+
+
+
+
+
+
diff --git a/mpeglib/example/yaf/README b/mpeglib/example/yaf/README
new file mode 100644
index 00000000..4fe128b0
--- /dev/null
+++ b/mpeglib/example/yaf/README
@@ -0,0 +1,4 @@
+
+Someday this directory will be replaced with a simple readline
+interface.
+
diff --git a/mpeglib/example/yaf/yafcdda/Makefile.am b/mpeglib/example/yaf/yafcdda/Makefile.am
new file mode 100644
index 00000000..2930f348
--- /dev/null
+++ b/mpeglib/example/yaf/yafcdda/Makefile.am
@@ -0,0 +1,35 @@
+# splay-yaf - Makefile.am
+
+INCLUDES = -I../../include $(all_includes)
+
+
+bin_PROGRAMS = yaf-cdda
+
+yaf_cdda_SOURCES = cdda_control.cpp
+
+
+noinst_HEADERS =
+
+yaf_cdda_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+
+yaf_cdda_LDADD = ../yafxplayer/libyafxplayer.la \
+ ../yafcore/libyafcore.la \
+ ../../../lib/libmpeg.la \
+ $(THIS_LIB_LIBS)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/example/yaf/yafcdda/cdda_control.cpp b/mpeglib/example/yaf/yafcdda/cdda_control.cpp
new file mode 100644
index 00000000..5bac58e2
--- /dev/null
+++ b/mpeglib/example/yaf/yafcdda/cdda_control.cpp
@@ -0,0 +1,184 @@
+/*
+ generic interactive controller
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+// Set for local include
+#define DIRECT_INCLUDE
+
+#include "../yafcore/yaf_control.h"
+#include "../yafxplayer/inputDecoderYAF.h"
+
+
+
+
+
+
+#include <iostream>
+using namespace std;
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#if defined(HAVE_GETOPT_H)
+#include <getopt.h>
+#endif
+
+// we include our plugin here
+#include "../../../lib/decoder/cddaPlugin.h"
+
+#if defined(HAVE_GETOPT_H)
+#include <getopt.h>
+#endif
+
+
+
+
+
+void control_divx(InputInterface* input,OutputInterface* output,
+ InputDecoder* decoder) {
+
+
+ cout<< "Command:0 Msg:protocol yaf-0.1" << endl;
+ cout<< "Command:0 Msg:implements xplayer" << endl;
+ cout<< "Command:0 Msg:decoder cdparanoia " << endl;
+ cout<< "Command:0 Msg:mimetypes audio/cdda;" << endl;
+ cout<< "Command:0 Msg:comment cdparanoia by Monty <xiphmont@mit.edu>" << endl;
+ cout<< "Command:0 Msg:comment yaf port by mvogt@rhrk.uni-kl.de"<<endl;
+ cout<< "Command:0 Msg:comment based on sources from audiocd"<<endl;
+ cout<< "Command:0 Msg:comment enter 'help' " << endl;
+
+
+
+
+
+ yaf_control(input,output,decoder);
+}
+
+
+void usage() {
+ cout << "yaf-cdda is a interactive frontend for the cdda decoder"<<endl;
+ cout << "Usage : yaf-cdda [ahy] [url]"<<endl;
+ cout << endl;
+ cout << "-a : no internal audio device"<<endl;
+ cout << "-h : help"<<endl;
+ cout << "-y : autoplay off"<<endl;
+ cout << endl;
+ cout << "THIS SOFTWARE COMES WITH ABSOLUTELY NO WARRANTY! " \
+ << "USE AT YOUR OWN RISK!"<<endl;
+ cout << endl;
+ cout << "for more help type 'help' in interactive mode"<<endl;
+}
+
+
+
+
+int main(int argc, char** argv) {
+ int c;
+ int lInternalAudio=true;
+
+ pow(6.0,3.0); // fixes bug in __math.h
+ InputInterface input;
+ OutputInterface output(&cout);
+
+
+ YafOutputStream* yafOutput=new YafOutputStream(&input);
+ CDDAPlugin* plugin=new CDDAPlugin();
+
+ plugin->setOutputPlugin(yafOutput);
+ InputDecoderYAF decoder(plugin,yafOutput);
+
+
+
+ while(1) {
+ c = getopt (argc, argv, "2al:L:scb:mhpywd:");
+ if (c == -1) break;
+ switch(c) {
+ case 'a': {
+ lInternalAudio=false;
+ break;
+ }
+ case 'h': {
+ usage();
+ exit(0);
+ }
+ case 'y': {
+ decoder.setAutoPlay(false);
+ break;
+ }
+ case '2': {
+ plugin->config("-2","true",NULL);
+ break;
+ }
+ case 'm': {
+ plugin->config("-m","true",NULL);
+ break;
+ }
+ case 'c': {
+ plugin->config("-c","true",NULL);
+ break;
+ }
+ case 'w': {
+ plugin->config("-w","true",NULL);
+ break;
+ }
+ case 's': {
+ yafOutput->config("-s","true",NULL);
+ break;
+ }
+ case 'b': {
+ yafOutput->config("-b",optarg,NULL);
+ break;
+ }
+ case 'd': {
+ yafOutput->config("yufDump",optarg,NULL);
+ break;
+ }
+ case 'p': {
+ yafOutput->config("-p",optarg,NULL);
+ break;
+ }
+ case 'l': {
+ plugin->config("AudioLayer",optarg,NULL);
+ cout << "trying to configure" << endl;
+ break;
+ }
+ case 'L': {
+ plugin->config("VideoLayer",optarg,NULL);
+ break;
+ }
+
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ usage();
+ exit(-1);
+ }
+ }
+
+ if (optind < argc ) {
+ Buffer buffer(300);
+ buffer.append("open ");
+ buffer.append(argv[optind]);
+ buffer.append("\n");
+ input.addInputLine(&buffer);
+
+ }
+ yafOutput->internalDevice(lInternalAudio);
+ control_divx(&input,&output,&decoder);
+ delete plugin;
+ delete yafOutput;
+
+}
+
+
diff --git a/mpeglib/example/yaf/yafcore/Makefile.am b/mpeglib/example/yaf/yafcore/Makefile.am
new file mode 100644
index 00000000..95119cb5
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/Makefile.am
@@ -0,0 +1,44 @@
+# libyafshared - Makefile.am
+
+INCLUDES = -I../../include $(all_includes)
+
+EXTRA_DIST = PROTOCOL.v01 yafCommand.defs yafRuntime.defs
+
+
+lib_LTLIBRARIES = libyafcore.la
+
+noinst_HEADERS = commandTableYAF.h multiReader.h \
+ runtimeTableYAF.h \
+ outputDecoder.h \
+ commandLine.h \
+ commandTable.h lineStack.h \
+ parser.h
+
+
+libyafcore_la_SOURCES = buffer.cpp \
+ lineStack.cpp \
+ commandLine.cpp \
+ parser.cpp \
+ outputInterface.cpp \
+ inputInterface.cpp \
+ commandTable.cpp \
+ commandTableYAF.cpp \
+ runtimeTableYAF.cpp \
+ inputDecoder.cpp \
+ outputDecoder.cpp \
+ yaf_control.cpp \
+ multiReader.cpp
+
+libyafcore_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) \
+ -no-undefined -avoid-version
+
+libyafcore_la_LIBADD = -lkdefakes $(THIS_LIB_LIBS)
+
+
+
+
+yafcoredir = $(includedir)/$(THIS_LIB_NAME)/util/yaf/yafcore
+
+yafcore_HEADERS = inputInterface.h outputInterface.h \
+ buffer.h inputDecoder.h yaf_control.h
+
diff --git a/mpeglib/example/yaf/yafcore/PROTOCOL.v01 b/mpeglib/example/yaf/yafcore/PROTOCOL.v01
new file mode 100644
index 00000000..fce6f1c5
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/PROTOCOL.v01
@@ -0,0 +1,243 @@
+
+This text describes how the interactive interface for mpg123 works.
+******************************************************************
+
+
+Interaction means, that the user can enters commands during
+the mpg123 decoder is running.
+For example, he can stops decoding and can jump to a special
+framenumber.
+
+The current frontends (e.g. tk3play) already allow this, but they are
+a bit difficult for a user because he must enter
+numbers for special command instead of a more
+readable command like "open xyz.mp3"
+
+Then the tk3 interface can't produce return codes for the commands
+In this interface every command the user
+sends to the decoder produces a return Message.
+(for now this messages always defaults to "success")
+
+Why the return codes?
+This allows a controlling GUI (which is a "user" in some way)
+to get a notification if e.g. the file cannot be opened
+by the decoder (wrong format, etc...)
+
+How does it work?
+****************
+Every command the user enters on stdin (cin) produces a return code.
+Thus if the user (or a GUI) sends "open xyz" he gets
+a message whether this succeded or not.
+
+
+E.g:
+
+The user starts mpg123 in interacive mode and enters the first command:
+
+>open foo.mp3
+
+This command and is internally converted to the string:
+
+"Command:101 Msg:open foo.mp3"
+
+The control then writes to stdout :
+"Command:101 Ret:(file not found) Msg:open foo.mp3"
+
+
+The brackets behind "Ret:" belongs to the protocol
+they are delimeters for the return string.
+
+Or, in the case the command was successfull, the user enters :
+play
+
+is translated to
+"Command:102 Msg:play"
+return is:
+"Command:102 Ret:(ok) Msg:play"
+
+
+For what is the "Command:xyz" string good for?
+**********************************************
+
+This string is the current Command number. If a controlling
+GUI sends "Command:x Msg:open foo.mp3" it can search the outout
+from mpg123 for a string : "Command:x ........" This
+ist the return value of the decoder for the command "open foo.mp3"
+
+The command-number must be unique >= 40.
+All Command < 40 are runtime Commands.
+Runtime Commands always have the format :
+
+Command:<nr> Msg: <...>
+
+Example for runtime Commands:
+++++++++++++++++++++++++++++
+
+In the output stream of mpg123 there may be different message.
+For example mpg123 produces runtime information (time, currentframe or
+something like this).
+
+"Command:0 Msg:info1 p I 2.0 III 22050 Joint-Stereo 2 64 209"
+"Command:0 Msg:info1 p I 2.0 III 22050 Joint-Stereo 2 64 209"
+"Command:0 Msg:info1 p I 2.0 III 22050 Joint-Stereo 2 64 209"
+
+The Command number zero means that this output does not belong
+to any special input command.(The part after Msg: is decoder specific)
+
+There are more pre-definied Command Numbers:
+Nr Description Example
+
+0 : Runtime info in general Command:0 Msg:live long and prosper->SIGSEV
+1 : Debug info Command:1 Msg:search the bug
+
+Note:
+The debug Command is the only command which has *no* identifer
+after Msg: All other Commands < 40 needs a Message-identifer after
+Msg.
+In the example for Command 0 the Message identifer is "life"
+and the "real" Message is "long and prosper->SIGSEV".
+This mechanism allows to extend the runtime info sytem only
+with identifiers and not with numbers
+Why?
+Because the protocol should be easy to read.
+(One of the ideas behind this protocol)
+Eg:
+
+Command:0 Msg:currentFrame 23
+is more "readable" than an extension with numbers
+eg:
+Command:0 Msg:2 23
+
+In this example "2" mean that the 23 is the currentFrame Number.
+
+
+
+
+Runtime Commands may be processed by the frontend:
++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Maybe there are errors during playing:
+
+"Command:0 Msg:exception p internal error- ignoring RIFF Header"
+
+The frontend checks the kind of runtime command:
+
+Frontend knows that there was an error(exception) but the status says (p)
+that the decoder continues playing (p)
+and for a more precise description (for the user)
+"error- ignoring RIFF Header"
+
+
+Back to the question:
+++++++++++++++++++++
+The frontend cannot be sure that the next message by the decoder
+is the return message of the previous sended command "open foo.mp3"
+This is the reason for the command number. They synchonise the
+input (open foo.mp3) with the output.
+With a command number the GUI can filter the output for the return code.
+
+
+
+Restriction of the proctocol:
+****************************
+You cannot send binary data because a "\n" is interpreted as
+something like "end of input"
+This means that you cannot open a file in the mpg decoder
+which has "\n" in its name.
+Hm. Is this a restriction? Yes.
+An alternative would be to extend the Protocol to something
+like :
+
+Size:<Integer> Command:... Msg....
+
+(Maybe in the future)
+
+
+
+How is this implemented?
+*************************
+
+
+In the source there are two classes:
+
+* InputInterface
+* OutputInterface
+
+
+First Example
+*************
+
+Let's say we want to write a Command-Line-Frontend for mpg123,
+which is included in the mpg123 source.
+(see the file: mpg123_control.c)
+
+What we must do?
+The Input for the decoder is the command by the user and
+the output is the output by the decoder.
+
+The user should not have to write the real syntax of the
+protocol. Thus he should not write:
+
+Command:1 Msg:open file.mp3
+
+But simply : open file.mp3
+
+The Inputinterface must translate the wrong input
+(in the view of the protocol) into the right
+protocolSyntax.
+(here: Command:<xyz> Msg:open file.mp3)
+
+This input is then passed to the decoder.
+The decoder translates the part after "Msg:" and
+trys to open file.mp3, but in this example he cannot find it.
+Now he can send the return value with the valid Command number.
+Command:<xyz> Ret:(file not found) Msg:open file.mp3
+
+When the decoder returns from the "translations"
+we send the return-Msg (stored in the decoder)
+to the outputstream.
+For this we set in the Outputinterface the current Command number
+And then we send the return message and again the command
+which produced the return message.
+
+"Command:<Number> Ret:(file not found) Msg:eopn foo.mp3"
+
+Second Example
+**************
+
+Write a Gui frontend which starts mpg123 as a seperate process
+(how kmpg does).
+kmpg starts a new mpg123 with the compiled in yaf-interface.
+the yaf-interface is a commandline interface, but this is
+not what we want. We need another behaviour.
+mpg123 must expect protocol data and not "user written" inputs.
+Thus kmpg must set the mpg123 frontend in another internal state
+which expects protocol data.
+The first command which kmpg sends to the mpg123 frontend
+is : "protocol"
+This sets the InputInterface of mpg123 in a protocol-raw-modus.
+A user can type this on the command line as well, but
+then he must also generate the right protocol syntax.
+
+Then kmpg opens a file and send
+e.g:
+Command:1 Msg:open abc.mp3
+
+Then the mpg123 output is sent back:
+
+Command:1 Ret:(ok) Msg:open abc.mp3
+
+
+DEBUGGING
+*********
+
+In the file InputInterface and OutputInterface are debug switches.
+If they are set the incoming/outgoing
+stream is copied to the files
+instream.dbg
+outstream.dbg
+
+tail -f instream.dbg outstream.dbg
+
+monitors the interaction between a frontend and the decoder.
+
diff --git a/mpeglib/example/yaf/yafcore/README b/mpeglib/example/yaf/yafcore/README
new file mode 100644
index 00000000..c3cfd130
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/README
@@ -0,0 +1,27 @@
+
+yaf - <y>et <a>nother <f>rontend
+
+yaf is a simple protocol for communication over streams.
+
+Its purpose is to offer an extensible interface
+for the communitcation between the kmpg GUI and the mpg123 decoder.
+
+
+DESCRIPTION
+
+The basic idea is, that the GUI and the decoder (splay) work
+together over their file streams (cin,cout)
+This means, that kmpg sends string-messages to "stdout"
+and splay receive these messages as "stdin".
+The results of splay (runtime Information, error
+codes) are send to "stdout" and kmpg receive them as "stdin"
+
+The protocol is "human readable" just enter "help"
+or "help <command>" on the prompt and you will see.
+These directory includes classes for dealing with this
+text protocol. In normal developement you can completly
+ingore these classe.
+
+For more info look in the file PROTOCOL
+
+
diff --git a/mpeglib/example/yaf/yafcore/buffer.cpp b/mpeglib/example/yaf/yafcore/buffer.cpp
new file mode 100644
index 00000000..3c58f0ee
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/buffer.cpp
@@ -0,0 +1,156 @@
+/*
+ This class implements a dynamic string buffer
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "buffer.h"
+
+
+
+Buffer::Buffer(int size) {
+ nSize=size;
+ msg=(char*) malloc(sizeof(char)*(nSize+1));
+ msg[nSize]='\0';
+ clear();
+
+}
+
+
+Buffer::~Buffer() {
+ free (msg);
+}
+
+
+void Buffer::clear() {
+ msg[0]='\0';
+}
+
+void Buffer::append(int value) {
+ Buffer buf(30);
+ sprintf(buf.getData(),"%d",value);
+ append(buf.getData());
+}
+
+
+
+
+void Buffer::append(char* appendMsg) {
+ char* appendPos=getAppendPos();
+ int nlen=strlen(appendMsg);
+ if (appendPos == NULL) return;
+
+ append(appendMsg,nlen);
+}
+
+
+void Buffer::append(const char* appendMsg) {
+ append((char*)appendMsg);
+}
+
+
+void Buffer::append(char* buffer,int buflen) {
+ int nlen=len();
+ int nBedarf;
+
+ if (buflen+nlen <= nSize) {
+ char* appendPos=getAppendPos();
+ strncpy(appendPos,buffer,buflen);
+ appendPos[buflen]='\0';
+ return;
+ }
+ nBedarf=(nlen+buflen)-nSize;
+ grow(nBedarf);
+ append(buffer,buflen);
+}
+
+char* Buffer::getAppendPos() {
+ int i;
+ // this Array has nSize+1 entries!
+ // and it *is* granted that msg[nSize]=0; (think so)
+ for (i=0;i<=nSize;i++) {
+ if (msg[i] == '\0') return &(msg[i]);
+ }
+ // should never reach this point
+ return NULL;
+}
+
+
+void Buffer::setData(char* msg) {
+ if (strlen(msg) == 0) {
+ clear();
+ return;
+ }
+ clear();
+ append(msg);
+}
+
+char* Buffer::getData() {
+ return msg;
+}
+
+
+int Buffer::len() {
+ return strlen(msg);
+}
+
+int Buffer::getSize() {
+ return nSize;
+}
+
+void Buffer::grow(int size) {
+ int i;
+ int newSize=nSize+size;
+ char* tmp=(char*) malloc(sizeof(char)*(newSize+1));
+ tmp[newSize]='\0';
+ for(i=0;i<=nSize;i++) {
+ tmp[i]=msg[i];
+ }
+
+ nSize=newSize;
+ free(msg);
+ msg=tmp;
+
+}
+
+
+int Buffer::find(char zeichen) {
+ int i;
+ int nlen=len();
+ for(i=0;i<nlen;i++) {
+ if (msg[i] == zeichen) return i;
+ }
+ return -1;
+}
+
+
+
+void Buffer::forward(int bytes) {
+ int i;
+ int aktPos;
+ int nlen=len();
+ if (bytes > nlen) {
+ bytes=nlen;
+ }
+ i=0;
+ aktPos=bytes;
+ while(aktPos <= nlen) {
+ msg[i]=msg[aktPos];
+ i++;
+ aktPos++;
+ }
+}
+
+
+
+void Buffer::print() {
+ printf("Buffer:%s\n",msg);
+}
diff --git a/mpeglib/example/yaf/yafcore/buffer.h b/mpeglib/example/yaf/yafcore/buffer.h
new file mode 100644
index 00000000..7c1a3d5c
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/buffer.h
@@ -0,0 +1,64 @@
+/*
+ This class implements a static string buffer
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __BUFFER_H
+#define __BUFFER_H
+
+
+extern "C" {
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+ }
+#include <kdemacros.h>
+
+class KDE_EXPORT Buffer {
+
+ char* msg;
+ int nSize;
+
+ public:
+ Buffer(int size);
+ ~Buffer();
+
+ void clear();
+ void append(int value); // appends values as string
+ void append(char* msg);
+ void append(const char* msg);
+
+ void append(char* buffer,int buflen);
+
+ int find(char zeichen);
+ int len();
+
+ void setData(char* msg);
+ char* getData();
+
+ int getSize();
+ void grow(int size);
+
+ void forward(int bytes);
+ void print();
+ private:
+ char* getAppendPos();
+ void read(FILE stream);
+
+};
+
+
+#endif
+
+
+
diff --git a/mpeglib/example/yaf/yafcore/commandLine.cpp b/mpeglib/example/yaf/yafcore/commandLine.cpp
new file mode 100644
index 00000000..e3ae8252
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/commandLine.cpp
@@ -0,0 +1,86 @@
+/*
+ An abstraction of a Command-Line
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "commandLine.h"
+#include <iostream>
+using namespace std;
+
+CommandLine::CommandLine(){
+ int i;
+ for (i=0; i < 10; i++) {
+ command[i].identifier=new Buffer(20);
+ command[i].value=new Buffer(100);
+ }
+ commandCount=0;
+
+}
+
+CommandLine::~CommandLine(){
+
+ int i;
+ for (i=0; i < 10; i++) {
+ delete command[i].value;
+ delete command[i].identifier;
+
+ }
+
+}
+
+
+int CommandLine::getCommandCount() {
+ return commandCount;
+}
+
+void CommandLine::setCommandCount(int commandCount){
+ this->commandCount=commandCount;
+}
+
+
+char* CommandLine::getIdentifier(int i){
+ return (command[i].identifier)->getData();
+}
+
+void CommandLine::clear() {
+ int i;
+ for (i=0; i < 10; i++) {
+ command[i].identifier->clear();
+ command[i].value->clear();
+ }
+ commandCount=0;
+}
+
+char* CommandLine::getValue(int i) {
+ return (command[i].value)->getData();
+}
+
+void CommandLine::setIdentifier(int i,char* identifier){
+ (command[i].identifier)->setData(identifier);
+}
+
+
+void CommandLine::setValue(int i,char* value) {
+ (command[i].value)->setData(value);
+}
+
+void CommandLine::printCommand() {
+ int i;
+ for(i=0;i<commandCount;i++) {
+ cout << "Command:" << i << "\n";
+ cout << "identifier:" << (command[i].identifier)->getData() << "\n";
+ cout << "value:" << (command[i].value)->getData() << "\n";
+ }
+}
+
+
+
diff --git a/mpeglib/example/yaf/yafcore/commandLine.h b/mpeglib/example/yaf/yafcore/commandLine.h
new file mode 100644
index 00000000..677c1018
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/commandLine.h
@@ -0,0 +1,60 @@
+/*
+ An abstraction of a Command-Line
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+
+#ifndef __BASICMESSAGE_H
+#define __BASICMESSAGE_H
+
+
+#include "buffer.h"
+
+
+struct BasicMessageStruct {
+
+ Buffer* identifier;
+ Buffer* value;
+
+};
+
+typedef struct BasicMessageStruct tBasicMessage;
+
+
+class CommandLine {
+
+
+ int commandCount;
+ tBasicMessage command[40];
+
+ public:
+
+ CommandLine();
+ ~CommandLine();
+
+ int getCommandCount();
+ void setCommandCount(int commandCount);
+
+ char* getIdentifier(int i);
+ char* getValue(int i);
+
+ void setIdentifier(int i,char* identifier);
+ void setValue(int i,char* value);
+
+ void printCommand();
+ void clear();
+};
+
+#endif
+
+
diff --git a/mpeglib/example/yaf/yafcore/commandTable.cpp b/mpeglib/example/yaf/yafcore/commandTable.cpp
new file mode 100644
index 00000000..10e36dc3
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/commandTable.cpp
@@ -0,0 +1,256 @@
+/*
+ valid Commands for Input/Output
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "commandTable.h"
+#include <iostream>
+
+using namespace std;
+
+
+CommandTable::CommandTable(){
+ nCommandDesc=0;
+}
+
+CommandTable::~CommandTable(){
+}
+
+
+const char* CommandTable::getCommand(int nr) {
+ int i;
+ const char* back="";
+ for(i=0;i<nCommandDesc;i++) {
+ if (commandDesc[i].number == nr) {
+ return commandDesc[i].longName;
+ }
+ }
+ return back;
+}
+
+
+const char* CommandTable::getCommand(const char* name) {
+ int i;
+ int comp;
+ unsigned int nlen;
+ const char* back="";
+ for(i=0;i<nCommandDesc;i++) {
+ nlen=strlen(commandDesc[i].longName);
+ comp=strncmp(commandDesc[i].longName,name,nlen);
+ if (comp == 0) {
+ if (strlen(name) == nlen) {
+ return commandDesc[i].longName;
+ } else if ((strlen(name) > nlen) && (name[nlen]==' ')) {
+ return commandDesc[i].longName;
+ }
+ }
+
+ if (strlen(commandDesc[i].shortName) > 0) {
+ nlen=strlen(commandDesc[i].shortName);
+ comp=strncmp(commandDesc[i].shortName,name,nlen);
+
+ if (comp == 0) {
+ if (strlen(name) == nlen) {
+ return commandDesc[i].shortName;
+ } else if ((strlen(name) > nlen) && (name[nlen]==' ')) {
+ return commandDesc[i].shortName;
+ }
+ }
+ }
+ }
+ return back;
+}
+
+
+int CommandTable::getNr(const char* command) {
+ int i;
+ int comp;
+ int back=-1;
+ unsigned int nlen;
+ for(i=0;i<nCommandDesc;i++) {
+ nlen=strlen(commandDesc[i].longName);
+ comp=strncmp(commandDesc[i].longName,command,nlen);
+ if (comp == 0) {
+ if (strlen(command) == nlen) {
+ return commandDesc[i].number;
+ } else if ((strlen(command) > nlen) && (command[nlen]==' ')) {
+ return commandDesc[i].number;
+ }
+ }
+
+ if (strlen(commandDesc[i].shortName) > 0) {
+ nlen=strlen(commandDesc[i].shortName);
+ comp=strncmp(commandDesc[i].shortName,command,nlen);
+
+ if (comp == 0) {
+ if (strlen(command) == nlen) {
+ return commandDesc[i].number;
+ } else if((strlen(command) > nlen) && (command[nlen]==' ')){
+ return commandDesc[i].number;
+ }
+ }
+ }
+ }
+ return back;
+}
+
+
+const char* CommandTable::getArgs(const char* command,const char* wholeLine) {
+ unsigned int i;
+ unsigned int n;
+ const char* back;
+ back=wholeLine;
+ n=strlen(command);
+ if (n==0) return back;
+ for(i=0;i<n;i++) {
+ back++;
+ }
+ // we must take care that after every command there may be a
+ // space
+ if (strlen(wholeLine) > n) {
+ back++;
+ }
+ return back;
+}
+
+
+
+void CommandTable::print() {
+ int i;
+ cout << "internal Help System V. 0.2\n";
+ cout << "known commands are :\n\n";
+ for(i=0;i<nCommandDesc;i++) {
+ print(commandDesc[i].number,false);
+ }
+}
+
+
+int CommandTable::getPos(int commandNr) {
+ int i;
+ int pos=-1;
+ for (i=0;i<nCommandDesc;i++) {
+ if (commandDesc[i].number == commandNr) {
+ pos=i;
+ break;
+ }
+ }
+ return pos;
+}
+
+void CommandTable::print (int command,int lWithHelp) {
+ int i=getPos(command);
+ if (i<0) {
+ cout << "unknown Command\n";
+ return;
+ }
+ if (commandDesc[i].lexternalUse == false) return;
+
+ cout << commandDesc[i].longName << "(" ;
+ if (strlen(commandDesc[i].shortName) == 0) {
+ cout << "No";
+ } else {
+ cout << commandDesc[i].shortName;
+ }
+ cout << ") Nr :" << commandDesc[i].number <<" ";
+ if (lWithHelp==true) {
+ cout << commandDesc[i].help ;
+ }
+ cout <<"\n";
+}
+
+
+
+CommandDescription* CommandTable::getCommandDescription(int i) {
+ return &(commandDesc[i]);
+}
+
+
+
+int CommandTable::getCommandCounter() {
+ return nCommandDesc;
+}
+
+
+void CommandTable::join(CommandTable* table) {
+ int i;
+ int n=table->getCommandCounter();
+ CommandDescription* cmdDesc;
+
+ for (i=0;i<n;i++) {
+ cmdDesc=table->getCommandDescription(i);
+ insert(cmdDesc);
+
+ }
+
+}
+
+
+
+
+
+
+
+void CommandTable::insert(CommandDescription* cmdDesc) {
+ const char* lNameTest;
+ const char* sNameTest;
+ int pos=getPos(cmdDesc->number);
+
+ if (pos != -1) {
+ cout << "number "<< cmdDesc->number
+ << " for command "<< cmdDesc->longName << " already defined!" << endl;
+ }
+
+ lNameTest=getCommand(cmdDesc->longName);
+ if (strlen(lNameTest) > 0) {
+ cout << "longName "<< cmdDesc->longName << " already defined."
+ << "Previous definition has number : "
+ << getNr(cmdDesc->longName) << endl;
+ }
+
+ sNameTest=getCommand(cmdDesc->shortName);
+ if (strlen(sNameTest) > 0) {
+ cout << "shortName "<< cmdDesc->shortName << " already defined."
+ << "Previous definition has number : "
+ << getNr(cmdDesc->shortName) << endl;
+
+ }
+
+
+ commandDesc[nCommandDesc].lexternalUse=cmdDesc->lexternalUse;
+ commandDesc[nCommandDesc].lReturn=cmdDesc->lReturn;
+ commandDesc[nCommandDesc].longName=cmdDesc->longName;
+ commandDesc[nCommandDesc].shortName=cmdDesc->shortName;
+ commandDesc[nCommandDesc].number=cmdDesc->number;
+ commandDesc[nCommandDesc].help=cmdDesc->help;
+ nCommandDesc++;
+}
+
+
+int CommandTable::getReturnFlag(int cmdNr) {
+ int i=getPos(cmdNr);
+ CommandDescription* cmdDesc;
+ if (i == -1) {
+ return -1;
+ }
+ cmdDesc=getCommandDescription(i);
+ return cmdDesc->lReturn;
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/example/yaf/yafcore/commandTable.h b/mpeglib/example/yaf/yafcore/commandTable.h
new file mode 100644
index 00000000..75dd4325
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/commandTable.h
@@ -0,0 +1,82 @@
+/*
+ valid Commands for Input/Output
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __COMMANDTABLE_H
+#define __COMMANDTABLE_H
+
+
+extern"C" {
+#include <string.h>
+#include <stdio.h>
+}
+
+#include <kdemacros.h>
+
+#define _CT_START 40
+
+
+
+
+
+
+struct CommandDescriptionStruct {
+
+ int lexternalUse; // written when user enters "help"
+
+ // this field can be used to deliver a delayed return msg
+ int lReturn; // true generate ret-msg automatically
+
+ const char* longName;
+ const char* shortName;
+ int number;
+ const char* help;
+};
+
+
+typedef struct CommandDescriptionStruct CommandDescription;
+
+
+class KDE_EXPORT CommandTable {
+
+ int nCommandDesc;
+ CommandDescription commandDesc[50];
+
+ public:
+ CommandTable();
+ virtual ~CommandTable();
+ void insert(CommandDescription* cmdDesc);
+
+
+ int getNr(const char* name);
+ const char* getCommand(const char* name);
+ const char* getCommand(int nr);
+ const char* getArgs(const char* command,const char* wholeLine);
+
+ int getReturnFlag(int cmdNr);
+ void print();
+ void print (int i,int lWithHelp);
+
+ int getCommandCounter();
+ void join(CommandTable* table); // join two tables (no deep join!)
+
+ private:
+ CommandDescription* getCommandDescription(int i);
+ int getPos(int commandNr);
+};
+
+
+#endif
+
+
+
diff --git a/mpeglib/example/yaf/yafcore/commandTableYAF.cpp b/mpeglib/example/yaf/yafcore/commandTableYAF.cpp
new file mode 100644
index 00000000..d071817b
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/commandTableYAF.cpp
@@ -0,0 +1,36 @@
+/*
+ valid Commands for yaf (basic yaf commands)
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#define _USE_YAF_STRUC
+
+#include "commandTableYAF.h"
+
+
+CommandTableYAF::CommandTableYAF(){
+ init();
+}
+
+CommandTableYAF::~CommandTableYAF(){
+}
+
+void CommandTableYAF::init() {
+
+
+ int i;
+ for (i=0;i<YAFCOMMANDS_SIZE;i++) {
+ insert(&yafCommands[i]);
+ }
+
+
+}
+
diff --git a/mpeglib/example/yaf/yafcore/commandTableYAF.h b/mpeglib/example/yaf/yafcore/commandTableYAF.h
new file mode 100644
index 00000000..fe12793d
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/commandTableYAF.h
@@ -0,0 +1,36 @@
+/*
+ valid Commands for yaf (basic yaf commands)
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __COMMAND_YAF_H
+#define __COMMAND_YAF_H
+
+#include "commandTable.h"
+#include "yafCommand.defs"
+
+
+
+
+
+class CommandTableYAF : public CommandTable {
+
+ public:
+ CommandTableYAF();
+ ~CommandTableYAF();
+ void init();
+
+};
+
+#endif
+
diff --git a/mpeglib/example/yaf/yafcore/inputDecoder.cpp b/mpeglib/example/yaf/yafcore/inputDecoder.cpp
new file mode 100644
index 00000000..e24b7758
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/inputDecoder.cpp
@@ -0,0 +1,237 @@
+/*
+ process Messages on the decoder
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "inputDecoder.h"
+
+#include "commandTable.h"
+#include "commandTableYAF.h"
+#include "yafRuntime.defs"
+#include "commandLine.h"
+#include <iostream>
+
+using namespace std;
+
+InputDecoder::InputDecoder() {
+ commandTable=new CommandTable();
+ yafCommands=new CommandTableYAF();
+ appendCommandTable(yafCommands);
+ status=_DECODER_STATUS_IDLE;
+ lRuntimeInfo=true;
+ returnBuffer= new Buffer(200);
+ returnLine= new Buffer(200);
+
+ commandCounter=-1;
+ commandCounter=-1;
+ commandId=-1;
+ commandCounterString=NULL;
+
+}
+
+
+InputDecoder::~InputDecoder(){
+ delete yafCommands;
+ delete returnBuffer;
+ delete returnLine;
+ delete commandTable;
+}
+
+
+
+void InputDecoder::setRuntimeInfo(int lRuntimeInfo) {
+ this->lRuntimeInfo=lRuntimeInfo;
+}
+
+
+int InputDecoder::getRuntimeInfo() {
+ return lRuntimeInfo;
+}
+
+
+void InputDecoder::appendCommandTable(CommandTable* table){
+ commandTable->join(table);
+}
+
+
+
+
+void InputDecoder::processCommandLine(CommandLine* commandLine){
+ CommandTable* ct=commandTable; // "ct" is shorter
+ commandId=-1;
+
+ // The number of the command (unique for every command)
+ commandCounterString=commandLine->getValue(0);
+ commandCounter=atoi(commandCounterString);
+
+ // the command (longName or shortName )
+ if (commandLine->getCommandCount() == 0) {
+ clearReturnBuffer();
+ appendReturnBuffer("no Msg");
+ }
+ commandMsg=ct->getCommand(commandLine->getValue(1));
+ if ((commandMsg == NULL) || (strlen(commandMsg) == 0)) {
+ clearReturnBuffer();
+ appendReturnBuffer("unknown Command");
+ commandMsg=commandLine->getValue(1);
+
+ return ;
+ }
+
+ // the int value of the command (faster for compare)
+ commandId=ct->getNr(commandMsg);
+
+ // the Arguments of the command
+ commandArgs=ct->getArgs(commandMsg,(const char*) commandLine->getValue(1));
+
+ retString=processCommand(commandId,commandArgs);
+
+ if ((retString == NULL) || (strlen(retString) == 0)) {
+ retString=(char*)"ok";
+ }
+ clearReturnBuffer();
+ appendReturnBuffer(retString);
+}
+
+
+const char* InputDecoder::processCommand(int command,const char* args){
+
+ if (command == _YAF_I_HELP) {
+ if (strlen(args)==0) {
+ commandTable->print();
+ } else {
+ commandTable->print(commandTable->getNr(args),true);
+ }
+ return "";
+ }
+ if (command == _YAF_I_RUNTIME) {
+ if (strcmp("off",args)==0) {
+ setRuntimeInfo(false);
+ return "";
+ }
+ setRuntimeInfo(true);
+ return "";
+ }
+ if (command == _YAF_I_QUIT) {
+ ::exit(0);
+ return "";
+ }
+ if (command == _YAF_I_WHATIS) {
+ cout << "Yaf <y>et <a>nother <f>rontend" << endl;
+ cout << endl;
+ cout << "Yaf is an interactive command line oriented shell for decoders." \
+ << endl;
+ cout << endl;
+ cout << "Copyright (C) 1998,1999 Martin Vogt <mvogt@rhrk.uni-kl.de>"\
+ <<endl;
+ cout << "This program is free software; you can redistribute "\
+ << "it and/or modify"<<endl;
+ cout << "it under the terms of the GNU Library General Public License "\
+ << "as published by"<<endl;
+ cout << "the Free Software Foundation." <<endl;
+
+ cout << "For more information look at the file COPYRIGHT in "\
+ << "this package" <<endl;
+ cout << endl;
+ cout << "THIS SOFTWARE COMES WITH ABSOLUTELY NO WARRANTY! " \
+ << "USE AT YOUR OWN RISK!"<<endl;
+ return "";
+ }
+
+
+ if (command == _YAF_I_PING) {
+ return "";
+ }
+ if (command == _YAF_I_PROTOCOL) {
+ return "";
+ }
+ if (command == _YAF_I_NOPROTOCOL) {
+ return "";
+ }
+
+ return "unknown Command";
+}
+
+
+/**
+ These two functions are entered by the decoder
+ thread [START]
+*/
+
+
+void InputDecoder::doSomething(){
+ cout << "did something"<<endl;
+}
+
+
+
+int InputDecoder::getDecoderStatus() {
+ return status;
+}
+
+
+
+void InputDecoder::setDecoderStatus(int newState) {
+
+ // First start a new thread with start()!
+ // Their is no recovery if the thread status is set to exit!
+ if (status == _DECODER_STATUS_EXIT) {
+ return;
+ }
+ status=newState;
+}
+
+
+
+void InputDecoder::clearReturnBuffer() {
+ returnBuffer->clear();
+}
+
+
+void InputDecoder::appendReturnBuffer(const char* str) {
+ returnBuffer->append(str);
+}
+
+
+
+const char* InputDecoder::getReturnCode() {
+ // now we send back the return code
+
+ if ((commandId != -1) && (commandTable->getReturnFlag(commandId)==false)) {
+ return "";
+ }
+
+ returnLine->clear();
+ returnLine->append("Command:");
+
+
+ // The number of the command (unique for every command)
+ // 0: the command nr
+ returnLine->append(commandCounterString);
+ returnLine->append(" Ret:(");
+ returnLine->append(returnBuffer->getData());
+ returnLine->append(") Msg:");
+
+
+ // now get the part after Msg: (in the inputLine)
+
+ returnLine->append(commandMsg);
+ returnLine->append(" ");
+ returnLine->append(commandArgs);
+
+
+
+ return (const char*)returnLine->getData();
+
+}
+
+
+
diff --git a/mpeglib/example/yaf/yafcore/inputDecoder.h b/mpeglib/example/yaf/yafcore/inputDecoder.h
new file mode 100644
index 00000000..0a5623a0
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/inputDecoder.h
@@ -0,0 +1,80 @@
+/*
+ process Messages on the decoder
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __INPUTDECODER_H
+#define __INPUTDECODER_H
+#include <pthread.h>
+
+#include "outputInterface.h"
+#include <strings.h>
+#include <kdemacros.h>
+
+#define _DECODER_STATUS_IDLE 1
+#define _DECODER_STATUS_WORKING 2
+#define _DECODER_STATUS_EXIT 3
+
+class CommandTable;
+class CommandTableYAF;
+class CommandLine;
+
+class KDE_EXPORT InputDecoder {
+
+ int status;
+ CommandTable* commandTable;
+ CommandTableYAF* yafCommands;
+ Buffer* returnBuffer;
+ Buffer* returnLine;
+ int lRuntimeInfo;
+
+
+
+
+ public:
+ InputDecoder();
+ virtual ~InputDecoder();
+
+ void processCommandLine(CommandLine*);
+ virtual const char* processCommand(int command,const char* args);
+ virtual void doSomething();
+
+ virtual void setDecoderStatus(int status);
+ int getDecoderStatus();
+
+ const char* getReturnCode();
+
+ void appendCommandTable(CommandTable* table);
+
+ void setRuntimeInfo(int lRuntimeInfo);
+ int getRuntimeInfo();
+
+
+ private:
+
+ void clearReturnBuffer();
+ void appendReturnBuffer(const char* msg);
+
+
+ int commandCounter;
+ int commandId;
+ const char* commandMsg;
+ const char* commandArgs;
+ const char* retString;
+ char* commandCounterString;
+
+
+};
+
+#endif
+
diff --git a/mpeglib/example/yaf/yafcore/inputInterface.cpp b/mpeglib/example/yaf/yafcore/inputInterface.cpp
new file mode 100644
index 00000000..455ddd40
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/inputInterface.cpp
@@ -0,0 +1,195 @@
+/*
+ This class can wait for an input by the user
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include <config.h>
+
+#include "inputInterface.h"
+#include "multiReader.h"
+#include <iostream>
+
+using namespace std;
+
+InputInterface::InputInterface(){
+
+ currentLine= new Buffer(300);
+ rawLine=new Buffer(300);
+ loopback=new Buffer(300);
+ protocolSyntax=false;
+ currentCommandNumber=42;
+ multiReader=new MultiReader();
+ yafInput=new ifstream("yaf.script");
+ if (yafInput->fail() == false) {
+ cout << "Command:0 Msg:comment found yaf.script. Parsing first"<<endl;
+ insertYafScript(yafInput);
+ }
+ yafInput->close();
+
+}
+
+InputInterface::~InputInterface(){
+ delete yafInput;
+ delete multiReader;
+ delete currentLine;
+ delete rawLine;
+ delete loopback;
+}
+
+
+int InputInterface::addFileDescriptor(int fd) {
+ int back;
+ back=multiReader->add(fd);
+ return back;
+}
+
+
+void InputInterface::removeFileDescriptor(int fd) {
+ multiReader->remove(fd);
+}
+
+
+int InputInterface::getCurrentCommandNumber() {
+ int back=0;
+ back=currentCommandNumber;
+ return back;
+}
+
+void InputInterface::increaseCurrentCommandNumber(){
+ currentCommandNumber++;
+}
+
+int InputInterface::write(int fd,const char* txt) {
+ int back=0;
+
+ int len;
+ loopback->clear();
+ if (protocolSyntax == true) {
+ snprintf(loopback->getData(),300,
+ "Command:41 Msg:%s",txt);
+ } else {
+ strlcpy(loopback->getData(),txt, loopback->getSize());
+ }
+ len =loopback->len();
+ back=::write(fd,loopback->getData(),len);
+
+ return back;
+}
+
+
+
+void InputInterface::waitForLine() {
+
+ while(multiReader->hasLine() == false) {
+ multiReader->waitForLine();
+ }
+ multiReader->getLine(rawLine);
+ makeValidLine(rawLine->getData());
+
+}
+
+void InputInterface::setProtocolSyntax (int proto) {
+ protocolSyntax=proto;
+}
+
+
+
+void InputInterface::makeValidLine(char* line) {
+
+ int len;
+ len=strlen(line);
+ if (len >= 1) {
+ if (line[len-1] == '\n') {
+ line[len-1]='\0';
+ }
+ }
+ if (strncmp("noprotocol",line,10) == 0){
+ setProtocolSyntax(false);
+ clearLine();
+ increaseCurrentCommandNumber();
+ snprintf(currentLine->getData(),300,
+ "Command:%d Msg:%s",currentCommandNumber,line);
+ return;
+ }
+ if (strncmp("protocol",line,8) == 0 ){
+ setProtocolSyntax(true);
+ clearLine();
+ increaseCurrentCommandNumber();
+ snprintf(currentLine->getData(),300,
+ "Command:%d Msg:%s",currentCommandNumber,line);
+ return;
+ }
+
+ // Now the part if we do _not_ bypass the protocol-state
+
+ if (protocolSyntax == false) {
+ clearLine();
+ increaseCurrentCommandNumber();
+ snprintf(currentLine->getData(),300,
+ "Command:%d Msg:%s",currentCommandNumber,line);
+ } else {
+ increaseCurrentCommandNumber();
+ strlcpy(currentLine->getData(),line, currentLine->getSize());
+ }
+
+ return;
+}
+
+
+void InputInterface::addInputLine(struct Buffer* buffer) {
+ multiReader->add(buffer);
+}
+
+
+void InputInterface::insertYafScript(ifstream* stream) {
+ char bst;
+ int nBytes=0;
+ Buffer yafScriptBuffer(300);
+
+ if (stream->fail()) return;
+ while (stream->eof()==false) {
+ stream->get(bst);
+ if (stream->eof()) break;
+
+ yafScriptBuffer.append(&bst,1);
+ }
+ nBytes=(yafScriptBuffer.len()-1); // EOF is a character we dont want
+
+ addInputLine(&yafScriptBuffer);
+}
+
+
+
+int InputInterface::hasLine() {
+ if (currentLine->len() == 0) return 0;
+ return 1;
+}
+
+void InputInterface::clearLine() {
+ currentLine->clear();
+}
+
+
+char* InputInterface::getLine() {
+ char* back=NULL;
+
+ back=currentLine->getData();
+#ifdef _DEBUG_INPUT
+ ofstream infile("instream.dbg",ios::app);
+ infile << back <<endl;
+ infile.close();
+#endif
+
+
+ return back;
+}
+
+
+
diff --git a/mpeglib/example/yaf/yafcore/inputInterface.h b/mpeglib/example/yaf/yafcore/inputInterface.h
new file mode 100644
index 00000000..f3ea0065
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/inputInterface.h
@@ -0,0 +1,102 @@
+/*
+ This class can wait for an input by the user
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __INPUTINTERFACE_H
+#define __INPUTINTERFACE_H
+
+
+#include "buffer.h"
+#include <fstream>
+#include <kdemacros.h>
+
+extern "C" {
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <stdio.h>
+}
+
+// uncomment this for debugging the input stream
+// (written to file instream.dbg)
+//#define _DEBUG_INPUT
+
+#define _TIMEOUT_MS 2000
+
+class MultiReader;
+
+class KDE_EXPORT InputInterface {
+
+ int currentCommandNumber;
+
+ int protocolSyntax;
+ Buffer* currentLine;
+ Buffer* rawLine;
+ MultiReader* multiReader;
+ Buffer* loopback;
+ Buffer* yafScript;
+ std::ifstream* yafInput;
+
+
+ public:
+
+ InputInterface();
+ ~InputInterface();
+
+ int addFileDescriptor(int fd);
+ void removeFileDescriptor(int fd);
+ int write(int fd,const char* txt);
+
+ /**
+ waits until a complete Line is entered
+ */
+ void waitForLine();
+
+ /**
+ returns true if a complete new InputLine is avaiable
+ */
+
+ int hasLine();
+
+ int getCurrentCommandNumber();
+
+ void increaseCurrentCommandNumber();
+
+ /**
+ clears the input Line
+ */
+ void clearLine();
+
+
+ /**
+ returns the new Line
+ */
+ char* getLine();
+
+
+ void setProtocolSyntax(int proto);
+ void addInputLine(struct Buffer* buffer);
+
+ private:
+ void makeValidLine(char* line);
+ void insertYafScript(std::ifstream* stream);
+
+
+};
+
+#endif
+
+
+
diff --git a/mpeglib/example/yaf/yafcore/lineStack.cpp b/mpeglib/example/yaf/yafcore/lineStack.cpp
new file mode 100644
index 00000000..871665ed
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/lineStack.cpp
@@ -0,0 +1,95 @@
+/*
+ a class which scans a string and converts it into commandLines
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "lineStack.h"
+#include <iostream>
+
+using namespace std;
+
+
+LineStack::LineStack() {
+ stack=new Buffer(1);
+}
+
+
+LineStack::~LineStack() {
+ delete stack;
+}
+
+
+int LineStack::hasLine() {
+ int nPos;
+ nPos=stack->find('\n');
+ if (nPos == -1) return false;
+ return true;
+
+}
+
+
+
+void LineStack::nextLine(Buffer* nextLine) {
+ int nPos;
+ char* retPos;
+ int restLen;
+ char* data=stack->getData();
+ int nSize=stack->getSize();
+
+ nPos=stack->find('\n');
+ if (nPos == -1) {
+ nextLine->clear();
+ return;
+ }
+ retPos=&(data[nPos]);
+ (*retPos)='\0';
+ nextLine->clear();
+ nextLine->setData(data);
+ retPos++;
+ restLen=nSize+1-(nPos+1);
+ if (strlen(retPos) > 0) {
+ strncpy(data,retPos,restLen);
+ } else{
+ stack->clear();
+ }
+}
+
+
+void LineStack::appendBottom(char* buffer) {
+ int n=strlen(buffer);
+ appendBottom(buffer,n);
+}
+
+
+
+void LineStack::appendBottom(char* buffer, int buflen) {
+
+ stack->append(buffer,buflen);
+
+}
+
+void LineStack::appendBottom(LineStack* lStack) {
+ char* data;
+ int len;
+ data=lStack->stack->getData();
+ len=lStack->stack->len();
+ appendBottom(data,len);
+}
+
+
+
+void LineStack::print(char* name) {
+ cout << "LineStack:"<<name<<endl;
+ stack->print();
+}
+
+
diff --git a/mpeglib/example/yaf/yafcore/lineStack.h b/mpeglib/example/yaf/yafcore/lineStack.h
new file mode 100644
index 00000000..aea1f686
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/lineStack.h
@@ -0,0 +1,55 @@
+/*
+ a class which scans a string and converts it into commandLines
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+
+#ifndef __LINESTACK_H
+#define __LINESTACK_H
+
+
+#include "buffer.h"
+
+/*
+ This class is a hack because I have problems with
+ the io-streams. Why these stupid things cannot buffer
+ a whole line?
+ They behave strange, I hate them.
+*/
+
+
+
+class LineStack {
+
+ public:
+ LineStack();
+ ~LineStack();
+
+ void appendBottom(char* buffer);
+ void appendBottom(char* buffer,int buflen);
+ void appendBottom(LineStack* stack);
+
+ int hasLine();
+ void nextLine(Buffer* nextLine);
+
+ void print(char* name);
+ private:
+ char* getReturnPos();
+
+ Buffer* stack;
+};
+
+
+#endif
+
+
diff --git a/mpeglib/example/yaf/yafcore/multiReader.cpp b/mpeglib/example/yaf/yafcore/multiReader.cpp
new file mode 100644
index 00000000..41a62263
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/multiReader.cpp
@@ -0,0 +1,206 @@
+/*
+ This class can waits for input on different istreams
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "multiReader.h"
+
+
+
+MultiReader::MultiReader() {
+ int i;
+
+ buffer=new Buffer(201);
+ for(i=0;i<_MAX_INPUT;i++) {
+ lineInputArray[i]=new LineInput;
+ lineInputArray[i]->tmpLineStack=new LineStack();
+ lineInputArray[i]->empty=true;
+ }
+ script=new LineStack();
+}
+
+
+
+MultiReader::~MultiReader() {
+ int i;
+ for(i=0;i<_MAX_INPUT;i++) {
+ delete lineInputArray[i]->tmpLineStack;
+ delete lineInputArray[i];
+ }
+ delete script;
+}
+
+
+
+int MultiReader::add(int fd) {
+ int nPos;
+ nPos=getEmptySlot();
+
+ if (nPos == -1) {
+ return -1;
+ }
+ lineInputArray[nPos]->fd=fd;
+ lineInputArray[nPos]->empty=false;
+ return nPos;
+}
+
+void MultiReader::add(LineStack* aScript) {
+ script->appendBottom(aScript);
+}
+
+
+void MultiReader::add(Buffer* aScript) {
+ script->appendBottom(aScript->getData(),aScript->len());
+}
+
+
+
+
+void MultiReader::remove (int fd) {
+ int nPos;
+ nPos=getSlot(fd);
+
+ if (nPos == -1) {
+ return ;
+ }
+ lineInputArray[nPos]->empty=true;
+}
+
+
+
+void MultiReader::waitForLine() {
+ while(hasLine() == false) {
+ doSelect(NULL);
+ }
+}
+
+
+void MultiReader::poll(struct timeval* timeout) {
+ doSelect(timeout);
+}
+
+
+
+void MultiReader::doSelect(struct timeval* timeout) {
+ int i;
+ int ret;
+ fd_set readfds;
+ int nBytes;
+ int maxFd=0;
+
+ FD_ZERO(&readfds);
+ for(i=0;i<_MAX_INPUT;i++) {
+ if (lineInputArray[i]->empty == false) {
+ FD_SET(lineInputArray[i]->fd,&readfds);
+ if (lineInputArray[i]->fd > maxFd) {
+ maxFd=lineInputArray[i]->fd;
+ }
+ }
+ }
+ ret=select(maxFd+1,&readfds,NULL,NULL,timeout);
+ if (ret < 0) {
+ if (errno < 0) {
+ perror("nach select multireader:");
+ exit(0);
+ }
+ }
+ if (ret == 0) return;
+
+ for(i=0;i<_MAX_INPUT;i++) {
+ if (lineInputArray[i]->empty == false) {
+ if (FD_ISSET(lineInputArray[i]->fd,&readfds)) {
+ nBytes=read(lineInputArray[i]->fd,buffer->getData(),200);
+ if (nBytes == 0) {
+ perror("MultiReader:read error!");
+ exit(-1);
+ }
+ (buffer->getData())[nBytes]='\0';
+
+ lineInputArray[i]->tmpLineStack->appendBottom(buffer->getData(),
+ nBytes);
+ FD_CLR(lineInputArray[i]->fd,&readfds);
+ }
+ }
+ }
+}
+
+
+
+int MultiReader::hasLine() {
+ int i;
+ LineStack* lineStack; // owned by class
+
+ if (script->hasLine() == true) {
+ return true;
+ }
+
+ for(i=0;i<_MAX_INPUT;i++) {
+ if (lineInputArray[i]->empty == false) {
+ lineStack=lineInputArray[i]->tmpLineStack;
+ if (lineStack->hasLine()) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+void MultiReader::getLine(Buffer* buffer) {
+ int i;
+ LineStack* lineStack; // owned by class
+ buffer->clear();
+
+ if (script->hasLine()==true) {
+ script->nextLine(buffer);
+ } else {
+ for(i=0;i<_MAX_INPUT;i++) {
+ if (lineInputArray[i]->empty == false) {
+ lineStack=lineInputArray[i]->tmpLineStack;
+
+ if (lineStack->hasLine()) {
+ lineStack->nextLine(buffer);
+ return;
+ }
+ }
+ }
+ }
+ buffer->append("\n");
+}
+
+
+
+
+int MultiReader::getEmptySlot() {
+ int i;
+
+ for(i=0;i<_MAX_INPUT;i++) {
+ if (lineInputArray[i]->empty == true) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+int MultiReader::getSlot(int fd) {
+ int i;
+
+ for(i=0;i<_MAX_INPUT;i++) {
+ if (lineInputArray[i]->empty == false) {
+ if (lineInputArray[i]->fd == fd) {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
diff --git a/mpeglib/example/yaf/yafcore/multiReader.h b/mpeglib/example/yaf/yafcore/multiReader.h
new file mode 100644
index 00000000..79f7dd8b
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/multiReader.h
@@ -0,0 +1,88 @@
+/*
+ This class can waits for input on different istreams
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __MULTIREADER_H
+#define __MULTIREADER_H
+
+#include "lineStack.h"
+#include "buffer.h"
+
+extern "C" {
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <strings.h>
+#include <string.h>
+ }
+
+/**
+ The purpose of this class is to have a thread safe input method
+ for yaf command.
+ <p>
+ Sometime the decoder thread must send data to the control thread.
+ E.g. the Stream has ended. The decoder thread must inform the
+ control thread that the player should go into the "off" state.
+ A direct call is not thread safe and this is why the decoder
+ thread must use this class. For the control thread it looks
+ like the user has typed "off".
+
+*/
+
+#define _MAX_INPUT 5
+
+typedef struct {
+ LineStack* tmpLineStack; // owned by class
+ int fd; // filedescriptor for input
+ int empty;
+} LineInput;
+
+
+class MultiReader {
+
+ Buffer* buffer;
+ LineInput* lineInputArray[_MAX_INPUT];
+ LineStack* script;
+
+ public:
+ MultiReader();
+ ~MultiReader();
+
+ int add(int fd);
+ void add(LineStack* aScript);
+ void add(Buffer* aScript);
+ void remove (int fd);
+
+ void waitForLine();
+ void poll(struct timeval* timeout);
+
+ int hasLine();
+ void getLine(Buffer* buffer);
+
+ private:
+
+ void doSelect(struct timeval *timeout);
+
+ int getEmptySlot();
+ int getSlot(int fd);
+
+
+};
+
+
+
+
+
+#endif
diff --git a/mpeglib/example/yaf/yafcore/outputDecoder.cpp b/mpeglib/example/yaf/yafcore/outputDecoder.cpp
new file mode 100644
index 00000000..0993add0
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/outputDecoder.cpp
@@ -0,0 +1,144 @@
+/*
+ Default output decoder
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include "outputDecoder.h"
+#include <iostream>
+
+using namespace std;
+
+#define DEBUG cout << "Command:1 Msg:"
+
+
+OutputDecoder::OutputDecoder() {
+ yafCommands=new CommandTableYAF();
+ yafRuntime=new RuntimeTableYAF();
+
+ appendCommandTable(yafCommands);
+ appendRuntimeTable(yafRuntime);
+
+}
+
+
+
+OutputDecoder::~OutputDecoder() {
+ delete yafCommands;
+ delete yafRuntime;
+}
+
+
+
+void OutputDecoder::appendCommandTable(CommandTable* table) {
+ commandTable.join(table);
+}
+
+
+void OutputDecoder::appendRuntimeTable(CommandTable* table) {
+ runtimeTable.join(table);
+}
+
+
+
+CommandTable* OutputDecoder::getCommandTable() {
+ return &commandTable;
+}
+
+
+CommandTable* OutputDecoder::getRuntimeTable() {
+ return &runtimeTable;
+}
+
+
+
+
+void OutputDecoder::processCommandLine(CommandLine* commandLine){
+ CommandTable* ct=&commandTable; // ct , rt are shorter..
+ CommandTable* rt=&runtimeTable;
+
+
+ int command;
+ int commandNr;
+ const char* commandStr;
+ const char* retString;
+ const char* args;
+
+
+ // The number of the command (unique for every command)
+ commandNr=atoi(commandLine->getValue(0));
+
+ // if commandNr is greater zero then we have a return command from decoder
+ if (commandNr >= 40) {
+
+ // the command (longName or shortName )
+ commandStr=ct->getCommand(commandLine->getValue(2));
+ // the int value of the command (faster for compare)
+ command=ct->getNr(commandStr);
+
+ // the Arguments of the command
+ args=ct->getArgs(commandStr,commandLine->getValue(2));
+ retString=commandLine->getValue(1);
+
+ processReturnCommand(commandNr,command,retString,args);
+ return;
+ }
+
+ // if commandNr < 40 then we have a runtime command from decoder
+
+ if (commandNr < 40) {
+ // the command (longName or shortName )
+ commandStr=rt->getCommand(commandLine->getValue(1));
+ // the int value of the command (faster for compare)
+ command=rt->getNr(commandStr);
+
+ // here I make a hack because it cannot be expected
+ // that during debugging every debug-info has its clean
+ // installed debug-identifer.
+ // hack:
+ // if the commandNr==1 (debug) and we have NO defined
+ // debug identifier then we do not send -1 (as in all other case)
+ // but the number 1 itsself.
+
+ if ((commandNr == 1) && (command == -1)) {
+ args=rt->getArgs(commandStr,commandLine->getValue(1));
+ processRuntimeCommand(1,args);
+ return;
+ }
+
+ // in *all* other cases the developer should
+ // implement a clean protokoll with identifiers
+ // (this leads to well defined interfaces :-)
+
+ // the Arguments of the command
+ args=rt->getArgs(commandStr,commandLine->getValue(1));
+
+ processRuntimeCommand(command,args);
+ return;
+ }
+
+
+}
+
+
+
+int OutputDecoder::processRuntimeCommand(int command,const char* args){
+ cout << command <<" * "<< args <<" * "<< endl;
+ return false;
+}
+
+int OutputDecoder::processReturnCommand(int cmdNr,int cmdId,
+ const char* ret,const char* args){
+ cout << cmdNr <<" * "<< cmdId<<" * "<< ret<<" * " << args << endl;
+ return false;
+}
+
+
+
+
diff --git a/mpeglib/example/yaf/yafcore/outputDecoder.h b/mpeglib/example/yaf/yafcore/outputDecoder.h
new file mode 100644
index 00000000..1448258b
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/outputDecoder.h
@@ -0,0 +1,52 @@
+/*
+ Default output decoder
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __OUTPUTDECODER_H
+#define __OUTPUTDECODER_H
+
+#include "commandLine.h"
+
+#include "runtimeTableYAF.h"
+#include "commandTableYAF.h"
+
+
+class OutputDecoder {
+
+ public:
+ OutputDecoder();
+ virtual ~OutputDecoder();
+
+ void processCommandLine(CommandLine*);
+ virtual int processRuntimeCommand(int command,const char* args);
+ virtual int processReturnCommand(int cmdNr,int cmdId,
+ const char* ret,const char* args);
+
+ void appendCommandTable(CommandTable* table);
+ void appendRuntimeTable(CommandTable* table);
+
+ CommandTable* getRuntimeTable();
+ CommandTable* getCommandTable();
+
+ private:
+ CommandTable* yafCommands;
+ CommandTable* yafRuntime;
+
+ CommandTable commandTable;
+ CommandTable runtimeTable;
+
+
+};
+
+
+#endif
diff --git a/mpeglib/example/yaf/yafcore/outputInterface.cpp b/mpeglib/example/yaf/yafcore/outputInterface.cpp
new file mode 100644
index 00000000..b57563fd
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/outputInterface.cpp
@@ -0,0 +1,85 @@
+/*
+ This class sends an output to the outputstream
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "outputInterface.h"
+#include <iostream>
+
+using namespace std;
+
+OutputInterface::OutputInterface(ostream* out) {
+ protocolSyntax=false;
+ this->out=out;
+ outBuffer=new Buffer(250);
+ setlinebuf(stdout);
+ pthread_mutex_init(&writeOutMut,NULL);
+}
+
+
+OutputInterface::~OutputInterface() {
+ delete outBuffer;
+}
+
+void OutputInterface::flushBuffer() {
+ if (protocolSyntax) {
+ (*out) << outBuffer->getData() << endl;
+#ifdef _DEBUG_OUTPUT
+ ofstream outfile("outstream.dbg",ios::app);
+ outfile << outBuffer->getData() << endl;
+ outfile.flush();
+ outfile.close();
+#endif
+ return;
+ } else {
+ (*out) << "Command:" << nr << " Msg:" << outBuffer->getData() << endl;
+#ifdef _DEBUG_OUTPUT
+ ofstream outfile("outstream.dbg",ios::app);
+ outfile << "Command:" << nr << " Msg:" << outBuffer->getData() << endl;
+ outfile.flush();
+ outfile.close();
+#endif
+ }
+ fflush(NULL);
+}
+
+
+
+void OutputInterface::setProtocolSyntax(int proto) {
+ protocolSyntax=proto;
+}
+
+
+void OutputInterface::setNr(int nr) {
+ this->nr=nr;
+}
+
+void OutputInterface::clearBuffer() {
+ outBuffer->clear();
+}
+
+
+void OutputInterface::appendBuffer(const char* msg) {
+ outBuffer->append(msg);
+}
+
+
+
+void OutputInterface::lock() {
+ pthread_mutex_lock(&writeOutMut);
+}
+
+
+void OutputInterface::unlock() {
+ pthread_mutex_unlock(&writeOutMut);
+}
+
diff --git a/mpeglib/example/yaf/yafcore/outputInterface.h b/mpeglib/example/yaf/yafcore/outputInterface.h
new file mode 100644
index 00000000..0a24fd92
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/outputInterface.h
@@ -0,0 +1,60 @@
+/*
+ This class sends an output to the outputstream
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+
+
+#ifndef __OUTPUTINTERFACE_H
+#define __OUTPUTINTERFACE_H
+
+#include <pthread.h>
+#include <fstream>
+#include "buffer.h"
+#include <kdemacros.h>
+
+// uncomment this for debugging the output stream
+// (written to file outstream.dbg)
+//#define _DEBUG_OUTPUT
+
+
+class KDE_EXPORT OutputInterface {
+
+ int protocolSyntax;
+ int nr;
+ Buffer* outBuffer;
+ std::ostream* out;
+ pthread_mutex_t writeOutMut;
+
+
+ public:
+
+ OutputInterface(std::ostream* out);
+ ~OutputInterface();
+
+
+ void flushBuffer();
+ void setProtocolSyntax(int proto);
+ void setNr(int nr);
+ void clearBuffer();
+ void appendBuffer(const char* msg);
+
+ // make this interface threadsafe
+ void lock();
+ void unlock();
+
+};
+
+
+
+#endif
diff --git a/mpeglib/example/yaf/yafcore/parser.cpp b/mpeglib/example/yaf/yafcore/parser.cpp
new file mode 100644
index 00000000..21b27c6b
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/parser.cpp
@@ -0,0 +1,152 @@
+/*
+ Checks if a valid Command Line is avaiable
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "parser.h"
+
+
+Parser::Parser() {
+ commandLine=new CommandLine();
+ parseString=new Buffer(200);
+}
+
+
+Parser::~Parser() {
+ delete commandLine;
+ delete parseString;
+}
+
+void Parser::setParseString(char* parseString){
+ (this->parseString)->setData(parseString);
+ // clear everything in commandline
+ commandLine->clear();
+}
+
+void Parser::parse() {
+ int commandCounter=0;
+
+ parse(parseString->getData(),&commandCounter);
+ commandLine->setCommandCount(commandCounter);
+}
+
+
+void Parser::parse(char* strStart,int* nCommand){
+ char* doppelPkt;
+ char* current;
+ char* space;
+
+ if (strlen(strStart) == 0) return;
+
+ doppelPkt=strchr(strStart,':');
+ if (doppelPkt == NULL) return;
+ (*doppelPkt)='\0';
+ commandLine->setIdentifier(*nCommand,strStart);
+ strStart=++doppelPkt;
+ current=strStart;
+
+ // Now two possibilities:
+ // a command with Msg : means the rest ist the value
+ // a command different from Msg means: further processing
+
+ if (strcmp("Msg",commandLine->getIdentifier(*nCommand)) == 0) {
+ commandLine->setValue((*nCommand),current);
+ (*nCommand)++;
+ return;
+ }
+
+ // Now two possibilities:
+ // a command with "Ret:" means string until the the ")" is the value
+ // a command different from "Ret:" means: the string until the first space
+ // ist the value
+ if (strcmp("Ret",commandLine->getIdentifier(*nCommand)) == 0) {
+ char* seperator;
+ seperator=strchr(current,')');
+ if (seperator == NULL) {
+ commandLine->setValue((*nCommand),current);
+ (*nCommand)++;
+ return;
+ } else {
+ (*seperator)='\0';
+ current++;
+ commandLine->setValue((*nCommand),current);
+ (*nCommand)++;
+ seperator++;
+ if (strlen(seperator) == 0) return;
+ parse(++seperator,&(*nCommand));
+ return;
+ }
+ }
+
+ space=strchr(current,' ');
+ if (space == NULL) {
+ commandLine->setValue((*nCommand),current);
+ (*nCommand)++;
+ return;
+ }
+ (*space)='\0';
+ commandLine->setValue((*nCommand),current);
+ parse(++space,&(++(*nCommand)));
+}
+
+
+
+
+int Parser::isOK(){
+ // a Commandline ist valid when it contains :
+
+ // * 2 * (identifer/value)
+ // * first identifier ist "Command"
+ // * second identifier is "Msg"
+
+
+ // or:
+ // 3 identifier
+ // first: "Command"
+ // second "Ret"
+ // third: "Msg"
+
+ if (commandLine->getCommandCount() == 2){
+ if (strcmp("Command",commandLine->getIdentifier(0)) != 0) return 0;
+ if (strcmp("Msg",commandLine->getIdentifier(1)) != 0) return 0;
+ return 1;
+ }
+
+ if (commandLine->getCommandCount() == 3){
+ if (strcmp("Command",commandLine->getIdentifier(0)) != 0) return 0;
+ if (strcmp("Ret",commandLine->getIdentifier(1)) != 0) return 0;
+ if (strcmp("Msg",commandLine->getIdentifier(2)) != 0) return 0;
+ return 1;
+ }
+
+ return 0;
+}
+
+CommandLine* Parser::getCommandLine(){
+ return commandLine;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/example/yaf/yafcore/parser.h b/mpeglib/example/yaf/yafcore/parser.h
new file mode 100644
index 00000000..d9c46211
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/parser.h
@@ -0,0 +1,55 @@
+/*
+ Checks if a valid Command Line is avaiable
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+
+#ifndef __PARSER_H
+#define __PARSER_H
+
+#include "buffer.h"
+#include "commandLine.h"
+
+extern "C" {
+#include <string.h>
+ }
+
+class Parser {
+
+
+ Buffer* parseString;
+ CommandLine* commandLine;
+
+ public:
+
+ Parser();
+ ~Parser();
+
+ void setParseString(char* parseString);
+ void parse();
+ int isOK();
+ CommandLine* getCommandLine();
+
+
+ private:
+ void parse(char* currentPos,int* nCommand);
+
+};
+
+
+#endif
+
+
+
+
+
diff --git a/mpeglib/example/yaf/yafcore/runtimeTableYAF.cpp b/mpeglib/example/yaf/yafcore/runtimeTableYAF.cpp
new file mode 100644
index 00000000..987c10ae
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/runtimeTableYAF.cpp
@@ -0,0 +1,34 @@
+/*
+ basic runtime command (fits for every decoder)
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#define _USE_RUNTIME_YAF_STRUC
+
+#include "runtimeTableYAF.h"
+
+
+RuntimeTableYAF::RuntimeTableYAF(){
+ init();
+}
+
+RuntimeTableYAF::~RuntimeTableYAF(){
+}
+
+void RuntimeTableYAF::init() {
+
+ int i;
+ for (i=0;i<YAFRUNTIME_SIZE;i++) {
+ insert(&yafRuntime[i]);
+ }
+
+}
+
diff --git a/mpeglib/example/yaf/yafcore/runtimeTableYAF.h b/mpeglib/example/yaf/yafcore/runtimeTableYAF.h
new file mode 100644
index 00000000..6a77467a
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/runtimeTableYAF.h
@@ -0,0 +1,35 @@
+/*
+ basic runtime command (fits for every decoder)
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __RUNTIME_TABLEYAF_H
+#define __RUNTIME_TABLEYAF_H
+
+#include "commandTable.h"
+#include "yafRuntime.defs"
+
+
+
+
+class RuntimeTableYAF : public CommandTable {
+
+ public:
+ RuntimeTableYAF();
+ ~RuntimeTableYAF();
+ void init();
+
+};
+
+#endif
+
+
diff --git a/mpeglib/example/yaf/yafcore/yafCommand.defs b/mpeglib/example/yaf/yafcore/yafCommand.defs
new file mode 100644
index 00000000..ab2559df
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/yafCommand.defs
@@ -0,0 +1,89 @@
+/*
+ definition file for basic yaf commands
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __YAF_COMMAND_DEFS
+#define __YAF_COMMAND_DEFS
+
+#include "commandTable.h" // defines CommandDesc-Struct...
+
+// Command Numbers
+
+#define _YAF_I_COMMAND 1
+#define _YAF_I_MSG 2
+#define _YAF_I_HELP 3
+#define _YAF_I_RUNTIME 4
+#define _YAF_I_QUIT 5
+#define _YAF_I_PING 6
+#define _YAF_I_PROTOCOL 7
+#define _YAF_I_NOPROTOCOL 8
+#define _YAF_I_WHATIS 9
+#define _YAF_I_SELECT_A_LAYER 10
+#define _YAF_I_SELECT_V_LAYER 11
+#define _YAF_I_PLAYTIME 12
+#define _YAF_I_WRITE 13
+
+
+
+
+
+
+
+
+#define _YAF_START 40
+
+
+
+// The commands as String. The Syntax is :
+//
+
+// { lexternalUse,lReturnMsg,longName,shortName,number,helpText }
+//
+// lexternal use ist true when the text should be written when
+// user enters "help"
+
+
+#ifdef _USE_YAF_STRUC
+
+static struct CommandDescriptionStruct yafCommands[]={
+
+ {0,1,"Command","",_YAF_I_COMMAND,"internal identifier"},
+ {0,1,"Msg","",_YAF_I_MSG,"identifier for unstructured String"},
+ {1,1,"help","h",_YAF_I_HELP,"show this help"},
+ {1,1,"ping","",_YAF_I_PING,"command tests if client is alive"},
+ {1,1,"runtime","r",_YAF_I_RUNTIME,"runtime [on|off] shows runtime infos"},
+ {1,1,"protocol","",_YAF_I_NOPROTOCOL,"internal protocol wrapper on [def]"},
+ {1,1,"noprotocol","",_YAF_I_PROTOCOL,"internal protocol wrapper off"},
+ {1,1,"whatis" ,"",_YAF_I_WHATIS,"gives a short introduction" },
+ {1,1,"audioLayer","", _YAF_I_SELECT_A_LAYER,
+ "selects audio layer from stream [0..31]"},
+ {1,1,"videoLayer" ,"", _YAF_I_SELECT_V_LAYER,
+ "selects video layer from stream [0..15]" },
+ {1,1,"playtime","", _YAF_I_PLAYTIME,
+ "retrives the current playtime" },
+ {1,1,"write","", _YAF_I_WRITE,
+ "writes stream to disk [on|off] default[off]" },
+ {1,1,"quit","q",_YAF_I_QUIT,"ends program"}
+
+};
+#endif
+
+
+// How much Commands are in the Array :
+#define YAFCOMMANDS_SIZE 13
+
+
+
+#endif
+
diff --git a/mpeglib/example/yaf/yafcore/yafRuntime.defs b/mpeglib/example/yaf/yafcore/yafRuntime.defs
new file mode 100644
index 00000000..2918d178
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/yafRuntime.defs
@@ -0,0 +1,67 @@
+/*
+ basic Runtime definitions for yaf
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __YAF_RUNTIME_DEFS
+#define __YAF_RUNTIME_DEFS
+
+#include "../yafcore/commandTable.h" // defines CommandDesc-Struct...
+
+// Command Numbers
+
+// define the YAF debug -its not a real command, but I know
+// what I'm doing (see: outputDecoder.cpp)
+
+#define _YAF_DEBUG 1
+
+
+// For all other commands use the following schema:
+
+#define _YAF_RUN_COMMENT _CT_START+1
+#define _YAF_RUN_DECODER _CT_START+2
+#define _YAF_RUN_PROTOCOL _CT_START+3
+#define _YAF_RUN_IMPLEMENTS _CT_START+4
+#define _YAF_RUN_EXIT _CT_START+5
+
+
+#define _YAF_RUN_START _CT_START+100
+
+
+
+// The commands as String. The Syntax is :
+//
+
+// { lexternalUse,lReturnMsg,longName,shortName,number,helpText }
+//
+// lexternal use ist true when the text should be written when
+// user enters "help"
+#ifdef _USE_RUNTIME_YAF_STRUC
+static struct CommandDescriptionStruct yafRuntime[]={
+
+ { 0,1,"comment","",_YAF_RUN_COMMENT,"commands should not be interpreted"},
+ { 0,1,"decoder", "",_YAF_RUN_DECODER, "Name of decoder"},
+ { 0,1,"implements", "",_YAF_RUN_IMPLEMENTS, "basic behaviour"},
+ { 0,1,"exit", "",_YAF_RUN_EXIT, "yaf protocol terminated"},
+ { 0,1,"protocol", "",_YAF_RUN_PROTOCOL, "yaf protocol version"}
+
+};
+#endif
+
+// How much Commands are in the Array :
+#define YAFRUNTIME_SIZE 5
+
+
+
+#endif
+
diff --git a/mpeglib/example/yaf/yafcore/yaf_control.cpp b/mpeglib/example/yaf/yafcore/yaf_control.cpp
new file mode 100644
index 00000000..6e789588
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/yaf_control.cpp
@@ -0,0 +1,81 @@
+/*
+ generic interactive controller
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "yaf_control.h"
+#include "parser.h"
+#include <iostream>
+
+using namespace std;
+
+
+void yaf_control(InputInterface* input,
+ OutputInterface* output,
+ InputDecoder* decoder) {
+
+ Parser parser;
+ CommandLine* commandLine;
+ struct timeval timeout;
+ const char* retCode;
+ int status;
+
+ timeout.tv_sec=0;
+ timeout.tv_usec=0;
+
+ output->setProtocolSyntax(true); // we send "raw" protocol data
+ input->addFileDescriptor(0);
+
+ while(1) {
+
+ status=decoder->getDecoderStatus();
+
+ if (status == _DECODER_STATUS_EXIT) {
+ break;
+ }
+ input->waitForLine();
+
+ if (input->hasLine()) {
+ parser.setParseString(input->getLine());
+ parser.parse();
+
+ if (parser.isOK()){
+
+
+ commandLine=parser.getCommandLine();
+
+ decoder->processCommandLine(commandLine);
+ retCode=decoder->getReturnCode();
+
+ // now get the part after Msg: (in the inputLine)
+
+ output->lock();
+ output->clearBuffer();
+ output->appendBuffer(retCode);
+ output->flushBuffer();
+ output->unlock();
+ } else {
+ cout << "Error parsing input:"<<input->getLine()<<endl;
+ }
+
+ input->clearLine();
+ } else {
+ cout << "no line"<<endl;
+ }
+ }
+ input->removeFileDescriptor(0);
+
+}
+
+
+
+
diff --git a/mpeglib/example/yaf/yafcore/yaf_control.h b/mpeglib/example/yaf/yafcore/yaf_control.h
new file mode 100644
index 00000000..890a1bb8
--- /dev/null
+++ b/mpeglib/example/yaf/yafcore/yaf_control.h
@@ -0,0 +1,26 @@
+
+
+#ifndef __YAF_CONTROL_H
+#define __YAF_CONTROL_H
+
+
+
+#include "inputInterface.h"
+#include "outputInterface.h"
+#include "inputDecoder.h"
+
+#include <sys/time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <kdemacros.h>
+
+
+// prototypes
+
+//extern "C" void control_xplayer();
+KDE_EXPORT void yaf_control(InputInterface* input,
+ OutputInterface* output,
+ InputDecoder* decoder);
+
+#endif
diff --git a/mpeglib/example/yaf/yafmpgplay/Makefile.am b/mpeglib/example/yaf/yafmpgplay/Makefile.am
new file mode 100644
index 00000000..c9fd9b55
--- /dev/null
+++ b/mpeglib/example/yaf/yafmpgplay/Makefile.am
@@ -0,0 +1,34 @@
+# mpegplay-yaf - Makefile.am
+
+INCLUDES = -I../../include $(all_includes)
+
+EXTRA_DIST = stresstest
+
+bin_PROGRAMS = yaf-mpgplay
+
+yaf_mpgplay_SOURCES = mpg_control.cpp
+
+noinst_HEADERS =
+
+
+yaf_mpgplay_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+
+yaf_mpgplay_LDADD = ../yafcore/libyafcore.la \
+ ../yafxplayer/libyafxplayer.la \
+ ../../../lib/libmpeg.la \
+ $(THIS_LIB_LIBS)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/example/yaf/yafmpgplay/mpg_control.cpp b/mpeglib/example/yaf/yafmpgplay/mpg_control.cpp
new file mode 100644
index 00000000..ee080a73
--- /dev/null
+++ b/mpeglib/example/yaf/yafmpgplay/mpg_control.cpp
@@ -0,0 +1,197 @@
+/*
+ generic interactive controller
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+// Set for local include
+#define DIRECT_INCLUDE
+
+#include "../yafcore/yaf_control.h"
+#include "../yafxplayer/inputDecoderYAF.h"
+
+
+
+
+#include <iostream>
+using namespace std;
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#if defined(HAVE_GETOPT_H)
+#include <getopt.h>
+#endif
+
+// we include our plugin here
+#include "../../../lib/decoder/mpgPlugin.h"
+
+#if defined(HAVE_GETOPT_H)
+#include <getopt.h>
+#endif
+
+
+
+
+
+void control_mpgplay(InputInterface* input,OutputInterface* output,
+ InputDecoder* decoder) {
+
+
+ cout<< "Command:0 Msg:protocol yaf-0.1" << endl;
+ cout<< "Command:0 Msg:implements xplayer" << endl;
+ cout<< "Command:0 Msg:decoder splay Version:0.8.2" << endl;
+ cout<< "Command:0 Msg:decoder mpeg_play Version:2.4.0" << endl;
+ cout<< "Command:0 Msg:mimetypes video/mpeg;" << endl;
+ cout<< "Command:0 Msg:comment splay by Woo-jae Jung" << endl;
+ cout<< "Command:0 Msg:comment mpeg_play by University of California" << endl;
+ cout<< "Command:0 Msg:comment yaf port by mvogt@rhrk.uni-kl.de"<<endl;
+ cout<< "Command:0 Msg:comment based on sources from mpeg_play and splay"<<endl;
+ cout<< "Command:0 Msg:comment enter 'help' " << endl;
+
+
+
+
+
+ yaf_control(input,output,decoder);
+}
+
+
+void usage() {
+ cout << "yaf-mpgplay is a interactive frontend for the mpeg I decoder"<<endl;
+ cout << "Usage : yaf-mpgplay [2al:L:scb:mhpywd:] [url]"<<endl;
+ cout << endl;
+ cout << "-2 : playing with half frequency."<<endl;
+ cout << "-m : force to mono."<<endl;
+ cout << "-a : no internal audio device"<<endl;
+ cout << "-c : no time calculation"<<endl;
+ cout << "-s : no audio/video sync"<<endl;
+ cout << "-b : audio buffer delay (bytes)[65535 byte]"<<endl;
+ cout << "-p : measure performance (audio is decoded, but not played)"<<endl;
+ cout << "-l : select audio Layer [0]"<<endl;
+ cout << "-L : select video Layer [0]"<<endl;
+ cout << "-w : write streams to disk [demultiplex]"<<endl;
+ cout << "-y : autoplay off"<<endl;
+ cout << "-d : [1..2] dump yuv images 1: single images 2: as stream"<<endl;
+ cout << endl;
+ cout << "THIS SOFTWARE COMES WITH ABSOLUTELY NO WARRANTY! " \
+ << "USE AT YOUR OWN RISK!"<<endl;
+ cout << endl;
+ cout << "for more help type 'help' in interactive mode"<<endl;
+}
+
+
+
+
+int main(int argc, char** argv) {
+ int c;
+ int lInternalAudio=true;
+
+ pow(6.0,3.0); // fixes bug in __math.h
+ InputInterface input;
+ OutputInterface output(&cout);
+
+
+
+
+
+ YafOutputStream* yafOutput=new YafOutputStream(&input);
+ MpgPlugin* plugin=new MpgPlugin();
+
+ plugin->setOutputPlugin(yafOutput);
+ InputDecoderYAF decoder(plugin,yafOutput);
+
+
+
+ while(1) {
+ c = getopt (argc, argv, "2al:L:scb:mhpywd:");
+ if (c == -1) break;
+ switch(c) {
+ case 'a': {
+ lInternalAudio=false;
+ break;
+ }
+ case 'h': {
+ usage();
+ exit(0);
+ }
+ case 'y': {
+ decoder.setAutoPlay(false);
+ break;
+ }
+ case '2': {
+ plugin->config("-2","true",NULL);
+ break;
+ }
+ case 'm': {
+ plugin->config("-m","true",NULL);
+ break;
+ }
+ case 'c': {
+ plugin->config("-c","true",NULL);
+ break;
+ }
+ case 'w': {
+ plugin->config("-w","true",NULL);
+ break;
+ }
+ case 's': {
+ yafOutput->config("-s","true",NULL);
+ break;
+ }
+ case 'b': {
+ yafOutput->config("-b",optarg,NULL);
+ break;
+ }
+ case 'd': {
+ yafOutput->config("yufDump",optarg,NULL);
+ break;
+ }
+ case 'p': {
+ yafOutput->config("-p",optarg,NULL);
+ break;
+ }
+ case 'l': {
+ plugin->config("AudioLayer",optarg,NULL);
+ cout << "trying to configure" << endl;
+ break;
+ }
+ case 'L': {
+ plugin->config("VideoLayer",optarg,NULL);
+ break;
+ }
+
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ usage();
+ exit(-1);
+ }
+ }
+
+ if (optind < argc ) {
+ Buffer buffer(300);
+ buffer.append("open ");
+ buffer.append(argv[optind]);
+ buffer.append("\n");
+ input.addInputLine(&buffer);
+
+ }
+ yafOutput->internalDevice(lInternalAudio);
+ control_mpgplay(&input,&output,&decoder);
+ delete plugin;
+ delete yafOutput;
+
+}
+
+
diff --git a/mpeglib/example/yaf/yafmpgplay/stresstest b/mpeglib/example/yaf/yafmpgplay/stresstest
new file mode 100644
index 00000000..1d73ad92
--- /dev/null
+++ b/mpeglib/example/yaf/yafmpgplay/stresstest
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+declare -i zahl=100
+declare -i zahl1=3
+rm -f jumptable
+echo "sleep 5" >>jumptable
+
+while [ 0 -le $zahl ] ; do
+ echo "j $zahl" >>jumptable
+ echo "sleep 0" >>jumptable
+ echo "j $zahl1" >>jumptable
+ echo "sleep 0" >>jumptable
+
+ zahl=$[zahl-7]
+ zahl1=$[zahl1+7]
+done
+echo "sleep 20" >>jumptable
+echo "quit" >>jumptable
+
+for i in "/mnt/diskD/mpeg"/* ; do
+ echo "open $i" >yaf.script
+ ./yaf-mpgplay <jumptable
+ sleep 5
+done
diff --git a/mpeglib/example/yaf/yafsplay/Makefile.am b/mpeglib/example/yaf/yafsplay/Makefile.am
new file mode 100644
index 00000000..463ec7db
--- /dev/null
+++ b/mpeglib/example/yaf/yafsplay/Makefile.am
@@ -0,0 +1,35 @@
+# splay-yaf - Makefile.am
+
+INCLUDES = -I../../include $(all_includes)
+
+EXTRA_DIST = stresstest
+
+bin_PROGRAMS = yaf-splay
+
+yaf_splay_SOURCES = splay_control.cpp
+
+
+noinst_HEADERS =
+
+yaf_splay_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+
+yaf_splay_LDADD = ../yafxplayer/libyafxplayer.la \
+ ../yafcore/libyafcore.la \
+ ../../../lib/libmpeg.la \
+ $(THIS_LIB_LIBS)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/example/yaf/yafsplay/splay_control.cpp b/mpeglib/example/yaf/yafsplay/splay_control.cpp
new file mode 100644
index 00000000..82be9c11
--- /dev/null
+++ b/mpeglib/example/yaf/yafsplay/splay_control.cpp
@@ -0,0 +1,161 @@
+/*
+ generic interactive controller
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+// Set for local include
+#define DIRECT_INCLUDE
+
+#include "../yafcore/yaf_control.h"
+#include "../yafxplayer/inputDecoderYAF.h"
+
+#include <iostream>
+using namespace std;
+
+#include <math.h>
+
+
+
+// we include our plugin here
+#include "../../../lib/decoder/splayPlugin.h"
+
+#if defined(HAVE_GETOPT_H)
+#include <getopt.h>
+#endif
+
+
+void control_splay(InputInterface* input,OutputInterface* output,
+ InputDecoder* decoder) {
+
+
+ cout<< "Command:0 Msg:protocol yaf-0.1" << endl;
+ cout<< "Command:0 Msg:implements xplayer" << endl;
+ cout<< "Command:0 Msg:decoder splay Version:0.8.2" << endl;
+ cout<< "Command:0 Msg:mimetypes audio/mpg1;audio/mpg2;audio/mp3;" << endl;
+ cout<< "Command:0 Msg:comment splay by Woo-jae Jung" << endl;
+ cout<< "Command:0 Msg:comment yaf port by mvogt@rhrk.uni-kl.de"<<endl;
+ cout<< "Command:0 Msg:comment based on sources from eMusic and splay"<<endl;
+ cout<< "Command:0 Msg:comment enter 'help' " << endl;
+
+
+
+ yaf_control(input,output,decoder);
+}
+
+
+void usage() {
+ cout << "yaf-splay is a interactive frontend for the splay decoder"<<endl;
+ cout << "Usage : yaf-splay [-2am] [url]"<<endl;
+ cout << endl;
+ cout << "-2 : playing with half frequency."<<endl;
+ cout << "-m : force to mono."<<endl;
+ cout << "-r : autoupdate musicinfo"<<endl;
+ cout << "-a : no internal audio device"<<endl;
+ cout << "-c : no time calculation"<<endl;
+ cout << "-y : autoplay off"<<endl;
+ cout << "-d : only decode"<<endl;
+ cout << endl;
+ cout << "THIS SOFTWARE COMES WITH ABSOLUTELY NO WARRANTY! " \
+ << "USE AT YOUR OWN RISK!"<<endl;
+ cout << endl;
+ cout << "for more help type 'help' in interactive mode"<<endl;
+}
+
+
+
+int main(int argc, char** argv) {
+ int c;
+ int lInternalAudio=true;
+
+
+ pow(6.0,3.0); // fixes bug in __math.h
+
+
+
+
+ InputInterface* input=new InputInterface();
+
+ OutputInterface output(&cout);
+
+ YafOutputStream* yafOutput=new YafOutputStream(input);
+
+
+ SplayPlugin* plugin=new SplayPlugin();
+
+ plugin->setOutputPlugin(yafOutput);
+ InputDecoderYAF decoder(plugin,yafOutput);
+
+
+
+ while(1) {
+ c = getopt (argc, argv, "2yamhd");
+ if (c == -1) break;
+ switch(c) {
+ case 'a': {
+ lInternalAudio=false;
+ break;
+ }
+ case 'h': {
+ usage();
+ exit(0);
+ }
+ case '2': {
+ plugin->config("-2","true",NULL);
+ break;
+ }
+ case 'y': {
+ decoder.setAutoPlay(false);
+ break;
+ }
+ case 'm': {
+ plugin->config("-m","true",NULL);
+ break;
+ }
+ case 'r': {
+ plugin->config("runtime","on",NULL);
+ break;
+ }
+ case 'd': {
+ plugin->config("-d","on",NULL);
+ break;
+ }
+ case 'c': {
+ plugin->config("-c","true",NULL);
+ break;
+ }
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ usage();
+ exit(-1);
+ }
+ }
+ if (optind < argc ) {
+ Buffer buffer(300);
+ buffer.append("open ");
+ buffer.append(argv[optind]);
+ buffer.append("\n");
+
+ input->addInputLine(&buffer);
+
+ }
+
+
+
+ yafOutput->internalDevice(lInternalAudio);
+ control_splay(input,&output,&decoder);
+ delete plugin;
+ delete yafOutput;
+ delete input;
+
+}
+
+
diff --git a/mpeglib/example/yaf/yafsplay/stresstest b/mpeglib/example/yaf/yafsplay/stresstest
new file mode 100644
index 00000000..caeba598
--- /dev/null
+++ b/mpeglib/example/yaf/yafsplay/stresstest
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+declare -i zahl=150
+declare -i zahl1=3
+rm -f jumptable
+
+while [ 0 -le $zahl ] ; do
+ echo "j $zahl" >>jumptable
+ echo "sleep 1" >>jumptable
+ echo "j $zahl1" >>jumptable
+ echo "sleep 1" >>jumptable
+
+ zahl=$[zahl-7]
+ zahl1=$[zahl1+7]
+done
+
+echo "quit" >>jumptable
+
+for i in "/mnt/diskD/rh/mp3/neu3"/* ; do
+ echo "open $i" >yaf.script
+ ./yaf-splay <jumptable
+ sleep 5
+done
diff --git a/mpeglib/example/yaf/yaftplay/Makefile.am b/mpeglib/example/yaf/yaftplay/Makefile.am
new file mode 100644
index 00000000..2d82700d
--- /dev/null
+++ b/mpeglib/example/yaf/yaftplay/Makefile.am
@@ -0,0 +1,32 @@
+# tplay-yaf - Makefile.am
+
+INCLUDES = -I../../include $(all_includes)
+
+
+bin_PROGRAMS = yaf-tplay
+
+yaf_tplay_SOURCES = tplay_control.cpp
+
+noinst_HEADERS =
+
+yaf_tplay_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+
+yaf_tplay_LDADD = ../yafcore/libyafcore.la \
+ ../yafxplayer/libyafxplayer.la \
+ ../../../lib/libmpeg.la \
+ $(THIS_LIB_LIBS)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/example/yaf/yaftplay/tplay_control.cpp b/mpeglib/example/yaf/yaftplay/tplay_control.cpp
new file mode 100644
index 00000000..6203c727
--- /dev/null
+++ b/mpeglib/example/yaf/yaftplay/tplay_control.cpp
@@ -0,0 +1,135 @@
+/*
+ generic interactive controller
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+// Set for local include
+#define DIRECT_INCLUDE
+
+#include "../yafcore/yaf_control.h"
+#include "../yafxplayer/inputDecoderYAF.h"
+
+#include <iostream>
+using namespace std;
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+
+// we include our plugin here
+#include "../../../lib/decoder/tplayPlugin.h"
+
+
+#if defined(HAVE_GETOPT_H)
+#include <getopt.h>
+#endif
+
+
+void control_tplay(InputInterface* input,OutputInterface* output,
+ InputDecoder* decoder) {
+
+
+ cout<< "Command:0 Msg:protocol yaf-0.1" << endl;
+ cout<< "Command:0 Msg:implements xplayer" << endl;
+ cout<< "Command:0 Msg:decoder tplay Version:0.5.5" << endl;
+ cout<< "Command:0 Msg:mimetypes audio/wav;audio/au;" << endl;
+ cout<< "Command:0 Msg:comment tplay by Ilkka Karvinen" << endl;
+ cout<< "Command:0 Msg:comment yaf port by mvogt@rhrk.uni-kl.de"<<endl;
+ cout<< "Command:0 Msg:comment based on sources from eMusic and tplay"<<endl;
+ cout<< "Command:0 Msg:comment enter 'help' " << endl;
+
+
+
+
+
+ yaf_control(input,output,decoder);
+}
+
+
+void usage() {
+ cout << "yaf-tplay is a interactive frontend for the tplay decoder"<<endl;
+ cout << "Usage : yaf-tplay [-a] [url]"<<endl;
+ cout << endl;
+ cout << "-a : no internal audio device"<<endl;
+ cout << "-y : autoplay off"<<endl;
+ cout << endl;
+ cout << "THIS SOFTWARE COMES WITH ABSOLUTELY NO WARRANTY! " \
+ << "USE AT YOUR OWN RISK!"<<endl;
+ cout << endl;
+ cout << "for more help type 'help' in interactive mode"<<endl;
+}
+
+
+
+int main(int argc, char** argv) {
+ int c;
+ int lInternalAudio=true;
+
+ pow(6.0,3.0); // fixes bug in __math.h
+ InputInterface input;
+ OutputInterface output(&cout);
+
+
+
+
+
+ YafOutputStream* yafOutput=new YafOutputStream(&input);
+ TplayPlugin* plugin=new TplayPlugin();
+
+ plugin->setOutputPlugin(yafOutput);
+ InputDecoderYAF decoder(plugin,yafOutput);
+
+
+
+ while(1) {
+ c = getopt (argc, argv, "2yamh");
+ if (c == -1) break;
+ switch(c) {
+ case 'a': {
+ lInternalAudio=false;
+ break;
+ }
+ case 'y': {
+ decoder.setAutoPlay(false);
+ break;
+ }
+ case 'h': {
+ usage();
+ exit(0);
+ }
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ usage();
+ exit(-1);
+ }
+ }
+ if (optind < argc ) {
+ Buffer buffer(300);
+ buffer.append("open ");
+ buffer.append(argv[optind]);
+ buffer.append("\n");
+
+ input.addInputLine(&buffer);
+
+ }
+
+ yafOutput->internalDevice(lInternalAudio);
+ control_tplay(&input,&output,&decoder);
+ delete plugin;
+ delete yafOutput;
+
+}
+
+
+
+
+
diff --git a/mpeglib/example/yaf/yafvorbis/Makefile.am b/mpeglib/example/yaf/yafvorbis/Makefile.am
new file mode 100644
index 00000000..f0a01965
--- /dev/null
+++ b/mpeglib/example/yaf/yafvorbis/Makefile.am
@@ -0,0 +1,33 @@
+# splay-yaf - Makefile.am
+
+INCLUDES = -I../../include -I../../include/vorbis/include $(all_includes)
+
+EXTRA_DIST =
+
+bin_PROGRAMS = yaf-vorbis
+
+yaf_vorbis_SOURCES = vorbis_control.cpp
+
+noinst_HEADERS =
+
+yaf_vorbis_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+yaf_vorbis_LDADD = \
+ ../yafcore/libyafcore.la \
+ ../yafxplayer/libyafxplayer.la \
+ ../../../lib/libmpeg.la \
+ $(THIS_LIB_LIBS)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/example/yaf/yafvorbis/vorbis_control.cpp b/mpeglib/example/yaf/yafvorbis/vorbis_control.cpp
new file mode 100644
index 00000000..1e8c2b09
--- /dev/null
+++ b/mpeglib/example/yaf/yafvorbis/vorbis_control.cpp
@@ -0,0 +1,150 @@
+/*
+ generic interactive controller
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+// Set for local include
+#define DIRECT_INCLUDE
+
+#include "../yafcore/yaf_control.h"
+#include "../yafxplayer/inputDecoderYAF.h"
+
+
+
+#include <iostream>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+// for dynamic loading
+#include <dlfcn.h>
+
+#include "../../../lib/decoder/vorbisPlugin.h"
+
+#if defined(HAVE_GETOPT_H)
+#include <getopt.h>
+#endif
+
+using namespace std;
+
+void control_vorbis(InputInterface* input,OutputInterface* output,
+ InputDecoder* decoder) {
+
+
+ cout<< "Command:0 Msg:protocol yaf-0.1" << endl;
+ cout<< "Command:0 Msg:implements xplayer" << endl;
+ cout<< "Command:0 Msg:decoder vorbis Version:20000223" << endl;
+ cout<< "Command:0 Msg:mimetypes audio/ogg;" << endl;
+ cout<< "Command:0 Msg:comment vorbis by Monty http://www.xiph.org/" << endl;
+ cout<< "Command:0 Msg:comment yaf port by mvogt@rhrk.uni-kl.de"<<endl;
+ cout<< "Command:0 Msg:comment based on sources from vorbis"<<endl;
+ cout<< "Command:0 Msg:comment enter 'help' " << endl;
+
+
+
+
+
+ yaf_control(input,output,decoder);
+}
+
+
+void usage() {
+ cout << "yaf-vorbis is a interactive frontend for the vorbis decoder"<<endl;
+ cout << "Usage : yaf-vorbis [url]"<<endl;
+ cout << endl;
+ cout << "-a : no internal audio device"<<endl;
+ cout << "-y : autoplay off"<<endl;
+ cout << endl;
+ cout << "THIS SOFTWARE COMES WITH ABSOLUTELY NO WARRANTY! " \
+ << "USE AT YOUR OWN RISK!"<<endl;
+ cout << endl;
+ cout << "for more help type 'help' in interactive mode"<<endl;
+}
+
+
+
+int main(int argc, char** argv) {
+ int c;
+ int lInternalAudio=true;
+
+
+ pow(6.0,3.0); // fixes bug in __math.h
+ InputInterface* input=new InputInterface();
+ OutputInterface output(&cout);
+
+ YafOutputStream* yafOutput=new YafOutputStream(input);
+
+
+ VorbisPlugin* plugin=new VorbisPlugin();
+
+ plugin->setOutputPlugin(yafOutput);
+ InputDecoderYAF decoder(plugin,yafOutput);
+
+
+
+ while(1) {
+ c = getopt (argc, argv, "2amhy");
+ if (c == -1) break;
+ switch(c) {
+ case 'a': {
+ lInternalAudio=false;
+ break;
+ }
+ case 'h': {
+ usage();
+ exit(0);
+ }
+ case '2': {
+ plugin->config("-2","true",NULL);
+ break;
+ }
+ case 'y': {
+ decoder.setAutoPlay(false);
+ break;
+ }
+ case 'm': {
+ plugin->config("-m","true",NULL);
+ break;
+ }
+ case 'r': {
+ plugin->config("runtime","on",NULL);
+ break;
+ }
+ case 'c': {
+ plugin->config("-c","true",NULL);
+ break;
+ }
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ usage();
+ exit(-1);
+ }
+ }
+ if (optind < argc ) {
+ Buffer buffer(300);
+ buffer.append("open ");
+ buffer.append(argv[optind]);
+ buffer.append("\n");
+
+ input->addInputLine(&buffer);
+
+ }
+
+
+
+ yafOutput->internalDevice(lInternalAudio);
+ control_vorbis(input,&output,&decoder);
+ delete plugin;
+ delete yafOutput;
+
+}
+
+
diff --git a/mpeglib/example/yaf/yafxplayer/Makefile.am b/mpeglib/example/yaf/yafxplayer/Makefile.am
new file mode 100644
index 00000000..e49e999e
--- /dev/null
+++ b/mpeglib/example/yaf/yafxplayer/Makefile.am
@@ -0,0 +1,37 @@
+# libxplayer - Makefile.am
+
+INCLUDES = -I../../include $(all_includes)
+
+AM_CPPFLAGS = -DDIRECT_INCLUDE
+
+EXTRA_DIST = xplayerCommand.defs xplayerRuntime.defs
+
+lib_LTLIBRARIES = libyafxplayer.la
+
+
+noinst_HEADERS = commandTableXPlayer.h runtimeTableXPlayer.h \
+ xplayer_control.h
+
+
+libyafxplayer_la_SOURCES = commandTableXPlayer.cpp \
+ inputDecoderXPlayer.cpp \
+ xplayer_control.cpp \
+ runtimeTableXPlayer.cpp \
+ inputDecoderYAF.cpp yafOutputStream.cpp
+
+
+libyafxplayer_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) \
+ -no-undefined -avoid-version
+
+libyafxplayer_la_LIBADD = ../../../lib/libmpeg.la \
+ ../yafcore/libyafcore.la \
+ $(THIS_LIB_LIBS)
+
+
+
+yafxplayerdir = $(includedir)/$(THIS_LIB_NAME)/util/yaf/yafxplayer
+
+yafxplayer_HEADERS = inputDecoderYAF.h \
+ inputDecoderXPlayer.h \
+ yafOutputStream.h
+
diff --git a/mpeglib/example/yaf/yafxplayer/README b/mpeglib/example/yaf/yafxplayer/README
new file mode 100644
index 00000000..3eb486c4
--- /dev/null
+++ b/mpeglib/example/yaf/yafxplayer/README
@@ -0,0 +1,30 @@
+
+This directory contains a generic player frontend.
+Use this as a start if you add new decoder support
+to yaf.
+
+The first step is to build the generic frontend.
+Uncomment in xplayer_control.c the "main" part
+and link the *.o fails againt yafcore.
+
+g++ -o xplayer *.o ../yafcore.a
+
+
+This should build ./xplayer
+
+
+If you implement a new player ans you have to change all the names,
+the following script is *very* usefull
+
+
+for i in *.cpp; do
+sed s\/XPlayer\/helloName\/g $i >$i.x
+mv $i.x $i
+done
+
+which replaces all occurances of "XPlayer" in all .cpp files
+to "helloName"
+
+(usefull, isn't it)
+
+
diff --git a/mpeglib/example/yaf/yafxplayer/commandTableXPlayer.cpp b/mpeglib/example/yaf/yafxplayer/commandTableXPlayer.cpp
new file mode 100644
index 00000000..31d3a454
--- /dev/null
+++ b/mpeglib/example/yaf/yafxplayer/commandTableXPlayer.cpp
@@ -0,0 +1,35 @@
+/*
+ valid Commands for generic cd player
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#define _USE_XPLAYER_STRUC
+
+#include "commandTableXPlayer.h"
+
+
+CommandTableXPlayer::CommandTableXPlayer(){
+ init();
+}
+
+CommandTableXPlayer::~CommandTableXPlayer(){
+}
+
+void CommandTableXPlayer::init() {
+
+
+ int i;
+ for (i=0;i<XPLAYERCOMMANDS_SIZE;i++) {
+ insert(&xplayerCommands[i]);
+ }
+
+
+}
+
diff --git a/mpeglib/example/yaf/yafxplayer/commandTableXPlayer.h b/mpeglib/example/yaf/yafxplayer/commandTableXPlayer.h
new file mode 100644
index 00000000..2bc23094
--- /dev/null
+++ b/mpeglib/example/yaf/yafxplayer/commandTableXPlayer.h
@@ -0,0 +1,36 @@
+/*
+ valid Commands for generic cd player
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __COMMAND_TABLEXPLAYER_H
+#define __COMMAND_TABLEXPLAYER_H
+
+#include "../yafcore/commandTable.h"
+#include "xplayerCommand.defs"
+
+
+
+
+
+class CommandTableXPlayer : public CommandTable {
+
+ public:
+ CommandTableXPlayer();
+ ~CommandTableXPlayer();
+ void init();
+
+};
+
+#endif
+
diff --git a/mpeglib/example/yaf/yafxplayer/inputDecoderXPlayer.cpp b/mpeglib/example/yaf/yafxplayer/inputDecoderXPlayer.cpp
new file mode 100644
index 00000000..4b3bf3b3
--- /dev/null
+++ b/mpeglib/example/yaf/yafxplayer/inputDecoderXPlayer.cpp
@@ -0,0 +1,239 @@
+/*
+ generic Implementation of a cd-player
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#include "inputDecoderXPlayer.h"
+#include "commandTableXPlayer.h"
+#include <iostream>
+using namespace std;
+
+#define DEBUG cout << "Command:1 Msg:"
+
+
+InputDecoderXPlayer::InputDecoderXPlayer(YafOutputStream* yafOutput) :
+ InputDecoder() {
+
+ output=new OutputInterface(&cout);
+ output->setProtocolSyntax(true);
+ this->yafOutput=yafOutput;
+ ct=new CommandTableXPlayer();
+
+ setDecoderStatus(_DECODER_STATUS_IDLE);
+ appendCommandTable(ct);
+ majorMode=_PLAYER_MAJOR_MODE_OFF;
+ setRuntimeInfo(false);
+ // As default we expect a user which does not want to know
+ // about the player status.
+ // This is *not* true if the user is a controlling GUI.
+ // A GUI should alwas send :
+ // "MajorModeInfo on"
+ setMajorModeInfo(true); // as default we don't inform about the state!
+ isOn=false;
+}
+
+InputDecoderXPlayer::~InputDecoderXPlayer(){
+ delete ct;
+ delete output;
+}
+
+void InputDecoderXPlayer::setMajorModeInfo(int lDisplayMajorMode) {
+ this->lDisplayMajorMode=lDisplayMajorMode;
+}
+
+int InputDecoderXPlayer::getMajorModeInfo() {
+ return lDisplayMajorMode;
+}
+
+
+
+int InputDecoderXPlayer::getOn() {
+ return isOn;
+}
+
+
+void InputDecoderXPlayer::setOn(int lOn) {
+ isOn=lOn;
+}
+
+
+
+
+
+void InputDecoderXPlayer::setMajorMode(int mode) {
+ const char* ptr;
+ majorMode=mode;
+
+ if (lDisplayMajorMode==false) {
+ cout << "lDisplayMajorMode false"<<endl;
+ return;
+ }
+ output->lock();
+ output->clearBuffer();
+ output->appendBuffer("Command:0 Msg:player-status ");
+ if (majorMode == _PLAYER_MAJOR_MODE_OFF) {
+ ptr="off";
+ } else if (majorMode == _PLAYER_MAJOR_MODE_ON) {
+ ptr="on";
+ } else if (majorMode == _PLAYER_MAJOR_MODE_OPEN_TRACK) {
+ ptr="open";
+ } else if (majorMode == _PLAYER_MAJOR_MODE_CLOSE_TRACK) {
+ ptr="close";
+ } else if (majorMode == _PLAYER_MAJOR_MODE_PLAYING) {
+ ptr="playing";
+ } else if (majorMode == _PLAYER_MAJOR_MODE_PAUSE) {
+ ptr="pause";
+ } else {
+ ptr="unknown";
+ }
+ output->appendBuffer(ptr);
+ if (majorMode == _PLAYER_MAJOR_MODE_OFF) {
+ char val[40];
+ long bytes=yafOutput->getBytesCounter();
+ long allWrite=yafOutput->getAllWriteCounter();
+
+ snprintf(val,40,"%ld %ld",bytes,allWrite);
+ output->appendBuffer(" ");
+ output->appendBuffer(val);
+ }
+ output->flushBuffer();
+ output->unlock();
+}
+
+
+
+int InputDecoderXPlayer::getMajorMode() {
+ return majorMode;
+}
+
+
+
+void InputDecoderXPlayer::doSomething(){
+ DEBUG << "Decoder did something" << endl;
+ // after decoding is ready we close the file
+
+ InputDecoder::doSomething();
+}
+
+
+
+const char* InputDecoderXPlayer::processCommand(int command,const char* args){
+
+ if (command == _PLAYER_OFF) {
+ if (isOn == true) {
+ isOn=false;
+ processCommand(_PLAYER_PAUSE,"");
+ processCommand(_PLAYER_CLOSE,"");
+
+ setMajorMode(_PLAYER_MAJOR_MODE_OFF);
+ yafOutput->setBytesCounter(0);
+ }
+ return"";
+ }
+
+ if (command == _PLAYER_ON) {
+ if (isOn == false) {
+ setMajorMode(_PLAYER_MAJOR_MODE_ON);
+ isOn=true;
+ }
+ return"";
+ }
+
+
+ if (command == _PLAYER_OPEN) {
+ setMajorMode(_PLAYER_MAJOR_MODE_OPEN_TRACK);
+ return"";
+ }
+
+ if (command == _PLAYER_CLOSE) {
+ setMajorMode(_PLAYER_MAJOR_MODE_CLOSE_TRACK);
+ return"";
+ }
+
+ if (command == _PLAYER_PLAY) {
+ setDecoderStatus(_DECODER_STATUS_WORKING);
+ setMajorMode(_PLAYER_MAJOR_MODE_PLAYING);
+ return"";
+ }
+
+ if (command == _PLAYER_PAUSE) {
+ setMajorMode(_PLAYER_MAJOR_MODE_PAUSE);
+ return"";
+ }
+
+ if (command == _PLAYER_VERBOSE) {
+ if (strcmp(args,"off")==0) {
+ setMajorModeInfo(false);
+ } else {
+ setMajorModeInfo(true);
+ }
+ return"";
+ }
+
+ if (command == _PLAYER_OUTPUTFILE) {
+ int ret;
+ if (yafOutput->isOpenStream() == true) {
+ return "already output file selected";
+ }
+ yafOutput->setStreamFile(args);
+ // now we have set the fifo. But we must say the parent
+ // process that we start with waiting
+
+ cout << "Command:0 Msg:fileopen before"<<endl;
+ ret=yafOutput->openStream();
+ cout << "Command:0 Msg:fileopen after"<<endl;
+
+ if (ret < 0) {
+ return "cannot open outfile";
+ }
+ return "";
+ }
+ if (command == _PLAYER_CLOSEOUTPUTFILE) {
+ if (yafOutput->isOpenStream() == false) {
+ return "no output file selected";
+ }
+ yafOutput->closeStream();
+ return "";
+ }
+
+ if (command == _PLAYER_INTERNALAUDIO) {
+ if (strcmp("on",args)==0) {
+ yafOutput->internalDevice(true);
+ return "";
+ }
+ yafOutput->internalDevice(false);
+ return "";
+ }
+
+ if (command == _PLAYER_SLEEP) {
+ int nSec;
+ sscanf(args,"%d",&nSec); // convert string to int
+ sleep(nSec);
+ return "";
+ }
+ if (command == _PLAYER_CLEAR) {
+ yafOutput->setBytesCounter(0);
+ return "";
+ }
+
+ return InputDecoder::processCommand(command,args);
+}
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/example/yaf/yafxplayer/inputDecoderXPlayer.h b/mpeglib/example/yaf/yafxplayer/inputDecoderXPlayer.h
new file mode 100644
index 00000000..e6602b52
--- /dev/null
+++ b/mpeglib/example/yaf/yafxplayer/inputDecoderXPlayer.h
@@ -0,0 +1,117 @@
+/*
+ generic Implementation of a cd-player
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __INPUTDECODERXPLAYER_H
+#define __INPUTDECODERXPLAYER_H
+
+
+#include "../yafcore/inputDecoder.h"
+#include "yafOutputStream.h"
+
+
+
+/**
+ major modes . it should be not necessary to define
+ new modes.
+ What are "major modes"?
+ <p>
+ The idea of the major modes, is that this is a very
+ general description of an abstract cd player.
+ When the decoder works yaf send these
+ major modes to the frontend.
+ <p>
+ These Major modes describes the basic commands which
+ every yaf-decoder implementation should support:
+ <pre>
+ on/off
+ open/close
+ pause/play
+ </pre>
+ Thus it is possible that a frontend works with
+ a decoder even if the frontend does not fully support all
+ the commands (e.g: switch to mono etc...)
+
+ <pre>
+ If you think you have a new "mode" its almost a "feature"
+ eg: You have a command "jump" but this is not a "mode"
+ because you can press "jump" on your cd player even
+ if the cd player is off OR on OR playing OR paused
+ In all these cases the command does not affect the
+ state of your cd-player. Or do you have a player
+ which switches itself on if you press e.g. "Track 1" ?
+ </pre>
+*/
+
+#define _PLAYER_MAJOR_MODE_OFF 1
+#define _PLAYER_MAJOR_MODE_ON 2
+#define _PLAYER_MAJOR_MODE_OPEN_TRACK 3
+#define _PLAYER_MAJOR_MODE_CLOSE_TRACK 4
+#define _PLAYER_MAJOR_MODE_PLAYING 5
+#define _PLAYER_MAJOR_MODE_PAUSE 6
+
+
+/*
+ off: the cd player is switched off (/dev/dsp is unlocked)
+
+
+ on : (you may play the inserte cd) (/dev/dsp is locked)
+
+ playing: cd player plays something (player is switched on)
+ pause: does not play "
+
+ open track : think of it that you "select a track" you
+ can only open a track if the player is "ON"
+
+ close track: eject the cd from player.
+ (This does not necessarily mean that /dev/dsp is unlocked)
+*/
+
+
+class CommandTableXPlayer;
+
+class InputDecoderXPlayer : public InputDecoder {
+
+
+ public:
+ InputDecoderXPlayer(YafOutputStream* yafOutput);
+ ~InputDecoderXPlayer();
+
+ const char* processCommand(int command,const char* args);
+ void doSomething();
+
+ void setMajorMode(int mode);
+ int getMajorMode();
+
+ void setMajorModeInfo(int lDisplayMajorMode);
+ int getMajorModeInfo();
+
+ int getOn();
+ void setOn(int lOn);
+
+ private:
+
+ int majorMode;
+ CommandTableXPlayer* ct;
+ int lDisplayMajorMode;
+ OutputInterface* output;
+ YafOutputStream* yafOutput;
+
+ int isOn;
+};
+
+
+#endif
+
+
diff --git a/mpeglib/example/yaf/yafxplayer/inputDecoderYAF.cpp b/mpeglib/example/yaf/yafxplayer/inputDecoderYAF.cpp
new file mode 100644
index 00000000..b4b6a317
--- /dev/null
+++ b/mpeglib/example/yaf/yafxplayer/inputDecoderYAF.cpp
@@ -0,0 +1,220 @@
+/*
+ base Implementation of a yaf decoder
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#define DEBUG cout << "Command:1 Msg:"
+
+#include "inputDecoderYAF.h"
+#include "xplayerCommand.defs"
+#include "../yafcore/yafCommand.defs"
+#include <iostream>
+
+using namespace std;
+
+InputDecoderYAF::InputDecoderYAF(DecoderPlugin* plugin,
+ YafOutputStream* output)
+ : InputDecoderXPlayer(output) {
+
+ this->plugin=plugin;
+ this->output=output;
+ input=NULL;
+
+ lAutoPlay=true;
+ lFileSelected=false;
+ songPath=new Buffer(256);
+}
+
+
+InputDecoderYAF::~InputDecoderYAF(){
+ delete songPath;
+}
+
+
+/**
+ This method is entered by the global loop. (xplayer_control.c)
+ This method should never (!) block or it
+ is unpossible to read commands on stdin!
+
+
+ Note: YAF is not a threaded player this is why we need
+ this doSomething method. Other player may already be threaded and
+ offer a "control"/"working" channel.
+ These players won't need this loop.
+
+*/
+
+
+void InputDecoderYAF::doSomething(){
+
+}
+
+
+const char* InputDecoderYAF::processCommand(int command,const char* args){
+
+ if (command == _PLAYER_UPDATE) {
+ int state=plugin->getStreamState();
+ if (state == _STREAM_STATE_EOF) {
+ if (getMajorMode() != _PLAYER_MAJOR_MODE_OFF) {
+ return processCommand(_PLAYER_OFF,"");
+ }
+ }
+ return "";
+ }
+
+
+ if (command == _PLAYER_OPEN) {
+ if (strlen(args) == 0) {
+ return "no file";
+ }
+ if (lFileSelected == true) {
+ processCommand(_PLAYER_CLOSE,"");
+ }
+
+ if (getOn() == true) {
+ processCommand(_PLAYER_OFF,"");
+ }
+
+ if (getOn() == false) {
+ processCommand(_PLAYER_ON,"");
+ }
+ output->setBytesCounter(0);
+
+ input=InputPlugin::createInputStream(args,_INPUT_THREADSAFE);
+ if (input == NULL) {
+ cout << "createInputStream failed"<<endl;
+ return processCommand(_PLAYER_OFF,"");
+ }
+ lFileSelected=true;
+ setMajorMode(_PLAYER_MAJOR_MODE_OPEN_TRACK);
+ input->open(args);
+ if (plugin->setInputPlugin(input) == false) {
+ return processCommand(_PLAYER_OFF,"");
+ }
+ songPath->clear();
+ songPath->append(args);
+ if (lAutoPlay) {
+ return processCommand(_PLAYER_PLAY,"");
+ }
+ return "";
+ }
+
+ if (command == _PLAYER_CLOSE) {
+ if (lFileSelected) {
+ processCommand(_PLAYER_PAUSE,"");
+ plugin->close();
+
+ delete input;
+ input=NULL;
+ setMajorMode(_PLAYER_MAJOR_MODE_CLOSE_TRACK);
+ lFileSelected=false;
+ }
+ return "";
+ }
+
+
+ if (command == _PLAYER_PLAY) {
+ if (lFileSelected) {
+ setMajorMode(_PLAYER_MAJOR_MODE_PLAYING);
+ plugin->play();
+ return "";
+ }
+ return "no file";
+ }
+
+ if (command == _PLAYER_PAUSE) {
+ int mode=getMajorMode();
+ if (mode == _PLAYER_MAJOR_MODE_PLAYING) {
+ plugin->pause();
+ setMajorMode(_PLAYER_MAJOR_MODE_PAUSE);
+ }
+
+ return "";
+ }
+
+ if (command == _YAF_I_PLAYTIME) {
+ int current=plugin->getTime(true);
+ int total=plugin->getTime(false);
+
+ cout << "Command:0 Msg:playtime current:"<<current<<" total:"<<total<<endl;
+ return "";
+ }
+
+ // another YAF part:
+ if (command == _PLAYER_JUMP) {
+ if (lFileSelected) {
+ int nSec=0;
+ int mode=getMajorMode();
+ processCommand(_PLAYER_PAUSE,"");
+ sscanf(args,"%d",&nSec); // convert string to int
+ /* if the value is signed, do a relative jump */
+ if(index(args, '-') || index(args, '+')) {
+ nSec += plugin->getTime(true);
+ }
+ // nPos is the time in seconds!!!!
+ plugin->seek(nSec);
+ output->setBytesCounter(0);
+
+ if (mode == _PLAYER_MAJOR_MODE_PLAYING) {
+ processCommand(_PLAYER_PLAY,"");
+ }
+ return "";
+ }
+ return "no file";
+ }
+
+
+ if (command == _YAF_I_RUNTIME) {
+ if (strcmp("off",args)==0) {
+ plugin->config("runtime","off",NULL);
+ } else {
+ plugin->config("runtime","on",NULL);
+ }
+ }
+ if (command == _PLAYER_MUSICINFO) {
+ PluginInfo* plugInfo=plugin->getPluginInfo();
+ output->writeInfo(plugInfo);
+ return "";
+ }
+
+ if (command == _YAF_I_SELECT_A_LAYER) {
+ plugin->config("AudioLayer",args,NULL);
+ return "";
+ }
+ if (command == _YAF_I_SELECT_V_LAYER) {
+ plugin->config("VideoLayer",args,NULL);
+ return "";
+ }
+
+ if (command == _YAF_I_WRITE) {
+ if (strcmp(args,"on")==0) {
+ plugin->config("-w","true",NULL);
+ } else {
+ plugin->config("-w","false",NULL);
+ }
+ return "";
+ }
+
+
+ return (InputDecoderXPlayer::processCommand(command,args));
+}
+
+
+
+
+int InputDecoderYAF::getAutoPlay() {
+ return lAutoPlay;
+}
+
+void InputDecoderYAF::setAutoPlay(int lAutoPlay) {
+ this->lAutoPlay=lAutoPlay;
+}
diff --git a/mpeglib/example/yaf/yafxplayer/inputDecoderYAF.h b/mpeglib/example/yaf/yafxplayer/inputDecoderYAF.h
new file mode 100644
index 00000000..2002dc58
--- /dev/null
+++ b/mpeglib/example/yaf/yafxplayer/inputDecoderYAF.h
@@ -0,0 +1,51 @@
+/*
+ base Implementation of a yaf decoder
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __INPUTDECODERYAF_H
+#define __INPUTDECODERYAF_H
+
+#include <pthread.h>
+
+#include "inputDecoderXPlayer.h"
+#include "yafOutputStream.h"
+#include <kdemacros.h>
+
+
+class KDE_EXPORT InputDecoderYAF : public InputDecoderXPlayer {
+
+ DecoderPlugin* plugin;
+ YafOutputStream* output;
+ InputStream* input;
+ int lAutoPlay;
+
+ public:
+ InputDecoderYAF(DecoderPlugin* plugin,YafOutputStream* output);
+ ~InputDecoderYAF();
+
+ const char* processCommand(int command,const char* args);
+ void doSomething();
+
+ int getAutoPlay();
+ void setAutoPlay(int lAutoPlay);
+
+ private:
+
+ int lFileSelected;
+ Buffer* songPath;
+};
+
+
+#endif
+
diff --git a/mpeglib/example/yaf/yafxplayer/runtimeTableXPlayer.cpp b/mpeglib/example/yaf/yafxplayer/runtimeTableXPlayer.cpp
new file mode 100644
index 00000000..792d0d45
--- /dev/null
+++ b/mpeglib/example/yaf/yafxplayer/runtimeTableXPlayer.cpp
@@ -0,0 +1,37 @@
+/*
+ valid runtime commands for generic player
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#define _USE_RUNTIME_XPLAYER_STRUC
+
+#include "runtimeTableXPlayer.h"
+
+
+RuntimeTableXPlayer::RuntimeTableXPlayer(){
+ init();
+}
+
+RuntimeTableXPlayer::~RuntimeTableXPlayer(){
+}
+
+void RuntimeTableXPlayer::init() {
+
+
+ int i;
+ for (i=0;i<XPLAYERRUNTIME_SIZE;i++) {
+ insert(&xplayerRuntime[i]);
+ }
+
+
+}
+
+
diff --git a/mpeglib/example/yaf/yafxplayer/runtimeTableXPlayer.h b/mpeglib/example/yaf/yafxplayer/runtimeTableXPlayer.h
new file mode 100644
index 00000000..8f3ad2d0
--- /dev/null
+++ b/mpeglib/example/yaf/yafxplayer/runtimeTableXPlayer.h
@@ -0,0 +1,40 @@
+/*
+ valid runtime commands for generic player
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __RUNTIME_TABLEXPLAYER_H
+#define __RUNTIME_TABLEXPLAYER_H
+
+#include "../yafcore/commandTable.h"
+#include "xplayerRuntime.defs"
+
+
+
+
+
+class RuntimeTableXPlayer : public CommandTable {
+
+ public:
+ RuntimeTableXPlayer();
+ ~RuntimeTableXPlayer();
+ void init();
+
+};
+
+#endif
+
+
+
+
+
diff --git a/mpeglib/example/yaf/yafxplayer/xplayerCommand.defs b/mpeglib/example/yaf/yafxplayer/xplayerCommand.defs
new file mode 100644
index 00000000..853eb2eb
--- /dev/null
+++ b/mpeglib/example/yaf/yafxplayer/xplayerCommand.defs
@@ -0,0 +1,84 @@
+/*
+ definition file for generic decoder commands
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __XPLAYER_COMMAND_DEFS
+#define __XPLAYER_COMMAND_DEFS
+
+#include "../yafcore/commandTable.h" // defines CommandDesc-Struct...
+
+// Command Numbers
+
+#define _PLAYER_ON _CT_START+1
+#define _PLAYER_OFF _CT_START+2
+#define _PLAYER_OPEN _CT_START+3
+#define _PLAYER_CLOSE _CT_START+4
+
+#define _PLAYER_PLAY _CT_START+5
+#define _PLAYER_PAUSE _CT_START+6
+#define _PLAYER_VERBOSE _CT_START+7
+#define _PLAYER_JUMP _CT_START+8
+
+
+#define _PLAYER_OUTPUTFILE _CT_START+9
+#define _PLAYER_CLOSEOUTPUTFILE _CT_START+10
+#define _PLAYER_INTERNALAUDIO _CT_START+11
+#define _PLAYER_SLEEP _CT_START+12
+#define _PLAYER_UPDATE _CT_START+13
+#define _PLAYER_CLEAR _CT_START+14
+#define _PLAYER_MUSICINFO _CT_START+15
+
+
+#define _XPLAYER_START _CT_START+100
+
+
+// The commands as String. The Syntax is :
+//
+
+// { lexternalUse,lReturnMsg,longName,shortName,number,helpText }
+//
+// lexternal use ist true when the text should be written when
+// user enters "help"
+
+#ifdef _USE_XPLAYER_STRUC
+static struct CommandDescriptionStruct xplayerCommands[]={
+
+ { 1,1,"on","",_PLAYER_ON,"turn decoder ON"},
+ { 1,1,"off","",_PLAYER_OFF,"turn it OFF"},
+ { 1,1,"open","o",_PLAYER_OPEN,"open <filename>"},
+ { 1,1,"close","c",_PLAYER_CLOSE,"closes current file "},
+ { 1,1,"play" ,"p",_PLAYER_PLAY,"plays the mpeg file" },
+ { 1,1,"verbose" ,"v",_PLAYER_VERBOSE,"verbose [on|off] info about player" },
+ { 1,1,"jump" ,"j",_PLAYER_JUMP,"jump [+|-]<second>" },
+ { 1,1,"pause" ,"a",_PLAYER_PAUSE,"pauses playing mpeg file"},
+ { 1,1,"update" ,"",_PLAYER_UPDATE,"checks state of plugin"},
+ { 1,1,"clear" ,"",_PLAYER_CLEAR,"usefull loopback for frontends"},
+ { 1,1,"musicinfo" ,"",_PLAYER_MUSICINFO,"print useful info about stream"},
+ { 1,1,"outputfile" ,"",_PLAYER_OUTPUTFILE,"outputfile foo.txt" },
+ { 1,1,"closeoutputfile" ,"",_PLAYER_CLOSEOUTPUTFILE,
+ "closes the outputfile " },
+ { 1,1,"sleep" ,"",_PLAYER_SLEEP,"sleep for x seconds"},
+ {1,1,"audio" ,"",_PLAYER_INTERNALAUDIO,
+ "audio [on|off] for internal audiodevice" }
+
+};
+#endif
+
+
+// How much Commands are in the Array :
+#define XPLAYERCOMMANDS_SIZE 15
+
+
+
+#endif
+
+
diff --git a/mpeglib/example/yaf/yafxplayer/xplayerRuntime.defs b/mpeglib/example/yaf/yafxplayer/xplayerRuntime.defs
new file mode 100644
index 00000000..34402881
--- /dev/null
+++ b/mpeglib/example/yaf/yafxplayer/xplayerRuntime.defs
@@ -0,0 +1,123 @@
+/*
+ Runtime definitions from generic player
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __XPLAYER_RUNTIME_DEFS
+#define __XPLAYER_RUNTIME_DEFS
+
+#include "../yafcore/commandTable.h" // defines CommandDesc-Struct...
+#include "../yafcore/yafRuntime.defs"
+
+// Command Numbers
+
+#define _PLAYER_RUN_PLAYER_STATUS _YAF_RUN_START+1
+
+#define _PLAYER_RUN_STREAMINFO_START _YAF_RUN_START+2
+#define _PLAYER_RUN_STREAMINFO_CHANNELS _YAF_RUN_START+3
+#define _PLAYER_RUN_STREAMINFO_SAMPLESIZE _YAF_RUN_START+4
+#define _PLAYER_RUN_STREAMINFO_SPEED _YAF_RUN_START+5
+#define _PLAYER_RUN_STREAMINFO_END _YAF_RUN_START+6
+
+#define _PLAYER_RUN_FILEOPEN _YAF_RUN_START+7
+
+#define _PLAYER_RUN_MUSICINFO_START _YAF_RUN_START+8
+#define _PLAYER_RUN_MUSICINFO_SONG_FILENAME _YAF_RUN_START+10
+#define _PLAYER_RUN_MUSICINFO_SONG_NAME _YAF_RUN_START+11
+#define _PLAYER_RUN_MUSICINFO_SONG_LEN _YAF_RUN_START+12
+#define _PLAYER_RUN_MUSICINFO_SONG_JUMPS _YAF_RUN_START+13
+#define _PLAYER_RUN_MUSICINFO_END _YAF_RUN_START+14
+#define _PLAYER_RUN_ID3_INFO_START _YAF_RUN_START+15
+#define _PLAYER_RUN_ID3_NAME _YAF_RUN_START+16
+#define _PLAYER_RUN_ID3_ARTIST _YAF_RUN_START+17
+#define _PLAYER_RUN_ID3_ALBUM _YAF_RUN_START+18
+#define _PLAYER_RUN_ID3_YEAR _YAF_RUN_START+19
+#define _PLAYER_RUN_ID3_COMMENT _YAF_RUN_START+20
+#define _PLAYER_RUN_ID3_GENRE _YAF_RUN_START+21
+#define _PLAYER_RUN_ID3_INFO_END _YAF_RUN_START+22
+#define _PLAYER_RUN_MP3_INFO_START _YAF_RUN_START+23
+#define _PLAYER_RUN_MP3_BPS _YAF_RUN_START+24
+#define _PLAYER_RUN_MP3_INFO_END _YAF_RUN_START+25
+
+
+
+
+
+#define _XPLAYER_RUN_START _YAF_RUN_START+100
+
+
+typedef struct {
+ int channels; //2 = stereo 1=mono
+ int nStreamPos; // Position in Stream on which this Info becomes "true"
+} ChannelInfo;
+
+
+typedef struct {
+ int samplesize; // 16 for 16 Bit
+ int nStreamPos; // Position in Stream on which this Info becomes "true"
+} SampleSizeInfo;
+
+
+typedef struct {
+ int channels; //2 = stereo 1=mono
+ int nStreamPos; // Position in Stream on which this Info becomes "true"
+} SpeedInfo;
+
+
+
+
+// The commands as String. The Syntax is :
+//
+// { lexternalUse,lReturnMsg,longName,shortName,number,helpText }
+//
+// lexternal use ist true when the text should be written when
+// user enters "help"
+#ifdef _USE_RUNTIME_XPLAYER_STRUC
+static struct CommandDescriptionStruct xplayerRuntime[]={
+
+ { 0,1,"player-status","",_PLAYER_RUN_PLAYER_STATUS,"state of cdplayer"},
+ { 0,1,"streamInfo-Start","",_PLAYER_RUN_STREAMINFO_START,"StreamInfo start"},
+ { 0,1,"streamInfo-Channels","",_PLAYER_RUN_STREAMINFO_CHANNELS,
+ "ChannelInfo"},
+ { 0,1,"streamInfo-SampleSize","",_PLAYER_RUN_STREAMINFO_SAMPLESIZE,
+ "SampleSizeInfo"},
+ { 0,1,"streamInfo-Speed","",_PLAYER_RUN_STREAMINFO_SPEED,"SpeedInfo"},
+ { 0,1,"streamInfo-End","",_PLAYER_RUN_STREAMINFO_END,"StreamInfo end"},
+ { 0,1,"musicinfo-Start","",_PLAYER_RUN_MUSICINFO_START,"begin block"},
+ { 0,1,"song_filename","",_PLAYER_RUN_MUSICINFO_SONG_FILENAME,
+ "filename to the song"},
+ { 0,1,"song_name","",_PLAYER_RUN_MUSICINFO_SONG_NAME,"name of the song"},
+ { 0,1,"song_len","",_PLAYER_RUN_MUSICINFO_SONG_LEN,"length in secs"},
+ { 0,1,"song_jumps","",_PLAYER_RUN_MUSICINFO_SONG_JUMPS,"jump entities in song"},
+ { 0,1,"musicinfo-End","",_PLAYER_RUN_MUSICINFO_END,"end block"},
+ { 0,1,"id3Info-Start","",_PLAYER_RUN_ID3_INFO_START,"ID3Info start"},
+ { 0,1,"id3Name","",_PLAYER_RUN_ID3_NAME,"ID3 Name"},
+ { 0,1,"id3Artist","",_PLAYER_RUN_ID3_ARTIST,"ID3 Artist"},
+ { 0,1,"id3Album","",_PLAYER_RUN_ID3_ALBUM,"ID3 Album"},
+ { 0,1,"id3Year","",_PLAYER_RUN_ID3_YEAR,"ID3 Year"},
+ { 0,1,"id3Comment","",_PLAYER_RUN_ID3_COMMENT,"ID3 Comment"},
+ { 0,1,"id3Genre","",_PLAYER_RUN_ID3_GENRE,"ID3 Genre"},
+ { 0,1,"id3Info-End","",_PLAYER_RUN_ID3_INFO_END,"ID3Info start"},
+ { 0,1,"mp3Info-Start","",_PLAYER_RUN_MP3_INFO_START,"MP3Info start"},
+ { 0,1,"mp3BitsPerSec","",_PLAYER_RUN_MP3_BPS,"Samplebits per second"},
+ { 0,1,"mp3Info-End","",_PLAYER_RUN_MP3_INFO_END,"MP3Info end"},
+ { 0,1,"fileopen", "",_PLAYER_RUN_FILEOPEN,
+ "sended before yaf tries to open outputfile"}
+
+};
+#endif
+
+// How much Commands are in the Array :
+#define XPLAYERRUNTIME_SIZE 24
+
+
+
+#endif
diff --git a/mpeglib/example/yaf/yafxplayer/xplayer_control.cpp b/mpeglib/example/yaf/yafxplayer/xplayer_control.cpp
new file mode 100644
index 00000000..c09b2cbd
--- /dev/null
+++ b/mpeglib/example/yaf/yafxplayer/xplayer_control.cpp
@@ -0,0 +1,44 @@
+/*
+ generic interactive controller
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "xplayer_control.h"
+#include <iostream>
+
+using namespace std;
+
+
+extern "C" void control_xplayer() {
+ InputInterface input;
+ OutputInterface output(&cout);
+ YafOutputStream yafOutput(&input);
+ InputDecoderXPlayer decoder(&yafOutput);
+
+
+ cout<< "Command:0 Msg:protocol yaf-0.1" << endl;
+ cout<< "Command:0 Msg:decoder generic player demo Version:0.1" << endl;
+ cout<< "Command:0 Msg:comment enter 'help' for list of commands" << endl;
+
+
+ yaf_control(&input,&output,&decoder);
+}
+
+
+/*
+
+int main() {
+ control_xplayer();
+}
+
+
+*/
diff --git a/mpeglib/example/yaf/yafxplayer/xplayer_control.h b/mpeglib/example/yaf/yafxplayer/xplayer_control.h
new file mode 100644
index 00000000..771e8ee4
--- /dev/null
+++ b/mpeglib/example/yaf/yafxplayer/xplayer_control.h
@@ -0,0 +1,25 @@
+
+
+#ifndef __XPLAYER_CONTROL_H
+#define __XPLAYER_CONTROL_H
+
+
+
+#include "../yafcore/inputInterface.h"
+#include "../yafcore/outputInterface.h"
+#include "inputDecoderXPlayer.h"
+#include "../yafcore/parser.h"
+
+#include "../yafcore/yaf_control.h"
+
+#include <sys/time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+
+
+// prototypes
+
+
+#endif
diff --git a/mpeglib/example/yaf/yafxplayer/yafOutputStream.cpp b/mpeglib/example/yaf/yafxplayer/yafOutputStream.cpp
new file mode 100644
index 00000000..95e61d05
--- /dev/null
+++ b/mpeglib/example/yaf/yafxplayer/yafOutputStream.cpp
@@ -0,0 +1,307 @@
+/*
+ concret OutputClass for yaf text consol player
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "yafOutputStream.h"
+#include <iostream>
+
+using namespace std;
+
+#define _AUDIO_DELAY_STAMPS 200
+
+
+
+
+YafOutputStream::YafOutputStream(InputInterface* input) {
+ lInternalDevice=false;
+ bytes=0;
+ allWrite=0;
+ writeToBuffer=0;
+ lOpen=false;
+ directOutput=OutPlugin::createOutputStream(_OUTPUT_LOCAL,_OUTPUT_THREADSAFE);
+
+
+ this->input=input;
+ ::pipe(fd);
+
+ if (errno < 0) {
+ perror("YafOutputStream pipe");
+ exit(0);
+ }
+ input->addFileDescriptor(fd[0]);
+}
+
+
+YafOutputStream::~YafOutputStream() {
+ delete directOutput;
+}
+
+
+int YafOutputStream::audioSetup(int frequency,int stereo,
+ int sign,int big,int bits) {
+
+ cout << "Command:0 Msg:streamInfo-Start"<<endl;
+ cout << "Command:0 Msg:streamInfo-Channels "<<stereo+1<<endl;
+
+ cout << "Command:0 Msg:streamInfo-SampleSize "<<bits<<endl;
+ cout << "Command:0 Msg:streamInfo-Speed "<<frequency<<endl;
+ cout << "Command:0 Msg:streamInfo-End"<<endl;
+
+ directOutput->audioSetup(frequency,stereo,sign,big,bits);
+
+ return true;
+}
+
+void YafOutputStream::audioOpen() {
+ if (lInternalDevice) {
+ directOutput->audioOpen();
+ }
+}
+
+void YafOutputStream::audioClose(void) {
+ if (lInternalDevice) {
+ // never close internal audio, because
+ // its not possible to open it again fast enough
+ //directOutput->audioClose();
+ } else {
+ directOutput->audioClose();
+ }
+}
+
+
+int YafOutputStream::audioPlay(TimeStamp* startStamp,
+ TimeStamp* endStamp,char *buffer, int size) {
+
+ int ret=0;
+ if (lInternalDevice) {
+ directOutput->audioPlay(startStamp,endStamp,buffer,size);
+ }
+
+
+
+ if (lOpen) {
+ // if we are doing video, we even have to sync
+ // audio writing.
+ // Because yaf sends the audio data to another process
+ // but the video data _not_ the audio data is directly
+ // flushed here, but maybe stay on the other side
+ // in a very big buffer
+ // until the protocol has no better handle
+ // for this, we must make sure to
+ // dont get the buffer on the other
+ // side too big.
+ // we sleep some time until we write the next sample
+
+
+
+
+ directOutput->audioPlay(startStamp,endStamp,buffer,size);
+
+
+ ret=::send(sd,buffer,size,0);
+
+
+
+
+
+
+ if (ret < 0) {
+
+ closeStream();
+ cout << "fifo error in streamWriter";
+ perror("writeStream");
+ exit(-1);
+ return ret;
+ }
+
+ bytes=bytes+size;
+ allWrite+=size;
+ return size;
+ }
+
+
+
+
+ bytes=bytes+size;
+ allWrite+=size;
+ return size;
+}
+
+
+void YafOutputStream::audioFlush() {
+ // notify command line, that plugin state changed
+ input->write(fd[1],"update\n");
+
+ if (lInternalDevice) {
+ directOutput->audioFlush();
+ }
+}
+
+int YafOutputStream::getPreferredDeliverSize() {
+ return directOutput->getPreferredDeliverSize();
+}
+
+
+
+
+int YafOutputStream::openWindow(int width, int height,const char *title) {
+ return directOutput->openWindow(width,height,title);
+}
+
+
+void YafOutputStream::closeWindow() {
+ directOutput->closeWindow();
+}
+
+void YafOutputStream::flushWindow() {
+ directOutput->flushWindow();
+}
+
+PictureArray* YafOutputStream::lockPictureArray() {
+ return directOutput->lockPictureArray();
+}
+
+
+void YafOutputStream::unlockPictureArray(PictureArray* pictureArray) {
+ directOutput->unlockPictureArray(pictureArray);
+}
+
+
+int YafOutputStream::getFrameusec() {
+ return directOutput->getFrameusec();
+}
+
+
+
+int YafOutputStream::getOutputInit() {
+ return directOutput->getOutputInit();
+}
+
+
+void YafOutputStream::setOutputInit(int lInit) {
+ directOutput->setOutputInit(lInit);
+}
+
+
+
+
+void YafOutputStream::writeInfo(PluginInfo* pluginInfo) {
+ char* url=pluginInfo->getUrl();
+ const char* nameStart=strrchr(url,'/');
+
+ if (nameStart==NULL) {
+ nameStart="noname";
+ } else {
+ nameStart++;
+ if (strlen(nameStart) == 0) {
+ nameStart="noname";
+ }
+ }
+
+ cout << "Command:0 Msg:musicinfo-Start"<<endl;
+ cout << "Command:0 Msg:song_filename "<<pluginInfo->getUrl()<<endl;
+ cout << "Command:0 Msg:song_name "<<nameStart<<endl;
+ cout << "Command:0 Msg:song_len "<<pluginInfo->getLength()<<endl;
+ cout << "Command:0 Msg:song_jumps 0"<<endl;
+ cout << "Command:0 Msg:musicinfo-End"<<endl;
+}
+
+
+
+
+
+
+void YafOutputStream::setBytesCounter(long value) {
+ bytes=value;
+ writeToBuffer=value;
+}
+
+
+
+int YafOutputStream::isOpenStream() {
+ return lOpen;
+}
+
+
+void YafOutputStream::setStreamFile(const char* filename) {
+ if (lOpen == true) {
+ closeStream();
+ }
+ this->filename=filename;
+}
+
+
+int YafOutputStream::openStream() {
+ if (lOpen == true) {
+ cout << "stream already open! call ignored"<<endl;
+ return sd;
+ }
+ sd=::socket(AF_UNIX,SOCK_STREAM,0);
+ if (sd < 0) {
+ perror("sd-Socket StreamWriter");exit(1);
+ }
+
+ unsigned int i=0;
+ sockad.sun_family=AF_UNIX;
+
+ while(i<strlen(filename)) {
+ sockad.sun_path[i]=filename[i];
+ i++;
+ }
+ sockad.sun_path[i]=0;
+ if (::connect(sd,(sockaddr*)&sockad,strlen(filename)+2)<0) {
+ perror("connect StreamWriter");exit(1);
+ }
+ if (sd > 0) {
+ lOpen=true;
+ }
+ return sd;
+}
+
+
+int YafOutputStream::closeStream() {
+ int ret=0;
+ if (lOpen) {
+ ret=close(sd);
+ lOpen=false;
+ filename=NULL;
+ } else {
+ cout << "stream already closed. call ignored!"<<endl;
+ }
+ return ret;
+}
+
+
+void YafOutputStream::internalDevice(int lInternal){
+ lInternalDevice=lInternal;
+}
+
+
+
+long YafOutputStream::getBytesCounter() {
+ return bytes;
+}
+
+
+long YafOutputStream::getAllWriteCounter() {
+ return allWrite;
+}
+
+
+
+void YafOutputStream::config(const char* key,const char* value,
+ void* user_data) {
+
+ directOutput->config(key,value,user_data);
+
+}
+
diff --git a/mpeglib/example/yaf/yafxplayer/yafOutputStream.h b/mpeglib/example/yaf/yafxplayer/yafOutputStream.h
new file mode 100644
index 00000000..bb7df423
--- /dev/null
+++ b/mpeglib/example/yaf/yafxplayer/yafOutputStream.h
@@ -0,0 +1,140 @@
+/*
+ concret OutputClass for yaf text consol player
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __YAFOUTPUTSTREAM_H
+#define __YAFOUTPUTSTREAM_H
+
+
+
+//
+// If we build mpeglib we include the path absolute
+// otherwise we must have installed the includes (eg /usr/include/mpeglib)
+//
+
+#ifdef DIRECT_INCLUDE
+#include "../../../lib/output/avSyncer.h"
+#include "../../../lib/output/pluginInfo.h"
+#include "../../../lib/decoder/decoderPlugin.h"
+#include "../../../lib/input/inputPlugin.h"
+#include "../../../lib/output/outPlugin.h"
+#else
+#include <mpeglib/output/avSyncer.h>
+#include <mpeglib/output/pluginInfo.h>
+#include <mpeglib/decoder/decoderPlugin.h>
+#include <mpeglib/input/inputPlugin.h>
+#include <mpeglib/output/outPlugin.h>
+#endif
+
+#include <sys/un.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <kdemacros.h>
+
+// If you get compile errors when using mpeglib
+// simply remove the yaf dependencies
+#include "../yafcore/inputInterface.h"
+
+
+
+class KDE_EXPORT YafOutputStream : public OutputStream {
+
+
+ long bytes;
+ int lInternalDevice;
+ long allWrite;
+ int lOpen;
+ int sd;
+ struct sockaddr_un sockad;
+ const char* filename;
+ OutputStream* directOutput;
+ int fd[2];
+ InputInterface* input;
+
+ int writeToBuffer;
+
+
+
+
+ public:
+ // to the filedescriptor we write status messages from
+ // the thread. This makes it thread safe
+ YafOutputStream(InputInterface* input);
+ ~YafOutputStream();
+
+ // Audio Output
+
+ int audioSetup(int frequency,int stereo,int sign,int big,int sixteen);
+ void audioClose(void);
+ int audioPlay(TimeStamp* startStamp,
+ TimeStamp* endStamp,char *buffer, int size);
+
+ void audioFlush();
+ void audioOpen();
+
+ int getPreferredDeliverSize();
+
+
+ // Video part (no yaf protocol for this)
+ // Maybe in a future version we distribute this interface
+ // over shared mem or something, but for now
+ // a video player must include the mpeg video lib directly
+ // we forward the calls to the direct output
+
+
+ int openWindow(int width, int height,const char *title);
+ void closeWindow();
+ void flushWindow();
+
+ PictureArray* lockPictureArray();
+ void unlockPictureArray(PictureArray* pictureArray);
+
+ int getFrameusec();
+
+ int getDepth();
+ int getOutputInit();
+ void setOutputInit(int lInit);
+
+
+ // Info Output
+ void writeInfo(PluginInfo* pluginInfo);
+
+ void config(const char* key,const char* value,void* user_data);
+
+
+ // additional functionality (needed for the yaf-protocol)
+ void setBytesCounter(long value);
+
+
+ int isOpenStream();
+ void setStreamFile(const char* filename);
+ int openStream();
+ int closeStream();
+
+ void internalDevice(int lInternal);
+ long getBytesCounter();
+ long getAllWriteCounter();
+
+
+
+
+
+
+
+};
+
+
+
+#endif
diff --git a/mpeglib/example/yaf/yafyuv/Makefile.am b/mpeglib/example/yaf/yafyuv/Makefile.am
new file mode 100644
index 00000000..3400a34c
--- /dev/null
+++ b/mpeglib/example/yaf/yafyuv/Makefile.am
@@ -0,0 +1,37 @@
+# splay-yaf - Makefile.am
+
+INCLUDES = -I../../include $(all_includes)
+
+EXTRA_DIST =
+
+bin_PROGRAMS = yaf-yuv
+
+yaf_yuv_SOURCES = yuv_control.cpp
+
+
+noinst_HEADERS =
+
+yaf_yuv_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+
+yaf_yuv_LDADD = ../yafxplayer/libyafxplayer.la \
+ ../yafcore/libyafcore.la \
+ ../../../lib/libmpeg.la \
+ $(THIS_LIB_LIBS)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/example/yaf/yafyuv/yuv_control.cpp b/mpeglib/example/yaf/yafyuv/yuv_control.cpp
new file mode 100644
index 00000000..b62e50cf
--- /dev/null
+++ b/mpeglib/example/yaf/yafyuv/yuv_control.cpp
@@ -0,0 +1,207 @@
+/*
+ generic interactive controller
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+// Set for local include
+#define DIRECT_INCLUDE
+
+#include "../yafcore/yaf_control.h"
+#include "../yafxplayer/inputDecoderYAF.h"
+
+#include <iostream>
+using namespace std;
+
+#include <math.h>
+
+
+
+// we include our plugin here
+#include "../../../lib/yuv/yuvPlugin.h"
+
+#if defined(HAVE_GETOPT_H)
+#include <getopt.h>
+#endif
+
+
+void control_yuv(InputInterface* input,OutputInterface* output,
+ InputDecoder* decoder) {
+
+
+ cout<< "Command:0 Msg:protocol yaf-0.1" << endl;
+ cout<< "Command:0 Msg:implements xplayer" << endl;
+ cout<< "Command:0 Msg:decoder yuv Version:0.0.2" << endl;
+ cout<< "Command:0 Msg:mimetypes vide/yuv1;" << endl;
+ cout<< "Command:0 Msg:comment yuv Plugin by Martin Vogt" << endl;
+ cout<< "Command:0 Msg:comment yaf port by mvogt@rhrk.uni-kl.de"<<endl;
+ cout<< "Command:0 Msg:comment enter 'help' " << endl;
+
+
+
+ yaf_control(input,output,decoder);
+}
+
+
+void usage() {
+ cout << "yaf-yuv is a interactive frontend for the still image decoder"
+ <<endl;
+ cout << "Usage : yaf-yuv [w:g:y:f:hr:] <file.raw> [url]"<<endl;
+ cout << endl;
+ cout << "-w : width of raw yuv stream in pixel"<<endl;
+ cout << "-g : height of raw yuv stream in pixel"<<endl;
+ cout << "-h : help"<<endl;
+ cout << "-y : set image Format:"<<endl;
+ cout << " 1 : yuv lum/Cr/Cb"<<endl;
+ cout << " 2 : yuv lum/Cb/Cr"<<endl;
+ cout << " 3 : rgb 24 BIT"<<endl;
+ cout << " 4 : rgb 24 BIT (flipped)"<<endl;
+
+ cout << "-f : [1..n] frames per second"<<endl;
+
+ cout << endl;
+ cout << "THIS SOFTWARE COMES WITH ABSOLUTELY NO WARRANTY! " \
+ << "USE AT YOUR OWN RISK!"<<endl;
+ cout << endl;
+ cout << "for more help type 'help' in interactive mode"<<endl;
+}
+
+
+int readFormat(int* w,int* h,char* filename) {
+ char formatFileName[100];
+ FILE* file=NULL;
+ snprintf(formatFileName,100,"%s.format",filename);
+ cout << "open:"<<formatFileName<<endl;
+ file=fopen(formatFileName,"r");
+ if (file == NULL) {
+ cout << "no format file found"<<endl;
+ return false;
+ }
+
+ if (fscanf(file,"Version 0.1\nw:%dh:%d\n",w,h)==2) {
+ cout << "read ! w:"<<*w<<"h:"<<*h<<endl;
+ return true;
+ }
+
+
+ cout << "format file mismatch"<<endl;
+ return false;
+}
+
+
+int main(int argc, char** argv) {
+ int c;
+ int lInternalAudio=true;
+ char* width=NULL;
+ char* height=NULL;
+ int fps=1;
+
+ pow(6.0,3.0); // fixes bug in __math.h
+
+
+
+
+ InputInterface* input=new InputInterface();
+
+ OutputInterface output(&cout);
+
+ YafOutputStream* yafOutput=new YafOutputStream(input);
+
+ YUVPlugin* plugin=new YUVPlugin();
+
+ plugin->setOutputPlugin(yafOutput);
+ InputDecoderYAF decoder(plugin,yafOutput);
+
+
+
+ while(1) {
+ c = getopt (argc, argv, "w:g:y:hf:");
+ if (c == -1) break;
+ switch(c) {
+ case 'a': {
+ lInternalAudio=false;
+ break;
+ }
+ case 'h': {
+ cout << "query for help"<<endl;
+ usage();
+ exit(0);
+ }
+ case 'w': {
+ width=optarg;
+ printf("setting width:%s\n",width);
+ break;
+ }
+ case 'f': {
+ fps=atoi(optarg);
+ if (fps <= 0) {
+ cout << "wrong fps. must be > 0"<<endl;
+ exit(0);
+ }
+ break;
+ }
+ case 'g': {
+ height=optarg;
+ printf("setting height:%s\n",height);
+ break;
+ }
+ case 'y': {
+ cout << "config:imageType:"<<optarg<<endl;
+ plugin->config("imageType",optarg,NULL);
+ break;
+ }
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ usage();
+ exit(-1);
+ }
+ }
+ if (optind < argc ) {
+ Buffer buffer(300);
+ buffer.append("open ");
+ buffer.append(argv[optind]);
+ buffer.append("\n");
+
+ input->addInputLine(&buffer);
+
+ }
+ if ((width == NULL) || (height == NULL)) {
+ int w;
+ int h;
+ cout << "automatic try to read a .format file"<<endl;
+ if (readFormat(&w,&h,argv[optind]) == false) {
+ cout << "no format specified"<<endl;
+ exit(-1);
+ }
+ width=new char[20];
+ height=new char[20];
+ snprintf(width,20,"%d",w);
+ snprintf(height,20,"%d",h);
+ }
+
+ char* picPerSec=new char[20];
+ snprintf(picPerSec,20,"%d",fps);
+
+
+ plugin->config("picPerSec",picPerSec,NULL);
+ plugin->config("height",height,NULL);
+ plugin->config("width",width,NULL);
+
+
+ yafOutput->internalDevice(lInternalAudio);
+ control_yuv(input,&output,&decoder);
+ delete plugin;
+ delete yafOutput;
+ delete input;
+
+}
+
diff --git a/mpeglib/lib/Makefile.am b/mpeglib/lib/Makefile.am
new file mode 100644
index 00000000..74a7d181
--- /dev/null
+++ b/mpeglib/lib/Makefile.am
@@ -0,0 +1,36 @@
+# player - Makefile.am
+
+SUBDIRS = frame util input output decoder \
+ splay oggvorbis mpegplay mpgplayer tplay yuv
+
+MMXFILES =
+
+
+EXTRA_DIST = README
+
+
+THIS_EXTRALIBS = mpgplayer/libmpgplayer.la \
+ mpegplay/libmpegplay.la \
+ splay/libsplay.la \
+ oggvorbis/liboggvorbisbase.la \
+ tplay/libtplay.la \
+ decoder/libdecoder.la \
+ output/liboutput.la \
+ input/libinput.la \
+ util/libutil.la \
+ frame/libframe.la \
+ yuv/libyuvPlugin.la
+
+
+
+lib_LTLIBRARIES = libmpeg.la
+
+libmpeg_la_SOURCES = dummy.cpp
+
+
+libmpeg_la_LDFLAGS = $(all_libraries) \
+ -release $(THIS_LIB_VERSION)
+
+
+libmpeg_la_LIBADD = $(THIS_EXTRALIBS) $(MMXFILES) \
+ $(THIS_LIB_LIBS)
diff --git a/mpeglib/lib/README b/mpeglib/lib/README
new file mode 100644
index 00000000..91a3b071
--- /dev/null
+++ b/mpeglib/lib/README
@@ -0,0 +1,80 @@
+
+
+HOW TO WRITE A PLUGIN
+=====================
+
+we need a audio cd player and a grabber and <insert your cool plugin here>
+The Plugin approach is minimalistic.
+
+The plugin interface only supports one thing:
+
+* open a stream play it and then close it.
+
+nothing more.
+
+
+For this you have a class "decoderPlugin.cpp" derive your decoder
+from this class and then overwrite the decoder_loop method.
+Thats it.
+
+The typical structure in decoder loop look like this:
+
+void decoder_loop() {
+
+1. make instance of decoder and connect it to the input/output
+2. go in loop and check sometime a few mutex variables
+3. leave loop and destroy your decoder
+
+}
+
+For this your have 3 examples how it can be done.
+I think splayPlugin.cpp and mpegPlugin.cpp are clean/good
+examples for "education".
+
+
+If your decoder supports "seek" you are a bit on your own.
+You can look in splayPlugin.cpp how it is handled there.
+If your decoder supports seek you must overwrite the seek method.
+
+Ok, now you have your raw plugin.
+If you want to make it a real KDE compliant player with arts integration
+you need to write an arts plugin.
+
+
+
+further readings
+======================
+
+* This interface doesnt offer a gui based config dialog
+
+ To my mind a configuration dialog does not belong into a decoder plugin.
+ And thats the main reason why other solutions are not portable:
+ The plugin interface depend on a widget lib.
+
+
+* This version has a completely reworked plugin interface.
+ I removed the C bindings. And now the interface is C++.
+ This made it much more readable.
+ This should make it a bit easier to inlcude new decoders.
+ For example look in the directories for mainXYZ.cpp
+
+inputPlugin:
+ inputInterfaces like file,http,buffered(loopback) ,cdi
+outPlugin :
+ output for audio/video
+playerPlugin:
+ this is the base class fo new decoders
+splay :
+ mpeg I Layer 1,2,3 audio decoder
+mpegplay :
+ mpeg I video decoder
+mpgplayer :
+ mpeg I audio/video decoder
+tplay :
+ wav decoder
+vorbisPlugin:
+ vorbis decoder.(GPLed mp3 replacement, better compression,
+ better sound ->www.xiph.org)
+
+
+ \ No newline at end of file
diff --git a/mpeglib/lib/decoder/Makefile.am b/mpeglib/lib/decoder/Makefile.am
new file mode 100644
index 00000000..9362e851
--- /dev/null
+++ b/mpeglib/lib/decoder/Makefile.am
@@ -0,0 +1,39 @@
+# libplayerplugin - Makefile.am
+
+INCLUDES = $(all_includes)
+
+noinst_LTLIBRARIES = libdecoder.la
+
+kmpgincludedir = $(includedir)/$(THIS_LIB_NAME)/decoder
+
+kmpginclude_HEADERS = decoderPlugin.h command.h \
+ commandPipe.h nukePlugin.h \
+ vorbisPlugin.h cddaPlugin.h \
+ splayPlugin.h mpegPlugin.h \
+ mpgPlugin.h tplayPlugin.h
+
+libdecoder_la_SOURCES = decoderPlugin.cpp command.cpp \
+ commandPipe.cpp nukePlugin.cpp \
+ vorbisPlugin.cpp cddaPlugin.cpp \
+ splayPlugin.cpp mpegPlugin.cpp \
+ mpgPlugin.cpp tplayPlugin.cpp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/lib/decoder/cddaPlugin.cpp b/mpeglib/lib/decoder/cddaPlugin.cpp
new file mode 100644
index 00000000..fe84986b
--- /dev/null
+++ b/mpeglib/lib/decoder/cddaPlugin.cpp
@@ -0,0 +1,150 @@
+/*
+ splay player plugin
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "cddaPlugin.h"
+
+#ifdef CDDA_PARANOIA
+
+#include <sys/types.h>
+
+#include <iostream>
+
+using namespace std;
+
+typedef int16_t size16;
+typedef int32_t size32;
+
+extern "C" {
+#include <cdda_interface.h>
+#include <cdda_paranoia.h>
+}
+
+
+CDDAPlugin::CDDAPlugin() {
+}
+
+
+CDDAPlugin::~CDDAPlugin() {
+}
+
+
+// here we can config our decoder with special flags
+void CDDAPlugin::config(const char* key,const char* value,void* user_data) {
+ DecoderPlugin::config(key,value,user_data);
+}
+
+
+
+void CDDAPlugin::decoder_loop() {
+
+ if (input == NULL) {
+ cout << "CDDAPlugin::decoder_loop input is NULL"<<endl;
+ exit(0);
+ }
+ if (output == NULL) {
+ cout << "CDDAPlugin::decoder_loop output is NULL"<<endl;
+ exit(0);
+ }
+ // init decoder
+ output->audioInit();
+ TimeStamp* stamp;
+ char buf[CD_FRAMESIZE_RAW*4];
+ int len=0;
+ // start decoding
+ while(runCheck()) {
+
+ switch(streamState) {
+ case _STREAM_STATE_FIRST_INIT :
+ output->audioSetup(44100,1,0,0,16);
+ output->audioOpen();
+ setStreamState(_STREAM_STATE_PLAY);
+ len=getTotalLength();
+ pluginInfo->setLength(len);
+ output->writeInfo(pluginInfo);
+ break;
+ case _STREAM_STATE_INIT :
+ setStreamState(_STREAM_STATE_PLAY);
+ break;
+ case _STREAM_STATE_PLAY : {
+ // decode
+ int read=input->read(buf,2*CD_FRAMESIZE_RAW);
+ int pos=input->getBytePosition();
+ stamp=input->getTimeStamp(pos);
+ output->audioPlay(stamp,stamp,buf,read);
+ break;
+ }
+ case _STREAM_STATE_WAIT_FOR_END:
+ // exit while loop
+ lDecoderLoop=false;
+ break;
+ default:
+ cout << "unknown stream state:"<<streamState<<endl;
+ }
+ }
+ // remove decoder
+
+ output->audioFlush();
+}
+
+// splay can seek in streams
+int CDDAPlugin::seek_impl(int second) {
+ int bytes_per_second;
+ int seconds;
+ int back;
+ float frequence=44100;
+ int bits=16;
+ int channels=2;
+
+ bytes_per_second = (int)(frequence * channels * (bits / 8));
+
+
+
+ seconds = bytes_per_second * second;
+ cout << "seek to :"<<seconds<<endl;
+ back=input->seek(seconds);
+
+ return true;
+}
+
+
+int CDDAPlugin::getTotalLength() {
+ shutdownLock();
+ float wavfilesize;
+ int back=0;
+ float frequence=44100;
+ if (input == NULL) {
+ shutdownUnlock();
+ return 0;
+ }
+ wavfilesize = input->getByteLength();
+ int bits=16;
+ int channels=2;
+ if (bits == 16) {
+ wavfilesize=wavfilesize/2;
+ }
+ if (channels==2) {
+ wavfilesize=wavfilesize/2;
+ }
+ if (frequence != 0) {
+ back=(int)(wavfilesize/frequence);
+ }
+ shutdownUnlock();
+ return back;
+}
+
+
+
+
+#endif
+//CDDA_PARANOIA
+
diff --git a/mpeglib/lib/decoder/cddaPlugin.h b/mpeglib/lib/decoder/cddaPlugin.h
new file mode 100644
index 00000000..c0adf5e2
--- /dev/null
+++ b/mpeglib/lib/decoder/cddaPlugin.h
@@ -0,0 +1,49 @@
+/*
+ splay player plugin
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __CDDAPLUGIN_H
+#define __CDDAPLUGIN_H
+
+#include "nukePlugin.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef CDDA_PARANOIA
+class CDDAPlugin : public NukePlugin {
+};
+#else
+
+
+
+class CDDAPlugin : public DecoderPlugin {
+
+ public:
+ CDDAPlugin();
+ ~CDDAPlugin();
+
+ void decoder_loop();
+ int seek_impl(int second);
+ void config(const char* key,const char* value,void* user_data);
+
+ protected:
+ int getTotalLength();
+
+
+};
+
+#endif
+//CDDA_PARANOIA
+
+#endif
diff --git a/mpeglib/lib/decoder/command.cpp b/mpeglib/lib/decoder/command.cpp
new file mode 100644
index 00000000..8b29efe6
--- /dev/null
+++ b/mpeglib/lib/decoder/command.cpp
@@ -0,0 +1,85 @@
+/*
+ describes a command
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include "command.h"
+
+#include <iostream>
+
+using namespace std;
+
+Command::Command(int id) {
+ this->id=id;
+ this->intArg=0;
+}
+
+Command::Command(int id,int arg) {
+ this->id=id;
+ this->intArg=arg;
+}
+
+
+Command::~Command() {
+}
+
+
+int Command::getID() {
+ return id;
+}
+
+
+int Command::getIntArg() {
+ return intArg;
+}
+
+
+void Command::copyTo(Command* dest) {
+ dest->id=this->id;
+ dest->intArg=this->intArg;
+}
+
+void Command::print(const char* text) {
+ cout << "COMMAND:"<<text<<endl;
+ switch(id) {
+ case _COMMAND_NONE:
+ cout << "_COMMAND_NONE";
+ break;
+ case _COMMAND_PING:
+ cout << "_COMMAND_PING";
+ break;
+ case _COMMAND_PAUSE:
+ cout << "_COMMAND_PAUSE";
+ break;
+ case _COMMAND_PLAY:
+ cout << "_COMMAND_PLAY";
+ break;
+ case _COMMAND_SEEK:
+ cout << "_COMMAND_SEEK";
+ cout << " intArg:"<<intArg;
+ break;
+ case _COMMAND_CLOSE:
+ cout << "_COMMAND_CLOSE";
+ break;
+ case _COMMAND_START:
+ cout << "_COMMAND_START";
+ break;
+ case _COMMAND_RESYNC_START:
+ cout << "_COMMAND_RESYNC_START";
+ break;
+ case _COMMAND_RESYNC_END:
+ cout << "_COMMAND_RESYNC_END";
+ break;
+
+ default:
+ cout << "unknown command id in Command::print"<<endl;
+ }
+ cout << endl;
+}
diff --git a/mpeglib/lib/decoder/command.h b/mpeglib/lib/decoder/command.h
new file mode 100644
index 00000000..fc96fd73
--- /dev/null
+++ b/mpeglib/lib/decoder/command.h
@@ -0,0 +1,52 @@
+/*
+ describes a command
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __COMMAND_H
+#define __COMMAND_H
+
+#include "../util/abstract/abs_thread.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#define _COMMAND_NONE 0
+#define _COMMAND_PLAY 1
+#define _COMMAND_PAUSE 2
+#define _COMMAND_SEEK 3
+#define _COMMAND_CLOSE 4
+#define _COMMAND_START 5
+#define _COMMAND_RESYNC_START 6
+#define _COMMAND_RESYNC_END 7
+#define _COMMAND_PING 8
+
+
+
+class Command {
+
+ int id;
+ int intArg;
+
+ public:
+ Command(int id);
+ Command(int id,int arg);
+ ~Command();
+
+ int getID();
+ int getIntArg();
+ void copyTo(Command* dest);
+ void print(const char* text);
+};
+
+#endif
diff --git a/mpeglib/lib/decoder/commandPipe.cpp b/mpeglib/lib/decoder/commandPipe.cpp
new file mode 100644
index 00000000..05991ca5
--- /dev/null
+++ b/mpeglib/lib/decoder/commandPipe.cpp
@@ -0,0 +1,163 @@
+/*
+ thread safe fifo queue for commands
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "commandPipe.h"
+
+#define _COMMAND_ARRAY_SIZE 100
+
+CommandPipe::CommandPipe() {
+ abs_thread_cond_init(&spaceCond);
+ abs_thread_cond_init(&emptyCond);
+ abs_thread_cond_init(&dataCond);
+ abs_thread_mutex_init(&pipeMut);
+
+ entries=0;
+ readPos=0;
+ writePos=0;
+
+
+ int i;
+
+ commandArray=new Command*[_COMMAND_ARRAY_SIZE];
+
+
+ for(i=0;i<_COMMAND_ARRAY_SIZE;i++) {
+ commandArray[i]=new Command(_COMMAND_NONE,0);
+ }
+}
+
+
+CommandPipe::~CommandPipe() {
+ abs_thread_cond_destroy(&spaceCond);
+ abs_thread_cond_destroy(&emptyCond);
+ abs_thread_cond_destroy(&dataCond);
+ abs_thread_mutex_destroy(&pipeMut);
+
+
+ int i;
+
+ for(i=0;i<_COMMAND_ARRAY_SIZE;i++) {
+ delete commandArray[i];
+ }
+ delete [] commandArray;
+}
+
+void CommandPipe::sendCommand(Command& cmd) {
+ sendCommand(cmd,true);
+}
+
+void CommandPipe::sendCommandNoWait(Command& cmd) {
+ sendCommand(cmd,false);
+}
+
+
+void CommandPipe::sendCommand(Command& cmd,int lWait) {
+ lockCommandPipe();
+ if (entries==_COMMAND_ARRAY_SIZE) {
+ waitForSpace();
+ }
+ cmd.copyTo(commandArray[writePos]);
+ writePos++;
+ if (writePos==_COMMAND_ARRAY_SIZE) {
+ writePos=0;
+ }
+ entries++;
+ // low water signal
+ if (entries==1) {
+ signalData();
+ }
+ unlockCommandPipe();
+ if (lWait) {
+ waitForEmptyQueue();
+ }
+}
+
+
+int CommandPipe::hasCommand(Command* dest) {
+ lockCommandPipe();
+ if (entries==0) {
+ unlockCommandPipe();
+ return false;
+ }
+
+ commandArray[readPos]->copyTo(dest);
+ readPos++;
+ if (readPos==_COMMAND_ARRAY_SIZE) {
+ readPos=0;
+ }
+ entries--;
+ // low water
+ if (entries==0) {
+ signalEmpty();
+ unlockCommandPipe();
+ return true;
+ }
+ // if we are on highwater, signal space
+ if (entries==_COMMAND_ARRAY_SIZE-1) {
+ signalSpace();
+ }
+ unlockCommandPipe();
+ return true;
+}
+
+
+void CommandPipe::waitForEmptyQueue() {
+ lockCommandPipe();
+ while (entries > 0) {
+ waitForEmpty();
+ }
+ unlockCommandPipe();
+}
+
+void CommandPipe::waitForCommand() {
+ lockCommandPipe();
+ while (entries == 0) {
+ waitForData();
+ }
+ unlockCommandPipe();
+}
+
+
+void CommandPipe::lockCommandPipe() {
+ abs_thread_mutex_lock(&pipeMut);
+}
+
+
+void CommandPipe::unlockCommandPipe() {
+ abs_thread_mutex_unlock(&pipeMut);
+}
+
+void CommandPipe::waitForSpace() {
+ abs_thread_cond_wait(&spaceCond,&pipeMut);
+}
+
+void CommandPipe::waitForEmpty() {
+ abs_thread_cond_wait(&emptyCond,&pipeMut);
+}
+
+void CommandPipe::waitForData() {
+ abs_thread_cond_wait(&dataCond,&pipeMut);
+}
+
+
+void CommandPipe::signalSpace() {
+ abs_thread_cond_signal(&spaceCond);
+}
+
+void CommandPipe::signalEmpty() {
+ abs_thread_cond_signal(&emptyCond);
+}
+
+void CommandPipe::signalData() {
+ abs_thread_cond_signal(&dataCond);
+}
diff --git a/mpeglib/lib/decoder/commandPipe.h b/mpeglib/lib/decoder/commandPipe.h
new file mode 100644
index 00000000..4ecf84eb
--- /dev/null
+++ b/mpeglib/lib/decoder/commandPipe.h
@@ -0,0 +1,78 @@
+/*
+ thread safe fifo queue for commands
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __COMMAND_PIPE_H
+#define __COMMAND_PIPE_H
+
+
+
+#include "command.h"
+
+/**
+ This queue deals with the ugly deadlock problem.
+ We insert all commands in a queue and the thread
+ polls this queue for next commands.
+*/
+
+class CommandPipe {
+
+ Command** commandArray;
+ int entries;
+ int readPos;
+ int writePos;
+
+ abs_thread_mutex_t pipeMut;
+ abs_thread_cond_t spaceCond;
+ abs_thread_cond_t emptyCond;
+ abs_thread_cond_t dataCond;
+
+ public:
+ CommandPipe();
+ ~CommandPipe();
+
+ // writer thread:
+ void sendCommand(Command& cmd);
+ void sendCommandNoWait(Command& cmd);
+
+ void waitForEmptyQueue();
+
+ // reader thread
+ int hasCommand(Command* dest);
+ void waitForCommand();
+
+
+
+
+ private:
+ void sendCommand(Command& cmd,int lWait);
+
+ void lockCommandPipe();
+ void unlockCommandPipe();
+
+
+ // writer thread wait here
+ void waitForSpace();
+ void waitForEmpty();
+ void waitForData();
+
+ // reader thread signals this
+ void signalSpace();
+ void signalEmpty();
+ void signalData();
+
+
+
+
+};
+
+#endif
diff --git a/mpeglib/lib/decoder/decoderPlugin.cpp b/mpeglib/lib/decoder/decoderPlugin.cpp
new file mode 100644
index 00000000..0a45bb42
--- /dev/null
+++ b/mpeglib/lib/decoder/decoderPlugin.cpp
@@ -0,0 +1,428 @@
+/*
+ base class for the player plugin
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include "decoderPlugin.h"
+
+#include <iostream>
+
+using namespace std;
+
+
+static void *playerThread(void *arg){
+ ((DecoderPlugin*)arg)->idleThread();
+ return NULL;
+}
+
+static int instanceCnt=0;
+
+DecoderPlugin::DecoderPlugin(){
+ input=NULL;
+ output=NULL;
+ commandPipe=new CommandPipe();
+ threadCommand=new Command(_COMMAND_NONE);
+ abs_thread_cond_init(&streamStateCond);
+ abs_thread_mutex_init(&streamStateMut);
+ abs_thread_mutex_init(&shutdownMut);
+
+ lCreatorLoop=true;
+ lDecoderLoop=false;
+ linDecoderLoop=false;
+ streamState=_STREAM_STATE_EOF;
+ lDecode=false;
+ lhasLength=false;
+ // this default is necessary. if you have a blocking
+ // device and we start automatic the thread
+ // may block on it and all commands (including play)
+ // blocks everything
+ // *you should not use autoplay*
+ lAutoPlay=false;
+
+ pluginInfo=new PluginInfo();
+ runCheck_Counter=0;
+ decode_loopCounter=0;
+ instance=instanceCnt;
+ instanceCnt++;
+ abs_thread_create(&tr,playerThread,this);
+
+ // we send a ping. because this signal is synchron
+ // we block until the thread is started and
+ // has read the ping singnal
+ Command cmd(_COMMAND_PING);
+ insertSyncCommand(&cmd);
+
+
+}
+
+
+DecoderPlugin::~DecoderPlugin(){
+ void* ret;
+ lCreatorLoop=false;
+ Command cmd(_COMMAND_CLOSE);
+ insertAsyncCommand(&cmd);
+
+ abs_thread_join(tr,&ret);
+
+ abs_thread_cond_destroy(&streamStateCond);
+ abs_thread_mutex_destroy(&streamStateMut);
+ abs_thread_mutex_destroy(&shutdownMut);
+
+ delete commandPipe;
+ delete threadCommand;
+ delete pluginInfo;
+}
+
+
+void DecoderPlugin::close(){
+ // from here we can only walk to init or eof
+ // in both cases we sometimes catch out decoderMut :-)
+ Command cmd(_COMMAND_CLOSE);
+ insertAsyncCommand(&cmd);
+ shutdownLock();
+ if (input != NULL) {
+ input->close();
+ }
+ shutdownUnlock();
+ insertSyncCommand(&cmd);
+ waitForStreamState(_STREAM_STATE_EOF);
+ input=NULL;
+}
+
+
+void DecoderPlugin::pause() {
+ Command cmd(_COMMAND_PAUSE);
+ insertSyncCommand(&cmd);
+}
+
+
+
+
+int DecoderPlugin::play() {
+ Command cmd(_COMMAND_PLAY);
+ insertSyncCommand(&cmd);
+
+ return true;
+}
+
+int DecoderPlugin::seek(int second) {
+ Command cmd(_COMMAND_SEEK,second);
+ insertSyncCommand(&cmd);
+
+ return true;
+}
+
+void DecoderPlugin::insertAsyncCommand(Command* cmd) {
+ commandPipe->sendCommandNoWait(*cmd);
+}
+
+void DecoderPlugin::insertSyncCommand(Command* cmd) {
+ commandPipe->sendCommand(*cmd);
+}
+
+
+void DecoderPlugin::shutdownLock() {
+ abs_thread_mutex_lock(&shutdownMut);
+}
+
+
+void DecoderPlugin::shutdownUnlock() {
+ abs_thread_mutex_unlock(&shutdownMut);
+}
+
+int DecoderPlugin::getTime(int lCurrent) {
+ int secLen=getTotalLength();
+
+ if (lCurrent==false) {
+ return secLen;
+ }
+ shutdownLock();
+ int byteLen=1;
+ int pos=1;
+ if (input != NULL) {
+ pos=input->getBytePosition()+1;
+ byteLen=input->getByteLength()+1;
+ }
+ int back=(int)(((double)pos/(double)byteLen) * (double)secLen);
+ shutdownUnlock();
+ return back;
+
+}
+
+int DecoderPlugin::getTotalLength() {
+ cout << "plugin does not support total playtime reporting"<<endl;
+ return 0;
+}
+
+int DecoderPlugin::seek_impl(int) {
+ cout << "plugin does not support seek"<<endl;
+ return false;
+}
+
+
+
+
+void DecoderPlugin::setOutputPlugin(OutputStream* output) {
+ this->output=output;
+}
+
+
+int DecoderPlugin::setInputPlugin(InputStream* input) {
+ this->input=input;
+
+ if (!input) {
+ cout << "input is NULL"<<endl;
+ exit(0);
+ }
+ pluginInfo->setUrl(input->getUrl());
+
+
+ // the command is synchron we block until the
+ // thread has read it
+ Command cmd(_COMMAND_START);
+ insertSyncCommand(&cmd);
+
+
+ // now that we know he has read it, we send another
+ // command, this is only read if the thread is in the
+ // decode_loop, and we then know that the streamState
+ // is FIRST_INIT
+ Command ping(_COMMAND_PING);
+ insertSyncCommand(&ping);
+
+
+ if (lAutoPlay) {
+ play();
+ }
+ return true;
+}
+
+
+void DecoderPlugin::config(const char* key,const char* value,void* ){
+ if (strcmp(key,"-y")==0) {
+ if (strcmp(value,"on")==0) {
+ lAutoPlay=true;
+ } else {
+ lAutoPlay=false;
+ }
+ }
+
+}
+
+/**
+ during shutdown the streamState is undefined until
+ the thread has left the decode_loop().
+ Make sure we wait for this.
+*/
+int DecoderPlugin::getStreamState() {
+ shutdownLock();
+ int back=streamState;
+ shutdownUnlock();
+ return back;
+}
+
+
+int DecoderPlugin::waitForStreamState(int state) {
+ int back;
+ abs_thread_mutex_lock(&streamStateMut);
+ while ((streamState & state) == false) {
+ abs_thread_cond_wait(&streamStateCond,&streamStateMut);
+ }
+ back=streamState;
+ abs_thread_mutex_unlock(&streamStateMut);
+ return back;
+}
+
+
+void DecoderPlugin::setStreamState(int streamState) {
+ abs_thread_mutex_lock(&streamStateMut);
+ this->streamState=streamState;
+ abs_thread_cond_signal(&streamStateCond);
+ abs_thread_mutex_unlock(&streamStateMut);
+}
+
+
+void DecoderPlugin::decoder_loop() {
+ cout << "direct call decoder loop->plugin not found ???"<<endl;
+ TimeWrapper::usleep(100000);
+}
+
+
+void* DecoderPlugin::idleThread() {
+
+ while(lCreatorLoop) {
+ linDecoderLoop=true;
+ commandPipe->waitForCommand();
+ commandPipe->hasCommand(threadCommand);
+ int id=threadCommand->getID();
+ switch(id) {
+ case _COMMAND_START:
+ lDecoderLoop=true;
+ break;
+ case _COMMAND_PING:
+ break;
+ /*
+ default:
+ threadCommand->print("ignoring non START command in idleThread");
+ */
+ }
+
+
+ if (lDecoderLoop) {
+ setStreamState(_STREAM_STATE_FIRST_INIT);
+ linDecoderLoop=false;
+ decode_loopCounter++;
+ runCheck_Counter=0;
+ shutdownLock();
+ decoder_loop();
+ lDecode=false;
+ lDecoderLoop=false;
+ lhasLength=false;
+ setStreamState(_STREAM_STATE_EOF);
+ shutdownUnlock();
+ }
+ }
+ return NULL;
+}
+
+
+PluginInfo* DecoderPlugin::getPluginInfo() {
+ return pluginInfo;
+}
+
+
+int DecoderPlugin::runCheck() {
+ if (runCheck_Counter==0) {
+ shutdownUnlock();
+ }
+ runCheck_Counter++;
+ while (lDecoderLoop && lCreatorLoop) {
+
+ // if we have an eof this always leads to
+ // a shutdown of the decode_loop thread
+ // it has more priority than the resyn request
+ if (input->eof()) {
+ setStreamState(_STREAM_STATE_WAIT_FOR_END);
+ }
+ //
+ // if we are in _STREAM_STATE_RESYNC_COMMIT
+ // we only leave it if command is _COMMAND_RESYNC_END
+ // (or close)
+
+ //
+ // check user commands
+ //
+ if (lDecode==false) {
+ commandPipe->waitForCommand();
+ commandPipe->hasCommand(threadCommand);
+ } else {
+ if (commandPipe->hasCommand(threadCommand)==false) {
+ // no commands and lDecode=true
+ return true;
+ }
+ }
+
+ // here we forward the command to a special
+ // method who can handle everything
+ // (the default method should work fine);
+ int nextCheck= processThreadCommand(threadCommand);
+ switch(nextCheck) {
+ case _RUN_CHECK_CONTINUE:
+ break;
+ case _RUN_CHECK_FALSE:
+ shutdownLock();
+ return false;
+ case _RUN_CHECK_TRUE:
+ return true;
+ default:
+ cout << "unknown runCheck return command"<<endl;
+ exit(0);
+ }
+
+
+ }
+
+ shutdownLock();
+ return false;
+}
+
+int DecoderPlugin::processThreadCommand(Command* command) {
+
+
+ int id=command->getID();
+ int intArg;
+
+ //
+ // if we are in _STREAM_STATE_RESYNC_COMMIT
+ // we only leave it if command is _COMMAND_RESYNC_END
+ //
+ if (streamState==_STREAM_STATE_RESYNC_COMMIT) {
+ switch(id) {
+ case _COMMAND_RESYNC_END:
+ setStreamState(_STREAM_STATE_INIT);
+ input->clear();
+ break;
+ case _COMMAND_CLOSE:
+ //
+ // we return false so that the plugin clears
+ // all its allocated classes
+ // its a _must_ !!
+ // in the next call we exit immediately
+ return _RUN_CHECK_FALSE;
+
+ /*
+ default:
+ command->print("ignore command in _STREAM_STATE_RESYNC_COMMIT");
+ */
+ }
+ return _RUN_CHECK_CONTINUE;
+ }
+
+
+ switch(id) {
+ case _COMMAND_NONE:
+ break;
+ case _COMMAND_PING:
+ break;
+ case _COMMAND_PAUSE:
+ lDecode=false;
+ break;
+ case _COMMAND_PLAY:
+ lDecode=true;
+ break;
+ case _COMMAND_SEEK: {
+ if (streamState==_STREAM_STATE_FIRST_INIT) {
+ command->print("ignore command seek in _STREAM_STATE_FIRST_INIT");
+ } else {
+ intArg=command->getIntArg();
+ seek_impl(intArg);
+ }
+ break;
+ }
+ case _COMMAND_CLOSE:
+ //
+ // we return false so that the plugin clears
+ // all its allocated classes
+ // its a _must_ !!
+ // in the next call we exit immediately
+ return _RUN_CHECK_FALSE;
+ case _COMMAND_RESYNC_START:
+ setStreamState(_STREAM_STATE_RESYNC_COMMIT);
+ input->clear();
+ break;
+ /*
+ default:
+ cout << "unknown command id in Command::print"<<endl;
+ */
+ }
+ return _RUN_CHECK_CONTINUE;
+}
+
+
diff --git a/mpeglib/lib/decoder/decoderPlugin.h b/mpeglib/lib/decoder/decoderPlugin.h
new file mode 100644
index 00000000..b616ed51
--- /dev/null
+++ b/mpeglib/lib/decoder/decoderPlugin.h
@@ -0,0 +1,201 @@
+/*
+ base class for the player plugin
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+
+#ifndef __DECODERPLUGIN_H
+#define __DECODERPLUGIN_H
+
+
+
+
+
+#include "commandPipe.h"
+#include "../input/inputPlugin.h"
+#include "../output/outPlugin.h"
+#include "../util/timeWrapper.h"
+
+#include <kdemacros.h>
+
+/**
+ Note: streamstate can be "or'ed" for the waitStreamState call
+*/
+
+#define _STREAM_STATE_EOF 1
+#define _STREAM_STATE_FIRST_INIT 4
+#define _STREAM_STATE_INIT 8
+#define _STREAM_STATE_PLAY 16
+#define _STREAM_STATE_WAIT_FOR_END 32
+#define _STREAM_STATE_RESYNC_COMMIT 64
+#define _STREAM_STATE_ALL 1+4+8+16+32+64
+
+
+#define _RUN_CHECK_FALSE 0
+#define _RUN_CHECK_TRUE 1
+#define _RUN_CHECK_CONTINUE 2
+
+
+
+/**
+ Here is the base class for the player plugin.
+ we can set the output of the player, and the input.
+ <p>
+ This class offer you a thread which you have to use
+ in derived class for the decoding work.
+ <p>
+ All calls to your decoder goes through this class
+ which must make sure that there are no races.
+ <p>
+ The life of the decoder thread:
+ ------------------------------
+
+ We start every time a thread if you make an instance of this class.
+ The thread then goes to the idleThread() method and waits
+ until we wake him up.
+ If we wake him um he graps the decoderMut and enters the
+ decode_loop() This method must be used for your plugin.
+ Then this thread "polls" during decoding the decoderChangeMut
+ (see runCheck())
+ if he can get the decoderChangeMut,the thread is authenticated for another
+ decode round if not, he waits until we wake him up again.
+ If the stream ends or the user stops decoding we leave the decoder_loop
+ and go back to the idleThread().
+ If you delete this class the thread is joined.
+
+*/
+
+class KDE_EXPORT DecoderPlugin {
+
+ public:
+
+ DecoderPlugin();
+ virtual ~DecoderPlugin();
+
+ //
+ // you can submit the following commands to the decode_loop thread
+ //
+ virtual void close();
+ virtual void pause();
+ virtual int play();
+ virtual int seek(int second);
+ void insertAsyncCommand(Command* cmd);
+ void insertSyncCommand(Command* cmd);
+
+ // returns total length (lCurrent==false) or current time (true)
+ // Note: you should not call this too often because
+ // it locks the decoder thread.
+ // Should be called only after the initialisation of the stream
+ // no need to override this, overide getTotalLength()
+ int getTime(int lCurrent);
+
+
+
+ virtual void setOutputPlugin(OutputStream* output);
+
+ // This method can only be called _once_ after a "close"
+ virtual int setInputPlugin(InputStream* input);
+ virtual void config(const char* key,const char* value,void* user_data);
+ virtual int getStreamState();
+
+ /**
+ Use this method to wait for a stream signal.
+ For example: after setInputPlugin the thread starts with
+ the state _EOF then the state changes between:
+
+ After setInput you can wait for (INIT | DECODE | WAIT_FOR_END | EOF) this
+ make sure the first init is passed. (successful or not)
+ return value is current streamState, which had a succesfull match.
+ Note: after return the state may already have changed (nature of threads)
+
+ */
+ int waitForStreamState(int state);
+ PluginInfo* getPluginInfo();
+
+ // Needed for Video Embedding
+ int x11WindowId() { return output->x11WindowId(); }
+
+ // never use this!
+ void* idleThread();
+
+ // Note: can only be called by the decode_loop thread !!!
+ void setStreamState(int streamState);
+
+ protected:
+
+ // override this if you have the total length in second
+ virtual int getTotalLength();
+ // implement this if your plugin supports seek
+ // (called by decoder_loop thread
+ virtual int seek_impl(int second);
+
+
+ /**
+ should be called from decoder_loop
+ checks some mutex variables if user want decoder to pause/quit
+ returns:
+ false : quit decoding
+ true : continue decoding
+
+ Note: method blocks if user "pauses" the stream
+ */
+ int runCheck();
+
+ // this mut is set from the start of decoder_loop()
+ // its _not_ set if the thread runs in the while() loop
+ // it it set if the thread leaves the while loop (runCheck->false)
+ // (its used internally to make the getTime() call safe
+ void shutdownLock();
+ void shutdownUnlock();
+
+
+ // this is the method to override in your decoder
+ virtual void decoder_loop();
+
+ // this method handles the commands for the decode_loop thread
+ virtual int processThreadCommand(Command* command);
+
+ OutputStream* output;
+ InputStream* input;
+
+ abs_thread_t tr;
+ abs_thread_mutex_t shutdownMut;
+ abs_thread_mutex_t streamStateMut;
+ abs_thread_cond_t streamStateCond;
+
+
+
+
+ int lDecoderLoop;
+ int lCreatorLoop;
+ int linDecoderLoop;
+ int lDecode;
+ int streamState;
+ int lhasLength;
+ int lAutoPlay;
+ int decode_loopCounter; // count how much we started decode_loop
+ int runCheck_Counter; // count how much we called runCheck
+ int instance;
+ PluginInfo* pluginInfo;
+
+ private:
+
+ CommandPipe* commandPipe;
+ Command* threadCommand;
+
+
+
+};
+
+#endif
+
diff --git a/mpeglib/lib/decoder/mpegPlugin.cpp b/mpeglib/lib/decoder/mpegPlugin.cpp
new file mode 100644
index 00000000..e230adb5
--- /dev/null
+++ b/mpeglib/lib/decoder/mpegPlugin.cpp
@@ -0,0 +1,160 @@
+/*
+ mpeg player plugin
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "mpegPlugin.h"
+
+#include "../mpegplay/mpegVideoStream.h"
+#include "../mpegplay/proto.h"
+#include "../mpegplay/mpegVideoHeader.h"
+
+using namespace std;
+
+
+MpegPlugin::MpegPlugin() {
+ init();
+}
+
+
+MpegPlugin::~MpegPlugin() {
+}
+
+
+void MpegPlugin::init() {
+ lCalcLength=false;
+ mpegVideoHeader=NULL;
+ mpegVideoStream=NULL;
+}
+
+
+void MpegPlugin::decoder_loop() {
+
+
+ VideoDecoder* video=NULL;
+ if (input == NULL) {
+ cout << "MpegPlugin::decoder_loop input is NULL"<<endl;
+ exit(0);
+ }
+ if (output == NULL) {
+ cout << "MpegPlugin::decoder_loop output is NULL"<<endl;
+ exit(0);
+ }
+
+ mpegVideoHeader=new MpegVideoHeader();
+ mpegVideoStream=new MpegVideoStream(input);
+
+ PictureArray* pictureArray;
+ YUVPicture* pic;
+ int skipMode=_SYNC_TO_NONE;
+ // decode loop
+
+ while(runCheck()) {
+ switch(streamState) {
+ case _STREAM_STATE_FIRST_INIT :
+ if (mpegVideoStream->firstInitialize(mpegVideoHeader)==false) {
+
+ } else {
+ pluginInfo->setLength(getSongLength());
+
+ // now create pictureArray from the sequence
+ int width=mpegVideoHeader->getMB_Width()*16;
+ int height=mpegVideoHeader->getMB_Height()*16;
+
+ output->openWindow(width,height,(char*)"kmpg");
+ video=new VideoDecoder(mpegVideoStream,mpegVideoHeader);
+ setStreamState(_STREAM_STATE_INIT);
+ }
+ break;
+ case _STREAM_STATE_INIT :
+ // cout << "mpeg _STREAM_STATE_INI"<<endl;
+ if (skipMode==_SYNC_TO_GOP) {
+ if (mpegVideoStream->nextGOP()==false) {
+ continue;
+ }
+ video->resyncToI_Frame();
+ }
+ if (skipMode==_SYNC_TO_PIC) {
+ if (mpegVideoStream->nextPIC()==false) {
+ continue;
+ }
+ }
+ skipMode=_SYNC_TO_NONE;
+ setStreamState(_STREAM_STATE_PLAY);
+ break;
+ case _STREAM_STATE_PLAY :
+ pictureArray=output->lockPictureArray();
+ skipMode=video->mpegVidRsrc(pictureArray);
+
+ if (skipMode != _SYNC_TO_NONE) {
+ setStreamState(_STREAM_STATE_INIT);
+ }
+ pic=pictureArray->getYUVPictureCallback();
+ if (pic == NULL) {
+ // nothin to display
+ break;
+ }
+
+ output->unlockPictureArray(pictureArray);
+ pictureArray->setYUVPictureCallback(NULL);
+ break;
+ case _STREAM_STATE_WAIT_FOR_END:
+ // exit while loop
+ lDecoderLoop=false;
+ break;
+ default:
+ cout << "unknown stream state:"<<streamState<<endl;
+ }
+ }
+
+ output->flushWindow();
+ // copy sequence back if needed
+ if (video != NULL) {
+ delete video;
+ }
+ delete mpegVideoStream;
+ delete mpegVideoHeader;
+ mpegVideoStream=NULL;
+ mpegVideoHeader=NULL;
+}
+
+
+
+
+
+// here we can config our decoder with special flags
+void MpegPlugin::config(const char* key,const char* value,void* user_data) {
+ if (strcmp(key,"-c")==0) {
+ lCalcLength=false;
+ }
+
+ if (strcmp(key,"decode")==0) {
+ if (strcmp(value,"true")==0) {
+ lDecode=true;
+ } else {
+ lDecode=false;
+ }
+ }
+ DecoderPlugin::config(key,value,user_data);
+}
+
+
+
+int MpegPlugin::getSongLength() {
+ int back=0;
+ return back;
+}
+
+
+
+
+
+
diff --git a/mpeglib/lib/decoder/mpegPlugin.h b/mpeglib/lib/decoder/mpegPlugin.h
new file mode 100644
index 00000000..ff236f9b
--- /dev/null
+++ b/mpeglib/lib/decoder/mpegPlugin.h
@@ -0,0 +1,47 @@
+/*
+ mpeg player plugin
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __MPEGPLUGIN_H
+#define __MPEGPLUGIN_H
+
+#include "../decoder/decoderPlugin.h"
+
+
+class MpegVideoHeader;
+class MpegVideoStream;
+
+
+class MpegPlugin : public DecoderPlugin {
+
+ int lCalcLength;
+
+ MpegVideoHeader* mpegVideoHeader;
+ MpegVideoStream* mpegVideoStream;
+
+ public:
+ MpegPlugin();
+ ~MpegPlugin();
+
+ void decoder_loop();
+ void config(const char* key,const char* value,void* user_data);
+
+
+ private:
+ void init();
+
+ int getSongLength();
+
+
+};
+#endif
+
diff --git a/mpeglib/lib/decoder/mpgPlugin.cpp b/mpeglib/lib/decoder/mpgPlugin.cpp
new file mode 100644
index 00000000..fd7170c8
--- /dev/null
+++ b/mpeglib/lib/decoder/mpgPlugin.cpp
@@ -0,0 +1,260 @@
+/*
+ mpg I video/audio player plugin
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "mpgPlugin.h"
+
+#include "../mpegplay/mpegSystemStream.h"
+#include "../mpgplayer/mpegStreamPlayer.h"
+#include "../mpegplay/mpegVideoLength.h"
+
+#include "splayPlugin.h"
+#include "mpegPlugin.h"
+
+#include <iostream>
+
+using namespace std;
+
+
+MpgPlugin::MpgPlugin() {
+
+ mpegSystemHeader=NULL;
+ mpegSystemStream=NULL;
+ mpegStreamPlayer=NULL;
+ mpegVideoLength=NULL;
+
+ // make runtime config easier, no NULL check necessary
+ mpegSystemHeader=new MpegSystemHeader();
+ timeStamp=new TimeStamp();
+ lMono=false;
+ lDown=false;
+ lWriteStreams=false;
+ lDoLength=true;
+}
+
+
+MpgPlugin::~MpgPlugin() {
+ delete mpegSystemHeader;
+ delete timeStamp;
+}
+
+
+
+void MpgPlugin::decoder_loop() {
+
+ if (input == NULL) {
+ cout << "MPGPlugin::decoder_loop input is NULL"<<endl;
+ exit(0);
+ }
+ if (output == NULL) {
+ cout << "MPGPlugin::decoder_loop output is NULL"<<endl;
+ exit(0);
+ }
+
+ // decoder
+ SplayPlugin* splayPlugin=new SplayPlugin();
+ splayPlugin->config("-c","true",NULL);
+ if (lMono) {
+ splayPlugin->config("-m","true",NULL);
+ }
+ if (lDown) {
+ splayPlugin->config("-2","true",NULL);
+ }
+
+ MpegPlugin* mpegplayPlugin=new MpegPlugin();
+
+ mpegSystemStream=new MpegSystemStream(input);
+ mpegStreamPlayer=new MpegStreamPlayer(input,output,
+ splayPlugin,mpegplayPlugin);
+ mpegStreamPlayer->setWriteToDisk(lWriteStreams);
+ mpegVideoLength=new MpegVideoLength(input);
+
+
+ int lSysLayer=false;
+ int lHasLength=false;
+
+ while(runCheck()) {
+ switch(streamState) {
+ case _STREAM_STATE_FIRST_INIT :
+ if ((lHasLength==false) && (lDoLength)) {
+ if (mpegVideoLength->firstInitialize()==false) {
+ continue;
+ }
+ lHasLength=true;
+ }
+ if (mpegSystemStream->firstInitialize(mpegSystemHeader)==false) {
+ //cout << "still initializing system stream"<<endl;
+ } else {
+ // if we have found a raw stream,
+ // make sure we pass as first argument a SEQ_START_CODE
+ if (mpegSystemHeader->getLayer() == _PACKET_SYSLAYER) {
+ lSysLayer=true;
+ mpegStreamPlayer->processSystemHeader(mpegSystemHeader);
+ } else {
+ //output->config("-s","false");
+ unsigned int startCodeRaw=htonl(_SEQ_START_CODE);
+ mpegStreamPlayer->
+ insertVideoDataRaw((unsigned char*)&startCodeRaw,4,timeStamp);
+ }
+ pluginInfo->setLength(mpegVideoLength->getLength());
+ output->writeInfo(pluginInfo);
+ setStreamState(_STREAM_STATE_INIT);
+
+ if (mpegSystemHeader->getMPEG2()==true) {
+ cout << "this plugin does not support MPEG2/VOB/DVD"<<endl;
+ lDecoderLoop=false;
+ if (lWriteStreams == true) {
+ cout << "demux is supported"<<endl;
+ lDecoderLoop=true;
+
+ }
+ break;
+ }
+
+ }
+ break;
+ case _STREAM_STATE_INIT :
+ setStreamState(_STREAM_STATE_PLAY);
+ break;
+ case _STREAM_STATE_PLAY :
+ // syslayer
+
+ if (mpegSystemStream->nextPacket(mpegSystemHeader) == false) {
+
+ break;
+ }
+
+ if (mpegStreamPlayer->processSystemHeader(mpegSystemHeader) == false) {
+ mpegSystemStream->reset();
+ setStreamState(_STREAM_STATE_INIT);
+ }
+
+ break;
+ case _STREAM_STATE_WAIT_FOR_END:
+ if (mpegStreamPlayer->hasEnd()==true) {
+ // exit while loop
+ lDecoderLoop=false;
+ }
+ TimeWrapper::usleep(100000);
+ break;
+ default:
+ cout << "unknown stream state:"<<streamState<<endl;
+ }
+ }
+
+ delete mpegStreamPlayer;
+ delete mpegSystemStream;
+ delete mpegVideoLength;
+ delete mpegplayPlugin;
+ delete splayPlugin;
+
+ mpegSystemStream=NULL;
+ mpegStreamPlayer=NULL;
+ mpegVideoLength=NULL;
+
+
+ output->audioFlush();
+ output->flushWindow();
+
+}
+
+
+
+// here we can config our decoder with special flags
+void MpgPlugin::config(const char* key,const char* value,void* user_data) {
+ if(strcmp("VideoLayer", key) == 0) {
+ int videoLayerSelect=atoi(value);
+ mpegSystemHeader->setVideoLayerSelect(videoLayerSelect);
+ }
+
+ if(strcmp("AudioLayer", key) == 0) {
+ int audioLayerSelect=atoi(value);
+ mpegSystemHeader->setAudioLayerSelect(audioLayerSelect);
+ }
+ if (strcmp(key,"-2")==0) {
+ lDown=true;
+ }
+ if (strcmp(key,"-m")==0) {
+ lMono=true;
+ }
+ if (strcmp(key,"-c")==0) {
+ lDoLength=false;
+ }
+ if (strcmp(key,"-w")==0) {
+ if (strcmp(value,"true")==0) {
+ lWriteStreams=true;
+ } else {
+ lWriteStreams=true;
+ }
+ }
+ // now try to set it if stream is running:
+ shutdownLock();
+ if (mpegStreamPlayer != NULL) {
+ mpegStreamPlayer->setWriteToDisk(lWriteStreams);
+ }
+ shutdownUnlock();
+
+
+ DecoderPlugin::config(key,value,user_data);
+}
+
+
+
+
+int MpgPlugin::getTotalLength() {
+ shutdownLock();
+ int back=0;
+ if (mpegVideoLength != NULL) {
+ back=mpegVideoLength->getLength();
+ } else {
+ cout << "cannot report total length, plugin not initialized"<<endl;
+ }
+ shutdownUnlock();
+
+ return back;
+}
+
+
+
+
+
+int MpgPlugin::processThreadCommand(Command* command) {
+
+ int id=command->getID();
+ int arg;
+ switch(id) {
+ case _COMMAND_SEEK: {
+ //
+ // mapping from second to bytePosition
+ //
+ if (mpegStreamPlayer->isInit()==false) {
+ command->print("MPGPLUGIN:ignore command in _STREAM_STATE_FIRST_INIT");
+ } else {
+ arg=command->getIntArg();
+ int pos=mpegVideoLength->getSeekPos(arg);
+ Command seek(_COMMAND_SEEK,pos);
+ // insert correct values
+ mpegStreamPlayer->processThreadCommand(&seek);
+ }
+ return _RUN_CHECK_CONTINUE;
+ }
+
+ default:
+ // we forward the command to the mpegStreamPlayer
+ mpegStreamPlayer->processThreadCommand(command);
+ }
+
+ // use default
+ return (DecoderPlugin::processThreadCommand(command));
+}
+
+
diff --git a/mpeglib/lib/decoder/mpgPlugin.h b/mpeglib/lib/decoder/mpgPlugin.h
new file mode 100644
index 00000000..79d986cc
--- /dev/null
+++ b/mpeglib/lib/decoder/mpgPlugin.h
@@ -0,0 +1,62 @@
+/*
+ mpg I video/audio player plugin
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __MPGPLUGIN_H
+#define __MPGPLUGIN_H
+
+#include "../decoder/decoderPlugin.h"
+#include <kdemacros.h>
+
+#define _INSERT_NO 0
+#define _INSERT_VIDEO 1
+#define _INSERT_AUDIO 2
+#define _INSERT_ALL 3
+
+class MpegSystemHeader;
+class MpegSystemStream;
+class MpegStreamPlayer;
+class MpegVideoLength;
+
+class KDE_EXPORT MpgPlugin : public DecoderPlugin {
+
+ MpegSystemHeader* mpegSystemHeader;
+ MpegSystemStream* mpegSystemStream;
+ MpegStreamPlayer* mpegStreamPlayer;
+ MpegVideoLength* mpegVideoLength;
+ TimeStamp* timeStamp;
+ int lMono;
+ int lDown;
+ int lWriteStreams;
+ int lDoLength;
+
+ public:
+ MpgPlugin();
+ ~MpgPlugin();
+
+ void decoder_loop();
+ int getTime(int lCurrent);
+
+ void config(const char* key,const char* value,void* user_data);
+
+ int processThreadCommand(Command* command);
+
+
+
+ protected:
+ int getTotalLength();
+
+
+};
+#endif
+
diff --git a/mpeglib/lib/decoder/nukePlugin.cpp b/mpeglib/lib/decoder/nukePlugin.cpp
new file mode 100644
index 00000000..08c8ce89
--- /dev/null
+++ b/mpeglib/lib/decoder/nukePlugin.cpp
@@ -0,0 +1,62 @@
+/*
+ this plugin nukes the input data.
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "nukePlugin.h"
+
+#include <iostream>
+
+using namespace std;
+
+NukePlugin::NukePlugin() {
+}
+
+
+NukePlugin::~NukePlugin() {
+}
+
+
+
+void NukePlugin::decoder_loop() {
+ if (input == NULL) {
+ cout << "NukePlugin::decoder_loop input is NULL"<<endl;
+ exit(0);
+ }
+ if (output == NULL) {
+ cout << "NukePlugin::decoder_loop output is NULL"<<endl;
+ exit(0);
+ }
+ char nukeBuffer[8192];
+
+ while(runCheck()) {
+
+ switch(streamState) {
+ case _STREAM_STATE_FIRST_INIT :
+ case _STREAM_STATE_INIT :
+ case _STREAM_STATE_PLAY :
+ input->read(nukeBuffer,8192);
+ break;
+ case _STREAM_STATE_WAIT_FOR_END:
+ // exit while loop
+ cout << "nukePlugin _STREAM_STATE_WAIT_FOR_END"<<endl;
+ lDecoderLoop=false;
+ break;
+ default:
+ cout << "unknown stream state:"<<streamState<<endl;
+ }
+ }
+}
+
+
+void NukePlugin::config(const char* key,const char* value,void* user_data) {
+ DecoderPlugin::config(key,value,user_data);
+}
diff --git a/mpeglib/lib/decoder/nukePlugin.h b/mpeglib/lib/decoder/nukePlugin.h
new file mode 100644
index 00000000..8a9aa9a3
--- /dev/null
+++ b/mpeglib/lib/decoder/nukePlugin.h
@@ -0,0 +1,34 @@
+/*
+ this plugin nukes the input data.
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __NUKEPLUGIN_H
+#define __NUKEPLUGIN_H
+
+
+#include "../decoder/decoderPlugin.h"
+#include <kdemacros.h>
+
+class KDE_EXPORT NukePlugin : public DecoderPlugin {
+
+ public:
+
+ NukePlugin();
+ ~NukePlugin();
+
+ void decoder_loop();
+ void config(const char* key,const char* value,void* user_data);
+
+ private:
+
+};
+#endif
diff --git a/mpeglib/lib/decoder/splayPlugin.cpp b/mpeglib/lib/decoder/splayPlugin.cpp
new file mode 100644
index 00000000..d0caa077
--- /dev/null
+++ b/mpeglib/lib/decoder/splayPlugin.cpp
@@ -0,0 +1,308 @@
+/*
+ splay player plugin
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "splayPlugin.h"
+#include "../splay/mpegsound.h"
+
+#include "../splay/splayDecoder.h"
+#include "../frame/floatFrame.h"
+#include "../frame/pcmFrame.h"
+#include "../splay/mpegAudioFrame.h"
+#include "../splay/mpegAudioInfo.h"
+#include "../input/fileAccessWrapper.h"
+
+#include <iostream>
+
+using namespace std;
+
+#define INPUTBUFFER_SIZE 8192
+
+SplayPlugin::SplayPlugin() {
+ pow(6.0,3.0); // fixes bug in __math.h
+ doFloat = false;
+ lnoLength=false;
+ inputbuffer=new unsigned char[INPUTBUFFER_SIZE];
+ pcmFrame=new PCMFrame(MP3FRAMESIZE);
+ floatFrame=new FloatFrame(MP3FRAMESIZE);
+ audioFrame=new AudioFrame();
+ framer=new MpegAudioFrame();
+ splay=new SplayDecoder();
+ lenghtInSec=0;
+
+ fileAccess=NULL;
+ info=NULL;
+ lOutput=true;
+}
+
+
+SplayPlugin::~SplayPlugin() {
+ delete [] inputbuffer;
+ delete pcmFrame;
+ delete floatFrame;
+ delete framer;
+ delete splay;
+ delete audioFrame;
+}
+
+
+// here we can config our decoder with special flags
+void SplayPlugin::config(const char* key,const char* value,void* user_data) {
+ if (strcmp(key,"dofloat")==0) {
+ doFloat=true;
+ }
+ if (strcmp(key,"-m")==0) {
+ splay->config("m","0",NULL);
+ }
+ if (strcmp(key,"-2")==0) {
+ splay->config("2","1",NULL);
+ }
+ if (strcmp(key,"-c")==0) {
+ lnoLength=true;
+ }
+ if (strcmp(key,"-d")==0) {
+ lOutput=false;
+ }
+
+ if (strcmp(key,"decode")==0) {
+ if (strcmp(value,"true")==0) {
+ lDecode=true;
+ } else {
+ lDecode=false;
+ }
+ }
+ DecoderPlugin::config(key,value,user_data);
+}
+
+
+
+void SplayPlugin::decoder_loop() {
+
+ if (input == NULL) {
+ cout << "SplayPlugin::decoder_loop input is NULL"<<endl;
+ exit(0);
+ }
+ if (output == NULL) {
+ cout << "SplayPlugin::decoder_loop output is NULL"<<endl;
+ exit(0);
+ }
+ // init decoder
+ output->audioInit();
+
+ // start decoding
+
+ fileAccess=new FileAccessWrapper(input);
+ info= new MpegAudioInfo(fileAccess);
+
+
+
+ framer->reset();
+ lenghtInSec=0;
+ resyncCounter=0;
+
+ AudioFrame* audioFrame=pcmFrame;
+ if (doFloat) {
+ audioFrame=floatFrame;
+ }
+
+ output->audioInit();
+ while(runCheck()) {
+
+ //
+ // check for re-init or for "eof"
+ //
+ switch(streamState) {
+ case _STREAM_STATE_INIT :
+ framer->reset();
+ resyncCounter=5;
+ setStreamState(_STREAM_STATE_PLAY);
+ continue;
+ case _STREAM_STATE_WAIT_FOR_END:
+ // exit while loop
+ lDecoderLoop=false;
+ continue;
+ }
+ if (doFrameFind() == true) {
+ if (splay->decode(framer->outdata(),framer->len(),audioFrame) == false){
+ continue;
+ }
+ // send data out:
+
+ int rest=framer->restBytes();
+ // we have inserted more data already than the
+ // framer has processed. But framer tells us
+ // how much he still needs to process.
+ long pos=input->getBytePosition();
+ TimeStamp* stamp=input->getTimeStamp(pos-rest);
+ processStreamState(stamp,audioFrame);
+
+
+ // make this stamp invalid for further synchronisation
+ stamp->setPTSFlag(false);
+ }
+ }
+
+ output->audioFlush();
+
+ delete fileAccess;
+ delete info;
+ fileAccess=NULL;
+ info=NULL;
+
+
+}
+
+
+int SplayPlugin::doFrameFind() {
+
+ //
+ // fine, we can work on the stream:
+ //
+ int back=false;
+ int framerState=framer->getState();
+ switch(framerState) {
+ case FRAME_NEED: {
+ int bytes=framer->canStore();
+ int read=input->read((char*)inputbuffer,bytes);
+
+ if (read <= 0) {
+ // read error. reset framer, drop frames
+ setStreamState(_STREAM_STATE_INIT);
+ break;
+ }
+ framer->store(inputbuffer,read);
+ break;
+ }
+ case FRAME_WORK:
+ back=framer->work();
+ break;
+ case FRAME_HAS: {
+ break;
+ }
+ default:
+ cout << "unknown state in mpeg audio framing"<<endl;
+ exit(0);
+ }
+ return back;
+}
+
+void SplayPlugin::audioSetup(AudioFrame* setupFrame) {
+ setupFrame->copyFormat(audioFrame);
+ output->audioSetup(audioFrame->getFrequenceHZ(),
+ audioFrame->getStereo(),
+ audioFrame->getSigned(),
+ audioFrame->getBigEndian(),
+ audioFrame->getSampleSize());
+}
+
+
+
+void SplayPlugin::processStreamState(TimeStamp* stamp,AudioFrame* playFrame){
+
+ // we always have a frame here, with the correct timestamp here.
+
+ switch(streamState) {
+ case _STREAM_STATE_FIRST_INIT :
+ output->audioOpen();
+
+ audioSetup(playFrame);
+
+ if (lnoLength==false) {
+ lenghtInSec=getTotalLength();
+ pluginInfo->setLength(lenghtInSec);
+ output->writeInfo(pluginInfo);
+ }
+
+ setStreamState(_STREAM_STATE_PLAY);
+ // yes, here is no break.
+ // we want to send the frame to the output.
+
+ case _STREAM_STATE_PLAY :
+ if (resyncCounter > 0) {
+ resyncCounter--;
+ break;
+ }
+ if (audioFrame->isFormatEqual(playFrame)==false) {
+ audioSetup(playFrame);
+ }
+ if (lOutput == false) {
+ break;
+ }
+ if(doFloat) {
+ FloatFrame* floatFrame=(FloatFrame*)playFrame;
+ output->audioPlay(stamp,stamp,
+ (char*) floatFrame->getData(),
+ playFrame->getLen()*sizeof(float) );
+ } else {
+ PCMFrame* pcmFrame=(PCMFrame*)playFrame;
+ output->audioPlay(stamp,stamp,
+ (char*)pcmFrame->getData(),
+ playFrame->getLen()*sizeof(short int));
+ }
+ break;
+ default:
+ cout << "unknown stream state:"<<streamState<<endl;
+ }
+}
+
+
+
+// splay can seek in streams
+int SplayPlugin::seek_impl(int second) {
+
+
+ if (info != NULL) {
+ int pos=info->getSeekPosition(second);
+ input->seek(pos);
+ setStreamState(_STREAM_STATE_INIT);
+ } else {
+ cout << "cannot seek, plugin not initialized"<<endl;
+ }
+ return true;
+}
+
+
+int SplayPlugin::getTotalLength() {
+ shutdownLock();
+ int back=0;
+
+ if (info->getNeedInit()) {
+ long pos=input->getBytePosition();
+ if (input->seek(0) == true) {
+ int bytes=1024;
+ // try reading all info, but not more
+ // than 1024 iterations.
+ info->reset();
+ while(bytes>0) {
+ if (info->initialize()==true) {
+ break;
+ }
+ bytes--;
+ }
+ input->seek(pos);
+ }
+ // wheter successful or not, never touch
+ // the info thing again.
+ info->setNeedInit(false);
+ }
+
+ back=info->getLength();
+
+
+ shutdownUnlock();
+ return back;
+}
+
+
+
+
+
diff --git a/mpeglib/lib/decoder/splayPlugin.h b/mpeglib/lib/decoder/splayPlugin.h
new file mode 100644
index 00000000..34a9751b
--- /dev/null
+++ b/mpeglib/lib/decoder/splayPlugin.h
@@ -0,0 +1,67 @@
+/*
+ splay player plugin
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __SPLAYPLUGIN_H
+#define __SPLAYPLUGIN_H
+
+#include "../decoder/decoderPlugin.h"
+#include <kdemacros.h>
+
+class SplayDecoder;
+class MpegAudioFrame;
+class AudioFrame;
+class FloatFrame;
+class PCMFrame;
+class FileAccessWrapper;
+class MpegAudioInfo;
+
+class KDE_EXPORT SplayPlugin : public DecoderPlugin {
+
+ int lnoLength;
+ int lfirst;
+ int lOutput;
+ /*
+ * directly writes decoded data as float, instead of converting it to
+ * short int samples first. float->int conversions are _very_
+ * time intensiv!
+ */
+ bool doFloat;
+
+ SplayDecoder* splay;
+ MpegAudioFrame* framer;
+ FloatFrame* floatFrame;
+ PCMFrame* pcmFrame;
+ unsigned char* inputbuffer;
+ int lenghtInSec;
+ MpegAudioInfo* info;
+ FileAccessWrapper* fileAccess;
+ int resyncCounter;
+ AudioFrame* audioFrame;
+
+
+ public:
+ SplayPlugin();
+ ~SplayPlugin();
+
+ void decoder_loop();
+ int seek_impl(int second);
+ void config(const char* key,const char* value,void* user_data);
+
+ protected:
+ int getTotalLength();
+ void processStreamState(TimeStamp* stamp,AudioFrame* audioFrame);
+ void audioSetup(AudioFrame* setupFrame);
+ int doFrameFind();
+
+};
+#endif
diff --git a/mpeglib/lib/decoder/tplayPlugin.cpp b/mpeglib/lib/decoder/tplayPlugin.cpp
new file mode 100644
index 00000000..01c44cbc
--- /dev/null
+++ b/mpeglib/lib/decoder/tplayPlugin.cpp
@@ -0,0 +1,264 @@
+/*
+ tplay player plugin
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "tplayPlugin.h"
+
+#include "../tplay/tplayfunctions.h"
+
+#include <iostream>
+
+using namespace std;
+
+TplayPlugin::TplayPlugin() {
+ info=new info_struct();
+
+ info->progname = NULL;
+ info->loop = FALSE;
+ info->in_seconds = FALSE;
+ info->speed = DEFAULT_SPEED;
+ info->bits = DEFAULT_BITS;
+ info->channels = DEFAULT_CHANNELS;
+ info->buffer_size = BUFFER_SIZE;
+ info->show_usage = FALSE;
+ info->swap = FALSE;
+ info->forceraw = FALSE;
+ info->force = FALSE;
+ info->device = NULL;
+ info->verbose = FALSE;
+ info->optind = 0;
+ info->buffer = NULL;
+ info->firstblock = NULL;
+ info->number_of_blocks = 0;
+ info->readblock = 0;
+ info->writeblock = 0;
+ info->readcount = 0;
+ info->writecount = 0;
+ info->alldone = FALSE;
+ info->overflow = FALSE;
+ info->underflow = FALSE;
+ info->audioset = FALSE;
+ info->headerskip = 0;
+ info->blocksize = 4096;
+ info->bytes_on_last_block = 0;
+
+ startStamp=new TimeStamp();
+ endStamp=new TimeStamp();
+}
+
+
+TplayPlugin::~TplayPlugin() {
+ delete startStamp;
+ delete endStamp;
+ delete info;
+}
+
+
+
+void TplayPlugin::decoder_loop() {
+ int bytesread, count, stereo;
+ char *p, *bufferp;
+
+ if (input == NULL) {
+ cout << "TplayPlugin::decoder_loop input is NULL"<<endl;
+ exit(0);
+ }
+ if (output == NULL) {
+ cout << "TplayPlugin::decoder_loop output is NULL"<<endl;
+ exit(0);
+ }
+
+ TimeStamp* stamp;
+ long pos;
+
+ info->buffer = (char*) malloc(info->buffer_size * sizeof(char));
+
+ while(runCheck()) {
+ switch(streamState) {
+ case _STREAM_STATE_FIRST_INIT : {
+ read_header();
+
+ if (info->channels == 1)
+ stereo = FALSE;
+ else
+ stereo = TRUE;
+
+ info->number_of_blocks = 0;
+ bufferp = info->buffer;
+ pluginInfo->setLength(getTotalLength());
+ output->writeInfo(pluginInfo);
+ lhasLength=true;
+
+ setStreamState(_STREAM_STATE_INIT);
+ break;
+ }
+ case _STREAM_STATE_INIT :
+ setStreamState(_STREAM_STATE_PLAY);
+ cout << "audioSetup call"<<endl;
+ output->audioOpen();
+ output->audioSetup(info->speed, stereo, 1, 0, info->bits);
+ break;
+ case _STREAM_STATE_PLAY :
+ bytesread = 0;
+ count = 0;
+ p = bufferp;
+ while ((bytesread < info->blocksize) && (count != -1) &&
+ ((count = input->read(p,info->blocksize-bytesread))!=0)){
+ p += count;
+ bytesread += count;
+ }
+ if (info->swap) {
+ swap_block(bufferp, bytesread);
+ }
+
+ if (bytesread > 0) {
+ pos=input->getBytePosition();
+ stamp=input->getTimeStamp(pos-bytesread);
+ output->audioPlay(stamp,endStamp,(char *) bufferp, bytesread);
+ }
+
+ if (bytesread < info->blocksize) {
+ info->alldone = TRUE;
+
+ }
+ break;
+ case _STREAM_STATE_WAIT_FOR_END:
+ // exit while loop
+ lDecoderLoop=false;
+ break;
+ default:
+ cout << "unknown stream state:"<<streamState<<endl;
+ }
+ }
+ cout << "tplay exit"<<endl;
+ free(info->buffer);
+ info->buffer = NULL;
+ output->audioFlush();
+}
+
+
+int TplayPlugin::seek_impl(int second) {
+ int bytes_per_second;
+ int seconds;
+ int back;
+
+
+ bytes_per_second = info->speed * info->channels * (info->bits / 8);
+
+
+
+ seconds = bytes_per_second * second;
+ back=input->seek(seconds);
+
+ return back;
+}
+
+
+void TplayPlugin::config(const char* key,const char* value,void* user_data) {
+ DecoderPlugin::config(key,value,user_data);
+}
+
+
+void TplayPlugin::swap_block(char * buffer, int blocksize) {
+ register int i;
+ char c, *p;
+
+ p = buffer;
+ for (i = 0; i < (blocksize / 2); i++) {
+ c = *p;
+ *p = *(p + 1);
+ *++p = c;
+ p++;
+ }
+}
+
+
+void TplayPlugin::read_header() {
+ int bytesread, count;
+ char *p, *bufferp;
+
+ info->firstblock = (char*) malloc(info->blocksize * sizeof(char));
+ bufferp = info->firstblock;
+ if (!info->forceraw) {
+ bytesread = 0;
+ count = 0;
+ p = bufferp;
+ while ((bytesread < info->blocksize) && (count != -1) &&
+ ((count=input->read(p,info->blocksize-bytesread)) != 0)){
+ p += count;
+ bytesread += count;
+ }
+
+ if (bytesread < SUN_HDRSIZE)
+ cout << "Sample size is too small"<<endl;
+
+ if (read_au(info,info->firstblock) && read_wav(info,info->firstblock)) {
+ if (info->verbose)
+ printf("Playing raw data: %ld samples/s, %d bits, %d channels.\n",
+ info->speed, info->bits, info->channels);
+ }
+
+ if (info->swap)
+ swap_block(bufferp, bytesread);
+
+ if (bytesread < info->blocksize) {
+ info->alldone = TRUE;
+ info->bytes_on_last_block = bytesread;
+ return;
+ }
+
+ if (info->headerskip) {
+ count = 0;
+ bytesread = info->blocksize - info->headerskip;
+ p = info->firstblock + (info->blocksize - info->headerskip);
+ while ((bytesread < info->blocksize) && (count != -1) &&
+ ((count = input->read(p,info->blocksize - bytesread)) != 0)) {
+ p += count;
+ bytesread += count;
+ }
+ }
+
+ info->writeblock++;
+ info->writecount++;
+ }
+ else {
+ if (info->verbose)
+ printf("Playing raw data: %ld samples/s, %d bits, %d channels\n",
+ info->speed, info->bits, info->channels);
+ }
+}
+
+/**
+ Nobody is perfect I'm too tired to fix this race
+*/
+
+int TplayPlugin::getTotalLength() {
+ float wavfilesize = input->getByteLength();
+ int back=0;
+
+
+ float frequence=info->speed;
+
+ int bits=info->bits;
+
+
+ if (bits == 16) {
+ wavfilesize=wavfilesize/2;
+ }
+ if (info->channels==2) {
+ wavfilesize=wavfilesize/2;
+ }
+ if (frequence != 0) {
+ back=(int)(wavfilesize/frequence);
+ }
+ return back;
+}
diff --git a/mpeglib/lib/decoder/tplayPlugin.h b/mpeglib/lib/decoder/tplayPlugin.h
new file mode 100644
index 00000000..daa0de74
--- /dev/null
+++ b/mpeglib/lib/decoder/tplayPlugin.h
@@ -0,0 +1,47 @@
+/*
+ tplay player plugin
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __TPLAYPLUGIN_H
+#define __TPLAYPLUGIN_H
+
+#include "../decoder/decoderPlugin.h"
+#include <kdemacros.h>
+/**
+ The tplayPlugin is ugly and needs a rewrite.
+ Im not sure if you can make mutiple instances of it
+*/
+
+class KDE_EXPORT TplayPlugin : public DecoderPlugin {
+
+ struct info_struct* info;
+ class TimeStamp* startStamp;
+ class TimeStamp* endStamp;
+
+ public:
+ TplayPlugin();
+ ~TplayPlugin();
+
+ void decoder_loop();
+ int seek_impl(int second);
+ void config(const char* key,const char* value,void* user_data);
+
+ protected:
+ int getTotalLength();
+
+ private:
+ void swap_block(char* buffer, int blocksize);
+ void read_header();
+
+
+};
+#endif
diff --git a/mpeglib/lib/decoder/vorbisPlugin.cpp b/mpeglib/lib/decoder/vorbisPlugin.cpp
new file mode 100644
index 00000000..b32ba93f
--- /dev/null
+++ b/mpeglib/lib/decoder/vorbisPlugin.cpp
@@ -0,0 +1,305 @@
+/*
+ vorbis player plugin
+ Copyright (C) 2000 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "vorbisPlugin.h"
+
+#include <iostream>
+
+using namespace std;
+
+#ifdef OGG_VORBIS
+
+size_t fread_func(void *ptr, size_t size, size_t nmemb, void *stream) {
+ InputStream* input=((VorbisPlugin*)stream)->getInputStream();
+ int bytes=input->read((char*)ptr,size*nmemb);
+ // workaround for RC3, report success during seek
+ /*
+ if (((VorbisPlugin*)stream)->vorbis_seek_bug_active == true) {
+ errno=0;
+ return 0;
+ }
+ */
+ // error on read and no "seek workaround"
+ if (bytes == 0) {
+ //
+ // If different thread close the input we signal
+ // a read error to vorbis
+ //
+ if (input->isOpen() == false) {
+ // note: errno is in this thread another variable, than in
+ // the thread which closed the file.
+ // here we "fake" the errno var.
+ errno=EBADF;
+ return 0;
+ }
+ }
+ // successful read
+ return bytes;
+}
+
+
+int fseek_func(void *stream, ogg_int64_t offset, int whence) {
+ int ret=-1;
+ InputStream* input=((VorbisPlugin*)stream)->getInputStream();
+
+ switch(whence) {
+ case SEEK_SET:
+ ret=input->seek(offset);
+ break;
+ case SEEK_CUR:
+ ret=input->seek(input->getBytePosition()+offset);
+ break;
+ case SEEK_END:
+ ret=input->seek(input->getByteLength());
+ break;
+ default:
+ cout << "fseek_func VorbisPlugn strange call"<<endl;
+ }
+
+ if (ret == false) {
+ // vorbis does not handle errors in seek at all
+ // and if they are handled vorbis segfaults. (RC3)
+ // here we always "success" but set an seek_error_mark
+ //
+ ret=-1;
+ }
+
+ return ret;
+
+}
+
+
+int fclose_func (void *) {
+ //InputStream* input=(InputStream*) stream;
+
+ // its handled different in kmpg
+ // we close the stream if the decoder signals eof.
+ return true;
+
+}
+
+
+long ftell_func (void *stream) {
+ InputStream* input=((VorbisPlugin*)stream)->getInputStream();
+ return input->getBytePosition();
+}
+
+
+VorbisPlugin::VorbisPlugin() {
+
+
+ memset(&vf, 0, sizeof(vf));
+ timeDummy=new TimeStamp();
+ pcmout=new char[4096];
+ lnoLength=false;
+ lshutdown=true;
+
+}
+
+
+VorbisPlugin::~VorbisPlugin() {
+ delete timeDummy;
+ delete pcmout;
+}
+
+
+// here we can config our decoder with special flags
+void VorbisPlugin::config(const char* key,const char* value,void* user_data) {
+
+ if (strcmp(key,"-c")==0) {
+ lnoLength=true;
+ }
+ DecoderPlugin::config(key,value,user_data);
+}
+
+
+int VorbisPlugin::init() {
+ ov_callbacks callbacks;
+
+ callbacks.read_func = fread_func;
+ callbacks.seek_func = fseek_func;
+ callbacks.close_func = fclose_func;
+ callbacks.tell_func = ftell_func;
+
+ // here is the hack to pass the pointer to
+ // our streaming interface.
+
+ if(ov_open_callbacks(this, &vf, NULL, 0, callbacks) < 0) {
+ return false;
+ }
+
+ return true;
+}
+
+
+// called by decoder thread
+int VorbisPlugin::processVorbis(vorbis_info* vi,vorbis_comment* comment) {
+
+ // decode
+ int ret;
+ int current_section=-1; /* A vorbis physical bitstream may
+ consist of many logical sections
+ (information for each of which may be
+ fetched from the vf structure). This
+ value is filled in by ov_read to alert
+ us what section we're currently
+ decoding in case we need to change
+ playback settings at a section
+ boundary */
+ ret=ov_read(&vf,pcmout,4096,0,2,1,&current_section);
+ switch(ret){
+ case 0:
+ /* EOF */
+ lDecoderLoop=false;
+ break;
+ case -1:
+ /* error in the stream. Not a problem, just reporting it in
+ case we (the app) cares. In this case, we don't. */
+ cout << "error found"<<endl;
+ break;
+ default:
+ if(current_section!=last_section){
+ vi=ov_info(&vf,-1); /* The info struct is different in each
+ section. vf holds them all for the
+ given bitstream. This requests the
+ current one */
+
+ double timeoffset=ov_time_tell(&vf);
+
+ comment = ov_comment(&vf, -1);
+ if(comment) {
+ cout << "we have a comment:"<<timeoffset<<endl;
+ }
+ }
+ last_section=current_section;
+ output->audioPlay(timeDummy,timeDummy,pcmout,ret);
+ break;
+ }
+ return true;
+}
+
+
+void VorbisPlugin::decoder_loop() {
+ vorbis_info *vi=NULL;
+ vorbis_comment *comment=NULL;
+ last_section=0;
+ current_section=0;
+
+
+
+ if (input == NULL) {
+ cout << "VorbisPlugin::decoder_loop input is NULL"<<endl;
+ exit(0);
+ }
+ if (output == NULL) {
+ cout << "VorbisPlugin::decoder_loop output is NULL"<<endl;
+ exit(0);
+ }
+ // init audio stream
+ output->audioInit();
+
+ /********** Decode setup ************/
+ // start decoding
+ lshutdown=false;
+ /**
+ Vorbis RC3 introducted a new bug:
+ seek on a closed stream segfaults vorbis.
+ The bugfix in vorbis is (RC3) around line 1190 should be:
+ seek_error:
+ // the caller of this goto may not set a return code
+ ret=OV_ENOSEEK;
+ The workaround is to check if we are in a seek operation
+ and always "fake" successful reads.
+ */
+ vorbis_seek_bug_active=false;
+
+
+ while(runCheck()) {
+
+ switch(streamState) {
+ case _STREAM_STATE_FIRST_INIT :
+ if (init()== false) {
+ // total failure. exit decoding
+ lDecoderLoop=false;
+ break;
+ }
+ // now init stream
+ vi=ov_info(&vf,-1);
+ if (lnoLength==false) {
+ pluginInfo->setLength(getTotalLength());
+ output->writeInfo(pluginInfo);
+ }
+ output->audioOpen();
+ output->audioSetup(vi->rate,vi->channels-1,1,0,16);
+
+
+ lhasLength=true;
+ setStreamState(_STREAM_STATE_PLAY);
+ break;
+ case _STREAM_STATE_INIT :
+ case _STREAM_STATE_PLAY :
+ processVorbis(vi,comment);
+ break;
+ case _STREAM_STATE_WAIT_FOR_END:
+ // exit while loop
+ lDecoderLoop=false;
+ usleep(2000000);
+ break;
+ default:
+ cout << "unknown stream state vorbis decoder:"<<streamState<<endl;
+ }
+
+ }
+
+ lshutdown=true;
+ ov_clear(&vf); /* ov_clear closes the stream if its open. Safe to
+ call on an uninitialized structure as long as
+ we've zeroed it */
+ memset(&vf, 0, sizeof(vf));
+
+ output->audioFlush();
+}
+
+// vorbis can seek in streams
+int VorbisPlugin::seek_impl(int second) {
+ vorbis_seek_bug_active=true;
+ ov_time_seek(&vf,(double) second);
+ vorbis_seek_bug_active=false;
+ return true;
+}
+
+
+
+int VorbisPlugin::getTotalLength() {
+ int back=0;
+ int byteLen=input->getByteLength();
+ if (byteLen == 0) {
+ return 0;
+ }
+ /* Retrieve the length in second*/
+ shutdownLock();
+ if (lshutdown==false) {
+ back = (int) ov_time_total(&vf, -1);
+ }
+ shutdownUnlock();
+
+ return back;
+}
+
+
+
+
+#endif
+//OGG_VORBIS
+
+
diff --git a/mpeglib/lib/decoder/vorbisPlugin.h b/mpeglib/lib/decoder/vorbisPlugin.h
new file mode 100644
index 00000000..b680ed69
--- /dev/null
+++ b/mpeglib/lib/decoder/vorbisPlugin.h
@@ -0,0 +1,87 @@
+/*
+ vorbis player plugin
+ Copyright (C) 2000 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __VORBISPLUGIN_H
+#define __VORBISPLUGIN_H
+
+#include "nukePlugin.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+
+#ifndef OGG_VORBIS
+class VorbisPlugin : public NukePlugin {
+};
+#else
+
+
+#include <vorbis/codec.h>
+#include <vorbis/vorbisfile.h>
+
+/**
+ callbacks from vorbisfile
+*/
+extern "C" {
+
+extern size_t fread_func (void *ptr,size_t size,size_t nmemb, void *stream);
+extern int fseek_func (void *stream, ogg_int64_t offset, int whence);
+extern int fclose_func (void *stream);
+extern long ftell_func (void *stream);
+
+}
+
+
+class VorbisPlugin : public DecoderPlugin {
+
+ OggVorbis_File vf;
+
+
+ // END vorbis setup
+
+
+ int lnoLength;
+ int lAutoPlay;
+ TimeStamp* timeDummy;
+ char* pcmout; // temporay pcm buffer
+ int last_section;
+ int current_section;
+ int lshutdown;
+
+ public:
+ VorbisPlugin();
+ ~VorbisPlugin();
+
+ void decoder_loop();
+ int seek_impl(int second);
+ void config(const char* key,const char* value,void* user_data);
+
+ // vorbis bug workaround [START]
+ int vorbis_seek_bug_active;
+ ::InputStream* getInputStream() { return input; }
+ // vorbis bug workaround [END]
+
+ private:
+ int processVorbis(vorbis_info* vi,vorbis_comment* comment);
+ int getTotalLength();
+ int init();
+
+};
+
+#endif
+//OGG_VORBIS
+
+#endif
diff --git a/mpeglib/lib/dummy.cpp b/mpeglib/lib/dummy.cpp
new file mode 100644
index 00000000..94bd1cb7
--- /dev/null
+++ b/mpeglib/lib/dummy.cpp
@@ -0,0 +1,6 @@
+
+
+
+static int dummy() {
+ return 0;
+}
diff --git a/mpeglib/lib/frame/IOFrameQueue.cpp b/mpeglib/lib/frame/IOFrameQueue.cpp
new file mode 100644
index 00000000..9a13f1fa
--- /dev/null
+++ b/mpeglib/lib/frame/IOFrameQueue.cpp
@@ -0,0 +1,60 @@
+/*
+ queues frames in a "empty" and "data" queue
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "IOFrameQueue.h"
+
+
+IOFrameQueue::IOFrameQueue(int size) {
+ empty=new FrameQueue(size);
+ data=new FrameQueue(size);
+
+}
+
+
+IOFrameQueue::~IOFrameQueue() {
+ delete empty;
+ delete data;
+}
+
+
+//
+// empty queue
+//
+int IOFrameQueue::emptyQueueCanRead() {
+ return empty->canRead();
+}
+
+
+int IOFrameQueue::emptyQueueCanWrite() {
+ return empty->canWrite();
+}
+
+
+
+
+//
+// data queue
+//
+int IOFrameQueue::dataQueueCanRead() {
+ return data->canRead();
+}
+
+
+int IOFrameQueue::dataQueueCanWrite() {
+ return data->canWrite();
+}
+
+
+
+
+
diff --git a/mpeglib/lib/frame/IOFrameQueue.h b/mpeglib/lib/frame/IOFrameQueue.h
new file mode 100644
index 00000000..d56f25aa
--- /dev/null
+++ b/mpeglib/lib/frame/IOFrameQueue.h
@@ -0,0 +1,60 @@
+/*
+ queues frames in a "empty" and "data" queue
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __IOFRAMEQUEUE_H
+#define __IOFRAMEQUEUE_H
+
+#include "frameQueue.h"
+#include <kdemacros.h>
+
+/**
+ This class can store up to <size> frames.
+ The frames are marked "empty" these frames can be re-used
+ and filled with data.
+ The other queue stores "data" frames. These frames can
+ be dequeued and then are be passed to the "empty" queue.
+
+ Note: you need to _fill_ the empty queue with allocated frames,
+ after constructions. The pointers then are owned by this
+ class (== deleted in destructor)
+
+*/
+
+class KDE_EXPORT IOFrameQueue {
+
+
+ public:
+ IOFrameQueue(int size);
+ ~IOFrameQueue();
+
+ //
+ // empty queue
+ //
+ int emptyQueueCanRead();
+ int emptyQueueCanWrite();
+
+
+ //
+ // data queue
+ //
+ int dataQueueCanRead();
+ int dataQueueCanWrite();
+
+
+ protected:
+ FrameQueue* empty;
+ FrameQueue* data;
+
+};
+#endif
diff --git a/mpeglib/lib/frame/Makefile.am b/mpeglib/lib/frame/Makefile.am
new file mode 100644
index 00000000..753b5f18
--- /dev/null
+++ b/mpeglib/lib/frame/Makefile.am
@@ -0,0 +1,33 @@
+
+# ---- @OS_TYPE@/@ARCH_TYPE@ ----
+
+INCLUDES = $(all_includes)
+
+EXTRA_DIST = README
+
+kmpgincludedir = $(includedir)/$(THIS_LIB_NAME)/frame
+
+kmpginclude_HEADERS = pcmFrame.h audioFrame.h \
+ floatFrame.h frameQueue.h frame.h \
+ IOFrameQueue.h audioFrameQueue.h \
+ framer.h rawDataBuffer.h rawFrame.h
+
+noinst_LTLIBRARIES = libframe.la
+
+libframe_la_SOURCES = pcmFrame.cpp \
+ audioFrame.cpp floatFrame.cpp frame.cpp \
+ frameQueue.cpp IOFrameQueue.cpp\
+ audioFrameQueue.cpp framer.cpp \
+ rawDataBuffer.cpp rawFrame.cpp
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/lib/frame/README b/mpeglib/lib/frame/README
new file mode 100644
index 00000000..9e21059f
--- /dev/null
+++ b/mpeglib/lib/frame/README
@@ -0,0 +1,68 @@
+
+
+
+Frames
+======
+
+
+Frames are needed for passing data between decoders around.
+A decoder gets an mpeg audio encoded frame as input and
+writes data to an audioFrame (pcm/float)
+
+ frame
+ |
+ audioFrame
+ / \
+ pcmFrame floatFrame
+
+
+FrameQueues
+===========
+
+FrameQueues are needed for storing some frames. (For example
+you store 100 pre-decoded pcmFrames.)
+A FrameQueue is a queue, simply not more.
+
+IOFrameQueues
+=============
+
+IOFrameQueues deal with the problem, that you first start
+with an empty FrameQueue (this is a FrameQueue which contains
+prealloceated data, but the data is empty (eg:all pcm samples zero)
+Only after converting an "empty" Frame by a decoder (mp3decoder)
+the frame if "full" (== Frame with data)
+So: IOFrameQueues mark frames with
+
+i) empty
+ii) data
+
+You can get an empty Frame from the empty-frame-queue and
+then enqueue it in the data-frame-queue.
+After the frame was used (played) we dequeue it from the
+data queue and put them back in the empty queue.
+You can see IOFrameQueues as a ringbuffer.
+(get free space. write to it. read data. mark space as free,....)
+
+
+ FrameQueue
+ |
+ IOFrameQueue
+ /
+ audioFrameQueue
+
+
+AudioFrameQueue
+===============
+
+A IOFrameQueue, which allows converting "dataFrames back to continous
+stream".
+What is this?
+Lets say an application wants only 20 byte from a dataFrame which
+contains 3KB of data?
+There must be some mechanism to read less or more data from the
+queue. And the data should be written to a continus memory
+segment.
+AudioFrameQueue deals with this problem.You can read data from
+the queue to a given pointer location.
+
+
diff --git a/mpeglib/lib/frame/audioFrame.cpp b/mpeglib/lib/frame/audioFrame.cpp
new file mode 100644
index 00000000..55464afa
--- /dev/null
+++ b/mpeglib/lib/frame/audioFrame.cpp
@@ -0,0 +1,111 @@
+/*
+ abstract definition of an audio frame
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "audioFrame.h"
+
+#include <iostream>
+
+using namespace std;
+
+AudioFrame::AudioFrame() {
+
+ stereo=-1;
+ frequencyHZ=-1;
+
+ sampleSize=-1;
+ lBigEndian=-1;
+ lSigned=-1;
+ setFrameType(_FRAME_AUDIO_BASE);
+}
+
+
+AudioFrame::~AudioFrame() {
+}
+
+
+int AudioFrame::getLen() {
+ cout << "direct virtual call AudioFrame::getLen"<<endl;
+ return 0;
+}
+
+
+void AudioFrame::setLen(int ) {
+ cout << "direct virtual call AudioFrame::setLen"<<endl;
+}
+
+
+int AudioFrame::getSize() {
+ cout << "direct virtual call AudioFrame::getSize"<<endl;
+ return 0;
+}
+
+
+void AudioFrame::putFloatData(float* ,int ) {
+ cout << "direct virtual call AudioFrame::putFloatData"<<endl;
+}
+
+void AudioFrame::putFloatData(float* ,float* ,int ) {
+ cout << "direct virtual call AudioFrame::putFloatData L/R version"<<endl;
+}
+
+void AudioFrame::clearrawdata() {
+ cout << "direct virtual call AudioFrame::clearrawdata"<<endl;
+}
+
+void AudioFrame::setFrameFormat(int stereo,int freq) {
+ this->stereo=stereo;
+ this->frequencyHZ=freq;
+}
+
+
+
+int AudioFrame::isFormatEqual(AudioFrame* compare) {
+ if(compare->getStereo() != stereo) {
+ return false;
+ }
+ if(compare->getSampleSize() != sampleSize) {
+ return false;
+ }
+ if(compare->getBigEndian() != lBigEndian) {
+ return false;
+ }
+ if(compare->getFrequenceHZ() != frequencyHZ) {
+ return false;
+ }
+ if(compare->getSigned() != lSigned) {
+ return false;
+ }
+ return true;
+}
+
+void AudioFrame::print(const char* msg) {
+ cout << "PCMFrame::print:"<<msg<<endl;
+ cout << "stereo:"<<stereo<<endl;
+ cout << "sampleSize:"<<sampleSize<<endl;
+ cout << "lBigEndian:"<<lBigEndian<<endl;
+ cout << "frequencyHZ:"<<frequencyHZ<<endl;
+ cout << "lSigned:"<<lSigned<<endl;
+}
+
+
+void AudioFrame::copyFormat(AudioFrame* dest) {
+ if (dest->getFrameType() != _FRAME_AUDIO_BASE) {
+ cout << "cannot copy frameFormat into frametype!= _FRAME_AUDIO_BASE"<<endl;
+ exit(0);
+ }
+ dest->setFrameFormat(getStereo(),getFrequenceHZ());
+ dest->sampleSize=getSampleSize();
+ dest->lBigEndian=getBigEndian();
+ dest->lSigned=getSigned();
+}
diff --git a/mpeglib/lib/frame/audioFrame.h b/mpeglib/lib/frame/audioFrame.h
new file mode 100644
index 00000000..b1c586c3
--- /dev/null
+++ b/mpeglib/lib/frame/audioFrame.h
@@ -0,0 +1,83 @@
+/*
+ abstract definition of an audio frame
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __AUDIOFRAME_H
+#define __AUDIOFRAME_H
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef WORDS_BIGENDIAN
+#define AUDIOFRAME_BIGENDIAN 1
+#else
+#define AUDIOFRAME_BIGENDIAN 0
+#endif
+
+
+#include "frame.h"
+#include <kdemacros.h>
+#define SCALFACTOR SHRT_MAX
+#define MP3FRAMESIZE (2*2*2*32*18)
+
+
+class KDE_EXPORT AudioFrame : public Frame {
+
+ int stereo;
+ int frequencyHZ;
+
+ public:
+ AudioFrame();
+ virtual ~AudioFrame();
+
+ // info about "import" data
+ void setFrameFormat(int stereo,int freq);
+
+ inline int getStereo() { return stereo; }
+ inline int getFrequenceHZ() { return frequencyHZ; }
+
+ // these return values depend on the implementation
+ // how the data is stored internally after "import"
+ inline int getSampleSize() { return sampleSize; }
+ inline int getBigEndian() { return lBigEndian; }
+ inline int getSigned() { return lSigned; }
+
+ // info about output
+ virtual int getLen();
+ virtual void setLen(int len);
+ virtual int getSize();
+ virtual void clearrawdata();
+
+ // data import
+ virtual void putFloatData(float* data,int len);
+ virtual void putFloatData(float* left,float* right,int len);
+
+
+ int isFormatEqual(AudioFrame* compare);
+ // Note: this can only be called with _real_ AudioFrame's as dest
+ void copyFormat(AudioFrame* dest);
+
+ void print(const char* msg);
+
+ protected:
+ int sampleSize;
+ int lBigEndian;
+ int lSigned;
+
+};
+
+
+#endif
diff --git a/mpeglib/lib/frame/audioFrameQueue.cpp b/mpeglib/lib/frame/audioFrameQueue.cpp
new file mode 100644
index 00000000..16488748
--- /dev/null
+++ b/mpeglib/lib/frame/audioFrameQueue.cpp
@@ -0,0 +1,339 @@
+/*
+ queues audio frames in an IOQueue, allows streaming from frames
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "audioFrameQueue.h"
+
+#define _FLOAT_2_TRANSFER 1
+#define _FLOAT_1_TRANSFER 2
+#define _INT_2_TRANSFER 3
+#define _INT_1_TRANSFER 4
+#define _FORWARD_TRANSFER 5
+
+#include <iostream>
+
+using namespace std;
+
+AudioFrameQueue::AudioFrameQueue(int queueSize,
+ int frameSize,
+ int frameType):IOFrameQueue(queueSize) {
+ switch(frameType) {
+ case _FRAME_AUDIO_PCM: {
+ // fill queue with elements
+ while(emptyQueueCanWrite()) {
+ PCMFrame* pcmFrame=new PCMFrame(frameSize);
+ emptyQueueEnqueue(pcmFrame);
+ }
+ break;
+ }
+ case _FRAME_AUDIO_FLOAT: {
+ // fill queue with elements
+ while(emptyQueueCanWrite()) {
+ FloatFrame* floatFrame=new FloatFrame(frameSize);
+ emptyQueueEnqueue(floatFrame);
+ }
+ break;
+ }
+ default:
+ cout << "unknown frameType:"<<Frame::getFrameName(frameType)
+ <<" in AudioFrameQueue"<<endl;
+ exit(0);
+ }
+ len=0;
+ currentAudioFrame=new AudioFrame();
+ currentRead=0;
+ this->frameType=frameType;
+}
+
+
+AudioFrameQueue::~AudioFrameQueue() {
+ delete currentAudioFrame;
+}
+
+
+void AudioFrameQueue::emptyQueueEnqueue(AudioFrame* frame) {
+ empty->enqueue(frame);
+}
+
+
+AudioFrame* AudioFrameQueue::emptyQueueDequeue() {
+ return (AudioFrame*)empty->dequeue();
+}
+
+
+void AudioFrameQueue::dataQueueEnqueue(AudioFrame* frame) {
+ if (data->getFillgrade() == 0) {
+ frame->copyFormat(currentAudioFrame);
+ }
+
+ len+=frame->getLen();
+ data->enqueue(frame);
+
+}
+
+
+AudioFrame* AudioFrameQueue::dataQueueDequeue() {
+ AudioFrame* back=(AudioFrame*)data->dequeue();
+ currentRead=0;
+ len-=back->getLen();
+ back->copyFormat(currentAudioFrame);
+ return back;
+}
+
+AudioFrame* AudioFrameQueue::getCurrent() {
+ return currentAudioFrame;
+}
+
+
+int AudioFrameQueue::getLen() {
+ return len-currentRead;
+}
+
+
+int AudioFrameQueue::copy(float* left,float* right,int wantLen) {
+ if (frameType != _FRAME_AUDIO_FLOAT) {
+ cout << "AudioFrameQueue::copy class is frameType short int"<<endl;
+ exit(0);
+ }
+ // whats mux?
+ // well the routines are badly broken and need a rewrite
+ // mux deals with the problem that
+ // if the src is stereo we expect the left/right pointers
+ // to have half the size of wantlen*2
+ // ridicouls complex?
+ // right, as I said, this needs a clean solution
+ int mux=1;
+
+ if (currentAudioFrame->getStereo()) {
+ wantLen*=2;
+ mux=2;
+ }
+
+ int back=copygeneric((char*)left,(char*)right,
+ wantLen,_FLOAT_2_TRANSFER,mux);
+ if (currentAudioFrame->getStereo()) back/=2;
+
+ return back;
+}
+
+int AudioFrameQueue::copy(short int* left,short int* right,int wantLen) {
+ if (frameType != _FRAME_AUDIO_PCM) {
+ cout << "AudioFrameQueue::copy class is frameType float"<<endl;
+ exit(0);
+ }
+ // for this mux ugly hack read above
+ int mux=1;
+
+ if (currentAudioFrame->getStereo()) {
+ wantLen*=2;
+ mux=2;
+ }
+ int back=copygeneric((char*)left,(char*)right,
+ wantLen,_INT_2_TRANSFER,mux);
+ if (currentAudioFrame->getStereo()) back/=2;
+
+ return back;
+}
+
+int AudioFrameQueue::copy(short int* dest,int wantLen) {
+ if (frameType != _FRAME_AUDIO_PCM) {
+ cout << "AudioFrameQueue::copy class is frameType int single"<<endl;
+ exit(0);
+ }
+ int back=copygeneric((char*)dest,(char*)NULL,
+ wantLen,_INT_1_TRANSFER,1);
+ return back;
+}
+
+int AudioFrameQueue::copy(float* dest,int wantLen) {
+ if (frameType != _FRAME_AUDIO_FLOAT) {
+ cout << "AudioFrameQueue::copy class is frameType float single"<<endl;
+ exit(0);
+ }
+ int back=copygeneric((char*)dest,(char*)NULL,
+ wantLen,_FLOAT_1_TRANSFER,1);
+ return back;
+}
+
+void AudioFrameQueue::forwardStreamSingle(int forwardLen) {
+ int back=copygeneric((char*)NULL,(char*)NULL,
+ forwardLen,_FORWARD_TRANSFER,1);
+ if (back != forwardLen) {
+ cout << "error while forwarding stream"<<endl;
+ exit(0);
+ }
+}
+
+void AudioFrameQueue::forwardStreamDouble(int forwardLen) {
+
+ int mux=1;
+
+ if (currentAudioFrame->getStereo()) {
+ forwardLen*=2;
+ }
+ int back=copygeneric((char*)NULL,(char*)NULL,
+ forwardLen,_FORWARD_TRANSFER,mux);
+
+ if (back != forwardLen) {
+ cout << "error while forwarding stream"<<endl;
+ exit(0);
+ }
+}
+
+
+int AudioFrameQueue::copygeneric(char* left,char* right,
+ int wantLen,int version,int mux) {
+
+
+ int processed=currentRead;
+
+ if (wantLen > (len-processed)) {
+ wantLen=len-processed;
+ }
+ int pos=0;
+ int doLen=wantLen;
+ while(doLen > 0) {
+ AudioFrame* current=(AudioFrame*)data->peekqueue(pos);
+ int totallen=current->getLen();
+ int restlen=totallen-processed;
+ int copylen=doLen;
+ if (doLen > restlen) {
+ copylen=restlen;
+ }
+ doLen-=copylen;
+
+ switch(version) {
+ case _FLOAT_2_TRANSFER:
+ transferFrame((float*)left,
+ (float*)right,
+ (FloatFrame*) current,processed,copylen);
+ left+=copylen/mux*(sizeof(float));
+ right+=copylen/mux*(sizeof(float));
+ break;
+ case _FLOAT_1_TRANSFER:
+ transferFrame((float*)left,
+ (FloatFrame*) current,processed,copylen);
+ left+=copylen*sizeof(short int);
+ break;
+ case _INT_2_TRANSFER:
+ transferFrame((short int*)left,
+ (short int*)right,
+ (PCMFrame*) current,processed,copylen);
+ left+=copylen/mux*(sizeof(short int));
+ right+=copylen/mux*(sizeof(short int));
+ break;
+ case _INT_1_TRANSFER:
+ transferFrame((short int*)left,
+ (PCMFrame*) current,processed,copylen);
+ left+=copylen*sizeof(short int);
+ break;
+ case _FORWARD_TRANSFER:
+ break;
+ default:
+ cout << "unknown transfer method AudioFrameQueue::copygeneric"<<endl;
+ exit(0);
+ }
+ processed+=copylen;
+ if ( processed == totallen) {
+ processed=0;
+ if (version == _FORWARD_TRANSFER) {
+ current=dataQueueDequeue();
+ emptyQueueEnqueue(current);
+ } else {
+ pos++;
+ }
+ }
+ }
+ if (version == _FORWARD_TRANSFER) {
+ currentRead=processed;
+ }
+ if (doLen < 0) {
+ cout << "error while copy in AudioFrameQueue"<<endl;
+ exit(0);
+ }
+ return wantLen;
+}
+
+
+void AudioFrameQueue::transferFrame(float* left,float* right,
+ FloatFrame* current,int start,int len) {
+ float* ptr=current->getData()+start;
+ switch(currentAudioFrame->getStereo()) {
+ case true:
+ len=len/2;
+ while(len) {
+ *left++=*ptr++;
+ *right++=*ptr++;
+ len--;
+ }
+ break;
+ case false:
+ while(len) {
+ *left++=*ptr;
+ *right++=*ptr++;
+ len--;
+ }
+ break;
+ default:
+ cout << "bad stereo value AudioFrameQueue::transferFrame (float)"<<endl;
+ exit(0);
+ }
+}
+
+void AudioFrameQueue::transferFrame(short int* left,short int* right,
+ PCMFrame* current,int start,int len) {
+ short int* ptr=current->getData()+start;
+ switch(currentAudioFrame->getStereo()) {
+ case true:
+ len=len/2;
+ while(len) {
+ *left++=*ptr++;
+ *right++=*ptr++;
+ len--;
+ }
+ break;
+ case false:
+ while(len) {
+ *left++=*ptr;
+ *right++=*ptr++;
+ len--;
+ }
+ break;
+ default:
+ cout << "bad stereo value AudioFrameQueue::transferFrame (int)"<<endl;
+ exit(0);
+ }
+}
+
+
+void AudioFrameQueue::transferFrame(short int* dest,
+ PCMFrame* current,int start,int len) {
+ short int* ptr=current->getData()+start;
+ memcpy(dest,ptr,len*sizeof(short int));
+
+}
+
+
+void AudioFrameQueue::transferFrame(float* dest,
+ FloatFrame* current,int start,int len) {
+ float* ptr=current->getData()+start;
+ memcpy(dest,ptr,len*sizeof(float));
+
+}
+
+
+void AudioFrameQueue::clear() {
+ while(dataQueueCanRead()) {
+ AudioFrame* current=dataQueueDequeue();
+ emptyQueueEnqueue(current);
+ }
+}
diff --git a/mpeglib/lib/frame/audioFrameQueue.h b/mpeglib/lib/frame/audioFrameQueue.h
new file mode 100644
index 00000000..7e7a01c0
--- /dev/null
+++ b/mpeglib/lib/frame/audioFrameQueue.h
@@ -0,0 +1,75 @@
+/*
+ queues audio frames in an IOQueue, allows streaming from frames
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __AUDIOFRAMEQUE_H
+#define __AUDIOFRAMEQUE_H
+
+#include "IOFrameQueue.h"
+#include "floatFrame.h"
+#include "pcmFrame.h"
+
+#include <kdemacros.h>
+
+/**
+ This class solves the problem that we produce audioFrames
+ in a packet, but often we want that these packets looks
+ like a stream.
+ This class can convert from packets back to a stream.
+*/
+
+class KDE_EXPORT AudioFrameQueue : public IOFrameQueue {
+
+ int frameType;
+ int len;
+ AudioFrame* currentAudioFrame;
+ int currentRead;
+
+ public:
+ AudioFrameQueue(int queueSize,int frameSize,int frameType);
+ ~AudioFrameQueue();
+
+ void emptyQueueEnqueue(AudioFrame* frame);
+ AudioFrame* emptyQueueDequeue();
+
+ void dataQueueEnqueue(AudioFrame* frame);
+ AudioFrame* dataQueueDequeue();
+
+ // Meta info about stream
+ AudioFrame* getCurrent();
+
+ // Data info about "stream" (calculated from the packets)
+ int getLen();
+
+ // copy from packets to destination
+ int copy(float* left,float* right,int len);
+ int copy(short int* left,short int* right,int len);
+ void forwardStreamDouble(int len);
+
+ int copy(short int* dest,int len);
+ int copy(float* dest,int len);
+ void forwardStreamSingle(int len);
+
+ void clear();
+
+ private:
+ void transferFrame(float* left,float* right,FloatFrame*,int start,int len);
+ void transferFrame(short int* left,short int* right,
+ PCMFrame*,int start,int len);
+ void transferFrame(short int* dest,
+ PCMFrame*,int start,int len);
+ void transferFrame(float* dest,
+ FloatFrame*,int start,int len);
+ int copygeneric(char* left,char* right,int wantLen,int version,int mux);
+};
+#endif
diff --git a/mpeglib/lib/frame/floatFrame.cpp b/mpeglib/lib/frame/floatFrame.cpp
new file mode 100644
index 00000000..5e17d1e1
--- /dev/null
+++ b/mpeglib/lib/frame/floatFrame.cpp
@@ -0,0 +1,50 @@
+/*
+ stores frames as floats.
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "floatFrame.h"
+
+#include <iostream>
+
+using namespace std;
+
+FloatFrame::FloatFrame(int size) {
+ data=new float[size];
+ len=0;
+ this->size=size;
+ this->sampleSize=sizeof(float)*8;
+ this->lSigned=true;
+ this->lBigEndian=AUDIOFRAME_BIGENDIAN;
+ setFrameType(_FRAME_AUDIO_FLOAT);
+}
+
+
+FloatFrame::~FloatFrame() {
+ delete [] data;
+}
+
+
+void FloatFrame::putFloatData(float* in,int lenCopy) {
+ if ((len+lenCopy) > size) {
+ cout << "cannot copy putFloatData. Does not fit"<<endl;
+ exit(0);
+ }
+ memcpy(data+len,in,lenCopy*sizeof(float));
+ len+=lenCopy;
+
+
+}
+
+
+void FloatFrame::putFloatData(float* left,float* right,int len) {
+ cout << "not yet implemented"<<endl;
+}
diff --git a/mpeglib/lib/frame/floatFrame.h b/mpeglib/lib/frame/floatFrame.h
new file mode 100644
index 00000000..4d817021
--- /dev/null
+++ b/mpeglib/lib/frame/floatFrame.h
@@ -0,0 +1,46 @@
+/*
+ stores frames as floats.
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __FLOATFRAME_H
+#define __FLOATFRAME_H
+
+#include "audioFrame.h"
+
+// this format has a sampleSize of sizeof(float), signed, endian==machine
+
+class FloatFrame : public AudioFrame {
+
+ float* data;
+ int len;
+ int size;
+
+
+ public:
+ FloatFrame(int size);
+ ~FloatFrame();
+
+ int getLen() { return len; }
+ void setLen(int len) { this->len=len; }
+ int getSize() { return size; }
+ float* getData() { return data; }
+
+ void putFloatData(float* data,int len);
+ void putFloatData(float* left,float* right,int len);
+
+ void clearrawdata() { len=0; }
+
+
+
+};
+#endif
diff --git a/mpeglib/lib/frame/frame.cpp b/mpeglib/lib/frame/frame.cpp
new file mode 100644
index 00000000..c6a43969
--- /dev/null
+++ b/mpeglib/lib/frame/frame.cpp
@@ -0,0 +1,73 @@
+/*
+ base class for frames
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "frame.h"
+
+
+Frame::Frame() {
+ type=_FRAME_UNK;
+}
+
+
+Frame::~Frame() {
+}
+
+
+const char* Frame::getMajorFrameName(int type) {
+ int majorID=type >> 12;
+ switch(majorID) {
+ case _FRAME_UNK:
+ return "_FRAME_UNK";
+ case _FRAME_RAW:
+ return "_FRAME_RAW";
+ case _FRAME_AUDIO:
+ return "_FRAME_AUDIO";
+ case _FRAME_VIDEO:
+ return "_FRAME_VIDEO";
+ case _FRAME_PAKET:
+ return "_FRAME_PAKET";
+ default:
+ return "unknown major frameType";
+ }
+ return "never happens Frame::getMajorFrameName";
+}
+
+
+
+const char* Frame::getFrameName(int type) {
+ switch(type) {
+ // Raw
+ case _FRAME_RAW_BASE:
+ return "_FRAME_RAW_BASE";
+ case _FRAME_RAW_OGG:
+ return "_FRAME_RAW_OGG";
+
+
+ // Audio
+ case _FRAME_AUDIO_BASE:
+ return "_FRAME_AUDIO_BASE";
+ case _FRAME_AUDIO_PCM:
+ return "_FRAME_AUDIO_PCM";
+ case _FRAME_AUDIO_FLOAT:
+ return "_FRAME_AUDIO_FLOAT";
+
+
+
+ // Rest
+ default:
+ return "cannot find name";
+ }
+ return "never happens Frame::getFrameName";
+}
+
diff --git a/mpeglib/lib/frame/frame.h b/mpeglib/lib/frame/frame.h
new file mode 100644
index 00000000..301b997c
--- /dev/null
+++ b/mpeglib/lib/frame/frame.h
@@ -0,0 +1,101 @@
+/*
+ base class for frames
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __FRAME_H
+#define __FRAME_H
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+
+/**
+ The base class for frames. Every derived class from this class
+ must belong to some "major" class type and it must have an unique
+ id for itsself. Even if it is a base class it must have a unique id.
+
+ How does this work. We have an int for the Frame id. In the int
+ itsself we but the majorid as well.
+ The Start codes are all multiple of 2 so for example
+ 0..127 belongs to FRAME UNK
+ 128..255 belongs to FRAME RAW
+
+ So we can with a simple shift operation find out the major class
+*/
+#define _FRAME_SHIFT 7
+#define _FRAME_ID_MAX 128 //(2^_FRAME_SHIFT)
+
+
+// Major Frame classes
+#define _FRAME_UNK 0
+#define _FRAME_RAW 1
+#define _FRAME_AUDIO 2
+#define _FRAME_VIDEO 3
+#define _FRAME_PAKET 4
+
+// start ids of minor classes
+
+#define _FRAME_UNK_START (0)
+#define _FRAME_RAW_START (_FRAME_ID_MAX)
+#define _FRAME_AUDIO_START (_FRAME_ID_MAX*2)
+#define _FRAME_VIDEO_START (_FRAME_ID_MAX*2*2)
+#define _FRAME_PAKET_START (_FRAME_ID_MAX*2*2*2)
+
+// Minor Frame type IDs
+
+
+// Raw
+#define _FRAME_RAW_BASE (_FRAME_RAW_START+1)
+#define _FRAME_RAW_OGG (_FRAME_RAW_START+2)
+#define _FRAME_RAW_MPEG_I_VIDEO (_FRAME_RAW_START+3)
+#define _FRAME_RAW_MPEG_SYSTEM (_FRAME_RAW_START+4)
+
+
+// Audio:
+#define _FRAME_AUDIO_BASE (_FRAME_AUDIO_START+1)
+#define _FRAME_AUDIO_PCM (_FRAME_AUDIO_START+2)
+#define _FRAME_AUDIO_FLOAT (_FRAME_AUDIO_START+3)
+
+// Video:
+#define _FRAME_VIDEO_BASE (_FRAME_VIDEO_START+1)
+#define _FRAME_VIDEO_YUV (_FRAME_VIDEO_START+2)
+#define _FRAME_VIDEO_RGB_8 (_FRAME_VIDEO_START+3)
+#define _FRAME_VIDEO_RGB_16 (_FRAME_VIDEO_START+4)
+#define _FRAME_VIDEO_RGB_32 (_FRAME_VIDEO_START+5)
+
+// Packet:
+#define _FRAME_PACKET_SYNC (_FRAME_PAKET_START+1)
+#define _FRAME_PACKET_PACKET_CONTAINER (_FRAME_PAKET_START+2)
+
+
+
+
+class Frame {
+ int type;
+
+ public:
+ Frame();
+ ~Frame();
+ inline int getMajorFrameType() { return (type>>_FRAME_SHIFT);}
+ inline int getFrameType() { return type; }
+ inline void setFrameType(int type) { this->type=type; }
+
+
+ static const char* getMajorFrameName(int type);
+ static const char* getFrameName(int type);
+};
+#endif
diff --git a/mpeglib/lib/frame/frameQueue.cpp b/mpeglib/lib/frame/frameQueue.cpp
new file mode 100644
index 00000000..fe524976
--- /dev/null
+++ b/mpeglib/lib/frame/frameQueue.cpp
@@ -0,0 +1,101 @@
+/*
+ queues frames
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "frameQueue.h"
+
+#include <iostream>
+
+using namespace std;
+
+FrameQueue::FrameQueue(int maxsize) {
+ this->size=maxsize;
+ entries = new Frame*[size];
+ int i;
+ for(i=0;i<size;i++) {
+ entries[i]=NULL;
+ }
+ fillgrade=0;
+ writepos=0;
+ readpos=0;
+}
+
+
+FrameQueue::~FrameQueue() {
+ int i;
+ for(i=0;i<size;i++) {
+ if (entries[i] != NULL) {
+ delete entries[i];
+ }
+ }
+ delete entries;
+}
+
+int FrameQueue::canRead() {
+ return (fillgrade > 0);
+}
+
+
+int FrameQueue::canWrite() {
+ return (fillgrade < size);
+}
+
+
+int FrameQueue::getFillgrade() {
+ return fillgrade;
+}
+
+
+void FrameQueue::enqueue(Frame* frame) {
+ if(canWrite() == false) {
+ cout << "FrameQueue full cannot enqueue"<<endl;
+ exit(0);
+ }
+ fillgrade++;
+ entries[writepos]=frame;
+ writepos++;
+ if (writepos == size) {
+ writepos=0;
+ }
+}
+
+
+Frame* FrameQueue::dequeue() {
+ if(canRead() == false) {
+ cout << "FrameQueue empty cannot dequeue"<<endl;
+ exit(0);
+ }
+ Frame* back=entries[readpos];
+ // invalide this frame, we do not longer own it!
+ entries[readpos]=NULL;
+ fillgrade--;
+ readpos++;
+ if (readpos == size) {
+ readpos=0;
+ }
+ return back;
+}
+
+Frame* FrameQueue::peekqueue(int pos) {
+ if(fillgrade-pos <= 0) {
+ cout << "FrameQueue : cannot peek this positon"<<endl;
+ cout << "fillgrade:"<<fillgrade<<endl;
+ cout << "pos:"<<pos<<endl;
+ exit(0);
+ }
+ int getpos=(readpos+pos) % size;
+
+
+ Frame* back=entries[getpos];
+ return back;
+}
+
diff --git a/mpeglib/lib/frame/frameQueue.h b/mpeglib/lib/frame/frameQueue.h
new file mode 100644
index 00000000..27747aa3
--- /dev/null
+++ b/mpeglib/lib/frame/frameQueue.h
@@ -0,0 +1,43 @@
+/*
+ queues frames
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __FRAMEQUEUE_H
+#define __FRAMEQUEUE_H
+
+#include "frame.h"
+
+class FrameQueue {
+
+ Frame** entries;
+ int fillgrade;
+ int size;
+ int writepos;
+ int readpos;
+
+
+ public:
+ FrameQueue(int maxsize);
+ ~FrameQueue();
+
+ int getFillgrade();
+ int canRead();
+ int canWrite();
+ void enqueue(Frame* frame);
+ Frame* dequeue();
+ Frame* peekqueue(int pos);
+
+
+};
+#endif
diff --git a/mpeglib/lib/frame/framer.cpp b/mpeglib/lib/frame/framer.cpp
new file mode 100644
index 00000000..d380b2ec
--- /dev/null
+++ b/mpeglib/lib/frame/framer.cpp
@@ -0,0 +1,241 @@
+/*
+ base class for converting raw data(stream) into some frametype.
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "framer.h"
+
+
+
+#define PROCESS_FIND 0
+#define PROCESS_READ 1
+
+#include <iostream>
+
+using namespace std;
+
+Framer::Framer(int outsize) {
+ unsigned char* outptr=new unsigned char[outsize];
+ init(outsize,outptr,true);
+}
+
+
+Framer::Framer(int outsize,unsigned char* outptr) {
+ if (outptr == NULL) {
+ cout << "Framer::Framer outptr NULL"<<endl;
+ exit(0);
+ }
+ if (outsize <= 0) {
+ cout << "Framer::Framer size <= 0"<<endl;
+ exit(0);
+ }
+ init(outsize,outptr,false);
+}
+
+
+void Framer::init(int outsize,unsigned char* outptr,int lDeleteOutPtr) {
+ buffer_data=outptr;
+ this->lDeleteOutPtr=lDeleteOutPtr;
+ input_info=new RawDataBuffer(NULL,0);
+ buffer_info=new RawDataBuffer(buffer_data,outsize);
+ lConstruct=true;
+ reset();
+}
+
+
+Framer::~Framer() {
+ if (lDeleteOutPtr) {
+ delete[] buffer_data;
+ }
+ delete buffer_info;
+ delete input_info;
+}
+
+
+int Framer::canStore() {
+ return buffer_info->untilend();
+}
+
+int Framer::restBytes() {
+ return (input_info->size()-input_info->pos());
+}
+
+
+void Framer::store(unsigned char* start,int bytes) {
+ if (buffer_info->pos()+bytes > buffer_info->size()) {
+ cout << "too much bytes inserted. cannot store that"<<endl;
+ exit(0);
+ }
+ if (main_state != FRAME_NEED) {
+ cout << "cannot store data, when not in MPEGAUDIOFRAME_NEED"<<endl;
+ exit(0);
+ }
+ input_info->set(start,bytes,0);
+ int fillgrade=input_info->untilend();
+ if (fillgrade > 0) {
+ main_state=FRAME_WORK;
+ }
+}
+
+
+int Framer::work() {
+ if (main_state != FRAME_WORK) {
+ cout << "cannot find_frame, when not in MPEGAUDIOFRAME_WORK"<<endl;
+ exit(0);
+ }
+ if (lAutoNext) {
+ next();
+ }
+ switch(process_state) {
+ case PROCESS_FIND:
+ if (find_frame(input_info,buffer_info) == true) {
+ setState(PROCESS_READ);
+ }
+ break;
+ case PROCESS_READ:
+ if (read_frame(input_info,buffer_info) == true) {
+ main_state=FRAME_HAS;
+ }
+ break;
+ default:
+ cout << "unknown process state in work. "<<endl;
+ printMainStates("printing states");
+ exit(0);
+ }
+ // do not go to NEED if we have a frame and now the input is empty
+ if (main_state == FRAME_WORK) {
+ if (input_info->eof()) {
+ main_state=FRAME_NEED;
+ }
+ }
+ if (main_state == FRAME_HAS) return true;
+ return false;
+}
+
+
+void Framer::reset() {
+ unsync(buffer_info,true);
+ lAutoNext=false;
+ main_state=FRAME_NEED;
+ input_info->set(NULL,0,0);
+ buffer_info->setpos(0);
+ setState(PROCESS_FIND);
+}
+
+
+void Framer::next() {
+ unsync(buffer_info,false);
+ lAutoNext=false;
+ main_state=FRAME_WORK;
+ setState(PROCESS_FIND);
+}
+
+
+int Framer::getState() {
+ int back=main_state;
+ if (main_state == FRAME_HAS) {
+ // autonext when we devlivered one frame
+ lAutoNext=true;
+ main_state=FRAME_WORK;
+ setState(PROCESS_FIND);
+ }
+ if (lConstruct == true) {
+ lConstruct=false;
+ unsync(buffer_info,true);
+ }
+ return back;
+}
+
+
+void Framer::setState(int state) {
+ this->process_state=state;
+}
+
+
+
+unsigned char* Framer::outdata() {
+ return buffer_info->ptr();
+}
+
+
+unsigned char* Framer::indata() {
+ return buffer_info->current();
+}
+
+int Framer::len() {
+ return buffer_info->pos();
+}
+
+
+
+void Framer::printMainStates(const char* msg) {
+ cout << msg<<endl;
+ switch(main_state) {
+ case FRAME_NEED:
+ cout << "main_state: FRAME_NEED"<<endl;
+ break;
+ case FRAME_WORK:
+ cout << "main_state: FRAME_WORK"<<endl;
+ break;
+ case FRAME_HAS:
+ cout << "main_state: FRAME_HAS"<<endl;
+ break;
+ default:
+ cout << "unknown illegal main_state:"<<main_state<<endl;
+ }
+
+ switch(process_state) {
+ case PROCESS_FIND:
+ cout << "process_state: PROCESS_FIND"<<endl;
+ break;
+ case PROCESS_READ:
+ cout << "process_state: PROCESS_READ"<<endl;
+ break;
+ default:
+ cout << "unknown illegal process_state:"<<process_state<<endl;
+ }
+ printPrivateStates();
+
+
+}
+
+
+
+
+int Framer::find_frame(RawDataBuffer* ,RawDataBuffer* ) {
+ cout << "direct virtual call Framer::find_frame"<<endl;
+ return false;
+}
+
+
+int Framer::read_frame(RawDataBuffer* ,RawDataBuffer* ) {
+ cout << "direct virtual call Framer::read_frame"<<endl;
+ return false;
+}
+
+void Framer::unsync(RawDataBuffer* ,int ) {
+ if (lConstruct == false) {
+ // invalidate header in buffer
+ cout << "direct virtual call Framer::unsync"<<endl;
+ }
+}
+
+
+void Framer::printPrivateStates() {
+ cout << "direct virtual call Framer::printPrivateStates"<<endl;
+}
+
+
+void Framer::setRemoteFrameBuffer(unsigned char* outptr,int size) {
+
+ input_info->set(NULL,0,0);
+ buffer_info->set(outptr,size,0);
+}
diff --git a/mpeglib/lib/frame/framer.h b/mpeglib/lib/frame/framer.h
new file mode 100644
index 00000000..51c4b26e
--- /dev/null
+++ b/mpeglib/lib/frame/framer.h
@@ -0,0 +1,182 @@
+/*
+ base class for converting raw data(stream) into some frametype.
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __FRAMER_H
+#define __FRAMER_H
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <kdemacros.h>
+
+#define FRAME_NEED 0
+#define FRAME_WORK 1
+#define FRAME_HAS 2
+
+
+#include "rawDataBuffer.h"
+
+
+/**
+ If we want to have a "push" interface, this means the decoder
+ does not read from an input interface, we must be sure
+ that we have a valid mpeg audio frame, before we feed
+ this whole frame to the decoder.
+
+ This class tells you how many bytes it can read and
+ gives you information wether we have a valid frame or not.
+
+ This class has three public states:
+
+ FRAME_NEED
+ / \
+ / \
+ / \
+ FRAME_WORK <----------> FRAME_HAS
+
+
+ after the "FRAME_HAS" state we devliver _excatly_ one time
+ this state, and then automatically go to "FRAME_WORK" state.
+
+
+ You can reset() the class which empties the buffer and then
+ starts searching for a new sync code, or you can
+ do a next() which searches for the sync code, without
+ emptying the buffer (this is done automatically,
+ after the first call, when we was ins state "FRAME_HAS"
+
+ Note: i) You need a reset() if a "seek" occurs in your stream
+ ii) First call to the class must be "getState" which does the
+ Post-Constructor setup in derived classes!
+
+ The FRAME_NEED state is entered if the input buffer is empty
+ you then need to "push" data in this class.
+ The FRAME_NEED state is necessary to avoid an additonal memcpy
+ and a ringbuffer.(This safes "overhead"/"cpu cycles")
+*/
+
+
+
+
+class KDE_EXPORT Framer {
+
+ // this is our destination buffer for the output frame
+ // this buffer must be able to store the maximum size
+ // a frame of this type can have.
+ // Examples:
+ // avi-Chunk : 65KB
+ // mpeg audio: 4KB is always enough
+ // mpeg video: 224K (spec says this)
+
+ // this can be a remote buffer or a local one
+ unsigned char* buffer_data;
+ RawDataBuffer* buffer_info;
+
+ // state from FIND->READ->HAS
+ int process_state;
+ // state between NEED <-> PROCESS
+ int main_state;
+
+ RawDataBuffer* input_info;
+ int lAutoNext;
+
+ // stores if we have alloceated outdata or not
+ int lDeleteOutPtr;
+ // internal: unsync not done
+ int lConstruct;
+
+ public:
+ // allocate local output buffer
+ Framer(int outsize);
+
+ // outbuffer is remote.
+ Framer(int outsize,unsigned char* outptr);
+
+ virtual ~Framer();
+
+ //
+ // process states (transitions) [START]
+ //
+
+ // stores pointer to input and len
+ void store(unsigned char* start,int bytes);
+ int work();
+ void reset();
+ void next();
+ // returns pointer to outbuffer (frameheader+data)
+ unsigned char* outdata();
+
+ // returns pointer to inbuffer (raw data)
+ // Note: this ptr is not fixed! It may vary from time to time
+ // Cannot be stores in a variable!
+ unsigned char* indata();
+
+
+ //
+ // process states (transitions) [END]
+ //
+
+
+ //
+ // state helper functions [START]
+ //
+ int getState();
+ // returns number of bytes.
+ int canStore();
+
+ // returns length of frame == len(frameheader+data)
+ int len();
+
+ // returns number of bytes still in input(needed for a/v sync)
+ int restBytes();
+ //
+ // state helper functions [END]
+ //
+
+ // debugging
+ void printMainStates(const char* msg);
+
+ private:
+ void init(int outsize,unsigned char* outptr,int lDeleteOutptr);
+
+ void setState(int state);
+
+ //
+ // Overload functions for specialized framers [START]
+ //
+
+ // return true, if frame header found
+ virtual int find_frame(RawDataBuffer* input,RawDataBuffer* store);
+ // return true, if frame data read.
+ virtual int read_frame(RawDataBuffer* input,RawDataBuffer* store);
+ // makes buffer invalid, reset to begin of "find_frame"
+ virtual void unsync(RawDataBuffer* store,int lReset);
+ // debugging
+ virtual void printPrivateStates();
+
+ //
+ // Overload functions for specialized framers [END]
+ //
+
+ protected:
+ // this can be used, if the outptr come from a different framer
+ // (eg: OGG framer). You then need to call the construtor with
+ // some "dummy" size.
+ void setRemoteFrameBuffer(unsigned char* outptr,int size);
+
+};
+
+
+#endif
diff --git a/mpeglib/lib/frame/pcmFrame.cpp b/mpeglib/lib/frame/pcmFrame.cpp
new file mode 100644
index 00000000..24ab9792
--- /dev/null
+++ b/mpeglib/lib/frame/pcmFrame.cpp
@@ -0,0 +1,140 @@
+/*
+ pcm frame description.
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "pcmFrame.h"
+
+#include <iostream>
+
+using namespace std;
+
+#ifndef WORDS_BIGENDIAN
+// this is fast on INTEL, but maybe not work on other little
+// endian machines. (are there any?)
+#define convMacro(in,dtemp,tmp) \
+ in[0]*=SCALFACTOR; \
+ dtemp = ((((65536.0 * 65536.0 * 16)+(65536.0 * 0.5))* 65536.0))+(in[0]); \
+ tmp = ((*(int *)&dtemp) - 0x80000000); \
+ in++; \
+ if(tmp>32767) { \
+ tmp=32767; \
+ } else if (tmp<-32768) { \
+ tmp =-0x8000; \
+ }
+
+#else /* big endian conversion: _AIX */
+static inline void convMacro(float*& in, double dtemp, int& tmp)
+{
+ in[0]*=SCALFACTOR;
+ tmp = (int)*in;
+ in++;
+ if(tmp>32767) {
+ tmp=32767;
+ } else if (tmp<-32768) {
+ tmp =-0x8000;
+ }
+ tmp = ((tmp & 0xff) << 8) | ((tmp >> 8) & 0xff);
+}
+#endif /* _AIX */
+
+
+PCMFrame::PCMFrame(int size) {
+ data=new short int[size];
+ len=0;
+ this->size=size;
+ // this format has a sampleSize of 16, signed, endian==machine
+ this->sampleSize=sizeof(short int)*8;
+ this->lSigned=true;
+ this->lBigEndian=AUDIOFRAME_BIGENDIAN;
+ setFrameType(_FRAME_AUDIO_PCM);
+}
+
+
+PCMFrame::~PCMFrame() {
+ delete [] data;
+}
+
+
+void PCMFrame::putFloatData(float* left,float* right,int copyLen) {
+ int destSize=0;
+ if (left != NULL) destSize++;
+ if (right != NULL) destSize++;
+ destSize*=copyLen;
+ if ((len+destSize) > size) {
+ cout << "cannot copy putFloatData L/R version . Does not fit"<<endl;
+ cout << "size:"<<size<<endl;
+ cout << "len:"<<len<<endl;
+ cout << "destSize:"<<destSize<<endl;
+ exit(0);
+ }
+ double dtemp;
+ int tmp;
+ int i=copyLen;
+ switch(getStereo()) {
+ case 1:
+ while(i > 0) {
+ convMacro(left,dtemp,tmp);
+ data[len++]=(short int)tmp;
+ convMacro(right,dtemp,tmp);
+ data[len++]=(short int)tmp;
+ i--;
+ }
+ break;
+ case 0:
+ if (left != NULL) {
+ int i=copyLen;
+ while(i > 0) {
+ convMacro(left,dtemp,tmp);
+ data[len++]=(short int)tmp;
+ i--;
+ // right channel empty
+ len++;
+ }
+ }
+ if (right != NULL) {
+ int i=copyLen;
+ len=len-destSize;
+ while(i > 0) {
+ // select right channel
+ len++;
+ convMacro(right,dtemp,tmp);
+ data[len++]=(short int)tmp;
+ i--;
+ // left channel already copied
+ }
+ }
+ break;
+ default:
+ cout << "unknown stereo value in pcmFrame"<<endl;
+ exit(0);
+ }
+}
+
+void PCMFrame::putFloatData(float* in,int lenCopy) {
+
+ if ((len+lenCopy) > size) {
+ cout << "cannot copy putFloatData. Does not fit"<<endl;
+ exit(0);
+ }
+ double dtemp;
+ int tmp;
+ while(lenCopy > 0) {
+ convMacro(in,dtemp,tmp);
+ data[len++]=(short int)tmp;
+ lenCopy--;
+ }
+
+}
+
+
+
diff --git a/mpeglib/lib/frame/pcmFrame.h b/mpeglib/lib/frame/pcmFrame.h
new file mode 100644
index 00000000..a19941bc
--- /dev/null
+++ b/mpeglib/lib/frame/pcmFrame.h
@@ -0,0 +1,45 @@
+/*
+ pcm frame description.
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __PCMFRAME_H
+#define __PCMFRAME_H
+
+
+#include "audioFrame.h"
+#include <kdemacros.h>
+// this format has a sampleSize of 16, signed, endian==machine
+
+class KDE_EXPORT PCMFrame : public AudioFrame {
+
+ short int* data;
+ int len;
+ int size;
+
+ public:
+ PCMFrame(int size);
+ ~PCMFrame();
+
+ int getLen() { return len; }
+ void setLen(int len) { this->len=len; }
+ int getSize() { return size; }
+ short int* getData() { return data; }
+
+ void putFloatData(float* data,int len);
+ void putFloatData(float* left,float* right,int len);
+
+ void clearrawdata() { len=0; }
+
+
+};
+#endif
diff --git a/mpeglib/lib/frame/rawDataBuffer.cpp b/mpeglib/lib/frame/rawDataBuffer.cpp
new file mode 100644
index 00000000..f8635211
--- /dev/null
+++ b/mpeglib/lib/frame/rawDataBuffer.cpp
@@ -0,0 +1,21 @@
+/*
+ stores simple buffer information. does not allocate anything
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "rawDataBuffer.h"
+
+// hm
+
+RawDataBuffer::~RawDataBuffer() {
+}
+
+
diff --git a/mpeglib/lib/frame/rawDataBuffer.h b/mpeglib/lib/frame/rawDataBuffer.h
new file mode 100644
index 00000000..755818ea
--- /dev/null
+++ b/mpeglib/lib/frame/rawDataBuffer.h
@@ -0,0 +1,48 @@
+/*
+ stores simple buffer information. does not allocate anything
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __RAWDATABUFFER_H
+#define __RAWDATABUFFER_H
+
+
+class RawDataBuffer {
+
+ int _size;
+ unsigned char* _ptr;
+ int _pos;
+
+ public:
+ RawDataBuffer(unsigned char* ptr,int size) { set(ptr,size,0); }
+ ~RawDataBuffer();
+
+
+
+ unsigned char* ptr() { return _ptr; }
+ unsigned char* current() { return _ptr+_pos; }
+ int size() { return _size; }
+ int pos() { return _pos; }
+ int untilend() { return _size-_pos; }
+ int eof() { return _pos>=_size; }
+
+ void inc() { this->_pos++; }
+ void inc(int val) { this->_pos+=val; }
+ void setpos(int val) { this->_pos=val; }
+ void setptr(unsigned char* ptr) { this->_ptr=ptr; }
+ void setsize(int size) { this->_size=size; }
+
+ void set(unsigned char* ptr,
+ int size,int pos) { setpos(pos);setptr(ptr);setsize(size);}
+
+};
+#endif
diff --git a/mpeglib/lib/frame/rawFrame.cpp b/mpeglib/lib/frame/rawFrame.cpp
new file mode 100644
index 00000000..fbe662c5
--- /dev/null
+++ b/mpeglib/lib/frame/rawFrame.cpp
@@ -0,0 +1,90 @@
+/*
+ base class for raw frames (encoded data, where only the type is known)
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "rawFrame.h"
+
+#include <iostream>
+
+using namespace std;
+
+RawFrame::RawFrame(int size) {
+ init(_FRAME_RAW_BASE,size);
+}
+
+
+RawFrame::RawFrame(int type,int size) {
+ init(type,size);
+}
+
+
+RawFrame::~RawFrame() {
+ if (data != NULL) {
+ if (lRemoteData==false) {
+ delete data;
+ }
+ }
+}
+
+
+void RawFrame::print(const char* msg) {
+ cout << msg<<endl;
+ cout << "major Frametype:"<<Frame::getFrameName(getFrameType());
+ cout << "size:"<<getSize();
+ cout << "len:"<<getLen();
+
+}
+
+
+void RawFrame::init(int type,int size) {
+
+ if (size < 0) {
+ cout << "size <= 0 in RawFrame::RawFrame"<<endl;
+ exit(-1);
+ }
+ setFrameType(type);
+ int majorType=getMajorFrameType();
+ if (majorType != _FRAME_RAW) {
+ cout << "invalid Major Frametype:"<<Frame::getFrameName(getFrameType())
+ << " for this class"<<endl;
+ printf("ID:0x%x dec:%d majorID:%d\n",type,type,majorType);
+ cout << "RawFrame::init"<<endl;
+ exit(-1);
+ }
+ if (size == 0) {
+ data=NULL;
+ this->size=0;
+ }
+ if (size > 0) {
+ data=new unsigned char[size];
+ if (data != NULL) {
+ cout <<"malloc error RawFrame"<<endl;
+ exit(-1);
+ }
+ this->size=size;
+ }
+ setLen(0);
+ lRemoteData=false;
+}
+
+
+void RawFrame::setRemoteData(unsigned char* data,int size) {
+ if (this->data != NULL) {
+ if (lRemoteData==false) {
+ delete this->data;
+ }
+ }
+ lRemoteData=true;
+ this->data=data;
+ this->size=size;
+}
diff --git a/mpeglib/lib/frame/rawFrame.h b/mpeglib/lib/frame/rawFrame.h
new file mode 100644
index 00000000..e1a05e9e
--- /dev/null
+++ b/mpeglib/lib/frame/rawFrame.h
@@ -0,0 +1,75 @@
+/*
+ base class for raw frames (encoded data, where only the type is known)
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __RAW_FRAME_H
+#define __RAW_FRAME_H
+
+#include "frame.h"
+
+/**
+ Raw frames represents bitstreams. They most likely have have now
+ real value for anyone but a decoder.
+ In general you simply allocate a rawFrame with a given size, This
+ size should make sure, that the bitstream packet does fit into
+ the frame. Sometime, in derived classes you can set thes pointer
+ to the allocated directly by calling the protected method: setRemoteData.
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef WORDS_BIGENDIAN
+#define RAWFRAME_BIGENDIAN 1
+#else
+#define RAWFRAME_BIGENDIAN 0
+#endif
+
+
+class RawFrame : public Frame {
+
+ unsigned char* data;
+ int size;
+ int len;
+ int lRemoteData;
+
+ public:
+ RawFrame(int size);
+ RawFrame(int type,int size);
+ ~RawFrame();
+
+ // access start of frameData
+ unsigned char* getData() { return data; }
+
+ // current size in byte
+ int getLen() { return len; }
+ void setLen(int bytes) { this->len=bytes; }
+
+ // maximum size of allocated memory
+ int getSize() { return size; }
+
+
+ void print(const char* msg);
+
+ private:
+ void init(int type,int size);
+
+ protected:
+ void setRemoteData(unsigned char* data,int size);
+};
+
+
+
+#endif
diff --git a/mpeglib/lib/input/Makefile.am b/mpeglib/lib/input/Makefile.am
new file mode 100644
index 00000000..f7e849eb
--- /dev/null
+++ b/mpeglib/lib/input/Makefile.am
@@ -0,0 +1,37 @@
+# libinputplugin - Makefile.am
+
+EXTRA_DIST = cdromAccess_Linux.cpp cdromAccess_Empty.cpp \
+ cdigrap.cpp README
+
+INCLUDES = $(all_includes)
+
+noinst_LTLIBRARIES = libinput.la
+
+noinst_HEADERS = cdromToc.h cdromRawAccess.h \
+ simpleRingBuffer.h fileAccessWrapper.h
+
+kmpgincludedir = $(includedir)/$(THIS_LIB_NAME)/input
+
+kmpginclude_HEADERS = inputStream.h fileInputStream.h \
+ inputPlugin.h \
+ cdromInputStream.h bufferInputStream.h \
+ inputDetector.h httpInputStream.h \
+ threadSafeInputStream.h cddaInputStream.h
+
+
+
+libinput_la_SOURCES = inputStream.cpp fileInputStream.cpp \
+ inputPlugin.cpp \
+ cdromToc.cpp cdromRawAccess.cpp \
+ cdromInputStream.cpp \
+ bufferInputStream.cpp \
+ simpleRingBuffer.cpp \
+ cdromAccess.cpp inputDetector.cpp \
+ httpInputStream.cpp \
+ threadSafeInputStream.cpp \
+ cddaInputStream.cpp \
+ fileAccessWrapper.cpp
+
+# workaround for compile errors caused by linux/cdrom.h.
+# Linux kernel headers suck donkeyballs.
+KDE_CXXFLAGS = $(ENABLE_PERMISSIVE_FLAG)
diff --git a/mpeglib/lib/input/README b/mpeglib/lib/input/README
new file mode 100644
index 00000000..844a17bb
--- /dev/null
+++ b/mpeglib/lib/input/README
@@ -0,0 +1,15 @@
+
+
+Here is the abstraction of the inputplugin.
+Its a base class, with the usual open/seek/read methods.
+The only nice thing is the factory inputPlugin which
+creates you for a given url the correct class.
+
+
+All inputStreams can take a timeStamp. Its necessary for
+audio/video sync, but this must be supported
+by the outputplugin and the playerPlugin!
+Video is not that easy as audio :(
+
+
+
diff --git a/mpeglib/lib/input/bufferInputStream.cpp b/mpeglib/lib/input/bufferInputStream.cpp
new file mode 100644
index 00000000..68e502ff
--- /dev/null
+++ b/mpeglib/lib/input/bufferInputStream.cpp
@@ -0,0 +1,288 @@
+/*
+ reads input data
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "bufferInputStream.h"
+#include "simpleRingBuffer.h"
+
+
+BufferInputStream::BufferInputStream(int size,int minlinSize,
+ const char* name) {
+ ringBuffer=new SimpleRingBuffer(size,minlinSize);
+ leof=false;
+ bytePos=0;
+ fillgrade=0;
+ setUrl(name);
+ lLock=false;
+ abs_thread_mutex_init(&writeInMut);
+}
+
+
+BufferInputStream::~BufferInputStream() {
+ delete ringBuffer;
+ abs_thread_mutex_destroy(&writeInMut);
+}
+
+
+int BufferInputStream::open(const char*) {
+ leof=false;
+ setBlocking(true);
+ return true;
+}
+
+void BufferInputStream::close() {
+ leof=true;
+ setBlocking(false);
+}
+
+int BufferInputStream::eof() {
+ return (leof && (fillgrade==0));
+}
+
+int BufferInputStream::isOpen() {
+ return !leof;
+}
+
+void BufferInputStream::setBlocking(int lblock) {
+ ringBuffer->setCanWaitForSpace(lblock);
+ ringBuffer->setCanWaitForData(lblock);
+}
+
+int BufferInputStream::getHold() {
+ return lLock;
+}
+
+
+void BufferInputStream::setHold(int lLock) {
+ if (lLock) {
+ lockBuffer();
+ } else {
+ unlockBuffer();
+ }
+ this->lLock=lLock;
+}
+
+int BufferInputStream::read(char* ptr,int size) {
+ int i=0;
+ int n=size;
+ int canCopy=n;
+ char* readPtr;
+ // here we read even if leof is true
+ // we make sure that we empty the whole buffer!
+ while((eof()==false) && (n > 0)) {
+ canCopy=n;
+ ringBuffer->getReadArea(readPtr,canCopy);
+ if (canCopy <= 0){
+ ringBuffer->waitForData(1);
+ continue;
+ }
+ if (n < canCopy) {
+ canCopy=n;
+ }
+ memcpy((char*)ptr+i,readPtr,canCopy);
+ i=i+canCopy;
+ n=n-canCopy;
+ ringBuffer->forwardReadPtr(canCopy);
+ ringBuffer->forwardLockPtr(canCopy);
+ lockBuffer();
+ bytePos+=canCopy;
+ fillgrade-=canCopy;
+ unlockBuffer();
+ }
+ return i;
+}
+
+
+int BufferInputStream::write(char* ptr,int len,TimeStamp* stamp) {
+ int i=0;
+ int n=len;
+ int canWrite=n;
+ char* writePtr;
+
+ if (stamp) {
+ lockBuffer();
+ long key;
+ key=bytePos+fillgrade;
+ InputStream::insertTimeStamp(stamp,key,len);
+ unlockBuffer();
+ }
+ // if eof is set we do not insert any more data
+ // we do not call eof() !!!
+ while((leof==false) && (n > 0)) {
+ canWrite=n;
+ ringBuffer->getWriteArea(writePtr,canWrite);
+ if (canWrite <= 0){
+ ringBuffer->waitForSpace(1);
+ continue;
+ }
+ if (canWrite > n) {
+ canWrite=n;
+ }
+ memcpy(writePtr,(char*)ptr+i,canWrite);
+ i=i+canWrite;
+ n=n-canWrite;
+ ringBuffer->forwardWritePtr(canWrite);
+ lockBuffer();
+ fillgrade+=canWrite;
+ unlockBuffer();
+ }
+
+ return i;
+}
+
+
+
+int BufferInputStream::write(InputStream* input,int len,TimeStamp* stamp) {
+ int i=0;
+ int n=len;
+ int canWrite=n;
+ int didWrite;
+ char* writePtr;
+
+ if (stamp) {
+ lockBuffer();
+ long key;
+ key=bytePos+fillgrade;
+ InputStream::insertTimeStamp(stamp,key,len);
+ unlockBuffer();
+ }
+ // if eof is set we do not insert any more data
+ // we do not call eof() !!!
+ while((leof==false) && (n > 0)) {
+ canWrite=n;
+ ringBuffer->getWriteArea(writePtr,canWrite);
+ if (canWrite <= 0){
+ ringBuffer->waitForSpace(1);
+ continue;
+ }
+ if (canWrite > n) {
+ canWrite=n;
+ }
+ didWrite=input->read(writePtr,canWrite);
+ if (input->eof()) break;
+ i=i+didWrite;
+ n=n-didWrite;
+ ringBuffer->forwardWritePtr(didWrite);
+ lockBuffer();
+ fillgrade+=canWrite;
+ unlockBuffer();
+ }
+
+ return i;
+}
+
+
+
+long BufferInputStream::getByteLength() {
+ return ringBuffer->getFillgrade();
+}
+
+int BufferInputStream::getFillgrade() {
+ return ringBuffer->getFillgrade();
+}
+
+
+int BufferInputStream::getFreeRead() {
+ return ringBuffer->getFreeRead();
+}
+
+
+int BufferInputStream::getFreeSpace() {
+ return ringBuffer->getFreeWrite();
+}
+
+
+
+long BufferInputStream::getBytePosition() {
+ return bytePos;
+}
+
+void BufferInputStream::setBytePosition(long bytePos) {
+ this->bytePos=bytePos;
+}
+
+
+int BufferInputStream::seek(long) {
+ return false;
+}
+
+
+void BufferInputStream::clear() {
+
+ ringBuffer->emptyBuffer();
+ ringBuffer->exitWaitForData();
+ ringBuffer->exitWaitForSpace();
+ timeStampArray->clear();
+
+ lockBuffer();
+ bytePos=0;
+ fillgrade=0;
+ unlockBuffer();
+
+}
+
+
+
+
+// remote read extension
+int BufferInputStream::readRemote(char** ptr,int size) {
+ int n=0;
+ char* readPtr;
+ while((eof()==false)) {
+ n=size;
+ ringBuffer->getReadArea(readPtr,n);
+ if (n < size){
+ ringBuffer->waitForData(size);
+ if (ringBuffer->getCanWaitForData()==false) {
+ break;
+ }
+ continue;
+ }
+ break;
+ }
+ *ptr=readPtr;
+ return n;
+}
+
+
+void BufferInputStream::forwardReadPtr(int bytes) {
+
+ ringBuffer->forwardReadPtr(bytes);
+ ringBuffer->forwardLockPtr(bytes);
+ lockBuffer();
+ bytePos+=bytes;
+ fillgrade-=bytes;
+ unlockBuffer();
+ getTimeStamp(bytePos);
+}
+
+
+void BufferInputStream::setCanWaitForData(int lBlock) {
+ ringBuffer->setCanWaitForData(lBlock);
+}
+
+
+void BufferInputStream::lockBuffer() {
+ abs_thread_mutex_lock(&writeInMut);
+}
+
+
+void BufferInputStream::unlockBuffer() {
+ abs_thread_mutex_unlock(&writeInMut);
+}
+
+
+int BufferInputStream::getSize() {
+ return ringBuffer->getSize();
+}
+
+
diff --git a/mpeglib/lib/input/bufferInputStream.h b/mpeglib/lib/input/bufferInputStream.h
new file mode 100644
index 00000000..3bd3e691
--- /dev/null
+++ b/mpeglib/lib/input/bufferInputStream.h
@@ -0,0 +1,88 @@
+/*
+ reads input data
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#ifndef __BUFFERINPUTSTREAM_H
+#define __BUFFERINPUTSTREAM_H
+
+#include "../util/abstract/abs_thread.h"
+
+#include "inputStream.h"
+
+
+class SimpleRingBuffer;
+
+class BufferInputStream : public InputStream {
+
+ SimpleRingBuffer* ringBuffer;
+ int leof;
+ long bytePos;
+ int fillgrade;
+ int lLock;
+ abs_thread_mutex_t writeInMut;
+
+ public:
+ BufferInputStream(int size,int minlinSize,const char* name);
+ ~BufferInputStream();
+
+ int open(const char* name);
+ void close();
+
+ int isOpen();
+
+
+ int eof();
+ void setBlocking(int lblock);
+ int read(char* ptr,int size);
+
+ // reads from a buffer
+ int write(char* ptr,int len,TimeStamp* stamp);
+
+ // this method directy read from another inputstream (faster);
+ int write(InputStream* ptr,int len,TimeStamp* stamp);
+
+ int seek(long bytePos);
+
+
+ long getByteLength();
+ long getBytePosition();
+
+ void setBytePosition(long bytePos);
+ int getFillgrade();
+ int getSize();
+ int getFreeRead();
+ int getFreeSpace();
+
+ void clear();
+
+ // remote read extension
+ // Note you _need_ to call always both methods
+ // readRemote and forwardReadPtr even if bytes==0!!!
+ // (we hold a resizeLock during this operation)
+ int readRemote(char** ptr,int size);
+ void forwardReadPtr(int bytes);
+ void setCanWaitForData(int lBlock);
+
+
+ // this method is only safe to call by the writer in the buffer
+ // a reader never should call this (segfault possible)
+ void resizeBuffer(int changeSize);
+
+ // for pause/play over loopback
+ int getHold();
+ void setHold(int lLock);
+
+ private:
+ void lockBuffer();
+ void unlockBuffer();
+
+};
+#endif
diff --git a/mpeglib/lib/input/cddaInputStream.cpp b/mpeglib/lib/input/cddaInputStream.cpp
new file mode 100644
index 00000000..9bc8f5f2
--- /dev/null
+++ b/mpeglib/lib/input/cddaInputStream.cpp
@@ -0,0 +1,225 @@
+/*
+ cdda input class based on cdparanoia
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef CDDA_PARANOIA
+
+#include <iostream>
+
+using namespace std;
+
+#include "cddaInputStream.h"
+#include "inputDetector.h"
+
+
+void paranoiaCallback(long, int) {
+ //cout << "long:"<<a<<" int:"<<b<<endl;
+}
+
+CDDAInputStream::CDDAInputStream() {
+ drive = NULL;
+ paranoia=NULL;
+ device=NULL;
+ track=1;
+}
+
+
+CDDAInputStream::~CDDAInputStream() {
+ close();
+}
+
+// here we should encdoe the track Nr. as well
+// eg: /dev/cdrom#1
+int CDDAInputStream::getTrackAndDevice(const char* url) {
+ int matches=0;
+ // dest = "cdda:/dev/cdrom/track01.cda"
+ char* noprotoString=InputDetector::removeProtocol(url);
+ // noprotoString="/dev/cdrom/track01.cda"
+ char* filename=InputDetector::getFilename(noprotoString);
+ // filename="track01.cda"
+ char* filenameNoExt=InputDetector::getWithoutExtension(filename);
+ // filenameNoExt="track01"
+ char* dir=InputDetector::removeExtension(noprotoString,filename);
+ // dir="/dev/cdrom/"
+ device=InputDetector::removeSlash(dir);
+ track=1;
+ if (filenameNoExt != NULL) {
+ matches=sscanf(filenameNoExt,"track%02d",&track);
+ }
+ if (matches == 0) {
+ cout << "no trackNumber found using default"<<endl;
+ }
+ cout << "device:"<<device<<" track:"<<track<<endl;
+
+ if (noprotoString != NULL) {
+ delete noprotoString;
+ }
+ if (filename != NULL) {
+ delete filename;
+ }
+ if (filenameNoExt != NULL) {
+ delete filenameNoExt;
+ }
+ if (dir != NULL) {
+ delete dir;
+ }
+ if (device == NULL) {
+ cout << "no device found, using any"<<endl;
+ return false;
+ }
+ return true;
+}
+
+int CDDAInputStream::open(const char* dest) {
+ if (getTrackAndDevice(dest) == true) {
+ drive = cdda_identify(device, CDDA_MESSAGE_PRINTIT, 0);
+ }
+
+ if (drive == NULL) {
+ cout << "cdda_identify failed trying to find a device"<<endl;
+ drive=cdda_find_a_cdrom(CDDA_MESSAGE_PRINTIT, 0);
+ }
+ if (drive == NULL) {
+ cout << "nope. nothing found. give up"<<endl;
+ return false;
+ }
+ cout << "cdda_open -s"<<endl;
+ if (cdda_open(drive) != 0) {
+ cout << "cdda_open(drive) failed"<<endl;
+ close();
+ return false;
+ }
+ cout << "cdda_open -e"<<endl;
+
+ // debug things a bit
+ int trackCount = drive->tracks;
+ for (int i = 1; i <= trackCount; i++) {
+ if (IS_AUDIO(drive, i)) {
+ printf("track%02d.cda\n", i);
+ } else {
+ printf("no audio:%d\n",i);
+ }
+ }
+
+ paranoia = paranoia_init(drive);
+ if (paranoia == NULL) {
+ cout << "paranoia init failed"<<endl;
+ close();
+ return false;
+ }
+
+ firstSector=cdda_track_firstsector(drive, track);
+ lastSector=cdda_track_lastsector(drive, track);
+ currentSector=firstSector;
+ // paranoia && drive != NULL -> initialized!
+
+ int paranoiaLevel = PARANOIA_MODE_FULL ^ PARANOIA_MODE_NEVERSKIP;
+ paranoia_modeset(paranoia, paranoiaLevel);
+ cdda_verbose_set(drive, CDDA_MESSAGE_PRINTIT, CDDA_MESSAGE_PRINTIT);
+ paranoia_seek(paranoia, firstSector, SEEK_SET);
+
+ return true;
+}
+
+
+void CDDAInputStream::close() {
+ if (isOpen() == false) {
+ return;
+ }
+ cdda_close(drive);
+ drive=NULL;
+ if (paranoia != NULL) {
+ paranoia_free(paranoia);
+ paranoia = 0;
+ }
+ if (device != NULL) {
+ delete device;
+ device=NULL;
+ }
+}
+
+
+int CDDAInputStream::isOpen() {
+ return (drive != NULL);
+}
+
+
+int CDDAInputStream::eof() {
+ if (isOpen()==false) {
+ return true;
+ }
+ if (currentSector >= lastSector) {
+ return true;
+ }
+ return false;
+}
+
+
+int CDDAInputStream::read(char* dest,int len) {
+ if (len != 2*CD_FRAMESIZE_RAW) {
+ cout << "len must be 2*CD_FRAMESIZE_RAW"<<endl;
+ exit(0);
+ }
+ int16_t * buf = paranoia_read(paranoia, paranoiaCallback);
+ currentSector++;
+ if (buf == NULL) {
+ cout << "paranoia_read failed"<<endl;
+ close();
+ return 0;
+ }
+ memcpy(dest,buf,sizeof(int16_t)*CD_FRAMESIZE_RAW);
+ return CD_FRAMESIZE_RAW;
+}
+
+
+int CDDAInputStream::seek(long bytePos) {
+ int byteLength=getByteLength();
+ float ratio=(float)bytePos/(float)(byteLength+1);
+ float wantSector=ratio*(float)((lastSector-firstSector));
+ if (isOpen()) {
+ currentSector=(int)wantSector;
+ cout << "paranoia_seek:"<<currentSector<<endl;
+ paranoia_seek(paranoia, currentSector, SEEK_SET);
+ }
+ return true;
+}
+
+void CDDAInputStream::clear() {
+ cout << "direct virtual call CDDAInputStream::clear:"<<endl;
+}
+
+
+long CDDAInputStream::getByteLength() {
+ int sectors=lastSector-firstSector;
+ int bytes=sectors*CD_FRAMESIZE_RAW*sizeof(int16_t);
+ cout << "getByteLength:"<<bytes<<endl;
+ return bytes;
+}
+
+
+long CDDAInputStream::getBytePosition() {
+ int readSectors=currentSector-firstSector;
+ int bytes=readSectors*CD_FRAMESIZE_RAW*sizeof(int16_t);
+ return bytes;
+}
+
+#endif
+//CDDA_PARANOIA
+
+
+
+
+
diff --git a/mpeglib/lib/input/cddaInputStream.h b/mpeglib/lib/input/cddaInputStream.h
new file mode 100644
index 00000000..55989c65
--- /dev/null
+++ b/mpeglib/lib/input/cddaInputStream.h
@@ -0,0 +1,85 @@
+/*
+ cdda input class based on cdparanoia
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __CDDAINPUTSTREAM_H
+#define __CDDAINPUTSTREAM_H
+
+
+#include "inputStream.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef CDDA_PARANOIA
+class CDDAInputStream : public InputStream {
+};
+#else
+
+#include <sys/types.h>
+typedef int16_t size16;
+typedef int32_t size32;
+
+extern "C" {
+#include <cdda_interface.h>
+#include <cdda_paranoia.h>
+void paranoiaCallback(long, int);
+}
+//#define CDDA_INCLUDE
+
+
+class CDDAInputStream : public InputStream {
+
+//#ifdef CDDA_INCLUDE
+ cdrom_paranoia * paranoia;
+ struct cdrom_drive * drive;
+//#else
+// void * drive;
+// void * paranoia;
+//#endif
+
+
+ char* device;
+ int track;
+
+ int firstSector;
+ int lastSector;
+ int currentSector;
+
+ public:
+ CDDAInputStream();
+ ~CDDAInputStream();
+
+ int open(const char* dest);
+ void close();
+ int isOpen();
+
+ int eof();
+ int read(char* ptr,int size);
+ int seek(long bytePos);
+ // clears possible input buffers
+ // (called by the decoderPlugin after a resyncCommit)
+ void clear();
+
+ long getByteLength();
+ long getBytePosition();
+ private:
+ int getTrackAndDevice(const char* url);
+
+};
+#endif
+//CDDA_PARANOIA
+
+#endif
diff --git a/mpeglib/lib/input/cdigrap.cpp b/mpeglib/lib/input/cdigrap.cpp
new file mode 100644
index 00000000..37d9de0e
--- /dev/null
+++ b/mpeglib/lib/input/cdigrap.cpp
@@ -0,0 +1,100 @@
+/**
+ graps cdis
+
+ Compile with
+
+ g++ -o cdigrap cdigrap.cpp -lmpeg
+
+*/
+#ifdef CONFIG_H
+#include "config.h"
+#endif
+#include "inputPlugin.h"
+
+#if defined(HAVE_GETOPT_H)
+#include <getopt.h>
+#endif
+
+void usage() {
+ cout << "cdigrab grabs video cds"<<endl;
+ cout << "Usage : cdigrab [s:b:f:h] cdi:/device"<<endl;
+ cout << endl;
+ cout << "-s : bytes start a positions <bytes>"<<endl;
+ cout << "-b : set block size (default: 32768)"<<endl;
+ cout << "-f : set filename (default: a.cdi)"<<endl;
+ cout << "-h : help"<<endl;
+ cout << "THIS SOFTWARE COMES WITH ABSOLUTELY NO WARRANTY! " \
+ << "USE AT YOUR OWN RISK!"<<endl;
+ cout << endl;
+}
+
+
+int main(int argn,char** args) {
+
+
+ if (argn <= 1) {
+ usage();
+ exit(0);
+ }
+ long startBytes=0;
+ int len=32768;
+ char *fname = strdup("a.cdi");
+ int c;
+
+ while(1) {
+ c = getopt (argn, args, "s:b:f:h");
+ if (c == -1) break;
+ switch(c) {
+ case 'h': {
+ usage();
+ exit(0);
+ }
+ case 's': {
+ startBytes=atoi(optarg);
+ break;
+ }
+ case 'b': {
+ len=atoi(optarg);
+ break;
+ }
+ case 'f': {
+ fname = strdup(optarg);
+ break;
+ }
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ usage();
+ exit(-1);
+ }
+ }
+ if (optind >= argn ) {
+ usage();
+ exit(-1);
+ }
+
+ InputStream* in=InputPlugin::createInputStream(args[optind]);
+
+ in->open(args[optind]);
+
+ if (startBytes != 0) {
+ cout << "seeking to :"<<startBytes<<endl;
+ in->seek(startBytes);
+ }
+ char* buffer=new char[len];
+ int cnt=0;
+ FILE* f=fopen(fname,"a+");
+ while(1) {
+ if (in->eof() == true) {
+ cout << "******* plugin->getStreamState() EOF"<<endl;
+ break;
+ }
+ in->read(buffer,len);
+ fwrite(buffer,len,1,f);
+ cnt++;
+ cout << "grapped:"<<cnt*len<<endl;
+ }
+ fclose(f);
+}
+
+
+
diff --git a/mpeglib/lib/input/cdromAccess.cpp b/mpeglib/lib/input/cdromAccess.cpp
new file mode 100644
index 00000000..98d49779
--- /dev/null
+++ b/mpeglib/lib/input/cdromAccess.cpp
@@ -0,0 +1,54 @@
+/*
+ * system dependent wrapper for access to cdrom
+ * Copyright (C) 1999 Martin Vogt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation.
+ *
+ * For more information look at the file COPYRIGHT in this package
+ *
+ * $Id$
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+ #include <config.h>
+#endif
+
+
+
+#ifdef OS_AIX
+ #include "cdromAccess_Empty.cpp"
+#endif
+
+#ifdef OS_Linux
+ #include <sys/types.h>
+ #include "cdromAccess_Linux.cpp"
+#endif
+
+#ifdef OS_BSD
+ #include "cdromAccess_Empty.cpp"
+#endif
+
+#if defined(OS_IRIX) || defined(OS_IRIX64)
+ #include "cdromAccess_Empty.cpp"
+#endif
+
+#ifdef OS_HPUX
+ #include "cdromAccess_Empty.cpp"
+#endif
+
+#ifdef OS_SunOS
+ #include "cdromAccess_Empty.cpp"
+#endif
+
+#ifdef __BEOS__
+ #include "cdromAccess_Empty.cpp"
+#endif
+
+#ifdef WIN32
+ #include "cdromAccess_Empty.cpp"
+#endif
+
+
diff --git a/mpeglib/lib/input/cdromAccess_Empty.cpp b/mpeglib/lib/input/cdromAccess_Empty.cpp
new file mode 100644
index 00000000..63da4b69
--- /dev/null
+++ b/mpeglib/lib/input/cdromAccess_Empty.cpp
@@ -0,0 +1,47 @@
+/*
+ system dependent wrapper for access to cdrom (no system)
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "cdromToc.h"
+#include "cdromRawAccess.h"
+
+#include <iostream>
+using namespace std;
+
+
+int CDRomToc::getStartEnd(FILE* file,int& startToc,int& endToc) {
+ cout << "CDRomToc::getStartEnd not implemented on your system"<<endl;
+ return false;
+}
+
+
+int CDRomToc::readToc(FILE* file,int num,int& min,int& sec, int& frame) {
+ cout << "CDRomToc::readToc not implemented on your system"<<endl;
+ return false;
+}
+
+
+int CDRomToc::readLeadOut(FILE* file,int& min,int& sec, int& frame) {
+ cout << "CDRomToc::reatLeadOut not implemented on your system"<<endl;
+ return false;
+}
+
+
+
+int CDRomRawAccess::readDirect(int minute,int second, int frame) {
+
+
+ cout << "no CDRomRawAccess::read implemented for your system"<<endl;
+ lData=false;
+ return true;
+}
+
diff --git a/mpeglib/lib/input/cdromAccess_Linux.cpp b/mpeglib/lib/input/cdromAccess_Linux.cpp
new file mode 100644
index 00000000..a3bde622
--- /dev/null
+++ b/mpeglib/lib/input/cdromAccess_Linux.cpp
@@ -0,0 +1,124 @@
+/*
+ system dependent wrapper for access to cdrom (Linux)
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#ifdef __STRICT_ANSI__
+#undef __STRICT_ANSI__
+#define _ANSI_WAS_HERE_
+#endif
+#include <linux/types.h>
+#include <linux/cdrom.h>
+#ifdef _ANSI_WAS_HERE_
+#define __STRICT_ANSI__
+#endif
+#include <sys/ioctl.h>
+
+#include "cdromToc.h"
+#include "cdromRawAccess.h"
+
+#include <iostream>
+
+using namespace std;
+
+/**
+ here you find an example how to port the access method
+ to your system.
+*/
+
+
+int CDRomToc::getStartEnd(FILE* file,int& startToc,int& endToc) {
+ struct cdrom_tochdr tochdr;
+ int fd=fileno(file);
+ if (ioctl(fd, CDROMREADTOCHDR, &tochdr) == -1) {
+ perror("ioctl cdromreadtochdr");
+ return false;
+ }
+
+ startToc=tochdr.cdth_trk0;
+ endToc=tochdr.cdth_trk1;
+ return true;
+}
+
+
+int CDRomToc::readToc(FILE* file,int num,int& min,int& sec, int& frame) {
+ struct cdrom_tocentry tocent;
+ int fd=fileno(file);
+ tocent.cdte_track = num;
+ tocent.cdte_format = CDROM_MSF;
+ if (ioctl(fd, CDROMREADTOCENTRY, &tocent) == -1 ) {
+ perror("ioctl cdromreadtocentry");
+ return false;
+ }
+ min=tocent.cdte_addr.msf.minute;
+ sec=tocent.cdte_addr.msf.second;
+ frame=tocent.cdte_addr.msf.frame;
+ return true;
+}
+
+
+int CDRomToc::readLeadOut(FILE* file,int& min,int& sec, int& frame) {
+ struct cdrom_tocentry tocent;
+ int fd=fileno(file);
+ tocent.cdte_track = CDROM_LEADOUT;
+ tocent.cdte_format = CDROM_MSF;
+ if (ioctl(fd, CDROMREADTOCENTRY, &tocent) == -1 ) {
+ perror("ioctl cdromreadLeadoutn");
+ return false;
+ }
+ min=tocent.cdte_addr.msf.minute;
+ sec=tocent.cdte_addr.msf.second;
+ frame=tocent.cdte_addr.msf.frame;
+ return true;
+}
+
+
+
+int CDRomRawAccess::readDirect(int minute,int second, int frame) {
+
+ // this comes from smpeg
+ // smpeg is an mpeg I player from lokigames www.lokigames.com
+
+ struct cdrom_msf *msf;
+ int fd=fileno(cdfile);
+
+ msf = (struct cdrom_msf*) data;
+ msf->cdmsf_min0 = minute;
+ msf->cdmsf_sec0 = second;
+ msf->cdmsf_frame0 = frame;
+ if (ioctl(fd, CDROMREADMODE2, msf) == -1) {
+ perror("ioctl cdromreadmode2");
+ cout << "min:"<<minute
+ << " sec:"<<second
+ << " frame:"<<frame<<endl;
+ return false;
+ } else {
+ //cout << "read success ****************"<<endl;
+ }
+
+ char* subheader=data+sizeof(int);
+
+
+ if ((subheader[1]==1) &&
+ (((subheader[2]==0x62) &&
+ (subheader[3]==0x0f)) || ((subheader[2]==0x64) &&
+ (subheader[3]==0x7f)))) {
+ lData=true;
+ dataStart=sizeof(int)+4;
+ } else {
+ lData=false;
+ }
+
+ len=2324;
+
+
+ return true;
+}
+
diff --git a/mpeglib/lib/input/cdromInputStream.cpp b/mpeglib/lib/input/cdromInputStream.cpp
new file mode 100644
index 00000000..1cf3f905
--- /dev/null
+++ b/mpeglib/lib/input/cdromInputStream.cpp
@@ -0,0 +1,309 @@
+/*
+ reads input data from cdrom
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "cdromInputStream.h"
+#include "cdromRawAccess.h"
+#include "cdromToc.h"
+#include <string.h>
+#include "inputDetector.h"
+
+#include <iostream>
+
+using namespace std;
+
+CDRomInputStream::CDRomInputStream() {
+ cdRomToc=new CDRomToc();
+ cdRomRawAccess=new CDRomRawAccess(cdRomToc);
+
+ buflen=0;
+ bufCurrent=NULL;
+
+ currentMinute=0;
+ currentSecond=2;
+ currentFrame=0;
+ byteCounter=0;
+}
+
+
+CDRomInputStream::~CDRomInputStream() {
+ delete cdRomRawAccess;
+ delete cdRomToc;
+}
+
+
+int CDRomInputStream::readCurrent() {
+ int ok=cdRomRawAccess->read(currentMinute,currentSecond,currentFrame);
+ if (ok==false) {
+ if (cdRomRawAccess->eof() == false) {
+ int pos=cdRomToc->getNextTocEntryPos(currentMinute,
+ currentSecond,
+ currentFrame);
+
+ // now try to read a few sectors
+ int cnt=0;
+ int back=false;
+
+ while(1) {
+ // jump forward
+ int i;
+ for(i=0;i<_CDROM_FRAMES-currentFrame;i++) {
+ next_sector();
+ }
+ cout << "trying next ..."<<endl;
+ ok=cdRomRawAccess->read(currentMinute,currentSecond,currentFrame);
+ if (ok) {
+ bufCurrent=cdRomRawAccess->getBufferStart();
+ buflen=cdRomRawAccess->getBufferLen();
+ return true;
+ }
+ cnt++;
+ if (cnt > 100) {
+ break;
+ }
+ }
+ cout << "last possible jump"<<endl;
+ if (pos > 1) {
+ TocEntry* tocEntry=cdRomToc->getTocEntry(pos-1);
+ currentMinute=tocEntry->minute;
+ currentSecond=tocEntry->second;
+ currentFrame=tocEntry->frame;
+ back=cdRomRawAccess->read(currentMinute,currentSecond,currentFrame);
+ if (back) {
+ bufCurrent=cdRomRawAccess->getBufferStart();
+ buflen=cdRomRawAccess->getBufferLen();
+ }
+ }
+ return back;
+ }
+ return false;
+ }
+ bufCurrent=cdRomRawAccess->getBufferStart();
+ buflen=cdRomRawAccess->getBufferLen();
+ return true;
+}
+
+
+int CDRomInputStream::getByteDirect() {
+ int back;
+ if (buflen==0) {
+ fillBuffer();
+ }
+ if (buflen==0){
+ return EOF;
+ }
+ back=*bufCurrent;
+ buflen--;
+ bufCurrent++;
+ byteCounter++;
+ return back;
+}
+
+
+int CDRomInputStream::read(char* ptr,int size) {
+ char* dest=(char*)ptr;
+ int bytesRead=0;
+ int doRead=size;
+ int canRead;
+
+ while(eof() == false) {
+ if (buflen == 0) {
+ if (fillBuffer() == false) {
+ return 0;
+ }
+ continue;
+ }
+ canRead=buflen;
+ if (doRead < canRead) {
+ canRead=doRead;
+ }
+ memcpy((void*)dest,(void*)bufCurrent,canRead);
+ buflen-=canRead;
+ bufCurrent+=canRead;
+ bytesRead+=canRead;
+ dest+=canRead;
+ doRead-=canRead;
+ if (doRead == 0) {
+ byteCounter+=bytesRead;
+ return bytesRead;
+ }
+ }
+ return 0;
+}
+
+int CDRomInputStream::eof() {
+ return cdRomRawAccess->eof();
+}
+
+
+
+
+long CDRomInputStream::getBytePosition() {
+ return byteCounter;
+}
+
+
+void CDRomInputStream::print() {
+}
+
+
+void CDRomInputStream::next_sector() {
+ currentFrame++;
+ if (currentFrame>=_CDROM_FRAMES) {
+ currentFrame = 0;
+ currentSecond++;
+ if (currentSecond>=_CDROM_SECS) {
+ currentSecond = 0;
+ currentMinute++;
+ }
+ }
+}
+
+int CDRomInputStream::open(const char* file) {
+ cout << "CDRomInputStream::open:"<<file<<endl;
+ char* noExtension=InputDetector::getWithoutExtension(file);
+ cout << "CDRomInputStream::noExt:"<<noExtension<<endl;
+ if (noExtension == NULL) {
+ return false;
+ }
+ cdRomToc->open(noExtension);
+ cdRomRawAccess->open(noExtension);
+ if (isOpen()==false) {
+ return false;
+ }
+ setUrl(noExtension);
+ int entries=cdRomToc->getTocEntries();
+ cdRomToc->print();
+ if (entries == 1) {
+ cerr << "only lead out"<<endl;
+ }
+
+ // cdRomRawAccess->insertTocEntry(0,2,0);
+ //cdRomToc->insertTocEntry(1,13,5);
+
+ TocEntry* tocEntry=cdRomToc->getTocEntry(0);
+ currentMinute=tocEntry->minute;
+ currentSecond=tocEntry->second;
+ currentFrame=tocEntry->frame;
+ delete noExtension;
+
+ return readCurrent();
+}
+
+
+void CDRomInputStream::close() {
+ cdRomRawAccess->close();
+ byteCounter=0;
+ setUrl(NULL);
+}
+
+
+int CDRomInputStream::isOpen() {
+ return cdRomRawAccess->isOpen();
+}
+
+
+
+
+long CDRomInputStream::getBytePos(int min,int sec) {
+ long back;
+ // 2324 is the size of a cdfram
+ back=sec*_CDROM_FRAMES*2324;
+ back=back+min*60*_CDROM_FRAMES*2324;
+ cout << "CDRomInputStream::getByteLength"<<back<<endl;
+ return back;
+}
+
+long CDRomInputStream::getByteLength() {
+ // we get the length out of the toc and then multiply like hell
+ long totalSecs=cdRomToc->getEndSecond();
+ long min=totalSecs/60;
+ long sec=totalSecs-60*min;
+
+ long back=getBytePos(min,sec);
+
+ return back;
+}
+
+
+int CDRomInputStream::seek(long posInBytes) {
+ int entries=cdRomToc->getTocEntries();
+
+ TocEntry* firstEntry;
+ if (entries == 0) {
+ return false;
+ }
+ if (posInBytes < 0) {
+ return false;
+ }
+ firstEntry=cdRomToc->getTocEntry(0);
+
+ long startByte=getBytePos(firstEntry->minute,firstEntry->second+1);
+
+ posInBytes=posInBytes+startByte;
+
+
+ float fmin=(float)posInBytes/(float)(60*_CDROM_FRAMES*2324);
+ int min=(int)fmin;
+
+
+ long sec=posInBytes-min*(60*_CDROM_FRAMES*2324);
+ sec=sec/(_CDROM_FRAMES*2324);
+
+ byteCounter=posInBytes;
+ if (cdRomRawAccess->read(min,sec,0)==false) {
+ return false;
+ }
+ setTimePos(min*60+sec);
+
+ return true;
+}
+
+
+int CDRomInputStream::setTimePos(int second) {
+ currentFrame=0;
+ currentMinute=second/60;
+ currentSecond=second%60;
+
+ return fillBuffer();
+}
+
+
+
+
+
+int CDRomInputStream::fillBuffer() {
+ int maxNoData=30;
+ int cnt=0;
+ if (buflen==0) {
+ while (cnt < maxNoData) {
+ next_sector();
+ if (readCurrent() == false) {
+ return false;
+ }
+ if (cdRomRawAccess->isData() == false) {
+ // cerr << "no data"<<endl;
+ cnt++;
+ } else {
+ return true;
+ }
+ }
+ return false;
+ }
+ return true;
+}
+
+
+
+
+
+
diff --git a/mpeglib/lib/input/cdromInputStream.h b/mpeglib/lib/input/cdromInputStream.h
new file mode 100644
index 00000000..cda18efe
--- /dev/null
+++ b/mpeglib/lib/input/cdromInputStream.h
@@ -0,0 +1,88 @@
+/*
+ reads input data from cdrom
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __VIDEOCDINPUTSTREAM_H
+#define __VIDEOCDINPUTSTREAM_H
+
+
+
+#include "inputStream.h"
+
+
+
+#define _CDROM_FRAMES 75
+#define _CDROM_SECS 60
+
+
+/**
+ This is necessary because CD-I or VCD disks are not correctly
+ mounted by the linux kernel.
+ Windows seems to do it correct (sometimes, the other time it nearly crash)
+ During my test I found out that CD-I and VCD seems to be different.
+ One works with the CD-I loader the other with the VCD loader.
+
+ This class tries to find out if its a CD-I / VCD stream.
+ Then you can set the interface and load from this interface.
+
+ This class depends on the reader routines from xreadcdi/ xreadvcd,
+ Author: Ales Makarov <xmakarov@sun.felk.cvut.cz>
+ FTP : ftp://mca.sh.cvut.cz/pub/readvcd/
+
+ The code is used in a few other packages, in which I looked as well.
+
+*/
+
+class CDRomRawAccess;
+class CDRomToc;
+
+class CDRomInputStream : public InputStream{
+
+ CDRomRawAccess* cdRomRawAccess;
+ CDRomToc* cdRomToc;
+
+ int buflen;
+ char* bufCurrent;
+
+ int currentFrame;
+ int currentMinute;
+ int currentSecond;
+ long byteCounter;
+
+ public:
+ CDRomInputStream();
+ ~CDRomInputStream();
+
+ int open(const char* dest);
+ void close();
+ int isOpen();
+
+ int eof();
+ int read(char* ptr,int size);
+ int seek(long bytePos);
+
+ long getByteLength();
+ long getBytePosition();
+
+ void print();
+
+ private:
+ long getBytePos(int min,int sec);
+
+ int setTimePos(int posInTime);
+ int getByteDirect();
+ void next_sector();
+ int readCurrent();
+ int fillBuffer();
+};
+#endif
diff --git a/mpeglib/lib/input/cdromRawAccess.cpp b/mpeglib/lib/input/cdromRawAccess.cpp
new file mode 100644
index 00000000..b0df4747
--- /dev/null
+++ b/mpeglib/lib/input/cdromRawAccess.cpp
@@ -0,0 +1,113 @@
+/*
+ reads raw input data from cdrom (system dependent)
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include <iostream>
+
+#include "cdromRawAccess.h"
+
+using namespace std;
+
+CDRomRawAccess::CDRomRawAccess(CDRomToc* cdromToc) {
+ cdfile=NULL;
+ lOpen=false;
+ leof=true;
+ this->cdromToc=cdromToc;
+ lData=false;
+ dataStart=0;
+ len=0;
+}
+
+
+CDRomRawAccess::~CDRomRawAccess() {
+ close();
+}
+
+
+
+char* CDRomRawAccess::getBufferStart() {
+ return (char*)(data+dataStart);
+}
+
+int CDRomRawAccess::getBufferLen() {
+ if (eof()) {
+ return 0;
+ }
+ return len;
+}
+
+int CDRomRawAccess::open(const char* filename) {
+ if (isOpen()) {
+ close();
+ }
+ if (filename==NULL) {
+ filename=(char*)"/dev/cdrom";
+ }
+ if (strlen(filename) <= 1) {
+ filename="/dev/cdrom";
+ }
+ char* openfile=strchr(filename,'/');
+ cout << "openfile:"<<openfile<<endl;
+ cdfile=fopen(openfile, "rb");
+ lOpen=false;
+
+ if (cdfile == NULL) {
+ perror("open CDRomRawAccess");
+ } else {
+ lOpen=true;
+ leof=false;
+ }
+ return lOpen;
+}
+
+
+int CDRomRawAccess::eof() {
+ return leof;
+}
+
+
+int CDRomRawAccess::read(int minute,int second, int frame) {
+ if (isOpen()==false) {
+ cerr << "CDRomRawAccess not open"<<endl;
+ return false;
+ }
+
+ int lInRange=cdromToc->isInRange(minute,second,frame);
+ if (lInRange == false) {
+ if (minute*60+second+1 > cdromToc->getEndSecond()) {
+ leof=true;
+ }
+ return false;
+ }
+ return readDirect(minute,second,frame);
+}
+
+
+void CDRomRawAccess::close() {
+ if (isOpen()) {
+ fclose(cdfile);
+ lOpen=false;
+ leof=true;
+ }
+}
+
+int CDRomRawAccess::isData() {
+ return lData;
+}
+
+int CDRomRawAccess::isOpen() {
+ return lOpen;
+}
+
+
+
+
diff --git a/mpeglib/lib/input/cdromRawAccess.h b/mpeglib/lib/input/cdromRawAccess.h
new file mode 100644
index 00000000..d089a85b
--- /dev/null
+++ b/mpeglib/lib/input/cdromRawAccess.h
@@ -0,0 +1,67 @@
+/*
+ reads raw input data from cdrom (system dependent)
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __CDROMRAWACCESS_H
+#define __CDROMRAWACCESS_H
+
+#include "cdromToc.h"
+#include "inputStream.h"
+
+
+/**
+ Here we have a system wrapper for raw cdrom access.
+ Currently this is tested on Linux.
+*/
+
+
+
+
+
+class CDRomRawAccess {
+
+ CDRomToc* cdromToc;
+ char data[2352];
+ int dataStart;
+ int len;
+ int lData;
+
+ public:
+ CDRomRawAccess(CDRomToc* cdromToc);
+ virtual ~CDRomRawAccess();
+
+ // overload this for new Systems
+ virtual int readDirect(int minute,int second, int frame);
+
+ // wrapper for readDirect
+ int read(int minute,int second, int frame);
+
+ char* getBufferStart();
+ int getBufferLen();
+
+ int open(const char* filename);
+ int eof();
+ void close();
+ int isOpen();
+
+ int isData();
+
+ private:
+ FILE* cdfile;
+ int lOpen;
+ int leof;
+ int buflen;
+};
+
+#endif
+
+
diff --git a/mpeglib/lib/input/cdromToc.cpp b/mpeglib/lib/input/cdromToc.cpp
new file mode 100644
index 00000000..919ecdc8
--- /dev/null
+++ b/mpeglib/lib/input/cdromToc.cpp
@@ -0,0 +1,234 @@
+/*
+ reads toc from cdrom (system dependent)
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "cdromToc.h"
+
+#include <iostream>
+
+using namespace std;
+
+CDRomToc::CDRomToc() {
+ maxEntries=0;
+}
+
+
+CDRomToc::~CDRomToc() {
+}
+
+
+void CDRomToc::insertTocEntry(int minute,int second,int frame) {
+ int i;
+ int j;
+ if (isElement(minute,second,frame)) {
+ return;
+ }
+
+ i=getNextTocEntryPos(minute,second,frame);
+
+ // now shift from i to end
+ if (maxEntries == 100) {
+ cerr << "maximum of toc entries reached"<<endl;
+ exit(0);
+ }
+ for (j=maxEntries;j>i;j--) {
+ tocEntries[j].minute=tocEntries[j-1].minute;
+ tocEntries[j].second=tocEntries[j-1].second;
+ tocEntries[j].frame=tocEntries[j-1].frame;
+ }
+ maxEntries++;
+
+ tocEntries[i].minute=minute;
+ tocEntries[i].second=second;
+ tocEntries[i].frame=frame;
+ calculateRange();
+}
+
+
+int CDRomToc::getNextTocEntryPos(int minute,int second,int frame) {
+ int i;
+ if (maxEntries == 0) {
+ return 0;
+ }
+
+ for (i=0;i<maxEntries;i++) {
+ if (tocEntries[i].minute <= minute) {
+ continue;
+ } else {
+ break;
+ }
+ if (tocEntries[i].second <= second) {
+ continue;
+ } else {
+ break;
+ }
+ if (tocEntries[i].frame <= frame) {
+ continue;
+ }
+ break;
+ }
+ return i;
+}
+
+
+
+int CDRomToc::isElement(int minute,int second,int frame) {
+ int i;
+
+ for (i=0;i<maxEntries;i++) {
+ if (tocEntries[i].minute == minute) {
+ if (tocEntries[i].second == second) {
+ if (tocEntries[i].frame == frame) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+
+
+
+
+
+int CDRomToc::getTocEntries() {
+ return maxEntries;
+}
+
+
+TocEntry* CDRomToc::getTocEntry(int entry) {
+ return &(tocEntries[entry]);
+}
+
+
+void CDRomToc::print() {
+ int i;
+ cerr << "******* printing TOC [START]"<<endl;
+ for(i=0;i<maxEntries;i++) {
+ cerr << "i:"<<i
+ <<" M:"<<tocEntries[i].minute
+ <<" S:"<<tocEntries[i].second
+ <<" F:"<<tocEntries[i].frame<<endl;
+ }
+ cerr << "******* printing TOC [END}"<<endl;
+}
+
+
+int CDRomToc::isInRange(int minute,int second,int) {
+ long val=minute*60+second;
+ if (val < startByte) {
+ return false;
+ }
+ if (val > endByte) {
+ return false;
+ }
+ return true;
+}
+
+int CDRomToc::calculateRange() {
+
+
+ if (maxEntries < 2) {
+ cout << "no two elemts in toc"<<endl;
+ return false;
+ }
+ startByte=tocEntries[0].minute*60*+tocEntries[0].second;
+
+ // do a safty end because of the kernel bug
+ int minute=tocEntries[maxEntries-1].minute;
+ int second=tocEntries[maxEntries-1].second-20;
+ if (second < 0) {
+ minute--;
+ second=60+second;
+ }
+ if (minute < 0) {
+ endByte=0;
+ return true;
+ }
+
+ endByte=minute*60+second;
+
+ return true;
+
+}
+
+
+int CDRomToc::getEndSecond() {
+ return endByte;
+}
+
+
+
+
+int CDRomToc::open(const char* openfile) {
+ int i;
+ int pos=0;
+ maxEntries=0;
+ const char* filename=strchr(openfile,'/');
+ FILE* file =fopen(filename, "rb");
+ if (file == NULL) {
+ perror("open");
+ return false;
+ }
+ cout << "reading toc on:"<<filename<<" openfile:"<<openfile<<endl;
+
+ int startToc=0;
+ int endToc=0;
+
+ if (getStartEnd(file,startToc,endToc) == false) {
+ cout << "getStartEnd in CDRomToc failed"<<endl;
+ fclose(file);
+ return false;
+ }
+ cout << "startToc:"<<startToc<<" endToc:"<<endToc<<endl;
+ cout << "reading toc -2"<<endl;
+ /* read individual tracks */
+ int min;
+ int sec;
+ int frame;
+
+ for (i=startToc; i<=endToc; i++) {
+ int min;
+ int sec;
+ int frame;
+
+ if (readToc(file,i,min,sec,frame) == false) {
+ cout << "error in CDRomToc::readToc"<<endl;
+ fclose(file);
+ return false;
+ }
+ cout << "min:"<<min<<endl;
+ cout << "sec:"<<sec<<endl;
+ cout << "frame:"<<frame<<endl;
+
+ insertTocEntry(min,sec,frame);
+ pos++;
+ }
+
+
+ /* read the lead-out track */
+ if (readLeadOut(file,min,sec,frame) == false) {
+ cout << "error in CDRomToc::reatLeadOut"<<endl;
+ return false;
+ }
+ pos++;
+ insertTocEntry(min,sec,frame);
+
+ maxEntries=pos;
+
+ fclose(file);
+
+ return true;
+}
+
diff --git a/mpeglib/lib/input/cdromToc.h b/mpeglib/lib/input/cdromToc.h
new file mode 100644
index 00000000..20b60e10
--- /dev/null
+++ b/mpeglib/lib/input/cdromToc.h
@@ -0,0 +1,65 @@
+/*
+ reads toc from cdrom (system dependent)
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __CDROMTOC_H
+#define __CDROMTOC_H
+
+extern "C" {
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+}
+
+struct TocEntry {
+ int minute;
+ int second;
+ int frame;
+};
+
+
+class CDRomToc {
+
+ TocEntry tocEntries[100];
+ int maxEntries;
+ int startByte;
+ int endByte;
+
+ public:
+ CDRomToc();
+ virtual ~CDRomToc();
+ int open(const char* device);
+ int getTocEntries();
+ TocEntry* getTocEntry(int entry);
+ void insertTocEntry(int minute,int second,int frame);
+ int getNextTocEntryPos(int minute,int second,int frame);
+ int isInRange(int minute,int second,int frame);
+ int isElement(int minute,int second,int frame);
+
+ int getEndSecond();
+
+ void print();
+
+ private:
+ // platform specific calls.
+ int getStartEnd(FILE* file,int& start, int& end);
+ int readToc(FILE* file,int num,int& min,int& sec, int& frame);
+ int readLeadOut(FILE* file,int& min,int& sec, int& frame);
+
+
+
+
+ int calculateRange();
+
+};
+
+#endif
diff --git a/mpeglib/lib/input/fileAccessWrapper.cpp b/mpeglib/lib/input/fileAccessWrapper.cpp
new file mode 100644
index 00000000..246807a8
--- /dev/null
+++ b/mpeglib/lib/input/fileAccessWrapper.cpp
@@ -0,0 +1,65 @@
+/*
+ wraps an inputStream for the splayFileBuffer.
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "fileAccessWrapper.h"
+
+#include <iostream>
+
+using namespace std;
+
+FileAccessWrapper::FileAccessWrapper(InputStream* input) {
+ this->input=input;
+}
+
+
+FileAccessWrapper::~FileAccessWrapper() {
+
+}
+
+
+int FileAccessWrapper::open(const char*) {
+ cout << "FileAccessWrapper open not implemented"<<endl;
+ exit(0);
+}
+
+
+void FileAccessWrapper::close() {
+ cout << "FileAccessWrapper close not implemented"<<endl;
+ exit(0);
+}
+
+
+int FileAccessWrapper::read(char* dest,int len) {
+ return input->read(dest,len);
+}
+
+int FileAccessWrapper::eof() {
+ return input->eof();
+}
+
+
+int FileAccessWrapper::seek(long pos) {
+ return input->seek(pos);
+}
+
+
+long FileAccessWrapper::getBytePosition() {
+ return input->getBytePosition();
+}
+
+
+long FileAccessWrapper::getByteLength() {
+ return input->getByteLength();
+}
+
diff --git a/mpeglib/lib/input/fileAccessWrapper.h b/mpeglib/lib/input/fileAccessWrapper.h
new file mode 100644
index 00000000..d7882abb
--- /dev/null
+++ b/mpeglib/lib/input/fileAccessWrapper.h
@@ -0,0 +1,41 @@
+/*
+ wraps an inputStream for the splayFileBuffer.
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __FILEACCESSWRAPPER_H
+#define __FILEACCESSWRAPPER_H
+
+
+#include "../util/file/fileAccess.h"
+#include "inputStream.h"
+
+
+class FileAccessWrapper : public FileAccess {
+
+ InputStream* input;
+
+ public:
+ FileAccessWrapper(InputStream* input);
+ virtual ~FileAccessWrapper();
+
+ int open(const char* file);
+ void close();
+ int read(char* dest,int len);
+ int eof();
+ int seek(long pos);
+ long getBytePosition();
+ long getByteLength();
+
+};
+
+
+#endif
diff --git a/mpeglib/lib/input/fileInputStream.cpp b/mpeglib/lib/input/fileInputStream.cpp
new file mode 100644
index 00000000..77c31ed6
--- /dev/null
+++ b/mpeglib/lib/input/fileInputStream.cpp
@@ -0,0 +1,148 @@
+/*
+ reads input data
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "fileInputStream.h"
+
+#include <iostream>
+
+using namespace std;
+
+FileInputStream::FileInputStream() {
+ file=NULL;
+ lopen=false;
+ fileLen=0;
+}
+
+
+FileInputStream::~FileInputStream() {
+ close();
+}
+
+
+int FileInputStream::open(const char* dest) {
+
+ close();
+ if (dest == NULL) {
+ return false;
+ }
+ setUrl(dest);
+ if (strlen(dest) == 1) {
+ if (strncmp(dest,"-",1)==0) {
+ file=::fdopen(0,"rb");
+ }
+ }
+ // load out of current dir if no full path is given
+ if (file == NULL) {
+ file=fopen(dest,"rb");
+ }
+ fileLen=0;
+ if (file == NULL) {
+ cout <<"cannot open file:"<< dest<<endl;
+
+ } else {
+ lopen=true;
+ struct stat fileStat;
+ stat(dest,&fileStat);
+ fileLen=(long)fileStat.st_size;
+ }
+ int back=(file!=NULL);
+ return back;
+}
+
+
+void FileInputStream::close() {
+ if (isOpen()) {
+ ::fclose(file);
+ file=NULL;
+ lopen=false;
+ }
+}
+
+
+int FileInputStream::isOpen() {
+ return lopen;
+}
+
+
+int FileInputStream::eof() {
+ if (isOpen()==false){
+ return true;
+ }
+ int back=true;
+ if (file != NULL) {
+ back=feof(file);
+ }
+
+ return back;
+}
+
+
+int FileInputStream::read(char* ptr,int size) {
+ int bytesRead=-1;
+ if (isOpen()) {
+ if (size <= 0) {
+ cout << "size is <= 0!"<<endl;
+ return 0;
+ }
+ if (file != NULL) {
+ bytesRead=fread(ptr,1,size,file);
+ }
+ } else {
+ cerr << "read on not open file want:"<<size<<endl;
+ return 0;
+ }
+ return bytesRead;
+}
+
+
+int FileInputStream::seek(long posInBytes) {
+ int back=true;
+ if (isOpen()==false) {
+ return false;
+ }
+ long pos=-1;
+ if (file != NULL) {
+ pos=fseek(file,posInBytes,SEEK_SET);
+ }
+
+ if (pos < 0) {
+ cout <<"seek error in FileInputStream::seek"<<endl;
+ back=false;
+ }
+ return back;
+}
+
+
+long FileInputStream::getByteLength() {
+ return fileLen;
+}
+
+
+long FileInputStream::getBytePosition() {
+ int back=0;
+ if (isOpen()) {
+ if (file != NULL) {
+ back=ftell(file);
+ }
+ }
+ return back;
+}
+
+
+
+void FileInputStream::print() {
+ printf("pos in file:%8x\n",(int)getBytePosition());
+}
+
+
diff --git a/mpeglib/lib/input/fileInputStream.h b/mpeglib/lib/input/fileInputStream.h
new file mode 100644
index 00000000..ea421a55
--- /dev/null
+++ b/mpeglib/lib/input/fileInputStream.h
@@ -0,0 +1,46 @@
+/*
+ reads input data
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __FILEINPUTSTREAM_H
+#define __FILEINPUTSTREAM_H
+
+
+
+#include "inputStream.h"
+
+
+
+class FileInputStream : public InputStream{
+
+ FILE* file;
+ int lopen;
+ long fileLen;
+ public:
+ FileInputStream();
+ ~FileInputStream();
+
+ int open(const char* dest);
+ void close();
+ int isOpen();
+
+ int eof();
+ int read(char* ptr,int size);
+ int seek(long bytePos);
+
+ long getByteLength();
+ long getBytePosition();
+
+ void print();
+
+};
+#endif
diff --git a/mpeglib/lib/input/httpInputStream.cpp b/mpeglib/lib/input/httpInputStream.cpp
new file mode 100644
index 00000000..df2f09c4
--- /dev/null
+++ b/mpeglib/lib/input/httpInputStream.cpp
@@ -0,0 +1,327 @@
+/*
+ reads input data
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "httpInputStream.h"
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+#include <iostream>
+
+using namespace std;
+
+static const char *httpstr="http://";
+
+
+
+static char *strndup(char *src,int num) {
+ char *dst;
+
+ if(!(dst=(char *)malloc(num+1)))return NULL;
+ dst[num]='\0';
+
+ return strncpy(dst, src, num);
+}
+
+static char *url2hostport(char *url,char **hname,
+ unsigned long *hip,unsigned int *port) {
+ char *cptr;
+ struct hostent *myhostent;
+ struct in_addr myaddr;
+ int isip=1;
+
+ if(!(strncmp(url,httpstr,7)))url+=7;
+ cptr=url;
+ while(*cptr && *cptr!=':' && *cptr!='/') {
+ if((*cptr<'0' || *cptr>'9') && *cptr!='.')isip=0;
+ cptr++;
+ }
+ if(!(*hname=strndup(url,cptr-url))) {
+ *hname=NULL;
+ return NULL;
+ }
+ if(!isip)
+ {
+ if (!(myhostent=gethostbyname(*hname)))return NULL;
+ memcpy(&myaddr,myhostent->h_addr,sizeof(myaddr));
+ *hip=myaddr.s_addr;
+ }
+ else if((*hip=inet_addr(*hname))==INADDR_NONE)return NULL;
+ if(!*cptr || *cptr=='/') {
+ *port=80;
+ return cptr;
+ }
+ *port=atoi(++cptr);
+ while(*cptr && *cptr!='/')cptr++;
+ return cptr;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+HttpInputStream::HttpInputStream() {
+
+ proxyurl=NULL;
+ proxyip=0;
+ lopen=false;
+ byteCnt=0;
+}
+
+
+HttpInputStream::~HttpInputStream() {
+ close();
+}
+
+
+int HttpInputStream::open(const char* filename) {
+ close();
+ if (filename == NULL) {
+ return false;
+ }
+ /*
+ int matchPos=InputStream::getPath(filename,"http");
+ if (matchPos=0) {
+ return false;
+ }
+ */
+
+ char* filename2=strdup(filename);
+ if((fp=http_open(filename2))==NULL) {
+ cout << "seterrorcode(SOUND_ERROR_FILEOPENFAIL)"<<endl;
+ delete filename2;
+ return false;
+ }
+ delete filename2;
+ lopen=true;
+ setUrl(filename);
+ return lopen;
+}
+
+
+void HttpInputStream::close() {
+ if (isOpen()) {
+ ::fclose(fp);
+ }
+ lopen=false;
+ setUrl(NULL);
+}
+
+
+int HttpInputStream::isOpen() {
+ return lopen;
+}
+
+
+int HttpInputStream::eof() {
+ if (isOpen()==false){
+ return true;
+ }
+ return feof(fp);
+}
+
+
+int HttpInputStream::read(char* ptr,int size) {
+ int bytesRead=0;
+ if (isOpen()) {
+ bytesRead=fread(ptr, 1,size, fp);
+ if (ferror(fp) != 0){
+ cout <<"http fread error"<<endl;
+ } else {
+ byteCnt+=bytesRead;
+ }
+ }
+ return bytesRead;
+}
+
+int HttpInputStream::seek(long posInBytes) {
+ cout << "HttpInputStream::setBytePos not implemented:"<<posInBytes<<endl;
+ return false;
+}
+
+
+
+long HttpInputStream::getByteLength() {
+ cout << "HttpInputStream::getByteLength not implemented"<<endl;
+ return 0;
+}
+
+
+
+void HttpInputStream::print() {
+ printf("pos in file:%8x\n",(int)ftell(fp));
+}
+
+
+int HttpInputStream::writestring(int fd, char *string) {
+ int result,bytes=strlen(string);
+
+ while (bytes) {
+ if((result=SOCKETWRITEFUNC(fd,string,bytes))<0 && errno!=EINTR) {
+ cout << "writestring fail -1"<<endl;
+ return false;
+ }
+ else if(result==0) {
+ cout << "writestring fail -2"<<endl;
+ return false;
+ }
+ string += result;
+ bytes -= result;
+ }
+ return true;
+}
+
+
+
+FILE* HttpInputStream::http_open(char *url) {
+ char *purl=NULL,*host,*request,*sptr;
+ char agent[50];
+ int linelength;
+ unsigned long myip;
+ unsigned int myport;
+ int sock;
+ int relocate=0,numrelocs=0;
+ struct sockaddr_in server;
+ FILE *myfile;
+ if (url == NULL) {
+ cout << "cannot open NULL http_open"<<endl;
+ return NULL;
+ }
+ if (strlen(url)==0) {
+ cout << "zero length http_open"<<endl;
+ return NULL;
+ }
+
+ if(!proxyip)
+ {
+ if(!proxyurl)
+ if(!(proxyurl=getenv("MP3_HTTP_PROXY")))
+ if(!(proxyurl=getenv("http_proxy")))
+ proxyurl = getenv("HTTP_PROXY");
+ if (proxyurl && proxyurl[0] && strcmp(proxyurl, "none"))
+ {
+ if (!(url2hostport(proxyurl, &host, &proxyip, &proxyport)))
+ {
+ cout << "seterrorcode(SOUND_ERROR_UNKNOWNPROXY)"<<endl;;
+ return NULL;
+ }
+ if(host)free(host);
+ }
+ else
+ proxyip = INADDR_NONE;
+ }
+
+ if((linelength=strlen(url)+100)<1024)
+ linelength=1024;
+ if(!(request=(char *)malloc(linelength)) || !(purl=(char *)malloc(1024)))
+ {
+ cout << "seterrorcode(SOUND_ERROR_MEMORYNOTENOUGH)"<<endl;
+ return NULL;
+ }
+ strncpy(purl,url,1023);
+ purl[1023]='\0';
+ do{
+ strcpy(request,"GET ");
+ if(proxyip!=INADDR_NONE)
+ {
+ if(strncmp(url,httpstr,7))
+ strcat(request,httpstr);
+ strcat(request,purl);
+ myport=proxyport;
+ myip=proxyip;
+ }
+ else
+ {
+ if(!(sptr=url2hostport(purl,&host,&myip,&myport)))
+ {
+ cout << "seterrorcode(SOUND_ERROR_UNKNOWNHOST)"<<endl;;
+ return NULL;
+ }
+ if (host)
+ free (host);
+ strcat (request, sptr);
+ }
+ sprintf (agent, " HTTP/1.0\r\nUser-Agent: %s/%s\r\n\r\n",
+ "Splay","0.6");
+ strcat (request, agent);
+ server.sin_family = AF_INET;
+ server.sin_port = htons(myport);
+ server.sin_addr.s_addr = myip;
+ if((sock=socket(PF_INET,SOCK_STREAM,6))<0) {
+ cout <<"seterrorcode(SOUND_ERROR_SOCKET)"<<endl;
+ return NULL;
+ }
+ if(connect(sock,(struct sockaddr *)&server,sizeof(server))) {
+ cout <<"seterrorcode(SOUND_ERROR_CONNECT)"<<endl;
+ return NULL;
+ }
+ if(!writestring(sock,request))return NULL;
+ if(!(myfile=::fdopen(sock, "rb"))) {
+ cout << "seterrorcode(SOUND_ERROR_FDOPEN)"<<endl;
+ return NULL;
+ };
+ relocate=false;
+ purl[0]='\0';
+ if(!readstring(request,linelength-1,myfile))return NULL;
+ if((sptr=strchr(request,' '))) {
+ switch(sptr[1]) {
+ case '3':relocate=true;
+ case '2':break;
+ default:
+ cout <<"seterrorcode(SOUND_ERROR_HTTPFAIL)"<<endl;
+ return NULL;
+ }
+ }
+ do {
+ if(!readstring(request,linelength-1,myfile))return NULL;
+ if(!strncmp(request,"Location:",9))
+ strncpy (purl,request+10,1023);
+ } while (request[0]!='\r' && request[0]!='n');
+ } while(relocate && purl[0] && numrelocs++<5);
+ if(relocate) {
+ cout << "seterrorcode(SOUND_ERROR_TOOMANYRELOC)"<<endl;
+ return NULL;
+ }
+ free(purl);
+ free(request);
+ return myfile;
+}
+
+long HttpInputStream::getBytePosition() {
+ return 0;
+}
+
+int HttpInputStream::readstring(char *string,int maxlen,FILE *f) {
+ char *result;
+
+ do{
+ result=fgets(string,maxlen,f);
+ }while(!result && errno==EINTR);
+ if(!result)
+ {
+ cout << "seterrorcode(SOUND_ERROR_FILEREADFAIL)"<<endl;
+ return false;
+ }
+
+ return true;
+}
diff --git a/mpeglib/lib/input/httpInputStream.h b/mpeglib/lib/input/httpInputStream.h
new file mode 100644
index 00000000..78ec10f0
--- /dev/null
+++ b/mpeglib/lib/input/httpInputStream.h
@@ -0,0 +1,84 @@
+/*
+ reads input data
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __HTTPINPUTSTREAM_H
+#define __HTTPINPUTSTREAM_H
+
+
+#include "inputStream.h"
+
+#ifndef __STRICT_ANSI__
+#define __STRICT_ANSI__
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+
+
+#if defined WIN32
+#include <io.h>
+#define SOCKETWRITEFUNC _write
+#else
+
+extern "C" {
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+ }
+#define SOCKETWRITEFUNC write
+#endif
+
+#include <sys/types.h>
+#include <errno.h>
+
+class HttpInputStream : public InputStream{
+
+ long byteCnt;
+
+ public:
+ HttpInputStream();
+ ~HttpInputStream();
+
+ int open(const char* dest);
+ void close();
+ int isOpen();
+
+ int eof();
+ int read(char* ptr,int size);
+ int seek(long bytePos);
+
+ long getByteLength();
+ long getBytePosition();
+
+ void print();
+
+ private:
+
+ FILE* fp;
+ int size;
+
+ int writestring(int fd,char *string);
+ int readstring(char *string,int maxlen,FILE *f);
+ FILE* http_open(char *url);
+
+ char* proxyurl;
+ unsigned long proxyip;
+ unsigned int proxyport;
+
+ int lopen;
+
+};
+#endif
diff --git a/mpeglib/lib/input/inputDetector.cpp b/mpeglib/lib/input/inputDetector.cpp
new file mode 100644
index 00000000..28bcad5c
--- /dev/null
+++ b/mpeglib/lib/input/inputDetector.cpp
@@ -0,0 +1,192 @@
+/*
+ returns inputtype for a given string
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "inputDetector.h"
+
+#include <iostream>
+
+using namespace std;
+
+static const char *filestrglobal1="/";
+static const char *filestrglobal2="./";
+
+typedef struct prot2type_s {
+ const char* name;
+ int type;
+} prot2type;
+
+static prot2type protocols[]= { {"http:" , __INPUT_HTTP },
+ {"cdi:" , __INPUT_CDI },
+ {"vcd:" , __INPUT_CDI },
+ {"file:" , __INPUT_FILE },
+ {"cdda:" , __INPUT_CDDA },
+ {"audiocd:", __INPUT_CDDA },
+ {NULL , __INPUT_UNKNOWN} };
+
+
+InputDetector::InputDetector() {
+}
+
+
+InputDetector::~InputDetector() {
+}
+
+int InputDetector::getProtocolPos(int type,const char* url) {
+ prot2type* current=protocols;
+ int i=0;
+ while(current->name != NULL) {
+ if (current->type == type) {
+ int len=strlen(current->name);
+ if (strncmp(url,current->name,len)==0) {
+ return i;
+ }
+ }
+ i++;
+ current++;
+ }
+ return -1;
+}
+
+int InputDetector::getProtocolType(const char* url) {
+ prot2type* current=protocols;
+ int n=strlen(url);
+ if (n > 0) {
+ while(current->name != NULL) {
+ int k=strlen(current->name);
+ if (n >= k) {
+ if (strncmp(url,current->name,k)==0) {
+ return current->type;
+ }
+ }
+ current++;
+ }
+ }
+ return __INPUT_UNKNOWN;
+}
+
+int InputDetector::getInputType(const char* url) {
+ int back=__INPUT_FILE;
+
+ if (url == NULL) {
+ return back;
+ }
+
+ back=InputDetector::getProtocolType(url);
+ if (back == __INPUT_UNKNOWN) {
+ back = __INPUT_FILE;
+ }
+
+ return back;
+
+}
+
+char* InputDetector::removeProtocol(const char* url) {
+ int type=InputDetector::getProtocolType(url);
+ int n=strlen(url);
+ if (n == 0) {
+ return NULL;
+ }
+ if (type == __INPUT_UNKNOWN) {
+ return strdup(url);
+ }
+ int pos=InputDetector::getProtocolPos(type,url);
+ if (pos == -1) {
+ return NULL;
+ }
+ const char* name=protocols[pos].name;
+ int k=strlen(name);
+ if (n >= k) {
+ return strdup(url+k);
+ }
+ return NULL;
+
+}
+
+char* InputDetector::getExtension(const char* url) {
+ if (url == NULL) {
+ cout << "get url NULL"<<endl;
+ return NULL;
+ }
+ char* back=NULL;
+ char* extStart=strrchr(url,'.');
+ if (extStart != NULL) {
+ cout << "extStart:"<<extStart<<endl;
+ back=strdup(extStart);
+ }
+ return back;
+}
+
+char* InputDetector::removeExtension(const char* url,char* extension) {
+ if (url == NULL) {
+ cout << "removeExtension url NULL"<<endl;
+ return NULL;
+ }
+ if (extension == NULL) {
+ cout << "removeExtension extension NULL"<<endl;
+ return strdup(url);
+ }
+ char* back=NULL;
+ int nExt=strlen(extension);
+ int nUrl=strlen(url);
+ cout << "extension:"<<extension<<" url:"<<url<<endl;
+ if (nUrl >= nExt) {
+ if(strncmp(url+nUrl-nExt,extension,nExt)==0) {
+ back=new char[nUrl-nExt+1];
+ back[nUrl-nExt]=0;
+ strncpy(back,url,nUrl-nExt);
+ }
+ }
+ cout << "removeExt:"<<back<<endl;
+ return back;
+
+}
+
+char* InputDetector::getWithoutExtension(const char* url) {
+ char* extension=NULL;
+ char* back=NULL;
+ if (url == NULL) {
+ return NULL;
+ }
+ extension=InputDetector::getExtension(url);
+ if (extension == NULL) {
+ back=strdup(url);
+ } else {
+ back=InputDetector::removeExtension(url,extension);
+ delete extension;
+ }
+ return back;
+}
+
+char* InputDetector::getFilename(const char* url) {
+ if (url == NULL) {
+ return NULL;
+ }
+ char* startSlash=strrchr(url,'/');
+ if (startSlash == NULL) {
+ return NULL;
+ }
+ if (strlen(startSlash) == 1) {
+ return NULL;
+ }
+ startSlash++;
+ if (*startSlash == 0) {
+ return NULL;
+ }
+ return strdup(startSlash);
+}
+
+
+char* InputDetector::removeSlash(const char* url) {
+ return InputDetector::removeExtension(url,(char*)"/");
+}
diff --git a/mpeglib/lib/input/inputDetector.h b/mpeglib/lib/input/inputDetector.h
new file mode 100644
index 00000000..f4c3e47d
--- /dev/null
+++ b/mpeglib/lib/input/inputDetector.h
@@ -0,0 +1,56 @@
+/*
+ returns inputtype for a given string
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __INPUTDETECTOR_H
+#define __INPUTDETECTOR_H
+
+
+
+#define __INPUT_UNKNOWN 0
+#define __INPUT_FILE 1
+#define __INPUT_HTTP 2
+#define __INPUT_CDI 3
+#define __INPUT_BUFFER 4
+#define __INPUT_CDDA 5
+
+extern "C" {
+#include <string.h>
+}
+
+
+class InputDetector {
+
+
+ public:
+ InputDetector();
+ ~InputDetector();
+
+ static int getInputType(const char* url);
+
+ static int getProtocolType(const char* url);
+ // returns new allocated string without protocol specifier
+ static char* removeProtocol(const char* url);
+ static char* getWithoutExtension(const char* url);
+ static char* getExtension(const char* url);
+ static char* removeExtension(const char* url,char* extension);
+ static char* removeSlash(const char* url);
+ static char* getFilename(const char* url);
+
+ private:
+ static int getProtocolPos(int type,const char* url);
+
+};
+
+#endif
+
diff --git a/mpeglib/lib/input/inputPlugin.cpp b/mpeglib/lib/input/inputPlugin.cpp
new file mode 100644
index 00000000..f3a33002
--- /dev/null
+++ b/mpeglib/lib/input/inputPlugin.cpp
@@ -0,0 +1,92 @@
+/*
+ C interface creator for input_plugins
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "inputPlugin.h"
+
+#include <iostream>
+
+using namespace std;
+
+
+InputPlugin::InputPlugin() {
+}
+
+
+InputPlugin::~InputPlugin() {
+}
+
+
+
+int InputPlugin::getInputType(const char* dest) {
+ return InputDetector::getInputType(dest);
+}
+
+
+InputStream* InputPlugin::createInputStream(int inputType) {
+ // make checks which input routine to use
+ InputStream* inputStream;
+ int method;
+
+ inputStream=NULL;
+ method=inputType;
+
+ switch(method) {
+ case __INPUT_FILE: {
+ inputStream=new FileInputStream();
+ break;
+ }
+ case __INPUT_CDDA: {
+ inputStream=new CDDAInputStream();
+ break;
+ }
+
+ case __INPUT_HTTP: {
+ inputStream=new HttpInputStream();
+ break;
+ }
+ case __INPUT_CDI: {
+ inputStream=new CDRomInputStream();
+ break;
+ }
+ default:
+ cout << "error cannot create default input stream"<<endl;
+ exit(0);
+ }
+
+ return inputStream;
+}
+
+InputStream* InputPlugin::createInputStream(int inputType,int lThreadSafe) {
+ InputStream* input=InputPlugin::createInputStream(inputType);
+ if (lThreadSafe == false) {
+ return input;
+ }
+ InputStream* tsInput=new ThreadSafeInputStream(input);
+ return tsInput;
+}
+
+
+InputStream* InputPlugin::createInputStream(const char* dest) {
+ int method;
+
+ method=InputPlugin::getInputType(dest);
+ return (InputPlugin::createInputStream(method));
+}
+
+
+InputStream* InputPlugin::createInputStream(const char* dest,int lThreadSafe) {
+ int method;
+
+ method=InputPlugin::getInputType(dest);
+ return (InputPlugin::createInputStream(method,lThreadSafe));
+}
diff --git a/mpeglib/lib/input/inputPlugin.h b/mpeglib/lib/input/inputPlugin.h
new file mode 100644
index 00000000..5ac68307
--- /dev/null
+++ b/mpeglib/lib/input/inputPlugin.h
@@ -0,0 +1,45 @@
+/*
+ C interface creator for input_plugins
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __INPUTPLUGIN_H
+#define __INPUTPLUGIN_H
+
+#include "httpInputStream.h"
+#include "fileInputStream.h"
+#include "cdromInputStream.h"
+#include "bufferInputStream.h"
+#include "threadSafeInputStream.h"
+#include "cddaInputStream.h"
+#include <string.h>
+
+#include "inputDetector.h"
+#include <kdemacros.h>
+
+#define _INPUT_THREADSAFE 1
+
+class KDE_EXPORT InputPlugin {
+
+ public:
+ InputPlugin();
+ ~InputPlugin();
+
+ static InputStream* createInputStream(int inputType);
+ static InputStream* createInputStream(int inputType,int lThreadSafe);
+ static InputStream* createInputStream(const char* dest);
+ static InputStream* createInputStream(const char* dest,int lThreadSafe);
+ static int getInputType(const char* dest);
+
+};
+#endif
diff --git a/mpeglib/lib/input/inputStream.cpp b/mpeglib/lib/input/inputStream.cpp
new file mode 100644
index 00000000..7eb9943b
--- /dev/null
+++ b/mpeglib/lib/input/inputStream.cpp
@@ -0,0 +1,135 @@
+/*
+ generic input class
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "inputStream.h"
+#include "../util/mmx/mmx.h"
+
+#include <iostream>
+
+using namespace std;
+
+
+InputStream::InputStream() {
+ timeStampArray=new TimeStampArray((char*)"Input",3000);
+ urlBuffer=new DynBuffer(20);
+ // we call mm_support() here because it is the only position
+ // where we gurantee that not threads are
+ // running (the call is not thread safe)
+ // afer the call we never execute the asm part again
+ // and everything is fine
+ mm_support();
+}
+
+
+
+InputStream::~InputStream() {
+ delete timeStampArray;
+ delete urlBuffer;
+
+}
+
+
+int InputStream::open(const char* dest) {
+ cout << "direct virtual call InputStream::open:"<<dest<<endl;
+ return false;
+}
+
+
+void InputStream::close() {
+ cout << "direct virtual call InputStream::close"<<endl;
+ exit(0);
+}
+
+
+int InputStream::isOpen() {
+ cout << "direct virtual call InputStream::isOpen"<<endl;
+ exit(0);
+ return false;
+}
+
+
+int InputStream::eof() {
+ cout << "direct virtual call InputStream::eof"<<endl;
+ exit(0);
+ return true;
+}
+
+
+int InputStream::read(char* ,int ) {
+ cout << "direct virtual call InputStream::read"<<endl;
+ exit(0);
+ return 0;
+}
+
+
+int InputStream::seek(long bytePos) {
+ cout << "direct virtual call InputStream::seek:"<<bytePos<<endl;
+ exit(0);
+ return false;
+}
+
+void InputStream::clear() {
+ cout << "direct virtual call InputStream::clear:"<<endl;
+ exit(0);
+}
+
+
+long InputStream::getByteLength() {
+ cout << "direct virtual call InputStream::getByteLength"<<endl;
+ return 0;
+}
+
+
+long InputStream::getBytePosition() {
+ cout << "direct virtual call InputStream::getBytePosition"<<endl;
+ return 0;
+}
+
+
+void InputStream::insertTimeStamp(TimeStamp* src,long key,int len) {
+ timeStampArray->insertTimeStamp(src,key,len);
+}
+
+TimeStamp* InputStream::getTimeStamp(long key) {
+ return timeStampArray->getTimeStamp(key);
+}
+
+
+int InputStream::bytesUntilNext(long key) {
+ return timeStampArray->bytesUntilNext(key);
+}
+
+void InputStream::print() {
+ cout << "direct virtual call InputStream::print"<<endl;
+}
+
+
+
+
+
+char* InputStream::getUrl() {
+ return urlBuffer->getData();
+}
+
+
+void InputStream::setUrl(const char* url) {
+ urlBuffer->clear();
+ if (url != NULL) {
+ urlBuffer->append(url);
+ }
+}
+
+
+
+
+
diff --git a/mpeglib/lib/input/inputStream.h b/mpeglib/lib/input/inputStream.h
new file mode 100644
index 00000000..e232e99b
--- /dev/null
+++ b/mpeglib/lib/input/inputStream.h
@@ -0,0 +1,79 @@
+/*
+ generic input class
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __MPEGLIBINPUTSTREAM_H
+#define __MPEGLIBINPUTSTREAM_H
+
+
+extern "C" {
+#include <string.h>
+#include <stdio.h>
+#include <sys/stat.h>
+}
+
+#include "../util/timeStampArray.h"
+#include "../util/dynBuffer.h"
+
+
+
+
+/**
+ The abstraction for the input stream. In derived classes
+ we implement http,file and cdi access.
+*/
+
+
+class InputStream {
+
+
+
+ public:
+ InputStream();
+
+
+ virtual ~InputStream();
+
+ virtual int open(const char* dest);
+ virtual void close();
+ virtual int isOpen();
+
+ virtual int eof();
+ virtual int read(char* ptr,int size);
+ virtual int seek(long bytePos);
+ // clears possible input buffers
+ // (called by the decoderPlugin after a resyncCommit)
+ virtual void clear();
+
+ virtual long getByteLength();
+ virtual long getBytePosition();
+
+ virtual void insertTimeStamp(TimeStamp* src,long key,int len);
+ virtual TimeStamp* getTimeStamp(long key);
+ virtual int bytesUntilNext(long key);
+ virtual void print();
+ char* getUrl();
+
+
+
+ protected:
+ DynBuffer* urlBuffer;
+
+
+ class TimeStampArray* timeStampArray;
+ void setUrl(const char* url);
+
+
+};
+#endif
diff --git a/mpeglib/lib/input/simpleRingBuffer.cpp b/mpeglib/lib/input/simpleRingBuffer.cpp
new file mode 100644
index 00000000..71c92329
--- /dev/null
+++ b/mpeglib/lib/input/simpleRingBuffer.cpp
@@ -0,0 +1,420 @@
+/*
+ a thread safe ring buffer without dependencies
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#include "simpleRingBuffer.h"
+#include <string.h>
+
+#include <iostream>
+
+using namespace std;
+
+static int instanceCnt=0;
+
+SimpleRingBuffer::SimpleRingBuffer(int size,int minLinSize) {
+ abs_thread_mutex_init(&mut);
+ abs_thread_cond_init(&dataCond);
+ abs_thread_cond_init(&spaceCond);
+
+ instance=instanceCnt;
+ instanceCnt++;
+ this->size=size;
+ startPos=(char*)malloc(size);
+ readPos=startPos;
+ writePos=startPos;
+ lockPos=startPos;
+
+ lockgrade=0;
+ fillgrade=0;
+ linAvail=size;
+ lastPos=(startPos+size-1);
+ eofPos=lastPos+1;
+
+ canWrite=size;
+ canRead=0;
+
+ minLinBuf=new char[minLinSize];
+ this->minLinBufSize=minLinSize;
+ waitMinData=0;
+ waitMinSpace=0;
+ lWaitForData=false;
+ lWaitForSpace=false;
+
+ readBytes=0;
+ writeBytes=0;
+
+ lCanWaitForData=true;
+ lCanWaitForSpace=true;
+}
+
+
+SimpleRingBuffer::~SimpleRingBuffer() {
+ // The user of this class must take care that the threads
+ // have exited!
+ free(startPos);
+ delete [] minLinBuf;
+ abs_thread_mutex_destroy(&mut);
+ abs_thread_cond_destroy(&dataCond);
+ abs_thread_cond_destroy(&spaceCond);
+}
+
+
+int SimpleRingBuffer::getSize() {
+ return size;
+}
+
+
+int SimpleRingBuffer::getWriteArea(char* &ptr,int &size) {
+
+ // When we are in this area the following
+ // can happen by the other thread
+ // fillgrade is decreased (because reader fowards)
+ // readPos is changed.
+
+ ptr=writePos;
+ size=canWrite;
+
+ return size;
+}
+
+
+void SimpleRingBuffer::updateCanWrite() {
+ if (lockPos < writePos) {
+ canWrite=eofPos-writePos;
+ //printf("1 c:%d l:%p w:%p",canWrite,lockPos,writePos);
+ } else if (lockPos > writePos) {
+ canWrite=lockPos-writePos;
+ //printf("2 c:%d l:%p w:%p",canWrite,lockPos,writePos);
+ } else {
+ if (fillgrade > 0) {
+ canWrite=0;
+ } else {
+ canWrite=eofPos-writePos;
+ }
+ //printf("2 c:%d ",canWrite);
+
+ }
+ if (canWrite < 0) {
+ printf("error canWrite:%d fill:%d lock:%p start:%p eof:%p write:%p\n",
+ canWrite,fillgrade,lockPos,startPos,eofPos,writePos);
+ }
+
+}
+
+
+void SimpleRingBuffer::updateCanRead() {
+ canRead=fillgrade-lockgrade;
+ int currentSpace=size-fillgrade;
+ if (currentSpace >= waitMinSpace) {
+ abs_thread_cond_signal(&spaceCond);
+ }
+ if (canRead < 0) {
+ printf("error canRead:%d fillgrade:%d lockgrade:%d \n",
+ canRead,fillgrade,lockgrade);
+ }
+
+}
+
+
+void SimpleRingBuffer::forwardLockPtr(int nBytes) {
+ abs_thread_mutex_lock(&mut);
+
+ if (fillgrade < lockgrade) {
+ printf("1:fillgrade:%d < lockgrade:%d\n",fillgrade,lockgrade);
+ }
+ fillgrade-=nBytes;
+ lockgrade-=nBytes;
+ if (fillgrade < lockgrade) {
+ printf("2:fillgrade:%d < lockgrade:%d nBytes:%d\n",
+ fillgrade,lockgrade,nBytes);
+ }
+ lockPos=lockPos+nBytes;
+ if (lockPos > lastPos) { // we expects that we had a linAvail part
+ // if user forwards more than buffer boundary
+ nBytes=lockPos-lastPos;
+ lockPos=startPos+nBytes-1;
+ }
+ updateCanWrite();
+ updateCanRead();
+
+ abs_thread_mutex_unlock(&mut);
+ return;
+}
+
+
+void SimpleRingBuffer::forwardWritePtr(int nBytes) {
+ abs_thread_mutex_lock(&mut);
+
+ fillgrade=fillgrade+nBytes;
+ if (fillgrade < lockgrade) {
+ printf("3:fillgrade:%d < lockgrade:%d nBytes:%d\n",
+ fillgrade,lockgrade,nBytes);
+ }
+ writeBytes+=nBytes;
+ writePos=writePos+nBytes;
+ if(writePos >= eofPos) {
+ if (writePos == eofPos) {
+ writePos=startPos;
+ } else {
+ cout << "writePos > eofPos ! forward error:"<<(eofPos-writePos)
+ <<" bytes"<<endl;
+ }
+ }
+
+ updateCanWrite();
+ updateCanRead();
+ if (fillgrade >= waitMinData) {
+ abs_thread_cond_signal(&dataCond);
+ }
+ abs_thread_mutex_unlock(&mut);
+}
+
+
+int SimpleRingBuffer::waitForSpace(int bytes){
+ abs_thread_mutex_lock(&mut);
+ int back=0;
+ waitMinSpace=bytes;
+ if (waitMinSpace > size) {
+ waitMinSpace=size;
+ }
+ if (waitMinSpace < 0) {
+ cout << "negative waitForSpace"<<endl;
+ waitMinSpace=0;
+ }
+ int currentSpace=size-fillgrade;
+ if (lCanWaitForSpace) {
+ if (currentSpace < waitMinSpace) {
+ lWaitForSpace=true;
+ // it is not possible to wait for data/space simultanously
+ if (lWaitForData == true) {
+ abs_thread_cond_signal(&dataCond);
+ }
+ abs_thread_cond_wait(&spaceCond,&mut);
+ lWaitForSpace=false;
+ }
+ }
+ if (size-fillgrade >= waitMinSpace) {
+ back=1;
+ }
+ abs_thread_mutex_unlock(&mut);
+ return back;
+}
+
+
+void SimpleRingBuffer::exitWaitForSpace(){
+ abs_thread_mutex_lock(&mut);
+ abs_thread_cond_signal(&spaceCond);
+ abs_thread_mutex_unlock(&mut);
+}
+
+void SimpleRingBuffer::setCanWaitForSpace(int lCanWaitForSpace) {
+ abs_thread_mutex_lock(&mut);
+ this->lCanWaitForSpace=lCanWaitForSpace;
+ abs_thread_cond_signal(&spaceCond);
+ abs_thread_mutex_unlock(&mut);
+
+}
+
+
+
+void SimpleRingBuffer::forwardReadPtr(int nBytes) {
+ abs_thread_mutex_lock(&mut);
+ readBytes+=nBytes;
+ readPos+=nBytes;
+ linAvail=linAvail-nBytes;
+ lockgrade+=nBytes;
+ if (readPos > lastPos) { // we expects that we had a linAvail part
+ // if user forwards more than buffer boundary
+ nBytes=readPos-lastPos;
+ readPos=startPos+nBytes-1;
+ linAvail=lastPos+1-readPos;
+ }
+ if (fillgrade < lockgrade) {
+ printf("5:fillgrade:%d < lockgrade:%d nBytes:%d\n",
+ fillgrade,lockgrade,nBytes);
+ }
+ updateCanRead();
+ abs_thread_mutex_unlock(&mut);
+}
+
+
+
+int SimpleRingBuffer::getReadArea(char* &ptr,int &readSize) {
+ int pSize=readSize;
+ ptr=readPos;
+
+ if (canRead == 0) {
+ readSize=0;
+ return 0;
+ }
+ if (pSize < 0) {
+ cout << "Generic Memory Info invalid"<<endl;
+ pSize=size/2;
+ }
+ //
+ // Now the part the we deliver a minimum buffer if it is
+ // possible
+ //
+
+ if ( (pSize > linAvail) &&
+ (minLinBufSize >linAvail) &&
+ (canRead > linAvail) ) {
+ int copySize;
+ copySize=canRead; // we cannot copy more than this
+ if (copySize > pSize) { // if it is too much reduche it
+ copySize=pSize;
+ }
+ if (copySize > minLinBufSize) { // if it does not fit in buffer->reduce
+ copySize=minLinBufSize;
+ }
+ memcpy(minLinBuf,readPos,linAvail);
+ memcpy(minLinBuf+linAvail,startPos,copySize-linAvail);
+ readSize=copySize;
+ ptr=minLinBuf;
+ return copySize;
+ }
+
+ // linAvail part end
+
+ int copyBytes=linAvail;
+ if (canRead < copyBytes) {
+ copyBytes=canRead;
+ }
+ if (copyBytes >= pSize) {
+ readSize=pSize;
+ } else {
+ readSize=copyBytes;
+ }
+ return readSize;
+}
+
+
+
+void SimpleRingBuffer::exitWaitForData(){
+ abs_thread_mutex_lock(&mut);
+ abs_thread_cond_signal(&dataCond);
+ abs_thread_mutex_unlock(&mut);
+}
+
+
+
+int SimpleRingBuffer::waitForData(int bytes){
+ abs_thread_mutex_lock(&mut);
+ int back=0;
+ waitMinData=bytes;
+ if (waitMinData > size) {
+ waitMinData=size;
+ }
+ if (waitMinData < 0) {
+ cout << "negative waitForData"<<endl;
+ waitMinData=0;
+ }
+ if (lCanWaitForData) {
+ if (fillgrade < waitMinData) {
+ lWaitForData=true;
+ // it is not possible to wait for data space simultanously
+ if (lWaitForSpace == true) {
+ abs_thread_cond_signal(&spaceCond);
+ }
+ abs_thread_cond_wait(&dataCond,&mut);
+ lWaitForData=false;
+ }
+ }
+ if (fillgrade >= waitMinData) {
+ back=1;
+ }
+ abs_thread_mutex_unlock(&mut);
+ return back;
+}
+
+int SimpleRingBuffer::getCanWaitForData() {
+ return lCanWaitForData;
+}
+
+
+void SimpleRingBuffer::setCanWaitForData(int lCanWaitForData) {
+ abs_thread_mutex_lock(&mut);
+ this->lCanWaitForData=lCanWaitForData;
+ abs_thread_cond_signal(&dataCond);
+ abs_thread_mutex_unlock(&mut);
+
+}
+
+void SimpleRingBuffer::emptyBuffer() {
+ abs_thread_mutex_lock(&mut);
+ writePos=readPos;
+ if (fillgrade < lockgrade) {
+ printf("4:fillgrade:%d < lockgrade:%d\n",fillgrade,lockgrade);
+ }
+ linAvail=lastPos+1-writePos;
+ fillgrade=lockgrade;
+ updateCanRead();
+ updateCanWrite();
+ readBytes=0;
+ writeBytes=0;
+ if (size-fillgrade >= waitMinSpace) {
+ abs_thread_cond_signal(&spaceCond);
+ }
+ if (fillgrade >= waitMinData) {
+ abs_thread_cond_signal(&dataCond);
+ }
+ abs_thread_mutex_unlock(&mut);
+}
+
+
+int SimpleRingBuffer::getFillgrade() {
+ return fillgrade;
+}
+
+
+int SimpleRingBuffer::getReadBytes() {
+ return readBytes;
+}
+
+
+int SimpleRingBuffer::getWriteBytes() {
+ return writeBytes;
+}
+
+
+int SimpleRingBuffer::getFreeRead() {
+ return fillgrade;
+}
+
+int SimpleRingBuffer::getFreeWrite() {
+ return size-fillgrade;
+}
+
+void SimpleRingBuffer::resizeBuffer(int changeSize) {
+ abs_thread_mutex_lock(&mut);
+ int lPos=lockPos-startPos;
+ int wPos=writePos-startPos;
+ int rPos=readPos-startPos;
+ startPos=(char *)realloc(startPos,changeSize);
+ size=changeSize;
+ readPos=startPos+lPos;
+ writePos=startPos+wPos;
+ lockPos=startPos+rPos;
+
+ lastPos=(startPos+size-1);
+ eofPos=lastPos+1;
+
+ linAvail=lastPos+1-readPos;
+
+ updateCanWrite();
+ updateCanRead();
+ abs_thread_mutex_unlock(&mut);
+
+
+}
diff --git a/mpeglib/lib/input/simpleRingBuffer.h b/mpeglib/lib/input/simpleRingBuffer.h
new file mode 100644
index 00000000..101725bf
--- /dev/null
+++ b/mpeglib/lib/input/simpleRingBuffer.h
@@ -0,0 +1,136 @@
+/*
+ a thread safe ring buffer without dependencies
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef _SIMPLERINGBUFFER_H
+#define _SIMPLERINGBUFFER_H
+
+#include "../util/abstract/abs_thread.h"
+
+extern "C" {
+ #include <stdio.h>
+}
+
+/**
+ Note to parameter minLinBufSize in Constructor:
+ <p>
+ If the fillgrade is sufficient we can delivier at least
+ this amount of bytes with one "fill"
+ (If the fillgrade is not suffficient we can only deliever fillgrade)
+ This values adresses the problem that a ring buffer cannot
+ deliever linear memory the whole time(eg. if you read near the
+ upper end)
+ <p>
+ If the requested Buffersize by the device is smaller than
+ this number you can be sure that you get exactly
+ your preferred buffersize. not more not less.(but
+ only if the fillgrade allows this)
+*/
+
+
+
+
+class SimpleRingBuffer {
+
+
+ public:
+
+ SimpleRingBuffer(int ringBufferSize, int minLinBufferSize);
+ virtual ~SimpleRingBuffer();
+
+ // Writer thread can call these:
+
+ int getWriteArea(char* &ptr,int &size);
+ void forwardWritePtr(int bytes);
+ int waitForSpace(int minSpace);
+ void exitWaitForSpace();
+ void setCanWaitForSpace(int lCanWaitForSpace);
+
+
+ // Reader thread these:
+
+ void forwardReadPtr(int bytes);
+ int getReadArea(char* &ptr,int &size);
+ int waitForData(int minData);
+ void exitWaitForData();
+ void setCanWaitForData(int lCanWaitForData);
+ int getCanWaitForData();
+
+
+ // and the lockPos
+ void forwardLockPtr(int bytes);
+
+
+ // both:
+
+ int getFillgrade(); // return how much buffer between reader/writer
+ void emptyBuffer(); // frees the space between them
+ int getFreeRead();
+ int getFreeWrite();
+
+ int getSize();
+ int getReadBytes();
+ int getWriteBytes();
+
+ // make sure that no one calls getReadArea/getWriteArea
+ void resizeBuffer(int changeSize);
+ private:
+ void updateCanWrite();
+ void updateCanRead();
+
+ int size;
+
+ int lockgrade;
+ int fillgrade;
+
+ char* readPos;
+ char* writePos;
+ char* lockPos;
+
+ char* startPos;
+
+ char* lastPos;
+ char* eofPos;
+ int canWrite;
+ int canRead;
+
+ int waitMinData;
+ int waitMinSpace;
+
+ abs_thread_mutex_t mut;
+ abs_thread_cond_t dataCond;
+ abs_thread_cond_t spaceCond;
+
+ int insertBlock;
+ int readBlock;
+ int linAvail;
+
+ char* minLinBuf;
+ int minLinBufSize;
+ int lWaitForData;
+ int lWaitForSpace;
+
+ // statistic purpose:
+ int readBytes;
+ int writeBytes;
+
+ int lCanWaitForSpace;
+ int lCanWaitForData;
+ int instance;
+};
+
+#endif
+
+
+
diff --git a/mpeglib/lib/input/threadSafeInputStream.cpp b/mpeglib/lib/input/threadSafeInputStream.cpp
new file mode 100644
index 00000000..8b18a4c5
--- /dev/null
+++ b/mpeglib/lib/input/threadSafeInputStream.cpp
@@ -0,0 +1,137 @@
+/*
+ thread safe wrapper for input Stream
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "threadSafeInputStream.h"
+
+
+ThreadSafeInputStream::ThreadSafeInputStream(InputStream* input) {
+ threadQueue=new ThreadQueue();
+ this->input=input;
+}
+
+
+ThreadSafeInputStream::~ThreadSafeInputStream() {
+ delete threadQueue;
+ delete input;
+}
+
+
+
+int ThreadSafeInputStream::open(const char* dest) {
+ int back;
+ threadQueue->waitForExclusiveAccess();
+ back=input->open(dest);
+ threadQueue->releaseExclusiveAccess();
+ return back;
+}
+
+
+void ThreadSafeInputStream::close() {
+ threadQueue->waitForExclusiveAccess();
+ input->close();
+ threadQueue->releaseExclusiveAccess();
+}
+
+
+int ThreadSafeInputStream::isOpen() {
+ int back;
+ threadQueue->waitForExclusiveAccess();
+ back=input->isOpen();
+ threadQueue->releaseExclusiveAccess();
+ return back;
+}
+
+
+int ThreadSafeInputStream::eof() {
+ int back;
+ threadQueue->waitForExclusiveAccess();
+ back=input->eof();
+ threadQueue->releaseExclusiveAccess();
+ return back;
+}
+
+
+int ThreadSafeInputStream::read(char* buf,int len) {
+ int back;
+ threadQueue->waitForExclusiveAccess();
+ back=input->read(buf,len);
+ threadQueue->releaseExclusiveAccess();
+ return back;
+}
+
+
+int ThreadSafeInputStream::seek(long bytePos) {
+ int back;
+ threadQueue->waitForExclusiveAccess();
+ back=input->seek(bytePos);
+ threadQueue->releaseExclusiveAccess();
+ return back;
+}
+
+void ThreadSafeInputStream::clear() {
+ threadQueue->waitForExclusiveAccess();
+ input->clear();
+ threadQueue->releaseExclusiveAccess();
+}
+
+
+long ThreadSafeInputStream::getByteLength() {
+ long back;
+ threadQueue->waitForExclusiveAccess();
+ back=input->getByteLength();
+ threadQueue->releaseExclusiveAccess();
+ return back;
+}
+
+
+long ThreadSafeInputStream::getBytePosition() {
+ long back;
+ threadQueue->waitForExclusiveAccess();
+ back=input->getBytePosition();
+ threadQueue->releaseExclusiveAccess();
+ return back;
+}
+
+
+void ThreadSafeInputStream::insertTimeStamp(TimeStamp* src,long key,int len) {
+ threadQueue->waitForExclusiveAccess();
+ input->insertTimeStamp(src,key,len);
+ threadQueue->releaseExclusiveAccess();
+}
+
+
+TimeStamp* ThreadSafeInputStream::getTimeStamp(long key) {
+ TimeStamp* back;
+ threadQueue->waitForExclusiveAccess();
+ back=input->getTimeStamp(key);
+ threadQueue->releaseExclusiveAccess();
+ return back;
+}
+
+
+int ThreadSafeInputStream::bytesUntilNext(long key) {
+ int back;
+ threadQueue->waitForExclusiveAccess();
+ back=input->bytesUntilNext(key);
+ threadQueue->releaseExclusiveAccess();
+ return back;
+}
+
+void ThreadSafeInputStream::print() {
+ threadQueue->waitForExclusiveAccess();
+ input->print();
+ threadQueue->releaseExclusiveAccess();
+}
+
+
+
diff --git a/mpeglib/lib/input/threadSafeInputStream.h b/mpeglib/lib/input/threadSafeInputStream.h
new file mode 100644
index 00000000..0a29637b
--- /dev/null
+++ b/mpeglib/lib/input/threadSafeInputStream.h
@@ -0,0 +1,56 @@
+/*
+ thread safe wrapper for input Stream
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __THREADSAFEINPUTSTREAM_H
+#define __THREADSAFEINPUTSTREAM_H
+
+// read INTRO in threadQueue.h
+// This class makes the inputStream (given in constructor)
+// threadsafe by wrapping each call with a threadqueue.
+//
+// Important NOTE: the input pointer is the owned by this class !!!
+// which means: we call delete on it!
+
+#include "../util/abstract/threadQueue.h"
+#include "inputStream.h"
+
+
+class ThreadSafeInputStream : public InputStream {
+
+ ThreadQueue* threadQueue;
+ InputStream* input;
+
+ public:
+ ThreadSafeInputStream(InputStream* input);
+ ~ThreadSafeInputStream();
+
+
+ int open(const char* dest);
+ void close();
+ int isOpen();
+
+ int eof();
+ int read(char* ptr,int size);
+ int seek(long bytePos);
+ void clear();
+
+ long getByteLength();
+ long getBytePosition();
+
+ void insertTimeStamp(TimeStamp* src,long key,int len);
+ TimeStamp* getTimeStamp(long key);
+ int bytesUntilNext(long key);
+ void print();
+
+};
+#endif
diff --git a/mpeglib/lib/mpegplay/COPYRIGHT b/mpeglib/lib/mpegplay/COPYRIGHT
new file mode 100644
index 00000000..39076276
--- /dev/null
+++ b/mpeglib/lib/mpegplay/COPYRIGHT
@@ -0,0 +1,145 @@
+
+The copyright of this package is a bit interesting.
+
+I release my modification under GPL, although many
+parts (which I only copy&paste) are still the BSD license.
+Thus, if you use this software, you use GPL source with
+all advantages off the GPL license :-)
+
+Martin Vogt <mvogt@rhrk.uni-kl.de>
+
+
+Here is the "history" of this package:
+
+
+The copyrights from mpeg_play:
+===========================================
+
+First the source comes from the Berkeley mpeg_play player.
+and was programmed by:
+
+ MPEG Video Software Decoder
+ (Version 2.3; March 1996)
+
+ Lawrence A. Rowe<Rowe@CS.Berkeley.EDU>, Ketan Patel, Brian Smith, Steve Smoot, and Eugene Hung
+ Computer Science Division-EECS, Univ. of Calif. at Berkeley
+
+mpeg_play related emails:
+ mpeg-bugs@plateau.cs.berkeley.edu
+
+The JPEG code comes from :
+
+ jrevdct.c comes from the IJG libjpeg release,
+The "official" archive site for this software is ftp.uu.net (Internet
+address 192.48.96.9). The most recent released version can always be
+found there in directory graphics/jpeg.
+
+
+
+Many other peoples have contributed to the mpeg_play package:
+
+ACKNOWLEDGEMENTS:
+ We gratefully thank Hewlett-Packard, Fujitsu, the Semiconductor
+ Research Corporation for financial support.
+
+ We also want to thank the following people for their help:
+
+ Tom Lane of the Independent JPEG Group provided us with
+ the basic inverse DCT code used by our player.
+ (tom_lane@g.gp.cs.cmu.edu)
+
+ Reid Judd of Sun Microsystems provided advice and assistance.
+
+ Todd Brunhoff of NVR provided advice and assistance.
+
+ Toshihiko Kawai of Sony provided advice and assistance.
+
+ Portions of this software Copyright (c) 1995 Brown University.
+ All rights reserved.(Loring Holden lsh@cs.brown.edu)
+
+
+
+Here is my work:
+
+The mpeg_play source has become over the years more and more _ugly_.
+When I started the work (I wanted to play my Titanic CD-I with Linux)
+I saw that mpeg_play needed a complete reengineering. This package
+is the result of this work:
+
+* a readable source code
+* better structures (although not real OOP)
+* a few frames/second slower (buy faster cpus!)
+* and usefull input interfaces (for file,http,..)
+
+But my modification (which makes programmers life much easier)
+are done under the GPL!
+Ok, the main part is still under the BSD License (all these
+strange and complicated algorithm, I don't understand) but if you
+use this package (with my modifications) you use GPL source!
+
+And ere are all theses Licences:
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+
+/*
+ * Portions of this software Copyright (c) 1995 Brown University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement
+ * is hereby granted, provided that the above copyright notice and the
+ * following two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL BROWN UNIVERSITY BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF BROWN
+ * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * BROWN UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
+ * BASIS, AND BROWN UNIVERSITY HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
+ * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+
+Copyright (c) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This 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 Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+
+For more information you can conntact me by electronic mail:
+
+ mvogt@rhrk.uni-kl.de
+
+
diff --git a/mpeglib/lib/mpegplay/Makefile.am b/mpeglib/lib/mpegplay/Makefile.am
new file mode 100644
index 00000000..172d4a96
--- /dev/null
+++ b/mpeglib/lib/mpegplay/Makefile.am
@@ -0,0 +1,48 @@
+# libsplay - Makefile.am
+
+EXTRA_DIST = mainMpegPlay.cpp README COPYRIGHT \
+ mmxidct_asm.S copyFunctions.cpp
+
+INCLUDES = $(all_includes)
+
+AM_ASFLAGS = $(DEFS) $(DEFAULT_INCLUDES) $(all_includes) $(INTELCPPFLAG)
+AM_CCASFLAGS = $(AM_ASFLAGS)
+
+
+noinst_LTLIBRARIES = libmpegplay.la
+
+noinst_HEADERS = mpegVideoHeader.h mpegVideoStream.h \
+ decoderTables.h gop.h \
+ proto.h \
+ recon.h startCodes.h \
+ jrevdct.h \
+ motionVector.h slice.h \
+ decoderClass.h \
+ mmxidct.h \
+ picture.h mpegExtension.h macroBlock.h \
+ copyFunctions.h \
+ mpegVideoBitWindow.h videoDecoder.h \
+ copyFunctions_asm.h copyFunctions_mmx.h
+
+mpegutildir = $(includedir)/$(THIS_LIB_NAME)/mpegutil
+
+mpegutil_HEADERS = mpegSystemStream.h mpegVideoLength.h \
+ mpegSystemHeader.h tsSystemStream.h \
+ psSystemStream.h pesSystemStream.h
+
+libmpegplay_la_SOURCES = mpegVideoHeader.cpp mpegVideoStream.cpp \
+ globals.cpp jrevdct.cpp \
+ recon.cpp decoderClass.cpp \
+ decoderTables.cpp motionVector.cpp \
+ slice.cpp gop.cpp \
+ mmxidct.cpp \
+ mpegSystemHeader.cpp mpegSystemStream.cpp \
+ picture.cpp mpegExtension.cpp \
+ macroBlock.cpp \
+ mpegVideoLength.cpp \
+ mpegVideoBitWindow.cpp videoDecoder.cpp \
+ copyFunctions_asm.cpp copyFunctions_mmx.cpp \
+ mmxidct_asm.S copyFunctions.cpp \
+ tsSystemStream.cpp psSystemStream.cpp \
+ pesSystemStream.cpp
+
diff --git a/mpeglib/lib/mpegplay/README b/mpeglib/lib/mpegplay/README
new file mode 100644
index 00000000..5bd23c08
--- /dev/null
+++ b/mpeglib/lib/mpegplay/README
@@ -0,0 +1,26 @@
+
+This directory contains a highly modified version of
+mpeg_play the Berkley mpeg video player.
+
+Please view the file COPYRIGHT for licence questions.
+
+mpeg_play is in its current state (2.4) old and ugly source.
+It's an old software package, which has been ported to too
+many architectures, without keeping the source clean.
+
+(No, #ifdef/#defines is not the way to write cross platform code!)
+
+This is a new approach of an mpeg1 video player.
+
+I think this library is much cleaner and is now really usable
+for mpeg1 video/audio playing.
+
+It needed a lot of work to even come to this (not
+optimal) source code. But if you knew the mpeg_play source, you
+obviously understand :-)
+
+
+For comments or bug reports you can write me an email:
+
+Martin Vogt <mvogt@rhrk.uni-kl.de>
+
diff --git a/mpeglib/lib/mpegplay/configure.in.in b/mpeglib/lib/mpegplay/configure.in.in
new file mode 100644
index 00000000..b2830f33
--- /dev/null
+++ b/mpeglib/lib/mpegplay/configure.in.in
@@ -0,0 +1 @@
+AM_PROG_AS
diff --git a/mpeglib/lib/mpegplay/copyFunctions.cpp b/mpeglib/lib/mpegplay/copyFunctions.cpp
new file mode 100644
index 00000000..e290da50
--- /dev/null
+++ b/mpeglib/lib/mpegplay/copyFunctions.cpp
@@ -0,0 +1,330 @@
+/*
+ stores heavily used copy functions (makes mmx support easier)
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "copyFunctions.h"
+
+
+/*
+ * We use a lookup table to make sure values stay in the 0..255 range.
+ * Since this is cropping (ie, x = (x < 0)?0:(x>255)?255:x; ), wee call this
+ * table the "crop table".
+ * MAX_NEG_CROP is the maximum neg/pos value we can handle.
+ */
+/*
+ * We use a lookup table to make sure values stay in the 0..255 range.
+ * Since this is cropping (ie, x = (x < 0)?0:(x>255)?255:x; ), wee call this
+ * table the "crop table".
+ * MAX_NEG_CROP is the maximum neg/pos value we can handle.
+ */
+
+// Compiler cannot allocate too big arrays.
+
+
+
+
+CopyFunctions::CopyFunctions() {
+ /* Initialize crop table. */
+ cropTbl=new unsigned char[NUM_CROP_ENTRIES];
+
+ int i;
+
+ for (i = (-MAX_NEG_CROP); i < NUM_CROP_ENTRIES - MAX_NEG_CROP; i++) {
+ if (i <= 0) {
+ cropTbl[i + MAX_NEG_CROP] = 0;
+ } else if (i >= 255) {
+ cropTbl[i + MAX_NEG_CROP] = 255;
+ } else {
+ cropTbl[i + MAX_NEG_CROP] = i;
+ }
+ }
+ cm=cropTbl + MAX_NEG_CROP;
+
+ copyFunctions_asm = new CopyFunctions_MMX();
+ lmmx=copyFunctions_asm->support();
+
+
+
+}
+
+
+CopyFunctions::~CopyFunctions() {
+ delete cropTbl;
+}
+
+void CopyFunctions::startNOFloatSection() {
+ // nothing
+ copyFunctions_asm->startNOFloatSection();
+}
+
+
+void CopyFunctions::endNOFloatSection() {
+ copyFunctions_asm->endNOFloatSection();
+
+}
+
+
+void CopyFunctions::copy8_byte(unsigned char* source1,
+ unsigned char* dest,int inc) {
+ if (lmmx == false) {
+ int rr;
+
+ for (rr = 0; rr < 8; rr++) {
+ memcpy(dest,source1,sizeof(char)*8);
+ source1+=inc;
+ dest+=inc;
+ }
+
+ } else {
+ copyFunctions_asm->copy8_byte(source1,dest,inc);
+ }
+
+
+}
+
+void CopyFunctions::copy8_word(unsigned short* source1,
+ unsigned short* dest,int inc) {
+ int rr;
+
+ // Optimisation is slower, leave it in C
+ for (rr = 0; rr < 8; rr++) {
+ memcpy(dest,source1,sizeof(short)*8);
+ source1+=inc;
+ dest+=inc;
+ }
+
+}
+
+
+
+void CopyFunctions::copy8_src1linear_crop(short* source1,
+ unsigned char* dest,int inc) {
+
+ if (lmmx == false) {
+ int rr;
+
+ for (rr = 0; rr < 8; rr++) {
+
+ dest[0] = cm[source1[0]];
+ dest[1] = cm[source1[1]];
+ dest[2] = cm[source1[2]];
+ dest[3] = cm[source1[3]];
+ dest[4] = cm[source1[4]];
+ dest[5] = cm[source1[5]];
+ dest[6] = cm[source1[6]];
+ dest[7] = cm[source1[7]];
+
+
+ dest += inc;
+ source1 += 8;
+
+ }
+ } else {
+ copyFunctions_asm->copy8_src1linear_crop(source1,dest,inc);
+ }
+
+}
+
+void CopyFunctions::copy8_div2_nocrop(unsigned char* source1,
+ unsigned char* source2,
+ unsigned char* dest,int inc) {
+ if (lmmx == false) {
+ int rr;
+ for (rr = 0; rr < 8; rr++) {
+
+ dest[0] = (int) (source1[0] + source2[0]+1) >> 1;
+ dest[1] = (int) (source1[1] + source2[1]+1) >> 1;
+ dest[2] = (int) (source1[2] + source2[2]+1) >> 1;
+ dest[3] = (int) (source1[3] + source2[3]+1) >> 1;
+ dest[4] = (int) (source1[4] + source2[4]+1) >> 1;
+ dest[5] = (int) (source1[5] + source2[5]+1) >> 1;
+ dest[6] = (int) (source1[6] + source2[6]+1) >> 1;
+ dest[7] = (int) (source1[7] + source2[7]+1) >> 1;
+ dest += inc;
+ source1 += inc;
+ source2 += inc;
+ }
+ } else {
+ copyFunctions_asm->copy8_div2_nocrop(source1,source2, dest, inc);
+ }
+
+}
+
+void CopyFunctions::copy8_div2_destlinear_nocrop(unsigned char* source1,
+ unsigned char* source2,
+ unsigned char* dest,int inc) {
+
+ if (lmmx == false) {
+ int rr;
+ for (rr = 0; rr < 8; rr++) {
+ dest[0] = (int) (source1[0] + source2[0]) >> 1;
+ dest[1] = (int) (source1[1] + source2[1]) >> 1;
+ dest[2] = (int) (source1[2] + source2[2]) >> 1;
+ dest[3] = (int) (source1[3] + source2[3]) >> 1;
+ dest[4] = (int) (source1[4] + source2[4]) >> 1;
+ dest[5] = (int) (source1[5] + source2[5]) >> 1;
+ dest[6] = (int) (source1[6] + source2[6]) >> 1;
+ dest[7] = (int) (source1[7] + source2[7]) >> 1;
+ dest += 8;
+ source1 += inc;
+ source2 += inc;
+ }
+ } else {
+ copyFunctions_asm->copy8_div2_destlinear_nocrop(source1,source2,dest,inc);
+ }
+}
+
+
+void CopyFunctions::copy16_div2_destlinear_nocrop(unsigned char* source1,
+ unsigned char* source2,
+ unsigned char* dest,int inc){
+
+ if (lmmx == false) {
+ int rr;
+ for (rr = 0; rr < 16; rr++) {
+ dest[0] = (int) (source1[0] + source2[0]) >> 1;
+ dest[1] = (int) (source1[1] + source2[1]) >> 1;
+ dest[2] = (int) (source1[2] + source2[2]) >> 1;
+ dest[3] = (int) (source1[3] + source2[3]) >> 1;
+ dest[4] = (int) (source1[4] + source2[4]) >> 1;
+ dest[5] = (int) (source1[5] + source2[5]) >> 1;
+ dest[6] = (int) (source1[6] + source2[6]) >> 1;
+ dest[7] = (int) (source1[7] + source2[7]) >> 1;
+ dest[8] = (int) (source1[8] + source2[8]) >> 1;
+ dest[9] = (int) (source1[9] + source2[9]) >> 1;
+ dest[10] = (int) (source1[10] + source2[10]) >> 1;
+ dest[11] = (int) (source1[11] + source2[11]) >> 1;
+ dest[12] = (int) (source1[12] + source2[12]) >> 1;
+ dest[13] = (int) (source1[13] + source2[13]) >> 1;
+ dest[14] = (int) (source1[14] + source2[14]) >> 1;
+ dest[15] = (int) (source1[15] + source2[15]) >> 1;
+ dest += 16;
+ source1 += inc;
+ source2 += inc;
+ }
+ } else {
+ copyFunctions_asm->copy16_div2_destlinear_nocrop(source1,source2,dest,inc);
+ }
+
+}
+
+
+
+void CopyFunctions::copy8_div4_nocrop(unsigned char* source1,
+ unsigned char* source2,
+ unsigned char* source3,
+ unsigned char* source4,
+ unsigned char* dest,int inc) {
+ int rr;
+
+ for (rr = 0; rr < 8; rr++) {
+ dest[0]=(int) (source1[0]+source2[0]+source3[0]+source4[0] + 2) >> 2;
+ dest[1]=(int) (source1[1]+source2[1]+source3[1]+source4[1] + 2) >> 2;
+ dest[2]=(int) (source1[2]+source2[2]+source3[2]+source4[2] + 2) >> 2;
+ dest[3]=(int) (source1[3]+source2[3]+source3[3]+source4[3] + 2) >> 2;
+ dest[4]=(int) (source1[4]+source2[4]+source3[4]+source4[4] + 2) >> 2;
+ dest[5]=(int) (source1[5]+source2[5]+source3[5]+source4[5] + 2) >> 2;
+ dest[6]=(int) (source1[6]+source2[6]+source3[6]+source4[6] + 2) >> 2;
+ dest[7]=(int) (source1[7]+source2[7]+source3[7]+source4[7] + 2) >> 2;
+ dest += inc;
+ source1 += inc;
+ source2 += inc;
+ source3 += inc;
+ source4 += inc;
+ }
+}
+
+// Optimize me!
+// should be mmx perfomance analysis shows: 8 % overall time
+
+void CopyFunctions::copy8_src2linear_crop(unsigned char* source1,
+ short int* source2,
+ unsigned char* dest,int inc) {
+ int rr;
+ if (lmmx == false) {
+ for (rr = 0; rr < 8; rr++) {
+ dest[0] = cm[(int) source1[0] + (int) source2[0]];
+ dest[1] = cm[(int) source1[1] + (int) source2[1]];
+ dest[2] = cm[(int) source1[2] + (int) source2[2]];
+ dest[3] = cm[(int) source1[3] + (int) source2[3]];
+ dest[4] = cm[(int) source1[4] + (int) source2[4]];
+ dest[5] = cm[(int) source1[5] + (int) source2[5]];
+ dest[6] = cm[(int) source1[6] + (int) source2[6]];
+ dest[7] = cm[(int) source1[7] + (int) source2[7]];
+ dest += inc;
+ source1 += inc;
+ source2 += 8;
+ }
+ } else {
+ copyFunctions_asm->copy8_src2linear_crop(source1,source2,dest,inc);
+ }
+
+}
+
+// Optimize me!
+// should be mmx perfomance analysis shows: 13 % overall time
+void CopyFunctions::copy8_div2_src3linear_crop(unsigned char* source1,
+ unsigned char* source2,
+ short int* source3,
+ unsigned char* dest,int inc) {
+ int rr;
+ if (lmmx==false) {
+ for (rr = 0; rr < 8; rr++) {
+ dest[0] = cm[((int) (source1[0] + source2[0]+1) >> 1) + source3[0]];
+ dest[1] = cm[((int) (source1[1] + source2[1]+1) >> 1) + source3[1]];
+ dest[2] = cm[((int) (source1[2] + source2[2]+1) >> 1) + source3[2]];
+ dest[3] = cm[((int) (source1[3] + source2[3]+1) >> 1) + source3[3]];
+ dest[4] = cm[((int) (source1[4] + source2[4]+1) >> 1) + source3[4]];
+ dest[5] = cm[((int) (source1[5] + source2[5]+1) >> 1) + source3[5]];
+ dest[6] = cm[((int) (source1[6] + source2[6]+1) >> 1) + source3[6]];
+ dest[7] = cm[((int) (source1[7] + source2[7]+1) >> 1) + source3[7]];
+ dest += inc;
+ source1 += inc;
+ source2 += inc;
+ source3 += 8;
+
+ }
+ } else {
+ copyFunctions_asm->copy8_div2_src3linear_crop(source1,source2,source3,
+ dest,inc);
+ }
+
+
+}
+
+
+void CopyFunctions::copy8_div4_src5linear_crop(unsigned char* source1,
+ unsigned char* source2,
+ unsigned char* source3,
+ unsigned char* source4,
+ short int* source5,
+ unsigned char* dest,int inc) {
+
+ int rr;
+
+ for (rr = 0; rr < 8; rr++) {
+ dest[0]=cm[((int) (source1[0]+source2[0]+source3[0]+source4[0]+2) >> 2) + source5[0]];
+ dest[1]=cm[((int) (source1[1]+source2[1]+source3[1]+source4[1]+2) >> 2) + source5[1]];
+ dest[2]=cm[((int) (source1[2]+source2[2]+source3[2]+source4[2]+2) >> 2) + source5[2]];
+ dest[3]=cm[((int) (source1[3]+source2[3]+source3[3]+source4[3]+2) >> 2) + source5[3]];
+ dest[4]=cm[((int) (source1[4]+source2[4]+source3[4]+source4[4]+2) >> 2) + source5[4]];
+ dest[5]=cm[((int) (source1[5]+source2[5]+source3[5]+source4[5]+2) >> 2) + source5[5]];
+ dest[6]=cm[((int) (source1[6]+source2[6]+source3[6]+source4[6]+2) >> 2) + source5[6]];
+ dest[7]=cm[((int) (source1[7]+source2[7]+source3[7]+source4[7]+2) >> 2) + source5[7]];
+ dest +=inc;
+ source1 += inc;
+ source2 += inc;
+ source3 += inc;
+ source4 += inc;
+ source5 += 8;
+ }
+}
diff --git a/mpeglib/lib/mpegplay/copyFunctions.h b/mpeglib/lib/mpegplay/copyFunctions.h
new file mode 100644
index 00000000..a1436739
--- /dev/null
+++ b/mpeglib/lib/mpegplay/copyFunctions.h
@@ -0,0 +1,95 @@
+/*
+ stores heavily used copy functions (makes mmx support easier)
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __COPYFUNCTIONS_H
+#define __COPYFUNCTIONS_H
+
+#define MAX_NEG_CROP 32768
+#define NUM_CROP_ENTRIES (2048+2*MAX_NEG_CROP)
+
+#include <iostream>
+#include "../util/mmx/mmx.h"
+
+extern "C" {
+#include <string.h>
+}
+
+#include "copyFunctions_mmx.h"
+
+
+class CopyFunctions {
+
+ unsigned char *cm;
+ int lmmx;
+ unsigned char* cropTbl;
+ CopyFunctions_ASM* copyFunctions_asm;
+
+ public:
+ CopyFunctions();
+ ~CopyFunctions();
+
+ /**
+ We make sure, that during the whole construcion block
+ we never (!!!) do float operations, thus we move
+ the time consumin emms call really at the end of
+ the whole reconstrucion/motion compensation
+ */
+
+ void startNOFloatSection();
+ void endNOFloatSection();
+
+ void copy8_byte(unsigned char* source1,
+ unsigned char* dest,int inc);
+
+ void copy8_word(unsigned short* source1,
+ unsigned short* dest,int inc);
+
+
+ void copy8_div2_nocrop(unsigned char* source1,
+ unsigned char* source2,
+ unsigned char* dest,int inc);
+
+ void copy8_div2_destlinear_nocrop(unsigned char* source1,
+ unsigned char* source2,
+ unsigned char* dest,int inc);
+
+ void copy16_div2_destlinear_nocrop(unsigned char* source1,
+ unsigned char* source2,
+ unsigned char* dest,int inc);
+
+
+ void copy8_div4_nocrop(unsigned char* source1,
+ unsigned char* source2,
+ unsigned char* source3,
+ unsigned char* source4,
+ unsigned char* dest,int inc);
+
+ void copy8_src1linear_crop(short* source1,unsigned char* dest,int inc);
+
+ void copy8_src2linear_crop(unsigned char* source1,
+ short int* source2,
+ unsigned char* dest,int inc);
+ void copy8_div2_src3linear_crop(unsigned char* source1,
+ unsigned char* source2,
+ short int* source3,
+ unsigned char* dest,int inc);
+
+ void copy8_div4_src5linear_crop(unsigned char* source1,
+ unsigned char* source2,
+ unsigned char* source3,
+ unsigned char* source4,
+ short int* source5,
+ unsigned char* dest,int inc);
+
+};
+#endif
diff --git a/mpeglib/lib/mpegplay/copyFunctions_asm.cpp b/mpeglib/lib/mpegplay/copyFunctions_asm.cpp
new file mode 100644
index 00000000..40d4580f
--- /dev/null
+++ b/mpeglib/lib/mpegplay/copyFunctions_asm.cpp
@@ -0,0 +1,90 @@
+/*
+ copyfunctions base class
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "copyFunctions_asm.h"
+
+using namespace std;
+
+CopyFunctions_ASM::CopyFunctions_ASM() {
+}
+
+CopyFunctions_ASM::~CopyFunctions_ASM() {
+}
+
+
+int CopyFunctions_ASM::support() {
+ return false;
+}
+
+
+void CopyFunctions_ASM::startNOFloatSection() {
+}
+
+
+void CopyFunctions_ASM::endNOFloatSection() {
+}
+
+void CopyFunctions_ASM::copy8_byte(unsigned char* ,
+ unsigned char* ,int ) {
+ cout << "CopyFunctions_ASM::copy8_byte not implemented"<<endl;
+}
+
+
+void CopyFunctions_ASM::copy8_src1linear_crop(short* ,
+ unsigned char* ,int ) {
+ cout << "CopyFunctions_ASM:: not implemented"<<endl;
+}
+
+
+
+
+void CopyFunctions_ASM::copy8_div2_nocrop(unsigned char* ,
+ unsigned char* ,
+ unsigned char* ,int ) {
+ cout << "CopyFunctions_ASM:: copy8_div2_nocrop not implemented"<<endl;
+}
+
+
+void CopyFunctions_ASM::copy8_div2_destlinear_nocrop(unsigned char* ,
+ unsigned char* ,
+ unsigned char* ,
+ int ) {
+ cout << "CopyFunctions_ASM:: copy8_div2_destlinear_nocrop not implemented"
+ <<endl;
+}
+
+
+void CopyFunctions_ASM::copy16_div2_destlinear_nocrop(unsigned char* ,
+ unsigned char* ,
+ unsigned char* ,
+ int ) {
+ cout << "CopyFunctions_ASM:: copy16_div2_destlinear_nocrop not implemented"
+ <<endl;
+}
+
+
+void CopyFunctions_ASM::copy8_src2linear_crop(unsigned char* ,
+ short int* ,
+ unsigned char* ,int ) {
+ cout << "CopyFunctions_ASM:: copy8_src2linear_crop not implemented"<<endl;
+}
+
+
+void CopyFunctions_ASM::copy8_div2_src3linear_crop(unsigned char* ,
+ unsigned char* ,
+ short int* ,
+ unsigned char* ,
+ int ) {
+ cout << "CopyFunctions_ASM:: copy8_div2_src3linear_crop not implemented"
+ <<endl;
+}
diff --git a/mpeglib/lib/mpegplay/copyFunctions_asm.h b/mpeglib/lib/mpegplay/copyFunctions_asm.h
new file mode 100644
index 00000000..1156345a
--- /dev/null
+++ b/mpeglib/lib/mpegplay/copyFunctions_asm.h
@@ -0,0 +1,61 @@
+/*
+ copyfunctions base class
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __COPYFUNCTIONS_ASM_H
+#define __COPYFUNCTIONS_ASM_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <iostream>
+
+class CopyFunctions_ASM {
+
+ public:
+ CopyFunctions_ASM();
+ virtual ~CopyFunctions_ASM();
+
+ virtual int support();
+
+ virtual void startNOFloatSection();
+ virtual void endNOFloatSection();
+
+ virtual void copy8_byte(unsigned char* source1,
+ unsigned char* dest,int inc);
+
+ virtual void copy8_src1linear_crop(short* source1,
+ unsigned char* dest,int inc);
+
+ virtual void copy8_div2_nocrop(unsigned char* source1,
+ unsigned char* source2,
+ unsigned char* dest,int inc);
+
+ virtual void copy8_div2_destlinear_nocrop(unsigned char* source1,
+ unsigned char* source2,
+ unsigned char* dest,int inc);
+
+ virtual void copy16_div2_destlinear_nocrop(unsigned char* source1,
+ unsigned char* source2,
+ unsigned char* dest,int inc);
+
+ virtual void copy8_src2linear_crop(unsigned char* source1,
+ short int* source2,
+ unsigned char* dest,int inc);
+
+ virtual void copy8_div2_src3linear_crop(unsigned char* source1,
+ unsigned char* source2,
+ short int* source3,
+ unsigned char* dest,int inc);
+};
+#endif
diff --git a/mpeglib/lib/mpegplay/copyFunctions_mmx.cpp b/mpeglib/lib/mpegplay/copyFunctions_mmx.cpp
new file mode 100644
index 00000000..3e295e76
--- /dev/null
+++ b/mpeglib/lib/mpegplay/copyFunctions_mmx.cpp
@@ -0,0 +1,313 @@
+/*
+ copyfunctions implementation in mmx
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "copyFunctions_mmx.h"
+
+using namespace std;
+
+// mmx goodies
+static long ADD_1[] = {0x01010101, 0x01010101};
+static long ADDW_1[] = {0x00010001, 0x00010001};
+static long MASK_AND[] = {0x7f7f7f7f, 0x7f7f7f7f};
+
+void dummyCopyFunctions() {
+ cout << "ADD_1:"<<ADD_1<<endl;
+ cout << "ADDW_1:"<<ADDW_1<<endl;
+ cout << "MASK_AND:"<<MASK_AND<<endl;
+}
+
+CopyFunctions_MMX::CopyFunctions_MMX() {
+#ifdef INTEL
+ lmmx=mm_support();
+#else
+ lmmx=false;
+ cout << "no INTEL arch- disable MMX in copyFunctions"<<endl;
+#endif
+}
+
+
+CopyFunctions_MMX::~CopyFunctions_MMX() {
+}
+
+int CopyFunctions_MMX::support() {
+ return lmmx;
+}
+
+
+#if defined (__GNUC__) && defined (INTEL)
+
+void CopyFunctions_MMX::startNOFloatSection() {
+}
+
+
+void CopyFunctions_MMX::endNOFloatSection() {
+ emms();
+}
+
+
+
+void CopyFunctions_MMX::copy8_byte(unsigned char* source1,
+ unsigned char* dest,int inc) {
+ int rr=4;
+
+ asm (
+ "1:\n"
+ "movq (%0) ,%%mm0\n"
+ "leal (%0,%2) ,%0\n"
+ "movq (%0) ,%%mm1\n"
+ "leal (%0,%2) ,%0\n"
+
+ // Write
+ "movq %%mm0 ,(%1)\n"
+ "leal (%1,%2) ,%1\n"
+ "movq %%mm1 ,(%1)\n"
+ "leal (%1,%2) ,%1\n"
+
+ "decl %3\n"
+ "jnz 1b\n"
+ :
+ : "r"(source1),"r"(dest),"r"(inc),"r"(rr)
+ );
+}
+
+
+
+void CopyFunctions_MMX::copy8_src1linear_crop(short* source1,
+ unsigned char* dest,int inc) {
+
+ asm (
+ "movq (%1),%%mm0\n"
+ "packuswb 8(%1),%%mm0\n"
+ "movq %%mm0,(%0)\n"
+ "addl %2,%0\n"
+
+ "movq 16(%1),%%mm0\n"
+ "packuswb 24(%1),%%mm0\n"
+ "movq %%mm0,(%0)\n"
+ "addl %2,%0\n"
+
+ "movq 32(%1),%%mm0\n"
+ "packuswb 40(%1),%%mm0\n"
+ "movq %%mm0,(%0)\n"
+ "addl %2,%0\n"
+
+ "movq 48(%1),%%mm0\n"
+ "packuswb 56(%1),%%mm0\n"
+ "movq %%mm0,(%0)\n"
+ "addl %2,%0\n"
+
+ "movq 64(%1),%%mm0\n"
+ "packuswb 72(%1),%%mm0\n"
+ "movq %%mm0,(%0)\n"
+ "addl %2,%0\n"
+
+ "movq 80(%1),%%mm0\n"
+ "packuswb 88(%1),%%mm0\n"
+ "movq %%mm0,(%0)\n"
+ "addl %2,%0\n"
+
+ "movq 96(%1),%%mm0\n"
+ "packuswb 104(%1),%%mm0\n"
+ "movq %%mm0,(%0)\n"
+ "addl %2,%0\n"
+
+ "movq 112(%1),%%mm0\n"
+ "packuswb 120(%1),%%mm0\n"
+ "movq %%mm0,(%0)\n"
+ :
+ :"r" (dest), "r" (source1),"r" (inc)
+ );
+
+}
+
+
+
+void CopyFunctions_MMX::copy8_div2_nocrop(unsigned char* source1,
+ unsigned char* source2,
+ unsigned char* dest,int inc) {
+ int h=8;
+ asm (
+ "movq MASK_AND, %%mm5\n"
+ "movq ADD_1, %%mm6\n"
+ "1:\t"
+ "movq (%1), %%mm0\n" /* 8 s */
+ "movq (%4), %%mm1\n" /* 8 s +lx */
+ "psrlw $1,%%mm0\n"
+ "psrlw $1,%%mm1\n"
+ "pand %%mm5,%%mm0\n"
+ "pand %%mm5,%%mm1\n"
+ "paddusb %%mm1,%%mm0\n"
+ "addl %3,%1\n"
+ "paddusb %%mm6,%%mm0\n"
+ "addl %3,%4\n"
+ "movq %%mm0,(%2)\n"
+ "decl %0\n"
+ "leal (%2, %3), %2\n"
+ "jnz 1b\n"
+ :
+ : "c"(h), "r"(source1), "r"(dest), "r"(inc), "r"(source2)
+ );
+}
+
+
+void CopyFunctions_MMX::copy8_div2_destlinear_nocrop(unsigned char* source1,
+ unsigned char* source2,
+ unsigned char* dest,
+ int inc) {
+ int h=8;
+ asm (
+ "movq MASK_AND, %%mm5\n"
+ "1:\t"
+ "movq (%1), %%mm0\n" /* 8 s */
+ "movq (%4), %%mm1\n" /* 8 s +lx */
+ "psrlw $1,%%mm0\n"
+ "psrlw $1,%%mm1\n"
+ "pand %%mm5,%%mm0\n"
+ "pand %%mm5,%%mm1\n"
+ "paddusb %%mm1,%%mm0\n"
+ "addl %3,%1\n"
+ "addl %3,%4\n"
+ "movq %%mm0,(%2)\n"
+ "decl %0\n"
+ "leal 8(%2), %2\n"
+ "jnz 1b\n"
+ :
+ : "c"(h), "r"(source1), "r"(dest), "r"(inc), "r"(source2)
+ );
+}
+
+
+
+void CopyFunctions_MMX::copy16_div2_destlinear_nocrop(unsigned char* source1,
+ unsigned char* source2,
+ unsigned char* dest,
+ int inc) {
+ int h=16;
+ inc=inc-8;
+ asm (
+ "movq MASK_AND, %%mm5\n"
+ "1:\t"
+ "movq (%1), %%mm0\n" /* 8 s */
+ "movq (%4), %%mm1\n" /* 8 s +lx */
+ "psrlw $1,%%mm0\n"
+ "psrlw $1,%%mm1\n"
+ "pand %%mm5,%%mm0\n"
+ "pand %%mm5,%%mm1\n"
+ "paddusb %%mm1,%%mm0\n"
+ "leal 8(%1),%1\n"
+ "leal 8(%4),%4\n"
+ "movq %%mm0,(%2)\n"
+ "leal 8(%2),%2\n"
+
+ "movq (%1), %%mm0\n" /* 8 s */
+ "movq (%4), %%mm1\n" /* 8 s +lx */
+ "psrlw $1,%%mm0\n"
+ "psrlw $1,%%mm1\n"
+ "pand %%mm5,%%mm0\n"
+ "pand %%mm5,%%mm1\n"
+ "paddusb %%mm1,%%mm0\n"
+ "leal (%3,%1),%1\n"
+ "leal (%3,%4),%4\n"
+ "movq %%mm0,(%2)\n"
+ "leal 8(%2),%2\n"
+
+ "decl %0\n"
+ "jnz 1b\n"
+ :
+ : "c"(h), "r"(source1), "r"(dest), "r"(inc), "r"(source2)
+ );
+}
+
+
+void CopyFunctions_MMX::copy8_src2linear_crop(unsigned char* source1,
+ short int* source2,
+ unsigned char* dest,int inc) {
+
+ int rr=8;
+ // buggy
+
+ asm (
+ ".align 32\n"
+ "pxor %%mm2 ,%%mm2\n" // 0 0 0 0 0 0 0 0
+ "1:\n"
+ "movq (%0) ,%%mm0\n" // s1_7 s1_6 s1_5 s1_4 s1_3 s1_2 s1_1 s1_0
+ "movq (%0) ,%%mm4\n" // s1_7 s1_6 s1_5 s1_4 s1_3 s1_2 s1_1 s1_0
+ "punpckhbw %%mm2 ,%%mm0\n" // 0 s1_7 0 s1_6 0 s1_5 0 s1_4
+ "punpcklbw %%mm2 ,%%mm4\n" // 0 s1_3 0 s1_2 0 s1_1 0 s1_0
+ "movq (%1) ,%%mm1\n" // s23h s23l s22h s22l s21l s21h s20h s20l
+ "movq 8(%1) ,%%mm5\n" // s27h s27l s26h s26l s25l s25h s24h s24l
+ "paddw %%mm0 ,%%mm5\n" // mm4=mm4 + s3_0..3
+ "paddw %%mm4 ,%%mm1\n" // mm0=mm0 + s3_4..7
+ "packuswb %%mm5 ,%%mm1\n" // cm[...]
+
+ "movq %%mm1 ,(%2)\n" // wrote out
+
+ "leal (%0,%3), %0\n" // source1+=inc
+ "leal 16(%1) , %1\n" // source2+=inc
+ "leal (%2,%3), %2\n" // dest+=inc
+ "decl %4\n"
+ "jnz 1b\n"
+ //"emms\n"
+ :
+ : "r"(source1), "r"(source2), "r"(dest),"r"(inc),"r"(rr)
+ );
+}
+
+
+void CopyFunctions_MMX::copy8_div2_src3linear_crop(unsigned char* source1,
+ unsigned char* source2,
+ short int* source3,
+ unsigned char* dest,
+ int inc){
+ // buggy
+ int rr=8;
+
+ asm (
+ "pxor %%mm2 ,%%mm2\n" // 0 0 0 0 0 0 0 0
+ "pxor %%mm3 ,%%mm3\n" // 0 0 0 0 0 0 0 0
+ "movq ADDW_1,%%mm6\n" // 0 1 0 1 0 1 0 1
+ "1:\n"
+ "movq (%0) ,%%mm0\n" // s1_7 s1_6 s1_5 s1_4 s1_3 s1_2 s1_1 s1_0
+ "movq (%1) ,%%mm1\n" // s2_7 s2_6 s2_5 s2_4 s2_3 s2_2 s2_1 s2_0
+ "movq %%mm0 ,%%mm4\n" // s1_7 s1_6 s1_5 s1_4 s1_3 s1_2 s1_1 s1_0
+ "movq %%mm1 ,%%mm5\n" // s2_7 s2_6 s2_5 s2_4 s2_3 s2_2 s2_1 s2_0
+ "punpckhbw %%mm2 ,%%mm0\n" // 0 s1_7 0 s1_6 0 s1_5 0 s1_4
+ "punpckhbw %%mm3 ,%%mm1\n" // 0 s2_7 0 s2_6 0 s2_5 0 s2_4
+ "punpcklbw %%mm2 ,%%mm4\n" // 0 s1_3 0 s1_2 0 s1_1 0 s1_0
+ "punpcklbw %%mm3 ,%%mm5\n" // 0 s2_3 0 s2_2 0 s2_1 0 s2_0
+ "paddusw %%mm4 ,%%mm5\n" // mm5=s1_0..3 + s2_0..3
+ "paddusw %%mm0 ,%%mm1\n" // mm1=s1_4..7 + s2_4..7
+ "paddusw %%mm6 ,%%mm5\n" // mm5=mm5 + 1
+ "paddusw %%mm6 ,%%mm1\n" // mm1=mm1 + 1
+ "psraw $1 ,%%mm1\n" // mm1=mm1/2
+ "psraw $1 ,%%mm5\n" // mm5=mm5/2
+ "movq (%2) ,%%mm0\n" // s33h s33l s32h s32l s31l s31h s30h s30l
+ "movq 8(%2) ,%%mm4\n" // s37h s37l s36h s36l s35l s35h s34h s34l
+ "paddw %%mm0 ,%%mm5\n" // mm5=mm5 + s3_0..3
+ "paddw %%mm4 ,%%mm1\n" // mm1=mm1 + s3_4..7
+ "packuswb %%mm1 ,%%mm5\n" // cm[...]
+ "movq %%mm5 ,(%3)\n" // wrote out
+ "leal (%0,%4), %0\n" // source1+=inc
+ "leal (%1,%4), %1\n" // source2+=inc
+ "addl $16 ,%2 \n" // source3+8
+ "leal (%3,%4), %3\n" // dest+=inc
+ "decl %5\n"
+ "jnz 1b\n"
+ :
+ : "r"(source1), "r"(source2), "r"(source3),
+ "r"(dest),"r"(inc),"m"(rr)
+ );
+}
+
+
+#endif
diff --git a/mpeglib/lib/mpegplay/copyFunctions_mmx.h b/mpeglib/lib/mpegplay/copyFunctions_mmx.h
new file mode 100644
index 00000000..36e157be
--- /dev/null
+++ b/mpeglib/lib/mpegplay/copyFunctions_mmx.h
@@ -0,0 +1,68 @@
+/*
+ copyfunctions implementation in mmx
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __COPYFUNCTIONS_MMX_H
+#define __COPYFUNCTIONS_MMX_H
+
+#include "copyFunctions_asm.h"
+#include "../util/mmx/mmx.h"
+
+
+class CopyFunctions_MMX : public CopyFunctions_ASM {
+
+ int lmmx;
+
+ public:
+ CopyFunctions_MMX();
+ ~CopyFunctions_MMX();
+
+ int support();
+
+#if defined (__GNUC__) && defined (INTEL)
+
+ void startNOFloatSection();
+ void endNOFloatSection();
+
+
+ void copy8_byte(unsigned char* source1,
+ unsigned char* dest,int inc);
+
+ void copy8_src1linear_crop(short* source1,
+ unsigned char* dest,int inc);
+
+ void copy8_div2_nocrop(unsigned char* source1,
+ unsigned char* source2,
+ unsigned char* dest,int inc);
+
+ void copy8_div2_destlinear_nocrop(unsigned char* source1,
+ unsigned char* source2,
+ unsigned char* dest,int inc);
+
+ void copy16_div2_destlinear_nocrop(unsigned char* source1,
+ unsigned char* source2,
+ unsigned char* dest,int inc);
+
+ void copy8_src2linear_crop(unsigned char* source1,
+ short int* source2,
+ unsigned char* dest,int inc);
+
+ void copy8_div2_src3linear_crop(unsigned char* source1,
+ unsigned char* source2,
+ short int* source3,
+ unsigned char* dest,int inc);
+
+#endif
+
+};
+#endif
+
diff --git a/mpeglib/lib/mpegplay/decoderClass.cpp b/mpeglib/lib/mpegplay/decoderClass.cpp
new file mode 100644
index 00000000..e259c8e6
--- /dev/null
+++ b/mpeglib/lib/mpegplay/decoderClass.cpp
@@ -0,0 +1,893 @@
+/*
+ class for decoders
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "decoderClass.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+
+#ifndef NDEBUG
+#define NDEBUG
+#endif
+#include <assert.h>
+
+
+#define DEBUG_DECODERCLASS(x)
+//#define DEBUG_DECODERCLASS(x) x
+
+/* Array mapping zigzag to array pointer offset. */
+
+
+static const int zigzag_direct_nommx[64] = {
+ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12,
+ 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35,
+ 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
+ 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63};
+
+
+static const int zigzag_direct_mmx[64] = {
+
+ 0*8+0/* 0*/, 1*8+0/* 1*/, 0*8+1/* 8*/, 0*8+2/*16*/, 1*8+1/* 9*/, 2*8+0/* 2*/, 3*8+0/* 3*/, 2*8+1/*10*/,
+ 1*8+2/*17*/, 0*8+3/*24*/, 0*8+4/*32*/, 1*8+3/*25*/, 2*8+2/*18*/, 3*8+1/*11*/, 4*8+0/* 4*/, 5*8+0/* 5*/,
+ 4*8+1/*12*/, 5*8+2/*19*/, 2*8+3/*26*/, 1*8+4/*33*/, 0*8+5/*40*/, 0*8+6/*48*/, 1*8+5/*41*/, 2*8+4/*34*/,
+ 3*8+3/*27*/, 4*8+2/*20*/, 5*8+1/*13*/, 6*8+0/* 6*/, 7*8+0/* 7*/, 6*8+1/*14*/, 5*8+2/*21*/, 4*8+3/*28*/,
+ 3*8+4/*35*/, 2*8+5/*42*/, 1*8+6/*49*/, 0*8+7/*56*/, 1*8+7/*57*/, 2*8+6/*50*/, 3*8+5/*43*/, 4*8+4/*36*/,
+ 5*8+3/*29*/, 6*8+2/*22*/, 7*8+1/*15*/, 7*8+2/*23*/, 6*8+3/*30*/, 5*8+4/*37*/, 4*8+5/*44*/, 3*8+6/*51*/,
+ 2*8+7/*58*/, 3*8+7/*59*/, 4*8+6/*52*/, 5*8+5/*45*/, 6*8+4/*38*/, 7*8+3/*31*/, 7*8+4/*39*/, 6*8+5/*46*/,
+ 7*8+6/*53*/, 4*8+7/*60*/, 5*8+7/*61*/, 6*8+6/*54*/, 7*8+5/*47*/, 7*8+6/*55*/, 6*8+7/*62*/, 7*8+7/*63*/
+};
+
+
+
+
+
+/* Bit masks used by bit i/o operations. */
+
+
+
+static unsigned int bitMask[] = {0xffffffff,0x7fffffff,0x3fffffff,0x1fffffff,
+ 0x0fffffff, 0x07ffffff, 0x03ffffff, 0x01ffffff,
+ 0x00ffffff, 0x007fffff, 0x003fffff, 0x001fffff,
+ 0x000fffff, 0x0007ffff, 0x0003ffff, 0x0001ffff,
+ 0x0000ffff, 0x00007fff, 0x00003fff, 0x00001fff,
+ 0x00000fff, 0x000007ff, 0x000003ff, 0x000001ff,
+ 0x000000ff, 0x0000007f, 0x0000003f, 0x0000001f,
+ 0x0000000f, 0x00000007, 0x00000003, 0x00000001};
+
+static unsigned int rBitMask[] = {0xffffffff,0xfffffffe,0xfffffffc,0xfffffff8,
+ 0xfffffff0, 0xffffffe0, 0xffffffc0, 0xffffff80,
+ 0xffffff00, 0xfffffe00, 0xfffffc00, 0xfffff800,
+ 0xfffff000, 0xffffe000, 0xffffc000, 0xffff8000,
+ 0xffff0000, 0xfffe0000, 0xfffc0000, 0xfff80000,
+ 0xfff00000, 0xffe00000, 0xffc00000, 0xff800000,
+ 0xff000000, 0xfe000000, 0xfc000000, 0xf8000000,
+ 0xf0000000, 0xe0000000, 0xc0000000, 0x80000000};
+
+static unsigned int bitTest[] = { 0x80000000,0x40000000,0x20000000,0x10000000,
+ 0x08000000, 0x04000000, 0x02000000, 0x01000000,
+ 0x00800000, 0x00400000, 0x00200000, 0x00100000,
+ 0x00080000, 0x00040000, 0x00020000, 0x00010000,
+ 0x00008000, 0x00004000, 0x00002000, 0x00001000,
+ 0x00000800, 0x00000400, 0x00000200, 0x00000100,
+ 0x00000080, 0x00000040, 0x00000020, 0x00000010,
+ 0x00000008, 0x00000004, 0x00000002, 0x00000001};
+
+
+
+
+
+DecoderClass::DecoderClass(VideoDecoder* vid_stream,
+ MpegVideoStream* mpegVideoStream) {
+
+ this->vid_stream=vid_stream;
+ this->mpegVideoStream=mpegVideoStream;
+
+
+#ifdef INTEL
+ lmmx=mm_support();
+#else
+ lmmx=false;
+ DEBUG_DECODERCLASS(cout << "no INTEL arch- disable MMX in decoderClass"<<endl;)
+
+#endif
+
+ if (lmmx==true) {
+ lmmx=4;
+ }
+
+ int i;
+ for(i=0;i<64;i++) {
+ zigzag_direct[i]=zigzag_direct_nommx[i];
+ }
+ if (lmmx) {
+ for(i=0;i<64;i++) {
+ // they are different !!!
+ zigzag_direct[i]=zigzag_direct_mmx[i];
+ }
+ }
+ for(i=64;i<256;i++) {
+ zigzag_direct[i]=0;
+ }
+ resetDCT();
+ reconptr = dct_recon[0];
+
+}
+
+
+DecoderClass::~DecoderClass() {
+}
+
+
+
+int DecoderClass::decodeDCTDCSizeLum() {
+ unsigned int macro_val;
+ unsigned int index;
+ index=mpegVideoStream->showBits(5);
+ if (index < 31) {
+ macro_val = dct_dc_size_luminance[index].value;
+ mpegVideoStream->flushBits(dct_dc_size_luminance[index].num_bits);
+ } else {
+ index=mpegVideoStream->showBits(9);
+ index -= 0x1f0;
+ macro_val = dct_dc_size_luminance1[index].value;
+ mpegVideoStream->flushBits(dct_dc_size_luminance1[index].num_bits);
+ }
+ return macro_val;
+}
+
+
+int DecoderClass::decodeDCTDCSizeChrom() {
+ unsigned int macro_val;
+ unsigned int index;
+ index=mpegVideoStream->showBits(5);
+ if (index < 31) {
+ macro_val = dct_dc_size_chrominance[index].value;
+ mpegVideoStream->flushBits(dct_dc_size_chrominance[index].num_bits);
+ }else {
+ index=mpegVideoStream->showBits(10);
+ index -= 0x3e0;
+ macro_val = dct_dc_size_chrominance1[index].value;
+ mpegVideoStream->flushBits(dct_dc_size_chrominance1[index].num_bits);
+ }
+ return macro_val;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DecodeMBAddrInc --
+ *
+ * Huffman DecoderClass for macro_block_address_increment; the location
+ * in which the result will be placed is being passed as argument.
+ * The decoded value is obtained by doing a table lookup on
+ * mb_addr_inc.
+ *
+ * Results:
+ * The decoded value for macro_block_address_increment or MPGDECODE_ERROR
+ * for unbound values will be placed in the location specified.
+ *
+ * Side effects:
+ * Bit stream is irreversibly parsed.
+ *
+ *--------------------------------------------------------------
+ */
+int DecoderClass::decodeMBAddrInc() {
+ unsigned int index;
+ int val;
+ index=mpegVideoStream->showBits(11);
+ val = mb_addr_inc[index].value;
+ mpegVideoStream->flushBits(mb_addr_inc[index].num_bits);
+ if (mb_addr_inc[index].num_bits==0) {
+ DEBUG_DECODERCLASS(cout<<"num_bits==0"<<endl;)
+ val=1;
+ }
+
+ if (val == -1) {
+ DEBUG_DECODERCLASS(cout <<"EROR: decodeMBAddrInc"<<endl;)
+ val=MB_STUFFING;
+ }
+ return val;
+
+}
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DecodeMotionVectors --
+ *
+ * Huffman DecoderClass for the various motion vectors, including
+ * motion_horizontal_forward_code, motion_vertical_forward_code,
+ * motion_horizontal_backward_code, motion_vertical_backward_code.
+ * Location where the decoded result will be placed is being passed
+ * as argument. The decoded values are obtained by doing a table
+ * lookup on motion_vectors.
+ *
+ * Results:
+ * The decoded value for the motion vector or MPGDECODE_ERROR for unbound
+ * values will be placed in the location specified.
+ *
+ * Side effects:
+ * Bit stream is irreversibly parsed.
+ *
+ *--------------------------------------------------------------
+ */
+int DecoderClass::decodeMotionVectors() {
+ unsigned int index;
+ int value;
+ index=mpegVideoStream->showBits(11);
+ value = motion_vectors[index].code;
+
+ mpegVideoStream->flushBits(motion_vectors[index].num_bits);
+ return value;
+}
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DecodeCBP --
+ *
+ * Huffman DecoderClass for coded_block_pattern; location in which the
+ * decoded result will be placed is being passed as argument. The
+ * decoded values are obtained by doing a table lookup on
+ * coded_block_pattern.
+ *
+ * Results:
+ * The decoded value for coded_block_pattern or MPGDECODE_ERROR for unbound
+ * values will be placed in the location specified.
+ *
+ * Side effects:
+ * Bit stream is irreversibly parsed.
+ *
+ *--------------------------------------------------------------
+ */
+int DecoderClass::decodeCBP() {
+ unsigned int index;
+ unsigned int coded_bp;
+ index=mpegVideoStream->showBits(9);
+ coded_bp = coded_block_pattern[index].cbp;
+ mpegVideoStream->flushBits(coded_block_pattern[index].num_bits);
+ return coded_bp;
+}
+
+
+
+
+
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DecodeMBTypeB --
+ *
+ * Huffman Decoder for macro_block_type in bidirectionally-coded
+ * pictures;locations in which the decoded results: macroblock_quant,
+ * macroblock_motion_forward, macro_block_motion_backward,
+ * macroblock_pattern, macro_block_intra, will be placed are
+ * being passed as argument. The decoded values are obtained by
+ * doing a table lookup on mb_type_B.
+ *
+ * Results:
+ * The various decoded values for macro_block_type in
+ * bidirectionally-coded pictures or MPGDECODE_ERROR for unbound values will
+ * be placed in the locations specified.
+ *
+ * Side effects:
+ * Bit stream is irreversibly parsed.
+ *
+ *--------------------------------------------------------------
+ */
+void DecoderClass::decodeMBTypeB(int& quant,int& motion_fwd,
+ int& motion_bwd,int& pat,int& intra){
+ unsigned int index;
+
+ index=mpegVideoStream->showBits(6);
+
+ quant = mb_type_B[index].mb_quant;
+ motion_fwd = mb_type_B[index].mb_motion_forward;
+ motion_bwd = mb_type_B[index].mb_motion_backward;
+ pat = mb_type_B[index].mb_pattern;
+ intra = mb_type_B[index].mb_intra;
+ if (index == 0) {
+ DEBUG_DECODERCLASS(cout << "error in decodeMBTypeB"<<endl;)
+ }
+ mpegVideoStream->flushBits(mb_type_B[index].num_bits);
+}
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DecodeMBTypeI --
+ *
+ * Huffman Decoder for macro_block_type in intra-coded pictures;
+ * locations in which the decoded results: macroblock_quant,
+ * macroblock_motion_forward, macro_block_motion_backward,
+ * macroblock_pattern, macro_block_intra, will be placed are
+ * being passed as argument.
+ *
+ * Results:
+ * The various decoded values for macro_block_type in intra-coded
+ * pictures or MPGDECODE_ERROR for unbound values will be placed in the
+ * locations specified.
+ *
+ * Side effects:
+ * Bit stream is irreversibly parsed.
+ *
+ *--------------------------------------------------------------
+ */
+void DecoderClass::decodeMBTypeI(int& quant,int& motion_fwd,
+ int& motion_bwd,int& pat,int& intra) {
+
+ unsigned int index;
+ static int quantTbl[4] = {MPGDECODE_ERROR, 1, 0, 0};
+
+ index=mpegVideoStream->showBits(2);
+
+ motion_fwd = 0;
+ motion_bwd = 0;
+ pat = 0;
+ intra = 1;
+ quant = quantTbl[index];
+ if (quant == MPGDECODE_ERROR) {
+ DEBUG_DECODERCLASS(cout << "decodeMBTypeI Error"<<endl;)
+ }
+ if (index) {
+ mpegVideoStream->flushBits (1 + quant);
+ }
+
+}
+
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DecodeMBTypeP --
+ *
+ * Huffman Decoder for macro_block_type in predictive-coded pictures;
+ * locations in which the decoded results: macroblock_quant,
+ * macroblock_motion_forward, macro_block_motion_backward,
+ * macroblock_pattern, macro_block_intra, will be placed are
+ * being passed as argument. The decoded values are obtained by
+ * doing a table lookup on mb_type_P.
+ *
+ * Results:
+ * The various decoded values for macro_block_type in
+ * predictive-coded pictures or MPGDECODE_ERROR for unbound values will be
+ * placed in the locations specified.
+ *
+ * Side effects:
+ * Bit stream is irreversibly parsed.
+ *
+ *--------------------------------------------------------------
+ */
+void DecoderClass::decodeMBTypeP(int& quant,int& motion_fwd,
+ int& motion_bwd,int& pat,int& intra) {
+
+ unsigned int index;
+
+ index=mpegVideoStream->showBits(6);
+
+ quant = mb_type_P[index].mb_quant;
+ motion_fwd = mb_type_P[index].mb_motion_forward;
+ motion_bwd = mb_type_P[index].mb_motion_backward;
+ pat = mb_type_P[index].mb_pattern;
+ intra = mb_type_P[index].mb_intra;
+ if (index == 0) {
+ DEBUG_DECODERCLASS(cout << "error in decodeMBTypeP"<<endl;)
+ }
+ mpegVideoStream->flushBits(mb_type_P[index].num_bits);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * decodeDCTCoeff --
+ *
+ * Huffman Decoder for dct_coeff_first and dct_coeff_next;
+ * locations where the results of decoding: run and level, are to
+ * be placed and also the type of DCT coefficients, either
+ * dct_coeff_first or dct_coeff_next, are being passed as argument.
+ *
+ * The decoder first examines the next 8 bits in the input stream,
+ * and perform according to the following cases:
+ *
+ * '0000 0000' - examine 8 more bits (i.e. 16 bits total) and
+ * perform a table lookup on dct_coeff_tbl_0.
+ * One more bit is then examined to determine the sign
+ * of level.
+ *
+ * '0000 0001' - examine 4 more bits (i.e. 12 bits total) and
+ * perform a table lookup on dct_coeff_tbl_1.
+ * One more bit is then examined to determine the sign
+ * of level.
+ *
+ * '0000 0010' - examine 2 more bits (i.e. 10 bits total) and
+ * perform a table lookup on dct_coeff_tbl_2.
+ * One more bit is then examined to determine the sign
+ * of level.
+ *
+ * '0000 0011' - examine 2 more bits (i.e. 10 bits total) and
+ * perform a table lookup on dct_coeff_tbl_3.
+ * One more bit is then examined to determine the sign
+ * of level.
+ *
+ * otherwise - perform a table lookup on dct_coeff_tbl. If the
+ * value of run is not ESCAPE, extract one more bit
+ * to determine the sign of level; otherwise 6 more
+ * bits will be extracted to obtain the actual value
+ * of run , and then 8 or 16 bits to get the value of level.
+ *
+ *
+ *
+ * Results:
+ * The decoded values of run and level or MPGDECODE_ERROR
+ * for unbound values
+ * are placed in the locations specified.
+ *
+ * Side effects:
+ * Bit stream is irreversibly parsed.
+ *
+ *--------------------------------------------------------------
+ */
+
+void DecoderClass::decodeDCTCoeff(unsigned short int* dct_coeff_tbl,
+ unsigned RUNTYPE& run,int& level) {
+
+
+ unsigned int temp, index;
+ unsigned int value, next32bits, flushed;
+
+ /*
+ * Grab the next 32 bits and use it to improve performance of
+ * getting the bits to parse. Thus, calls are translated as:
+ *
+ * show_bitsX <--> next32bits >> (32-X)
+ * get_bitsX <--> val = next32bits >> (32-flushed-X);
+ * flushed += X;
+ * next32bits &= bitMask[flushed];
+ * flush_bitsX <--> flushed += X;
+ * next32bits &= bitMask[flushed];
+ *
+ */
+ next32bits=mpegVideoStream->showBits32();
+
+ flushed = 0;
+
+ /* show_bits8(index); */
+ index = next32bits >> 24;
+
+ if (index > 3) {
+ value = dct_coeff_tbl[index];
+ run = (value & RUN_MASK) >> RUN_SHIFT;
+ if (run == END_OF_BLOCK) {
+ level = END_OF_BLOCK;
+ }
+ else {
+ /* num_bits = (value & NUM_MASK) + 1; */
+ /* flush_bits(num_bits); */
+ flushed = (value & NUM_MASK) + 1;
+ next32bits &= bitMask[flushed];
+ if (run != ESCAPE) {
+ level = (value & LEVEL_MASK) >> LEVEL_SHIFT;
+ /* get_bits1(value); */
+ /* if (value) *level = -*level; */
+ if (next32bits >> (31-flushed)) level = -level;
+ flushed++;
+ /* next32bits &= bitMask[flushed]; last op before update */
+ }
+ else { /* *run == ESCAPE */
+ /* get_bits14(temp); */
+ temp = next32bits >> (18-flushed);
+ flushed += 14;
+ next32bits &= bitMask[flushed];
+ run = temp >> 8;
+ temp &= 0xff;
+ if (temp == 0) {
+ /* get_bits8(*level); */
+ level = next32bits >> (24-flushed);
+ flushed += 8;
+ /* next32bits &= bitMask[flushed]; last op before update */
+ assert(level >= 128);
+ } else if (temp != 128) {
+ /* Grab sign bit */
+ level = ((int) (temp << 24)) >> 24;
+ } else {
+ /* get_bits8(*level); */
+ level = next32bits >> (24-flushed);
+ flushed += 8;
+ /* next32bits &= bitMask[flushed]; last op before update */
+ level = level - 256;
+ assert(level <= -128 && level >= -255);
+ }
+ }
+ /* Update bitstream... */
+ mpegVideoStream->flushBitsDirect(flushed);
+ }
+ }
+ else {
+ if (index == 2) {
+ /* show_bits10(index); */
+ index = next32bits >> 22;
+ value = dct_coeff_tbl_2[index & 3];
+ }
+ else if (index == 3) {
+ /* show_bits10(index); */
+ index = next32bits >> 22;
+ value = dct_coeff_tbl_3[index & 3];
+ }
+ else if (index) { /* index == 1 */
+ /* show_bits12(index); */
+ index = next32bits >> 20;
+ value = dct_coeff_tbl_1[index & 15];
+ }
+ else { /* index == 0 */
+ /* show_bits16(index); */
+ index = next32bits >> 16;
+ value = dct_coeff_tbl_0[index & 255];
+ }
+ run = (value & RUN_MASK) >> RUN_SHIFT;
+ level = (value & LEVEL_MASK) >> LEVEL_SHIFT;
+
+ /*
+ * Fold these operations together to make it fast...
+ */
+ /* num_bits = (value & NUM_MASK) + 1; */
+ /* flush_bits(num_bits); */
+ /* get_bits1(value); */
+ /* if (value) *level = -*level; */
+
+ flushed = (value & NUM_MASK) + 2;
+ if ((next32bits >> (32-flushed)) & 0x1) level = -level;
+
+ /* Update bitstream ... */
+ mpegVideoStream->flushBitsDirect(flushed);
+
+ }
+
+}
+
+
+void DecoderClass::resetDCT() {
+ /* Reset past dct dc y, cr, and cb values. */
+
+ dct_dc_y_past = 1024 << 3;
+ dct_dc_cr_past = 1024 << 3;
+ dct_dc_cb_past = 1024 << 3;
+
+}
+
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ParseReconBlock --
+ *
+ * Parse values for block structure from bitstream.
+ * n is an indication of the position of the block within
+ * the macroblock (i.e. 0-5) and indicates the type of
+ * block (i.e. luminance or chrominance). Reconstructs
+ * coefficients from values parsed and puts in
+ * block.dct_recon array in vid stream structure.
+ * sparseFlag is set when the block contains only one
+ * coeffictient and is used by the IDCT.
+ *
+ * Results:
+ *
+ *
+ * Side effects:
+ * Bit stream irreversibly parsed.
+ *
+ *--------------------------------------------------------------
+ */
+
+
+void DecoderClass::ParseReconBlock(int& n,int& mb_intra,
+ unsigned int& qscale,
+ int& lflag,
+ unsigned int* iqmatrixptr,
+ unsigned int* niqmatrixptr) {
+
+
+
+
+ int coeffCount=0;
+ if (mpegVideoStream->hasBytes(512) == false) {
+ DEBUG_DECODERCLASS(cout << "cannot get 512 raw bytes"<<endl;)
+ return;
+ }
+
+ {
+ /*
+ * Copy the VideoDecoder fields curBits, bitOffset, and bitBuffer
+ * into local variables with the same names, so the macros use the
+ * local variables instead. This allows register allocation and
+ * can provide 1-2 fps speedup. On machines with not so many registers,
+ * don't do this.
+ */
+ int size, pos, coeff;
+ int level;
+ unsigned RUNTYPE run;
+ unsigned RUNTYPE i;
+
+
+ memset((char *) dct_recon, 0, 64*sizeof(short int));
+
+ if (mb_intra) {
+
+ if (n < 4) {
+
+ /*
+ * Get the luminance bits. This code has been hand optimized to
+ * get by the normal bit parsing routines. We get some speedup
+ * by grabbing the next 16 bits and parsing things locally.
+ * Thus, calls are translated as:
+ *
+ * show_bitsX <--> next16bits >> (16-X)
+ * get_bitsX <--> val = next16bits >> (16-flushed-X);
+ * flushed += X;
+ * next16bits &= bitMask[flushed];
+ * flush_bitsX <--> flushed += X;
+ * next16bits &= bitMask[flushed];
+ *
+ * I've streamlined the code a lot, so that we don't have to mask
+ * out the low order bits and a few of the extra adds are removed.
+ * bsmith
+ */
+ unsigned int next16bits, index, flushed;
+ next16bits=mpegVideoStream->showBits16();
+
+ index = next16bits >> (16-5);
+ if (index < 31) {
+ size = dct_dc_size_luminance[index].value;
+ flushed = dct_dc_size_luminance[index].num_bits;
+ } else {
+ index = next16bits >> (16-9);
+ index -= 0x1f0;
+ size = dct_dc_size_luminance1[index].value;
+ flushed = dct_dc_size_luminance1[index].num_bits;
+ }
+ next16bits &= bitMask[(16+flushed)&0x1f];
+ if (size != 0) {
+ flushed += size;
+ coeff = next16bits >> (16-flushed);
+ if (!(coeff & bitTest[32-size])) {
+ coeff++;
+ coeff|= rBitMask[size&0x1f];
+ }
+ coeff <<= 3;
+ } else {
+ coeff = 0;
+ }
+ mpegVideoStream->flushBitsDirect(flushed);
+
+ if ( (n == 0) && (lflag) ) {
+ coeff += 1024;
+ } else {
+ coeff += dct_dc_y_past;
+ }
+ dct_dc_y_past = coeff;
+
+ } else { /* n = 4 or 5 */
+ /*
+ * Get the chrominance bits. This code has been hand optimized to
+ * as described above
+ */
+
+ unsigned int next16bits, index, flushed;
+ next16bits=mpegVideoStream->showBits16();
+
+ index = next16bits >> (16-5);
+ if (index < 31) {
+ size = dct_dc_size_chrominance[index].value;
+ flushed = dct_dc_size_chrominance[index].num_bits;
+ } else {
+ index = next16bits >> (16-10);
+ index -= 0x3e0;
+ size = dct_dc_size_chrominance1[index].value;
+ flushed = dct_dc_size_chrominance1[index].num_bits;
+ }
+ next16bits &= bitMask[(16+flushed)&0x1f];
+
+ if (size != 0) {
+ flushed += size;
+ coeff = next16bits >> (16-flushed);
+ if (!(coeff & bitTest[32-size])) {
+ coeff++;
+ coeff|=rBitMask[size&0x1f];
+ }
+ coeff <<= 3;
+ } else {
+ coeff = 0;
+ }
+ mpegVideoStream->flushBitsDirect(flushed);
+
+ /* We test 5 first; a result of the mixup of Cr and Cb */
+
+ if (n == 5) {
+ if (lflag) {
+ coeff += 1024;
+ } else {
+ coeff += dct_dc_cr_past;
+ }
+ dct_dc_cr_past = coeff;
+ } else {
+ if (lflag) {
+ coeff += 1024;
+ } else {
+ coeff += dct_dc_cb_past;
+ }
+ dct_dc_cb_past = coeff;
+ }
+ }
+
+ coeff <<= lmmx;
+ reconptr[0] = coeff;
+
+ pos=0;
+ i = 0;
+ coeffCount = (coeff != 0);
+
+
+ // we never have d_types so there is no check
+
+
+ while(1) {
+ decodeDCTCoeff(dct_coeff_next,run,level);
+
+ if (run >=END_OF_BLOCK) {
+ break;
+ }
+ i++;
+ i+=run;
+ pos = zigzag_direct[i&0x3f];
+
+
+ coeff = (level * qscale * iqmatrixptr[pos]) >> 3 ;
+
+ if (level < 0) {
+ coeff += (1 - (coeff & 1));
+ } else {
+ coeff -= (1 - (coeff & 1));
+ }
+
+ coeff <<= lmmx;
+
+ reconptr[pos] = coeff;
+ coeffCount++;
+
+ }
+ mpegVideoStream->flushBitsDirect(2);
+
+ goto end;
+
+ } else { /* non-intra-coded macroblock */
+
+ decodeDCTCoeff(dct_coeff_first,run,level);
+ i = run;
+
+ pos = zigzag_direct[i&0x3f];
+
+ /* quantizes and oddifies each coefficient */
+ if (level < 0) {
+ coeff = ((level - 1) * qscale * niqmatrixptr[pos]) >>3;
+ if ((coeff & 1) == 0) {coeff++;}
+ } else {
+ coeff = ((level + 1) * qscale * niqmatrixptr[pos]) >>3;
+ coeff = (coeff-1) | 1;
+ }
+
+ coeff <<= lmmx;
+
+ reconptr[pos] = coeff;
+ coeffCount = (coeff!=0);
+
+ // we never have d_type pictures here, we do not support them
+
+ while(1) {
+ decodeDCTCoeff(dct_coeff_next,run,level);
+
+ if (run >= END_OF_BLOCK) {
+ break;
+ }
+ i++;
+ i+=run;
+ pos = zigzag_direct[i&0x3f];
+
+ if (level < 0) {
+ coeff = ((level - 1) * qscale * niqmatrixptr[pos]) >>3;
+ if ((coeff & 1) == 0) {coeff++;}
+ } else {
+ coeff = ((level + 1) * qscale * niqmatrixptr[pos]) >> 3;
+ coeff = (coeff-1) | 1;
+ }
+
+ coeff <<= lmmx;
+
+ reconptr[pos] = coeff;
+ coeffCount++;
+ } /* end while */
+
+ mpegVideoStream->flushBitsDirect(2);
+ goto end;
+ }
+
+ end:
+ if (coeffCount == 1) {
+ if (lmmx) {
+ emms();
+ reconptr[pos]>>=lmmx;
+ j_rev_dct_sparse (reconptr, pos);
+ //IDCT_mmx(reconptr);
+ } else {
+ j_rev_dct_sparse (reconptr, pos);
+ }
+
+ } else {
+
+ if (lmmx) {
+ IDCT_mmx(reconptr);
+ } else {
+ j_rev_dct(reconptr);
+ }
+
+ }
+ }
+
+ // we call this at the edn of the "critical sections"
+ /*
+ if (lmmx) {
+ emms();
+ }
+ */
+
+ return;
+
+}
+
+
+
+
+
+void DecoderClass::print() {
+ int i;
+ for(i=0;i<64;i++) {
+ printf(" %d ",zigzag_direct[i]);
+ }
+ printf("\n");
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/lib/mpegplay/decoderClass.h b/mpeglib/lib/mpegplay/decoderClass.h
new file mode 100644
index 00000000..2e969edc
--- /dev/null
+++ b/mpeglib/lib/mpegplay/decoderClass.h
@@ -0,0 +1,96 @@
+/*
+ class for decoders
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __DECODERCLASS_H
+#define __DECODERCLASS_H
+
+
+
+#include "videoDecoder.h"
+#include "mpegVideoStream.h"
+#include "decoderTables.h"
+#include "slice.h"
+#include "proto.h"
+#include "../util/mmx/mmx.h"
+#include "mmxidct.h"
+#include "picture.h"
+
+/* Special values for DCT Coefficients */
+#define END_OF_BLOCK 62
+#define ESCAPE 61
+
+
+/* DCT coeff tables. */
+#define RUN_MASK 0xfc00
+#define LEVEL_MASK 0x03f0
+#define NUM_MASK 0x000f
+#define RUN_SHIFT 10
+#define LEVEL_SHIFT 4
+#define RUNTYPE char
+
+class DecoderClass {
+
+ int lmmx;
+ int zigzag_direct[256];
+
+ /* Block structure. */
+
+ short int dct_recon[8][8]; /* Reconstructed dct coeff matrix. */
+ int dct_dc_y_past; /* Past lum. dc dct coefficient. */
+ int dct_dc_cr_past; /* Past cr dc dct coefficient. */
+ int dct_dc_cb_past; /* Past cb dc dct coefficient. */
+ short int *reconptr; /* reconptr = dct_recon[0]; */
+
+ class VideoDecoder* vid_stream;
+ class MpegVideoStream* mpegVideoStream;
+ public:
+ DecoderClass(class VideoDecoder* vid_stream,
+ class MpegVideoStream* mpegVideoStream);
+ ~DecoderClass();
+ int decodeDCTDCSizeLum();
+ int decodeDCTDCSizeChrom();
+ int decodeMotionVectors();
+ int decodeCBP();
+
+ inline short int* getDCT() { return ((short int*) &(dct_recon[0][0]));}
+ void resetDCT();
+
+ void decodeMBTypeB(int& quant,int& motion_fwd,
+ int& motion_bwd,int& pat,int& intra);
+
+ void decodeMBTypeI(int& quant,int& motion_fwd,
+ int& motion_bwd,int& pat,int& intra);
+
+ void decodeMBTypeP(int& quant,int& motion_fwd,
+ int& motion_bwd,int& pat,int& intra);
+
+ void ParseReconBlock(int& n,int& mb_intra,unsigned int& quant_scale,
+ int& lflag,
+ unsigned int* iqmatrixptr,
+ unsigned int* niqmatrixptr);
+ int decodeMBAddrInc();
+
+
+ void print();
+
+ private:
+
+
+ inline void decodeDCTCoeff(unsigned short int* dct_coeff_tbl,
+ unsigned RUNTYPE& run,
+ int& level);
+
+
+
+};
+#endif
diff --git a/mpeglib/lib/mpegplay/decoderTables.cpp b/mpeglib/lib/mpegplay/decoderTables.cpp
new file mode 100644
index 00000000..9ada4901
--- /dev/null
+++ b/mpeglib/lib/mpegplay/decoderTables.cpp
@@ -0,0 +1,594 @@
+/*
+ * decoders.c
+ *
+ * This file contains all the routines for Huffman decoding required in
+ * MPEG
+ *
+ */
+
+
+#include "decoderTables.h"
+
+using namespace std;
+
+
+/* Decoding table for macroblock_address_increment */
+mb_addr_inc_entry mb_addr_inc[2048];
+
+/* Decoding table for macroblock_type in predictive-coded pictures */
+mb_type_entry mb_type_P[64];
+
+/* Decoding table for macroblock_type in bidirectionally-coded pictures */
+mb_type_entry mb_type_B[64];
+
+/* Decoding table for motion vectors */
+motion_vectors_entry motion_vectors[2048];
+
+/* Decoding table for coded_block_pattern */
+
+coded_block_pattern_entry coded_block_pattern[512] =
+{ {(unsigned int)MPGDECODE_ERROR, 0}, {(unsigned int)MPGDECODE_ERROR, 0}, {39, 9}, {27, 9}, {59, 9}, {55, 9}, {47, 9}, {31, 9},
+ {58, 8}, {58, 8}, {54, 8}, {54, 8}, {46, 8}, {46, 8}, {30, 8}, {30, 8},
+ {57, 8}, {57, 8}, {53, 8}, {53, 8}, {45, 8}, {45, 8}, {29, 8}, {29, 8},
+ {38, 8}, {38, 8}, {26, 8}, {26, 8}, {37, 8}, {37, 8}, {25, 8}, {25, 8},
+ {43, 8}, {43, 8}, {23, 8}, {23, 8}, {51, 8}, {51, 8}, {15, 8}, {15, 8},
+ {42, 8}, {42, 8}, {22, 8}, {22, 8}, {50, 8}, {50, 8}, {14, 8}, {14, 8},
+ {41, 8}, {41, 8}, {21, 8}, {21, 8}, {49, 8}, {49, 8}, {13, 8}, {13, 8},
+ {35, 8}, {35, 8}, {19, 8}, {19, 8}, {11, 8}, {11, 8}, {7, 8}, {7, 8},
+ {34, 7}, {34, 7}, {34, 7}, {34, 7}, {18, 7}, {18, 7}, {18, 7}, {18, 7},
+ {10, 7}, {10, 7}, {10, 7}, {10, 7}, {6, 7}, {6, 7}, {6, 7}, {6, 7},
+ {33, 7}, {33, 7}, {33, 7}, {33, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7},
+ {9, 7}, {9, 7}, {9, 7}, {9, 7}, {5, 7}, {5, 7}, {5, 7}, {5, 7},
+ {63, 6}, {63, 6}, {63, 6}, {63, 6}, {63, 6}, {63, 6}, {63, 6}, {63, 6},
+ {3, 6}, {3, 6}, {3, 6}, {3, 6}, {3, 6}, {3, 6}, {3, 6}, {3, 6},
+ {36, 6}, {36, 6}, {36, 6}, {36, 6}, {36, 6}, {36, 6}, {36, 6}, {36, 6},
+ {24, 6}, {24, 6}, {24, 6}, {24, 6}, {24, 6}, {24, 6}, {24, 6}, {24, 6},
+ {62, 5}, {62, 5}, {62, 5}, {62, 5}, {62, 5}, {62, 5}, {62, 5}, {62, 5},
+ {62, 5}, {62, 5}, {62, 5}, {62, 5}, {62, 5}, {62, 5}, {62, 5}, {62, 5},
+ {2, 5}, {2, 5}, {2, 5}, {2, 5}, {2, 5}, {2, 5}, {2, 5}, {2, 5},
+ {2, 5}, {2, 5}, {2, 5}, {2, 5}, {2, 5}, {2, 5}, {2, 5}, {2, 5},
+ {61, 5}, {61, 5}, {61, 5}, {61, 5}, {61, 5}, {61, 5}, {61, 5}, {61, 5},
+ {61, 5}, {61, 5}, {61, 5}, {61, 5}, {61, 5}, {61, 5}, {61, 5}, {61, 5},
+ {1, 5}, {1, 5}, {1, 5}, {1, 5}, {1, 5}, {1, 5}, {1, 5}, {1, 5},
+ {1, 5}, {1, 5}, {1, 5}, {1, 5}, {1, 5}, {1, 5}, {1, 5}, {1, 5},
+ {56, 5}, {56, 5}, {56, 5}, {56, 5}, {56, 5}, {56, 5}, {56, 5}, {56, 5},
+ {56, 5}, {56, 5}, {56, 5}, {56, 5}, {56, 5}, {56, 5}, {56, 5}, {56, 5},
+ {52, 5}, {52, 5}, {52, 5}, {52, 5}, {52, 5}, {52, 5}, {52, 5}, {52, 5},
+ {52, 5}, {52, 5}, {52, 5}, {52, 5}, {52, 5}, {52, 5}, {52, 5}, {52, 5},
+ {44, 5}, {44, 5}, {44, 5}, {44, 5}, {44, 5}, {44, 5}, {44, 5}, {44, 5},
+ {44, 5}, {44, 5}, {44, 5}, {44, 5}, {44, 5}, {44, 5}, {44, 5}, {44, 5},
+ {28, 5}, {28, 5}, {28, 5}, {28, 5}, {28, 5}, {28, 5}, {28, 5}, {28, 5},
+ {28, 5}, {28, 5}, {28, 5}, {28, 5}, {28, 5}, {28, 5}, {28, 5}, {28, 5},
+ {40, 5}, {40, 5}, {40, 5}, {40, 5}, {40, 5}, {40, 5}, {40, 5}, {40, 5},
+ {40, 5}, {40, 5}, {40, 5}, {40, 5}, {40, 5}, {40, 5}, {40, 5}, {40, 5},
+ {20, 5}, {20, 5}, {20, 5}, {20, 5}, {20, 5}, {20, 5}, {20, 5}, {20, 5},
+ {20, 5}, {20, 5}, {20, 5}, {20, 5}, {20, 5}, {20, 5}, {20, 5}, {20, 5},
+ {48, 5}, {48, 5}, {48, 5}, {48, 5}, {48, 5}, {48, 5}, {48, 5}, {48, 5},
+ {48, 5}, {48, 5}, {48, 5}, {48, 5}, {48, 5}, {48, 5}, {48, 5}, {48, 5},
+ {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5},
+ {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5},
+ {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4},
+ {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4},
+ {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4},
+ {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4},
+ {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4},
+ {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4},
+ {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4},
+ {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4},
+ {8, 4}, {8, 4}, {8, 4}, {8, 4}, {8, 4}, {8, 4}, {8, 4}, {8, 4},
+ {8, 4}, {8, 4}, {8, 4}, {8, 4}, {8, 4}, {8, 4}, {8, 4}, {8, 4},
+ {8, 4}, {8, 4}, {8, 4}, {8, 4}, {8, 4}, {8, 4}, {8, 4}, {8, 4},
+ {8, 4}, {8, 4}, {8, 4}, {8, 4}, {8, 4}, {8, 4}, {8, 4}, {8, 4},
+ {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4},
+ {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4},
+ {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4},
+ {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, {4, 4},
+ {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3},
+ {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3},
+ {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3},
+ {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3},
+ {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3},
+ {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3},
+ {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3},
+ {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}, {60, 3}
+};
+
+/* Decoding tables for dct_dc_size_luminance */
+dct_dc_size_entry dct_dc_size_luminance[32] =
+{ {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2},
+ {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2},
+ {0, 3}, {0, 3}, {0, 3}, {0, 3}, {3, 3}, {3, 3}, {3, 3}, {3, 3},
+ {4, 3}, {4, 3}, {4, 3}, {4, 3}, {5, 4}, {5, 4}, {6, 5}, {(unsigned int)MPGDECODE_ERROR, 0}
+};
+
+dct_dc_size_entry dct_dc_size_luminance1[16] =
+{ {7, 6}, {7, 6}, {7, 6}, {7, 6}, {7, 6}, {7, 6}, {7, 6}, {7, 6},
+ {8, 7}, {8, 7}, {8, 7}, {8, 7}, {9, 8}, {9, 8}, {10, 9}, {11, 9}
+};
+
+/* Decoding table for dct_dc_size_chrominance */
+dct_dc_size_entry dct_dc_size_chrominance[32] =
+{ {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2},
+ {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2},
+ {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2},
+ {3, 3}, {3, 3}, {3, 3}, {3, 3}, {4, 4}, {4, 4}, {5, 5}, {(unsigned int)MPGDECODE_ERROR, 0}
+};
+
+dct_dc_size_entry dct_dc_size_chrominance1[32] =
+{ {6, 6}, {6, 6}, {6, 6}, {6, 6}, {6, 6}, {6, 6}, {6, 6}, {6, 6},
+ {6, 6}, {6, 6}, {6, 6}, {6, 6}, {6, 6}, {6, 6}, {6, 6}, {6, 6},
+ {7, 7}, {7, 7}, {7, 7}, {7, 7}, {7, 7}, {7, 7}, {7, 7}, {7, 7},
+ {8, 8}, {8, 8}, {8, 8}, {8, 8}, {9, 9}, {9, 9}, {10, 10}, {11, 10}
+};
+
+/* DCT coeff tables. */
+
+unsigned short int dct_coeff_tbl_0[256] =
+{
+0xffff, 0xffff, 0xffff, 0xffff,
+0xffff, 0xffff, 0xffff, 0xffff,
+0xffff, 0xffff, 0xffff, 0xffff,
+0xffff, 0xffff, 0xffff, 0xffff,
+0x052f, 0x051f, 0x050f, 0x04ff,
+0x183f, 0x402f, 0x3c2f, 0x382f,
+0x342f, 0x302f, 0x2c2f, 0x7c1f,
+0x781f, 0x741f, 0x701f, 0x6c1f,
+0x028e, 0x028e, 0x027e, 0x027e,
+0x026e, 0x026e, 0x025e, 0x025e,
+0x024e, 0x024e, 0x023e, 0x023e,
+0x022e, 0x022e, 0x021e, 0x021e,
+0x020e, 0x020e, 0x04ee, 0x04ee,
+0x04de, 0x04de, 0x04ce, 0x04ce,
+0x04be, 0x04be, 0x04ae, 0x04ae,
+0x049e, 0x049e, 0x048e, 0x048e,
+0x01fd, 0x01fd, 0x01fd, 0x01fd,
+0x01ed, 0x01ed, 0x01ed, 0x01ed,
+0x01dd, 0x01dd, 0x01dd, 0x01dd,
+0x01cd, 0x01cd, 0x01cd, 0x01cd,
+0x01bd, 0x01bd, 0x01bd, 0x01bd,
+0x01ad, 0x01ad, 0x01ad, 0x01ad,
+0x019d, 0x019d, 0x019d, 0x019d,
+0x018d, 0x018d, 0x018d, 0x018d,
+0x017d, 0x017d, 0x017d, 0x017d,
+0x016d, 0x016d, 0x016d, 0x016d,
+0x015d, 0x015d, 0x015d, 0x015d,
+0x014d, 0x014d, 0x014d, 0x014d,
+0x013d, 0x013d, 0x013d, 0x013d,
+0x012d, 0x012d, 0x012d, 0x012d,
+0x011d, 0x011d, 0x011d, 0x011d,
+0x010d, 0x010d, 0x010d, 0x010d,
+0x282c, 0x282c, 0x282c, 0x282c,
+0x282c, 0x282c, 0x282c, 0x282c,
+0x242c, 0x242c, 0x242c, 0x242c,
+0x242c, 0x242c, 0x242c, 0x242c,
+0x143c, 0x143c, 0x143c, 0x143c,
+0x143c, 0x143c, 0x143c, 0x143c,
+0x0c4c, 0x0c4c, 0x0c4c, 0x0c4c,
+0x0c4c, 0x0c4c, 0x0c4c, 0x0c4c,
+0x085c, 0x085c, 0x085c, 0x085c,
+0x085c, 0x085c, 0x085c, 0x085c,
+0x047c, 0x047c, 0x047c, 0x047c,
+0x047c, 0x047c, 0x047c, 0x047c,
+0x046c, 0x046c, 0x046c, 0x046c,
+0x046c, 0x046c, 0x046c, 0x046c,
+0x00fc, 0x00fc, 0x00fc, 0x00fc,
+0x00fc, 0x00fc, 0x00fc, 0x00fc,
+0x00ec, 0x00ec, 0x00ec, 0x00ec,
+0x00ec, 0x00ec, 0x00ec, 0x00ec,
+0x00dc, 0x00dc, 0x00dc, 0x00dc,
+0x00dc, 0x00dc, 0x00dc, 0x00dc,
+0x00cc, 0x00cc, 0x00cc, 0x00cc,
+0x00cc, 0x00cc, 0x00cc, 0x00cc,
+0x681c, 0x681c, 0x681c, 0x681c,
+0x681c, 0x681c, 0x681c, 0x681c,
+0x641c, 0x641c, 0x641c, 0x641c,
+0x641c, 0x641c, 0x641c, 0x641c,
+0x601c, 0x601c, 0x601c, 0x601c,
+0x601c, 0x601c, 0x601c, 0x601c,
+0x5c1c, 0x5c1c, 0x5c1c, 0x5c1c,
+0x5c1c, 0x5c1c, 0x5c1c, 0x5c1c,
+0x581c, 0x581c, 0x581c, 0x581c,
+0x581c, 0x581c, 0x581c, 0x581c,
+};
+
+unsigned short int dct_coeff_tbl_1[16] =
+{
+0x00bb, 0x202b, 0x103b, 0x00ab,
+0x084b, 0x1c2b, 0x541b, 0x501b,
+0x009b, 0x4c1b, 0x481b, 0x045b,
+0x0c3b, 0x008b, 0x182b, 0x441b,
+};
+
+unsigned short int dct_coeff_tbl_2[4] =
+{
+0x4019, 0x1429, 0x0079, 0x0839,
+};
+
+unsigned short int dct_coeff_tbl_3[4] =
+{
+0x0449, 0x3c19, 0x3819, 0x1029,
+};
+
+unsigned short int dct_coeff_next[256] =
+{
+0xffff, 0xffff, 0xffff, 0xffff,
+0xf7d5, 0xf7d5, 0xf7d5, 0xf7d5,
+0x0826, 0x0826, 0x2416, 0x2416,
+0x0046, 0x0046, 0x2016, 0x2016,
+0x1c15, 0x1c15, 0x1c15, 0x1c15,
+0x1815, 0x1815, 0x1815, 0x1815,
+0x0425, 0x0425, 0x0425, 0x0425,
+0x1415, 0x1415, 0x1415, 0x1415,
+0x3417, 0x0067, 0x3017, 0x2c17,
+0x0c27, 0x0437, 0x0057, 0x2817,
+0x0034, 0x0034, 0x0034, 0x0034,
+0x0034, 0x0034, 0x0034, 0x0034,
+0x1014, 0x1014, 0x1014, 0x1014,
+0x1014, 0x1014, 0x1014, 0x1014,
+0x0c14, 0x0c14, 0x0c14, 0x0c14,
+0x0c14, 0x0c14, 0x0c14, 0x0c14,
+0x0023, 0x0023, 0x0023, 0x0023,
+0x0023, 0x0023, 0x0023, 0x0023,
+0x0023, 0x0023, 0x0023, 0x0023,
+0x0023, 0x0023, 0x0023, 0x0023,
+0x0813, 0x0813, 0x0813, 0x0813,
+0x0813, 0x0813, 0x0813, 0x0813,
+0x0813, 0x0813, 0x0813, 0x0813,
+0x0813, 0x0813, 0x0813, 0x0813,
+0x0412, 0x0412, 0x0412, 0x0412,
+0x0412, 0x0412, 0x0412, 0x0412,
+0x0412, 0x0412, 0x0412, 0x0412,
+0x0412, 0x0412, 0x0412, 0x0412,
+0x0412, 0x0412, 0x0412, 0x0412,
+0x0412, 0x0412, 0x0412, 0x0412,
+0x0412, 0x0412, 0x0412, 0x0412,
+0x0412, 0x0412, 0x0412, 0x0412,
+0xfbe1, 0xfbe1, 0xfbe1, 0xfbe1,
+0xfbe1, 0xfbe1, 0xfbe1, 0xfbe1,
+0xfbe1, 0xfbe1, 0xfbe1, 0xfbe1,
+0xfbe1, 0xfbe1, 0xfbe1, 0xfbe1,
+0xfbe1, 0xfbe1, 0xfbe1, 0xfbe1,
+0xfbe1, 0xfbe1, 0xfbe1, 0xfbe1,
+0xfbe1, 0xfbe1, 0xfbe1, 0xfbe1,
+0xfbe1, 0xfbe1, 0xfbe1, 0xfbe1,
+0xfbe1, 0xfbe1, 0xfbe1, 0xfbe1,
+0xfbe1, 0xfbe1, 0xfbe1, 0xfbe1,
+0xfbe1, 0xfbe1, 0xfbe1, 0xfbe1,
+0xfbe1, 0xfbe1, 0xfbe1, 0xfbe1,
+0xfbe1, 0xfbe1, 0xfbe1, 0xfbe1,
+0xfbe1, 0xfbe1, 0xfbe1, 0xfbe1,
+0xfbe1, 0xfbe1, 0xfbe1, 0xfbe1,
+0xfbe1, 0xfbe1, 0xfbe1, 0xfbe1,
+0x0011, 0x0011, 0x0011, 0x0011,
+0x0011, 0x0011, 0x0011, 0x0011,
+0x0011, 0x0011, 0x0011, 0x0011,
+0x0011, 0x0011, 0x0011, 0x0011,
+0x0011, 0x0011, 0x0011, 0x0011,
+0x0011, 0x0011, 0x0011, 0x0011,
+0x0011, 0x0011, 0x0011, 0x0011,
+0x0011, 0x0011, 0x0011, 0x0011,
+0x0011, 0x0011, 0x0011, 0x0011,
+0x0011, 0x0011, 0x0011, 0x0011,
+0x0011, 0x0011, 0x0011, 0x0011,
+0x0011, 0x0011, 0x0011, 0x0011,
+0x0011, 0x0011, 0x0011, 0x0011,
+0x0011, 0x0011, 0x0011, 0x0011,
+0x0011, 0x0011, 0x0011, 0x0011,
+0x0011, 0x0011, 0x0011, 0x0011,
+};
+
+unsigned short int dct_coeff_first[256] =
+{
+0xffff, 0xffff, 0xffff, 0xffff,
+0xf7d5, 0xf7d5, 0xf7d5, 0xf7d5,
+0x0826, 0x0826, 0x2416, 0x2416,
+0x0046, 0x0046, 0x2016, 0x2016,
+0x1c15, 0x1c15, 0x1c15, 0x1c15,
+0x1815, 0x1815, 0x1815, 0x1815,
+0x0425, 0x0425, 0x0425, 0x0425,
+0x1415, 0x1415, 0x1415, 0x1415,
+0x3417, 0x0067, 0x3017, 0x2c17,
+0x0c27, 0x0437, 0x0057, 0x2817,
+0x0034, 0x0034, 0x0034, 0x0034,
+0x0034, 0x0034, 0x0034, 0x0034,
+0x1014, 0x1014, 0x1014, 0x1014,
+0x1014, 0x1014, 0x1014, 0x1014,
+0x0c14, 0x0c14, 0x0c14, 0x0c14,
+0x0c14, 0x0c14, 0x0c14, 0x0c14,
+0x0023, 0x0023, 0x0023, 0x0023,
+0x0023, 0x0023, 0x0023, 0x0023,
+0x0023, 0x0023, 0x0023, 0x0023,
+0x0023, 0x0023, 0x0023, 0x0023,
+0x0813, 0x0813, 0x0813, 0x0813,
+0x0813, 0x0813, 0x0813, 0x0813,
+0x0813, 0x0813, 0x0813, 0x0813,
+0x0813, 0x0813, 0x0813, 0x0813,
+0x0412, 0x0412, 0x0412, 0x0412,
+0x0412, 0x0412, 0x0412, 0x0412,
+0x0412, 0x0412, 0x0412, 0x0412,
+0x0412, 0x0412, 0x0412, 0x0412,
+0x0412, 0x0412, 0x0412, 0x0412,
+0x0412, 0x0412, 0x0412, 0x0412,
+0x0412, 0x0412, 0x0412, 0x0412,
+0x0412, 0x0412, 0x0412, 0x0412,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010,
+};
+
+/* Macro for filling up the decoding table for mb_addr_inc */
+#define ASSIGN1(start, end, step, val, num) \
+ for (i = start; i < end; i+= step) { \
+ for (j = 0; j < step; j++) { \
+ mb_addr_inc[i+j].value = val; \
+ mb_addr_inc[i+j].num_bits = num; \
+ } \
+ val--; \
+ }
+
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * init_mb_addr_inc --
+ *
+ * Initialize the VLC decoding table for macro_block_address_increment
+ *
+ * Results:
+ * The decoding table for macro_block_address_increment will
+ * be filled; illegal values will be filled as MPGDECODE_ERROR.
+ *
+ * Side effects:
+ * The global array mb_addr_inc will be filled.
+ *
+ *--------------------------------------------------------------
+ */
+static void init_mb_addr_inc()
+{
+ int i, j, val;
+
+ for (i = 0; i < 8; i++) {
+ mb_addr_inc[i].value = MPGDECODE_ERROR;
+ mb_addr_inc[i].num_bits = 0;
+ }
+
+ mb_addr_inc[8].value = MACRO_BLOCK_ESCAPE;
+ mb_addr_inc[8].num_bits = 11;
+
+ for (i = 9; i < 15; i++) {
+ mb_addr_inc[i].value = MPGDECODE_ERROR;
+ mb_addr_inc[i].num_bits = 0;
+ }
+
+ mb_addr_inc[15].value = MACRO_BLOCK_STUFFING;
+ mb_addr_inc[15].num_bits = 11;
+
+ for (i = 16; i < 24; i++) {
+ mb_addr_inc[i].value = MPGDECODE_ERROR;
+ mb_addr_inc[i].num_bits = 0;
+ }
+
+ val = 33;
+
+ ASSIGN1(24, 36, 1, val, 11);
+ ASSIGN1(36, 48, 2, val, 10);
+ ASSIGN1(48, 96, 8, val, 8);
+ ASSIGN1(96, 128, 16, val, 7);
+ ASSIGN1(128, 256, 64, val, 5);
+ ASSIGN1(256, 512, 128, val, 4);
+ ASSIGN1(512, 1024, 256, val, 3);
+ ASSIGN1(1024, 2048, 1024, val, 1);
+}
+
+
+/* Macro for filling up the decoding table for mb_type */
+#define ASSIGN2(start, end, quant, motion_forward, motion_backward, pattern, intra, num, mb_type) \
+ for (i = start; i < end; i ++) { \
+ mb_type[i].mb_quant = quant; \
+ mb_type[i].mb_motion_forward = motion_forward; \
+ mb_type[i].mb_motion_backward = motion_backward; \
+ mb_type[i].mb_pattern = pattern; \
+ mb_type[i].mb_intra = intra; \
+ mb_type[i].num_bits = num; \
+ }
+
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * init_mb_type_P --
+ *
+ * Initialize the VLC decoding table for macro_block_type in
+ * predictive-coded pictures.
+ *
+ * Results:
+ * The decoding table for macro_block_type in predictive-coded
+ * pictures will be filled; illegal values will be filled as MPGDECODE_ERROR.
+ *
+ * Side effects:
+ * The global array mb_type_P will be filled.
+ *
+ *--------------------------------------------------------------
+ */
+static void init_mb_type_P() {
+ int i;
+
+ mb_type_P[0].mb_quant = mb_type_P[0].mb_motion_forward
+ = mb_type_P[0].mb_motion_backward = mb_type_P[0].mb_pattern
+ = mb_type_P[0].mb_intra = (unsigned int)MPGDECODE_ERROR;
+ mb_type_P[0].num_bits = 0;
+
+ ASSIGN2(1, 2, 1, 0, 0, 0, 1, 6, mb_type_P)
+ ASSIGN2(2, 4, 1, 0, 0, 1, 0, 5, mb_type_P)
+ ASSIGN2(4, 6, 1, 1, 0, 1, 0, 5, mb_type_P);
+ ASSIGN2(6, 8, 0, 0, 0, 0, 1, 5, mb_type_P);
+ ASSIGN2(8, 16, 0, 1, 0, 0, 0, 3, mb_type_P);
+ ASSIGN2(16, 32, 0, 0, 0, 1, 0, 2, mb_type_P);
+ ASSIGN2(32, 64, 0, 1, 0, 1, 0, 1, mb_type_P);
+}
+
+
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * init_mb_type_B --
+ *
+ * Initialize the VLC decoding table for macro_block_type in
+ * bidirectionally-coded pictures.
+ *
+ * Results:
+ * The decoding table for macro_block_type in bidirectionally-coded
+ * pictures will be filled; illegal values will be filled as MPGDECODE_ERROR.
+ *
+ * Side effects:
+ * The global array mb_type_B will be filled.
+ *
+ *--------------------------------------------------------------
+ */
+static void init_mb_type_B() {
+ int i;
+
+ mb_type_B[0].mb_quant = mb_type_B[0].mb_motion_forward
+ = mb_type_B[0].mb_motion_backward = mb_type_B[0].mb_pattern
+ = mb_type_B[0].mb_intra = (unsigned int) MPGDECODE_ERROR;
+ mb_type_B[0].num_bits = 0;
+
+ ASSIGN2(1, 2, 1, 0, 0, 0, 1, 6, mb_type_B);
+ ASSIGN2(2, 3, 1, 0, 1, 1, 0, 6, mb_type_B);
+ ASSIGN2(3, 4, 1, 1, 0, 1, 0, 6, mb_type_B);
+ ASSIGN2(4, 6, 1, 1, 1, 1, 0, 5, mb_type_B);
+ ASSIGN2(6, 8, 0, 0, 0, 0, 1, 5, mb_type_B);
+ ASSIGN2(8, 12, 0, 1, 0, 0, 0, 4, mb_type_B);
+ ASSIGN2(12, 16, 0, 1, 0, 1, 0, 4, mb_type_B);
+ ASSIGN2(16, 24, 0, 0, 1, 0, 0, 3, mb_type_B);
+ ASSIGN2(24, 32, 0, 0, 1, 1, 0, 3, mb_type_B);
+ ASSIGN2(32, 48, 0, 1, 1, 0, 0, 2, mb_type_B);
+ ASSIGN2(48, 64, 0, 1, 1, 1, 0, 2, mb_type_B);
+}
+
+
+/* Macro for filling up the decoding tables for motion_vectors */
+#define ASSIGN3(start, end, step, val, num) \
+ for (i = start; i < end; i+= step) { \
+ for (j = 0; j < step / 2; j++) { \
+ motion_vectors[i+j].code = val; \
+ motion_vectors[i+j].num_bits = num; \
+ } \
+ for (j = step / 2; j < step; j++) { \
+ motion_vectors[i+j].code = -val; \
+ motion_vectors[i+j].num_bits = num; \
+ } \
+ val--; \
+ }
+
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * init_motion_vectors --
+ *
+ * Initialize the VLC decoding table for the various motion
+ * vectors, including motion_horizontal_forward_code,
+ * motion_vertical_forward_code, motion_horizontal_backward_code,
+ * and motion_vertical_backward_code.
+ *
+ * Results:
+ * The decoding table for the motion vectors will be filled;
+ * illegal values will be filled as MPGDECODE_ERROR.
+ *
+ * Side effects:
+ * The global array motion_vector will be filled.
+ *
+ *--------------------------------------------------------------
+ */
+static void init_motion_vectors()
+{
+ int i, j, val = 16;
+
+ for (i = 0; i < 24; i++) {
+ motion_vectors[i].code = MPGDECODE_ERROR;
+ motion_vectors[i].num_bits = 0;
+ }
+
+ ASSIGN3(24, 36, 2, val, 11);
+ ASSIGN3(36, 48, 4, val, 10);
+ ASSIGN3(48, 96, 16, val, 8);
+ ASSIGN3(96, 128, 32, val, 7);
+ ASSIGN3(128, 256, 128, val, 5);
+ ASSIGN3(256, 512, 256, val, 4);
+ ASSIGN3(512, 1024, 512, val, 3);
+ ASSIGN3(1024, 2048, 1024, val, 1);
+}
+
+
+
+extern void init_pre_idct();
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * init_tables --
+ *
+ * Initialize all the tables for VLC decoding; this must be
+ * called when the system is set up before any decoding can
+ * take place.
+ *
+ * Results:
+ * All the decoding tables will be filled accordingly.
+ *
+ * Side effects:
+ * The corresponding global array for each decoding table
+ * will be filled.
+ *
+ *--------------------------------------------------------------
+ */
+void init_tables() {
+
+ init_mb_addr_inc();
+ init_mb_type_P();
+ init_mb_type_B();
+ init_motion_vectors();
+
+ if (qualityFlag) {
+ cout << "qualityFlag in init_tables float idct removed"<<endl;
+ }
+ init_pre_idct();
+
+}
+
+
diff --git a/mpeglib/lib/mpegplay/decoderTables.h b/mpeglib/lib/mpegplay/decoderTables.h
new file mode 100644
index 00000000..00b9b4f2
--- /dev/null
+++ b/mpeglib/lib/mpegplay/decoderTables.h
@@ -0,0 +1,110 @@
+/*
+ declares various tables to make things faster
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __DECODERTABLES_H
+#define __DECODERTABLES_H
+
+#include "videoDecoder.h"
+#include "proto.h"
+
+/* Code for unbound values in decoding tables */
+#define MPGDECODE_ERROR (-1)
+
+#define MACRO_BLOCK_STUFFING 34
+#define MACRO_BLOCK_ESCAPE 35
+
+
+/* Structure for an entry in the decoding table of
+ * macroblock_address_increment */
+typedef struct {
+ int value; /* value for macroblock_address_increment */
+ int num_bits; /* length of the Huffman code */
+} mb_addr_inc_entry;
+
+
+/* Structure for an entry in the decoding table of macroblock_type */
+typedef struct {
+ unsigned int mb_quant; /* macroblock_quant */
+ unsigned int mb_motion_forward; /* macroblock_motion_forward */
+ unsigned int mb_motion_backward; /* macroblock_motion_backward */
+ unsigned int mb_pattern; /* macroblock_pattern */
+ unsigned int mb_intra; /* macroblock_intra */
+ unsigned int num_bits; /* length of the Huffman code */
+} mb_type_entry;
+
+
+/* Structures for an entry in the decoding table of coded_block_pattern */
+typedef struct {
+ unsigned int cbp; /* coded_block_pattern */
+ int num_bits; /* length of the Huffman code */
+} coded_block_pattern_entry;
+
+
+/* Structure for an entry in the decoding table of motion vectors */
+typedef struct {
+ int code; /* value for motion_horizontal_forward_code,
+ * motion_vertical_forward_code,
+ * motion_horizontal_backward_code, or
+ * motion_vertical_backward_code.
+ */
+ int num_bits; /* length of the Huffman code */
+} motion_vectors_entry;
+
+
+/* Structure for an entry in the decoding table of dct_dc_size */
+typedef struct {
+ unsigned int value; /* value of dct_dc_size (luminance or chrominance) */
+ int num_bits; /* length of the Huffman code */
+} dct_dc_size_entry;
+
+
+
+/* External declaration of dct coeff tables. */
+
+extern unsigned short int dct_coeff_tbl_0[256];
+extern unsigned short int dct_coeff_tbl_1[16];
+extern unsigned short int dct_coeff_tbl_2[4];
+extern unsigned short int dct_coeff_tbl_3[4];
+extern unsigned short int dct_coeff_next[256];
+extern unsigned short int dct_coeff_first[256];
+
+/* External declaration of dct dc size lumiance table. */
+
+extern dct_dc_size_entry dct_dc_size_luminance[32];
+extern dct_dc_size_entry dct_dc_size_luminance1[16];
+
+/* External declaration of dct dc size chrom table. */
+
+extern dct_dc_size_entry dct_dc_size_chrominance[32];
+extern dct_dc_size_entry dct_dc_size_chrominance1[32];
+
+/* External declaration of coded block pattern table. */
+
+extern coded_block_pattern_entry coded_block_pattern[512];
+
+/* Decoding table for macroblock_type in predictive-coded pictures */
+extern mb_type_entry mb_type_P[64];
+
+/* Decoding table for macroblock_type in bidirectionally-coded pictures */
+extern mb_type_entry mb_type_B[64];
+
+/* Decoding table for macroblock_address_increment */
+extern mb_addr_inc_entry mb_addr_inc[2048];
+
+/* Decoding table for motion vectors */
+extern motion_vectors_entry motion_vectors[2048];
+
+extern void init_tables();
+
+#endif
diff --git a/mpeglib/lib/mpegplay/globals.cpp b/mpeglib/lib/mpegplay/globals.cpp
new file mode 100644
index 00000000..b1e416be
--- /dev/null
+++ b/mpeglib/lib/mpegplay/globals.cpp
@@ -0,0 +1,41 @@
+/*
+ global variable for mpegplay
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+
+
+
+
+
+/*
+ if used definetely uses more cpu, but I don't see
+ if the image is better or not.
+*/
+int qualityFlag = 0;
+
+int gXErrorFlag = 0;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/lib/mpegplay/gop.cpp b/mpeglib/lib/mpegplay/gop.cpp
new file mode 100644
index 00000000..b2199070
--- /dev/null
+++ b/mpeglib/lib/mpegplay/gop.cpp
@@ -0,0 +1,170 @@
+/*
+ parse/stores GOP (group of picture) information from a mpegVideoStream
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "gop.h"
+
+#include <iostream>
+
+using namespace std;
+
+GOP::GOP() {
+ drop_flag=false;
+ tc_hours=0;
+ tc_minutes=0;
+ tc_seconds=0;
+ tc_pictures=0;
+ closed_gop=false;
+ broken_link=false;
+
+ mpegExtension=new MpegExtension();
+
+}
+
+
+GOP::~GOP() {
+ delete mpegExtension;
+}
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ParseGOP --
+ *
+ * Parses of group of pictures header from bit stream
+ * associated with vid_stream.
+ *
+ * Results:
+ * Values in gop header placed into video stream structure.
+ *
+ * Side effects:
+ * Bit stream irreversibly parsed.
+ *
+ *--------------------------------------------------------------
+ */
+int GOP::processGOP(MpegVideoStream* mpegVideoStream) {
+ unsigned int data;
+
+ /* Flush group of pictures start code. */
+
+ mpegVideoStream->flushBits(32);
+
+ /* Parse off drop frame flag. */
+
+ data=mpegVideoStream->getBits(1);
+
+ if (data) {
+ drop_flag = true;
+ } else
+ drop_flag = false;
+
+ /* Parse off hour component of time code. */
+
+ tc_hours=mpegVideoStream->getBits(5);
+
+ /* Parse off minute component of time code. */
+
+ tc_minutes=mpegVideoStream->getBits(6);
+
+
+ /* Flush marker bit. */
+
+ mpegVideoStream->flushBits(1);
+
+ /* Parse off second component of time code. */
+
+ tc_seconds=mpegVideoStream->getBits(6);
+
+ /* Parse off picture count component of time code. */
+
+ tc_pictures=mpegVideoStream->getBits(6);
+
+ /* Parse off closed gop and broken link flags. */
+ data=mpegVideoStream->getBits(2);
+ if (data > 1) {
+ closed_gop = true;
+ if (data > 2) {
+ broken_link = true;
+ } else
+ broken_link = false;
+ } else {
+ closed_gop = false;
+ if (data) {
+ broken_link = true;
+ } else
+ broken_link = false;
+ }
+
+
+ /*
+ * If next start code is extension/user start code,
+ * parse off extension data.
+ */
+
+ mpegExtension->processExtensionData(mpegVideoStream);
+
+ return true;
+}
+
+
+
+int GOP::substract(GOP* minus,GOP* dest) {
+ int hours;
+ int minutes;
+ int seconds;
+ hours=getHour()-minus->getHour();
+ minutes=getMinutes()-minus->getMinutes();
+ seconds=getSeconds()-minus->getSeconds();
+
+ if (seconds < 0) {
+ seconds+=60;
+ minutes--;
+ }
+ if (minutes < 0) {
+ minutes+=60;
+ hours--;
+ }
+ dest->tc_hours=hours;
+ dest->tc_minutes=minutes;
+ dest->tc_seconds=seconds;
+ if (hours < 0) {
+ return false;
+ }
+ return true;
+}
+
+
+void GOP::copyTo(GOP* dest) {
+ dest->tc_hours=getHour();
+ dest->tc_minutes=getMinutes();
+ dest->tc_seconds=getSeconds();
+ dest->drop_flag=getDropFlag();
+ dest->tc_pictures=getPictures();
+ dest->closed_gop=getClosedGOP();
+ dest->broken_link=getBrokenLink();
+
+ // currently do not copy ext/user data FIX ME
+}
+
+
+void GOP::print(const char* description) {
+ cout << "GOP [START]:"<<description<<endl;
+ cout << "tc_hours:"<<getHour()<<endl;
+ cout << "tc_minutes:"<<getMinutes()<<endl;
+ cout << "tc_seconds:"<<getSeconds()<<endl;
+ cout << "drop_flag:"<<getDropFlag()<<endl;
+ cout << "tc_pictures:"<<getPictures()<<endl;
+ cout << "closed_gop:"<<getClosedGOP()<<endl;
+ cout << "broken_link:"<<getBrokenLink()<<endl;
+ cout << "GOP [END]"<<endl;
+}
diff --git a/mpeglib/lib/mpegplay/gop.h b/mpeglib/lib/mpegplay/gop.h
new file mode 100644
index 00000000..a36d2886
--- /dev/null
+++ b/mpeglib/lib/mpegplay/gop.h
@@ -0,0 +1,62 @@
+/*
+ parse/stores GOP (group of picture) information from a mpegVideoStream
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+
+#ifndef __GOP_H
+#define __GOP_H
+
+#include "mpegVideoStream.h"
+#include "mpegExtension.h"
+
+class GOP {
+ /* Group of pictures structure. */
+
+ int drop_flag; /* Flag indicating dropped frame. */
+ int tc_hours; /* Hour component of time code. */
+ unsigned int tc_minutes; /* Minute component of time code. */
+ unsigned int tc_seconds; /* Second component of time code. */
+ unsigned int tc_pictures; /* Picture counter of time code. */
+ int closed_gop; /* Indicates no pred. vectors to
+ previous group of pictures. */
+ int broken_link; /* B frame unable to be decoded. */
+ MpegExtension* mpegExtension;
+
+
+ public:
+ GOP();
+ ~GOP();
+
+ int processGOP(MpegVideoStream* mpegVideoStream);
+ void copyTo(GOP* dest);
+
+ inline int getDropFlag() { return drop_flag; }
+ inline unsigned int getHour() { return tc_hours; }
+ inline unsigned int getMinutes() { return tc_minutes; }
+ inline unsigned int getSeconds() { return tc_seconds; }
+ inline unsigned int getPictures() { return tc_pictures; }
+ inline int getClosedGOP() { return closed_gop; }
+ inline int getBrokenLink() { return broken_link; }
+
+
+ inline void setHour(int hour) { this->tc_hours=hour; }
+ inline void setMinute(unsigned int minute) { this->tc_minutes=minute; }
+ inline void setSecond(unsigned int second) { this->tc_seconds=second; }
+
+
+ // returns diff in seconds
+ int substract(GOP* minus,GOP* dest);
+ void print(const char* description);
+};
+#endif
diff --git a/mpeglib/lib/mpegplay/jrevdct.cpp b/mpeglib/lib/mpegplay/jrevdct.cpp
new file mode 100644
index 00000000..4ffe48ab
--- /dev/null
+++ b/mpeglib/lib/mpegplay/jrevdct.cpp
@@ -0,0 +1,1690 @@
+/*
+ * jrevdct.c
+ *
+ * This file is part of the Independent JPEG Group's software.
+ * The IJG code is distributed under the terms reproduced here:
+ *
+ * LEGAL ISSUES
+ * ============
+ *
+ * In plain English:
+ *
+ * 1. We don't promise that this software works. (But if you find any bugs,
+ * please let us know!)
+ * 2. You can use this software for whatever you want. You don't have to
+ * pay us.
+ * 3. You may not pretend that you wrote this software. If you use it in a
+ * program, you must acknowledge somewhere in your documentation that
+ * you've used the IJG code.
+ *
+ * In legalese:
+ *
+ * The authors make NO WARRANTY or representation, either express or implied,
+ * with respect to this software, its quality, accuracy, merchantability, or
+ * fitness for a particular purpose. This software is provided "AS IS", and
+ * you, its user, assume the entire risk as to its quality and accuracy.
+ *
+ * This software is copyright (C) 1991, 1992, Thomas G. Lane.
+ * All Rights Reserved except as specified below.
+ *
+ * Permission is hereby granted to use, copy, modify, and distribute this
+ * software (or portions thereof) for any purpose, without fee, subject to
+ * these conditions:
+ * (1) If any part of the source code for this software is distributed, then
+ * this copyright and no-warranty notice must be included unaltered; and any
+ * additions, deletions, or changes to the original files must be clearly
+ * indicated in accompanying documentation.
+ * (2) If only executable code is distributed, then the accompanying
+ * documentation must state that "this software is based in part on the
+ * work of the Independent JPEG Group".
+ * (3) Permission for use of this software is granted only if the user
+ * accepts full responsibility for any undesirable consequences; the authors
+ * accept NO LIABILITY for damages of any kind.
+ *
+ * These conditions apply to any software derived from or based on the IJG
+ * code, not just to the unmodified library. If you use our work, you ought
+ * to acknowledge us.
+ *
+ * Permission is NOT granted for the use of any IJG author's name or company
+ * name in advertising or publicity relating to this software or products
+ * derived from it. This software may be referred to only as
+ * "the Independent JPEG Group's software".
+ *
+ * We specifically permit and encourage the use of this software as the
+ * basis of commercial products, provided that all warranty or liability
+ * claims are assumed by the product vendor.
+ *
+ *
+ * ARCHIVE LOCATIONS
+ * =================
+ *
+ * The "official" archive site for this software is ftp.uu.net (Internet
+ * address 192.48.96.9). The most recent released version can always be
+ * found there in directory graphics/jpeg. This particular version will
+ * be archived as graphics/jpeg/jpegsrc.v6a.tar.gz. If you are on the
+ * Internet, you can retrieve files from ftp.uu.net by standard anonymous
+ * FTP. If you don't have FTP access, UUNET's archives are also available
+ * via UUCP; contact help@uunet.uu.net for information on retrieving files
+ * that way.
+ *
+ * Numerous Internet sites maintain copies of the UUNET files. However,
+ * only ftp.uu.net is guaranteed to have the latest official version.
+ *
+ * You can also obtain this software in DOS-compatible "zip" archive
+ * format from the SimTel archives (ftp.coast.net:/SimTel/msdos/graphics/),
+ * or on CompuServe in the Graphics Support forum (GO CIS:GRAPHSUP),
+ * library 12 "JPEG Tools". Again, these versions may sometimes lag behind
+ * the ftp.uu.net release.
+ *
+ * The JPEG FAQ (Frequently Asked Questions) article is a useful source of
+ * general information about JPEG. It is updated constantly and therefore
+ * is not included in this distribution. The FAQ is posted every two weeks
+ * to Usenet newsgroups comp.graphics.misc, news.answers, and other groups.
+ * You can always obtain the latest version from the news.answers archive
+ * at rtfm.mit.edu. By FTP, fetch /pub/usenet/news.answers/jpeg-faq/part1
+ * and .../part2. If you don't have FTP, send e-mail to
+ * mail-server@rtfm.mit.edu with body
+ * send usenet/news.answers/jpeg-faq/part1
+ * send usenet/news.answers/jpeg-faq/part2
+ *
+ * ==============
+ *
+ *
+ * This file contains the basic inverse-DCT transformation subroutine.
+ *
+ * This implementation is based on an algorithm described in
+ * C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT
+ * Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics,
+ * Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991.
+ * The primary algorithm described there uses 11 multiplies and 29 adds.
+ * We use their alternate method with 12 multiplies and 32 adds.
+ * The advantage of this method is that no data path contains more than one
+ * multiplication; this allows a very simple and accurate implementation in
+ * scaled fixed-point arithmetic, with a minimal number of shifts.
+ *
+ *
+ * CHANGES FOR BERKELEY MPEG
+ * =========================
+ *
+ * This file has been altered to use the Berkeley MPEG header files,
+ * to add the capability to handle sparse DCT matrices efficiently,
+ * and to relabel the inverse DCT function as well as the file
+ * (formerly jidctint.c).
+ *
+ * I've made lots of modifications to attempt to take advantage of the
+ * sparse nature of the DCT matrices we're getting. Although the logic
+ * is cumbersome, it's straightforward and the resulting code is much
+ * faster.
+ *
+ * A better way to do this would be to pass in the DCT block as a sparse
+ * matrix, perhaps with the difference cases encoded.
+ */
+
+#include "jrevdct.h"
+
+
+
+
+/* We assume that right shift corresponds to signed division by 2 with
+ * rounding towards minus infinity. This is correct for typical "arithmetic
+ * shift" instructions that shift in copies of the sign bit. But some
+ * C compilers implement >> with an unsigned shift. For these machines you
+ * must define RIGHT_SHIFT_IS_UNSIGNED.
+ * RIGHT_SHIFT provides a proper signed right shift of an INT32 quantity.
+ * It is only applied with constant shift counts. SHIFT_TEMPS must be
+ * included in the variables of any routine using RIGHT_SHIFT.
+ */
+
+#ifdef RIGHT_SHIFT_IS_UNSIGNED
+#define SHIFT_TEMPS INT32 shift_temp;
+#define RIGHT_SHIFT(x,shft) \
+ ((shift_temp = (x)) < 0 ? \
+ (shift_temp >> (shft)) | ((~((INT32) 0)) << (32-(shft))) : \
+ (shift_temp >> (shft)))
+#else
+#define SHIFT_TEMPS
+#define RIGHT_SHIFT(x,shft) ((x) >> (shft))
+#endif
+
+/*
+ * This routine is specialized to the case DCTSIZE = 8.
+ */
+
+#if DCTSIZE != 8
+ Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */
+#endif
+
+
+/*
+ * A 2-D IDCT can be done by 1-D IDCT on each row followed by 1-D IDCT
+ * on each column. Direct algorithms are also available, but they are
+ * much more complex and seem not to be any faster when reduced to code.
+ *
+ * The poop on this scaling stuff is as follows:
+ *
+ * Each 1-D IDCT step produces outputs which are a factor of sqrt(N)
+ * larger than the true IDCT outputs. The final outputs are therefore
+ * a factor of N larger than desired; since N=8 this can be cured by
+ * a simple right shift at the end of the algorithm. The advantage of
+ * this arrangement is that we save two multiplications per 1-D IDCT,
+ * because the y0 and y4 inputs need not be divided by sqrt(N).
+ *
+ * We have to do addition and subtraction of the integer inputs, which
+ * is no problem, and multiplication by fractional constants, which is
+ * a problem to do in integer arithmetic. We multiply all the constants
+ * by CONST_SCALE and convert them to integer constants (thus retaining
+ * CONST_BITS bits of precision in the constants). After doing a
+ * multiplication we have to divide the product by CONST_SCALE, with proper
+ * rounding, to produce the correct output. This division can be done
+ * cheaply as a right shift of CONST_BITS bits. We postpone shifting
+ * as long as possible so that partial sums can be added together with
+ * full fractional precision.
+ *
+ * The outputs of the first pass are scaled up by PASS1_BITS bits so that
+ * they are represented to better-than-integral precision. These outputs
+ * require BITS_IN_JSAMPLE + PASS1_BITS + 3 bits; this fits in a 16-bit word
+ * with the recommended scaling. (To scale up 12-bit sample data further, an
+ * intermediate INT32 array would be needed.)
+ *
+ * To avoid overflow of the 32-bit intermediate results in pass 2, we must
+ * have BITS_IN_JSAMPLE + CONST_BITS + PASS1_BITS <= 26. Error analysis
+ * shows that the values given below are the most effective.
+ */
+
+#ifdef EIGHT_BIT_SAMPLES
+#define PASS1_BITS 2
+#else
+#define PASS1_BITS 1 /* lose a little precision to avoid overflow */
+#endif
+
+#define ONE ((INT32) 1)
+
+#define CONST_SCALE (ONE << CONST_BITS)
+
+/* Convert a positive real constant to an integer scaled by CONST_SCALE.
+ * IMPORTANT: if your compiler doesn't do this arithmetic at compile time,
+ * you will pay a significant penalty in run time. In that case, figure
+ * the correct integer constant values and insert them by hand.
+ */
+
+#define FIX(x) ((INT32) ((x) * CONST_SCALE + 0.5))
+
+/* When adding two opposite-signed fixes, the 0.5 cancels */
+#define FIX2(x) ((INT32) ((x) * CONST_SCALE))
+
+/* Descale and correctly round an INT32 value that's scaled by N bits.
+ * We assume RIGHT_SHIFT rounds towards minus infinity, so adding
+ * the fudge factor is correct for either sign of X.
+ */
+
+#define DESCALE(x,n) RIGHT_SHIFT((x) + (ONE << ((n)-1)), n)
+
+/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result.
+ * For 8-bit samples with the recommended scaling, all the variable
+ * and constant values involved are no more than 16 bits wide, so a
+ * 16x16->32 bit multiply can be used instead of a full 32x32 multiply;
+ * this provides a useful speedup on many machines.
+ * There is no way to specify a 16x16->32 multiply in portable C, but
+ * some C compilers will do the right thing if you provide the correct
+ * combination of casts.
+ * NB: for 12-bit samples, a full 32-bit multiplication will be needed.
+ */
+
+#ifdef EIGHT_BIT_SAMPLES
+#ifdef SHORTxSHORT_32 /* may work if 'int' is 32 bits */
+#define MULTIPLY(var,const) (((INT16) (var)) * ((INT16) (const)))
+#endif
+#ifdef SHORTxLCONST_32 /* known to work with Microsoft C 6.0 */
+#define MULTIPLY(var,const) (((INT16) (var)) * ((INT32) (const)))
+#endif
+#endif
+
+#ifndef MULTIPLY /* default definition */
+#define MULTIPLY(var,const) ((var) * (const))
+#endif
+
+#ifndef NO_SPARSE_DCT
+#define SPARSE_SCALE_FACTOR 8
+#endif
+
+/* Precomputed idct value arrays. */
+
+static DCTELEM PreIDCT[64][64];
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * init_pre_idct --
+ *
+ * Pre-computes singleton coefficient IDCT values.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+void init_pre_idct() {
+ int i;
+
+ for (i=0; i<64; i++) {
+ memset((char *) PreIDCT[i], 0, 64*sizeof(DCTELEM));
+ PreIDCT[i][i] = 1 << SPARSE_SCALE_FACTOR;
+ j_rev_dct(PreIDCT[i]);
+ }
+
+ int pos;
+ int rr;
+ DCTELEM *ndataptr;
+
+ for(pos=0;pos<64;pos++) {
+ ndataptr = PreIDCT[pos];
+
+ for(rr=0; rr<4; rr++) {
+ for(i=0;i<16;i++) {
+ ndataptr[i] = ndataptr[i]/256;
+ }
+ ndataptr += 16;
+
+ }
+ }
+
+
+
+
+
+
+}
+
+#ifndef NO_SPARSE_DCT
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * j_rev_dct_sparse --
+ *
+ * Performs the inverse DCT on one block of coefficients.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+void j_rev_dct_sparse (DCTBLOCK data, int pos) {
+ short int val;
+ register int *dp;
+ register int v;
+ int quant;
+
+ // cout << "j_rev_dct_sparse"<<endl;
+
+ /* If DC Coefficient. */
+
+ if (pos == 0) {
+ dp = (int *)data;
+ v = *data;
+ quant = 8;
+
+ /* Compute 32 bit value to assign. This speeds things up a bit */
+ if (v < 0) {
+ val = -v;
+ val += (quant / 2);
+ val /= quant;
+ val = -val;
+ }
+ else {
+ val = (v + (quant / 2)) / quant;
+ }
+
+ v = ((val & 0xffff) | (val << 16));
+
+ dp[0] = v; dp[1] = v; dp[2] = v; dp[3] = v;
+ dp[4] = v; dp[5] = v; dp[6] = v; dp[7] = v;
+ dp[8] = v; dp[9] = v; dp[10] = v; dp[11] = v;
+ dp[12] = v; dp[13] = v; dp[14] = v; dp[15] = v;
+ dp[16] = v; dp[17] = v; dp[18] = v; dp[19] = v;
+ dp[20] = v; dp[21] = v; dp[22] = v; dp[23] = v;
+ dp[24] = v; dp[25] = v; dp[26] = v; dp[27] = v;
+ dp[28] = v; dp[29] = v; dp[30] = v; dp[31] = v;
+
+ return;
+ }
+ //printf("sparse is: %d val:%8x\n",pos,data[pos]);
+
+ /*
+ j_rev_dct(data);
+ return;
+ */
+
+ /* Some other coefficient. */
+
+ DCTELEM *dataptr;
+ DCTELEM *ndataptr;
+ int coeff, rr;
+
+
+
+ dataptr = (DCTELEM *)data;
+ coeff = dataptr[pos];
+ ndataptr = PreIDCT[pos];
+
+ //printf ("COEFFICIENT = %3d, POSITION = %2d\n", coeff, pos);
+ coeff=coeff/256;
+
+ for (rr=0; rr<4; rr++) {
+
+ dataptr[0] = (ndataptr[0] * coeff);
+ dataptr[1] = (ndataptr[1] * coeff);
+ dataptr[2] = (ndataptr[2] * coeff);
+ dataptr[3] = (ndataptr[3] * coeff);
+ dataptr[4] = (ndataptr[4] * coeff);
+ dataptr[5] = (ndataptr[5] * coeff);
+ dataptr[6] = (ndataptr[6] * coeff);
+ dataptr[7] = (ndataptr[7] * coeff);
+ dataptr[8] = (ndataptr[8] * coeff);
+ dataptr[9] = (ndataptr[9] * coeff);
+ dataptr[10] = (ndataptr[10] * coeff);
+ dataptr[11] = (ndataptr[11] * coeff);
+ dataptr[12] = (ndataptr[12] * coeff);
+ dataptr[13] = (ndataptr[13] * coeff);
+ dataptr[14] = (ndataptr[14] * coeff);
+ dataptr[15] = (ndataptr[15] * coeff);
+
+
+ dataptr += 16;
+ ndataptr += 16;
+ }
+
+ dataptr = (DCTELEM *) data;
+
+
+
+ return;
+
+}
+
+#else
+
+/*
+ *--------------------------------------------------------------
+ *
+ * j_rev_dct_sparse --
+ *
+ * Performs the original inverse DCT on one block of
+ * coefficients.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+void j_rev_dct_sparse (DCTBLOCK data,int pos) {
+ j_rev_dct(data);
+}
+#endif /* SPARSE_DCT */
+
+
+#ifndef FIVE_DCT
+
+#ifndef ORIG_DCT
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * j_rev_dct --
+ *
+ * The inverse DCT function.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+void j_rev_dct (DCTBLOCK data) {
+
+
+ INT32 tmp0, tmp1, tmp2, tmp3;
+ INT32 tmp10, tmp11, tmp12, tmp13;
+ INT32 z1, z2, z3, z4, z5;
+ INT32 d0, d1, d2, d3, d4, d5, d6, d7;
+ register DCTELEM *dataptr;
+ int rowctr;
+ SHIFT_TEMPS
+
+
+ /* Pass 1: process rows. */
+ /* Note results are scaled up by sqrt(8) compared to a true IDCT; */
+ /* furthermore, we scale the results by 2**PASS1_BITS. */
+
+ dataptr = data;
+
+ for (rowctr = DCTSIZE-1; rowctr >= 0; rowctr--) {
+ /* Due to quantization, we will usually find that many of the input
+ * coefficients are zero, especially the AC terms. We can exploit this
+ * by short-circuiting the IDCT calculation for any row in which all
+ * the AC terms are zero. In that case each output is equal to the
+ * DC coefficient (with scale factor as needed).
+ * With typical images and quantization tables, half or more of the
+ * row DCT calculations can be simplified this way.
+ */
+
+ register int *idataptr = (int*)dataptr;
+ d0 = dataptr[0];
+ d1 = dataptr[1];
+ if ((d1 == 0) && (idataptr[1] + idataptr[2] + idataptr[3]) == 0) {
+ /* AC terms all zero */
+ if (d0) {
+ /* Compute a 32 bit value to assign. */
+ DCTELEM dcval = (DCTELEM) (d0 << PASS1_BITS);
+ register int v = (dcval & 0xffff) + (dcval << 16);
+
+ idataptr[0] = v;
+ idataptr[1] = v;
+ idataptr[2] = v;
+ idataptr[3] = v;
+ }
+
+ dataptr += DCTSIZE; /* advance pointer to next row */
+ continue;
+ }
+ d2 = dataptr[2];
+ d3 = dataptr[3];
+ d4 = dataptr[4];
+ d5 = dataptr[5];
+ d6 = dataptr[6];
+ d7 = dataptr[7];
+
+ /* Even part: reverse the even part of the forward DCT. */
+ /* The rotator is sqrt(2)*c(-6). */
+ if (d6) {
+ if (d4) {
+ if (d2) {
+ if (d0) {
+ /* d0 != 0, d2 != 0, d4 != 0, d6 != 0 */
+ z1 = MULTIPLY(d2 + d6, FIX(0.541196100));
+ tmp2 = z1 + MULTIPLY(d6, - FIX(1.847759065));
+ tmp3 = z1 + MULTIPLY(d2, FIX(0.765366865));
+
+ tmp0 = (d0 + d4) << CONST_BITS;
+ tmp1 = (d0 - d4) << CONST_BITS;
+
+ tmp10 = tmp0 + tmp3;
+ tmp13 = tmp0 - tmp3;
+ tmp11 = tmp1 + tmp2;
+ tmp12 = tmp1 - tmp2;
+ } else {
+ /* d0 == 0, d2 != 0, d4 != 0, d6 != 0 */
+ z1 = MULTIPLY(d2 + d6, FIX(0.541196100));
+ tmp2 = z1 + MULTIPLY(d6, - FIX(1.847759065));
+ tmp3 = z1 + MULTIPLY(d2, FIX(0.765366865));
+
+ tmp0 = d4 << CONST_BITS;
+
+ tmp10 = tmp0 + tmp3;
+ tmp13 = tmp0 - tmp3;
+ tmp11 = tmp2 - tmp0;
+ tmp12 = -(tmp0 + tmp2);
+ }
+ } else {
+ if (d0) {
+ /* d0 != 0, d2 == 0, d4 != 0, d6 != 0 */
+ tmp2 = MULTIPLY(d6, - FIX2(1.306562965));
+ tmp3 = MULTIPLY(d6, FIX(0.541196100));
+
+ tmp0 = (d0 + d4) << CONST_BITS;
+ tmp1 = (d0 - d4) << CONST_BITS;
+
+ tmp10 = tmp0 + tmp3;
+ tmp13 = tmp0 - tmp3;
+ tmp11 = tmp1 + tmp2;
+ tmp12 = tmp1 - tmp2;
+ } else {
+ /* d0 == 0, d2 == 0, d4 != 0, d6 != 0 */
+ tmp2 = MULTIPLY(d6, - FIX2(1.306562965));
+ tmp3 = MULTIPLY(d6, FIX(0.541196100));
+
+ tmp0 = d4 << CONST_BITS;
+
+ tmp10 = tmp0 + tmp3;
+ tmp13 = tmp0 - tmp3;
+ tmp11 = tmp2 - tmp0;
+ tmp12 = -(tmp0 + tmp2);
+ }
+ }
+ } else {
+ if (d2) {
+ if (d0) {
+ /* d0 != 0, d2 != 0, d4 == 0, d6 != 0 */
+ z1 = MULTIPLY(d2 + d6, FIX(0.541196100));
+ tmp2 = z1 + MULTIPLY(d6, - FIX(1.847759065));
+ tmp3 = z1 + MULTIPLY(d2, FIX(0.765366865));
+
+ tmp0 = d0 << CONST_BITS;
+
+ tmp10 = tmp0 + tmp3;
+ tmp13 = tmp0 - tmp3;
+ tmp11 = tmp0 + tmp2;
+ tmp12 = tmp0 - tmp2;
+ } else {
+ /* d0 == 0, d2 != 0, d4 == 0, d6 != 0 */
+ z1 = MULTIPLY(d2 + d6, FIX(0.541196100));
+ tmp2 = z1 + MULTIPLY(d6, - FIX(1.847759065));
+ tmp3 = z1 + MULTIPLY(d2, FIX(0.765366865));
+
+ tmp10 = tmp3;
+ tmp13 = -tmp3;
+ tmp11 = tmp2;
+ tmp12 = -tmp2;
+ }
+ } else {
+ if (d0) {
+ /* d0 != 0, d2 == 0, d4 == 0, d6 != 0 */
+ tmp2 = MULTIPLY(d6, - FIX2(1.306562965));
+ tmp3 = MULTIPLY(d6, FIX(0.541196100));
+
+ tmp0 = d0 << CONST_BITS;
+
+ tmp10 = tmp0 + tmp3;
+ tmp13 = tmp0 - tmp3;
+ tmp11 = tmp0 + tmp2;
+ tmp12 = tmp0 - tmp2;
+ } else {
+ /* d0 == 0, d2 == 0, d4 == 0, d6 != 0 */
+ tmp2 = MULTIPLY(d6, - FIX2(1.306562965));
+ tmp3 = MULTIPLY(d6, FIX(0.541196100));
+
+ tmp10 = tmp3;
+ tmp13 = -tmp3;
+ tmp11 = tmp2;
+ tmp12 = -tmp2;
+ }
+ }
+ }
+ } else {
+ if (d4) {
+ if (d2) {
+ if (d0) {
+ /* d0 != 0, d2 != 0, d4 != 0, d6 == 0 */
+ tmp2 = MULTIPLY(d2, FIX(0.541196100));
+ tmp3 = (INT32) (MULTIPLY(d2, (FIX(1.306562965) + .5)));
+
+ tmp0 = (d0 + d4) << CONST_BITS;
+ tmp1 = (d0 - d4) << CONST_BITS;
+
+ tmp10 = tmp0 + tmp3;
+ tmp13 = tmp0 - tmp3;
+ tmp11 = tmp1 + tmp2;
+ tmp12 = tmp1 - tmp2;
+ } else {
+ /* d0 == 0, d2 != 0, d4 != 0, d6 == 0 */
+ tmp2 = MULTIPLY(d2, FIX(0.541196100));
+ tmp3 = (INT32) (MULTIPLY(d2, (FIX(1.306562965) + .5)));
+
+ tmp0 = d4 << CONST_BITS;
+
+ tmp10 = tmp0 + tmp3;
+ tmp13 = tmp0 - tmp3;
+ tmp11 = tmp2 - tmp0;
+ tmp12 = -(tmp0 + tmp2);
+ }
+ } else {
+ if (d0) {
+ /* d0 != 0, d2 == 0, d4 != 0, d6 == 0 */
+ tmp10 = tmp13 = (d0 + d4) << CONST_BITS;
+ tmp11 = tmp12 = (d0 - d4) << CONST_BITS;
+ } else {
+ /* d0 == 0, d2 == 0, d4 != 0, d6 == 0 */
+ tmp10 = tmp13 = d4 << CONST_BITS;
+ tmp11 = tmp12 = -tmp10;
+ }
+ }
+ } else {
+ if (d2) {
+ if (d0) {
+ /* d0 != 0, d2 != 0, d4 == 0, d6 == 0 */
+ tmp2 = MULTIPLY(d2, FIX(0.541196100));
+ tmp3 = (INT32) (MULTIPLY(d2, (FIX(1.306562965) + .5)));
+
+ tmp0 = d0 << CONST_BITS;
+
+ tmp10 = tmp0 + tmp3;
+ tmp13 = tmp0 - tmp3;
+ tmp11 = tmp0 + tmp2;
+ tmp12 = tmp0 - tmp2;
+ } else {
+ /* d0 == 0, d2 != 0, d4 == 0, d6 == 0 */
+ tmp2 = MULTIPLY(d2, FIX(0.541196100));
+ tmp3 = (INT32) (MULTIPLY(d2, (FIX(1.306562965) + .5)));
+
+ tmp10 = tmp3;
+ tmp13 = -tmp3;
+ tmp11 = tmp2;
+ tmp12 = -tmp2;
+ }
+ } else {
+ if (d0) {
+ /* d0 != 0, d2 == 0, d4 == 0, d6 == 0 */
+ tmp10 = tmp13 = tmp11 = tmp12 = d0 << CONST_BITS;
+ } else {
+ /* d0 == 0, d2 == 0, d4 == 0, d6 == 0 */
+ tmp10 = tmp13 = tmp11 = tmp12 = 0;
+ }
+ }
+ }
+ }
+
+
+ /* Odd part per figure 8; the matrix is unitary and hence its
+ * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively.
+ */
+
+ if (d7) {
+ if (d5) {
+ if (d3) {
+ if (d1) {
+ /* d1 != 0, d3 != 0, d5 != 0, d7 != 0 */
+ z1 = d7 + d1;
+ z2 = d5 + d3;
+ z3 = d7 + d3;
+ z4 = d5 + d1;
+ z5 = MULTIPLY(z3 + z4, FIX(1.175875602));
+
+ tmp0 = MULTIPLY(d7, FIX(0.298631336));
+ tmp1 = MULTIPLY(d5, FIX(2.053119869));
+ tmp2 = MULTIPLY(d3, FIX(3.072711026));
+ tmp3 = MULTIPLY(d1, FIX(1.501321110));
+ z1 = MULTIPLY(z1, - FIX(0.899976223));
+ z2 = MULTIPLY(z2, - FIX(2.562915447));
+ z3 = MULTIPLY(z3, - FIX(1.961570560));
+ z4 = MULTIPLY(z4, - FIX(0.390180644));
+
+ z3 += z5;
+ z4 += z5;
+
+ tmp0 += z1 + z3;
+ tmp1 += z2 + z4;
+ tmp2 += z2 + z3;
+ tmp3 += z1 + z4;
+ } else {
+ /* d1 == 0, d3 != 0, d5 != 0, d7 != 0 */
+ z2 = d5 + d3;
+ z3 = d7 + d3;
+ z5 = MULTIPLY(z3 + d5, FIX(1.175875602));
+
+ tmp0 = MULTIPLY(d7, FIX(0.298631336));
+ tmp1 = MULTIPLY(d5, FIX(2.053119869));
+ tmp2 = MULTIPLY(d3, FIX(3.072711026));
+ z1 = MULTIPLY(d7, - FIX(0.899976223));
+ z2 = MULTIPLY(z2, - FIX(2.562915447));
+ z3 = MULTIPLY(z3, - FIX(1.961570560));
+ z4 = MULTIPLY(d5, - FIX(0.390180644));
+
+ z3 += z5;
+ z4 += z5;
+
+ tmp0 += z1 + z3;
+ tmp1 += z2 + z4;
+ tmp2 += z2 + z3;
+ tmp3 = z1 + z4;
+ }
+ } else {
+ if (d1) {
+ /* d1 != 0, d3 == 0, d5 != 0, d7 != 0 */
+ z1 = d7 + d1;
+ z4 = d5 + d1;
+ z5 = MULTIPLY(d7 + z4, FIX(1.175875602));
+
+ tmp0 = MULTIPLY(d7, FIX(0.298631336));
+ tmp1 = MULTIPLY(d5, FIX(2.053119869));
+ tmp3 = MULTIPLY(d1, FIX(1.501321110));
+ z1 = MULTIPLY(z1, - FIX(0.899976223));
+ z2 = MULTIPLY(d5, - FIX(2.562915447));
+ z3 = MULTIPLY(d7, - FIX(1.961570560));
+ z4 = MULTIPLY(z4, - FIX(0.390180644));
+
+ z3 += z5;
+ z4 += z5;
+
+ tmp0 += z1 + z3;
+ tmp1 += z2 + z4;
+ tmp2 = z2 + z3;
+ tmp3 += z1 + z4;
+ } else {
+ /* d1 == 0, d3 == 0, d5 != 0, d7 != 0 */
+ z5 = MULTIPLY(d7 + d5, FIX(1.175875602));
+
+ tmp0 = MULTIPLY(d7, - FIX2(0.601344887));
+ tmp1 = MULTIPLY(d5, - FIX2(0.509795578));
+ z1 = MULTIPLY(d7, - FIX(0.899976223));
+ z3 = MULTIPLY(d7, - FIX(1.961570560));
+ z2 = MULTIPLY(d5, - FIX(2.562915447));
+ z4 = MULTIPLY(d5, - FIX(0.390180644));
+
+ z3 += z5;
+ z4 += z5;
+
+ tmp0 += z3;
+ tmp1 += z4;
+ tmp2 = z2 + z3;
+ tmp3 = z1 + z4;
+ }
+ }
+ } else {
+ if (d3) {
+ if (d1) {
+ /* d1 != 0, d3 != 0, d5 == 0, d7 != 0 */
+ z1 = d7 + d1;
+ z3 = d7 + d3;
+ z5 = MULTIPLY(z3 + d1, FIX(1.175875602));
+
+ tmp0 = MULTIPLY(d7, FIX(0.298631336));
+ tmp2 = MULTIPLY(d3, FIX(3.072711026));
+ tmp3 = MULTIPLY(d1, FIX(1.501321110));
+ z1 = MULTIPLY(z1, - FIX(0.899976223));
+ z2 = MULTIPLY(d3, - FIX(2.562915447));
+ z3 = MULTIPLY(z3, - FIX(1.961570560));
+ z4 = MULTIPLY(d1, - FIX(0.390180644));
+
+ z3 += z5;
+ z4 += z5;
+
+ tmp0 += z1 + z3;
+ tmp1 = z2 + z4;
+ tmp2 += z2 + z3;
+ tmp3 += z1 + z4;
+ } else {
+ /* d1 == 0, d3 != 0, d5 == 0, d7 != 0 */
+ z3 = d7 + d3;
+ z5 = MULTIPLY(z3, FIX(1.175875602));
+
+ tmp0 = MULTIPLY(d7, - FIX2(0.601344887));
+ tmp2 = MULTIPLY(d3, FIX(0.509795579));
+ z1 = MULTIPLY(d7, - FIX(0.899976223));
+ z2 = MULTIPLY(d3, - FIX(2.562915447));
+ z3 = MULTIPLY(z3, - FIX2(0.785694958));
+
+ tmp0 += z3;
+ tmp1 = z2 + z5;
+ tmp2 += z3;
+ tmp3 = z1 + z5;
+ }
+ } else {
+ if (d1) {
+ /* d1 != 0, d3 == 0, d5 == 0, d7 != 0 */
+ z1 = d7 + d1;
+ z5 = MULTIPLY(z1, FIX(1.175875602));
+
+ tmp0 = MULTIPLY(d7, - FIX2(1.662939224));
+ tmp3 = MULTIPLY(d1, FIX2(1.111140466));
+ z1 = MULTIPLY(z1, FIX2(0.275899379));
+ z3 = MULTIPLY(d7, - FIX(1.961570560));
+ z4 = MULTIPLY(d1, - FIX(0.390180644));
+
+ tmp0 += z1;
+ tmp1 = z4 + z5;
+ tmp2 = z3 + z5;
+ tmp3 += z1;
+ } else {
+ /* d1 == 0, d3 == 0, d5 == 0, d7 != 0 */
+ tmp0 = MULTIPLY(d7, - FIX2(1.387039845));
+ tmp1 = MULTIPLY(d7, FIX(1.175875602));
+ tmp2 = MULTIPLY(d7, - FIX2(0.785694958));
+ tmp3 = MULTIPLY(d7, FIX2(0.275899379));
+ }
+ }
+ }
+ } else {
+ if (d5) {
+ if (d3) {
+ if (d1) {
+ /* d1 != 0, d3 != 0, d5 != 0, d7 == 0 */
+ z2 = d5 + d3;
+ z4 = d5 + d1;
+ z5 = MULTIPLY(d3 + z4, FIX(1.175875602));
+
+ tmp1 = MULTIPLY(d5, FIX(2.053119869));
+ tmp2 = MULTIPLY(d3, FIX(3.072711026));
+ tmp3 = MULTIPLY(d1, FIX(1.501321110));
+ z1 = MULTIPLY(d1, - FIX(0.899976223));
+ z2 = MULTIPLY(z2, - FIX(2.562915447));
+ z3 = MULTIPLY(d3, - FIX(1.961570560));
+ z4 = MULTIPLY(z4, - FIX(0.390180644));
+
+ z3 += z5;
+ z4 += z5;
+
+ tmp0 = z1 + z3;
+ tmp1 += z2 + z4;
+ tmp2 += z2 + z3;
+ tmp3 += z1 + z4;
+ } else {
+ /* d1 == 0, d3 != 0, d5 != 0, d7 == 0 */
+ z2 = d5 + d3;
+ z5 = MULTIPLY(z2, FIX(1.175875602));
+
+ tmp1 = MULTIPLY(d5, FIX2(1.662939225));
+ tmp2 = MULTIPLY(d3, FIX2(1.111140466));
+ z2 = MULTIPLY(z2, - FIX2(1.387039845));
+ z3 = MULTIPLY(d3, - FIX(1.961570560));
+ z4 = MULTIPLY(d5, - FIX(0.390180644));
+
+ tmp0 = z3 + z5;
+ tmp1 += z2;
+ tmp2 += z2;
+ tmp3 = z4 + z5;
+ }
+ } else {
+ if (d1) {
+ /* d1 != 0, d3 == 0, d5 != 0, d7 == 0 */
+ z4 = d5 + d1;
+ z5 = MULTIPLY(z4, FIX(1.175875602));
+
+ tmp1 = MULTIPLY(d5, - FIX2(0.509795578));
+ tmp3 = MULTIPLY(d1, FIX2(0.601344887));
+ z1 = MULTIPLY(d1, - FIX(0.899976223));
+ z2 = MULTIPLY(d5, - FIX(2.562915447));
+ z4 = MULTIPLY(z4, FIX2(0.785694958));
+
+ tmp0 = z1 + z5;
+ tmp2 = z2 + z5;
+ tmp1 += z4;
+ tmp3 += z4;
+ } else {
+ /* d1 == 0, d3 == 0, d5 != 0, d7 == 0 */
+ tmp0 = MULTIPLY(d5, FIX(1.175875602));
+ tmp1 = MULTIPLY(d5, FIX2(0.275899380));
+ tmp2 = MULTIPLY(d5, - FIX2(1.387039845));
+ tmp3 = MULTIPLY(d5, FIX2(0.785694958));
+ }
+ }
+ } else {
+ if (d3) {
+ if (d1) {
+ /* d1 != 0, d3 != 0, d5 == 0, d7 == 0 */
+ z5 = d3 + d1;
+
+ tmp2 = MULTIPLY(d3, - FIX(1.451774981));
+ tmp3 = MULTIPLY(d1, (FIX(0.211164243) - 1));
+ z1 = MULTIPLY(d1, FIX(1.061594337));
+ z2 = MULTIPLY(d3, - FIX(2.172734803));
+ z4 = MULTIPLY(z5, FIX(0.785694958));
+ z5 = MULTIPLY(z5, FIX(1.175875602));
+
+ tmp0 = z1 - z4;
+ tmp1 = z2 + z4;
+ tmp2 += z5;
+ tmp3 += z5;
+ } else {
+ /* d1 == 0, d3 != 0, d5 == 0, d7 == 0 */
+ tmp0 = MULTIPLY(d3, - FIX2(0.785694958));
+ tmp1 = MULTIPLY(d3, - FIX2(1.387039845));
+ tmp2 = MULTIPLY(d3, - FIX2(0.275899379));
+ tmp3 = MULTIPLY(d3, FIX(1.175875602));
+ }
+ } else {
+ if (d1) {
+ /* d1 != 0, d3 == 0, d5 == 0, d7 == 0 */
+ tmp0 = MULTIPLY(d1, FIX2(0.275899379));
+ tmp1 = MULTIPLY(d1, FIX2(0.785694958));
+ tmp2 = MULTIPLY(d1, FIX(1.175875602));
+ tmp3 = MULTIPLY(d1, FIX2(1.387039845));
+ } else {
+ /* d1 == 0, d3 == 0, d5 == 0, d7 == 0 */
+ tmp0 = tmp1 = tmp2 = tmp3 = 0;
+ }
+ }
+ }
+ }
+
+ /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */
+
+ dataptr[0] = (DCTELEM) DESCALE(tmp10 + tmp3, CONST_BITS-PASS1_BITS);
+ dataptr[7] = (DCTELEM) DESCALE(tmp10 - tmp3, CONST_BITS-PASS1_BITS);
+ dataptr[1] = (DCTELEM) DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS);
+ dataptr[6] = (DCTELEM) DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS);
+ dataptr[2] = (DCTELEM) DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS);
+ dataptr[5] = (DCTELEM) DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS);
+ dataptr[3] = (DCTELEM) DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS);
+ dataptr[4] = (DCTELEM) DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS);
+
+ dataptr += DCTSIZE; /* advance pointer to next row */
+ }
+
+ /* Pass 2: process columns. */
+ /* Note that we must descale the results by a factor of 8 == 2**3, */
+ /* and also undo the PASS1_BITS scaling. */
+
+ dataptr = data;
+ for (rowctr = DCTSIZE-1; rowctr >= 0; rowctr--) {
+ /* Columns of zeroes can be exploited in the same way as we did with rows.
+ * However, the row calculation has created many nonzero AC terms, so the
+ * simplification applies less often (typically 5% to 10% of the time).
+ * On machines with very fast multiplication, it's possible that the
+ * test takes more time than it's worth. In that case this section
+ * may be commented out.
+ */
+
+ d0 = dataptr[DCTSIZE*0];
+ d1 = dataptr[DCTSIZE*1];
+ d2 = dataptr[DCTSIZE*2];
+ d3 = dataptr[DCTSIZE*3];
+ d4 = dataptr[DCTSIZE*4];
+ d5 = dataptr[DCTSIZE*5];
+ d6 = dataptr[DCTSIZE*6];
+ d7 = dataptr[DCTSIZE*7];
+
+ /* Even part: reverse the even part of the forward DCT. */
+ /* The rotator is sqrt(2)*c(-6). */
+ if (d6) {
+ if (d4) {
+ if (d2) {
+ if (d0) {
+ /* d0 != 0, d2 != 0, d4 != 0, d6 != 0 */
+ z1 = MULTIPLY(d2 + d6, FIX(0.541196100));
+ tmp2 = z1 + MULTIPLY(d6, - FIX(1.847759065));
+ tmp3 = z1 + MULTIPLY(d2, FIX(0.765366865));
+
+ tmp0 = (d0 + d4) << CONST_BITS;
+ tmp1 = (d0 - d4) << CONST_BITS;
+
+ tmp10 = tmp0 + tmp3;
+ tmp13 = tmp0 - tmp3;
+ tmp11 = tmp1 + tmp2;
+ tmp12 = tmp1 - tmp2;
+ } else {
+ /* d0 == 0, d2 != 0, d4 != 0, d6 != 0 */
+ z1 = MULTIPLY(d2 + d6, FIX(0.541196100));
+ tmp2 = z1 + MULTIPLY(d6, - FIX(1.847759065));
+ tmp3 = z1 + MULTIPLY(d2, FIX(0.765366865));
+
+ tmp0 = d4 << CONST_BITS;
+
+ tmp10 = tmp0 + tmp3;
+ tmp13 = tmp0 - tmp3;
+ tmp11 = tmp2 - tmp0;
+ tmp12 = -(tmp0 + tmp2);
+ }
+ } else {
+ if (d0) {
+ /* d0 != 0, d2 == 0, d4 != 0, d6 != 0 */
+ tmp2 = MULTIPLY(d6, - FIX2(1.306562965));
+ tmp3 = MULTIPLY(d6, FIX(0.541196100));
+
+ tmp0 = (d0 + d4) << CONST_BITS;
+ tmp1 = (d0 - d4) << CONST_BITS;
+
+ tmp10 = tmp0 + tmp3;
+ tmp13 = tmp0 - tmp3;
+ tmp11 = tmp1 + tmp2;
+ tmp12 = tmp1 - tmp2;
+ } else {
+ /* d0 == 0, d2 == 0, d4 != 0, d6 != 0 */
+ tmp2 = MULTIPLY(d6, -FIX2(1.306562965));
+ tmp3 = MULTIPLY(d6, FIX(0.541196100));
+
+ tmp0 = d4 << CONST_BITS;
+
+ tmp10 = tmp0 + tmp3;
+ tmp13 = tmp0 - tmp3;
+ tmp11 = tmp2 - tmp0;
+ tmp12 = -(tmp0 + tmp2);
+ }
+ }
+ } else {
+ if (d2) {
+ if (d0) {
+ /* d0 != 0, d2 != 0, d4 == 0, d6 != 0 */
+ z1 = MULTIPLY(d2 + d6, FIX(0.541196100));
+ tmp2 = z1 + MULTIPLY(d6, - FIX(1.847759065));
+ tmp3 = z1 + MULTIPLY(d2, FIX(0.765366865));
+
+ tmp0 = d0 << CONST_BITS;
+
+ tmp10 = tmp0 + tmp3;
+ tmp13 = tmp0 - tmp3;
+ tmp11 = tmp0 + tmp2;
+ tmp12 = tmp0 - tmp2;
+ } else {
+ /* d0 == 0, d2 != 0, d4 == 0, d6 != 0 */
+ z1 = MULTIPLY(d2 + d6, FIX(0.541196100));
+ tmp2 = z1 + MULTIPLY(d6, - FIX(1.847759065));
+ tmp3 = z1 + MULTIPLY(d2, FIX(0.765366865));
+
+ tmp10 = tmp3;
+ tmp13 = -tmp3;
+ tmp11 = tmp2;
+ tmp12 = -tmp2;
+ }
+ } else {
+ if (d0) {
+ /* d0 != 0, d2 == 0, d4 == 0, d6 != 0 */
+ tmp2 = MULTIPLY(d6, - FIX2(1.306562965));
+ tmp3 = MULTIPLY(d6, FIX(0.541196100));
+
+ tmp0 = d0 << CONST_BITS;
+
+ tmp10 = tmp0 + tmp3;
+ tmp13 = tmp0 - tmp3;
+ tmp11 = tmp0 + tmp2;
+ tmp12 = tmp0 - tmp2;
+ } else {
+ /* d0 == 0, d2 == 0, d4 == 0, d6 != 0 */
+ tmp2 = MULTIPLY(d6, - FIX2(1.306562965));
+ tmp3 = MULTIPLY(d6, FIX(0.541196100));
+
+ tmp10 = tmp3;
+ tmp13 = -tmp3;
+ tmp11 = tmp2;
+ tmp12 = -tmp2;
+ }
+ }
+ }
+ } else {
+ if (d4) {
+ if (d2) {
+ if (d0) {
+ /* d0 != 0, d2 != 0, d4 != 0, d6 == 0 */
+ tmp2 = MULTIPLY(d2, FIX(0.541196100));
+ tmp3 = (INT32) (MULTIPLY(d2, (FIX(1.306562965) + .5)));
+
+ tmp0 = (d0 + d4) << CONST_BITS;
+ tmp1 = (d0 - d4) << CONST_BITS;
+
+ tmp10 = tmp0 + tmp3;
+ tmp13 = tmp0 - tmp3;
+ tmp11 = tmp1 + tmp2;
+ tmp12 = tmp1 - tmp2;
+ } else {
+ /* d0 == 0, d2 != 0, d4 != 0, d6 == 0 */
+ tmp2 = MULTIPLY(d2, FIX(0.541196100));
+ tmp3 = (INT32) (MULTIPLY(d2, (FIX(1.306562965) + .5)));
+
+ tmp0 = d4 << CONST_BITS;
+
+ tmp10 = tmp0 + tmp3;
+ tmp13 = tmp0 - tmp3;
+ tmp11 = tmp2 - tmp0;
+ tmp12 = -(tmp0 + tmp2);
+ }
+ } else {
+ if (d0) {
+ /* d0 != 0, d2 == 0, d4 != 0, d6 == 0 */
+ tmp10 = tmp13 = (d0 + d4) << CONST_BITS;
+ tmp11 = tmp12 = (d0 - d4) << CONST_BITS;
+ } else {
+ /* d0 == 0, d2 == 0, d4 != 0, d6 == 0 */
+ tmp10 = tmp13 = d4 << CONST_BITS;
+ tmp11 = tmp12 = -tmp10;
+ }
+ }
+ } else {
+ if (d2) {
+ if (d0) {
+ /* d0 != 0, d2 != 0, d4 == 0, d6 == 0 */
+ tmp2 = MULTIPLY(d2, FIX(0.541196100));
+ tmp3 = (INT32) (MULTIPLY(d2, (FIX(1.306562965) + .5)));
+
+ tmp0 = d0 << CONST_BITS;
+
+ tmp10 = tmp0 + tmp3;
+ tmp13 = tmp0 - tmp3;
+ tmp11 = tmp0 + tmp2;
+ tmp12 = tmp0 - tmp2;
+ } else {
+ /* d0 == 0, d2 != 0, d4 == 0, d6 == 0 */
+ tmp2 = MULTIPLY(d2, FIX(0.541196100));
+ tmp3 = (INT32) (MULTIPLY(d2, (FIX(1.306562965) + .5)));
+
+ tmp10 = tmp3;
+ tmp13 = -tmp3;
+ tmp11 = tmp2;
+ tmp12 = -tmp2;
+ }
+ } else {
+ if (d0) {
+ /* d0 != 0, d2 == 0, d4 == 0, d6 == 0 */
+ tmp10 = tmp13 = tmp11 = tmp12 = d0 << CONST_BITS;
+ } else {
+ /* d0 == 0, d2 == 0, d4 == 0, d6 == 0 */
+ tmp10 = tmp13 = tmp11 = tmp12 = 0;
+ }
+ }
+ }
+ }
+
+ /* Odd part per figure 8; the matrix is unitary and hence its
+ * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively.
+ */
+ if (d7) {
+ if (d5) {
+ if (d3) {
+ if (d1) {
+ /* d1 != 0, d3 != 0, d5 != 0, d7 != 0 */
+ z1 = d7 + d1;
+ z2 = d5 + d3;
+ z3 = d7 + d3;
+ z4 = d5 + d1;
+ z5 = MULTIPLY(z3 + z4, FIX(1.175875602));
+
+ tmp0 = MULTIPLY(d7, FIX(0.298631336));
+ tmp1 = MULTIPLY(d5, FIX(2.053119869));
+ tmp2 = MULTIPLY(d3, FIX(3.072711026));
+ tmp3 = MULTIPLY(d1, FIX(1.501321110));
+ z1 = MULTIPLY(z1, - FIX(0.899976223));
+ z2 = MULTIPLY(z2, - FIX(2.562915447));
+ z3 = MULTIPLY(z3, - FIX(1.961570560));
+ z4 = MULTIPLY(z4, - FIX(0.390180644));
+
+ z3 += z5;
+ z4 += z5;
+
+ tmp0 += z1 + z3;
+ tmp1 += z2 + z4;
+ tmp2 += z2 + z3;
+ tmp3 += z1 + z4;
+ } else {
+ /* d1 == 0, d3 != 0, d5 != 0, d7 != 0 */
+ z2 = d5 + d3;
+ z3 = d7 + d3;
+ z5 = MULTIPLY(z3 + d5, FIX(1.175875602));
+
+ tmp0 = MULTIPLY(d7, FIX(0.298631336));
+ tmp1 = MULTIPLY(d5, FIX(2.053119869));
+ tmp2 = MULTIPLY(d3, FIX(3.072711026));
+ z1 = MULTIPLY(d7, - FIX(0.899976223));
+ z2 = MULTIPLY(z2, - FIX(2.562915447));
+ z3 = MULTIPLY(z3, - FIX(1.961570560));
+ z4 = MULTIPLY(d5, - FIX(0.390180644));
+
+ z3 += z5;
+ z4 += z5;
+
+ tmp0 += z1 + z3;
+ tmp1 += z2 + z4;
+ tmp2 += z2 + z3;
+ tmp3 = z1 + z4;
+ }
+ } else {
+ if (d1) {
+ /* d1 != 0, d3 == 0, d5 != 0, d7 != 0 */
+ z1 = d7 + d1;
+ z4 = d5 + d1;
+ z5 = MULTIPLY(d7 + z4, FIX(1.175875602));
+
+ tmp0 = MULTIPLY(d7, FIX(0.298631336));
+ tmp1 = MULTIPLY(d5, FIX(2.053119869));
+ tmp3 = MULTIPLY(d1, FIX(1.501321110));
+ z1 = MULTIPLY(z1, - FIX(0.899976223));
+ z2 = MULTIPLY(d5, - FIX(2.562915447));
+ z3 = MULTIPLY(d7, - FIX(1.961570560));
+ z4 = MULTIPLY(z4, - FIX(0.390180644));
+
+ z3 += z5;
+ z4 += z5;
+
+ tmp0 += z1 + z3;
+ tmp1 += z2 + z4;
+ tmp2 = z2 + z3;
+ tmp3 += z1 + z4;
+ } else {
+ /* d1 == 0, d3 == 0, d5 != 0, d7 != 0 */
+ z5 = MULTIPLY(d5 + d7, FIX(1.175875602));
+
+ tmp0 = MULTIPLY(d7, - FIX2(0.601344887));
+ tmp1 = MULTIPLY(d5, - FIX2(0.509795578));
+ z1 = MULTIPLY(d7, - FIX(0.899976223));
+ z3 = MULTIPLY(d7, - FIX(1.961570560));
+ z2 = MULTIPLY(d5, - FIX(2.562915447));
+ z4 = MULTIPLY(d5, - FIX(0.390180644));
+
+ z3 += z5;
+ z4 += z5;
+
+ tmp0 += z3;
+ tmp1 += z4;
+ tmp2 = z2 + z3;
+ tmp3 = z1 + z4;
+ }
+ }
+ } else {
+ if (d3) {
+ if (d1) {
+ /* d1 != 0, d3 != 0, d5 == 0, d7 != 0 */
+ z1 = d7 + d1;
+ z3 = d7 + d3;
+ z5 = MULTIPLY(z3 + d1, FIX(1.175875602));
+
+ tmp0 = MULTIPLY(d7, FIX(0.298631336));
+ tmp2 = MULTIPLY(d3, FIX(3.072711026));
+ tmp3 = MULTIPLY(d1, FIX(1.501321110));
+ z1 = MULTIPLY(z1, - FIX(0.899976223));
+ z2 = MULTIPLY(d3, - FIX(2.562915447));
+ z3 = MULTIPLY(z3, - FIX(1.961570560));
+ z4 = MULTIPLY(d1, - FIX(0.390180644));
+
+ z3 += z5;
+ z4 += z5;
+
+ tmp0 += z1 + z3;
+ tmp1 = z2 + z4;
+ tmp2 += z2 + z3;
+ tmp3 += z1 + z4;
+ } else {
+ /* d1 == 0, d3 != 0, d5 == 0, d7 != 0 */
+ z3 = d7 + d3;
+ z5 = MULTIPLY(z3, FIX(1.175875602));
+
+ tmp0 = MULTIPLY(d7, - FIX2(0.601344887));
+ z1 = MULTIPLY(d7, - FIX(0.899976223));
+ tmp2 = MULTIPLY(d3, FIX(0.509795579));
+ z2 = MULTIPLY(d3, - FIX(2.562915447));
+ z3 = MULTIPLY(z3, - FIX2(0.785694958));
+
+ tmp0 += z3;
+ tmp1 = z2 + z5;
+ tmp2 += z3;
+ tmp3 = z1 + z5;
+ }
+ } else {
+ if (d1) {
+ /* d1 != 0, d3 == 0, d5 == 0, d7 != 0 */
+ z1 = d7 + d1;
+ z5 = MULTIPLY(z1, FIX(1.175875602));
+
+ tmp0 = MULTIPLY(d7, - FIX2(1.662939224));
+ tmp3 = MULTIPLY(d1, FIX2(1.111140466));
+ z1 = MULTIPLY(z1, FIX2(0.275899379));
+ z3 = MULTIPLY(d7, - FIX(1.961570560));
+ z4 = MULTIPLY(d1, - FIX(0.390180644));
+
+ tmp0 += z1;
+ tmp1 = z4 + z5;
+ tmp2 = z3 + z5;
+ tmp3 += z1;
+ } else {
+ /* d1 == 0, d3 == 0, d5 == 0, d7 != 0 */
+ tmp0 = MULTIPLY(d7, - FIX2(1.387039845));
+ tmp1 = MULTIPLY(d7, FIX(1.175875602));
+ tmp2 = MULTIPLY(d7, - FIX2(0.785694958));
+ tmp3 = MULTIPLY(d7, FIX2(0.275899379));
+ }
+ }
+ }
+ } else {
+ if (d5) {
+ if (d3) {
+ if (d1) {
+ /* d1 != 0, d3 != 0, d5 != 0, d7 == 0 */
+ z2 = d5 + d3;
+ z4 = d5 + d1;
+ z5 = MULTIPLY(d3 + z4, FIX(1.175875602));
+
+ tmp1 = MULTIPLY(d5, FIX(2.053119869));
+ tmp2 = MULTIPLY(d3, FIX(3.072711026));
+ tmp3 = MULTIPLY(d1, FIX(1.501321110));
+ z1 = MULTIPLY(d1, - FIX(0.899976223));
+ z2 = MULTIPLY(z2, - FIX(2.562915447));
+ z3 = MULTIPLY(d3, - FIX(1.961570560));
+ z4 = MULTIPLY(z4, - FIX(0.390180644));
+
+ z3 += z5;
+ z4 += z5;
+
+ tmp0 = z1 + z3;
+ tmp1 += z2 + z4;
+ tmp2 += z2 + z3;
+ tmp3 += z1 + z4;
+ } else {
+ /* d1 == 0, d3 != 0, d5 != 0, d7 == 0 */
+ z2 = d5 + d3;
+ z5 = MULTIPLY(z2, FIX(1.175875602));
+
+ tmp1 = MULTIPLY(d5, FIX2(1.662939225));
+ tmp2 = MULTIPLY(d3, FIX2(1.111140466));
+ z2 = MULTIPLY(z2, - FIX2(1.387039845));
+ z3 = MULTIPLY(d3, - FIX(1.961570560));
+ z4 = MULTIPLY(d5, - FIX(0.390180644));
+
+ tmp0 = z3 + z5;
+ tmp1 += z2;
+ tmp2 += z2;
+ tmp3 = z4 + z5;
+ }
+ } else {
+ if (d1) {
+ /* d1 != 0, d3 == 0, d5 != 0, d7 == 0 */
+ z4 = d5 + d1;
+ z5 = MULTIPLY(z4, FIX(1.175875602));
+
+ tmp1 = MULTIPLY(d5, - FIX2(0.509795578));
+ tmp3 = MULTIPLY(d1, FIX2(0.601344887));
+ z1 = MULTIPLY(d1, - FIX(0.899976223));
+ z2 = MULTIPLY(d5, - FIX(2.562915447));
+ z4 = MULTIPLY(z4, FIX2(0.785694958));
+
+ tmp0 = z1 + z5;
+ tmp1 += z4;
+ tmp2 = z2 + z5;
+ tmp3 += z4;
+ } else {
+ /* d1 == 0, d3 == 0, d5 != 0, d7 == 0 */
+ tmp0 = MULTIPLY(d5, FIX(1.175875602));
+ tmp1 = MULTIPLY(d5, FIX2(0.275899380));
+ tmp2 = MULTIPLY(d5, - FIX2(1.387039845));
+ tmp3 = MULTIPLY(d5, FIX2(0.785694958));
+ }
+ }
+ } else {
+ if (d3) {
+ if (d1) {
+ /* d1 != 0, d3 != 0, d5 == 0, d7 == 0 */
+ z5 = d3 + d1;
+
+ tmp2 = MULTIPLY(d3, - FIX(1.451774981));
+ tmp3 = MULTIPLY(d1, (FIX(0.211164243) - 1));
+ z1 = MULTIPLY(d1, FIX(1.061594337));
+ z2 = MULTIPLY(d3, - FIX(2.172734803));
+ z4 = MULTIPLY(z5, FIX(0.785694958));
+ z5 = MULTIPLY(z5, FIX(1.175875602));
+
+ tmp0 = z1 - z4;
+ tmp1 = z2 + z4;
+ tmp2 += z5;
+ tmp3 += z5;
+ } else {
+ /* d1 == 0, d3 != 0, d5 == 0, d7 == 0 */
+ tmp0 = MULTIPLY(d3, - FIX2(0.785694958));
+ tmp1 = MULTIPLY(d3, - FIX2(1.387039845));
+ tmp2 = MULTIPLY(d3, - FIX2(0.275899379));
+ tmp3 = MULTIPLY(d3, FIX(1.175875602));
+ }
+ } else {
+ if (d1) {
+ /* d1 != 0, d3 == 0, d5 == 0, d7 == 0 */
+ tmp0 = MULTIPLY(d1, FIX2(0.275899379));
+ tmp1 = MULTIPLY(d1, FIX2(0.785694958));
+ tmp2 = MULTIPLY(d1, FIX(1.175875602));
+ tmp3 = MULTIPLY(d1, FIX2(1.387039845));
+ } else {
+ /* d1 == 0, d3 == 0, d5 == 0, d7 == 0 */
+ tmp0 = tmp1 = tmp2 = tmp3 = 0;
+ }
+ }
+ }
+ }
+
+ /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */
+
+ dataptr[DCTSIZE*0] = (DCTELEM) DESCALE(tmp10 + tmp3,
+ CONST_BITS+PASS1_BITS+3);
+ dataptr[DCTSIZE*7] = (DCTELEM) DESCALE(tmp10 - tmp3,
+ CONST_BITS+PASS1_BITS+3);
+ dataptr[DCTSIZE*1] = (DCTELEM) DESCALE(tmp11 + tmp2,
+ CONST_BITS+PASS1_BITS+3);
+ dataptr[DCTSIZE*6] = (DCTELEM) DESCALE(tmp11 - tmp2,
+ CONST_BITS+PASS1_BITS+3);
+ dataptr[DCTSIZE*2] = (DCTELEM) DESCALE(tmp12 + tmp1,
+ CONST_BITS+PASS1_BITS+3);
+ dataptr[DCTSIZE*5] = (DCTELEM) DESCALE(tmp12 - tmp1,
+ CONST_BITS+PASS1_BITS+3);
+ dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp13 + tmp0,
+ CONST_BITS+PASS1_BITS+3);
+ dataptr[DCTSIZE*4] = (DCTELEM) DESCALE(tmp13 - tmp0,
+ CONST_BITS+PASS1_BITS+3);
+
+ dataptr++; /* advance pointer to next column */
+ }
+}
+
+#else
+
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * j_rev_dct --
+ *
+ * The original inverse DCT function.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+void j_rev_dct (DCTBLOCK data)
+{
+ INT32 tmp0, tmp1, tmp2, tmp3;
+ INT32 tmp10, tmp11, tmp12, tmp13;
+ INT32 z1, z2, z3, z4, z5;
+ register DCTELEM *dataptr;
+ int rowctr;
+ SHIFT_TEMPS
+
+ /* Pass 1: process rows. */
+ /* Note results are scaled up by sqrt(8) compared to a true IDCT; */
+ /* furthermore, we scale the results by 2**PASS1_BITS. */
+
+ dataptr = data;
+ for (rowctr = DCTSIZE-1; rowctr >= 0; rowctr--) {
+ /* Due to quantization, we will usually find that many of the input
+ * coefficients are zero, especially the AC terms. We can exploit this
+ * by short-circuiting the IDCT calculation for any row in which all
+ * the AC terms are zero. In that case each output is equal to the
+ * DC coefficient (with scale factor as needed).
+ * With typical images and quantization tables, half or more of the
+ * row DCT calculations can be simplified this way.
+ */
+
+ if ((dataptr[1] | dataptr[2] | dataptr[3] | dataptr[4] |
+ dataptr[5] | dataptr[6] | dataptr[7]) == 0) {
+ /* AC terms all zero */
+ DCTELEM dcval = (DCTELEM) (dataptr[0] << PASS1_BITS);
+
+ dataptr[0] = dcval;
+ dataptr[1] = dcval;
+ dataptr[2] = dcval;
+ dataptr[3] = dcval;
+ dataptr[4] = dcval;
+ dataptr[5] = dcval;
+ dataptr[6] = dcval;
+ dataptr[7] = dcval;
+
+ dataptr += DCTSIZE; /* advance pointer to next row */
+ continue;
+ }
+
+ /* Even part: reverse the even part of the forward DCT. */
+ /* The rotator is sqrt(2)*c(-6). */
+
+ z2 = (INT32) dataptr[2];
+ z3 = (INT32) dataptr[6];
+
+ z1 = MULTIPLY(z2 + z3, FIX(0.541196100));
+ tmp2 = z1 + MULTIPLY(z3, - FIX(1.847759065));
+ tmp3 = z1 + MULTIPLY(z2, FIX(0.765366865));
+
+ tmp0 = ((INT32) dataptr[0] + (INT32) dataptr[4]) << CONST_BITS;
+ tmp1 = ((INT32) dataptr[0] - (INT32) dataptr[4]) << CONST_BITS;
+
+ tmp10 = tmp0 + tmp3;
+ tmp13 = tmp0 - tmp3;
+ tmp11 = tmp1 + tmp2;
+ tmp12 = tmp1 - tmp2;
+
+ /* Odd part per figure 8; the matrix is unitary and hence its
+ * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively.
+ */
+
+ tmp0 = (INT32) dataptr[7];
+ tmp1 = (INT32) dataptr[5];
+ tmp2 = (INT32) dataptr[3];
+ tmp3 = (INT32) dataptr[1];
+
+ z1 = tmp0 + tmp3;
+ z2 = tmp1 + tmp2;
+ z3 = tmp0 + tmp2;
+ z4 = tmp1 + tmp3;
+ z5 = MULTIPLY(z3 + z4, FIX(1.175875602)); /* sqrt(2) * c3 */
+
+ tmp0 = MULTIPLY(tmp0, FIX(0.298631336)); /* sqrt(2) * (-c1+c3+c5-c7) */
+ tmp1 = MULTIPLY(tmp1, FIX(2.053119869)); /* sqrt(2) * ( c1+c3-c5+c7) */
+ tmp2 = MULTIPLY(tmp2, FIX(3.072711026)); /* sqrt(2) * ( c1+c3+c5-c7) */
+ tmp3 = MULTIPLY(tmp3, FIX(1.501321110)); /* sqrt(2) * ( c1+c3-c5-c7) */
+ z1 = MULTIPLY(z1, - FIX(0.899976223)); /* sqrt(2) * (c7-c3) */
+ z2 = MULTIPLY(z2, - FIX(2.562915447)); /* sqrt(2) * (-c1-c3) */
+ z3 = MULTIPLY(z3, - FIX(1.961570560)); /* sqrt(2) * (-c3-c5) */
+ z4 = MULTIPLY(z4, - FIX(0.390180644)); /* sqrt(2) * (c5-c3) */
+
+ z3 += z5;
+ z4 += z5;
+
+ tmp0 += z1 + z3;
+ tmp1 += z2 + z4;
+ tmp2 += z2 + z3;
+ tmp3 += z1 + z4;
+
+ /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */
+
+ dataptr[0] = (DCTELEM) DESCALE(tmp10 + tmp3, CONST_BITS-PASS1_BITS);
+ dataptr[7] = (DCTELEM) DESCALE(tmp10 - tmp3, CONST_BITS-PASS1_BITS);
+ dataptr[1] = (DCTELEM) DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS);
+ dataptr[6] = (DCTELEM) DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS);
+ dataptr[2] = (DCTELEM) DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS);
+ dataptr[5] = (DCTELEM) DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS);
+ dataptr[3] = (DCTELEM) DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS);
+ dataptr[4] = (DCTELEM) DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS);
+
+ dataptr += DCTSIZE; /* advance pointer to next row */
+ }
+
+ /* Pass 2: process columns. */
+ /* Note that we must descale the results by a factor of 8 == 2**3, */
+ /* and also undo the PASS1_BITS scaling. */
+
+ dataptr = data;
+ for (rowctr = DCTSIZE-1; rowctr >= 0; rowctr--) {
+ /* Columns of zeroes can be exploited in the same way as we did with rows.
+ * However, the row calculation has created many nonzero AC terms, so the
+ * simplification applies less often (typically 5% to 10% of the time).
+ * On machines with very fast multiplication, it's possible that the
+ * test takes more time than it's worth. In that case this section
+ * may be commented out.
+ */
+
+#ifndef NO_ZERO_COLUMN_TEST
+ if ((dataptr[DCTSIZE*1] | dataptr[DCTSIZE*2] | dataptr[DCTSIZE*3] |
+ dataptr[DCTSIZE*4] | dataptr[DCTSIZE*5] | dataptr[DCTSIZE*6] |
+ dataptr[DCTSIZE*7]) == 0) {
+ /* AC terms all zero */
+ DCTELEM dcval = (DCTELEM) DESCALE((INT32) dataptr[0], PASS1_BITS+3);
+
+ dataptr[DCTSIZE*0] = dcval;
+ dataptr[DCTSIZE*1] = dcval;
+ dataptr[DCTSIZE*2] = dcval;
+ dataptr[DCTSIZE*3] = dcval;
+ dataptr[DCTSIZE*4] = dcval;
+ dataptr[DCTSIZE*5] = dcval;
+ dataptr[DCTSIZE*6] = dcval;
+ dataptr[DCTSIZE*7] = dcval;
+
+ dataptr++; /* advance pointer to next column */
+ continue;
+ }
+#endif
+
+ /* Even part: reverse the even part of the forward DCT. */
+ /* The rotator is sqrt(2)*c(-6). */
+
+ z2 = (INT32) dataptr[DCTSIZE*2];
+ z3 = (INT32) dataptr[DCTSIZE*6];
+
+ z1 = MULTIPLY(z2 + z3, FIX(0.541196100));
+ tmp2 = z1 + MULTIPLY(z3, - FIX(1.847759065));
+ tmp3 = z1 + MULTIPLY(z2, FIX(0.765366865));
+
+ tmp0 = ((INT32) dataptr[DCTSIZE*0] + (INT32) dataptr[DCTSIZE*4]) << CONST_BITS;
+ tmp1 = ((INT32) dataptr[DCTSIZE*0] - (INT32) dataptr[DCTSIZE*4]) << CONST_BITS;
+
+ tmp10 = tmp0 + tmp3;
+ tmp13 = tmp0 - tmp3;
+ tmp11 = tmp1 + tmp2;
+ tmp12 = tmp1 - tmp2;
+
+ /* Odd part per figure 8; the matrix is unitary and hence its
+ * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively.
+ */
+
+ tmp0 = (INT32) dataptr[DCTSIZE*7];
+ tmp1 = (INT32) dataptr[DCTSIZE*5];
+ tmp2 = (INT32) dataptr[DCTSIZE*3];
+ tmp3 = (INT32) dataptr[DCTSIZE*1];
+
+ z1 = tmp0 + tmp3;
+ z2 = tmp1 + tmp2;
+ z3 = tmp0 + tmp2;
+ z4 = tmp1 + tmp3;
+ z5 = MULTIPLY(z3 + z4, FIX(1.175875602)); /* sqrt(2) * c3 */
+
+ tmp0 = MULTIPLY(tmp0, FIX(0.298631336)); /* sqrt(2) * (-c1+c3+c5-c7) */
+ tmp1 = MULTIPLY(tmp1, FIX(2.053119869)); /* sqrt(2) * ( c1+c3-c5+c7) */
+ tmp2 = MULTIPLY(tmp2, FIX(3.072711026)); /* sqrt(2) * ( c1+c3+c5-c7) */
+ tmp3 = MULTIPLY(tmp3, FIX(1.501321110)); /* sqrt(2) * ( c1+c3-c5-c7) */
+ z1 = MULTIPLY(z1, - FIX(0.899976223)); /* sqrt(2) * (c7-c3) */
+ z2 = MULTIPLY(z2, - FIX(2.562915447)); /* sqrt(2) * (-c1-c3) */
+ z3 = MULTIPLY(z3, - FIX(1.961570560)); /* sqrt(2) * (-c3-c5) */
+ z4 = MULTIPLY(z4, - FIX(0.390180644)); /* sqrt(2) * (c5-c3) */
+
+ z3 += z5;
+ z4 += z5;
+
+ tmp0 += z1 + z3;
+ tmp1 += z2 + z4;
+ tmp2 += z2 + z3;
+ tmp3 += z1 + z4;
+
+ /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */
+
+ dataptr[DCTSIZE*0] = (DCTELEM) DESCALE(tmp10 + tmp3,
+ CONST_BITS+PASS1_BITS+3);
+ dataptr[DCTSIZE*7] = (DCTELEM) DESCALE(tmp10 - tmp3,
+ CONST_BITS+PASS1_BITS+3);
+ dataptr[DCTSIZE*1] = (DCTELEM) DESCALE(tmp11 + tmp2,
+ CONST_BITS+PASS1_BITS+3);
+ dataptr[DCTSIZE*6] = (DCTELEM) DESCALE(tmp11 - tmp2,
+ CONST_BITS+PASS1_BITS+3);
+ dataptr[DCTSIZE*2] = (DCTELEM) DESCALE(tmp12 + tmp1,
+ CONST_BITS+PASS1_BITS+3);
+ dataptr[DCTSIZE*5] = (DCTELEM) DESCALE(tmp12 - tmp1,
+ CONST_BITS+PASS1_BITS+3);
+ dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp13 + tmp0,
+ CONST_BITS+PASS1_BITS+3);
+ dataptr[DCTSIZE*4] = (DCTELEM) DESCALE(tmp13 - tmp0,
+ CONST_BITS+PASS1_BITS+3);
+
+ dataptr++; /* advance pointer to next column */
+ }
+}
+
+
+#endif /* ORIG_DCT */
+#endif /* FIVE_DCT */
+
diff --git a/mpeglib/lib/mpegplay/jrevdct.h b/mpeglib/lib/mpegplay/jrevdct.h
new file mode 100644
index 00000000..d280c2ce
--- /dev/null
+++ b/mpeglib/lib/mpegplay/jrevdct.h
@@ -0,0 +1,57 @@
+/*
+ definittion for reverse dct
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __JREVDCT_H
+#define __JREVDCT_H
+
+extern "C" {
+#include <string.h>
+}
+
+#include "proto.h"
+
+
+#ifndef XMD_H
+typedef int INT32;
+typedef short INT16;
+typedef char INT8;
+#endif
+typedef unsigned int UINT32;
+typedef unsigned short UINT16;
+typedef unsigned char UINT8;
+
+
+
+/* Definition of Contant integer scale factor. */
+
+#define CONST_BITS 13
+
+/* Misc DCT definitions */
+#define DCTSIZE 8 /* The basic DCT block is 8x8 samples */
+#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */
+
+#define GLOBAL /* a function referenced thru EXTERNs */
+
+typedef short DCTELEM;
+typedef DCTELEM DCTBLOCK[DCTSIZE2];
+
+
+/* jrevdct.c */
+void init_pre_idct (void);
+void j_rev_dct_sparse (DCTBLOCK data , int pos);
+void j_rev_dct (DCTBLOCK data);
+void j_rev_dct_sparse (DCTBLOCK data , int pos);
+void j_rev_dct (DCTBLOCK data);
+
+
+#endif
diff --git a/mpeglib/lib/mpegplay/macroBlock.cpp b/mpeglib/lib/mpegplay/macroBlock.cpp
new file mode 100644
index 00000000..2e35e551
--- /dev/null
+++ b/mpeglib/lib/mpegplay/macroBlock.cpp
@@ -0,0 +1,1152 @@
+/*
+ stores macroblock infos
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "macroBlock.h"
+
+#define DEBUG_MACROBLOCK(x)
+//#define DEBUG_MACROBLOCK(x) x
+
+
+
+MacroBlock::MacroBlock(VideoDecoder* vid_stream) {
+ this->vid_stream=vid_stream;
+ copyFunctions=new CopyFunctions();
+}
+
+
+MacroBlock::~MacroBlock() {
+ delete copyFunctions;
+}
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ParseMacroBlock --
+ *
+ * Parseoff macroblock. Reconstructs DCT values. Applies
+ * inverse DCT, reconstructs motion vectors, calculates and
+ * set pixel values for macroblock in current pict image
+ * structure.
+ *
+ * Results:
+ * Here's where everything really happens. Welcome to the
+ * heart of darkness.
+ *
+ * Side effects:
+ * Bit stream irreversibly parsed off.
+ *
+ *--------------------------------------------------------------
+ */
+
+int MacroBlock::processMacroBlock(PictureArray* pictureArray) {
+ unsigned int data;
+ int recon_right_for, recon_down_for, recon_right_back,
+ recon_down_back;
+ int mb_quant = 0, mb_motion_forw = 0, mb_motion_back = 0,
+ mb_pattern = 0;
+
+ int addr_incr;
+ MpegVideoStream* mpegVideoStream=vid_stream->mpegVideoStream;
+ DecoderClass* decoderClass=vid_stream->decoderClass;
+
+ /*
+ * Parse off macroblock address increment and add to macroblock address.
+ */
+ do {
+ addr_incr=decoderClass->decodeMBAddrInc();
+ if (addr_incr==MB_ESCAPE) {
+ mb_address += 33;
+ addr_incr=MB_STUFFING;
+ }
+
+ } while (addr_incr == MB_STUFFING);
+ mb_address+=addr_incr;
+
+
+
+ if (mb_address > (vid_stream->mpegVideoHeader)->getMB_Size()) {
+
+ DEBUG_MACROBLOCK(cout <<"ParseMacroBlock: SKIP_TO_START_CODE"<<endl;)
+ DEBUG_MACROBLOCK(cout <<"mb_address "<<mb_address<<endl;)
+
+ int h=(vid_stream->mpegVideoHeader)->getMB_Height();
+ int w=(vid_stream->mpegVideoHeader)->getMB_Width();
+
+
+ DEBUG_MACROBLOCK(cout <<"mb_height*mb_width-1:"<<(h*w - 1)<<endl;)
+ return false;
+ }
+
+ /*
+ * If macroblocks have been skipped, process skipped macroblocks.
+ */
+
+ int code_type=(vid_stream->picture)->getCodeType();
+
+ if (mb_address - past_mb_addr > 1) {
+
+ processSkippedPictures(pictureArray,code_type,
+ (vid_stream->mpegVideoHeader)->getMB_Width());
+
+ }
+
+
+ /* Set past macroblock address to current macroblock address. */
+ past_mb_addr = mb_address;
+ /* Based on picture type decode macroblock type. */
+
+
+ switch (code_type) {
+ case I_TYPE:
+ decoderClass->decodeMBTypeI(mb_quant, mb_motion_forw,
+ mb_motion_back, mb_pattern,
+ mb_intra);
+ break;
+
+ case P_TYPE:
+ decoderClass->decodeMBTypeP(mb_quant, mb_motion_forw,
+ mb_motion_back, mb_pattern,
+ mb_intra);
+ break;
+
+ case B_TYPE:
+ decoderClass->decodeMBTypeB(mb_quant, mb_motion_forw,
+ mb_motion_back, mb_pattern,
+ mb_intra);
+ break;
+ case D_TYPE:
+ DEBUG_MACROBLOCK(cout <<"ERROR: MPEG-1 Streams with D-frames are not supported"<<endl;)
+ return false;
+
+ }
+ /* If quantization flag set, parse off new quantization scale. */
+ if (mb_quant == true) {
+ data=mpegVideoStream->getBits(5);
+ (vid_stream->slice)->setQuantScale(data);
+ }
+ /* If forward motion vectors exist... */
+
+ if (mb_motion_forw == true) {
+ // Parse off and decode horizontal forward motion vector.
+ motion_h_forw_code=decoderClass->decodeMotionVectors();
+
+ // If horiz. forward r data exists, parse off.
+
+ if ((vid_stream->picture->getForw_f() != 1) &&
+ (motion_h_forw_code != 0)) {
+ data=vid_stream->picture->geth_forw_r(mpegVideoStream);
+ motion_h_forw_r = data;
+ }
+ // Parse off and decode vertical forward motion vector.
+
+ motion_v_forw_code=decoderClass->decodeMotionVectors();
+
+ // If vert. forw. r data exists, parse off.
+
+ if ((vid_stream->picture->getForw_f() != 1) &&
+ (motion_v_forw_code != 0)) {
+
+ data=vid_stream->picture->getv_forw_r(mpegVideoStream);
+ motion_v_forw_r = data;
+ }
+ }
+
+ /* If back motion vectors exist... */
+
+ if (mb_motion_back == true) {
+ // Parse off and decode horiz. back motion vector.
+ motion_h_back_code=decoderClass->decodeMotionVectors();
+
+ // If horiz. back r data exists, parse off.
+
+ if ((vid_stream->picture->getBack_f() != 1) &&
+ (motion_h_back_code != 0)) {
+ data=vid_stream->picture->geth_back_r(mpegVideoStream);
+ motion_h_back_r = data;
+ }
+ // Parse off and decode vert. back motion vector.
+ motion_v_back_code=decoderClass->decodeMotionVectors();
+
+ // If vert. back r data exists, parse off.
+
+ if ((vid_stream->picture->getBack_f() != 1) &&
+ (motion_v_back_code != 0)) {
+ data=vid_stream->picture->getv_back_r(mpegVideoStream);
+ motion_v_back_r = data;
+ }
+ }
+
+ /* If mblock pattern flag set, parse and decode CBP (code block pattern). */
+ if (mb_pattern == true) {
+ cbp=decoderClass->decodeCBP();
+ }
+ /* Otherwise, set CBP to zero. */
+ else
+ cbp = 0;
+
+
+
+ /* Reconstruct motion vectors depending on picture type. */
+ if (code_type == P_TYPE) {
+
+ /*
+ * If no forw motion vectors, reset previous and current vectors to 0.
+ */
+ if (!mb_motion_forw) {
+ recon_right_for = 0;
+ recon_down_for = 0;
+ recon_right_for_prev = 0;
+ recon_down_for_prev = 0;
+ }
+ /*
+ * Otherwise, compute new forw motion vectors. Reset previous vectors to
+ * current vectors.
+ */
+
+ else {
+ computeForwVector(&recon_right_for, &recon_down_for);
+
+ }
+ }
+ if (code_type == B_TYPE) {
+
+ /* Reset prev. and current vectors to zero if mblock is intracoded. */
+ if (mb_intra) {
+ recon_right_for_prev = 0;
+ recon_down_for_prev = 0;
+ recon_right_back_prev = 0;
+ recon_down_back_prev = 0;
+ } else {
+
+ /* If no forw vectors, current vectors equal prev. vectors. */
+
+ if (!mb_motion_forw) {
+ recon_right_for = recon_right_for_prev;
+ recon_down_for = recon_down_for_prev;
+ }
+ /*
+ * Otherwise compute forw. vectors. Reset prev vectors to new values.
+ */
+
+ else {
+ computeForwVector(&recon_right_for, &recon_down_for);
+
+ }
+
+ /* If no back vectors, set back vectors to prev back vectors. */
+
+ if (!mb_motion_back) {
+ recon_right_back = recon_right_back_prev;
+ recon_down_back = recon_down_back_prev;
+ }
+ /* Otherwise compute new vectors and reset prev. back vectors. */
+
+ else {
+ computeBackVector(&recon_right_back,&recon_down_back);
+
+ }
+
+ /*
+ * Store vector existence flags in structure for possible skipped
+ * macroblocks to follow.
+ */
+
+ bpict_past_forw = mb_motion_forw;
+ bpict_past_back = mb_motion_back;
+ }
+ }
+ int back;
+ back=reconstruct(recon_right_for,
+ recon_down_for,
+ recon_right_back,
+ recon_down_back,
+ mb_motion_forw,
+ mb_motion_back,
+ pictureArray);
+
+
+ /* If D Type picture, flush marker bit. */
+ if (code_type == D_TYPE) {
+ mpegVideoStream->flushBits(1);
+ }
+
+ /* If macroblock was intracoded, set macroblock past intra address. */
+ if (mb_intra) {
+ past_intra_addr=mb_address;
+ }
+ if (back == false) {
+ return false;
+ }
+ return true;
+}
+
+
+int MacroBlock::resetMacroBlock() {
+ /* Reset past intrablock address. */
+ past_intra_addr = -2;
+
+ /* Reset previous recon motion vectors. */
+
+
+ recon_right_for_prev = 0;
+ recon_down_for_prev = 0;
+ recon_right_back_prev = 0;
+ recon_down_back_prev = 0;
+
+ /* Reset macroblock address. */
+ mb_address = (((vid_stream->slice)->getVertPos()-1) *
+ (vid_stream->mpegVideoHeader)->getMB_Width()) - 1;
+ return true;
+
+}
+
+
+int MacroBlock::resetPastMacroBlock() {
+ /* Reset past macroblock address field. */
+
+ past_mb_addr = -1;
+ return true;
+}
+
+
+
+
+int MacroBlock::reconstruct(int& recon_right_for,
+ int& recon_down_for,
+ int& recon_right_back,
+ int& recon_down_back,
+ int& mb_motion_forw,
+ int& mb_motion_back,
+ PictureArray* pictureArray) {
+ int mask, i;
+ int zero_block_flag;
+ int mb_row;
+ int mb_col;
+ int mb_width=(vid_stream->mpegVideoHeader)->getMB_Width();
+ int row_size=pictureArray->getWidth();
+ short int* dct_start=(vid_stream->decoderClass)->getDCT();
+ unsigned int qscale=(vid_stream->slice)->getQuantScale();
+ int codeType=(vid_stream->picture)->getCodeType();
+ DecoderClass* decoderClass=vid_stream->decoderClass;
+ int lflag=false;
+ Recon* recon=vid_stream->recon;
+ unsigned int* iqmatrixptr=
+ (vid_stream->mpegVideoHeader)->getIntra_quant_matrix();
+ unsigned int* niqmatrixptr=
+ (vid_stream->mpegVideoHeader)->getNon_intra_quant_matrix();
+
+
+
+
+
+ if (mb_address-past_intra_addr > 1) {
+ lflag=true;
+ }
+
+ if (mb_width <= 0) {
+ DEBUG_MACROBLOCK(cout << "mb_width <= 0"<<endl;)
+ return false;
+ }
+ /* Calculate macroblock row and column from address. */
+ mb_row=mb_address / mb_width;
+ mb_col=mb_address % mb_width;
+
+ copyFunctions->startNOFloatSection();
+
+ for (mask = 32, i = 0; i < 6; mask >>= 1, i++) {
+
+
+ /* If block exists... */
+ if ((mb_intra) || (cbp & mask)) {
+ zero_block_flag = 0;
+ //copyFunctions->endNOFloatSection();
+ decoderClass->ParseReconBlock(i,mb_intra,
+ qscale,lflag,iqmatrixptr,niqmatrixptr);
+ //copyFunctions->endNOFloatSection();
+ } else {
+ zero_block_flag = 1;
+ }
+
+
+ // If macroblock is intra coded...
+
+
+ if (mb_intra) {
+ recon->ReconIMBlock(i,mb_row,mb_col,row_size,
+ dct_start,pictureArray);
+ //copyFunctions->endNOFloatSection();
+ } else if (mb_motion_forw && mb_motion_back) {
+ recon->ReconBiMBlock(i,recon_right_for,
+ recon_down_for,recon_right_back,
+ recon_down_back,zero_block_flag,
+ mb_row,mb_col,row_size,dct_start,
+ pictureArray);
+ //copyFunctions->endNOFloatSection();
+ } else if (mb_motion_forw || (codeType ==P_TYPE)){
+ recon->ReconPMBlock(i,recon_right_for,
+ recon_down_for,zero_block_flag,
+ mb_row,mb_col,row_size,dct_start,
+ pictureArray, codeType);
+ //copyFunctions->endNOFloatSection();
+ } else if (mb_motion_back) {
+ recon->ReconBMBlock(i,recon_right_back,
+ recon_down_back,zero_block_flag,
+ mb_row,mb_col,row_size,dct_start,
+ pictureArray);
+ //copyFunctions->endNOFloatSection();
+
+ } else {
+ //DEBUG_MACROBLOCK(cout << "nothing"<<endl;)
+ }
+
+ }
+ copyFunctions->endNOFloatSection();
+ return true;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ComputeForwVector --
+ *
+ * Computes forward motion vector by calling ComputeVector
+ * with appropriate parameters.
+ *
+ * Results:
+ * Reconstructed motion vector placed in recon_right_for_ptr and
+ * recon_down_for_ptr.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+void MacroBlock::computeForwVector(int* recon_right_for_ptr,
+ int* recon_down_for_ptr) {
+
+ Picture *picture;
+
+
+ picture = vid_stream->picture;
+
+
+ unsigned int forw_f=picture->getForw_f();
+ unsigned int full_pel_forw_vector=picture->getFull_pel_forw_vector();
+ vid_stream->motionVector->computeVector(recon_right_for_ptr,
+ recon_down_for_ptr,
+ recon_right_for_prev,
+ recon_down_for_prev,
+ forw_f,
+ full_pel_forw_vector,
+ motion_h_forw_code,
+ motion_v_forw_code,
+ motion_h_forw_r,
+ motion_v_forw_r);
+ picture->setForw_f(forw_f);
+ picture->setFull_pel_forw_vector(full_pel_forw_vector);
+}
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ComputeBackVector --
+ *
+ * Computes backward motion vector by calling ComputeVector
+ * with appropriate parameters.
+ *
+ * Results:
+ * Reconstructed motion vector placed in recon_right_back_ptr and
+ * recon_down_back_ptr.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+void MacroBlock::computeBackVector(int* recon_right_back_ptr,
+ int* recon_down_back_ptr) {
+
+ Picture *picture;
+
+
+ picture = vid_stream->picture;
+
+
+ unsigned int back_f=picture->getBack_f();
+ unsigned int full_pel_back_vector=picture->getFull_pel_back_vector();
+
+ vid_stream->motionVector->computeVector(recon_right_back_ptr,
+ recon_down_back_ptr,
+ recon_right_back_prev,
+ recon_down_back_prev,
+ back_f,
+ full_pel_back_vector,
+ motion_h_back_code,
+ motion_v_back_code,
+ motion_h_back_r,
+ motion_v_back_r);
+ picture->setBack_f(back_f);
+ picture->setFull_pel_back_vector(full_pel_back_vector);
+}
+
+
+
+
+
+int MacroBlock::processSkippedPictures(PictureArray* pictureArray,
+ int code_type,
+ int mb_width) {
+ copyFunctions->startNOFloatSection();
+
+ if (code_type == P_TYPE) {
+
+ ProcessSkippedPFrameMBlocks(pictureArray->getCurrent(),
+ pictureArray->getFuture(),
+ mb_width);
+
+ } else {
+ if (code_type == B_TYPE) {
+ ProcessSkippedBFrameMBlocks(vid_stream->picture,
+ pictureArray->getPast(),
+ pictureArray->getCurrent(),
+ pictureArray->getFuture(),
+ mb_width);
+ }
+ }
+
+ copyFunctions->endNOFloatSection();
+
+ return true;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ProcessSkippedPFrameMBlocks --
+ *
+ * Processes skipped macroblocks in P frames.
+ *
+ * Results:
+ * Calculates pixel values for luminance, Cr, and Cb planes
+ * in current pict image for skipped macroblocks.
+ *
+ * Side effects:
+ * Pixel values in pict image changed.
+ *
+ *--------------------------------------------------------------
+ */
+void MacroBlock::ProcessSkippedPFrameMBlocks(YUVPicture* current,
+ YUVPicture* future,
+ int mb_width) {
+
+ int row_size, half_row, mb_row, mb_col, row, col, rr;
+ int addr, row_incr, half_row_incr, crow, ccol;
+ int *dest, *src, *dest1, *src1;
+
+ /* For each row in macroblock luminance plane... */
+ if (mb_width == 0) {
+ DEBUG_MACROBLOCK(cout << "mb_width in skipped is 0"<<endl;)
+ return;
+ }
+
+
+ /* Calculate row sizes for luminance and Cr/Cb macroblock areas. */
+
+ row_size = mb_width << 4;
+ half_row = (row_size >> 1);
+ row_incr = row_size >> 2;
+ half_row_incr = half_row >> 2;
+
+ /* For each skipped macroblock, do... */
+ int lumEnd=current->getLumLength();
+ int colorEnd=current->getColorLength();
+
+ unsigned char *picDest;
+ unsigned char *picSrc;
+
+ unsigned char *picDestStart;
+ unsigned char *picSrcStart;
+
+
+
+ for (addr = past_mb_addr + 1; addr < mb_address; addr++) {
+
+ /* Calculate macroblock row and col. */
+
+ mb_row = addr / mb_width;
+ mb_col = addr % mb_width;
+
+ /* Calculate upper left pixel row,col for luminance plane. */
+
+ row = mb_row << 4;
+ col = mb_col << 4;
+
+ picDest=current->getLuminancePtr();
+ picSrc=future->getLuminancePtr();
+
+ picDestStart=(picDest+(row*row_size)+col);
+ picSrcStart=(picSrc+(row*row_size)+col);
+
+ if ((picDestStart+7*row_size+7 >= picDest+lumEnd) ||
+ (picDestStart < picDest)) {
+ DEBUG_MACROBLOCK(cout << "urg! last resort caught before sigsev skipped -1"<<endl;)
+ break;
+ }
+ if ((picSrcStart+7*row_size+7 >= picSrc+lumEnd) ||
+ (picSrcStart < picSrc)) {
+ DEBUG_MACROBLOCK(cout << "urg! last resort caught before sigsev skipped -2"<<endl;)
+ break;
+ }
+
+ dest=(int*)picDestStart;
+ src=(int*)picSrcStart;
+
+
+
+ for (rr = 0; rr < 8; rr++) {
+
+ /* Copy pixel values from last I or P picture. */
+ memcpy(dest,src,sizeof(int)*4);
+
+ dest += row_incr;
+ src += row_incr;
+ memcpy(dest,src,sizeof(int)*4);
+
+ dest += row_incr;
+ src += row_incr;
+ }
+
+ /*
+ * Divide row,col to get upper left pixel of macroblock in Cr and Cb
+ * planes.
+ */
+
+ crow = row >> 1;
+ ccol = col >> 1;
+
+ /* For each row in Cr, and Cb planes... */
+ picDest=current->getCrPtr();
+ picDestStart=(picDest+(crow*half_row)+ccol);
+ if ((picDestStart+7*half_row_incr+7 >= picDest+colorEnd) ||
+ (picDestStart < picDest)) {
+ DEBUG_MACROBLOCK(cout << "urg! last resort caught before sigsev skipped -3"<<endl;)
+ break;
+ }
+
+
+ dest=(int*)(current->getCrPtr()+(crow*half_row)+ccol);
+ src=(int*)(future->getCrPtr()+(crow*half_row)+ccol);
+ dest1=(int*)(current->getCbPtr()+(crow*half_row)+ccol);
+ src1=(int*)(future->getCbPtr()+(crow*half_row)+ccol);
+
+ for (rr = 0; rr < 4; rr++) {
+
+ /* Copy pixel values from last I or P picture. */
+ memcpy(dest,src,sizeof(int)*2);
+ memcpy(dest1,src1,sizeof(int)*2);
+
+
+ dest += half_row_incr;
+ src += half_row_incr;
+ dest1 += half_row_incr;
+ src1 += half_row_incr;
+
+ memcpy(dest,src,sizeof(int)*2);
+ memcpy(dest1,src1,sizeof(int)*2);
+
+ dest += half_row_incr;
+ src += half_row_incr;
+ dest1 += half_row_incr;
+ src1 += half_row_incr;
+ }
+
+ }
+
+ recon_right_for_prev = 0;
+ recon_down_for_prev = 0;
+
+}
+
+
+
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ProcessSkippedBFrameMBlocks --
+ *
+ * Processes skipped macroblocks in B frames.
+ *
+ * Results:
+ * Calculates pixel values for luminance, Cr, and Cb planes
+ * in current pict image for skipped macroblocks.
+ *
+ * Side effects:
+ * Pixel values in pict image changed.
+ *
+ *--------------------------------------------------------------
+ */
+
+void MacroBlock::ProcessSkippedBFrameMBlocks(Picture* picture,
+ YUVPicture* past,
+ YUVPicture* current,
+ YUVPicture* future,
+ int mb_width) {
+ int row_size, half_row, mb_row, mb_col, row, col, rr;
+ int right_half_for = 0, down_half_for = 0;
+ int c_right_half_for = 0, c_down_half_for = 0;
+ int right_half_back = 0, down_half_back = 0;
+ int c_right_half_back = 0, c_down_half_back = 0;
+ int addr, right_for = 0, down_for = 0;
+ int recon_right_for, recon_down_for;
+ int recon_right_back, recon_down_back;
+ int right_back = 0, down_back = 0;
+ int c_right_for = 0, c_down_for = 0;
+ int c_right_back = 0, c_down_back = 0;
+ unsigned char forw_lum[256];
+ unsigned char forw_cr[64], forw_cb[64];
+ unsigned char back_lum[256], back_cr[64], back_cb[64];
+ int row_incr, half_row_incr;
+ int ccol, crow;
+
+
+ /* Calculate row sizes for luminance and Cr/Cb macroblock areas. */
+
+ if (mb_width == 0) {
+ DEBUG_MACROBLOCK(cout << "mb_width in skipped is 0 (2)"<<endl;)
+ return;
+ }
+
+ row_size = mb_width << 4;
+ half_row = (row_size >> 1);
+ row_incr = row_size >> 2;
+ half_row_incr = half_row >> 2;
+
+ /* Establish motion vector codes based on full pixel flag. */
+
+ if (picture->getFull_pel_forw_vector()) {
+ recon_right_for = recon_right_for_prev << 1;
+ recon_down_for = recon_down_for_prev << 1;
+ } else {
+ recon_right_for = recon_right_for_prev;
+ recon_down_for = recon_down_for_prev;
+ }
+
+ if (picture->getFull_pel_back_vector()) {
+ recon_right_back = recon_right_back_prev << 1;
+ recon_down_back = recon_down_back_prev << 1;
+ } else {
+ recon_right_back = recon_right_back_prev;
+ recon_down_back = recon_down_back_prev;
+ }
+
+
+ /* If only one motion vector, do display copy, else do full
+ calculation.
+ */
+
+ /* Calculate motion vectors. */
+
+ if (bpict_past_forw) {
+ right_for = recon_right_for >> 1;
+ down_for = recon_down_for >> 1;
+ right_half_for = recon_right_for & 0x1;
+ down_half_for = recon_down_for & 0x1;
+
+ recon_right_for /= 2;
+ recon_down_for /= 2;
+ c_right_for = recon_right_for >> 1;
+ c_down_for = recon_down_for >> 1;
+ c_right_half_for = recon_right_for & 0x1;
+ c_down_half_for = recon_down_for & 0x1;
+
+ }
+ if (bpict_past_back) {
+ right_back = recon_right_back >> 1;
+ down_back = recon_down_back >> 1;
+ right_half_back = recon_right_back & 0x1;
+ down_half_back = recon_down_back & 0x1;
+
+ recon_right_back /= 2;
+ recon_down_back /= 2;
+ c_right_back = recon_right_back >> 1;
+ c_down_back = recon_down_back >> 1;
+ c_right_half_back = recon_right_back & 0x1;
+ c_down_half_back = recon_down_back & 0x1;
+
+ }
+ /* For each skipped macroblock, do... */
+
+ for (addr = past_mb_addr + 1;
+ addr < mb_address; addr++) {
+
+ /* Calculate macroblock row and col. */
+
+ mb_row = addr / mb_width;
+ mb_col = addr % mb_width;
+
+ /* Calculate upper left pixel row,col for luminance plane. */
+
+ row = mb_row << 4;
+ col = mb_col << 4;
+ crow = row / 2;
+ ccol = col / 2;
+
+ /* If forward predicted, calculate prediction values. */
+ if (bpict_past_forw) {
+ int lumEnd=current->getLumLength();
+ int colorEnd=current->getColorLength();
+ ReconSkippedBlock(past->getLuminancePtr(),
+ forw_lum,row,col,row_size,
+ right_for,down_for,
+ right_half_for,
+ down_half_for,16,lumEnd);
+ ReconSkippedBlock(past->getCrPtr(),
+ forw_cr,crow,ccol, half_row,
+ c_right_for,c_down_for,
+ c_right_half_for,
+ c_down_half_for,8,colorEnd);
+ ReconSkippedBlock(past->getCbPtr(),
+ forw_cb,crow,ccol,half_row,
+ c_right_for,c_down_for,
+ c_right_half_for,
+ c_down_half_for,8,colorEnd);
+ }
+ /* If back predicted, calculate prediction values. */
+
+ if (bpict_past_back) {
+ int lumEnd=current->getLumLength();
+ int colorEnd=current->getColorLength();
+ ReconSkippedBlock(future->getLuminancePtr(),
+ back_lum,row,col,row_size,
+ right_back,down_back,
+ right_half_back,down_half_back,
+ 16,lumEnd);
+ ReconSkippedBlock(future->getCrPtr(),
+ back_cr,crow,ccol,
+ half_row,c_right_back,
+ c_down_back,c_right_half_back,
+ c_down_half_back,8,colorEnd);
+ ReconSkippedBlock(future->getCbPtr(),
+ back_cb,crow,ccol,half_row,
+ c_right_back,c_down_back,
+ c_right_half_back,
+ c_down_half_back,8,colorEnd);
+ }
+ unsigned char* picDest=current->getLuminancePtr();
+ int lumEnd=current->getLumLength();
+ int colorEnd=current->getColorLength();
+
+ unsigned char* picDestStart=(picDest+(row*row_size)+col);
+
+
+ if ((picDestStart+7*row_size+7 >= picDest+lumEnd) ||
+ (picDestStart < picDest)) {
+ DEBUG_MACROBLOCK(cout << "urg! last resort caught before sigsev skipped -4"<<endl;)
+ return;
+ }
+
+ picDest=current->getCrPtr();
+ picDestStart=(picDest+(crow*half_row)+ccol);
+ if ((picDestStart+7*half_row_incr+7 >= picDest+colorEnd) ||
+ (picDestStart < picDest)) {
+ DEBUG_MACROBLOCK(cout << "urg! last resort caught before sigsev skipped -5"<<endl;)
+ exit(0);
+ }
+
+
+ if (bpict_past_forw && !bpict_past_back) {
+
+ int *dest, *dest1;
+ int *src, *src1;
+ dest=(int*)(current->getLuminancePtr()+(row*row_size)+col);
+ src=(int*)forw_lum;
+
+ for (rr = 0; rr < 16; rr++) {
+
+ /* memcpy(dest, forw_lum+(rr<<4), 16); */
+
+ dest[0] = src[0];
+ dest[1] = src[1];
+ dest[2] = src[2];
+ dest[3] = src[3];
+ dest += row_incr;
+ src += 4;
+ }
+
+ dest = (int*)(current->getCrPtr()+(crow*half_row)+ccol);
+ dest1 = (int*)(current->getCbPtr()+(crow*half_row)+ccol);
+ src = (int*)forw_cr;
+ src1 = (int*)forw_cb;
+
+ for (rr = 0; rr < 8; rr++) {
+ /*
+ * memcpy(dest, forw_cr+(rr<<3), 8); memcpy(dest1, forw_cb+(rr<<3),
+ * 8);
+ */
+
+ dest[0] = src[0];
+ dest[1] = src[1];
+
+ dest1[0] = src1[0];
+ dest1[1] = src1[1];
+
+ dest += half_row_incr;
+ dest1 += half_row_incr;
+ src += 2;
+ src1 += 2;
+ }
+ } else if (bpict_past_back && !bpict_past_forw) {
+
+ int *src, *src1;
+ int *dest, *dest1;
+ dest=(int*)(current->getLuminancePtr()+(row*row_size)+col);
+ src = (int*)back_lum;
+
+ for (rr = 0; rr < 16; rr++) {
+ dest[0] = src[0];
+ dest[1] = src[1];
+ dest[2] = src[2];
+ dest[3] = src[3];
+ dest += row_incr;
+ src += 4;
+ }
+
+
+ dest = (int *)(current->getCrPtr()+(crow*half_row)+ccol);
+ dest1 = (int *)(current->getCbPtr()+(crow*half_row)+ccol);
+ src = (int *)back_cr;
+ src1 = (int *)back_cb;
+
+ for (rr = 0; rr < 8; rr++) {
+ /*
+ * memcpy(dest, back_cr+(rr<<3), 8); memcpy(dest1, back_cb+(rr<<3),
+ * 8);
+ */
+
+ dest[0] = src[0];
+ dest[1] = src[1];
+
+ dest1[0] = src1[0];
+ dest1[1] = src1[1];
+
+ dest += half_row_incr;
+ dest1 += half_row_incr;
+ src += 2;
+ src1 += 2;
+ }
+ } else {
+
+ unsigned char *src1, *src2, *src1a, *src2a;
+ unsigned char *dest, *dest1;
+ dest = current->getLuminancePtr()+(row*row_size)+col;
+ src1 = forw_lum;
+ src2 = back_lum;
+
+ for (rr = 0; rr < 16; rr++) {
+ dest[0] = (int) (src1[0] + src2[0]) >> 1;
+ dest[1] = (int) (src1[1] + src2[1]) >> 1;
+ dest[2] = (int) (src1[2] + src2[2]) >> 1;
+ dest[3] = (int) (src1[3] + src2[3]) >> 1;
+ dest[4] = (int) (src1[4] + src2[4]) >> 1;
+ dest[5] = (int) (src1[5] + src2[5]) >> 1;
+ dest[6] = (int) (src1[6] + src2[6]) >> 1;
+ dest[7] = (int) (src1[7] + src2[7]) >> 1;
+ dest[8] = (int) (src1[8] + src2[8]) >> 1;
+ dest[9] = (int) (src1[9] + src2[9]) >> 1;
+ dest[10] = (int) (src1[10] + src2[10]) >> 1;
+ dest[11] = (int) (src1[11] + src2[11]) >> 1;
+ dest[12] = (int) (src1[12] + src2[12]) >> 1;
+ dest[13] = (int) (src1[13] + src2[13]) >> 1;
+ dest[14] = (int) (src1[14] + src2[14]) >> 1;
+ dest[15] = (int) (src1[15] + src2[15]) >> 1;
+ dest += row_size;
+ src1 += 16;
+ src2 += 16;
+ }
+
+
+ dest = current->getCrPtr() + (crow * half_row) + ccol;
+ dest1 = current->getCbPtr() + (crow * half_row) + ccol;
+ src1 = forw_cr;
+ src2 = back_cr;
+ src1a = forw_cb;
+ src2a = back_cb;
+
+ for (rr = 0; rr < 8; rr++) {
+ dest[0] = (int) (src1[0] + src2[0]) >> 1;
+ dest[1] = (int) (src1[1] + src2[1]) >> 1;
+ dest[2] = (int) (src1[2] + src2[2]) >> 1;
+ dest[3] = (int) (src1[3] + src2[3]) >> 1;
+ dest[4] = (int) (src1[4] + src2[4]) >> 1;
+ dest[5] = (int) (src1[5] + src2[5]) >> 1;
+ dest[6] = (int) (src1[6] + src2[6]) >> 1;
+ dest[7] = (int) (src1[7] + src2[7]) >> 1;
+ dest += half_row;
+ src1 += 8;
+ src2 += 8;
+
+ dest1[0] = (int) (src1a[0] + src2a[0]) >> 1;
+ dest1[1] = (int) (src1a[1] + src2a[1]) >> 1;
+ dest1[2] = (int) (src1a[2] + src2a[2]) >> 1;
+ dest1[3] = (int) (src1a[3] + src2a[3]) >> 1;
+ dest1[4] = (int) (src1a[4] + src2a[4]) >> 1;
+ dest1[5] = (int) (src1a[5] + src2a[5]) >> 1;
+ dest1[6] = (int) (src1a[6] + src2a[6]) >> 1;
+ dest1[7] = (int) (src1a[7] + src2a[7]) >> 1;
+ dest1 += half_row;
+ src1a += 8;
+ src2a += 8;
+ }
+ }
+
+ }
+}
+
+
+
+
+
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ReconSkippedBlock --
+ *
+ * Reconstructs predictive block for skipped macroblocks
+ * in B Frames.
+ *
+ * Results:
+ * No return values.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+void MacroBlock::ReconSkippedBlock(unsigned char* source,
+ unsigned char* dest,
+ int row,
+ int col,
+ int row_size,
+ int right,
+ int down,
+ int right_half,
+ int down_half,
+ int width,int maxLen) {
+ int rr;
+ unsigned char *source2;
+ unsigned char *tmp;
+
+ tmp = source+((row + down) * row_size) + col + right;
+
+
+ if ((tmp+7*row_size+7 >= source+maxLen) ||
+ (tmp < source)) {
+ DEBUG_MACROBLOCK(cout << "urg! last resort caught before sigsev skipped -6"<<endl;)
+ return;
+ }
+
+ source=tmp;
+ if (width == 16) {
+ if ((!right_half) && (!down_half)) {
+ if (right & 0x1) {
+ /* No alignment, use bye copy */
+ for (rr = 0; rr < 16; rr++) {
+
+ memcpy(dest,source,sizeof(char)*16);
+
+ dest += 16;
+ source += row_size;
+ }
+ } else if (right & 0x2) {
+ /* Half-word bit aligned, use 16 bit copy */
+ short *src = (short *)source;
+ short *d = (short *)dest;
+ row_size >>= 1;
+ for (rr = 0; rr < 16; rr++) {
+
+ memcpy(d,src,sizeof(short)*8);
+
+ d += 8;
+ src += row_size;
+ }
+ } else {
+ /* Word aligned, use 32 bit copy */
+ int *src = (int *)source;
+ int *d = (int *)dest;
+ row_size >>= 2;
+ for (rr = 0; rr < 16; rr++) {
+ d[0] = src[0];
+ d[1] = src[1];
+ d[2] = src[2];
+ d[3] = src[3];
+ d += 4;
+ src += row_size;
+ }
+ }
+ } else {
+ source2 = source + right_half + (row_size * down_half);
+ copyFunctions->copy16_div2_destlinear_nocrop(source,source2,dest,
+ row_size);
+
+
+ }
+ } else { /* (width == 8) */
+ assert(width == 8);
+ if ((!right_half) && (!down_half)) {
+ if (right & 0x1) {
+ for (rr = 0; rr < width; rr++) {
+
+ memcpy(dest,source,sizeof(char)*8);
+
+ dest += 8;
+ source += row_size;
+ }
+ } else if (right & 0x02) {
+ short *d = (short *)dest;
+ short *src = (short *)source;
+ row_size >>= 1;
+ for (rr = 0; rr < width; rr++) {
+ d[0] = src[0];
+ d[1] = src[1];
+ d[2] = src[2];
+ d[3] = src[3];
+ d += 4;
+ src += row_size;
+ }
+ } else {
+ int *d = (int *)dest;
+ int *src = (int *)source;
+ row_size >>= 2;
+ for (rr = 0; rr < width; rr++) {
+ d[0] = src[0];
+ d[1] = src[1];
+ d += 2;
+ src += row_size;
+ }
+ }
+ } else {
+ source2 = source + right_half + (row_size * down_half);
+ copyFunctions->copy8_div2_destlinear_nocrop(source,source2,
+ dest,row_size);
+ }
+ }
+}
+
diff --git a/mpeglib/lib/mpegplay/macroBlock.h b/mpeglib/lib/mpegplay/macroBlock.h
new file mode 100644
index 00000000..db56e7be
--- /dev/null
+++ b/mpeglib/lib/mpegplay/macroBlock.h
@@ -0,0 +1,97 @@
+/*
+ stores macroblock infos
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __MACROBLOCK_H
+#define __MACROBLOCK_H
+
+#include "videoDecoder.h"
+#include "decoderClass.h"
+#include "motionVector.h"
+#include "recon.h"
+#include "copyFunctions.h"
+
+class MacroBlock {
+
+ /* Macroblock structure. */
+
+ int mb_address; /* Macroblock address. */
+ int past_mb_addr; /* Previous mblock address. */
+ int motion_h_forw_code; /* Forw. horiz. motion vector code. */
+ unsigned int motion_h_forw_r; /* Used in decoding vectors. */
+ int motion_v_forw_code; /* Forw. vert. motion vector code. */
+ unsigned int motion_v_forw_r; /* Used in decdoinge vectors. */
+ int motion_h_back_code; /* Back horiz. motion vector code. */
+ unsigned int motion_h_back_r; /* Used in decoding vectors. */
+ int motion_v_back_code; /* Back vert. motion vector code. */
+ unsigned int motion_v_back_r; /* Used in decoding vectors. */
+ unsigned int cbp; /* Coded block pattern. */
+ int mb_intra; /* Intracoded mblock flag. */
+ int bpict_past_forw; /* Past B frame forw. vector flag. */
+ int bpict_past_back; /* Past B frame back vector flag. */
+ int past_intra_addr; /* Addr of last intracoded mblock. */
+ int recon_right_for_prev; /* Past right forw. vector. */
+ int recon_down_for_prev; /* Past down forw. vector. */
+ int recon_right_back_prev; /* Past right back vector. */
+ int recon_down_back_prev; /* Past down back vector. */
+
+ class VideoDecoder* vid_stream;
+ CopyFunctions* copyFunctions;
+
+ public:
+ MacroBlock(class VideoDecoder* vid_stream);
+ ~MacroBlock();
+ int processMacroBlock(PictureArray* pictureArray);
+ int resetMacroBlock();
+ int resetPastMacroBlock();
+
+ private:
+ int reconstruct(int& recon_right_for,
+ int& recon_down_for,
+ int& recon_right_back,
+ int& recon_down_back,
+ int& mb_motion_forw,
+ int& mb_motion_back,
+ PictureArray* pictureArray);
+
+ void computeForwVector(int* recon_right_for_ptr,
+ int* recon_down_for_ptr);
+
+ void computeBackVector(int* recon_right_back_ptr,
+ int* recon_down_back_ptr);
+
+ int processSkippedPictures(PictureArray* pictureArray,
+ int code_type,
+ int mb_width);
+
+ void ProcessSkippedPFrameMBlocks(YUVPicture* current,
+ YUVPicture* future,
+ int mb_width);
+
+
+ void ProcessSkippedBFrameMBlocks(Picture* picture,
+ YUVPicture* past,
+ YUVPicture* current,
+ YUVPicture* future,
+ int mb_width);
+
+
+ void ReconSkippedBlock(unsigned char* source,unsigned char* dest,
+ int row, int col,int row_size,
+ int right,int down,
+ int right_half,int down_half,int width,
+ int maxLen);
+
+
+};
+#endif
diff --git a/mpeglib/lib/mpegplay/mainMpegPlay.cpp b/mpeglib/lib/mpegplay/mainMpegPlay.cpp
new file mode 100644
index 00000000..1c3d9659
--- /dev/null
+++ b/mpeglib/lib/mpegplay/mainMpegPlay.cpp
@@ -0,0 +1,179 @@
+/*
+ * main.c --
+ *
+ * Example program for mpegplay library.
+ * Build with :
+ BUILD with:(if you have intel mmx)
+
+
+ g++ -DINTEL -I/usr/X11R6/include -I.. \
+ -o mpegplay mainMpegPlay.cpp -L/usr/X11R6/lib \
+ ../.libs/libmpeg.a -lX11 -lXext -lXv -lXxf86dga \
+ -lXxf86dga -lpthread
+
+ */
+
+
+#include "mpegPlugin.h"
+#include "../output/outPlugin.h"
+#include "mpegVideoLength.h"
+
+// Includes for non plugin version
+#define _NO_PLUGIN_VERSION
+//#define _PLUGIN_VERSION
+
+
+#ifdef _NO_PLUGIN_VERSION
+
+int main(int argc, char** argv) {
+
+
+
+ if (argc <= 1) {
+ printf("Usage:\n\n");
+ printf("%s filename\n\n",argv[0]);
+ exit(0);
+ }
+ cout << "open -s 1"<<endl;
+
+ InputStream* input=InputPlugin::createInputStream(argv[1]);
+ OutputStream* output=OutPlugin::createOutputStream(_OUTPUT_LOCAL);
+
+ output->config("performance","true",NULL);
+ cout << "open -s"<<endl;
+ input->open(argv[1]);
+ //loader->seek(1024*1024*364+1024*600);
+ //loader->seek(1024*1024*333);
+
+ MpegVideoLength* mpegVideoLength=new MpegVideoLength(input);
+
+
+ cout << "START length calc"<<endl;
+ while (mpegVideoLength->firstInitialize()==false) {
+ if (input->eof()) {
+ break;
+ }
+ continue;
+ }
+ int len=mpegVideoLength->getLength();
+ cout << "END length calc"<<endl;
+
+
+ MpegVideoStream* mpegVideoStream=new MpegVideoStream(input);
+ MpegVideoHeader* mpegVideoHeader=new MpegVideoHeader();
+ cout << "start init"<<endl;
+ while (mpegVideoStream->firstInitialize(mpegVideoHeader)==false) {
+
+ if (input->eof()) {
+ break;
+ }
+ }
+
+ VideoDecoder* video;
+ mpegVideoHeader->print("start");
+ cout << "**************"<<endl;
+
+ PictureArray* pictureArray;
+ YUVPicture* pic;
+
+ // now create pictureArray from the sequence
+ int width=mpegVideoHeader->getMB_Width()*16;
+ int height=mpegVideoHeader->getMB_Height()*16;
+ cout << "width:"<<width<<" height:"<<height<<endl;
+
+ output->openWindow(width,height,(char*)"kmpg");
+
+ video= new VideoDecoder(mpegVideoStream,mpegVideoHeader);
+
+
+ // init is true
+ int cnt=0;
+ while (input->eof()==false){
+ pictureArray=output->lockPictureArray();
+ video->mpegVidRsrc(pictureArray);
+ pic=pictureArray->getYUVPictureCallback();
+ if (pic == NULL) {
+ // nothin to display
+ continue;
+ }
+
+ output->unlockPictureArray(pictureArray);
+ pictureArray->setYUVPictureCallback(NULL);
+
+ }
+ cout << "DestroyVideoDecoder"<<endl;
+ delete video;
+
+
+ delete mpegVideoHeader;
+ delete mpegVideoStream;
+ cout << "end"<<endl;
+
+}
+
+#endif
+
+
+/*
+
+ threaded Plugin version
+
+ ( not useful for gprof )
+*/
+
+#ifdef _PLUGIN_VERSION
+
+int main(int argc, char** argv) {
+
+
+
+ if (argc <= 1) {
+ printf("Usage:\n\n");
+ printf("%s filename\n\n",argv[0]);
+ exit(0);
+ }
+
+ //
+ // The order is important !!!!
+ // 1. construct
+ // 2. set Output
+ // 3. open input
+ // 4. set input
+ //
+ // you cannot set the input _before_ the output
+ // in fact you can, but this gives you a segfault!
+
+ MpegPlugin* plugin=new MpegPlugin();
+ OutputStream* out=OutPlugin::createOutputStream(_OUTPUT_LOCAL);
+ InputStream* in=InputPlugin::createInputStream(argv[1]);
+
+ cout << "open -s 1"<<endl;
+
+ // The plugin does not do "open"
+ in->open(argv[1]);
+ cout << "open -s 2"<<endl;
+
+ // watch the order!
+ plugin->setOutputPlugin(out);
+ cout << "open -s 3"<<endl;
+ plugin->setInputPlugin(in);
+ cout << "open -s 4"<<endl;
+
+ plugin->play();
+ int cnt=0;
+ while(plugin->getStreamState() != _STREAM_STATE_EOF) {
+ sleep(1);
+ PluginInfo* pluginInfo=plugin->getPluginInfo();
+ pluginInfo->print();
+ }
+ cout << "plugin eof"<<endl;
+ plugin->close();
+
+ delete plugin;
+ delete in;
+ delete out;
+
+}
+
+#endif
+
diff --git a/mpeglib/lib/mpegplay/mmxidct.cpp b/mpeglib/lib/mpegplay/mmxidct.cpp
new file mode 100644
index 00000000..f6e12409
--- /dev/null
+++ b/mpeglib/lib/mpegplay/mmxidct.cpp
@@ -0,0 +1,27 @@
+/*
+ wapper for other architectures than mmx
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "mmxidct.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#ifndef INTEL
+ void IDCT_mmx(short int* reconptr) {
+ printf("urgs mmxidct!\n");
+ exit(0);
+ }
+
+#endif
diff --git a/mpeglib/lib/mpegplay/mmxidct.h b/mpeglib/lib/mpegplay/mmxidct.h
new file mode 100644
index 00000000..9cdfe18c
--- /dev/null
+++ b/mpeglib/lib/mpegplay/mmxidct.h
@@ -0,0 +1,22 @@
+/*
+ wapper for other architectures than mmx
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __MMXIDCT_H
+#define __MMXIDCT_H
+
+#include <stdio.h>
+#include <stdlib.h>
+
+extern "C" void IDCT_mmx(short int* reconptr);
+
+#endif
diff --git a/mpeglib/lib/mpegplay/mmxidct_asm.S b/mpeglib/lib/mpegplay/mmxidct_asm.S
new file mode 100644
index 00000000..b618c561
--- /dev/null
+++ b/mpeglib/lib/mpegplay/mmxidct_asm.S
@@ -0,0 +1,738 @@
+
+#ifndef INTEL
+
+/**
+ Some compilers cannot compile empty files.
+*/
+
+IDCT_mmx_dummy:
+ .section .note.GNU-stack,"",%progbits
+#endif
+
+
+#ifdef INTEL
+
+
+/*
+ * the input data is tranposed and each 16 bit element in the 8x8 matrix
+ * is left aligned:
+ * for example in 11...1110000 format
+ * If the iDCT is of I macroblock then 0.5 needs to be added to the;
+ * DC Component
+ * (element[0][0] of the matrix)
+ */
+
+/* extrn re_matrix */
+
+.data
+ .align 16
+ .type preSC,@object
+preSC: .short 16384,22725,21407,19266,16384,12873,8867,4520
+ .short 22725,31521,29692,26722,22725,17855,12299,6270
+ .short 21407,29692,27969,25172,21407,16819,11585,5906
+ .short 19266,26722,25172,22654,19266,15137,10426,5315
+ .short 16384,22725,21407,19266,16384,12873,8867,4520
+ .short 12873,17855,16819,15137,25746,20228,13933,7103
+ .short 17734,24598,23170,20853,17734,13933,9597,4892
+ .short 18081,25080,23624,21261,18081,14206,9785,4988
+ .size preSC,128
+ .align 8
+ .type x0005000200010001,@object
+ .size x0005000200010001,8
+x0005000200010001:
+ .long 0x00010001,0x00050002
+ .align 8
+ .type x0040000000000000,@object
+ .size x0040000000000000,8
+x0040000000000000:
+ .long 0, 0x00400000
+ .align 8
+ .type x5a825a825a825a82,@object
+ .size x5a825a825a825a82,8
+x5a825a825a825a82:
+ .long 0x5a825a82, 0x5a825a82
+ .align 8
+ .type x539f539f539f539f,@object
+ .size x539f539f539f539f,8
+x539f539f539f539f:
+ .long 0x539f539f,0x539f539f
+ .align 8
+ .type x4546454645464546,@object
+ .size x4546454645464546,8
+x4546454645464546:
+ .long 0x45464546,0x45464546
+ .align 8
+ .type x61f861f861f861f8,@object
+ .size x61f861f861f861f8,8
+x61f861f861f861f8:
+ .long 0x61f861f8,0x61f861f8
+ .align 8
+ .type scratch1,@object
+ .size scratch1,8
+scratch1:
+ .long 0,0
+ .align 8
+ .type scratch3,@object
+ .size scratch3,8
+scratch3:
+ .long 0,0
+ .align 8
+ .type scratch5,@object
+ .size scratch5,8
+scratch5:
+ .long 0,0
+ .align 8
+ .type scratch7,@object
+ .size scratch7,8
+scratch7:
+ .long 0,0
+ .type x0,@object
+ .size x0,8
+x0:
+ .long 0,0
+ .align 8
+.text
+ .align 4
+.globl IDCT_mmx
+ .type IDCT_mmx,@function
+IDCT_mmx:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %ebx
+ pushl %ecx
+ pushl %edx
+ pushl %esi
+ pushl %edi
+ movl 8(%ebp),%esi /* source matrix */
+#if 0
+ movq (%esi), %mm0
+ movq 8(%esi), %mm1
+ psllw $4, %mm0
+ movq 16(%esi), %mm2
+ psllw $4, %mm1
+ movq 24(%esi), %mm3
+ psllw $4, %mm2
+ movq 32(%esi), %mm4
+ psllw $4, %mm3
+ movq 40(%esi), %mm5
+ psllw $4, %mm4
+ movq 48(%esi), %mm6
+ psllw $4, %mm5
+ movq 56(%esi), %mm7
+ psllw $4, %mm6
+ psllw $4, %mm7
+ movq %mm0, (%esi)
+ movq %mm1, 8(%esi)
+ movq %mm2,16(%esi)
+ movq %mm3,24(%esi)
+ movq %mm4,32(%esi)
+ movq %mm5,40(%esi)
+ movq %mm6,48(%esi)
+ movq %mm7,56(%esi)
+ movq 64(%esi), %mm0
+ movq 72(%esi), %mm1
+ psllw $4, %mm0
+ movq 80(%esi), %mm2
+ psllw $4, %mm1
+ movq 88(%esi), %mm3
+ psllw $4, %mm2
+ movq 96(%esi), %mm4
+ psllw $4, %mm3
+ movq 104(%esi), %mm5
+ psllw $4, %mm4
+ movq 112(%esi), %mm6
+ psllw $4, %mm5
+ movq 120(%esi), %mm7
+ psllw $4, %mm6
+ psllw $4, %mm7
+ movq %mm0,64(%esi)
+ movq %mm1,72(%esi)
+ movq %mm2,80(%esi)
+ movq %mm3,88(%esi)
+ movq %mm4,96(%esi)
+ movq %mm5,104(%esi)
+ movq %mm6,112(%esi)
+ movq %mm7,120(%esi)
+#endif
+ leal preSC, %ecx
+/* column 0: even part
+ * use V4, V12, V0, V8 to produce V22..V25
+ */
+ movq 8*12(%ecx), %mm0 /* maybe the first mul can be done together */
+ /* with the dequantization in iHuff module */
+ pmulhw 8*12(%esi), %mm0 /* V12 */
+ movq 8*4(%ecx), %mm1
+ pmulhw 8*4(%esi), %mm1 /* V4 */
+ movq (%ecx), %mm3
+ psraw $1, %mm0 /* t64=t66 */
+ pmulhw (%esi), %mm3 /* V0 */
+ movq 8*8(%ecx), %mm5 /* duplicate V4 */
+ movq %mm1, %mm2 /* added 11/1/96 */
+ pmulhw 8*8(%esi),%mm5 /* V8 */
+ psubsw %mm0, %mm1 /* V16 */
+ pmulhw x5a825a825a825a82, %mm1 /* 23170 ->V18 */
+ paddsw %mm0, %mm2 /* V17 */
+ movq %mm2, %mm0 /* duplicate V17 */
+ psraw $1, %mm2 /* t75=t82 */
+ psraw $2, %mm0 /* t72 */
+ movq %mm3, %mm4 /* duplicate V0 */
+ paddsw %mm5, %mm3 /* V19 */
+ psubsw %mm5, %mm4 /* V20 ;mm5 free */
+/* moved from the block below */
+ movq 8*10(%ecx), %mm7
+ psraw $1, %mm3 /* t74=t81 */
+ movq %mm3, %mm6 /* duplicate t74=t81 */
+ psraw $2, %mm4 /* t77=t79 */
+ psubsw %mm0, %mm1 /* V21 ; mm0 free */
+ paddsw %mm2, %mm3 /* V22 */
+ movq %mm1, %mm5 /* duplicate V21 */
+ paddsw %mm4, %mm1 /* V23 */
+ movq %mm3, 8*4(%esi) /* V22 */
+ psubsw %mm5, %mm4 /* V24; mm5 free */
+ movq %mm1, 8*12(%esi) /* V23 */
+ psubsw %mm2, %mm6 /* V25; mm2 free */
+ movq %mm4, (%esi) /* V24 */
+/* keep mm6 alive all along the next block */
+ /* movq %mm6, 8*8(%esi) V25 */
+/* column 0: odd part
+ * use V2, V6, V10, V14 to produce V31, V39, V40, V41
+ */
+/* moved above: movq 8*10(%ecx), %mm7 */
+
+ pmulhw 8*10(%esi), %mm7 /* V10 */
+ movq 8*6(%ecx), %mm0
+ pmulhw 8*6(%esi), %mm0 /* V6 */
+ movq 8*2(%ecx), %mm5
+ movq %mm7, %mm3 /* duplicate V10 */
+ pmulhw 8*2(%esi), %mm5 /* V2 */
+ movq 8*14(%ecx), %mm4
+ psubsw %mm0, %mm7 /* V26 */
+ pmulhw 8*14(%esi), %mm4 /* V14 */
+ paddsw %mm0, %mm3 /* V29 ; free mm0 */
+ movq %mm7, %mm1 /* duplicate V26 */
+ psraw $1, %mm3 /* t91=t94 */
+ pmulhw x539f539f539f539f,%mm7 /* V33 */
+ psraw $1, %mm1 /* t96 */
+ movq %mm5, %mm0 /* duplicate V2 */
+ psraw $2, %mm4 /* t85=t87 */
+ paddsw %mm4,%mm5 /* V27 */
+ psubsw %mm4, %mm0 /* V28 ; free mm4 */
+ movq %mm0, %mm2 /* duplicate V28 */
+ psraw $1, %mm5 /* t90=t93 */
+ pmulhw x4546454645464546,%mm0 /* V35 */
+ psraw $1, %mm2 /* t97 */
+ movq %mm5, %mm4 /* duplicate t90=t93 */
+ psubsw %mm2, %mm1 /* V32 ; free mm2 */
+ pmulhw x61f861f861f861f8,%mm1 /* V36 */
+ psllw $1, %mm7 /* t107 */
+ paddsw %mm3, %mm5 /* V31 */
+ psubsw %mm3, %mm4 /* V30 ; free mm3 */
+ pmulhw x5a825a825a825a82,%mm4 /* V34 */
+ nop
+ psubsw %mm1, %mm0 /* V38 */
+ psubsw %mm7, %mm1 /* V37 ; free mm7 */
+ psllw $1, %mm1 /* t114 */
+/* move from the next block */
+ movq %mm6, %mm3 /* duplicate V25 */
+/* move from the next block */
+ movq 8*4(%esi), %mm7 /* V22 */
+ psllw $1, %mm0 /* t110 */
+ psubsw %mm5, %mm0 /* V39 (mm5 needed for next block) */
+ psllw $2, %mm4 /* t112 */
+/* moved from the next block */
+ movq 8*12(%esi), %mm2 /* V23 */
+ psubsw %mm0, %mm4 /* V40 */
+ paddsw %mm4, %mm1 /* V41; free mm0 */
+/* moved from the next block */
+ psllw $1, %mm2 /* t117=t125 */
+/* column 0: output butterfly */
+/* moved above:
+ * movq %mm6, %mm3 duplicate V25
+ * movq 8*4(%esi), %mm7 V22
+ * movq 8*12(%esi), %mm2 V23
+ * psllw $1, %mm2 t117=t125
+ */
+ psubsw %mm1, %mm6 /* tm6 */
+ paddsw %mm1, %mm3 /* tm8; free mm1 */
+ movq %mm7, %mm1 /* duplicate V22 */
+ paddsw %mm5, %mm7 /* tm0 */
+ movq %mm3, 8*8(%esi) /* tm8; free mm3 */
+ psubsw %mm5, %mm1 /* tm14; free mm5 */
+ movq %mm6, 8*6(%esi) /* tm6; free mm6 */
+ movq %mm2, %mm3 /* duplicate t117=t125 */
+ movq (%esi), %mm6 /* V24 */
+ paddsw %mm0, %mm2 /* tm2 */
+ movq %mm7, (%esi) /* tm0; free mm7 */
+ psubsw %mm0, %mm3 /* tm12; free mm0 */
+ movq %mm1, 8*14(%esi) /* tm14; free mm1 */
+ psllw $1, %mm6 /* t119=t123 */
+ movq %mm2, 8*2(%esi) /* tm2; free mm2 */
+ movq %mm6, %mm0 /* duplicate t119=t123 */
+ movq %mm3, 8*12(%esi) /* tm12; free mm3 */
+ paddsw %mm4, %mm6 /* tm4 */
+/* moved from next block */
+ movq 8*5(%ecx), %mm1
+ psubsw %mm4, %mm0 /* tm10; free mm4 */
+/* moved from next block */
+ pmulhw 8*5(%esi), %mm1 /* V5 */
+ movq %mm6, 8*4(%esi) /* tm4; free mm6 */
+ movq %mm0, 8*10(%esi) /* tm10; free mm0 */
+/* column 1: even part
+ * use V5, V13, V1, V9 to produce V56..V59
+ */
+/* moved to prev block:
+ * movq 8*5(%ecx), %mm1
+ * pmulhw 8*5(%esi), %mm1 V5
+ */
+ movq 8*13(%ecx), %mm7
+ psllw $1, %mm1 /* t128=t130 */
+ pmulhw 8*13(%esi), %mm7 /* V13 */
+ movq %mm1, %mm2 /* duplicate t128=t130 */
+ movq 8(%ecx), %mm3
+ pmulhw 8(%esi), %mm3 /* V1 */
+ movq 8*9(%ecx), %mm5
+ psubsw %mm7, %mm1 /* V50 */
+ pmulhw 8*9(%esi), %mm5 /* V9 */
+ paddsw %mm7, %mm2 /* V51 */
+ pmulhw x5a825a825a825a82, %mm1 /* 23170 ->V52 */
+ movq %mm2, %mm6 /* duplicate V51 */
+ psraw $1, %mm2 /* t138=t144 */
+ movq %mm3, %mm4 /* duplicate V1 */
+ psraw $2, %mm6 /* t136 */
+ paddsw %mm5, %mm3 /* V53 */
+ psubsw %mm5, %mm4 /* V54 ;mm5 free */
+ movq %mm3, %mm7 /* duplicate V53 */
+/* moved from next block */
+ movq 8*11(%ecx), %mm0
+ psraw $1, %mm4 /* t140=t142 */
+ psubsw %mm6, %mm1 /* V55 ; mm6 free */
+ paddsw %mm2, %mm3 /* V56 */
+ movq %mm4, %mm5 /* duplicate t140=t142 */
+ paddsw %mm1, %mm4 /* V57 */
+ movq %mm3, 8*5(%esi) /* V56 */
+ psubsw %mm1, %mm5 /* V58; mm1 free */
+ movq %mm4, 8*13(%esi) /* V57 */
+ psubsw %mm2, %mm7 /* V59; mm2 free */
+ movq %mm5, 8*9(%esi) /* V58 */
+/* keep mm7 alive all along the next block
+ * movq %mm7, 8(%esi) V59
+ * moved above
+ * movq 8*11(%ecx), %mm0
+ */
+ pmulhw 8*11(%esi), %mm0 /* V11 */
+ movq 8*7(%ecx), %mm6
+ pmulhw 8*7(%esi), %mm6 /* V7 */
+ movq 8*15(%ecx), %mm4
+ movq %mm0, %mm3 /* duplicate V11 */
+ pmulhw 8*15(%esi), %mm4 /* V15 */
+ movq 8*3(%ecx), %mm5
+ psllw $1, %mm6 /* t146=t152 */
+ pmulhw 8*3(%esi), %mm5 /* V3 */
+ paddsw %mm6, %mm0 /* V63 */
+/* note that V15 computation has a correction step:
+ * this is a 'magic' constant that rebiases the results to be closer to the
+ * expected result. this magic constant can be refined to reduce the error
+ * even more by doing the correction step in a later stage when the number
+ * is actually multiplied by 16
+ */
+ paddw x0005000200010001, %mm4
+ psubsw %mm6, %mm3 /* V60 ; free mm6 */
+ psraw $1, %mm0 /* t154=t156 */
+ movq %mm3, %mm1 /* duplicate V60 */
+ pmulhw x539f539f539f539f, %mm1 /* V67 */
+ movq %mm5, %mm6 /* duplicate V3 */
+ psraw $2, %mm4 /* t148=t150 */
+ paddsw %mm4, %mm5 /* V61 */
+ psubsw %mm4, %mm6 /* V62 ; free mm4 */
+ movq %mm5, %mm4 /* duplicate V61 */
+ psllw $1, %mm1 /* t169 */
+ paddsw %mm0, %mm5 /* V65 -> result */
+ psubsw %mm0, %mm4 /* V64 ; free mm0 */
+ pmulhw x5a825a825a825a82, %mm4 /* V68 */
+ psraw $1, %mm3 /* t158 */
+ psubsw %mm6, %mm3 /* V66 */
+ movq %mm5, %mm2 /* duplicate V65 */
+ pmulhw x61f861f861f861f8, %mm3 /* V70 */
+ psllw $1, %mm6 /* t165 */
+ pmulhw x4546454645464546, %mm6 /* V69 */
+ psraw $1, %mm2 /* t172 */
+/* moved from next block */
+ movq 8*5(%esi), %mm0 /* V56 */
+ psllw $1, %mm4 /* t174 */
+/* moved from next block */
+ psraw $1, %mm0 /* t177=t188 */
+ nop
+ psubsw %mm3, %mm6 /* V72 */
+ psubsw %mm1, %mm3 /* V71 ; free mm1 */
+ psubsw %mm2, %mm6 /* V73 ; free mm2 */
+/* moved from next block */
+ psraw $1, %mm5 /* t178=t189 */
+ psubsw %mm6, %mm4 /* V74 */
+/* moved from next block */
+ movq %mm0, %mm1 /* duplicate t177=t188 */
+ paddsw %mm4, %mm3 /* V75 */
+/* moved from next block */
+ paddsw %mm5, %mm0 /* tm1 */
+/* location
+ * 5 - V56
+ * 13 - V57
+ * 9 - V58
+ * X - V59, mm7
+ * X - V65, mm5
+ * X - V73, mm6
+ * X - V74, mm4
+ * X - V75, mm3
+ * free mm0, mm1 & mm2
+ * moved above
+ * movq 8*5(%esi), %mm0 V56
+ * psllw $1, %mm0 t177=t188 ! new !!
+ * psllw $1, %mm5 t178=t189 ! new !!
+ * movq %mm0, %mm1 duplicate t177=t188
+ * paddsw %mm5, %mm0 tm1
+ */
+ movq 8*13(%esi), %mm2 /* V57 */
+ psubsw %mm5, %mm1 /* tm15; free mm5 */
+ movq %mm0, 8(%esi) /* tm1; free mm0 */
+ psraw $1, %mm7 /* t182=t184 ! new !! */
+/* save the store as used directly in the transpose
+ * movq %mm1, 120(%esi) tm15; free mm1
+ */
+ movq %mm7, %mm5 /* duplicate t182=t184 */
+ psubsw %mm3, %mm7 /* tm7 */
+ paddsw %mm3, %mm5 /* tm9; free mm3 */
+ movq 8*9(%esi), %mm0 /* V58 */
+ movq %mm2, %mm3 /* duplicate V57 */
+ movq %mm7, 8*7(%esi) /* tm7; free mm7 */
+ psubsw %mm6, %mm3 /* tm13 */
+ paddsw %mm6, %mm2 /* tm3 ; free mm6 */
+/* moved up from the transpose */
+ movq %mm3, %mm7
+/* moved up from the transpose */
+ punpcklwd %mm1, %mm3
+ movq %mm0, %mm6 /* duplicate V58 */
+ movq %mm2, 8*3(%esi) /* tm3; free mm2 */
+ paddsw %mm4, %mm0 /* tm5 */
+ psubsw %mm4, %mm6 /* tm11; free mm4 */
+/* moved up from the transpose */
+ punpckhwd %mm1, %mm7
+ movq %mm0, 8*5(%esi) /* tm5; free mm0 */
+/* moved up from the transpose */
+ movq %mm5, %mm2
+/* transpose - M4 part
+ * --------- ---------
+ * | M1 | M2 | | M1'| M3'|
+ * --------- --> ---------
+ * | M3 | M4 | | M2'| M4'|
+ * --------- ---------
+ * Two alternatives: use full mmword approach so the following code can be
+ * scheduled before the transpose is done without stores, or use the faster
+ * half mmword stores (when possible)
+ */
+ movd %mm3, 8*9+4(%esi) /* MS part of tmt9 */
+ punpcklwd %mm6, %mm5
+ movd %mm7, 8*13+4(%esi) /* MS part of tmt13 */
+ punpckhwd %mm6, %mm2
+ movd %mm5, 8*9(%esi) /* LS part of tmt9 */
+ punpckhdq %mm3, %mm5 /* free mm3 */
+ movd %mm2, 8*13(%esi) /* LS part of tmt13 */
+ punpckhdq %mm7, %mm2 /* free mm7 */
+/* moved up from the M3 transpose */
+ movq 8*8(%esi), %mm0
+/* moved up from the M3 transpose */
+ movq 8*10(%esi), %mm1
+/* moved up from the M3 transpose */
+ movq %mm0, %mm3
+/* shuffle the rest of the data, and write it with 2 mmword writes */
+ movq %mm5, 8*11(%esi) /* tmt11 */
+/* moved up from the M3 transpose */
+ punpcklwd %mm1, %mm0
+ movq %mm2, 8*15(%esi) /* tmt15 */
+/* moved up from the M3 transpose */
+ punpckhwd %mm1, %mm3
+/* transpose - M3 part
+ * moved up to previous code section
+ * movq 8*8(%esi), %mm0
+ * movq 8*10(%esi), %mm1
+ * movq %mm0, %mm3
+ * punpcklwd %mm1, %mm0
+ * punpckhwd %mm1, %mm3
+ */
+ movq 8*12(%esi), %mm6
+ movq 8*14(%esi), %mm4
+ movq %mm6, %mm2
+/* shuffle the data and write the lower parts of the transposed in 4 dwords */
+ punpcklwd %mm4, %mm6
+ movq %mm0, %mm1
+ punpckhdq %mm6, %mm1
+ movq %mm3, %mm7
+ punpckhwd %mm4, %mm2 /* free mm4 */
+ punpckldq %mm6, %mm0 /* free mm6 */
+/* moved from next block */
+ movq 8*13(%esi), %mm4 /* tmt13 */
+ punpckldq %mm2, %mm3
+ punpckhdq %mm2, %mm7 /* free mm2 */
+/* moved from next block */
+ movq %mm3, %mm5 /* duplicate tmt5 */
+/* column 1: even part (after transpose)
+* moved above
+* movq %mm3, %mm5 duplicate tmt5
+* movq 8*13(%esi), %mm4 tmt13
+*/
+ psubsw %mm4, %mm3 /* V134 */
+ pmulhw x5a825a825a825a82, %mm3 /* 23170 ->V136 */
+ movq 8*9(%esi), %mm6 /* tmt9 */
+ paddsw %mm4, %mm5 /* V135 ; mm4 free */
+ movq %mm0, %mm4 /* duplicate tmt1 */
+ paddsw %mm6, %mm0 /* V137 */
+ psubsw %mm6, %mm4 /* V138 ; mm6 free */
+ psllw $2, %mm3 /* t290 */
+ psubsw %mm5, %mm3 /* V139 */
+ movq %mm0, %mm6 /* duplicate V137 */
+ paddsw %mm5, %mm0 /* V140 */
+ movq %mm4, %mm2 /* duplicate V138 */
+ paddsw %mm3, %mm2 /* V141 */
+ psubsw %mm3, %mm4 /* V142 ; mm3 free */
+ movq %mm0, 8*9(%esi) /* V140 */
+ psubsw %mm5, %mm6 /* V143 ; mm5 free */
+/* moved from next block */
+ movq 8*11(%esi), %mm0 /* tmt11 */
+ movq %mm2, 8*13(%esi) /* V141 */
+/* moved from next block */
+ movq %mm0, %mm2 /* duplicate tmt11 */
+/* column 1: odd part (after transpose) */
+/* moved up to the prev block
+ * movq 8*11(%esi), %mm0 tmt11
+ * movq %mm0, %mm2 duplicate tmt11
+ */
+ movq 8*15(%esi), %mm5 /* tmt15 */
+ psubsw %mm7, %mm0 /* V144 */
+ movq %mm0, %mm3 /* duplicate V144 */
+ paddsw %mm7, %mm2 /* V147 ; free mm7 */
+ pmulhw x539f539f539f539f, %mm0 /* 21407-> V151 */
+ movq %mm1, %mm7 /* duplicate tmt3 */
+ paddsw %mm5, %mm7 /* V145 */
+ psubsw %mm5, %mm1 /* V146 ; free mm5 */
+ psubsw %mm1, %mm3 /* V150 */
+ movq %mm7, %mm5 /* duplicate V145 */
+ pmulhw x4546454645464546, %mm1 /* 17734-> V153 */
+ psubsw %mm2, %mm5 /* V148 */
+ pmulhw x61f861f861f861f8, %mm3 /* 25080-> V154 */
+ psllw $2, %mm0 /* t311 */
+ pmulhw x5a825a825a825a82, %mm5 /* 23170-> V152 */
+ paddsw %mm2, %mm7 /* V149 ; free mm2 */
+ psllw $1, %mm1 /* t313 */
+ nop /* without the nop - freeze here for one clock */
+ movq %mm3, %mm2 /* duplicate V154 */
+ psubsw %mm0, %mm3 /* V155 ; free mm0 */
+ psubsw %mm2, %mm1 /* V156 ; free mm2 */
+/* moved from the next block */
+ movq %mm6, %mm2 /* duplicate V143 */
+/* moved from the next block */
+ movq 8*13(%esi), %mm0 /* V141 */
+ psllw $1, %mm1 /* t315 */
+ psubsw %mm7, %mm1 /* V157 (keep V149) */
+ psllw $2, %mm5 /* t317 */
+ psubsw %mm1, %mm5 /* V158 */
+ psllw $1, %mm3 /* t319 */
+ paddsw %mm5, %mm3 /* V159 */
+/* column 1: output butterfly (after transform)
+ * moved to the prev block
+ * movq %mm6, %mm2 duplicate V143
+ * movq 8*13(%esi), %mm0 V141
+ */
+ psubsw %mm3, %mm2 /* V163 */
+ paddsw %mm3, %mm6 /* V164 ; free mm3 */
+ movq %mm4, %mm3 /* duplicate V142 */
+ psubsw %mm5, %mm4 /* V165 ; free mm5 */
+ movq %mm2, scratch7 /* out7 */
+ psraw $4, %mm6
+ psraw $4, %mm4
+ paddsw %mm5, %mm3 /* V162 */
+ movq 8*9(%esi), %mm2 /* V140 */
+ movq %mm0, %mm5 /* duplicate V141 */
+/* in order not to perculate this line up,
+ * we read 72(%esi) very near to this location
+ */
+ movq %mm6, 8*9(%esi) /* out9 */
+ paddsw %mm1, %mm0 /* V161 */
+ movq %mm3, scratch5 /* out5 */
+ psubsw %mm1, %mm5 /* V166 ; free mm1 */
+ movq %mm4, 8*11(%esi) /* out11 */
+ psraw $4, %mm5
+ movq %mm0, scratch3 /* out3 */
+ movq %mm2, %mm4 /* duplicate V140 */
+ movq %mm5, 8*13(%esi) /* out13 */
+ paddsw %mm7, %mm2 /* V160 */
+/* moved from the next block */
+ movq 8(%esi), %mm0
+ psubsw %mm7, %mm4 /* V167 ; free mm7 */
+/* moved from the next block */
+ movq 8*3(%esi), %mm7
+ psraw $4, %mm4
+ movq %mm2, scratch1 /* out1 */
+/* moved from the next block */
+ movq %mm0, %mm1
+ movq %mm4, 8*15(%esi) /* out15 */
+/* moved from the next block */
+ punpcklwd %mm7, %mm0
+/* transpose - M2 parts
+ * moved up to the prev block
+ * movq 8(%esi), %mm0
+ * movq 8*3(%esi), %mm7
+ * movq %mm0, %mm1
+ * punpcklwd %mm7, %mm0
+ */
+ movq 8*5(%esi), %mm5
+ punpckhwd %mm7, %mm1
+ movq 8*7(%esi), %mm4
+ movq %mm5, %mm3
+/* shuffle the data and write the lower parts of the trasposed in 4 dwords */
+ movd %mm0, 8*8(%esi) /* LS part of tmt8 */
+ punpcklwd %mm4, %mm5
+ movd %mm1, 8*12(%esi) /* LS part of tmt12 */
+ punpckhwd %mm4, %mm3
+ movd %mm5, 8*8+4(%esi) /* MS part of tmt8 */
+ punpckhdq %mm5, %mm0 /* tmt10 */
+ movd %mm3, 8*12+4(%esi) /* MS part of tmt12 */
+ punpckhdq %mm3, %mm1 /* tmt14 */
+/* transpose - M1 parts */
+ movq (%esi), %mm7
+ movq 8*2(%esi), %mm2
+ movq %mm7, %mm6
+ movq 8*4(%esi), %mm5
+ punpcklwd %mm2, %mm7
+ movq 8*6(%esi), %mm4
+ punpckhwd %mm2, %mm6 /* free mm2 */
+ movq %mm5, %mm3
+ punpcklwd %mm4, %mm5
+ punpckhwd %mm4, %mm3 /* free mm4 */
+ movq %mm7, %mm2
+ movq %mm6, %mm4
+ punpckldq %mm5, %mm7 /* tmt0 */
+ punpckhdq %mm5, %mm2 /* tmt2 ; free mm5 */
+/* shuffle the rest of the data, and write it with 2 mmword writes */
+ punpckldq %mm3, %mm6 /* tmt4 */
+/* moved from next block */
+ movq %mm2, %mm5 /* duplicate tmt2 */
+ punpckhdq %mm3, %mm4 /* tmt6 ; free mm3 */
+/* moved from next block */
+ movq %mm0, %mm3 /* duplicate tmt10 */
+/* column 0: odd part (after transpose)
+ *moved up to prev block
+ * movq %mm0, %mm3 duplicate tmt10
+ * movq %mm2, %mm5 duplicate tmt2
+ */
+ psubsw %mm4, %mm0 /* V110 */
+ paddsw %mm4, %mm3 /* V113 ; free mm4 */
+ movq %mm0, %mm4 /* duplicate V110 */
+ paddsw %mm1, %mm2 /* V111 */
+ pmulhw x539f539f539f539f, %mm0 /* 21407-> V117 */
+ psubsw %mm1, %mm5 /* V112 ; free mm1 */
+ psubsw %mm5, %mm4 /* V116 */
+ movq %mm2, %mm1 /* duplicate V111 */
+ pmulhw x4546454645464546, %mm5 /* 17734-> V119 */
+ psubsw %mm3, %mm2 /* V114 */
+ pmulhw x61f861f861f861f8, %mm4 /* 25080-> V120 */
+ paddsw %mm3, %mm1 /* V115 ; free mm3 */
+ pmulhw x5a825a825a825a82, %mm2 /* 23170-> V118 */
+ psllw $2, %mm0 /* t266 */
+ movq %mm1, (%esi) /* save V115 */
+ psllw $1, %mm5 /* t268 */
+ psubsw %mm4, %mm5 /* V122 */
+ psubsw %mm0, %mm4 /* V121 ; free mm0 */
+ psllw $1, %mm5 /* t270 */
+ psubsw %mm1, %mm5 /* V123 ; free mm1 */
+ psllw $2, %mm2 /* t272 */
+ psubsw %mm5, %mm2 /* V124 (keep V123) */
+ psllw $1, %mm4 /* t274 */
+ movq %mm5, 8*2(%esi) /* save V123 ; free mm5 */
+ paddsw %mm2, %mm4 /* V125 (keep V124) */
+/* column 0: even part (after transpose) */
+ movq 8*12(%esi), %mm0 /* tmt12 */
+ movq %mm6, %mm3 /* duplicate tmt4 */
+ psubsw %mm0, %mm6 /* V100 */
+ paddsw %mm0, %mm3 /* V101 ; free mm0 */
+ pmulhw x5a825a825a825a82, %mm6 /* 23170 ->V102 */
+ movq %mm7, %mm5 /* duplicate tmt0 */
+ movq 8*8(%esi), %mm1 /* tmt8 */
+ paddsw %mm1, %mm7 /* V103 */
+ psubsw %mm1, %mm5 /* V104 ; free mm1 */
+ movq %mm7, %mm0 /* duplicate V103 */
+ psllw $2, %mm6 /* t245 */
+ paddsw %mm3, %mm7 /* V106 */
+ movq %mm5, %mm1 /* duplicate V104 */
+ psubsw %mm3, %mm6 /* V105 */
+ psubsw %mm3, %mm0 /* V109; free mm3 */
+ paddsw %mm6, %mm5 /* V107 */
+ psubsw %mm6, %mm1 /* V108 ; free mm6 */
+/* column 0: output butterfly (after transform) */
+ movq %mm1, %mm3 /* duplicate V108 */
+ paddsw %mm2, %mm1 /* out4 */
+ psraw $4, %mm1
+ psubsw %mm2, %mm3 /* out10 ; free mm2 */
+ psraw $4, %mm3
+ movq %mm0, %mm6 /* duplicate V109 */
+ movq %mm1, 8*4(%esi) /* out4 ; free mm1 */
+ psubsw %mm4, %mm0 /* out6 */
+ movq %mm3, 8*10(%esi) /* out10 ; free mm3 */
+ psraw $4, %mm0
+ paddsw %mm4, %mm6 /* out8 ; free mm4 */
+ movq %mm7, %mm1 /* duplicate V106 */
+ movq %mm0, 8*6(%esi) /* out6 ; free mm0 */
+ psraw $4, %mm6
+ movq (%esi), %mm4 /* V115 */
+ movq %mm6, 8*8(%esi) /* out8 ; free mm6 */
+ movq %mm5, %mm2 /* duplicate V107 */
+ movq 8*2(%esi), %mm3 /* V123 */
+ paddsw %mm4, %mm7 /* out0 */
+/* moved up from next block */
+ movq scratch3, %mm0
+ psraw $4, %mm7
+/* moved up from next block */
+ movq scratch5, %mm6
+ psubsw %mm4, %mm1 /* out14 ; free mm4 */
+ paddsw %mm3, %mm5 /* out2 */
+ psraw $4, %mm1
+ movq %mm7, (%esi) /* out0 ; free mm7 */
+ psraw $4, %mm5
+ movq %mm1, 8*14(%esi) /* out14 ; free mm1 */
+ psubsw %mm3, %mm2 /* out12 ; free mm3 */
+ movq %mm5, 8*2(%esi) /* out2 ; free mm5 */
+ psraw $4, %mm2
+/* moved up to the prev block */
+ movq scratch7, %mm4
+/* moved up to the prev block */
+ psraw $4, %mm0
+ movq %mm2, 8*12(%esi) /* out12 ; free mm2 */
+/* moved up to the prev block */
+ psraw $4, %mm6
+/* move back the data to its correct place
+* moved up to the prev block
+ * movq scratch3, %mm0
+ * movq scratch5, %mm6
+ * movq scratch7, %mm4
+ * psraw $4, %mm0
+ * psraw $4, %mm6
+*/
+ movq scratch1, %mm1
+ psraw $4, %mm4
+ movq %mm0, 8*3(%esi) /* out3 */
+ psraw $4, %mm1
+ movq %mm6, 8*5(%esi) /* out5 */
+ movq %mm4, 8*7(%esi) /* out7 */
+ movq %mm1, 8(%esi) /* out1 */
+ popl %edi
+ popl %esi
+ popl %edx
+ popl %ecx
+ popl %ebx
+ movl %ebp,%esp
+ popl %ebp
+ ret
+.Lfe1:
+ .size IDCT_mmx,.Lfe1-IDCT_mmx
+ .section .note.GNU-stack,"",%progbits
+
+
+#endif
+
diff --git a/mpeglib/lib/mpegplay/motionVector.cpp b/mpeglib/lib/mpegplay/motionVector.cpp
new file mode 100644
index 00000000..ebec0369
--- /dev/null
+++ b/mpeglib/lib/mpegplay/motionVector.cpp
@@ -0,0 +1,130 @@
+/*
+ class for motionvectors
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "motionVector.h"
+
+
+
+
+
+MotionVector::MotionVector() {
+}
+
+
+MotionVector::~MotionVector() {
+}
+
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ComputeVector --
+ *
+ * Computes motion vector given parameters previously parsed
+ * and reconstructed.
+ *
+ * Results:
+ * Reconstructed motion vector info is put into recon_* parameters
+ * passed to this function. Also updated previous motion vector
+ * information.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+void MotionVector::computeVector(int* recon_right_ptr,
+ int* recon_down_ptr,
+ int& recon_right_prev,
+ int& recon_down_prev,
+ unsigned int& f,
+ unsigned int& full_pel_vector,
+ int& motion_h_code,
+ int& motion_v_code,
+ unsigned int& motion_h_r,
+ unsigned int& motion_v_r) {
+
+
+ int comp_h_r, comp_v_r;
+ int right_little, right_big, down_little, down_big;
+ int max, min, new_vector;
+
+ /* The following procedure for the reconstruction of motion vectors
+ is a direct and simple implementation of the instructions given
+ in the mpeg December 1991 standard draft.
+ */
+
+ if (f == 1 || motion_h_code == 0)
+ comp_h_r = 0;
+ else
+ comp_h_r = f - 1 - motion_h_r;
+
+ if (f == 1 || motion_v_code == 0)
+ comp_v_r = 0;
+ else
+ comp_v_r = f - 1 - motion_v_r;
+
+ right_little = motion_h_code * f;
+ if (right_little == 0)
+ right_big = 0;
+ else {
+ if (right_little > 0) {
+ right_little = right_little - comp_h_r;
+ right_big = right_little - 32 * f;
+ }
+ else {
+ right_little = right_little + comp_h_r;
+ right_big = right_little + 32 * f;
+ }
+ }
+
+ down_little = motion_v_code * f;
+ if (down_little == 0)
+ down_big = 0;
+ else {
+ if (down_little > 0) {
+ down_little = down_little - comp_v_r;
+ down_big = down_little - 32 * f;
+ }
+ else {
+ down_little = down_little + comp_v_r;
+ down_big = down_little + 32 * f;
+ }
+ }
+
+ max = 16 * f - 1;
+ min = -16 * f;
+
+ new_vector = recon_right_prev + right_little;
+
+ if (new_vector <= max && new_vector >= min)
+ *recon_right_ptr = recon_right_prev + right_little;
+ /* just new_vector */
+ else
+ *recon_right_ptr = recon_right_prev + right_big;
+ recon_right_prev = *recon_right_ptr;
+ if (full_pel_vector)
+ *recon_right_ptr = *recon_right_ptr << 1;
+
+ new_vector = recon_down_prev + down_little;
+ if (new_vector <= max && new_vector >= min)
+ *recon_down_ptr = recon_down_prev + down_little;
+ /* just new_vector */
+ else
+ *recon_down_ptr = recon_down_prev + down_big;
+ recon_down_prev = *recon_down_ptr;
+ if (full_pel_vector)
+ *recon_down_ptr = *recon_down_ptr << 1;
+
+}
diff --git a/mpeglib/lib/mpegplay/motionVector.h b/mpeglib/lib/mpegplay/motionVector.h
new file mode 100644
index 00000000..5a58e027
--- /dev/null
+++ b/mpeglib/lib/mpegplay/motionVector.h
@@ -0,0 +1,40 @@
+/*
+ class for motionvectors
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __MOTIONVECTOR_H
+#define __MOTIONVECTOR_H
+
+
+#include "picture.h"
+
+
+class MotionVector {
+
+
+ public:
+ MotionVector();
+ ~MotionVector();
+
+ void computeVector(int* recon_right_ptr,
+ int* recon_down_ptr,
+ int& recon_right_prev,
+ int& recon_down_prev,
+ unsigned int& f,
+ unsigned int& full_pel_vector,
+ int& motion_h_code,
+ int& motion_v_code,
+ unsigned int& motion_h_r,
+ unsigned int& motion_v_r);
+
+};
+#endif
diff --git a/mpeglib/lib/mpegplay/mpegExtension.cpp b/mpeglib/lib/mpegplay/mpegExtension.cpp
new file mode 100644
index 00000000..657fe683
--- /dev/null
+++ b/mpeglib/lib/mpegplay/mpegExtension.cpp
@@ -0,0 +1,258 @@
+/*
+ parses extension data (picture and sequence)
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "mpegExtension.h"
+
+#include <iostream>
+
+using namespace std;
+
+MpegExtension::MpegExtension() {
+ userData=NULL;
+ extData=NULL;
+ extraBit=NULL;
+}
+
+
+MpegExtension::~MpegExtension() {
+ if (userData != NULL) {
+ delete userData;
+ }
+ if (extData != NULL) {
+ delete extData;
+ }
+ if (extraBit != NULL) {
+ delete extraBit;
+ }
+}
+
+
+int MpegExtension::processExtensionData(MpegVideoStream* mpegVideoStream) {
+
+ /* Goto next start code. */
+ mpegVideoStream->next_start_code();
+
+ /*
+ * If next start code is extension/user start code,
+ * parse off extension data.
+ */
+
+ /*
+ * If next start code is extension start code, parse off extension data.
+ */
+ if (next_bits(32, EXT_START_CODE,mpegVideoStream)) {
+ mpegVideoStream->flushBits(32);
+ if (extData != NULL) {
+ delete extData;
+ extData = NULL;
+ }
+ cout << "ext"<<endl;
+ extData = get_ext_data(mpegVideoStream);
+ }
+
+ /*
+ * If next start code is user start code, parse off user data.
+ */
+ if (next_bits(32, USER_START_CODE,mpegVideoStream)) {
+ mpegVideoStream->flushBits(32);
+ if (userData != NULL) {
+ delete userData;
+ userData=NULL;
+ }
+ userData =get_ext_data(mpegVideoStream);
+ }
+
+ return true;
+}
+
+
+
+int MpegExtension::processExtra_bit_info(MpegVideoStream* mpegVideoStream) {
+ if (extraBit != NULL) {
+ delete extraBit;
+ extraBit = NULL;
+ }
+ get_extra_bit_info(mpegVideoStream);
+ return true;
+}
+
+
+
+
+int MpegExtension::next_bits(int num,unsigned int mask,
+ MpegVideoStream* input) {
+ unsigned int data;
+
+ /* Get next num bits, no buffer pointer advance. */
+
+ data=input->showBits(num);
+
+ /* Compare bit stream and mask. Set return value toTRUE if equal, FALSE if
+ differs.
+ */
+
+ if (mask == data) {
+ return true;
+ }
+ return false;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * get_ext_data --
+ *
+ * Assumes that bit stream is at begining of extension
+ * data. Parses off extension data into dynamically
+ * allocated space until start code is hit.
+ *
+ * Results:
+ * Pointer to dynamically allocated memory containing
+ * extension data.
+ *
+ * Side effects:
+ * Bit stream irreversibly parsed.
+ *
+ *--------------------------------------------------------------
+ */
+char* MpegExtension::get_ext_data(MpegVideoStream* mpegVideoStream) {
+ unsigned int size, marker;
+ char *dataPtr;
+ unsigned int data;
+
+ /* Set initial ext data buffer size. */
+
+ size = EXT_BUF_SIZE;
+
+ /* Allocate ext data buffer. */
+
+ dataPtr = (char *) malloc(size);
+
+ /* Initialize marker to keep place in ext data buffer. */
+
+ marker = 0;
+
+ /* While next data is not start code... */
+ while (!next_bits(24, 0x000001,mpegVideoStream )) {
+
+ /* Get next byte of ext data. */
+
+ data=mpegVideoStream->getBits(8);
+
+
+
+ /* Put ext data into ext data buffer. Advance marker. */
+
+ dataPtr[marker] = (char) data;
+ marker++;
+
+ /* If end of ext data buffer reached, resize data buffer. */
+
+ if (marker == size) {
+ size += EXT_BUF_SIZE;
+ dataPtr = (char *) realloc(dataPtr, size);
+ }
+ }
+
+ /* Realloc data buffer to free any extra space. */
+
+ dataPtr = (char *) realloc(dataPtr, marker);
+ delete dataPtr;
+ dataPtr=NULL;
+ /* Return pointer to ext data buffer. */
+ return dataPtr;
+}
+
+void MpegExtension::processExtBuffer(MpegVideoStream* mpegVideoStream){
+ unsigned int size, marker;
+ char *dataPtr;
+ unsigned int data=1;
+
+ /* Initialize size of extra bit info buffer and allocate. */
+
+
+ size = EXT_BUF_SIZE;
+ dataPtr = (char *) malloc(size);
+
+ /* Reset marker to hold place in buffer. */
+
+ marker = 0;
+
+ /* While flag bit is true. */
+
+ while (data) {
+
+ /* Get next 8 bits of data. */
+ data=mpegVideoStream->getBits(8);
+
+ /* Place in extra bit info buffer. */
+
+ dataPtr[marker] = (char) data;
+ marker++;
+
+ /* If buffer is full, reallocate. */
+
+ if (marker == size) {
+ size += EXT_BUF_SIZE;
+ dataPtr = (char *) realloc(dataPtr, size);
+ }
+
+ /* Get next flag bit. */
+ data=mpegVideoStream->getBits(1);
+ }
+
+ /* Reallocate buffer to free extra space. */
+
+ dataPtr = (char *) realloc(dataPtr, marker);
+ delete dataPtr;
+ dataPtr=NULL;
+ /* Return pointer to extra bit info buffer. */
+}
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * get_extra_bit_info --
+ *
+ * Parses off extra bit info stream into dynamically
+ * allocated memory. Extra bit info is indicated by
+ * a flag bit set to 1, followed by 8 bits of data.
+ * This continues until the flag bit is zero. Assumes
+ * that bit stream set to first flag bit in extra
+ * bit info stream.
+ *
+ * Results:
+ * Pointer to dynamically allocated memory with extra
+ * bit info in it. Flag bits are NOT included.
+ *
+ * Side effects:
+ * Bit stream irreversibly parsed.
+ *
+ *--------------------------------------------------------------
+ */
+
+char* MpegExtension::get_extra_bit_info(MpegVideoStream* mpegVideoStream) {
+ unsigned int data;
+
+ /* Get first flag bit. */
+ data=mpegVideoStream->getBits(1);
+
+ /* If flag is false, return NULL pointer (i.e. no extra bit info). */
+
+ if (!data) {
+ return NULL;
+ }
+ processExtBuffer(mpegVideoStream);
+ return NULL;
+}
diff --git a/mpeglib/lib/mpegplay/mpegExtension.h b/mpeglib/lib/mpegplay/mpegExtension.h
new file mode 100644
index 00000000..23b79c5b
--- /dev/null
+++ b/mpeglib/lib/mpegplay/mpegExtension.h
@@ -0,0 +1,49 @@
+/*
+ parses extension data (picture and sequence)
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+
+#ifndef __MPEGEXTENSION_H
+#define __MPEGEXTENSION_H
+
+#include "mpegVideoStream.h"
+
+#define EXT_BUF_SIZE 1024
+
+class MpegExtension {
+
+ char* userData;
+ char* extData;
+ char* extraBit;
+
+ public:
+ MpegExtension();
+ ~MpegExtension();
+
+ inline char* getExtData() { return extData;}
+ inline char* getUserData() { return userData;}
+ inline char* getExtraBit() { return extraBit;}
+
+ int processExtensionData(class MpegVideoStream* mpegVideoStream);
+ int processExtra_bit_info(class MpegVideoStream* mpegVideoStream);
+ void processExtBuffer(class MpegVideoStream* mpegVideoStream);
+ char* get_ext_data(class MpegVideoStream* mpegVideoStream);
+
+ private:
+ char* get_extra_bit_info(MpegVideoStream* mpegVideoStream);
+
+ int next_bits(int num,unsigned int mask,MpegVideoStream* input);
+
+};
+#endif
diff --git a/mpeglib/lib/mpegplay/mpegSystemHeader.cpp b/mpeglib/lib/mpegplay/mpegSystemHeader.cpp
new file mode 100644
index 00000000..e8fe1d91
--- /dev/null
+++ b/mpeglib/lib/mpegplay/mpegSystemHeader.cpp
@@ -0,0 +1,786 @@
+/*
+ stores info about system stream and sends data to audio/video
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "mpegSystemHeader.h"
+
+#include <iostream>
+
+using namespace std;
+
+// we do a fast mod based lookup pid->Map
+#define MAX_PIDS 23
+
+
+MpegSystemHeader::MpegSystemHeader() {
+
+ packetLen=0;
+ pesPacketLen=0;
+ tsPacketLen=0;
+
+
+ audioLayerSelect=0;
+ videoLayerSelect=0;
+ lmpeg2=false;
+ lPTSFlag=false;
+
+ layer=_PACKET_UNKNOWN_LAYER;
+ lHasPSHeader=false;
+
+ //
+ // MPEG2 Stuff [START]
+ //
+
+ lOriginal=false;
+ lCopyRight=false;
+ lDataAlignmentIndicator=false;
+ lPesPriority=false;
+ lEncrypted=false;
+ startCodePrefix=0;
+
+ lPTSDTSFlag=false;
+ lESCR=false;
+ lESRateFlag=false;
+ lDMSTRICKFLAG=false;
+ lADDITIONAL_COPY_FLAG=false;
+ lPES_CRC_FLAG=false;
+ lPES_EXT_FLAG=false;
+ nPES_HEADER_DATA_LENGTH=0;
+
+ // EXT FILED [START]
+
+ lPrivateDataFlag=false;
+ lPackHeaderFieldFlag=false;
+ lSequenceCounterFlag=false;
+ lSTDBufferFlag=false;
+ lPES_EXT_FLAG_2=false;
+ nPES_EXT_FIELD_LENGTH=0;
+ // EXT FILED [END]
+
+ subStreamID=0;
+ //
+ // MPEG2 Stuff [END]
+ //
+
+ // TS Stuff [START]
+ lHasTSHeader=false;
+ programs=0;
+ programNumber=INVALID_PROGRAM;
+ pmtPID=INVALID_PID;
+
+ mapPidStreamArray=new MapPidStream*[MAX_PIDS];
+ int i;
+ for(i=0;i<MAX_PIDS;i++) {
+ mapPidStreamArray[i]=new MapPidStream();
+ mapPidStreamArray[i]->isValid=false;
+ }
+ currentPos=0;
+
+
+
+ // TS Stuff [END]
+}
+
+
+MpegSystemHeader::~MpegSystemHeader() {
+ int i;
+ for(i=0;i<MAX_PIDS;i++) {
+ delete mapPidStreamArray[i];
+ }
+ delete mapPidStreamArray;
+}
+
+
+void MpegSystemHeader::setHeader(unsigned int header) {
+ setPSHeader(header);
+ setTSHeader(header);
+ this->header=header;
+}
+
+int MpegSystemHeader::hasRAWHeader() {
+ return header==_SEQ_START_CODE;
+}
+
+
+unsigned int MpegSystemHeader::getHeader() {
+ return header;
+}
+
+double MpegSystemHeader::getSCRTimeStamp() {
+ return scrTimeStamp;
+}
+
+
+double MpegSystemHeader::getPTSTimeStamp() {
+ return ptsTimeStamp;
+}
+
+
+int MpegSystemHeader::getPTSFlag() {
+ return lPTSFlag;
+}
+
+
+void MpegSystemHeader::setPTSFlag(int lPTSFlag) {
+ this->lPTSFlag=lPTSFlag;
+}
+
+
+double MpegSystemHeader::getDTSTimeStamp() {
+ return dtsTimeStamp;
+}
+
+
+void MpegSystemHeader::setSCRTimeStamp(double scrTimeStamp) {
+ this->scrTimeStamp=scrTimeStamp;
+}
+
+
+void MpegSystemHeader::setPTSTimeStamp(double ptsTimeStamp) {
+ this->ptsTimeStamp=ptsTimeStamp;
+}
+
+
+void MpegSystemHeader::setDTSTimeStamp(double dtsTimeStamp) {
+ this->dtsTimeStamp=dtsTimeStamp;
+}
+
+
+
+int MpegSystemHeader::getLayer() {
+ return layer;
+}
+
+
+void MpegSystemHeader::setLayer(int layer) {
+ this->layer=layer;
+}
+
+int MpegSystemHeader::hasPSHeader() {
+ return lHasPSHeader;
+}
+
+
+void MpegSystemHeader::addAvailableLayer(int streamID) {
+ switch (streamID>>4){
+ case _PAKET_ID_AUDIO_1>>4:
+ case _PAKET_ID_AUDIO_2>>4:
+ availableAudioLayers |= 1<<(streamID - 0xc0);
+ break;
+ case _PAKET_ID_VIDEO>>4:
+ availableVideoLayers |= 1<<(streamID - 0xe0);
+ break;
+ case _SUBSTREAM_AC3_ID>>4:
+ availableAudioLayers |= 1<<(streamID - 0x80);
+ break;
+ default:
+ cout << "unknown streamID MpegSystemHeader::addAvailableLayer"<<endl;
+ }
+
+
+}
+void MpegSystemHeader::resetAvailableLayers() {
+ availableAudioLayers = 0;
+ availableVideoLayers = 0;
+}
+
+int MpegSystemHeader::getAudioLayerSelect() {
+ if (availableAudioLayers & (1<<audioLayerSelect))
+ return audioLayerSelect;
+ else
+ return 0;
+}
+
+void MpegSystemHeader::setAudioLayerSelect(int layer) {
+ audioLayerSelect = layer;
+}
+
+int MpegSystemHeader::getVideoLayerSelect() {
+ if (availableVideoLayers & (1<<videoLayerSelect))
+ return videoLayerSelect;
+ else
+ return 0;
+}
+
+void MpegSystemHeader::setVideoLayerSelect(int layer) {
+ videoLayerSelect = layer;
+}
+
+
+
+
+
+int MpegSystemHeader::getPacketID() {
+ return packetID;
+}
+
+
+void MpegSystemHeader::setPacketID(int packetID) {
+ this->packetID=packetID;
+}
+
+int MpegSystemHeader::getPacketLen() {
+ return packetLen;
+}
+
+
+void MpegSystemHeader::setPacketLen(int packetLen) {
+ this->packetLen=packetLen;
+}
+
+
+int MpegSystemHeader::getPESPacketLen() {
+ return pesPacketLen;
+}
+
+void MpegSystemHeader::setPESPacketLen(int packetLen) {
+ this->pesPacketLen=packetLen;
+}
+
+
+int MpegSystemHeader::getTSPacketLen() {
+ return tsPacketLen;
+}
+
+
+void MpegSystemHeader::setTSPacketLen(int packetLen) {
+ this->tsPacketLen=packetLen;
+}
+
+
+
+
+int MpegSystemHeader::getRate() {
+ return rate;
+}
+
+
+void MpegSystemHeader::setRate(int rate) {
+ this->rate=rate;
+}
+
+
+int MpegSystemHeader::getStdBufferScale() {
+ return stdBufferScale;
+}
+
+
+void MpegSystemHeader::setStdBufferScale(int stdBufferScale) {
+ this->stdBufferScale=stdBufferScale;
+}
+
+unsigned long MpegSystemHeader::getStdBufferSize() {
+ return stdBufferSize;
+}
+
+
+void MpegSystemHeader::setStdBufferSize(unsigned long stdBufferSize) {
+ this->stdBufferSize=stdBufferSize;
+}
+
+
+int MpegSystemHeader::getMPEG2() {
+ return lmpeg2;
+}
+
+
+//
+// MPEG2 Stuff [START]
+//
+
+
+int MpegSystemHeader::getOriginalOrCopy() {
+ return lOriginal;
+}
+
+
+void MpegSystemHeader::setOriginalOrCopy(int lOriginal) {
+ this->lOriginal=lOriginal;
+}
+
+
+int MpegSystemHeader::getCopyRight() {
+ return lCopyRight;
+}
+
+
+void MpegSystemHeader::setCopyRight(int lCopyRight) {
+ this->lCopyRight=lCopyRight;
+}
+
+
+
+int MpegSystemHeader::getDataAlignmentIndicator() {
+ return lDataAlignmentIndicator;
+}
+
+
+void MpegSystemHeader::setDataAlignmentIndicator(int lDataAlignmentIndicator) {
+ this->lDataAlignmentIndicator=lDataAlignmentIndicator;
+}
+
+
+int MpegSystemHeader::getPesPriority() {
+ return lPesPriority;
+}
+
+
+void MpegSystemHeader::setPesPriority(int lPesPriority) {
+ this->lPesPriority=lPesPriority;
+}
+
+
+
+int MpegSystemHeader::getEncrypted() {
+ return lEncrypted;
+}
+
+
+void MpegSystemHeader::setEncrypted(int lEncrypted) {
+ this->lEncrypted=lEncrypted;
+}
+
+
+int MpegSystemHeader::getStartCodePrefix() {
+ return startCodePrefix;
+}
+
+
+void MpegSystemHeader::setStartCodePrefix(int startCodePrefix) {
+ this->startCodePrefix=startCodePrefix;
+}
+
+
+int MpegSystemHeader::getPTSDTSFlag(){
+ return lPTSDTSFlag;
+}
+
+
+void MpegSystemHeader::setPTSDTSFlag(int lPTSDTSFlag){
+ this->lPTSDTSFlag=lPTSDTSFlag;
+}
+
+
+int MpegSystemHeader::getESCRFlag() {
+ return lESCR;
+}
+
+
+void MpegSystemHeader::setESCRFlag(int lESCR) {
+ this->lESCR=lESCR;
+}
+
+
+int MpegSystemHeader::getES_RATE_Flag() {
+ return lESRateFlag;
+}
+
+
+void MpegSystemHeader::setES_RATE_Flag(int lESRateFlag) {
+ this->lESRateFlag=lESRateFlag;
+}
+
+
+
+int MpegSystemHeader::getDMSTRICKFLAG(){
+ return lDMSTRICKFLAG;
+}
+
+
+void MpegSystemHeader::setDMSTRICKFLAG(int lDMSTRICKFLAG) {
+ this->lDMSTRICKFLAG=lDMSTRICKFLAG;
+}
+
+
+int MpegSystemHeader::getADDITIONAL_COPY_FLAG() {
+ return lADDITIONAL_COPY_FLAG;
+}
+
+
+void MpegSystemHeader::setADDITIONAL_COPY_FLAG(int lADDITIONAL_COPY_FLAG) {
+ this->lADDITIONAL_COPY_FLAG=lADDITIONAL_COPY_FLAG;
+}
+
+
+
+int MpegSystemHeader::getPES_CRC_FLAG() {
+ return lPES_CRC_FLAG;
+}
+
+
+void MpegSystemHeader::setPES_CRC_FLAG(int lPES_CRC_FLAG) {
+ this->lPES_CRC_FLAG=lPES_CRC_FLAG;
+}
+
+
+int MpegSystemHeader::getPES_EXT_FLAG() {
+ return lPES_EXT_FLAG;
+}
+
+
+void MpegSystemHeader::setPES_EXT_FLAG(int lPES_EXT_FLAG) {
+ this->lPES_EXT_FLAG=lPES_EXT_FLAG;
+}
+
+
+ //
+ // PES EXTENSION [START]
+ //
+
+int MpegSystemHeader::getPrivateDataFlag() {
+ return lPrivateDataFlag;
+}
+
+
+void MpegSystemHeader::setPrivateDataFlag(int lPrivateDataFlag) {
+ this->lPrivateDataFlag=lPrivateDataFlag;
+}
+
+
+int MpegSystemHeader::getPackHeaderFieldFlag() {
+ return lPackHeaderFieldFlag;
+}
+
+
+void MpegSystemHeader::setPackHeaderFieldFlag(int lPackHeaderFieldFlag) {
+ this->lPackHeaderFieldFlag=lPackHeaderFieldFlag;
+}
+
+
+int MpegSystemHeader::getSequenceCounterFlag() {
+ return lSequenceCounterFlag;
+}
+
+
+void MpegSystemHeader::setSequenceCounterFlag(int lSequenceCounterFlag) {
+ this->lSequenceCounterFlag=lSequenceCounterFlag;
+}
+
+
+
+int MpegSystemHeader::getSTDBufferFlag() {
+ return lSTDBufferFlag;
+}
+
+
+void MpegSystemHeader::setSTDBufferFlag(int lSTDBufferFlag) {
+ this->lSTDBufferFlag=lSTDBufferFlag;
+}
+
+
+
+int MpegSystemHeader::getPES_EXT_FLAG_2() {
+ return lPES_EXT_FLAG_2;
+}
+
+
+void MpegSystemHeader::setPES_EXT_FLAG_2(int lPES_EXT_FLAG_2) {
+ this->lPES_EXT_FLAG_2=lPES_EXT_FLAG_2;
+}
+
+
+int MpegSystemHeader::getPES_EXT_FIELD_LENGTH() {
+ return nPES_EXT_FIELD_LENGTH;
+}
+
+
+void MpegSystemHeader::setPES_EXT_FIELD_LENGTH(int nPES_EXT_FIELD_LENGTH) {
+ this->nPES_EXT_FIELD_LENGTH=nPES_EXT_FIELD_LENGTH;
+}
+
+
+
+ //
+ // PES EXTENSION [END]
+ //
+
+
+
+int MpegSystemHeader::getPES_HEADER_DATA_LENGTH() {
+ return nPES_HEADER_DATA_LENGTH;
+}
+
+
+void MpegSystemHeader::setPES_HEADER_DATA_LENGTH(int nPES_HEADER_DATA_LENGTH){
+ this->nPES_HEADER_DATA_LENGTH=nPES_HEADER_DATA_LENGTH;
+}
+
+
+int MpegSystemHeader::getSubStreamID() {
+ return subStreamID;
+}
+
+
+void MpegSystemHeader::setSubStreamID(int subStreamID) {
+ this->subStreamID=subStreamID;
+}
+
+//
+// MPEG2 Stuff [END]
+//
+
+
+//
+// Transport Stream Header [START]
+//
+
+int MpegSystemHeader:: hasTSHeader() {
+ return lHasTSHeader;
+}
+
+void MpegSystemHeader::setTSHeader(unsigned int header) {
+
+ lHasTSHeader=false;
+
+ int byte3=header & 0xff;
+ header>>=8;
+ int byte2=header & 0xff;
+ header>>=8;
+ int byte1=header & 0xff;
+ header>>=8;
+ int byte0=header & 0xff;
+
+
+ sync_byte=byte0;
+ header=header >> 8;
+ transport_error_indicator = (byte1 >> 7) & 0x01;
+ payload_unit_start_indicator = (byte1 >> 6) & 0x01;
+ transport_priority = (byte1 >> 5) & 0x01;
+ pid = ((byte1 << 8) | byte2) & 0x1fff;
+ transport_scrambling_control = (byte3 >> 6) & 0x03;
+ adaption_field_control = (byte3 >> 4) & 0x03;
+ continuity_counter = byte3 & 0x0f;
+
+ if (sync_byte != 0x47) {
+ return ;
+ }
+ if (transport_error_indicator) {
+ return ;
+ }
+
+
+ lHasTSHeader=true;
+
+}
+
+unsigned int MpegSystemHeader::getSync_byte() {
+ return sync_byte;
+}
+
+
+unsigned int MpegSystemHeader::getTransport_error_indicator() {
+ return transport_error_indicator;
+}
+
+
+unsigned int MpegSystemHeader::getPayload_unit_start_indicator() {
+ return payload_unit_start_indicator;
+}
+
+
+unsigned int MpegSystemHeader::getTransport_priority() {
+ return transport_priority;
+}
+
+
+unsigned int MpegSystemHeader::getPid() {
+ return pid;
+}
+
+
+unsigned int MpegSystemHeader::getTransport_scrambling_control() {
+ return transport_scrambling_control;
+}
+
+
+unsigned int MpegSystemHeader::getAdaption_field_control() {
+ return adaption_field_control;
+}
+
+
+unsigned int MpegSystemHeader::getContinuity_counter() {
+ return continuity_counter;
+}
+
+unsigned int MpegSystemHeader::getPrograms() {
+ return programs;
+}
+
+
+void MpegSystemHeader::setPrograms(unsigned int programs) {
+ this->programs=programs;
+}
+
+
+unsigned int MpegSystemHeader::getProgramNumber() {
+ return programNumber;
+}
+
+
+void MpegSystemHeader::setProgramNumber(unsigned int programNumber) {
+ this->programNumber=programNumber;
+}
+
+
+unsigned int MpegSystemHeader::getPMTPID() {
+ return pmtPID;
+}
+
+
+void MpegSystemHeader::setPMTPID(unsigned int pmtPID) {
+ this->pmtPID=pmtPID;
+}
+
+
+MapPidStream* MpegSystemHeader::lookup(unsigned int pid) {
+ return mapPidStreamArray[pid % MAX_PIDS];
+}
+
+
+/*
+ Here we do a mapping from the pid to the "meaning" for the
+ pid (defined in the TS spec) and then we map this to
+ a normal mpeg stream ID.
+ (Which is used in the MpegSystemHeder to identify the
+ kind of packet we have)
+*/
+void MpegSystemHeader::insert(unsigned int pid,unsigned int tsType,
+ MpegSystemHeader* mpegHeader) {
+ if (currentPos >= MAX_PIDS) {
+ cout << "error to much pids in stream.TSSystemStream::insert"<<endl;
+ return;
+ }
+ printf("tsType:%x\n",tsType);
+ switch(tsType) {
+ case ISO_11172_VIDEO:
+ case ISO_13818_VIDEO:
+ case ISO_11172_AUDIO:
+ case ISO_13818_AUDIO:
+ case ISO_13818_PRIVATE:
+ case ISO_13818_PES_PRIVATE:
+ case ISO_13522_MHEG:
+ case ISO_13818_DSMCC:
+ case ISO_13818_TYPE_A:
+ case ISO_13818_TYPE_B:
+ case ISO_13818_TYPE_C:
+ case ISO_13818_TYPE_D:
+ case ISO_13818_TYPE_E:
+ case ISO_13818_AUX:
+ break;
+ default:
+ cout << "ignoring unknown tsType in TSSystemStream::insert"<<endl;
+ return;
+ }
+
+ MapPidStream* mapPidStream=lookup(pid);
+ int psType=_PAKET_ID_NUKE;
+
+ mapPidStream->pid=pid;
+ mapPidStream->tsType=tsType;
+ mapPidStream->psType=psType;
+ mapPidStream->isValid=true;
+
+ currentPos++;
+}
+
+
+void MpegSystemHeader::printTSHeader() {
+ if (hasTSHeader() == false) {
+ cout << "MpegSystemHeader::printTSHeader: NO TS HEADER"<<endl;
+ return;
+ }
+
+ printf("sync:%02X TE:%02X PUS:%02X TP:%02X PID:%04X TSC:%02X "
+ "AFC:%02X CC:%02X\n",
+ sync_byte,
+ transport_error_indicator,
+ payload_unit_start_indicator,
+ transport_priority,
+ pid,
+ transport_scrambling_control,
+ adaption_field_control,
+ continuity_counter);
+
+}
+
+
+void MpegSystemHeader::printProgramInfo() {
+ if (programs == 0) {
+ cout << "MpegSystemHeader::printProgramInfo: NO programs"<<endl;
+ } else {
+ cout << "MpegSystemHeader::printProgramInfo: programs:"<<programs<<endl;
+ }
+ printf("MPTS: programNumber=%x pmtPid=%x\n",programNumber, pmtPID);
+
+}
+
+void MpegSystemHeader::printMap(MapPidStream* mapPidStream) {
+ if (mapPidStream->isValid == false) {
+ cout << "TSSystemStream::printMap ** INVALID** "<<endl;
+ return;
+ }
+
+ printf("printMap->isValid:%x\n",mapPidStream->isValid);
+ printf("printMap->pid:%x\n",mapPidStream->pid);
+ printf("printMap->tsType:%x\n",mapPidStream->tsType);
+ printf("printMap->psType:%x\n",mapPidStream->psType);
+
+}
+
+
+//
+// Transport Stream Header [END]
+//
+
+
+
+void MpegSystemHeader::setMPEG2(int lmpeg2) {
+ this->lmpeg2=lmpeg2;
+}
+
+
+void MpegSystemHeader::print() {
+ cout << "MpegSystemHeader [START]"<<endl;
+ cout << "layer:"<<getLayer()<<endl;
+ cout << "MpegSystemHeader [END]"<<endl;
+
+}
+
+
+void MpegSystemHeader::setPSHeader(unsigned int header) {
+ lHasPSHeader=false;
+ if (header == _PACK_START_CODE) {
+ lHasPSHeader=true;
+ }
+ if (header == _SYSTEM_HEADER_START_CODE) {
+ lHasPSHeader=true;
+ }
+}
+
+int MpegSystemHeader::isSystemHeader(unsigned int header) {
+
+ if (header == _PACK_START_CODE) return true;
+ if (header == _SYSTEM_HEADER_START_CODE) return true;
+
+ int byte3=header & 0xff;
+ header>>=8;
+ int byte2=header & 0xff;
+ header>>=8;
+ int byte1=header & 0xff;
+ header>>=8;
+ int byte0=header & 0xff;
+
+ if (byte0 != 0x47) return false;
+ if ((byte1 >> 7) & 0x01) return false;
+
+ return true;
+}
diff --git a/mpeglib/lib/mpegplay/mpegSystemHeader.h b/mpeglib/lib/mpegplay/mpegSystemHeader.h
new file mode 100644
index 00000000..abad3794
--- /dev/null
+++ b/mpeglib/lib/mpegplay/mpegSystemHeader.h
@@ -0,0 +1,484 @@
+/*
+ stores info about system stream and sends data to audio/video
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __MPEGSYSTEMHEADER_H
+#define __MPEGSYSTEMHEADER_H
+
+
+
+#define _PACKET_SYSLAYER 1
+#define _PACKET_NO_SYSLAYER 0
+#define _PACKET_UNKNOWN_LAYER -1
+#define _PACKET_HEADER_SIZE 8
+
+
+// note: packetid 1 & 2 are normalized to _PAKET_ID_AUDIO
+// see packet.cpp
+
+#define _PAKET_ID_AUDIO_1 ((unsigned char) 0xc0)
+#define _PAKET_ID_AUDIO_2 ((unsigned char) 0xd0)
+#define _PAKET_ID_VIDEO ((unsigned char) 0xe0)
+#define _PAKET_ID_NUKE ((unsigned char) 0x0)
+
+#define _STD_SYSTEM_CLOCK_FREQ (unsigned long)90000
+#define _MUX_RATE_SCALE_FACTOR 50
+#define _MAX_STREAMS 8
+#define _NOT_PACKET_ID ((unsigned char) 0xff)
+#define _KILL_BUFFER ((unsigned char) 0xfe)
+
+
+
+#define _STD_AUDIO_STREAM_ID ((unsigned char) 0xb8)
+#define _STD_VIDEO_STREAM_ID ((unsigned char) 0xb9)
+#define _MIN_STREAM_ID_ID ((unsigned char) 0xbc)
+#define _RESERVED_STREAM_ID ((unsigned char) 0xbc)
+#define _PRIVATE_STREAM_1_ID ((unsigned char) 0xbd)
+#define _PADDING_STREAM_ID ((unsigned char) 0xbe)
+#define _PRIVATE_STREAM_2_ID ((unsigned char) 0xbf)
+#define _ECM_STREAM_ID ((unsigned char) 0xf0)
+#define _EMM_STREAM_ID ((unsigned char) 0xf1)
+#define _PROGRAM_STREAM_DIRECTORY_ID ((unsigned char) 0xff)
+#define _DSMCC_STREAM_ID ((unsigned char) 0xf2)
+#define _ITUTRECH222TYPEE_STREAM_ID ((unsigned char) 0xf8)
+#define _SUBSTREAM_AC3_ID ((unsigned char) 0x80)
+#define _SUBSTREAM_LPCM_ID ((unsigned char) 0xA0)
+#define _SUBSTREAM_SUBPIC_ID ((unsigned char) 0x20)
+
+
+/* Silly Constants.... */
+#define _PACK_START_CODE ((unsigned int)0x000001ba)
+#define _SYSTEM_HEADER_START_CODE ((unsigned int)0x000001bb)
+#define _PACKET_START_CODE_MASK ((unsigned int)0xffffff00)
+#define _PACKET_START_CODE_PREFIX ((unsigned int)0x00000100)
+#define _ISO_11172_END_CODE ((unsigned int)0x000001b9)
+
+#define _SEQ_START_CODE 0x000001b3
+
+
+// More constants for TS streams
+#define NULL_PID 8191
+#define INVALID_PID ((unsigned int)(-1))
+#define INVALID_PROGRAM ((unsigned int)(-1))
+#define INVALID_CC ((unsigned int)(-1))
+
+#define ISO_11172_VIDEO 1
+#define ISO_13818_VIDEO 2
+#define ISO_11172_AUDIO 3
+#define ISO_13818_AUDIO 4
+#define ISO_13818_PRIVATE 5
+#define ISO_13818_PES_PRIVATE 6
+#define ISO_13522_MHEG 7
+#define ISO_13818_DSMCC 8
+#define ISO_13818_TYPE_A 9
+#define ISO_13818_TYPE_B 10
+#define ISO_13818_TYPE_C 11
+#define ISO_13818_TYPE_D 12
+#define ISO_13818_TYPE_E 13
+#define ISO_13818_AUX 14
+
+
+struct MapPidStream_s {
+ // if this structure "is Valid" (association pid->tsType is ok)
+ int isValid;
+ // a not nearer defined int
+ unsigned int pid;
+ // the type for the pid defined in Transportstream
+ unsigned int tsType;
+ // runtime associated "fake" type for PS stream
+ unsigned int psType;
+ // outstanding bytes for PES packet
+ unsigned int pesPacketSize;
+};
+
+typedef MapPidStream_s MapPidStream;
+
+
+
+
+
+// More useful things
+
+#define FLOAT_0x10000 (double)((unsigned long)1 << 16)
+
+
+// MPEG2 PACK HEADER
+// SCRbase and SCRext
+// 32 .......................................0 9.........0
+// [PACK_START_CODE][01---1--][--------][-----1--][--------][-----1--][-------1
+
+#define ui64 unsigned long
+#define ui32 unsigned int
+#define i64 long
+#define i32 int
+
+#define MPEG1_CLK_REF (i64)90000
+#define MPEG2_CLK_REF (i64)27000000
+
+#define ZERO (i64)0
+#define GET_SCRBASE(buf) ( ((ui64)buf[4]&0xF8)>>3 | \
+ (ui64)buf[3]<<5 | \
+ ((ui64)buf[2]&0x03)<<13 | \
+ ((ui64)buf[2]&0xF8)<<12 | \
+ (ui64)buf[1]<<20 | \
+ ((ui64)buf[0]&0x03)<<28 | \
+ (ui64)buf[0]&0x38 <<27 )
+
+#define GET_SCREXT(buf) ( ((ui64)buf[5]&0xFE)>>1 | \
+ ((ui64)buf[4]&0x03)<<7 )
+
+// muxrate
+// 22 ......................0 stl
+// ... [--------][--------][------11][rrrrr---]
+#define GET_MPEG2MUXRATE(buf) ( (ui32)buf[6]<<14 | \
+ ((ui32)buf[7])<<6 | \
+ ((ui32)buf[8]&0x03)>>2) \
+
+#define GET_MPEG2STUFFING(buf) ((buf[9]&0x07))
+
+// MPEG1 PACK HEADER
+// SCR muxrate
+// 32........................................0 22......
+//[PACK_START_CODE][0010---1][--------][-------1][--------][-------1][1-------]
+#define GET_SCR(buf) ( ((ui64)buf[4]&0xFE) >>1 | \
+ ((ui64)buf[3]) <<7 | \
+ ((ui64)buf[2]&0xFE) <<14 | \
+ ((ui64)buf[1]) <<22 | \
+ ((ui64)buf[0]&0x0E) <<29 )
+
+#define GET_MPEG1_PTS(x) (GET_SCR(x)) //they follow the same pattern
+
+#define GET_MPEG1_MUXRATE(x) ( ((ui32)x[7]&0xFE) >>1 | \
+ ((ui32)x[6]) <<7 | \
+ ((ui32)x[5]&0x7F) <<15 )
+
+#define GET_MPEG2_PTS_FLAGS(buf) ( ((ui8)buf[3]&0xC0)>>6 )
+// MPEG2 PES packet (optional parameters)
+// PTS
+// 32........................................0
+// [PACKET_START_CODE][001x---1][--------][-------1][--------][-------1]
+#define GET_MPEG2_PTS(buf) GET_MPEG1_PTS(buf)
+
+
+
+#include <stdio.h>
+
+
+class MpegSystemHeader {
+
+ int lPTSFlag;
+ double scrTimeStamp;
+ double ptsTimeStamp;
+ double dtsTimeStamp;
+
+ int layer;
+ unsigned int header;
+ int lHasPSHeader;
+
+ int lmpeg2;
+
+ int packetID;
+ int packetLen;
+
+ int pesPacketLen;
+ int tsPacketLen;
+
+ unsigned long rate;
+ int stdBufferScale;
+ unsigned long stdBufferSize;
+
+ /* This are bitmaps, if bit x is a 1 the stream number x is
+ available */
+ unsigned long availableAudioLayers;
+ unsigned long availableVideoLayers;
+
+ int audioLayerSelect;
+ int videoLayerSelect;
+
+ //
+ // MPEG2 Header Info [START]
+ //
+ int lOriginal;
+ int lCopyRight;
+ int lDataAlignmentIndicator;
+ int lPesPriority;
+ int lEncrypted;
+ int startCodePrefix;
+
+
+
+ int lPTSDTSFlag;
+ int lESCR;
+ int lESRateFlag;
+ int lDMSTRICKFLAG;
+ int lADDITIONAL_COPY_FLAG;
+ int lPES_CRC_FLAG;
+ int lPES_EXT_FLAG;
+ int nPES_HEADER_DATA_LENGTH;
+
+ // EXT FILED [START]
+
+ int lPrivateDataFlag;
+ int lPackHeaderFieldFlag;
+ int lSequenceCounterFlag;
+ int lSTDBufferFlag;
+ int lPES_EXT_FLAG_2;
+ int nPES_EXT_FIELD_LENGTH;
+ // EXT FILED [END]
+
+ int subStreamID;
+ //
+ // MPEG2 Header Info [END]
+ //
+
+ //
+ // Transport Stream Header [START]
+ //
+
+ int lHasTSHeader;
+
+ unsigned int sync_byte;
+ unsigned int transport_error_indicator;
+ unsigned int payload_unit_start_indicator;
+ unsigned int transport_priority;
+ unsigned int pid;
+ unsigned int transport_scrambling_control;
+ unsigned int adaption_field_control;
+ unsigned int continuity_counter;
+
+ unsigned int programs;
+ unsigned int programNumber;
+ unsigned int pmtPID;
+
+ MapPidStream** mapPidStreamArray;
+ int currentPos;
+
+ //
+ // Transport Stream Header [END]
+ //
+
+ public:
+ MpegSystemHeader();
+ ~MpegSystemHeader();
+
+ void setHeader(unsigned int header);
+ unsigned int getHeader();
+ int hasRAWHeader();
+
+
+ int getPTSFlag();
+ void setPTSFlag(int lPTSFlag);
+
+ double getSCRTimeStamp();
+ double getPTSTimeStamp();
+ double getDTSTimeStamp();
+
+
+ void setSCRTimeStamp(double timeStamp);
+ void setPTSTimeStamp(double ptsTimeStamp);
+ void setDTSTimeStamp(double dtsTimeStamp);
+
+ // returns 1 if it is a syslayer 0 if non syslayer -1 if unknown
+ int getLayer();
+ void setLayer(int layer);
+ int hasPSHeader();
+
+ //
+ // MPEG2 Stuff [START]
+ //
+
+ int getMPEG2();
+ void setMPEG2(int lmpeg2);
+
+ // 1 Byte [Start]
+ int getOriginalOrCopy();
+ void setOriginalOrCopy(int lOriginal);
+
+ int getCopyRight();
+ void setCopyRight(int lCopyRight);
+
+ int getDataAlignmentIndicator();
+ void setDataAlignmentIndicator(int lDataAlignmentIndicator);
+
+ int getPesPriority();
+ void setPesPriority(int lPesPriority);
+
+ int getEncrypted();
+ void setEncrypted(int lEncrypted);
+
+ int getStartCodePrefix();
+ void setStartCodePrefix(int startCodePrefix);
+
+ // 1. Byte [End]
+
+ int getPTSDTSFlag();
+ void setPTSDTSFlag(int lPTSDTSFlag);
+
+ int getESCRFlag();
+ void setESCRFlag(int lESCR);
+
+ int getES_RATE_Flag();
+ void setES_RATE_Flag(int lESRateFlag);
+
+
+ int getDMSTRICKFLAG();
+ void setDMSTRICKFLAG(int lDMSTRICKFLAG);
+
+ int getADDITIONAL_COPY_FLAG();
+ void setADDITIONAL_COPY_FLAG(int lADDITIONAL_COPY_FLAG);
+
+ int getPES_CRC_FLAG();
+ void setPES_CRC_FLAG(int lPES_CRC_FLAG);
+
+ int getPES_EXT_FLAG();
+ void setPES_EXT_FLAG(int lPES_EXT_FLAG);
+
+ //
+ // PES EXTENSION [START]
+ //
+
+ int getPrivateDataFlag();
+ void setPrivateDataFlag(int lPrivateDataFlag);
+
+ int getPackHeaderFieldFlag();
+ void setPackHeaderFieldFlag(int lPackHeaderFieldFlag);
+
+ int getSequenceCounterFlag();
+ void setSequenceCounterFlag(int lSequenceCounterFlag);
+
+ int getSTDBufferFlag();
+ void setSTDBufferFlag(int lSTDBufferFlag);
+
+ int getPES_EXT_FLAG_2();
+ void setPES_EXT_FLAG_2(int lPES_EXT_FLAG_2);
+
+ int getPES_EXT_FIELD_LENGTH();
+ void setPES_EXT_FIELD_LENGTH(int nPES_EXT_FIELD_LENGTH);
+
+
+
+ //
+ // PES EXTENSION [END]
+ //
+
+ int getPES_HEADER_DATA_LENGTH();
+ void setPES_HEADER_DATA_LENGTH(int nPES_HEADER_DATA_LENGTH);
+
+
+ int getSubStreamID();
+ void setSubStreamID(int subStreamID);
+
+
+ //
+ // MPEG2 Stuff [END]
+ //
+
+
+ //
+ // Transport Stream Header [START]
+ //
+
+ // returns true if it is ts header & sets the TS
+ // values in this class.
+ int hasTSHeader();
+
+
+ unsigned int getSync_byte();
+ unsigned int getTransport_error_indicator();
+ unsigned int getPayload_unit_start_indicator();
+ unsigned int getTransport_priority();
+ unsigned int getPid();
+ unsigned int getTransport_scrambling_control();
+ unsigned int getAdaption_field_control();
+ unsigned int getContinuity_counter();
+
+ unsigned int getPrograms();
+ void setPrograms(unsigned int programs);
+
+ unsigned int getProgramNumber();
+ void setProgramNumber(unsigned int programNumber);
+
+ unsigned int getPMTPID();
+ void setPMTPID(unsigned int pmtPID);
+
+ //
+ // manage different pids and their tsTypes and the fake psTypes
+ //
+
+ MapPidStream* lookup(unsigned int pid);
+ void insert(unsigned int pid,unsigned int tsType,
+ MpegSystemHeader* mpegHeader);
+
+
+ void printTSHeader();
+ void printProgramInfo();
+ void printMap(MapPidStream* mapPidStream);
+
+ //
+ // Transport Stream Header [END]
+ //
+
+
+
+ void addAvailableLayer(int streamID);
+ void resetAvailableLayers();
+
+ int getAudioLayerSelect();
+ void setAudioLayerSelect(int layer);
+
+ int getVideoLayerSelect();
+ void setVideoLayerSelect(int layer);
+
+
+ int getPacketID();
+ void setPacketID(int packetID);
+
+ // data which can be safley read and belongs to packetID
+ int getPacketLen();
+ void setPacketLen(int packetLen);
+
+ // length of PES packet (normally == getPacketLent)
+ int getPESPacketLen();
+ void setPESPacketLen(int packetLen);
+
+ // rest lenght of TS packet.
+ int getTSPacketLen();
+ void setTSPacketLen(int packetLen);
+
+
+
+ int getRate();
+ void setRate(int rate);
+
+ int getStdBufferScale();
+ void setStdBufferScale(int stdBufferScale);
+
+ unsigned long getStdBufferSize();
+ void setStdBufferSize(unsigned long stdBufferSize);
+
+ void print();
+
+ // return true is system header in some kind (TS or 1ba/1bb)
+ static int isSystemHeader(unsigned int header);
+
+ private:
+ void setTSHeader(unsigned int header);
+ void setPSHeader(unsigned int header);
+
+};
+
+#endif
+
+
+
diff --git a/mpeglib/lib/mpegplay/mpegSystemStream.cpp b/mpeglib/lib/mpegplay/mpegSystemStream.cpp
new file mode 100644
index 00000000..ca25d7e8
--- /dev/null
+++ b/mpeglib/lib/mpegplay/mpegSystemStream.cpp
@@ -0,0 +1,235 @@
+/*
+ reads paket input data
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "mpegSystemStream.h"
+
+#define _RESYNC_STATE_INIT 0
+#define _RESYNC_STATE_NEED_PACKET 1
+#define _RESYNC_STATE_NEED_START 2
+
+#include <iostream>
+
+using namespace std;
+
+
+MpegSystemStream::MpegSystemStream(InputStream* input) {
+ this->input=input;
+ syncCode=0xff; // invalid
+ lState=_RESYNC_STATE_INIT;
+ tsSystemStream=new TSSystemStream(input);
+ psSystemStream=new PSSystemStream(input);
+ pesSystemStream=new PESSystemStream(input);
+}
+
+
+MpegSystemStream::~MpegSystemStream() {
+ delete tsSystemStream;
+ delete psSystemStream;
+ delete pesSystemStream;
+}
+
+
+int MpegSystemStream::getByteDirect() {
+ unsigned char byte;
+ if (input->read((char*)&byte,1) != 1) {
+ return -1;
+ }
+ return (int)byte;
+}
+
+int MpegSystemStream::readSyncCode() {
+ int byte;
+ byte=getByteDirect();
+ if (byte==-1) {
+ return false;
+ }
+
+ syncCode<<=8;
+ syncCode|=byte;
+ syncCode&=0xffffffff;
+ return true;
+
+}
+
+int MpegSystemStream::firstInitialize(MpegSystemHeader* mpegHeader) {
+
+ if (readSyncCode() == false) {
+ return false;
+ }
+ mpegHeader->setHeader(syncCode);
+
+ // SEQ_START_CODE: 1b3
+ if (mpegHeader->hasRAWHeader()) {
+ // If we found a seq_heade_start code we assume
+ // that we are a video only stream
+ // we must directly insert it.
+
+ // here we set for all future calls fixed
+ // parameter
+ mpegHeader->setLayer(_PACKET_NO_SYSLAYER); // no syslayer
+ mpegHeader->setPacketID(_PAKET_ID_VIDEO);
+ mpegHeader->setPacketLen(8192);
+ mpegHeader->resetAvailableLayers();
+ return true;
+ }
+ // no 1bb
+ if (syncCode == _SYSTEM_HEADER_START_CODE) return false;
+
+ // TS 1ba
+ if (processSystemHeader(mpegHeader) == true) {
+ // have init. we are a syslayer
+ mpegHeader->setLayer(_PACKET_SYSLAYER); // syslayer
+ lState=_RESYNC_STATE_NEED_START;
+ return true;
+ }
+ return false;
+
+}
+
+void MpegSystemStream::reset() {
+ lState=_RESYNC_STATE_NEED_PACKET;
+}
+
+
+int MpegSystemStream::nextPacket(MpegSystemHeader* mpegHeader) {
+ int layer=mpegHeader->getLayer();
+ if (layer == _PACKET_NO_SYSLAYER) {
+ return true;
+ }
+ // seek to packet
+ if (readSyncCode() == false) {
+ return false;
+ }
+
+
+ // default to "HLT" operation
+ mpegHeader->setPacketID(_PAKET_ID_NUKE);
+ mpegHeader->setPacketLen(0);
+
+
+ // here we know that we are a SYSLAYER (TS or 1ba)
+ if (lState == _RESYNC_STATE_NEED_PACKET) {
+ // no 1bb codes in resync state
+ if (syncCode == _SYSTEM_HEADER_START_CODE) return false;
+ }
+
+ // is this a syslayer _STARTCODE_ ?
+ if (MpegSystemHeader::isSystemHeader(syncCode)==true) {
+ // set startcode & process it
+ mpegHeader->setHeader(syncCode);
+ // TS 1ba 1bb
+ if (processSystemHeader(mpegHeader) == true) {
+ lState=_RESYNC_STATE_NEED_START;
+ return true;
+ }
+ reset();
+ return false;
+ }
+
+ if (lState != _RESYNC_STATE_NEED_START) return false;
+
+ // no syslayer startcode. PES processing
+ if ((syncCode & 0xFFFFFF00) == 0x00000100) {
+ int bytes=pesSystemStream->processStartCode(syncCode,mpegHeader);
+ if (bytes == 0) {
+ reset();
+ return false;
+ }
+ // if we are a TS layer we cannot read getPESPacketLength byte
+ // but only the bytes given by getTSPacketLength
+ // additionally we must store the mapping between pid and real packetID.
+ if (mpegHeader->hasTSHeader()) {
+ unsigned int pid=mpegHeader->getPid();
+ unsigned int id =mpegHeader->getPacketID();
+ printf("current PID:%x current PacketID:%x\n",pid,id);
+ MapPidStream* mapPidStream=mpegHeader->lookup(pid);
+ mapPidStream->psType=id;
+ mapPidStream->pesPacketSize=mpegHeader->getPESPacketLen();
+
+ int ts=mpegHeader->getTSPacketLen();
+ if (bytes > ts) {
+ cout << "ERROR PES READ MORE than TS HAS"<<endl;
+ return false;
+ }
+ mpegHeader->setTSPacketLen(ts-bytes);
+ return demux_ts_pes_buffer(mpegHeader);
+ }
+ // 1ba stream
+ mpegHeader->setPacketLen(mpegHeader->getPESPacketLen());
+ return true;
+ }
+
+ // something unknown/else went wrong
+ return false;
+}
+
+int MpegSystemStream::demux_ts_pes_buffer(MpegSystemHeader* mpegHeader) {
+
+
+ if (lState == _RESYNC_STATE_NEED_PACKET) {
+ cout << "NO ts_pes because of resync"<<endl;
+ return false;
+ }
+ MapPidStream* mapPidStream=mpegHeader->lookup(mpegHeader->getPid());
+
+ int pes=mapPidStream->pesPacketSize;
+ int ts=mpegHeader->getTSPacketLen();
+ int wantRead=pes;
+
+ // bug workaround for PES packetlength == 0
+ if (pes > 0) {
+ if (ts < wantRead) {
+ cout << "TS is less setting wantRead:"<<ts<<endl;
+ mapPidStream->pesPacketSize=pes-ts;
+ wantRead=ts;
+ } else {
+ // pes < ts
+ mpegHeader->setTSPacketLen(ts-pes);
+ wantRead=pes;
+ }
+ } else {
+ // in case of bug, always put the whole packet for pid in
+ wantRead=ts;
+ }
+ mpegHeader->setPacketID(mapPidStream->psType);
+ mpegHeader->setPacketLen(wantRead);
+ return true;
+}
+
+
+int MpegSystemStream::processSystemHeader(MpegSystemHeader* mpegHeader) {
+ if (mpegHeader->hasTSHeader()) {
+ if (tsSystemStream->processStartCode(mpegHeader) == true) {
+ if (mpegHeader->getPacketID() == _PAKET_ID_NUKE) {
+ // nuke the data by signalling how much bytes to nuke
+ mpegHeader->setPacketLen(mpegHeader->getTSPacketLen());
+ return true;
+ }
+ unsigned int pus=mpegHeader->getPayload_unit_start_indicator();
+ if (pus == false) {
+ // we need to append data
+ return demux_ts_pes_buffer(mpegHeader);
+ }
+ return true;
+ }
+ return false;
+ }
+ if (mpegHeader->hasPSHeader()) {
+ return (psSystemStream->processStartCode(mpegHeader));
+ }
+ return false;
+
+}
+
+
+
diff --git a/mpeglib/lib/mpegplay/mpegSystemStream.h b/mpeglib/lib/mpegplay/mpegSystemStream.h
new file mode 100644
index 00000000..8c5d5ffa
--- /dev/null
+++ b/mpeglib/lib/mpegplay/mpegSystemStream.h
@@ -0,0 +1,53 @@
+/*
+ reads paket input data
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#ifndef __MPEGSYSTEMSTREAM_H
+#define __MPEGSYSTEMSTREAM_H
+
+
+
+#include "tsSystemStream.h"
+#include "psSystemStream.h"
+#include "pesSystemStream.h"
+
+
+class MpegSystemStream {
+
+ InputStream* input;
+ int lHasLength;
+ unsigned int syncCode;
+ int lState;
+ TSSystemStream* tsSystemStream;
+ PSSystemStream* psSystemStream;
+ PESSystemStream* pesSystemStream;
+
+ public:
+ MpegSystemStream(InputStream* input);
+ ~MpegSystemStream();
+
+ // returns true if init successful
+ int firstInitialize(MpegSystemHeader* mpegHeader);
+ int nextPacket(MpegSystemHeader* mpegHeader);
+ inline int eof() { return input->eof(); }
+ void reset();
+
+ private:
+ int readSyncCode();
+ int getByteDirect();
+ int demux_ts_pes_buffer(MpegSystemHeader* mpegHeader);
+
+ int processSystemHeader(MpegSystemHeader* mpegHeader);
+
+
+};
+
+#endif
diff --git a/mpeglib/lib/mpegplay/mpegVideoBitWindow.cpp b/mpeglib/lib/mpegplay/mpegVideoBitWindow.cpp
new file mode 100644
index 00000000..87467a9c
--- /dev/null
+++ b/mpeglib/lib/mpegplay/mpegVideoBitWindow.cpp
@@ -0,0 +1,258 @@
+/*
+ bitwindow mpeg video
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "mpegVideoBitWindow.h"
+
+#include <iostream>
+
+using namespace std;
+
+
+MpegVideoBitWindow::MpegVideoBitWindow() {
+ // Make buffer length multiple of 4.
+
+ this->size=80000;
+
+
+
+ if ((size % 4) != 0) {
+ cout << "MpegVideoStream not multiple of 4"<<endl;
+ exit(-1);
+ }
+
+ /* Create MpegVideoStream. */
+ buf_start = (unsigned int*) malloc (sizeof(unsigned int)*(size*4));
+
+ /*
+ * Set max_buf_length to one less than actual length to deal with messy
+ * data without proper seq. end codes.
+ */
+ max_buf_length=size-1;
+
+
+ /* Initialize bitstream i/o fields. */
+
+ bit_offset = 0;
+ buffer = buf_start;
+ buf_length = 0;
+ num_left=0;
+ leftover_bytes=0;
+ curBits = 0;
+
+
+
+
+ nBitMask[0] = 0x00000000;
+ nBitMask[1] = 0x80000000;
+ nBitMask[2] = 0xc0000000;
+ nBitMask[3] = 0xe0000000;
+ nBitMask[4] = 0xf0000000;
+ nBitMask[5] = 0xf8000000;
+ nBitMask[6] = 0xfc000000;
+ nBitMask[7] = 0xfe000000;
+ nBitMask[8] = 0xff000000;
+ nBitMask[9] = 0xff800000;
+ nBitMask[10] = 0xffc00000;
+ nBitMask[11] = 0xffe00000;
+ nBitMask[12] = 0xfff00000;
+ nBitMask[13] = 0xfff80000;
+ nBitMask[14] = 0xfffc0000;
+ nBitMask[15] = 0xfffe0000;
+ nBitMask[16] = 0xffff0000;
+ nBitMask[17] = 0xffff8000;
+ nBitMask[18] = 0xffffc000;
+ nBitMask[19] = 0xffffe000;
+ nBitMask[20] = 0xfffff000;
+ nBitMask[21] = 0xfffff800;
+ nBitMask[22] = 0xfffffc00;
+ nBitMask[23] = 0xfffffe00;
+ nBitMask[24] = 0xffffff00;
+ nBitMask[25] = 0xffffff80;
+ nBitMask[26] = 0xffffffc0;
+ nBitMask[27] = 0xffffffe0;
+ nBitMask[28] = 0xfffffff0;
+ nBitMask[29] = 0xfffffff8;
+ nBitMask[30] = 0xfffffffc;
+ nBitMask[31] = 0xfffffffe;
+ nBitMask[32] = 0xffffffff;
+
+
+}
+
+
+MpegVideoBitWindow::~MpegVideoBitWindow() {
+ delete buf_start;
+}
+
+
+
+int MpegVideoBitWindow::appendToBuffer(unsigned char* ptr,int len) {
+ int byte_length = getLength()*4;
+
+
+ resizeBuffer(len);
+
+ if (num_left != 0) {
+ byte_length += num_left;
+ *(buffer+buf_length)=leftover_bytes;
+ }
+ memcpy(((unsigned char *)buffer)+byte_length,ptr,len);
+
+ if ((unsigned long int)1 != ntohl(1)) {
+ unsigned int *mark = buffer+buf_length;
+ int i;
+ int n=(len+num_left)&0xfffffffc;
+ for (i=0; i < n; i+=4) {
+ *mark=ntohl(*mark);
+ mark++;
+ }
+ }
+ byte_length = byte_length + len;
+ num_left = byte_length % 4;
+ buf_length = byte_length / 4;
+ updateCurBits();
+
+ leftover_bytes = *(buffer +buf_length);
+ return true;
+}
+
+int MpegVideoBitWindow::getLinearFree() {
+ unsigned int* endPos=buf_start+size;
+ unsigned int* currPos=buffer+buf_length;
+
+ int back=endPos-currPos;
+ return back;
+}
+
+
+
+
+
+void MpegVideoBitWindow::flushByteOffset() {
+ int byteoff;
+
+ byteoff = bit_offset % 8;
+
+ if (byteoff != 0) {
+ flushBitsDirect((8-byteoff));
+ }
+}
+
+
+void MpegVideoBitWindow::appendToBuffer(unsigned int startCode) {
+ unsigned int startCodeRaw=htonl(startCode);
+ resizeBuffer(4);
+ appendToBuffer((unsigned char*)&startCodeRaw,4);
+}
+
+
+void MpegVideoBitWindow::clear() {
+ buffer = buf_start;
+ buf_length = 0;
+ bit_offset = 0;
+ curBits = 0;
+}
+
+
+int MpegVideoBitWindow::getLength() {
+ return buf_length;
+}
+
+
+
+
+
+
+
+void MpegVideoBitWindow::printChar(int bytes) {
+ int i;
+ unsigned char* mark;
+
+ mark=(unsigned char *)buffer;
+
+ for(i=0;i<bytes;i++) {
+ printf("i:%d read=%x\n",i,mark[i]);
+ }
+ printf("*********\n");
+
+}
+
+
+void MpegVideoBitWindow::printInt(int bytes) {
+ int i;
+ int n;
+ unsigned int* mark;
+
+ mark=(unsigned int*)buf_start;
+
+ n=bytes/sizeof(int);
+ for(i=0;i<n;i++) {
+ printf("i:%d read=%x\n",i,mark[i]);
+ }
+ printf("*********\n");
+
+}
+
+
+void MpegVideoBitWindow::print() {
+ int byte_length = getLength()*4;
+
+ printf("bit_offset:%x\n",bit_offset);
+ printf("num_left:%x\n",num_left);
+ printf("leftover_bytes:%x\n",leftover_bytes);
+ printf("buf_length:%x\n",buf_length);
+ printf("curBits:%x\n",curBits);
+ printf("pos:%8x\n",byte_length);
+ printChar(8);
+}
+
+
+void MpegVideoBitWindow::resizeBuffer(int insertBytes) {
+ /* Read all the headers, now make room for packet */
+ if (buf_start+max_buf_length < buffer+insertBytes/4+buf_length) {
+
+ if (max_buf_length - buf_length < (int) insertBytes/4) {
+
+ /* Buffer too small for a packet (plus whats there),
+ * time to enlarge it!
+ */
+
+ unsigned int *old = buf_start;
+ max_buf_length=buf_length+insertBytes/4+1;
+
+ buf_start=(unsigned int*) malloc(sizeof(unsigned int)*max_buf_length);
+ if (buf_start == NULL) {
+ cout << "allocation of:"<<max_buf_length<<" bytes failed"<<endl;
+ exit(0);
+ }
+ memcpy((unsigned char *)buf_start,buffer,(unsigned int)buf_length*4);
+ delete old;
+ buffer = buf_start;
+ cout << "enlarge buffer-1 end***********"<<endl;
+
+ } else {
+ memcpy((unsigned char *)buf_start,
+ buffer, (unsigned int) buf_length*4);
+ buffer = buf_start;
+ }
+ }
+}
+
+
+void MpegVideoBitWindow::fillWithIsoEndCode(int bytes) {
+ int i;
+ int n=bytes/4;
+ for (i=0;i<n;i++) {
+ appendToBuffer(ISO_11172_END_CODE);
+ }
+}
diff --git a/mpeglib/lib/mpegplay/mpegVideoBitWindow.h b/mpeglib/lib/mpegplay/mpegVideoBitWindow.h
new file mode 100644
index 00000000..46abe44e
--- /dev/null
+++ b/mpeglib/lib/mpegplay/mpegVideoBitWindow.h
@@ -0,0 +1,108 @@
+/*
+ bitwindow mpeg video
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __MPEGVIDEOBITWINDOW_H
+#define __MPEGVIDEOBITWINDOW_H
+
+#include "../input/inputPlugin.h"
+
+
+#define ISO_11172_END_CODE ((unsigned int)0x000001b9)
+
+class MpegVideoBitWindow {
+ int size; /* size of buffer */
+ unsigned int bit_offset; /* Bit offset in stream. */
+ unsigned int *buffer; /* Pointer to next byte in buffer */
+ int buf_length; /* Length of remaining buffer.*/
+ unsigned int *buf_start; /* Pointer to buffer start. */
+ int max_buf_length; /* Max length of buffer. */
+ unsigned int num_left; /* from ReadPacket - leftover */
+ unsigned int leftover_bytes; /* from ReadPacket - leftover */
+ unsigned int curBits; /* current bits */
+
+
+ unsigned int nBitMask[33];
+
+
+ public:
+ MpegVideoBitWindow();
+ ~MpegVideoBitWindow();
+
+ int appendToBuffer(unsigned char* ptr,int len);
+ int getLinearFree();
+ // true if feof() is true and the buffer is emtpy
+ int isEof();
+ void flushByteOffset();
+ void appendToBuffer(unsigned int startCode);
+
+
+ inline void updateCurBits() {
+ curBits = *buffer << bit_offset;
+ }
+
+ inline unsigned int showBits(int bits) {
+
+ unsigned int mask=nBitMask[bits];
+ int shift=32-(bits);
+ int bO;
+ shift=(curBits & mask)>>shift;
+ bO = bit_offset + bits;
+ if (bO > 32) {
+ return (shift | (*(buffer+1)>>(64-bO)));
+ }
+ return shift;
+ }
+
+ inline unsigned int showBits32() {
+ if (bit_offset) {
+ return (curBits | (*(buffer+1) >> (32 - bit_offset)));
+ }
+ return curBits;
+ }
+
+ inline void flushBitsDirect(unsigned int bits) {
+ bit_offset += bits;
+ if (bit_offset & 0x20) {
+ bit_offset -= 32;
+ bit_offset &= 0x1f;
+ buffer++;
+ updateCurBits();
+ buf_length--;
+ }
+ else {
+ curBits <<= bits;
+ }
+ }
+
+ inline unsigned int getBits(int bits) {
+ unsigned int result=showBits(bits);
+ flushBitsDirect(bits);
+ return result;
+ }
+
+ void clear();
+ int getLength();
+
+ int hasBytes(int bytes);
+
+ void printChar(int bytes);
+ void printInt(int bytes);
+ void print();
+ void resizeBuffer(int insertBytes);
+ void fillWithIsoEndCode(int bytes);
+
+
+
+};
+#endif
diff --git a/mpeglib/lib/mpegplay/mpegVideoHeader.cpp b/mpeglib/lib/mpegplay/mpegVideoHeader.cpp
new file mode 100644
index 00000000..e91401a8
--- /dev/null
+++ b/mpeglib/lib/mpegplay/mpegVideoHeader.cpp
@@ -0,0 +1,293 @@
+/*
+ stores sequence header info, for reinit of stream
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "mpegVideoHeader.h"
+
+#include "mpegExtension.h"
+#include "mpegVideoStream.h"
+
+#include <iostream>
+
+using namespace std;
+
+/* Video rates table */
+/* Cheat on Vid rates, round to 30, and use 30 if illegal value
+ Except for 9, where Xing means 15, and given their popularity, we'll
+ be nice and do it */
+
+static double VidRateNum[16]={29.97, 24, 24, 25, 29.97, 29.97, 50, 59.94,
+ 59.94, 14.985, 29.97, 29.97, 29.97, 29.97,
+ 29.97, 29.97};
+
+
+
+static const unsigned char default_intra_matrix[64] = {
+ 8, 16, 19, 22, 26, 27, 29, 34,
+ 16, 16, 22, 24, 27, 29, 34, 37,
+ 19, 22, 26, 27, 29, 34, 34, 38,
+ 22, 22, 26, 27, 29, 34, 37, 40,
+ 22, 26, 27, 29, 32, 35, 40, 48,
+ 26, 27, 29, 32, 35, 40, 48, 58,
+ 26, 27, 29, 34, 38, 46, 56, 69,
+ 27, 29, 35, 38, 46, 56, 69, 83};
+
+
+
+/* Set up array for fast conversion from zig zag order to row/column
+ coordinates.
+*/
+
+const int zigzag[64][2] = {
+ {0,0},{1,0},{0,1},{0,2},{1,1},{2,0},{3,0},{2,1},{1,2},{0,3},{0,4},{1,3},
+ {2,2},{3,1},{4,0},{5,0},{4,1},{3,2},{2,3},{1,4},{0,5},{0,6},{1,5},{2,4},
+ {3,3},{4,2},{5,1},{6,0},{7,0},{6,1},{5,2},{4,3},{3,4},{2,5},{1,6},{0,7},
+ {1,7},{2,6},{3,5},{4,4},{5,3},{6,2},{7,1},{7,2},{6,3},{5,4},{4,5},{3,6},
+ {2,7},{3,7},{4,6},{5,5},{6,4},{7,3},{7,4},{6,5},{5,6},{4,7},{5,7},{6,6},
+ {7,5},{7,6},{6,7},{7,7} };
+
+
+
+
+
+MpegVideoHeader::MpegVideoHeader() {
+ init();
+
+}
+
+
+void MpegVideoHeader::init() {
+
+ h_size=0;
+ v_size=0;
+ mb_height=0;
+ mb_width=0;
+ mb_size=0;
+ aspect_ratio=0;
+ bit_rate=0;
+ vbv_buffer_size=0;
+ const_param_flag=0;
+ picture_rate=0.0;
+ extension=new MpegExtension();
+ init_quanttables();
+
+}
+
+void MpegVideoHeader::init_quanttables() {
+ int i;
+ int j;
+
+ /* Copy default intra matrix. */
+
+ for (i = 0; i < 8; i++) {
+ for (j = 0; j < 8; j++) {
+ intra_quant_matrix[i][j]=default_intra_matrix[i*8+j];
+ }
+ }
+
+ /* Initialize non intra quantization matrix. */
+
+ for (i = 0; i < 8; i++) {
+ for (j = 0; j < 8; j++) {
+ non_intra_quant_matrix[i][j] = 16;
+ }
+ }
+}
+
+
+MpegVideoHeader::~MpegVideoHeader() {
+
+ delete extension;
+
+}
+
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ParseSeqHead --
+ *
+ * Assumes bit stream is at the END of the sequence
+ * header start code. Parses off the sequence header.
+ *
+ * Results:
+ * Fills the vid_stream structure with values derived and
+ * decoded from the sequence header. Allocates the pict image
+ * structures based on the dimensions of the image space
+ * found in the sequence header.
+ *
+ * Side effects:
+ * Bit stream irreversibly parsed off.
+ *
+ *--------------------------------------------------------------
+ */
+int MpegVideoHeader::parseSeq(MpegVideoStream* mpegVideoStream) {
+ unsigned int data;
+ int i ;
+
+ // seq_start_code already flushed!!!
+
+ /* Get horizontal size of image space. */
+
+ h_size=mpegVideoStream->getBits(12);
+
+ /* Get vertical size of image space. */
+
+ v_size=mpegVideoStream->getBits(12);
+
+
+ /* Calculate macroblock width and height of image space. */
+
+ mb_width = (h_size + 15) / 16;
+ mb_height = (v_size + 15) / 16;
+ mb_size=mb_height * mb_width-1;
+
+
+
+ /* Parse of aspect ratio code. */
+
+ data=mpegVideoStream->getBits(4);
+
+ aspect_ratio = (unsigned char) data;
+
+ /* Parse off picture rate code. */
+
+ data=mpegVideoStream->getBits(4);
+ picture_rate=VidRateNum[data];
+
+ /* Parse off bit rate. */
+
+ data=mpegVideoStream->getBits(18);
+ bit_rate = data;
+
+ /* Flush marker bit. */
+
+ mpegVideoStream->flushBits(1);
+
+ /* Parse off vbv buffer size. */
+
+ data=mpegVideoStream->getBits(10);
+ vbv_buffer_size = data;
+
+ /* Parse off contrained parameter flag. */
+
+ data=mpegVideoStream->getBits(1);
+ if (data) {
+ const_param_flag = true;
+ } else
+ const_param_flag = false;
+
+ /*
+ * If intra_quant_matrix_flag set, parse off intra quant matrix values.
+ */
+ data=mpegVideoStream->getBits(1);
+ if (data) {
+ for (i = 0; i < 64; i++) {
+ data=mpegVideoStream->getBits(8);
+ intra_quant_matrix[zigzag[i][1]][zigzag[i][0]]=(unsigned char)data;
+ }
+ }
+ /*
+ * If non intra quant matrix flag set, parse off non intra quant matrix
+ * values.
+ */
+
+ data=mpegVideoStream->getBits(1);
+ if (data) {
+ for (i = 0; i < 64; i++) {
+ data=mpegVideoStream->getBits(8);
+
+ non_intra_quant_matrix[zigzag[i&0x3f][1]][zigzag[i&0x3f][0]]=
+ (unsigned char) data;
+ }
+ }
+ /*
+ * If next start code is extension/user start code,
+ * parse off extension data.
+ */
+ extension->processExtensionData(mpegVideoStream);
+
+ return true;
+
+}
+
+
+
+
+void MpegVideoHeader::copyTo(MpegVideoHeader* dest) {
+
+
+ dest->h_size=h_size;
+ dest->v_size=v_size;
+ dest->mb_height=mb_height;
+ dest->mb_width=mb_width;
+ dest->mb_size=mb_size;
+ dest->aspect_ratio=aspect_ratio;
+ dest->bit_rate=bit_rate;
+ dest->vbv_buffer_size=vbv_buffer_size;
+ dest->const_param_flag=const_param_flag;
+ dest->picture_rate=picture_rate;
+
+ int i;
+ int j;
+
+
+ /* Copy default intra matrix. */
+
+ for (i = 0; i < 8; i++) {
+ for (j = 0; j < 8; j++) {
+ dest->intra_quant_matrix[i][j]=intra_quant_matrix[i][j];
+
+ }
+ }
+
+ /* Initialize non intra quantization matrix. */
+
+ for (i = 0; i < 8; i++) {
+ for (j = 0; j < 8; j++) {
+ dest->non_intra_quant_matrix[i][j] =non_intra_quant_matrix[i][j] ;
+ }
+ }
+
+}
+
+
+
+
+double MpegVideoHeader::getPictureTime() {
+ if (picture_rate > 0) {
+ return 1.0/picture_rate;
+ }
+ return 0.0;
+}
+
+
+
+
+
+
+
+void MpegVideoHeader::print(char* description) {
+ cout << "MpegVideoHeader [START]:"<<description<<endl;
+ cout <<"h_size:"<<h_size<<endl;
+ cout <<"v_size:"<<v_size<<endl;
+ cout <<"mb_height:"<<mb_height<<endl;
+ cout <<"mb_width:"<<mb_width<<endl;
+ cout <<"mb_size:"<<mb_size<<endl;
+ cout <<"aspect_ratio:"<<aspect_ratio<<endl;
+ cout <<"bit_rate:"<<bit_rate<<endl;
+ cout <<"vbv_buffer_size:"<<vbv_buffer_size<<endl;
+ cout <<"const_param_flag:"<<const_param_flag<<endl;
+ cout << "MpegVideoHeader [END]:"<<endl;
+
+}
diff --git a/mpeglib/lib/mpegplay/mpegVideoHeader.h b/mpeglib/lib/mpegplay/mpegVideoHeader.h
new file mode 100644
index 00000000..bb3302a1
--- /dev/null
+++ b/mpeglib/lib/mpegplay/mpegVideoHeader.h
@@ -0,0 +1,83 @@
+/*
+ stores sequence header info, for reinit of stream
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+
+#ifndef __MPEGVIDEOHEADER_H
+#define __MPEGVIDEOHEADER_H
+
+
+
+/**
+ Some vcd do only have one sequence header for the whole
+ cd.
+ This means we must store this info even after a seek
+ which usually destroy the video stream object.
+ We then can re-init the video object with the values
+ we store here.
+
+ Here we have the "header" for an mpeg video stream.
+
+*/
+
+
+class MpegVideoHeader {
+
+ unsigned int h_size; /* Horiz. size in pixels. */
+ unsigned int v_size; /* Vert. size in pixels. */
+ int mb_height; /* Vert. size in mblocks. */
+ int mb_width; /* Horiz. size in mblocks. */
+ int mb_size; /* mb_height*mb_width-1 */
+ unsigned char aspect_ratio; /* Code for aspect ratio. */
+ unsigned int bit_rate; /* Bit rate. */
+ unsigned int vbv_buffer_size; /* Minimum buffer size. */
+ int const_param_flag; /* Contrained parameter flag. */
+ float picture_rate; /* Parse off picture rate code*/
+ unsigned int intra_quant_matrix[8][8]; /* Quantization matrix for
+ intracoded frames. */
+ unsigned int non_intra_quant_matrix[8][8]; /* Quanitization matrix for
+ non intracoded frames. */
+ class MpegExtension* extension;
+
+
+ public:
+
+ MpegVideoHeader();
+ ~MpegVideoHeader();
+
+ int parseSeq(class MpegVideoStream* mpegVideoStream);
+ void copyTo(MpegVideoHeader* dest);
+ void print(char* description);
+ void init_quanttables();
+
+ inline int getMB_Height() { return mb_height;}
+ inline int getMB_Size() { return mb_size;}
+ inline int getMB_Width() { return mb_width;}
+
+ inline float getPictureRate() { return picture_rate ;}
+ double getPictureTime();
+
+ inline unsigned int* getIntra_quant_matrix() {
+ // printf("getI:%p\n",intra_quant_matrix[0]);
+ return intra_quant_matrix[0]; }
+ inline unsigned int* getNon_intra_quant_matrix() {
+ // printf("getN:%p\n",non_intra_quant_matrix[0]);
+ return non_intra_quant_matrix[0]; }
+
+ private:
+ void init();
+
+};
+
+#endif
diff --git a/mpeglib/lib/mpegplay/mpegVideoLength.cpp b/mpeglib/lib/mpegplay/mpegVideoLength.cpp
new file mode 100644
index 00000000..4befa31a
--- /dev/null
+++ b/mpeglib/lib/mpegplay/mpegVideoLength.cpp
@@ -0,0 +1,424 @@
+/*
+ mpg I video/audio player plugin
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "mpegVideoLength.h"
+
+#include "../mpegplay/mpegVideoStream.h"
+#include "../mpegplay/gop.h"
+
+#include <iostream>
+
+using namespace std;
+
+#define SEARCH_SIZE 1024*1024*6
+#define SEEKWINDOW 1024*1024
+
+MpegVideoLength::MpegVideoLength(InputStream* input) {
+
+ this->input=input;
+ this->mpegVideoStream=new MpegVideoStream(input);
+
+ startGOP=new GOP();
+ endGOP=new GOP();
+ lengthGOP=new GOP();
+ mpegVideoHeader=new MpegVideoHeader();
+
+
+ lHasStart=false;
+ lHasEnd=false;
+ lHasStream=false;
+ lHasResync=false;
+ lHasSystemStream=false;
+ lHasRawStream=false;
+ lSysLayer=false;
+
+ mpegSystemStream=new MpegSystemStream(input);
+ mpegSystemHeader=new MpegSystemHeader();
+
+
+ lCanSeek=input->seek(0);
+ if (lCanSeek == false) {
+ cout << "mpegVideoLength: stream does not support seek"<<endl;
+ }
+ realLength=input->getByteLength();
+ upperEnd=realLength;
+ if (realLength > 1024*1024*600) {
+ upperEnd=1024*1024*600;
+ }
+}
+
+
+MpegVideoLength::~MpegVideoLength() {
+ delete startGOP;
+ delete endGOP;
+ delete lengthGOP;
+ delete mpegVideoStream;
+ delete mpegVideoHeader;
+ delete mpegSystemHeader;
+ delete mpegSystemStream;
+}
+
+
+/**
+ This long and ugly functions initialize a reader from
+ where to get the time informations.
+ All these switches deal with the problem, that we really should
+ have never a while loop, because this makes the decoder
+ thread unresponsive and can lead to deadlocks.
+*/
+
+int MpegVideoLength::firstInitialize() {
+
+ // no seek means no length detection
+ if (lCanSeek==false) {
+ // no detection possible, initialized ready
+ input->seek(0);
+ return true;
+ }
+ // do we have already a reader from where to get time?
+ if (lHasStream == false) {
+ // still init system ?
+ if (lHasSystemStream == false) {
+ if (mpegSystemStream->firstInitialize(mpegSystemHeader) == true) {
+ lHasSystemStream=true;
+
+ // if non system, reset everything and work on raw stream
+ if (mpegSystemHeader->getLayer() == _PACKET_SYSLAYER) {
+ lSysLayer=true;
+ }
+ if (lSysLayer == false) {
+ input->seek(0);
+ }
+ }
+ return false;
+ }
+ // if working on syslayer level we dont need the raw stream
+ // set it to true
+ if (lSysLayer == true) {
+ lHasRawStream=true;
+ }
+ if (lHasRawStream == false) {
+ if (mpegVideoStream->firstInitialize(mpegVideoHeader) == true) {
+ lHasRawStream=true;
+ }
+ return false;
+ }
+ lHasStream=true;
+ return false;
+ }
+ if (lHasStart == false) {
+ if (seekToStart() == true) {
+ lHasStart=true;
+ }
+
+ // clear and jump near the end
+ mpegVideoStream->clear();
+ int success=input->seek(upperEnd-SEARCH_SIZE);
+ if (success == false) {
+ cout << "mpegVideoStreamStream does not support seek"<<endl;
+ // we can't find an upper end, thus we are ready
+ input->seek(0);
+ return true;
+ }
+ return false;
+ }
+ if (lHasResync==false) {
+ if (lSysLayer) {
+ if (mpegSystemStream->nextPacket(mpegSystemHeader) == false) {
+ return false;
+ }
+ } else {
+ if (mpegVideoStream->nextGOP()==false) {
+ return false;
+ }
+ }
+ lHasResync=true;
+ return false;
+ }
+ if (lHasEnd == false) {
+ if (seekToEnd() == true) {
+ lHasEnd=true;
+ if (endGOP->substract(startGOP,lengthGOP) == false) {
+ cout << "substract error in final length detection"<<endl;
+ if (startGOP->substract(endGOP,lengthGOP) == true) {
+ cout << "this stream counts the time backwards"<<endl;
+ } else {
+ cout << "couldnt determine stream length"<<endl;
+ GOP dummy;
+ dummy.copyTo(lengthGOP);
+ }
+ }
+ // ok now we have the length but we must calculate
+ // the length of the whole stream
+ // we do not jump ofer 600 MB because this makes problems
+ // on some cdrom.
+ // here we calculate the real length if upperEnd != realEnd
+ int hour=lengthGOP->getHour();
+ int minute=lengthGOP->getMinutes();
+ long seconds=lengthGOP->getSeconds();
+ seconds=seconds+minute*60+hour*60*60;
+ if (upperEnd > 1) {
+ if (realLength > upperEnd) {
+ float ratio=realLength/upperEnd;
+ float realSeconds=seconds*ratio;
+ hour=(int)(realSeconds/(float)(60*60));
+ realSeconds=realSeconds-hour*60*60;
+ minute=(int)(realSeconds/60.0);
+ realSeconds=realSeconds-minute*60;
+ seconds=(int)realSeconds;
+ lengthGOP->setHour(hour);
+ lengthGOP->setMinute(minute);
+ lengthGOP->setSecond(seconds);
+ }
+ }
+ }
+ }
+ input->seek(0);
+ return true;
+}
+
+
+long MpegVideoLength::getLength() {
+ long back=0;
+ back=lengthGOP->getHour()*60*60;
+ back+=lengthGOP->getMinutes()*60;
+ back+=lengthGOP->getSeconds();
+
+ // length in second
+ return back;
+}
+
+
+long MpegVideoLength::getSeekPos(int seconds) {
+ // calculate from seconds to predicted position in stream
+ long back=0;
+ double ratio;
+
+ ratio=(double)realLength*(double)seconds;
+ ratio=ratio/((double)getLength()+1.0);
+ back=(long)ratio;
+
+ return back;
+
+}
+
+
+// We try to search the first SEARCH_SIZE KB for a valid picture start.
+int MpegVideoLength::seekToStart() {
+ int success;
+
+ /**
+ If we are a system Layer we calculate the startGOP
+ by the system Packets
+ */
+ if (lSysLayer == true) {
+ success=parseToPTS(startGOP);
+ } else {
+
+ mpegVideoStream->hasBytes(100);
+
+ success=parseToGOP(startGOP);
+ }
+
+ if (success == false) {
+ cout << "picture startcode not found [START]"<<endl;
+
+ }
+ // we return always true
+ // if we really have the start, fine, otherwithe we can
+ // nothing do about it, nor yet, nor in future, thus
+ // we have even success (in terms of failure)
+ return true;
+}
+
+
+
+int MpegVideoLength::seekToEnd() {
+ int success;
+
+ if (lSysLayer == true) {
+ success=parseToPTS(endGOP);
+ } else {
+ mpegVideoStream->hasBytes(100);
+ success=parseToGOP(endGOP);
+ }
+ if (success == false) {
+ cout << "picture endcode not found [END]"<<endl;
+ }
+
+ return true;
+}
+
+
+int MpegVideoLength::parseToGOP(GOP* dest) {
+ int success;
+ // we try ten attempts
+ // and the diff between each must be (less) than one second
+
+ int successCnt=0;
+
+
+ long maxArea=0;
+ long area=0;
+
+ GOP lastGOP;
+ GOP currentGOP;
+ GOP diffGOP;
+
+
+ while(successCnt < 4) {
+ if (mpegVideoStream->eof()) {
+ return false;
+ }
+ if (input->eof() == true) {
+ cout << "abort"<<endl;
+ return false;
+ }
+ if (maxArea > SEARCH_SIZE) {
+ return false;
+ }
+
+
+ success=seekValue(GOP_START_CODE,area);
+ maxArea+=area;
+ if (success == false) {
+ continue;
+ }
+
+
+
+ currentGOP.copyTo(&lastGOP);
+ // currentGOP.print("current");
+ successCnt++;
+ currentGOP.processGOP(mpegVideoStream);
+ if (currentGOP.substract(&lastGOP,&diffGOP) == false) {
+ cout << "substract error"<<endl;
+ }
+ /*
+ currentGOP.print("current");
+ lastGOP.print("last");
+ diffGOP.print("diff");
+ */
+ if (diffGOP.getHour() != 0) {
+ successCnt=0;
+ continue;
+ }
+ if (diffGOP.getMinutes() != 0) {
+ successCnt=0;
+ continue;
+ }
+ if (diffGOP.getSeconds() > 8) {
+ successCnt=0;
+ continue;
+ }
+
+ }
+ currentGOP.copyTo(dest);
+ return true;
+}
+
+
+
+
+
+
+int MpegVideoLength::seekValue(unsigned int ,long& valueSeeked) {
+ long start=input->getBytePosition();
+ long cnt=0;
+ long end=start+SEEKWINDOW;
+ long area=0;
+
+ // if we have not enough space we skip
+ // because of the mpeg frame garbage "mb_stuffing,etc..."
+
+ if (end > upperEnd-SEEKWINDOW){
+ valueSeeked=SEEKWINDOW;
+ return false;
+ }
+ area=end-start;
+ while(mpegVideoStream->nextGOP() == false) {
+ if (mpegVideoStream->eof()) {
+ return false;
+ }
+ cnt++;
+ if (cnt >= area) {
+ valueSeeked=cnt;
+ cout << "nothing found"<<area<<endl;
+ return false;
+ }
+ }
+ return true;
+}
+
+
+
+int MpegVideoLength::parseToPTS(GOP* dest) {
+
+ // we try ten attempts
+ // and the diff between each must be (less) than one second
+
+ int successCnt=0;
+
+ long startArea=input->getBytePosition();
+ long maxArea=0;
+
+
+ double lastPTS=0;
+ double currentPTS=0;
+ double diffPTS=0;
+
+
+ while(successCnt < 4) {
+ if (input->eof() == true) {
+ cout << "abort"<<endl;
+ return false;
+ }
+ maxArea=input->getBytePosition()-startArea;
+ if (maxArea > SEARCH_SIZE) {
+ return false;
+ }
+ if (mpegSystemStream->nextPacket(mpegSystemHeader) == false) {
+ continue;
+ }
+ if (mpegSystemHeader->getPTSFlag()==false) {
+ continue;
+ }
+ // we have a packet
+ lastPTS=currentPTS;
+ currentPTS=mpegSystemHeader->getPTSTimeStamp();
+ diffPTS=currentPTS-lastPTS;
+ successCnt++;
+
+ if (diffPTS > 1.0) {
+ successCnt=0;
+ continue;
+ }
+ }
+
+ // now put it into the gop structure
+ // this is the interface for the time.
+ // a little hack here and there....
+ unsigned int hour=((long)currentPTS) / 3600;
+ currentPTS=currentPTS-hour*3600;
+ unsigned int minute=((long)currentPTS) / 60;
+ currentPTS=currentPTS-minute*60;
+ unsigned int seconds=((long)currentPTS);
+
+
+ dest->setHour(hour);
+ dest->setMinute(minute);
+ dest->setSecond(seconds);
+ return true;
+}
+
+
diff --git a/mpeglib/lib/mpegplay/mpegVideoLength.h b/mpeglib/lib/mpegplay/mpegVideoLength.h
new file mode 100644
index 00000000..79bece6a
--- /dev/null
+++ b/mpeglib/lib/mpegplay/mpegVideoLength.h
@@ -0,0 +1,93 @@
+/*
+ mpg I video/audio player plugin
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __MPEGVIDEOLENGTH_H
+#define __MPEGVIDEOLENGTH_H
+
+#include "../input/inputPlugin.h"
+
+
+
+
+
+
+/**
+ This class calculates the length (in seconds) of an mpeg stream.
+ I do not know how to make it right, my approach
+ seeks at the beginning of the stream for the timecode in
+ the picture gop then jumps near the end and does the same.
+ then it substracts the values.
+ It has some limitations, it does not seek higher than 600 MB,
+ because it seems the kernel has some problems with this.
+ (It resets the scsi system, and sometimes hang/crash)
+ Thus it only seek near 600 and assumes linear relation
+ for the rest. (Means: it calculates the length of the rest
+ with the sec/mb value calculates from the 600 mb
+ should be exact enough.
+*/
+
+class MpegVideoStream;
+class MpegVideoHeader;
+class MpegSystemStream;
+class MpegSystemHeader;
+class GOP;
+
+class MpegVideoLength {
+
+ // these are used fo non system streams
+ MpegVideoHeader* mpegVideoHeader;
+ MpegVideoStream* mpegVideoStream;
+
+ // these for system streams
+ MpegSystemHeader* mpegSystemHeader;
+ MpegSystemStream* mpegSystemStream;
+
+
+ InputStream* input;
+ GOP* startGOP;
+ GOP* endGOP;
+ GOP* lengthGOP;
+
+
+ int lHasStart;
+ int lHasEnd;
+ int lCanSeek;
+ int lHasStream;
+ int lHasSystemStream;
+ int lHasRawStream;
+ int lHasResync;
+ int lSysLayer;
+ long upperEnd;
+ long realLength;
+
+ public:
+ MpegVideoLength(InputStream* input);
+ ~MpegVideoLength();
+
+ int firstInitialize();
+
+ long getLength();
+ long getSeekPos(int seconds);
+
+
+ private:
+ int seekToStart();
+ int seekToEnd();
+ int seekValue(unsigned int code,long& endPos);
+ int parseToGOP(GOP* gop);
+ int parseToPTS(GOP* gop);
+
+
+};
+#endif
+
diff --git a/mpeglib/lib/mpegplay/mpegVideoStream.cpp b/mpeglib/lib/mpegplay/mpegVideoStream.cpp
new file mode 100644
index 00000000..5e16b116
--- /dev/null
+++ b/mpeglib/lib/mpegplay/mpegVideoStream.cpp
@@ -0,0 +1,224 @@
+/*
+ a mpegVideoStream buffer
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "mpegVideoStream.h"
+
+#include <iostream>
+
+using namespace std;
+
+MpegVideoStream::MpegVideoStream(InputStream* input) {
+
+
+ this->input=input;
+ lHasStream=false;
+
+ mpegSystemStream=new MpegSystemStream(input);
+ mpegSystemHeader=new MpegSystemHeader();
+ mpegVideoBitWindow= new MpegVideoBitWindow();
+
+
+}
+
+
+MpegVideoStream::~MpegVideoStream() {
+ delete mpegSystemStream;
+ delete mpegSystemHeader;
+ delete mpegVideoBitWindow;
+}
+
+
+
+
+int MpegVideoStream::firstInitialize(MpegVideoHeader* mpegHeader) {
+ if (lHasStream == false) {
+ if (mpegSystemStream->firstInitialize(mpegSystemHeader)==false) {
+ return false;
+ }
+ fill_videoBuffer(mpegSystemHeader);
+ lHasStream=true;
+ }
+ // now find SEQ_START_CODE
+ hasBytes(4);
+ mpegVideoBitWindow->flushByteOffset();
+
+ if (mpegSystemHeader->getLayer() == _PACKET_SYSLAYER) {
+ unsigned int startCode=showBits(32);
+ if (startCode != _SEQ_START_CODE) {
+ flushBits(8);
+ return false;
+ }
+ flushBits(32);
+ }
+ if (mpegHeader->parseSeq(this)==false) {
+ return false;
+ }
+ return true;
+}
+
+
+int MpegVideoStream::isStartCode(unsigned int data) {
+
+ switch(data) {
+ case SEQ_END_CODE:
+ case SEQ_START_CODE:
+ case GOP_START_CODE:
+ case PICTURE_START_CODE:
+ case SLICE_MIN_START_CODE:
+ case SLICE_MAX_START_CODE:
+ case EXT_START_CODE:
+ case USER_START_CODE:
+ case SEQUENCE_ERROR_CODE:
+ case SYSTEM_HEADER_START_CODE:
+ case ISO_11172_END_CODE:
+ case EOF:
+ return true;
+ }
+ if ((SLICE_MIN_START_CODE < data) && (data < SLICE_MAX_START_CODE)) {
+ return true;
+ }
+ return false;
+}
+
+int MpegVideoStream::next_start_code() {
+ flushByteOffset();
+
+ unsigned int data;
+ data=showBits(32);
+
+
+ while(eof()==false) {
+ data=showBits(32);
+ if (isStartCode(data)) {
+ return true;
+ }
+ flushBits(8);
+ }
+ return true;
+}
+
+
+int MpegVideoStream::nextGOP() {
+
+ mpegVideoBitWindow->flushByteOffset();
+ unsigned int data=showBits(32);
+ if (data != GOP_START_CODE) {
+ flushBits(8);
+ return false;
+ }
+ return true;
+}
+
+
+int MpegVideoStream::nextPIC() {
+ mpegVideoBitWindow->flushByteOffset();
+ unsigned int data=showBits(32);
+ if ( (data != PICTURE_START_CODE) &&
+ (data != GOP_START_CODE) &&
+ (data != SEQ_START_CODE) ) {
+ flushBits(8);
+ return false;
+ }
+ return true;
+}
+
+
+
+
+
+int MpegVideoStream::hasBytes(int byteCnt) {
+ if (mpegVideoBitWindow->getLength() < byteCnt) {
+ get_more_video_data();
+ if (mpegVideoBitWindow->getLength() < byteCnt) {
+ return hasBytes(byteCnt);
+ }
+ }
+ return true;
+}
+
+
+
+
+int MpegVideoStream::eof() {
+ if (input->eof()) {
+ return true;
+ }
+ return false;
+}
+
+
+int MpegVideoStream::getByteDirect() {
+ unsigned char byte;
+ if (input->read((char*)&byte,1) != 1) {
+ return -1;
+ }
+ return (int)byte;
+}
+
+
+int MpegVideoStream::get_more_video_data() {
+
+
+ while(1) {
+ if (mpegSystemStream->nextPacket(mpegSystemHeader)==false) {
+ continue;
+ }
+ if (mpegSystemStream->eof() == true) {
+ printf ("\n");
+ mpegVideoBitWindow->fillWithIsoEndCode(1024);
+ cout <<"Unexpected read error."<<endl;
+ return false;
+ }
+ if (mpegSystemHeader->getPacketID() == _PAKET_ID_VIDEO) {
+ break;
+ }
+
+ }
+
+
+ fill_videoBuffer(mpegSystemHeader);
+ return true;
+}
+
+
+void MpegVideoStream::fill_videoBuffer(MpegSystemHeader* mpegSystemHeader) {
+ int bytes;
+
+ bytes=mpegSystemHeader->getPacketLen();
+ unsigned char* packetBuffer= new unsigned char[bytes];
+ int didRead;
+ didRead=input->read((char*)packetBuffer,bytes);
+ if (bytes==0) {
+ mpegVideoBitWindow->fillWithIsoEndCode(1024);
+ return ;
+ }
+
+ mpegVideoBitWindow->appendToBuffer(packetBuffer,didRead);
+ if (input->eof()) {
+ mpegVideoBitWindow->fillWithIsoEndCode(bytes-didRead);
+ }
+
+ delete packetBuffer;
+}
+
+
+TimeStamp* MpegVideoStream::getCurrentTimeStamp() {
+ long pos=input->getBytePosition();
+ int transfered=4*mpegVideoBitWindow->getLength();
+ pos=pos-transfered;
+
+ TimeStamp* timeStamp=input->getTimeStamp(pos);
+
+ return timeStamp;
+}
+
diff --git a/mpeglib/lib/mpegplay/mpegVideoStream.h b/mpeglib/lib/mpegplay/mpegVideoStream.h
new file mode 100644
index 00000000..86b80553
--- /dev/null
+++ b/mpeglib/lib/mpegplay/mpegVideoStream.h
@@ -0,0 +1,110 @@
+/*
+ a buffer
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+
+#ifndef __MPEGVIDEOSTREAM_H
+#define __MPEGVIDEOSTREAM_H
+
+#include "startCodes.h"
+#include "mpegVideoHeader.h"
+#include "mpegSystemStream.h"
+#include "mpegVideoBitWindow.h"
+
+#define _BYTE_TEST 1024
+
+/**
+ A really ugly class. Most of the methods have names
+ which does not make it clear for what they are useful.
+ (Don't touch a running system :-)
+
+ We wrap the inputStream and offer functions for getting
+ bits, appending to the internal buffer, flushing, syncing
+ all this stuff.
+
+
+*/
+
+class MpegVideoStream {
+
+ int size;
+ InputStream* input;
+ MpegSystemStream* mpegSystemStream;
+ MpegSystemHeader* mpegSystemHeader;
+ MpegVideoBitWindow* mpegVideoBitWindow;
+
+ int lHasStream;
+
+ public:
+ MpegVideoStream(InputStream* input);
+ ~MpegVideoStream();
+
+ // returns true if init successful
+ int firstInitialize(MpegVideoHeader* mpegHeader);
+ int nextGOP();
+ int nextPIC();
+ int next_start_code();
+
+ int hasBytes(int bytes);
+ int eof();
+
+ inline void clear() {
+ mpegVideoBitWindow->clear();
+ }
+
+ inline void flushBits(int bits) {
+ hasBytes(_BYTE_TEST);
+ mpegVideoBitWindow->flushBitsDirect(bits);
+ }
+
+ inline unsigned int showBits(int bits) {
+ hasBytes(_BYTE_TEST);
+ return mpegVideoBitWindow->showBits(bits);
+ }
+
+ inline unsigned int showBits32() {
+ return mpegVideoBitWindow->showBits32();
+ }
+
+ inline unsigned int showBits16() {
+ return mpegVideoBitWindow->showBits(16);
+ }
+
+ inline void flushBitsDirect(unsigned int bits) {
+ mpegVideoBitWindow->flushBitsDirect(bits);
+ }
+
+ unsigned int getBits(int bits) {
+ hasBytes(_BYTE_TEST);
+ return mpegVideoBitWindow->getBits(bits);
+ }
+
+ inline void flushByteOffset() {
+ mpegVideoBitWindow->flushByteOffset();
+ }
+
+ TimeStamp* getCurrentTimeStamp();
+
+ private:
+ int getByteDirect();
+ int get_more_video_data();
+ void fill_videoBuffer(MpegSystemHeader* mpegSystemHeader);
+ int isStartCode(unsigned int data);
+
+
+};
+#endif
+
+
+
diff --git a/mpeglib/lib/mpegplay/pesSystemStream.cpp b/mpeglib/lib/mpegplay/pesSystemStream.cpp
new file mode 100644
index 00000000..10787f21
--- /dev/null
+++ b/mpeglib/lib/mpegplay/pesSystemStream.cpp
@@ -0,0 +1,498 @@
+/*
+ demux pes mpeg stream
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "pesSystemStream.h"
+
+#include <iostream>
+
+using namespace std;
+
+PESSystemStream::PESSystemStream(InputStream* input) {
+ this->input=input;
+}
+
+
+PESSystemStream::~PESSystemStream() {
+}
+
+
+
+int PESSystemStream::getByteDirect() {
+ unsigned char byte;
+ if (input->read((char*)&byte,1) != 1) {
+ return -1;
+ }
+ bytes_read++;
+ return (int)byte;
+}
+
+int PESSystemStream::read(char* pointer,int bytes) {
+ if (input->read(pointer,bytes) != bytes) {
+ return false;
+ }
+ bytes_read+=bytes;
+
+ return true;
+}
+
+int PESSystemStream::processStartCode(unsigned int startCode,
+ MpegSystemHeader* mpegHeader) {
+ int lok=true;
+ bytes_read=4; // startcode
+ mpegHeader->setPacketLen(0);
+ mpegHeader->setPacketID(_PAKET_ID_NUKE);
+
+ // handle default
+ bytes_read=processPacket(startCode,mpegHeader);
+ return bytes_read;
+}
+
+
+/* Returns:
+ 0 - no error, but not video packet we want
+ -1 - error
+ >0 - length of packet
+*/
+int PESSystemStream::processPacket(unsigned int startCode,
+ MpegSystemHeader* mpegHeader) {
+
+ int ioBytes;
+ unsigned short packetLength;
+ int packetDataLength;
+
+ /* Leftovers from previous video packets */
+
+ int packetID=startCode & 0xff;
+ mpegHeader->setPacketID(packetID);
+ int lPacket=startCode & _PACKET_START_CODE_MASK &_PACKET_START_CODE_PREFIX;
+ if ((lPacket == false) || (packetID < 0xbc)) {
+ //printf("unknown startcode,packet or packetID:%8x\n",startCode);
+ return false;
+ }
+
+ if (packetID == _NOT_PACKET_ID) {
+ cout << "(vid_stream->mpegVideoStream)->makeEnd()"<<endl;
+ } else if (packetID==_KILL_BUFFER) {
+ printf("packetID==_KILL_BUFFER\n");
+ }
+
+ if (read((char*)&packetLength, 2) == false) return false;
+ packetLength = htons(packetLength);
+
+ mpegHeader->setPTSFlag(false);
+ mpegHeader->setPacketID(packetID);
+ mpegHeader->setPESPacketLen(packetLength);
+ switch (packetID>>4) {
+ case _PAKET_ID_AUDIO_1>>4:
+ case _PAKET_ID_AUDIO_2>>4:
+ case _PAKET_ID_VIDEO>>4:
+ break;
+ default:
+ switch(packetID) {
+ case _PRIVATE_STREAM_1_ID:
+ break;
+ default:
+ switch (packetID) {
+ case _PRIVATE_STREAM_2_ID:
+ case _PADDING_STREAM_ID:
+ case _RESERVED_STREAM_ID:
+ case _ECM_STREAM_ID:
+ case _EMM_STREAM_ID:
+ case _PROGRAM_STREAM_DIRECTORY_ID:
+ case _DSMCC_STREAM_ID:
+ case _ITUTRECH222TYPEE_STREAM_ID:
+ return bytes_read;
+ }
+ printf("\nUnknown packet type. (%x) at %ld\n",
+ packetID,input->getBytePosition());
+ return bytes_read;
+ }
+ }
+ // this is only processed if audio or video found
+
+ if (mpegHeader->getMPEG2()==false) {
+ packetDataLength = packetLength-processPacketHeader(mpegHeader);
+ } else {
+ int len=processMPEG2PacketHeader(mpegHeader);
+
+ if (len < 0) {
+ return false;
+ }
+ packetDataLength = packetLength-len;
+
+ // now check in private stream for AC3
+ if ( packetID == _PRIVATE_STREAM_1_ID ) {
+ packetDataLength = packetDataLength-processPrivateHeader(mpegHeader);
+ }
+ }
+
+ if (packetDataLength <= 0) {
+ if (mpegHeader->hasPSHeader()) return false;
+ // -> buggy TS stream
+ packetDataLength=0;
+ }
+ mpegHeader->setPESPacketLen(packetDataLength);
+
+ return bytes_read;
+
+}
+
+
+int PESSystemStream::processPrivateHeader(MpegSystemHeader* mpegHeader) {
+ char nukeBuffer[30];
+ int pos=0;
+ int one=getByteDirect();
+ pos++;
+ mpegHeader->setSubStreamID(one);
+ switch(one>>4) {
+ case _SUBSTREAM_AC3_ID>>4:
+ if (read(nukeBuffer,3) == false) return false;
+ mpegHeader->addAvailableLayer(one);
+ cout << "addAvailableLayer:"<<one<<endl;
+ pos+=3;
+ break;
+ case _SUBSTREAM_LPCM_ID>>4:
+ if (read(nukeBuffer,6) == false) return false;
+ pos+=6;
+ break;
+ case _SUBSTREAM_SUBPIC_ID>>4:
+ if (read(nukeBuffer,3) == false) return false;
+ pos+=3;
+ break;
+ default:
+ printf("unknown sub id :%8x\n",one);
+ }
+ return pos;
+
+}
+
+
+int PESSystemStream::processMPEG2PacketHeader(MpegSystemHeader* mpegHeader){
+
+ int stdCnt=0;
+ int pos=0;
+
+ // 1. Byte
+ /*
+ FROM FLASK:
+ int getbits(2);
+ encrypted = getbits(2); // PES_scrambling_control
+ getbits(4);
+ //LIVID
+ u_char original_or_copy : 1;
+ u_char copyright : 1;
+ u_char data_alignment_indicator : 1;
+ u_char pes_priority : 1;
+ u_char pes_scrambling_control : 2;
+ u_char start_code_prefix : 2; // 0x02
+
+ */
+ int first=getByteDirect();
+ stdCnt++;
+ mpegHeader->setOriginalOrCopy(first&(128)>>7);
+ mpegHeader->setCopyRight(first&(64)>>6);
+ mpegHeader->setDataAlignmentIndicator(first&(32)>>5);
+ mpegHeader->setPesPriority(first&(16)>>4);
+ mpegHeader->setEncrypted((first&(8+4))>>2);
+ mpegHeader->setStartCodePrefix(first&(1+2));
+
+
+ // 2. Byte
+ /*
+ PTS_DTS_flags = getbits(2);
+ ESCR_flag = get1bit();
+ ES_rate_flag = get1bit();
+ DSM_trick_mode_flag = get1bit();
+ additional_copy_info_flag = get1bit();
+ PES_CRC_flag = get1bit();
+ PES_extension_flag = get1bit();
+ */
+ int second=getByteDirect();
+ stdCnt++;
+
+ mpegHeader->setPTSDTSFlag((second&(128+64))>>6);
+ mpegHeader->setESCRFlag((second&(32))>>5);
+ mpegHeader->setES_RATE_Flag((second%(16))>>4);
+ mpegHeader->setDMSTRICKFLAG((second&(8))>>3);
+ mpegHeader->setADDITIONAL_COPY_FLAG((second&(4))>>2);
+ mpegHeader->setPES_CRC_FLAG((second&(2))>>1);
+ mpegHeader->setPES_EXT_FLAG(second&(1));
+
+
+ // 3. Byte
+ /*
+ PES_header_data_length = getbits(8);
+ */
+ int third=getByteDirect();
+ stdCnt++;
+ mpegHeader->setPES_HEADER_DATA_LENGTH(third);
+
+
+ //
+ // PARSING MPEG 2 HEADER FLAGS [START]
+ //
+ unsigned char nukeBuffer[300];
+
+ int PTS_DTS_flags=mpegHeader->getPTSDTSFlag();
+ if (PTS_DTS_flags == 0) {
+ mpegHeader->setPTSFlag(false);
+ } else {
+ mpegHeader->setPTSFlag(true);
+ }
+
+ if (PTS_DTS_flags > 0x1) {
+ if (read((char*)nukeBuffer,5) == false) return false;
+ double pts=GET_MPEG2_PTS(nukeBuffer);
+ pts=(pts*300.0)/(double)MPEG2_CLK_REF;
+ mpegHeader->setPTSTimeStamp(pts);
+ pos+=5;
+ }
+ if (PTS_DTS_flags > 0x2) {
+ if (read((char*)nukeBuffer,5) == false) return false;
+ double dts=GET_MPEG2_PTS(nukeBuffer);
+ mpegHeader->setDTSTimeStamp((dts*300.0)/(double)MPEG2_CLK_REF);
+ pos+=5;
+ }
+
+ int ESCRFlag=mpegHeader->getESCRFlag();
+ if (ESCRFlag == 1){
+ cout << "ESCRFlag == 1"<<endl;
+ if (read((char*)nukeBuffer,6) == false) return false;
+ pos+=6;
+ }
+
+ int ES_rate_flag=mpegHeader->getES_RATE_Flag();
+ if (ES_rate_flag == 1){
+ cout << "ES_rate_flag == 1"<<endl;
+ if (read((char*)nukeBuffer,3) == false) return false;
+ pos+=3;
+ }
+
+ int DSM_trick_mode_flag=mpegHeader->getDMSTRICKFLAG();
+ if (DSM_trick_mode_flag == 1){
+ cout << "DSM_trick_mode_flag == 1"<<endl;
+ if (read((char*)nukeBuffer,1) == false) return false;
+ pos++;
+ }
+
+ int additional_copy_info_flag=mpegHeader->getADDITIONAL_COPY_FLAG();
+ if (additional_copy_info_flag == 1) {
+ cout << "additional_copy_info_flag == 1"<<endl;
+ if (read((char*)nukeBuffer,1) == false) return false;
+ pos++;
+ }
+
+ int PES_CRC_flag=mpegHeader->getPES_CRC_FLAG();
+ if (PES_CRC_flag == 1) {
+ cout << "PES_CRC_flag == 1"<<endl;
+ if (read((char*)nukeBuffer,2) == false) return false;
+ pos+=2;
+ }
+
+ //
+ // PES Extension [START]
+ //
+
+ int PES_extension_flag=mpegHeader->getPES_EXT_FLAG();
+ if (PES_extension_flag == 1) {
+ /*
+ FLASK:
+ PES_private_data_flag = get1bit();
+ pack_header_field_flag = get1bit();
+ program_packet_sequence_counter_flag = get1bit();
+ PSTD_buffer_flag = get1bit();
+ getbits(3);
+ PES_extension_flag_2 = get1bit();
+ */
+ int extensionByte=getByteDirect();
+
+ pos++;
+ mpegHeader->setPrivateDataFlag((extensionByte&(128))>>7);
+ mpegHeader->setPackHeaderFieldFlag((extensionByte&(64))>>6);
+ mpegHeader->setSequenceCounterFlag((extensionByte&(32))>>5);
+ mpegHeader->setSTDBufferFlag((extensionByte&(16))>>4);
+ mpegHeader->setPES_EXT_FLAG_2(extensionByte&(1));
+
+ int PES_private_data_flag=mpegHeader->getPrivateDataFlag();
+ if (PES_private_data_flag == 1) {
+ if (read((char*)nukeBuffer,128) == false) return false;
+ pos+=128;
+ }
+
+ int pack_header_field_flag=mpegHeader->getPackHeaderFieldFlag();
+ if (pack_header_field_flag == 1) {
+ printf("pack header field flag value not allowed in program streams\n");
+ return false;
+ }
+
+ int sequence_counter_flag=mpegHeader->getSequenceCounterFlag();
+ if (sequence_counter_flag==1) {
+ cout<<"sequence_counter_flag ==1"<<endl;
+ if (read((char*)nukeBuffer,2) == false) return false;
+ pos+=2;
+ }
+
+ int PSTD_buffer_flag=mpegHeader->getSTDBufferFlag();
+ if (PSTD_buffer_flag==1) {
+ if (read((char*)nukeBuffer,2) == false) return false;
+ pos+=2;
+ }
+
+ int PES_extension_flag_2=mpegHeader->getPES_EXT_FLAG_2();
+ if (PES_extension_flag_2 == 1) {
+ int extension2_byte=getByteDirect();
+ pos++;
+ mpegHeader->setPES_EXT_FIELD_LENGTH(extension2_byte&(254));
+
+
+ int PES_field_length=mpegHeader->getPES_EXT_FIELD_LENGTH();
+ int j;
+ for (j=0;j<PES_field_length ; j++) {
+ cout << "PES_field_length (nuke)"<<endl;
+ getByteDirect();
+ pos++;
+ }
+ }
+ }
+ //
+ // PES Extension [END]
+ //
+
+ // now nuke remaining bytes from PES DATA Length
+ int PES_HEADER_DATA_LENGTH=mpegHeader->getPES_HEADER_DATA_LENGTH();
+ int tmp=PES_HEADER_DATA_LENGTH-pos;
+ if (tmp>0) {
+ if (read((char*)nukeBuffer,tmp) == false) return false;
+ pos+=tmp;
+ }
+
+
+
+
+
+ //
+ // PARSING MPEG 2 HEADER FLAGS [START]
+ //
+
+ int parsed=stdCnt+pos;
+ return parsed;
+
+}
+
+
+int PESSystemStream::processPacketHeader(MpegSystemHeader* mpegHeader) {
+ unsigned char nextByte;
+ int pos;
+ int val;
+ unsigned char scratch[10];
+
+
+ nextByte=getByteDirect();
+
+ mpegHeader->setPTSFlag(false);
+
+ pos = 1;
+ while (nextByte & 0x80) {
+ ++pos;
+ val=getByteDirect();
+ if (val == -1) return false;
+ scratch[0]=val;
+
+ nextByte=scratch[0];
+ }
+ if ((nextByte >> 6) == 0x01) {
+ pos += 2;
+ scratch[1]=getByteDirect();
+ scratch[2]=getByteDirect();
+ nextByte=scratch[2];
+ }
+ if ((nextByte >> 4) == 0x02) {
+ scratch[0] = nextByte;
+ if (read((char*)&scratch[1],4) == false) return false;
+ /* presentation time stamps */
+ unsigned char hiBit;
+ unsigned long low4Bytes;
+ double ptsTimeStamp;
+ double dtsTimeStamp=0.0;
+ readTimeStamp((unsigned char*)scratch,&hiBit,&low4Bytes);
+ makeClockTime(hiBit,low4Bytes,&ptsTimeStamp);
+ mpegHeader->setPTSFlag(true);
+ mpegHeader->setPTSTimeStamp(ptsTimeStamp);
+ mpegHeader->setDTSTimeStamp(dtsTimeStamp);
+
+ pos += 4;
+ }
+ else if ((nextByte >> 4) == 0x03) {
+ scratch[0] = nextByte;
+ if (read((char*)&scratch[1],9) == false) return false;
+ /* presentation and decoding time stamps */
+ unsigned char hiBit;
+ unsigned long low4Bytes;
+ double ptsTimeStamp;
+ double dtsTimeStamp;
+ readTimeStamp((unsigned char*)scratch,&hiBit,&low4Bytes);
+ makeClockTime(hiBit,low4Bytes,&ptsTimeStamp);
+
+ readTimeStamp((unsigned char*)&(scratch[5]),&hiBit,&low4Bytes);
+ makeClockTime(hiBit,low4Bytes,&dtsTimeStamp);
+ mpegHeader->setPTSFlag(true);
+ mpegHeader->setPTSTimeStamp(ptsTimeStamp);
+ mpegHeader->setDTSTimeStamp(dtsTimeStamp);
+
+ pos += 9;
+ }
+ return pos;
+}
+
+
+
+void PESSystemStream::readTimeStamp(unsigned char* inputBuffer,
+ unsigned char* hiBit,
+ unsigned long* low4Bytes) {
+ *hiBit = ((unsigned long)inputBuffer[0] >> 3) & 0x01;
+ *low4Bytes = (((unsigned long)inputBuffer[0] >> 1) & 0x03) << 30;
+ *low4Bytes |= (unsigned long)inputBuffer[1] << 22;
+ *low4Bytes |= ((unsigned long)inputBuffer[2] >> 1) << 15;
+ *low4Bytes |= (unsigned long)inputBuffer[3] << 7;
+ *low4Bytes |= ((unsigned long)inputBuffer[4]) >> 1;
+}
+
+
+void PESSystemStream::readSTD(unsigned char* inputBuffer,
+ MpegSystemHeader* mpegHeader) {
+ int stdBufferScale;
+ unsigned long stdBufferSize;
+ stdBufferScale = ((int)(inputBuffer[0] & 0x20) >> 5);
+ stdBufferSize = ((unsigned long)inputBuffer[0] & 0x1f) << 8;
+ stdBufferSize |= (unsigned long)inputBuffer[1];
+ mpegHeader->setStdBufferScale(stdBufferScale);
+ mpegHeader->setStdBufferSize(stdBufferSize);
+}
+
+
+int PESSystemStream::makeClockTime(unsigned char hiBit,
+ unsigned long low4Bytes,
+ double * clockTime) {
+ if (hiBit != 0 && hiBit != 1) {
+ *clockTime = 0.0;
+ return 1;
+ }
+ *clockTime = (double)hiBit*FLOAT_0x10000*FLOAT_0x10000 + (double)low4Bytes;
+ *clockTime /= (double)_STD_SYSTEM_CLOCK_FREQ;
+ return 0;
+}
+
+
+
+
diff --git a/mpeglib/lib/mpegplay/pesSystemStream.h b/mpeglib/lib/mpegplay/pesSystemStream.h
new file mode 100644
index 00000000..f7b650c5
--- /dev/null
+++ b/mpeglib/lib/mpegplay/pesSystemStream.h
@@ -0,0 +1,57 @@
+/*
+ demux pes mpeg stream
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __PESSYSTEMSTREAM_H
+#define __PESSYSTEMSTREAM_H
+
+
+#include <stdlib.h>
+#include "../input/inputPlugin.h"
+#include "mpegSystemHeader.h"
+
+
+class PESSystemStream {
+
+
+ InputStream* input;
+
+ int bytes_read;
+
+ public:
+ PESSystemStream(InputStream* input);
+ ~PESSystemStream();
+
+ // called when we found a valid ts startcode
+ // return number of bytes read from input and zero on error
+ int processStartCode(unsigned int startCode,MpegSystemHeader* mpegHeader);
+
+ private:
+ int getByteDirect();
+ int read(char* prt,int bytes);
+ int processPacket(unsigned int startCode,MpegSystemHeader* mpegHeader);
+ int processPacketHeader(MpegSystemHeader* mpegHeader);
+ int processMPEG2PacketHeader(MpegSystemHeader* mpegHeader);
+ int processPrivateHeader(MpegSystemHeader* mpegHeader);
+
+ int readStartCode(unsigned int startCode,MpegSystemHeader* mpegHeader);
+
+ void readSTD(unsigned char* inputBuffer,MpegSystemHeader* mpegHeader);
+ void readTimeStamp(unsigned char* inputBuffer,unsigned char* hiBit,
+ unsigned long* low4Bytes);
+
+ int makeClockTime(unsigned char hiBit, unsigned long low4Bytes,
+ double * clockTime);
+
+
+};
+#endif
diff --git a/mpeglib/lib/mpegplay/picture.cpp b/mpeglib/lib/mpegplay/picture.cpp
new file mode 100644
index 00000000..6d8c1efe
--- /dev/null
+++ b/mpeglib/lib/mpegplay/picture.cpp
@@ -0,0 +1,149 @@
+/*
+ mpeg video picture
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "picture.h"
+
+
+Picture::Picture() {
+
+ /* Initialize pointers to extension and user data. */
+ extraBit=0;
+ startOfPicStamp=new TimeStamp();
+ extension=new MpegExtension();
+}
+
+
+
+Picture::~Picture() {
+ delete startOfPicStamp;
+ delete extension;
+}
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ParsePicture --
+ *
+ * Parses picture header. Marks picture to be presented
+ * at particular time given a time stamp.
+ *
+ * Results:
+ * Values from picture header put into video stream structure.
+ *
+ * Side effects:
+ * Bit stream irreversibly parsed.
+ *
+ *--------------------------------------------------------------
+ */
+int Picture::processPicture(MpegVideoStream* mpegVideoStream) {
+ TimeStamp* stamp;
+
+ /* Flush header start code. */
+ mpegVideoStream->flushBits(32);
+
+ /* Parse off temporal reference. */
+ temp_ref=mpegVideoStream->getBits(10);
+
+ /* Parse of picture type. */
+ code_type=mpegVideoStream->getBits(3);
+
+
+ // get timestamp from stamp queue
+ stamp=mpegVideoStream->getCurrentTimeStamp();
+
+ stamp->copyTo(startOfPicStamp);
+ // now invalidate the PTSFlag
+ stamp->setPTSFlag(false);
+
+
+ /* Parse off vbv buffer delay value. */
+ vbv_delay=mpegVideoStream->getBits(16);
+
+
+ /* If P or B type frame... */
+
+ if ((code_type == P_TYPE) || (code_type == B_TYPE)) {
+
+ /* Parse off forward vector full pixel flag. sets it to true/false*/
+ full_pel_forw_vector=mpegVideoStream->getBits(1);
+
+
+ /* Parse of forw_r_code. */
+ /* Decode forw_r_code into forw_r_size and forw_f. */
+ forw_r_size=mpegVideoStream->getBits(3);
+ forw_r_size--;
+
+
+ forw_f = (1 << forw_r_size);
+ }
+ /* If B type frame... */
+
+ if (code_type == B_TYPE) {
+
+ /* Parse off back vector full pixel flag. */
+ full_pel_back_vector=mpegVideoStream->getBits(1);
+
+
+ /* Parse off back_r_code. */
+ /* Decode back_r_code into back_r_size and back_f. */
+ back_r_size=mpegVideoStream->getBits(3);
+ back_r_size--;
+
+
+ back_f = (1 << back_r_size);
+ }
+ /* Get extra bit picture info. */
+
+
+ /*
+ extraBit=mpegVideoStream->getBits(1);
+ if (extraBit) {
+ cout << "extraBit"<<endl;
+ exit(0);
+ extension->processExtBuffer(mpegVideoStream);
+ }
+ */
+ extension->processExtra_bit_info(mpegVideoStream);
+ extension->processExtensionData(mpegVideoStream);
+
+
+
+
+
+ return true;
+}
+
+int Picture::processPictureCodingExtension(MpegVideoStream* ) {
+ return true;
+}
+
+unsigned int Picture::geth_back_r(MpegVideoStream* mpegVideoStream) {
+ return mpegVideoStream->getBits(back_r_size);
+}
+
+
+unsigned int Picture::getv_back_r(MpegVideoStream* mpegVideoStream) {
+ return mpegVideoStream->getBits(back_r_size);
+}
+
+
+unsigned int Picture::geth_forw_r(MpegVideoStream* mpegVideoStream) {
+ return mpegVideoStream->getBits(forw_r_size);
+}
+
+
+unsigned int Picture::getv_forw_r(MpegVideoStream* mpegVideoStream) {
+ return mpegVideoStream->getBits(forw_r_size);
+}
+
diff --git a/mpeglib/lib/mpegplay/picture.h b/mpeglib/lib/mpegplay/picture.h
new file mode 100644
index 00000000..7be35ab7
--- /dev/null
+++ b/mpeglib/lib/mpegplay/picture.h
@@ -0,0 +1,82 @@
+/*
+ mpeg video picture
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+
+#ifndef __PICTURE_H
+#define __PICTURE_H
+
+
+#include "mpegExtension.h"
+
+
+class Picture {
+
+/* Picture structure. */
+
+ unsigned int temp_ref; /* Temporal reference. */
+ unsigned int code_type; /* Frame type: P, B, I */
+ unsigned int vbv_delay; /* Buffer delay. */
+ int full_pel_forw_vector; /* Forw. vectors specified in full
+ pixel values flag. */
+ unsigned int forw_r_size; /* Used for vector decoding. */
+ unsigned int forw_f; /* Used for vector decoding. */
+ int full_pel_back_vector; /* Back vectors specified in full
+ pixel values flag. */
+ unsigned int back_r_size; /* Used in decoding. */
+ unsigned int back_f; /* Used in decoding. */
+
+
+ // MPEG 2 [START]
+
+
+ MpegExtension* extension;
+ // sync info
+ class TimeStamp* startOfPicStamp;
+
+ // MPEG 2 things
+ int extraBit;
+
+ public:
+ Picture();
+ ~Picture();
+
+ int processPicture(class MpegVideoStream* mpegVideoStream);
+ int processPictureCodingExtension(class MpegVideoStream* mpegVideoStream);
+
+
+ inline unsigned int getCodeType() { return code_type; }
+ inline unsigned int getForw_f() { return forw_f;}
+ inline unsigned int getBack_f() { return back_f;}
+ inline void setForw_f(unsigned int f) { forw_f=f;}
+ inline void setBack_f(unsigned int f) { back_f=f;}
+
+ inline int getExtraBit() { return extraBit; }
+
+ inline TimeStamp* getStartOfPicStamp() { return startOfPicStamp;}
+ inline unsigned int getFull_pel_forw_vector() {return full_pel_forw_vector;}
+ inline unsigned int getFull_pel_back_vector() {return full_pel_back_vector;}
+
+ inline void setFull_pel_forw_vector(unsigned int v) {full_pel_forw_vector=v;}
+ inline void setFull_pel_back_vector(unsigned int v) {full_pel_back_vector=v;}
+
+ unsigned int geth_back_r(class MpegVideoStream* mpegVideoStream);
+ unsigned int getv_back_r(class MpegVideoStream* mpegVideoStream);
+
+ unsigned int geth_forw_r(class MpegVideoStream* mpegVideoStream);
+ unsigned int getv_forw_r(class MpegVideoStream* mpegVideoStream);
+
+
+};
+#endif
diff --git a/mpeglib/lib/mpegplay/proto.h b/mpeglib/lib/mpegplay/proto.h
new file mode 100644
index 00000000..b9c35c2d
--- /dev/null
+++ b/mpeglib/lib/mpegplay/proto.h
@@ -0,0 +1,36 @@
+
+
+#ifndef __PROTO_H
+#define __PROTO_H
+
+
+
+
+#include "videoDecoder.h"
+#include "mpegVideoHeader.h"
+
+
+
+/* decoders.c */
+void init_tables (void);
+extern "C" void Fast16Dither(unsigned char *lum,
+ unsigned char *cr,
+ unsigned char *cb,
+ unsigned char *out,
+ int rows,
+ int cols,
+ int mod);
+
+
+/* floatdct.c */
+void init_float_idct (void);
+void float_idct (short* block);
+extern "C" void IDCT_mmx(short *);
+
+
+
+
+
+
+#endif
+
diff --git a/mpeglib/lib/mpegplay/psSystemStream.cpp b/mpeglib/lib/mpegplay/psSystemStream.cpp
new file mode 100644
index 00000000..b63843bc
--- /dev/null
+++ b/mpeglib/lib/mpegplay/psSystemStream.cpp
@@ -0,0 +1,163 @@
+/*
+ demux "normal" mpeg stream (does this have a name?)
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "psSystemStream.h"
+
+#include <iostream>
+
+using namespace std;
+
+PSSystemStream::PSSystemStream(InputStream* input) {
+ this->input=input;
+}
+
+
+PSSystemStream::~PSSystemStream() {
+}
+
+
+int PSSystemStream::read(char* ptr,int bytes) {
+ if (input->read(ptr,bytes) != bytes) {
+ return false;
+ }
+ paket_read+=bytes;
+
+ return true;
+}
+
+int PSSystemStream::getByteDirect() {
+ unsigned char byte;
+ if (input->read((char*)&byte,1) != 1) {
+ return -1;
+ }
+ paket_read++;
+ return (int)byte;
+}
+
+
+// nuke bytes modulo 10
+int PSSystemStream::nukeBytes(int bytes) {
+ // nukebuffer
+ char nuke[10];
+
+ while(bytes > 0) {
+ int doNuke=10;
+ if (bytes < 10) doNuke=bytes;
+ if (input->read((char*)&nuke,doNuke) != doNuke) {
+ return false;
+ }
+ bytes-=doNuke;
+ paket_read+=doNuke;
+ }
+ return true;
+}
+
+
+int PSSystemStream::processStartCode(MpegSystemHeader* mpegHeader) {
+ unsigned int header=mpegHeader->getHeader();
+ mpegHeader->setPacketLen(0);
+ mpegHeader->setPacketID(_PAKET_ID_NUKE);
+
+ switch(header) {
+ case _PACK_START_CODE:
+ return processPackHeader(mpegHeader);
+ case _SYSTEM_HEADER_START_CODE:
+ return processSystemHeader(mpegHeader);
+ default:
+ cout << "PSSystemStream::processStartCode unknown PS header"<<endl;
+ exit(-1);
+ }
+ // never
+ return false;
+}
+
+
+int PSSystemStream::processPackHeader(MpegSystemHeader* mpegHeader) {
+ int back=false;
+ int numRead;
+ unsigned long scrbase;
+ unsigned long scrext;
+ unsigned long rate;
+
+ double timeStamp;
+
+ unsigned char inputBuffer[_PACKET_HEADER_SIZE+2];
+ if (read((char*)inputBuffer, _PACKET_HEADER_SIZE) == false) return false;
+
+
+ // only set this if we do an initialize
+ int layer=mpegHeader->getLayer();
+ if (layer == _PACKET_UNKNOWN_LAYER) {
+ if ((inputBuffer[0]>>6)==1) {
+ mpegHeader->setMPEG2(true);
+ }
+ }
+
+ if (mpegHeader->getMPEG2()) {
+ if (read((char*)inputBuffer+_PACKET_HEADER_SIZE, 2) == false) return false;
+
+ scrbase=GET_SCRBASE(inputBuffer);
+ scrext=GET_SCREXT(inputBuffer);
+
+ // BUGGY:
+ timeStamp = (double)(scrbase*300 + scrext) / (double)MPEG2_CLK_REF;
+
+ rate=GET_MPEG2MUXRATE(inputBuffer);
+
+ int stuffing = GET_MPEG2STUFFING(inputBuffer); // always <= 7
+
+ numRead=stuffing;
+ if (stuffing) {
+ if (read((char*)inputBuffer, stuffing) == false) return false;
+ }
+ } else {
+
+ // MPEG 1
+ scrbase=GET_SCR(inputBuffer);
+ timeStamp=(double)scrbase/MPEG1_CLK_REF;
+ rate=GET_MPEG1_MUXRATE(inputBuffer);
+ rate*=_MUX_RATE_SCALE_FACTOR;
+ }
+ mpegHeader->setSCRTimeStamp(timeStamp);
+ mpegHeader->setRate(rate);
+
+
+ return true;
+}
+
+
+int PSSystemStream::processSystemHeader(MpegSystemHeader* mpegHeader) {
+ unsigned char *inputBuffer = NULL;
+ int numRead;
+ unsigned short headerSize;
+
+
+ if (read((char *)&headerSize, 2) == false) return false;
+ headerSize = ntohs(headerSize);
+
+ inputBuffer = (unsigned char*) malloc (sizeof(unsigned char)*(headerSize+1));
+ inputBuffer[headerSize]=0;
+ if (read((char*)inputBuffer, headerSize) == false) return false;
+
+ mpegHeader->resetAvailableLayers();
+ int i = 6;
+ while (i<headerSize) {
+ if (inputBuffer[i] & 0x80) {
+ mpegHeader->addAvailableLayer(inputBuffer[i]);
+ }
+ i += 3;
+ }
+ free(inputBuffer);
+ return true;
+}
+
diff --git a/mpeglib/lib/mpegplay/psSystemStream.h b/mpeglib/lib/mpegplay/psSystemStream.h
new file mode 100644
index 00000000..bb3b23fd
--- /dev/null
+++ b/mpeglib/lib/mpegplay/psSystemStream.h
@@ -0,0 +1,57 @@
+/*
+ demux "normal" mpeg stream (does this have a name?)
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __PSSYSTEMSTREAM_H
+#define __PSSYSTEMSTREAM_H
+
+
+#include <stdlib.h>
+#include "../input/inputPlugin.h"
+#include "mpegSystemHeader.h"
+
+/**
+ This class is used inside mpegSystemStream.h when we found
+ during initialisation an "normal" mpeg stream.
+ This means: 1ba,1bb startcodes.
+
+
+*/
+
+class PSSystemStream {
+
+ InputStream* input;
+
+ int paket_len;
+ int paket_read;
+
+ public:
+ PSSystemStream(InputStream* input);
+ ~PSSystemStream();
+
+ // called when we found a valid ts startcode
+ int processStartCode(MpegSystemHeader* mpegHeader);
+
+ private:
+
+ // read stream methods
+ int read(char* ptr,int bytes);
+ int getByteDirect();
+ int nukeBytes(int bytes);
+ int skipNextByteInLength();
+
+ int processPackHeader(MpegSystemHeader* mpegHeader);
+ int processSystemHeader(MpegSystemHeader* mpegHeader);
+
+
+};
+#endif
diff --git a/mpeglib/lib/mpegplay/recon.cpp b/mpeglib/lib/mpegplay/recon.cpp
new file mode 100644
index 00000000..6116157a
--- /dev/null
+++ b/mpeglib/lib/mpegplay/recon.cpp
@@ -0,0 +1,735 @@
+/*
+ class for reconstruction
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "recon.h"
+
+
+#define DEBUG_RECON(x)
+//#define DEBUG_RECON(x) x
+
+
+
+Recon::Recon() {
+ copyFunctions=new CopyFunctions();
+}
+
+
+Recon::~Recon() {
+ delete copyFunctions;
+}
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ReconIMBlock --
+ *
+ * Reconstructs intra coded macroblock.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+int Recon::ReconIMBlock(int bnum,int mb_row,int mb_col,int row_size,
+ short int* dct_start,PictureArray* pictureArray) {
+ int row, col;
+ unsigned char *dest;
+ unsigned char *picDest;
+ int lumLength=(pictureArray->getCurrent())->getLumLength();
+ int colorLength=(pictureArray->getCurrent())->getColorLength();
+ int endDest=0;
+
+
+
+ /* If block is luminance block... */
+
+ if (bnum < 4) {
+
+ /* Calculate row and col values for upper left pixel of block. */
+
+ row = mb_row << 4;
+ col = mb_col << 4;
+ if (bnum > 1)
+ row += 8;
+ if (bnum % 2)
+ col += 8;
+
+ /* Set dest to luminance plane of current pict image. */
+
+ picDest = (pictureArray->getCurrent())->getLuminancePtr();
+ endDest=lumLength;
+
+ }
+ /* Otherwise if block is Cr block... */
+ /* Cr first because of the earlier mixup */
+
+ else if (bnum == 5) {
+
+ /* Set dest to Cr plane of current pict image. */
+
+ picDest = (pictureArray->getCurrent())->getCrPtr();
+ endDest=colorLength;
+
+ /* Establish row size. yuv color is half of whole size*/
+ row_size >>=1;
+
+ /* Calculate row,col for upper left pixel of block. */
+
+ row = mb_row << 3;
+ col = mb_col << 3;
+ }
+ /* Otherwise block is Cb block, and ... */
+
+ else {
+
+ /* Set dest to Cb plane of current pict image. */
+
+ picDest = (pictureArray->getCurrent())->getCbPtr();
+ endDest=colorLength;
+
+
+ /* Establish row size. yuv color is half of whole size*/
+
+ row_size /=2;
+
+ /* Calculate row,col for upper left pixel value of block. */
+
+ row = mb_row << 3;
+ col = mb_col << 3;
+ }
+
+
+ /*
+ * For each pixel in block, set to cropped reconstructed value from inverse
+ * dct.
+ */
+
+ dest = picDest+row * row_size + col;
+
+
+ if ((dest+7*row_size+7 >= picDest+endDest) || (dest < picDest)) {
+ DEBUG_RECON(cout << "urg! last resort caught before sigsev -4"<<endl;)
+ return false;
+ }
+
+
+ copyFunctions->copy8_src1linear_crop(dct_start,dest,row_size);
+
+
+ return true;
+}
+
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ReconPMBlock --
+ *
+ * Reconstructs forward predicted macroblocks.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+int Recon::ReconPMBlock(int bnum,
+ int recon_right_for,
+ int recon_down_for,
+ int zflag,
+ int mb_row,int mb_col,
+ int row_size,short int* dct_start,
+ PictureArray* pictureArray,int codeType) {
+ int row, col, rr;
+ unsigned char *picDest, *past;
+ unsigned char *rindex1, *rindex2, *rindex3, *rindex4;
+ unsigned char *index;
+ int lumLength=(pictureArray->getCurrent())->getLumLength();
+ int colorLength=(pictureArray->getCurrent())->getColorLength();
+ int endPast=0;
+ int endDest=0;
+
+
+ int right_for;
+ int down_for;
+ int right_half_for;
+ int down_half_for;
+
+
+
+ picDest=0;
+
+ if (bnum < 4) {
+
+
+
+ /* Set dest to luminance plane of current pict image. */
+
+ picDest = (pictureArray->getCurrent())->getLuminancePtr();
+ endDest=lumLength;
+
+ if (codeType == B_TYPE) {
+ past = (pictureArray->getPast())->getLuminancePtr();
+ } else {
+
+ /* Set predictive frame to current future frame. */
+
+ past = (pictureArray->getFuture())->getLuminancePtr();
+ }
+ endPast=lumLength;
+
+
+ /* Calculate row,col of upper left pixel in block. */
+
+ row = mb_row << 4;
+ col = mb_col << 4;
+ if (bnum > 1)
+ row += 8;
+ if (bnum % 2)
+ col += 8;
+
+ /* Otherwise, block is NOT luminance block, ... */
+
+ } else {
+
+ /* Construct motion vectors. */
+
+ recon_right_for >>= 1;
+ recon_down_for >>= 1;
+
+ /* Establish row size. yuv color is half of whole size*/
+
+ row_size /= 2;
+
+
+ /* Calculate row,col of upper left pixel in block. */
+
+ row = mb_row << 3;
+ col = mb_col << 3;
+
+ /* If block is Cr block... */
+ /* 5 first because order was mixed up in earlier versions */
+
+ if (bnum == 5) {
+
+ /* Set dest to Cr plane of current pict image. */
+
+ picDest = (pictureArray->getCurrent())->getCrPtr();
+
+ if (codeType == B_TYPE) {
+
+ past = (pictureArray->getPast())->getCrPtr();
+ } else {
+ past = (pictureArray->getFuture())->getCrPtr();
+ }
+ }
+ /* Otherwise, block is Cb block... */
+
+ else {
+
+ /* Set dest to Cb plane of current pict image. */
+
+ picDest = (pictureArray->getCurrent())->getCbPtr();
+
+ if (codeType == B_TYPE) {
+ past = (pictureArray->getPast())->getCbPtr();
+ } else {
+ past = (pictureArray->getFuture())->getCbPtr();
+ }
+ }
+ endPast=colorLength;
+ endDest=colorLength;
+
+ }
+
+ /* Calculate right_back, down_back motion vectors. */
+ right_for = recon_right_for >> 1;
+ down_for = recon_down_for >> 1;
+ right_half_for = recon_right_for & 0x1;
+ down_half_for = recon_down_for & 0x1;
+
+
+ /* For each pixel in block... */
+
+ index = picDest + (row * row_size) + col;
+ rindex1 = past + (row + down_for) * row_size + col + right_for;
+
+
+
+
+ if ((rindex1+7*row_size+7 >= past+endPast) || (rindex1 < past)) {
+ DEBUG_RECON(cout << "urg! last resort caught before sigsev -1"<<endl;)
+ return false;
+ }
+ if ((index+7*row_size+7 >= picDest+endDest) || (index < picDest)) {
+ DEBUG_RECON(cout << "urg! last resort caught before sigsev -2"<<endl;)
+ return false;
+ }
+
+
+ /*
+ * Calculate predictive pixel value based on motion vectors and copy to
+ * dest plane.
+ */
+
+ if ((!down_half_for) && (!right_half_for)) {
+
+ if (!zflag) {
+ copyFunctions->copy8_src2linear_crop(rindex1,dct_start,index,row_size);
+ } else {
+ if (right_for & 0x1) {
+
+ /* No alignment, used byte copy */
+ copyFunctions->copy8_byte(rindex1,index,row_size);
+
+
+ } else if (right_for & 0x2) {
+ /* Half-word bit aligned, use 16 bit copy */
+ unsigned short *src = (unsigned short *)rindex1;
+ unsigned short *dest = (unsigned short *)index;
+ row_size >>= 1;
+ copyFunctions->copy8_word(src,dest,row_size);
+
+ } else {
+ /* Word aligned, use 32 bit copy */
+ int *src = (int*) rindex1;
+ int *dest = (int*) index;
+
+ row_size >>= 2;
+
+
+ for (rr = 0; rr < 8; rr++) {
+ dest[0] = src[0];
+ dest[1] = src[1];
+ dest += row_size;
+ src += row_size;
+ }
+
+ }
+ }
+ } else {
+ rindex2 = rindex1 + right_half_for + (down_half_for * row_size);
+
+ /* if one of the two is zero, then quality makes no difference */
+ if ((!right_half_for) || (!down_half_for) || (!qualityFlag)) {
+
+ if (!zflag) {
+ // was +1
+ copyFunctions->copy8_div2_src3linear_crop(rindex1,rindex2,dct_start,
+ index,row_size);
+ } else { /* zflag */
+ // was +1
+ copyFunctions->copy8_div2_nocrop(rindex1,rindex2,index,row_size);
+ }
+ } else { /* qualityFlag on and both vectors are non-zero */
+ rindex3 = rindex1 + right_half_for;
+ rindex4 = rindex1 + (down_half_for * row_size);
+ if (!zflag) {
+ copyFunctions->copy8_div4_src5linear_crop(rindex1,rindex2,rindex3,
+ rindex4,dct_start,
+ index,row_size);
+ } else { /* zflag */
+ copyFunctions->copy8_div4_nocrop(rindex1,rindex2,rindex3,rindex4,
+ index,row_size);
+ }
+ }
+
+ }
+
+ return true;
+}
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ReconBMBlock --
+ *
+ * Reconstructs back predicted macroblocks.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+int Recon::ReconBMBlock(int bnum,
+ int recon_right_back,
+ int recon_down_back,
+ int zflag,
+ int mb_row,int mb_col,
+ int row_size,short int* dct_start,
+ PictureArray* pictureArray) {
+ int row, col, rr;
+ unsigned char *dest, *future;
+ int right_back, down_back, right_half_back, down_half_back;
+ unsigned char *rindex1, *rindex2, *rindex3, *rindex4;
+ unsigned char *index;
+ int lumLength=(pictureArray->getCurrent())->getLumLength();
+ int colorLength=(pictureArray->getCurrent())->getColorLength();
+
+ int endFuture=0;
+ int endDest=0;
+
+
+ /* If block is luminance block... */
+
+ if (bnum < 4) {
+
+
+ /* Set dest to luminance plane of current pict image. */
+
+ dest = (pictureArray->getCurrent())->getLuminancePtr();
+ endDest=lumLength;
+ /*
+ * If future frame exists, set future to luminance plane of future frame.
+ */
+
+ future = (pictureArray->getFuture())->getLuminancePtr();
+ endFuture=lumLength;
+
+ /* Calculate row,col of upper left pixel in block. */
+
+ row = mb_row << 4;
+ col = mb_col << 4;
+ if (bnum > 1)
+ row += 8;
+ if (bnum % 2)
+ col += 8;
+
+
+ }
+ /* Otherwise, block is NOT luminance block, ... */
+
+ else {
+
+ /* Construct motion vectors. */
+
+ recon_right_back >>= 1;
+ recon_down_back >>= 1;
+
+ /* Establish row size. yuv color is half of whole size*/
+
+ row_size >>= 1;
+
+ /* Calculate row,col of upper left pixel in block. */
+
+ row = mb_row << 3;
+ col = mb_col << 3;
+
+
+ /* If block is Cr block... */
+ /* They were switched earlier, so 5 is first - eyhung */
+
+ if (bnum == 5) {
+
+ /* Set dest to Cr plane of current pict image. */
+
+ dest = (pictureArray->getCurrent())->getCrPtr();
+
+ /*
+ * If future frame exists, set future to Cr plane of future image.
+ */
+
+ future = (pictureArray->getFuture())->getCrPtr();
+ }
+ /* Otherwise, block is Cb block... */
+
+ else {
+
+ /* Set dest to Cb plane of current pict image. */
+
+ dest = (pictureArray->getCurrent())->getCbPtr();
+
+ /*
+ * If future frame exists, set future to Cb plane of future frame.
+ */
+
+ future = (pictureArray->getFuture())->getCbPtr();
+ }
+ endDest=colorLength;
+ endFuture=colorLength;
+ }
+
+ /* Calculate right_back, down_back motion vectors. */
+ right_back = recon_right_back >> 1;
+ down_back = recon_down_back >> 1;
+ right_half_back = recon_right_back & 0x1;
+ down_half_back = recon_down_back & 0x1;
+
+ /* For each pixel in block do... */
+
+ index = dest + (row * row_size) + col;
+ rindex1 = future + (row + down_back) * row_size + col + right_back;
+
+ if ((index+7*row_size+7 >= dest+endDest) || (index < dest)) {
+ DEBUG_RECON(cout << "urg! last resort -9"<<endl;)
+ return false;
+ }
+ if ((rindex1+7*row_size+7 >= future+endFuture) || (rindex1 < future)) {
+ DEBUG_RECON(cout << "urg! last resort -8"<<endl;)
+ return false;
+ }
+
+ if ((!right_half_back) && (!down_half_back)) {
+ if (!zflag) {
+ copyFunctions->copy8_src2linear_crop(rindex1,dct_start,index,row_size);
+ } else {
+ if (right_back & 0x1) {
+ /* No alignment, use byte copy */
+
+ copyFunctions->copy8_byte(rindex1,index,row_size);
+
+ } else if (right_back & 0x2) {
+ /* Half-word bit aligned, use 16 bit copy */
+ unsigned short *src = (unsigned short *)rindex1;
+ unsigned short *dest = (unsigned short *)index;
+ row_size >>= 1;
+ copyFunctions->copy8_word(src,dest,row_size);
+
+ } else {
+ /* Word aligned, use 32 bit copy */
+ int *src = (int *)rindex1;
+ int *dest = (int *)index;
+ row_size >>= 2;
+ for (rr = 0; rr < 8; rr++) {
+ dest[0] = src[0];
+ dest[1] = src[1];
+ dest += row_size;
+ src += row_size;
+ }
+ }
+ }
+ } else {
+ rindex2 = rindex1 + right_half_back + (down_half_back * row_size);
+ if (!qualityFlag) {
+
+ if (!zflag) {
+ // was +1
+ copyFunctions->copy8_div2_src3linear_crop(rindex1,rindex2,dct_start,
+ index,row_size);
+ } else { /* zflag */
+ // was +1
+ copyFunctions->copy8_div2_nocrop(rindex1,rindex2,index,row_size);
+ }
+ } else { /* qualityFlag on */
+ rindex3 = rindex1 + right_half_back;
+ rindex4 = rindex1 + (down_half_back * row_size);
+ if (!zflag) {
+ copyFunctions->copy8_div4_src5linear_crop(rindex1,rindex2,rindex3,
+ rindex4,dct_start,
+ index,row_size);
+ } else { /* zflag */
+ copyFunctions->copy8_div4_nocrop(rindex1,rindex2,rindex3,rindex4,
+ index,row_size);
+ }
+ }
+
+ }
+ return true;
+}
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ReconBiMBlock --
+ *
+ * Reconstructs bidirectionally predicted macroblocks.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+int Recon::ReconBiMBlock(int bnum,
+ int recon_right_for,
+ int recon_down_for,
+ int recon_right_back,
+ int recon_down_back,
+ int zflag,int mb_row,
+ int mb_col,int row_size,short int* dct_start,
+ PictureArray* pictureArray) {
+ int row, col;
+ unsigned char *dest, *past=NULL, *future=NULL;
+ int right_for, down_for, right_half_for, down_half_for;
+ int right_back, down_back, right_half_back, down_half_back;
+ unsigned char *index, *rindex1, *bindex1;
+ int forw_row_start, back_row_start, forw_col_start, back_col_start;
+ int lumLength=(pictureArray->getCurrent())->getLumLength();
+ int colorLength=(pictureArray->getCurrent())->getColorLength();
+ int endPast=0;
+ int endFuture=0;
+
+
+
+
+ /* If block is luminance block... */
+
+ if (bnum < 4) {
+
+
+ /* Set dest to luminance plane of current pict image. */
+
+ dest = (pictureArray->getCurrent())->getLuminancePtr();
+
+ /* If past frame exists, set past to luminance plane of past frame. */
+
+ past = (pictureArray->getPast())->getLuminancePtr();
+ endPast=lumLength;
+
+ /*
+ * If future frame exists, set future to luminance plane of future frame.
+ */
+
+ future = (pictureArray->getFuture())->getLuminancePtr();
+ endFuture=lumLength;
+
+ /* Calculate row,col of upper left pixel in block. */
+
+ row = (mb_row << 4);
+ col = (mb_col << 4);
+ if (bnum > 1)
+ row += 8;
+ if (bnum & 0x01)
+ col += 8;
+
+
+
+ } else {
+ /* Otherwise, block is NOT luminance block, ... */
+
+ /* Construct motion vectors. */
+ recon_right_for >>= 1;
+ recon_down_for >>= 1;
+ recon_right_back >>= 1;
+ recon_down_back >>= 1;
+
+ /* Establish row size. yuv color is half of whole size*/
+
+ row_size /= 2;
+
+
+ /* Calculate row,col of upper left pixel in block. */
+
+ row = (mb_row << 3);
+ col = (mb_col << 3);
+
+
+
+
+ /* If block is Cr block... */
+ /* Switched earlier, so we test Cr first - eyhung */
+
+ if (bnum == 5) {
+ /* Set dest to Cr plane of current pict image. */
+
+ dest = (pictureArray->getCurrent())->getCrPtr();
+
+ /* If past frame exists, set past to Cr plane of past image. */
+
+ past = (pictureArray->getPast())->getCrPtr();
+ endPast=colorLength;
+ /*
+ * If future frame exists, set future to Cr plane of future image.
+ */
+
+ future = (pictureArray->getFuture())->getCrPtr();
+ endFuture=colorLength;
+ }
+ /* Otherwise, block is Cb block... */
+
+ else {
+ /* Set dest to Cb plane of current pict image. */
+ dest = (pictureArray->getCurrent())->getCbPtr();
+
+ /* If past frame exists, set past to Cb plane of past frame. */
+
+ past = (pictureArray->getPast())->getCbPtr();
+ endPast=colorLength;
+
+ /*
+ * If future frame exists, set future to Cb plane of future frame.
+ */
+
+ future = (pictureArray->getFuture())->getCbPtr();
+ endFuture=colorLength;
+ }
+ }
+ /*
+ * Calculate right_for, down_for, right_half_for, down_half_for,
+ * right_back, down_bakc, right_half_back, and down_half_back, motion
+ * vectors.
+ */
+
+ right_for = recon_right_for >> 1;
+ down_for = recon_down_for >> 1;
+ right_half_for = recon_right_for & 0x1;
+ down_half_for = recon_down_for & 0x1;
+
+ right_back = recon_right_back >> 1;
+ down_back = recon_down_back >> 1;
+ right_half_back = recon_right_back & 0x1;
+ down_half_back = recon_down_back & 0x1;
+
+ forw_col_start = col + right_for;
+ forw_row_start = row + down_for;
+
+ back_col_start = col + right_back;
+ back_row_start = row + down_back;
+
+ /* For each pixel in block... */
+
+ index = dest + (row * row_size) + col;
+
+ rindex1 = past + forw_row_start * row_size + forw_col_start;
+
+ bindex1 = future + back_row_start * row_size + back_col_start;
+
+ if ((rindex1+7*row_size+7 >= past+endPast) || (rindex1 < past)) {
+ DEBUG_RECON(cout << "urg! last resort -1"<<endl;)
+ return false;
+ }
+ if ((bindex1+7*row_size+7 >= future+endFuture) || (bindex1 < future)) {
+ DEBUG_RECON(cout << "urg! last resort -2"<<endl;)
+ return false;
+ }
+
+
+
+ if (!zflag) {
+ copyFunctions->copy8_div2_src3linear_crop(rindex1,bindex1,dct_start,
+ index,row_size);
+ } else {
+ copyFunctions->copy8_div2_nocrop(rindex1,bindex1,index,row_size);
+ }
+
+ return true;
+}
+
+
diff --git a/mpeglib/lib/mpegplay/recon.h b/mpeglib/lib/mpegplay/recon.h
new file mode 100644
index 00000000..26c3e92e
--- /dev/null
+++ b/mpeglib/lib/mpegplay/recon.h
@@ -0,0 +1,55 @@
+/*
+ class for reconstruction
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __RECON_H
+#define __RECON_H
+
+
+#include <iostream>
+#include "../util/render/pictureArray.h"
+#include "copyFunctions.h"
+#include "videoDecoder.h"
+
+class Recon {
+
+
+ CopyFunctions* copyFunctions;
+
+ public:
+ Recon();
+ ~Recon();
+
+ int ReconIMBlock(int bnum,int mb_row,int mb_col,int row_size,
+ short int* dct_start,PictureArray* pictureArray);
+
+ int ReconPMBlock(int bnum,int recon_right_for,
+ int recon_down_for,int zflag,
+ int mb_row,int mb_col,int row_size,
+ short int* dct_start,PictureArray* pictureArray,
+ int codeType);
+
+ int ReconBMBlock(int bnum,int recon_right_back,
+ int recon_down_back,int zflag,
+ int mb_row,int mb_col,int row_size,
+ short int* dct_start,PictureArray* pictureArray);
+
+ int ReconBiMBlock(int bnum,int recon_right_for,
+ int recon_down_for,int recon_right_back,
+ int recon_down_back,int zflag,
+ int mb_row,int mb_col,int row_size,
+ short int* dct_start,PictureArray* pictureArray);
+
+
+
+};
+#endif
diff --git a/mpeglib/lib/mpegplay/slice.cpp b/mpeglib/lib/mpegplay/slice.cpp
new file mode 100644
index 00000000..1a64378f
--- /dev/null
+++ b/mpeglib/lib/mpegplay/slice.cpp
@@ -0,0 +1,73 @@
+/*
+ reads/parse slice infos
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "slice.h"
+
+
+
+
+Slice::Slice() {
+ mpegExtension=new MpegExtension();
+}
+
+
+Slice::~Slice() {
+ delete mpegExtension;
+
+}
+
+unsigned int Slice::getVertPos() {
+ return vert_pos;
+}
+
+
+
+void Slice::setQuantScale(unsigned int quant_scale) {
+ this->quant_scale=quant_scale;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ParseSlice --
+ *
+ * Parses off slice header.
+ *
+ * Results:
+ * Values found in slice header put into video stream structure.
+ *
+ * Side effects:
+ * Bit stream irreversibly parsed.
+ *
+ *--------------------------------------------------------------
+ */
+int Slice::parseSlice(MpegVideoStream* mpegVideoStream) {
+
+ /* Flush slice start code. */
+ mpegVideoStream->flushBits(24);
+
+ /* Parse off slice vertical position. */
+ /* its the "slice number" */
+ vert_pos=mpegVideoStream->getBits(8);
+
+ /* Parse off quantization scale. */
+ quant_scale=mpegVideoStream->getBits(5);
+
+ /* Parse off extra bit slice info. */
+ mpegExtension->processExtra_bit_info(mpegVideoStream);
+
+ return true;
+}
+
+
+
diff --git a/mpeglib/lib/mpegplay/slice.h b/mpeglib/lib/mpegplay/slice.h
new file mode 100644
index 00000000..10689d34
--- /dev/null
+++ b/mpeglib/lib/mpegplay/slice.h
@@ -0,0 +1,48 @@
+/*
+ reads/parse slice infos
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __SLICE_H
+#define __SLICE_H
+
+#include "jrevdct.h"
+#include "mpegExtension.h"
+
+/* Size increment of extension data buffers. */
+
+#define EXT_BUF_SIZE 1024
+
+class Slice {
+
+
+ public:
+ Slice();
+ ~Slice();
+ int parseSlice(MpegVideoStream* mpegVideoStream);
+ unsigned int getVertPos();
+ inline unsigned int getQuantScale() {return quant_scale;}
+ void setQuantScale(unsigned int quant_scale);
+
+
+ private:
+
+ /* Slice structure. */
+
+ unsigned int vert_pos; /* Vertical position of slice. */
+ unsigned int quant_scale; /* Quantization scale. */
+ MpegExtension* mpegExtension; /* Extra bit slice info. */
+
+};
+
+
+#endif
diff --git a/mpeglib/lib/mpegplay/startCodes.h b/mpeglib/lib/mpegplay/startCodes.h
new file mode 100644
index 00000000..c305a39f
--- /dev/null
+++ b/mpeglib/lib/mpegplay/startCodes.h
@@ -0,0 +1,63 @@
+/*
+ definitions of common startcodes
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+
+#ifndef __STARTCODES_H
+#define __STARTCODES_H
+
+
+
+/* Macros for picture code type. */
+
+#define I_TYPE 1
+#define P_TYPE 2
+#define B_TYPE 3
+#define D_TYPE 4
+
+/* Start codes. */
+
+
+#define SEQ_END_CODE 0x000001b7
+#define SEQ_START_CODE 0x000001b3
+#define GOP_START_CODE 0x000001b8
+#define PICTURE_START_CODE 0x00000100
+#define SLICE_MIN_START_CODE 0x00000101
+#define SLICE_MAX_START_CODE 0x000001af
+#define EXT_START_CODE 0x000001b5
+#define USER_START_CODE 0x000001b2
+#define SEQUENCE_ERROR_CODE 0x000001b4
+
+//extension start code ids
+#define SEQUENCE_EXTENSION_ID 1
+#define SEQUENCE_DISPLAY_EXTENSION_ID 2
+#define QUANT_MATRIX_EXTENSION_ID 3
+#define COPYRIGHT_EXTENSION_ID 4
+#define SEQUENCE_SCALABLE_EXTENSION_ID 5
+#define PICTURE_DISPLAY_EXTENSION_ID 7
+#define PICTURE_CODING_EXTENSION_ID 8
+#define PICTURE_SPATIAL_SCALABLE_EXTENSION_ID 9
+#define PICTURE_TEMPORAL_SCALABLE_EXTENSION_ID 10
+
+#define ISO_11172_END_CODE ((unsigned int)0x000001b9)
+#define SYSTEM_HEADER_START_CODE ((unsigned int)0x000001bb)
+
+/* Macros used with macroblock address decoding. */
+
+#define MB_STUFFING 34
+#define MB_ESCAPE 35
+
+
+
+#endif
diff --git a/mpeglib/lib/mpegplay/tsSystemStream.cpp b/mpeglib/lib/mpegplay/tsSystemStream.cpp
new file mode 100644
index 00000000..4c9221ac
--- /dev/null
+++ b/mpeglib/lib/mpegplay/tsSystemStream.cpp
@@ -0,0 +1,377 @@
+/*
+ demux transport stream
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "tsSystemStream.h"
+
+
+#define PKT_SIZE 188
+
+
+TSSystemStream::TSSystemStream(InputStream* input) {
+ this->input=input;
+
+}
+
+
+TSSystemStream::~TSSystemStream() {
+}
+
+int TSSystemStream::read(char* ptr,int bytes) {
+ if (input->read(ptr,bytes) != bytes) {
+ return false;
+ }
+ paket_read+=bytes;
+
+ return true;
+}
+
+int TSSystemStream::getByteDirect() {
+ unsigned char byte;
+ if (input->read((char*)&byte,1) != 1) {
+ return -1;
+ }
+ paket_read++;
+ return (int)byte;
+}
+
+
+// nuke bytes modulo 10
+int TSSystemStream::nukeBytes(int bytes) {
+ // nukebuffer
+ char nuke[10];
+
+ while(bytes > 0) {
+ int doNuke=10;
+ if (bytes < 10) doNuke=bytes;
+ if (input->read((char*)&nuke,doNuke) != doNuke) {
+ return false;
+ }
+ bytes-=doNuke;
+ paket_read+=doNuke;
+ }
+ return true;
+}
+
+
+int TSSystemStream::skipNextByteInLength() {
+ int length=getByteDirect();
+ if (length < 0) return false;
+
+ /*
+ * Skip read byte in length, but check paket_size
+ */
+ if (paket_read+length > PKT_SIZE) {
+ printf ("demux error! invalid payload size %d\n",length);
+ return false;
+ }
+ if (nukeBytes(length) == false) return false;
+ return true;
+}
+
+
+
+
+int TSSystemStream::processStartCode(MpegSystemHeader* mpegHeader) {
+ paket_len=PKT_SIZE;
+ paket_read=4; // startcode=4 bytes
+
+ mpegHeader->setTSPacketLen(0);
+ mpegHeader->setPacketID(_PAKET_ID_NUKE);
+
+ unsigned int pid=mpegHeader->getPid();
+ unsigned int pmtPID=mpegHeader->getPMTPID();
+ if ( (pmtPID == INVALID_PID) && (pid != 0)) {
+ return false;
+ }
+
+ if ((mpegHeader->getAdaption_field_control() & 0x1)==0) {
+ return true;
+ }
+
+ /*
+ * Has a payload! Calculate & check payload length.
+ */
+ if (mpegHeader->getAdaption_field_control() & 0x2) {
+ if (skipNextByteInLength() == false) return false;
+ }
+
+ /*
+ * Do the demuxing in based on the pids
+ */
+
+ if (pid == mpegHeader->getPMTPID()) {
+ return demux_ts_pmt_parse(mpegHeader);
+ }
+
+ if (pid == 0) {
+ return demux_ts_pat_parse(mpegHeader);
+ }
+ //
+ // ok, no the only things left to do is the
+ // decision what to do with the packet
+ //
+
+ mpegHeader->setTSPacketLen(paket_len-paket_read);
+
+ if (pid == 0x1fff) {
+ printf("Nuke Packet\n");
+ return true;
+ }
+
+
+ MapPidStream* mapPidStream=mpegHeader->lookup(pid);
+
+ if (mapPidStream->isValid == true) {
+ // set to something different from "NUKE_PAKET"
+ mpegHeader->setPacketID(_PAKET_ID_AUDIO_1);
+ return true;
+ }
+ // well the raw stream has a TS header and a PID, but we have not a valid
+ // mapping pid->tsType.
+ // we return false here to have a recovery if our
+ // previous decision that we actually have a TS stream was wrong.
+
+ // force resync
+ return false;
+}
+
+/*
+ * NAME demux_ts_pmt_parse
+ *
+ * Parse a PMT. The PMT is expected to be exactly one section long,
+ * and that section is expected to be contained in a single TS packet.
+ *
+ * In other words, the PMT is assumed to describe a reasonable number of
+ * video, audio and other streams (with descriptors).
+ */
+
+int TSSystemStream::demux_ts_pmt_parse(MpegSystemHeader* mpegHeader) {
+ int sectionLength=processSection(mpegHeader);
+ if (sectionLength == 0) return false;
+
+ //?
+ if (nukeBytes(2) == false) return false;
+ sectionLength-=2;
+
+ /*
+ * ES definitions start here...we are going to learn upto one video
+ * PID and one audio PID.
+ */
+
+
+ unsigned char pkt[2];
+ if (read((char*)pkt,2) == false) return false;
+ sectionLength-=2;
+
+ unsigned int programInfoLength;
+
+ programInfoLength=(((unsigned int)pkt[0] & 0x0f) << 8) | pkt[1];
+ if (paket_read+programInfoLength > paket_len) {
+ printf ("demux error! PMT with inconsistent progInfo length\n");
+ return false;
+ }
+
+ if (nukeBytes(programInfoLength) == false) return false;
+ sectionLength-=programInfoLength;
+
+ return processElementary(sectionLength,mpegHeader);
+
+
+}
+
+/**
+ return false on error or section length info on success
+*/
+int TSSystemStream::processSection(MpegSystemHeader* mpegHeader) {
+ unsigned int pus=mpegHeader->getPayload_unit_start_indicator();
+
+ /*
+ * A PAT in a single section should start with a payload unit start
+ * indicator set.
+ */
+ if (pus==0) {
+ printf ("demux error! PAT without payload unit start\n");
+ return false;
+ }
+ /*
+ * PAT packets with a pus start with a pointer. Skip it!
+ */
+ if (skipNextByteInLength() == false) return false;
+
+ // ??
+ if (nukeBytes(1) == false) return false;
+
+ // read sectionLength
+ unsigned char pkt[2];
+ if (read((char*)pkt,2) ==false) return false;
+
+
+ int sectionLength=(((unsigned int)pkt[0] & 0x3) << 8) | pkt[1];
+ if (paket_read+sectionLength > PKT_SIZE) {
+ printf ("demux error! invalid section size %d\n",sectionLength);
+ return false;
+ }
+
+ // ??
+ if (nukeBytes(2) == false) return false;
+
+ int byte=getByteDirect();
+ if (byte < 0) return false;
+
+ if ((byte & 0x01) == false) {
+ /*
+ * Not current!
+ */
+ return false;
+ }
+ if (read((char*)pkt,2) == false) return false;
+
+ if ((pkt[0]) || (pkt[1])) {
+ printf ("demux error! PAT with invalid section %02x of %02x\n",
+ pkt[0], pkt[1]);
+ return false;
+ }
+
+ /*
+ * TBD: at this point, we should check the CRC. Its not that expensive, and
+ * the consequences of getting it wrong are dire!
+ */
+ return sectionLength-5;
+}
+
+/*
+ * NAME demux_ts_pat_parse
+ *
+ * Parse a PAT. The PAT is expected to be exactly one section long,
+ * and that section is expected to be contained in a single TS packet.
+ *
+ * The PAT is assumed to contain a single program definition, though
+ * we can cope with the stupidity of SPTSs which contain NITs.
+ */
+
+int TSSystemStream::demux_ts_pat_parse(MpegSystemHeader* mpegHeader) {
+ int sectionLength=processSection(mpegHeader);
+ if (sectionLength == 0) return false;
+
+ return processPrograms(sectionLength,mpegHeader);
+
+}
+
+
+/*
+ * Process all programs in the program loop.
+ */
+int TSSystemStream::processPrograms(int sectionLength,
+ MpegSystemHeader* mpegHeader) {
+ int programs=sectionLength / 4;
+ int i;
+ // what happens with the last 4 byte?
+ // seems they have no meaning?
+ programs--;
+
+ for(i=0;i<programs;i++) {
+ unsigned char program[4];
+ if (read((char*)program,4) == false) return false;
+
+
+ unsigned int programNumber;
+ unsigned int pmtPid;
+ unsigned int programCount;
+ programNumber = ((unsigned int)program[0] << 8) | program[1];
+
+ /*
+ * Skip NITs completely.
+ */
+ if (!programNumber)
+ continue;
+
+ pmtPid = (((unsigned int)program[2] & 0x1f) << 8) | program[3];
+
+
+ /*
+ * If we have yet to learn our program number, then learn it.
+ */
+
+ if (mpegHeader->getProgramNumber() == INVALID_PROGRAM) {
+ mpegHeader->setProgramNumber(programNumber);
+ mpegHeader->setPMTPID(pmtPid);
+ }
+
+ if (mpegHeader->getProgramNumber() != programNumber) {
+ printf("demux error! MPTS: programNumber=%u pmtPid=%04x\n",
+ programNumber, pmtPid);
+ }
+
+ if (mpegHeader->getPMTPID() != pmtPid) {
+ printf("pmtPid changed %04x\n", pmtPid);
+ mpegHeader->setPMTPID(pmtPid);
+ }
+
+ }
+ // nuke last four bytes
+ if (nukeBytes(4) == false) return false;
+
+ //
+ // now we can nuke the rest of the PAKET_SIZE (188 byte)
+ //
+ mpegHeader->setTSPacketLen(paket_len-paket_read);
+ return true;
+}
+
+
+
+int TSSystemStream::processElementary(int sectionLength,
+ MpegSystemHeader* mpegHeader) {
+
+
+ /*
+ * Extract the elementary streams.
+ */
+ int mediaIndex=0;
+ // what happens with the last 4 byte?
+ // seems they have no meaning?
+ while (sectionLength > 4) {
+ unsigned int streamInfoLength;
+ unsigned char stream[5];
+ if (read((char*)stream,5) == false) return false;
+ sectionLength-=5;
+
+ unsigned int pid;
+ pid = (((unsigned int)stream[1] & 0x1f) << 8) | stream[2];
+
+ streamInfoLength = (((unsigned int)stream[3] & 0xf) << 8) | stream[4];
+ if(paket_read+streamInfoLength > paket_len) {
+ printf ("demux error! PMT with inconsistent streamInfo length\n");
+ return false;
+ }
+ mpegHeader->insert(pid,stream[0],mpegHeader);
+
+ }
+
+ // nuke last four bytes
+ if (nukeBytes(4) == false) return false;
+ //
+ // now we can nuke the rest of the PAKET_SIZE (188 byte)
+ //
+ mpegHeader->setTSPacketLen(paket_len-paket_read);
+
+ //
+ // now we can be sure that we have in fact an TS stream
+ // so, switch to MPEG2 PES now
+ mpegHeader->setMPEG2(true);
+
+ return true;
+}
+
+
diff --git a/mpeglib/lib/mpegplay/tsSystemStream.h b/mpeglib/lib/mpegplay/tsSystemStream.h
new file mode 100644
index 00000000..114365c4
--- /dev/null
+++ b/mpeglib/lib/mpegplay/tsSystemStream.h
@@ -0,0 +1,68 @@
+/*
+ demux transport stream
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __TSSYSTEMSTREAM_H
+#define __TSSYSTEMSTREAM_H
+
+
+#include <stdlib.h>
+#include "../input/inputPlugin.h"
+#include "mpegSystemHeader.h"
+
+
+
+/**
+ This class is used inside mpegSystemStream.h when we found
+ during initialisation an transport stream.
+
+
+*/
+
+class TSSystemStream {
+
+ InputStream* input;
+
+ int paket_len;
+ int paket_read;
+
+
+
+ public:
+ TSSystemStream(InputStream* input);
+ ~TSSystemStream();
+
+ // called when we found a valid ts startcode
+ int processStartCode(MpegSystemHeader* mpegHeader);
+
+
+ private:
+
+ // read stream methods
+ int read(char* ptr,int bytes);
+ int getByteDirect();
+ int nukeBytes(int bytes);
+ int skipNextByteInLength();
+
+
+ // process Format methods
+ int processSection(MpegSystemHeader* mpegHeader);
+ int processPrograms(int sectionLength,MpegSystemHeader* mpegHeader);
+ int processElementary(int sectionLength,MpegSystemHeader* mpegHeader);
+
+ int demux_ts_pat_parse(MpegSystemHeader* mpegHeader);
+ int demux_ts_pmt_parse(MpegSystemHeader* mpegHeader);
+
+
+};
+#endif
diff --git a/mpeglib/lib/mpegplay/videoDecoder.cpp b/mpeglib/lib/mpegplay/videoDecoder.cpp
new file mode 100644
index 00000000..b1971795
--- /dev/null
+++ b/mpeglib/lib/mpegplay/videoDecoder.cpp
@@ -0,0 +1,476 @@
+/*
+ mpeg I video decoder (derived from mpeg_play)
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "videoDecoder.h"
+
+using namespace std;
+
+
+
+VideoDecoder::VideoDecoder(MpegVideoStream* inputStream,
+ MpegVideoHeader* initSequence) {
+
+ /* Check for legal buffer length. */
+
+ init_tables();
+
+
+ /* Initialize fields that used to be global */
+
+ mpegVideoStream=inputStream;
+ decoderClass=new DecoderClass(this,mpegVideoStream);
+ recon=new Recon();
+ motionVector=new MotionVector();
+ slice=new Slice();
+ group=new GOP();
+ mpegVideoHeader=new MpegVideoHeader();
+ picture=new Picture();
+ macroBlock=new MacroBlock(this);
+
+ // init this stream with the init sequence
+ initSequence->copyTo(mpegVideoHeader);
+
+ syncState=SYNC_TO_CLOSED_GOP;
+ extension=new MpegExtension();
+ frameCounter=0;
+}
+
+
+VideoDecoder::~VideoDecoder() {
+
+ delete mpegVideoHeader;
+ delete picture;
+ delete decoderClass;
+ delete recon;
+ delete motionVector;
+ delete slice;
+ delete group;
+ delete extension;
+ delete macroBlock;
+}
+
+
+
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * mpegVidRsrc --
+ *
+ * Parses bit stream until MB_QUANTUM number of
+ * macroblocks have been decoded or current slice or
+ * picture ends, whichever comes first. If the start
+ * of a frame is encountered, the frame is time stamped
+ * with the value passed in time_stamp. If the value
+ * passed in buffer is not null, the video stream buffer
+ * is set to buffer and the length of the buffer is
+ * expected in value passed in through length. The current
+ * video stream is set to vid_stream. If vid_stream
+ * is passed as NULL, a new VideoDecoder structure is created
+ * and initialized and used as the current video stream.
+ *
+ * Results:
+ * A pointer to the video stream structure used.
+ *
+ * Side effects:
+ * Bit stream is irreversibly parsed. If a picture is completed,
+ * a function is called to display the frame at the correct time.
+ *
+ *--------------------------------------------------------------
+ */
+
+int VideoDecoder::mpegVidRsrc(PictureArray* pictureArray) {
+ int back=_SYNC_TO_NONE;
+
+ unsigned int data;
+ int i;
+
+
+ /*
+ * If called for the first time, find start code, make sure it is a
+ * sequence start code.
+ */
+
+
+ /* Get next 32 bits (size of start codes). */
+
+ data=mpegVideoStream->showBits(32);
+
+
+ /*
+ * Process according to start code (or parse macroblock if not a start code
+ * at all).
+ */
+ switch (data) {
+ /*
+ case PACK_START_CODE:
+ case SYSTEM_HEADER_START_CODE:
+ cout << "Packet in Loop **************"<<endl;
+ mpegVideoStream->flushBits(32);
+ packet->read_sys(data,vid_stream->bufferReader);
+ */
+ case SEQ_END_CODE:
+ case ISO_11172_END_CODE: /* handle ISO_11172_END_CODE too */
+
+ /* Display last frame. */
+
+ // removed!
+
+ /* Sequence done. Do the right thing. For right now, exit. */
+
+
+ cout << "******** flushin end code"<<endl;
+ mpegVideoStream->flushBits(32);
+ goto done;
+ break;
+ case EXT_START_CODE:
+ cout << "found EXT_START_CODE skipping"<<endl;
+ mpegVideoStream->flushBits(32);
+ /* Goto next start code. */
+ mpegVideoStream->next_start_code();
+
+ break;
+ case USER_START_CODE:
+ mpegVideoStream->flushBits(32);
+ /* Goto next start code. */
+ mpegVideoStream->next_start_code();
+
+ break;
+ case SEQ_START_CODE:
+
+ /* Sequence start code. Parse sequence header. */
+ if (ParseSeqHead() == false) {
+ printf("SEQ_START_CODE 1-error\n");
+ goto error;
+ }
+ goto done;
+
+ case GOP_START_CODE:
+ /* Group of Pictures start code. Parse gop header. */
+ if (ParseGOP() == false) {
+ printf("GOP_START_CODE 1-error\n");
+ goto error;
+ }
+ goto done;
+
+ case PICTURE_START_CODE:
+
+ /* Picture start code. Parse picture header and first slice header. */
+
+
+ back=ParsePicture();
+ if (back != _SYNC_TO_NONE) {
+ //cout << "skip B Frame we are late"<<endl;
+ return back;
+ }
+
+ // parse ok
+ if (ParseSlice() == false) {
+ printf("PICTURE_START_CODE 2-error\n");
+ goto error;
+ }
+ break;
+
+ case SEQUENCE_ERROR_CODE:
+ mpegVideoStream->flushBits(32);
+ mpegVideoStream->next_start_code();
+
+ goto done;
+
+
+ default:
+
+ /* Check for slice start code. */
+ if ((data >= SLICE_MIN_START_CODE) && (data <= SLICE_MAX_START_CODE)) {
+
+ /* Slice start code. Parse slice header. */
+ if (ParseSlice() == false) {
+ printf("default 1-error\n");
+ goto error;
+ }
+ }
+ break;
+ }
+
+ /* Parse next MB_QUANTUM macroblocks. */
+ for (i = 0; i < MB_QUANTUM; i++) {
+
+ /* Check to see if actually a startcode and not a macroblock. */
+ data=mpegVideoStream->showBits(23);
+ if (data != 0x0) {
+ /* Not start code. Parse Macroblock. fill yuv pictures*/
+ if (macroBlock->processMacroBlock(pictureArray) == false) {
+ goto error;
+ }
+ } else {
+ /* Not macroblock, actually start code. Get start code. */
+ mpegVideoStream->next_start_code();
+
+ /*
+ * If start code is outside range of slice start codes, frame is
+ * complete, display frame.
+ */
+ data=mpegVideoStream->showBits(32);
+
+ if (((data < SLICE_MIN_START_CODE) || (data > SLICE_MAX_START_CODE)) &&
+ (data != SEQUENCE_ERROR_CODE)) {
+ doPictureDisplay(pictureArray);
+ }
+ goto done;
+ }
+ }
+ data=mpegVideoStream->showBits(23);
+ /* Check if we just finished a picture on the MB_QUANTUM macroblock */
+ if (data == 0x0) {
+ mpegVideoStream->next_start_code();
+
+ data=mpegVideoStream->showBits(32);
+ if ((data < SLICE_MIN_START_CODE) || (data > SLICE_MAX_START_CODE)) {
+ doPictureDisplay(pictureArray);
+ }
+ }
+
+ /* Return pointer to video stream structure. */
+
+ goto done;
+
+error:
+ init_tables();
+ back=_SYNC_TO_GOP;
+ mpegVideoHeader->init_quanttables();
+
+ goto done;
+
+done:
+ return back;
+
+}
+
+
+
+
+
+
+int VideoDecoder::ParseSeqHead() {
+ int back;
+
+ /* Flush off sequence start code. */
+
+ mpegVideoStream->flushBits(32);
+
+ back=mpegVideoHeader->parseSeq(mpegVideoStream);
+
+ return back;
+
+}
+
+
+
+
+int VideoDecoder::ParseGOP() {
+ if (syncState==SYNC_TO_CLOSED_GOP) {
+ syncState=SYNC_HAS_CLOSED_GOP;
+ }
+
+ return group->processGOP(mpegVideoStream);
+}
+
+
+
+
+int VideoDecoder::ParsePicture() {
+ int back;
+ back=picture->processPicture(mpegVideoStream);
+
+ macroBlock->resetPastMacroBlock();
+ if (back == false) {
+ return _SYNC_TO_GOP;
+ }
+
+
+ return _SYNC_TO_NONE;
+}
+
+
+
+int VideoDecoder::ParseSlice() {
+
+
+ slice->parseSlice(mpegVideoStream);
+ macroBlock->resetMacroBlock();
+ decoderClass->resetDCT();
+ return true;
+}
+
+
+
+
+/**
+ After a seek we can only start with an I frame
+*/
+
+void VideoDecoder::resyncToI_Frame() {
+
+ syncState=SYNC_TO_CLOSED_GOP;
+}
+
+
+void VideoDecoder::doPictureDisplay(PictureArray* pictureArray) {
+
+
+ // insert end timestamp to current picture
+ YUVPicture* pic=pictureArray->getCurrent();
+ unsigned int code_type=picture->getCodeType();
+
+ TimeStamp* startTimeStamp=picture->getStartOfPicStamp();
+
+ pic->setStartTimeStamp(startTimeStamp);
+ float rate=mpegVideoHeader->getPictureRate();
+
+ pictureArray->setPicturePerSecond(rate);
+
+
+
+ pic->setMpegPictureType(code_type);
+
+ if (syncState < SYNC_HAS_CLOSED_GOP) {
+ return;
+ }
+ if (syncState < SYNC_HAS_I_FRAME_SYNC) {
+ if (code_type != I_TYPE) {
+ return;
+ }
+ }
+ if (code_type == I_TYPE) {
+ YUVPicture* past=pictureArray->getPast();
+ YUVPicture* future=pictureArray->getFuture();
+ YUVPicture* current=pictureArray->getCurrent();
+
+ YUVPicture* tmp=past;
+ past = future;
+ future = current;
+ current = tmp;
+
+ pic=past;
+
+
+ pictureArray->setPast(past);
+ pictureArray->setCurrent(current);
+ pictureArray->setFuture(future);
+
+ if (syncState < SYNC_HAS_I_FRAME_SYNC) {
+ syncState=SYNC_HAS_I_FRAME_SYNC;
+ return;
+ }
+ if (syncState == SYNC_HAS_P_FRAME_SYNC) {
+ syncState=SYNC_HAS_FRAME_SYNC;
+ return;
+ }
+ if (syncState == SYNC_HAS_I_FRAME_SYNC) {
+ syncState=SYNC_HAS_P_FRAME_SYNC;
+ return;
+ }
+
+
+
+ }
+
+ if (code_type == P_TYPE) {
+ YUVPicture* past=pictureArray->getPast();
+ YUVPicture* future=pictureArray->getFuture();
+ YUVPicture* current=pictureArray->getCurrent();
+
+ YUVPicture* tmp=past;
+ past = future;
+ future = current;
+ current = tmp;
+
+ pic = past;
+
+
+
+ pictureArray->setPast(past);
+ pictureArray->setCurrent(current);
+ pictureArray->setFuture(future);
+
+ if (syncState < SYNC_HAS_P_FRAME_SYNC) {
+ syncState=SYNC_HAS_P_FRAME_SYNC;
+ return;
+ }
+
+ }
+ if (code_type == B_TYPE) {
+ if (syncState == SYNC_HAS_P_FRAME_SYNC) {
+ syncState=SYNC_HAS_FRAME_SYNC;
+ YUVPicture* past=pictureArray->getPast();
+ pic=past;
+ }
+ /**
+ Now check for PTS timeStamp error. It seems some encoders
+ handles this different.
+ If the P frame has a timeStamp earlier than our B stamp
+ we swap them.
+ */
+ YUVPicture* pframe=pictureArray->getFuture();
+ YUVPicture* bframe=pictureArray->getCurrent();
+
+ TimeStamp* pTime=pframe->getStartTimeStamp();
+ TimeStamp* bTime=bframe->getStartTimeStamp();
+
+ double pPTS=pTime->getPTSTimeStamp();
+ double bPTS=bTime->getPTSTimeStamp();
+ if (pPTS < bPTS) {
+ //cout << "********P/B Frame PTS error -> enable swap and pray"<<endl;
+ bTime->copyTo(pTime);
+ }
+
+
+
+ // we display the current picture
+ // (already set)
+ }
+
+
+ if (pic == NULL) {
+ cout << "pic NULL"<<endl;
+ exit(0);
+ return;
+ }
+ if (syncState < SYNC_HAS_FRAME_SYNC) {
+ return;
+ }
+
+ double val=pictureArray->getPicturePerSecond();
+ pic->setPicturePerSecond(val);
+
+ TimeStamp* currentStamp=pic->getStartTimeStamp();
+
+
+ frameCounter++;
+
+ if (currentStamp->getPTSFlag()==true) {
+ frameCounter=0;
+ }
+ currentStamp->setVideoFrameCounter(frameCounter);
+
+
+ // let plugin "rip" the picture
+ pictureArray->setYUVPictureCallback(pic);
+}
+
+
+
+
+
diff --git a/mpeglib/lib/mpegplay/videoDecoder.h b/mpeglib/lib/mpegplay/videoDecoder.h
new file mode 100644
index 00000000..cba45a29
--- /dev/null
+++ b/mpeglib/lib/mpegplay/videoDecoder.h
@@ -0,0 +1,118 @@
+/*
+ mpeg I video decoder (derived from mpeg_play)
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __VIDEO_H
+#define __VIDEO_H
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "decoderClass.h"
+#include "recon.h"
+#include "motionVector.h"
+#include "slice.h"
+#include "proto.h"
+#include "../input/inputStream.h"
+#include "../output/outputStream.h"
+#include "../util/timeStamp.h"
+#include "mpegVideoHeader.h"
+#include "gop.h"
+#include "picture.h"
+#include "macroBlock.h"
+#include "startCodes.h"
+
+
+
+/* Define Parsing error codes. */
+
+
+
+#define SYNC_TO_CLOSED_GOP 1
+#define SYNC_HAS_CLOSED_GOP 2
+#define SYNC_HAS_I_FRAME_SYNC 3
+#define SYNC_HAS_P_FRAME_SYNC 4
+#define SYNC_HAS_FRAME_SYNC 5
+
+#define _SYNC_TO_GOP 1
+#define _SYNC_TO_PIC 2
+#define _SYNC_TO_NONE 3
+
+
+
+/* Number of macroblocks to process in one call to mpegVidRsrc. */
+
+#define MB_QUANTUM 100
+
+
+/* Video stream structure. */
+class VideoDecoder {
+
+
+ public:
+ VideoDecoder(MpegVideoStream* mpegVideoStream,
+ MpegVideoHeader* initSequence);
+
+ ~VideoDecoder();
+ int mpegVidRsrc(PictureArray* pictureArray);
+
+
+ // ugly callbacks FIX ME!
+ class MpegVideoStream* mpegVideoStream;
+ class DecoderClass* decoderClass;
+ class Recon* recon;
+ class MotionVector* motionVector;
+ class Slice* slice; /* Current slice. */
+ class MpegVideoHeader* mpegVideoHeader; /* Sequence info in stream */
+ class GOP* group;
+ class Picture* picture; /* Current picture. */
+ class MacroBlock* macroBlock; /* Current macroblock. */
+
+ void resyncToI_Frame();
+
+
+
+ private:
+
+ int syncState;
+
+ int ParseSeqHead();
+ int ParseGOP();
+ int ParsePicture();
+ int ParseSlice();
+ void doPictureDisplay(PictureArray* pictureArray);
+ MpegExtension* extension;
+ int frameCounter;
+
+};
+
+
+
+/* Declaration of global display pointer. */
+
+
+
+extern int qualityFlag;
+
+
+
+
+extern int gXErrorFlag;
+
+
+
+
+#endif /* videoDecoder.h already included */
+
+
diff --git a/mpeglib/lib/mpgplayer/Makefile.am b/mpeglib/lib/mpgplayer/Makefile.am
new file mode 100644
index 00000000..b10b247a
--- /dev/null
+++ b/mpeglib/lib/mpgplayer/Makefile.am
@@ -0,0 +1,31 @@
+# libsplay - Makefile.am
+
+INCLUDES = $(all_includes)
+
+
+noinst_LTLIBRARIES = libmpgplayer.la
+
+mpegutildir = $(includedir)/$(THIS_LIB_NAME)/mpegutil
+
+mpegutil_HEADERS = mpegStreamPlayer.h
+
+libmpgplayer_la_SOURCES = mpegStreamPlayer.cpp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/lib/mpgplayer/mpegStreamPlayer.cpp b/mpeglib/lib/mpgplayer/mpegStreamPlayer.cpp
new file mode 100644
index 00000000..133e6319
--- /dev/null
+++ b/mpeglib/lib/mpgplayer/mpegStreamPlayer.cpp
@@ -0,0 +1,431 @@
+/*
+ feeds audio/video streams to the decoders
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "mpegStreamPlayer.h"
+#include "../mpegplay/mpegSystemHeader.h"
+#include "../util/syncClockMPEG.h"
+
+#define _NUKE_BUFFER_SIZE 1024
+
+#define _BUFFER_HIGH_WATER 0.8
+#define _BUFFER_LOW_WATER 0.2
+
+#define _BUFFER_CHUNK_SIZE 1500
+
+#include <iostream>
+
+using namespace std;
+
+
+MpegStreamPlayer::MpegStreamPlayer(InputStream* input,
+ OutputStream* output,
+ DecoderPlugin* audioDecoder,
+ DecoderPlugin* videoDecoder) {
+ this->input=input;
+ this->output=output;
+ this->audioDecoder=audioDecoder;
+ this->videoDecoder=videoDecoder;
+
+ /**
+ here we set the plugins in autoPlay mode.
+ because we handle all commands over the loopback buffer.
+ This is necessary, because on start we send play()
+ but this does not mean that we already have
+ build this class (divxplugin for example)
+ See this switch as an implicit play command
+ */
+
+ audioDecoder->config("-y","on",NULL);
+ videoDecoder->config("-y","on",NULL);
+
+
+ audioInput=new BufferInputStream(1024*_BUFFER_CHUNK_SIZE,
+ 1024*200,"audioInput");
+ videoInput=new BufferInputStream(1024*_BUFFER_CHUNK_SIZE,
+ 1024*200,"videoInput");
+ audioInput->open("audio loopback");
+ videoInput->open("video loopback");
+
+ audioDecoder->setOutputPlugin(output);
+ audioDecoder->setInputPlugin(audioInput);
+
+ videoDecoder->setOutputPlugin(output);
+ videoDecoder->setInputPlugin(videoInput);
+
+ timeStampVideo=new TimeStamp();
+ timeStampAudio=new TimeStamp();
+
+ packetCnt=0;
+ audioPacketCnt=0;
+ videoPacketCnt=0;
+ seekPos=-1;
+ nukeBuffer=new char[_NUKE_BUFFER_SIZE];
+
+ syncClock=new SyncClockMPEG();
+ syncClock->setSyncMode(__SYNC_AUDIO);
+ writeToDisk=false;
+}
+
+
+MpegStreamPlayer::~MpegStreamPlayer() {
+ audioInput->close();
+ videoInput->close();
+
+ // everything is prepared for future shutdown
+ // restart any waiting decoders
+ // (they exit immediately)
+ audioDecoder->close();
+ videoDecoder->close();
+ audioDecoder->waitForStreamState(_STREAM_STATE_EOF);
+ videoDecoder->waitForStreamState(_STREAM_STATE_EOF);
+
+ delete videoInput;
+ delete audioInput;
+ delete nukeBuffer;
+ delete timeStampVideo;
+ delete timeStampAudio;
+ delete syncClock;
+}
+
+int MpegStreamPlayer::getByteDirect() {
+ unsigned char byte;
+ if (input->read((char*)&byte,1) != 1) {
+ return -1;
+ }
+ return (int)byte;
+}
+
+int MpegStreamPlayer::processResyncRequest() {
+ // here is our protocol for resync over the loopback stream
+ // if out clients are in sync and we set the resync method on the
+ // stream he sometimes set a "commit" in the stream
+ // we check if both stream are committed (== clients are in sync and paused)
+ // if both are in _STREAM_STATE_RESYNC_COMMIT
+ // we can safley do a seek
+ // and restart them
+
+ if (seekPos != -1) {
+ int audioDecoderState=audioDecoder->getStreamState();
+ int videoDecoderState=videoDecoder->getStreamState();
+ if (audioDecoderState == _STREAM_STATE_RESYNC_COMMIT) {
+ if (videoDecoderState == _STREAM_STATE_RESYNC_COMMIT) {
+ Command restart(_COMMAND_RESYNC_END);
+ input->seek(seekPos);
+ seekPos=-1;
+ audioDecoder->insertSyncCommand(&restart);
+ videoDecoder->insertSyncCommand(&restart);
+ Command play(_COMMAND_PLAY);
+ audioDecoder->insertAsyncCommand(&play);
+ videoDecoder->insertAsyncCommand(&play);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/**
+ according to the header info we read the packets
+*/
+int MpegStreamPlayer::processSystemHeader(MpegSystemHeader* mpegSystemHeader) {
+ int layer=mpegSystemHeader->getLayer();
+
+
+ if (processResyncRequest() == true) {
+ return false;
+ }
+
+ if (layer==_PACKET_NO_SYSLAYER) {
+ syncClock->setSyncMode(__SYNC_NONE);
+ return insertVideoData(mpegSystemHeader,8192);
+ }
+ if (layer==_PACKET_SYSLAYER) {
+ int packetID=mpegSystemHeader->getPacketID();
+ int packetLength=mpegSystemHeader->getPacketLen();
+ int subStreamID=mpegSystemHeader->getSubStreamID();
+
+ switch(packetID>>4) {
+ case _PAKET_ID_VIDEO>>4:
+ if((packetID - _PAKET_ID_VIDEO) !=
+ mpegSystemHeader->getVideoLayerSelect()) {
+ break;
+ }
+
+ insertVideoData(mpegSystemHeader,packetLength);
+ //dumpData(mpegSystemHeader);
+ return true;
+ //break;
+ case _PAKET_ID_AUDIO_1>>4:
+ case _PAKET_ID_AUDIO_2>>4:
+ if((packetID - _PAKET_ID_AUDIO_1) !=
+ mpegSystemHeader->getAudioLayerSelect()) {
+ break;
+ }
+ insertAudioData(mpegSystemHeader,packetLength);
+ return true;
+ }
+ switch(packetID) {
+ case _PRIVATE_STREAM_1_ID:
+ switch(subStreamID) {
+ case _SUBSTREAM_AC3_ID:
+ insertAudioData(mpegSystemHeader,packetLength);
+ //dumpData(mpegSystemHeader);
+ return true;
+ default:
+ printf("unknown private stream id:%8x\n",subStreamID);
+ }
+ }
+
+ // if we got here the packet is not useful
+ // nuke it
+ nuke(packetLength);
+ return true;
+
+
+ }
+ cout << "unknown layer"<<endl;
+ return false;
+}
+
+
+void MpegStreamPlayer::nuke(int packetLength) {
+ int nukeSize;
+ while(packetLength > 0) {
+ nukeSize=packetLength;
+ if (nukeSize > _NUKE_BUFFER_SIZE) {
+ nukeSize=_NUKE_BUFFER_SIZE;
+ }
+ input->read(nukeBuffer,nukeSize);
+ packetLength-=nukeSize;
+ }
+}
+
+
+
+int MpegStreamPlayer::isInit() {
+ int audioDecoderState=audioDecoder->getStreamState();
+ int videoDecoderState=videoDecoder->getStreamState();
+ if ( (audioDecoderState != _STREAM_STATE_FIRST_INIT) &&
+ (videoDecoderState != _STREAM_STATE_FIRST_INIT) ) {
+ return true;
+ }
+ return false;
+}
+
+/**
+ here we make sure that our plugins make
+ a clean shutdown for a seek request.
+*/
+void MpegStreamPlayer::processThreadCommand(Command* command) {
+
+ int id=command->getID();
+
+ switch(id) {
+ case _COMMAND_NONE:
+ break;
+ case _COMMAND_PAUSE:
+ case _COMMAND_PLAY:
+ audioDecoder->insertAsyncCommand(command);
+ videoDecoder->insertAsyncCommand(command);
+ break;
+ case _COMMAND_CLOSE:
+ audioDecoder->close();
+ videoDecoder->close();
+ break;
+ case _COMMAND_SEEK: {
+ Command cmd1(_COMMAND_PAUSE);
+ audioDecoder->insertAsyncCommand(&cmd1);
+ videoDecoder->insertAsyncCommand(&cmd1);
+
+ Command cmd2(_COMMAND_RESYNC_START);
+ audioDecoder->insertAsyncCommand(&cmd2);
+ videoDecoder->insertAsyncCommand(&cmd2);
+
+ seekPos=command->getIntArg();
+ break;
+ }
+ default:
+ cout << "unknown command id in Command::print"<<endl;
+ }
+}
+
+
+void MpegStreamPlayer::setWriteToDisk(int lwriteToDisk) {
+ this->writeToDisk=lwriteToDisk;
+}
+
+
+int MpegStreamPlayer::getWriteToDisk() {
+ return writeToDisk;
+}
+
+
+int MpegStreamPlayer::hasEnd() {
+ audioInput->close();
+ videoInput->close();
+ TimeWrapper::usleep(100000);
+ if (audioInput->getFillgrade()>0) {
+ return false;
+ }
+ if (videoInput->getFillgrade()>0) {
+ return false;
+ }
+ return true;
+}
+
+
+
+int MpegStreamPlayer::insertAudioData(MpegSystemHeader* header,int len) {
+
+ audioPacketCnt++;
+ packetCnt++;
+ timeStampAudio->setVideoFrameCounter(0);
+
+ timeStampAudio->setPTSFlag(false);
+ if (header->getPTSFlag()==true) {
+
+ timeStampAudio->setPTSFlag(true);
+
+ double pts=header->getPTSTimeStamp();
+ double scr=header->getSCRTimeStamp();
+
+ if (pts==timeStampAudio->getPTSTimeStamp()) {
+ cout << "(audio) old PTS == NEW PTS"<<pts<<endl;
+ }
+
+ timeStampAudio->setSCRTimeStamp(scr);
+ timeStampAudio->setPTSTimeStamp(pts);
+ }
+
+ timeStampAudio->setSyncClock(syncClock);
+
+
+ finishAudio(len);
+
+ return true;
+}
+
+
+int MpegStreamPlayer::insertVideoData(MpegSystemHeader* header,int len) {
+
+ videoPacketCnt++;
+ packetCnt++;
+ timeStampVideo->setVideoFrameCounter(0);
+ timeStampVideo->setPTSFlag(false);
+ if (header->getPTSFlag()==true) {
+ timeStampVideo->setPTSFlag(true);
+
+ double pts=header->getPTSTimeStamp();
+ double scr=header->getSCRTimeStamp();
+ double dts=header->getDTSTimeStamp();
+
+ if (pts==timeStampVideo->getPTSTimeStamp()) {
+ cout << "(video) old PTS == NEW PTS"<<pts<<endl;
+ }
+
+ timeStampVideo->setSCRTimeStamp(scr);
+ timeStampVideo->setPTSTimeStamp(pts);
+ timeStampVideo->setDTSTimeStamp(dts);
+ }
+
+
+ timeStampVideo->setSyncClock(syncClock);
+
+ finishVideo(len);
+
+ return true;
+}
+
+int MpegStreamPlayer::insertAudioDataRaw(unsigned char* input,
+ int len,TimeStamp* stamp) {
+ audioInput->write((char*)input,len,stamp);
+ if (writeToDisk==true) {
+ FILE* audio=fopen("audio.mpg","a+");
+ fwrite(input,1,len,audio);
+ fclose(audio);
+ }
+ return true;
+}
+
+
+int MpegStreamPlayer::insertVideoDataRaw(unsigned char* input,
+ int len,TimeStamp* stamp) {
+
+ videoInput->write((char*)input,len,stamp);
+ if (writeToDisk==true) {
+ FILE* video=fopen("video.mpg","a+");
+ fwrite(input,1,len,video);
+ fclose(video);
+ }
+
+ return true;
+}
+
+
+void MpegStreamPlayer::dumpData(MpegSystemHeader* mpegSystemHeader) {
+
+ int packetLength=mpegSystemHeader->getPacketLen();
+
+ unsigned char* packetBuffer= new unsigned char[packetLength];
+ input->read((char*)packetBuffer,packetLength);
+ int cnt;
+ for(cnt=0;cnt < packetLength;cnt++) {
+ printf(" %2x ",packetBuffer[cnt]);
+ if ((cnt+1)%16 == 0) {
+ printf("\n");
+ }
+ }
+
+ printf("\n");
+ cout << "**************************************** packt Dump"<<endl;
+}
+
+
+
+int MpegStreamPlayer::finishAudio(int len) {
+ int resyncState=audioDecoder->getStreamState();
+ if (resyncState != _STREAM_STATE_RESYNC_COMMIT) {
+ if (writeToDisk==true) {
+ char* buf=new char[len];
+ len=input->read(buf,len);
+ insertAudioDataRaw((unsigned char*) buf,len,timeStampAudio);
+ delete buf;
+ } else {
+ audioInput->write(input,len,timeStampAudio);
+ }
+
+ } else {
+ //cout << "throw away audio"<<endl;
+ }
+ return true;
+}
+
+
+int MpegStreamPlayer::finishVideo(int len) {
+ int resyncState=videoDecoder->getStreamState();
+ if (resyncState != _STREAM_STATE_RESYNC_COMMIT) {
+ if (writeToDisk==true) {
+ char* buf=new char[len];
+ len=input->read(buf,len);
+ insertVideoDataRaw((unsigned char*)buf,len,timeStampVideo);
+ delete buf;
+ } else {
+ videoInput->write(input,len,timeStampVideo);
+ }
+
+ } else {
+ //cout <<"throw away video"<<endl;
+ }
+ return true;
+}
+
diff --git a/mpeglib/lib/mpgplayer/mpegStreamPlayer.h b/mpeglib/lib/mpgplayer/mpegStreamPlayer.h
new file mode 100644
index 00000000..584e0a22
--- /dev/null
+++ b/mpeglib/lib/mpgplayer/mpegStreamPlayer.h
@@ -0,0 +1,90 @@
+/*
+ feeds audio/video streams to the decoders
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __MPEGSTREAMPLAYER_H
+#define __MPEGSTREAMPLAYER_H
+
+#include "../decoder/decoderPlugin.h"
+#include "../util/syncClock.h"
+
+class MpegSystemHeader;
+
+class MpegStreamPlayer {
+
+ protected:
+ SyncClock* syncClock;
+ DecoderPlugin* audioDecoder;
+ DecoderPlugin* videoDecoder;
+ BufferInputStream* audioInput;
+ BufferInputStream* videoInput;
+ OutputStream* output;
+ InputStream* input;
+ TimeStamp* timeStampVideo;
+ TimeStamp* timeStampAudio;
+
+ int packetCnt;
+ int audioPacketCnt;
+ int videoPacketCnt;
+ int seekPos;
+ int writeToDisk;
+
+ char* nukeBuffer;
+
+ public:
+ MpegStreamPlayer(InputStream* input,
+ OutputStream* output,
+ DecoderPlugin* audioDecoder,
+ DecoderPlugin* videoDecoder);
+ ~MpegStreamPlayer();
+
+ int isInit();
+ void processThreadCommand(Command* command);
+
+ //
+ // Mpeg special functions [START]
+ //
+
+ int processSystemHeader(MpegSystemHeader* mpegSystemHeader);
+ int insertAudioData(MpegSystemHeader* header,int len);
+ int insertVideoData(MpegSystemHeader* header,int len);
+
+ //
+ // Mpeg special functions [END]
+ //
+
+
+
+ int insertAudioDataRaw(unsigned char* input,int len,TimeStamp* stamp);
+ int insertVideoDataRaw(unsigned char* input,int len,TimeStamp* stamp);
+
+ void dumpData(MpegSystemHeader* mpegSystemHeader);
+
+ void setWriteToDisk(int lwriteToDisk);
+ int getWriteToDisk();
+
+ int hasEnd();
+ protected:
+ int processResyncRequest();
+
+ int getByteDirect();
+ void nuke(int byte);
+
+ // useful methods
+ int finishAudio(int len);
+ int finishVideo(int len);
+
+};
+
+#endif
diff --git a/mpeglib/lib/oggvorbis/Makefile.am b/mpeglib/lib/oggvorbis/Makefile.am
new file mode 100644
index 00000000..2fe00b72
--- /dev/null
+++ b/mpeglib/lib/oggvorbis/Makefile.am
@@ -0,0 +1,32 @@
+# liboggvorbis - Makefile.am
+
+
+INCLUDES = $(all_includes)
+
+
+noinst_LTLIBRARIES = liboggvorbisbase.la
+
+kmpgincludedir = $(includedir)/$(THIS_LIB_NAME)/oggvorbis
+
+kmpginclude_HEADERS = ovFramer.h vorbisDecoder.h oggFrame.h \
+ vorbisInfo.h
+
+
+liboggvorbisbase_la_SOURCES = ovFramer.cpp vorbisDecoder.cpp \
+ oggFrame.cpp vorbisInfo.cpp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/lib/oggvorbis/oggFrame.cpp b/mpeglib/lib/oggvorbis/oggFrame.cpp
new file mode 100644
index 00000000..ace7dadc
--- /dev/null
+++ b/mpeglib/lib/oggvorbis/oggFrame.cpp
@@ -0,0 +1,29 @@
+/*
+ here we have an ogg frame. Its still a raw frame.
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "oggFrame.h"
+
+#ifdef OGG_VORBIS
+
+
+OGGFrame::OGGFrame() : RawFrame(_FRAME_RAW_OGG,0) {
+ setRemoteData((unsigned char*) &op,sizeof(op));
+
+}
+
+OGGFrame::~OGGFrame() {
+}
+
+
+
+#endif
diff --git a/mpeglib/lib/oggvorbis/oggFrame.h b/mpeglib/lib/oggvorbis/oggFrame.h
new file mode 100644
index 00000000..a069a33e
--- /dev/null
+++ b/mpeglib/lib/oggvorbis/oggFrame.h
@@ -0,0 +1,50 @@
+/*
+ here we have an ogg frame. Its still a raw frame.
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __OGG_FRAME_H
+#define __OGG_FRAME_H
+
+
+/**
+ One Ogg Paket as frame. We pass the datapointer and the size
+ to the rawFrame and of course, set the PaktedID to:
+ _FRAME_RAW_OGG
+*/
+
+#include "../frame/rawFrame.h"
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef OGG_VORBIS
+
+#include <vorbis/codec.h>
+
+class OGGFrame : public RawFrame {
+
+ ogg_packet op;
+
+ public:
+ OGGFrame();
+ ~OGGFrame();
+
+
+};
+
+#endif
+
+#endif
+
diff --git a/mpeglib/lib/oggvorbis/ovFramer.cpp b/mpeglib/lib/oggvorbis/ovFramer.cpp
new file mode 100644
index 00000000..ca3506af
--- /dev/null
+++ b/mpeglib/lib/oggvorbis/ovFramer.cpp
@@ -0,0 +1,128 @@
+/*
+ frames raw data into Ogg/Vorbis frames.
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "ovFramer.h"
+
+#define OGG_SYNC_BUFF_SIZE 4096
+
+
+#define _OV_SETSERIAL 1
+#define _OV_STREAMIN 2
+#define _OV_STREAMOUT 3
+
+#include <iostream>
+
+using namespace std;
+
+
+#ifdef OGG_VORBIS
+
+OVFramer::OVFramer(OGGFrame* dest):Framer(1) {
+ if (dest == NULL) {
+ cout << "OGGFrame NULL in OVFramer"<<endl;
+ exit(-1);
+ }
+ this->dest=dest;
+ /********** Decode setup ************/
+ ogg_sync_init(&oy); /* Now we can read pages */
+
+ vorbis_state=_OV_SETSERIAL;
+}
+
+
+OVFramer::~OVFramer() {
+ /* OK, clean up the framer */
+ ogg_sync_clear(&oy);
+}
+
+
+int OVFramer::find_frame(RawDataBuffer* input,RawDataBuffer* store) {
+ while(input->eof()==true) {
+ cout << "input eof"<<endl;
+ return false;
+ }
+
+ if (vorbis_state == _OV_STREAMOUT) {
+ if(ogg_stream_packetout(&os,(ogg_packet*)dest->getData())!=1){
+ vorbis_state=_OV_STREAMIN;
+ return false;
+ }
+ return true;
+ }
+
+ // do we have ogg packets in the ogg framer?
+ if (ogg_sync_pageout(&oy,&og) == 0) {
+ // no, ok insert some.
+ int bytes=input->untilend();
+ input->inc(bytes);
+ store->inc(bytes);
+ ogg_sync_wrote(&oy,bytes);
+ // and setup the next buffer
+ /* submit a 4k block to libvorbis' Ogg layer */
+ buffer=ogg_sync_buffer(&oy,OGG_SYNC_BUFF_SIZE);
+ /* override our own dummy buffer with size 1 */
+ setRemoteFrameBuffer((unsigned char*)buffer,OGG_SYNC_BUFF_SIZE);
+ return false;
+ }
+ // we have an ogg page
+ // now try to build an ogg packet
+ switch(vorbis_state) {
+ case _OV_SETSERIAL:
+ /* Get the serial number and set up the rest of decode. */
+ /* serialno first; use it to set up a logical stream */
+ ogg_stream_init(&os,ogg_page_serialno(&og));
+ vorbis_state=_OV_STREAMIN;
+ // yes we need to put this into the "pager"
+ // no break.
+ case _OV_STREAMIN:
+ if(ogg_stream_pagein(&os,&og)<0){
+ /* error; stream version mismatch perhaps */
+ fprintf(stderr,"Error reading first page of Ogg bitstream data.\n");
+ exit(1);
+ }
+ vorbis_state=_OV_STREAMOUT;
+ break;
+ default:
+ cout << "unknow vorbis_state"<<endl;
+ exit(-1);
+ }
+
+ return false;
+}
+
+int OVFramer::read_frame(RawDataBuffer* ,RawDataBuffer* ) {
+ return true;
+}
+
+
+
+void OVFramer::unsync(RawDataBuffer* store,int lReset) {
+ if (lReset) {
+ store->setpos(0);
+ ogg_sync_reset(&oy);
+ /* submit a 4k block to libvorbis' Ogg layer */
+ buffer=ogg_sync_buffer(&oy,OGG_SYNC_BUFF_SIZE);
+ /* override our own dummy buffer with size 1 */
+ setRemoteFrameBuffer((unsigned char*)buffer,OGG_SYNC_BUFF_SIZE);
+ }
+
+}
+
+
+void OVFramer::printPrivateStates() {
+ cout << "OVFramer::printPrivateStates"<<endl;
+}
+
+
+#endif
diff --git a/mpeglib/lib/oggvorbis/ovFramer.h b/mpeglib/lib/oggvorbis/ovFramer.h
new file mode 100644
index 00000000..4845fddb
--- /dev/null
+++ b/mpeglib/lib/oggvorbis/ovFramer.h
@@ -0,0 +1,69 @@
+/*
+ frames raw data into Ogg/Vorbis frames.
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __OVFRAMER_H
+#define __OVFRAMER_H
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef OGG_VORBIS
+
+#include <vorbis/codec.h>
+#include "../frame/framer.h"
+#include "oggFrame.h"
+
+/**
+
+ This framer works directly on the raw ogg_packet as output
+ Note: the internal setup makes sure, that we initialize
+ the ogg stream to the first found logical bitstream.
+ (For now this should mean: we found vorbis)
+ When the frame goes into the "HAS" state, you have the ogg
+ packet in the in the dest struct from the constructor.
+*/
+
+class OVFramer : public Framer {
+
+ int vorbis_state;
+
+ ogg_sync_state oy; /* sync and verify incoming physical bitstream */
+ ogg_stream_state os; /* take physical pages, weld into a logical
+ stream of packets */
+ ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */
+
+ char *buffer; /* sync buffer from ogg */
+
+ OGGFrame* dest; /* one raw packet of data for decode */
+
+ public:
+ // IMPORTANT: because we use this ptr internally the
+ // data to op cannot be on the stack!
+ OVFramer(OGGFrame* dest);
+ ~OVFramer();
+
+
+ private:
+
+ int find_frame(RawDataBuffer* input,RawDataBuffer* store);
+ int read_frame(RawDataBuffer* input,RawDataBuffer* store);
+
+ void unsync(RawDataBuffer* store,int lReset);
+ void printPrivateStates();
+
+};
+#endif
+
+#endif
diff --git a/mpeglib/lib/oggvorbis/vorbisDecoder.cpp b/mpeglib/lib/oggvorbis/vorbisDecoder.cpp
new file mode 100644
index 00000000..24e9370d
--- /dev/null
+++ b/mpeglib/lib/oggvorbis/vorbisDecoder.cpp
@@ -0,0 +1,135 @@
+/*
+ converts ogg frames into audioFrames
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "vorbisDecoder.h"
+
+#ifdef OGG_VORBIS
+
+#define _VORBIS_NEED_SYNTHHEADER_1 1
+#define _VORBIS_NEED_SYNTHHEADER_2 2
+#define _VORBIS_NEED_SYNTHHEADER_3 3
+
+#define _VORBIS_DECODE_SETUP 4
+#define _VORBIS_DECODE_LOOP 5
+
+#include <iostream>
+
+using namespace std;
+
+
+VorbisDecoder::VorbisDecoder() {
+ vorbis_info_init(&vi);
+ vorbis_comment_init(&vc);
+ reset();
+}
+
+
+VorbisDecoder::~VorbisDecoder() {
+}
+
+int VorbisDecoder::hasHeader() {
+ return (initState>=_VORBIS_DECODE_LOOP);
+}
+
+int VorbisDecoder::decode(RawFrame* rawFrame,AudioFrame* dest) {
+
+ if ((rawFrame == NULL) || (dest == NULL)) {
+ cout << "VorbisDecoder::decode NULL pointer!"<<endl;
+ exit(-1);
+ }
+ if (rawFrame->getFrameType() != _FRAME_RAW_OGG) {
+ cout << "VorbisDecoder::decode not _FRAME_RAW_OGG"<<endl;
+ exit(-1);
+ }
+
+ ogg_packet* op=(ogg_packet*) rawFrame->getData();
+ switch(initState) {
+ case _VORBIS_NEED_SYNTHHEADER_1:
+ case _VORBIS_NEED_SYNTHHEADER_2:
+ case _VORBIS_NEED_SYNTHHEADER_3:
+ cout << "_VORBIS_NEED_SYNTHHEADER:"<<initState<<endl;
+ if(vorbis_synthesis_headerin(&vi,&vc,op)<0){
+ /* error case; not a vorbis header */
+ fprintf(stderr,"This Ogg bitstream does not contain Vorbis "
+ "audio data.\n");
+ exit(1);
+ }
+ initState++;
+ break;
+ case _VORBIS_DECODE_SETUP:
+ cout << "_VORBIS_DECODE_SETUP"<<endl;
+ vorbis_synthesis_init(&vd,&vi); /* central decode state */
+ vorbis_block_init(&vd,&vb); /* local state for most of the decode
+ so multiple block decodes can
+ proceed in parallel. We could init
+ multiple vorbis_block structures
+ for vd here */
+ initState=_VORBIS_DECODE_LOOP;
+ // yes right, we must decode the packet!
+ // so there is no break here.
+ case _VORBIS_DECODE_LOOP: {
+ if(vorbis_synthesis(&vb,op)==0) {/* test for success! */
+ vorbis_synthesis_blockin(&vd,&vb);
+ } else {
+ cout << "vorbis_synthesis error"<<endl;
+ exit(0);
+ }
+ float **pcm;
+ /*
+
+ **pcm is a multichannel float vector. In stereo, for
+ example, pcm[0] is left, and pcm[1] is right. samples is
+ the size of each channel. Convert the float values
+ (-1.<=range<=1.) to whatever PCM format and write it out
+
+ */
+ int samples=vorbis_synthesis_pcmout(&vd,&pcm);
+ if (samples > 0) {
+ int maxSamples=dest->getSize();
+ if (samples > maxSamples) {
+ cout << "more samples in vorbis than we can store"<<endl;
+ exit(0);
+ }
+ dest->clearrawdata();
+ dest->setFrameFormat(vi.channels-1,vi.rate);
+
+ if (vi.channels == 2) {
+ dest->putFloatData(pcm[0],pcm[1],samples);
+ } else {
+ dest->putFloatData(pcm[0],NULL,samples);
+ }
+
+ vorbis_synthesis_read(&vd,samples); /* tell libvorbis how
+ many samples we
+ actually consumed */
+ return true;
+ }
+
+ return false;
+ }
+ default:
+ cout << "unknown state in vorbis decoder"<<endl;
+ exit(0);
+ }
+ return false;
+}
+
+void VorbisDecoder::reset() {
+ initState=_VORBIS_NEED_SYNTHHEADER_1;
+}
+
+void VorbisDecoder::config(const char* ,const char* ,void* ) {
+
+}
+
+#endif
diff --git a/mpeglib/lib/oggvorbis/vorbisDecoder.h b/mpeglib/lib/oggvorbis/vorbisDecoder.h
new file mode 100644
index 00000000..e67a94bb
--- /dev/null
+++ b/mpeglib/lib/oggvorbis/vorbisDecoder.h
@@ -0,0 +1,53 @@
+/*
+ converts ogg frames into audioFrames
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __VORBISDECODER_H
+#define __VORBISDECODER_H
+
+#include "../frame/audioFrame.h"
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef OGG_VORBIS
+
+#include <vorbis/codec.h>
+#include "oggFrame.h"
+
+class VorbisDecoder {
+
+ vorbis_info vi; /* struct that stores all the static vorbis bitstream
+ settings */
+ vorbis_comment vc; /* struct that stores all the bitstream user comments */
+ vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
+ vorbis_block vb; /* local working space for packet->PCM decode */
+
+ int initState;
+
+ public:
+ VorbisDecoder();
+ ~VorbisDecoder();
+
+ void reset();
+ int hasHeader();
+
+ int decode(RawFrame* rawFrame,AudioFrame* dest);
+ void config(const char* key,const char* val,void* ret);
+};
+
+#endif
+
+#endif
diff --git a/mpeglib/lib/oggvorbis/vorbisInfo.cpp b/mpeglib/lib/oggvorbis/vorbisInfo.cpp
new file mode 100644
index 00000000..b71f99c3
--- /dev/null
+++ b/mpeglib/lib/oggvorbis/vorbisInfo.cpp
@@ -0,0 +1,150 @@
+/*
+ info about vorbis files.
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "vorbisInfo.h"
+
+#include <iostream>
+
+using namespace std;
+
+#ifdef OGG_VORBIS
+#define CHUNKSIZE 4096
+
+#define GETINPUT(stream,input) \
+ VorbisInfo* info=(VorbisInfo*) stream; \
+ FileAccess* input=info->getInput();
+
+
+size_t fread_func2(void *ptr, size_t size, size_t nmemb, void *stream) {
+ GETINPUT(stream,input);
+
+ size_t want=size*nmemb;
+ size_t back=input->read((char*)ptr,want);
+ return back;
+}
+
+
+int fseek_func2(void *stream, ogg_int64_t offset, int whence) {
+ int ret;
+ GETINPUT(stream,input);
+
+ if (whence==SEEK_SET) {
+ ret=input->seek(offset);
+ info->setSeekPos(offset);
+ return ret;
+ }
+ if (whence==SEEK_CUR) {
+ ret=input->seek(input->getBytePosition()+offset);
+ return ret;
+ }
+ if (whence==SEEK_END) {
+ ret=input->seek(input->getByteLength());
+ return ret;
+ }
+ cout << "hm, strange call"<<endl;
+ return -1;
+}
+
+
+int fclose_func2 (void * stream) {
+ cout << "fclose_func"<<endl;
+ GETINPUT(stream,input);
+ // its handled different in kmpg
+ // we close the stream if the decoder signals eof.
+ return true;
+
+}
+
+
+long ftell_func2 (void *stream) {
+ GETINPUT(stream,input);
+ return input->getBytePosition();
+}
+
+
+
+
+VorbisInfo::VorbisInfo(FileAccess* input) {
+ this->input=input;
+ vf=new OggVorbis_File();
+
+ ov_callbacks callbacks;
+
+ callbacks.read_func = fread_func2;
+ callbacks.seek_func = fseek_func2;
+ callbacks.close_func = fclose_func2;
+ callbacks.tell_func = ftell_func2;
+
+ if(ov_open_callbacks(this, vf, NULL, 0, callbacks) < 0) {
+ cout << "error ov_open_callbacks"<<endl;
+ }
+
+ // now init stream
+ vi=ov_info(vf,-1);
+ lastSeekPos=0;
+}
+
+
+VorbisInfo::~VorbisInfo() {
+ delete vf;
+ if (vi != NULL) {
+ //?
+ }
+}
+
+
+long VorbisInfo::getSeekPosition(int seconds) {
+ int back=0;
+ if (vi != NULL) {
+ lastSeekPos=0;
+ ov_time_seek(vf,seconds);
+ back=lastSeekPos;
+ }
+ return back;
+}
+
+
+long VorbisInfo::getLength() {
+ int back=0;
+ if (vi != NULL) {
+ back = (int) ov_time_total(vf, -1);
+ }
+ return back;
+}
+
+
+
+
+void VorbisInfo::print(const char* msg) {
+ cout << "VorbisInfo:"<<msg<<endl;
+ cout << "Length (sec):"<<getLength()<<endl;
+
+}
+
+
+void VorbisInfo::setSeekPos(long pos) {
+ this->lastSeekPos=pos;
+}
+
+
+long VorbisInfo::getSeekPos() {
+ return lastSeekPos;
+}
+
+FileAccess* VorbisInfo::getInput() {
+ return input;
+}
+
+
+
+#endif
diff --git a/mpeglib/lib/oggvorbis/vorbisInfo.h b/mpeglib/lib/oggvorbis/vorbisInfo.h
new file mode 100644
index 00000000..70c1dc8a
--- /dev/null
+++ b/mpeglib/lib/oggvorbis/vorbisInfo.h
@@ -0,0 +1,76 @@
+/*
+ info about vorbis files.
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __VORBISINFO_H
+#define __VORBISINFO_H
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef OGG_VORBIS
+
+#include <vorbis/codec.h>
+#include <vorbis/vorbisfile.h>
+#include "../util/file/fileAccess.h"
+
+/**
+ callbacks from vorbisfile
+*/
+extern "C" {
+
+extern size_t fread_func2 (void *ptr,size_t size,size_t nmemb, void *stream);
+extern int fseek_func2 (void *stream, ogg_int64_t offset, int whence);
+extern int fclose_func2 (void *stream);
+extern long ftell_func2 (void *stream);
+
+}
+
+
+
+class VorbisInfo {
+
+ FileAccess* input;
+ OggVorbis_File* vf;
+ vorbis_info *vi;
+
+ long lastSeekPos;
+
+ public:
+ VorbisInfo(FileAccess* input);
+ ~VorbisInfo();
+
+ // returns byte positions
+ long getSeekPosition(int second);
+ // returns length in seconds
+ long getLength();
+
+ void print(const char* msg);
+
+ void setSeekPos(long pos);
+ long getSeekPos();
+
+ FileAccess* getInput();
+
+
+};
+
+#endif
+
+#endif
diff --git a/mpeglib/lib/output/Makefile.am b/mpeglib/lib/output/Makefile.am
new file mode 100644
index 00000000..e430550e
--- /dev/null
+++ b/mpeglib/lib/output/Makefile.am
@@ -0,0 +1,52 @@
+# liboutplugin - Makefile.am
+
+INCLUDES = $(all_includes)
+
+
+noinst_LTLIBRARIES = liboutput.la
+
+noinst_HEADERS = windowOut.h \
+ audioData.h audioDataArray.h \
+ performance.h yuvDumper.h
+
+kmpgincludedir = $(includedir)/$(THIS_LIB_NAME)/output
+
+kmpginclude_HEADERS = outputStream.h pluginInfo.h \
+ outPlugin.h dspX11OutputStream.h \
+ artsOutputStream.h audioTime.h \
+ avSyncer.h threadSafeOutputStream.h
+
+
+liboutput_la_SOURCES = outPlugin.cpp outputStream.cpp \
+ dspX11OutputStream.cpp \
+ windowOut.cpp \
+ audioTime.cpp \
+ audioData.cpp audioDataArray.cpp \
+ avSyncer.cpp performance.cpp \
+ artsOutputStream.cpp \
+ pluginInfo.cpp yuvDumper.cpp \
+ threadSafeOutputStream.cpp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/lib/output/artsOutputStream.cpp b/mpeglib/lib/output/artsOutputStream.cpp
new file mode 100644
index 00000000..571dd258
--- /dev/null
+++ b/mpeglib/lib/output/artsOutputStream.cpp
@@ -0,0 +1,205 @@
+/*
+ output to arts
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "artsOutputStream.h"
+#include "windowOut.h"
+#include "avSyncer.h"
+#include "../util/abstract/threadQueue.h"
+
+
+ArtsOutputStream::ArtsOutputStream(void (*streamStateChangeCallback)(void*)) {
+
+ audioTime=new AudioTime();
+ x11Window=new WindowOut();
+ privateBufferSize=1024*32;
+
+ // we can dynamically change the buffer size,
+ // over a config switch in avSyncer
+ // but for now this should be ok
+ // Arts must pass the total size of the audiobuffer
+ // from /dev/dsp + connected nodes
+ // or arts need a video interface
+ stream=new BufferInputStream(privateBufferSize,1024*64,"artsLoopback");
+ avSyncer=new AVSyncer(privateBufferSize);
+
+ // with the ThreadQueue we protect all calls to the
+ // x11 output. This is necessary, because arts may call
+ // in the future "config" entries to switch the video
+ // mode (full,double,desktop)
+ threadQueue=new ThreadQueue();
+ // we set it, but its never used !
+ this->streamStateChangeCallback=streamStateChangeCallback;
+}
+
+
+ArtsOutputStream::~ArtsOutputStream() {
+ delete stream;
+ delete audioTime;
+ delete x11Window;
+ delete avSyncer;
+ delete threadQueue;
+}
+
+
+
+
+int ArtsOutputStream::audioSetup(int freq,int stereo,
+ int sign,int big,int sampleSize) {
+ audioTime->setFormat(stereo,sampleSize,freq,sign,big);
+ avSyncer->audioSetup(freq,stereo,sign,big,sampleSize);
+
+ OutputStream::audioSetup(freq,stereo,sign,big,sampleSize);
+
+ return true;
+}
+
+AudioTime* ArtsOutputStream::getAudioTime() {
+ return audioTime;
+}
+
+
+void ArtsOutputStream::audioClose() {
+ audioTime->setTime(0.0);
+ // if we close this stream the thread is kicked out.
+ stream->close();
+ stream->clear();
+ avSyncer->audioClose();
+}
+
+void ArtsOutputStream::audioFlush() {
+ OutputStream::audioFlush();
+ audioClose();
+}
+
+
+void ArtsOutputStream::audioOpen() {
+ audioTime->setTime(0.0);
+ // if we close this stream the thread is kicked out.
+ stream->open("artsLoopback");
+}
+
+
+int ArtsOutputStream::audioPlay(TimeStamp* startStamp,
+ TimeStamp* endStamp,char *buffer, int size) {
+ int write=size;
+
+
+ // we call this for stream state handling
+ OutputStream::audioPlay(startStamp,endStamp,buffer,size);
+
+ // store sync information
+ avSyncer->audioPlay(startStamp,endStamp,buffer,size);
+
+
+ // here we simulate our own blocking audio device
+ if (stream->isOpen()==false) {
+ audioTime->sleepWrite(size);
+ } else {
+ write=stream->write(buffer,size,startStamp);
+ }
+ return write;
+
+}
+
+
+int ArtsOutputStream::read(char** buffer,int bytes) {
+ int back=stream->readRemote(buffer,bytes);
+ return back;
+}
+
+
+void ArtsOutputStream::forwardReadPtr(int bytes) {
+ audioTime->forwardTime(bytes);
+ stream->forwardReadPtr(bytes);
+
+}
+
+
+
+
+int ArtsOutputStream::getPreferredDeliverSize() {
+ return avSyncer->getPreferredDeliverSize();
+}
+
+
+int ArtsOutputStream::openWindow(int width, int height,const char *title) {
+ threadQueue->waitForExclusiveAccess();
+ int back=x11Window->openWindow(width,height,title);
+ threadQueue->releaseExclusiveAccess();
+
+ return back;
+}
+
+int ArtsOutputStream::x11WindowId() {
+ return x11Window->x11WindowId();
+}
+
+void ArtsOutputStream::closeWindow() {
+ threadQueue->waitForExclusiveAccess();
+ x11Window->closeWindow();
+ threadQueue->releaseExclusiveAccess();
+}
+
+void ArtsOutputStream::flushWindow() {
+ threadQueue->waitForExclusiveAccess();
+ x11Window->flushWindow();
+ threadQueue->releaseExclusiveAccess();
+}
+
+
+PictureArray* ArtsOutputStream::lockPictureArray() {
+ PictureArray* back;
+ threadQueue->waitForExclusiveAccess();
+ back=x11Window->lockPictureArray();
+ threadQueue->releaseExclusiveAccess();
+ return back;
+}
+
+
+void ArtsOutputStream::unlockPictureArray(PictureArray* pictureArray) {
+
+
+ YUVPicture* pic=pictureArray->getYUVPictureCallback();
+ if (avSyncer->syncPicture(pic)==false) {
+ return;
+ }
+ threadQueue->waitForExclusiveAccess();
+ x11Window->unlockPictureArray(pictureArray);
+ threadQueue->releaseExclusiveAccess();
+
+}
+
+int ArtsOutputStream::getBufferFillgrade() {
+ return stream->getFillgrade();
+}
+
+int ArtsOutputStream::getFrameusec() {
+ return avSyncer->getFrameusec();
+}
+
+
+
+void ArtsOutputStream::setAudioBufferSize(int size) {
+ avSyncer->setAudioBufferSize(privateBufferSize+size);
+}
+
+
+void ArtsOutputStream::config(const char* key,const char* value,
+ void* user_data) {
+
+ threadQueue->waitForExclusiveAccess();
+ avSyncer->config(key,value,user_data);
+ x11Window->config(key,value,user_data);
+ threadQueue->releaseExclusiveAccess();
+}
diff --git a/mpeglib/lib/output/artsOutputStream.h b/mpeglib/lib/output/artsOutputStream.h
new file mode 100644
index 00000000..757022fd
--- /dev/null
+++ b/mpeglib/lib/output/artsOutputStream.h
@@ -0,0 +1,123 @@
+/*
+ output to arts
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __ARTSOUTPUTSTREAM_H
+#define __ARTSOUTPUTSTREAM_H
+
+#include "outputStream.h"
+#include "../input/bufferInputStream.h"
+#include "audioTime.h"
+
+
+class WindowOut;
+class AVSyncer;
+
+
+
+
+/**
+ a word about synchronisation: Arts currently has not asynchronous
+ starting of stream, which means, poll if stream is initialized
+ or set a callbackfunction.
+
+ mpeglib is threaded and would not have much problems
+ with a "callback" but currently we only support blocking wait
+ for initilisation.
+
+ I think, if people realize that smooth mixing of streams
+ is impossible(because of the blocking start behaviour),
+ this will become a desired feature :-)
+
+*/
+
+
+/**
+ This class offers a blocking device, its behaviour is similar
+ to /dev/dsp we use a buffer to store the pcm data, if
+ we open/close the buffer the stored data is removed
+ and the blocking thread is "kicked"
+
+ Arts must first wait for an init signal. it has its own
+ method to check for eof. during seek we close this
+ device, after seek we reopen it.
+
+ close and open have the same functionality, they simply
+ clear the buffer.
+*/
+
+
+
+class ArtsOutputStream : public OutputStream {
+
+ BufferInputStream* stream;
+ AudioTime* audioTime;
+
+ void (*streamStateChangeCallback)(void*);
+
+ WindowOut* x11Window;
+ AVSyncer* avSyncer;
+ int privateBufferSize;
+ class ThreadQueue* threadQueue;
+
+ public:
+ ArtsOutputStream(void (*streamStateChangeCallback)(void*));
+ ~ArtsOutputStream();
+
+ // Audio part
+
+ int audioSetup(int freq,int stereo,int sign,int big,int sampleSize);
+ void audioClose();
+ void audioOpen();
+ void audioFlush();
+
+ int audioPlay(TimeStamp* startStamp,
+ TimeStamp* endStamp,char *buffer, int size);
+
+
+ AudioTime* getAudioTime();
+
+ int getPreferredDeliverSize();
+
+ // Video part
+
+ int openWindow(int width, int height,const char *title);
+ int x11WindowId();
+ void closeWindow();
+ void flushWindow();
+
+ PictureArray* lockPictureArray();
+ void unlockPictureArray(PictureArray* pictureArray);
+
+ int getFrameusec();
+
+ void config(const char* key,const char* value,void* user_data);
+
+ // Remote read extension
+
+ int read(char** buffer,int bytes);
+ void forwardReadPtr(int bytes);
+
+ // buffer control
+ int getBufferFillgrade();
+
+ // sync control
+ void setAudioBufferSize(int size);
+
+ private:
+ void sendSignal(int state,int value);
+ void initStream();
+
+};
+#endif
diff --git a/mpeglib/lib/output/audioData.cpp b/mpeglib/lib/output/audioData.cpp
new file mode 100644
index 00000000..d28e74cf
--- /dev/null
+++ b/mpeglib/lib/output/audioData.cpp
@@ -0,0 +1,100 @@
+/*
+ describes a paket oriented audioData, because Im fed up
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#include "audioData.h"
+
+#include <iostream>
+
+using namespace std;
+
+AudioData::AudioData() {
+ pcmLen=0;
+ audioTime=new AudioTime();
+ start=new TimeStamp();
+ end=new TimeStamp();
+ writeStamp=new TimeStamp();
+}
+
+
+AudioData::~AudioData() {
+ delete audioTime;
+ delete start;
+ delete end;
+ delete writeStamp;
+}
+
+
+void AudioData::copyTo(AudioData* dest) {
+ dest->setAudioTime(getAudioTime());
+ dest->setStart(getStart());
+ dest->setEnd(getEnd());
+ dest->setWrite(getWrite());
+ dest->setPCMLen(getPCMLen());
+}
+
+
+int AudioData::getPCMLen(){
+ return pcmLen;
+}
+
+void AudioData::setPCMLen(int pcmLen) {
+ this->pcmLen=pcmLen;
+}
+
+void AudioData::setAudioTime(AudioTime* aTime) {
+ aTime->copyTo(audioTime);
+}
+
+AudioTime* AudioData::getAudioTime() {
+ return audioTime;
+}
+
+
+void AudioData::setStart(TimeStamp* s) {
+ s->copyTo(start);
+}
+
+
+TimeStamp* AudioData::getStart() {
+ return start;
+}
+
+void AudioData::setEnd(TimeStamp* e) {
+ e->copyTo(end);
+}
+
+
+TimeStamp* AudioData::getEnd() {
+ return end;
+}
+
+void AudioData::setWrite(TimeStamp* e) {
+ e->copyTo(writeStamp);
+}
+
+
+TimeStamp* AudioData::getWrite() {
+ return writeStamp;
+}
+
+
+
+void AudioData::print() {
+ cout << "AudioData::print [START]"<<endl;
+ start->print("audioData start");
+ end->print("audioData end");
+ cout << "pcmlen:"<<pcmLen<<endl;
+ cout << "AudioData::print [END]"<<endl;
+}
diff --git a/mpeglib/lib/output/audioData.h b/mpeglib/lib/output/audioData.h
new file mode 100644
index 00000000..ff6c8242
--- /dev/null
+++ b/mpeglib/lib/output/audioData.h
@@ -0,0 +1,71 @@
+/*
+ describes a paket oriented audioData, because Im fed up
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __AUDIODATA_H
+#define __AUDIODATA_H
+
+#include "../util/timeStamp.h"
+#include "audioTime.h"
+
+
+#define _AUDIODATA_MAX_SIZE 8192
+
+
+/**
+ My attempt to encapsulate all this stupid things which deals
+ with timing, synchronisation, length, stamps and everything
+ unimaginable else.
+*/
+
+
+
+class AudioData {
+
+
+ AudioTime* audioTime;
+ TimeStamp* start;
+ TimeStamp* end;
+ TimeStamp* writeStamp;
+
+ int pcmLen;
+
+ public:
+ AudioData();
+ ~AudioData();
+
+ void copyTo(AudioData* dest);
+
+
+ int getPCMLen();
+ void setPCMLen(int pcmLen);
+
+ void setAudioTime(AudioTime* audioTime);
+ AudioTime* getAudioTime();
+
+ void setStart(TimeStamp* start);
+ TimeStamp* getStart();
+
+ void setWrite(TimeStamp* writeStamp);
+ TimeStamp* getWrite();
+
+
+ void setEnd(TimeStamp* end);
+ TimeStamp* getEnd();
+
+ void print();
+};
+#endif
+
+
+
diff --git a/mpeglib/lib/output/audioDataArray.cpp b/mpeglib/lib/output/audioDataArray.cpp
new file mode 100644
index 00000000..a0b91ec1
--- /dev/null
+++ b/mpeglib/lib/output/audioDataArray.cpp
@@ -0,0 +1,138 @@
+/*
+ fifo for audioData
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "audioDataArray.h"
+
+#include <iostream>
+
+using namespace std;
+
+
+AudioDataArray::AudioDataArray(int entries) {
+
+ this->entries=entries;
+ fillgrade=0;
+ readPos=0;
+ writePos=0;
+ pcmSum=0;
+ abs_thread_mutex_init(&writeInMut);
+ abs_thread_mutex_init(&changeMut);
+
+ audioDataArray=new AudioData*[entries];
+
+ int i;
+ for(i=0;i<entries;i++) {
+ audioDataArray[i]=new AudioData();
+ }
+
+ abs_thread_mutex_init(&writeInMut);
+ abs_thread_mutex_init(&changeMut);
+
+}
+
+AudioDataArray::~AudioDataArray() {
+
+ int i;
+ for(i=0;i<entries;i++) {
+ delete audioDataArray[i];
+ }
+
+ delete audioDataArray;
+
+ abs_thread_mutex_destroy(&writeInMut);
+ abs_thread_mutex_destroy(&changeMut);
+
+}
+
+
+
+
+void AudioDataArray::lockStampArray() {
+
+ abs_thread_mutex_lock(&changeMut);
+ abs_thread_mutex_lock(&writeInMut);
+ abs_thread_mutex_unlock(&changeMut);
+
+}
+
+
+void AudioDataArray::unlockStampArray() {
+ abs_thread_mutex_unlock(&writeInMut);
+}
+
+
+void AudioDataArray::internalForward() {
+ pcmSum=pcmSum-readAudioData()->getPCMLen();
+ readPos++;
+ fillgrade--;
+ if (readPos == entries-1) {
+ readPos=0;
+ }
+}
+
+int AudioDataArray::getPCMSum() {
+ return pcmSum;
+}
+
+
+
+int AudioDataArray::insertAudioData(AudioData* src) {
+ lockStampArray();
+
+ int back=true;
+ src->copyTo(audioDataArray[writePos]);
+ pcmSum+=src->getPCMLen();
+
+ writePos++;
+ fillgrade++;
+ if (writePos == entries-1) {
+ writePos=0;
+ }
+ if (fillgrade == entries) {
+ cout <<" Audiodata::array overfull forward"<<endl;
+ internalForward();
+ back=false;
+ }
+ unlockStampArray();
+ return back;
+}
+
+
+AudioData* AudioDataArray::readAudioData() {
+ return audioDataArray[readPos];
+}
+
+
+int AudioDataArray::getFillgrade() {
+ return fillgrade;
+}
+
+
+void AudioDataArray::forward() {
+ lockStampArray();
+ internalForward();
+ unlockStampArray();
+}
+
+
+void AudioDataArray::clear() {
+ lockStampArray();
+ fillgrade=0;
+ readPos=0;
+ writePos=0;
+ pcmSum=0;
+ unlockStampArray();
+}
+
+
diff --git a/mpeglib/lib/output/audioDataArray.h b/mpeglib/lib/output/audioDataArray.h
new file mode 100644
index 00000000..81cebfee
--- /dev/null
+++ b/mpeglib/lib/output/audioDataArray.h
@@ -0,0 +1,55 @@
+/*
+ fifo for audioData
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __AUDIODATAARRAY_H
+#define __AUDIODATAARRAY_H
+
+#include "../util/abstract/abs_thread.h"
+#include "audioData.h"
+
+
+class AudioDataArray {
+
+ AudioData** audioDataArray;
+ int fillgrade;
+ int entries;
+
+ int writePos;
+ int readPos;
+ int pcmSum;
+
+ public:
+ AudioDataArray(int entries);
+ ~AudioDataArray();
+
+ int insertAudioData(AudioData* src);
+ AudioData* readAudioData();
+
+ int getFillgrade();
+ int getPCMSum();
+ void forward();
+ void clear();
+
+ private:
+ void lockStampArray();
+ void unlockStampArray();
+ void internalForward();
+
+ abs_thread_mutex_t writeInMut;
+ abs_thread_mutex_t changeMut;
+
+
+
+};
+#endif
diff --git a/mpeglib/lib/output/audioTime.cpp b/mpeglib/lib/output/audioTime.cpp
new file mode 100644
index 00000000..f4ed2e9c
--- /dev/null
+++ b/mpeglib/lib/output/audioTime.cpp
@@ -0,0 +1,154 @@
+/*
+ defines the format of an audio stream
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "audioTime.h"
+
+#include <iostream>
+
+using namespace std;
+
+
+AudioTime::AudioTime(){
+ time=0.0;
+}
+
+
+AudioTime::~AudioTime() {
+}
+
+
+void AudioTime::setFormat(int stereo,int samplesize,int speed,int sign, int bigendian) {
+ setStereo(stereo);
+ setSampleSize(samplesize);
+ setSpeed(speed);
+ setSign(sign);
+ setBigendian(bigendian);
+}
+
+
+int AudioTime::getStereo() {
+ return stereo;
+}
+
+
+int AudioTime::getSampleSize() {
+ return samplesize;
+}
+
+
+int AudioTime::getSpeed() {
+ return speed;
+}
+
+int AudioTime::getSign() {
+ return _sign;
+}
+
+int AudioTime::getBigendian() {
+ return _bigendian;
+}
+
+void AudioTime::setStereo(int stereo) {
+ this->stereo=stereo;
+}
+
+
+void AudioTime::setSampleSize(int samplesize) {
+ this->samplesize=samplesize;
+}
+
+
+void AudioTime::setSpeed(int speed) {
+ this->speed=speed;
+}
+
+void AudioTime::setSign(int sign) {
+ this->_sign=sign;
+}
+
+void AudioTime::setBigendian(int bigendian) {
+ this->_bigendian = bigendian;
+}
+
+float AudioTime::getTime() {
+ return time;
+}
+
+
+void AudioTime::setTime(float time) {
+ this->time=time;
+}
+
+
+void AudioTime::forwardTime(int bytes){
+ time+=calculateTime(bytes);
+}
+
+
+float AudioTime::calculateTime(int bytes) {
+ float back=0;
+
+ bytes=bytes/(samplesize/8);
+ if (stereo==1) {
+ bytes=bytes/2;
+ }
+ if (speed != 0) {
+ back=(float)bytes/(float)speed;
+ }
+ return back;
+}
+
+
+/**
+ How much byte we need for time usecs?
+*/
+int AudioTime::calculateBytes(float time) {
+ float back=time;
+
+ if (speed != 0) {
+ back=back*(float)speed;
+ }
+ back=back*(float)(samplesize/8);
+ if (stereo==1) {
+ back=back*2.0;
+ }
+ return (int)back;
+}
+
+
+
+void AudioTime::sleepWrite(int size) {
+ timeval_t time;
+ float timeLength=calculateTime(size);
+ time.tv_sec=(long)timeLength;
+ time.tv_usec=(long)(1000000*(timeLength-time.tv_sec));
+ TimeWrapper::usleep(&time);
+}
+
+
+void AudioTime::print() {
+ cout << "AudioTime-begin-"<<endl;
+ cout << "stereo:"<<getStereo()<<" sampleSize:"<<getSampleSize()
+ << " speed: "<<getSpeed()<<endl;
+
+ cout << "AudioTime-end-"<<endl;
+
+}
+
+
+void AudioTime::copyTo(AudioTime* dest) {
+ dest->setStereo(getStereo());
+ dest->setSampleSize(getSampleSize());
+ dest->setSpeed(getSpeed());
+}
+
diff --git a/mpeglib/lib/output/audioTime.h b/mpeglib/lib/output/audioTime.h
new file mode 100644
index 00000000..3b16bb02
--- /dev/null
+++ b/mpeglib/lib/output/audioTime.h
@@ -0,0 +1,68 @@
+/*
+ defines the format of an audio stream
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __AUDIOTIME_H
+#define __AUDIOTIME_H
+
+#include "../util/timeWrapper.h"
+
+class AudioTime {
+
+ int stereo;
+ int samplesize;
+ int speed;
+ int _sign;
+ int _bigendian;
+ float time;
+
+ public:
+ AudioTime();
+ ~AudioTime();
+
+ //cd-quality:true,16,44100
+ void setFormat(int stereo,int samplesize,int speed,int sign=true, int bigendian=false);
+
+ int getStereo();
+ int getSampleSize();
+ int getSpeed();
+ int getSign();
+ int getBigendian();
+
+ void setStereo(int stereo);
+ void setSampleSize(int samplesize);
+ void setSpeed(int speed);
+ void setSign(int sign);
+ void setBigendian(int bigendian);
+
+
+ float getTime();
+ void setTime(float time);
+ void forwardTime(int bytes);
+
+ // do not modify the internal time
+ float calculateTime(int bytes);
+ int calculateBytes(float time);
+
+ void sleepWrite(int size);
+
+
+ void copyTo(AudioTime* audioTime);
+ void print();
+
+};
+
+
+
+#endif
diff --git a/mpeglib/lib/output/avSyncer.cpp b/mpeglib/lib/output/avSyncer.cpp
new file mode 100644
index 00000000..ca12a21e
--- /dev/null
+++ b/mpeglib/lib/output/avSyncer.cpp
@@ -0,0 +1,386 @@
+/*
+ encapsulates the syncing methods, to use it in other classes
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "avSyncer.h"
+
+#include "audioDataArray.h"
+#include "performance.h"
+
+#include <iostream>
+
+using namespace std;
+
+AVSyncer::AVSyncer(int bufferSize) {
+ oneFrameTime=0;
+ onePicFrameInAudioBytes=8192;
+ this->bufferSize=bufferSize;
+
+ abs_thread_mutex_init(&writeInMut);
+ abs_thread_mutex_init(&changeMut);
+
+
+ audioDataInsert=new AudioData();
+ audioDataArray=new AudioDataArray(400);
+ audioDataCurrent=audioDataArray->readAudioData();
+
+ startAudio=new TimeStamp();
+ endAudio=new TimeStamp();
+ audioTime=new AudioTime();
+ lAudioRunning=false;
+ performance=new Performance();
+
+ waitTime=new TimeStamp();
+ diffTime=new TimeStamp();
+ videoTimeStamp=new TimeStamp();
+
+ lPerformance=false;
+ lavSync=true;
+
+
+}
+
+
+AVSyncer::~AVSyncer() {
+ delete audioDataArray;
+ delete audioDataInsert;
+ delete audioTime;
+ delete startAudio;
+ delete endAudio;
+ abs_thread_mutex_destroy(&writeInMut);
+ abs_thread_mutex_destroy(&changeMut);
+ delete waitTime;
+ delete diffTime;
+ delete performance;
+ delete videoTimeStamp;
+}
+
+
+
+int AVSyncer::audioSetup(int frequency,int stereo,int sign,
+ int big,int sixteen) {
+ audioTime->setFormat(stereo,sixteen,frequency,sign,big);
+ setAudioRunning(true);
+ return true;
+}
+
+
+int AVSyncer::audioPlay(TimeStamp* startStamp,
+ TimeStamp* endStamp,char* , int size) {
+
+
+
+
+ audioDataInsert->setStart(startStamp);
+ audioDataInsert->setEnd(endStamp);
+ audioDataInsert->setAudioTime(audioTime);
+ audioDataInsert->setPCMLen(size);
+
+ setAudioSync(audioDataInsert);
+ return size;
+}
+
+
+
+void AVSyncer::audioClose(void) {
+ lockSyncData();
+ setAudioRunning(false);
+ audioDataArray->clear();
+ unlockSyncData();
+}
+
+
+
+
+
+int AVSyncer::getPreferredDeliverSize() {
+ return onePicFrameInAudioBytes;
+}
+
+
+
+int AVSyncer::getFrameusec() {
+ lockSyncData();
+ int back=oneFrameTime;
+ unlockSyncData();
+ return back;
+}
+
+void AVSyncer::setAudioBufferSize(int size) {
+ bufferSize=size;
+}
+
+
+void AVSyncer::config(const char* key,const char* value,void*) {
+ if (strcmp(key,"-s")==0) {
+ if (strcmp(value,"on")==0) {
+ lavSync=true;
+ cout << "******** lavSync on"<<endl;
+ } else {
+ lavSync=false;
+ cout << "******** lavSync off"<<endl;
+ }
+ }
+ if (strcmp(key,"-p")==0) {
+ cout << "setting perfomance test true"<<endl;
+ lPerformance=true;
+ }
+
+}
+
+
+
+void AVSyncer::setAudioSync(AudioData* audioData) {
+
+ lockSyncData();
+
+ if (onePicFrameInAudioBytes <= 0) {
+ // no video present, we cannot calculate audio
+ // pcm length
+ //cout << "no video present, we cannot calculate audio pcm length"<<endl;
+ unlockSyncData();
+ return;
+ }
+ // buffersize is the simulated size of /dev/dsp
+
+
+
+ // we implement a fifo hopefully
+ // we check the right audio pts time out
+ // when /dev/dsp acutally plays it.
+ audioDataArray->insertAudioData(audioData);
+ int pcmSum=audioDataArray->getPCMSum();
+ if (pcmSum >= bufferSize) {
+ audioDataCurrent=audioDataArray->readAudioData();
+ setAudioRunning(true);
+ audioDataArray->forward();
+ }
+ TimeStamp* startAudio=audioDataCurrent->getStart();
+ int lpts=startAudio->getPTSFlag();
+
+ if (lpts==true) {
+ SyncClock* syncClock=startAudio->getSyncClock();
+ if (syncClock != NULL) {
+ double pts=startAudio->getPTSTimeStamp();
+ double scr=startAudio->getSCRTimeStamp();
+
+ syncClock->syncAudio(pts,scr);
+
+ } else {
+ cout <<"syncClock == NULL (audio)"<<endl;
+ }
+ } else {
+ //cout << "lpts is false"<<endl;
+ }
+ unlockSyncData();
+
+}
+
+
+
+int AVSyncer::syncPicture(YUVPicture* syncPic) {
+ if (syncPic == NULL) {
+ cout << "syncPic == NULL"<<endl;
+ return false;
+ }
+
+ float picPerSec=syncPic->getPicturePerSecond();
+ int oneFrameTime=0;
+
+ if (picPerSec > 0.0) {
+ oneFrameTime=(int) (1000000.0/picPerSec);
+ } else {
+ syncPic->print("picPersec is 0");
+ return true;
+ }
+
+ if (lPerformance==true) {
+ waitTime->set(0,0);
+ syncPic->setWaitTime(waitTime);
+ performance->incPictureCount();
+ return true;
+ }
+
+ int lpacketSync=true;
+
+ videoTimeStamp->gettimeofday();
+ diffTime->minus(videoTimeStamp,videoTimeStamp);
+
+
+ if (lavSync==false) {
+ if (videoTimeStamp->isNegative()) {
+ diffTime->gettimeofday();
+ diffTime->addOffset(0,oneFrameTime);
+ cout << "skip time based"<<endl;
+ return false;
+ }
+ }
+
+
+
+ videoTimeStamp->copyTo(waitTime);
+
+ TimeStamp* earlyTime=syncPic->getEarlyTime();
+ earlyTime->set(0,0);
+
+
+ if (lavSync) {
+
+ lpacketSync=avSync(syncPic->getStartTimeStamp(),
+ waitTime,
+ earlyTime,
+ syncPic->getPicturePerSecond());
+
+ }
+
+
+
+
+ if (lpacketSync == false) {
+ //cout << "skip"<<endl;
+ diffTime->gettimeofday();
+ diffTime->addOffset(0,oneFrameTime);
+ return false;
+ }
+
+ syncPic->setWaitTime(waitTime);
+
+ if (lavSync) {
+ waitTime->minus(videoTimeStamp,waitTime);
+ if (waitTime->isPositive()) {
+ diffTime->addOffset(waitTime);
+ }
+ }
+ diffTime->addOffset(0,oneFrameTime);
+ return true;
+
+
+}
+
+
+
+/**
+ Heart of the sync routine is here!
+
+ Currently its more in a state of "try/test"
+
+
+*/
+int AVSyncer::avSync(TimeStamp* startVideo,
+ TimeStamp* waitTime,
+ TimeStamp* earlyTime,
+ float picPerSec ) {
+
+
+ double videoStartPTSTime=startVideo->getPTSTimeStamp();
+ double videoStartSCRTime=startVideo->getSCRTimeStamp();
+ int videoFrameCounter=startVideo->getVideoFrameCounter();
+ double frameTime=0.0;
+
+
+
+ lockSyncData();
+ if (picPerSec > 0) {
+ oneFrameTime=(long)(1000000.0/picPerSec);
+ frameTime=1.0/picPerSec;
+ onePicFrameInAudioBytes=audioTime->calculateBytes(1.0/picPerSec);
+
+ }
+ if (lAudioRunning==false) {
+ waitTime->set(0,oneFrameTime);
+
+ unlockSyncData();
+ return true;
+ }
+
+
+ /*
+ startAudio->print("audio");
+ startVideo->print("video");
+ */
+
+
+
+
+
+
+ /*
+ cout << "audioStartAudioPacketNrMinor:"<<audioStartAudioPacketNrMinor<<endl;
+ cout << "audioStartPTSTime:"<<audioStartPTSTime<<endl;
+ cout << "audioStartEndPTSTime:"<<audioStartEndPTSTime<<endl;
+ cout << "videoStartPTSTime:"<<videoStartPTSTime<<endl;
+ */
+
+
+ /**
+ Here we make sure that we sync over an audio packet only one
+ time.
+ */
+
+ waitTime->set(0,0);
+ SyncClock* syncClock=startVideo->getSyncClock();
+ int back=false;
+ double addPts=videoFrameCounter*frameTime;
+ double pts=videoStartPTSTime+addPts;
+
+ if (syncClock != NULL) {
+
+ back=syncClock->syncVideo(pts,
+ videoStartSCRTime,earlyTime,waitTime);
+ } else {
+ cout << "syncClock == NULL (video)"<<endl;
+ }
+ unlockSyncData();
+
+ if (back==true) {
+ //earlyTime->print("earlyTime");
+ earlyTime->waitForIt();
+ /*
+ double tmp;
+ double time=syncClock->getPTSTime(&tmp);
+ cout << "time after wait:"<<time<<endl;
+ */
+ }
+ /*
+ if (back == false) {
+ cout <<"real pts:"<<videoStartPTSTime
+ <<" calc pts"<<pts
+ <<" frameNo:"<<videoFrameCounter
+ <<" frameTime:"<<frameTime<<endl;
+ }
+ */
+
+
+
+ return back;
+
+}
+
+int AVSyncer::getAudioRunning() {
+ return lAudioRunning;
+}
+
+
+void AVSyncer::setAudioRunning(int lAudioRunning) {
+ this->lAudioRunning=lAudioRunning;
+}
+
+
+void AVSyncer::lockSyncData() {
+ abs_thread_mutex_lock(&changeMut);
+ abs_thread_mutex_lock(&writeInMut);
+ abs_thread_mutex_unlock(&changeMut);
+}
+
+void AVSyncer::unlockSyncData() {
+ abs_thread_mutex_unlock(&writeInMut);
+}
diff --git a/mpeglib/lib/output/avSyncer.h b/mpeglib/lib/output/avSyncer.h
new file mode 100644
index 00000000..b5a7ae7d
--- /dev/null
+++ b/mpeglib/lib/output/avSyncer.h
@@ -0,0 +1,96 @@
+/*
+ encapsulates the syncing methods, to use it in other classes
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __AVSYNCER_H
+#define __AVSYNCER_H
+
+#include <stdlib.h>
+
+#include "../util/render/yuvPicture.h"
+#include "../util/syncClock.h"
+#include "audioTime.h"
+
+class Performance;
+class AudioDataArray;
+class AudioData;
+
+class AVSyncer {
+
+ AudioData* audioDataInsert;
+ AudioData* audioDataCurrent;
+ AudioDataArray* audioDataArray;
+ Performance* performance;
+
+ AudioTime* audioTime;
+ int onePicFrameInAudioBytes;
+ int oneFrameTime;
+
+ int lAudioRunning;
+
+ abs_thread_mutex_t writeInMut;
+ abs_thread_mutex_t changeMut;
+
+
+ int bufferSize;
+
+ TimeStamp* startAudio;
+ TimeStamp* endAudio;
+
+ TimeStamp* videoTimeStamp;
+ TimeStamp* diffTime;
+ TimeStamp* waitTime;
+
+ int lPerformance;
+ int lavSync;
+ int lastAudioPacket;
+ double pts_jitter;
+
+
+ public:
+ AVSyncer(int bufferSize);
+ ~AVSyncer();
+
+ // audio
+ int audioSetup(int frequency,int stereo,int sign,int big,int sixteen);
+ int audioPlay(TimeStamp* startStamp,
+ TimeStamp* endStamp,char *buffer, int size);
+ void audioClose(void);
+ void setAudioBufferSize(int size);
+
+ int getPreferredDeliverSize();
+
+ // video
+ int getFrameusec();
+ int syncPicture(YUVPicture* syncPic);
+
+ void config(const char* key,const char* value,void* user_data);
+
+
+
+ private:
+ int getAudioRunning();
+ void setAudioRunning(int lAudioRunning);
+
+ void lockSyncData();
+ void unlockSyncData();
+ void setAudioSync(AudioData* audioData);
+
+ // methods which belong not to the OutputStream interface
+ int avSync(TimeStamp* startVideoStamp,
+ TimeStamp* waitTime,
+ TimeStamp* earlyTime,
+ float picPerSec);
+
+};
+#endif
diff --git a/mpeglib/lib/output/dspX11OutputStream.cpp b/mpeglib/lib/output/dspX11OutputStream.cpp
new file mode 100644
index 00000000..05503ffa
--- /dev/null
+++ b/mpeglib/lib/output/dspX11OutputStream.cpp
@@ -0,0 +1,236 @@
+/*
+ concret OutputClass
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "dspX11OutputStream.h"
+#include "../util/audio/dspWrapper.h"
+#include "windowOut.h"
+#include "avSyncer.h"
+#include "yuvDumper.h"
+
+#include <iostream>
+
+using namespace std;
+
+DspX11OutputStream::DspX11OutputStream(int bufferSize) {
+ dspWrapper=new DSPWrapper();
+ x11Window=new WindowOut();
+ avSyncer=new AVSyncer(bufferSize);
+ yuvDumper=new YUVDumper();
+
+ audioTime=new AudioTime();
+
+ lPerformance=false;
+ lneedInit=false;
+ lVideoInit=false;
+ lBufferSet=false;
+ lYUVDump=false;
+}
+
+
+DspX11OutputStream::~DspX11OutputStream() {
+ delete dspWrapper;
+ delete x11Window;
+
+ delete avSyncer;
+ delete audioTime;
+ delete yuvDumper;
+}
+
+
+
+int DspX11OutputStream::audioSetup(int frequency,int stereo,
+ int sign,int big,int sixteen) {
+
+ dspWrapper->audioSetup(stereo,sixteen,sign,big,frequency);
+ audioTime->setFormat(stereo,sixteen,frequency,sign,big);
+ avSyncer->audioSetup(frequency,stereo,sign,big,sixteen);
+ if (dspWrapper->isOpenDevice() == true) {
+ if (lBufferSet==false) {
+ int size=dspWrapper->getAudioBufferSize();
+ avSyncer->setAudioBufferSize(size);
+ }
+ }
+ return true;
+}
+
+
+void DspX11OutputStream::audioClose(void) {
+ avSyncer->audioClose();
+ dspWrapper->closeDevice();
+}
+
+
+void DspX11OutputStream::audioOpen() {
+ if (dspWrapper->isOpenDevice() == false) {
+ dspWrapper->openDevice();
+ if (lBufferSet==false) {
+ int size=dspWrapper->getAudioBufferSize();
+ avSyncer->setAudioBufferSize(size);
+ }
+ }
+}
+
+
+
+
+int DspX11OutputStream::audioPlay(TimeStamp* startStamp,
+ TimeStamp* endStamp,char *buffer, int size) {
+
+ if (lneedInit) {
+ cout << "FIXME. work on audioFrames!!"<<endl;
+ lneedInit=false;
+ }
+
+ if (lPerformance==false) {
+ //
+ // Now feed data smoothly into the dsp/avSyncer
+ //
+ int pos=0;
+ int rest=size;
+ int inc=getPreferredDeliverSize();
+ int len;
+
+ while (rest > 0) {
+ len=rest;
+ if (len>inc) {
+ len=inc;
+ }
+ if (dspWrapper->isOpenDevice()) {
+ if (dspWrapper->audioPlay(buffer,len) != len) {
+ cout << "write error to dsp"<<endl;
+ lneedInit=true;
+ return size-rest;
+ }
+ }
+
+ avSyncer->audioPlay(startStamp,endStamp,buffer,len);
+ buffer+=len;
+ rest-=len;
+ }
+ return size;
+ } else {
+ return size;
+ }
+}
+
+
+int DspX11OutputStream::getPreferredDeliverSize() {
+ if (avSyncer->getPreferredDeliverSize() <= 500) {
+ return 500;
+ }
+ return avSyncer->getPreferredDeliverSize();
+}
+
+
+int DspX11OutputStream::openWindow(int width, int height,const char *title) {
+ int back=x11Window->openWindow(width,height,title);
+ setOutputInit(true);
+ if (lYUVDump) {
+ yuvDumper->openWindow(width,height,title);
+ }
+ return back;
+}
+
+int DspX11OutputStream::x11WindowId() {
+ return x11Window->x11WindowId();
+}
+
+
+void DspX11OutputStream::closeWindow() {
+ x11Window->closeWindow();
+}
+
+void DspX11OutputStream::flushWindow() {
+ x11Window->flushWindow();
+}
+
+
+PictureArray* DspX11OutputStream::lockPictureArray() {
+ return x11Window->lockPictureArray();
+}
+
+
+void DspX11OutputStream::unlockPictureArray(PictureArray* pictureArray) {
+
+
+
+ YUVPicture* pic=pictureArray->getYUVPictureCallback();
+
+ if (lYUVDump) {
+ yuvDumper->unlockPictureArray(pictureArray);
+ }
+ if (avSyncer->syncPicture(pic)==false) {
+ return;
+ }
+
+ x11Window->unlockPictureArray(pictureArray);
+
+
+}
+
+int DspX11OutputStream::getFrameusec() {
+ return avSyncer->getFrameusec();
+}
+
+
+
+int DspX11OutputStream::getOutputInit() {
+ return lVideoInit;
+}
+
+
+void DspX11OutputStream::setOutputInit(int lInit) {
+ this->lVideoInit=lInit;
+}
+
+
+
+void DspX11OutputStream::config(const char* key,
+ const char* value,void* user_data) {
+
+ cout << "key:"<<key<<endl;
+ if (strcmp(key,"-s")==0) {
+ avSyncer->config(key,value,user_data);
+ }
+ if (strcmp(key,"-b")==0) {
+ lBufferSet=true;
+ int size=strtol(value,(char **)NULL,10);
+ cout << "simulated audio buffersize:"<<size<<" bytes"<<endl;
+ avSyncer->setAudioBufferSize(size);
+ }
+ if (strcmp(key,"-p")==0) {
+ lPerformance=true;
+ avSyncer->config(key,value,user_data);
+ }
+ if (strcmp(key,"yufDump")==0) {
+ int method=atoi(value);
+ switch(method) {
+ case 2:
+ yuvDumper->setMethod(_DUMP_YUV_AS_STREAM);
+ break;
+ default:
+ cout << "unknown dump method"<<endl;
+ }
+ lYUVDump=true;
+ }
+ x11Window->config(key,value,user_data);
+}
+
+
+AVSyncer* DspX11OutputStream::getAVSyncer() {
+ return avSyncer;
+}
+
+
+
diff --git a/mpeglib/lib/output/dspX11OutputStream.h b/mpeglib/lib/output/dspX11OutputStream.h
new file mode 100644
index 00000000..9b3eb859
--- /dev/null
+++ b/mpeglib/lib/output/dspX11OutputStream.h
@@ -0,0 +1,89 @@
+/*
+ concret OutputClass
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __DSPX11OUTPUTSTREAM_H
+#define __DSPX11OUTPUTSTREAM_H
+
+#include "outputStream.h"
+
+
+
+class DSPWrapper;
+class WindowOut;
+class AVSyncer;
+class AudioTime;
+class Performance;
+class YUVDumper;
+
+class DspX11OutputStream : public OutputStream {
+
+ DSPWrapper* dspWrapper;
+ WindowOut* x11Window;
+ AVSyncer* avSyncer;
+
+
+ int lBufferSet;
+ int lVideoInit;
+ int lavSync;
+ int lneedInit;
+ int lPerformance;
+ int lYUVDump;
+
+ AudioTime* audioTime;
+ YUVDumper* yuvDumper;
+
+ public:
+ DspX11OutputStream(int bufferSize);
+ ~DspX11OutputStream();
+
+ // Audio Output
+
+ int audioSetup(int frequency,int stereo,int sign,int big,int sixteen);
+ void audioClose();
+ void audioOpen();
+ int audioPlay(TimeStamp* startStamp,
+ TimeStamp* endStamp,char *buffer, int size);
+
+
+ int getPreferredDeliverSize();
+
+
+ // Video Output
+
+ int openWindow(int width, int height,const char *title);
+ int x11WindowId();
+ void closeWindow();
+ void flushWindow();
+
+ PictureArray* lockPictureArray();
+ void unlockPictureArray(PictureArray* pictureArray);
+
+ int getFrameusec();
+
+ int getDepth();
+ int getOutputInit();
+ void setOutputInit(int lInit);
+
+ void config(const char* key,const char* value,void* user_data);
+
+ // methods which do not belong to the outputStream intferface;
+ AVSyncer* getAVSyncer();
+
+
+};
+
+
+
+#endif
diff --git a/mpeglib/lib/output/outPlugin.cpp b/mpeglib/lib/output/outPlugin.cpp
new file mode 100644
index 00000000..cbae10ec
--- /dev/null
+++ b/mpeglib/lib/output/outPlugin.cpp
@@ -0,0 +1,71 @@
+/*
+ generic output plugin
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "outPlugin.h"
+
+#include <iostream>
+
+using namespace std;
+
+
+OutPlugin::OutPlugin() {
+}
+
+
+OutPlugin::~OutPlugin() {
+}
+
+
+
+OutputStream* OutPlugin::createOutputStream(int outputType) {
+
+ // make checks which input routine to use
+ OutputStream* outputStream;
+ int method;
+
+ outputStream=NULL;
+ method=outputType;
+
+ switch(method) {
+ case _OUTPUT_LOCAL: {
+ outputStream=new DspX11OutputStream(1024*64);
+ break;
+ }
+ case _OUTPUT_ARTS: {
+ outputStream=new ArtsOutputStream(NULL);
+ break;
+ }
+ case _OUTPUT_EMPTY: {
+ outputStream=new OutputStream();
+ break;
+ }
+ default:
+ cout << "error cannot create default output stream"<<endl;
+ exit(0);
+ }
+
+ return outputStream;
+
+
+}
+
+OutputStream* OutPlugin::createOutputStream(int outputType,int lThreadSafe) {
+ OutputStream* output=OutPlugin::createOutputStream(outputType);
+ if (lThreadSafe==false) {
+ return output;
+ }
+ OutputStream* tsOutput=new ThreadSafeOutputStream(output);
+ return tsOutput;
+}
+
diff --git a/mpeglib/lib/output/outPlugin.h b/mpeglib/lib/output/outPlugin.h
new file mode 100644
index 00000000..b70620a6
--- /dev/null
+++ b/mpeglib/lib/output/outPlugin.h
@@ -0,0 +1,44 @@
+/*
+ generic output plugin
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __OUTPLUGIN_H
+#define __OUTPLUGIN_H
+
+
+#include "dspX11OutputStream.h"
+#include "artsOutputStream.h"
+#include "threadSafeOutputStream.h"
+#include <kdemacros.h>
+
+#define _OUTPUT_LOCAL 1
+#define _OUTPUT_EMPTY 2
+#define _OUTPUT_YAF 3
+#define _OUTPUT_ARTS 4
+
+#define _OUTPUT_THREADSAFE 1
+
+class KDE_EXPORT OutPlugin {
+
+ public:
+ OutPlugin();
+ ~OutPlugin();
+
+ static OutputStream* createOutputStream(int outputType);
+ static OutputStream* createOutputStream(int outputType,int lThreadSafe);
+
+};
+#endif
+
+
+
+
diff --git a/mpeglib/lib/output/outputStream.cpp b/mpeglib/lib/output/outputStream.cpp
new file mode 100644
index 00000000..ef209cbc
--- /dev/null
+++ b/mpeglib/lib/output/outputStream.cpp
@@ -0,0 +1,238 @@
+/*
+ generic output class
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "outputStream.h"
+#include "../util/mmx/mmx.h"
+
+#include <iostream>
+
+using namespace std;
+
+OutputStream::OutputStream() {
+ // we call mm_support() here because it is the only position
+ // where we gurantee that not threads are
+ // running (the call is not thread safe)
+ // afer the call we never execute the asm part again
+ // and everything is fine
+ mm_support();
+ abs_thread_mutex_init(&stateChangeMut);
+ abs_thread_cond_init(&stateChangeCond);
+
+ audioState=0;
+ videoState=0;
+ audioInit();
+ videoInit();
+}
+
+
+OutputStream::~OutputStream() {
+ audioInit();
+ videoInit();
+ abs_thread_cond_destroy(&stateChangeCond);
+ abs_thread_mutex_destroy(&stateChangeMut);
+}
+
+
+int OutputStream::audioInit() {
+ sendSignal(_STREAM_MASK_IS_INIT,false,_STREAMTYPE_AUDIO);
+ sendSignal(_STREAM_MASK_IS_EOF,false,_STREAMTYPE_AUDIO);
+ sendSignal(_STREAM_MASK_IS_DATA,false,_STREAMTYPE_AUDIO);
+ return true;
+}
+
+
+int OutputStream::audioSetup(int ,int ,
+ int ,int ,int ) {
+ sendSignal(_STREAM_MASK_IS_INIT,true,_STREAMTYPE_AUDIO);
+ return true;
+}
+
+
+int OutputStream::audioPlay(TimeStamp* ,
+ TimeStamp* ,char* , int len) {
+ sendSignal(_STREAM_MASK_IS_DATA,true,_STREAMTYPE_AUDIO);
+ return len;
+}
+
+void OutputStream::audioFlush() {
+ sendSignal(_STREAM_MASK_IS_EOF,true,_STREAMTYPE_AUDIO);
+}
+
+
+void OutputStream::audioClose() {
+ cerr << "direct virtual call OutputStream::audioClose"<<endl;
+ exit(0);
+}
+
+void OutputStream::audioOpen() {
+ cerr << "direct virtual call OutputStream::audioOpen"<<endl;
+ exit(0);
+}
+
+
+
+void OutputStream::sendSignal(int signal,int value,int streamType) {
+ abs_thread_mutex_lock(&stateChangeMut);
+ int* modifyState=NULL;
+ switch(streamType) {
+ case _STREAMTYPE_AUDIO:
+ modifyState=&audioState;
+ break;
+ case _STREAMTYPE_VIDEO:
+ modifyState=&videoState;
+ break;
+ default:
+ cout << "unknown streamType:"<<streamType
+ <<" in OutputStream::sendSignal"<<endl;
+ exit(0);
+ }
+ // should we set the bit?
+ if (value==true) {
+ // set it with "or"
+ *modifyState|=signal;
+ } else {
+ // we should remove the bit
+ // is it currently set?
+ if (*modifyState & signal) {
+ // remove it
+ *modifyState-=signal;
+ }
+ }
+
+
+ abs_thread_cond_signal(&stateChangeCond);
+ abs_thread_mutex_unlock(&stateChangeMut);
+
+}
+
+
+int OutputStream::getPreferredDeliverSize() {
+ cerr << "direct virtual call OutputStream::getPreferredDeliverSize()"<<endl;
+ return 4096;
+}
+
+int OutputStream::videoInit() {
+ sendSignal(_STREAM_MASK_IS_INIT,false,_STREAMTYPE_VIDEO);
+ sendSignal(_STREAM_MASK_IS_EOF,false,_STREAMTYPE_VIDEO);
+ sendSignal(_STREAM_MASK_IS_DATA,false,_STREAMTYPE_VIDEO);
+ return true;
+}
+
+int OutputStream::openWindow(int , int ,const char* ) {
+ sendSignal(_STREAM_MASK_IS_INIT,true,_STREAMTYPE_VIDEO);
+ return true;
+}
+
+int OutputStream::x11WindowId() {
+ cout << "direct virtual call OutputStream::x11WindowId()" << endl;
+ return -1;
+}
+
+void OutputStream::closeWindow() {
+ cerr << "direct virtual call OutputStream::closeWindow"<<endl;
+ exit(0);
+}
+
+void OutputStream::flushWindow() {
+ sendSignal(_STREAM_MASK_IS_EOF,true,_STREAMTYPE_VIDEO);
+}
+
+
+PictureArray* OutputStream::lockPictureArray() {
+ cerr << "direct virtual call OutputStream::lockPictureArray"<<endl;
+ exit(0);
+ return NULL;
+}
+
+
+void OutputStream::unlockPictureArray(PictureArray* ) {
+ sendSignal(_STREAM_MASK_IS_DATA,true,_STREAMTYPE_VIDEO);
+}
+
+
+
+
+
+int OutputStream::getOutputInit() {
+ cerr << "direct virtual call OutputStream::getOutputInit"<<endl;
+ exit(0);
+ return false;
+}
+
+
+void OutputStream::setOutputInit(int lInit) {
+ cerr << "direct virtual call OutputStream::setOutputInit:"<<lInit<<endl;
+ exit(0);
+}
+
+
+
+void OutputStream::writeInfo(PluginInfo* ) {
+
+}
+
+
+void OutputStream::config(const char* key,
+ const char* value,void* user_data) {
+ cerr << "direct virtual call OutputStream::config"<<endl;
+ printf("key:%s\n",key);
+ printf("value:%s\n",value);
+ printf("user_data:%p\n",user_data);
+ exit(0);
+}
+
+int OutputStream::getFrameusec() {
+ cerr << "direct virtual call OutputStream::getFrameusec"<<endl;
+ return 0;
+}
+
+
+
+int OutputStream::waitStreamState(int method,int mask,int streamType) {
+
+ int* waitState=NULL;
+ switch(streamType) {
+ case _STREAMTYPE_AUDIO:
+ waitState=&audioState;
+ break;
+ case _STREAMTYPE_VIDEO:
+ waitState=&videoState;
+ break;
+ default:
+ cout << "unknown streamType:"<<streamType
+ <<" in OutputStream::waitStreamState"<<endl;
+ exit(0);
+ }
+
+ if (method == _OUTPUT_WAIT_METHOD_BLOCK) {
+ abs_thread_mutex_lock(&stateChangeMut);
+ while ((*waitState &= mask)==0) {
+ cout << "waitStreamState:"<<waitState<<endl;
+ cout << "mask:"<<mask<<endl;
+ abs_thread_cond_wait(&stateChangeCond,&stateChangeMut);
+ }
+ abs_thread_mutex_unlock(&stateChangeMut);
+ return true;
+ }
+
+ int back=false;
+ if (method == _OUTPUT_WAIT_METHOD_POLL) {
+ abs_thread_mutex_lock(&stateChangeMut);
+ back=*waitState;
+ abs_thread_mutex_unlock(&stateChangeMut);
+ return back;
+ }
+ cout << " OutputStream::waitStreamState method not implemented"<<endl;
+ exit(0);
+ return 0;
+}
diff --git a/mpeglib/lib/output/outputStream.h b/mpeglib/lib/output/outputStream.h
new file mode 100644
index 00000000..9ef538fd
--- /dev/null
+++ b/mpeglib/lib/output/outputStream.h
@@ -0,0 +1,151 @@
+/*
+ generic output class
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __OUTPUTSTREAM_H
+#define __OUTPUTSTREAM_H
+
+#include "../util/timeStamp.h"
+#include "pluginInfo.h"
+#include "../util/render/pictureArray.h"
+#include "../util/abstract/abs_thread.h"
+#include <kdemacros.h>
+
+#define _OUTPUT_WAIT_METHOD_BLOCK 1
+#define _OUTPUT_WAIT_METHOD_POLL 2
+#define _OUTPUT_WAIT_METHOD_CALLBACK 3
+
+
+#define _STREAM_MASK_IS_INIT 1
+#define _STREAM_MASK_IS_EOF 2
+#define _STREAM_MASK_IS_DATA 4
+
+#define _STREAM_MASK_ALL 1+2+4
+
+#define _STREAMTYPE_AUDIO 1
+#define _STREAMTYPE_VIDEO 2
+
+
+/**
+ Outputstream. A nice base class. Mostly obvious methods,
+ except PictureArray.
+ write your picture into this structure
+ and then put it on the surface with the unlock call.
+
+*/
+
+
+/**
+ life of stream states:
+ --------------
+ method flag additional calls
+ construct audioInit()
+
+
+
+ destruct audioInit()
+
+ audioInit isInit=false
+ isEof=false
+ isData=false
+
+ audioSetup isInit=true
+ audioFlush isEof=true
+ audioPlay isData=true
+
+
+ Note: after a call of streamInit we have an "invalid" state
+ obviously there is no stream where eof==false but
+ and has not been initialised.
+
+ another invalid state is audioInit->audioPlay
+
+*/
+
+
+
+
+class KDE_EXPORT OutputStream {
+
+ int audioState;
+ int videoState;
+ abs_thread_mutex_t stateChangeMut;
+ abs_thread_cond_t stateChangeCond;
+
+ public:
+ OutputStream();
+
+ virtual ~OutputStream();
+
+ // Audio Stream handling
+ virtual int audioInit();
+ virtual int audioSetup(int freq,int stereo,int sign,int big,int sampleSize);
+ virtual int audioPlay(TimeStamp* startStamp,
+ TimeStamp* endStamp,char *buffer, int size);
+ virtual void audioFlush();
+
+ // Audio device "/dev/dsp" handling
+ virtual void audioClose();
+ virtual void audioOpen();
+
+ // hack: FIX it
+ virtual int getPreferredDeliverSize();
+
+
+ // stream State handling
+
+ // we return the mask which triggerd (by "AND")
+ // or the current polled mask when method is POLL
+ // Note: you can only wait for "true" signals
+ virtual int waitStreamState(int method,int mask,int streamType);
+
+
+
+
+ // Video Output
+ virtual int videoInit();
+ virtual int openWindow(int width, int height,const char *title);
+ virtual int x11WindowId();
+ virtual void closeWindow();
+ virtual void flushWindow();
+
+
+ // get the current surfaces to draw into
+ virtual PictureArray* lockPictureArray();
+ virtual void unlockPictureArray(PictureArray* pictureArray);
+
+
+
+ // maybe not needed:
+ virtual int getFrameusec();
+ virtual int getOutputInit();
+ virtual void setOutputInit(int lInit);
+
+ // Info Output
+ virtual void writeInfo(class PluginInfo* pluginInfo);
+
+
+ // config Output
+ virtual void config(const char* key,
+ const char* value,void* user_data);
+
+ private:
+ void initStream();
+
+ protected:
+ // sometimes useful, but use with care
+ void sendSignal(int signal,int value,int streamType);
+
+};
+#endif
diff --git a/mpeglib/lib/output/performance.cpp b/mpeglib/lib/output/performance.cpp
new file mode 100644
index 00000000..9e2b7f92
--- /dev/null
+++ b/mpeglib/lib/output/performance.cpp
@@ -0,0 +1,50 @@
+/*
+ measures picture/second
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "performance.h"
+
+#include <iostream>
+
+using namespace std;
+
+
+Performance::Performance() {
+ picCnt=0;
+ startTime=new TimeStamp();
+ endTime=new TimeStamp();
+
+}
+
+
+Performance::~Performance() {
+ delete startTime;
+ delete endTime;
+}
+
+
+void Performance::incPictureCount() {
+ if (picCnt==0) {
+ startTime->gettimeofday();
+ }
+ picCnt++;
+ if (picCnt==200) {
+ endTime->gettimeofday();
+ TimeStamp diffTime;
+ endTime->minus(startTime,&diffTime);
+ double secs=(double)diffTime.getAsSeconds();
+
+ double picSec=(double)picCnt/secs;
+ cout << "picPerSec:"<<picSec<<" secs:"<<secs<<endl;
+ picCnt=0;
+ }
+}
diff --git a/mpeglib/lib/output/performance.h b/mpeglib/lib/output/performance.h
new file mode 100644
index 00000000..d7d54572
--- /dev/null
+++ b/mpeglib/lib/output/performance.h
@@ -0,0 +1,34 @@
+/*
+ measures picture/second
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __PERFORMANCE_H
+#define __PERFORMANCE_H
+
+#include "../util/timeStamp.h"
+
+class Performance {
+
+ int picCnt;
+ TimeStamp* startTime;
+ TimeStamp* endTime;
+
+
+ public:
+
+ Performance();
+ ~Performance();
+ void incPictureCount();
+
+};
+#endif
diff --git a/mpeglib/lib/output/pluginInfo.cpp b/mpeglib/lib/output/pluginInfo.cpp
new file mode 100644
index 00000000..c741852c
--- /dev/null
+++ b/mpeglib/lib/output/pluginInfo.cpp
@@ -0,0 +1,65 @@
+/*
+ add on information about plugin.
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "pluginInfo.h"
+
+#include <iostream>
+
+using namespace std;
+
+
+PluginInfo::PluginInfo() {
+ musicName=new DynBuffer(20);
+
+ reset();
+
+}
+
+
+PluginInfo::~PluginInfo() {
+ delete musicName;
+}
+
+void PluginInfo::setLength(int sec) {
+ this->sec=sec;
+}
+
+
+int PluginInfo::getLength() {
+ return sec;
+}
+
+
+void PluginInfo::reset(){
+ sec=0;
+ musicName->clear();
+ musicName->append("none");
+}
+
+void PluginInfo::print() {
+ cerr << "length in sec:"<<sec<<endl;
+ cerr << "url:"<<getUrl()<<endl;
+}
+
+
+void PluginInfo::setUrl(char* name) {
+ musicName->clear();
+ if (name != NULL) {
+ musicName->append(name);
+ }
+}
+
+
+char* PluginInfo::getUrl() {
+ return musicName->getData();
+}
diff --git a/mpeglib/lib/output/pluginInfo.h b/mpeglib/lib/output/pluginInfo.h
new file mode 100644
index 00000000..7026fce9
--- /dev/null
+++ b/mpeglib/lib/output/pluginInfo.h
@@ -0,0 +1,48 @@
+/*
+ add on information about plugin.
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __PLUGININFO_H
+#define __PLUGININFO_H
+
+#include "../util/dynBuffer.h"
+#include <kdemacros.h>
+
+/**
+ Here we have the base class for all additional information
+ about a plugin.
+ The len in time of the current song belongs here, as
+ well as the author, version number etc..
+*/
+
+
+class KDE_EXPORT PluginInfo {
+
+ int sec;
+ DynBuffer* musicName;
+
+
+ public:
+ PluginInfo();
+ ~PluginInfo();
+
+ void setLength(int sec);
+ int getLength();
+
+ void setUrl(char* name);
+ char* getUrl();
+
+ void reset();
+ void print();
+};
+#endif
diff --git a/mpeglib/lib/output/threadSafeOutputStream.cpp b/mpeglib/lib/output/threadSafeOutputStream.cpp
new file mode 100644
index 00000000..ec7ab510
--- /dev/null
+++ b/mpeglib/lib/output/threadSafeOutputStream.cpp
@@ -0,0 +1,153 @@
+/*
+ thread safe wrapper for output Stream
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "threadSafeOutputStream.h"
+
+
+ThreadSafeOutputStream::ThreadSafeOutputStream(OutputStream* output) {
+ threadQueueAudio=new ThreadQueue();
+ threadQueueVideo=new ThreadQueue();
+ this->output=output;
+}
+
+
+ThreadSafeOutputStream::~ThreadSafeOutputStream() {
+ delete threadQueueAudio;
+ delete threadQueueVideo;
+ delete output;
+}
+
+int ThreadSafeOutputStream::audioInit() {
+ int back;
+ threadQueueAudio->waitForExclusiveAccess();
+ back=output->audioInit();
+ threadQueueAudio->releaseExclusiveAccess();
+ return back;
+}
+
+
+int ThreadSafeOutputStream::audioSetup(int freq,int stereo,
+ int sign,int big,int sampleSize) {
+ int back;
+ threadQueueAudio->waitForExclusiveAccess();
+ back=output->audioSetup(freq,stereo,sign,big,sampleSize);
+ threadQueueAudio->releaseExclusiveAccess();
+ return back;
+}
+
+
+int ThreadSafeOutputStream::audioPlay(TimeStamp* start,
+ TimeStamp* end,char* buf, int len) {
+ int back;
+ threadQueueAudio->waitForExclusiveAccess();
+ back=output->audioPlay(start,end,buf,len);
+ threadQueueAudio->releaseExclusiveAccess();
+ return back;
+}
+
+void ThreadSafeOutputStream::audioFlush() {
+ threadQueueAudio->waitForExclusiveAccess();
+ output->audioFlush();
+ threadQueueAudio->releaseExclusiveAccess();
+}
+
+
+void ThreadSafeOutputStream::audioClose() {
+ threadQueueAudio->waitForExclusiveAccess();
+ output->audioClose();
+ threadQueueAudio->releaseExclusiveAccess();
+}
+
+void ThreadSafeOutputStream::audioOpen() {
+ threadQueueAudio->waitForExclusiveAccess();
+ output->audioOpen();
+ threadQueueAudio->releaseExclusiveAccess();
+}
+
+
+
+int ThreadSafeOutputStream::getPreferredDeliverSize() {
+ int back;
+ threadQueueAudio->waitForExclusiveAccess();
+ back=output->getPreferredDeliverSize();
+ threadQueueAudio->releaseExclusiveAccess();
+ return back;
+}
+
+int ThreadSafeOutputStream::videoInit() {
+ int back;
+ threadQueueVideo->waitForExclusiveAccess();
+ back=output->videoInit();
+ threadQueueVideo->releaseExclusiveAccess();
+ return back;
+}
+
+int ThreadSafeOutputStream::openWindow(int w, int h,const char* title) {
+ int back;
+ threadQueueVideo->waitForExclusiveAccess();
+ back=output->openWindow(w,h,title);
+ threadQueueVideo->releaseExclusiveAccess();
+ return back;
+}
+
+
+void ThreadSafeOutputStream::closeWindow() {
+ threadQueueVideo->waitForExclusiveAccess();
+ output->closeWindow();
+ threadQueueVideo->releaseExclusiveAccess();
+}
+
+void ThreadSafeOutputStream::flushWindow() {
+ threadQueueVideo->waitForExclusiveAccess();
+ output->flushWindow();
+ threadQueueVideo->releaseExclusiveAccess();
+}
+
+
+PictureArray* ThreadSafeOutputStream::lockPictureArray() {
+ PictureArray* back;
+ threadQueueVideo->waitForExclusiveAccess();
+ back=output->lockPictureArray();
+ threadQueueVideo->releaseExclusiveAccess();
+ return back;
+}
+
+
+void ThreadSafeOutputStream::unlockPictureArray(PictureArray* array) {
+ threadQueueVideo->waitForExclusiveAccess();
+ output->unlockPictureArray(array);
+ threadQueueVideo->releaseExclusiveAccess();
+}
+
+
+int ThreadSafeOutputStream::getFrameusec() {
+ int back;
+ threadQueueVideo->waitForExclusiveAccess();
+ back=output->getFrameusec();
+ threadQueueVideo->releaseExclusiveAccess();
+ return back;
+}
+
+void ThreadSafeOutputStream::config(const char* key,
+ const char* value,void* user_data) {
+
+ threadQueueVideo->waitForExclusiveAccess();
+ threadQueueAudio->waitForExclusiveAccess();
+ output->config(key,value,user_data);
+ threadQueueVideo->releaseExclusiveAccess();
+ threadQueueAudio->releaseExclusiveAccess();
+
+}
+
+
+
diff --git a/mpeglib/lib/output/threadSafeOutputStream.h b/mpeglib/lib/output/threadSafeOutputStream.h
new file mode 100644
index 00000000..c9852a37
--- /dev/null
+++ b/mpeglib/lib/output/threadSafeOutputStream.h
@@ -0,0 +1,67 @@
+/*
+ thread safe wrapper for output Stream
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#ifndef __THREADSAFEOUTPUTSTREAM_H
+#define __THREADSAFEOUTPUTSTREAM_H
+
+// read INTRO in threadQueue.h
+// This class makes the outputStream (given in constructor)
+// threadsafe by wrapping each call with a threadqueue.
+//
+// Important NOTE: the output pointer is the owned by this class !!!
+// which means: we call delete on it!
+// More Important NOTES:
+//
+// We make the audio and video calls seperate threadsafe
+// and ONLY the config call threadsafe to both!
+
+#include "../util/abstract/threadQueue.h"
+#include "outputStream.h"
+
+
+class ThreadSafeOutputStream : public OutputStream {
+
+ ThreadQueue* threadQueueAudio;
+ ThreadQueue* threadQueueVideo;
+ OutputStream* output;
+
+ public:
+ ThreadSafeOutputStream(OutputStream* output);
+ ~ThreadSafeOutputStream();
+
+ // Thread safe Audio Stream handling
+ int audioInit();
+ int audioSetup(int freq,int stereo,int sign,int big,int sampleSize);
+ int audioPlay(TimeStamp* startStamp,
+ TimeStamp* endStamp,char *buffer, int size);
+ void audioFlush();
+ void audioClose();
+ void audioOpen();
+ int getPreferredDeliverSize();
+
+
+ // Video Output
+ int videoInit();
+ int openWindow(int width, int height,const char *title);
+ void closeWindow();
+ void flushWindow();
+ PictureArray* lockPictureArray();
+ void unlockPictureArray(PictureArray* pictureArray);
+ int getFrameusec();
+
+ // config Output
+ void config(const char* key,
+ const char* value,void* user_data);
+
+
+};
+#endif
diff --git a/mpeglib/lib/output/windowOut.cpp b/mpeglib/lib/output/windowOut.cpp
new file mode 100644
index 00000000..3e199095
--- /dev/null
+++ b/mpeglib/lib/output/windowOut.cpp
@@ -0,0 +1,64 @@
+/*
+ wrapper for X11 Window
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "windowOut.h"
+
+
+
+
+
+
+
+WindowOut::WindowOut() {
+
+ renderMachine=new RenderMachine();
+
+}
+
+
+WindowOut::~WindowOut() {
+ delete renderMachine;
+}
+
+int WindowOut::openWindow(int width, int height,const char *title){
+ return renderMachine->openWindow(width,height,title);
+}
+
+int WindowOut::x11WindowId() {
+ return renderMachine->x11WindowId();
+}
+
+void WindowOut::flushWindow() {
+ renderMachine->flushWindow();
+}
+
+
+void WindowOut::closeWindow() {
+ renderMachine->closeWindow();
+}
+
+
+PictureArray* WindowOut::lockPictureArray() {
+ return renderMachine->lockPictureArray();
+}
+
+
+void WindowOut::unlockPictureArray(PictureArray* pictureArray) {
+ renderMachine->unlockPictureArray(pictureArray);
+}
+
+
+
+void WindowOut::config(const char* key, const char* value,void* user_data) {
+ renderMachine->config(key,value,user_data);
+}
diff --git a/mpeglib/lib/output/windowOut.h b/mpeglib/lib/output/windowOut.h
new file mode 100644
index 00000000..648d1dc4
--- /dev/null
+++ b/mpeglib/lib/output/windowOut.h
@@ -0,0 +1,53 @@
+/*
+ wrapper for X11 Window
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __WINDOWOUT_H
+#define __WINDOWOUT_H
+
+
+#include "../util/abstract/abs_thread.h"
+#include "../util/timeStamp.h"
+#include "../util/render/renderMachine.h"
+
+/**
+ Stupid class. survivor of ancient days.
+*/
+
+
+class WindowOut {
+
+
+ RenderMachine* renderMachine;
+
+ public:
+ WindowOut();
+ ~WindowOut();
+
+ int openWindow(int width, int height,const char *title);
+ int x11WindowId();
+ void closeWindow();
+ void flushWindow();
+
+ // get the current surfaces to draw into
+ PictureArray* lockPictureArray();
+ void unlockPictureArray(PictureArray* pictureArray);
+
+ void config(const char* key, const char* value,void* user_data);
+
+
+ private:
+
+
+};
+#endif
diff --git a/mpeglib/lib/output/yuvDumper.cpp b/mpeglib/lib/output/yuvDumper.cpp
new file mode 100644
index 00000000..1962b151
--- /dev/null
+++ b/mpeglib/lib/output/yuvDumper.cpp
@@ -0,0 +1,81 @@
+/*
+ writes yuv images on HD
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "yuvDumper.h"
+
+YUVDumper::YUVDumper() {
+ picCnt=1;
+ method=_DUMP_YUV_AS_STREAM;
+}
+
+YUVDumper::~YUVDumper() {
+}
+
+
+int YUVDumper::openWindow(int w, int h,const char*) {
+
+ FILE* formatFile=fopen("stream.yuv.format","w+");
+ fprintf(formatFile,"Version 0.1\nw:%dh:%d\n",w,h);
+ fclose(formatFile);
+
+
+
+ if (method == _DUMP_YUV_AS_STREAM) {
+ FILE* outFile=fopen("stream.yuv","w+");
+ fclose(outFile);
+ }
+ return true;
+}
+
+void YUVDumper::closeWindow() {
+}
+
+
+void YUVDumper::flushWindow() {
+}
+
+
+int YUVDumper::getMethod() {
+ return method;
+}
+
+
+void YUVDumper::setMethod(int method) {
+ this->method=method;
+}
+
+void YUVDumper::unlockPictureArray(PictureArray* pictureArray) {
+ YUVPicture* pic=pictureArray->getYUVPictureCallback();
+ if (pic == NULL) {
+ return;
+ }
+
+ FILE* outFile=NULL;
+
+ if (method == _DUMP_YUV_AS_STREAM) {
+ outFile=fopen("stream.yuv","a+");
+ }
+
+ if (outFile == NULL) {
+ perror("fopen");
+ return;
+ }
+
+ int lumSize=pic->getLumLength();
+ int colorSize=pic->getColorLength();
+ fwrite(pic->getLuminancePtr(),1,lumSize,outFile);
+ fwrite(pic->getCrPtr(),1,colorSize,outFile);
+ fwrite(pic->getCbPtr(),1,colorSize,outFile);
+ fclose(outFile);
+
+}
diff --git a/mpeglib/lib/output/yuvDumper.h b/mpeglib/lib/output/yuvDumper.h
new file mode 100644
index 00000000..2e4880b0
--- /dev/null
+++ b/mpeglib/lib/output/yuvDumper.h
@@ -0,0 +1,51 @@
+/*
+ writes yuv images on HD
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __YUVDUMPER_H
+#define __YUVDUMPER_H
+
+#include "outputStream.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+
+#define _DUMP_YUV_AS_STREAM 2
+
+
+class YUVDumper : public OutputStream {
+
+
+ int picCnt;
+ int method;
+
+ public:
+ YUVDumper();
+ ~YUVDumper();
+
+ // Video Output
+
+ int openWindow(int width, int height,const char *title);
+ void closeWindow();
+ void flushWindow();
+
+ void unlockPictureArray(PictureArray* pictureArray);
+ int getMethod();
+ void setMethod(int method);
+
+
+};
+
+#endif
diff --git a/mpeglib/lib/splay/Makefile.am b/mpeglib/lib/splay/Makefile.am
new file mode 100644
index 00000000..70fdc105
--- /dev/null
+++ b/mpeglib/lib/splay/Makefile.am
@@ -0,0 +1,53 @@
+# libsplay - Makefile.am
+
+
+INCLUDES = $(all_includes)
+
+
+EXTRA_DIST = dct64.cpp dct64_down.cpp dct36_12.cpp \
+ window.cpp dct.h
+
+
+noinst_LTLIBRARIES = libsplay.la
+
+noinst_HEADERS = mpegAudioHeader.h mpegAudioStream.h \
+ mpegsound.h op.h \
+ sigsev.c dump.h \
+ dxHead.h mpeg2tables.h \
+ mpegAudioBitWindow.h huffmanlookup.h \
+ common.h attribute.h synthesis.h
+
+kmpgincludedir = $(includedir)/$(THIS_LIB_NAME)/splay
+
+kmpginclude_HEADERS = splayDecoder.h mpegAudioInfo.h \
+ mpegAudioFrame.h
+
+
+libsplay_la_SOURCES = mpegAudioHeader.cpp mpegAudioStream.cpp \
+ huffmantable.cpp \
+ mpeglayer1.cpp \
+ mpeglayer2.cpp \
+ mpeglayer3.cpp \
+ mpegtable.cpp \
+ mpegtoraw.cpp \
+ dxHead.cpp \
+ mpegAudioBitWindow.cpp huffmanlookup.cpp \
+ splayDecoder.cpp \
+ dump.cpp synth_filter.cpp \
+ synthesis.cpp synth_Std.cpp synth_Down.cpp \
+ mpegAudioFrame.cpp \
+ mpegAudioInfo.cpp
+
+
+
+#CXXFLAGS += -fno-strength-reduce
+# -funroll-all-loops -finline-functions -ffast-math -m486
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/lib/splay/attribute.h b/mpeglib/lib/splay/attribute.h
new file mode 100644
index 00000000..9e6bf0e6
--- /dev/null
+++ b/mpeglib/lib/splay/attribute.h
@@ -0,0 +1,33 @@
+/*
+ align attribut definition (g++)
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __ATTRIBUTE_H
+#define __ATTRIBUTE_H
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* use gcc attribs to align critical data structures */
+
+#ifdef ATTRIBUTE_ALIGNED_MAX
+#define ATTR_ALIGN(align) __attribute__ \
+ ((__aligned__ ((ATTRIBUTE_ALIGNED_MAX <align) ? \
+ ATTRIBUTE_ALIGNED_MAX : align)))
+#else
+#define ATTR_ALIGN(align)
+#endif
+
+
+#endif
diff --git a/mpeglib/lib/splay/common.h b/mpeglib/lib/splay/common.h
new file mode 100644
index 00000000..df0945b8
--- /dev/null
+++ b/mpeglib/lib/splay/common.h
@@ -0,0 +1,60 @@
+/*
+ include header for dcts/windows
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __COMMON_H
+#define __COMMON_H
+
+#include "attribute.h"
+#include <stdio.h>
+
+#define SSLIMIT 18
+#define SBLIMIT 32
+
+
+#define LS 0
+#define RS 1
+
+
+typedef float REAL;
+
+// The inline code works on intel only with egcs >= 1.1
+#ifdef __GNUC__
+#if (__GNUC__ < 2 || ( __GNUC__ == 2 && __GNUC_MINOR__ < 91 ) )
+#ifndef _AIX
+#warning "inline code disabled! (buggy egcs version)"
+#undef __NO_MATH_INLINES
+#define __NO_MATH_INLINES 1
+#endif
+#endif
+#endif
+#include <math.h>
+#include <stdlib.h>
+
+#ifndef M_PI
+#define MY_PI 3.14159265358979323846
+#else
+#define MY_PI M_PI
+#endif
+
+#ifdef PI
+#undef PI
+#endif
+#define PI MY_PI
+#define PI_12 (PI/12.0)
+#define PI_18 (PI/18.0)
+#define PI_24 (PI/24.0)
+#define PI_36 (PI/36.0)
+#define PI_72 (PI/72.0)
+
+
+#endif
diff --git a/mpeglib/lib/splay/dct.h b/mpeglib/lib/splay/dct.h
new file mode 100644
index 00000000..ad707a19
--- /dev/null
+++ b/mpeglib/lib/splay/dct.h
@@ -0,0 +1,33 @@
+/*
+ wrapper for dcts
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __DCT_HEADER_H
+#define __DCT_HEADER_H
+
+// one source:
+extern void initialize_dct64();
+
+//extern void dct64(REAL* out1,REAL* out2,REAL *fraction);
+
+// one source:
+extern void initialize_dct64_downsample();
+//extern void dct64_downsample(REAL* out1,REAL* out2,REAL *fraction);
+
+// one source file:
+extern void initialize_dct12_dct36();
+
+//extern void dct12(REAL *in,REAL *prevblk1,REAL *prevblk2,REAL *wi,REAL *out);
+//extern void dct36(REAL *inbuf,REAL *prevblk1,REAL *prevblk2,REAL *wi,REAL *out);
+
+extern void initialize_win();
+#endif
diff --git a/mpeglib/lib/splay/dct36_12.cpp b/mpeglib/lib/splay/dct36_12.cpp
new file mode 100644
index 00000000..b9978d7d
--- /dev/null
+++ b/mpeglib/lib/splay/dct36_12.cpp
@@ -0,0 +1,288 @@
+/*
+ wrapper for dcts
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include "dct.h"
+#include "common.h"
+
+
+
+ATTR_ALIGN(64) static REAL hsec_12[3];
+ATTR_ALIGN(64) static REAL cos2_6=cos(PI/6.0*2.0);
+ATTR_ALIGN(64) static REAL cos1_6=cos(PI/6.0*1.0);
+ATTR_ALIGN(64) static REAL hsec_36[9];
+ATTR_ALIGN(64) static REAL cos_18[9];
+
+
+/**
+ This was some time ago a standalone dct class,
+ but to get more speed I made it an inline dct
+ int the filter classes
+*/
+
+static int dct36_12Init=false;
+
+void initialize_dct12_dct36() {
+ if (dct36_12Init==true) {
+ return;
+ }
+ dct36_12Init=true;
+
+
+ int i;
+
+ for(i=0;i<3;i++)
+ hsec_12[i]=0.5/cos(double(i*2+1)* PI_12);
+
+ for(i=0;i<9;i++)
+ hsec_36[i]=0.5/cos(double(i*2+1)* PI_36);
+
+ for(i=0;i<9;i++)
+ cos_18[i]=cos(PI_18*double(i));
+
+}
+
+
+inline void dct36(REAL *inbuf,REAL *prevblk1,
+ REAL *prevblk2,REAL *wi,REAL *out){
+
+#define MACRO0(v) { \
+ REAL tmp; \
+ out2[9+(v)]=(tmp=sum0+sum1)*wi[27+(v)]; \
+ out2[8-(v)]=tmp * wi[26-(v)]; } \
+ sum0-=sum1; \
+ ts[SBLIMIT*(8-(v))]=out1[8-(v)]+sum0*wi[8-(v)]; \
+ ts[SBLIMIT*(9+(v))]=out1[9+(v)]+sum0*wi[9+(v)];
+#define MACRO1(v) { \
+ REAL sum0,sum1; \
+ sum0=tmp1a+tmp2a; \
+ sum1=(tmp1b+tmp2b)*hsec_36[(v)]; \
+ MACRO0(v); }
+#define MACRO2(v) { \
+ REAL sum0,sum1; \
+ sum0=tmp2a-tmp1a; \
+ sum1=(tmp2b-tmp1b) * hsec_36[(v)]; \
+ MACRO0(v); }
+
+ {
+ REAL *in = inbuf;
+
+ in[17]+=in[16];in[16]+=in[15];in[15]+=in[14];in[14]+=in[13];
+ in[13]+=in[12];in[12]+=in[11];in[11]+=in[10];in[10]+=in[ 9];
+ in[ 9]+=in[ 8];in[ 8]+=in[ 7];in[ 7]+=in[ 6];in[ 6]+=in[ 5];
+ in[ 5]+=in[ 4];in[ 4]+=in[ 3];in[ 3]+=in[ 2];in[ 2]+=in[ 1];
+ in[ 1]+=in[ 0];
+
+
+ in[17]+=in[15];in[15]+=in[13];in[13]+=in[11];in[11]+=in[ 9];
+ in[ 9]+=in[ 7];in[7] +=in[ 5];in[ 5]+=in[ 3];in[ 3]+=in[ 1];
+
+ {
+ REAL *c = cos_18;
+ REAL *out2 = prevblk2;
+ REAL *out1 = prevblk1;
+ REAL *ts = out;
+
+ REAL ta33,ta66,tb33,tb66;
+
+ ta33=in[2*3+0]*c[3];
+ ta66=in[2*6+0]*c[6];
+ tb33=in[2*3+1]*c[3];
+ tb66=in[2*6+1]*c[6];
+
+ {
+ REAL tmp1a,tmp2a,tmp1b,tmp2b;
+ tmp1a= in[2*1+0]*c[1]+ta33 +in[2*5+0]*c[5]+in[2*7+0]*c[7];
+ tmp1b= in[2*1+1]*c[1]+tb33 +in[2*5+1]*c[5]+in[2*7+1]*c[7];
+ tmp2a=in[2*0+0]+in[2*2+0]*c[2]+in[2*4+0]*c[4]+ta66 +in[2*8+0]*c[8];
+ tmp2b=in[2*0+1]+in[2*2+1]*c[2]+in[2*4+1]*c[4]+tb66 +in[2*8+1]*c[8];
+ MACRO1(0);
+ MACRO2(8);
+ }
+
+ {
+ REAL tmp1a,tmp2a,tmp1b,tmp2b;
+ tmp1a=(in[2*1+0]-in[2*5+0]-in[2*7+0])*c[3];
+ tmp1b=(in[2*1+1]-in[2*5+1]-in[2*7+1])*c[3];
+ tmp2a=(in[2*2+0]-in[2*4+0]-in[2*8+0])*c[6]-in[2*6+0]+in[2*0+0];
+ tmp2b=(in[2*2+1]-in[2*4+1]-in[2*8+1])*c[6]-in[2*6+1]+in[2*0+1];
+ MACRO1(1);
+ MACRO2(7);
+ }
+
+ {
+ REAL tmp1a,tmp2a,tmp1b,tmp2b;
+ tmp1a= in[2*1+0]*c[5]-ta33 -in[2*5+0]*c[7]+in[2*7+0]*c[1];
+ tmp1b= in[2*1+1]*c[5]-tb33 -in[2*5+1]*c[7]+in[2*7+1]*c[1];
+ tmp2a=in[2*0+0]-in[2*2+0]*c[8]-in[2*4+0]*c[2]+ta66 +in[2*8+0]*c[4];
+ tmp2b=in[2*0+1]-in[2*2+1]*c[8]-in[2*4+1]*c[2]+tb66 +in[2*8+1]*c[4];
+ MACRO1(2);
+ MACRO2(6);
+ }
+
+ {
+ REAL tmp1a,tmp2a,tmp1b,tmp2b;
+ tmp1a= in[2*1+0]*c[7]-ta33 +in[2*5+0]*c[1]-in[2*7+0]*c[5];
+ tmp1b= in[2*1+1]*c[7]-tb33 +in[2*5+1]*c[1]-in[2*7+1]*c[5];
+ tmp2a=in[2*0+0]-in[2*2+0]*c[4]+in[2*4+0]*c[8]+ta66 -in[2*8+0]*c[2];
+ tmp2b=in[2*0+1]-in[2*2+1]*c[4]+in[2*4+1]*c[8]+tb66 -in[2*8+1]*c[2];
+ MACRO1(3);
+ MACRO2(5);
+ }
+
+ {
+ REAL sum0,sum1;
+ sum0= in[2*0+0]-in[2*2+0]+in[2*4+0]-in[2*6+0]+in[2*8+0];
+ sum1=(in[2*0+1]-in[2*2+1]+in[2*4+1]-in[2*6+1]+in[2*8+1])*hsec_36[4];
+ MACRO0(4);
+ }
+ }
+ }
+
+
+}
+
+
+inline void dct12(REAL *in,REAL *prevblk1,REAL *prevblk2,REAL *wi,REAL *out) {
+
+#define DCT12_PART1 \
+ in5=in[5*3]; \
+ in5+=(in4=in[4*3]); \
+ in4+=(in3=in[3*3]); \
+ in3+=(in2=in[2*3]); \
+ in2+=(in1=in[1*3]); \
+ in1+=(in0=in[0*3]); \
+ \
+ in5+=in3;in3+=in1; \
+ \
+ in2*=cos1_6; \
+ in3*=cos1_6;
+
+#define DCT12_PART2 \
+ in0+=in4*cos2_6; \
+ \
+ in4=in0+in2; \
+ in0-=in2; \
+ \
+ in1+=in5*cos2_6; \
+ \
+ in5=(in1+in3)*hsec_12[0]; \
+ in1=(in1-in3)*hsec_12[2]; \
+ \
+ in3=in4+in5; \
+ in4-=in5; \
+ \
+ in2=in0+in1; \
+ in0-=in1;
+
+ {
+ REAL in0,in1,in2,in3,in4,in5;
+ register REAL *pb1=prevblk1;
+ out[SBLIMIT*0]=pb1[0];out[SBLIMIT*1]=pb1[1];out[SBLIMIT*2]=pb1[2];
+ out[SBLIMIT*3]=pb1[3];out[SBLIMIT*4]=pb1[4];out[SBLIMIT*5]=pb1[5];
+
+ DCT12_PART1;
+
+ {
+ REAL tmp0,tmp1=(in0-in4);
+ {
+ register REAL tmp2=(in1-in5)*hsec_12[1];
+ tmp0=tmp1+tmp2;
+ tmp1-=tmp2;
+ }
+ out[(17-1)*SBLIMIT]=pb1[17-1]+tmp0*wi[11-1];
+ out[(12+1)*SBLIMIT]=pb1[12+1]+tmp0*wi[ 6+1];
+ out[(6 +1)*SBLIMIT]=pb1[6 +1]+tmp1*wi[ 1 ];
+ out[(11-1)*SBLIMIT]=pb1[11-1]+tmp1*wi[ 5-1];
+ }
+
+ DCT12_PART2;
+ out[(17-0)*SBLIMIT]=pb1[17-0]+in2*wi[11-0];
+ out[(12+0)*SBLIMIT]=pb1[12+0]+in2*wi[ 6+0];
+ out[(12+2)*SBLIMIT]=pb1[12+2]+in3*wi[ 6+2];
+ out[(17-2)*SBLIMIT]=pb1[17-2]+in3*wi[11-2];
+
+ out[( 6+0)*SBLIMIT]=pb1[ 6+0]+in0*wi[0];
+ out[(11-0)*SBLIMIT]=pb1[11-0]+in0*wi[5-0];
+ out[( 6+2)*SBLIMIT]=pb1[ 6+2]+in4*wi[2];
+ out[(11-2)*SBLIMIT]=pb1[11-2]+in4*wi[5-2];
+ }
+
+ in++;
+ {
+ REAL in0,in1,in2,in3,in4,in5;
+ register REAL *pb2 = prevblk2;
+
+ DCT12_PART1;
+
+ {
+ REAL tmp0,tmp1=(in0-in4);
+ {
+ REAL tmp2=(in1-in5)*hsec_12[1];
+ tmp0=tmp1+tmp2;
+ tmp1-=tmp2;
+ }
+ pb2[5-1]=tmp0*wi[11-1];
+ pb2[0+1]=tmp0*wi[6+1];
+ out[(12+1)*SBLIMIT]+=tmp1*wi[1];
+ out[(17-1)*SBLIMIT]+=tmp1*wi[5-1];
+ }
+
+ DCT12_PART2;
+
+ pb2[5-0]=in2*wi[11-0];
+ pb2[0+0]=in2*wi[6+0];
+ pb2[0+2]=in3*wi[6+2];
+ pb2[5-2]=in3*wi[11-2];
+
+ out[(12+0)*SBLIMIT]+=in0*wi[0];
+ out[(17-0)*SBLIMIT]+=in0*wi[5-0];
+ out[(12+2)*SBLIMIT]+=in4*wi[2];
+ out[(17-2)*SBLIMIT]+=in4*wi[5-2];
+ }
+
+ in++;
+ {
+ REAL in0,in1,in2,in3,in4,in5;
+ register REAL *pb2 = prevblk2;
+ pb2[12]=pb2[13]=pb2[14]=pb2[15]=pb2[16]=pb2[17]=0.0;
+
+ DCT12_PART1;
+
+ {
+ REAL tmp0,tmp1=(in0-in4);
+ {
+ REAL tmp2=(in1-in5)*hsec_12[1];
+ tmp0=tmp1+tmp2;
+ tmp1-=tmp2;
+ }
+ pb2[11-1]=tmp0*wi[11-1];
+ pb2[ 6+1]=tmp0*wi[6+1];
+ pb2[ 0+1]+=tmp1*wi[1];
+ pb2[ 5-1]+=tmp1*wi[5-1];
+ }
+
+ DCT12_PART2;
+ pb2[11-0]=in2*wi[11-0];
+ pb2[ 6+0]=in2*wi[ 6+0];
+ pb2[ 6+2]=in3*wi[ 6+2];
+ pb2[11-2]=in3*wi[11-2];
+
+ pb2[ 0+0]+=in0*wi[0 ];
+ pb2[ 5-0]+=in0*wi[5-0];
+ pb2[ 0+2]+=in4*wi[2 ];
+ pb2[ 5-2]+=in4*wi[5-2];
+ }
+
+
+}
+
+
diff --git a/mpeglib/lib/splay/dct64.cpp b/mpeglib/lib/splay/dct64.cpp
new file mode 100644
index 00000000..14241651
--- /dev/null
+++ b/mpeglib/lib/splay/dct64.cpp
@@ -0,0 +1,202 @@
+/*
+ wrapper for dcts
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include "dct.h"
+#include "common.h"
+
+ATTR_ALIGN(64) static REAL hcos_64[16];
+ATTR_ALIGN(64) static REAL hcos_32[8];
+ATTR_ALIGN(64) static REAL hcos_16[4];
+ATTR_ALIGN(64) static REAL hcos_8[2];
+ATTR_ALIGN(64) static REAL hcos_4;
+
+/**
+ This was some time ago a standalone dct class,
+ but to get more speed I made it an inline dct
+ int the filter classes
+*/
+
+static int dct64Init=false;
+
+void initialize_dct64() {
+ if (dct64Init==true) {
+ return;
+ }
+ dct64Init=true;
+
+ int i;
+
+ for(i=0;i<16;i++) {
+ hcos_64[i]=1.0/(2.0*cos(MY_PI*double(i*2+1)/64.0));
+ }
+ for(i=0;i< 8;i++) {
+ hcos_32[i]=1.0/(2.0*cos(MY_PI*double(i*2+1)/32.0));
+ }
+ for(i=0;i< 4;i++) {
+ hcos_16[i]=1.0/(2.0*cos(MY_PI*double(i*2+1)/16.0));
+ }
+ for(i=0;i< 2;i++) {
+ hcos_8[i]=1.0/(2.0*cos(MY_PI*double(i*2+1)/ 8.0));
+ }
+ hcos_4=1.0/(2.0*cos(MY_PI*1.0/4.0));
+
+}
+
+
+
+
+//
+// splay dct64 , faster than mpeg123 dct. (from decode.c)
+//
+inline void dct64(REAL* out1,REAL* out2,REAL *fraction) {
+ ATTR_ALIGN(64) REAL p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pa,pb,pc,pd,pe,pf;
+ ATTR_ALIGN(64) REAL q0,q1,q2,q3,q4,q5,q6,q7,q8,q9,qa,qb,qc,qd,qe,qf;
+
+#define OUT1(v,t) out1[(32-(v))*16] =(-(out1[(v)*16]=t))
+#define OUT2(v) out2[(96-(v)-32)*16]=out2[((v)-32)*16]
+
+ // compute new values via a fast cosine transform:
+ {
+ // put to buffer 0..15
+ register REAL* x=fraction;
+
+ p0=x[ 0]+x[31];p1=x[ 1]+x[30];p2=x[ 2]+x[29];p3=x[ 3]+x[28];
+ p4=x[ 4]+x[27];p5=x[ 5]+x[26];p6=x[ 6]+x[25];p7=x[ 7]+x[24];
+ p8=x[ 8]+x[23];p9=x[ 9]+x[22];pa=x[10]+x[21];pb=x[11]+x[20];
+ pc=x[12]+x[19];pd=x[13]+x[18];pe=x[14]+x[17];pf=x[15]+x[16];
+ }
+
+ // put to buffer 32..39
+ q0=p0+pf;q1=p1+pe;q2=p2+pd;q3=p3+pc;
+ q4=p4+pb;q5=p5+pa;q6=p6+p9;q7=p7+p8;
+ // put to buffer 40..47
+ q8=hcos_32[0]*(p0-pf);q9=hcos_32[1]*(p1-pe);
+ qa=hcos_32[2]*(p2-pd);qb=hcos_32[3]*(p3-pc);
+ qc=hcos_32[4]*(p4-pb);qd=hcos_32[5]*(p5-pa);
+ qe=hcos_32[6]*(p6-p9);qf=hcos_32[7]*(p7-p8);
+
+ p0=q0+q7;p1=q1+q6;p2=q2+q5;p3=q3+q4;
+ p4=hcos_16[0]*(q0-q7);p5=hcos_16[1]*(q1-q6);
+ p6=hcos_16[2]*(q2-q5);p7=hcos_16[3]*(q3-q4);
+ p8=q8+qf;p9=q9+qe;pa=qa+qd;pb=qb+qc;
+ pc=hcos_16[0]*(q8-qf);pd=hcos_16[1]*(q9-qe);
+ pe=hcos_16[2]*(qa-qd);pf=hcos_16[3]*(qb-qc);
+
+ q0=p0+p3;q1=p1+p2;q2=hcos_8[0]*(p0-p3);q3=hcos_8[1]*(p1-p2);
+ q4=p4+p7;q5=p5+p6;q6=hcos_8[0]*(p4-p7);q7=hcos_8[1]*(p5-p6);
+ q8=p8+pb;q9=p9+pa;qa=hcos_8[0]*(p8-pb);qb=hcos_8[1]*(p9-pa);
+ qc=pc+pf;qd=pd+pe;qe=hcos_8[0]*(pc-pf);qf=hcos_8[1]*(pd-pe);
+
+ p0=q0+q1;p1=hcos_4*(q0-q1);p2=q2+q3;p3=hcos_4*(q2-q3);
+ p4=q4+q5;p5=hcos_4*(q4-q5);p6=q6+q7;p7=hcos_4*(q6-q7);
+ p8=q8+q9;p9=hcos_4*(q8-q9);pa=qa+qb;pb=hcos_4*(qa-qb);
+ pc=qc+qd;pd=hcos_4*(qc-qd);pe=qe+qf;pf=hcos_4*(qe-qf);
+
+ {
+ register REAL tmp;
+
+ tmp=p6+p7;
+ OUT2(36)=-(p5+tmp);
+ OUT2(44)=-(p4+tmp);
+ tmp=pb+pf;
+ OUT1(10,tmp);
+ OUT1(6,pd+tmp);
+ tmp=pe+pf;
+ OUT2(46)=-(p8+pc+tmp);
+ OUT2(34)=-(p9+pd+tmp);
+ tmp+=pa+pb;
+ OUT2(38)=-(pd+tmp);
+ OUT2(42)=-(pc+tmp);
+ OUT1(2,p9+pd+pf);
+ OUT1(4,p5+p7);
+ OUT2(48)=-p0;
+ out2[0]=-(out1[0]=p1);
+ OUT1( 8,p3);
+ OUT1(12,p7);
+ OUT1(14,pf);
+ OUT2(40)=-(p2+p3);
+ }
+
+ {
+ // put to buffer 16..31
+ register REAL *x=fraction;
+
+ p0=hcos_64[ 0]*(x[ 0]-x[31]);p1=hcos_64[ 1]*(x[ 1]-x[30]);
+ p2=hcos_64[ 2]*(x[ 2]-x[29]);p3=hcos_64[ 3]*(x[ 3]-x[28]);
+ p4=hcos_64[ 4]*(x[ 4]-x[27]);p5=hcos_64[ 5]*(x[ 5]-x[26]);
+ p6=hcos_64[ 6]*(x[ 6]-x[25]);p7=hcos_64[ 7]*(x[ 7]-x[24]);
+ p8=hcos_64[ 8]*(x[ 8]-x[23]);p9=hcos_64[ 9]*(x[ 9]-x[22]);
+ pa=hcos_64[10]*(x[10]-x[21]);pb=hcos_64[11]*(x[11]-x[20]);
+ pc=hcos_64[12]*(x[12]-x[19]);pd=hcos_64[13]*(x[13]-x[18]);
+ pe=hcos_64[14]*(x[14]-x[17]);pf=hcos_64[15]*(x[15]-x[16]);
+ }
+
+ // put to 48..63
+ q0=p0+pf;q1=p1+pe;q2=p2+pd;q3=p3+pc;
+ q4=p4+pb;q5=p5+pa;q6=p6+p9;q7=p7+p8;
+ q8=hcos_32[0]*(p0-pf);q9=hcos_32[1]*(p1-pe);
+ qa=hcos_32[2]*(p2-pd);qb=hcos_32[3]*(p3-pc);
+ qc=hcos_32[4]*(p4-pb);qd=hcos_32[5]*(p5-pa);
+ qe=hcos_32[6]*(p6-p9);qf=hcos_32[7]*(p7-p8);
+
+ p0=q0+q7;p1=q1+q6;p2=q2+q5;p3=q3+q4;
+ p4=hcos_16[0]*(q0-q7);p5=hcos_16[1]*(q1-q6);
+ p6=hcos_16[2]*(q2-q5);p7=hcos_16[3]*(q3-q4);
+ p8=q8+qf;p9=q9+qe;pa=qa+qd;pb=qb+qc;
+ pc=hcos_16[0]*(q8-qf);pd=hcos_16[1]*(q9-qe);
+ pe=hcos_16[2]*(qa-qd);pf=hcos_16[3]*(qb-qc);
+
+ q0=p0+p3;q1=p1+p2;q2=hcos_8[0]*(p0-p3);q3=hcos_8[1]*(p1-p2);
+ q4=p4+p7;q5=p5+p6;q6=hcos_8[0]*(p4-p7);q7=hcos_8[1]*(p5-p6);
+ q8=p8+pb;q9=p9+pa;qa=hcos_8[0]*(p8-pb);qb=hcos_8[1]*(p9-pa);
+ qc=pc+pf;qd=pd+pe;qe=hcos_8[0]*(pc-pf);qf=hcos_8[1]*(pd-pe);
+
+ p0=q0+q1;p1=hcos_4*(q0-q1);
+ p2=q2+q3;p3=hcos_4*(q2-q3);
+ p4=q4+q5;p5=hcos_4*(q4-q5);
+ p6=q6+q7;p7=hcos_4*(q6-q7);
+ p8=q8+q9;p9=hcos_4*(q8-q9);
+ pa=qa+qb;pb=hcos_4*(qa-qb);
+ pc=qc+qd;pd=hcos_4*(qc-qd);
+ pe=qe+qf;pf=hcos_4*(qe-qf);
+
+ {
+ REAL tmp;
+
+ tmp=pd+pf;
+ OUT1(5,p5+p7+pb+tmp);
+ tmp+=p9;
+ OUT1(1,p1+tmp);
+ OUT2(33)=-(p1+pe+tmp);
+ tmp+=p5+p7;
+ OUT1(3,tmp);
+ OUT2(35)=-(p6+pe+tmp);
+ tmp=pa+pb+pc+pd+pe+pf;
+ OUT2(39)=-(p2+p3+tmp-pc);
+ OUT2(43)=-(p4+p6+p7+tmp-pd);
+ OUT2(37)=-(p5+p6+p7+tmp-pc);
+ OUT2(41)=-(p2+p3+tmp-pd);
+ tmp=p8+pc+pe+pf;
+ OUT2(47)=-(p0+tmp);
+ OUT2(45)=-(p4+p6+p7+tmp);
+ tmp=pb+pf;
+ OUT1(11,p7+tmp);
+ tmp+=p3;
+ OUT1( 9,tmp);
+ OUT1( 7,pd+tmp);
+ OUT1(13,p7+pf);
+ OUT1(15,pf);
+ }
+
+}
+
+
diff --git a/mpeglib/lib/splay/dct64_down.cpp b/mpeglib/lib/splay/dct64_down.cpp
new file mode 100644
index 00000000..7137664c
--- /dev/null
+++ b/mpeglib/lib/splay/dct64_down.cpp
@@ -0,0 +1,215 @@
+/*
+ wrapper for dcts
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include "dct.h"
+#include "common.h"
+
+#include <iostream>
+
+using namespace std;
+
+ATTR_ALIGN(64) static REAL hcos_64_down[16];
+ATTR_ALIGN(64) static REAL hcos_32_down[8];
+ATTR_ALIGN(64) static REAL hcos_16_down[4];
+ATTR_ALIGN(64) static REAL hcos_8_down[2];
+ATTR_ALIGN(64) static REAL hcos_4_down;
+
+/**
+ This was some time ago a standalone dct class,
+ but to get more speed I made it an inline dct
+ int the filter classes
+*/
+
+static int dctInit=false;
+
+void initialize_dct64_downsample() {
+ if (dctInit==true) {
+ return;
+ }
+ dctInit=true;
+
+ int i;
+
+ for(i=0;i<16;i++) {
+ hcos_64_down[i]=1.0/(2.0*cos(MY_PI*double(i*2+1)/64.0));
+ }
+ for(i=0;i< 8;i++) {
+ hcos_32_down[i]=1.0/(2.0*cos(MY_PI*double(i*2+1)/32.0));
+ }
+ for(i=0;i< 4;i++) {
+ hcos_16_down[i]=1.0/(2.0*cos(MY_PI*double(i*2+1)/16.0));
+ }
+ for(i=0;i< 2;i++) {
+ hcos_8_down[i]=1.0/(2.0*cos(MY_PI*double(i*2+1)/ 8.0));
+ }
+ hcos_4_down=1.0/(2.0*cos(MY_PI*1.0/4.0));
+
+}
+
+inline void dct64_downsample(REAL* out1,REAL* out2,REAL *fraction) {
+ REAL p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pa,pb,pc,pd,pe,pf;
+ REAL q0,q1,q2,q3,q4,q5,q6,q7,q8,q9,qa,qb,qc,qd,qe,qf;
+
+#define OUT1(v,t) out1[(32-(v))*16] =(-(out1[(v)*16]=t))
+#define OUT2(v) out2[(96-(v)-32)*16]=out2[((v)-32)*16]
+
+ // compute new values via a fast cosine transform:
+ /* {
+ register REAL *x=fraction;
+
+ p0=x[ 0]+x[31];p1=x[ 1]+x[30];p2=x[ 2]+x[29];p3=x[ 3]+x[28];
+ p4=x[ 4]+x[27];p5=x[ 5]+x[26];p6=x[ 6]+x[25];p7=x[ 7]+x[24];
+ p8=x[ 8]+x[23];p9=x[ 9]+x[22];pa=x[10]+x[21];pb=x[11]+x[20];
+ pc=x[12]+x[19];pd=x[13]+x[18];pe=x[14]+x[17];pf=x[15]+x[16];
+ }
+
+ q0=p0+pf;q1=p1+pe;q2=p2+pd;q3=p3+pc;
+ q4=p4+pb;q5=p5+pa;q6=p6+p9;q7=p7+p8;
+ q8=hcos_32_down[0]*(p0-pf);q9=hcos_32_down[1]*(p1-pe);
+ qa=hcos_32_down[2]*(p2-pd);qb=hcos_32_down[3]*(p3-pc);
+ qc=hcos_32_down[4]*(p4-pb);qd=hcos_32_down[5]*(p5-pa);
+ qe=hcos_32_down[6]*(p6-p9);qf=hcos_32_down[7]*(p7-p8); */
+
+ {
+
+ register REAL *x=fraction;
+
+ q0=x[ 0]+x[15];q1=x[ 1]+x[14];q2=x[ 2]+x[13];q3=x[ 3]+x[12];
+ q4=x[ 4]+x[11];q5=x[ 5]+x[10];q6=x[ 6]+x[ 9];q7=x[ 7]+x[ 8];
+
+ q8=hcos_32_down[0]*(x[ 0]-x[15]);q9=hcos_32_down[1]*(x[ 1]-x[14]);
+ qa=hcos_32_down[2]*(x[ 2]-x[13]);qb=hcos_32_down[3]*(x[ 3]-x[12]);
+ qc=hcos_32_down[4]*(x[ 4]-x[11]);qd=hcos_32_down[5]*(x[ 5]-x[10]);
+ qe=hcos_32_down[6]*(x[ 6]-x[ 9]);qf=hcos_32_down[7]*(x[ 7]-x[ 8]);
+ }
+
+ p0=q0+q7;p1=q1+q6;p2=q2+q5;p3=q3+q4;
+ p4=hcos_16_down[0]*(q0-q7);p5=hcos_16_down[1]*(q1-q6);
+ p6=hcos_16_down[2]*(q2-q5);p7=hcos_16_down[3]*(q3-q4);
+ p8=q8+qf;p9=q9+qe;pa=qa+qd;pb=qb+qc;
+ pc=hcos_16_down[0]*(q8-qf);pd=hcos_16_down[1]*(q9-qe);
+ pe=hcos_16_down[2]*(qa-qd);pf=hcos_16_down[3]*(qb-qc);
+
+ q0=p0+p3;q1=p1+p2;q2=hcos_8_down[0]*(p0-p3);q3=hcos_8_down[1]*(p1-p2);
+ q4=p4+p7;q5=p5+p6;q6=hcos_8_down[0]*(p4-p7);q7=hcos_8_down[1]*(p5-p6);
+ q8=p8+pb;q9=p9+pa;qa=hcos_8_down[0]*(p8-pb);qb=hcos_8_down[1]*(p9-pa);
+ qc=pc+pf;qd=pd+pe;qe=hcos_8_down[0]*(pc-pf);qf=hcos_8_down[1]*(pd-pe);
+
+ p0=q0+q1;p1=hcos_4_down*(q0-q1);p2=q2+q3;p3=hcos_4_down*(q2-q3);
+ p4=q4+q5;p5=hcos_4_down*(q4-q5);p6=q6+q7;p7=hcos_4_down*(q6-q7);
+ p8=q8+q9;p9=hcos_4_down*(q8-q9);pa=qa+qb;pb=hcos_4_down*(qa-qb);
+ pc=qc+qd;pd=hcos_4_down*(qc-qd);pe=qe+qf;pf=hcos_4_down*(qe-qf);
+
+ {
+ register REAL tmp;
+
+ tmp=p6+p7;
+ OUT2(36)=-(p5+tmp);
+ OUT2(44)=-(p4+tmp);
+ tmp=pb+pf;
+ OUT1(10,tmp);
+ OUT1(6,pd+tmp);
+ tmp=pe+pf;
+ OUT2(46)=-(p8+pc+tmp);
+ OUT2(34)=-(p9+pd+tmp);
+ tmp+=pa+pb;
+ OUT2(38)=-(pd+tmp);
+ OUT2(42)=-(pc+tmp);
+ OUT1(2,p9+pd+pf);
+ OUT1(4,p5+p7);
+ OUT2(48)=-p0;
+ out2[0]=-(out1[0]=p1);
+ OUT1( 8,p3);
+ OUT1(12,p7);
+ OUT1(14,pf);
+ OUT2(40)=-(p2+p3);
+ }
+
+ {
+ register REAL *x=fraction;
+
+ /* p0=hcos_64_down[ 0]*(x[ 0]-x[31]);p1=hcos_64_down[ 1]*(x[ 1]-x[30]);
+ p2=hcos_64_down[ 2]*(x[ 2]-x[29]);p3=hcos_64_down[ 3]*(x[ 3]-x[28]);
+ p4=hcos_64_down[ 4]*(x[ 4]-x[27]);p5=hcos_64_down[ 5]*(x[ 5]-x[26]);
+ p6=hcos_64_down[ 6]*(x[ 6]-x[25]);p7=hcos_64_down[ 7]*(x[ 7]-x[24]);
+ p8=hcos_64_down[ 8]*(x[ 8]-x[23]);p9=hcos_64_down[ 9]*(x[ 9]-x[22]);
+ pa=hcos_64_down[10]*(x[10]-x[21]);pb=hcos_64_down[11]*(x[11]-x[20]);
+ pc=hcos_64_down[12]*(x[12]-x[19]);pd=hcos_64_down[13]*(x[13]-x[18]);
+ pe=hcos_64_down[14]*(x[14]-x[17]);pf=hcos_64_down[15]*(x[15]-x[16]); */
+
+ p0=hcos_64_down[ 0]*x[ 0];p1=hcos_64_down[ 1]*x[ 1];
+ p2=hcos_64_down[ 2]*x[ 2];p3=hcos_64_down[ 3]*x[ 3];
+ p4=hcos_64_down[ 4]*x[ 4];p5=hcos_64_down[ 5]*x[ 5];
+ p6=hcos_64_down[ 6]*x[ 6];p7=hcos_64_down[ 7]*x[ 7];
+ p8=hcos_64_down[ 8]*x[ 8];p9=hcos_64_down[ 9]*x[ 9];
+ pa=hcos_64_down[10]*x[10];pb=hcos_64_down[11]*x[11];
+ pc=hcos_64_down[12]*x[12];pd=hcos_64_down[13]*x[13];
+ pe=hcos_64_down[14]*x[14];pf=hcos_64_down[15]*x[15];
+ }
+
+ q0=p0+pf;q1=p1+pe;q2=p2+pd;q3=p3+pc;
+ q4=p4+pb;q5=p5+pa;q6=p6+p9;q7=p7+p8;
+ q8=hcos_32_down[0]*(p0-pf);q9=hcos_32_down[1]*(p1-pe);
+ qa=hcos_32_down[2]*(p2-pd);qb=hcos_32_down[3]*(p3-pc);
+ qc=hcos_32_down[4]*(p4-pb);qd=hcos_32_down[5]*(p5-pa);
+ qe=hcos_32_down[6]*(p6-p9);qf=hcos_32_down[7]*(p7-p8);
+
+ p0=q0+q7;p1=q1+q6;p2=q2+q5;p3=q3+q4;
+ p4=hcos_16_down[0]*(q0-q7);p5=hcos_16_down[1]*(q1-q6);
+ p6=hcos_16_down[2]*(q2-q5);p7=hcos_16_down[3]*(q3-q4);
+ p8=q8+qf;p9=q9+qe;pa=qa+qd;pb=qb+qc;
+ pc=hcos_16_down[0]*(q8-qf);pd=hcos_16_down[1]*(q9-qe);
+ pe=hcos_16_down[2]*(qa-qd);pf=hcos_16_down[3]*(qb-qc);
+
+ q0=p0+p3;q1=p1+p2;q2=hcos_8_down[0]*(p0-p3);q3=hcos_8_down[1]*(p1-p2);
+ q4=p4+p7;q5=p5+p6;q6=hcos_8_down[0]*(p4-p7);q7=hcos_8_down[1]*(p5-p6);
+ q8=p8+pb;q9=p9+pa;qa=hcos_8_down[0]*(p8-pb);qb=hcos_8_down[1]*(p9-pa);
+ qc=pc+pf;qd=pd+pe;qe=hcos_8_down[0]*(pc-pf);qf=hcos_8_down[1]*(pd-pe);
+
+ p0=q0+q1;p1=hcos_4_down*(q0-q1);
+ p2=q2+q3;p3=hcos_4_down*(q2-q3);
+ p4=q4+q5;p5=hcos_4_down*(q4-q5);
+ p6=q6+q7;p7=hcos_4_down*(q6-q7);
+ p8=q8+q9;p9=hcos_4_down*(q8-q9);
+ pa=qa+qb;pb=hcos_4_down*(qa-qb);
+ pc=qc+qd;pd=hcos_4_down*(qc-qd);
+ pe=qe+qf;pf=hcos_4_down*(qe-qf);
+
+ {
+ REAL tmp;
+
+ tmp=pd+pf;
+ OUT1(5,p5+p7+pb+tmp);
+ tmp+=p9;
+ OUT1(1,p1+tmp);
+ OUT2(33)=-(p1+pe+tmp);
+ tmp+=p5+p7;
+ OUT1(3,tmp);
+ OUT2(35)=-(p6+pe+tmp);
+ tmp=pa+pb+pc+pd+pe+pf;
+ OUT2(39)=-(p2+p3+tmp-pc);
+ OUT2(43)=-(p4+p6+p7+tmp-pd);
+ OUT2(37)=-(p5+p6+p7+tmp-pc);
+ OUT2(41)=-(p2+p3+tmp-pd);
+ tmp=p8+pc+pe+pf;
+ OUT2(47)=-(p0+tmp);
+ OUT2(45)=-(p4+p6+p7+tmp);
+ tmp=pb+pf;
+ OUT1(11,p7+tmp);
+ tmp+=p3;
+ OUT1( 9,tmp);
+ OUT1( 7,pd+tmp);
+ OUT1(13,p7+pf);
+ OUT1(15,pf);
+ }
+}
+
diff --git a/mpeglib/lib/splay/dump.cpp b/mpeglib/lib/splay/dump.cpp
new file mode 100644
index 00000000..62551194
--- /dev/null
+++ b/mpeglib/lib/splay/dump.cpp
@@ -0,0 +1,157 @@
+/*
+ frame dumper
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+typedef float REAL;
+
+#define SSLIMIT 18
+#define SBLIMIT 32
+
+
+
+#include "dump.h"
+#include <stdio.h>
+
+
+Dump::Dump() {
+
+}
+
+
+Dump::~Dump() {
+}
+
+
+void Dump::dump(REAL out[SBLIMIT][SSLIMIT]) {
+ FILE* f=fopen("dump.raw","a+");
+ int i;
+ int j;
+ for(i=0;i<SBLIMIT;i++) {
+ fprintf(f,"Line:%d\n",i);
+ for(j=0;j<SSLIMIT;j++) {
+ fprintf(f,"%.25f\n",out[i][j]);
+ }
+ }
+ fclose(f);
+}
+
+void Dump::dump(REAL out[SBLIMIT*SSLIMIT]) {
+ FILE* f=fopen("dump.raw","a+");
+ int i;
+ int line=0;
+ for(i=0;i<SBLIMIT*SSLIMIT;i++) {
+ if ( (i % SSLIMIT) == 0) {
+ fprintf(f,"Line:%d\n",line++);
+ }
+ fprintf(f,"%.25f\n",out[i]);
+ }
+ fclose(f);
+}
+
+void Dump::dump2(REAL out[SSLIMIT*SBLIMIT]) {
+ FILE* f=fopen("dump.raw","a+");
+ int i;
+ int j;
+ int line=0;
+ for(i=0;i<SSLIMIT;i++) {
+ fprintf(f,"Line:%d\n",line++);
+ for(j=0;j<SBLIMIT;j++) {
+ fprintf(f,"%.25f\n",out[i*SBLIMIT+j]);
+ }
+ }
+ fclose(f);
+}
+
+void Dump::dump(REAL out[SSLIMIT][SBLIMIT]) {
+ FILE* f=fopen("dump.raw","a+");
+ int i;
+ int j;
+ for(i=0;i<SBLIMIT;i++) {
+ fprintf(f,"Line:%d\n",i);
+ for(j=0;j<SSLIMIT;j++) {
+ fprintf(f,"%.25f\n",out[j][i]);
+ }
+ }
+ fclose(f);
+}
+
+void Dump::dump(int out[SBLIMIT][SSLIMIT]) {
+ FILE* f=fopen("dump.raw","a+");
+ int i;
+ int j;
+ for(i=0;i<SBLIMIT;i++) {
+ fprintf(f,"Line:%d\n",i);
+ for(j=0;j<SSLIMIT;j++) {
+ if (out[i][j] == 0) {
+ fprintf(f," %d ",out[i][j]);
+ continue;
+ }
+ if (out[i][j] < 0) {
+ fprintf(f," -x");
+ continue;
+ }
+ fprintf(f," +x");
+
+ }
+ fprintf(f," \n");
+
+ }
+ fclose(f);
+}
+
+void Dump::dump(char* ptr,int len,int ldelete) {
+ FILE* f;
+ if (ldelete) {
+ f=fopen("/tmp/dump.raw","w+");
+ } else {
+ f=fopen("/tmp/dump.raw","a+");
+ }
+ fwrite(ptr,len,1,f);
+ fclose(f);
+}
+
+
+void Dump::scale_zero(layer3scalefactor* out) {
+
+ int i;
+ int j;
+
+ for(i=0;i<23;i++) {
+ out->l[i]=0;
+ }
+
+ for(i=0;i<3;i++) {
+ for(j=0;j<13;j++) {
+ out->s[i][j]=0;
+ }
+ }
+}
+
+
+void Dump::dump(layer3scalefactor* out) {
+ FILE* f=fopen("dump.raw","a+");
+ int i;
+ int j;
+
+ for(i=0;i<23;i++) {
+ fprintf(f,"l[%d]=%d\n",i,out->l[i]);
+ }
+
+ for(i=0;i<3;i++) {
+ for(j=0;j<13;j++) {
+ fprintf(f,"s[%d][%d]=%d\n",i,j,out->s[i][j]);
+ }
+ }
+ fprintf(f,"---------\n");
+
+ fclose(f);
+}
diff --git a/mpeglib/lib/splay/dump.h b/mpeglib/lib/splay/dump.h
new file mode 100644
index 00000000..47ef4d48
--- /dev/null
+++ b/mpeglib/lib/splay/dump.h
@@ -0,0 +1,36 @@
+/*
+ frame dumper
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __DUMP_H
+#define __DUMP_H
+
+#include "mpegsound.h"
+
+class Dump {
+
+ public:
+ Dump();
+ ~Dump();
+
+ void dump(REAL out[SBLIMIT][SSLIMIT]);
+ void dump(REAL out[SBLIMIT*SSLIMIT]);
+ void dump2(REAL out[SSLIMIT*SBLIMIT]);
+ void dump(REAL out[SSLIMIT][SBLIMIT]);
+ void dump(int out[SBLIMIT][SSLIMIT]);
+ void dump(layer3scalefactor* out);
+ void dump(char* prt,int len,int ldelete=false);
+ void scale_zero(layer3scalefactor* out);
+
+
+};
+#endif
diff --git a/mpeglib/lib/splay/dxHead.cpp b/mpeglib/lib/splay/dxHead.cpp
new file mode 100644
index 00000000..a96bb553
--- /dev/null
+++ b/mpeglib/lib/splay/dxHead.cpp
@@ -0,0 +1,129 @@
+/*---- DXhead.c --------------------------------------------
+
+decoder MPEG Layer III
+handle Xing header
+mod 12/7/98 add vbr scale
+
+Copyright 1998 Xing Technology Corp.
+-----------------------------------------------------------*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <float.h>
+#include <math.h>
+#include "dxHead.h"
+
+// 4 Xing
+// 4 flags
+// 4 frames
+// 4 bytes
+// 100 toc
+
+/*-------------------------------------------------------------*/
+static int ExtractI4(unsigned char *buf)
+{
+int x;
+// big endian extract
+
+x = buf[0];
+x <<= 8;
+x |= buf[1];
+x <<= 8;
+x |= buf[2];
+x <<= 8;
+x |= buf[3];
+
+return x;
+}
+/*-------------------------------------------------------------*/
+int GetXingHeader(XHEADDATA *X, unsigned char *buf)
+{
+int i, head_flags;
+int h_id, h_mode, h_sr_index;
+static const int sr_table[4] = { 44100, 48000, 32000, 99999 };
+
+// get Xing header data
+
+
+X->flags = 0; // clear to null incase fail
+
+
+// get selected MPEG header data
+h_id = (buf[1] >> 3) & 1;
+h_sr_index = (buf[2] >> 2) & 3;
+h_mode = (buf[3] >> 6) & 3;
+
+
+// determine offset of header
+if( h_id ) { // mpeg1
+ if( h_mode != 3 ) buf+=(32+4);
+ else buf+=(17+4);
+}
+else { // mpeg2
+ if( h_mode != 3 ) buf+=(17+4);
+ else buf+=(9+4);
+}
+
+if( buf[0] != 'X' ) return 0; // fail
+if( buf[1] != 'i' ) return 0; // header not found
+if( buf[2] != 'n' ) return 0;
+if( buf[3] != 'g' ) return 0;
+buf+=4;
+
+X->h_id = h_id;
+X->samprate = sr_table[h_sr_index];
+if( h_id == 0 ) X->samprate >>= 1;
+
+head_flags = X->flags = ExtractI4(buf); buf+=4; // get flags
+
+if( head_flags & FRAMES_FLAG ) {X->frames = ExtractI4(buf); buf+=4;}
+if( head_flags & BYTES_FLAG ) {X->bytes = ExtractI4(buf); buf+=4;}
+
+if( head_flags & TOC_FLAG ) {
+ if( X->toc != NULL ) {
+ for(i=0;i<100;i++) X->toc[i] = buf[i];
+ }
+ buf+=100;
+}
+
+X->vbr_scale = -1;
+if( head_flags & VBR_SCALE_FLAG ) {X->vbr_scale = ExtractI4(buf); buf+=4;}
+
+//if( X->toc != NULL ) {
+//for(i=0;i<100;i++) {
+// if( (i%10) == 0 ) printf("\n");
+// printf(" %3d", (int)(X->toc[i]));
+//}
+//}
+
+return 1; // success
+}
+/*-------------------------------------------------------------*/
+int SeekPoint(unsigned char TOC[100], int file_bytes, float percent)
+{
+// interpolate in TOC to get file seek point in bytes
+int a, seekpoint;
+float fa, fb, fx;
+
+
+if( percent < 0.0f ) percent = 0.0f;
+if( percent > 100.0f ) percent = 100.0f;
+
+a = (int)percent;
+if( a > 99 ) a = 99;
+fa = TOC[a];
+if( a < 99 ) {
+ fb = TOC[a+1];
+}
+else {
+ fb = 256.0f;
+}
+
+fx = fa + (fb-fa)*(percent-a);
+//printf("%f ....... %f ...... %f\n",fa,fb,fx);
+
+seekpoint = (int)((1.0f/256.0f)*fx*file_bytes);
+
+
+return seekpoint;
+}
+/*-------------------------------------------------------------*/
diff --git a/mpeglib/lib/splay/dxHead.h b/mpeglib/lib/splay/dxHead.h
new file mode 100644
index 00000000..da0f2eff
--- /dev/null
+++ b/mpeglib/lib/splay/dxHead.h
@@ -0,0 +1,66 @@
+/*---- DXhead.h -------------------------------------------
+
+
+decoder MPEG Layer III
+
+handle Xing header
+
+
+Copyright 1998 Xing Technology Corp.
+-----------------------------------------------------------*/
+// A Xing header may be present in the ancillary
+// data field of the first frame of an mp3 bitstream
+// The Xing header (optionally) contains
+// frames total number of audio frames in the bitstream
+// bytes total number of bytes in the bitstream
+// toc table of contents
+
+// toc (table of contents^) gives seek points
+// for random access
+// the ith entry determines the seek point for
+// i-percent duration
+// seek point in bytes = (toc[i]/256.0) * total_bitstream_bytes
+// e.g. half duration seek point = (toc[50]/256.0) * total_bitstream_bytes
+
+#ifndef __DXHEAD_H
+#define __DXHEAD_H
+
+#define FRAMES_FLAG 0x0001
+#define BYTES_FLAG 0x0002
+#define TOC_FLAG 0x0004
+#define VBR_SCALE_FLAG 0x0008
+
+#define FRAMES_AND_BYTES (FRAMES_FLAG | BYTES_FLAG)
+
+// structure to receive extracted header
+// toc may be NULL
+
+typedef struct XHEADDATA_s {
+ int h_id; // from MPEG header, 0=MPEG2, 1=MPEG1
+ int samprate; // determined from MPEG header
+ int flags; // from Xing header data
+ int frames; // total bit stream frames from Xing header data
+ int bytes; // total bit stream bytes from Xing header data
+ int vbr_scale; // encoded vbr scale from Xing header data
+ unsigned char *toc; // pointer to unsigned char toc_buffer[100]
+ // may be NULL if toc not desired
+} XHEADDATA;
+
+int GetXingHeader(XHEADDATA *X, unsigned char *buf);
+
+// return 0=fail, 1=success
+// X structure to receive header data (output)
+// buf bitstream input
+
+
+int SeekPoint(unsigned char TOC[100], int file_bytes, float percent);
+// return seekpoint in bytes (may be at eof if percent=100.0)
+// TOC = table of contents from Xing header
+// file_bytes = number of bytes in mp3 file
+// percent = play time percentage of total playtime. May be
+// fractional (e.g. 87.245)
+
+
+#endif
+
+
diff --git a/mpeglib/lib/splay/huffmanlookup.cpp b/mpeglib/lib/splay/huffmanlookup.cpp
new file mode 100644
index 00000000..e4b37453
--- /dev/null
+++ b/mpeglib/lib/splay/huffmanlookup.cpp
@@ -0,0 +1,120 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "huffmanlookup.h"
+#include <assert.h>
+
+
+
+
+struct HuffmanLookup::decodeData HuffmanLookup::qdecode[32][256];
+/* for initialization */
+static HuffmanLookup l;
+
+HuffmanLookup::HuffmanLookup()
+{
+ int table,p,x,y;
+
+ for(table = 0; table < 32; table++)
+ {
+ // 8 bits pattern
+ for(p = 0; p < 256; p++)
+ {
+ bits = 24;
+ pattern = (p << 16);
+
+ huffmandecoder_1(&Mpegtoraw::ht[table], &x,&y);
+
+ int used = 24 - bits;
+ qdecode[table][p].skip = (used <= 8)?used:0;
+ qdecode[table][p].x = x;
+ qdecode[table][p].y = y;
+ }
+ }
+}
+
+int HuffmanLookup::wgetbit()
+{
+ return (pattern >> --bits) & 1;
+}
+
+int HuffmanLookup::wgetbits (int b)
+{
+ bits -= b;
+ return (pattern >> bits) & ((1 << b) - 1);
+}
+
+void HuffmanLookup::huffmandecoder_1(const HUFFMANCODETABLE *h, int *x, int *y)
+{
+ typedef unsigned int HUFFBITS;
+
+ HUFFBITS level=(1<<(sizeof(HUFFBITS)*8-1));
+ int point=0;
+ /* Lookup in Huffman table. */
+ for(;;)
+ {
+ if(h->val[point][0]==0)
+ { /*end of tree*/
+ int xx,yy;
+
+ xx=h->val[point][1]>>4;
+ yy=h->val[point][1]&0xf;
+
+ if(h->linbits)
+ {
+ if((h->xlen)==(unsigned)xx)xx+=wgetbits(h->linbits);
+ if(xx)if(wgetbit())xx=-xx;
+ if((h->ylen)==(unsigned)yy)yy+=wgetbits(h->linbits);
+ if(yy)if(wgetbit())yy=-yy;
+ }
+ else
+ {
+ if(xx)if(wgetbit())xx=-xx;
+ if(yy)if(wgetbit())yy=-yy;
+ }
+ *x=xx;*y=yy;
+ break;
+ }
+
+ point+=h->val[point][wgetbit()];
+
+ level>>=1;
+ if(!(level || ((unsigned)point<Mpegtoraw::ht->treelen)))
+ {
+ register int xx,yy;
+
+ xx=(h->xlen<<1);// set x and y to a medium value as a simple concealment
+ yy=(h->ylen<<1);
+
+ // h->xlen and h->ylen can't be 1 under tablename 32
+ // if(xx)
+ if(wgetbit())xx=-xx;
+ // if(yy)
+ if(wgetbit())yy=-yy;
+
+ *x=xx;*y=yy;
+ break;
+ }
+ }
+}
+
+
diff --git a/mpeglib/lib/splay/huffmanlookup.h b/mpeglib/lib/splay/huffmanlookup.h
new file mode 100644
index 00000000..a5bdb26c
--- /dev/null
+++ b/mpeglib/lib/splay/huffmanlookup.h
@@ -0,0 +1,57 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef __HUFFMANLOOKUP_H
+#define __HUFFMANLOOKUP_H
+#include "mpegsound.h"
+
+/*
+ * This class speeds up the huffman table decoding by largely replacing it
+ * by a table lookup. It uses the fact that the huffman tables don't change,
+ * and that given a byte of the bitstream, we can predict what the result
+ * will be (without reading it bit by bit).
+ */
+
+class HuffmanLookup {
+private:
+ long pattern, bits;
+ int wgetbit();
+ int wgetbits (int b);
+ void huffmandecoder_1(const HUFFMANCODETABLE *h,int *x, int *y);
+
+ ATTR_ALIGN(64) static struct decodeData {
+ int x : 8;
+ int y : 8;
+ int skip : 16;
+ } qdecode[32][256];
+public:
+ HuffmanLookup();
+
+ static int decode(int table, int pattern, int* x, int* y)
+ {
+ *x = qdecode[table][pattern].x;
+ *y = qdecode[table][pattern].y;
+ return qdecode[table][pattern].skip;
+ }
+};
+
+#endif
diff --git a/mpeglib/lib/splay/huffmantable.cpp b/mpeglib/lib/splay/huffmantable.cpp
new file mode 100644
index 00000000..a1fe20e5
--- /dev/null
+++ b/mpeglib/lib/splay/huffmantable.cpp
@@ -0,0 +1,584 @@
+/* MPEG/WAVE Sound library
+
+ (C) 1997 by Jung woo-jae */
+
+// Huffmantable.cc
+// It contains initialized huffman table for MPEG layer 3
+
+
+#include "mpegsound.h"
+
+static const unsigned int
+htd01[ 7][2]={{ 2, 1},{ 0, 0},{ 2, 1},{ 0, 16},{ 2, 1},{ 0, 1},
+ { 0, 17}},
+
+htd02[ 17][2]={{ 2, 1},{ 0, 0},{ 4, 1},{ 2, 1},{ 0, 16},{ 0, 1},
+ { 2, 1},{ 0, 17},{ 4, 1},{ 2, 1},{ 0, 32},{ 0, 33},
+ { 2, 1},{ 0, 18},{ 2, 1},{ 0, 2},{ 0, 34}},
+
+htd03[ 17][2]={{ 4, 1},{ 2, 1},{ 0, 0},{ 0, 1},{ 2, 1},{ 0, 17},
+ { 2, 1},{ 0, 16},{ 4, 1},{ 2, 1},{ 0, 32},{ 0, 33},
+ { 2, 1},{ 0, 18},{ 2, 1},{ 0, 2},{ 0, 34}},
+
+htd05[ 31][2]={{ 2, 1},{ 0, 0},{ 4, 1},{ 2, 1},{ 0, 16},{ 0, 1},
+ { 2, 1},{ 0, 17},{ 8, 1},{ 4, 1},{ 2, 1},{ 0, 32},
+ { 0, 2},{ 2, 1},{ 0, 33},{ 0, 18},{ 8, 1},{ 4, 1},
+ { 2, 1},{ 0, 34},{ 0, 48},{ 2, 1},{ 0, 3},{ 0, 19},
+ { 2, 1},{ 0, 49},{ 2, 1},{ 0, 50},{ 2, 1},{ 0, 35},
+ { 0, 51}},
+
+htd06[ 31][2]={{ 6, 1},{ 4, 1},{ 2, 1},{ 0, 0},{ 0, 16},{ 0, 17},
+ { 6, 1},{ 2, 1},{ 0, 1},{ 2, 1},{ 0, 32},{ 0, 33},
+ { 6, 1},{ 2, 1},{ 0, 18},{ 2, 1},{ 0, 2},{ 0, 34},
+ { 4, 1},{ 2, 1},{ 0, 49},{ 0, 19},{ 4, 1},{ 2, 1},
+ { 0, 48},{ 0, 50},{ 2, 1},{ 0, 35},{ 2, 1},{ 0, 3},
+ { 0, 51}},
+
+htd07[ 71][2]={{ 2, 1},{ 0, 0},{ 4, 1},{ 2, 1},{ 0, 16},{ 0, 1},
+ { 8, 1},{ 2, 1},{ 0, 17},{ 4, 1},{ 2, 1},{ 0, 32},
+ { 0, 2},{ 0, 33},{ 18, 1},{ 6, 1},{ 2, 1},{ 0, 18},
+ { 2, 1},{ 0, 34},{ 0, 48},{ 4, 1},{ 2, 1},{ 0, 49},
+ { 0, 19},{ 4, 1},{ 2, 1},{ 0, 3},{ 0, 50},{ 2, 1},
+ { 0, 35},{ 0, 4},{ 10, 1},{ 4, 1},{ 2, 1},{ 0, 64},
+ { 0, 65},{ 2, 1},{ 0, 20},{ 2, 1},{ 0, 66},{ 0, 36},
+ { 12, 1},{ 6, 1},{ 4, 1},{ 2, 1},{ 0, 51},{ 0, 67},
+ { 0, 80},{ 4, 1},{ 2, 1},{ 0, 52},{ 0, 5},{ 0, 81},
+ { 6, 1},{ 2, 1},{ 0, 21},{ 2, 1},{ 0, 82},{ 0, 37},
+
+ { 4, 1},{ 2, 1},{ 0, 68},{ 0, 53},{ 4, 1},{ 2, 1},
+ { 0, 83},{ 0, 84},{ 2, 1},{ 0, 69},{ 0, 85}},
+
+htd08[ 71][2]={{ 6, 1},{ 2, 1},{ 0, 0},{ 2, 1},{ 0, 16},{ 0, 1},
+ { 2, 1},{ 0, 17},{ 4, 1},{ 2, 1},{ 0, 33},{ 0, 18},
+ { 14, 1},{ 4, 1},{ 2, 1},{ 0, 32},{ 0, 2},{ 2, 1},
+ { 0, 34},{ 4, 1},{ 2, 1},{ 0, 48},{ 0, 3},{ 2, 1},
+ { 0, 49},{ 0, 19},{ 14, 1},{ 8, 1},{ 4, 1},{ 2, 1},
+ { 0, 50},{ 0, 35},{ 2, 1},{ 0, 64},{ 0, 4},{ 2, 1},
+ { 0, 65},{ 2, 1},{ 0, 20},{ 0, 66},{ 12, 1},{ 6, 1},
+ { 2, 1},{ 0, 36},{ 2, 1},{ 0, 51},{ 0, 80},{ 4, 1},
+ { 2, 1},{ 0, 67},{ 0, 52},{ 0, 81},{ 6, 1},{ 2, 1},
+ { 0, 21},{ 2, 1},{ 0, 5},{ 0, 82},{ 6, 1},{ 2, 1},
+
+ { 0, 37},{ 2, 1},{ 0, 68},{ 0, 53},{ 2, 1},{ 0, 83},
+ { 2, 1},{ 0, 69},{ 2, 1},{ 0, 84},{ 0, 85}},
+
+htd09[ 71][2]={{ 8, 1},{ 4, 1},{ 2, 1},{ 0, 0},{ 0, 16},{ 2, 1},
+ { 0, 1},{ 0, 17},{ 10, 1},{ 4, 1},{ 2, 1},{ 0, 32},
+ { 0, 33},{ 2, 1},{ 0, 18},{ 2, 1},{ 0, 2},{ 0, 34},
+ { 12, 1},{ 6, 1},{ 4, 1},{ 2, 1},{ 0, 48},{ 0, 3},
+ { 0, 49},{ 2, 1},{ 0, 19},{ 2, 1},{ 0, 50},{ 0, 35},
+ { 12, 1},{ 4, 1},{ 2, 1},{ 0, 65},{ 0, 20},{ 4, 1},
+ { 2, 1},{ 0, 64},{ 0, 51},{ 2, 1},{ 0, 66},{ 0, 36},
+ { 10, 1},{ 6, 1},{ 4, 1},{ 2, 1},{ 0, 4},{ 0, 80},
+ { 0, 67},{ 2, 1},{ 0, 52},{ 0, 81},{ 8, 1},{ 4, 1},
+ { 2, 1},{ 0, 21},{ 0, 82},{ 2, 1},{ 0, 37},{ 0, 68},
+
+ { 6, 1},{ 4, 1},{ 2, 1},{ 0, 5},{ 0, 84},{ 0, 83},
+ { 2, 1},{ 0, 53},{ 2, 1},{ 0, 69},{ 0, 85}},
+
+htd10[127][2]={{ 2, 1},{ 0, 0},{ 4, 1},{ 2, 1},{ 0, 16},{ 0, 1},
+ { 10, 1},{ 2, 1},{ 0, 17},{ 4, 1},{ 2, 1},{ 0, 32},
+ { 0, 2},{ 2, 1},{ 0, 33},{ 0, 18},{ 28, 1},{ 8, 1},
+ { 4, 1},{ 2, 1},{ 0, 34},{ 0, 48},{ 2, 1},{ 0, 49},
+ { 0, 19},{ 8, 1},{ 4, 1},{ 2, 1},{ 0, 3},{ 0, 50},
+ { 2, 1},{ 0, 35},{ 0, 64},{ 4, 1},{ 2, 1},{ 0, 65},
+ { 0, 20},{ 4, 1},{ 2, 1},{ 0, 4},{ 0, 51},{ 2, 1},
+ { 0, 66},{ 0, 36},{ 28, 1},{ 10, 1},{ 6, 1},{ 4, 1},
+ { 2, 1},{ 0, 80},{ 0, 5},{ 0, 96},{ 2, 1},{ 0, 97},
+ { 0, 22},{ 12, 1},{ 6, 1},{ 4, 1},{ 2, 1},{ 0, 67},
+
+ { 0, 52},{ 0, 81},{ 2, 1},{ 0, 21},{ 2, 1},{ 0, 82},
+ { 0, 37},{ 4, 1},{ 2, 1},{ 0, 38},{ 0, 54},{ 0,113},
+ { 20, 1},{ 8, 1},{ 2, 1},{ 0, 23},{ 4, 1},{ 2, 1},
+ { 0, 68},{ 0, 83},{ 0, 6},{ 6, 1},{ 4, 1},{ 2, 1},
+ { 0, 53},{ 0, 69},{ 0, 98},{ 2, 1},{ 0,112},{ 2, 1},
+ { 0, 7},{ 0,100},{ 14, 1},{ 4, 1},{ 2, 1},{ 0,114},
+ { 0, 39},{ 6, 1},{ 2, 1},{ 0, 99},{ 2, 1},{ 0, 84},
+ { 0, 85},{ 2, 1},{ 0, 70},{ 0,115},{ 8, 1},{ 4, 1},
+ { 2, 1},{ 0, 55},{ 0,101},{ 2, 1},{ 0, 86},{ 0,116},
+ { 6, 1},{ 2, 1},{ 0, 71},{ 2, 1},{ 0,102},{ 0,117},
+
+ { 4, 1},{ 2, 1},{ 0, 87},{ 0,118},{ 2, 1},{ 0,103},
+ { 0,119}},
+
+htd11[127][2]={{ 6, 1},{ 2, 1},{ 0, 0},{ 2, 1},{ 0, 16},{ 0, 1},
+ { 8, 1},{ 2, 1},{ 0, 17},{ 4, 1},{ 2, 1},{ 0, 32},
+ { 0, 2},{ 0, 18},{ 24, 1},{ 8, 1},{ 2, 1},{ 0, 33},
+ { 2, 1},{ 0, 34},{ 2, 1},{ 0, 48},{ 0, 3},{ 4, 1},
+ { 2, 1},{ 0, 49},{ 0, 19},{ 4, 1},{ 2, 1},{ 0, 50},
+ { 0, 35},{ 4, 1},{ 2, 1},{ 0, 64},{ 0, 4},{ 2, 1},
+ { 0, 65},{ 0, 20},{ 30, 1},{ 16, 1},{ 10, 1},{ 4, 1},
+ { 2, 1},{ 0, 66},{ 0, 36},{ 4, 1},{ 2, 1},{ 0, 51},
+ { 0, 67},{ 0, 80},{ 4, 1},{ 2, 1},{ 0, 52},{ 0, 81},
+ { 0, 97},{ 6, 1},{ 2, 1},{ 0, 22},{ 2, 1},{ 0, 6},
+
+ { 0, 38},{ 2, 1},{ 0, 98},{ 2, 1},{ 0, 21},{ 2, 1},
+ { 0, 5},{ 0, 82},{ 16, 1},{ 10, 1},{ 6, 1},{ 4, 1},
+ { 2, 1},{ 0, 37},{ 0, 68},{ 0, 96},{ 2, 1},{ 0, 99},
+ { 0, 54},{ 4, 1},{ 2, 1},{ 0,112},{ 0, 23},{ 0,113},
+ { 16, 1},{ 6, 1},{ 4, 1},{ 2, 1},{ 0, 7},{ 0,100},
+ { 0,114},{ 2, 1},{ 0, 39},{ 4, 1},{ 2, 1},{ 0, 83},
+ { 0, 53},{ 2, 1},{ 0, 84},{ 0, 69},{ 10, 1},{ 4, 1},
+ { 2, 1},{ 0, 70},{ 0,115},{ 2, 1},{ 0, 55},{ 2, 1},
+ { 0,101},{ 0, 86},{ 10, 1},{ 6, 1},{ 4, 1},{ 2, 1},
+ { 0, 85},{ 0, 87},{ 0,116},{ 2, 1},{ 0, 71},{ 0,102},
+
+ { 4, 1},{ 2, 1},{ 0,117},{ 0,118},{ 2, 1},{ 0,103},
+ { 0,119}},
+
+htd12[127][2]={{ 12, 1},{ 4, 1},{ 2, 1},{ 0, 16},{ 0, 1},{ 2, 1},
+ { 0, 17},{ 2, 1},{ 0, 0},{ 2, 1},{ 0, 32},{ 0, 2},
+ { 16, 1},{ 4, 1},{ 2, 1},{ 0, 33},{ 0, 18},{ 4, 1},
+ { 2, 1},{ 0, 34},{ 0, 49},{ 2, 1},{ 0, 19},{ 2, 1},
+ { 0, 48},{ 2, 1},{ 0, 3},{ 0, 64},{ 26, 1},{ 8, 1},
+ { 4, 1},{ 2, 1},{ 0, 50},{ 0, 35},{ 2, 1},{ 0, 65},
+ { 0, 51},{ 10, 1},{ 4, 1},{ 2, 1},{ 0, 20},{ 0, 66},
+ { 2, 1},{ 0, 36},{ 2, 1},{ 0, 4},{ 0, 80},{ 4, 1},
+ { 2, 1},{ 0, 67},{ 0, 52},{ 2, 1},{ 0, 81},{ 0, 21},
+ { 28, 1},{ 14, 1},{ 8, 1},{ 4, 1},{ 2, 1},{ 0, 82},
+
+ { 0, 37},{ 2, 1},{ 0, 83},{ 0, 53},{ 4, 1},{ 2, 1},
+ { 0, 96},{ 0, 22},{ 0, 97},{ 4, 1},{ 2, 1},{ 0, 98},
+ { 0, 38},{ 6, 1},{ 4, 1},{ 2, 1},{ 0, 5},{ 0, 6},
+ { 0, 68},{ 2, 1},{ 0, 84},{ 0, 69},{ 18, 1},{ 10, 1},
+ { 4, 1},{ 2, 1},{ 0, 99},{ 0, 54},{ 4, 1},{ 2, 1},
+ { 0,112},{ 0, 7},{ 0,113},{ 4, 1},{ 2, 1},{ 0, 23},
+ { 0,100},{ 2, 1},{ 0, 70},{ 0,114},{ 10, 1},{ 6, 1},
+ { 2, 1},{ 0, 39},{ 2, 1},{ 0, 85},{ 0,115},{ 2, 1},
+ { 0, 55},{ 0, 86},{ 8, 1},{ 4, 1},{ 2, 1},{ 0,101},
+ { 0,116},{ 2, 1},{ 0, 71},{ 0,102},{ 4, 1},{ 2, 1},
+
+ { 0,117},{ 0, 87},{ 2, 1},{ 0,118},{ 2, 1},{ 0,103},
+ { 0,119}},
+
+htd13[511][2]={{ 2, 1},{ 0, 0},{ 6, 1},{ 2, 1},{ 0, 16},{ 2, 1},
+ { 0, 1},{ 0, 17},{ 28, 1},{ 8, 1},{ 4, 1},{ 2, 1},
+ { 0, 32},{ 0, 2},{ 2, 1},{ 0, 33},{ 0, 18},{ 8, 1},
+ { 4, 1},{ 2, 1},{ 0, 34},{ 0, 48},{ 2, 1},{ 0, 3},
+ { 0, 49},{ 6, 1},{ 2, 1},{ 0, 19},{ 2, 1},{ 0, 50},
+ { 0, 35},{ 4, 1},{ 2, 1},{ 0, 64},{ 0, 4},{ 0, 65},
+ { 70, 1},{ 28, 1},{ 14, 1},{ 6, 1},{ 2, 1},{ 0, 20},
+ { 2, 1},{ 0, 51},{ 0, 66},{ 4, 1},{ 2, 1},{ 0, 36},
+ { 0, 80},{ 2, 1},{ 0, 67},{ 0, 52},{ 4, 1},{ 2, 1},
+ { 0, 81},{ 0, 21},{ 4, 1},{ 2, 1},{ 0, 5},{ 0, 82}, // 60
+
+ { 2, 1},{ 0, 37},{ 2, 1},{ 0, 68},{ 0, 83},{ 14, 1},
+ { 8, 1},{ 4, 1},{ 2, 1},{ 0, 96},{ 0, 6},{ 2, 1},
+ { 0, 97},{ 0, 22},{ 4, 1},{ 2, 1},{ 0,128},{ 0, 8},
+ { 0,129},{ 16, 1},{ 8, 1},{ 4, 1},{ 2, 1},{ 0, 53},
+ { 0, 98},{ 2, 1},{ 0, 38},{ 0, 84},{ 4, 1},{ 2, 1},
+ { 0, 69},{ 0, 99},{ 2, 1},{ 0, 54},{ 0,112},{ 6, 1},
+ { 4, 1},{ 2, 1},{ 0, 7},{ 0, 85},{ 0,113},{ 2, 1},
+ { 0, 23},{ 2, 1},{ 0, 39},{ 0, 55},{ 72, 1},{ 24, 1},
+ { 12, 1},{ 4, 1},{ 2, 1},{ 0, 24},{ 0,130},{ 2, 1},
+ { 0, 40},{ 4, 1},{ 2, 1},{ 0,100},{ 0, 70},{ 0,114}, // 120
+
+ { 8, 1},{ 4, 1},{ 2, 1},{ 0,132},{ 0, 72},{ 2, 1},
+ { 0,144},{ 0, 9},{ 2, 1},{ 0,145},{ 0, 25},{ 24, 1},
+ { 14, 1},{ 8, 1},{ 4, 1},{ 2, 1},{ 0,115},{ 0,101},
+ { 2, 1},{ 0, 86},{ 0,116},{ 4, 1},{ 2, 1},{ 0, 71},
+ { 0,102},{ 0,131},{ 6, 1},{ 2, 1},{ 0, 56},{ 2, 1},
+ { 0,117},{ 0, 87},{ 2, 1},{ 0,146},{ 0, 41},{ 14, 1},
+ { 8, 1},{ 4, 1},{ 2, 1},{ 0,103},{ 0,133},{ 2, 1},
+ { 0, 88},{ 0, 57},{ 2, 1},{ 0,147},{ 2, 1},{ 0, 73},
+ { 0,134},{ 6, 1},{ 2, 1},{ 0,160},{ 2, 1},{ 0,104},
+ { 0, 10},{ 2, 1},{ 0,161},{ 0, 26},{ 68, 1},{ 24, 1}, // 180
+
+ { 12, 1},{ 4, 1},{ 2, 1},{ 0,162},{ 0, 42},{ 4, 1},
+ { 2, 1},{ 0,149},{ 0, 89},{ 2, 1},{ 0,163},{ 0, 58},
+ { 8, 1},{ 4, 1},{ 2, 1},{ 0, 74},{ 0,150},{ 2, 1},
+ { 0,176},{ 0, 11},{ 2, 1},{ 0,177},{ 0, 27},{ 20, 1},
+ { 8, 1},{ 2, 1},{ 0,178},{ 4, 1},{ 2, 1},{ 0,118},
+ { 0,119},{ 0,148},{ 6, 1},{ 4, 1},{ 2, 1},{ 0,135},
+ { 0,120},{ 0,164},{ 4, 1},{ 2, 1},{ 0,105},{ 0,165},
+ { 0, 43},{ 12, 1},{ 6, 1},{ 4, 1},{ 2, 1},{ 0, 90},
+ { 0,136},{ 0,179},{ 2, 1},{ 0, 59},{ 2, 1},{ 0,121},
+ { 0,166},{ 6, 1},{ 4, 1},{ 2, 1},{ 0,106},{ 0,180}, // 240
+
+ { 0,192},{ 4, 1},{ 2, 1},{ 0, 12},{ 0,152},{ 0,193},
+ { 60, 1},{ 22, 1},{ 10, 1},{ 6, 1},{ 2, 1},{ 0, 28},
+ { 2, 1},{ 0,137},{ 0,181},{ 2, 1},{ 0, 91},{ 0,194},
+ { 4, 1},{ 2, 1},{ 0, 44},{ 0, 60},{ 4, 1},{ 2, 1},
+ { 0,182},{ 0,107},{ 2, 1},{ 0,196},{ 0, 76},{ 16, 1},
+ { 8, 1},{ 4, 1},{ 2, 1},{ 0,168},{ 0,138},{ 2, 1},
+ { 0,208},{ 0, 13},{ 2, 1},{ 0,209},{ 2, 1},{ 0, 75},
+ { 2, 1},{ 0,151},{ 0,167},{ 12, 1},{ 6, 1},{ 2, 1},
+ { 0,195},{ 2, 1},{ 0,122},{ 0,153},{ 4, 1},{ 2, 1},
+ { 0,197},{ 0, 92},{ 0,183},{ 4, 1},{ 2, 1},{ 0, 29}, // 300
+
+ { 0,210},{ 2, 1},{ 0, 45},{ 2, 1},{ 0,123},{ 0,211},
+ { 52, 1},{ 28, 1},{ 12, 1},{ 4, 1},{ 2, 1},{ 0, 61},
+ { 0,198},{ 4, 1},{ 2, 1},{ 0,108},{ 0,169},{ 2, 1},
+ { 0,154},{ 0,212},{ 8, 1},{ 4, 1},{ 2, 1},{ 0,184},
+ { 0,139},{ 2, 1},{ 0, 77},{ 0,199},{ 4, 1},{ 2, 1},
+ { 0,124},{ 0,213},{ 2, 1},{ 0, 93},{ 0,224},{ 10, 1},
+ { 4, 1},{ 2, 1},{ 0,225},{ 0, 30},{ 4, 1},{ 2, 1},
+ { 0, 14},{ 0, 46},{ 0,226},{ 8, 1},{ 4, 1},{ 2, 1},
+ { 0,227},{ 0,109},{ 2, 1},{ 0,140},{ 0,228},{ 4, 1},
+ { 2, 1},{ 0,229},{ 0,186},{ 0,240},{ 38, 1},{ 16, 1}, // 360
+
+ { 4, 1},{ 2, 1},{ 0,241},{ 0, 31},{ 6, 1},{ 4, 1},
+ { 2, 1},{ 0,170},{ 0,155},{ 0,185},{ 2, 1},{ 0, 62},
+ { 2, 1},{ 0,214},{ 0,200},{ 12, 1},{ 6, 1},{ 2, 1},
+ { 0, 78},{ 2, 1},{ 0,215},{ 0,125},{ 2, 1},{ 0,171},
+ { 2, 1},{ 0, 94},{ 0,201},{ 6, 1},{ 2, 1},{ 0, 15},
+ { 2, 1},{ 0,156},{ 0,110},{ 2, 1},{ 0,242},{ 0, 47},
+ { 32, 1},{ 16, 1},{ 6, 1},{ 4, 1},{ 2, 1},{ 0,216},
+ { 0,141},{ 0, 63},{ 6, 1},{ 2, 1},{ 0,243},{ 2, 1},
+ { 0,230},{ 0,202},{ 2, 1},{ 0,244},{ 0, 79},{ 8, 1},
+ { 4, 1},{ 2, 1},{ 0,187},{ 0,172},{ 2, 1},{ 0,231}, // 420
+
+ { 0,245},{ 4, 1},{ 2, 1},{ 0,217},{ 0,157},{ 2, 1},
+ { 0, 95},{ 0,232},{ 30, 1},{ 12, 1},{ 6, 1},{ 2, 1},
+ { 0,111},{ 2, 1},{ 0,246},{ 0,203},{ 4, 1},{ 2, 1},
+ { 0,188},{ 0,173},{ 0,218},{ 8, 1},{ 2, 1},{ 0,247},
+ { 4, 1},{ 2, 1},{ 0,126},{ 0,127},{ 0,142},{ 6, 1},
+ { 4, 1},{ 2, 1},{ 0,158},{ 0,174},{ 0,204},{ 2, 1},
+ { 0,248},{ 0,143},{ 18, 1},{ 8, 1},{ 4, 1},{ 2, 1},
+ { 0,219},{ 0,189},{ 2, 1},{ 0,234},{ 0,249},{ 4, 1},
+ { 2, 1},{ 0,159},{ 0,235},{ 2, 1},{ 0,190},{ 2, 1},
+ { 0,205},{ 0,250},{ 14, 1},{ 4, 1},{ 2, 1},{ 0,221}, // 480
+
+ { 0,236},{ 6, 1},{ 4, 1},{ 2, 1},{ 0,233},{ 0,175},
+ { 0,220},{ 2, 1},{ 0,206},{ 0,251},{ 8, 1},{ 4, 1},
+ { 2, 1},{ 0,191},{ 0,222},{ 2, 1},{ 0,207},{ 0,238},
+ { 4, 1},{ 2, 1},{ 0,223},{ 0,239},{ 2, 1},{ 0,255},
+ { 2, 1},{ 0,237},{ 2, 1},{ 0,253},{ 2, 1},{ 0,252},
+ { 0,254}},
+
+htd15[511][2]={{ 16, 1},{ 6, 1},{ 2, 1},{ 0, 0},{ 2, 1},{ 0, 16},
+ { 0, 1},{ 2, 1},{ 0, 17},{ 4, 1},{ 2, 1},{ 0, 32},
+ { 0, 2},{ 2, 1},{ 0, 33},{ 0, 18},{ 50, 1},{ 16, 1},
+ { 6, 1},{ 2, 1},{ 0, 34},{ 2, 1},{ 0, 48},{ 0, 49},
+ { 6, 1},{ 2, 1},{ 0, 19},{ 2, 1},{ 0, 3},{ 0, 64},
+ { 2, 1},{ 0, 50},{ 0, 35},{ 14, 1},{ 6, 1},{ 4, 1},
+ { 2, 1},{ 0, 4},{ 0, 20},{ 0, 65},{ 4, 1},{ 2, 1},
+ { 0, 51},{ 0, 66},{ 2, 1},{ 0, 36},{ 0, 67},{ 10, 1},
+ { 6, 1},{ 2, 1},{ 0, 52},{ 2, 1},{ 0, 80},{ 0, 5},
+ { 2, 1},{ 0, 81},{ 0, 21},{ 4, 1},{ 2, 1},{ 0, 82}, // 60
+
+ { 0, 37},{ 4, 1},{ 2, 1},{ 0, 68},{ 0, 83},{ 0, 97},
+ { 90, 1},{ 36, 1},{ 18, 1},{ 10, 1},{ 6, 1},{ 2, 1},
+ { 0, 53},{ 2, 1},{ 0, 96},{ 0, 6},{ 2, 1},{ 0, 22},
+ { 0, 98},{ 4, 1},{ 2, 1},{ 0, 38},{ 0, 84},{ 2, 1},
+ { 0, 69},{ 0, 99},{ 10, 1},{ 6, 1},{ 2, 1},{ 0, 54},
+ { 2, 1},{ 0,112},{ 0, 7},{ 2, 1},{ 0,113},{ 0, 85},
+ { 4, 1},{ 2, 1},{ 0, 23},{ 0,100},{ 2, 1},{ 0,114},
+ { 0, 39},{ 24, 1},{ 16, 1},{ 8, 1},{ 4, 1},{ 2, 1},
+ { 0, 70},{ 0,115},{ 2, 1},{ 0, 55},{ 0,101},{ 4, 1},
+ { 2, 1},{ 0, 86},{ 0,128},{ 2, 1},{ 0, 8},{ 0,116}, // 120
+
+ { 4, 1},{ 2, 1},{ 0,129},{ 0, 24},{ 2, 1},{ 0,130},
+ { 0, 40},{ 16, 1},{ 8, 1},{ 4, 1},{ 2, 1},{ 0, 71},
+ { 0,102},{ 2, 1},{ 0,131},{ 0, 56},{ 4, 1},{ 2, 1},
+ { 0,117},{ 0, 87},{ 2, 1},{ 0,132},{ 0, 72},{ 6, 1},
+ { 4, 1},{ 2, 1},{ 0,144},{ 0, 25},{ 0,145},{ 4, 1},
+ { 2, 1},{ 0,146},{ 0,118},{ 2, 1},{ 0,103},{ 0, 41},
+ { 92, 1},{ 36, 1},{ 18, 1},{ 10, 1},{ 4, 1},{ 2, 1},
+ { 0,133},{ 0, 88},{ 4, 1},{ 2, 1},{ 0, 9},{ 0,119},
+ { 0,147},{ 4, 1},{ 2, 1},{ 0, 57},{ 0,148},{ 2, 1},
+ { 0, 73},{ 0,134},{ 10, 1},{ 6, 1},{ 2, 1},{ 0,104}, // 180
+
+ { 2, 1},{ 0,160},{ 0, 10},{ 2, 1},{ 0,161},{ 0, 26},
+ { 4, 1},{ 2, 1},{ 0,162},{ 0, 42},{ 2, 1},{ 0,149},
+ { 0, 89},{ 26, 1},{ 14, 1},{ 6, 1},{ 2, 1},{ 0,163},
+ { 2, 1},{ 0, 58},{ 0,135},{ 4, 1},{ 2, 1},{ 0,120},
+ { 0,164},{ 2, 1},{ 0, 74},{ 0,150},{ 6, 1},{ 4, 1},
+ { 2, 1},{ 0,105},{ 0,176},{ 0,177},{ 4, 1},{ 2, 1},
+ { 0, 27},{ 0,165},{ 0,178},{ 14, 1},{ 8, 1},{ 4, 1},
+ { 2, 1},{ 0, 90},{ 0, 43},{ 2, 1},{ 0,136},{ 0,151},
+ { 2, 1},{ 0,179},{ 2, 1},{ 0,121},{ 0, 59},{ 8, 1},
+ { 4, 1},{ 2, 1},{ 0,106},{ 0,180},{ 2, 1},{ 0, 75}, // 240
+
+ { 0,193},{ 4, 1},{ 2, 1},{ 0,152},{ 0,137},{ 2, 1},
+ { 0, 28},{ 0,181},{ 80, 1},{ 34, 1},{ 16, 1},{ 6, 1},
+ { 4, 1},{ 2, 1},{ 0, 91},{ 0, 44},{ 0,194},{ 6, 1},
+ { 4, 1},{ 2, 1},{ 0, 11},{ 0,192},{ 0,166},{ 2, 1},
+ { 0,167},{ 0,122},{ 10, 1},{ 4, 1},{ 2, 1},{ 0,195},
+ { 0, 60},{ 4, 1},{ 2, 1},{ 0, 12},{ 0,153},{ 0,182},
+ { 4, 1},{ 2, 1},{ 0,107},{ 0,196},{ 2, 1},{ 0, 76},
+ { 0,168},{ 20, 1},{ 10, 1},{ 4, 1},{ 2, 1},{ 0,138},
+ { 0,197},{ 4, 1},{ 2, 1},{ 0,208},{ 0, 92},{ 0,209},
+ { 4, 1},{ 2, 1},{ 0,183},{ 0,123},{ 2, 1},{ 0, 29}, // 300
+
+ { 2, 1},{ 0, 13},{ 0, 45},{ 12, 1},{ 4, 1},{ 2, 1},
+ { 0,210},{ 0,211},{ 4, 1},{ 2, 1},{ 0, 61},{ 0,198},
+ { 2, 1},{ 0,108},{ 0,169},{ 6, 1},{ 4, 1},{ 2, 1},
+ { 0,154},{ 0,184},{ 0,212},{ 4, 1},{ 2, 1},{ 0,139},
+ { 0, 77},{ 2, 1},{ 0,199},{ 0,124},{ 68, 1},{ 34, 1},
+ { 18, 1},{ 10, 1},{ 4, 1},{ 2, 1},{ 0,213},{ 0, 93},
+ { 4, 1},{ 2, 1},{ 0,224},{ 0, 14},{ 0,225},{ 4, 1},
+ { 2, 1},{ 0, 30},{ 0,226},{ 2, 1},{ 0,170},{ 0, 46},
+ { 8, 1},{ 4, 1},{ 2, 1},{ 0,185},{ 0,155},{ 2, 1},
+ { 0,227},{ 0,214},{ 4, 1},{ 2, 1},{ 0,109},{ 0, 62}, // 360
+
+ { 2, 1},{ 0,200},{ 0,140},{ 16, 1},{ 8, 1},{ 4, 1},
+ { 2, 1},{ 0,228},{ 0, 78},{ 2, 1},{ 0,215},{ 0,125},
+ { 4, 1},{ 2, 1},{ 0,229},{ 0,186},{ 2, 1},{ 0,171},
+ { 0, 94},{ 8, 1},{ 4, 1},{ 2, 1},{ 0,201},{ 0,156},
+ { 2, 1},{ 0,241},{ 0, 31},{ 6, 1},{ 4, 1},{ 2, 1},
+ { 0,240},{ 0,110},{ 0,242},{ 2, 1},{ 0, 47},{ 0,230},
+ { 38, 1},{ 18, 1},{ 8, 1},{ 4, 1},{ 2, 1},{ 0,216},
+ { 0,243},{ 2, 1},{ 0, 63},{ 0,244},{ 6, 1},{ 2, 1},
+ { 0, 79},{ 2, 1},{ 0,141},{ 0,217},{ 2, 1},{ 0,187},
+ { 0,202},{ 8, 1},{ 4, 1},{ 2, 1},{ 0,172},{ 0,231}, // 420
+
+ { 2, 1},{ 0,126},{ 0,245},{ 8, 1},{ 4, 1},{ 2, 1},
+ { 0,157},{ 0, 95},{ 2, 1},{ 0,232},{ 0,142},{ 2, 1},
+ { 0,246},{ 0,203},{ 34, 1},{ 18, 1},{ 10, 1},{ 6, 1},
+ { 4, 1},{ 2, 1},{ 0, 15},{ 0,174},{ 0,111},{ 2, 1},
+ { 0,188},{ 0,218},{ 4, 1},{ 2, 1},{ 0,173},{ 0,247},
+ { 2, 1},{ 0,127},{ 0,233},{ 8, 1},{ 4, 1},{ 2, 1},
+ { 0,158},{ 0,204},{ 2, 1},{ 0,248},{ 0,143},{ 4, 1},
+ { 2, 1},{ 0,219},{ 0,189},{ 2, 1},{ 0,234},{ 0,249},
+ { 16, 1},{ 8, 1},{ 4, 1},{ 2, 1},{ 0,159},{ 0,220},
+ { 2, 1},{ 0,205},{ 0,235},{ 4, 1},{ 2, 1},{ 0,190}, // 480
+
+ { 0,250},{ 2, 1},{ 0,175},{ 0,221},{ 14, 1},{ 6, 1},
+ { 4, 1},{ 2, 1},{ 0,236},{ 0,206},{ 0,251},{ 4, 1},
+ { 2, 1},{ 0,191},{ 0,237},{ 2, 1},{ 0,222},{ 0,252},
+ { 6, 1},{ 4, 1},{ 2, 1},{ 0,207},{ 0,253},{ 0,238},
+ { 4, 1},{ 2, 1},{ 0,223},{ 0,254},{ 2, 1},{ 0,239},
+ { 0,255}},
+
+htd16[511][2]={{ 2, 1},{ 0, 0},{ 6, 1},{ 2, 1},{ 0, 16},{ 2, 1},
+ { 0, 1},{ 0, 17},{ 42, 1},{ 8, 1},{ 4, 1},{ 2, 1},
+ { 0, 32},{ 0, 2},{ 2, 1},{ 0, 33},{ 0, 18},{ 10, 1},
+ { 6, 1},{ 2, 1},{ 0, 34},{ 2, 1},{ 0, 48},{ 0, 3},
+ { 2, 1},{ 0, 49},{ 0, 19},{ 10, 1},{ 4, 1},{ 2, 1},
+ { 0, 50},{ 0, 35},{ 4, 1},{ 2, 1},{ 0, 64},{ 0, 4},
+ { 0, 65},{ 6, 1},{ 2, 1},{ 0, 20},{ 2, 1},{ 0, 51},
+ { 0, 66},{ 4, 1},{ 2, 1},{ 0, 36},{ 0, 80},{ 2, 1},
+ { 0, 67},{ 0, 52},{138, 1},{ 40, 1},{ 16, 1},{ 6, 1},
+ { 4, 1},{ 2, 1},{ 0, 5},{ 0, 21},{ 0, 81},{ 4, 1}, // 60
+
+ { 2, 1},{ 0, 82},{ 0, 37},{ 4, 1},{ 2, 1},{ 0, 68},
+ { 0, 53},{ 0, 83},{ 10, 1},{ 6, 1},{ 4, 1},{ 2, 1},
+ { 0, 96},{ 0, 6},{ 0, 97},{ 2, 1},{ 0, 22},{ 0, 98},
+ { 8, 1},{ 4, 1},{ 2, 1},{ 0, 38},{ 0, 84},{ 2, 1},
+ { 0, 69},{ 0, 99},{ 4, 1},{ 2, 1},{ 0, 54},{ 0,112},
+ { 0,113},{ 40, 1},{ 18, 1},{ 8, 1},{ 2, 1},{ 0, 23},
+ { 2, 1},{ 0, 7},{ 2, 1},{ 0, 85},{ 0,100},{ 4, 1},
+ { 2, 1},{ 0,114},{ 0, 39},{ 4, 1},{ 2, 1},{ 0, 70},
+ { 0,101},{ 0,115},{ 10, 1},{ 6, 1},{ 2, 1},{ 0, 55},
+ { 2, 1},{ 0, 86},{ 0, 8},{ 2, 1},{ 0,128},{ 0,129}, // 120
+
+ { 6, 1},{ 2, 1},{ 0, 24},{ 2, 1},{ 0,116},{ 0, 71},
+ { 2, 1},{ 0,130},{ 2, 1},{ 0, 40},{ 0,102},{ 24, 1},
+ { 14, 1},{ 8, 1},{ 4, 1},{ 2, 1},{ 0,131},{ 0, 56},
+ { 2, 1},{ 0,117},{ 0,132},{ 4, 1},{ 2, 1},{ 0, 72},
+ { 0,144},{ 0,145},{ 6, 1},{ 2, 1},{ 0, 25},{ 2, 1},
+ { 0, 9},{ 0,118},{ 2, 1},{ 0,146},{ 0, 41},{ 14, 1},
+ { 8, 1},{ 4, 1},{ 2, 1},{ 0,133},{ 0, 88},{ 2, 1},
+ { 0,147},{ 0, 57},{ 4, 1},{ 2, 1},{ 0,160},{ 0, 10},
+ { 0, 26},{ 8, 1},{ 2, 1},{ 0,162},{ 2, 1},{ 0,103},
+ { 2, 1},{ 0, 87},{ 0, 73},{ 6, 1},{ 2, 1},{ 0,148}, // 180
+
+ { 2, 1},{ 0,119},{ 0,134},{ 2, 1},{ 0,161},{ 2, 1},
+ { 0,104},{ 0,149},{220, 1},{126, 1},{ 50, 1},{ 26, 1},
+ { 12, 1},{ 6, 1},{ 2, 1},{ 0, 42},{ 2, 1},{ 0, 89},
+ { 0, 58},{ 2, 1},{ 0,163},{ 2, 1},{ 0,135},{ 0,120},
+ { 8, 1},{ 4, 1},{ 2, 1},{ 0,164},{ 0, 74},{ 2, 1},
+ { 0,150},{ 0,105},{ 4, 1},{ 2, 1},{ 0,176},{ 0, 11},
+ { 0,177},{ 10, 1},{ 4, 1},{ 2, 1},{ 0, 27},{ 0,178},
+ { 2, 1},{ 0, 43},{ 2, 1},{ 0,165},{ 0, 90},{ 6, 1},
+ { 2, 1},{ 0,179},{ 2, 1},{ 0,166},{ 0,106},{ 4, 1},
+ { 2, 1},{ 0,180},{ 0, 75},{ 2, 1},{ 0, 12},{ 0,193}, // 240
+
+ { 30, 1},{ 14, 1},{ 6, 1},{ 4, 1},{ 2, 1},{ 0,181},
+ { 0,194},{ 0, 44},{ 4, 1},{ 2, 1},{ 0,167},{ 0,195},
+ { 2, 1},{ 0,107},{ 0,196},{ 8, 1},{ 2, 1},{ 0, 29},
+ { 4, 1},{ 2, 1},{ 0,136},{ 0,151},{ 0, 59},{ 4, 1},
+ { 2, 1},{ 0,209},{ 0,210},{ 2, 1},{ 0, 45},{ 0,211},
+ { 18, 1},{ 6, 1},{ 4, 1},{ 2, 1},{ 0, 30},{ 0, 46},
+ { 0,226},{ 6, 1},{ 4, 1},{ 2, 1},{ 0,121},{ 0,152},
+ { 0,192},{ 2, 1},{ 0, 28},{ 2, 1},{ 0,137},{ 0, 91},
+ { 14, 1},{ 6, 1},{ 2, 1},{ 0, 60},{ 2, 1},{ 0,122},
+ { 0,182},{ 4, 1},{ 2, 1},{ 0, 76},{ 0,153},{ 2, 1}, // 300
+
+ { 0,168},{ 0,138},{ 6, 1},{ 2, 1},{ 0, 13},{ 2, 1},
+ { 0,197},{ 0, 92},{ 4, 1},{ 2, 1},{ 0, 61},{ 0,198},
+ { 2, 1},{ 0,108},{ 0,154},{ 88, 1},{ 86, 1},{ 36, 1},
+ { 16, 1},{ 8, 1},{ 4, 1},{ 2, 1},{ 0,139},{ 0, 77},
+ { 2, 1},{ 0,199},{ 0,124},{ 4, 1},{ 2, 1},{ 0,213},
+ { 0, 93},{ 2, 1},{ 0,224},{ 0, 14},{ 8, 1},{ 2, 1},
+ { 0,227},{ 4, 1},{ 2, 1},{ 0,208},{ 0,183},{ 0,123},
+ { 6, 1},{ 4, 1},{ 2, 1},{ 0,169},{ 0,184},{ 0,212},
+ { 2, 1},{ 0,225},{ 2, 1},{ 0,170},{ 0,185},{ 24, 1},
+ { 10, 1},{ 6, 1},{ 4, 1},{ 2, 1},{ 0,155},{ 0,214}, // 360
+
+ { 0,109},{ 2, 1},{ 0, 62},{ 0,200},{ 6, 1},{ 4, 1},
+ { 2, 1},{ 0,140},{ 0,228},{ 0, 78},{ 4, 1},{ 2, 1},
+ { 0,215},{ 0,229},{ 2, 1},{ 0,186},{ 0,171},{ 12, 1},
+ { 4, 1},{ 2, 1},{ 0,156},{ 0,230},{ 4, 1},{ 2, 1},
+ { 0,110},{ 0,216},{ 2, 1},{ 0,141},{ 0,187},{ 8, 1},
+ { 4, 1},{ 2, 1},{ 0,231},{ 0,157},{ 2, 1},{ 0,232},
+ { 0,142},{ 4, 1},{ 2, 1},{ 0,203},{ 0,188},{ 0,158},
+ { 0,241},{ 2, 1},{ 0, 31},{ 2, 1},{ 0, 15},{ 0, 47},
+ { 66, 1},{ 56, 1},{ 2, 1},{ 0,242},{ 52, 1},{ 50, 1},
+ { 20, 1},{ 8, 1},{ 2, 1},{ 0,189},{ 2, 1},{ 0, 94}, // 420
+
+ { 2, 1},{ 0,125},{ 0,201},{ 6, 1},{ 2, 1},{ 0,202},
+ { 2, 1},{ 0,172},{ 0,126},{ 4, 1},{ 2, 1},{ 0,218},
+ { 0,173},{ 0,204},{ 10, 1},{ 6, 1},{ 2, 1},{ 0,174},
+ { 2, 1},{ 0,219},{ 0,220},{ 2, 1},{ 0,205},{ 0,190},
+ { 6, 1},{ 4, 1},{ 2, 1},{ 0,235},{ 0,237},{ 0,238},
+ { 6, 1},{ 4, 1},{ 2, 1},{ 0,217},{ 0,234},{ 0,233},
+ { 2, 1},{ 0,222},{ 4, 1},{ 2, 1},{ 0,221},{ 0,236},
+ { 0,206},{ 0, 63},{ 0,240},{ 4, 1},{ 2, 1},{ 0,243},
+ { 0,244},{ 2, 1},{ 0, 79},{ 2, 1},{ 0,245},{ 0, 95},
+ { 10, 1},{ 2, 1},{ 0,255},{ 4, 1},{ 2, 1},{ 0,246}, // 480
+
+ { 0,111},{ 2, 1},{ 0,247},{ 0,127},{ 12, 1},{ 6, 1},
+ { 2, 1},{ 0,143},{ 2, 1},{ 0,248},{ 0,249},{ 4, 1},
+ { 2, 1},{ 0,159},{ 0,250},{ 0,175},{ 8, 1},{ 4, 1},
+ { 2, 1},{ 0,251},{ 0,191},{ 2, 1},{ 0,252},{ 0,207},
+ { 4, 1},{ 2, 1},{ 0,253},{ 0,223},{ 2, 1},{ 0,254},
+ { 0,239}},
+
+htd24[512][2]={{ 60, 1},{ 8, 1},{ 4, 1},{ 2, 1},{ 0, 0},{ 0, 16},
+ { 2, 1},{ 0, 1},{ 0, 17},{ 14, 1},{ 6, 1},{ 4, 1},
+ { 2, 1},{ 0, 32},{ 0, 2},{ 0, 33},{ 2, 1},{ 0, 18},
+ { 2, 1},{ 0, 34},{ 2, 1},{ 0, 48},{ 0, 3},{ 14, 1},
+ { 4, 1},{ 2, 1},{ 0, 49},{ 0, 19},{ 4, 1},{ 2, 1},
+ { 0, 50},{ 0, 35},{ 4, 1},{ 2, 1},{ 0, 64},{ 0, 4},
+ { 0, 65},{ 8, 1},{ 4, 1},{ 2, 1},{ 0, 20},{ 0, 51},
+ { 2, 1},{ 0, 66},{ 0, 36},{ 6, 1},{ 4, 1},{ 2, 1},
+ { 0, 67},{ 0, 52},{ 0, 81},{ 6, 1},{ 4, 1},{ 2, 1},
+ { 0, 80},{ 0, 5},{ 0, 21},{ 2, 1},{ 0, 82},{ 0, 37}, // 60
+
+ {250+85, 1},{ 98, 1},{ 34, 1},{ 18, 1},{ 10, 1},{ 4, 1},
+ { 2, 1},{ 0, 68},{ 0, 83},{ 2, 1},{ 0, 53},{ 2, 1},
+ { 0, 96},{ 0, 6},{ 4, 1},{ 2, 1},{ 0, 97},{ 0, 22},
+ { 2, 1},{ 0, 98},{ 0, 38},{ 8, 1},{ 4, 1},{ 2, 1},
+ { 0, 84},{ 0, 69},{ 2, 1},{ 0, 99},{ 0, 54},{ 4, 1},
+ { 2, 1},{ 0,113},{ 0, 85},{ 2, 1},{ 0,100},{ 0, 70},
+ { 32, 1},{ 14, 1},{ 6, 1},{ 2, 1},{ 0,114},{ 2, 1},
+ { 0, 39},{ 0, 55},{ 2, 1},{ 0,115},{ 4, 1},{ 2, 1},
+ { 0,112},{ 0, 7},{ 0, 23},{ 10, 1},{ 4, 1},{ 2, 1},
+ { 0,101},{ 0, 86},{ 4, 1},{ 2, 1},{ 0,128},{ 0, 8}, // 120
+
+ { 0,129},{ 4, 1},{ 2, 1},{ 0,116},{ 0, 71},{ 2, 1},
+ { 0, 24},{ 0,130},{ 16, 1},{ 8, 1},{ 4, 1},{ 2, 1},
+ { 0, 40},{ 0,102},{ 2, 1},{ 0,131},{ 0, 56},{ 4, 1},
+ { 2, 1},{ 0,117},{ 0, 87},{ 2, 1},{ 0,132},{ 0, 72},
+ { 8, 1},{ 4, 1},{ 2, 1},{ 0,145},{ 0, 25},{ 2, 1},
+ { 0,146},{ 0,118},{ 4, 1},{ 2, 1},{ 0,103},{ 0, 41},
+ { 2, 1},{ 0,133},{ 0, 88},{ 92, 1},{ 34, 1},{ 16, 1},
+ { 8, 1},{ 4, 1},{ 2, 1},{ 0,147},{ 0, 57},{ 2, 1},
+ { 0,148},{ 0, 73},{ 4, 1},{ 2, 1},{ 0,119},{ 0,134},
+ { 2, 1},{ 0,104},{ 0,161},{ 8, 1},{ 4, 1},{ 2, 1}, // 180
+
+ { 0,162},{ 0, 42},{ 2, 1},{ 0,149},{ 0, 89},{ 4, 1},
+ { 2, 1},{ 0,163},{ 0, 58},{ 2, 1},{ 0,135},{ 2, 1},
+ { 0,120},{ 0, 74},{ 22, 1},{ 12, 1},{ 4, 1},{ 2, 1},
+ { 0,164},{ 0,150},{ 4, 1},{ 2, 1},{ 0,105},{ 0,177},
+ { 2, 1},{ 0, 27},{ 0,165},{ 6, 1},{ 2, 1},{ 0,178},
+ { 2, 1},{ 0, 90},{ 0, 43},{ 2, 1},{ 0,136},{ 0,179},
+ { 16, 1},{ 10, 1},{ 6, 1},{ 2, 1},{ 0,144},{ 2, 1},
+ { 0, 9},{ 0,160},{ 2, 1},{ 0,151},{ 0,121},{ 4, 1},
+ { 2, 1},{ 0,166},{ 0,106},{ 0,180},{ 12, 1},{ 6, 1},
+ { 2, 1},{ 0, 26},{ 2, 1},{ 0, 10},{ 0,176},{ 2, 1}, // 240
+
+ { 0, 59},{ 2, 1},{ 0, 11},{ 0,192},{ 4, 1},{ 2, 1},
+ { 0, 75},{ 0,193},{ 2, 1},{ 0,152},{ 0,137},{ 67, 1},
+ { 34, 1},{ 16, 1},{ 8, 1},{ 4, 1},{ 2, 1},{ 0, 28},
+ { 0,181},{ 2, 1},{ 0, 91},{ 0,194},{ 4, 1},{ 2, 1},
+ { 0, 44},{ 0,167},{ 2, 1},{ 0,122},{ 0,195},{ 10, 1},
+ { 6, 1},{ 2, 1},{ 0, 60},{ 2, 1},{ 0, 12},{ 0,208},
+ { 2, 1},{ 0,182},{ 0,107},{ 4, 1},{ 2, 1},{ 0,196},
+ { 0, 76},{ 2, 1},{ 0,153},{ 0,168},{ 16, 1},{ 8, 1},
+ { 4, 1},{ 2, 1},{ 0,138},{ 0,197},{ 2, 1},{ 0, 92},
+ { 0,209},{ 4, 1},{ 2, 1},{ 0,183},{ 0,123},{ 2, 1}, // 300
+
+ { 0, 29},{ 0,210},{ 9, 1},{ 4, 1},{ 2, 1},{ 0, 45},
+ { 0,211},{ 2, 1},{ 0, 61},{ 0,198},{ 85,250},{ 4, 1}, // 306 -
+ { 2, 1},{ 0,108},{ 0,169},{ 2, 1},{ 0,154},{ 0,212},
+ { 32, 1},{ 16, 1},{ 8, 1},{ 4, 1},{ 2, 1},{ 0,184},
+ { 0,139},{ 2, 1},{ 0, 77},{ 0,199},{ 4, 1},{ 2, 1},
+ { 0,124},{ 0,213},{ 2, 1},{ 0, 93},{ 0,225},{ 8, 1},
+ { 4, 1},{ 2, 1},{ 0, 30},{ 0,226},{ 2, 1},{ 0,170},
+ { 0,185},{ 4, 1},{ 2, 1},{ 0,155},{ 0,227},{ 2, 1},
+ { 0,214},{ 0,109},{ 20, 1},{ 10, 1},{ 6, 1},{ 2, 1},
+ { 0, 62},{ 2, 1},{ 0, 46},{ 0, 78},{ 2, 1},{ 0,200}, // 360
+
+ { 0,140},{ 4, 1},{ 2, 1},{ 0,228},{ 0,215},{ 4, 1},
+ { 2, 1},{ 0,125},{ 0,171},{ 0,229},{ 10, 1},{ 4, 1},
+ { 2, 1},{ 0,186},{ 0, 94},{ 2, 1},{ 0,201},{ 2, 1},
+ { 0,156},{ 0,110},{ 8, 1},{ 2, 1},{ 0,230},{ 2, 1},
+ { 0, 13},{ 2, 1},{ 0,224},{ 0, 14},{ 4, 1},{ 2, 1},
+ { 0,216},{ 0,141},{ 2, 1},{ 0,187},{ 0,202},{ 74, 1},
+ { 2, 1},{ 0,255},{ 64, 1},{ 58, 1},{ 32, 1},{ 16, 1},
+ { 8, 1},{ 4, 1},{ 2, 1},{ 0,172},{ 0,231},{ 2, 1},
+ { 0,126},{ 0,217},{ 4, 1},{ 2, 1},{ 0,157},{ 0,232},
+ { 2, 1},{ 0,142},{ 0,203},{ 8, 1},{ 4, 1},{ 2, 1}, // 420
+
+ { 0,188},{ 0,218},{ 2, 1},{ 0,173},{ 0,233},{ 4, 1},
+ { 2, 1},{ 0,158},{ 0,204},{ 2, 1},{ 0,219},{ 0,189},
+ { 16, 1},{ 8, 1},{ 4, 1},{ 2, 1},{ 0,234},{ 0,174},
+ { 2, 1},{ 0,220},{ 0,205},{ 4, 1},{ 2, 1},{ 0,235},
+ { 0,190},{ 2, 1},{ 0,221},{ 0,236},{ 8, 1},{ 4, 1},
+ { 2, 1},{ 0,206},{ 0,237},{ 2, 1},{ 0,222},{ 0,238},
+ { 0, 15},{ 4, 1},{ 2, 1},{ 0,240},{ 0, 31},{ 0,241},
+ { 4, 1},{ 2, 1},{ 0,242},{ 0, 47},{ 2, 1},{ 0,243},
+ { 0, 63},{ 18, 1},{ 8, 1},{ 4, 1},{ 2, 1},{ 0,244},
+ { 0, 79},{ 2, 1},{ 0,245},{ 0, 95},{ 4, 1},{ 2, 1}, // 480
+
+ { 0,246},{ 0,111},{ 2, 1},{ 0,247},{ 2, 1},{ 0,127},
+ { 0,143},{ 10, 1},{ 4, 1},{ 2, 1},{ 0,248},{ 0,249},
+ { 4, 1},{ 2, 1},{ 0,159},{ 0,175},{ 0,250},{ 8, 1},
+ { 4, 1},{ 2, 1},{ 0,251},{ 0,191},{ 2, 1},{ 0,252},
+ { 0,207},{ 4, 1},{ 2, 1},{ 0,253},{ 0,223},{ 2, 1},
+ { 0,254},{ 0,239}},
+
+htd32[ 31][2]={{ 2, 1},{ 0, 0},{ 8, 1},{ 4, 1},{ 2, 1},{ 0, 8},
+ { 0, 4},{ 2, 1},{ 0, 1},{ 0, 2},{ 8, 1},{ 4, 1},
+ { 2, 1},{ 0, 12},{ 0, 10},{ 2, 1},{ 0, 3},{ 0, 6},
+ { 6, 1},{ 2, 1},{ 0, 9},{ 2, 1},{ 0, 5},{ 0, 7},
+ { 4, 1},{ 2, 1},{ 0, 14},{ 0, 13},{ 2, 1},{ 0, 15},
+ { 0, 11}},
+
+htd33[ 31][2]={{ 16, 1},{ 8, 1},{ 4, 1},{ 2, 1},{ 0, 0},{ 0, 1},
+ { 2, 1},{ 0, 2},{ 0, 3},{ 4, 1},{ 2, 1},{ 0, 4},
+ { 0, 5},{ 2, 1},{ 0, 6},{ 0, 7},{ 8, 1},{ 4, 1},
+ { 2, 1},{ 0, 8},{ 0, 9},{ 2, 1},{ 0, 10},{ 0, 11},
+ { 4, 1},{ 2, 1},{ 0, 12},{ 0, 13},{ 2, 1},{ 0, 14},
+ { 0, 15}};
+
+const ATTR_ALIGN(64) HUFFMANCODETABLE Mpegtoraw::ht[HTN]=
+{
+ { 0, 0-1, 0-1, 0, 0, htd33},
+ { 1, 2-1, 2-1, 0, 7,htd01},
+ { 2, 3-1, 3-1, 0, 17,htd02},
+ { 3, 3-1, 3-1, 0, 17,htd03},
+ { 4, 0-1, 0-1, 0, 0, htd33},
+ { 5, 4-1, 4-1, 0, 31,htd05},
+ { 6, 4-1, 4-1, 0, 31,htd06},
+ { 7, 6-1, 6-1, 0, 71,htd07},
+ { 8, 6-1, 6-1, 0, 71,htd08},
+ { 9, 6-1, 6-1, 0, 71,htd09},
+ {10, 8-1, 8-1, 0,127,htd10},
+ {11, 8-1, 8-1, 0,127,htd11},
+ {12, 8-1, 8-1, 0,127,htd12},
+ {13,16-1,16-1, 0,511,htd13},
+ {14, 0-1, 0-1, 0, 0, htd33},
+ {15,16-1,16-1, 0,511,htd15},
+ {16,16-1,16-1, 1,511,htd16},
+ {17,16-1,16-1, 2,511,htd16},
+ {18,16-1,16-1, 3,511,htd16},
+ {19,16-1,16-1, 4,511,htd16},
+ {20,16-1,16-1, 6,511,htd16},
+ {21,16-1,16-1, 8,511,htd16},
+ {22,16-1,16-1,10,511,htd16},
+ {23,16-1,16-1,13,511,htd16},
+ {24,16-1,16-1, 4,512,htd24},
+ {25,16-1,16-1, 5,512,htd24},
+ {26,16-1,16-1, 6,512,htd24},
+ {27,16-1,16-1, 7,512,htd24},
+ {28,16-1,16-1, 8,512,htd24},
+ {29,16-1,16-1, 9,512,htd24},
+ {30,16-1,16-1,11,512,htd24},
+ {31,16-1,16-1,13,512,htd24},
+ {32, 1-1,16-1, 0, 31,htd32},
+ {33, 1-1,16-1, 0, 31,htd33}
+};
diff --git a/mpeglib/lib/splay/mpeg2tables.h b/mpeglib/lib/splay/mpeg2tables.h
new file mode 100644
index 00000000..1430a123
--- /dev/null
+++ b/mpeglib/lib/splay/mpeg2tables.h
@@ -0,0 +1,432 @@
+
+#ifndef __MPEG2TABLES_H
+#define __MPEG2TABLES_H
+
+
+#define MAXTABLE 3
+
+
+// Tables for layer 2.
+
+// bitalloclengthtable :0,1 supported 2. 2 ot tested & disabled
+// this table merges the subbands to the longer one.
+// 8 < 12 , 27 < 30 but the "length" is the same
+
+static const int bitalloclengthtable[MAXTABLE][MAXSUBBAND]=
+{ {4,4,3,3,3,3,3,3,3,3,3,3,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ {4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3, 3,3,3,3,3,3,3,2,2,2,2,2,2,2,0,0},
+ {4,4,4,4,3,3,3,3,3,3,3,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0} };
+
+
+/*
+Orignal is:
+{ {4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3, 3,3,3,3,3,3,3,2,2,2,2,0,0,0,0,0},
+ {4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3, 3,3,3,3,3,3,3,2,2,2,2,2,2,2,0,0},
+ {4,4,3,3,3,3,3,3,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ {4,4,3,3,3,3,3,3,3,3,3,3,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ {4,4,4,4,3,3,3,3,3,3,3,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0} };
+
+*/
+
+
+
+
+
+static const REAL group5bits[27*3]=
+{
+ -2.0/3.0, -2.0/3.0, -2.0/3.0,
+ 0.0, -2.0/3.0, -2.0/3.0,
+ 2.0/3.0, -2.0/3.0, -2.0/3.0,
+ -2.0/3.0, 0.0, -2.0/3.0,
+ 0.0, 0.0, -2.0/3.0,
+ 2.0/3.0, 0.0, -2.0/3.0,
+ -2.0/3.0, 2.0/3.0, -2.0/3.0,
+ 0.0, 2.0/3.0, -2.0/3.0,
+ 2.0/3.0, 2.0/3.0, -2.0/3.0,
+ -2.0/3.0, -2.0/3.0, 0.0,
+ 0.0, -2.0/3.0, 0.0,
+ 2.0/3.0, -2.0/3.0, 0.0,
+ -2.0/3.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0,
+ 2.0/3.0, 0.0, 0.0,
+ -2.0/3.0, 2.0/3.0, 0.0,
+ 0.0, 2.0/3.0, 0.0,
+ 2.0/3.0, 2.0/3.0, 0.0,
+ -2.0/3.0, -2.0/3.0, 2.0/3.0,
+ 0.0, -2.0/3.0, 2.0/3.0,
+ 2.0/3.0, -2.0/3.0, 2.0/3.0,
+ -2.0/3.0, 0.0, 2.0/3.0,
+ 0.0, 0.0, 2.0/3.0,
+ 2.0/3.0, 0.0, 2.0/3.0,
+ -2.0/3.0, 2.0/3.0, 2.0/3.0,
+ 0.0, 2.0/3.0, 2.0/3.0,
+ 2.0/3.0, 2.0/3.0, 2.0/3.0
+};
+
+static const REAL group7bits[125*3]=
+{
+ -0.8,-0.8,-0.8, -0.4,-0.8,-0.8, 0.0,-0.8,-0.8, 0.4,-0.8,-0.8, 0.8,-0.8,-0.8,
+ -0.8,-0.4,-0.8, -0.4,-0.4,-0.8, 0.0,-0.4,-0.8, 0.4,-0.4,-0.8, 0.8,-0.4,-0.8,
+ -0.8, 0.0,-0.8, -0.4, 0.0,-0.8, 0.0, 0.0,-0.8, 0.4, 0.0,-0.8, 0.8, 0.0,-0.8,
+ -0.8, 0.4,-0.8, -0.4, 0.4,-0.8, 0.0, 0.4,-0.8, 0.4, 0.4,-0.8, 0.8, 0.4,-0.8,
+ -0.8, 0.8,-0.8, -0.4, 0.8,-0.8, 0.0, 0.8,-0.8, 0.4, 0.8,-0.8, 0.8, 0.8,-0.8,
+ -0.8,-0.8,-0.4, -0.4,-0.8,-0.4, 0.0,-0.8,-0.4, 0.4,-0.8,-0.4, 0.8,-0.8,-0.4,
+ -0.8,-0.4,-0.4, -0.4,-0.4,-0.4, 0.0,-0.4,-0.4, 0.4,-0.4,-0.4, 0.8,-0.4,-0.4,
+ -0.8, 0.0,-0.4, -0.4, 0.0,-0.4, 0.0, 0.0,-0.4, 0.4, 0.0,-0.4, 0.8, 0.0,-0.4,
+ -0.8, 0.4,-0.4, -0.4, 0.4,-0.4, 0.0, 0.4,-0.4, 0.4, 0.4,-0.4, 0.8, 0.4,-0.4,
+ -0.8, 0.8,-0.4, -0.4, 0.8,-0.4, 0.0, 0.8,-0.4, 0.4, 0.8,-0.4, 0.8, 0.8,-0.4,
+ -0.8,-0.8, 0.0, -0.4,-0.8, 0.0, 0.0,-0.8, 0.0, 0.4,-0.8, 0.0, 0.8,-0.8, 0.0,
+ -0.8,-0.4, 0.0, -0.4,-0.4, 0.0, 0.0,-0.4, 0.0, 0.4,-0.4, 0.0, 0.8,-0.4, 0.0,
+ -0.8, 0.0, 0.0, -0.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.4, 0.0, 0.0, 0.8, 0.0, 0.0,
+ -0.8, 0.4, 0.0, -0.4, 0.4, 0.0, 0.0, 0.4, 0.0, 0.4, 0.4, 0.0, 0.8, 0.4, 0.0,
+ -0.8, 0.8, 0.0, -0.4, 0.8, 0.0, 0.0, 0.8, 0.0, 0.4, 0.8, 0.0, 0.8, 0.8, 0.0,
+ -0.8,-0.8, 0.4, -0.4,-0.8, 0.4, 0.0,-0.8, 0.4, 0.4,-0.8, 0.4, 0.8,-0.8, 0.4,
+ -0.8,-0.4, 0.4, -0.4,-0.4, 0.4, 0.0,-0.4, 0.4, 0.4,-0.4, 0.4, 0.8,-0.4, 0.4,
+ -0.8, 0.0, 0.4, -0.4, 0.0, 0.4, 0.0, 0.0, 0.4, 0.4, 0.0, 0.4, 0.8, 0.0, 0.4,
+ -0.8, 0.4, 0.4, -0.4, 0.4, 0.4, 0.0, 0.4, 0.4, 0.4, 0.4, 0.4, 0.8, 0.4, 0.4,
+ -0.8, 0.8, 0.4, -0.4, 0.8, 0.4, 0.0, 0.8, 0.4, 0.4, 0.8, 0.4, 0.8, 0.8, 0.4,
+ -0.8,-0.8, 0.8, -0.4,-0.8, 0.8, 0.0,-0.8, 0.8, 0.4,-0.8, 0.8, 0.8,-0.8, 0.8,
+ -0.8,-0.4, 0.8, -0.4,-0.4, 0.8, 0.0,-0.4, 0.8, 0.4,-0.4, 0.8, 0.8,-0.4, 0.8,
+ -0.8, 0.0, 0.8, -0.4, 0.0, 0.8, 0.0, 0.0, 0.8, 0.4, 0.0, 0.8, 0.8, 0.0, 0.8,
+ -0.8, 0.4, 0.8, -0.4, 0.4, 0.8, 0.0, 0.4, 0.8, 0.4, 0.4, 0.8, 0.8, 0.4, 0.8,
+ -0.8, 0.8, 0.8, -0.4, 0.8, 0.8, 0.0, 0.8, 0.8, 0.4, 0.8, 0.8, 0.8, 0.8, 0.8
+};
+
+static const REAL group10bits[729*3]=
+{
+ -8.0/9.0,-8.0/9.0,-8.0/9.0, -6.0/9.0,-8.0/9.0,-8.0/9.0, -4.0/9.0,-8.0/9.0,-8.0/9.0,
+ -2.0/9.0,-8.0/9.0,-8.0/9.0, 0.0,-8.0/9.0,-8.0/9.0, 2.0/9.0,-8.0/9.0,-8.0/9.0,
+ 4.0/9.0,-8.0/9.0,-8.0/9.0, 6.0/9.0,-8.0/9.0,-8.0/9.0, 8.0/9.0,-8.0/9.0,-8.0/9.0,
+ -8.0/9.0,-6.0/9.0,-8.0/9.0, -6.0/9.0,-6.0/9.0,-8.0/9.0, -4.0/9.0,-6.0/9.0,-8.0/9.0,
+ -2.0/9.0,-6.0/9.0,-8.0/9.0, 0.0,-6.0/9.0,-8.0/9.0, 2.0/9.0,-6.0/9.0,-8.0/9.0,
+ 4.0/9.0,-6.0/9.0,-8.0/9.0, 6.0/9.0,-6.0/9.0,-8.0/9.0, 8.0/9.0,-6.0/9.0,-8.0/9.0,
+ -8.0/9.0,-4.0/9.0,-8.0/9.0, -6.0/9.0,-4.0/9.0,-8.0/9.0, -4.0/9.0,-4.0/9.0,-8.0/9.0,
+ -2.0/9.0,-4.0/9.0,-8.0/9.0, 0.0,-4.0/9.0,-8.0/9.0, 2.0/9.0,-4.0/9.0,-8.0/9.0,
+ 4.0/9.0,-4.0/9.0,-8.0/9.0, 6.0/9.0,-4.0/9.0,-8.0/9.0, 8.0/9.0,-4.0/9.0,-8.0/9.0,
+ -8.0/9.0,-2.0/9.0,-8.0/9.0, -6.0/9.0,-2.0/9.0,-8.0/9.0, -4.0/9.0,-2.0/9.0,-8.0/9.0,
+ -2.0/9.0,-2.0/9.0,-8.0/9.0, 0.0,-2.0/9.0,-8.0/9.0, 2.0/9.0,-2.0/9.0,-8.0/9.0,
+ 4.0/9.0,-2.0/9.0,-8.0/9.0, 6.0/9.0,-2.0/9.0,-8.0/9.0, 8.0/9.0,-2.0/9.0,-8.0/9.0,
+ -8.0/9.0, 0.0,-8.0/9.0, -6.0/9.0, 0.0,-8.0/9.0, -4.0/9.0, 0.0,-8.0/9.0,
+ -2.0/9.0, 0.0,-8.0/9.0, 0.0, 0.0,-8.0/9.0, 2.0/9.0, 0.0,-8.0/9.0,
+ 4.0/9.0, 0.0,-8.0/9.0, 6.0/9.0, 0.0,-8.0/9.0, 8.0/9.0, 0.0,-8.0/9.0,
+ -8.0/9.0, 2.0/9.0,-8.0/9.0, -6.0/9.0, 2.0/9.0,-8.0/9.0, -4.0/9.0, 2.0/9.0,-8.0/9.0,
+ -2.0/9.0, 2.0/9.0,-8.0/9.0, 0.0, 2.0/9.0,-8.0/9.0, 2.0/9.0, 2.0/9.0,-8.0/9.0,
+ 4.0/9.0, 2.0/9.0,-8.0/9.0, 6.0/9.0, 2.0/9.0,-8.0/9.0, 8.0/9.0, 2.0/9.0,-8.0/9.0,
+ -8.0/9.0, 4.0/9.0,-8.0/9.0, -6.0/9.0, 4.0/9.0,-8.0/9.0, -4.0/9.0, 4.0/9.0,-8.0/9.0,
+ -2.0/9.0, 4.0/9.0,-8.0/9.0, 0.0, 4.0/9.0,-8.0/9.0, 2.0/9.0, 4.0/9.0,-8.0/9.0,
+ 4.0/9.0, 4.0/9.0,-8.0/9.0, 6.0/9.0, 4.0/9.0,-8.0/9.0, 8.0/9.0, 4.0/9.0,-8.0/9.0,
+ -8.0/9.0, 6.0/9.0,-8.0/9.0, -6.0/9.0, 6.0/9.0,-8.0/9.0, -4.0/9.0, 6.0/9.0,-8.0/9.0,
+ -2.0/9.0, 6.0/9.0,-8.0/9.0, 0.0, 6.0/9.0,-8.0/9.0, 2.0/9.0, 6.0/9.0,-8.0/9.0,
+ 4.0/9.0, 6.0/9.0,-8.0/9.0, 6.0/9.0, 6.0/9.0,-8.0/9.0, 8.0/9.0, 6.0/9.0,-8.0/9.0,
+ -8.0/9.0, 8.0/9.0,-8.0/9.0, -6.0/9.0, 8.0/9.0,-8.0/9.0, -4.0/9.0, 8.0/9.0,-8.0/9.0,
+ -2.0/9.0, 8.0/9.0,-8.0/9.0, 0.0, 8.0/9.0,-8.0/9.0, 2.0/9.0, 8.0/9.0,-8.0/9.0,
+ 4.0/9.0, 8.0/9.0,-8.0/9.0, 6.0/9.0, 8.0/9.0,-8.0/9.0, 8.0/9.0, 8.0/9.0,-8.0/9.0,
+ -8.0/9.0,-8.0/9.0,-6.0/9.0, -6.0/9.0,-8.0/9.0,-6.0/9.0, -4.0/9.0,-8.0/9.0,-6.0/9.0,
+ -2.0/9.0,-8.0/9.0,-6.0/9.0, 0.0,-8.0/9.0,-6.0/9.0, 2.0/9.0,-8.0/9.0,-6.0/9.0,
+ 4.0/9.0,-8.0/9.0,-6.0/9.0, 6.0/9.0,-8.0/9.0,-6.0/9.0, 8.0/9.0,-8.0/9.0,-6.0/9.0,
+ -8.0/9.0,-6.0/9.0,-6.0/9.0, -6.0/9.0,-6.0/9.0,-6.0/9.0, -4.0/9.0,-6.0/9.0,-6.0/9.0,
+ -2.0/9.0,-6.0/9.0,-6.0/9.0, 0.0,-6.0/9.0,-6.0/9.0, 2.0/9.0,-6.0/9.0,-6.0/9.0,
+ 4.0/9.0,-6.0/9.0,-6.0/9.0, 6.0/9.0,-6.0/9.0,-6.0/9.0, 8.0/9.0,-6.0/9.0,-6.0/9.0,
+ -8.0/9.0,-4.0/9.0,-6.0/9.0, -6.0/9.0,-4.0/9.0,-6.0/9.0, -4.0/9.0,-4.0/9.0,-6.0/9.0,
+ -2.0/9.0,-4.0/9.0,-6.0/9.0, 0.0,-4.0/9.0,-6.0/9.0, 2.0/9.0,-4.0/9.0,-6.0/9.0,
+ 4.0/9.0,-4.0/9.0,-6.0/9.0, 6.0/9.0,-4.0/9.0,-6.0/9.0, 8.0/9.0,-4.0/9.0,-6.0/9.0,
+ -8.0/9.0,-2.0/9.0,-6.0/9.0, -6.0/9.0,-2.0/9.0,-6.0/9.0, -4.0/9.0,-2.0/9.0,-6.0/9.0,
+ -2.0/9.0,-2.0/9.0,-6.0/9.0, 0.0,-2.0/9.0,-6.0/9.0, 2.0/9.0,-2.0/9.0,-6.0/9.0,
+ 4.0/9.0,-2.0/9.0,-6.0/9.0, 6.0/9.0,-2.0/9.0,-6.0/9.0, 8.0/9.0,-2.0/9.0,-6.0/9.0,
+ -8.0/9.0, 0.0,-6.0/9.0, -6.0/9.0, 0.0,-6.0/9.0, -4.0/9.0, 0.0,-6.0/9.0,
+ -2.0/9.0, 0.0,-6.0/9.0, 0.0, 0.0,-6.0/9.0, 2.0/9.0, 0.0,-6.0/9.0,
+ 4.0/9.0, 0.0,-6.0/9.0, 6.0/9.0, 0.0,-6.0/9.0, 8.0/9.0, 0.0,-6.0/9.0,
+ -8.0/9.0, 2.0/9.0,-6.0/9.0, -6.0/9.0, 2.0/9.0,-6.0/9.0, -4.0/9.0, 2.0/9.0,-6.0/9.0,
+ -2.0/9.0, 2.0/9.0,-6.0/9.0, 0.0, 2.0/9.0,-6.0/9.0, 2.0/9.0, 2.0/9.0,-6.0/9.0,
+ 4.0/9.0, 2.0/9.0,-6.0/9.0, 6.0/9.0, 2.0/9.0,-6.0/9.0, 8.0/9.0, 2.0/9.0,-6.0/9.0,
+ -8.0/9.0, 4.0/9.0,-6.0/9.0, -6.0/9.0, 4.0/9.0,-6.0/9.0, -4.0/9.0, 4.0/9.0,-6.0/9.0,
+ -2.0/9.0, 4.0/9.0,-6.0/9.0, 0.0, 4.0/9.0,-6.0/9.0, 2.0/9.0, 4.0/9.0,-6.0/9.0,
+ 4.0/9.0, 4.0/9.0,-6.0/9.0, 6.0/9.0, 4.0/9.0,-6.0/9.0, 8.0/9.0, 4.0/9.0,-6.0/9.0,
+ -8.0/9.0, 6.0/9.0,-6.0/9.0, -6.0/9.0, 6.0/9.0,-6.0/9.0, -4.0/9.0, 6.0/9.0,-6.0/9.0,
+ -2.0/9.0, 6.0/9.0,-6.0/9.0, 0.0, 6.0/9.0,-6.0/9.0, 2.0/9.0, 6.0/9.0,-6.0/9.0,
+ 4.0/9.0, 6.0/9.0,-6.0/9.0, 6.0/9.0, 6.0/9.0,-6.0/9.0, 8.0/9.0, 6.0/9.0,-6.0/9.0,
+ -8.0/9.0, 8.0/9.0,-6.0/9.0, -6.0/9.0, 8.0/9.0,-6.0/9.0, -4.0/9.0, 8.0/9.0,-6.0/9.0,
+ -2.0/9.0, 8.0/9.0,-6.0/9.0, 0.0, 8.0/9.0,-6.0/9.0, 2.0/9.0, 8.0/9.0,-6.0/9.0,
+ 4.0/9.0, 8.0/9.0,-6.0/9.0, 6.0/9.0, 8.0/9.0,-6.0/9.0, 8.0/9.0, 8.0/9.0,-6.0/9.0,
+ -8.0/9.0,-8.0/9.0,-4.0/9.0, -6.0/9.0,-8.0/9.0,-4.0/9.0, -4.0/9.0,-8.0/9.0,-4.0/9.0,
+ -2.0/9.0,-8.0/9.0,-4.0/9.0, 0.0,-8.0/9.0,-4.0/9.0, 2.0/9.0,-8.0/9.0,-4.0/9.0,
+ 4.0/9.0,-8.0/9.0,-4.0/9.0, 6.0/9.0,-8.0/9.0,-4.0/9.0, 8.0/9.0,-8.0/9.0,-4.0/9.0,
+ -8.0/9.0,-6.0/9.0,-4.0/9.0, -6.0/9.0,-6.0/9.0,-4.0/9.0, -4.0/9.0,-6.0/9.0,-4.0/9.0,
+ -2.0/9.0,-6.0/9.0,-4.0/9.0, 0.0,-6.0/9.0,-4.0/9.0, 2.0/9.0,-6.0/9.0,-4.0/9.0,
+ 4.0/9.0,-6.0/9.0,-4.0/9.0, 6.0/9.0,-6.0/9.0,-4.0/9.0, 8.0/9.0,-6.0/9.0,-4.0/9.0,
+ -8.0/9.0,-4.0/9.0,-4.0/9.0, -6.0/9.0,-4.0/9.0,-4.0/9.0, -4.0/9.0,-4.0/9.0,-4.0/9.0,
+ -2.0/9.0,-4.0/9.0,-4.0/9.0, 0.0,-4.0/9.0,-4.0/9.0, 2.0/9.0,-4.0/9.0,-4.0/9.0,
+ 4.0/9.0,-4.0/9.0,-4.0/9.0, 6.0/9.0,-4.0/9.0,-4.0/9.0, 8.0/9.0,-4.0/9.0,-4.0/9.0,
+ -8.0/9.0,-2.0/9.0,-4.0/9.0, -6.0/9.0,-2.0/9.0,-4.0/9.0, -4.0/9.0,-2.0/9.0,-4.0/9.0,
+ -2.0/9.0,-2.0/9.0,-4.0/9.0, 0.0,-2.0/9.0,-4.0/9.0, 2.0/9.0,-2.0/9.0,-4.0/9.0,
+ 4.0/9.0,-2.0/9.0,-4.0/9.0, 6.0/9.0,-2.0/9.0,-4.0/9.0, 8.0/9.0,-2.0/9.0,-4.0/9.0,
+ -8.0/9.0, 0.0,-4.0/9.0, -6.0/9.0, 0.0,-4.0/9.0, -4.0/9.0, 0.0,-4.0/9.0,
+ -2.0/9.0, 0.0,-4.0/9.0, 0.0, 0.0,-4.0/9.0, 2.0/9.0, 0.0,-4.0/9.0,
+ 4.0/9.0, 0.0,-4.0/9.0, 6.0/9.0, 0.0,-4.0/9.0, 8.0/9.0, 0.0,-4.0/9.0,
+ -8.0/9.0, 2.0/9.0,-4.0/9.0, -6.0/9.0, 2.0/9.0,-4.0/9.0, -4.0/9.0, 2.0/9.0,-4.0/9.0,
+ -2.0/9.0, 2.0/9.0,-4.0/9.0, 0.0, 2.0/9.0,-4.0/9.0, 2.0/9.0, 2.0/9.0,-4.0/9.0,
+ 4.0/9.0, 2.0/9.0,-4.0/9.0, 6.0/9.0, 2.0/9.0,-4.0/9.0, 8.0/9.0, 2.0/9.0,-4.0/9.0,
+ -8.0/9.0, 4.0/9.0,-4.0/9.0, -6.0/9.0, 4.0/9.0,-4.0/9.0, -4.0/9.0, 4.0/9.0,-4.0/9.0,
+ -2.0/9.0, 4.0/9.0,-4.0/9.0, 0.0, 4.0/9.0,-4.0/9.0, 2.0/9.0, 4.0/9.0,-4.0/9.0,
+ 4.0/9.0, 4.0/9.0,-4.0/9.0, 6.0/9.0, 4.0/9.0,-4.0/9.0, 8.0/9.0, 4.0/9.0,-4.0/9.0,
+ -8.0/9.0, 6.0/9.0,-4.0/9.0, -6.0/9.0, 6.0/9.0,-4.0/9.0, -4.0/9.0, 6.0/9.0,-4.0/9.0,
+ -2.0/9.0, 6.0/9.0,-4.0/9.0, 0.0, 6.0/9.0,-4.0/9.0, 2.0/9.0, 6.0/9.0,-4.0/9.0,
+ 4.0/9.0, 6.0/9.0,-4.0/9.0, 6.0/9.0, 6.0/9.0,-4.0/9.0, 8.0/9.0, 6.0/9.0,-4.0/9.0,
+ -8.0/9.0, 8.0/9.0,-4.0/9.0, -6.0/9.0, 8.0/9.0,-4.0/9.0, -4.0/9.0, 8.0/9.0,-4.0/9.0,
+ -2.0/9.0, 8.0/9.0,-4.0/9.0, 0.0, 8.0/9.0,-4.0/9.0, 2.0/9.0, 8.0/9.0,-4.0/9.0,
+ 4.0/9.0, 8.0/9.0,-4.0/9.0, 6.0/9.0, 8.0/9.0,-4.0/9.0, 8.0/9.0, 8.0/9.0,-4.0/9.0,
+ -8.0/9.0,-8.0/9.0,-2.0/9.0, -6.0/9.0,-8.0/9.0,-2.0/9.0, -4.0/9.0,-8.0/9.0,-2.0/9.0,
+ -2.0/9.0,-8.0/9.0,-2.0/9.0, 0.0,-8.0/9.0,-2.0/9.0, 2.0/9.0,-8.0/9.0,-2.0/9.0,
+ 4.0/9.0,-8.0/9.0,-2.0/9.0, 6.0/9.0,-8.0/9.0,-2.0/9.0, 8.0/9.0,-8.0/9.0,-2.0/9.0,
+ -8.0/9.0,-6.0/9.0,-2.0/9.0, -6.0/9.0,-6.0/9.0,-2.0/9.0, -4.0/9.0,-6.0/9.0,-2.0/9.0,
+ -2.0/9.0,-6.0/9.0,-2.0/9.0, 0.0,-6.0/9.0,-2.0/9.0, 2.0/9.0,-6.0/9.0,-2.0/9.0,
+ 4.0/9.0,-6.0/9.0,-2.0/9.0, 6.0/9.0,-6.0/9.0,-2.0/9.0, 8.0/9.0,-6.0/9.0,-2.0/9.0,
+ -8.0/9.0,-4.0/9.0,-2.0/9.0, -6.0/9.0,-4.0/9.0,-2.0/9.0, -4.0/9.0,-4.0/9.0,-2.0/9.0,
+ -2.0/9.0,-4.0/9.0,-2.0/9.0, 0.0,-4.0/9.0,-2.0/9.0, 2.0/9.0,-4.0/9.0,-2.0/9.0,
+ 4.0/9.0,-4.0/9.0,-2.0/9.0, 6.0/9.0,-4.0/9.0,-2.0/9.0, 8.0/9.0,-4.0/9.0,-2.0/9.0,
+ -8.0/9.0,-2.0/9.0,-2.0/9.0, -6.0/9.0,-2.0/9.0,-2.0/9.0, -4.0/9.0,-2.0/9.0,-2.0/9.0,
+ -2.0/9.0,-2.0/9.0,-2.0/9.0, 0.0,-2.0/9.0,-2.0/9.0, 2.0/9.0,-2.0/9.0,-2.0/9.0,
+ 4.0/9.0,-2.0/9.0,-2.0/9.0, 6.0/9.0,-2.0/9.0,-2.0/9.0, 8.0/9.0,-2.0/9.0,-2.0/9.0,
+ -8.0/9.0, 0.0,-2.0/9.0, -6.0/9.0, 0.0,-2.0/9.0, -4.0/9.0, 0.0,-2.0/9.0,
+ -2.0/9.0, 0.0,-2.0/9.0, 0.0, 0.0,-2.0/9.0, 2.0/9.0, 0.0,-2.0/9.0,
+ 4.0/9.0, 0.0,-2.0/9.0, 6.0/9.0, 0.0,-2.0/9.0, 8.0/9.0, 0.0,-2.0/9.0,
+ -8.0/9.0, 2.0/9.0,-2.0/9.0, -6.0/9.0, 2.0/9.0,-2.0/9.0, -4.0/9.0, 2.0/9.0,-2.0/9.0,
+ -2.0/9.0, 2.0/9.0,-2.0/9.0, 0.0, 2.0/9.0,-2.0/9.0, 2.0/9.0, 2.0/9.0,-2.0/9.0,
+ 4.0/9.0, 2.0/9.0,-2.0/9.0, 6.0/9.0, 2.0/9.0,-2.0/9.0, 8.0/9.0, 2.0/9.0,-2.0/9.0,
+ -8.0/9.0, 4.0/9.0,-2.0/9.0, -6.0/9.0, 4.0/9.0,-2.0/9.0, -4.0/9.0, 4.0/9.0,-2.0/9.0,
+ -2.0/9.0, 4.0/9.0,-2.0/9.0, 0.0, 4.0/9.0,-2.0/9.0, 2.0/9.0, 4.0/9.0,-2.0/9.0,
+ 4.0/9.0, 4.0/9.0,-2.0/9.0, 6.0/9.0, 4.0/9.0,-2.0/9.0, 8.0/9.0, 4.0/9.0,-2.0/9.0,
+ -8.0/9.0, 6.0/9.0,-2.0/9.0, -6.0/9.0, 6.0/9.0,-2.0/9.0, -4.0/9.0, 6.0/9.0,-2.0/9.0,
+ -2.0/9.0, 6.0/9.0,-2.0/9.0, 0.0, 6.0/9.0,-2.0/9.0, 2.0/9.0, 6.0/9.0,-2.0/9.0,
+ 4.0/9.0, 6.0/9.0,-2.0/9.0, 6.0/9.0, 6.0/9.0,-2.0/9.0, 8.0/9.0, 6.0/9.0,-2.0/9.0,
+ -8.0/9.0, 8.0/9.0,-2.0/9.0, -6.0/9.0, 8.0/9.0,-2.0/9.0, -4.0/9.0, 8.0/9.0,-2.0/9.0,
+ -2.0/9.0, 8.0/9.0,-2.0/9.0, 0.0, 8.0/9.0,-2.0/9.0, 2.0/9.0, 8.0/9.0,-2.0/9.0,
+ 4.0/9.0, 8.0/9.0,-2.0/9.0, 6.0/9.0, 8.0/9.0,-2.0/9.0, 8.0/9.0, 8.0/9.0,-2.0/9.0,
+ -8.0/9.0,-8.0/9.0, 0.0, -6.0/9.0,-8.0/9.0, 0.0, -4.0/9.0,-8.0/9.0, 0.0,
+ -2.0/9.0,-8.0/9.0, 0.0, 0.0,-8.0/9.0, 0.0, 2.0/9.0,-8.0/9.0, 0.0,
+ 4.0/9.0,-8.0/9.0, 0.0, 6.0/9.0,-8.0/9.0, 0.0, 8.0/9.0,-8.0/9.0, 0.0,
+ -8.0/9.0,-6.0/9.0, 0.0, -6.0/9.0,-6.0/9.0, 0.0, -4.0/9.0,-6.0/9.0, 0.0,
+ -2.0/9.0,-6.0/9.0, 0.0, 0.0,-6.0/9.0, 0.0, 2.0/9.0,-6.0/9.0, 0.0,
+ 4.0/9.0,-6.0/9.0, 0.0, 6.0/9.0,-6.0/9.0, 0.0, 8.0/9.0,-6.0/9.0, 0.0,
+ -8.0/9.0,-4.0/9.0, 0.0, -6.0/9.0,-4.0/9.0, 0.0, -4.0/9.0,-4.0/9.0, 0.0,
+ -2.0/9.0,-4.0/9.0, 0.0, 0.0,-4.0/9.0, 0.0, 2.0/9.0,-4.0/9.0, 0.0,
+ 4.0/9.0,-4.0/9.0, 0.0, 6.0/9.0,-4.0/9.0, 0.0, 8.0/9.0,-4.0/9.0, 0.0,
+ -8.0/9.0,-2.0/9.0, 0.0, -6.0/9.0,-2.0/9.0, 0.0, -4.0/9.0,-2.0/9.0, 0.0,
+ -2.0/9.0,-2.0/9.0, 0.0, 0.0,-2.0/9.0, 0.0, 2.0/9.0,-2.0/9.0, 0.0,
+ 4.0/9.0,-2.0/9.0, 0.0, 6.0/9.0,-2.0/9.0, 0.0, 8.0/9.0,-2.0/9.0, 0.0,
+ -8.0/9.0, 0.0, 0.0, -6.0/9.0, 0.0, 0.0, -4.0/9.0, 0.0, 0.0,
+ -2.0/9.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0/9.0, 0.0, 0.0,
+ 4.0/9.0, 0.0, 0.0, 6.0/9.0, 0.0, 0.0, 8.0/9.0, 0.0, 0.0,
+ -8.0/9.0, 2.0/9.0, 0.0, -6.0/9.0, 2.0/9.0, 0.0, -4.0/9.0, 2.0/9.0, 0.0,
+ -2.0/9.0, 2.0/9.0, 0.0, 0.0, 2.0/9.0, 0.0, 2.0/9.0, 2.0/9.0, 0.0,
+ 4.0/9.0, 2.0/9.0, 0.0, 6.0/9.0, 2.0/9.0, 0.0, 8.0/9.0, 2.0/9.0, 0.0,
+ -8.0/9.0, 4.0/9.0, 0.0, -6.0/9.0, 4.0/9.0, 0.0, -4.0/9.0, 4.0/9.0, 0.0,
+ -2.0/9.0, 4.0/9.0, 0.0, 0.0, 4.0/9.0, 0.0, 2.0/9.0, 4.0/9.0, 0.0,
+ 4.0/9.0, 4.0/9.0, 0.0, 6.0/9.0, 4.0/9.0, 0.0, 8.0/9.0, 4.0/9.0, 0.0,
+ -8.0/9.0, 6.0/9.0, 0.0, -6.0/9.0, 6.0/9.0, 0.0, -4.0/9.0, 6.0/9.0, 0.0,
+ -2.0/9.0, 6.0/9.0, 0.0, 0.0, 6.0/9.0, 0.0, 2.0/9.0, 6.0/9.0, 0.0,
+ 4.0/9.0, 6.0/9.0, 0.0, 6.0/9.0, 6.0/9.0, 0.0, 8.0/9.0, 6.0/9.0, 0.0,
+ -8.0/9.0, 8.0/9.0, 0.0, -6.0/9.0, 8.0/9.0, 0.0, -4.0/9.0, 8.0/9.0, 0.0,
+ -2.0/9.0, 8.0/9.0, 0.0, 0.0, 8.0/9.0, 0.0, 2.0/9.0, 8.0/9.0, 0.0,
+ 4.0/9.0, 8.0/9.0, 0.0, 6.0/9.0, 8.0/9.0, 0.0, 8.0/9.0, 8.0/9.0, 0.0,
+ -8.0/9.0,-8.0/9.0, 2.0/9.0, -6.0/9.0,-8.0/9.0, 2.0/9.0, -4.0/9.0,-8.0/9.0, 2.0/9.0,
+ -2.0/9.0,-8.0/9.0, 2.0/9.0, 0.0,-8.0/9.0, 2.0/9.0, 2.0/9.0,-8.0/9.0, 2.0/9.0,
+ 4.0/9.0,-8.0/9.0, 2.0/9.0, 6.0/9.0,-8.0/9.0, 2.0/9.0, 8.0/9.0,-8.0/9.0, 2.0/9.0,
+ -8.0/9.0,-6.0/9.0, 2.0/9.0, -6.0/9.0,-6.0/9.0, 2.0/9.0, -4.0/9.0,-6.0/9.0, 2.0/9.0,
+ -2.0/9.0,-6.0/9.0, 2.0/9.0, 0.0,-6.0/9.0, 2.0/9.0, 2.0/9.0,-6.0/9.0, 2.0/9.0,
+ 4.0/9.0,-6.0/9.0, 2.0/9.0, 6.0/9.0,-6.0/9.0, 2.0/9.0, 8.0/9.0,-6.0/9.0, 2.0/9.0,
+ -8.0/9.0,-4.0/9.0, 2.0/9.0, -6.0/9.0,-4.0/9.0, 2.0/9.0, -4.0/9.0,-4.0/9.0, 2.0/9.0,
+ -2.0/9.0,-4.0/9.0, 2.0/9.0, 0.0,-4.0/9.0, 2.0/9.0, 2.0/9.0,-4.0/9.0, 2.0/9.0,
+ 4.0/9.0,-4.0/9.0, 2.0/9.0, 6.0/9.0,-4.0/9.0, 2.0/9.0, 8.0/9.0,-4.0/9.0, 2.0/9.0,
+ -8.0/9.0,-2.0/9.0, 2.0/9.0, -6.0/9.0,-2.0/9.0, 2.0/9.0, -4.0/9.0,-2.0/9.0, 2.0/9.0,
+ -2.0/9.0,-2.0/9.0, 2.0/9.0, 0.0,-2.0/9.0, 2.0/9.0, 2.0/9.0,-2.0/9.0, 2.0/9.0,
+ 4.0/9.0,-2.0/9.0, 2.0/9.0, 6.0/9.0,-2.0/9.0, 2.0/9.0, 8.0/9.0,-2.0/9.0, 2.0/9.0,
+ -8.0/9.0, 0.0, 2.0/9.0, -6.0/9.0, 0.0, 2.0/9.0, -4.0/9.0, 0.0, 2.0/9.0,
+ -2.0/9.0, 0.0, 2.0/9.0, 0.0, 0.0, 2.0/9.0, 2.0/9.0, 0.0, 2.0/9.0,
+ 4.0/9.0, 0.0, 2.0/9.0, 6.0/9.0, 0.0, 2.0/9.0, 8.0/9.0, 0.0, 2.0/9.0,
+ -8.0/9.0, 2.0/9.0, 2.0/9.0, -6.0/9.0, 2.0/9.0, 2.0/9.0, -4.0/9.0, 2.0/9.0, 2.0/9.0,
+ -2.0/9.0, 2.0/9.0, 2.0/9.0, 0.0, 2.0/9.0, 2.0/9.0, 2.0/9.0, 2.0/9.0, 2.0/9.0,
+ 4.0/9.0, 2.0/9.0, 2.0/9.0, 6.0/9.0, 2.0/9.0, 2.0/9.0, 8.0/9.0, 2.0/9.0, 2.0/9.0,
+ -8.0/9.0, 4.0/9.0, 2.0/9.0, -6.0/9.0, 4.0/9.0, 2.0/9.0, -4.0/9.0, 4.0/9.0, 2.0/9.0,
+ -2.0/9.0, 4.0/9.0, 2.0/9.0, 0.0, 4.0/9.0, 2.0/9.0, 2.0/9.0, 4.0/9.0, 2.0/9.0,
+ 4.0/9.0, 4.0/9.0, 2.0/9.0, 6.0/9.0, 4.0/9.0, 2.0/9.0, 8.0/9.0, 4.0/9.0, 2.0/9.0,
+ -8.0/9.0, 6.0/9.0, 2.0/9.0, -6.0/9.0, 6.0/9.0, 2.0/9.0, -4.0/9.0, 6.0/9.0, 2.0/9.0,
+ -2.0/9.0, 6.0/9.0, 2.0/9.0, 0.0, 6.0/9.0, 2.0/9.0, 2.0/9.0, 6.0/9.0, 2.0/9.0,
+ 4.0/9.0, 6.0/9.0, 2.0/9.0, 6.0/9.0, 6.0/9.0, 2.0/9.0, 8.0/9.0, 6.0/9.0, 2.0/9.0,
+ -8.0/9.0, 8.0/9.0, 2.0/9.0, -6.0/9.0, 8.0/9.0, 2.0/9.0, -4.0/9.0, 8.0/9.0, 2.0/9.0,
+ -2.0/9.0, 8.0/9.0, 2.0/9.0, 0.0, 8.0/9.0, 2.0/9.0, 2.0/9.0, 8.0/9.0, 2.0/9.0,
+ 4.0/9.0, 8.0/9.0, 2.0/9.0, 6.0/9.0, 8.0/9.0, 2.0/9.0, 8.0/9.0, 8.0/9.0, 2.0/9.0,
+ -8.0/9.0,-8.0/9.0, 4.0/9.0, -6.0/9.0,-8.0/9.0, 4.0/9.0, -4.0/9.0,-8.0/9.0, 4.0/9.0,
+ -2.0/9.0,-8.0/9.0, 4.0/9.0, 0.0,-8.0/9.0, 4.0/9.0, 2.0/9.0,-8.0/9.0, 4.0/9.0,
+ 4.0/9.0,-8.0/9.0, 4.0/9.0, 6.0/9.0,-8.0/9.0, 4.0/9.0, 8.0/9.0,-8.0/9.0, 4.0/9.0,
+ -8.0/9.0,-6.0/9.0, 4.0/9.0, -6.0/9.0,-6.0/9.0, 4.0/9.0, -4.0/9.0,-6.0/9.0, 4.0/9.0,
+ -2.0/9.0,-6.0/9.0, 4.0/9.0, 0.0,-6.0/9.0, 4.0/9.0, 2.0/9.0,-6.0/9.0, 4.0/9.0,
+ 4.0/9.0,-6.0/9.0, 4.0/9.0, 6.0/9.0,-6.0/9.0, 4.0/9.0, 8.0/9.0,-6.0/9.0, 4.0/9.0,
+ -8.0/9.0,-4.0/9.0, 4.0/9.0, -6.0/9.0,-4.0/9.0, 4.0/9.0, -4.0/9.0,-4.0/9.0, 4.0/9.0,
+ -2.0/9.0,-4.0/9.0, 4.0/9.0, 0.0,-4.0/9.0, 4.0/9.0, 2.0/9.0,-4.0/9.0, 4.0/9.0,
+ 4.0/9.0,-4.0/9.0, 4.0/9.0, 6.0/9.0,-4.0/9.0, 4.0/9.0, 8.0/9.0,-4.0/9.0, 4.0/9.0,
+ -8.0/9.0,-2.0/9.0, 4.0/9.0, -6.0/9.0,-2.0/9.0, 4.0/9.0, -4.0/9.0,-2.0/9.0, 4.0/9.0,
+ -2.0/9.0,-2.0/9.0, 4.0/9.0, 0.0,-2.0/9.0, 4.0/9.0, 2.0/9.0,-2.0/9.0, 4.0/9.0,
+ 4.0/9.0,-2.0/9.0, 4.0/9.0, 6.0/9.0,-2.0/9.0, 4.0/9.0, 8.0/9.0,-2.0/9.0, 4.0/9.0,
+ -8.0/9.0, 0.0, 4.0/9.0, -6.0/9.0, 0.0, 4.0/9.0, -4.0/9.0, 0.0, 4.0/9.0,
+ -2.0/9.0, 0.0, 4.0/9.0, 0.0, 0.0, 4.0/9.0, 2.0/9.0, 0.0, 4.0/9.0,
+ 4.0/9.0, 0.0, 4.0/9.0, 6.0/9.0, 0.0, 4.0/9.0, 8.0/9.0, 0.0, 4.0/9.0,
+ -8.0/9.0, 2.0/9.0, 4.0/9.0, -6.0/9.0, 2.0/9.0, 4.0/9.0, -4.0/9.0, 2.0/9.0, 4.0/9.0,
+ -2.0/9.0, 2.0/9.0, 4.0/9.0, 0.0, 2.0/9.0, 4.0/9.0, 2.0/9.0, 2.0/9.0, 4.0/9.0,
+ 4.0/9.0, 2.0/9.0, 4.0/9.0, 6.0/9.0, 2.0/9.0, 4.0/9.0, 8.0/9.0, 2.0/9.0, 4.0/9.0,
+ -8.0/9.0, 4.0/9.0, 4.0/9.0, -6.0/9.0, 4.0/9.0, 4.0/9.0, -4.0/9.0, 4.0/9.0, 4.0/9.0,
+ -2.0/9.0, 4.0/9.0, 4.0/9.0, 0.0, 4.0/9.0, 4.0/9.0, 2.0/9.0, 4.0/9.0, 4.0/9.0,
+ 4.0/9.0, 4.0/9.0, 4.0/9.0, 6.0/9.0, 4.0/9.0, 4.0/9.0, 8.0/9.0, 4.0/9.0, 4.0/9.0,
+ -8.0/9.0, 6.0/9.0, 4.0/9.0, -6.0/9.0, 6.0/9.0, 4.0/9.0, -4.0/9.0, 6.0/9.0, 4.0/9.0,
+ -2.0/9.0, 6.0/9.0, 4.0/9.0, 0.0, 6.0/9.0, 4.0/9.0, 2.0/9.0, 6.0/9.0, 4.0/9.0,
+ 4.0/9.0, 6.0/9.0, 4.0/9.0, 6.0/9.0, 6.0/9.0, 4.0/9.0, 8.0/9.0, 6.0/9.0, 4.0/9.0,
+ -8.0/9.0, 8.0/9.0, 4.0/9.0, -6.0/9.0, 8.0/9.0, 4.0/9.0, -4.0/9.0, 8.0/9.0, 4.0/9.0,
+ -2.0/9.0, 8.0/9.0, 4.0/9.0, 0.0, 8.0/9.0, 4.0/9.0, 2.0/9.0, 8.0/9.0, 4.0/9.0,
+ 4.0/9.0, 8.0/9.0, 4.0/9.0, 6.0/9.0, 8.0/9.0, 4.0/9.0, 8.0/9.0, 8.0/9.0, 4.0/9.0,
+ -8.0/9.0,-8.0/9.0, 6.0/9.0, -6.0/9.0,-8.0/9.0, 6.0/9.0, -4.0/9.0,-8.0/9.0, 6.0/9.0,
+ -2.0/9.0,-8.0/9.0, 6.0/9.0, 0.0,-8.0/9.0, 6.0/9.0, 2.0/9.0,-8.0/9.0, 6.0/9.0,
+ 4.0/9.0,-8.0/9.0, 6.0/9.0, 6.0/9.0,-8.0/9.0, 6.0/9.0, 8.0/9.0,-8.0/9.0, 6.0/9.0,
+ -8.0/9.0,-6.0/9.0, 6.0/9.0, -6.0/9.0,-6.0/9.0, 6.0/9.0, -4.0/9.0,-6.0/9.0, 6.0/9.0,
+ -2.0/9.0,-6.0/9.0, 6.0/9.0, 0.0,-6.0/9.0, 6.0/9.0, 2.0/9.0,-6.0/9.0, 6.0/9.0,
+ 4.0/9.0,-6.0/9.0, 6.0/9.0, 6.0/9.0,-6.0/9.0, 6.0/9.0, 8.0/9.0,-6.0/9.0, 6.0/9.0,
+ -8.0/9.0,-4.0/9.0, 6.0/9.0, -6.0/9.0,-4.0/9.0, 6.0/9.0, -4.0/9.0,-4.0/9.0, 6.0/9.0,
+ -2.0/9.0,-4.0/9.0, 6.0/9.0, 0.0,-4.0/9.0, 6.0/9.0, 2.0/9.0,-4.0/9.0, 6.0/9.0,
+ 4.0/9.0,-4.0/9.0, 6.0/9.0, 6.0/9.0,-4.0/9.0, 6.0/9.0, 8.0/9.0,-4.0/9.0, 6.0/9.0,
+ -8.0/9.0,-2.0/9.0, 6.0/9.0, -6.0/9.0,-2.0/9.0, 6.0/9.0, -4.0/9.0,-2.0/9.0, 6.0/9.0,
+ -2.0/9.0,-2.0/9.0, 6.0/9.0, 0.0,-2.0/9.0, 6.0/9.0, 2.0/9.0,-2.0/9.0, 6.0/9.0,
+ 4.0/9.0,-2.0/9.0, 6.0/9.0, 6.0/9.0,-2.0/9.0, 6.0/9.0, 8.0/9.0,-2.0/9.0, 6.0/9.0,
+ -8.0/9.0, 0.0, 6.0/9.0, -6.0/9.0, 0.0, 6.0/9.0, -4.0/9.0, 0.0, 6.0/9.0,
+ -2.0/9.0, 0.0, 6.0/9.0, 0.0, 0.0, 6.0/9.0, 2.0/9.0, 0.0, 6.0/9.0,
+ 4.0/9.0, 0.0, 6.0/9.0, 6.0/9.0, 0.0, 6.0/9.0, 8.0/9.0, 0.0, 6.0/9.0,
+ -8.0/9.0, 2.0/9.0, 6.0/9.0, -6.0/9.0, 2.0/9.0, 6.0/9.0, -4.0/9.0, 2.0/9.0, 6.0/9.0,
+ -2.0/9.0, 2.0/9.0, 6.0/9.0, 0.0, 2.0/9.0, 6.0/9.0, 2.0/9.0, 2.0/9.0, 6.0/9.0,
+ 4.0/9.0, 2.0/9.0, 6.0/9.0, 6.0/9.0, 2.0/9.0, 6.0/9.0, 8.0/9.0, 2.0/9.0, 6.0/9.0,
+ -8.0/9.0, 4.0/9.0, 6.0/9.0, -6.0/9.0, 4.0/9.0, 6.0/9.0, -4.0/9.0, 4.0/9.0, 6.0/9.0,
+ -2.0/9.0, 4.0/9.0, 6.0/9.0, 0.0, 4.0/9.0, 6.0/9.0, 2.0/9.0, 4.0/9.0, 6.0/9.0,
+ 4.0/9.0, 4.0/9.0, 6.0/9.0, 6.0/9.0, 4.0/9.0, 6.0/9.0, 8.0/9.0, 4.0/9.0, 6.0/9.0,
+ -8.0/9.0, 6.0/9.0, 6.0/9.0, -6.0/9.0, 6.0/9.0, 6.0/9.0, -4.0/9.0, 6.0/9.0, 6.0/9.0,
+ -2.0/9.0, 6.0/9.0, 6.0/9.0, 0.0, 6.0/9.0, 6.0/9.0, 2.0/9.0, 6.0/9.0, 6.0/9.0,
+ 4.0/9.0, 6.0/9.0, 6.0/9.0, 6.0/9.0, 6.0/9.0, 6.0/9.0, 8.0/9.0, 6.0/9.0, 6.0/9.0,
+ -8.0/9.0, 8.0/9.0, 6.0/9.0, -6.0/9.0, 8.0/9.0, 6.0/9.0, -4.0/9.0, 8.0/9.0, 6.0/9.0,
+ -2.0/9.0, 8.0/9.0, 6.0/9.0, 0.0, 8.0/9.0, 6.0/9.0, 2.0/9.0, 8.0/9.0, 6.0/9.0,
+ 4.0/9.0, 8.0/9.0, 6.0/9.0, 6.0/9.0, 8.0/9.0, 6.0/9.0, 8.0/9.0, 8.0/9.0, 6.0/9.0,
+ -8.0/9.0,-8.0/9.0, 8.0/9.0, -6.0/9.0,-8.0/9.0, 8.0/9.0, -4.0/9.0,-8.0/9.0, 8.0/9.0,
+ -2.0/9.0,-8.0/9.0, 8.0/9.0, 0.0,-8.0/9.0, 8.0/9.0, 2.0/9.0,-8.0/9.0, 8.0/9.0,
+ 4.0/9.0,-8.0/9.0, 8.0/9.0, 6.0/9.0,-8.0/9.0, 8.0/9.0, 8.0/9.0,-8.0/9.0, 8.0/9.0,
+ -8.0/9.0,-6.0/9.0, 8.0/9.0, -6.0/9.0,-6.0/9.0, 8.0/9.0, -4.0/9.0,-6.0/9.0, 8.0/9.0,
+ -2.0/9.0,-6.0/9.0, 8.0/9.0, 0.0,-6.0/9.0, 8.0/9.0, 2.0/9.0,-6.0/9.0, 8.0/9.0,
+ 4.0/9.0,-6.0/9.0, 8.0/9.0, 6.0/9.0,-6.0/9.0, 8.0/9.0, 8.0/9.0,-6.0/9.0, 8.0/9.0,
+ -8.0/9.0,-4.0/9.0, 8.0/9.0, -6.0/9.0,-4.0/9.0, 8.0/9.0, -4.0/9.0,-4.0/9.0, 8.0/9.0,
+ -2.0/9.0,-4.0/9.0, 8.0/9.0, 0.0,-4.0/9.0, 8.0/9.0, 2.0/9.0,-4.0/9.0, 8.0/9.0,
+ 4.0/9.0,-4.0/9.0, 8.0/9.0, 6.0/9.0,-4.0/9.0, 8.0/9.0, 8.0/9.0,-4.0/9.0, 8.0/9.0,
+ -8.0/9.0,-2.0/9.0, 8.0/9.0, -6.0/9.0,-2.0/9.0, 8.0/9.0, -4.0/9.0,-2.0/9.0, 8.0/9.0,
+ -2.0/9.0,-2.0/9.0, 8.0/9.0, 0.0,-2.0/9.0, 8.0/9.0, 2.0/9.0,-2.0/9.0, 8.0/9.0,
+ 4.0/9.0,-2.0/9.0, 8.0/9.0, 6.0/9.0,-2.0/9.0, 8.0/9.0, 8.0/9.0,-2.0/9.0, 8.0/9.0,
+ -8.0/9.0, 0.0, 8.0/9.0, -6.0/9.0, 0.0, 8.0/9.0, -4.0/9.0, 0.0, 8.0/9.0,
+ -2.0/9.0, 0.0, 8.0/9.0, 0.0, 0.0, 8.0/9.0, 2.0/9.0, 0.0, 8.0/9.0,
+ 4.0/9.0, 0.0, 8.0/9.0, 6.0/9.0, 0.0, 8.0/9.0, 8.0/9.0, 0.0, 8.0/9.0,
+ -8.0/9.0, 2.0/9.0, 8.0/9.0, -6.0/9.0, 2.0/9.0, 8.0/9.0, -4.0/9.0, 2.0/9.0, 8.0/9.0,
+ -2.0/9.0, 2.0/9.0, 8.0/9.0, 0.0, 2.0/9.0, 8.0/9.0, 2.0/9.0, 2.0/9.0, 8.0/9.0,
+ 4.0/9.0, 2.0/9.0, 8.0/9.0, 6.0/9.0, 2.0/9.0, 8.0/9.0, 8.0/9.0, 2.0/9.0, 8.0/9.0,
+ -8.0/9.0, 4.0/9.0, 8.0/9.0, -6.0/9.0, 4.0/9.0, 8.0/9.0, -4.0/9.0, 4.0/9.0, 8.0/9.0,
+ -2.0/9.0, 4.0/9.0, 8.0/9.0, 0.0, 4.0/9.0, 8.0/9.0, 2.0/9.0, 4.0/9.0, 8.0/9.0,
+ 4.0/9.0, 4.0/9.0, 8.0/9.0, 6.0/9.0, 4.0/9.0, 8.0/9.0, 8.0/9.0, 4.0/9.0, 8.0/9.0,
+ -8.0/9.0, 6.0/9.0, 8.0/9.0, -6.0/9.0, 6.0/9.0, 8.0/9.0, -4.0/9.0, 6.0/9.0, 8.0/9.0,
+ -2.0/9.0, 6.0/9.0, 8.0/9.0, 0.0, 6.0/9.0, 8.0/9.0, 2.0/9.0, 6.0/9.0, 8.0/9.0,
+ 4.0/9.0, 6.0/9.0, 8.0/9.0, 6.0/9.0, 6.0/9.0, 8.0/9.0, 8.0/9.0, 6.0/9.0, 8.0/9.0,
+ -8.0/9.0, 8.0/9.0, 8.0/9.0, -6.0/9.0, 8.0/9.0, 8.0/9.0, -4.0/9.0, 8.0/9.0, 8.0/9.0,
+ -2.0/9.0, 8.0/9.0, 8.0/9.0, 0.0, 8.0/9.0, 8.0/9.0, 2.0/9.0, 8.0/9.0, 8.0/9.0,
+ 4.0/9.0, 8.0/9.0, 8.0/9.0, 6.0/9.0, 8.0/9.0, 8.0/9.0, 8.0/9.0, 8.0/9.0, 8.0/9.0
+};
+
+
+static const REAL *grouptableA[16] =
+{ 0,group5bits,group7bits,group10bits,0,0,0,0,0,0,0,0,0,0,0,0};
+static const REAL *grouptableB1[16] =
+{ 0,group5bits,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
+static const REAL *grouptableB234[16] =
+{ 0,group5bits,group7bits,0,group10bits,0,0,0,0,0,0,0,0,0,0,0};
+
+static const int codelengthtableA[16] =
+{ 0, 5, 7, 10, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+static const int codelengthtableB1[16] =
+{ 0, 5, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+static const int codelengthtableB2[16] =
+{ 0, 5, 7, 3, 10, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16 };
+static const int codelengthtableB3[8] = { 0, 5, 7, 3, 10, 4, 5, 16 };
+static const int codelengthtableB4[4] = { 0, 5, 7, 16 };
+
+
+static const REAL factortableA[16] =
+{ 0.0, 1.0/2.0, 1.0/4.0, 1.0/8.0,
+ 1.0/8.0, 1.0/16.0, 1.0/32.0, 1.0/64.0,
+ 1.0/128.0, 1.0/256.0, 1.0/512.0, 1.0/1024.0,
+ 1.0/2048.0, 1.0/4096.0, 1.0/8192.0, 1.0/16384.0 };
+static const REAL factortableB1[16] =
+{ 0.0, 1.0/2.0, 1.0/4.0, 1.0/8.0,
+ 1.0/16.0, 1.0/32.0, 1.0/64.0, 1.0/128.0,
+ 1.0/256.0, 1.0/512.0, 1.0/1024.0, 1.0/2048.0,
+ 1.0/4096.0, 1.0/8192.0, 1.0/16384.0, 1.0/32768.0 };
+static const REAL factortableB2[16] =
+{ 0.0, 1.0/2.0, 1.0/4.0, 1.0/4.0,
+ 1.0/8.0, 1.0/8.0, 1.0/16.0, 1.0/32.0,
+ 1.0/64.0, 1.0/128.0, 1.0/256.0, 1.0/512.0,
+ 1.0/1024.0, 1.0/2048.0, 1.0/4096.0, 1.0/32768.0 };
+static const REAL factortableB3[8] =
+{ 0.0, 1.0/2.0, 1.0/4.0, 1.0/4.0, 1.0/8.0, 1.0/8.0, 1.0/16.0, 1.0/32768.0 };
+static const REAL factortableB4[4] = { 0.0, 1.0/2.0, 1.0/4.0, 1.0/32768.0 };
+
+
+static const REAL ctableA[16]=
+{ 0.0, 1.33333333333, 1.60000000000, 1.77777777777,
+ 1.06666666666, 1.03225806452, 1.01587301587, 1.00787401575,
+ 1.00392156863, 1.00195694716, 1.00097751711, 1.00048851979,
+ 1.00024420024, 1.00012208522, 1.00006103888, 1.00003051851};
+static const REAL ctableB1[16]=
+{ 0.0, 1.33333333333, 1.14285714286, 1.06666666666,
+ 1.03225806452, 1.01587301587, 1.00787401575, 1.00392156863,
+ 1.00195694716, 1.00097751711, 1.00048851979, 1.00024420024,
+ 1.00012208522, 1.00006103888, 1.00003051851, 1.00001525902};
+static const REAL ctableB2[16] =
+{ 0.0, 1.33333333333, 1.60000000000, 1.14285714286,
+ 1.77777777777, 1.06666666666, 1.03225806452, 1.01587301587,
+ 1.00787401575, 1.00392156863, 1.00195694716, 1.00097751711,
+ 1.00048851979, 1.00024420024, 1.00012208522, 1.00001525902};
+static const REAL ctableB3[8] =
+{ 0.0, 1.33333333333, 1.60000000000, 1.14285714286,
+ 1.77777777777, 1.06666666666, 1.03225806452, 1.00001525902 };
+static const REAL ctableB4[4] =
+{ 0.0, 1.33333333333, 1.60000000000, 1.00001525902 };
+
+
+static const REAL dtableA[16]=
+{ 0.0, 0.50000000000, 0.50000000000, 0.50000000000,
+ 0.12500000000, 0.06250000000, 0.03125000000, 0.01562500000,
+ 0.00781250000, 0.00390625000, 0.00195312500, 0.00097656250,
+ 0.00048828125, 0.00024414063, 0.00012207031, 0.00006103516};
+
+static const REAL dtableB1[16]=
+{ 0.0, 0.50000000000, 0.25000000000, 0.12500000000,
+ 0.06250000000, 0.03125000000, 0.01562500000, 0.00781250000,
+ 0.00390625000, 0.00195312500, 0.00097656250, 0.00048828125,
+ 0.00024414063, 0.00012207031, 0.00006103516, 0.00003051758};
+
+static const REAL dtableB2[16]=
+{ 0.0, 0.50000000000, 0.50000000000, 0.25000000000,
+ 0.50000000000, 0.12500000000, 0.06250000000, 0.03125000000,
+ 0.01562500000, 0.00781250000, 0.00390625000, 0.00195312500,
+ 0.00097656250, 0.00048828125, 0.00024414063, 0.00003051758};
+
+static const REAL dtableB3[8]=
+{ 0.0, 0.50000000000, 0.50000000000, 0.25000000000,
+ 0.50000000000, 0.12500000000, 0.06250000000, 0.00003051758};
+
+static const REAL dtableB4[4]=
+{0.0, 0.50000000000, 0.50000000000, 0.00003051758};
+
+
+
+
+
+
+#endif
diff --git a/mpeglib/lib/splay/mpegAudioBitWindow.cpp b/mpeglib/lib/splay/mpegAudioBitWindow.cpp
new file mode 100644
index 00000000..5e63f1fc
--- /dev/null
+++ b/mpeglib/lib/splay/mpegAudioBitWindow.cpp
@@ -0,0 +1,41 @@
+/*
+ bitwindow class
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "mpegAudioBitWindow.h"
+
+
+#include <iostream>
+
+using namespace std;
+
+int MpegAudioBitWindow::getCanReadBits() {
+ int p=bitindex>>3;
+ int bytes=point - p;
+ int bits=bytes*8+(bitindex&7);
+ cout << "point:"<<point
+ << " p:"<<p
+ << " bytes:"<<bytes
+ <<" bitindex:"<<bitindex<<" can read:"<<bits<<endl;
+ return bits;
+}
+
+void MpegAudioBitWindow::wrap(void) {
+ int p=bitindex>>3;
+ point&=(WINDOWSIZE-1);
+
+ if(p>=point) {
+ for(register int i=4;i<point;i++)
+ buffer[WINDOWSIZE+i]=buffer[i];
+ }
+ *((int *)(buffer+WINDOWSIZE))=*((int *)buffer);
+}
diff --git a/mpeglib/lib/splay/mpegAudioBitWindow.h b/mpeglib/lib/splay/mpegAudioBitWindow.h
new file mode 100644
index 00000000..f7a2f64c
--- /dev/null
+++ b/mpeglib/lib/splay/mpegAudioBitWindow.h
@@ -0,0 +1,141 @@
+/*
+ bitwindow class
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __MPEGBITWINDOW_H
+#define __MPEGBITWINDOW_H
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef WORDS_BIGENDIAN
+#define _KEY 0
+#else
+#define _KEY 3
+#endif
+
+
+
+#define WINDOWSIZE 4096
+#define BITWINDOWSIZE (WINDOWSIZE*8)
+
+
+class MpegAudioBitWindow {
+
+ int point,bitindex;
+ char buffer[2*WINDOWSIZE];
+
+ public:
+ MpegAudioBitWindow(){bitindex=point=0;}
+
+ inline void initialize(void) {bitindex=point=0;}
+ inline int gettotalbit(void) const {return bitindex;}
+
+ inline void putbyte(int c) {buffer[point&(WINDOWSIZE-1)]=c;point++;}
+ void wrap(void);
+ inline void rewind(int bits) {bitindex-=bits;}
+ inline void forward(int bits) {bitindex+=bits;}
+
+ // returns number of bits which can safley read
+ int getCanReadBits();
+
+
+ //
+ // Ugly bitgetting inline functions for higher speed
+ //
+
+
+ inline int getbits(int bits) {
+ union
+ {
+ char store[4];
+ int current;
+ }u;
+ int bi;
+
+ if(!bits)return 0;
+
+ u.current=0;
+ bi=(bitindex&7);
+ u.store[_KEY]=buffer[(bitindex>>3)&(WINDOWSIZE-1)]<<bi;
+ //u.store[_KEY]=buffer[bitindex>>3]<<bi;
+ bi=8-bi;
+ bitindex+=bi;
+
+ while(bits) {
+ if(!bi) {
+ u.store[_KEY]=buffer[(bitindex>>3)&(WINDOWSIZE-1)];
+ //u.store[_KEY]=buffer[bitindex>>3];
+ bitindex+=8;
+ bi=8;
+ }
+
+ if(bits>=bi) {
+ u.current<<=bi;
+ bits-=bi;
+ bi=0;
+ }
+ else {
+ u.current<<=bits;
+ bi-=bits;
+ bits=0;
+ }
+ }
+ bitindex-=bi;
+
+ return (u.current>>8);
+ }
+
+
+ int getbit(void) {
+ register int r=(buffer[(bitindex>>3)&(WINDOWSIZE-1)]>>(7-(bitindex&7)))&1;
+ //register int r=(buffer[bitindex>>3]>>(7-(bitindex&7)))&1;
+ bitindex++;
+ return r;
+ }
+
+ // no range check version
+ inline int getbits9_f(int bits) {
+ register unsigned short a;
+ {
+ int offset=bitindex>>3;
+ a=(((unsigned char)buffer[offset])<<8)|((unsigned char)buffer[offset+1]);
+ }
+ a<<=(bitindex&7);
+ bitindex+=bits;
+ return (int)((unsigned int)(a>>(16-bits)));
+ }
+
+ // range check version
+ int getbits9(int bits) {
+ register unsigned short a;
+ {
+ int offset=(bitindex>>3)&(WINDOWSIZE-1);
+ a=(((unsigned char)buffer[offset])<<8)|((unsigned char)buffer[offset+1]);
+ }
+
+ a<<=(bitindex&7);
+ bitindex+=bits;
+ return (int)((unsigned int)(a>>(16-bits)));
+ }
+
+ int peek8() {
+ int offset = (bitindex>>3)&(WINDOWSIZE-1), a;
+ a=(((unsigned char)buffer[offset])<<8) | ((unsigned char)buffer[offset+1]);
+ return (a >> (8-(bitindex&7))) & 0xff;
+ }
+
+
+};
+#endif
diff --git a/mpeglib/lib/splay/mpegAudioFrame.cpp b/mpeglib/lib/splay/mpegAudioFrame.cpp
new file mode 100644
index 00000000..807a23a6
--- /dev/null
+++ b/mpeglib/lib/splay/mpegAudioFrame.cpp
@@ -0,0 +1,200 @@
+/*
+ converts raw mpeg audio stream data into mpeg I encoded audio frames/packets
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "mpegAudioFrame.h"
+
+#define MPEGAUDIOFRAMESIZE 4096
+
+#define FRAME_SYNC 0
+#define FRAME_CHECK_HEADER_1 1
+#define FRAME_CHECK_HEADER_2 2
+
+#include <iostream>
+
+using namespace std;
+
+MpegAudioFrame::MpegAudioFrame():Framer(MPEGAUDIOFRAMESIZE) {
+ mpegAudioHeader=new MpegAudioHeader();
+}
+
+
+MpegAudioFrame::~MpegAudioFrame() {
+ delete mpegAudioHeader;
+}
+
+
+void MpegAudioFrame::unsync(RawDataBuffer* store,int ) {
+ // invalidate header in buffer
+ unsigned char* start=store->ptr();
+ start[0]=0x0;
+ start[1]=0x0;
+ store->setpos(0);
+ find_frame_state=FRAME_SYNC;
+ framesize=0;
+}
+
+
+/**
+ here we parse byte by byte the raw input, first
+ we search for the magic bytes: 0xfffx, then
+ we read the remaining 2 bytes for the header,
+ check if they are "wrong" in a mpeg I audio special way.
+ If everything is fine, we continue with the next state.
+
+ Note: we can be sure here, that the "store" can at least
+ store 4 bytes.
+*/
+int MpegAudioFrame::find_frame(RawDataBuffer* input_buffer,
+ RawDataBuffer* store_buffer) {
+ unsigned char* store=store_buffer->current();
+ int back=false;
+
+ if (find_frame_state == FRAME_SYNC) {
+ if (store_buffer->pos() != 0) {
+ cout << "store buffer not at beginning MpegAudioFrame::find_frame"<<endl;
+ cout << "current state requires this"<<endl;
+ exit(0);
+ }
+ }
+
+ while(input_buffer->eof()==false) {
+ // search for sync bytes
+ unsigned char* input=input_buffer->current();
+ if (find_frame_state == FRAME_SYNC) {
+ while(input_buffer->eof() == false) {
+ input=input_buffer->current();
+ // shift
+ store[0]=store[1];
+ store[1]=input[0];
+ input_buffer->inc();
+
+ if (store[0] != 0xff) {
+ continue;
+ }
+ // upper 4 bit are syncword, except bit one
+ // which is layer 2.5 indicator.
+ if ( (store[1] & 0xe0) != 0xe0) {
+ continue;
+ }
+
+ // found header, now check if other info is ok, too.
+ store_buffer->setpos(2);
+ find_frame_state=FRAME_CHECK_HEADER_1;
+ break;
+ }
+ // back to "main loop"
+ continue;
+ }
+ // ok, try to read two other bytes and then validate the header
+ if (find_frame_state == FRAME_CHECK_HEADER_1) {
+ store[2]=input[0];
+ input_buffer->inc();
+ find_frame_state=FRAME_CHECK_HEADER_2;
+ // back to main loop
+ continue;
+ }
+ if (find_frame_state == FRAME_CHECK_HEADER_2) {
+ store[3]=input[0];
+ input_buffer->inc();
+ }
+
+ // ok, read 4 header bytes. Now check the validity of the
+ // header.
+ int lHeaderOk;
+ lHeaderOk=mpegAudioHeader->parseHeader(store);
+ if (lHeaderOk == false) {
+ find_frame_state=FRAME_SYNC;
+ store_buffer->setpos(0);
+ // go back to "sync for frame"
+ continue;
+ }
+ framesize=mpegAudioHeader->getFramesize();
+ // if framesize > max mepg framsize its an error
+ if (framesize+4 >= store_buffer->size()) {
+ find_frame_state=FRAME_SYNC;
+ store_buffer->setpos(0);
+ // go back to "sync for frame"
+ continue;
+ }
+ // don't allow stupid framesizes:
+ if (framesize <= 4) {
+ find_frame_state=FRAME_SYNC;
+ store_buffer->setpos(0);
+ continue;
+ }
+ // yup! found valid header. forward states
+ store_buffer->setpos(4);
+ back=true;
+ break;
+ }
+ return back;
+}
+
+
+/**
+ here we read data until len=framesize.
+ Then we go to the HAS_FRAME state.
+*/
+int MpegAudioFrame::read_frame(RawDataBuffer* input_buffer,
+ RawDataBuffer* store_buffer) {
+ unsigned char* store=store_buffer->current();
+
+ while(input_buffer->eof()==false) {
+ int has=store_buffer->pos();
+ int need=framesize-has;
+ if(need == 0) {
+ // yup! frame fully read. forward states.
+ // go to end state for main_state.
+ return true;
+ }
+ // try to do a memcpy for speed.
+ int can=input_buffer->untilend();
+ if(can > need) {
+ can=need;
+ }
+ unsigned char* input=input_buffer->current();
+ memcpy(store,input,can);
+ store_buffer->inc(can);
+ input_buffer->inc(can);
+ }
+ int need=framesize-store_buffer->pos();
+
+ if(need == 0) {
+ // yup! frame fully read. forward states.
+ // go to end state for main_state.
+ return true;
+ }
+ return false;
+}
+
+
+
+void MpegAudioFrame::printPrivateStates() {
+ cout << "MpegAudioFrame::printPrivateStates"<<endl;
+ switch(find_frame_state) {
+ case FRAME_SYNC:
+ cout << "frame_state: FRAME_SYNC"<<endl;
+ break;
+ case FRAME_CHECK_HEADER_1:
+ cout << "frame_state: FRAME_CHECK_HEADER_1"<<endl;
+ break;
+ case FRAME_CHECK_HEADER_2:
+ cout << "frame_state: FRAME_CHECK_HEADER_2"<<endl;
+ break;
+ default:
+ cout << "unknown illegal frame_state:"<<find_frame_state<<endl;
+ }
+
+
+}
+
diff --git a/mpeglib/lib/splay/mpegAudioFrame.h b/mpeglib/lib/splay/mpegAudioFrame.h
new file mode 100644
index 00000000..8b4f6c4f
--- /dev/null
+++ b/mpeglib/lib/splay/mpegAudioFrame.h
@@ -0,0 +1,66 @@
+/*
+ converts raw mpeg audio stream data into mpeg I encoded audio frames/packets
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __MPEGAUDIOFRAME_H
+#define __MPEGAUDIOFRAME_H
+
+#include "mpegAudioHeader.h"
+#include "../frame/framer.h"
+#include <kdemacros.h>
+
+/*
+ Here we are framing from raw to mpeg audio.
+*/
+
+
+
+
+class KDE_EXPORT MpegAudioFrame : public Framer {
+
+ // max size of buffer is:
+ // header: 4
+ // max bitrate: 448
+ // min freq: 22050
+ // padding: 1
+ // ------------------
+ // maxsize: 4+144000*max(bitrate)/min(freq)+1 ca: 2931 byte
+ // then we add a "sentinel" at the end these are 4 byte.
+ // so we should be ok, with a 4KB buffer.
+
+ // internal, how much data we need to read
+ int framesize;
+
+ // internall, for header searching
+ int find_frame_state;
+
+ // internal use for header parsing+validating
+ MpegAudioHeader* mpegAudioHeader;
+
+ public:
+ MpegAudioFrame();
+ ~MpegAudioFrame();
+
+
+ private:
+
+ int find_frame(RawDataBuffer* input,RawDataBuffer* store);
+ int read_frame(RawDataBuffer* input,RawDataBuffer* store);
+
+ void unsync(RawDataBuffer* store,int lReset);
+ void printPrivateStates();
+
+};
+
+
+#endif
diff --git a/mpeglib/lib/splay/mpegAudioHeader.cpp b/mpeglib/lib/splay/mpegAudioHeader.cpp
new file mode 100644
index 00000000..7e34b212
--- /dev/null
+++ b/mpeglib/lib/splay/mpegAudioHeader.cpp
@@ -0,0 +1,268 @@
+/*
+ stores information after we found a header.
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "mpegAudioHeader.h"
+
+#define DEBUG_HEADER(x)
+//#define DEBUG_HEADER(x) x
+
+
+#include <iostream>
+
+using namespace std;
+
+static const int frequencies[3][3]= {
+ {44100,48000,32000}, // MPEG 1
+ {22050,24000,16000}, // MPEG 2
+ {11025,12000,8000} // MPEG 2.5
+};
+
+static int translate[3][2][16] = { { { 2,0,0,2,2,2,3,3,3,3,3,3,3,3,3,2 } ,
+ { 2,0,0,0,0,0,0,2,2,2,3,3,3,3,3,2 } } ,
+ { { 2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2 } ,
+ { 2,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2 } } ,
+ { { 2,1,1,2,2,2,3,3,3,3,3,3,3,3,3,2 } ,
+ { 2,1,1,1,1,1,1,2,2,2,3,3,3,3,3,2 } } };
+
+
+static int sblims[5] = { 8 , 12 , 27, 30 , 30 };
+
+
+static const int bitrate[2][3][15]= {
+ // MPEG 1
+ {{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448},
+ {0,32,48,56,64,80,96,112,128,160,192,224,256,320,384},
+ {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320}},
+
+ // MPEG 2
+ {{0,32,48,56,64,80,96,112,128,144,160,176,192,224,256},
+ {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160},
+ {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160}}
+};
+
+
+MpegAudioHeader::MpegAudioHeader() {
+
+}
+
+
+MpegAudioHeader::~MpegAudioHeader() {
+}
+
+
+int MpegAudioHeader::getChannelbitrate() {
+ cout << "getChannelbitrate not implemented"<<endl;
+ return 0;
+}
+
+int MpegAudioHeader::parseHeader(unsigned char* buf){
+
+ int c;
+ int mpeg25=false;
+
+
+ // Analyzing
+ header[0]=buf[0];
+ header[1]=buf[1];
+ header[2]=buf[2];
+ header[3]=buf[3];
+
+ c=buf[1];
+ lmpeg25=false;
+ if ( (c&0xf0) == 0xe0) {
+ lmpeg25=true;
+ }
+
+ c&=0xf;
+ protection=c&1;
+ layer=4-((c>>1)&3);
+ // we catch the layer==4 error later, for now go on with parsing
+ version=(int)(((c>>3)&1)^1);
+ if ((version==0) && lmpeg25) {
+ DEBUG_HEADER(cout << "wrong lsf/mpeg25 combination"<<endl;)
+ return false;
+ }
+ c=buf[2];
+
+ c=((c))>>1;
+ padding=(c&1);
+ c>>=1;
+ frequency=(int)(c&3);
+ c>>=2;
+ bitrateindex=(int)c;
+ if(bitrateindex>=15) {
+ DEBUG_HEADER(cout << "bitrateindex error"<<endl;)
+ return false;
+ }
+ c=buf[3];
+
+ c=((unsigned int)(c))>>4;
+ extendedmode=c&3;
+ mode=(int)(c>>2);
+
+
+ // Making information
+ inputstereo= (mode==_MODE_SINGLE)?0:1;
+
+ //
+ // frequency can be 0,1 or 2 but the mask above allows 3 as well
+ // check now.
+ if (frequency > 2) {
+ DEBUG_HEADER(cout << "frequency value out of range"<<endl;)
+ return false;
+ }
+
+ //
+ // does not belong here should be in the layer specific parts. [START]
+ //
+
+ switch(layer) {
+ case 3:
+ subbandnumber=0;
+ stereobound=0;
+ tableindex=0;
+ break;
+ case 2:
+ tableindex = translate[frequency][inputstereo][bitrateindex];
+ subbandnumber = sblims[tableindex];
+ stereobound = subbandnumber;
+ /*
+ Now merge the tableindex, for the bitalloclengthtable
+ */
+ tableindex=tableindex>>1;
+ if(mode==_MODE_SINGLE)stereobound=0;
+ if(mode==_MODE_JOINT)stereobound=(extendedmode+1)<<2;
+
+ break;
+ case 1:
+ subbandnumber=MAXSUBBAND;
+ stereobound=subbandnumber;
+ tableindex=0;
+ if(mode==_MODE_SINGLE)stereobound=0;
+ if(mode==_MODE_JOINT)stereobound=(extendedmode+1)<<2;
+ break;
+ default:
+ DEBUG_HEADER(cout <<"unknown layer"<<endl;)
+ return false;
+ }
+
+ //
+ // does not belong here should be in the layer specific parts. [END]
+ //
+ frequencyHz=frequencies[version+lmpeg25][frequency];
+ // framesize & slots
+ if(layer==1) {
+ if (frequencyHz <= 0) {
+ return false;
+ }
+ framesize=(12000*bitrate[version][0][bitrateindex])/frequencyHz;
+
+ if(frequency==_FREQUENCY_44100 && padding)framesize++;
+ framesize<<=2;
+ } else {
+ int freq=frequencyHz<<version;
+ if (freq <= 0) {
+ return false;
+ }
+ framesize=(144000*bitrate[version][layer-1][bitrateindex])/freq;
+
+ if(padding)framesize++;
+ if(layer==3) {
+ if(version)
+ layer3slots=framesize-((mode==_MODE_SINGLE)?9:17)
+ -(protection?0:2)
+ -4;
+ else
+ layer3slots=framesize-((mode==_MODE_SINGLE)?17:32)
+ -(protection?0:2)
+ -4;
+ }
+ }
+ if (framesize <= 0) {
+ DEBUG_HEADER(cout << "framesize negative"<<endl;)
+ return false;
+ }
+ return true;
+
+}
+
+
+int MpegAudioHeader::getpcmperframe() {
+ int s;
+
+ s=32;
+ if(layer==3) {
+ s*=18;
+ if(version==0)s*=2;
+ }
+ else {
+ s*=SCALEBLOCK;
+ if(layer==2)s*=3;
+ }
+
+ return s;
+}
+
+
+void MpegAudioHeader::copyTo(MpegAudioHeader* dest) {
+ dest->protection=protection;
+ dest->layer=layer;
+ dest->version=version;
+ dest->padding=padding;
+ dest->frequency=frequency;
+ dest->frequencyHz=frequencyHz;
+ dest->bitrateindex=bitrateindex;
+ dest->extendedmode=extendedmode;
+ dest->mode=mode;
+ dest->inputstereo=inputstereo;
+ dest->channelbitrate=channelbitrate;
+ dest->tableindex=tableindex;
+ dest->subbandnumber=subbandnumber;
+ dest->stereobound=stereobound;
+ dest->framesize=framesize;
+ dest->layer3slots=layer3slots;
+ dest->lmpeg25=lmpeg25;
+}
+
+
+void MpegAudioHeader::print(const char* name) {
+ cout << "MpegAudioHeader [START]:"<<name<<endl;
+ printf("header:%1x%1x%1x%1x\n",header[0],header[1],header[2],header[3]);
+ cout << "getProtection:"<<getProtection()<<endl;
+ cout << "getLayer:"<<getLayer()<<endl;
+ cout << "getVersion:"<<getVersion()<<endl;
+ cout << "getPadding:"<<getPadding()<<endl;
+ cout << "getFrequency:"<<getFrequency()<<endl;
+ cout << "getFrequencyHz:"<<getFrequencyHz()<<endl;
+ cout << "getBitrateindex:"<<getBitrateindex()<<endl;
+ cout << "getExtendedmode:"<<getExtendedmode()<<endl;
+ cout << "getMode():"<<getMode()<<endl;
+ cout << "getInputstereo:"<<getInputstereo()<<endl;
+ cout << "getChannelbitrate:"<<getChannelbitrate()<<endl;
+ cout << "getTableindex:"<<getTableindex()<<endl;
+ cout << "getSubbandnumber:"<<getSubbandnumber()<<endl;
+ cout << "getStereobound:"<<getStereobound()<<endl;
+ cout << "getFramesize:"<<getFramesize()<<endl;
+ cout << "getLayer3slots:"<<getLayer3slots()<<endl;
+ cout << "getpcmperframe:"<<getpcmperframe()<<endl;
+ cout << "MpegAudioHeader [END]:"<<name<<endl;
+
+}
+
+
+
+
+
+
+
diff --git a/mpeglib/lib/splay/mpegAudioHeader.h b/mpeglib/lib/splay/mpegAudioHeader.h
new file mode 100644
index 00000000..ff5b885a
--- /dev/null
+++ b/mpeglib/lib/splay/mpegAudioHeader.h
@@ -0,0 +1,101 @@
+/*
+ stores information after we found a header.
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __MPEGHEADERINFO_H
+#define __MPEGHEADERINFO_H
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define _FREQUENCY_44100 0
+#define _FREQUENCY_48000 1
+#define _FREQUENCY_32000 2
+
+#define _MODE_FULLSTEREO 0
+#define _MODE_JOINT 1
+#define _MODE_DUAL 2
+#define _MODE_SINGLE 3
+
+#define _VERSION_1 0
+#define _VERSION_2 1
+
+
+#define MAXSUBBAND 32
+#define SCALEBLOCK 12
+
+
+
+class MpegAudioHeader {
+
+ int protection;
+ int layer;
+ int version;
+ int padding;
+ int frequency;
+ int frequencyHz;
+ int bitrateindex;
+ int extendedmode;
+ int mode;
+ int inputstereo;
+ int channelbitrate;
+ int tableindex;
+ int subbandnumber;
+ int stereobound;
+ int framesize;
+ int layer3slots;
+ int lmpeg25;
+ unsigned char header[4];
+
+ public:
+ MpegAudioHeader();
+ ~MpegAudioHeader();
+
+ int parseHeader(unsigned char* buf);
+
+ inline int getProtection() { return protection; }
+ inline int getLayer() { return layer; }
+ inline int getVersion() { return version; }
+ inline int getPadding() { return padding; }
+ inline int getFrequency() { return frequency; }
+ inline int getFrequencyHz() { return frequencyHz; }
+ inline int getBitrateindex() { return bitrateindex; }
+ inline int getExtendedmode() { return extendedmode; }
+ inline int getMode() { return mode; }
+ inline int getInputstereo() { return inputstereo; }
+ inline int getFramesize() { return framesize; }
+ inline int getLayer25() { return lmpeg25; }
+
+ // MPEG layer 2
+ inline int getTableindex() { return tableindex; }
+ // MPEG layer 1/2
+ inline int getSubbandnumber() { return subbandnumber; }
+ inline int getStereobound() { return stereobound; }
+ // MPEG layer 3
+ inline int getLayer3slots() { return layer3slots; }
+
+ int getChannelbitrate();
+
+
+ inline unsigned char* getHeader() { return header; }
+ int getpcmperframe();
+
+
+ void copyTo(MpegAudioHeader* dest);
+
+ void print(const char* name);
+ void printStates(const char* name);
+};
+#endif
diff --git a/mpeglib/lib/splay/mpegAudioInfo.cpp b/mpeglib/lib/splay/mpegAudioInfo.cpp
new file mode 100644
index 00000000..981b5bd6
--- /dev/null
+++ b/mpeglib/lib/splay/mpegAudioInfo.cpp
@@ -0,0 +1,262 @@
+/*
+ length detection etc.. for mpeg audio
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#ifndef _FROM_SOURCE
+#define _FROM_SOURCE 1
+#endif
+#include "dxHead.h"
+#include <string.h>
+#include "mpegAudioInfo.h"
+#include "mpegAudioHeader.h"
+#include "mpegAudioStream.h"
+#include "mpegAudioFrame.h"
+
+#include <iostream>
+
+using namespace std;
+
+#define _NEED_LENGTH 1
+#define _NEED_ID3 2
+#define _NEED_NOTHING 3
+
+
+MpegAudioInfo::MpegAudioInfo(FileAccess* input) {
+ xHeadData=new XHEADDATA();
+ xHeadData->toc=new unsigned char[101];
+ lXingVBR=false;
+ id3=new ID3TAG();
+ this->input=input;
+ mpegAudioFrame =new MpegAudioFrame();
+ mpegAudioStream=new MpegAudioStream();
+ mpegAudioHeader=new MpegAudioHeader();
+ reset();
+}
+
+
+MpegAudioInfo::~MpegAudioInfo() {
+ delete[] (xHeadData->toc);
+ delete xHeadData;
+ delete id3;
+ delete mpegAudioStream;
+ delete mpegAudioHeader;
+ delete mpegAudioFrame;
+}
+
+
+int MpegAudioInfo::getByteDirect() {
+ unsigned char byte;
+ if (input->read((char*)&byte,1) != 1) {
+ leof=true;
+ return -1;
+ }
+ return (int)byte;
+}
+
+void MpegAudioInfo::reset() {
+ length=0;
+ initState=_NEED_LENGTH;
+ lNeedInit=true;
+}
+
+
+int MpegAudioInfo::getNeedInit() {
+ return lNeedInit;
+}
+
+void MpegAudioInfo::setNeedInit(int lNeedInit) {
+ this->lNeedInit=lNeedInit;
+}
+
+
+int MpegAudioInfo::initialize() {
+ long fileSize=input->getByteLength();
+ switch(initState) {
+ case _NEED_NOTHING:
+ return true;
+ break;
+ case _NEED_LENGTH:
+ if (initializeLength(fileSize) == true) {
+ initState=_NEED_ID3;
+ }
+ return false;
+ case _NEED_ID3:
+ if (initializeID3(fileSize) == true) {
+ initState=_NEED_NOTHING;
+ return true;
+ }
+ return false;
+ default:
+ cout << "unknown initState in MpegAudioInfo::initialize"<<endl;
+ exit(0);
+ }
+ // never happens
+ return true;
+}
+
+
+int MpegAudioInfo::initializeLength(long fileSize) {
+ // if we are streaming don't touch the stream for length detection
+ if (fileSize == 0) {
+ return true;
+ }
+ int back=getFrame(mpegAudioFrame);
+ if (back != true) {
+ return back;
+ }
+ // found a valid frame (back == true)
+ // store information in header.
+ if (mpegAudioHeader->parseHeader(mpegAudioFrame->outdata()) == false) {
+ cout << "parse header false"<<endl;
+ return false;
+ }
+ calculateLength(fileSize);
+ return back;
+}
+
+int MpegAudioInfo::initializeID3(long fileSize) {
+ int pos=input->getBytePosition();
+ if (input->seek(fileSize-128)<0) {
+ return true;
+ }
+ parseID3();
+ input->seek(pos);
+ return true;
+}
+
+
+long MpegAudioInfo::getLength() {
+ return length;
+}
+
+
+void MpegAudioInfo::calculateLength(long fileSize) {
+
+ int totalframe=0;
+ int framesize=mpegAudioHeader->getFramesize();
+ if (framesize > 0) {
+ totalframe=fileSize/framesize;
+
+ if (parseXing(mpegAudioFrame->outdata(),mpegAudioFrame->len()) == true) {
+ lXingVBR=true;
+ totalframe=xHeadData->frames;
+ }
+ }
+
+ float pcm=mpegAudioHeader->getpcmperframe();
+ float wavfilesize=(totalframe*pcm);
+ float frequence=(float)mpegAudioHeader->getFrequencyHz();
+ length=0;
+ if (frequence != 0) {
+ length=(int)(wavfilesize/frequence);
+ }
+}
+
+
+int MpegAudioInfo::parseXing(unsigned char* frame,int size) {
+
+ int back=false;
+ if (size < 152) {
+ return false;
+ }
+ back=GetXingHeader(xHeadData,(unsigned char*)frame);
+ return back;
+
+}
+
+
+
+long MpegAudioInfo::getSeekPosition(int second) {
+ float length=getLength();
+ long fileSize=input->getByteLength();
+ long pos=0;
+ if (length<1.0) {
+ return 0;
+ }
+ float percent=(float)second/length;
+
+ if (lXingVBR) {
+ pos=SeekPoint(xHeadData->toc,(int)fileSize,100.0*percent);
+ return pos;
+ }
+ pos=(long)(percent*(float)fileSize);
+ return pos;
+}
+
+
+void MpegAudioInfo::parseID3() {
+
+ id3->name [0]=0;
+ id3->artist [0]=0;
+ id3->album [0]=0;
+ id3->year [0]=0;
+ id3->comment [0]=0;
+ id3->genre =0;
+
+ leof=false;
+
+ while(leof == false) {
+ if(getByteDirect()==0x54)
+ if(getByteDirect()==0x41)
+ if(getByteDirect()==0x47) {
+ input->read((char*)&id3->name ,30);id3->name[30]=0;
+ input->read((char*)&id3->artist ,30);id3->artist[30]=0;
+ input->read((char*)&id3->album ,30);id3->album[30]=0;
+ input->read((char*)&id3->year , 4);id3->year[4]=0;
+ input->read((char*)&id3->comment ,30);id3->comment[30]=0;
+ input->read((char*)&id3->genre ,1);
+ return;
+ }
+ }
+}
+
+
+
+void MpegAudioInfo::print(const char* msg) {
+ cout << "MpegAudioInfo:"<<msg<<endl;
+ cout << "Length (sec):"<<length<<endl;
+ cout << "VBR:"<<lXingVBR<<endl;
+ cout << "ID3: Name:"<<id3->name<<endl;
+ cout << "ID3: Artist:"<<id3->artist<<endl;
+ cout << "ID3: Album:"<<id3->album<<endl;
+ cout << "ID3: year:"<<id3->year<<endl;
+ cout << "ID3: genre:"<<(int)(id3->genre)<<endl;
+ cout << "ID3: comment:"<<id3->comment<<endl;
+
+}
+
+
+int MpegAudioInfo::getFrame(MpegAudioFrame* frame) {
+ int state=frame->getState();
+ switch(state) {
+ case FRAME_NEED: {
+ int bytes=frame->canStore();
+ int read=input->read((char*)inputbuffer,bytes);
+ if (read <= 0) {
+ // read error. reset framer
+ frame->reset();
+ break;
+ }
+ frame->store(inputbuffer,bytes);
+ break;
+ }
+ case FRAME_WORK:
+ frame->work();
+ break;
+ case FRAME_HAS:
+ return true;
+ break;
+ default:
+ cout << "unknown state in mpeg audio framing"<<endl;
+ exit(0);
+ }
+ return false;
+}
diff --git a/mpeglib/lib/splay/mpegAudioInfo.h b/mpeglib/lib/splay/mpegAudioInfo.h
new file mode 100644
index 00000000..65298ca6
--- /dev/null
+++ b/mpeglib/lib/splay/mpegAudioInfo.h
@@ -0,0 +1,95 @@
+/*
+ length detection etc.. for mpeg audio
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#ifndef __MPEGAUDIOINFO_H
+#define __MPEGAUDIOINFO_H
+
+#include "../util/file/fileAccess.h"
+
+class MpegAudioStream;
+class MpegAudioHeader;
+class MpegAudioFrame;
+
+typedef struct {
+ char name [30+1];
+ char artist [30+1];
+ char album [30+1];
+ char year [ 4+1];
+ char comment[30+1];
+ unsigned char genre;
+} ID3TAG;
+
+
+class MpegAudioInfo {
+
+ long length;
+ int lXingVBR;
+ ID3TAG* id3;
+ MpegAudioStream* mpegAudioStream;
+ MpegAudioHeader* mpegAudioHeader;
+ MpegAudioFrame * mpegAudioFrame;
+ FileAccess* input;
+ int leof;
+
+ int initState;
+ unsigned char inputbuffer[8192];
+ int lNeedInit;
+ public:
+ MpegAudioInfo(FileAccess* input);
+ ~MpegAudioInfo();
+
+ void reset();
+ int initialize();
+
+ // store info wheter init need or not here.(after reset its true)
+ int getNeedInit();
+ void setNeedInit(int lNeedInit);
+
+
+ // returns byte positions
+ long getSeekPosition(int second);
+ // returns length in seconds
+ long getLength();
+
+ // for id3 parsing you need to seek -128 bytes before the end of strem
+ // after parseID3 you must jump back to 0. This method is blocking
+ int initializeID3();
+ ID3TAG* getID3();
+
+ void print(const char* msg);
+
+ private:
+ // non-blocking init. return true,false,or -1==eof
+ int initializeLength(long fileSize);
+ int initializeID3(long fileSize);
+
+
+
+ void calculateLength(long fileSize);
+ int parseXing(unsigned char* frame,int size);
+
+ // used for seek
+ void parseID3();
+ int getByteDirect();
+
+ // extension for xing vbr
+#ifdef _FROM_SOURCE
+ XHEADDATA* xHeadData;
+#else
+ void* xHeadData;
+#endif
+
+ private:
+ int getFrame(MpegAudioFrame* frame);
+
+};
+#endif
diff --git a/mpeglib/lib/splay/mpegAudioStream.cpp b/mpeglib/lib/splay/mpegAudioStream.cpp
new file mode 100644
index 00000000..b3061224
--- /dev/null
+++ b/mpeglib/lib/splay/mpegAudioStream.cpp
@@ -0,0 +1,43 @@
+/*
+ initializer/resyncer/frame detection etc.. for mpeg audio
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "mpegAudioStream.h"
+
+#include <stdlib.h>
+
+MpegAudioStream::MpegAudioStream() {
+ buffer=NULL;
+}
+
+
+MpegAudioStream::~MpegAudioStream() {
+}
+
+
+
+void MpegAudioStream::setFrame(unsigned char* ptr,int len) {
+ this->buffer=(char*)ptr;
+ this->len=len;
+ bitindex=0;
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/lib/splay/mpegAudioStream.h b/mpeglib/lib/splay/mpegAudioStream.h
new file mode 100644
index 00000000..9e4accc3
--- /dev/null
+++ b/mpeglib/lib/splay/mpegAudioStream.h
@@ -0,0 +1,139 @@
+/*
+ initializer/resyncer/frame detection etc.. for mpeg audio
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __MPEGAUDIOSTREAM_H
+#define __MPEGAUDIOSTREAM_H
+
+// we include this for the big_endian define
+
+#include "mpegAudioBitWindow.h"
+
+#define _MAX_MPEG_BUFFERSIZE 4096
+
+
+/**
+ Here we go from the frame to the bitlevel.
+
+
+*/
+
+class MpegAudioStream {
+
+ char* buffer;
+ int len;
+ int bitindex;
+
+ public:
+ MpegAudioStream();
+ ~MpegAudioStream();
+
+ void setFrame(unsigned char* prt,int len);
+
+ // Bit functions
+
+ inline char* getBuffer() { return buffer; }
+ inline int getBufferSize() { return _MAX_MPEG_BUFFERSIZE ;}
+ inline void sync() { bitindex=(bitindex+7)&0xFFFFFFF8; }
+ inline int issync() { return (bitindex&7);};
+
+ /**
+ Now follow ugly inline function. The performance gain is 1.5 %
+ on a 400 MHz AMD
+ */
+
+ inline int getbyte() {
+ int r=(unsigned char)buffer[bitindex>>3];
+ bitindex+=8;
+ return r;
+ }
+
+ inline int getbits9(int bits) {
+ register unsigned short a;
+ {
+ int offset=bitindex>>3;
+
+ a=(((unsigned char)buffer[offset])<<8) |
+ ((unsigned char)buffer[offset+1]);
+ }
+
+ a<<=(bitindex&7);
+ bitindex+=bits;
+ return (int)((unsigned int)(a>>(16-bits)));
+ }
+
+ inline int getbits8() {
+ register unsigned short a;
+
+ {
+ int offset=bitindex>>3;
+
+ a=(((unsigned char)buffer[offset])<<8) |
+ ((unsigned char)buffer[offset+1]);
+ }
+
+ a<<=(bitindex&7);
+ bitindex+=8;
+ return (int)((unsigned int)(a>>8));
+ }
+
+ inline int getbit() {
+ register int r=(buffer[bitindex>>3]>>(7-(bitindex&7)))&1;
+
+ bitindex++;
+ return r;
+ }
+
+ inline int getbits(int bits) {
+ union
+ {
+ char store[4];
+ int current;
+ }u;
+ int bi;
+
+ if(!bits)return 0;
+
+ u.current=0;
+ bi=(bitindex&7);
+ u.store[_KEY]=buffer[bitindex>>3]<<bi;
+ bi=8-bi;
+ bitindex+=bi;
+
+ while(bits) {
+ if(!bi) {
+ u.store[_KEY]=buffer[bitindex>>3];
+ bitindex+=8;
+ bi=8;
+ }
+ if(bits>=bi) {
+ u.current<<=bi;
+ bits-=bi;
+ bi=0;
+ } else {
+ u.current<<=bits;
+ bi-=bits;
+ bits=0;
+ }
+ }
+ bitindex-=bi;
+ return (u.current>>8);
+ }
+
+
+
+
+};
+
+
+#endif
diff --git a/mpeglib/lib/splay/mpeglayer1.cpp b/mpeglib/lib/splay/mpeglayer1.cpp
new file mode 100644
index 00000000..a32951c4
--- /dev/null
+++ b/mpeglib/lib/splay/mpeglayer1.cpp
@@ -0,0 +1,109 @@
+/* MPEG/WAVE Sound library
+
+ (C) 1997 by Jung woo-jae */
+
+// Mpeglayer1.cc
+// It's for MPEG Layer 1
+
+
+
+#include "mpegsound.h"
+#include "synthesis.h"
+
+// Tables for layer 1
+static const REAL factortable[15] =
+{
+ 0.0,
+ (1.0/2.0) * (4.0/3.0), (1.0/4.0) * (8.0/7.0),
+ (1.0/8.0) * (16.0/15.0), (1.0/16.0) * (32.0/31.0),
+ (1.0/32.0) * (64.0/63.0), (1.0/64.0) * (128.0/127.0),
+ (1.0/128.0) * (256.0/255.0), (1.0/256.0) * (512.0/511.0),
+ (1.0/512.0) * (1024.0/1023.0), (1.0/1024.0) * (2048.0/2047.0),
+ (1.0/2048.0) * (4096.0/4095.0), (1.0/4096.0) * (8192.0/8191.0),
+ (1.0/8192.0) * (16384.0/16383.0), (1.0/16384.0) * (32768.0/32767.0)
+};
+
+static const REAL offsettable[15] =
+{
+ 0.0,
+ ((1.0/2.0)-1.0) * (4.0/3.0), ((1.0/4.0)-1.0) * (8.0/7.0),
+ ((1.0/8.0)-1.0) * (16.0/15.0), ((1.0/16.0)-1.0) * (32.0/31.0),
+ ((1.0/32.0)-1.0) * (64.0/63.0), ((1.0/64.0)-1.0) * (128.0/127.0),
+ ((1.0/128.0)-1.0) * (256.0/255.0), ((1.0/256.0)-1.0) * (512.0/511.0),
+ ((1.0/512.0)-1.0) * (1024.0/1023.0), ((1.0/1024.0)-1.0) * (2048.0/2047.0),
+ ((1.0/2048.0)-1.0) * (4096.0/4095.0), ((1.0/4096.0)-1.0) * (8192.0/8191.0),
+ ((1.0/8192.0)-1.0) * (16384.0/16383.0), ((1.0/16384.0)-1.0) * (32768.0/32767.0)
+};
+
+// Mpeg layer 1
+void Mpegtoraw::extractlayer1(void)
+{
+ int inputstereo=mpegAudioHeader->getInputstereo();
+ int stereobound=mpegAudioHeader->getStereobound();
+ REAL fraction[MAXCHANNEL][MAXSUBBAND];
+ REAL scalefactor[MAXCHANNEL][MAXSUBBAND];
+
+ int bitalloc[MAXCHANNEL][MAXSUBBAND],
+ sample[MAXCHANNEL][MAXSUBBAND];
+
+ register int i,j;
+ int s=stereobound,l;
+
+
+// Bitalloc
+ for(i=0;i<s;i++)
+ {
+ bitalloc[LS][i]=getbits(4);
+ bitalloc[RS][i]=getbits(4);
+ }
+ for(;i<MAXSUBBAND;i++)
+ bitalloc[LS][i]=
+ bitalloc[RS][i]=getbits(4);
+
+// Scale index
+ if(inputstereo)
+ for(i=0;i<MAXSUBBAND;i++)
+ {
+ if(bitalloc[LS][i])scalefactor[LS][i]=scalefactorstable[getbits(6)];
+ if(bitalloc[RS][i])scalefactor[RS][i]=scalefactorstable[getbits(6)];
+ }
+ else
+ for(i=0;i<MAXSUBBAND;i++)
+ if(bitalloc[LS][i])scalefactor[LS][i]=scalefactorstable[getbits(6)];
+
+ for(l=0;l<SCALEBLOCK;l++)
+ {
+ // Sample
+ for(i=0;i<s;i++)
+ {
+ if((j=bitalloc[LS][i]))sample[LS][i]=getbits(j+1);
+ if((j=bitalloc[RS][i]))sample[RS][i]=getbits(j+1);
+ }
+ for(;i<MAXSUBBAND;i++)
+ if((j=bitalloc[LS][i]))sample[LS][i]=sample[RS][i]=getbits(j+1);
+
+
+ // Fraction
+ if(lOutputStereo)
+ for(i=0;i<MAXSUBBAND;i++)
+ {
+ if((j=bitalloc[LS][i]))
+ fraction[LS][i]=(REAL(sample[LS][i])*factortable[j]+offsettable[j])
+ *scalefactor[LS][i];
+ else fraction[LS][i]=0.0;
+ if((j=bitalloc[RS][i]))
+ fraction[RS][i]=(REAL(sample[RS][i])*factortable[j]+offsettable[j])
+ *scalefactor[RS][i];
+ else fraction[RS][i]=0.0;
+ }
+ else
+ for(i=0;i<MAXSUBBAND;i++)
+ if((j=bitalloc[LS][i]))
+ fraction[LS][i]=(REAL(sample[LS][i])*factortable[j]+offsettable[j])
+ *scalefactor[LS][i];
+ else fraction[LS][i]=0.0;
+
+ synthesis->doSynth(lDownSample,lOutputStereo,
+ fraction[LS],fraction[RS]);
+ }
+}
diff --git a/mpeglib/lib/splay/mpeglayer2.cpp b/mpeglib/lib/splay/mpeglayer2.cpp
new file mode 100644
index 00000000..4012ff27
--- /dev/null
+++ b/mpeglib/lib/splay/mpeglayer2.cpp
@@ -0,0 +1,449 @@
+/* MPEG/WAVE Sound library
+
+ (C) 1997 by Jung woo-jae */
+
+// Mpeglayer2.cc
+// It's for MPEG Layer 2
+
+
+#include "mpegsound.h"
+#include "synthesis.h"
+
+#include <iostream>
+
+using namespace std;
+
+#define BUGFIX
+#include "mpeg2tables.h"
+
+
+// workaround for buggy mpeg2 streams.
+// tested with 12 monkey cdi, worgked fine.
+// problem was: the stream produced ints
+// with access out of the tables
+// if we have such an access we set it to a zero entry
+#ifdef BUGFIX
+static int checkCodeRange(int code,const REAL* group) {
+ int back=0;
+ if (group == NULL) {
+ cout << "group null"<<endl;
+ return 0;
+ }
+ back=code;
+
+ if (group == group5bits) {
+ if (back > 27*3) {
+ // redirect to zero value
+ back=3;
+ }
+ return back;
+ }
+ if (group == group7bits) {
+ if (back > 125*3) {
+ back=6;
+ }
+ return back;
+ }
+ if (group == group10bits) {
+ if (back > 729*3) {
+ back=12;
+ }
+ return back;
+ }
+ DEBUG_LAYER(cout << "unknown group found!"<<endl;)
+ return -1;
+}
+
+
+#endif
+
+
+
+
+
+
+
+
+// Mpeg layer 2
+void Mpegtoraw::extractlayer2(void) {
+ int inputstereo=mpegAudioHeader->getInputstereo();
+ int tableindex=mpegAudioHeader->getTableindex();
+ int subbandnumber=mpegAudioHeader->getSubbandnumber();
+ int stereobound=mpegAudioHeader->getStereobound();
+
+ REAL fraction[MAXCHANNEL][3][MAXSUBBAND];
+ unsigned int bitalloc[MAXCHANNEL][MAXSUBBAND],
+ scaleselector[MAXCHANNEL][MAXSUBBAND];
+ REAL scalefactor[2][3][MAXSUBBAND];
+
+ const REAL *group[MAXCHANNEL][MAXSUBBAND];
+ unsigned int codelength[MAXCHANNEL][MAXSUBBAND];
+ REAL factor[MAXCHANNEL][MAXSUBBAND];
+ REAL c[MAXCHANNEL][MAXSUBBAND],d[MAXCHANNEL][MAXSUBBAND];
+
+ int s=stereobound,n=subbandnumber;
+
+
+ // Bitalloc
+ {
+ register int i;
+ register const int *t=bitalloclengthtable[tableindex];
+ for(i=0;i<s;i++,t++)
+ {
+ bitalloc[LS][i]=getbits(*t);
+ bitalloc[RS][i]=getbits(*t);
+ }
+ for(;i<n;i++,t++) {
+ bitalloc[LS][i]=bitalloc[RS][i]=getbits(*t);
+ }
+ }
+
+
+
+ // Scale selector
+ if(inputstereo)
+ for(register int i=0;i<n;i++)
+ {
+ if(bitalloc[LS][i])scaleselector[LS][i]=getbits(2);
+ if(bitalloc[RS][i])scaleselector[RS][i]=getbits(2);
+ }
+ else
+ for(register int i=0;i<n;i++)
+ if(bitalloc[LS][i])scaleselector[LS][i]=getbits(2);
+
+ // Scale index
+ {
+ register int i,j;
+
+
+ for(i=0;i<n;i++)
+ {
+ if((j=bitalloc[LS][i]))
+ {
+ if(!tableindex)
+ {
+ group[LS][i]=grouptableA[j];
+ codelength[LS][i]=codelengthtableA[j];
+ factor[LS][i]=factortableA[j];
+ c[LS][i]=ctableA[j];
+ d[LS][i]=dtableA[j];
+ }
+ else
+ {
+ if(i<=2)
+ {
+ group[LS][i]=grouptableB1[j];
+ codelength[LS][i]=codelengthtableB1[j];
+ factor[LS][i]=factortableB1[j];
+ c[LS][i]=ctableB1[j];
+ d[LS][i]=dtableB1[j];
+ }
+ else
+ {
+ group[LS][i]=grouptableB234[j];
+ if(i<=10)
+ {
+ codelength[LS][i]=codelengthtableB2[j];
+ factor[LS][i]=factortableB2[j];
+ c[LS][i]=ctableB2[j];
+ d[LS][i]=dtableB2[j];
+ }
+ else if(i<=22)
+ {
+ codelength[LS][i]=codelengthtableB3[j];
+ factor[LS][i]=factortableB3[j];
+ c[LS][i]=ctableB3[j];
+ d[LS][i]=dtableB3[j];
+ }
+ else
+ {
+ codelength[LS][i]=codelengthtableB4[j];
+ factor[LS][i]=factortableB4[j];
+ c[LS][i]=ctableB4[j];
+ d[LS][i]=dtableB4[j];
+ }
+ }
+ }
+
+
+ switch(scaleselector[LS][i])
+ {
+ case 0:scalefactor[LS][0][i]=scalefactorstable[getbits(6)];
+ scalefactor[LS][1][i]=scalefactorstable[getbits(6)];
+ scalefactor[LS][2][i]=scalefactorstable[getbits(6)];
+ break;
+ case 1:scalefactor[LS][0][i]=
+ scalefactor[LS][1][i]=scalefactorstable[getbits(6)];
+ scalefactor[LS][2][i]=scalefactorstable[getbits(6)];
+ break;
+ case 2:scalefactor[LS][0][i]=
+ scalefactor[LS][1][i]=
+ scalefactor[LS][2][i]=scalefactorstable[getbits(6)];
+ break;
+ case 3:scalefactor[LS][0][i]=scalefactorstable[getbits(6)];
+ scalefactor[LS][1][i]=
+ scalefactor[LS][2][i]=scalefactorstable[getbits(6)];
+ break;
+ default:
+ cout << "scaleselector left default never happens"<<endl;
+ break;
+ }
+ }
+
+
+ if(inputstereo && (j=bitalloc[RS][i]))
+ {
+ if(!tableindex)
+ {
+ group[RS][i]=grouptableA[j];
+ codelength[RS][i]=codelengthtableA[j];
+ factor[RS][i]=factortableA[j];
+ c[RS][i]=ctableA[j];
+ d[RS][i]=dtableA[j];
+ }
+ else
+ {
+ if(i<=2)
+ {
+ group[RS][i]=grouptableB1[j];
+ codelength[RS][i]=codelengthtableB1[j];
+ factor[RS][i]=factortableB1[j];
+ c[RS][i]=ctableB1[j];
+ d[RS][i]=dtableB1[j];
+ }
+ else
+ {
+ group[RS][i]=grouptableB234[j];
+ if(i<=10)
+ {
+ codelength[RS][i]=codelengthtableB2[j];
+ factor[RS][i]=factortableB2[j];
+ c[RS][i]=ctableB2[j];
+ d[RS][i]=dtableB2[j];
+ }
+ else if(i<=22)
+ {
+ codelength[RS][i]=codelengthtableB3[j];
+ factor[RS][i]=factortableB3[j];
+ c[RS][i]=ctableB3[j];
+ d[RS][i]=dtableB3[j];
+ }
+ else
+ {
+ codelength[RS][i]=codelengthtableB4[j];
+ factor[RS][i]=factortableB4[j];
+ c[RS][i]=ctableB4[j];
+ d[RS][i]=dtableB4[j];
+ }
+ }
+ }
+
+
+ switch(scaleselector[RS][i])
+ {
+ case 0 : scalefactor[RS][0][i]=scalefactorstable[getbits(6)];
+ scalefactor[RS][1][i]=scalefactorstable[getbits(6)];
+ scalefactor[RS][2][i]=scalefactorstable[getbits(6)];
+ break;
+ case 1 : scalefactor[RS][0][i]=
+ scalefactor[RS][1][i]=scalefactorstable[getbits(6)];
+ scalefactor[RS][2][i]=scalefactorstable[getbits(6)];
+ break;
+ case 2 : scalefactor[RS][0][i]=
+ scalefactor[RS][1][i]=
+ scalefactor[RS][2][i]=scalefactorstable[getbits(6)];
+ break;
+ case 3 : scalefactor[RS][0][i]=scalefactorstable[getbits(6)];
+ scalefactor[RS][1][i]=
+ scalefactor[RS][2][i]=scalefactorstable[getbits(6)];
+ break;
+ default:
+ cout << "scaleselector right default never happens"<<endl;
+ break;
+ }
+
+
+ }
+ }
+ }
+
+
+
+// Read Sample
+ {
+ register int i;
+
+ for(int l=0;l<SCALEBLOCK;l++)
+ {
+ // Read Sample
+ for(i=0;i<s;i++)
+ {
+ if(bitalloc[LS][i])
+ {
+ if(group[LS][i])
+ {
+ register const REAL *s;
+ int code=getbits(codelength[LS][i]);
+
+
+
+ code+=code<<1;
+#ifdef BUGFIX
+ // bugfix for bad streams
+ code=checkCodeRange(code,group[LS][i]);
+ if (code == -1) return;
+#endif
+ s=group[LS][i]+code;
+
+ fraction[LS][0][i]=s[0];
+ fraction[LS][1][i]=s[1];
+ fraction[LS][2][i]=s[2];
+ }
+ else
+ {
+ fraction[LS][0][i]=
+ REAL(getbits(codelength[LS][i]))*factor[LS][i]-1.0;
+ fraction[LS][1][i]=
+ REAL(getbits(codelength[LS][i]))*factor[LS][i]-1.0;
+ fraction[LS][2][i]=
+ REAL(getbits(codelength[LS][i]))*factor[LS][i]-1.0;
+ }
+ }
+ else fraction[LS][0][i]=fraction[LS][1][i]=fraction[LS][2][i]=0.0;
+
+
+ if(inputstereo && bitalloc[RS][i])
+ {
+ if(group[RS][i])
+ {
+ const REAL *s;
+ int code=getbits(codelength[RS][i]);
+
+ code+=code<<1;
+#ifdef BUGFIX
+ // bugfix for bad streams
+ code=checkCodeRange(code,group[RS][i]);
+ if (code == -1) return;
+#endif
+ s=group[RS][i]+code;
+
+ fraction[RS][0][i]=s[0];
+ fraction[RS][1][i]=s[1];
+ fraction[RS][2][i]=s[2];
+ }
+ else
+ {
+ fraction[RS][0][i]=
+ REAL(getbits(codelength[RS][i]))*factor[RS][i]-1.0;
+ fraction[RS][1][i]=
+ REAL(getbits(codelength[RS][i]))*factor[RS][i]-1.0;
+ fraction[RS][2][i]=
+ REAL(getbits(codelength[RS][i]))*factor[RS][i]-1.0;
+ }
+ }
+ else fraction[RS][0][i]=fraction[RS][1][i]=fraction[RS][2][i]=0.0;
+ }
+
+
+ for(;i<n;i++)
+ {
+ if(bitalloc[LS][i])
+ {
+ if(group[LS][i])
+ {
+ register const REAL *s;
+ int code=getbits(codelength[LS][i]);
+
+ code+=code<<1;
+#ifdef BUGFIX
+ // bugfix for bad streams
+ code=checkCodeRange(code,group[LS][i]);
+ if (code == -1) return;
+#endif
+ s=group[LS][i]+code;
+
+ fraction[LS][0][i]=fraction[RS][0][i]=s[0];
+ fraction[LS][1][i]=fraction[RS][1][i]=s[1];
+ fraction[LS][2][i]=fraction[RS][2][i]=s[2];
+ }
+ else
+ {
+ fraction[LS][0][i]=fraction[RS][0][i]=
+ REAL(getbits(codelength[LS][i]))*factor[LS][i]-1.0;
+ fraction[LS][1][i]=fraction[RS][1][i]=
+ REAL(getbits(codelength[LS][i]))*factor[LS][i]-1.0;
+ fraction[LS][2][i]=fraction[RS][2][i]=
+ REAL(getbits(codelength[LS][i]))*factor[LS][i]-1.0;
+ }
+ }
+ else fraction[LS][0][i]=fraction[LS][1][i]=fraction[LS][2][i]=
+ fraction[RS][0][i]=fraction[RS][1][i]=fraction[RS][2][i]=0.0;
+ }
+
+
+
+
+ //Fraction
+ if(lOutputStereo)
+ for(i=0;i<n;i++)
+ {
+ if(bitalloc[LS][i])
+ {
+ if(!group[LS][i])
+ {
+ fraction[LS][0][i]=(fraction[LS][0][i]+d[LS][i])*c[LS][i];
+ fraction[LS][1][i]=(fraction[LS][1][i]+d[LS][i])*c[LS][i];
+ fraction[LS][2][i]=(fraction[LS][2][i]+d[LS][i])*c[LS][i];
+ }
+
+ register REAL t=scalefactor[LS][l>>2][i];
+ fraction[LS][0][i]*=t;
+ fraction[LS][1][i]*=t;
+ fraction[LS][2][i]*=t;
+ }
+
+ if(bitalloc[RS][i])
+ {
+ if(!group[RS][i])
+ {
+ fraction[RS][0][i]=(fraction[RS][0][i]+d[RS][i])*c[LS][i];
+ fraction[RS][1][i]=(fraction[RS][1][i]+d[RS][i])*c[LS][i];
+ fraction[RS][2][i]=(fraction[RS][2][i]+d[RS][i])*c[LS][i];
+ }
+
+ register REAL t=scalefactor[RS][l>>2][i];
+ fraction[RS][0][i]*=t;
+ fraction[RS][1][i]*=t;
+ fraction[RS][2][i]*=t;
+ }
+ }
+ else
+ for(i=0;i<n;i++)
+ if(bitalloc[LS][i])
+ {
+ if(!group[LS][i])
+ {
+ fraction[LS][0][i]=(fraction[LS][0][i]+d[LS][i])*c[LS][i];
+ fraction[LS][1][i]=(fraction[LS][1][i]+d[LS][i])*c[LS][i];
+ fraction[LS][2][i]=(fraction[LS][2][i]+d[LS][i])*c[LS][i];
+ }
+
+ register REAL t=scalefactor[LS][l>>2][i];
+ fraction[LS][0][i]*=t;
+ fraction[LS][1][i]*=t;
+ fraction[LS][2][i]*=t;
+ }
+
+
+ for(;i<MAXSUBBAND;i++)
+ fraction[LS][0][i]=fraction[LS][1][i]=fraction[LS][2][i]=
+ fraction[RS][0][i]=fraction[RS][1][i]=fraction[RS][2][i]=0.0;
+
+ for(i=0;i<3;i++) {
+ synthesis->doSynth(lDownSample,lOutputStereo,
+ fraction[LS][i],fraction[RS][i]);
+ }
+
+ }
+ }
+}
diff --git a/mpeglib/lib/splay/mpeglayer3.cpp b/mpeglib/lib/splay/mpeglayer3.cpp
new file mode 100644
index 00000000..eeb09697
--- /dev/null
+++ b/mpeglib/lib/splay/mpeglayer3.cpp
@@ -0,0 +1,1761 @@
+/* MPEG/WAVE Sound library
+
+ (C) 1997 by Jung woo-jae */
+
+// Mpeglayer3.cc
+// It's for MPEG Layer 3
+// I've made array of superior functions for speed.
+// Extend TO_FOUR_THIRDS to negative.
+// Bug fix : maplay 1.2+ have wrong TO_FOUR_THIRDS ranges.
+// Force to mono!!
+// MPEG-2 is implemented
+// Speed up in fixstereo (maybe buggy)
+
+
+
+#include "mpegsound.h"
+#include "huffmanlookup.h"
+#include "dump.h"
+#include "synthesis.h"
+
+inline int Mpegtoraw::wgetbit (void) {return bitwindow.getbit (); }
+inline int Mpegtoraw::wgetbits9(int bits){return bitwindow.getbits9(bits);}
+inline int Mpegtoraw::wgetbits (int bits){return bitwindow.getbits (bits);}
+inline int Mpegtoraw::wgetCanReadBits () {return bitwindow.getCanReadBits();}
+
+
+#define MUL3(a) (((a)<<1)+(a))
+
+#define REAL0 0
+
+// 576
+#define ARRAYSIZE (SBLIMIT*SSLIMIT)
+#define REALSIZE (sizeof(REAL))
+
+
+#define MAPLAY_OPT 1
+
+
+#ifdef NATIVE_ASSEMBLY
+inline void long_memset(void * s,unsigned int c,int count)
+{
+__asm__ __volatile__(
+ "cld\n\t"
+ "rep ; stosl\n\t"
+ : /* no output */
+ :"a" (c), "c" (count/4), "D" ((long) s)
+ :"cx","di","memory");
+}
+#endif
+
+#define FOURTHIRDSTABLENUMBER (8250)
+static int initializedlayer3=false;
+
+static REAL two_to_negative_half_pow[70];
+static REAL TO_FOUR_THIRDSTABLE[FOURTHIRDSTABLENUMBER*2];
+static REAL POW2[256];
+static REAL POW2_1[8][2][16];
+static REAL ca[8],cs[8];
+
+
+
+
+typedef struct
+{
+ REAL l,r;
+}RATIOS;
+
+static RATIOS rat_1[16],rat_2[2][64];
+
+void Mpegtoraw::layer3initialize(void)
+{
+
+ int i,j,k,l;
+
+ //maplay opt.
+ nonzero[0] = nonzero[1] = nonzero[2]=ARRAYSIZE;
+
+ layer3framestart=0;
+ currentprevblock=0;
+
+ for(l=0;l<2;l++)
+ for(i=0;i<2;i++)
+ for(j=0;j<SBLIMIT;j++)
+ for(k=0;k<SSLIMIT;k++)
+ prevblck[l][i][j][k]=0.0f;
+
+ bitwindow.initialize();
+
+ if(initializedlayer3) {
+ return;
+ }
+
+
+
+ for(i=0;i<256;i++) {
+ POW2[i]=(REAL)pow((double)2.0,(0.25* (double) (i-210.0)));
+ }
+ REAL *TO_FOUR_THIRDS=TO_FOUR_THIRDSTABLE+FOURTHIRDSTABLENUMBER;
+
+ for(i=1;i<FOURTHIRDSTABLENUMBER;i++)
+ TO_FOUR_THIRDS[-i]=
+ -(TO_FOUR_THIRDS[i]=(REAL)pow((double)i,(double)4.0/3.0));
+ // now set the zero value for both (otherwise it would be -0.0)
+ TO_FOUR_THIRDS[0]=(REAL)0;
+
+
+ for(i=0;i<8;i++) {
+ static double Ci[8]= {-0.6,-0.535,-0.33,-0.185,
+ -0.095,-0.041,-0.0142,-0.0037};
+ double sq=sqrt(1.0f+Ci[i]*Ci[i]);
+ cs[i]=1.0f/sq;
+ ca[i]=Ci[i]/sq;
+ }
+
+ initialize_win();
+ initialize_dct12_dct36();
+ for(i=0;i<70;i++) {
+ two_to_negative_half_pow[i]=(REAL)pow(2.0,-0.5*(double)i);
+ }
+
+
+ for(i=0;i<8;i++)
+ for(j=0;j<2;j++)
+ for(k=0;k<16;k++)POW2_1[i][j][k]=pow(2.0,(-2.0*i)-(0.5*(1.0+j)*k));
+
+
+ /*
+
+ for(i=0;i<8;i++)
+ for(k=0;k<16;k++) {
+ for(j=0;j<2;j++) {
+ REAL base=pow(2.0,-0.25*(j+1.0));
+ REAL val=1.0;
+
+ if (k>0) {
+ if ( k & 1) {
+ val=pow(base,(k+1.0)*0.5);
+ } else {
+ val=pow(base,k*0.5);
+ }
+ }
+
+ POW2_MV[i][j][k]=val;
+ }
+ }
+
+ for(i=0;i<8;i++)
+ for(j=0;j<2;j++)
+ for(k=0;k<16;k++) {
+ REAL a=POW2_1[i][j][k];
+ REAL b=POW2_MV[i][j][k];
+ printf("i:%d j%d k%d",i,j,k);
+ if (a != b) {
+ cout << "a:"<<a<<" b:"<<b<<endl;
+ } else {
+ cout << "same:"<<a<<endl;
+ }
+ }
+ */
+
+
+ for(i=0;i<16;i++) {
+ double t = tan( (double) i * MY_PI / 12.0 );
+ rat_1[i].l=t / (1.0+t);
+ rat_1[i].r=1.0 /(1.0+t);
+ }
+
+
+#define IO0 ((double)0.840896415256)
+#define IO1 ((double)0.707106781188)
+ rat_2[0][0].l=rat_2[0][0].r=
+ rat_2[1][0].l=rat_2[1][0].r=1.;
+
+ for(i=1;i<64;i++) {
+ if((i%2)==1) {
+ rat_2[0][i].l=pow(IO0,(i+1)/2);
+ rat_2[1][i].l=pow(IO1,(i+1)/2);
+ rat_2[0][i].r=
+ rat_2[1][i].r=1.;
+ }
+ else {
+ rat_2[0][i].l=
+ rat_2[1][i].l=1.;
+ rat_2[0][i].r=pow(IO0,i/2);
+ rat_2[1][i].r=pow(IO1,i/2);
+ }
+ }
+
+ initializedlayer3=true;
+}
+
+bool Mpegtoraw::layer3getsideinfo(void) {
+ int inputstereo=mpegAudioHeader->getInputstereo();
+
+
+ sideinfo.main_data_begin=getbits(9);
+ if(!inputstereo)sideinfo.private_bits=getbits(5);
+ else sideinfo.private_bits=getbits(3);
+
+ sideinfo.ch[LS].scfsi[0]=getbit();
+ sideinfo.ch[LS].scfsi[1]=getbit();
+ sideinfo.ch[LS].scfsi[2]=getbit();
+ sideinfo.ch[LS].scfsi[3]=getbit();
+ if(inputstereo) {
+ sideinfo.ch[RS].scfsi[0]=getbit();
+ sideinfo.ch[RS].scfsi[1]=getbit();
+ sideinfo.ch[RS].scfsi[2]=getbit();
+ sideinfo.ch[RS].scfsi[3]=getbit();
+ }
+
+ for(int gr=0,ch;gr<2;gr++)
+ for(ch=0;;ch++) {
+ layer3grinfo *gi=&(sideinfo.ch[ch].gr[gr]);
+
+ gi->part2_3_length =getbits(12);
+ gi->big_values =getbits(9);
+ if(gi->big_values > 288) {
+ DEBUG_LAYER(fprintf(stderr,"big_values too large!\n");)
+ gi->big_values = 288;
+ return false;
+ }
+
+ gi->global_gain =getbits(8);
+ gi->scalefac_compress =getbits(4);
+ gi->window_switching_flag=getbit();
+ if(gi->window_switching_flag) {
+ gi->block_type =getbits(2);
+ gi->mixed_block_flag=getbit();
+
+ gi->table_select[0] =getbits(5);
+ gi->table_select[1] =getbits(5);
+
+ gi->subblock_gain[0]=getbits(3);
+ gi->subblock_gain[1]=getbits(3);
+ gi->subblock_gain[2]=getbits(3);
+
+ /* Set region_count parameters since they are implicit in this case. */
+ if(gi->block_type==0)
+ {
+ DEBUG_LAYER(printf("Side info bad: block_type==0 split block.\n");)
+ return false;
+ }
+ else if (gi->block_type==2 && gi->mixed_block_flag==0)
+ gi->region0_count=8; /* MI 9; */
+ else gi->region0_count=7; /* MI 8; */
+ gi->region1_count=20-(gi->region0_count);
+ }
+ else
+ {
+ gi->table_select[0] =getbits(5);
+ gi->table_select[1] =getbits(5);
+ gi->table_select[2] =getbits(5);
+ gi->region0_count =getbits(4);
+ gi->region1_count =getbits(3);
+ gi->block_type =0;
+ }
+ gi->preflag =getbit();
+ gi->scalefac_scale =getbit();
+ gi->count1table_select=getbit();
+
+ gi->generalflag=gi->window_switching_flag && (gi->block_type==2);
+
+ if(!inputstereo || ch)break;
+ }
+
+ return true;
+}
+
+bool Mpegtoraw::layer3getsideinfo_2(void) {
+ int inputstereo=mpegAudioHeader->getInputstereo();
+ sideinfo.main_data_begin=getbits(8);
+
+ if(!inputstereo)sideinfo.private_bits=getbit();
+ else sideinfo.private_bits=getbits(2);
+
+ for(int ch=0;;ch++)
+ {
+ layer3grinfo *gi=&(sideinfo.ch[ch].gr[0]);
+
+ gi->part2_3_length =getbits(12);
+ gi->big_values =getbits(9);
+ if(gi->big_values > 288) {
+ DEBUG_LAYER(fprintf(stderr,"big_values too large!\n");)
+ gi->big_values = 288;
+ return false;
+ }
+
+ gi->global_gain =getbits(8);
+ gi->scalefac_compress =getbits(9);
+ gi->window_switching_flag=getbit();
+ if(gi->window_switching_flag)
+ {
+ gi->block_type =getbits(2);
+ gi->mixed_block_flag=getbit();
+
+ gi->table_select[0] =getbits(5);
+ gi->table_select[1] =getbits(5);
+
+ gi->subblock_gain[0]=getbits(3);
+ gi->subblock_gain[1]=getbits(3);
+ gi->subblock_gain[2]=getbits(3);
+
+ /* Set region_count parameters since they are implicit in this case. */
+ if(gi->block_type==0)
+ {
+ DEBUG_LAYER(printf("Side info bad: block_type==0 split block.\n");)
+ return false;
+ }
+ else if (gi->block_type==2 && gi->mixed_block_flag==0)
+ gi->region0_count=8; /* MI 9; */
+ else gi->region0_count=7; /* MI 8; */
+ gi->region1_count=20-(gi->region0_count);
+ }
+ else
+ {
+ gi->table_select[0] =getbits(5);
+ gi->table_select[1] =getbits(5);
+ gi->table_select[2] =getbits(5);
+ gi->region0_count =getbits(4);
+ gi->region1_count =getbits(3);
+ gi->block_type =0;
+ }
+ gi->scalefac_scale =getbit();
+ gi->count1table_select=getbit();
+
+ gi->generalflag=gi->window_switching_flag && (gi->block_type==2);
+
+ if(!inputstereo || ch)break;
+ }
+
+ return true;
+}
+
+void Mpegtoraw::layer3getscalefactors(int ch,int gr)
+{
+ static int slen[2][16]={{0, 0, 0, 0, 3, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4},
+ {0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 3}};
+
+ layer3grinfo *gi=&(sideinfo.ch[ch].gr[gr]);
+ register layer3scalefactor *sf=(&scalefactors[ch]);
+ int l0,l1;
+
+ {
+ int scale_comp=gi->scalefac_compress;
+
+ l0=slen[0][scale_comp];
+ l1=slen[1][scale_comp];
+ }
+ /*
+ wgetCanReadBits();
+ cout << "lo:"<<l0<<" l1:"<<l1<<endl;
+ */
+ if(gi->generalflag)
+ {
+ if(gi->mixed_block_flag)
+ { /* MIXED */ /* NEW-ag 11/25 */
+ sf->l[0]=wgetbits9(l0);sf->l[1]=wgetbits9(l0);
+ sf->l[2]=wgetbits9(l0);sf->l[3]=wgetbits9(l0);
+ sf->l[4]=wgetbits9(l0);sf->l[5]=wgetbits9(l0);
+ sf->l[6]=wgetbits9(l0);sf->l[7]=wgetbits9(l0);
+
+ sf->s[0][ 3]=wgetbits9(l0);sf->s[1][ 3]=wgetbits9(l0);
+ sf->s[2][ 3]=wgetbits9(l0);
+ sf->s[0][ 4]=wgetbits9(l0);sf->s[1][ 4]=wgetbits9(l0);
+ sf->s[2][ 4]=wgetbits9(l0);
+ sf->s[0][ 5]=wgetbits9(l0);sf->s[1][ 5]=wgetbits9(l0);
+ sf->s[2][ 5]=wgetbits9(l0);
+
+ sf->s[0][ 6]=wgetbits9(l1);sf->s[1][ 6]=wgetbits9(l1);
+ sf->s[2][ 6]=wgetbits9(l1);
+ sf->s[0][ 7]=wgetbits9(l1);sf->s[1][ 7]=wgetbits9(l1);
+ sf->s[2][ 7]=wgetbits9(l1);
+ sf->s[0][ 8]=wgetbits9(l1);sf->s[1][ 8]=wgetbits9(l1);
+ sf->s[2][ 8]=wgetbits9(l1);
+ sf->s[0][ 9]=wgetbits9(l1);sf->s[1][ 9]=wgetbits9(l1);
+ sf->s[2][ 9]=wgetbits9(l1);
+ sf->s[0][10]=wgetbits9(l1);sf->s[1][10]=wgetbits9(l1);
+ sf->s[2][10]=wgetbits9(l1);
+ sf->s[0][11]=wgetbits9(l1);sf->s[1][11]=wgetbits9(l1);
+ sf->s[2][11]=wgetbits9(l1);
+
+ sf->s[0][12]=sf->s[1][12]=sf->s[2][12]=0;
+ }
+ else
+ { /* SHORT*/
+ sf->s[0][ 0]=wgetbits9(l0);sf->s[1][ 0]=wgetbits9(l0);
+ sf->s[2][ 0]=wgetbits9(l0);
+ sf->s[0][ 1]=wgetbits9(l0);sf->s[1][ 1]=wgetbits9(l0);
+ sf->s[2][ 1]=wgetbits9(l0);
+ sf->s[0][ 2]=wgetbits9(l0);sf->s[1][ 2]=wgetbits9(l0);
+ sf->s[2][ 2]=wgetbits9(l0);
+ sf->s[0][ 3]=wgetbits9(l0);sf->s[1][ 3]=wgetbits9(l0);
+ sf->s[2][ 3]=wgetbits9(l0);
+ sf->s[0][ 4]=wgetbits9(l0);sf->s[1][ 4]=wgetbits9(l0);
+ sf->s[2][ 4]=wgetbits9(l0);
+ sf->s[0][ 5]=wgetbits9(l0);sf->s[1][ 5]=wgetbits9(l0);
+ sf->s[2][ 5]=wgetbits9(l0);
+
+ sf->s[0][ 6]=wgetbits9(l1);sf->s[1][ 6]=wgetbits9(l1);
+ sf->s[2][ 6]=wgetbits9(l1);
+ sf->s[0][ 7]=wgetbits9(l1);sf->s[1][ 7]=wgetbits9(l1);
+ sf->s[2][ 7]=wgetbits9(l1);
+ sf->s[0][ 8]=wgetbits9(l1);sf->s[1][ 8]=wgetbits9(l1);
+ sf->s[2][ 8]=wgetbits9(l1);
+ sf->s[0][ 9]=wgetbits9(l1);sf->s[1][ 9]=wgetbits9(l1);
+ sf->s[2][ 9]=wgetbits9(l1);
+ sf->s[0][10]=wgetbits9(l1);sf->s[1][10]=wgetbits9(l1);
+ sf->s[2][10]=wgetbits9(l1);
+ sf->s[0][11]=wgetbits9(l1);sf->s[1][11]=wgetbits9(l1);
+ sf->s[2][11]=wgetbits9(l1);
+
+ sf->s[0][12]=sf->s[1][12]=sf->s[2][12]=0;
+ }
+ }
+ else
+ { /* LONG types 0,1,3 */
+ if(gr==0)
+ {
+ sf->l[ 0]=wgetbits9(l0);sf->l[ 1]=wgetbits9(l0);
+ sf->l[ 2]=wgetbits9(l0);sf->l[ 3]=wgetbits9(l0);
+ sf->l[ 4]=wgetbits9(l0);sf->l[ 5]=wgetbits9(l0);
+ sf->l[ 6]=wgetbits9(l0);sf->l[ 7]=wgetbits9(l0);
+ sf->l[ 8]=wgetbits9(l0);sf->l[ 9]=wgetbits9(l0);
+ sf->l[10]=wgetbits9(l0);
+ sf->l[11]=wgetbits9(l1);sf->l[12]=wgetbits9(l1);
+ sf->l[13]=wgetbits9(l1);sf->l[14]=wgetbits9(l1);
+ sf->l[15]=wgetbits9(l1);
+ sf->l[16]=wgetbits9(l1);sf->l[17]=wgetbits9(l1);
+ sf->l[18]=wgetbits9(l1);sf->l[19]=wgetbits9(l1);
+ sf->l[20]=wgetbits9(l1);
+ }
+ else
+ {
+ if(sideinfo.ch[ch].scfsi[0]==0)
+ {
+ sf->l[ 0]=wgetbits9(l0);sf->l[ 1]=wgetbits9(l0);
+ sf->l[ 2]=wgetbits9(l0);sf->l[ 3]=wgetbits9(l0);
+ sf->l[ 4]=wgetbits9(l0);sf->l[ 5]=wgetbits9(l0);
+ }
+ if(sideinfo.ch[ch].scfsi[1]==0)
+ {
+ sf->l[ 6]=wgetbits9(l0);sf->l[ 7]=wgetbits9(l0);
+ sf->l[ 8]=wgetbits9(l0);sf->l[ 9]=wgetbits9(l0);
+ sf->l[10]=wgetbits9(l0);
+ }
+ if(sideinfo.ch[ch].scfsi[2]==0)
+ {
+ sf->l[11]=wgetbits9(l1);sf->l[12]=wgetbits9(l1);
+ sf->l[13]=wgetbits9(l1);sf->l[14]=wgetbits9(l1);
+ sf->l[15]=wgetbits9(l1);
+ }
+ if(sideinfo.ch[ch].scfsi[3]==0)
+ {
+ sf->l[16]=wgetbits9(l1);sf->l[17]=wgetbits9(l1);
+ sf->l[18]=wgetbits9(l1);sf->l[19]=wgetbits9(l1);
+ sf->l[20]=wgetbits9(l1);
+ }
+ }
+ sf->l[21]=sf->l[22]=0;
+ }
+ /*
+ cout << "end parse:"<<endl;
+ wgetCanReadBits();
+ */
+}
+
+void Mpegtoraw::layer3getscalefactors_2(int ch)
+{
+ static int sfbblockindex[6][3][4]=
+ {
+ {{ 6, 5, 5, 5},{ 9, 9, 9, 9},{ 6, 9, 9, 9}},
+ {{ 6, 5, 7, 3},{ 9, 9,12, 6},{ 6, 9,12, 6}},
+ {{11,10, 0, 0},{18,18, 0, 0},{15,18, 0, 0}},
+ {{ 7, 7, 7, 0},{12,12,12, 0},{ 6,15,12, 0}},
+ {{ 6, 6, 6, 3},{12, 9, 9, 6},{ 6,12, 9, 6}},
+ {{ 8, 8, 5, 0},{15,12, 9, 0},{ 6,18, 9, 0}}
+ };
+
+ int sb[54];
+ int extendedmode=mpegAudioHeader->getExtendedmode();
+ layer3grinfo *gi=&(sideinfo.ch[ch].gr[0]);
+ register layer3scalefactor *sf=(&scalefactors[ch]);
+
+ {
+ int blocktypenumber,sc;
+ int blocknumber;
+ int slen[4];
+
+ if(gi->block_type==2)blocktypenumber=1+gi->mixed_block_flag;
+ else blocktypenumber=0;
+
+ sc=gi->scalefac_compress;
+ if(!((extendedmode==1 || extendedmode==3) && (ch==1)))
+ {
+ if(sc<400)
+ {
+ slen[0]=(sc>>4)/5;
+ slen[1]=(sc>>4)%5;
+ slen[2]=(sc%16)>>2;
+ slen[3]=(sc%4);
+ gi->preflag=0;
+ blocknumber=0;
+ }
+ else if(sc<500)
+ {
+ sc-=400;
+ slen[0]=(sc>>2)/5;
+ slen[1]=(sc>>2)%5;
+ slen[2]=sc%4;
+ slen[3]=0;
+ gi->preflag=0;
+ blocknumber=1;
+ }
+ else // if(sc<512)
+ {
+ sc-=500;
+ slen[0]=sc/3;
+ slen[1]=sc%3;
+ slen[2]=0;
+ slen[3]=0;
+ gi->preflag=1;
+ blocknumber=2;
+ }
+ }
+ else
+ {
+ sc>>=1;
+ if(sc<180)
+ {
+ slen[0]=sc/36;
+ slen[1]=(sc%36)/6;
+ slen[2]=(sc%36)%6;
+ slen[3]=0;
+ gi->preflag=0;
+ blocknumber=3;
+ }
+ else if(sc<244)
+ {
+ sc-=180;
+ slen[0]=(sc%64)>>4;
+ slen[1]=(sc%16)>>2;
+ slen[2]=sc%4;
+ slen[3]=0;
+ gi->preflag=0;
+ blocknumber=4;
+ }
+ else // if(sc<255)
+ {
+ sc-=244;
+ slen[0]=sc/3;
+ slen[1]=sc%3;
+ slen[2]=
+ slen[3]=0;
+ gi->preflag=0;
+ blocknumber=5;
+ }
+ }
+
+ {
+ int i,j,k,*si;
+
+ si=sfbblockindex[blocknumber][blocktypenumber];
+ for(i=0;i<45;i++)sb[i]=0;
+
+ for(k=i=0;i<4;i++)
+ for(j=0;j<si[i];j++,k++)
+ if(slen[i]==0)sb[k]=0;
+ else sb[k]=wgetbits(slen[i]);
+ }
+ }
+
+
+ {
+ int sfb,window;
+ int k=0;
+
+ if(gi->window_switching_flag && (gi->block_type==2))
+ {
+ if(gi->mixed_block_flag)
+ {
+ for(sfb=0;sfb<8;sfb++)sf->l[sfb]=sb[k++];
+ sfb=3;
+ }
+ else sfb=0;
+
+ for(;sfb<12;sfb++)
+ for(window=0;window<3;window++)
+ sf->s[window][sfb]=sb[k++];
+
+ sf->s[0][12]=sf->s[1][12]=sf->s[2][12]=0;
+ }
+ else
+ {
+ for(sfb=0;sfb<21;sfb++)
+ sf->l[sfb]=sb[k++];
+ sf->l[21]=sf->l[22]=0;
+ }
+ }
+}
+
+
+typedef unsigned int HUFFBITS;
+#define MXOFF 250
+
+/* do the huffman-decoding */
+/* note! for counta,countb -the 4 bit value is returned in y, discard x */
+// Huffman decoder for tablename<32
+inline void Mpegtoraw::huffmandecoder_1(const HUFFMANCODETABLE *h,int *x,int *y)
+{
+ HUFFBITS level=(1<<(sizeof(HUFFBITS)*8-1));
+ int point=0;
+
+ /* Lookup in Huffman table. */
+ for(;;)
+ {
+ if(h->val[point][0]==0)
+ { /*end of tree*/
+ int xx,yy;
+
+ xx=h->val[point][1]>>4;
+ yy=h->val[point][1]&0xf;
+
+ if(h->linbits)
+ {
+ if((h->xlen)==(unsigned)xx)xx+=wgetbits(h->linbits);
+ if(xx)if(wgetbit())xx=-xx;
+ if((h->ylen)==(unsigned)yy)yy+=wgetbits(h->linbits);
+ if(yy)if(wgetbit())yy=-yy;
+ }
+ else
+ {
+ if(xx)if(wgetbit())xx=-xx;
+ if(yy)if(wgetbit())yy=-yy;
+ }
+ *x=xx;*y=yy;
+ break;
+ }
+
+ point+=h->val[point][wgetbit()];
+
+ level>>=1;
+ if(!(level || ((unsigned)point<ht->treelen)))
+ {
+ register int xx,yy;
+
+ xx=(h->xlen<<1);// set x and y to a medium value as a simple concealment
+ yy=(h->ylen<<1);
+
+ // h->xlen and h->ylen can't be 1 under tablename 32
+ // if(xx)
+ if(wgetbit())xx=-xx;
+ // if(yy)
+ if(wgetbit())yy=-yy;
+
+ *x=xx;*y=yy;
+ break;
+ }
+ }
+}
+
+// Huffman decoder tablenumber>=32
+inline void Mpegtoraw::huffmandecoder_2(const HUFFMANCODETABLE *h,
+ int *x,int *y,int *v,int *w)
+{
+ HUFFBITS level=(1<<(sizeof(HUFFBITS)*8-1));
+ int point=0;
+
+ /* Lookup in Huffman table. */
+ for(;;)
+ {
+ if(h->val[point][0]==0)
+ { /*end of tree*/
+ register int t=h->val[point][1];
+
+ if(t&8)*v=1-(wgetbit()<<1); else *v=0;
+ if(t&4)*w=1-(wgetbit()<<1); else *w=0;
+ if(t&2)*x=1-(wgetbit()<<1); else *x=0;
+ if(t&1)*y=1-(wgetbit()<<1); else *y=0;
+ break;
+ }
+ point+=h->val[point][wgetbit()];
+ level>>=1;
+ if(!(level || ((unsigned)point<ht->treelen)))
+ {
+ *v=1-(wgetbit()<<1);
+ *w=1-(wgetbit()<<1);
+ *x=1-(wgetbit()<<1);
+ *y=1-(wgetbit()<<1);
+ break;
+ }
+ }
+}
+
+typedef struct
+{
+ int l[23];
+ int s[14];
+}SFBANDINDEX;
+
+static SFBANDINDEX sfBandIndextable[3][3]=
+{
+ // MPEG 1
+ {{{0,4,8,12,16,20,24,30,36,44,52,62,74,90,110,134,162,196,238,288,342,418,576},
+ {0,4,8,12,16,22,30,40,52,66,84,106,136,192}},
+ {{0,4,8,12,16,20,24,30,36,42,50,60,72,88,106,128,156,190,230,276,330,384,576},
+ {0,4,8,12,16,22,28,38,50,64,80,100,126,192}},
+ {{0,4,8,12,16,20,24,30,36,44,54,66,82,102,126,156,194,240,296,364,448,550,576},
+ {0,4,8,12,16,22,30,42,58,78,104,138,180,192}}},
+
+ // MPEG 2
+ {{{0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576},
+ {0,4,8,12,18,24,32,42,56,74,100,132,174,192}},
+ {{0,6,12,18,24,30,36,44,54,66,80,96,114,136,162,194,232,278,330,394,464,540,576},
+ {0,4,8,12,18,26,36,48,62,80,104,136,180,192}},
+ {{0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576},
+ {0,4,8,12,18,26,36,48,62,80,104,134,174,192}}},
+ // MPEG 2.5
+ {{{0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576},
+ {0,4,8,12,18,26,36,48,62,80,104,134,174,192}},
+ {{0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576},
+ {0,4,8,12,18,26,36,48,62,80,104,134,174,192}},
+ {{0,12,24,36,48,60,72,88,108,132,160,192,232,280,336,400,476,566,568,570,572,574,576},
+ {0,8,16,24,36,52,72,96,124,160,162,164,166,192}}}
+};
+
+
+void Mpegtoraw::layer3huffmandecode(int ch,int gr,int out[SBLIMIT][SSLIMIT])
+{
+ layer3grinfo *gi=&(sideinfo.ch[ch].gr[gr]);
+ int part2_3_end=layer3part2start+(gi->part2_3_length);
+ int region1Start,region2Start;
+ int i,e=gi->big_values<<1;
+ int version=mpegAudioHeader->getVersion();
+ int frequency=mpegAudioHeader->getFrequency();
+ int mpeg25=mpegAudioHeader->getLayer25();
+
+ /* Find region boundary for short block case. */
+ if(gi->generalflag) {
+ /* Region2. */
+ region1Start=
+ sfBandIndextable[mpeg25?2:version][frequency].s[3]*3;
+ /* MPEG1:sfb[9/3]*3=36 */
+ region2Start=576;/* No Region2 for short block case. */
+ } else {
+ /* Find region boundary for long block case. */
+ region1Start=
+ sfBandIndextable[mpeg25?2:version][frequency].l[gi->region0_count+1];
+ region2Start=
+ sfBandIndextable[mpeg25?2:version][frequency].l[gi->region0_count+
+ gi->region1_count+2];
+ }
+
+ /* Read bigvalues area. */
+ for(i=0;i<e;)
+ {
+ const HUFFMANCODETABLE *h;
+ register int end;
+
+ if (i<region1Start)
+ {
+ h=&ht[gi->table_select[0]];
+ if(region1Start>e)end=e; else end=region1Start;
+ }
+ else if(i<region2Start)
+ {
+ h=&ht[gi->table_select[1]];
+ if(region2Start>e)end=e; else end=region2Start;
+ }
+ else
+ {
+ h=&ht[gi->table_select[2]];
+ end=e;
+ }
+
+ if(h->treelen) {
+ while(i<end)
+ {
+
+ int skip = HuffmanLookup::decode(h->tablename, bitwindow.peek8(),
+ &out[0][i], &out[0][i+1]);
+
+ if(skip)
+ bitwindow.forward(skip);
+ else
+ huffmandecoder_1(h,&out[0][i],&out[0][i+1]);
+ i+=2;
+ }
+ } else {
+ for(;i<end;i+=2)
+ out[0][i] =
+ out[0][i+1]=0;
+ }
+ }
+
+ /* Read count1 area. */
+ const HUFFMANCODETABLE *h=&ht[gi->count1table_select+32];
+ while(bitwindow.gettotalbit()<part2_3_end)
+ {
+ huffmandecoder_2(h,&out[0][i+2],&out[0][i+3],
+ &out[0][i ],&out[0][i+1]);
+ i+=4;
+
+ if(i>=ARRAYSIZE)
+ {
+ break;
+ }
+ }
+
+ // nonzero is the _size_ of the array with the last nonzero value
+ if (i < ARRAYSIZE) {
+ nonzero[ch] = i;
+ } else {
+ // catch bugs
+ nonzero[ch] = ARRAYSIZE;
+ }
+
+ // debug start
+#ifndef MAPLAY_OPT
+ nonzero[ch]=ARRAYSIZE;
+ for(;i<ARRAYSIZE;i++)out[0][i]=0;
+#endif
+ // debug end
+
+ bitwindow.rewind(bitwindow.gettotalbit()-part2_3_end);
+}
+
+
+static int pretab[22]={0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,2,2,3,3,3,2,0};
+
+inline REAL Mpegtoraw::layer3twopow2(int scale,int preflag,
+ int pretab_offset,int l)
+{
+ int index=l;
+
+ if(preflag)index+=pretab_offset;
+
+ return(two_to_negative_half_pow[index<<scale]);
+}
+
+inline REAL Mpegtoraw::layer3twopow2_1(int a,int b,int c)
+{
+ return POW2_1[a][b][c];
+}
+
+
+void Mpegtoraw::layer3dequantizesample(int ch,int gr,
+ int in[SBLIMIT][SSLIMIT],
+ REAL out[SBLIMIT][SSLIMIT])
+{
+ int version=mpegAudioHeader->getVersion();
+ int frequency=mpegAudioHeader->getFrequency();
+ int mpeg25=mpegAudioHeader->getLayer25();
+ layer3grinfo *gi=&(sideinfo.ch[ch].gr[gr]);
+ SFBANDINDEX *sfBandIndex=&(sfBandIndextable[mpeg25?2:version][frequency]);
+ REAL globalgain=POW2[gi->global_gain];
+ REAL *TO_FOUR_THIRDS=TO_FOUR_THIRDSTABLE+FOURTHIRDSTABLENUMBER;
+ int arrayEnd=nonzero[ch];
+ /* choose correct scalefactor band per block type, initialize boundary */
+ /* and apply formula per block type */
+ if(!gi->generalflag) {
+ /* LONG blocks: 0,1,3 */
+ int next_cb_boundary;
+ int cb=-1,index=0;
+ REAL factor;
+
+
+ do
+ {
+
+ next_cb_boundary=sfBandIndex->l[(++cb)+1];
+ REAL val=layer3twopow2(gi->scalefac_scale,gi->preflag,
+ pretab[cb],scalefactors[ch].l[cb]);
+ factor=globalgain*val;
+ // maplay opt
+ if (arrayEnd < next_cb_boundary) {
+ next_cb_boundary=arrayEnd;
+ }
+
+ for(;index<next_cb_boundary;)
+ {
+ out[0][index]=factor*TO_FOUR_THIRDS[in[0][index]];index++;
+ out[0][index]=factor*TO_FOUR_THIRDS[in[0][index]];index++;
+ }
+
+ }while(index<arrayEnd);
+ } else if(!gi->mixed_block_flag) {
+ int cb=0,index=0;
+ int cb_width;
+ do
+ {
+ cb_width=(sfBandIndex->s[cb+1]-sfBandIndex->s[cb])>>1;
+
+ for(register int k=0;k<3;k++)
+ {
+ register REAL factor;
+ register int count=cb_width;
+ // maplay12 opt.
+ if(index+(count<<1) > arrayEnd) {
+ if (index >= arrayEnd) break;
+ count=(arrayEnd-index)>>1;
+ }
+
+ factor=globalgain*
+ layer3twopow2_1(gi->subblock_gain[k],gi->scalefac_scale,
+ scalefactors[ch].s[k][cb]);
+
+
+ do{
+ out[0][index]=factor*TO_FOUR_THIRDS[in[0][index]];index++;
+ out[0][index]=factor*TO_FOUR_THIRDS[in[0][index]];index++;
+ }while(--count);
+ }
+ cb++;
+ }while(index<arrayEnd);
+ } else {
+ int cb_begin=0,cb_width=0;
+ int cb=0;
+ int next_cb_boundary=sfBandIndex->l[1]; /* LONG blocks: 0,1,3 */
+ int index;
+ // I do not have an mp3 with this format,
+ // so we restore the "make rest of array zero"
+ // in this case
+ // to use the maplay opt here, we must make sure, that
+ // arrayEnd==ArraySize.
+ for(int i=arrayEnd;i<ARRAYSIZE;i++)in[0][i]=0;
+
+ /* Compute overall (global) scaling. */
+ {
+ for(int sb=0;sb<SBLIMIT;sb++)
+ {
+ int *i=in[sb];
+ REAL *o=out[sb];
+
+ o[ 0]=globalgain*TO_FOUR_THIRDS[i[ 0]];
+ o[ 1]=globalgain*TO_FOUR_THIRDS[i[ 1]];
+ o[ 2]=globalgain*TO_FOUR_THIRDS[i[ 2]];
+ o[ 3]=globalgain*TO_FOUR_THIRDS[i[ 3]];
+ o[ 4]=globalgain*TO_FOUR_THIRDS[i[ 4]];
+ o[ 5]=globalgain*TO_FOUR_THIRDS[i[ 5]];
+ o[ 6]=globalgain*TO_FOUR_THIRDS[i[ 6]];
+ o[ 7]=globalgain*TO_FOUR_THIRDS[i[ 7]];
+ o[ 8]=globalgain*TO_FOUR_THIRDS[i[ 8]];
+ o[ 9]=globalgain*TO_FOUR_THIRDS[i[ 9]];
+ o[10]=globalgain*TO_FOUR_THIRDS[i[10]];
+ o[11]=globalgain*TO_FOUR_THIRDS[i[11]];
+ o[12]=globalgain*TO_FOUR_THIRDS[i[12]];
+ o[13]=globalgain*TO_FOUR_THIRDS[i[13]];
+ o[14]=globalgain*TO_FOUR_THIRDS[i[14]];
+ o[15]=globalgain*TO_FOUR_THIRDS[i[15]];
+ o[16]=globalgain*TO_FOUR_THIRDS[i[16]];
+ o[17]=globalgain*TO_FOUR_THIRDS[i[17]];
+ }
+ }
+ for(index=0;index<SSLIMIT*2;index++)
+ {
+ if(index==next_cb_boundary)
+ {
+ if(index==sfBandIndex->l[8])
+ {
+ next_cb_boundary=sfBandIndex->s[4];
+ next_cb_boundary=MUL3(next_cb_boundary);
+ cb=3;
+ cb_width=sfBandIndex->s[4]-sfBandIndex->s[3];
+ cb_begin=sfBandIndex->s[3];
+ cb_begin=MUL3(cb_begin);
+ }
+ else if(index<sfBandIndex->l[8])
+ next_cb_boundary=sfBandIndex->l[(++cb)+1];
+ else
+ {
+ next_cb_boundary=sfBandIndex->s[(++cb)+1];
+ next_cb_boundary=MUL3(next_cb_boundary);
+ cb_begin=sfBandIndex->s[cb];
+ cb_width=sfBandIndex->s[cb+1]-cb_begin;
+ cb_begin=MUL3(cb_begin);
+ }
+ }
+ /* LONG block types 0,1,3 & 1st 2 subbands of switched blocks */
+ out[0][index]*=layer3twopow2(gi->scalefac_scale,gi->preflag,
+ pretab[cb],scalefactors[ch].l[cb]);
+
+ }
+
+ for(;index<ARRAYSIZE;index++) {
+ if(index==next_cb_boundary) {
+ if(index==sfBandIndex->l[8]) {
+ next_cb_boundary=sfBandIndex->s[4];
+ next_cb_boundary=MUL3(next_cb_boundary);
+ cb=3;
+ cb_width=sfBandIndex->s[4]-sfBandIndex->s[3];
+ cb_begin=sfBandIndex->s[3];
+ cb_begin=(cb_begin<<2)-cb_begin;
+ } else if(index<sfBandIndex->l[8])
+ next_cb_boundary=sfBandIndex->l[(++cb)+1];
+ else {
+ next_cb_boundary=sfBandIndex->s[(++cb)+1];
+ next_cb_boundary=MUL3(next_cb_boundary);
+ cb_begin=sfBandIndex->s[cb];
+ cb_width=sfBandIndex->s[cb+1]-cb_begin;
+ cb_begin=MUL3(cb_begin);
+ }
+ }
+ {
+ /**
+ Here we check if we do a division by zero
+ and if the resulting t_index points
+ outside the array. (Needed for better robustness
+ of the mp3 decoder)
+ */
+ unsigned int t_index=0;
+ if (cb_width) {
+ t_index=(unsigned int)((index-cb_begin)/cb_width);
+ if (t_index > 2) {
+ t_index=0;
+ }
+ }
+
+ out[0][index]*=layer3twopow2_1(gi->subblock_gain[t_index],
+ gi->scalefac_scale,
+ scalefactors[ch].s[t_index][cb]);
+ }
+ }
+ }
+ /*
+ int i;
+ for(i=arrayEnd;i<ARRAYSIZE;i++) {
+ out[0][i]=(REAL) 0.0;
+ }
+ */
+}
+// make the input to nonzero[2] zero
+inline void Mpegtoraw::adjustNonZero(REAL in[2][SBLIMIT][SSLIMIT]) {
+ if ((nonzero[0] == 0) && (nonzero[1]==0)) {
+ in[RS][0][0]=(REAL) 0.0;
+ in[LS][0][0]=(REAL) 0.0;
+ nonzero[0]=1;
+ nonzero[1]=1;
+ nonzero[2]=1;
+ return;
+ }
+
+ while(nonzero[0] > nonzero[1]) {
+ in[RS][0][nonzero[1]]=(REAL) 0.0;
+ nonzero[1]++;
+ }
+ while(nonzero[1] > nonzero[0]) {
+ in[LS][0][nonzero[0]]=(REAL) 0.0;
+ nonzero[0]++;
+ }
+ // now they are the same
+ // put this into the "max" var.
+ nonzero[2]=nonzero[1];
+
+}
+
+
+inline void Mpegtoraw::layer3fixtostereo(int gr,REAL in[2][SBLIMIT][SSLIMIT])
+{
+ int version=mpegAudioHeader->getVersion();
+ int frequency=mpegAudioHeader->getFrequency();
+ int extendedmode=mpegAudioHeader->getExtendedmode();
+ int mode=mpegAudioHeader->getMode();
+ int inputstereo=mpegAudioHeader->getInputstereo();
+ int mpeg25=mpegAudioHeader->getLayer25();
+ layer3grinfo *gi=&(sideinfo.ch[0].gr[gr]);
+ SFBANDINDEX *sfBandIndex=&(sfBandIndextable[mpeg25?2:version][frequency]);
+
+ int ms_stereo=(mode==_MODE_JOINT) && (extendedmode & 0x2);
+ int i_stereo =(mode==_MODE_JOINT) && (extendedmode & 0x1);
+
+
+ if(!inputstereo)
+ { /* mono , bypass xr[0][][] to lr[0][][]*/
+ // memcpy(out[0][0],in[0][0],ARRAYSIZE*REALSIZE);
+ for(int i=nonzero[0];i<ARRAYSIZE;i++) {
+ in[LS][0][i]=(REAL) 0.0;
+ }
+ return;
+ }
+ // maplay opt.
+ adjustNonZero(in);
+ int maxArray=nonzero[2];
+
+ if(i_stereo)
+ {
+
+ // not maplay optimised make defaults
+ int i;
+ for(i=maxArray;i<ARRAYSIZE;i++) {
+ in[LS][0][i]=in[RS][0][i]=(REAL) 0.0;
+ }
+
+
+ int is_pos[ARRAYSIZE];
+ RATIOS is_ratio[ARRAYSIZE];
+ RATIOS *ratios;
+ if(version)ratios=rat_2[gi->scalefac_compress%2];
+ else ratios=rat_1;
+
+ /* initialization */
+ for(i=0;i<ARRAYSIZE;i+=2)is_pos[i]=is_pos[i+1]=7;
+
+ if(gi->generalflag)
+ {
+ if(gi->mixed_block_flag) // Part I
+ {
+ int max_sfb=0;
+
+ for(int j=0;j<3;j++)
+ {
+ int sfb,sfbcnt=2;
+
+ for(sfb=12;sfb>=3;sfb--)
+ {
+ int lines;
+
+ i=sfBandIndex->s[sfb];
+ lines=sfBandIndex->s[sfb+1]-i;
+ i=MUL3(i)+(j+1)*lines-1;
+ for(;lines>0;lines--,i--)
+ if(in[1][0][i]!=0.0f)
+ {
+ sfbcnt=sfb;
+ sfb=0;break; // quit loop
+ }
+ }
+ sfb=sfbcnt+1;
+
+ if(sfb>max_sfb)max_sfb=sfb;
+
+ for(;sfb<12;sfb++)
+ {
+ int k,t;
+
+ t=sfBandIndex->s[sfb];
+ k=sfBandIndex->s[sfb+1]-t;
+ i=MUL3(t)+j*k;
+
+ t=scalefactors[1].s[j][sfb];
+ if(t!=7)
+ {
+ RATIOS r=ratios[t];
+
+ for(;k>0;k--,i++){
+ is_pos[i]=t;is_ratio[i]=r;}
+ }
+ else
+ for(;k>0;k--,i++)is_pos[i]=t;
+ }
+ sfb=sfBandIndex->s[10];
+ sfb=MUL3(sfb)+j*(sfBandIndex->s[11]-sfb);
+
+ {
+ int k,t;
+
+ t=sfBandIndex->s[11];
+ k=sfBandIndex->s[12]-t;
+ i=MUL3(t)+j*k;
+
+ t=is_pos[sfb];
+ if(t!=7)
+ {
+ RATIOS r=is_ratio[sfb];
+
+ for(;k>0;k--,i++){
+ is_pos[i]=t;is_ratio[i]=r;}
+ }
+ else
+ for(;k>0;k--,i++)is_pos[i]=t;
+ }
+ }
+
+ if(max_sfb<=3)
+ {
+ {
+ REAL temp;
+ int k;
+
+ temp=in[1][0][0];in[1][0][0]=1.0;
+ for(k=3*SSLIMIT-1;in[1][0][k]==0.0;k--);
+ in[1][0][0]=temp;
+ for(i=0;sfBandIndex->l[i]<=k;i++);
+ }
+ {
+ int sfb=i;
+
+ i=sfBandIndex->l[i];
+ for(;sfb<8;sfb++)
+ {
+ int t=scalefactors[1].l[sfb];
+ int k=sfBandIndex->l[sfb+1]-sfBandIndex->l[sfb];
+
+ if(t!=7)
+ {
+ RATIOS r=ratios[t];
+
+ for(;k>0;k--,i++){
+ is_pos[i]=t;is_ratio[i]=r;}
+ }
+ else for(;k>0;k--,i++)is_pos[i]=t;
+ }
+ }
+ }
+ }
+ else // Part II
+ {
+ for(int j=0;j<3;j++)
+ {
+ int sfbcnt=-1;
+ int sfb;
+ for(sfb=12;sfb>=0;sfb--)
+ {
+ int lines;
+
+ {
+ int t;
+
+ t=sfBandIndex->s[sfb];
+ lines=sfBandIndex->s[sfb+1]-t;
+ i=MUL3(t)+(j+1)*lines-1;
+ }
+
+ for(;lines>0;lines--,i--)
+ if(in[1][0][i]!=0.0f)
+ {
+ sfbcnt=sfb;
+ sfb=0;break; // quit loop
+ }
+ }
+
+ for(sfb=sfbcnt+1;sfb<12;sfb++)
+ {
+ int k,t;
+
+ t=sfBandIndex->s[sfb];
+ k=sfBandIndex->s[sfb+1]-t;
+ i=MUL3(t)+j*k;
+
+ t=scalefactors[1].s[j][sfb];
+ if(t!=7)
+ {
+ RATIOS r=ratios[t];
+
+ for(;k>0;k--,i++){
+ is_pos[i]=t;is_ratio[i]=r;}
+ }
+ else for(;k>0;k--,i++)is_pos[i]=t;
+ }
+
+ {
+ int t1=sfBandIndex->s[10],
+ t2=sfBandIndex->s[11];
+ int k,tt;
+
+ tt=MUL3(t1)+j*(t2-t1);
+ k =sfBandIndex->s[12]-t2;
+ if(is_pos[tt]!=7)
+ {
+ RATIOS r=is_ratio[tt];
+ int t=is_pos[tt];
+
+ i =MUL3(t1)+j*k;
+ for(;k>0;k--,i++){
+ is_pos[i]=t;is_ratio[i]=r;}
+ }
+ else
+ for(;k>0;k--,i++)is_pos[i]=7;
+ }
+ }
+ }
+ }
+ else // ms-stereo (Part III)
+ {
+ {
+ REAL temp;
+ int k;
+
+ temp=in[1][0][0];in[1][0][0]=1.0;
+ for(k=ARRAYSIZE-1;in[1][0][k]==0.0;k--);
+ in[1][0][0]=temp;
+ for(i=0;sfBandIndex->l[i]<=k;i++);
+ }
+
+ {
+ int sfb;
+
+ sfb=i;
+ i=sfBandIndex->l[i];
+ for(;sfb<21;sfb++)
+ {
+ int k,t;
+
+ k=sfBandIndex->l[sfb+1]-sfBandIndex->l[sfb];
+ t=scalefactors[1].l[sfb];
+ if(t!=7)
+ {
+ RATIOS r=ratios[t];
+
+ for(;k>0;k--,i++){
+ is_pos[i]=t;is_ratio[i]=r;}
+ }
+ else
+ for(;k>0;k--,i++)is_pos[i]=t;
+ }
+ }
+
+ {
+ int k,t,tt;
+
+ tt=sfBandIndex->l[20];
+ k=576-sfBandIndex->l[21];
+ t=is_pos[tt];
+ if(t!=7)
+ {
+ RATIOS r=is_ratio[tt];
+
+ for(;k>0;k--,i++){
+ is_pos[i]=t;is_ratio[i]=r;}
+ }
+ else
+ for(;k>0;k--,i++)is_pos[i]=t;
+ }
+ }
+
+ if(ms_stereo)
+ {
+ i=ARRAYSIZE-1;
+ do{
+ if(is_pos[i]==7)
+ {
+ register REAL t=in[LS][0][i];
+ in[LS][0][i]=(t+in[RS][0][i])*0.7071068f;
+ in[RS][0][i]=(t-in[RS][0][i])*0.7071068f;
+ }
+ else
+ {
+ in[RS][0][i]=in[LS][0][i]*is_ratio[i].r;
+ in[LS][0][i]*=is_ratio[i].l;
+ }
+ }while(i--);
+ }
+ else
+ {
+ i=ARRAYSIZE-1;
+ do{
+ if(is_pos[i]!=7)
+ {
+ in[RS][0][i]=in[LS][0][i]*is_ratio[i].r;
+ in[LS][0][i]*=is_ratio[i].l;
+ }
+ }while(i--);
+ }
+ }
+ else
+ {
+
+ if(ms_stereo)
+ {
+ int i=maxArray-1;
+ do{
+ register REAL t=in[LS][0][i];
+
+ in[LS][0][i]=(t+in[RS][0][i])*0.7071068f;
+ in[RS][0][i]=(t-in[RS][0][i])*0.7071068f;
+ }while(i--);
+ }
+ for(int i=maxArray;i<ARRAYSIZE;i++) {
+ in[LS][0][i]=in[RS][0][i]=(REAL) 0.0;
+ }
+
+ }
+
+
+ // channels==2
+}
+
+inline void layer3reorder_1(int version,int frequency,
+ REAL in[SBLIMIT][SSLIMIT],
+ REAL out[SBLIMIT][SSLIMIT])
+{
+ SFBANDINDEX *sfBandIndex=&(sfBandIndextable[version][frequency]);
+ int sfb,sfb_start,sfb_lines;
+
+ /* NO REORDER FOR LOW 2 SUBBANDS */
+ out[0][ 0]=in[0][ 0];out[0][ 1]=in[0][ 1];out[0][ 2]=in[0][ 2];
+ out[0][ 3]=in[0][ 3];out[0][ 4]=in[0][ 4];out[0][ 5]=in[0][ 5];
+ out[0][ 6]=in[0][ 6];out[0][ 7]=in[0][ 7];out[0][ 8]=in[0][ 8];
+ out[0][ 9]=in[0][ 9];out[0][10]=in[0][10];out[0][11]=in[0][11];
+ out[0][12]=in[0][12];out[0][13]=in[0][13];out[0][14]=in[0][14];
+ out[0][15]=in[0][15];out[0][16]=in[0][16];out[0][17]=in[0][17];
+
+ out[1][ 0]=in[1][ 0];out[1][ 1]=in[1][ 1];out[1][ 2]=in[1][ 2];
+ out[1][ 3]=in[1][ 3];out[1][ 4]=in[1][ 4];out[1][ 5]=in[1][ 5];
+ out[1][ 6]=in[1][ 6];out[1][ 7]=in[1][ 7];out[1][ 8]=in[1][ 8];
+ out[1][ 9]=in[1][ 9];out[1][10]=in[1][10];out[1][11]=in[1][11];
+ out[1][12]=in[1][12];out[1][13]=in[1][13];out[1][14]=in[1][14];
+ out[1][15]=in[1][15];out[1][16]=in[1][16];out[1][17]=in[1][17];
+
+
+ /* REORDERING FOR REST SWITCHED SHORT */
+ for(sfb=3,sfb_start=sfBandIndex->s[3],
+ sfb_lines=sfBandIndex->s[4]-sfb_start;
+ sfb<13;
+ sfb++,sfb_start=sfBandIndex->s[sfb],
+ (sfb_lines=sfBandIndex->s[sfb+1]-sfb_start))
+ {
+ for(int freq=0;freq<sfb_lines;freq++)
+ {
+ int src_line=sfb_start+(sfb_start<<1)+freq;
+ int des_line=src_line+(freq<<1);
+ out[0][des_line ]=in[0][src_line ];
+ out[0][des_line+1]=in[0][src_line+sfb_lines ];
+ out[0][des_line+2]=in[0][src_line+(sfb_lines<<1)];
+ }
+ }
+}
+
+inline void layer3reorder_2(int version,int frequency,REAL in[SBLIMIT][SSLIMIT],
+ REAL out[SBLIMIT][SSLIMIT])
+{
+ SFBANDINDEX *sfBandIndex=&(sfBandIndextable[version][frequency]);
+ int sfb,sfb_start,sfb_lines;
+
+ for(sfb=0,sfb_start=0,sfb_lines=sfBandIndex->s[1];
+ sfb<13;
+ sfb++,sfb_start=sfBandIndex->s[sfb],
+ (sfb_lines=sfBandIndex->s[sfb+1]-sfb_start))
+ {
+ for(int freq=0;freq<sfb_lines;freq++)
+ {
+ int src_line=sfb_start+(sfb_start<<1)+freq;
+ int des_line=src_line+(freq<<1);
+
+ out[0][des_line ]=in[0][src_line ];
+ out[0][des_line+1]=in[0][src_line+sfb_lines ];
+ out[0][des_line+2]=in[0][src_line+(sfb_lines<<1)];
+ }
+ }
+}
+
+
+inline void layer3antialias_1(REAL in[SBLIMIT][SSLIMIT])
+{
+ for(int ss=0;ss<8;ss++)
+ {
+ REAL bu,bd; /* upper and lower butterfly inputs */
+
+ bu=in[0][17-ss];bd=in[1][ss];
+ in[0][17-ss]=(bu*cs[ss])-(bd*ca[ss]);
+ in[1][ss] =(bd*cs[ss])+(bu*ca[ss]);
+ }
+}
+
+inline
+void layer3antialias_2(REAL in[SBLIMIT][SSLIMIT],
+ REAL out[SBLIMIT][SSLIMIT])
+{
+ out[0][0]=in[0][0];out[0][1]=in[0][1];
+ out[0][2]=in[0][2];out[0][3]=in[0][3];
+ out[0][4]=in[0][4];out[0][5]=in[0][5];
+ out[0][6]=in[0][6];out[0][7]=in[0][7];
+
+ for(int index=SSLIMIT;index<=(SBLIMIT-1)*SSLIMIT;index+=SSLIMIT)
+ {
+ for(int n=0;n<8;n++)
+ {
+ REAL bu,bd;
+
+ bu=in[0][index-n-1];bd=in[0][index+n];
+ out[0][index-n-1]=(bu*cs[n])-(bd*ca[n]);
+ out[0][index+n ]=(bd*cs[n])+(bu*ca[n]);
+ }
+ out[0][index-SSLIMIT+8]=in[0][index-SSLIMIT+8];
+ out[0][index-SSLIMIT+9]=in[0][index-SSLIMIT+9];
+ }
+
+ out[31][ 8]=in[31][ 8];out[31][ 9]=in[31][ 9];
+ out[31][10]=in[31][10];out[31][11]=in[31][11];
+ out[31][12]=in[31][12];out[31][13]=in[31][13];
+ out[31][14]=in[31][14];out[31][15]=in[31][15];
+ out[31][16]=in[31][16];out[31][17]=in[31][17];
+}
+
+void Mpegtoraw::layer3reorderandantialias(int ch,int gr,
+ REAL in[SBLIMIT][SSLIMIT],
+ REAL out[SBLIMIT][SSLIMIT])
+{
+ int version=mpegAudioHeader->getVersion();
+ int frequency=mpegAudioHeader->getFrequency();
+ int mpeg25=mpegAudioHeader->getLayer25();
+ register layer3grinfo *gi=&(sideinfo.ch[ch].gr[gr]);
+
+ if(gi->generalflag) {
+ if(gi->mixed_block_flag) {
+ layer3reorder_1 (mpeg25?2:version,frequency,in,out); // Not checked...
+ layer3antialias_1(out);
+ }
+ else {
+ layer3reorder_2(mpeg25?2:version,frequency,in,out);
+ }
+ }
+ else {
+
+ layer3antialias_2(in,out);
+
+ }
+}
+
+
+#include "dct36_12.cpp"
+#include "window.cpp"
+
+void Mpegtoraw::layer3hybrid(int ch,int gr,REAL in[SBLIMIT][SSLIMIT],
+ REAL out[SSLIMIT][SBLIMIT])
+{
+ layer3grinfo *gi=&(sideinfo.ch[ch].gr[gr]);
+ int bt1,bt2;
+ REAL *prev1,*prev2;
+
+ prev1=prevblck[ch][currentprevblock][0];
+ prev2=prevblck[ch][currentprevblock^1][0];
+
+ bt1 = gi->mixed_block_flag ? 0 : gi->block_type;
+ bt2 = gi->block_type;
+
+ {
+ REAL *ci=(REAL *)in,
+ *co=(REAL *)out;
+ int i;
+
+ if(lDownSample)i=(SBLIMIT/2)-2;
+ else i=SBLIMIT-2;
+
+
+ if(bt2==2)
+ {
+ if(!bt1)
+ {
+ dct36(ci,prev1,prev2,getSplayWindow(0),co);
+ ci+=SSLIMIT;prev1+=SSLIMIT;prev2+=SSLIMIT;co++;
+ dct36(ci,prev1,prev2,getSplayWindowINV(0),co);
+ }
+ else
+ {
+ dct12(ci,prev1,prev2,getSplayWindow(2),co);
+ ci+=SSLIMIT;prev1+=SSLIMIT;prev2+=SSLIMIT;co++;
+ dct12(ci,prev1,prev2,getSplayWindowINV(2),co);
+ }
+
+ do{
+ ci+=SSLIMIT;prev1+=SSLIMIT;prev2+=SSLIMIT;co++;
+ dct12(ci,prev1,prev2,getSplayWindow(2),co);
+ i--;
+ ci+=SSLIMIT;prev1+=SSLIMIT;prev2+=SSLIMIT;co++;
+ dct12(ci,prev1,prev2,getSplayWindowINV(2),co);
+ }while(--i);
+ }
+ else
+ {
+ dct36(ci,prev1,prev2,getSplayWindow(bt1),co);
+ ci+=SSLIMIT;prev1+=SSLIMIT;prev2+=SSLIMIT;co++;
+ dct36(ci,prev1,prev2,getSplayWindowINV(bt1),co);
+
+ do
+ {
+ ci+=SSLIMIT;prev1+=SSLIMIT;prev2+=SSLIMIT;co++;
+ dct36(ci,prev1,prev2,getSplayWindow(bt2),co);
+ i--;
+ ci+=SSLIMIT;prev1+=SSLIMIT;prev2+=SSLIMIT;co++;
+ dct36(ci,prev1,prev2,getSplayWindowINV(bt2),co);
+ }while(--i);
+ }
+ }
+}
+
+void Mpegtoraw::extractlayer3(void) {
+ int version=mpegAudioHeader->getVersion();
+ int inputstereo=mpegAudioHeader->getInputstereo();
+ int layer3slots=mpegAudioHeader->getLayer3slots();
+
+ if(version) {
+ extractlayer3_2();
+ return;
+ }
+
+ {
+ int main_data_end,flush_main;
+ int bytes_to_discard;
+ if (layer3getsideinfo() == false) {
+ return;
+ }
+ // read main data.
+ if(issync()) {
+ for(register int i=layer3slots;i>0;i--) {
+ bitwindow.putbyte(getbyte());
+ }
+ } else {
+ // read main data.
+ for(register int i=layer3slots;i>0;i--) {
+ bitwindow.putbyte(getbits8());
+ }
+ }
+
+ main_data_end=bitwindow.gettotalbit()>>3;// of previous frame
+ if (main_data_end < 0) {
+ DEBUG_LAYER(printf("main_data_end < 0\n");)
+ return;
+ }
+
+ if((flush_main=(bitwindow.gettotalbit() & 0x7))) {
+ bitwindow.forward(8-flush_main);
+ main_data_end++;
+ }
+
+ bytes_to_discard=layer3framestart-(main_data_end+sideinfo.main_data_begin);
+ if(main_data_end>WINDOWSIZE) {
+ layer3framestart-=WINDOWSIZE;
+ bitwindow.rewind(WINDOWSIZE*8);
+ }
+ layer3framestart+=layer3slots;
+ bitwindow.wrap();
+ if(bytes_to_discard<0) return;
+ bitwindow.forward(bytes_to_discard<<3);
+ }
+ for(int gr=0;gr<2;gr++) {
+ ATTR_ALIGN(64) union
+ {
+ int is [SBLIMIT][SSLIMIT];
+ REAL hin [2][SBLIMIT][SSLIMIT];
+ }b1;
+ ATTR_ALIGN(64) union
+ {
+ REAL ro [2][SBLIMIT][SSLIMIT];
+ REAL lr [2][SBLIMIT][SSLIMIT];
+ REAL hout [2][SSLIMIT][SBLIMIT];
+ }b2;
+
+ layer3part2start=bitwindow.gettotalbit();
+ layer3getscalefactors (LS,gr);
+
+ layer3huffmandecode (LS,gr ,b1.is);
+ layer3dequantizesample(LS,gr,b1.is,b2.ro[LS]);
+ //dump->dump(b2.ro[LS]);
+
+ if(inputstereo) {
+ layer3part2start=bitwindow.gettotalbit();
+ layer3getscalefactors (RS,gr);
+ layer3huffmandecode (RS,gr ,b1.is);
+ layer3dequantizesample(RS,gr,b1.is,b2.ro[RS]);
+ }
+ layer3fixtostereo(gr,b2.ro); // b2.ro -> b2.lr
+ currentprevblock^=1;
+
+
+ layer3reorderandantialias(LS,gr,b2.lr[LS],b1.hin[LS]);
+ //dump->dump(b1.hin[LS]);
+ layer3hybrid (LS,gr,b1.hin[LS],b2.hout[LS]);
+ //dump->dump(b2.hout[LS]);
+
+
+
+
+ if(lOutputStereo) {
+ layer3reorderandantialias(RS,gr,b2.lr[RS],b1.hin[RS]);
+ layer3hybrid (RS,gr,b1.hin[RS],b2.hout[RS]);
+
+ }
+ synthesis->doMP3Synth(lDownSample,lOutputStereo,b2.hout);
+ }
+}
+
+void Mpegtoraw::extractlayer3_2(void) {
+ int inputstereo=mpegAudioHeader->getInputstereo();
+ int layer3slots=mpegAudioHeader->getLayer3slots();
+
+ {
+ int main_data_end,flush_main;
+ int bytes_to_discard;
+ if (layer3getsideinfo_2() == false) {
+ return;
+ }
+ // read main data.
+ if(issync()) {
+ for(register int i=layer3slots;i>0;i--) {
+ bitwindow.putbyte(getbyte());
+ }
+ }
+ else {
+ // read main data.
+ for(register int i=layer3slots;i>0;i--) {
+ bitwindow.putbyte(getbits8());
+ }
+ }
+
+ //bitwindow.wrap();
+
+ main_data_end=bitwindow.gettotalbit()>>3;// of previous frame
+ if (main_data_end < 0) {
+ DEBUG_LAYER(printf("main_data_end < 0\n");)
+ return;
+ }
+
+ if((flush_main=(bitwindow.gettotalbit() & 0x7))) {
+ bitwindow.forward(8-flush_main);
+ main_data_end++;
+ }
+
+ bytes_to_discard=layer3framestart-(main_data_end+sideinfo.main_data_begin);
+ if(main_data_end>WINDOWSIZE) {
+ layer3framestart-=WINDOWSIZE;
+ bitwindow.rewind(WINDOWSIZE*8);
+ }
+ layer3framestart+=layer3slots;
+
+ bitwindow.wrap();
+ if(bytes_to_discard<0)return;
+ bitwindow.forward(bytes_to_discard<<3);
+ }
+
+ //for(int gr=0;gr<2;gr++) {
+ ATTR_ALIGN(64) union
+ {
+ int is [SBLIMIT][SSLIMIT];
+ REAL hin [2][SBLIMIT][SSLIMIT];
+ }b1;
+ ATTR_ALIGN(64) union
+ {
+ REAL ro [2][SBLIMIT][SSLIMIT];
+ REAL lr [2][SBLIMIT][SSLIMIT];
+ REAL hout [2][SSLIMIT][SBLIMIT];
+ }b2;
+
+
+ layer3part2start=bitwindow.gettotalbit();
+ layer3getscalefactors_2(LS);
+ //dump->dump(&scalefactors[LS]);
+
+ layer3huffmandecode (LS,0 ,b1.is);
+ //dump->dump(b1.is);
+ layer3dequantizesample (LS,0,b1.is,b2.ro[LS]);
+
+ if(inputstereo) {
+ layer3part2start=bitwindow.gettotalbit();
+ layer3getscalefactors_2(RS);
+ layer3huffmandecode (RS,0 ,b1.is);
+ layer3dequantizesample (RS,0,b1.is,b2.ro[RS]);
+ }
+
+ layer3fixtostereo(0,b2.ro); // b2.ro -> b2.lr
+ currentprevblock^=1;
+
+ layer3reorderandantialias(LS,0,b2.lr[LS],b1.hin[LS]);
+ layer3hybrid (LS,0,b1.hin[LS],b2.hout[LS]);
+ if(lOutputStereo) {
+ layer3reorderandantialias(RS,0,b2.lr[RS],b1.hin[RS]);
+ layer3hybrid (RS,0,b1.hin[RS],b2.hout[RS]);
+ }
+ synthesis->doMP3Synth(lDownSample,lOutputStereo,b2.hout);
+
+
+}
diff --git a/mpeglib/lib/splay/mpegsound.h b/mpeglib/lib/splay/mpegsound.h
new file mode 100644
index 00000000..cd0c3571
--- /dev/null
+++ b/mpeglib/lib/splay/mpegsound.h
@@ -0,0 +1,248 @@
+// MPEG/WAVE Sound library
+
+// (C) 1997 by Woo-jae Jung
+
+// Mpegsound.h
+// This is typeset for functions in MPEG/WAVE Sound library.
+// Now, it's for only linux-pc-?86
+
+/************************************/
+/* Include default library packages */
+/************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+
+#include "mpegAudioStream.h"
+#include "common.h"
+
+class Synthesis;
+class AudioFrame;
+
+
+#ifndef _L__SOUND__
+#define _L__SOUND__
+
+#include "mpegAudioHeader.h"
+#include "mpegAudioBitWindow.h"
+
+
+//#define DEBUG_LAYER(x) x
+#define DEBUG_LAYER(x)
+
+
+/**************************/
+/* Define values for MPEG */
+/**************************/
+#define SCALEBLOCK 12
+#define MAXSUBBAND 32
+#define MAXCHANNEL 2
+#define RAWDATASIZE (2*2*2*32*SSLIMIT)
+
+
+
+// Huffmancode
+#define HTN 34
+
+
+
+#define MODE_MONO 0
+#define MODE_STEREO 1
+
+/********************/
+/* Type definitions */
+/********************/
+
+typedef struct {
+ bool generalflag;
+ unsigned int part2_3_length;
+ unsigned int big_values;
+ unsigned int global_gain;
+ unsigned int scalefac_compress;
+ unsigned int window_switching_flag;
+ unsigned int block_type;
+ unsigned int mixed_block_flag;
+ unsigned int table_select[3];
+ unsigned int subblock_gain[3];
+ unsigned int region0_count;
+ unsigned int region1_count;
+ unsigned int preflag;
+ unsigned int scalefac_scale;
+ unsigned int count1table_select;
+}layer3grinfo;
+
+typedef struct {
+ unsigned main_data_begin;
+ unsigned private_bits;
+ struct {
+ unsigned scfsi[4];
+ layer3grinfo gr[2];
+ }ch[2];
+}layer3sideinfo;
+
+typedef struct {
+ int l[23]; /* [cb] */
+ int s[3][13]; /* [window][cb] */
+}layer3scalefactor; /* [ch] */
+
+typedef struct {
+ int tablename;
+ unsigned int xlen,ylen;
+ unsigned int linbits;
+ unsigned int treelen;
+ const unsigned int (*val)[2];
+}HUFFMANCODETABLE;
+
+
+
+
+
+
+
+class DCT;
+class Dump;
+
+// Class for converting mpeg format to raw format
+class Mpegtoraw {
+ /*****************************/
+ /* Constant tables for layer */
+ /*****************************/
+private:
+ static const int bitrate[2][3][15];
+ static const int frequencies[2][3];
+ static const REAL scalefactorstable[64];
+ static const ATTR_ALIGN(64) HUFFMANCODETABLE ht[HTN];
+
+
+ friend class HuffmanLookup;
+
+ /*************************/
+ /* MPEG header variables */
+ /*************************/
+
+ // comes from constructor, decoder works on them
+ MpegAudioStream* mpegAudioStream;
+ MpegAudioHeader* mpegAudioHeader;
+ AudioFrame* audioFrame;
+ Dump* dump;
+ Synthesis* synthesis;
+
+ /***************************************/
+ /* Interface for setting music quality */
+ /***************************************/
+
+ int lWantStereo;
+ int lOutputStereo;
+ int lDownSample;
+
+public:
+ Mpegtoraw(MpegAudioStream* mpegAudioStream,
+ MpegAudioHeader* mpegAudioHeader);
+
+
+ ~Mpegtoraw();
+ int decode(AudioFrame* audioFrame);
+
+
+ void setStereo(int lStereo);
+ int getStereo();
+
+ void setDownSample(int lDownSample);
+ int getDownSample();
+
+
+
+private:
+ void initialize();
+
+
+
+ /*****************************/
+ /* Loading MPEG-Audio stream */
+ /*****************************/
+
+ union
+ {
+ unsigned char store[4];
+ unsigned int current;
+ }u;
+
+
+ int getbyte() { return mpegAudioStream->getbyte(); }
+ int getbits(int bits) { return mpegAudioStream->getbits(bits); }
+ int getbits9(int bits) { return mpegAudioStream->getbits9(bits); }
+ int getbits8() { return mpegAudioStream->getbits8(); }
+ int getbit() { return mpegAudioStream->getbit(); }
+
+
+ void sync() { mpegAudioStream->sync(); }
+ bool issync() { return mpegAudioStream->issync(); }
+
+
+ /********************/
+ /* Global variables */
+ /********************/
+
+ // optimisation from maplay12+
+ // 0/1: nonzero for channel 0/1 2: max position for both
+ int nonzero[3];
+
+ // for Layer3
+
+ int layer3framestart;
+ int layer3part2start;
+
+ ATTR_ALIGN(64) REAL prevblck[2][2][SBLIMIT][SSLIMIT];
+ int currentprevblock;
+ ATTR_ALIGN(64) layer3sideinfo sideinfo;
+ ATTR_ALIGN(64) layer3scalefactor scalefactors[2];
+
+ ATTR_ALIGN(64) MpegAudioBitWindow bitwindow;
+ MpegAudioBitWindow lastValidBitwindow;
+
+ int wgetbit(void);
+ int wgetbits9(int bits);
+ int wgetbits(int bits);
+ int wgetCanReadBits();
+
+ /*************************************/
+ /* Decoding functions for each layer */
+ /*************************************/
+
+ // Extractor
+ void extractlayer1(void); // MPEG-1
+ void extractlayer2(void);
+ void extractlayer3(void);
+ void extractlayer3_2(void); // MPEG-2
+
+
+ // Functions for layer 3
+ void layer3initialize(void);
+ bool layer3getsideinfo(void);
+ bool layer3getsideinfo_2(void);
+ void layer3getscalefactors(int ch,int gr);
+ void layer3getscalefactors_2(int ch);
+ void layer3huffmandecode(int ch,int gr,int out[SBLIMIT][SSLIMIT]);
+ REAL layer3twopow2(int scale,int preflag,int pretab_offset,int l);
+ REAL layer3twopow2_1(int a,int b,int c);
+ void layer3dequantizesample(int ch,int gr,int in[SBLIMIT][SSLIMIT],
+ REAL out[SBLIMIT][SSLIMIT]);
+ void adjustNonZero(REAL in[2][SBLIMIT][SSLIMIT]);
+ void layer3fixtostereo(int gr,REAL in[2][SBLIMIT][SSLIMIT]);
+ void layer3reorderandantialias(int ch,int gr,REAL in[SBLIMIT][SSLIMIT],
+ REAL out[SBLIMIT][SSLIMIT]);
+
+ void layer3hybrid(int ch,int gr,REAL in[SBLIMIT][SSLIMIT],
+ REAL out[SSLIMIT][SBLIMIT]);
+
+ void huffmandecoder_1(const HUFFMANCODETABLE *h,int *x,int *y);
+ void huffmandecoder_2(const HUFFMANCODETABLE *h,int *x,int *y,int *v,int *w);
+
+};
+
+
+
+#endif
diff --git a/mpeglib/lib/splay/mpegtable.cpp b/mpeglib/lib/splay/mpegtable.cpp
new file mode 100644
index 00000000..be539ddd
--- /dev/null
+++ b/mpeglib/lib/splay/mpegtable.cpp
@@ -0,0 +1,35 @@
+/* MPEG/WAVE Sound library
+
+ (C) 1997 by Jung woo-jae */
+
+// Mpegtable.cc
+// It has tables for MPEG layer 1, 2 and a part of layer 3
+
+
+#include "mpegsound.h"
+
+
+
+// Mpeg general table
+const REAL Mpegtoraw::scalefactorstable[64] =
+{
+ 2.00000000000000, 1.58740105196820, 1.25992104989487, 1.00000000000000,
+ 0.79370052598410, 0.62996052494744, 0.50000000000000, 0.39685026299205,
+ 0.31498026247372, 0.25000000000000, 0.19842513149602, 0.15749013123686,
+ 0.12500000000000, 0.09921256574801, 0.07874506561843, 0.06250000000000,
+ 0.04960628287401, 0.03937253280921, 0.03125000000000, 0.02480314143700,
+ 0.01968626640461, 0.01562500000000, 0.01240157071850, 0.00984313320230,
+ 0.00781250000000, 0.00620078535925, 0.00492156660115, 0.00390625000000,
+ 0.00310039267963, 0.00246078330058, 0.00195312500000, 0.00155019633981,
+ 0.00123039165029, 0.00097656250000, 0.00077509816991, 0.00061519582514,
+ 0.00048828125000, 0.00038754908495, 0.00030759791257, 0.00024414062500,
+ 0.00019377454248, 0.00015379895629, 0.00012207031250, 0.00009688727124,
+ 0.00007689947814, 0.00006103515625, 0.00004844363562, 0.00003844973907,
+ 0.00003051757813, 0.00002422181781, 0.00001922486954, 0.00001525878906,
+ 0.00001211090890, 0.00000961243477, 0.00000762939453, 0.00000605545445,
+ 0.00000480621738, 0.00000381469727, 0.00000302772723, 0.00000240310869,
+ 0.00000190734863, 0.00000151386361, 0.00000120155435, 0.00000000000000
+};
+
+
+
diff --git a/mpeglib/lib/splay/mpegtoraw.cpp b/mpeglib/lib/splay/mpegtoraw.cpp
new file mode 100644
index 00000000..93143bbe
--- /dev/null
+++ b/mpeglib/lib/splay/mpegtoraw.cpp
@@ -0,0 +1,127 @@
+/* MPEG/WAVE Sound library
+
+ (C) 1997 by Jung woo-jae */
+
+// Mpegtoraw.cc
+// Server which get mpeg format and put raw format.
+
+
+#include "mpegsound.h"
+#include "synthesis.h"
+#include "dump.h"
+#include "../frame/audioFrame.h"
+
+#include <iostream>
+
+using namespace std;
+
+Mpegtoraw::Mpegtoraw(MpegAudioStream* mpegAudioStream,
+ MpegAudioHeader* mpegAudioHeader) {
+
+ this->mpegAudioStream=mpegAudioStream;
+ this->mpegAudioHeader=mpegAudioHeader;
+
+ this->lOutputStereo=true;
+ setStereo(true);
+ setDownSample(false);
+
+ dump=new Dump();
+ synthesis=new Synthesis();
+ initialize();
+
+}
+
+Mpegtoraw::~Mpegtoraw() {
+
+ delete synthesis;
+ delete dump;
+}
+
+
+
+
+
+
+
+void Mpegtoraw::setStereo(int flag) {
+ lWantStereo=flag;
+}
+
+void Mpegtoraw::setDownSample(int flag) {
+ lDownSample=flag;
+}
+
+int Mpegtoraw::getStereo() {
+ return lWantStereo;
+}
+
+int Mpegtoraw::getDownSample() {
+ return lDownSample;
+}
+
+
+
+
+
+
+
+// Convert mpeg to raw
+// Mpeg headder class
+void Mpegtoraw::initialize() {
+
+
+
+ layer3initialize();
+
+
+}
+
+
+
+
+
+// Convert mpeg to raw
+int Mpegtoraw::decode(AudioFrame* audioFrame) {
+ int back=true;
+
+ this->audioFrame=audioFrame;
+ if (audioFrame->getSize() < RAWDATASIZE) {
+ cout << "audioFrame needs at least:"<<RAWDATASIZE<<" size"<<endl;
+ exit(0);
+ }
+
+ audioFrame->clearrawdata();
+ synthesis->clearrawdata();
+
+ int layer=mpegAudioHeader->getLayer();
+ this->lOutputStereo=lWantStereo & mpegAudioHeader->getInputstereo();
+
+ if (mpegAudioHeader->getProtection()==false) {
+ mpegAudioStream->getbyte();
+ mpegAudioStream->getbyte();
+ }
+ switch(layer) {
+ case 3:
+ extractlayer3();
+ break;
+ case 2:
+ extractlayer2();
+ break;
+ case 1:
+ extractlayer1();
+ break;
+ default:
+ cout << "unknown layer:"<<layer<<endl;
+ back=false;
+ }
+
+ //
+ // Now put the frequencies/output etc.. in the frame
+ //
+ audioFrame->setFrameFormat(lOutputStereo,
+ mpegAudioHeader->getFrequencyHz()>>lDownSample);
+
+ audioFrame->putFloatData(synthesis->getOutputData(),synthesis->getLen());
+ return back;
+
+}
diff --git a/mpeglib/lib/splay/op.h b/mpeglib/lib/splay/op.h
new file mode 100644
index 00000000..a741e97b
--- /dev/null
+++ b/mpeglib/lib/splay/op.h
@@ -0,0 +1,96 @@
+/*
+ unrolled operations, for better Pentium FPU scheduling
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __OP_H
+#define __OP_H
+
+/**
+ The Pentium has two pipelined FPUs which makes it possible
+ to do two operations in one cycle.
+ (If you are lucky)
+
+*/
+
+#define PTR_DIST (1024)
+
+#define OS r1=vp1[0] * dp[0]; \
+ r2=vp1[PTR_DIST-0] * dp[0]; \
+ dp++;
+
+#define XX1 vp1+=15;dp++;
+
+#define XX2 r1+=vp1[0] * dp[-1]; \
+ r2+=vp1[PTR_DIST-0] * dp[-1];
+
+#define OP_END(val) vp1-=val;dp+=val;
+
+#define OP_END_1(vVal,dVal) vp1+=(vVal-dVal),dp+=dVal
+
+
+// this is OP_END(x);XX1; together:
+#define OP_END_2(vVal) vp1+=(15-vVal),dp+=vVal+1
+
+
+// check this to test pipelining
+
+#define SCHEDULE1(op,r1,r2) r1;op;r2;
+#define SCHEDULE2(op,r1,r2) op;r1;r2;
+
+#define SCHEDULE(a,b,c) SCHEDULE2(a,b,c);
+
+
+#define OP r1+=vp1[-1] * dp[0]; \
+ r2+=vp1[PTR_DIST-1] * dp[0];
+
+
+
+#define OP2 SCHEDULE(OP ,r1+=vp1[-2] * dp[1] ,r2+=vp1[PTR_DIST-2] *dp[1]);
+#define OP3 SCHEDULE(OP2 ,r1+=vp1[-3] * dp[2] ,r2+=vp1[PTR_DIST-3] *dp[2]);
+#define OP4 SCHEDULE(OP3 ,r1+=vp1[-4] * dp[3] ,r2+=vp1[PTR_DIST-4] *dp[3]);
+#define OP5 SCHEDULE(OP4 ,r1+=vp1[-5] * dp[4] ,r2+=vp1[PTR_DIST-5] *dp[4]);
+#define OP6 SCHEDULE(OP5 ,r1+=vp1[-6] * dp[5] ,r2+=vp1[PTR_DIST-6] *dp[5]);
+#define OP7 SCHEDULE(OP6 ,r1+=vp1[-7] * dp[6] ,r2+=vp1[PTR_DIST-7] *dp[6]);
+#define OP8 SCHEDULE(OP7 ,r1+=vp1[-8] * dp[7] ,r2+=vp1[PTR_DIST-8] *dp[7]);
+#define OP9 SCHEDULE(OP8 ,r1+=vp1[-9] * dp[8] ,r2+=vp1[PTR_DIST-9] *dp[8]);
+#define OP10 SCHEDULE(OP9 ,r1+=vp1[-10] * dp[9] ,r2+=vp1[PTR_DIST-10] *dp[9]);
+#define OP11 SCHEDULE(OP10,r1+=vp1[-11] * dp[10],r2+=vp1[PTR_DIST-11] *dp[10]);
+#define OP12 SCHEDULE(OP11,r1+=vp1[-12] * dp[11],r2+=vp1[PTR_DIST-12] *dp[11]);
+#define OP13 SCHEDULE(OP12,r1+=vp1[-13] * dp[12],r2+=vp1[PTR_DIST-13] *dp[12]);
+#define OP14 SCHEDULE(OP13,r1+=vp1[-14] * dp[13],r2+=vp1[PTR_DIST-14] *dp[13]);
+#define OP15 SCHEDULE(OP14,r1+=vp1[-15] * dp[14],r2+=vp1[PTR_DIST-15] *dp[14]);
+
+
+/*
+#define OP r1+=vp1[-1] * dp[0]; \
+ r2+=vp2[-1] * dp[0];
+
+
+
+#define OP2 SCHEDULE(OP ,r1+=vp1[-2] * dp[1] ,r2+=vp2[-2] * dp[1]);
+#define OP3 SCHEDULE(OP2 ,r1+=vp1[-3] * dp[2] ,r2+=vp2[-3] * dp[2]);
+#define OP4 SCHEDULE(OP3 ,r1+=vp1[-4] * dp[3] ,r2+=vp2[-4] * dp[3]);
+#define OP5 SCHEDULE(OP4 ,r1+=vp1[-5] * dp[4] ,r2+=vp2[-5] * dp[4]);
+#define OP6 SCHEDULE(OP5 ,r1+=vp1[-6] * dp[5] ,r2+=vp2[-6] * dp[5]);
+#define OP7 SCHEDULE(OP6 ,r1+=vp1[-7] * dp[6] ,r2+=vp2[-7] * dp[6]);
+#define OP8 SCHEDULE(OP7 ,r1+=vp1[-8] * dp[7] ,r2+=vp2[-8] * dp[7]);
+#define OP9 SCHEDULE(OP8 ,r1+=vp1[-9] * dp[8] ,r2+=vp2[-9] * dp[8]);
+#define OP10 SCHEDULE(OP9 ,r1+=vp1[-10] * dp[9] ,r2+=vp2[-10] * dp[9]);
+#define OP11 SCHEDULE(OP10,r1+=vp1[-11] * dp[10],r2+=vp2[-11] * dp[10]);
+#define OP12 SCHEDULE(OP11,r1+=vp1[-12] * dp[11],r2+=vp2[-12] * dp[11]);
+#define OP13 SCHEDULE(OP12,r1+=vp1[-13] * dp[12],r2+=vp2[-13] * dp[12]);
+#define OP14 SCHEDULE(OP13,r1+=vp1[-14] * dp[13],r2+=vp2[-14] * dp[13]);
+#define OP15 SCHEDULE(OP14,r1+=vp1[-15] * dp[14],r2+=vp2[-15] * dp[14]);
+*/
+
+#endif
diff --git a/mpeglib/lib/splay/sigsev.c b/mpeglib/lib/splay/sigsev.c
new file mode 100644
index 00000000..906d8cb9
--- /dev/null
+++ b/mpeglib/lib/splay/sigsev.c
@@ -0,0 +1,29 @@
+/*
+ No SegFault:
+
+ g++ -o sigsev.exe sigsevTest.cpp
+
+ SegFault:
+
+ g++ -o sigsev.exe -O6 sigsevTest.cpp
+ */
+
+#include <stdio.h>
+#undef __NO_MATH_INLINES // <<<< Add this line
+#define __NO_MATH_INLINES 1 // <<<< and this.
+#include <math.h>
+
+
+int main() {
+ printf("hello Martin test->main\n");
+
+ //pow(6.0,3.0);
+ float value;
+ value=cos(double(0));
+ printf("Wert: %f\n",value);
+ pow(6.0,3.0);
+
+ printf("hi:\n");
+ exit(0);
+}
+
diff --git a/mpeglib/lib/splay/splayDecoder.cpp b/mpeglib/lib/splay/splayDecoder.cpp
new file mode 100644
index 00000000..13d6de1b
--- /dev/null
+++ b/mpeglib/lib/splay/splayDecoder.cpp
@@ -0,0 +1,73 @@
+/*
+ decoder interface for the splay mp3 decoder.
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#define _FROM_SOURCE
+#include "dxHead.h"
+#include "splayDecoder.h"
+#include "mpegsound.h"
+
+
+
+SplayDecoder::SplayDecoder() {
+ header = new MpegAudioHeader();
+ stream = new MpegAudioStream();
+ server = new Mpegtoraw(stream,header);
+
+ xHeadData=new XHEADDATA();
+ xHeadData->toc=new unsigned char[101];
+ dump=new Dump();
+}
+
+
+SplayDecoder::~SplayDecoder() {
+ delete [] xHeadData->toc;
+ delete xHeadData;
+
+ delete server;
+ delete header;
+ delete stream;
+ delete dump;
+}
+
+
+
+int SplayDecoder::decode(unsigned char* ptr, int len,AudioFrame* dest) {
+ int back;
+ // fist setup the stream and the 4 bytes header info;
+ //dump->dump((char*)ptr,len);
+ if (header->parseHeader(ptr) == false) {
+ return false;
+ }
+ // maybe a Xing Header?
+ if (len >= 152+4) {
+ int lXing=GetXingHeader(xHeadData,(unsigned char*)ptr);
+ if (lXing) {
+ return false;
+ }
+ }
+ stream->setFrame(ptr+4,len-4);
+ back=server->decode(dest);
+
+ return back;
+
+}
+
+
+void SplayDecoder::config(const char* key,const char* val,void* ) {
+ if (strcmp(key,"2")==0) {
+ server->setDownSample(atoi(val));
+ }
+ if (strcmp(key,"m")==0) {
+ server->setStereo(atoi(val));
+ }
+}
+
diff --git a/mpeglib/lib/splay/splayDecoder.h b/mpeglib/lib/splay/splayDecoder.h
new file mode 100644
index 00000000..acbfdbfc
--- /dev/null
+++ b/mpeglib/lib/splay/splayDecoder.h
@@ -0,0 +1,70 @@
+/*
+ decoder interface for the splay mp3 decoder.
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __SPLAYDECODER_H
+#define __SPLAYDECODER_H
+
+// state definitions for splay decoder
+
+#define _SPLAY_RESET 0
+#define _SPLAY_EOF 1
+#define _SPLAY_FIRSTINIT 2
+#define _SPLAY_REINIT 3
+#define _SPLAY_DECODE 4
+#define _SPLAY_FRAME 5
+
+
+#include "../frame/audioFrame.h"
+#include "dump.h"
+#include <string.h>
+#include <kdemacros.h>
+
+class Mpegtoraw;
+class MpegAudioStream;
+class MpegAudioHeader;
+
+
+/**
+ The decoder interface.
+ The decoder expects an mpeg audio frame.
+ The call to decode is "atomic", after that you have
+ a PCMFrame to play.
+
+*/
+
+
+
+class KDE_EXPORT SplayDecoder {
+
+ MpegAudioStream* stream;
+ MpegAudioHeader* header;
+ Mpegtoraw* server;
+ Dump* dump;
+#ifdef _FROM_SOURCE
+ XHEADDATA* xHeadData;
+#else
+ void* xHeadData;
+#endif
+
+
+ public:
+ SplayDecoder();
+ ~SplayDecoder();
+
+ int decode(unsigned char* ptr, int len,AudioFrame* dest);
+ void config(const char* key,const char* val,void* ret);
+
+};
+#endif
diff --git a/mpeglib/lib/splay/synth_Down.cpp b/mpeglib/lib/splay/synth_Down.cpp
new file mode 100644
index 00000000..fbe3887b
--- /dev/null
+++ b/mpeglib/lib/splay/synth_Down.cpp
@@ -0,0 +1,231 @@
+/*
+ downsample implementation
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "synthesis.h"
+#include "dct64_down.cpp"
+
+#include <iostream>
+
+using namespace std;
+
+
+void Synthesis::computebuffer_Down(REAL *fraction,
+ REAL buffer[2][CALCBUFFERSIZE]){
+ REAL *out1,*out2;
+
+ out1=buffer[currentcalcbuffer]+calcbufferoffset;
+ out2=buffer[currentcalcbuffer^1]+calcbufferoffset;
+ dct64_downsample(out1,out2,fraction);
+}
+
+
+
+#define SAVE putraw(r); \
+ dp+=16;vp+=15+(15-14)
+#define OS r=*vp * *dp++
+#define XX vp+=15;r+=*vp * *dp++
+#define OP r+=*--vp * *dp++
+
+inline void Synthesis::generatesingle_Down(void)
+{
+ int i;
+ register REAL r, *vp;
+ register const REAL *dp;
+
+ i=32/2;
+ dp=filter;
+ vp=calcbuffer[LS][currentcalcbuffer]+calcbufferoffset;
+// actual_v+actual_write_pos;
+
+ switch (calcbufferoffset)
+ {
+ case 0:for(;i;i--,vp+=15){
+ OS;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 1:for(;i;i--,vp+=15){
+ OS;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 2:for(;i;i--,vp+=15){
+ OS;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 3:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 4:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 5:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 6:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 7:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 8:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 9:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 10:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;
+ SAVE;}break;
+ case 11:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;
+ SAVE;}break;
+ case 12:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;
+ SAVE;}break;
+ case 13:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;
+ SAVE;}break;
+ case 14:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;
+ SAVE;}break;
+ case 15:for(;i;i--,vp+=31){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ }
+}
+
+#undef OS
+#undef XX
+#undef OP
+#undef SAVE
+
+#define SAVE \
+ putraw(r1); \
+ putraw(r2); \
+ dp+=16;vp1+=15+(15-14);vp2+=15+(15-14)
+#define OS r1=*vp1 * *dp; \
+ r2=*vp2 * *dp++
+#define XX vp1+=15;r1+=*vp1 * *dp; \
+ vp2+=15;r2+=*vp2 * *dp++
+#define OP r1+=*--vp1 * *dp; \
+ r2+=*--vp2 * *dp++
+
+
+inline void Synthesis::generate_Down(void)
+{
+ int i;
+ REAL r1,r2;
+ register REAL *vp1,*vp2;
+ register const REAL *dp;
+
+ dp=filter;
+ vp1=calcbuffer[LS][currentcalcbuffer]+calcbufferoffset;
+ vp2=calcbuffer[RS][currentcalcbuffer]+calcbufferoffset;
+// actual_v+actual_write_pos;
+
+ i=32/2;
+ switch (calcbufferoffset)
+ {
+ case 0:for(;i;i--,vp1+=15,vp2+=15){
+ OS;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 1:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 2:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 3:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 4:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 5:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 6:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 7:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 8:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 9:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 10:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;
+ SAVE;}break;
+ case 11:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;
+ SAVE;}break;
+ case 12:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;
+ SAVE;}break;
+ case 13:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;
+ SAVE;}break;
+ case 14:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;
+ SAVE;}break;
+ case 15:for(;i;i--,vp1+=31,vp2+=31){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ }
+}
+
+
+
+void Synthesis::synth_Down(int lOutputStereo,REAL *fractionL,REAL *fractionR) {
+ switch(lOutputStereo) {
+ case true:
+ computebuffer_Down(fractionL,calcbuffer[LS]);
+ computebuffer_Down(fractionR,calcbuffer[RS]);
+ generate_Down();
+ nextOffset();
+ break;
+ case false:
+ computebuffer_Down(fractionL,calcbuffer[LS]);
+ generatesingle_Down();
+ nextOffset();
+ break;
+ default:
+ cout << "unknown lOutputStereo in Synthesis::synth_Std"<<endl;
+ exit(0);
+ }
+}
+
+
+void Synthesis::synthMP3_Down(int lOutputStereo,
+ REAL hout [2][SSLIMIT][SBLIMIT]) {
+ int ss;
+ switch(lOutputStereo) {
+ case true:
+ for(ss=0;ss<SSLIMIT;ss++) {
+ computebuffer_Down(hout[LS][ss],calcbuffer[LS]);
+ computebuffer_Down(hout[RS][ss],calcbuffer[RS]);
+ generate_Down();
+ nextOffset();
+ }
+ break;
+ case false:
+ for(ss=0;ss<SSLIMIT;ss++) {
+ computebuffer_Down(hout[LS][ss],calcbuffer[LS]);
+ generatesingle_Down();
+ nextOffset();
+ }
+ break;
+ default:
+ cout << "unknown lOutputStereo in Synthesis::synth_Std"<<endl;
+ exit(0);
+ }
+}
diff --git a/mpeglib/lib/splay/synth_Std.cpp b/mpeglib/lib/splay/synth_Std.cpp
new file mode 100644
index 00000000..6ea1d027
--- /dev/null
+++ b/mpeglib/lib/splay/synth_Std.cpp
@@ -0,0 +1,330 @@
+/*
+ std synth implementation
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "synthesis.h"
+
+
+
+#include "dct64.cpp"
+
+#include <iostream>
+
+using namespace std;
+
+inline void Synthesis::computebuffer_Std(REAL *fraction,
+ REAL buffer[2][CALCBUFFERSIZE]) {
+ REAL *out1,*out2;
+ out1=buffer[currentcalcbuffer]+calcbufferoffset;
+ out2=buffer[currentcalcbuffer^1]+calcbufferoffset;
+ dct64(out1,out2,fraction);
+}
+
+
+#define SAVE putraw(r);
+#define OS r=*vp * *dp++
+#define XX vp+=15;r+=*vp * *dp++
+#define OP r+=*--vp * *dp++
+
+
+inline void Synthesis::generatesingle_Std(void) {
+ int i;
+ register REAL r, *vp;
+ register const REAL *dp;
+
+ i=32;
+ dp=filter;
+ vp=calcbuffer[LS][currentcalcbuffer]+calcbufferoffset;
+// actual_v+actual_write_pos;
+
+ switch (calcbufferoffset)
+ {
+ case 0:for(;i;i--,vp+=15){
+ OS;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 1:for(;i;i--,vp+=15){
+ OS;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 2:for(;i;i--,vp+=15){
+ OS;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 3:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 4:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 5:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 6:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 7:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 8:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 9:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 10:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;
+ SAVE;}break;
+ case 11:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;
+ SAVE;}break;
+ case 12:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;
+ SAVE;}break;
+ case 13:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;
+ SAVE;}break;
+ case 14:for(;i;i--,vp+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;
+ SAVE;}break;
+ case 15:for(;i;i--,vp+=31){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ }
+}
+
+#undef OS
+#undef XX
+#undef OP
+#undef SAVE
+
+
+#define SAVE putraw(r1); putraw(r2);
+
+#define OS r1=*vp1 * *dp; \
+ r2=*vp2 * *dp++
+#define XX vp1+=15;r1+=*vp1 * *dp; \
+ vp2+=15;r2+=*vp2 * *dp++
+#define OP r1+=*--vp1 * *dp; \
+ r2+=*--vp2 * *dp++
+
+/*
+inline void Synthesis::generate_old(void)
+{
+ int i;
+ REAL r1,r2;
+ register REAL *vp1,*vp2;
+ register const REAL *dp;
+
+ dp=filter;
+ vp1=calcbuffer[LS][currentcalcbuffer]+calcbufferoffset;
+ vp2=calcbuffer[RS][currentcalcbuffer]+calcbufferoffset;
+// actual_v+actual_write_pos;
+
+ i=32;
+ switch (calcbufferoffset)
+ {
+ case 0:for(;i;i--,vp1+=15,vp2+=15){
+ OS;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 1:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 2:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 3:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 4:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 5:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 6:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 7:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 8:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 9:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ case 10:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;OP;
+ SAVE;}break;
+ case 11:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;OP;
+ SAVE;}break;
+ case 12:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;OP;
+ SAVE;}break;
+ case 13:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;OP;
+ SAVE;}break;
+ case 14:for(;i;i--,vp1+=15,vp2+=15){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;XX;
+ SAVE;}break;
+ case 15:for(;i;i--,vp1+=31,vp2+=31){
+ OS;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;OP;
+ SAVE;}break;
+ }
+}
+*/
+#undef OS
+#undef XX
+#undef OP
+#undef SAVE
+
+#include "op.h"
+#define SAVE putraw(r1); putraw(r2);
+
+inline void Synthesis::generate_Std(void)
+{
+ int i;
+ REAL r1,r2;
+
+ register REAL *vp1;
+ register const REAL *dp;
+
+ dp=filter;
+ vp1=calcbuffer[LS][currentcalcbuffer]+calcbufferoffset;
+ // we calculate cp2 from vp1 because they are both
+ // in the same array. code was:
+ // register REAL* vp2
+ //vp2=calcbuffer[RS][currentcalcbuffer]+calcbufferoffset;
+
+ i=32;
+ switch (calcbufferoffset)
+ {
+ case 0:for(;i;i--,OP_END_1(15,14)){
+ OS;XX1;XX2;OP14;
+ SAVE;}break;
+ case 1:for(;i;i--,OP_END_1(15,13)){
+ OS;OP;OP_END_2(1);XX2;OP13;
+ SAVE;}break;
+ case 2:for(;i;i--,OP_END_1(15,12)){
+ OS;OP2;OP_END_2(2);XX2;OP12;
+ SAVE;}break;
+ case 3:for(;i;i--,OP_END_1(15,11)){
+ OS;OP3;OP_END_2(3);XX2;OP11;
+ SAVE;}break;
+ case 4:for(;i;i--,OP_END_1(15,10)){
+ OS;OP4;OP_END_2(4);XX2;OP10;
+ SAVE;}break;
+ case 5:for(;i;i--,OP_END_1(15,9)){
+ OS;OP5;OP_END_2(5);XX2;OP9;
+ SAVE;}break;
+ case 6:for(;i;i--,OP_END_1(15,8)){
+ OS;OP6;OP_END_2(6);XX2;OP8;
+ SAVE;}break;
+ case 7:for(;i;i--,OP_END_1(15,7)){
+ OS;OP7;OP_END_2(7);XX2;OP7;
+ SAVE;}break;
+ case 8:for(;i;i--,OP_END_1(15,6)){
+ OS;OP8;OP_END_2(8);XX2;OP6;
+ SAVE;}break;
+ case 9:for(;i;i--,OP_END_1(15,5)){
+ OS;OP9;OP_END_2(9);XX2;OP5;
+ SAVE;}break;
+ case 10:for(;i;i--,OP_END_1(15,4)){
+ OS;OP10;OP_END_2(10);XX2;OP4;
+ SAVE;}break;
+ case 11:for(;i;i--,OP_END_1(15,3)){
+ OS;OP11;OP_END_2(11);XX2;OP3;
+ SAVE;}break;
+ case 12:for(;i;i--,OP_END_1(15,2)){
+ OS;OP12;OP_END_2(12);XX2;OP2;
+ SAVE;}break;
+ case 13:for(;i;i--,OP_END_1(15,1)){
+ OS;OP13;OP_END_2(13);XX2;OP;
+ SAVE;}break;
+ case 14:for(;i;i--,vp1+=15){
+ OS;OP14;OP_END_2(14);XX2;
+ SAVE;}break;
+ case 15:for(;i;i--,OP_END_1(31,15)){
+ OS;OP15;
+ SAVE;}break;
+ }
+}
+#undef OP_END_1
+#undef OP_END_2
+#undef OP
+#undef OP1
+#undef OP2
+#undef OP3
+#undef OP4
+#undef OP5
+#undef OP6
+#undef OP7
+#undef OP8
+#undef OP9
+#undef OP10
+#undef OP11
+#undef OP12
+#undef OP13
+#undef OP14
+#undef OP15
+#undef OS
+#undef XX1
+#undef XX2
+#undef SCHEDULE
+#undef SCHEDULE1
+#undef SCHEDULE2
+#undef SAVE
+
+void Synthesis::synth_Std(int lOutputStereo,REAL *fractionL,REAL *fractionR) {
+ switch(lOutputStereo) {
+ case true:
+ computebuffer_Std(fractionL,calcbuffer[LS]);
+ computebuffer_Std(fractionR,calcbuffer[RS]);
+ generate_Std();
+ nextOffset();
+ break;
+ case false:
+ computebuffer_Std(fractionL,calcbuffer[LS]);
+ generatesingle_Std();
+ nextOffset();
+ break;
+ default:
+ cout << "unknown lOutputStereo in Synthesis::synth_Std"<<endl;
+ exit(0);
+ }
+}
+
+
+void Synthesis::synthMP3_Std(int lOutputStereo,
+ REAL hout [2][SSLIMIT][SBLIMIT]) {
+ int ss;
+ switch(lOutputStereo) {
+ case true:
+ for(ss=0;ss<SSLIMIT;ss++) {
+ computebuffer_Std(hout[LS][ss],calcbuffer[LS]);
+ computebuffer_Std(hout[RS][ss],calcbuffer[RS]);
+ generate_Std();
+ nextOffset();
+ }
+ break;
+ case false:
+ for(ss=0;ss<SSLIMIT;ss++) {
+ computebuffer_Std(hout[LS][ss],calcbuffer[LS]);
+ generatesingle_Std();
+ nextOffset();
+ }
+ break;
+ default:
+ cout << "unknown lOutputStereo in Synthesis::synth_Std"<<endl;
+ exit(0);
+ }
+}
diff --git a/mpeglib/lib/splay/synth_filter.cpp b/mpeglib/lib/splay/synth_filter.cpp
new file mode 100644
index 00000000..47455288
--- /dev/null
+++ b/mpeglib/lib/splay/synth_filter.cpp
@@ -0,0 +1,147 @@
+/*
+ filer definition
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "synthesis.h"
+
+
+ATTR_ALIGN(64) const REAL Synthesis::filter[512]=
+{
+ 0.000000000, -0.000442505, 0.003250122, -0.007003784,
+ 0.031082153, -0.078628540, 0.100311279, -0.572036743,
+ 1.144989014, 0.572036743, 0.100311279, 0.078628540,
+ 0.031082153, 0.007003784, 0.003250122, 0.000442505,
+ -0.000015259, -0.000473022, 0.003326416, -0.007919312,
+ 0.030517578, -0.084182739, 0.090927124, -0.600219727,
+ 1.144287109, 0.543823242, 0.108856201, 0.073059082,
+ 0.031478882, 0.006118774, 0.003173828, 0.000396729,
+ -0.000015259, -0.000534058, 0.003387451, -0.008865356,
+ 0.029785156, -0.089706421, 0.080688477, -0.628295898,
+ 1.142211914, 0.515609741, 0.116577148, 0.067520142,
+ 0.031738281, 0.005294800, 0.003082275, 0.000366211,
+ -0.000015259, -0.000579834, 0.003433228, -0.009841919,
+ 0.028884888, -0.095169067, 0.069595337, -0.656219482,
+ 1.138763428, 0.487472534, 0.123474121, 0.061996460,
+ 0.031845093, 0.004486084, 0.002990723, 0.000320435,
+ -0.000015259, -0.000625610, 0.003463745, -0.010848999,
+ 0.027801514, -0.100540161, 0.057617188, -0.683914185,
+ 1.133926392, 0.459472656, 0.129577637, 0.056533813,
+ 0.031814575, 0.003723145, 0.002899170, 0.000289917,
+ -0.000015259, -0.000686646, 0.003479004, -0.011886597,
+ 0.026535034, -0.105819702, 0.044784546, -0.711318970,
+ 1.127746582, 0.431655884, 0.134887695, 0.051132202,
+ 0.031661987, 0.003005981, 0.002792358, 0.000259399,
+ -0.000015259, -0.000747681, 0.003479004, -0.012939453,
+ 0.025085449, -0.110946655, 0.031082153, -0.738372803,
+ 1.120223999, 0.404083252, 0.139450073, 0.045837402,
+ 0.031387329, 0.002334595, 0.002685547, 0.000244141,
+ -0.000030518, -0.000808716, 0.003463745, -0.014022827,
+ 0.023422241, -0.115921021, 0.016510010, -0.765029907,
+ 1.111373901, 0.376800537, 0.143264771, 0.040634155,
+ 0.031005859, 0.001693726, 0.002578735, 0.000213623,
+ -0.000030518, -0.000885010, 0.003417969, -0.015121460,
+ 0.021575928, -0.120697021, 0.001068115, -0.791213989,
+ 1.101211548, 0.349868774, 0.146362305, 0.035552979,
+ 0.030532837, 0.001098633, 0.002456665, 0.000198364,
+ -0.000030518, -0.000961304, 0.003372192, -0.016235352,
+ 0.019531250, -0.125259399, -0.015228271, -0.816864014,
+ 1.089782715, 0.323318481, 0.148773193, 0.030609131,
+ 0.029937744, 0.000549316, 0.002349854, 0.000167847,
+ -0.000030518, -0.001037598, 0.003280640, -0.017349243,
+ 0.017257690, -0.129562378, -0.032379150, -0.841949463,
+ 1.077117920, 0.297210693, 0.150497437, 0.025817871,
+ 0.029281616, 0.000030518, 0.002243042, 0.000152588,
+ -0.000045776, -0.001113892, 0.003173828, -0.018463135,
+ 0.014801025, -0.133590698, -0.050354004, -0.866363525,
+ 1.063217163, 0.271591187, 0.151596069, 0.021179199,
+ 0.028533936, -0.000442505, 0.002120972, 0.000137329,
+ -0.000045776, -0.001205444, 0.003051758, -0.019577026,
+ 0.012115479, -0.137298584, -0.069168091, -0.890090942,
+ 1.048156738, 0.246505737, 0.152069092, 0.016708374,
+ 0.027725220, -0.000869751, 0.002014160, 0.000122070,
+ -0.000061035, -0.001296997, 0.002883911, -0.020690918,
+ 0.009231567, -0.140670776, -0.088775635, -0.913055420,
+ 1.031936646, 0.221984863, 0.151962280, 0.012420654,
+ 0.026840210, -0.001266479, 0.001907349, 0.000106812,
+ -0.000061035, -0.001388550, 0.002700806, -0.021789551,
+ 0.006134033, -0.143676758, -0.109161377, -0.935195923,
+ 1.014617920, 0.198059082, 0.151306152, 0.008316040,
+ 0.025909424, -0.001617432, 0.001785278, 0.000106812,
+ -0.000076294, -0.001480103, 0.002487183, -0.022857666,
+ 0.002822876, -0.146255493, -0.130310059, -0.956481934,
+ 0.996246338, 0.174789429, 0.150115967, 0.004394531,
+ 0.024932861, -0.001937866, 0.001693726, 0.000091553,
+ -0.000076294, -0.001586914, 0.002227783, -0.023910522,
+ -0.000686646, -0.148422241, -0.152206421, -0.976852417,
+ 0.976852417, 0.152206421, 0.148422241, 0.000686646,
+ 0.023910522, -0.002227783, 0.001586914, 0.000076294,
+ -0.000091553, -0.001693726, 0.001937866, -0.024932861,
+ -0.004394531, -0.150115967, -0.174789429, -0.996246338,
+ 0.956481934, 0.130310059, 0.146255493, -0.002822876,
+ 0.022857666, -0.002487183, 0.001480103, 0.000076294,
+ -0.000106812, -0.001785278, 0.001617432, -0.025909424,
+ -0.008316040, -0.151306152, -0.198059082, -1.014617920,
+ 0.935195923, 0.109161377, 0.143676758, -0.006134033,
+ 0.021789551, -0.002700806, 0.001388550, 0.000061035,
+ -0.000106812, -0.001907349, 0.001266479, -0.026840210,
+ -0.012420654, -0.151962280, -0.221984863, -1.031936646,
+ 0.913055420, 0.088775635, 0.140670776, -0.009231567,
+ 0.020690918, -0.002883911, 0.001296997, 0.000061035,
+ -0.000122070, -0.002014160, 0.000869751, -0.027725220,
+ -0.016708374, -0.152069092, -0.246505737, -1.048156738,
+ 0.890090942, 0.069168091, 0.137298584, -0.012115479,
+ 0.019577026, -0.003051758, 0.001205444, 0.000045776,
+ -0.000137329, -0.002120972, 0.000442505, -0.028533936,
+ -0.021179199, -0.151596069, -0.271591187, -1.063217163,
+ 0.866363525, 0.050354004, 0.133590698, -0.014801025,
+ 0.018463135, -0.003173828, 0.001113892, 0.000045776,
+ -0.000152588, -0.002243042, -0.000030518, -0.029281616,
+ -0.025817871, -0.150497437, -0.297210693, -1.077117920,
+ 0.841949463, 0.032379150, 0.129562378, -0.017257690,
+ 0.017349243, -0.003280640, 0.001037598, 0.000030518,
+ -0.000167847, -0.002349854, -0.000549316, -0.029937744,
+ -0.030609131, -0.148773193, -0.323318481, -1.089782715,
+ 0.816864014, 0.015228271, 0.125259399, -0.019531250,
+ 0.016235352, -0.003372192, 0.000961304, 0.000030518,
+ -0.000198364, -0.002456665, -0.001098633, -0.030532837,
+ -0.035552979, -0.146362305, -0.349868774, -1.101211548,
+ 0.791213989, -0.001068115, 0.120697021, -0.021575928,
+ 0.015121460, -0.003417969, 0.000885010, 0.000030518,
+ -0.000213623, -0.002578735, -0.001693726, -0.031005859,
+ -0.040634155, -0.143264771, -0.376800537, -1.111373901,
+ 0.765029907, -0.016510010, 0.115921021, -0.023422241,
+ 0.014022827, -0.003463745, 0.000808716, 0.000030518,
+ -0.000244141, -0.002685547, -0.002334595, -0.031387329,
+ -0.045837402, -0.139450073, -0.404083252, -1.120223999,
+ 0.738372803, -0.031082153, 0.110946655, -0.025085449,
+ 0.012939453, -0.003479004, 0.000747681, 0.000015259,
+ -0.000259399, -0.002792358, -0.003005981, -0.031661987,
+ -0.051132202, -0.134887695, -0.431655884, -1.127746582,
+ 0.711318970, -0.044784546, 0.105819702, -0.026535034,
+ 0.011886597, -0.003479004, 0.000686646, 0.000015259,
+ -0.000289917, -0.002899170, -0.003723145, -0.031814575,
+ -0.056533813, -0.129577637, -0.459472656, -1.133926392,
+ 0.683914185, -0.057617188, 0.100540161, -0.027801514,
+ 0.010848999, -0.003463745, 0.000625610, 0.000015259,
+ -0.000320435, -0.002990723, -0.004486084, -0.031845093,
+ -0.061996460, -0.123474121, -0.487472534, -1.138763428,
+ 0.656219482, -0.069595337, 0.095169067, -0.028884888,
+ 0.009841919, -0.003433228, 0.000579834, 0.000015259,
+ -0.000366211, -0.003082275, -0.005294800, -0.031738281,
+ -0.067520142, -0.116577148, -0.515609741, -1.142211914,
+ 0.628295898, -0.080688477, 0.089706421, -0.029785156,
+ 0.008865356, -0.003387451, 0.000534058, 0.000015259,
+ -0.000396729, -0.003173828, -0.006118774, -0.031478882,
+ -0.073059082, -0.108856201, -0.543823242, -1.144287109,
+ 0.600219727, -0.090927124, 0.084182739, -0.030517578,
+ 0.007919312, -0.003326416, 0.000473022, 0.000015259
+};
diff --git a/mpeglib/lib/splay/synthesis.cpp b/mpeglib/lib/splay/synthesis.cpp
new file mode 100644
index 00000000..699037c9
--- /dev/null
+++ b/mpeglib/lib/splay/synthesis.cpp
@@ -0,0 +1,73 @@
+/*
+ header for synthesis
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "synthesis.h"
+
+
+#include <iostream>
+
+using namespace std;
+
+Synthesis::Synthesis() {
+
+ int i;
+ outpos=0;
+ calcbufferoffset=15;
+ currentcalcbuffer=0;
+
+ for(i=CALCBUFFERSIZE-1;i>=0;i--)
+ calcbuffer[LS][0][i]=calcbuffer[LS][1][i]=
+ calcbuffer[RS][0][i]=calcbuffer[RS][1][i]=0.0;
+
+ initialize_dct64();
+ initialize_dct64_downsample();
+}
+
+
+Synthesis::~Synthesis() {
+}
+
+
+void Synthesis::doSynth(int lDownSample,int lOutputStereo,
+ REAL *fractionL,REAL *fractionR) {
+ switch(lDownSample) {
+ case false:
+ synth_Std(lOutputStereo,fractionL,fractionR);
+ break;
+ case true:
+ synth_Down(lOutputStereo,fractionL,fractionR);
+ break;
+ default:
+ cout << "unknown downsample parameter"<<lDownSample<<endl;
+ exit(0);
+ }
+}
+
+
+void Synthesis::doMP3Synth(int lDownSample,int lOutputStereo,
+ REAL in[2][SSLIMIT][SBLIMIT]) {
+
+ switch(lDownSample) {
+ case false:
+ synthMP3_Std(lOutputStereo,in);
+ break;
+ case true:
+ synthMP3_Down(lOutputStereo,in);
+ break;
+ default:
+ cout << "unknown downsample parameter:"<<lDownSample<<endl;
+ exit(0);
+ }
+}
+
+
diff --git a/mpeglib/lib/splay/synthesis.h b/mpeglib/lib/splay/synthesis.h
new file mode 100644
index 00000000..801d658e
--- /dev/null
+++ b/mpeglib/lib/splay/synthesis.h
@@ -0,0 +1,95 @@
+/*
+ header for synthesis
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __SYNTHESIS_H
+#define __SYNTHESIS_H
+
+#include "common.h"
+#include "dct.h"
+
+#define CALCBUFFERSIZE 512
+// AIX seems to have FRAMESIZE defined
+#undef FRAMESIZE
+
+#define FRAMESIZE (2*2*2*32*18)
+
+
+class Synthesis {
+
+ //
+ // Subbandsynthesis two calcbuffers for every channel, and two channels.
+ // calcbufferL[0]=calcbuffer[0]
+ // calcbufferL[1]=calcbuffer[1]
+ // calcbufferR[0]=calcbuffer[2]
+ // calcbufferR[1]=calcbuffer[3]
+ ATTR_ALIGN(64) REAL calcbuffer[2][2][CALCBUFFERSIZE];
+ ATTR_ALIGN(64) int currentcalcbuffer,calcbufferoffset;
+ static ATTR_ALIGN(64) const REAL filter[512];
+ ATTR_ALIGN(64) REAL out[FRAMESIZE];
+ int outpos;
+
+ public:
+ Synthesis();
+ ~Synthesis();
+
+ // mpeg1,2
+ void doSynth(int lDownSample,int lOutputStereo,
+ REAL *fractionL,REAL *fractionR);
+
+ void doMP3Synth(int lDownSample,int lOutputStereo,
+ REAL in[2][SSLIMIT][SBLIMIT]);
+
+ // put mpeg to raw
+ inline void putraw(REAL val) {
+ out[outpos++]=val;
+ }
+
+ inline REAL* getOutputData() { return out; }
+ inline void clearrawdata() { outpos=0; }
+ inline int getLen() { return outpos; }
+
+
+ private:
+ void synth_Down(int lOutputStereo,REAL *fractionL,REAL *fractionR);
+ void synth_Std(int lOutputStereo,REAL *fractionL,REAL *fractionR);
+
+ void synthMP3_Down(int lOutputStereo,REAL hout [2][SSLIMIT][SBLIMIT]);
+ void synthMP3_Std(int lOutputStereo,REAL hout [2][SSLIMIT][SBLIMIT]);
+
+ inline void nextOffset() {
+ calcbufferoffset++;
+ calcbufferoffset&=0xf;
+ /*
+ if (calcbufferoffset<15) {
+ calcbufferoffset++;
+ } else {
+ calcbufferoffset=0;
+ }
+ */
+ currentcalcbuffer^=1;
+ }
+
+
+ void computebuffer_Std(REAL *fraction,REAL buffer[2][CALCBUFFERSIZE]);
+ void generate_Std(void);
+ void generatesingle_Std(void);
+
+ void computebuffer_Down(REAL *fraction,REAL buffer[2][CALCBUFFERSIZE]);
+ void generate_Down(void);
+ void generatesingle_Down(void);
+
+
+};
+
+#endif
diff --git a/mpeglib/lib/splay/window.cpp b/mpeglib/lib/splay/window.cpp
new file mode 100644
index 00000000..b95dbdcf
--- /dev/null
+++ b/mpeglib/lib/splay/window.cpp
@@ -0,0 +1,70 @@
+/*
+ wrapper for window functions
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "mpegsound.h"
+
+
+static int windowInit=0;
+
+ATTR_ALIGN(64) REAL win[4][36];
+ATTR_ALIGN(64) REAL winINV[4][36];
+
+
+void initialize_win() {
+ if (windowInit==true) {
+ return;
+ }
+ windowInit=true;
+
+ int i;
+
+ for(i=0;i<18;i++) {
+ /*
+ win[0][i]=win[1][i]=0.5*sin(PI_72*(double)(2*i+1))/
+ cos(PI_72*(double)(2*i+19));
+ */
+ win[0][i]=win[1][i]=0.5*sin(PI_72*(double)(2*(i+0)+1))/cos(MY_PI * (double) (2*(i+0) +19) / 72.0 );
+ win[0][i+18] = win[3][i+18] = 0.5 * sin( MY_PI / 72.0 * (double) (2*(i+18)+1) ) / cos ( MY_PI * (double) (2*(i+18)+19) / 72.0 );
+ }
+ /*
+ for(;i<36;i++) {
+ win[0][i]=win[3][i]=0.5*sin(PI_72*(double)(2*i+1))/cos(PI_72*(double)(2*i+19));
+
+ }
+ */
+ for(i=0;i<6;i++) {
+ win[1][i+18]=0.5/cos(MY_PI*(double)(2*(i+18)+19)/72.0);
+ win[3][i+12]=0.5/cos(MY_PI*(double)(2*(i+12)+19)/72.0);
+ win[1][i+24]=0.5*sin(PI_24*(double)(2*i+13))/cos(MY_PI*(double)(2*(i+24)+19)/72.0);
+ win[1][i+30]=win[3][i]=0.0;
+ win[3][i+6 ]=0.5*sin(PI_24*(double)(2*i+1))/cos(MY_PI*(double)(2*(i+6)+19)/72.0);
+ }
+ for(i=0;i<12;i++)
+ win[2][i]=0.5*sin(PI_24*(double)(2*i+1))/cos(MY_PI*(double)(2*i+7)/24.0);
+
+ int j;
+
+ for(j=0;j<4;j++) {
+ int len[4] = { 36,36,12,36 };
+ for(i=0;i<len[j];i+=2)
+ winINV[j][i] = + win[j][i];
+ for(i=1;i<len[j];i+=2)
+ winINV[j][i] = - win[j][i];
+ }
+
+
+}
+
+
+inline REAL* getSplayWindow(int nr) { return win[nr]; }
+inline REAL* getSplayWindowINV(int nr) { return winINV[nr]; }
diff --git a/mpeglib/lib/tplay/CHANGES b/mpeglib/lib/tplay/CHANGES
new file mode 100644
index 00000000..aad2446d
--- /dev/null
+++ b/mpeglib/lib/tplay/CHANGES
@@ -0,0 +1,154 @@
+Version 0.1, 2.4.1997:
+
+ - the first released version
+
+5.4.1997:
+
+ - audio sync added before changing parameters
+
+Version 0.2, 9.4.1997:
+
+ - it was useless to start producer as a thread;
+ it was made that way just for historical reasons.
+ only consumer is threaded now. this may result
+ as more robust behaviour.
+
+ - there are min and max sizes for block now. i'm
+ not sure yet what size for a block and the audio
+ buffer would be good. needs more research.
+
+ - fill_buffer function. fills the audio buffer
+ before use.
+
+Version 0.2.1, 15.4.1997:
+
+ - signal() seems to be a bad idea in a threaded
+ application like this. causes kernel oops in
+ the sound driver function audio_write (sometimes).
+ let's have faith on the kernel and remove it.
+
+Version 0.2.2, 17.4.1997 morning:
+
+ - the last block was written from very wrong point.
+
+ - block counting added. this makes stream ending
+ simpler and (hopefully) more robust.
+
+ - first lines for handling underflow (buffer empty)
+ situation.
+
+Version 0.3, 17.4.1997 afternoon:
+
+ - underflow handling should work now.
+
+ - function buffer_usage added. returns buffer usage
+ in percents. nowhere used yet.
+
+ - minimum block size increased to 16k.
+
+Version 0.3.1, 19.4.1997:
+
+ - GNU style options.
+
+ - option -v (or --version) added.
+
+ - buffer usage option -u (or --usage) added.
+
+Version 0.4, 2.5.1997:
+
+ - support for RIFF/WAVE (WAV) and Sun audio
+ (AU) files.
+
+ - swap endianness flag -x (or --swap) added.
+
+ - verbose mode flag -V (or --verbose) added.
+
+ - force raw flag -r (or --raw) added. WAV- or
+ AU-file headers are ignored if this flag is set.
+
+Version 0.4.1, 3.5.1997:
+
+ - sun header gives odd aligned starting point
+ for sample. temporary fix.
+
+ - read_big_endian_long returned wrong value if
+ sampling rate was 44100. this caused tplay
+ not to work with that speed when playing sun
+ audio or wav file. fixed now.
+
+Version 0.4.2, 7.5.1997:
+
+ - read_big_endian_long and similar functions:
+ parameter's type was char* and that was a bug.
+ changed to byte* (unsigned char *). conversion
+ should also be saner now.
+
+ - force playing -f (or --force) flag added. this
+ makes tplay to ignore sound driver's results
+ when changing parameters.
+
+Version 0.4.3, 12.5.1997:
+
+ - binary is statically linked to LinuxThreads
+ version 0.6 now.
+
+ - print sun header comment if verbose is requested.
+
+ - sun audio file's data stream starting pointer
+ is read and set from the header.
+
+ - to avoid rounding errors when playing 16bit
+ and/or stereo (au or wav) sample, data section
+ is moved now to the beginning of buffer before
+ playing.
+
+Version 0.5, 23.10.1997:
+
+ - set_audio_parameters() partly rewritten and
+ changes to open_audio().
+
+ - in some WAV-files, data-portion is not started
+ with 'data'-magic but 'INFO' instead. some
+ players don't even check that so tplay prints
+ just a warning message now if neither of these
+ magics exists.
+
+Version 0.5.1, 25.10.1997:
+
+ - printing of buffer usage changed from producer
+ to consumer. this makes this silly feature a bit
+ more informative as it is still active after the
+ producer has stopped. underflow situation (when
+ the big buffer needs to be refilled) is also
+ possible to show now.
+
+Version 0.5.2, 9.5.1998:
+
+ - Jerko Golubovic <jerko.golubovic@public.srce.hr>
+ kindly modified the code to support those soundcards
+ that may result slightly different sampling rate
+ than requested. he also provided RPM of tplay.
+
+ - added -D (or --device=DEVICE) flag for setting
+ audio device to be used.
+
+ - added feature to -B (or --buffer-size=SIZE) flag.
+ buffer size can be given in seconds now, too.
+
+Version 0.5.3, 11.5.1998:
+
+ - rewrite of playing routine to support multiple
+ sound samples from command line.
+
+Version 0.5.4, 19.5.1998:
+
+ - added -l (or --loop) flag to support looping sound
+ samples.
+
+ - sun port.
+
+Version 0.5.5, 24.5.1998:
+
+ - added environment variable TPLAYDEV, which sets the
+ audio device to be used. decoding of command line
+ options is moved from main() to another function.
diff --git a/mpeglib/lib/tplay/Makefile.am b/mpeglib/lib/tplay/Makefile.am
new file mode 100644
index 00000000..c83b25ea
--- /dev/null
+++ b/mpeglib/lib/tplay/Makefile.am
@@ -0,0 +1,15 @@
+# libtplay - Makefile.am
+
+
+EXTRA_DIST = CHANGES README
+
+INCLUDES = $(all_includes)
+
+
+noinst_LTLIBRARIES = libtplay.la
+
+noinst_HEADERS = tplayfunctions.h
+
+libtplay_la_SOURCES = au.cpp \
+ tplayfunctions.cpp wav.cpp
+
diff --git a/mpeglib/lib/tplay/README b/mpeglib/lib/tplay/README
new file mode 100644
index 00000000..275d6a9f
--- /dev/null
+++ b/mpeglib/lib/tplay/README
@@ -0,0 +1,95 @@
+This is a buffered audio player for Linux. POSIX-thread library is
+used. This is still considered BETA software and may not work as
+expected. Please mail me for bug reports, opinions or suggestions.
+
+This is primarily made for use with MPEG-decoders. They typically
+consume lots of CPU-time and some kind of audio buffer is needed to
+reduce cutting while writing to audio device. You can also play any
+audio files with tplay or use it with any program that writes audio
+data to standard out.
+
+RIFF/WAVE (WAV) and Sun audio (AU) file headers are recognized by
+now.
+
+Binary:
+
+In the source tree there is a readily compiled binary that is build
+under Linux/ELF 2.0.30 with libc 5.4.20 and LinuxThreads 0.6 (the
+thread library is statically linked).
+
+Command line options:
+
+ tplay [-hvVmuxrf] [-s Hz] [-b 8|16] [-B kilobytes] [filename]
+
+ -h, --help Print help, then exit
+ -v, --version Print version, then exit
+ -V, --verbose Print useful information about the sample
+ -x, --swap Swap endianness
+ -r, --raw Force raw audio format. Ignore headers.
+ -f, --force Force playing with any parameters
+ -m, --mono Mono sample
+ -u, --usage Print buffer usage while playing
+ -s, --speed=SPEED Sample speed (Hz)
+ -b, --bytes=BYTES Bytes in a sample
+ -B, --buffer-size=SIZE Buffer size in (kB)
+
+Buffer size is defaulted to 512k. It is about 3 seconds CD audio
+(44100Hz/sample, 16bytes, stereo). If filename is not given, standard
+input is used. If -x (or --swap) flag is set, the byte order of
+audio sample is swapped before playing. The default is Intel little-
+endian which is mostly used in x86 machines. The world outside Intel
+is big-endian.
+Option -r (or --raw) forces tplay to handle the sample as an raw
+PCM audio sample. Sun audio or WAV headers are ignored.
+
+Requirements:
+
+ - Linux 2.0 or newer with audio card support
+ - POSIX thread library
+ - Audio card
+
+There are several POSIX thread libraries available. I used
+LinuxThreads by Xavier Leroy (Xavier.Leroy@inria.fr). LinuxThreads
+library use clone() that is provided by Linux 2.0 kernel.
+
+The code:
+
+tplay starts one thread, named consumer, that reads circular audio
+buffer and writes it to audio device. The producer is a function that
+runs in parallel with the consumer and its task is to read the sample
+file or standard input and write this data to audio buffer to meet
+consumer's needs. Usually, the buffer is full but on the times when
+CPU-time is suddenly needed for other processes (usually: disk
+read/write), the producer can't write fast enough and consumer can use
+the buffer to keep audio stream uninterrupted. If the buffer is used
+and the producer is still unable to feed it fast enough, underflow
+situation is met and consumer waits for awhile (typically: one second)
+for the producer to fill the buffer again.
+
+Building:
+
+If you want to link tplay with static libpthread library, edit
+Makefile and uncomment preferred LIBS-setting there. Type:
+
+ - make
+ - make install
+ - make install.man
+
+Thanks:
+
+Jerko Golubovic <jerko.golubovic@public.srce.hr>
+Jukka Palviainen <oh3kjt@ele.tut.fi>
+
+TODO:
+
+Find out the best sizes for the audio buffer and one block.
+Better documentation.
+Better RIFF/WAVE checking.
+
+Ilkka Karvinen
+ik@iki.fi
+
+
+
+
+
diff --git a/mpeglib/lib/tplay/au.cpp b/mpeglib/lib/tplay/au.cpp
new file mode 100644
index 00000000..8880515f
--- /dev/null
+++ b/mpeglib/lib/tplay/au.cpp
@@ -0,0 +1,100 @@
+/*
+ * tplay - buffered audio player
+ *
+ * (c) 1997 ilkka karvinen <ik@iki.fi>
+ *
+ * Copyright under the GNU GENERAL PUBLIC LICENSE
+ * (see the file COPYING in this directory)
+ *
+ *
+ * SunOS audio file header functions.
+ * Reference: http://www.wotsit.org
+ */
+
+#include "tplayfunctions.h"
+
+/* read_au returns zero if Sun audio file format is found. */
+int read_au(struct info_struct* info,char * buffer) {
+ DWORD magic, start, end, encoding, speed, channels;
+ int bits;
+
+
+ /* If '.snd'-header exits, this should be an au-file */
+ magic = read_big_endian_long(buffer);
+ if (magic != SUN_MAGIC)
+ return (1);
+
+ start = read_big_endian_long(buffer + 0x04);
+ end = read_big_endian_long(buffer + 0x08);
+ encoding = read_big_endian_long(buffer + 0x0C);
+ speed = read_big_endian_long(buffer + 0x10);
+ channels = read_big_endian_long(buffer + 0x14);
+
+#ifdef DEBUG
+ printf("Sun audio file.\nspeed: %ld, start: %ld, end: %ld, \
+encoding: %X, channels: %ld\n",
+ speed, start, end, encoding, channels);
+ fflush(stdout);
+#endif
+
+ bits = DEFAULT_BITS;
+ switch (encoding) {
+ case 1:
+ die("8-bit ISDN u-law Sun audio file not supported");
+ break;
+ case 2:
+ bits = 8;
+ break;
+ case 3:
+ bits = 16;
+ break;
+ case 4:
+ die("24-bit linear PCM Sun audio file not supported");
+ break;
+ case 5:
+ die("32-bit linear PCM Sun audio file not supported");
+ break;
+ case 6:
+ die("32-bit IEEE floating point Sun audio file not supported");
+ break;
+ case 7:
+ die("64-bit IEEE floating point Sun audio file not supported");
+ break;
+ case 23:
+ die("8-bit ISDN u-law compressed(G.721 ADPCM) Sun audio file \
+not supported");
+ break;
+ default:
+ errdie("Unknown Sun audio file");
+ break;
+ }
+
+ info->filetype = SUN_FILE;
+
+ /* Set audio parameters */
+ info->speed = (int) speed;
+ info->bits = bits;
+ info->channels = (int) channels;
+
+ if (info->verbose) {
+ printf("Sun audio file: %ld samples/s, %d bits, %d channel(s).\n",
+ info->speed, info->bits, info->channels);
+ /*
+ if ((comment_size = start - SUN_HDRSIZE) > 0) {
+ printf("Header info: ");
+ for (i = 0; i < comment_size; i++)
+ nice_fputc((int) buffer[SUN_HDRSIZE + i], stdout);
+ printf("\n");
+ }
+ */
+ }
+
+ /* Move data to start from the beginning of the buffer. */
+ /* This is to ensure the correct behaviour of rounding when 16bits */
+ /* and/or stereo sample is to be played. */
+ memmove(buffer, buffer + (start + 1), info->blocksize - start - 1);
+
+ info->headerskip = (int) (start + 1);
+
+ return (0);
+}
diff --git a/mpeglib/lib/tplay/tplayfunctions.cpp b/mpeglib/lib/tplay/tplayfunctions.cpp
new file mode 100644
index 00000000..c105f4a2
--- /dev/null
+++ b/mpeglib/lib/tplay/tplayfunctions.cpp
@@ -0,0 +1,84 @@
+/*
+ * tplay - buffered audio player
+ *
+ * (c) 1997 ilkka karvinen <ik@iki.fi>
+ *
+ * Copyright under the GNU GENERAL PUBLIC LICENSE
+ * (see the file COPYING in this directory)
+ *
+ *
+ * common functions
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "tplayfunctions.h"
+
+DWORD read_big_endian_long(char * buf)
+{
+ DWORD byte0, byte1, byte2, byte3;
+ unsigned char* buffer=(unsigned char*) buf;
+
+ byte0 = (DWORD) buffer[0];
+ byte1 = (DWORD) buffer[1];
+ byte2 = (DWORD) buffer[2];
+ byte3 = (DWORD) buffer[3];
+ return (byte0 << 24 | byte1 << 16 | byte2 << 8 | byte3);
+}
+
+void write_big_endian_long(char * buf, DWORD value)
+{
+ unsigned char* buffer=(unsigned char*) buf;
+ buffer[0] = (unsigned char) (value >> 24 & 0xFF);
+ buffer[1] = (unsigned char) (value >> 16 & 0xFF);
+ buffer[2] = (unsigned char) (value >> 8 & 0xFF);
+ buffer[3] = (unsigned char) (value & 0xFF);
+}
+
+DWORD read_little_endian_long(char* buf) {
+ DWORD byte0, byte1, byte2, byte3;
+ unsigned char* buffer=(unsigned char*) buf;
+
+ byte0 = (DWORD) buffer[0];
+ byte1 = (DWORD) buffer[1];
+ byte2 = (DWORD) buffer[2];
+ byte3 = (DWORD) buffer[3];
+ return (byte3 << 24 | byte2 << 16 | byte1 << 8 | byte0);
+}
+
+WORD read_little_endian_word(char * buf)
+{
+ WORD byte0, byte1;
+ unsigned char* buffer=(unsigned char*) buf;
+
+ byte0 = (WORD) buffer[0];
+ byte1 = (WORD) buffer[1];
+ return (byte1 << 8 | byte0);
+}
+
+void errprintf(char *fmt,...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+}
+
+
+
+void die(const char *str)
+{
+ fprintf(stderr, "%s: \n", str);
+ exit(-1);
+}
+
+void errdie(const char *str)
+{
+ fprintf(stderr, "Error: %s\n", str);
+ exit(-1);
+}
+
+
+
diff --git a/mpeglib/lib/tplay/tplayfunctions.h b/mpeglib/lib/tplay/tplayfunctions.h
new file mode 100644
index 00000000..0c159847
--- /dev/null
+++ b/mpeglib/lib/tplay/tplayfunctions.h
@@ -0,0 +1,128 @@
+/*
+ * tplay - buffered audio player header file
+ *
+ * (c) 1997 ilkka karvinen <ik@iki.fi>
+ *
+ * Copyright under the GNU GENERAL PUBLIC LICENSE
+ * (see the file COPYING in this directory)
+ *
+ */
+
+#ifndef __TPLAYCONTROL_H
+#define __TPLAYCONTROL_H
+
+extern "C" {
+#include <stdio.h>
+#include <string.h>
+}
+
+/* tplay version */
+#define MAJOR_VERSION 0
+#define MINOR_VERSION 5
+#define PATCHLEVEL 5
+
+/* Default audio parameters */
+#define DEFAULT_BITS 16
+#define DEFAULT_SPEED 44100
+#define DEFAULT_CHANNELS 2
+
+/* Audio memory pool. 512k is the default. */
+#define BUFFER_SIZE 0x80000
+
+/* The minimum and maximum buffer block sizes. */
+#if 0
+#define MIN_BLOCK_SIZE 0x4000 /* 16k */
+#else
+#define MIN_BLOCK_SIZE 4096
+#endif
+#define MAX_BLOCK_SIZE 0x10000 /* 64k */
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+/* The maximum retry count for buffer fill tries. */
+#define RETRY_COUNT 5
+
+/* Magics. Little-endian. */
+#define RIFF_MAGIC 0x46464952 /* ASCII: 'RIFF' */
+#define WAVE_MAGIC 0x45564157 /* ASCII: 'WAVE' */
+#define DATA_MAGIC 0x61746164 /* ASCII: 'data' */
+#define INFO_MAGIC 0x4f464e49 /* ASCII: 'INFO' */
+#define SUN_MAGIC 0x2e736e64 /* ASCII: '.snd' */
+
+/* Magics. Big-endian. */
+#define SUN_INV_MAGIC 0x646e732e /* ASCII: '.snd' */
+
+/* Sun headersize */
+#define SUN_HDRSIZE 24
+
+/* File types */
+#define UNKNOWN_FILE 0
+#define RIFF_FILE 1
+#define SUN_FILE 2
+
+typedef unsigned long DWORD;
+typedef unsigned short WORD;
+
+/* Circular buffer info structure of audio data blocks. */
+/* declared in tplay.c */
+struct info_struct {
+ char *buffer; /* the audio data */
+ char *firstblock; /* pointer to the first block */
+ int readblock, writeblock; /* reading and writing block number */
+ long readcount, writecount;
+ int alldone;
+ int in_seconds;
+ double seconds;
+ int blocksize; /* size of one block */
+ int buffer_size; /* size of the buffer */
+ int number_of_blocks;
+ int last_block; /* -1 if not the last block */
+ int bytes_on_last_block;
+ int overflow;
+ int underflow;
+ int swap;
+ int forceraw;
+ int force;
+ int filetype;
+ int headerskip;
+ int audioset;
+ int show_usage;
+ DWORD speed;
+ int channels;
+ int bits;
+ char *progname;
+ char *device; /* Audio device name */
+ int loop;
+ int verbose;
+ int optind;
+};
+
+/* au.c */
+ int read_au(struct info_struct* info, char * buffer);
+
+/* wav.c */
+ int read_wav(struct info_struct* info, char * buffer);
+
+/* common.c */
+ DWORD read_big_endian_long(char * buffer);
+ void write_big_endian_long(char * buffer, DWORD value);
+ DWORD read_little_endian_long(char * buffer);
+ WORD read_little_endian_word(char * buffer);
+ void errprintf(char *fmt,...);
+ void warning(char *str);
+ void warning2(char *str1, char *str2);
+ void die(const char *str);
+ void errdie(const char *str);
+ void open_audio();
+ void set_audio_parameters();
+ void sync_audio(void);
+ void reset_audio(void);
+ void post_audio(void);
+ void destroy_buffer(void);
+ void nice_fputc(int c, FILE * fp);
+
+
+#endif
diff --git a/mpeglib/lib/tplay/wav.cpp b/mpeglib/lib/tplay/wav.cpp
new file mode 100644
index 00000000..ca284c37
--- /dev/null
+++ b/mpeglib/lib/tplay/wav.cpp
@@ -0,0 +1,91 @@
+/*
+ * tplay - buffered audio player
+ *
+ * (c) 1997 ilkka karvinen <ik@iki.fi>
+ *
+ * Copyright under the GNU GENERAL PUBLIC LICENSE
+ * (see the file COPYING in this directory)
+ *
+ *
+ * RIFF/WAVE file header checking.
+ * check_wav returns zero if RIFF/WAVE file format is found.
+ * Reference: http://www.wotsit.demon.co.uk/formats/wav/wav.txt
+ */
+
+#include "tplayfunctions.h"
+
+#include <iostream>
+
+using namespace std;
+
+int read_wav(struct info_struct* info, char * buffer) {
+ WORD format, channels, bits;
+ DWORD magic, samples_per_second, length, data_length;
+
+ magic = read_little_endian_long(buffer);
+ if (magic != RIFF_MAGIC) /* RIFF file? */
+ return (1);
+ magic = read_little_endian_long(buffer + 0x08);
+ if (magic != WAVE_MAGIC) /* WAVE file? */
+ return (1);
+ magic = read_little_endian_long(buffer + 0x24);
+ if ((magic != DATA_MAGIC) && (magic != INFO_MAGIC)) { /* data-portion there? */
+ cout << "Unknown WAV-header magic. Continuing anyway."<<endl;
+ }
+
+ length = read_little_endian_long(buffer + 0x10);
+
+ /* Subchunk length should be 16 here */
+ if (length != 16)
+ errdie("Unknown RIFF/WAVE header");
+
+ format = read_little_endian_word(buffer + 0x14);
+
+ switch (format) {
+ case 0x0001: /* PCM format */
+ break;
+ case 0x0101: /* mu-law */
+ die("Mu-law RIFF/WAVE audio file not supported");
+ break;
+ case 0x0102: /* a-law */
+ die("A-law RIFF/WAVE audio file not supported");
+ break;
+ case 0x0103: /* ADPCM */
+ die("ADPCM RIFF/WAVE audio file not supported");
+ break;
+ default:
+ errdie("Unknown RIFF/WAVE audio file format");
+ break;
+ }
+
+ info->filetype = RIFF_FILE;
+
+ channels = read_little_endian_word(buffer + 0x16);
+ samples_per_second = read_little_endian_long(buffer + 0x18);
+ cout << "samples_per_second:"<<samples_per_second<<endl;
+ bits = read_little_endian_word(buffer + 0x22);
+
+ if (bits == 12)
+ die("12 bits per sample not supported");
+
+ data_length = read_little_endian_long(buffer + 0x28);
+
+ /* Set audio parameters */
+ info->speed = (int) samples_per_second;
+ info->bits = (int) bits;
+ info->channels = (int) channels;
+
+ if (info->verbose)
+ printf("RIFF/WAVE audio file: %ld samples/s, %d bits, %d channel(s).\n",
+ info->speed, info->bits, info->channels);
+
+ /* Move data to start from the beginning of the buffer. */
+ /* This is to ensure the correct behaviour of rounding when 16bits */
+ /* and/or stereo sample is to be played. */
+ memmove(buffer, buffer + 0x2c, info->blocksize - 0x2c);
+
+ /* Save audio sample starting point */
+ info->headerskip = 0x2c;
+
+ return (0);
+}
diff --git a/mpeglib/lib/util/Makefile.am b/mpeglib/lib/util/Makefile.am
new file mode 100644
index 00000000..c73de2cc
--- /dev/null
+++ b/mpeglib/lib/util/Makefile.am
@@ -0,0 +1,61 @@
+# player - Makefile.am
+
+SUBDIRS = mmx abstract audio file render
+
+
+INCLUDES = $(all_includes)
+
+
+
+THIS_EXTRALIBS = abstract/libutilabstract.la \
+ audio/libaudio.la \
+ file/libsimplefile.la \
+ render/libutilrender.la \
+ mmx/libmmx.la
+
+
+noinst_LTLIBRARIES = libutil.la
+
+noinst_HEADERS = syncClockMPEG.h
+
+kmpgincludedir = $(includedir)/$(THIS_LIB_NAME)/util
+
+kmpginclude_HEADERS = timeStamp.h dynBuffer.h \
+ timeStampArray.h syncClock.h timeWrapper.h
+
+
+
+libutil_la_SOURCES = timeStamp.cpp \
+ timeStampArray.cpp \
+ dynBuffer.cpp syncClock.cpp \
+ syncClockMPEG.cpp timeWrapper.cpp
+
+libutil_la_LIBADD = $(THIS_EXTRALIBS)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/lib/util/abstract/Makefile.am b/mpeglib/lib/util/abstract/Makefile.am
new file mode 100644
index 00000000..a5c49d6b
--- /dev/null
+++ b/mpeglib/lib/util/abstract/Makefile.am
@@ -0,0 +1,14 @@
+# player - Makefile.am
+
+
+INCLUDES = $(all_includes)
+
+noinst_LTLIBRARIES = libutilabstract.la
+
+kmpgincludedir = $(includedir)/$(THIS_LIB_NAME)/util/abstract
+
+kmpginclude_HEADERS = abs_thread.h threadQueue.h
+
+
+libutilabstract_la_SOURCES = abs_thread_sdl.cpp threadQueue.cpp
+
diff --git a/mpeglib/lib/util/abstract/abs_thread.h b/mpeglib/lib/util/abstract/abs_thread.h
new file mode 100644
index 00000000..f65445d8
--- /dev/null
+++ b/mpeglib/lib/util/abstract/abs_thread.h
@@ -0,0 +1,123 @@
+/*
+ abstraction for threads
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __ABS_THREAD_H
+#define __ABS_THREAD_H
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/**
+ This passed alle pthread_xxx calls to this interface, thus
+ it can be easier replaced with other thread "layers"
+
+ All posix pthread calls are conveterd to abs_thread.
+*/
+
+extern "C" {
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+}
+
+#define _ABS_BUSY EBUSY
+
+#ifndef SDL_WRAPPER
+// definitions for direct pthread support
+#include <pthread.h>
+
+typedef pthread_mutex_t abs_thread_mutex_t;
+typedef pthread_cond_t abs_thread_cond_t;
+typedef pthread_t abs_thread_t;
+
+
+
+#define abs_thread_cond_init(cond) pthread_cond_init(cond,NULL)
+#define abs_thread_cond_destroy(cond) pthread_cond_destroy(cond)
+#define abs_thread_cond_signal(cond) pthread_cond_signal(cond)
+#define abs_thread_cond_wait(cond,mutex) pthread_cond_wait(cond,mutex)
+
+// CREATE / JOIN THREAD
+
+#define abs_thread_create(thread,func,arg) pthread_create(thread,NULL,func,arg)
+#define abs_thread_join(th,thread_return) pthread_join(th,thread_return)
+
+// MUTEX FUNCTIONS
+
+#define abs_thread_mutex_lock(mutex) pthread_mutex_lock(mutex)
+#define abs_thread_mutex_unlock(mutex) pthread_mutex_unlock(mutex)
+#define abs_thread_mutex_init(mutex) pthread_mutex_init(mutex,NULL)
+#define abs_thread_mutex_destroy(mutex) pthread_mutex_destroy(mutex)
+
+#endif
+// not SDL_WRAPPER
+
+#ifdef SDL_WRAPPER
+
+
+// SDL SUPPORT DISABLED
+
+#if defined WIN32
+ #include <SDL_thread.h>
+ #include <SDL_mutex.h>
+#else
+ #include <SDL/SDL_thread.h>
+ #include <SDL/SDL_mutex.h>
+#endif
+
+
+typedef SDL_mutex* abs_thread_mutex_t;
+typedef SDL_cond* abs_thread_cond_t;
+typedef SDL_Thread* abs_thread_t;
+
+// SIGNAL FUNCTIONS
+// note we have _no_ cond attribut (not needed)
+int abs_thread_cond_init(abs_thread_cond_t* cond);
+int abs_thread_cond_destroy(abs_thread_cond_t *cond);
+
+int abs_thread_cond_signal(abs_thread_cond_t* cond);
+
+int abs_thread_cond_wait(abs_thread_cond_t* cond,
+ abs_thread_mutex_t *mutex);
+// CREATE / JOIN THREAD
+// Note: we have thread attribute
+int abs_thread_create(abs_thread_t* thread,
+ void * (*start_routine)(void *), void * arg);
+
+int abs_thread_join(abs_thread_t th,
+ void **thread_return);
+
+
+// MUTEX FUNCTIONS
+
+int abs_thread_mutex_lock(abs_thread_mutex_t *mutex);
+int abs_thread_mutex_trylock(abs_thread_mutex_t *mutex);
+int abs_thread_mutex_unlock(abs_thread_mutex_t *mutex);
+// not attribute!
+int abs_thread_mutex_init(abs_thread_mutex_t *mutex);
+
+int abs_thread_mutex_destroy(abs_thread_mutex_t *mutex);
+
+
+
+#endif
+//SDL_WRAPPER
+
+
+
+#endif
+
+
diff --git a/mpeglib/lib/util/abstract/abs_thread_sdl.cpp b/mpeglib/lib/util/abstract/abs_thread_sdl.cpp
new file mode 100644
index 00000000..13c9ce6c
--- /dev/null
+++ b/mpeglib/lib/util/abstract/abs_thread_sdl.cpp
@@ -0,0 +1,89 @@
+/*
+ abstraction for threads
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "abs_thread.h"
+
+
+// START SDL
+
+
+#ifdef SDL_WRAPPER
+
+
+
+int abs_thread_cond_init(abs_thread_cond_t* cond) {
+ *cond=SDL_CreateCond();
+ return (*cond != NULL);
+}
+
+int abs_thread_cond_destroy(abs_thread_cond_t *cond) {
+ SDL_DestroyCond(*cond);
+ return true;
+}
+
+
+int abs_thread_cond_signal(abs_thread_cond_t* cond) {
+
+ return SDL_CondSignal(*cond);
+}
+
+
+int abs_thread_cond_wait(abs_thread_cond_t* cond,
+ abs_thread_mutex_t* mutex) {
+ SDL_CondWait(*cond,*mutex);
+ return true;
+}
+
+
+// CREATE / JOIN THREAD
+int abs_thread_create(abs_thread_t* thread,
+ void * (*start_routine)(void *), void * arg) {
+ int (*func)(void *);
+ func=(int (*)(void *))start_routine;
+ *thread=SDL_CreateThread(func,arg);
+ return (*thread != NULL);
+}
+
+int abs_thread_join(abs_thread_t th,
+ void **thread_return) {
+ SDL_WaitThread(th,(int*)*thread_return);
+ return true;
+}
+
+
+// MUTEX FUNCTIONS
+
+int abs_thread_mutex_lock(abs_thread_mutex_t *mutex) {
+ return SDL_LockMutex(*mutex);
+}
+
+
+int abs_thread_mutex_unlock(abs_thread_mutex_t *mutex) {
+ return SDL_UnlockMutex(*mutex);
+}
+
+
+int abs_thread_mutex_init(abs_thread_mutex_t *mutex) {
+ *mutex=SDL_CreateMutex();
+ return true;
+}
+
+
+int abs_thread_mutex_destroy(abs_thread_mutex_t *mutex) {
+ SDL_DestroyMutex(*mutex);
+ return true;
+}
+
+
+
+#endif
diff --git a/mpeglib/lib/util/abstract/threadQueue.cpp b/mpeglib/lib/util/abstract/threadQueue.cpp
new file mode 100644
index 00000000..1b130ba9
--- /dev/null
+++ b/mpeglib/lib/util/abstract/threadQueue.cpp
@@ -0,0 +1,108 @@
+/*
+ fifo waitqueue for threads.(Multi-in, single out)
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include "threadQueue.h"
+
+#define _MAX_THREAD_IN_QUEUE 5
+
+#include <iostream>
+
+using namespace std;
+
+//
+// WaitThreadEntry class [START]
+//
+
+WaitThreadEntry::WaitThreadEntry() {
+ abs_thread_cond_init(&waitCond);
+}
+
+
+WaitThreadEntry::~WaitThreadEntry() {
+ abs_thread_cond_destroy(&waitCond);
+}
+
+//
+// WaitThreadEntry class [END]
+//
+
+
+ThreadQueue::ThreadQueue() {
+ waitThreadEntries=new WaitThreadEntry* [_MAX_THREAD_IN_QUEUE];
+ int i;
+ for(i=0;i<_MAX_THREAD_IN_QUEUE;i++) {
+ waitThreadEntries[i]=new WaitThreadEntry();
+ }
+ abs_thread_mutex_init(&queueMut);
+ insertPos=0;
+ removePos=0;
+ size=0;
+}
+
+
+ThreadQueue::~ThreadQueue() {
+ abs_thread_mutex_lock(&queueMut);
+ if (size != 0) {
+ cout << "Aieee! Make sure that all threads are out of ThreadQueue"<<endl;
+ exit(0);
+ }
+ int i;
+ for(i=0;i<_MAX_THREAD_IN_QUEUE;i++) {
+ delete waitThreadEntries[i];
+ }
+ delete [] waitThreadEntries;
+ abs_thread_mutex_unlock(&queueMut);
+ abs_thread_mutex_destroy(&queueMut);
+}
+
+
+void ThreadQueue::waitForExclusiveAccess() {
+ abs_thread_mutex_lock(&queueMut);
+ if (size == 0) {
+ abs_thread_mutex_unlock(&queueMut);
+ return;
+ }
+ // wait
+ size++;
+ if (size == _MAX_THREAD_IN_QUEUE) {
+ cout << "Aieee! ThreadQueue can only buffer:"<<_MAX_THREAD_IN_QUEUE<<endl;
+ exit(0);
+ }
+ abs_thread_cond_t* waitCond=&(waitThreadEntries[insertPos]->waitCond);
+ insertPos++;
+ // wrap counter
+ if (insertPos == _MAX_THREAD_IN_QUEUE) {
+ insertPos=0;
+ }
+ abs_thread_cond_wait(waitCond,&queueMut);
+ abs_thread_mutex_unlock(&queueMut);
+}
+
+
+void ThreadQueue::releaseExclusiveAccess() {
+ abs_thread_mutex_lock(&queueMut);
+ if (size == 0) {
+ abs_thread_mutex_unlock(&queueMut);
+ return;
+ }
+ // wake up next thread
+ abs_thread_cond_t* waitCond=&(waitThreadEntries[removePos]->waitCond);
+ removePos++;
+ // wrap counter
+ if (removePos == _MAX_THREAD_IN_QUEUE) {
+ removePos=0;
+ }
+ size--;
+ abs_thread_cond_signal(waitCond);
+ abs_thread_mutex_unlock(&queueMut);
+}
+
diff --git a/mpeglib/lib/util/abstract/threadQueue.h b/mpeglib/lib/util/abstract/threadQueue.h
new file mode 100644
index 00000000..4c650e21
--- /dev/null
+++ b/mpeglib/lib/util/abstract/threadQueue.h
@@ -0,0 +1,74 @@
+/*
+ fifo waitqueue for threads.(Multi-in, single out)
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __THREADQUEUE_H
+#define __THREADQUEUE_H
+
+
+#include "abs_thread.h"
+
+class WaitThreadEntry {
+ public:
+ WaitThreadEntry();
+ ~WaitThreadEntry();
+
+ abs_thread_cond_t waitCond;
+};
+
+/**
+ This class can be used as a general purpuse wrapper to
+ make C++ classes thread safe.
+ Mpeglib uses for every decoder a single thread which
+ reads from the input and write to one output type (video/audio)
+ To make the input and output classes thread safe you have
+ two solutions. First you can try to do it in every class
+ itsself, this is much work and needs understanding of
+ threads or you can use this wrapper class.
+ Normally you don't need two threads in one class, only
+ for the audio/video sync this is necessary, but for
+ the inputstream (file,http,..) this not necessary.
+ For the output this is the same.
+ This class offers two methods. waitForExclusiceAcess()
+ and releaseExlusiveAcess. Internally the thread who
+ calls waitFor.. in enqueued (if it does not get the exclusive
+ access) the thread who have the exclusive access calls
+ sometimes release.. with then pass the exclusive access
+ to the next thread.
+ Why it is needed?
+ Because we access the input/output streams from different
+ threads. A user of mpeglib may want to set mpeg video
+ in fullscreen mode, this means two threads call
+ methods in the output classes including: closing windows,
+ resizing windows ... now this is safley possible when
+ the threadSafeInputStream / threadSafeoutputStream wrappers
+ are used, which forward the calls to the real classes.
+*/
+
+class ThreadQueue {
+
+ abs_thread_mutex_t queueMut;
+ int insertPos;
+ int removePos;
+ int size;
+ WaitThreadEntry** waitThreadEntries;
+
+ public:
+ ThreadQueue();
+ ~ThreadQueue();
+
+ void waitForExclusiveAccess();
+ void releaseExclusiveAccess();
+
+};
+
+#endif
diff --git a/mpeglib/lib/util/audio/Makefile.am b/mpeglib/lib/util/audio/Makefile.am
new file mode 100644
index 00000000..968d1d46
--- /dev/null
+++ b/mpeglib/lib/util/audio/Makefile.am
@@ -0,0 +1,18 @@
+
+# ---- @OS_TYPE@/@ARCH_TYPE@ ----
+
+INCLUDES = $(all_includes)
+
+EXTRA_DIST = audioIO_AIX.cpp audioIO_BeOS.cpp \
+ audioIO_HPUX.cpp \
+ audioIO_IRIX.cpp audioIO_Linux.cpp \
+ audioIO_SunOS.cpp audioIO_SDL.cpp
+
+noinst_HEADERS = audioIO.h dspWrapper.h
+
+noinst_LTLIBRARIES = libaudio.la
+
+libaudio_la_SOURCES = audioIO.cpp dspWrapper.cpp
+
+
+
diff --git a/mpeglib/lib/util/audio/audioIO.cpp b/mpeglib/lib/util/audio/audioIO.cpp
new file mode 100644
index 00000000..d066210f
--- /dev/null
+++ b/mpeglib/lib/util/audio/audioIO.cpp
@@ -0,0 +1,49 @@
+
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#ifdef SDL_WRAPPER
+
+ #include "audioIO_SDL.cpp"
+
+//
+// If native sound is defined compiled for that
+//
+
+#else
+
+
+#ifdef OS_AIX
+ #include "audioIO_AIX.cpp"
+#endif
+
+#ifdef OS_Linux
+ #include "audioIO_Linux.cpp"
+#endif
+
+#ifdef OS_BSD
+ #include "audioIO_Linux.cpp"
+#endif
+
+#if defined(OS_IRIX) || defined(OS_IRIX64)
+ #include "audioIO_IRIX.cpp"
+#endif
+
+#ifdef OS_HPUX
+ #include "audioIO_HPUX.cpp"
+#endif
+
+#ifdef OS_SunOS
+ #include "audioIO_SunOS.cpp"
+#endif
+
+#ifdef __BEOS__
+ #include "audioIO_BeOS.cpp"
+#endif
+
+
+#endif
diff --git a/mpeglib/lib/util/audio/audioIO.h b/mpeglib/lib/util/audio/audioIO.h
new file mode 100644
index 00000000..41e1ceb2
--- /dev/null
+++ b/mpeglib/lib/util/audio/audioIO.h
@@ -0,0 +1,80 @@
+
+
+#ifndef __AUDIOIO_H
+#define __AUDIOIO_H
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+
+extern "C" {
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+}
+
+/* AUSUZ should be the amount of data your audio device will accept after it
+ * has said it is ready to receive data. ie when the device is ready
+ * for data it
+ * will accept it without blocking. It must also be a multiple of 128
+ */
+
+#ifdef OS_AIX
+ #define AUSIZ 32768
+#endif
+
+#ifdef OS_Linux
+ extern int AUSIZ;
+#endif
+
+#ifdef OS_BSD
+ #define AUSIZ 32768
+#endif
+
+#if defined(OS_IRIX) || defined(OS_IRIX64)
+ #define AUSIZ 32768
+#endif
+
+#ifdef OS_HPUX
+ #define AUSIZ 4096
+#endif
+
+#ifdef OS_SunOS
+ #define AUSIZ 4096
+#endif
+
+
+#ifdef DEBUG
+ #define DB(type,cmd) if (debugFlags.type) { cmd ; }
+#else
+ #define DB(type,cmd)
+#endif
+
+
+
+
+//Prototypes:
+
+int audioConstruct();
+void audioDestruct();
+
+
+
+int audioOpen();
+void audioClose();
+void audioInit(int sampleSize,int frequency, int stereo,int sign, int bigendian);
+
+
+int mixerOpen();
+void mixerClose();
+void mixerSetVolume(int volumeLeft,int volumeRight);
+
+int audioWrite(char *buffer, int count);
+int getAudioFd();
+int getAudioBufferSize();
+
+#endif
diff --git a/mpeglib/lib/util/audio/audioIO_AIX.cpp b/mpeglib/lib/util/audio/audioIO_AIX.cpp
new file mode 100644
index 00000000..15316852
--- /dev/null
+++ b/mpeglib/lib/util/audio/audioIO_AIX.cpp
@@ -0,0 +1,533 @@
+/*
+ * AIX audio - griff@acm.org 02aug2000
+ * tested on 43P 260 with builtin audio
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+/* A conflict within AIX 4.3.3 <sys/> headers and probably others as well.
+ * I guess nobody ever uses audio... Shame over AIX header files. */
+#include <sys/machine.h>
+#undef BIG_ENDIAN
+#include <sys/audio.h>
+
+static int audio_fd;
+
+static void debugUpdate( unsigned long& flags, long& bsize );
+
+#ifndef AUDIO_BIG_ENDIAN
+#define AUDIO_BIG_ENDIAN BIG_ENDIAN
+#endif
+
+
+
+int audioConstruct() {
+ printf("audioConstruct AIX ********\n");
+ audio_fd=-1;
+ return true;
+}
+
+
+void audioDestruct() {
+
+}
+
+int audioOpen()
+{
+ char devname[14];
+ for ( int dev=0; dev<4; dev++ )
+ {
+ for ( int chan=1; chan<8; chan++ )
+ {
+ sprintf(devname,"/dev/paud%d/%d",dev,chan);
+ audio_fd = open (devname, O_WRONLY, 0);
+ if ( audio_fd >= 0 )
+ {
+ return 1;
+ }
+ sprintf(devname,"/dev/baud%d/%d",dev,chan);
+ audio_fd = open (devname, O_WRONLY, 0);
+ if ( audio_fd >= 0 )
+ {
+ return 1;
+ }
+ }
+ }
+
+ fprintf(stderr, "Could not open AIX audio device, faking\n" );
+ return 1;
+}
+
+int getAudioBufferSize()
+{
+ audio_buffer paud_bufinfo;
+
+ if( audio_fd < 0 ) return 1024*65;
+
+ if ( ioctl(audio_fd, AUDIO_BUFFER, &paud_bufinfo) < 0 )
+ {
+ perror("ioctl getAudioBufferSize using default");
+ return 1024*65;
+ }
+
+ /*
+ * Do you need the total capacity or the current capacity?
+ * This is the total capacity:
+ */
+ return paud_bufinfo.write_buf_cap;
+ /*
+ * This is the current capacity:
+ * return (paud_bufinfo.write_buf_cap - paud_bufinfo.write_buf_size);
+ */
+}
+
+void audioInit(int sampleSize,int frequency, int stereo, int sign, int bigendian )
+{
+ // int format;
+ int bytes_per_sample;
+ audio_init paud_init;
+ audio_buffer paud_bufinfo;
+ // audio_status paud_status;
+ audio_control paud_control;
+ audio_change paud_change;
+
+ if( audio_fd < 0 ) return;
+
+ /*
+ * We can't set the buffer size - just ask the device for the maximum
+ * that we can have.
+ */
+ if ( ioctl(audio_fd, AUDIO_BUFFER, &paud_bufinfo) < 0 )
+ {
+ perror("Couldn't get audio buffer information");
+ return;
+ }
+
+ /*
+ * Fields in the audio_init structure:
+ *
+ * Ignored by us:
+ *
+ * paud.loadpath[LOAD_PATH]; * DSP code to load, MWave chip only?
+ * paud.slot_number; * slot number of the adapter
+ * paud.device_id; * adapter identification number
+ *
+ * Input:
+ *
+ * paud.srate; * the sampling rate in Hz
+ * paud.bits_per_sample; * 8, 16, 32, ...
+ * paud.bsize; * block size for this rate
+ * paud.mode; * ADPCM, PCM, MU_LAW, A_LAW, SOURCE_MIX
+ * paud.channels; * 1=mono, 2=stereo
+ * paud.flags; * FIXED - fixed length data
+ * * LEFT_ALIGNED, RIGHT_ALIGNED (var len only)
+ * * TWOS_COMPLEMENT - 2's complement data
+ * * SIGNED - signed? comment seems wrong in sys/audio.h
+ * * BIG_ENDIAN
+ * paud.operation; * PLAY, RECORD
+ *
+ * Output:
+ *
+ * paud.flags; * PITCH - pitch is supported
+ * * INPUT - input is supported
+ * * OUTPUT - output is supported
+ * * MONITOR - monitor is supported
+ * * VOLUME - volume is supported
+ * * VOLUME_DELAY - volume delay is supported
+ * * BALANCE - balance is supported
+ * * BALANCE_DELAY - balance delay is supported
+ * * TREBLE - treble control is supported
+ * * BASS - bass control is supported
+ * * BESTFIT_PROVIDED - best fit returned
+ * * LOAD_CODE - DSP load needed
+ * paud.rc; * NO_PLAY - DSP code can't do play requests
+ * * NO_RECORD - DSP code can't do record requests
+ * * INVALID_REQUEST - request was invalid
+ * * CONFLICT - conflict with open's flags
+ * * OVERLOADED - out of DSP MIPS or memory
+ * paud.position_resolution; * smallest increment for position
+ */
+
+ paud_init.srate = frequency;
+ paud_init.mode = PCM;
+ paud_init.operation = PLAY;
+ paud_init.channels = (stereo?2:1);
+
+ /*
+ * options in AIX:
+ * paud_init.bits_per_sample: 8 | 16
+ * paud_init.flags: AUDIO_BIG_ENDIAN (not used here)
+ * SIGNED (always used here)
+ * TWOS_COMPLEMENT (always on for Linux dsp porting?)
+ * FIXED <- that's right for SDL
+ * or LEFT_ALIGNED <- that's right for mpeglib
+ * or RIGHT_ALIGNED
+ * paud_init.bsize: sample byte size,
+ * bits_per_sample * (stereo?2:1) - for SDL
+ * bits_per_sample * (stereo?2:1) * 2 - for mpeglib
+ */
+ if ( sampleSize == 8 )
+ {
+ /* AFMT_S8 in linux dsp */
+ bytes_per_sample = 2; // why not 1 ?
+ paud_init.bits_per_sample = 8;
+ paud_init.flags = TWOS_COMPLEMENT | LEFT_ALIGNED;
+ }
+ else
+ {
+ /* AFMT_S16_LE in linux dsp */
+ bytes_per_sample = 4; // why not 2 ?
+ paud_init.bits_per_sample = 16;
+ paud_init.flags = TWOS_COMPLEMENT | LEFT_ALIGNED;
+ }
+ if( sign ) paud_init.flags |= SIGNED;
+ if( bigendian ) paud_init.flags |= AUDIO_BIG_ENDIAN;
+
+ paud_init.bsize = bytes_per_sample * (stereo?2:1);
+
+#if 0
+ debugUpdate(paud_init.flags, paud_init.bsize);
+
+ printf("CG: sampleSize = %d\n", sampleSize);
+ printf("CG: frequency = %d\n", frequency);
+ printf("CG: stereo = %s\n", (stereo)?"y":"n");
+ printf("CG: mode = %s\n", "PCM");
+ printf("CG: channels = %d\n", paud_init.channels);
+ printf("CG: bsize = %d\n", paud_init.bsize);
+ printf("CG: bits_per_sample = %d\n", paud_init.bits_per_sample);
+ printf("CG: flags & BIG_ENDIAN = %s\n", ((paud_init.flags&AUDIO_BIG_ENDIAN)?"y":"n"));
+ printf("CG: flags & SIGNED = %s\n", ((paud_init.flags&SIGNED)?"y":"n"));
+ printf("CG: flags & TWOS_COMPLEMENT = %s\n", ((paud_init.flags&TWOS_COMPLEMENT)?"y":"n"));
+ printf("CG: flags & FIXED = %s\n", ((paud_init.flags&FIXED)?"y":"n"));
+ printf("CG: flags & LEFT_ALIGNED = %s\n", ((paud_init.flags&LEFT_ALIGNED)?"y":"n"));
+ printf("CG: flags & RIGHT_ALIGNED = %s\n", ((paud_init.flags&RIGHT_ALIGNED)?"y":"n"));
+#endif
+
+ /*
+ * We know the buffer size and the max number of subsequent writes
+ * that can be pending. If more than one can pend, allow the application
+ * to do something like double buffering between our write buffer and
+ * the device's own buffer that we are filling with write() anyway.
+ *
+ * We can calculate the number of samples that fit into the audio
+ * device buffer if that is necessary:
+ *
+ * samples_capacity = paud_bufinfo.write_buf_cap
+ * / bytes_per_sample
+ * / (stereo?2:1);
+ * if ( paud_bufinfo.request_buf_cap != 1 ) samples_capacity /= 2;
+ */
+
+ /*
+ * The AIX paud device init can't modify the values of the audio_init
+ * structure that we pass to it. So we don't need any recalculation
+ * of this stuff and no reinit call as in linux SDL dsp and dma code.
+ *
+ * /dev/paud supports all of the encoding formats, so we don't need
+ * to do anything like reopening the device, either.
+ */
+ if ( ioctl(audio_fd, AUDIO_INIT, &paud_init) < 0 )
+ {
+ switch ( paud_init.rc )
+ {
+ case 1 :
+ perror("Couldn't set audio format: DSP can't do play requests");
+ return;
+ break;
+ case 2 :
+ perror("Couldn't set audio format: DSP can't do record requests");
+ return;
+ break;
+ case 4 :
+ perror("Couldn't set audio format: request was invalid");
+ return;
+ break;
+ case 5 :
+ perror("Couldn't set audio format: conflict with open's flags");
+ return;
+ break;
+ case 6 :
+ perror("Couldn't set audio format: out of DSP MIPS or memory");
+ return;
+ break;
+ default :
+ perror("Couldn't set audio format: not documented in sys/audio.h");
+ return;
+ break;
+ }
+ }
+
+ /*
+ * Set some parameters: full volume, first speaker that we can find.
+ * Ignore the other settings for now.
+ */
+ paud_change.input = AUDIO_IGNORE; /* the new input source */
+ paud_change.output = OUTPUT_1;
+ /* EXTERNAL_SPEAKER,
+ * INTERNAL_SPEAKER,
+ * OUTPUT_1 */
+ paud_change.monitor = AUDIO_IGNORE; /* the new monitor state */
+ paud_change.volume = 0x7fffffff; /* volume level [0-0x7fffffff] */
+ paud_change.volume_delay = AUDIO_IGNORE; /* the new volume delay */
+ paud_change.balance = 0x3fffffff; /* the new balance */
+ paud_change.balance_delay = AUDIO_IGNORE; /* the new balance delay */
+ paud_change.treble = AUDIO_IGNORE; /* the new treble state */
+ paud_change.bass = AUDIO_IGNORE; /* the new bass state */
+ paud_change.pitch = AUDIO_IGNORE; /* the new pitch state */
+
+ paud_control.ioctl_request = AUDIO_CHANGE;
+ paud_control.request_info = (char*)&paud_change;
+ if ( ioctl(audio_fd, AUDIO_CONTROL, &paud_control) < 0 )
+ {
+ perror("Can't change audio display settings (ignoring)" );
+ }
+
+ /*
+ * Tell the device to expect data. Actual start will wait for
+ * the first write() call.
+ */
+ paud_control.ioctl_request = AUDIO_START;
+ paud_control.position = 0;
+ if ( ioctl(audio_fd, AUDIO_CONTROL, &paud_control) < 0 )
+ {
+ perror("Can't start audio play");
+ return;
+ }
+}
+
+
+void audioSetVolume(int volume)
+{
+ long vol = (long)(volume/100.0) * 0x7fffffff;
+ if( audio_fd < 0 ) return;
+
+ audio_control paud_control;
+ audio_change paud_change;
+
+ paud_change.input = AUDIO_IGNORE; /* the new input source */
+ paud_change.output = OUTPUT_1; /* EXTERNAL_SPEAKER,INTERNAL_SPEAKER,
+ OUTPUT_1 */
+ paud_change.monitor = AUDIO_IGNORE; /* the new monitor state */
+ paud_change.volume = vol; /* volume level [0-0x7fffffff] */
+ paud_change.volume_delay = AUDIO_IGNORE; /* the new volume delay */
+ paud_change.balance = AUDIO_IGNORE; /* the new balance */
+ paud_change.balance_delay = AUDIO_IGNORE; /* the new balance delay */
+ paud_change.treble = AUDIO_IGNORE; /* the new treble state */
+ paud_change.bass = AUDIO_IGNORE; /* the new bass state */
+ paud_change.pitch = AUDIO_IGNORE; /* the new pitch state */
+
+ paud_control.ioctl_request = AUDIO_CHANGE;
+ paud_control.request_info = (char*)&paud_change;
+
+ if ( ioctl(audio_fd, AUDIO_CONTROL, &paud_control) < 0 )
+ {
+ perror("Change audio volume failed");
+ }
+}
+
+void audioFlush()
+{
+ if( audio_fd < 0 ) return;
+
+ if ( ioctl(audio_fd, AUDIO_WAIT, NULL) < 0 )
+ {
+ perror("Flush audio buffers failed");
+ }
+}
+
+void audioClose()
+{
+ if( audio_fd < 0 ) return;
+
+ if ( ioctl(audio_fd, AUDIO_WAIT, NULL) < 0 )
+ {
+ perror("Flush audio buffers failed");
+ }
+ close(audio_fd);
+}
+
+int audioWrite(char *buffer, int count)
+{
+ int written = write(audio_fd, buffer, count);
+ if( written < count )
+ {
+ return count;
+ }
+
+ return written;
+}
+
+int
+getAudioFd()
+{
+ return audio_fd;
+}
+
+int mixerOpen()
+{
+ return true;
+}
+
+void mixerClose()
+{
+}
+
+void mixerSetVolume(int leftVolume,int rightVolume)
+{
+ long balance;
+
+ if( audio_fd < 0 ) return;
+
+ balance = 2 * (leftVolume-rightVolume) / (leftVolume+rightVolume);
+ balance = 0x3fffffff + balance*0x3fffffff;
+
+ audio_control paud_control;
+ audio_change paud_change;
+
+ paud_change.input = AUDIO_IGNORE; /* the new input source */
+ paud_change.output = OUTPUT_1; /* EXTERNAL_SPEAKER,INTERNAL_SPEAKER,
+ OUTPUT_1 */
+ paud_change.monitor = AUDIO_IGNORE; /* the new monitor state */
+ paud_change.volume = AUDIO_IGNORE; /* volume level [0-0x7fffffff] */
+ paud_change.volume_delay = AUDIO_IGNORE; /* the new volume delay */
+ paud_change.balance = balance; /* the new balance */
+ paud_change.balance_delay = AUDIO_IGNORE; /* the new balance delay */
+ paud_change.treble = AUDIO_IGNORE; /* the new treble state */
+ paud_change.bass = AUDIO_IGNORE; /* the new bass state */
+ paud_change.pitch = AUDIO_IGNORE; /* the new pitch state */
+
+ paud_control.ioctl_request = AUDIO_CHANGE;
+ paud_control.request_info = (char*)&paud_change;
+
+ if ( ioctl(audio_fd, AUDIO_CONTROL, &paud_control) < 0 )
+ {
+ perror("Change audio volume failed");
+ }
+}
+
+static void debugUpdate( unsigned long& flags, long& bsize )
+{
+ const char* g;
+
+ g = getenv("AUDIO_BIG_ENDIAN");
+ if ( g )
+ {
+ int i = atoi(g);
+ if ( i == 1 )
+ {
+ flags |= AUDIO_BIG_ENDIAN;
+ }
+ else if ( i == 0 )
+ {
+ flags &= ~AUDIO_BIG_ENDIAN;
+ }
+ else
+ {
+ printf("CG: bad AUDIO_BIG_ENDIAN env variable %s\n", g);
+ }
+ }
+
+ g = getenv("SIGNED");
+ if ( g )
+ {
+ int i = atoi(g);
+ if ( i == 1 )
+ {
+ flags |= SIGNED;
+ }
+ else if ( i == 0 )
+ {
+ flags &= ~SIGNED;
+ }
+ else
+ {
+ printf("CG: bad SIGNED env variable %s\n", g);
+ }
+ }
+
+ g = getenv("TWOS_COMPLEMENT");
+ if ( g )
+ {
+ int i = atoi(g);
+ if ( i == 1 )
+ {
+ flags |= TWOS_COMPLEMENT;
+ }
+ else if ( i == 0 )
+ {
+ flags &= ~TWOS_COMPLEMENT;
+ }
+ else
+ {
+ printf("CG: bad TWOS_COMPLEMENT env variable %s\n", g);
+ }
+ }
+
+ g = getenv("FIXED");
+ if ( g )
+ {
+ int i = atoi(g);
+ if ( i == 1 )
+ {
+ flags |= FIXED;
+ }
+ else if ( i == 0 )
+ {
+ flags &= ~FIXED;
+ }
+ else
+ {
+ printf("CG: bad FIXED env variable %s\n", g);
+ }
+ }
+
+ g = getenv("LEFT_ALIGNED");
+ if ( g )
+ {
+ int i = atoi(g);
+ if ( i == 1 )
+ {
+ flags |= LEFT_ALIGNED;
+ }
+ else if ( i == 0 )
+ {
+ flags &= ~LEFT_ALIGNED;
+ }
+ else
+ {
+ printf("CG: bad LEFT_ALIGNED env variable %s\n", g);
+ }
+ }
+
+ g = getenv("RIGHT_ALIGNED");
+ if ( g )
+ {
+ int i = atoi(g);
+ if ( i == 1 )
+ {
+ flags |= RIGHT_ALIGNED;
+ }
+ else if ( i == 0 )
+ {
+ flags &= ~RIGHT_ALIGNED;
+ }
+ else
+ {
+ printf("CG: bad RIGHT_ALIGNED env variable %s\n", g);
+ }
+ }
+
+ g = getenv("BSIZE");
+ if ( g )
+ {
+ bsize = atoi(g);
+ }
+}
+
diff --git a/mpeglib/lib/util/audio/audioIO_BeOS.cpp b/mpeglib/lib/util/audio/audioIO_BeOS.cpp
new file mode 100644
index 00000000..ae4cf5a1
--- /dev/null
+++ b/mpeglib/lib/util/audio/audioIO_BeOS.cpp
@@ -0,0 +1,227 @@
+//
+// BeOS code for amp-0.7.4, (C) 1997 Andy Lo A Foe
+//
+
+
+#include <MediaKit.h>
+#include <KernelKit.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "transform.h"
+#include "audioIO.h"
+#include "audio.h"
+
+// Define the streambuf size here. streambuf is used
+// as a simple fit buffer.
+
+#define STREAMBUF_SIZE (32*1152)
+
+long bytes_in_streambuf;
+
+// streambuf definition and base indicator
+
+BSubscriber *the_sub;
+BDACStream *the_stream;
+
+sem_id ok_to_read;
+sem_id ok_to_write;
+
+
+char streambuf[STREAMBUF_SIZE];
+char *readbase;
+
+static int au_vol = 100;
+
+// Define our own printout function since we are
+// first writing in the streambuf (not needed, I think,
+// but I don't know if the DACStream accepts a buffer
+// size of 1152. Try it...
+
+void printout(void)
+{
+if (A_WRITE_TO_FILE) {
+#ifndef NO_BYTE_SWAPPING
+int i,j;
+short *ptr;
+
+ if (nch==2) {
+ ptr=(short*)stereo_samples;
+ i=j=32 * 18 * 2;
+ } else {
+ ptr=(short*)mono_samples;
+ i=j=32 * 18;
+ }
+
+ for (;i>=0;--i)
+ ptr[i] = ptr[i] << 8 | ptr[i] >> 8;
+#endif
+
+ if (nch==2)
+ fwrite(stereo_samples,1,sizeof stereo_samples,out_file);
+ else
+ fwrite(mono_samples,1,sizeof mono_samples,out_file);
+}
+
+
+ if (A_AUDIO_PLAY) {
+ static char au_buf[STREAMBUF_SIZE];
+ static int au_ptr =0, avail = 0;
+ static int num_smp;
+ static char *readbase = &au_buf[0];
+
+ // Write amount of samples to copy somewhere
+ num_smp = (nch == 2 ? sizeof stereo_samples : sizeof mono_samples);
+
+ // Copy samples in the fit buffer
+ memcpy(au_buf+au_ptr,(nch == 2 ? (char *)stereo_samples : (char *)mono_samples),
+ num_smp);
+
+ // Increase fit buffer pointer and available sample count
+ au_ptr+=num_smp;
+ avail+=num_smp;
+
+ if (avail >= 4096) { // Are there enough smps to feed the stream?
+ audioWrite((char*)readbase,4096); // Feed it!
+ readbase+=4096; // Increase readbase
+ avail-=4096; // Decrease avail smps count
+ if (au_ptr == STREAMBUF_SIZE) { // At end of fit buffer?
+ au_ptr=0; // Reset all pointers
+ readbase=&au_buf[0];
+ }
+ }
+ }
+
+}
+
+
+// Fake Buffer functions, just to keep the sources clean,
+// buffer.c should not be included in the link process...
+
+int AUDIO_BUFFER_SIZE;
+
+int
+audioBufferOpen(int frequency, int stereo, int volume)
+{
+ audioOpen(frequency, stereo, volume);
+}
+
+
+inline void
+audioBufferWrite(char *buf,int bytes)
+{
+ audioWrite(buf, bytes);
+}
+
+
+void
+audioBufferClose()
+{
+ audioClose();
+}
+
+
+int audioRead(char *buffer, int count)
+{
+ //printf("acquiring ok_to_read (%d bytes)\n", count);
+ if (acquire_sem(ok_to_read)==B_NO_ERROR) {
+ for (register int i=0; i < count;i++) {
+ *(buffer++)+=*(readbase++);
+ }
+ bytes_in_streambuf-=count;
+
+ if (bytes_in_streambuf <= 0) {
+ release_sem(ok_to_write);
+ bytes_in_streambuf = 0;
+ } else {
+ release_sem(ok_to_read);
+ }
+ }
+ return (0);
+}
+
+
+bool stream_func(void *arg, char *buf, size_t count, void *header)
+{
+ audioRead(buf, count);
+ return TRUE;
+}
+
+void audioOpen() {
+ readbase = &streambuf[0];
+
+ bytes_in_streambuf = 0;
+
+ the_sub = new BSubscriber("amp DAC writer");
+ the_stream = new BDACStream();
+
+ the_sub->Subscribe(the_stream);
+
+ the_stream->SetSamplingRate(frequency);
+
+ // Create semaphores
+ ok_to_read = create_sem(0, "read sem");
+ ok_to_write = create_sem(1, "write sem");
+
+}
+
+void audioInit(int sampleSize,int frequency, int stereo)
+{
+
+ // Initialize the streambuf
+ bytes_in_streambuf = 0;
+ memset(&streambuf, 0, STREAMBUF_SIZE);
+
+
+ // Enter the stream
+ the_sub->EnterStream(NULL, TRUE, NULL, stream_func, NULL, TRUE);
+}
+
+
+void audioSetVolume(int volume)
+{
+ if (volume > 128) // This allows for a modest volume boost
+ volume = 128;
+ au_vol = volume;
+}
+
+
+void audioClose()
+{
+ the_sub->ExitStream(TRUE);
+ the_sub->Unsubscribe();
+
+ delete_sem(ok_to_read);
+ delete_sem(ok_to_write);
+
+ delete the_sub;
+ delete the_stream;
+}
+
+
+// audioWrite is called from the player thread
+
+int audioWrite(char *buffer, int count)
+{
+ //printf("acquiring ok_to_write (%d bytes)\n", count);
+ if(acquire_sem(ok_to_write)==B_NO_ERROR)
+ {
+ memcpy(&streambuf, buffer, count);
+
+ if (au_vol != 100) { // Handle volume scaling here
+ short *b=(short *)&streambuf;
+ for (int i=0; i < count/2; i++) {
+ int v=((int)b[i]*au_vol)/100;
+ b[i]=(v>32767) ? 32767 : ((v<-32768) ? -32768 : v);
+ }
+ }
+
+ bytes_in_streambuf = count;
+ readbase = &streambuf[0];
+
+ release_sem(ok_to_read);
+ }
+ return 0;
+}
+
diff --git a/mpeglib/lib/util/audio/audioIO_HPUX.cpp b/mpeglib/lib/util/audio/audioIO_HPUX.cpp
new file mode 100644
index 00000000..ae07a148
--- /dev/null
+++ b/mpeglib/lib/util/audio/audioIO_HPUX.cpp
@@ -0,0 +1,190 @@
+/* this file is a part of amp software, (C) tomislav uzelac 1996,1997
+
+ Origional code by: Lutz Vieweg
+ Modified by:
+ * Andrew Richards - moved code from audio.c
+
+ */
+
+
+#include <sys/audio.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/lock.h>
+#include <unistd.h>
+#include <stdio.h>
+#include "audioIO.h"
+
+/* declare these static to effectively isolate the audio device */
+
+static int audio_fd;
+
+
+/* audioOpen() */
+/* should open the audio device, perform any special initialization */
+/* Set the frequency, no of channels and volume. Volume is only set if */
+/* it is not -1 */
+
+void audioOpen() {
+ if ((audio_fd = open("/dev/audio",O_RDWR))==-1)
+ die(" unable to open the audio device\n");
+
+ DB(audio, msg("Audio device opened on %d\n",audio_fd); )
+ }
+
+
+void
+audioInit(int sampleSize,int frequency, int stereo)
+{
+ int flags;
+ int failed = 0;
+ int volume=100;
+
+ if ((flags = fcntl (audio_fd, F_GETFL, 0)) < 0) {
+ die("unable to set non-blocking mode for /dev/audio\n");
+ }
+ flags |= O_NDELAY;
+ if (fcntl (audio_fd, F_SETFL, flags) < 0) {
+ die("unable to set non-blocking mode for /dev/audio\n");
+ }
+
+ if ( ioctl(audio_fd, AUDIO_SET_DATA_FORMAT, AUDIO_FORMAT_LINEAR16BIT) < 0 ||
+ ioctl(audio_fd, AUDIO_SET_CHANNELS, stereo ? 2 : 1) < 0 ||
+ ioctl(audio_fd, AUDIO_SET_OUTPUT, AUDIO_OUT_SPEAKER | AUDIO_OUT_HEADPHONE
+ | AUDIO_OUT_LINE) < 0 ||
+ ioctl(audio_fd, AUDIO_SET_SAMPLE_RATE, frequency) < 0) {
+ failed = -1;
+ }
+ if (volume != -1) {
+ struct audio_describe description;
+ struct audio_gains gains;
+ float fvolume = (float)volume / 100.0f;
+ if (ioctl(audio_fd, AUDIO_DESCRIBE, &description)) {
+ failed = -1;
+ }
+ if (ioctl (audio_fd, AUDIO_GET_GAINS, &gains)) {
+ failed = -1;
+ }
+
+ gains.transmit_gain = (int)((float)description.min_transmit_gain +
+ (float)(description.max_transmit_gain
+ - description.min_transmit_gain)
+ * fvolume);
+
+ /* gains.monitor_gain = description.min_monitor_gain; */ /* don't monitor ! */
+
+ if (ioctl (audio_fd, AUDIO_SET_GAINS, &gains)) {
+ failed = -1;
+ }
+ }
+
+ if (ioctl(audio_fd, AUDIO_SET_TXBUFSIZE, 4096 * 8)) {
+ failed = -1;
+ }
+ if (failed)
+ die(" unable to setup /dev/audio\n");
+}
+
+
+/* audioSetVolume - only code this if your system can change the volume while */
+/* playing. sets the output volume 0-100 */
+
+void
+audioSetVolume(int volume)
+{
+ struct audio_describe description;
+ struct audio_gains gains;
+ int failed = 0;
+ float fvolume = ((float)volume) / 100.0f;
+ if (ioctl(audio_fd, AUDIO_DESCRIBE, &description)) {
+ failed = -1;
+ }
+ if (ioctl (audio_fd, AUDIO_GET_GAINS, &gains)) {
+ failed = -1;
+}
+
+ gains.transmit_gain = (int)((float)description.min_transmit_gain +
+ (float)(description.max_transmit_gain
+ - description.min_transmit_gain)
+ * fvolume);
+ if (ioctl (audio_fd, AUDIO_SET_GAINS, &gains)) {
+ failed = -1;
+ }
+
+ /* could evaluate "failed" here - but who cares? */
+
+ DB(audio, msg("volume set to %d%%\n",volume); )
+
+}
+
+/* audioFlush() */
+/* should flush the audio device */
+
+inline void
+audioFlush()
+{
+ DB(audio, msg("audio: flush %d\n",audio_fd) );
+}
+
+
+/* audioClose() */
+/* should close the audio device and perform any special shutdown */
+
+void
+audioClose()
+{
+ close(audio_fd);
+ DB(audio, msg("audio: closed %d\n",audio_fd) );
+}
+
+
+/* audioWrite */
+/* writes count bytes from buffer to the audio device */
+/* returns the number of bytes actually written */
+
+int audioWrite(char *buffer, int count)
+{
+ DB(audio, msg("audio: Writing %d bytes to audio descriptor %d\n",count,getAudioFd()) );
+ return(write(audio_fd,buffer,count));
+}
+
+
+/* Let buffer.c have the audio descriptor so it can select on it. This means */
+/* that the program is dependent on an file descriptor to work. Should really */
+/* move the select's etc (with inlines of course) in here so that this is the */
+/* ONLY file which has hardware dependent audio stuff in it */
+
+int
+getAudioFd()
+{
+ return(audio_fd);
+}
+
+/*
+ Try to set the priority of this process to a value which
+ allows us to play without buffering, thus saving memory
+ and avoiding cache-misses.
+ If we cannot get any priority high enough to allow for
+ undisturbed replay (because we don't have sufficient
+ privilege), return a zero, otherwise, return a one.
+*/
+int audioSetPriority(void) {
+
+ /* try to lock process in physical memory, just ignore if this fails */
+ plock(PROCSHLIBLOCK);
+
+ /* try to set a realtime-priority of 64 */
+ if (-1 != rtprio(0, 64)) {
+ DB(audio, msg("using real-time priority\n"); )
+ return 1;
+ }
+
+ /* try to set a nice-level of -20 */
+ if (-1 != nice(-20)) {
+ DB(audio, msg("using nice-level -20\n"); )
+ return 1;
+ }
+
+ DB(audio, msg("using buffered output\n"); )
+ return 0; /* need to use a buffer */
+}
diff --git a/mpeglib/lib/util/audio/audioIO_IRIX.cpp b/mpeglib/lib/util/audio/audioIO_IRIX.cpp
new file mode 100644
index 00000000..8498a487
--- /dev/null
+++ b/mpeglib/lib/util/audio/audioIO_IRIX.cpp
@@ -0,0 +1,157 @@
+/* this file is a part of amp software, (C) tomislav uzelac 1996,1997
+
+ Origional code by: Karl Anders Oygard
+ Modified by:
+ * Andrew Richards - moved code from audio.c
+
+ */
+#include <assert.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <dmedia/audio.h>
+#include "audioIO.h"
+
+/* declare these static to effectively isolate the audio device */
+
+static ALport audioport;
+static ALconfig audioconfig;
+
+
+/* audioOpen() */
+/* should open the audio device, perform any special initialization */
+/* Set the frequency, no of channels and volume. Volume is only set if */
+/* it is not -1 */
+
+
+void audioOpen() {
+ printf("sorry. The audio part for irix must be fixed. \n");
+}
+
+void
+audioInit(int sampleSize,int frequency, int stereo)
+{
+ ALconfig audioconfig;
+ audioconfig = ALnewconfig();
+
+ if (!audioconfig)
+ die("out of memory\n");
+ else {
+ long pvbuf[] = { AL_OUTPUT_COUNT, 0, AL_MONITOR_CTL, 0, AL_OUTPUT_RATE, 0};
+
+ if (ALgetparams(AL_DEFAULT_DEVICE, pvbuf, 6) < 0)
+ if (oserror() == AL_BAD_DEVICE_ACCESS)
+ die("couldn't access audio device\n");
+
+ if (pvbuf[1] == 0 && pvbuf[3] == AL_MONITOR_OFF) {
+ long al_params[] = { AL_OUTPUT_RATE, 0};
+
+ al_params[1] = frequency;
+ ALsetparams(AL_DEFAULT_DEVICE, al_params, 2);
+ } else
+ if (pvbuf[5] != frequency)
+ die("audio device is already in use with wrong sample output rate\n");
+
+ /* ALsetsampfmt(audioconfig, AL_SAMPFMT_TWOSCOMP); this is the default */
+ /* ALsetwidth(audioconfig, AL_SAMPLE_16); this is the default */
+
+ if (!stereo) ALsetchannels(audioconfig, AL_MONO);
+ /* else ALsetchannels(audioconfig, AL_STEREO); this is the default */
+
+ ALsetqueuesize(audioconfig, AUSIZ * 2);
+
+ audioport = ALopenport("amp", "w", audioconfig);
+ if (audioport == (ALport) 0) {
+ switch (oserror()) {
+ case AL_BAD_NO_PORTS:
+ die("system is out of ports\n");
+
+ case AL_BAD_DEVICE_ACCESS:
+ die("couldn't access audio device\n");
+
+ case AL_BAD_OUT_OF_MEM:
+ die("out of memory\n");
+ }
+ exit(-1);
+ }
+ ALsetfillpoint(audioport, AUSIZ);
+ }
+}
+
+
+/* audioSetVolume - only code this if your system can change the volume while */
+/* playing. sets the output volume 0-100 */
+
+void
+audioSetVolume(int volume)
+{
+ long al_params[] = { AL_LEFT_SPEAKER_GAIN, 0, AL_RIGHT_SPEAKER_GAIN, 0};
+
+ al_params[1] = al_params[3] = volume * 100 / 255;
+
+ ALsetparams(AL_DEFAULT_DEVICE, al_params, 4);
+}
+
+
+/* audioFlush() */
+/* should flush the audio device */
+
+void
+audioFlush()
+{
+ DB(audio, msg("audio: flush %d\n",audio_fd) );
+}
+
+
+/* audioClose() */
+/* should close the audio device and perform any special shutdown */
+
+void
+audioClose()
+{
+int write_fd;
+
+ /* wait for all samples to be played */
+
+ write_fd = ALgetfd(audioport);
+ if (write_fd >= 0) {
+ fd_set write_fds;
+
+ FD_ZERO(&write_fds);
+ FD_SET(write_fd, &write_fds);
+
+ ALsetfillpoint(audioport, AUSIZ * 2);
+ select(write_fd + 1, NULL, &write_fds, NULL, NULL);
+ }
+
+ /* now close it */
+
+ ALcloseport(audioport);
+ DB(audio, msg("audio: closed %d\n",audio_fd) );
+}
+
+
+/* audioWrite */
+/* writes count bytes from buffer to the audio device */
+/* returns the number of bytes actually written */
+
+int audioWrite(char *buffer, int count)
+{
+ if (ALwritesamps(audioport, buffer, count / 2) == 0) {
+ ALsetfillpoint(audioport, AUSIZ);
+ return(count);
+ } else
+ return 0;
+}
+
+/* Let buffer.c have the audio descriptor so it can select on it. This
+ means that the program is dependent on an file descriptor to
+ work. Should really move the select's etc (with inlines of course) in
+ here so that this is the ONLY file which has hardware dependent audio
+ stuff in it. */
+
+int
+getAudioFd()
+{
+ return ALgetfd(audioport);
+}
+
diff --git a/mpeglib/lib/util/audio/audioIO_Linux.cpp b/mpeglib/lib/util/audio/audioIO_Linux.cpp
new file mode 100644
index 00000000..5ca9231c
--- /dev/null
+++ b/mpeglib/lib/util/audio/audioIO_Linux.cpp
@@ -0,0 +1,220 @@
+/* this file is a part of amp software, (C) tomislav uzelac 1996,1997
+
+ Origional code by: tomislav uzelac
+ Modified by:
+ * Dan Nelson - BSD mods.
+ * Andrew Richards - moved code from audio.c and added mixer support etc
+ * Martin Vogt
+ */
+
+/* Support for Linux and BSD sound devices */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include "audioIO.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+//
+// Why all these different system cannot make a standard where this
+// soundcard.h file is ?
+//
+#if defined(HAVE_SYS_SOUNDCARD_H)
+ #undef AUSIZ
+ #undef HAVE_SYS_SOUNDCARD_H
+ #include <sys/soundcard.h>
+#elif defined(HAVE_MACHINE_SOUNDCARD_H)
+ #undef AUSIZ
+ #include <machine/soundcard.h>
+#elif defined(__NetBSD__)
+ #undef AUSIZ
+ #include <soundcard.h>
+#else
+ // fallback:
+ #include <linux/soundcard.h>
+#endif
+
+
+/* optimal fragment size */
+
+int AUSIZ = 0;
+
+// declare these static to effectively isolate the audio device
+
+static int audio_fd;
+static int mixer_fd;
+static int volumeIoctl;
+
+
+
+int audioConstruct() {
+ audio_fd=-1;
+ mixer_fd=-1;
+ return true;
+}
+
+
+void audioDestruct() {
+
+}
+
+
+
+/*
+ should open the audio device, perform any special initialization
+*/
+int audioOpen() {
+ audio_fd = open ("/dev/dsp", O_WRONLY, 0);
+ if (audio_fd < 0) {
+ perror("Unable to open the audio");
+ }
+
+ // Ok here something important if your programm forks:
+ if (audio_fd > 0) {
+ if (fcntl(audio_fd,F_SETFD,true) < 0) {
+ perror("fcntl socket");exit(1);
+ }
+ }
+
+ return (audio_fd > 0);
+}
+
+inline void audioFlush() {
+ if (ioctl(audio_fd, SNDCTL_DSP_RESET, 0) == -1)
+ perror("Unable to reset audio device\n");
+}
+
+/*
+ should close the audio device and perform any special shutdown
+*/
+void audioClose() {
+ audioFlush();
+ if (close(audio_fd) < 0) {
+ perror("error close audiodevice:");
+ }
+}
+
+
+void audioInit(int sampleSize,int frequency, int stereo, int sign, int big) {
+ if( sign == 0 )
+ {
+ fprintf(stderr,
+ "%s, %d: expecting signed audio data, "
+ "initialized unsigned (ignored)\n",
+ __FILE__, __LINE__ );
+ }
+ if( big != 0 )
+ {
+ fprintf(stderr,
+ "%s, %d: expecting little endian audio data, "
+ "initialized big endian (ignored)\n",
+ __FILE__, __LINE__ );
+ }
+
+ int play_format=AFMT_S16_LE;
+
+ if (sampleSize == 8) {
+ play_format=AFMT_S8;
+ }
+ ioctl(audio_fd,SNDCTL_DSP_RESET,NULL);
+
+ if (ioctl(audio_fd, SNDCTL_DSP_SETFMT,&play_format) < 0) {
+ perror("Unable to set required audio format\n");
+ }
+
+ /* Set 1 or 2 channels */
+ stereo=(stereo ? 1 : 0);
+
+ if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) < 0) {
+ perror("Unable to set stereo/mono\n");
+ exit(0);
+ }
+
+ /* Set the output frequency */
+ if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &frequency) < 0) {
+ perror("Unable to set frequency");
+ exit(0);
+ }
+
+ if (ioctl(audio_fd, SNDCTL_DSP_GETBLKSIZE, &AUSIZ) == -1) {
+ perror("Unable to get fragment size\n");
+ exit(0);
+ }
+}
+
+
+int getAudioBufferSize() {
+ struct audio_buf_info buf_info;
+ int buf=1024*65;
+ if (ioctl(audio_fd,SNDCTL_DSP_GETOSPACE,&buf_info) == -1) {
+ perror("ioctl getAudioBufferSize using default");
+ } else {
+ buf=buf_info.bytes;
+ }
+ return buf;
+}
+
+
+int mixerOpen() {
+ int supportedMixers;
+
+ if ((mixer_fd=open("/dev/mixer",O_RDWR)) == -1) {
+ perror("Unable to open mixer device");
+ }
+
+ // Ok here something important if your programm forks:
+ if (mixer_fd > 0) {
+ if (fcntl(mixer_fd,F_SETFD,true) < 0) {
+ perror("fcntl socket");exit(1);
+ }
+ }
+
+ if (ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &supportedMixers) == -1){
+ perror("Unable to get mixer info assuming master volume");
+ volumeIoctl=SOUND_MIXER_WRITE_VOLUME;
+ } else {
+ if ((supportedMixers & SOUND_MASK_PCM) != 0)
+ volumeIoctl=SOUND_MIXER_WRITE_PCM;
+ else
+ volumeIoctl=0;
+ }
+
+ return (mixer_fd > 0);
+}
+
+
+void mixerClose() {
+ if (mixer_fd != -1) {
+ close(mixer_fd);
+ }
+}
+
+/*
+ only code this if your system can change the volume while
+ playing
+*/
+void mixerSetVolume(int leftVolume,int rightVolume) {
+ int volume;
+
+ volume=leftVolume+(rightVolume<<8);
+ if ((mixer_fd != -1) && (volumeIoctl!=0)) {
+ if (ioctl(mixer_fd, volumeIoctl, &volume) < 0) {
+ perror("Unable to set sound volume");
+ }
+ }
+}
+
+
+
+int audioWrite(char *buffer, int count) {
+ return(write(audio_fd,buffer,count));
+}
+
+
+int getAudioFd() {
+ return(audio_fd);
+}
diff --git a/mpeglib/lib/util/audio/audioIO_SDL.cpp b/mpeglib/lib/util/audio/audioIO_SDL.cpp
new file mode 100644
index 00000000..782fa388
--- /dev/null
+++ b/mpeglib/lib/util/audio/audioIO_SDL.cpp
@@ -0,0 +1,164 @@
+/*
+ audio wrapper for SDL
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+#include "../../input/bufferInputStream.h"
+#include <assert.h>
+#include <iostream.h>
+#if defined WIN32
+#include <SDL.h>
+#include <SDL_audio.h>
+#else
+#include <SDL/SDL.h>
+#include <SDL/SDL_audio.h>
+#endif
+
+//static SDL_AudioSpec actual;
+static BufferInputStream* audioRing;
+static TimeStamp* dummy;
+static int lOpen=false;
+
+
+
+int audioConstruct() {
+ cout << "audioConstruct ********* SDL"<<endl;
+ if ( SDL_Init(SDL_INIT_AUDIO) < 0 ) {
+ fprintf(stderr, "Warning: Couldn't init SDL audio: %s\n",
+ SDL_GetError());
+ exit(0);
+ }
+ atexit(SDL_Quit);
+ audioRing=new BufferInputStream(1024*65,1024*8,"audioSDL");
+ audioRing->open("audioSDL");
+ dummy=new TimeStamp();
+ lOpen=false;
+ return true;
+}
+
+
+void audioDestruct() {
+ delete audioRing;
+ delete dummy;
+}
+
+
+
+
+int audioOpen() {
+ return true;
+}
+
+
+void audioClose() {
+ lOpen=false;
+ SDL_CloseAudio();
+}
+
+
+void audioCallback(void *, Uint8 *stream, int len) {
+ char* startPtr;
+ TimeStamp* start;
+ int bytePos;
+
+ int read=audioRing->readRemote(&startPtr,len);
+ SDL_MixAudio(stream, (Uint8*) startPtr, read, SDL_MIX_MAXVOLUME);
+
+ audioRing->forwardReadPtr(read);
+ // dequeue time stamps
+ bytePos=audioRing->getBytePosition();
+ start=audioRing->getTimeStamp(bytePos);
+
+}
+
+
+
+void audioInit(int sampleSize,int frequency, int stereo, int sign, int big) {
+ if( sign == 0 )
+ {
+ fprintf(stderr,
+ "%s, %d: expecting signed audio data, "
+ "initialized unsigned (ignored)\n",
+ __FILE__, __LINE__ );
+ }
+ if( big != 0 )
+ {
+ fprintf(stderr,
+ "%s, %d: expecting little endian audio data, "
+ "initialized big endian (ignored)\n",
+ __FILE__, __LINE__ );
+ }
+
+ cout << "SDL audioInit: "
+ << " sampleSize:"<<sampleSize
+ << " frequency:"<<frequency
+ << " stereo:"<<stereo<<endl;
+ if (lOpen==true) {
+ cout << "SDL is buggy, because open != init -> return"<<endl;
+ return;
+ }
+ lOpen=true;
+ SDL_AudioSpec wanted;
+ //SDL_AudioSpec actual;
+ if (sampleSize == 16) {
+ wanted.format= AUDIO_S16LSB;
+ } else {
+ wanted.format= AUDIO_S8;
+ }
+
+ wanted.freq=frequency;
+ wanted.channels=stereo+1;
+ wanted.samples = 1024;
+ wanted.callback = audioCallback;
+ wanted.userdata = NULL;
+
+ int err=SDL_OpenAudio(&wanted, NULL);
+ if (err != 0) {
+ cout << "SDL_OpenAudio not ok"<<endl;
+ cout << "error is:"<<SDL_GetError()<<endl;
+ exit(0);
+ }
+ SDL_PauseAudio(0);
+
+}
+
+
+int mixerOpen() {
+ return true;
+}
+
+
+void mixerClose() {
+}
+
+
+void mixerSetVolume(int volumeLeft,int volumeRight) {
+ cout << "volumeLeft:"<<volumeLeft
+ << " volumeRight:"<<volumeRight<<endl;
+}
+
+
+int audioWrite(char *buffer, int count) {
+
+ audioRing->write(buffer,count,dummy);
+
+
+ return count;
+}
+
+
+int getAudioFd() {
+ return false;
+}
+
+
+int getAudioBufferSize() {
+ int buf=1024*65;
+ return buf;
+}
diff --git a/mpeglib/lib/util/audio/audioIO_SunOS.cpp b/mpeglib/lib/util/audio/audioIO_SunOS.cpp
new file mode 100644
index 00000000..4e9958a1
--- /dev/null
+++ b/mpeglib/lib/util/audio/audioIO_SunOS.cpp
@@ -0,0 +1,167 @@
+/* this file is a part of amp software, (C) tomislav uzelac 1996,1997
+
+ Origional code by: tomislav uzelac
+ Modified by:
+ * Andrew Richards - moved code from audio.c
+ * Jim Crumley - ported some code from other audioIO_'s
+
+ */
+
+#include <sys/types.h>
+#include <sys/stropts.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/audioio.h>
+#include "audioIO.h"
+#include <iostream.h>
+
+/* declare these static to effectively isolate the audio device */
+
+static int audio_fd;
+static audio_info_t auinfo;
+
+
+int audioConstruct(){
+ audio_fd=-1;
+ return true;
+}
+
+void audioDestruct() {
+
+}
+
+
+/* audioOpen() */
+/* should open the audio device and perform any special initialization */
+/* returns the file descriptior of the audio device */
+
+int audioOpen() {
+ AUDIO_INITINFO(&auinfo);
+
+ if ((audio_fd = open("/dev/audio",O_RDWR))==-1) {
+ perror("unable to open the audio device");
+ }
+ // Ok here something important if your programm forks:
+ if (audio_fd > 0) {
+ if (fcntl(audio_fd,F_SETFD,true) < 0) {
+ perror("fcntl socket");exit(1);
+ }
+ }
+
+ DB(audio, msg("Audio device opened on %d\n",audio_fd) );
+ return (audio_fd > 0);
+}
+
+/* audioFlush() */
+/* should flush the audio device */
+
+inline void audioFlush() {
+ DB(audio, msg("audio: flush %d\n",audio_fd) );
+}
+
+
+/* audioClose() */
+/* should close the audio device and perform any special shutdown */
+
+void audioClose() {
+ close(audio_fd);
+}
+
+
+/**
+ Audio init assumes that the audiodevice is open. It initializes
+ it to the given values
+*/
+
+void audioInit(int sampleSize ,int frequency, int stereo,int sign, int big){
+
+ if (ioctl(audio_fd,AUDIO_GETINFO,&auinfo)<0)
+ perror("Unable to get audio info");
+
+ auinfo.play.precision=sampleSize;
+ auinfo.play.encoding=AUDIO_ENCODING_LINEAR;
+ auinfo.play.channels=(stereo ? 2 : 1);
+ DB(audio, msg("setting sample rate to %d Hz",frequency) );
+ auinfo.play.sample_rate=frequency;
+ if (ioctl(audio_fd,AUDIO_SETINFO,&auinfo)<0)
+ perror("Unable to set audio info");
+
+}
+
+/*
+ only code this if your system can change the volume while
+ playing
+*/
+
+
+int getAudioBufferSize() {
+ int buf;
+ if (ioctl(audio_fd,AUDIO_GETINFO,&auinfo) == -1) {
+ perror("ioctl getAudioBufferSize using default");
+ buf=1024*65;
+ } else {
+ buf=auinfo.play.buffer_size;
+ }
+ return buf;
+}
+
+
+void mixerSetVolume(int leftVolume,int rightVolume) {
+ int volume;
+
+ volume=(leftVolume+rightVolume)/2;
+ auinfo.play.gain=(volume*255)/100;
+
+ // now normalize to values 0...32
+ leftVolume=(32*leftVolume)/100;
+ rightVolume=(32*rightVolume)/100;
+
+ // eg : leftVolume=32, rightVolume=32 => balance=32
+ // eg : leftVolume=0, rightVolume=32 => balance=64
+ // eg : leftVolume=32, rightVolume=0 => balance=0
+
+ //cout << "leftVolume:"<<leftVolume<<endl;
+ //cout << "rightVolume:"<<rightVolume<<endl;
+ int balance=leftVolume-rightVolume+32;
+ //cout << "balance:"<<balance<<endl;
+ //someone should fix the volume on solaris
+ balance=0;
+
+ auinfo.play.balance=(uchar_t)balance;
+ if (ioctl(audio_fd,AUDIO_SETINFO,&auinfo)<0)
+ perror("Unable to set sound volume");
+}
+
+
+
+
+int mixerOpen() {
+ return true;
+}
+
+void mixerClose() {
+}
+
+
+
+
+/* audioWrite */
+/* writes count bytes from buffer to the audio device */
+/* returns the number of bytes actually written */
+
+int audioWrite(char *buffer, int count)
+{
+ return(write(audio_fd,buffer,count));
+}
+
+/* Let buffer.c have the audio descriptor so it can select on it. This means */
+/* that the program is dependent on an file descriptor to work. Should really */
+/* move the select's etc (with inlines of course) in here so that this is the */
+/* ONLY file which has hardware dependent audio stuff in it */
+
+int
+getAudioFd()
+{
+ return(audio_fd);
+}
diff --git a/mpeglib/lib/util/audio/dspWrapper.cpp b/mpeglib/lib/util/audio/dspWrapper.cpp
new file mode 100644
index 00000000..cd9afa57
--- /dev/null
+++ b/mpeglib/lib/util/audio/dspWrapper.cpp
@@ -0,0 +1,193 @@
+/*
+ a wrapper for the audioDevice.
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "dspWrapper.h"
+#include "audioIO.h"
+
+
+#include "../../frame/pcmFrame.h"
+#include "../../frame/floatFrame.h"
+
+#include <iostream>
+
+using namespace std;
+
+DSPWrapper::DSPWrapper() {
+ currentFormat=new PCMFrame(0);
+ lopenDevice=false;
+ lopenMixer=false;
+
+ audioConstruct();
+}
+
+DSPWrapper::~DSPWrapper() {
+ if (lopenDevice) {
+ audioClose();
+ }
+ if (lopenMixer) {
+ mixerClose();
+ }
+ audioDestruct();
+ delete currentFormat;
+}
+
+
+int DSPWrapper::isOpenDevice() {
+ return lopenDevice;
+}
+
+int DSPWrapper::openDevice() {
+ if (lopenDevice==true) {
+ return true;
+ }
+ lopenDevice=audioOpen();
+ return lopenDevice;
+}
+
+int DSPWrapper::closeDevice() {
+ if (isOpenDevice() == true) {
+ audioClose();
+ currentFormat->setFrameFormat(-1,-1);
+ lopenDevice=false;
+ }
+ return true;
+}
+
+
+int DSPWrapper::isOpenMixer() {
+ return lopenMixer;
+}
+
+
+int DSPWrapper::getAudioBufferSize() {
+ return ::getAudioBufferSize();
+}
+
+
+int DSPWrapper::openMixer() {
+ lopenMixer=mixerOpen();
+ return lopenMixer;
+}
+
+int DSPWrapper::closeMixer() {
+ if (isOpenMixer() == true) {
+ mixerClose();
+ lopenMixer=false;
+ }
+ return true;
+}
+
+
+int DSPWrapper::audioPlay(char *buf, int len) {
+ return audioWrite(buf,len);
+}
+
+int DSPWrapper::audioSetup(int stereo,int sampleSize,int lSigned,
+ int lBigEndian,int freq) {
+
+
+ if (isOpenDevice()==false) {
+ cout << "device not open"<<endl;
+ exit(-1);
+ }
+ /*
+ cout << "sampleSize:"<<sampleSize<<endl;
+ cout << "freq:"<<freq<<endl;
+ cout << "stereo:"<<stereo<<endl;
+ cout << "lSigned:"<<lSigned<<endl;
+ cout << "lBigEndian:"<<lBigEndian<<endl;
+ */
+ audioInit(sampleSize,freq,stereo,lSigned,lBigEndian);
+ if (currentFormat->getSampleSize() != sampleSize) {
+ cout << "FIXME: pcmFrame with sampleSize:"<<sampleSize<<endl;
+ }
+ currentFormat->setFrameFormat(stereo,freq);
+ return true;
+}
+
+int DSPWrapper::audioSetup(AudioFrame* audioFrame) {
+ if (audioFrame == NULL) {
+ cout << "audioFrame NULL: DSPWrapper:audioSetup"<<endl;
+ exit(0);
+ }
+ if(audioFrame->isFormatEqual(currentFormat)==false) {
+ audioSetup(audioFrame->getStereo(),audioFrame->getSampleSize(),
+ audioFrame->getSigned(),audioFrame->getBigEndian(),
+ audioFrame->getFrequenceHZ());
+ }
+ return true;
+}
+
+
+int DSPWrapper::audioPlay(PCMFrame* pcmFrame) {
+ if (pcmFrame == NULL) {
+ cout << "pcmFrame NULL: DSPWrapper:audioPlay"<<endl;
+ exit(0);
+ }
+ if(pcmFrame->isFormatEqual(currentFormat)==false) {
+ audioSetup(pcmFrame->getStereo(),pcmFrame->getSampleSize(),
+ pcmFrame->getSigned(),pcmFrame->getBigEndian(),
+ pcmFrame->getFrequenceHZ());
+ }
+ int len=pcmFrame->getLen()*2;
+ int played=audioPlay((char*)pcmFrame->getData(),len);
+ return (len == played);
+}
+
+
+//
+// Misuse our internal currentFormat for the float->int conversion.
+//
+
+int DSPWrapper::audioPlay(FloatFrame* floatFrame) {
+ if (floatFrame == NULL) {
+ cout << "floatFrame NULL: DSPWrapper:audioPlay"<<endl;
+ exit(0);
+ }
+ if(floatFrame->isFormatEqual(currentFormat)==false) {
+ audioSetup(floatFrame->getStereo(),floatFrame->getSampleSize(),
+ floatFrame->getSigned(),floatFrame->getBigEndian(),
+ floatFrame->getFrequenceHZ());
+ }
+
+ int tmpLen=currentFormat->getLen();
+ if (tmpLen < floatFrame->getLen()) {
+ delete currentFormat;
+ currentFormat=new PCMFrame(floatFrame->getLen());
+ floatFrame->copyFormat(currentFormat);
+ }
+ currentFormat->clearrawdata();
+ currentFormat->putFloatData(floatFrame->getData(),floatFrame->getLen());
+ return audioPlay(currentFormat);
+}
+
+void DSPWrapper::audioFlush() {
+ closeDevice();
+}
+
+
+void DSPWrapper::setVolume(float leftPercent,float rightPercent) {
+ if (isOpenMixer()) {
+ mixerSetVolume((int)leftPercent,(int)rightPercent);
+ } else {
+ cout << "cannot set Mixer settings:not open!"<<endl;
+ }
+}
+
+
+void DSPWrapper::print() {
+ cout<<"lopenDevice:"<<lopenDevice<<endl;
+ cout<<"lopenMixer:"<<lopenMixer<<endl;
+ currentFormat->print("currentFormat");
+
+}
diff --git a/mpeglib/lib/util/audio/dspWrapper.h b/mpeglib/lib/util/audio/dspWrapper.h
new file mode 100644
index 00000000..f323096d
--- /dev/null
+++ b/mpeglib/lib/util/audio/dspWrapper.h
@@ -0,0 +1,75 @@
+/*
+ a wrapper for the audioDevice.
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef _DSPWRAPPER_H
+#define _DSPWRAPPER_H
+
+class AudioFrame;
+class PCMFrame;
+class FloatFrame;
+#include <kdemacros.h>
+
+/**
+ This class wraps the platform specific /dev/dsp implementation.
+ The only unusal thing is, that it supports each order of
+ init/open.
+ i) you can first init the device and the open
+ ii) you can first open the device and the init it
+ The implementation takes care that the calls are forwarded
+ in the right order to the /dev/dsp implementation.
+ (means: before the init it, we need to open it)
+ But a caller can do it in both orders.
+*/
+
+class KDE_EXPORT DSPWrapper {
+
+ int lopenDevice;
+ int lopenMixer;
+ PCMFrame* currentFormat;
+
+
+ public:
+ DSPWrapper();
+ ~DSPWrapper();
+
+ int openDevice();
+ int closeDevice();
+ int isOpenDevice();
+
+ int openMixer();
+ int closeMixer();
+ int isOpenMixer();
+
+ int getAudioBufferSize();
+ void setVolume(float leftPercent,float rightPercent);
+
+ int audioSetup(int stereo,int sampleSize,int lSigned,
+ int lBigEndian,int freq);
+ int audioSetup(AudioFrame* audioFrame);
+
+ int audioPlay(char *buffer, int size);
+ int audioPlay(PCMFrame* pcmFrame);
+ int audioPlay(FloatFrame* floatFrame);
+ void audioFlush();
+
+ int isEqual(int samplesize,int speed,int stereo,int lSigned,int lBigEndian);
+ int write(char* buf,int len);
+ void print();
+};
+
+#endif
+
+
+
+
diff --git a/mpeglib/lib/util/dynBuffer.cpp b/mpeglib/lib/util/dynBuffer.cpp
new file mode 100644
index 00000000..c93d5381
--- /dev/null
+++ b/mpeglib/lib/util/dynBuffer.cpp
@@ -0,0 +1,166 @@
+/*
+ This class implements a dynamic string buffer
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "dynBuffer.h"
+
+#include <iostream>
+
+using namespace std;
+
+
+DynBuffer::DynBuffer(int size) {
+ nSize=size;
+ msg=(char*) malloc(sizeof(char)*(nSize+1));
+ msg[nSize]='\0';
+ clear();
+
+}
+
+
+DynBuffer::~DynBuffer() {
+ free (msg);
+}
+
+
+void DynBuffer::clear() {
+ msg[0]='\0';
+}
+
+void DynBuffer::append(int value) {
+ DynBuffer buf(30);
+ sprintf(buf.getData(),"%d",value);
+ append(buf.getData());
+}
+
+
+
+
+void DynBuffer::append(char* appendMsg) {
+ if (appendMsg == msg) {
+ cout << "cannot append to self"<<endl;
+ exit(0);
+ }
+ char* appendPos=getAppendPos();
+ int nlen=strlen(appendMsg);
+ if (appendPos == NULL) return;
+
+ append(appendMsg,nlen);
+}
+
+
+void DynBuffer::append(const char* appendMsg) {
+ append((char*)appendMsg);
+}
+
+
+void DynBuffer::append(char* buffer,int buflen) {
+ int nlen=len();
+ int nBedarf;
+ if (buffer == msg) {
+ cout << "cannot append to self"<<endl;
+ exit(0);
+ }
+ if (buflen+nlen <= nSize) {
+ char* appendPos=getAppendPos();
+ strncpy(appendPos,buffer,buflen);
+ appendPos[buflen]='\0';
+ return;
+ }
+ nBedarf=(nlen+buflen)-nSize;
+ grow(nBedarf);
+ append(buffer,buflen);
+}
+
+char* DynBuffer::getAppendPos() {
+ int i;
+ // this Array has nSize+1 entries!
+ // and it *is* granted that msg[nSize]=0; (think so)
+ for (i=0;i<=nSize;i++) {
+ if (msg[i] == '\0') return &(msg[i]);
+ }
+ // should never reach this point
+ return NULL;
+}
+
+
+void DynBuffer::setData(char* msg) {
+ if (strlen(msg) == 0) {
+ clear();
+ return;
+ }
+ clear();
+ append(msg);
+}
+
+char* DynBuffer::getData() {
+ return msg;
+}
+
+
+int DynBuffer::len() {
+ return strlen(msg);
+}
+
+int DynBuffer::getSize() {
+ return nSize;
+}
+
+void DynBuffer::grow(int size) {
+ int i;
+ int newSize=nSize+size;
+ char* tmp=(char*) malloc(sizeof(char)*(newSize+1));
+ tmp[newSize]='\0';
+ for(i=0;i<=nSize;i++) {
+ tmp[i]=msg[i];
+ }
+
+ nSize=newSize;
+ free(msg);
+ msg=tmp;
+
+}
+
+
+int DynBuffer::find(char zeichen) {
+ int i;
+ int nlen=len();
+ for(i=0;i<nlen;i++) {
+ if (msg[i] == zeichen) return i;
+ }
+ return -1;
+}
+
+
+
+void DynBuffer::forward(int bytes) {
+ int i;
+ int aktPos;
+ int nlen=len();
+ if (bytes > nlen) {
+ bytes=nlen;
+ }
+ i=0;
+ aktPos=bytes;
+ while(aktPos <= nlen) {
+ msg[i]=msg[aktPos];
+ i++;
+ aktPos++;
+ }
+}
+
+
+
+void DynBuffer::print() {
+ printf("DynBuffer:%s\n",msg);
+}
diff --git a/mpeglib/lib/util/dynBuffer.h b/mpeglib/lib/util/dynBuffer.h
new file mode 100644
index 00000000..7ba99d42
--- /dev/null
+++ b/mpeglib/lib/util/dynBuffer.h
@@ -0,0 +1,63 @@
+/*
+ This class implements a static string buffer
+ Copyright (C) 1998 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __DYNBUFFER_H
+#define __DYNBUFFER_H
+
+
+extern "C" {
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+ }
+
+class DynBuffer {
+
+ char* msg;
+ int nSize;
+
+ public:
+ DynBuffer(int size);
+ ~DynBuffer();
+
+ void clear();
+ void append(int value); // appends values as string
+ void append(char* msg);
+ void append(const char* msg);
+
+ void append(char* buffer,int buflen);
+
+ int find(char zeichen);
+ int len();
+
+ void setData(char* msg);
+ char* getData();
+
+ int getSize();
+ void grow(int size);
+
+ void forward(int bytes);
+ void print();
+ private:
+ char* getAppendPos();
+ void read(FILE stream);
+
+};
+
+
+#endif
+
+
+
diff --git a/mpeglib/lib/util/file/Makefile.am b/mpeglib/lib/util/file/Makefile.am
new file mode 100644
index 00000000..96b16fff
--- /dev/null
+++ b/mpeglib/lib/util/file/Makefile.am
@@ -0,0 +1,25 @@
+# libsplay - Makefile.am
+
+
+INCLUDES = $(all_includes)
+
+noinst_LTLIBRARIES = libsimplefile.la
+
+noinst_HEADERS = fileAccess.h
+
+kmpgincludedir = $(includedir)/$(THIS_LIB_NAME)/util/file
+
+kmpginclude_HEADERS = fileAccess.h
+
+
+libsimplefile_la_SOURCES = fileAccess.cpp
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/lib/util/file/fileAccess.cpp b/mpeglib/lib/util/file/fileAccess.cpp
new file mode 100644
index 00000000..8eab864f
--- /dev/null
+++ b/mpeglib/lib/util/file/fileAccess.cpp
@@ -0,0 +1,95 @@
+/*
+ simple file access interface.
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "fileAccess.h"
+
+
+FileAccess::FileAccess() {
+ file=NULL;
+ length=0;
+}
+
+
+FileAccess::~FileAccess() {
+ close();
+}
+
+
+int FileAccess::open(const char* path) {
+ close();
+ file=fopen(path,"rb");
+ length=calcByteLength();
+ return (file != NULL);
+}
+
+
+void FileAccess::close() {
+ if (file != NULL) {
+ fclose(file);
+ file=NULL;
+ length=0;
+ }
+}
+
+
+int FileAccess::read(char* dest,int len) {
+ int back=0;
+ if (file != NULL) {
+ back=fread(dest,1,len,file);
+ } else {
+ printf("FileAccess::read not open\n");
+ }
+ return back;
+}
+
+int FileAccess::eof() {
+ int back=true;
+ if (file != NULL) {
+ back=feof(file);
+ }
+ return back;
+}
+
+
+int FileAccess::seek(long pos) {
+ if (file == NULL) {
+ return -1;
+ }
+ return fseek(file,pos,SEEK_SET);
+}
+
+
+long FileAccess::getBytePosition() {
+ if (file == NULL) {
+ return 0;
+ }
+ return ftell(file);
+}
+
+
+long FileAccess::getByteLength() {
+ return length;
+}
+
+long FileAccess::calcByteLength() {
+ if (file == NULL) {
+ return 0;
+ }
+ long pos=getBytePosition();
+ fseek(file,0,SEEK_END);
+ long back=getBytePosition();
+ fseek(file,pos,SEEK_SET);
+ return back;
+}
+
diff --git a/mpeglib/lib/util/file/fileAccess.h b/mpeglib/lib/util/file/fileAccess.h
new file mode 100644
index 00000000..d9bf9e2e
--- /dev/null
+++ b/mpeglib/lib/util/file/fileAccess.h
@@ -0,0 +1,46 @@
+/*
+ simple file access interface.
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#ifndef __FILEACCESS_H
+#define __FILEACCESS_H
+
+
+#include "stdio.h"
+
+/**
+ Extension to the normal "read" function.
+ This interface is useful for the AudioInfo.
+*/
+
+class FileAccess {
+
+ FILE* file;
+ long length;
+
+ public:
+ FileAccess();
+ virtual ~FileAccess();
+
+ virtual int open(const char* file);
+ virtual void close();
+ virtual int read(char* dest,int len);
+ virtual int eof();
+ virtual int seek(long pos);
+ virtual long getBytePosition();
+ virtual long getByteLength();
+
+ private:
+ long calcByteLength();
+
+};
+#endif
+
diff --git a/mpeglib/lib/util/mmx/Makefile.am b/mpeglib/lib/util/mmx/Makefile.am
new file mode 100644
index 00000000..ce8875e0
--- /dev/null
+++ b/mpeglib/lib/util/mmx/Makefile.am
@@ -0,0 +1,46 @@
+# ---- @OS_TYPE@/@ARCH_TYPE@ ----
+
+# For cpu_accel compile we cannot have ansi
+# (I dont have debugged why)
+
+INCLUDES = $(all_includes)
+
+noinst_LTLIBRARIES = libmmx.la
+noinst_HEADERS =
+
+kmpgincludedir = $(includedir)/$(THIS_LIB_NAME)/util/mmx
+
+kmpginclude_HEADERS = mmx.h mmx_asm.h mm_accel.h
+
+libmmx_la_SOURCES = cpu_accel.c mmx.c
+
+AM_ASFLAGS = $(DEFS) $(DEFAULT_INCLUDES) $(all_includes)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/lib/util/mmx/attribute.h b/mpeglib/lib/util/mmx/attribute.h
new file mode 100644
index 00000000..4dfd3e1e
--- /dev/null
+++ b/mpeglib/lib/util/mmx/attribute.h
@@ -0,0 +1,33 @@
+/*
+ align attribut definition (g++)
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __ATTRIBUTE_H
+#define __ATTRIBUTE_H
+
+
+
+#include "vidi/vidi_config.h"
+
+
+/* use gcc attribs to align critical data structures */
+
+#ifdef ATTRIBUTE_ALIGNED_MAX
+#define ATTR_ALIGN(align) __attribute__ \
+ ((__aligned__ ((ATTRIBUTE_ALIGNED_MAX <align) ? \
+ ATTRIBUTE_ALIGNED_MAX : align)))
+#else
+#define ATTR_ALIGN(align)
+#endif
+
+
+#endif
diff --git a/mpeglib/lib/util/mmx/cpu_accel.c b/mpeglib/lib/util/mmx/cpu_accel.c
new file mode 100644
index 00000000..8f138308
--- /dev/null
+++ b/mpeglib/lib/util/mmx/cpu_accel.c
@@ -0,0 +1,162 @@
+/*
+ * cpu_accel.c
+ * Copyright (C) 2000-2001 Michel Lespinasse <walken@zoy.org>
+ * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ * See http://libmpeg2.sourceforge.net/ for updates.
+ *
+ * mpeg2dec 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.
+ *
+ * mpeg2dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+
+#if defined(MMX_SUPPORT) || defined(INTEL)
+#define ARCH_X86 1
+#endif
+
+#include <inttypes.h>
+
+#include "mm_accel.h"
+
+#ifdef ARCH_X86
+static uint32_t arch_accel (void)
+{
+ uint32_t eax, ebx, ecx, edx;
+ int AMD;
+ uint32_t caps;
+
+#ifndef PIC
+#define cpuid(op,eax,ebx,ecx,edx) \
+ __asm__ ("cpuid" \
+ : "=a" (eax), \
+ "=b" (ebx), \
+ "=c" (ecx), \
+ "=d" (edx) \
+ : "a" (op) \
+ : "cc")
+#else /* PIC version : save ebx */
+#define cpuid(op,eax,ebx,ecx,edx) \
+ __asm__ ("pushl %%ebx\n\t" \
+ "cpuid\n\t" \
+ "movl %%ebx,%1\n\t" \
+ "popl %%ebx" \
+ : "=a" (eax), \
+ "=r" (ebx), \
+ "=c" (ecx), \
+ "=d" (edx) \
+ : "a" (op) \
+ : "cc")
+#endif
+
+ __asm__ ("pushfl\n\t"
+ "pushfl\n\t"
+ "popl %0\n\t"
+ "movl %0,%1\n\t"
+ "xorl $0x200000,%0\n\t"
+ "pushl %0\n\t"
+ "popfl\n\t"
+ "pushfl\n\t"
+ "popl %0\n\t"
+ "popfl"
+ : "=r" (eax),
+ "=r" (ebx)
+ :
+ : "cc");
+
+ if (eax == ebx) /* no cpuid */
+ return 0;
+
+ cpuid (0x00000000, eax, ebx, ecx, edx);
+ if (!eax) /* vendor string only */
+ return 0;
+
+ AMD = (ebx == 0x68747541) && (ecx == 0x444d4163) && (edx == 0x69746e65);
+
+ cpuid (0x00000001, eax, ebx, ecx, edx);
+ if (! (edx & 0x00800000)) /* no MMX */
+ return 0;
+
+ caps = MM_ACCEL_X86_MMX;
+ if (edx & 0x02000000) /* SSE - identical to AMD MMX extensions */
+ caps = MM_ACCEL_X86_MMX | MM_ACCEL_X86_MMXEXT;
+
+ cpuid (0x80000000, eax, ebx, ecx, edx);
+ if (eax < 0x80000001) /* no extended capabilities */
+ return caps;
+
+ cpuid (0x80000001, eax, ebx, ecx, edx);
+
+ if (edx & 0x80000000)
+ caps |= MM_ACCEL_X86_3DNOW;
+
+ if (AMD && (edx & 0x00400000)) /* AMD MMX extensions */
+ caps |= MM_ACCEL_X86_MMXEXT;
+
+ return caps;
+}
+#endif /* ARCH_X86 */
+
+#ifdef ARCH_PPC
+#include <signal.h>
+#include <setjmp.h>
+
+static sigjmp_buf jmpbuf;
+static volatile sig_atomic_t canjump = 0;
+
+static RETSIGTYPE sigill_handler (int sig)
+{
+ if (!canjump) {
+ signal (sig, SIG_DFL);
+ raise (sig);
+ }
+
+ canjump = 0;
+ siglongjmp (jmpbuf, 1);
+}
+
+static uint32_t arch_accel (void)
+{
+ signal (SIGILL, sigill_handler);
+ if (sigsetjmp (jmpbuf, 1)) {
+ signal (SIGILL, SIG_DFL);
+ return 0;
+ }
+
+ canjump = 1;
+
+ __asm__ volatile ("mtspr 256,%0" :: "r" (-1));
+
+ signal (SIGILL, SIG_DFL);
+ return MM_ACCEL_PPC_ALTIVEC;
+}
+#endif /* ARCH_PPC */
+
+uint32_t mm_accel (void)
+{
+#if defined (ARCH_X86) || defined (ARCH_PPC)
+ static int got_accel = 0;
+ static uint32_t accel;
+
+ if (!got_accel) {
+ got_accel = 1;
+ accel = arch_accel ();
+ }
+
+ return accel;
+#else
+ return 0;
+#endif
+}
diff --git a/mpeglib/lib/util/mmx/mm_accel.h b/mpeglib/lib/util/mmx/mm_accel.h
new file mode 100644
index 00000000..e0690804
--- /dev/null
+++ b/mpeglib/lib/util/mmx/mm_accel.h
@@ -0,0 +1,58 @@
+/*
+ * mm_accel.h
+ * Copyright (C) 2000-2001 Michel Lespinasse <walken@zoy.org>
+ * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ * See http://libmpeg2.sourceforge.net/ for updates.
+ *
+ * mpeg2dec 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.
+ *
+ * mpeg2dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MM_ACCEL_H
+#define MM_ACCEL_H
+
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/* generic accelerations */
+#define MM_ACCEL_MLIB 0x00000001
+
+/* x86 accelerations */
+#define MM_ACCEL_X86_MMX 0x80000000
+#define MM_ACCEL_X86_3DNOW 0x40000000
+#define MM_ACCEL_X86_MMXEXT 0x20000000
+
+/* powerpc accelerations */
+#define MM_ACCEL_PPC_ALTIVEC 0x80000000
+
+/* detailed SIMD info */
+uint32_t mm_accel (void);
+
+/* true if MMX support */
+int mm_support();
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif
+/* MM_ACCEL_H */
diff --git a/mpeglib/lib/util/mmx/mmx.c b/mpeglib/lib/util/mmx/mmx.c
new file mode 100644
index 00000000..4ac3d6b8
--- /dev/null
+++ b/mpeglib/lib/util/mmx/mmx.c
@@ -0,0 +1,73 @@
+/*
+ wrapper for MMX calls
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "mmx.h"
+
+static int mmSupport=-1;
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if defined(MMX_SUPPORT) || defined(INTEL)
+#define HAVE_ACCEL
+#endif
+
+
+#ifndef HAVE_ACCEL
+int emms() {
+ printf("urgs! emms() never should happen\n");
+ exit(0);
+}
+
+int mm_support() {
+ return 0;
+}
+
+#endif
+
+
+#ifdef HAVE_ACCEL
+#include <inttypes.h>
+#include "mm_accel.h"
+
+
+
+
+int emms() {
+ __asm__ ("emms");
+ return 1;
+}
+
+
+int mm_support() {
+
+ int val;
+
+ if (mmSupport == -1) {
+
+ val=mm_accel();
+ if (val & MM_ACCEL_X86_MMX) {
+ mmSupport=1;
+ } else {
+ mmSupport=0;
+ }
+
+ }
+ /* Return */
+ return(mmSupport);
+}
+
+
+#endif
diff --git a/mpeglib/lib/util/mmx/mmx.h b/mpeglib/lib/util/mmx/mmx.h
new file mode 100644
index 00000000..c4b8340d
--- /dev/null
+++ b/mpeglib/lib/util/mmx/mmx.h
@@ -0,0 +1,26 @@
+
+
+
+#ifndef __MMX_H
+#define __MMX_H
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+extern int emms();
+extern int mm_support();
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif
diff --git a/mpeglib/lib/util/mmx/mmx_asm.h b/mpeglib/lib/util/mmx/mmx_asm.h
new file mode 100644
index 00000000..8717eff5
--- /dev/null
+++ b/mpeglib/lib/util/mmx/mmx_asm.h
@@ -0,0 +1,258 @@
+/*
+ * mmx.h
+ * Copyright (C) 1997-2001 H. Dietz and R. Fisher
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ *
+ * mpeg2dec 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.
+ *
+ */
+
+/*
+ * The type of an value that fits in an MMX register (note that long
+ * long constant values MUST be suffixed by LL and unsigned long long
+ * values by ULL, lest they be truncated by the compiler)
+ */
+
+#ifndef __MMX_ASM_H
+#define __MMX_ASM_H
+
+#include "attribute.h"
+
+
+typedef union {
+ long long q; /* Quadword (64-bit) value */
+ unsigned long long uq; /* Unsigned Quadword */
+ int d[2]; /* 2 Doubleword (32-bit) values */
+ unsigned int ud[2]; /* 2 Unsigned Doubleword */
+ short w[4]; /* 4 Word (16-bit) values */
+ unsigned short uw[4]; /* 4 Unsigned Word */
+ char b[8]; /* 8 Byte (8-bit) values */
+ unsigned char ub[8]; /* 8 Unsigned Byte */
+ float s[2]; /* Single-precision (32-bit) value */
+} ATTR_ALIGN(8) mmx_t; /* On an 8-byte (64-bit) boundary */
+
+
+#define mmx_i2r(op,imm,reg) \
+ __asm__ __volatile__ (#op " %0, %%" #reg \
+ : /* nothing */ \
+ : "X" (imm) )
+
+#define mmx_m2r(op,mem,reg) \
+ __asm__ __volatile__ (#op " %0, %%" #reg \
+ : /* nothing */ \
+ : "X" (mem))
+
+#define mmx_r2m(op,reg,mem) \
+ __asm__ __volatile__ (#op " %%" #reg ", %0" \
+ : "=X" (mem) \
+ : /* nothing */ )
+
+#define mmx_r2r(op,regs,regd) \
+ __asm__ __volatile__ (#op " %" #regs ", %" #regd)
+
+
+#define emms() __asm__ __volatile__ ("emms")
+
+#define movd_m2r(var,reg) mmx_m2r (movd, var, reg)
+#define movd_r2m(reg,var) mmx_r2m (movd, reg, var)
+#define movd_r2r(regs,regd) mmx_r2r (movd, regs, regd)
+
+#define movq_m2r(var,reg) mmx_m2r (movq, var, reg)
+#define movq_r2m(reg,var) mmx_r2m (movq, reg, var)
+#define movq_r2r(regs,regd) mmx_r2r (movq, regs, regd)
+
+#define packssdw_m2r(var,reg) mmx_m2r (packssdw, var, reg)
+#define packssdw_r2r(regs,regd) mmx_r2r (packssdw, regs, regd)
+#define packsswb_m2r(var,reg) mmx_m2r (packsswb, var, reg)
+#define packsswb_r2r(regs,regd) mmx_r2r (packsswb, regs, regd)
+
+#define packuswb_m2r(var,reg) mmx_m2r (packuswb, var, reg)
+#define packuswb_r2r(regs,regd) mmx_r2r (packuswb, regs, regd)
+
+#define paddb_m2r(var,reg) mmx_m2r (paddb, var, reg)
+#define paddb_r2r(regs,regd) mmx_r2r (paddb, regs, regd)
+#define paddd_m2r(var,reg) mmx_m2r (paddd, var, reg)
+#define paddd_r2r(regs,regd) mmx_r2r (paddd, regs, regd)
+#define paddw_m2r(var,reg) mmx_m2r (paddw, var, reg)
+#define paddw_r2r(regs,regd) mmx_r2r (paddw, regs, regd)
+
+#define paddsb_m2r(var,reg) mmx_m2r (paddsb, var, reg)
+#define paddsb_r2r(regs,regd) mmx_r2r (paddsb, regs, regd)
+#define paddsw_m2r(var,reg) mmx_m2r (paddsw, var, reg)
+#define paddsw_r2r(regs,regd) mmx_r2r (paddsw, regs, regd)
+
+#define paddusb_m2r(var,reg) mmx_m2r (paddusb, var, reg)
+#define paddusb_r2r(regs,regd) mmx_r2r (paddusb, regs, regd)
+#define paddusw_m2r(var,reg) mmx_m2r (paddusw, var, reg)
+#define paddusw_r2r(regs,regd) mmx_r2r (paddusw, regs, regd)
+
+#define pand_m2r(var,reg) mmx_m2r (pand, var, reg)
+#define pand_r2r(regs,regd) mmx_r2r (pand, regs, regd)
+
+#define pandn_m2r(var,reg) mmx_m2r (pandn, var, reg)
+#define pandn_r2r(regs,regd) mmx_r2r (pandn, regs, regd)
+
+#define pcmpeqb_m2r(var,reg) mmx_m2r (pcmpeqb, var, reg)
+#define pcmpeqb_r2r(regs,regd) mmx_r2r (pcmpeqb, regs, regd)
+#define pcmpeqd_m2r(var,reg) mmx_m2r (pcmpeqd, var, reg)
+#define pcmpeqd_r2r(regs,regd) mmx_r2r (pcmpeqd, regs, regd)
+#define pcmpeqw_m2r(var,reg) mmx_m2r (pcmpeqw, var, reg)
+#define pcmpeqw_r2r(regs,regd) mmx_r2r (pcmpeqw, regs, regd)
+
+#define pcmpgtb_m2r(var,reg) mmx_m2r (pcmpgtb, var, reg)
+#define pcmpgtb_r2r(regs,regd) mmx_r2r (pcmpgtb, regs, regd)
+#define pcmpgtd_m2r(var,reg) mmx_m2r (pcmpgtd, var, reg)
+#define pcmpgtd_r2r(regs,regd) mmx_r2r (pcmpgtd, regs, regd)
+#define pcmpgtw_m2r(var,reg) mmx_m2r (pcmpgtw, var, reg)
+#define pcmpgtw_r2r(regs,regd) mmx_r2r (pcmpgtw, regs, regd)
+
+#define pmaddwd_m2r(var,reg) mmx_m2r (pmaddwd, var, reg)
+#define pmaddwd_r2r(regs,regd) mmx_r2r (pmaddwd, regs, regd)
+
+#define pmulhw_m2r(var,reg) mmx_m2r (pmulhw, var, reg)
+#define pmulhw_r2r(regs,regd) mmx_r2r (pmulhw, regs, regd)
+
+#define pmullw_m2r(var,reg) mmx_m2r (pmullw, var, reg)
+#define pmullw_r2r(regs,regd) mmx_r2r (pmullw, regs, regd)
+
+#define por_m2r(var,reg) mmx_m2r (por, var, reg)
+#define por_r2r(regs,regd) mmx_r2r (por, regs, regd)
+
+#define pslld_i2r(imm,reg) mmx_i2r (pslld, imm, reg)
+#define pslld_m2r(var,reg) mmx_m2r (pslld, var, reg)
+#define pslld_r2r(regs,regd) mmx_r2r (pslld, regs, regd)
+#define psllq_i2r(imm,reg) mmx_i2r (psllq, imm, reg)
+#define psllq_m2r(var,reg) mmx_m2r (psllq, var, reg)
+#define psllq_r2r(regs,regd) mmx_r2r (psllq, regs, regd)
+#define psllw_i2r(imm,reg) mmx_i2r (psllw, imm, reg)
+#define psllw_m2r(var,reg) mmx_m2r (psllw, var, reg)
+#define psllw_r2r(regs,regd) mmx_r2r (psllw, regs, regd)
+
+#define psrad_i2r(imm,reg) mmx_i2r (psrad, imm, reg)
+#define psrad_m2r(var,reg) mmx_m2r (psrad, var, reg)
+#define psrad_r2r(regs,regd) mmx_r2r (psrad, regs, regd)
+#define psraw_i2r(imm,reg) mmx_i2r (psraw, imm, reg)
+#define psraw_m2r(var,reg) mmx_m2r (psraw, var, reg)
+#define psraw_r2r(regs,regd) mmx_r2r (psraw, regs, regd)
+
+#define psrld_i2r(imm,reg) mmx_i2r (psrld, imm, reg)
+#define psrld_m2r(var,reg) mmx_m2r (psrld, var, reg)
+#define psrld_r2r(regs,regd) mmx_r2r (psrld, regs, regd)
+#define psrlq_i2r(imm,reg) mmx_i2r (psrlq, imm, reg)
+#define psrlq_m2r(var,reg) mmx_m2r (psrlq, var, reg)
+#define psrlq_r2r(regs,regd) mmx_r2r (psrlq, regs, regd)
+#define psrlw_i2r(imm,reg) mmx_i2r (psrlw, imm, reg)
+#define psrlw_m2r(var,reg) mmx_m2r (psrlw, var, reg)
+#define psrlw_r2r(regs,regd) mmx_r2r (psrlw, regs, regd)
+
+#define psubb_m2r(var,reg) mmx_m2r (psubb, var, reg)
+#define psubb_r2r(regs,regd) mmx_r2r (psubb, regs, regd)
+#define psubd_m2r(var,reg) mmx_m2r (psubd, var, reg)
+#define psubd_r2r(regs,regd) mmx_r2r (psubd, regs, regd)
+#define psubw_m2r(var,reg) mmx_m2r (psubw, var, reg)
+#define psubw_r2r(regs,regd) mmx_r2r (psubw, regs, regd)
+
+#define psubsb_m2r(var,reg) mmx_m2r (psubsb, var, reg)
+#define psubsb_r2r(regs,regd) mmx_r2r (psubsb, regs, regd)
+#define psubsw_m2r(var,reg) mmx_m2r (psubsw, var, reg)
+#define psubsw_r2r(regs,regd) mmx_r2r (psubsw, regs, regd)
+
+#define psubusb_m2r(var,reg) mmx_m2r (psubusb, var, reg)
+#define psubusb_r2r(regs,regd) mmx_r2r (psubusb, regs, regd)
+#define psubusw_m2r(var,reg) mmx_m2r (psubusw, var, reg)
+#define psubusw_r2r(regs,regd) mmx_r2r (psubusw, regs, regd)
+
+#define punpckhbw_m2r(var,reg) mmx_m2r (punpckhbw, var, reg)
+#define punpckhbw_r2r(regs,regd) mmx_r2r (punpckhbw, regs, regd)
+#define punpckhdq_m2r(var,reg) mmx_m2r (punpckhdq, var, reg)
+#define punpckhdq_r2r(regs,regd) mmx_r2r (punpckhdq, regs, regd)
+#define punpckhwd_m2r(var,reg) mmx_m2r (punpckhwd, var, reg)
+#define punpckhwd_r2r(regs,regd) mmx_r2r (punpckhwd, regs, regd)
+
+#define punpcklbw_m2r(var,reg) mmx_m2r (punpcklbw, var, reg)
+#define punpcklbw_r2r(regs,regd) mmx_r2r (punpcklbw, regs, regd)
+#define punpckldq_m2r(var,reg) mmx_m2r (punpckldq, var, reg)
+#define punpckldq_r2r(regs,regd) mmx_r2r (punpckldq, regs, regd)
+#define punpcklwd_m2r(var,reg) mmx_m2r (punpcklwd, var, reg)
+#define punpcklwd_r2r(regs,regd) mmx_r2r (punpcklwd, regs, regd)
+
+#define pxor_m2r(var,reg) mmx_m2r (pxor, var, reg)
+#define pxor_r2r(regs,regd) mmx_r2r (pxor, regs, regd)
+
+
+/* 3DNOW extensions */
+
+#define pavgusb_m2r(var,reg) mmx_m2r (pavgusb, var, reg)
+#define pavgusb_r2r(regs,regd) mmx_r2r (pavgusb, regs, regd)
+
+
+/* AMD MMX extensions - also available in intel SSE */
+
+
+#define mmx_m2ri(op,mem,reg,imm) \
+ __asm__ __volatile__ (#op " %1, %0, %%" #reg \
+ : /* nothing */ \
+ : "X" (mem), "X" (imm))
+#define mmx_r2ri(op,regs,regd,imm) \
+ __asm__ __volatile__ (#op " %0, %%" #regs ", %%" #regd \
+ : /* nothing */ \
+ : "X" (imm) )
+
+#define mmx_fetch(mem,hint) \
+ __asm__ __volatile__ ("prefetch" #hint " %0" \
+ : /* nothing */ \
+ : "X" (mem))
+
+
+#define maskmovq(regs,maskreg) mmx_r2ri (maskmovq, regs, maskreg)
+
+#define movntq_r2m(mmreg,var) mmx_r2m (movntq, mmreg, var)
+
+#define pavgb_m2r(var,reg) mmx_m2r (pavgb, var, reg)
+#define pavgb_r2r(regs,regd) mmx_r2r (pavgb, regs, regd)
+#define pavgw_m2r(var,reg) mmx_m2r (pavgw, var, reg)
+#define pavgw_r2r(regs,regd) mmx_r2r (pavgw, regs, regd)
+
+#define pextrw_r2r(mmreg,reg,imm) mmx_r2ri (pextrw, mmreg, reg, imm)
+
+#define pinsrw_r2r(reg,mmreg,imm) mmx_r2ri (pinsrw, reg, mmreg, imm)
+
+#define pmaxsw_m2r(var,reg) mmx_m2r (pmaxsw, var, reg)
+#define pmaxsw_r2r(regs,regd) mmx_r2r (pmaxsw, regs, regd)
+
+#define pmaxub_m2r(var,reg) mmx_m2r (pmaxub, var, reg)
+#define pmaxub_r2r(regs,regd) mmx_r2r (pmaxub, regs, regd)
+
+#define pminsw_m2r(var,reg) mmx_m2r (pminsw, var, reg)
+#define pminsw_r2r(regs,regd) mmx_r2r (pminsw, regs, regd)
+
+#define pminub_m2r(var,reg) mmx_m2r (pminub, var, reg)
+#define pminub_r2r(regs,regd) mmx_r2r (pminub, regs, regd)
+
+#define pmovmskb(mmreg,reg) \
+ __asm__ __volatile__ ("movmskps %" #mmreg ", %" #reg)
+
+#define pmulhuw_m2r(var,reg) mmx_m2r (pmulhuw, var, reg)
+#define pmulhuw_r2r(regs,regd) mmx_r2r (pmulhuw, regs, regd)
+
+#define prefetcht0(mem) mmx_fetch (mem, t0)
+#define prefetcht1(mem) mmx_fetch (mem, t1)
+#define prefetcht2(mem) mmx_fetch (mem, t2)
+#define prefetchnta(mem) mmx_fetch (mem, nta)
+
+#define psadbw_m2r(var,reg) mmx_m2r (psadbw, var, reg)
+#define psadbw_r2r(regs,regd) mmx_r2r (psadbw, regs, regd)
+
+#define pshufw_m2r(var,reg,imm) mmx_m2ri(pshufw, var, reg, imm)
+#define pshufw_r2r(regs,regd,imm) mmx_r2ri(pshufw, regs, regd, imm)
+
+#define sfence() __asm__ __volatile__ ("sfence\n\t")
+
+
+#endif
+
+
diff --git a/mpeglib/lib/util/render/Makefile.am b/mpeglib/lib/util/render/Makefile.am
new file mode 100644
index 00000000..24bc691f
--- /dev/null
+++ b/mpeglib/lib/util/render/Makefile.am
@@ -0,0 +1,59 @@
+# player - Makefile.am
+
+SUBDIRS = dither dither2YUV x11 sdl
+
+INCLUDES = $(all_includes)
+
+THIS_EXTRALIBS = dither/libdither.la \
+ dither2YUV/libdivxutil_dither.la \
+ x11/libutilx11.la
+
+
+noinst_LTLIBRARIES = libutilrender.la
+
+noinst_HEADERS = imageBase.h \
+ renderMachine.h surface.h
+
+
+kmpgincludedir = $(includedir)/$(THIS_LIB_NAME)/util/render
+
+kmpginclude_HEADERS = yuvPicture.h pictureArray.h
+
+
+libutilrender_la_SOURCES = yuvPicture.cpp imageBase.cpp \
+ renderMachine.cpp surface.cpp \
+ pictureArray.cpp
+
+libutilrender_la_LIBADD = $(THIS_EXTRALIBS)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/lib/util/render/dither/Makefile.am b/mpeglib/lib/util/render/dither/Makefile.am
new file mode 100644
index 00000000..166d5ca3
--- /dev/null
+++ b/mpeglib/lib/util/render/dither/Makefile.am
@@ -0,0 +1,40 @@
+# liboutplugin - Makefile.am
+
+INCLUDES = $(all_includes)
+
+EXTRA_DIST = ditherDef.h ditherMMX.h \
+ ditherer_mmx16.cpp dither32mmx.cpp
+
+noinst_LTLIBRARIES = libdither.la
+
+noinst_HEADERS = ditherWrapper.h \
+ dither8Bit.h colorTable8Bit.h \
+ colorTableHighBit.h dither16Bit.h \
+ dither32Bit.h ditherRGB_flipped.h \
+ ditherRGB.h
+
+libdither_la_SOURCES = ditherWrapper.cpp \
+ dither8Bit.cpp \
+ colorTable8Bit.cpp colorTableHighBit.cpp \
+ dither16Bit.cpp dither32Bit.cpp \
+ ditherRGB_flipped.cpp ditherRGB.cpp \
+ ditherer_mmx16.cpp dither32mmx.cpp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/lib/util/render/dither/colorTable8Bit.cpp b/mpeglib/lib/util/render/dither/colorTable8Bit.cpp
new file mode 100644
index 00000000..57c533de
--- /dev/null
+++ b/mpeglib/lib/util/render/dither/colorTable8Bit.cpp
@@ -0,0 +1,147 @@
+/*
+ colorTables for 8 Bit depth
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "colorTable8Bit.h"
+
+
+ColorTable8Bit::ColorTable8Bit() {
+
+ lum_values = new int[LUM_RANGE];
+ cr_values = new int[CR_RANGE];
+ cb_values = new int[CB_RANGE];
+
+
+ /* We can exploit cache by allocating contiguous blocks */
+
+ colortab = new TABTYPE[5*256];
+
+ Cr_r_tab = &colortab[0*256];
+ Cr_g_tab = &colortab[1*256];
+ Cb_g_tab = &colortab[2*256];
+ Cb_b_tab = &colortab[3*256];
+ L_tab = &colortab[4*256];
+
+ init8BitColor();
+}
+
+
+ColorTable8Bit::~ColorTable8Bit() {
+ delete lum_values;
+ delete cr_values;
+ delete cb_values;
+ delete colortab;
+}
+
+
+void ColorTable8Bit::init8BitColor() {
+ int i;
+
+
+
+ for (i=0; i<LUM_RANGE; i++) {
+ lum_values[i] = ((i * 256) / (LUM_RANGE)) + (256/(LUM_RANGE*2));
+ L_tab[i] = lum_values[i];
+ if (gammaCorrectFlag) {
+ L_tab[i] = GAMMA_CORRECTION(L_tab[i]);
+ }
+
+ }
+
+
+ for (i=0; i<CR_RANGE; i++) {
+ register double tmp;
+ if (chromaCorrectFlag) {
+ tmp = ((i * 256) / (CR_RANGE)) + (256/(CR_RANGE*2));
+ Cr_r_tab[i]=(TABTYPE) ((0.419/0.299)*CHROMA_CORRECTION128D(tmp-128.0));
+ Cr_g_tab[i]=(TABTYPE) (-(0.299/0.419)*CHROMA_CORRECTION128D(tmp-128.0));
+ cr_values[i] = CHROMA_CORRECTION256(tmp);
+ } else {
+ tmp = ((i * 256) / (CR_RANGE)) + (256/(CR_RANGE*2));
+ Cr_r_tab[i] = (TABTYPE) ((0.419/0.299) * (tmp - 128.0));
+ Cr_g_tab[i] = (TABTYPE) (-(0.299/0.419) * (tmp - 128.0));
+ cr_values[i] = (int) tmp;
+ }
+ }
+
+
+ for (i=0; i<CB_RANGE; i++) {
+ register double tmp;
+ if (chromaCorrectFlag) {
+ tmp = ((i * 256) / (CB_RANGE)) + (256/(CB_RANGE*2));
+ Cb_g_tab[i]=(TABTYPE) (-(0.114/0.331)*CHROMA_CORRECTION128D(tmp-128.0));
+ Cb_b_tab[i]=(TABTYPE) ((0.587/0.331)*CHROMA_CORRECTION128D(tmp-128.0));
+ cb_values[i] = CHROMA_CORRECTION256(tmp);
+ } else {
+ tmp = ((i * 256) / (CB_RANGE)) + (256/(CB_RANGE*2));
+ Cb_g_tab[i] = (TABTYPE) (-(0.114/0.331) * (tmp - 128.0));
+ Cb_b_tab[i] = (TABTYPE) ((0.587/0.331) * (tmp - 128.0));
+ cb_values[i] = (int) tmp;
+ }
+ }
+}
+
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ConvertColor --
+ *
+ * Given a l, cr, cb tuple, converts it to r,g,b.
+ *
+ * Results:
+ * r,g,b values returned in pointers passed as parameters.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+void ColorTable8Bit::ConvertColor(unsigned int l, unsigned int cr,
+ unsigned int cb, unsigned char* r,
+ unsigned char* g, unsigned char* b) {
+
+ double fl, fr, fg, fb;
+
+ /*
+ * Old method w/o lookup table
+ *
+ * fl = 1.164*(((double) l)-16.0);
+ * fcr = ((double) cr) - 128.0;
+ * fcb = ((double) cb) - 128.0;
+ *
+ * fr = fl + (1.366 * fcr);
+ * fg = fl - (0.700 * fcr) - (0.334 * fcb);
+ * fb = fl + (1.732 * fcb);
+ */
+
+ fl = L_tab[l];
+
+ fr = fl + Cr_r_tab[cr];
+ fg = fl + Cr_g_tab[cr] + Cb_g_tab[cb];
+ fb = fl + Cb_b_tab[cb];
+
+ if (fr < 0.0) fr = 0.0;
+ else if (fr > 255.0) fr = 255.0;
+
+ if (fg < 0.0) fg = 0.0;
+ else if (fg > 255.0) fg = 255.0;
+
+ if (fb < 0.0) fb = 0.0;
+ else if (fb > 255.0) fb = 255.0;
+
+ *r = (unsigned char) fr;
+ *g = (unsigned char) fg;
+ *b = (unsigned char) fb;
+
+}
diff --git a/mpeglib/lib/util/render/dither/colorTable8Bit.h b/mpeglib/lib/util/render/dither/colorTable8Bit.h
new file mode 100644
index 00000000..6d873d1d
--- /dev/null
+++ b/mpeglib/lib/util/render/dither/colorTable8Bit.h
@@ -0,0 +1,57 @@
+/*
+ colorTables for 8 Bit depth
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __COLORTABLE8BIT_H
+#define __COLORTABLE8BIT_H
+
+#include "ditherDef.h"
+
+
+class ColorTable8Bit {
+
+ // Arrays holding quantized value ranged for lum, cr, and cb.
+ // (used for 8 Bit)
+
+ int* lum_values;
+ int* cr_values;
+ int* cb_values;
+
+
+
+
+ TABTYPE *L_tab;
+ TABTYPE *Cr_r_tab;
+ TABTYPE *Cr_g_tab;
+ TABTYPE *Cb_g_tab;
+ TABTYPE *Cb_b_tab;
+ TABTYPE *colortab;
+
+
+ public:
+ ColorTable8Bit();
+ ~ColorTable8Bit();
+
+ inline int* getLumValues() { return lum_values; }
+ inline int* getCrValues() { return cr_values; }
+ inline int* getCbValues() { return cb_values; }
+
+ void ConvertColor(unsigned int l, unsigned int cr, unsigned int cb,
+ unsigned char* r, unsigned char* g, unsigned char* b);
+
+
+ private:
+ void init8BitColor();
+
+
+};
+#endif
diff --git a/mpeglib/lib/util/render/dither/colorTableHighBit.cpp b/mpeglib/lib/util/render/dither/colorTableHighBit.cpp
new file mode 100644
index 00000000..171f4e97
--- /dev/null
+++ b/mpeglib/lib/util/render/dither/colorTableHighBit.cpp
@@ -0,0 +1,248 @@
+/*
+ colorTables for 16,32 Bit depth
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "colorTableHighBit.h"
+
+//#define INTERPOLATE
+
+
+/*
+ * Erik Corry's multi-byte dither routines.
+ *
+ * The basic idea is that the Init generates all the necessary tables.
+ * The tables incorporate the information about the layout of pixels
+ * in the XImage, so that it should be able to cope with 15-bit, 16-bit
+ * 24-bit (non-packed) and 32-bit (10-11 bits per color!) screens.
+ * At present it cannot cope with 24-bit packed mode, since this involves
+ * getting down to byte level again. It is assumed that the bits for each
+ * color are contiguous in the longword.
+ *
+ * Writing to memory is done in shorts or ints. (Unfortunately, short is not
+ * very fast on Alpha, so there is room for improvement here). There is no
+ * dither time check for overflow - instead the tables have slack at
+ * each end. This is likely to be faster than an 'if' test as many modern
+ * architectures are really bad at ifs. Potentially, each '&&' causes a
+ * pipeline flush!
+ *
+ * There is no shifting and fixed point arithmetic, as I really doubt you
+ * can see the difference, and it costs. This may be just my bias, since I
+ * heard that Intel is really bad at shifting.
+ */
+
+
+/*
+ * How many 1 bits are there in the PIXVALword.
+ * Low performance, do not call often.
+ */
+static int number_of_bits_set(unsigned PIXVAL a) {
+ if(!a) return 0;
+ if(a & 1) return 1 + number_of_bits_set(a >> 1);
+ return(number_of_bits_set(a >> 1));
+}
+
+
+
+/*
+ * How many 0 bits are there at most significant end of PIXVALword.
+ * Low performance, do not call often.
+ */
+static int free_bits_at_top(unsigned PIXVAL a) {
+ /* assume char is 8 bits */
+ if(!a) return sizeof(unsigned PIXVAL) * 8;
+ /* assume twos complement */
+ if(((PIXVAL)a) < 0l) return 0;
+ return 1 + free_bits_at_top ( a << 1);
+}
+
+/*
+ * How many 0 bits are there at least significant end of PIXVALword.
+ * Low performance, do not call often.
+ */
+static int free_bits_at_bottom(unsigned PIXVAL a) {
+ /* assume char is 8 bits */
+ if(!a) return sizeof(unsigned PIXVAL) * 8;
+ if(((PIXVAL)a) & 1l) return 0;
+ return 1 + free_bits_at_bottom ( a >> 1);
+}
+
+
+
+ColorTableHighBit::ColorTableHighBit(int bpp,unsigned int redMask,
+ unsigned int greenMask,
+ unsigned int blueMask) {
+ this->bpp=bpp;
+ this->redMask=redMask;
+ this->greenMask=greenMask;
+ this->blueMask=blueMask;
+
+ colortab = new TABTYPE[5*256];
+
+ Cr_r_tab = &colortab[0*256];
+ Cr_g_tab = &colortab[1*256];
+ Cb_g_tab = &colortab[2*256];
+ Cb_b_tab = &colortab[3*256];
+ L_tab = &colortab[4*256];
+
+ rgb_2_pix = new PIXVAL [3*768];
+
+ r_2_pix_alloc = &rgb_2_pix[0*768];
+ g_2_pix_alloc = &rgb_2_pix[1*768];
+ b_2_pix_alloc = &rgb_2_pix[2*768];
+
+ initHighColor(bpp>=24,redMask,greenMask,blueMask);
+
+}
+
+
+ColorTableHighBit::~ColorTableHighBit() {
+ delete colortab;
+ delete rgb_2_pix;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * InitColor16Dither --
+ *
+ * To get rid of the multiply and other conversions in color
+ * dither, we use a lookup table.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The lookup tables are initialized.
+ *
+ *--------------------------------------------------------------
+ */
+
+void ColorTableHighBit::initHighColor(int thirty2,unsigned int redMask,
+ unsigned int greenMask,
+ unsigned int blueMask) {
+
+ unsigned PIXVAL red_mask = redMask;
+ unsigned PIXVAL green_mask =greenMask;
+ unsigned PIXVAL blue_mask = blueMask;
+
+ int CR, CB, i;
+
+
+ for (i=0; i<256; i++) {
+ L_tab[i] = i;
+ if (gammaCorrectFlag) {
+ L_tab[i] = (TABTYPE)GAMMA_CORRECTION(i);
+ }
+
+ CB = CR = i;
+
+ if (chromaCorrectFlag) {
+ CB -= 128;
+ CB = CHROMA_CORRECTION128(CB);
+ CR -= 128;
+ CR = CHROMA_CORRECTION128(CR);
+ } else {
+ CB -= 128; CR -= 128;
+ }
+/* was
+ Cr_r_tab[i] = 1.596 * CR;
+ Cr_g_tab[i] = -0.813 * CR;
+ Cb_g_tab[i] = -0.391 * CB;
+ Cb_b_tab[i] = 2.018 * CB;
+ but they were just messed up.
+ Then was (_Video Deymstified_):
+ Cr_r_tab[i] = 1.366 * CR;
+ Cr_g_tab[i] = -0.700 * CR;
+ Cb_g_tab[i] = -0.334 * CB;
+ Cb_b_tab[i] = 1.732 * CB;
+ but really should be:
+ (from ITU-R BT.470-2 System B, G and SMPTE 170M )
+*/
+ Cr_r_tab[i] = (TABTYPE) ( (0.419/0.299) * CR );
+ Cr_g_tab[i] = (TABTYPE) ( -(0.299/0.419) * CR );
+ Cb_g_tab[i] = (TABTYPE) ( -(0.114/0.331) * CB );
+ Cb_b_tab[i] = (TABTYPE) ( (0.587/0.331) * CB );
+
+/*
+ though you could argue for:
+ SMPTE 240M
+ Cr_r_tab[i] = (0.445/0.212) * CR;
+ Cr_g_tab[i] = -(0.212/0.445) * CR;
+ Cb_g_tab[i] = -(0.087/0.384) * CB;
+ Cb_b_tab[i] = (0.701/0.384) * CB;
+ FCC
+ Cr_r_tab[i] = (0.421/0.30) * CR;
+ Cr_g_tab[i] = -(0.30/0.421) * CR;
+ Cb_g_tab[i] = -(0.11/0.331) * CB;
+ Cb_b_tab[i] = (0.59/0.331) * CB;
+ ITU-R BT.709
+ Cr_r_tab[i] = (0.454/0.2125) * CR;
+ Cr_g_tab[i] = -(0.2125/0.454) * CR;
+ Cb_g_tab[i] = -(0.0721/0.386) * CB;
+ Cb_b_tab[i] = (0.7154/0.386) * CB;
+*/
+ }
+
+ /*
+ * Set up entries 0-255 in rgb-to-pixel value tables.
+ */
+ for (i = 0; i < 256; i++) {
+ r_2_pix_alloc[i + 256] = i >> (8 - number_of_bits_set(red_mask));
+ r_2_pix_alloc[i + 256] <<= free_bits_at_bottom(red_mask);
+ g_2_pix_alloc[i + 256] = i >> (8 - number_of_bits_set(green_mask));
+ g_2_pix_alloc[i + 256] <<= free_bits_at_bottom(green_mask);
+ b_2_pix_alloc[i + 256] = i >> (8 - number_of_bits_set(blue_mask));
+ b_2_pix_alloc[i + 256] <<= free_bits_at_bottom(blue_mask);
+ /*
+ * If we have 16-bit output depth, then we double the value
+ * in the top word. This means that we can write out both
+ * pixels in the pixel doubling mode with one op. It is
+ * harmless in the normal case as storing a 32-bit value
+ * through a short pointer will lose the top bits anyway.
+ * A similar optimisation for Alpha for 64 bit has been
+ * prepared for, but is not yet implemented.
+ */
+ if(!thirty2) {
+ r_2_pix_alloc[i + 256] |= (r_2_pix_alloc[i + 256]) << 16;
+ g_2_pix_alloc[i + 256] |= (g_2_pix_alloc[i + 256]) << 16;
+ b_2_pix_alloc[i + 256] |= (b_2_pix_alloc[i + 256]) << 16;
+
+ }
+#ifdef SIXTYFOUR_BIT
+ if(thirty2) {
+
+ r_2_pix_alloc[i + 256] |= (r_2_pix_alloc[i + 256]) << 32;
+ g_2_pix_alloc[i + 256] |= (g_2_pix_alloc[i + 256]) << 32;
+ b_2_pix_alloc[i + 256] |= (b_2_pix_alloc[i + 256]) << 32;
+
+ }
+#endif
+ }
+
+ /*
+ * Spread out the values we have to the rest of the array so that
+ * we do not need to check for overflow.
+ */
+ for (i = 0; i < 256; i++) {
+ r_2_pix_alloc[i] = r_2_pix_alloc[256];
+ r_2_pix_alloc[i+ 512] = r_2_pix_alloc[511];
+ g_2_pix_alloc[i] = g_2_pix_alloc[256];
+ g_2_pix_alloc[i+ 512] = g_2_pix_alloc[511];
+ b_2_pix_alloc[i] = b_2_pix_alloc[256];
+ b_2_pix_alloc[i+ 512] = b_2_pix_alloc[511];
+ }
+
+ r_2_pix = r_2_pix_alloc + 256;
+ g_2_pix = g_2_pix_alloc + 256;
+ b_2_pix = b_2_pix_alloc + 256;
+}
diff --git a/mpeglib/lib/util/render/dither/colorTableHighBit.h b/mpeglib/lib/util/render/dither/colorTableHighBit.h
new file mode 100644
index 00000000..9945414d
--- /dev/null
+++ b/mpeglib/lib/util/render/dither/colorTableHighBit.h
@@ -0,0 +1,73 @@
+/*
+ colorTables for 16,32 Bit depth
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __COLORTABLEHIGHBIT_H
+#define __COLORTABLEHIGHBIT_H
+
+#include "ditherDef.h"
+
+
+
+
+class ColorTableHighBit {
+
+ TABTYPE *L_tab;
+ TABTYPE *Cr_r_tab;
+ TABTYPE *Cr_g_tab;
+ TABTYPE *Cb_g_tab;
+ TABTYPE *Cb_b_tab;
+ TABTYPE *colortab;
+
+
+ PIXVAL *r_2_pix;
+ PIXVAL *g_2_pix;
+ PIXVAL *b_2_pix;
+ PIXVAL *rgb_2_pix;
+
+ PIXVAL *r_2_pix_alloc;
+ PIXVAL *g_2_pix_alloc;
+ PIXVAL *b_2_pix_alloc;
+
+
+
+ // init stuff
+ int bpp;
+ // colorMask
+ unsigned int redMask;
+ unsigned int greenMask;
+ unsigned int blueMask;
+
+ public:
+ ColorTableHighBit(int bpp,unsigned int redMask,
+ unsigned int greenMask,unsigned int blueMask);
+ ~ColorTableHighBit();
+
+ inline TABTYPE* getL_tab() { return L_tab ; }
+ inline TABTYPE* getCr_r_tab() { return Cr_r_tab ; }
+ inline TABTYPE* getCr_g_tab() { return Cr_g_tab ; }
+ inline TABTYPE* getCb_g_tab() { return Cb_g_tab ; }
+ inline TABTYPE* getCb_b_tab() { return Cb_b_tab ; }
+
+
+ inline PIXVAL* getr_2_pix() { return r_2_pix ; }
+ inline PIXVAL* getg_2_pix() { return g_2_pix ; }
+ inline PIXVAL* getb_2_pix() { return b_2_pix ; }
+
+
+
+ private:
+ void initHighColor(int thirty2,unsigned int redMask,
+ unsigned int greenMask,unsigned int blueMask);
+
+};
+#endif
diff --git a/mpeglib/lib/util/render/dither/dither16Bit.cpp b/mpeglib/lib/util/render/dither/dither16Bit.cpp
new file mode 100644
index 00000000..0a843ee9
--- /dev/null
+++ b/mpeglib/lib/util/render/dither/dither16Bit.cpp
@@ -0,0 +1,300 @@
+/*
+ dither 16 bit depth yuv images
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "dither16Bit.h"
+
+
+Dither16Bit::Dither16Bit(unsigned int redMask,
+ unsigned int greenMask,unsigned int blueMask) {
+
+
+ colorTableHighBit=new ColorTableHighBit(16,redMask,greenMask,blueMask);
+ L_tab=colorTableHighBit->getL_tab();
+ Cr_r_tab=colorTableHighBit->getCr_r_tab();
+ Cr_g_tab=colorTableHighBit->getCr_g_tab();
+ Cb_g_tab=colorTableHighBit->getCb_g_tab();
+ Cb_b_tab=colorTableHighBit->getCb_b_tab();
+
+ r_2_pix=colorTableHighBit->getr_2_pix();
+ g_2_pix=colorTableHighBit->getg_2_pix();
+ b_2_pix=colorTableHighBit->getb_2_pix();
+
+}
+
+
+Dither16Bit::~Dither16Bit() {
+ delete colorTableHighBit;
+}
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Color16DitherImage --
+ *
+ * Converts image into 16 bit color.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+void Dither16Bit::ditherImageColor16(unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,
+ unsigned char* out,
+ int rows,
+ int cols,
+ int offset) {
+
+ int L, CR, CB;
+ unsigned short *row1, *row2;
+ unsigned char *lum2;
+ int x, y;
+ int cr_r;
+ int cr_g;
+ int cb_g;
+ int cb_b;
+ int cols_2 = cols/2;
+
+ row1 = (unsigned short *)out;
+ row2=row1+cols_2+cols_2+offset; // start of second row
+
+ offset=2*offset+cols_2+cols_2;
+
+ lum2 = lum + cols_2 + cols_2;
+
+
+ for (y=0; y<rows; y+=2) {
+ for (x=0; x<cols_2; x++) {
+ int R, G, B;
+
+ CR = *cr++;
+ CB = *cb++;
+ cr_r = Cr_r_tab[CR];
+ cr_g = Cr_g_tab[CR];
+ cb_g = Cb_g_tab[CB];
+ cb_b = Cb_b_tab[CB];
+
+ L = L_tab[(int) *lum++];
+
+ R = L + cr_r;
+ G = L + cr_g + cb_g;
+ B = L + cb_b;
+
+ *row1++ = (r_2_pix[R] | g_2_pix[G] | b_2_pix[B]);
+
+
+#ifdef INTERPOLATE
+ if(x != cols_2 - 1) {
+ CR = (CR + *cr) >> 1;
+ CB = (CB + *cb) >> 1;
+ cr_r = Cr_r_tab[CR];
+ cr_g = Cr_g_tab[CR];
+ cb_g = Cb_g_tab[CB];
+ cb_b = Cb_b_tab[CB];
+ }
+#endif
+
+ L = L_tab[(int) *lum++];
+
+ R = L + cr_r;
+ G = L + cr_g + cb_g;
+ B = L + cb_b;
+
+ *row1++ = (r_2_pix[R] | g_2_pix[G] | b_2_pix[B]);
+
+ /*
+ * Now, do second row.
+ */
+#ifdef INTERPOLATE
+ if(y != rows - 2) {
+ CR = (CR + *(cr + cols_2 - 1)) >> 1;
+ CB = (CB + *(cb + cols_2 - 1)) >> 1;
+ cr_r = Cr_r_tab[CR];
+ cr_g = Cr_g_tab[CR];
+ cb_g = Cb_g_tab[CB];
+ cb_b = Cb_b_tab[CB];
+ }
+#endif
+
+ L = L_tab[(int) *lum2++];
+ R = L + cr_r;
+ G = L + cr_g + cb_g;
+ B = L + cb_b;
+
+ *row2++ = (r_2_pix[R] | g_2_pix[G] | b_2_pix[B]);
+
+ L = L_tab[(int) *lum2++];
+ R = L + cr_r;
+ G = L + cr_g + cb_g;
+ B = L + cb_b;
+
+ *row2++ = (r_2_pix[R] | g_2_pix[G] | b_2_pix[B]);
+ }
+ /*
+ * These values are at the start of the next line, (due
+ * to the ++'s above),but they need to be at the start
+ * of the line after that.
+ */
+ lum += cols_2 + cols_2;
+ lum2 += cols_2 + cols_2;
+ row1 += offset;
+ row2 += offset;
+ }
+}
+
+
+/*
+ * Erik Corry's pixel doubling routines for 15/16/24/32 bit screens.
+ */
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Twox2Color16DitherImage --
+ *
+ * Converts image into 16 bit color at double size.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+/*
+ * In this function I make use of a nasty trick. The tables have the lower
+ * 16 bits replicated in the upper 16. This means I can write ints and get
+ * the horisontal doubling for free (almost).
+ */
+
+void Dither16Bit::ditherImageTwox2Color16(unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,
+ unsigned char* out,
+ int rows,
+ int cols,
+ int mod) {
+ int L, CR, CB;
+ unsigned int *row1 = (unsigned int *)out;
+ unsigned int *row2 = row1 + cols + mod/2;
+ unsigned int *row3 = row2 + cols + mod/2;
+ unsigned int *row4 = row3 + cols + mod/2;
+ unsigned char *lum2;
+ int x, y;
+ int cr_r;
+ int cr_g;
+ int cb_g;
+ int cb_b;
+ int cols_2 = cols/2;
+
+ lum2 = lum + cols_2 + cols_2;
+ for (y=0; y<rows; y+=2) {
+ for (x=0; x<cols_2; x++) {
+ int R, G, B;
+ int t;
+
+ CR = *cr++;
+ CB = *cb++;
+ cr_r = Cr_r_tab[CR];
+ cr_g = Cr_g_tab[CR];
+ cb_g = Cb_g_tab[CB];
+ cb_b = Cb_b_tab[CB];
+
+ L = L_tab[(int) *lum++];
+
+ R = L + cr_r;
+ G = L + cr_g + cb_g;
+ B = L + cb_b;
+
+ t = (r_2_pix[R] | g_2_pix[G] | b_2_pix[B]);
+ row1[0] = t;
+ row1++;
+ row2[0] = t;
+ row2++;
+
+ // INTERPOLATE
+ if(x != cols_2 - 1) {
+ CR = (CR + *cr) >> 1;
+ CB = (CB + *cb) >> 1;
+ cr_r = Cr_r_tab[CR];
+ cr_g = Cr_g_tab[CR];
+ cb_g = Cb_g_tab[CB];
+ cb_b = Cb_b_tab[CB];
+ }
+ // end
+
+ L = L_tab[(int) *lum++];
+
+ R = L + cr_r;
+ G = L + cr_g + cb_g;
+ B = L + cb_b;
+
+ t = (r_2_pix[R] | g_2_pix[G] | b_2_pix[B]);
+ row1[0] = t;
+ row1++;
+ row2[0] = t;
+ row2++;
+
+ /*
+ * Now, do second row.
+ */
+ // INTERPOLATE
+ if(y != rows - 2) {
+ CR = (CR + *(cr + cols_2 - 1)) >> 1;
+ CB = (CB + *(cb + cols_2 - 1)) >> 1;
+ cr_r = Cr_r_tab[CR];
+ cr_g = Cr_g_tab[CR];
+ cb_g = Cb_g_tab[CB];
+ cb_b = Cb_b_tab[CB];
+ }
+ // end
+
+ L = L_tab[(int) *lum2++];
+ R = L + cr_r;
+ G = L + cr_g + cb_g;
+ B = L + cb_b;
+
+ t = (r_2_pix[R] | g_2_pix[G] | b_2_pix[B]);
+ row3[0] = t;
+ row3++;
+ row4[0] = t;
+ row4++;
+
+ L = L_tab[(int) *lum2++];
+ R = L + cr_r;
+ G = L + cr_g + cb_g;
+ B = L + cb_b;
+
+ t = (r_2_pix[R] | g_2_pix[G] | b_2_pix[B]);
+ row3[0] = t;
+ row3++;
+ row4[0] = t;
+ row4++;
+ }
+ lum += cols_2 + cols_2;
+ lum2 += cols_2 + cols_2;
+ row1 += 6 * cols_2 + 2*mod;
+ row3 += 6 * cols_2 + 2*mod;
+ row2 += 6 * cols_2 + 2*mod;
+ row4 += 6 * cols_2 + 2*mod;
+ }
+}
diff --git a/mpeglib/lib/util/render/dither/dither16Bit.h b/mpeglib/lib/util/render/dither/dither16Bit.h
new file mode 100644
index 00000000..2e47c01c
--- /dev/null
+++ b/mpeglib/lib/util/render/dither/dither16Bit.h
@@ -0,0 +1,55 @@
+/*
+ dither 16 bit depth yuv images
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#ifndef __DITHER16Bit_H
+#define __DITHER16Bit_H
+
+#include "colorTableHighBit.h"
+
+class Dither16Bit {
+
+ ColorTableHighBit* colorTableHighBit;
+
+ TABTYPE *L_tab;
+ TABTYPE *Cr_r_tab;
+ TABTYPE *Cr_g_tab;
+ TABTYPE *Cb_g_tab;
+ TABTYPE *Cb_b_tab;
+
+ PIXVAL *r_2_pix;
+ PIXVAL *g_2_pix;
+ PIXVAL *b_2_pix;
+
+ public:
+ Dither16Bit(unsigned int redMask,
+ unsigned int greenMask,unsigned int blueMask);
+ ~Dither16Bit();
+
+ void ditherImageColor16(unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,
+ unsigned char* out,
+ int rows,
+ int cols,
+ int offset);
+
+ void ditherImageTwox2Color16(unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,
+ unsigned char* out,
+ int rows,
+ int cols,
+ int mod);
+
+};
+
+#endif
diff --git a/mpeglib/lib/util/render/dither/dither32Bit.cpp b/mpeglib/lib/util/render/dither/dither32Bit.cpp
new file mode 100644
index 00000000..61a1d2dc
--- /dev/null
+++ b/mpeglib/lib/util/render/dither/dither32Bit.cpp
@@ -0,0 +1,253 @@
+/*
+ dither 32 bit depth yuv images
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "dither32Bit.h"
+
+
+#define doRow(row,Lum) *row++=(local_r_2_pix[Lum] | \
+ local_g_2_pix[Lum] | local_b_2_pix[Lum])
+
+
+Dither32Bit::Dither32Bit(unsigned int redMask,
+ unsigned int greenMask,unsigned int blueMask) {
+
+
+ colorTableHighBit=new ColorTableHighBit(32,redMask,greenMask,blueMask);
+ L_tab=colorTableHighBit->getL_tab();
+ Cr_r_tab=colorTableHighBit->getCr_r_tab();
+ Cr_g_tab=colorTableHighBit->getCr_g_tab();
+ Cb_g_tab=colorTableHighBit->getCb_g_tab();
+ Cb_b_tab=colorTableHighBit->getCb_b_tab();
+
+ r_2_pix=colorTableHighBit->getr_2_pix();
+ g_2_pix=colorTableHighBit->getg_2_pix();
+ b_2_pix=colorTableHighBit->getb_2_pix();
+
+}
+
+
+Dither32Bit::~Dither32Bit() {
+ delete colorTableHighBit;
+}
+
+
+void Dither32Bit::ditherImageColor32(unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,
+ unsigned char* out,
+ int rows,
+ int cols,
+ int mod) {
+
+ int L;
+ int n;
+ int rowWork;
+ int colWork;
+
+ unsigned int *row1, *row2;
+ unsigned char *lum2;
+ PIXVAL* local_r_2_pix;
+ PIXVAL* local_g_2_pix;
+ PIXVAL* local_b_2_pix;
+
+ row1 = (unsigned int *)out;
+
+ row2 = row1+cols+mod;
+ lum2 = lum+cols;
+
+ // because the width/height are a multiply of a macroblocksize
+ // cols/rows always are even
+ colWork=cols>>1;
+ rowWork=rows>>1;
+ mod=cols+2*mod;
+
+ while(rowWork--) {
+ n=colWork;
+ while(n--) {
+
+ local_r_2_pix=r_2_pix+Cr_r_tab[*cr];
+ local_g_2_pix=g_2_pix+Cr_g_tab[*cr++] + Cb_g_tab[*cb];
+ local_b_2_pix=b_2_pix+Cb_b_tab[*cb++];
+
+ L = L_tab[*lum++];
+ doRow(row1,L);
+
+ L = L_tab[*lum++];
+ doRow(row1,L);
+
+ L = L_tab [*lum2++];
+ doRow(row2,L);
+
+ L = L_tab [*lum2++];
+ doRow(row2,L);
+
+
+ }
+ row2 += mod;
+ lum += cols;
+ lum2 += cols;
+ row1 += mod;
+
+ }
+
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Twox2Color32 --
+ *
+ * Converts image into 24/32 bit color.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+void Dither32Bit::ditherImageTwox2Color32(unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,
+ unsigned char* out,
+ int rows,
+ int cols,
+ int mod) {
+ int L, CR, CB;
+ unsigned PIXVAL *row1 = (unsigned PIXVAL *)out;
+ unsigned PIXVAL *row2 = row1 + cols * ONE_TWO + mod;
+ unsigned PIXVAL *row3 = row2 + cols * ONE_TWO + mod;
+ unsigned PIXVAL *row4 = row3 + cols * ONE_TWO + mod;
+ unsigned char *lum2;
+ int x, y;
+ int cr_r;
+ int cr_g;
+ int cb_g;
+ int cb_b;
+ int cols_2 = cols/2;
+ int loffset = ONE_TWO * 6 *cols_2 + 4*mod ;
+
+ lum2 = lum + cols_2 + cols_2;
+ for (y=0; y<rows; y+=2) {
+ for (x=0; x<cols_2; x++) {
+ int R, G, B;
+ PIXVAL t;
+
+ CR = *cr++;
+ CB = *cb++;
+ cr_r = Cr_r_tab[CR];
+ cr_g = Cr_g_tab[CR];
+ cb_g = Cb_g_tab[CB];
+ cb_b = Cb_b_tab[CB];
+
+ L = L_tab[ (int) *lum++];
+
+ R = L + cr_r;
+ G = L + cr_g + cb_g;
+ B = L + cb_b;
+
+ t = (r_2_pix[R] | g_2_pix[G] | b_2_pix[B]);
+ row1[0] = t;
+ row2[0] = t;
+#ifndef SIXTYFOUR_BIT
+ row1[1] = t;
+ row2[1] = t;
+#endif
+ row1 += ONE_TWO;
+ row2 += ONE_TWO;
+
+ /* INTERPOLATE is now standard */
+ // INTERPOLATE
+ if(x != cols_2 - 1) {
+ CR = (CR + *cr) >> 1;
+ CB = (CB + *cb) >> 1;
+ cr_r = Cr_r_tab[CR];
+ cr_g = Cr_g_tab[CR];
+ cb_g = Cb_g_tab[CB];
+ cb_b = Cb_b_tab[CB];
+ }
+ // end
+ /* end INTERPOLATE */
+
+ L = L_tab[ (int) *lum++];
+
+ R = L + cr_r;
+ G = L + cr_g + cb_g;
+ B = L + cb_b;
+
+ t = (r_2_pix[R] | g_2_pix[G] | b_2_pix[B]);
+ row1[0] = t;
+ row2[0] = t;
+#ifndef SIXTYFOUR_BIT
+ row1[1] = t;
+ row2[1] = t;
+#endif
+ row1 += ONE_TWO;
+ row2 += ONE_TWO;
+
+ /*
+ * Now, do second row.
+ */
+ /* INTERPOLATE is now standard */
+ // INTERPOLATE
+ if(y != rows - 2) {
+ CR = (unsigned int) (CR + *(cr + cols_2 - 1)) >> 1;
+ CB = (unsigned int) (CB + *(cb + cols_2 - 1)) >> 1;
+ cr_r = Cr_r_tab[CR];
+ cr_g = Cr_g_tab[CR];
+ cb_g = Cb_g_tab[CB];
+ cb_b = Cb_b_tab[CB];
+ }
+ // end
+ /* endif */
+ L = L_tab[ (int) *lum2++];
+ R = L + cr_r;
+ G = L + cr_g + cb_g;
+ B = L + cb_b;
+
+ t = (r_2_pix[R] | g_2_pix[G] | b_2_pix[B]);
+ row3[0] = t;
+ row4[0] = t;
+#ifndef SIXTYFOUR_BIT
+ row3[1] = t;
+ row4[1] = t;
+#endif
+ row3 += ONE_TWO;
+ row4 += ONE_TWO;
+
+ L = L_tab[(int) *lum2++];
+ R = L + cr_r;
+ G = L + cr_g + cb_g;
+ B = L + cb_b;
+
+ t = (r_2_pix[R] | g_2_pix[G] | b_2_pix[B]);
+ row3[0] = t;
+ row4[0] = t;
+#ifndef SIXTYFOUR_BIT
+ row3[1] = t;
+ row4[1] = t;
+#endif
+ row3 += ONE_TWO;
+ row4 += ONE_TWO;
+ }
+ lum += cols_2 + cols_2;
+ lum2 += cols_2 + cols_2;
+
+ row1 += loffset;
+ row3 += loffset;
+ row2 += loffset;
+ row4 += loffset;
+ }
+}
diff --git a/mpeglib/lib/util/render/dither/dither32Bit.h b/mpeglib/lib/util/render/dither/dither32Bit.h
new file mode 100644
index 00000000..440d021a
--- /dev/null
+++ b/mpeglib/lib/util/render/dither/dither32Bit.h
@@ -0,0 +1,55 @@
+/*
+ dither 32 bit depth yuv images
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#ifndef __DITHER32Bit_H
+#define __DITHER32Bit_H
+
+#include "colorTableHighBit.h"
+
+class Dither32Bit {
+
+ ColorTableHighBit* colorTableHighBit;
+
+ TABTYPE *L_tab;
+ TABTYPE *Cr_r_tab;
+ TABTYPE *Cr_g_tab;
+ TABTYPE *Cb_g_tab;
+ TABTYPE *Cb_b_tab;
+
+ PIXVAL *r_2_pix;
+ PIXVAL *g_2_pix;
+ PIXVAL *b_2_pix;
+
+ public:
+ Dither32Bit(unsigned int redMask,
+ unsigned int greenMask,unsigned int blueMask);
+ ~Dither32Bit();
+
+ void ditherImageColor32(unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,
+ unsigned char* out,
+ int rows,
+ int cols,
+ int offset);
+
+ void ditherImageTwox2Color32(unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,
+ unsigned char* out,
+ int rows,
+ int cols,
+ int mod);
+
+};
+
+#endif
diff --git a/mpeglib/lib/util/render/dither/dither32mmx.cpp b/mpeglib/lib/util/render/dither/dither32mmx.cpp
new file mode 100644
index 00000000..b5fa4807
--- /dev/null
+++ b/mpeglib/lib/util/render/dither/dither32mmx.cpp
@@ -0,0 +1,272 @@
+/*
+ MMX ditherer for 32 bit displays
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "ditherMMX.h"
+
+#include <iostream>
+
+using namespace std;
+
+
+#ifndef INTEL
+ void dither32_mmx(unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,
+ unsigned char* out,
+ int rows,
+ int cols,
+ int mod) {
+ printf("urgs! dither32_mmx \n");
+ printf("never should happen!\n");
+ exit(0);
+}
+
+#else
+
+
+static unsigned long MMX32_80w[] = {0x00800080, 0x00800080};
+static unsigned long MMX32_10w[] = {0x00100010, 0x00100010};
+static unsigned long MMX32_00FFw[] = {0x00ff00ff, 0x00ff00ff};
+static unsigned long MMX32_FF00w[] = {0xff00ff00, 0xff00ff00};
+static unsigned short MMX32_Ycoeff[] = {0x4a, 0x4a, 0x4a, 0x4a};
+static unsigned short MMX32_Vredcoeff[] = {0x59, 0x59, 0x59, 0x59};
+static unsigned short MMX32_Ubluecoeff[] = {0x72, 0x72, 0x72, 0x72};
+static unsigned short MMX32_Ugrncoeff[] = {0xffea,0xffea,0xffea,0xffea};
+static unsigned short MMX32_Vgrncoeff[] = {0xffd2,0xffd2,0xffd2,0xffd2};
+
+void dummy_dithermmx32() {
+ cout << "MMX32_10w:"<<MMX32_10w<<endl;
+ cout << "MMX32_80w:"<<MMX32_80w<<endl;
+ cout << "MMX32_Ubluecoeff:"<<MMX32_Ubluecoeff<<endl;
+ cout << "MMX32_Vredcoeff:"<<MMX32_Vredcoeff<<endl;
+ cout << "MMX32_Ugrncoeff:"<<MMX32_Ugrncoeff<<endl;
+ cout << "MMX32_Vgrncoeff:"<<MMX32_Vgrncoeff<<endl;
+ cout << "MMX32_Ycoeff:"<<MMX32_Ycoeff<<endl;
+ cout << "MMX32_00FFw:"<<MMX32_00FFw<<endl;
+ cout << "MMX32_FF00w:"<<MMX32_FF00w<<endl;
+}
+
+
+/**
+ This MMX assembler is my first assembler/MMX program ever.
+ Thus it maybe buggy.
+ Send patches to:
+ mvogt@rhrk.uni-kl.de
+
+ After it worked fine I have "obfuscated" the code a bit to have
+ more parallism in the MMX units. This means I moved
+ initilisation around and delayed other instruction.
+ Performance measurement did not show that this brought any advantage
+ but in theory it _should_ be faster this way.
+
+ The overall performanve gain to the C based dither was 30%-40%.
+ The MMX routine calculates 256bit=8RGB values in each cycle
+ (4 for row1 & 4 for row2)
+
+ The red/green/blue.. coefficents are taken from the mpeg_play
+ player. They look nice, but I dont know if you can have
+ better values, to avoid integer rounding errors.
+
+
+ IMPORTANT:
+ ==========
+
+ It is a requirement that the cr/cb/lum are 8 byte aligned and
+ the out are 16byte aligned or you will/may get segfaults
+
+*/
+
+void dither32_mmx(unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,
+ unsigned char* out,
+ int rows,
+ int cols,
+ int mod) {
+
+
+
+ unsigned int *row1;
+ unsigned int *row2;
+ row1 = (unsigned int *)out; // 32 bit target
+
+ unsigned char* end = lum +cols*rows; // Pointer to the end
+ int x=cols;
+ row2=row1+cols+mod; // start of second row
+ mod=4*cols+8*mod; // increment for row1 in byte
+
+ // buffer for asm function
+ int buf[6];
+ buf[0]=(int)(lum+cols); // lum2 pointer
+ buf[1]=(int)end;
+ buf[2]=x;
+ buf[3]=mod;
+ buf[4]=0; //tmp0;
+ buf[5]=cols;
+
+
+ __asm__ __volatile__ (
+ ".align 32\n"
+ "1:\n"
+
+ // create Cr (result in mm1)
+ "movd (%0), %%mm1\n" // 0 0 0 0 v3 v2 v1 v0
+ "pxor %%mm7,%%mm7\n" // 00 00 00 00 00 00 00 00
+ "movd (%2), %%mm2\n" // 0 0 0 0 l3 l2 l1 l0
+ "punpcklbw %%mm7,%%mm1\n" // 0 v3 0 v2 00 v1 00 v0
+ "punpckldq %%mm1,%%mm1\n" // 00 v1 00 v0 00 v1 00 v0
+ "psubw MMX32_80w,%%mm1\n" // mm1-128:r1 r1 r0 r0 r1 r1 r0 r0
+
+ // create Cr_g (result in mm0)
+ "movq %%mm1,%%mm0\n" // r1 r1 r0 r0 r1 r1 r0 r0
+ "pmullw MMX32_Vgrncoeff,%%mm0\n" // red*-46dec=0.7136*64
+ "pmullw MMX32_Vredcoeff,%%mm1\n" // red*89dec=1.4013*64
+ "psraw $6, %%mm0\n" // red=red/64
+ "psraw $6, %%mm1\n" // red=red/64
+
+
+ // create L1 L2 (result in mm2,mm4)
+ // L2=lum2
+ "movl %2,16%5\n" // store register in tmp0
+ "movl %5,%2\n" // lum2->register
+ "movd (%2),%%mm3\n" // 0 0 0 0 L3 L2 L1 L0
+ "movl 16%5,%2\n" // tmp0->register
+ "punpckldq %%mm3,%%mm2\n" // L3 L2 L1 L0 l3 l2 l1 l0
+ "movq %%mm2,%%mm4\n" // L3 L2 L1 L0 l3 l2 l1 l0
+ "pand MMX32_FF00w, %%mm2\n" // L3 0 L1 0 l3 0 l1 0
+ "pand MMX32_00FFw, %%mm4\n" // 0 L2 0 L0 0 l2 0 l0
+ "psrlw $8,%%mm2\n" // 0 L3 0 L1 0 l3 0 l1
+
+
+
+ // create R (result in mm6)
+ "movq %%mm2,%%mm5\n" // 0 L3 0 L1 0 l3 0 l1
+ "movq %%mm4,%%mm6\n" // 0 L2 0 L0 0 l2 0 l0
+ "paddsw %%mm1, %%mm5\n" // lum1+red:x R3 x R1 x r3 x r1
+ "paddsw %%mm1, %%mm6\n" // lum1+red:x R2 x R0 x r2 x r0
+ "packuswb %%mm5,%%mm5\n" // R3 R1 r3 r1 R3 R1 r3 r1
+ "packuswb %%mm6,%%mm6\n" // R2 R0 r2 r0 R2 R0 r2 r0
+ "pxor %%mm7,%%mm7\n" // 00 00 00 00 00 00 00 00
+ "punpcklbw %%mm5,%%mm6\n" // R3 R2 R1 R0 r3 r2 r1 r0
+
+
+ // create Cb (result in mm1)
+ "movd (%1), %%mm1\n" // 0 0 0 0 u3 u2 u1 u0
+ "punpcklbw %%mm7,%%mm1\n" // 0 u3 0 u2 00 u1 00 u0
+ "punpckldq %%mm1,%%mm1\n" // 00 u1 00 u0 00 u1 00 u0
+ "psubw MMX32_80w,%%mm1\n" // mm1-128:u1 u1 u0 u0 u1 u1 u0 u0
+ // create Cb_g (result in mm5)
+ "movq %%mm1,%%mm5\n" // u1 u1 u0 u0 u1 u1 u0 u0
+ "pmullw MMX32_Ugrncoeff,%%mm5\n" // blue*-109dec=1.7129*64
+ "pmullw MMX32_Ubluecoeff,%%mm1\n" // blue*114dec=1.78125*64
+ "psraw $6, %%mm5\n" // blue=red/64
+ "psraw $6, %%mm1\n" // blue=blue/64
+
+
+ // create G (result in mm7)
+ "movq %%mm2,%%mm3\n" // 0 L3 0 L1 0 l3 0 l1
+ "movq %%mm4,%%mm7\n" // 0 L2 0 L0 0 l2 0 l1
+ "paddsw %%mm5, %%mm3\n" // lum1+Cb_g:x G3t x G1t x g3t x g1t
+ "paddsw %%mm5, %%mm7\n" // lum1+Cb_g:x G2t x G0t x g2t x g0t
+ "paddsw %%mm0, %%mm3\n" // lum1+Cr_g:x G3 x G1 x g3 x g1
+ "paddsw %%mm0, %%mm7\n" // lum1+blue:x G2 x G0 x g2 x g0
+ "packuswb %%mm3,%%mm3\n" // G3 G1 g3 g1 G3 G1 g3 g1
+ "packuswb %%mm7,%%mm7\n" // G2 G0 g2 g0 G2 G0 g2 g0
+ "punpcklbw %%mm3,%%mm7\n" // G3 G2 G1 G0 g3 g2 g1 g0
+
+
+ // create B (result in mm5)
+ "movq %%mm2,%%mm3\n" // 0 L3 0 L1 0 l3 0 l1
+ "movq %%mm4,%%mm5\n" // 0 L2 0 L0 0 l2 0 l1
+ "paddsw %%mm1, %%mm3\n" // lum1+blue:x B3 x B1 x b3 x b1
+ "paddsw %%mm1, %%mm5\n" // lum1+blue:x B2 x B0 x b2 x b0
+ "packuswb %%mm3,%%mm3\n" // B3 B1 b3 b1 B3 B1 b3 b1
+ "packuswb %%mm5,%%mm5\n" // B2 B0 b2 b0 B2 B0 b2 b0
+ "punpcklbw %%mm3,%%mm5\n" // B3 B2 B1 B0 b3 b2 b1 b0
+
+
+ // fill destination row1 (needed are mm6=Rr,mm7=Gg,mm5=Bb)
+
+ "pxor %%mm2,%%mm2\n" // 0 0 0 0 0 0 0 0
+ "pxor %%mm4,%%mm4\n" // 0 0 0 0 0 0 0 0
+ "movq %%mm6,%%mm1\n" // R3 R2 R1 R0 r3 r2 r1 r0
+ "movq %%mm5,%%mm3\n" // B3 B2 B1 B0 b3 b2 b1 b0
+ // process lower lum
+ "punpcklbw %%mm4,%%mm1\n" // 0 r3 0 r2 0 r1 0 r0
+ "punpcklbw %%mm4,%%mm3\n" // 0 b3 0 b2 0 b1 0 b0
+ "movq %%mm1,%%mm2\n" // 0 r3 0 r2 0 r1 0 r0
+ "movq %%mm3,%%mm0\n" // 0 b3 0 b2 0 b1 0 b0
+ "punpcklwd %%mm1,%%mm3\n" // 0 r1 0 b1 0 r0 0 b0
+ "punpckhwd %%mm2,%%mm0\n" // 0 r3 0 b3 0 r2 0 b2
+
+ "pxor %%mm2,%%mm2\n" // 0 0 0 0 0 0 0 0
+ "movq %%mm7,%%mm1\n" // G3 G2 G1 G0 g3 g2 g1 g0
+ "punpcklbw %%mm1,%%mm2\n" // g3 0 g2 0 g1 0 g0 0
+ "punpcklwd %%mm4,%%mm2\n" // 0 0 g1 0 0 0 g0 0
+ "por %%mm3, %%mm2\n" // 0 r1 g1 b1 0 r0 g0 b0
+ "movq %%mm2,(%3)\n" // wrote out ! row1
+
+ "pxor %%mm2,%%mm2\n" // 0 0 0 0 0 0 0 0
+ "punpcklbw %%mm1,%%mm4\n" // g3 0 g2 0 g1 0 g0 0
+ "punpckhwd %%mm2,%%mm4\n" // 0 0 g3 0 0 0 g2 0
+ "por %%mm0, %%mm4\n" // 0 r3 g3 b3 0 r2 g2 b2
+ "movq %%mm4,8(%3)\n" // wrote out ! row1
+
+ // fill destination row2 (needed are mm6=Rr,mm7=Gg,mm5=Bb)
+ // this can be done "destructive"
+ "pxor %%mm2,%%mm2\n" // 0 0 0 0 0 0 0 0
+ "punpckhbw %%mm2,%%mm6\n" // 0 R3 0 R2 0 R1 0 R0
+ "punpckhbw %%mm1,%%mm5\n" // G3 B3 G2 B2 G1 B1 G0 B0
+ "movq %%mm5,%%mm1\n" // G3 B3 G2 B2 G1 B1 G0 B0
+ "punpcklwd %%mm6,%%mm1\n" // 0 R1 G1 B1 0 R0 G0 B0
+ "movq %%mm1,(%4)\n" // wrote out ! row2
+ "punpckhwd %%mm6,%%mm5\n" // 0 R3 G3 B3 0 R2 G2 B2
+ "movq %%mm5,8(%4)\n" // wrote out ! row2
+
+ "addl $4,%2\n" // lum+4
+ "addl $4,%5\n" // lum2+4
+ "leal 16(%3),%3\n" // row1+16
+ "leal 16(%4),%4\n" // row2+16
+ "addl $2, %0\n" // cr+2
+ "addl $2, %1\n" // cb+2
+
+ "subl $4,8%5\n" // x+4 x is buf[2]
+ "cmpl $0,8%5\n"
+
+ "jne 1b\n"
+ "addl 20%5, %2\n" // lum += cols
+ "movl %2,16%5\n" // store register in tmp0
+ "movl 20%5,%2\n" // cols->register
+
+ "addl %2, %5\n" // lum2 += cols
+ "addl 12%5, %3\n" // row1+= mod is buf[0]
+ "addl 12%5, %4\n" // row2+= mod is buf[0]
+
+ "movl %2, 8%5\n" // x=cols
+ "movl 16%5,%2\n" // store tmp0 in register
+
+ "cmpl 4%5, %2\n" // buf[1] is end
+ "jl 1b\n"
+ "emms\n"
+ :
+ : "r" (cr), "r"(cb),"r"(lum),
+ "r"(row1),"r"(row2),"m"(buf[0])
+ );
+
+
+
+}
+
+
+#endif
diff --git a/mpeglib/lib/util/render/dither/dither8Bit.cpp b/mpeglib/lib/util/render/dither/dither8Bit.cpp
new file mode 100644
index 00000000..4f85d3fb
--- /dev/null
+++ b/mpeglib/lib/util/render/dither/dither8Bit.cpp
@@ -0,0 +1,306 @@
+/*
+ dither 8 bit depth yuv images
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "dither8Bit.h"
+
+
+Dither8Bit::Dither8Bit(unsigned char pixel[256]) {
+
+ int i;
+ for(i=0;i<256;i++) {
+ this->pixel[i]=pixel[i];
+ }
+ colorTable8Bit=new ColorTable8Bit();
+
+ lum_values = colorTable8Bit->getLumValues();
+ cr_values = colorTable8Bit->getCrValues();
+ cb_values = colorTable8Bit->getCbValues();
+
+
+
+ initOrderedDither();
+
+}
+
+
+Dither8Bit::~Dither8Bit() {
+ int i;
+ for (i=0; i<DITH_SIZE; i++) {
+ delete cb_darrays[i];
+ delete l_darrays[i];
+ delete cr_darrays[i];
+ }
+}
+
+
+
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * InitOrderedDither--
+ *
+ * Structures initialized for ordered dithering.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+void Dither8Bit::initOrderedDither() {
+ int i, j, k, err_range, threshval;
+ unsigned char *lmark, *cmark;
+
+ for (i=0; i<DITH_SIZE; i++) {
+ lmark = l_darrays[i] = new unsigned char[256];
+ for (j=0; j<lum_values[0]; j++) {
+ *lmark++ = 0;
+ }
+ for (j=0; j<(LUM_RANGE-1); j++) {
+ err_range = lum_values[j+1] - lum_values[j];
+ threshval = ((i * err_range) / DITH_SIZE)+lum_values[j];
+
+ for (k=lum_values[j]; k<lum_values[j+1]; k++) {
+ if (k > threshval) {
+ *lmark++ = ((j+1) * (CR_RANGE * CB_RANGE));
+ }
+ else {
+ *lmark++ = (j * (CR_RANGE * CB_RANGE));
+ }
+ }
+ }
+ for (j=lum_values[LUM_RANGE-1]; j<256; j++) {
+ *lmark++ = (LUM_RANGE-1)*(CR_RANGE * CB_RANGE);
+ }
+ }
+ for (i=0; i<DITH_SIZE; i++) {
+ cmark = cr_darrays[i] = new unsigned char[256];
+
+ for (j=0; j<cr_values[0]; j++) {
+ *cmark++ = 0;
+ }
+
+ for (j=0; j<(CR_RANGE-1); j++) {
+ err_range = cr_values[j+1] - cr_values[j];
+ threshval = ((i * err_range) / DITH_SIZE)+cr_values[j];
+
+ for (k=cr_values[j]; k<cr_values[j+1]; k++) {
+ if (k > threshval) {
+ *cmark++ = ((j+1) * CB_RANGE);
+ }
+ else {
+ *cmark++ = (j * CB_RANGE);
+ }
+ }
+ }
+
+ for (j=cr_values[CR_RANGE-1]; j<256; j++) {
+ *cmark++ = (CR_RANGE-1)*(CB_RANGE);
+ }
+ }
+
+ for (i=0; i<DITH_SIZE; i++) {
+ cmark = cb_darrays[i] = new unsigned char[256];
+
+ for (j=0; j<cb_values[0]; j++) {
+ *cmark++ = 0;
+ }
+
+ for (j=0; j<(CB_RANGE-1); j++) {
+ err_range = cb_values[j+1] - cb_values[j];
+ threshval = ((i * err_range) / DITH_SIZE)+cb_values[j];
+
+ for (k=cb_values[j]; k<cb_values[j+1]; k++) {
+ if (k > threshval) {
+ *cmark++ = j+1;
+ }
+ else {
+ *cmark++ = j;
+ }
+ }
+ }
+
+ for (j=cb_values[CB_RANGE-1]; j<256; j++) {
+ *cmark++ = CB_RANGE-1;
+ }
+ }
+}
+
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * OrderedDitherImage --
+ *
+ * Dithers an image using an ordered dither.
+ * Assumptions made:
+ * 1) The color space is allocated y:cr:cb = 8:4:4
+ * 2) The spatial resolution of y:cr:cb is 4:1:1
+ * The channels are dithered based on the standard
+ * ordered dither pattern for a 4x4 area.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+void Dither8Bit::ditherImageOrdered (unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,
+ unsigned char* out,
+ int h,
+ int w) {
+ unsigned char *l, *r, *b, *o1, *o2;
+ unsigned char *l2;
+ unsigned char L, R, B;
+ int i, j;
+
+ l = lum;
+ l2 = lum+w;
+ r = cr;
+ b = cb;
+ o1 = out;
+ o2 = out+w;
+
+
+ for (i=0; i<h; i+=4) {
+
+ for (j=0; j<w; j+=8) {
+
+ R = r[0]; B = b[0];
+
+ L = l[0];
+ o1[0] = pixel[(l_darrays[0][L] + cr_darrays[0][R] + cb_darrays[0][B])];
+ L = l[1];
+ o1[1] = pixel[(l_darrays[8][L] + cr_darrays[8][R] + cb_darrays[8][B])];
+ L = l2[0];
+ o2[0] = pixel[(l_darrays[12][L] + cr_darrays[12][R] + cb_darrays[12][B])];
+ L = l2[1];
+ o2[1] = pixel[(l_darrays[4][L] + cr_darrays[4][R] + cb_darrays[4][B])];
+
+ R = r[1]; B = b[1];
+
+ L = l[2];
+ o1[2] = pixel[(l_darrays[2][L] + cr_darrays[2][R] + cb_darrays[2][B])];
+ L = l[3];
+ o1[3] = pixel[(l_darrays[10][L] + cr_darrays[10][R] + cb_darrays[10][B])];
+ L = l2[2];
+ o2[2] = pixel[(l_darrays[14][L] + cr_darrays[14][R] + cb_darrays[14][B])];
+ L = l2[3];
+ o2[3] = pixel[(l_darrays[6][L] + cr_darrays[6][R] + cb_darrays[6][B])];
+
+ R = r[2]; B = b[2];
+
+ L = l[4];
+ o1[4] = pixel[(l_darrays[0][L] + cr_darrays[0][R] + cb_darrays[0][B])];
+ L = l[5];
+ o1[5] = pixel[(l_darrays[8][L] + cr_darrays[8][R] + cb_darrays[8][B])];
+ L = l2[4];
+ o2[4] = pixel[(l_darrays[12][L] + cr_darrays[12][R] + cb_darrays[12][B])];
+ L = l2[5];
+ o2[5] = pixel[(l_darrays[4][L] + cr_darrays[4][R] + cb_darrays[4][B])];
+
+ R = r[3]; B = b[3];
+
+ L = l[6];
+ o1[6] = pixel[(l_darrays[2][L] + cr_darrays[2][R] + cb_darrays[2][B])];
+ L = l[7];
+ o1[7] = pixel[(l_darrays[10][L] + cr_darrays[10][R] + cb_darrays[10][B])];
+ L = l2[6];
+ o2[6] = pixel[(l_darrays[14][L] + cr_darrays[14][R] + cb_darrays[14][B])];
+ L = l2[7];
+ o2[7] = pixel[(l_darrays[6][L] + cr_darrays[6][R] + cb_darrays[6][B])];
+
+ l += 8;
+ l2 += 8;
+ r += 4;
+ b += 4;
+ o1 += 8;
+ o2 += 8;
+ }
+
+ l += w;
+ l2 += w;
+ o1 += w;
+ o2 += w;
+
+ for (j=0; j<w; j+=8) {
+
+ R = r[0]; B = b[0];
+
+ L = l[0];
+ o1[0] = pixel[(l_darrays[3][L] + cr_darrays[3][R] + cb_darrays[3][B])];
+ L = l[1];
+ o1[1] = pixel[(l_darrays[11][L] + cr_darrays[11][R] + cb_darrays[11][B])];
+ L = l2[0];
+ o2[0] = pixel[(l_darrays[15][L] + cr_darrays[15][R] + cb_darrays[15][B])];
+ L = l2[1];
+ o2[1] = pixel[(l_darrays[7][L] + cr_darrays[7][R] + cb_darrays[7][B])];
+
+ R = r[1]; B = b[1];
+
+ L = l[2];
+ o1[2] = pixel[(l_darrays[1][L] + cr_darrays[1][R] + cb_darrays[1][B])];
+ L = l[3];
+ o1[3] = pixel[(l_darrays[9][L] + cr_darrays[9][R] + cb_darrays[9][B])];
+ L = l2[2];
+ o2[2] = pixel[(l_darrays[13][L] + cr_darrays[13][R] + cb_darrays[13][B])];
+ L = l2[3];
+ o2[3] = pixel[(l_darrays[5][L] + cr_darrays[5][R] + cb_darrays[5][B])];
+
+ R = r[2]; B = b[2];
+
+ L = l[4];
+ o1[4] = pixel[(l_darrays[3][L] + cr_darrays[3][R] + cb_darrays[3][B])];
+ L = l[5];
+ o1[5] = pixel[(l_darrays[11][L] + cr_darrays[11][R] + cb_darrays[11][B])];
+ L = l2[4];
+ o2[4] = pixel[(l_darrays[15][L] + cr_darrays[15][R] + cb_darrays[15][B])];
+ L = l2[5];
+ o2[5] = pixel[(l_darrays[7][L] + cr_darrays[7][R] + cb_darrays[7][B])];
+
+ R = r[3]; B = b[3];
+
+ L = l[6];
+ o1[6] = pixel[(l_darrays[1][L] + cr_darrays[1][R] + cb_darrays[1][B])];
+ L = l[7];
+ o1[7] = pixel[(l_darrays[9][L] + cr_darrays[9][R] + cb_darrays[9][B])];
+ L = l2[6];
+ o2[6] = pixel[(l_darrays[13][L] + cr_darrays[13][R] + cb_darrays[13][B])];
+ L = l2[7];
+ o2[7] = pixel[(l_darrays[5][L] + cr_darrays[5][R] + cb_darrays[5][B])];
+
+ l += 8;
+ l2 += 8;
+ r += 4;
+ b += 4;
+ o1 += 8;
+ o2 += 8;
+ }
+
+ l += w;
+ l2 += w;
+ o1 += w;
+ o2 += w;
+ }
+}
+
diff --git a/mpeglib/lib/util/render/dither/dither8Bit.h b/mpeglib/lib/util/render/dither/dither8Bit.h
new file mode 100644
index 00000000..7bdd4d8f
--- /dev/null
+++ b/mpeglib/lib/util/render/dither/dither8Bit.h
@@ -0,0 +1,63 @@
+/*
+ dither 8 bit depth yuv images
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __DITHER_8BIT_H
+#define __DITHER_8BIT_H
+
+
+#include "colorTable8Bit.h"
+
+#define DITH_SIZE 16
+
+
+class Dither8Bit {
+
+ /* Structures used to implement hybrid ordered dither/floyd-steinberg
+ dither algorithm.
+ */
+
+ unsigned char *l_darrays[DITH_SIZE];
+ unsigned char *cr_darrays[DITH_SIZE];
+ unsigned char *cb_darrays[DITH_SIZE];
+
+ // private colormap
+ unsigned char pixel[256];
+
+ ColorTable8Bit* colorTable8Bit;
+
+ // Arrays holding quantized value ranged for lum, cr, and cb.
+ // (used for 8 Bit)
+
+ int* lum_values;
+ int* cr_values;
+ int* cb_values;
+
+
+ public:
+ Dither8Bit(unsigned char pixel[256]);
+ ~Dither8Bit();
+
+ void ditherImageOrdered (unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,
+ unsigned char* out,
+ int h,
+ int w);
+
+ private:
+ void initOrderedDither();
+};
+
+#endif
+
diff --git a/mpeglib/lib/util/render/dither/ditherDef.h b/mpeglib/lib/util/render/dither/ditherDef.h
new file mode 100644
index 00000000..2e8d7d0e
--- /dev/null
+++ b/mpeglib/lib/util/render/dither/ditherDef.h
@@ -0,0 +1,100 @@
+/*
+ global definitions for dithering
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __DITHERDEF_H
+#define __DITHERDEF_H
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+extern "C" {
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+}
+
+
+#ifdef __GNUC__
+#if (__GNUC__ < 2 || ( __GNUC__ == 2 && __GNUC_MINOR__ < 91 ) )
+#ifndef _AIX
+#warning "inline code disabled! (buggy egcs version)"
+#undef __NO_MATH_INLINES
+#define __NO_MATH_INLINES 1
+#endif
+#endif
+#endif
+#include <math.h>
+
+
+
+/* Gamma correction stuff */
+extern int gammaCorrectFlag;
+extern double gammaCorrect;
+
+/* Chroma correction stuff */
+extern int chromaCorrectFlag;
+extern double chromaCorrect;
+
+
+#define CB_BASE 1
+#define CR_BASE (CB_BASE*CB_RANGE)
+#define LUM_BASE (CR_BASE*CR_RANGE)
+
+#define TABTYPE short
+
+#ifdef SIXTYFOUR_BIT
+#define PIXVAL long
+#else
+#define PIXVAL int
+#endif
+
+#ifdef SIXTYFOUR_BIT
+#define ONE_TWO 1
+#else
+#define ONE_TWO 2
+#endif
+
+
+
+#define Min(x,y) (((x) < (y)) ? (x) : (y))
+#define Max(x,y) (((x) > (y)) ? (x) : (y))
+
+#define CHROMA_CORRECTION128(x) ((x) >= 0 \
+ ? Min(127, (int)(((x) * chromaCorrect))) \
+ : Max(-128, (int)(((x) * chromaCorrect))))
+#define CHROMA_CORRECTION256D(x) ((x) >= 128 \
+ ? 128.0 + Min(127.0, (((x)-128.0) * chromaCorrect)) \
+ : 128.0 - Min(128.0, (((128.0-(x))* chromaCorrect))))
+
+
+
+#define GAMMA_CORRECTION(x) ((int)(pow((x) / 255.0, 1.0/gammaCorrect)* 255.0))
+
+#define CHROMA_CORRECTION128D(x) ((x) >= 0 \
+ ? Min(127.0, ((x) * chromaCorrect)) \
+ : Max(-128.0, ((x) * chromaCorrect)))
+
+#define CHROMA_CORRECTION256(x) ((x) >= 128 \
+ ? 128 + Min(127, (int)(((x)-128.0) * chromaCorrect)) \
+ : 128 - Min(128, (int)((128.0-(x)) * chromaCorrect)))
+
+// Range values for lum, cr, cb.
+#define LUM_RANGE 8
+#define CR_RANGE 4
+#define CB_RANGE 4
+
+
+#endif
diff --git a/mpeglib/lib/util/render/dither/ditherMMX.h b/mpeglib/lib/util/render/dither/ditherMMX.h
new file mode 100644
index 00000000..2f08b689
--- /dev/null
+++ b/mpeglib/lib/util/render/dither/ditherMMX.h
@@ -0,0 +1,38 @@
+/*
+ mmx ditherer
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __DITHERMMX_H
+#define __DITHERMMX_H
+
+#include "ditherDef.h"
+
+
+// The mmx dither routine come from NIST
+// NIST is an mpeg2/dvd player
+// more: http://home.germany.net/100-5083/
+extern void ditherBlock(unsigned char *lum,
+ unsigned char *cr,
+ unsigned char *cb,
+ unsigned char *out,
+ int rows, int cols, int mod);
+
+extern void dither32_mmx(unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,
+ unsigned char* out,
+ int rows,
+ int cols,
+ int mod);
+
+
+#endif
diff --git a/mpeglib/lib/util/render/dither/ditherRGB.cpp b/mpeglib/lib/util/render/dither/ditherRGB.cpp
new file mode 100644
index 00000000..1bcdb2ff
--- /dev/null
+++ b/mpeglib/lib/util/render/dither/ditherRGB.cpp
@@ -0,0 +1,230 @@
+/*
+ copys RGB images to a destination
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "ditherRGB.h"
+
+#include <iostream>
+
+using namespace std;
+
+DitherRGB::DitherRGB() {
+}
+
+
+DitherRGB::~DitherRGB() {
+}
+
+
+int DitherRGB::getDepth(int pixel) {
+ int byteDepth=0;
+
+ switch(pixel) {
+ case 8:
+ byteDepth=1;
+ break;
+ case 15:
+ case 16:
+ byteDepth=2;
+ break;
+ case 24:
+ case 32:
+ byteDepth=4;
+ break;
+ default:
+ cout << "unknown byteDepth:"<<pixel
+ << " in DitherRGB_flipped::flipRGBImage"<<endl;
+ }
+ return byteDepth;
+
+}
+
+void DitherRGB::ditherRGBImage(unsigned char* dest,unsigned char* src,
+ int depth,int width,int height,int offset) {
+ int byteDepth=getDepth(depth);
+ if (byteDepth == 0) {
+ return;
+ }
+
+
+ if (offset==0) {
+ int bytes=height*width*byteDepth;
+ memcpy(dest,src,bytes);
+ return;
+ }
+
+ int i;
+ int lineSize=width*byteDepth;
+
+ offset=offset*byteDepth+lineSize;
+
+ for (i=0;i<height;i++) {
+ memcpy(dest,src,lineSize);
+ src+=lineSize;
+ dest+=offset;
+ }
+
+
+}
+
+void DitherRGB::ditherRGBImage_x2(unsigned char* dest,unsigned char* src,
+ int depth,int width,int height,int offset) {
+
+ int byteDepth=getDepth(depth);
+ if (byteDepth == 0) {
+ return;
+ }
+
+ switch(byteDepth) {
+ case 1:
+ ditherRGB1Byte_x2(dest,src,1,width, height,offset);
+ break;
+ case 2:
+ ditherRGB2Byte_x2(dest,src,2,width, height,offset);
+ break;
+ case 4:
+ ditherRGB4Byte_x2(dest,src,4,width, height,offset);
+ break;
+ default:
+ cout <<"ditherRGBImage_x2 byteDepth:"<<byteDepth
+ <<" not supported"<<endl;
+ }
+}
+
+
+void DitherRGB::ditherRGB1Byte_x2(unsigned char* dest,unsigned char* src,
+ int depth,int width,int height,int offset) {
+
+ //
+ // dest destr
+ // destd destrd
+
+ int lineInc=2*width+offset;
+ unsigned char* destr=dest+1;
+ unsigned char* destd=dest+lineInc;
+ unsigned char* destrd=destd+1;
+
+ int row;
+ int col;
+ //
+ // We copy byte by byte this is slow, but works for
+ // all byteDepth
+ // this memcpy can be optimized with MMX very i) good ii) easily
+
+ for(row=0;row<height;row++) {
+ for(col=0;col<width;col++) {
+ *dest++=*src;
+ *destr++=*src;
+ *destd++=*src;
+ *destrd++=*src;
+ dest++;
+ destr++;
+ destd++;
+ destrd++;
+
+ src++;
+ }
+ dest+=lineInc;
+ destr+=lineInc;
+ destd+=lineInc;
+ destrd+=lineInc;
+ }
+}
+
+
+void DitherRGB::ditherRGB2Byte_x2(unsigned char* destination,
+ unsigned char* source,
+ int depth,int width,int height,int offset) {
+ //
+ // dest destr
+ // destd destrd
+
+ unsigned short int* src=(unsigned short int*) source;
+ unsigned short int* dest=(unsigned short int*) destination;
+
+ int lineInc=2*width+offset;
+ unsigned short int* destr=dest+1;
+ unsigned short int* destd=dest+lineInc;
+ unsigned short int* destrd=destd+1;
+
+ int row;
+ int col;
+ //
+ // We copy byte by byte this is slow, but works for
+ // all byteDepth
+ // this memcpy can be optimized with MMX very i) good ii) easily
+
+ for(row=0;row<height;row++) {
+ for(col=0;col<width;col++) {
+ *dest++=*src;
+ *destr++=*src;
+ *destd++=*src;
+ *destrd++=*src;
+ dest++;
+ destr++;
+ destd++;
+ destrd++;
+
+ src++;
+ }
+ dest+=lineInc;
+ destr+=lineInc;
+ destd+=lineInc;
+ destrd+=lineInc;
+ }
+}
+
+
+void DitherRGB::ditherRGB4Byte_x2(unsigned char* destination,
+ unsigned char* source,
+ int depth,int width,int height,int offset) {
+
+ //
+ // dest destr
+ // destd destrd
+
+ unsigned int* src=(unsigned int*) source;
+ unsigned int* dest=(unsigned int*) destination;
+
+ int lineInc=2*width+offset;
+ unsigned int* destr=dest+1;
+ unsigned int* destd=dest+lineInc;
+ unsigned int* destrd=destd+1;
+
+ int row;
+ int col;
+ //
+ // We copy byte by byte this is slow, but works for
+ // all byteDepth
+ // this memcpy can be optimized with MMX very i) good ii) easily
+
+ for(row=0;row<height;row++) {
+ for(col=0;col<width;col++) {
+ *dest++=*src;
+ *destr++=*src;
+ *destd++=*src;
+ *destrd++=*src;
+ dest++;
+ destr++;
+ destd++;
+ destrd++;
+
+ src++;
+ }
+ dest+=lineInc;
+ destr+=lineInc;
+ destd+=lineInc;
+ destrd+=lineInc;
+ }
+
+}
+
diff --git a/mpeglib/lib/util/render/dither/ditherRGB.h b/mpeglib/lib/util/render/dither/ditherRGB.h
new file mode 100644
index 00000000..6f24cd8c
--- /dev/null
+++ b/mpeglib/lib/util/render/dither/ditherRGB.h
@@ -0,0 +1,45 @@
+/*
+ copys RGB images to a destination
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#ifndef __DITHERRGB_H
+#define __DITHERRGB_H
+
+#include "colorTableHighBit.h"
+
+class DitherRGB {
+
+ int flipSize;
+ unsigned char* flipSpace;
+
+ public:
+ DitherRGB();
+ ~DitherRGB();
+
+ // Note: this methods swaps the image
+ // itsself
+ void ditherRGBImage(unsigned char* dest,unsigned char* src,
+ int depth,int width,int height,int offset);
+ void ditherRGBImage_x2(unsigned char* dest,unsigned char* src,
+ int depth,int width,int height,int offset);
+ private:
+ int getDepth(int pixel);
+ // depth is here in byte!
+ void ditherRGB1Byte_x2(unsigned char* dest,unsigned char* src,
+ int depth,int width,int height,int offset);
+ void ditherRGB2Byte_x2(unsigned char* dest,unsigned char* src,
+ int depth,int width,int height,int offset);
+ void ditherRGB4Byte_x2(unsigned char* dest,unsigned char* src,
+ int depth,int width,int height,int offset);
+
+};
+
+#endif
diff --git a/mpeglib/lib/util/render/dither/ditherRGB_flipped.cpp b/mpeglib/lib/util/render/dither/ditherRGB_flipped.cpp
new file mode 100644
index 00000000..ba177675
--- /dev/null
+++ b/mpeglib/lib/util/render/dither/ditherRGB_flipped.cpp
@@ -0,0 +1,82 @@
+/*
+ flips RGB images
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "ditherRGB_flipped.h"
+
+#include <iostream>
+
+using namespace std;
+
+
+DitherRGB_flipped::DitherRGB_flipped() {
+ flipSpace=NULL;
+ flipSize=0;
+}
+
+DitherRGB_flipped::~DitherRGB_flipped() {
+ if (flipSpace != NULL) {
+ delete flipSpace;
+ }
+}
+
+
+
+
+void DitherRGB_flipped::flipRGBImage(unsigned char* dest,unsigned char* src,
+ int depth,int width,int height,int ) {
+
+ int byteDepth;
+
+ switch(depth) {
+ case 8:
+ byteDepth=1;
+ break;
+ case 15:
+ case 16:
+ byteDepth=2;
+ break;
+ case 24:
+ case 32:
+ byteDepth=4;
+ break;
+ default:
+ cout << "unknown byteDepth:"<<depth
+ << " in DitherRGB_flipped::flipRGBImage"<<endl;
+ return;
+ }
+
+
+ int spaceNeeded=width*height*byteDepth;
+
+ if (spaceNeeded > flipSize) {
+ if (flipSpace != NULL) {
+ delete flipSpace;
+ }
+ cout << "flipSpace:"<<spaceNeeded<<endl;
+ flipSpace=new unsigned char[spaceNeeded+64];
+ flipSize=spaceNeeded;
+ }
+
+ int i;
+ int lineSize=width*byteDepth;
+ unsigned char* end=dest+lineSize*(height-1);
+
+ for (i=0;i<height;i++) {
+ memcpy(end,src,lineSize);
+ src+=lineSize;
+ end-=lineSize;
+ }
+
+}
+
+
diff --git a/mpeglib/lib/util/render/dither/ditherRGB_flipped.h b/mpeglib/lib/util/render/dither/ditherRGB_flipped.h
new file mode 100644
index 00000000..1d99f7f6
--- /dev/null
+++ b/mpeglib/lib/util/render/dither/ditherRGB_flipped.h
@@ -0,0 +1,34 @@
+/*
+ flips RGB images
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#ifndef __DITHERRGB_FLIPPED_H
+#define __DITHERRGB_FLIPPED_H
+
+#include "colorTableHighBit.h"
+
+class DitherRGB_flipped {
+
+ int flipSize;
+ unsigned char* flipSpace;
+
+ public:
+ DitherRGB_flipped();
+ ~DitherRGB_flipped();
+
+ // Note: this methods swaps the image
+ // itsself
+ void flipRGBImage(unsigned char* dest,unsigned char* src,
+ int depth,int width,int height,int offset);
+
+};
+
+#endif
diff --git a/mpeglib/lib/util/render/dither/ditherWrapper.cpp b/mpeglib/lib/util/render/dither/ditherWrapper.cpp
new file mode 100644
index 00000000..c6c37a79
--- /dev/null
+++ b/mpeglib/lib/util/render/dither/ditherWrapper.cpp
@@ -0,0 +1,246 @@
+/*
+ wrapper for X11 Window
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "ditherWrapper.h"
+
+#include <iostream>
+
+using namespace std;
+
+
+/*
+ Flag for gamma correction
+ Makes images brighter/darker.
+ It's in the source but not activated (for now)
+*/
+int gammaCorrectFlag = 0;
+double gammaCorrect = 1.0;
+
+/*
+ Flag for chroma correction.
+ reduce the color intensity..
+ It's in the source but not activated (for now)
+*/
+int chromaCorrectFlag = 0;
+double chromaCorrect = 1.0;
+
+
+
+DitherWrapper::DitherWrapper(int bpp,unsigned int redMask,
+ unsigned int greenMask,unsigned int blueMask,
+ unsigned char pixel[256]) {
+
+ this->bpp=bpp;
+ this->redMask=redMask;
+ this->greenMask=greenMask;
+ this->blueMask=blueMask;
+
+
+ dither8Bit=new Dither8Bit(pixel);
+ dither16Bit=new Dither16Bit(redMask,greenMask,blueMask);
+ dither32Bit=new Dither32Bit(redMask,greenMask,blueMask);
+ ditherRGB_flipped=new DitherRGB_flipped();
+ ditherRGB=new DitherRGB();
+
+
+#ifdef INTEL
+ lmmx=mm_support();
+#else
+ lmmx=false;
+#endif
+
+
+}
+
+
+DitherWrapper::~DitherWrapper(){
+ delete dither16Bit;
+ delete dither8Bit;
+ delete dither32Bit;
+ delete ditherRGB_flipped;
+ delete ditherRGB;
+}
+
+
+
+
+
+void DitherWrapper::doDither(YUVPicture* pic,int depth,int imageMode,
+ unsigned char* dest,int offset) {
+
+
+ //
+ // according to the input imageType and the output area
+ // handle different dither methods
+ //
+
+ int inputType=pic->getImageType();
+
+ if ( (inputType == PICTURE_YUVMODE_CR_CB) ||
+ (inputType == PICTURE_YUVMODE_CB_CR) ) {
+ doDitherYUV(pic,depth,imageMode,dest,offset);
+ return;
+ }
+
+ if ( (inputType == PICTURE_RGB) ||
+ (inputType == PICTURE_RGB_FLIPPED) ){
+ doDitherRGB(pic,depth,imageMode,dest,offset);
+ return;
+ }
+
+ cout << "unknown inputType:"<<inputType
+ << " in DitherWrapper::doDither"<<endl;
+}
+
+
+void DitherWrapper::doDitherRGB(YUVPicture* pic,int depth,int imageMode,
+ unsigned char* dest,int offset) {
+
+ int inputType=pic->getImageType();
+
+ switch(inputType) {
+ case PICTURE_RGB:
+ doDitherRGB_NORMAL(pic,depth,imageMode,dest,offset);
+ break;
+ case PICTURE_RGB_FLIPPED:
+ doDitherRGB_FLIPPED(pic,depth,imageMode,dest,offset);
+ break;
+ default:
+ cout << "unknown RGB type:"<<inputType<<" in DitherWrapper"<<endl;
+ exit(0);
+ }
+}
+
+
+void DitherWrapper::doDitherRGB_NORMAL(YUVPicture* pic,
+ int depth,int imageMode,
+ unsigned char* dest,int offset) {
+
+ int w=pic->getWidth();
+ int h=pic->getHeight();
+
+ unsigned char* src=pic->getImagePtr();
+
+ if (imageMode & _IMAGE_DOUBLE) {
+ ditherRGB->ditherRGBImage_x2(dest,src,depth,w,h,offset);
+ } else {
+ ditherRGB->ditherRGBImage(dest,src,depth,w,h,offset);
+ }
+}
+
+void DitherWrapper::doDitherRGB_FLIPPED(YUVPicture* pic,
+ int depth,int imageMode,
+ unsigned char* dest,int offset) {
+
+ int w=pic->getWidth();
+ int h=pic->getHeight();
+
+ unsigned char* src=pic->getImagePtr();
+
+ ditherRGB_flipped->flipRGBImage(dest,src,depth,w,h,offset);
+}
+
+
+
+void DitherWrapper::doDitherYUV(YUVPicture* pic,int depth,int imageMode,
+ unsigned char* dest,int offset) {
+
+ if (imageMode & _IMAGE_DOUBLE) {
+ doDither_x2(pic,depth,dest,offset);
+ } else {
+ doDither_std(pic,depth,dest,offset);
+ }
+}
+
+
+void DitherWrapper::doDither_std(YUVPicture* pic,int depth,
+ unsigned char* dest,int offset){
+
+ int h=pic->getHeight();
+ int w=pic->getWidth();
+ unsigned char* lum=pic->getLuminancePtr();
+ unsigned char* cr=pic->getCrPtr();
+ unsigned char* cb=pic->getCbPtr();
+
+
+ switch (depth) {
+ case 8:
+ dither8Bit->ditherImageOrdered(lum, cr, cb,dest , h, w);
+ break;
+ case 16:
+ if (lmmx) {
+ ditherBlock(lum,cr,cb,dest,h,w,offset);
+ } else {
+ dither16Bit->ditherImageColor16(lum,cr,cb,dest,h,w,offset);
+ }
+
+ break;
+ case 24:
+ case 32:
+ if (lmmx) {
+ dither32_mmx(lum, cr, cb,dest ,h,w,offset);
+ } else {
+ dither32Bit->ditherImageColor32(lum, cr, cb,dest ,h,w,offset);
+ }
+
+
+ break;
+ default:
+ cout << "cannot dither depth:"<<depth<<endl;
+ }
+
+}
+
+
+void DitherWrapper::doDither_x2(YUVPicture* pic,int depth,
+ unsigned char* dest,int offset){
+
+ int h=pic->getHeight();
+ int w=pic->getWidth();
+ unsigned char* lum=pic->getLuminancePtr();
+ unsigned char* cr=pic->getCrPtr();
+ unsigned char* cb=pic->getCbPtr();
+
+
+ switch (depth) {
+ case 8: {
+ // we do dither with the 8Bit std YUV ditherer to RGB
+ // and then we do the double part with the
+ // RGB ditherer. Its obviously much slower but at
+ // least it works. To not allocate memory twice
+ // we are a bit tricky. We know that the image
+ // has space for doubls size. We but the not double size
+ // image at the bottom of the dest. Maybe that
+ // the last line gets overwritten
+ int memPos=3*h*w;
+ dither8Bit->ditherImageOrdered(lum, cr, cb,dest+memPos, h, w);
+ unsigned char* src=dest+memPos;
+ ditherRGB->ditherRGBImage_x2(dest,src,depth,w,h,0);
+ break;
+ }
+ case 16:
+ dither16Bit->ditherImageTwox2Color16(lum,cr,cb,dest,h,w,offset);
+ break;
+ case 24:
+ case 32:
+ if (lmmx) {
+ //dither32x2_mmx(lum, cr, cb,dest ,h,w,offset);
+ dither32Bit->ditherImageTwox2Color32(lum,cr,cb,dest,h,w,offset);
+ } else {
+ dither32Bit->ditherImageTwox2Color32(lum,cr,cb,dest,h,w,offset);
+ }
+ break;
+ default:
+ cout << "cannot dither depth:" << depth << endl;
+ }
+}
diff --git a/mpeglib/lib/util/render/dither/ditherWrapper.h b/mpeglib/lib/util/render/dither/ditherWrapper.h
new file mode 100644
index 00000000..b01abff8
--- /dev/null
+++ b/mpeglib/lib/util/render/dither/ditherWrapper.h
@@ -0,0 +1,80 @@
+/*
+ wrapper for X11 Window
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __DITHERWRAPPER_H
+#define __DITHERWRAPPER_H
+
+
+#include "../../mmx/mmx.h"
+
+#include "../yuvPicture.h"
+#include "../imageBase.h"
+#include <stdlib.h>
+#include "ditherMMX.h"
+#include "dither8Bit.h"
+#include "dither16Bit.h"
+#include "dither32Bit.h"
+#include "ditherRGB_flipped.h"
+#include "ditherRGB.h"
+
+
+/**
+ Wraps all calls to software ditherer and the different
+ resolutions,mmx enhancements, and doublesize ditherers.
+*/
+
+
+class DitherWrapper {
+
+ int lmmx;
+
+ int bpp;
+ // colorMask
+ unsigned int redMask;
+ unsigned int greenMask;
+ unsigned int blueMask;
+
+ Dither8Bit* dither8Bit;
+ Dither16Bit* dither16Bit;
+ Dither32Bit* dither32Bit;
+ DitherRGB_flipped* ditherRGB_flipped;
+ DitherRGB* ditherRGB;
+
+ public:
+ DitherWrapper(int bpp,unsigned int redMask,
+ unsigned int greenMask,unsigned int blueMask,
+ unsigned char pixel[256]);
+ ~DitherWrapper();
+
+/* int getDitherSize(); */
+/* void setDitherSize(int ditherMode); */
+
+ void doDither(YUVPicture* pic,int depth,int imageMode,
+ unsigned char* dest,int offset);
+
+
+ private:
+ void doDitherYUV(YUVPicture* pic,int depth,int imageMode,
+ unsigned char* dest,int offset);
+ void doDitherRGB(YUVPicture* pic,int depth,int imageMode,
+ unsigned char* dest,int offset);
+ void doDitherRGB_NORMAL(YUVPicture* pic,int depth,int imageMode,
+ unsigned char* dest,int offset);
+ void doDitherRGB_FLIPPED(YUVPicture* pic,int depth,int imageMode,
+ unsigned char* dest,int offset);
+
+ void doDither_std(YUVPicture* pic,int depth,unsigned char* dest,int offset);
+ void doDither_x2(YUVPicture* pic,int depth,unsigned char* dest,int offset);
+};
+
+#endif
diff --git a/mpeglib/lib/util/render/dither/ditherer_mmx16.cpp b/mpeglib/lib/util/render/dither/ditherer_mmx16.cpp
new file mode 100644
index 00000000..757f0676
--- /dev/null
+++ b/mpeglib/lib/util/render/dither/ditherer_mmx16.cpp
@@ -0,0 +1,256 @@
+
+#include "ditherMMX.h"
+
+#include <iostream>
+
+using namespace std;
+
+#ifndef INTEL
+// nothing
+void ditherBlock(unsigned char *lum, unsigned char *cr, unsigned char *cb,
+ unsigned char *out,
+ int cols, int rows, int screen_width) {
+ printf("call to ditherBlock. this should never happen\n");
+ printf("check mmx detection routine.\n");
+ exit(0);
+}
+#else
+
+
+static long long MMX16_0 = 0L;
+static unsigned long MMX16_10w[] = {0x00100010, 0x00100010};
+static unsigned long MMX16_80w[] = {0x00800080, 0x00800080};
+static unsigned long MMX16_00FFw[] = {0x00ff00ff, 0x00ff00ff};
+static unsigned short MMX16_Ublucoeff[] = {0x81, 0x81, 0x81, 0x81};
+static unsigned short MMX16_Vredcoeff[] = {0x66, 0x66, 0x66, 0x66};
+static unsigned short MMX16_Ugrncoeff[] = {0xffe8, 0xffe8, 0xffe8, 0xffe8};
+static unsigned short MMX16_Vgrncoeff[] = {0xffcd, 0xffcd, 0xffcd, 0xffcd};
+static unsigned short MMX16_Ycoeff[] = {0x4a, 0x4a, 0x4a, 0x4a};
+static unsigned short MMX16_redmask[] = {0xf800, 0xf800, 0xf800, 0xf800};
+static unsigned short MMX16_grnmask[] = {0x7e0, 0x7e0, 0x7e0, 0x7e0};
+
+void dummy_dithermmx16() {
+ cout << "MMX16_0"<<MMX16_0<<endl;
+ cout << "MMX16_10w:"<<MMX16_10w<<endl;
+ cout << "MMX16_80w:"<<MMX16_80w<<endl;
+ cout << "MMX16_Ublucoeff:"<<MMX16_Ublucoeff<<endl;
+ cout << "MMX16_Vredcoeff:"<<MMX16_Vredcoeff<<endl;
+ cout << "MMX16_Ugrncoeff:"<<MMX16_Ugrncoeff<<endl;
+ cout << "MMX16_Vgrncoeff:"<<MMX16_Vgrncoeff<<endl;
+ cout << "MMX16_Ycoeff:"<<MMX16_Ycoeff<<endl;
+ cout << "MMX16_redmask:"<<MMX16_redmask<<endl;
+ cout << "MMX16_grnmask:"<<MMX16_grnmask<<endl;
+ cout << "MMX16_00FFw:"<<MMX16_00FFw<<endl;
+}
+
+
+void ditherBlock(unsigned char *lum,
+ unsigned char *cr,
+ unsigned char *cb,
+ unsigned char *out,
+ int rows,
+ int cols,
+ int mod) {
+
+ unsigned short *row1;
+ unsigned short *row2;
+ row1 = (unsigned short* )out; // 16 bit target
+
+ unsigned char* end = lum +cols*rows; // Pointer to the end
+ int x=cols;
+ row2=row1+mod+cols; // start of second row
+ mod=2*cols+4*mod; // increment for row1 in byte
+
+ // buffer for asm function
+ int buf[6];
+ buf[0]=(int)(lum+cols); // lum2 pointer
+ buf[1]=(int)end;
+ buf[2]=x;
+ buf[3]=mod;
+ buf[4]=0; //tmp0;
+ buf[5]=cols;
+
+
+
+ __asm__ __volatile__(
+ ".align 32\n"
+ "1:\n"
+ "movd (%1), %%mm0\n" // 4 Cb 0 0 0 0 u3 u2 u1 u0
+ "pxor %%mm7, %%mm7\n"
+ "movd (%0), %%mm1\n" // 4 Cr 0 0 0 0 v3 v2 v1 v0
+ "punpcklbw %%mm7, %%mm0\n" // 4 W cb 0 u3 0 u2 0 u1 0 u0
+ "punpcklbw %%mm7, %%mm1\n" // 4 W cr 0 v3 0 v2 0 v1 0 v0
+ "psubw MMX16_80w, %%mm0\n"
+ "psubw MMX16_80w, %%mm1\n"
+ "movq %%mm0, %%mm2\n" // Cb 0 u3 0 u2 0 u1 0 u0
+ "movq %%mm1, %%mm3\n" // Cr
+ "pmullw MMX16_Ugrncoeff, %%mm2\n" // Cb2green 0 R3 0 R2 0 R1 0 R0
+ "movq (%2), %%mm6\n" // L1 l7 L6 L5 L4 L3 L2 L1 L0
+ "pmullw MMX16_Ublucoeff, %%mm0\n" // Cb2blue
+ "pand MMX16_00FFw, %%mm6\n" // L1 00 L6 00 L4 00 L2 00 L0
+ "pmullw MMX16_Vgrncoeff, %%mm3\n" // Cr2green
+ "movq (%2), %%mm7\n" // L2
+ "pmullw MMX16_Vredcoeff, %%mm1\n" // Cr2red
+ // "psubw MMX16_10w, %%mm6\n"
+ "psrlw $8, %%mm7\n" // L2 00 L7 00 L5 00 L3 00 L1
+ "pmullw MMX16_Ycoeff, %%mm6\n" // lum1
+ // "psubw MMX16_10w, %%mm7\n" // L2
+ "paddw %%mm3, %%mm2\n" // Cb2green + Cr2green == green
+ "pmullw MMX16_Ycoeff, %%mm7\n" // lum2
+
+ "movq %%mm6, %%mm4\n" // lum1
+ "paddw %%mm0, %%mm6\n" // lum1 +blue 00 B6 00 B4 00 B2 00 B0
+ "movq %%mm4, %%mm5\n" // lum1
+ "paddw %%mm1, %%mm4\n" // lum1 +red 00 R6 00 R4 00 R2 00 R0
+ "paddw %%mm2, %%mm5\n" // lum1 +green 00 G6 00 G4 00 G2 00 G0
+ "psraw $6, %%mm4\n" // R1 0 .. 64
+ "movq %%mm7, %%mm3\n" // lum2 00 L7 00 L5 00 L3 00 L1
+ "psraw $6, %%mm5\n" // G1 - .. +
+ "paddw %%mm0, %%mm7\n" // Lum2 +blue 00 B7 00 B5 00 B3 00 B1
+ "psraw $6, %%mm6\n" // B1 0 .. 64
+ "packuswb %%mm4, %%mm4\n" // R1 R1
+ "packuswb %%mm5, %%mm5\n" // G1 G1
+ "packuswb %%mm6, %%mm6\n" // B1 B1
+ "punpcklbw %%mm4, %%mm4\n"
+ "punpcklbw %%mm5, %%mm5\n"
+
+ "pand MMX16_redmask, %%mm4\n"
+ "psllw $3, %%mm5\n" // GREEN 1
+ "punpcklbw %%mm6, %%mm6\n"
+ "pand MMX16_grnmask, %%mm5\n"
+ "pand MMX16_redmask, %%mm6\n"
+ "por %%mm5, %%mm4\n" //
+ "psrlw $11, %%mm6\n" // BLUE 1
+ "movq %%mm3, %%mm5\n" // lum2
+ "paddw %%mm1, %%mm3\n" // lum2 +red 00 R7 00 R5 00 R3 00 R1
+ "paddw %%mm2, %%mm5\n" // lum2 +green 00 G7 00 G5 00 G3 00 G1
+ "psraw $6, %%mm3\n" // R2
+ "por %%mm6, %%mm4\n" // MM4
+ "psraw $6, %%mm5\n" // G2
+
+ "movl %2,16%5\n" // store register in tmp0
+ "movl %5,%2\n" // lum2->register
+ "movq (%2),%%mm6\n" // 0 0 0 0 L3 L2 L1 L0 (load lum2)
+
+
+ //"movq (%2, %5), %%mm6\n" // L3 load lum2
+ "psraw $6, %%mm7\n"
+ "packuswb %%mm3, %%mm3\n"
+ "packuswb %%mm5, %%mm5\n"
+ "packuswb %%mm7, %%mm7\n"
+ "pand MMX16_00FFw, %%mm6\n" // L3
+ "punpcklbw %%mm3, %%mm3\n"
+ // "psubw MMX16_10w, %%mm6\n" // L3
+ "punpcklbw %%mm5, %%mm5\n"
+ "pmullw MMX16_Ycoeff, %%mm6\n" // lum3
+ "punpcklbw %%mm7, %%mm7\n"
+ "psllw $3, %%mm5\n" // GREEN 2
+ "pand MMX16_redmask, %%mm7\n"
+ "pand MMX16_redmask, %%mm3\n"
+ "psrlw $11, %%mm7\n" // BLUE 2
+ "pand MMX16_grnmask, %%mm5\n"
+ "por %%mm7, %%mm3\n"
+
+ "movq (%2), %%mm7\n" // L4 load lum2
+ "movl 16%5,%2\n" // tmp0->register
+
+ "por %%mm5, %%mm3\n" //
+ "psrlw $8, %%mm7\n" // L4
+ "movq %%mm4, %%mm5\n"
+ // "psubw MMX16_10w, %%mm7\n" // L4
+ "punpcklwd %%mm3, %%mm4\n"
+ "pmullw MMX16_Ycoeff, %%mm7\n" // lum4
+ "punpckhwd %%mm3, %%mm5\n"
+
+ "movq %%mm4, (%3)\n" // write row1
+ "movq %%mm5, 8(%3)\n" // write row1
+
+ "movq %%mm6, %%mm4\n" // Lum3
+ "paddw %%mm0, %%mm6\n" // Lum3 +blue
+
+ "movq %%mm4, %%mm5\n" // Lum3
+ "paddw %%mm1, %%mm4\n" // Lum3 +red
+ "paddw %%mm2, %%mm5\n" // Lum3 +green
+ "psraw $6, %%mm4\n"
+ "movq %%mm7, %%mm3\n" // Lum4
+ "psraw $6, %%mm5\n"
+ "paddw %%mm0, %%mm7\n" // Lum4 +blue
+ "psraw $6, %%mm6\n" // Lum3 +blue
+ "movq %%mm3, %%mm0\n" // Lum4
+ "packuswb %%mm4, %%mm4\n"
+ "paddw %%mm1, %%mm3\n" // Lum4 +red
+ "packuswb %%mm5, %%mm5\n"
+ "paddw %%mm2, %%mm0\n" // Lum4 +green
+ "packuswb %%mm6, %%mm6\n"
+ "punpcklbw %%mm4, %%mm4\n"
+ "punpcklbw %%mm5, %%mm5\n"
+ "punpcklbw %%mm6, %%mm6\n"
+ "psllw $3, %%mm5\n" // GREEN 3
+ "pand MMX16_redmask, %%mm4\n"
+ "psraw $6, %%mm3\n" // psr 6
+ "psraw $6, %%mm0\n"
+ "pand MMX16_redmask, %%mm6\n" // BLUE
+ "pand MMX16_grnmask, %%mm5\n"
+ "psrlw $11, %%mm6\n" // BLUE 3
+ "por %%mm5, %%mm4\n"
+ "psraw $6, %%mm7\n"
+ "por %%mm6, %%mm4\n"
+ "packuswb %%mm3, %%mm3\n"
+ "packuswb %%mm0, %%mm0\n"
+ "packuswb %%mm7, %%mm7\n"
+ "punpcklbw %%mm3, %%mm3\n"
+ "punpcklbw %%mm0, %%mm0\n"
+ "punpcklbw %%mm7, %%mm7\n"
+ "pand MMX16_redmask, %%mm3\n"
+ "pand MMX16_redmask, %%mm7\n" // BLUE
+ "psllw $3, %%mm0\n" // GREEN 4
+ "psrlw $11, %%mm7\n"
+ "pand MMX16_grnmask, %%mm0\n"
+ "por %%mm7, %%mm3\n"
+ "por %%mm0, %%mm3\n"
+
+ "movq %%mm4, %%mm5\n"
+
+ "punpcklwd %%mm3, %%mm4\n"
+ "punpckhwd %%mm3, %%mm5\n"
+
+ "movq %%mm4, (%4)\n"
+ "movq %%mm5, 8(%4)\n"
+
+ "subl $8, 8%5\n" // x-=8
+ "addl $8, %5\n" // lum2+8
+ "addl $8, %2\n"
+ "addl $4, %0\n"
+ "addl $4, %1\n"
+ "cmpl $0, 8%5\n"
+ "leal 16(%3), %3\n"
+ "leal 16(%4), %4\n" // row2+16
+
+
+ "jne 1b\n"
+ "addl 20%5, %2\n" // lum += cols
+
+ "movl %2,16%5\n" // store register in tmp0
+ "movl 20%5,%2\n" // cols->register
+
+ "addl %2, %5\n" // lum2 += cols
+ "addl 12%5, %3\n" // row1+= mod
+ "addl 12%5, %4\n" // row2+= mod
+ "movl %2, 8%5\n" // x=cols
+ "movl 16%5,%2\n" // store tmp0 in register
+
+ "cmpl 4%5, %2\n"
+ "jl 1b\n"
+
+ :
+ :"r" (cr), "r"(cb),"r"(lum),
+ "r"(row1),"r"(row2),"m"(buf[0])
+
+ );
+ __asm__ (
+ "emms\n"
+ );
+
+ }
+
+#endif
diff --git a/mpeglib/lib/util/render/dither2YUV/Makefile.am b/mpeglib/lib/util/render/dither2YUV/Makefile.am
new file mode 100644
index 00000000..374658a3
--- /dev/null
+++ b/mpeglib/lib/util/render/dither2YUV/Makefile.am
@@ -0,0 +1,22 @@
+# libdivxplugin - Makefile.am
+
+EXTRA_DIST = README
+
+INCLUDES = -I.. $(all_includes)
+
+
+noinst_LTLIBRARIES = libdivxutil_dither.la
+
+noinst_HEADERS = dither2YUV.h rgb2yuvdefs.h rgb2yuv16.h \
+ rgb2yuv32.h
+
+libdivxutil_dither_la_SOURCES = dither2YUV.cpp rgb2yuv16.cpp \
+ rgb2yuv32.cpp
+
+
+
+
+
+
+
+
diff --git a/mpeglib/lib/util/render/dither2YUV/README b/mpeglib/lib/util/render/dither2YUV/README
new file mode 100644
index 00000000..66246c13
--- /dev/null
+++ b/mpeglib/lib/util/render/dither2YUV/README
@@ -0,0 +1,13 @@
+
+
+* we have a Dither2Yuv base class. Currently this is not derived
+ from a basic ditherWrapper class because we don not have this
+ in mpeglib yet.
+ TODO: change in mpeglib DitherWrapper->Dither2RGB and
+ make DitherWrapper pure virtual and derive Dither2YUV
+ Dither2RGB from this class.
+
+* Note we do not support 8 Bit here, thus the constructor looks
+ dofferent.
+
+
diff --git a/mpeglib/lib/util/render/dither2YUV/dither2YUV.cpp b/mpeglib/lib/util/render/dither2YUV/dither2YUV.cpp
new file mode 100644
index 00000000..db4a3288
--- /dev/null
+++ b/mpeglib/lib/util/render/dither2YUV/dither2YUV.cpp
@@ -0,0 +1,124 @@
+/*
+ this class dithery RGB picture to yuv12
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "dither2YUV.h"
+
+#include <iostream>
+
+using namespace std;
+
+
+Dither2YUV::Dither2YUV() {
+
+
+ lmmx=mm_support();
+
+}
+
+
+Dither2YUV::~Dither2YUV(){
+}
+
+
+
+
+
+
+void Dither2YUV::doDither(YUVPicture* pic,int depth,int ditherSize,
+ unsigned char* dest,int offset) {
+
+ int inputType=pic->getImageType();
+
+ switch(inputType) {
+ case PICTURE_RGB:
+ doDitherRGB_NORMAL(pic,depth,ditherSize,dest,offset);
+ break;
+ default:
+ std::cout << "unknown RGB type:"<<inputType<<" in Dither2YUV"<<std::endl;
+ exit(0);
+ }
+}
+
+
+void Dither2YUV::doDitherRGB_NORMAL(YUVPicture* rgbPic,
+ int depth,int ditherSize,
+ unsigned char* dest,int offset) {
+
+ switch (ditherSize) {
+ case _SIZE_NORMAL:
+ doDither2YUV_std(rgbPic,depth,dest,offset);
+ break;
+ case _SIZE_DOUBLE:
+ std::cout << "double not supported for RGB"<<std::endl;
+ break;
+ default:
+ std::cout << "unknown size:"<<ditherSize<<" in Dither2YUV"<<std::endl;
+ exit(0);
+ }
+}
+
+
+void Dither2YUV::doDither2YUV_std(YUVPicture* rgbPic,int depth,
+ unsigned char* dest,int offset){
+
+ int h=rgbPic->getHeight();
+ int w=rgbPic->getWidth();
+ int lumLength=w * h;
+ int colorLength=(w * h) / 4;
+
+ unsigned char* lum=dest;
+ unsigned char* cr=lum+lumLength;
+ unsigned char* cb=cr+colorLength;
+ unsigned char* rgbSource=rgbPic->getImagePtr();
+
+
+ switch (depth) {
+ case 8:
+ std::cout << "8 bit dither to yuv not supported"<<std::endl;
+ exit(0);
+ break;
+ case 16:
+ if (lmmx) {
+#ifdef INTEL
+ rgb2yuv16bit_mmx(rgbSource,lum,cr,cb,h,w);
+#endif
+ } else {
+ rgb2yuv16bit(rgbSource,lum,cr,cb,h,w);
+ }
+
+ break;
+ case 24:
+ if (lmmx) {
+#ifdef INTEL
+ rgb2yuv24bit_mmx(rgbSource,lum,cr,cb,h,w);
+#endif
+ } else {
+ rgb2yuv24bit(rgbSource,lum,cr,cb,h,w);
+ }
+ break;
+ case 32:
+ if (lmmx) {
+#ifdef INTEL
+ rgb2yuv32bit_mmx(rgbSource,lum,cr,cb,h,w);
+#endif
+ } else {
+ rgb2yuv32bit(rgbSource,lum,cr,cb,h,w);
+ }
+ break;
+ default:
+ std::cout << "cannot dither depth:"<<depth<<std::endl;
+ }
+
+}
+
+
diff --git a/mpeglib/lib/util/render/dither2YUV/dither2YUV.h b/mpeglib/lib/util/render/dither2YUV/dither2YUV.h
new file mode 100644
index 00000000..5ef26b2b
--- /dev/null
+++ b/mpeglib/lib/util/render/dither2YUV/dither2YUV.h
@@ -0,0 +1,64 @@
+/*
+ this class dithery RGB picture to yuv12
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __DITHER2YUV_H
+#define __DITHER2YUV_H
+
+
+#include "../../mmx/mmx.h"
+#include "../yuvPicture.h"
+
+
+#include <stdlib.h>
+#include "rgb2yuv16.h"
+#include "rgb2yuv32.h"
+
+#define _SIZE_NONE 0
+#define _SIZE_NORMAL 1
+#define _SIZE_DOUBLE 2
+
+
+/**
+ Wraps all calls to software ditherer and the different
+ resolutions,mmx enhancements, and doublesize ditherers.
+*/
+
+
+class Dither2YUV {
+
+ int lmmx;
+
+ int bpp;
+
+
+ public:
+ Dither2YUV();
+ ~Dither2YUV();
+
+ int getDitherSize();
+ void setDitherSize(int ditherSize);
+
+ void doDither(YUVPicture* pic,int depth,int ditherSize,
+ unsigned char* dest,int offset);
+
+
+ private:
+ void doDitherRGB_NORMAL(YUVPicture* pic,int depth,int ditherSize,
+ unsigned char* dest,int offset);
+
+ void doDither2YUV_std(YUVPicture* pic,int depth,
+ unsigned char* dest,int offset);
+
+};
+
+#endif
diff --git a/mpeglib/lib/util/render/dither2YUV/rgb2yuv16.cpp b/mpeglib/lib/util/render/dither2YUV/rgb2yuv16.cpp
new file mode 100644
index 00000000..e0d7fc86
--- /dev/null
+++ b/mpeglib/lib/util/render/dither2YUV/rgb2yuv16.cpp
@@ -0,0 +1,916 @@
+/***************************************************************************
+ rgb2yuv16.c - description
+ -------------------
+ begin : Tue Nov 2 2000
+ copyright : (C) 2000 by Christian Gerlach
+ email : cgerlach@rhrk.uni-kl.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "rgb2yuv16.h"
+#include <iostream>
+
+static unsigned short KEEPR[4] = { 63488, 63488, 63488, 63488 };
+unsigned short KEEPG[4] = { 2016, 2016, 2016, 2016 };
+unsigned short KEEPB[4] = { 31, 31, 31, 31 };
+
+short Y_RED[4] = { 307, 307, 307, 307 };
+short Y_GREEN[4] = { 302, 302, 302, 302 };
+short Y_BLUE[4] = { 117, 117, 117, 117 };
+
+short U_RED[4] = { -150, -150, -150, -150 };
+short U_GREEN[4] = { -147, -147, -147, -147 };
+short U_BLUE[4] = { 444, 444, 444, 444 };
+
+short V_RED[4] = { 632, 632, 632, 632 };
+short V_GREEN[4] = { -265, -265, -265, -265 };
+short V_BLUE[4] = { -102, -102, -102, -102 };
+
+
+// how to avoid these nasty compiler warinings?
+// heres one (maybe bad) method
+void dummyRGB2YUV16Bit() {
+
+ printf("%p\n",KEEPR);
+ printf("%p\n",KEEPG);
+ printf("%p\n",KEEPB);
+ printf("%p\n",Y_RED);
+ printf("%p\n",Y_GREEN);
+ printf("%p\n",Y_BLUE);
+ printf("%p\n",U_RED);
+ printf("%p\n",U_GREEN);
+ printf("%p\n",U_BLUE);
+ printf("%p\n",V_RED);
+ printf("%p\n",V_GREEN);
+ printf("%p\n",V_BLUE);
+}
+
+
+#ifndef INTEL
+void rgb2yuv16bit_mmx(unsigned char* ,unsigned char* ,unsigned char* ,
+ unsigned char* ,int , int ) {
+ std::cout << "RGB->YUV not compiled with INTEL" << std::endl;
+ exit(0);
+}
+
+void rgb2yuv16bit_mmx_fast(unsigned char* ,unsigned char* ,unsigned char* ,
+ unsigned char* ,int , int ) {
+ std::cout << "RGB->YUV not compiled with INTEL" << std::endl;
+ exit(0);
+}
+#endif
+
+
+void rgb2yuv16(unsigned char* rgbSource, unsigned char* dest)
+{
+ int rgb = *((unsigned short*) rgbSource++ );
+ int r = RED(rgb);
+ int g = GREEN(rgb);
+ int b = BLUE(rgb);
+
+ dest[0] = Y_RGB(r, g, b);
+ dest[1] = U_RGB(r, g, b);
+ dest[2] = V_RGB(r, g, b);
+}
+
+void rgb2yuv16bit(unsigned char* rgbSource,
+ unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,int height, int width) {
+
+ int height2 = height / 2;
+ int width2 = width / 2;
+ int r, g, b, row, col, rgb;
+
+ for (row=0 ; row<height2 ; row++) {
+ for (col=0 ; col<width2 ; col++) {
+ rgb = *((unsigned short*) rgbSource++ );
+ r = RED(rgb);
+ g = GREEN(rgb);
+ b = BLUE(rgb);
+
+ *lum++ = Y_RGB(r, g, b);
+ *cr++ = U_RGB(r, g, b);
+ *cb++ = V_RGB(r, g, b);
+
+ rgb = *((unsigned short*) rgbSource++ );
+ r = RED(rgb);
+ g = GREEN(rgb);
+ b = BLUE(rgb);
+
+ *lum++ = Y_RGB(r, g, b);
+ }
+ for (col=0 ; col<width ; col++) {
+ rgb = *((unsigned short*) rgbSource++ );
+ r = RED(rgb);
+ g = GREEN(rgb);
+ b = BLUE(rgb);
+
+ *lum++ = Y_RGB(r, g, b);
+ }
+ }
+}
+
+
+#ifdef INTEL
+
+void rgb2yuv16bit_mmx(unsigned char* rgbSource,
+ unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,int height, int width) {
+ int height2 = height / 2;
+ int width2 = width / 2;
+ int bytesPerLine = width * 2;
+
+ for (int row=0 ; row<height2 ; row++) {
+ rgb2yuv16bit_mmx422_row(rgbSource, lum, cr, cb, width);
+ rgbSource += bytesPerLine;
+ lum += width;
+ cr += width2;
+ cb += width2;
+
+ rgb2y16bit_mmx_row(rgbSource, lum, width);
+ rgbSource += bytesPerLine;
+ lum += width;
+ }
+}
+
+void rgb2yuv16bit_mmx_fast(unsigned char* rgbSource,
+ unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,int height, int width) {
+
+ int height2 = height / 2;
+ int width2 = width / 2;
+ int bytesPerLine = width * 2;
+
+ for (int row=0 ; row<height2 ; row++) {
+ rgb2yuv16bit_mmx422_row_fast(rgbSource, lum, cr, cb, width);
+ rgbSource += bytesPerLine;
+ lum += width;
+ cr += width2;
+ cb += width2;
+
+ rgb2y16bit_mmx_row_fast(rgbSource, lum, width);
+ rgbSource += bytesPerLine;
+ lum += width;
+ }
+}
+
+void rgb2yuv16bit_mmx422_row(unsigned char* rgb,
+ unsigned char* lum, unsigned char* cr,
+ unsigned char* cb, int pixel) {
+ unsigned int buf[17];
+
+ // 36%5 = TEMP0
+ // 44%5 = TEMPY
+ // 52%5 = TEMPU
+ // 60%5 = TEMPV
+
+ __asm__ __volatile__ (
+ "1:\n"
+
+ // unpack hicolor ( pixel 1 - 4)
+ "movq (%0), %%mm0\n"
+
+ "movq %%mm0, %%mm1\n"
+ "pand KEEPR, %%mm1\n"
+ "psrlq $8, %%mm1\n" // B3B2B1B0 -> mm1
+ "movq %%mm0, %%mm2\n"
+ "pand KEEPG, %%mm2\n"
+ "psrlq $3, %%mm2\n" // G3G2G1G0 -> mm2
+ "movq %%mm0, %%mm3\n"
+ "pand KEEPB, %%mm3\n"
+ "psllq $3, %%mm3\n" // G3G2G1G0 -> mm3
+
+ "movq %%mm2, %%mm0\n"
+ "punpcklbw %%mm1, %%mm2\n"
+ "punpckhbw %%mm1, %%mm0\n"
+
+ "pxor %%mm5, %%mm5\n"
+ "movq %%mm3, %%mm4\n"
+ "punpcklbw %%mm5, %%mm3\n"
+ "punpckhbw %%mm5, %%mm4\n"
+
+ "psllq $8, %%mm2\n"
+ "por %%mm2, %%mm3\n" // 0B1G1R10B0G0G0 -> mm3
+ "psllq $8, %%mm0\n"
+ "por %%mm0, %%mm4\n" // 0B3G3R30B2G2G2 -> mm4
+
+ "movq %%mm3, %5\n"
+ "movq %%mm4, 8%5\n"
+
+ // next 4 pixels ------------------------------
+
+ "movq 8(%0), %%mm0\n"
+
+ "movq %%mm0, %%mm1\n"
+ "pand KEEPR, %%mm1\n"
+ "psrlq $8, %%mm1\n" // B3B2B1B0 -> mm1
+ "movq %%mm0, %%mm2\n"
+ "pand KEEPG, %%mm2\n"
+ "psrlq $3, %%mm2\n" // G3G2G1G0 -> mm2
+ "movq %%mm0, %%mm3\n"
+ "pand KEEPB, %%mm3\n"
+ "psllq $3, %%mm3\n" // G3G2G1G0 -> mm3
+
+ "movq %%mm2, %%mm0\n"
+ "punpcklbw %%mm1, %%mm2\n"
+ "punpckhbw %%mm1, %%mm0\n"
+
+ "pxor %%mm5, %%mm5\n"
+ "movq %%mm3, %%mm4\n"
+ "punpcklbw %%mm5, %%mm3\n"
+ "punpckhbw %%mm5, %%mm4\n"
+
+ "psllq $8, %%mm2\n"
+ "por %%mm2, %%mm3\n" // 0B1G1R10B0G0G0 -> mm3
+ "psllq $8, %%mm0\n"
+ "por %%mm0, %%mm4\n" // 0B3G3R30B2G2G2 -> mm4
+
+ "movq %%mm3, 16%5\n"
+ "movq %%mm4, 24%5\n"
+
+ "add $16, %0\n"
+
+ // standard algorithm --------------------------------------------------
+
+ // pack rgb
+ // was: "movq (%0), %%mm1\n" // load G2R2B1G1R1B0G0R0
+ // ------------------------------
+ // (uses: mm0, mm1)
+ "movd 8%5, %%mm0\n"
+ "psllq $24, %%mm0\n"
+ "movd 4%5, %%mm1\n"
+ "por %%mm1, %%mm0\n"
+ "psllq $24, %%mm0\n"
+ "movd %5, %%mm1\n"
+ "por %%mm0, %%mm1\n"
+ // ------------------------------
+
+ "pxor %%mm6, %%mm6\n" // 0 -> mm6
+ "movq %%mm1, %%mm0\n" // G2R2B1G1R1B0G0R0 -> mm0
+ "psrlq $16, %%mm1\n" // 00G2R2B1G1R1B0 -> mm1
+ "punpcklbw ZEROSX, %%mm0\n" // R1B0G0R0 -> mm0
+ "movq %%mm1, %%mm7\n" // 00G2R2B1G1R1B0 -> mm7
+ "punpcklbw ZEROSX, %%mm1\n" // B1G1R1B0 -> mm1
+ "movq %%mm0, %%mm2\n" // R1B0G0R0 -> mm2
+ "pmaddwd YR0GRX, %%mm0\n" // yrR1,ygG0+yrR0 -> mm0
+
+ "movq %%mm1, %%mm3\n" // B1G1R1B0 -> mm3
+ "pmaddwd YBG0BX, %%mm1\n" // ybB1+ygG1,ybB0 -> mm1
+ "movq %%mm2, %%mm4\n" // R1B0G0R0 -> mm4
+ "pmaddwd UR0GRX, %%mm2\n" // urR1,ugG0+urR0 -> mm2
+ "movq %%mm3, %%mm5\n" // B1G1R1B0 -> mm5
+ "pmaddwd UBG0BX, %%mm3\n" // ubB1+ugG1,ubB0 -> mm3
+ "punpckhbw %%mm6, %%mm7\n" // 00G2R2 -> mm7
+ "pmaddwd VR0GRX, %%mm4\n" // vrR1,vgG0+vrR0 -> mm4
+ "paddd %%mm1, %%mm0\n" // Y1Y0 -> mm0
+
+ "pmaddwd VBG0BX, %%mm5\n" // vbB1+vgG1,vbB0 -> mm5
+
+ // pack rgb
+ // was: "movq 8(%0),%%mm1\n" // R5B4G4R4B3G3R3B2 -> mm1
+ // ------------------------------
+ // (uses: mm1, mm6)
+ "movd 20%5, %%mm1\n"
+ "psllq $24, %%mm1\n"
+ "movd 16%5, %%mm6\n"
+ "por %%mm6, %%mm1\n"
+ "psllq $24, %%mm1\n"
+ "movd 12%5, %%mm6\n"
+ "por %%mm6, %%mm1\n"
+ "psllq $8, %%mm1\n"
+ "movd 8%5, %%mm6\n"
+ "psrlq $16, %%mm6\n"
+ "por %%mm6, %%mm1\n"
+ // ------------------------------
+
+ "paddd %%mm3, %%mm2\n" // U1U0 -> mm2
+
+ "movq %%mm1, %%mm6\n" // R5B4G4R4B3G3R3B2 -> mm6
+ "punpcklbw ZEROSX, %%mm1\n" // B3G3R3B2 -> mm1
+ "paddd %%mm5, %%mm4\n" // V1V0 -> mm4
+
+ //----------------------------------------------------------------------
+
+ "movq %%mm1, %%mm5\n" // B3G3R3B2 -> mm5
+ "psllq $32, %%mm1\n" // R3B200 -> mm1
+
+ "paddd %%mm7, %%mm1\n" // R3B200+00G2R2=R3B2G2R2->mm1
+
+ "punpckhbw ZEROSX, %%mm6\n" // R5B4G4R3 -> mm6
+ "movq %%mm1, %%mm3\n" // R3B2G2R2 -> mm3
+
+ "pmaddwd YR0GRX, %%mm1\n" // yrR3,ygG2+yrR2 -> mm1
+ "movq %%mm5, %%mm7\n" // B3G3R3B2 -> mm7
+
+ "pmaddwd YBG0BX, %%mm5\n" // ybB3+ygG3,ybB2 -> mm5
+ "psrad $15, %%mm0\n" // 32-bit scaled Y1Y0 -> mm0
+
+ "movq %%mm6, 36%5\n" // R5B4G4R4 -> TEMP0
+ "movq %%mm3, %%mm6\n" // R3B2G2R2 -> mm6
+ "pmaddwd UR0GRX, %%mm6\n" // urR3,ugG2+urR2 -> mm6
+ "psrad $15, %%mm2\n" // 32-bit scaled U1U0 -> mm2
+
+ "paddd %%mm5, %%mm1\n" // Y3Y2 -> mm1
+ "movq %%mm7, %%mm5\n" // B3G3R3B2 -> mm5
+ "pmaddwd UBG0BX, %%mm7\n" // ubB3+ugG3,ubB2
+ "psrad $15, %%mm1\n" // 32-bit scaled Y3Y2 -> mm1
+
+ "pmaddwd VR0GRX, %%mm3\n" // vrR3,vgG2+vgR2
+ "packssdw %%mm1, %%mm0\n" // Y3Y2Y1Y0 -> mm0
+
+ "pmaddwd VBG0BX, %%mm5\n" // vbB3+vgG3,vbB2 -> mm5
+ "psrad $15, %%mm4\n" // 32-bit scaled V1V0 -> mm4
+
+ //----------------------------------------------------------------------
+
+ "paddd %%mm7, %%mm6\n" // U3U2 -> mm6
+
+ // pack rgb
+ // was: "movq 16(%0), %%mm1\n" // B7G7R7B6G6R6B5G5 -> mm1
+ // ------------------------------
+ // (uses: mm1, mm7)
+ "movd 28%5, %%mm1\n"
+ "psllq $24, %%mm1\n"
+ "movd 24%5, %%mm7\n"
+ "por %%mm7, %%mm1\n"
+ "psllq $16, %%mm1\n"
+ "movd 20%5, %%mm7\n"
+ "psrlq $8, %%mm7\n"
+ "por %%mm7, %%mm1\n"
+ // ------------------------------
+
+ "movq %%mm1, %%mm7\n" // B7G7R7B6G6R6B5G5 -> mm1
+ "psrad $15, %%mm6\n" // 32-bit scaled U3U2 -> mm6
+
+ "paddd %%mm5, %%mm3\n" // V3V2 -> mm3
+ "psllq $16, %%mm7\n" // R7B6G6R6B5G500 -> mm7
+
+ "movq %%mm7, %%mm5\n" // R7B6G6R6B5G500 -> mm5
+ "psrad $15, %%mm3\n" // 32-bit scaled V3V2 -> mm3
+
+ "movq %%mm0, 44%5\n" // 32-bit scaled Y3Y2Y1Y0 -> TEMPY
+
+ "packssdw %%mm6, %%mm2\n" // 32-bit scaled U3U2U1U0 -> mm2
+
+ "movq 36%5, %%mm0\n" // R5B4G4R4 -> mm0
+
+ "punpcklbw ZEROSX, %%mm7\n" // B5G500 -> mm7
+ "movq %%mm0, %%mm6\n" // R5B4G4R4 -> mm6
+
+ "movq %%mm2, 52%5\n" // 32-bit scaled U3U2U1U0 -> TEMPU
+ "psrlq $32, %%mm0\n" // 00R5B4 -> mm0
+
+ "paddw %%mm0, %%mm7\n" // B5G5R5B4 -> mm7
+ "movq %%mm6, %%mm2\n" // B5B4G4R4 -> mm2
+
+ "pmaddwd YR0GRX, %%mm2\n" // yrR5,ygG4+yrR4 -> mm2
+ "movq %%mm7, %%mm0\n" // B5G5R5B4 -> mm0
+
+ "pmaddwd YBG0BX, %%mm7\n" // ybB5+ygG5,ybB4 -> mm7
+ "packssdw %%mm3, %%mm4\n" // 32-bit scaled V3V2V1V0 -> mm4
+
+ //----------------------------------------------------------------------
+
+ "movq %%mm4, 60%5\n" // (V3V2V1V0)/256 -> mm4
+
+ "movq %%mm6, %%mm4\n" // B5B4G4R4 -> mm4
+
+ "pmaddwd UR0GRX, %%mm6\n" // urR5,ugG4+urR4
+ "movq %%mm0, %%mm3\n" // B5G5R5B4 -> mm0
+
+ "pmaddwd UBG0BX, %%mm0\n" // ubB5+ugG5,ubB4
+ "paddd %%mm7, %%mm2\n" // Y5Y4 -> mm2
+
+ //----------------------------------------------------------------------
+
+ "pmaddwd VR0GRX, %%mm4\n" // vrR5,vgG4+vrR4 -> mm4
+ "pxor %%mm7, %%mm7\n" // 0 -> mm7
+
+ "pmaddwd VBG0BX, %%mm3\n" // vbB5+vgG5,vbB4 -> mm3
+ "punpckhbw %%mm7, %%mm1\n" // B7G7R7B6 -> mm1
+
+ "paddd %%mm6, %%mm0\n" // U5U4 -> mm0
+ "movq %%mm1, %%mm6\n" // B7G7R7B6 -> mm6
+
+ "pmaddwd YBG0BX, %%mm6\n" // ybB7+ygG7,ybB6 -> mm6
+ "punpckhbw %%mm7, %%mm5\n" // R7B6G6R6 -> mm5
+
+ "movq %%mm5, %%mm7\n" // R7B6G6R6 -> mm7
+ "paddd %%mm4, %%mm3\n" // V5V4 -> mm3
+
+ "pmaddwd YR0GRX, %%mm5\n" // yrR7,ygG6+yrR6 -> mm5
+ "movq %%mm1, %%mm4\n" // B7G7R7B6 -> mm4
+
+ "pmaddwd UBG0BX, %%mm4\n" // ubB7+ugG7,ubB6 -> mm4
+ "psrad $15, %%mm0\n" // 32-bit scaled U5U4 -> %%mm0
+
+ //----------------------------------------------------------------------
+
+ "paddd OFFSETWX, %%mm0\n" // add offset to U5U4 -> mm0
+ "psrad $15, %%mm2\n" // 32-bit scaled Y5Y4 -> mm2
+
+ "paddd %%mm5, %%mm6\n" // Y7Y6 -> mm6
+ "movq %%mm7, %%mm5\n" // R7B6G6R6 -> mm5
+
+ "pmaddwd UR0GRX, %%mm7\n" // urR7,ugG6+ugR6 -> mm7
+ "psrad $15, %%mm3\n" // 32-bit scaled V5V4 -> mm3
+
+ "pmaddwd VBG0BX, %%mm1\n" // vbB7+vgG7,vbB6 -> mm1
+ "psrad $15, %%mm6\n" // 32-bit scaled Y7Y6 -> mm6
+
+ "paddd OFFSETDX, %%mm4\n" // add offset to U7U6
+ "packssdw %%mm6, %%mm2\n" // Y7Y6Y5Y4 -> mm2
+
+ "pmaddwd VR0GRX, %%mm5\n" // vrR7,vgG6+vrR6 -> mm5
+ "paddd %%mm4, %%mm7\n" // U7U6 -> mm7
+
+ "psrad $15, %%mm7\n" // 32-bit scaled U7U6 -> mm7
+
+ //----------------------------------------------------------------------
+
+ "movq 44%5, %%mm6\n" // 32-bit scaled Y3Y2Y1Y0 -> mm6
+ "packssdw %%mm7, %%mm0\n" // 32-bit scaled U7U6U5U4 -> mm0
+
+ "movq 52%5, %%mm4\n" // 32-bit scaled U3U2U1U0 -> mm4
+ "packuswb %%mm2, %%mm6\n" // all 8 Y values -> mm6
+
+ "movq OFFSETBX, %%mm7\n" // 128,128,128,128 -> mm7
+ "paddd %%mm5, %%mm1\n" // V7V6 -> mm1
+
+ "paddw %%mm7, %%mm4\n" // add offset to U3U2U1U0/256
+ "psrad $15, %%mm1\n" // 32-bit scaled V7V6 -> mm1
+
+ //----------------------------------------------------------------------
+
+ "movq %%mm6, (%1)\n" // store Y
+
+ "packuswb %%mm0, %%mm4\n" // all 8 U values -> mm4
+ "movq 60%5, %%mm5\n" // 32-bit scaled V3V2V1V0 -> mm5
+
+ "packssdw %%mm1, %%mm3\n" // V7V6V5V4 -> mm3
+ "paddw %%mm7, %%mm5\n" // add offset to V3V2V1V0
+ "paddw %%mm7, %%mm3\n" // add offset to V7V6V5V4
+
+ "packuswb %%mm3, %%mm5\n" // ALL 8 V values -> mm5
+
+ "movq CLEARX, %%mm2\n"
+ "pand %%mm2, %%mm4\n"
+ "pand %%mm2, %%mm5\n"
+
+ "packuswb %%mm5, %%mm4\n"
+
+ "movd %%mm4, (%2)\n"
+ "psrlq $32, %%mm4\n"
+ "movd %%mm4, (%3)\n"
+
+ "add $8, %1\n"
+ "add $4, %2\n"
+ "add $4, %3\n"
+
+ "sub $8, %4\n"
+ "jnz 1b\n"
+
+ "emms\n"
+
+ :
+ : "r" (rgb), "r" (lum), "r" (cr), "r" (cb),
+ "m" (pixel), "m" (buf[0])
+
+ );
+}
+
+void rgb2yuv16bit_mmx422_row_fast(unsigned char* rgb,
+ unsigned char* lum, unsigned char* cr,
+ unsigned char* cb, int pixel)
+{
+ __asm__ __volatile__ (
+ "1:\n"
+
+ // unpack hicolor ( pixel 0 - 3)
+ "movq (%0), %%mm0\n"
+
+ "movq %%mm0, %%mm1\n"
+ "pand KEEPR, %%mm1\n"
+ "psrlq $11, %%mm1\n" // B3B2B1B0 -> mm1
+
+ "movq %%mm0, %%mm2\n"
+ "pand KEEPG, %%mm2\n"
+ "psrlq $5, %%mm2\n" // G3G2G1G0 -> mm2
+
+ "movq %%mm0, %%mm3\n"
+ "pand KEEPB, %%mm3\n" // R3R2R1R0 -> mm3
+
+ // unpack hicolor ( pixel 4 - 7)
+ "movq 8(%0), %%mm0\n"
+
+ "movq %%mm0, %%mm4\n"
+ "pand KEEPR, %%mm4\n"
+ "psrlq $11, %%mm4\n" // B7B6B5B4 -> mm4
+
+ "movq %%mm0, %%mm5\n"
+ "pand KEEPG, %%mm5\n"
+ "psrlq $5, %%mm5\n" // G7G6G5G4 -> mm5
+
+ "movq %%mm0, %%mm6\n"
+ "pand KEEPB, %%mm6\n" // R7R6R5R4 -> mm6
+
+ // calculate Y
+ "movq %%mm6, %%mm7\n"
+ "pmullw Y_RED, %%mm7\n"
+
+ "movq %%mm5, %%mm0\n"
+ "pmullw Y_GREEN, %%mm0\n"
+ "paddw %%mm0, %%mm7\n"
+
+ "movq %%mm4, %%mm0\n"
+ "pmullw Y_BLUE, %%mm0\n"
+ "paddw %%mm0, %%mm7\n"
+
+ "psrlw $7, %%mm7\n" // Y3Y2Y1Y0 -> mm7
+
+ "pxor %%mm0, %%mm0\n"
+ "packuswb %%mm0, %%mm7\n"
+ "movd %%mm7, 4(%1)\n" // Y3Y2Y1Y0 -> lum
+
+ // --------
+
+ "movq %%mm3, %%mm7\n"
+ "pmullw Y_RED, %%mm7\n"
+
+ "movq %%mm2, %%mm0\n"
+ "pmullw Y_GREEN, %%mm0\n"
+ "paddw %%mm0, %%mm7\n"
+
+ "movq %%mm1, %%mm0\n"
+ "pmullw Y_BLUE, %%mm0\n"
+ "paddw %%mm0, %%mm7\n"
+
+ "psrlw $7, %%mm7\n" // Y7Y6Y5Y4 -> mm7
+
+ "pxor %%mm0, %%mm0\n"
+ "packuswb %%mm0, %%mm7\n"
+ "movd %%mm7, (%1)\n" // Y7Y6Y5Y4 -> lum
+ "add $8, %1\n"
+
+ // pack RGB
+ "packuswb %%mm4, %%mm1\n"
+ "pand CLEARX, %%mm1\n" // B6B4B2B0 -> mm1
+ "packuswb %%mm5, %%mm2\n"
+ "pand CLEARX, %%mm2\n" // GRG4G2G0 -> mm2
+ "packuswb %%mm6, %%mm3\n"
+ "pand CLEARX, %%mm3\n" // R6R4R2R0 -> mm3
+
+ // calculate U
+ "movq %%mm3, %%mm7\n"
+ "pmullw U_RED, %%mm7\n"
+
+ "movq %%mm2, %%mm0\n"
+ "pmullw U_GREEN, %%mm0\n"
+ "paddw %%mm0, %%mm7\n"
+
+ "movq %%mm1, %%mm0\n"
+ "pmullw U_BLUE, %%mm0\n"
+ "paddw %%mm0, %%mm7\n"
+
+ "psrlw $7, %%mm7\n" // U3U2U1U0 -> mm7
+ "paddw OFFSETBX,%%mm7\n"
+ "pand CLEARX, %%mm7\n"
+
+ "pxor %%mm0, %%mm0\n"
+ "packuswb %%mm0, %%mm7\n"
+ "movd %%mm7, (%2)\n" // U3U2U1U0 -> lum
+ "add $4, %2\n"
+
+ // calculate V
+ "movq %%mm3, %%mm7\n"
+ "pmullw V_RED, %%mm7\n"
+
+ "movq %%mm2, %%mm0\n"
+ "pmullw V_GREEN, %%mm0\n"
+ "paddw %%mm0, %%mm7\n"
+
+ "movq %%mm1, %%mm0\n"
+ "pmullw V_BLUE, %%mm0\n"
+ "paddw %%mm0, %%mm7\n"
+
+ "psrlw $7, %%mm7\n" // V3V2V1V0 -> mm7
+ "paddw OFFSETBX,%%mm7\n"
+ "pand CLEARX, %%mm7\n"
+
+ "pxor %%mm0, %%mm0\n"
+ "packuswb %%mm0, %%mm7\n"
+ "movd %%mm7, (%3)\n" // V3V2V1V0 -> lum
+ "add $4, %3\n"
+
+ "add $16, %0\n"
+
+ "sub $8, %4\n"
+ "jnz 1b\n"
+
+ "emms\n"
+
+ :
+ : "r" (rgb), "r" (lum), "r" (cr), "r" (cb), "m" (pixel)
+
+ );
+}
+
+void rgb2y16bit_mmx_row(unsigned char* rgbSource,
+ unsigned char* lum, int pixel)
+{
+ unsigned int buf[16];
+
+ // 36%3 = TEMP0
+ // 44%3 = TEMPY
+
+ __asm__ __volatile__ (
+ "1:\n"
+
+ // unpack hicolor ( pixel 1 - 4)
+ "movq (%0), %%mm0\n"
+
+ "movq %%mm0, %%mm1\n"
+ "pand KEEPR, %%mm1\n"
+ "psrlq $8, %%mm1\n" // B3B2B1B0 -> mm1
+ "movq %%mm0, %%mm2\n"
+ "pand KEEPG, %%mm2\n"
+ "psrlq $3, %%mm2\n" // G3G2G1G0 -> mm2
+ "movq %%mm0, %%mm3\n"
+ "pand KEEPB, %%mm3\n"
+ "psllq $3, %%mm3\n" // G3G2G1G0 -> mm3
+
+ "movq %%mm2, %%mm0\n"
+ "punpcklbw %%mm1, %%mm2\n"
+ "punpckhbw %%mm1, %%mm0\n"
+
+ "pxor %%mm5, %%mm5\n"
+ "movq %%mm3, %%mm4\n"
+ "punpcklbw %%mm5, %%mm3\n"
+ "punpckhbw %%mm5, %%mm4\n"
+
+ "psllq $8, %%mm2\n"
+ "por %%mm2, %%mm3\n" // 0B1G1R10B0G0G0 -> mm3
+ "psllq $8, %%mm0\n"
+ "por %%mm0, %%mm4\n" // 0B3G3R30B2G2G2 -> mm4
+
+ "movq %%mm3, %3\n"
+ "movq %%mm4, 8%3\n"
+
+ // next 4 pixels ------------------------------
+
+ "movq 8(%0), %%mm0\n"
+
+ "movq %%mm0, %%mm1\n"
+ "pand KEEPR, %%mm1\n"
+ "psrlq $8, %%mm1\n" // B3B2B1B0 -> mm1
+ "movq %%mm0, %%mm2\n"
+ "pand KEEPG, %%mm2\n"
+ "psrlq $3, %%mm2\n" // G3G2G1G0 -> mm2
+ "movq %%mm0, %%mm3\n"
+ "pand KEEPB, %%mm3\n"
+ "psllq $3, %%mm3\n" // G3G2G1G0 -> mm3
+
+ "movq %%mm2, %%mm0\n"
+ "punpcklbw %%mm1, %%mm2\n"
+ "punpckhbw %%mm1, %%mm0\n"
+
+ "pxor %%mm5, %%mm5\n"
+ "movq %%mm3, %%mm4\n"
+ "punpcklbw %%mm5, %%mm3\n"
+ "punpckhbw %%mm5, %%mm4\n"
+
+ "psllq $8, %%mm2\n"
+ "por %%mm2, %%mm3\n" // 0B1G1R10B0G0G0 -> mm3
+ "psllq $8, %%mm0\n"
+ "por %%mm0, %%mm4\n" // 0B3G3R30B2G2G2 -> mm4
+
+ "movq %%mm3, 16%3\n"
+ "movq %%mm4, 24%3\n"
+
+ "add $16, %0\n"
+
+ // standard algorithm --------------------------------------------------
+
+ // pack rgb
+ // was: "movq (%0), %%mm1\n" // load G2R2B1G1R1B0G0R0
+ // ------------------------------
+ // (uses: mm0, mm1)
+ "movd 8%3, %%mm0\n"
+ "psllq $24, %%mm0\n"
+ "movd 4%3, %%mm1\n"
+ "por %%mm1, %%mm0\n"
+ "psllq $24, %%mm0\n"
+ "movd %3, %%mm1\n"
+ "por %%mm0, %%mm1\n"
+ // ------------------------------
+
+ "pxor %%mm6, %%mm6\n" // 0 -> mm6
+ "movq %%mm1, %%mm0\n" // G2R2B1G1R1B0G0R0 -> mm0
+ "psrlq $16, %%mm1\n" // 00G2R2B1G1R1B0 -> mm1
+ "punpcklbw ZEROSX, %%mm0\n" // R1B0G0R0 -> mm0
+ "movq %%mm1, %%mm7\n" // 00G2R2B1G1R1B0 -> mm7
+ "punpcklbw ZEROSX, %%mm1\n" // B1G1R1B0 -> mm1
+ "movq %%mm0, %%mm2\n" // R1B0G0R0 -> mm2
+ "pmaddwd YR0GRX, %%mm0\n" // yrR1,ygG0+yrR0 -> mm0
+ "movq %%mm1, %%mm3\n" // B1G1R1B0 -> mm3
+ "pmaddwd YBG0BX, %%mm1\n" // ybB1+ygG1,ybB0 -> mm1
+ "movq %%mm2, %%mm4\n" // R1B0G0R0 -> mm4
+ "movq %%mm3, %%mm5\n" // B1G1R1B0 -> mm5
+ "punpckhbw %%mm6, %%mm7\n" // 00G2R2 -> mm7
+ "paddd %%mm1, %%mm0\n" // Y1Y0 -> mm0
+
+ // pack rgb
+ // was: "movq 8(%0),%%mm1\n" // R5B4G4R4B3G3R3B2 -> mm1
+ // ------------------------------
+ // (uses: mm1, mm6)
+ "movd 20%3, %%mm1\n"
+ "psllq $24, %%mm1\n"
+ "movd 16%3, %%mm6\n"
+ "por %%mm6, %%mm1\n"
+ "psllq $24, %%mm1\n"
+ "movd 12%3, %%mm6\n"
+ "por %%mm6, %%mm1\n"
+ "psllq $8, %%mm1\n"
+ "movd 8%3, %%mm6\n"
+ "psrlq $16, %%mm6\n"
+ "por %%mm6, %%mm1\n"
+ // ------------------------------
+
+ "movq %%mm1, %%mm6\n" // R5B4G4R4B3G3R3B2 -> mm6
+ "punpcklbw ZEROSX, %%mm1\n" // B3G3R3B2 -> mm1
+
+ //----------------------------------------------------------------------
+
+ "movq %%mm1, %%mm5\n" // B3G3R3B2 -> mm5
+ "psllq $32, %%mm1\n" // R3B200 -> mm1
+
+ "paddd %%mm7, %%mm1\n" // R3B200+00G2R2=R3B2G2R2->mm1
+
+ "punpckhbw ZEROSX, %%mm6\n" // R5B4G4R3 -> mm6
+ "movq %%mm1, %%mm3\n" // R3B2G2R2 -> mm3
+
+ "pmaddwd YR0GRX, %%mm1\n" // yrR3,ygG2+yrR2 -> mm1
+ "movq %%mm5, %%mm7\n" // B3G3R3B2 -> mm7
+
+ "pmaddwd YBG0BX, %%mm5\n" // ybB3+ygG3,ybB2 -> mm5
+ "psrad $15, %%mm0\n" // 32-bit scaled Y1Y0 -> mm0
+
+ "movq %%mm6, 36%3\n" // R5B4G4R4 -> TEMP0
+ "movq %%mm3, %%mm6\n" // R3B2G2R2 -> mm6
+
+ "paddd %%mm5, %%mm1\n" // Y3Y2 -> mm1
+ "movq %%mm7, %%mm5\n" // B3G3R3B2 -> mm5
+ "psrad $15, %%mm1\n" // 32-bit scaled Y3Y2 -> mm1
+
+ "packssdw %%mm1, %%mm0\n" // Y3Y2Y1Y0 -> mm0
+
+ //----------------------------------------------------------------------
+
+ // pack rgb
+ // was: "movq 16(%0), %%mm1\n" // B7G7R7B6G6R6B5G5 -> mm1
+ // ------------------------------
+ // (uses: mm1, mm7)
+ "movd 28%3, %%mm1\n"
+ "psllq $24, %%mm1\n"
+ "movd 24%3, %%mm7\n"
+ "por %%mm7, %%mm1\n"
+ "psllq $16, %%mm1\n"
+ "movd 20%3, %%mm7\n"
+ "psrlq $8, %%mm7\n"
+ "por %%mm7, %%mm1\n"
+ // ------------------------------
+
+ "movq %%mm1, %%mm7\n" // B7G7R7B6G6R6B5G5 -> mm1
+
+ "psllq $16, %%mm7\n" // R7B6G6R6B5G500 -> mm7
+
+ "movq %%mm7, %%mm5\n" // R7B6G6R6B5G500 -> mm5
+
+ "movq %%mm0, 44%3\n" // 32-bit scaled Y3Y2Y1Y0 -> TEMPY
+
+ "movq 36%3, %%mm0\n" // R5B4G4R4 -> mm0
+
+ "punpcklbw ZEROSX, %%mm7\n" // B5G500 -> mm7
+ "movq %%mm0, %%mm6\n" // R5B4G4R4 -> mm6
+
+ "psrlq $32, %%mm0\n" // 00R5B4 -> mm0
+
+ "paddw %%mm0, %%mm7\n" // B5G5R5B4 -> mm7
+ "movq %%mm6, %%mm2\n" // B5B4G4R4 -> mm2
+
+ "pmaddwd YR0GRX, %%mm2\n" // yrR5,ygG4+yrR4 -> mm2
+
+ "pmaddwd YBG0BX, %%mm7\n" // ybB5+ygG5,ybB4 -> mm7
+
+ //----------------------------------------------------------------------
+ "paddd %%mm7, %%mm2\n" // Y5Y4 -> mm2
+
+ //----------------------------------------------------------------------
+
+ "pxor %%mm7, %%mm7\n" // 0 -> mm7
+
+ "punpckhbw %%mm7, %%mm1\n" // B7G7R7B6 -> mm1
+
+ "movq %%mm1, %%mm6\n" // B7G7R7B6 -> mm6
+
+ "pmaddwd YBG0BX, %%mm6\n" // ybB7+ygG7,ybB6 -> mm6
+ "punpckhbw %%mm7, %%mm5\n" // R7B6G6R6 -> mm5
+
+ "pmaddwd YR0GRX, %%mm5\n" // yrR7,ygG6+yrR6 -> mm5
+
+ //----------------------------------------------------------------------
+
+ "psrad $15, %%mm2\n" // 32-bit scaled Y5Y4 -> mm2
+
+ "paddd %%mm5, %%mm6\n" // Y7Y6 -> mm6
+ "psrad $15, %%mm6\n" // 32-bit scaled Y7Y6 -> mm6
+
+ "packssdw %%mm6, %%mm2\n" // Y7Y6Y5Y4 -> mm2
+
+ //----------------------------------------------------------------------
+
+ "movq 44%3, %%mm6\n" // 32-bit scaled Y3Y2Y1Y0 -> mm6
+ "packuswb %%mm2, %%mm6\n" // all 8 Y values -> mm6
+
+ //----------------------------------------------------------------------
+
+ "movq %%mm6, (%1)\n" // store Y
+
+ "add $8, %1\n"
+
+ "sub $8, %2\n"
+ "jnz 1b\n"
+ "emms\n"
+
+ :
+ : "r" (rgbSource), "r" (lum), "m" (pixel), "m" (buf[0])
+
+ );
+}
+
+void rgb2y16bit_mmx_row_fast(unsigned char* rgb, unsigned char* lum, int pixel)
+{
+ __asm__ __volatile__ (
+ "1:\n"
+
+ // unpack hicolor ( pixel 1 - 4)
+ "movq (%0), %%mm0\n"
+
+ "movq %%mm0, %%mm1\n"
+ "pand KEEPR, %%mm1\n"
+ "psrlq $11, %%mm1\n" // B3B2B1B0 -> mm1
+
+ "movq %%mm0, %%mm2\n"
+ "pand KEEPG, %%mm2\n"
+ "psrlq $5, %%mm2\n" // G3G2G1G0 -> mm2
+ "movq %%mm0, %%mm3\n"
+ "pand KEEPB, %%mm3\n" // R3R2R1R0 -> mm3
+
+ // calculate Y
+ "movq %%mm3, %%mm4\n"
+ "pmullw Y_RED, %%mm4\n"
+
+ "movq %%mm2, %%mm5\n"
+ "pmullw Y_GREEN, %%mm5\n"
+ "paddw %%mm5, %%mm4\n"
+
+ "movq %%mm1, %%mm6\n"
+ "pmullw Y_BLUE, %%mm6\n"
+ "paddw %%mm6, %%mm4\n"
+
+ "psrlw $7, %%mm4\n" // Y3Y2Y1Y0 -> mm4
+
+ "pxor %%mm5, %%mm5\n"
+ "packuswb %%mm5, %%mm4\n"
+
+ "movd %%mm4, (%1)\n"
+ "add $4, %1\n"
+
+ "add $8, %0\n"
+
+ "sub $4, %2\n"
+ "jnz 1b\n"
+
+ "emms\n"
+
+ :
+ : "r" (rgb), "r" (lum), "m" (pixel)
+ );
+}
+
+
+#endif
+// INTEL
+
+
diff --git a/mpeglib/lib/util/render/dither2YUV/rgb2yuv16.h b/mpeglib/lib/util/render/dither2YUV/rgb2yuv16.h
new file mode 100644
index 00000000..7e4d6508
--- /dev/null
+++ b/mpeglib/lib/util/render/dither2YUV/rgb2yuv16.h
@@ -0,0 +1,74 @@
+/***************************************************************************
+ rgb2yuv16.h - description
+ -------------------
+ begin : Tue Nov 2 2000
+ copyright : (C) 2000 by Christian Gerlach
+ email : cgerlach@rhrk.uni-kl.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 __RGB2YUV16_H
+#define __RGB2YUV16_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "../yuvPicture.h"
+#include "rgb2yuvdefs.h"
+
+// slow C implementation
+void rgb2yuv16bit(unsigned char* rgbSource,
+ unsigned char* destLum,
+ unsigned char* destCr,
+ unsigned char* destCb,int height, int width);
+
+
+
+//
+// We compile with MMX if we are on INTEL arch
+// (this does not mean that we really support MMX,
+// this is a seperate/runtime check)
+//
+
+#ifdef INTEL
+
+void rgb2yuv16bit_mmx(unsigned char* rgbSource,
+ unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,int height, int width);
+
+void rgb2yuv16bit_mmx_fast(unsigned char* rgbSource,
+ unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,int height, int width);
+
+void rgb2yuv16bit_mmx422_row(unsigned char* rgb,
+ unsigned char* lum, unsigned char* cr,
+ unsigned char* cb, int pixel);
+
+void rgb2y16bit_mmx_row(unsigned char* rgbSource,
+ unsigned char* lum, int pixel);
+
+void rgb2yuv16bit_mmx422_row_fast(unsigned char* rgb,
+ unsigned char* lum, unsigned char* cr,
+ unsigned char* cb, int pixel);
+
+void rgb2y16bit_mmx_row_fast(unsigned char* rgb,
+ unsigned char* lum, int pixel);
+
+
+#endif
+// INTEL
+
+
+
+#endif
diff --git a/mpeglib/lib/util/render/dither2YUV/rgb2yuv32.cpp b/mpeglib/lib/util/render/dither2YUV/rgb2yuv32.cpp
new file mode 100644
index 00000000..3e246e25
--- /dev/null
+++ b/mpeglib/lib/util/render/dither2YUV/rgb2yuv32.cpp
@@ -0,0 +1,1143 @@
+/***************************************************************************
+ rgb2yuv32.cpp - description
+ -------------------
+ begin : Tue Nov 2 2000
+ copyright : (C) 2000 by Christian Gerlach
+ email : cgerlach@rhrk.uni-kl.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "rgb2yuv32.h"
+#include <iostream>
+
+void rgb2yuv32(unsigned char* rgb, unsigned char* dest)
+{
+ dest[0] = Y_RGB(rgb[0], rgb[1], rgb[2]);
+ dest[1] = U_RGB(rgb[0], rgb[1], rgb[2]);
+ dest[2] = V_RGB(rgb[0], rgb[1], rgb[2]);
+}
+
+void rgb2yuv24bit(unsigned char* rgbSource,
+ unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,int height, int width) {
+ int width2 = width / 2;
+ int height2 = height / 2;
+ int r, g, b, row, col;
+
+ for (row=0 ; row<height2 ; row++) {
+ for (col=0 ; col<width2 ; col++) {
+ r = *rgbSource++;
+ g = *rgbSource++;
+ b = *rgbSource++;
+
+ *lum++ = Y_RGB(r, g, b);
+ *cr++ = U_RGB(r, g, b);
+ *cb++ = V_RGB(r, g, b);
+
+ r = *rgbSource++;
+ g = *rgbSource++;
+ b = *rgbSource++;
+
+ *lum++ = Y_RGB(r, g, b);
+ }
+ for (col=0 ; col<width ; col++) {
+ r = *rgbSource++;
+ g = *rgbSource++;
+ b = *rgbSource++;
+
+ *lum++ = Y_RGB(r, g, b);
+ }
+ }
+}
+
+void rgb2yuv32bit(unsigned char* rgbSource,
+ unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,int height, int width) {
+
+ int width2 = width / 2;
+ int height2 = height / 2;
+ int r, g, b, row, col;
+
+ for (row=0 ; row<height2 ; row++) {
+ for (col=0 ; col<width2 ; col++) {
+ r = *rgbSource++;
+ g = *rgbSource++;
+ b = *rgbSource++;
+ rgbSource ++;
+
+ *lum++ = Y_RGB(r, g, b);
+ *cr++ = U_RGB(r, g, b);
+ *cb++ = V_RGB(r, g, b);
+
+ r = *rgbSource++;
+ g = *rgbSource++;
+ b = *rgbSource++;
+ rgbSource++;
+
+ *lum++ = Y_RGB(r, g, b);
+ }
+ for (col=0 ; col<width ; col++) {
+ r = *rgbSource++;
+ g = *rgbSource++;
+ b = *rgbSource++;
+ rgbSource ++;
+
+ *lum++ = Y_RGB(r, g, b);
+ }
+ }
+}
+
+#ifndef INTEL
+void rgb2yuv24bit_mmx(unsigned char* rgbSource,
+ unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,int height, int width) {
+ std::cout << "RGB->YUV render not compiled for INTEL"<<std::endl;
+ exit(0);
+}
+
+void rgb2yuv32bit_mmx(unsigned char* rgbSource,
+ unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,int height, int width) {
+ std::cout << "RGB->YUV render not compiled for INTEL"<<std::endl;
+ exit(0);
+}
+
+#endif
+
+#ifdef INTEL
+
+void rgb2yuv24bit_mmx(unsigned char* rgbSource,
+ unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,int height, int width) {
+ int width2 = width / 2;
+ int height2 = height / 2;
+ int row;
+ int bytesPerLine = width * 3;
+
+ for (row=0 ; row<height2 ; row++) {
+ rgb2yuv24bit_mmx422_row(rgbSource, lum, cr, cb, width);
+ rgbSource += bytesPerLine;
+ lum += width;
+ cr += width2;
+ cb += width2;
+
+ rgb2y24bit_mmx_row(rgbSource, lum, width);
+ rgbSource += bytesPerLine;
+ lum += width;
+ }
+}
+
+void rgb2yuv32bit_mmx(unsigned char* rgbSource,
+ unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,int height, int width) {
+
+
+ int width2 = width / 2;
+ int height2 = height / 2;
+ int bytesPerLine = width * 4;
+
+ for (int row=0 ; row<height2 ; row++) {
+ rgb2yuv32bit_mmx422_row(rgbSource, lum,cr, cb, width);
+ rgbSource += bytesPerLine;
+ lum += width;
+ cr += width2;
+ cb += width2;
+
+ rgb2y32bit_mmx_row(rgbSource, lum, width);
+ rgbSource += bytesPerLine;
+ lum += width;
+ }
+}
+
+void rgb2yuv24bit_mmx444_row(unsigned char* rgb, unsigned char* lum, unsigned char* cr,
+ unsigned char* cb, int pixel)
+{
+ unsigned int buf[8];
+
+ // %5 = TEMP0
+ // 8%5 = TEMPY
+ // 16%5 = TEMPU
+ // 24%5 = TEMPV
+
+ __asm__ __volatile__ (
+ "1:\n"
+
+ "movq (%0), %%mm1\n" // load G2R2B1G1R1B0G0R0
+ "pxor %%mm6, %%mm6\n" // 0 -> mm6
+ "movq %%mm1, %%mm0\n" // G2R2B1G1R1B0G0R0 -> mm0
+ "psrlq $16, %%mm1\n" // 00G2R2B1G1R1B0 -> mm1
+ "punpcklbw ZEROSX, %%mm0\n" // R1B0G0R0 -> mm0
+ "movq %%mm1, %%mm7\n" // 00G2R2B1G1R1B0 -> mm7
+ "punpcklbw ZEROSX, %%mm1\n" // B1G1R1B0 -> mm1
+ "movq %%mm0, %%mm2\n" // R1B0G0R0 -> mm2
+ "pmaddwd YR0GRX, %%mm0\n" // yrR1,ygG0+yrR0 -> mm0
+ "movq %%mm1, %%mm3\n" // B1G1R1B0 -> mm3
+ "pmaddwd YBG0BX, %%mm1\n" // ybB1+ygG1,ybB0 -> mm1
+ "movq %%mm2, %%mm4\n" // R1B0G0R0 -> mm4
+ "pmaddwd UR0GRX, %%mm2\n" // urR1,ugG0+urR0 -> mm2
+ "movq %%mm3, %%mm5\n" // B1G1R1B0 -> mm5
+ "pmaddwd UBG0BX, %%mm3\n" // ubB1+ugG1,ubB0 -> mm3
+ "punpckhbw %%mm6, %%mm7\n" // 00G2R2 -> mm7
+ "pmaddwd VR0GRX, %%mm4\n" // vrR1,vgG0+vrR0 -> mm4
+ "paddd %%mm1, %%mm0\n" // Y1Y0 -> mm0
+
+ "pmaddwd VBG0BX, %%mm5\n" // vbB1+vgG1,vbB0 -> mm5
+
+ "movq 8(%0),%%mm1\n" // load G2R2B1G1R1B0G0R0
+ "paddd %%mm3, %%mm2\n" // U1U0 -> mm2
+
+ "movq %%mm1, %%mm6\n" // R5B4G4R4B3G3R3B2 -> mm6
+ "punpcklbw ZEROSX, %%mm1\n" // B3G3R3B2 -> mm1
+ "paddd %%mm5, %%mm4\n" // V1V0 -> mm4
+
+ //----------------------------------------------------------------------
+
+ "movq %%mm1, %%mm5\n" // B3G3R3B2 -> mm5
+ "psllq $32, %%mm1\n" // R3B200 -> mm1
+
+ "paddd %%mm7, %%mm1\n" // R3B200+00G2R2=R3B2G2R2->mm1
+
+ "punpckhbw ZEROSX, %%mm6\n" // R5B4G4R3 -> mm6
+ "movq %%mm1, %%mm3\n" // R3B2G2R2 -> mm3
+
+ "pmaddwd YR0GRX, %%mm1\n" // yrR3,ygG2+yrR2 -> mm1
+ "movq %%mm5, %%mm7\n" // B3G3R3B2 -> mm7
+
+ "pmaddwd YBG0BX, %%mm5\n" // ybB3+ygG3,ybB2 -> mm5
+ "psrad $15, %%mm0\n" // 32-bit scaled Y1Y0 -> mm0
+
+ "movq %%mm6, %5\n" // R5B4G4R4 -> TEMP0
+ "movq %%mm3, %%mm6\n" // R3B2G2R2 -> mm6
+ "pmaddwd UR0GRX, %%mm6\n" // urR3,ugG2+urR2 -> mm6
+ "psrad $15, %%mm2\n" // 32-bit scaled U1U0 -> mm2
+
+ "paddd %%mm5, %%mm1\n" // Y3Y2 -> mm1
+ "movq %%mm7, %%mm5\n" // B3G3R3B2 -> mm5
+ "pmaddwd UBG0BX, %%mm7\n" // ubB3+ugG3,ubB2
+ "psrad $15, %%mm1\n" // 32-bit scaled Y3Y2 -> mm1
+
+ "pmaddwd VR0GRX, %%mm3\n" // vrR3,vgG2+vgR2
+ "packssdw %%mm1, %%mm0\n" // Y3Y2Y1Y0 -> mm0
+
+ "pmaddwd VBG0BX, %%mm5\n" // vbB3+vgG3,vbB2 -> mm5
+ "psrad $15, %%mm4\n" // 32-bit scaled V1V0 -> mm4
+
+ //----------------------------------------------------------------------
+
+ "movq 16(%0), %%mm1\n" // B7G7R7B6G6R6B5G5 -> mm7
+ "paddd %%mm7, %%mm6\n" // U3U2 -> mm6
+
+ "movq %%mm1, %%mm7\n" // B7G7R7B6G6R6B5G5 -> mm1
+ "psrad $15, %%mm6\n" // 32-bit scaled U3U2 -> mm6
+
+ "paddd %%mm5, %%mm3\n" // V3V2 -> mm3
+ "psllq $16, %%mm7\n" // R7B6G6R6B5G500 -> mm7
+
+ "movq %%mm7, %%mm5\n" // R7B6G6R6B5G500 -> mm5
+ "psrad $15, %%mm3\n" // 32-bit scaled V3V2 -> mm3
+
+ "movq %%mm0, 8%5\n" // 32-bit scaled Y3Y2Y1Y0 -> TEMPY
+ "packssdw %%mm6, %%mm2\n" // 32-bit scaled U3U2U1U0 -> mm2
+
+ "movq %5, %%mm0\n" // R5B4G4R4 -> mm0
+
+ "punpcklbw ZEROSX, %%mm7\n" // B5G500 -> mm7
+ "movq %%mm0, %%mm6\n" // R5B4G4R4 -> mm6
+
+ "movq %%mm2, 16%5\n" // 32-bit scaled U3U2U1U0 -> TEMPU
+ "psrlq $32, %%mm0\n" // 00R5B4 -> mm0
+
+ "paddw %%mm0, %%mm7\n" // B5G5R5B4 -> mm7
+ "movq %%mm6, %%mm2\n" // B5B4G4R4 -> mm2
+
+ "pmaddwd YR0GRX, %%mm2\n" // yrR5,ygG4+yrR4 -> mm2
+ "movq %%mm7, %%mm0\n" // B5G5R5B4 -> mm0
+
+ "pmaddwd YBG0BX, %%mm7\n" // ybB5+ygG5,ybB4 -> mm7
+ "packssdw %%mm3, %%mm4\n" // 32-bit scaled V3V2V1V0 -> mm4
+
+ //----------------------------------------------------------------------
+
+ "movq %%mm4, 24%5\n" // (V3V2V1V0)/256 -> mm4
+
+ "movq %%mm6, %%mm4\n" // B5B4G4R4 -> mm4
+
+ "pmaddwd UR0GRX, %%mm6\n" // urR5,ugG4+urR4
+ "movq %%mm0, %%mm3\n" // B5G5R5B4 -> mm0
+
+ "pmaddwd UBG0BX, %%mm0\n" // ubB5+ugG5,ubB4
+ "paddd %%mm7, %%mm2\n" // Y5Y4 -> mm2
+
+ //----------------------------------------------------------------------
+
+ "pmaddwd VR0GRX, %%mm4\n" // vrR5,vgG4+vrR4 -> mm4
+ "pxor %%mm7, %%mm7\n" // 0 -> mm7
+
+ "pmaddwd VBG0BX, %%mm3\n" // vbB5+vgG5,vbB4 -> mm3
+ "punpckhbw %%mm7, %%mm1\n" // B7G7R7B6 -> mm1
+
+ "paddd %%mm6, %%mm0\n" // U5U4 -> mm0
+ "movq %%mm1, %%mm6\n" // B7G7R7B6 -> mm6
+
+ "pmaddwd YBG0BX, %%mm6\n" // ybB7+ygG7,ybB6 -> mm6
+ "punpckhbw %%mm7, %%mm5\n" // R7B6G6R6 -> mm5
+
+ "movq %%mm5, %%mm7\n" // R7B6G6R6 -> mm7
+ "paddd %%mm4, %%mm3\n" // V5V4 -> mm3
+
+ "pmaddwd YR0GRX, %%mm5\n" // yrR7,ygG6+yrR6 -> mm5
+ "movq %%mm1, %%mm4\n" // B7G7R7B6 -> mm4
+
+ "pmaddwd UBG0BX, %%mm4\n" // ubB7+ugG7,ubB6 -> mm4
+ "psrad $15, %%mm0\n" // 32-bit scaled U5U4 -> %%mm0
+
+ //----------------------------------------------------------------------
+
+ "paddd OFFSETWX, %%mm0\n" // add offset to U5U4 -> mm0
+ "psrad $15, %%mm2\n" // 32-bit scaled Y5Y4 -> mm2
+
+ "paddd %%mm5, %%mm6\n" // Y7Y6 -> mm6
+ "movq %%mm7, %%mm5\n" // R7B6G6R6 -> mm5
+
+ "pmaddwd UR0GRX, %%mm7\n" // urR7,ugG6+ugR6 -> mm7
+ "psrad $15, %%mm3\n" // 32-bit scaled V5V4 -> mm3
+
+ "pmaddwd VBG0BX, %%mm1\n" // vbB7+vgG7,vbB6 -> mm1
+ "psrad $15, %%mm6\n" // 32-bit scaled Y7Y6 -> mm6
+
+ "paddd OFFSETDX, %%mm4\n" // add offset to U7U6
+ "packssdw %%mm6, %%mm2\n" // Y7Y6Y5Y4 -> mm2
+
+ "pmaddwd VR0GRX, %%mm5\n" // vrR7,vgG6+vrR6 -> mm5
+ "paddd %%mm4, %%mm7\n" // U7U6 -> mm7
+
+ "psrad $15, %%mm7\n" // 32-bit scaled U7U6 -> mm7
+
+ //----------------------------------------------------------------------
+
+ "movq 8%5, %%mm6\n" // 32-bit scaled Y3Y2Y1Y0 -> mm6
+ "packssdw %%mm7, %%mm0\n" // 32-bit scaled U7U6U5U4 -> mm0
+
+ "movq 16%5, %%mm4\n" // 32-bit scaled U3U2U1U0 -> mm4
+ "packuswb %%mm2, %%mm6\n" // all 8 Y values -> mm6
+
+ "movq OFFSETBX, %%mm7\n" // 128,128,128,128 -> mm7
+ "paddd %%mm5, %%mm1\n" // V7V6 -> mm1
+
+ "paddw %%mm7, %%mm4\n" // add offset to U3U2U1U0/256
+ "psrad $15, %%mm1\n" // 32-bit scaled V7V6 -> mm1
+
+ //----------------------------------------------------------------------
+
+ "movq %%mm6, (%1)\n" // store Y
+ "packuswb %%mm0, %%mm4\n" // all 8 U values -> mm4
+
+ "movq 24%5, %%mm5\n" // 32-bit scaled V3V2V1V0 -> mm5
+ "packssdw %%mm1, %%mm3\n" // V7V6V5V4 -> mm3
+ "paddw %%mm7, %%mm5\n" // add offset to V3V2V1V0
+ "paddw %%mm7, %%mm3\n" // add offset to V7V6V5V4
+
+ "movq %%mm4, (%2)\n" // store U
+ "packuswb %%mm3, %%mm5\n" // ALL 8 V values -> mm5
+
+ "movq %%mm5, (%3)\n" // store V
+
+ "sub $8, %4\n"
+ "jnz 1b\n"
+ "emms\n"
+
+ :
+ : "r" (rgb), "r" (lum), "r" (cr), "r" (cb), "m" (pixel), "m" (buf[0])
+ );
+}
+
+void rgb2yuv24bit_mmx422_row(unsigned char* rgb, unsigned char* lum, unsigned char* cr,
+ unsigned char* cb, int pixel)
+{
+ unsigned int buf[8];
+
+ // %5 = TEMP0
+ // 8%5 = TEMPY
+ // 16%5 = TEMPU
+ // 24%5 = TEMPV
+
+ __asm__ __volatile__ (
+ "1:\n"
+
+ "movq (%0), %%mm1\n" // load G2R2B1G1R1B0G0R0
+ "pxor %%mm6, %%mm6\n" // 0 -> mm6
+ "movq %%mm1, %%mm0\n" // G2R2B1G1R1B0G0R0 -> mm0
+ "psrlq $16, %%mm1\n" // 00G2R2B1G1R1B0 -> mm1
+ "punpcklbw ZEROSX, %%mm0\n" // R1B0G0R0 -> mm0
+ "movq %%mm1, %%mm7\n" // 00G2R2B1G1R1B0 -> mm7
+ "punpcklbw ZEROSX, %%mm1\n" // B1G1R1B0 -> mm1
+ "movq %%mm0, %%mm2\n" // R1B0G0R0 -> mm2
+ "pmaddwd YR0GRX, %%mm0\n" // yrR1,ygG0+yrR0 -> mm0
+
+ "movq %%mm1, %%mm3\n" // B1G1R1B0 -> mm3
+ "pmaddwd YBG0BX, %%mm1\n" // ybB1+ygG1,ybB0 -> mm1
+ "movq %%mm2, %%mm4\n" // R1B0G0R0 -> mm4
+ "pmaddwd UR0GRX, %%mm2\n" // urR1,ugG0+urR0 -> mm2
+ "movq %%mm3, %%mm5\n" // B1G1R1B0 -> mm5
+ "pmaddwd UBG0BX, %%mm3\n" // ubB1+ugG1,ubB0 -> mm3
+ "punpckhbw %%mm6, %%mm7\n" // 00G2R2 -> mm7
+ "pmaddwd VR0GRX, %%mm4\n" // vrR1,vgG0+vrR0 -> mm4
+ "paddd %%mm1, %%mm0\n" // Y1Y0 -> mm0
+
+ "pmaddwd VBG0BX, %%mm5\n" // vbB1+vgG1,vbB0 -> mm5
+
+ "movq 8(%0),%%mm1\n" // load G2R2B1G1R1B0G0R0
+ "paddd %%mm3, %%mm2\n" // U1U0 -> mm2
+
+ "movq %%mm1, %%mm6\n" // R5B4G4R4B3G3R3B2 -> mm6
+ "punpcklbw ZEROSX, %%mm1\n" // B3G3R3B2 -> mm1
+ "paddd %%mm5, %%mm4\n" // V1V0 -> mm4
+
+ //----------------------------------------------------------------------
+
+ "movq %%mm1, %%mm5\n" // B3G3R3B2 -> mm5
+ "psllq $32, %%mm1\n" // R3B200 -> mm1
+
+ "paddd %%mm7, %%mm1\n" // R3B200+00G2R2=R3B2G2R2->mm1
+
+ "punpckhbw ZEROSX, %%mm6\n" // R5B4G4R3 -> mm6
+ "movq %%mm1, %%mm3\n" // R3B2G2R2 -> mm3
+
+ "pmaddwd YR0GRX, %%mm1\n" // yrR3,ygG2+yrR2 -> mm1
+ "movq %%mm5, %%mm7\n" // B3G3R3B2 -> mm7
+
+ "pmaddwd YBG0BX, %%mm5\n" // ybB3+ygG3,ybB2 -> mm5
+ "psrad $15, %%mm0\n" // 32-bit scaled Y1Y0 -> mm0
+
+ "movq %%mm6, %5\n" // R5B4G4R4 -> TEMP0
+ "movq %%mm3, %%mm6\n" // R3B2G2R2 -> mm6
+ "pmaddwd UR0GRX, %%mm6\n" // urR3,ugG2+urR2 -> mm6
+ "psrad $15, %%mm2\n" // 32-bit scaled U1U0 -> mm2
+
+ "paddd %%mm5, %%mm1\n" // Y3Y2 -> mm1
+ "movq %%mm7, %%mm5\n" // B3G3R3B2 -> mm5
+ "pmaddwd UBG0BX, %%mm7\n" // ubB3+ugG3,ubB2
+ "psrad $15, %%mm1\n" // 32-bit scaled Y3Y2 -> mm1
+
+ "pmaddwd VR0GRX, %%mm3\n" // vrR3,vgG2+vgR2
+ "packssdw %%mm1, %%mm0\n" // Y3Y2Y1Y0 -> mm0
+
+ "pmaddwd VBG0BX, %%mm5\n" // vbB3+vgG3,vbB2 -> mm5
+ "psrad $15, %%mm4\n" // 32-bit scaled V1V0 -> mm4
+
+ //----------------------------------------------------------------------
+
+ "movq 16(%0), %%mm1\n" // B7G7R7B6G6R6B5G5 -> mm7
+ "paddd %%mm7, %%mm6\n" // U3U2 -> mm6
+
+ "movq %%mm1, %%mm7\n" // B7G7R7B6G6R6B5G5 -> mm1
+ "psrad $15, %%mm6\n" // 32-bit scaled U3U2 -> mm6
+
+ "paddd %%mm5, %%mm3\n" // V3V2 -> mm3
+ "psllq $16, %%mm7\n" // R7B6G6R6B5G500 -> mm7
+
+ "movq %%mm7, %%mm5\n" // R7B6G6R6B5G500 -> mm5
+ "psrad $15, %%mm3\n" // 32-bit scaled V3V2 -> mm3
+
+ "movq %%mm0, 8%5\n" // 32-bit scaled Y3Y2Y1Y0 -> TEMPY
+ "packssdw %%mm6, %%mm2\n" // 32-bit scaled U3U2U1U0 -> mm2
+
+ "movq %5, %%mm0\n" // R5B4G4R4 -> mm0
+
+ "punpcklbw ZEROSX, %%mm7\n" // B5G500 -> mm7
+ "movq %%mm0, %%mm6\n" // R5B4G4R4 -> mm6
+
+ "movq %%mm2, 16%5\n" // 32-bit scaled U3U2U1U0 -> TEMPU
+ "psrlq $32, %%mm0\n" // 00R5B4 -> mm0
+
+ "paddw %%mm0, %%mm7\n" // B5G5R5B4 -> mm7
+ "movq %%mm6, %%mm2\n" // B5B4G4R4 -> mm2
+
+ "pmaddwd YR0GRX, %%mm2\n" // yrR5,ygG4+yrR4 -> mm2
+ "movq %%mm7, %%mm0\n" // B5G5R5B4 -> mm0
+
+ "pmaddwd YBG0BX, %%mm7\n" // ybB5+ygG5,ybB4 -> mm7
+ "packssdw %%mm3, %%mm4\n" // 32-bit scaled V3V2V1V0 -> mm4
+
+ //----------------------------------------------------------------------
+
+ "movq %%mm4, 24%5\n" // (V3V2V1V0)/256 -> mm4
+
+ "movq %%mm6, %%mm4\n" // B5B4G4R4 -> mm4
+
+ "pmaddwd UR0GRX, %%mm6\n" // urR5,ugG4+urR4
+ "movq %%mm0, %%mm3\n" // B5G5R5B4 -> mm0
+
+ "pmaddwd UBG0BX, %%mm0\n" // ubB5+ugG5,ubB4
+ "paddd %%mm7, %%mm2\n" // Y5Y4 -> mm2
+
+ //----------------------------------------------------------------------
+
+ "pmaddwd VR0GRX, %%mm4\n" // vrR5,vgG4+vrR4 -> mm4
+ "pxor %%mm7, %%mm7\n" // 0 -> mm7
+
+ "pmaddwd VBG0BX, %%mm3\n" // vbB5+vgG5,vbB4 -> mm3
+ "punpckhbw %%mm7, %%mm1\n" // B7G7R7B6 -> mm1
+
+ "paddd %%mm6, %%mm0\n" // U5U4 -> mm0
+ "movq %%mm1, %%mm6\n" // B7G7R7B6 -> mm6
+
+ "pmaddwd YBG0BX, %%mm6\n" // ybB7+ygG7,ybB6 -> mm6
+ "punpckhbw %%mm7, %%mm5\n" // R7B6G6R6 -> mm5
+
+ "movq %%mm5, %%mm7\n" // R7B6G6R6 -> mm7
+ "paddd %%mm4, %%mm3\n" // V5V4 -> mm3
+
+ "pmaddwd YR0GRX, %%mm5\n" // yrR7,ygG6+yrR6 -> mm5
+ "movq %%mm1, %%mm4\n" // B7G7R7B6 -> mm4
+
+ "pmaddwd UBG0BX, %%mm4\n" // ubB7+ugG7,ubB6 -> mm4
+ "psrad $15, %%mm0\n" // 32-bit scaled U5U4 -> %%mm0
+
+ //----------------------------------------------------------------------
+
+ "paddd OFFSETWX, %%mm0\n" // add offset to U5U4 -> mm0
+ "psrad $15, %%mm2\n" // 32-bit scaled Y5Y4 -> mm2
+
+ "paddd %%mm5, %%mm6\n" // Y7Y6 -> mm6
+ "movq %%mm7, %%mm5\n" // R7B6G6R6 -> mm5
+
+ "pmaddwd UR0GRX, %%mm7\n" // urR7,ugG6+ugR6 -> mm7
+ "psrad $15, %%mm3\n" // 32-bit scaled V5V4 -> mm3
+
+ "pmaddwd VBG0BX, %%mm1\n" // vbB7+vgG7,vbB6 -> mm1
+ "psrad $15, %%mm6\n" // 32-bit scaled Y7Y6 -> mm6
+
+ "paddd OFFSETDX, %%mm4\n" // add offset to U7U6
+ "packssdw %%mm6, %%mm2\n" // Y7Y6Y5Y4 -> mm2
+
+ "pmaddwd VR0GRX, %%mm5\n" // vrR7,vgG6+vrR6 -> mm5
+ "paddd %%mm4, %%mm7\n" // U7U6 -> mm7
+
+ "psrad $15, %%mm7\n" // 32-bit scaled U7U6 -> mm7
+
+ //----------------------------------------------------------------------
+
+ "movq 8%5, %%mm6\n" // 32-bit scaled Y3Y2Y1Y0 -> mm6
+ "packssdw %%mm7, %%mm0\n" // 32-bit scaled U7U6U5U4 -> mm0
+
+ "movq 16%5, %%mm4\n" // 32-bit scaled U3U2U1U0 -> mm4
+ "packuswb %%mm2, %%mm6\n" // all 8 Y values -> mm6
+
+ "movq OFFSETBX, %%mm7\n" // 128,128,128,128 -> mm7
+ "paddd %%mm5, %%mm1\n" // V7V6 -> mm1
+
+ "paddw %%mm7, %%mm4\n" // add offset to U3U2U1U0/256
+ "psrad $15, %%mm1\n" // 32-bit scaled V7V6 -> mm1
+
+ //----------------------------------------------------------------------
+
+ "movq %%mm6, (%1)\n" // store Y
+ "packuswb %%mm0, %%mm4\n" // all 8 U values -> mm4
+
+ "movq 24%5, %%mm5\n" // 32-bit scaled V3V2V1V0 -> mm5
+ "packssdw %%mm1, %%mm3\n" // V7V6V5V4 -> mm3
+ "paddw %%mm7, %%mm5\n" // add offset to V3V2V1V0
+ "paddw %%mm7, %%mm3\n" // add offset to V7V6V5V4
+
+ "packuswb %%mm3, %%mm5\n" // ALL 8 V values -> mm5
+
+ // pack U and V
+ "movq CLEARX, %%mm2\n"
+ "pand %%mm2, %%mm4\n"
+ "pand %%mm2, %%mm5\n"
+
+ "packuswb %%mm5, %%mm4\n"
+
+ "movd %%mm4, (%2)\n"
+ "psrlq $32, %%mm4\n"
+ "movd %%mm4, (%3)\n"
+
+ "add $24, %0\n"
+ "add $8, %1\n"
+ "add $4, %2\n"
+ "add $4, %3\n"
+
+ "sub $8, %4\n"
+ "jnz 1b\n"
+ "emms\n"
+
+ :
+ : "r" (rgb), "r" (lum), "r" (cr), "r" (cb), "m" (pixel), "m" (buf[0])
+ );
+}
+
+void rgb2yuv32bit_mmx422_row(unsigned char* rgb, unsigned char* lum, unsigned char* cr,
+ unsigned char* cb, int pixel)
+{
+ unsigned int buf[8];
+
+ // %5 = TEMP0
+ // 8%5 = TEMPY
+ // 16%5 = TEMPU
+ // 24%5 = TEMPV
+
+ __asm__ __volatile__ (
+ "1:\n"
+
+ // pack rgb
+ // was: "movq (%0), %%mm1\n" // load G2R2B1G1R1B0G0R0
+ // ------------------------------
+ // (uses: mm0, mm1)
+ "movd 8(%0), %%mm0\n"
+ "psllq $24, %%mm0\n"
+ "movd 4(%0), %%mm1\n"
+ "por %%mm1, %%mm0\n"
+ "psllq $24, %%mm0\n"
+ "movd (%0), %%mm1\n"
+ "por %%mm0, %%mm1\n"
+ // ------------------------------
+
+ "pxor %%mm6, %%mm6\n" // 0 -> mm6
+ "movq %%mm1, %%mm0\n" // G2R2B1G1R1B0G0R0 -> mm0
+ "psrlq $16, %%mm1\n" // 00G2R2B1G1R1B0 -> mm1
+ "punpcklbw ZEROSX, %%mm0\n" // R1B0G0R0 -> mm0
+ "movq %%mm1, %%mm7\n" // 00G2R2B1G1R1B0 -> mm7
+ "punpcklbw ZEROSX, %%mm1\n" // B1G1R1B0 -> mm1
+ "movq %%mm0, %%mm2\n" // R1B0G0R0 -> mm2
+ "pmaddwd YR0GRX, %%mm0\n" // yrR1,ygG0+yrR0 -> mm0
+
+ "movq %%mm1, %%mm3\n" // B1G1R1B0 -> mm3
+ "pmaddwd YBG0BX, %%mm1\n" // ybB1+ygG1,ybB0 -> mm1
+ "movq %%mm2, %%mm4\n" // R1B0G0R0 -> mm4
+ "pmaddwd UR0GRX, %%mm2\n" // urR1,ugG0+urR0 -> mm2
+ "movq %%mm3, %%mm5\n" // B1G1R1B0 -> mm5
+ "pmaddwd UBG0BX, %%mm3\n" // ubB1+ugG1,ubB0 -> mm3
+ "punpckhbw %%mm6, %%mm7\n" // 00G2R2 -> mm7
+ "pmaddwd VR0GRX, %%mm4\n" // vrR1,vgG0+vrR0 -> mm4
+ "paddd %%mm1, %%mm0\n" // Y1Y0 -> mm0
+
+ "pmaddwd VBG0BX, %%mm5\n" // vbB1+vgG1,vbB0 -> mm5
+
+ // pack rgb
+ // was: "movq 8(%0),%%mm1\n" // R5B4G4R4B3G3R3B2 -> mm1
+ // ------------------------------
+ // (uses: mm1, mm6)
+ "movd 20(%0), %%mm1\n"
+ "psllq $24, %%mm1\n"
+ "movd 16(%0), %%mm6\n"
+ "por %%mm6, %%mm1\n"
+ "psllq $24, %%mm1\n"
+ "movd 12(%0), %%mm6\n"
+ "por %%mm6, %%mm1\n"
+ "psllq $8, %%mm1\n"
+ "movd 8(%0), %%mm6\n"
+ "psrlq $16, %%mm6\n"
+ "por %%mm6, %%mm1\n"
+ // ------------------------------
+
+ "paddd %%mm3, %%mm2\n" // U1U0 -> mm2
+
+ "movq %%mm1, %%mm6\n" // R5B4G4R4B3G3R3B2 -> mm6
+ "punpcklbw ZEROSX, %%mm1\n" // B3G3R3B2 -> mm1
+ "paddd %%mm5, %%mm4\n" // V1V0 -> mm4
+
+ //----------------------------------------------------------------------
+
+ "movq %%mm1, %%mm5\n" // B3G3R3B2 -> mm5
+ "psllq $32, %%mm1\n" // R3B200 -> mm1
+
+ "paddd %%mm7, %%mm1\n" // R3B200+00G2R2=R3B2G2R2->mm1
+
+ "punpckhbw ZEROSX, %%mm6\n" // R5B4G4R3 -> mm6
+ "movq %%mm1, %%mm3\n" // R3B2G2R2 -> mm3
+
+ "pmaddwd YR0GRX, %%mm1\n" // yrR3,ygG2+yrR2 -> mm1
+ "movq %%mm5, %%mm7\n" // B3G3R3B2 -> mm7
+
+ "pmaddwd YBG0BX, %%mm5\n" // ybB3+ygG3,ybB2 -> mm5
+ "psrad $15, %%mm0\n" // 32-bit scaled Y1Y0 -> mm0
+
+ "movq %%mm6, %5\n" // R5B4G4R4 -> TEMP0
+ "movq %%mm3, %%mm6\n" // R3B2G2R2 -> mm6
+ "pmaddwd UR0GRX, %%mm6\n" // urR3,ugG2+urR2 -> mm6
+ "psrad $15, %%mm2\n" // 32-bit scaled U1U0 -> mm2
+
+ "paddd %%mm5, %%mm1\n" // Y3Y2 -> mm1
+ "movq %%mm7, %%mm5\n" // B3G3R3B2 -> mm5
+ "pmaddwd UBG0BX, %%mm7\n" // ubB3+ugG3,ubB2
+ "psrad $15, %%mm1\n" // 32-bit scaled Y3Y2 -> mm1
+
+ "pmaddwd VR0GRX, %%mm3\n" // vrR3,vgG2+vgR2
+ "packssdw %%mm1, %%mm0\n" // Y3Y2Y1Y0 -> mm0
+
+ "pmaddwd VBG0BX, %%mm5\n" // vbB3+vgG3,vbB2 -> mm5
+ "psrad $15, %%mm4\n" // 32-bit scaled V1V0 -> mm4
+
+ //----------------------------------------------------------------------
+
+ "paddd %%mm7, %%mm6\n" // U3U2 -> mm6
+
+ // pack rgb
+ // was: "movq 16(%0), %%mm1\n" // B7G7R7B6G6R6B5G5 -> mm1
+ // ------------------------------
+ // (uses: mm1, mm7)
+ "movd 28(%0), %%mm1\n"
+ "psllq $24, %%mm1\n"
+ "movd 24(%0), %%mm7\n"
+ "por %%mm7, %%mm1\n"
+ "psllq $16, %%mm1\n"
+ "movd 20(%0), %%mm7\n"
+ "psrlq $8, %%mm7\n"
+ "por %%mm7, %%mm1\n"
+ // ------------------------------
+
+ "movq %%mm1, %%mm7\n" // B7G7R7B6G6R6B5G5 -> mm1
+ "psrad $15, %%mm6\n" // 32-bit scaled U3U2 -> mm6
+
+ "paddd %%mm5, %%mm3\n" // V3V2 -> mm3
+ "psllq $16, %%mm7\n" // R7B6G6R6B5G500 -> mm7
+
+ "movq %%mm7, %%mm5\n" // R7B6G6R6B5G500 -> mm5
+ "psrad $15, %%mm3\n" // 32-bit scaled V3V2 -> mm3
+
+ "movq %%mm0, 8%5\n" // 32-bit scaled Y3Y2Y1Y0 -> TEMPY
+
+ "packssdw %%mm6, %%mm2\n" // 32-bit scaled U3U2U1U0 -> mm2
+
+ "movq %5, %%mm0\n" // R5B4G4R4 -> mm0
+
+ "punpcklbw ZEROSX, %%mm7\n" // B5G500 -> mm7
+ "movq %%mm0, %%mm6\n" // R5B4G4R4 -> mm6
+
+ "movq %%mm2, 16%5\n" // 32-bit scaled U3U2U1U0 -> TEMPU
+ "psrlq $32, %%mm0\n" // 00R5B4 -> mm0
+
+ "paddw %%mm0, %%mm7\n" // B5G5R5B4 -> mm7
+ "movq %%mm6, %%mm2\n" // B5B4G4R4 -> mm2
+
+ "pmaddwd YR0GRX, %%mm2\n" // yrR5,ygG4+yrR4 -> mm2
+ "movq %%mm7, %%mm0\n" // B5G5R5B4 -> mm0
+
+ "pmaddwd YBG0BX, %%mm7\n" // ybB5+ygG5,ybB4 -> mm7
+ "packssdw %%mm3, %%mm4\n" // 32-bit scaled V3V2V1V0 -> mm4
+
+ //----------------------------------------------------------------------
+
+ "movq %%mm4, 24%5\n" // (V3V2V1V0)/256 -> mm4
+
+ "movq %%mm6, %%mm4\n" // B5B4G4R4 -> mm4
+
+ "pmaddwd UR0GRX, %%mm6\n" // urR5,ugG4+urR4
+ "movq %%mm0, %%mm3\n" // B5G5R5B4 -> mm0
+
+ "pmaddwd UBG0BX, %%mm0\n" // ubB5+ugG5,ubB4
+ "paddd %%mm7, %%mm2\n" // Y5Y4 -> mm2
+
+ //----------------------------------------------------------------------
+
+ "pmaddwd VR0GRX, %%mm4\n" // vrR5,vgG4+vrR4 -> mm4
+ "pxor %%mm7, %%mm7\n" // 0 -> mm7
+
+ "pmaddwd VBG0BX, %%mm3\n" // vbB5+vgG5,vbB4 -> mm3
+ "punpckhbw %%mm7, %%mm1\n" // B7G7R7B6 -> mm1
+
+ "paddd %%mm6, %%mm0\n" // U5U4 -> mm0
+ "movq %%mm1, %%mm6\n" // B7G7R7B6 -> mm6
+
+ "pmaddwd YBG0BX, %%mm6\n" // ybB7+ygG7,ybB6 -> mm6
+ "punpckhbw %%mm7, %%mm5\n" // R7B6G6R6 -> mm5
+
+ "movq %%mm5, %%mm7\n" // R7B6G6R6 -> mm7
+ "paddd %%mm4, %%mm3\n" // V5V4 -> mm3
+
+ "pmaddwd YR0GRX, %%mm5\n" // yrR7,ygG6+yrR6 -> mm5
+ "movq %%mm1, %%mm4\n" // B7G7R7B6 -> mm4
+
+ "pmaddwd UBG0BX, %%mm4\n" // ubB7+ugG7,ubB6 -> mm4
+ "psrad $15, %%mm0\n" // 32-bit scaled U5U4 -> %%mm0
+
+ //----------------------------------------------------------------------
+
+ "paddd OFFSETWX, %%mm0\n" // add offset to U5U4 -> mm0
+ "psrad $15, %%mm2\n" // 32-bit scaled Y5Y4 -> mm2
+
+ "paddd %%mm5, %%mm6\n" // Y7Y6 -> mm6
+ "movq %%mm7, %%mm5\n" // R7B6G6R6 -> mm5
+
+ "pmaddwd UR0GRX, %%mm7\n" // urR7,ugG6+ugR6 -> mm7
+ "psrad $15, %%mm3\n" // 32-bit scaled V5V4 -> mm3
+
+ "pmaddwd VBG0BX, %%mm1\n" // vbB7+vgG7,vbB6 -> mm1
+ "psrad $15, %%mm6\n" // 32-bit scaled Y7Y6 -> mm6
+
+ "paddd OFFSETDX, %%mm4\n" // add offset to U7U6
+ "packssdw %%mm6, %%mm2\n" // Y7Y6Y5Y4 -> mm2
+
+ "pmaddwd VR0GRX, %%mm5\n" // vrR7,vgG6+vrR6 -> mm5
+ "paddd %%mm4, %%mm7\n" // U7U6 -> mm7
+
+ "psrad $15, %%mm7\n" // 32-bit scaled U7U6 -> mm7
+
+ //----------------------------------------------------------------------
+
+ "movq 8%5, %%mm6\n" // 32-bit scaled Y3Y2Y1Y0 -> mm6
+ "packssdw %%mm7, %%mm0\n" // 32-bit scaled U7U6U5U4 -> mm0
+
+ "movq 16%5, %%mm4\n" // 32-bit scaled U3U2U1U0 -> mm4
+ "packuswb %%mm2, %%mm6\n" // all 8 Y values -> mm6
+
+ "movq OFFSETBX, %%mm7\n" // 128,128,128,128 -> mm7
+ "paddd %%mm5, %%mm1\n" // V7V6 -> mm1
+
+ "paddw %%mm7, %%mm4\n" // add offset to U3U2U1U0/256
+ "psrad $15, %%mm1\n" // 32-bit scaled V7V6 -> mm1
+
+ //----------------------------------------------------------------------
+
+ "movq %%mm6, (%1)\n" // store Y
+
+ "packuswb %%mm0, %%mm4\n" // all 8 U values -> mm4
+ "movq 24%5, %%mm5\n" // 32-bit scaled V3V2V1V0 -> mm5
+
+ "packssdw %%mm1, %%mm3\n" // V7V6V5V4 -> mm3
+ "paddw %%mm7, %%mm5\n" // add offset to V3V2V1V0
+ "paddw %%mm7, %%mm3\n" // add offset to V7V6V5V4
+
+ "packuswb %%mm3, %%mm5\n" // ALL 8 V values -> mm5
+
+ "movq CLEARX, %%mm2\n"
+ "pand %%mm2, %%mm4\n"
+ "pand %%mm2, %%mm5\n"
+
+ "packuswb %%mm5, %%mm4\n"
+
+ "movd %%mm4, (%2)\n"
+ "psrlq $32, %%mm4\n"
+ "movd %%mm4, (%3)\n"
+
+ "add $32, %0\n"
+ "add $8, %1\n"
+ "add $4, %2\n"
+ "add $4, %3\n"
+
+ "sub $8, %4\n"
+ "jnz 1b\n"
+
+ "emms\n"
+
+ :
+ : "r" (rgb), "r" (lum), "r" (cr), "r" (cb), "m" (pixel), "m" (buf[0])
+
+ );
+}
+
+void rgb2y24bit_mmx_row(unsigned char* rgbSource, unsigned char* lum, int pixel)
+{
+ unsigned int buf[4];
+
+ // %3 = TEMP0
+ // 8%3 = TEMPY
+
+ __asm__ __volatile__ (
+ "1:\n"
+
+ "movq (%0), %%mm1\n" // load G2R2B1G1R1B0G0R0
+ "pxor %%mm6, %%mm6\n" // 0 -> mm6
+ "movq %%mm1, %%mm0\n" // G2R2B1G1R1B0G0R0 -> mm0
+ "psrlq $16, %%mm1\n" // 00G2R2B1G1R1B0 -> mm1
+ "punpcklbw ZEROSX, %%mm0\n" // R1B0G0R0 -> mm0
+ "movq %%mm1, %%mm7\n" // 00G2R2B1G1R1B0 -> mm7
+ "punpcklbw ZEROSX, %%mm1\n" // B1G1R1B0 -> mm1
+ "movq %%mm0, %%mm2\n" // R1B0G0R0 -> mm2
+ "pmaddwd YR0GRX, %%mm0\n" // yrR1,ygG0+yrR0 -> mm0
+ "movq %%mm1, %%mm3\n" // B1G1R1B0 -> mm3
+ "pmaddwd YBG0BX, %%mm1\n" // ybB1+ygG1,ybB0 -> mm1
+ "movq %%mm2, %%mm4\n" // R1B0G0R0 -> mm4
+ "movq %%mm3, %%mm5\n" // B1G1R1B0 -> mm5
+ "punpckhbw %%mm6, %%mm7\n" // 00G2R2 -> mm7
+ "paddd %%mm1, %%mm0\n" // Y1Y0 -> mm0
+
+ "movq 8(%0),%%mm1\n" // load G2R2B1G1R1B0G0R0
+
+ "movq %%mm1, %%mm6\n" // R5B4G4R4B3G3R3B2 -> mm6
+ "punpcklbw ZEROSX, %%mm1\n" // B3G3R3B2 -> mm1
+
+ //----------------------------------------------------------------------
+
+ "movq %%mm1, %%mm5\n" // B3G3R3B2 -> mm5
+ "psllq $32, %%mm1\n" // R3B200 -> mm1
+
+ "paddd %%mm7, %%mm1\n" // R3B200+00G2R2=R3B2G2R2->mm1
+
+ "punpckhbw ZEROSX, %%mm6\n" // R5B4G4R3 -> mm6
+ "movq %%mm1, %%mm3\n" // R3B2G2R2 -> mm3
+
+ "pmaddwd YR0GRX, %%mm1\n" // yrR3,ygG2+yrR2 -> mm1
+ "movq %%mm5, %%mm7\n" // B3G3R3B2 -> mm7
+
+ "pmaddwd YBG0BX, %%mm5\n" // ybB3+ygG3,ybB2 -> mm5
+ "psrad $15, %%mm0\n" // 32-bit scaled Y1Y0 -> mm0
+
+ "movq %%mm6, %3\n" // R5B4G4R4 -> TEMP0
+ "movq %%mm3, %%mm6\n" // R3B2G2R2 -> mm6
+
+ "paddd %%mm5, %%mm1\n" // Y3Y2 -> mm1
+ "movq %%mm7, %%mm5\n" // B3G3R3B2 -> mm5
+ "psrad $15, %%mm1\n" // 32-bit scaled Y3Y2 -> mm1
+
+ "packssdw %%mm1, %%mm0\n" // Y3Y2Y1Y0 -> mm0
+
+ //----------------------------------------------------------------------
+
+ "movq 16(%0), %%mm1\n" // B7G7R7B6G6R6B5G5 -> mm7
+
+ "movq %%mm1, %%mm7\n" // B7G7R7B6G6R6B5G5 -> mm1
+
+ "psllq $16, %%mm7\n" // R7B6G6R6B5G500 -> mm7
+
+ "movq %%mm7, %%mm5\n" // R7B6G6R6B5G500 -> mm5
+
+ "movq %%mm0, 8%3\n" // 32-bit scaled Y3Y2Y1Y0 -> TEMPY
+
+ "movq %3, %%mm0\n" // R5B4G4R4 -> mm0
+
+ "punpcklbw ZEROSX, %%mm7\n" // B5G500 -> mm7
+ "movq %%mm0, %%mm6\n" // R5B4G4R4 -> mm6
+
+ "psrlq $32, %%mm0\n" // 00R5B4 -> mm0
+
+ "paddw %%mm0, %%mm7\n" // B5G5R5B4 -> mm7
+ "movq %%mm6, %%mm2\n" // B5B4G4R4 -> mm2
+
+ "pmaddwd YR0GRX, %%mm2\n" // yrR5,ygG4+yrR4 -> mm2
+
+ "pmaddwd YBG0BX, %%mm7\n" // ybB5+ygG5,ybB4 -> mm7
+
+ //----------------------------------------------------------------------
+ "paddd %%mm7, %%mm2\n" // Y5Y4 -> mm2
+
+ //----------------------------------------------------------------------
+
+ "pxor %%mm7, %%mm7\n" // 0 -> mm7
+
+ "punpckhbw %%mm7, %%mm1\n" // B7G7R7B6 -> mm1
+
+ "movq %%mm1, %%mm6\n" // B7G7R7B6 -> mm6
+
+ "pmaddwd YBG0BX, %%mm6\n" // ybB7+ygG7,ybB6 -> mm6
+ "punpckhbw %%mm7, %%mm5\n" // R7B6G6R6 -> mm5
+
+ "pmaddwd YR0GRX, %%mm5\n" // yrR7,ygG6+yrR6 -> mm5
+
+ //----------------------------------------------------------------------
+
+ "psrad $15, %%mm2\n" // 32-bit scaled Y5Y4 -> mm2
+
+ "paddd %%mm5, %%mm6\n" // Y7Y6 -> mm6
+ "psrad $15, %%mm6\n" // 32-bit scaled Y7Y6 -> mm6
+
+ "packssdw %%mm6, %%mm2\n" // Y7Y6Y5Y4 -> mm2
+
+ //----------------------------------------------------------------------
+
+ "movq 8%3, %%mm6\n" // 32-bit scaled Y3Y2Y1Y0 -> mm6
+ "packuswb %%mm2, %%mm6\n" // all 8 Y values -> mm6
+
+ //----------------------------------------------------------------------
+
+ "movq %%mm6, (%1)\n" // store Y
+
+ "add $24, %0\n"
+ "add $8, %1\n"
+
+ "sub $8, %2\n"
+ "jnz 1b\n"
+ "emms\n"
+
+ :
+ : "r" (rgbSource), "r" (lum), "m" (pixel), "m" (buf[0])
+
+ );
+}
+
+void rgb2y32bit_mmx_row(unsigned char* rgbSource, unsigned char* lum, int pixel)
+{
+ unsigned int buf[4];
+
+ // %3 = TEMP0
+ // 8%3 = TEMPY
+
+ __asm__ __volatile__ (
+ "1:\n"
+
+ // pack rgb
+ // was: "movq (%0), %%mm1\n" // load G2R2B1G1R1B0G0R0
+ // ------------------------------
+ // (uses: mm0, mm1)
+ "movd 8(%0), %%mm0\n"
+ "psllq $24, %%mm0\n"
+ "movd 4(%0), %%mm1\n"
+ "por %%mm1, %%mm0\n"
+ "psllq $24, %%mm0\n"
+ "movd (%0), %%mm1\n"
+ "por %%mm0, %%mm1\n"
+ // ------------------------------
+
+ "pxor %%mm6, %%mm6\n" // 0 -> mm6
+ "movq %%mm1, %%mm0\n" // G2R2B1G1R1B0G0R0 -> mm0
+ "psrlq $16, %%mm1\n" // 00G2R2B1G1R1B0 -> mm1
+ "punpcklbw ZEROSX, %%mm0\n" // R1B0G0R0 -> mm0
+ "movq %%mm1, %%mm7\n" // 00G2R2B1G1R1B0 -> mm7
+ "punpcklbw ZEROSX, %%mm1\n" // B1G1R1B0 -> mm1
+ "movq %%mm0, %%mm2\n" // R1B0G0R0 -> mm2
+ "pmaddwd YR0GRX, %%mm0\n" // yrR1,ygG0+yrR0 -> mm0
+ "movq %%mm1, %%mm3\n" // B1G1R1B0 -> mm3
+ "pmaddwd YBG0BX, %%mm1\n" // ybB1+ygG1,ybB0 -> mm1
+ "movq %%mm2, %%mm4\n" // R1B0G0R0 -> mm4
+ "movq %%mm3, %%mm5\n" // B1G1R1B0 -> mm5
+ "punpckhbw %%mm6, %%mm7\n" // 00G2R2 -> mm7
+ "paddd %%mm1, %%mm0\n" // Y1Y0 -> mm0
+
+ // pack rgb
+ // was: "movq 8(%0),%%mm1\n" // R5B4G4R4B3G3R3B2 -> mm1
+ // ------------------------------
+ // (uses: mm1, mm6)
+ "movd 20(%0), %%mm1\n"
+ "psllq $24, %%mm1\n"
+ "movd 16(%0), %%mm6\n"
+ "por %%mm6, %%mm1\n"
+ "psllq $24, %%mm1\n"
+ "movd 12(%0), %%mm6\n"
+ "por %%mm6, %%mm1\n"
+ "psllq $8, %%mm1\n"
+ "movd 8(%0), %%mm6\n"
+ "psrlq $16, %%mm6\n"
+ "por %%mm6, %%mm1\n"
+ // ------------------------------
+
+ "movq %%mm1, %%mm6\n" // R5B4G4R4B3G3R3B2 -> mm6
+ "punpcklbw ZEROSX, %%mm1\n" // B3G3R3B2 -> mm1
+
+ //----------------------------------------------------------------------
+
+ "movq %%mm1, %%mm5\n" // B3G3R3B2 -> mm5
+ "psllq $32, %%mm1\n" // R3B200 -> mm1
+
+ "paddd %%mm7, %%mm1\n" // R3B200+00G2R2=R3B2G2R2->mm1
+
+ "punpckhbw ZEROSX, %%mm6\n" // R5B4G4R3 -> mm6
+ "movq %%mm1, %%mm3\n" // R3B2G2R2 -> mm3
+
+ "pmaddwd YR0GRX, %%mm1\n" // yrR3,ygG2+yrR2 -> mm1
+ "movq %%mm5, %%mm7\n" // B3G3R3B2 -> mm7
+
+ "pmaddwd YBG0BX, %%mm5\n" // ybB3+ygG3,ybB2 -> mm5
+ "psrad $15, %%mm0\n" // 32-bit scaled Y1Y0 -> mm0
+
+ "movq %%mm6, %3\n" // R5B4G4R4 -> TEMP0
+ "movq %%mm3, %%mm6\n" // R3B2G2R2 -> mm6
+
+ "paddd %%mm5, %%mm1\n" // Y3Y2 -> mm1
+ "movq %%mm7, %%mm5\n" // B3G3R3B2 -> mm5
+ "psrad $15, %%mm1\n" // 32-bit scaled Y3Y2 -> mm1
+
+ "packssdw %%mm1, %%mm0\n" // Y3Y2Y1Y0 -> mm0
+
+ //----------------------------------------------------------------------
+
+ // pack rgb
+ // was: "movq 16(%0), %%mm1\n" // B7G7R7B6G6R6B5G5 -> mm1
+ // ------------------------------
+ // (uses: mm1, mm7)
+ "movd 28(%0), %%mm1\n"
+ "psllq $24, %%mm1\n"
+ "movd 24(%0), %%mm7\n"
+ "por %%mm7, %%mm1\n"
+ "psllq $16, %%mm1\n"
+ "movd 20(%0), %%mm7\n"
+ "psrlq $8, %%mm7\n"
+ "por %%mm7, %%mm1\n"
+ // ------------------------------
+
+ "movq %%mm1, %%mm7\n" // B7G7R7B6G6R6B5G5 -> mm1
+
+ "psllq $16, %%mm7\n" // R7B6G6R6B5G500 -> mm7
+
+ "movq %%mm7, %%mm5\n" // R7B6G6R6B5G500 -> mm5
+
+ "movq %%mm0, 8%3\n" // 32-bit scaled Y3Y2Y1Y0 -> TEMPY
+
+ "movq %3, %%mm0\n" // R5B4G4R4 -> mm0
+
+ "punpcklbw ZEROSX, %%mm7\n" // B5G500 -> mm7
+ "movq %%mm0, %%mm6\n" // R5B4G4R4 -> mm6
+
+ "psrlq $32, %%mm0\n" // 00R5B4 -> mm0
+
+ "paddw %%mm0, %%mm7\n" // B5G5R5B4 -> mm7
+ "movq %%mm6, %%mm2\n" // B5B4G4R4 -> mm2
+
+ "pmaddwd YR0GRX, %%mm2\n" // yrR5,ygG4+yrR4 -> mm2
+
+ "pmaddwd YBG0BX, %%mm7\n" // ybB5+ygG5,ybB4 -> mm7
+
+ //----------------------------------------------------------------------
+ "paddd %%mm7, %%mm2\n" // Y5Y4 -> mm2
+
+ //----------------------------------------------------------------------
+
+ "pxor %%mm7, %%mm7\n" // 0 -> mm7
+
+ "punpckhbw %%mm7, %%mm1\n" // B7G7R7B6 -> mm1
+
+ "movq %%mm1, %%mm6\n" // B7G7R7B6 -> mm6
+
+ "pmaddwd YBG0BX, %%mm6\n" // ybB7+ygG7,ybB6 -> mm6
+ "punpckhbw %%mm7, %%mm5\n" // R7B6G6R6 -> mm5
+
+ "pmaddwd YR0GRX, %%mm5\n" // yrR7,ygG6+yrR6 -> mm5
+
+ //----------------------------------------------------------------------
+
+ "psrad $15, %%mm2\n" // 32-bit scaled Y5Y4 -> mm2
+
+ "paddd %%mm5, %%mm6\n" // Y7Y6 -> mm6
+ "psrad $15, %%mm6\n" // 32-bit scaled Y7Y6 -> mm6
+
+ "packssdw %%mm6, %%mm2\n" // Y7Y6Y5Y4 -> mm2
+
+ //----------------------------------------------------------------------
+
+ "movq 8%3, %%mm6\n" // 32-bit scaled Y3Y2Y1Y0 -> mm6
+ "packuswb %%mm2, %%mm6\n" // all 8 Y values -> mm6
+
+ //----------------------------------------------------------------------
+
+ "movq %%mm6, (%1)\n" // store Y
+
+ "add $32, %0\n"
+ "add $8, %1\n"
+
+ "sub $8, %2\n"
+ "jnz 1b\n"
+ "emms\n"
+
+ :
+ : "r" (rgbSource), "r" (lum), "r" (pixel), "m" (buf[0])
+
+ );
+}
+
+#endif
+// INTEL
+
diff --git a/mpeglib/lib/util/render/dither2YUV/rgb2yuv32.h b/mpeglib/lib/util/render/dither2YUV/rgb2yuv32.h
new file mode 100644
index 00000000..75fea27f
--- /dev/null
+++ b/mpeglib/lib/util/render/dither2YUV/rgb2yuv32.h
@@ -0,0 +1,93 @@
+/***************************************************************************
+ rgb2yuv32.h - description
+ -------------------
+ begin : Tue Nov 2 2000
+ copyright : (C) 2000 by Christian Gerlach
+ email : cgerlach@rhrk.uni-kl.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 _RGB2YUV32_H_
+#define _RGB2YUV32_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "../yuvPicture.h"
+#include "rgb2yuvdefs.h"
+
+void rgb2yuv32(unsigned char* rgb, unsigned char* dest);
+
+// slow C rountines
+void rgb2yuv24bit(unsigned char* rgbSource,
+ unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,int height, int width);
+
+void rgb2yuv32bit(unsigned char* rgbSource,
+ unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,int height, int width);
+
+
+
+
+#ifndef INTEL
+void rgb2yuv24bit_mmx(unsigned char* rgbSource,
+ unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,int height, int width);
+
+void rgb2yuv32bit_mmx(unsigned char* rgbSource,
+ unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,int height, int width);
+#endif
+
+
+#ifdef INTEL
+
+void rgb2yuv24bit_mmx(unsigned char* rgbSource,
+ unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,int height, int width);
+
+void rgb2yuv32bit_mmx(unsigned char* rgbSource,
+ unsigned char* lum,
+ unsigned char* cr,
+ unsigned char* cb,int height, int width);
+
+
+void rgb2yuv24bit_mmx444_row(unsigned char* rgb,
+ unsigned char* lum, unsigned char* cr,
+ unsigned char* cb, int pixel);
+
+
+void rgb2yuv24bit_mmx422_row(unsigned char* rgb,
+ unsigned char* lum, unsigned char* cr,
+ unsigned char* cb, int pixel);
+
+void rgb2yuv32bit_mmx422_row(unsigned char* rgb,
+ unsigned char* lum, unsigned char* cr,
+ unsigned char* cb, int pixel);
+
+void rgb2y24bit_mmx_row(unsigned char* rgbSource,
+ unsigned char* lum, int pixel);
+
+void rgb2y32bit_mmx_row(unsigned char* rgbSource,
+ unsigned char* lum, int pixel);
+
+#endif
+//INTEL
+
+
+#endif
diff --git a/mpeglib/lib/util/render/dither2YUV/rgb2yuvdefs.h b/mpeglib/lib/util/render/dither2YUV/rgb2yuvdefs.h
new file mode 100644
index 00000000..5c7ae574
--- /dev/null
+++ b/mpeglib/lib/util/render/dither2YUV/rgb2yuvdefs.h
@@ -0,0 +1,74 @@
+/***************************************************************************
+ rgb2yuvdefs.h - description
+ -------------------
+ begin : Tue Nov 2 2000
+ copyright : (C) 2000 by Christian Gerlach
+ email : cgerlach@rhrk.uni-kl.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 __RGB2YUVDEFS_H
+#define __RGB2YUVDEFS_H
+
+/* gcc 3.3.1 and later optimise the "not used" (only in asm code)
+ symbols away. So we need to mark them as used. */
+#if defined(__GNUC_PREREQ__) && !defined(__GNUC_PREREQ)
+#define __GNUC_PREREQ __GNUC_PREREQ__
+#endif
+#ifdef __GNUC_PREREQ
+#if __GNUC_PREREQ (3,1)
+# define __attribute_used__ __attribute__ ((__used__))
+#else
+# define __attribute_used__
+#endif
+#else
+# define __attribute_used__
+#endif
+
+// hicolor mode (16 bit) with r(5) g(6) b(5) bits (reverse order b, g, r)
+#define RED(rgb) (unsigned char) ((rgb) << 3)
+#define GREEN(rgb) (((rgb) & 0x7e0) >> 3)
+#define BLUE(rgb) (((rgb) & 0xf800) >> 8)
+
+#define YUV_SHIFT 15
+#define YUV_HALF (1<<(YUV_SHIFT-1))
+#define YUV_ONE (1<<YUV_SHIFT)
+#define Y_R ((int)( 0.299 * YUV_ONE ))
+#define Y_G ((int)( 0.587 * YUV_ONE ))
+#define Y_B ((int)( 0.114 * YUV_ONE ))
+#define U_R ((int)(-0.146 * YUV_ONE ))
+#define U_G ((int)(-0.288 * YUV_ONE ))
+#define U_B ((int)( 0.434 * YUV_ONE ))
+#define V_R ((int)( 0.617 * YUV_ONE ))
+#define V_G ((int)(-0.517 * YUV_ONE ))
+#define V_B ((int)(-0.100 * YUV_ONE ))
+
+#define Y_RGB(R,G,B) (( Y_R * (R) + Y_G * (G) + Y_B * (B)) >> YUV_SHIFT)
+#define U_RGB(R,G,B) ((( U_R * (R) + U_G * (G) + U_B * (B)) >> YUV_SHIFT) + 128)
+#define V_RGB(R,G,B) ((( V_R * (R) + V_G * (G) + V_B * (B)) >> YUV_SHIFT) + 128)
+
+static unsigned char __attribute_used__ CLEARX[8] = { 255, 0, 255, 0, 255, 0, 255, 0 };
+static short __attribute_used__ ZEROSX[4] = { 0, 0, 0, 0 };
+
+static short __attribute_used__ OFFSETDX[4] = { 0, 64, 0, 64 };
+static short __attribute_used__ OFFSETWX[4] = { 128, 0, 128, 0 };
+static short __attribute_used__ OFFSETBX[4] = { 128, 128, 128, 128 };
+
+static short __attribute_used__ YR0GRX[4] = { Y_R, Y_G, 0, Y_R };
+static short __attribute_used__ YBG0BX[4] = { Y_B, 0, Y_G, Y_B };
+
+static short __attribute_used__ UR0GRX[4] = { U_R, U_G, 0, U_R };
+static short __attribute_used__ UBG0BX[4] = { U_B, 0, U_G, U_B };
+
+static short __attribute_used__ VR0GRX[4] = { V_R, V_G, 0, V_R };
+static short __attribute_used__ VBG0BX[4] = { V_B, 0, V_G, V_B };
+
+#endif
diff --git a/mpeglib/lib/util/render/imageBase.cpp b/mpeglib/lib/util/render/imageBase.cpp
new file mode 100644
index 00000000..040486ea
--- /dev/null
+++ b/mpeglib/lib/util/render/imageBase.cpp
@@ -0,0 +1,76 @@
+/*
+ base clase for X11 images (stores important parameters and dither pic)
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "imageBase.h"
+
+#include <iostream>
+
+using namespace std;
+
+ImageBase::ImageBase() {
+ identifier = NULL;
+}
+
+
+ImageBase::~ImageBase() {
+ if (identifier != NULL)
+ delete [] identifier;
+}
+
+void ImageBase::init(XWindow* xWindow, YUVPicture*)
+{
+ cout << "direct virtual call: ImageBase::init "<<endl;
+}
+
+int ImageBase::support() {
+ cout << "direct virtual call: ImageBase::support "<<endl;
+ return false;
+}
+
+int ImageBase::openImage(int) {
+ cout << "direct virtual call: ImageBase::openImage "<<endl;
+ return false;
+}
+
+
+int ImageBase::closeImage(){
+ cout << "direct virtual call: ImageBase::closeImage "<<endl;
+ return false;
+}
+
+void ImageBase::ditherImage(YUVPicture*) {
+ cout << "direct virtual call: ImageBase::ditherImage "<<endl;
+}
+
+
+void ImageBase::putImage() {
+ cout << "direct virtual call: ImageBase::putImage "<<endl;
+}
+
+void ImageBase::putImage(int ,int ) {
+ cout << "direct virtual call: ImageBase::putImage(w,h) "<<endl;
+}
+
+void ImageBase::setIdentifier(const char *id)
+{
+ if (identifier != NULL)
+ delete [] identifier;
+
+ identifier = new char [strlen(id) + 1];
+ strcpy(identifier, id);
+}
+
+char *ImageBase::getIdentifier()
+{
+ return identifier;
+}
diff --git a/mpeglib/lib/util/render/imageBase.h b/mpeglib/lib/util/render/imageBase.h
new file mode 100644
index 00000000..e1f7ae8c
--- /dev/null
+++ b/mpeglib/lib/util/render/imageBase.h
@@ -0,0 +1,140 @@
+/*
+ base clase for X11 images (stores important parameters and dither pic)
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __IMAGEBASE_H
+#define __IMAGEBASE_H
+
+
+#include <stdio.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#include "yuvPicture.h"
+#include "dither/ditherWrapper.h"
+
+
+
+#define _IMAGE_NONE 0
+#define _IMAGE_DESK 1
+#define _IMAGE_FULL 2
+#define _IMAGE_DOUBLE 4
+#define _IMAGE_RESIZE 8
+#define _IMAGE_DISABLED 16
+
+
+#define HAS_DESK(image) ((((image)->supportedModes) & _IMAGE_DESK) > 0)
+#define HAS_FULL(image) ((((image)->supportedModes) & _IMAGE_FULL) > 0)
+#define HAS_DOUBLE(image) ((((image)->supportedModes) & _IMAGE_DOUBLE) > 0)
+#define HAS_RESIZE(image) ((((image)->supportedModes) & _IMAGE_RESIZE) > 0)
+#define IS_DISABLED(image) ((((image)->supportedModes) & _IMAGE_DISABLED) > 0)
+
+#define IS_DESK(mode) (((mode) & _IMAGE_DESK) > 0)
+#define IS_FULL(mode) (((mode) & _IMAGE_FULL) > 0)
+#define IS_DOUBLE(mode) (((mode) & _IMAGE_DOUBLE) > 0)
+#define IS_RESIZEABLE(mode) (((mode) & _IMAGE_RESIZE) > 0)
+
+
+/**
+ This class creates from a given X11 Window a special image to
+ display.
+ An image has some characteristics, like startadress, width height,
+ if each row has a terminating modifier etc...
+
+ The image is resposible for the conversion from the yuv
+ format to the destination.
+
+ It is initialized with the constructed x11Window.
+ During a mode-switch (which is handled by x11window) the following
+ sequence is called:
+
+ support() ->true/false if this image type is supported
+ (switching to it is allowed)
+
+ openImage() called once when we switch to this
+ image type
+
+ ditherImage(..) for the conversion from yuv->rgb
+ obviously called for every image
+ putImage(..) time for display it
+
+ closeImage() called once, when we leave this image type
+
+
+ This sequence is necessary, because the user likey to switch
+ form desktop display to dga fullscreen.
+
+ The following image classes seems to be useful:
+
+ imageDeskX11 : standard ximage, maybe with shared mem support
+ full software rendering
+ imageDeskXV : image with hardware yuv->rgb rendering
+
+ imageDGAFull : dga 2.0 full software rendering (needs root)
+ imageXVFull : fullscreen hardware yuv->rgb rendering
+
+ The hierarchy is as follows:
+
+ (desk mode)
+ imageStdX11 : fallback, should work everywhere
+ imageStdXV : if supported imageStdX11 is disabled
+
+ (fullscreen mode)
+ imageDGAFull :
+ imageXVFull :
+
+ The supported switches between the modes are
+
+
+ desktop <-> fullscreen mode.
+
+
+*/
+
+
+class XWindow;
+
+class ImageBase {
+ private:
+ char *identifier;
+
+ public:
+ unsigned int supportedModes;
+
+ public:
+ ImageBase();
+ virtual ~ImageBase();
+
+ virtual void init(XWindow* xWindow, YUVPicture* pic = NULL);
+
+ virtual int support();
+
+ virtual int openImage(int imageMode);
+ virtual int closeImage();
+
+ virtual void ditherImage(YUVPicture* pic);
+ virtual void putImage();
+ virtual void putImage(int w,int h);
+
+ virtual int active() { return true; }
+
+ void setIdentifier(const char *id);
+ char *getIdentifier();
+
+};
+
+#endif
+
diff --git a/mpeglib/lib/util/render/pictureArray.cpp b/mpeglib/lib/util/render/pictureArray.cpp
new file mode 100644
index 00000000..71381ea0
--- /dev/null
+++ b/mpeglib/lib/util/render/pictureArray.cpp
@@ -0,0 +1,101 @@
+/*
+ nice try of an X11 output plugin
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "pictureArray.h"
+
+
+
+PictureArray::PictureArray(int width, int height) {
+ int i;
+ pictureCallback=NULL;
+ imageType=PICTURE_NO_TYPE;
+
+ for (i=0;i<_PICTURE_ARRAY_SIZE;i++) {
+ pictureArray[i]=new YUVPicture(width,height);
+ imageType=pictureArray[i]->getImageType();
+ }
+
+ /* Find a pict image structure in ring buffer not currently locked. */
+ /* Set current pict image structure to the one just found in ring. */
+
+ current=pictureArray[0];
+ past=pictureArray[1];
+ future=pictureArray[2];
+
+ picPerSec=0.0;
+ this->width=width;
+ this->height=height;
+
+}
+
+
+PictureArray::~PictureArray() {
+ int i;
+ for (i=0;i<_PICTURE_ARRAY_SIZE;i++) {
+ if (pictureArray[i] != NULL) {
+ delete pictureArray[i];
+ pictureArray[i]=NULL;
+ }
+
+ }
+}
+
+
+
+
+
+void PictureArray::setPicturePerSecond(double val) {
+ picPerSec=val;
+}
+
+
+double PictureArray::getPicturePerSecond() {
+ return picPerSec;
+}
+
+
+
+
+void PictureArray::forward() {
+ /* Update past and future references if needed. */
+
+ YUVPicture* tmp=past;
+
+ past = future;
+ future = current;
+ current = tmp;
+
+
+}
+
+
+
+
+YUVPicture* PictureArray::getYUVPictureCallback() {
+ return pictureCallback;
+}
+
+
+void PictureArray::setYUVPictureCallback(YUVPicture* pic) {
+ this->pictureCallback=pic;
+}
+
+
+void PictureArray::setImageType(int imageType) {
+ int i;
+ this->imageType=imageType;
+ for (i=0;i<_PICTURE_ARRAY_SIZE;i++) {
+ pictureArray[i]->setImageType(imageType);
+ }
+}
+
diff --git a/mpeglib/lib/util/render/pictureArray.h b/mpeglib/lib/util/render/pictureArray.h
new file mode 100644
index 00000000..6a7e731c
--- /dev/null
+++ b/mpeglib/lib/util/render/pictureArray.h
@@ -0,0 +1,76 @@
+/*
+ nice try of an X11 output plugin
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __VIDEOOUTPUTX11_H
+#define __VIDEOOUTPUTX11_H
+
+
+#include "yuvPicture.h"
+#include <stdlib.h>
+
+#define _PICTURE_ARRAY_SIZE 5
+
+class PictureArray {
+
+ class YUVPicture* pictureArray[_PICTURE_ARRAY_SIZE];
+
+ class YUVPicture* past; /* Past predictive frame. */
+ class YUVPicture* future; /* Future predictive frame. */
+ class YUVPicture* current; /* Current frame. */
+
+ double picPerSec;
+ int width;
+ int height;
+
+ int imageType;
+
+ public:
+ PictureArray(int width, int height);
+ ~PictureArray();
+
+ inline YUVPicture* getPast() {return past;}
+ inline YUVPicture* getFuture() {return future;}
+ inline YUVPicture* getCurrent() {return current;}
+
+
+ inline void setPast(YUVPicture* pic) {past=pic;}
+ inline void setFuture(YUVPicture* pic) {future=pic;}
+ inline void setCurrent(YUVPicture* pic) {current=pic;}
+ inline int getWidth() { return width; }
+ inline int getWidth_Half() { return width/2; }
+
+ // attention with these!
+ // these are shares pointer
+ // only call after mpegVidRsrc and then set them back to NULL
+ YUVPicture* getYUVPictureCallback();
+ void setYUVPictureCallback(YUVPicture* pic);
+
+
+ void forward();
+
+ void setPicturePerSecond(double val);
+ double getPicturePerSecond();
+
+ // use these to swap the yuv Mode
+ inline int getImageType() { return imageType; }
+ void setImageType(int mode);
+
+
+ private:
+ YUVPicture* pictureCallback;
+
+
+};
+#endif
+
diff --git a/mpeglib/lib/util/render/renderMachine.cpp b/mpeglib/lib/util/render/renderMachine.cpp
new file mode 100644
index 00000000..c6327930
--- /dev/null
+++ b/mpeglib/lib/util/render/renderMachine.cpp
@@ -0,0 +1,205 @@
+ /*
+ puts the yuv images onto a surface
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "renderMachine.h"
+
+#include <iostream>
+
+using namespace std;
+
+
+RenderMachine::RenderMachine() {
+
+#ifndef SDL_WRAPPER
+ surface=new X11Surface();
+#endif
+#ifdef SDL_WRAPPER
+ surface=new SDLSurface();
+#endif
+
+
+ pictureArray=NULL;
+
+ startTime=new TimeStamp();
+ endTime=new TimeStamp();
+
+ initialMode = _IMAGE_DESK;
+}
+
+
+RenderMachine::~RenderMachine() {
+
+
+ closeWindow();
+ delete surface;
+
+ delete startTime;
+ delete endTime;
+
+}
+
+
+
+
+
+
+void RenderMachine::waitRestTime() {
+ endTime->gettimeofday();
+ startTime->minus(endTime,endTime);
+ endTime->waitForIt();
+
+}
+
+int RenderMachine::x11WindowId()
+{
+ return surface->x11WindowId();
+}
+
+
+int RenderMachine::openWindow(int width,
+ int height,const char *title) {
+if (surface->open(width,height,title)) {
+ pictureArray=new PictureArray(width, height);
+
+ return switchToMode(initialMode);
+ }
+ return false;
+}
+
+
+void RenderMachine::closeWindow() {
+
+ if (surface->isOpen()==false) {
+ return;
+ }
+ if (pictureArray != NULL) {
+ delete pictureArray;
+ pictureArray=NULL;
+ }
+ surface->close();
+}
+
+/**
+ important method. This is our only way to switch from
+ fullscreen back to the desktop screen.
+ This method is called if the video end (normal or by user request)
+ We dont have a callback, thus after the image stops, we would
+ never get events.
+*/
+void RenderMachine::flushWindow() {
+
+ // we always switch back to desk mode.
+ if (IS_FULL(surface->getImageMode())) {
+ switchToMode(surface->getImageMode() ^ _IMAGE_DESK ^ _IMAGE_FULL);
+ }
+
+
+}
+
+
+
+
+void RenderMachine::putImage(YUVPicture* pic,
+ TimeStamp* waitTime,
+ TimeStamp* ) {
+ if (pic == NULL) {
+ cout << "pic is null"<<endl;
+ return;
+ }
+ startTime->gettimeofday();
+ startTime->addOffset(waitTime);
+
+ // need dither?
+ surface->dither(pic);
+
+ int nextMode;
+ if (surface->checkEvent(&nextMode) == true) {
+ switchToMode(nextMode);
+ }
+ surface->putImage(pic);
+ waitRestTime();
+}
+
+
+int RenderMachine::switchToMode(int mode) {
+ if (surface->getImageMode() != mode) {
+ surface->closeImage();
+ if (mode != _IMAGE_NONE) {
+ surface->openImage(mode);
+ }
+
+ else {
+ cout << "no imageMode, no open, that's life"<<endl;
+ return false;
+ }
+ }
+ return true;
+}
+
+
+
+PictureArray* RenderMachine::lockPictureArray() {
+ return pictureArray;
+}
+
+
+void RenderMachine::unlockPictureArray(PictureArray* pictureArray) {
+
+ // chance to switch mode
+
+
+ // put picture out
+ if (surface->getImageMode() != _IMAGE_NONE) {
+ YUVPicture* pic=pictureArray->getYUVPictureCallback();
+ if (pic != NULL) {
+ TimeStamp* waitTime=pic->getWaitTime();
+ TimeStamp* earlyTime=pic->getEarlyTime();
+ putImage(pic,waitTime,earlyTime);
+ }
+ } else {
+ cout << "no mode selected"<<endl;
+ }
+}
+
+
+void RenderMachine::config(const char* key,
+ const char* value,void* user_data) {
+ if (strcmp(key,"getDepth")==0) {
+ int* val=(int*)user_data;
+ *val=surface->getDepth();
+ }
+ if (surface != NULL) {
+ int mode = surface->getImageMode();
+ if (strcmp(key,"toggleFullscreen")==0) {
+ if (surface->findImage(mode ^ _IMAGE_FULL) != NULL) {
+ if (surface->isOpen())
+ switchToMode(mode ^ _IMAGE_FULL);
+ else
+ initialMode = _IMAGE_FULL;
+ }
+ }
+ if (strcmp(key,"toggleDouble")==0) {
+ if (surface->findImage(mode ^ _IMAGE_DOUBLE) != NULL) {
+ if (surface->isOpen())
+ switchToMode(mode ^ _IMAGE_DOUBLE);
+ else
+ initialMode = _IMAGE_DOUBLE;
+ }
+ }
+ }
+
+ surface->config(key,value,user_data);
+}
+
+
+
diff --git a/mpeglib/lib/util/render/renderMachine.h b/mpeglib/lib/util/render/renderMachine.h
new file mode 100644
index 00000000..728a6740
--- /dev/null
+++ b/mpeglib/lib/util/render/renderMachine.h
@@ -0,0 +1,90 @@
+/*
+ puts the yuv images onto a surface
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __RENDERMACHINE_H
+#define __RENDERMACHINE_H
+
+
+
+
+/**
+ RenderMachine. We still have the problem, because of performance,
+ that we cannot have a yuv picture format in the decoder
+ and one in the output to x11. they must be shared.
+ XV support then directly works on them and SDL images
+ can be exported to the decoder as well.
+
+ Another point is : mode switch. User want desktop->fullscreen switch.
+ Due to the threaded nature, we must have a single synchronization
+ point, when we know that the decoder currently does _not_ decode
+ so that we can switch the imaged and free the memory.
+
+ Some points are currently unclear, for example how to handle
+ applications, which want to redirect the image into their own
+ buffers, but hey, there are that many classes and layers
+ I really think it should be doable somehow
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef SDL_WRAPPER
+#include "x11/x11Surface.h"
+#endif
+
+#ifdef SDL_WRAPPER
+#include "sdl/sdlSurface.h"
+#endif
+
+#include "pictureArray.h"
+#include "../abstract/abs_thread.h"
+
+class RenderMachine {
+
+
+ Surface* surface;
+ PictureArray* pictureArray;
+
+
+ TimeStamp* startTime;
+ TimeStamp* endTime;
+
+ int initialMode;
+
+ public:
+ RenderMachine();
+ ~RenderMachine();
+
+ int openWindow(int width, int height,const char *title);
+ int x11WindowId();
+ void closeWindow();
+ void flushWindow();
+
+
+ PictureArray* lockPictureArray();
+ void unlockPictureArray(PictureArray* pictureArray);
+
+ void config(const char* key, const char* value,void* user_data);
+
+ private:
+ void waitRestTime();
+ void putImage(YUVPicture* pic,TimeStamp* waitTime,TimeStamp* earlyTime);
+
+ int switchToMode(int mode);
+
+};
+#endif
diff --git a/mpeglib/lib/util/render/sdl/Makefile.am b/mpeglib/lib/util/render/sdl/Makefile.am
new file mode 100644
index 00000000..1a4b85eb
--- /dev/null
+++ b/mpeglib/lib/util/render/sdl/Makefile.am
@@ -0,0 +1,43 @@
+# player - Makefile.am
+
+INCLUDES = $(all_includes)
+
+
+noinst_LTLIBRARIES = libutilsdl.la
+
+noinst_HEADERS = imageDeskSDL.h sdlSurface.h
+
+libutilsdl_la_SOURCES = imageDeskSDL.cpp sdlSurface.cpp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/lib/util/render/sdl/imageDeskSDL.cpp b/mpeglib/lib/util/render/sdl/imageDeskSDL.cpp
new file mode 100644
index 00000000..b1ff9a7c
--- /dev/null
+++ b/mpeglib/lib/util/render/sdl/imageDeskSDL.cpp
@@ -0,0 +1,110 @@
+/*
+ SDL surface output
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "imageDeskSDL.h"
+
+#ifdef SDL_WRAPPER
+
+
+ImageDeskSDL::ImageDeskSDL() {
+
+ this->surface=NULL;
+ this->rect=NULL;
+ imageMode=_IMAGE_NONE;
+ lSupport=true;
+ image=NULL;
+}
+
+
+ImageDeskSDL::~ImageDeskSDL() {
+ closeImage();
+ cout << "SDL destry needed"<<endl;
+}
+
+
+
+int ImageDeskSDL::support() {
+ return lSupport;
+}
+
+
+void ImageDeskSDL::init(XWindow* xWindow, YUVPicture* pic) {
+ cout << "ImageDeskSDL::init"<<endl;
+ this->surface=(SDL_Surface*)xWindow;
+ this->rect=(SDL_Rect*)pic;
+}
+
+int ImageDeskSDL::openImage(int imageMode) {
+ int w=rect->w;
+ int h=rect->h;
+ this->imageMode=imageMode;
+ /* Create a YV12 image (Y + V + U) */
+ cout << "CreateYUVOverlay -s"<<imageMode<<" w:"<<w<<" h:"<<h<<endl;
+ image = SDL_CreateYUVOverlay(w,h,
+ SDL_YV12_OVERLAY,
+ surface);
+ if (image == NULL) {
+ cout << "error creating image"<<endl;
+ exit(0);
+ }
+ cout << "CreateYUVOverlay -e"<<endl;
+ return true;
+}
+
+
+int ImageDeskSDL::closeImage() {
+ if (image != NULL) {
+ cout << "FreeYUVOverlay -s"<<endl;
+ SDL_FreeYUVOverlay(image);
+ // otherwise test of NULL will fail
+ image = NULL;
+ cout << "FreeYUVOverlay -e"<<endl;
+ }
+ return true;
+}
+
+
+
+void ImageDeskSDL::ditherImage(YUVPicture* pic) {
+
+ int w=pic->getWidth();
+ int h=pic->getHeight();
+ int size=w*h+(w*h)/2;
+ SDL_LockYUVOverlay(image);
+ memcpy(*((char**)image->pixels),pic->getLuminancePtr(),size);
+ SDL_UnlockYUVOverlay(image);
+
+}
+
+
+void ImageDeskSDL::putImage(int w, int h) {
+ SDL_Rect dest;
+ dest.x=0;
+ dest.y=0;
+ dest.w=rect->w;
+ dest.h=rect->h;
+ if (imageMode & _IMAGE_RESIZE) {
+ dest.w = w;
+ dest.h = h;
+ }
+
+ if (imageMode & _IMAGE_DOUBLE) {
+ dest.w*=2;
+ dest.h*=2;
+ }
+ SDL_DisplayYUVOverlay(image,&dest);
+
+}
+
+#endif
+
diff --git a/mpeglib/lib/util/render/sdl/imageDeskSDL.h b/mpeglib/lib/util/render/sdl/imageDeskSDL.h
new file mode 100644
index 00000000..1bccb94a
--- /dev/null
+++ b/mpeglib/lib/util/render/sdl/imageDeskSDL.h
@@ -0,0 +1,65 @@
+/*
+ SDL surface output
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __IMAGEDESKSDL_H
+#define __IMAGEDESKSDL_H
+
+#include "../imageBase.h"
+
+
+
+#ifndef SDL_WRAPPER
+ class ImageDeskSDL : public ImageBase {
+ };
+#endif
+
+#ifdef SDL_WRAPPER
+
+#if defined WIN32
+#include <SDL.h>
+#include <SDL_video.h>
+#else
+#include <SDL/SDL.h>
+#include <SDL/SDL_video.h>
+#endif
+
+class ImageDeskSDL : public ImageBase {
+
+ int lSupport;
+ SDL_Overlay *image;
+
+ SDL_Surface* surface;
+ SDL_Rect* rect;
+ int imageMode;
+
+ public:
+ ImageDeskSDL();
+ ~ImageDeskSDL();
+
+ int support();
+ void init(XWindow* xWindow, YUVPicture* pic=NULL);
+
+
+ int openImage(int imageMode);
+ int closeImage();
+
+ void ditherImage(YUVPicture* pic);
+
+ void putImage(int w, int h);
+
+
+
+};
+#endif
+
+#endif
diff --git a/mpeglib/lib/util/render/sdl/sdlSurface.cpp b/mpeglib/lib/util/render/sdl/sdlSurface.cpp
new file mode 100644
index 00000000..86ef41bb
--- /dev/null
+++ b/mpeglib/lib/util/render/sdl/sdlSurface.cpp
@@ -0,0 +1,219 @@
+/*
+ surface wrapper for SDL
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "sdlSurface.h"
+
+
+#ifdef SDL_WRAPPER
+
+
+SDLSurface::SDLSurface() {
+ surface=NULL;
+ lOpen=false;
+ imageMode=_IMAGE_NONE;
+ lSDLInit=false;
+ imageDeskSDL=new ImageDeskSDL();
+}
+
+
+SDLSurface::~SDLSurface() {
+ close();
+ if (imageCurrent != NULL) {
+ imageCurrent->closeImage();
+ delete imageCurrent;
+ // otherwise test of NULL will fail
+ imageCurrent = NULL;
+ }
+}
+
+
+int SDLSurface::isOpen() {
+ return lOpen;
+}
+
+int SDLSurface::getImageMode() {
+ return imageMode;
+}
+
+int SDLSurface::open(int width, int height,const char *title,bool border) {
+ cout << "SDL openImage:"<<title<<endl;
+
+ sdlinit();
+
+ rect.x = 0;
+ rect.y = 0;
+ rect.w = width;
+ rect.h = height;
+
+ return true;
+}
+
+
+int SDLSurface::close() {
+ if (isOpen()==false) {
+ cout << "WindowOut::closeWindow already closed"<<endl;
+ return true;
+ }
+ if(surface) {
+ SDL_FreeSurface(surface);
+ surface = NULL;
+ }
+
+ lOpen=false;
+ return true;
+}
+
+
+int SDLSurface::getHeight() {
+ return rect.h;
+}
+
+
+int SDLSurface::getWidth() {
+ return rect.w;
+}
+
+
+int SDLSurface::getDepth() {
+ return video_bpp;
+}
+
+int SDLSurface::putImage(YUVPicture* ) {
+ return true;
+}
+
+
+int SDLSurface::openImage(int imageMode, YUVPicture* pic) {
+ if (this->imageMode != _IMAGE_NONE) {
+ cout << "bad open error X11Surface::openImage"<<endl;
+ exit(0);
+ }
+ cout << "************* openImage SDL"<<imageMode<<endl;
+ this->imageMode=imageMode;
+ imageCurrent=NULL;
+ int w=getWidth();
+ int h=getHeight();
+ if(imageMode & _IMAGE_RESIZE) {
+ w=resize_rect.w;
+ h=resize_rect.h;
+ }
+
+ if (imageMode & _IMAGE_DOUBLE) {
+ w=rect.w*2;
+ h=rect.h*2;
+ }
+
+ if (imageMode & _IMAGE_DESK) {
+ if (imageDeskSDL->support()) {
+ imageCurrent=imageDeskSDL;
+ int video_flags = SDL_SWSURFACE;
+ video_flags |= SDL_ASYNCBLIT;
+ video_flags |= SDL_RESIZABLE;
+ if(surface)
+ SDL_FreeSurface(surface);
+ surface = SDL_SetVideoMode(w, h, video_bpp, video_flags);
+
+ }
+ }
+ if (imageMode & _IMAGE_FULL) {
+ if (imageDeskSDL->support()) {
+ imageCurrent=imageDeskSDL;
+ int video_flags = SDL_FULLSCREEN;
+ video_flags |= SDL_ASYNCBLIT;
+ video_flags |= SDL_HWSURFACE;
+ if(surface)
+ SDL_FreeSurface(surface);
+ surface = SDL_SetVideoMode(w, h, video_bpp, video_flags);
+ }
+ }
+ if (imageCurrent != NULL) {
+ cout << "surface:"<<surface<<endl;
+ imageCurrent->init((XWindow*)surface,(YUVPicture*)&rect);
+ imageCurrent->openImage(imageMode);
+ }
+ return (imageCurrent != NULL);
+}
+
+
+int SDLSurface::closeImage() {
+ this->imageMode = _IMAGE_NONE;
+ if (imageCurrent != NULL) {
+ imageCurrent->closeImage();
+ }
+ imageCurrent=NULL;
+ return true;
+}
+
+int SDLSurface::checkEvent(int* newMode) {
+ int back=false;
+ SDL_Event event;
+ SDL_PollEvent(&event);
+ switch (event.type) {
+ case SDL_MOUSEBUTTONDOWN: {
+ int button=event.button.button;
+ if (button == 1) {
+ *newMode = imageMode ^ _IMAGE_DOUBLE;
+ back=true;
+ }
+ if (button == 3) {
+ *newMode = imageMode ^ _IMAGE_DESK ^ _IMAGE_FULL;
+ back=true;
+ }
+ break;
+ }
+ case SDL_VIDEORESIZE : {
+ resize_rect.w = event.resize.w;
+ resize_rect.h = event.resize.h;
+ *newMode = imageMode | _IMAGE_RESIZE;
+ back = true;
+ break;
+ }
+
+ }
+ return back;
+}
+
+int SDLSurface::dither(YUVPicture* pic) {
+ if (imageCurrent != NULL) {
+ imageCurrent->ditherImage(pic);
+ if(imageMode & _IMAGE_RESIZE) {
+ imageCurrent->putImage(resize_rect.w, resize_rect.h);
+ } else {
+ imageCurrent->putImage(rect.w, rect.h);
+ }
+ }
+ return true;
+}
+
+void SDLSurface::sdlinit() {
+ if (lSDLInit == false) {
+ if (SDL_Init(SDL_INIT_VIDEO) < 0 ) {
+ fprintf(stderr, "Warning: Couldn't init SDL video: %s\n",
+ SDL_GetError());
+ fprintf(stderr, "Will ignore video stream\n");
+ exit(0);
+ }
+ atexit(SDL_Quit);
+ cout << "****************** SDL VIDEO System **********"<<endl;
+ /* Get the "native" video mode */
+ video_info=SDL_GetVideoInfo();
+ video_bpp=video_info->vfmt->BitsPerPixel;
+ imageMode=_IMAGE_NONE;
+
+ imageCurrent=NULL;
+ }
+ lSDLInit=true;
+}
+
+#endif
diff --git a/mpeglib/lib/util/render/sdl/sdlSurface.h b/mpeglib/lib/util/render/sdl/sdlSurface.h
new file mode 100644
index 00000000..aca5f293
--- /dev/null
+++ b/mpeglib/lib/util/render/sdl/sdlSurface.h
@@ -0,0 +1,78 @@
+/*
+ surface wrapper for SDL
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __SDLSURFACE_H
+#define __SDLSURFACE_H
+
+
+#include "../surface.h"
+#include "imageDeskSDL.h"
+
+
+
+#ifndef SDL_WRAPPER
+ class SDLSurface : public Surface {
+ };
+#endif
+
+#ifdef SDL_WRAPPER
+#if defined WIN32
+#include <SDL.h>
+#include <SDL_video.h>
+#else
+#include <SDL/SDL.h>
+#include <SDL/SDL_video.h>
+#endif
+
+
+class SDLSurface : public Surface {
+
+ int lOpen;
+ int imageMode;
+ int lSDLInit;
+ int video_bpp;
+ SDL_Surface* surface;
+ SDL_Rect rect;
+ SDL_Rect resize_rect;
+ const SDL_VideoInfo *video_info;
+
+ ImageBase* imageCurrent;
+
+ ImageDeskSDL* imageDeskSDL;
+
+ public:
+ SDLSurface();
+ ~SDLSurface();
+
+ int isOpen();
+ int open(int width, int height,const char *title, bool border=false);
+ int close();
+ int getHeight();
+ int getWidth();
+ int getDepth();
+ int getImageMode();
+ int checkEvent(int* mode);
+
+ int openImage(int imageMode, YUVPicture* pic = NULL);
+ int closeImage();
+ int dither(YUVPicture* pic);
+ int putImage(YUVPicture* pic);
+
+
+ private:
+ void sdlinit();
+};
+#endif
+
+#endif
diff --git a/mpeglib/lib/util/render/surface.cpp b/mpeglib/lib/util/render/surface.cpp
new file mode 100644
index 00000000..12a15410
--- /dev/null
+++ b/mpeglib/lib/util/render/surface.cpp
@@ -0,0 +1,117 @@
+/*
+ surface base class
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "surface.h"
+
+#include <iostream>
+
+using namespace std;
+
+Surface::Surface() {
+}
+
+
+Surface::~Surface() {
+}
+
+
+int Surface::isOpen() {
+ cout << "direct virtual call Surface::isOpen "<<endl;
+ return false;
+}
+
+
+int Surface::open(int width, int height,const char *title, bool border) {
+ cout << "direct virtual call Surface::open "<<endl;
+ cout << "width:"<<width<<" height:"<<height
+ << " title:"<<title<<endl;
+ return false;
+}
+
+
+int Surface::close() {
+ cout << "direct virtual call Surface::close "<<endl;
+ return true;
+}
+
+
+int Surface::getHeight() {
+ cout << "direct virtual call Surface::getHeight "<<endl;
+ return 0;
+}
+
+
+int Surface::getWidth() {
+ cout << "direct virtual call Surface::getWidth "<<endl;
+ return 0;
+}
+
+
+int Surface::getDepth() {
+ cout << "direct virtual call Surface::getDepth "<<endl;
+ return 0;
+}
+
+int Surface::getImageMode() {
+ cout << "direct virtual call Surface::getImageMode "<<endl;
+ return 0;
+}
+
+int Surface::x11WindowId() {
+ cout << "direct virtual call Surface::x11WindowId " << endl;
+ return -1;
+}
+
+ImageBase *Surface::findImage(int)
+{
+ cout << "direct virtual call: Surface::findImage "<<endl;
+ return NULL;
+}
+
+int Surface::openImage(int mode, YUVPicture*) {
+ cout << "direct virtual call Surface::openImage "<<endl;
+ cout << "imageMode:"<<mode<<endl;
+ return false;
+}
+
+
+int Surface::closeImage() {
+ cout << "direct virtual call Surface::closeImage "<<endl;
+ return true;
+}
+
+
+int Surface::dither(YUVPicture* pic) {
+ cout << "direct virtual call Surface::dither "<<endl;
+ pic->print("Surface::dither");
+ return false;
+}
+
+
+int Surface::putImage(YUVPicture* pic) {
+ cout << "direct virtual call Surface::putImage "<<endl;
+ pic->print("Surface::putImage");
+ return false;
+}
+
+int Surface::checkEvent(int*) {
+ cout << "direct virtual call Surface::checkEvent "<<endl;
+ return false;
+}
+
+void Surface::config(const char* ,
+ const char* ,void* ) {
+ cout << "direct virtual call Surface::config"<<endl;
+}
+
diff --git a/mpeglib/lib/util/render/surface.h b/mpeglib/lib/util/render/surface.h
new file mode 100644
index 00000000..84de996a
--- /dev/null
+++ b/mpeglib/lib/util/render/surface.h
@@ -0,0 +1,55 @@
+/*
+ surface base class
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __SURFACE_H
+#define __SURFACE_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "yuvPicture.h"
+
+class ImageBase;
+
+class Surface {
+
+ public:
+ Surface();
+ virtual ~Surface();
+
+ virtual int isOpen();
+ virtual int open(int width, int height,const char *title, bool border=false);
+ virtual int close();
+ virtual int getHeight();
+ virtual int getWidth();
+ virtual int getDepth();
+ virtual int getImageMode();
+ virtual int x11WindowId();
+
+ virtual ImageBase *findImage(int imageMode);
+
+ virtual int openImage(int mode, YUVPicture* pic = NULL);
+ virtual int closeImage();
+ virtual int dither(YUVPicture* pic);
+ virtual int putImage(YUVPicture* pic);
+
+ virtual int checkEvent(int* mode);
+
+ // config surface
+ virtual void config(const char* key,
+ const char* value,void* user_data);
+
+
+};
+#endif
diff --git a/mpeglib/lib/util/render/x11/Makefile.am b/mpeglib/lib/util/render/x11/Makefile.am
new file mode 100644
index 00000000..97ab0d67
--- /dev/null
+++ b/mpeglib/lib/util/render/x11/Makefile.am
@@ -0,0 +1,48 @@
+# player - Makefile.am
+
+INCLUDES = $(all_includes)
+
+
+noinst_LTLIBRARIES = libutilx11.la
+
+
+noinst_HEADERS = initDisplay.h \
+ imageDeskX11.h imageDGAFull.h \
+ imageXVDesk.h x11Surface.h xinit.h
+
+libutilx11_la_SOURCES = initDisplay.cpp \
+ imageDeskX11.cpp \
+ imageDGAFull.cpp imageXVDesk.cpp \
+ x11Surface.cpp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/lib/util/render/x11/imageDGAFull.cpp b/mpeglib/lib/util/render/x11/imageDGAFull.cpp
new file mode 100644
index 00000000..6e07b658
--- /dev/null
+++ b/mpeglib/lib/util/render/x11/imageDGAFull.cpp
@@ -0,0 +1,289 @@
+/*
+ xfree 4.0 dga fullscreen mode
+ Copyright (C) 2000 Martin Vogt, Christian Gerlach
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "imageDGAFull.h"
+
+#include <iostream>
+
+using namespace std;
+
+ImageDGAFull::ImageDGAFull() {
+
+ m_iMode = -1;
+ m_bIsActive = false;
+ lSupport=false;
+ m_pxWindow = NULL;
+ m_iImageMode = _IMAGE_NONE;
+ ditherWrapper=NULL;
+ supportedModes = _IMAGE_NONE;
+ setIdentifier("DGA");
+}
+
+
+ImageDGAFull::~ImageDGAFull() {
+ if (ditherWrapper != NULL) {
+ delete ditherWrapper;
+ }
+}
+
+void ImageDGAFull::init(XWindow *xWindow, YUVPicture*)
+{
+ int uid;
+
+ m_pxWindow = xWindow;
+ if (ditherWrapper == NULL) {
+ ditherWrapper=new DitherWrapper(xWindow->depth,
+ xWindow->redMask,
+ xWindow->greenMask,
+ xWindow->blueMask,
+ xWindow->pixel);
+ }
+
+#ifndef X11_DGA2
+ return;
+#endif
+
+#ifdef X11_DGA2
+ m_pDGAModes=NULL;
+ m_iNumberModes = 0;
+
+ m_iVideoWidth = xWindow->width;
+ m_iVideoHeight = xWindow->height;
+ uid=getuid();
+ if (uid != 0) {
+ //cout << "you are :"<<uid<<" and not root(0). DGA 2.0 needs root"<<endl;
+ return;
+ }
+
+ if ((m_pDisplay =xWindow->display)==NULL ) {
+ fprintf( stderr, " cannot connect to X server %s\n", XDisplayName(NULL));
+ return;
+ }
+
+ m_iScreen = DefaultScreen(xWindow->display);
+
+
+ if (!XF86DGAQueryVersion(xWindow->display,
+ &m_iMajorVersion, &m_iMinorVersion)) {
+ fprintf(stderr, "Unable to query video extension version\n");
+ return ;
+ }
+ printf("DGA version %d.%d detected!\n", m_iMajorVersion, m_iMinorVersion);
+
+ // Fail if the extension version in the server is too old
+ if (m_iMajorVersion < DGA_MINMAJOR ||
+ (m_iMajorVersion == DGA_MINMAJOR && m_iMinorVersion < DGA_MINMINOR)) {
+ fprintf(stderr, "Xserver is running an old XFree86-DGA version"
+ " (%d.%d)\n", m_iMajorVersion, m_iMinorVersion);
+ fprintf(stderr, "Minimum required version is %d.%d\n",
+ DGA_MINMAJOR, DGA_MINMINOR);
+ return ;
+ }
+
+ if (!XF86DGAQueryExtension(m_pDisplay, &m_iEventBase, &m_iErrorBase)) {
+ fprintf(stderr, "Unable to query video extension information\n");
+ return ;
+ }
+ printf("Event base %d\n", m_iEventBase);
+ printf("Error base %d\n", m_iErrorBase);
+
+ lSupport=true;
+ supportedModes = _IMAGE_FULL;
+#endif
+}
+
+int ImageDGAFull::support() {
+ return lSupport;
+}
+
+
+int ImageDGAFull::openImage(int mode) {
+#ifdef X11_DGA2
+ int width, bank, ram;
+ m_bAllowZoom = IS_DOUBLE(mode);
+ m_iImageMode = mode;
+
+ /* Open access to the framebuffer */
+ if ( ! XDGAOpenFramebuffer(m_pDisplay,m_iScreen) ) {
+ return(false);
+ }
+
+ findMode(m_pxWindow->width, m_pxWindow->height, m_pxWindow->depth);
+ m_pDevice = XDGASetMode(m_pDisplay, m_iScreen, m_pDGAModes[m_iMode].num);
+
+
+ XDGASelectInput(m_pDisplay, m_iScreen,
+ KeyPressMask | ButtonPressMask | PointerMotionMask);
+
+ XF86DGAGetVideo(m_pDisplay,m_iScreen,&m_pAddr,&width,&bank,&ram);
+
+
+ if(bank < (ram * 1024)) {
+ XF86DGASetVidPage(m_pxWindow->display,
+ DefaultScreen(m_pxWindow->display), 0);
+ }
+
+ XF86DGASetViewPort(m_pxWindow->display,
+ DefaultScreen(m_pxWindow->display),0,0);
+
+
+ printf("Offset:%8x\n",m_iOffsetScreen);
+ m_pStartAddr = m_pAddr + m_iOffsetScreen;
+ m_iOffsetLine = (m_iBytesPerLine - m_iBytesPerRow) / m_iBytesPerPixel;
+ cout << "LineOffset: " << m_iOffsetLine << endl;
+
+ // Clear the screen
+ memset(m_pAddr, 0, m_iBytesPerLine * m_iScreenHeight);
+ /* char *pos = m_pStartAddr;
+ int end = (m_bZoom) ? 2*m_iVideoHeight : m_iVideoHeight;
+ for (int line=0 ; line<end ; line++) {
+ memset(pos, 80, m_iBytesPerRow);
+ pos += m_iBytesPerRow + m_iOffsetLine * m_iBytesPerPixel;
+ }
+ sleep(2);*/
+
+ m_bIsActive = true;
+#endif
+ return true;
+}
+
+
+int ImageDGAFull::closeImage() {
+#ifdef X11_DGA2
+ m_bIsActive = false;
+ stop();
+
+ // delete resources
+ if (m_pDGAModes != NULL) {
+ delete m_pDGAModes;
+ m_pDGAModes=NULL;
+ }
+#endif
+ return true;
+}
+
+
+unsigned char* ImageDGAFull::address() {
+ return (unsigned char*) m_pStartAddr;
+}
+
+
+int ImageDGAFull::offset() {
+ return m_iOffsetLine;
+}
+
+
+void ImageDGAFull::ditherImage(YUVPicture* pic) {
+
+ int useMode = (m_bZoom) ? m_iImageMode : m_iImageMode & (!_IMAGE_DOUBLE);
+
+ ditherWrapper->doDither(pic,m_pxWindow->depth,useMode,
+ address(),offset());
+}
+
+
+void ImageDGAFull::putImage() {
+
+ if (event())
+ closeImage();
+}
+
+
+int ImageDGAFull::findMode(int width, int height, int bpp) {
+#ifdef X11_DGA2
+ int minBorder = INT_MAX;
+ int yBorder=0;
+ int border;
+
+ // TODO: also check the y-axis
+
+ m_iMode = -1;
+ m_iNumberModes = 0;
+ m_pDGAModes = XDGAQueryModes(m_pDisplay, m_iScreen, &m_iNumberModes);
+ printf("Number modes: %d\n", m_iNumberModes);
+
+ for (int count=0 ; count<m_iNumberModes ; count++) {
+
+
+ if (m_pDGAModes[count].depth != bpp)
+ continue;
+
+ printf("Mode: %d %dx%d \t bpp %d\n",
+ count,
+ m_pDGAModes[count].viewportWidth,
+ m_pDGAModes[count].viewportHeight,
+ m_pDGAModes[count].bitsPerPixel);
+
+ // test normal video
+ border = m_pDGAModes[count].viewportWidth - width;
+ if ((border >= 0) && (border < minBorder)) {
+ minBorder = border;
+ m_iMode = count;
+ m_bZoom = false;
+ yBorder = m_pDGAModes[count].viewportHeight - height;
+ }
+
+ // test zoomed video
+ if (m_bAllowZoom) {
+ border = m_pDGAModes[count].viewportWidth - 2 * width;
+ if ((border >= 0) && (border < minBorder)) {
+ minBorder = border;
+ m_iMode = count;
+ m_bZoom = true;
+ yBorder = m_pDGAModes[count].viewportHeight-2*height;
+ }
+ }
+ }
+
+ if (m_iMode != -1) {
+ m_iScreenWidth = m_pDGAModes[m_iMode].viewportWidth;
+ m_iScreenHeight = m_pDGAModes[m_iMode].viewportHeight;
+
+ m_iBytesPerPixel = m_pDGAModes[m_iMode].bitsPerPixel / 8;
+ m_iBytesPerLine = m_pDGAModes[m_iMode].bytesPerScanline;
+ m_iBytesPerRow = width * m_iBytesPerPixel;
+ if (m_bZoom) {
+ m_iBytesPerRow += m_iBytesPerRow;
+ }
+
+ m_iOffsetScreen = minBorder * (m_iBytesPerPixel / 2) +
+ (yBorder / 2) * m_iBytesPerLine;
+ }
+
+ cout << "Best Mode: " << m_iMode << endl;
+ cout << "Border Size: " << minBorder / 2 << endl;
+ cout << "Zoom: " << m_bZoom << endl;
+ cout << "Bytes per Line: " << m_iBytesPerLine << endl;
+ cout << "Bytes per Row: " << m_iBytesPerRow << endl;
+ cout << "Bytes per Pixel:" << m_iBytesPerPixel << endl;
+ cout << "Total offset: " << m_iOffsetScreen << endl;
+#endif
+ return (m_iMode != -1);
+}
+
+
+
+int ImageDGAFull::event() {
+ XEvent event;
+ return XCheckTypedEvent(m_pDisplay, ButtonPress + m_iEventBase, &event);
+}
+
+void ImageDGAFull::stop() {
+#ifdef X11_DGA2
+ m_bIsActive = false;
+ XF86DGADirectVideo(m_pDisplay, m_iScreen, 0);
+
+ XUngrabPointer(m_pDisplay, CurrentTime);
+ XUngrabKeyboard(m_pDisplay, CurrentTime);
+#endif
+}
diff --git a/mpeglib/lib/util/render/x11/imageDGAFull.h b/mpeglib/lib/util/render/x11/imageDGAFull.h
new file mode 100644
index 00000000..ddb8f493
--- /dev/null
+++ b/mpeglib/lib/util/render/x11/imageDGAFull.h
@@ -0,0 +1,131 @@
+/*
+ xfree 4.0 dga fullscreen mode
+ Copyright (C) 2000 Martin Vogt, Christian Gerlach
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __IMAGEDGAFULL_H
+#define __IMAGEDGAFULL_H
+#include "xinit.h"
+
+#include "../imageBase.h"
+
+
+
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/types.h>
+
+
+
+
+#define DGA_MINMAJOR 2
+#define DGA_MINMINOR 0
+
+
+
+
+/**
+
+ Displays and renders X11 images in software with the help
+ of the ditherWrapper class. It switches to xfree 4.0 dga 2.0
+ and needs root priviliges for that
+
+*/
+
+
+class ImageDGAFull : public ImageBase {
+
+
+ XWindow* m_pxWindow;
+
+ // DGA status
+ int m_iMajorVersion;
+ int m_iMinorVersion;
+ int m_iEventBase;
+ int m_iErrorBase;
+
+ int m_iScreen;
+
+ Display *m_pDisplay;
+
+ int m_iNumberModes;
+
+#ifdef X11_DGA2
+ XDGAMode *m_pDGAModes;
+ XDGADevice *m_pDevice;
+#endif
+
+ int m_iScreenWidth;
+ int m_iScreenHeight;
+
+ char *m_pAddr; // Base address of the screen
+
+ // DGA parameter
+ int m_iVideoWidth;
+ int m_iVideoHeight;
+
+ int m_iBytesPerLine;
+ int m_iBytesPerRow; // Size of one image line
+ int m_iBytesPerPixel;
+ int m_iOffsetScreen;
+ int m_iOffsetLine;
+ char *m_pStartAddr; // Start address for a centered image
+
+ int m_iImageMode;
+ int m_iMode;
+ bool m_bZoom;
+ bool m_bAllowZoom;
+
+ bool m_bIsActive;
+
+ int lSupport;
+ DitherWrapper* ditherWrapper;
+
+
+ public:
+ ImageDGAFull();
+ ~ImageDGAFull();
+
+ void init(XWindow *xWindow, YUVPicture* pic = NULL);
+
+ int support();
+
+ int openImage(int mode);
+ int closeImage();
+
+ void ditherImage(YUVPicture* pic);
+ void putImage();
+
+ int active() { return m_bIsActive; }
+
+ private:
+
+ // Tries to find a fullscreen-mode which matches the resolution best
+ int findMode(int width, int height, int bpp);
+
+ // Returns TRUE if an event is waiting
+ int event();
+
+ // Returns the start address of the upper left corner of the video frame
+ unsigned char *address();
+
+ // Number of bytes from the end of a row to the beginning of next one
+ int offset();
+
+ // Disables DGA-View (performs a mode-switch if neccesary)
+ void stop();
+
+
+
+};
+
+#endif
diff --git a/mpeglib/lib/util/render/x11/imageDeskX11.cpp b/mpeglib/lib/util/render/x11/imageDeskX11.cpp
new file mode 100644
index 00000000..9607d749
--- /dev/null
+++ b/mpeglib/lib/util/render/x11/imageDeskX11.cpp
@@ -0,0 +1,439 @@
+/*
+ standard and shared mem X11 images
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "imageDeskX11.h"
+
+#include <iostream>
+
+using namespace std;
+
+static int lXerror;
+
+static int dummy(Display* , XErrorEvent*) {
+ lXerror=true;
+ return true;
+}
+
+
+
+ImageDeskX11::ImageDeskX11() {
+ lSupport=true;
+ supportedModes = _IMAGE_DESK | _IMAGE_DOUBLE | _IMAGE_FULL;
+ setIdentifier("Standard X11");
+ xWindow = NULL;
+ ditherWrapper=NULL;
+#ifdef X11_XVIDMODE
+ iOldMode = -1;
+ vm_modelines = NULL;
+#endif
+}
+
+
+ImageDeskX11::~ImageDeskX11() {
+ destroyImage();
+ if (ditherWrapper != NULL) {
+ delete ditherWrapper;
+ }
+}
+
+
+void ImageDeskX11::init(XWindow* xWindow, YUVPicture*)
+{
+ videoaccesstype=VIDEO_XI_NONE;
+ this->xWindow=xWindow;
+ virtualscreen=NULL;
+ ximage=NULL;
+ imageMode=_IMAGE_NONE;
+ if (ditherWrapper == NULL) {
+ ditherWrapper=new DitherWrapper(xWindow->depth,
+ xWindow->redMask,
+ xWindow->greenMask,
+ xWindow->blueMask,
+ xWindow->pixel);
+ }
+
+#ifdef X11_SHARED_MEM
+ shmseginfo=NULL;
+#endif
+}
+
+int ImageDeskX11::support() {
+ return lSupport;
+}
+
+
+int ImageDeskX11::openImage(int mode) {
+
+ if (xWindow == NULL) {
+ cout << "ImageDeskX11::openImage - call init before open!" << endl;
+ return false;
+ }
+
+ closeImage();
+ imageMode = mode;
+ int err;
+
+ if ((err=createImage(VIDEO_XI_SHMSTD,imageMode)) != ERR_XI_OK) {
+ printf("\nX initialisation error:\n *** %s\n",ERR_XI_STR[err]);
+ printf("check ipcs and delete resources with ipcrm\n");
+ if ((err=createImage(VIDEO_XI_STANDARD,imageMode)) != ERR_XI_OK) {
+ printf("\nX initialisation error:\n *** %s\n",ERR_XI_STR[err]);
+ videoaccesstype=VIDEO_XI_NONE;
+ } else {
+ lSupport=true;
+ }
+ } else {
+ lSupport=true;
+ }
+ switch(videoaccesstype) {
+ case VIDEO_XI_STANDARD:
+ //printf(" # using conventional Xlib calls.\n\n");
+ break;
+ case VIDEO_XI_SHMSTD:
+ //printf(" # Using Xlib shared memory extension %d.%d\n\n",
+ //XShmMajor,XShmMinor);
+ break;
+ default:
+ cout << "could not create image->no video output possible"<<endl;
+
+ }
+
+ iOffsetX = iOffsetY = 0;
+ int w = xWindow->width;
+ int h = xWindow->height;
+ if (IS_FULL(imageMode)) {
+ switchMode(xWindow->width, xWindow->height, IS_DOUBLE(imageMode));
+ iOffsetX = (iWidth - w) / 2;
+ iOffsetY = (iHeight - h) / 2;
+ if (bZoom) {
+ iOffsetX -= w / 2;
+ iOffsetY -= h / 2;
+ }
+ XResizeWindow(xWindow->display, xWindow->window, iWidth, iHeight);
+ } else if (IS_DOUBLE(imageMode)) {
+ XResizeWindow(xWindow->display, xWindow->window,
+ xWindow->width * 2, xWindow->height * 2);
+ }
+
+ if (lSupport==true) {
+ return true;
+ }
+ return false;
+}
+
+
+int ImageDeskX11::closeImage() {
+ destroyImage();
+
+#ifdef X11_XVIDMODE
+ if (iOldMode != -1) {
+ cout << "switch back to original videomode" << endl;
+ XF86VidModeSwitchToMode(xWindow->display,XDefaultScreen(xWindow->display),
+ vm_modelines[iOldMode]);
+ XFlush(xWindow->display);
+ iOldMode=-1;
+ }
+#endif
+
+ return true;
+}
+
+
+void ImageDeskX11::ditherImage(YUVPicture* pic) {
+ if (xWindow == NULL) {
+ cout << "ImageDeskX11::ditherImage - you have to call init first!" << endl;
+ return;
+ }
+
+ ditherWrapper->doDither(pic,xWindow->depth,imageMode,
+ virtualscreen,0);
+}
+
+
+void ImageDeskX11::putImage(){
+ if (xWindow == NULL) {
+ cout << "ImageDeskX11::putImage - you have to call init first!" << endl;
+ return;
+ }
+
+
+ int height=xWindow->height;
+ int width=xWindow->width;
+
+ if (imageMode & _IMAGE_DOUBLE) {
+ height=2*height;
+ width=2*width;
+ }
+
+#ifdef X11_SHARED_MEM
+ switch(videoaccesstype) {
+ case VIDEO_XI_SHMSTD:
+ XShmPutImage(xWindow->display,xWindow->window,
+ xWindow->gc,ximage,
+ 0, 0, iOffsetX, iOffsetY, width, height, False);
+ XSync(xWindow->display,false); /* true not needed, done by XPending */
+ break;
+
+
+ case VIDEO_XI_STANDARD:
+#endif
+ XPutImage(xWindow->display,xWindow->window,
+ xWindow->gc, ximage,
+ 0, 0, iOffsetX, iOffsetY, width, height);
+ XSync(xWindow->display,false); /* true not needed, done by XPending */
+#ifdef X11_SHARED_MEM
+ break;
+ }
+#endif
+}
+
+
+
+int ImageDeskX11::createImage(int createType,int mode) {
+
+ if (xWindow == NULL) {
+ cout << "ImageDeskX11::createImage - you have to call init first!" << endl;
+ return false;
+ }
+
+ videoaccesstype=VIDEO_XI_NONE;
+
+#ifdef X11_SHARED_MEM
+ if(XShmQueryVersion(xWindow->display,&XShmMajor,&XShmMinor,&XShmPixmaps)) {
+ if (XShmPixmaps==True) {
+ if (createType & VIDEO_XI_SHMSTD) {
+ videoaccesstype=VIDEO_XI_SHMSTD;
+ }
+ }
+ } else {
+ if (createType & VIDEO_XI_SHMSTD) {
+ return ERR_XI_NOSHAREDMEMORY;
+ }
+ }
+#endif
+ if (videoaccesstype == VIDEO_XI_NONE) {
+ videoaccesstype=createType;
+ }
+
+ switch(videoaccesstype)
+ {
+#ifdef X11_SHARED_MEM
+
+
+ case VIDEO_XI_SHMSTD:
+
+ lXerror=false;
+ XSetErrorHandler(dummy);
+
+ shmseginfo=(XShmSegmentInfo *)malloc(sizeof(XShmSegmentInfo));
+ if(!shmseginfo)
+ return ERR_XI_SHMALLOC;
+
+ memset(shmseginfo,0, sizeof(XShmSegmentInfo));
+
+ if (imageMode & _IMAGE_DOUBLE) {
+ ximage=XShmCreateImage(xWindow->display,xWindow->visual,
+ xWindow->depth,
+ ZPixmap,NULL,shmseginfo,2*xWindow->width,
+ 2*xWindow->height);
+ } else {
+ ximage=XShmCreateImage(xWindow->display,xWindow->visual,
+ xWindow->depth,
+ ZPixmap,NULL,shmseginfo,xWindow->width,
+ xWindow->height);
+ }
+
+ if(!ximage)
+ return ERR_XI_SHMXIMAGE;
+
+ shmseginfo->shmid=shmget(IPC_PRIVATE,
+ ximage->bytes_per_line*
+ ximage->height,IPC_CREAT|0777);
+
+ if(shmseginfo->shmid<0)
+ return ERR_XI_SHMSEGINFO;
+
+ shmseginfo->shmaddr=(char*)shmat(shmseginfo->shmid,NULL,0);
+ ximage->data=shmseginfo->shmaddr;
+ virtualscreen=(unsigned char *)ximage->data;
+
+ if(!virtualscreen)
+ return ERR_XI_SHMVIRTALLOC;
+
+ shmseginfo->readOnly=False;
+
+ XShmAttach(xWindow->display,shmseginfo);
+ XSync(xWindow->display, False);
+ XSetErrorHandler(NULL);
+ XFlush(xWindow->display);
+ if (lXerror) {
+ cout << "ERR_XI_SHMATTACH -2"<<endl;
+ return ERR_XI_SHMATTACH;
+ }
+
+ break;
+#endif
+
+ case VIDEO_XI_STANDARD:
+ if (mode & _IMAGE_DOUBLE) {
+ virtualscreen=(unsigned char *)
+ malloc(xWindow->screensize*sizeof(char)*4);
+
+ if(virtualscreen==NULL)
+ return ERR_XI_VIRTALLOC;
+
+ ximage=XCreateImage(xWindow->display,xWindow->visual,
+ xWindow->depth,ZPixmap,
+ 0,(char*)virtualscreen,
+ 2*xWindow->width,2*xWindow->height,
+ 32,2*xWindow->width*xWindow->pixelsize);
+ } else {
+ virtualscreen=(unsigned char *)
+ malloc(xWindow->screensize*sizeof(char));
+
+ if(virtualscreen==NULL)
+ return ERR_XI_VIRTALLOC;
+
+ ximage=XCreateImage(xWindow->display,xWindow->visual,
+ xWindow->depth,ZPixmap,
+ 0,(char*)virtualscreen,
+ xWindow->width,xWindow->height,
+ 32,xWindow->width*xWindow->pixelsize);
+ }
+
+ if(!ximage)
+ return ERR_XI_XIMAGE;
+ break;
+
+ default:
+ return ERR_XI_FAILURE;
+
+ }
+
+ if ( (videoaccesstype == VIDEO_XI_STANDARD) ||
+ (videoaccesstype == VIDEO_XI_SHMSTD) ) {
+#ifndef WORDS_BIGENDIAN
+ ximage->byte_order = LSBFirst;
+ ximage->bitmap_bit_order = LSBFirst;
+#else
+ ximage->byte_order = MSBFirst;
+ ximage->bitmap_bit_order = MSBFirst;
+#endif
+
+ }
+ return ERR_XI_OK;
+}
+
+
+
+int ImageDeskX11::destroyImage() {
+ if(xWindow && xWindow->display && xWindow->window) {
+ switch(videoaccesstype) {
+#ifdef X11_SHARED_MEM
+ case VIDEO_XI_SHMSTD:
+ if (shmseginfo) {
+ XShmDetach(xWindow->display,shmseginfo);
+ if(ximage) {
+ XDestroyImage(ximage);
+ ximage=NULL;
+ }
+ if(shmseginfo->shmaddr) {
+ shmdt(shmseginfo->shmaddr);
+ shmseginfo->shmaddr=NULL;
+ }
+ if(shmseginfo->shmid>=0)
+ shmctl(shmseginfo->shmid,IPC_RMID,NULL);
+
+ free(shmseginfo);
+ }
+ shmseginfo=NULL;
+ break;
+
+#endif
+ case VIDEO_XI_STANDARD:
+ if(ximage) {
+ XDestroyImage(ximage);
+ ximage=NULL;
+ /*
+ XDestroyImage function calls frees both the image structure
+ and the data pointed to by the image structure.
+ */
+ virtualscreen=NULL;
+ }
+ break;
+
+ default:
+ // cout << "no open window to close"<<endl;
+ break;
+ }
+ }
+ videoaccesstype=VIDEO_XI_NONE;
+ imageMode=_IMAGE_NONE;
+ return true;
+}
+
+
+bool ImageDeskX11::switchMode(int width, int , bool zoom)
+{
+ iWidth = xWindow->screenptr->width;
+ iHeight = xWindow->screenptr->height;
+
+#ifdef X11_XVIDMODE
+ iOldMode = -1;
+ int vm_count,i;
+
+ cout << "Find best matching videomode ..." << endl;
+
+ if (!XF86VidModeGetAllModeLines(xWindow->display,XDefaultScreen(xWindow->display),
+ &vm_count,&vm_modelines)) {
+ return false;
+ }
+
+ int bestMode = -1;
+ int border, minBorder = INT_MAX;
+
+ for (i = 0; i < vm_count; i++) {
+ printf("mode %d: %dx%d\n",i, vm_modelines[i]->hdisplay,vm_modelines[i]->vdisplay);
+
+ if (xWindow->screenptr->width == vm_modelines[i]->hdisplay)
+ iOldMode = i;
+
+ border = vm_modelines[i]->hdisplay - width;
+ if ((border > 0) && (border < minBorder)) {
+ bestMode = i;
+ minBorder = border;
+ bZoom = false;
+ }
+ if (zoom) {
+ border = vm_modelines[i]->hdisplay - 2 * width;
+ if ((border > 0) && (border < minBorder)) {
+ bestMode = i;
+ minBorder = border;
+ bZoom = true;
+ }
+ }
+ }
+ cout << "best mode: " << bestMode << endl;
+
+ iWidth = vm_modelines[bestMode]->hdisplay;
+ iHeight = vm_modelines[bestMode]->vdisplay;
+
+ if (XF86VidModeSwitchToMode(xWindow->display,XDefaultScreen(xWindow->display),
+ vm_modelines[bestMode])) {
+ XF86VidModeSetViewPort(xWindow->display,XDefaultScreen(xWindow->display), 0, 0);
+ XFlush(xWindow->display);
+ return true;
+ }
+#endif
+ return false;
+}
diff --git a/mpeglib/lib/util/render/x11/imageDeskX11.h b/mpeglib/lib/util/render/x11/imageDeskX11.h
new file mode 100644
index 00000000..efbd4e90
--- /dev/null
+++ b/mpeglib/lib/util/render/x11/imageDeskX11.h
@@ -0,0 +1,85 @@
+/*
+ standard and shared mem X11 images
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __IMAGEDESKX11_H
+#define __IMAGEDESKX11_H
+#include <limits.h>
+#include "xinit.h"
+
+#include "../imageBase.h"
+
+#define VIDEO_XI_NONE 0x00 /* No access defined */
+#define VIDEO_XI_STANDARD 0x01 /* Use standard Xlib calls */
+#define VIDEO_XI_SHMSTD 0X02 /* Use Xlib shared memory extension */
+
+/**
+
+ Displays and renders X11 images in software with the help
+ of the ditherWrapper class.
+*/
+
+
+class ImageDeskX11 : public ImageBase {
+
+#ifdef X11_SHARED_MEM
+ XShmSegmentInfo *shmseginfo;
+#endif
+
+ unsigned char *virtualscreen;
+ int videoaccesstype;
+ XImage *ximage;
+ int lSupport;
+
+ int XShmMajor,XShmMinor;
+ Bool XShmPixmaps;
+
+ XWindow* xWindow;
+ int imageMode;
+ DitherWrapper* ditherWrapper;
+
+ int iOffsetX;
+ int iOffsetY;
+ int iWidth;
+ int iHeight;
+
+#ifdef X11_XVIDMODE
+ XF86VidModeModeInfo **vm_modelines;
+
+ int iOldMode;
+#endif
+
+ bool bZoom;
+
+ public:
+ ImageDeskX11();
+ ~ImageDeskX11();
+
+ void init(XWindow* xWindow, YUVPicture* pic = NULL);
+
+ int support();
+
+ int openImage(int ditherSize);
+ int closeImage();
+
+ void ditherImage(YUVPicture* pic);
+ void putImage();
+
+ private:
+ int createImage(int createType,int size);
+ int destroyImage();
+
+ bool switchMode(int width, int height, bool zoom);
+
+};
+
+#endif
diff --git a/mpeglib/lib/util/render/x11/imageXVDesk.cpp b/mpeglib/lib/util/render/x11/imageXVDesk.cpp
new file mode 100644
index 00000000..e087ba40
--- /dev/null
+++ b/mpeglib/lib/util/render/x11/imageXVDesk.cpp
@@ -0,0 +1,405 @@
+/*
+ xfree 4.0 XV extension desk mode
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include "imageXVDesk.h"
+
+#include <iostream>
+
+using namespace std;
+
+ImageXVDesk::ImageXVDesk() {
+
+ lSupport=false;
+
+ ditherWrapper=NULL;
+ supportedModes = _IMAGE_NONE;
+ setIdentifier("XV");
+
+ xWindow = NULL;
+
+#ifdef X11_XV
+ keepRatio = false;
+
+#endif
+}
+
+
+ImageXVDesk::~ImageXVDesk() {
+ if (ditherWrapper != NULL) {
+ delete ditherWrapper;
+ }
+ freeImage();
+}
+
+void ImageXVDesk::init(XWindow* xWindow, YUVPicture*)
+{
+#ifdef X11_XV
+ this->xWindow=xWindow;
+
+ xv_port=-1;
+ shmem_flag = 0;
+ yuv_image=NULL;
+ yuv_shminfo.shmaddr=NULL;
+ yuv_shminfo.shmid=-1;
+
+ if (XShmQueryExtension(xWindow->display)) shmem_flag = 1;
+ if (!shmem_flag) {
+ printf("no shmem available.\n");
+ return;
+ }
+
+
+ if (haveXVSupport(xWindow)==true) {
+ supportedModes = _IMAGE_DESK | _IMAGE_DOUBLE | _IMAGE_FULL | _IMAGE_RESIZE;
+ lSupport=true;
+ } else {
+ return;
+ }
+
+ if (ditherWrapper == NULL) {
+ ditherWrapper=new Dither2YUV();
+ }
+ imageID = -1;
+#endif
+}
+
+int ImageXVDesk::support() {
+ return lSupport;
+}
+
+int ImageXVDesk::openImage(int imageMode) {
+
+
+ if (imageMode & _IMAGE_FULL) {
+ XResizeWindow(xWindow->display, xWindow->window,
+ xWindow->screenptr->width, xWindow->screenptr->height);
+ setKeepRatio(true);
+ } else if (imageMode & _IMAGE_DOUBLE) {
+ XResizeWindow(xWindow->display, xWindow->window,
+ xWindow->width * 2, xWindow->height * 2);
+ setKeepRatio(false);
+ } else {
+ setKeepRatio(false);
+ }
+
+ return true;
+}
+
+
+int ImageXVDesk::closeImage() {
+ freeImage();
+ return true;
+}
+
+void ImageXVDesk::ditherImage(YUVPicture* pic) {
+
+#ifdef X11_XV
+ int x_return;
+ int y_return;
+ int height, dy;
+ unsigned int border_width_return;
+ unsigned int depth_return;
+ unsigned int _w;
+ unsigned int _h;
+ Window _dw;
+
+ if (xWindow == NULL) {
+ cout << "ImageXVDesk::ditherImage - you have to call before dithering an image!" << endl;
+ return;
+ }
+
+ // check for not supported formats and if possible convert them
+ int inputType=pic->getImageType();
+ if (inputType == PICTURE_RGB_FLIPPED) {
+ cout << "xv for flipped rgb not implemented"<<endl;
+ return;
+ }
+
+ // create xv image
+ int id;
+ if (imageID != pic->getImageType()) {
+ imageID = pic->getImageType();
+ switch (imageID) {
+ case PICTURE_YUVMODE_CR_CB:
+ case PICTURE_YUVMODE_CB_CR:
+ case PICTURE_RGB:
+ id = GUID_YUV12_PLANAR;
+ break;
+ case PICTURE_YUVMODE_YUY2:
+ id = GUID_YUY2_PACKED;
+ break;
+ case PICTURE_YUVMODE_UYVY:
+ id = GUID_UYVY_PACKED;
+ break;
+ default:
+ cout << "unknown type for yuv image!" << endl;
+ return;
+ }
+ freeImage();
+
+ createImage(id);
+ }
+
+ XGetGeometry(xWindow->display,(Drawable)xWindow->window,
+ &_dw, &x_return, &y_return, &_w, &_h,
+ &border_width_return, &depth_return);
+
+ // now dither the image
+
+ // we (currently) cannot create yuvPicture _in_
+ // the shared segment here we copy it
+
+ unsigned char* image=pic->getImagePtr();
+ if (inputType == PICTURE_RGB) {
+ ditherWrapper->doDither(pic,
+ DefaultDepth(xWindow->display,xWindow->screennum),
+ _SIZE_NORMAL, (unsigned char*) yuv_image->data, 0);
+ } else {
+ memcpy(yuv_image->data,image,pic->getImageSize());
+ }
+
+ if (keepRatio) {
+ height = (_w * yuv_image->height) / yuv_image->width;
+ dy = (((int) _h) - height + 1) / 2;
+ XvShmPutImage(xWindow->display, xv_port,xWindow->window,
+ xWindow->gc, yuv_image,
+ 0, 0, yuv_image->width, yuv_image->height,
+ 0, dy, _w, height, False);
+ if (dy > 0) {
+ XFillRectangle(xWindow->display, xWindow->window,xWindow->gc,
+ 0, 0, _w, dy);
+ XFillRectangle(xWindow->display, xWindow->window,xWindow->gc,
+ 0, height+dy-1, _w, dy+1);
+ }
+ } else {
+ XvShmPutImage(xWindow->display, xv_port,xWindow->window,
+ xWindow->gc, yuv_image,
+ 0, 0, yuv_image->width, yuv_image->height,
+ 0, 0, _w, _h, False);
+ }
+#endif
+}
+
+
+void ImageXVDesk::putImage() {
+
+ //XFlush(xWindow->display);
+ XSync(xWindow->display, false);
+}
+
+void ImageXVDesk::setKeepRatio(bool enable)
+{
+#ifdef X11_XV
+ keepRatio = enable;
+#endif
+}
+
+
+int ImageXVDesk::haveXVSupport(XWindow* xWindow) {
+#ifdef X11_XV
+ int ret;
+ unsigned int p_version=0;
+ unsigned int p_release=0;
+ unsigned int p_request_base=0;
+ unsigned int p_event_base=0;
+ unsigned int p_error_base=0;
+
+ unsigned int p_num_adaptors=0;
+
+ /**------------------------------- XV ------------------------------------*/
+
+ /** query and print Xvideo properties */
+
+ ret = XvQueryExtension(xWindow->display,
+ &p_version, &p_release, &p_request_base,
+ &p_event_base, &p_error_base);
+ if (ret != Success) {
+ if (ret == XvBadExtension) {
+ printf("XvBadExtension returned at XvQueryExtension.\n");
+ } else if (ret == XvBadAlloc) {
+ printf("XvBadAlloc returned at XvQueryExtension.\n");
+ } else {
+ printf("other error happened at XvQueryExtension.\n");
+ }
+ return false;
+ }
+ /*
+ printf("========================================\n");
+ printf("XvQueryExtension returned the following:\n");
+ printf("p_version : %u\n", p_version);
+ printf("p_release : %u\n", p_release);
+ printf("p_request_base : %u\n", p_request_base);
+ printf("p_event_base : %u\n", p_event_base);
+ printf("p_error_base : %u\n", p_error_base);
+ printf("========================================\n");
+ */
+
+ ret = XvQueryAdaptors(xWindow->display, DefaultRootWindow(xWindow->display),
+ &p_num_adaptors, &ai);
+
+ if (ret != Success) {
+ if (ret == XvBadExtension) {
+ printf("XvBadExtension returned at XvQueryExtension.\n");
+ } else if (ret == XvBadAlloc) {
+ printf("XvBadAlloc returned at XvQueryExtension.\n");
+ } else {
+ printf("other error happaned at XvQueryAdaptors.\n");
+ }
+ return false;
+ }
+ /*
+ printf("=======================================\n");
+ printf("XvQueryAdaptors returned the following:\n");
+ printf("%d adaptors available.\n", p_num_adaptors);
+ */
+ if (p_num_adaptors == 0) {
+ //cout << "no adaptors found. XV not possible"<<endl;
+ return false;
+ }
+
+ unsigned int i;
+ unsigned int j;
+
+ for (i = 0; i < p_num_adaptors; i++) {
+ /*
+ printf(" name: %s\n"
+ " type: %s%s%s%s%s\n"
+ " ports: %ld\n"
+ " first port: %ld\n",
+ ai[i].name,
+ (ai[i].type & XvInputMask) ? "input | " : "",
+ (ai[i].type & XvOutputMask) ? "output | " : "",
+ (ai[i].type & XvVideoMask) ? "video | " : "",
+ (ai[i].type & XvStillMask) ? "still | " : "",
+ (ai[i].type & XvImageMask) ? "image | " : "",
+ ai[i].num_ports,
+ ai[i].base_id);
+ */
+ xv_port = ai[i].base_id;
+
+ //printf("adaptor %d ; format list:\n", i);
+ for (j = 0; j < ai[i].num_formats; j++) {
+ /*
+ printf(" depth=%d, visual=%ld\n",
+ ai[i].formats[j].depth,
+ ai[i].formats[j].visual_id);
+ */
+ }
+ unsigned int p;
+ unsigned int encodings;
+ int attributes;
+ int formats;
+
+ for (p = ai[i].base_id; p < ai[i].base_id+ai[i].num_ports; p++) {
+
+ //printf(" encoding list for port %d\n", p);
+ if (XvQueryEncodings(xWindow->display, p, &encodings, &ei) != Success) {
+ //printf("XvQueryEncodings failed.\n");
+ continue;
+ }
+ for (j = 0; j < encodings; j++) {
+ /*
+ printf(" id=%ld, name=%s, size=%ldx%ld, numerator=%d, denominator=%d\n",
+ ei[j].encoding_id, ei[j].name, ei[j].width, ei[j].height,
+ ei[j].rate.numerator, ei[j].rate.denominator);
+ */
+ }
+ XvFreeEncodingInfo(ei);
+ int k;
+ //printf(" attribute list for port %d\n", p);
+ at = XvQueryPortAttributes(xWindow->display, p, &attributes);
+ for (k = 0; k < attributes; k++) {
+ /*
+ printf(" name: %s\n"
+ " flags: %s%s\n"
+ " min_color: %i\n"
+ " max_color: %i\n",
+ at[k].name,
+ (at[k].flags & XvGettable) ? " get" : "",
+ (at[k].flags & XvSettable) ? " set" : "",
+ at[k].min_value, at[k].max_value);
+ */
+ }
+ if (at)
+ XFree(at);
+
+ //printf(" image format list for port %d\n", p);
+ fo = XvListImageFormats(xWindow->display, p, &formats);
+ for (k = 0; k < formats; k++) {
+ /*
+ printf(" 0x%x (%4.4s) %s\n",
+ fo[k].id,
+ (char *)&fo[k].id,
+ (fo[k].format == XvPacked) ? "packed" : "planar");
+ */
+ }
+ if (fo)
+ XFree(fo);
+ }
+ printf("\n");
+ }
+ if (p_num_adaptors > 0)
+ XvFreeAdaptorInfo(ai);
+ if (xv_port == -1) {
+ return false;
+ }
+#endif
+ return true;
+
+}
+
+
+void ImageXVDesk::freeImage() {
+#ifdef X11_XV
+ if (xWindow == NULL) {
+ return;
+ }
+ if (yuv_shminfo.shmid >=0) {
+ XShmDetach(xWindow->display,&yuv_shminfo);
+ if(yuv_shminfo.shmaddr) {
+ shmdt(yuv_shminfo.shmaddr);
+ XFree(yuv_image);
+ yuv_shminfo.shmaddr=NULL;
+ }
+ XSync(xWindow->display, False);
+ yuv_shminfo.shmid=-1;
+ }
+#endif
+}
+
+
+void ImageXVDesk::createImage(int id) {
+#ifdef X11_XV
+ if (xWindow == NULL) {
+ cout << "ImageXVDesk::freeImage - you have to call init before creating an image!" << endl;
+ return;
+ }
+
+ yuv_image = XvShmCreateImage(xWindow->display, xv_port,
+ id, 0,
+ xWindow->width,
+ xWindow->height, &yuv_shminfo);
+
+ yuv_shminfo.shmid = shmget(IPC_PRIVATE,
+ yuv_image->data_size, IPC_CREAT | 0777);
+ yuv_shminfo.shmaddr = yuv_image->data =
+ (char*)shmat(yuv_shminfo.shmid, 0, 0);
+ yuv_shminfo.readOnly = False;
+
+ if (!XShmAttach(xWindow->display, &yuv_shminfo)) {
+ printf("XShmAttach failed !\n");
+ lSupport=false;
+ return;
+ }
+ shmctl(yuv_shminfo.shmid, IPC_RMID, 0);
+#endif
+}
diff --git a/mpeglib/lib/util/render/x11/imageXVDesk.h b/mpeglib/lib/util/render/x11/imageXVDesk.h
new file mode 100644
index 00000000..44124428
--- /dev/null
+++ b/mpeglib/lib/util/render/x11/imageXVDesk.h
@@ -0,0 +1,88 @@
+/*
+ xfree 4.0 XV extension desk mode
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __IMAGEXVDESK_H
+#define __IMAGEXVDESK_H
+
+#include "xinit.h"
+
+#include "../imageBase.h"
+#include "../dither2YUV/dither2YUV.h"
+
+#include <stdio.h>
+
+#if !defined(__NetBSD__)
+#include <semaphore.h>
+#endif
+
+//#undef X11_XV
+
+#define GUID_YUV12_PLANAR 0x32315659
+#define GUID_I420_PLANAR 0x30323449
+#define GUID_YUY2_PACKED 0x32595559
+#define GUID_UYVY_PACKED 0x59565955
+
+/**
+ The XV extension dither yuv images in hardware and allows
+ scaling of images.
+ But its currently not supported by many drivers.
+
+*/
+
+
+class ImageXVDesk : public ImageBase {
+
+#ifdef X11_XV
+ XvAdaptorInfo *ai;
+ XvEncodingInfo *ei;
+ XvAttribute *at;
+ XvImageFormatValues *fo;
+
+ XvImage *yuv_image;
+ bool keepRatio;
+
+ int xv_port;
+ int imageID;
+
+ int shmem_flag;
+ XShmSegmentInfo yuv_shminfo;
+#endif
+ Dither2YUV* ditherWrapper;
+
+ int lSupport;
+ XWindow* xWindow;
+
+ public:
+ ImageXVDesk();
+ ~ImageXVDesk();
+
+ void init(XWindow* xWindow, YUVPicture* pic = NULL);
+
+ int support();
+
+ int openImage(int imageMode);
+ int closeImage();
+
+ void ditherImage(YUVPicture* pic);
+ void putImage();
+
+ void setKeepRatio(bool enable);
+
+ private:
+ int haveXVSupport(XWindow* xWindow);
+ void freeImage();
+ void createImage(int id);
+
+};
+
+#endif
diff --git a/mpeglib/lib/util/render/x11/initDisplay.cpp b/mpeglib/lib/util/render/x11/initDisplay.cpp
new file mode 100644
index 00000000..d0029eb6
--- /dev/null
+++ b/mpeglib/lib/util/render/x11/initDisplay.cpp
@@ -0,0 +1,255 @@
+/*
+ here are the different initialisation routines for different displays
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "initDisplay.h"
+
+#include <iostream>
+
+using namespace std;
+
+
+
+static unsigned long wpixel[256];
+
+
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * InitColorDisplay --
+ *
+ * Initialized display for full color output.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+void initColorDisplay(XWindow* xwindow) {
+ XWindowAttributes winattr;
+
+
+ XGetWindowAttributes(xwindow->display, xwindow->window, &winattr);
+
+ xwindow->redMask = winattr.visual->red_mask;
+ xwindow->greenMask = winattr.visual->green_mask;
+ xwindow->blueMask = winattr.visual->blue_mask;
+}
+
+
+
+
+
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * FindFullColorVisual
+ *
+ * Returns a pointer to a full color bit visual on the display
+ *
+ * Results:
+ * See above.
+ *
+ * Side effects:
+ * Unknown.
+ *
+ *--------------------------------------------------------------
+ */
+Visual* FindFullColorVisual (Display* dpy,int* depth) {
+ XVisualInfo vinfo;
+ XVisualInfo *vinfo_ret;
+ int numitems, maxdepth;
+
+#if defined(__cplusplus) || defined(c_plusplus)
+ vinfo.c_class = TrueColor;
+#else
+ vinfo.class = TrueColor;
+#endif
+
+ vinfo_ret = XGetVisualInfo(dpy, VisualClassMask, &vinfo, &numitems);
+
+ if (numitems == 0) return NULL;
+
+ maxdepth = 0;
+ while(numitems > 0) {
+ if (vinfo_ret[numitems-1].depth > maxdepth) {
+ maxdepth = vinfo_ret[numitems-1 ].depth;
+ }
+ numitems--;
+ }
+ XFree((void *) vinfo_ret);
+
+ if (maxdepth < 16) return NULL;
+
+ if (XMatchVisualInfo(dpy, DefaultScreen(dpy), maxdepth,
+ TrueColor, &vinfo)) {
+ *depth = maxdepth;
+ return vinfo.visual;
+ }
+
+ return NULL;
+}
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * CreateFullColorWindow
+ *
+ * Creates a window capable of handling 32 bit color.
+ *
+ * Results:
+ * See above.
+ *
+ * Side effects:
+ * Unknown.
+ *
+ *--------------------------------------------------------------
+ */
+void CreateFullColorWindow (XWindow* xwindow) {
+ int depth;
+ Visual *visual;
+ XSetWindowAttributes xswa;
+ unsigned long mask;
+ unsigned int c_class;
+ int screen;
+ Display *dpy=xwindow->display;
+ /*
+ int x = xinfo->hints.x,
+ y = xinfo->hints.y;
+ unsigned int w = xinfo->hints.width,
+ h = xinfo->hints.height;
+ */
+ screen = XDefaultScreen(dpy);
+ c_class = InputOutput; /* Could be InputOnly */
+ if (xwindow->visual == NULL) {
+ xwindow->visual = visual = FindFullColorVisual (dpy, &depth);
+ xwindow->depth = depth;
+ } else {
+ visual=xwindow->visual;
+ depth=xwindow->depth;
+ }
+
+ if (visual == NULL) {
+ cout << "visual is null"<<endl;
+ return;
+ }
+ mask = CWBackPixel | CWColormap | CWBorderPixel;
+ if (xwindow->colormap==0) {
+ xswa.colormap = XCreateColormap(dpy,
+ XRootWindow(dpy, screen),
+ visual, AllocNone);
+ } else xswa.colormap = xwindow->colormap;
+ xswa.background_pixel = BlackPixel(dpy, DefaultScreen(dpy));
+ xswa.border_pixel = WhitePixel(dpy, DefaultScreen(dpy));
+ XSetWindowColormap(xwindow->display,xwindow->window,xwindow->colormap);
+
+
+ /*
+ xwindow->window = XCreateWindow(dpy, RootWindow(dpy, screen), x, y, w, h,
+ (unsigned int) 1, depth, c_class,
+ visual, mask, &xswa);
+ */
+}
+
+
+
+
+
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * InitSimpleDisplay --
+ *
+ * Initialized display, sets up colormap, etc. Use for 8 Bit color
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+void initSimpleDisplay(XWindow* xwindow) {
+ int ncolors = LUM_RANGE*CB_RANGE*CR_RANGE;
+ XColor xcolor;
+ int i, lum_num, cr_num, cb_num;
+ unsigned char r, g, b;
+ Colormap dcmap;
+ Display *display;
+ ColorTable8Bit colorTable8Bit;
+
+ display = xwindow->display;
+
+
+ xwindow->colormap = XDefaultColormap(display, DefaultScreen(display));
+ dcmap = xwindow->colormap;
+
+ xcolor.flags = DoRed | DoGreen | DoBlue;
+
+
+ //if (xinfo->owncmFlag) goto create_map;
+
+retry_alloc_colors:
+ for (i=0; i<ncolors; i++) {
+
+ lum_num = (i / (CR_RANGE*CB_RANGE))%LUM_RANGE;
+ cr_num = (i / CB_RANGE)%CR_RANGE;
+ cb_num = i % CB_RANGE;
+
+ colorTable8Bit.ConvertColor(lum_num, cr_num, cb_num, &r, &g, &b);
+
+ xcolor.red = r * 256;
+ xcolor.green = g * 256;
+ xcolor.blue = b * 256;
+
+ if ((XAllocColor(display,xwindow->colormap , &xcolor) == 0
+ && xwindow->colormap == dcmap)) {
+ int j;
+ unsigned long tmp_pixel;
+ XWindowAttributes xwa;
+
+ // Free colors.
+ for (j = 0; j < i; j ++) {
+ tmp_pixel = wpixel[j];
+ XFreeColors(display,xwindow->colormap , &tmp_pixel, 1, 0);
+ }
+
+
+ //create_map:
+ XGetWindowAttributes(display, xwindow->window, &xwa);
+ xwindow->colormap = XCreateColormap(display, xwindow->window,
+ xwa.visual, AllocNone);
+ XSetWindowColormap(display, xwindow->window,xwindow->colormap );
+
+ goto retry_alloc_colors;
+ }
+ xwindow->pixel[i]=xcolor.pixel;
+ wpixel[i] = xcolor.pixel;
+ }
+
+}
+
+
diff --git a/mpeglib/lib/util/render/x11/initDisplay.h b/mpeglib/lib/util/render/x11/initDisplay.h
new file mode 100644
index 00000000..62841f19
--- /dev/null
+++ b/mpeglib/lib/util/render/x11/initDisplay.h
@@ -0,0 +1,34 @@
+/*
+ here are the different initialisation routines for different displays
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __INITDISPLAY_H
+#define __INITDISPLAY_H
+
+#include "math.h"
+#include "xinit.h"
+#include "../dither/colorTable8Bit.h"
+
+
+
+extern void initColorDisplay(XWindow* xwindow);
+extern void initSimpleDisplay(XWindow* xwindow);
+
+// helper functions
+Visual *FindFullColorVisual (Display *dpy ,int *depth);
+void CreateFullColorWindow (XWindow* xwindow);
+
+
+
+#endif
diff --git a/mpeglib/lib/util/render/x11/x11Surface.cpp b/mpeglib/lib/util/render/x11/x11Surface.cpp
new file mode 100644
index 00000000..d7b8f052
--- /dev/null
+++ b/mpeglib/lib/util/render/x11/x11Surface.cpp
@@ -0,0 +1,389 @@
+/*
+ surface wrapper for X11 Window
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "x11Surface.h"
+
+#include <iostream>
+
+using namespace std;
+
+const char *ERR_XI_STR[] = {
+ "X initialisation OK!",
+ "No Shared Memory available",
+ "cannot open Display",
+ "bad color depth",
+ "can't create Window",
+ "can't alloc memory for virtual screen",
+ "cannot create XImage",
+ "can't alloc memory for Shared memory segment info",
+ "cannot create Shared Memory XImage",
+ "Shared memory segment info error",
+ "Shared memory virtual screen allocation failed",
+ "cannot attach Shared Memory segment to display"
+};
+
+
+#ifndef KDE_USE_FINAL
+static int dummy(Display* , XErrorEvent*) {
+ cout << "received x11 error!"<<endl;
+ return true;
+}
+#endif
+
+X11Surface::X11Surface() {
+ xWindow=(XWindow *)malloc(sizeof(XWindow));
+ xWindow->lOpen=false;
+ xWindow->x = xWindow->y = 0;
+ xWindow->window = 0;
+ m_windowIdAvailable = false;
+
+ imageMode=_IMAGE_NONE;
+
+ imageCurrent=NULL;
+ xWindow->lOpen=false;
+
+
+ xWindow->display=XOpenDisplay(NULL);
+ if (xWindow->display)
+ XFlush(xWindow->display);
+ xWindow->redMask=0;
+ xWindow->greenMask=0;
+ xWindow->blueMask=0;
+ lXVAllow=true;
+
+ images=0;
+ imageList = new ImageBase* [4];
+ imageList[images++] = new ImageXVDesk();
+ imageList[images++] = new ImageDGAFull();
+ imageList[images++] = new ImageDeskX11();
+ imageList[images] = NULL;
+}
+
+
+X11Surface::~X11Surface() {
+ close();
+ if (xWindow->display)
+ XCloseDisplay(xWindow->display);
+ free(xWindow);
+
+ for (int count=0 ; count<images ; count++) {
+ if (imageList[count] != NULL)
+ delete imageList[count];
+ }
+ delete [] imageList;
+}
+
+
+int X11Surface::getHeight() {
+ return xWindow->height;
+}
+
+
+int X11Surface::getWidth() {
+ return xWindow->width;
+}
+
+int X11Surface::isOpen() {
+ return xWindow->lOpen;
+}
+
+int X11Surface::x11WindowId() {
+ if(m_windowIdAvailable)
+ return xWindow->window;
+ else
+ return -1;
+}
+
+int X11Surface::open(int width, int height,const char *title, bool border) {
+
+ close();
+ xWindow->width=width;
+ xWindow->height=height;
+
+ if(!xWindow->display) {
+ printf("\nX initialisation error:\n *** %s\n",ERR_XI_STR[ERR_XI_DISPLAY]);
+ printf("check ipcs and delete resources with ipcrm\n");
+ exit(0);
+ }
+
+ xWindow->screennum=DefaultScreen(xWindow->display);
+ xWindow->screenptr=DefaultScreenOfDisplay(xWindow->display);
+ xWindow->visual=DefaultVisualOfScreen(xWindow->screenptr);
+ xWindow->depth=DefaultDepth(xWindow->display,xWindow->screennum);
+
+ switch(xWindow->depth) {
+ case 8:
+ xWindow->pixelsize=1;
+ break;
+ case 16:
+ xWindow->pixelsize=2;
+ break;
+ case 24:
+ xWindow->pixelsize=4;
+ break;
+ case 32:
+ xWindow->pixelsize=4;
+ break;
+ default:
+ cout << "unknown pixelsize for depth:"<<xWindow->depth<<endl;
+ exit(0);
+ }
+
+ XColor background, ignored;
+ XAllocNamedColor (xWindow->display,
+ DefaultColormap (xWindow->display, xWindow->screennum),
+ "black", &background, &ignored);
+
+ XSetWindowAttributes attributes;
+ attributes.background_pixel=background.pixel;
+ attributes.backing_store=NotUseful;
+ attributes.override_redirect=True;
+
+ xWindow->window=XCreateWindow(xWindow->display,
+ RootWindowOfScreen(xWindow->screenptr),
+ 0,0,
+ xWindow->width,
+ xWindow->height,0,
+ xWindow->depth,
+ InputOutput, xWindow->visual,
+ (border) ? CWBackingStore : CWBackPixel|CWOverrideRedirect,
+ &attributes);
+
+ m_windowIdAvailable = true;
+ if(!xWindow->window) {
+ printf("\nX initialisation error:\n *** %s\n",ERR_XI_STR[ERR_XI_WINDOW]);
+ printf("check ipcs and delete resources with ipcrm\n");
+ return false;
+ }
+
+ WM_DELETE_WINDOW = XInternAtom(xWindow->display, "WM_DELETE_WINDOW", False);
+ XSetWMProtocols(xWindow->display, xWindow->window, &WM_DELETE_WINDOW, 1);
+
+ XSetErrorHandler(dummy);
+
+ XStoreName(xWindow->display,xWindow->window,title);
+ XSelectInput(xWindow->display,xWindow->window,
+ ExposureMask|KeyPressMask|KeyReleaseMask|ButtonPressMask);
+ xWindow->gc=XCreateGC(xWindow->display,xWindow->window,0,NULL);
+ XMapRaised(xWindow->display,xWindow->window);
+
+ if (xWindow->depth >= 16) {
+ initColorDisplay(xWindow);
+
+ } else {
+ // depth is <= 8
+ // allocate memory for dithertables
+ // gets the rgb masks
+ initColorDisplay(xWindow);
+ // create 8 bit dithertables
+ // create private colormap
+ initSimpleDisplay(xWindow);
+
+ }
+
+ xWindow->palette=NULL;
+ xWindow->screensize=xWindow->height*xWindow->width*xWindow->pixelsize;
+ xWindow->lOpen=true;
+
+ for (int count=0 ; count<images ; count++) {
+ if (imageList[count] != NULL)
+ imageList[count]->init(xWindow);
+ }
+
+ return true;
+}
+
+
+int X11Surface::close() {
+ if (isOpen()==false) {
+ return true;
+ }
+ closeImage();
+
+ XFreeGC(xWindow->display,xWindow->gc);
+ XDestroyWindow(xWindow->display,xWindow->window);
+
+ xWindow->lOpen=false;
+
+
+ return true;
+}
+
+ImageBase *X11Surface::findImage(int mode) {
+ for (int count=0 ; count<images ; count++) {
+ if ((imageList[count] == NULL) || (IS_DISABLED(imageList[count])))
+ continue;
+
+ if (imageList[count]->supportedModes & mode)
+ return imageList[count];
+ }
+ return NULL;
+}
+
+ImageBase **X11Surface::getModes() {
+ return imageList;
+}
+
+void X11Surface::setModes(ImageBase **modes) {
+ imageList = modes;
+}
+
+int X11Surface::openImage(int mode, YUVPicture*) {
+ if (imageMode != _IMAGE_NONE) {
+ cout << "bad open error X11Surface::openImage"<<endl;
+ return false;
+ }
+ if (mode == _IMAGE_NONE) {
+ cout << "X11Surface::openImage - no valid mode specified"<<endl;
+ return false;
+ }
+
+ ImageBase *newImage=findImage(mode);
+
+ if (newImage == NULL) {
+ cout << " X11Surface::openImage - no matching image found"<<endl;
+ imageMode=_IMAGE_NONE;
+ } else {
+ /*
+ printf("Best image found: %s\n", newImage->getIdentifier());
+ printf("\tsupported modes: desk=%d, double=%d, full=%d, resize=%d\n",
+ HAS_DESK(newImage),
+ HAS_DOUBLE(newImage),
+ HAS_FULL(newImage),
+ HAS_RESIZE(newImage));
+ */
+
+ open(xWindow->width, xWindow->height, "mpeglib", !(mode & _IMAGE_FULL));
+ newImage->openImage(mode);
+ if (!IS_FULL(mode)) {
+ XMoveWindow(xWindow->display, xWindow->window,
+ xWindow->x, xWindow->y);
+
+ XSizeHints hints;
+ hints.flags = PMaxSize;
+ if (HAS_RESIZE(newImage)) {
+ hints.max_width = INT_MAX;
+ hints.max_height = INT_MAX;
+ } else {
+ hints.max_width = xWindow->width;
+ hints.max_height = xWindow->height;
+ }
+ XSetWMNormalHints(xWindow->display, xWindow->window, &hints);
+ }
+
+ imageMode=mode;
+ }
+ imageCurrent = newImage;
+ XSync(xWindow->display,true);
+ return (imageCurrent != NULL);
+}
+
+
+int X11Surface::closeImage() {
+
+ if ((imageMode == _IMAGE_NONE) || (!xWindow->lOpen))
+ return false;
+
+ ImageBase *old = imageCurrent;
+ imageCurrent=NULL;
+
+ XWindowAttributes attr;
+ Window junkwin;
+
+ if (!IS_FULL(imageMode)) {
+ if (!XGetWindowAttributes(xWindow->display, xWindow->window, &attr))
+ cout << "Can't get window attributes." << endl;
+
+ XTranslateCoordinates (xWindow->display, xWindow->window, attr.root,
+ -attr.border_width,
+ -attr.border_width,
+ &xWindow->x, &xWindow->y, &junkwin);
+ }
+
+ imageMode=_IMAGE_NONE;
+ old->closeImage();
+
+ return true;
+}
+
+
+int X11Surface::dither(YUVPicture* pic) {
+ if (imageCurrent != NULL) {
+ imageCurrent->ditherImage(pic);
+ }
+ return true;
+}
+
+
+int X11Surface::putImage(YUVPicture* ) {
+ if (imageCurrent != NULL) {
+ imageCurrent->putImage();
+ }
+ return true;
+}
+
+
+int X11Surface::getDepth() {
+ return xWindow->depth;
+}
+
+int X11Surface::getImageMode() {
+ return imageMode;
+}
+
+int X11Surface::checkEvent(int* newMode) {
+ XEvent event;
+
+ if (isOpen()==false)
+ return false;
+
+ // check if we forward the call to the FULLSCREEN mode
+ if (!imageCurrent->active()) {
+ if (IS_FULL(imageMode)) {
+ *newMode=imageMode ^ _IMAGE_FULL;
+ return true;
+ }
+ }
+
+ // normal X11 images use the X11 event queue
+ if (XCheckTypedWindowEvent(xWindow->display,
+ xWindow->window,ButtonPress,&event)) {
+ if (event.xbutton.button == Button1) {
+ if (findImage(_IMAGE_DOUBLE) != NULL)
+ *newMode = imageMode ^ _IMAGE_DOUBLE;
+ } else if (event.xbutton.button == Button3) {
+ if (findImage(_IMAGE_FULL) != NULL)
+ *newMode = imageMode ^ _IMAGE_DESK ^ _IMAGE_FULL;
+ }
+ return true;
+ }
+ // now check if there are unneeded events in the queue,
+ // then delete them
+ int eventCnt=XPending(xWindow->display);
+ if (eventCnt > 10) {
+ XSync(xWindow->display,true);
+ }
+ return false;
+
+
+}
+
+
+
+void X11Surface::config(const char* key,
+ const char* value, void* ) {
+ if (strcmp(key,"xvAllow")==0) {
+ lXVAllow=atoi(value);
+ }
+}
+
diff --git a/mpeglib/lib/util/render/x11/x11Surface.h b/mpeglib/lib/util/render/x11/x11Surface.h
new file mode 100644
index 00000000..54a6582d
--- /dev/null
+++ b/mpeglib/lib/util/render/x11/x11Surface.h
@@ -0,0 +1,79 @@
+/*
+ surface wrapper for X11 Window
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __X11SURFACE_H
+#define __X11SURFACE_H
+
+#include <limits.h>
+#include "xinit.h"
+#include "../surface.h"
+#include "initDisplay.h"
+#include "../dither/ditherWrapper.h"
+#include "imageDeskX11.h"
+#include "imageDGAFull.h"
+#include "imageXVDesk.h"
+
+
+
+class X11Surface : public Surface {
+
+ int lOpen;
+ int imageMode;
+
+ XWindow* xWindow;
+
+ ImageBase** imageList;
+ int images;
+
+ ImageBase* imageCurrent;
+
+ Atom WM_DELETE_WINDOW;
+ Atom WM_RESIZE_WINDOW;
+ int lXVAllow;
+
+ public:
+ X11Surface();
+ ~X11Surface();
+
+ int isOpen();
+ int open(int width, int height,const char *title, bool border = false);
+ int close();
+ int getHeight();
+ int getWidth();
+ int getDepth();
+ int getImageMode();
+ int x11WindowId();
+
+ ImageBase *findImage(int mode);
+
+ // these functions grant access to the supported images. be careful when changing
+ // entries, because these are no copies. they are the original values!
+ ImageBase **getModes();
+ void setModes(ImageBase **modes);
+
+ int openImage(int mode, YUVPicture* pic = NULL);
+ int closeImage();
+ int dither(YUVPicture* pic);
+ int putImage(YUVPicture* pic);
+
+ int checkEvent(int* mode);
+
+ void config(const char* key,
+ const char* value,void* user_data);
+
+
+ private:
+ int initX11();
+ bool m_windowIdAvailable;
+};
+#endif
diff --git a/mpeglib/lib/util/render/x11/xinit.h b/mpeglib/lib/util/render/x11/xinit.h
new file mode 100644
index 00000000..c42c290f
--- /dev/null
+++ b/mpeglib/lib/util/render/x11/xinit.h
@@ -0,0 +1,99 @@
+
+#ifndef __XINIT_H__
+#define __XINIT_H__
+
+#define __USE_X_SHAREDMEMORY__
+
+#include <pthread.h>
+
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+#include <X11/Xatom.h>
+
+
+
+#ifdef X11_SHARED_MEM
+#include <X11/extensions/XShm.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#endif
+
+
+#ifdef X11_XV
+#include <X11/extensions/Xv.h>
+#include <X11/extensions/Xvlib.h>
+#include <X11/extensions/XShm.h>
+#endif
+
+#ifdef X11_XVIDMODE
+#include <X11/extensions/xf86vmode.h>
+#endif
+
+#ifdef X11_DGA2
+#include <X11/extensions/xf86dga.h>
+#endif
+
+#define ERR_XI_FAILURE 0xFF
+#define ERR_XI_OK 0x00
+#define ERR_XI_NOSHAREDMEMORY 0x01
+#define ERR_XI_DISPLAY 0x02
+#define ERR_XI_BADDEPTH 0x03
+#define ERR_XI_WINDOW 0x04
+#define ERR_XI_VIRTALLOC 0x05
+#define ERR_XI_XIMAGE 0x06
+#define ERR_XI_SHMALLOC 0x07
+#define ERR_XI_SHMXIMAGE 0x08
+#define ERR_XI_SHMSEGINFO 0x09
+#define ERR_XI_SHMVIRTALLOC 0x0A
+#define ERR_XI_SHMATTACH 0x0B
+
+
+
+
+#define PIXEL unsigned long
+
+extern const char *ERR_XI_STR[];
+
+struct XWindow {
+
+ Display *display;
+ Window window;
+ Screen *screenptr;
+ int screennum;
+ Visual *visual;
+ GC gc;
+
+ Colormap colormap;
+ PIXEL *palette;
+ int colorcells;
+
+ int x;
+ int y;
+ int width;
+ int height;
+ int depth;
+ int pixelsize;
+ int screensize;
+ int lOpen;
+
+ // colorMask
+ unsigned int redMask;
+ unsigned int greenMask;
+ unsigned int blueMask;
+
+ // colortable for 8 bit colormap
+ // (created with interference by the XServer)
+ unsigned char pixel[256];
+
+};
+
+
+#endif /* __XINIT_H__ */
diff --git a/mpeglib/lib/util/render/yuvPicture.cpp b/mpeglib/lib/util/render/yuvPicture.cpp
new file mode 100644
index 00000000..e79d3bde
--- /dev/null
+++ b/mpeglib/lib/util/render/yuvPicture.cpp
@@ -0,0 +1,253 @@
+/*
+ describes a picture in yuv format
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "yuvPicture.h"
+
+#include <iostream>
+
+using namespace std;
+
+static int instanceCnt=0;
+
+YUVPicture::YUVPicture(int width,int height) {
+ this->width=width;
+ this->height=height;
+
+ instance=instanceCnt;
+ instanceCnt++;
+ imagePtr=NULL;
+
+ setImageType(PICTURE_YUVMODE_CR_CB);
+
+
+ startTimeStamp=new TimeStamp();
+ waitTime=new TimeStamp();
+ earlyTime=new TimeStamp();
+ mpegType=-1;
+
+}
+
+
+YUVPicture::~YUVPicture() {
+ delete imagePtr;
+ delete earlyTime;
+ delete startTimeStamp;
+ delete waitTime;
+}
+
+
+
+int YUVPicture::getHeight() {
+ return height;
+}
+
+
+int YUVPicture::getWidth() {
+ return width;
+}
+
+
+
+int YUVPicture::getLumLength() {
+ return lumLength;
+}
+
+
+int YUVPicture::getColorLength() {
+ return colorLength;
+}
+
+int YUVPicture::getImageSize() {
+ return imageSize;
+}
+
+
+
+
+
+
+
+void YUVPicture::print(const char* title) {
+ cout << title <<":";
+ printf(" instance:%d ",instance);
+ printf(" width:%d ",width);
+ printf(" height:%d ",height);
+ cout <<" picPerSec:"<<picPerSec;
+ switch(mpegType) {
+ case 1:
+ printf("I_FRAME ");
+ break;
+ case 2:
+ printf("P_FRAME ");
+ break;
+ case 3:
+ printf("B_FRAME ");
+ break;
+ case 4:
+ printf("D_FRAME ");
+ break;
+ default:
+ printf("<unknown> ");
+ }
+ printf("\n");
+
+
+}
+
+
+void YUVPicture::setPicturePerSecond(float val) {
+ this->picPerSec=val;
+}
+
+
+float YUVPicture::getPicturePerSecond() {
+ return picPerSec;
+}
+
+
+void YUVPicture::setStartTimeStamp(TimeStamp* aStamp) {
+ aStamp->copyTo(startTimeStamp);
+}
+
+
+TimeStamp* YUVPicture::getStartTimeStamp() {
+ return startTimeStamp;
+}
+
+void YUVPicture::setWaitTime(TimeStamp* aStamp) {
+ aStamp->copyTo(waitTime);
+}
+
+
+TimeStamp* YUVPicture::getWaitTime() {
+ return waitTime;
+}
+
+void YUVPicture::setEarlyTime(TimeStamp* earlyTime) {
+ this->earlyTime=earlyTime;
+}
+
+
+TimeStamp* YUVPicture::getEarlyTime() {
+ return earlyTime;
+}
+
+
+
+void YUVPicture::setMpegPictureType(int type) {
+ this->mpegType=type;
+}
+
+
+int YUVPicture::getMpegPictureType() {
+ return mpegType;
+}
+
+
+void YUVPicture::setImageType(int imageType) {
+
+
+ //
+ // Reset everything
+ //
+
+ if (imagePtr != NULL) {
+ delete [] imagePtr;
+ imagePtr=NULL;
+ }
+ this->imageType=imageType;
+
+ lumLength=0;
+ colorLength=0;
+ Cr_mode=NULL;
+ Cb_mode=NULL;
+ luminance_mode=NULL;
+
+
+ //
+ // YUV Images
+ //
+ if ( (imageType == PICTURE_YUVMODE_CR_CB) ||
+ (imageType == PICTURE_YUVMODE_CB_CR) ) {
+
+
+ lumLength=width * height;
+ colorLength=width * height / 4;
+ imageSize=lumLength+colorLength+colorLength;
+
+ // the 64 is some "security" space
+ imagePtr=new unsigned char[imageSize+64];
+
+ if (imagePtr == NULL) {
+ cout << "cannot create image"<<endl;
+ exit(0);
+ }
+
+ // now caculate pointers to start addresses of lum/Cr/Cb blocks
+ // we need the yuvPtr for direct dither in hardware
+ // this should save a memcpy
+
+ luminance = imagePtr;
+ Cr = imagePtr+lumLength;
+ Cb = imagePtr+lumLength+colorLength;
+
+
+ if ( (luminance == NULL) ||
+ (Cr == NULL) ||
+ (Cb == NULL) ) {
+ cout << "allocation luminance/Cr/Cb error"<<endl;
+ exit(0);
+ }
+
+ switch(imageType) {
+ case PICTURE_YUVMODE_CR_CB:
+ Cr_mode=Cr;
+ Cb_mode=Cb;
+ luminance_mode=imagePtr;
+ break;
+ case PICTURE_YUVMODE_CB_CR:
+ Cr_mode=Cb;
+ Cb_mode=Cr;
+ luminance_mode=imagePtr;
+ break;
+ default:
+ cout << "unknown yuv mode:"<<imageType<<endl;
+ }
+ }
+ else if ( (imageType == PICTURE_YUVMODE_YUY2) ||
+ (imageType == PICTURE_YUVMODE_UYVY) ) {
+ // these yuv-modes are packed
+
+ imageSize=width * height * 2;
+
+ // the 64 is some "security" space
+ imagePtr=new unsigned char[imageSize+64];
+
+ if (imagePtr == NULL) {
+ cout << "cannot create image"<<endl;
+ exit(0);
+ }
+ }
+
+ //
+ // RGB Imcdages
+ //
+
+ if ( (imageType == PICTURE_RGB) ||
+ (imageType == PICTURE_RGB_FLIPPED) ){
+ imageSize=width*height*4;
+ imagePtr=new unsigned char[imageSize];
+ }
+ memset(imagePtr,0,imageSize);
+}
diff --git a/mpeglib/lib/util/render/yuvPicture.h b/mpeglib/lib/util/render/yuvPicture.h
new file mode 100644
index 00000000..1995b473
--- /dev/null
+++ b/mpeglib/lib/util/render/yuvPicture.h
@@ -0,0 +1,110 @@
+/*
+ describes a picture in yuv format
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __YUVPICTURE_H
+#define __YUVPICTURE_H
+
+
+extern "C" {
+#include <stdio.h>
+#include <string.h>
+}
+
+#include "../timeStamp.h"
+
+#define PICTURE_NO_TYPE -1
+
+#define PICTURE_YUVMODE_CR_CB 1
+#define PICTURE_YUVMODE_CB_CR 2
+#define PICTURE_RGB 3
+#define PICTURE_RGB_FLIPPED 4
+#define PICTURE_YUVMODE_YUY2 5
+#define PICTURE_YUVMODE_UYVY 6
+
+class YUVPicture {
+
+ unsigned char* imagePtr; /* Pointer to complete yuv image */
+
+ unsigned char* luminance; /* Luminance plane. */
+ unsigned char* Cr; /* Cr plane. */
+ unsigned char* Cb; /* Cb plane. */
+
+
+ int width;
+ int height;
+
+ float picPerSec;
+ int lumLength;
+ int colorLength;
+ int imageSize;
+
+ TimeStamp* startTimeStamp;
+ TimeStamp* waitTime;
+ TimeStamp* earlyTime;
+
+ int mpegType;
+ int instance;
+ int imageType;
+
+ unsigned char* image_mode; /* start Pointer to complete image */
+ unsigned char* luminance_mode; /* Luminace plane. */
+ unsigned char* Cr_mode; /* Cr plane. */
+ unsigned char* Cb_mode; /* Cb plane. */
+
+ public:
+ YUVPicture(int width,int height);
+ ~YUVPicture();
+
+ //
+ // For YUV Images
+ //
+ inline unsigned char* getLuminancePtr() {return luminance_mode;}
+ inline unsigned char* getCrPtr() {return Cr_mode;}
+ inline unsigned char* getCbPtr() {return Cb_mode;}
+
+ // general
+ inline unsigned char* getImagePtr() {return imagePtr;}
+
+ // use these to swap the image Types
+ inline int getImageType() { return imageType; }
+ void setImageType(int mode);
+
+
+ int getHeight();
+ int getWidth();
+
+ int getLumLength();
+ int getColorLength();
+ int getImageSize();
+
+ void setPicturePerSecond(float val);
+ float getPicturePerSecond();
+
+
+ void setStartTimeStamp(TimeStamp* timeStamp);
+ TimeStamp* getStartTimeStamp();
+
+ void setWaitTime(TimeStamp* waitTime);
+ TimeStamp* getWaitTime();
+
+ void setEarlyTime(TimeStamp* earlyTime);
+ TimeStamp* getEarlyTime();
+
+ void setMpegPictureType(int type);
+ int getMpegPictureType();
+
+
+ void print(const char* title);
+};
+#endif
diff --git a/mpeglib/lib/util/syncClock.cpp b/mpeglib/lib/util/syncClock.cpp
new file mode 100644
index 00000000..6e03bc4b
--- /dev/null
+++ b/mpeglib/lib/util/syncClock.cpp
@@ -0,0 +1,59 @@
+/*
+ basic synchronisation Classs
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "syncClock.h"
+
+#include <iostream>
+
+using namespace std;
+
+SyncClock::SyncClock() {
+}
+
+
+SyncClock::~SyncClock() {
+}
+
+
+
+int SyncClock::getSyncMode() {
+ cout << "direct virtual call SyncClock::getSyncMode"<<endl;
+ return __SYNC_NONE;
+}
+
+
+void SyncClock::setSyncMode(int) {
+ cout << "direct virtual call SyncClock::setSyncMode"<<endl;
+}
+
+
+
+int SyncClock::syncAudio(double ,double ) {
+ cout << "direct virtual call SyncClock::syncAudio"<<endl;
+ return true;
+}
+
+
+int SyncClock::syncVideo(double,double,
+ TimeStamp*,
+ TimeStamp*) {
+ cout << "direct virtual call SyncClock::syncVideo"<<endl;
+ return true;
+}
+
+
+
+void SyncClock::print(char*) {
+ cout << "direct virtual call print"<<endl;
+}
+
diff --git a/mpeglib/lib/util/syncClock.h b/mpeglib/lib/util/syncClock.h
new file mode 100644
index 00000000..4b6bc2bb
--- /dev/null
+++ b/mpeglib/lib/util/syncClock.h
@@ -0,0 +1,61 @@
+/*
+ basic synchronisation Classs
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __SYNCCLOCK_H
+#define __SYNCCLOCK_H
+
+#include "timeWrapper.h"
+#include "abstract/abs_thread.h"
+#include "timeStamp.h"
+#include <errno.h>
+
+#define __SYNC_NONE 0
+#define __SYNC_AUDIO 1
+#define __SYNC_VIDEO 2
+#define __SYNC_BOTH 3
+
+
+/**
+ The idea is similar to this:
+ We start a clock and selext a synchronisation mode.
+ (AUDIO,VIDEO,BOTH,NONE)
+ Assume we select AUDIO. When the audio thread delivers an SCR
+ and an PTS, we set the SCR from this set, as the new
+ time reference fo rthis clock.
+ If a video thread enters the class, with an SCR,PTS we wait,
+ or directly return if the PTS is in time or out of time,
+ but we never set the SCR.
+*/
+
+class SyncClock {
+
+
+ public:
+ SyncClock();
+ virtual ~SyncClock();
+
+ virtual int getSyncMode();
+ virtual void setSyncMode(int syncMode);
+
+
+ virtual int syncAudio(double pts,double scr);
+ virtual int syncVideo(double pts,double scr,
+ class TimeStamp* earlyTime,
+ class TimeStamp* waitTime);
+
+ virtual void print(char* text);
+
+
+};
+#endif
diff --git a/mpeglib/lib/util/syncClockMPEG.cpp b/mpeglib/lib/util/syncClockMPEG.cpp
new file mode 100644
index 00000000..53ae7e2d
--- /dev/null
+++ b/mpeglib/lib/util/syncClockMPEG.cpp
@@ -0,0 +1,221 @@
+/*
+ synchronisation of audio/video (PTS) against system clock stamps (SCR)
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "syncClockMPEG.h"
+
+
+#include <iostream>
+
+using namespace std;
+
+
+SyncClockMPEG::SyncClockMPEG() {
+ syncMode=__SYNC_NONE;
+ abs_thread_mutex_init(&scrMut);
+ abs_thread_mutex_init(&changeMut);
+ lastSCR=0.0;
+ lastPTS=0.0;
+ jitter=0.0;
+ oldjitter=0.0;
+
+ TimeWrapper::gettimeofday(&lastPTS_time);
+
+
+}
+
+
+SyncClockMPEG::~SyncClockMPEG() {
+ abs_thread_mutex_destroy(&changeMut);
+ abs_thread_mutex_destroy(&scrMut);
+}
+
+
+
+int SyncClockMPEG::getSyncMode() {
+ return syncMode;
+}
+
+
+void SyncClockMPEG::setSyncMode(int syncMode) {
+ this->syncMode=syncMode;
+}
+
+
+
+int SyncClockMPEG::syncAudio(double pts,double scr) {
+ switch(syncMode) {
+ case __SYNC_AUDIO:
+ markLastPTSTime(scr,pts);
+ break;
+ default:
+ cout << "syncMode not implemented:"<<syncMode<<endl;
+ }
+ return true;
+}
+
+
+int SyncClockMPEG::syncVideo(double pts,double scr,
+ TimeStamp* earlyTime,
+ TimeStamp* waitTime) {
+ int back;
+ switch(syncMode) {
+ case __SYNC_AUDIO:
+ back=gowait(scr,pts,earlyTime,waitTime);
+ return back;
+ break;
+ case __SYNC_NONE:
+ return true;
+ default:
+ cout << "syncMode not implemented"<<endl;
+ }
+ return true;
+}
+
+
+void SyncClockMPEG::lockSyncClock() {
+ abs_thread_mutex_lock(&changeMut);
+ abs_thread_mutex_lock(&scrMut);
+ abs_thread_mutex_unlock(&changeMut);
+
+}
+
+
+void SyncClockMPEG::unlockSyncClock() {
+ abs_thread_mutex_unlock(&scrMut);
+}
+
+
+double SyncClockMPEG::getPTSTime(double* window) {
+ lockSyncClock();
+ double currentPTS;
+ timeval_t current_time;
+ timeval_t passed_time;
+ TimeWrapper::gettimeofday(&current_time);
+
+ a_Minus_b_Is_C(&current_time,&lastPTS_time,&passed_time);
+ currentPTS=lastPTS+timeval2Double(&passed_time);
+ *window=jitter+oldjitter;
+
+
+ unlockSyncClock();
+ return currentPTS;
+}
+
+
+void SyncClockMPEG::markLastPTSTime(double ,double pts) {
+ double tmp;
+ double expect_time=getPTSTime(&tmp);
+
+ lockSyncClock();
+ oldjitter=jitter;
+ jitter=expect_time-pts;
+
+ TimeWrapper::gettimeofday(&lastPTS_time);
+ lastPTS=pts;
+
+
+ unlockSyncClock();
+ /*
+ cout << "(audio) pts stream:"<<pts
+ << " expect:"<<expect_time<<" jitter:"<<jitter<<endl;
+ */
+
+}
+
+
+int SyncClockMPEG::a_Minus_b_Is_C(timeval_t* a,timeval_t* b,timeval_t* c){
+ c->tv_usec=a->tv_usec;
+ c->tv_sec=a->tv_sec;
+
+ c->tv_usec-=b->tv_usec;
+ c->tv_sec-=b->tv_sec;
+
+ if (c->tv_usec <= 0) {
+ c->tv_usec=c->tv_usec+1000000;
+ c->tv_sec--;
+ }
+ if (c->tv_usec >= 1000000) {
+ c->tv_usec=c->tv_usec-1000000;
+ c->tv_sec++;
+ }
+ return true;
+}
+
+
+double SyncClockMPEG::timeval2Double(timeval_t* a) {
+ return (double)a->tv_sec+(double)(a->tv_usec)/1000000.0;
+}
+
+
+void SyncClockMPEG::double2Timeval(double a,timeval_t* dest) {
+ dest->tv_sec=(int)a;
+ dest->tv_usec=(int)(1000000.0*(double)(a-(double)dest->tv_sec));
+}
+
+
+int SyncClockMPEG::gowait(double ,double pts,
+ TimeStamp* earlyTime,TimeStamp* ) {
+ double window;
+ double currentPTS=getPTSTime(&window);
+ double diff_time;
+
+ // in window we have the jitter
+ // diff_time is positive when we are in the future
+ diff_time=pts-(currentPTS+window);
+
+
+
+ // tolerate one frame (for 25 frames/s) in the future
+ // but wait a bit
+ if (diff_time > 0.0) {
+ diff_time=diff_time/4.0;
+
+ double2Timeval(diff_time,earlyTime->getTime());
+ if (diff_time > 1) {
+ // cannot be, we assume stange clock error
+ earlyTime->set(1,0);
+ }
+
+ return true;
+ }
+
+ earlyTime->set(0,0);
+ // one frame late, display it, without waiting
+ // and hope we catch up
+ if (diff_time > -0.04) {
+ return true;
+ }
+ /*
+ cout << "(video) pts stream:"<<pts
+ << " pts expect:"<<currentPTS
+ << " window:"<<window<<endl;
+ */
+ return false;
+}
+
+
+void SyncClockMPEG::printTime(timeval_t* a,char* text) {
+ cout << text
+ << "time(sec):"<<a->tv_sec
+ << "time(usec)"<<a->tv_usec<<endl;
+}
+
+void SyncClockMPEG::print(char* text) {
+ cout << text
+ << " lastPTS:"<<lastPTS
+ << " lastSCR:"<<lastSCR
+ << " jitter:"<<jitter;
+ printTime(&lastPTS_time,(char*)"lastPTS_time");
+ printTime(&lastSCR_time,(char*)"lastSCR_time");
+}
diff --git a/mpeglib/lib/util/syncClockMPEG.h b/mpeglib/lib/util/syncClockMPEG.h
new file mode 100644
index 00000000..1a812c45
--- /dev/null
+++ b/mpeglib/lib/util/syncClockMPEG.h
@@ -0,0 +1,77 @@
+/*
+ synchronisation of audio/video (PTS) against system clock stamps (SCR)
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __SYNCCLOCKMPEG_H
+#define __SYNCCLOCKMPEG_H
+
+
+#include "syncClock.h"
+
+/**
+ The idea is similar to this:
+ We start a clock and selext a synchronisation mode.
+ (AUDIO,VIDEO,BOTH,NONE)
+ Assume we select AUDIO. When the audio thread delivers an SCR
+ and an PTS, we set the SCR from this set, as the new
+ time reference fo rthis clock.
+ If a video thread enters the class, with an SCR,PTS we wait,
+ or directly return if the PTS is in time or out of time,
+ but we never set the SCR.
+*/
+
+class SyncClockMPEG : public SyncClock {
+
+ int syncMode;
+ double lastSCR;
+ double lastPTS;
+ double jitter;
+ double oldjitter;
+ timeval_t lastSCR_time;
+ timeval_t lastPTS_time;
+ timeval_t drift_time;
+
+ public:
+ SyncClockMPEG();
+ ~SyncClockMPEG();
+
+ int getSyncMode();
+ void setSyncMode(int syncMode);
+
+
+ int syncAudio(double pts,double scr);
+ int syncVideo(double pts,double scr,
+ class TimeStamp* earlyTime,class TimeStamp* waitTime);
+
+ void print(char* text);
+
+ double getPTSTime(double* window);
+ private:
+
+ void printTime(timeval_t* a,char* text);
+ void markLastPTSTime(double scr,double pts);
+ int gowait(double scr,double pts,
+ class TimeStamp* earlyTime,class TimeStamp* waitTime);
+ int a_Minus_b_Is_C(timeval_t* a,timeval_t* b,timeval_t* c);
+ double timeval2Double(timeval_t* a);
+ void double2Timeval(double a,timeval_t* dest);
+
+ void lockSyncClock();
+ void unlockSyncClock();
+
+ abs_thread_mutex_t scrMut;
+ abs_thread_mutex_t changeMut;
+
+
+};
+#endif
+
diff --git a/mpeglib/lib/util/timeStamp.cpp b/mpeglib/lib/util/timeStamp.cpp
new file mode 100644
index 00000000..2804cc5b
--- /dev/null
+++ b/mpeglib/lib/util/timeStamp.cpp
@@ -0,0 +1,273 @@
+/*
+ class for managing byte positions and associated time positions
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "timeStamp.h"
+
+#include <iostream>
+
+using namespace std;
+
+
+TimeStamp::TimeStamp() {
+ key=0;
+ keylen=0;
+ time.tv_sec=0;
+ time.tv_usec=0;
+
+ ptsTimeStamp=0.0;
+ scrTimeStamp=0.0;
+ videoFrameCounter=0;
+ dtsTimeStamp=0.0;
+ lPTSFlag=false;
+
+}
+
+
+TimeStamp::~TimeStamp() {
+}
+
+
+void TimeStamp::copyTo(TimeStamp* dest) {
+ dest->setKey(key,keylen);
+ dest->setTime(&time);
+ dest->setPTSTimeStamp(ptsTimeStamp);
+ dest->setVideoFrameCounter(videoFrameCounter);
+
+ dest->setSCRTimeStamp(scrTimeStamp);
+ dest->setDTSTimeStamp(dtsTimeStamp);
+ dest->setSyncClock(syncClock);
+ dest->setPTSFlag(lPTSFlag);
+}
+
+
+long TimeStamp::getKey() {
+ return key;
+}
+
+int TimeStamp::getKeyLen() {
+ return keylen;
+}
+
+void TimeStamp::setKey(long key,int len) {
+ this->key=key;
+ this->keylen=len;
+}
+
+
+void TimeStamp::setTime(timeval_t* newTime) {
+ time.tv_sec=newTime->tv_sec;
+ time.tv_usec=newTime->tv_usec;
+}
+
+
+void TimeStamp::gettimeofday() {
+ TimeWrapper::gettimeofday(&time);
+}
+
+
+timeval_t* TimeStamp::getTime() {
+ return &time;
+}
+
+
+void TimeStamp::waitForIt() {
+ timeval_t waitTime;
+
+ waitTime.tv_sec=time.tv_sec;
+ waitTime.tv_usec=time.tv_usec;
+
+ if (isPositive()) {
+ //cout << "wait:"<<waitTime.tv_sec<<" usec:"<<waitTime.tv_usec<<endl;
+ TimeWrapper::usleep(&waitTime);
+ }
+
+}
+
+void TimeStamp::addOffset(TimeStamp* stamp) {
+ addOffset(stamp->time.tv_sec,stamp->time.tv_usec);
+}
+
+void TimeStamp::addOffset(int sec,long usec) {
+ time.tv_sec=time.tv_sec+sec;
+ time.tv_usec=time.tv_usec+usec;
+ if (time.tv_usec >= 1000000) {
+ time.tv_usec-=1000000;
+ time.tv_sec++;
+ }
+ if (time.tv_usec < 0) {
+ time.tv_usec+=1000000;
+ time.tv_sec--;
+ }
+}
+
+void TimeStamp::minus(int sec,long usec) {
+
+ time.tv_usec-=usec;
+ time.tv_sec-=sec;
+ if (time.tv_usec <= 0) {
+ time.tv_usec=time.tv_usec+1000000;
+ time.tv_sec--;
+ }
+ if (time.tv_usec >= 1000000) {
+ time.tv_usec=time.tv_usec-1000000;
+ time.tv_sec++;
+ }
+
+}
+
+void TimeStamp::minus(TimeStamp* stamp,TimeStamp* dest) {
+
+ long sec=time.tv_sec;
+ long usec=time.tv_usec;
+
+ minus(stamp->time.tv_sec,stamp->time.tv_usec);
+
+ dest->set(time.tv_sec,time.tv_usec);
+ if (dest != this) {
+ time.tv_sec=sec;
+ time.tv_usec=usec;
+ }
+}
+
+
+int TimeStamp::lessThan(TimeStamp* stamp) {
+ return lessThan(stamp->time.tv_sec,stamp->time.tv_usec);
+}
+
+
+int TimeStamp::lessThan(int sec,long usec) {
+ int back=false;
+ if (time.tv_sec < sec) {
+ back=true;
+ } else {
+ if (time.tv_sec == sec) {
+ if (time.tv_usec < usec) {
+ back=true;
+ }
+ }
+ }
+ return back;
+}
+
+void TimeStamp::set(long sec,long usec) {
+ time.tv_sec=sec;
+ time.tv_usec=usec;
+}
+
+
+
+void TimeStamp::print(const char* name) {
+ cout << name
+ <<" lPTS:"<<lPTSFlag
+ <<" pts:"<<ptsTimeStamp
+ <<" dts:"<<dtsTimeStamp
+ <<" scr:"<<scrTimeStamp
+ <<" key:"<<key
+ <<" sec:"<<time.tv_sec
+ <<" usec:"<<time.tv_usec
+ <<" v-Minor:"<<videoFrameCounter<<endl;
+
+
+}
+
+
+
+
+
+int TimeStamp::isPositive() {
+ if ((time.tv_sec == 0) && (time.tv_usec == 0)) {
+ return false;
+ }
+ return ((time.tv_sec >= 0) && (time.tv_usec >= 0));
+}
+
+
+int TimeStamp::isNegative() {
+ if (time.tv_sec < 0) {
+ return true;
+ }
+ if (time.tv_usec < 0) {
+ return true;
+ }
+ return false;
+}
+
+
+int TimeStamp::getPTSFlag() {
+ return lPTSFlag;
+}
+
+void TimeStamp::setPTSFlag(int lPTSFlag) {
+ this->lPTSFlag=lPTSFlag;
+}
+
+double TimeStamp::getPTSTimeStamp() {
+ return ptsTimeStamp;
+}
+
+
+void TimeStamp::setPTSTimeStamp(double ptsTimeStamp) {
+ this->ptsTimeStamp=ptsTimeStamp;
+}
+
+
+double TimeStamp::getSCRTimeStamp() {
+ return scrTimeStamp;
+}
+
+
+void TimeStamp::setSCRTimeStamp(double scrTimeStamp) {
+ this->scrTimeStamp=scrTimeStamp;
+}
+
+
+double TimeStamp::getDTSTimeStamp() {
+ return dtsTimeStamp;
+}
+
+
+void TimeStamp::setDTSTimeStamp(double dtsTimeStamp) {
+ this->dtsTimeStamp=dtsTimeStamp;
+}
+
+int TimeStamp::getVideoFrameCounter() {
+ return videoFrameCounter;
+}
+
+
+void TimeStamp::setVideoFrameCounter(int nr) {
+ this->videoFrameCounter=nr;
+}
+
+
+
+double TimeStamp::getAsSeconds() {
+ return (double)time.tv_sec+(double)time.tv_usec/(double)1000000;
+}
+
+
+
+SyncClock* TimeStamp::getSyncClock() {
+ return syncClock;
+}
+
+void TimeStamp::setSyncClock(SyncClock* syncClock) {
+ this->syncClock=syncClock;
+}
+
+
+
+
+
+
+
diff --git a/mpeglib/lib/util/timeStamp.h b/mpeglib/lib/util/timeStamp.h
new file mode 100644
index 00000000..a45bb582
--- /dev/null
+++ b/mpeglib/lib/util/timeStamp.h
@@ -0,0 +1,92 @@
+/*
+ class for managing byte positions and associated time positions
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __TIMESTAMP_H
+#define __TIMESTAMP_H
+
+
+#include "syncClock.h"
+
+
+class TimeStamp {
+
+ long key;
+ int keylen;
+
+ int videoFrameCounter;
+
+ timeval_t time;
+
+ int lPTSFlag;
+
+ double ptsTimeStamp;
+ double scrTimeStamp;
+ double dtsTimeStamp;
+
+ class SyncClock* syncClock;
+
+ public:
+ TimeStamp();
+ ~TimeStamp();
+
+ void gettimeofday();
+ void set(long sec,long usec);
+
+ void addOffset(TimeStamp* stamp);
+ void addOffset(int sec,long usec);
+ void copyTo(TimeStamp* dest);
+
+ long getKey();
+ int getKeyLen();
+ void setKey(long key,int keylen);
+
+ int getPTSFlag();
+ void setPTSFlag(int lPTSFlag);
+
+
+ double getPTSTimeStamp();
+ void setPTSTimeStamp(double ptsTimeStamp);
+
+ double getDTSTimeStamp();
+ void setDTSTimeStamp(double dtsTimeStamp);
+
+ double getSCRTimeStamp();
+ void setSCRTimeStamp(double scrTimeStamp);
+
+ int getVideoFrameCounter();
+ void setVideoFrameCounter(int nr);
+
+ SyncClock* getSyncClock();
+ void setSyncClock(class SyncClock* syncClock);
+
+
+ void print(const char* name);
+ void minus(TimeStamp* stamp,TimeStamp* dest);
+ void minus(int sec,long usec);
+ int lessThan(TimeStamp* stamp);
+ int lessThan(int sec,long usec);
+ double getAsSeconds();
+
+ void waitForIt();
+ timeval_t* getTime();
+
+ int isPositive();
+ int isNegative();
+
+ private:
+ void setTime(timeval_t* newTime);
+ void normalize();
+
+
+};
+#endif
diff --git a/mpeglib/lib/util/timeStampArray.cpp b/mpeglib/lib/util/timeStampArray.cpp
new file mode 100644
index 00000000..730dc280
--- /dev/null
+++ b/mpeglib/lib/util/timeStampArray.cpp
@@ -0,0 +1,178 @@
+/*
+ class for managing byte positions and associated time positions
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include <iostream>
+
+#include "timeStampArray.h"
+
+using namespace std;
+
+
+TimeStampArray::TimeStampArray(char* aName,int entries) {
+
+ writePos=0;
+ readPos=0;
+ fillgrade=0;
+ lastWritePos=0;
+ this->entries=entries;
+ if (entries < 1) {
+ cout << "TimeStampArray entries must be >= 1";
+ exit(0);
+ }
+
+
+ abs_thread_mutex_init(&writeInMut);
+ abs_thread_mutex_init(&changeMut);
+
+
+
+ name=strdup(aName);
+ int i;
+ tStampArray=new TimeStamp*[entries];
+
+ for(i=0;i<entries;i++) {
+ tStampArray[i]=new TimeStamp();
+ }
+
+}
+
+
+TimeStampArray::~TimeStampArray() {
+
+ int i;
+ for(i=0;i<entries;i++) {
+ delete tStampArray[i];
+ }
+ delete [] tStampArray;
+ if (name != NULL) {
+ free(name); // allocated with strdup
+ }
+ abs_thread_mutex_destroy(&writeInMut);
+ abs_thread_mutex_destroy(&changeMut);
+}
+
+TimeStamp* TimeStampArray::getReadTimeStamp() {
+ return tStampArray[readPos];
+}
+
+
+int TimeStampArray::getFillgrade() {
+ return fillgrade;
+}
+
+int TimeStampArray::insertTimeStamp(TimeStamp* src,long key,int len) {
+ if (src == NULL) {
+ return true;
+ }
+ lockStampArray();
+ int back=true;
+ src->copyTo(tStampArray[writePos]);
+ tStampArray[writePos]->setKey(key,len);
+ /*
+ if (fillgrade > 0) {
+ if (tStampArray[lastWritePos]->getKey() == key) {
+ unlockStampArray();
+ return;
+ }
+ }
+ */
+
+ lastWritePos=writePos;
+ writePos++;
+ fillgrade++;
+ if (writePos == entries) {
+ writePos=0;
+ }
+ if (fillgrade == entries) {
+ cout << name<<" TimeStampArray::array overfull forward"<<endl;
+ internalForward();
+ back=false;
+ }
+ unlockStampArray();
+ return back;
+}
+
+
+int TimeStampArray::bytesUntilNext(long key) {
+ lockStampArray();
+ TimeStamp* current=tStampArray[readPos];
+ int back=current->getKey()-key;
+ unlockStampArray();
+ return back;
+}
+
+TimeStamp* TimeStampArray::getTimeStamp(long key) {
+ lockStampArray();
+ TimeStamp* back=tStampArray[readPos];
+ if (key > back->getKey()+back->getKeyLen()) {
+ if (fillgrade > 1) {
+ internalForward();
+ unlockStampArray();
+ return getTimeStamp(key);
+ }
+ }
+
+ /*
+ if (back->getKey() > key) {
+ cout << "key "<<key<<" too big"<<back->getKey()-key<<endl;
+ back->print("key access");
+ }
+ */
+ unlockStampArray();
+ /* maybe we should return NULL here to indicate
+ that there is no valid timestamp */
+ /* This would need a check for every getTimeStamp call
+ I think returning the last available stamp is ok
+ */
+ return back;
+}
+
+
+void TimeStampArray::forward() {
+ lockStampArray();
+ internalForward();
+ unlockStampArray();
+}
+
+
+
+void TimeStampArray::clear() {
+ lockStampArray();
+ writePos=0;
+ readPos=0;
+ fillgrade=0;
+ unlockStampArray();
+}
+
+
+void TimeStampArray::lockStampArray() {
+
+ abs_thread_mutex_lock(&changeMut);
+ abs_thread_mutex_lock(&writeInMut);
+ abs_thread_mutex_unlock(&changeMut);
+
+}
+
+
+void TimeStampArray::unlockStampArray() {
+ abs_thread_mutex_unlock(&writeInMut);
+}
+
+
+void TimeStampArray::internalForward() {
+ readPos++;
+ fillgrade--;
+ if (readPos == entries) {
+ readPos=0;
+ }
+}
diff --git a/mpeglib/lib/util/timeStampArray.h b/mpeglib/lib/util/timeStampArray.h
new file mode 100644
index 00000000..8b117652
--- /dev/null
+++ b/mpeglib/lib/util/timeStampArray.h
@@ -0,0 +1,85 @@
+/*
+ class for managing byte positions and associated time positions
+ Copyright (C) 1999 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __TIMESTAMPARRAY_H
+#define __TIMESTAMPARRAY_H
+
+#include "abstract/abs_thread.h"
+#include "timeStamp.h"
+
+
+/**
+ This class deals with the problem to sync audio and video.
+ Both streams are decoded in different threads, sometimes
+ the video is decoded faster than the audio and sometimes
+ not.
+ <p>
+ You need a general mechanism to decide, which is faster.
+ It works like this:
+ <p>
+ When the mpeg stream is split in video/audio part the split thread
+ writes the video/audio data to the inputInterface.
+ Additionally it writes a timestamp to the interface.
+ The interface counts the bytes and forward the bytes/timeStamp
+ pait to this class.
+ Later when the threads write to the outputInterface the ask
+ this class (with the bytePostions) which timestamp it
+ has and hass the data and the timestamp to the outputInterface.
+ There we can decide what to do with the data.
+ <p>
+ 1) audio faster than video = drop video picture
+ <p>
+ 2) video faster than audio - wait for audio.
+*/
+
+
+
+class TimeStampArray {
+
+ TimeStamp** tStampArray;
+
+ int lastWritePos;
+ int writePos;
+ int readPos;
+ int fillgrade;
+ char* name;
+ int entries;
+
+ abs_thread_mutex_t writeInMut;
+ abs_thread_mutex_t changeMut;
+
+
+ public:
+ TimeStampArray(char* name,int entries);
+ ~TimeStampArray();
+
+
+ int insertTimeStamp(TimeStamp* src,long key,int len);
+ TimeStamp* getReadTimeStamp();
+ TimeStamp* getTimeStamp(long key);
+ int getFillgrade();
+ void forward();
+ void clear();
+
+ int bytesUntilNext(long key);
+
+ private:
+ void lockStampArray();
+ void unlockStampArray();
+ void internalForward();
+
+
+
+};
+#endif
+
diff --git a/mpeglib/lib/util/timeWrapper.cpp b/mpeglib/lib/util/timeWrapper.cpp
new file mode 100644
index 00000000..adf87681
--- /dev/null
+++ b/mpeglib/lib/util/timeWrapper.cpp
@@ -0,0 +1,77 @@
+/*
+ wrapps calls to usleep, gettimeofday,...
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "timeWrapper.h"
+
+#if defined WIN32
+// usec goes from 0 -> 1000000 (one Million) under Unix
+int gettimeofday(struct timeval *tv, struct timezone *tz) {
+ if(tv) {
+ struct _timeb tb;
+ _ftime(&tb);
+ tv->tv_sec=tb.time;
+ tv->tv_usec=1000*tb.millitm;
+ }
+ return(0);
+}
+
+void abs_usleep(const timeval* tm) {
+ _sleep((tm->tv_usec / 1000) + (tm->tv_sec * 1000));
+}
+#endif
+
+#ifndef WIN32
+
+void abs_usleep(struct timeval* tm) {
+ select(0,NULL,NULL,NULL,tm);
+}
+
+#endif
+
+TimeWrapper::TimeWrapper() {
+}
+
+
+TimeWrapper::~TimeWrapper() {
+}
+
+void TimeWrapper::sleep(int sec) {
+ timeval_t time;
+ time.tv_sec=sec;
+ time.tv_usec=0;
+ TimeWrapper::usleep(&time);
+}
+
+void TimeWrapper::usleep(unsigned long usec) {
+ timeval_t time;
+ time.tv_sec=0;
+ time.tv_usec=usec;
+ TimeWrapper::usleep(&time);
+}
+
+void TimeWrapper::usleep(timeval_t* time) {
+ struct timeval waitTime;
+ waitTime.tv_sec=time->tv_sec;
+ waitTime.tv_usec=time->tv_usec;
+ /*Threads and usleep does not work, select is very portable*/
+ ::abs_usleep(&waitTime);
+}
+
+
+void TimeWrapper::gettimeofday(timeval_t* time) {
+ struct timeval waitTime;
+ ::gettimeofday(&waitTime,NULL);
+ time->tv_sec=waitTime.tv_sec;
+ time->tv_usec=waitTime.tv_usec;
+}
diff --git a/mpeglib/lib/util/timeWrapper.h b/mpeglib/lib/util/timeWrapper.h
new file mode 100644
index 00000000..608d5bd0
--- /dev/null
+++ b/mpeglib/lib/util/timeWrapper.h
@@ -0,0 +1,44 @@
+/*
+ wrapps calls to usleep, gettimeofday,...
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __TIMEWRAPPER_H
+#define __TIMEWRAPPER_H
+
+#if defined WIN32
+#include <winsock.h>
+#include <sys/timeb.h>
+#else
+#include <unistd.h>
+#include <sys/time.h>
+#include <strings.h>
+#endif
+
+#include <kdemacros.h>
+
+typedef struct timeval_s {
+ long tv_sec; /* seconds */
+ long tv_usec; /* microseconds */
+} timeval_t;
+
+class KDE_EXPORT TimeWrapper {
+
+ public:
+ TimeWrapper();
+ ~TimeWrapper();
+ static void sleep(int sec);
+ static void usleep(unsigned long usec);
+ static void usleep(timeval_t* time);
+ static void gettimeofday(timeval_t* time);
+
+};
+#endif
diff --git a/mpeglib/lib/yuv/Makefile.am b/mpeglib/lib/yuv/Makefile.am
new file mode 100644
index 00000000..b8d7155d
--- /dev/null
+++ b/mpeglib/lib/yuv/Makefile.am
@@ -0,0 +1,28 @@
+# libplayerplugin - Makefile.am
+
+INCLUDES = $(all_includes)
+
+noinst_LTLIBRARIES = libyuvPlugin.la
+
+
+noinst_HEADERS =
+
+kmpgincludedir = $(includedir)/$(THIS_LIB_NAME)/decoder
+
+kmpginclude_HEADERS = yuvPlugin.h
+
+libyuvPlugin_la_SOURCES = yuvPlugin.cpp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpeglib/lib/yuv/yuvPlugin.cpp b/mpeglib/lib/yuv/yuvPlugin.cpp
new file mode 100644
index 00000000..94697aa8
--- /dev/null
+++ b/mpeglib/lib/yuv/yuvPlugin.cpp
@@ -0,0 +1,158 @@
+/*
+ raw yuv stream player
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "yuvPlugin.h"
+
+#include <iostream>
+
+using namespace std;
+
+
+YUVPlugin::YUVPlugin() {
+ init();
+}
+
+
+YUVPlugin::~YUVPlugin() {
+}
+
+
+void YUVPlugin::init() {
+ lCalcLength=false;
+ nWidth=0;
+ nHeight=0;
+ imageType=PICTURE_YUVMODE_CR_CB;
+ picPerSec=1.0;
+}
+
+
+void YUVPlugin::decoder_loop() {
+
+
+
+ cout << "YUVPlugin::decoder_loop() 1"<<endl;
+ if (input == NULL) {
+ cout << "YUVPlugin::decoder_loop input is NULL"<<endl;
+ exit(0);
+ }
+ if (output == NULL) {
+ cout << "YUVPlugin::decoder_loop output is NULL"<<endl;
+ exit(0);
+ }
+
+
+ PictureArray* pictureArray;
+
+
+ int bytes=nWidth*nHeight;
+
+ if ( (imageType == PICTURE_YUVMODE_CR_CB) ||
+ (imageType == PICTURE_YUVMODE_CB_CR) ) {
+ bytes=bytes+bytes/2;
+ }
+ if ( (imageType == PICTURE_RGB) ||
+ (imageType == PICTURE_RGB_FLIPPED) ) {
+ bytes=bytes*4;
+ }
+
+
+
+
+ // decode loop
+ while(runCheck()) {
+ switch(streamState) {
+ case _STREAM_STATE_FIRST_INIT :
+ output->openWindow(nWidth,nHeight,(char*)"yuv Viewer");
+ pictureArray=output->lockPictureArray();
+ cout << "pictureArray->setImageType"<<endl;
+ pictureArray->setImageType(imageType);
+
+ setStreamState(_STREAM_STATE_INIT);
+ break;
+ case _STREAM_STATE_INIT :
+ setStreamState(_STREAM_STATE_PLAY);
+ break;
+ case _STREAM_STATE_PLAY : {
+ // we read from input raw yuv bytes
+ // and display them.
+ pictureArray=output->lockPictureArray();
+ // now we have the color info
+ YUVPicture* pic=pictureArray->getPast();
+ unsigned char* dest=pic->getImagePtr();
+ input->read((char*)dest,bytes);
+ pic->setPicturePerSecond(picPerSec);
+ // now inform subsystem about the frame to display
+ pictureArray->setYUVPictureCallback(pic);
+ // display it
+ output->unlockPictureArray(pictureArray);
+
+ pictureArray->setYUVPictureCallback(NULL);
+ break;
+ }
+ case _STREAM_STATE_WAIT_FOR_END:
+ // exit while loop
+ lDecoderLoop=false;
+ cout << "mpegplugin _STREAM_STATE_WAIT_FOR_END"<<endl;
+ break;
+ default:
+ cout << "unknown stream state:"<<streamState<<endl;
+ }
+ }
+
+ cout << "*********mpegPLugin exit"<<endl;
+
+ output->flushWindow();
+ // copy sequence back if needed
+
+ cout << "delete mpegVideoStream"<<endl;
+
+}
+
+
+
+
+
+// here we can config our decoder with special flags
+void YUVPlugin::config(const char* key,const char* value,void* user_data) {
+ if (strcmp(key,"-c")==0) {
+ lCalcLength=false;
+ }
+
+ if (strcmp(key,"height")==0) {
+ nHeight=atoi(value);
+ }
+ if (strcmp(key,"width")==0) {
+ nWidth=atoi(value);
+ }
+ if (strcmp(key,"imageType")==0) {
+ imageType=atoi(value);
+ cout << "imageType:"<<imageType<<endl;
+ }
+ if (strcmp(key,"picPerSec")==0) {
+ picPerSec=atoi(value);
+ }
+ DecoderPlugin::config(key,value,user_data);
+}
+
+
+
+int YUVPlugin::getSongLength() {
+ int back=0;
+ return back;
+}
+
+
+
+
+
+
diff --git a/mpeglib/lib/yuv/yuvPlugin.h b/mpeglib/lib/yuv/yuvPlugin.h
new file mode 100644
index 00000000..4a5dc72b
--- /dev/null
+++ b/mpeglib/lib/yuv/yuvPlugin.h
@@ -0,0 +1,47 @@
+/*
+ raw yuv stream player
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+
+#ifndef __YUVPLUGIN_H
+#define __YUVPLUGIN_H
+
+#include "../decoder/decoderPlugin.h"
+#include <kdemacros.h>
+
+class KDE_EXPORT YUVPlugin : public DecoderPlugin {
+
+ int lCalcLength;
+ int nWidth;
+ int nHeight;
+ int imageType;
+ float picPerSec;
+
+ public:
+ YUVPlugin();
+ ~YUVPlugin();
+
+ void decoder_loop();
+ void config(const char* key,const char* value,void* user_data);
+
+
+ private:
+ void init();
+
+ int getSongLength();
+
+
+};
+
+#endif
diff --git a/mpeglib_artsplug/CDDAPlayObject.mcopclass b/mpeglib_artsplug/CDDAPlayObject.mcopclass
new file mode 100644
index 00000000..cafe6c68
--- /dev/null
+++ b/mpeglib_artsplug/CDDAPlayObject.mcopclass
@@ -0,0 +1,7 @@
+Interface=CDDAPlayObject,Arts::PitchablePlayObject,Arts::StreamPlayObject,Arts::PlayObject,Arts::SynthModule,Arts::Object
+Author="Martin Vogt <mvogt@rhrk.uni-kl.de>"
+URL="http://mpeglib.sourceforge.net/"
+Extension=cdda,cda
+MimeType=application/x-cda,
+Language=C++
+Library=libarts_mpeglib.la
diff --git a/mpeglib_artsplug/MP3PlayObject.mcopclass b/mpeglib_artsplug/MP3PlayObject.mcopclass
new file mode 100644
index 00000000..a19b32a0
--- /dev/null
+++ b/mpeglib_artsplug/MP3PlayObject.mcopclass
@@ -0,0 +1,8 @@
+Interface=MP3PlayObject,Arts::PitchablePlayObject,Arts::PlayObject,Arts::StreamPlayObject,Arts::SynthModule,Arts::Object
+Author="Martin Vogt <mvogt@rhrk.uni-kl.de>"
+URL="http://mpeglib.sourceforge.net"
+Extension=mp3,mp1,mp2
+MimeType=audio/x-mp3,audio/x-mp1,audio/x-mp2,audio/mpeg
+Language=C++
+Library=libarts_mpeglib.la
+Preference=3
diff --git a/mpeglib_artsplug/MPGPlayObject.mcopclass b/mpeglib_artsplug/MPGPlayObject.mcopclass
new file mode 100644
index 00000000..f2c7322f
--- /dev/null
+++ b/mpeglib_artsplug/MPGPlayObject.mcopclass
@@ -0,0 +1,8 @@
+Interface=MPGPlayObject,Arts::PitchablePlayObject,Arts::PlayObject,Arts::StreamPlayObject,Arts::SynthModule,Arts::Object
+Author="Martin Vogt <mvogt@rhrk.uni-kl.de>"
+URL="http://mpeglib.sourceforge.net"
+Extension=mpg,dat,mpeg
+MimeType=video/x-mpg,video/x-dat,video/x-mpeg,video/mpeg
+Language=C++
+Library=libarts_mpeglib.la
+Preference=3
diff --git a/mpeglib_artsplug/Makefile.am b/mpeglib_artsplug/Makefile.am
new file mode 100644
index 00000000..5cc7af54
--- /dev/null
+++ b/mpeglib_artsplug/Makefile.am
@@ -0,0 +1,106 @@
+# This file you have to edit. Change the name here
+
+
+mcopclassdir = $(libdir)/mcop
+mcopclass_DATA = WAVPlayObject.mcopclass \
+ MP3PlayObject.mcopclass \
+ OGGPlayObject.mcopclass \
+ CDDAPlayObject.mcopclass \
+ NULLPlayObject.mcopclass \
+ SplayPlayObject.mcopclass
+
+
+
+EXTRA_DIST = doemacs \
+ configure.in.in \
+ decoderBaseObject.idl \
+ splayPlayObject.idl \
+ $(mcopclass_DATA)
+
+
+BUILT_SOURCES = decoderBaseObject.h \
+ decoderBaseObject.cc \
+ splayPlayObject.h \
+ splayPlayObject.cc
+
+
+noinst_HEADERS = mp3PlayObject_impl.h \
+ oggPlayObject_impl.h \
+ decoderBaseObject_impl.h \
+ mpgPlayObject_impl.h \
+ wavPlayObject_impl.h \
+ cddaPlayObject_impl.h \
+ vcdPlayObject_impl.h \
+ nullPlayObject_impl.h \
+ splayPlayObject_impl.h
+
+
+ARTS_INCLUDES = $(ARTSC_INCLUDE) \
+ -I$(kde_includes)/arts
+
+
+INCLUDES = -I../mpeglib/lib -I$(srcdir)/../mpeglib/lib $(ARTS_INCLUDES) $(all_includes)
+
+
+lib_LTLIBRARIES = libarts_mpeglib.la \
+ libarts_splay.la
+
+decoderBaseObject.cc decoderBaseObject.h : $(srcdir)/decoderBaseObject.idl
+ $(MCOPIDL) -I$(kde_includes)/arts $(srcdir)/decoderBaseObject.idl
+
+splayPlayObject.cc splayPlayObject.h : $(srcdir)/splayPlayObject.idl
+ $(MCOPIDL) -I$(kde_includes)/arts $(srcdir)/splayPlayObject.idl
+
+mpeglibartsplugdir = $(includedir)/mpeglib_artsplug
+
+mpeglibartsplug_HEADERS = decoderBaseObject_impl.h \
+ decoderBaseObject.h \
+ decoderBaseObject.idl \
+ splayPlayObject_impl.h \
+ splayPlayObject.h \
+ splayPlayObject.idl
+
+
+
+libarts_mpeglib_la_SOURCES = decoderBaseObject.cc \
+ splayPlayObject.cc \
+ decoderBaseObject_impl.cpp \
+ oggPlayObject_impl.cpp \
+ mpgPlayObject_impl.cpp \
+ wavPlayObject_impl.cpp \
+ mp3PlayObject_impl.cpp \
+ cddaPlayObject_impl.cpp \
+ vcdPlayObject_impl.cpp \
+ nullPlayObject_impl.cpp
+libarts_mpeglib_la_COMPILE_FIRST = decoderBaseObject.h
+
+libarts_mpeglib_la_LDFLAGS = $(all_libraries) \
+ -no-undefined \
+ -module \
+ -release $(MPEGLIB_ARTS_VERSION) \
+ -version-info $(MPEGLIB_ARTS_MAJOR_VERSION):$(MPEGLIB_ARTS_MINOR_VERSION):$(MPEGLIB_ARTS_MICRO_VERSION)
+libarts_mpeglib_la_LIBADD = $(top_builddir)/mpeglib/lib/libmpeg.la \
+ $(LIB_ARTS)
+
+libarts_splay_la_SOURCES = splayPlayObject.cc \
+ splayPlayObject_impl.cpp
+
+libarts_splay_la_LDFLAGS = $(all_libraries) \
+ -no-undefined \
+ -module
+libarts_splay_la_LIBADD = $(top_builddir)/mpeglib/lib/libmpeg.la \
+ $(LIB_ARTS)
+
+
+bin_PROGRAMS = mpeglibartsplay
+
+mpeglibartsplay_LDFLAGS = $(ARTSC_LIBS) $(all_libraries)
+mpeglibartsplay_SOURCES = mpeglibartsplay.cpp
+
+mpeglibartsplay_LDADD = -lsoundserver_idl \
+ -lkmedia2_idl \
+ -lartsflow_idl \
+ -lmcop
+
+splayPlayObject.lo: splayPlayObject.h
+splayPlayObject_impl.lo: splayPlayObject.h
diff --git a/mpeglib_artsplug/NULLPlayObject.mcopclass b/mpeglib_artsplug/NULLPlayObject.mcopclass
new file mode 100644
index 00000000..296fcc9b
--- /dev/null
+++ b/mpeglib_artsplug/NULLPlayObject.mcopclass
@@ -0,0 +1,7 @@
+Interface=NULLPlayObject,Arts::PitchablePlayObject,Arts::PlayObject,Arts::SynthModule,Arts::Object
+Author="Martin Vogt <mvogt@rhrk.uni-kl.de>"
+URL="http://mpeglib.sourceforge.net/"
+Extension=null,
+MimeType=null/null,
+Language=C++
+Library=libarts_mpeglib.la
diff --git a/mpeglib_artsplug/OGGPlayObject.mcopclass b/mpeglib_artsplug/OGGPlayObject.mcopclass
new file mode 100644
index 00000000..d41424b2
--- /dev/null
+++ b/mpeglib_artsplug/OGGPlayObject.mcopclass
@@ -0,0 +1,8 @@
+Interface=OGGPlayObject,Arts::PitchablePlayObject,Arts::PlayObject,Arts::StreamPlayObject,Arts::SynthModule,Arts::Object
+Author="Martin Vogt <mvogt@rhrk.uni-kl.de>"
+URL="http://mpeglib.sourceforge.net"
+Extension=ogg
+MimeType=audio/vorbis,application/ogg
+Language=C++
+Library=libarts_mpeglib.la
+Preference=3
diff --git a/mpeglib_artsplug/README b/mpeglib_artsplug/README
new file mode 100644
index 00000000..20015161
--- /dev/null
+++ b/mpeglib_artsplug/README
@@ -0,0 +1,78 @@
+
+
+1) compile arts snapshot with
+
+ ./configure --prefix=/tmp/arts
+ ^^^^^^^^^^^^^^^^^^
+ needed
+
+ This Makefile depends on the assumption that arts is in /tmp/arts !
+
+2) Put the arts libraries into your library search path:
+
+ edit ld.so.conf
+ add: /tmp/arts/lib
+ /sbin/ldconfig
+
+ ---> Make sure the arts path is BEFORE your KDE Library path!
+ (you will link against old arts versions and will get
+ "undefined references")
+
+4) compile & install mpeglib
+
+ ./configure
+ make <-- mabe you get errors here , because I have XFree4.0
+ make install
+
+5) add the library search path for mpeg lib to /etc/ld.so.conf
+ see 2)
+ Per default mpeglib installs in /usr/lib
+
+6) Now build the arts plugins in this directory
+
+ * You should edit the Makefile and remove the XFree4.0 dependecies
+
+Makefile:
+ -lmpeg -lX11 -lXext -lXt -lXv -lXxf86dga -lpthread
+# ^^^^^^^^^^^^^^^
+# needed if you have XFree4.0
+
+
+
+ Type in this directory
+
+ make
+
+ then pray.
+
+ Now check the library dependcies.
+ A success looks similar to this:
+[m_vogt@mv arts]$ ldd ./libarts_splay.so
+ libmpeg--version-info.so => /usr/local/lib/libmpeg--version-info.so (0x40022000)
+ libX11.so.6 => /usr/X11R6/lib/libX11.so.6 (0x4009d000)
+ libXext.so.6 => /usr/X11R6/lib/libXext.so.6 (0x4015f000)
+ libXt.so.6 => /usr/X11R6/lib/libXt.so.6 (0x4016c000)
+ libpthread.so.0 => /lib/libpthread.so.0 (0x401b5000)
+ libstdc++-libc6.1-1.so.2 => /usr/lib/libstdc++-libc6.1-1.so.2 (0x401c8000)
+ libm.so.6 => /lib/libm.so.6 (0x4020a000)
+ libc.so.6 => /lib/libc.so.6 (0x40227000)
+ libSM.so.6 => /usr/X11R6/lib/libSM.so.6 (0x4031c000)
+ libICE.so.6 => /usr/X11R6/lib/libICE.so.6 (0x40325000)
+ /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x80000000)
+
+
+If you get undefined references read: 2.
+
+
+7.) Now install the lib
+
+ make install
+
+
+8) start artsd
+
+9) start the ./artsplay in the current directoy with a given mp3
+
+
+
+
diff --git a/mpeglib_artsplug/SplayPlayObject.mcopclass b/mpeglib_artsplug/SplayPlayObject.mcopclass
new file mode 100644
index 00000000..ae366b36
--- /dev/null
+++ b/mpeglib_artsplug/SplayPlayObject.mcopclass
@@ -0,0 +1,8 @@
+Interface=SplayPlayObject,Arts::PitchablePlayObject,Arts::StreamPlayObject,Arts::SynthModule,Arts::Object
+Author="Martin Vogt <mvogt@rhrk.uni-kl.de>"
+URL="http://mpeglib.sourceforge.net"
+Extension=mp3,mp1,mp2
+MimeType=audio/x-mp3,audio/x-mp1,audio/x-mp2
+Language=C++
+Library=libarts_splay.la
+Preference=2
diff --git a/mpeglib_artsplug/TODO b/mpeglib_artsplug/TODO
new file mode 100644
index 00000000..8595715a
--- /dev/null
+++ b/mpeglib_artsplug/TODO
@@ -0,0 +1,19 @@
+
+
+* The arts plugin interface is not touched by me for the
+ next time. Thus you have a good chance that your patches
+ will be applied.
+
+ I have to work on better fullscreen support and other things
+ in mpeglib. And I have to update the vorbis plugin.
+
+
+* The seeking and info things are only experimental.
+ They show you how it works in mpeglib, but are not usefull
+ for a gui. patch welcome.
+
+* MPEG Video Currently exist with :
+ XIO: fatal IO error 0 (Success) on X server ":0.0"
+ ^^^^^ ^^^^^^^^ ?->funny
+
+ after 14 requests (7 known processed) with 0 events remaining.
diff --git a/mpeglib_artsplug/VCDPlayObject.mcopclass b/mpeglib_artsplug/VCDPlayObject.mcopclass
new file mode 100644
index 00000000..993a0140
--- /dev/null
+++ b/mpeglib_artsplug/VCDPlayObject.mcopclass
@@ -0,0 +1,7 @@
+Interface=VCDPlayObject,Arts::PitchablePlayObject,Arts::PlayObject,Arts::SynthModule,Arts::Object
+Author="Martin Vogt <mvogt@rhrk.uni-kl.de>"
+URL="http://mpeglib.sourceforge.net/"
+Extension=vcd,
+MimeType=video/x-vcd,
+Language=C++
+Library=libarts_mpeglib.la
diff --git a/mpeglib_artsplug/WAVPlayObject.mcopclass b/mpeglib_artsplug/WAVPlayObject.mcopclass
new file mode 100644
index 00000000..d368a121
--- /dev/null
+++ b/mpeglib_artsplug/WAVPlayObject.mcopclass
@@ -0,0 +1,7 @@
+Interface=WAVPlayObject,Arts::PitchablePlayObject,Arts::PlayObject,Arts::StreamPlayObject,Arts::SynthModule,Arts::Object
+Author="Martin Vogt <mvogt@rhrk.uni-kl.de>"
+URL="http://mpeglib.sourceforge.net"
+Extension=wav,au
+MimeType=audio/x-wav,audio/x-au
+Language=C++
+Library=libarts_mpeglib.la
diff --git a/mpeglib_artsplug/cddaPlayObject_impl.cpp b/mpeglib_artsplug/cddaPlayObject_impl.cpp
new file mode 100644
index 00000000..f5b85ca8
--- /dev/null
+++ b/mpeglib_artsplug/cddaPlayObject_impl.cpp
@@ -0,0 +1,48 @@
+/*
+ class for mp3-mpeglib decoder
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include "cddaPlayObject_impl.h"
+#include "debug.h"
+#include <iostream>
+
+
+
+CDDAPlayObject_impl::CDDAPlayObject_impl() {
+ arts_debug("CDDAPlayObject_impl");
+}
+
+CDDAPlayObject_impl::~CDDAPlayObject_impl() {
+ arts_debug("~CDDAPlayObject_impl");
+}
+
+
+DecoderPlugin* CDDAPlayObject_impl::createPlugin() {
+ std::cout << "CDDAPlayObject_impl::getPlugin"<<std::endl;
+ return new CDDAPlugin();
+}
+
+InputStream* CDDAPlayObject_impl::createInputStream(const char* ) {
+ cout << "CDDAPlayObject_impl::createInputStream"<<endl;
+ InputStream* back;
+ back=InputPlugin::createInputStream(__INPUT_CDDA,_INPUT_THREADSAFE);
+ return back;
+}
+
+void CDDAPlayObject_impl::calculateBlock(unsigned long samples) {
+ DecoderBaseObject_impl::calculateBlock(samples,left,right);
+}
+
+
+
+REGISTER_IMPLEMENTATION(CDDAPlayObject_impl);
+
+
diff --git a/mpeglib_artsplug/cddaPlayObject_impl.h b/mpeglib_artsplug/cddaPlayObject_impl.h
new file mode 100644
index 00000000..b68aba0d
--- /dev/null
+++ b/mpeglib_artsplug/cddaPlayObject_impl.h
@@ -0,0 +1,43 @@
+/*
+ class for mp3-mpeglib decoder
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __CDDAPLAYOBJECT_IMPL_H
+#define __CDDAPLAYOBJECT_IMPL_H
+
+
+#include "decoderBaseObject_impl.h"
+#include "../mpeglib/lib/decoder/cddaPlugin.h"
+
+
+class CDDAPlayObject_impl:
+ virtual public DecoderBaseObject_impl,
+ virtual public CDDAPlayObject_skel {
+
+
+
+public:
+
+ CDDAPlayObject_impl();
+ virtual ~CDDAPlayObject_impl();
+
+
+ DecoderPlugin* createPlugin();
+
+ InputStream* createInputStream(const char* url);
+
+ void calculateBlock(unsigned long samples);
+
+};
+
+
+#endif
diff --git a/mpeglib_artsplug/configure.in.in b/mpeglib_artsplug/configure.in.in
new file mode 100644
index 00000000..e5a3023f
--- /dev/null
+++ b/mpeglib_artsplug/configure.in.in
@@ -0,0 +1,3 @@
+if test "x$build_arts" = "xno"; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE mpeglib_artsplug"
+fi
diff --git a/mpeglib_artsplug/decoderBaseObject.idl b/mpeglib_artsplug/decoderBaseObject.idl
new file mode 100644
index 00000000..678a9da1
--- /dev/null
+++ b/mpeglib_artsplug/decoderBaseObject.idl
@@ -0,0 +1,58 @@
+#include "kmedia2.idl"
+#include "artsflow.idl"
+
+/* the interfaces below are not kept binary compatible */
+
+interface DecoderBaseObject :
+ Arts::StreamPlayObject,
+ Arts::SynthModule,
+ Arts::PitchablePlayObject
+{
+
+ async in byte stream indata;
+ /**
+ * blocking flag - defaults to false - when set to true, mpeglib will not
+ * try to minimize latencies by generating answers (i.e. empty blocks)
+ * when the thread producing the data wasn't ready
+ */
+ attribute boolean blocking;
+};
+
+
+interface MP3PlayObject : DecoderBaseObject
+{
+ out audio stream left,right;
+};
+
+
+interface WAVPlayObject : DecoderBaseObject
+{
+ out audio stream left,right;
+};
+
+
+interface MPGPlayObject : DecoderBaseObject
+{
+ out audio stream left,right;
+};
+
+
+interface OGGPlayObject : DecoderBaseObject
+{
+ out audio stream left,right;
+};
+
+interface CDDAPlayObject : DecoderBaseObject
+{
+ out audio stream left,right;
+};
+
+interface VCDPlayObject : DecoderBaseObject
+{
+ out audio stream left,right;
+};
+
+interface NULLPlayObject : Arts::PlayObject , Arts::SynthModule
+{
+ out audio stream left,right;
+};
diff --git a/mpeglib_artsplug/decoderBaseObject_impl.cpp b/mpeglib_artsplug/decoderBaseObject_impl.cpp
new file mode 100644
index 00000000..bd69a901
--- /dev/null
+++ b/mpeglib_artsplug/decoderBaseObject_impl.cpp
@@ -0,0 +1,620 @@
+/*
+ base class for all mpeglib decoders
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include <queue>
+#include <iostream>
+
+#include <connect.h>
+
+#include "decoderBaseObject_impl.h"
+#include "../mpeglib/lib/decoder/decoderPlugin.h"
+#include "debug.h"
+
+// define this to run the playobject without the
+// arts backend. (useful to check if a bug is in arts or mpeglib)
+//#define _STRIP_ZERO
+
+static int instanceCnt=0;
+
+DecoderBaseObject_impl::DecoderBaseObject_impl()
+ : _speed(1.0f)
+{
+
+ flpos=0.0;
+ _blocking = false;
+
+#ifdef _STRIP_ZERO
+ outputStream=NULL;
+#else
+ m_outputStream=new ArtsOutputStream(NULL);
+ arts_debug("outputStream created");
+ decoderPlugin=NULL;
+#endif
+ startTime=0.0;
+ m_inputStream=NULL;
+ setStreamState(_THREADSTATE_INIT);
+ _state=Arts::posIdle;
+ instance=instanceCnt;
+ instanceCnt++;
+
+ m_packetQueue = new std::queue<DataPacket<mcopbyte>*>;
+}
+
+DecoderBaseObject_impl::~DecoderBaseObject_impl() {
+ arts_debug("~DecoderBaseObject_impl -s");
+ shudownPlugins();
+
+ if (decoderPlugin != NULL) {
+ arts_debug("delete decoderPlugin");
+ delete decoderPlugin;
+ decoderPlugin=NULL;
+ }
+ if (m_outputStream != NULL) {
+ arts_debug("delete outputStream");
+ delete m_outputStream;
+ m_outputStream=NULL;
+ }
+
+ if (m_streaming)
+ m_artsInputStream.streamEnd();
+
+ delete m_packetQueue;
+}
+
+
+DecoderPlugin* DecoderBaseObject_impl::createPlugin() {
+ arts_fatal("direct virtual call DecoderBaseObject_impl::getPlugin");
+ return NULL;
+}
+
+
+InputStream* DecoderBaseObject_impl::createInputStream(const char* url) {
+ InputStream* back = InputPlugin::createInputStream(url,true);
+ return back;
+}
+
+
+bool DecoderBaseObject_impl::loadMedia(const string &filename) {
+ arts_debug("loadMedia");
+ int back=true;
+
+ m_streaming = false;
+
+ if ( m_inputStream != NULL ) {
+ arts_fatal("remove resources first with a call to: halt()");
+ }
+ if (decoderPlugin == NULL) {
+ decoderPlugin=createPlugin();
+ if(doFloat()) decoderPlugin->config("dofloat",0,0);
+ }
+
+ flpos=0.0;
+ startTime=0.0;
+
+ lastAudioBufferSize=-1;
+ /**
+ Note: you can only play one file with a PlayObject !!
+ Then you must destroy it.
+ A StreamEnd call should do the job.
+ */
+
+#ifdef _STRIP_ZERO
+ return true;
+#endif
+
+ m_inputStream=createInputStream(filename.c_str());
+
+ // the plugin does not open the stream!
+ // we do it.
+ back=m_inputStream->open((char*)filename.c_str());
+ setStreamState(_THREADSTATE_OPENED);
+
+ // we are still in posIdle here
+ m_outputStream->audioOpen();
+
+ // watch the order!
+ decoderPlugin->setOutputPlugin(m_outputStream);
+ decoderPlugin->setInputPlugin(m_inputStream);
+
+ return back;
+}
+
+#define INPUT_BUFFER_SIZE 32768
+
+bool DecoderBaseObject_impl::streamMedia(Arts::InputStream instream) {
+ arts_debug("DecoderBaseObject_impl::streamMedia -s");
+
+ bool back = true;
+
+ if (m_inputStream != NULL) {
+ arts_fatal("resource in use, call halt() first");
+ }
+ if (decoderPlugin == NULL) {
+ decoderPlugin = createPlugin();
+ if (doFloat())
+ decoderPlugin->config("dofloat", 0, 0);
+ // streaming, don't know the length
+ decoderPlugin->config("-c", 0, 0);
+ }
+
+ flpos = 0.0;
+ startTime = 0.0;
+ m_streaming = true;
+ lastAudioBufferSize = -1;
+
+ m_artsInputStream = instream;
+
+ m_inputStream = new BufferInputStream(INPUT_BUFFER_SIZE, 4096, (char*)"InputStream");
+ m_inputStream->open((char*)"InputStream");
+
+ // connect the stream now
+ Arts::StreamPlayObject self = Arts::StreamPlayObject::_from_base(_copy());
+ connect(m_artsInputStream, "outdata", self);
+
+ setStreamState(_THREADSTATE_OPENED);
+
+ m_outputStream->audioOpen();
+
+ decoderPlugin->setOutputPlugin(m_outputStream);
+ decoderPlugin->setInputPlugin(m_inputStream);
+
+ arts_debug("DecoderBaseObject_impl::streamMedia -e");
+
+ return back;
+}
+
+void DecoderBaseObject_impl::process_indata(DataPacket<mcopbyte> *inpacket) {
+
+ m_packetQueue->push(inpacket);
+ processQueue();
+}
+
+void DecoderBaseObject_impl::processQueue() {
+
+ // early exit if no packets in the queue
+ if (m_packetQueue->empty())
+ return;
+
+ // see how much space we have in the stream
+ BufferInputStream* stream = static_cast<BufferInputStream*>(m_inputStream);
+ if (!stream) return;
+
+ int length = stream->getByteLength();
+ int freeSpace = INPUT_BUFFER_SIZE - length;
+
+ DataPacket<mcopbyte> *inpacket = m_packetQueue->front();
+ if (!inpacket) return;
+
+ if (freeSpace >= inpacket->size) {
+ stream->write((char*)inpacket->contents, inpacket->size, 0);
+ m_packetQueue->pop();
+ inpacket->processed();
+ }
+}
+
+string DecoderBaseObject_impl::description() {
+ arts_debug("description");
+ string back;
+#ifdef _STRIP_ZERO
+ return back;
+#endif
+ PluginInfo* pluginInfo=decoderPlugin->getPluginInfo();
+ pluginInfo->print();
+ return back;
+
+}
+
+void DecoderBaseObject_impl::description(const string &) {
+ arts_debug("description");
+ // what should do this?
+}
+
+poTime DecoderBaseObject_impl::currentTime() {
+ poTime time;
+#ifdef _STRIP_ZERO
+ return time;
+#endif
+ AudioTime* audioTime=m_outputStream->getAudioTime();
+ float currentTime=audioTime->getTime()+(float)startTime;
+ time.seconds=(long)(currentTime);
+ time.ms=(long) (1000.0*(currentTime-(float)time.seconds));
+ return time;
+}
+
+
+
+poTime DecoderBaseObject_impl::overallTime() {
+ poTime time;
+#ifdef _STRIP_ZERO
+ return time;
+#endif
+
+ PluginInfo* pluginInfo=decoderPlugin->getPluginInfo();
+ time.seconds=pluginInfo->getLength();
+ time.ms=0;
+ return time;
+}
+
+poCapabilities DecoderBaseObject_impl::capabilities() {
+ arts_debug("capabilities");
+#ifdef _STRIP_ZERO
+ return capSeek;
+#endif
+ PluginInfo* pluginInfo=decoderPlugin->getPluginInfo();
+ long len=pluginInfo->getLength();
+ if (len == 0) {
+ return Arts::capPause; /* no seek supported */
+ }
+ // seek and pause supported
+ return (poCapabilities)(Arts::capSeek | Arts::capPause);
+}
+
+string DecoderBaseObject_impl::mediaName() {
+ arts_debug("mediaName");
+ string back;
+ // whats a mediaName?
+ return back;
+}
+
+poState DecoderBaseObject_impl::state() {
+ return _state;
+}
+
+void DecoderBaseObject_impl::play() {
+ arts_debug("play: %d", (int)streamState);
+ if (streamState == _THREADSTATE_OPENED) {
+ decoderPlugin->play();
+ } else {
+ Command cmd(_COMMAND_PLAY);
+ decoderPlugin->insertAsyncCommand(&cmd);
+ }
+ setStreamState(_THREADSTATE_PLAYING);
+ _state = Arts::posPlaying;
+}
+
+void DecoderBaseObject_impl::seek(const class poTime& seekTime) {
+#ifdef _STRIP_ZERO
+ return;
+#endif
+
+ long sec=seekTime.seconds;
+
+ arts_debug("sec in plugin is %d:", sec);
+
+ // we send an async command
+ Command cmd(_COMMAND_SEEK,sec);
+ decoderPlugin->insertAsyncCommand(&cmd);
+
+ // if the thread blocks on the artsOutputstream: kick him out
+ // the next command will the the seek command
+ m_outputStream->audioClose();
+
+
+ // thread blocking allowed
+ m_outputStream->audioOpen();
+ arts_debug("************ reopen");
+ // now set a new startTime
+ startTime=sec;
+}
+
+void DecoderBaseObject_impl::pause() {
+ arts_debug("pause");
+ _state = Arts::posPaused;
+ Command cmd(_COMMAND_PAUSE);
+ decoderPlugin->insertAsyncCommand(&cmd);
+}
+
+void DecoderBaseObject_impl::halt() {
+ /*
+ *
+ * halt() (which the normal programmer would probably refer to as stop())
+ * should seek to the beginning and go into the posIdle state, like a just
+ * opened PlayObject
+ *
+ */
+
+ arts_debug("halt");
+ _state=Arts::posIdle;
+ shudownPlugins();
+}
+
+
+void DecoderBaseObject_impl::streamInit() {
+#ifdef _STRIP_ZERO
+ return;
+#endif
+
+}
+
+
+void DecoderBaseObject_impl::streamStart() {
+ arts_debug("DecoderBaseObject_impl::streamStart");
+}
+
+int DecoderBaseObject_impl::fillArts(unsigned long samples,
+ float* left , float* right) {
+ unsigned long haveSamples = 0;
+
+ AudioTime* audioTime=m_outputStream->getAudioTime();
+ int wav_samplingRate=audioTime->getSpeed();
+ int wav_sampleWidth=audioTime->getSampleSize();
+ int wav_channelCount=audioTime->getStereo()+1;
+
+ if(doFloat()) wav_sampleWidth = sizeof(float)*8;
+
+ // here seems to be an error, I have clicks sometimes in the stream
+ //int byteMultiplikator=(wav_sampleWidth/8)*wav_channelCount;
+
+ // maybe first multiply, then divide?
+ int byteMultiplikator = wav_channelCount * wav_sampleWidth / 8;
+
+ char* buffer;
+ int hasBytes = 0;
+ int wantBytes = 0;
+ int bufferSize=getBufferSize();
+ if (bufferSize != lastAudioBufferSize) {
+ lastAudioBufferSize=bufferSize;
+ m_outputStream->setAudioBufferSize(bufferSize);
+ }
+
+ /* difference between the sampling rates in percent */
+ float diff = fabs((double)wav_samplingRate - (double)(samplingRateFloat/_speed))
+ / (double)samplingRateFloat;
+
+ /*
+ * efficient optimized case:
+ * 1. decoder -> float rendering
+ * 2. no resampling (i.e. artsd running @ 44100 Hz, playing an 44100 Hz mp3)
+ */
+ if(_state == Arts::posPlaying && doFloat() && diff < 0.0005) {
+ wantBytes = sizeof(float) * wav_channelCount * samples;
+ hasBytes = m_outputStream->read(&buffer,wantBytes);
+ float *flptr = (float *)buffer;
+
+ if(wav_channelCount == 1)
+ {
+ while((int)(haveSamples * sizeof(float)) < hasBytes)
+ {
+ left[haveSamples] = right[haveSamples] = flptr[haveSamples];
+ haveSamples++;
+ }
+ }
+ else if(wav_channelCount == 2)
+ {
+ while((int)(haveSamples * 2 * sizeof(float)) < hasBytes)
+ {
+ left[haveSamples] = flptr[haveSamples*2];
+ right[haveSamples] = flptr[haveSamples*2+1];
+ haveSamples++;
+ }
+ }
+ m_outputStream->forwardReadPtr(haveSamples*sizeof(float)*wav_channelCount);
+ }
+ else if(_state == Arts::posPlaying) {
+ //
+ // since the samplingrate of the MP3 and the samplingrate of the output
+ // device (soundcard) are not necessarily the same, it's a bit tricky
+ //
+
+ // calculate "how fast" we consume input samples (2.0 means, we need 2
+ // input samples to generate one output sample)
+ double speed = (double)wav_samplingRate / (double)(samplingRateFloat/_speed);
+
+ // calculate how many input samples we need, then to satisfy the request
+ // use a larger amount than "really" required, to ensure that samples are
+ // available for rounding errors and interpolation
+ double wantWavSamples = (double)samples*speed+8.0;
+
+ // convert that into bytes and try to read that many bytes
+ wantBytes=(int) (wantWavSamples*byteMultiplikator);
+ hasBytes=m_outputStream->read(&buffer,wantBytes);
+
+ int format = doFloat()?Arts::uni_convert_float_ne:wav_sampleWidth;
+
+ // convert those bytes into the suitable output form
+ haveSamples = Arts::uni_convert_stereo_2float(samples, (unsigned char *)buffer,
+ hasBytes, wav_channelCount,
+ format,
+ left,right,speed,flpos);
+
+ // calculate where we are now (as floating point position) in our
+ // inputsample buffer
+ flpos += (double)haveSamples * speed;
+
+ // Good - so how many input samples we won't need anymore (for the
+ // next request)? Skip them.
+ int skip = (int)floor(flpos);
+ // we need to call this even on skip == 0
+ // because we must unlock the remoteBuffer
+ int forward=skip*byteMultiplikator;
+
+
+ flpos = flpos - floor(flpos);
+
+ m_outputStream->forwardReadPtr(forward);
+ }
+
+ if(haveSamples != samples) {
+
+ unsigned long i;
+
+ for(i=haveSamples;i<samples;i++)
+ left[i] = right[i] = 0.0;
+
+ }
+ return samples;
+}
+
+void DecoderBaseObject_impl::calculateBlock(unsigned long samples,
+ float* left , float* right) {
+
+
+#ifndef _STRIP_ZERO
+
+ int audioState=m_outputStream->waitStreamState(_OUTPUT_WAIT_METHOD_POLL,
+ _STREAM_MASK_ALL,
+ _STREAMTYPE_AUDIO);
+
+ if (audioState & _STREAM_MASK_IS_INIT) {
+ // now check if we already have enough data
+ int lenough=false;
+ if (audioState & _STREAM_MASK_IS_EOF) {
+ if(_state == Arts::posPlaying) {
+ arts_debug("eof got in arts********** END");
+ _state = Arts::posIdle;
+ }
+
+ lenough=true;
+ }
+ if (m_outputStream->getBufferFillgrade() >= 4096) {
+ lenough=true;
+ }
+
+ if (_state == Arts::posPlaying) {
+ if (m_streaming) {
+ // produce more data
+ processQueue();
+ // check for stream end
+ if ( m_inputStream->getByteLength() == 0 ) {
+ if ( m_artsInputStream.eof() ) {
+ m_inputStream->close();
+ m_artsInputStream.streamEnd();
+ }
+ }
+ }
+ if (lenough || _blocking) {
+ fillArts(samples, left, right);
+ return;
+ }
+ }
+ }
+#endif
+
+ // filling with zero , stream not ready(yet)
+
+ unsigned int i;
+ for(i=0;i<samples;i++)
+ left[i] = right[i] = 0.0;
+}
+
+void DecoderBaseObject_impl::streamEnd() {
+ arts_debug("streamEnd");
+#ifdef _STRIP_ZERO
+ return;
+#endif
+
+ halt();
+}
+
+
+int DecoderBaseObject_impl::getBufferSize() {
+ float hardwareBuffer;
+ float fragmentSize;
+ float fragmentCount;
+ float channels;
+ float sampleSize;
+ fragmentSize=Arts::AudioSubSystem::the()->fragmentSize();
+ fragmentCount=Arts::AudioSubSystem::the()->fragmentCount();
+ channels=Arts::AudioSubSystem::the()->channels();
+ sampleSize=16.0/8.0;
+
+ hardwareBuffer=fragmentSize*fragmentCount;
+
+
+ return (int)hardwareBuffer;
+}
+
+
+void DecoderBaseObject_impl::shudownPlugins() {
+ arts_debug("shudownPlugins -s");
+ /**
+ The order here is important.
+ First we close the audio so that the thread never blocks
+ on the ringbuffer.
+ Then we are sure thst we can safley call plugin->close,
+ because the thread does not block.
+ We then have the thread back in the decoder_loop of
+ the plugin.
+ */
+
+ // this should theoretically be faster
+ if (decoderPlugin != NULL) {
+ Command cmd(_COMMAND_CLOSE);
+ decoderPlugin->insertAsyncCommand(&cmd);
+ }
+ if (m_outputStream != NULL) {
+ m_outputStream->audioClose();
+ }
+
+ // very likely the thread already is closed
+ // because of the asyncCommand above.
+ if (decoderPlugin) {
+ decoderPlugin->close();
+ }
+
+ delete m_inputStream;
+ m_inputStream=NULL;
+
+ if (m_streaming)
+ m_artsInputStream.streamEnd();
+
+ setStreamState(_THREADSTATE_CLOSED);
+
+ arts_debug("shudownPlugins -e");
+}
+
+void DecoderBaseObject_impl::setStreamState(int state) {
+
+ switch (state) {
+ case _THREADSTATE_INIT: {
+ streamState=_THREADSTATE_INIT;
+ break;
+ }
+ case _THREADSTATE_OPENED: {
+ streamState=_THREADSTATE_OPENED;
+ break;
+ }
+ case _THREADSTATE_PLAYING: {
+ streamState=_THREADSTATE_PLAYING;
+ break;
+ }
+ case _THREADSTATE_CLOSED: {
+ streamState=_THREADSTATE_INIT;
+ break;
+ }
+ default:
+ std::cerr << "unknown streamState:DecoderBaseObject_impl:"<<state<<std::endl;
+ }
+
+}
+
+/** internal stuff for testing **/
+
+void DecoderBaseObject_impl::blocking(bool newvalue)
+{
+ _blocking = newvalue;
+}
+
+bool DecoderBaseObject_impl::blocking()
+{
+ return _blocking;
+}
+
+void DecoderBaseObject_impl::speed(float newValue)
+{
+ _speed= newValue;
+}
+
+float DecoderBaseObject_impl::speed()
+{
+ return _speed;
+}
diff --git a/mpeglib_artsplug/decoderBaseObject_impl.h b/mpeglib_artsplug/decoderBaseObject_impl.h
new file mode 100644
index 00000000..6ed3f6da
--- /dev/null
+++ b/mpeglib_artsplug/decoderBaseObject_impl.h
@@ -0,0 +1,121 @@
+/*
+ base class for all mpeglib decoders
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __DECODERBASEOBJECT_IMPL_H
+#define __DECODERBASEOBJECT_IMPL_H
+
+
+#include <math.h>
+#include "decoderBaseObject.h"
+#include "stdsynthmodule.h"
+#include "convert.h"
+#include <X11/Xlib.h>
+#include <audiosubsys.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <arts/kmedia2.h>
+
+#define _THREADSTATE_INIT 0
+#define _THREADSTATE_OPENED 1
+#define _THREADSTATE_PAUSED 2
+#define _THREADSTATE_PLAYING 3
+#define _THREADSTATE_CLOSED 4
+
+#include <queue>
+
+class DecoderPlugin;
+class InputStream;
+class ArtsOutputStream;
+
+using namespace std;
+using Arts::poState;
+using Arts::poTime;
+using Arts::poCapabilities;
+using Arts::DataPacket;
+using Arts::mcopbyte;
+
+class DecoderBaseObject_impl :
+ virtual public Arts::StdSynthModule,
+ virtual public DecoderBaseObject_skel {
+
+ poState _state;
+ InputStream* m_inputStream;
+ Arts::InputStream m_artsInputStream;
+ ArtsOutputStream* m_outputStream;
+
+ double flpos;
+ float startTime;
+ int instance;
+ int lastAudioBufferSize;
+ int streamState;
+ bool _blocking;
+ float _speed;
+
+ bool m_streaming;
+
+ queue<DataPacket<mcopbyte>*> *m_packetQueue;
+
+public:
+
+ DecoderBaseObject_impl();
+ virtual ~DecoderBaseObject_impl();
+
+ virtual DecoderPlugin* createPlugin();
+ virtual InputStream* createInputStream(const char* url);
+ virtual bool doFloat() { return false; }
+
+ bool loadMedia(const string &filename);
+ bool streamMedia(Arts::InputStream instream);
+ void process_indata(DataPacket<mcopbyte>* inpacket);
+ Arts::InputStream inputStream() { return m_artsInputStream; }
+
+ string description();
+ void description(const string &);
+
+ poTime currentTime();
+ poTime overallTime();
+
+ poCapabilities capabilities();
+ string mediaName();
+ poState state();
+ void play();
+ void halt();
+
+ void blocking(bool newvalue);
+ bool blocking();
+
+ void speed(float newValue);
+ float speed();
+
+ void seek(const class poTime &);
+ void pause();
+ void streamInit();
+ void streamStart();
+ void calculateBlock(unsigned long samples,float* left,float* right);
+ void streamEnd();
+
+ DecoderPlugin* decoderPlugin;
+
+ private:
+ void shudownPlugins();
+ int getBufferSize();
+ int fillArts(unsigned long samples,float* left , float* right);
+ void setStreamState(int state);
+ void processQueue();
+
+};
+
+
+
+#endif
diff --git a/mpeglib_artsplug/doemacs b/mpeglib_artsplug/doemacs
new file mode 100644
index 00000000..a875517d
--- /dev/null
+++ b/mpeglib_artsplug/doemacs
@@ -0,0 +1,29 @@
+
+
+A=`find . | grep -v moc > flist.txt`
+A=`grep "\.cpp$" flist.txt >cpp.txt`
+A=`grep "\.h$" flist.txt >h.txt`
+A=`grep "\.c$" flist.txt >c.txt`
+A=`grep "\.cc$" flist.txt >cc.txt`
+A=`grep "\.idl$" flist.txt >idl.txt`
+A=`grep "\.defs$" flist.txt >defs.txt`
+C=`cat c.txt`
+CP=`cat cpp.txt`
+H=`cat h.txt`
+CC=`cat cc.txt`
+IDL=`cat idl.txt`
+DEFS=`cat defs.txt`
+
+B="${CP} ${H} ${C} ${CC} ${DEFS} ${IDL}"
+
+emacs $B &
+rm -f cpp.txt
+rm -f h.txt
+rm c.txt
+rm cc.txt
+rm flist.txt
+rm defs.txt
+rm idl.txt
+
+
+
diff --git a/mpeglib_artsplug/mp3PlayObject_impl.cpp b/mpeglib_artsplug/mp3PlayObject_impl.cpp
new file mode 100644
index 00000000..4073ffbe
--- /dev/null
+++ b/mpeglib_artsplug/mp3PlayObject_impl.cpp
@@ -0,0 +1,45 @@
+/*
+ class for mp3-mpeglib decoder
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include "mp3PlayObject_impl.h"
+#include "debug.h"
+
+
+
+MP3PlayObject_impl::MP3PlayObject_impl() {
+ arts_debug("MP3PlayObject_impl");
+}
+
+MP3PlayObject_impl::~MP3PlayObject_impl() {
+ arts_debug("~MP3PlayObject_impl");
+}
+
+
+DecoderPlugin* MP3PlayObject_impl::createPlugin() {
+ arts_debug("MP3PlayObject_impl::getPlugin");
+ return new SplayPlugin();
+}
+
+bool MP3PlayObject_impl::doFloat()
+{
+ return true;
+}
+
+void MP3PlayObject_impl::calculateBlock(unsigned long samples) {
+ DecoderBaseObject_impl::calculateBlock(samples,left,right);
+}
+
+
+
+REGISTER_IMPLEMENTATION(MP3PlayObject_impl);
+
+
diff --git a/mpeglib_artsplug/mp3PlayObject_impl.h b/mpeglib_artsplug/mp3PlayObject_impl.h
new file mode 100644
index 00000000..22a421dc
--- /dev/null
+++ b/mpeglib_artsplug/mp3PlayObject_impl.h
@@ -0,0 +1,43 @@
+/*
+ class for mp3-mpeglib decoder
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __MP3PLAYOBJECT_IMPL_H
+#define __MP3PLAYOBJECT_IMPL_H
+
+
+#include "decoderBaseObject_impl.h"
+#include "../mpeglib/lib/decoder/splayPlugin.h"
+
+
+class MP3PlayObject_impl:
+ virtual public DecoderBaseObject_impl,
+ virtual public MP3PlayObject_skel {
+
+
+
+public:
+
+ MP3PlayObject_impl();
+ virtual ~MP3PlayObject_impl();
+
+ DecoderPlugin* createPlugin();
+ bool doFloat();
+
+
+
+ void calculateBlock(unsigned long samples);
+
+};
+
+
+#endif
diff --git a/mpeglib_artsplug/mpeglibartsplay.cpp b/mpeglib_artsplug/mpeglibartsplay.cpp
new file mode 100644
index 00000000..66b69150
--- /dev/null
+++ b/mpeglib_artsplug/mpeglibartsplay.cpp
@@ -0,0 +1,276 @@
+// vim:ts=2:sw=2:sts=2:et
+/**
+ Starter for plugins.
+ The plugins are identified by their extension
+
+ .wav ->WavPlayObject
+ .mp3 ->MP3PlayObject
+ .mpg ->MPGPlayObject
+ .ogg ->OGGPlayObject
+*/
+
+
+#include "soundserver.h"
+#include "kmedia2.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <vector>
+#include <string>
+#include <iostream>
+#include <climits>
+
+using namespace std;
+
+#if defined(HAVE_GETOPT_H)
+#include <getopt.h>
+#endif
+
+
+string file1;
+string file2;
+
+Arts::Dispatcher* d=0;
+Arts::SimpleSoundServer* server=0;
+
+
+
+void usage() {
+ cout << "mpeglibartsply command line tool for arts playobjects"<<endl;
+ cout << "Usage : mpeglibartsply [url]"<<endl;
+ cout << endl;
+ cout << "-1 : create/destroy PlayObject test."<<endl;
+ cout << "-2 : create/seek/play/destroy PlayObject test."<<endl;
+ cout << "-3 : torture seek implementation test"<<endl;
+ cout << "-h : help"<<endl;
+ cout << endl;
+ cout << "THIS SOFTWARE COMES WITH ABSOLUTELY NO WARRANTY! " \
+ << "USE AT YOUR OWN RISK!"<<endl;
+ cout << endl;
+ exit(0);
+}
+
+
+void initServer() {
+ d=new Arts::Dispatcher();
+ server= new Arts::SimpleSoundServer (Arts::Reference("global:Arts_SimpleSoundServer"));
+
+ if(server->isNull()) {
+ cerr << "Can't connect to sound server" << endl;
+ exit(0);
+ }
+
+}
+
+
+void destroyServer() {
+ delete server;
+ delete d;
+}
+
+/**
+ create destroy playobjects without playing anything
+*/
+void doStress1() {
+ int cnt=0;
+ while (cnt < 10000) {
+ Arts::PlayObject play=server->createPlayObject(file1);
+ cout << "cnt:"<<cnt++<<endl;
+ }
+}
+
+/**
+ create playobjects playing some time, do seek play destroy
+*/
+void doStress2() {
+ int cnt=0;
+ while (cnt < 1000) {
+ Arts::PlayObject play=server->createPlayObject(file1);
+ play.play();
+ sleep(3);
+ Arts::poTime seekTime;
+ seekTime.seconds=100;
+ seekTime.ms=0;
+ play.seek(seekTime);
+ cout << "cnt:"<<cnt++<<endl;
+ }
+}
+
+/**
+ create playobject seek random play seek again
+*/
+void doStress3() {
+ int cnt=0;
+ Arts::PlayObject play=server->createPlayObject(file1);
+ play.play();
+ long secs=0;
+ while(1) {
+ cout << "waiting for length info.."<<endl;
+ sleep(3);
+ Arts::poTime length=play.overallTime();
+ secs=length.seconds;
+ if (secs > 0) break;
+ }
+ // do not jump near to end, the danger that
+ // we get an eof is to high
+ if (secs < 100) {
+ cout << "file to short for test"<<endl;
+ exit(-1);
+ }
+ secs-=10;
+ Arts::poTime seekTime;
+ Arts::poTime startTime;
+ startTime.seconds=0;
+ startTime.ms=0;
+
+ while (cnt < 1000) {
+
+ //
+ // seek to a know position
+ //
+ play.seek(startTime);
+ // now wait for seek completion we need a poll here
+ while(1) {
+ Arts::poTime current=play.currentTime();
+ if (current.seconds < 5) {
+ break;
+ }
+ usleep(50000);
+ }
+
+ //
+ // now we know that the playObject is near the beginning
+ //
+
+ int randNo=rand();
+ long seekPos=(long)(((float)randNo*(float)secs)/(float)RAND_MAX);
+ // need to jump over the start area
+
+ seekPos+=5;
+ cout << "seek to :"<<seekPos
+ << " | cnt:"<<cnt++
+ << " of 1000"<<endl;
+ seekTime.seconds=seekPos;
+ seekTime.ms=0;
+ play.seek(seekTime);
+ // now wait for seek completion we need a poll here
+ while(1) {
+ Arts::poTime current=play.currentTime();
+ if (current.seconds > seekPos) {
+ break;
+ }
+ if (current.seconds == seekPos) {
+ if (current.ms > 0) {
+ break;
+ }
+ }
+ usleep(50000);
+ }
+
+
+ }
+ cout << "stresstest successfully passed."<<endl;
+}
+
+
+int main(int argc, char **argv) {
+ int testNr=0;
+ int c;
+ initServer();
+
+ while(1) {
+ c = getopt (argc, argv, "123456h");
+ if (c == -1) break;
+ switch(c) {
+ case '1': {
+ testNr=1;
+ break;
+ }
+ case '2': {
+ testNr=2;
+ break;
+ }
+ case '3': {
+ testNr=3;
+ break;
+ }
+ case '4': {
+ testNr=4;
+ break;
+ }
+ case '5': {
+ testNr=5;
+ break;
+ }
+ case '6': {
+ testNr=5;
+ break;
+ }
+ case 'h': {
+ usage();
+ break;
+ }
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ usage();
+ exit(-1);
+ }
+ }
+
+ if (optind >= argc ) {
+ usage();
+ exit(-1);
+ }
+
+ if (optind < argc ) {
+ file1=argv[optind];
+ optind++;
+ file2=file1;
+ }
+ if (optind < argc ) {
+ file2=argv[optind];
+ }
+
+
+ switch(testNr) {
+ case 1: {
+ doStress1();
+ break;
+ }
+ case 2: {
+ doStress2();
+ break;
+ }
+ case 3: {
+ doStress3();
+ break;
+ }
+ default:
+ if (file1[0] != '/') {
+ char buf[PATH_MAX+1];
+ char *path = getcwd(buf, PATH_MAX - file1.length());
+ if (path) {
+ file1.insert(0, "/");
+ file1.insert(0, path);
+ }
+ }
+ Arts::PlayObject play=server->createPlayObject(file1);
+ if (play.isNull()) {
+ cout << "cannot play this"<<endl;
+ destroyServer();
+ exit(0);
+ }
+ play.play();
+ while (play.state() != Arts::posIdle) {
+ sleep(1);
+ }
+ }
+
+ destroyServer();
+}
+
diff --git a/mpeglib_artsplug/mpgPlayObject_impl.cpp b/mpeglib_artsplug/mpgPlayObject_impl.cpp
new file mode 100644
index 00000000..3ad5a568
--- /dev/null
+++ b/mpeglib_artsplug/mpgPlayObject_impl.cpp
@@ -0,0 +1,53 @@
+/*
+ class for mpeg video decoder
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "mpgPlayObject_impl.h"
+#include "debug.h"
+
+
+
+
+
+MPGPlayObject_impl::MPGPlayObject_impl() {
+ arts_debug("MPGPlayObject_impl");
+ m_fullscreen = false;
+}
+
+MPGPlayObject_impl::~MPGPlayObject_impl() {
+ arts_debug("~MPGPlayObject_impl");
+}
+
+
+DecoderPlugin* MPGPlayObject_impl::createPlugin() {
+ return new MpgPlugin();
+}
+
+void MPGPlayObject_impl::calculateBlock(unsigned long samples) {
+ DecoderBaseObject_impl::calculateBlock(samples,left,right);
+}
+
+long int MPGPlayObject_impl::x11WindowId() {
+ return decoderPlugin->x11WindowId();
+}
+
+bool MPGPlayObject_impl::fullscreen() {
+ return m_fullscreen;
+}
+
+void MPGPlayObject_impl::fullscreen(bool value) {
+ m_fullscreen = value;
+}
+
+
+REGISTER_IMPLEMENTATION(MPGPlayObject_impl);
+
diff --git a/mpeglib_artsplug/mpgPlayObject_impl.h b/mpeglib_artsplug/mpgPlayObject_impl.h
new file mode 100644
index 00000000..02980a22
--- /dev/null
+++ b/mpeglib_artsplug/mpgPlayObject_impl.h
@@ -0,0 +1,48 @@
+/*
+ class for mpeg video decoder
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __MPGPLAYOBJECT_IMPL_H
+#define __MPGPLAYOBJECT_IMPL_H
+
+
+#include "decoderBaseObject_impl.h"
+#include "../mpeglib/lib/decoder/mpgPlugin.h"
+
+
+
+class MPGPlayObject_impl:
+ virtual public DecoderBaseObject_impl,
+ virtual public MPGPlayObject_skel {
+
+
+public:
+
+ MPGPlayObject_impl();
+ virtual ~MPGPlayObject_impl();
+
+
+ DecoderPlugin* createPlugin();
+
+ long int x11WindowId();
+ void fullscreen(bool value);
+ bool fullscreen();
+
+ void calculateBlock(unsigned long samples);
+
+private:
+ bool m_fullscreen;
+
+};
+
+
+#endif
diff --git a/mpeglib_artsplug/nullPlayObject_impl.cpp b/mpeglib_artsplug/nullPlayObject_impl.cpp
new file mode 100644
index 00000000..2e291ac2
--- /dev/null
+++ b/mpeglib_artsplug/nullPlayObject_impl.cpp
@@ -0,0 +1,120 @@
+/*
+ base class for all mpeglib decoders
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include "nullPlayObject_impl.h"
+#include "debug.h"
+
+NULLPlayObject_impl::NULLPlayObject_impl() {
+ _state=Arts::posIdle;
+
+}
+
+NULLPlayObject_impl::~NULLPlayObject_impl() {
+ arts_debug("~NULLPlayObject_impl -s");
+}
+
+
+bool NULLPlayObject_impl::loadMedia(const string &filename) {
+ arts_debug("NULLPlayObject_impl::loadMedia");
+ return true;
+}
+
+
+
+string NULLPlayObject_impl::description() {
+ arts_debug("description");
+ string back;
+ return back;
+}
+
+void NULLPlayObject_impl::description(const string &) {
+ arts_debug("description");
+ // what should do this?
+}
+
+Arts::poTime NULLPlayObject_impl::currentTime() {
+ poTime time;
+ return time;
+}
+
+
+
+poTime NULLPlayObject_impl::overallTime() {
+ poTime time;
+ return time;
+}
+
+poCapabilities NULLPlayObject_impl::capabilities() {
+ arts_debug("capabilities");
+ return Arts::capPause; /* no seek supported */
+}
+
+string NULLPlayObject_impl::mediaName() {
+ arts_debug("mediaName");
+ string back;
+ // whats a mediaName?
+ return back;
+}
+
+poState NULLPlayObject_impl::state() {
+ return _state;
+}
+
+void NULLPlayObject_impl::play() {
+ arts_debug("NULLPlayObject_impl::play");
+ _state = Arts::posPlaying;
+}
+
+void NULLPlayObject_impl::seek(const class poTime& seekTime) {
+ long sec=seekTime.seconds;
+
+ arts_debug("NULLPlayObject_impl::sec is %d:", sec);
+
+}
+
+void NULLPlayObject_impl::pause() {
+ arts_debug("NULLPlayObject_impl::pause");
+ _state = Arts::posPaused;
+}
+
+void NULLPlayObject_impl::halt() {
+ arts_debug("NULLPlayObject_impl::halt");
+ _state=Arts::posIdle;
+}
+
+
+void NULLPlayObject_impl::streamInit() {
+ arts_debug(" NULLPlayObject_impl::streamInit");
+}
+
+
+void NULLPlayObject_impl::streamStart() {
+ arts_debug(" NULLPlayObject_impl::streamStart");
+}
+
+void NULLPlayObject_impl::calculateBlock(unsigned long samples,
+ float* left , float* right) {
+
+
+ arts_debug(" NULLPlayObject_impl::calculateBlock");
+
+ unsigned int i;
+ for(i=0;i<samples;i++)
+ left[i] = right[i] = 0.0;
+}
+
+void NULLPlayObject_impl::streamEnd() {
+ arts_debug("NULLPlayObject_impl::streamEnd");
+}
+
+
+REGISTER_IMPLEMENTATION(NULLPlayObject_impl);
diff --git a/mpeglib_artsplug/nullPlayObject_impl.h b/mpeglib_artsplug/nullPlayObject_impl.h
new file mode 100644
index 00000000..c557b2f0
--- /dev/null
+++ b/mpeglib_artsplug/nullPlayObject_impl.h
@@ -0,0 +1,75 @@
+/*
+ this playobkect does nothing
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __NULLPLAYOBJECT_IMPL_H
+#define __NULLPLAYOBJECT_IMPL_H
+
+
+
+#include <math.h>
+#include "decoderBaseObject.h"
+#include "stdsynthmodule.h"
+#include "convert.h"
+#include <X11/Xlib.h>
+#include <audiosubsys.h>
+#include <iostream>
+#include <stdio.h>
+#include <string.h>
+
+
+using namespace std;
+using Arts::poState;
+using Arts::poTime;
+using Arts::poCapabilities;
+
+
+class NULLPlayObject_impl :
+ virtual public Arts::StdSynthModule,
+ virtual public NULLPlayObject_skel {
+
+ poState _state;
+
+public:
+
+ NULLPlayObject_impl();
+ virtual ~NULLPlayObject_impl();
+
+
+ bool loadMedia(const string &filename);
+ string description();
+ void description(const string &);
+
+ poTime currentTime();
+ poTime overallTime();
+
+ poCapabilities capabilities();
+ string mediaName();
+ poState state();
+ void play();
+ void halt();
+
+ void blocking(bool newvalue);
+ bool blocking();
+
+ void seek(const class poTime &);
+ void pause();
+ void streamInit();
+ void streamStart();
+ void calculateBlock(unsigned long samples,float* left,float* right);
+ void streamEnd();
+
+};
+
+
+
+#endif
diff --git a/mpeglib_artsplug/oggPlayObject_impl.cpp b/mpeglib_artsplug/oggPlayObject_impl.cpp
new file mode 100644
index 00000000..04b03a63
--- /dev/null
+++ b/mpeglib_artsplug/oggPlayObject_impl.cpp
@@ -0,0 +1,43 @@
+/*
+ class for ogg-mpeglib decoder
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#include "oggPlayObject_impl.h"
+#include "debug.h"
+
+
+
+
+OGGPlayObject_impl::OGGPlayObject_impl() {
+ arts_debug("OGGPlayObject_impl");
+}
+
+OGGPlayObject_impl::~OGGPlayObject_impl() {
+ arts_debug("~OGGPlayObject_impl");
+}
+
+
+DecoderPlugin* OGGPlayObject_impl::createPlugin() {
+ arts_debug("OGGPlayObject_impl::getPlugin");
+ return new VorbisPlugin();
+}
+
+void OGGPlayObject_impl::calculateBlock(unsigned long samples) {
+ DecoderBaseObject_impl::calculateBlock(samples,left,right);
+}
+
+
+
+REGISTER_IMPLEMENTATION(OGGPlayObject_impl);
+
diff --git a/mpeglib_artsplug/oggPlayObject_impl.h b/mpeglib_artsplug/oggPlayObject_impl.h
new file mode 100644
index 00000000..bae72a28
--- /dev/null
+++ b/mpeglib_artsplug/oggPlayObject_impl.h
@@ -0,0 +1,47 @@
+/*
+ class for ogg-mpeglib decoder
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __OGGPLAYOBJECT_IMPL_H
+#define __OGGPLAYOBJECT_IMPL_H
+
+
+#include "decoderBaseObject_impl.h"
+#include "../mpeglib/lib/decoder/vorbisPlugin.h"
+
+
+// for dynamic loading
+#include <dlfcn.h>
+
+
+class OGGPlayObject_impl:
+ virtual public DecoderBaseObject_impl,
+ virtual public OGGPlayObject_skel {
+
+
+
+public:
+
+ OGGPlayObject_impl();
+ virtual ~OGGPlayObject_impl();
+
+
+ DecoderPlugin* createPlugin();
+
+
+
+ void calculateBlock(unsigned long samples);
+
+};
+
+
+#endif
diff --git a/mpeglib_artsplug/splayPlayObject.idl b/mpeglib_artsplug/splayPlayObject.idl
new file mode 100644
index 00000000..51aa277d
--- /dev/null
+++ b/mpeglib_artsplug/splayPlayObject.idl
@@ -0,0 +1,15 @@
+#include "kmedia2.idl"
+#include "artsflow.idl"
+
+/* the interfaces below are not kept binary compatible */
+
+interface SplayPlayObject : Arts::StreamPlayObject, Arts::SynthModule
+{
+ async in byte stream indata;
+
+ out audio stream left,right;
+
+};
+
+
+
diff --git a/mpeglib_artsplug/splayPlayObject_impl.cpp b/mpeglib_artsplug/splayPlayObject_impl.cpp
new file mode 100644
index 00000000..d29aa61a
--- /dev/null
+++ b/mpeglib_artsplug/splayPlayObject_impl.cpp
@@ -0,0 +1,500 @@
+/*
+ base class for splay mp3 decoder
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include "splayPlayObject_impl.h"
+#include "../mpeglib/lib/splay/splayDecoder.h"
+#include "../mpeglib/lib/frame/audioFrameQueue.h"
+#include "../mpeglib/lib/splay/mpegAudioFrame.h"
+
+#include "debug.h"
+
+#define INPUT_SIZE 8192
+
+/**
+ Problems with streaming, which must be solved:
+ ==============================================
+
+
+ Deadlocks. I think we cannot have sync streams
+ for input/output.
+
+ This example is an OLD style PlayObject. (it uses sync streams)
+ The task is to convert this into an async PlayObject.
+
+ What does this PlayObject do:
+ -----------------------------
+
+ Current:
+ It reads data from a file and insert it
+ into a queue.
+
+ Future:
+ A "remote" sender of raw data, can test how much
+ bytes he can insert into the "framer".
+ If the framer produces a frame, we decode it,
+ and send an async dataPacket out.
+ (Then the frameQueue is not necessary anymore -IMHO)
+
+*/
+
+
+SplayPlayObject_impl::SplayPlayObject_impl() {
+
+ flpos=0.0;
+
+ splay=new SplayDecoder();
+ frameQueue= new AudioFrameQueue(10,MP3FRAMESIZE,_FRAME_AUDIO_FLOAT);
+ framer=new MpegAudioFrame();
+
+ arts_debug("outputStream created");
+
+ _state=Arts::posIdle;
+ file=NULL;
+ lStreaming=false;
+
+ resampleBuffer=NULL;
+ resampleBufferSize=0;
+ currentPacket=NULL;
+ currentPos=0;
+ // ok, we store packets here. Someday I should make this a template.
+ // use STL? maybe.
+ packetQueue=new FrameQueue(10);
+
+ // Note: The inputbuffer must NOT NOT NOT be on the stack!
+ inputbuffer=new unsigned char[INPUT_SIZE];
+
+}
+
+SplayPlayObject_impl::~SplayPlayObject_impl() {
+ arts_debug("~SplayPlayObject_impl -s");
+ delete splay;
+ delete frameQueue;
+ delete framer;
+ arts_debug("~SplayPlayObject_impl -e");
+
+ if (resampleBuffer != NULL) {
+ delete resampleBuffer;
+ }
+ //
+ // arts wants to free the packets and packetQueue too.
+ // so we dequeue all packets here, before deleting packetQueue
+ // otherwise we call delete on the packets twice.
+ //
+ while (packetQueue->getFillgrade() > 0) packetQueue->dequeue();
+ // now delete the (empty) queue
+
+ delete packetQueue;
+ delete [] inputbuffer;
+}
+
+
+bool SplayPlayObject_impl::loadMedia(const string &filename) {
+ arts_debug("loadMedia");
+
+ if (file != NULL) {
+ arts_fatal("~SplayPlayObject_impl already loaded");
+ }
+
+ lStreaming=false;
+
+ file=fopen(filename.c_str(),"r");
+ if (file == NULL) {
+ arts_debug("splay cannot open file");
+ return false;
+ }
+
+ flpos=0.0;
+
+ return true;
+}
+
+bool SplayPlayObject_impl::streamMedia(Arts::InputStream instream) {
+ arts_debug("streamMedia");
+ lStreaming=true;
+ currentStream = instream;
+ Arts::StreamPlayObject self = Arts::StreamPlayObject::_from_base(_copy());
+ connect(currentStream, "outdata", self);
+ return true;
+}
+
+void SplayPlayObject_impl::process_indata(Arts::DataPacket<Arts::mcopbyte> *inpacket) {
+ arts_debug("receiving packet");
+
+
+ packetQueue->enqueue((Frame*)inpacket);
+ if (packetQueue->getFillgrade() == 1) {
+ currentPos=0;
+ }
+
+ processQueue();
+}
+
+
+void SplayPlayObject_impl::processQueue() {
+
+
+
+ unsigned char* ptr;
+
+ if (packetQueue->getFillgrade() == 0) {
+ return;
+ }
+ Arts::DataPacket<Arts::mcopbyte>* currentPacket;
+
+ currentPacket=(Arts::DataPacket<Arts::mcopbyte>*) packetQueue->peekqueue(0);
+ int rest=currentPacket->size-currentPos;
+
+ int cnt=0;
+ while( (rest > 0) && (frameQueue->emptyQueueCanRead()) ) {
+
+ int state=framer->getState();
+ switch(state) {
+ case FRAME_NEED: {
+ int bytes=framer->canStore();
+ ptr=currentPacket->contents+currentPos;
+ // don't read beyond the packet.
+ if (bytes >= rest) {
+ bytes=rest;
+ // we must copy this, because we can commit a packet,
+ // which then deletes the pointer to the packet.
+ // but the framer still wants to access the data, later!
+ if (bytes > INPUT_SIZE) {
+ cout << "inputbuffer too small"<<endl;
+ exit(0);
+ }
+ memcpy(inputbuffer,ptr,bytes);
+ ptr=inputbuffer;
+ }
+ framer->store(ptr,bytes);
+ currentPos+=bytes;
+ rest-=bytes;
+ break;
+ }
+ case FRAME_WORK:
+ framer->work();
+ break;
+ case FRAME_HAS:{
+ AudioFrame* emptyFrame= frameQueue->emptyQueueDequeue();
+ if (splay->decode(framer->outdata(),framer->len(),emptyFrame)==true) {
+ frameQueue->dataQueueEnqueue(emptyFrame);
+ cnt++;
+ }
+ break;
+ }
+ default:
+ cout << "unknown state in mpeg audio framing"<<endl;
+ exit(0);
+ }
+ }
+ //cout << "rest-last:"<<rest<<" cnt:"<<cnt<<endl;
+
+ if (rest == 0) {
+ arts_debug("packet processed");
+ currentPacket->processed();
+ packetQueue->dequeue();
+ currentPos=0;
+ }
+
+}
+
+string SplayPlayObject_impl::description() {
+ arts_debug("description [GET1]");
+ string back;
+ return back;
+}
+
+void SplayPlayObject_impl::description(const string &) {
+ arts_debug("description [GET2]");
+}
+
+Arts::poTime SplayPlayObject_impl::currentTime() {
+ Arts::poTime time;
+ return time;
+}
+
+
+
+Arts::poTime SplayPlayObject_impl::overallTime() {
+ Arts::poTime time;
+ return time;
+}
+
+Arts::poCapabilities SplayPlayObject_impl::capabilities() {
+ arts_debug("capabilities");
+ return (Arts::poCapabilities)(Arts::capPause);
+}
+
+string SplayPlayObject_impl::mediaName() {
+ arts_debug("mediaName");
+ string back;
+ return back;
+}
+
+Arts::poState SplayPlayObject_impl::state() {
+ return _state;
+}
+
+void SplayPlayObject_impl::play() {
+ arts_debug("play:");
+ if (file == NULL) {
+ arts_debug("file is NULL:");
+ if (lStreaming && _state != Arts::posPlaying) {
+ currentStream.streamStart();
+ _state = Arts::posPlaying;
+ }
+
+ } else {
+ _state = Arts::posPlaying;
+ }
+}
+
+
+void SplayPlayObject_impl::seek(const class Arts::poTime& ) {
+ arts_debug("SEEK - RESET in decoder.");
+ framer->reset();
+ // and remove pre-decoded frame:
+ frameQueue->clear();
+
+ return;
+}
+
+
+void SplayPlayObject_impl::pause() {
+ arts_debug("pause");
+ _state=Arts::posPaused;
+}
+/*
+ *
+ * halt() (which the normal programmer would probably refer to as stop())
+ * should seek to the beginning and go into the posIdle state, like a just
+ * opened PlayObject
+ *
+ */
+
+void SplayPlayObject_impl::halt() {
+ arts_debug("halt");
+ _state=Arts::posIdle;
+}
+
+
+void SplayPlayObject_impl::streamInit() {
+ arts_debug("streamInit");
+ return;
+}
+
+
+void SplayPlayObject_impl::streamStart() {
+ arts_debug("streamStart");
+ return;
+}
+
+
+
+void SplayPlayObject_impl::calculateBlock(unsigned long wantSamples) {
+ unsigned long i;
+ unsigned long haveSamples=frameQueue->getLen();
+
+ // the wantSamples*2 takes care that there is enough data
+ // even if we have stereo.
+
+
+ if (haveSamples < wantSamples*2) {
+ if (lStreaming) {
+ for(i=0;i<wantSamples;i++) {
+ left[i] = right[i] = 0.0;
+ }
+ return;
+ }
+ // else (file access)
+ getMoreSamples(wantSamples*2);
+ }
+
+ // Yep, we have enough data
+
+
+
+ // now check if we need resampling.
+ AudioFrame* audioFrame=frameQueue->getCurrent();
+ double wav_samplingRate=(double)audioFrame->getFrequenceHZ();
+
+ /* difference between the sampling rates in percent */
+ float diff = fabs(wav_samplingRate-samplingRateFloat) / samplingRateFloat;
+ //cout << "wav_samplingRate:"<<wav_samplingRate<<endl;
+ /*
+ * efficient optimized case:
+ * 1. decoder -> float rendering
+ * 2. no resampling (i.e. artsd running @ 44100 Hz, playing an 44100 Hz mp3)
+ */
+
+ if(diff < 0.02) {
+ // no resample (easy+fast case)
+ haveSamples=frameQueue->copy(left,right,wantSamples);
+
+ for(i=haveSamples;i<wantSamples;i++) {
+ left[i] = right[i] = 0.0;
+ }
+ frameQueue->forwardStreamDouble(haveSamples);
+
+ if (lStreaming) {
+ processQueue();
+ }
+ } else {
+ /** FIXME: you can use this to implement pitchable playobject **/
+ double _speed = 1.0;
+
+ // calculate "how fast" we consume input samples (speed = 2.0 means,
+ // we need 2 input samples to generate one output sample)
+ double speed = (double)wav_samplingRate/(double)(samplingRateFloat/_speed);
+
+ // calculate how many samples we need to read from the frameQueue to
+ // satisfy the request
+ long readSamples = long((double)wantSamples*speed+8.0);
+ checkResampleBuffer(readSamples*2);
+
+ // read the samples
+ // - readSamplesOk contains how much we really got
+ // - we put the samples non-interleaved into the resampleBuffer,
+ // first the left channel, then the right channel
+ long readSamplesOk = frameQueue->copy(&resampleBuffer[0],
+ &resampleBuffer[readSamples],
+ readSamples);
+ // check how many output samples we will be able to generate from that
+ long samplesToConvert = long((double)readSamplesOk/speed)-4;
+ if(samplesToConvert < 0) samplesToConvert = 0;
+ if(samplesToConvert > wantSamples) samplesToConvert = wantSamples;
+
+ // do the conversion
+ Arts::interpolate_mono_float_float(samplesToConvert,flpos,speed,
+ &resampleBuffer[0],left);
+ Arts::interpolate_mono_float_float(samplesToConvert,flpos,speed,
+ &resampleBuffer[readSamples],right);
+
+ // calculate where we are now (as floating point position) in our
+ // inputsample buffer
+ flpos += (double)samplesToConvert * speed;
+
+ // Good - so how many input samples we won't need anymore (for the
+ // next request)? Skip them.
+ int skip = (int)floor(flpos);
+ if(skip)
+ {
+ frameQueue->forwardStreamDouble(skip);
+ flpos = flpos - floor(flpos);
+ }
+
+ for(i=samplesToConvert; i<wantSamples; i++) {
+ left[i] = right[i] = 0.0;
+ }
+
+ if (lStreaming) {
+ processQueue();
+ }
+ }
+
+ // ok, eof stop.
+ /*
+ if (haveSamples < wantSamples) {
+ halt();
+ }
+ */
+}
+
+void SplayPlayObject_impl::streamEnd() {
+ arts_debug("streamEnd");
+ if (file != NULL) {
+ fclose(file);
+ file=NULL;
+ }
+ return;
+}
+
+
+void SplayPlayObject_impl::checkResampleBuffer(int size) {
+
+
+ if (resampleBufferSize != size) {
+ if (resampleBuffer != NULL) {
+ delete resampleBuffer;
+ }
+ resampleBuffer = new float[size];
+ resampleBufferSize=size;
+ }
+
+}
+
+
+
+//
+// This method shows how we insert data in the framer,
+// decode a frame and store it in the queue.
+// This damned queue is necessary, becasue a sync out
+// stream needs a guranteed streamsize == samples.
+// an async stream would simply send a packet
+// and we are done.
+
+void SplayPlayObject_impl::getMoreSamples(int needLen) {
+
+
+ while( (feof(file) == false) && (frameQueue->getLen() < needLen) ) {
+
+ //
+ // This switch/case statement "partitions" the fileinput
+ // into mp3frames. We directly decode them to pcmFrames.
+ //
+
+ //
+ // There are three states. One is "I want Input, give me input"
+ // The other is "I have enough input, dont' bother me and let
+ // me do my work"
+ // The last ist "I have a frame here, take care of this, or
+ // I will continue with my work"
+
+ int state=framer->getState();
+ int cnt=0;
+ switch(state) {
+ case FRAME_NEED: {
+ int bytes=framer->canStore();
+ int read=fread(inputbuffer,1,bytes,file);
+ if (read != bytes) {
+ // read error. reset framer
+ framer->reset();
+ continue;
+ }
+ // Note: The inputbuffer must NOT NOT NOT be on the stack!
+ framer->store(inputbuffer,bytes);
+ break;
+ }
+ case FRAME_WORK:
+ framer->work();
+ break;
+ case FRAME_HAS:{
+ AudioFrame* emptyFrame= frameQueue->emptyQueueDequeue();
+ if (splay->decode(framer->outdata(),framer->len(),emptyFrame)==true) {
+ frameQueue->dataQueueEnqueue(emptyFrame);
+ cnt++;
+ }
+ break;
+ }
+ default:
+ cout << "unknown state in mpeg audio framing"<<endl;
+ exit(0);
+ }
+ }
+ if (feof(file)==true) {
+ halt();
+ return;
+ }
+}
+
+REGISTER_IMPLEMENTATION(SplayPlayObject_impl);
+
+// vim:ts=8:sw=2:sts=2
diff --git a/mpeglib_artsplug/splayPlayObject_impl.h b/mpeglib_artsplug/splayPlayObject_impl.h
new file mode 100644
index 00000000..fb0be931
--- /dev/null
+++ b/mpeglib_artsplug/splayPlayObject_impl.h
@@ -0,0 +1,112 @@
+/*
+ base class for all mpeglib decoders
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __DECODERBASEOBJECT_IMPL_H
+#define __DECODERBASEOBJECT_IMPL_H
+
+
+#include <math.h>
+#include "splayPlayObject.h"
+#include "stdsynthmodule.h"
+#include "convert.h"
+#include <X11/Xlib.h>
+#include <audiosubsys.h>
+#include <iostream>
+#include <stdio.h>
+#include <string.h>
+#include <arts/kmedia2.h>
+#include <stdlib.h>
+#include <connect.h>
+
+class SplayDecoder;
+class MpegAudioFrame;
+class AudioFrameQueue;
+class FrameQueue;
+
+
+
+using namespace std;
+
+class SplayPlayObject_impl :
+ virtual public Arts::StdSynthModule,
+ virtual public SplayPlayObject_skel {
+
+
+ Arts::poState _state;
+
+ // decoder
+ SplayDecoder* splay;
+ MpegAudioFrame* framer;
+ AudioFrameQueue* frameQueue;
+ FrameQueue* packetQueue;
+
+ // local input
+ FILE* file;
+
+ double flpos;
+ int lStreaming;
+
+ float* resampleBuffer;
+ int resampleBufferSize;
+
+ unsigned char* inputbuffer;
+ Arts::DataPacket<Arts::mcopbyte> *currentPacket;
+ int currentPos;
+
+
+public:
+
+ SplayPlayObject_impl();
+ virtual ~SplayPlayObject_impl();
+
+
+ bool loadMedia(const string &filename);
+
+ bool streamMedia(Arts::InputStream instream);
+ Arts::InputStream inputStream() { return currentStream; }
+ void process_indata(Arts::DataPacket<Arts::mcopbyte> *inpacket);
+
+ string description();
+ void description(const string &);
+
+ Arts::poTime currentTime();
+ Arts::poTime overallTime();
+
+ Arts::poCapabilities capabilities();
+ string mediaName();
+ Arts::poState state();
+ void play();
+ void halt();
+
+ void seek(const class Arts::poTime &);
+ void pause();
+ void streamInit();
+ void streamStart();
+ void calculateBlock(unsigned long samples);
+ void streamEnd();
+
+ private:
+ Arts::InputStream currentStream;
+
+ void checkResampleBuffer(int size);
+ void checkPacketBuffer(int size);
+ void getMoreSamples(int needLen);
+ void processQueue();
+
+
+
+};
+
+
+
+#endif
diff --git a/mpeglib_artsplug/vcdPlayObject_impl.cpp b/mpeglib_artsplug/vcdPlayObject_impl.cpp
new file mode 100644
index 00000000..4e7e4a40
--- /dev/null
+++ b/mpeglib_artsplug/vcdPlayObject_impl.cpp
@@ -0,0 +1,47 @@
+/*
+ class for mp3-mpeglib decoder
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include "vcdPlayObject_impl.h"
+#include "debug.h"
+#include <iostream>
+
+
+VCDPlayObject_impl::VCDPlayObject_impl() {
+ arts_debug("VCDPlayObject_impl");
+}
+
+VCDPlayObject_impl::~VCDPlayObject_impl() {
+ arts_debug("~VCDPlayObject_impl");
+}
+
+
+DecoderPlugin* VCDPlayObject_impl::createPlugin() {
+ std::cout << "VCDPlayObject_impl::getPlugin"<<std::endl;
+ return new MpgPlugin();
+}
+
+InputStream* VCDPlayObject_impl::createInputStream(const char* ) {
+ cout << "VCDPlayObject_impl::createInputStream"<<endl;
+ InputStream* back;
+ back=InputPlugin::createInputStream(__INPUT_CDI,_INPUT_THREADSAFE);
+ return back;
+}
+
+void VCDPlayObject_impl::calculateBlock(unsigned long samples) {
+ DecoderBaseObject_impl::calculateBlock(samples,left,right);
+}
+
+
+
+REGISTER_IMPLEMENTATION(VCDPlayObject_impl);
+
+
diff --git a/mpeglib_artsplug/vcdPlayObject_impl.h b/mpeglib_artsplug/vcdPlayObject_impl.h
new file mode 100644
index 00000000..fd871a17
--- /dev/null
+++ b/mpeglib_artsplug/vcdPlayObject_impl.h
@@ -0,0 +1,44 @@
+/*
+ class for mp3-mpeglib decoder
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __VCDPLAYOBJECT_IMPL_H
+#define __VCDPLAYOBJECT_IMPL_H
+
+
+#include "decoderBaseObject_impl.h"
+#include "../mpeglib/lib/decoder/mpgPlugin.h"
+
+
+
+class VCDPlayObject_impl:
+ virtual public DecoderBaseObject_impl,
+ virtual public VCDPlayObject_skel {
+
+
+
+public:
+
+ VCDPlayObject_impl();
+ virtual ~VCDPlayObject_impl();
+
+
+ DecoderPlugin* createPlugin();
+
+ InputStream* createInputStream(const char* url);
+
+ void calculateBlock(unsigned long samples);
+
+};
+
+
+#endif
diff --git a/mpeglib_artsplug/wavPlayObject_impl.cpp b/mpeglib_artsplug/wavPlayObject_impl.cpp
new file mode 100644
index 00000000..76d717dc
--- /dev/null
+++ b/mpeglib_artsplug/wavPlayObject_impl.cpp
@@ -0,0 +1,39 @@
+/*
+ class for mp3-mpeglib decoder
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include "wavPlayObject_impl.h"
+#include "debug.h"
+#include <iostream>
+
+
+WAVPlayObject_impl::WAVPlayObject_impl() {
+ arts_debug("WAVPlayObject_impl");
+}
+
+WAVPlayObject_impl::~WAVPlayObject_impl() {
+ arts_debug("~WAVPlayObject_impl");
+}
+
+
+DecoderPlugin* WAVPlayObject_impl::createPlugin() {
+ std::cout << "WAVPlayObject_impl::getPlugin"<<std::endl;
+ return new TplayPlugin();
+}
+
+void WAVPlayObject_impl::calculateBlock(unsigned long samples) {
+ DecoderBaseObject_impl::calculateBlock(samples,left,right);
+}
+
+
+
+REGISTER_IMPLEMENTATION(WAVPlayObject_impl);
+
diff --git a/mpeglib_artsplug/wavPlayObject_impl.h b/mpeglib_artsplug/wavPlayObject_impl.h
new file mode 100644
index 00000000..69601066
--- /dev/null
+++ b/mpeglib_artsplug/wavPlayObject_impl.h
@@ -0,0 +1,44 @@
+/*
+ class for wav-mpeglib decoder
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __WAVPLAYOBJECT_IMPL_H
+#define __WAVPLAYOBJECT_IMPL_H
+
+
+#include "decoderBaseObject_impl.h"
+#include "../mpeglib/lib/decoder/tplayPlugin.h"
+
+
+
+class WAVPlayObject_impl:
+ virtual public DecoderBaseObject_impl,
+ virtual public WAVPlayObject_skel {
+
+
+
+public:
+
+ WAVPlayObject_impl();
+ virtual ~WAVPlayObject_impl();
+
+
+ DecoderPlugin* createPlugin();
+
+
+
+ void calculateBlock(unsigned long samples);
+
+};
+
+
+#endif
diff --git a/mpg123_artsplugin/Makefile.am b/mpg123_artsplugin/Makefile.am
new file mode 100644
index 00000000..15cf345b
--- /dev/null
+++ b/mpg123_artsplugin/Makefile.am
@@ -0,0 +1,25 @@
+# $Id$
+
+INCLUDES= -I$(kde_includes)/arts $(all_includes)
+
+noinst_HEADERS = mpg123PlayObject_impl.h
+
+lib_LTLIBRARIES = libmpg123arts.la
+libmpg123arts_la_COMPILE_FIRST = mpg123arts.h
+libmpg123arts_la_SOURCES = mpg123arts.cc mpg123PlayObject_impl.cpp dxhead.c
+libmpg123arts_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+libmpg123arts_la_LIBADD = -lkmedia2_idl -lsoundserver_idl -lartsflow mpg123/libmpg123.la
+libmpg123arts_la_METASOURCES = AUTO
+
+mpg123arts.mcoptype: mpg123arts.h
+mpg123arts.mcopclass: mpg123arts.h
+mpg123arts.cc mpg123arts.h: $(srcdir)/mpg123arts.idl $(MCOPIDL)
+ $(MCOPIDL) -t -I$(kde_includes)/arts $(srcdir)/mpg123arts.idl
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = mpg123arts.mcoptype mpg123arts.mcopclass
+
+mcopclassdir = $(libdir)/mcop/Arts
+mcopclass_DATA = mpg123PlayObject.mcopclass
+
+SUBDIRS = mpg123 .
diff --git a/mpg123_artsplugin/compare_to_original.sh b/mpg123_artsplugin/compare_to_original.sh
new file mode 100755
index 00000000..b9787084
--- /dev/null
+++ b/mpg123_artsplugin/compare_to_original.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+diff \
+"--exclude-from=distexclude" -Bbdu1 ~/cvs/mpg123/mpg123 mpg123 &> ~/mpg.diff
diff --git a/mpg123_artsplugin/configure.in.in b/mpg123_artsplugin/configure.in.in
new file mode 100644
index 00000000..33da7fa9
--- /dev/null
+++ b/mpg123_artsplugin/configure.in.in
@@ -0,0 +1,92 @@
+dnl ============
+dnl Machine type
+dnl ============
+
+ARCH_CFLAGS="$CFLAGS -O2 -funroll-all-loops -finline-functions -ffast-math"
+
+case "$ARCH_TYPE" in
+ i486)
+ AC_DEFINE(ARCH_486, 1, [WE ARE BUILDING FOR A 486])
+ OPTIMIZED_ARCH="YES"
+ ;;
+ i586)
+ AC_DEFINE(ARCH_586, 1, [WE ARE BUILDING FOR A PENTIUM])
+ OPTIMIZED_ARCH="YES"
+ ;;
+ i686)
+ AC_DEFINE(ARCH_686, 1, [WE ARE BUILDING FOR A PPRO])
+ OPTIMIZED_ARCH="YES"
+ #EXTRA_CPU_CFLAGS=$(if $CC -march=i686 -S -o /dev/null -xc /dev/null > /dev/null; then echo "-march=i686"; fi)
+ ;;
+ ppc)
+ AC_DEFINE(ARCH_PPC, 1, [WE ARE BUILDING FOR A POWERPC])
+ OPTIMIZED_ARCH="YES"
+ ;;
+ sun4u)
+ AC_DEFINE(ARCH_ULTRA, 1, [WE ARE BUILDING FOR A SUN ULTRASPARC])
+ # Is there mpg123 optimization for UltraSparc?
+ ;;
+esac
+
+# ARCH_X86
+if test "$ARCH_TYPE" = "i486" || test "$ARCH_TYPE" = "i586" || test "$ARCH_TYPE" = "i686"; then
+ MPG123_PLAT_LIB="$MPG123_PLAT_LIB libmpx86.la"
+fi
+# ARCH_486
+if test "$ARCH_TYPE" = "i486"; then
+ MPG123_PLAT_LIB="$MPG123_PLAT_LIB libmp486.la"
+ ARCH_CFLAGS="$ARCH_CFLAGS -DROT_I386 -DI386_ASSEM -DI486_OPT -DLINUX -DREAL_IS_FLOAT"
+fi
+# ARCH_586
+if test "$ARCH_TYPE" = "i586" -a "$MMX_SUPPORT" = "no"; then
+ MPG123_PLAT_LIB="$MPG123_PLAT_LIB libmp586.la"
+ ARCH_CFLAGS="$ARCH_CFLAGS -DROT_I386 -DI386_ASSEM -DPENTIUM_OPT -DLINUX -DREAL_IS_FLOAT"
+fi
+# ARCH_586_MMX
+if test "$ARCH_TYPE" = "i586" -a "$MMX_SUPPORT" = "yes"; then
+ ARCH_CFLAGS="$ARCH_CFLAGS -DROT_I386 -DI386_ASSEM -DPENTIUM_OPT -DLINUX -DREAL_IS_FLOAT"
+ MPG123_PLAT_LIB=libmp586mmx.la
+fi
+# ARCH_686
+if test "$ARCH_TYPE" = "i686" -a "$MMX_SUPPORT" = "no"; then
+ ARCH_CFLAGS="$ARCH_CFLAGS -DROT_I386 -DI386_ASSEM -DPENTIUM_OPT -DLINUX -DREAL_IS_FLOAT -march=pentiumpro"
+ MPG123_PLAT_LIB=libmp686.la
+fi
+# ARCH_686_MMX
+if test "$ARCH_TYPE" = "i686" -a "$MMX_SUPPORT" = "yes"; then
+ ARCH_CFLAGS="$ARCH_CFLAGS -DROT_I386 -DI386_ASSEM -DPENTIUM_OPT -DUSE_MMX -DLINUX -DREAL_IS_FLOAT -march=pentiumpro"
+ MPG123_PLAT_LIB=libmp686mmx.la
+fi
+# ARCH_PPC
+if test "$ARCH_TYPE" = "ppc"; then
+ MPG123_PLAT_LIB=libmpppc.la
+fi
+# ARCH_ULTRA
+if test "$ARCH_TYPE" = "sun4u"; then
+ :
+ # nothing for now
+fi
+# ARCH_PLAIN
+if test -z "$OPTIMIZED_ARCH"; then
+ MPG123_PLAT_LIB=libmpplain.la
+fi
+
+AC_SUBST(ARCH_CFLAGS)
+AC_SUBST(MPG123_PLAT_LIB)
+
+if test "$kde_mpeglib_compiles" = "yes"
+then
+DO_NOT_COMPILE="$DO_NOT_COMPILE mpg123_artsplugin"
+fi
+
+# this is run after libtool configure parts, which set AS to as
+# which is the wrong one for us. E.g. libtool doesn't recognize that as
+# tag. So we unset it here, so the below macro can set it.
+save_AS=$AS
+unset AS
+ifdef([AM_PROG_AS],[AM_PROG_AS],[])
+#if the macro did set something usefull fallback.
+test -z "$AS" && AS=$save_AS
+#and if AS was set to as by the macro, and we have a saved value,
+#it's likely it contains a better guess (or it's also as)
+test "x$AS" = xas && test -n "$save_AS" && AS=$save_AS
diff --git a/mpg123_artsplugin/distexclude b/mpg123_artsplugin/distexclude
new file mode 100644
index 00000000..9e9f6792
--- /dev/null
+++ b/mpg123_artsplugin/distexclude
@@ -0,0 +1,24 @@
+audio_*.c
+README*
+control_*.[ch]
+precompiled
+mpglib
+jukebox
+mpg123.[1c]
+Makefile*
+BENCHMARKING
+BUGS
+CHANGES
+COPYING
+CVS
+INSTALL
+JUKEBOX
+TODO
+tools
+playlist.[ch]
+wav.c
+version.h
+term.[ch]
+getlopt.[ch]
+.cvsignore
+equaliz*
diff --git a/mpg123_artsplugin/dxhead.c b/mpg123_artsplugin/dxhead.c
new file mode 100644
index 00000000..fb056055
--- /dev/null
+++ b/mpg123_artsplugin/dxhead.c
@@ -0,0 +1,250 @@
+/*---- DXhead.c --------------------------------------------
+
+
+decoder MPEG Layer III
+
+handle Xing header
+
+mod 12/7/98 add vbr scale
+
+Copyright 1998 Xing Technology Corp.
+-----------------------------------------------------------*/
+#include <stdlib.h>
+#include <unistd.h>
+#include <float.h>
+#include <math.h>
+#include "mpg123/mpg123.h"
+#include "dxhead.h"
+
+/* 4 Xing
+ * 4 flags
+ * 4 frames
+ * 4 bytes
+ * 100 toc
+ */
+
+/*-------------------------------------------------------------*/
+static int ExtractI4(unsigned char *buf)
+{
+
+ int x;
+
+/* big endian extract */
+
+ x = buf[0];
+
+ x <<= 8;
+
+ x |= buf[1];
+
+ x <<= 8;
+
+ x |= buf[2];
+
+ x <<= 8;
+
+ x |= buf[3];
+
+
+ return x;
+
+}
+
+/*-------------------------------------------------------------*/
+int mpg123_get_xing_header(XHEADDATA * X, unsigned char *buf)
+{
+
+ int i, head_flags;
+
+ int h_id, h_mode, h_sr_index;
+
+ static int sr_table[4] =
+ {44100, 48000, 32000, 99999};
+
+
+/* get Xing header data */
+
+
+ X->flags = 0; /* clear to null incase fail */
+ X->toc = NULL;
+
+
+/* get selected MPEG header data */
+ h_id = (buf[1] >> 3) & 1;
+
+ h_sr_index = (buf[2] >> 2) & 3;
+
+ h_mode = (buf[3] >> 6) & 3;
+
+
+
+/* determine offset of header */
+ if (h_id)
+ { /* mpeg1 */
+
+ if (h_mode != 3) {
+ buf += (32 + 4);
+
+}
+
+ else
+ buf += (17 + 4);
+
+ }
+
+ else
+ { /* mpeg2 */
+
+ if (h_mode != 3)
+ buf += (17 + 4);
+
+ else
+ buf += (9 + 4);
+
+ }
+
+
+ if (buf[0] != 'X')
+ return 0; /* fail */
+
+ if (buf[1] != 'i')
+ return 0; /* header not found */
+
+ if (buf[2] != 'n')
+ return 0;
+
+ if (buf[3] != 'g')
+ return 0;
+
+ buf += 4;
+
+
+ X->h_id = h_id;
+
+ X->samprate = sr_table[h_sr_index];
+
+ if (h_id == 0)
+ X->samprate >>= 1;
+
+
+ head_flags = X->flags = ExtractI4(buf);
+ buf += 4; /* get flags */
+
+
+ if (head_flags & FRAMES_FLAG)
+ {
+ X->frames = ExtractI4(buf);
+ buf += 4;
+ }
+
+ if (head_flags & BYTES_FLAG)
+ {
+ X->bytes = ExtractI4(buf);
+ buf += 4;
+ }
+
+
+ if (head_flags & TOC_FLAG)
+ {
+
+ X->toc = malloc(100);
+ if (X->toc != NULL)
+ {
+
+ for (i = 0; i < 100; i++)
+ X->toc[i] = buf[i];
+
+ }
+
+ buf += 100;
+
+ }
+
+
+ X->vbr_scale = -1;
+
+ if (head_flags & VBR_SCALE_FLAG)
+ {
+ X->vbr_scale = ExtractI4(buf);
+ buf += 4;
+ }
+
+
+/*if( X->toc != NULL ) {
+ *for(i=0;i<100;i++) {
+ * if( (i%10) == 0 ) printf("\n");
+ * printf(" %3d", (int)(X->toc[i]));
+ *}
+ *}
+ */
+
+ return 1; /* success */
+
+}
+
+/*-------------------------------------------------------------*/
+int mpg123_seek_point(unsigned char TOC[100], int file_bytes, float percent)
+{
+
+/* interpolate in TOC to get file seek point in bytes */
+ int a, seekpoint;
+
+ float fa, fb, fx;
+
+
+
+ if (percent < 0.0f)
+ percent = 0.0f;
+
+ if (percent > 100.0f)
+ percent = 100.0f;
+
+
+ a = (int) percent;
+
+ if (a > 99)
+ a = 99;
+
+ fa = TOC[a];
+
+ if (a < 99)
+ {
+
+ fb = TOC[a + 1];
+
+ }
+
+ else
+ {
+
+ fb = 256.0f;
+
+ }
+
+
+
+ fx = fa + (fb - fa) * (percent - a);
+
+
+ seekpoint = (int) ((1.0f / 256.0f) * fx * file_bytes);
+
+
+
+ return seekpoint;
+
+}
+
+/*-------------------------------------------------------------*/
+int mpg123_stream_check_for_xing_header(struct frame *fr, XHEADDATA * xhead)
+{
+ unsigned char *head_data;
+ int ret;
+
+ lseek(rd->filept, -(fr->framesize + 4), SEEK_CUR);
+ head_data = malloc(fr->framesize + 4);
+ read(rd->filept,head_data, fr->framesize +4); /* now read the rest */
+ ret = mpg123_get_xing_header(xhead, head_data);
+ free(head_data);
+ return ret;
+}
+
diff --git a/mpg123_artsplugin/dxhead.h b/mpg123_artsplugin/dxhead.h
new file mode 100644
index 00000000..16a6a0fc
--- /dev/null
+++ b/mpg123_artsplugin/dxhead.h
@@ -0,0 +1,77 @@
+/*---- DXhead.h --------------------------------------------
+
+
+decoder MPEG Layer III
+
+handle Xing header
+
+
+Copyright 1998 Xing Technology Corp.
+-----------------------------------------------------------*/
+/* A Xing header may be present in the ancillary
+ * data field of the first frame of an mp3 bitstream
+ * The Xing header (optionally) contains
+ * frames total number of audio frames in the bitstream
+ * bytes total number of bytes in the bitstream
+ * toc table of contents
+
+ * toc (table of contents) gives seek points
+ * for random access
+ * the ith entry determines the seek point for
+ * i-percent duration
+ * seek point in bytes = (toc[i]/256.0) * total_bitstream_bytes
+ * e.g. half duration seek point = (toc[50]/256.0) * total_bitstream_bytes
+ */
+
+#define FRAMES_FLAG 0x0001
+#define BYTES_FLAG 0x0002
+#define TOC_FLAG 0x0004
+#define VBR_SCALE_FLAG 0x0008
+
+#define FRAMES_AND_BYTES (FRAMES_FLAG | BYTES_FLAG)
+
+/* structure to receive extracted header
+ * toc may be NULL
+ */
+typedef struct
+{
+
+ int h_id; /* from MPEG header, 0=MPEG2, 1=MPEG1 */
+
+ int samprate; /* determined from MPEG header */
+
+ int flags; /* from Xing header data */
+
+ int frames; /* total bit stream frames from Xing header data */
+
+ int bytes; /* total bit stream bytes from Xing header data */
+
+ int vbr_scale; /* encoded vbr scale from Xing header data */
+
+ unsigned char *toc; /* pointer to unsigned char toc_buffer[100] */
+
+ /* may be NULL if toc not desired */
+}
+XHEADDATA;
+
+
+
+int mpg123_get_xing_header(XHEADDATA * X, unsigned char *buf);
+
+/* return 0=fail, 1=success
+ * X structure to receive header data (output)
+ * buf bitstream input
+ */
+
+int mpg123_seek_point(unsigned char TOC[100], int file_bytes, float percent);
+
+/* return seekpoint in bytes (may be at eof if percent=100.0)
+ * TOC = table of contents from Xing header
+ * file_bytes = number of bytes in mp3 file
+ * percent = play time percentage of total playtime. May be
+ * fractional (e.g. 87.245)
+ */
+int mpg123_stream_check_for_xing_header(struct frame *fr, XHEADDATA * xhead);
+
+
+
diff --git a/mpg123_artsplugin/mpg123/Makefile.am b/mpg123_artsplugin/mpg123/Makefile.am
new file mode 100644
index 00000000..2243efc2
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/Makefile.am
@@ -0,0 +1,34 @@
+# $Id$
+
+EXTRA_DIST = README decode_i386.c decode_i586.s dct64_i386.c decode.c \
+ dct64.c audio.h
+
+# -I$(ROOT_DIR)/app
+INCLUDES = -I$(top_srcdir)/include
+COMMON_CFLAGS = -D_REENTRANT -DNOXFERMEM -DNO_EQUALIZER \
+ -DNO_DECODE_AUDIO -DNO_DECODE_FILE -DNO_DECODE_WAV
+
+libmpg123_la_SOURCES = common.c decode_2to1.c decode_4to1.c decode_ntom.c \
+ layer1.c layer2.c layer3.c readers.c \
+ httpget.c getbits.c tabinit.c xfermem.c vbrhead.c buffer.c audio.c
+
+noinst_HEADERS = huffman.h mpg123.h l2tables.h audio.h common.h \
+ genre.h getbits.h xfermem.h buffer.h
+
+EXTRA_LTLIBRARIES = libmp486.la libmp586.la libmp586mmx.la libmpppc.la libmp686.la libmp686mmx.la libmpplain.la
+libmp486_la_SOURCES = decode_i386.c dct64_i386.c decode_i486.c dct64_i486.c
+libmp586_la_SOURCES = decode_i386.c dct64_i386.c decode_i586.s
+libmp586mmx_la_SOURCES = decode_i386.c dct64_MMX.s decode_MMX.s tabinit_MMX.s
+libmp686_la_SOURCES = decode_i386.c dct64_i386.c decode_i586.s
+libmp686mmx_la_SOURCES = decode_i386.c dct64_MMX.s decode_MMX.s tabinit_MMX.s
+libmpppc_la_SOURCES = decode.c dct64.c
+libmpplain_la_SOURCES = decode.c dct64.c
+
+AM_CFLAGS = $(COMMON_CFLAGS) $(ARCH_CFLAGS)
+
+lib_LTLIBRARIES = libmpg123.la
+libmpg123_la_LDFLAGS = -module -avoid-version
+libmpg123_la_LIBADD = ./$(MPG123_PLAT_LIB)
+libmpg123_la_DEPENDENCIES = ./$(MPG123_PLAT_LIB)
+
+
diff --git a/mpg123_artsplugin/mpg123/README b/mpg123_artsplugin/mpg123/README
new file mode 100644
index 00000000..383b694b
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/README
@@ -0,0 +1,42 @@
+$Id$
+
+Keeping in mind this is a slightly modified version of mpg123 (available
+at http://www.mpg123.de/), and somewhat trimmed down to not include any of
+the front ends, I present you with this:
+
+Maybe I change the policy to GPL in the future ...
+But at the moment this is the policy:
+
+Copyright (c) 1995-99 by Michael Hipp, all rights reserved.
+Parts of the software are contributed by other people, please
+refer to the README file for details!
+
+DISTRIBUTION:
+This software may be distributed freely, provided that it is
+distributed in its entirety, without modifications, and with
+the original copyright notice and license included. You may
+distribute your own seperate patches together with this software
+package or a modified package if you always include a pointer
+where to get the original unmodified package. In this case
+you must inform the author about the modified package.
+The software may not be sold for profit or as "hidden" part of
+another software, but it may be included with collections
+of other software, such as CD-ROM images of FTP servers and
+similar, provided that this software is not a significant part
+of that collection.
+Precompiled binaries of this software may be distributed in the
+same way, provided that this copyright notice and license is
+included without modification.
+
+USAGE:
+This software may be used freely, provided that the original
+author is always credited. If you intend to use this software
+as a significant part of business (for-profit) activities, you
+have to contact the author first. Also, any usage that is not
+covered by this license requires the explicit permission of
+the author.
+
+DISCLAIMER:
+This software is provided as-is. The author can not be held
+liable for any damage that might arise from the use of this
+software. Use it at your own risk.
diff --git a/mpg123_artsplugin/mpg123/audio.c b/mpg123_artsplugin/mpg123/audio.c
new file mode 100644
index 00000000..b37c6659
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/audio.c
@@ -0,0 +1,298 @@
+
+#include "mpg123.h"
+
+void audio_info_struct_init(struct audio_info_struct *ai)
+{
+#ifdef AUDIO_USES_FD
+ ai->fn = -1;
+#endif
+#ifdef SGI
+#if 0
+ ALconfig config;
+ ALport port;
+#endif
+#endif
+ ai->rate = -1;
+ ai->gain = -1;
+ ai->output = -1;
+#ifdef ALSA
+ ai->handle = NULL;
+ ai->alsa_format.format = -1;
+ ai->alsa_format.rate = -1;
+ ai->alsa_format.channels = -1;
+#endif
+ ai->device = NULL;
+ ai->channels = -1;
+ ai->format = -1;
+}
+
+#define NUM_CHANNELS 2
+#define NUM_ENCODINGS 6
+#define NUM_RATES 10
+
+struct audio_name audio_val2name[NUM_ENCODINGS+1] = {
+ { AUDIO_FORMAT_SIGNED_16 , "signed 16 bit" , "s16 " } ,
+ { AUDIO_FORMAT_UNSIGNED_16, "unsigned 16 bit" , "u16 " } ,
+ { AUDIO_FORMAT_UNSIGNED_8 , "unsigned 8 bit" , "u8 " } ,
+ { AUDIO_FORMAT_SIGNED_8 , "signed 8 bit" , "s8 " } ,
+ { AUDIO_FORMAT_ULAW_8 , "mu-law (8 bit)" , "ulaw " } ,
+ { AUDIO_FORMAT_ALAW_8 , "a-law (8 bit)" , "alaw " } ,
+ { -1 , NULL }
+};
+
+#if 0
+static char *channel_name[NUM_CHANNELS] =
+ { "mono" , "stereo" };
+#endif
+
+static int channels[NUM_CHANNELS] = { 1 , 2 };
+static int rates[NUM_RATES] = {
+ 8000, 11025, 12000,
+ 16000, 22050, 24000,
+ 32000, 44100, 48000,
+ 8000 /* 8000 = dummy for user forced */
+
+};
+static int encodings[NUM_ENCODINGS] = {
+ AUDIO_FORMAT_SIGNED_16,
+ AUDIO_FORMAT_UNSIGNED_16,
+ AUDIO_FORMAT_UNSIGNED_8,
+ AUDIO_FORMAT_SIGNED_8,
+ AUDIO_FORMAT_ULAW_8,
+ AUDIO_FORMAT_ALAW_8
+};
+
+static char capabilities[NUM_CHANNELS][NUM_ENCODINGS][NUM_RATES];
+
+void audio_capabilities(struct audio_info_struct *ai)
+{
+ int fmts;
+ int i,j,k,k1=NUM_RATES-1;
+ struct audio_info_struct ai1 = *ai;
+
+ if (param.outmode != DECODE_AUDIO) {
+ memset(capabilities,1,sizeof(capabilities));
+ return;
+ }
+
+ memset(capabilities,0,sizeof(capabilities));
+ if(param.force_rate) {
+ rates[NUM_RATES-1] = param.force_rate;
+ k1 = NUM_RATES;
+ }
+
+#ifndef NO_DECODE_AUDIO
+ if(audio_open(&ai1) < 0) {
+ perror("audio");
+ exit(1);
+ }
+#endif
+
+ for(i=0;i<NUM_CHANNELS;i++) {
+ for(j=0;j<NUM_RATES;j++) {
+ ai1.channels = channels[i];
+ ai1.rate = rates[j];
+ fmts = audio_get_formats(&ai1);
+ if(fmts < 0)
+ continue;
+ for(k=0;k<NUM_ENCODINGS;k++) {
+ if((fmts & encodings[k]) == encodings[k])
+ capabilities[i][k][j] = 1;
+ }
+ }
+ }
+
+#ifndef NO_DECODE_AUDIO
+ audio_close(&ai1);
+#endif
+
+ if(param.verbose > 1) {
+ fprintf(stderr,"\nAudio capabilities:\n |");
+ for(j=0;j<NUM_ENCODINGS;j++) {
+ fprintf(stderr," %5s |",audio_val2name[j].sname);
+ }
+ fprintf(stderr,"\n --------------------------------------------------------\n");
+ for(k=0;k<k1;k++) {
+ fprintf(stderr," %5d |",rates[k]);
+ for(j=0;j<NUM_ENCODINGS;j++) {
+ if(capabilities[0][j][k]) {
+ if(capabilities[1][j][k])
+ fprintf(stderr," M/S |");
+ else
+ fprintf(stderr," M |");
+ }
+ else if(capabilities[1][j][k])
+ fprintf(stderr," S |");
+ else
+ fprintf(stderr," |");
+ }
+ fprintf(stderr,"\n");
+ }
+ fprintf(stderr,"\n");
+ }
+}
+
+static int rate2num(int r)
+{
+ int i;
+ for(i=0;i<NUM_RATES;i++)
+ if(rates[i] == r)
+ return i;
+ return -1;
+}
+
+
+static int audio_fit_cap_helper(struct audio_info_struct *ai,int rn,int f0,int f2,int c)
+{
+ int i;
+
+ if(rn >= 0) {
+ for(i=f0;i<f2;i++) {
+ if(capabilities[c][i][rn]) {
+ ai->rate = rates[rn];
+ ai->format = encodings[i];
+ ai->channels = channels[c];
+ return 1;
+ }
+ }
+ }
+ return 0;
+
+}
+
+/*
+ * c=num of channels of stream
+ * r=rate of stream
+ */
+void audio_fit_capabilities(struct audio_info_struct *ai,int c,int r)
+{
+ int rn;
+ int f0=0;
+ int save_channels = c;
+ int save_rate = r;
+
+ if(param.force_8bit) {
+ f0 = 2;
+ }
+
+ c--; /* stereo=1 ,mono=0 */
+
+ if(param.force_mono >= 0)
+ c = 0;
+ if(param.force_stereo)
+ c = 1;
+
+ if(param.force_rate) {
+ rn = rate2num(param.force_rate);
+ if(audio_fit_cap_helper(ai,rn,f0,2,c))
+ return;
+ if(audio_fit_cap_helper(ai,rn,2,NUM_ENCODINGS,c))
+ return;
+
+ if(c == 1 && !param.force_stereo)
+ c = 0;
+ else if(c == 0 && !param.force_mono)
+ c = 1;
+
+ if(audio_fit_cap_helper(ai,rn,f0,2,c))
+ return;
+ if(audio_fit_cap_helper(ai,rn,2,NUM_ENCODINGS,c))
+ return;
+ }
+ else {
+
+ rn = rate2num(r>>0);
+ if(audio_fit_cap_helper(ai,rn,f0,2,c))
+ return;
+ rn = rate2num(r<<1);
+ if(audio_fit_cap_helper(ai,rn,f0,2,c))
+ return;
+ rn = rate2num(r<<2);
+ if(audio_fit_cap_helper(ai,rn,f0,2,c))
+ return;
+ rn = rate2num(r>>1);
+ if(audio_fit_cap_helper(ai,rn,f0,2,c))
+ return;
+ rn = rate2num(r>>2);
+ if(audio_fit_cap_helper(ai,rn,f0,2,c))
+ return;
+
+ rn = rate2num(r>>0);
+ if(audio_fit_cap_helper(ai,rn,2,NUM_ENCODINGS,c))
+ return;
+ rn = rate2num(r<<1);
+ if(audio_fit_cap_helper(ai,rn,2,NUM_ENCODINGS,c))
+ return;
+ rn = rate2num(r<<2);
+ if(audio_fit_cap_helper(ai,rn,2,NUM_ENCODINGS,c))
+ return;
+ rn = rate2num(r>>1);
+ if(audio_fit_cap_helper(ai,rn,2,NUM_ENCODINGS,c))
+ return;
+ rn = rate2num(r>>2);
+ if(audio_fit_cap_helper(ai,rn,2,NUM_ENCODINGS,c))
+ return;
+
+
+ if(c == 1 && !param.force_stereo)
+ c = 0;
+ else if(c == 0 && !param.force_mono)
+ c = 1;
+
+ rn = rate2num(r>>0);
+ if(audio_fit_cap_helper(ai,rn,f0,2,c))
+ return;
+ rn = rate2num(r<<1);
+ if(audio_fit_cap_helper(ai,rn,f0,2,c))
+ return;
+ rn = rate2num(r<<2);
+ if(audio_fit_cap_helper(ai,rn,f0,2,c))
+ return;
+ rn = rate2num(r>>1);
+ if(audio_fit_cap_helper(ai,rn,f0,2,c))
+ return;
+ rn = rate2num(r>>2);
+ if(audio_fit_cap_helper(ai,rn,f0,2,c))
+ return;
+
+ rn = rate2num(r>>0);
+ if(audio_fit_cap_helper(ai,rn,2,NUM_ENCODINGS,c))
+ return;
+ rn = rate2num(r<<1);
+ if(audio_fit_cap_helper(ai,rn,2,NUM_ENCODINGS,c))
+ return;
+ rn = rate2num(r<<2);
+ if(audio_fit_cap_helper(ai,rn,2,NUM_ENCODINGS,c))
+ return;
+ rn = rate2num(r>>1);
+ if(audio_fit_cap_helper(ai,rn,2,NUM_ENCODINGS,c))
+ return;
+ rn = rate2num(r>>2);
+ if(audio_fit_cap_helper(ai,rn,2,NUM_ENCODINGS,c))
+ return;
+ }
+
+ fprintf(stderr,"\nAudiodevice: No supported audio rate found for %d Hz and %d channels !\n",save_rate,save_channels);
+ fprintf(stderr,"Use '-vv' to list all possible audio rates and\n");
+ fprintf(stderr,"choose a supported rate with the '-r <rate>' option.\n");
+
+ exit(1);
+}
+
+char *audio_encoding_name(int format)
+{
+ int i;
+
+ for(i=0;i<NUM_ENCODINGS;i++) {
+ if(audio_val2name[i].val == format)
+ return audio_val2name[i].name;
+ }
+ return "Unknown";
+}
+
+#if !defined(SOLARIS) && !defined(__NetBSD__) && !defined(AIX_UMS) || defined(NAS)
+void audio_queueflush(struct audio_info_struct *ai)
+{
+}
+#endif
+
diff --git a/mpg123_artsplugin/mpg123/audio.h b/mpg123_artsplugin/mpg123/audio.h
new file mode 100644
index 00000000..78a6e0b3
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/audio.h
@@ -0,0 +1,106 @@
+/*
+ * Audio 'LIB' defines
+ */
+
+#define AUDIO_OUT_HEADPHONES 0x01
+#define AUDIO_OUT_INTERNAL_SPEAKER 0x02
+#define AUDIO_OUT_LINE_OUT 0x04
+
+enum { DECODE_TEST, DECODE_AUDIO, DECODE_FILE, DECODE_BUFFER, DECODE_WAV,
+ DECODE_AU,DECODE_CDR,DECODE_AUDIOFILE };
+
+#define AUDIO_FORMAT_MASK 0x100
+#define AUDIO_FORMAT_16 0x100
+#define AUDIO_FORMAT_8 0x000
+
+#define AUDIO_FORMAT_SIGNED_16 0x110
+#define AUDIO_FORMAT_UNSIGNED_16 0x120
+#define AUDIO_FORMAT_UNSIGNED_8 0x1
+#define AUDIO_FORMAT_SIGNED_8 0x2
+#define AUDIO_FORMAT_ULAW_8 0x4
+#define AUDIO_FORMAT_ALAW_8 0x8
+
+/* 3% rate tolerance */
+#define AUDIO_RATE_TOLERANCE 3
+
+#if 0
+#if defined(HPUX) || defined(SUNOS) || defined(SOLARIS) || defined(OSS) || defined(__NetBSD__) || defined(SPARCLINUX) || defined(__FreeBSD__)
+#endif
+#endif
+
+#ifndef AIX_UMS
+#define AUDIO_USES_FD
+#endif
+#ifdef AIX_UMS
+#include <UMSAudioDevice.h>
+#endif
+
+#ifdef SGI
+/* #include <audio.h> */
+#include <dmedia/audio.h>
+#endif
+
+
+#ifdef HAVE_ALSA_ASOUNDLIB_H
+#include <alsa/asoundlib.h>
+#elif defined(HAVE_SYS_ASOUNDLIB_H)
+#include <sys/asoundlib.h>
+#endif
+
+struct audio_info_struct
+{
+#ifdef AUDIO_USES_FD
+ int fn; /* filenumber */
+#endif
+#ifdef SGI
+ ALconfig config;
+ ALport port;
+#endif
+ long rate;
+ long gain;
+ int output;
+#ifdef ALSA
+ snd_pcm_t *handle;
+ snd_pcm_format_t alsa_format;
+#endif
+#ifdef AIX_UMS
+ UMSAudioDevice dev;
+ UMSAudioDeviceMClass class;
+ Environment *ev;
+ UMSAudioDeviceMClass_ErrorCode err;
+ char *errstr;
+ char *fmtal;
+ char *inp;
+ char *out;
+#endif
+ char *device;
+ int channels;
+ int format;
+ int private1;
+ void *private2;
+};
+
+struct audio_name {
+ int val;
+ char *name;
+ char *sname;
+};
+
+extern void audio_capabilities(struct audio_info_struct *);
+extern void audio_fit_capabilities(struct audio_info_struct *ai,int c,int r);
+
+extern char *audio_encoding_name(int format);
+
+extern int audio_play_samples(struct audio_info_struct *,unsigned char *,int);
+extern int audio_open(struct audio_info_struct *);
+extern int audio_reset_parameters(struct audio_info_struct *);
+extern int audio_rate_best_match(struct audio_info_struct *ai);
+extern int audio_set_rate(struct audio_info_struct *);
+extern int audio_set_format(struct audio_info_struct *);
+extern int audio_get_formats(struct audio_info_struct *);
+extern int audio_set_channels(struct audio_info_struct *);
+extern int audio_write_sample(struct audio_info_struct *,short *,int);
+extern int audio_close(struct audio_info_struct *);
+extern void audio_info_struct_init(struct audio_info_struct *);
+extern void audio_queueflush(struct audio_info_struct *ai);
+
diff --git a/mpg123_artsplugin/mpg123/buffer.c b/mpg123_artsplugin/mpg123/buffer.c
new file mode 100644
index 00000000..3c85b654
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/buffer.c
@@ -0,0 +1,245 @@
+/*
+ * buffer.c
+ *
+ * Oliver Fromme <oliver.fromme@heim3.tu-clausthal.de>
+ * Mon Apr 14 03:53:18 MET DST 1997
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include "mpg123.h"
+
+int outburst = MAXOUTBURST;
+int preload;
+
+static int intflag = FALSE;
+static int usr1flag = FALSE;
+
+static void catch_interrupt (void)
+{
+ intflag = TRUE;
+}
+
+static void catch_usr1 (void)
+{
+ usr1flag = TRUE;
+}
+
+/* Interfaces to writer process */
+
+extern void buffer_sig(int signal, int block);
+
+void buffer_ignore_lowmem(void)
+{
+#ifndef NOXFERMEM
+ if(buffermem->wakeme[XF_READER])
+ xfermem_putcmd(buffermem->fd[XF_WRITER], XF_CMD_WAKEUP);
+#endif
+}
+
+void buffer_end(void)
+{
+#ifndef NOXFERMEM
+ xfermem_putcmd(buffermem->fd[XF_WRITER], XF_CMD_TERMINATE);
+#endif
+}
+
+void buffer_resync(void)
+{
+ buffer_sig(SIGINT, TRUE);
+}
+
+void buffer_reset(void)
+{
+ buffer_sig(SIGUSR1, TRUE);
+}
+
+void buffer_start(void)
+{
+ buffer_sig(SIGCONT, FALSE);
+}
+
+void buffer_stop(void)
+{
+ buffer_sig(SIGSTOP, FALSE);
+}
+
+extern int buffer_pid;
+
+void buffer_sig(int signal, int block)
+{
+
+#ifndef NOXFERMEM
+
+ kill(buffer_pid, signal);
+
+ if (!buffermem || !block)
+ return;
+
+ if(xfermem_block(XF_WRITER, buffermem) != XF_CMD_WAKEUP)
+ perror("Could not resync/reset buffers");
+#endif
+
+ return;
+}
+
+#ifndef NOXFERMEM
+
+void buffer_loop(struct audio_info_struct *ai, sigset_t *oldsigset)
+{
+ int bytes;
+ int my_fd = buffermem->fd[XF_READER];
+ txfermem *xf = buffermem;
+ int done = FALSE;
+
+ catchsignal (SIGINT, catch_interrupt);
+ catchsignal (SIGUSR1, catch_usr1);
+ sigprocmask (SIG_SETMASK, oldsigset, NULL);
+#ifndef NO_DECODE_AUDIO
+ if (param.outmode == DECODE_AUDIO) {
+ if (audio_open(ai) < 0) {
+ perror("audio");
+ exit(1);
+ }
+ }
+#endif
+
+ for (;;) {
+ if (intflag) {
+ intflag = FALSE;
+#ifndef NO_DECODE_AUDIO
+ if (param.outmode == DECODE_AUDIO)
+ audio_queueflush (ai);
+#endif
+ xf->readindex = xf->freeindex;
+ if (xf->wakeme[XF_WRITER])
+ xfermem_putcmd(my_fd, XF_CMD_WAKEUP);
+ }
+ if (usr1flag) {
+ usr1flag = FALSE;
+ /* close and re-open in order to flush
+ * the device's internal buffer before
+ * changing the sample rate. [OF]
+ */
+ /* writer must block when sending SIGUSR1
+ * or we will lose all data processed
+ * in the meantime! [dk]
+ */
+ xf->readindex = xf->freeindex;
+ /* We've nailed down the new starting location -
+ * writer is now safe to go on. [dk]
+ */
+ if (xf->wakeme[XF_WRITER])
+ xfermem_putcmd(my_fd, XF_CMD_WAKEUP);
+#ifndef NO_DECODE_AUDIO
+ if (param.outmode == DECODE_AUDIO) {
+ audio_close (ai);
+ ai->rate = xf->buf[0];
+ ai->channels = xf->buf[1];
+ ai->format = xf->buf[2];
+ if (audio_open(ai) < 0) {
+ sleep(1);
+ /* give em a 2. try */
+ if (audio_open(ai) < 0) {
+ perror("audio");
+ exit(1);
+ }
+ }
+ }
+#endif
+ }
+ if ( (bytes = xfermem_get_usedspace(xf)) < outburst ) {
+ /* if we got a buffer underrun we first
+ * fill 1/8 of the buffer before continue/start
+ * playing */
+ preload = xf->size>>3;
+ if(preload < outburst)
+ preload = outburst;
+ }
+ if(bytes < preload) {
+ int cmd;
+ if (done && !bytes) {
+ break;
+ }
+
+ if(!done) {
+
+ cmd = xfermem_block(XF_READER, xf);
+
+ switch(cmd) {
+
+ /* More input pending. */
+ case XF_CMD_WAKEUP_INFO:
+ continue;
+ /* Yes, we know buffer is low but
+ * know we don't care.
+ */
+ case XF_CMD_WAKEUP:
+ break; /* Proceed playing. */
+ case XF_CMD_TERMINATE:
+ /* Proceed playing without
+ * blocking any further.
+ */
+ done=TRUE;
+ break;
+ case -1:
+ if(errno==EINTR)
+ continue;
+ perror("Yuck! Error in buffer handling...");
+ done = TRUE;
+ xf->readindex = xf->freeindex;
+ xfermem_putcmd(xf->fd[XF_READER], XF_CMD_TERMINATE);
+ break;
+ default:
+ fprintf(stderr, "\nEh!? Received unknown command 0x%x in buffer process. Tell Daniel!\n", cmd);
+ }
+ }
+ }
+ preload = outburst; /* set preload to lower mark */
+ if (bytes > xf->size - xf->readindex)
+ bytes = xf->size - xf->readindex;
+ if (bytes > outburst)
+ bytes = outburst;
+
+ if (param.outmode == DECODE_FILE)
+ bytes = write(OutputDescriptor, xf->data + xf->readindex, bytes);
+#ifndef NO_DECODE_AUDIO
+ else if (param.outmode == DECODE_AUDIO)
+ bytes = audio_play_samples(ai,
+ (unsigned char *) (xf->data + xf->readindex), bytes);
+#endif
+
+ if(bytes < 0) {
+ bytes = 0;
+ if(errno != EINTR) {
+ perror("Ouch ... error while writing audio data: ");
+ /*
+ * done==TRUE tells writer process to stop
+ * sending data. There might be some latency
+ * involved when resetting readindex to
+ * freeindex so we might need more than one
+ * cycle to terminate. (The number of cycles
+ * should be finite unless I managed to mess
+ * up something. ;-) [dk]
+ */
+ done = TRUE;
+ xf->readindex = xf->freeindex;
+ xfermem_putcmd(xf->fd[XF_READER], XF_CMD_TERMINATE);
+ }
+ }
+
+ xf->readindex = (xf->readindex + bytes) % xf->size;
+ if (xf->wakeme[XF_WRITER])
+ xfermem_putcmd(my_fd, XF_CMD_WAKEUP);
+ }
+
+#ifndef NO_DECODE_AUDIO
+ if (param.outmode == DECODE_AUDIO)
+ audio_close (ai);
+#endif
+}
+
+#endif
+
+/* EOF */
diff --git a/mpg123_artsplugin/mpg123/buffer.h b/mpg123_artsplugin/mpg123/buffer.h
new file mode 100644
index 00000000..c1c771d4
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/buffer.h
@@ -0,0 +1,13 @@
+/*
+ * Application specific interaction between main and buffer
+ * process. This is much less generic than the functions in
+ * xfermem so I chose to put it in buffer.[hc].
+ * 01/28/99 [dk]
+ */
+
+void buffer_ignore_lowmem(void);
+void buffer_end(void);
+void buffer_resync(void);
+void buffer_reset(void);
+void buffer_start(void);
+void buffer_stop(void);
diff --git a/mpg123_artsplugin/mpg123/common.c b/mpg123_artsplugin/mpg123/common.c
new file mode 100644
index 00000000..65d658fd
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/common.c
@@ -0,0 +1,921 @@
+/* GPL clean */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef WIN32
+#include <time.h>
+#else
+#include <sys/time.h>
+#endif
+
+#include <fcntl.h>
+
+#ifdef READ_MMAP
+#include <sys/mman.h>
+#ifndef MAP_FAILED
+#define MAP_FAILED ( (void *) -1 )
+#endif
+#endif
+
+#include "mpg123.h"
+#include "genre.h"
+#include "common.h"
+
+int tabsel_123[2][3][16] = {
+ { {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,},
+ {0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,},
+ {0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,} },
+
+ { {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,},
+ {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,},
+ {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,} }
+};
+
+long freqs[9] = { 44100, 48000, 32000, 22050, 24000, 16000 , 11025 , 12000 , 8000 };
+
+struct bitstream_info bsi;
+
+static int bsbufend[2]= { 0,0 };
+static int bsbufold_end;
+static unsigned char bsspace[2][MAXFRAMESIZE+512]; /* MAXFRAMESIZE */
+static unsigned char *bsbuf=bsspace[1],*bsbufold;
+static int bsnum=0;
+
+static int skip_riff(struct reader *);
+static int skip_new_id3(struct reader *);
+
+unsigned char *pcm_sample;
+int pcm_point = 0;
+int audiobufsize = AUDIOBUFSIZE;
+
+static int decode_header(struct frame *fr,unsigned long newhead);
+
+void safewrite(int fd, const void *buf, size_t count) {
+ int donesofar = 0;
+ while(donesofar < count) {
+ int retval;
+ char *p = (char*) buf + donesofar;
+ retval = write(fd,(void*) p,(count-donesofar));
+ if(retval == -1) {
+ if((errno != EINTR) && (errno != EAGAIN))
+ exit(fprintf(stderr,"exception on output!\n"));
+ } else
+ donesofar += retval;
+ }
+}
+
+void audio_flush(int outmode, struct audio_info_struct *ai)
+{
+ if (pcm_point) {
+ switch (outmode) {
+#ifndef NO_DECODE_FILE
+ case DECODE_FILE:
+ safewrite (OutputDescriptor, pcm_sample, pcm_point);
+ break;
+#endif
+#ifndef NO_DECODE_AUDIO
+ case DECODE_AUDIO:
+ audio_play_samples (ai, pcm_sample, pcm_point);
+ break;
+#endif
+#ifndef NOXFERMEM
+ case DECODE_BUFFER:
+ safewrite (buffer_fd[1], pcm_sample, pcm_point);
+ break;
+#endif
+#ifndef NO_DECODE_WAV
+ case DECODE_WAV:
+ case DECODE_CDR:
+ case DECODE_AU:
+ wav_write(pcm_sample, pcm_point);
+ break;
+#endif
+ default:
+ break;
+ }
+ pcm_point = 0;
+ }
+}
+
+#if !defined(WIN32) && !defined(GENERIC)
+void (*catchsignal(int signum, void(*handler)()))()
+{
+ struct sigaction new_sa;
+ struct sigaction old_sa;
+
+#ifdef DONT_CATCH_SIGNALS
+ printf ("Not catching any signals.\n");
+ return ((void (*)()) -1);
+#endif
+
+ new_sa.sa_handler = handler;
+ sigemptyset(&new_sa.sa_mask);
+ new_sa.sa_flags = 0;
+ if (sigaction(signum, &new_sa, &old_sa) == -1)
+ return ((void (*)()) -1);
+ return (old_sa.sa_handler);
+}
+#endif
+
+void read_frame_init (struct frame *fr)
+{
+ fr->firsthead = 0;
+ fr->thishead = 0;
+ fr->freeformatsize = 0;
+}
+
+int head_check(unsigned long head)
+{
+ if( (head & 0xffe00000) != 0xffe00000)
+ return FALSE;
+ if(!((head>>17)&3))
+ return FALSE;
+ if( ((head>>12)&0xf) == 0xf)
+ return FALSE;
+ if( ((head>>10)&0x3) == 0x3 )
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * return 0: EOF or other stream error
+ * -1: giving up
+ * 1: synched
+ */
+#define MAX_INPUT_FRAMESIZE 1920
+#define SYNC_HEAD_MASK 0xffff0000
+#define SYNC_HEAD_MASK_FF 0x0000f000
+#define LOOK_AHEAD_NUM 3
+#define SCAN_LENGTH 16384
+
+#define CHECK_FOR_RIFF 0x0001
+#define CHECK_FOR_ID3_V1 0x0002
+#define CHECK_FOR_ID3_V2 0x0004
+
+int sync_stream(struct reader *rds,struct frame *fr,int flags,int *skipped)
+{
+ int i,j,l,ret;
+ unsigned long firsthead,nexthead;
+ struct frame frameInfo,nextInfo;
+ unsigned char dummybuf[MAX_INPUT_FRAMESIZE];
+ int found=0;
+ int freeformatsize=0;
+
+ for(i=0;i<SCAN_LENGTH;i++) {
+
+ readers_mark_pos(rds); /* store our current position */
+
+ if(!rds->head_read(rds,&firsthead))
+ return 0;
+
+ /* first a few simple checks */
+ if( !head_check(firsthead) || !decode_header(&frameInfo,firsthead) ) {
+
+ /* Check for RIFF Headers */
+ if( (flags & CHECK_FOR_RIFF) && firsthead == ('R'<<24)+('I'<<16)+('F'<<8)+'F') {
+ fprintf(stderr,"Found RIFF Header\n");
+ ret = skip_riff(rds);
+ if(ret > 0) { /* RIFF was OK continue with next byte */
+ *skipped += ret+4;
+ continue;
+ }
+ if(ret == 0)
+ return 0;
+ }
+
+ /* Check for old ID3 Header (or better Footer ;) */
+ if( (flags & CHECK_FOR_ID3_V1) && (firsthead>>8) == ('T'<<16)+('A'<<8)+'G') {
+ fprintf(stderr,"Found old ID3 Header\n");
+ }
+
+ /* Check for new ID3 header */
+ if( (flags & CHECK_FOR_ID3_V2) && (firsthead>>8) == ('I'<<16)+('D'<<8)+'3') {
+ if( (firsthead & 0xff) != 0xff) {
+ fprintf(stderr,"Found new ID3 Header\n");
+ ret = skip_new_id3(rds);
+ if(!ret)
+ return 0;
+ if(ret > 0) {
+ *skipped += ret+4;
+ continue;
+ }
+ }
+ }
+
+ readers_goto_mark(rds); /* reset to old mark and continue */
+ if(!rds->read_frame_body(rds,dummybuf,1))
+ return 0;
+
+ (*skipped)++;
+ continue;
+ }
+
+ found = 0;
+ freeformatsize = 0;
+
+ /*
+ * At the first free format paket we do not know the size
+ */
+ if(frameInfo.bitrate_index == 0) {
+ int maxframesize = MAX_INPUT_FRAMESIZE; /* FIXME depends on layer and sampling freq */
+
+fprintf(stderr,"Searching for next FF header\n");
+
+ if(!rds->head_read(rds,&nexthead))
+ return 0;
+
+ for(j=0;j<maxframesize;j++) {
+ if(head_check(nexthead) && (nexthead & (SYNC_HEAD_MASK|SYNC_HEAD_MASK_FF) ) == (firsthead & (SYNC_HEAD_MASK|SYNC_HEAD_MASK_FF)) &&
+ decode_header(&nextInfo,nexthead) ) {
+
+/* fprintf(stderr,"j: %d %d %d\n",j,frameInfo.padsize,nextInfo.padsize); */
+
+ freeformatsize = j - frameInfo.padsize;
+ found = 1;
+ break;
+ }
+ if(!rds->head_shift(rds,&nexthead))
+ return 0;
+ }
+ }
+ else {
+ if(!rds->read_frame_body(rds,dummybuf,frameInfo.framesize))
+ return 0;
+
+ if(!rds->head_read(rds,&nexthead))
+ return 0;
+
+/*
+fprintf(stderr,"S: %08lx %08lx %d %d %d %d\n",firsthead,nexthead, head_check(nexthead),(nexthead & SYNC_HEAD_MASK) == firsthead,(nexthead & SYNC_HEAD_MASK_FF) != 0x0,decode_header(&nextInfo,nexthead));
+*/
+
+ if( head_check(nexthead) && (nexthead & SYNC_HEAD_MASK) == (firsthead & SYNC_HEAD_MASK) &&
+ (nexthead & SYNC_HEAD_MASK_FF) != 0x0 && decode_header(&nextInfo,nexthead)) {
+ found = 1;
+ }
+ }
+
+ if(!found) {
+ readers_goto_mark(rds); /* reset to old mark and continue */
+ if(!rds->read_frame_body(rds,dummybuf,1))
+ return 0;
+ (*skipped)++;
+ continue;
+ }
+
+/*
+fprintf(stderr,"s: %08lx %08lx %d %d %d\n",firsthead,nexthead,frameInfo.framesize,nextInfo.framesize,freeformatsize);
+*/
+
+ /* check some more frames */
+ for(l=0;l<LOOK_AHEAD_NUM;l++) {
+ int size;
+
+ if( freeformatsize > 0 ) {
+ size = freeformatsize + nextInfo.padsize;
+ }
+ else
+ size = nextInfo.framesize;
+
+ /* step over data */
+ if(!rds->read_frame_body(rds,dummybuf,size))
+ return 0;
+
+ if(!rds->head_read(rds,&nexthead))
+ return 0;
+
+ if(!head_check(nexthead) ||
+ (nexthead & SYNC_HEAD_MASK) != (firsthead & SYNC_HEAD_MASK) ||
+ !decode_header(&nextInfo,nexthead) ) {
+ found = 0;
+ break;
+ }
+ if( freeformatsize > 0) {
+ if( ( nexthead & SYNC_HEAD_MASK_FF ) != 0x0) {
+ found = 0;
+ break;
+ }
+ }
+ else {
+ if( (nexthead & SYNC_HEAD_MASK_FF) == 0x0) {
+ found = 0;
+ break;
+ }
+ }
+ }
+
+ if(found)
+ break;
+
+ readers_goto_mark(rds); /* reset to old mark and continue */
+ if(!rds->read_frame_body(rds,dummybuf,1)) /* skip first byte */
+ return 0;
+ (*skipped)++;
+
+ }
+
+ if(i == SCAN_LENGTH)
+ return -1;
+
+ readers_goto_mark(rds);
+ fr->freeformatsize = freeformatsize;
+ fr->firsthead = firsthead;
+
+ return 1;
+
+}
+
+/*
+ * skips the RIFF header at the beginning
+ *
+ * returns: 0 = read-error
+ * -1/-2 = illegal RIFF header (= -2 backstep not valid)
+ * 1 = skipping succeeded
+ */
+static int skip_riff(struct reader *rds)
+{
+ unsigned long length;
+ unsigned char buf[16];
+
+ if(!rds->read_frame_body(rds,buf,16)) /* read header information */
+ return 0;
+
+ if( strncmp("WAVEfmt ",(char *)buf+4,8) ) /* check 2. signature */
+ return -1;
+
+ length = (unsigned long) buf[12] + /* decode the header length */
+ (((unsigned long) buf[13])<<8) +
+ (((unsigned long) buf[14])<<16) +
+ (((unsigned long) buf[15])<<24);
+
+ if(!rds->skip_bytes(rds,length)) /* will not store data in backbuff! */
+ return 0;
+
+ if(!rds->read_frame_body(rds,buf,8)) /* skip "data" plus length */
+ return 0;
+
+ if(strncmp("data",(char *)buf,4))
+ return -2;
+
+ return length+8+16;
+}
+
+/*
+ * skips the ID3 header at the beginning
+ *
+ * returns: 0 = read-error
+ * -1 = illegal ID3 header
+ * 1 = skipping succeeded
+ */
+static int skip_new_id3(struct reader *rds)
+{
+ unsigned long length;
+ unsigned char buf[6];
+
+ if(!rds->read_frame_body(rds,buf,6)) /* read more header information */
+ return 0;
+
+ if(buf[0] == 0xff)
+ return -1;
+
+ if( (buf[2]|buf[3]|buf[4]|buf[5]) & 0x80)
+ return -1;
+
+ length = (unsigned long) buf[2] & 0x7f;
+ length <<= 7;
+ length += (unsigned long) buf[3] & 0x7f;
+ length <<= 7;
+ length += (unsigned long) buf[4] & 0x7f;
+ length <<= 7;
+ length += (unsigned long) buf[5] & 0x7f;
+
+ if(!rds->skip_bytes(rds,length)) /* will not store data in backbuff! */
+ return 0;
+
+ return length+6;
+
+}
+
+
+
+
+
+/*****************************************************************
+ * read next frame
+ */
+int read_frame(struct reader *rds,struct frame *fr)
+{
+ unsigned long newhead,oldhead;
+ static unsigned char ssave[34];
+
+ oldhead = fr->thishead;
+
+ if (param.halfspeed) {
+ static int halfphase = 0;
+ if (halfphase--) {
+ bsi.bitindex = 0;
+ bsi.wordpointer = (unsigned char *) bsbuf;
+ if (fr->lay == 3)
+ memcpy (bsbuf, ssave, fr->sideInfoSize);
+ return 1;
+ }
+ else
+ halfphase = param.halfspeed - 1;
+ }
+
+ while(1) {
+
+ if(!rds->head_read(rds,&newhead))
+ return FALSE;
+
+/*
+ fprintf(stderr,"n %08lx",newhead);
+*/
+
+ if( !head_check(newhead) || !decode_header(fr,newhead) ) {
+ if (!param.quiet)
+ fprintf(stderr,"Illegal Audio-MPEG-Header 0x%08lx at offset 0x%lx.\n",
+ newhead,rds->tell(rds)-4);
+
+ if(param.tryresync) {
+ int try = 0;
+ readers_pushback_header(rds,newhead);
+ if(sync_stream(rds,fr,0xffff,&try) <= 0)
+ return 0;
+ if(!param.quiet)
+ fprintf (stderr, "Skipped %d bytes in input.\n", try);
+ }
+ else
+ return (0);
+ }
+ else
+ break;
+ }
+
+/*
+ fprintf(stderr,"N %08lx",newhead);
+*/
+
+ fr->header_change = 2;
+ if(oldhead) {
+ if((oldhead & 0xc00) == (fr->thishead & 0xc00)) {
+ if( (oldhead & 0xc0) == 0 && (fr->thishead & 0xc0) == 0)
+ fr->header_change = 1;
+ else if( (oldhead & 0xc0) > 0 && (fr->thishead & 0xc0) > 0)
+ fr->header_change = 1;
+ }
+ }
+
+
+ if(!fr->bitrate_index) {
+ fr->framesize = fr->freeformatsize + fr->padsize;
+ }
+
+/*
+fprintf(stderr,"Reading %d\n",fr->framesize);
+*/
+
+ /* flip/init buffer for Layer 3 */
+ /* FIXME for reentrance */
+ bsbufold = bsbuf;
+ bsbufold_end = bsbufend[bsnum];
+ bsbuf = bsspace[bsnum]+512;
+ bsnum = (bsnum + 1) & 1;
+ bsbufend[bsnum] = fr->framesize;
+
+ /* read main data into memory */
+ if(!rds->read_frame_body(rds,bsbuf,fr->framesize))
+ return 0;
+
+ {
+ /* Test */
+ static struct vbrHeader head;
+ static int vbr = 0; /* FIXME */
+ if(!vbr) {
+ getVBRHeader(&head,bsbuf,fr);
+ vbr = 1;
+ }
+ }
+
+ bsi.bitindex = 0;
+ bsi.wordpointer = (unsigned char *) bsbuf;
+
+ if (param.halfspeed && fr->lay == 3)
+ memcpy (ssave, bsbuf, fr->sideInfoSize);
+
+ return 1;
+}
+
+/*
+ * decode a header and write the information
+ * into the frame structure
+ */
+static int decode_header(struct frame *fr,unsigned long newhead)
+{
+ if(!head_check(newhead)) {
+ fprintf(stderr,"Oopps header is wrong %08lx\n",newhead);
+ return 0;
+ }
+
+ if( newhead & (1<<20) ) {
+ fr->lsf = (newhead & (1<<19)) ? 0x0 : 0x1;
+ fr->mpeg25 = 0;
+ }
+ else {
+ fr->lsf = 1;
+ fr->mpeg25 = 1;
+ }
+
+ /*
+ * CHECKME: should be add more consistency checks here ?
+ * changed layer, changed CRC bit, changed sampling frequency
+ */
+ {
+ fr->lay = 4-((newhead>>17)&3);
+ if( ((newhead>>10)&0x3) == 0x3) {
+ fprintf(stderr,"Stream error\n");
+ return 0;
+ }
+ if(fr->mpeg25) {
+ fr->sampling_frequency = 6 + ((newhead>>10)&0x3);
+ }
+ else
+ fr->sampling_frequency = ((newhead>>10)&0x3) + (fr->lsf*3);
+ fr->error_protection = ((newhead>>16)&0x1)^0x1;
+ }
+
+ fr->bitrate_index = ((newhead>>12)&0xf);
+ fr->padding = ((newhead>>9)&0x1);
+ fr->extension = ((newhead>>8)&0x1);
+ fr->mode = ((newhead>>6)&0x3);
+ fr->mode_ext = ((newhead>>4)&0x3);
+ fr->copyright = ((newhead>>3)&0x1);
+ fr->original = ((newhead>>2)&0x1);
+ fr->emphasis = newhead & 0x3;
+
+ fr->stereo = (fr->mode == MPG_MD_MONO) ? 1 : 2;
+
+ switch(fr->lay) {
+ case 1:
+ fr->framesize = (long) tabsel_123[fr->lsf][0][fr->bitrate_index] * 12000;
+ fr->framesize /= freqs[fr->sampling_frequency];
+ fr->framesize = ((fr->framesize+fr->padding)<<2)-4;
+ fr->sideInfoSize = 0;
+ fr->padsize = fr->padding << 2;
+ break;
+ case 2:
+ fr->framesize = (long) tabsel_123[fr->lsf][1][fr->bitrate_index] * 144000;
+ fr->framesize /= freqs[fr->sampling_frequency];
+ fr->framesize += fr->padding - 4;
+ fr->sideInfoSize = 0;
+ fr->padsize = fr->padding;
+ break;
+ case 3:
+ if(fr->lsf)
+ fr->sideInfoSize = (fr->stereo == 1) ? 9 : 17;
+ else
+ fr->sideInfoSize = (fr->stereo == 1) ? 17 : 32;
+ if(fr->error_protection)
+ fr->sideInfoSize += 2;
+ fr->framesize = (long) tabsel_123[fr->lsf][2][fr->bitrate_index] * 144000;
+ fr->framesize /= freqs[fr->sampling_frequency]<<(fr->lsf);
+ fr->framesize = fr->framesize + fr->padding - 4;
+ fr->padsize = fr->padding;
+ break;
+ default:
+ fprintf(stderr,"Sorry, unknown layer type.\n");
+ return (0);
+ }
+
+ if(!fr->bitrate_index) {
+ /* fprintf(stderr,"Warning, Free format not heavily tested: (head %08lx)\n",newhead); */
+ fr->framesize = 0;
+ }
+ fr->thishead = newhead;
+
+ return 1;
+}
+
+#ifdef MPG123_REMOTE
+void print_rheader(struct frame *fr)
+{
+ static char *modes[4] = { "Stereo", "Joint-Stereo", "Dual-Channel", "Single-Channel" };
+ static char *layers[4] = { "Unknown" , "I", "II", "III" };
+ static char *mpeg_type[2] = { "1.0" , "2.0" };
+
+ /* version, layer, freq, mode, channels, bitrate, BPF */
+ fprintf(stderr,"@I %s %s %ld %s %d %d %d\n",
+ mpeg_type[fr->lsf],layers[fr->lay],freqs[fr->sampling_frequency],
+ modes[fr->mode],fr->stereo,
+ tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index],
+ fr->framesize+4);
+}
+#endif
+
+void print_header(struct frame *fr)
+{
+ static char *modes[4] = { "Stereo", "Joint-Stereo", "Dual-Channel", "Single-Channel" };
+ static char *layers[4] = { "Unknown" , "I", "II", "III" };
+
+ fprintf(stderr,"MPEG %s, Layer: %s, Freq: %ld, mode: %s, modext: %d, BPF : %d\n",
+ fr->mpeg25 ? "2.5" : (fr->lsf ? "2.0" : "1.0"),
+ layers[fr->lay],freqs[fr->sampling_frequency],
+ modes[fr->mode],fr->mode_ext,fr->framesize+4);
+ fprintf(stderr,"Channels: %d, copyright: %s, original: %s, CRC: %s, emphasis: %d.\n",
+ fr->stereo,fr->copyright?"Yes":"No",
+ fr->original?"Yes":"No",fr->error_protection?"Yes":"No",
+ fr->emphasis);
+ fprintf(stderr,"Bitrate: %d Kbits/s, Extension value: %d\n",
+ tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index],fr->extension);
+}
+
+void print_header_compact(struct frame *fr)
+{
+ static char *modes[4] = { "stereo", "joint-stereo", "dual-channel", "mono" };
+ static char *layers[4] = { "Unknown" , "I", "II", "III" };
+
+ fprintf(stderr,"MPEG %s layer %s, %d kbit/s, %ld Hz %s\n",
+ fr->mpeg25 ? "2.5" : (fr->lsf ? "2.0" : "1.0"),
+ layers[fr->lay],
+ tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index],
+ freqs[fr->sampling_frequency], modes[fr->mode]);
+}
+
+void print_id3_tag(unsigned char *buf)
+{
+ struct id3tag {
+ char tag[3];
+ char title[30];
+ char artist[30];
+ char album[30];
+ char year[4];
+ char comment[30];
+ unsigned char genre;
+ };
+ struct id3tag *tag = (struct id3tag *) buf;
+ char title[31]={0,};
+ char artist[31]={0,};
+ char album[31]={0,};
+ char year[5]={0,};
+ char comment[31]={0,};
+ char genre[31]={0,};
+
+ if(param.quiet)
+ return;
+
+ strncpy(title,tag->title,30);
+ strncpy(artist,tag->artist,30);
+ strncpy(album,tag->album,30);
+ strncpy(year,tag->year,4);
+ strncpy(comment,tag->comment,30);
+
+ if ( tag->genre < sizeof(genre_table)/sizeof(*genre_table) ) {
+ strncpy(genre, genre_table[tag->genre], 30);
+ } else {
+ strncpy(genre,"Unknown",30);
+ }
+
+ fprintf(stderr,"Title : %-30s Artist: %s\n",title,artist);
+ fprintf(stderr,"Album : %-30s Year : %4s\n",album,year);
+ fprintf(stderr,"Comment: %-30s Genre : %s\n",comment,genre);
+}
+
+#if 0
+/* removed the strndup for better portability */
+/*
+ * Allocate space for a new string containing the first
+ * "num" characters of "src". The resulting string is
+ * always zero-terminated. Returns NULL if malloc fails.
+ */
+char *strndup (const char *src, int num)
+{
+ char *dst;
+
+ if (!(dst = (char *) malloc(num+1)))
+ return (NULL);
+ dst[num] = '\0';
+ return (strncpy(dst, src, num));
+}
+#endif
+
+/*
+ * Split "path" into directory and filename components.
+ *
+ * Return value is 0 if no directory was specified (i.e.
+ * "path" does not contain a '/'), OR if the directory
+ * is the same as on the previous call to this function.
+ *
+ * Return value is 1 if a directory was specified AND it
+ * is different from the previous one (if any).
+ */
+
+int split_dir_file (const char *path, char **dname, char **fname)
+{
+ static char *lastdir = NULL;
+ char *slashpos;
+
+ if ((slashpos = strrchr(path, '/'))) {
+ *fname = slashpos + 1;
+ *dname = strdup(path); /* , 1 + slashpos - path); */
+ if(!(*dname)) {
+ perror("memory");
+ exit(1);
+ }
+ (*dname)[1 + slashpos - path] = 0;
+ if (lastdir && !strcmp(lastdir, *dname)) {
+ /*** same as previous directory ***/
+ free (*dname);
+ *dname = lastdir;
+ return 0;
+ }
+ else {
+ /*** different directory ***/
+ if (lastdir)
+ free (lastdir);
+ lastdir = *dname;
+ return 1;
+ }
+ }
+ else {
+ /*** no directory specified ***/
+ if (lastdir) {
+ free (lastdir);
+ lastdir = NULL;
+ };
+ *dname = NULL;
+ *fname = (char *)path;
+ return 0;
+ }
+}
+
+void set_pointer(int ssize,long backstep)
+{
+ bsi.wordpointer = bsbuf + ssize - backstep;
+ if (backstep)
+ memcpy(bsi.wordpointer,bsbufold+bsbufold_end-backstep,backstep);
+ bsi.bitindex = 0;
+}
+
+/********************************/
+
+double compute_bpf(struct frame *fr)
+{
+ double bpf;
+
+ if(!fr->bitrate_index) {
+ return fr->freeformatsize + 4;
+ }
+
+ switch(fr->lay) {
+ case 1:
+ bpf = tabsel_123[fr->lsf][0][fr->bitrate_index];
+ bpf *= 12000.0 * 4.0;
+ bpf /= freqs[fr->sampling_frequency] <<(fr->lsf);
+ break;
+ case 2:
+ case 3:
+ bpf = tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index];
+ bpf *= 144000;
+ bpf /= freqs[fr->sampling_frequency] << (fr->lsf);
+ break;
+ default:
+ bpf = 1.0;
+ }
+
+ return bpf;
+}
+
+double compute_tpf(struct frame *fr)
+{
+ static int bs[4] = { 0,384,1152,1152 };
+ double tpf;
+
+ tpf = (double) bs[fr->lay];
+ tpf /= freqs[fr->sampling_frequency] << (fr->lsf);
+ return tpf;
+}
+
+/*
+ * Returns number of frames queued up in output buffer, i.e.
+ * offset between currently played and currently decoded frame.
+ */
+
+#ifndef NOXFERMEM
+long compute_buffer_offset(struct frame *fr)
+{
+ long bufsize;
+
+ /*
+ * buffermem->buf[0] holds output sampling rate,
+ * buffermem->buf[1] holds number of channels,
+ * buffermem->buf[2] holds audio format of output.
+ */
+
+ if(!param.usebuffer || !(bufsize=xfermem_get_usedspace(buffermem))
+ || !buffermem->buf[0] || !buffermem->buf[1])
+ return 0;
+
+ bufsize = (long)((double) bufsize / buffermem->buf[0] /
+ buffermem->buf[1] / compute_tpf(fr));
+
+ if((buffermem->buf[2] & AUDIO_FORMAT_MASK) == AUDIO_FORMAT_16)
+ return bufsize/2;
+ else
+ return bufsize;
+}
+#endif
+
+void print_stat(struct reader *rds,struct frame *fr,int no,long buffsize,struct audio_info_struct *ai)
+{
+ double bpf,tpf,tim1,tim2;
+ double dt = 0.0;
+ int sno,rno;
+ char outbuf[256];
+
+ if(!rds || !fr)
+ return;
+
+ outbuf[0] = 0;
+
+#ifndef GENERIC
+ {
+ struct timeval t;
+ fd_set serr;
+ int n,errfd = fileno(stderr);
+
+ t.tv_sec=t.tv_usec=0;
+
+ FD_ZERO(&serr);
+ FD_SET(errfd,&serr);
+ n = select(errfd+1,NULL,&serr,NULL,&t);
+ if(n <= 0)
+ return;
+ }
+#endif
+
+ bpf = compute_bpf(fr);
+ tpf = compute_tpf(fr);
+
+ if(buffsize > 0 && ai && ai->rate > 0 && ai->channels > 0) {
+ dt = (double) buffsize / ai->rate / ai->channels;
+ if( (ai->format & AUDIO_FORMAT_MASK) == AUDIO_FORMAT_16)
+ dt *= 0.5;
+ }
+
+ rno = 0;
+ sno = no;
+ if(rds->filelen >= 0) {
+ long t = rds->tell(rds);
+ rno = (int)((double)(rds->filelen-t)/bpf);
+ sno = (int)((double)t/bpf);
+ }
+
+ sprintf(outbuf+strlen(outbuf),"\rFrame# %5d [%5d], ",sno,rno);
+
+ tim1 = sno*tpf-dt;
+ tim2 = rno*tpf+dt;
+#if 0
+ tim1 = tim1 < 0 ? 0.0 : tim1;
+#endif
+ tim2 = tim2 < 0 ? 0.0 : tim2;
+
+ sprintf(outbuf+strlen(outbuf),"Time: %02u:%02u.%02u [%02u:%02u.%02u], ",
+ (unsigned int)tim1/60,
+ (unsigned int)tim1%60,
+ (unsigned int)(tim1*100)%100,
+ (unsigned int)tim2/60,
+ (unsigned int)tim2%60,
+ (unsigned int)(tim2*100)%100);
+
+ if(param.usebuffer)
+ sprintf(outbuf+strlen(outbuf),"[%8ld] ",(long)buffsize);
+ write(fileno(stderr),outbuf,strlen(outbuf));
+#if 0
+ fflush(out); /* hmm not really nec. */
+#endif
+}
+
+int get_songlen(struct reader *rds,struct frame *fr,int no)
+{
+ double tpf;
+
+ if(!fr)
+ return 0;
+
+ if(no < 0) {
+ if(!rds || rds->filelen < 0)
+ return 0;
+ no = (double) rds->filelen / compute_bpf(fr);
+ }
+
+ tpf = compute_tpf(fr);
+ return no*tpf;
+}
+
+
diff --git a/mpg123_artsplugin/mpg123/common.h b/mpg123_artsplugin/mpg123/common.h
new file mode 100644
index 00000000..716a3c83
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/common.h
@@ -0,0 +1,19 @@
+/*
+ * common.h
+ */
+
+extern void print_id3_tag(unsigned char *buf);
+extern unsigned long firsthead;
+extern int tabsel_123[2][3][16];
+extern double compute_tpf(struct frame *fr);
+extern double compute_bpf(struct frame *fr);
+extern long compute_buffer_offset(struct frame *fr);
+
+/*
+struct bitstream_info {
+ int bitindex;
+ unsigned char *wordpointer;
+};
+*/
+
+
diff --git a/mpg123_artsplugin/mpg123/dct36_3dnow.s b/mpg123_artsplugin/mpg123/dct36_3dnow.s
new file mode 100644
index 00000000..05b7b81d
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/dct36_3dnow.s
@@ -0,0 +1,499 @@
+#
+# dct36_3dnow.s - 3DNow! optimized dct36()
+#
+# This code based 'dct36_3dnow.s' by Syuuhei Kashiyama
+# <squash@mb.kcom.ne.jp>,only two types of changes have been made:
+#
+# - remove PREFETCH instruction for speedup
+# - change function name for support 3DNow! automatic detect
+#
+# You can find Kashiyama's original 3dnow! support patch
+# (for mpg123-0.59o) at
+# http:#/user.ecc.u-tokyo.ac.jp/~g810370/linux-simd/ (Japanese).
+#
+# by KIMURA Takuhiro <kim@hannah.ipc.miyakyo-u.ac.jp> - until 31.Mar.1999
+# <kim@comtec.co.jp> - after 1.Apr.1999
+#
+
+##/
+##/ Replacement of dct36() with AMD's 3DNow! SIMD operations support
+##/
+##/ Syuuhei Kashiyama <squash@mb.kcom.ne.jp>
+##/
+##/ The author of this program disclaim whole expressed or implied
+##/ warranties with regard to this program, and in no event shall the
+##/ author of this program liable to whatever resulted from the use of
+##/ this program. Use it at your own risk.
+##/
+
+ .globl dct36_3dnow
+ .type dct36_3dnow,@function
+dct36_3dnow:
+ pushl %ebp
+ movl %esp,%ebp
+ subl $120,%esp
+ pushl %esi
+ pushl %ebx
+ movl 8(%ebp),%eax
+ movl 12(%ebp),%esi
+ movl 16(%ebp),%ecx
+ movl 20(%ebp),%edx
+ movl 24(%ebp),%ebx
+ leal -128(%ebp),%esp
+
+ femms
+ movq (%eax),%mm0
+ movq 4(%eax),%mm1
+ pfadd %mm1,%mm0
+ movq %mm0,4(%eax)
+ psrlq $32,%mm1
+ movq 12(%eax),%mm2
+ punpckldq %mm2,%mm1
+ pfadd %mm2,%mm1
+ movq %mm1,12(%eax)
+ psrlq $32,%mm2
+ movq 20(%eax),%mm3
+ punpckldq %mm3,%mm2
+ pfadd %mm3,%mm2
+ movq %mm2,20(%eax)
+ psrlq $32,%mm3
+ movq 28(%eax),%mm4
+ punpckldq %mm4,%mm3
+ pfadd %mm4,%mm3
+ movq %mm3,28(%eax)
+ psrlq $32,%mm4
+ movq 36(%eax),%mm5
+ punpckldq %mm5,%mm4
+ pfadd %mm5,%mm4
+ movq %mm4,36(%eax)
+ psrlq $32,%mm5
+ movq 44(%eax),%mm6
+ punpckldq %mm6,%mm5
+ pfadd %mm6,%mm5
+ movq %mm5,44(%eax)
+ psrlq $32,%mm6
+ movq 52(%eax),%mm7
+ punpckldq %mm7,%mm6
+ pfadd %mm7,%mm6
+ movq %mm6,52(%eax)
+ psrlq $32,%mm7
+ movq 60(%eax),%mm0
+ punpckldq %mm0,%mm7
+ pfadd %mm0,%mm7
+ movq %mm7,60(%eax)
+ psrlq $32,%mm0
+ movd 68(%eax),%mm1
+ pfadd %mm1,%mm0
+ movd %mm0,68(%eax)
+ movd 4(%eax),%mm0
+ movd 12(%eax),%mm1
+ punpckldq %mm1,%mm0
+ punpckldq 20(%eax),%mm1
+ pfadd %mm1,%mm0
+ movd %mm0,12(%eax)
+ psrlq $32,%mm0
+ movd %mm0,20(%eax)
+ psrlq $32,%mm1
+ movd 28(%eax),%mm2
+ punpckldq %mm2,%mm1
+ punpckldq 36(%eax),%mm2
+ pfadd %mm2,%mm1
+ movd %mm1,28(%eax)
+ psrlq $32,%mm1
+ movd %mm1,36(%eax)
+ psrlq $32,%mm2
+ movd 44(%eax),%mm3
+ punpckldq %mm3,%mm2
+ punpckldq 52(%eax),%mm3
+ pfadd %mm3,%mm2
+ movd %mm2,44(%eax)
+ psrlq $32,%mm2
+ movd %mm2,52(%eax)
+ psrlq $32,%mm3
+ movd 60(%eax),%mm4
+ punpckldq %mm4,%mm3
+ punpckldq 68(%eax),%mm4
+ pfadd %mm4,%mm3
+ movd %mm3,60(%eax)
+ psrlq $32,%mm3
+ movd %mm3,68(%eax)
+
+ movq 24(%eax),%mm0
+ movq 48(%eax),%mm1
+ movd COS9+12,%mm2
+ punpckldq %mm2,%mm2
+ movd COS9+24,%mm3
+ punpckldq %mm3,%mm3
+ pfmul %mm2,%mm0
+ pfmul %mm3,%mm1
+ pushl %eax
+ movl $1,%eax
+ movd %eax,%mm7
+ pi2fd %mm7,%mm7
+ popl %eax
+ movq 8(%eax),%mm2
+ movd COS9+4,%mm3
+ punpckldq %mm3,%mm3
+ pfmul %mm3,%mm2
+ pfadd %mm0,%mm2
+ movq 40(%eax),%mm3
+ movd COS9+20,%mm4
+ punpckldq %mm4,%mm4
+ pfmul %mm4,%mm3
+ pfadd %mm3,%mm2
+ movq 56(%eax),%mm3
+ movd COS9+28,%mm4
+ punpckldq %mm4,%mm4
+ pfmul %mm4,%mm3
+ pfadd %mm3,%mm2
+ movq (%eax),%mm3
+ movq 16(%eax),%mm4
+ movd COS9+8,%mm5
+ punpckldq %mm5,%mm5
+ pfmul %mm5,%mm4
+ pfadd %mm4,%mm3
+ movq 32(%eax),%mm4
+ movd COS9+16,%mm5
+ punpckldq %mm5,%mm5
+ pfmul %mm5,%mm4
+ pfadd %mm4,%mm3
+ pfadd %mm1,%mm3
+ movq 64(%eax),%mm4
+ movd COS9+32,%mm5
+ punpckldq %mm5,%mm5
+ pfmul %mm5,%mm4
+ pfadd %mm4,%mm3
+ movq %mm2,%mm4
+ pfadd %mm3,%mm4
+ movq %mm7,%mm5
+ punpckldq tfcos36+0,%mm5
+ pfmul %mm5,%mm4
+ movq %mm4,%mm5
+ pfacc %mm5,%mm5
+ movd 108(%edx),%mm6
+ punpckldq 104(%edx),%mm6
+ pfmul %mm6,%mm5
+ movd %mm5,36(%ecx)
+ psrlq $32,%mm5
+ movd %mm5,32(%ecx)
+ movq %mm4,%mm6
+ punpckldq %mm6,%mm5
+ pfsub %mm6,%mm5
+ punpckhdq %mm5,%mm5
+ movd 32(%edx),%mm6
+ punpckldq 36(%edx),%mm6
+ pfmul %mm6,%mm5
+ movd 32(%esi),%mm6
+ punpckldq 36(%esi),%mm6
+ pfadd %mm6,%mm5
+ movd %mm5,1024(%ebx)
+ psrlq $32,%mm5
+ movd %mm5,1152(%ebx)
+ movq %mm3,%mm4
+ pfsub %mm2,%mm4
+ movq %mm7,%mm5
+ punpckldq tfcos36+32,%mm5
+ pfmul %mm5,%mm4
+ movq %mm4,%mm5
+ pfacc %mm5,%mm5
+ movd 140(%edx),%mm6
+ punpckldq 72(%edx),%mm6
+ pfmul %mm6,%mm5
+ movd %mm5,68(%ecx)
+ psrlq $32,%mm5
+ movd %mm5,0(%ecx)
+ movq %mm4,%mm6
+ punpckldq %mm6,%mm5
+ pfsub %mm6,%mm5
+ punpckhdq %mm5,%mm5
+ movd 0(%edx),%mm6
+ punpckldq 68(%edx),%mm6
+ pfmul %mm6,%mm5
+ movd 0(%esi),%mm6
+ punpckldq 68(%esi),%mm6
+ pfadd %mm6,%mm5
+ movd %mm5,0(%ebx)
+ psrlq $32,%mm5
+ movd %mm5,2176(%ebx)
+ movq 8(%eax),%mm2
+ movq 40(%eax),%mm3
+ pfsub %mm3,%mm2
+ movq 56(%eax),%mm3
+ pfsub %mm3,%mm2
+ movd COS9+12,%mm3
+ punpckldq %mm3,%mm3
+ pfmul %mm3,%mm2
+ movq 16(%eax),%mm3
+ movq 32(%eax),%mm4
+ pfsub %mm4,%mm3
+ movq 64(%eax),%mm4
+ pfsub %mm4,%mm3
+ movd COS9+24,%mm4
+ punpckldq %mm4,%mm4
+ pfmul %mm4,%mm3
+ movq 48(%eax),%mm4
+ pfsub %mm4,%mm3
+ movq (%eax),%mm4
+ pfadd %mm4,%mm3
+ movq %mm2,%mm4
+ pfadd %mm3,%mm4
+ movq %mm7,%mm5
+ punpckldq tfcos36+4,%mm5
+ pfmul %mm5,%mm4
+ movq %mm4,%mm5
+ pfacc %mm5,%mm5
+ movd 112(%edx),%mm6
+ punpckldq 100(%edx),%mm6
+ pfmul %mm6,%mm5
+ movd %mm5,40(%ecx)
+ psrlq $32,%mm5
+ movd %mm5,28(%ecx)
+ movq %mm4,%mm6
+ punpckldq %mm6,%mm5
+ pfsub %mm6,%mm5
+ punpckhdq %mm5,%mm5
+ movd 28(%edx),%mm6
+ punpckldq 40(%edx),%mm6
+ pfmul %mm6,%mm5
+ movd 28(%esi),%mm6
+ punpckldq 40(%esi),%mm6
+ pfadd %mm6,%mm5
+ movd %mm5,896(%ebx)
+ psrlq $32,%mm5
+ movd %mm5,1280(%ebx)
+ movq %mm3,%mm4
+ pfsub %mm2,%mm4
+ movq %mm7,%mm5
+ punpckldq tfcos36+28,%mm5
+ pfmul %mm5,%mm4
+ movq %mm4,%mm5
+ pfacc %mm5,%mm5
+ movd 136(%edx),%mm6
+ punpckldq 76(%edx),%mm6
+ pfmul %mm6,%mm5
+ movd %mm5,64(%ecx)
+ psrlq $32,%mm5
+ movd %mm5,4(%ecx)
+ movq %mm4,%mm6
+ punpckldq %mm6,%mm5
+ pfsub %mm6,%mm5
+ punpckhdq %mm5,%mm5
+ movd 4(%edx),%mm6
+ punpckldq 64(%edx),%mm6
+ pfmul %mm6,%mm5
+ movd 4(%esi),%mm6
+ punpckldq 64(%esi),%mm6
+ pfadd %mm6,%mm5
+ movd %mm5,128(%ebx)
+ psrlq $32,%mm5
+ movd %mm5,2048(%ebx)
+
+ movq 8(%eax),%mm2
+ movd COS9+20,%mm3
+ punpckldq %mm3,%mm3
+ pfmul %mm3,%mm2
+ pfsub %mm0,%mm2
+ movq 40(%eax),%mm3
+ movd COS9+28,%mm4
+ punpckldq %mm4,%mm4
+ pfmul %mm4,%mm3
+ pfsub %mm3,%mm2
+ movq 56(%eax),%mm3
+ movd COS9+4,%mm4
+ punpckldq %mm4,%mm4
+ pfmul %mm4,%mm3
+ pfadd %mm3,%mm2
+ movq (%eax),%mm3
+ movq 16(%eax),%mm4
+ movd COS9+32,%mm5
+ punpckldq %mm5,%mm5
+ pfmul %mm5,%mm4
+ pfsub %mm4,%mm3
+ movq 32(%eax),%mm4
+ movd COS9+8,%mm5
+ punpckldq %mm5,%mm5
+ pfmul %mm5,%mm4
+ pfsub %mm4,%mm3
+ pfadd %mm1,%mm3
+ movq 64(%eax),%mm4
+ movd COS9+16,%mm5
+ punpckldq %mm5,%mm5
+ pfmul %mm5,%mm4
+ pfadd %mm4,%mm3
+ movq %mm2,%mm4
+ pfadd %mm3,%mm4
+ movq %mm7,%mm5
+ punpckldq tfcos36+8,%mm5
+ pfmul %mm5,%mm4
+ movq %mm4,%mm5
+ pfacc %mm5,%mm5
+ movd 116(%edx),%mm6
+ punpckldq 96(%edx),%mm6
+ pfmul %mm6,%mm5
+ movd %mm5,44(%ecx)
+ psrlq $32,%mm5
+ movd %mm5,24(%ecx)
+ movq %mm4,%mm6
+ punpckldq %mm6,%mm5
+ pfsub %mm6,%mm5
+ punpckhdq %mm5,%mm5
+ movd 24(%edx),%mm6
+ punpckldq 44(%edx),%mm6
+ pfmul %mm6,%mm5
+ movd 24(%esi),%mm6
+ punpckldq 44(%esi),%mm6
+ pfadd %mm6,%mm5
+ movd %mm5,768(%ebx)
+ psrlq $32,%mm5
+ movd %mm5,1408(%ebx)
+ movq %mm3,%mm4
+ pfsub %mm2,%mm4
+ movq %mm7,%mm5
+ punpckldq tfcos36+24,%mm5
+ pfmul %mm5,%mm4
+ movq %mm4,%mm5
+ pfacc %mm5,%mm5
+ movd 132(%edx),%mm6
+ punpckldq 80(%edx),%mm6
+ pfmul %mm6,%mm5
+ movd %mm5,60(%ecx)
+ psrlq $32,%mm5
+ movd %mm5,8(%ecx)
+ movq %mm4,%mm6
+ punpckldq %mm6,%mm5
+ pfsub %mm6,%mm5
+ punpckhdq %mm5,%mm5
+ movd 8(%edx),%mm6
+ punpckldq 60(%edx),%mm6
+ pfmul %mm6,%mm5
+ movd 8(%esi),%mm6
+ punpckldq 60(%esi),%mm6
+ pfadd %mm6,%mm5
+ movd %mm5,256(%ebx)
+ psrlq $32,%mm5
+ movd %mm5,1920(%ebx)
+ movq 8(%eax),%mm2
+ movd COS9+28,%mm3
+ punpckldq %mm3,%mm3
+ pfmul %mm3,%mm2
+ pfsub %mm0,%mm2
+ movq 40(%eax),%mm3
+ movd COS9+4,%mm4
+ punpckldq %mm4,%mm4
+ pfmul %mm4,%mm3
+ pfadd %mm3,%mm2
+ movq 56(%eax),%mm3
+ movd COS9+20,%mm4
+ punpckldq %mm4,%mm4
+ pfmul %mm4,%mm3
+ pfsub %mm3,%mm2
+ movq (%eax),%mm3
+ movq 16(%eax),%mm4
+ movd COS9+16,%mm5
+ punpckldq %mm5,%mm5
+ pfmul %mm5,%mm4
+ pfsub %mm4,%mm3
+ movq 32(%eax),%mm4
+ movd COS9+32,%mm5
+ punpckldq %mm5,%mm5
+ pfmul %mm5,%mm4
+ pfadd %mm4,%mm3
+ pfadd %mm1,%mm3
+ movq 64(%eax),%mm4
+ movd COS9+8,%mm5
+ punpckldq %mm5,%mm5
+ pfmul %mm5,%mm4
+ pfsub %mm4,%mm3
+ movq %mm2,%mm4
+ pfadd %mm3,%mm4
+ movq %mm7,%mm5
+ punpckldq tfcos36+12,%mm5
+ pfmul %mm5,%mm4
+ movq %mm4,%mm5
+ pfacc %mm5,%mm5
+ movd 120(%edx),%mm6
+ punpckldq 92(%edx),%mm6
+ pfmul %mm6,%mm5
+ movd %mm5,48(%ecx)
+ psrlq $32,%mm5
+ movd %mm5,20(%ecx)
+ movq %mm4,%mm6
+ punpckldq %mm6,%mm5
+ pfsub %mm6,%mm5
+ punpckhdq %mm5,%mm5
+ movd 20(%edx),%mm6
+ punpckldq 48(%edx),%mm6
+ pfmul %mm6,%mm5
+ movd 20(%esi),%mm6
+ punpckldq 48(%esi),%mm6
+ pfadd %mm6,%mm5
+ movd %mm5,640(%ebx)
+ psrlq $32,%mm5
+ movd %mm5,1536(%ebx)
+ movq %mm3,%mm4
+ pfsub %mm2,%mm4
+ movq %mm7,%mm5
+ punpckldq tfcos36+20,%mm5
+ pfmul %mm5,%mm4
+ movq %mm4,%mm5
+ pfacc %mm5,%mm5
+ movd 128(%edx),%mm6
+ punpckldq 84(%edx),%mm6
+ pfmul %mm6,%mm5
+ movd %mm5,56(%ecx)
+ psrlq $32,%mm5
+ movd %mm5,12(%ecx)
+ movq %mm4,%mm6
+ punpckldq %mm6,%mm5
+ pfsub %mm6,%mm5
+ punpckhdq %mm5,%mm5
+ movd 12(%edx),%mm6
+ punpckldq 56(%edx),%mm6
+ pfmul %mm6,%mm5
+ movd 12(%esi),%mm6
+ punpckldq 56(%esi),%mm6
+ pfadd %mm6,%mm5
+ movd %mm5,384(%ebx)
+ psrlq $32,%mm5
+ movd %mm5,1792(%ebx)
+
+ movq (%eax),%mm4
+ movq 16(%eax),%mm3
+ pfsub %mm3,%mm4
+ movq 32(%eax),%mm3
+ pfadd %mm3,%mm4
+ movq 48(%eax),%mm3
+ pfsub %mm3,%mm4
+ movq 64(%eax),%mm3
+ pfadd %mm3,%mm4
+ movq %mm7,%mm5
+ punpckldq tfcos36+16,%mm5
+ pfmul %mm5,%mm4
+ movq %mm4,%mm5
+ pfacc %mm5,%mm5
+ movd 124(%edx),%mm6
+ punpckldq 88(%edx),%mm6
+ pfmul %mm6,%mm5
+ movd %mm5,52(%ecx)
+ psrlq $32,%mm5
+ movd %mm5,16(%ecx)
+ movq %mm4,%mm6
+ punpckldq %mm6,%mm5
+ pfsub %mm6,%mm5
+ punpckhdq %mm5,%mm5
+ movd 16(%edx),%mm6
+ punpckldq 52(%edx),%mm6
+ pfmul %mm6,%mm5
+ movd 16(%esi),%mm6
+ punpckldq 52(%esi),%mm6
+ pfadd %mm6,%mm5
+ movd %mm5,512(%ebx)
+ psrlq $32,%mm5
+ movd %mm5,1664(%ebx)
+
+ femms
+ popl %ebx
+ popl %esi
+ movl %ebp,%esp
+ popl %ebp
+ ret
diff --git a/mpg123_artsplugin/mpg123/dct64.c b/mpg123_artsplugin/mpg123/dct64.c
new file mode 100644
index 00000000..59c19676
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/dct64.c
@@ -0,0 +1,168 @@
+
+/*
+ * Discrete Cosine Tansform (DCT) for subband synthesis
+ *
+ * -funroll-loops (for gcc) will remove the loops for better performance
+ * using loops in the source-code enhances readabillity
+ */
+
+/*
+ * TODO: write an optimized version for the down-sampling modes
+ * (in these modes the bands 16-31 (2:1) or 8-31 (4:1) are zero
+ */
+
+#include "mpg123.h"
+
+void dct64(real *out0,real *out1,real *samples)
+{
+ real bufs[64];
+
+ {
+ register int i,j;
+ register real *b1,*b2,*bs,*costab;
+
+ b1 = samples;
+ bs = bufs;
+ costab = pnts[0]+16;
+ b2 = b1 + 32;
+
+ for(i=15;i>=0;i--)
+ *bs++ = (*b1++ + *--b2);
+ for(i=15;i>=0;i--)
+ *bs++ = REAL_MUL((*--b2 - *b1++), *--costab);
+
+ b1 = bufs;
+ costab = pnts[1]+8;
+ b2 = b1 + 16;
+
+ {
+ for(i=7;i>=0;i--)
+ *bs++ = (*b1++ + *--b2);
+ for(i=7;i>=0;i--)
+ *bs++ = REAL_MUL((*--b2 - *b1++), *--costab);
+ b2 += 32;
+ costab += 8;
+ for(i=7;i>=0;i--)
+ *bs++ = (*b1++ + *--b2);
+ for(i=7;i>=0;i--)
+ *bs++ = REAL_MUL((*b1++ - *--b2), *--costab);
+ b2 += 32;
+ }
+
+ bs = bufs;
+ costab = pnts[2];
+ b2 = b1 + 8;
+
+ for(j=2;j;j--)
+ {
+ for(i=3;i>=0;i--)
+ *bs++ = (*b1++ + *--b2);
+ for(i=3;i>=0;i--)
+ *bs++ = REAL_MUL((*--b2 - *b1++), costab[i]);
+ b2 += 16;
+ for(i=3;i>=0;i--)
+ *bs++ = (*b1++ + *--b2);
+ for(i=3;i>=0;i--)
+ *bs++ = REAL_MUL((*b1++ - *--b2), costab[i]);
+ b2 += 16;
+ }
+
+ b1 = bufs;
+ costab = pnts[3];
+ b2 = b1 + 4;
+
+ for(j=4;j;j--)
+ {
+ *bs++ = (*b1++ + *--b2);
+ *bs++ = (*b1++ + *--b2);
+ *bs++ = REAL_MUL((*--b2 - *b1++), costab[1]);
+ *bs++ = REAL_MUL((*--b2 - *b1++), costab[0]);
+ b2 += 8;
+ *bs++ = (*b1++ + *--b2);
+ *bs++ = (*b1++ + *--b2);
+ *bs++ = REAL_MUL((*b1++ - *--b2), costab[1]);
+ *bs++ = REAL_MUL((*b1++ - *--b2), costab[0]);
+ b2 += 8;
+ }
+ bs = bufs;
+ costab = pnts[4];
+
+ for(j=8;j;j--)
+ {
+ real v0,v1;
+ v0=*b1++; v1 = *b1++;
+ *bs++ = (v0 + v1);
+ *bs++ = REAL_MUL((v0 - v1), (*costab));
+ v0=*b1++; v1 = *b1++;
+ *bs++ = (v0 + v1);
+ *bs++ = REAL_MUL((v1 - v0), (*costab));
+ }
+
+ }
+
+
+ {
+ register real *b1;
+ register int i;
+
+ for(b1=bufs,i=8;i;i--,b1+=4)
+ b1[2] += b1[3];
+
+ for(b1=bufs,i=4;i;i--,b1+=8)
+ {
+ b1[4] += b1[6];
+ b1[6] += b1[5];
+ b1[5] += b1[7];
+ }
+
+ for(b1=bufs,i=2;i;i--,b1+=16)
+ {
+ b1[8] += b1[12];
+ b1[12] += b1[10];
+ b1[10] += b1[14];
+ b1[14] += b1[9];
+ b1[9] += b1[13];
+ b1[13] += b1[11];
+ b1[11] += b1[15];
+ }
+ }
+
+
+ out0[0x10*16] = bufs[0];
+ out0[0x10*15] = bufs[16+0] + bufs[16+8];
+ out0[0x10*14] = bufs[8];
+ out0[0x10*13] = bufs[16+8] + bufs[16+4];
+ out0[0x10*12] = bufs[4];
+ out0[0x10*11] = bufs[16+4] + bufs[16+12];
+ out0[0x10*10] = bufs[12];
+ out0[0x10* 9] = bufs[16+12] + bufs[16+2];
+ out0[0x10* 8] = bufs[2];
+ out0[0x10* 7] = bufs[16+2] + bufs[16+10];
+ out0[0x10* 6] = bufs[10];
+ out0[0x10* 5] = bufs[16+10] + bufs[16+6];
+ out0[0x10* 4] = bufs[6];
+ out0[0x10* 3] = bufs[16+6] + bufs[16+14];
+ out0[0x10* 2] = bufs[14];
+ out0[0x10* 1] = bufs[16+14] + bufs[16+1];
+ out0[0x10* 0] = bufs[1];
+
+ out1[0x10* 0] = bufs[1];
+ out1[0x10* 1] = bufs[16+1] + bufs[16+9];
+ out1[0x10* 2] = bufs[9];
+ out1[0x10* 3] = bufs[16+9] + bufs[16+5];
+ out1[0x10* 4] = bufs[5];
+ out1[0x10* 5] = bufs[16+5] + bufs[16+13];
+ out1[0x10* 6] = bufs[13];
+ out1[0x10* 7] = bufs[16+13] + bufs[16+3];
+ out1[0x10* 8] = bufs[3];
+ out1[0x10* 9] = bufs[16+3] + bufs[16+11];
+ out1[0x10*10] = bufs[11];
+ out1[0x10*11] = bufs[16+11] + bufs[16+7];
+ out1[0x10*12] = bufs[7];
+ out1[0x10*13] = bufs[16+7] + bufs[16+15];
+ out1[0x10*14] = bufs[15];
+ out1[0x10*15] = bufs[16+15];
+
+}
+
+
diff --git a/mpg123_artsplugin/mpg123/dct64_3dnow.s b/mpg123_artsplugin/mpg123/dct64_3dnow.s
new file mode 100644
index 00000000..2a214905
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/dct64_3dnow.s
@@ -0,0 +1,706 @@
+##/
+##/ Replacement of dct64() with AMD's 3DNow! SIMD operations support
+##/
+##/ Syuuhei Kashiyama <squash@mb.kcom.ne.jp>
+##/
+##/ The author of this program disclaim whole expressed or implied
+##/ warranties with regard to this program, and in no event shall the
+##/ author of this program liable to whatever resulted from the use of
+##/ this program. Use it at your own risk.
+##/
+
+ .globl dct64_3dnow
+ .type dct64_3dnow,@function
+dct64_3dnow:
+ subl $256,%esp
+ pushl %ebp
+ pushl %edi
+ pushl %esi
+ pushl %ebx
+ leal 16(%esp),%ebx
+ movl 284(%esp),%edi
+ movl 276(%esp),%ebp
+ movl 280(%esp),%edx
+ leal 128(%ebx),%esi
+
+ # femms
+
+ ## 1
+ movl pnts,%eax
+ movq 0(%edi),%mm0
+ movq %mm0,%mm1
+ movd 124(%edi),%mm2
+ punpckldq 120(%edi),%mm2
+ movq 0(%eax),%mm3
+ pfadd %mm2,%mm0
+ movq %mm0,0(%ebx)
+ pfsub %mm2,%mm1
+ pfmul %mm3,%mm1
+ movd %mm1,124(%ebx)
+ psrlq $32,%mm1
+ movd %mm1,120(%ebx)
+ movq 8(%edi),%mm4
+ movq %mm4,%mm5
+ movd 116(%edi),%mm6
+ punpckldq 112(%edi),%mm6
+ movq 8(%eax),%mm7
+ pfadd %mm6,%mm4
+ movq %mm4,8(%ebx)
+ pfsub %mm6,%mm5
+ pfmul %mm7,%mm5
+ movd %mm5,116(%ebx)
+ psrlq $32,%mm5
+ movd %mm5,112(%ebx)
+ movq 16(%edi),%mm0
+ movq %mm0,%mm1
+ movd 108(%edi),%mm2
+ punpckldq 104(%edi),%mm2
+ movq 16(%eax),%mm3
+ pfadd %mm2,%mm0
+ movq %mm0,16(%ebx)
+ pfsub %mm2,%mm1
+ pfmul %mm3,%mm1
+ movd %mm1,108(%ebx)
+ psrlq $32,%mm1
+ movd %mm1,104(%ebx)
+ movq 24(%edi),%mm4
+ movq %mm4,%mm5
+ movd 100(%edi),%mm6
+ punpckldq 96(%edi),%mm6
+ movq 24(%eax),%mm7
+ pfadd %mm6,%mm4
+ movq %mm4,24(%ebx)
+ pfsub %mm6,%mm5
+ pfmul %mm7,%mm5
+ movd %mm5,100(%ebx)
+ psrlq $32,%mm5
+ movd %mm5,96(%ebx)
+ movq 32(%edi),%mm0
+ movq %mm0,%mm1
+ movd 92(%edi),%mm2
+ punpckldq 88(%edi),%mm2
+ movq 32(%eax),%mm3
+ pfadd %mm2,%mm0
+ movq %mm0,32(%ebx)
+ pfsub %mm2,%mm1
+ pfmul %mm3,%mm1
+ movd %mm1,92(%ebx)
+ psrlq $32,%mm1
+ movd %mm1,88(%ebx)
+ movq 40(%edi),%mm4
+ movq %mm4,%mm5
+ movd 84(%edi),%mm6
+ punpckldq 80(%edi),%mm6
+ movq 40(%eax),%mm7
+ pfadd %mm6,%mm4
+ movq %mm4,40(%ebx)
+ pfsub %mm6,%mm5
+ pfmul %mm7,%mm5
+ movd %mm5,84(%ebx)
+ psrlq $32,%mm5
+ movd %mm5,80(%ebx)
+ movq 48(%edi),%mm0
+ movq %mm0,%mm1
+ movd 76(%edi),%mm2
+ punpckldq 72(%edi),%mm2
+ movq 48(%eax),%mm3
+ pfadd %mm2,%mm0
+ movq %mm0,48(%ebx)
+ pfsub %mm2,%mm1
+ pfmul %mm3,%mm1
+ movd %mm1,76(%ebx)
+ psrlq $32,%mm1
+ movd %mm1,72(%ebx)
+ movq 56(%edi),%mm4
+ movq %mm4,%mm5
+ movd 68(%edi),%mm6
+ punpckldq 64(%edi),%mm6
+ movq 56(%eax),%mm7
+ pfadd %mm6,%mm4
+ movq %mm4,56(%ebx)
+ pfsub %mm6,%mm5
+ pfmul %mm7,%mm5
+ movd %mm5,68(%ebx)
+ psrlq $32,%mm5
+ movd %mm5,64(%ebx)
+
+ ## 2
+ movl pnts+4,%eax
+ ## 0, 14
+ movq 0(%ebx),%mm0
+ movq %mm0,%mm1
+ movd 60(%ebx),%mm2
+ punpckldq 56(%ebx),%mm2
+ movq 0(%eax),%mm3
+ pfadd %mm2,%mm0
+ movq %mm0,0(%esi)
+ pfsub %mm2,%mm1
+ pfmul %mm3,%mm1
+ movd %mm1,60(%esi)
+ psrlq $32,%mm1
+ movd %mm1,56(%esi)
+ ## 16, 30
+ movq 64(%ebx),%mm0
+ movq %mm0,%mm1
+ movd 124(%ebx),%mm2
+ punpckldq 120(%ebx),%mm2
+ pfadd %mm2,%mm0
+ movq %mm0,64(%esi)
+ pfsubr %mm2,%mm1
+ pfmul %mm3,%mm1
+ movd %mm1,124(%esi)
+ psrlq $32,%mm1
+ movd %mm1,120(%esi)
+ ## 2, 12
+ movq 8(%ebx),%mm4
+ movq %mm4,%mm5
+ movd 52(%ebx),%mm6
+ punpckldq 48(%ebx),%mm6
+ movq 8(%eax),%mm7
+ pfadd %mm6,%mm4
+ movq %mm4,8(%esi)
+ pfsub %mm6,%mm5
+ pfmul %mm7,%mm5
+ movd %mm5,52(%esi)
+ psrlq $32,%mm5
+ movd %mm5,48(%esi)
+ ## 18, 28
+ movq 72(%ebx),%mm4
+ movq %mm4,%mm5
+ movd 116(%ebx),%mm6
+ punpckldq 112(%ebx),%mm6
+ pfadd %mm6,%mm4
+ movq %mm4,72(%esi)
+ pfsubr %mm6,%mm5
+ pfmul %mm7,%mm5
+ movd %mm5,116(%esi)
+ psrlq $32,%mm5
+ movd %mm5,112(%esi)
+ ## 4, 10
+ movq 16(%ebx),%mm0
+ movq %mm0,%mm1
+ movd 44(%ebx),%mm2
+ punpckldq 40(%ebx),%mm2
+ movq 16(%eax),%mm3
+ pfadd %mm2,%mm0
+ movq %mm0,16(%esi)
+ pfsub %mm2,%mm1
+ pfmul %mm3,%mm1
+ movd %mm1,44(%esi)
+ psrlq $32,%mm1
+ movd %mm1,40(%esi)
+ ## 20, 26
+ movq 80(%ebx),%mm0
+ movq %mm0,%mm1
+ movd 108(%ebx),%mm2
+ punpckldq 104(%ebx),%mm2
+ pfadd %mm2,%mm0
+ movq %mm0,80(%esi)
+ pfsubr %mm2,%mm1
+ pfmul %mm3,%mm1
+ movd %mm1,108(%esi)
+ psrlq $32,%mm1
+ movd %mm1,104(%esi)
+ ## 6, 8
+ movq 24(%ebx),%mm4
+ movq %mm4,%mm5
+ movd 36(%ebx),%mm6
+ punpckldq 32(%ebx),%mm6
+ movq 24(%eax),%mm7
+ pfadd %mm6,%mm4
+ movq %mm4,24(%esi)
+ pfsub %mm6,%mm5
+ pfmul %mm7,%mm5
+ movd %mm5,36(%esi)
+ psrlq $32,%mm5
+ movd %mm5,32(%esi)
+ ## 22, 24
+ movq 88(%ebx),%mm4
+ movq %mm4,%mm5
+ movd 100(%ebx),%mm6
+ punpckldq 96(%ebx),%mm6
+ pfadd %mm6,%mm4
+ movq %mm4,88(%esi)
+ pfsubr %mm6,%mm5
+ pfmul %mm7,%mm5
+ movd %mm5,100(%esi)
+ psrlq $32,%mm5
+ movd %mm5,96(%esi)
+
+ ## 3
+ movl pnts+8,%eax
+ movq 0(%eax),%mm0
+ movq 8(%eax),%mm1
+ ## 0, 6
+ movq 0(%esi),%mm2
+ movq %mm2,%mm3
+ movd 28(%esi),%mm4
+ punpckldq 24(%esi),%mm4
+ pfadd %mm4,%mm2
+ pfsub %mm4,%mm3
+ pfmul %mm0,%mm3
+ movq %mm2,0(%ebx)
+ movd %mm3,28(%ebx)
+ psrlq $32,%mm3
+ movd %mm3,24(%ebx)
+ ## 2, 4
+ movq 8(%esi),%mm5
+ movq %mm5,%mm6
+ movd 20(%esi),%mm7
+ punpckldq 16(%esi),%mm7
+ pfadd %mm7,%mm5
+ pfsub %mm7,%mm6
+ pfmul %mm1,%mm6
+ movq %mm5,8(%ebx)
+ movd %mm6,20(%ebx)
+ psrlq $32,%mm6
+ movd %mm6,16(%ebx)
+ ## 8, 14
+ movq 32(%esi),%mm2
+ movq %mm2,%mm3
+ movd 60(%esi),%mm4
+ punpckldq 56(%esi),%mm4
+ pfadd %mm4,%mm2
+ pfsubr %mm4,%mm3
+ pfmul %mm0,%mm3
+ movq %mm2,32(%ebx)
+ movd %mm3,60(%ebx)
+ psrlq $32,%mm3
+ movd %mm3,56(%ebx)
+ ## 10, 12
+ movq 40(%esi),%mm5
+ movq %mm5,%mm6
+ movd 52(%esi),%mm7
+ punpckldq 48(%esi),%mm7
+ pfadd %mm7,%mm5
+ pfsubr %mm7,%mm6
+ pfmul %mm1,%mm6
+ movq %mm5,40(%ebx)
+ movd %mm6,52(%ebx)
+ psrlq $32,%mm6
+ movd %mm6,48(%ebx)
+ ## 16, 22
+ movq 64(%esi),%mm2
+ movq %mm2,%mm3
+ movd 92(%esi),%mm4
+ punpckldq 88(%esi),%mm4
+ pfadd %mm4,%mm2
+ pfsub %mm4,%mm3
+ pfmul %mm0,%mm3
+ movq %mm2,64(%ebx)
+ movd %mm3,92(%ebx)
+ psrlq $32,%mm3
+ movd %mm3,88(%ebx)
+ ## 18, 20
+ movq 72(%esi),%mm5
+ movq %mm5,%mm6
+ movd 84(%esi),%mm7
+ punpckldq 80(%esi),%mm7
+ pfadd %mm7,%mm5
+ pfsub %mm7,%mm6
+ pfmul %mm1,%mm6
+ movq %mm5,72(%ebx)
+ movd %mm6,84(%ebx)
+ psrlq $32,%mm6
+ movd %mm6,80(%ebx)
+ ## 24, 30
+ movq 96(%esi),%mm2
+ movq %mm2,%mm3
+ movd 124(%esi),%mm4
+ punpckldq 120(%esi),%mm4
+ pfadd %mm4,%mm2
+ pfsubr %mm4,%mm3
+ pfmul %mm0,%mm3
+ movq %mm2,96(%ebx)
+ movd %mm3,124(%ebx)
+ psrlq $32,%mm3
+ movd %mm3,120(%ebx)
+ ## 26, 28
+ movq 104(%esi),%mm5
+ movq %mm5,%mm6
+ movd 116(%esi),%mm7
+ punpckldq 112(%esi),%mm7
+ pfadd %mm7,%mm5
+ pfsubr %mm7,%mm6
+ pfmul %mm1,%mm6
+ movq %mm5,104(%ebx)
+ movd %mm6,116(%ebx)
+ psrlq $32,%mm6
+ movd %mm6,112(%ebx)
+
+ ## 4
+ movl pnts+12,%eax
+ movq 0(%eax),%mm0
+ ## 0
+ movq 0(%ebx),%mm1
+ movq %mm1,%mm2
+ movd 12(%ebx),%mm3
+ punpckldq 8(%ebx),%mm3
+ pfadd %mm3,%mm1
+ pfsub %mm3,%mm2
+ pfmul %mm0,%mm2
+ movq %mm1,0(%esi)
+ movd %mm2,12(%esi)
+ psrlq $32,%mm2
+ movd %mm2,8(%esi)
+ ## 4
+ movq 16(%ebx),%mm4
+ movq %mm4,%mm5
+ movd 28(%ebx),%mm6
+ punpckldq 24(%ebx),%mm6
+ pfadd %mm6,%mm4
+ pfsubr %mm6,%mm5
+ pfmul %mm0,%mm5
+ movq %mm4,16(%esi)
+ movd %mm5,28(%esi)
+ psrlq $32,%mm5
+ movd %mm5,24(%esi)
+ ## 8
+ movq 32(%ebx),%mm1
+ movq %mm1,%mm2
+ movd 44(%ebx),%mm3
+ punpckldq 40(%ebx),%mm3
+ pfadd %mm3,%mm1
+ pfsub %mm3,%mm2
+ pfmul %mm0,%mm2
+ movq %mm1,32(%esi)
+ movd %mm2,44(%esi)
+ psrlq $32,%mm2
+ movd %mm2,40(%esi)
+ ## 12
+ movq 48(%ebx),%mm4
+ movq %mm4,%mm5
+ movd 60(%ebx),%mm6
+ punpckldq 56(%ebx),%mm6
+ pfadd %mm6,%mm4
+ pfsubr %mm6,%mm5
+ pfmul %mm0,%mm5
+ movq %mm4,48(%esi)
+ movd %mm5,60(%esi)
+ psrlq $32,%mm5
+ movd %mm5,56(%esi)
+ ## 16
+ movq 64(%ebx),%mm1
+ movq %mm1,%mm2
+ movd 76(%ebx),%mm3
+ punpckldq 72(%ebx),%mm3
+ pfadd %mm3,%mm1
+ pfsub %mm3,%mm2
+ pfmul %mm0,%mm2
+ movq %mm1,64(%esi)
+ movd %mm2,76(%esi)
+ psrlq $32,%mm2
+ movd %mm2,72(%esi)
+ ## 20
+ movq 80(%ebx),%mm4
+ movq %mm4,%mm5
+ movd 92(%ebx),%mm6
+ punpckldq 88(%ebx),%mm6
+ pfadd %mm6,%mm4
+ pfsubr %mm6,%mm5
+ pfmul %mm0,%mm5
+ movq %mm4,80(%esi)
+ movd %mm5,92(%esi)
+ psrlq $32,%mm5
+ movd %mm5,88(%esi)
+ ## 24
+ movq 96(%ebx),%mm1
+ movq %mm1,%mm2
+ movd 108(%ebx),%mm3
+ punpckldq 104(%ebx),%mm3
+ pfadd %mm3,%mm1
+ pfsub %mm3,%mm2
+ pfmul %mm0,%mm2
+ movq %mm1,96(%esi)
+ movd %mm2,108(%esi)
+ psrlq $32,%mm2
+ movd %mm2,104(%esi)
+ ## 28
+ movq 112(%ebx),%mm4
+ movq %mm4,%mm5
+ movd 124(%ebx),%mm6
+ punpckldq 120(%ebx),%mm6
+ pfadd %mm6,%mm4
+ pfsubr %mm6,%mm5
+ pfmul %mm0,%mm5
+ movq %mm4,112(%esi)
+ movd %mm5,124(%esi)
+ psrlq $32,%mm5
+ movd %mm5,120(%esi)
+
+ ## 5
+ movl $-1,%eax
+ movd %eax,%mm1
+ movl $1,%eax
+ ## L | H
+ movd %eax,%mm0
+ punpckldq %mm1,%mm0
+ ## 1.0 | -1.0
+ pi2fd %mm0,%mm0
+ movd %eax,%mm1
+ pi2fd %mm1,%mm1
+ movl pnts+16,%eax
+ movd 0(%eax),%mm2
+ ## 1.0 | cos0
+ punpckldq %mm2,%mm1
+ ## 0
+ movq 0(%esi),%mm2
+ movq %mm2,%mm3
+ pfmul %mm0,%mm3
+ pfacc %mm3,%mm2
+ pfmul %mm1,%mm2
+ movq %mm2,0(%ebx)
+ movq 8(%esi),%mm4
+ movq %mm4,%mm5
+ pfmul %mm0,%mm5
+ pfacc %mm5,%mm4
+ pfmul %mm0,%mm4
+ pfmul %mm1,%mm4
+ movq %mm4,%mm5
+ psrlq $32,%mm5
+ pfacc %mm5,%mm4
+ movq %mm4,8(%ebx)
+ ## 4
+ movq 16(%esi),%mm2
+ movq %mm2,%mm3
+ pfmul %mm0,%mm3
+ pfacc %mm3,%mm2
+ pfmul %mm1,%mm2
+ movq 24(%esi),%mm4
+ movq %mm4,%mm5
+ pfmul %mm0,%mm5
+ pfacc %mm5,%mm4
+ pfmul %mm0,%mm4
+ pfmul %mm1,%mm4
+ movq %mm4,%mm5
+ psrlq $32,%mm5
+ pfacc %mm5,%mm4
+ movq %mm2,%mm3
+ psrlq $32,%mm3
+ pfadd %mm4,%mm2
+ pfadd %mm3,%mm4
+ movq %mm2,16(%ebx)
+ movq %mm4,24(%ebx)
+ ## 8
+ movq 32(%esi),%mm2
+ movq %mm2,%mm3
+ pfmul %mm0,%mm3
+ pfacc %mm3,%mm2
+ pfmul %mm1,%mm2
+ movq %mm2,32(%ebx)
+ movq 40(%esi),%mm4
+ movq %mm4,%mm5
+ pfmul %mm0,%mm5
+ pfacc %mm5,%mm4
+ pfmul %mm0,%mm4
+ pfmul %mm1,%mm4
+ movq %mm4,%mm5
+ psrlq $32,%mm5
+ pfacc %mm5,%mm4
+ movq %mm4,40(%ebx)
+ ## 12
+ movq 48(%esi),%mm2
+ movq %mm2,%mm3
+ pfmul %mm0,%mm3
+ pfacc %mm3,%mm2
+ pfmul %mm1,%mm2
+ movq 56(%esi),%mm4
+ movq %mm4,%mm5
+ pfmul %mm0,%mm5
+ pfacc %mm5,%mm4
+ pfmul %mm0,%mm4
+ pfmul %mm1,%mm4
+ movq %mm4,%mm5
+ psrlq $32,%mm5
+ pfacc %mm5,%mm4
+ movq %mm2,%mm3
+ psrlq $32,%mm3
+ pfadd %mm4,%mm2
+ pfadd %mm3,%mm4
+ movq %mm2,48(%ebx)
+ movq %mm4,56(%ebx)
+ ## 16
+ movq 64(%esi),%mm2
+ movq %mm2,%mm3
+ pfmul %mm0,%mm3
+ pfacc %mm3,%mm2
+ pfmul %mm1,%mm2
+ movq %mm2,64(%ebx)
+ movq 72(%esi),%mm4
+ movq %mm4,%mm5
+ pfmul %mm0,%mm5
+ pfacc %mm5,%mm4
+ pfmul %mm0,%mm4
+ pfmul %mm1,%mm4
+ movq %mm4,%mm5
+ psrlq $32,%mm5
+ pfacc %mm5,%mm4
+ movq %mm4,72(%ebx)
+ ## 20
+ movq 80(%esi),%mm2
+ movq %mm2,%mm3
+ pfmul %mm0,%mm3
+ pfacc %mm3,%mm2
+ pfmul %mm1,%mm2
+ movq 88(%esi),%mm4
+ movq %mm4,%mm5
+ pfmul %mm0,%mm5
+ pfacc %mm5,%mm4
+ pfmul %mm0,%mm4
+ pfmul %mm1,%mm4
+ movq %mm4,%mm5
+ psrlq $32,%mm5
+ pfacc %mm5,%mm4
+ movq %mm2,%mm3
+ psrlq $32,%mm3
+ pfadd %mm4,%mm2
+ pfadd %mm3,%mm4
+ movq %mm2,80(%ebx)
+ movq %mm4,88(%ebx)
+ ## 24
+ movq 96(%esi),%mm2
+ movq %mm2,%mm3
+ pfmul %mm0,%mm3
+ pfacc %mm3,%mm2
+ pfmul %mm1,%mm2
+ movq %mm2,96(%ebx)
+ movq 104(%esi),%mm4
+ movq %mm4,%mm5
+ pfmul %mm0,%mm5
+ pfacc %mm5,%mm4
+ pfmul %mm0,%mm4
+ pfmul %mm1,%mm4
+ movq %mm4,%mm5
+ psrlq $32,%mm5
+ pfacc %mm5,%mm4
+ movq %mm4,104(%ebx)
+ ## 28
+ movq 112(%esi),%mm2
+ movq %mm2,%mm3
+ pfmul %mm0,%mm3
+ pfacc %mm3,%mm2
+ pfmul %mm1,%mm2
+ movq 120(%esi),%mm4
+ movq %mm4,%mm5
+ pfmul %mm0,%mm5
+ pfacc %mm5,%mm4
+ pfmul %mm0,%mm4
+ pfmul %mm1,%mm4
+ movq %mm4,%mm5
+ psrlq $32,%mm5
+ pfacc %mm5,%mm4
+ movq %mm2,%mm3
+ psrlq $32,%mm3
+ pfadd %mm4,%mm2
+ pfadd %mm3,%mm4
+ movq %mm2,112(%ebx)
+ movq %mm4,120(%ebx)
+
+ ## Phase6
+ movl 0(%ebx),%eax
+ movl %eax,1024(%ebp)
+ movl 4(%ebx),%eax
+ movl %eax,0(%ebp)
+ movl %eax,0(%edx)
+ movl 8(%ebx),%eax
+ movl %eax,512(%ebp)
+ movl 12(%ebx),%eax
+ movl %eax,512(%edx)
+
+ movl 16(%ebx),%eax
+ movl %eax,768(%ebp)
+ movl 20(%ebx),%eax
+ movl %eax,256(%edx)
+
+ movl 24(%ebx),%eax
+ movl %eax,256(%ebp)
+ movl 28(%ebx),%eax
+ movl %eax,768(%edx)
+
+ movq 32(%ebx),%mm0
+ movq 48(%ebx),%mm1
+ pfadd %mm1,%mm0
+ movd %mm0,896(%ebp)
+ psrlq $32,%mm0
+ movd %mm0,128(%edx)
+ movq 40(%ebx),%mm2
+ pfadd %mm2,%mm1
+ movd %mm1,640(%ebp)
+ psrlq $32,%mm1
+ movd %mm1,384(%edx)
+
+ movq 56(%ebx),%mm3
+ pfadd %mm3,%mm2
+ movd %mm2,384(%ebp)
+ psrlq $32,%mm2
+ movd %mm2,640(%edx)
+
+ movd 36(%ebx),%mm4
+ pfadd %mm4,%mm3
+ movd %mm3,128(%ebp)
+ psrlq $32,%mm3
+ movd %mm3,896(%edx)
+ movq 96(%ebx),%mm0
+ movq 64(%ebx),%mm1
+
+ movq 112(%ebx),%mm2
+ pfadd %mm2,%mm0
+ movq %mm0,%mm3
+ pfadd %mm1,%mm3
+ movd %mm3,960(%ebp)
+ psrlq $32,%mm3
+ movd %mm3,64(%edx)
+ movq 80(%ebx),%mm1
+ pfadd %mm1,%mm0
+ movd %mm0,832(%ebp)
+ psrlq $32,%mm0
+ movd %mm0,192(%edx)
+ movq 104(%ebx),%mm3
+ pfadd %mm3,%mm2
+ movq %mm2,%mm4
+ pfadd %mm1,%mm4
+ movd %mm4,704(%ebp)
+ psrlq $32,%mm4
+ movd %mm4,320(%edx)
+ movq 72(%ebx),%mm1
+ pfadd %mm1,%mm2
+ movd %mm2,576(%ebp)
+ psrlq $32,%mm2
+ movd %mm2,448(%edx)
+
+ movq 120(%ebx),%mm4
+ pfadd %mm4,%mm3
+ movq %mm3,%mm5
+ pfadd %mm1,%mm5
+ movd %mm5,448(%ebp)
+ psrlq $32,%mm5
+ movd %mm5,576(%edx)
+ movq 88(%ebx),%mm1
+ pfadd %mm1,%mm3
+ movd %mm3,320(%ebp)
+ psrlq $32,%mm3
+ movd %mm3,704(%edx)
+
+ movd 100(%ebx),%mm5
+ pfadd %mm5,%mm4
+ movq %mm4,%mm6
+ pfadd %mm1,%mm6
+ movd %mm6,192(%ebp)
+ psrlq $32,%mm6
+ movd %mm6,832(%edx)
+ movd 68(%ebx),%mm1
+ pfadd %mm1,%mm4
+ movd %mm4,64(%ebp)
+ psrlq $32,%mm4
+ movd %mm4,960(%edx)
+
+ # femms
+
+ popl %ebx
+ popl %esi
+ popl %edi
+ popl %ebp
+ addl $256,%esp
+
+ ret
+
diff --git a/mpg123_artsplugin/mpg123/dct64_MMX.s b/mpg123_artsplugin/mpg123/dct64_MMX.s
new file mode 100644
index 00000000..965f4c6a
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/dct64_MMX.s
@@ -0,0 +1,836 @@
+.data
+ .align 32
+costab:
+ .long 1056974725
+ .long 1057056395
+ .long 1057223771
+ .long 1057485416
+ .long 1057855544
+ .long 1058356026
+ .long 1059019886
+ .long 1059897405
+ .long 1061067246
+ .long 1062657950
+ .long 1064892987
+ .long 1066774581
+ .long 1069414683
+ .long 1073984175
+ .long 1079645762
+ .long 1092815430
+ .long 1057005197
+ .long 1057342072
+ .long 1058087743
+ .long 1059427869
+ .long 1061799040
+ .long 1065862217
+ .long 1071413542
+ .long 1084439708
+ .long 1057128951
+ .long 1058664893
+ .long 1063675095
+ .long 1076102863
+ .long 1057655764
+ .long 1067924853
+ .long 1060439283
+
+.text
+
+ .align 32
+.globl dct64
+dct64:
+
+ xorl %ecx,%ecx
+.globl dct64_MMX
+dct64_MMX:
+ pushl %ebx
+ pushl %esi
+ pushl %edi
+ subl $256,%esp
+ movl 280(%esp),%eax
+ flds (%eax)
+ leal 128(%esp),%edx
+ fadds 124(%eax)
+ movl 272(%esp),%esi
+ fstps (%edx)
+ movl 276(%esp),%edi
+ flds 4(%eax)
+ movl $costab,%ebx
+ fadds 120(%eax)
+ orl %ecx,%ecx
+ fstps 4(%edx)
+ flds (%eax)
+ movl %esp,%ecx
+ fsubs 124(%eax)
+ fmuls (%ebx)
+ fstps 124(%edx)
+ flds 4(%eax)
+ fsubs 120(%eax)
+ fmuls 4(%ebx)
+ fstps 120(%edx)
+ flds 8(%eax)
+ fadds 116(%eax)
+ fstps 8(%edx)
+ flds 12(%eax)
+ fadds 112(%eax)
+ fstps 12(%edx)
+ flds 8(%eax)
+ fsubs 116(%eax)
+ fmuls 8(%ebx)
+ fstps 116(%edx)
+ flds 12(%eax)
+ fsubs 112(%eax)
+ fmuls 12(%ebx)
+ fstps 112(%edx)
+ flds 16(%eax)
+ fadds 108(%eax)
+ fstps 16(%edx)
+ flds 20(%eax)
+ fadds 104(%eax)
+ fstps 20(%edx)
+ flds 16(%eax)
+ fsubs 108(%eax)
+ fmuls 16(%ebx)
+ fstps 108(%edx)
+ flds 20(%eax)
+ fsubs 104(%eax)
+ fmuls 20(%ebx)
+ fstps 104(%edx)
+ flds 24(%eax)
+ fadds 100(%eax)
+ fstps 24(%edx)
+ flds 28(%eax)
+ fadds 96(%eax)
+ fstps 28(%edx)
+ flds 24(%eax)
+ fsubs 100(%eax)
+ fmuls 24(%ebx)
+ fstps 100(%edx)
+ flds 28(%eax)
+ fsubs 96(%eax)
+ fmuls 28(%ebx)
+ fstps 96(%edx)
+ flds 32(%eax)
+ fadds 92(%eax)
+ fstps 32(%edx)
+ flds 36(%eax)
+ fadds 88(%eax)
+ fstps 36(%edx)
+ flds 32(%eax)
+ fsubs 92(%eax)
+ fmuls 32(%ebx)
+ fstps 92(%edx)
+ flds 36(%eax)
+ fsubs 88(%eax)
+ fmuls 36(%ebx)
+ fstps 88(%edx)
+ flds 40(%eax)
+ fadds 84(%eax)
+ fstps 40(%edx)
+ flds 44(%eax)
+ fadds 80(%eax)
+ fstps 44(%edx)
+ flds 40(%eax)
+ fsubs 84(%eax)
+ fmuls 40(%ebx)
+ fstps 84(%edx)
+ flds 44(%eax)
+ fsubs 80(%eax)
+ fmuls 44(%ebx)
+ fstps 80(%edx)
+ flds 48(%eax)
+ fadds 76(%eax)
+ fstps 48(%edx)
+ flds 52(%eax)
+ fadds 72(%eax)
+ fstps 52(%edx)
+ flds 48(%eax)
+ fsubs 76(%eax)
+ fmuls 48(%ebx)
+ fstps 76(%edx)
+ flds 52(%eax)
+ fsubs 72(%eax)
+ fmuls 52(%ebx)
+ fstps 72(%edx)
+ flds 56(%eax)
+ fadds 68(%eax)
+ fstps 56(%edx)
+ flds 60(%eax)
+ fadds 64(%eax)
+ fstps 60(%edx)
+ flds 56(%eax)
+ fsubs 68(%eax)
+ fmuls 56(%ebx)
+ fstps 68(%edx)
+ flds 60(%eax)
+ fsubs 64(%eax)
+ fmuls 60(%ebx)
+ fstps 64(%edx)
+
+ flds (%edx)
+ fadds 60(%edx)
+ fstps (%ecx)
+ flds 4(%edx)
+ fadds 56(%edx)
+ fstps 4(%ecx)
+ flds (%edx)
+ fsubs 60(%edx)
+ fmuls 64(%ebx)
+ fstps 60(%ecx)
+ flds 4(%edx)
+ fsubs 56(%edx)
+ fmuls 68(%ebx)
+ fstps 56(%ecx)
+ flds 8(%edx)
+ fadds 52(%edx)
+ fstps 8(%ecx)
+ flds 12(%edx)
+ fadds 48(%edx)
+ fstps 12(%ecx)
+ flds 8(%edx)
+ fsubs 52(%edx)
+ fmuls 72(%ebx)
+ fstps 52(%ecx)
+ flds 12(%edx)
+ fsubs 48(%edx)
+ fmuls 76(%ebx)
+ fstps 48(%ecx)
+ flds 16(%edx)
+ fadds 44(%edx)
+ fstps 16(%ecx)
+ flds 20(%edx)
+ fadds 40(%edx)
+ fstps 20(%ecx)
+ flds 16(%edx)
+ fsubs 44(%edx)
+ fmuls 80(%ebx)
+ fstps 44(%ecx)
+ flds 20(%edx)
+ fsubs 40(%edx)
+ fmuls 84(%ebx)
+ fstps 40(%ecx)
+ flds 24(%edx)
+ fadds 36(%edx)
+ fstps 24(%ecx)
+ flds 28(%edx)
+ fadds 32(%edx)
+ fstps 28(%ecx)
+ flds 24(%edx)
+ fsubs 36(%edx)
+ fmuls 88(%ebx)
+ fstps 36(%ecx)
+ flds 28(%edx)
+ fsubs 32(%edx)
+ fmuls 92(%ebx)
+ fstps 32(%ecx)
+
+ flds 64(%edx)
+ fadds 124(%edx)
+ fstps 64(%ecx)
+ flds 68(%edx)
+ fadds 120(%edx)
+ fstps 68(%ecx)
+ flds 124(%edx)
+ fsubs 64(%edx)
+ fmuls 64(%ebx)
+ fstps 124(%ecx)
+ flds 120(%edx)
+ fsubs 68(%edx)
+ fmuls 68(%ebx)
+ fstps 120(%ecx)
+ flds 72(%edx)
+ fadds 116(%edx)
+ fstps 72(%ecx)
+ flds 76(%edx)
+ fadds 112(%edx)
+ fstps 76(%ecx)
+ flds 116(%edx)
+ fsubs 72(%edx)
+ fmuls 72(%ebx)
+ fstps 116(%ecx)
+ flds 112(%edx)
+ fsubs 76(%edx)
+ fmuls 76(%ebx)
+ fstps 112(%ecx)
+ flds 80(%edx)
+ fadds 108(%edx)
+ fstps 80(%ecx)
+ flds 84(%edx)
+ fadds 104(%edx)
+ fstps 84(%ecx)
+ flds 108(%edx)
+ fsubs 80(%edx)
+ fmuls 80(%ebx)
+ fstps 108(%ecx)
+ flds 104(%edx)
+ fsubs 84(%edx)
+ fmuls 84(%ebx)
+ fstps 104(%ecx)
+ flds 88(%edx)
+ fadds 100(%edx)
+ fstps 88(%ecx)
+ flds 92(%edx)
+ fadds 96(%edx)
+ fstps 92(%ecx)
+ flds 100(%edx)
+ fsubs 88(%edx)
+ fmuls 88(%ebx)
+ fstps 100(%ecx)
+ flds 96(%edx)
+ fsubs 92(%edx)
+ fmuls 92(%ebx)
+ fstps 96(%ecx)
+
+ flds (%ecx)
+ fadds 28(%ecx)
+ fstps (%edx)
+ flds (%ecx)
+ fsubs 28(%ecx)
+ fmuls 96(%ebx)
+ fstps 28(%edx)
+ flds 4(%ecx)
+ fadds 24(%ecx)
+ fstps 4(%edx)
+ flds 4(%ecx)
+ fsubs 24(%ecx)
+ fmuls 100(%ebx)
+ fstps 24(%edx)
+ flds 8(%ecx)
+ fadds 20(%ecx)
+ fstps 8(%edx)
+ flds 8(%ecx)
+ fsubs 20(%ecx)
+ fmuls 104(%ebx)
+ fstps 20(%edx)
+ flds 12(%ecx)
+ fadds 16(%ecx)
+ fstps 12(%edx)
+ flds 12(%ecx)
+ fsubs 16(%ecx)
+ fmuls 108(%ebx)
+ fstps 16(%edx)
+ flds 32(%ecx)
+ fadds 60(%ecx)
+ fstps 32(%edx)
+ flds 60(%ecx)
+ fsubs 32(%ecx)
+ fmuls 96(%ebx)
+ fstps 60(%edx)
+ flds 36(%ecx)
+ fadds 56(%ecx)
+ fstps 36(%edx)
+ flds 56(%ecx)
+ fsubs 36(%ecx)
+ fmuls 100(%ebx)
+ fstps 56(%edx)
+ flds 40(%ecx)
+ fadds 52(%ecx)
+ fstps 40(%edx)
+ flds 52(%ecx)
+ fsubs 40(%ecx)
+ fmuls 104(%ebx)
+ fstps 52(%edx)
+ flds 44(%ecx)
+ fadds 48(%ecx)
+ fstps 44(%edx)
+ flds 48(%ecx)
+ fsubs 44(%ecx)
+ fmuls 108(%ebx)
+ fstps 48(%edx)
+ flds 64(%ecx)
+ fadds 92(%ecx)
+ fstps 64(%edx)
+ flds 64(%ecx)
+ fsubs 92(%ecx)
+ fmuls 96(%ebx)
+ fstps 92(%edx)
+ flds 68(%ecx)
+ fadds 88(%ecx)
+ fstps 68(%edx)
+ flds 68(%ecx)
+ fsubs 88(%ecx)
+ fmuls 100(%ebx)
+ fstps 88(%edx)
+ flds 72(%ecx)
+ fadds 84(%ecx)
+ fstps 72(%edx)
+ flds 72(%ecx)
+ fsubs 84(%ecx)
+ fmuls 104(%ebx)
+ fstps 84(%edx)
+ flds 76(%ecx)
+ fadds 80(%ecx)
+ fstps 76(%edx)
+ flds 76(%ecx)
+ fsubs 80(%ecx)
+ fmuls 108(%ebx)
+ fstps 80(%edx)
+ flds 96(%ecx)
+ fadds 124(%ecx)
+ fstps 96(%edx)
+ flds 124(%ecx)
+ fsubs 96(%ecx)
+ fmuls 96(%ebx)
+ fstps 124(%edx)
+ flds 100(%ecx)
+ fadds 120(%ecx)
+ fstps 100(%edx)
+ flds 120(%ecx)
+ fsubs 100(%ecx)
+ fmuls 100(%ebx)
+ fstps 120(%edx)
+ flds 104(%ecx)
+ fadds 116(%ecx)
+ fstps 104(%edx)
+ flds 116(%ecx)
+ fsubs 104(%ecx)
+ fmuls 104(%ebx)
+ fstps 116(%edx)
+ flds 108(%ecx)
+ fadds 112(%ecx)
+ fstps 108(%edx)
+ flds 112(%ecx)
+ fsubs 108(%ecx)
+ fmuls 108(%ebx)
+ fstps 112(%edx)
+ flds (%edx)
+ fadds 12(%edx)
+ fstps (%ecx)
+ flds (%edx)
+ fsubs 12(%edx)
+ fmuls 112(%ebx)
+ fstps 12(%ecx)
+ flds 4(%edx)
+ fadds 8(%edx)
+ fstps 4(%ecx)
+ flds 4(%edx)
+ fsubs 8(%edx)
+ fmuls 116(%ebx)
+ fstps 8(%ecx)
+ flds 16(%edx)
+ fadds 28(%edx)
+ fstps 16(%ecx)
+ flds 28(%edx)
+ fsubs 16(%edx)
+ fmuls 112(%ebx)
+ fstps 28(%ecx)
+ flds 20(%edx)
+ fadds 24(%edx)
+ fstps 20(%ecx)
+ flds 24(%edx)
+ fsubs 20(%edx)
+ fmuls 116(%ebx)
+ fstps 24(%ecx)
+ flds 32(%edx)
+ fadds 44(%edx)
+ fstps 32(%ecx)
+ flds 32(%edx)
+ fsubs 44(%edx)
+ fmuls 112(%ebx)
+ fstps 44(%ecx)
+ flds 36(%edx)
+ fadds 40(%edx)
+ fstps 36(%ecx)
+ flds 36(%edx)
+ fsubs 40(%edx)
+ fmuls 116(%ebx)
+ fstps 40(%ecx)
+ flds 48(%edx)
+ fadds 60(%edx)
+ fstps 48(%ecx)
+ flds 60(%edx)
+ fsubs 48(%edx)
+ fmuls 112(%ebx)
+ fstps 60(%ecx)
+ flds 52(%edx)
+ fadds 56(%edx)
+ fstps 52(%ecx)
+ flds 56(%edx)
+ fsubs 52(%edx)
+ fmuls 116(%ebx)
+ fstps 56(%ecx)
+ flds 64(%edx)
+ fadds 76(%edx)
+ fstps 64(%ecx)
+ flds 64(%edx)
+ fsubs 76(%edx)
+ fmuls 112(%ebx)
+ fstps 76(%ecx)
+ flds 68(%edx)
+ fadds 72(%edx)
+ fstps 68(%ecx)
+ flds 68(%edx)
+ fsubs 72(%edx)
+ fmuls 116(%ebx)
+ fstps 72(%ecx)
+ flds 80(%edx)
+ fadds 92(%edx)
+ fstps 80(%ecx)
+ flds 92(%edx)
+ fsubs 80(%edx)
+ fmuls 112(%ebx)
+ fstps 92(%ecx)
+ flds 84(%edx)
+ fadds 88(%edx)
+ fstps 84(%ecx)
+ flds 88(%edx)
+ fsubs 84(%edx)
+ fmuls 116(%ebx)
+ fstps 88(%ecx)
+ flds 96(%edx)
+ fadds 108(%edx)
+ fstps 96(%ecx)
+ flds 96(%edx)
+ fsubs 108(%edx)
+ fmuls 112(%ebx)
+ fstps 108(%ecx)
+ flds 100(%edx)
+ fadds 104(%edx)
+ fstps 100(%ecx)
+ flds 100(%edx)
+ fsubs 104(%edx)
+ fmuls 116(%ebx)
+ fstps 104(%ecx)
+ flds 112(%edx)
+ fadds 124(%edx)
+ fstps 112(%ecx)
+ flds 124(%edx)
+ fsubs 112(%edx)
+ fmuls 112(%ebx)
+ fstps 124(%ecx)
+ flds 116(%edx)
+ fadds 120(%edx)
+ fstps 116(%ecx)
+ flds 120(%edx)
+ fsubs 116(%edx)
+ fmuls 116(%ebx)
+ fstps 120(%ecx)
+
+ flds 32(%ecx)
+ fadds 36(%ecx)
+ fstps 32(%edx)
+ flds 32(%ecx)
+ fsubs 36(%ecx)
+ fmuls 120(%ebx)
+ fstps 36(%edx)
+ flds 44(%ecx)
+ fsubs 40(%ecx)
+ fmuls 120(%ebx)
+ fsts 44(%edx)
+ fadds 40(%ecx)
+ fadds 44(%ecx)
+ fstps 40(%edx)
+ flds 48(%ecx)
+ fsubs 52(%ecx)
+ fmuls 120(%ebx)
+ flds 60(%ecx)
+ fsubs 56(%ecx)
+ fmuls 120(%ebx)
+ fld %st(0)
+ fadds 56(%ecx)
+ fadds 60(%ecx)
+ fld %st(0)
+ fadds 48(%ecx)
+ fadds 52(%ecx)
+ fstps 48(%edx)
+ fadd %st(2)
+ fstps 56(%edx)
+ fsts 60(%edx)
+ faddp %st(1)
+ fstps 52(%edx)
+ flds 64(%ecx)
+ fadds 68(%ecx)
+ fstps 64(%edx)
+ flds 64(%ecx)
+ fsubs 68(%ecx)
+ fmuls 120(%ebx)
+ fstps 68(%edx)
+ flds 76(%ecx)
+ fsubs 72(%ecx)
+ fmuls 120(%ebx)
+ fsts 76(%edx)
+ fadds 72(%ecx)
+ fadds 76(%ecx)
+ fstps 72(%edx)
+ flds 92(%ecx)
+ fsubs 88(%ecx)
+ fmuls 120(%ebx)
+ fsts 92(%edx)
+ fadds 92(%ecx)
+ fadds 88(%ecx)
+ fld %st(0)
+ fadds 80(%ecx)
+ fadds 84(%ecx)
+ fstps 80(%edx)
+ flds 80(%ecx)
+ fsubs 84(%ecx)
+ fmuls 120(%ebx)
+ fadd %st(0), %st(1)
+ fadds 92(%edx)
+ fstps 84(%edx)
+ fstps 88(%edx)
+ flds 96(%ecx)
+ fadds 100(%ecx)
+ fstps 96(%edx)
+ flds 96(%ecx)
+ fsubs 100(%ecx)
+ fmuls 120(%ebx)
+ fstps 100(%edx)
+ flds 108(%ecx)
+ fsubs 104(%ecx)
+ fmuls 120(%ebx)
+ fsts 108(%edx)
+ fadds 104(%ecx)
+ fadds 108(%ecx)
+ fstps 104(%edx)
+ flds 124(%ecx)
+ fsubs 120(%ecx)
+ fmuls 120(%ebx)
+ fsts 124(%edx)
+ fadds 120(%ecx)
+ fadds 124(%ecx)
+ fld %st(0)
+ fadds 112(%ecx)
+ fadds 116(%ecx)
+ fstps 112(%edx)
+ flds 112(%ecx)
+ fsubs 116(%ecx)
+ fmuls 120(%ebx)
+ fadd %st(0),%st(1)
+ fadds 124(%edx)
+ fstps 116(%edx)
+ fstps 120(%edx)
+ jnz .L01
+
+ flds (%ecx)
+ fadds 4(%ecx)
+ fstps 1024(%esi)
+ flds (%ecx)
+ fsubs 4(%ecx)
+ fmuls 120(%ebx)
+ fsts (%esi)
+ fstps (%edi)
+ flds 12(%ecx)
+ fsubs 8(%ecx)
+ fmuls 120(%ebx)
+ fsts 512(%edi)
+ fadds 12(%ecx)
+ fadds 8(%ecx)
+ fstps 512(%esi)
+ flds 16(%ecx)
+ fsubs 20(%ecx)
+ fmuls 120(%ebx)
+ flds 28(%ecx)
+ fsubs 24(%ecx)
+ fmuls 120(%ebx)
+ fsts 768(%edi)
+ fld %st(0)
+ fadds 24(%ecx)
+ fadds 28(%ecx)
+ fld %st(0)
+ fadds 16(%ecx)
+ fadds 20(%ecx)
+ fstps 768(%esi)
+ fadd %st(2)
+ fstps 256(%esi)
+ faddp %st(1)
+ fstps 256(%edi)
+
+ flds 32(%edx)
+ fadds 48(%edx)
+ fstps 896(%esi)
+ flds 48(%edx)
+ fadds 40(%edx)
+ fstps 640(%esi)
+ flds 40(%edx)
+ fadds 56(%edx)
+ fstps 384(%esi)
+ flds 56(%edx)
+ fadds 36(%edx)
+ fstps 128(%esi)
+ flds 36(%edx)
+ fadds 52(%edx)
+ fstps 128(%edi)
+ flds 52(%edx)
+ fadds 44(%edx)
+ fstps 384(%edi)
+ flds 60(%edx)
+ fsts 896(%edi)
+ fadds 44(%edx)
+ fstps 640(%edi)
+ flds 96(%edx)
+ fadds 112(%edx)
+ fld %st(0)
+ fadds 64(%edx)
+ fstps 960(%esi)
+ fadds 80(%edx)
+ fstps 832(%esi)
+ flds 112(%edx)
+ fadds 104(%edx)
+ fld %st(0)
+ fadds 80(%edx)
+ fstps 704(%esi)
+ fadds 72(%edx)
+ fstps 576(%esi)
+ flds 104(%edx)
+ fadds 120(%edx)
+ fld %st(0)
+ fadds 72(%edx)
+ fstps 448(%esi)
+ fadds 88(%edx)
+ fstps 320(%esi)
+ flds 120(%edx)
+ fadds 100(%edx)
+ fld %st(0)
+ fadds 88(%edx)
+ fstps 192(%esi)
+ fadds 68(%edx)
+ fstps 64(%esi)
+ flds 100(%edx)
+ fadds 116(%edx)
+ fld %st(0)
+ fadds 68(%edx)
+ fstps 64(%edi)
+ fadds 84(%edx)
+ fstps 192(%edi)
+ flds 116(%edx)
+ fadds 108(%edx)
+ fld %st(0)
+ fadds 84(%edx)
+ fstps 320(%edi)
+ fadds 76(%edx)
+ fstps 448(%edi)
+ flds 108(%edx)
+ fadds 124(%edx)
+ fld %st(0)
+ fadds 76(%edx)
+ fstps 576(%edi)
+ fadds 92(%edx)
+ fstps 704(%edi)
+ flds 124(%edx)
+ fsts 960(%edi)
+ fadds 92(%edx)
+ fstps 832(%edi)
+ addl $256,%esp
+ popl %edi
+ popl %esi
+ popl %ebx
+ ret
+.L01:
+ flds (%ecx)
+ fadds 4(%ecx)
+ fistp 512(%esi)
+ flds (%ecx)
+ fsubs 4(%ecx)
+ fmuls 120(%ebx)
+
+ fistp (%esi)
+
+ flds 12(%ecx)
+ fsubs 8(%ecx)
+ fmuls 120(%ebx)
+ fist 256(%edi)
+ fadds 12(%ecx)
+ fadds 8(%ecx)
+ fistp 256(%esi)
+ flds 16(%ecx)
+ fsubs 20(%ecx)
+ fmuls 120(%ebx)
+ flds 28(%ecx)
+ fsubs 24(%ecx)
+ fmuls 120(%ebx)
+ fist 384(%edi)
+ fld %st(0)
+ fadds 24(%ecx)
+ fadds 28(%ecx)
+ fld %st(0)
+ fadds 16(%ecx)
+ fadds 20(%ecx)
+ fistp 384(%esi)
+ fadd %st(2)
+ fistp 128(%esi)
+ faddp %st(1)
+ fistp 128(%edi)
+
+ flds 32(%edx)
+ fadds 48(%edx)
+ fistp 448(%esi)
+ flds 48(%edx)
+ fadds 40(%edx)
+ fistp 320(%esi)
+ flds 40(%edx)
+ fadds 56(%edx)
+ fistp 192(%esi)
+ flds 56(%edx)
+ fadds 36(%edx)
+ fistp 64(%esi)
+ flds 36(%edx)
+ fadds 52(%edx)
+ fistp 64(%edi)
+ flds 52(%edx)
+ fadds 44(%edx)
+ fistp 192(%edi)
+ flds 60(%edx)
+ fist 448(%edi)
+ fadds 44(%edx)
+ fistp 320(%edi)
+ flds 96(%edx)
+ fadds 112(%edx)
+ fld %st(0)
+ fadds 64(%edx)
+ fistp 480(%esi)
+ fadds 80(%edx)
+ fistp 416(%esi)
+ flds 112(%edx)
+ fadds 104(%edx)
+ fld %st(0)
+ fadds 80(%edx)
+ fistp 352(%esi)
+ fadds 72(%edx)
+ fistp 288(%esi)
+ flds 104(%edx)
+ fadds 120(%edx)
+ fld %st(0)
+ fadds 72(%edx)
+ fistp 224(%esi)
+ fadds 88(%edx)
+ fistp 160(%esi)
+ flds 120(%edx)
+ fadds 100(%edx)
+ fld %st(0)
+ fadds 88(%edx)
+ fistp 96(%esi)
+ fadds 68(%edx)
+ fistp 32(%esi)
+ flds 100(%edx)
+ fadds 116(%edx)
+ fld %st(0)
+ fadds 68(%edx)
+ fistp 32(%edi)
+ fadds 84(%edx)
+ fistp 96(%edi)
+ flds 116(%edx)
+ fadds 108(%edx)
+ fld %st(0)
+ fadds 84(%edx)
+ fistp 160(%edi)
+ fadds 76(%edx)
+ fistp 224(%edi)
+ flds 108(%edx)
+ fadds 124(%edx)
+ fld %st(0)
+ fadds 76(%edx)
+ fistp 288(%edi)
+ fadds 92(%edx)
+ fistp 352(%edi)
+ flds 124(%edx)
+ fist 480(%edi)
+ fadds 92(%edx)
+ fistp 416(%edi)
+ movsw
+ addl $256,%esp
+ popl %edi
+ popl %esi
+ popl %ebx
+ ret
+
+
diff --git a/mpg123_artsplugin/mpg123/dct64_i386.c b/mpg123_artsplugin/mpg123/dct64_i386.c
new file mode 100644
index 00000000..6d42c79d
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/dct64_i386.c
@@ -0,0 +1,329 @@
+
+/*
+ * Discrete Cosine Tansform (DCT) for subband synthesis
+ * optimized for machines with no auto-increment.
+ * The performance is highly compiler dependent. Maybe
+ * the dct64.c version for 'normal' processor may be faster
+ * even for Intel processors.
+ */
+
+#include "mpg123.h"
+
+static void dct64_1(real *out0,real *out1,real *b1,real *b2,real *samples)
+{
+ {
+ register real *costab = pnts[0];
+
+ b1[0x00] = samples[0x00] + samples[0x1F];
+ b1[0x01] = samples[0x01] + samples[0x1E];
+ b1[0x1F] = (samples[0x00] - samples[0x1F]) * costab[0x0];
+ b1[0x1E] = (samples[0x01] - samples[0x1E]) * costab[0x1];
+
+ b1[0x02] = samples[0x02] + samples[0x1D];
+ b1[0x03] = samples[0x03] + samples[0x1C];
+ b1[0x1D] = (samples[0x02] - samples[0x1D]) * costab[0x2];
+ b1[0x1C] = (samples[0x03] - samples[0x1C]) * costab[0x3];
+
+ b1[0x04] = samples[0x04] + samples[0x1B];
+ b1[0x05] = samples[0x05] + samples[0x1A];
+ b1[0x1B] = (samples[0x04] - samples[0x1B]) * costab[0x4];
+ b1[0x1A] = (samples[0x05] - samples[0x1A]) * costab[0x5];
+
+ b1[0x06] = samples[0x06] + samples[0x19];
+ b1[0x07] = samples[0x07] + samples[0x18];
+ b1[0x19] = (samples[0x06] - samples[0x19]) * costab[0x6];
+ b1[0x18] = (samples[0x07] - samples[0x18]) * costab[0x7];
+
+ b1[0x08] = samples[0x08] + samples[0x17];
+ b1[0x09] = samples[0x09] + samples[0x16];
+ b1[0x17] = (samples[0x08] - samples[0x17]) * costab[0x8];
+ b1[0x16] = (samples[0x09] - samples[0x16]) * costab[0x9];
+
+ b1[0x0A] = samples[0x0A] + samples[0x15];
+ b1[0x0B] = samples[0x0B] + samples[0x14];
+ b1[0x15] = (samples[0x0A] - samples[0x15]) * costab[0xA];
+ b1[0x14] = (samples[0x0B] - samples[0x14]) * costab[0xB];
+
+ b1[0x0C] = samples[0x0C] + samples[0x13];
+ b1[0x0D] = samples[0x0D] + samples[0x12];
+ b1[0x13] = (samples[0x0C] - samples[0x13]) * costab[0xC];
+ b1[0x12] = (samples[0x0D] - samples[0x12]) * costab[0xD];
+
+ b1[0x0E] = samples[0x0E] + samples[0x11];
+ b1[0x0F] = samples[0x0F] + samples[0x10];
+ b1[0x11] = (samples[0x0E] - samples[0x11]) * costab[0xE];
+ b1[0x10] = (samples[0x0F] - samples[0x10]) * costab[0xF];
+
+ }
+
+
+ {
+ register real *costab = pnts[1];
+
+ b2[0x00] = b1[0x00] + b1[0x0F];
+ b2[0x01] = b1[0x01] + b1[0x0E];
+ b2[0x0F] = (b1[0x00] - b1[0x0F]) * costab[0];
+ b2[0x0E] = (b1[0x01] - b1[0x0E]) * costab[1];
+
+ b2[0x02] = b1[0x02] + b1[0x0D];
+ b2[0x03] = b1[0x03] + b1[0x0C];
+ b2[0x0D] = (b1[0x02] - b1[0x0D]) * costab[2];
+ b2[0x0C] = (b1[0x03] - b1[0x0C]) * costab[3];
+
+ b2[0x04] = b1[0x04] + b1[0x0B];
+ b2[0x05] = b1[0x05] + b1[0x0A];
+ b2[0x0B] = (b1[0x04] - b1[0x0B]) * costab[4];
+ b2[0x0A] = (b1[0x05] - b1[0x0A]) * costab[5];
+
+ b2[0x06] = b1[0x06] + b1[0x09];
+ b2[0x07] = b1[0x07] + b1[0x08];
+ b2[0x09] = (b1[0x06] - b1[0x09]) * costab[6];
+ b2[0x08] = (b1[0x07] - b1[0x08]) * costab[7];
+
+ /* */
+
+ b2[0x10] = b1[0x10] + b1[0x1F];
+ b2[0x11] = b1[0x11] + b1[0x1E];
+ b2[0x1F] = (b1[0x1F] - b1[0x10]) * costab[0];
+ b2[0x1E] = (b1[0x1E] - b1[0x11]) * costab[1];
+
+ b2[0x12] = b1[0x12] + b1[0x1D];
+ b2[0x13] = b1[0x13] + b1[0x1C];
+ b2[0x1D] = (b1[0x1D] - b1[0x12]) * costab[2];
+ b2[0x1C] = (b1[0x1C] - b1[0x13]) * costab[3];
+
+ b2[0x14] = b1[0x14] + b1[0x1B];
+ b2[0x15] = b1[0x15] + b1[0x1A];
+ b2[0x1B] = (b1[0x1B] - b1[0x14]) * costab[4];
+ b2[0x1A] = (b1[0x1A] - b1[0x15]) * costab[5];
+
+ b2[0x16] = b1[0x16] + b1[0x19];
+ b2[0x17] = b1[0x17] + b1[0x18];
+ b2[0x19] = (b1[0x19] - b1[0x16]) * costab[6];
+ b2[0x18] = (b1[0x18] - b1[0x17]) * costab[7];
+ }
+
+ {
+ register real *costab = pnts[2];
+
+ b1[0x00] = b2[0x00] + b2[0x07];
+ b1[0x07] = (b2[0x00] - b2[0x07]) * costab[0];
+ b1[0x01] = b2[0x01] + b2[0x06];
+ b1[0x06] = (b2[0x01] - b2[0x06]) * costab[1];
+ b1[0x02] = b2[0x02] + b2[0x05];
+ b1[0x05] = (b2[0x02] - b2[0x05]) * costab[2];
+ b1[0x03] = b2[0x03] + b2[0x04];
+ b1[0x04] = (b2[0x03] - b2[0x04]) * costab[3];
+
+ b1[0x08] = b2[0x08] + b2[0x0F];
+ b1[0x0F] = (b2[0x0F] - b2[0x08]) * costab[0];
+ b1[0x09] = b2[0x09] + b2[0x0E];
+ b1[0x0E] = (b2[0x0E] - b2[0x09]) * costab[1];
+ b1[0x0A] = b2[0x0A] + b2[0x0D];
+ b1[0x0D] = (b2[0x0D] - b2[0x0A]) * costab[2];
+ b1[0x0B] = b2[0x0B] + b2[0x0C];
+ b1[0x0C] = (b2[0x0C] - b2[0x0B]) * costab[3];
+
+ b1[0x10] = b2[0x10] + b2[0x17];
+ b1[0x17] = (b2[0x10] - b2[0x17]) * costab[0];
+ b1[0x11] = b2[0x11] + b2[0x16];
+ b1[0x16] = (b2[0x11] - b2[0x16]) * costab[1];
+ b1[0x12] = b2[0x12] + b2[0x15];
+ b1[0x15] = (b2[0x12] - b2[0x15]) * costab[2];
+ b1[0x13] = b2[0x13] + b2[0x14];
+ b1[0x14] = (b2[0x13] - b2[0x14]) * costab[3];
+
+ b1[0x18] = b2[0x18] + b2[0x1F];
+ b1[0x1F] = (b2[0x1F] - b2[0x18]) * costab[0];
+ b1[0x19] = b2[0x19] + b2[0x1E];
+ b1[0x1E] = (b2[0x1E] - b2[0x19]) * costab[1];
+ b1[0x1A] = b2[0x1A] + b2[0x1D];
+ b1[0x1D] = (b2[0x1D] - b2[0x1A]) * costab[2];
+ b1[0x1B] = b2[0x1B] + b2[0x1C];
+ b1[0x1C] = (b2[0x1C] - b2[0x1B]) * costab[3];
+ }
+
+ {
+ register real const cos0 = pnts[3][0];
+ register real const cos1 = pnts[3][1];
+
+ b2[0x00] = b1[0x00] + b1[0x03];
+ b2[0x03] = (b1[0x00] - b1[0x03]) * cos0;
+ b2[0x01] = b1[0x01] + b1[0x02];
+ b2[0x02] = (b1[0x01] - b1[0x02]) * cos1;
+
+ b2[0x04] = b1[0x04] + b1[0x07];
+ b2[0x07] = (b1[0x07] - b1[0x04]) * cos0;
+ b2[0x05] = b1[0x05] + b1[0x06];
+ b2[0x06] = (b1[0x06] - b1[0x05]) * cos1;
+
+ b2[0x08] = b1[0x08] + b1[0x0B];
+ b2[0x0B] = (b1[0x08] - b1[0x0B]) * cos0;
+ b2[0x09] = b1[0x09] + b1[0x0A];
+ b2[0x0A] = (b1[0x09] - b1[0x0A]) * cos1;
+
+ b2[0x0C] = b1[0x0C] + b1[0x0F];
+ b2[0x0F] = (b1[0x0F] - b1[0x0C]) * cos0;
+ b2[0x0D] = b1[0x0D] + b1[0x0E];
+ b2[0x0E] = (b1[0x0E] - b1[0x0D]) * cos1;
+
+ b2[0x10] = b1[0x10] + b1[0x13];
+ b2[0x13] = (b1[0x10] - b1[0x13]) * cos0;
+ b2[0x11] = b1[0x11] + b1[0x12];
+ b2[0x12] = (b1[0x11] - b1[0x12]) * cos1;
+
+ b2[0x14] = b1[0x14] + b1[0x17];
+ b2[0x17] = (b1[0x17] - b1[0x14]) * cos0;
+ b2[0x15] = b1[0x15] + b1[0x16];
+ b2[0x16] = (b1[0x16] - b1[0x15]) * cos1;
+
+ b2[0x18] = b1[0x18] + b1[0x1B];
+ b2[0x1B] = (b1[0x18] - b1[0x1B]) * cos0;
+ b2[0x19] = b1[0x19] + b1[0x1A];
+ b2[0x1A] = (b1[0x19] - b1[0x1A]) * cos1;
+
+ b2[0x1C] = b1[0x1C] + b1[0x1F];
+ b2[0x1F] = (b1[0x1F] - b1[0x1C]) * cos0;
+ b2[0x1D] = b1[0x1D] + b1[0x1E];
+ b2[0x1E] = (b1[0x1E] - b1[0x1D]) * cos1;
+ }
+
+ {
+ register real const cos0 = pnts[4][0];
+
+ b1[0x00] = b2[0x00] + b2[0x01];
+ b1[0x01] = (b2[0x00] - b2[0x01]) * cos0;
+ b1[0x02] = b2[0x02] + b2[0x03];
+ b1[0x03] = (b2[0x03] - b2[0x02]) * cos0;
+ b1[0x02] += b1[0x03];
+
+ b1[0x04] = b2[0x04] + b2[0x05];
+ b1[0x05] = (b2[0x04] - b2[0x05]) * cos0;
+ b1[0x06] = b2[0x06] + b2[0x07];
+ b1[0x07] = (b2[0x07] - b2[0x06]) * cos0;
+ b1[0x06] += b1[0x07];
+ b1[0x04] += b1[0x06];
+ b1[0x06] += b1[0x05];
+ b1[0x05] += b1[0x07];
+
+ b1[0x08] = b2[0x08] + b2[0x09];
+ b1[0x09] = (b2[0x08] - b2[0x09]) * cos0;
+ b1[0x0A] = b2[0x0A] + b2[0x0B];
+ b1[0x0B] = (b2[0x0B] - b2[0x0A]) * cos0;
+ b1[0x0A] += b1[0x0B];
+
+ b1[0x0C] = b2[0x0C] + b2[0x0D];
+ b1[0x0D] = (b2[0x0C] - b2[0x0D]) * cos0;
+ b1[0x0E] = b2[0x0E] + b2[0x0F];
+ b1[0x0F] = (b2[0x0F] - b2[0x0E]) * cos0;
+ b1[0x0E] += b1[0x0F];
+ b1[0x0C] += b1[0x0E];
+ b1[0x0E] += b1[0x0D];
+ b1[0x0D] += b1[0x0F];
+
+ b1[0x10] = b2[0x10] + b2[0x11];
+ b1[0x11] = (b2[0x10] - b2[0x11]) * cos0;
+ b1[0x12] = b2[0x12] + b2[0x13];
+ b1[0x13] = (b2[0x13] - b2[0x12]) * cos0;
+ b1[0x12] += b1[0x13];
+
+ b1[0x14] = b2[0x14] + b2[0x15];
+ b1[0x15] = (b2[0x14] - b2[0x15]) * cos0;
+ b1[0x16] = b2[0x16] + b2[0x17];
+ b1[0x17] = (b2[0x17] - b2[0x16]) * cos0;
+ b1[0x16] += b1[0x17];
+ b1[0x14] += b1[0x16];
+ b1[0x16] += b1[0x15];
+ b1[0x15] += b1[0x17];
+
+ b1[0x18] = b2[0x18] + b2[0x19];
+ b1[0x19] = (b2[0x18] - b2[0x19]) * cos0;
+ b1[0x1A] = b2[0x1A] + b2[0x1B];
+ b1[0x1B] = (b2[0x1B] - b2[0x1A]) * cos0;
+ b1[0x1A] += b1[0x1B];
+
+ b1[0x1C] = b2[0x1C] + b2[0x1D];
+ b1[0x1D] = (b2[0x1C] - b2[0x1D]) * cos0;
+ b1[0x1E] = b2[0x1E] + b2[0x1F];
+ b1[0x1F] = (b2[0x1F] - b2[0x1E]) * cos0;
+ b1[0x1E] += b1[0x1F];
+ b1[0x1C] += b1[0x1E];
+ b1[0x1E] += b1[0x1D];
+ b1[0x1D] += b1[0x1F];
+ }
+
+ out0[0x10*16] = b1[0x00];
+ out0[0x10*12] = b1[0x04];
+ out0[0x10* 8] = b1[0x02];
+ out0[0x10* 4] = b1[0x06];
+ out0[0x10* 0] = b1[0x01];
+ out1[0x10* 0] = b1[0x01];
+ out1[0x10* 4] = b1[0x05];
+ out1[0x10* 8] = b1[0x03];
+ out1[0x10*12] = b1[0x07];
+
+#if 1
+ out0[0x10*14] = b1[0x08] + b1[0x0C];
+ out0[0x10*10] = b1[0x0C] + b1[0x0a];
+ out0[0x10* 6] = b1[0x0A] + b1[0x0E];
+ out0[0x10* 2] = b1[0x0E] + b1[0x09];
+ out1[0x10* 2] = b1[0x09] + b1[0x0D];
+ out1[0x10* 6] = b1[0x0D] + b1[0x0B];
+ out1[0x10*10] = b1[0x0B] + b1[0x0F];
+ out1[0x10*14] = b1[0x0F];
+#else
+ b1[0x08] += b1[0x0C];
+ out0[0x10*14] = b1[0x08];
+ b1[0x0C] += b1[0x0a];
+ out0[0x10*10] = b1[0x0C];
+ b1[0x0A] += b1[0x0E];
+ out0[0x10* 6] = b1[0x0A];
+ b1[0x0E] += b1[0x09];
+ out0[0x10* 2] = b1[0x0E];
+ b1[0x09] += b1[0x0D];
+ out1[0x10* 2] = b1[0x09];
+ b1[0x0D] += b1[0x0B];
+ out1[0x10* 6] = b1[0x0D];
+ b1[0x0B] += b1[0x0F];
+ out1[0x10*10] = b1[0x0B];
+ out1[0x10*14] = b1[0x0F];
+#endif
+
+ {
+ real tmp;
+ tmp = b1[0x18] + b1[0x1C];
+ out0[0x10*15] = tmp + b1[0x10];
+ out0[0x10*13] = tmp + b1[0x14];
+ tmp = b1[0x1C] + b1[0x1A];
+ out0[0x10*11] = tmp + b1[0x14];
+ out0[0x10* 9] = tmp + b1[0x12];
+ tmp = b1[0x1A] + b1[0x1E];
+ out0[0x10* 7] = tmp + b1[0x12];
+ out0[0x10* 5] = tmp + b1[0x16];
+ tmp = b1[0x1E] + b1[0x19];
+ out0[0x10* 3] = tmp + b1[0x16];
+ out0[0x10* 1] = tmp + b1[0x11];
+ tmp = b1[0x19] + b1[0x1D];
+ out1[0x10* 1] = tmp + b1[0x11];
+ out1[0x10* 3] = tmp + b1[0x15];
+ tmp = b1[0x1D] + b1[0x1B];
+ out1[0x10* 5] = tmp + b1[0x15];
+ out1[0x10* 7] = tmp + b1[0x13];
+ tmp = b1[0x1B] + b1[0x1F];
+ out1[0x10* 9] = tmp + b1[0x13];
+ out1[0x10*11] = tmp + b1[0x17];
+ out1[0x10*13] = b1[0x17] + b1[0x1F];
+ out1[0x10*15] = b1[0x1F];
+ }
+}
+
+/*
+ * the call via dct64 is a trick to force GCC to use
+ * (new) registers for the b1,b2 pointer to the bufs[xx] field
+ */
+void dct64(real *a,real *b,real *c)
+{
+ real bufs[0x40];
+ dct64_1(a,b,bufs,bufs+0x20,c);
+}
+
diff --git a/mpg123_artsplugin/mpg123/dct64_i486-a.s b/mpg123_artsplugin/mpg123/dct64_i486-a.s
new file mode 100644
index 00000000..37e91730
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/dct64_i486-a.s
@@ -0,0 +1,831 @@
+.section .rodata
+ .align 8
+.LC48:
+ .long 0x80000000,0x42400000
+.text
+ .align 16
+ .globl dct64_1_486_a
+ .type dct64_1_486_a,@function
+dct64_1_486_a:
+ pushl %ebx
+ pushl %edi
+ pushl %esi
+ movl 16(%esp),%esi
+ movl 20(%esp),%ebx
+ fldl .LC48
+
+ fld %st(0)
+ fadds (%esi)
+ fstpl (%ebx)
+
+ fld %st(0)
+ fadds 4(%esi)
+ fstpl 4(%ebx)
+
+ fld %st(0)
+ fadds 8(%esi)
+ fstpl 8(%ebx)
+
+ fld %st(0)
+ fadds 12(%esi)
+ fstpl 12(%ebx)
+
+ fld %st(0)
+ fadds 16(%esi)
+ fstpl 16(%ebx)
+
+ fld %st(0)
+ fadds 20(%esi)
+ fstpl 20(%ebx)
+
+ fld %st(0)
+ fadds 24(%esi)
+ fstpl 24(%ebx)
+
+ fld %st(0)
+ fadds 28(%esi)
+ fstpl 28(%ebx)
+
+ fld %st(0)
+ fadds 32(%esi)
+ fstpl 32(%ebx)
+
+ fld %st(0)
+ fadds 36(%esi)
+ fstpl 36(%ebx)
+
+ fld %st(0)
+ fadds 40(%esi)
+ fstpl 40(%ebx)
+
+ fld %st(0)
+ fadds 44(%esi)
+ fstpl 44(%ebx)
+
+ fld %st(0)
+ fadds 48(%esi)
+ fstpl 48(%ebx)
+
+ fld %st(0)
+ fadds 52(%esi)
+ fstpl 52(%ebx)
+
+ fld %st(0)
+ fadds 56(%esi)
+ fstpl 56(%ebx)
+
+ fld %st(0)
+ fadds 60(%esi)
+ fstpl 60(%ebx)
+
+ fld %st(0)
+ fadds 64(%esi)
+ fstpl 64(%ebx)
+
+ fld %st(0)
+ fadds 68(%esi)
+ fstpl 68(%ebx)
+
+ fld %st(0)
+ fadds 72(%esi)
+ fstpl 72(%ebx)
+
+ fld %st(0)
+ fadds 76(%esi)
+ fstpl 76(%ebx)
+
+ fld %st(0)
+ fadds 80(%esi)
+ fstpl 80(%ebx)
+
+ fld %st(0)
+ fadds 84(%esi)
+ fstpl 84(%ebx)
+
+ fld %st(0)
+ fadds 88(%esi)
+ fstpl 88(%ebx)
+
+ fld %st(0)
+ fadds 92(%esi)
+ fstpl 92(%ebx)
+
+ fld %st(0)
+ fadds 96(%esi)
+ fstpl 96(%ebx)
+
+ fld %st(0)
+ fadds 100(%esi)
+ fstpl 100(%ebx)
+
+ fld %st(0)
+ fadds 104(%esi)
+ fstpl 104(%ebx)
+
+ fld %st(0)
+ fadds 108(%esi)
+ fstpl 108(%ebx)
+
+ fld %st(0)
+ fadds 112(%esi)
+ fstpl 112(%ebx)
+
+ fld %st(0)
+ fadds 116(%esi)
+ fstpl 116(%ebx)
+
+ fld %st(0)
+ fadds 120(%esi)
+ fstpl 120(%ebx)
+
+ fadds 124(%esi)
+ fstpl 124(%ebx)
+
+ mov (%ebx),%eax
+ mov 124(%ebx),%edx
+ add %edx, (%ebx)
+ sub %edx,%eax
+ mov $16403,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,124(%ebx)
+
+ mov 4(%ebx),%eax
+ mov 120(%ebx),%edx
+ add %edx,4(%ebx)
+ sub %edx,%eax
+ mov $16563,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,120(%ebx)
+
+ mov 8(%ebx),%eax
+ mov 116(%ebx),%edx
+ add %edx,8(%ebx)
+ sub %edx,%eax
+ mov $16890,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,116(%ebx)
+
+ mov 12(%ebx),%eax
+ mov 112(%ebx),%edx
+ add %edx,12(%ebx)
+ sub %edx,%eax
+ mov $17401,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,112(%ebx)
+
+ mov 16(%ebx),%eax
+ mov 108(%ebx),%edx
+ add %edx,16(%ebx)
+ sub %edx,%eax
+ mov $18124,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,108(%ebx)
+
+ mov 20(%ebx),%eax
+ mov 104(%ebx),%edx
+ add %edx,20(%ebx)
+ sub %edx,%eax
+ mov $19101,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,104(%ebx)
+
+ mov 24(%ebx),%eax
+ mov 100(%ebx),%edx
+ add %edx,24(%ebx)
+ sub %edx,%eax
+ mov $20398,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,100(%ebx)
+
+ mov 28(%ebx),%eax
+ mov 96(%ebx),%edx
+ add %edx,28(%ebx)
+ sub %edx,%eax
+ mov $22112,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,96(%ebx)
+
+ mov 32(%ebx),%eax
+ mov 92(%ebx),%edx
+ add %edx,32(%ebx)
+ sub %edx,%eax
+ mov $24396,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,92(%ebx)
+
+ mov 36(%ebx),%eax
+ mov 88(%ebx),%edx
+ add %edx,36(%ebx)
+ sub %edx,%eax
+ mov $27503,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,88(%ebx)
+
+ mov 40(%ebx),%eax
+ mov 84(%ebx),%edx
+ add %edx,40(%ebx)
+ sub %edx,%eax
+ mov $31869,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,84(%ebx)
+
+ mov 44(%ebx),%eax
+ mov 80(%ebx),%edx
+ add %edx,44(%ebx)
+ sub %edx,%eax
+ mov $38320,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,80(%ebx)
+
+ mov 48(%ebx),%eax
+ mov 76(%ebx),%edx
+ add %edx,48(%ebx)
+ sub %edx,%eax
+ mov $48633,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,76(%ebx)
+
+ mov 52(%ebx),%eax
+ mov 72(%ebx),%edx
+ add %edx,52(%ebx)
+ sub %edx,%eax
+ mov $67429,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,72(%ebx)
+
+ mov 56(%ebx),%eax
+ mov 68(%ebx),%edx
+ add %edx,56(%ebx)
+ sub %edx,%eax
+ mov $111660,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,68(%ebx)
+
+ mov 60(%ebx),%eax
+ mov 64(%ebx),%edx
+ add %edx,60(%ebx)
+ sub %edx,%eax
+ mov $333906,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,64(%ebx)
+
+ mov (%ebx),%eax
+ mov 60(%ebx),%edx
+ add %edx, (%ebx)
+ sub %edx,%eax
+ mov $16463,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,60(%ebx)
+
+ mov 4(%ebx),%eax
+ mov 56(%ebx),%edx
+ add %edx,4(%ebx)
+ sub %edx,%eax
+ mov $17121,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,56(%ebx)
+
+ mov 8(%ebx),%eax
+ mov 52(%ebx),%edx
+ add %edx,8(%ebx)
+ sub %edx,%eax
+ mov $18577,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,52(%ebx)
+
+ mov 12(%ebx),%eax
+ mov 48(%ebx),%edx
+ add %edx,12(%ebx)
+ sub %edx,%eax
+ mov $21195,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,48(%ebx)
+
+ mov 16(%ebx),%eax
+ mov 44(%ebx),%edx
+ add %edx,16(%ebx)
+ sub %edx,%eax
+ mov $25826,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,44(%ebx)
+
+ mov 20(%ebx),%eax
+ mov 40(%ebx),%edx
+ add %edx,20(%ebx)
+ sub %edx,%eax
+ mov $34756,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,40(%ebx)
+
+ mov 24(%ebx),%eax
+ mov 36(%ebx),%edx
+ add %edx,24(%ebx)
+ sub %edx,%eax
+ mov $56441,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,36(%ebx)
+
+ mov 28(%ebx),%eax
+ mov 32(%ebx),%edx
+ add %edx,28(%ebx)
+ sub %edx,%eax
+ mov $167154,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,32(%ebx)
+
+ mov 124(%ebx),%eax
+ mov 64(%ebx),%edx
+ add %eax,64(%ebx)
+ sub %edx,%eax
+ mov $16463,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,124(%ebx)
+
+ mov 120(%ebx),%eax
+ mov 68(%ebx),%edx
+ add %eax,68(%ebx)
+ sub %edx,%eax
+ mov $17121,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,120(%ebx)
+
+ mov 116(%ebx),%eax
+ mov 72(%ebx),%edx
+ add %eax,72(%ebx)
+ sub %edx,%eax
+ mov $18577,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,116(%ebx)
+
+ mov 112(%ebx),%eax
+ mov 76(%ebx),%edx
+ add %eax,76(%ebx)
+ sub %edx,%eax
+ mov $21195,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,112(%ebx)
+
+ mov 108(%ebx),%eax
+ mov 80(%ebx),%edx
+ add %eax,80(%ebx)
+ sub %edx,%eax
+ mov $25826,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,108(%ebx)
+
+ mov 104(%ebx),%eax
+ mov 84(%ebx),%edx
+ add %eax,84(%ebx)
+ sub %edx,%eax
+ mov $34756,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,104(%ebx)
+
+ mov 100(%ebx),%eax
+ mov 88(%ebx),%edx
+ add %eax,88(%ebx)
+ sub %edx,%eax
+ mov $56441,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,100(%ebx)
+
+ mov 96(%ebx),%eax
+ mov 92(%ebx),%edx
+ add %eax,92(%ebx)
+ sub %edx,%eax
+ mov $167154,%edx
+ imul %edx
+ shrdl $15,%edx,%eax
+ movl %eax,96(%ebx)
+
+ mov $16704,%edi
+ mov $19704,%esi
+
+ mov (%ebx),%eax
+ mov 28(%ebx),%edx
+ add %edx, (%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,28(%ebx)
+
+ mov 4(%ebx),%eax
+ mov 24(%ebx),%edx
+ add %edx,4(%ebx)
+ sub %edx,%eax
+ imul %esi
+ shrdl $15,%edx,%eax
+ movl %eax,24(%ebx)
+
+ mov 60(%ebx),%eax
+ mov 32(%ebx),%edx
+ add %eax,32(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,60(%ebx)
+
+ mov 56(%ebx),%eax
+ mov 36(%ebx),%edx
+ add %eax,36(%ebx)
+ sub %edx,%eax
+ imul %esi
+ shrdl $15,%edx,%eax
+ movl %eax,56(%ebx)
+
+ mov 64(%ebx),%eax
+ mov 92(%ebx),%edx
+ add %edx,64(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,92(%ebx)
+
+ mov 68(%ebx),%eax
+ mov 88(%ebx),%edx
+ add %edx,68(%ebx)
+ sub %edx,%eax
+ imul %esi
+ shrdl $15,%edx,%eax
+ movl %eax,88(%ebx)
+
+ mov 124(%ebx),%eax
+ mov 96(%ebx),%edx
+ add %eax,96(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,124(%ebx)
+
+ mov 120(%ebx),%eax
+ mov 100(%ebx),%edx
+ add %eax,100(%ebx)
+ sub %edx,%eax
+ imul %esi
+ shrdl $15,%edx,%eax
+ movl %eax,120(%ebx)
+
+ mov $29490,%edi
+ mov $83981,%esi
+
+ mov 8(%ebx),%eax
+ mov 20(%ebx),%edx
+ add %edx,8(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,20(%ebx)
+
+ mov 12(%ebx),%eax
+ mov 16(%ebx),%edx
+ add %edx,12(%ebx)
+ sub %edx,%eax
+ imul %esi
+ shrdl $15,%edx,%eax
+ movl %eax,16(%ebx)
+
+ mov 52(%ebx),%eax
+ mov 40(%ebx),%edx
+ add %eax,40(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,52(%ebx)
+
+ mov 48(%ebx),%eax
+ mov 44(%ebx),%edx
+ add %eax,44(%ebx)
+ sub %edx,%eax
+ imul %esi
+ shrdl $15,%edx,%eax
+ movl %eax,48(%ebx)
+
+ mov 72(%ebx),%eax
+ mov 84(%ebx),%edx
+ add %edx,72(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,84(%ebx)
+
+ mov 76(%ebx),%eax
+ mov 80(%ebx),%edx
+ add %edx,76(%ebx)
+ sub %edx,%eax
+ imul %esi
+ shrdl $15,%edx,%eax
+ movl %eax,80(%ebx)
+
+ mov 116(%ebx),%eax
+ mov 104(%ebx),%edx
+ add %eax,104(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,116(%ebx)
+
+ mov 112(%ebx),%eax
+ mov 108(%ebx),%edx
+ add %eax,108(%ebx)
+ sub %edx,%eax
+ imul %esi
+ shrdl $15,%edx,%eax
+ movl %eax,112(%ebx)
+
+ mov $17733,%edi
+ mov $42813,%esi
+
+ mov (%ebx),%eax
+ mov 12(%ebx),%edx
+ add %edx, (%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,12(%ebx)
+
+ mov 4(%ebx),%eax
+ mov 8(%ebx),%edx
+ add %edx,4(%ebx)
+ sub %edx,%eax
+ imul %esi
+ shrdl $15,%edx,%eax
+ movl %eax,8(%ebx)
+
+ mov 28(%ebx),%eax
+ mov 16(%ebx),%edx
+ add %eax,16(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,28(%ebx)
+
+ mov 24(%ebx),%eax
+ mov 20(%ebx),%edx
+ add %eax,20(%ebx)
+ sub %edx,%eax
+ imul %esi
+ shrdl $15,%edx,%eax
+ movl %eax,24(%ebx)
+
+ mov 32(%ebx),%eax
+ mov 44(%ebx),%edx
+ add %edx,32(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,44(%ebx)
+
+ mov 36(%ebx),%eax
+ mov 40(%ebx),%edx
+ add %edx,36(%ebx)
+ sub %edx,%eax
+ imul %esi
+ shrdl $15,%edx,%eax
+ movl %eax,40(%ebx)
+
+ mov 60(%ebx),%eax
+ mov 48(%ebx),%edx
+ add %eax,48(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,60(%ebx)
+
+ mov 56(%ebx),%eax
+ mov 52(%ebx),%edx
+ add %eax,52(%ebx)
+ sub %edx,%eax
+ imul %esi
+ shrdl $15,%edx,%eax
+ movl %eax,56(%ebx)
+
+ mov 64(%ebx),%eax
+ mov 76(%ebx),%edx
+ add %edx,64(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,76(%ebx)
+
+ mov 68(%ebx),%eax
+ mov 72(%ebx),%edx
+ add %edx,68(%ebx)
+ sub %edx,%eax
+ imul %esi
+ shrdl $15,%edx,%eax
+ movl %eax,72(%ebx)
+
+ mov 92(%ebx),%eax
+ mov 80(%ebx),%edx
+ add %eax,80(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,92(%ebx)
+
+ mov 88(%ebx),%eax
+ mov 84(%ebx),%edx
+ add %eax,84(%ebx)
+ sub %edx,%eax
+ imul %esi
+ shrdl $15,%edx,%eax
+ movl %eax,88(%ebx)
+
+ mov 96(%ebx),%eax
+ mov 108(%ebx),%edx
+ add %edx,96(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,108(%ebx)
+
+ mov 100(%ebx),%eax
+ mov 104(%ebx),%edx
+ add %edx,100(%ebx)
+ sub %edx,%eax
+ imul %esi
+ shrdl $15,%edx,%eax
+ movl %eax,104(%ebx)
+
+ mov 124(%ebx),%eax
+ mov 112(%ebx),%edx
+ add %eax,112(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,124(%ebx)
+
+ mov 120(%ebx),%eax
+ mov 116(%ebx),%edx
+ add %eax,116(%ebx)
+ sub %edx,%eax
+ imul %esi
+ shrdl $15,%edx,%eax
+ movl %eax,120(%ebx)
+
+ mov $23170,%edi
+
+ mov (%ebx),%eax
+ mov 4(%ebx),%edx
+ add %edx, (%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,4(%ebx)
+
+ mov 12(%ebx),%eax
+ mov 8(%ebx),%edx
+ add %eax,8(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,12(%ebx)
+
+ mov 16(%ebx),%eax
+ mov 20(%ebx),%edx
+ add %edx,16(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,20(%ebx)
+
+ mov 28(%ebx),%eax
+ mov 24(%ebx),%edx
+ add %eax,24(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,28(%ebx)
+
+ mov 32(%ebx),%eax
+ mov 36(%ebx),%edx
+ add %edx,32(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,36(%ebx)
+
+ mov 44(%ebx),%eax
+ mov 40(%ebx),%edx
+ add %eax,40(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,44(%ebx)
+
+ mov 48(%ebx),%eax
+ mov 52(%ebx),%edx
+ add %edx,48(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,52(%ebx)
+
+ mov 60(%ebx),%eax
+ mov 56(%ebx),%edx
+ add %eax,56(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,60(%ebx)
+
+ mov 64(%ebx),%eax
+ mov 68(%ebx),%edx
+ add %edx,64(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,68(%ebx)
+
+ mov 76(%ebx),%eax
+ mov 72(%ebx),%edx
+ add %eax,72(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,76(%ebx)
+
+ mov 80(%ebx),%eax
+ mov 84(%ebx),%edx
+ add %edx,80(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,84(%ebx)
+
+ mov 92(%ebx),%eax
+ mov 88(%ebx),%edx
+ add %eax,88(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,92(%ebx)
+
+ mov 96(%ebx),%eax
+ mov 100(%ebx),%edx
+ add %edx,96(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,100(%ebx)
+
+ mov 108(%ebx),%eax
+ mov 104(%ebx),%edx
+ add %eax,104(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,108(%ebx)
+
+ mov 112(%ebx),%eax
+ mov 116(%ebx),%edx
+ add %edx,112(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,116(%ebx)
+
+ mov 124(%ebx),%eax
+ mov 120(%ebx),%edx
+ add %eax,120(%ebx)
+ sub %edx,%eax
+ imul %edi
+ shrdl $15,%edx,%eax
+ movl %eax,124(%ebx)
+
+ popl %esi
+ popl %edi
+ popl %ebx
+ ret
+
diff --git a/mpg123_artsplugin/mpg123/dct64_i486-b.c b/mpg123_artsplugin/mpg123/dct64_i486-b.c
new file mode 100644
index 00000000..cb57f57f
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/dct64_i486-b.c
@@ -0,0 +1,140 @@
+/* Discrete Cosine Tansform (DCT) for subband synthesis.
+ *
+ * This code is optimized for 80486. It should be compiled with gcc
+ * 2.7.2 or higher.
+ *
+ * Note: This code does not give the necessary accuracy. Moreover, no
+ * overflow test are done.
+ *
+ * (c) 1998 Fabrice Bellard.
+ *
+ * Rewrite to asm with algorithm and memory optimization
+ *
+ * (c) 2000 Petr Salinger
+ *
+ * GPL clean
+ *
+ */
+
+#include "mpg123.h"
+
+#define COS_0_0 16403
+#define COS_0_1 16563
+#define COS_0_2 16890
+#define COS_0_3 17401
+#define COS_0_4 18124
+#define COS_0_5 19101
+#define COS_0_6 20398
+#define COS_0_7 22112
+#define COS_0_8 24396
+#define COS_0_9 27503
+#define COS_0_10 31869
+#define COS_0_11 38320
+#define COS_0_12 48633
+#define COS_0_13 67429
+#define COS_0_14 111660
+#define COS_0_15 333906
+#define COS_1_0 16463
+#define COS_1_1 17121
+#define COS_1_2 18577
+#define COS_1_3 21195
+#define COS_1_4 25826
+#define COS_1_5 34756
+#define COS_1_6 56441
+#define COS_1_7 167154
+#define COS_2_0 16704
+#define COS_2_1 19704
+#define COS_2_2 29490
+#define COS_2_3 83981
+#define COS_3_0 17733
+#define COS_3_1 42813
+#define COS_4_0 23170
+
+#define SETOUT(out,n,expr) out[FIR_BUFFER_SIZE*(n)]=(expr)
+#define MUL(a,b) (((a)*(b)) >> 15)
+#define MULL(a,b) (((long long)(a)*(long long)(b)) >> 15)
+#define TOINT(a) ((int)((a)*32768.0))
+
+void dct64_1_486_a(real *samples, int *bx);
+
+void dct64_1_486_b(int *out0, int *out1, int *b1)
+{
+ b1[0x02] += b1[0x03];
+ b1[0x06] += b1[0x07];
+ b1[0x04] += b1[0x06];
+ b1[0x06] += b1[0x05];
+ b1[0x05] += b1[0x07];
+
+ b1[0x0A] += b1[0x0B];
+ b1[0x0E] += b1[0x0F];
+ b1[0x0C] += b1[0x0E];
+ b1[0x0E] += b1[0x0D];
+ b1[0x0D] += b1[0x0F];
+
+ b1[0x12] += b1[0x13];
+ b1[0x16] += b1[0x17];
+ b1[0x14] += b1[0x16];
+ b1[0x16] += b1[0x15];
+ b1[0x15] += b1[0x17];
+
+ b1[0x1A] += b1[0x1B];
+ b1[0x1E] += b1[0x1F];
+ b1[0x1C] += b1[0x1E];
+ b1[0x1E] += b1[0x1D];
+ b1[0x1D] += b1[0x1F];
+
+ SETOUT(out0,16,b1[0x00]);
+ SETOUT(out0,12,b1[0x04]);
+ SETOUT(out0, 8,b1[0x02]);
+ SETOUT(out0, 4,b1[0x06]);
+ SETOUT(out0, 0,b1[0x01]);
+ SETOUT(out1, 0,b1[0x01]);
+ SETOUT(out1, 4,b1[0x05]);
+ SETOUT(out1, 8,b1[0x03]);
+ SETOUT(out1,12,b1[0x07]);
+
+ SETOUT(out0,14, b1[0x08] + b1[0x0C]);
+ SETOUT(out0,10, b1[0x0C] + b1[0x0A]);
+ SETOUT(out0, 6, b1[0x0A] + b1[0x0E]);
+ SETOUT(out0, 2, b1[0x0E] + b1[0x09]);
+ SETOUT(out1, 2, b1[0x09] + b1[0x0D]);
+ SETOUT(out1, 6, b1[0x0D] + b1[0x0B]);
+ SETOUT(out1,10, b1[0x0B] + b1[0x0F]);
+ SETOUT(out1,14, b1[0x0F]);
+
+ b1[0x18] += b1[0x1C];
+ SETOUT(out0,15,b1[0x10] + b1[0x18]);
+ SETOUT(out0,13,b1[0x18] + b1[0x14]);
+ b1[0x1C] += b1[0x1a];
+ SETOUT(out0,11,b1[0x14] + b1[0x1C]);
+ SETOUT(out0, 9,b1[0x1C] + b1[0x12]);
+ b1[0x1A] += b1[0x1E];
+ SETOUT(out0, 7,b1[0x12] + b1[0x1A]);
+ SETOUT(out0, 5,b1[0x1A] + b1[0x16]);
+ b1[0x1E] += b1[0x19];
+ SETOUT(out0, 3,b1[0x16] + b1[0x1E]);
+ SETOUT(out0, 1,b1[0x1E] + b1[0x11]);
+ b1[0x19] += b1[0x1D];
+ SETOUT(out1, 1,b1[0x11] + b1[0x19]);
+ SETOUT(out1, 3,b1[0x19] + b1[0x15]);
+ b1[0x1D] += b1[0x1B];
+ SETOUT(out1, 5,b1[0x15] + b1[0x1D]);
+ SETOUT(out1, 7,b1[0x1D] + b1[0x13]);
+ b1[0x1B] += b1[0x1F];
+ SETOUT(out1, 9,b1[0x13] + b1[0x1B]);
+ SETOUT(out1,11,b1[0x1B] + b1[0x17]);
+ SETOUT(out1,13,b1[0x17] + b1[0x1F]);
+ SETOUT(out1,15,b1[0x1F]);
+}
+
+/*
+ * main DCT function
+ */
+void dct64_486(int *a,int *b,real *samples)
+{
+ int bufs[33]; /* 32 + 1 extra for 8B store from x87 */
+
+ dct64_1_486_a(samples,bufs);
+ dct64_1_486_b(a,b,bufs);
+}
+
diff --git a/mpg123_artsplugin/mpg123/dct64_i486.c b/mpg123_artsplugin/mpg123/dct64_i486.c
new file mode 100644
index 00000000..b8f5d08b
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/dct64_i486.c
@@ -0,0 +1,322 @@
+
+/* Discrete Cosine Tansform (DCT) for subband synthesis.
+ *
+ * This code is optimized for 80486. It should be compiled with gcc
+ * 2.7.2 or higher.
+ *
+ * Note: This code does not give the necessary accuracy. Moreover, no
+ * overflow test are done.
+ *
+ * (c) 1998 Fabrice Bellard.
+ *
+ * GPL clean
+ */
+
+#include "mpg123.h"
+
+#define COS_0_0 16403
+#define COS_0_1 16563
+#define COS_0_2 16890
+#define COS_0_3 17401
+#define COS_0_4 18124
+#define COS_0_5 19101
+#define COS_0_6 20398
+#define COS_0_7 22112
+#define COS_0_8 24396
+#define COS_0_9 27503
+#define COS_0_10 31869
+#define COS_0_11 38320
+#define COS_0_12 48633
+#define COS_0_13 67429
+#define COS_0_14 111660
+#define COS_0_15 333906
+#define COS_1_0 16463
+#define COS_1_1 17121
+#define COS_1_2 18577
+#define COS_1_3 21195
+#define COS_1_4 25826
+#define COS_1_5 34756
+#define COS_1_6 56441
+#define COS_1_7 167154
+#define COS_2_0 16704
+#define COS_2_1 19704
+#define COS_2_2 29490
+#define COS_2_3 83981
+#define COS_3_0 17733
+#define COS_3_1 42813
+#define COS_4_0 23170
+
+#define SETOUT(out,n,expr) out[FIR_BUFFER_SIZE*(n)]=(expr)
+#define MUL(a,b) (((a)*(b)) >> 15)
+#define MULL(a,b) (((long long)(a)*(long long)(b)) >> 15)
+#ifdef REAL_IS_FIXED
+#define TOINT(a) ((a) * 32768 / (int)REAL_FACTOR)
+#else
+#define TOINT(a) ((int)((a)*32768.0))
+#endif
+
+void dct64_1_486(int *out0,int *out1,int *b1,int *b2)
+{
+ b1[0x00] = b2[0x00] + b2[0x1F];
+ b1[0x1F] = MUL((b2[0x00] - b2[0x1F]),COS_0_0);
+
+ b1[0x01] = b2[0x01] + b2[0x1E];
+ b1[0x1E] = MUL((b2[0x01] - b2[0x1E]),COS_0_1);
+
+ b1[0x02] = b2[0x02] + b2[0x1D];
+ b1[0x1D] = MUL((b2[0x02] - b2[0x1D]),COS_0_2);
+
+ b1[0x03] = b2[0x03] + b2[0x1C];
+ b1[0x1C] = MUL((b2[0x03] - b2[0x1C]),COS_0_3);
+
+ b1[0x04] = b2[0x04] + b2[0x1B];
+ b1[0x1B] = MUL((b2[0x04] - b2[0x1B]),COS_0_4);
+
+ b1[0x05] = b2[0x05] + b2[0x1A];
+ b1[0x1A] = MUL((b2[0x05] - b2[0x1A]),COS_0_5);
+
+ b1[0x06] = b2[0x06] + b2[0x19];
+ b1[0x19] = MUL((b2[0x06] - b2[0x19]),COS_0_6);
+
+ b1[0x07] = b2[0x07] + b2[0x18];
+ b1[0x18] = MUL((b2[0x07] - b2[0x18]),COS_0_7);
+
+ b1[0x08] = b2[0x08] + b2[0x17];
+ b1[0x17] = MUL((b2[0x08] - b2[0x17]),COS_0_8);
+
+ b1[0x09] = b2[0x09] + b2[0x16];
+ b1[0x16] = MUL((b2[0x09] - b2[0x16]),COS_0_9);
+
+ b1[0x0A] = b2[0x0A] + b2[0x15];
+ b1[0x15] = MUL((b2[0x0A] - b2[0x15]),COS_0_10);
+
+ b1[0x0B] = b2[0x0B] + b2[0x14];
+ b1[0x14] = MUL((b2[0x0B] - b2[0x14]),COS_0_11);
+
+ b1[0x0C] = b2[0x0C] + b2[0x13];
+ b1[0x13] = MUL((b2[0x0C] - b2[0x13]),COS_0_12);
+
+ b1[0x0D] = b2[0x0D] + b2[0x12];
+ b1[0x12] = MULL((b2[0x0D] - b2[0x12]),COS_0_13);
+
+ b1[0x0E] = b2[0x0E] + b2[0x11];
+ b1[0x11] = MULL((b2[0x0E] - b2[0x11]),COS_0_14);
+
+ b1[0x0F] = b2[0x0F] + b2[0x10];
+ b1[0x10] = MULL((b2[0x0F] - b2[0x10]),COS_0_15);
+
+
+ b2[0x00] = b1[0x00] + b1[0x0F];
+ b2[0x0F] = MUL((b1[0x00] - b1[0x0F]),COS_1_0);
+ b2[0x01] = b1[0x01] + b1[0x0E];
+ b2[0x0E] = MUL((b1[0x01] - b1[0x0E]),COS_1_1);
+ b2[0x02] = b1[0x02] + b1[0x0D];
+ b2[0x0D] = MUL((b1[0x02] - b1[0x0D]),COS_1_2);
+ b2[0x03] = b1[0x03] + b1[0x0C];
+ b2[0x0C] = MUL((b1[0x03] - b1[0x0C]),COS_1_3);
+ b2[0x04] = b1[0x04] + b1[0x0B];
+ b2[0x0B] = MUL((b1[0x04] - b1[0x0B]),COS_1_4);
+ b2[0x05] = b1[0x05] + b1[0x0A];
+ b2[0x0A] = MUL((b1[0x05] - b1[0x0A]),COS_1_5);
+ b2[0x06] = b1[0x06] + b1[0x09];
+ b2[0x09] = MUL((b1[0x06] - b1[0x09]),COS_1_6);
+ b2[0x07] = b1[0x07] + b1[0x08];
+ b2[0x08] = MULL((b1[0x07] - b1[0x08]),COS_1_7);
+
+ b2[0x10] = b1[0x10] + b1[0x1F];
+ b2[0x1F] = MUL((b1[0x1F] - b1[0x10]),COS_1_0);
+ b2[0x11] = b1[0x11] + b1[0x1E];
+ b2[0x1E] = MUL((b1[0x1E] - b1[0x11]),COS_1_1);
+ b2[0x12] = b1[0x12] + b1[0x1D];
+ b2[0x1D] = MUL((b1[0x1D] - b1[0x12]),COS_1_2);
+ b2[0x13] = b1[0x13] + b1[0x1C];
+ b2[0x1C] = MUL((b1[0x1C] - b1[0x13]),COS_1_3);
+ b2[0x14] = b1[0x14] + b1[0x1B];
+ b2[0x1B] = MUL((b1[0x1B] - b1[0x14]),COS_1_4);
+ b2[0x15] = b1[0x15] + b1[0x1A];
+ b2[0x1A] = MUL((b1[0x1A] - b1[0x15]),COS_1_5);
+ b2[0x16] = b1[0x16] + b1[0x19];
+ b2[0x19] = MUL((b1[0x19] - b1[0x16]),COS_1_6);
+ b2[0x17] = b1[0x17] + b1[0x18];
+ b2[0x18] = MULL((b1[0x18] - b1[0x17]),COS_1_7);
+
+
+ b1[0x00] = b2[0x00] + b2[0x07];
+ b1[0x07] = MUL((b2[0x00] - b2[0x07]),COS_2_0);
+ b1[0x01] = b2[0x01] + b2[0x06];
+ b1[0x06] = MUL((b2[0x01] - b2[0x06]),COS_2_1);
+ b1[0x02] = b2[0x02] + b2[0x05];
+ b1[0x05] = MUL((b2[0x02] - b2[0x05]),COS_2_2);
+ b1[0x03] = b2[0x03] + b2[0x04];
+ b1[0x04] = MULL((b2[0x03] - b2[0x04]),COS_2_3);
+
+ b1[0x08] = b2[0x08] + b2[0x0F];
+ b1[0x0F] = MUL((b2[0x0F] - b2[0x08]),COS_2_0);
+ b1[0x09] = b2[0x09] + b2[0x0E];
+ b1[0x0E] = MUL((b2[0x0E] - b2[0x09]),COS_2_1);
+ b1[0x0A] = b2[0x0A] + b2[0x0D];
+ b1[0x0D] = MUL((b2[0x0D] - b2[0x0A]),COS_2_2);
+ b1[0x0B] = b2[0x0B] + b2[0x0C];
+ b1[0x0C] = MULL((b2[0x0C] - b2[0x0B]),COS_2_3);
+
+ b1[0x10] = b2[0x10] + b2[0x17];
+ b1[0x17] = MUL((b2[0x10] - b2[0x17]),COS_2_0);
+ b1[0x11] = b2[0x11] + b2[0x16];
+ b1[0x16] = MUL((b2[0x11] - b2[0x16]),COS_2_1);
+ b1[0x12] = b2[0x12] + b2[0x15];
+ b1[0x15] = MUL((b2[0x12] - b2[0x15]),COS_2_2);
+ b1[0x13] = b2[0x13] + b2[0x14];
+ b1[0x14] = MULL((b2[0x13] - b2[0x14]),COS_2_3);
+
+ b1[0x18] = b2[0x18] + b2[0x1F];
+ b1[0x1F] = MUL((b2[0x1F] - b2[0x18]),COS_2_0);
+ b1[0x19] = b2[0x19] + b2[0x1E];
+ b1[0x1E] = MUL((b2[0x1E] - b2[0x19]),COS_2_1);
+ b1[0x1A] = b2[0x1A] + b2[0x1D];
+ b1[0x1D] = MUL((b2[0x1D] - b2[0x1A]),COS_2_2);
+ b1[0x1B] = b2[0x1B] + b2[0x1C];
+ b1[0x1C] = MULL((b2[0x1C] - b2[0x1B]),COS_2_3);
+
+
+ b2[0x00] = b1[0x00] + b1[0x03];
+ b2[0x03] = MUL((b1[0x00] - b1[0x03]),COS_3_0);
+ b2[0x01] = b1[0x01] + b1[0x02];
+ b2[0x02] = MUL((b1[0x01] - b1[0x02]),COS_3_1);
+
+ b2[0x04] = b1[0x04] + b1[0x07];
+ b2[0x07] = MUL((b1[0x07] - b1[0x04]),COS_3_0);
+ b2[0x05] = b1[0x05] + b1[0x06];
+ b2[0x06] = MUL((b1[0x06] - b1[0x05]),COS_3_1);
+
+ b2[0x08] = b1[0x08] + b1[0x0B];
+ b2[0x0B] = MUL((b1[0x08] - b1[0x0B]),COS_3_0);
+ b2[0x09] = b1[0x09] + b1[0x0A];
+ b2[0x0A] = MUL((b1[0x09] - b1[0x0A]),COS_3_1);
+
+ b2[0x0C] = b1[0x0C] + b1[0x0F];
+ b2[0x0F] = MUL((b1[0x0F] - b1[0x0C]),COS_3_0);
+ b2[0x0D] = b1[0x0D] + b1[0x0E];
+ b2[0x0E] = MUL((b1[0x0E] - b1[0x0D]),COS_3_1);
+
+ b2[0x10] = b1[0x10] + b1[0x13];
+ b2[0x13] = MUL((b1[0x10] - b1[0x13]),COS_3_0);
+ b2[0x11] = b1[0x11] + b1[0x12];
+ b2[0x12] = MUL((b1[0x11] - b1[0x12]),COS_3_1);
+
+ b2[0x14] = b1[0x14] + b1[0x17];
+ b2[0x17] = MUL((b1[0x17] - b1[0x14]),COS_3_0);
+ b2[0x15] = b1[0x15] + b1[0x16];
+ b2[0x16] = MUL((b1[0x16] - b1[0x15]),COS_3_1);
+
+ b2[0x18] = b1[0x18] + b1[0x1B];
+ b2[0x1B] = MUL((b1[0x18] - b1[0x1B]),COS_3_0);
+ b2[0x19] = b1[0x19] + b1[0x1A];
+ b2[0x1A] = MUL((b1[0x19] - b1[0x1A]),COS_3_1);
+
+ b2[0x1C] = b1[0x1C] + b1[0x1F];
+ b2[0x1F] = MUL((b1[0x1F] - b1[0x1C]),COS_3_0);
+ b2[0x1D] = b1[0x1D] + b1[0x1E];
+ b2[0x1E] = MUL((b1[0x1E] - b1[0x1D]),COS_3_1);
+
+ {
+ int i;
+ for(i=0;i<32;i+=4) {
+ b1[i+0x00] = b2[i+0x00] + b2[i+0x01];
+ b1[i+0x01] = MUL((b2[i+0x00] - b2[i+0x01]),COS_4_0);
+ b1[i+0x02] = b2[i+0x02] + b2[i+0x03];
+ b1[i+0x03] = MUL((b2[i+0x03] - b2[i+0x02]),COS_4_0);
+ }
+ }
+
+ b1[0x02] += b1[0x03];
+ b1[0x06] += b1[0x07];
+ b1[0x04] += b1[0x06];
+ b1[0x06] += b1[0x05];
+ b1[0x05] += b1[0x07];
+
+ b1[0x0A] += b1[0x0B];
+ b1[0x0E] += b1[0x0F];
+ b1[0x0C] += b1[0x0E];
+ b1[0x0E] += b1[0x0D];
+ b1[0x0D] += b1[0x0F];
+
+ b1[0x12] += b1[0x13];
+ b1[0x16] += b1[0x17];
+ b1[0x14] += b1[0x16];
+ b1[0x16] += b1[0x15];
+ b1[0x15] += b1[0x17];
+
+ b1[0x1A] += b1[0x1B];
+ b1[0x1E] += b1[0x1F];
+ b1[0x1C] += b1[0x1E];
+ b1[0x1E] += b1[0x1D];
+ b1[0x1D] += b1[0x1F];
+
+ SETOUT(out0,16,b1[0x00]);
+ SETOUT(out0,12,b1[0x04]);
+ SETOUT(out0, 8,b1[0x02]);
+ SETOUT(out0, 4,b1[0x06]);
+ SETOUT(out0, 0,b1[0x01]);
+ SETOUT(out1, 0,b1[0x01]);
+ SETOUT(out1, 4,b1[0x05]);
+ SETOUT(out1, 8,b1[0x03]);
+ SETOUT(out1,12,b1[0x07]);
+
+ b1[0x08] += b1[0x0C];
+ SETOUT(out0,14,b1[0x08]);
+ b1[0x0C] += b1[0x0a];
+ SETOUT(out0,10,b1[0x0C]);
+ b1[0x0A] += b1[0x0E];
+ SETOUT(out0, 6,b1[0x0A]);
+ b1[0x0E] += b1[0x09];
+ SETOUT(out0, 2,b1[0x0E]);
+ b1[0x09] += b1[0x0D];
+ SETOUT(out1, 2,b1[0x09]);
+ b1[0x0D] += b1[0x0B];
+ SETOUT(out1, 6,b1[0x0D]);
+ b1[0x0B] += b1[0x0F];
+ SETOUT(out1,10,b1[0x0B]);
+ SETOUT(out1,14,b1[0x0F]);
+
+ b1[0x18] += b1[0x1C];
+ SETOUT(out0,15,b1[0x10] + b1[0x18]);
+ SETOUT(out0,13,b1[0x18] + b1[0x14]);
+ b1[0x1C] += b1[0x1a];
+ SETOUT(out0,11,b1[0x14] + b1[0x1C]);
+ SETOUT(out0, 9,b1[0x1C] + b1[0x12]);
+ b1[0x1A] += b1[0x1E];
+ SETOUT(out0, 7,b1[0x12] + b1[0x1A]);
+ SETOUT(out0, 5,b1[0x1A] + b1[0x16]);
+ b1[0x1E] += b1[0x19];
+ SETOUT(out0, 3,b1[0x16] + b1[0x1E]);
+ SETOUT(out0, 1,b1[0x1E] + b1[0x11]);
+ b1[0x19] += b1[0x1D];
+ SETOUT(out1, 1,b1[0x11] + b1[0x19]);
+ SETOUT(out1, 3,b1[0x19] + b1[0x15]);
+ b1[0x1D] += b1[0x1B];
+ SETOUT(out1, 5,b1[0x15] + b1[0x1D]);
+ SETOUT(out1, 7,b1[0x1D] + b1[0x13]);
+ b1[0x1B] += b1[0x1F];
+ SETOUT(out1, 9,b1[0x13] + b1[0x1B]);
+ SETOUT(out1,11,b1[0x1B] + b1[0x17]);
+ SETOUT(out1,13,b1[0x17] + b1[0x1F]);
+ SETOUT(out1,15,b1[0x1F]);
+}
+
+
+/*
+ * the call via dct64 is a trick to force GCC to use
+ * (new) registers for the b1,b2 pointer to the bufs[xx] field
+ */
+void dct64_486(int *a,int *b,real *samples)
+{
+ int bufs[64];
+ int i;
+
+ for(i=0;i<32;i++) {
+ bufs[i+32]=TOINT(samples[i]);
+ }
+
+ dct64_1_486(a,b,bufs,bufs+0x20);
+}
+
diff --git a/mpg123_artsplugin/mpg123/decode.c b/mpg123_artsplugin/mpg123/decode.c
new file mode 100644
index 00000000..0c7036a8
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/decode.c
@@ -0,0 +1,223 @@
+/*
+ * Mpeg Layer-1,2,3 audio decoder
+ * ------------------------------
+ * copyright (c) 1995,1996,1997 by Michael Hipp, All rights reserved.
+ * See also 'README'
+ *
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "mpg123.h"
+
+#define WRITE_SAMPLE(samples,sum,clip) \
+ if( (sum) > REAL_PLUS_32767) { *(samples) = 0x7fff; (clip)++; } \
+ else if( (sum) < REAL_MINUS_32768) { *(samples) = -0x8000; (clip)++; } \
+ else { *(samples) = REAL_TO_SHORT(sum); }
+
+int synth_1to1_8bit(real *bandPtr,int channel,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[64];
+ short *tmp1 = samples_tmp + channel;
+ int i,ret;
+ int pnt1=0;
+
+ ret = synth_1to1(bandPtr,channel,(unsigned char *) samples_tmp,&pnt1);
+ samples += channel + *pnt;
+
+ for(i=0;i<32;i++) {
+ *samples = conv16to8[*tmp1>>AUSHIFT];
+ samples += 2;
+ tmp1 += 2;
+ }
+ *pnt += 64;
+
+ return ret;
+}
+
+int synth_1to1_8bit_mono(real *bandPtr,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[64];
+ short *tmp1 = samples_tmp;
+ int i,ret;
+ int pnt1 = 0;
+
+ ret = synth_1to1(bandPtr,0,(unsigned char *) samples_tmp,&pnt1);
+ samples += *pnt;
+
+ for(i=0;i<32;i++) {
+ *samples++ = conv16to8[*tmp1>>AUSHIFT];
+ tmp1 += 2;
+ }
+ *pnt += 32;
+
+ return ret;
+}
+
+int synth_1to1_8bit_mono2stereo(real *bandPtr,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[64];
+ short *tmp1 = samples_tmp;
+ int i,ret;
+ int pnt1 = 0;
+
+ ret = synth_1to1(bandPtr,0,(unsigned char *) samples_tmp,&pnt1);
+ samples += *pnt;
+
+ for(i=0;i<32;i++) {
+ *samples++ = conv16to8[*tmp1>>AUSHIFT];
+ *samples++ = conv16to8[*tmp1>>AUSHIFT];
+ tmp1 += 2;
+ }
+ *pnt += 64;
+
+ return ret;
+}
+
+int synth_1to1_mono(real *bandPtr,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[64];
+ short *tmp1 = samples_tmp;
+ int i,ret;
+ int pnt1 = 0;
+
+ ret = synth_1to1(bandPtr,0,(unsigned char *) samples_tmp,&pnt1);
+ samples += *pnt;
+
+ for(i=0;i<32;i++) {
+ *( (short *)samples) = *tmp1;
+ samples += 2;
+ tmp1 += 2;
+ }
+ *pnt += 64;
+
+ return ret;
+}
+
+
+int synth_1to1_mono2stereo(real *bandPtr,unsigned char *samples,int *pnt)
+{
+ int i,ret;
+
+ ret = synth_1to1(bandPtr,0,samples,pnt);
+ samples = samples + *pnt - 128;
+
+ for(i=0;i<32;i++) {
+ ((short *)samples)[1] = ((short *)samples)[0];
+ samples+=4;
+ }
+
+ return ret;
+}
+
+
+int synth_1to1(real *bandPtr,int channel,unsigned char *out,int *pnt)
+{
+ static real buffs[2][2][0x110];
+ static const int step = 2;
+ static int bo = 1;
+ short *samples = (short *) (out+*pnt);
+
+ real *b0,(*buf)[0x110];
+ int clip = 0;
+ int bo1;
+
+#ifndef NO_EQUALIZER
+ if(param.enable_equalizer)
+ do_equalizer(bandPtr,channel);
+#endif
+
+ if(!channel) {
+ bo--;
+ bo &= 0xf;
+ buf = buffs[0];
+ }
+ else {
+ samples++;
+ buf = buffs[1];
+ }
+
+ if(bo & 0x1) {
+ b0 = buf[0];
+ bo1 = bo;
+ dct64(buf[1]+((bo+1)&0xf),buf[0]+bo,bandPtr);
+ }
+ else {
+ b0 = buf[1];
+ bo1 = bo+1;
+ dct64(buf[0]+bo,buf[1]+bo+1,bandPtr);
+ }
+
+
+ {
+ register int j;
+ real *window = decwin + 16 - bo1;
+
+ for (j=16;j;j--,window+=0x10,samples+=step)
+ {
+ real sum;
+ sum = REAL_MUL(*window++, *b0++);
+ sum -= REAL_MUL(*window++, *b0++);
+ sum += REAL_MUL(*window++, *b0++);
+ sum -= REAL_MUL(*window++, *b0++);
+ sum += REAL_MUL(*window++, *b0++);
+ sum -= REAL_MUL(*window++, *b0++);
+ sum += REAL_MUL(*window++, *b0++);
+ sum -= REAL_MUL(*window++, *b0++);
+ sum += REAL_MUL(*window++, *b0++);
+ sum -= REAL_MUL(*window++, *b0++);
+ sum += REAL_MUL(*window++, *b0++);
+ sum -= REAL_MUL(*window++, *b0++);
+ sum += REAL_MUL(*window++, *b0++);
+ sum -= REAL_MUL(*window++, *b0++);
+ sum += REAL_MUL(*window++, *b0++);
+ sum -= REAL_MUL(*window++, *b0++);
+
+ WRITE_SAMPLE(samples,sum,clip);
+ }
+
+ {
+ real sum;
+ sum = REAL_MUL(window[0x0], b0[0x0]);
+ sum += REAL_MUL(window[0x2], b0[0x2]);
+ sum += REAL_MUL(window[0x4], b0[0x4]);
+ sum += REAL_MUL(window[0x6], b0[0x6]);
+ sum += REAL_MUL(window[0x8], b0[0x8]);
+ sum += REAL_MUL(window[0xA], b0[0xA]);
+ sum += REAL_MUL(window[0xC], b0[0xC]);
+ sum += REAL_MUL(window[0xE], b0[0xE]);
+ WRITE_SAMPLE(samples,sum,clip);
+ b0-=0x10,window-=0x20,samples+=step;
+ }
+ window += bo1<<1;
+
+ for (j=15;j;j--,b0-=0x20,window-=0x10,samples+=step)
+ {
+ real sum;
+ sum = -REAL_MUL(*(--window), *b0++);
+ sum -= REAL_MUL(*(--window), *b0++);
+ sum -= REAL_MUL(*(--window), *b0++);
+ sum -= REAL_MUL(*(--window), *b0++);
+ sum -= REAL_MUL(*(--window), *b0++);
+ sum -= REAL_MUL(*(--window), *b0++);
+ sum -= REAL_MUL(*(--window), *b0++);
+ sum -= REAL_MUL(*(--window), *b0++);
+ sum -= REAL_MUL(*(--window), *b0++);
+ sum -= REAL_MUL(*(--window), *b0++);
+ sum -= REAL_MUL(*(--window), *b0++);
+ sum -= REAL_MUL(*(--window), *b0++);
+ sum -= REAL_MUL(*(--window), *b0++);
+ sum -= REAL_MUL(*(--window), *b0++);
+ sum -= REAL_MUL(*(--window), *b0++);
+ sum -= REAL_MUL(*(--window), *b0++);
+
+ WRITE_SAMPLE(samples,sum,clip);
+ }
+ }
+
+ *pnt += 128;
+
+ return clip;
+}
diff --git a/mpg123_artsplugin/mpg123/decode_2to1.c b/mpg123_artsplugin/mpg123/decode_2to1.c
new file mode 100644
index 00000000..2eb5e07d
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/decode_2to1.c
@@ -0,0 +1,233 @@
+/*
+ * Mpeg Layer-1,2,3 audio decoder
+ * ------------------------------
+ * copyright (c) 1995 by Michael Hipp, All rights reserved. See also 'README'
+ * version for slower machines .. decodes only every second sample
+ * sounds like 24000,22050 or 16000 kHz .. (depending on original sample freq.)
+ *
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "mpg123.h"
+
+#define WRITE_SAMPLE(samples,sum,clip) \
+ if( (sum) > 32767.0) { *(samples) = 0x7fff; (clip)++; } \
+ else if( (sum) < -32768.0) { *(samples) = -0x8000; (clip)++; } \
+ else { *(samples) = sum; }
+
+int synth_2to1_8bit(real *bandPtr,int channel,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[32];
+ short *tmp1 = samples_tmp + channel;
+ int i,ret;
+ int pnt1 = 0;
+
+ ret = synth_2to1(bandPtr,channel,(unsigned char *) samples_tmp,&pnt1);
+ samples += channel + *pnt;
+
+ for(i=0;i<16;i++) {
+ *samples = conv16to8[*tmp1>>AUSHIFT];
+ samples += 2;
+ tmp1 += 2;
+ }
+ *pnt += 32;
+
+ return ret;
+}
+
+int synth_2to1_8bit_mono(real *bandPtr,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[32];
+ short *tmp1 = samples_tmp;
+ int i,ret;
+ int pnt1 = 0;
+
+ ret = synth_2to1(bandPtr,0,(unsigned char *) samples_tmp,&pnt1);
+ samples += *pnt;
+
+ for(i=0;i<16;i++) {
+ *samples++ = conv16to8[*tmp1>>AUSHIFT];
+ tmp1 += 2;
+ }
+ *pnt += 16;
+
+ return ret;
+}
+
+
+int synth_2to1_8bit_mono2stereo(real *bandPtr,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[32];
+ short *tmp1 = samples_tmp;
+ int i,ret;
+ int pnt1 = 0;
+
+ ret = synth_2to1(bandPtr,0,(unsigned char *) samples_tmp,&pnt1);
+ samples += *pnt;
+
+ for(i=0;i<16;i++) {
+ *samples++ = conv16to8[*tmp1>>AUSHIFT];
+ *samples++ = conv16to8[*tmp1>>AUSHIFT];
+ tmp1 += 2;
+ }
+ *pnt += 32;
+
+ return ret;
+}
+
+int synth_2to1_mono(real *bandPtr,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[32];
+ short *tmp1 = samples_tmp;
+ int i,ret;
+ int pnt1=0;
+
+ ret = synth_2to1(bandPtr,0,(unsigned char *) samples_tmp,&pnt1);
+ samples += *pnt;
+
+ for(i=0;i<16;i++) {
+ *( (short *) samples) = *tmp1;
+ samples += 2;
+ tmp1 += 2;
+ }
+ *pnt += 32;
+
+ return ret;
+}
+
+int synth_2to1_mono2stereo(real *bandPtr,unsigned char *samples,int *pnt)
+{
+ int i,ret;
+
+ ret = synth_2to1(bandPtr,0,samples,pnt);
+ samples = samples + *pnt - 64;
+
+ for(i=0;i<16;i++) {
+ ((short *)samples)[1] = ((short *)samples)[0];
+ samples+=4;
+ }
+
+ return ret;
+}
+
+int synth_2to1(real *bandPtr,int channel,unsigned char *out,int *pnt)
+{
+ static real buffs[2][2][0x110];
+ static const int step = 2;
+ static int bo = 1;
+ short *samples = (short *) (out + *pnt);
+
+ real *b0,(*buf)[0x110];
+ int clip = 0;
+ int bo1;
+
+#ifndef NO_EQUALIZER
+ if(param.enable_equalizer)
+ do_equalizer(bandPtr,channel);
+#endif
+
+ if(!channel) {
+ bo--;
+ bo &= 0xf;
+ buf = buffs[0];
+ }
+ else {
+ samples++;
+ buf = buffs[1];
+ }
+
+ if(bo & 0x1) {
+ b0 = buf[0];
+ bo1 = bo;
+ dct64(buf[1]+((bo+1)&0xf),buf[0]+bo,bandPtr);
+ }
+ else {
+ b0 = buf[1];
+ bo1 = bo+1;
+ dct64(buf[0]+bo,buf[1]+bo+1,bandPtr);
+ }
+
+ {
+ register int j;
+ real *window = decwin + 16 - bo1;
+
+ for (j=8;j;j--,b0+=0x10,window+=0x30)
+ {
+ real sum;
+ sum = *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+
+ WRITE_SAMPLE(samples,sum,clip); samples += step;
+#if 0
+ WRITE_SAMPLE(samples,sum,clip); samples += step;
+#endif
+ }
+
+ {
+ real sum;
+ sum = window[0x0] * b0[0x0];
+ sum += window[0x2] * b0[0x2];
+ sum += window[0x4] * b0[0x4];
+ sum += window[0x6] * b0[0x6];
+ sum += window[0x8] * b0[0x8];
+ sum += window[0xA] * b0[0xA];
+ sum += window[0xC] * b0[0xC];
+ sum += window[0xE] * b0[0xE];
+ WRITE_SAMPLE(samples,sum,clip); samples += step;
+#if 0
+ WRITE_SAMPLE(samples,sum,clip); samples += step;
+#endif
+ b0-=0x20,window-=0x40;
+ }
+ window += bo1<<1;
+
+ for (j=7;j;j--,b0-=0x30,window-=0x30)
+ {
+ real sum;
+ sum = -*(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+
+ WRITE_SAMPLE(samples,sum,clip); samples += step;
+#if 0
+ WRITE_SAMPLE(samples,sum,clip); samples += step;
+#endif
+ }
+ }
+
+ *pnt += 64;
+
+ return clip;
+}
+
+
diff --git a/mpg123_artsplugin/mpg123/decode_3dnow.s b/mpg123_artsplugin/mpg123/decode_3dnow.s
new file mode 100644
index 00000000..fd39429a
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/decode_3dnow.s
@@ -0,0 +1,279 @@
+#
+# decode_3dnow.s - 3DNow! optimized synth_1to1()
+#
+# This code based 'decode_3dnow.s' by Syuuhei Kashiyama
+# <squash@mb.kcom.ne.jp>,only two types of changes have been made:
+#
+# - remove PREFETCH instruction for speedup
+# - change function name for support 3DNow! automatic detect
+# - femms moved to before 'call dct64_3dnow'
+#
+# You can find Kashiyama's original 3dnow! support patch
+# (for mpg123-0.59o) at
+# http:#/user.ecc.u-tokyo.ac.jp/~g810370/linux-simd/ (Japanese).
+#
+# by KIMURA Takuhiro <kim@hannah.ipc.miyakyo-u.ac.jp> - until 31.Mar.1999
+# <kim@comtec.co.jp> - after 1.Apr.1999
+#
+
+##/
+##/ Replacement of synth_1to1() with AMD's 3DNow! SIMD operations support
+##/
+##/ Syuuhei Kashiyama <squash@mb.kcom.ne.jp>
+##/
+##/ The author of this program disclaim whole expressed or implied
+##/ warranties with regard to this program, and in no event shall the
+##/ author of this program liable to whatever resulted from the use of
+##/ this program. Use it at your own risk.
+##/
+
+ .local buffs.40
+ .comm buffs.40,4352,32
+.data
+ .align 4
+ .type bo.42,@object
+ .size bo.42,4
+bo.42:
+ .long 1
+.text
+.globl synth_1to1_3dnow
+ .type synth_1to1_3dnow,@function
+synth_1to1_3dnow:
+ subl $24,%esp
+ pushl %ebp
+ pushl %edi
+ xorl %ebp,%ebp
+ pushl %esi
+ pushl %ebx
+ movl 56(%esp),%esi
+ movl 52(%esp),%edi
+ movl 0(%esi),%esi
+ movl 48(%esp),%ebx
+ addl %edi,%esi
+ movl %esi,16(%esp)
+
+ femms
+
+ # fixed by Takuhiro
+ cmpl $0,param+348
+ je .L25
+ pushl %ebx
+ pushl 48(%esp)
+ call do_equalizer_3dnow
+ addl $8,%esp
+.L25:
+ testl %ebx,%ebx
+ jne .L26
+ decl bo.42
+ movl $buffs.40,%ecx
+ andl $15,bo.42
+ jmp .L27
+.L26:
+ addl $2,16(%esp)
+ movl $buffs.40+2176,%ecx
+.L27:
+ movl bo.42,%edx
+ testb $1,%dl
+ je .L28
+ movl %edx,36(%esp)
+ movl %ecx,%ebx
+ movl 44(%esp),%esi
+ movl %edx,%edi
+ pushl %esi
+ sall $2,%edi
+ movl %ebx,%eax
+ movl %edi,24(%esp)
+ addl %edi,%eax
+ pushl %eax
+ movl %edx,%eax
+ incl %eax
+ andl $15,%eax
+ leal 1088(,%eax,4),%eax
+ addl %ebx,%eax
+ pushl %eax
+ call dct64_3dnow
+ addl $12,%esp
+ jmp .L29
+.L28:
+ leal 1(%edx),%esi
+ movl 44(%esp),%edi
+ movl %esi,36(%esp)
+ leal 1092(%ecx,%edx,4),%eax
+ pushl %edi
+ leal 1088(%ecx),%ebx
+ pushl %eax
+ sall $2,%esi
+ leal (%ecx,%edx,4),%eax
+ pushl %eax
+ call dct64_3dnow
+ addl $12,%esp
+ movl %esi,20(%esp)
+.L29:
+ movl $decwin+64,%edx
+ movl $16,%ecx
+ subl 20(%esp),%edx
+ movl 16(%esp),%edi
+
+ movq (%edx),%mm0
+ movq (%ebx),%mm1
+ .align 32
+.L33:
+ movq 8(%edx),%mm3
+ pfmul %mm1,%mm0
+ movq 8(%ebx),%mm4
+ movq 16(%edx),%mm5
+ pfmul %mm4,%mm3
+ movq 16(%ebx),%mm6
+ pfadd %mm3,%mm0
+ movq 24(%edx),%mm1
+ pfmul %mm6,%mm5
+ movq 24(%ebx),%mm2
+ pfadd %mm5,%mm0
+ movq 32(%edx),%mm3
+ pfmul %mm2,%mm1
+ movq 32(%ebx),%mm4
+ pfadd %mm1,%mm0
+ movq 40(%edx),%mm5
+ pfmul %mm4,%mm3
+ movq 40(%ebx),%mm6
+ pfadd %mm3,%mm0
+ movq 48(%edx),%mm1
+ pfmul %mm6,%mm5
+ movq 48(%ebx),%mm2
+ pfadd %mm0,%mm5
+ movq 56(%edx),%mm3
+ pfmul %mm1,%mm2
+ movq 56(%ebx),%mm4
+ pfadd %mm5,%mm2
+ addl $64,%ebx
+ subl $-128,%edx
+ movq (%edx),%mm0
+ pfmul %mm4,%mm3
+ movq (%ebx),%mm1
+ pfadd %mm3,%mm2
+ movq %mm2,%mm3
+ psrlq $32,%mm3
+ pfsub %mm3,%mm2
+ incl %ebp
+ pf2id %mm2,%mm2
+ packssdw %mm2,%mm2
+ movd %mm2,%eax
+ movw %ax,0(%edi)
+ addl $4,%edi
+ decl %ecx
+ jnz .L33
+
+ movd (%ebx),%mm0
+ movd (%edx),%mm1
+ punpckldq 8(%ebx),%mm0
+ punpckldq 8(%edx),%mm1
+ movd 16(%ebx),%mm3
+ movd 16(%edx),%mm4
+ pfmul %mm1,%mm0
+ punpckldq 24(%ebx),%mm3
+ punpckldq 24(%edx),%mm4
+ movd 32(%ebx),%mm5
+ movd 32(%edx),%mm6
+ pfmul %mm4,%mm3
+ punpckldq 40(%ebx),%mm5
+ punpckldq 40(%edx),%mm6
+ pfadd %mm3,%mm0
+ movd 48(%ebx),%mm1
+ movd 48(%edx),%mm2
+ pfmul %mm6,%mm5
+ punpckldq 56(%ebx),%mm1
+ punpckldq 56(%edx),%mm2
+ pfadd %mm5,%mm0
+ pfmul %mm2,%mm1
+ pfadd %mm1,%mm0
+ pfacc %mm1,%mm0
+ pf2id %mm0,%mm0
+ packssdw %mm0,%mm0
+ movd %mm0,%eax
+ movw %ax,0(%edi)
+ incl %ebp
+ movl 36(%esp),%esi
+ addl $-64,%ebx
+ movl $15,%ebp
+ addl $4,%edi
+ leal -128(%edx,%esi,8),%edx
+
+ movl $15,%ecx
+ movd (%ebx),%mm0
+ movd -4(%edx),%mm1
+ punpckldq 4(%ebx),%mm0
+ punpckldq -8(%edx),%mm1
+ .align 32
+.L46:
+ movd 8(%ebx),%mm3
+ movd -12(%edx),%mm4
+ pfmul %mm1,%mm0
+ punpckldq 12(%ebx),%mm3
+ punpckldq -16(%edx),%mm4
+ movd 16(%ebx),%mm5
+ movd -20(%edx),%mm6
+ pfmul %mm4,%mm3
+ punpckldq 20(%ebx),%mm5
+ punpckldq -24(%edx),%mm6
+ pfadd %mm3,%mm0
+ movd 24(%ebx),%mm1
+ movd -28(%edx),%mm2
+ pfmul %mm6,%mm5
+ punpckldq 28(%ebx),%mm1
+ punpckldq -32(%edx),%mm2
+ pfadd %mm5,%mm0
+ movd 32(%ebx),%mm3
+ movd -36(%edx),%mm4
+ pfmul %mm2,%mm1
+ punpckldq 36(%ebx),%mm3
+ punpckldq -40(%edx),%mm4
+ pfadd %mm1,%mm0
+ movd 40(%ebx),%mm5
+ movd -44(%edx),%mm6
+ pfmul %mm4,%mm3
+ punpckldq 44(%ebx),%mm5
+ punpckldq -48(%edx),%mm6
+ pfadd %mm3,%mm0
+ movd 48(%ebx),%mm1
+ movd -52(%edx),%mm2
+ pfmul %mm6,%mm5
+ punpckldq 52(%ebx),%mm1
+ punpckldq -56(%edx),%mm2
+ pfadd %mm0,%mm5
+ movd 56(%ebx),%mm3
+ movd -60(%edx),%mm4
+ pfmul %mm2,%mm1
+ punpckldq 60(%ebx),%mm3
+ punpckldq (%edx),%mm4
+ pfadd %mm1,%mm5
+ addl $-128,%edx
+ addl $-64,%ebx
+ movd (%ebx),%mm0
+ movd -4(%edx),%mm1
+ pfmul %mm4,%mm3
+ punpckldq 4(%ebx),%mm0
+ punpckldq -8(%edx),%mm1
+ pfadd %mm5,%mm3
+ pfacc %mm3,%mm3
+ incl %ebp
+ pf2id %mm3,%mm3
+ movd %mm3,%eax
+ negl %eax
+ movd %eax,%mm3
+ packssdw %mm3,%mm3
+ movd %mm3,%eax
+ movw %ax,(%edi)
+ addl $4,%edi
+ decl %ecx
+ jnz .L46
+
+ femms
+ movl 56(%esp),%esi
+ movl %ebp,%eax
+ subl $-128,0(%esi)
+ popl %ebx
+ popl %esi
+ popl %edi
+ popl %ebp
+ addl $24,%esp
+ ret
diff --git a/mpg123_artsplugin/mpg123/decode_4to1.c b/mpg123_artsplugin/mpg123/decode_4to1.c
new file mode 100644
index 00000000..00f6b038
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/decode_4to1.c
@@ -0,0 +1,240 @@
+/*
+ * Mpeg Layer-1,2,3 audio decoder
+ * ------------------------------
+ * copyright (c) 1995,1996,1997 by Michael Hipp, All rights reserved.
+ * See also 'README'
+ * version for slower machines .. decodes only every fourth sample
+ * dunno why it sounds THIS annoying (maybe we should adapt the window?)
+ * absolutely not optimized for this operation
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "mpg123.h"
+
+#define WRITE_SAMPLE(samples,sum,clip) \
+ if( (sum) > 32767.0) { *(samples) = 0x7fff; (clip)++; } \
+ else if( (sum) < -32768.0) { *(samples) = -0x8000; (clip)++; } \
+ else { *(samples) = sum; }
+
+int synth_4to1_8bit(real *bandPtr,int channel,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[16];
+ short *tmp1 = samples_tmp + channel;
+ int i,ret;
+ int pnt1 = 0;
+
+ ret = synth_4to1(bandPtr,channel,(unsigned char *) samples_tmp,&pnt1);
+ samples += channel + *pnt;
+
+ for(i=0;i<8;i++) {
+ *samples = conv16to8[*tmp1>>AUSHIFT];
+ samples += 2;
+ tmp1 += 2;
+ }
+ *pnt += 16;
+
+ return ret;
+}
+
+int synth_4to1_8bit_mono(real *bandPtr,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[16];
+ short *tmp1 = samples_tmp;
+ int i,ret;
+ int pnt1 = 0;
+
+ ret = synth_4to1(bandPtr,0,(unsigned char *) samples_tmp,&pnt1);
+ samples += *pnt;
+
+ for(i=0;i<8;i++) {
+ *samples++ = conv16to8[*tmp1>>AUSHIFT];
+ tmp1 += 2;
+ }
+ *pnt += 8;
+
+ return ret;
+}
+
+
+int synth_4to1_8bit_mono2stereo(real *bandPtr,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[16];
+ short *tmp1 = samples_tmp;
+ int i,ret;
+ int pnt1 = 0;
+
+ ret = synth_4to1(bandPtr,0,(unsigned char *) samples_tmp,&pnt1);
+ samples += *pnt;
+
+ for(i=0;i<8;i++) {
+ *samples++ = conv16to8[*tmp1>>AUSHIFT];
+ *samples++ = conv16to8[*tmp1>>AUSHIFT];
+ tmp1 += 2;
+ }
+ *pnt += 16;
+
+ return ret;
+}
+
+int synth_4to1_mono(real *bandPtr,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[16];
+ short *tmp1 = samples_tmp;
+ int i,ret;
+ int pnt1 = 0;
+
+ ret = synth_4to1(bandPtr,0,(unsigned char *) samples_tmp,&pnt1);
+ samples += *pnt;
+
+ for(i=0;i<8;i++) {
+ *( (short *)samples) = *tmp1;
+ samples += 2;
+ tmp1 += 2;
+ }
+ *pnt += 16;
+
+ return ret;
+}
+
+int synth_4to1_mono2stereo(real *bandPtr,unsigned char *samples,int *pnt)
+{
+ int i,ret;
+
+ ret = synth_4to1(bandPtr,0,samples,pnt);
+ samples = samples + *pnt - 32;
+
+ for(i=0;i<8;i++) {
+ ((short *)samples)[1] = ((short *)samples)[0];
+ samples+=4;
+ }
+
+ return ret;
+}
+
+int synth_4to1(real *bandPtr,int channel,unsigned char *out,int *pnt)
+{
+ static real buffs[2][2][0x110];
+ static const int step = 2;
+ static int bo = 1;
+ short *samples = (short *) (out + *pnt);
+
+ real *b0,(*buf)[0x110];
+ int clip = 0;
+ int bo1;
+
+#ifndef NO_EQUALIZER
+ if(param.enable_equalizer)
+ do_equalizer(bandPtr,channel);
+#endif
+
+ if(!channel) {
+ bo--;
+ bo &= 0xf;
+ buf = buffs[0];
+ }
+ else {
+ samples++;
+ buf = buffs[1];
+ }
+
+ if(bo & 0x1) {
+ b0 = buf[0];
+ bo1 = bo;
+ dct64(buf[1]+((bo+1)&0xf),buf[0]+bo,bandPtr);
+ }
+ else {
+ b0 = buf[1];
+ bo1 = bo+1;
+ dct64(buf[0]+bo,buf[1]+bo+1,bandPtr);
+ }
+
+ {
+ register int j;
+ real *window = decwin + 16 - bo1;
+
+ for (j=4;j;j--,b0+=0x30,window+=0x70)
+ {
+ real sum;
+ sum = *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+
+ WRITE_SAMPLE(samples,sum,clip); samples += step;
+#if 0
+ WRITE_SAMPLE(samples,sum,clip); samples += step;
+ WRITE_SAMPLE(samples,sum,clip); samples += step;
+ WRITE_SAMPLE(samples,sum,clip); samples += step;
+#endif
+ }
+
+ {
+ real sum;
+ sum = window[0x0] * b0[0x0];
+ sum += window[0x2] * b0[0x2];
+ sum += window[0x4] * b0[0x4];
+ sum += window[0x6] * b0[0x6];
+ sum += window[0x8] * b0[0x8];
+ sum += window[0xA] * b0[0xA];
+ sum += window[0xC] * b0[0xC];
+ sum += window[0xE] * b0[0xE];
+ WRITE_SAMPLE(samples,sum,clip); samples += step;
+#if 0
+ WRITE_SAMPLE(samples,sum,clip); samples += step;
+ WRITE_SAMPLE(samples,sum,clip); samples += step;
+ WRITE_SAMPLE(samples,sum,clip); samples += step;
+#endif
+ b0-=0x40,window-=0x80;
+ }
+ window += bo1<<1;
+
+ for (j=3;j;j--,b0-=0x50,window-=0x70)
+ {
+ real sum;
+ sum = -*(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+
+ WRITE_SAMPLE(samples,sum,clip); samples += step;
+#if 0
+ WRITE_SAMPLE(samples,sum,clip); samples += step;
+ WRITE_SAMPLE(samples,sum,clip); samples += step;
+ WRITE_SAMPLE(samples,sum,clip); samples += step;
+#endif
+ }
+ }
+
+ *pnt += 32;
+
+ return clip;
+}
+
+
diff --git a/mpg123_artsplugin/mpg123/decode_MMX.s b/mpg123_artsplugin/mpg123/decode_MMX.s
new file mode 100644
index 00000000..1c9a1adb
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/decode_MMX.s
@@ -0,0 +1,108 @@
+# this code comes under GPL
+
+.text
+
+.globl synth_1to1_MMX
+
+synth_1to1_MMX:
+ pushl %ebp
+ pushl %edi
+ pushl %esi
+ pushl %ebx
+ movl 24(%esp),%ecx
+ movl 28(%esp),%edi
+ movl $15,%ebx
+ movl 36(%esp),%edx
+ leal (%edi,%ecx,2),%edi
+ decl %ecx
+ movl 32(%esp),%esi
+ movl (%edx),%eax
+ jecxz .L1
+ decl %eax
+ andl %ebx,%eax
+ leal 1088(%esi),%esi
+ movl %eax,(%edx)
+.L1:
+ leal (%esi,%eax,2),%edx
+ movl %eax,%ebp
+ incl %eax
+ pushl 20(%esp)
+ andl %ebx,%eax
+ leal 544(%esi,%eax,2),%ecx
+ incl %ebx
+ testl $1, %eax
+ jnz .L2
+ xchgl %edx,%ecx
+ incl %ebp
+ leal 544(%esi),%esi
+.L2:
+ pushl %edx
+ pushl %ecx
+ call dct64_MMX
+ addl $12,%esp
+ leal 1(%ebx), %ecx
+ subl %ebp,%ebx
+
+ leal decwins(%ebx,%ebx,1), %edx
+.L3:
+ movq (%edx),%mm0
+ pmaddwd (%esi),%mm0
+ movq 8(%edx),%mm1
+ pmaddwd 8(%esi),%mm1
+ movq 16(%edx),%mm2
+ pmaddwd 16(%esi),%mm2
+ movq 24(%edx),%mm3
+ pmaddwd 24(%esi),%mm3
+ paddd %mm1,%mm0
+ paddd %mm2,%mm0
+ paddd %mm3,%mm0
+ movq %mm0,%mm1
+ psrlq $32,%mm1
+ paddd %mm1,%mm0
+ psrad $13,%mm0
+ packssdw %mm0,%mm0
+ movd %mm0,%eax
+ movw %ax, (%edi)
+
+ leal 32(%esi),%esi
+ leal 64(%edx),%edx
+ leal 4(%edi),%edi
+ loop .L3
+
+
+ subl $64,%esi
+ movl $15,%ecx
+.L4:
+ movq (%edx),%mm0
+ pmaddwd (%esi),%mm0
+ movq 8(%edx),%mm1
+ pmaddwd 8(%esi),%mm1
+ movq 16(%edx),%mm2
+ pmaddwd 16(%esi),%mm2
+ movq 24(%edx),%mm3
+ pmaddwd 24(%esi),%mm3
+ paddd %mm1,%mm0
+ paddd %mm2,%mm0
+ paddd %mm3,%mm0
+ movq %mm0,%mm1
+ psrlq $32,%mm1
+ paddd %mm0,%mm1
+ psrad $13,%mm1
+ packssdw %mm1,%mm1
+ psubd %mm0,%mm0
+ psubsw %mm1,%mm0
+ movd %mm0,%eax
+ movw %ax,(%edi)
+
+ subl $32,%esi
+ addl $64,%edx
+ leal 4(%edi),%edi
+ loop .L4
+ emms
+ popl %ebx
+ popl %esi
+ popl %edi
+ popl %ebp
+ ret
+
+
diff --git a/mpg123_artsplugin/mpg123/decode_i386.c b/mpg123_artsplugin/mpg123/decode_i386.c
new file mode 100644
index 00000000..d39795f8
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/decode_i386.c
@@ -0,0 +1,257 @@
+/*
+ * Mpeg Layer-1,2,3 audio decoder
+ * ------------------------------
+ * copyright (c) 1995,1996,1997 by Michael Hipp, All rights reserved.
+ * See also 'README'
+ *
+ * slighlty optimized for machines without autoincrement/decrement.
+ * The performance is highly compiler dependent. Maybe
+ * the decode.c version for 'normal' processor may be faster
+ * even for Intel processors.
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "mpg123.h"
+
+#if 0
+ /* old WRITE_SAMPLE */
+#define WRITE_SAMPLE(samples,sum,clip) \
+ if( (sum) > 32767.0) { *(samples) = 0x7fff; (clip)++; } \
+ else if( (sum) < -32768.0) { *(samples) = -0x8000; (clip)++; } \
+ else { *(samples) = sum; }
+#else
+ /* new WRITE_SAMPLE */
+#define WRITE_SAMPLE(samples,sum,clip) { \
+ double dtemp; int v; /* sizeof(int) == 4 */ \
+ dtemp = ((((65536.0 * 65536.0 * 16)+(65536.0 * 0.5))* 65536.0)) + (sum); \
+ v = ((*(int *)&dtemp) - 0x80000000); \
+ if( v > 32767) { *(samples) = 0x7fff; (clip)++; } \
+ else if( v < -32768) { *(samples) = -0x8000; (clip)++; } \
+ else { *(samples) = v; } \
+}
+#endif
+
+int synth_1to1_8bit(real *bandPtr,int channel,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[64];
+ short *tmp1 = samples_tmp + channel;
+ int i,ret;
+ int pnt1 = 0;
+
+ ret = synth_1to1(bandPtr,channel,(unsigned char *)samples_tmp,&pnt1);
+ samples += channel + *pnt;
+
+ for(i=0;i<32;i++) {
+ *samples = conv16to8[*tmp1>>AUSHIFT];
+ samples += 2;
+ tmp1 += 2;
+ }
+ *pnt += 64;
+
+ return ret;
+}
+
+int synth_1to1_8bit_mono(real *bandPtr,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[64];
+ short *tmp1 = samples_tmp;
+ int i,ret;
+ int pnt1 = 0;
+
+ ret = synth_1to1(bandPtr,0,(unsigned char *)samples_tmp,&pnt1);
+ samples += *pnt;
+
+ for(i=0;i<32;i++) {
+ *samples++ = conv16to8[*tmp1>>AUSHIFT];
+ tmp1+=2;
+ }
+ *pnt += 32;
+
+ return ret;
+}
+
+int synth_1to1_8bit_mono2stereo(real *bandPtr,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[64];
+ short *tmp1 = samples_tmp;
+ int i,ret;
+ int pnt1 = 0;
+
+ ret = synth_1to1(bandPtr,0,(unsigned char *)samples_tmp,&pnt1);
+ samples += *pnt;
+
+ for(i=0;i<32;i++) {
+ *samples++ = conv16to8[*tmp1>>AUSHIFT];
+ *samples++ = conv16to8[*tmp1>>AUSHIFT];
+ tmp1 += 2;
+ }
+ *pnt += 64;
+
+ return ret;
+}
+
+int synth_1to1_mono(real *bandPtr,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[64];
+ short *tmp1 = samples_tmp;
+ int i,ret;
+ int pnt1 = 0;
+
+ ret = synth_1to1(bandPtr,0,(unsigned char *) samples_tmp,&pnt1);
+ samples += *pnt;
+
+ for(i=0;i<32;i++) {
+ *( (short *) samples) = *tmp1;
+ samples += 2;
+ tmp1 += 2;
+ }
+ *pnt += 64;
+
+ return ret;
+}
+
+
+int synth_1to1_mono2stereo(real *bandPtr,unsigned char *samples,int *pnt)
+{
+ int i,ret;
+
+ ret = synth_1to1(bandPtr,0,samples,pnt);
+ samples = samples + *pnt - 128;
+
+ for(i=0;i<32;i++) {
+ ((short *)samples)[1] = ((short *)samples)[0];
+ samples+=4;
+ }
+
+ return ret;
+}
+
+int synth_1to1(real *bandPtr,int channel,unsigned char *out,int *pnt)
+{
+#ifndef PENTIUM_OPT
+ static real buffs[2][2][0x110];
+ static const int step = 2;
+ static int bo = 1;
+ short *samples = (short *) (out + *pnt);
+
+ real *b0,(*buf)[0x110];
+ int clip = 0;
+ int bo1;
+#endif
+
+#ifndef NO_EQUALIZER
+ if(param.enable_equalizer)
+ do_equalizer(bandPtr,channel);
+#endif
+
+#ifndef PENTIUM_OPT
+ if(!channel) {
+ bo--;
+ bo &= 0xf;
+ buf = buffs[0];
+ }
+ else {
+ samples++;
+ buf = buffs[1];
+ }
+
+ if(bo & 0x1) {
+ b0 = buf[0];
+ bo1 = bo;
+ dct64(buf[1]+((bo+1)&0xf),buf[0]+bo,bandPtr);
+ }
+ else {
+ b0 = buf[1];
+ bo1 = bo+1;
+ dct64(buf[0]+bo,buf[1]+bo+1,bandPtr);
+ }
+
+ {
+ register int j;
+ real *window = decwin + 16 - bo1;
+
+ for (j=16;j;j--,b0+=0x10,window+=0x20,samples+=step)
+ {
+ real sum;
+ sum = window[0x0] * b0[0x0];
+ sum -= window[0x1] * b0[0x1];
+ sum += window[0x2] * b0[0x2];
+ sum -= window[0x3] * b0[0x3];
+ sum += window[0x4] * b0[0x4];
+ sum -= window[0x5] * b0[0x5];
+ sum += window[0x6] * b0[0x6];
+ sum -= window[0x7] * b0[0x7];
+ sum += window[0x8] * b0[0x8];
+ sum -= window[0x9] * b0[0x9];
+ sum += window[0xA] * b0[0xA];
+ sum -= window[0xB] * b0[0xB];
+ sum += window[0xC] * b0[0xC];
+ sum -= window[0xD] * b0[0xD];
+ sum += window[0xE] * b0[0xE];
+ sum -= window[0xF] * b0[0xF];
+
+ WRITE_SAMPLE(samples,sum,clip);
+ }
+
+ {
+ real sum;
+ sum = window[0x0] * b0[0x0];
+ sum += window[0x2] * b0[0x2];
+ sum += window[0x4] * b0[0x4];
+ sum += window[0x6] * b0[0x6];
+ sum += window[0x8] * b0[0x8];
+ sum += window[0xA] * b0[0xA];
+ sum += window[0xC] * b0[0xC];
+ sum += window[0xE] * b0[0xE];
+ WRITE_SAMPLE(samples,sum,clip);
+ b0-=0x10,window-=0x20,samples+=step;
+ }
+ window += bo1<<1;
+
+ for (j=15;j;j--,b0-=0x10,window-=0x20,samples+=step)
+ {
+ real sum;
+ sum = -window[-0x1] * b0[0x0];
+ sum -= window[-0x2] * b0[0x1];
+ sum -= window[-0x3] * b0[0x2];
+ sum -= window[-0x4] * b0[0x3];
+ sum -= window[-0x5] * b0[0x4];
+ sum -= window[-0x6] * b0[0x5];
+ sum -= window[-0x7] * b0[0x6];
+ sum -= window[-0x8] * b0[0x7];
+ sum -= window[-0x9] * b0[0x8];
+ sum -= window[-0xA] * b0[0x9];
+ sum -= window[-0xB] * b0[0xA];
+ sum -= window[-0xC] * b0[0xB];
+ sum -= window[-0xD] * b0[0xC];
+ sum -= window[-0xE] * b0[0xD];
+ sum -= window[-0xF] * b0[0xE];
+ sum -= window[-0x0] * b0[0xF];
+
+ WRITE_SAMPLE(samples,sum,clip);
+ }
+ }
+ *pnt += 128;
+
+ return clip;
+#elif defined(USE_MMX)
+ {
+ static short buffs[2][2][0x110];
+ static int bo = 1;
+ short *samples = (short *) (out + *pnt);
+ synth_1to1_MMX(bandPtr, channel, samples, (short *) buffs, &bo);
+ *pnt += 128;
+ return 0;
+ }
+#else
+ {
+ int ret;
+ ret = synth_1to1_pent(bandPtr,channel,out+*pnt);
+ *pnt += 128;
+ return ret;
+ }
+#endif
+}
diff --git a/mpg123_artsplugin/mpg123/decode_i486.c b/mpg123_artsplugin/mpg123/decode_i486.c
new file mode 100644
index 00000000..b0378f32
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/decode_i486.c
@@ -0,0 +1,250 @@
+/*
+ * Subband Synthesis for MPEG Audio.
+ *
+ * Version optimized for 80486 by using integer arithmetic,
+ * multiplications by shift and add, and by increasing locality in
+ * order to fit the 8KB L1 cache. This code should be compiled with gcc
+ * 2.7.2 or higher.
+ *
+ * Note: this version does not guaranty a good accuracy. The filter
+ * coefficients are quantified on 14 bits.
+ *
+ * (c) 1998 Fabrice Bellard
+ *
+ * GPL clean
+ */
+
+#include <stdlib.h>
+
+#include "mpg123.h"
+
+#define FIR_SIZE 16
+
+#define FIR16_1(pos,c0,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15) \
+{\
+ int sum;\
+ sum=(c0)*b0[0]+(c1)*b0[1]+(c2)*b0[2]+(c3)*b0[3]+\
+ (c4)*b0[4]+(c5)*b0[5]+(c6)*b0[6]+(c7)*b0[7]+\
+ (c8)*b0[8]+(c9)*b0[9]+(c10)*b0[10]+(c11)*b0[11]+\
+ (c12)*b0[12]+(c13)*b0[13]+(c14)*b0[14]+(c15)*b0[15];\
+ sum=(sum+(1 << 13))>>14;\
+ if (sum<-32768) sum=-32768;\
+ else if (sum>32767) sum=32767;\
+ samples[2*(pos)]=sum;\
+ b0+=FIR_BUFFER_SIZE;\
+}
+
+#define FIR16_2(pos1,c0,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15,\
+ pos2,d0,d1,d2,d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15) \
+{\
+ int sum1,sum2,v;\
+\
+ v=b0[0];\
+ sum1=(c0)*v;\
+ sum2=(d0)*v;\
+ v=b0[1];\
+ sum1+=(c1)*v;\
+ sum2+=(d1)*v;\
+ v=b0[2];\
+ sum1+=(c2)*v;\
+ sum2+=(d2)*v;\
+ v=b0[3];\
+ sum1+=(c3)*v;\
+ sum2+=(d3)*v;\
+ v=b0[4];\
+ sum1+=(c4)*v;\
+ sum2+=(d4)*v;\
+ v=b0[5];\
+ sum1+=(c5)*v;\
+ sum2+=(d5)*v;\
+ v=b0[6];\
+ sum1+=(c6)*v;\
+ sum2+=(d6)*v;\
+ v=b0[7];\
+ sum1+=(c7)*v;\
+ sum2+=(d7)*v;\
+ v=b0[8];\
+ sum1+=(c8)*v;\
+ sum2+=(d8)*v;\
+ v=b0[9];\
+ sum1+=(c9)*v;\
+ sum2+=(d9)*v;\
+ v=b0[10];\
+ sum1+=(c10)*v;\
+ sum2+=(d10)*v;\
+ v=b0[11];\
+ sum1+=(c11)*v;\
+ sum2+=(d11)*v;\
+ v=b0[12];\
+ sum1+=(c12)*v;\
+ sum2+=(d12)*v;\
+ v=b0[13];\
+ sum1+=(c13)*v;\
+ sum2+=(d13)*v;\
+ v=b0[14];\
+ sum1+=(c14)*v;\
+ sum2+=(d14)*v;\
+ v=b0[15];\
+ sum1+=(c15)*v;\
+ sum2+=(d15)*v;\
+\
+ sum1=(sum1+(1<<13))>>14;\
+ sum2=(sum2+(1<<13))>>14;\
+\
+ if (sum1<-32768) sum1=-32768;\
+ else if (sum1>32767) sum1=32767;\
+ samples[(pos1)*2]=sum1;\
+\
+ if (sum2<-32768) sum2=-32768;\
+ else if (sum2>32767) sum2=32767;\
+ samples[(pos2)*2]=sum2;\
+ b0+=FIR_BUFFER_SIZE;\
+}
+
+int synth_1to1_486(real *bandPtr,int channel,unsigned char *out,int nb_blocks)
+{
+ static int buffs[2][2][17*FIR_BUFFER_SIZE];
+ static int bo[2] = { FIR_SIZE-1, FIR_SIZE-1 };
+ short *samples = (short *) out;
+ int *b0,(*buf)[17*FIR_BUFFER_SIZE];
+ int clip = 0;
+ int block,b,bo_start;
+
+ /* samples address */
+ samples+=channel;
+
+ bo_start=bo[channel];
+ buf = buffs[channel];
+
+ b=bo_start;
+ for(block=0;block<nb_blocks;block++) {
+
+ /* FIR offset */
+ b++;
+ if (b >= FIR_BUFFER_SIZE) {
+ int *p,*q;
+ int c,i,j;
+
+ /* we shift the buffers */
+ for(c=0;c<2;c++) {
+ p=&buf[c][0]+1;
+ q=p+(FIR_BUFFER_SIZE-FIR_SIZE);
+ for(i=0;i<17;i++) {
+ for(j=0;j<FIR_SIZE-1;j++) p[j]=q[j];
+ p+=FIR_BUFFER_SIZE;
+ q+=FIR_BUFFER_SIZE;
+ }
+ }
+ /* we update 'bo' accordingly */
+ b=bo[channel]=FIR_SIZE;
+ }
+
+ if(b & 1) {
+ dct64_486(buf[1]+b,buf[0]+b,bandPtr);
+ } else {
+ dct64_486(buf[0]+b,buf[1]+b,bandPtr);
+ }
+ bandPtr+=32;
+ }
+ bo[channel]=b;
+
+ /* filter bank: part 1 */
+ b=bo_start;
+ for(block=0;block<nb_blocks;block++) {
+ b++;
+ if (b >= FIR_BUFFER_SIZE) b=FIR_SIZE;
+ if(b & 1) {
+ b0 = buf[0] + b - (FIR_SIZE-1);
+ } else {
+ b0 = buf[1] + b - (FIR_SIZE-1);
+ }
+
+ FIR16_1(0,-7,53,-114,509,-1288,1643,-9372,18759,9372,1643,1288,509,114,53,7,0);
+ FIR16_2(1,-6,52,-100,515,-1197,1783,-8910,18748,9834,1489,1379,500,129,54,7,0,
+ 31,0,-7,54,-129,500,-1379,1489,-9834,18748,8910,1783,1197,515,100,52,6);
+ FIR16_2(2,-6,50,-86,520,-1106,1910,-8447,18714,10294,1322,1469,488,145,55,8,0,
+ 30,0,-8,55,-145,488,-1469,1322,-10294,18714,8447,1910,1106,520,86,50,6);
+ FIR16_2(3,-5,49,-73,521,-1015,2023,-7986,18657,10751,1140,1559,473,161,56,9,0,
+ 29,0,-9,56,-161,473,-1559,1140,-10751,18657,7986,2023,1015,521,73,49,5);
+ samples+=64;
+ }
+ samples-=64*nb_blocks;
+
+ /* filter bank: part 2 */
+
+ b=bo_start;
+ for(block=0;block<nb_blocks;block++) {
+ b++;
+ if (b >= FIR_BUFFER_SIZE) b=FIR_SIZE;
+ if(b & 1) {
+ b0 = buf[0] + b - (FIR_SIZE-1) + 4*FIR_BUFFER_SIZE;
+ } else {
+ b0 = buf[1] + b - (FIR_SIZE-1) + 4*FIR_BUFFER_SIZE;
+ }
+
+ FIR16_2(4,-4,47,-61,521,-926,2123,-7528,18578,11205,944,1647,455,177,56,10,0,
+ 28,0,-10,56,-177,455,-1647,944,-11205,18578,7528,2123,926,521,61,47,4);
+ FIR16_2(5,-4,45,-49,518,-837,2210,-7072,18477,11654,733,1733,434,194,57,11,0,
+ 27,0,-11,57,-194,434,-1733,733,-11654,18477,7072,2210,837,518,49,45,4);
+ FIR16_2(6,-4,44,-38,514,-751,2284,-6620,18353,12097,509,1817,411,212,57,12,0,
+ 26,0,-12,57,-212,411,-1817,509,-12097,18353,6620,2284,751,514,38,44,4);
+ FIR16_2(7,-3,42,-27,508,-665,2347,-6173,18208,12534,270,1899,383,229,56,13,0,
+ 25,0,-13,56,-229,383,-1899,270,-12534,18208,6173,2347,665,508,27,42,3);
+
+ samples+=64;
+ }
+ samples-=64*nb_blocks;
+
+ /* filter bank: part 3 */
+
+ b=bo_start;
+ for(block=0;block<nb_blocks;block++) {
+ b++;
+ if (b >= FIR_BUFFER_SIZE) b=FIR_SIZE;
+ if(b & 1) {
+ b0 = buf[0] + b - (FIR_SIZE-1) + 8*FIR_BUFFER_SIZE;
+ } else {
+ b0 = buf[1] + b - (FIR_SIZE-1) + 8*FIR_BUFFER_SIZE;
+ }
+
+ FIR16_2(8,-3,40,-18,500,-582,2398,-5732,18042,12963,17,1977,353,247,56,14,0,
+ 24,0,-14,56,-247,353,-1977,17,-12963,18042,5732,2398,582,500,18,40,3);
+ FIR16_2(9,-2,38,-9,490,-501,2437,-5297,17855,13383,-249,2052,320,266,55,15,0,
+ 23,0,-15,55,-266,320,-2052,-249,-13383,17855,5297,2437,501,490,9,38,2);
+ FIR16_2(10,-2,36,0,479,-423,2465,-4869,17647,13794,-530,2122,282,284,53,17,0,
+ 22,0,-17,53,-284,282,-2122,-530,-13794,17647,4869,2465,423,479,0,36,2);
+ FIR16_2(11,-2,34,7,467,-347,2483,-4449,17419,14194,-825,2188,242,302,52,18,0,
+ 21,0,-18,52,-302,242,-2188,-825,-14194,17419,4449,2483,347,467,-7,34,2);
+
+ samples+=64;
+ }
+ samples-=64*nb_blocks;
+
+ /* filter bank: part 4 */
+
+ b=bo_start;
+ for(block=0;block<nb_blocks;block++) {
+ b++;
+ if (b >= FIR_BUFFER_SIZE) b=FIR_SIZE;
+ if(b & 1) {
+ b0 = buf[0] + b - (FIR_SIZE-1) + 12*FIR_BUFFER_SIZE;
+ } else {
+ b0 = buf[1] + b - (FIR_SIZE-1) + 12*FIR_BUFFER_SIZE;
+ }
+
+ FIR16_2(12,-2,33,14,454,-273,2491,-4038,17173,14583,-1133,2249,198,320,50,19,0,
+ 20,0,-19,50,-320,198,-2249,-1133,-14583,17173,4038,2491,273,454,-14,33,2);
+ FIR16_2(13,-1,31,20,439,-203,2489,-3637,16907,14959,-1454,2304,151,339,47,21,-1,
+ 19,-1,-21,47,-339,151,-2304,-1454,-14959,16907,3637,2489,203,439,-20,31,1);
+ FIR16_2(14,-1,29,26,424,-136,2479,-3245,16623,15322,-1788,2354,100,357,44,22,-1,
+ 18,-1,-22,44,-357,100,-2354,-1788,-15322,16623,3245,2479,136,424,-26,29,1);
+ FIR16_2(15,-1,27,31,408,-72,2459,-2863,16322,15671,-2135,2396,46,374,40,24,-1,
+ 17,-1,-24,40,-374,46,-2396,-2135,-15671,16322,2863,2459,72,408,-31,27,1);
+ FIR16_1(16,-1,0,36,0,-11,0,-2493,0,16004,0,2431,0,391,0,26,0);
+
+ samples+=64;
+ }
+
+ return clip;
+}
+
diff --git a/mpg123_artsplugin/mpg123/decode_i586.s b/mpg123_artsplugin/mpg123/decode_i586.s
new file mode 100644
index 00000000..5b5169a5
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/decode_i586.s
@@ -0,0 +1,323 @@
+#
+# synth_1to1 works the same way as the c version of this
+# file. only two types of changes have been made:
+# - reordered floating point instructions to
+# prevent pipline stalls
+# - made WRITE_SAMPLE use integer instead of
+# (slower) floating point
+# all kinds of x86 processors should benefit from these
+# modifications.
+#
+# useful sources of information on optimizing x86 code include:
+#
+# Intel Architecture Optimization Manual
+# http:#/www.intel.com/design/pentium/manuals/242816.htm
+#
+# Cyrix 6x86 Instruction Set Summary
+# ftp:#/ftp.cyrix.com/6x86/6x-dbch6.pdf
+#
+# AMD-K5 Processor Software Development
+# http:#/www.amd.com/products/cpg/techdocs/appnotes/20007e.pdf
+#
+# Stefan Bieschewski <stb@acm.org>
+#
+# You can use this part under GPL.
+#
+# $Id$
+#
+.bss
+ .comm buffs,4352,4
+.data
+ .align 4
+bo:
+ .long 1
+.section .rodata
+ .align 8
+.LC0:
+ .long 0x0,0x40dfffc0
+ .align 8
+.LC1:
+ .long 0x0,0xc0e00000
+ .align 8
+.text
+.globl synth_1to1_pent
+synth_1to1_pent:
+ subl $12,%esp
+ pushl %ebp
+ pushl %edi
+ pushl %esi
+ pushl %ebx
+ movl 32(%esp),%eax
+ movl 40(%esp),%esi
+ xorl %edi,%edi
+ movl bo,%ebp
+ cmpl %edi,36(%esp)
+ jne .L48
+ decl %ebp
+ andl $15,%ebp
+ movl %ebp,bo
+ movl $buffs,%ecx
+ jmp .L49
+.L48:
+ addl $2,%esi
+ movl $buffs+2176,%ecx
+.L49:
+ testl $1,%ebp
+ je .L50
+ movl %ecx,%ebx
+ movl %ebp,16(%esp)
+ pushl %eax
+ movl 20(%esp),%edx
+ leal (%ebx,%edx,4),%eax
+ pushl %eax
+ movl 24(%esp),%eax
+ incl %eax
+ andl $15,%eax
+ leal 1088(,%eax,4),%eax
+ addl %ebx,%eax
+ jmp .L74
+.L50:
+ leal 1088(%ecx),%ebx
+ leal 1(%ebp),%edx
+ movl %edx,16(%esp)
+ pushl %eax
+ leal 1092(%ecx,%ebp,4),%eax
+ pushl %eax
+ leal (%ecx,%ebp,4),%eax
+.L74:
+ pushl %eax
+ call dct64
+ addl $12,%esp
+ movl 16(%esp),%edx
+ leal 0(,%edx,4),%edx
+ movl $decwin+64,%eax
+ movl %eax,%ecx
+ subl %edx,%ecx
+ movl $16,%ebp
+.L55:
+ flds (%ecx)
+ fmuls (%ebx)
+ flds 4(%ecx)
+ fmuls 4(%ebx)
+ fxch %st(1)
+ flds 8(%ecx)
+ fmuls 8(%ebx)
+ fxch %st(2)
+ fsubrp %st,%st(1)
+ flds 12(%ecx)
+ fmuls 12(%ebx)
+ fxch %st(2)
+ faddp %st,%st(1)
+ flds 16(%ecx)
+ fmuls 16(%ebx)
+ fxch %st(2)
+ fsubrp %st,%st(1)
+ flds 20(%ecx)
+ fmuls 20(%ebx)
+ fxch %st(2)
+ faddp %st,%st(1)
+ flds 24(%ecx)
+ fmuls 24(%ebx)
+ fxch %st(2)
+ fsubrp %st,%st(1)
+ flds 28(%ecx)
+ fmuls 28(%ebx)
+ fxch %st(2)
+ faddp %st,%st(1)
+ flds 32(%ecx)
+ fmuls 32(%ebx)
+ fxch %st(2)
+ fsubrp %st,%st(1)
+ flds 36(%ecx)
+ fmuls 36(%ebx)
+ fxch %st(2)
+ faddp %st,%st(1)
+ flds 40(%ecx)
+ fmuls 40(%ebx)
+ fxch %st(2)
+ fsubrp %st,%st(1)
+ flds 44(%ecx)
+ fmuls 44(%ebx)
+ fxch %st(2)
+ faddp %st,%st(1)
+ flds 48(%ecx)
+ fmuls 48(%ebx)
+ fxch %st(2)
+ fsubrp %st,%st(1)
+ flds 52(%ecx)
+ fmuls 52(%ebx)
+ fxch %st(2)
+ faddp %st,%st(1)
+ flds 56(%ecx)
+ fmuls 56(%ebx)
+ fxch %st(2)
+ fsubrp %st,%st(1)
+ flds 60(%ecx)
+ fmuls 60(%ebx)
+ fxch %st(2)
+ subl $4,%esp
+ faddp %st,%st(1)
+ fxch %st(1)
+ fsubrp %st,%st(1)
+ fistpl (%esp)
+ popl %eax
+ cmpl $32767,%eax
+ jg 1f
+ cmpl $-32768,%eax
+ jl 2f
+ movw %ax,(%esi)
+ jmp 4f
+1: movw $32767,(%esi)
+ jmp 3f
+2: movw $-32768,(%esi)
+3: incl %edi
+4:
+.L54:
+ addl $64,%ebx
+ subl $-128,%ecx
+ addl $4,%esi
+ decl %ebp
+ jnz .L55
+ flds (%ecx)
+ fmuls (%ebx)
+ flds 8(%ecx)
+ fmuls 8(%ebx)
+ flds 16(%ecx)
+ fmuls 16(%ebx)
+ fxch %st(2)
+ faddp %st,%st(1)
+ flds 24(%ecx)
+ fmuls 24(%ebx)
+ fxch %st(2)
+ faddp %st,%st(1)
+ flds 32(%ecx)
+ fmuls 32(%ebx)
+ fxch %st(2)
+ faddp %st,%st(1)
+ flds 40(%ecx)
+ fmuls 40(%ebx)
+ fxch %st(2)
+ faddp %st,%st(1)
+ flds 48(%ecx)
+ fmuls 48(%ebx)
+ fxch %st(2)
+ faddp %st,%st(1)
+ flds 56(%ecx)
+ fmuls 56(%ebx)
+ fxch %st(2)
+ subl $4,%esp
+ faddp %st,%st(1)
+ fxch %st(1)
+ faddp %st,%st(1)
+ fistpl (%esp)
+ popl %eax
+ cmpl $32767,%eax
+ jg 1f
+ cmpl $-32768,%eax
+ jl 2f
+ movw %ax,(%esi)
+ jmp 4f
+1: movw $32767,(%esi)
+ jmp 3f
+2: movw $-32768,(%esi)
+3: incl %edi
+4:
+.L62:
+ addl $-64,%ebx
+ addl $4,%esi
+ movl 16(%esp),%edx
+ leal -128(%ecx,%edx,8),%ecx
+ movl $15,%ebp
+.L68:
+ flds -4(%ecx)
+ fchs
+ fmuls (%ebx)
+ flds -8(%ecx)
+ fmuls 4(%ebx)
+ fxch %st(1)
+ flds -12(%ecx)
+ fmuls 8(%ebx)
+ fxch %st(2)
+ fsubrp %st,%st(1)
+ flds -16(%ecx)
+ fmuls 12(%ebx)
+ fxch %st(2)
+ fsubrp %st,%st(1)
+ flds -20(%ecx)
+ fmuls 16(%ebx)
+ fxch %st(2)
+ fsubrp %st,%st(1)
+ flds -24(%ecx)
+ fmuls 20(%ebx)
+ fxch %st(2)
+ fsubrp %st,%st(1)
+ flds -28(%ecx)
+ fmuls 24(%ebx)
+ fxch %st(2)
+ fsubrp %st,%st(1)
+ flds -32(%ecx)
+ fmuls 28(%ebx)
+ fxch %st(2)
+ fsubrp %st,%st(1)
+ flds -36(%ecx)
+ fmuls 32(%ebx)
+ fxch %st(2)
+ fsubrp %st,%st(1)
+ flds -40(%ecx)
+ fmuls 36(%ebx)
+ fxch %st(2)
+ fsubrp %st,%st(1)
+ flds -44(%ecx)
+ fmuls 40(%ebx)
+ fxch %st(2)
+ fsubrp %st,%st(1)
+ flds -48(%ecx)
+ fmuls 44(%ebx)
+ fxch %st(2)
+ fsubrp %st,%st(1)
+ flds -52(%ecx)
+ fmuls 48(%ebx)
+ fxch %st(2)
+ fsubrp %st,%st(1)
+ flds -56(%ecx)
+ fmuls 52(%ebx)
+ fxch %st(2)
+ fsubrp %st,%st(1)
+ flds -60(%ecx)
+ fmuls 56(%ebx)
+ fxch %st(2)
+ fsubrp %st,%st(1)
+ flds (%ecx)
+ fmuls 60(%ebx)
+ fxch %st(2)
+ subl $4,%esp
+ fsubrp %st,%st(1)
+ fxch %st(1)
+ fsubrp %st,%st(1)
+ fistpl (%esp)
+ popl %eax
+ cmpl $32767,%eax
+ jg 1f
+ cmpl $-32768,%eax
+ jl 2f
+ movw %ax,(%esi)
+ jmp 4f
+1: movw $32767,(%esi)
+ jmp 3f
+2: movw $-32768,(%esi)
+3: incl %edi
+4:
+.L67:
+ addl $-64,%ebx
+ addl $-128,%ecx
+ addl $4,%esi
+ decl %ebp
+ jnz .L68
+ movl %edi,%eax
+ popl %ebx
+ popl %esi
+ popl %edi
+ popl %ebp
+ addl $12,%esp
+ ret
+
diff --git a/mpg123_artsplugin/mpg123/decode_ntom.c b/mpg123_artsplugin/mpg123/decode_ntom.c
new file mode 100644
index 00000000..41ca0dc0
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/decode_ntom.c
@@ -0,0 +1,290 @@
+/*
+ * Mpeg Layer-1,2,3 audio decoder
+ * ------------------------------
+ * copyright (c) 1995,1996,1997 by Michael Hipp, All rights reserved.
+ * See also 'README'
+ *
+ * N->M down/up sampling. Not optimized for speed.
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "mpg123.h"
+
+#define WRITE_SAMPLE(samples,sum,clip) \
+ if( (sum) > 32767.0) { *(samples) = 0x7fff; (clip)++; } \
+ else if( (sum) < -32768.0) { *(samples) = -0x8000; (clip)++; } \
+ else { *(samples) = sum; }
+
+#define NTOM_MUL (32768)
+static unsigned long ntom_val[2] = { NTOM_MUL>>1,NTOM_MUL>>1 };
+static unsigned long ntom_step = NTOM_MUL;
+
+
+void synth_ntom_set_step(long m,long n)
+{
+ if(param.verbose > 1)
+ fprintf(stderr,"Init rate converter: %ld->%ld\n",m,n);
+
+ if(n >= 96000 || m >= 96000 || m == 0 || n == 0) {
+ fprintf(stderr,"NtoM converter: illegal rates\n");
+ exit(1);
+ }
+
+ n *= NTOM_MUL;
+ ntom_step = n / m;
+
+ if(ntom_step > 8*NTOM_MUL) {
+ fprintf(stderr,"max. 1:8 conversion allowed!\n");
+ exit(1);
+ }
+
+ ntom_val[0] = ntom_val[1] = NTOM_MUL>>1;
+}
+
+int synth_ntom_8bit(real *bandPtr,int channel,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[8*64];
+ short *tmp1 = samples_tmp + channel;
+ int i,ret;
+ int pnt1 = 0;
+
+ ret = synth_ntom(bandPtr,channel,(unsigned char *) samples_tmp,&pnt1);
+ samples += channel + *pnt;
+
+ for(i=0;i<(pnt1>>2);i++) {
+ *samples = conv16to8[*tmp1>>AUSHIFT];
+ samples += 2;
+ tmp1 += 2;
+ }
+ *pnt += pnt1>>1;
+
+ return ret;
+}
+
+int synth_ntom_8bit_mono(real *bandPtr,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[8*64];
+ short *tmp1 = samples_tmp;
+ int i,ret;
+ int pnt1 = 0;
+
+ ret = synth_ntom(bandPtr,0,(unsigned char *) samples_tmp,&pnt1);
+ samples += *pnt;
+
+ for(i=0;i<(pnt1>>2);i++) {
+ *samples++ = conv16to8[*tmp1>>AUSHIFT];
+ tmp1 += 2;
+ }
+ *pnt += pnt1 >> 2;
+
+ return ret;
+}
+
+int synth_ntom_8bit_mono2stereo(real *bandPtr,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[8*64];
+ short *tmp1 = samples_tmp;
+ int i,ret;
+ int pnt1 = 0;
+
+ ret = synth_ntom(bandPtr,0,(unsigned char *) samples_tmp,&pnt1);
+ samples += *pnt;
+
+ for(i=0;i<(pnt1>>2);i++) {
+ *samples++ = conv16to8[*tmp1>>AUSHIFT];
+ *samples++ = conv16to8[*tmp1>>AUSHIFT];
+ tmp1 += 2;
+ }
+ *pnt += pnt1 >> 1;
+
+ return ret;
+}
+
+int synth_ntom_mono(real *bandPtr,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[8*64];
+ short *tmp1 = samples_tmp;
+ int i,ret;
+ int pnt1 = 0;
+
+ ret = synth_ntom(bandPtr,0,(unsigned char *) samples_tmp,&pnt1);
+ samples += *pnt;
+
+ for(i=0;i<(pnt1>>2);i++) {
+ *( (short *)samples) = *tmp1;
+ samples += 2;
+ tmp1 += 2;
+ }
+ *pnt += pnt1 >> 1;
+
+ return ret;
+}
+
+
+int synth_ntom_mono2stereo(real *bandPtr,unsigned char *samples,int *pnt)
+{
+ int i,ret;
+ int pnt1 = *pnt;
+
+ ret = synth_ntom(bandPtr,0,samples,pnt);
+ samples += pnt1;
+
+ for(i=0;i<((*pnt-pnt1)>>2);i++) {
+ ((short *)samples)[1] = ((short *)samples)[0];
+ samples+=4;
+ }
+
+ return ret;
+}
+
+
+int synth_ntom(real *bandPtr,int channel,unsigned char *out,int *pnt)
+{
+ static real buffs[2][2][0x110];
+ static const int step = 2;
+ static int bo = 1;
+ short *samples = (short *) (out + *pnt);
+
+ real *b0,(*buf)[0x110];
+ int clip = 0;
+ int bo1;
+ int ntom;
+
+#ifndef NO_EQUALIZER
+ if(param.enable_equalizer)
+ do_equalizer(bandPtr,channel);
+#endif
+
+ if(!channel) {
+ bo--;
+ bo &= 0xf;
+ buf = buffs[0];
+ ntom = ntom_val[1] = ntom_val[0];
+ }
+ else {
+ samples++;
+ out += 2; /* to compute the right *pnt value */
+ buf = buffs[1];
+ ntom = ntom_val[1];
+ }
+
+ if(bo & 0x1) {
+ b0 = buf[0];
+ bo1 = bo;
+ dct64(buf[1]+((bo+1)&0xf),buf[0]+bo,bandPtr);
+ }
+ else {
+ b0 = buf[1];
+ bo1 = bo+1;
+ dct64(buf[0]+bo,buf[1]+bo+1,bandPtr);
+ }
+
+
+ {
+ register int j;
+ real *window = decwin + 16 - bo1;
+
+ for (j=16;j;j--,window+=0x10)
+ {
+ real sum;
+
+ ntom += ntom_step;
+ if(ntom < NTOM_MUL) {
+ window += 16;
+ b0 += 16;
+ continue;
+ }
+
+ sum = *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+
+ while(ntom >= NTOM_MUL) {
+ WRITE_SAMPLE(samples,sum,clip);
+ samples += step;
+ ntom -= NTOM_MUL;
+ }
+ }
+
+ ntom += ntom_step;
+ if(ntom >= NTOM_MUL)
+ {
+ real sum;
+ sum = window[0x0] * b0[0x0];
+ sum += window[0x2] * b0[0x2];
+ sum += window[0x4] * b0[0x4];
+ sum += window[0x6] * b0[0x6];
+ sum += window[0x8] * b0[0x8];
+ sum += window[0xA] * b0[0xA];
+ sum += window[0xC] * b0[0xC];
+ sum += window[0xE] * b0[0xE];
+
+ while(ntom >= NTOM_MUL) {
+ WRITE_SAMPLE(samples,sum,clip);
+ samples += step;
+ ntom -= NTOM_MUL;
+ }
+ }
+
+ b0-=0x10,window-=0x20;
+ window += bo1<<1;
+
+ for (j=15;j;j--,b0-=0x20,window-=0x10)
+ {
+ real sum;
+
+ ntom += ntom_step;
+ if(ntom < NTOM_MUL) {
+ window -= 16;
+ b0 += 16;
+ continue;
+ }
+
+ sum = -*(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+
+ while(ntom >= NTOM_MUL) {
+ WRITE_SAMPLE(samples,sum,clip);
+ samples += step;
+ ntom -= NTOM_MUL;
+ }
+ }
+ }
+
+ ntom_val[channel] = ntom;
+ *pnt = ((unsigned char *) samples - out);
+
+ return clip;
+}
+
+
diff --git a/mpg123_artsplugin/mpg123/genre.h b/mpg123_artsplugin/mpg123/genre.h
new file mode 100644
index 00000000..3a6c5ba3
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/genre.h
@@ -0,0 +1,263 @@
+char *genre_table[] =
+{
+ "Blues",
+ "Classic Rock",
+ "Country",
+ "Dance",
+ "Disco",
+ "Funk",
+ "Grunge",
+ "Hip-Hop",
+ "Jazz",
+ "Metal",
+ "New Age",
+ "Oldies",
+ "Other",
+ "Pop",
+ "R&B",
+ "Rap",
+ "Reggae",
+ "Rock",
+ "Techno",
+ "Industrial",
+ "Alternative",
+ "Ska",
+ "Death Metal",
+ "Pranks",
+ "Soundtrack",
+ "Euro-Techno",
+ "Ambient",
+ "Trip-Hop",
+ "Vocal",
+ "Jazz+Funk",
+ "Fusion",
+ "Trance",
+ "Classical",
+ "Instrumental",
+ "Acid",
+ "House",
+ "Game",
+ "Sound Clip",
+ "Gospel",
+ "Noise",
+ "AlternRock",
+ "Bass",
+ "Soul",
+ "Punk",
+ "Space",
+ "Meditative",
+ "Instrumental Pop",
+ "Instrumental Rock",
+ "Ethnic",
+ "Gothic",
+ "Darkwave",
+ "Techno-Industrial",
+ "Electronic",
+ "Pop-Folk",
+ "Eurodance",
+ "Dream",
+ "Southern Rock",
+ "Comedy",
+ "Cult",
+ "Gangsta",
+ "Top 40",
+ "Christian Rap",
+ "Pop/Funk",
+ "Jungle",
+ "Native American",
+ "Cabaret",
+ "New Wave",
+ "Psychadelic",
+ "Rave",
+ "Showtunes",
+ "Trailer",
+ "Lo-Fi",
+ "Tribal",
+ "Acid Punk",
+ "Acid Jazz",
+ "Polka",
+ "Retro",
+ "Musical",
+ "Rock & Roll",
+ "Hard Rock",
+ "Folk",
+ "Folk/Rock",
+ "National folk",
+ "Swing",
+ "Fast-fusion",
+ "Bebob",
+ "Latin",
+ "Revival",
+ "Celtic",
+ "Bluegrass",
+ "Avantgarde",
+ "Gothic Rock",
+ "Progressive Rock",
+ "Psychedelic Rock",
+ "Symphonic Rock",
+ "Slow Rock",
+ "Big Band",
+ "Chorus",
+ "Easy Listening",
+ "Acoustic",
+ "Humour",
+ "Speech",
+ "Chanson",
+ "Opera",
+ "Chamber Music",
+ "Sonata",
+ "Symphony",
+ "Booty Bass",
+ "Primus",
+ "Porn Groove",
+ "Satire",
+ "Slow Jam",
+ "Club",
+ "Tango",
+ "Samba",
+ "Folklore",
+ "Ballad",
+ "Powder Ballad",
+ "Rhythmic Soul",
+ "Freestyle",
+ "Duet",
+ "Punk Rock",
+ "Drum Solo",
+ "A Capella",
+ "Euro-House",
+ "Dance Hall",
+ "Goa",
+ "Drum & Bass",
+ "Club House",
+ "Hardcore",
+ "Terror",
+ "Indie",
+ "BritPop",
+ "NegerPunk",
+ "Polsk Punk",
+ "Beat",
+ "Christian Gangsta",
+ "Heavy Metal",
+ "Black Metal",
+ "Crossover",
+ "Contemporary C",
+ "Christian Rock",
+ "Merengue",
+ "Salsa",
+ "Thrash Metal",
+ "Anime",
+ "JPop",
+ "SynthPop"
+/* ,
+ "Unknown",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ ""
+*/
+};
+
+const int genre_count = ((int)(sizeof(genre_table)/sizeof(char*))-1);
diff --git a/mpg123_artsplugin/mpg123/getbits.c b/mpg123_artsplugin/mpg123/getbits.c
new file mode 100644
index 00000000..3ed993cb
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/getbits.c
@@ -0,0 +1,116 @@
+#include "mpg123.h"
+#include "common.h"
+
+void backbits(struct bitstream_info *bitbuf,int number_of_bits)
+{
+ bitbuf->bitindex -= number_of_bits;
+ bitbuf->wordpointer += (bitbuf->bitindex>>3);
+ bitbuf->bitindex &= 0x7;
+}
+
+int getbitoffset(struct bitstream_info *bitbuf)
+{
+ return (-bitbuf->bitindex)&0x7;
+}
+
+int getbyte(struct bitstream_info *bitbuf)
+{
+#ifdef DEBUG_GETBITS
+ if(bitbuf->bitindex)
+ fprintf(stderr,"getbyte called unsynched!\n");
+#endif
+ return *bitbuf->wordpointer++;
+}
+
+unsigned int getbits(struct bitstream_info *bitbuf,int number_of_bits)
+{
+ unsigned long rval;
+
+#ifdef DEBUG_GETBITS
+fprintf(stderr,"g%d",number_of_bits);
+#endif
+
+ if(!number_of_bits)
+ return 0;
+
+#if 0
+ check_buffer_range(number_of_bits+bitbuf->bitindex);
+#endif
+
+ {
+ rval = bitbuf->wordpointer[0];
+ rval <<= 8;
+ rval |= bitbuf->wordpointer[1];
+ rval <<= 8;
+ rval |= bitbuf->wordpointer[2];
+
+ rval <<= bitbuf->bitindex;
+ rval &= 0xffffff;
+
+ bitbuf->bitindex += number_of_bits;
+
+ rval >>= (24-number_of_bits);
+
+ bitbuf->wordpointer += (bitbuf->bitindex>>3);
+ bitbuf->bitindex &= 7;
+ }
+
+#ifdef DEBUG_GETBITS
+fprintf(stderr,":%x ",rval);
+#endif
+
+ return rval;
+}
+
+unsigned int getbits_fast(struct bitstream_info *bitbuf,int number_of_bits)
+{
+ unsigned int rval;
+#ifdef DEBUG_GETBITS
+fprintf(stderr,"g%d",number_of_bits);
+#endif
+
+#if 0
+ check_buffer_range(number_of_bits+bitbuf->bitindex);
+#endif
+
+ rval = (unsigned char) (bitbuf->wordpointer[0] << bitbuf->bitindex);
+ rval |= ((unsigned int) bitbuf->wordpointer[1]<<bitbuf->bitindex)>>8;
+ rval <<= number_of_bits;
+ rval >>= 8;
+
+ bitbuf->bitindex += number_of_bits;
+
+ bitbuf->wordpointer += (bitbuf->bitindex>>3);
+ bitbuf->bitindex &= 7;
+
+#ifdef DEBUG_GETBITS
+fprintf(stderr,":%x ",rval);
+#endif
+ return rval;
+}
+
+unsigned int get1bit(struct bitstream_info *bitbuf)
+{
+ unsigned char rval;
+
+#ifdef DEBUG_GETBITS
+fprintf(stderr,"g%d",1);
+#endif
+
+#if 0
+ check_buffer_range(1+bitbuf->bitindex);
+#endif
+
+ rval = *(bitbuf->wordpointer) << bitbuf->bitindex;
+
+ bitbuf->bitindex++;
+ bitbuf->wordpointer += (bitbuf->bitindex>>3);
+ bitbuf->bitindex &= 7;
+
+#ifdef DEBUG_GETBITS
+fprintf(stderr,":%d ",rval>>7);
+#endif
+
+ return rval>>7;
+}
+
diff --git a/mpg123_artsplugin/mpg123/getbits.h b/mpg123_artsplugin/mpg123/getbits.h
new file mode 100644
index 00000000..65e3e745
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/getbits.h
@@ -0,0 +1,33 @@
+
+/* that's the same file as getits.c but with defines to
+ force inlining */
+
+static unsigned long rval;
+static unsigned char rval_uc;
+
+#define backbits(bitbuf,nob) ((void)( \
+ (*(bitbuf)).bitindex -= (nob), \
+ (*(bitbuf)).wordpointer += ((*(bitbuf)).bitindex>>3), \
+ (*(bitbuf)).bitindex &= 0x7 ))
+
+#define getbitoffset(bitbuf) ((-(*(bitbuf)).bitindex)&0x7)
+#define getbyte(bitbuf) (*(*(bitbuf)).wordpointer++)
+
+#define getbits(bitbuf,nob) ( \
+ rval = (*(bitbuf)).wordpointer[0], rval <<= 8, rval |= (*(bitbuf)).wordpointer[1], \
+ rval <<= 8, rval |= (*(bitbuf)).wordpointer[2], rval <<= (*(bitbuf)).bitindex, \
+ rval &= 0xffffff, (*(bitbuf)).bitindex += (nob), \
+ rval >>= (24-(nob)), (*(bitbuf)).wordpointer += ((*(bitbuf)).bitindex>>3), \
+ (*(bitbuf)).bitindex &= 7,rval)
+
+#define getbits_fast(bitbuf,nob) ( \
+ rval = (unsigned char) ((*(bitbuf)).wordpointer[0] << (*(bitbuf)).bitindex), \
+ rval |= ((unsigned long) (*(bitbuf)).wordpointer[1]<<(*(bitbuf)).bitindex)>>8, \
+ rval <<= (nob), rval >>= 8, \
+ (*(bitbuf)).bitindex += (nob), (*(bitbuf)).wordpointer += ((*(bitbuf)).bitindex>>3), \
+ (*(bitbuf)).bitindex &= 7, rval )
+
+#define get1bit(bitbuf) ( \
+ rval_uc = *(*(bitbuf)).wordpointer << (*(bitbuf)).bitindex, (*(bitbuf)).bitindex++, \
+ (*(bitbuf)).wordpointer += ((*(bitbuf)).bitindex>>3), (*(bitbuf)).bitindex &= 7, rval_uc>>7 )
+
diff --git a/mpg123_artsplugin/mpg123/httpget.c b/mpg123_artsplugin/mpg123/httpget.c
new file mode 100644
index 00000000..71750115
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/httpget.c
@@ -0,0 +1,481 @@
+/*
+ * httpget.c
+ *
+ * Oliver Fromme <oliver.fromme@heim3.tu-clausthal.de>
+ * Wed Apr 9 20:57:47 MET DST 1997
+ */
+
+#undef ALSA
+
+#if !defined(WIN32) && !defined(GENERIC)
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/errno.h>
+#include <ctype.h>
+
+extern int errno;
+
+#include "mpg123.h"
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+void writestring (int fd, char *string)
+{
+ int result, bytes = strlen(string);
+
+ while (bytes) {
+ if ((result = write(fd, string, bytes)) < 0 && errno != EINTR) {
+ perror ("write");
+ exit (1);
+ }
+ else if (result == 0) {
+ fprintf (stderr, "write: %s\n",
+ "socket closed unexpectedly");
+ exit (1);
+ }
+ string += result;
+ bytes -= result;
+ }
+}
+
+void readstring (char *string, int maxlen, FILE *f)
+{
+#if 0
+ char *result;
+#endif
+ int pos = 0;
+
+ while(1) {
+ if( read(fileno(f),string+pos,1) == 1) {
+ pos++;
+ if(string[pos-1] == '\n') {
+ string[pos] = 0;
+ break;
+ }
+ }
+ else if(errno != EINTR) {
+ fprintf (stderr, "Error reading from socket or unexpected EOF.\n");
+ exit(1);
+ }
+ }
+#if 0
+ do {
+ result = fgets(string, maxlen, f);
+ } while (!result && errno == EINTR);
+ if (!result) {
+ fprintf (stderr, "Error reading from socket or unexpected EOF.\n");
+ exit (1);
+ }
+#endif
+
+}
+
+void encode64 (char *source,char *destination)
+{
+ static char *Base64Digits =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ int n = 0;
+ int ssiz=strlen(source);
+ int i;
+
+ for (i = 0 ; i < ssiz ; i += 3) {
+ unsigned int buf;
+ buf = ((unsigned char *)source)[i] << 16;
+ if (i+1 < ssiz)
+ buf |= ((unsigned char *)source)[i+1] << 8;
+ if (i+2 < ssiz)
+ buf |= ((unsigned char *)source)[i+2];
+
+ destination[n++] = Base64Digits[(buf >> 18) % 64];
+ destination[n++] = Base64Digits[(buf >> 12) % 64];
+ if (i+1 < ssiz)
+ destination[n++] = Base64Digits[(buf >> 6) % 64];
+ else
+ destination[n++] = '=';
+ if (i+2 < ssiz)
+ destination[n++] = Base64Digits[buf % 64];
+ else
+ destination[n++] = '=';
+ }
+ destination[n++] = 0;
+}
+
+/* VERY simple auth-from-URL grabber */
+int getauthfromURL(char *url,char *auth)
+{
+ char *pos;
+
+ *auth = 0;
+
+ if (!(strncasecmp(url, "http://", 7)))
+ url += 7;
+
+ if (!(strncasecmp(url, "ftp://", 6)))
+ url += 6;
+
+ if( (pos = strchr(url,'@')) ) {
+ int i;
+ for(i=0;i<pos-url;i++) {
+ if( url[i] == '/' )
+ return 0;
+ }
+ strncpy(auth,url,pos-url);
+ auth[pos-url] = 0;
+ strcpy(url,pos+1);
+ return 1;
+ }
+ return 0;
+}
+
+static char *defaultportstr = "80";
+
+char *url2hostport (char *url, char **hname, unsigned long *hip, unsigned char **port)
+{
+ char *h, *p;
+ char *hostptr;
+ char *r_hostptr;
+ char *pathptr;
+ char *portptr;
+ char *p0;
+ size_t stringlength;
+
+ p = url;
+ if (strncasecmp(p, "http://", 7) == 0)
+ p += 7;
+
+ if (strncasecmp(p, "ftp://", 6) == 0)
+ p += 6;
+
+ hostptr = p;
+ while (*p && *p != '/')
+ p++;
+ pathptr = p;
+
+ r_hostptr = --p;
+ while (*p && hostptr < p && *p != ':' && *p != ']')
+ p--;
+
+ if (!*p || p < hostptr || *p != ':') {
+ portptr = NULL;
+ }
+ else{
+ portptr = p + 1;
+ r_hostptr = p - 1;
+ }
+ if (*hostptr == '[' && *r_hostptr == ']') {
+ hostptr++;
+ r_hostptr--;
+ }
+
+ stringlength = r_hostptr - hostptr + 1;
+ h = malloc(stringlength + 1); /* removed the strndup for better portability */
+ if (h == NULL) {
+ *hname = NULL;
+ *port = NULL;
+ return NULL;
+ }
+ strncpy(h, hostptr, stringlength);
+ *(h+stringlength) = '\0';
+ *hname = h;
+
+ if (portptr) {
+ stringlength = (pathptr - portptr);
+ if(!stringlength) portptr = NULL;
+ }
+ if (portptr == NULL) {
+ portptr = defaultportstr;
+ stringlength = strlen(defaultportstr);
+ }
+ p0 = malloc(stringlength + 1);
+ if (p0 == NULL) {
+ free(h);
+ *hname = NULL;
+ *port = NULL;
+ return NULL;
+ }
+ strncpy(p0, portptr, stringlength);
+ *(p0 + stringlength) = '\0';
+
+ for (p = p0; *p && isdigit((unsigned char) *p); p++) ;
+
+ *p = '\0';
+ *port = (unsigned char *) p0;
+
+ return pathptr;
+}
+
+char *proxyurl = NULL;
+unsigned long proxyip = 0;
+unsigned char *proxyport;
+
+#define ACCEPT_HEAD "Accept: audio/mpeg, audio/x-mpegurl, */*\r\n"
+
+char *httpauth = NULL;
+char httpauth1[256];
+
+int http_open (const char *url)
+{
+ char *purl, *host, *request, *sptr;
+ int linelength;
+ unsigned long myip;
+ unsigned char *myport;
+ int sock;
+ int relocate, numrelocs = 0;
+ FILE *myfile;
+#ifdef INET6
+ struct addrinfo hints, *res, *res0;
+ int error;
+#else
+ struct hostent *hp;
+ struct sockaddr_in sin;
+#endif
+
+ host = NULL;
+ proxyport = NULL;
+ myport = NULL;
+ if (!proxyip) {
+ if (!proxyurl)
+ if (!(proxyurl = getenv("MP3_HTTP_PROXY")))
+ if (!(proxyurl = getenv("http_proxy")))
+ proxyurl = getenv("HTTP_PROXY");
+ if (proxyurl && proxyurl[0] && strcmp(proxyurl, "none")) {
+ if (!(url2hostport(proxyurl, &host, &proxyip, &proxyport))) {
+ fprintf (stderr, "Unknown proxy host \"%s\".\n",
+ host ? host : "");
+ exit (1);
+ }
+#if 0
+ if (host)
+ free (host);
+#endif
+ }
+ else
+ proxyip = INADDR_NONE;
+ }
+
+
+ if (proxyip == INADDR_NONE)
+ if (strncasecmp(url, "ftp://", 6) == 0){
+ fprintf(stderr,"Downloading from ftp servers without PROXY not allowed\n");
+ exit(1);
+ }
+
+
+ if ((linelength = strlen(url)+200) < 1024)
+ linelength = 1024;
+ if (!(request = malloc(linelength)) || !(purl = malloc(1024))) {
+ fprintf (stderr, "malloc() failed, out of memory.\n");
+ exit (1);
+ }
+ /*
+ * 2000-10-21:
+ * We would like spaces to be automatically converted to %20's when
+ * fetching via HTTP.
+ * -- Martin Sjgren <md9ms@mdstud.chalmers.se>
+ */
+ if ((sptr = strchr(url, ' ')) == NULL) {
+ strncpy (purl, url, 1023);
+ purl[1023] = '\0';
+ }
+ else {
+ int purllength = 0;
+ char *urlptr = url;
+ purl[0] = '\0';
+ do {
+ purllength += sptr-urlptr + 3;
+ if (purllength >= 1023)
+ break;
+ strncat (purl, urlptr, sptr-urlptr);
+ /*purl[sptr-url] = '\0';*/
+ strcat (purl, "%20");
+ urlptr = sptr + 1;
+ }
+ while ((sptr = strchr (urlptr, ' ')) != NULL);
+ strcat (purl, urlptr);
+ }
+
+
+ getauthfromURL(purl,httpauth1);
+
+ do {
+ strcpy (request, "GET ");
+ if (proxyip != INADDR_NONE) {
+ if (strncasecmp(url, "http://", 7) != 0 && strncasecmp(url,"ftp://", 6) != 0)
+ strcat (request, "http://");
+ strcat (request, purl);
+ myport = proxyport;
+ myip = proxyip;
+ }
+ else {
+ if (host) {
+ free(host);
+ host=NULL;
+ }
+ if (proxyport) {
+ free(proxyport);
+ proxyport=NULL;
+ }
+ if (!(sptr = url2hostport(purl, &host, &myip, &myport))) {
+ fprintf (stderr, "Unknown host \"%s\".\n",
+ host ? host : "");
+ exit (1);
+ }
+ strcat (request, sptr);
+ }
+ sprintf (request + strlen(request),
+ " HTTP/1.0\r\nUser-Agent: %s/%s\r\n",
+ prgName, prgVersion);
+ if (host) {
+ sprintf(request + strlen(request),
+ "Host: %s:%s\r\n", host, myport);
+#if 0
+ free (host);
+#endif
+ }
+ strcat (request, ACCEPT_HEAD);
+
+#ifdef INET6
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(host, (char *)myport, &hints, &res0);
+ if (error) {
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error));
+ exit(1);
+ }
+
+ sock = -1;
+ for (res = res0; res; res = res->ai_next) {
+ if ((sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) {
+ continue;
+ }
+ if (connect(sock, res->ai_addr, res->ai_addrlen)) {
+ close(sock);
+ sock = -1;
+ continue;
+ }
+ break;
+ }
+
+ freeaddrinfo(res0);
+#else
+ sock = -1;
+ hp = gethostbyname(host);
+ if (!hp)
+ goto fail;
+ if (hp->h_length != sizeof(sin.sin_addr))
+ goto fail;
+ sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sock < 0)
+ goto fail;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ /* sin.sin_len = sizeof(struct sockaddr_in); */
+ memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
+ sin.sin_port = htons(atoi( (char *) myport));
+ if (connect(sock, (struct sockaddr *)&sin, sizeof(struct sockaddr_in) ) < 0) {
+ close(sock);
+ sock = -1;
+ }
+fail:
+#endif
+
+ if (sock < 0) {
+ perror("socket");
+ exit(1);
+ }
+
+ if (strlen(httpauth1) || httpauth) {
+ char buf[1023];
+ strcat (request,"Authorization: Basic ");
+ if(strlen(httpauth1))
+ encode64(httpauth1,buf);
+ else
+ encode64(httpauth,buf);
+ strcat (request,buf);
+ strcat (request,"\r\n");
+ }
+ strcat (request, "\r\n");
+
+ writestring (sock, request);
+ if (!(myfile = fdopen(sock, "rb"))) {
+ perror ("fdopen");
+ exit (1);
+ };
+ relocate = FALSE;
+ purl[0] = '\0';
+ readstring (request, linelength-1, myfile);
+ if ((sptr = strchr(request, ' '))) {
+ switch (sptr[1]) {
+ case '3':
+ relocate = TRUE;
+ case '2':
+ break;
+ default:
+ fprintf (stderr, "HTTP request failed: %s",
+ sptr+1); /* '\n' is included */
+ exit (1);
+ }
+ }
+ do {
+ readstring (request, linelength-1, myfile);
+ if (!strncmp(request, "Location:", 9))
+ strncpy (purl, request+10, 1023);
+ } while (request[0] != '\r' && request[0] != '\n');
+ } while (relocate && purl[0] && numrelocs++ < 5);
+ if (relocate) {
+ fprintf (stderr, "Too many HTTP relocations.\n");
+ exit (1);
+ }
+ free (purl);
+ free (request);
+ free(host);
+ free(proxyport);
+ free(myport);
+
+ return sock;
+}
+
+#else
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+extern int errno;
+
+#include "mpg123.h"
+
+void writestring (int fd, char *string)
+{
+}
+
+void readstring (char *string, int maxlen, FILE *f)
+{
+}
+
+char *url2hostport (char *url, char **hname, unsigned long *hip, unsigned int *port)
+{
+}
+
+char *proxyurl = NULL;
+unsigned long proxyip = 0;
+unsigned int proxyport;
+
+#define ACCEPT_HEAD "Accept: audio/mpeg, audio/x-mpegurl, */*\r\n"
+
+int http_open (char *url)
+{
+}
+#endif
+
+/* EOF */
+
diff --git a/mpg123_artsplugin/mpg123/huffman.h b/mpg123_artsplugin/mpg123/huffman.h
new file mode 100644
index 00000000..7fec0d58
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/huffman.h
@@ -0,0 +1,332 @@
+/*
+ * huffman tables ... recalcualted to work with my optimzed
+ * decoder scheme (MH)
+ *
+ * probably we could save a few bytes of memory, because the
+ * smaller tables are often the part of a bigger table
+ */
+
+struct newhuff
+{
+ unsigned int linbits;
+ short *table;
+};
+
+static short tab0[] =
+{
+ 0
+};
+
+static short tab1[] =
+{
+ -5, -3, -1, 17, 1, 16, 0
+};
+
+static short tab2[] =
+{
+ -15, -11, -9, -5, -3, -1, 34, 2, 18, -1, 33, 32, 17, -1, 1,
+ 16, 0
+};
+
+static short tab3[] =
+{
+ -13, -11, -9, -5, -3, -1, 34, 2, 18, -1, 33, 32, 16, 17, -1,
+ 1, 0
+};
+
+static short tab5[] =
+{
+ -29, -25, -23, -15, -7, -5, -3, -1, 51, 35, 50, 49, -3, -1, 19,
+ 3, -1, 48, 34, -3, -1, 18, 33, -1, 2, 32, 17, -1, 1, 16,
+ 0
+};
+
+static short tab6[] =
+{
+ -25, -19, -13, -9, -5, -3, -1, 51, 3, 35, -1, 50, 48, -1, 19,
+ 49, -3, -1, 34, 2, 18, -3, -1, 33, 32, 1, -1, 17, -1, 16,
+ 0
+};
+
+static short tab7[] =
+{
+ -69, -65, -57, -39, -29, -17, -11, -7, -3, -1, 85, 69, -1, 84, 83,
+ -1, 53, 68, -3, -1, 37, 82, 21, -5, -1, 81, -1, 5, 52, -1,
+ 80, -1, 67, 51, -5, -3, -1, 36, 66, 20, -1, 65, 64, -11, -7,
+ -3, -1, 4, 35, -1, 50, 3, -1, 19, 49, -3, -1, 48, 34, 18,
+ -5, -1, 33, -1, 2, 32, 17, -1, 1, 16, 0
+};
+
+static short tab8[] =
+{
+ -65, -63, -59, -45, -31, -19, -13, -7, -5, -3, -1, 85, 84, 69, 83,
+ -3, -1, 53, 68, 37, -3, -1, 82, 5, 21, -5, -1, 81, -1, 52,
+ 67, -3, -1, 80, 51, 36, -5, -3, -1, 66, 20, 65, -3, -1, 4,
+ 64, -1, 35, 50, -9, -7, -3, -1, 19, 49, -1, 3, 48, 34, -1,
+ 2, 32, -1, 18, 33, 17, -3, -1, 1, 16, 0
+};
+
+static short tab9[] =
+{
+ -63, -53, -41, -29, -19, -11, -5, -3, -1, 85, 69, 53, -1, 83, -1,
+ 84, 5, -3, -1, 68, 37, -1, 82, 21, -3, -1, 81, 52, -1, 67,
+ -1, 80, 4, -7, -3, -1, 36, 66, -1, 51, 64, -1, 20, 65, -5,
+ -3, -1, 35, 50, 19, -1, 49, -1, 3, 48, -5, -3, -1, 34, 2,
+ 18, -1, 33, 32, -3, -1, 17, 1, -1, 16, 0
+};
+
+static short tab10[] =
+{
+-125,-121,-111, -83, -55, -35, -21, -13, -7, -3, -1, 119, 103, -1, 118,
+ 87, -3, -1, 117, 102, 71, -3, -1, 116, 86, -1, 101, 55, -9, -3,
+ -1, 115, 70, -3, -1, 85, 84, 99, -1, 39, 114, -11, -5, -3, -1,
+ 100, 7, 112, -1, 98, -1, 69, 53, -5, -1, 6, -1, 83, 68, 23,
+ -17, -5, -1, 113, -1, 54, 38, -5, -3, -1, 37, 82, 21, -1, 81,
+ -1, 52, 67, -3, -1, 22, 97, -1, 96, -1, 5, 80, -19, -11, -7,
+ -3, -1, 36, 66, -1, 51, 4, -1, 20, 65, -3, -1, 64, 35, -1,
+ 50, 3, -3, -1, 19, 49, -1, 48, 34, -7, -3, -1, 18, 33, -1,
+ 2, 32, 17, -1, 1, 16, 0
+};
+
+static short tab11[] =
+{
+-121,-113, -89, -59, -43, -27, -17, -7, -3, -1, 119, 103, -1, 118, 117,
+ -3, -1, 102, 71, -1, 116, -1, 87, 85, -5, -3, -1, 86, 101, 55,
+ -1, 115, 70, -9, -7, -3, -1, 69, 84, -1, 53, 83, 39, -1, 114,
+ -1, 100, 7, -5, -1, 113, -1, 23, 112, -3, -1, 54, 99, -1, 96,
+ -1, 68, 37, -13, -7, -5, -3, -1, 82, 5, 21, 98, -3, -1, 38,
+ 6, 22, -5, -1, 97, -1, 81, 52, -5, -1, 80, -1, 67, 51, -1,
+ 36, 66, -15, -11, -7, -3, -1, 20, 65, -1, 4, 64, -1, 35, 50,
+ -1, 19, 49, -5, -3, -1, 3, 48, 34, 33, -5, -1, 18, -1, 2,
+ 32, 17, -3, -1, 1, 16, 0
+};
+
+static short tab12[] =
+{
+-115, -99, -73, -45, -27, -17, -9, -5, -3, -1, 119, 103, 118, -1, 87,
+ 117, -3, -1, 102, 71, -1, 116, 101, -3, -1, 86, 55, -3, -1, 115,
+ 85, 39, -7, -3, -1, 114, 70, -1, 100, 23, -5, -1, 113, -1, 7,
+ 112, -1, 54, 99, -13, -9, -3, -1, 69, 84, -1, 68, -1, 6, 5,
+ -1, 38, 98, -5, -1, 97, -1, 22, 96, -3, -1, 53, 83, -1, 37,
+ 82, -17, -7, -3, -1, 21, 81, -1, 52, 67, -5, -3, -1, 80, 4,
+ 36, -1, 66, 20, -3, -1, 51, 65, -1, 35, 50, -11, -7, -5, -3,
+ -1, 64, 3, 48, 19, -1, 49, 34, -1, 18, 33, -7, -5, -3, -1,
+ 2, 32, 0, 17, -1, 1, 16
+};
+
+static short tab13[] =
+{
+-509,-503,-475,-405,-333,-265,-205,-153,-115, -83, -53, -35, -21, -13, -9,
+ -7, -5, -3, -1, 254, 252, 253, 237, 255, -1, 239, 223, -3, -1, 238,
+ 207, -1, 222, 191, -9, -3, -1, 251, 206, -1, 220, -1, 175, 233, -1,
+ 236, 221, -9, -5, -3, -1, 250, 205, 190, -1, 235, 159, -3, -1, 249,
+ 234, -1, 189, 219, -17, -9, -3, -1, 143, 248, -1, 204, -1, 174, 158,
+ -5, -1, 142, -1, 127, 126, 247, -5, -1, 218, -1, 173, 188, -3, -1,
+ 203, 246, 111, -15, -7, -3, -1, 232, 95, -1, 157, 217, -3, -1, 245,
+ 231, -1, 172, 187, -9, -3, -1, 79, 244, -3, -1, 202, 230, 243, -1,
+ 63, -1, 141, 216, -21, -9, -3, -1, 47, 242, -3, -1, 110, 156, 15,
+ -5, -3, -1, 201, 94, 171, -3, -1, 125, 215, 78, -11, -5, -3, -1,
+ 200, 214, 62, -1, 185, -1, 155, 170, -1, 31, 241, -23, -13, -5, -1,
+ 240, -1, 186, 229, -3, -1, 228, 140, -1, 109, 227, -5, -1, 226, -1,
+ 46, 14, -1, 30, 225, -15, -7, -3, -1, 224, 93, -1, 213, 124, -3,
+ -1, 199, 77, -1, 139, 184, -7, -3, -1, 212, 154, -1, 169, 108, -1,
+ 198, 61, -37, -21, -9, -5, -3, -1, 211, 123, 45, -1, 210, 29, -5,
+ -1, 183, -1, 92, 197, -3, -1, 153, 122, 195, -7, -5, -3, -1, 167,
+ 151, 75, 209, -3, -1, 13, 208, -1, 138, 168, -11, -7, -3, -1, 76,
+ 196, -1, 107, 182, -1, 60, 44, -3, -1, 194, 91, -3, -1, 181, 137,
+ 28, -43, -23, -11, -5, -1, 193, -1, 152, 12, -1, 192, -1, 180, 106,
+ -5, -3, -1, 166, 121, 59, -1, 179, -1, 136, 90, -11, -5, -1, 43,
+ -1, 165, 105, -1, 164, -1, 120, 135, -5, -1, 148, -1, 119, 118, 178,
+ -11, -3, -1, 27, 177, -3, -1, 11, 176, -1, 150, 74, -7, -3, -1,
+ 58, 163, -1, 89, 149, -1, 42, 162, -47, -23, -9, -3, -1, 26, 161,
+ -3, -1, 10, 104, 160, -5, -3, -1, 134, 73, 147, -3, -1, 57, 88,
+ -1, 133, 103, -9, -3, -1, 41, 146, -3, -1, 87, 117, 56, -5, -1,
+ 131, -1, 102, 71, -3, -1, 116, 86, -1, 101, 115, -11, -3, -1, 25,
+ 145, -3, -1, 9, 144, -1, 72, 132, -7, -5, -1, 114, -1, 70, 100,
+ 40, -1, 130, 24, -41, -27, -11, -5, -3, -1, 55, 39, 23, -1, 113,
+ -1, 85, 7, -7, -3, -1, 112, 54, -1, 99, 69, -3, -1, 84, 38,
+ -1, 98, 53, -5, -1, 129, -1, 8, 128, -3, -1, 22, 97, -1, 6,
+ 96, -13, -9, -5, -3, -1, 83, 68, 37, -1, 82, 5, -1, 21, 81,
+ -7, -3, -1, 52, 67, -1, 80, 36, -3, -1, 66, 51, 20, -19, -11,
+ -5, -1, 65, -1, 4, 64, -3, -1, 35, 50, 19, -3, -1, 49, 3,
+ -1, 48, 34, -3, -1, 18, 33, -1, 2, 32, -3, -1, 17, 1, 16,
+ 0
+};
+
+static short tab15[] =
+{
+-495,-445,-355,-263,-183,-115, -77, -43, -27, -13, -7, -3, -1, 255, 239,
+ -1, 254, 223, -1, 238, -1, 253, 207, -7, -3, -1, 252, 222, -1, 237,
+ 191, -1, 251, -1, 206, 236, -7, -3, -1, 221, 175, -1, 250, 190, -3,
+ -1, 235, 205, -1, 220, 159, -15, -7, -3, -1, 249, 234, -1, 189, 219,
+ -3, -1, 143, 248, -1, 204, 158, -7, -3, -1, 233, 127, -1, 247, 173,
+ -3, -1, 218, 188, -1, 111, -1, 174, 15, -19, -11, -3, -1, 203, 246,
+ -3, -1, 142, 232, -1, 95, 157, -3, -1, 245, 126, -1, 231, 172, -9,
+ -3, -1, 202, 187, -3, -1, 217, 141, 79, -3, -1, 244, 63, -1, 243,
+ 216, -33, -17, -9, -3, -1, 230, 47, -1, 242, -1, 110, 240, -3, -1,
+ 31, 241, -1, 156, 201, -7, -3, -1, 94, 171, -1, 186, 229, -3, -1,
+ 125, 215, -1, 78, 228, -15, -7, -3, -1, 140, 200, -1, 62, 109, -3,
+ -1, 214, 227, -1, 155, 185, -7, -3, -1, 46, 170, -1, 226, 30, -5,
+ -1, 225, -1, 14, 224, -1, 93, 213, -45, -25, -13, -7, -3, -1, 124,
+ 199, -1, 77, 139, -1, 212, -1, 184, 154, -7, -3, -1, 169, 108, -1,
+ 198, 61, -1, 211, 210, -9, -5, -3, -1, 45, 13, 29, -1, 123, 183,
+ -5, -1, 209, -1, 92, 208, -1, 197, 138, -17, -7, -3, -1, 168, 76,
+ -1, 196, 107, -5, -1, 182, -1, 153, 12, -1, 60, 195, -9, -3, -1,
+ 122, 167, -1, 166, -1, 192, 11, -1, 194, -1, 44, 91, -55, -29, -15,
+ -7, -3, -1, 181, 28, -1, 137, 152, -3, -1, 193, 75, -1, 180, 106,
+ -5, -3, -1, 59, 121, 179, -3, -1, 151, 136, -1, 43, 90, -11, -5,
+ -1, 178, -1, 165, 27, -1, 177, -1, 176, 105, -7, -3, -1, 150, 74,
+ -1, 164, 120, -3, -1, 135, 58, 163, -17, -7, -3, -1, 89, 149, -1,
+ 42, 162, -3, -1, 26, 161, -3, -1, 10, 160, 104, -7, -3, -1, 134,
+ 73, -1, 148, 57, -5, -1, 147, -1, 119, 9, -1, 88, 133, -53, -29,
+ -13, -7, -3, -1, 41, 103, -1, 118, 146, -1, 145, -1, 25, 144, -7,
+ -3, -1, 72, 132, -1, 87, 117, -3, -1, 56, 131, -1, 102, 71, -7,
+ -3, -1, 40, 130, -1, 24, 129, -7, -3, -1, 116, 8, -1, 128, 86,
+ -3, -1, 101, 55, -1, 115, 70, -17, -7, -3, -1, 39, 114, -1, 100,
+ 23, -3, -1, 85, 113, -3, -1, 7, 112, 54, -7, -3, -1, 99, 69,
+ -1, 84, 38, -3, -1, 98, 22, -3, -1, 6, 96, 53, -33, -19, -9,
+ -5, -1, 97, -1, 83, 68, -1, 37, 82, -3, -1, 21, 81, -3, -1,
+ 5, 80, 52, -7, -3, -1, 67, 36, -1, 66, 51, -1, 65, -1, 20,
+ 4, -9, -3, -1, 35, 50, -3, -1, 64, 3, 19, -3, -1, 49, 48,
+ 34, -9, -7, -3, -1, 18, 33, -1, 2, 32, 17, -3, -1, 1, 16,
+ 0
+};
+
+static short tab16[] =
+{
+-509,-503,-461,-323,-103, -37, -27, -15, -7, -3, -1, 239, 254, -1, 223,
+ 253, -3, -1, 207, 252, -1, 191, 251, -5, -1, 175, -1, 250, 159, -3,
+ -1, 249, 248, 143, -7, -3, -1, 127, 247, -1, 111, 246, 255, -9, -5,
+ -3, -1, 95, 245, 79, -1, 244, 243, -53, -1, 240, -1, 63, -29, -19,
+ -13, -7, -5, -1, 206, -1, 236, 221, 222, -1, 233, -1, 234, 217, -1,
+ 238, -1, 237, 235, -3, -1, 190, 205, -3, -1, 220, 219, 174, -11, -5,
+ -1, 204, -1, 173, 218, -3, -1, 126, 172, 202, -5, -3, -1, 201, 125,
+ 94, 189, 242, -93, -5, -3, -1, 47, 15, 31, -1, 241, -49, -25, -13,
+ -5, -1, 158, -1, 188, 203, -3, -1, 142, 232, -1, 157, 231, -7, -3,
+ -1, 187, 141, -1, 216, 110, -1, 230, 156, -13, -7, -3, -1, 171, 186,
+ -1, 229, 215, -1, 78, -1, 228, 140, -3, -1, 200, 62, -1, 109, -1,
+ 214, 155, -19, -11, -5, -3, -1, 185, 170, 225, -1, 212, -1, 184, 169,
+ -5, -1, 123, -1, 183, 208, 227, -7, -3, -1, 14, 224, -1, 93, 213,
+ -3, -1, 124, 199, -1, 77, 139, -75, -45, -27, -13, -7, -3, -1, 154,
+ 108, -1, 198, 61, -3, -1, 92, 197, 13, -7, -3, -1, 138, 168, -1,
+ 153, 76, -3, -1, 182, 122, 60, -11, -5, -3, -1, 91, 137, 28, -1,
+ 192, -1, 152, 121, -1, 226, -1, 46, 30, -15, -7, -3, -1, 211, 45,
+ -1, 210, 209, -5, -1, 59, -1, 151, 136, 29, -7, -3, -1, 196, 107,
+ -1, 195, 167, -1, 44, -1, 194, 181, -23, -13, -7, -3, -1, 193, 12,
+ -1, 75, 180, -3, -1, 106, 166, 179, -5, -3, -1, 90, 165, 43, -1,
+ 178, 27, -13, -5, -1, 177, -1, 11, 176, -3, -1, 105, 150, -1, 74,
+ 164, -5, -3, -1, 120, 135, 163, -3, -1, 58, 89, 42, -97, -57, -33,
+ -19, -11, -5, -3, -1, 149, 104, 161, -3, -1, 134, 119, 148, -5, -3,
+ -1, 73, 87, 103, 162, -5, -1, 26, -1, 10, 160, -3, -1, 57, 147,
+ -1, 88, 133, -9, -3, -1, 41, 146, -3, -1, 118, 9, 25, -5, -1,
+ 145, -1, 144, 72, -3, -1, 132, 117, -1, 56, 131, -21, -11, -5, -3,
+ -1, 102, 40, 130, -3, -1, 71, 116, 24, -3, -1, 129, 128, -3, -1,
+ 8, 86, 55, -9, -5, -1, 115, -1, 101, 70, -1, 39, 114, -5, -3,
+ -1, 100, 85, 7, 23, -23, -13, -5, -1, 113, -1, 112, 54, -3, -1,
+ 99, 69, -1, 84, 38, -3, -1, 98, 22, -1, 97, -1, 6, 96, -9,
+ -5, -1, 83, -1, 53, 68, -1, 37, 82, -1, 81, -1, 21, 5, -33,
+ -23, -13, -7, -3, -1, 52, 67, -1, 80, 36, -3, -1, 66, 51, 20,
+ -5, -1, 65, -1, 4, 64, -1, 35, 50, -3, -1, 19, 49, -3, -1,
+ 3, 48, 34, -3, -1, 18, 33, -1, 2, 32, -3, -1, 17, 1, 16,
+ 0
+};
+
+static short tab24[] =
+{
+-451,-117, -43, -25, -15, -7, -3, -1, 239, 254, -1, 223, 253, -3, -1,
+ 207, 252, -1, 191, 251, -5, -1, 250, -1, 175, 159, -1, 249, 248, -9,
+ -5, -3, -1, 143, 127, 247, -1, 111, 246, -3, -1, 95, 245, -1, 79,
+ 244, -71, -7, -3, -1, 63, 243, -1, 47, 242, -5, -1, 241, -1, 31,
+ 240, -25, -9, -1, 15, -3, -1, 238, 222, -1, 237, 206, -7, -3, -1,
+ 236, 221, -1, 190, 235, -3, -1, 205, 220, -1, 174, 234, -15, -7, -3,
+ -1, 189, 219, -1, 204, 158, -3, -1, 233, 173, -1, 218, 188, -7, -3,
+ -1, 203, 142, -1, 232, 157, -3, -1, 217, 126, -1, 231, 172, 255,-235,
+-143, -77, -45, -25, -15, -7, -3, -1, 202, 187, -1, 141, 216, -5, -3,
+ -1, 14, 224, 13, 230, -5, -3, -1, 110, 156, 201, -1, 94, 186, -9,
+ -5, -1, 229, -1, 171, 125, -1, 215, 228, -3, -1, 140, 200, -3, -1,
+ 78, 46, 62, -15, -7, -3, -1, 109, 214, -1, 227, 155, -3, -1, 185,
+ 170, -1, 226, 30, -7, -3, -1, 225, 93, -1, 213, 124, -3, -1, 199,
+ 77, -1, 139, 184, -31, -15, -7, -3, -1, 212, 154, -1, 169, 108, -3,
+ -1, 198, 61, -1, 211, 45, -7, -3, -1, 210, 29, -1, 123, 183, -3,
+ -1, 209, 92, -1, 197, 138, -17, -7, -3, -1, 168, 153, -1, 76, 196,
+ -3, -1, 107, 182, -3, -1, 208, 12, 60, -7, -3, -1, 195, 122, -1,
+ 167, 44, -3, -1, 194, 91, -1, 181, 28, -57, -35, -19, -7, -3, -1,
+ 137, 152, -1, 193, 75, -5, -3, -1, 192, 11, 59, -3, -1, 176, 10,
+ 26, -5, -1, 180, -1, 106, 166, -3, -1, 121, 151, -3, -1, 160, 9,
+ 144, -9, -3, -1, 179, 136, -3, -1, 43, 90, 178, -7, -3, -1, 165,
+ 27, -1, 177, 105, -1, 150, 164, -17, -9, -5, -3, -1, 74, 120, 135,
+ -1, 58, 163, -3, -1, 89, 149, -1, 42, 162, -7, -3, -1, 161, 104,
+ -1, 134, 119, -3, -1, 73, 148, -1, 57, 147, -63, -31, -15, -7, -3,
+ -1, 88, 133, -1, 41, 103, -3, -1, 118, 146, -1, 25, 145, -7, -3,
+ -1, 72, 132, -1, 87, 117, -3, -1, 56, 131, -1, 102, 40, -17, -7,
+ -3, -1, 130, 24, -1, 71, 116, -5, -1, 129, -1, 8, 128, -1, 86,
+ 101, -7, -5, -1, 23, -1, 7, 112, 115, -3, -1, 55, 39, 114, -15,
+ -7, -3, -1, 70, 100, -1, 85, 113, -3, -1, 54, 99, -1, 69, 84,
+ -7, -3, -1, 38, 98, -1, 22, 97, -5, -3, -1, 6, 96, 53, -1,
+ 83, 68, -51, -37, -23, -15, -9, -3, -1, 37, 82, -1, 21, -1, 5,
+ 80, -1, 81, -1, 52, 67, -3, -1, 36, 66, -1, 51, 20, -9, -5,
+ -1, 65, -1, 4, 64, -1, 35, 50, -1, 19, 49, -7, -5, -3, -1,
+ 3, 48, 34, 18, -1, 33, -1, 2, 32, -3, -1, 17, 1, -1, 16,
+ 0
+};
+
+static short tab_c0[] =
+{
+ -29, -21, -13, -7, -3, -1, 11, 15, -1, 13, 14, -3, -1, 7, 5,
+ 9, -3, -1, 6, 3, -1, 10, 12, -3, -1, 2, 1, -1, 4, 8,
+ 0
+};
+
+static short tab_c1[] =
+{
+ -15, -7, -3, -1, 15, 14, -1, 13, 12, -3, -1, 11, 10, -1, 9,
+ 8, -7, -3, -1, 7, 6, -1, 5, 4, -3, -1, 3, 2, -1, 1,
+ 0
+};
+
+
+
+static struct newhuff ht[] =
+{
+ { /* 0 */ 0 , tab0 } ,
+ { /* 2 */ 0 , tab1 } ,
+ { /* 3 */ 0 , tab2 } ,
+ { /* 3 */ 0 , tab3 } ,
+ { /* 0 */ 0 , tab0 } ,
+ { /* 4 */ 0 , tab5 } ,
+ { /* 4 */ 0 , tab6 } ,
+ { /* 6 */ 0 , tab7 } ,
+ { /* 6 */ 0 , tab8 } ,
+ { /* 6 */ 0 , tab9 } ,
+ { /* 8 */ 0 , tab10 } ,
+ { /* 8 */ 0 , tab11 } ,
+ { /* 8 */ 0 , tab12 } ,
+ { /* 16 */ 0 , tab13 } ,
+ { /* 0 */ 0 , tab0 } ,
+ { /* 16 */ 0 , tab15 } ,
+
+ { /* 16 */ 1 , tab16 } ,
+ { /* 16 */ 2 , tab16 } ,
+ { /* 16 */ 3 , tab16 } ,
+ { /* 16 */ 4 , tab16 } ,
+ { /* 16 */ 6 , tab16 } ,
+ { /* 16 */ 8 , tab16 } ,
+ { /* 16 */ 10, tab16 } ,
+ { /* 16 */ 13, tab16 } ,
+ { /* 16 */ 4 , tab24 } ,
+ { /* 16 */ 5 , tab24 } ,
+ { /* 16 */ 6 , tab24 } ,
+ { /* 16 */ 7 , tab24 } ,
+ { /* 16 */ 8 , tab24 } ,
+ { /* 16 */ 9 , tab24 } ,
+ { /* 16 */ 11, tab24 } ,
+ { /* 16 */ 13, tab24 }
+};
+
+static struct newhuff htc[] =
+{
+ { /* 1 , 1 , */ 0 , tab_c0 } ,
+ { /* 1 , 1 , */ 0 , tab_c1 }
+};
+
+
diff --git a/mpg123_artsplugin/mpg123/l2tables.h b/mpg123_artsplugin/mpg123/l2tables.h
new file mode 100644
index 00000000..643201ef
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/l2tables.h
@@ -0,0 +1,154 @@
+/*
+ * Layer 2 Alloc tables ..
+ * most other tables are calculated on program start (which is (of course)
+ * not ISO-conform) ..
+ * Layer-3 huffman table is in huffman.h
+ */
+
+struct al_table alloc_0[] = {
+ {4,0},{5,3},{3,-3},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},{10,-511},
+ {11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},{16,-32767},
+ {4,0},{5,3},{3,-3},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},{10,-511},
+ {11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},{16,-32767},
+ {4,0},{5,3},{3,-3},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},{10,-511},
+ {11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},{16,-32767},
+ {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},
+ {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767},
+ {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},
+ {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767},
+ {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},
+ {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767},
+ {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},
+ {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767},
+ {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},
+ {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767},
+ {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},
+ {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767},
+ {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},
+ {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767},
+ {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},
+ {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {2,0},{5,3},{7,5},{16,-32767},
+ {2,0},{5,3},{7,5},{16,-32767},
+ {2,0},{5,3},{7,5},{16,-32767},
+ {2,0},{5,3},{7,5},{16,-32767} };
+
+struct al_table alloc_1[] = {
+ {4,0},{5,3},{3,-3},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},{10,-511},
+ {11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},{16,-32767},
+ {4,0},{5,3},{3,-3},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},{10,-511},
+ {11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},{16,-32767},
+ {4,0},{5,3},{3,-3},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},{10,-511},
+ {11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},{16,-32767},
+ {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},
+ {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767},
+ {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},
+ {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767},
+ {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},
+ {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767},
+ {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},
+ {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767},
+ {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},
+ {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767},
+ {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},
+ {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767},
+ {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},
+ {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767},
+ {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},
+ {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767},
+ {2,0},{5,3},{7,5},{16,-32767},
+ {2,0},{5,3},{7,5},{16,-32767},
+ {2,0},{5,3},{7,5},{16,-32767},
+ {2,0},{5,3},{7,5},{16,-32767},
+ {2,0},{5,3},{7,5},{16,-32767},
+ {2,0},{5,3},{7,5},{16,-32767},
+ {2,0},{5,3},{7,5},{16,-32767} };
+
+struct al_table alloc_2[] = {
+ {4,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},
+ {10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},
+ {4,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},
+ {10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63} };
+
+struct al_table alloc_3[] = {
+ {4,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},
+ {10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},
+ {4,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},
+ {10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63} };
+
+struct al_table alloc_4[] = {
+ {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},
+ {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191},
+ {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},
+ {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191},
+ {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},
+ {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191},
+ {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},
+ {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},
+ {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},
+ {2,0},{5,3},{7,5},{10,9},
+ {2,0},{5,3},{7,5},{10,9},
+ {2,0},{5,3},{7,5},{10,9},
+ {2,0},{5,3},{7,5},{10,9},
+ {2,0},{5,3},{7,5},{10,9},
+ {2,0},{5,3},{7,5},{10,9},
+ {2,0},{5,3},{7,5},{10,9},
+ {2,0},{5,3},{7,5},{10,9},
+ {2,0},{5,3},{7,5},{10,9},
+ {2,0},{5,3},{7,5},{10,9},
+ {2,0},{5,3},{7,5},{10,9},
+ {2,0},{5,3},{7,5},{10,9},
+ {2,0},{5,3},{7,5},{10,9},
+ {2,0},{5,3},{7,5},{10,9},
+ {2,0},{5,3},{7,5},{10,9},
+ {2,0},{5,3},{7,5},{10,9},
+ {2,0},{5,3},{7,5},{10,9},
+ {2,0},{5,3},{7,5},{10,9},
+ {2,0},{5,3},{7,5},{10,9} };
+
diff --git a/mpg123_artsplugin/mpg123/layer1.c b/mpg123_artsplugin/mpg123/layer1.c
new file mode 100644
index 00000000..a33335ab
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/layer1.c
@@ -0,0 +1,157 @@
+/*
+ * Mpeg Layer-1 audio decoder
+ * --------------------------
+ * copyright (c) 1995 by Michael Hipp, All rights reserved. See also 'README'
+ * near unoptimzed ...
+ *
+ * may have a few bugs after last optimization ...
+ *
+ */
+
+#include "mpg123.h"
+
+#include "getbits.h"
+
+void I_step_one(unsigned int balloc[], unsigned int scale_index[2][SBLIMIT],struct frame *fr)
+{
+ unsigned int *ba=balloc;
+ unsigned int *sca = (unsigned int *) scale_index;
+
+ if(fr->stereo) {
+ int i;
+ int jsbound = fr->jsbound;
+ for (i=0;i<jsbound;i++) {
+ *ba++ = getbits(&bsi,4);
+ *ba++ = getbits(&bsi,4);
+ }
+ for (i=jsbound;i<SBLIMIT;i++)
+ *ba++ = getbits(&bsi,4);
+
+ ba = balloc;
+
+ for (i=0;i<jsbound;i++) {
+ if ((*ba++))
+ *sca++ = getbits(&bsi,6);
+ if ((*ba++))
+ *sca++ = getbits(&bsi,6);
+ }
+ for (i=jsbound;i<SBLIMIT;i++)
+ if ((*ba++)) {
+ *sca++ = getbits(&bsi,6);
+ *sca++ = getbits(&bsi,6);
+ }
+ }
+ else {
+ int i;
+ for (i=0;i<SBLIMIT;i++)
+ *ba++ = getbits(&bsi,4);
+ ba = balloc;
+ for (i=0;i<SBLIMIT;i++)
+ if ((*ba++))
+ *sca++ = getbits(&bsi,6);
+ }
+}
+
+void I_step_two(real fraction[2][SBLIMIT],unsigned int balloc[2*SBLIMIT],
+ unsigned int scale_index[2][SBLIMIT],struct frame *fr)
+{
+ int i,n;
+ int smpb[2*SBLIMIT]; /* values: 0-65535 */
+ int *sample;
+ register unsigned int *ba;
+ register unsigned int *sca = (unsigned int *) scale_index;
+
+ if(fr->stereo) {
+ int jsbound = fr->jsbound;
+ register real *f0 = fraction[0];
+ register real *f1 = fraction[1];
+ ba = balloc;
+ for (sample=smpb,i=0;i<jsbound;i++) {
+ if ((n = *ba++))
+ *sample++ = getbits(&bsi,n+1);
+ if ((n = *ba++))
+ *sample++ = getbits(&bsi,n+1);
+ }
+ for (i=jsbound;i<SBLIMIT;i++)
+ if ((n = *ba++))
+ *sample++ = getbits(&bsi,n+1);
+
+ ba = balloc;
+ for (sample=smpb,i=0;i<jsbound;i++) {
+ if((n=*ba++))
+ *f0++ = (real) ( ((-1)<<n) + (*sample++) + 1) * muls[n+1][*sca++];
+ else
+ *f0++ = 0.0;
+ if((n=*ba++))
+ *f1++ = (real) ( ((-1)<<n) + (*sample++) + 1) * muls[n+1][*sca++];
+ else
+ *f1++ = 0.0;
+ }
+ for (i=jsbound;i<SBLIMIT;i++) {
+ if ((n=*ba++)) {
+ real samp = ( ((-1)<<n) + (*sample++) + 1);
+ *f0++ = samp * muls[n+1][*sca++];
+ *f1++ = samp * muls[n+1][*sca++];
+ }
+ else
+ *f0++ = *f1++ = 0.0;
+ }
+ for(i=fr->down_sample_sblimit;i<32;i++)
+ fraction[0][i] = fraction[1][i] = 0.0;
+ }
+ else {
+ register real *f0 = fraction[0];
+ ba = balloc;
+ for (sample=smpb,i=0;i<SBLIMIT;i++)
+ if ((n = *ba++))
+ *sample++ = getbits(&bsi,n+1);
+ ba = balloc;
+ for (sample=smpb,i=0;i<SBLIMIT;i++) {
+ if((n=*ba++))
+ *f0++ = (real) ( ((-1)<<n) + (*sample++) + 1) * muls[n+1][*sca++];
+ else
+ *f0++ = 0.0;
+ }
+ for(i=fr->down_sample_sblimit;i<32;i++)
+ fraction[0][i] = 0.0;
+ }
+}
+
+int do_layer1(struct mpstr *mp,struct frame *fr,int outmode,struct audio_info_struct *ai)
+{
+ int clip=0;
+ int i,stereo = fr->stereo;
+ unsigned int balloc[2*SBLIMIT];
+ unsigned int scale_index[2][SBLIMIT];
+ real fraction[2][SBLIMIT];
+ int single = fr->single;
+
+ fr->jsbound = (fr->mode == MPG_MD_JOINT_STEREO) ? (fr->mode_ext<<2)+4 : 32;
+
+ if(stereo == 1 || single == 3)
+ single = 0;
+
+ I_step_one(balloc,scale_index,fr);
+
+ for (i=0;i<SCALE_BLOCK;i++)
+ {
+ I_step_two(fraction,balloc,scale_index,fr);
+
+ if(single >= 0)
+ {
+ clip += (fr->synth_mono)( (real *) fraction[single],pcm_sample,&pcm_point);
+ }
+ else {
+ int p1 = pcm_point;
+ clip += (fr->synth)( (real *) fraction[0],0,pcm_sample,&p1);
+ clip += (fr->synth)( (real *) fraction[1],1,pcm_sample,&pcm_point);
+ }
+
+ if(pcm_point >= audiobufsize)
+ audio_flush(outmode,ai);
+ }
+
+ return clip;
+}
+
+
diff --git a/mpg123_artsplugin/mpg123/layer2.c b/mpg123_artsplugin/mpg123/layer2.c
new file mode 100644
index 00000000..54ba1fd0
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/layer2.c
@@ -0,0 +1,318 @@
+/*
+ * Mpeg Layer-2 audio decoder
+ * --------------------------
+ * copyright (c) 1995 by Michael Hipp, All rights reserved. See also 'README'
+ *
+ */
+
+#include "mpg123.h"
+#include "l2tables.h"
+
+#include "getbits.h"
+
+static int grp_3tab[32 * 3] = { 0, }; /* used: 27 */
+static int grp_5tab[128 * 3] = { 0, }; /* used: 125 */
+static int grp_9tab[1024 * 3] = { 0, }; /* used: 729 */
+
+real muls[27][64]; /* also used by layer 1 */
+
+
+void init_layer2(void)
+{
+ static double mulmul[27] = {
+ 0.0 , -2.0/3.0 , 2.0/3.0 ,
+ 2.0/7.0 , 2.0/15.0 , 2.0/31.0, 2.0/63.0 , 2.0/127.0 , 2.0/255.0 ,
+ 2.0/511.0 , 2.0/1023.0 , 2.0/2047.0 , 2.0/4095.0 , 2.0/8191.0 ,
+ 2.0/16383.0 , 2.0/32767.0 , 2.0/65535.0 ,
+ -4.0/5.0 , -2.0/5.0 , 2.0/5.0, 4.0/5.0 ,
+ -8.0/9.0 , -4.0/9.0 , -2.0/9.0 , 2.0/9.0 , 4.0/9.0 , 8.0/9.0 };
+ static int base[3][9] = {
+ { 1 , 0, 2 , } ,
+ { 17, 18, 0 , 19, 20 , } ,
+ { 21, 1, 22, 23, 0, 24, 25, 2, 26 } };
+ int i,j,k,l,len;
+ real *table;
+ static int tablen[3] = { 3 , 5 , 9 };
+ static int *itable,*tables[3] = { grp_3tab , grp_5tab , grp_9tab };
+#ifdef REAL_IS_FIXED
+ const double twotothethird = pow((double)2.0, (double)1/3);
+#endif
+
+ for(i=0;i<3;i++)
+ {
+ itable = tables[i];
+ len = tablen[i];
+ for(j=0;j<len;j++)
+ for(k=0;k<len;k++)
+ for(l=0;l<len;l++)
+ {
+ *itable++ = base[i][l];
+ *itable++ = base[i][k];
+ *itable++ = base[i][j];
+ }
+ }
+
+ for(k=0;k<27;k++)
+ {
+ double m=mulmul[k];
+#ifdef REAL_IS_FIXED
+ double oldtable = m * 2.0 * twotothethird;
+#endif
+
+ table = muls[k];
+#ifdef USE_MMX
+ if(!param.down_sample)
+ for(j=3,i=0;i<63;i++,j--)
+ *table++ = 16384 * m * pow(2.0,(double) j / 3.0);
+ else
+#endif
+ for(j=3,i=0;i<63;i++,j--) {
+#ifdef REAL_IS_FIXED
+ *table = oldtable / twotothethird;
+ oldtable = *table++;
+#else
+ *table++ = m * pow(2.0,(double) j / 3.0);
+#endif
+ }
+ *table++ = 0.0;
+ }
+}
+
+
+void II_step_one(unsigned int *bit_alloc,int *scale,struct frame *fr)
+{
+ int stereo = fr->stereo-1;
+ int sblimit = fr->II_sblimit;
+ int jsbound = fr->jsbound;
+ int sblimit2 = fr->II_sblimit<<stereo;
+ struct al_table *alloc1 = fr->alloc;
+ int i;
+ static unsigned int scfsi_buf[64];
+ unsigned int *scfsi,*bita;
+ int sc,step;
+
+ bita = bit_alloc;
+ if(stereo)
+ {
+ for (i=jsbound;i;i--,alloc1+=(1<<step))
+ {
+ *bita++ = (char) getbits(&bsi,step=alloc1->bits);
+ *bita++ = (char) getbits(&bsi,step);
+ }
+ for (i=sblimit-jsbound;i;i--,alloc1+=(1<<step))
+ {
+ bita[0] = (char) getbits(&bsi,step=alloc1->bits);
+ bita[1] = bita[0];
+ bita+=2;
+ }
+ bita = bit_alloc;
+ scfsi=scfsi_buf;
+ for (i=sblimit2;i;i--)
+ if (*bita++)
+ *scfsi++ = (char) getbits_fast(&bsi,2);
+ }
+ else /* mono */
+ {
+ for (i=sblimit;i;i--,alloc1+=(1<<step))
+ *bita++ = (char) getbits(&bsi,step=alloc1->bits);
+ bita = bit_alloc;
+ scfsi=scfsi_buf;
+ for (i=sblimit;i;i--)
+ if (*bita++)
+ *scfsi++ = (char) getbits_fast(&bsi,2);
+ }
+
+ bita = bit_alloc;
+ scfsi=scfsi_buf;
+ for (i=sblimit2;i;i--)
+ if (*bita++)
+ switch (*scfsi++)
+ {
+ case 0:
+ *scale++ = getbits_fast(&bsi,6);
+ *scale++ = getbits_fast(&bsi,6);
+ *scale++ = getbits_fast(&bsi,6);
+ break;
+ case 1 :
+ *scale++ = sc = getbits_fast(&bsi,6);
+ *scale++ = sc;
+ *scale++ = getbits_fast(&bsi,6);
+ break;
+ case 2:
+ *scale++ = sc = getbits_fast(&bsi,6);
+ *scale++ = sc;
+ *scale++ = sc;
+ break;
+ default: /* case 3 */
+ *scale++ = getbits_fast(&bsi,6);
+ *scale++ = sc = getbits_fast(&bsi,6);
+ *scale++ = sc;
+ break;
+ }
+
+}
+
+void II_step_two(unsigned int *bit_alloc,real fraction[2][4][SBLIMIT],int *scale,struct frame *fr,int x1)
+{
+ int i,j,k,ba;
+ int stereo = fr->stereo;
+ int sblimit = fr->II_sblimit;
+ int jsbound = fr->jsbound;
+ struct al_table *alloc2,*alloc1 = fr->alloc;
+ unsigned int *bita=bit_alloc;
+ int d1,step;
+
+ for (i=0;i<jsbound;i++,alloc1+=(1<<step))
+ {
+ step = alloc1->bits;
+ for (j=0;j<stereo;j++)
+ {
+ if ( (ba=*bita++) )
+ {
+ k=(alloc2 = alloc1+ba)->bits;
+ if( (d1=alloc2->d) < 0)
+ {
+ real cm=muls[k][scale[x1]];
+ fraction[j][0][i] = ((real) ((int)getbits(&bsi,k) + d1)) * cm;
+ fraction[j][1][i] = ((real) ((int)getbits(&bsi,k) + d1)) * cm;
+ fraction[j][2][i] = ((real) ((int)getbits(&bsi,k) + d1)) * cm;
+ }
+ else
+ {
+ static int *table[] = { 0,0,0,grp_3tab,0,grp_5tab,0,0,0,grp_9tab };
+ unsigned int idx,*tab,m=scale[x1];
+ idx = (unsigned int) getbits(&bsi,k);
+ tab = (unsigned int *) (table[d1] + idx + idx + idx);
+ fraction[j][0][i] = muls[*tab++][m];
+ fraction[j][1][i] = muls[*tab++][m];
+ fraction[j][2][i] = muls[*tab][m];
+ }
+ scale+=3;
+ }
+ else
+ fraction[j][0][i] = fraction[j][1][i] = fraction[j][2][i] = 0.0;
+ }
+ }
+
+ for (i=jsbound;i<sblimit;i++,alloc1+=(1<<step))
+ {
+ step = alloc1->bits;
+ bita++; /* channel 1 and channel 2 bitalloc are the same */
+ if ( (ba=*bita++) )
+ {
+ k=(alloc2 = alloc1+ba)->bits;
+ if( (d1=alloc2->d) < 0)
+ {
+ real cm;
+ cm=muls[k][scale[x1+3]];
+ fraction[1][0][i] = (fraction[0][0][i] = (real) ((int)getbits(&bsi,k) + d1) ) * cm;
+ fraction[1][1][i] = (fraction[0][1][i] = (real) ((int)getbits(&bsi,k) + d1) ) * cm;
+ fraction[1][2][i] = (fraction[0][2][i] = (real) ((int)getbits(&bsi,k) + d1) ) * cm;
+ cm=muls[k][scale[x1]];
+ fraction[0][0][i] *= cm; fraction[0][1][i] *= cm; fraction[0][2][i] *= cm;
+ }
+ else
+ {
+ static int *table[] = { 0,0,0,grp_3tab,0,grp_5tab,0,0,0,grp_9tab };
+ unsigned int idx,*tab,m1,m2;
+ m1 = scale[x1]; m2 = scale[x1+3];
+ idx = (unsigned int) getbits(&bsi,k);
+ tab = (unsigned int *) (table[d1] + idx + idx + idx);
+ fraction[0][0][i] = muls[*tab][m1]; fraction[1][0][i] = muls[*tab++][m2];
+ fraction[0][1][i] = muls[*tab][m1]; fraction[1][1][i] = muls[*tab++][m2];
+ fraction[0][2][i] = muls[*tab][m1]; fraction[1][2][i] = muls[*tab][m2];
+ }
+ scale+=6;
+ }
+ else {
+ fraction[0][0][i] = fraction[0][1][i] = fraction[0][2][i] =
+ fraction[1][0][i] = fraction[1][1][i] = fraction[1][2][i] = 0.0;
+ }
+/*
+ should we use individual scalefac for channel 2 or
+ is the current way the right one , where we just copy channel 1 to
+ channel 2 ??
+ The current 'strange' thing is, that we throw away the scalefac
+ values for the second channel ...!!
+-> changed .. now we use the scalefac values of channel one !!
+*/
+ }
+
+ if(sblimit > (fr->down_sample_sblimit) )
+ sblimit = fr->down_sample_sblimit;
+
+ for(i=sblimit;i<SBLIMIT;i++)
+ for (j=0;j<stereo;j++)
+ fraction[j][0][i] = fraction[j][1][i] = fraction[j][2][i] = 0.0;
+
+}
+
+static void II_select_table(struct frame *fr)
+{
+ static int translate[3][2][16] =
+ { { { 0,2,2,2,2,2,2,0,0,0,1,1,1,1,1,0 } ,
+ { 0,2,2,0,0,0,1,1,1,1,1,1,1,1,1,0 } } ,
+ { { 0,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0 } ,
+ { 0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0 } } ,
+ { { 0,3,3,3,3,3,3,0,0,0,1,1,1,1,1,0 } ,
+ { 0,3,3,0,0,0,1,1,1,1,1,1,1,1,1,0 } } };
+
+ int table,sblim;
+ static struct al_table *tables[5] =
+ { alloc_0, alloc_1, alloc_2, alloc_3 , alloc_4 };
+ static int sblims[5] = { 27 , 30 , 8, 12 , 30 };
+
+ if(fr->lsf)
+ table = 4;
+ else
+ table = translate[fr->sampling_frequency][2-fr->stereo][fr->bitrate_index];
+ sblim = sblims[table];
+
+ fr->alloc = tables[table];
+ fr->II_sblimit = sblim;
+}
+
+
+int do_layer2(struct mpstr *mp,struct frame *fr,int outmode,struct audio_info_struct *ai)
+{
+ int clip=0;
+ int i,j;
+ int stereo = fr->stereo;
+ real fraction[2][4][SBLIMIT]; /* pick_table clears unused subbands */
+ unsigned int bit_alloc[64];
+ int scale[192];
+ int single = fr->single;
+
+ II_select_table(fr);
+ fr->jsbound = (fr->mode == MPG_MD_JOINT_STEREO) ?
+ (fr->mode_ext<<2)+4 : fr->II_sblimit;
+
+ if(stereo == 1 || single == 3)
+ single = 0;
+
+ II_step_one(bit_alloc, scale, fr);
+
+ for (i=0;i<SCALE_BLOCK;i++)
+ {
+ II_step_two(bit_alloc,fraction,scale,fr,i>>2);
+ for (j=0;j<3;j++)
+ {
+ if(single >= 0)
+ {
+ clip += (fr->synth_mono) (fraction[single][j],pcm_sample,&pcm_point);
+ }
+ else {
+ int p1 = pcm_point;
+ clip += (fr->synth) (fraction[0][j],0,pcm_sample,&p1);
+ clip += (fr->synth) (fraction[1][j],1,pcm_sample,&pcm_point);
+ }
+
+ if(pcm_point >= audiobufsize)
+ audio_flush(outmode,ai);
+ }
+ }
+
+ return clip;
+}
+
+
diff --git a/mpg123_artsplugin/mpg123/layer3.c b/mpg123_artsplugin/mpg123/layer3.c
new file mode 100644
index 00000000..b4f33510
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/layer3.c
@@ -0,0 +1,1880 @@
+/*
+ * Mpeg Layer-3 audio decoder
+ * --------------------------
+ * copyright (c) 1995-1999 by Michael Hipp.
+ * All rights reserved. See also 'README'
+ *
+ * Optimize-TODO: put short bands into the band-field without the stride
+ * of 3 reals
+ * Length-optimze: unify long and short band code where it is possible
+ */
+
+#if 0
+#define L3_DEBUG 1
+#endif
+
+#if 0
+#define CUT_HF
+#endif
+
+#include <stdlib.h>
+#include "mpg123.h"
+#include "huffman.h"
+
+#include "common.h"
+
+#include "getbits.h"
+
+static real ispow[8207];
+static real aa_ca[8],aa_cs[8];
+static real COS1[12][6];
+static real win[4][36];
+static real win1[4][36];
+static real gainpow2[256+118+4];
+
+/* non static for external 3dnow functions */
+real COS9[9];
+static real COS6_1,COS6_2;
+real tfcos36[9];
+
+static real tfcos12[3];
+#define NEW_DCT9
+#ifdef NEW_DCT9
+static real cos9[3],cos18[3];
+#endif
+
+struct bandInfoStruct {
+ int longIdx[23];
+ int longDiff[22];
+ int shortIdx[14];
+ int shortDiff[13];
+};
+
+int longLimit[9][23];
+int shortLimit[9][14];
+
+struct bandInfoStruct bandInfo[9] = {
+
+/* MPEG 1.0 */
+ { {0,4,8,12,16,20,24,30,36,44,52,62,74, 90,110,134,162,196,238,288,342,418,576},
+ {4,4,4,4,4,4,6,6,8, 8,10,12,16,20,24,28,34,42,50,54, 76,158},
+ {0,4*3,8*3,12*3,16*3,22*3,30*3,40*3,52*3,66*3, 84*3,106*3,136*3,192*3},
+ {4,4,4,4,6,8,10,12,14,18,22,30,56} } ,
+
+ { {0,4,8,12,16,20,24,30,36,42,50,60,72, 88,106,128,156,190,230,276,330,384,576},
+ {4,4,4,4,4,4,6,6,6, 8,10,12,16,18,22,28,34,40,46,54, 54,192},
+ {0,4*3,8*3,12*3,16*3,22*3,28*3,38*3,50*3,64*3, 80*3,100*3,126*3,192*3},
+ {4,4,4,4,6,6,10,12,14,16,20,26,66} } ,
+
+ { {0,4,8,12,16,20,24,30,36,44,54,66,82,102,126,156,194,240,296,364,448,550,576} ,
+ {4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102, 26} ,
+ {0,4*3,8*3,12*3,16*3,22*3,30*3,42*3,58*3,78*3,104*3,138*3,180*3,192*3} ,
+ {4,4,4,4,6,8,12,16,20,26,34,42,12} } ,
+
+/* MPEG 2.0 */
+ { {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576},
+ {6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54 } ,
+ {0,4*3,8*3,12*3,18*3,24*3,32*3,42*3,56*3,74*3,100*3,132*3,174*3,192*3} ,
+ {4,4,4,6,6,8,10,14,18,26,32,42,18 } } ,
+/*
+ { {0,6,12,18,24,30,36,44,54,66,80,96,114,136,162,194,232,278,330,394,464,540,576},
+ {6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,52,64,70,76,36 } ,
+*/
+/* changed 19th value fropm 330 to 332 */
+ { {0,6,12,18,24,30,36,44,54,66,80,96,114,136,162,194,232,278,332,394,464,540,576},
+ {6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36 } ,
+ {0,4*3,8*3,12*3,18*3,26*3,36*3,48*3,62*3,80*3,104*3,136*3,180*3,192*3} ,
+ {4,4,4,6,8,10,12,14,18,24,32,44,12 } } ,
+
+ { {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576},
+ {6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54 },
+ {0,4*3,8*3,12*3,18*3,26*3,36*3,48*3,62*3,80*3,104*3,134*3,174*3,192*3},
+ {4,4,4,6,8,10,12,14,18,24,30,40,18 } } ,
+/* MPEG 2.5 */
+ { {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576} ,
+ {6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54},
+ {0,12,24,36,54,78,108,144,186,240,312,402,522,576},
+ {4,4,4,6,8,10,12,14,18,24,30,40,18} },
+ { {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576} ,
+ {6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54},
+ {0,12,24,36,54,78,108,144,186,240,312,402,522,576},
+ {4,4,4,6,8,10,12,14,18,24,30,40,18} },
+ { {0,12,24,36,48,60,72,88,108,132,160,192,232,280,336,400,476,566,568,570,572,574,576},
+ {12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2},
+ {0, 24, 48, 72,108,156,216,288,372,480,486,492,498,576},
+ {8,8,8,12,16,20,24,28,36,2,2,2,26} } ,
+};
+
+static int mapbuf0[9][152];
+static int mapbuf1[9][156];
+static int mapbuf2[9][44];
+static int *map[9][3];
+static int *mapend[9][3];
+
+static unsigned int n_slen2[512]; /* MPEG 2.0 slen for 'normal' mode */
+static unsigned int i_slen2[256]; /* MPEG 2.0 slen for intensity stereo */
+
+static real tan1_1[16],tan2_1[16],tan1_2[16],tan2_2[16];
+static real pow1_1[2][16],pow2_1[2][16],pow1_2[2][16],pow2_2[2][16];
+
+/*
+ * init tables for layer-3
+ */
+void init_layer3(int down_sample_sblimit)
+{
+ int i,j,k,l;
+
+#ifdef REAL_IS_FIXED
+ double tmparray[8207];
+ double twotothequarter = pow((double)2.0, (double)0.25);
+ double current = pow((double)2.0, (double)(0.25 * 47));
+#endif
+
+ for(i=-256;i<118+4;i++) {
+#ifdef REAL_IS_FIXED
+ /* Possibly a few too many multiplies - single bit errors will
+ * propagate. It may change the gradient of the (log) power curve
+ * slighty */
+ current = current / twotothequarter;
+ gainpow2[i+256] = DOUBLE_TO_REAL(current);
+#else
+# ifdef USE_MMX
+ if(!param.down_sample)
+ gainpow2[i+256] = 16384.0 * pow((double)2.0,-0.25 * (double) (i+210) );
+ else
+# endif
+ gainpow2[i+256] = DOUBLE_TO_REAL(pow((double)2.0,-0.25 * (double) (i+210) ));
+#endif
+ }
+
+#ifdef REAL_IS_FIXED
+ for(i=0;i<8207;i++)
+ tmparray[i] = 0.0;
+ tmparray[1] = 1.0;
+ for(i=2;i<8207;i++) {
+ if(!tmparray[i]) {
+ tmparray[i] = pow((double)i,(double)(4.0/3.0));
+ for(j = 2; (j <= i) && ((i * j) < 8207); j++) {
+ /* Degradation due to lots of multiplies: A double has
+ * 52 bits of mantissa. A long has 32 bits (on the IPaq).
+ * Hence we can create 20 bits of error without fussing.
+ * Assuming that a 1 bit error multiplies to 2 bits, then 4,
+ * then 8, and noting that 2^13 is 8196 (we go up to 8207),
+ * we may have a problem. Resolve this by limiting to 4
+ * multiplies before recalculating. */
+ for(k = i, l = 0; (k * j) <= 8207 && (l < 4); k *= j, l++) {
+ tmparray[k * j] = tmparray[k] * tmparray[j];
+ }
+ }
+ }
+ ispow[i] = DOUBLE_TO_REAL(tmparray[i]);
+ }
+#else
+ for(i=0;i<8207;i++)
+ ispow[i] = DOUBLE_TO_REAL(pow((double)i,(double)4.0/3.0));
+#endif
+
+ for (i=0;i<8;i++) {
+ static double Ci[8]={-0.6,-0.535,-0.33,-0.185,-0.095,-0.041,-0.0142,-0.0037};
+ double sq=sqrt(1.0+Ci[i]*Ci[i]);
+ aa_cs[i] = DOUBLE_TO_REAL(1.0/sq);
+ aa_ca[i] = DOUBLE_TO_REAL(Ci[i]/sq);
+ }
+
+ for(i=0;i<18;i++) {
+ win[0][i] = win[1][i] =
+ DOUBLE_TO_REAL(0.5 * sin( M_PI / 72.0 * (double) (2*(i+0) +1) ) / cos ( M_PI * (double) (2*(i+0) +19) / 72.0 ));
+ win[0][i+18] = win[3][i+18] =
+ DOUBLE_TO_REAL(0.5 * sin( M_PI / 72.0 * (double) (2*(i+18)+1) ) / cos ( M_PI * (double) (2*(i+18)+19) / 72.0 ));
+ }
+ for(i=0;i<6;i++) {
+ win[1][i+18] = DOUBLE_TO_REAL(0.5 / cos ( M_PI * (double) (2*(i+18)+19) / 72.0 ));
+ win[3][i+12] = DOUBLE_TO_REAL(0.5 / cos ( M_PI * (double) (2*(i+12)+19) / 72.0 ));
+ win[1][i+24] = DOUBLE_TO_REAL(0.5 * sin( M_PI / 24.0 * (double) (2*i+13) ) / cos ( M_PI * (double) (2*(i+24)+19) / 72.0 ));
+ win[1][i+30] = win[3][i] = DOUBLE_TO_REAL(0.0);
+ win[3][i+6 ] = DOUBLE_TO_REAL(0.5 * sin( M_PI / 24.0 * (double) (2*i+1) ) / cos ( M_PI * (double) (2*(i+6 )+19) / 72.0 ));
+ }
+
+ for(i=0;i<9;i++)
+ COS9[i] = DOUBLE_TO_REAL(cos( M_PI / 18.0 * (double) i));
+
+ for(i=0;i<9;i++)
+ tfcos36[i] = DOUBLE_TO_REAL(0.5 / cos ( M_PI * (double) (i*2+1) / 36.0 ));
+ for(i=0;i<3;i++)
+ tfcos12[i] = DOUBLE_TO_REAL(0.5 / cos ( M_PI * (double) (i*2+1) / 12.0 ));
+
+ COS6_1 = DOUBLE_TO_REAL(cos( M_PI / 6.0 * (double) 1));
+ COS6_2 = DOUBLE_TO_REAL(cos( M_PI / 6.0 * (double) 2));
+
+#ifdef NEW_DCT9
+ cos9[0] = DOUBLE_TO_REAL(cos(1.0*M_PI/9.0));
+ cos9[1] = DOUBLE_TO_REAL(cos(5.0*M_PI/9.0));
+ cos9[2] = DOUBLE_TO_REAL(cos(7.0*M_PI/9.0));
+ cos18[0] = DOUBLE_TO_REAL(cos(1.0*M_PI/18.0));
+ cos18[1] = DOUBLE_TO_REAL(cos(11.0*M_PI/18.0));
+ cos18[2] = DOUBLE_TO_REAL(cos(13.0*M_PI/18.0));
+#endif
+
+ for(i=0;i<12;i++) {
+ win[2][i] = DOUBLE_TO_REAL(0.5 * sin( M_PI / 24.0 * (double) (2*i+1) ) / cos ( M_PI * (double) (2*i+7) / 24.0 ));
+ for(j=0;j<6;j++)
+ COS1[i][j] = DOUBLE_TO_REAL(cos( M_PI / 24.0 * (double) ((2*i+7)*(2*j+1)) ));
+ }
+
+ for(j=0;j<4;j++) {
+ static int len[4] = { 36,36,12,36 };
+ for(i=0;i<len[j];i+=2)
+ win1[j][i] = + win[j][i];
+ for(i=1;i<len[j];i+=2)
+ win1[j][i] = - win[j][i];
+ }
+
+ for(i=0;i<16;i++) {
+ double t = tan( (double) i * M_PI / 12.0 );
+ tan1_1[i] = DOUBLE_TO_REAL(t / (1.0+t));
+ tan2_1[i] = DOUBLE_TO_REAL(1.0 / (1.0 + t));
+ tan1_2[i] = DOUBLE_TO_REAL(M_SQRT2 * t / (1.0+t));
+ tan2_2[i] = DOUBLE_TO_REAL(M_SQRT2 / (1.0 + t));
+
+ for(j=0;j<2;j++) {
+ double base = pow(2.0,-0.25*(j+1.0));
+ double p1=1.0,p2=1.0;
+ if(i > 0) {
+ if( i & 1 )
+ p1 = pow(base,(i+1.0)*0.5);
+ else
+ p2 = pow(base,i*0.5);
+ }
+ pow1_1[j][i] = DOUBLE_TO_REAL(p1);
+ pow2_1[j][i] = DOUBLE_TO_REAL(p2);
+ pow1_2[j][i] = DOUBLE_TO_REAL(M_SQRT2 * p1);
+ pow2_2[j][i] = DOUBLE_TO_REAL(M_SQRT2 * p2);
+ }
+ }
+
+ for(j=0;j<9;j++) {
+ struct bandInfoStruct *bi = &bandInfo[j];
+ int *mp;
+ int cb,lwin;
+ int *bdf;
+
+ mp = map[j][0] = mapbuf0[j];
+ bdf = bi->longDiff;
+ for(i=0,cb = 0; cb < 8 ; cb++,i+=*bdf++) {
+ *mp++ = (*bdf) >> 1;
+ *mp++ = i;
+ *mp++ = 3;
+ *mp++ = cb;
+ }
+ bdf = bi->shortDiff+3;
+ for(cb=3;cb<13;cb++) {
+ int l = (*bdf++) >> 1;
+ for(lwin=0;lwin<3;lwin++) {
+ *mp++ = l;
+ *mp++ = i + lwin;
+ *mp++ = lwin;
+ *mp++ = cb;
+ }
+ i += 6*l;
+ }
+ mapend[j][0] = mp;
+
+ mp = map[j][1] = mapbuf1[j];
+ bdf = bi->shortDiff+0;
+ for(i=0,cb=0;cb<13;cb++) {
+ int l = (*bdf++) >> 1;
+ for(lwin=0;lwin<3;lwin++) {
+ *mp++ = l;
+ *mp++ = i + lwin;
+ *mp++ = lwin;
+ *mp++ = cb;
+ }
+ i += 6*l;
+ }
+ mapend[j][1] = mp;
+
+ mp = map[j][2] = mapbuf2[j];
+ bdf = bi->longDiff;
+ for(cb = 0; cb < 22 ; cb++) {
+ *mp++ = (*bdf++) >> 1;
+ *mp++ = cb;
+ }
+ mapend[j][2] = mp;
+
+ }
+
+ for(j=0;j<9;j++) {
+ for(i=0;i<23;i++) {
+ longLimit[j][i] = (bandInfo[j].longIdx[i] - 1 + 8) / 18 + 1;
+ if(longLimit[j][i] > (down_sample_sblimit) )
+ longLimit[j][i] = down_sample_sblimit;
+ }
+ for(i=0;i<14;i++) {
+ shortLimit[j][i] = (bandInfo[j].shortIdx[i] - 1) / 18 + 1;
+ if(shortLimit[j][i] > (down_sample_sblimit) )
+ shortLimit[j][i] = down_sample_sblimit;
+ }
+ }
+
+ for(i=0;i<5;i++) {
+ for(j=0;j<6;j++) {
+ for(k=0;k<6;k++) {
+ int n = k + j * 6 + i * 36;
+ i_slen2[n] = i|(j<<3)|(k<<6)|(3<<12);
+ }
+ }
+ }
+ for(i=0;i<4;i++) {
+ for(j=0;j<4;j++) {
+ for(k=0;k<4;k++) {
+ int n = k + j * 4 + i * 16;
+ i_slen2[n+180] = i|(j<<3)|(k<<6)|(4<<12);
+ }
+ }
+ }
+ for(i=0;i<4;i++) {
+ for(j=0;j<3;j++) {
+ int n = j + i * 3;
+ i_slen2[n+244] = i|(j<<3) | (5<<12);
+ n_slen2[n+500] = i|(j<<3) | (2<<12) | (1<<15);
+ }
+ }
+
+ for(i=0;i<5;i++) {
+ for(j=0;j<5;j++) {
+ for(k=0;k<4;k++) {
+ for(l=0;l<4;l++) {
+ int n = l + k * 4 + j * 16 + i * 80;
+ n_slen2[n] = i|(j<<3)|(k<<6)|(l<<9)|(0<<12);
+ }
+ }
+ }
+ }
+ for(i=0;i<5;i++) {
+ for(j=0;j<5;j++) {
+ for(k=0;k<4;k++) {
+ int n = k + j * 4 + i * 20;
+ n_slen2[n+400] = i|(j<<3)|(k<<6)|(1<<12);
+ }
+ }
+ }
+}
+
+/*
+ * read additional side information (for MPEG 1 and MPEG 2)
+ */
+static int III_get_side_info(struct III_sideinfo *si,int stereo,
+ int ms_stereo,long sfreq,int single,int lsf)
+{
+ int ch, gr;
+ int powdiff = (single == 3) ? 4 : 0;
+
+ static const int tabs[2][5] = { { 2,9,5,3,4 } , { 1,8,1,2,9 } };
+ const int *tab = tabs[lsf];
+
+ si->main_data_begin = getbits(&bsi,tab[1]);
+ if (stereo == 1)
+ si->private_bits = getbits_fast(&bsi,tab[2]);
+ else
+ si->private_bits = getbits_fast(&bsi,tab[3]);
+
+ if(!lsf) {
+ for (ch=0; ch<stereo; ch++) {
+ si->ch[ch].gr[0].scfsi = -1;
+ si->ch[ch].gr[1].scfsi = getbits_fast(&bsi,4);
+ }
+ }
+
+ for (gr=0; gr<tab[0]; gr++) {
+ for (ch=0; ch<stereo; ch++) {
+ register struct gr_info_s *gr_info = &(si->ch[ch].gr[gr]);
+
+ gr_info->part2_3_length = getbits(&bsi,12);
+ gr_info->big_values = getbits(&bsi,9);
+ if(gr_info->big_values > 288) {
+ if(param.verbose)
+ fprintf(stderr,"big_values too large!\n");
+ gr_info->big_values = 288;
+ }
+ gr_info->pow2gain = gainpow2+256 - getbits_fast(&bsi,8) + powdiff;
+ if(ms_stereo)
+ gr_info->pow2gain += 2;
+ gr_info->scalefac_compress = getbits(&bsi,tab[4]);
+
+ if(get1bit(&bsi)) { /* window switch flag */
+ int i;
+#ifdef L3_DEBUG
+if(2*gr_info->big_values > bandInfo[sfreq].shortIdx[12])
+ fprintf(stderr,"L3: BigValues too large, doesn't make sense %d %d\n",2*gr_info->big_values,bandInfo[sfreq].shortIdx[12]);
+#endif
+
+ gr_info->block_type = getbits_fast(&bsi,2);
+ gr_info->mixed_block_flag = get1bit(&bsi);
+ gr_info->table_select[0] = getbits_fast(&bsi,5);
+ gr_info->table_select[1] = getbits_fast(&bsi,5);
+ /*
+ * table_select[2] not needed, because there is no region2,
+ * but to satisfy some verifications tools we set it either.
+ */
+ gr_info->table_select[2] = 0;
+ for(i=0;i<3;i++)
+ gr_info->full_gain[i] = gr_info->pow2gain + (getbits_fast(&bsi,3)<<3);
+
+ if(gr_info->block_type == 0) {
+ if(param.verbose)
+ fprintf(stderr,"Blocktype == 0 and window-switching == 1 not allowed.\n");
+ return 0;
+ }
+
+ /* region_count/start parameters are implicit in this case. */
+ if(!lsf || gr_info->block_type == 2)
+ gr_info->region1start = 36>>1;
+ else {
+/* check this again for 2.5 and sfreq=8 */
+ if(sfreq == 8)
+ gr_info->region1start = 108>>1;
+ else
+ gr_info->region1start = 54>>1;
+ }
+ gr_info->region2start = 576>>1;
+ }
+ else {
+ int i,r0c,r1c;
+#ifdef L3_DEBUG
+if(2*gr_info->big_values > bandInfo[sfreq].longIdx[21])
+ fprintf(stderr,"L3: BigValues too large, doesn't make sense %d %d\n",2*gr_info->big_values,bandInfo[sfreq].longIdx[21]);
+#endif
+ for (i=0; i<3; i++)
+ gr_info->table_select[i] = getbits_fast(&bsi,5);
+ r0c = getbits_fast(&bsi,4);
+ r1c = getbits_fast(&bsi,3);
+ gr_info->region1start = bandInfo[sfreq].longIdx[r0c+1] >> 1 ;
+ if(r0c + r1c + 2 > 22)
+ gr_info->region2start = 576>>1;
+ else
+ gr_info->region2start = bandInfo[sfreq].longIdx[r0c+1+r1c+1] >> 1;
+ gr_info->block_type = 0;
+ gr_info->mixed_block_flag = 0;
+ }
+ if(!lsf)
+ gr_info->preflag = get1bit(&bsi);
+ gr_info->scalefac_scale = get1bit(&bsi);
+ gr_info->count1table_select = get1bit(&bsi);
+ }
+ }
+
+ return !0;
+}
+
+/*
+ * read scalefactors
+ */
+static int III_get_scale_factors_1(int *scf,struct gr_info_s *gr_info)
+{
+ static const unsigned char slen[2][16] = {
+ {0, 0, 0, 0, 3, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4},
+ {0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 3}
+ };
+ int numbits;
+ int num0 = slen[0][gr_info->scalefac_compress];
+ int num1 = slen[1][gr_info->scalefac_compress];
+
+ if (gr_info->block_type == 2) {
+ int i=18;
+ numbits = (num0 + num1) * 18;
+
+ if (gr_info->mixed_block_flag) {
+ for (i=8;i;i--)
+ *scf++ = getbits_fast(&bsi,num0);
+ i = 9;
+ numbits -= num0; /* num0 * 17 + num1 * 18 */
+ }
+
+ for (;i;i--)
+ *scf++ = getbits_fast(&bsi,num0);
+ for (i = 18; i; i--)
+ *scf++ = getbits_fast(&bsi,num1);
+ *scf++ = 0; *scf++ = 0; *scf++ = 0; /* short[13][0..2] = 0 */
+ }
+ else {
+ int i;
+ int scfsi = gr_info->scfsi;
+
+ if(scfsi < 0) { /* scfsi < 0 => granule == 0 */
+ for(i=11;i;i--)
+ *scf++ = getbits_fast(&bsi,num0);
+ for(i=10;i;i--)
+ *scf++ = getbits_fast(&bsi,num1);
+ numbits = (num0 + num1) * 10 + num0;
+ *scf++ = 0;
+ }
+ else {
+ numbits = 0;
+ if(!(scfsi & 0x8)) {
+ for (i=0;i<6;i++)
+ *scf++ = getbits_fast(&bsi,num0);
+ numbits += num0 * 6;
+ }
+ else {
+ scf += 6;
+ }
+
+ if(!(scfsi & 0x4)) {
+ for (i=0;i<5;i++)
+ *scf++ = getbits_fast(&bsi,num0);
+ numbits += num0 * 5;
+ }
+ else {
+ scf += 5;
+ }
+
+ if(!(scfsi & 0x2)) {
+ for(i=0;i<5;i++)
+ *scf++ = getbits_fast(&bsi,num1);
+ numbits += num1 * 5;
+ }
+ else {
+ scf += 5;
+ }
+
+ if(!(scfsi & 0x1)) {
+ for (i=0;i<5;i++)
+ *scf++ = getbits_fast(&bsi,num1);
+ numbits += num1 * 5;
+ }
+ else {
+ scf += 5;
+ }
+ *scf++ = 0; /* no l[21] in original sources */
+ }
+ }
+ return numbits;
+}
+
+static int III_get_scale_factors_2(int *scf,struct gr_info_s *gr_info,int i_stereo)
+{
+ unsigned char *pnt;
+ int i,j,n=0,numbits=0;
+ unsigned int slen;
+
+ static unsigned char stab[3][6][4] = {
+ { { 6, 5, 5,5 } , { 6, 5, 7,3 } , { 11,10,0,0} ,
+ { 7, 7, 7,0 } , { 6, 6, 6,3 } , { 8, 8,5,0} } ,
+ { { 9, 9, 9,9 } , { 9, 9,12,6 } , { 18,18,0,0} ,
+ {12,12,12,0 } , {12, 9, 9,6 } , { 15,12,9,0} } ,
+ { { 6, 9, 9,9 } , { 6, 9,12,6 } , { 15,18,0,0} ,
+ { 6,15,12,0 } , { 6,12, 9,6 } , { 6,18,9,0} } };
+
+ if(i_stereo) /* i_stereo AND second channel -> do_layer3() checks this */
+ slen = i_slen2[gr_info->scalefac_compress>>1];
+ else
+ slen = n_slen2[gr_info->scalefac_compress];
+
+ gr_info->preflag = (slen>>15) & 0x1;
+
+ n = 0;
+ if( gr_info->block_type == 2 ) {
+ n++;
+ if(gr_info->mixed_block_flag)
+ n++;
+ }
+
+ pnt = stab[n][(slen>>12)&0x7];
+
+ for(i=0;i<4;i++) {
+ int num = slen & 0x7;
+ slen >>= 3;
+ if(num) {
+ for(j=0;j<(int)(pnt[i]);j++)
+ *scf++ = getbits_fast(&bsi,num);
+ numbits += pnt[i] * num;
+ }
+ else {
+ for(j=0;j<(int)(pnt[i]);j++)
+ *scf++ = 0;
+ }
+ }
+
+ n = (n << 1) + 1;
+ for(i=0;i<n;i++)
+ *scf++ = 0;
+
+ return numbits;
+}
+
+static int pretab1[22] = {0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,2,2,3,3,3,2,0};
+static int pretab2[22] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+/*
+ * Dequantize samples (includes huffman decoding)
+ */
+/* 24 is enough because tab13 has max. a 19 bit huffvector */
+#define BITSHIFT ((sizeof(long)-1)*8)
+#define REFRESH_MASK \
+ while(num < BITSHIFT) { \
+ mask |= ((unsigned long)getbyte(&bsi))<<(BITSHIFT-num); \
+ num += 8; \
+ part2remain -= 8; }
+
+static int III_dequantize_sample(real xr[SBLIMIT][SSLIMIT],int *scf,
+ struct gr_info_s *gr_info,int sfreq,int part2bits)
+{
+ int shift = 1 + gr_info->scalefac_scale;
+ real *xrpnt = (real *) xr;
+ int l[3],l3;
+ int part2remain = gr_info->part2_3_length - part2bits;
+ int *me;
+
+ int num=getbitoffset(&bsi);
+ long mask;
+ /* we must split this, because for num==0 the shift is undefined if you do it in one step */
+ mask = ((unsigned long) getbits(&bsi,num))<<BITSHIFT;
+ mask <<= 8-num;
+ part2remain -= num;
+
+ {
+ int bv = gr_info->big_values;
+ int region1 = gr_info->region1start;
+ int region2 = gr_info->region2start;
+
+ l3 = ((576>>1)-bv)>>1;
+/*
+ * we may lose the 'odd' bit here !!
+ * check this later again
+ */
+ if(bv <= region1) {
+ l[0] = bv; l[1] = l[2] = 0;
+ }
+ else {
+ l[0] = region1;
+ if(bv <= region2) {
+ l[1] = bv - l[0]; l[2] = 0;
+ }
+ else {
+ l[1] = region2 - l[0]; l[2] = bv - region2;
+ }
+ }
+ }
+
+ if(gr_info->block_type == 2) {
+ /*
+ * decoding with short or mixed mode BandIndex table
+ */
+ int i,max[4];
+ int step=0,lwin=3,cb=0;
+ register real v = 0.0;
+ register int *m,mc;
+
+ if(gr_info->mixed_block_flag) {
+ max[3] = -1;
+ max[0] = max[1] = max[2] = 2;
+ m = map[sfreq][0];
+ me = mapend[sfreq][0];
+ }
+ else {
+ max[0] = max[1] = max[2] = max[3] = -1;
+ /* max[3] not really needed in this case */
+ m = map[sfreq][1];
+ me = mapend[sfreq][1];
+ }
+
+ mc = 0;
+ for(i=0;i<2;i++) {
+ int lp = l[i];
+ struct newhuff *h = ht+gr_info->table_select[i];
+ for(;lp;lp--,mc--) {
+ register int x,y;
+ if( (!mc) ) {
+ mc = *m++;
+ xrpnt = ((real *) xr) + (*m++);
+ lwin = *m++;
+ cb = *m++;
+ if(lwin == 3) {
+ v = gr_info->pow2gain[(*scf++) << shift];
+ step = 1;
+ }
+ else {
+ v = gr_info->full_gain[lwin][(*scf++) << shift];
+ step = 3;
+ }
+ }
+ {
+ register short *val = h->table;
+ REFRESH_MASK;
+ while((y=*val++)<0) {
+ if (mask < 0)
+ val -= y;
+ num--;
+ mask <<= 1;
+ }
+ x = y >> 4;
+ y &= 0xf;
+ }
+ if(x == 15 && h->linbits) {
+ max[lwin] = cb;
+ REFRESH_MASK;
+ x += ((unsigned long) mask) >> (BITSHIFT+8-h->linbits);
+ num -= h->linbits+1;
+ mask <<= h->linbits;
+ if(mask < 0)
+ *xrpnt = REAL_MUL(-ispow[x], v);
+ else
+ *xrpnt = REAL_MUL(ispow[x], v);
+ mask <<= 1;
+ }
+ else if(x) {
+ max[lwin] = cb;
+ if(mask < 0)
+ *xrpnt = REAL_MUL(-ispow[x], v);
+ else
+ *xrpnt = REAL_MUL(ispow[x], v);
+ num--;
+ mask <<= 1;
+ }
+ else
+ *xrpnt = DOUBLE_TO_REAL(0.0);
+ xrpnt += step;
+ if(y == 15 && h->linbits) {
+ max[lwin] = cb;
+ REFRESH_MASK;
+ y += ((unsigned long) mask) >> (BITSHIFT+8-h->linbits);
+ num -= h->linbits+1;
+ mask <<= h->linbits;
+ if(mask < 0)
+ *xrpnt = REAL_MUL(-ispow[y], v);
+ else
+ *xrpnt = REAL_MUL(ispow[y], v);
+ mask <<= 1;
+ }
+ else if(y) {
+ max[lwin] = cb;
+ if(mask < 0)
+ *xrpnt = REAL_MUL(-ispow[y], v);
+ else
+ *xrpnt = REAL_MUL(ispow[y], v);
+ num--;
+ mask <<= 1;
+ }
+ else
+ *xrpnt = DOUBLE_TO_REAL(0.0);
+ xrpnt += step;
+ }
+ }
+
+ for(;l3 && (part2remain+num > 0);l3--) {
+ struct newhuff *h = htc+gr_info->count1table_select;
+ register short *val = h->table,a;
+
+ REFRESH_MASK;
+ while((a=*val++)<0) {
+ if (mask < 0)
+ val -= a;
+ num--;
+ mask <<= 1;
+ }
+ if(part2remain+num <= 0) {
+ num -= part2remain+num;
+ break;
+ }
+
+ for(i=0;i<4;i++) {
+ if(!(i & 1)) {
+ if(!mc) {
+ mc = *m++;
+ xrpnt = ((real *) xr) + (*m++);
+ lwin = *m++;
+ cb = *m++;
+ if(lwin == 3) {
+ v = gr_info->pow2gain[(*scf++) << shift];
+ step = 1;
+ }
+ else {
+ v = gr_info->full_gain[lwin][(*scf++) << shift];
+ step = 3;
+ }
+ }
+ mc--;
+ }
+ if( (a & (0x8>>i)) ) {
+ max[lwin] = cb;
+ if(part2remain+num <= 0) {
+ break;
+ }
+ if(mask < 0)
+ *xrpnt = -v;
+ else
+ *xrpnt = v;
+ num--;
+ mask <<= 1;
+ }
+ else
+ *xrpnt = DOUBLE_TO_REAL(0.0);
+ xrpnt += step;
+ }
+ }
+
+ if(lwin < 3) { /* short band? */
+ while(1) {
+ for(;mc > 0;mc--) {
+ *xrpnt = DOUBLE_TO_REAL(0.0); xrpnt += 3; /* short band -> step=3 */
+ *xrpnt = DOUBLE_TO_REAL(0.0); xrpnt += 3;
+ }
+ if(m >= me)
+ break;
+ mc = *m++;
+ xrpnt = ((real *) xr) + *m++;
+ if(*m++ == 0)
+ break; /* optimize: field will be set to zero at the end of the function */
+ m++; /* cb */
+ }
+ }
+
+ gr_info->maxband[0] = max[0]+1;
+ gr_info->maxband[1] = max[1]+1;
+ gr_info->maxband[2] = max[2]+1;
+ gr_info->maxbandl = max[3]+1;
+
+ {
+ int rmax = max[0] > max[1] ? max[0] : max[1];
+ rmax = (rmax > max[2] ? rmax : max[2]) + 1;
+ gr_info->maxb = rmax ? shortLimit[sfreq][rmax] : longLimit[sfreq][max[3]+1];
+ }
+
+ }
+ else {
+ /*
+ * decoding with 'long' BandIndex table (block_type != 2)
+ */
+ int *pretab = gr_info->preflag ? pretab1 : pretab2;
+ int i,max = -1;
+ int cb = 0;
+ int *m = map[sfreq][2];
+ register real v = 0.0;
+ int mc = 0;
+
+ /*
+ * long hash table values
+ */
+ for(i=0;i<3;i++) {
+ int lp = l[i];
+ struct newhuff *h = ht+gr_info->table_select[i];
+
+ for(;lp;lp--,mc--) {
+ int x,y;
+
+ if(!mc) {
+ mc = *m++;
+ cb = *m++;
+#ifdef CUT_HF
+ if(cb == 21) {
+ fprintf(stderr,"c");
+ v = 0.0;
+ }
+ else
+#endif
+ v = gr_info->pow2gain[((*scf++) + (*pretab++)) << shift];
+
+ }
+ {
+ register short *val = h->table;
+ REFRESH_MASK;
+ while((y=*val++)<0) {
+ if (mask < 0)
+ val -= y;
+ num--;
+ mask <<= 1;
+ }
+ x = y >> 4;
+ y &= 0xf;
+ }
+
+ if (x == 15 && h->linbits) {
+ max = cb;
+ REFRESH_MASK;
+ x += ((unsigned long) mask) >> (BITSHIFT+8-h->linbits);
+ num -= h->linbits+1;
+ mask <<= h->linbits;
+ if(mask < 0)
+ *xrpnt++ = REAL_MUL(-ispow[x], v);
+ else
+ *xrpnt++ = REAL_MUL(ispow[x], v);
+ mask <<= 1;
+ }
+ else if(x) {
+ max = cb;
+ if(mask < 0)
+ *xrpnt++ = REAL_MUL(-ispow[x], v);
+ else
+ *xrpnt++ = REAL_MUL(ispow[x], v);
+ num--;
+ mask <<= 1;
+ }
+ else
+ *xrpnt++ = DOUBLE_TO_REAL(0.0);
+
+ if (y == 15 && h->linbits) {
+ max = cb;
+ REFRESH_MASK;
+ y += ((unsigned long) mask) >> (BITSHIFT+8-h->linbits);
+ num -= h->linbits+1;
+ mask <<= h->linbits;
+ if(mask < 0)
+ *xrpnt++ = REAL_MUL(-ispow[y], v);
+ else
+ *xrpnt++ = REAL_MUL(ispow[y], v);
+ mask <<= 1;
+ }
+ else if(y) {
+ max = cb;
+ if(mask < 0)
+ *xrpnt++ = REAL_MUL(-ispow[y], v);
+ else
+ *xrpnt++ = REAL_MUL(ispow[y], v);
+ num--;
+ mask <<= 1;
+ }
+ else
+ *xrpnt++ = DOUBLE_TO_REAL(0.0);
+ }
+ }
+
+ /*
+ * short (count1table) values
+ */
+ for(;l3 && (part2remain+num > 0);l3--) {
+ struct newhuff *h = htc+gr_info->count1table_select;
+ register short *val = h->table,a;
+
+ REFRESH_MASK;
+ while((a=*val++)<0) {
+ if (mask < 0)
+ val -= a;
+ num--;
+ mask <<= 1;
+ }
+ if(part2remain+num <= 0) {
+ num -= part2remain+num;
+ break;
+ }
+
+ for(i=0;i<4;i++) {
+ if(!(i & 1)) {
+ if(!mc) {
+ mc = *m++;
+ cb = *m++;
+#ifdef CUT_HF
+ if(cb == 21) {
+ fprintf(stderr,"c");
+ v = 0.0;
+ }
+ else
+#endif
+ v = gr_info->pow2gain[((*scf++) + (*pretab++)) << shift];
+ }
+ mc--;
+ }
+ if ( (a & (0x8>>i)) ) {
+ max = cb;
+ if(part2remain+num <= 0) {
+ break;
+ }
+ if(mask < 0)
+ *xrpnt++ = -v;
+ else
+ *xrpnt++ = v;
+ num--;
+ mask <<= 1;
+ }
+ else
+ *xrpnt++ = DOUBLE_TO_REAL(0.0);
+ }
+ }
+
+ gr_info->maxbandl = max+1;
+ gr_info->maxb = longLimit[sfreq][gr_info->maxbandl];
+ }
+
+ part2remain += num;
+ backbits(&bsi,num);
+ num = 0;
+
+ while(xrpnt < &xr[SBLIMIT][0])
+ *xrpnt++ = DOUBLE_TO_REAL(0.0);
+
+ while( part2remain > 16 ) {
+ getbits(&bsi,16); /* Dismiss stuffing Bits */
+ part2remain -= 16;
+ }
+ if(part2remain > 0)
+ getbits(&bsi,part2remain);
+ else if(part2remain < 0) {
+ if(param.verbose)
+ fprintf(stderr,"mpg123: Can't rewind stream by %d bits!\n",-part2remain);
+ return 1; /* -> error */
+ }
+ return 0;
+}
+
+/*
+ * III_stereo: calculate real channel values for Joint-I-Stereo-mode
+ */
+static void III_i_stereo(real xr_buf[2][SBLIMIT][SSLIMIT],int *scalefac,
+ struct gr_info_s *gr_info,int sfreq,int ms_stereo,int lsf)
+{
+ real (*xr)[SBLIMIT*SSLIMIT] = (real (*)[SBLIMIT*SSLIMIT] ) xr_buf;
+ struct bandInfoStruct *bi = &bandInfo[sfreq];
+
+ const real *tab1,*tab2;
+
+ int tab;
+ static const real *tabs[3][2][2] = {
+ { { tan1_1,tan2_1 } , { tan1_2,tan2_2 } },
+ { { pow1_1[0],pow2_1[0] } , { pow1_2[0],pow2_2[0] } } ,
+ { { pow1_1[1],pow2_1[1] } , { pow1_2[1],pow2_2[1] } }
+ };
+
+ tab = lsf + (gr_info->scalefac_compress & lsf);
+ tab1 = tabs[tab][ms_stereo][0];
+ tab2 = tabs[tab][ms_stereo][1];
+#if 0
+ if(lsf) {
+ int p = gr_info->scalefac_compress & 0x1;
+ if(ms_stereo) {
+ tab1 = pow1_2[p]; tab2 = pow2_2[p];
+ }
+ else {
+ tab1 = pow1_1[p]; tab2 = pow2_1[p];
+ }
+ }
+ else {
+ if(ms_stereo) {
+ tab1 = tan1_2; tab2 = tan2_2;
+ }
+ else {
+ tab1 = tan1_1; tab2 = tan2_1;
+ }
+ }
+#endif
+
+ if (gr_info->block_type == 2) {
+ int lwin,do_l = 0;
+ if( gr_info->mixed_block_flag )
+ do_l = 1;
+
+ for (lwin=0;lwin<3;lwin++) { /* process each window */
+ /* get first band with zero values */
+ int is_p,sb,idx,sfb = gr_info->maxband[lwin]; /* sfb is minimal 3 for mixed mode */
+ if(sfb > 3)
+ do_l = 0;
+
+ for(;sfb<12;sfb++) {
+ is_p = scalefac[sfb*3+lwin-gr_info->mixed_block_flag]; /* scale: 0-15 */
+ if(is_p != 7) {
+ real t1,t2;
+ sb = bi->shortDiff[sfb];
+ idx = bi->shortIdx[sfb] + lwin;
+ t1 = tab1[is_p]; t2 = tab2[is_p];
+ for (; sb > 0; sb--,idx+=3) {
+ real v = xr[0][idx];
+ xr[0][idx] = REAL_MUL(v, t1);
+ xr[1][idx] = REAL_MUL(v, t2);
+ }
+ }
+ }
+
+#if 1
+/* in the original: copy 10 to 11 , here: copy 11 to 12
+maybe still wrong??? (copy 12 to 13?) */
+ is_p = scalefac[11*3+lwin-gr_info->mixed_block_flag]; /* scale: 0-15 */
+ sb = bi->shortDiff[12];
+ idx = bi->shortIdx[12] + lwin;
+#else
+ is_p = scalefac[10*3+lwin-gr_info->mixed_block_flag]; /* scale: 0-15 */
+ sb = bi->shortDiff[11];
+ idx = bi->shortIdx[11] + lwin;
+#endif
+ if(is_p != 7) {
+ real t1,t2;
+ t1 = tab1[is_p]; t2 = tab2[is_p];
+ for ( ; sb > 0; sb--,idx+=3 ) {
+ real v = xr[0][idx];
+ xr[0][idx] = REAL_MUL(v, t1);
+ xr[1][idx] = REAL_MUL(v, t2);
+ }
+ }
+ } /* end for(lwin; .. ; . ) */
+
+/* also check l-part, if ALL bands in the three windows are 'empty'
+ * and mode = mixed_mode
+ */
+ if (do_l) {
+ int sfb = gr_info->maxbandl;
+ int idx = bi->longIdx[sfb];
+
+ for ( ; sfb<8; sfb++ ) {
+ int sb = bi->longDiff[sfb];
+ int is_p = scalefac[sfb]; /* scale: 0-15 */
+ if(is_p != 7) {
+ real t1,t2;
+ t1 = tab1[is_p]; t2 = tab2[is_p];
+ for ( ; sb > 0; sb--,idx++) {
+ real v = xr[0][idx];
+ xr[0][idx] = REAL_MUL(v, t1);
+ xr[1][idx] = REAL_MUL(v, t2);
+ }
+ }
+ else
+ idx += sb;
+ }
+ }
+ }
+ else { /* ((gr_info->block_type != 2)) */
+ int sfb = gr_info->maxbandl;
+ int is_p,idx = bi->longIdx[sfb];
+
+/* hmm ... maybe the maxbandl stuff for i-stereo is buggy? */
+ if(sfb <= 21) {
+ for ( ; sfb<21; sfb++) {
+ int sb = bi->longDiff[sfb];
+ is_p = scalefac[sfb]; /* scale: 0-15 */
+ if(is_p != 7) {
+ real t1,t2;
+ t1 = tab1[is_p]; t2 = tab2[is_p];
+ for ( ; sb > 0; sb--,idx++) {
+ real v = xr[0][idx];
+ xr[0][idx] = REAL_MUL(v, t1);
+ xr[1][idx] = REAL_MUL(v, t2);
+ }
+ }
+ else
+ idx += sb;
+ }
+
+ is_p = scalefac[20];
+ if(is_p != 7) { /* copy l-band 20 to l-band 21 */
+ int sb;
+ real t1 = tab1[is_p],t2 = tab2[is_p];
+
+ for ( sb = bi->longDiff[21]; sb > 0; sb--,idx++ ) {
+ real v = xr[0][idx];
+ xr[0][idx] = REAL_MUL(v, t1);
+ xr[1][idx] = REAL_MUL(v, t2);
+ }
+ }
+ } /* end: if(sfb <= 21) */
+ } /* ... */
+}
+
+static void III_antialias(real xr[SBLIMIT][SSLIMIT],struct gr_info_s *gr_info) {
+ int sblim;
+
+ if(gr_info->block_type == 2) {
+ if(!gr_info->mixed_block_flag)
+ return;
+ sblim = 1;
+ }
+ else {
+ sblim = gr_info->maxb-1;
+ }
+
+ /* 31 alias-reduction operations between each pair of sub-bands */
+ /* with 8 butterflies between each pair */
+
+ {
+ int sb;
+ real *xr1=(real *) xr[1];
+
+ for(sb=sblim;sb;sb--,xr1+=10) {
+ int ss;
+ real *cs=aa_cs,*ca=aa_ca;
+ real *xr2 = xr1;
+
+ for(ss=7;ss>=0;ss--)
+ { /* upper and lower butterfly inputs */
+ register real bu = *--xr2,bd = *xr1;
+ *xr2 = REAL_MUL(bu, *cs) - REAL_MUL(bd, *ca);
+ *xr1++ = REAL_MUL(bd, *cs++) + REAL_MUL(bu, *ca++);
+ }
+ }
+ }
+}
+
+/*
+ This is an optimized DCT from Jeff Tsay's maplay 1.2+ package.
+ Saved one multiplication by doing the 'twiddle factor' stuff
+ together with the window mul. (MH)
+
+ This uses Byeong Gi Lee's Fast Cosine Transform algorithm, but the
+ 9 point IDCT needs to be reduced further. Unfortunately, I don't
+ know how to do that, because 9 is not an even number. - Jeff.
+
+ ****************************************************************
+
+ 9 Point Inverse Discrete Cosine Transform
+
+ This piece of code is Copyright 1997 Mikko Tommila and is freely usable
+ by anybody. The algorithm itself is of course in the public domain.
+
+ Again derived heuristically from the 9-point WFTA.
+
+ The algorithm is optimized (?) for speed, not for small rounding errors or
+ good readability.
+
+ 36 additions, 11 multiplications
+
+ Again this is very likely sub-optimal.
+
+ The code is optimized to use a minimum number of temporary variables,
+ so it should compile quite well even on 8-register Intel x86 processors.
+ This makes the code quite obfuscated and very difficult to understand.
+
+ References:
+ [1] S. Winograd: "On Computing the Discrete Fourier Transform",
+ Mathematics of Computation, Volume 32, Number 141, January 1978,
+ Pages 175-199
+*/
+
+/*------------------------------------------------------------------*/
+/* */
+/* Function: Calculation of the inverse MDCT */
+/* */
+/*------------------------------------------------------------------*/
+
+void dct36(real *inbuf,real *o1,real *o2,real *wintab,real *tsbuf)
+{
+#ifdef NEW_DCT9
+ real tmp[18];
+#endif
+
+ {
+ register real *in = inbuf;
+
+ in[17]+=in[16]; in[16]+=in[15]; in[15]+=in[14];
+ in[14]+=in[13]; in[13]+=in[12]; in[12]+=in[11];
+ in[11]+=in[10]; in[10]+=in[9]; in[9] +=in[8];
+ in[8] +=in[7]; in[7] +=in[6]; in[6] +=in[5];
+ in[5] +=in[4]; in[4] +=in[3]; in[3] +=in[2];
+ in[2] +=in[1]; in[1] +=in[0];
+
+ in[17]+=in[15]; in[15]+=in[13]; in[13]+=in[11]; in[11]+=in[9];
+ in[9] +=in[7]; in[7] +=in[5]; in[5] +=in[3]; in[3] +=in[1];
+
+
+#ifdef NEW_DCT9
+#if 1
+ {
+ real t3;
+ {
+ real t0, t1, t2;
+
+ t0 = REAL_MUL(COS6_2, (in[8] + in[16] - in[4]));
+ t1 = REAL_MUL(COS6_2, in[12]);
+
+ t3 = in[0];
+ t2 = t3 - t1 - t1;
+ tmp[1] = tmp[7] = t2 - t0;
+ tmp[4] = t2 + t0 + t0;
+ t3 += t1;
+
+ t2 = REAL_MUL(COS6_1, (in[10] + in[14] - in[2]));
+ tmp[1] -= t2;
+ tmp[7] += t2;
+ }
+ {
+ real t0, t1, t2;
+
+ t0 = REAL_MUL(cos9[0], (in[4] + in[8] ));
+ t1 = REAL_MUL(cos9[1], (in[8] - in[16]));
+ t2 = REAL_MUL(cos9[2], (in[4] + in[16]));
+
+ tmp[2] = tmp[6] = t3 - t0 - t2;
+ tmp[0] = tmp[8] = t3 + t0 + t1;
+ tmp[3] = tmp[5] = t3 - t1 + t2;
+ }
+ }
+ {
+ real t1, t2, t3;
+
+ t1 = REAL_MUL(cos18[0], (in[2] + in[10]));
+ t2 = REAL_MUL(cos18[1], (in[10] - in[14]));
+ t3 = REAL_MUL(COS6_1, in[6]);
+
+ {
+ real t0 = t1 + t2 + t3;
+ tmp[0] += t0;
+ tmp[8] -= t0;
+ }
+
+ t2 -= t3;
+ t1 -= t3;
+
+ t3 = REAL_MUL(cos18[2], (in[2] + in[14]));
+
+ t1 += t3;
+ tmp[3] += t1;
+ tmp[5] -= t1;
+
+ t2 -= t3;
+ tmp[2] += t2;
+ tmp[6] -= t2;
+ }
+
+#else
+ {
+ real t0, t1, t2, t3, t4, t5, t6, t7;
+
+ t1 = REAL_MUL(COS6_2, in[12]);
+ t2 = REAL_MUL(COS6_2, (in[8] + in[16] - in[4]));
+
+ t3 = in[0] + t1;
+ t4 = in[0] - t1 - t1;
+ t5 = t4 - t2;
+ tmp[4] = t4 + t2 + t2;
+
+ t0 = REAL_MUL(cos9[0], (in[4] + in[8]));
+ t1 = REAL_MUL(cos9[1], (in[8] - in[16]));
+
+ t2 = REAL_MUL(cos9[2], (in[4] + in[16]));
+
+ t6 = t3 - t0 - t2;
+ t0 += t3 + t1;
+ t3 += t2 - t1;
+
+ t2 = REAL_MUL(cos18[0], (in[2] + in[10]));
+ t4 = REAL_MUL(cos18[1], (in[10] - in[14]));
+ t7 = REAL_MUL(COS6_1, in[6]);
+
+ t1 = t2 + t4 + t7;
+ tmp[0] = t0 + t1;
+ tmp[8] = t0 - t1;
+ t1 = REAL_MUL(cos18[2], (in[2] + in[14]));
+ t2 += t1 - t7;
+
+ tmp[3] = t3 + t2;
+ t0 = REAL_MUL(COS6_1, (in[10] + in[14] - in[2]));
+ tmp[5] = t3 - t2;
+
+ t4 -= t1 + t7;
+
+ tmp[1] = t5 - t0;
+ tmp[7] = t5 + t0;
+ tmp[2] = t6 + t4;
+ tmp[6] = t6 - t4;
+ }
+#endif
+
+ {
+ real t0, t1, t2, t3, t4, t5, t6, t7;
+
+ t1 = REAL_MUL(COS6_2, in[13]);
+ t2 = REAL_MUL(COS6_2, (in[9] + in[17] - in[5]));
+
+ t3 = in[1] + t1;
+ t4 = in[1] - t1 - t1;
+ t5 = t4 - t2;
+
+ t0 = REAL_MUL(cos9[0], (in[5] + in[9]));
+ t1 = REAL_MUL(cos9[1], (in[9] - in[17]));
+
+ tmp[13] = REAL_MUL((t4 + t2 + t2), tfcos36[17-13]);
+ t2 = REAL_MUL(cos9[2], (in[5] + in[17]));
+
+ t6 = t3 - t0 - t2;
+ t0 += t3 + t1;
+ t3 += t2 - t1;
+
+ t2 = REAL_MUL(cos18[0], (in[3] + in[11]));
+ t4 = REAL_MUL(cos18[1], (in[11] - in[15]));
+ t7 = REAL_MUL(COS6_1, in[7]);
+
+ t1 = t2 + t4 + t7;
+ tmp[17] = REAL_MUL((t0 + t1), tfcos36[17-17]);
+ tmp[9] = REAL_MUL((t0 - t1), tfcos36[17-9]);
+ t1 = REAL_MUL(cos18[2], (in[3] + in[15]));
+ t2 += t1 - t7;
+
+ tmp[14] = REAL_MUL((t3 + t2), tfcos36[17-14]);
+ t0 = REAL_MUL(COS6_1, (in[11] + in[15] - in[3]));
+ tmp[12] = REAL_MUL((t3 - t2), tfcos36[17-12]);
+
+ t4 -= t1 + t7;
+
+ tmp[16] = REAL_MUL((t5 - t0), tfcos36[17-16]);
+ tmp[10] = REAL_MUL((t5 + t0), tfcos36[17-10]);
+ tmp[15] = REAL_MUL((t6 + t4), tfcos36[17-15]);
+ tmp[11] = REAL_MUL((t6 - t4), tfcos36[17-11]);
+ }
+
+#define MACRO(v) { \
+ real tmpval; \
+ tmpval = tmp[(v)] + tmp[17-(v)]; \
+ out2[9+(v)] = REAL_MUL(tmpval, w[27+(v)]); \
+ out2[8-(v)] = REAL_MUL(tmpval, w[26-(v)]); \
+ tmpval = tmp[(v)] - tmp[17-(v)]; \
+ ts[SBLIMIT*(8-(v))] = out1[8-(v)] + REAL_MUL(tmpval, w[8-(v)]); \
+ ts[SBLIMIT*(9+(v))] = out1[9+(v)] + REAL_MUL(tmpval, w[9+(v)]); }
+
+{
+ register real *out2 = o2;
+ register real *w = wintab;
+ register real *out1 = o1;
+ register real *ts = tsbuf;
+
+ MACRO(0);
+ MACRO(1);
+ MACRO(2);
+ MACRO(3);
+ MACRO(4);
+ MACRO(5);
+ MACRO(6);
+ MACRO(7);
+ MACRO(8);
+}
+
+#else
+
+ {
+
+#define MACRO0(v) { \
+ real tmp; \
+ out2[9+(v)] = REAL_MUL((tmp = sum0 + sum1), w[27+(v)]); \
+ out2[8-(v)] = REAL_MUL(tmp, w[26-(v)]); } \
+ sum0 -= sum1; \
+ ts[SBLIMIT*(8-(v))] = out1[8-(v)] + REAL_MUL(sum0, w[8-(v)]); \
+ ts[SBLIMIT*(9+(v))] = out1[9+(v)] + REAL_MUL(sum0, w[9+(v)]);
+#define MACRO1(v) { \
+ real sum0,sum1; \
+ sum0 = tmp1a + tmp2a; \
+ sum1 = REAL_MUL((tmp1b + tmp2b), tfcos36[(v)]); \
+ MACRO0(v); }
+#define MACRO2(v) { \
+ real sum0,sum1; \
+ sum0 = tmp2a - tmp1a; \
+ sum1 = REAL_MUL((tmp2b - tmp1b), tfcos36[(v)]); \
+ MACRO0(v); }
+
+ register const real *c = COS9;
+ register real *out2 = o2;
+ register real *w = wintab;
+ register real *out1 = o1;
+ register real *ts = tsbuf;
+
+ real ta33,ta66,tb33,tb66;
+
+ ta33 = REAL_MUL(in[2*3+0], c[3]);
+ ta66 = REAL_MUL(in[2*6+0], c[6]);
+ tb33 = REAL_MUL(in[2*3+1], c[3]);
+ tb66 = REAL_MUL(in[2*6+1], c[6]);
+
+ {
+ real tmp1a,tmp2a,tmp1b,tmp2b;
+ tmp1a = REAL_MUL(in[2*1+0], c[1]) + ta33 + REAL_MUL(in[2*5+0], c[5]) + REAL_MUL(in[2*7+0], c[7]);
+ tmp1b = REAL_MUL(in[2*1+1], c[1]) + tb33 + REAL_MUL(in[2*5+1], c[5]) + REAL_MUL(in[2*7+1], c[7]);
+ tmp2a = REAL_MUL(in[2*2+0], c[2]) + REAL_MUL(in[2*4+0], c[4]) + ta66 + REAL_MUL(in[2*8+0], c[8]);
+ tmp2b = REAL_MUL(in[2*2+1], c[2]) + REAL_MUL(in[2*4+1], c[4]) + tb66 + REAL_MUL(in[2*8+1], c[8]);
+
+ MACRO1(0);
+ MACRO2(8);
+ }
+
+ {
+ real tmp1a,tmp2a,tmp1b,tmp2b;
+ tmp1a = REAL_MUL(( in[2*1+0] - in[2*5+0] - in[2*7+0] ), c[3]);
+ tmp1b = REAL_MUL(( in[2*1+1] - in[2*5+1] - in[2*7+1] ), c[3]);
+ tmp2a = REAL_MUL(( in[2*2+0] - in[2*4+0] - in[2*8+0] ), c[6]) - in[2*6+0] + in[2*0+0];
+ tmp2b = REAL_MUL(( in[2*2+1] - in[2*4+1] - in[2*8+1] ), c[6]) - in[2*6+1] + in[2*0+1];
+
+ MACRO1(1);
+ MACRO2(7);
+ }
+
+ {
+ real tmp1a,tmp2a,tmp1b,tmp2b;
+ tmp1a = REAL_MUL(in[2*1+0], c[5]) - ta33 - REAL_MUL(in[2*5+0], c[7]) + REAL_MUL(in[2*7+0], c[1]);
+ tmp1b = REAL_MUL(in[2*1+1], c[5]) - tb33 - REAL_MUL(in[2*5+1], c[7]) + REAL_MUL(in[2*7+1], c[1]);
+ tmp2a = - REAL_MUL(in[2*2+0], c[8]) - REAL_MUL(in[2*4+0], c[2]) + ta66 + REAL_MUL(in[2*8+0], c[4]);
+ tmp2b = - REAL_MUL(in[2*2+1], c[8]) - REAL_MUL(in[2*4+1], c[2]) + tb66 + REAL_MUL(in[2*8+1], c[4]);
+
+ MACRO1(2);
+ MACRO2(6);
+ }
+
+ {
+ real tmp1a,tmp2a,tmp1b,tmp2b;
+ tmp1a = REAL_MUL(in[2*1+0], c[7]) - ta33 + REAL_MUL(in[2*5+0], c[1]) - REAL_MUL(in[2*7+0], c[5]);
+ tmp1b = REAL_MUL(in[2*1+1], c[7]) - tb33 + REAL_MUL(in[2*5+1], c[1]) - REAL_MUL(in[2*7+1], c[5]);
+ tmp2a = - REAL_MUL(in[2*2+0], c[4]) + REAL_MUL(in[2*4+0], c[8]) + ta66 - REAL_MUL(in[2*8+0], c[2]);
+ tmp2b = - REAL_MUL(in[2*2+1], c[4]) + REAL_MUL(in[2*4+1], c[8]) + tb66 - REAL_MUL(in[2*8+1], c[2]);
+
+ MACRO1(3);
+ MACRO2(5);
+ }
+
+ {
+ real sum0,sum1;
+ sum0 = in[2*0+0] - in[2*2+0] + in[2*4+0] - in[2*6+0] + in[2*8+0];
+ sum1 = REAL_MUL((in[2*0+1] - in[2*2+1] + in[2*4+1] - in[2*6+1] + in[2*8+1] ), tfcos36[4]);
+ MACRO0(4);
+ }
+ }
+#endif
+
+ }
+}
+
+/*
+ * new DCT12
+ */
+static void dct12(real *in,real *rawout1,real *rawout2,register real *wi,register real *ts)
+{
+#define DCT12_PART1 \
+ in5 = in[5*3]; \
+ in5 += (in4 = in[4*3]); \
+ in4 += (in3 = in[3*3]); \
+ in3 += (in2 = in[2*3]); \
+ in2 += (in1 = in[1*3]); \
+ in1 += (in0 = in[0*3]); \
+ \
+ in5 += in3; in3 += in1; \
+ \
+ in2 = REAL_MUL(in2, COS6_1); \
+ in3 = REAL_MUL(in3, COS6_1); \
+
+#define DCT12_PART2 \
+ in0 += REAL_MUL(in4, COS6_2); \
+ \
+ in4 = in0 + in2; \
+ in0 -= in2; \
+ \
+ in1 += REAL_MUL(in5, COS6_2); \
+ \
+ in5 = REAL_MUL((in1 + in3), tfcos12[0]); \
+ in1 = REAL_MUL((in1 - in3), tfcos12[2]); \
+ \
+ in3 = in4 + in5; \
+ in4 -= in5; \
+ \
+ in2 = in0 + in1; \
+ in0 -= in1;
+
+
+ {
+ real in0,in1,in2,in3,in4,in5;
+ register real *out1 = rawout1;
+ ts[SBLIMIT*0] = out1[0]; ts[SBLIMIT*1] = out1[1]; ts[SBLIMIT*2] = out1[2];
+ ts[SBLIMIT*3] = out1[3]; ts[SBLIMIT*4] = out1[4]; ts[SBLIMIT*5] = out1[5];
+
+ DCT12_PART1
+
+ {
+ real tmp0,tmp1 = (in0 - in4);
+ {
+ real tmp2 = REAL_MUL((in1 - in5), tfcos12[1]);
+ tmp0 = tmp1 + tmp2;
+ tmp1 -= tmp2;
+ }
+ ts[(17-1)*SBLIMIT] = out1[17-1] + REAL_MUL(tmp0, wi[11-1]);
+ ts[(12+1)*SBLIMIT] = out1[12+1] + REAL_MUL(tmp0, wi[6+1]);
+ ts[(6 +1)*SBLIMIT] = out1[6 +1] + REAL_MUL(tmp1, wi[1]);
+ ts[(11-1)*SBLIMIT] = out1[11-1] + REAL_MUL(tmp1, wi[5-1]);
+ }
+
+ DCT12_PART2
+
+ ts[(17-0)*SBLIMIT] = out1[17-0] + REAL_MUL(in2, wi[11-0]);
+ ts[(12+0)*SBLIMIT] = out1[12+0] + REAL_MUL(in2, wi[6+0]);
+ ts[(12+2)*SBLIMIT] = out1[12+2] + REAL_MUL(in3, wi[6+2]);
+ ts[(17-2)*SBLIMIT] = out1[17-2] + REAL_MUL(in3, wi[11-2]);
+
+ ts[(6 +0)*SBLIMIT] = out1[6+0] + REAL_MUL(in0, wi[0]);
+ ts[(11-0)*SBLIMIT] = out1[11-0] + REAL_MUL(in0, wi[5-0]);
+ ts[(6 +2)*SBLIMIT] = out1[6+2] + REAL_MUL(in4, wi[2]);
+ ts[(11-2)*SBLIMIT] = out1[11-2] + REAL_MUL(in4, wi[5-2]);
+ }
+
+ in++;
+
+ {
+ real in0,in1,in2,in3,in4,in5;
+ register real *out2 = rawout2;
+
+ DCT12_PART1
+
+ {
+ real tmp0,tmp1 = (in0 - in4);
+ {
+ real tmp2 = REAL_MUL((in1 - in5), tfcos12[1]);
+ tmp0 = tmp1 + tmp2;
+ tmp1 -= tmp2;
+ }
+ out2[5-1] = REAL_MUL(tmp0, wi[11-1]);
+ out2[0+1] = REAL_MUL(tmp0, wi[6+1]);
+ ts[(12+1)*SBLIMIT] += REAL_MUL(tmp1, wi[1]);
+ ts[(17-1)*SBLIMIT] += REAL_MUL(tmp1, wi[5-1]);
+ }
+
+ DCT12_PART2
+
+ out2[5-0] = REAL_MUL(in2, wi[11-0]);
+ out2[0+0] = REAL_MUL(in2, wi[6+0]);
+ out2[0+2] = REAL_MUL(in3, wi[6+2]);
+ out2[5-2] = REAL_MUL(in3, wi[11-2]);
+
+ ts[(12+0)*SBLIMIT] += REAL_MUL(in0, wi[0]);
+ ts[(17-0)*SBLIMIT] += REAL_MUL(in0, wi[5-0]);
+ ts[(12+2)*SBLIMIT] += REAL_MUL(in4, wi[2]);
+ ts[(17-2)*SBLIMIT] += REAL_MUL(in4, wi[5-2]);
+ }
+
+ in++;
+
+ {
+ real in0,in1,in2,in3,in4,in5;
+ register real *out2 = rawout2;
+ out2[12]=out2[13]=out2[14]=out2[15]=out2[16]=out2[17]=0.0;
+
+ DCT12_PART1
+
+ {
+ real tmp0,tmp1 = (in0 - in4);
+ {
+ real tmp2 = REAL_MUL((in1 - in5), tfcos12[1]);
+ tmp0 = tmp1 + tmp2;
+ tmp1 -= tmp2;
+ }
+ out2[11-1] = REAL_MUL(tmp0, wi[11-1]);
+ out2[6 +1] = REAL_MUL(tmp0, wi[6+1]);
+ out2[0+1] += REAL_MUL(tmp1, wi[1]);
+ out2[5-1] += REAL_MUL(tmp1, wi[5-1]);
+ }
+
+ DCT12_PART2
+
+ out2[11-0] = REAL_MUL(in2, wi[11-0]);
+ out2[6 +0] = REAL_MUL(in2, wi[6+0]);
+ out2[6 +2] = REAL_MUL(in3, wi[6+2]);
+ out2[11-2] = REAL_MUL(in3, wi[11-2]);
+
+ out2[0+0] += REAL_MUL(in0, wi[0]);
+ out2[5-0] += REAL_MUL(in0, wi[5-0]);
+ out2[0+2] += REAL_MUL(in4, wi[2]);
+ out2[5-2] += REAL_MUL(in4, wi[5-2]);
+ }
+}
+
+/*
+ * III_hybrid
+ */
+static void III_hybrid(struct mpstr *mp,real fsIn[SBLIMIT][SSLIMIT],real tsOut[SSLIMIT][SBLIMIT],
+ int ch,struct gr_info_s *gr_info,struct frame *fr)
+{
+/*
+ static real block[2][2][SBLIMIT*SSLIMIT] = { { { 0, } } };
+ static int blc[2]={0,0};
+ */
+
+ real *tspnt = (real *) tsOut;
+ real *rawout1,*rawout2;
+ int bt,sb = 0;
+
+ {
+ int b = mp->hybrid_blc[ch];
+ rawout1=mp->hybrid_block[b][ch];
+ b=-b+1;
+ rawout2=mp->hybrid_block[b][ch];
+ mp->hybrid_blc[ch] = b;
+ }
+
+ if(gr_info->mixed_block_flag) {
+ sb = 2;
+#ifdef USE_3DNOW
+ (fr->dct36)(fsIn[0],rawout1,rawout2,win[0],tspnt);
+ (fr->dct36)(fsIn[1],rawout1+18,rawout2+18,win1[0],tspnt+1);
+#else
+ dct36(fsIn[0],rawout1,rawout2,win[0],tspnt);
+ dct36(fsIn[1],rawout1+18,rawout2+18,win1[0],tspnt+1);
+#endif
+ rawout1 += 36; rawout2 += 36; tspnt += 2;
+ }
+
+ bt = gr_info->block_type;
+ if(bt == 2) {
+ for (; sb<gr_info->maxb; sb+=2,tspnt+=2,rawout1+=36,rawout2+=36) {
+ dct12(fsIn[sb] ,rawout1 ,rawout2 ,win[2] ,tspnt);
+ dct12(fsIn[sb+1],rawout1+18,rawout2+18,win1[2],tspnt+1);
+ }
+ }
+ else {
+ for (; sb<gr_info->maxb; sb+=2,tspnt+=2,rawout1+=36,rawout2+=36) {
+#ifdef USE_3DNOW
+ (fr->dct36)(fsIn[sb],rawout1,rawout2,win[bt],tspnt);
+ (fr->dct36)(fsIn[sb+1],rawout1+18,rawout2+18,win1[bt],tspnt+1);
+#else
+ dct36(fsIn[sb],rawout1,rawout2,win[bt],tspnt);
+ dct36(fsIn[sb+1],rawout1+18,rawout2+18,win1[bt],tspnt+1);
+#endif
+ }
+ }
+
+ for(;sb<SBLIMIT;sb++,tspnt++) {
+ int i;
+ for(i=0;i<SSLIMIT;i++) {
+ tspnt[i*SBLIMIT] = *rawout1++;
+ *rawout2++ = DOUBLE_TO_REAL(0.0);
+ }
+ }
+}
+
+
+
+/*
+ * main layer3 handler
+ */
+int do_layer3(struct mpstr *mp,struct frame *fr,int outmode,struct audio_info_struct *ai)
+{
+ int gr, ch, ss,clip=0;
+ int scalefacs[2][39]; /* max 39 for short[13][3] mode, mixed: 38, long: 22 */
+ struct III_sideinfo sideinfo;
+ int stereo = fr->stereo;
+ int single = fr->single;
+ int ms_stereo,i_stereo;
+ int sfreq = fr->sampling_frequency;
+ int stereo1,granules;
+
+ if(stereo == 1) { /* stream is mono */
+ stereo1 = 1;
+ single = 0;
+ }
+ else if(single >= 0) /* stream is stereo, but force to mono */
+ stereo1 = 1;
+ else
+ stereo1 = 2;
+
+ if(fr->mode == MPG_MD_JOINT_STEREO) {
+ ms_stereo = (fr->mode_ext & 0x2)>>1;
+ i_stereo = fr->mode_ext & 0x1;
+ }
+ else
+ ms_stereo = i_stereo = 0;
+
+ granules = fr->lsf ? 1 : 2;
+ if(!III_get_side_info(&sideinfo,stereo,ms_stereo,sfreq,single,fr->lsf))
+ return -1;
+
+ set_pointer(fr->sideInfoSize,sideinfo.main_data_begin);
+
+ for (gr=0;gr<granules;gr++) {
+ real hybridIn [2][SBLIMIT][SSLIMIT];
+ real hybridOut[2][SSLIMIT][SBLIMIT];
+
+ {
+ struct gr_info_s *gr_info = &(sideinfo.ch[0].gr[gr]);
+ long part2bits;
+ if(fr->lsf)
+ part2bits = III_get_scale_factors_2(scalefacs[0],gr_info,0);
+ else
+ part2bits = III_get_scale_factors_1(scalefacs[0],gr_info);
+
+ if(III_dequantize_sample(hybridIn[0], scalefacs[0],gr_info,sfreq,part2bits))
+ return clip;
+ }
+
+ if(stereo == 2) {
+ struct gr_info_s *gr_info = &(sideinfo.ch[1].gr[gr]);
+ long part2bits;
+ if(fr->lsf)
+ part2bits = III_get_scale_factors_2(scalefacs[1],gr_info,i_stereo);
+ else
+ part2bits = III_get_scale_factors_1(scalefacs[1],gr_info);
+
+ if(III_dequantize_sample(hybridIn[1],scalefacs[1],gr_info,sfreq,part2bits))
+ return clip;
+
+ if(ms_stereo) {
+ int i;
+ int maxb = sideinfo.ch[0].gr[gr].maxb;
+ if(sideinfo.ch[1].gr[gr].maxb > maxb)
+ maxb = sideinfo.ch[1].gr[gr].maxb;
+ for(i=0;i<SSLIMIT*maxb;i++) {
+ real tmp0 = ((real *)hybridIn[0])[i];
+ real tmp1 = ((real *)hybridIn[1])[i];
+ ((real *)hybridIn[0])[i] = tmp0 + tmp1;
+ ((real *)hybridIn[1])[i] = tmp0 - tmp1;
+ }
+ }
+
+ if(i_stereo)
+ III_i_stereo(hybridIn,scalefacs[1],gr_info,sfreq,ms_stereo,fr->lsf);
+
+ if(ms_stereo || i_stereo || (single == 3) ) {
+ if(gr_info->maxb > sideinfo.ch[0].gr[gr].maxb)
+ sideinfo.ch[0].gr[gr].maxb = gr_info->maxb;
+ else
+ gr_info->maxb = sideinfo.ch[0].gr[gr].maxb;
+ }
+
+ switch(single) {
+ case 3:
+ {
+ register int i;
+ register real *in0 = (real *) hybridIn[0],*in1 = (real *) hybridIn[1];
+ for(i=0;i<SSLIMIT*gr_info->maxb;i++,in0++)
+ *in0 = (*in0 + *in1++); /* *0.5 done by pow-scale */
+ }
+ break;
+ case 1:
+ {
+ register int i;
+ register real *in0 = (real *) hybridIn[0],*in1 = (real *) hybridIn[1];
+ for(i=0;i<SSLIMIT*gr_info->maxb;i++)
+ *in0++ = *in1++;
+ }
+ break;
+ }
+ }
+
+ for(ch=0;ch<stereo1;ch++) {
+ struct gr_info_s *gr_info = &(sideinfo.ch[ch].gr[gr]);
+ III_antialias(hybridIn[ch],gr_info);
+ III_hybrid(mp,hybridIn[ch], hybridOut[ch], ch,gr_info,fr);
+ }
+
+#ifdef I486_OPT
+ if (fr->synth != synth_1to1 || single >= 0) {
+#endif
+ for(ss=0;ss<SSLIMIT;ss++) {
+ if(single >= 0) {
+ clip += (fr->synth_mono)(hybridOut[0][ss],pcm_sample,&pcm_point);
+ }
+ else {
+ int p1 = pcm_point;
+ clip += (fr->synth)(hybridOut[0][ss],0,pcm_sample,&p1);
+ clip += (fr->synth)(hybridOut[1][ss],1,pcm_sample,&pcm_point);
+ }
+
+ if(pcm_point >= audiobufsize)
+ audio_flush(outmode,ai);
+ }
+#ifdef I486_OPT
+ } else {
+ /* Only stereo, 16 bits benefit from the 486 optimization. */
+ ss=0;
+ while (ss < SSLIMIT) {
+ int n;
+ n=(audiobufsize - pcm_point) / (2*2*32);
+ if (n > (SSLIMIT-ss)) n=SSLIMIT-ss;
+
+ synth_1to1_486(hybridOut[0][ss],0,pcm_sample+pcm_point,n);
+ synth_1to1_486(hybridOut[1][ss],1,pcm_sample+pcm_point,n);
+ ss+=n;
+ pcm_point+=(2*2*32)*n;
+
+ if(pcm_point >= audiobufsize)
+ audio_flush(outmode,ai);
+ }
+ }
+#endif
+ }
+
+ return clip;
+}
diff --git a/mpg123_artsplugin/mpg123/mpg123.h b/mpg123_artsplugin/mpg123/mpg123.h
new file mode 100644
index 00000000..9c61a4ce
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/mpg123.h
@@ -0,0 +1,465 @@
+/*
+ * mpg123 defines
+ * used source: musicout.h from mpegaudio package
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+
+#ifndef WIN32
+#include <sys/signal.h>
+#include <unistd.h>
+#endif
+
+#include <math.h>
+
+typedef unsigned char byte;
+
+#ifdef OS2
+#include <float.h>
+#endif
+
+#define MPG123_REMOTE
+#ifdef HPUX
+#define random rand
+#define srandom srand
+#endif
+
+#define FRONTEND_NONE 0
+#define FRONTEND_SAJBER 1
+#define FRONTEND_TK3PLAY 2
+#define FRONTEND_GENERIC 3
+
+#define SKIP_JUNK 1
+
+#ifdef _WIN32 /* Win32 Additions By Tony Million */
+# undef WIN32
+# define WIN32
+
+# define M_PI 3.14159265358979323846
+# define M_SQRT2 1.41421356237309504880
+# define REAL_IS_FLOAT
+# define NEW_DCT9
+
+# define random rand
+# define srandom srand
+
+# define SIGUSR1 0
+# define SIGCONT 0
+# define SIGSTOP 0
+
+# undef MPG123_REMOTE /* Get rid of this stuff for Win32 */
+#endif
+
+#include "xfermem.h"
+
+#ifdef SUNOS
+#define memmove(dst,src,size) bcopy(src,dst,size)
+#endif
+
+#ifdef REAL_IS_FLOAT
+# define real float
+#elif defined(REAL_IS_LONG_DOUBLE)
+# define real long double
+#elif defined(REAL_IS_FIXED)
+# define real long
+
+# define REAL_RADIX 15
+# define REAL_FACTOR (32.0 * 1024.0)
+
+# define REAL_PLUS_32767 ( 32767 << REAL_RADIX )
+# define REAL_MINUS_32768 ( -32768 << REAL_RADIX )
+
+# define DOUBLE_TO_REAL(x) ((int)((x) * REAL_FACTOR))
+# define REAL_TO_SHORT(x) ((x) >> REAL_RADIX)
+# define REAL_MUL(x, y) (((long long)(x) * (long long)(y)) >> REAL_RADIX)
+
+#else
+# define real double
+#endif
+
+#ifndef DOUBLE_TO_REAL
+# define DOUBLE_TO_REAL(x) (x)
+#endif
+#ifndef REAL_TO_SHORT
+# define REAL_TO_SHORT(x) (x)
+#endif
+#ifndef REAL_PLUS_32767
+# define REAL_PLUS_32767 32767.0
+#endif
+#ifndef REAL_MINUS_32768
+# define REAL_MINUS_32768 -32768.0
+#endif
+#ifndef REAL_MUL
+# define REAL_MUL(x, y) ((x) * (y))
+#endif
+
+#ifdef __GNUC__
+#define INLINE inline
+#else
+#define INLINE
+#endif
+
+#include "audio.h"
+
+/* AUDIOBUFSIZE = n*64 with n=1,2,3 ... */
+#define AUDIOBUFSIZE 16384
+
+#define FALSE 0
+#define TRUE 1
+
+#define MAX_NAME_SIZE 81
+#define SBLIMIT 32
+#define SCALE_BLOCK 12
+#define SSLIMIT 18
+
+#define MPG_MD_STEREO 0
+#define MPG_MD_JOINT_STEREO 1
+#define MPG_MD_DUAL_CHANNEL 2
+#define MPG_MD_MONO 3
+
+/* #define MAXFRAMESIZE 1792 */
+#define MAXFRAMESIZE 4096
+#define HDRCMPMASK 0xfffffd00
+
+#define MAXOUTBURST 32768
+
+/* Pre Shift fo 16 to 8 bit converter table */
+#define AUSHIFT (3)
+
+
+struct al_table
+{
+ short bits;
+ short d;
+};
+
+struct frame {
+ struct al_table *alloc;
+ int (*synth)(real *,int,unsigned char *,int *);
+ int (*synth_mono)(real *,unsigned char *,int *);
+#ifdef USE_3DNOW
+ void (*dct36)(real *,real *,real *,real *,real *);
+#endif
+ int stereo;
+ int jsbound;
+ int single;
+ int II_sblimit;
+ int down_sample_sblimit;
+ int lsf;
+ int mpeg25;
+ int down_sample;
+ int header_change;
+ int lay;
+ int error_protection;
+ int bitrate_index;
+ int sampling_frequency;
+ int padding;
+ int extension;
+ int mode;
+ int mode_ext;
+ int copyright;
+ int original;
+ int emphasis;
+ int framesize; /* computed framesize */
+ int padsize; /* */
+
+ int sideInfoSize; /* Layer3 sideInfo Header size */
+
+ /* FIXME: move this to another place */
+ unsigned long firsthead;
+ unsigned long thishead;
+ int freeformatsize;
+};
+
+#define VBR_TOC_SIZE 100
+
+#define VBR_FRAMES_FLAG 0x0001
+#define VBR_BYTES_FLAG 0x0002
+#define VBR_TOC_FLAG 0x0004
+#define VBR_SCALE_FLAG 0x0008
+
+struct vbrHeader {
+ unsigned long flags;
+ unsigned long frames;
+ unsigned long bytes;
+ unsigned long scale;
+ unsigned char toc[100];
+};
+
+struct parameter {
+ int aggressive; /* renice to max. priority */
+ int shuffle; /* shuffle/random play */
+ int remote; /* remote operation */
+ int outmode; /* where to out the decoded sampels */
+ int quiet; /* shut up! */
+ int xterm_title; /* print filename in xterm title */
+ long usebuffer; /* second level buffer size */
+ int tryresync; /* resync stream after error */
+ int verbose; /* verbose level */
+#ifdef TERM_CONTROL
+ int term_ctrl;
+#endif
+ int force_mono;
+ int force_stereo;
+ int force_8bit;
+ long force_rate;
+ double pitch;
+ int down_sample;
+ int checkrange;
+ long doublespeed;
+ long halfspeed;
+ int force_reopen;
+ int stat_3dnow; /* automatic/force/force-off 3DNow! optimized code */
+ int test_3dnow;
+ int realtime;
+ char filename[256];
+ char *esdserver;
+ char *equalfile;
+ int enable_equalizer;
+ long outscale;
+ long startFrame;
+ int print_version;
+};
+
+struct bitstream_info {
+ int bitindex;
+ unsigned char *wordpointer;
+};
+
+struct mpstr {
+ int bsize;
+ int framesize;
+ int fsizeold;
+ struct frame fr;
+ /* int (*do_layer)(struct mpstr *,struct frame *fr,int,struct audio_info_struct *); */
+ unsigned char bsspace[2][MAXFRAMESIZE+512]; /* MAXFRAMESIZE */
+ real hybrid_block[2][2][SBLIMIT*SSLIMIT];
+ int hybrid_blc[2];
+ unsigned long header;
+ int bsnum;
+ real synth_buffs[2][2][0x110];
+ int synth_bo;
+
+ struct bitstream_info bsi;
+};
+
+extern struct bitstream_info bsi;
+
+struct reader {
+ int (*init)(struct reader *);
+ void (*close)(struct reader *);
+ int (*head_read)(struct reader *,unsigned long *newhead);
+ int (*head_shift)(struct reader *,unsigned long *head);
+ int (*skip_bytes)(struct reader *,int len);
+ int (*read_frame_body)(struct reader *,unsigned char *,int size);
+ int (*back_bytes)(struct reader *,int bytes);
+ int (*back_frame)(struct reader *,struct frame *,int num);
+ long (*tell)(struct reader *);
+ void (*rewind)(struct reader *);
+ long filelen;
+ long filepos;
+ int filept;
+ int flags;
+ unsigned char id3buf[128];
+
+ unsigned char *backbuf;
+ int mark;
+ int bufpos,bufstart,bufend;
+ int bufsize;
+};
+#define READER_FD_OPENED 0x1
+#define READER_ID3TAG 0x2
+
+extern struct reader *rd,readers[];
+
+extern int halfspeed;
+extern int buffer_fd[2];
+extern txfermem *buffermem;
+extern char *prgName, *prgVersion;
+
+#ifndef NOXFERMEM
+extern void buffer_loop(struct audio_info_struct *ai,sigset_t *oldsigset);
+#endif
+
+extern void readers_pushback_header(struct reader *rds,unsigned long aLong);
+extern void readers_mark_pos(struct reader *rds);
+extern void readers_goto_mark(struct reader *rds);
+
+
+/* ------ Declarations from "httpget.c" ------ */
+
+extern char *proxyurl;
+extern unsigned long proxyip;
+extern int http_open (const char *url);
+extern char *httpauth;
+
+/* ------ Declarations from "common.c" ------ */
+
+int sync_stream(struct reader *rds,struct frame *fr,int flags,int *skipped);
+
+extern void audio_flush(int, struct audio_info_struct *);
+extern void (*catchsignal(int signum, void(*handler)()))();
+
+extern void print_header(struct frame *);
+extern void print_header_compact(struct frame *);
+extern void print_id3_tag(unsigned char *buf);
+
+extern int split_dir_file(const char *path, char **dname, char **fname);
+
+extern unsigned int get1bit(struct bitstream_info *);
+extern unsigned int getbits(struct bitstream_info *,int);
+extern unsigned int getbits_fast(struct bitstream_info *,int);
+extern void backbits(struct bitstream_info *,int);
+extern int getbitoffset(struct bitstream_info *);
+extern int getbyte(struct bitstream_info *);
+
+extern void set_pointer(int,long);
+
+extern unsigned char *pcm_sample;
+extern int pcm_point;
+extern int audiobufsize;
+
+extern int OutputDescriptor;
+
+struct gr_info_s {
+ int scfsi;
+ unsigned part2_3_length;
+ unsigned big_values;
+ unsigned scalefac_compress;
+ unsigned block_type;
+ unsigned mixed_block_flag;
+ unsigned table_select[3];
+ unsigned subblock_gain[3];
+ unsigned maxband[3];
+ unsigned maxbandl;
+ unsigned maxb;
+ unsigned region1start;
+ unsigned region2start;
+ unsigned preflag;
+ unsigned scalefac_scale;
+ unsigned count1table_select;
+ real *full_gain[3];
+ real *pow2gain;
+};
+
+struct III_sideinfo
+{
+ unsigned main_data_begin;
+ unsigned private_bits;
+ struct {
+ struct gr_info_s gr[2];
+ } ch[2];
+};
+
+extern struct reader *open_stream(const char *,int fd);
+extern void read_frame_init (struct frame *fr);
+extern int read_frame(struct reader *rd,struct frame *fr);
+extern int play_frame(struct mpstr *mp,int init,struct frame *fr);
+extern int do_layer3(struct mpstr *mp,struct frame *fr,int,struct audio_info_struct *);
+extern int do_layer2(struct mpstr *mp,struct frame *fr,int,struct audio_info_struct *);
+extern int do_layer1(struct mpstr *mp,struct frame *fr,int,struct audio_info_struct *);
+extern void do_equalizer(real *bandPtr,int channel);
+
+#ifdef PENTIUM_OPT
+extern int synth_1to1_pent (real *,int,unsigned char *);
+#endif
+extern int synth_1to1 (real *,int,unsigned char *,int *);
+extern int synth_1to1_8bit (real *,int,unsigned char *,int *);
+extern int synth_1to1_mono (real *,unsigned char *,int *);
+extern int synth_1to1_mono2stereo (real *,unsigned char *,int *);
+extern int synth_1to1_8bit_mono (real *,unsigned char *,int *);
+extern int synth_1to1_8bit_mono2stereo (real *,unsigned char *,int *);
+
+extern int synth_2to1 (real *,int,unsigned char *,int *);
+extern int synth_2to1_8bit (real *,int,unsigned char *,int *);
+extern int synth_2to1_mono (real *,unsigned char *,int *);
+extern int synth_2to1_mono2stereo (real *,unsigned char *,int *);
+extern int synth_2to1_8bit_mono (real *,unsigned char *,int *);
+extern int synth_2to1_8bit_mono2stereo (real *,unsigned char *,int *);
+
+extern int synth_4to1 (real *,int,unsigned char *,int *);
+extern int synth_4to1_8bit (real *,int,unsigned char *,int *);
+extern int synth_4to1_mono (real *,unsigned char *,int *);
+extern int synth_4to1_mono2stereo (real *,unsigned char *,int *);
+extern int synth_4to1_8bit_mono (real *,unsigned char *,int *);
+extern int synth_4to1_8bit_mono2stereo (real *,unsigned char *,int *);
+
+extern int synth_ntom (real *,int,unsigned char *,int *);
+extern int synth_ntom_8bit (real *,int,unsigned char *,int *);
+extern int synth_ntom_mono (real *,unsigned char *,int *);
+extern int synth_ntom_mono2stereo (real *,unsigned char *,int *);
+extern int synth_ntom_8bit_mono (real *,unsigned char *,int *);
+extern int synth_ntom_8bit_mono2stereo (real *,unsigned char *,int *);
+
+extern void rewindNbits(int bits);
+extern int hsstell(void);
+extern void huffman_decoder(int ,int *);
+extern void huffman_count1(int,int *);
+extern void print_stat(struct reader *rd,struct frame *fr,int no,long buffsize,struct audio_info_struct *ai);
+extern int get_songlen(struct reader *rd,struct frame *fr,int no);
+
+extern void init_layer3(int);
+extern void init_layer2(void);
+extern void make_decode_tables(long scale);
+extern void make_conv16to8_table(int);
+extern void dct64(real *,real *,real *);
+
+#ifdef USE_MMX
+extern void dct64_MMX(short *a,short *b,real *c);
+extern int synth_1to1_MMX(real *, int, short *, short *, int *);
+#endif
+
+extern void synth_ntom_set_step(long,long);
+
+extern void control_generic(struct mpstr *,struct frame *fr);
+extern void control_sajber(struct mpstr *,struct frame *fr);
+extern void control_tk3play(struct mpstr *,struct frame *fr);
+
+extern int cdr_open(struct audio_info_struct *ai, char *ame);
+extern int au_open(struct audio_info_struct *ai, char *name);
+extern int wav_open(struct audio_info_struct *ai, char *wavfilename);
+extern int wav_write(unsigned char *buf,int len);
+extern int cdr_close(void);
+extern int au_close(void);
+extern int wav_close(void);
+
+extern int au_open(struct audio_info_struct *ai, char *aufilename);
+extern int au_close(void);
+
+extern int cdr_open(struct audio_info_struct *ai, char *cdrfilename);
+extern int cdr_close(void);
+
+extern int getVBRHeader(struct vbrHeader *head,unsigned char *buf,
+ struct frame *fr);
+
+
+extern unsigned char *conv16to8;
+extern long freqs[9];
+extern real muls[27][64];
+extern real decwin[512+32];
+#ifndef USE_MMX
+extern real *pnts[5];
+#endif
+
+extern real equalizer[2][32];
+extern real equalizer_sum[2][32];
+extern int equalizer_cnt;
+
+extern struct audio_name audio_val2name[];
+
+extern struct parameter param;
+
+/* 486 optimizations */
+#define FIR_BUFFER_SIZE 128
+extern void dct64_486(int *a,int *b,real *c);
+extern int synth_1to1_486(real *bandPtr,int channel,unsigned char *out,int nb_blocks);
+
+/* 3DNow! optimizations */
+#ifdef USE_3DNOW
+extern int getcpuflags(void);
+extern void dct36(real *,real *,real *,real *,real *);
+extern void dct36_3dnow(real *,real *,real *,real *,real *);
+extern int synth_1to1_3dnow(real *,int,unsigned char *,int *);
+#endif
diff --git a/mpg123_artsplugin/mpg123/readers.c b/mpg123_artsplugin/mpg123/readers.c
new file mode 100644
index 00000000..de5f53db
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/readers.c
@@ -0,0 +1,618 @@
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "mpg123.h"
+#include "buffer.h"
+#include "common.h"
+
+#ifdef READ_MMAP
+#include <sys/mman.h>
+#ifndef MAP_FAILED
+#define MAP_FAILED ( (void *) -1 )
+#endif
+#endif
+
+static int get_fileinfo(struct reader *,char *buf);
+static void readers_add_data(struct reader *rds,unsigned char *buf,int len);
+
+/* can hold 4096-1 = 4095 bytes! */
+#define BACKBUF_SIZE (8192)
+
+/*******************************************************************
+ * stream based operation
+ */
+static int bufdiff(struct reader *rds,int start, int end)
+{
+ return (end >= start) ? end - start : rds->bufsize + end - start;
+}
+
+static int fullread(struct reader *rds,int fd,unsigned char *buf,int count)
+{
+ int ret,cnt=0;
+
+ while(cnt < count) {
+ int toread = count-cnt;
+ int num = bufdiff(rds,rds->bufpos,rds->bufend);
+
+ /* if we have some data in the backbuffer .. use it first */
+ if(num > 0) {
+ int part1,part2;
+
+ if(toread > num)
+ toread = num;
+
+ part1 = rds->bufsize - rds->bufpos;
+ if(part1 > toread)
+ part1 = toread;
+ part2 = toread - part1;
+ memcpy(buf+cnt,&rds->backbuf[rds->bufpos],part1);
+ if(part2 > 0)
+ memcpy(buf+cnt+part1,&rds->backbuf[0],part2);
+ rds->bufpos += toread;
+ if(rds->bufpos >= rds->bufsize)
+ rds->bufpos -= rds->bufsize;
+ ret = toread;
+
+ if(!rds->mark)
+ rds->bufstart = rds->bufpos;
+ }
+ else {
+ ret = read(fd,buf+cnt,toread);
+
+ if(ret < 0)
+ return ret;
+ if(ret == 0)
+ break;
+
+ if(rds->mark) {
+ readers_add_data(rds,buf+cnt,ret);
+ rds->bufpos = rds->bufend;
+ }
+
+ }
+ cnt += ret;
+ }
+
+
+if(0)
+{
+ int i;
+ fprintf(stderr,"Fullread2 %d\n",cnt);
+ for(i=0;i<cnt;i++) {
+ fprintf(stderr,"%02x ",buf[i]);
+ if(i % 16 == 15)
+ fprintf(stderr,"\n");
+ }
+}
+
+
+ return cnt;
+}
+
+static void readers_add_data(struct reader *rds,unsigned char *buf,int len)
+{
+ int diff,part1,part2,store = len;
+
+ if(store >= rds->bufsize)
+ store = rds->bufsize - 1;
+
+ /* check whether the new bytes would overwrite the buffer front */
+ diff = bufdiff(rds,rds->bufstart,rds->bufend);
+ if(diff+store >= rds->bufsize) {
+ fprintf(stderr,"Warning: backbuffer overfull %d %d\n",diff+store,rds->bufsize);
+ /* +1 because end should never be the same as start if the is data in the buffer */
+ rds->bufstart += diff + store + 1 - rds->bufsize;
+ if(rds->bufstart >= rds->bufsize)
+ rds->bufstart -= rds->bufsize;
+ }
+
+ part1 = rds->bufsize - rds->bufend;
+ if(part1 > store)
+ part1 = store;
+ part2 = store - part1;
+
+ memcpy(rds->backbuf+rds->bufend,&buf[len-part1+part2],part1);
+ if(part2 > 0)
+ memcpy(rds->backbuf,&buf[len-part2],part2);
+
+ rds->bufend += store;
+ if(rds->bufend >= rds->bufsize)
+ rds->bufend -= rds->bufsize;
+
+
+}
+
+void readers_pushback_header(struct reader *rds,unsigned long aLong)
+{
+ unsigned char buf[4];
+
+ if(rds->mark || (rds->bufpos != rds->bufend) ) {
+ rds->bufpos -= 4;
+ if(rds->bufpos < 0)
+ rds->bufpos += rds->bufsize;
+ }
+ else {
+ buf[0] = (aLong>>24) & 0xff;
+ buf[1] = (aLong>>16) & 0xff;
+ buf[2] = (aLong>>8) & 0xff;
+ buf[3] = (aLong>>0) & 0xff;
+ }
+
+ readers_add_data(rds,buf,4);
+}
+
+void readers_mark_pos(struct reader *rds) {
+ /* fprintf(stderr,"M%d ",rds->bufpos); */
+ rds->bufstart = rds->bufpos;
+ rds->mark = 1;
+}
+
+void readers_goto_mark(struct reader *rds) {
+ /* fprintf(stderr,"G%d ",rds->bufstart); */
+ rds->mark = 0;
+ rds->bufpos = rds->bufstart;
+}
+
+
+static int default_init(struct reader *rds)
+{
+ char buf[128];
+
+ rds->mark = 0;
+ rds->bufend = 0;
+ rds->bufstart = 0;
+ rds->bufpos = 0;
+ rds->bufsize = BACKBUF_SIZE;
+
+ rds->backbuf = (unsigned char *) malloc(rds->bufsize);
+
+ rds->filepos = 0;
+ rds->filelen = get_fileinfo(rds,buf);
+
+ if(rds->filelen > 0) {
+ if(!strncmp(buf,"TAG",3)) {
+ rds->flags |= READER_ID3TAG;
+ memcpy(rds->id3buf,buf,128);
+ }
+ }
+ return 0;
+}
+
+void stream_close(struct reader *rds)
+{
+ if (rds->flags & READER_FD_OPENED)
+ close(rds->filept);
+}
+
+/****************************************
+ * HACK,HACK,HACK: step back <num> frames
+ * can only work if the 'stream' isn't a real stream but a file
+ */
+static int stream_back_bytes(struct reader *rds,int bytes)
+{
+ if(lseek(rds->filept,-bytes,SEEK_CUR) < 0)
+ return -1;
+ if(param.usebuffer)
+ buffer_resync();
+ return 0;
+}
+
+static int stream_back_frame(struct reader *rds,struct frame *fr,int num)
+{
+ long bytes;
+ int skipped;
+
+ if(!fr->firsthead)
+ return 0;
+
+ bytes = (fr->framesize+8)*(num+2);
+
+ /* Skipping back/forth requires a bit more work in buffered mode.
+ * See mapped_back_frame().
+ */
+#ifndef NOXFERMEM
+ if(param.usebuffer)
+ bytes += (long)(xfermem_get_usedspace(buffermem) /
+ (buffermem->buf[0] * buffermem->buf[1]
+ * (buffermem->buf[2] & AUDIO_FORMAT_MASK ?
+ 16.0 : 8.0 ))
+ * (tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index] << 10));
+#endif
+ /*
+ bytes += (long)(compute_buffer_offset(fr)*compute_bpf(fr));
+ */
+ if(lseek(rds->filept,-bytes,SEEK_CUR) < 0)
+ return -1;
+
+ sync_stream(rds,fr,0xffff,&skipped);
+
+ read_frame(rds,fr);
+ read_frame(rds,fr);
+
+ if(fr->lay == 3) {
+ set_pointer(fr->sideInfoSize,512);
+ }
+
+ if(param.usebuffer)
+ buffer_resync();
+
+ return 0;
+}
+
+static int stream_head_read(struct reader *rds,unsigned long *newhead)
+{
+ unsigned char hbuf[4];
+
+ if(fullread(rds,rds->filept,hbuf,4) != 4)
+ return FALSE;
+
+ *newhead = ((unsigned long) hbuf[0] << 24) |
+ ((unsigned long) hbuf[1] << 16) |
+ ((unsigned long) hbuf[2] << 8) |
+ (unsigned long) hbuf[3];
+
+ return TRUE;
+}
+
+static int stream_head_shift(struct reader *rds,unsigned long *head)
+{
+ unsigned char hbuf;
+
+ if(fullread(rds,rds->filept,&hbuf,1) != 1)
+ return 0;
+ *head <<= 8;
+ *head |= hbuf;
+ *head &= 0xffffffff;
+ return 1;
+}
+
+static int stream_skip_bytes(struct reader *rds,int len)
+{
+ if (!param.usebuffer)
+ return lseek(rds->filept,len,SEEK_CUR);
+
+ else {
+ int ret = lseek(rds->filept,len,SEEK_CUR);
+ buffer_resync();
+ return ret;
+ }
+}
+
+static int stream_read_frame_body(struct reader *rds,unsigned char *buf,
+ int size)
+{
+ long l;
+
+ if( (l=fullread(rds,rds->filept,buf,size)) != size)
+ {
+ if(l <= 0)
+ return 0;
+ memset(buf+l,0,size-l);
+ }
+
+ return 1;
+}
+
+static long stream_tell(struct reader *rds)
+{
+ return lseek(rds->filept,0,SEEK_CUR);
+}
+
+static void stream_rewind(struct reader *rds)
+{
+ lseek(rds->filept,0,SEEK_SET);
+ if(param.usebuffer)
+ buffer_resync();
+}
+
+/*
+ * returns length of a file (if filept points to a file)
+ * reads the last 128 bytes information into buffer
+ */
+static int get_fileinfo(struct reader *rds,char *buf)
+{
+ int len;
+
+ if((len=lseek(rds->filept,0,SEEK_END)) < 0) {
+ return -1;
+ }
+ if(lseek(rds->filept,-128,SEEK_END) < 0)
+ return -1;
+ if(fullread(rds,rds->filept,(unsigned char *)buf,128) != 128) {
+ return -1;
+ }
+ if(!strncmp(buf,"TAG",3)) {
+ len -= 128;
+ }
+ if(lseek(rds->filept,0,SEEK_SET) < 0)
+ return -1;
+ if(len <= 0)
+ return -1;
+ return len;
+}
+
+
+#ifdef READ_MMAP
+/*********************************************************+
+ * memory mapped operation
+ *
+ */
+static unsigned char *mapbuf;
+static unsigned char *mappnt;
+static unsigned char *mapend;
+
+static int mapped_init(struct reader *rds)
+{
+ long len;
+ char buf[128];
+
+ len = get_fileinfo(rds,buf);
+ if(len < 0)
+ return -1;
+
+ if(!strncmp(buf,"TAG",3)) {
+ rds->flags |= READER_ID3TAG;
+ memcpy(rds->id3buf,buf,128);
+ }
+
+ mappnt = mapbuf = (unsigned char *)
+ mmap(NULL, len, PROT_READ, MAP_SHARED , rds->filept, 0);
+ if(!mapbuf || mapbuf == MAP_FAILED)
+ return -1;
+
+ mapend = mapbuf + len;
+
+ if(param.verbose > 1)
+ fprintf(stderr,"Using memory mapped IO for this stream.\n");
+
+ rds->filelen = len;
+ return 0;
+}
+
+static void mapped_rewind(struct reader *rds)
+{
+ mappnt = mapbuf;
+ if (param.usebuffer)
+ buffer_resync();
+}
+
+static void mapped_close(struct reader *rds)
+{
+ munmap((void *)mapbuf,mapend-mapbuf);
+ if (rds->flags & READER_FD_OPENED)
+ close(rds->filept);
+}
+
+static int mapped_head_read(struct reader *rds,unsigned long *newhead)
+{
+ unsigned long nh;
+
+ if(mappnt + 4 > mapend)
+ return FALSE;
+
+ nh = (*mappnt++) << 24;
+ nh |= (*mappnt++) << 16;
+ nh |= (*mappnt++) << 8;
+ nh |= (*mappnt++) ;
+
+ *newhead = nh;
+ return TRUE;
+}
+
+static int mapped_head_shift(struct reader *rds,unsigned long *head)
+{
+ if(mappnt + 1 > mapend)
+ return FALSE;
+ *head <<= 8;
+ *head |= *mappnt++;
+ *head &= 0xffffffff;
+ return TRUE;
+}
+
+static int mapped_skip_bytes(struct reader *rds,int len)
+{
+ if(mappnt + len > mapend)
+ return FALSE;
+ mappnt += len;
+ if (param.usebuffer)
+ buffer_resync();
+ return TRUE;
+}
+
+static int mapped_read_frame_body(struct reader *rds,unsigned char *buf,
+ int size)
+{
+ if(size <= 0) {
+ fprintf(stderr,"Ouch. Read_frame called with size <= 0\n");
+ return FALSE;
+ }
+ if(mappnt + size > mapend)
+ return FALSE;
+ memcpy(buf,mappnt,size);
+ mappnt += size;
+
+ return TRUE;
+}
+
+static int mapped_back_bytes(struct reader *rds,int bytes)
+{
+ if( (mappnt - bytes) < mapbuf || (mappnt - bytes + 4) > mapend)
+ return -1;
+ mappnt -= bytes;
+ if(param.usebuffer)
+ buffer_resync();
+ return 0;
+}
+
+static int mapped_back_frame(struct reader *rds,struct frame *fr,int num)
+{
+ long bytes;
+ unsigned long newhead;
+
+
+ if(!firsthead)
+ return 0;
+
+ bytes = (fr->framesize+8)*(num+2);
+
+ /* Buffered mode is a bit trickier. From the size of the buffered
+ * output audio stream we have to make a guess at the number of frames
+ * this corresponds to.
+ */
+#ifndef NOXFERMEM
+ if(param.usebuffer)
+ bytes += (long)(xfermem_get_usedspace(buffermem) /
+ (buffermem->buf[0] * buffermem->buf[1]
+ * (buffermem->buf[2] & AUDIO_FORMAT_MASK ?
+ 16.0 : 8.0 ))
+ * (tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index] << 10));
+#endif
+ /*
+ bytes += (long)(compute_buffer_offset(fr)*compute_bpf(fr));
+ */
+
+ if( (mappnt - bytes) < mapbuf || (mappnt - bytes + 4) > mapend)
+ return -1;
+ mappnt -= bytes;
+
+ newhead = (mappnt[0]<<24) + (mappnt[1]<<16) + (mappnt[2]<<8) + mappnt[3];
+ mappnt += 4;
+
+ while( (newhead & HDRCMPMASK) != (firsthead & HDRCMPMASK) ) {
+ if(mappnt + 1 > mapend)
+ return -1;
+ newhead <<= 8;
+ newhead |= *mappnt++;
+ newhead &= 0xffffffff;
+ }
+ mappnt -= 4;
+
+ read_frame(fr);
+ read_frame(fr);
+
+ if(fr->lay == 3)
+ set_pointer(fr->sideInfoSize,512);
+
+ if(param.usebuffer)
+ buffer_resync();
+
+ return 0;
+}
+
+static long mapped_tell(struct reader *rds)
+{
+ return mappnt - mapbuf;
+}
+
+#endif
+
+/*****************************************************************
+ * read frame helper
+ */
+
+struct reader *rd;
+struct reader readers[] = {
+#ifdef READ_SYSTEM
+ { system_init,
+ NULL, /* filled in by system_init() */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL } ,
+#endif
+#ifdef READ_MMAP
+ { mapped_init,
+ mapped_close,
+ mapped_head_read,
+ mapped_head_shift,
+ mapped_skip_bytes,
+ mapped_read_frame_body,
+ mapped_back_bytes,
+ mapped_back_frame,
+ mapped_tell,
+ mapped_rewind } ,
+#endif
+ { default_init,
+ stream_close,
+ stream_head_read,
+ stream_head_shift,
+ stream_skip_bytes,
+ stream_read_frame_body,
+ stream_back_bytes,
+ stream_back_frame,
+ stream_tell,
+ stream_rewind } ,
+ { NULL, }
+};
+
+
+/* open the device to read the bit stream from it */
+
+struct reader *open_stream(const char *bs_filenam,int fd)
+{
+ int i;
+ int filept_opened = 1;
+ int filept;
+
+ if (!bs_filenam) {
+ if(fd < 0) {
+ filept = 0;
+ filept_opened = 0;
+ }
+ else
+ filept = fd;
+ }
+ else if (!strncasecmp(bs_filenam, "http://", 7))
+ filept = http_open(bs_filenam);
+ else if (!strncasecmp(bs_filenam, "ftp://", 6))
+ filept = http_open(bs_filenam);
+
+#ifndef O_BINARY
+#define O_BINARY (0)
+#endif
+ else if ( (filept = open(bs_filenam, O_RDONLY|O_BINARY)) < 0) {
+ perror (bs_filenam);
+ return NULL;
+ }
+
+ rd = NULL;
+ for(i=0;;i++) {
+ readers[i].filelen = -1;
+ readers[i].filept = filept;
+ readers[i].flags = 0;
+ if(filept_opened)
+ readers[i].flags |= READER_FD_OPENED;
+ if(!readers[i].init) {
+ fprintf(stderr,"Fatal error!\n");
+ exit(1);
+ }
+ if(readers[i].init(readers+i) >= 0) {
+ rd = &readers[i];
+ break;
+ }
+ }
+
+ if(rd && rd->flags & READER_ID3TAG) {
+ print_id3_tag(rd->id3buf);
+ }
+
+ return rd;
+}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpg123_artsplugin/mpg123/tabinit.c b/mpg123_artsplugin/mpg123/tabinit.c
new file mode 100644
index 00000000..4622114e
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/tabinit.c
@@ -0,0 +1,139 @@
+
+#include <stdlib.h>
+
+#include "mpg123.h"
+
+static unsigned char *conv16to8_buf = NULL;
+unsigned char *conv16to8;
+
+#ifndef USE_MMX
+real decwin[512+32];
+static real cos64[16],cos32[8],cos16[4],cos8[2],cos4[1];
+
+real *pnts[] = { cos64,cos32,cos16,cos8,cos4 };
+
+
+static long intwinbase[] = {
+ 0, -1, -1, -1, -1, -1, -1, -2, -2, -2,
+ -2, -3, -3, -4, -4, -5, -5, -6, -7, -7,
+ -8, -9, -10, -11, -13, -14, -16, -17, -19, -21,
+ -24, -26, -29, -31, -35, -38, -41, -45, -49, -53,
+ -58, -63, -68, -73, -79, -85, -91, -97, -104, -111,
+ -117, -125, -132, -139, -147, -154, -161, -169, -176, -183,
+ -190, -196, -202, -208, -213, -218, -222, -225, -227, -228,
+ -228, -227, -224, -221, -215, -208, -200, -189, -177, -163,
+ -146, -127, -106, -83, -57, -29, 2, 36, 72, 111,
+ 153, 197, 244, 294, 347, 401, 459, 519, 581, 645,
+ 711, 779, 848, 919, 991, 1064, 1137, 1210, 1283, 1356,
+ 1428, 1498, 1567, 1634, 1698, 1759, 1817, 1870, 1919, 1962,
+ 2001, 2032, 2057, 2075, 2085, 2087, 2080, 2063, 2037, 2000,
+ 1952, 1893, 1822, 1739, 1644, 1535, 1414, 1280, 1131, 970,
+ 794, 605, 402, 185, -45, -288, -545, -814, -1095, -1388,
+ -1692, -2006, -2330, -2663, -3004, -3351, -3705, -4063, -4425, -4788,
+ -5153, -5517, -5879, -6237, -6589, -6935, -7271, -7597, -7910, -8209,
+ -8491, -8755, -8998, -9219, -9416, -9585, -9727, -9838, -9916, -9959,
+ -9966, -9935, -9863, -9750, -9592, -9389, -9139, -8840, -8492, -8092,
+ -7640, -7134, -6574, -5959, -5288, -4561, -3776, -2935, -2037, -1082,
+ -70, 998, 2122, 3300, 4533, 5818, 7154, 8540, 9975, 11455,
+ 12980, 14548, 16155, 17799, 19478, 21189, 22929, 24694, 26482, 28289,
+ 30112, 31947, 33791, 35640, 37489, 39336, 41176, 43006, 44821, 46617,
+ 48390, 50137, 51853, 53534, 55178, 56778, 58333, 59838, 61289, 62684,
+ 64019, 65290, 66494, 67629, 68692, 69679, 70590, 71420, 72169, 72835,
+ 73415, 73908, 74313, 74630, 74856, 74992, 75038 };
+
+void make_decode_tables(long scaleval)
+{
+ int i,j,k,kr,divv;
+ real *costab;
+ int idx;
+
+
+ for(i=0;i<5;i++)
+ {
+ kr=0x10>>i; divv=0x40>>i;
+ costab = pnts[i];
+ for(k=0;k<kr;k++)
+ costab[k] = DOUBLE_TO_REAL(1.0 / (2.0 * cos(M_PI * ((double) k * 2.0 + 1.0) / (double) divv)));
+ }
+
+ idx = 0;
+ scaleval = -scaleval;
+ for(i=0,j=0;i<256;i++,j++,idx+=32)
+ {
+ if(idx < 512+16)
+ decwin[idx+16] = decwin[idx] = DOUBLE_TO_REAL((double) intwinbase[j] / 65536.0 * (double) scaleval);
+
+ if(i % 32 == 31)
+ idx -= 1023;
+ if(i % 64 == 63)
+ scaleval = - scaleval;
+ }
+
+ for( /* i=256 */ ;i<512;i++,j--,idx+=32)
+ {
+ if(idx < 512+16)
+ decwin[idx+16] = decwin[idx] = DOUBLE_TO_REAL((double) intwinbase[j] / 65536.0 * (double) scaleval);
+
+ if(i % 32 == 31)
+ idx -= 1023;
+ if(i % 64 == 63)
+ scaleval = - scaleval;
+ }
+
+}
+#endif
+
+void make_conv16to8_table(int mode)
+{
+ int i;
+
+ /*
+ * ????: 8.0 is right but on SB cards '2.0' is a better value ???
+ */
+ const double mul = 8.0;
+
+ if(!conv16to8_buf) {
+ conv16to8_buf = (unsigned char *) malloc(8192);
+ if(!conv16to8_buf) {
+ fprintf(stderr,"Can't allocate 16 to 8 converter table!\n");
+ exit(1);
+ }
+ conv16to8 = conv16to8_buf + 4096;
+ }
+
+ if(mode == AUDIO_FORMAT_ULAW_8) {
+ double m=127.0 / log(256.0);
+ int c1;
+
+ for(i=-4096;i<4096;i++) {
+/* dunno whether this is a valid transformation rule ?!?!? */
+ if(i < 0)
+ c1 = 127 - (int) (log( 1.0 - 255.0 * (double) i*mul / 32768.0 ) * m);
+ else
+ c1 = 255 - (int) (log( 1.0 + 255.0 * (double) i*mul / 32768.0 ) * m);
+ if(c1 < 0 || c1 > 255)
+ fprintf(stderr,"Converror %d %d\n",i,c1);
+ if(c1 == 0)
+ c1 = 2;
+ conv16to8[i] = (unsigned char) c1;
+ }
+ }
+ else if(mode == AUDIO_FORMAT_SIGNED_8) {
+ for(i=-4096;i<4096;i++) {
+ conv16to8[i] = i>>5;
+ }
+ }
+ else if(mode == AUDIO_FORMAT_UNSIGNED_8) {
+ for(i=-4096;i<4096;i++) {
+ conv16to8[i] = (i>>5)+128;
+ }
+ }
+ else {
+ for(i=-4096;i<4096;i++) {
+ conv16to8[i] = 0;
+ }
+ }
+}
+
+
+
diff --git a/mpg123_artsplugin/mpg123/tabinit_MMX.s b/mpg123_artsplugin/mpg123/tabinit_MMX.s
new file mode 100644
index 00000000..06a54e5f
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/tabinit_MMX.s
@@ -0,0 +1,160 @@
+.bss
+ .align 32
+ .comm decwin,2176,32
+ .align 32
+ .comm decwins,2176,32
+
+.data
+ .align 32
+intwinbase:
+ .value 0, -1, -1, -1, -1, -1, -1, -2
+ .value -2, -2, -2, -3, -3, -4, -4, -5
+ .value -5, -6, -7, -7, -8, -9, -10, -11
+ .value -13, -14, -16, -17, -19, -21, -24, -26
+ .value -29, -31, -35, -38, -41, -45, -49, -53
+ .value -58, -63, -68, -73, -79, -85, -91, -97
+ .value -104, -111, -117, -125, -132, -139, -147, -154
+ .value -161, -169, -176, -183, -190, -196, -202, -208
+ .value -213, -218, -222, -225, -227, -228, -228, -227
+ .value -224, -221, -215, -208, -200, -189, -177, -163
+ .value -146, -127, -106, -83, -57, -29, 2, 36
+ .value 72, 111, 153, 197, 244, 294, 347, 401
+ .value 459, 519, 581, 645, 711, 779, 848, 919
+ .value 991, 1064, 1137, 1210, 1283, 1356, 1428, 1498
+ .value 1567, 1634, 1698, 1759, 1817, 1870, 1919, 1962
+ .value 2001, 2032, 2057, 2075, 2085, 2087, 2080, 2063
+ .value 2037, 2000, 1952, 1893, 1822, 1739, 1644, 1535
+ .value 1414, 1280, 1131, 970, 794, 605, 402, 185
+ .value -45, -288, -545, -814, -1095, -1388, -1692, -2006
+ .value -2330, -2663, -3004, -3351, -3705, -4063, -4425, -4788
+ .value -5153, -5517, -5879, -6237, -6589, -6935, -7271, -7597
+ .value -7910, -8209, -8491, -8755, -8998, -9219, -9416, -9585
+ .value -9727, -9838, -9916, -9959, -9966, -9935, -9863, -9750
+ .value -9592, -9389, -9139, -8840, -8492, -8092, -7640, -7134
+ .value -6574, -5959, -5288, -4561, -3776, -2935, -2037, -1082
+ .value -70, 998, 2122, 3300, 4533, 5818, 7154, 8540
+ .value 9975, 11455, 12980, 14548, 16155, 17799, 19478, 21189
+ .value 22929, 24694, 26482, 28289, 30112, 31947,-26209,-24360
+ .value -22511,-20664,-18824,-16994,-15179,-13383,-11610, -9863
+ .value -8147, -6466, -4822, -3222, -1667, -162, 1289, 2684
+ .value 4019, 5290, 6494, 7629, 8692, 9679, 10590, 11420
+ .value 12169, 12835, 13415, 13908, 14313, 14630, 14856, 14992
+ .value 15038
+
+intwindiv:
+ .long 0x47800000 # 65536.0
+.text
+ .align 32
+.globl make_decode_tables
+
+make_decode_tables:
+ pushl %edi
+ pushl %esi
+ pushl %ebx
+
+ xorl %ecx,%ecx
+ xorl %ebx,%ebx
+ movl $32,%esi
+ movl $intwinbase,%edi
+ negl 16(%esp) # scaleval
+ pushl $2 # intwinbase step
+.L00:
+ cmpl $528,%ecx
+ jnc .L02
+ movswl (%edi),%eax
+ cmpl $intwinbase+444,%edi
+ jc .L01
+ addl $60000,%eax
+.L01:
+ pushl %eax
+ fildl (%esp)
+ fdivs intwindiv
+ fimull 24(%esp)
+ popl %eax
+ fsts decwin(,%ecx,4)
+ fstps decwin+64(,%ecx,4)
+.L02:
+ leal -1(%esi),%edx
+ and %ebx,%edx
+ cmp $31,%edx
+ jnz .L03
+ addl $-1023,%ecx
+ test %esi,%ebx
+ jz .L03
+ negl 20(%esp)
+.L03:
+ addl %esi,%ecx
+ addl (%esp),%edi
+ incl %ebx
+ cmpl $intwinbase,%edi
+ jz .L04
+ cmp $256,%ebx
+ jnz .L00
+ negl (%esp)
+ jmp .L00
+.L04:
+ popl %eax
+
+ xorl %ecx,%ecx
+ xorl %ebx,%ebx
+ pushl $2
+.L05:
+ cmpl $528,%ecx
+ jnc .L11
+ movswl (%edi),%eax
+ cmpl $intwinbase+444,%edi
+ jc .L06
+ addl $60000,%eax
+.L06:
+ cltd
+ imull 20(%esp)
+ shrdl $17,%edx,%eax
+ cmpl $32767,%eax
+ movl $1055,%edx
+ jle .L07
+ movl $32767,%eax
+ jmp .L08
+.L07:
+ cmpl $-32767,%eax
+ jge .L08
+ movl $-32767,%eax
+.L08:
+ cmpl $512,%ecx
+ jnc .L09
+ subl %ecx,%edx
+ movw %ax,decwins(,%edx,2)
+ movw %ax,decwins-32(,%edx,2)
+.L09:
+ testl $1,%ecx
+ jnz .L10
+ negl %eax
+.L10:
+ movw %ax,decwins(,%ecx,2)
+ movw %ax,decwins+32(,%ecx,2)
+.L11:
+ leal -1(%esi),%edx
+ and %ebx,%edx
+ cmp $31,%edx
+ jnz .L12
+ addl $-1023,%ecx
+ test %esi,%ebx
+ jz .L12
+ negl 20(%esp)
+.L12:
+ addl %esi,%ecx
+ addl (%esp),%edi
+ incl %ebx
+ cmpl $intwinbase,%edi
+ jz .L13
+ cmp $256,%ebx
+ jnz .L05
+ negl (%esp)
+ jmp .L05
+.L13:
+ popl %eax
+
+ popl %ebx
+ popl %esi
+ popl %edi
+ ret
+
diff --git a/mpg123_artsplugin/mpg123/vbrhead.c b/mpg123_artsplugin/mpg123/vbrhead.c
new file mode 100644
index 00000000..f77ab279
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/vbrhead.c
@@ -0,0 +1,79 @@
+/*
+ * This checks for the VBR Header defined by Xing(tm)
+ */
+
+#include "mpg123.h"
+
+static unsigned long get32bits(unsigned char *buf) {
+ unsigned long ret = 0;
+
+ ret = (((unsigned long) buf[0]) << 24) |
+ (((unsigned long) buf[1]) << 16) |
+ (((unsigned long) buf[2]) << 8) |
+ ((unsigned long) buf[3]) ;
+
+ return ret;
+}
+
+int getVBRHeader(struct vbrHeader *head,unsigned char *buf, struct frame *fr)
+{
+ int ssize;
+
+ if(fr->lay != 3)
+ return 0;
+
+ if(fr->lsf)
+ ssize = (fr->stereo == 1) ? 9 : 17;
+ else
+ ssize = (fr->stereo == 1) ? 17 : 32;
+
+
+ buf += ssize;
+
+ if(( buf[0] != 'X' ) || ( buf[1] != 'i' ) ||
+ ( buf[2] != 'n' ) || ( buf[3] != 'g' ) )
+ return 0;
+ buf+=4;
+
+ head->flags = get32bits(buf);
+ buf+=4;
+
+ if(head->flags & VBR_FRAMES_FLAG) {
+ head->frames = get32bits(buf);
+ buf += 4;
+ }
+
+ if(head->flags & VBR_BYTES_FLAG) {
+ head->bytes = get32bits(buf);
+ buf += 4;
+ }
+
+ if(head->flags & VBR_TOC_FLAG) {
+ memcpy(head->toc,buf,100);
+ buf += 100;
+ }
+
+ if(head->flags & VBR_SCALE_FLAG) {
+ head->scale = get32bits(buf);
+ buf += 4;
+ }
+
+ fprintf(stderr,"Found XING %04lx\n",head->flags);
+
+ return 1;
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mpg123_artsplugin/mpg123/xfermem.c b/mpg123_artsplugin/mpg123/xfermem.c
new file mode 100644
index 00000000..8e43dab4
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/xfermem.c
@@ -0,0 +1,275 @@
+/*
+ * xfermem.c
+ *
+ * Oliver Fromme <oliver.fromme@heim3.tu-clausthal.de>
+ * Sun Apr 6 02:26:26 MET DST 1997
+ *
+ * See xfermem.h for documentation/description.
+ */
+
+#ifndef NOXFERMEM
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+
+#ifdef AIX
+#include <sys/select.h>
+#endif
+
+#include "mpg123.h"
+
+#ifndef USE_MMAP
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#endif
+
+extern int errno;
+
+#if defined (USE_MMAP) && defined(MAP_ANONYMOUS) && !defined(MAP_ANON)
+#define MAP_ANON MAP_ANONYMOUS
+#endif
+
+void xfermem_init (txfermem **xf, int bufsize, int msize, int skipbuf)
+{
+ int regsize = bufsize + msize + skipbuf + sizeof(txfermem);
+ extern int preload;
+
+#ifdef USE_MMAP
+# ifdef MAP_ANON
+ if ((*xf = (txfermem *) mmap(0, regsize, PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_SHARED, -1, 0)) == (txfermem *) -1) {
+ perror ("mmap()");
+ exit (1);
+ }
+# else
+ int devzero;
+ if ((devzero = open("/dev/zero", O_RDWR, 0)) == -1) {
+ perror ("open(/dev/zero)");
+ exit (1);
+ }
+ if ((*xf = (txfermem *) mmap(0, regsize, PROT_READ | PROT_WRITE,
+ MAP_SHARED, devzero, 0)) == (txfermem *) -1) {
+ perror ("mmap()");
+ exit (1);
+ }
+ close (devzero);
+# endif
+#else
+ struct shmid_ds shmemds;
+ int shmemid;
+ if ((shmemid = shmget(IPC_PRIVATE, regsize, IPC_CREAT | 0600)) == -1) {
+ perror ("shmget()");
+ exit (1);
+ }
+ if ((*xf = (txfermem *) shmat(shmemid, 0, 0)) == (txfermem *) -1) {
+ perror ("shmat()");
+ shmctl (shmemid, IPC_RMID, &shmemds);
+ exit (1);
+ }
+ if (shmctl(shmemid, IPC_RMID, &shmemds) == -1) {
+ perror ("shmctl()");
+ xfermem_done (*xf);
+ exit (1);
+ }
+#endif
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, (*xf)->fd) < 0) {
+ perror ("socketpair()");
+ xfermem_done (*xf);
+ exit (1);
+ }
+ (*xf)->freeindex = (*xf)->readindex = 0;
+ (*xf)->wakeme[0] = (*xf)->wakeme[1] = FALSE;
+ (*xf)->data = ((byte *) *xf) + sizeof(txfermem) + msize;
+ (*xf)->metadata = ((byte *) *xf) + sizeof(txfermem);
+ (*xf)->size = bufsize;
+ (*xf)->metasize = msize + skipbuf;
+ preload = bufsize>>3;
+}
+
+void xfermem_done (txfermem *xf)
+{
+ if(!xf)
+ return;
+#ifdef USE_MMAP
+ munmap ((caddr_t) xf, xf->size + xf->metasize + sizeof(txfermem));
+#else
+ if (shmdt((void *) xf) == -1) {
+ perror ("shmdt()");
+ exit (1);
+ }
+#endif
+}
+
+void xfermem_init_writer (txfermem *xf)
+{
+ if(xf)
+ close (xf->fd[XF_READER]);
+}
+
+void xfermem_init_reader (txfermem *xf)
+{
+ if(xf)
+ close (xf->fd[XF_WRITER]);
+}
+
+int xfermem_get_freespace (txfermem *xf)
+{
+ int freeindex, readindex;
+
+ if(!xf)
+ return 0;
+
+ if ((freeindex = xf->freeindex) < 0
+ || (readindex = xf->readindex) < 0)
+ return (0);
+ if (readindex > freeindex)
+ return ((readindex - freeindex) - 1);
+ else
+ return ((xf->size - (freeindex - readindex)) - 1);
+}
+
+int xfermem_get_usedspace (txfermem *xf)
+{
+ int freeindex, readindex;
+
+ if(!xf)
+ return 0;
+
+ if ((freeindex = xf->freeindex) < 0
+ || (readindex = xf->readindex) < 0)
+ return (0);
+ if (freeindex >= readindex)
+ return (freeindex - readindex);
+ else
+ return (xf->size - (readindex - freeindex));
+}
+
+int xfermem_getcmd (int fd, int block)
+{
+ fd_set selfds;
+ byte cmd;
+
+ for (;;) {
+ struct timeval selto = {0, 0};
+
+ FD_ZERO (&selfds);
+ FD_SET (fd, &selfds);
+ /* #ifdef HPUX */ /* seems to trigger performance problems? strange */
+#if 0
+ switch (select(FD_SETSIZE, (int *) &selfds, NULL, NULL, block ? NULL : &selto)) {
+#else
+ switch (select(FD_SETSIZE, &selfds, NULL, NULL, block ? NULL : &selto)) {
+#endif
+ case 0:
+ if (!block)
+ return (0);
+ continue;
+ case -1:
+ if (errno == EINTR)
+ continue;
+ return (-2);
+ case 1:
+ if (FD_ISSET(fd, &selfds))
+ switch (read(fd, &cmd, 1)) {
+ case 0: /* EOF */
+ return (-1);
+ case -1:
+ if (errno == EINTR)
+ continue;
+ return (-3);
+ case 1:
+ return (cmd);
+ default: /* ?!? */
+ return (-4);
+ }
+ else /* ?!? */
+ return (-5);
+ default: /* ?!? */
+ return (-6);
+ }
+ }
+}
+
+int xfermem_putcmd (int fd, byte cmd)
+{
+ for (;;) {
+ switch (write(fd, &cmd, 1)) {
+ case 1:
+ return (1);
+ case -1:
+ if (errno != EINTR)
+ return (-1);
+ }
+ }
+}
+
+int xfermem_block (int readwrite, txfermem *xf)
+{
+ int myfd = xf->fd[readwrite];
+ int result;
+
+ xf->wakeme[readwrite] = TRUE;
+ if (xf->wakeme[1 - readwrite])
+ xfermem_putcmd (myfd, XF_CMD_WAKEUP);
+ result = xfermem_getcmd(myfd, TRUE);
+ xf->wakeme[readwrite] = FALSE;
+ return ((result <= 0) ? -1 : result);
+}
+
+#elif defined(WIN32)
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "mpg123.h"
+
+extern int errno;
+
+void xfermem_init (txfermem **xf, int bufsize, int msize, int skipbuf)
+{
+}
+void xfermem_done (txfermem *xf)
+{
+}
+void xfermem_init_writer (txfermem *xf)
+{
+}
+void xfermem_init_reader (txfermem *xf)
+{
+}
+int xfermem_get_freespace (txfermem *xf)
+{
+ return 0;
+}
+int xfermem_get_usedspace (txfermem *xf)
+{
+ return 0;
+}
+int xfermem_getcmd (int fd, int block)
+{
+ return 0;
+}
+int xfermem_putcmd (int fd, byte cmd)
+{
+ return 0;
+}
+int xfermem_block (int readwrite, txfermem *xf)
+{
+ return 0;
+}
+#endif
+
+/* eof */
+
diff --git a/mpg123_artsplugin/mpg123/xfermem.h b/mpg123_artsplugin/mpg123/xfermem.h
new file mode 100644
index 00000000..ae7e5afd
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/xfermem.h
@@ -0,0 +1,60 @@
+/*
+ * xfermem.h
+ *
+ * Oliver Fromme <oliver.fromme@heim3.tu-clausthal.de>
+ * Sat Mar 29 04:41:34 MET 1997
+ *
+ * This is a stand-alone module which implements a unidirectional,
+ * fast pipe using mmap(). Its primary use is to transfer large
+ * amounts of data from a parent process to its child process,
+ * with a buffer in between which decouples blocking conditions
+ * on both sides. Control information is transferred between the
+ * processes through a socketpair. See xftest.c for an example on
+ * how to use this module.
+ */
+
+#ifndef TRUE
+#define FALSE 0
+#define TRUE 1
+#endif
+
+typedef struct {
+ int freeindex; /* [W] next free index */
+ int readindex; /* [R] next index to read */
+ int fd[2];
+ int wakeme[2];
+ byte *data;
+ byte *metadata;
+ int size;
+ int metasize;
+ int buf[3];
+} txfermem;
+/*
+ * [W] -- May be written to by the writing process only!
+ * [R] -- May be written to by the reading process only!
+ * All other entries are initialized once.
+ */
+
+void xfermem_init (txfermem **xf, int bufsize, int msize,int skipbuf);
+void xfermem_init_writer (txfermem *xf);
+void xfermem_init_reader (txfermem *xf);
+
+int xfermem_write (txfermem *xf, byte *data, int count);
+int xfermem_read (txfermem *xf, byte *data, int count);
+
+int xfermem_get_freespace (txfermem *xf);
+int xfermem_get_usedspace (txfermem *xf);
+#define XF_CMD_WAKEUP_INFO 0x04
+#define XF_CMD_WAKEUP 0x02
+#define XF_CMD_TERMINATE 0x03
+#define XF_WRITER 0
+#define XF_READER 1
+int xfermem_getcmd (int fd, int block);
+int xfermem_putcmd (int fd, byte cmd);
+int xfermem_block (int fd, txfermem *xf);
+
+void xfermem_done (txfermem *xf);
+#define xfermem_done_writer xfermem_init_reader
+#define xfermem_done_reader xfermem_init_writer
+
+/* EOF */
diff --git a/mpg123_artsplugin/mpg123PlayObject.mcopclass b/mpg123_artsplugin/mpg123PlayObject.mcopclass
new file mode 100644
index 00000000..24f3f8cf
--- /dev/null
+++ b/mpg123_artsplugin/mpg123PlayObject.mcopclass
@@ -0,0 +1,6 @@
+Interface=Arts::mpg123PlayObject,Arts::PlayObject,Arts::SynthModule,Arts::Object
+Library=libmpg123arts.la
+Author=Kevin Puetz <puetzk@iastate.edu>
+Extension=mp3,mp1,mp2
+Language=C++
+MimeType=audio/x-mp3,audio/x-mp1,audio/x-mp2
diff --git a/mpg123_artsplugin/mpg123PlayObject_impl.cpp b/mpg123_artsplugin/mpg123PlayObject_impl.cpp
new file mode 100644
index 00000000..29ab5dcf
--- /dev/null
+++ b/mpg123_artsplugin/mpg123PlayObject_impl.cpp
@@ -0,0 +1,675 @@
+/*
+ * $Id$
+ * Copyright (C) 2001 Kevin Puetz
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/stat.h>
+#include <sys/shm.h>
+#include <sys/wait.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/////////////////////////////////////////////////////////////
+// aRts interface
+
+#include <stdsynthmodule.h>
+#include "mpg123arts.h"
+#include <convert.h>
+#include <debug.h>
+
+
+#include "mpg123PlayObject_impl.h"
+
+using namespace Arts;
+
+int mpg123PlayObject_impl::decoder_init = 0;
+
+// This is to minimize the hackery in mpg123
+int audio_get_formats(struct audio_info_struct *)
+{
+ return AUDIO_FORMAT_SIGNED_16;
+}
+
+// This is purely convenience
+void mpg123PlayObject_impl::set_synth_functions(struct frame *fr)
+{
+ typedef int (*func)(real *,int,unsigned char *,int *);
+ typedef int (*func_mono)(real *,unsigned char *,int *);
+ typedef void (*func_dct36)(real *,real *,real *,real *,real *);
+
+ int ds = fr->down_sample;
+ int p8=0;
+ // it's as big as pcmdata (internal to mpg123 - where this size comes from I dunno
+ // but it'll be big enough, since all data comes as memcpy's from there.
+
+ static func funcs[][4] = {
+ { synth_1to1,
+ synth_2to1,
+ synth_4to1,
+ synth_ntom } ,
+ { synth_1to1_8bit,
+ synth_2to1_8bit,
+ synth_4to1_8bit,
+ synth_ntom_8bit }
+#ifdef USE_3DNOW
+ ,{synth_1to1_3dnow,
+ synth_2to1,
+ synth_4to1,
+ synth_ntom }
+#endif
+ };
+
+ static func_mono funcs_mono[2][2][4] = {
+ { { synth_1to1_mono2stereo ,
+ synth_2to1_mono2stereo ,
+ synth_4to1_mono2stereo ,
+ synth_ntom_mono2stereo } ,
+ { synth_1to1_8bit_mono2stereo ,
+ synth_2to1_8bit_mono2stereo ,
+ synth_4to1_8bit_mono2stereo ,
+ synth_ntom_8bit_mono2stereo } } ,
+ { { synth_1to1_mono ,
+ synth_2to1_mono ,
+ synth_4to1_mono ,
+ synth_ntom_mono } ,
+ { synth_1to1_8bit_mono ,
+ synth_2to1_8bit_mono ,
+ synth_4to1_8bit_mono ,
+ synth_ntom_8bit_mono } }
+ };
+
+#ifdef USE_3DNOW
+ static func_dct36 funcs_dct36[2] = {dct36 , dct36_3dnow};
+#endif
+
+ if (0) // ((ai.format & AUDIO_FORMAT_MASK) == AUDIO_FORMAT_8)
+ p8 = 1;
+ fr->synth = funcs[p8][ds];
+ fr->synth_mono = funcs_mono[param.force_stereo?0:1][p8][ds];
+
+#ifdef USE_3DNOW
+ arts_debug("set_synth_functions: 3dnow?");
+ /* check cpuflags bit 31 (3DNow!) and 23 (MMX) */
+ if((param.stat_3dnow < 2) &&
+ ((param.stat_3dnow == 1) ||
+ (getcpuflags() & 0x80800000) == 0x80800000))
+ {
+ fr->synth = funcs[2][ds]; /* 3DNow! optimized synth_1to1() */
+ fr->dct36 = funcs_dct36[1]; /* 3DNow! optimized dct36() */
+ } else {
+ fr->dct36 = funcs_dct36[0];
+ }
+#endif
+
+ if (p8)
+ {
+ make_conv16to8_table(-1); // FIX
+ }
+}
+
+void mpg123PlayObject_impl::initialise_decoder()
+{
+ arts_debug("initializing decoder");
+ set_synth_functions(&mp->fr);
+ make_decode_tables(param.outscale);
+ init_layer2(); // inits also shared tables with layer1
+ init_layer3(mp->fr.down_sample); // No down sample support (yet?)
+}
+
+int mpg123PlayObject_impl::play_frame(int init)
+{
+ struct frame *fr = &mp->fr;
+ int clip;
+ long newrate;
+ long old_rate,old_format,old_channels;
+
+ if(fr->header_change || init) {
+
+ if(fr->header_change > 1 || init) {
+ old_rate = ai.rate;
+ old_format = ai.format;
+ old_channels = ai.channels;
+
+ newrate = (long)param.pitch * (freqs[fr->sampling_frequency]>>(param.down_sample));
+ if(param.verbose && param.pitch != 1.0)
+ fprintf(stderr,"Pitching to %f => %ld Hz\n",param.pitch,newrate);
+
+ fr->down_sample = param.down_sample;
+
+ ai.format = AUDIO_FORMAT_SIGNED_16;
+ ai.rate = 44100;
+ ai.channels = 2;
+
+ /* check, whether the fitter setted our proposed rate */
+ if(ai.rate != newrate) {
+ arts_debug("resampling from %d to %d",newrate, ai.rate);
+ if(ai.rate == (newrate>>1) )
+ fr->down_sample++;
+ else if(ai.rate == (newrate>>2) )
+ fr->down_sample+=2;
+ else {
+ fr->down_sample = 3;
+ fprintf(stderr,"Warning, flexible rate not heavily tested!\n");
+ }
+ if(fr->down_sample > 3)
+ fr->down_sample = 3;
+ }
+
+ if(fr->down_sample > 3)
+ fr->down_sample = 3;
+
+ switch(fr->down_sample) {
+ case 0:
+ case 1:
+ case 2:
+ fr->down_sample_sblimit = SBLIMIT>>(fr->down_sample);
+ break;
+ case 3:
+ {
+ long n = (long)param.pitch * freqs[fr->sampling_frequency];
+ long m = ai.rate;
+
+ synth_ntom_set_step(n,m);
+
+ if(n>m) {
+ fr->down_sample_sblimit = SBLIMIT * m;
+ fr->down_sample_sblimit /= n;
+ }
+ else {
+ fr->down_sample_sblimit = SBLIMIT;
+ }
+ }
+ break;
+ }
+
+ set_synth_functions(fr);
+ //init_output(); XXX: eh?
+ if(ai.rate != old_rate || ai.channels != old_channels ||
+ ai.format != old_format || param.force_reopen) {
+ if(param.force_mono < 0) {
+ if(ai.channels == 1)
+ fr->single = 3;
+ else
+ fr->single = -1;
+ }
+ }
+ else
+ fr->single = param.force_mono;
+
+ param.force_stereo &= ~0x2;
+ if(fr->single >= 0 && ai.channels == 2) {
+ param.force_stereo |= 0x2;
+ }
+
+ set_synth_functions(fr);
+ init_layer3(fr->down_sample_sblimit);
+ // reset_audio(); XXX: wha?
+ if(param.verbose) {
+ if(fr->down_sample == 3) {
+ long n = (long)param.pitch * freqs[fr->sampling_frequency];
+ long m = ai.rate;
+ if(n > m) {
+ fprintf(stderr,"Audio: %2.4f:1 conversion,",(float)n/(float)m);
+ }
+ else {
+ fprintf(stderr,"Audio: 1:%2.4f conversion,",(float)m/(float)n);
+ }
+ }
+ else {
+ fprintf(stderr,"Audio: %ld:1 conversion,",(long)pow(2.0,fr->down_sample));
+ }
+ fprintf(stderr," rate: %ld, encoding: %s, channels: %d\n",ai.rate,audio_encoding_name(ai.format),ai.channels);
+ }
+ }
+ }
+
+ if (fr->error_protection) {
+ /* skip crc, we are byte aligned here */
+ getbyte(&bsi);
+ getbyte(&bsi);
+ }
+
+ /* do the decoding */
+ switch(fr->lay) {
+ case 1:
+ if( (clip=do_layer1(mp,fr,param.outmode,&ai)) < 0 )
+ return 0;
+ break;
+ case 2:
+ if( (clip=do_layer2(mp,fr,param.outmode,&ai)) < 0 )
+ return 0;
+ break;
+ case 3:
+ if( (clip=do_layer3(mp,fr,param.outmode,&ai)) < 0 )
+ return 0;
+ break;
+ default:
+ clip = 0;
+ }
+
+ if(clip > 0 && param.checkrange)
+ fprintf(stderr,"%d samples clipped\n", clip);
+
+ return pcm_point / 4;
+}
+
+mpg123PlayObject_impl::mpg123PlayObject_impl()
+{
+ pcm_buf = new unsigned char[16384*2+1024*2];
+ mp = (struct mpstr *)malloc(sizeof(struct mpstr));
+ memset(mp, 0, sizeof(struct mpstr));
+ //memset(&mp->fr, 0, sizeof(struct frame)); XXX: why would I need to do this?
+
+ prgName = strdup("arts/mpg123");
+ prgVersion = strdup("$Revision$");
+ pcm_point = 0;
+ pcm_sample=pcm_buf; // just point this to our internal buffer
+ memset(&param, 0, sizeof(struct parameter));
+ param.halfspeed = 0;
+ param.outmode = DECODE_BUFFER+999;
+ param.usebuffer = 0;
+ param.down_sample = 0;
+ param.force_stereo = 1; // XXX was 0
+ param.force_mono = -1;
+ param.pitch = 1.0;
+ param.checkrange = 0;
+ param.outscale = 32768;
+ param.tryresync = 2;
+
+ equalfile = NULL;
+ struct shmid_ds bleh;
+ shm_id = shmget(IPC_PRIVATE, sizeof(*shm_buf), 0600);
+ shm_buf = (struct buf_t *)shmat(shm_id, 0, 0);
+ // mark it to be destroyed after the last detach
+ shmctl(shm_id, IPC_RMID, &bleh);
+ // sem0 has base, sem1 remaining space,sem2 seekTo
+ buflen_sem = semget(IPC_PRIVATE, 3, 0600);
+ child_pid = 0;
+}
+
+mpg123PlayObject_impl::~mpg123PlayObject_impl()
+{
+ artsdebug("Destroying PlayObject");
+ halt();
+ union semun semdat;
+ arts_debug("removing IPC resources");
+ semctl(buflen_sem,0,IPC_RMID,semdat);
+ // WABA: Don't remove the cast, it is needed on some platforms.
+ shmdt((char *)shm_buf);
+ delete pcm_buf;
+}
+
+bool mpg123PlayObject_impl::loadMedia(const string &filename)
+{
+// string filename = "http://131.174.33.2:9024/";
+ arts_debug("mpg123: loadMedia %s", filename.c_str());
+ halt(); // stop playing any previous stream
+ arts_debug("previous playback killed");
+ struct sembuf semoper;
+ union semun semdat;
+
+ semoper.sem_flg = 0; // normal blocking semaphores
+
+ semdat.val = 0;
+ if(semctl(buflen_sem,0,SETVAL,semdat)) // no data in the queue
+ arts_debug("couldn't clear queue");
+ semdat.val = 0;
+ if(semctl(buflen_sem,2,SETVAL,semdat)) // seekTo is -1 (ie, no seek)
+ arts_debug("couldn't clear seekTo");
+ semdat.val = BACKBUFSIZ;
+ if(semctl(buflen_sem,1,SETVAL,semdat)) // setup the starting free space
+ arts_debug("couldn't mark buffer empty");
+
+ buf_pos = 0;
+
+ //throw off a process to handle the decoding
+ if((child_pid = fork())) {
+ return true; // all further setup happens in the child
+ }
+ arts_debug("child process");
+ initialise_decoder();
+
+ snprintf(param.filename, 250, filename.c_str());
+ memset(&ai, 0, sizeof(struct audio_info_struct));
+ mp->fr.sampling_frequency = 0;
+ mp->fr.down_sample = 0;
+ mp->fr.single = -1;
+ mp->fr.down_sample_sblimit = SBLIMIT>>(mp->fr.down_sample);
+ sample_freq = freqs[mp->fr.sampling_frequency]>>(param.down_sample);
+
+ // audio_info_struct_init
+ ai.rate = 44100;
+ ai.gain = -1;
+ ai.output = AUDIO_OUT_LINE_OUT;
+ ai.device = 0;
+ ai.channels = 2;
+ ai.format = AUDIO_FORMAT_SIGNED_16;
+ audio_capabilities(&ai);
+
+ set_synth_functions(&mp->fr);
+
+ if (rd)
+ rd->close(rd);
+ if (!open_stream(filename.c_str(), -1)) {
+ printf("erorr opening stream\n");
+ return 0;
+ }
+
+ mpeg_name[0] = 0;
+ snprintf(mpeg_name, 1000, filename.c_str());
+ if (strstr(filename.c_str(), "http://") != NULL) {
+ sprintf(mpeg_name, "ShoutCast from %s\n", filename.c_str());
+ streaming = 1;
+ }
+
+ read_frame_init(&mp->fr);
+
+ XHEADDATA xingHeader;
+
+ shm_buf->pos = 0;
+
+ read_frame(rd,&mp->fr); // read in a frame for the xing code to play with
+ bool gotXing = false;
+ if(!streaming) {
+ gotXing = mpg123_stream_check_for_xing_header(&mp->fr,&xingHeader);
+ if(gotXing)
+ shm_buf->len = xingHeader.frames;
+ else // assume no VBR for non-Xing
+ shm_buf->len = static_cast<unsigned long>(rd->filelen / compute_bpf(&mp->fr));
+ } else {
+ shm_buf->len = 1;
+ }
+ // can't calculate tpf until we reach a non-header frame
+
+ int skipped = 0;
+ if (sync_stream(rd, &mp->fr, 0xffff, &skipped) <= 0) {
+ fprintf(stderr,"Can't find frame start");
+ rd->close(rd);
+ return 0;
+ }
+
+
+/*
+ if (!mpeg_get_frame_info(filename.c_str())) {
+ printf("mpeg_get_frame_info(%s) failed\n", filename.c_str());
+ return 0;
+ }
+*/
+ arts_debug("mpg123: loadMedia %s got %s", filename.c_str(), mpeg_name);
+
+ short *decode_buf = reinterpret_cast<short *>(pcm_sample);
+ bool init=true;
+ do {
+ // get more data
+
+ int seekTo = semctl(buflen_sem, 2, GETVAL, semdat);
+ if (seekTo) { // we need to seek, to sync back up
+ unsigned long offset;
+ arts_debug("seeking to %d\n", seekTo);
+ if(gotXing && (xingHeader.flags & TOC_FLAG) && xingHeader.toc) // do we have a table of contents?
+ offset = mpg123_seek_point(xingHeader.toc,rd->filelen,100 * (seekTo -1) / double(shm_buf->len)); // relative position in file
+ else
+ offset = static_cast<unsigned long>(rd->filelen * ((seekTo-1) / double(shm_buf->len))); // the crude ole' fashioned way
+ rd->rewind(rd);
+ lseek(rd->filept, offset, SEEK_SET);
+
+ // now we need to sync back up :-)
+ read_frame(rd,&mp->fr);
+ read_frame(rd,&mp->fr);
+ // if (sync_stream(rd, &mp->fr, 0xffff, &skipped) <= 0) {
+ // arts_debug("Can't find frame start");
+ // rd->close(rd);
+ // break;
+ // }
+ shm_buf->pos = seekTo; // assume we got the frame we were after? I don't have a better idea...
+ semdat.val = 0;
+ semctl(buflen_sem, 2, SETVAL, semdat); // we've done it
+ }
+
+ if (!read_frame(rd,&mp->fr)) {
+ // mpg123 says we're done, or we errored (in which case we're done)
+ arts_debug("out of frames, exiting");
+ break;
+ }
+
+ if(init) // need to figure this one out with a real audio frame...
+ {
+ arts_debug("samplerate: %d (%d)",mp->fr.sampling_frequency,freqs[mp->fr.sampling_frequency]>>(param.down_sample));
+ shm_buf->tpf = compute_tpf(&mp->fr);
+ }
+ int thisPass = play_frame(init);
+ if(init) // need to figure this one out with a real audio frame...
+ arts_debug("samplerate: %d",mp->fr.sampling_frequency);
+ init=false; // no longer init :-)
+
+ semoper.sem_num = 1;
+ semoper.sem_op = -thisPass;
+ semop(buflen_sem, &semoper, 1);
+
+ // block until there's enough space to stick in this frame
+ int roomFor = semctl(buflen_sem, 1, GETVAL, semdat);
+ if (roomFor > BACKBUFSIZ) {
+ arts_debug("exit requested (%d slots available), bye!",roomFor);
+ // this can never go above BACKBUFSIZ in normal operation,
+ // the consumer wants us to exit
+ break;
+ }
+
+ //arts_debug("decoded %d frames (%d avail)",thisPass,roomFor);
+
+ for(int i=0 ; i <thisPass ;
+ ++i, buf_pos = ((buf_pos + 1) % BACKBUFSIZ)) {
+ shm_buf->left[buf_pos] = conv_16le_float(decode_buf[2*i]);
+ shm_buf->right[buf_pos] = conv_16le_float(decode_buf[2*i+1]);
+ }
+ shm_buf->pos++; // ran another frame through the mill
+ pcm_point=0;
+ //arts_debug("enqueued them");
+ semoper.sem_num = 0;
+ semoper.sem_op = thisPass;
+ semop(buflen_sem,&semoper,1); // mark the additional data now available
+
+ //arts_debug("calculated %d more samples",shm_buf->backbuflen );
+ } while(1);
+
+ //signal completion
+ semdat.val = 0;
+ // no more data available
+ semctl(buflen_sem, 0, SETVAL, semdat);
+ // and no room either (ie, none coming)
+ semctl(buflen_sem, 1, SETVAL, semdat);
+
+ arts_debug("decoder process exiting");
+ exit(0);
+ return true;
+}
+
+string mpg123PlayObject_impl::description()
+{
+ return "mpg123 artsplug - w00t!";
+}
+
+//XXX
+poTime mpg123PlayObject_impl::currentTime()
+{
+ return poTime(shm_buf->pos * shm_buf->tpf, 0, 0, "none");
+}
+
+//XXX
+poTime mpg123PlayObject_impl::overallTime()
+{
+ return poTime(shm_buf->len * shm_buf->tpf, 0, 0, "none");
+}
+
+poCapabilities mpg123PlayObject_impl::capabilities()
+{
+ return static_cast<poCapabilities>(capPause | capSeek);
+}
+
+//YYY
+string mpg123PlayObject_impl::mediaName()
+{
+ return param.filename;
+}
+
+poState mpg123PlayObject_impl::state()
+{
+ return mState;
+}
+
+void mpg123PlayObject_impl::play()
+{
+ arts_debug("mpg123: play");
+ mState = posPlaying;
+}
+
+void mpg123PlayObject_impl::halt()
+{
+ mState = posIdle;
+ union semun semdat;
+
+ if (child_pid) {
+ arts_debug("killing decoder process");
+ semdat.val = 2*BACKBUFSIZ;
+ semctl(buflen_sem, 1, SETVAL, semdat);
+ waitpid(child_pid, NULL, 0);
+ child_pid = 0;
+ }
+ // tell the producer to exit (would also result in us halting, if we weren't already)
+
+ // mainly this is to ensure that the decoder wakes up to notice
+}
+
+//XXX disabled for now
+void mpg123PlayObject_impl::seek(const class poTime &t)
+{
+ union semun foo;
+
+ // this index is one-based so 0 can represent no seek
+ foo.val = static_cast<int>(t.seconds / shm_buf->tpf + 1);
+ arts_debug("requesting seek to %d", foo.val);
+ semctl(buflen_sem, 2, SETVAL, foo); // we've done it
+}
+
+/* Pause implemented on the streaming-side - decoding will simply block on it's own */
+void mpg123PlayObject_impl::pause()
+{
+ mState = posPaused;
+}
+
+/*
+ * SynthModule interface
+ * - where is stop? initialize?
+ */
+
+void mpg123PlayObject_impl::streamInit()
+{
+ arts_debug("streamInit");
+}
+
+void mpg123PlayObject_impl::streamStart()
+{
+ arts_debug("streamStart");
+}
+
+void mpg123PlayObject_impl::calculateBlock(unsigned long samples)
+{
+ int samplesAvailable = 0;
+
+ //arts_debug("calculateBlock");
+
+ if (mState==posPlaying) {
+ //arts_debug("calculateBlock, %d(%d) of %d samples in buffer",
+ //shm_buf->buflen - bufpos, shm_buf->backbuflen,samples);
+
+ struct sembuf bleh;
+
+ bleh.sem_num = 0;
+ bleh.sem_flg = IPC_NOWAIT;
+
+ //arts_debug("%d samples wanted", samplesAvailable);
+ bleh.sem_op = -samples; // does the buffer have sufficient samples?
+ if (semop(buflen_sem, &bleh, 1) == -1) {
+ if (errno == EAGAIN) {
+ union semun semdat;
+ arts_debug("buffer underrun");
+// samplesAvailable = semctl(buflen_sem,0,GETVAL,semdat);
+// if (semctl(buflen_sem, 1, GETVAL, semdat) == 0) {
+// samplesAvailable = semctl(buflen_sem,0,GETVAL,semdat);
+ if ((semctl(buflen_sem, 1, GETVAL, semdat) == 0) && (semctl(buflen_sem,0,GETVAL,semdat) == 0))
+ {
+ arts_debug("decoder requested exit");
+ // no samples AND no room is the decoder's way of signalling completion
+ halt();
+ // samplesAvailable = 0;
+ }
+ samplesAvailable = 0; //
+ } else {
+ arts_debug("something awful happened to our semaphores...");
+ // something awful has happened
+ halt();
+ samplesAvailable = 0;
+ }
+ } else {
+ samplesAvailable = samples; // number of samples we pushed from buffers
+ // used to calculate the number we should zero out for an underrun
+ }
+ bleh.sem_flg = 0; // back to normal now
+
+ if(samplesAvailable) {
+ //arts_debug("%d samples available",samplesAvailable);
+ for (int i = 0; i < samplesAvailable;
+ ++i, buf_pos = ((buf_pos + 1) % BACKBUFSIZ)) {
+
+ left[i] = shm_buf->left[buf_pos];
+ right[i] = shm_buf->right[buf_pos];
+ }
+
+ bleh.sem_num = 1;
+ bleh.sem_op = samplesAvailable;
+ // 0 here CAN block, which is why this is in an if(samplesAvailable)
+ semop(buflen_sem, &bleh, 1); // mark the now-free space
+ }
+ }
+ // zero out any samples we didn't have enough to complete - no buzz of death!
+ while(static_cast<unsigned long>(samplesAvailable) < samples) {
+ left[samplesAvailable] = 0.0;
+ right[samplesAvailable] = 0.0;
+ samplesAvailable++;
+ }
+}
+
+void mpg123PlayObject_impl::streamEnd()
+{
+ arts_debug("streamEnd");
+}
+
+REGISTER_IMPLEMENTATION(mpg123PlayObject_impl);
+
diff --git a/mpg123_artsplugin/mpg123PlayObject_impl.h b/mpg123_artsplugin/mpg123PlayObject_impl.h
new file mode 100644
index 00000000..e8be2619
--- /dev/null
+++ b/mpg123_artsplugin/mpg123PlayObject_impl.h
@@ -0,0 +1,111 @@
+#ifndef MPG123PLAYER_IMPL_H
+#define MPG123PLAYER_IMPL_H "$Id$"
+
+using namespace std;
+
+#if (defined(__GNU_LIBRARY__) && defined(_SEM_SEMUN_UNDEFINED)) || defined(__osf__) || defined(__sun__)
+/* union semun is defined by including <sys/sem.h> */
+/* according to X/OPEN we have to define it ourselves */
+union semun {
+ int val; /* value for SETVAL */
+ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
+ unsigned short int *array; /* array for GETALL, SETALL */
+ struct seminfo *__buf; /* buffer for IPC_INFO */
+};
+#endif
+
+#define RESYNC_FRAMES 3
+
+extern "C" {
+ #include "mpg123/mpg123.h"
+ #include "dxhead.h"
+
+ // These are provided to make mpg123 happy
+ int audio_get_formats(struct audio_info_struct *ai);
+ char *equalfile;
+ struct parameter param;
+ char *prgName;
+ char *prgVersion;
+ struct audio_info_struct ai;
+
+ // We abuse these internal mpg123 objects
+ extern double compute_bpf(struct frame *fr);
+ extern double compute_tpf(struct frame *fr);
+ extern void set_pointer(int, long);
+ extern void audio_capabilities(struct audio_info_struct *);
+ extern unsigned char *pcm_sample;
+ extern int pcm_point;
+ extern int audiobufsize;
+}
+
+#define BACKBUFSIZ 8132
+
+struct id3tag {
+ char tag[3];
+ char title[30];
+ char artist[30];
+ char album[30];
+ char year[4];
+ char comment[30];
+ unsigned char genre;
+};
+
+namespace Arts {
+
+class mpg123PlayObject_impl
+ : public mpg123PlayObject_skel, public StdSynthModule
+{
+ public:
+ mpg123PlayObject_impl();
+ ~mpg123PlayObject_impl();
+ bool loadMedia(const string &filename);
+ string description();
+ poTime currentTime();
+ poTime overallTime();
+ poCapabilities capabilities();
+ string mediaName();
+ poState state();
+ void play();
+ void halt();
+ void seek(const class poTime &t);
+ void pause();
+ void streamInit();
+ void streamStart();
+ void calculateBlock(unsigned long samples);
+ void streamEnd();
+
+ protected:
+ // These are to enable seeking
+ static void stream_jump_to_frame(struct frame *fr, int frame);
+ static int calc_numframes(struct frame *);
+
+ // This is to enable playing, what else?
+ static void set_synth_functions(struct frame *fr);
+ void initialise_decoder();
+ int play_frame(int init);
+
+ inline float conv_16le_float(short x)
+ { return static_cast<float>(x) / 32768.0; }
+
+ int streaming, /*padded, cnt, junk_size, */ sample_freq /*frame_size*/;
+ char mpeg_name[FILENAME_MAX+1];
+ struct mpstr *mp;
+ static int decoder_init;
+
+ int buf_pos; // loops around the circular buffer
+ poState mState;
+ struct buf_t{
+ float left[BACKBUFSIZ];
+ float right[BACKBUFSIZ];
+ unsigned long len; // total frames
+ unsigned long pos; // last decoded frame
+ double tpf; // time per frame, seconds
+ } *shm_buf;
+ int shm_id, child_pid;
+ int buflen_sem;
+ unsigned char *pcm_buf;
+};
+
+};
+
+#endif
diff --git a/mpg123_artsplugin/mpg123arts.idl b/mpg123_artsplugin/mpg123arts.idl
new file mode 100644
index 00000000..f7ff2bc7
--- /dev/null
+++ b/mpg123_artsplugin/mpg123arts.idl
@@ -0,0 +1,12 @@
+#include <kmedia2.idl>
+#include <soundserver.idl>
+
+module Arts
+{
+
+interface mpg123PlayObject : PlayObject, SynthModule
+{
+ out audio stream left,right;
+};
+
+};
diff --git a/noatun/CHANGES b/noatun/CHANGES
new file mode 100644
index 00000000..eb2df008
--- /dev/null
+++ b/noatun/CHANGES
@@ -0,0 +1,4 @@
+* Added assert()'s to Itemlist interface. All client code must adhere
+with the reference semantics of Itemlist: never access it unless you make
+sure that the reference is valid.
+
diff --git a/noatun/COPYING b/noatun/COPYING
new file mode 100644
index 00000000..2c16ee94
--- /dev/null
+++ b/noatun/COPYING
@@ -0,0 +1,26 @@
+All files within Noatun are under the following license, unless otherwise
+stated. All plugins (within the modules directory) carry their own
+license, as read from the .plugin file.
+
+--
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/noatun/FILES b/noatun/FILES
new file mode 100644
index 00000000..b281f414
--- /dev/null
+++ b/noatun/FILES
@@ -0,0 +1,13 @@
+cmodule: Each panel in the settings window
+controls: those widgets that appear in the toolbars TODOLATER
+downloader: unused broken class for getting files
+engine: the only class that communicates to aRts
+klistview_t: like a KListView, but with more functionality, especially DnD TODOSLOWLY
+noatun: all the actions, makes toolbars, e.g., main window
+noatunpref: the main settings box (semi-unused) TODOLATER
+noatunview.cpp: The main view within the main view, which only contains the videoframe
+player: what happens when all the standard player buttons are fondled TODONOW
+playlist: abstraction of a playlist
+playlistview: the window with the playlist TODONOW
+setting: stores all the settings in KConfig
+videoframe: the little box that's supposed to get the video drawn in it
diff --git a/noatun/Makefile.am b/noatun/Makefile.am
new file mode 100644
index 00000000..bb024d36
--- /dev/null
+++ b/noatun/Makefile.am
@@ -0,0 +1,29 @@
+INCLUDES = -I$(kde_includes)/kio -I$(top_srcdir)/noatun/library -I$(kde_includes)/arts $(all_includes)
+
+SUBDIRS = pics library app . modules
+
+KDE_ICON = AUTO
+
+xdg_apps_DATA = noatun.desktop
+
+presetsdir = $(kde_datadir)/noatun/eq.preset
+presets_DATA = preset.dance preset.jazz preset.metal preset.trance preset.zero
+
+updatedir = $(kde_datadir)/kconf_update
+update_DATA = noatun.upd
+update_PROGRAMS = noatun20update
+
+noatun20update_SOURCES = noatun20update.cpp
+noatun20update_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+noatun20update_LDADD = $(LIB_QT)
+
+messages:
+ $(EXTRACTRC) `find . -name "*.rc" -o -name "*.ui"` > rc.cpp
+ $(XGETTEXT) rc.cpp `find . -name "*.cc" -o -name "*.cpp" -o -name "*.h"` -o $(podir)/noatun.pot
+
+api:
+ $(mkinstalldirs) $(top_builddir)/noatun/apidocs/libnoatun
+ if test ! -x $(top_builddir)/noatun/apidocs/common; then \
+ $(LN_S) $(kde_libs_htmldir)/en/common $(top_builddir)/noatun/apidocs/common; \
+ fi
+ doxygen noatun.api
diff --git a/noatun/TODO b/noatun/TODO
new file mode 100644
index 00000000..1bc55de0
--- /dev/null
+++ b/noatun/TODO
@@ -0,0 +1,15 @@
+1) get the command line stuff to work better
+2) variband equalizer:
+
+ y= log(x)/log(22000)*N
+ N : the amount of bands
+
+ perhaps you'll need to invert it
+
+Other CODECs for aRts:
+1) MIDI/timidity for transparent high-quality MID files
+2) SMPEG
+3) SPC
+4) NSF (requires multi-track files)
+5) libmpeg3
+6) rename addFile() to play() in class Playlist and the DCOP interface.
diff --git a/noatun/app/Makefile.am b/noatun/app/Makefile.am
new file mode 100644
index 00000000..59d8dbd2
--- /dev/null
+++ b/noatun/app/Makefile.am
@@ -0,0 +1,11 @@
+INCLUDES = -I$(top_srcdir)/noatun/library -I$(kde_includes)/arts $(all_includes)
+
+kdeinit_LTLIBRARIES = noatun.la
+noatun_la_SOURCES = main.cpp
+noatun_la_LDFLAGS = $(KDE_RPATH) $(all_libraries) -module -avoid-version
+noatun_la_LIBADD = -lqtmcop -lkmedia2_idl -lsoundserver_idl $(top_builddir)/noatun/library/libnoatun.la
+
+bin_PROGRAMS =
+lib_LTLIBRARIES =
+
+METASOURCES = AUTO
diff --git a/noatun/app/main.cpp b/noatun/app/main.cpp
new file mode 100644
index 00000000..86883461
--- /dev/null
+++ b/noatun/app/main.cpp
@@ -0,0 +1,47 @@
+#include <noatun/app.h>
+//#include "joinhandler.h"
+//#include <dcopclient.h>
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+
+static const char description[] = I18N_NOOP("The Fusion of Frequencies");
+static const char version[] = NOATUN_VERSION;
+
+static KCmdLineOptions options[] =
+{
+ { "+[URL]", I18N_NOOP("Files/URLs to open"), 0 },
+ KCmdLineLastOption
+};
+
+extern "C" KDE_EXPORT int kdemain(int argc, char **argv)
+{
+ KAboutData aboutData("noatun", I18N_NOOP("Noatun"),
+ version, description, KAboutData::License_BSD,
+ I18N_NOOP("(c) 2000-2004, The Noatun Developers"),
+ 0, "http://noatun.kde.org");
+ aboutData.addAuthor("Charles Samuels", I18N_NOOP("Noatun Developer"),
+ "charles@kde.org");
+ aboutData.addAuthor("Stefan Westerfeld", I18N_NOOP("Patron of the aRts"),
+ "stefan@space.twc.de");
+ aboutData.addAuthor("Martin Vogt", I18N_NOOP("MPEG Codec and OGG Vorbis Support"),
+ "mvogt@rhrk.uni-kl.de");
+ aboutData.addAuthor("Malte Starostik", I18N_NOOP("Infrared Control Support and HTML playlist export"),
+ "malte@kde.org");
+ aboutData.addAuthor("Nikolas Zimmermann", I18N_NOOP("HTML playlist export and Plugin System"),
+ "wildfox@kde.org");
+ aboutData.addAuthor("Stefan Schimanski", I18N_NOOP("Kaiman Skin Support"),
+ "1Stein@gmx.de");
+ aboutData.addAuthor("Stefan Gehn", I18N_NOOP("Extended K-Jöfol Skin Support, EXTM3U playlist loading"),
+ "metz@gehn.net");
+
+ aboutData.addCredit("Bill Huey", I18N_NOOP("Special help with the equalizer"));
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineArgs::addCmdLineOptions(options);
+ NoatunApp::addCmdLineOptions();
+
+ NoatunApp app;
+ return app.exec();
+}
+
diff --git a/noatun/configure.in.in b/noatun/configure.in.in
new file mode 100644
index 00000000..f8ab00da
--- /dev/null
+++ b/noatun/configure.in.in
@@ -0,0 +1,15 @@
+#MIN_CONFIG
+
+dnl These are common macros that you might or might not want to use
+
+dnl Checks for header files.
+AC_HEADER_DIRENT
+AC_HEADER_STDC
+AC_HEADER_TIME
+AC_CHECK_HEADERS(fcntl.h sys/time.h unistd.h stdlib.h paths.h)
+AC_CHECK_FUNCS(usleep)
+
+if test "x$build_arts" = "xno"; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE noatun"
+fi
+
diff --git a/noatun/hi128-app-noatun.png b/noatun/hi128-app-noatun.png
new file mode 100644
index 00000000..2eb53811
--- /dev/null
+++ b/noatun/hi128-app-noatun.png
Binary files differ
diff --git a/noatun/hi16-app-noatun.png b/noatun/hi16-app-noatun.png
new file mode 100644
index 00000000..c388b4ae
--- /dev/null
+++ b/noatun/hi16-app-noatun.png
Binary files differ
diff --git a/noatun/hi22-app-noatun.png b/noatun/hi22-app-noatun.png
new file mode 100644
index 00000000..d2f30ac4
--- /dev/null
+++ b/noatun/hi22-app-noatun.png
Binary files differ
diff --git a/noatun/hi32-app-noatun.png b/noatun/hi32-app-noatun.png
new file mode 100644
index 00000000..c440d092
--- /dev/null
+++ b/noatun/hi32-app-noatun.png
Binary files differ
diff --git a/noatun/hi48-app-noatun.png b/noatun/hi48-app-noatun.png
new file mode 100644
index 00000000..8ab19a28
--- /dev/null
+++ b/noatun/hi48-app-noatun.png
Binary files differ
diff --git a/noatun/hi64-app-noatun.png b/noatun/hi64-app-noatun.png
new file mode 100644
index 00000000..596ceaa7
--- /dev/null
+++ b/noatun/hi64-app-noatun.png
Binary files differ
diff --git a/noatun/library/Makefile.am b/noatun/library/Makefile.am
new file mode 100644
index 00000000..f3e1dfd9
--- /dev/null
+++ b/noatun/library/Makefile.am
@@ -0,0 +1,57 @@
+INCLUDES = -I$(top_srcdir)/noatun/library/noatun \
+ -I$(top_srcdir)/noatun/library \
+ -I$(top_srcdir)/arts/gui/kde -I$(top_builddir)/arts/gui/kde \
+ -I$(top_srcdir)/arts/gui/common -I$(top_builddir)/arts/gui/common \
+ -I$(kde_includes)/arts \
+ -I$(top_srcdir)/noatun/library/noatunarts \
+ -I$(top_builddir)/noatun/library/noatunarts \
+ $(all_includes)
+
+SUBDIRS= noatunarts . noatuntags noatun
+
+lib_LTLIBRARIES = libnoatun.la libnoatuncontrols.la
+
+libnoatun_la_SOURCES = pluginmodule.cpp cmodule.cpp downloader.cpp engine.cpp \
+ playlist.cpp pref.cpp \
+ player.cpp playlistsaver.cpp app.cpp \
+ pluginloader.cpp plugin.cpp \
+ noatunstdaction.cpp conversion.cpp\
+ noatunui.cpp effectview.cpp \
+ equalizerwidget.ui equalizerview.cpp equalizer.cpp \
+ effects.cpp mimetypetree.cpp stereobuttonaction.cpp ksaver.cpp \
+ video.cpp vequalizer.cpp spline.cpp titleproxy.cpp
+
+include_HEADERS = \
+ cmodule.h plugin_deps.h equalizerview.h effectview.h mimetypetree.h \
+ ksaver.h
+
+libnoatun_la_LDFLAGS = \
+ -version-info 3:0:2 $(KDE_RPATH) $(all_libraries)
+libnoatun_la_LIBADD = $(top_builddir)/arts/gui/common/libartsgui.la \
+ $(top_builddir)/arts/gui/kde/libartsgui_kde.la $(top_builddir)/arts/modules/libartsmodules.la \
+ -lkio -lqtmcop -lkmedia2_idl $(top_builddir)/noatun/library/noatunarts/libnoatunarts.la \
+ -lartsflow -lsoundserver_idl -lartskde $(LIBDL)
+
+libnoatuncontrols_la_SOURCES = controls.cpp scrollinglabel.cpp
+libnoatuncontrols_la_LDFLAGS = -version-info 3:0:2 $(KDE_RPATH) $(all_libraries) -no-undefined
+libnoatuncontrols_la_LIBADD = $(LIBDL) $(LIB_KDEUI)
+
+METASOURCES = AUTO
+META_INCLUDES = $(srcdir)/noatun
+
+noinst_PROGRAMS = gentable
+gentable_SOURCES = gentable.cpp
+gentable_LDFLAGS = $(KDE_EXTRA_RPATH)
+
+magictable: gentable
+ $(top_builddir)/noatun/library/gentable > magictable
+
+magictabledir = $(kde_datadir)/noatun
+magictable_DATA = magictable
+
+effects.lo: noatunarts/noatunarts.h ../../arts/gui/common/artsgui.h
+engine.lo: noatunarts/noatunarts.h
+equalizer.lo: noatunarts/noatunarts.h
+plugin.lo: noatunarts/noatunarts.h
+vequalizer.lo: noatunarts/noatunarts.h
+
diff --git a/noatun/library/app.cpp b/noatun/library/app.cpp
new file mode 100644
index 00000000..04167fe7
--- /dev/null
+++ b/noatun/library/app.cpp
@@ -0,0 +1,530 @@
+#include "cmodule.h"
+#include "downloader.h"
+#include "effects.h"
+#include "effectview.h"
+#include "engine.h"
+#include "equalizer.h"
+#include "equalizerview.h"
+#include "vequalizer.h"
+#include "app.h"
+#include "playlist.h"
+#include "pref.h"
+#include "player.h"
+#include "plugin.h"
+#include "pluginloader.h"
+#include "globalvideo.h"
+#include "stdaction.h"
+
+#include <common.h>
+#include <dcopobject.h>
+#include <dispatcher.h>
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kfiledialog.h>
+#include <kglobal.h>
+#include <klibloader.h>
+#include <klocale.h>
+#include <kmainwindow.h>
+#include <kmessagebox.h>
+#include <knotifyclient.h>
+#include <kpopupmenu.h>
+#include <qfile.h>
+#include <qimage.h>
+#include <qiomanager.h>
+#include <qsessionmanager.h>
+#include <qtextstream.h>
+#include <signal.h>
+#include <kmimetype.h>
+
+
+using std::string;
+using std::vector;
+
+// forgot the d pointer in Player TODO
+static GlobalVideo *globalVideo;
+struct NoatunApp::Private
+{
+ Effects *effects;
+ VEqualizer *vequalizer;
+};
+
+
+NoatunApp::NoatunApp()
+ : KUniqueApplication(true, true, true), mPluginMenu(0), mPluginActionMenu(0), mEqualizer(0)
+{
+ d = new Private;
+ d->vequalizer=0;
+ d->effects=0;
+
+ Visualization::internalVis=true;
+
+ mDownloader=new Downloader;
+
+ Visualization::initDispatcher(); // 316
+
+ showingInterfaces = true;
+
+ // set the default config data
+ // TODO: Maybe a first time wizard instead?
+ KConfig *config=KGlobal::config(); // +
+ config->setGroup(QString::null); // 1
+ if (!config->readEntry("Modules").length())
+ {
+ QStringList modules;
+ modules.append("excellent.plugin");
+ modules.append("splitplaylist.plugin");
+ modules.append("marquis.plugin");
+ modules.append("systray.plugin");
+ modules.append("metatag.plugin");
+ config->writeEntry("Modules", modules);
+ config->sync();
+ } // 1
+
+ mPref=new NoatunPreferences(0L); // 115
+ mPref->hide(); // 1
+ mLibraryLoader = new LibraryLoader; // 0
+
+ mLibraryLoader->add("dcopiface.plugin");
+
+ new General(this); // 25
+ new Plugins(this); // 149
+// new Types(this);
+
+ mPlayer=new Player; // 139
+ d->effects=new Effects; // 1
+ d->vequalizer = new VEqualizer;
+ d->vequalizer->init();
+ mEqualizer=new Equalizer; // 0
+ mEqualizer->init(); // 41
+ mEffectView=new EffectView; // 859
+ mEqualizerView=new EqualizerView; // 24
+
+ QTimer::singleShot(0, mDownloader, SLOT(start()));
+
+ ::globalVideo = new GlobalVideo;
+
+ if(isRestored())
+ {
+ mLibraryLoader->add("marquis.plugin");
+ static_cast<SessionManagement*>(mLibraryLoader->plugins().first())->restore();
+ }
+ else
+ {
+ loadPlugins(); // 1531
+ if (!playlist())
+ {
+ KMessageBox::error(0,i18n("No playlist plugin was found. " \
+ "Please make sure that Noatun was installed correctly."));
+ KApplication::quit();
+ delete this;
+ }
+ else
+ {
+ config->setGroup(QString::null); // 0
+ player()->setVolume(config->readNumEntry("Volume", 100)); // 10
+ player()->loop(config->readNumEntry("LoopStyle", (int)Player::None));
+ mPlayer->engine()->setInitialized(); // 0
+
+ switch (startupPlayMode())
+ {
+ case Restore:
+ restoreEngineState();
+ break;
+ case Play:
+ player()->play();
+ break;
+ case DontPlay:
+ default:
+ break;
+ }
+ }
+ }
+}
+
+NoatunApp::~NoatunApp()
+{
+ saveEngineState();
+ KConfig *config = KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("Volume", player()->volume());
+ config->writeEntry("LoopStyle", player()->loopStyle());
+ // for version continuity in the future
+ config->writeEntry("Version", QString(version())); // 1
+ config->sync(); // 40
+
+ mPlayer->stop();
+ delete ::globalVideo;
+ delete mLibraryLoader;
+ delete mEffectView;
+ delete mDownloader;
+ delete mEqualizer;
+ delete d->vequalizer;
+ delete mEqualizerView;
+ delete d->effects;
+
+ delete mPlayer;
+ delete mPref;
+ config->sync();
+
+ delete d;
+}
+
+QCString NoatunApp::version() const
+{
+ return aboutData()->version().ascii();
+}
+
+inline bool logicalXOR(bool A, bool B)
+{
+ return (A || B) && !(A && B);
+}
+
+#define READBOOLOPT_EX(name, string, def, group, reversal) \
+bool NoatunApp::name() const \
+{ \
+ KConfig *config=KGlobal::config(); \
+ config->setGroup(group); \
+ return logicalXOR(config->readBoolEntry(string, def), reversal); \
+}
+
+#define READBOOLOPT(name, string, def) \
+ READBOOLOPT_EX(name, string, def, "", false)
+
+
+READBOOLOPT(autoPlay, "AutoPlay", false)
+READBOOLOPT(loopList, "LoopList", true)
+READBOOLOPT(hackUpPlaylist, "HackUpPlaylist", true)
+READBOOLOPT(fastMixer, "FastMixer", false)
+READBOOLOPT(displayRemaining, "DisplayRemaining", false)
+READBOOLOPT(clearOnOpen, "ClearOnOpen", false)
+READBOOLOPT_EX(oneInstance, "MultipleInstances", true, "KDE", true)
+
+#undef READBOOLOPT
+#undef READBOOLOPT_EX
+
+bool NoatunApp::clearOnStart() const
+{
+ return clearOnOpen();
+}
+
+int NoatunApp::startupPlayMode() const
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ return config->readNumEntry("StartupPlayMode", autoPlay() ? Play : Restore);
+}
+
+void NoatunApp::setStartupPlayMode(int mode)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("StartupPlayMode", mode);
+ config->sync();
+}
+
+void NoatunApp::setHackUpPlaylist(bool b)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("HackUpPlaylist", b);
+ config->sync();
+}
+
+void NoatunApp::setFastMixer(bool b)
+{
+ bool whatBefore=fastMixer();
+ if (whatBefore!=b)
+ {
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("FastMixer", b);
+ config->sync();
+ player()->engine()->useHardwareMixer(b);
+ }
+}
+
+void NoatunApp::setOneInstance(bool b)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup("KDE");
+ config->writeEntry("MultipleInstances", !b);
+ config->sync();
+}
+
+void NoatunApp::setLoopList(bool b)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("LoopList", b);
+ KGlobal::config()->sync();
+}
+
+void NoatunApp::setAutoPlay(bool b)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("AutoPlay", b);
+ KGlobal::config()->sync();
+}
+
+void NoatunApp::setRememberPositions(bool b)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("RememberPositions", b);
+ KGlobal::config()->sync();
+}
+
+void NoatunApp::setSaveDirectory(const QString &s)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writePathEntry("SaveDirectory", s);
+ config->sync();
+}
+
+QString NoatunApp::saveDirectory() const
+{
+ KConfig *c=KGlobal::config();
+ c->setGroup(QString::null);
+ return c->readPathEntry("SaveDirectory", QString(getenv("HOME")));
+}
+
+QString NoatunApp::titleFormat() const
+{
+ KConfig *c=KGlobal::config();
+ c->setGroup(QString::null);
+ return c->readEntry("TitleFormat", "$(\"[\"author\"] - \")$(title)$(\" (\"bitrate\"kbps)\")");
+}
+
+void NoatunApp::setTitleFormat(const QString &format)
+{
+ KConfig *c=KGlobal::config();
+ c->setGroup(QString::null);
+ return c->writeEntry("TitleFormat", format);
+}
+
+void NoatunApp::setClearOnStart(bool b)
+{
+ setClearOnOpen(b);
+}
+
+void NoatunApp::setClearOnOpen(bool b)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("ClearOnOpen", b);
+ config->sync();
+}
+
+void NoatunApp::setDisplayRemaining(bool b)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("DisplayRemaining", b);
+ config->sync();
+}
+
+void NoatunApp::toggleInterfaces()
+{
+ showingInterfaces ^= true;
+
+ if(showingInterfaces)
+ emit showYourself();
+ else
+ emit hideYourself();
+}
+
+void NoatunApp::showInterfaces()
+{
+ showingInterfaces = true;
+ emit showYourself();
+}
+
+void NoatunApp::hideInterfaces()
+{
+ showingInterfaces = false;
+ emit hideYourself();
+}
+
+
+int NoatunApp::pluginMenuAdd(const QString &text, const QObject *receiver, const char *member)
+{
+ return pluginActionMenu()->menuAdd(text, receiver, member);
+}
+
+void NoatunApp::pluginMenuRemove(int id)
+{
+ pluginActionMenu()->menuRemove(id);
+}
+
+NoatunStdAction::PluginActionMenu *NoatunApp::pluginActionMenu()
+{
+ if (!mPluginActionMenu)
+ mPluginActionMenu = new NoatunStdAction::PluginActionMenu(this, "menu_actions");
+ return mPluginActionMenu;
+}
+
+KPopupMenu *NoatunApp::pluginMenu()
+{
+ if(!mPluginMenu)
+ mPluginMenu = pluginActionMenu()->popupMenu();
+ return mPluginMenu;
+}
+
+int NoatunApp::newInstance()
+{
+ // TODO, this should call playlist()->handleArguments();
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ bool clear = clearOnOpen();
+ bool playme=true;
+
+ for (int i=0; i<args->count(); i++)
+ {
+ player()->openFile(args->url(i), clear, playme);
+ playme=false;
+ clear=false;
+ }
+
+ args->clear();
+ return 0;
+}
+
+void NoatunApp::preferences()
+{
+ mPref->show();
+ mPref->raise();
+}
+
+void NoatunApp::quit()
+{
+ KApplication::quit();
+}
+
+void NoatunApp::fileOpen()
+{
+ KURL::List files = KFileDialog::getOpenURLs(":mediadir", mimeTypes(), 0,
+ i18n("Select File to Play"));
+
+ if (files.count())
+ mPlayer->openFile(files, clearOnOpen(), true);
+}
+
+void NoatunApp::effectView()
+{
+ mEffectView->show();
+}
+
+void NoatunApp::equalizerView()
+{
+ mEqualizerView->show();
+}
+
+VEqualizer * NoatunApp::vequalizer()
+{
+ return d->vequalizer;
+}
+
+Effects *NoatunApp::effects() const
+{
+ return d->effects;
+}
+
+QString NoatunApp::mimeTypes()
+{
+ QString mimeTypes;
+ Arts::TraderQuery q;
+ vector<Arts::TraderOffer> *results = q.query();
+ vector<Arts::TraderOffer>::iterator i;
+
+ for (i=results->begin(); i != results->end(); i++)
+ {
+ vector<string> *prop = (*i).getProperty("MimeType");
+
+ vector<string>::iterator istr;
+ for (istr = prop->begin(); istr != prop->end(); istr++)
+ {
+ if ( !(*istr).length())
+ continue;
+
+ const char *m = (*istr).c_str();
+ if ((KServiceType::serviceType(m)) && !mimeTypes.contains(m))
+ {
+ mimeTypes += m;
+ mimeTypes += ' ';
+ }
+ }
+ delete prop;
+ }
+ delete results;
+ return mimeTypes;
+}
+
+void NoatunApp::loadPlugins()
+{
+ mLibraryLoader->loadAll();
+ // backup in case of trouble
+ if(!mLibraryLoader->playlist())
+ {
+ kdDebug(66666) << k_funcinfo << "NO playlist plugin found!" << endl;
+ //mLibraryLoader->add("splitplaylist.plugin");
+ }
+}
+
+Playlist *NoatunApp::playlist() const
+{
+ return mLibraryLoader->playlist();
+}
+
+void NoatunApp::commitData(QSessionManager &)
+{
+}
+
+void NoatunApp::saveState(QSessionManager &sm)
+{
+ QStringList restartCommand = sm.restartCommand();
+ sm.setRestartCommand( restartCommand );
+
+ KApplication::saveState(sm);
+}
+
+// Deprecated
+QImage NoatunApp::readPNG(const QString &filename)
+{
+ QImageIO file(filename, "PNG");
+ file.setGamma(0.0);
+ file.read();
+ return file.image();
+}
+
+void NoatunApp::restoreEngineState()
+{
+ KConfig* config = KGlobal::config();
+ config->setGroup(QString::null);
+ int state = config->readNumEntry("EngineState", Arts::posPlaying);
+ switch (state)
+ {
+ case Arts::posPlaying:
+ player()->play();
+ break;
+ case Arts::posPaused:
+ if (player()->isPlaying())
+ player()->playpause();
+ break;
+ case Arts::posIdle:
+ default:
+ break;
+ }
+}
+
+void NoatunApp::saveEngineState()
+{
+ KConfig* config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("EngineState", player()->engine()->state());
+ // we don't sync here since it's done in the destructor afterwards anyway
+}
+
+#include "app.moc"
diff --git a/noatun/library/cmodule.cpp b/noatun/library/cmodule.cpp
new file mode 100644
index 00000000..a624fb64
--- /dev/null
+++ b/noatun/library/cmodule.cpp
@@ -0,0 +1,131 @@
+#include <cmodule.h>
+#include <noatun/pluginloader.h>
+#include <common.h>
+#include <noatun/app.h>
+
+#include <qpushbutton.h>
+#include <qbuttongroup.h>
+#include <qradiobutton.h>
+#include <qcheckbox.h>
+#include <qlayout.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <klistview.h>
+#include <qsplitter.h>
+#include <qlabel.h>
+#include <qdragobject.h>
+#include <kurlrequester.h>
+#include <kfiledialog.h>
+#include <kdialog.h>
+#include <klineedit.h>
+
+#include <qtextview.h>
+#include <qwhatsthis.h>
+
+#include "mimetypetree.h"
+
+/*****************************************************************
+ * General options
+ *****************************************************************/
+
+General::General(QObject *parent)
+ : CModule(i18n("General"), i18n("General Options"), "configure", parent)
+{
+ mLoopList=new QCheckBox(i18n("&Return to start of playlist on finish"), this);
+ mLoopList->setChecked(napp->loopList());
+ QWhatsThis::add(mLoopList, i18n("When the playlist is finished playing, return to the start, but do not start playing."));
+
+ mOneInstance=new QCheckBox(i18n("Allow only one &instance of Noatun"), this);
+ mOneInstance->setChecked(napp->oneInstance());
+ QWhatsThis::add(mOneInstance, i18n("Starting noatun a second time will cause it to just append items from the start to the current instance."));
+
+ mClearOnOpen = new QCheckBox(i18n("Clear playlist &when opening a file"), this);
+ mClearOnOpen->setChecked(napp->clearOnOpen());
+ QWhatsThis::add(mClearOnOpen, i18n("Opening a file with the global Open menu item will clear the playlist first."));
+
+ mFastVolume=new QCheckBox(i18n("&Use fast hardware volume control"), this);
+ mFastVolume->setChecked(napp->fastMixer());
+ QWhatsThis::add(mFastVolume, i18n("Use the hardware mixer instead of aRts'. It affects all streams, not just Noatun's, but is a little faster."));
+
+ mRemaining=new QCheckBox(i18n("Display &remaining play time"), this);
+ mRemaining->setChecked(napp->displayRemaining());
+ QWhatsThis::add(mRemaining, i18n("Counters count down towards zero, showing remaining time instead of elapsed time."));
+
+ QLabel *titleLabel=new QLabel(i18n("Title &format:"), this);
+ mTitleFormat=new KLineEdit(this);
+ titleLabel->setBuddy(mTitleFormat);
+ mTitleFormat->setText(napp->titleFormat());
+ QWhatsThis::add(mTitleFormat, i18n(
+ "Select a title to use for each file (in the playlist and user interface). "
+ "Each element such as $(title) is replaced with the property with the name "
+ "as given in the parentheses. The properties include, but are not limited to: "
+ "title, author, date, comments and album."));
+
+ QLabel *dlsaver=new QLabel(i18n("&Download folder:"), this);
+ mDlSaver=new KURLRequester(napp->saveDirectory(), this);
+ dlsaver->setBuddy(mDlSaver);
+ connect( mDlSaver, SIGNAL( openFileDialog( KURLRequester * )),
+ this, SLOT( slotRequesterClicked( KURLRequester * )));
+ QWhatsThis::add(mDlSaver, i18n("When opening a non-local file, download it to the selected folder."));
+
+ mPlayOnStartup = new QButtonGroup(1, Horizontal, i18n("Play Behavior on Startup"), this);
+ mPlayOnStartup->setExclusive(true);
+ mPlayOnStartup->insert(
+ new QRadioButton(i18n("Restore &play state"), mPlayOnStartup),
+ NoatunApp::Restore
+ );
+ mPlayOnStartup->insert(
+ new QRadioButton(i18n("Automatically play &first file"), mPlayOnStartup),
+ NoatunApp::Play
+ );
+ mPlayOnStartup->insert(
+ new QRadioButton(i18n("&Do not start playing"), mPlayOnStartup),
+ NoatunApp::DontPlay
+ );
+
+ if (QButton* b = mPlayOnStartup->find(napp->startupPlayMode()))
+ {
+ b->toggle();
+ }
+
+ QGridLayout *layout = new QGridLayout(this, 0, KDialog::spacingHint());
+ layout->setSpacing(KDialog::spacingHint());
+
+ layout->addMultiCellWidget(mLoopList, 0, 0, 0, 1);
+ layout->addMultiCellWidget(mOneInstance, 2, 2, 0, 1);
+ layout->addMultiCellWidget(mClearOnOpen, 4, 4, 0, 1);
+ layout->addMultiCellWidget(mFastVolume, 5, 5, 0, 1);
+ layout->addMultiCellWidget(mRemaining, 6, 6, 0, 1);
+
+ layout->addWidget(titleLabel, 7, 0);
+ layout->addWidget(mTitleFormat, 7, 1);
+
+ layout->addWidget(dlsaver, 8, 0);
+ layout->addWidget(mDlSaver, 8, 1);
+
+ layout->addMultiCellWidget(mPlayOnStartup, 9, 9, 0, 1);
+
+ layout->setRowStretch(10, 1);
+}
+
+
+void General::save()
+{
+ napp->setLoopList(mLoopList->isChecked());
+ napp->setOneInstance(mOneInstance->isChecked());
+ napp->setClearOnOpen(mClearOnOpen->isChecked());
+ napp->setSaveDirectory(mDlSaver->url());
+ napp->setFastMixer(mFastVolume->isChecked());
+ napp->setTitleFormat(mTitleFormat->text());
+ napp->setDisplayRemaining(mRemaining->isChecked());
+ napp->setStartupPlayMode(mPlayOnStartup->selectedId());
+}
+
+
+void General::slotRequesterClicked( KURLRequester * )
+{
+ mDlSaver->fileDialog()->setMode(
+ (KFile::Mode)(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly));
+}
+
+#include "cmodule.moc"
diff --git a/noatun/library/cmodule.h b/noatun/library/cmodule.h
new file mode 100644
index 00000000..6e0e1179
--- /dev/null
+++ b/noatun/library/cmodule.h
@@ -0,0 +1,43 @@
+#ifndef CMODULE_H
+#define CMODULE_H
+
+#include <qframe.h>
+#include <klistview.h>
+#include "noatun/pref.h"
+
+class KListView;
+class QSplitter;
+class QListViewItem;
+class NoatunLibraryInfo;
+class QTextView;
+class QButtonGroup;
+class MimeTypeTree;
+class KLineEdit;
+
+namespace Arts {class TraderOffer;}
+
+class QCheckBox;
+class KURLRequester;
+
+class General : public CModule
+{
+Q_OBJECT
+public:
+ General(QObject *parent=0);
+ virtual void save();
+
+private slots:
+ void slotRequesterClicked( KURLRequester * );
+
+private:
+ QCheckBox *mLoopList, *mOneInstance, *mRememberPositions,
+ *mClearOnOpen, *mFastVolume, *mRemaining;
+ QButtonGroup* mPlayOnStartup;
+ KURLRequester *mDlSaver;
+ KLineEdit *mTitleFormat;
+};
+
+// I'm too lazy to grep - Neil
+#include "pluginmodule.h"
+
+#endif
diff --git a/noatun/library/controls.cpp b/noatun/library/controls.cpp
new file mode 100644
index 00000000..1fb08269
--- /dev/null
+++ b/noatun/library/controls.cpp
@@ -0,0 +1,114 @@
+#include <noatun/controls.h>
+
+L33tSlider::L33tSlider(QWidget * parent, const char * name) :
+ QSlider(parent,name), pressed(false)
+{}
+L33tSlider::L33tSlider(Orientation o, QWidget * parent, const char * name) :
+ QSlider(o,parent,name), pressed(false)
+{}
+L33tSlider::L33tSlider(int minValue, int maxValue, int pageStep, int value,
+ Orientation o, QWidget * parent, const char * name) :
+ QSlider(minValue, maxValue, pageStep, value, o, parent,name), pressed(false)
+{}
+
+bool L33tSlider::currentlyPressed() const
+{
+ return pressed;
+}
+
+void L33tSlider::setValue(int i)
+{
+ if (!pressed)
+ QSlider::setValue(i);
+}
+
+void L33tSlider::mousePressEvent(QMouseEvent*e)
+{
+ if (e->button()!=RightButton)
+ {
+ pressed=true;
+ QSlider::mousePressEvent(e);
+ }
+}
+
+void L33tSlider::mouseReleaseEvent(QMouseEvent*e)
+{
+ pressed=false;
+ QSlider::mouseReleaseEvent(e);
+ emit userChanged(value());
+}
+
+void L33tSlider::wheelEvent(QWheelEvent *e)
+{
+ QSlider::wheelEvent(e);
+ int newValue=value() /* +e->delta()/120 */;
+ if (newValue<minValue())
+ newValue=minValue();
+ else if (newValue>maxValue())
+ newValue=maxValue();
+ setValue(newValue);
+ emit userChanged(newValue);
+}
+
+
+SliderAction::SliderAction(const QString& text, int accel, const QObject *receiver,
+ const char *member, QObject* parent, const char* name )
+ : KAction( text, accel, parent, name )
+{
+ m_receiver = receiver;
+ m_member = member;
+}
+
+int SliderAction::plug( QWidget *w, int index )
+{
+ if (!w->inherits("KToolBar")) return -1;
+
+ KToolBar *toolBar = (KToolBar *)w;
+ int id = KAction::getToolButtonID();
+
+ //Create it.
+ m_slider=new L33tSlider(0, 1000, 100, 0, Horizontal, toolBar);
+ m_slider->setMinimumWidth(10);
+ toolBar->insertWidget(id, 10, m_slider, index );
+
+
+ addContainer( toolBar, id );
+ connect( toolBar, SIGNAL( destroyed() ), this, SLOT( slotDestroyed() ) );
+ toolBar->setItemAutoSized( id, true );
+
+ if (w->inherits( "KToolBar" ))
+ connect(toolBar, SIGNAL(moved(KToolBar::BarPosition)), this, SLOT(toolbarMoved(KToolBar::BarPosition)));
+
+ emit plugged();
+
+ return containerCount() - 1;
+}
+
+void SliderAction::toolbarMoved(KToolBar::BarPosition)
+{
+// I wish this worked :)
+return;
+/*
+ if (pos == KToolBar::Left || pos == KToolBar::Right)
+ {
+ m_slider->setOrientation(Vertical);
+ m_slider->setFixedWidth(m_slider->height());
+ }
+ else
+ {
+ m_slider->setOrientation(Horizontal);
+ m_slider->resize(m_slider->height(), m_slider->height());
+ }
+*/
+}
+
+void SliderAction::unplug( QWidget *w )
+{
+ KToolBar *toolBar = (KToolBar *)w;
+ int idx = findContainer( w );
+
+ toolBar->removeItem( menuId( idx ) );
+ removeContainer( idx );
+}
+
+#include "controls.moc"
diff --git a/noatun/library/conversion.cpp b/noatun/library/conversion.cpp
new file mode 100644
index 00000000..791b5554
--- /dev/null
+++ b/noatun/library/conversion.cpp
@@ -0,0 +1,149 @@
+#include "noatun/conversion.h"
+
+// inherit the aRts routines
+#include <convert.h>
+
+#include <config.h>
+#include <limits.h>
+
+namespace Conversion
+{
+
+void convertMono8ToFloat(unsigned long samples, unsigned char *from, float *to)
+{
+ Arts::convert_mono_8_float(samples, from, to);
+}
+
+void interpolateMono8ToFloat(unsigned long samples, double start, double speed,
+ unsigned char *from, float *to)
+{
+ Arts::interpolate_mono_8_float(samples, start, speed, from, to);
+}
+
+void convertMono16leToFloat(unsigned long samples, unsigned char *from, float *to)
+{
+ Arts::convert_mono_16le_float(samples, from, to);
+}
+
+void interpolateMono16leToFloat(unsigned long samples, double startpos, double speed,
+ unsigned char *from, float *to)
+{
+ Arts::interpolate_mono_16le_float(samples, startpos, speed, from, to);
+}
+
+void convertStereoI8To2Float(unsigned long samples, unsigned char *from,
+ float *left, float *right)
+{
+ Arts::convert_stereo_i8_2float(samples, from, left, right);
+}
+
+
+void interpolateStereoI8To2Float(unsigned long samples, double startpos, double speed,
+ unsigned char *from, float *left, float *right)
+{
+ Arts::interpolate_stereo_i8_2float(samples, startpos, speed, from, left, right);
+}
+
+void convertStereoI16leTo2Float(unsigned long samples, unsigned char *from, float *left,
+ float *right)
+{
+ Arts::convert_stereo_i16le_2float(samples, from, left, right);
+}
+
+void interpolateStereoI16leTo2Float(unsigned long samples, double startpos, double speed,
+ unsigned char *from, float *left, float *right)
+{
+ Arts::interpolate_stereo_i16le_2float(samples, startpos, speed, from, left, right);
+}
+
+void interpolateMonoFloatToFloat(unsigned long samples, double startpos, double speed,
+ float *from, float *to)
+{
+ Arts::interpolate_mono_float_float( samples, startpos, speed, from, to);
+}
+
+void convertStereoIFloatTo2Float(unsigned long samples, float *from, float *left,
+ float *right)
+{
+ Arts::convert_stereo_ifloat_2float(samples, from, left, right);
+}
+
+void interpolateStereoIFloatTo2Float(unsigned long samples, double startpos,
+ double speed, float *from, float *left,
+ float *right)
+{
+ Arts::interpolate_stereo_ifloat_2float(samples, startpos, speed, from, left, right);
+}
+
+void convertMonoFloatTo16le(unsigned long samples, float *from, unsigned char *to)
+{
+ Arts::convert_mono_float_16le(samples, from, to);
+}
+
+void convertStereo2FloatToI16le(unsigned long samples, float *left, float *right,
+ unsigned char *to)
+{
+ Arts::convert_stereo_2float_i16le(samples, left, right, to);
+}
+
+void convertMonoFloatTo8(unsigned long samples, float *from, unsigned char *to)
+{
+ Arts::convert_mono_float_8(samples, from, to);
+}
+
+void convertStereo2FloatToI8(unsigned long samples, float *left, float *right,
+ unsigned char *to)
+{
+ Arts::convert_stereo_2float_i8(samples, left, right, to);
+}
+
+inline void toLittleEndian(unsigned long length, char *buffer)
+{
+#ifdef WORDS_BIGENDIAN
+ swapEndian(length, buffer);
+#else
+ (void)length;
+ (void)buffer;
+#endif
+}
+
+inline void toBigEndian(unsigned long length, char *buffer)
+{
+#ifndef WORDS_BIGENDIAN
+ swapEndian(length, buffer);
+#else
+ (void)length;
+ (void)buffer;
+#endif
+}
+
+void swapEndian(unsigned long length, char *buffer)
+{
+ // if you use a little-endian non intel box, and the ASM
+ // version doesn't work, it's safe to use the C version
+#ifdef __i386__
+ __asm__(
+ "shrl $1,%0\n"
+ "jz .l2\n"
+ ".l1:\n"
+ "rolw $8,(%1)\n"
+ "incl %1\n"
+ "incl %1\n"
+ "decl %0\n"
+ "jnz .l1\n"
+ ".l2:\n"
+ : : "r" (length), "r" (buffer));
+#else
+ while (length--)
+ {
+ register char c=*(buffer+1);
+ *(buffer+1)=*buffer;
+ *(buffer)=c;
+ buffer++; buffer++;
+ --length;
+ }
+#endif
+}
+
+}
+
diff --git a/noatun/library/downloader.cpp b/noatun/library/downloader.cpp
new file mode 100644
index 00000000..734f5e11
--- /dev/null
+++ b/noatun/library/downloader.cpp
@@ -0,0 +1,236 @@
+#include <noatun/downloader.h>
+#include <noatun/app.h>
+#include <assert.h>
+#include <qfile.h>
+#include <qtimer.h>
+#include <kio/job.h>
+#include <klocale.h>
+
+DownloadItem::DownloadItem()
+{
+
+}
+
+DownloadItem::~DownloadItem()
+{
+ dequeue();
+}
+
+bool DownloadItem::isDownloaded() const
+{
+ return true;
+}
+
+QString DownloadItem::localFilename() const
+{
+ return mLocalFilename;
+}
+
+void DownloadItem::setLocalFilename(const QString &filename)
+{
+ mLocalFilename=filename;
+}
+
+void DownloadItem::downloadFinished()
+{
+}
+
+void DownloadItem::downloaded(int )
+{
+}
+
+void DownloadItem::downloadTimeout()
+{
+}
+
+bool DownloadItem::enqueue(const KURL &url)
+{
+ if (url.isLocalFile())
+ {
+ setLocalFilename(url.path());
+ return false;
+ }
+ else
+ {
+ napp->downloader()->enqueue(this, url);
+ return true;
+ }
+}
+
+void DownloadItem::dequeue()
+{
+ napp->downloader()->dequeue(this);
+}
+
+
+
+
+Downloader::Downloader(QObject *parent)
+ : QObject(parent), localfile(0), current(0), mJob(0), mTimeout(0)
+{
+ mStarted=false;
+ mUnstartedQueue=new QPtrList<Downloader::QueueItem>;
+}
+
+Downloader::~Downloader()
+{
+
+}
+
+void Downloader::start()
+{
+ assert(!mStarted);
+ mStarted=true;
+ if (current)
+ emit enqueued(current->notifier, current->file);
+
+ for (QPtrListIterator<Downloader::QueueItem> i(*mUnstartedQueue); i.current(); ++i)
+ {
+ (*i)->notifier->mLocalFilename = (*i)->local;
+ mQueue.append(*i);
+ emit enqueued((*i)->notifier, (*i)->file);
+ }
+
+ delete mUnstartedQueue;
+ mUnstartedQueue=0;
+ QTimer::singleShot(0, this, SLOT(getNext()));
+}
+
+static QString nonExistantFile(const QString &file)
+{
+ if (file.right(1)=="/") return i18n("Unknown");
+ int i=0;
+ QString f(file);
+ while (QFile(f).exists())
+ {
+ i++;
+ f=file;
+ f.insert(f.findRev('.'), '_'+QString::number(i));
+ }
+ return f;
+}
+
+QString Downloader::enqueue(DownloadItem *notifier, const KURL &file)
+{
+ if (file.isLocalFile()) return 0;
+ QueueItem *i=new QueueItem;
+ i->notifier=notifier;
+ i->file=file;
+
+ if (!mStarted)
+ {
+ i->local=notifier->mLocalFilename;
+ if (!notifier->localFilename().length())
+ {
+ i->local =
+ nonExistantFile(napp->saveDirectory()+'/'+file.fileName());
+ }
+ mUnstartedQueue->append(i);
+ return i->local;
+ }
+
+
+ if (!notifier->localFilename().length())
+ {
+ notifier->mLocalFilename=
+ i->local =
+ nonExistantFile(napp->saveDirectory()+'/'+file.fileName());
+ }
+ else
+ {
+ i->local = notifier->mLocalFilename;
+ }
+
+ mQueue.append(i);
+ QTimer::singleShot(0, this, SLOT(getNext()));
+ emit enqueued(notifier, file);
+ return i->local;
+}
+
+void Downloader::dequeue(DownloadItem *notifier)
+{
+ if (current && notifier==current->notifier)
+ {
+ mJob->kill();
+ jobDone(mJob);
+ return;
+ }
+ for (QPtrListIterator<Downloader::QueueItem> i(mQueue); i.current(); ++i)
+ {
+ if ((*i)->notifier==notifier)
+ {
+ mQueue.removeRef(*i);
+ if (mStarted)
+ emit dequeued(notifier);
+ delete *i;
+ return;
+ }
+ }
+}
+
+void Downloader::getNext()
+{
+ if (current) return;
+ if (!mStarted) return;
+ if (mQueue.isEmpty()) return;
+ current=mQueue.take(0);
+
+ // open the QFile
+ localfile=new QFile(current->local);
+ localfile->open(IO_ReadWrite | IO_Append);
+
+ mJob= KIO::get(current->file, true, false);
+ connect(mJob, SIGNAL(data(KIO::Job*, const QByteArray&)), SLOT(data(KIO::Job*, const QByteArray&)));
+ connect(mJob, SIGNAL(result(KIO::Job*)), SLOT(jobDone(KIO::Job*)));
+ connect(mJob, SIGNAL(percent(KIO::Job*, unsigned long)), SLOT(percent(KIO::Job*, unsigned long)));
+
+ if (mTimeout)
+ delete mTimeout;
+ mTimeout=new QTimer(this);
+ mTimeout->start(30000, true);
+ connect(mTimeout, SIGNAL(timeout()), SLOT(giveUpWithThisDownloadServerIsRunningNT()));
+}
+
+void Downloader::data(KIO::Job *, const QByteArray &data)
+{
+ localfile->writeBlock(data);
+ localfile->flush();
+ delete mTimeout;
+ mTimeout=0;
+}
+
+void Downloader::jobDone(KIO::Job *)
+{
+ delete mTimeout;
+ mTimeout=0;
+ current->notifier->downloadFinished();
+ if (mStarted)
+ emit dequeued(current->notifier);
+ delete current;
+ current=0;
+ mJob=0;
+ getNext();
+}
+
+void Downloader::giveUpWithThisDownloadServerIsRunningNT()
+{
+ delete mTimeout;
+ mTimeout=0;
+ if (!current) return;
+ DownloadItem *old=current->notifier;
+ if (!old) return;
+ old->downloadTimeout();
+ current=0;
+ mJob=0;
+ delete old;
+ getNext();
+}
+
+void Downloader::percent( KIO::Job *, unsigned long percent)
+{
+ if (current && current->notifier)
+ current->notifier->downloaded((int)percent);
+}
+
+#include "downloader.moc"
+
diff --git a/noatun/library/effects.cpp b/noatun/library/effects.cpp
new file mode 100644
index 00000000..3c6c601e
--- /dev/null
+++ b/noatun/library/effects.cpp
@@ -0,0 +1,285 @@
+#include "effects.h"
+#include "engine.h"
+#include <common.h>
+#include <dynamicrequest.h>
+#include <artsflow.h>
+#include <app.h>
+#include <player.h>
+#include <soundserver.h>
+#include <noatunarts.h>
+#include <qlayout.h>
+
+#include <config.h>
+
+#define HAS_ARTSVERSION_H
+
+#ifdef HAS_ARTSVERSION_H
+#include <artsgui.h>
+#include <kartswidget.h>
+#endif
+
+#define engine napp->player()->engine()
+#define server (*(engine->server()))
+#define stack (*engine->effectStack())
+
+using namespace std;
+using namespace Arts;
+
+class EffectConfigWidget : public QWidget
+{
+public:
+ EffectConfigWidget(Effect *e, QWidget *parent=0)
+ : QWidget(parent), mEf(e)
+ {}
+
+ virtual ~EffectConfigWidget()
+ {
+ mEf->mConfig=0;
+ }
+
+private:
+ Effect *mEf;
+};
+
+
+Effect::Effect(const char *name)
+ : mId(0), mName(name), mConfig(0)
+{
+ mEffect=new StereoEffect;
+ *mEffect=DynamicCast(server.createObject(std::string(name)));
+ napp->effects()->mItems.append(this);
+}
+
+long Effect::id() const
+{
+ return mId;
+}
+
+StereoEffect *Effect::effect() const
+{
+ return mEffect;
+}
+
+Effect *Effect::after() const
+{
+ QPtrList<Effect> effects=napp->effects()->effects();
+ QPtrListIterator<Effect> i(effects);
+ for(; i.current(); ++i)
+ if ((*i)->id()==mId)
+ {
+ ++i;
+ if (*i)
+ return *i;
+ }
+
+ return 0;
+}
+
+Effect *Effect::before() const
+{
+ QPtrList<Effect> effects=napp->effects()->effects();
+ QPtrListIterator<Effect> i(effects);
+ for(; i.current(); ++i)
+ if ((*i)->id()==mId)
+ {
+ --i;
+ if (*i)
+ return *i;
+ }
+
+ return 0;
+}
+
+QCString Effect::name() const
+{
+ return mName;
+}
+
+QString Effect::title() const
+{
+ return clean(mName);
+}
+
+QString Effect::clean(const QCString &name)
+{
+ int pos=name.findRev("::");
+ if (pos>0)
+ return name.right(name.length()-pos-2);
+ return name;
+}
+
+bool Effect::isNull() const
+{
+ return effect()->isNull();
+}
+
+QWidget *Effect::configure(bool /*friendly*/)
+{
+#ifdef HAS_ARTSVERSION_H
+ if (mConfig) return mConfig;
+ if (!configurable()) return 0;
+
+ GenericGuiFactory factory;
+ Widget gui = factory.createGui(*mEffect);
+
+ if(!gui.isNull())
+ {
+ mConfig=new EffectConfigWidget(this);
+ mConfig->setCaption(title());
+
+ QBoxLayout *l=new QHBoxLayout(mConfig);
+ l->add(new KArtsWidget(gui, mConfig));
+ l->freeze();
+ }
+
+ return mConfig;
+#else
+ return 0;
+#endif
+}
+
+bool Effect::configurable() const
+{
+#ifdef HAS_ARTSVERSION_H
+ TraderQuery query;
+ query.supports("Interface","Arts::GuiFactory");
+ query.supports("CanCreate", mEffect->_interfaceName());
+
+ vector<TraderOffer> *queryResults = query.query();
+ bool yes= queryResults->size();
+ delete queryResults;
+
+ return yes;
+#else
+ return 0;
+#endif
+}
+
+Effect::~Effect()
+{
+ delete mConfig;
+ napp->effects()->remove(this, false);
+ emit napp->effects()->deleting(this);
+ delete mEffect;
+ napp->effects()->mItems.removeRef(this);
+}
+
+
+Effects::Effects()
+{
+ mItems.setAutoDelete(false);
+}
+
+bool Effects::insert(const Effect *after, Effect *item)
+{
+ if (!item) return false;
+ if (item->id()) return false;
+ if (item->isNull()) return false;
+ long i;
+ item->effect()->start();
+
+ if (!after)
+ i=stack.insertTop(*item->effect(), (const char*)item->name());
+ else
+ i=stack.insertAfter(after->id(), *item->effect(), (const char*)item->name());
+ if (!i)
+ {
+ item->effect()->stop();
+ return false;
+ }
+
+ item->mId=i;
+ emit added(item);
+ return true;
+}
+
+bool Effects::append(Effect *item)
+{
+ if (!item) return false;
+ if (item->id()) return false;
+ if (item->isNull()) return false;
+
+ item->effect()->start();
+ item->mId=stack.insertBottom(*item->effect(), (const char*)item->name());
+ if (!item->mId)
+ {
+ item->effect()->stop();
+ return false;
+ }
+ emit added(item);
+ return true;
+}
+
+void Effects::move(const Effect *after, Effect *item)
+{
+ if (!item) return;
+ if (!item->id()) return;
+ long id=after ? after->id() : 0;
+ stack.move(id, item->id());
+ emit moved(item);
+}
+
+void Effects::remove(Effect *item, bool del)
+{
+ if (!item) return;
+ if (!item->id()) return;
+
+ stack.remove(item->id());
+ item->effect()->stop();
+ item->mId=0;
+ removed(item);
+
+ if (del)
+ delete item;
+}
+
+void Effects::removeAll(bool del)
+{
+ for (QPtrListIterator<Effect> i(mItems); i.current(); ++i)
+ if ((*i)->id())
+ remove(*i, del);
+}
+
+QStrList Effects::available() const
+{
+ QStrList val;
+ Arts::TraderQuery query;
+ query.supports("Interface", "Arts::StereoEffect");
+ query.supports("Interface", "Arts::SynthModule");
+ query.supports("Use", "directly");
+ vector<Arts::TraderOffer> *offers = query.query();
+ for (vector<Arts::TraderOffer>::iterator i=offers->begin(); i!=offers->end(); i++)
+ {
+ Arts::TraderOffer &offer=*i;
+ QCString name = offer.interfaceName().c_str();
+ val.append(name);
+ }
+ delete offers;
+ return val;
+}
+
+Effect *Effects::findId(long id) const
+{
+ for (QPtrListIterator<Effect> i(mItems); i.current(); ++i)
+ if ((*i)->id()==id)
+ return *i;
+ return 0;
+}
+
+QPtrList<Effect> Effects::effects() const
+{
+ vector<long> *items=stack.effectList();
+ QPtrList<Effect> effects;
+ for (vector<long>::iterator i=items->begin();i!=items->end();i++)
+ if (Effect *e=findId(*i))
+ effects.append(e);
+
+ delete items;
+ return effects;
+}
+
+#undef server
+#undef stack
+#undef engine
+
+#include "effects.moc"
diff --git a/noatun/library/effectview.cpp b/noatun/library/effectview.cpp
new file mode 100644
index 00000000..73af1fc2
--- /dev/null
+++ b/noatun/library/effectview.cpp
@@ -0,0 +1,284 @@
+// Copyright (c) 2001 Charles Samuels <charles@kde.org>
+// Copyright (c) 2001 Neil Stevens <neil@qualityassistant.com>
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "effects.h"
+#include "effectview.h"
+#include "app.h"
+
+#include <kcombobox.h>
+#include <kdialog.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <qdragobject.h>
+#include <qheader.h>
+#include <qhgroupbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qtextedit.h>
+#include <qtoolbutton.h>
+#include <qvgroupbox.h>
+#include <qwhatsthis.h>
+
+class EffectListItem : public QListViewItem
+{
+public:
+ EffectListItem(QListView *parent, QListViewItem *after, Effect *e)
+ : QListViewItem(parent, after, e->title()), mEffect(e)
+ {
+ }
+
+ Effect *effect() const { return mEffect; }
+
+private:
+ Effect *mEffect;
+};
+
+EffectList::EffectList(QWidget *parent)
+ : KListView(parent)
+{
+}
+
+bool EffectList::acceptDrag(QDropEvent *event) const
+{
+ return QCString(event->format()) == "application/x-noatun-effectdrag";
+}
+
+QDragObject *EffectList::dragObject() const
+{
+ if (!currentItem()) return 0;
+ return new QStoredDrag("application/x-noatun-effectdrag", (QWidget*)this);
+}
+
+EffectView::EffectView()
+ : KDialogBase((QWidget*)0L, 0, false, i18n("Effects"), Help | Close, Close, true)
+ , initialized(false)
+{
+}
+
+void EffectView::show()
+{
+ init();
+ KDialogBase::show();
+}
+
+namespace
+{
+QToolButton *newButton(const QIconSet &iconSet, const QString &textLabel, QObject *receiver, const char * slot, QWidget *parent, const char *name = 0)
+{
+ QToolButton *button = new QToolButton(parent, name);
+ button->setIconSet(iconSet);
+ button->setTextLabel(textLabel, true);
+ QObject::connect(button, SIGNAL(clicked()), receiver, slot);
+ button->setFixedSize(QSize(22, 22));
+ return button;
+}
+}
+
+void EffectView::init(void)
+{
+ if(initialized) return;
+ initialized = true;
+
+ setCaption(i18n("Effects - Noatun"));
+ setIcon(SmallIcon("effect"));
+
+ // Create widgets and layouts
+ QFrame *box = makeMainWidget();
+ QVBoxLayout *boxLayout = new QVBoxLayout(box, 0, KDialog::spacingHint());
+
+ // Available
+ QVGroupBox *topBox = new QVGroupBox(i18n("Available Effects"), box);
+ topBox->setInsideSpacing(KDialog::spacingHint());
+
+ QFrame *topTopFrame = new QFrame(topBox);
+ QHBoxLayout *topTopLayout = new QHBoxLayout(topTopFrame, 0, KDialog::spacingHint());
+ topTopLayout->setAutoAdd(true);
+ available = new KComboBox(false, topTopFrame);
+ QToolButton *add = newButton(BarIconSet("down", KIcon::SizeSmall), i18n("Add"), this, SLOT(addEffect()), topTopFrame);
+
+ // Active
+ QHGroupBox *bottomBox = new QHGroupBox(i18n("Active Effects"), box);
+ bottomBox->setInsideSpacing(KDialog::spacingHint());
+
+ active = new EffectList(bottomBox);
+
+ boxLayout->addWidget(topBox);
+ boxLayout->addWidget(bottomBox);
+
+ // Fill and configure widgets
+ available->insertStrList(napp->effects()->available());
+
+ active->setAcceptDrops(true);
+ active->addColumn("");
+ active->header()->hide();
+ active->setSorting(-1);
+ active->setDropVisualizer(true);
+ active->setItemsMovable(true);
+ active->setSelectionMode(QListView::Single);
+ active->setDragEnabled(true);
+ connect(active, SIGNAL(dropped(QDropEvent *, QListViewItem *)), SLOT(activeDrop(QDropEvent *, QListViewItem *)));
+
+ // when a new effect is added
+ connect(napp->effects(), SIGNAL(added(Effect *)), SLOT(added(Effect *)));
+ connect(napp->effects(), SIGNAL(removed(Effect *)), SLOT(removed(Effect *)));
+ connect(napp->effects(), SIGNAL(moved(Effect *)), SLOT(moved(Effect *)));
+
+ available->setCurrentItem(0);
+
+ connect(active, SIGNAL(currentChanged(QListViewItem *)), SLOT(activeChanged(QListViewItem *)));
+ active->setCurrentItem(0);
+
+ // the buttons
+ QFrame *bottomLeftFrame = new QFrame(bottomBox);
+ QVBoxLayout *bottomLeftLayout = new QVBoxLayout(bottomLeftFrame, 0, KDialog::spacingHint());
+ up = newButton(BarIconSet("up", KIcon::SizeSmall), i18n("Up"), this, SLOT(moveUp()), bottomLeftFrame);
+ down = newButton(BarIconSet("down", KIcon::SizeSmall), i18n("Down"), this, SLOT(moveDown()), bottomLeftFrame);
+ configure = newButton(BarIconSet("configure", KIcon::SizeSmall), i18n("Configure"), this, SLOT(configureEffect()), bottomLeftFrame);
+ remove = newButton(BarIconSet("remove", KIcon::SizeSmall), i18n("Remove"), this, SLOT(removeEffect()), bottomLeftFrame);
+ bottomLeftLayout->addWidget(up);
+ bottomLeftLayout->addWidget(down);
+ bottomLeftLayout->addWidget(configure);
+ bottomLeftLayout->addWidget(remove);
+ bottomLeftLayout->addStretch();
+
+
+ activeChanged(active->currentItem());
+
+ // Inline documentation
+ QWhatsThis::add(available, i18n("This shows all available effects.\n\nTo activate a plugin, drag files from here to the active pane on the right."));
+ QWhatsThis::add(add, i18n("This will place the selected effect at the bottom of your chain."));
+ QWhatsThis::add(active, i18n("This shows your effect chain. Noatun supports an unlimited amount of effects in any order. You can even have the same effect twice.\n\nDrag the items to and from here to add and remove them, respectively. You may also reorder them with drag-and-drop. These actions can also be performed with the buttons to the right."));
+ QWhatsThis::add(up, i18n("Move the currently selected effect up in the chain."));
+ QWhatsThis::add(down, i18n("Move the currently selected effect down in the chain."));
+ QWhatsThis::add(configure, i18n("Configure the currently selected effect.\n\nYou can change things such as intensity from here."));
+ QWhatsThis::add(remove, i18n("This will remove the selected effect from your chain."));
+
+ resize(300, 400);
+}
+
+void EffectView::activeDrop(QDropEvent *, QListViewItem *pafter)
+{
+ EffectListItem *after(static_cast<EffectListItem*>(pafter));
+ napp->effects()->move(after ? after->effect() : 0,
+ static_cast<EffectListItem*>(active->currentItem())->effect());
+ activeChanged(active->currentItem());
+}
+
+QListViewItem *EffectView::toListItem(Effect *e) const
+{
+ for(QListViewItem *i = active->firstChild(); i; i = i->itemBelow())
+ if(static_cast<EffectListItem*>(i)->effect() == e)
+ return i;
+ return 0;
+}
+
+void EffectView::added(Effect *item)
+{
+ new EffectListItem(active, toListItem(item->before()), item);
+ activeChanged(active->currentItem());
+}
+
+void EffectView::moved(Effect *item)
+{
+ delete toListItem(item);
+ added(item);
+}
+
+void EffectView::removed(Effect *item)
+{
+ delete toListItem(item);
+ activeChanged(active->currentItem());
+}
+
+void EffectView::moveDown()
+{
+ Effect *e = static_cast<EffectListItem*>(active->currentItem())->effect();
+
+ if(e->after())
+ napp->effects()->move(e->after(), e);
+ active->setCurrentItem(toListItem(e));
+ active->setSelected(toListItem(e), true);
+ activeChanged(active->currentItem());
+}
+
+void EffectView::moveUp()
+{
+ Effect *e = static_cast<EffectListItem*>(active->currentItem())->effect();
+ if (e->before())
+ {
+ if (e->before()->before())
+ napp->effects()->move(e->before()->before(), e);
+ else
+ napp->effects()->move(0, e);
+ }
+ active->setCurrentItem(toListItem(e));
+ active->setSelected(toListItem(e), true);
+ activeChanged(active->currentItem());
+}
+
+void EffectView::removeEffect()
+{
+ EffectListItem *item = static_cast<EffectListItem*>(active->currentItem());
+ napp->effects()->remove(item->effect());
+ activeChanged(active->currentItem());
+}
+
+void EffectView::addEffect()
+{
+ // local8Bit() and arts makes me nervous
+ napp->effects()->append(new Effect(available->currentText().local8Bit()));
+ activeChanged(active->currentItem());
+}
+
+void EffectView::configureEffect()
+{
+ Effect *e = static_cast<EffectListItem*>(active->currentItem())->effect();
+ if(!e) return;
+
+ QWidget *c = e->configure();
+ if(c) c->show();
+}
+
+void EffectView::activeChanged(QListViewItem *i)
+{
+ if(i)
+ {
+ up->setEnabled(i->itemAbove());
+ down->setEnabled(i->itemBelow());
+ remove->setEnabled(true);
+
+ Effect *e = static_cast<EffectListItem*>(active->currentItem())->effect();
+ configure->setEnabled(e->configurable());
+ }
+ else
+ {
+ up->setEnabled(false);
+ down->setEnabled(false);
+ remove->setEnabled(false);
+ configure->setEnabled(false);
+ }
+}
+
+#include "effectview.moc"
diff --git a/noatun/library/effectview.h b/noatun/library/effectview.h
new file mode 100644
index 00000000..1cdb1907
--- /dev/null
+++ b/noatun/library/effectview.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2001 Charles Samuels <charles@kde.org>
+// Copyright (c) 2001 Neil Stevens <neil@qualityassistant.com>
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef EFFECTVIEW_H
+#define EFFECTVIEW_H
+
+#include <kdialogbase.h>
+#include <klistview.h>
+
+class EffectList;
+class Effect;
+class KComboBox;
+class QToolButton;
+
+class EffectView : public KDialogBase
+{
+Q_OBJECT
+public:
+ EffectView();
+
+ virtual void show();
+
+public slots:
+ void added(Effect *);
+ void removed(Effect *);
+ void moved(Effect *);
+
+ // buttons
+ void moveDown();
+ void moveUp();
+ void removeEffect();
+ void addEffect();
+ void configureEffect();
+
+ void activeChanged(QListViewItem *);
+
+protected slots:
+ void activeDrop(QDropEvent *, QListViewItem *);
+
+private:
+ QListViewItem *toListItem(Effect *) const;
+
+ void init(void);
+ bool initialized;
+
+ KComboBox *available;
+
+ QToolButton *up, *down, *configure, *remove;
+
+ EffectList *active;
+};
+
+class EffectList : public KListView
+{
+Q_OBJECT
+public:
+ EffectList(QWidget *parent);
+ virtual bool acceptDrag(QDropEvent *) const;
+ virtual QDragObject *dragObject() const;
+};
+
+#endif
diff --git a/noatun/library/engine.cpp b/noatun/library/engine.cpp
new file mode 100644
index 00000000..e937fa7f
--- /dev/null
+++ b/noatun/library/engine.cpp
@@ -0,0 +1,589 @@
+// $Id$
+
+#include <noatun/engine.h>
+#include <noatun/equalizer.h>
+#include <noatun/player.h>
+#include <noatun/plugin.h>
+#include <noatun/effects.h>
+#include "titleproxy.h"
+
+#include <string.h>
+
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <qfile.h>
+#include <qdir.h>
+#include <sys/wait.h>
+#include <kplayobject.h>
+#include <kplayobjectfactory.h>
+
+#include <dynamicrequest.h>
+#include <soundserver.h>
+#include <kmedia2.h>
+#include <flowsystem.h>
+#include <noatunarts.h>
+#include <connect.h>
+#include <cpuinfo.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <math.h>
+
+
+static Arts::PlayObject nullPO() { return Arts::PlayObject::null(); }
+
+#define HARDWARE_VOLUME
+
+#if defined(__osf__)
+#undef HARDWARE_VOLUME
+#elif defined(__linux__)
+#include <sys/soundcard.h>
+#elif defined(__FreeBSD__)
+#include <sys/soundcard.h>
+#elif defined(__NetBSD__)
+#include <soundcard.h>
+#elif defined(___SOMETHING_UNKNOWN__)
+#include <sys/soundcard.h>
+#elif defined(_UNIXWARE)
+#include <sys/soundcard.h>
+#else
+#undef HARDWARE_VOLUME
+#endif
+
+using namespace std;
+
+namespace VolumeControls
+{
+ struct VC
+ {
+ virtual ~VC() {}
+ virtual void setVolume(int percent)=0;
+ virtual int volume() const =0;
+ };
+
+#ifdef HARDWARE_VOLUME
+
+ struct Hardware : public VC
+ {
+ Hardware(Engine *)
+ {
+ if ((fd=::open( "/dev/mixer", O_RDWR)) < 0)
+ {
+ return;
+ }
+ else
+ {
+#define ERROR { fd=-1; return; }
+ int devmask, recmask, i_recsrc, stereodevs;
+ // Mixer is open. Now define properties
+ if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) ERROR
+ if (ioctl(fd, SOUND_MIXER_READ_RECMASK, &recmask) == -1) ERROR
+ if (ioctl(fd, SOUND_MIXER_READ_RECSRC, &i_recsrc) == -1) ERROR
+ if (ioctl(fd, SOUND_MIXER_READ_STEREODEVS, &stereodevs) == -1) ERROR
+ if (!devmask) ERROR
+#undef ERROR
+ }
+
+ }
+
+ virtual ~Hardware() { ::close(fd); }
+
+ virtual void setVolume(int percent)
+ {
+ percent=percent+(percent<<8);
+ ioctl(fd, MIXER_WRITE(4), &percent);
+ }
+
+ virtual int volume() const
+ {
+ int volume, left, right;
+ left=100;
+ if (ioctl(fd, MIXER_READ(4), &volume) != -1)
+ {
+ left=volume & 0x7f;
+ right=(volume>>8) & 0x7f;
+ left=(left+right)/2;
+ }
+ return left;
+ }
+ private:
+ int fd;
+ };
+#endif
+
+ struct SoftwareSSE : public VC
+ {
+ SoftwareSSE(Engine *e) : mVolume(100)
+ {
+ volumeControl = Arts::DynamicCast(e->server()->createObject("Noatun::StereoVolumeControlSSE"));
+
+ if(volumeControl.isNull())
+ volumeControl = Arts::DynamicCast(e->server()->createObject("Noatun::StereoVolumeControl"));
+
+ volumeControl.start();
+
+ id=e->globalEffectStack()->insertBottom(volumeControl,"Volume Control");
+ }
+
+ virtual void setVolume(int percent)
+ {
+ mVolume=percent;
+ float vol = pow(2.0, ((400 - (100-percent)*12)/200.0))/4.0;
+ if (percent == 0) vol = 0.0;
+ if (!volumeControl.isNull())
+ volumeControl.percent(vol);
+ }
+
+ virtual int volume() const
+ {
+ return mVolume;
+ }
+
+ private:
+ Noatun::StereoVolumeControlSSE volumeControl;
+ long id;
+ int mVolume;
+ };
+
+ struct Software : public VC
+ {
+ Software(Engine *e) : mVolume(100)
+ {
+ volumeControl = Arts::DynamicCast(e->server()->createObject("Noatun::StereoVolumeControl"));
+ volumeControl.start();
+
+ id=e->globalEffectStack()->insertBottom(volumeControl,"Volume Control");
+ }
+
+ virtual void setVolume(int percent)
+ {
+ mVolume=percent;
+ if (!volumeControl.isNull())
+ volumeControl.percent((float)percent/100.0);
+ }
+
+ virtual int volume() const
+ {
+ return mVolume;
+ }
+
+ private:
+ Noatun::StereoVolumeControl volumeControl;
+ long id;
+ int mVolume;
+ };
+
+ static VC *volumeControl(Engine *e)
+ {
+#ifdef HARDWARE_VOLUME
+ if (napp->fastMixer())
+ return new Hardware(e);
+ else
+ {
+#endif
+ if (!getenv("NO_SSE") && Arts::CpuInfo::flags()&Arts::CpuInfo::CpuSSE)
+ return new SoftwareSSE(e);
+ else
+ return new Software(e);
+
+#ifdef HARDWARE_VOLUME
+ }
+#endif
+ }
+}
+
+
+class Engine::EnginePrivate
+{
+public:
+ EnginePrivate()
+ : playobj(0),
+ server(Arts::SoundServerV2::null()),
+ globalEffectStack(Noatun::StereoEffectStack::null()),
+ effectsStack(Noatun::StereoEffectStack::null()),
+ visStack(Noatun::StereoEffectStack::null()),
+ volumeControl(0), session(Noatun::Session::null()),
+ pProxy(0)
+ {
+ }
+
+ ~EnginePrivate()
+ {
+ visStack=Noatun::StereoEffectStack::null();
+ }
+
+ KDE::PlayObject *playobj;
+ Arts::SoundServerV2 server;
+ Arts::Synth_AMAN_PLAY amanPlay;
+
+ // globalEffectStack
+ // |- effectsStack
+ // |- Effects...
+ // |- visStack
+ // |- Visualizations
+ // |- Volume Control
+ //
+ Noatun::StereoEffectStack globalEffectStack;
+ Noatun::StereoEffectStack effectsStack;
+ Noatun::StereoEffectStack visStack;
+ Noatun::Equalizer equalizer;
+
+ int volumeID;
+ VolumeControls::VC *volumeControl;
+ Noatun::Session session;
+ TitleProxy::Proxy *pProxy;
+};
+
+Arts::SoundServerV2 *Engine::server() const { return &d->server;}
+Arts::PlayObject Engine::playObject() const { return d->playobj ? d->playobj->object() : nullPO(); }
+Arts::SoundServerV2 *Engine::simpleSoundServer() const { return &d->server; }
+Noatun::StereoEffectStack *Engine::effectStack() const { return &d->effectsStack; }
+Noatun::StereoEffectStack *Engine::visualizationStack() const { return &d->visStack; }
+Noatun::StereoEffectStack *Engine::globalEffectStack() const { return &d->globalEffectStack; }
+Noatun::Equalizer *Engine::equalizer() const { return &d->equalizer; }
+Noatun::Session *Engine::session() const { return &d->session; }
+
+Engine::Engine(QObject *parent) : QObject(parent, "Engine"), mPlay(false)
+{
+ d=new EnginePrivate;
+ // Connect to aRts
+ if (!initArts())
+ {
+ KMessageBox::error(0, i18n("There was an error communicating to the aRts daemon."), i18n("aRts error"));
+ exit(0);
+ }
+
+}
+
+Engine::~Engine()
+{
+ stop();
+ delete d->volumeControl;
+ d->server=Arts::SoundServerV2::null();
+ delete d;
+}
+
+void Engine::setInitialized()
+{
+ mPlay=true;
+}
+
+bool Engine::initialized() const
+{
+ return mPlay;
+}
+
+bool Engine::open(const PlaylistItem &file)
+{
+ if(!initArts())
+ return false;
+
+ d->playobj = 0;
+
+ KDE::PlayObjectFactory factory(d->server);
+
+ if (file.isProperty("stream_") && file.url().protocol() == "http")
+ {
+ deleteProxy();
+ d->pProxy = new TitleProxy::Proxy(KURL(file.property("stream_")));
+ d->playobj = factory.createPlayObject(d->pProxy->proxyUrl(), false);
+
+ connect(d->playobj, SIGNAL(destroyed()), this, SLOT(deleteProxy()));
+ connect(
+ d->pProxy, SIGNAL(
+ metaData(
+ const QString &, const QString &,
+ const QString &, const QString &,
+ const QString &, const QString &)),
+ this, SIGNAL(
+ receivedStreamMeta(const QString &, const QString &,
+ const QString &, const QString &,
+ const QString &, const QString &))
+ );
+ connect(d->pProxy, SIGNAL(proxyError()), this, SLOT(slotProxyError()));
+ }
+ else
+ {
+ d->playobj = factory.createPlayObject(file.url(), false);
+ }
+
+ if (!d->playobj || d->playobj->isNull())
+ {
+ kdDebug(66666) << k_funcinfo <<
+ "No playobject for '" << file.url().prettyURL() << "'" << endl;
+ delete d->playobj;
+ d->playobj=0;
+ emit playingFailed();
+ return false;
+ }
+
+ if ( !d->playobj->object().isNull() )
+ {
+ connectPlayObject();
+ }
+ else
+ {
+ connect( d->playobj, SIGNAL( playObjectCreated() ), this, SLOT( connectPlayObject() ) );
+ }
+
+ if (mPlay)
+ d->playobj->play();
+
+ return true;
+}
+
+void Engine::slotProxyError()
+{
+ kdDebug(66666) << k_funcinfo << endl;
+ emit playingFailed();
+ deleteProxy();
+}
+
+void Engine::deleteProxy()
+{
+ delete d->pProxy;
+ d->pProxy = 0;
+}
+
+void Engine::connectPlayObject()
+{
+ if (d->playobj->object().isNull())
+ {
+ emit playingFailed();
+ return;
+ }
+ d->playobj->object()._node()->start();
+
+ // TODO: check for existence of left & right streams
+ Arts::connect(d->playobj->object(),"left",d->globalEffectStack,"inleft");
+ Arts::connect(d->playobj->object(),"right",d->globalEffectStack,"inright");
+
+ emit aboutToPlay();
+}
+
+bool Engine::play()
+{
+ if (!mPlay) return true;
+ if(!d->playobj)
+ return false;
+ d->playobj->play();
+ return true;
+}
+
+void Engine::pause()
+{
+ d->playobj->pause();
+}
+
+void Engine::stop()
+{
+ if(!d->playobj) return;
+
+ d->playobj->halt();
+ delete d->playobj;
+ d->playobj=0;
+}
+
+void Engine::seek(int msec) // pass time in msecs
+{
+ if(!d->playobj) return;
+
+ Arts::poTime t;
+
+ t.custom = 0.0;
+ t.ms = (long) msec % 1000;
+ t.seconds = (long) (msec - t.ms) / 1000;
+
+ if(d->playobj)
+ d->playobj->seek(t);
+}
+
+int Engine::position()
+{
+ if(!d->playobj) return -1;
+
+ Arts::poTime time(d->playobj->currentTime());
+ return (int)(time.ms + (time.seconds*1000)); // return position in milliseconds
+}
+
+int Engine::length()
+{
+ if(!d->playobj) return -1;
+ if (!(d->playobj->capabilities() & Arts::capSeek))
+ return -1;
+
+ Arts::poTime time(d->playobj->overallTime());
+ return (int)(time.ms + (time.seconds*1000)); // return track-length in milliseconds
+}
+
+int Engine::state()
+{
+ if(d->playobj)
+ return d->playobj->state();
+ else
+ return Arts::posIdle;
+}
+
+void Engine::setVolume(int percent)
+{
+ if (percent>100)
+ percent=100;
+ if (percent<0)
+ percent=0;
+ d->volumeControl->setVolume(percent);
+}
+
+int Engine::volume() const
+{
+ return d->volumeControl->volume();
+}
+
+void Engine::useHardwareMixer(bool)
+{
+ delete d->volumeControl;
+ d->volumeControl=VolumeControls::volumeControl(this);
+}
+
+bool Engine::initArts()
+{
+ if ( d->server.isNull() || d->server.error() )
+ {
+ d->server = Arts::Reference("global:Arts_SoundServerV2");
+ int volume = d->volumeControl ? d->volumeControl->volume() : -1;
+ delete d->volumeControl;
+ d->volumeControl=0;
+
+ if( d->server.isNull() || d->server.error() )
+ {
+ // aRts seems not to be running, let's try to run it
+ // First, let's read the configuration as in kcmarts
+ KConfig config("kcmartsrc");
+ QCString cmdline;
+
+ config.setGroup("Arts");
+
+ bool rt = config.readBoolEntry("StartRealtime",false);
+ bool x11Comm = config.readBoolEntry("X11GlobalComm",false);
+
+ // put the value of x11Comm into .mcoprc
+ {
+ KConfig X11CommConfig(QDir::homeDirPath()+"/.mcoprc");
+
+ if(x11Comm)
+ X11CommConfig.writeEntry("GlobalComm", "Arts::X11GlobalComm");
+ else
+ X11CommConfig.writeEntry("GlobalComm", "Arts::TmpGlobalComm");
+
+ X11CommConfig.sync();
+ }
+
+ cmdline = QFile::encodeName(KStandardDirs::findExe(QString::fromLatin1("kdeinit_wrapper")));
+ cmdline += " ";
+
+ if (rt)
+ cmdline += QFile::encodeName(KStandardDirs::findExe(
+ QString::fromLatin1("artswrapper")));
+ else
+ cmdline += QFile::encodeName(KStandardDirs::findExe(
+ QString::fromLatin1("artsd")));
+
+ cmdline += " ";
+ cmdline += config.readEntry("Arguments","-F 10 -S 4096 -s 60 -m artsmessage -l 3 -f").utf8();
+
+ int status=::system(cmdline);
+
+ if ( status!=-1 && WIFEXITED(status) )
+ {
+ // We could have a race-condition here.
+ // The correct way to do it is to make artsd fork-and-exit
+ // after starting to listen to connections (and running artsd
+ // directly instead of using kdeinit), but this is better
+ // than nothing.
+ int time = 0;
+ do
+ {
+ // every time it fails, we should wait a little longer
+ // between tries
+ ::sleep(1+time/2);
+ d->server = Arts::Reference("global:Arts_SoundServerV2");
+ } while(++time < 5 && (d->server.isNull()));
+ }
+ }
+
+ if ( !d->server.isNull() )
+ {
+ d->amanPlay = Arts::DynamicCast(
+ d->server.createObject("Arts::Synth_AMAN_PLAY")
+ );
+
+ if (d->amanPlay.isNull())
+ goto crapOut;
+
+ d->session = Arts::DynamicCast(
+ d->server.createObject("Noatun::Session"));
+ if (d->session.isNull())
+ {
+ kdWarning() << "Couldn't instanciate artsobject Noatun::Session. "
+ << "(This is normally caused by a broken package or "
+ << "compiling kdemultimedia in a --prefix different "
+ << "from arts. It may also be from two conflicting "
+ << "packages, so uninstall every arts/artsd package "
+ << "you have installed and try again." << endl;
+ goto crapOut;
+ }
+
+ d->amanPlay.title("noatun");
+ d->amanPlay.autoRestoreID("noatun");
+ d->amanPlay.start();
+
+ d->globalEffectStack=Arts::DynamicCast(
+ d->server.createObject("Noatun::StereoEffectStack"));;
+ d->globalEffectStack.start();
+ Arts::connect(d->globalEffectStack,d->amanPlay);
+
+ d->effectsStack=Arts::DynamicCast(
+ d->server.createObject("Noatun::StereoEffectStack"));
+ d->effectsStack.start();
+ d->globalEffectStack.insertBottom(d->effectsStack, "Effects Stack");
+
+ d->equalizer=Arts::DynamicCast(d->server.createObject("Noatun::Equalizer"));
+ d->equalizer.start();
+ d->globalEffectStack.insertBottom(d->equalizer, "Equalizer");
+
+ if (napp->equalizer())
+ {
+ napp->equalizer()->update(true);
+ napp->equalizer()->setPreamp(napp->equalizer()->preamp());
+ napp->equalizer()->setEnabled(napp->equalizer()->isEnabled());
+ }
+
+ d->visStack=Arts::DynamicCast(
+ d->server.createObject("Noatun::StereoEffectStack"));
+ d->visStack.start();
+
+ d->globalEffectStack.insertBottom(d->visStack, "Visualization Stack");
+ d->volumeControl=VolumeControls::volumeControl(this);
+ if (volume != -1)
+ d->volumeControl->setVolume(volume);
+ }
+ else
+ {
+ crapOut:
+ KMessageBox::error( 0, i18n("Connecting/starting aRts soundserver failed. Make sure that artsd is configured properly."));
+ exit(1);
+ }
+ }
+
+ d->playobj=0;
+ emit artsError();
+ return true;
+}
+
+
+#include "engine.moc"
+
diff --git a/noatun/library/equalizer.cpp b/noatun/library/equalizer.cpp
new file mode 100644
index 00000000..2ee208db
--- /dev/null
+++ b/noatun/library/equalizer.cpp
@@ -0,0 +1,339 @@
+#include "equalizer.h"
+#include "engine.h"
+#include <common.h>
+#include <dynamicrequest.h>
+#include <artsflow.h>
+#include <app.h>
+#include <player.h>
+#include <soundserver.h>
+#include <noatunarts.h>
+#include <ktempfile.h>
+#include <qdom.h>
+#include <kio/netaccess.h>
+#include <kstandarddirs.h>
+#include <qtextstream.h>
+#include <math.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include "ksaver.h"
+
+#define EQ napp->equalizer()
+#define VEQ napp->vequalizer()
+
+struct OldEqCruft
+{
+ VInterpolation *interpolated;
+
+};
+
+static OldEqCruft *eqCruft=0;
+
+Preset::Preset(const QString &)
+{ } // unused
+
+Preset::Preset(VPreset p)
+{
+ VPreset *copy = new VPreset(p);
+
+ // isn't this horrible? :)
+ mFile = QString::number((unsigned long)copy);
+}
+
+Preset::Preset()
+{ } // unused
+
+VPreset &Preset::vpreset() const
+{
+ unsigned long addr = mFile.toULong();
+ return *(VPreset*)addr;
+}
+
+QString Preset::name() const
+{
+ return vpreset().name();
+}
+
+bool Preset::setName(const QString &name)
+{
+ return vpreset().setName(name);
+}
+
+bool Preset::save() const
+{
+ vpreset().save();
+ return true;
+}
+
+bool Preset::load()
+{
+ vpreset().load();
+ return true;
+}
+
+void Preset::remove()
+{
+ vpreset().remove();
+}
+
+QString Preset::file() const
+{
+ return vpreset().file();
+}
+
+
+Band::Band(int, int)
+{
+ // Never used
+}
+
+Band::Band(int num)
+ : mNum(num)
+{
+
+}
+
+Band::~Band()
+{}
+
+int Band::level()
+{
+ return eqCruft->interpolated->band(mNum).level();
+}
+
+void Band::setLevel(int l)
+{
+ eqCruft->interpolated->band(mNum).setLevel(l);
+}
+
+int Band::start() const
+{
+ return eqCruft->interpolated->band(mNum).start();
+}
+
+int Band::end() const
+{
+ return eqCruft->interpolated->band(mNum).end();
+}
+
+int Band::center() const
+{
+ return eqCruft->interpolated->band(mNum).center();
+}
+
+QString Band::formatStart(bool withHz) const
+{
+ return eqCruft->interpolated->band(mNum).formatStart(withHz);
+}
+
+QString Band::formatEnd(bool withHz) const
+{
+ return eqCruft->interpolated->band(mNum).formatEnd(withHz);
+}
+
+QString Band::format(bool withHz) const
+{
+ return eqCruft->interpolated->band(mNum).format(withHz);
+}
+
+
+Equalizer::Equalizer()
+{
+}
+
+Equalizer::~Equalizer()
+{
+ delete eqCruft->interpolated;
+ delete eqCruft;
+
+// save(napp->dirs()->saveLocation("data", "noatun/") + "equalizer", "auto");
+ for (Band *i=mBands.first(); i!=0; i=mBands.next())
+ delete i;
+}
+
+
+void Equalizer::init()
+{
+ // must be called after VEqualizer::init
+ eqCruft = new OldEqCruft;
+ eqCruft->interpolated = new VInterpolation(6);
+
+ mBands.append(new Band(0));
+ mBands.append(new Band(1));
+ mBands.append(new Band(2));
+ mBands.append(new Band(3));
+ mBands.append(new Band(4));
+ mBands.append(new Band(5));
+
+ connect(VEQ, SIGNAL(changed()), SIGNAL(changed()));
+
+ connect(VEQ, SIGNAL(created(VPreset)), SLOT(created(VPreset)));
+ connect(VEQ, SIGNAL(selected(VPreset)), SLOT(selected(VPreset)));
+ connect(VEQ, SIGNAL(renamed(VPreset)), SLOT(renamed(VPreset)));
+ connect(VEQ, SIGNAL(removed(VPreset)), SLOT(removed(VPreset)));
+
+ connect(VEQ, SIGNAL(enabled()), SIGNAL(enabled()));
+ connect(VEQ, SIGNAL(disabled()), SIGNAL(disabled()));
+ connect(VEQ, SIGNAL(enabled(bool)), SIGNAL(enabled(bool)));
+
+ connect(VEQ, SIGNAL(preampChanged(int)), SIGNAL(preampChanged(int)));
+ connect(VEQ, SIGNAL(preampChanged(int)), SIGNAL(preampChanged(int)));
+}
+
+void Equalizer::created(VPreset preset)
+{
+ Preset *p = new Preset(preset);
+ emit created(p);
+ delete p;
+}
+
+void Equalizer::selected(VPreset preset)
+{
+ Preset *p = new Preset(preset);
+ emit changed(p);
+ delete p;
+}
+
+void Equalizer::renamed(VPreset preset)
+{
+ Preset *p = new Preset(preset);
+ emit renamed(p);
+ delete p;
+}
+
+void Equalizer::removed(VPreset preset)
+{
+ Preset *p = new Preset(preset);
+ emit removed(p);
+ delete p;
+}
+
+QPtrList<Preset> Equalizer::presets() const
+{
+ QValueList<VPreset> presets = VEQ->presets();
+ QPtrList<Preset> list;
+ for (
+ QValueList<VPreset>::Iterator i(presets.begin());
+ i != presets.end(); ++i
+ )
+ {
+ list.append(new Preset(*i));
+ }
+ return list;
+}
+
+Preset *Equalizer::preset(const QString &file)
+{
+ VPreset p = VEQ->presetByFile(file);
+ if (!p) return 0;
+ return new Preset(p);
+}
+
+bool Equalizer::presetExists(const QString &name) const
+{
+ return VEQ->presetExists(name);
+}
+
+Preset *Equalizer::createPreset(const QString &name, bool smart)
+{
+ VPreset p = VEQ->createPreset(name, smart);
+ if (!p) return 0;
+ return new Preset(p);
+}
+
+const QPtrList<Band> &Equalizer::bands() const
+{
+ return mBands;
+}
+
+Band *Equalizer::band(int num) const
+{
+ // can't use QPtrList::at since it sets current
+
+ QPtrListIterator<Band> item(mBands);
+ item+=(unsigned int)num;
+ return *item;
+}
+
+int Equalizer::bandCount() const
+{
+ return 6; // hmm ;)
+}
+
+int Equalizer::preamp() const
+{
+ return VEQ->preamp();
+}
+
+bool Equalizer::isEnabled() const
+{
+ return VEQ->isEnabled();
+
+}
+
+void Equalizer::setPreamp(int p)
+{
+ VEQ->setPreamp(p);
+}
+
+void Equalizer::enable()
+{
+ setEnabled(true);
+}
+
+void Equalizer::disable()
+{
+ setEnabled(false);
+}
+
+void Equalizer::setEnabled(bool e)
+{
+ VEQ->setEnabled(e);
+}
+
+QString Equalizer::toString(const QString &name) const
+{
+ return VEQ->toString(name);
+}
+
+bool Equalizer::fromString(const QString &str)
+{
+ return VEQ->fromString(str);
+}
+
+bool Equalizer::save(const KURL &filename, const QString &name) const
+{
+ return VEQ->save(filename, name);
+}
+
+
+
+bool Equalizer::load(const KURL &filename)
+{
+ return VEQ->load(filename);
+}
+
+void Equalizer::add(Band *)
+{
+ // should never be called
+}
+
+void Equalizer::remove(Band *)
+{
+ // should never be called
+}
+
+void Equalizer::update(bool)
+{
+ // should never be called
+}
+
+void Equalizer::enableUpdates(bool)
+{
+ // should never be called
+}
+
+#undef EQ
+#undef EQBACK
+
+#include "equalizer.moc"
+
diff --git a/noatun/library/equalizerview.cpp b/noatun/library/equalizerview.cpp
new file mode 100644
index 00000000..5e406e13
--- /dev/null
+++ b/noatun/library/equalizerview.cpp
@@ -0,0 +1,325 @@
+#include "vequalizer.h"
+#define EQVIEW_CPP
+#include "equalizerview.h"
+#undef EQVIEW_CPP
+#include "equalizerwidget.h"
+#include "app.h"
+
+#include <knuminput.h>
+#include <kdialog.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klocale.h>
+
+#include <qlayout.h>
+#include <qslider.h>
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <qtabwidget.h>
+#include <qpushbutton.h>
+#include <qheader.h>
+#include <qfileinfo.h>
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+
+#define EQ (napp->vequalizer())
+
+
+////////////////////////////////////////////////
+// PresetList
+
+PresetList::PresetList(QWidget *parent, const char *name)
+ : KListView(parent, name)
+{
+ setItemsRenameable(true);
+ setRenameable(0, true);
+ addColumn(""); // first column is the visible one
+ addColumn("", 0); // create one column to store cruft in
+ setColumnWidthMode(0, QListView::Maximum);
+ header()->setStretchEnabled(true,0);
+ header()->hide();
+ // a try to set a sne minimum width. unfortuately the custom item
+ // still doesn't draw all text with that minimum width
+ setMinimumWidth(kapp->fontMetrics().boundingRect(i18n("Custom")).width()+2*itemMargin());
+}
+
+void PresetList::rename(QListViewItem *item, int c)
+{
+ // We can't rename the "Custom" metapreset
+ if (item->text(0)==i18n("Custom"))
+ return;
+
+ // Or presets we don't have write access to
+ if (!QFileInfo(item->text(1)).isWritable())
+ return;
+
+ KListView::rename(item, c);
+}
+
+////////////////////////////////////////////////
+// EqualizerLevel
+
+EqualizerLevel::EqualizerLevel(QWidget *parent, VBand band)
+ : QWidget(parent), mBand(band)
+{
+ QVBoxLayout *layout = new QVBoxLayout(this,
+ 0, 0, "EqualizerLevel::layout");
+
+ mSlider = new QSlider(-200, 200, 25, 0, Qt::Vertical, this, "EqualizerLevel::mSlider");
+ mSlider->setTickmarks(QSlider::Left);
+ mSlider->setTickInterval(25);
+ layout->addWidget(mSlider);
+ connect(mSlider, SIGNAL(valueChanged(int)), SLOT(changed(int)));
+ mLabel = new QLabel("", this, "EqualizerLevel::mLabel");
+ mLabel->setAlignment(AlignHCenter | AlignVCenter);
+ layout->addWidget(mLabel);
+
+ setMinimumHeight(200);
+// setMinimumWidth(kapp->fontMetrics().width("158kHz"));
+// setMinimumWidth(kapp->fontMetrics().width("549kHz"));
+
+ setBand(band);
+
+ connect(EQ, SIGNAL(modified()), SLOT(changed()));
+ connect(mSlider, SIGNAL(valueChanged(int)), SLOT(changed(int)));
+}
+
+void EqualizerLevel::setBand(VBand band)
+{
+ mBand = band;
+ mLabel->setText(band.format());
+ changed();
+}
+
+void EqualizerLevel::changed()
+{
+ mSlider->setValue(-mBand.level());
+}
+
+void EqualizerLevel::changed(int v)
+{
+ mBand.setLevel(-v);
+}
+
+
+///////////////////////////////////////////////
+// EqualizerView
+
+EqualizerView::EqualizerView()
+ : KDialogBase(0L, "EqualizerView", false, i18n("Equalizer"), Help | Close, Close, true),
+ first(true), mWidget(0), bandsLayout(0), mPresets(0), mGoingPreset(false)
+{
+ mBands.setAutoDelete(true);
+}
+
+void EqualizerView::show()
+{
+ if (first)
+ {
+ first = false;
+ setIcon(SmallIcon("noatun"));
+ mWidget = new EqualizerWidget(this, "mWidget");
+ setMainWidget(mWidget);
+
+ bandsLayout = new QHBoxLayout(mWidget->bandsFrame,
+ 0, KDialog::spacingHint(), "bandsLayout");
+
+ connect(mWidget->preampSlider, SIGNAL(valueChanged(int)),
+ this, SLOT(setPreamp(int)));
+ connect(EQ, SIGNAL(preampChanged(int)),
+ this, SLOT(changedPreamp(int)));
+
+ mWidget->bandCount->setRange(EQ->minBands(), EQ->maxBands());
+ connect(mWidget->bandCount, SIGNAL(valueChanged(int)),
+ EQ, SLOT(setBands(int)));
+
+ QVBoxLayout *l = new QVBoxLayout(mWidget->presetFrame);
+ mPresets = new PresetList(mWidget->presetFrame, "mPresets");
+ l->addWidget(mPresets);
+
+ connect(mWidget->removePresetButton, SIGNAL(clicked()), SLOT(remove()));
+ connect(mWidget->addPresetButton, SIGNAL(clicked()), SLOT(create()));
+ connect(mWidget->resetEqButton, SIGNAL(clicked()), SLOT(reset()));
+
+ new KListViewItem(mPresets, i18n("Custom"));
+
+ connect(mPresets, SIGNAL(currentChanged(QListViewItem*)),
+ this, SLOT(select(QListViewItem*)));
+
+ connect(mPresets, SIGNAL(itemRenamed(QListViewItem*)),
+ this, SLOT(rename(QListViewItem*)));
+
+ // populate the preset list
+ QValueList<VPreset> presets = EQ->presets();
+ QValueList<VPreset>::Iterator it;
+ for (it=presets.begin(); it!=presets.end(); ++it)
+ {
+ created(*it);
+ }
+
+ connect(EQ, SIGNAL(created(VPreset)), SLOT(created(VPreset)));
+ connect(EQ, SIGNAL(renamed(VPreset)), SLOT(renamed(VPreset)));
+ connect(EQ, SIGNAL(removed(VPreset)), SLOT(removed(VPreset)));
+
+ mWidget->enabledCheckBox->setChecked(EQ->isEnabled());
+ connect(mWidget->enabledCheckBox, SIGNAL(toggled(bool)),
+ EQ, SLOT(setEnabled(bool)));
+ connect(EQ, SIGNAL(enabled(bool)),
+ mWidget->enabledCheckBox, SLOT(setChecked(bool)));
+
+ connect(EQ, SIGNAL(changed()),
+ this, SLOT(changedEq()));
+ connect(EQ, SIGNAL(changedBands()),
+ this, SLOT(changedBands()));
+
+ changedBands();
+ changedEq();
+ } // END if(first)
+
+ if (isVisible())
+ raise();
+ else
+ KDialogBase::show();
+}
+
+QListViewItem *EqualizerView::itemFor(const QString &filename)
+{
+ for (QListViewItem *i=mPresets->firstChild(); i!=0; i=i->itemBelow())
+ {
+ QString t = i->text(1);
+ if ((t.length()==0 && filename.length()==0) || t==filename)
+ return i;
+ }
+ return 0;
+}
+
+QListViewItem *EqualizerView::itemFor(const VPreset &preset)
+{
+ return itemFor(preset.file());
+}
+
+// why is it that when you move a QSlider up, it goes down?
+void EqualizerView::setPreamp(int x)
+{
+ EQ->setPreamp(-x);
+}
+
+void EqualizerView::changedPreamp(int x)
+{
+ mWidget->preampSlider->setValue(-x);
+}
+
+
+void EqualizerView::changedBands()
+{
+ mBands.clear();
+
+ VEqualizer &eq = *EQ;
+ for (int i=0; i < eq.bands(); ++i)
+ {
+ EqualizerLevel *l = new EqualizerLevel(mWidget->bandsFrame, eq[i]);
+ bandsLayout->addWidget(l);
+ l->show();
+ mBands.append(l);
+ }
+
+ mWidget->bandCount->setValue(eq.bands());
+ changedEq();
+}
+
+void EqualizerView::changedEq()
+{
+ if (!mGoingPreset)
+ {
+ QListViewItem *customitem = itemFor("");
+ if (!customitem) // this should never happen!
+ return;
+ mPresets->setSelected(customitem, true);
+ }
+}
+
+void EqualizerView::removed(VPreset p)
+{
+ delete itemFor(p);
+}
+
+void EqualizerView::created(VPreset p)
+{
+ // store the filename in QListViewItem::text(0)
+ QString n = p.name();
+ QString f = p.file();
+ new KListViewItem(mPresets, n, f);
+}
+
+void EqualizerView::renamed(VPreset p)
+{
+ QListViewItem *renamed = itemFor(p);
+ if (!renamed) // WTF !
+ {
+ created(p);
+ return;
+ }
+ renamed->setText(0, p.name());
+}
+
+void EqualizerView::remove()
+{
+ QListViewItem *current=mPresets->currentItem();
+ if (current->text(0)==i18n("Custom"))
+ return;
+ QListViewItem *then=current->itemAbove();
+ if (!then) then=current->itemBelow();
+
+ if (then)
+ mPresets->setSelected(then, true);
+
+ VPreset p = EQ->presetByFile(current->text(1));
+ p.remove();
+}
+
+void EqualizerView::create()
+{
+ VPreset p = EQ->createPreset(i18n("New Preset"));
+
+ mGoingPreset = true;
+
+ // Load the new preset
+ p.load();
+
+ // We should have just made a list view item for this preset
+ // See EquailizerView::presetAdded()
+ QListViewItem *i = itemFor(p);
+
+ if (i)
+ mPresets->setSelected(i, true);
+
+ mGoingPreset = false;
+}
+
+void EqualizerView::reset()
+{
+ VEqualizer &eq = *EQ;
+ eq.setPreamp(0);
+ for (int i=0; i < eq.bands(); ++i)
+ eq.band(i).setLevel(0);
+
+}
+
+void EqualizerView::rename(QListViewItem *item)
+{
+ EQ->presetByFile(item->text(1)).setName(item->text(0));
+ item->setText(0, EQ->presetByFile(item->text(1)).name());
+}
+
+void EqualizerView::select(QListViewItem *item)
+{
+ mGoingPreset = true;
+ EQ->presetByFile(item->text(1)).load();
+ mGoingPreset = false;
+ mWidget->removePresetButton->setEnabled(item->text(1).length());
+}
+
+#undef EQ
+#include "equalizerview.moc"
diff --git a/noatun/library/equalizerview.h b/noatun/library/equalizerview.h
new file mode 100644
index 00000000..98778fcc
--- /dev/null
+++ b/noatun/library/equalizerview.h
@@ -0,0 +1,91 @@
+#ifndef EQUALIZERVIEW_H
+#define EQUALIZERVIEW_H
+
+#include <qwidget.h>
+#include <kdialogbase.h>
+#include <klistview.h>
+
+class VBand;
+class QSlider;
+class QLabel;
+class QListViewItem;
+class VPreset;
+class QHBoxLayout;
+class EqualizerWidget;
+
+
+class EqualizerLevel : public QWidget
+{
+Q_OBJECT
+public:
+ EqualizerLevel(QWidget *parent, VBand band);
+
+public slots:
+ void changed();
+ void changed(int);
+
+ void setBand(VBand band);
+
+private:
+ VBand mBand;
+ QSlider *mSlider;
+ QLabel *mLabel;
+};
+
+
+class PresetList : public KListView
+{
+Q_OBJECT
+public:
+ PresetList(QWidget *parent, const char *name=0);
+
+public:
+ void rename(QListViewItem *item, int c);
+};
+
+
+class EqualizerView : public KDialogBase
+{
+Q_OBJECT
+ QPtrList<EqualizerLevel> mBands;
+
+public:
+ EqualizerView();
+ virtual void show();
+
+ QListViewItem *itemFor(const QString &filename);
+ QListViewItem *itemFor(const VPreset &preset);
+
+public slots:
+ void setPreamp(int);
+ void changedPreamp(int);
+
+private slots:
+ void changedBands();
+ void changedEq();
+
+ void removed(VPreset p);
+ void created(VPreset p);
+ void renamed(VPreset p);
+
+ void remove();
+ void create();
+ void reset();
+ void rename(QListViewItem *);
+ void select(QListViewItem*);
+
+private:
+ bool first;
+ EqualizerWidget *mWidget;
+ QHBoxLayout *bandsLayout;
+// QCheckBox *mOn;
+// QSlider *mPreamp;
+ PresetList *mPresets;
+ bool mGoingPreset;
+// QPushButton *mRemovePreset, *mAddPreset;
+// QFrame *mSliders;
+// KIntNumInput *mBandCount;
+};
+
+#endif
+
diff --git a/noatun/library/equalizerwidget.ui b/noatun/library/equalizerwidget.ui
new file mode 100644
index 00000000..1432f59a
--- /dev/null
+++ b/noatun/library/equalizerwidget.ui
@@ -0,0 +1,337 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>EqualizerWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>EqualizerWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>518</width>
+ <height>283</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Equalizer</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>Pre&amp;amp:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>preampSlider</cstring>
+ </property>
+ </widget>
+ <spacer row="1" column="2">
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Minimum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>1</width>
+ <height>8</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>spacer4_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Minimum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>1</width>
+ <height>8</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget" row="1" column="1">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QSlider">
+ <property name="name">
+ <cstring>preampSlider</cstring>
+ </property>
+ <property name="minValue">
+ <number>-200</number>
+ </property>
+ <property name="maxValue">
+ <number>200</number>
+ </property>
+ <property name="lineStep">
+ <number>0</number>
+ </property>
+ <property name="pageStep">
+ <number>25</number>
+ </property>
+ <property name="value">
+ <number>0</number>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="tickmarks">
+ <enum>Both</enum>
+ </property>
+ <property name="tickInterval">
+ <number>25</number>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>+/-</string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="1" column="1">
+ <property name="name">
+ <cstring>bandsGroupBox</cstring>
+ </property>
+ <property name="title">
+ <string>&amp;Bands</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>bandsFrame</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox" row="1" column="2">
+ <property name="name">
+ <cstring>presetsGroupBox</cstring>
+ </property>
+ <property name="title">
+ <string>&amp;Presets</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QFrame" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>presetFrame</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="0">
+ <property name="name">
+ <cstring>removePresetButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="0">
+ <property name="name">
+ <cstring>addPresetButton</cstring>
+ </property>
+ <property name="text">
+ <string>A&amp;dd</string>
+ </property>
+ </widget>
+ <spacer row="2" column="1">
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>8</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>enabledCheckBox</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Enabled</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Minimum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>8</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_3</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Number of bands:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>bandCount</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>bandCount</cstring>
+ </property>
+ <property name="maxValue">
+ <number>16</number>
+ </property>
+ <property name="value">
+ <number>6</number>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>8</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>resetEqButton</cstring>
+ </property>
+ <property name="text">
+ <string>Re&amp;set EQ</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<tabstops>
+ <tabstop>enabledCheckBox</tabstop>
+ <tabstop>bandCount</tabstop>
+ <tabstop>resetEqButton</tabstop>
+ <tabstop>preampSlider</tabstop>
+ <tabstop>removePresetButton</tabstop>
+ <tabstop>addPresetButton</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+</includehints>
+</UI>
diff --git a/noatun/library/gentable.cpp b/noatun/library/gentable.cpp
new file mode 100644
index 00000000..68e04162
--- /dev/null
+++ b/noatun/library/gentable.cpp
@@ -0,0 +1,1037 @@
+const float data[] =
+{
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 2,
+ 1.93113195896148681640625,
+ 1.78310096263885498046875,
+ 1.70247924327850341796875,
+ 1.624516010284423828125,
+ 1.548387050628662109375,
+ 1.49999988079071044921875,
+ 1.4569394588470458984375,
+ 1.4230768680572509765625,
+ 1.388888835906982421875,
+ 1.3620688915252685546875,
+ 1.3333332538604736328125,
+ 1.31213867664337158203125,
+ 1.28985500335693359375,
+ 1.27981650829315185546875,
+ 1.26315784454345703125,
+ 1.24999988079071044921875,
+ 1.2345678806304931640625,
+ 1.2253520488739013671875,
+ 1.21578943729400634765625,
+ 1.20482635498046875,
+ 1.192531108856201171875,
+ 1.18749988079071044921875,
+ 1.1790392398834228515625,
+ 1.1704347133636474609375,
+ 1.16666662693023681640625,
+ 1.1615383625030517578125,
+ 1.153512477874755859375,
+ 1.14848482608795166015625,
+ 1.143036365509033203125,
+ 1.13935959339141845703125,
+ 1.13513505458831787109375,
+ 1.1296908855438232421875,
+ 1.12499988079071044921875,
+ 1.12320911884307861328125,
+ 1.11992251873016357421875,
+ 1.11627900600433349609375,
+ 1.1115043163299560546875,
+ 1.1090908050537109375,
+ 1.1054852008819580078125,
+ 1.1032257080078125,
+ 1.099999904632568359375,
+ 1.0987341403961181640625,
+ 1.09541976451873779296875,
+ 1.09374988079071044921875,
+ 1.09090900421142578125,
+ 1.088842868804931640625,
+ 1.08620679378509521484375,
+ 1.08459866046905517578125,
+ 1.0833332538604736328125,
+ 1.08128535747528076171875,
+ 1.0793650150299072265625,
+ 1.07757294178009033203125,
+ 1.07648181915283203125,
+ 1.0740740299224853515625,
+ 1.07282412052154541015625,
+ 1.07149398326873779296875,
+ 1.0701754093170166015625,
+ 1.06882584095001220703125,
+ 1.0675990581512451171875,
+ 1.065758228302001953125,
+ 1.0645160675048828125,
+ 1.06382977962493896484375,
+ 1.06249988079071044921875,
+ 1.0615384578704833984375,
+ 1.0606060028076171875,
+ 1.05952370166778564453125,
+ 1.0579373836517333984375,
+ 1.05714285373687744140625,
+ 1.05629765987396240234375,
+ 1.055452823638916015625,
+ 1.05461633205413818359375,
+ 1.05350315570831298828125,
+ 1.05263149738311767578125,
+ 1.05194795131683349609375,
+ 1.05086696147918701171875,
+ 1.0499999523162841796875,
+ 1.04951560497283935546875,
+ 1.04892957210540771484375,
+ 1.0478632450103759765625,
+ 1.0472972393035888671875,
+ 1.04651153087615966796875,
+ 1.04577457904815673828125,
+ 1.04522609710693359375,
+ 1.04464280605316162109375,
+ 1.0439865589141845703125,
+ 1.0434782505035400390625,
+ 1.0425531864166259765625,
+ 1.04188477993011474609375,
+ 1.04166662693023681640625,
+ 1.04085254669189453125,
+ 1.0404479503631591796875,
+ 1.03986012935638427734375,
+ 1.03934001922607421875,
+ 1.03876841068267822265625,
+ 1.0384614467620849609375,
+ 1.03795063495635986328125,
+ 1.03748404979705810546875,
+ 1.03688514232635498046875,
+ 1.03637897968292236328125,
+ 1.03602182865142822265625,
+ 1.03559863567352294921875,
+ 1.03508770465850830078125,
+ 1.0347592830657958984375,
+ 1.03414630889892578125,
+ 1.03389823436737060546875,
+ 1.033333301544189453125,
+ 1.03316318988800048828125,
+ 1.03260862827301025390625,
+ 1.03225803375244140625,
+ 1.0319488048553466796875,
+ 1.0315067768096923828125,
+ 1.03124988079071044921875,
+ 1.03100764751434326171875,
+ 1.0304877758026123046875,
+ 1.0302250385284423828125,
+ 1.0298507213592529296875,
+ 1.0294573307037353515625,
+ 1.029239654541015625,
+ 1.02892553806304931640625,
+ 1.02875816822052001953125,
+ 1.02830183506011962890625,
+ 1.02789247035980224609375,
+ 1.02767288684844970703125,
+ 1.02745664119720458984375,
+ 1.02711856365203857421875,
+ 1.0268414020538330078125,
+ 1.0265486240386962890625,
+ 1.0263156890869140625,
+ 1.02608692646026611328125,
+ 1.02590668201446533203125,
+ 1.0255353450775146484375,
+ 1.02531635761260986328125,
+ 1.02504265308380126953125,
+ 1.0247933864593505859375,
+ 1.02454984188079833984375,
+ 1.02436733245849609375,
+ 1.02406990528106689453125,
+ 1.0238094329833984375,
+ 1.0236685276031494140625,
+ 1.0234191417694091796875,
+ 1.02319896221160888671875,
+ 1.0230023860931396484375,
+ 1.0227272510528564453125,
+ 1.02263367176055908203125,
+ 1.0223712921142578125,
+ 1.0221674442291259765625,
+ 1.0219669342041015625,
+ 1.02173912525177001953125,
+ 1.02160274982452392578125,
+ 1.02142846584320068359375,
+ 1.0211639404296875,
+ 1.0209677219390869140625,
+ 1.0208332538604736328125,
+ 1.02064216136932373046875,
+ 1.02050113677978515625,
+ 1.02027022838592529296875,
+ 1.0201342105865478515625,
+ 1.019999980926513671875,
+ 1.01986753940582275390625,
+ 1.0196077823638916015625,
+ 1.01941740512847900390625,
+ 1.01928365230560302734375,
+ 1.01915180683135986328125,
+ 1.01902878284454345703125,
+ 1.0188140869140625,
+ 1.01866245269775390625,
+ 1.0185184478759765625,
+ 1.01840484142303466796875,
+ 1.01818180084228515625,
+ 1.01807224750518798828125,
+ 1.017937183380126953125,
+ 1.017857074737548828125,
+ 1.0176470279693603515625,
+ 1.01752567291259765625,
+ 1.01736104488372802734375,
+ 1.01724135875701904296875,
+ 1.01709401607513427734375,
+ 1.01700675487518310546875,
+ 1.01684439182281494140625,
+ 1.0166666507720947265625,
+ 1.01657450199127197265625,
+ 1.01646339893341064453125,
+ 1.016326427459716796875,
+ 1.0161879062652587890625,
+ 1.016129016876220703125,
+ 1.01601827144622802734375,
+ 1.01583111286163330078125,
+ 1.01574802398681640625,
+ 1.01562488079071044921875,
+ 1.01554048061370849609375,
+ 1.01538455486297607421875,
+ 1.0152838230133056640625,
+ 1.015151500701904296875,
+ 1.01507270336151123046875,
+ 1.01495325565338134765625,
+ 1.01484715938568115234375,
+ 1.01473677158355712890625,
+ 1.01467132568359375,
+ 1.01453483104705810546875,
+ 1.01440918445587158203125,
+ 1.014302730560302734375,
+ 1.01425659656524658203125,
+ 1.0141642093658447265625,
+ 1.01401865482330322265625,
+ 1.0139102935791015625,
+ 1.01386821269989013671875,
+ 1.01374042034149169921875,
+ 1.01367175579071044921875,
+ 1.01355922222137451171875,
+ 1.013473033905029296875,
+ 1.013388156890869140625,
+ 1.01326954364776611328125,
+ 1.01322114467620849609375,
+ 1.0131232738494873046875,
+ 1.01303780078887939453125,
+ 1.01293098926544189453125,
+ 1.012861728668212890625,
+ 1.01279580593109130859375,
+ 1.01268112659454345703125,
+ 1.0126049518585205078125,
+ 1.01249992847442626953125,
+ 1.012468814849853515625,
+ 1.01236855983734130859375,
+ 1.0122928619384765625,
+ 1.012195110321044921875,
+ 1.01213753223419189453125,
+ 1.01205670833587646484375,
+ 1.0119571685791015625,
+ 1.01190471649169921875,
+ 1.011820316314697265625,
+ 1.01176464557647705078125,
+ 1.01167309284210205078125,
+ 1.01161289215087890625,
+ 1.0115451812744140625,
+ 1.011450290679931640625,
+ 1.011395931243896484375,
+ 1.011334896087646484375,
+ 1.011273860931396484375,
+ 1.0111939907073974609375,
+ 1.0111110210418701171875,
+ 1.01105844974517822265625,
+ 1.0109889507293701171875,
+ 1.0109169483184814453125,
+ 1.010869503021240234375,
+ 1.01078164577484130859375,
+ 1.0107219219207763671875,
+ 1.01063823699951171875,
+ 1.01058197021484375,
+ 1.01052629947662353515625,
+ 1.01044380664825439453125,
+ 1.0103969573974609375,
+ 1.0103447437286376953125,
+ 1.0103092193603515625,
+ 1.01023006439208984375,
+ 1.01018321514129638671875,
+ 1.01010096073150634765625,
+ 1.01004636287689208984375,
+ 1.0099833011627197265625,
+ 1.00994026660919189453125,
+ 1.0098683834075927734375,
+ 1.00980389118194580078125,
+ 1.00976550579071044921875,
+ 1.0097086429595947265625,
+ 1.00965249538421630859375,
+ 1.00961530208587646484375,
+ 1.0095388889312744140625,
+ 1.0094835758209228515625,
+ 1.00943386554718017578125,
+ 1.0093896389007568359375,
+ 1.0093457698822021484375,
+ 1.00925922393798828125,
+ 1.00923073291778564453125,
+ 1.00917422771453857421875,
+ 1.00911843776702880859375,
+ 1.009090900421142578125,
+ 1.0090415477752685546875,
+ 1.0089685916900634765625,
+ 1.0089285373687744140625,
+ 1.00888884067535400390625,
+ 1.00882351398468017578125,
+ 1.00877702236175537109375,
+ 1.00875270366668701171875,
+ 1.0086956024169921875,
+ 1.00864541530609130859375,
+ 1.0086123943328857421875,
+ 1.00854694843292236328125,
+ 1.00850331783294677734375,
+ 1.00846016407012939453125,
+ 1.008419036865234375,
+ 1.00836813449859619140625,
+ 1.00833332538604736328125,
+ 1.00828397274017333984375,
+ 1.00823962688446044921875,
+ 1.00819671154022216796875,
+ 1.00815546512603759765625,
+ 1.00809705257415771484375,
+ 1.0080687999725341796875,
+ 1.0080320835113525390625,
+ 1.0079839229583740234375,
+ 1.0079364776611328125,
+ 1.00790059566497802734375,
+ 1.007874011993408203125,
+ 1.00781857967376708984375,
+ 1.0077903270721435546875,
+ 1.0077369213104248046875,
+ 1.00769221782684326171875,
+ 1.00766277313232421875,
+ 1.00761687755584716796875,
+ 1.0075757503509521484375,
+ 1.0075511932373046875,
+ 1.00750744342803955078125,
+ 1.00747382640838623046875,
+ 1.0074441432952880859375,
+ 1.007380008697509765625,
+ 1.0073528289794921875,
+ 1.00732898712158203125,
+ 1.00728595256805419921875,
+ 1.00724637508392333984375,
+ 1.00722301006317138671875,
+ 1.0071890354156494140625,
+ 1.0071427822113037109375,
+ 1.00710475444793701171875,
+ 1.00707542896270751953125,
+ 1.00703394412994384765625,
+ 1.0070092678070068359375,
+ 1.00696861743927001953125,
+ 1.0069444179534912109375,
+ 1.00691235065460205078125,
+ 1.00686490535736083984375,
+ 1.0068492889404296875,
+ 1.00680267810821533203125,
+ 1.00676810741424560546875,
+ 1.0067365169525146484375,
+ 1.00670230388641357421875,
+ 1.006666660308837890625,
+ 1.0066444873809814453125,
+ 1.00661051273345947265625,
+ 1.006578922271728515625,
+ 1.00654447078704833984375,
+ 1.00651454925537109375,
+ 1.00649344921112060546875,
+ 1.006465435028076171875,
+ 1.00642049312591552734375,
+ 1.00639998912811279296875,
+ 1.0063693523406982421875,
+ 1.006329059600830078125,
+ 1.006311893463134765625,
+ 1.0062713623046875,
+ 1.00624024868011474609375,
+ 1.00621116161346435546875,
+ 1.0061790943145751953125,
+ 1.00615751743316650390625,
+ 1.00613486766815185546875,
+ 1.0060975551605224609375,
+ 1.006077289581298828125,
+ 1.0060422420501708984375,
+ 1.00602400302886962890625,
+ 1.005992412567138671875,
+ 1.005952358245849609375,
+ 1.0059320926666259765625,
+ 1.00591278076171875,
+ 1.00588226318359375,
+ 1.00585925579071044921875,
+ 1.00583088397979736328125,
+ 1.005802631378173828125,
+ 1.00577557086944580078125,
+ 1.0057470798492431640625,
+ 1.0057141780853271484375,
+ 1.0056979656219482421875,
+ 1.0056817531585693359375,
+ 1.00564968585968017578125,
+ 1.00561797618865966796875,
+ 1.00560224056243896484375,
+ 1.00556433200836181640625,
+ 1.00554430484771728515625,
+ 1.00552475452423095703125,
+ 1.00550043582916259765625,
+ 1.0054776668548583984375,
+ 1.0054495334625244140625,
+ 1.00542485713958740234375,
+ 1.00540268421173095703125,
+ 1.005376338958740234375,
+ 1.0053546428680419921875,
+ 1.00533330440521240234375,
+ 1.005319118499755859375,
+ 1.0052816867828369140625,
+ 1.0052630901336669921875,
+ 1.005244731903076171875,
+ 1.0052082538604736328125,
+ 1.00519025325775146484375,
+ 1.00516521930694580078125,
+ 1.00515186786651611328125,
+ 1.00512027740478515625,
+ 1.00510203838348388671875,
+ 1.00508248805999755859375,
+ 1.005056858062744140625,
+ 1.00504410266876220703125,
+ 1.00502192974090576171875,
+ 1.00499999523162841796875,
+ 1.0049750804901123046875,
+ 1.0049545764923095703125,
+ 1.00493824481964111328125,
+ 1.00490796566009521484375,
+ 1.00489127635955810546875,
+ 1.0048732757568359375,
+ 1.00485432147979736328125,
+ 1.004830837249755859375,
+ 1.00480759143829345703125,
+ 1.00479614734649658203125,
+ 1.00476181507110595703125,
+ 1.00474488735198974609375,
+ 1.00472247600555419921875,
+ 1.0047113895416259765625,
+ 1.0046837329864501953125,
+ 1.0046679973602294921875,
+ 1.00465381145477294921875,
+ 1.0046253204345703125,
+ 1.00460827350616455078125,
+ 1.00458705425262451171875,
+ 1.0045731067657470703125,
+ 1.00455367565155029296875,
+ 1.00453507900238037109375,
+ 1.0045135021209716796875,
+ 1.0044963359832763671875,
+ 1.00448429584503173828125,
+ 1.00446426868438720703125,
+ 1.0044443607330322265625,
+ 1.00442469120025634765625,
+ 1.0044116973876953125,
+ 1.00438594818115234375,
+ 1.00437152385711669921875,
+ 1.004350185394287109375,
+ 1.00433647632598876953125,
+ 1.00431025028228759765625,
+ 1.0043010711669921875,
+ 1.004278659820556640625,
+ 1.00425755977630615234375,
+ 1.0042402744293212890625,
+ 1.00422823429107666015625,
+ 1.00420868396759033203125,
+ 1.004192829132080078125,
+ 1.0041687488555908203125,
+ 1.00415790081024169921875,
+ 1.00414359569549560546875,
+ 1.00412642955780029296875,
+ 1.0041067600250244140625,
+ 1.004092693328857421875,
+ 1.00407683849334716796875,
+ 1.0040628910064697265625,
+ 1.0040452480316162109375,
+ 1.00403225421905517578125,
+ 1.00400960445404052734375,
+ 1.0039999485015869140625,
+ 1.00398170948028564453125,
+ 1.00396823883056640625,
+ 1.003952503204345703125,
+ 1.0039370059967041015625,
+ 1.00391638278961181640625,
+ 1.00390613079071044921875,
+ 1.0038909912109375,
+ 1.00387585163116455078125,
+ 1.003860950469970703125,
+ 1.00384604930877685546875,
+ 1.0038239955902099609375,
+ 1.003810882568359375,
+ 1.00378787517547607421875,
+ 1.0037782192230224609375,
+ 1.0037634372711181640625,
+ 1.003753662109375,
+ 1.0037348270416259765625,
+ 1.0037243366241455078125,
+ 1.003703594207763671875,
+ 1.0036900043487548828125,
+ 1.00367641448974609375,
+ 1.00365960597991943359375,
+ 1.00364959239959716796875,
+ 1.00363194942474365234375,
+ 1.00362050533294677734375,
+ 1.003606319427490234375,
+ 1.0035927295684814453125,
+ 1.003577709197998046875,
+ 1.00356709957122802734375,
+ 1.0035459995269775390625,
+ 1.0035398006439208984375,
+ 1.00352108478546142578125,
+ 1.00350868701934814453125,
+ 1.0034964084625244140625,
+ 1.0034883022308349609375,
+ 1.00347220897674560546875,
+ 1.0034601688385009765625,
+ 1.0034482479095458984375,
+ 1.0034313201904296875,
+ 1.00342261791229248046875,
+ 1.00340712070465087890625,
+ 1.003391742706298828125,
+ 1.00337827205657958984375,
+ 1.003366947174072265625,
+ 1.00335562229156494140625,
+ 1.0033333301544189453125,
+ 1.00332772731781005859375,
+ 1.00331342220306396484375,
+ 1.003302097320556640625,
+ 1.0032875537872314453125,
+ 1.00327503681182861328125,
+ 1.003261566162109375,
+ 1.00324666500091552734375,
+ 1.00323617458343505859375,
+ 1.003225803375244140625,
+ 1.00321018695831298828125,
+ 1.00320339202880859375,
+ 1.00318801403045654296875,
+ 1.00317656993865966796875,
+ 1.0031645298004150390625,
+ 1.0031511783599853515625,
+ 1.0031425952911376953125,
+ 1.0031268596649169921875,
+ 1.00311768054962158203125,
+ 1.00310552120208740234375,
+ 1.0030958652496337890625,
+ 1.00308477878570556640625,
+ 1.00307214260101318359375,
+ 1.003063678741455078125,
+ 1.00304877758026123046875,
+ 1.00303637981414794921875,
+ 1.003030300140380859375,
+ 1.003017425537109375,
+ 1.00300967693328857421875,
+ 1.00299394130706787109375,
+ 1.00298798084259033203125,
+ 1.00297462940216064453125,
+ 1.00296294689178466796875,
+ 1.00295555591583251953125,
+ 1.00294458866119384765625,
+ 1.00293254852294921875,
+ 1.0029239654541015625,
+ 1.00290691852569580078125,
+ 1.0028984546661376953125,
+ 1.00288593769073486328125,
+ 1.00287353992462158203125,
+ 1.002865314483642578125,
+ 1.0028550624847412109375,
+ 1.00284087657928466796875,
+ 1.00283014774322509765625,
+ 1.0028247833251953125,
+ 1.00280892848968505859375,
+ 1.00279712677001953125,
+ 1.00278937816619873046875,
+ 1.00277769565582275390625,
+ 1.00276815891265869140625,
+ 1.00275981426239013671875,
+ 1.00274717807769775390625,
+ 1.00273716449737548828125,
+ 1.00272655487060546875,
+ 1.00271737575531005859375,
+ 1.0027062892913818359375,
+ 1.00269830226898193359375,
+ 1.0026881694793701171875,
+ 1.00267612934112548828125,
+ 1.00266802310943603515625,
+ 1.0026581287384033203125,
+ 1.0026471614837646484375,
+ 1.00263845920562744140625,
+ 1.00263154506683349609375,
+ 1.00261914730072021484375,
+ 1.0026109218597412109375,
+ 1.00260007381439208984375,
+ 1.002590656280517578125,
+ 1.00258123874664306640625,
+ 1.00257396697998046875,
+ 1.00256407260894775390625,
+ 1.0025575160980224609375,
+ 1.00254929065704345703125,
+ 1.0025379657745361328125,
+ 1.002526760101318359375,
+ 1.00252091884613037109375,
+ 1.002512454986572265625,
+ 1.00250303745269775390625,
+ 1.00249683856964111328125,
+ 1.00248754024505615234375,
+ 1.0024797916412353515625,
+ 1.00246906280517578125,
+ 1.00246298313140869140625,
+ 1.0024509429931640625,
+ 1.0024464130401611328125,
+ 1.00243747234344482421875,
+ 1.00242710113525390625,
+ 1.0024154186248779296875,
+ 1.00240957736968994140625,
+ 1.002398014068603515625,
+ 1.002389430999755859375,
+ 1.002380847930908203125,
+ 1.0023696422576904296875,
+ 1.00236117839813232421875,
+ 1.0023515224456787109375,
+ 1.00234317779541015625,
+ 1.00233638286590576171875,
+ 1.002325534820556640625,
+ 1.0023148059844970703125,
+ 1.00230944156646728515625,
+ 1.00230228900909423828125,
+ 1.00229179859161376953125,
+ 1.0022830963134765625,
+ 1.00227272510528564453125,
+ 1.00226497650146484375,
+ 1.0022590160369873046875,
+ 1.0022487640380859375,
+ 1.00224208831787109375,
+ 1.00223338603973388671875,
+ 1.00222551822662353515625,
+ 1.00221729278564453125,
+ 1.002211093902587890625,
+ 1.00220263004302978515625,
+ 1.002192974090576171875,
+ 1.00218498706817626953125,
+ 1.00217616558074951171875,
+ 1.00217151641845703125,
+ 1.00216448307037353515625,
+ 1.0021550655364990234375,
+ 1.0021469593048095703125,
+ 1.00214016437530517578125,
+ 1.00213325023651123046875,
+ 1.0021264553070068359375,
+ 1.002118587493896484375,
+ 1.00210964679718017578125,
+ 1.002105236053466796875,
+ 1.0020964145660400390625,
+ 1.00208985805511474609375,
+ 1.002083301544189453125,
+ 1.00207459926605224609375,
+ 1.00206816196441650390625,
+ 1.00206077098846435546875,
+ 1.00205433368682861328125,
+ 1.00204908847808837890625,
+ 1.00204074382781982421875,
+ 1.0020325183868408203125,
+ 1.00202834606170654296875,
+ 1.00202286243438720703125,
+ 1.00201475620269775390625,
+ 1.002007961273193359375,
+ 1.00199997425079345703125,
+ 1.00199401378631591796875,
+ 1.0019893646240234375,
+ 1.0019814968109130859375,
+ 1.0019762516021728515625,
+ 1.00196945667266845703125,
+ 1.00196325778961181640625,
+ 1.001956939697265625,
+ 1.001949310302734375,
+ 1.00194299221038818359375,
+ 1.00193417072296142578125,
+ 1.00192487239837646484375,
+ 1.0019180774688720703125,
+ 1.00191199779510498046875,
+ 1.00190293788909912109375,
+ 1.00189387798309326171875,
+ 1.00188791751861572265625,
+ 1.0018813610076904296875,
+ 1.00187265872955322265625,
+ 1.0018656253814697265625,
+ 1.00185871124267578125,
+ 1.0018517971038818359375,
+ 1.00184500217437744140625,
+ 1.001838207244873046875,
+ 1.00183141231536865234375,
+ 1.00182473659515380859375,
+ 1.001818180084228515625,
+ 1.001811504364013671875,
+ 1.00180494785308837890625,
+ 1.0017974376678466796875,
+ 1.00179207324981689453125,
+ 1.00178563594818115234375,
+ 1.00177776813507080078125,
+ 1.00177085399627685546875,
+ 1.001765727996826171875,
+ 1.00175893306732177734375,
+ 1.00175130367279052734375,
+ 1.00174510478973388671875,
+ 1.00174009799957275390625,
+ 1.0017330646514892578125,
+ 1.00172555446624755859375,
+ 1.00172007083892822265625,
+ 1.001715183258056640625,
+ 1.00170791149139404296875,
+ 1.0017006397247314453125,
+ 1.0016958713531494140625,
+ 1.00169050693511962890625,
+ 1.0016834735870361328125,
+ 1.0016777515411376953125,
+ 1.00167214870452880859375,
+ 1.00166666507720947265625,
+ 1.0016610622406005859375,
+ 1.00165557861328125,
+ 1.0016500949859619140625,
+ 1.00164473056793212890625,
+ 1.00163924694061279296875,
+ 1.0016338825225830078125,
+ 1.0016286373138427734375,
+ 1.0016224384307861328125,
+ 1.00161802768707275390625,
+ 1.0016129016876220703125,
+ 1.00160634517669677734375,
+ 1.001600742340087890625,
+ 1.00159657001495361328125,
+ 1.00159108638763427734375,
+ 1.0015847682952880859375,
+ 1.001579761505126953125,
+ 1.00157558917999267578125,
+ 1.0015697479248046875,
+ 1.00156366825103759765625,
+ 1.00155913829803466796875,
+ 1.0015552043914794921875,
+ 1.00154912471771240234375,
+ 1.00154316425323486328125,
+ 1.0015392303466796875,
+ 1.00153481960296630859375,
+ 1.0015289783477783203125,
+ 1.00152432918548583984375,
+ 1.001519680023193359375,
+ 1.0015151500701904296875,
+ 1.00151050090789794921875,
+ 1.00150597095489501953125,
+ 1.00150144100189208984375,
+ 1.00149691104888916015625,
+ 1.00149250030517578125,
+ 1.00148808956146240234375,
+ 1.0014836788177490234375,
+ 1.00147855281829833984375,
+ 1.001474857330322265625,
+ 1.0014705657958984375,
+ 1.00146520137786865234375,
+ 1.001457691192626953125,
+ 1.00145232677459716796875,
+ 1.0014450550079345703125,
+ 1.0014398097991943359375,
+ 1.0014326572418212890625,
+ 1.00142753124237060546875,
+ 1.00142037868499755859375,
+ 1.00141537189483642578125,
+ 1.0014083385467529296875,
+ 1.00140345096588134765625,
+ 1.00139653682708740234375,
+ 1.00139176845550537109375,
+ 1.0013849735260009765625,
+ 1.0013802051544189453125,
+ 1.0013735294342041015625,
+ 1.00136888027191162109375,
+ 1.001362323760986328125,
+ 1.00135767459869384765625,
+ 1.00135123729705810546875,
+ 1.00134670734405517578125,
+ 1.001340389251708984375,
+ 1.00133597850799560546875,
+ 1.00132977962493896484375,
+ 1.0013253688812255859375,
+ 1.0013191699981689453125,
+ 1.0013148784637451171875,
+ 1.00130879878997802734375,
+ 1.00130462646484375,
+ 1.0012986660003662109375,
+ 1.00129449367523193359375,
+ 1.0012886524200439453125,
+ 1.00128448009490966796875,
+ 1.00127875804901123046875,
+ 1.001274585723876953125,
+ 1.00126898288726806640625,
+ 1.00126492977142333984375,
+ 1.001259326934814453125,
+ 1.00125539302825927734375,
+ 1.00124990940093994140625,
+ 1.00124609470367431640625,
+ 1.00124061107635498046875,
+ 1.00123679637908935546875,
+ 1.0012314319610595703125,
+ 1.00122773647308349609375,
+ 1.00122249126434326171875,
+ 1.00121867656707763671875,
+ 1.001213550567626953125,
+ 1.00120985507965087890625,
+ 1.0012047290802001953125,
+ 1.001201152801513671875,
+ 1.0011961460113525390625,
+ 1.001192569732666015625,
+ 1.0011875629425048828125,
+ 1.00118410587310791015625,
+ 1.001179218292236328125,
+ 1.00117576122283935546875,
+ 1.0011708736419677734375,
+ 1.0011675357818603515625,
+ 1.0011627674102783203125,
+ 1.00115931034088134765625,
+ 1.0011546611785888671875,
+ 1.0011513233184814453125,
+ 1.00114667415618896484375,
+ 1.00114345550537109375,
+ 1.0011389255523681640625,
+ 1.00113570690155029296875,
+ 1.00113117694854736328125,
+ 1.0011279582977294921875,
+ 1.00112354755401611328125,
+ 1.00112044811248779296875,
+ 1.0011160373687744140625,
+ 1.00111293792724609375,
+ 1.001108646392822265625,
+ 1.0011055469512939453125,
+ 1.0011012554168701171875,
+ 1.00109827518463134765625,
+ 1.00109398365020751953125,
+ 1.00109100341796875,
+ 1.0010869503021240234375,
+ 1.00108397006988525390625,
+ 1.0010797977447509765625,
+ 1.0010769367218017578125,
+ 1.00107288360595703125,
+ 1.0010700225830078125,
+ 1.00106608867645263671875,
+ 1.00106322765350341796875,
+ 1.0010592937469482421875,
+ 1.0010564327239990234375,
+ 1.0010526180267333984375,
+ 1.0010497570037841796875,
+ 1.0010459423065185546875,
+ 1.00104320049285888671875,
+ 1.00103938579559326171875,
+ 1.00103676319122314453125,
+ 1.00103294849395751953125,
+ 1.00103032588958740234375,
+ 1.001026630401611328125,
+ 1.0010240077972412109375,
+ 1.00102031230926513671875,
+ 1.0010178089141845703125,
+ 1.00101411342620849609375,
+ 1.0010116100311279296875,
+ 1.00100803375244140625,
+ 1.00100553035736083984375,
+ 1.00100195407867431640625,
+ 1.00099945068359375,
+ 1.00099599361419677734375,
+ 1.0009934902191162109375,
+ 1.00099003314971923828125,
+ 1.00098764896392822265625,
+ 1.00098419189453125,
+ 1.000981807708740234375,
+ 1.0009784698486328125,
+ 1.0009746551513671875,
+ 1.00096893310546875,
+ 1.00096333026885986328125,
+ 1.00095784664154052734375,
+ 1.00095236301422119140625,
+ 1.00094687938690185546875,
+ 1.0009415149688720703125,
+ 1.0009362697601318359375,
+ 1.0009310245513916015625,
+ 1.00092589855194091796875,
+ 1.000920772552490234375,
+ 1.00091564655303955078125,
+ 1.00091063976287841796875,
+ 1.0009057521820068359375,
+ 1.00090086460113525390625,
+ 1.000895977020263671875,
+ 1.000891208648681640625,
+ 1.000886440277099609375,
+ 1.00088179111480712890625,
+ 1.0008771419525146484375,
+ 1.00087249279022216796875,
+ 1.00086796283721923828125,
+ 1.000863552093505859375,
+ 1.0008590221405029296875,
+ 1.00085461139678955078125,
+ 1.00085031986236572265625,
+ 1.00084590911865234375,
+ 1.00084173679351806640625,
+ 1.00083744525909423828125,
+ 1.0008332729339599609375,
+ 1.00082910060882568359375,
+ 1.00082504749298095703125,
+ 1.00082099437713623046875,
+ 1.00081694126129150390625,
+ 1.000813007354736328125,
+ 1.0008089542388916015625,
+ 1.0008051395416259765625,
+ 1.00080120563507080078125,
+ 1.00079739093780517578125,
+ 1.00079357624053955078125,
+ 1.0007898807525634765625,
+ 1.0007860660552978515625,
+ 1.00078237056732177734375,
+ 1.00077879428863525390625,
+ 1.0007750988006591796875,
+ 1.00077152252197265625,
+ 1.0007679462432861328125,
+ 1.00076448917388916015625,
+ 1.0007610321044921875,
+ 1.00075757503509521484375,
+ 1.0007541179656982421875,
+ 1.00075066089630126953125,
+ 1.00074732303619384765625,
+ 1.00074398517608642578125,
+ 1.00074064731597900390625,
+ 1.0007374286651611328125,
+ 1.00073421001434326171875,
+ 1.000730991363525390625,
+ 1.00072777271270751953125,
+ 1.0007245540618896484375,
+ 1.000721454620361328125,
+ 1.0007183551788330078125,
+ 1.0007152557373046875,
+ 1.0007121562957763671875,
+ 1.00070917606353759765625,
+ 1.000706195831298828125,
+ 1.00070321559906005859375,
+ 1.0007002353668212890625,
+ 1.00069725513458251953125,
+ 1.00069439411163330078125,
+ 1.00069153308868408203125,
+ 1.00068867206573486328125,
+ 1.00068581104278564453125,
+ 1.00068295001983642578125,
+ 1.0006802082061767578125,
+ 1.00067746639251708984375,
+ 1.000674724578857421875,
+ 1.00067198276519775390625,
+ 1.0006692409515380859375,
+ 1.00066661834716796875,
+ 1.0006639957427978515625,
+ 1.000661373138427734375,
+ 1.0006587505340576171875,
+ 1.0006561279296875,
+ 1.0006535053253173828125,
+ 1.00065100193023681640625,
+ 1.00064849853515625,
+ 1.0006458759307861328125,
+ 1.0006434917449951171875,
+ 1.00064098834991455078125,
+ 1.000638484954833984375,
+ 1.00063610076904296875,
+ 1.00063359737396240234375,
+ 1.00063121318817138671875,
+ 1.00062882900238037109375,
+ 1.00062656402587890625,
+ 1.000624179840087890625,
+ 1.000621795654296875,
+ 1.00061953067779541015625,
+ 1.0006172657012939453125,
+ 1.00061500072479248046875,
+ 1.000612735748291015625,
+ 1.00061047077178955078125,
+ 1.0006082057952880859375,
+ 1.000606060028076171875,
+ 1.00060379505157470703125,
+ 1.00060164928436279296875,
+ 1.00059950351715087890625,
+ 1.00059735774993896484375,
+ 1.00059521198272705078125,
+ 1.00059306621551513671875,
+ 1.00059092044830322265625,
+ 1.000588893890380859375,
+ 1.0005867481231689453125,
+ 1.00058472156524658203125,
+ 1.00058269500732421875,
+ 1.00058066844940185546875,
+ 1.0005786418914794921875,
+ 1.00057661533355712890625,
+ 1.00057470798492431640625,
+ 1.000572681427001953125,
+ 1.000570774078369140625,
+ 1.00056874752044677734375,
+ 1.00056684017181396484375,
+ 1.00056493282318115234375,
+ 1.00056302547454833984375,
+ 1.00056111812591552734375,
+ 1.00055921077728271484375,
+ 1.00055730342864990234375,
+ 1.000555515289306640625,
+ 1.000553607940673828125,
+ 1.00055181980133056640625,
+ 1.0005500316619873046875,
+ 1.00054824352264404296875,
+ 1.00054633617401123046875,
+ 1.00054454803466796875,
+ 1.0005428791046142578125,
+ 1.00054109096527099609375,
+ 1.000539302825927734375,
+ 1.0005376338958740234375,
+ 1.00053584575653076171875,
+ 1.00053417682647705078125,
+ 1.0005323886871337890625,
+ 1.000530719757080078125,
+ 1.0005290508270263671875,
+ 1.00052738189697265625,
+ 1.0005257129669189453125,
+ 1.000524044036865234375,
+ 1.0005223751068115234375,
+ 1.00052082538604736328125,
+ 1.00051915645599365234375,
+ 1.00051748752593994140625,
+ 1.00051593780517578125,
+ 1.00051438808441162109375,
+ 1.00051271915435791015625,
+ 1.00051116943359375,
+ 1.00050961971282958984375,
+ 1.0005080699920654296875,
+ 1.00050652027130126953125,
+ 1.000504970550537109375,
+ 1.00050342082977294921875,
+ 1.00050199031829833984375,
+ 1.0005004405975341796875,
+ 1.00049889087677001953125,
+ 1.00049746036529541015625,
+ 1.00049602985382080078125,
+ 1.000494480133056640625,
+ 1.00049304962158203125,
+ 1.000491619110107421875,
+ 1.0004901885986328125,
+ 1.000488758087158203125
+};
+
+
+#include <unistd.h>
+
+int main()
+{
+ ::write(1, data, sizeof(data));
+}
+
diff --git a/noatun/library/globalvideo.h b/noatun/library/globalvideo.h
new file mode 100644
index 00000000..e0f28cb0
--- /dev/null
+++ b/noatun/library/globalvideo.h
@@ -0,0 +1,26 @@
+#ifndef __GLOBALVIDEO_H
+#define __GLOBALVIDEO_H
+
+#include "noatun/video.h"
+
+
+class GlobalVideo : public QWidget
+{
+Q_OBJECT
+ QPopupMenu *menu;
+ VideoFrame *video;
+
+public:
+ GlobalVideo();
+
+public slots:
+ void appear();
+ void hide();
+ void slotAdaptSize(int w, int h);
+
+protected:
+ void mouseReleaseEvent(QMouseEvent *e);
+};
+
+
+#endif
diff --git a/noatun/library/ksaver.cpp b/noatun/library/ksaver.cpp
new file mode 100644
index 00000000..f1cc6a61
--- /dev/null
+++ b/noatun/library/ksaver.cpp
@@ -0,0 +1,184 @@
+// ksaver.cpp
+//
+// Copyright (C) 2001 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <klocale.h>
+#include <ktempfile.h>
+#include <kio/netaccess.h>
+
+#include "ksaver.h"
+
+class Noatun::KSaver::KSaverPrivate
+{
+public:
+ KSaverPrivate() : isLocal(true), tempFile(0), file(0), textStream(0), dataStream(0) {};
+ bool isLocal;
+ KTempFile *tempFile;
+ QFile *file;
+ KURL url;
+ QString error;
+ QTextStream *textStream;
+ QDataStream *dataStream;
+};
+
+Noatun::KSaver::KSaver(const KURL &_target)
+{
+ d = new KSaverPrivate;
+ d->url = _target;
+
+ if(d->url.protocol() == "file")
+ {
+ d->isLocal = true;
+ d->file = new QFile(d->url.path());
+ }
+ else
+ {
+ d->isLocal = false;
+ }
+}
+
+Noatun::KSaver::~KSaver()
+{
+ close();
+ delete d;
+}
+
+bool Noatun::KSaver::open(void)
+{
+ if(d->isLocal)
+ {
+ if(d->file->open(IO_WriteOnly))
+ {
+ return true;
+ }
+ else
+ {
+ d->error = i18n("Could not write to %1.").arg(d->url.prettyURL());
+ return false;
+ }
+ }
+ else
+ {
+ d->tempFile = new KTempFile;
+ return true;
+ }
+}
+
+bool Noatun::KSaver::close(void)
+{
+ if(!d->isLocal && d->tempFile)
+ delete d->textStream;
+ d->textStream = 0;
+
+ if(!d->isLocal && d->tempFile)
+ delete d->dataStream;
+ d->dataStream = 0;
+
+ if(d->isLocal)
+ {
+ if(!d->file) return true;
+
+ delete d->file;
+ d->file = 0;
+ return true;
+ }
+ else
+ {
+ if(!d->tempFile) return true;
+
+ d->tempFile->close();
+ d->textStream = 0;
+ d->dataStream = 0;
+
+ bool retval = KIO::NetAccess::upload(d->tempFile->name(), d->url);
+
+ delete d->tempFile;
+ d->tempFile = 0;
+
+ return retval;
+ }
+}
+
+QString Noatun::KSaver::error(void)
+{
+ return d->error;
+}
+
+QFile &Noatun::KSaver::file(void)
+{
+ if(d->isLocal && d->file)
+ return *d->file;
+ else if(!d->isLocal && d->tempFile)
+ return *d->tempFile->file();
+ else
+ return *static_cast<QFile *>(0);
+}
+
+QTextStream &Noatun::KSaver::textStream()
+{
+ if(d->textStream)
+ {
+ return *d->textStream;
+ }
+ else if(d->isLocal && d->file)
+ {
+ d->textStream = new QTextStream(d->file);
+ return *d->textStream;
+ }
+ else if(!d->isLocal && d->tempFile)
+ {
+ d->textStream = d->tempFile->textStream();
+ return *d->textStream;
+ }
+ else
+ {
+ return *static_cast<QTextStream *>(0);
+ }
+}
+
+QDataStream &Noatun::KSaver::dataStream()
+{
+ if(d->dataStream)
+ {
+ return *d->dataStream;
+ }
+ else if(d->isLocal && d->file)
+ {
+ d->dataStream = new QDataStream(d->file);
+ return *d->dataStream;
+ }
+ else if(!d->isLocal && d->tempFile)
+ {
+ d->dataStream = d->tempFile->dataStream();
+ return *d->dataStream;
+ }
+ else
+ {
+ return *static_cast<QDataStream *>(0);
+ }
+}
diff --git a/noatun/library/ksaver.h b/noatun/library/ksaver.h
new file mode 100644
index 00000000..e55b2c0d
--- /dev/null
+++ b/noatun/library/ksaver.h
@@ -0,0 +1,102 @@
+// Copyright (C) 2001 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifndef KSAVER_H
+#define KSAVER_H
+
+#include <kurl.h>
+#include <qfile.h>
+#include <qstring.h>
+#include <qtextstream.h>
+#include <qdatastream.h>
+
+namespace Noatun
+{
+
+/**
+ * KSaver provides a way to save files in the most efficient way for
+ * both local and remote URLs.
+ */
+class KSaver
+{
+public:
+ /**
+ * The constructor takes the url and decides the best way to save,
+ * which will mean using something like KIO::NetAccess or QFile.
+ */
+ KSaver(const KURL &_target);
+
+ /**
+ * The destructor closes if necessary.
+ */
+ ~KSaver();
+
+ /**
+ * open actually tries to open the file
+ *
+ * true on success, false on failure (get the error in @ref error)
+ */
+ bool open(void);
+
+ /**
+ * close closes the file, and uploads if necessary.
+ *
+ * true on success, false on failure (get the error in @ref error)
+ */
+ bool close(void);
+
+ /**
+ * If open or close returns false, there was an error, and error
+ * returns what the error was, when available.
+ */
+ QString error(void);
+
+ /**
+ * file returns a QFile open for writing, which may be for a temporary
+ * file on the local filesystem.
+ *
+ * If this is called before the file is opened, you will crash.
+ */
+ QFile &file(void);
+
+ /**
+ * You can use this to write out your data.
+ *
+ * If this is called before the file is opened, you will crash.
+ */
+ QTextStream &textStream(void);
+
+ /**
+ * You can use this to write out your data.
+ *
+ * If this is called before the file is opened, you will crash.
+ */
+ QDataStream &dataStream(void);
+private:
+ class KSaverPrivate;
+ KSaverPrivate *d;
+};
+
+}
+
+#endif
diff --git a/noatun/library/mimetypetree.cpp b/noatun/library/mimetypetree.cpp
new file mode 100644
index 00000000..c2b60c81
--- /dev/null
+++ b/noatun/library/mimetypetree.cpp
@@ -0,0 +1,64 @@
+#include "mimetypetree.h"
+#include <kmimetype.h>
+#include <qdict.h>
+#include <qheader.h>
+
+
+MimeTypeTree::MimeTypeTree(QWidget *parent)
+ : KListView(parent)
+{
+ KMimeType::List list=KMimeType::allMimeTypes();
+ QDict<QListViewItem> map;
+ setRootIsDecorated(true);
+
+ addColumn("-");
+ header()->hide();
+ QValueListIterator<KMimeType::Ptr> i(list.begin());
+ for (; i != list.end(); ++i)
+ {
+ QString mimetype = (*i)->name();
+ int slash = mimetype.find("/");
+ QString major = mimetype.left(slash);
+
+ // hide all and inode majors
+ if (major == "all" || major=="inode")
+ continue;
+
+ QString minor = mimetype.mid(slash+1);
+ QListViewItem *majorItem=map[major];
+ if (!majorItem)
+ {
+ majorItem=addMajor(major);
+ map.insert(major, majorItem);
+ }
+
+ new QListViewItem(majorItem, minor);
+ }
+}
+
+void MimeTypeTree::sel(QListViewItem *item)
+{
+ QListViewItem *p=item->parent();
+ if (!p) return;
+ QString major=p->text(0);
+ QString minor=item->text(0);
+
+ emit selected(major+'/'+minor);
+}
+
+QListViewItem* MimeTypeTree::addMajor(const QString &name)
+{
+ return new QListViewItem(this, name);
+}
+
+
+// GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666
+// 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL
+// GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666
+// 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL
+// GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666
+// 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL
+// GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666
+// 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL
+
+#include "mimetypetree.moc"
diff --git a/noatun/library/mimetypetree.h b/noatun/library/mimetypetree.h
new file mode 100644
index 00000000..1f315526
--- /dev/null
+++ b/noatun/library/mimetypetree.h
@@ -0,0 +1,35 @@
+/***
+ * Copyright (c) 2001 Charles Samuels <charles@kde.org>
+ * Standard BSD License. Second version.
+ * The added stipulation is that this cannot link
+ * to GPL code. Except in the explicit case
+ * of Noatun linking to this, and to a GPL plugin,
+ * where the GPL plugin does not use any code
+ * in this class. However, it may link directly
+ * to the Qt Library, where Qt may be under any license.
+ *
+ * Debian, Gnome, and GNU must ALL DIE.
+ * Especially GNU's stupid info pages.
+ **/
+#ifndef MIMETYPETREE_H
+#define MIMETYPETREE_H
+
+#include <klistview.h>
+
+class MimeTypeTree : public KListView
+{
+Q_OBJECT
+public:
+ MimeTypeTree(QWidget *parent);
+
+private:
+ QListViewItem *addMajor(const QString &name);
+private slots:
+ void sel(QListViewItem *item);
+
+signals:
+ void selected(const QString &mimetype);
+};
+
+#endif
+
diff --git a/noatun/library/noatun/Makefile.am b/noatun/library/noatun/Makefile.am
new file mode 100644
index 00000000..115760b0
--- /dev/null
+++ b/noatun/library/noatun/Makefile.am
@@ -0,0 +1,10 @@
+
+noatuninclude_HEADERS = \
+ effects.h playlist.h plugin.h \
+ controls.h engine.h pref.h pluginloader.h \
+ conversion.h equalizer.h stdaction.h scrollinglabel.h \
+ downloader.h app.h player.h stereobuttonaction.h \
+ playlistsaver.h video.h vequalizer.h
+
+noatunincludedir = $(includedir)/noatun
+
diff --git a/noatun/library/noatun/app.h b/noatun/library/noatun/app.h
new file mode 100644
index 00000000..c4effa4d
--- /dev/null
+++ b/noatun/library/noatun/app.h
@@ -0,0 +1,271 @@
+#ifndef NOATUN_H
+#define NOATUN_H
+
+#include <kuniqueapplication.h>
+#include <kdemacros.h>
+class Playlist;
+class Player;
+class LibraryLoader;
+class KPopupMenu;
+class NoatunPreferences;
+class Downloader;
+class Effects;
+class EffectView;
+class Equalizer;
+class KDialogBase;
+class VEqualizer;
+
+namespace NoatunStdAction
+{
+ class PluginActionMenu;
+}
+
+/**
+ * @class NoatunApp app.h noatun/app.h
+ * Can be accessed from every plugin by using "napp"
+ *
+ * @short Noatun Application class
+ * @author Charles Samuels
+ * @version 2.3
+ */
+class KDE_EXPORT NoatunApp : public KUniqueApplication
+{
+Q_OBJECT
+friend class Playlist;
+
+public:
+ NoatunApp();
+ ~NoatunApp();
+
+ /**
+ * Provides access to the central playlist object.
+ * Any plugin can access the noatun playlist with
+ * <pre>napp->playlist();</pre>
+ **/
+ Playlist *playlist() const;
+ /**
+ * access to the central player object
+ * Any plugin can access the noatun player backend with
+ * <pre>napp->playlist();</pre>
+ **/
+ Player *player() const { return mPlayer; }
+ /**
+ * access to NoatunPreferences
+ **/
+ NoatunPreferences *preferencesBox() const { return mPref; }
+
+ /**
+ * @return a list of mimetypes aRts (and thus Noatun) can play
+ * KFileDialog accepts this QString instead of the shell globs in
+ * its static functions, make use of it :)
+ **/
+ QString mimeTypes();
+
+ LibraryLoader *libraryLoader() const { return mLibraryLoader; }
+ Downloader *downloader() const { return mDownloader; }
+ static QImage readPNG(const QString &filename);
+ Effects *effects() const;
+ ::Equalizer *equalizer() const { return mEqualizer; }
+ ::VEqualizer *vequalizer();
+ KDialogBase *equalizerView() const { return mEqualizerView; }
+
+ QCString version() const;
+
+ virtual void commitData(QSessionManager &);
+ virtual void saveState(QSessionManager &);
+
+ /**
+ * The three startup modes how noatun should behave when it is
+ * restarted.
+ *
+ * Restore - it can restore the player's last state
+ *
+ * Play - it automatically starts playing the next file in the
+ * playlist
+ *
+ * DontPlay - it doesn't start playing
+ */
+ enum StartupPlayMode { Restore = 0, Play, DontPlay };
+
+signals:
+ /**
+ * Tells you (a UI plugin) to hide
+ */
+ void hideYourself();
+
+ /**
+ * Tells you (a UI plugin) to show again
+ */
+ void showYourself();
+
+public slots:
+ /**
+ * ask the UIs to hide or show
+ **/
+ void toggleInterfaces();
+ /**
+ * ask the UIs to show
+ **/
+ void showInterfaces();
+ /**
+ * ask the UIs to hide, then you have
+ * to look around for them, or you'll lose
+ **/
+ void hideInterfaces();
+
+public: //options
+ /**
+ * @deprecated Use startupPlayMode() instead
+ */
+ bool autoPlay() const;
+ int startupPlayMode() const;
+ bool loopList() const;
+ bool oneInstance() const;
+ QString saveDirectory() const;
+ /**
+ * @deprecated
+ * now merged with clearOnOpen()
+ **/
+ bool clearOnStart() const;
+ /**
+ * @return true if the playlist will be cleared when opening a
+ * new file via commandline or file open dialog, false otherwise
+ **/
+ bool clearOnOpen() const;
+ bool hackUpPlaylist() const;
+ /**
+ * @return true if hardware-mixing is being used, false in case
+ * software mixing is active
+ **/
+ bool fastMixer() const;
+ QString titleFormat() const;
+ /**
+ * @return true if remaining time is displayed to the user, false if
+ * played time is displayed
+ **/
+ bool displayRemaining() const;
+
+ void setOneInstance(bool);
+ void setLoopList(bool);
+ /**
+ * @deprecated Use setStartupPlayMode() instead
+ */
+ void setAutoPlay(bool);
+ void setStartupPlayMode(int mode);
+ void setSaveDirectory(const QString &);
+ void setRememberPositions(bool);
+ /**
+ * @deprecated
+ * now merged with setClearOnOpen()
+ **/
+ void setClearOnStart(bool);
+ /**
+ * Set if the playlist will be cleared when opening a
+ * new file via commandline or file open dialog
+ **/
+ void setClearOnOpen(bool);
+ void setHackUpPlaylist(bool);
+
+ /**
+ * Set if hardware-mixing ("fast") or software-mixing ("slow") should be used
+ **/
+ void setFastMixer(bool);
+
+ void setTitleFormat(const QString &);
+
+ /**
+ * Pass true if remaining time should be displayed to the user, false if
+ * played time should be displayed
+ **/
+ void setDisplayRemaining(bool);
+
+ /**
+ * To insert items use KActions and insert() them into pluginActionMenu().
+ * @return pointer to the actionmenu
+ */
+ NoatunStdAction::PluginActionMenu *pluginActionMenu();
+
+ /**
+ * @deprecated
+ * Adds an item to the plugin menu.
+ * You may use this value with pluginMenu() for greater control of your menu entry
+ *
+ * @return the ID associated with the menu item, for use in pluginMenuRemove()
+ **/
+ int pluginMenuAdd(const QString &text, const QObject *receiver, const char *member);
+
+ /**
+ * @deprecated
+ * Removes an item previously added to the plugin menu.
+ * @param id the ID of the "to be removed" menu item
+ **/
+ void pluginMenuRemove(int id);
+
+ /**
+ * @deprecated
+ * Use pluginActionMenu() instead
+ * @return pointer to the plugin menu
+ */
+ KPopupMenu *pluginMenu();
+
+protected:
+ virtual int newInstance();
+
+public slots:
+ // slots for the contextMenu
+ /**
+ * Opens the preferences dialog
+ * You can also use
+ * <pre>napp->preferencesBox()->show()</pre>
+ * @see NoatunPreferences
+ */
+ void preferences();
+ /**
+ * Exits Noatun
+ */
+ void quit();
+ /**
+ * Shows the standard file-open dialog
+ */
+ void fileOpen();
+ /**
+ * Shows the effects window
+ */
+ void effectView();
+ /**
+ * Shows the equalizer window
+ */
+ void equalizerView();
+
+private:
+ void loadPlugins();
+ void saveEngineState();
+ void restoreEngineState();
+
+private:
+ Player *mPlayer;
+ LibraryLoader *mLibraryLoader;
+ KPopupMenu *mPluginMenu;
+ NoatunStdAction::PluginActionMenu *mPluginActionMenu;
+ Downloader *mDownloader;
+ struct Private;
+ Private *d;
+ EffectView *mEffectView;
+ NoatunPreferences *mPref;
+ ::Equalizer *mEqualizer;
+ KDialogBase *mEqualizerView;
+ bool showingInterfaces;
+};
+
+#define napp (static_cast<NoatunApp*>(kapp))
+
+// version info for the plugins
+// this is MAJOR.MINOR.PATCHLEVEL
+// and you developers better ignore patchlevel :)
+#define NOATUN_MAJOR 2
+#define NOATUN_MINOR 10
+#define NOATUN_PATCHLEVEL 0
+
+#define NOATUN_VERSION "2.10.0"
+
+#endif
diff --git a/noatun/library/noatun/controls.h b/noatun/library/noatun/controls.h
new file mode 100644
index 00000000..6db8d1cd
--- /dev/null
+++ b/noatun/library/noatun/controls.h
@@ -0,0 +1,79 @@
+#ifndef __CONTROLS_H
+#define __CONTROLS_H
+
+#include <qguardedptr.h>
+
+#include <kaction.h>
+#include <ktoolbar.h>
+#include <qslider.h>
+#include <qstringlist.h>
+#include <kdemacros.h>
+
+class QComboBox;
+class QLabel;
+
+/**
+ * A slider that can be moved around while being
+ * changed internally
+ *
+ * @short Special QSlider based class suitable for time sliders
+ * @author Charles Samuels
+ * @version 2.3
+ **/
+class KDE_EXPORT L33tSlider : public QSlider
+{
+Q_OBJECT
+public:
+ L33tSlider(QWidget * parent, const char * name=0);
+ L33tSlider(Orientation, QWidget * parent, const char * name=0);
+ L33tSlider(int minValue, int maxValue, int pageStep, int value,
+ Orientation, QWidget * parent, const char * name=0);
+
+ bool currentlyPressed() const;
+signals:
+ /**
+ * emmited only when the user changes the value by hand
+ **/
+ void userChanged(int value);
+
+public slots:
+ virtual void setValue(int);
+
+protected:
+ virtual void mousePressEvent(QMouseEvent*);
+ virtual void mouseReleaseEvent(QMouseEvent*);
+ virtual void wheelEvent(QWheelEvent *);
+
+private:
+ bool pressed;
+};
+
+/**
+ * @short A slider for your toolbar
+ * @author Charles Samuels
+ * @version 2.3
+ **/
+class SliderAction : public KAction
+{
+Q_OBJECT
+public:
+ SliderAction(const QString& text, int accel, const QObject *receiver,
+ const char *member, QObject* parent, const char* name );
+ virtual int plug( QWidget *w, int index = -1 );
+ virtual void unplug( QWidget *w );
+ QSlider* slider() const { return m_slider; }
+
+signals:
+ void plugged();
+
+public slots:
+ void toolbarMoved(KToolBar::BarPosition pos);
+
+private:
+ QGuardedPtr<QSlider> m_slider;
+ QStringList m_items;
+ const QObject *m_receiver;
+ const char *m_member;
+};
+
+#endif
diff --git a/noatun/library/noatun/conversion.h b/noatun/library/noatun/conversion.h
new file mode 100644
index 00000000..841ed0f8
--- /dev/null
+++ b/noatun/library/noatun/conversion.h
@@ -0,0 +1,117 @@
+#ifndef NOATUN_CONVERT_H
+#define NOATUN_CONVERT_H
+
+/**
+ * Helper functions to convert between sample types
+ * @short sample type conversion
+ **/
+namespace Conversion
+{
+/**
+ * Convert a mono 8 bit group to float
+ **/
+void convertMono8ToFloat(unsigned long samples, unsigned char *from, float *to);
+
+/**
+ * convert a mono 8 bit group to float, at a different speed
+ **/
+void interpolateMono8ToFloat(unsigned long samples, double start, double speed,
+ unsigned char *from, float *to);
+
+/**
+ * convert a mono 16 bit little-endian stream to float
+ **/
+void convertMono16leToFloat(unsigned long samples, unsigned char *from, float *to);
+
+/**
+ * convert a mono 16 bit little-endian stream to float at a different speed
+ **/
+void interpolateMono16leToFloat(unsigned long samples, double startpos, double speed,
+ unsigned char *from, float *to);
+
+/**
+ * convert an stereo 8-bit interleaved (Alternating left/right channel) to floats
+ **/
+void convertStereoI8To2Float(unsigned long samples, unsigned char *from,
+ float *left, float *right);
+
+/**
+ * convert a stereo 8-bit interleaved (alternating left/right channel) to floats,
+ * at a different speed
+ **/
+void interpolateStereoI8To2Float(unsigned long samples, double startpos, double speed,
+ unsigned char *from, float *left, float *right);
+/**
+ * convert an interleaved 16 bit little endian stream to two floats
+ **/
+void convertStereoI16leTo2Float(unsigned long samples, unsigned char *from, float *left,
+ float *right);
+
+/**
+ * convert an interleaved 16 bit little endian stream to two floats at a different
+ * speed
+ **/
+void interpolateStereoI16leTo2Float(unsigned long samples, double startpos, double speed,
+ unsigned char *from, float *left, float *right);
+
+/**
+ * convert a float to a float, but at a different speed
+ **/
+void interpolateMonoFloatToFloat(unsigned long samples, double startpos, double speed,
+ float *from, float *to);
+
+/**
+ * convert a stereo interleaved float to two floats
+ **/
+void convertStereoIFloatTo2Float(unsigned long samples, float *from, float *left,
+ float *right);
+
+/**
+ * convert a stereo interleaved float to two floats at a different speed
+ **/
+void interpolateStereoIFloatTo2Float(unsigned long samples, double startpos,
+ double speed, float *from, float *left,
+ float *right);
+
+/**
+ * convert a mono float to a 16 bit little endian float
+ **/
+void convertMonoFloatTo16le(unsigned long samples, float *from, unsigned char *to);
+
+/**
+ * convert a two floats to a 16 bit little endian interleaved float
+ **/
+void convertStereo2FloatToI16le(unsigned long samples, float *left, float *right,
+ unsigned char *to);
+
+/**
+ * convert a mono float to an 8 bit stream
+ **/
+void convertMonoFloatTo8(unsigned long samples, float *from, unsigned char *to);
+
+/**
+ * convert two floats to an 8 bit interleaved stream
+ **/
+void convertStereo2FloatToI8(unsigned long samples, float *left, float *right,
+ unsigned char *to);
+
+/**
+ * to little endian (Intel) with swapEndian
+ * does nothing if this is an intel
+ **/
+inline void toLittleEndian(unsigned long len, char *buffer);
+
+/**
+ * to big endian with swapEndian
+ * does nothing if this isn't an intel
+ **/
+inline void toBigEndian(unsigned long len, char *buffer);
+
+/**
+ * swap the endian, does so on every platform
+ * operates on 16 bits at a time. so loads 16, swaps, and copies them
+ **/
+void swapEndian(unsigned long length, char *buffer);
+}
+
+#endif
diff --git a/noatun/library/noatun/downloader.h b/noatun/library/noatun/downloader.h
new file mode 100644
index 00000000..c281a006
--- /dev/null
+++ b/noatun/library/noatun/downloader.h
@@ -0,0 +1,120 @@
+#ifndef _DOWNLOADER_H
+#define _DOWNLOADER_H
+
+#include <kurl.h>
+#include <qobject.h>
+#include <qptrlist.h>
+
+class QFile;
+class QTimer;
+class Downloader;
+
+
+namespace KIO
+{
+ class TransferJob;
+ class Job;
+}
+
+/**
+ * Item to download, usually queued up in Downloader
+ **/
+class DownloadItem
+{
+ friend class Downloader;
+public:
+ DownloadItem();
+ virtual ~DownloadItem();
+
+ bool isDownloaded() const;
+
+ /**
+ * @return the local filename this item will be saved to
+ **/
+ QString localFilename() const;
+
+ virtual void setLocalFilename(const QString &filename);
+
+ /**
+ * Called if this item has been fully downloaded
+ **/
+ virtual void downloadFinished();
+ /**
+ * Called at regular intervals while downloading this item
+ **/
+ virtual void downloaded(int percent);
+ /**
+ * Called when downloading this item timed out
+ **/
+ virtual void downloadTimeout();
+ /**
+ * @return true if the download was scheduled, false if the file is local
+ **/
+ bool enqueue(const KURL &url);
+ /**
+ * Remove this item from Downloader queue
+ **/
+ void dequeue();
+
+private:
+ QString mLocalFilename;
+};
+
+/**
+ * download playlistitems, in a queue based fasion
+ **/
+class Downloader : public QObject
+{
+Q_OBJECT
+ struct QueueItem
+ {
+ DownloadItem *notifier;
+ KURL file;
+ QString local;
+ };
+
+public:
+ Downloader(QObject *parent=0);
+ virtual ~Downloader();
+
+public slots:
+ QString enqueue(DownloadItem *notifier, const KURL &file);
+ void dequeue(DownloadItem *notifier);
+
+ /**
+ * @internal
+ **/
+ void start();
+
+signals:
+ /**
+ * Emitted when a new DownloadItem was added to the queue
+ * @p notifier is the added item
+ **/
+ void enqueued(DownloadItem *notifier, const KURL &file);
+ /**
+ * Emitted when a DownloadItem was removed from the queue
+ * @p notifier is the removed item
+ **/
+ void dequeued(DownloadItem *notifier);
+
+private slots:
+ void getNext();
+
+ void data( KIO::Job *, const QByteArray &data);
+ void percent( KIO::Job *, unsigned long percent);
+ void jobDone( KIO::Job *);
+ void giveUpWithThisDownloadServerIsRunningNT();
+
+private:
+ QPtrList<Downloader::QueueItem> mQueue;
+ QPtrList<Downloader::QueueItem> *mUnstartedQueue;
+ QFile *localfile;
+ Downloader::QueueItem *current;
+ KIO::TransferJob *mJob;
+ QTimer *mTimeout;
+ bool mStarted;
+};
+
+#endif
+
diff --git a/noatun/library/noatun/effects.h b/noatun/library/noatun/effects.h
new file mode 100644
index 00000000..c39cb8aa
--- /dev/null
+++ b/noatun/library/noatun/effects.h
@@ -0,0 +1,186 @@
+#ifndef EFFECTS_H
+#define EFFECTS_H
+
+#include <qptrlist.h>
+#include <qcstring.h>
+#include <qstrlist.h>
+#include <qobject.h>
+
+namespace Arts { class StereoEffect; }
+class Engine;
+class EffectConfigWidget;
+
+/**
+ * @short Base class for a noatun effect
+ *
+ * Example:
+ * \code
+ * new Effect("Arts::SomeEffect");
+ * \endcode
+ * Then you can add, insert, etc. it using Effects
+ **/
+class Effect
+{
+friend class Effects;
+friend class EffectConfigWidget;
+public:
+ Effect(const char *name);
+ ~Effect();
+
+ /**
+ * return the effect processed
+ * directly before this one
+ **/
+ Effect *before() const;
+ /**
+ * return the effect processed
+ * directly after this one
+ **/
+ Effect *after() const;
+ long id() const;
+
+ /**
+ * get the Arts object.
+ * @internal
+ **/
+ Arts::StereoEffect *effect() const;
+
+ /**
+ * Get the name of the object.
+ **/
+ QCString name() const;
+
+ /**
+ * get the "clean" title of effect
+ **/
+ QString title() const;
+
+ /**
+ * @return true if this effect name is invalid, false otherwise
+ * <b>Note:</b> If you call StereoEffect::start() on an invalid Effect you
+ * will probably be punished with a segmentation fault.
+ **/
+ bool isNull() const;
+
+ /**
+ * show the configure dialog box for
+ * this effect. if friendly is true,
+ * then create a top-level window,
+ * set an icon and make it purdy. Otherwise
+ * create a plan widget that you can reparent.
+ **/
+ QWidget *configure(bool friendly=true);
+
+ /**
+ * Does this widget have a configurable
+ * dialog box. E.g., will configure
+ * return null?
+ **/
+ bool configurable() const;
+
+ /**
+ * Return an effect name that can be presented to a user
+ * i.e. Arts::FREEVERB will end up as FREEVERB
+ **/
+ static QString clean(const QCString &name);
+private:
+ long mId;
+ Arts::StereoEffect *mEffect;
+ QCString mName;
+ QWidget *mConfig;
+};
+
+/**
+ * Noatuns effect stack
+ * @author Charles Samuels
+ **/
+class Effects : public QObject
+{
+Q_OBJECT
+friend class Effect;
+public:
+ Effects();
+
+ bool insert(const Effect *after, Effect *item);
+
+ /**
+ * create the Effect, by getting the proper item
+ * from the list, then append it here.
+ *
+ * for example
+ * \code
+ * append(new Effect(available()[0]));
+ * \endcode
+ **/
+ bool append(Effect *item);
+
+ /**
+ * reorder the effect stack. If @p after is null,
+ * it'll be first
+ **/
+ void move(const Effect *after, Effect *item);
+ /**
+ * Removes an item from the effect stack
+ * @param item item to remove
+ * @param del delete the item from the effect stack as well (default true)
+ **/
+ void remove(Effect *item, bool del=true);
+
+ /**
+ * Removes all items from the effect stack.
+ * @param del delete the items from the effect stack as well (default true)
+ **/
+ void removeAll(bool del=true);
+
+ /**
+ * A list of all available effects, by name
+ * each of these can be given to the first
+ * argument of the Effect constructor
+ **/
+ QStrList available() const;
+
+ /**
+ * A list of all available effects objects
+ **/
+ QPtrList<Effect> effects() const;
+
+ /**
+ * Get the Effect that has the following id
+ **/
+ Effect *findId(long id) const;
+
+private:
+ QPtrListIterator<Effect> stackPosition() const;
+
+signals:
+ /**
+ * Emitted when an effect has been added to the effect stack
+ * @param effect the effect that got added
+ **/
+ void added(Effect *effect);
+ /**
+ * Emitted when an effect has been removed to the effect stack
+ * @param effect the effect that got removed
+ **/
+ void removed(Effect *effect);
+ /**
+ * Emitted when an effect has been moved
+ * @param effect the effect that got moved
+ **/
+ void moved(Effect *effect);
+ /**
+ * Emitted when an effect is about to be
+ * deleted (from memory)
+ * @param effect the effect to be deleted
+ **/
+ void deleting(Effect *effect);
+
+private:
+ // stored in no specific order
+ QPtrList<Effect> mItems;
+};
+
+
+
+#endif
+
diff --git a/noatun/library/noatun/engine.h b/noatun/library/noatun/engine.h
new file mode 100644
index 00000000..2244fa1e
--- /dev/null
+++ b/noatun/library/noatun/engine.h
@@ -0,0 +1,120 @@
+#ifndef _ENGINE_H
+#define _ENGINE_H
+
+#include <qobject.h>
+#include <kurl.h>
+#include <arts/kmedia2.h>
+#include <noatun/playlist.h>
+#include <kdemacros.h>
+class Visualization;
+
+namespace Arts
+{
+ class SoundServerV2;
+
+ class Synth_AMAN_PLAY;
+}
+
+namespace Noatun
+{
+ class StereoEffectStack;
+ class StereoVolumeControl;
+ class Equalizer;
+ class Session;
+}
+
+class NoatunApp;
+
+/**
+ * Handles all playing, connecting to aRts.
+ * Does almost everything related to multimedia.
+ * Most interfacing should be done with Player
+ **/
+class KDE_EXPORT Engine : public QObject
+{
+Q_OBJECT
+friend class NoatunApp;
+public:
+ Engine(QObject *parent=0);
+ ~Engine();
+ void setInitialized();
+ bool initialized() const;
+
+public slots:
+ /**
+ * opens the file, use play() to start playing
+ **/
+ bool open(const PlaylistItem &file);
+ /**
+ * Continues playing
+ **/
+ bool play();
+ /**
+ * Terminates playing, does not close the file
+ **/
+ void pause();
+ /**
+ * resets the engine
+ **/
+ void stop();
+ /**
+ * skips to a timecode
+ * unit is milliseconds
+ **/
+ void seek(int msec);
+
+ void setVolume(int percent);
+
+ void connectPlayObject();
+signals:
+ void done();
+ /**
+ * emitted when arts dies and noatun has to start
+ * it again. This is called when the new arts
+ * is already initialized
+ **/
+ void artsError();
+
+ void aboutToPlay();
+
+ void receivedStreamMeta(
+ const QString &streamName, const QString &streamGenre,
+ const QString &streamUrl, const QString &streamBitrate,
+ const QString &trackTitle, const QString &trackUrl
+ );
+
+ void playingFailed();
+
+ private slots:
+ void slotProxyError();
+ void deleteProxy();
+
+public:
+ int state();
+ int position(); // return position in milliseconds
+ int length(); // return track-length in milliseconds
+ int volume() const;
+
+private:
+ int openMixerFD();
+ void closeMixerFD(int);
+ void useHardwareMixer(bool);
+ bool initArts();
+
+public:
+ Arts::SoundServerV2 *server() const;
+ Arts::PlayObject playObject() const;
+ Arts::SoundServerV2 *simpleSoundServer() const;
+ Noatun::StereoEffectStack *effectStack() const;
+ Noatun::Equalizer *equalizer() const;
+ Noatun::StereoEffectStack *visualizationStack() const;
+ Noatun::StereoEffectStack *globalEffectStack() const;
+ Noatun::Session *session() const;
+
+private:
+ class EnginePrivate;
+ EnginePrivate *d;
+ bool mPlay;
+};
+
+#endif
diff --git a/noatun/library/noatun/equalizer.h b/noatun/library/noatun/equalizer.h
new file mode 100644
index 00000000..10de88c5
--- /dev/null
+++ b/noatun/library/noatun/equalizer.h
@@ -0,0 +1,238 @@
+#ifndef NOATUN_EQUALIZER_H
+#define NOATUN_EQUALIZER_H
+
+#include <qptrlist.h>
+#include <qobject.h>
+#include <kurl.h>
+#include <noatun/vequalizer.h>
+
+class Engine;
+class Equalizer;
+
+/**
+ * a preset stores the state of the equalizer
+ *
+ * @short EQ Preset
+ * @author Charles Samuels
+ * @version 2.3
+ **/
+class Preset
+{
+friend class Equalizer;
+
+ Preset(const QString &file);
+ Preset();
+ Preset(VPreset p);
+
+public:
+ QString name() const;
+ bool setName(const QString &name);
+ bool save() const;
+ bool load();
+
+ void remove();
+
+ QString file() const;
+
+ VPreset &vpreset() const;
+private:
+ QString mFile;
+};
+
+/**
+ * This represents a single band in Noatuns %Equalizer
+ *
+ * @short EQ Band
+ * @author Charles Samuels
+ * @version 2.3
+ **/
+class Band
+{
+friend class Equalizer;
+friend class QPtrList<Band>;
+
+private:
+ Band();
+ Band(int start, int end);
+ Band(int band);
+ virtual ~Band();
+public:
+ /**
+ * the intensity of the change.
+ * it's logarithmic. 0 is no change
+ * negative numbers are loss in intensity
+ * positive numbers are a gain
+ * And +-100 is the most you'd need to go
+ **/
+ int level();
+ void setLevel(int l);
+
+ int start() const;
+ int end() const;
+
+ /**
+ * the middle between start and end
+ **/
+ int center() const;
+
+ QString formatStart(bool withHz=true) const;
+ QString formatEnd(bool withHz=true) const;
+ /**
+ * return the format for center()
+ **/
+ QString format(bool withHz=true) const;
+
+private:
+ int mOffset;
+ int mNum, mEnd;
+};
+
+
+/**
+ * @deprecated
+ *
+ * this API is deprecated!!! Do not use it!
+ * it acts as a wrapper around the new VEqualizer API
+ * This only exists to keep compatibility in both
+ * source and binary forms. It will go away in the future.
+ * @short old Equalizer
+ * @author Charles Samuels
+ * @version 2.3
+ **/
+class Equalizer : public QObject
+{
+friend class Band;
+friend class Preset;
+friend class Engine;
+
+Q_OBJECT
+public:
+ Equalizer();
+ ~Equalizer();
+
+ const QPtrList<Band> &bands() const;
+ Band *band(int num) const;
+ int bandCount() const;
+
+ int preamp() const;
+ bool isEnabled() const;
+
+ void init();
+
+public slots:
+ /**
+ * set the preamplification
+ * it's logarithmic. 0 is no change
+ * negative numbers are loss in intensity
+ * positive numbers are a gain
+ * And +-100 is the most you'd need to go
+ **/
+ void setPreamp(int p);
+ void enable();
+ void disable();
+ void setEnabled(bool e);
+
+
+public:
+// saving eq stuff
+ /**
+ * save the current levels
+ * all noatun equalizer files have the "*.noatunequalizer"
+ * pattern. Nevertheless, the file can be identified
+ * by magic, so it's not required
+ **/
+ bool save(const KURL &file, const QString &friendly) const;
+
+ /**
+ * restore the EQ settings from this file
+ **/
+ bool load(const KURL &file);
+
+ /**
+ * convert the current EQ settings to string form,
+ * suitable for storage, the given string is the title
+ *
+ * @see fromString
+ **/
+ QString toString(const QString &name="stored") const;
+
+ /**
+ * undo a toString, restoring the EQ
+ * to the settings in the given string,
+ * emitting the changed signals
+ **/
+ bool fromString(const QString &str);
+
+ /**
+ * create a preset with such a name
+ * and remember that it'l return zero
+ * if the name already exists
+ *
+ * If smart is true, append a number to the end
+ * of the name, if one already exists by the given
+ **/
+ Preset *createPreset(const QString &name, bool smart=true);
+
+ /**
+ * return all the presets
+ * remember to setAutoDelete on this
+ **/
+ QPtrList<Preset> presets() const;
+
+ Preset *preset(const QString &file);
+ bool presetExists(const QString &name) const;
+
+signals:
+ void changed(Band *band);
+ void changed();
+ void enabled();
+ void disabled();
+ void enabled(bool e);
+
+ void preampChanged(int p);
+ void preampChanged();
+
+ /**
+ * the preset with the given name
+ * was selected
+ **/
+ void changed(Preset *);
+
+ /**
+ * when a new preset has been created
+ **/
+ void created(Preset*);
+
+ /**
+ * when @p preset has been renamed to @p newname
+ **/
+ void renamed(Preset *);
+
+ /**
+ * the given preset has been removed
+ **/
+ void removed(Preset *);
+
+private slots:
+ void created(VPreset preset);
+ void selected(VPreset preset);
+ void renamed(VPreset preset);
+ void removed(VPreset preset);
+
+private:
+ void add(Band*);
+ void remove(Band*);
+ // apply the data to artsd
+ void enableUpdates(bool on=true);
+ void update(bool full=false);
+
+private:
+ QPtrList<Band> mBands;
+ bool mUpdates;
+ int mPreamp;
+};
+
+
+
+#endif
+
diff --git a/noatun/library/noatun/mocs.cpp b/noatun/library/noatun/mocs.cpp
new file mode 100644
index 00000000..3d503bcc
--- /dev/null
+++ b/noatun/library/noatun/mocs.cpp
@@ -0,0 +1,37 @@
+#ifdef STUPID_KDE_AUTOMAKE
+
+#include "effects.h"
+#include "effects.moc"
+#include "playlistsaver.h"
+#include "playlistsaver.moc"
+#include "playlist.h"
+#include "playlist.moc"
+#include "plugin.h"
+#include "plugin.moc"
+#include "video.h"
+#include "video.moc"
+#include "controls.h"
+#include "controls.moc"
+#include "engine.h"
+#include "engine.moc"
+#include "pref.h"
+#include "pref.moc"
+#include "equalizer.h"
+#include "equalizer.moc"
+#include "stdaction.h"
+#include "stdaction.moc"
+#include "scrollinglabel.h"
+#include "scrollinglabel.moc"
+#include "downloader.h"
+#include "downloader.moc"
+#include "app.h"
+#include "app.moc"
+#include "player.h"
+#include "player.moc"
+#include "stereobuttonaction.h"
+#include "stereobuttonaction.moc"
+#include "vequalizer.h"
+#include "vequalizer.moc"
+
+#endif
+
diff --git a/noatun/library/noatun/player.h b/noatun/library/noatun/player.h
new file mode 100644
index 00000000..bccda62b
--- /dev/null
+++ b/noatun/library/noatun/player.h
@@ -0,0 +1,270 @@
+#ifndef PLAYER_H
+#define PLAYER_H
+
+#include <qobject.h>
+#include <qtimer.h>
+#include <kurl.h>
+#include <noatun/playlist.h>
+#include <kdemacros.h>
+class Engine;
+class Playlist;
+class KLibrary;
+
+/**
+ * This class has slots for all the common media player buttons
+ * The slots are called, and it queries the Playlist for the appropriate
+ * file.
+ *
+ * @short Noatun player backend
+ * @author Charles Samuels
+ * @version 2.4
+ **/
+class KDE_EXPORT Player : public QObject
+{
+Q_OBJECT
+friend class Effects;
+friend class PlaylistItemData;
+friend class PlaylistNotifier;
+
+public:
+ /**
+ * "None": Plays the playlist entries sequentially until the
+ * end of the playlist.
+ * "Song": Repeats the current playlist entry indefinitely.
+ * "Playlist": Plays the playlist entries sequentially, until
+ * end of the playlist. Noatun will then restart
+ * playback at the first song.
+ * "Random": Plays the entries of the playlist in a random,
+ * non-repeating order. Playback will continue
+ * indefinitely.
+ **/
+ enum LoopType { None=0, Song, Playlist, Random };
+
+public:
+ Player(QObject *parent=0);
+ ~Player();
+
+ /**
+ * @return a string with the time that can be used in the UI:
+ * CC:CC/LL:LL (mm:ss)
+ **/
+ QString lengthString(int _position=-1);
+ /**
+ * @return LoopType enum
+ **/
+ int loopStyle() const { return mLoopStyle; }
+ /**
+ * @return the volume from 0-100
+ * use volume() instead
+ **/
+ int volume() const;
+ /**
+ * @return the position in milliseconds
+ **/
+ int getTime() const { return position; }
+ /**
+ * @return the track-length in milliseconds
+ **/
+ int getLength();
+ /**
+ * @return true if we're playing
+ **/
+ bool isPlaying();
+ /**
+ * @return true if paused
+ **/
+ bool isPaused();
+ /**
+ * @return true if stopped
+ **/
+ bool isStopped();
+
+ /**
+ * get the current playlist
+ * this may be null
+ * And this may not be necessarily an item allocated
+ * by playlist()
+ **/
+ PlaylistItem current() const { return mCurrent;} // TODO: uninline
+
+ /**
+ * loads a file and optionally plays it
+ * @param file the file to load
+ * @param purge true to clear the playlist on open
+ * @param autoplay start playing that file after loading it
+ **/
+ void openFile(const KURL &file, bool purge=true, bool autoplay=false);
+
+ /**
+ * loads all given files
+ * @param files list of files to load
+ * @param purge true to clear the playlist on open
+ * @param autoplay if true, play the first added item
+ **/
+ void openFile(const KURL::List &files, bool purge=true, bool autoplay=false);
+
+ Engine *engine() const { return mEngine; }
+
+public slots:
+ /**
+ * show or hide the playlist
+ **/
+ void toggleListView();
+ /**
+ * force the playing/paused/stopped/playlist shown signals to
+ * be sent out, also, you can send this if you want to
+ * make all the UIs re-display the current item
+ **/
+ void handleButtons();
+ /**
+ * remove current from playlist
+ **/
+ void removeCurrent();
+
+ /**
+ * go back a track
+ **/
+ void back();
+ /**
+ * stop playing
+ **/
+ void stop();
+ /**
+ * start playing
+ **/
+ void play();
+
+ /**
+ * play the given file
+ **/
+ void play(const PlaylistItem &item);
+ /**
+ * start playing, or pause if we're currently playing
+ **/
+ void playpause();
+ /**
+ * go forward a track
+ **/
+ void forward(bool allowLoop = true);
+
+ /**
+ * skip to the position, unit is milliseconds
+ **/
+ void skipTo(int msec);
+
+ /**
+ * goes to the next type of looping
+ **/
+ void loop();
+
+ /**
+ * set the type of looping
+ **/
+ void loop(int i);
+
+ void setVolume(int);
+
+public slots:
+ /**
+ * @internal
+ * Play the current file
+ **/
+ void playCurrent();
+ /**
+ * @internal
+ * load the current file
+ **/
+ void newCurrent();
+
+private slots:
+ void posTimeout();
+ void aboutToPlay();
+ void slotUpdateStreamMeta(
+ const QString &streamName, const QString &streamGenre,
+ const QString &streamUrl, const QString &streamBitrate,
+ const QString &trackTitle, const QString &trackUrl
+ );
+
+signals:
+ /**
+ * Tells you to update the seekbar, volume
+ * and title.
+ **/
+ void timeout();
+
+ void stopped();
+
+ void playing();
+
+ void paused();
+
+ /**
+ * when the type of looping is
+ * changed
+ **/
+ void loopTypeChange(int t);
+
+ /**
+ * the playlist is made visible
+ **/
+ void playlistShown();
+
+ /**
+ * the playlist is hidden
+ **/
+ void playlistHidden();
+
+ /**
+ * called at the same time as newSong, but
+ * maybe easier to work with
+ **/
+ void newSongLen(int mins, int sec);
+
+ /**
+ * when a new song is currently playing
+ **/
+ void newSong();
+
+ /**
+ * Called when a new song is about to be played, but
+ * hasn't started. player->current() is the
+ * next song
+ **/
+ void changed();
+
+ /**
+ * called when we're about to load item, but it hasn't been yet
+ *
+ * this is used for implementing new protocols
+ **/
+ void aboutToOpen(PlaylistItem item);
+
+ void volumeChanged(int);
+
+ /**
+ * this signal is emitted when the user (or a plugin) seeks
+ * the song with @sa skipTo
+ **/
+ void skipped();
+
+ /**
+ * this signal is emitted when the user (or a plugin) seeks
+ * the song with @sa skipTo
+ *
+ * @param msec is the position in the song in milliseconds
+ **/
+ void skipped(int msec);
+
+private:
+ Engine *mEngine;
+ QTimer filePos;
+ int position;
+ int mLoopStyle;
+ bool firstTimeout;
+ PlaylistItem mCurrent; // TODO eliminate
+ QPtrList<PlaylistNotifier> mNotifiers;
+};
+
+
+#endif
+
diff --git a/noatun/library/noatun/playlist.h b/noatun/library/noatun/playlist.h
new file mode 100644
index 00000000..37eabb0b
--- /dev/null
+++ b/noatun/library/noatun/playlist.h
@@ -0,0 +1,531 @@
+#ifndef NOATUNPLAYLIST_H
+#define NOATUNPLAYLIST_H
+
+#include <qobject.h>
+#include <kurl.h>
+#include <qdict.h>
+#include <qstringlist.h>
+#include <cassert>
+#include <kdemacros.h>
+
+class PlaylistItem;
+
+/**
+ * If you're not coding a playlist, ignore this class.
+ *
+ * The backend. Since PlaylistItemData is refcounted,
+ * this contains the data, the PlaylistItem is the "reference"
+ * <pre>
+ * PlaylistItem m=new PlaylistItemData;
+ * </pre>
+ * Of course, you're supposed to inherit from PlaylistItemData
+ * in your Playlist, since there are pure virtuals.
+ *
+ * You can create these objects on demand.
+ *
+ * @short Playlist item data
+ * @author Charles Samuels
+ * @version 2.3
+ **/
+class KDE_EXPORT PlaylistItemData
+{
+public:
+ PlaylistItemData();
+ virtual ~PlaylistItemData();
+
+ /**
+ * Noatun asks your playlist for properties. It is your
+ * responsiblity to store the information. But usually a QMap<QString,QString>
+ * is enough.
+ *
+ * If you return the default value, the default should not
+ * be written.
+ *
+ * This returns the property, or def if such a property doesn't exist
+ **/
+ virtual QString property(const QString &key, const QString &def=0) const=0;
+
+ /**
+ * This sets the property with the given key and value.
+ *
+ * Important: If you use a QMap, you'll have to remove the current
+ * item before adding a new one
+ **/
+ virtual void setProperty(const QString &key, const QString &property)=0;
+
+ /**
+ * remove the item with given key
+ **/
+ virtual void clearProperty(const QString &key)=0;
+
+ /**
+ * return a list of property keys
+ **/
+ virtual QStringList properties() const=0;
+
+ /**
+ * return whether if the given key exists
+ **/
+ virtual bool isProperty(const QString &key) const=0;
+
+ /**
+ * return the title of the song. By default, this will
+ * use the following by default, in order of priority
+ *
+ * property("realtitle")
+ * property("title")
+ * url().filename()
+ *
+ * you shouldn't need to override this.
+ **/
+ virtual QString title() const;
+
+ /**
+ * the true filename of the song, remote or local
+ **/
+ virtual KURL url() const { return KURL(property("url")); }
+ /**
+ * set the true filename of the song, remote or local
+ **/
+ virtual void setUrl(const KURL &url) { setProperty("url", url.url()); }
+
+ /**
+ * first, this checks for the property "mimetype", else
+ * it'l ask KMimeType based on file()
+ **/
+ virtual QCString mimetype() const;
+
+ /**
+ * first, checks for the property "playObject", else,
+ * it'l ask aRts
+ **/
+ virtual QCString playObject() const;
+
+ /**
+ * return the filename to send to the playobject
+ **/
+ virtual QString file() const { return url().path(); }
+
+ /**
+ * what's the length of the song, in milliseconds?
+ **/
+ virtual int length() const;
+
+ /**
+ * sets the length of the song, in milliseconds
+ **/
+ virtual void setLength(int ms);
+
+ /**
+ * returns a friendly representation of the length
+ * of this file
+ **/
+ QString lengthString() const;
+
+ /**
+ * compare yourself with the given PlaylistItemData
+ * This is implemented in the slow fashion of
+ * comparing all the properties. You may
+ * have a much faster way of implementing this
+ * if this==&d, this will not be called, normally
+ **/
+ virtual bool operator == (const PlaylistItemData &d) const;
+
+ /**
+ * this is implemented as !(*this==d), you may have a
+ * faster way to implement this
+ **/
+ virtual bool operator != (const PlaylistItemData &d) const;
+
+ /**
+ * remove this item from the list
+ **/
+ virtual void remove() = 0;
+
+
+ /**
+ * Playlists should not download files if this is true
+ **/
+ bool streamable() const { return isProperty("stream_"); }
+
+public:
+ /**
+ * Call this when you want to signal
+ * the given item has been added to the list
+ **/
+ void added();
+
+ /**
+ * Your playlist must call this when the file
+ * is removed from the playlist
+ **/
+ void removed();
+
+ /**
+ * Your playlist must call this when the file
+ * is modified
+ **/
+ void modified();
+
+
+public: // reference counting
+ /**
+ * Have the reference counter never delete this
+ *
+ * This is useful for when you want to keep all
+ * your items hanging around
+ **/
+ void addRef() { mRefs++; }
+ void removeRef()
+ {
+ mRefs--;
+ if (!mRefs)
+ delete this;
+ }
+
+private:
+ mutable int mRefs;
+};
+
+
+/**
+ * a reference to a PlaylistItem(Data)
+ *
+ * All methods here should have the same behavior
+ * as they do for PlaylistItemData
+ *
+ * If you're a playlist, you should inherit
+ * from PlaylistItemData
+ *
+ * It's client code's responsibility to ensure that
+ * PlaylistItem is not null by using either the boolean
+ * conversion or isNull()
+ *
+ * @short Playlist items
+ * @author Charles Samuels
+ * @version 2.3
+ **/
+class KDE_EXPORT PlaylistItem
+{
+public:
+ PlaylistItem(const PlaylistItem &source);
+ PlaylistItem(PlaylistItemData *source);
+ PlaylistItem() : mData(0) {}
+ ~PlaylistItem();
+
+ PlaylistItem &operator =(const PlaylistItem &source);
+ PlaylistItem &operator =(PlaylistItemData *source);
+
+ PlaylistItemData *data() { return mData; }
+ const PlaylistItemData *data() const { return mData; }
+
+ const PlaylistItem &operator =(const PlaylistItem &source) const;
+ const PlaylistItem &operator =(const PlaylistItemData *source) const;
+
+ operator bool() const { return (bool)mData; }
+ bool isNull() const { return !(bool)mData; }
+
+ bool operator ==(const PlaylistItem &i) const
+ {
+ if (data()==i.data()) return true;
+ if (!data() || !i.data()) return false;
+ return *i.data()==*data();
+ }
+ bool operator ==(const PlaylistItemData *i) const
+ {
+ if (data()==i) return true;
+ if (!data() || !i) return false;
+ return *i==*data();
+ }
+
+ bool operator !=(const PlaylistItem &i) const
+ { return ! (*this==i); }
+ bool operator !=(const PlaylistItemData *i) const
+ { return ! (*this->data()==*i); }
+
+ QString property(const QString &key, const QString &def=0) const
+ {
+ assert(mData);
+ return mData->property(key, def);
+ }
+
+ void setProperty(const QString &key, const QString &property)
+ {
+ assert(mData);
+ const_cast<PlaylistItemData*>(mData)->setProperty(key, property);
+ }
+
+ void clearProperty(const QString &key)
+ {
+ assert(mData);
+ const_cast<PlaylistItemData*>(mData)->clearProperty(key);
+ }
+
+ QStringList properties() const
+ {
+ assert(mData);
+ return mData->properties();
+ }
+
+ bool isProperty(const QString &key) const
+ {
+ assert(mData);
+ return mData->isProperty(key);
+ }
+
+ KURL url() const { assert(mData); return mData->url(); }
+ void setUrl(const KURL &url)
+ {
+ assert(mData);
+ const_cast<PlaylistItemData*>(mData)->setUrl(url);
+ }
+
+ QCString mimetype() const { assert(mData); return mData->mimetype(); }
+ QCString playObject() const { assert(mData); return mData->playObject(); }
+ QString file() const { assert(mData); return mData->file(); }
+
+ QString title() const
+ {
+ assert(mData);
+ return mData->title();
+ }
+
+ int length() const
+ {
+ assert(mData);
+ return mData->length();
+ }
+
+ void setLength(int ms) const
+ {
+ assert(mData);
+ mData->setLength(ms);
+ }
+
+ QString lengthString() const { assert(mData); return mData->lengthString(); }
+
+ void remove() { assert(mData); mData->remove(); }
+
+ bool streamable() const { assert(mData); return mData->streamable(); }
+
+private:
+ // reference counting
+ void removeRef() const;
+ void addRef() const; // requires mData already has item
+
+private:
+ mutable PlaylistItemData *mData;
+ void *_bc1, *_bc2;
+};
+
+/**
+ * The playlist, which you derive from when creating
+ * your own playlist.
+ *
+ * Do not, under any circumstances, call a Playlist method
+ * when you can call a Player method, unless, of course, you
+ * ARE the playlist.
+ **/
+class Playlist : public QObject
+{
+Q_OBJECT
+ friend class PlaylistItemData;
+public:
+ Playlist(QObject *parent, const char *name);
+ /**
+ * on playlist unload, your playlist must
+ * have current()==0 and emit playCurrent
+ **/
+ virtual ~Playlist();
+
+ /**
+ * go to the front
+ **/
+ virtual void reset()=0;
+
+ /**
+ * empty the list
+ **/
+ virtual void clear()=0;
+
+ /**
+ * add a file
+ */
+ virtual void addFile(const KURL&, bool play=false)=0;
+
+ /**
+ * cycle forward, return that
+ **/
+ virtual PlaylistItem next()=0;
+
+ /**
+ * cycle to next section, return that
+ * defaults to return next()
+ */
+ virtual PlaylistItem nextSection();
+
+ /**
+ * cycle back, return that
+ **/
+ virtual PlaylistItem previous()=0;
+
+ /**
+ * cycle to previous section, return that
+ * defaults to return previous()
+ */
+ virtual PlaylistItem previousSection();
+
+ /**
+ * current item
+ **/
+ virtual PlaylistItem current()=0;
+ /**
+ * set the current item
+ **/
+ virtual void setCurrent(const PlaylistItem &)=0;
+
+ /**
+ * get the first item
+ **/
+ virtual PlaylistItem getFirst() const =0;
+
+ /**
+ * get the item after item, note that getFirst and getAfter do not
+ * have to follow play order since they are used solely to iterate
+ * over the entire collection in some order. Duplicating the play
+ * order (by looking into the future) is not necessary.
+ **/
+ virtual PlaylistItem getAfter(const PlaylistItem &item) const =0;
+
+ /**
+ * is the view visible?
+ **/
+
+ virtual bool listVisible() const =0;
+
+ /**
+ * do the KCmdLineArgs stuff
+ **/
+ int handleArguments();
+
+ /**
+ * return a list of songs in which at least one
+ * of the keys matches at least one of the values
+ *
+ * the default implementation will call getFirst()
+ * and getAfter() which could be potentially slow,
+ * depending how your playlist is designed. So
+ * you're free to reimplement this if you could
+ * do better
+ *
+ * A value of "" is equal to an unset value
+ *
+ * limit is the maximum amount of items to return,
+ * or -1 if you want as many as possible
+ *
+ * if exact is true, a match is only made if
+ * the string is identical to a value. if false
+ * a match is made if the string contains a value
+ *
+ * caseSensitive, if false, means that the given
+ * values are compared case insensitively to
+ * to the items in the playlist. The keys
+ * are always compared with case sensitivity
+ **/
+ virtual QValueList<PlaylistItem> select(
+ const QStringList &keys, const QStringList &values,
+ int limit=-1, bool exact=false, bool caseSensitive=false
+ );
+
+ /**
+ * The default implementation will just call
+ * the above select. Of course, you're free to implement
+ * both of these (with different mechanisms if need be)
+ * for speed
+ **/
+ virtual QValueList<PlaylistItem> select(
+ const QString &key, const QString &value,
+ int limit=-1, bool exact=false, bool caseSensitive=false
+ );
+ /**
+ * exactly the same as the above, except converts
+ * the const char* to QString (utf8)
+ **/
+ inline QValueList<PlaylistItem> select(
+ const char *key, const char *value,
+ int limit=-1, bool exact=false, bool caseSensitive=false
+ )
+ {
+ return select(
+ QString(key), QString(value),
+ limit, exact, caseSensitive
+ );
+ }
+
+public slots:
+ /**
+ * show the list!
+ **/
+ virtual void showList()=0;
+ /**
+ * hide it
+ **/
+ virtual void hideList()=0;
+ /**
+ * toggle visibility
+ **/
+ virtual void toggleList();
+
+signals:
+ /**
+ * when you want the engine to reload current()
+ * This is how your playlist forces noatun to
+ * play a new song
+ **/
+ void playCurrent();
+
+ /**
+ * when the list is hidden
+ **/
+ void listHidden();
+
+ /**
+ * when the list is shown
+ **/
+ void listShown();
+};
+
+/**
+ * this class's methods will be called whenever
+ * something happens to the playlist or its
+ * items.
+ *
+ * If the playlist plugin changes, you don't have to do
+ * anything.
+ **/
+class PlaylistNotifier
+{
+public:
+ PlaylistNotifier();
+ virtual ~PlaylistNotifier();
+
+ /**
+ * a new item is added to the list
+ **/
+ virtual void added(PlaylistItem &) {}
+
+ /**
+ * an item is removed from the list
+ **/
+ virtual void removed(PlaylistItem &) {}
+
+ /**
+ * this item was modified (via a changed
+ * or added property
+ **/
+ virtual void modified(PlaylistItem &) {}
+};
+
+
+#endif
+
diff --git a/noatun/library/noatun/playlistsaver.h b/noatun/library/noatun/playlistsaver.h
new file mode 100644
index 00000000..e83761ca
--- /dev/null
+++ b/noatun/library/noatun/playlistsaver.h
@@ -0,0 +1,102 @@
+#ifndef NOATUNPLAYLISTSAVER_H
+#define NOATUNPLAYLISTSAVER_H
+
+#include <kurl.h>
+#include <qmap.h>
+#include <noatun/playlist.h>
+
+class Structure;
+
+/**
+ * a simple one-group implementatio of
+ * the standard XML playlist format:
+ *
+ * http://noatun.kde.org/formats/xmlplaylist.phtml
+ *
+ * A custom implementation like yours should have a
+ * different "client" attribute for the playlist tag
+ **/
+class PlaylistSaver
+{
+ friend class NoatunXMLStructure;
+ friend class MSASXStructure;
+public:
+ enum Options
+ {
+ XMLPlaylist=1,
+ M3U=2,
+ PLS=4,
+ EXTM3U=8,
+ ASX=16
+ };
+
+ PlaylistSaver();
+ virtual ~PlaylistSaver();
+
+ bool save(const KURL &file, int options=0);
+ bool load(const KURL &file, int options=0);
+
+ /**
+ * guess the list's content between M3U or
+ * PLS, and give it to you
+ *
+ * this is also the way to make icecast work.
+ * you can also pass true icecast urls
+ * here and it'l do its magic.
+ *
+ * This calls readItem
+ **/
+ bool metalist(const KURL &url);
+
+ /**
+ * unused, for future expansion, do not use
+ * @internal
+ **/
+ virtual void setGroup(const QString &);
+
+protected:
+ /**
+ * read the item, and add it to your list
+ * Given is a list of properties which coincide
+ * with the standard noatun ones
+ **/
+ virtual void readItem(const QMap<QString,QString> &properties) = 0;
+
+ /**
+ * add this item to the XML file
+ * or a null item if we're at the end
+ **/
+ virtual PlaylistItem writeItem() = 0;
+
+ /**
+ * unused, for future expansion
+ * @internal
+ **/
+ virtual void changeGroup(const QString &) {}
+
+ /**
+ * this is called when you should either
+ * clear your list, or start writing from the start of the list
+ *
+ * You usually don't need to implement this, since it'l always
+ * be called immediately after load() or save()
+ **/
+ virtual void reset() {}
+
+private:
+ bool loadXML(const KURL &file, int x=0);
+ bool saveXML(const KURL &file, int x=0);
+
+ bool loadM3U(const KURL &file, int x=0);
+ bool saveM3U(const KURL &file, int x=0);
+
+ bool loadPLS(const KURL &file, int x=0);
+ bool savePLS(const KURL &file, int x=0);
+
+private:
+ class Private;
+ PlaylistSaver::Private *d; // unused
+
+};
+
+#endif
diff --git a/noatun/library/noatun/plugin.h b/noatun/library/noatun/plugin.h
new file mode 100644
index 00000000..f90dcc0d
--- /dev/null
+++ b/noatun/library/noatun/plugin.h
@@ -0,0 +1,467 @@
+#ifndef NPLUGIN_H
+#define NPLUGIN_H
+
+#include <noatun/pluginloader.h>
+#include <qmemarray.h>
+#include <vector>
+#include <kdemacros.h>
+
+namespace Noatun { class FFTScopeStereo; class FFTScope; class RawScope;
+ class RawScopeStereo; class StereoEffectStack;
+ class Listener;
+ }
+namespace Arts { class SoundServerV2; class Dispatcher; }
+
+//backwards compatibility
+
+#define NOATUNPLUGIND
+#define NOATUNPLUGINC(classname) { }
+
+class Playlist;
+class Player;
+class ExitNotifier;
+class NoatunApp;
+/**
+ * @short Base class for all plugins
+ **/
+class KDE_EXPORT Plugin
+{
+public:
+ Plugin();
+ virtual ~Plugin();
+
+ /**
+ * called directly after the plugin, just in case
+ * you want Noatun to be "ready" with your class
+ **/
+ virtual void init();
+
+ /**
+ * unload the plugin
+ * if it returns true, return from your function
+ * immediately and don't access members of this
+ * TODO
+ **/
+ virtual bool unload();
+
+ /**
+ * make multiple inheritence work
+ * only needed with playlist plugins
+ * generally "return this" in here
+ **/
+ virtual Playlist *playlist() { return 0; }
+};
+
+/**
+ * @short Base class for user-interface plugins
+ *
+ * Inherit from this one instead of Plugin if you are
+ * a user-interface
+ **/
+class KDE_EXPORT UserInterface : public Plugin
+{
+public:
+ UserInterface();
+ virtual ~UserInterface();
+};
+
+
+class TimerThingy;
+
+
+/**
+ * @short Base class for all sorts of visualizations
+ *
+ * all Noatun Visualizations can be in
+ * separate processes! You must fork, and
+ * then exec() to be able to use this.
+ * Its perfectly safe to create
+ * a visualization in a binary executed by
+ * KProcess.
+ **/
+class Visualization
+{
+friend class TimerThingy;
+friend class ThingThatTellsAVisualizationThatNoatunIsGone;
+friend class ExitNotifier;
+friend class NoatunApp;
+public:
+ /**
+ * interval is how frequently the rescope
+ * will occur
+ * 0 means disabled
+ *
+ * Pid, if not zero, can force the visualizaton
+ * to use another noatun's process's visualization stack,
+ * if it is zero, it'l try to guess what to use
+ **/
+ Visualization(int interval=125, int pid=0);
+ virtual ~Visualization();
+
+ /**
+ * start the timer, if it's 0, then this
+ * will do nothing.
+ **/
+ virtual void start();
+ /**
+ * stop the timer
+ **/
+ virtual void stop();
+
+ /**
+ * how long between each rescoping in milliseconds
+ **/
+ int interval() const;
+
+ /**
+ * set how long to wait
+ * the change takes effect immediately,
+ * unless it hadn't been start()ed beforehand
+ **/
+ virtual void setInterval(int msecs);
+
+ /**
+ * cause a rescoping to take effect immediately
+ **/
+ virtual void timeout()=0;
+
+ Noatun::StereoEffectStack visualizationStack();
+ Arts::SoundServerV2 *server();
+
+ /**
+ * return noatun's pid, useful if you're doing remote-visualization
+ *
+ * It returns the pid to use, or -1, if there was an error, or
+ * there isn't a noatun running. If there isn't a noatun running, the
+ * computer will crash, the trains will take the wrong tracks, time
+ * will start moving in the wrong direction, and the whole universe will melt
+ * down.
+ **/
+ static int noatunPid();
+
+ /**
+ * test if the server is still there
+ **/
+ bool connected();
+
+ /**
+ * create a dispatcher object , does nothing if
+ * this already has been called
+ **/
+ static void initDispatcher();
+
+private:
+ int mTimeout;
+ TimerThingy *mTimerThingy;
+ QCString mVisualizationStack;
+ Arts::SoundServerV2 *mServer;
+ static Arts::Dispatcher *mDispatcher;
+ static bool internalVis;
+};
+
+/**
+ * Base class for all kinds of FFT scopes
+ **/
+class KDE_EXPORT FFTScope : public Visualization
+{
+public:
+ FFTScope(int interval, int pid=0);
+
+ /**
+ * the "width" of each scopeEvent
+ **/
+ virtual int bands() const=0;
+
+ /**
+ * returns the magic number that you can
+ * give to "setBands". For example:
+ * <pre>
+ * setBands(magic(50));
+ * bands()==50
+ * </pre>
+ **/
+ static float magic(int bands);
+
+ /**
+ * set the width combiner value. in theory,
+ * a value of 1.0 should give you exactly
+ * 1024 samples, greater values will decrease
+ * the amount quite rapidly. by default,
+ * 1.1 is used, which gives 50. You'll have
+ * to tweak this manually.
+ *
+ * This will only return valid responses
+ * for values between 10 and 1024
+ *
+ * This function is a terrible hack, and we apologize
+ * for it. The values of these magic numbers
+ * do occasionally change, so you must use
+ * @ref magic
+ **/
+ virtual void setBands(float n)=0;
+};
+
+/**
+ * An easy to use FFT scope, stereo version.
+ * You certainly want to reimplement scopeEvent()
+ **/
+class KDE_EXPORT StereoFFTScope : public FFTScope
+{
+public:
+ StereoFFTScope(int timeout=250, int pid=0);
+ virtual ~StereoFFTScope();
+
+ /**
+ * called according to the timeout
+ * the two floats contain the data, with @p len items
+ * you override this yourself
+ **/
+ virtual void scopeEvent(float *left, float *right, int len)
+ { (void)left; (void)right; (void)len; }
+
+ /**
+ * get the current data
+ * pass two vector<float>*, this will do the rest.
+ * do not allocate it beforehand.
+ * you must then delete the vectors
+ * @returns the amount of elements for both left and right
+ **/
+ void scopeData(std::vector<float> *&left, std::vector<float> *&right);
+ virtual void timeout();
+
+ virtual int bands() const;
+ virtual void setBands(float f);
+
+private:
+ Noatun::FFTScopeStereo *mScope;
+ long mId;
+};
+
+
+/**
+ * An easy to use FFT scope, mono version.
+ * You certainly want to reimplement scopeEvent()
+ **/
+class KDE_EXPORT MonoFFTScope : public FFTScope
+{
+public:
+ MonoFFTScope(int timeout=250, int pid=0);
+ virtual ~MonoFFTScope();
+
+ /**
+ * called according to the timeout
+ * the float contains the data, with @p len items
+ * you override this yourself
+ **/
+ virtual void scopeEvent(float *data, int len)
+ { (void)data; (void)len; }
+
+ /**
+ * get the current data
+ * pass a vector<float>*, this will do the rest.
+ * do not allocate it beforehand.
+ * you must then delete the vectors
+ * @returns the amount of elements for both left and right
+ **/
+ void scopeData(std::vector<float> *&data);
+
+ /**
+ * reimplemented from class Visualization, you
+ * should never need to reimplement this yourself
+ **/
+ virtual void timeout();
+
+ virtual int bands() const;
+ virtual void setBands(float f);
+
+private:
+ Noatun::FFTScope *mScope;
+ long mId;
+};
+
+
+/**
+ * Base class for all kinds of scope visualizations, i.e. if you want to display
+ * the currently played waveform
+ **/
+class Scope : public Visualization
+{
+public:
+ Scope(int interval, int pid=0);
+
+ /**
+ * the "width" of each scopeEvent
+ **/
+ virtual int samples() const=0;
+
+ virtual void setSamples(int )=0;
+};
+
+/**
+ * An easy to use scope visualization, mono version.
+ * Note: Of course this one also works for audio with more than one channel
+ * You certainly want to reimplement scopeEvent()
+ */
+class KDE_EXPORT MonoScope : public Scope
+{
+public:
+ MonoScope(int timeout=250, int pid=0);
+ virtual ~MonoScope();
+
+ /**
+ * called according to the timeout
+ * the float contains the data, with @p len items
+ * you override this yourself, and process the data
+ **/
+ virtual void scopeEvent(float *data, int len)
+ {(void)data; (void)len; }
+
+ /**
+ * get the current data
+ * pass a vector<float>*, this will do the rest.
+ * do not allocate it beforehand.
+ * you must then delete the vectors
+ * @returns the amount of elements for both left and right
+ **/
+ void scopeData(std::vector<float> *&data);
+
+ /**
+ * reimplemented from class Visualization, you
+ * should never need to reimplement this yourself
+ **/
+ virtual void timeout();
+
+ virtual int samples() const;
+ virtual void setSamples(int);
+
+private:
+ Noatun::RawScope *mScope;
+ long mId;
+};
+
+/**
+ * An easy to use scope visualization, stereo version.
+ * You certainly want to reimplement scopeEvent()
+ **/
+class StereoScope : public Scope
+{
+public:
+ StereoScope(int timeout=250, int pid=0);
+ virtual ~StereoScope();
+
+ /**
+ * called according to the timeout
+ * the float contains the data, with @p len items
+ * you override this yourself, and process the data
+ **/
+ virtual void scopeEvent(float *left, float *right, int len)
+ { (void)left; (void)right; (void)len; }
+
+ /**
+ * get the current data
+ * pass a vector<float>*, this will do the rest.
+ * do not allocate it beforehand.
+ * you must then delete the vectors
+ * @returns the amount of elements for both left and right
+ **/
+ void scopeData(std::vector<float> *&left, std::vector<float> *&right);
+
+ /**
+ * reimplemented from class Visualization, you
+ * should never need to reimplement this yourself
+ **/
+ virtual void timeout();
+
+ virtual int samples() const;
+ virtual void setSamples(int);
+
+
+private:
+ Noatun::RawScopeStereo *mScope;
+ long mId;
+};
+
+/**
+ * @short Base class for session manager plugins
+ *
+ * Inherit from this one instead of Plugin if you are
+ * a session manager plugin
+ **/
+class SessionManagement : public Plugin
+{
+public:
+ SessionManagement();
+ virtual ~SessionManagement();
+
+ virtual void restore();
+};
+
+class NoatunListenerNotif;
+/**
+ * @short Abstract base class to monitor noatun
+ *
+ * So far only used for ExitNotifier.
+ **/
+class NoatunListener : public QObject
+{
+Q_OBJECT
+friend class NoatunListenerNotif;
+
+public:
+ NoatunListener(QObject *parent=0);
+ virtual ~NoatunListener();
+
+signals:
+ void event();
+
+protected:
+ virtual void message();
+
+ NoatunListenerNotif *mNotif;
+};
+
+/**
+ * @short Notifies you as soon as Noatun exits
+ *
+ * Link to libnoatun, and the signal event() will
+ * be emitted whenever noatun exits, and the best
+ * part is how it doesn't matter if noatun exits
+ * cleanly so you even get informed in case noatun
+ * crashes.
+ **/
+class ExitNotifier : public NoatunListener
+{
+public:
+ ExitNotifier(int pid, QObject *parent=0);
+ virtual ~ExitNotifier();
+
+private:
+ QCString appid;
+};
+
+/**
+ * @short Wrapper to set a bool variable as soon as Noatun exits
+ * This class can even be used when you cannot use signals/slots
+ * Example:
+ * <pre>
+ * bool noatunOk = false;
+ * new BoolNotifier(&noatunOk, new ExitNotifier(this), this);
+ * </pre>
+ *
+ * When noatunOk is false, then noatun has exited somehow.
+ **/
+class BoolNotifier : public QObject
+{
+Q_OBJECT
+public:
+ BoolNotifier(bool *value, NoatunListener *listener, QObject *parent=0);
+
+private slots:
+ void event() {*mValue=false;}
+
+private:
+ bool *mValue;
+};
+
+#endif
+
diff --git a/noatun/library/noatun/pluginloader.h b/noatun/library/noatun/pluginloader.h
new file mode 100644
index 00000000..611fe358
--- /dev/null
+++ b/noatun/library/noatun/pluginloader.h
@@ -0,0 +1,99 @@
+#ifndef PLUGIN_LOADER_H
+#define PLUGIN_LOADER_H
+
+#include <qstring.h>
+#include <qvaluelist.h>
+#include <qstringlist.h>
+#include <noatun/app.h>
+
+#include <klibloader.h>
+#include <qdict.h>
+#include <kdemacros.h>
+
+struct NoatunLibraryInfo
+{
+ QString specfile;
+ QString filename;
+ QString author;
+ QString license;
+ QString type;
+ QString site;
+ QString email;
+ QString name;
+ QString comment;
+ QStringList require;
+};
+
+bool operator ==(const NoatunLibraryInfo &, const NoatunLibraryInfo &);
+
+class Playlist;
+class Visualization;
+class Plugin;
+
+/**
+ * Used for loading plugins at runtime
+ **/
+class KDE_EXPORT LibraryLoader
+{
+ friend class Plugin;
+ struct PluginLibrary
+ {
+ Plugin *plugin;
+ KLibrary *library;
+ };
+
+public:
+ LibraryLoader();
+ ~LibraryLoader();
+
+ QValueList<NoatunLibraryInfo> available() const;
+ QValueList<NoatunLibraryInfo> loaded() const;
+ QValueList<NoatunLibraryInfo> loadedByType(const QString &type) const;
+
+ /**
+ * loads all the enabled plugins
+ **/
+ bool loadAll(void);
+ bool loadAll(const QStringList &);
+
+ bool isLoaded(const QString &spec) const;
+ void add(const QString &spec);
+ void setModules(const QStringList &mods);
+ /**
+ * unload the plugin specified by spec
+ **/
+ bool remove(const QString &spec);
+ /**
+ * Same as the above, but does not call kapp->exit() even
+ * when the last userinterface plugin is removed. Necessary
+ * during session management (see marquis plugin).
+ * ### BIC: merge with above with terminateOnLastUI = true
+ */
+ bool remove(const QString &spec, bool terminateOnLastUI);
+ /**
+ * unload the plugin that is referenced by @par plugin
+ **/
+ bool remove(const LibraryLoader::PluginLibrary *plugin);
+ bool remove(const Plugin *plugin);
+
+ Playlist *playlist() const;
+
+ /**
+ * This is needed for the Plugin-List-View
+ * to see what plugins are required to show
+ * (when required by another noatun-plugin)
+ **/
+ NoatunLibraryInfo getInfo(const QString &spec) const;
+ QPtrList<Plugin> plugins() const;
+
+private:
+ bool loadSO(const QString &spec);
+ void removeNow(const QString &spec);
+
+private:
+ QDict<LibraryLoader::PluginLibrary> mLibHash;
+ Playlist *mPlaylist;
+};
+
+#endif
+
diff --git a/noatun/library/noatun/pref.h b/noatun/library/noatun/pref.h
new file mode 100644
index 00000000..95be5e0e
--- /dev/null
+++ b/noatun/library/noatun/pref.h
@@ -0,0 +1,91 @@
+#ifndef NOATUNPREF_H
+#define NOATUNPREF_H
+
+#include <kdialogbase.h>
+#include <qptrlist.h>
+#include <kdemacros.h>
+
+class CModule;
+
+/**
+ * Noatun configuration dialog
+ **/
+class NoatunPreferences : public KDialogBase
+{
+Q_OBJECT
+friend class CModule;
+
+public:
+ /**
+ * @internal
+ **/
+ NoatunPreferences(QWidget *);
+
+public:
+ /**
+ * Display noatun preferences dialog
+ **/
+ virtual void show();
+ /**
+ * Display noatun preferences dialog showing @p module
+ * Useful if you want to display your own plugin configuration tab
+ **/
+ virtual void show(CModule *module);
+
+protected:
+ virtual void slotOk();
+ virtual void slotApply();
+
+private:
+ void add(CModule *);
+ void remove(CModule *);
+
+private:
+ class NoatunPreferencesPrivate;
+ NoatunPreferencesPrivate *d;
+
+ QPtrList<CModule> mModules;
+};
+
+/**
+ * @short Base class for a configuration sheet that is shown in preferences dialog
+ *
+ * Create your GUI in constructor, reimplement reopen() and save() and
+ * you're all set.
+ **/
+class KDE_EXPORT CModule : public QWidget
+{
+Q_OBJECT
+
+public:
+ /**
+ * arguments are short and long descriptions
+ * for this module, respectively
+ *
+ * parent is the object that is this modules virtual-parent.
+ * When that is deleted, this also will go away, automagically.
+ **/
+ CModule(const QString &name, const QString &description, const QString &icon, QObject *parent=0);
+
+ virtual ~CModule();
+
+public slots:
+ /**
+ * save all your options, and apply them
+ **/
+ virtual void save() {}
+ /**
+ * reload all options (e.g., read config files)
+ **/
+ virtual void reopen() {}
+
+private slots:
+ void ownerDeleted();
+
+private:
+ class CModulePrivate;
+ CModulePrivate *d;
+};
+
+
+#endif // NOATUNPREF_H
diff --git a/noatun/library/noatun/scrollinglabel.h b/noatun/library/noatun/scrollinglabel.h
new file mode 100644
index 00000000..1615a64f
--- /dev/null
+++ b/noatun/library/noatun/scrollinglabel.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2000 Rik Hemsley (rikkus) <rik@kde.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef SCROLLING_LABEL_H
+#define SCROLLING_LABEL_H
+
+#include <qwidget.h>
+/**
+ * A clever label that scrolls its contents as soon as there is not enough
+ * space to show everything at once.
+ **/
+class ScrollingLabel : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ ScrollingLabel(const QString &initialText,QWidget *parent,
+ const char * name = 0);
+ virtual ~ScrollingLabel();
+
+ /**
+ * Sets the label's text
+ * set @p time (ms) if you want to display a temporary message
+ **/
+ virtual void setText(const QString &text, int time = -1);
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ /**
+ * @return the label's text.
+ **/
+ virtual QString text() const;
+
+ /**
+ * Used to enable/disable scrolling manually
+ */
+ virtual void setScroll(bool on);
+
+ protected:
+
+ virtual void paintEvent(QPaintEvent *);
+ virtual void resizeEvent(QResizeEvent *);
+
+ protected slots:
+
+ virtual void scroll();
+ virtual void restoreText();
+
+ private:
+
+ void _update();
+
+ class Private;
+ Private * d;
+};
+
+#endif
+// vim:ts=2:sw=2:tw=78:noet
diff --git a/noatun/library/noatun/stdaction.h b/noatun/library/noatun/stdaction.h
new file mode 100644
index 00000000..7cac67fe
--- /dev/null
+++ b/noatun/library/noatun/stdaction.h
@@ -0,0 +1,204 @@
+#ifndef _NOATUNSTDACTION_H_
+#define _NOATUNSTDACTION_H_
+
+#include <kaction.h>
+#include <kactionclasses.h>
+#include <kdemacros.h>
+class KPopupMenu;
+
+/**
+ * Holds all noatun related actions
+ * @short noatun specific actions
+ * @author Charles Samuels
+ **/
+namespace NoatunStdAction
+{
+
+/**
+ * An action starting noatun playback
+ **/
+class PlayAction : public KAction
+{
+Q_OBJECT
+public:
+ PlayAction(QObject *parent, const char *name);
+private slots:
+ void playing();
+ void notplaying();
+};
+
+/**
+ * An action starting/stopping noatun playback
+ **/
+class PlaylistAction : public KToggleAction
+{
+Q_OBJECT
+public:
+ PlaylistAction(QObject *parent, const char *name);
+private slots:
+ void shown();
+ void hidden();
+};
+
+/**
+ * actionmenu that holds all plugin defined actions
+ * @author Stefan Gehn
+ */
+class PluginActionMenu : public KActionMenu
+{
+Q_OBJECT
+public:
+ PluginActionMenu(QObject *parent, const char *name);
+ /**
+ * inserts the given @p action into the action-menu
+ * @param action the action to insert
+ * @param index defines the place where the action gets displayed in
+ */
+ virtual void insert (KAction *action, int index=-1);
+ /**
+ * removes the given @p action into the action-menu
+ */
+ virtual void remove(KAction *action);
+ /**
+ * Wrapper method for old Noatun API
+ * <b>DON'T USE</b>
+ **/
+ int menuAdd(const QString &text, const QObject *receiver, const char *member);
+ /**
+ * Wrapper method for old Noatun API
+ * <b>DON'T USE</b>
+ **/
+ void menuRemove(int id);
+private:
+ int mCount;
+};
+
+/**
+ * actionmenu that holds all vis-plugins for easier enabling/disabling
+ * @author Stefan Gehn
+ */
+class VisActionMenu : public KActionMenu
+{
+Q_OBJECT
+public:
+ VisActionMenu(QObject *parent, const char *name);
+private slots:
+ void fillPopup();
+ void toggleVisPlugin(int);
+private:
+ QMap<int, QString>mSpecMap;
+};
+
+
+/**
+ * actionmenu that holds all looping modes
+ * @author Stefan Gehn
+ */
+class LoopActionMenu : public KActionMenu
+{
+Q_OBJECT
+public:
+ LoopActionMenu(QObject *parent, const char *name);
+private slots:
+ void updateLooping(int);
+ void loopNoneSelected();
+ void loopSongSelected();
+ void loopPlaylistSelected();
+ void loopRandomSelected();
+private:
+ KRadioAction *mLoopNone;
+ KRadioAction *mLoopSong;
+ KRadioAction *mLoopPlaylist;
+ KRadioAction *mLoopRandom;
+};
+
+
+/**
+ * @return pointer to a KAction which opens the effects dialog on activation
+ */
+KDE_EXPORT KAction *effects(QObject *parent = 0, const char *name = 0);
+
+/**
+ * @return pointer to a KAction which opens the equalizer dialog on activation
+ */
+KDE_EXPORT KAction *equalizer(QObject *parent = 0, const char *name = 0);
+
+/**
+ * @return pointer to a KAction which goes back one track on activation
+ */
+KDE_EXPORT KAction *back(QObject *parent = 0, const char *name = 0);
+/**
+ * @return pointer to a KAction which stops playback on activation
+ */
+KDE_EXPORT KAction *stop(QObject *parent = 0, const char *name = 0);
+/**
+ * @return pointer to a KAction which starts/pauses playback on activation
+ */
+KDE_EXPORT KAction *playpause(QObject *parent = 0, const char *name = 0);
+/**
+ * @return pointer to a KAction which advances one track on activation
+ */
+KDE_EXPORT KAction *forward(QObject *parent = 0, const char *name = 0);
+/**
+ * @return pointer to a KToggleAction which shows/hides the playlist
+ */
+KDE_EXPORT KToggleAction *playlist(QObject *parent = 0, const char *name = 0);
+
+/**
+ * loop action
+ **/
+KDE_EXPORT LoopActionMenu *loop(QObject *parent, const char *name);
+
+/**
+ * play action
+ */
+KDE_EXPORT KAction *play(QObject *parent = 0, const char *name = 0);
+
+/**
+ * pause action
+ */
+KDE_EXPORT KAction *pause(QObject *parent = 0, const char *name = 0);
+
+/**
+ * @return pointer to the global PluginActionMenu object (there is only one instance)
+ */
+KDE_EXPORT PluginActionMenu *actions();
+
+/**
+ * @return pointer to a VisActionMenu object
+ */
+KDE_EXPORT VisActionMenu *visualizations(QObject *parent = 0, const char *name = 0);
+
+/**
+ * The global popupmenu of noatun, there's not two or three but only one of these :)
+ * @author Charles Samuels
+ **/
+class KDE_EXPORT ContextMenu
+{
+public:
+ static KPopupMenu *createContextMenu(QWidget *p);
+
+ /**
+ * One menu to show them all, One menu to find them
+ * One menu to bring them all and in the darkness bind them
+ *
+ * In the land of Noatun where the oceans die
+ */
+ static KPopupMenu *contextMenu();
+
+ /**
+ * Show the context menu at point
+ **/
+ static void showContextMenu(const QPoint &);
+
+ /**
+ * show the context menu at the mouse's current position
+ **/
+ static void showContextMenu();
+private:
+ static KPopupMenu *mContextMenu;
+};
+
+}
+
+#endif
diff --git a/noatun/library/noatun/stereobuttonaction.h b/noatun/library/noatun/stereobuttonaction.h
new file mode 100644
index 00000000..ce423b64
--- /dev/null
+++ b/noatun/library/noatun/stereobuttonaction.h
@@ -0,0 +1,31 @@
+#ifndef STEREOBUTTONACTION_H
+#define STEREOBUTTONACTION_H
+
+#include <kaction.h>
+
+namespace NoatunStdAction
+{
+
+/**
+ * No, I never owned StereoButtonAction, but I'm tired
+ * and PlayAction is already taken.
+ */
+class StereoButtonAction : public KAction
+{
+Q_OBJECT
+public:
+ StereoButtonAction(const QString& text, int accel = 0, QObject* parent = 0, const char* name = 0 );
+ StereoButtonAction(const QString& text, int accel, const QObject* receiver, const char* slot, QObject* parent, const char* name = 0 );
+ StereoButtonAction(const QString& text, const QIconSet& pix, int accel = 0, QObject* parent = 0, const char* name = 0 );
+ StereoButtonAction(const QString& text, const QString& pix, int accel = 0, QObject* parent = 0, const char* name = 0 );
+ StereoButtonAction(const QString& text, const QIconSet& pix, int accel, const QObject* receiver, const char* slot, QObject* parent, const char* name = 0 );
+ StereoButtonAction(const QString& text, const QString& pix, int accel, const QObject* receiver, const char* slot, QObject* parent, const char* name = 0 );
+public slots:
+ void disable(void);
+ void enable(void);
+ void toggleEnabled(void);
+};
+
+}
+
+#endif
diff --git a/noatun/library/noatun/vequalizer.h b/noatun/library/noatun/vequalizer.h
new file mode 100644
index 00000000..73c421e1
--- /dev/null
+++ b/noatun/library/noatun/vequalizer.h
@@ -0,0 +1,455 @@
+/*
+ * Copyright (c) 2003 Charles Samuels <charles@kde.org>
+ *
+ * This file is hereby licensed under the GNU General Public License version
+ * 2 or later at your option.
+ *
+ * This file is licensed under the Qt Public License version 1 with the
+ * condition that the licensed will be governed under the Laws of California
+ * (USA) instead of Norway. Disputes will be settled in Santa Clara county
+ * courts.
+ *
+ * This file is licensed under the following additional license. Be aware
+ * that it is identical to the BSD license, except for the added clause 3:
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. By integrating this software into any other software codebase, you waive
+ all rights to any patents associated with the stated codebase.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef NOATUN_VEQUALIZER_H
+#define NOATUN_VEQUALIZER_H
+
+#include <qptrlist.h>
+#include <qobject.h>
+#include <kurl.h>
+#include <kdemacros.h>
+class VBand;
+
+/**
+ * Abstract base class for VInterpolation and VEqualizer
+ **/
+class VBandsInterface
+{
+ friend class VBand;
+
+public:
+ VBandsInterface();
+ virtual ~VBandsInterface();
+
+ virtual int bands() const=0;
+ virtual VBand band(int num)=0;
+ /**
+ * same as @ref band(num)
+ **/
+ VBand operator [] (int num);
+
+private:
+ virtual int level(int index) const=0;
+ virtual void setLevel(int index, int level)=0;
+};
+
+
+class VInterpolation;
+class VEqualizer;
+
+/**
+ * Represents a single band in a vequalizer
+ * @author Charles Samuels
+ **/
+class KDE_EXPORT VBand
+{
+ friend class VInterpolation;
+ friend class VEqualizer;
+
+ struct Private;
+ VBand::Private *d;
+
+private:
+ VBand(VBandsInterface *bands, int index, int start, int end);
+
+public:
+ ~VBand();
+
+ VBand(const VBand &copy);
+ VBand & operator =(const VBand &copy);
+
+ /**
+ * the intensity of the change.
+ * it's logarithmic. 0 is no change
+ * negative numbers are loss in intensity
+ * positive numbers are a gain
+ * And +-100 is the most you'd need to go
+ **/
+ int level() const;
+ void setLevel(int l);
+
+ int start() const;
+ int end() const;
+
+ /**
+ * the middle between start and end
+ **/
+ int center() const;
+
+ QString formatStart(bool withHz=true) const;
+ QString formatEnd(bool withHz=true) const;
+ /**
+ * return the format for center()
+ **/
+ QString format(bool withHz=true) const;
+};
+
+
+/**
+ * This class is an interpolated representation to the Eq data.
+ * This means that no matter how many bands
+ * the real equalizer has, your interpolated
+ * version appears like it has only as many
+ * bands as you asked for. You can continue
+ * to interact with all interpolations and
+ * the true equalizer as normal. They even
+ * modify eachother according to a Spline
+ * interpolation.
+ *
+ * @short interpolated representation of Eq data
+ * @author Charles Samuels
+ **/
+class KDE_EXPORT VInterpolation : public QObject, public VBandsInterface
+{
+ Q_OBJECT
+ struct Private;
+ Private *d;
+ friend class VBand;
+
+public:
+ /**
+ * create an interpolation with the one and only
+ * Noatun equalizer, and @p bands
+ **/
+ VInterpolation(int bands);
+ virtual ~VInterpolation();
+
+ virtual int bands() const;
+ virtual VBand band(int num);
+ virtual int level(int index) const;
+ virtual void setLevel(int index, int level);
+
+signals:
+ void changed();
+
+private:
+ void refresh();
+ void getFrequencies(int num, int *high, int *low) const;
+ /**
+ * where on the spline is my own interpolation's bandNum
+ * @returns an x value on the spline
+ **/
+ double onSpline(int bandNum) const;
+};
+
+class VPreset;
+
+/**
+ * @short Noatun EQ
+ * @author Charles Samuels
+ **/
+class KDE_EXPORT VEqualizer : public QObject, public VBandsInterface
+{
+ Q_OBJECT
+ friend class VBand;
+ friend class VPreset;
+ friend class Engine;
+ friend class NoatunApp;
+ friend class VInterpolation;
+
+ struct Private;
+ Private *d;
+
+ VEqualizer();
+ ~VEqualizer();
+ void init();
+
+public:
+
+ /**
+ * @return the frequencies to use with @p num many
+ * bands. This is a list of the upper frequency
+ * of each band, for example: { 40, 60, 100 } if
+ * you hear up to 40hz
+ **/
+ static QValueList<int> frequencies(int num);
+
+ /**
+ * @return number of bands I have, which may be different
+ * than what setBands was called with
+ * @sa setBands
+ **/
+ int bands() const;
+
+ /**
+ * @return first frequency I operate on, currently 20
+ *
+ * does not apply to an interpolation
+ **/
+ static int start();
+ /**
+ * @return last frequency I operate on, currently 20000
+ */
+ static int end();
+
+ /**
+ * @return maximum amount of bands I may have
+ **/
+ int maxBands() const;
+
+ /**
+ * the minimum number of bands I may have (2)
+ **/
+ int minBands() const;
+
+ VBand band(int num);
+
+ bool isEnabled() const;
+
+ /**
+ * returns the level of preamp (-100 to 100 normally)
+ **/
+ int preamp() const;
+
+public: // serialization
+ /**
+ * save the current levels
+ * all noatun equalizer files have the "*.noatunequalizer"
+ * pattern. Nevertheless, the file can be identified
+ * by magic, so it's not required
+ **/
+ bool save(const KURL &file, const QString &friendly) const;
+
+ /**
+ * restore the EQ settings from this file
+ **/
+ bool load(const KURL &file);
+
+ /**
+ * convert the current EQ settings to string form,
+ * suitable for storage, the given string is the title
+ *
+ * @see fromString
+ **/
+ QString toString(const QString &name="stored") const;
+
+ /**
+ * undo a toString, restoring the EQ
+ * to the settings in the given string,
+ * emitting the changed signals
+ **/
+ bool fromString(const QString &str);
+
+public: // presets
+ /**
+ * create a preset with such a name
+ * and remember that it'l return an invalied Preset
+ * if the name already exists
+ *
+ * If smart is true, append a number to the end
+ * of the name, if one already exists by the given
+ **/
+ VPreset createPreset(const QString &name, bool smart=true);
+
+ /**
+ * return all the presets
+ * remember to setAutoDelete on this
+ **/
+ QValueList<VPreset> presets() const;
+
+ /**
+ * @returns the preset with the given name
+ * or an invalid Preset if none exists
+ **/
+ VPreset presetByName(const QString &name);
+
+ /**
+ * @returns the preset in the given file
+ * or an invalid Preset if none exists
+ **/
+ VPreset presetByFile(const QString &file);
+
+ /**
+ * @returns whether a preset with the
+ * given name exists
+ **/
+ bool presetExists(const QString &name) const;
+
+signals:
+ /**
+ * emitted when the number of bands has changed (and hence
+ * all my associated Bands are useless
+ **/
+ void changedBands();
+
+ /**
+ * emitted when something changes. Preamplication, band level,
+ * number of bands, enabled/disabled
+ **/
+ void changed();
+ /**
+ * emitted when the value of one or more of the bands
+ * has changed, but not immediately after
+ * a changedBands
+ **/
+ void modified();
+
+ void preampChanged();
+ void preampChanged(int);
+
+ void enabled();
+ void disabled();
+
+ void enabled(bool e);
+
+ /**
+ * emitted when a preset is created
+ **/
+ void created(VPreset preset);
+
+ /**
+ * emitted when the given @p preset is
+ * selected
+ **/
+ void selected(VPreset preset);
+
+ /**
+ * when @p preset has been renamed
+ **/
+ void renamed(VPreset preset);
+
+ /**
+ * the given @p preset has been removed
+ **/
+ void removed(VPreset preset);
+public slots:
+ /**
+ * set the preamplification
+ * it's logarithmic. 0 is no change
+ * negative numbers are loss in intensity
+ * positive numbers are a gain
+ * And +-100 is the most you'd need to go
+ **/
+ void setPreamp(int p);
+ /**
+ * turn on EQ
+ */
+ void enable();
+ /**
+ * turn off EQ
+ */
+ void disable();
+ /**
+ * turn on/off EQ depending on @p e
+ */
+ void setEnabled(bool e);
+ void setBands(int bands);
+ void setBands(int bands, bool interpolate);
+
+private:
+ virtual int level(int index) const;
+ virtual void setLevel(int index, int level);
+ void setLevels(const QValueList<int> &levels);
+
+private:
+ /**
+ * tell the filter to start using the new settings
+ * if @p full is false, it doesn't take the full
+ * update, but just the values (not the number of
+ * bands
+ **/
+ void update(bool full=false);
+};
+
+/**
+ * a preset stores the state of the equalizer
+ *
+ * VEqualizer provides a way to get a list of these
+ * or access them by name
+ *
+ * this acts as a reference to the preset, so
+ * it might be invalid in which case
+ * isValid() is false, isNull() is true, and
+ * operator bool() return false
+ **/
+class VPreset
+{
+ friend class VEqualizer;
+
+ struct Private;
+ Private *d;
+
+ void *_bc;
+ VPreset(const QString &file);
+public:
+ VPreset();
+ VPreset(const VPreset &copy);
+ VPreset & operator=(const VPreset &copy);
+ ~VPreset();
+ bool operator ==(const VPreset &other) const;
+
+ /**
+ * @returns the name of the preset, which is user visible
+ **/
+ QString name() const;
+ /**
+ * set the user-presentable name of the preset
+ *
+ * Equalizer will emit renamed
+ *
+ * @returns success. If another preset is named
+ * this, it'l fail.
+ **/
+ bool setName(const QString &name);
+
+ /**
+ * @returns the file that this preset is in
+ **/
+ QString file() const;
+
+ bool isValid() const;
+ bool isNull() const { return !isValid(); }
+ operator bool() const { return isValid(); }
+
+ /**
+ * save the state of the equalizer into this preset
+ **/
+ void save();
+
+ /**
+ * load the preset into the equalizer
+ **/
+ void load() const;
+
+ /**
+ * obliterate this preset!
+ **/
+ void remove();
+};
+
+#endif
diff --git a/noatun/library/noatun/video.h b/noatun/library/noatun/video.h
new file mode 100644
index 00000000..3655c489
--- /dev/null
+++ b/noatun/library/noatun/video.h
@@ -0,0 +1,60 @@
+#ifndef NOATUN__VIDEO_H
+#define NOATUN__VIDEO_H
+
+#include <kvideowidget.h>
+#include <arts/kmedia2.h>
+
+class QPopupMenu;
+
+/**
+ * a widget that contains the video being played
+ **/
+class VideoFrame : public KVideoWidget
+{
+Q_OBJECT
+ struct Private;
+ VideoFrame::Private *d;
+
+ static QPtrList<VideoFrame> frames;
+ static VideoFrame *whose;
+
+public:
+ VideoFrame(KXMLGUIClient *clientParent, QWidget *parent=0, const char *name=0, WFlags f=0);
+ VideoFrame(QWidget *parent = 0, const char *name=0, WFlags f=0);
+ ~VideoFrame();
+
+ /**
+ * which one has the video (or will have it next, if no video is playing)
+ **/
+ static VideoFrame *playing();
+
+ QPopupMenu *popupMenu(QWidget *parent);
+ QPopupMenu *popupMenu() { return popupMenu(this); }
+
+public slots:
+ /**
+ * only one VideoFrame can be playing a video, make this the one
+ **/
+ void give();
+
+private slots:
+ void stopped();
+ void changed();
+
+signals:
+ /**
+ * signaled when video is playing in here, when
+ * (width*height) != 0
+ **/
+ void acquired();
+ /**
+ * signaled when video is no longer playing
+ * here, when (width*heoght) == 0
+ */
+ void lost();
+};
+
+
+
+#endif
+
diff --git a/noatun/library/noatunarts/Equalizer.mcopclass b/noatun/library/noatunarts/Equalizer.mcopclass
new file mode 100644
index 00000000..85dac1eb
--- /dev/null
+++ b/noatun/library/noatunarts/Equalizer.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::Equalizer,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/EqualizerSSE.mcopclass b/noatun/library/noatunarts/EqualizerSSE.mcopclass
new file mode 100644
index 00000000..9002a4ad
--- /dev/null
+++ b/noatun/library/noatunarts/EqualizerSSE.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::EqualizerSSE,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/Equalizer_impl.cpp b/noatun/library/noatunarts/Equalizer_impl.cpp
new file mode 100644
index 00000000..5d852b17
--- /dev/null
+++ b/noatun/library/noatunarts/Equalizer_impl.cpp
@@ -0,0 +1,472 @@
+/*
+Copyright (C) 2001 Charles Samuels <charles@kde.org>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public License
+along with this library; see the file COPYING.LIB. If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+
+*/
+
+#include "noatunarts.h"
+#include "artsflow.h"
+#include "fft.h"
+#include <stdsynthmodule.h>
+#include <math.h>
+#include <cstring>
+
+using namespace std;
+using namespace Arts;
+
+/**
+ * This class is _VERY_ picky
+ * which is why Noatun has it's own Equalizer class,
+ * that does all the error checking and sends it to here
+ **/
+
+namespace Noatun
+{
+
+void resize(vector<float> &vec, unsigned int newsize)
+{
+ while (newsize < vec.size())
+ vec.pop_back();
+ while (newsize > vec.size())
+ vec.push_back(0.0);
+}
+
+class Equalizer_impl : public Equalizer_skel, public StdSynthModule
+{
+ vector<float> mLevels;
+
+ vector<BandPassInfo> mBandLeft, mBandRight;
+
+ vector<float> mLevelWidths;
+ vector<float> mLevelCenters;
+
+ bool mEnabled;
+ float mPreamp;
+
+
+ float *mBuffer;
+ unsigned int mBufferLength;
+
+ void reinit()
+ {
+ mBandLeft.clear();
+ mBandRight.clear();
+ for (unsigned int i=0; i< mLevelWidths.size(); ++i)
+ {
+ BandPassInfo nfo;
+ BandPassInit(&nfo, mLevelCenters[i], mLevelWidths[i]);
+ mBandLeft.push_back(nfo);
+ mBandRight.push_back(nfo);
+ }
+
+ }
+
+public:
+ void set(const std::vector<float>& levels, const std::vector<float>& centers, const std::vector<float>& widths)
+ {
+ mLevelCenters=centers;
+ mLevelWidths=widths;
+
+ mLevels=levels;
+ reinit();
+ }
+
+ vector<float>* levelCenters()
+ {
+ return new vector<float>(mLevelCenters);
+ }
+
+ void levelCenters(const vector<float> &l)
+ {
+ mLevelCenters=l;
+ reinit();
+ }
+
+ vector<float>* levelWidths()
+ {
+ return new vector<float>(mLevelWidths);
+ }
+
+ void levelWidths(const vector<float> &l)
+ {
+ mLevelWidths=l;
+ reinit();
+ }
+
+ vector<float>* levels()
+ {
+ return new vector<float>(mLevels);
+ }
+
+ void levels(const vector<float> &l)
+ {
+ mLevels=l;
+ reinit();
+ }
+
+ long bands()
+ {
+ return mLevels.size();
+ }
+
+ void bands(long b)
+ {
+ resize(mLevels, (int)b);
+ resize(mLevelWidths, (int)b);
+ resize(mLevelCenters, (int)b);
+ reinit();
+ }
+
+ long enabled()
+ {
+ return (long)mEnabled;
+ }
+
+ void enabled(long enabled)
+ {
+ mEnabled=(bool)enabled;
+ }
+
+ float preamp()
+ {
+ return mPreamp;
+ }
+
+ void preamp(float a)
+ {
+ mPreamp=a;
+ }
+
+ void streamInit()
+ {
+
+ }
+
+ void streamStart()
+ {
+
+ }
+
+/* BandPassInit(&nfoLeft, 15000.0, 5000.0);
+ * BandPassInit(&nfoLeft, 15000.0, 5000.0);
+ */
+
+ void calculateBlock(unsigned long samples)
+ {
+ // works by separating the bands
+ // multiplying, then adding
+ if (mEnabled && samples && &mLevels.front())
+ {
+
+ { // preamp;
+
+ float *left=inleft;
+ float *right=inright;
+ float *end=left+samples;
+
+ float *oleft=outleft;
+ float *oright=outright;
+
+ while (left<end)
+ {
+ // see the _long_ comment in
+ // kdemultimedia/arts/modules/synth_std_equalizer_impl.cc
+ if (::fabs(*left) + ::fabs(*right) < 0.00000001)
+ goto copy; // if you apologize, it's becomes ok
+ *oleft=*left * mPreamp;
+ *oright=*right * mPreamp;
+ ++left;
+ ++right;
+ ++oleft;
+ ++oright;
+ }
+ }
+
+ BandPassInfo *leftBand=&mBandLeft.front();
+ BandPassInfo *rightBand=&mBandRight.front();
+ float *level=&mLevels.front();
+ float *end=&mLevels.back();
+ float intensity=1.0/(float)mLevels.size();
+
+ if (mBufferLength != samples)
+ {
+ delete mBuffer;
+ mBuffer = new float[samples];
+ mBufferLength = samples;
+ }
+
+ register float *buffer=mBuffer;
+ register float *bufferEnd=buffer+samples;
+ while (level<end)
+ {
+ register float *buffIter, *outIter;
+ float levelAndIntensity=*level * intensity;
+
+ BandPass(leftBand, outleft, buffer, samples);
+ for (buffIter=buffer, outIter=outleft; buffIter<bufferEnd; ++buffIter, ++outIter)
+ *outIter+=(*buffIter) * levelAndIntensity;
+
+ BandPass(rightBand, outright, buffer, samples);
+ for (buffIter=buffer, outIter=outright; buffIter<bufferEnd; ++buffIter, ++outIter)
+ *outIter+=(*buffIter) * levelAndIntensity;
+
+ ++level;
+ ++leftBand;
+ ++rightBand;
+ }
+ }
+ else
+ {
+ copy:
+ // ASM optimized, so much faster
+ memcpy(outleft, inleft, samples*sizeof(float));
+ memcpy(outright, inright, samples*sizeof(float));
+ }
+
+ }
+
+ Equalizer_impl() : mEnabled(false)
+ {
+ mBuffer=0;
+ mBufferLength=0;
+ }
+
+ ~Equalizer_impl()
+ {
+ delete [] mBuffer;
+ }
+
+ // speed hack! assume that someone else will
+ // suspend us
+ AutoSuspendState autoSuspend() { return asSuspend; }
+
+};
+
+
+
+
+
+
+class EqualizerSSE_impl : public EqualizerSSE_skel, public StdSynthModule
+{
+ vector<float> mLevels;
+
+ vector<BandPassInfo> mBandLeft, mBandRight;
+
+ vector<float> mLevelWidths;
+ vector<float> mLevelCenters;
+
+ bool mEnabled;
+ float mPreamp;
+
+
+
+ void reinit()
+ {
+ mBandLeft.clear();
+ mBandRight.clear();
+ for (unsigned int i=0; i< mLevelWidths.size(); ++i)
+ {
+ BandPassInfo nfo;
+ BandPassInit(&nfo, mLevelCenters[i], mLevelWidths[i]);
+ mBandLeft.push_back(nfo);
+ mBandRight.push_back(nfo);
+ }
+
+ }
+
+public:
+ void set(const std::vector<float>& levels, const std::vector<float>& centers, const std::vector<float>& widths)
+ {
+ mLevelCenters=centers;
+ mLevelWidths=widths;
+
+ mLevels=levels;
+ reinit();
+ }
+
+ vector<float>* levelCenters()
+ {
+ return new vector<float>(mLevelCenters);
+ }
+
+ void levelCenters(const vector<float> &l)
+ {
+ mLevelCenters=l;
+ reinit();
+ }
+
+ vector<float>* levelWidths()
+ {
+ return new vector<float>(mLevelWidths);
+ }
+
+ void levelWidths(const vector<float> &l)
+ {
+ mLevelWidths=l;
+ reinit();
+ }
+
+ vector<float>* levels()
+ {
+ return new vector<float>(mLevels);
+ }
+
+ void levels(const vector<float> &l)
+ {
+ mLevels=l;
+ reinit();
+ }
+
+ long bands()
+ {
+ return mLevels.size();
+ }
+
+ void bands(long b)
+ {
+ resize(mLevels, (int)b);
+ resize(mLevelWidths, (int)b);
+ resize(mLevelCenters, (int)b);
+ reinit();
+ }
+
+ long enabled()
+ {
+ return (long)mEnabled;
+ }
+
+ void enabled(long enabled)
+ {
+ mEnabled=(bool)enabled;
+ }
+
+ float preamp()
+ {
+ return mPreamp;
+ }
+
+ void preamp(float a)
+ {
+ mPreamp=a;
+ }
+
+ void streamInit()
+ {
+
+ }
+
+ void streamStart()
+ {
+
+ }
+
+/* BandPassInit(&nfoLeft, 15000.0, 5000.0);
+ * BandPassInit(&nfoLeft, 15000.0, 5000.0);
+ */
+
+ void calculateBlock(unsigned long samples)
+ {
+#ifdef __i386__
+ // works by separating the bands
+ // multiplying, then adding
+ if (mEnabled && samples)
+ {
+ if (*inleft + *inright == 0.0)
+ goto copy; // just shut up :)
+
+ { // preamp;
+
+ float *left=inleft;
+ float *right=inright;
+ float *end=left+samples;
+
+ float *oleft=outleft;
+ float *oright=outright;
+
+ while (left<end)
+ {
+ *oleft=*left * mPreamp;
+ *oright=*right * mPreamp;
+ ++left;
+ ++right;
+ ++oleft;
+ ++oright;
+ }
+ }
+
+ BandPassInfo *leftBand=&mBandLeft.front();
+ BandPassInfo *rightBand=&mBandRight.front();
+ float *level=&mLevels.front();
+ float *end=&mLevels.back();
+ float intensity=1.0/(float)mLevels.size();
+
+ register float *buffer=new float[samples];
+ register float *bufferEnd=buffer+samples;
+ while (level<end)
+ {
+ register float *buffIter, *outIter;
+ float levelAndIntensity=*level * intensity;
+
+ BandPassSSE(leftBand, outleft, buffer, samples);
+ for (buffIter=buffer, outIter=outleft; buffIter<bufferEnd; ++buffIter, ++outIter)
+ *outIter+=(*buffIter) * levelAndIntensity;
+
+ BandPassSSE(rightBand, outright, buffer, samples);
+ for (buffIter=buffer, outIter=outright; buffIter<bufferEnd; ++buffIter, ++outIter)
+ *outIter+=(*buffIter) * levelAndIntensity;
+
+ ++level;
+ ++leftBand;
+ ++rightBand;
+ }
+ delete [] buffer;
+ }
+ else
+ {
+ copy:
+ // ASM optimized, so much faster
+ memcpy(outleft, inleft, samples*sizeof(float));
+ memcpy(outright, inright, samples*sizeof(float));
+ }
+#else
+ (void)samples; // squelch warnings
+#endif
+ }
+
+ EqualizerSSE_impl() : mEnabled(false)
+ {
+
+ }
+
+ ~EqualizerSSE_impl()
+ {
+ }
+
+ // speed hack! assume that someone else will
+ // suspend us
+ AutoSuspendState autoSuspend() { return asSuspend; }
+};
+
+
+REGISTER_IMPLEMENTATION(Equalizer_impl);
+REGISTER_IMPLEMENTATION(EqualizerSSE_impl);
+
+}
+
+#undef SAMPLES
+
diff --git a/noatun/library/noatunarts/FFTScope.mcopclass b/noatun/library/noatunarts/FFTScope.mcopclass
new file mode 100644
index 00000000..65826259
--- /dev/null
+++ b/noatun/library/noatunarts/FFTScope.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::FFTScope,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/FFTScopeStereo.mcopclass b/noatun/library/noatunarts/FFTScopeStereo.mcopclass
new file mode 100644
index 00000000..f463a2ed
--- /dev/null
+++ b/noatun/library/noatunarts/FFTScopeStereo.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::FFTScopeStereo,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/FFTScopes.cpp b/noatun/library/noatunarts/FFTScopes.cpp
new file mode 100644
index 00000000..ab2a3bbb
--- /dev/null
+++ b/noatun/library/noatunarts/FFTScopes.cpp
@@ -0,0 +1,422 @@
+/*
+Copyright (C) 2000 Stefan Westerfeld <stefan@space.twc.de>
+ 2000 Charles Samuels <charles@kde.org>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public License
+along with this library; see the file COPYING.LIB. If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+
+*/
+
+#include "noatunarts.h"
+#include "artsflow.h"
+#include "fft.h"
+#include <stdsynthmodule.h>
+#include <cmath>
+#include <cstring>
+
+#include <iostream>
+
+using namespace std;
+using namespace Arts;
+
+namespace Noatun
+{
+
+#define SAMPLES 4096
+
+static void doFft(float combine, float *inBuffer, vector<float> &scope)
+{
+ float out_real[SAMPLES],out_img[SAMPLES];
+ fft_float(SAMPLES,0,inBuffer,0,out_real,out_img);
+
+ scope.clear();
+
+ int previous=0;
+ int index=20;
+
+ while (previous < 2048 && index < 2048)
+ {
+ int end = int(std::exp(double(index)*combine));
+ float xrange = 0.0;
+
+ while (previous < end)
+ {
+ xrange += (std::fabs(out_img[previous]) + std::fabs(out_real[previous]));
+ previous++;
+ }
+
+ xrange /= float(SAMPLES);
+ scope.push_back(xrange);
+
+ index++;
+ }
+}
+
+
+class FFTScopeStereo_impl : public FFTScopeStereo_skel, public StdSynthModule
+{
+protected:
+ vector<float> mScopeLeft;
+ vector<float> mScopeRight;
+
+ float mCombine;
+
+ float *mWindow;
+
+ float *mInBufferLeft, *mInBufferRight;
+ unsigned long mInBufferPos;
+
+public:
+ void bandResolution(float res) { mCombine=res; }
+ float bandResolution() { return mCombine; }
+ void streamInit()
+ {
+ unsigned long i;
+ for(i=0;i<SAMPLES;i++)
+ {
+ float x = (float)i/(float)SAMPLES;
+ // double it, since we're at half intensity without
+ // adding both channels
+ mWindow[i] = sin(x*DDC_PI)*sin(x*DDC_PI)*2;
+
+ mInBufferLeft[i] = 0;
+ mInBufferRight[i] = 0;
+ }
+ doFft(mCombine, mInBufferLeft, mScopeLeft);
+ doFft(mCombine, mInBufferRight, mScopeRight);
+ }
+ void streamStart()
+ {
+ mInBufferPos = 0;
+ }
+ vector<float> *scopeLeft()
+ {
+ return new vector<float>(mScopeLeft);
+ }
+ vector<float> *scopeRight()
+ {
+ return new vector<float>(mScopeRight);
+ }
+ /*
+ in audio stream inleft, inright;
+ out audio stream outleft, outright;
+ */
+ void calculateBlock(unsigned long samples)
+ {
+ unsigned long i;
+ for(i=0;i<samples;i++)
+ {
+ mInBufferLeft[mInBufferPos] = inleft[i]*mWindow[mInBufferPos];
+ mInBufferRight[mInBufferPos] = inright[i]*mWindow[mInBufferPos];
+ if(++mInBufferPos == SAMPLES)
+ {
+ doFft(mCombine, mInBufferLeft, mScopeLeft);
+ doFft(mCombine, mInBufferRight, mScopeRight);
+ mInBufferPos = 0;
+ }
+ /*
+ monitoring only tasks can't be done with that StereoEffect
+ interface nicely - copy input to output until there is
+ something better
+ */
+ outleft[i] = inleft[i];
+ outright[i] = inright[i];
+ }
+ }
+
+ FFTScopeStereo_impl() : mCombine(0.152492)
+ {
+ mWindow = new float[SAMPLES];
+ mInBufferLeft = new float[SAMPLES];
+ mInBufferRight = new float[SAMPLES];
+
+ }
+ ~FFTScopeStereo_impl()
+ {
+ delete [] mWindow;
+ delete [] mInBufferLeft;
+ delete [] mInBufferRight;
+ }
+
+ AutoSuspendState autoSuspend() { return asSuspend; }
+
+};
+
+class FFTScope_impl : public FFTScope_skel, public StdSynthModule
+{
+protected:
+ vector<float> mScope;
+
+ float mCombine;
+
+ float *mWindow;
+ float *mInBuffer;
+ unsigned long mInBufferPos;
+
+public:
+ void bandResolution(float res) { mCombine=res; }
+ float bandResolution() { return mCombine; }
+ void streamInit()
+ {
+ unsigned long i;
+ for(i=0;i<SAMPLES;i++)
+ {
+ float x = (float)i/(float)SAMPLES;
+ mWindow[i] = sin(x*DDC_PI)*sin(x*DDC_PI);
+ mInBuffer[i] = 0;
+ }
+ doFft(mCombine, mInBuffer, mScope); // initialize so that we never return an empty scope
+ }
+ void streamStart()
+ {
+ mInBufferPos = 0;
+ }
+ vector<float> *scope()
+ {
+ return new vector<float>(mScope);
+ }
+
+ /*
+ in audio stream inleft, inright;
+ out audio stream outleft, outright;
+ */
+ void calculateBlock(unsigned long samples)
+ {
+ unsigned long i;
+
+ float *inBufferIt=mInBuffer+mInBufferPos;
+ float *inleftIt=inleft;
+ float *inrightIt=inright;
+ float *windowIt=mWindow+mInBufferPos;
+
+ for(i=0;i<samples;i++)
+ {
+ *inBufferIt = (*inleftIt + *inrightIt)* (*windowIt);
+ if(++mInBufferPos == SAMPLES)
+ {
+ doFft(mCombine, mInBuffer, mScope);
+ mInBufferPos = 0;
+ inBufferIt=mInBuffer;
+ }
+ inBufferIt++;
+ inleftIt++;
+ inrightIt++;
+ windowIt++;
+ }
+ /*
+ monitoring only tasks can't be done with that StereoEffect
+ interface nicely - copy input to output until there is
+ something better
+ */
+ memcpy(outleft, inleft, sizeof(float)*samples);
+ memcpy(outright, inright, sizeof(float)*samples);
+
+ }
+
+ FFTScope_impl() : mCombine(0.152492)
+ {
+ mWindow = new float[SAMPLES];
+ mInBuffer = new float[SAMPLES];
+ }
+
+ ~FFTScope_impl()
+ {
+ delete [] mWindow;
+ delete [] mInBuffer;
+ }
+
+ AutoSuspendState autoSuspend() { return asSuspend; }
+};
+
+class RawScope_impl : public RawScope_skel, public StdSynthModule
+{
+protected:
+ float *mScope;
+
+ int mScopeLength;
+ float *mScopeEnd;
+ float *mCurrent;
+
+public:
+ vector<float> *scope()
+ {
+ vector<float> *buf = new vector<float>;
+ buf->resize(mScopeLength);
+ char *front = (char *)(&buf->front());
+ memcpy(front, mCurrent, (mScopeEnd - mCurrent) * sizeof(float));
+ memcpy(front + (mScopeEnd - mCurrent)*sizeof(float), mScope,
+ (mCurrent - mScope) * sizeof(float));
+ return buf;
+ }
+
+ void buffer(long len)
+ {
+ delete [] mScope;
+
+ mScopeLength=len;
+ mScope=new float[len];
+ mScopeEnd=mScope+mScopeLength;
+ mCurrent=mScope;
+
+ memset(mScope, 0, mScopeLength);
+ }
+
+ long buffer()
+ {
+ return mScopeLength;
+ }
+
+ void calculateBlock(unsigned long samples)
+ {
+ for (unsigned long i=0; i<samples; ++i)
+ {
+ for (; mCurrent<mScopeEnd && i<samples; ++mCurrent, ++i)
+ {
+ *mCurrent = inleft[i] + inright[i];
+ }
+ if (mCurrent>=mScopeEnd)
+ mCurrent=mScope;
+ }
+
+ memcpy(outleft, inleft, sizeof(float)*samples);
+ memcpy(outright, inright, sizeof(float)*samples);
+
+ }
+
+ RawScope_impl()
+ {
+ mScope=0;
+ buffer(512);
+
+ }
+
+ ~RawScope_impl()
+ {
+ delete [] mScope;
+ }
+
+ AutoSuspendState autoSuspend() { return asSuspend; }
+};
+
+class RawScopeStereo_impl : public RawScopeStereo_skel, public StdSynthModule
+{
+protected:
+ int mScopeLength;
+
+ float *mScopeLeft;
+ float *mScopeEndLeft;
+ float *mCurrentLeft;
+
+ float *mScopeRight;
+ float *mScopeEndRight;
+ float *mCurrentRight;
+
+public:
+ vector<float> *scopeLeft()
+ {
+ vector<float> *buf = new vector<float>;
+ buf->resize(mScopeLength);
+ char *front = (char *)(&buf->front());
+ memcpy(front, mCurrentLeft, (mScopeEndLeft - mCurrentLeft) * sizeof(float));
+ memcpy(front + (mScopeEndLeft - mCurrentLeft)*sizeof(float), mScopeLeft,
+ (mCurrentLeft - mScopeLeft) * sizeof(float));
+ return buf;
+ }
+
+ vector<float> *scopeRight()
+ {
+ vector<float> *buf = new vector<float>;
+ buf->resize(mScopeLength);
+ char *front = (char *)(&buf->front());
+ memcpy(front, mCurrentRight, (mScopeEndRight - mCurrentRight) * sizeof(float));
+ memcpy(front + (mScopeEndRight - mCurrentRight)*sizeof(float), mScopeRight,
+ (mCurrentRight - mScopeRight) * sizeof(float));
+ return buf;
+ }
+
+ void buffer(long len)
+ {
+ delete [] mScopeRight;
+ delete [] mScopeLeft;
+
+ mScopeLength=len;
+ mScopeRight=new float[len];
+ mScopeLeft=new float[len];
+ mScopeEndRight=mScopeRight+mScopeLength;
+ mScopeEndLeft=mScopeLeft+mScopeLength;
+ mCurrentRight=mScopeRight;
+ mCurrentLeft=mScopeLeft;
+
+ memset(mScopeRight, 0, mScopeLength);
+ memset(mScopeLeft, 0, mScopeLength);
+ }
+
+ long buffer()
+ {
+ return mScopeLength;
+ }
+
+ void calculateBlock(unsigned long samples)
+ {
+ for (unsigned long i=0; i<samples; ++i)
+ {
+ for (; mCurrentLeft<mScopeEndLeft && i<samples; ++mCurrentLeft, ++i)
+ {
+ *mCurrentLeft = inleft[i];
+ }
+ if (mCurrentLeft>=mScopeEndLeft)
+ mCurrentLeft=mScopeLeft;
+ }
+
+ for (unsigned long i=0; i<samples; ++i)
+ {
+ for (; mCurrentRight<mScopeEndRight && i<samples; ++mCurrentRight, ++i)
+ {
+ *mCurrentRight = inright[i];
+ }
+ if (mCurrentRight>=mScopeEndRight)
+ mCurrentRight=mScopeRight;
+ }
+
+ memcpy(outleft, inleft, sizeof(float)*samples);
+ memcpy(outright, inright, sizeof(float)*samples);
+ }
+
+ RawScopeStereo_impl()
+ {
+ mScopeLeft=mScopeRight=0;
+ buffer(512);
+
+ }
+
+ ~RawScopeStereo_impl()
+ {
+ delete [] mScopeRight;
+ delete [] mScopeLeft;
+ }
+
+ AutoSuspendState autoSuspend() { return asSuspend; }
+};
+
+
+
+REGISTER_IMPLEMENTATION(FFTScope_impl);
+REGISTER_IMPLEMENTATION(FFTScopeStereo_impl);
+REGISTER_IMPLEMENTATION(RawScope_impl);
+REGISTER_IMPLEMENTATION(RawScopeStereo_impl);
+
+}
+
+#undef SAMPLES
diff --git a/noatun/library/noatunarts/Listener.mcopclass b/noatun/library/noatunarts/Listener.mcopclass
new file mode 100644
index 00000000..81f19d38
--- /dev/null
+++ b/noatun/library/noatunarts/Listener.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::Listener,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/Makefile.am b/noatun/library/noatunarts/Makefile.am
new file mode 100644
index 00000000..a08e279d
--- /dev/null
+++ b/noatun/library/noatunarts/Makefile.am
@@ -0,0 +1,33 @@
+INCLUDES= -I$(kde_includes)/arts $(all_includes)
+KDE_OPTIONS = nofinal
+
+lib_LTLIBRARIES = libnoatunarts.la
+libnoatunarts_la_SOURCES = noatunarts.cc fft.c Equalizer_impl.cpp \
+ FFTScopes.cpp StereoEffectStack_impl.cpp \
+ StereoVolumeControl_impl.cpp Session_impl.cpp
+libnoatunarts_la_COMPILE_FIRST = noatunarts.h
+libnoatunarts_la_LDFLAGS = $(all_libraries) -avoid-version -no-undefined
+libnoatunarts_la_LIBADD = -lkmedia2_idl -lsoundserver_idl -lartsflow
+libnoatunarts_la_METASOURCES = AUTO
+
+noatunarts.mcoptype: noatunarts.h
+noatunarts.mcopclass: noatunarts.h
+
+noatunarts.cc noatunarts.h: noatunarts.idl
+ $(MCOPIDL) -t -I$(kde_includes)/arts $(srcdir)/noatunarts.idl
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = noatunarts.mcoptype noatunarts.mcopclass
+
+mcopclassdir = $(libdir)/mcop/Noatun
+mcopclass_DATA = Equalizer.mcopclass FFTScopeStereo.mcopclass StereoEffectStack.mcopclass \
+ EqualizerSSE.mcopclass RawScope.mcopclass StereoVolumeControl.mcopclass \
+ FFTScope.mcopclass RawScopeStereo.mcopclass StereoVolumeControlSSE.mcopclass \
+ Session.mcopclass Listener.mcopclass
+
+noatuninclude_HEADERS= noatunarts.h
+
+noatunincludedir = $(includedir)/noatun
+
+DISTCLEANFILES = noatunarts.cc noatunarts.h noatunarts.mcopclass noatunarts.mcoptype
+
diff --git a/noatun/library/noatunarts/RawScope.mcopclass b/noatun/library/noatunarts/RawScope.mcopclass
new file mode 100644
index 00000000..fb3d95be
--- /dev/null
+++ b/noatun/library/noatunarts/RawScope.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::RawScope,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/RawScopeStereo.mcopclass b/noatun/library/noatunarts/RawScopeStereo.mcopclass
new file mode 100644
index 00000000..cad987ee
--- /dev/null
+++ b/noatun/library/noatunarts/RawScopeStereo.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::RawScopeStereo,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/Session.mcopclass b/noatun/library/noatunarts/Session.mcopclass
new file mode 100644
index 00000000..036b8409
--- /dev/null
+++ b/noatun/library/noatunarts/Session.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::Session,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/Session_impl.cpp b/noatun/library/noatunarts/Session_impl.cpp
new file mode 100644
index 00000000..63912801
--- /dev/null
+++ b/noatun/library/noatunarts/Session_impl.cpp
@@ -0,0 +1,82 @@
+#include "noatunarts.h"
+#include <list>
+#include <algorithm>
+
+using namespace Arts;
+using namespace std;
+
+static bool compareArtsObjects(const Noatun::Listener &left, const Noatun::Listener &right)
+{
+ return left._isEqual(right);
+}
+
+list<Noatun::Listener>::iterator find(list<Noatun::Listener> &v, const Noatun::Listener &is,
+ bool (*compare)(const Noatun::Listener& left, const Noatun::Listener& right))
+{
+ for (list<Noatun::Listener>::iterator i=v.begin(); i!=v.end(); ++i)
+ {
+ if ((*compare)(is, *i))
+ return i;
+ }
+
+ return v.end();
+}
+
+static void sendMessage(Noatun::Listener l)
+{
+ l.message();
+}
+
+namespace Noatun
+{
+
+class Session_impl : public Session_skel
+{
+ list<Listener> listeners;
+
+ long mPid;
+
+public:
+
+ ~Session_impl()
+ {
+ for_each(listeners.begin(), listeners.end(), sendMessage);
+ }
+
+ long pid() { return mPid; }
+ void pid(long p) { mPid=p; }
+
+
+ void addListener(Noatun::Listener listener)
+ {
+ listeners.push_back(listener);
+ }
+
+ void removeListener(Noatun::Listener listener)
+ {
+ list<Listener>::iterator i=
+ find(listeners, listener, &compareArtsObjects);
+ if (i!=listeners.end())
+ listeners.erase(i);
+ }
+
+};
+
+class Listener_impl : public Listener_skel
+{
+
+private:
+ virtual void message()
+ {
+ // hmm
+ }
+
+};
+
+
+REGISTER_IMPLEMENTATION(Session_impl);
+REGISTER_IMPLEMENTATION(Listener_impl);
+
+
+} // namespace Noatun
+
diff --git a/noatun/library/noatunarts/StereoEffectStack.mcopclass b/noatun/library/noatunarts/StereoEffectStack.mcopclass
new file mode 100644
index 00000000..71c9a9cf
--- /dev/null
+++ b/noatun/library/noatunarts/StereoEffectStack.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::StereoEffectStack,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/StereoEffectStack_impl.cpp b/noatun/library/noatunarts/StereoEffectStack_impl.cpp
new file mode 100644
index 00000000..684d9694
--- /dev/null
+++ b/noatun/library/noatunarts/StereoEffectStack_impl.cpp
@@ -0,0 +1,253 @@
+#include "noatunarts.h"
+
+#include <artsflow.h>
+#include <flowsystem.h>
+#include <stdsynthmodule.h>
+#include <debug.h>
+
+using namespace std;
+using namespace Arts;
+
+namespace Noatun
+{
+class StereoEffectStack_impl : public StereoEffectStack_skel, public StdSynthModule
+{
+ public:
+ long nextID;
+
+ struct EffectEntry
+ {
+ StereoEffect effect;
+ string name;
+ long id;
+ };
+ list<EffectEntry *> fx;
+
+ void xconnect(bool connect, Object from, string fromP, Object to, string toP)
+ {
+ if(connect)
+ from._node()->connect(fromP,to._node(),toP);
+ else
+ from._node()->disconnect(fromP,to._node(),toP);
+ }
+
+ void xvirtualize(bool connect, string myPort, Object impl, string implPort)
+ {
+ if(connect)
+ _node()->virtualize(myPort,impl._node(),implPort);
+ else
+ _node()->devirtualize(myPort,impl._node(),implPort);
+ }
+
+ void internalconnect(bool c)
+ {
+ if(fx.empty())
+ {
+ /* no effects - forward input through to output */
+ xvirtualize(c,"outleft",Object::_from_base(this->_copy()),"inleft");
+ xvirtualize(c,"outright",Object::_from_base(this->_copy()),"inright");
+ }
+ else
+ {
+ list<EffectEntry *>::iterator ei;
+ EffectEntry *laste = 0;
+
+ long count = 0;
+ for(ei = fx.begin(); ei != fx.end(); ei++, count++)
+ {
+ EffectEntry *e = *ei;
+ if(count == 0) /* top of chain? virtualize to effect */
+ {
+ xvirtualize(c,"inleft",e->effect,"inleft");
+ xvirtualize(c,"inright",e->effect,"inright");
+ }
+ else /* not top? connect last effect to current effect */
+ {
+ xconnect(c,laste->effect,"outleft",e->effect,"inleft");
+ xconnect(c,laste->effect,"outright",e->effect,"inright");
+ }
+ laste = e;
+ }
+ /* end: virtualize effect output to our output */
+ xvirtualize(c,"outleft",laste->effect,"outleft");
+ xvirtualize(c,"outright",laste->effect,"outright");
+ }
+ }
+ void disconnect() { internalconnect(false); }
+ void reconnect() { internalconnect(true); }
+
+
+ long insertAfter(long after, StereoEffect effect, const string &name)
+ {
+ arts_return_val_if_fail(!effect.isNull(),0);
+ disconnect();
+
+ list<EffectEntry*>::iterator i = fx.begin();
+
+ bool found=false;
+ // seek through until we find 'after'
+ while(i != fx.end())
+ if((*i)->id == after)
+ {
+ found = true;
+ break;
+ }
+ else
+ i++;
+
+ long newId=0;
+ if (found)
+ {
+ i++;
+ EffectEntry *e = new EffectEntry;
+ e->effect=effect;
+ e->name=name;
+ e->id=nextID++;
+ fx.insert(i, e);
+ newId=e->id;
+ }
+ else
+ arts_warning("StereoEffectStack::insertAfter failed. "
+ "id %d not found?", after);
+
+ reconnect();
+ return newId;
+
+ }
+
+ void move(long after, long item)
+ {
+ arts_return_if_fail(item != 0);
+ disconnect();
+
+ list<EffectEntry*>::iterator iAfter=fx.begin();
+ bool found=false;
+ if (after)
+ while(iAfter != fx.end())
+ if((*iAfter)->id == after)
+ {
+ found = true;
+ iAfter++;
+ break;
+ }
+ else
+ iAfter++;
+ else
+ found=true;
+
+ list<EffectEntry*>::iterator iItem=fx.begin();
+ while (iItem != fx.end())
+ if((*iItem)->id == item)
+ {
+ found &= true;
+ break;
+ }
+ else
+ iItem++;
+ if (!found)
+ arts_warning("StereoEffectStack::move couldn't find items");
+ else
+ {
+ fx.insert(iAfter, *iItem);
+ fx.erase(iItem);
+ }
+
+ reconnect();
+
+ }
+
+ vector<long> *effectList()
+ {
+ vector<long> *items=new vector<long>;
+ for (list<EffectEntry*>::iterator i=fx.begin(); i!=fx.end();i++)
+ items->push_back((*i)->id);
+ return items;
+ }
+
+ // as stolen from stereoeffectstack_impl.cc
+ StereoEffectStack_impl() : nextID(1)
+ {
+ reconnect();
+ }
+
+ ~StereoEffectStack_impl()
+ {
+ // disconnect remaining effects
+ EffectEntry *laste = 0;
+ list<EffectEntry *>::iterator ei;
+
+ for(ei = fx.begin(); ei != fx.end(); ei++)
+ {
+ EffectEntry *e = *ei;
+ if(laste)
+ {
+ xconnect(false,laste->effect,"outleft",e->effect,"inleft");
+ xconnect(false,laste->effect,"outright",e->effect,"inright");
+ }
+ laste = e;
+ }
+ // delete remaining effect entries
+ for(ei = fx.begin(); ei != fx.end(); ei++)
+ delete *ei;
+ fx.clear();
+ }
+ long insertTop(StereoEffect effect, const string& name)
+ {
+ arts_return_val_if_fail(!effect.isNull(),0);
+
+ disconnect();
+ EffectEntry *e = new EffectEntry();
+ e->effect = effect;
+ e->name = name;
+ e->id = nextID++;
+ fx.push_front(e);
+ reconnect();
+ return e->id;
+ }
+ long insertBottom(StereoEffect effect, const string& name)
+ {
+ arts_return_val_if_fail(!effect.isNull(),0);
+
+ disconnect();
+ EffectEntry *e = new EffectEntry();
+ e->effect = effect;
+ e->name = name;
+ e->id = nextID++;
+ fx.push_back(e);
+ reconnect();
+ return e->id;
+ }
+
+ void remove(long ID)
+ {
+ arts_return_if_fail(ID != 0);
+
+ bool found = false;
+ disconnect();
+ list<EffectEntry *>::iterator ei = fx.begin();
+
+ while(ei != fx.end())
+ {
+ if((*ei)->id == ID) {
+ found = true;
+ delete (*ei);
+ fx.erase(ei);
+ ei = fx.begin();
+ }
+ else ei++;
+ }
+ if(!found) {
+ arts_warning("StereoEffectStack::remove failed. id %d not found?",
+ ID);
+ }
+ reconnect();
+ }
+
+ AutoSuspendState autoSuspend() { return asSuspend; }
+
+};
+
+REGISTER_IMPLEMENTATION(StereoEffectStack_impl);
+
+}
+
diff --git a/noatun/library/noatunarts/StereoVolumeControl.mcopclass b/noatun/library/noatunarts/StereoVolumeControl.mcopclass
new file mode 100644
index 00000000..c4aded7e
--- /dev/null
+++ b/noatun/library/noatunarts/StereoVolumeControl.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::StereoVolumeControl,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/StereoVolumeControlSSE.mcopclass b/noatun/library/noatunarts/StereoVolumeControlSSE.mcopclass
new file mode 100644
index 00000000..9a979f30
--- /dev/null
+++ b/noatun/library/noatunarts/StereoVolumeControlSSE.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::StereoVolumeControlSSE,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/StereoVolumeControl_impl.cpp b/noatun/library/noatunarts/StereoVolumeControl_impl.cpp
new file mode 100644
index 00000000..425704f6
--- /dev/null
+++ b/noatun/library/noatunarts/StereoVolumeControl_impl.cpp
@@ -0,0 +1,181 @@
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <artsflow.h>
+#include <stdsynthmodule.h>
+#include <flowsystem.h>
+#include "noatunarts.h"
+
+using namespace Arts;
+
+namespace Noatun
+{
+
+class StereoVolumeControl_impl : virtual public StereoVolumeControl_skel,
+ virtual public StdSynthModule
+{
+ float mPercent;
+ float level;
+public:
+ StereoVolumeControl_impl() : mPercent(1.0), level(0.0)
+ { }
+
+ /*attribute float scaleFactor;*/
+ void percent(float p) { mPercent=p; }
+ float percent() { return mPercent; }
+
+ void calculateBlock(unsigned long samples)
+ {
+ register float *left=inleft;
+ register float *right=inright;
+ register float *oleft=outleft;
+ register float *oright=outright;
+
+ level = *right + *left;
+
+ register float p=mPercent;
+
+ register float *end=left+samples;
+
+ while (left<end)
+ {
+ *oleft=*left * p;
+ *oright=*right * p;
+
+ ++left;
+ ++right;
+ ++oleft;
+ ++oright;
+ }
+ }
+
+ AutoSuspendState autoSuspend()
+ {
+ return (level < 0.001) ? asSuspend : asNoSuspend;
+ }
+};
+
+class StereoVolumeControlSSE_impl : virtual public Noatun::StereoVolumeControlSSE_skel,
+ virtual public StdSynthModule
+{
+ float mPercent;
+ float level;
+
+public:
+ StereoVolumeControlSSE_impl() : mPercent(1.0), level(0.0)
+ { }
+
+ /*attribute float scaleFactor;*/
+ void percent(float p) { mPercent=p; }
+ float percent() { return mPercent; }
+
+ void calculateBlock(unsigned long samples)
+ {
+#ifdef HAVE_X86_SSE
+ float *left=inleft;
+ float *right=inright;
+ float *oleft=outleft;
+ float *oright=outright;
+
+ level = *right + *left;
+
+ // need to copy the data members to locals to get enough
+ // spare registers (malte)
+
+ long p = (long)(mPercent*100.0);
+ __asm__ __volatile__(
+ "pushl $100 \n"
+ "fildl (%%esp) \n"
+#if defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)
+ "addl $4, %%esp \n"
+#endif
+ "fildl %5 \n"
+ "fdivp \n" // percent / 100.0
+#if defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)
+ "pushl $100 \n"
+#endif
+ "fstps (%%esp) \n"
+ "movss (%%esp), %%xmm1 \n"
+ "shufps $0x00, %%xmm1, %%xmm1 \n" // percentage in all of xmm1
+ "addl $4, %%esp \n"
+#if defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)
+ "subl $4, %4 \n"
+ "jl .l2 \n" // samples < 4
+#else
+ "pushl %4 \n" // save sample count
+ "shrl $2, %4 \n"
+ "jz .l2 \n" // samples < 4
+#endif
+ "xorl %%ecx, %%ecx \n"
+
+ ".l1: \n"
+ // left
+ "movups (%0, %%ecx, 8), %%xmm0 \n"
+ "mulps %%xmm1, %%xmm0 \n"
+ "movl %2, %%eax \n"
+ "movups %%xmm0, (%%eax, %%ecx, 8) \n"
+ // right
+ "movups (%1, %%ecx, 8), %%xmm0 \n"
+ "mulps %%xmm1, %%xmm0 \n"
+ "movl %3, %%eax \n"
+ "movups %%xmm0, (%%eax, %%ecx, 8) \n"
+
+ "incl %%ecx \n"
+ "incl %%ecx \n"
+#if defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)
+ "subl $4, %4 \n"
+ "jge .l1 \n"
+ ".l2: \n"
+ "addl $4, %4 \n"
+#else
+ "decl %4 \n"
+ "jnz .l1 \n"
+ ".l2: \n"
+ "popl %4 \n" // restore sample count
+ "andl $3, %4 \n"
+#endif
+ "jz .l4 \n"
+
+ // calculate remaining samples for samples % 4 != 0
+ "shll $1, %%ecx \n"
+ ".l3: \n"
+ "movss (%0, %%ecx, 4), %%xmm0 \n" // load left
+ "movss (%1, %%ecx, 4), %%xmm2 \n" // load right
+ "shufps $0x00, %%xmm2, %%xmm0 \n" // both channels in xmm0
+ "mulps %%xmm1, %%xmm0 \n"
+ "movl %2, %%eax \n"
+ "movss %%xmm0, (%%eax, %%ecx, 4) \n" // store left
+ "shufps $0x02, %%xmm0, %%xmm0 \n"
+ "movl %3, %%eax \n"
+ "movss %%xmm0, (%%eax, %%ecx, 4) \n" // store right
+ "incl %%ecx \n"
+ "decl %4 \n"
+ "jnz .l3 \n"
+
+ ".l4: \n"
+ "emms \n"
+ :
+ : "r" (left), // %0
+ "r" (right), // %1
+ "m" (oleft), // %2
+ "m" (oright), // %3
+ "r" (samples), // %4
+ "m" (p) // %5
+ : "eax", "ecx"
+ );
+#endif
+ }
+
+ AutoSuspendState autoSuspend()
+ {
+ return (level < 0.001) ? asSuspend : asNoSuspend;
+ }
+};
+
+REGISTER_IMPLEMENTATION(StereoVolumeControlSSE_impl);
+REGISTER_IMPLEMENTATION(StereoVolumeControl_impl);
+
+}
+
diff --git a/noatun/library/noatunarts/fft.c b/noatun/library/noatunarts/fft.c
new file mode 100644
index 00000000..86d647fb
--- /dev/null
+++ b/noatun/library/noatunarts/fft.c
@@ -0,0 +1,384 @@
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include "fft.h"
+
+#define TRUE 1
+#define FALSE 0
+
+/*
+ band pass filter for the Eq. This is a modification of Kai Lassfolk's work, as
+ removed from the Sound Processing Kit:
+
+ Sound Processing Kit - A C++ Class Library for Audio Signal Processing
+ Copyright (C) 1995-1998 Kai Lassfolk
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#define SAMPLERATE 44100
+
+#ifndef M_PI
+#define M_PI DDC_PI
+#endif
+
+void BandPassInit(struct BandPassInfo *ip, float center, float bw)
+{
+ ip->center = center;
+ ip->bandwidth = bw;
+
+ ip->C = 1.0 / tan(M_PI * bw / SAMPLERATE);
+ ip->D = 2 * cos(2 * M_PI * center / SAMPLERATE);
+
+ ip->a[0] = 1.0 / (1.0 + ip->C);
+ ip->a[1] = 0.0;
+ ip->a[2] = -ip->a[0];
+
+ ip->b[0] = -ip->C * ip->D * ip->a[0];
+ ip->b[1] = (ip->C - 1.0) * ip->a[0];
+
+ ip->bufferX[0] = ip->bufferX[1] = 0.0;
+ ip->bufferY[0] = ip->bufferY[1] = 0.0;
+}
+void BandPassSSE(struct BandPassInfo *ip, float *inbuffer, float *outbuffer, unsigned long samples)
+{
+#ifdef HAVE_X86_SSE
+ __asm__(
+ "testl %0, %0 \n"
+ "jz .l5 \n" /* if (!samples) */
+
+ "movl %1, %%ecx \n"
+ "movups 0x10(%%ecx), %%xmm2 \n" /* ip->a[0] */
+ "shufps $0x00, %%xmm2, %%xmm2 \n" /* ip->a[0] all over xmm3 */
+ "movups 0x14(%%ecx), %%xmm4 \n" /* xmm4 = {ip->a[1], ip->a[2], ip->b} */
+ "movups 0x24(%%ecx), %%xmm5 \n" /* xmm5 = {ip->bufferX, ip->bufferY} */
+ "xorl %%ecx, %%ecx \n" /* i = 0 */
+ "movl $1, %%edx \n" /* j = 1 */
+ "prefetcht0 (%2) \n"
+ ".l1: \n"
+
+ "decl %%edx \n" /* --j */
+ "jnz .l4 \n" /* if (j) */
+
+ /* only load single values if less than four remain in inbuffer */
+ "testl $0xfffffffc, %0 \n"
+ "jnz .l2 \n"
+ "movss (%2, %%ecx, 4), %%xmm3\n"
+ "movl $1, %%edx \n"
+ "jmp .l3 \n"
+ ".l2: \n"
+ /* {inbuffer[i], inbuffer[i+1], inbuffer[i+2], inbuffer[i+3]} * ip->a[0] */
+ "movups (%2, %%ecx, 4), %%xmm3\n"
+ "movl $3, %%edx \n" /* j = 3 */
+ ".l3: \n"
+ "movaps %%xmm3, %%xmm6 \n"
+ "mulps %%xmm2, %%xmm3 \n"
+ ".l4: \n"
+
+ /* {ip->a[1], ip->a[2], ip->b} * {ip->bufferX, ip->bufferY} */
+ "movaps %%xmm4, %%xmm0 \n"
+ "mulps %%xmm5, %%xmm0 \n"
+ "movaps %%xmm0, %%xmm1 \n"
+ /* xmm0 = {xmm0[0] + xmm0[1], <unused>, xmm0[2] + xmm0[3], <unused>} */
+ "shufps $0xb1, %%xmm0, %%xmm1 \n"
+ "addps %%xmm1, %%xmm0 \n"
+ /* xmm0[0] -= xmm0[2] */
+ "movhlps %%xmm0, %%xmm1 \n"
+ "subss %%xmm1, %%xmm0 \n"
+ "addss %%xmm3, %%xmm0 \n" /* xmm0[0] += inbuffer[i] * ip->a[0] */
+ "movss %%xmm0, (%3, %%ecx, 4)\n" /* outbuffer[i] = xmm0[0] */
+
+ /* xmm5 = {inbuffer[i], xmm5[0], outbuffer[i], xmm5[2]} */
+ "shufps $0x24, %%xmm5, %%xmm0 \n"
+ "shufps $0x81, %%xmm0, %%xmm5 \n"
+ "movss %%xmm6, %%xmm5 \n"
+
+ /* right-shift xmm3 (inbuffer * ip->a[0]) and xmm6 (inbuffer) */
+ "shufps $0x39, %%xmm3, %%xmm3 \n"
+ "shufps $0x39, %%xmm6, %%xmm6 \n"
+
+ "incl %%ecx \n" /* ++i */
+ "decl %0 \n"
+ "jnz .l1 \n"
+
+ "movl %1,%%ecx \n"
+ "movups %%xmm5, 0x24(%%ecx) \n" /* {ip->bufferX, ip->bufferY} = xmm5 */
+ "emms \n"
+ ".l5: \n"
+ :
+ : "r" (samples), /* %0 */
+ "m" (ip), /* %1 */
+ "r" (inbuffer), /* %2 */
+ "r" (outbuffer) /* %3 */
+ : "ecx", "edx");
+#endif
+}
+
+void BandPass(struct BandPassInfo *ip, float *inbuffer, float *outbuffer, unsigned long samples)
+{
+ unsigned long i;
+ for (i=0; i<samples; ++i)
+ {
+ outbuffer[i] = ip->a[0] * inbuffer[i] + ip->a[1] * ip->bufferX[0] + ip->a[2]
+ * ip->bufferX[1] - ip->b[0] * ip->bufferY[0] - ip->b[1]
+ * ip->bufferY[1];
+
+ ip->bufferX[1] = ip->bufferX[0];
+ ip->bufferX[0] = inbuffer[i];
+ ip->bufferY[1] = ip->bufferY[0];
+ ip->bufferY[0] = outbuffer[i];
+ }
+}
+
+
+/*============================================================================
+
+ fftmisc.c - Don Cross <dcross@intersrv.com>
+
+ http://www.intersrv.com/~dcross/fft.html
+
+ Helper routines for Fast Fourier Transform implementation.
+ Contains common code for fft_float() and fft_double().
+
+ See also:
+ fourierf.c
+ fourierd.c
+ ..\include\fourier.h
+
+ Revision history:
+
+1998 September 19 [Don Cross]
+ Improved the efficiency of IsPowerOfTwo().
+ Updated coding standards.
+
+============================================================================*/
+
+
+#define BITS_PER_WORD (sizeof(unsigned) * 8)
+
+
+static int IsPowerOfTwo ( unsigned x )
+{
+ if ( x < 2 )
+ return FALSE;
+
+ if ( x & (x-1) ) /* Thanks to 'byang' for this cute trick! */
+ return FALSE;
+
+ return TRUE;
+}
+
+
+static unsigned NumberOfBitsNeeded ( unsigned PowerOfTwo )
+{
+ unsigned i;
+
+ if ( PowerOfTwo < 2 )
+ {
+ fprintf (
+ stderr,
+ ">>> Error in fftmisc.c: argument %d to NumberOfBitsNeeded is too small.\n",
+ PowerOfTwo );
+
+ exit(1);
+ }
+
+ for ( i=0; ; i++ )
+ {
+ if ( PowerOfTwo & (1 << i) )
+ return i;
+ }
+}
+
+
+
+static unsigned ReverseBits ( unsigned ind, unsigned NumBits )
+{
+ unsigned i, rev;
+
+ for ( i=rev=0; i < NumBits; i++ )
+ {
+ rev = (rev << 1) | (ind & 1);
+ ind >>= 1;
+ }
+
+ return rev;
+}
+
+/*
+static double Index_to_frequency ( unsigned NumSamples, unsigned Index )
+{
+ if ( Index >= NumSamples )
+ return 0.0;
+ else if ( Index <= NumSamples/2 )
+ return (double)Index / (double)NumSamples;
+
+ return -(double)(NumSamples-Index) / (double)NumSamples;
+}
+*/
+#undef TRUE
+#undef FALSE
+#undef BITS_PER_WORD
+/*============================================================================
+
+ fourierf.c - Don Cross <dcross@intersrv.com>
+
+ http://www.intersrv.com/~dcross/fft.html
+
+ Contains definitions for doing Fourier transforms
+ and inverse Fourier transforms.
+
+ This module performs operations on arrays of 'float'.
+
+ Revision history:
+
+1998 September 19 [Don Cross]
+ Updated coding standards.
+ Improved efficiency of trig calculations.
+
+============================================================================*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "fft.h"
+
+#define CHECKPOINTER(p) CheckPointer(p,#p)
+
+static void CheckPointer ( const void *p, const char *name )
+{
+ if ( p == NULL )
+ {
+ fprintf ( stderr, "Error in fft_float(): %s == NULL\n", name );
+ exit(1);
+ }
+}
+
+
+void fft_float (
+ unsigned NumSamples,
+ int InverseTransform,
+ float *RealIn,
+ float *ImagIn,
+ float *RealOut,
+ float *ImagOut )
+{
+ unsigned NumBits; /* Number of bits needed to store indices */
+ unsigned i, j, k, n;
+ unsigned BlockSize, BlockEnd;
+
+ double angle_numerator = 2.0 * DDC_PI;
+ double tr, ti; /* temp real, temp imaginary */
+
+ if ( !IsPowerOfTwo(NumSamples) )
+ {
+ fprintf (
+ stderr,
+ "Error in fft(): NumSamples=%u is not power of two\n",
+ NumSamples );
+
+ exit(1);
+ }
+
+ if ( InverseTransform )
+ angle_numerator = -angle_numerator;
+
+ CHECKPOINTER ( RealIn );
+ CHECKPOINTER ( RealOut );
+ CHECKPOINTER ( ImagOut );
+
+ NumBits = NumberOfBitsNeeded ( NumSamples );
+
+ /*
+ ** Do simultaneous data copy and bit-reversal ordering into outputs...
+ */
+
+ for ( i=0; i < NumSamples; i++ )
+ {
+ j = ReverseBits ( i, NumBits );
+ RealOut[j] = RealIn[i];
+ ImagOut[j] = (ImagIn == NULL) ? 0.0 : ImagIn[i];
+ }
+
+ /*
+ ** Do the FFT itself...
+ */
+
+ BlockEnd = 1;
+ for ( BlockSize = 2; BlockSize <= NumSamples; BlockSize <<= 1 )
+ {
+ double delta_angle = angle_numerator / (double)BlockSize;
+ double sm2 = sin ( -2 * delta_angle );
+ double sm1 = sin ( -delta_angle );
+ double cm2 = cos ( -2 * delta_angle );
+ double cm1 = cos ( -delta_angle );
+ double w = 2 * cm1;
+ double ar[3], ai[3];
+
+ for ( i=0; i < NumSamples; i += BlockSize )
+ {
+ ar[2] = cm2;
+ ar[1] = cm1;
+
+ ai[2] = sm2;
+ ai[1] = sm1;
+
+ for ( j=i, n=0; n < BlockEnd; j++, n++ )
+ {
+ ar[0] = w*ar[1] - ar[2];
+ ar[2] = ar[1];
+ ar[1] = ar[0];
+
+ ai[0] = w*ai[1] - ai[2];
+ ai[2] = ai[1];
+ ai[1] = ai[0];
+
+ k = j + BlockEnd;
+ tr = ar[0]*RealOut[k] - ai[0]*ImagOut[k];
+ ti = ar[0]*ImagOut[k] + ai[0]*RealOut[k];
+
+ RealOut[k] = RealOut[j] - tr;
+ ImagOut[k] = ImagOut[j] - ti;
+
+ RealOut[j] += tr;
+ ImagOut[j] += ti;
+ }
+ }
+
+ BlockEnd = BlockSize;
+ }
+
+ /*
+ ** Need to normalize if inverse transform...
+ */
+
+ if ( InverseTransform )
+ {
+ double denom = (double)NumSamples;
+
+ for ( i=0; i < NumSamples; i++ )
+ {
+ RealOut[i] /= denom;
+ ImagOut[i] /= denom;
+ }
+ }
+}
+
+
diff --git a/noatun/library/noatunarts/fft.h b/noatun/library/noatunarts/fft.h
new file mode 100644
index 00000000..e7f7804d
--- /dev/null
+++ b/noatun/library/noatunarts/fft.h
@@ -0,0 +1,88 @@
+#ifndef FFT_H
+#define FFT_H
+
+
+
+/* this is from ddcmath.h */
+
+#define DDC_PI (3.14159265358979323846)
+
+/*============================================================================
+
+ fourier.h - Don Cross <dcross@intersrv.com>
+
+ http://www.intersrv.com/~dcross/fft.html
+
+ Contains definitions for doing Fourier transforms
+ and inverse Fourier transforms.
+
+============================================================================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+** If you change anything here, make sure to check if
+** the offsets used in the asm version of BandPass() are affected
+*/
+struct BandPassInfo
+{
+ float center;
+ float bandwidth;
+
+ float C, D;
+ float a[3], b[2];
+
+ float bufferX[2];
+ float bufferY[2];
+
+};
+
+void BandPassInit(struct BandPassInfo *i, float center, float bw);
+void BandPassSSE(struct BandPassInfo *ip, float *inbuffer, float *outbuffer, unsigned long samples);
+void BandPass(struct BandPassInfo *ip, float *inbuffer, float *outbuffer, unsigned long samples);
+
+/*
+** fft() computes the Fourier transform or inverse transform
+** of the complex inputs to produce the complex outputs.
+** The number of samples must be a power of two to do the
+** recursive decomposition of the FFT algorithm.
+** See Chapter 12 of "Numerical Recipes in FORTRAN" by
+** Press, Teukolsky, Vetterling, and Flannery,
+** Cambridge University Press.
+**
+** Notes: If you pass ImaginaryIn = NULL, this function will "pretend"
+** that it is an array of all zeroes. This is convenient for
+** transforming digital samples of real number data without
+** wasting memory.
+*/
+
+void fft_float (
+ unsigned NumSamples, /* must be a power of 2 */
+ int InverseTransform, /* 0=forward FFT, 1=inverse FFT */
+ float *RealIn, /* array of input's real samples */
+ float *ImaginaryIn, /* array of input's imag samples */
+ float *RealOut, /* array of output's reals */
+ float *ImaginaryOut ); /* array of output's imaginaries */
+
+
+/*
+int IsPowerOfTwo ( unsigned x );
+unsigned NumberOfBitsNeeded ( unsigned PowerOfTwo );
+unsigned ReverseBits ( unsigned index, unsigned NumBits );
+*/
+
+/*
+** The following function returns an "abstract frequency" of a
+** given index into a buffer with a given number of frequency samples.
+** Multiply return value by sampling rate to get frequency expressed in Hz.
+*/
+/*
+double Index_to_frequency ( unsigned NumSamples, unsigned Index );
+*/
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* FFT_H */
diff --git a/noatun/library/noatunarts/noatunarts.idl b/noatun/library/noatunarts/noatunarts.idl
new file mode 100644
index 00000000..809e2bb6
--- /dev/null
+++ b/noatun/library/noatunarts/noatunarts.idl
@@ -0,0 +1,91 @@
+#include <artsflow.idl>
+
+module Noatun
+{
+
+interface Equalizer : Arts::StereoEffect
+{
+ attribute sequence<float> levelCenters;
+ attribute sequence<float> levelWidths;
+ attribute sequence<float> levels;
+
+ attribute long bands;
+ attribute long enabled;
+ attribute float preamp;
+ void set(sequence<float> levels, sequence<float> centers, sequence<float> widths);
+};
+
+interface EqualizerSSE : Arts::StereoEffect
+{
+ attribute sequence<float> levelCenters;
+ attribute sequence<float> levelWidths;
+ attribute sequence<float> levels;
+
+ attribute long bands;
+ attribute long enabled;
+ attribute float preamp;
+ void set(sequence<float> levels, sequence<float> centers, sequence<float> widths);
+};
+
+interface FFTScope : Arts::StereoEffect
+{
+ attribute float bandResolution;
+ sequence<float> scope();
+};
+
+interface FFTScopeStereo : Arts::StereoEffect
+{
+ attribute float bandResolution;
+ sequence<float> scopeRight();
+ sequence<float> scopeLeft();
+};
+
+interface RawScope : Arts::StereoEffect
+{
+ attribute long buffer;
+ sequence<float> scope();
+};
+
+interface RawScopeStereo : Arts::StereoEffect
+{
+ attribute long buffer;
+ sequence<float> scopeLeft();
+ sequence<float> scopeRight();
+};
+
+interface StereoEffectStack : Arts::StereoEffect
+{
+ long insertAfter(long after, Arts::StereoEffect effect, string name);
+ void move(long after, long item);
+ sequence<long> effectList();
+ long insertTop(Arts::StereoEffect effect, string name);
+ long insertBottom(Arts::StereoEffect effect, string name);
+ void remove(long ID);
+};
+
+interface StereoVolumeControl : Arts::StereoEffect
+{
+ attribute float percent;
+};
+
+interface StereoVolumeControlSSE : Arts::StereoEffect
+{
+ attribute float percent;
+};
+
+interface Listener
+{
+ void message();
+};
+
+interface Session
+{
+ attribute long pid;
+ void addListener(Noatun::Listener listener);
+ void removeListener(Noatun::Listener listener);
+};
+
+
+};
+
+
diff --git a/noatun/library/noatunlistview.h b/noatun/library/noatunlistview.h
new file mode 100644
index 00000000..1571f64e
--- /dev/null
+++ b/noatun/library/noatunlistview.h
@@ -0,0 +1,5 @@
+// yeah yeah yeah.. fix qt and this can go away
+
+#define private protected
+#include <qlistview.h>
+#undef private
diff --git a/noatun/library/noatunstdaction.cpp b/noatun/library/noatunstdaction.cpp
new file mode 100644
index 00000000..f2837623
--- /dev/null
+++ b/noatun/library/noatunstdaction.cpp
@@ -0,0 +1,362 @@
+#include "stdaction.h"
+#include "app.h"
+#include "player.h"
+#include "stereobuttonaction.h"
+#include "pluginloader.h"
+
+#include <khelpmenu.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kstdaction.h>
+#include <qcursor.h>
+#include <qmap.h>
+#include <kdebug.h>
+#include <kstdguiitem.h>
+
+/**
+ * A namespace to have all of noatun's standard actions
+ * This is treated like KStdAction
+ **/
+namespace NoatunStdAction
+{
+/////////////////////////////////////////////////////
+PlayAction::PlayAction(QObject *parent, const char *name)
+ : KAction(i18n("Play"), 0, napp->player(), SLOT(playpause()), parent, name)
+{
+ connect(napp->player(), SIGNAL(playing()), SLOT(playing()));
+ connect(napp->player(), SIGNAL(paused()), SLOT(notplaying()));
+ connect(napp->player(), SIGNAL(stopped()), SLOT(notplaying()));
+ if (napp->player()->isPlaying())
+ playing();
+ else if (napp->player()->isPaused() || napp->player()->isStopped())
+ notplaying();
+}
+
+void PlayAction::playing()
+{
+ setIconSet(QIconSet(SmallIcon("player_pause")));
+ setText(i18n("Pause"));
+}
+
+void PlayAction::notplaying()
+{
+ setIconSet(QIconSet(SmallIcon("player_play")));
+ setText(i18n("Play"));
+}
+/////////////////////////////////////////////////////
+
+PlaylistAction::PlaylistAction(QObject *parent, const char *name)
+ : KToggleAction(i18n("Show Playlist"), "playlist", 0, napp->player(), SLOT(toggleListView()), parent, name)
+{
+ setCheckedState(i18n("Hide Playlist"));
+ connect(napp->player(), SIGNAL(playlistShown()), SLOT(shown()));
+ connect(napp->player(), SIGNAL(playlistHidden()), SLOT(hidden()));
+ setChecked(napp->playlist()->listVisible());
+}
+
+void PlaylistAction::shown()
+{
+ setChecked(true);
+}
+
+void PlaylistAction::hidden()
+{
+ setChecked(false);
+}
+
+////////////////////////////////////////////////////
+
+PluginActionMenu::PluginActionMenu(QObject *parent, const char *name)
+ : KActionMenu(i18n("&Actions"), parent, name)
+{
+// kdDebug(66666) << k_funcinfo << "called" << endl;
+ setEnabled(false);
+ mCount=0;
+}
+
+void PluginActionMenu::insert (KAction *action, int index)
+{
+// kdDebug(66666) << k_funcinfo << "called" << endl;
+ KActionMenu::insert(action,index);
+ setEnabled(true);
+ mCount++;
+}
+
+void PluginActionMenu::remove(KAction *action)
+{
+// kdDebug(66666) << k_funcinfo << "called" << endl;
+ KActionMenu::remove(action);
+ mCount--;
+ if(mCount==0)
+ setEnabled(false);
+}
+
+int PluginActionMenu::menuAdd(const QString &text, const QObject *receiver, const char *member)
+{
+// kdDebug(66666) << k_funcinfo << "called, mCount is currently at " << mCount << endl;
+ setEnabled(true);
+ mCount++;
+ return popupMenu()->insertItem(text, receiver, member);
+}
+
+void PluginActionMenu::menuRemove(int id)
+{
+// kdDebug(66666) << k_funcinfo << "called, mCount is currently at " << mCount << endl;
+ popupMenu()->removeItem(id);
+ mCount--;
+ if(mCount==0)
+ setEnabled(false);
+}
+
+////////////////////////////////////////////////////
+
+VisActionMenu::VisActionMenu(QObject *parent, const char *name)
+ : KActionMenu(i18n("&Visualizations"), parent, name)
+{
+ connect(popupMenu(), SIGNAL(aboutToShow()), this, SLOT(fillPopup()));
+ connect(popupMenu(), SIGNAL(activated(int)), this, SLOT(toggleVisPlugin(int)));
+}
+
+void VisActionMenu::fillPopup()
+{
+ int id;
+ popupMenu()->clear();
+ mSpecMap.clear();
+
+ QValueList<NoatunLibraryInfo> available = napp->libraryLoader()->available();
+ QValueList<NoatunLibraryInfo> loaded = napp->libraryLoader()->loaded();
+
+ for(QValueList<NoatunLibraryInfo>::Iterator i = available.begin(); i != available.end(); ++i)
+ {
+ if ((*i).type == "visualization")
+ {
+ id = popupMenu()->insertItem((*i).name);
+ mSpecMap[id] = (*i).specfile;
+ popupMenu()->setItemChecked(id, loaded.contains(*i));
+ }
+ }
+}
+
+void VisActionMenu::toggleVisPlugin(int id)
+{
+ if(!mSpecMap.contains(id))
+ return;
+
+ QString specfile = mSpecMap[id];
+
+ if(popupMenu()->isItemChecked(id))
+ {
+ napp->libraryLoader()->remove(specfile);
+ popupMenu()->setItemChecked(id, false);
+ }
+ else
+ {
+ napp->libraryLoader()->add(specfile);
+ popupMenu()->setItemChecked(id, true);
+ }
+}
+
+////////////////////////////////////////////////////
+
+LoopActionMenu::LoopActionMenu(QObject *parent, const char *name)
+ : KActionMenu(i18n("&Loop"), parent, name)
+{
+ mLoopNone = new KRadioAction(i18n("&None"), QString::fromLocal8Bit("noatunloopnone"),
+ 0, this, SLOT(loopNoneSelected()), this, "loop_none");
+ mLoopNone->setExclusiveGroup("loopType");
+ insert(mLoopNone);
+
+ mLoopSong = new KRadioAction(i18n("&Song"), QString::fromLocal8Bit("noatunloopsong"),
+ 0, this, SLOT(loopSongSelected()), this, "loop_song");
+ mLoopSong->setExclusiveGroup("loopType");
+ insert(mLoopSong);
+
+ mLoopPlaylist = new KRadioAction(i18n("&Playlist"), QString::fromLocal8Bit("noatunloopplaylist"),
+ 0, this, SLOT(loopPlaylistSelected()), this, "loop_playlist");
+ mLoopPlaylist->setExclusiveGroup("loopType");
+ insert(mLoopPlaylist);
+
+ mLoopRandom = new KRadioAction(i18n("&Random"), QString::fromLocal8Bit("noatunlooprandom"),
+ 0, this, SLOT(loopRandomSelected()), this, "loop_random");
+ mLoopRandom->setExclusiveGroup("loopType");
+ insert(mLoopRandom);
+
+ connect(napp->player(), SIGNAL(loopTypeChange(int)), this, SLOT(updateLooping(int)));
+
+ updateLooping(static_cast<int>(napp->player()->loopStyle()));
+}
+
+void LoopActionMenu::updateLooping(int loopType)
+{
+ switch(loopType)
+ {
+ case Player::None:
+ mLoopNone->setChecked(true);
+ setIcon("noatunloopnone");
+ break;
+ case Player::Song:
+ mLoopSong->setChecked(true);
+ setIcon("noatunloopsong");
+ break;
+ case Player::Playlist:
+ mLoopPlaylist->setChecked(true);
+ setIcon("noatunloopplaylist");
+ break;
+ case Player::Random:
+ mLoopRandom->setChecked(true);
+ setIcon("noatunlooprandom");
+ break;
+ }
+}
+
+void LoopActionMenu::loopNoneSelected()
+{
+ napp->player()->loop(Player::None);
+}
+
+void LoopActionMenu::loopSongSelected()
+{
+ napp->player()->loop(Player::Song);
+}
+
+void LoopActionMenu::loopPlaylistSelected()
+{
+ napp->player()->loop(Player::Playlist);
+}
+
+void LoopActionMenu::loopRandomSelected()
+{
+ napp->player()->loop(Player::Random);
+}
+
+////////////////////////////////////////////////////
+
+KAction *playpause(QObject *parent, const char *name)
+{
+ return new PlayAction(parent, name);
+}
+
+KAction *effects(QObject *parent, const char *name)
+{
+ return new KAction(i18n("&Effects..."), "effect", 0, napp, SLOT(effectView()), parent, name);
+}
+
+KAction *equalizer(QObject *parent, const char *name)
+{
+ return new KAction(i18n("E&qualizer..."), "equalizer", 0, napp, SLOT(equalizerView()), parent, name);
+}
+
+KAction *back(QObject *parent, const char *name)
+{
+ return new KAction(i18n("&Back"), "player_start", 0, napp->player(), SLOT(back()), parent, name);
+}
+
+KAction *stop(QObject *parent, const char *name)
+{
+ StereoButtonAction *action = new StereoButtonAction(i18n("Stop"), "player_stop", 0, napp->player(), SLOT(stop()), parent, name);
+ QObject::connect(napp->player(), SIGNAL(playing()), action, SLOT(enable()));
+ QObject::connect(napp->player(), SIGNAL(paused()), action, SLOT(enable()));
+ QObject::connect(napp->player(), SIGNAL(stopped()), action, SLOT(disable()));
+ if(napp->player()->isStopped())
+ action->disable();
+ else
+ action->enable();
+ return action;
+}
+
+KAction *forward(QObject *parent, const char *name)
+{
+ return new KAction(i18n("&Forward"), "player_end", 0, napp->player(), SLOT(forward()), parent, name);
+}
+
+KAction *play(QObject *parent, const char *name)
+{
+ StereoButtonAction *action = new StereoButtonAction(i18n("&Play"), "player_play", 0, napp->player(), SLOT(playpause()), parent, name);
+ QObject::connect(napp->player(), SIGNAL(playing()), action, SLOT(disable()));
+ QObject::connect(napp->player(), SIGNAL(paused()), action, SLOT(enable()));
+ QObject::connect(napp->player(), SIGNAL(stopped()), action, SLOT(enable()));
+ if(napp->player()->isPlaying())
+ action->disable();
+ else
+ action->enable();
+ return action;
+}
+
+KAction *pause(QObject *parent, const char *name)
+{
+ StereoButtonAction *action = new StereoButtonAction(i18n("&Pause"), "player_pause", 0, napp->player(), SLOT(playpause()), parent, name);
+ QObject::connect(napp->player(), SIGNAL(playing()), action, SLOT(enable()));
+ QObject::connect(napp->player(), SIGNAL(paused()), action, SLOT(disable()));
+ QObject::connect(napp->player(), SIGNAL(stopped()), action, SLOT(disable()));
+ if(napp->player()->isPlaying())
+ action->enable();
+ else
+ action->disable();
+ return action;
+}
+
+LoopActionMenu *loop(QObject *parent, const char *name)
+{
+ return new LoopActionMenu(parent, name);
+}
+
+PluginActionMenu *actions()
+{
+ // NoatunApp makes sure that we only have one ActionMenu around
+ return napp->pluginActionMenu();
+}
+
+VisActionMenu *visualizations(QObject *parent, const char *name)
+{
+ return new VisActionMenu(parent, name);
+}
+
+KToggleAction *playlist(QObject *parent, const char *name)
+{
+ return new PlaylistAction(parent, name);
+}
+
+KPopupMenu *ContextMenu::mContextMenu = 0;
+
+KPopupMenu *ContextMenu::contextMenu()
+{
+ if(!mContextMenu) mContextMenu = createContextMenu(0);
+
+ return mContextMenu;
+}
+
+KPopupMenu *ContextMenu::createContextMenu(QWidget *p)
+{
+ KPopupMenu *contextMenu = new KPopupMenu(p, "NoatunContextMenu");
+
+ KHelpMenu *helpmenu = new KHelpMenu(contextMenu, kapp->aboutData(), false);
+ KActionCollection* actions = new KActionCollection(helpmenu);
+
+ KStdAction::open(napp, SLOT(fileOpen()), actions)->plug(contextMenu);
+ KStdAction::quit(napp, SLOT(quit()), actions)->plug(contextMenu);
+ contextMenu->insertItem(SmallIcon("help"), KStdGuiItem::help().text(), helpmenu->menu());
+ contextMenu->insertSeparator();
+ KStdAction::preferences(napp, SLOT(preferences()), actions)->plug(contextMenu);
+ NoatunStdAction::playlist(contextMenu)->plug(contextMenu);
+ NoatunStdAction::effects(contextMenu)->plug(contextMenu);
+ NoatunStdAction::equalizer(napp)->plug(contextMenu);
+ NoatunStdAction::visualizations(napp)->plug(contextMenu);
+ napp->pluginActionMenu()->plug(contextMenu);
+
+ return contextMenu;
+}
+
+void ContextMenu::showContextMenu(const QPoint &p)
+{
+ contextMenu()->exec(p);
+}
+
+void ContextMenu::showContextMenu()
+{
+ showContextMenu(QCursor::pos());
+}
+
+} // END namespace NoatunStdAction
+
+#include "stdaction.moc"
diff --git a/noatun/library/noatuntags/Makefile.am b/noatun/library/noatuntags/Makefile.am
new file mode 100644
index 00000000..ea90faa1
--- /dev/null
+++ b/noatun/library/noatuntags/Makefile.am
@@ -0,0 +1,14 @@
+INCLUDES = -I$(top_srcdir)/noatun/library $(all_includes)
+
+lib_LTLIBRARIES = libnoatuntags.la
+
+libnoatuntags_la_SOURCES = tags.cpp
+libnoatuntags_la_LDFLAGS = -version-info 3:0:2 $(all_libraries)
+libnoatuntags_la_LIBADD = ../libnoatun.la
+
+libnoatuntags_la_METASOURCES = AUTO
+
+noatuntagsincludedir = $(includedir)/noatun
+noatuntagsinclude_HEADERS = tags.h
+
+
diff --git a/noatun/library/noatuntags/tags.cpp b/noatun/library/noatuntags/tags.cpp
new file mode 100644
index 00000000..38fd4abf
--- /dev/null
+++ b/noatun/library/noatuntags/tags.cpp
@@ -0,0 +1,229 @@
+#include "tags.h"
+#include "tagsgetter.h"
+#include <klocale.h>
+#include <qslider.h>
+#include <qspinbox.h>
+#include <kconfig.h>
+#include <qlayout.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qtimer.h>
+#include <noatun/player.h>
+#include <qcheckbox.h>
+
+TagsGetter *Tags::getter=0;
+
+TagsGetter::TagsGetter()
+{
+ new Control(this);
+ connect(napp->player(), SIGNAL(newSong()), SLOT(newSong()));
+}
+
+int TagsGetter::interval() const
+{
+ KGlobal::config()->setGroup("Tags");
+ return KGlobal::config()->readNumEntry("interval", 250);
+}
+
+bool TagsGetter::loadAuto() const
+{
+ KGlobal::config()->setGroup("Tags");
+ return KGlobal::config()->readBoolEntry("LoadAuto", true);
+}
+
+void TagsGetter::added(PlaylistItem &i)
+{
+ items += i;
+ killTimers();
+ startTimer(interval());
+}
+
+void TagsGetter::removed(PlaylistItem &i)
+{
+ items.remove(i);
+}
+
+void TagsGetter::getSongs()
+{
+ items = napp->playlist()->select("Tags::tagged_", "", -1, true, true);
+ killTimers();
+ startTimer(interval());
+}
+
+void TagsGetter::timerEvent(QTimerEvent *)
+{
+ if (!items.size())
+ {
+ killTimers();
+ return;
+ }
+
+ PlaylistItem item=items.first();
+ for (Tags *i=tags.first(); i; i=tags.next())
+ {
+ if (i->update(item))
+ {
+ item.setProperty("Tags::tagged_", "1");
+ if (item==napp->player()->current())
+ napp->player()->handleButtons();
+ }
+ }
+
+ items.remove(items.begin());
+}
+
+void TagsGetter::newSong()
+{
+ PlaylistItem item=napp->player()->current();
+ if (!item) return;
+
+ for (Tags *i=tags.first(); i; i=tags.next())
+ {
+ if (i->update(item))
+ {
+ item.setProperty("Tags::tagged_", "1");
+ napp->player()->handleButtons();
+ }
+ }
+ items.remove(item);
+}
+
+void TagsGetter::setInterval(int ms)
+{
+ killTimers();
+ startTimer(ms);
+
+ KGlobal::config()->setGroup("Tags");
+ KGlobal::config()->writeEntry("interval", ms);
+ KGlobal::config()->sync();
+}
+
+void TagsGetter::setLoadAuto(bool eh)
+{
+
+ KGlobal::config()->setGroup("Tags");
+ KGlobal::config()->writeEntry("LoadAuto", eh);
+ KGlobal::config()->sync();
+
+ killTimers();
+
+ if (eh) startTimer(interval());
+}
+
+void TagsGetter::associate(Tags *t)
+{
+ tags.append(t);
+ sortPriority();
+// getSongs();
+ QTimer::singleShot(interval(), this, SLOT(getSongs()));
+}
+
+void TagsGetter::sortPriority()
+{
+ // find the lowest one, since it comes first
+
+ int lowest=0;
+ for (Tags *i=tags.first(); i; i=tags.next())
+ {
+ if (lowest>i->mPriority)
+ lowest=i->mPriority;
+ }
+
+ QPtrList<Tags> sorted;
+ while (tags.count())
+ {
+ // find the one equal to myself
+ for (Tags *i=tags.first(); i;)
+ {
+ if (lowest==i->mPriority)
+ {
+ sorted.append(i);
+ tags.removeRef(i);
+ i=tags.first();
+ }
+ else
+ {
+ i=tags.next();
+ }
+ }
+ lowest++;
+ }
+
+ tags=sorted;
+}
+
+bool TagsGetter::unassociate(Tags *t)
+{
+ tags.removeRef(t);
+ if (tags.count()==0)
+ {
+ delete this;
+ return true;
+ }
+ return false;
+}
+
+Tags::Tags(int priority) : mPriority(priority)
+{
+ if (!getter)
+ getter=new TagsGetter;
+ getter->associate(this);
+}
+
+Tags::~Tags()
+{
+ if (getter->unassociate(this))
+ getter=0;
+}
+
+
+Control::Control(TagsGetter *parent)
+ : CModule(i18n("Tagging"), i18n("Settings for Tag Loaders"), "edit", parent)
+{
+ // todo
+ (void)I18N_NOOP("Rescan All Tags");
+
+ QVBoxLayout *l=new QVBoxLayout(this);
+ QCheckBox *onPlay;
+ {
+ onPlay=new QCheckBox(i18n("Load tags &automatically"), this);
+ l->addWidget(onPlay);
+ onPlay->show();
+ }
+
+ {
+ QHBox *intervalLine=new QHBox(this);
+ l->addWidget(intervalLine);
+ l->addStretch();
+
+ new QLabel(i18n(
+ "The time between each time noatun scans for a new file"
+ ", and updates tags (e.g., ID3)",
+ "Interval:"), intervalLine);
+ QSlider *slider=new QSlider(
+ 0, 2000, 100, 0, Horizontal, intervalLine
+ );
+ QSpinBox *spin=new QSpinBox(
+ 0, 2000, 10, intervalLine
+ );
+
+ spin->setSuffix(i18n("Milliseconds", " ms"));
+
+
+ connect(slider, SIGNAL(valueChanged(int)), spin, SLOT(setValue(int)));
+ connect(spin, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)));
+ slider->setValue(parent->interval());
+ connect(slider, SIGNAL(valueChanged(int)), parent, SLOT(setInterval(int)));
+
+ connect(onPlay, SIGNAL(toggled(bool)), intervalLine, SLOT(setEnabled(bool)));
+ }
+ connect(onPlay, SIGNAL(toggled(bool)), parent, SLOT(setLoadAuto(bool)));
+
+ onPlay->setChecked(parent->loadAuto());
+}
+
+
+
+
+#include "tagsgetter.moc"
+
diff --git a/noatun/library/noatuntags/tags.h b/noatun/library/noatuntags/tags.h
new file mode 100644
index 00000000..ec98aef2
--- /dev/null
+++ b/noatun/library/noatuntags/tags.h
@@ -0,0 +1,35 @@
+#ifndef __NOATUN_TAGS___TACOS_ARE_YUMMY
+#define __NOATUN_TAGS___TACOS_ARE_YUMMY
+
+#include <noatun/playlist.h>
+
+class TagsGetter;
+
+class Tags
+{
+ friend class TagsGetter;
+public:
+ /**
+ * priority is how early this comes
+ * 0 means "normal"
+ * anything larger than zero means that this should come later,
+ * negative numbers mean it should come first
+ *
+ * I'm talking about the order which you're being processed
+ **/
+ Tags(int priority=0);
+ virtual ~Tags();
+
+ /**
+ * this will be called occasionally
+ * with an item you should fill up
+ **/
+ virtual bool update(PlaylistItem &item)=0;
+
+private:
+ static TagsGetter *getter;
+ int mPriority;
+};
+
+#endif
+
diff --git a/noatun/library/noatuntags/tagsgetter.h b/noatun/library/noatuntags/tagsgetter.h
new file mode 100644
index 00000000..269b7b90
--- /dev/null
+++ b/noatun/library/noatuntags/tagsgetter.h
@@ -0,0 +1,56 @@
+#ifndef TAGSGET_H
+#define TAGSGET_H
+
+#include <qobject.h>
+#include <cmodule.h>
+#include <qvaluelist.h>
+#include <qptrlist.h>
+#include "tags.h"
+
+class TagsGetter : public QObject, public PlaylistNotifier
+{
+Q_OBJECT
+public:
+ TagsGetter();
+ void associate(Tags *t);
+ // if returns true, I'm deleted
+ bool unassociate(Tags *t);
+
+ int interval() const;
+ bool loadAuto() const;
+
+public: //playlistnotifier
+ virtual void added(PlaylistItem &);
+ virtual void removed(PlaylistItem &);
+
+protected:
+ void timerEvent(QTimerEvent *);
+
+private slots:
+ // select the songs that need updating
+ void getSongs();
+
+ void newSong();
+
+public slots:
+ void setInterval(int ms);
+ void setLoadAuto(bool);
+
+private:
+ void sortPriority();
+
+private:
+ QPtrList<Tags> tags;
+ QValueList<PlaylistItem> items;
+};
+
+class Control : public CModule
+{
+Q_OBJECT
+public:
+ Control(TagsGetter* parent);
+};
+
+
+#endif
+
diff --git a/noatun/library/noatunui.cpp b/noatun/library/noatunui.cpp
new file mode 100644
index 00000000..9dbb1ff2
--- /dev/null
+++ b/noatun/library/noatunui.cpp
@@ -0,0 +1,12 @@
+#include "plugin.h"
+
+UserInterface::UserInterface()
+ : Plugin()
+{
+}
+
+UserInterface::~UserInterface()
+{
+
+}
+
diff --git a/noatun/library/player.cpp b/noatun/library/player.cpp
new file mode 100644
index 00000000..7760d9a8
--- /dev/null
+++ b/noatun/library/player.cpp
@@ -0,0 +1,379 @@
+#include "player.h"
+
+#include <noatun/playlist.h>
+#include "engine.h"
+#include "app.h"
+#include "titleproxy.h"
+
+#include <klibloader.h>
+#include <knotifyclient.h>
+#include <klocale.h>
+#include <qfile.h>
+
+enum ArtsPOS { posIdle=0, posPlaying, posPaused };
+
+
+
+Player::Player(QObject *parent) : QObject(parent, "Player"),
+ position(-1), mLoopStyle(None), firstTimeout(true)
+{
+ mEngine=new Engine;
+ connect(&filePos, SIGNAL(timeout()), SLOT(posTimeout()));
+ connect(mEngine, SIGNAL(aboutToPlay()), this, SLOT(aboutToPlay()));
+ connect(mEngine,
+ SIGNAL(receivedStreamMeta(const QString &, const QString &,
+ const QString &, const QString &,
+ const QString &, const QString &)),
+ this, SLOT(
+ slotUpdateStreamMeta(const QString &, const QString &,
+ const QString &, const QString &,
+ const QString &, const QString &))
+ );
+ connect(mEngine, SIGNAL(playingFailed()), this, SLOT(forward()));
+
+ handleButtons();
+}
+
+Player::~Player()
+{
+ delete mEngine;
+}
+
+bool Player::isPlaying()
+{
+ return mEngine->state()==posPlaying;
+}
+
+bool Player::isPaused()
+{
+ return mEngine->state()==posPaused;
+}
+
+bool Player::isStopped()
+{
+ return mEngine->state()==posIdle;
+}
+
+void Player::toggleListView()
+{
+ napp->playlist()->toggleList();
+}
+
+void Player::handleButtons()
+{
+ switch (mEngine->state())
+ {
+ case (posPlaying):
+ emit playing();
+ break;
+ case (posPaused):
+ emit paused();
+ break;
+ case (posIdle):
+ emit stopped();
+ }
+}
+
+void Player::back()
+{
+ if (napp->playlist()->previous())
+ {
+ stop();
+ play();
+ }
+}
+
+void Player::stop()
+{
+ filePos.stop();
+ position=0;
+ mEngine->stop();
+ emit stopped();
+ mCurrent=0;
+}
+
+void Player::play()
+{
+ napp->processEvents();
+ bool work=false;
+ firstTimeout=true;
+
+ if (mEngine->state()==posPlaying) // do nothing if already playing
+ return;
+
+ if (mEngine->state()==posPaused)
+ {
+ work=mEngine->play();
+ }
+ else
+ {
+ stop();
+ mCurrent = napp->playlist()->current();
+ if (!mCurrent)
+ {
+ work=false;
+ }
+ else
+ {
+ mEngine->blockSignals(true);
+ work=mEngine->open(mCurrent);
+ mEngine->blockSignals(false);
+ }
+ }
+
+ if (!work)
+ {
+ forward(false);
+ }
+ else
+ {
+ filePos.start(500);
+ emit changed();
+ mEngine->play();
+ }
+
+ handleButtons();
+}
+
+void Player::play(const PlaylistItem &item)
+{
+ napp->playlist()->setCurrent(item);
+}
+
+void Player::playpause()
+{
+ if (mEngine->state()==posPlaying)
+ {
+ filePos.stop();
+ mEngine->pause();
+// emit paused(); NOT necessary because emitted in handleButtons() (mETz)
+ handleButtons();
+ }
+ else
+ play();
+}
+
+void Player::forward(bool allowLoop)
+{
+ stop();
+ if (napp->playlist()->next())
+ play();
+ else if (allowLoop && napp->loopList())
+ if (napp->playlist()->reset(), napp->playlist()->current())
+ play();
+}
+
+void Player::skipTo(int msec) // skip to a certain time in the track
+{
+ if( (current()) && (msec>=0) )
+ {
+ mEngine->seek(msec);
+ position = mEngine->position(); // make sure position is recent
+ emit timeout(); // update the UI
+ emit skipped(msec);
+ emit skipped();
+ }
+}
+
+void Player::playCurrent()
+{
+ if (!mEngine->initialized()) return;
+ stop();
+ mCurrent=0;
+ if (napp->playlist()->current())
+ play();
+}
+
+void Player::newCurrent()
+{
+ // the second half of the following
+ if (!napp->playlist() || !mEngine->initialized())
+ return; // no playlist, or squelch playing as an optimization
+ if ((mEngine->state()!=posPlaying) && napp->autoPlay())
+ playCurrent();
+}
+
+void Player::posTimeout()
+{
+ if (mEngine->state()==posIdle)
+ {
+ stop();
+ handleButtons();
+ // If you're supposed to loop the song, don't go next
+ // otherwise, do go next
+ if (loopStyle()==Song || napp->playlist()->next())
+ play();
+ else if (loopStyle()==Playlist)
+ {
+ napp->playlist()->reset();
+ play();
+ }
+ else if (napp->loopList())
+ napp->playlist()->reset();
+
+ return;
+ }
+ position = mEngine->position();
+
+ if (current())
+ {
+ current().setLength(mEngine->length());
+ if (current().length() && firstTimeout)
+ {
+ int minutes = (int) ( current().length() / 60 );
+ int seconds = current().length() - minutes * 60;
+ emit newSongLen ( minutes, seconds );
+ firstTimeout = false;
+ emit newSong();
+ }
+ }
+
+ emit timeout();
+}
+
+QString Player::lengthString(int _position)
+{
+ if (!current())
+ return QString("--:--/--:--");
+
+ QString posString;
+ QString lenString;
+ int secs, seconds, minutes;
+
+ if (_position < 0)
+ _position = position;
+
+ if (_position<0)
+ {
+ posString="--:--/";
+ }
+ else
+ { // get the position
+ bool remain = napp->displayRemaining() && current();
+ if (remain && current().length()<0)
+ {
+ remain = false;
+ }
+
+ if (remain)
+ {
+ _position = current().length() - _position;
+ }
+
+ secs = _position / 1000; // convert milliseconds -> seconds
+ seconds = secs % 60;
+ minutes = (secs - seconds) / 60;
+
+ // the string has to look like '00:00/00:00'
+ posString.sprintf("%.2d:%.2d/", minutes, seconds);
+ if (remain) posString.prepend('-');
+ }
+
+ if (!current() || current().length()<0)
+ {
+ posString+="--:--";
+ }
+ else
+ { // get the length
+ secs = current().length() / 1000; // convert milliseconds -> seconds
+ seconds = secs % 60;
+ minutes = (secs - seconds) / 60;
+
+ // the string has to look like '00:00/00:00'
+ lenString.sprintf("%.2d:%.2d", minutes, seconds);
+ posString += lenString;
+ }
+
+ return posString;
+}
+
+int Player::getLength()
+{
+ if (!current())
+ return -1;
+ return current().length(); // return track-length in milliseconds
+}
+
+void Player::openFile(const KURL &f, bool purge, bool autoplay)
+{
+ if (purge)
+ napp->playlist()->clear();
+ napp->playlist()->addFile(f, autoplay);
+}
+
+void Player::openFile(const KURL::List &f, bool purge, bool autoplay)
+{
+ if (purge)
+ napp->playlist()->clear();
+ for (KURL::List::ConstIterator i(f.begin()); i != f.end(); ++i)
+ {
+ napp->playlist()->addFile(*i, autoplay);
+ autoplay=false;
+ }
+}
+
+
+void Player::loop()
+{
+ mLoopStyle++;
+ if (mLoopStyle>Random)
+ mLoopStyle=None;
+ emit loopTypeChange(mLoopStyle);
+}
+void Player::loop(int i)
+{
+ mLoopStyle=i;
+ emit loopTypeChange(mLoopStyle);
+}
+
+void Player::removeCurrent()
+{
+ if (napp->playlist()->current())
+ napp->playlist()->current().remove();
+}
+
+void Player::setVolume(int v)
+{
+ if (v<0) v=0;
+ if (v>100) v=100;
+ mEngine->setVolume(v);
+ emit timeout();
+ emit volumeChanged(v);
+}
+
+int Player::volume() const
+{
+ return mEngine->volume();
+}
+
+void Player::aboutToPlay()
+{
+ emit aboutToOpen( mCurrent );
+}
+
+void Player::slotUpdateStreamMeta(
+ const QString &streamName, const QString &streamGenre,
+ const QString &streamUrl, const QString &streamBitrate,
+ const QString &trackTitle, const QString &trackUrl)
+{
+ PlaylistItem currentItem = napp->playlist()->current();
+ if(currentItem)
+ {
+ currentItem.setProperty("title", trackTitle);
+ currentItem.setProperty("bitrate", streamBitrate);
+
+ if(!streamName.isEmpty())
+ currentItem.setProperty("author", streamName);
+ if(!streamGenre.isEmpty())
+ currentItem.setProperty("genre", streamGenre);
+ if(!trackUrl.isEmpty())
+ currentItem.setProperty("comment", trackUrl);
+ else if(!streamUrl.isEmpty())
+ currentItem.setProperty("comment", streamUrl);
+ else
+ currentItem.clearProperty("comment");
+ emit changed();
+ }
+}
+
+#include "player.moc"
diff --git a/noatun/library/playlist.cpp b/noatun/library/playlist.cpp
new file mode 100644
index 00000000..4e729924
--- /dev/null
+++ b/noatun/library/playlist.cpp
@@ -0,0 +1,442 @@
+#include <noatun/playlist.h>
+#include <noatun/app.h>
+#include <noatun/player.h>
+#include <noatun/downloader.h>
+#include <noatun/pluginloader.h>
+
+#include <kcmdlineargs.h>
+#include <kfile.h>
+#include <kmimetype.h>
+#include <qregexp.h>
+#include <assert.h>
+#include <qmetaobject.h>
+#include <kmedia2.h>
+#include <vector>
+
+PlaylistItemData::PlaylistItemData()
+{
+ mRefs=0;
+}
+
+PlaylistItemData::~PlaylistItemData()
+{
+}
+
+QCString PlaylistItemData::mimetype() const
+{
+ if (isProperty("mimetype"))
+ return property("mimetype").latin1();
+ KMimeType::Ptr mimetype = KMimeType::findByURL(url());
+
+ return mimetype->name().latin1();
+}
+
+QCString PlaylistItemData::playObject() const
+{
+ if (isProperty("playObject"))
+ return property("playObject").latin1();
+
+ std::string objectType;
+
+ Arts::TraderQuery query;
+ query.supports("Interface","Arts::PlayObject");
+ query.supports("MimeType", std::string(mimetype()));
+
+ std::vector<Arts::TraderOffer> *offers = query.query();
+ if (!offers) return "";
+
+ if(!offers->empty())
+ objectType = offers->front().interfaceName();
+
+ delete offers;
+
+ return QCString(objectType.c_str());
+}
+
+QString PlaylistItemData::title() const
+{
+ if (isProperty("realtitle"))
+ return property("realtitle");
+
+
+ // "$(property)"
+ QString format=napp->titleFormat();
+
+ QRegExp find("(?:(?:\\\\\\\\))*\\$\\((.*)");
+
+ int start=0;
+ while (start != -1)
+ {
+ start = find.search(format, start);
+ if (start == -1) break;
+
+ // test if there's an odd amount of backslashes
+ if (start>0 && format[start-1]=='\\')
+ {
+ // yes, so half the amount of backslashes
+
+ // count how many there are first
+ QRegExp counter("([\\\\]+)");
+ counter.search(format, start-1);
+ uint len=counter.cap(1).length()-1;
+
+ // and half them, and remove one more
+ format.replace(start-1, len/2+1, "");
+ start=start-1+len/2+find.cap(1).length()+3;
+ continue;
+ }
+
+ // now replace the backslashes with half as many
+
+ if (format[start]=='\\')
+ {
+ // count how many there are first
+ QRegExp counter("([\\\\]+)");
+ counter.search(format, start);
+ uint len=counter.cap(1).length();
+
+ // and half them
+ format.replace(start, len/2, "");
+ start=start+len/2;
+ }
+
+ // "sth"foo"sth"
+ QString cont(find.cap(1));
+ QString prefix,suffix,propname;
+ unsigned int i=0;
+ if (cont[i] == '"')
+ {
+ i++;
+ for (; i < cont.length(); i++)
+ {
+ if (cont[i] != '"')
+ prefix += cont[i];
+ else
+ break;
+ }
+ i++;
+ }
+
+
+ for (; i < cont.length(); ++i)
+ {
+ if (cont[i]!='"' && cont[i]!=')')
+ propname += cont[i];
+ else
+ break;
+ }
+
+ if (cont[i] == '"')
+ {
+ i++;
+ for (; i < cont.length(); i++)
+ {
+ if (cont[i] != '"')
+ suffix += cont[i];
+ else
+ break;
+ }
+ i++;
+ }
+ i++;
+
+
+ QString propval = property(propname);
+ if (propname == "title" && !propval.length())
+ {
+ propval = url().filename();
+ }
+ else if (propname == "comment")
+ {
+ // comments can contain newlines
+ // these are not wanted in a formatted title
+ propval.replace('\n', ' ');
+ }
+
+ if (propval.length())
+ {
+ propval = prefix+propval+suffix;
+ format.replace(start, i+2, propval);
+ start += propval.length();
+ }
+ else
+ {
+ format.replace(start, i+2, "");
+ }
+ }
+ return format;
+
+}
+
+int PlaylistItemData::length() const
+{
+ return property("length", "-1").toInt();
+}
+
+void PlaylistItemData::setLength(int ms)
+{
+ setProperty("length", QString::number(ms));
+}
+
+PlaylistItem::PlaylistItem(const PlaylistItem &source)
+{
+ mData=source.mData;
+ addRef();
+}
+
+PlaylistItem::PlaylistItem(PlaylistItemData *source)
+{
+ mData=source;
+ addRef();
+}
+
+PlaylistItem::~PlaylistItem()
+{
+ removeRef();
+}
+
+PlaylistItem &PlaylistItem::operator =(const PlaylistItem &source)
+{
+ if (source)
+ source.addRef();
+ removeRef();
+ mData=source.mData;
+ return *this;
+}
+
+PlaylistItem &PlaylistItem::operator =(PlaylistItemData *source)
+{
+ if (source)
+ source->addRef();
+ removeRef();
+ mData=source;
+ return *this;
+}
+
+const PlaylistItem &PlaylistItem::operator =(const PlaylistItem &source) const
+{
+ if (source)
+ source.addRef();
+ removeRef();
+ mData=source.mData;
+ return *this;
+}
+
+const PlaylistItem &PlaylistItem::operator =(const PlaylistItemData *source) const
+{
+ if (source)
+ const_cast<PlaylistItemData*>(source)->addRef();
+ removeRef();
+ mData=const_cast<PlaylistItemData*>(source);
+ return *this;
+}
+
+// reference counting
+void PlaylistItem::removeRef() const
+{
+ if (mData)
+ {
+ mData->removeRef();
+ }
+}
+
+void PlaylistItem::addRef() const
+{
+ if (mData)
+ mData->addRef();
+}
+
+QString PlaylistItemData::lengthString() const
+{
+ if ( length() == -1 ) // no file loaded
+ return QString("--:--");
+
+ int secs = length()/1000; // convert milliseconds -> seconds
+ int seconds = secs % 60;
+ return QString().sprintf("%.2d:%.2d", ((secs-seconds)/60), seconds);
+}
+
+
+bool PlaylistItemData::operator == (const PlaylistItemData &d) const
+{
+ return &d==this;
+}
+
+bool PlaylistItemData::operator != (const PlaylistItemData &d) const
+{
+ return ! (*this==d);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+Playlist::Playlist(QObject *parent, const char *name) : QObject(parent, name)
+{
+ napp->player()->connect(this, SIGNAL(playCurrent()), SLOT(playCurrent()));
+ napp->player()->connect(this, SIGNAL(listHidden()), SIGNAL(playlistHidden()));
+ napp->player()->connect(this, SIGNAL(listShown()), SIGNAL(playlistShown()));
+
+}
+
+Playlist::~Playlist()
+{
+
+}
+
+void Playlist::toggleList()
+{
+ if (listVisible())
+ hideList();
+ else
+ showList();
+}
+
+int Playlist::handleArguments()
+{
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ int i;
+ bool play= napp->autoPlay();
+
+ for (i=0; i < args->count(); i++)
+ {
+ KURL u(args->url(i));
+ if (u.isValid())
+ addFile(u, play);
+ play=false;
+ }
+ args->clear();
+ return i;
+}
+
+QValueList<PlaylistItem> Playlist::select(
+ const QStringList &keys, const QStringList &values,
+ int limit, bool exact, bool caseSensitive
+ )
+{
+ QValueList<PlaylistItem> list;
+ QString k;
+ QString v;
+ QStringList::ConstIterator key, val;
+ for (PlaylistItem i(getFirst()); i && limit; i=getAfter(i))
+ {
+ for (key = keys.begin(); key != keys.end() && limit ; ++key)
+ {
+ k=*key;
+ v=i.property(k);
+
+ for (val = values.begin(); val != values.end() && limit; ++val)
+ {
+ if ((*val).length()==0 && v.length()==0)
+ {
+ list.append(i);
+ limit--;
+ goto nextSong;
+ }
+ else if (exact)
+ {
+ if (
+ (!caseSensitive && (*val).lower()==v.lower())
+ ||
+ (caseSensitive && (*val)==v)
+ )
+
+ {
+ list.append(i);
+ limit--;
+ goto nextSong;
+ }
+ }
+ else
+ {
+ if ((*val).find(v, 0, caseSensitive)!=-1)
+ {
+ list.append(i);
+ limit--;
+ goto nextSong;
+ }
+ }
+ }
+ }
+ nextSong:
+ ;
+ }
+ return list;
+}
+
+QValueList<PlaylistItem> Playlist::select(
+ const QString &key, const QString &value,
+ int limit, bool exact, bool caseSensitive
+ )
+{
+ QStringList keys(key);
+ QStringList values(value);
+ return select(keys, values, limit, exact, caseSensitive);
+}
+
+void PlaylistItemData::added()
+{
+ PlaylistItem item(this);
+ for (
+ PlaylistNotifier *i=napp->player()->mNotifiers.first();
+ i; i=napp->player()->mNotifiers.next())
+ {
+ i->added(item);
+ }
+}
+
+void PlaylistItemData::removed()
+{
+ PlaylistItem item(this);
+ for (
+ PlaylistNotifier *i=napp->player()->mNotifiers.first();
+ i; i=napp->player()->mNotifiers.next())
+ {
+ i->removed(item);
+ }
+}
+
+void PlaylistItemData::modified()
+{
+ PlaylistItem item(this);
+ for (
+ PlaylistNotifier *i=napp->player()->mNotifiers.first();
+ i; i=napp->player()->mNotifiers.next())
+ {
+ i->modified(item);
+ }
+}
+
+
+PlaylistItem Playlist::nextSection()
+{
+ return next();
+}
+
+PlaylistItem Playlist::previousSection()
+{
+ return previous();
+}
+
+
+PlaylistNotifier::PlaylistNotifier()
+{
+ napp->player()->mNotifiers.append(this);
+}
+
+PlaylistNotifier::~PlaylistNotifier()
+{
+ napp->player()->mNotifiers.removeRef(this);
+}
+
+
+
+#include "playlist.moc"
+
diff --git a/noatun/library/playlistsaver.cpp b/noatun/library/playlistsaver.cpp
new file mode 100644
index 00000000..58f5d5d2
--- /dev/null
+++ b/noatun/library/playlistsaver.cpp
@@ -0,0 +1,771 @@
+#include <noatun/playlistsaver.h>
+#include <qdom.h>
+#include <kio/netaccess.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <noatun/app.h>
+#include "ksaver.h"
+#include <ksimpleconfig.h>
+#include <kmimetype.h>
+#include <klocale.h>
+#include <qregexp.h>
+#include <qxml.h>
+#include <kdebug.h>
+
+PlaylistSaver::PlaylistSaver()
+{
+}
+
+PlaylistSaver::~PlaylistSaver()
+{
+}
+
+bool PlaylistSaver::save(const KURL &file, int opt)
+{
+// kdDebug(66666) << k_funcinfo << "opt=" << opt << endl;
+
+ if(file.isEmpty() || !file.isValid())
+ return false;
+
+ switch (opt)
+ {
+ default:
+ case 0:
+ case XMLPlaylist:
+ return saveXML(file, opt);
+
+ case M3U:
+ case EXTM3U:
+ return saveM3U(file, opt);
+
+ case PLS:
+ return savePLS(file, opt);
+
+ case ASX:
+ return false; // No, I won't code that! [mETz]
+ }
+}
+
+bool PlaylistSaver::load(const KURL &file, int opt)
+{
+// kdDebug(66666) << k_funcinfo << "opt=" << opt << endl;
+
+ switch (opt)
+ {
+ default:
+ case 0:
+ case XMLPlaylist:
+ case ASX:
+ return loadXML(file, opt);
+
+ case M3U:
+ case EXTM3U:
+ return loadM3U(file, opt);
+
+ case PLS:
+ return loadPLS(file, opt);
+ }
+}
+
+bool PlaylistSaver::metalist(const KURL &url)
+{
+ kdDebug(66666) << k_funcinfo << "url=" << url.url() << endl;
+
+ QString end=url.filename().right(3).lower();
+ /*
+ if (end=="mp3" || end=="ogg") // we want to download streams only
+ {
+ kdDebug(66666) << k_funcinfo << "I can only load playlists" << endl;
+ return false;
+ }
+ */
+
+/*
+.wax audio/x-ms-wax Metafiles that reference Windows Media files with the
+ .asf, .wma or .wax file extensions.
+
+.wvx video/x-ms-wvx Metafiles that reference Windows Media files with the
+ .wma, .wmv, .wvx or .wax file extensions.
+
+.asx video/x-ms-asf Metafiles that reference Windows Media files with the
+ .wma, .wax, .wmv, .wvx, .asf, or .asx file extensions.
+*/
+
+ // it's actually a stream!
+ if (end!="pls" &&
+ end!="m3u" &&
+ end!="wax" && // windows mediaplayer metafile
+ end!="wvx" && // windows mediaplayer metafile
+ end!="asx" && // windows mediaplayer metafile
+ url.protocol().lower()=="http")
+ {
+ KMimeType::Ptr mimetype = KMimeType::findByURL(url);
+ QString type=mimetype->name();
+
+ if (type!="application/octet-stream")
+ return false;
+
+ QMap<QString,QString> map;
+ map["playObject"]="Arts::StreamPlayObject";
+ map["title"] = i18n("Stream from %1").arg(url.host());
+
+ KURL u(url);
+ if (!u.hasPath())
+ u.setPath("/");
+
+ map["stream_"] = map["url"] = u.url();
+
+ reset();
+ readItem(map);
+ return true;
+ }
+
+ // it is a pls, m3u or ms-media-player file by now
+ if(loadXML(url, XMLPlaylist))
+ return true;
+
+ if(loadXML(url,ASX))
+ return true;
+
+ if(loadPLS(url))
+ return true;
+
+ if(loadM3U(url))
+ return true;
+
+ return false;
+}
+
+bool PlaylistSaver::saveXML(const KURL &file, int )
+{
+ QString localFile;
+ if (file.isLocalFile())
+ localFile = QFile::encodeName(file.path());
+ else
+ localFile = napp->tempSaveName(file.path());
+
+ // QDom is a pain :)
+ QDomDocument doc("playlist");
+ doc.setContent(QString("<!DOCTYPE XMLPlaylist><playlist version=\"1.0\" client=\"noatun\"/>"));
+
+ QDomElement docElem=doc.documentElement();
+
+ reset();
+ PlaylistItem i;
+ QStringList props;
+ while ((i=writeItem()))
+ {
+ // write all properties
+ props=i.properties();
+ QDomElement elem=doc.createElement("item");
+ for (QStringList::Iterator pi(props.begin()); pi!=props.end(); ++pi)
+ {
+ QString val=i.property(*pi);
+ elem.setAttribute(*pi, val);
+
+ if ((*pi)=="url")
+ {
+ KURL u(val);
+ if (u.isLocalFile())
+ {
+ elem.setAttribute("local", u.path());
+ }
+ }
+ }
+
+ docElem.appendChild(elem);
+ props.clear();
+ }
+ Noatun::KSaver saver(localFile);
+ if (!saver.open())
+ return false;
+ saver.textStream().setEncoding(QTextStream::UnicodeUTF8);
+ saver.textStream() << doc.toString();
+ saver.close();
+
+ return true;
+}
+
+class NoatunXMLStructure : public QXmlDefaultHandler
+{
+public:
+ PlaylistSaver *saver;
+ bool fresh;
+
+ NoatunXMLStructure(PlaylistSaver *s)
+ : saver(s), fresh(true)
+ {
+ }
+
+ bool startElement(
+ const QString&, const QString &,
+ const QString &name, const QXmlAttributes &a
+ )
+ {
+ if (fresh)
+ {
+ if (name=="playlist")
+ {
+ fresh=false;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ if (name != "item")
+ return true;
+
+ QMap<QString,QString> propMap;
+
+ for (int i=0; i<a.count(); i++)
+ {
+ propMap[a.qName(i)] = a.value(i);
+ }
+ saver->readItem(propMap);
+
+ return true;
+ }
+};
+
+
+class MSASXStructure : public QXmlDefaultHandler
+{
+public:
+ PlaylistSaver *saver;
+ bool fresh;
+ bool inEntry, inTitle;
+ QMap<QString,QString> propMap;
+ QString mAbsPath;
+
+ MSASXStructure(PlaylistSaver *s, const QString &absPath)
+ : saver(s), fresh(true), inEntry(false),
+ inTitle(false), mAbsPath(absPath)
+ {
+ //kdDebug(66666) << k_funcinfo << endl;
+ }
+
+ bool startElement(const QString&, const QString &,
+ const QString &name, const QXmlAttributes &a)
+ {
+ if (fresh)
+ {
+ if (name.lower()=="asx")
+ {
+ //kdDebug(66666) << "found ASX format" << endl;
+ fresh=false;
+ return true;
+ }
+ else
+ {
+ kdDebug(66666) << "This is NOT an ASX style playlist!" << endl;
+ return false;
+ }
+ }
+
+ if (name.lower()=="entry")
+ {
+ if(inEntry) // WHOOPS, we are already in an entry, this should NEVER happen
+ {
+ kdDebug(66666) << "STOP, ENTRY INSIDE ENTRY!" << endl;
+ return false;
+ }
+// kdDebug(66666) << "<ENTRY> =====================" << endl;
+ inEntry=true;
+ }
+ else
+ {
+ if (inEntry) // inside entry block
+ {
+ // known stuff inside an <entry> ... </entry> block:
+ // <title>blah</title>
+ // <param album="blub" />
+ // <param artist="blah" />
+ // <ref HREF="file:/something" />
+ if(name.lower()=="ref")
+ {
+ for (int i=0; i<a.count(); i++)
+ {
+ if(a.qName(i).lower()=="href")
+ {
+ QString filename=a.value(i);
+ if (filename.find(QRegExp("^[a-zA-Z0-9]+:/"))==0)
+ {
+ KURL url(filename);
+ KMimeType::Ptr mimetype = KMimeType::findByURL(url);
+ QString type=mimetype->name();
+ if (type != "application/octet-stream")
+ {
+ propMap["url"]=filename;
+ }
+ else
+ {
+ propMap["playObject"]="SplayPlayObject";
+ propMap["title"] = i18n("Stream from %1").arg(url.host());
+ if (!url.hasPath())
+ url.setPath("/");
+ propMap["url"] = url.url();
+ propMap["stream_"]=propMap["url"];
+// readItem(propMap);
+// continue;
+ }
+ }
+ else
+ {
+ KURL u1;
+ // we have to deal with a relative path
+ if (filename.find('/'))
+ {
+ u1.setPath(mAbsPath); //FIXME: how to get the path in this place?
+ u1.setFileName(filename);
+ }
+ else
+ {
+ u1.setPath(filename);
+ }
+ propMap["url"]=u1.url();
+ }
+// kdDebug(66666) << "adding property url, value='" << propMap["url"] << "'" << endl;
+ }
+ }
+ }
+ else if(name.lower()=="param")
+ {
+ QString keyName="", keyValue="";
+
+ for (int i=0; i<a.count(); i++)
+ {
+ if(a.value(i).lower()=="album")
+ keyName="album";
+ else if(a.value(i).lower()=="artist")
+ keyName="author";
+ else if(!keyName.isEmpty()) // successfully found a key, the next key=value pair has to be the value
+ {
+// kdDebug(66666) << "keyName=" << keyName << ", next value is '" << a.value(i) << "'" << endl;
+ keyValue=a.value(i);
+ }
+ }
+
+ if (!keyName.isEmpty() && !keyValue.isEmpty())
+ {
+// kdDebug(66666) << "adding property; key='" << keyName << "', value='" << keyValue << "'" << endl;
+ propMap[keyName]=keyValue;
+ }
+ }
+ else if(name.lower()=="title")
+ {
+ if(inTitle) // WHOOPS, we are already in an entry, this should NEVER happen
+ {
+ kdDebug(66666) << "STOP, TITLE INSIDE TITLE!" << endl;
+ return false;
+ }
+// kdDebug(66666) << "<TITLE> ======" << endl;
+ inTitle=true;
+ }
+/* else
+ {
+ kdDebug(66666) << "Unknown/unused element inside ENTRY block, NAME='" << name << "'" << endl;
+ for (int i=0; i<a.count(); i++)
+ kdDebug(66666) << " | " << a.qName(i) << " = '" << a.value(i) << "'" << endl;
+ }*/
+ }
+/* else
+ {
+ kdDebug(66666) << "Uninteresting element, NAME='" << name << "'" << endl;
+ for (int i=0; i<a.count(); i++)
+ kdDebug(66666) << " | " << a.qName(i) << " = '" << a.value(i) << "'" << endl;
+ }*/
+ }
+
+ return true;
+ }
+
+ bool endElement(const QString&,const QString&, const QString& name)
+ {
+// kdDebug(66666) << k_funcinfo << "name='" << name << "'" << endl;
+ if (name.lower()=="entry")
+ {
+ if(inEntry)
+ {
+/* kdDebug(66666) << "</ENTRY> =====================" << endl;
+ for (QMap<QString,QString>::ConstIterator it=propMap.begin(); it!=propMap.end(); ++it )
+ kdDebug(66666) << "key='" << it.key() << "', val='" << it.data() << "'" << endl;
+*/
+ saver->readItem(propMap);
+ propMap.clear();
+ inEntry=false;
+ }
+ else // found </entry> without a start
+ {
+ kdDebug(66666) << "STOP, </ENTRY> without a start" << endl;
+ return false;
+ }
+ }
+ else if (name.lower()=="title")
+ {
+ if(inTitle && inEntry)
+ {
+// kdDebug(66666) << "</TITLE> ======" << endl;
+ inTitle=false;
+ }
+ else if (inTitle) // found </title> without a start or not inside an <entry> ... </entry>
+ {
+ kdDebug(66666) << "STOP, </TITLE> without a start" << endl;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool characters(const QString &ch)
+ {
+ if(inTitle)
+ {
+ if (!ch.isEmpty())
+ {
+ propMap["title"]=ch;
+// kdDebug(66666) << "adding property; key='title', value='" << ch << "'" << endl;
+ }
+ }
+ return true;
+ }
+};
+
+
+bool PlaylistSaver::loadXML(const KURL &url, int opt)
+{
+ kdDebug(66666) << k_funcinfo <<
+ "file='" << url.url() << "', opt=" << opt << endl;
+
+ QString dest;
+ if(KIO::NetAccess::download(url, dest, 0L))
+ {
+ QFile file(dest);
+ if (!file.open(IO_ReadOnly))
+ return false;
+
+ reset();
+
+ // QXml is horribly documented
+ QXmlInputSource source(&file);
+ QXmlSimpleReader reader;
+
+ if (opt == ASX ||
+ url.path().right(4).lower()==".wax" ||
+ url.path().right(4).lower()==".asx" ||
+ url.path().right(4).lower()==".wvx")
+ {
+ MSASXStructure ASXparser(this, url.path(0));
+ reader.setContentHandler(&ASXparser);
+ reader.parse(source);
+ return !ASXparser.fresh;
+ }
+ else
+ {
+ NoatunXMLStructure parser(this);
+ reader.setContentHandler(&parser);
+ reader.parse(source);
+ return !parser.fresh;
+ }
+ } // END download()
+
+ return false;
+}
+
+bool PlaylistSaver::loadM3U(const KURL &file, int /*opt*/)
+{
+ kdDebug(66666) << k_funcinfo << "file='" << file.path() << endl;
+
+ QString localFile;
+ if(!KIO::NetAccess::download(file, localFile, 0L))
+ return false;
+
+ // if it's a PLS, transfer control, again (KIO bug?)
+#if 0
+ {
+ KSimpleConfig list(local, true);
+ list.setGroup("playlist");
+
+ // some stupid Windows lusers like to be case insensitive
+ QStringList groups=list.groupList().grep(QRegExp("^playlist$", false));
+ if (groups.count())
+ {
+ KURL l;
+ l.setPath(local);
+ return loadPLS(l);
+ }
+ }
+#endif
+
+ QFile saver(localFile);
+ saver.open(IO_ReadOnly);
+ QTextStream t(&saver);
+
+ bool isExt = false; // flag telling if we load an EXTM3U file
+ QString filename;
+ QString extinf;
+ QMap<QString,QString> prop;
+ reset();
+
+ while (!t.eof())
+ {
+ if (isExt)
+ {
+ extinf = t.readLine();
+ if (!extinf.startsWith("#EXTINF:"))
+ {
+// kdDebug(66666) << "EXTM3U extinf line != extinf, assuming it's a filename." << endl;
+ filename = extinf;
+ extinf="";
+ }
+ else
+ {
+ filename = t.readLine(); // read in second line containing the filename
+ }
+ //kdDebug(66666) << "EXTM3U filename = '" << filename << "'" << endl;
+ }
+ else // old style m3u
+ {
+ filename = t.readLine();
+ }
+
+ if (filename == "#EXTM3U") // on first line
+ {
+// kdDebug(66666) << "FOUND '#EXTM3U' @ " << saver.at() << "." << endl;
+ isExt=true;
+ continue; // skip parsing the first (i.e. this) line
+ }
+
+ if (filename.isEmpty())
+ continue;
+
+ if (filename.find(QRegExp("^[a-zA-Z0-9]+:/"))==0)
+ {
+ //kdDebug(66666) << k_funcinfo << "url filename = " << filename << endl;
+
+ KURL protourl(filename);
+ KMimeType::Ptr mimetype = KMimeType::findByURL(protourl);
+
+ if (mimetype->name() != "application/octet-stream")
+ {
+ prop["url"] = filename;
+ }
+ else
+ {
+ prop["playObject"]="SplayPlayObject";
+ // Default title, might be overwritten by #EXTINF later
+ prop["title"] = i18n("Stream from %1").arg(protourl.host());
+
+ if (!protourl.hasPath())
+ protourl.setPath("/");
+
+ prop["url"] = protourl.url();
+ prop["stream_"] = prop["url"];
+ }
+ }
+ else // filename that is not of URL style (i.e. NOT "proto:/path/somefile")
+ {
+ KURL u1;
+ // we have to deal with a relative path
+ if (filename.find('/'))
+ {
+ u1.setPath(file.path(0));
+ u1.setFileName(filename);
+ }
+ else
+ {
+ u1.setPath(filename);
+ }
+ prop["url"] = u1.url();
+ }
+
+ // parse line of the following format:
+ //#EXTINF:length,displayed_title
+ if (isExt)
+ {
+ extinf.remove(0,8); // remove "#EXTINF:"
+ //kdDebug(66666) << "EXTM3U extinf = '" << extinf << "'" << endl;
+ int timeTitleSep = extinf.find(',', 0);
+
+ int length = (extinf.left(timeTitleSep)).toInt();
+ if (length>0)
+ prop["length"]=QString::number(length*1000);
+
+ QString displayTitle=extinf.mid(timeTitleSep+1);
+ if (!displayTitle.isEmpty())
+ {
+ int artistTitleSep = displayTitle.find(" - ",0);
+ if (artistTitleSep == -1) // no "artist - title" like format, just set it as title
+ {
+ prop["title"] = displayTitle;
+ }
+ else
+ {
+ prop["author"] = displayTitle.left(artistTitleSep);
+ prop["title"] = displayTitle.mid(artistTitleSep+3);
+ }
+ /*kdDebug(66666) << "EXTM3U author/artist='" << prop["author"] <<
+ "', title='" << prop["title"] << "'" << endl;*/
+ } // END !displayTitle.isEmpty()
+ } // END if(isExt)
+
+// kdDebug(66666) << k_funcinfo << "adding file '" << prop["url"] << "' to playlist" << endl;
+ readItem(prop);
+ prop.clear();
+ } // END while()
+
+ KIO::NetAccess::removeTempFile(localFile);
+
+// kdDebug(66666) << k_funcinfo << "END" << endl;
+ return true;
+}
+
+bool PlaylistSaver::saveM3U(const KURL &file, int opt)
+{
+// kdDebug(66666) << k_funcinfo << "file='" << file.path() << "', opt=" << opt << endl;
+
+ bool isExt=(opt==EXTM3U); // easier ;)
+
+ QString local(napp->tempSaveName(file.path()));
+ QFile saver(local);
+ saver.open(IO_ReadWrite | IO_Truncate);
+ QTextStream t(&saver);
+
+ reset();
+ PlaylistItem i;
+ QStringList props;
+
+ // this is more code but otoh faster than checking for isExt inside the loop
+ if(isExt)
+ {
+ t << "#EXTM3U" << '\n';
+
+ while ((i=writeItem()))
+ {
+ int length = static_cast<int>(((i.property("length")).toInt())/1000);
+ if(length==0) length=-1; // special value in an EXTM3U file, means "unknown"
+ KURL u(i.property("url"));
+ QString title;
+
+ // if a playlistitem is without a tag or ONLY title is set
+ if((i.property("author").isEmpty() && i.property("title").isEmpty())
+ || (i.property("author").isEmpty() && !i.property("title").isEmpty()) )
+ title = u.filename().left(u.filename().length()-4);
+ else
+ title = i.property("author") + " - " + i.property("title");
+
+// kdDebug(66666) << "#EXTINF:"<< QString::number(length) << "," << title << endl;
+ t << "#EXTINF:"<< QString::number(length) << "," << title << '\n';
+
+ if (u.isLocalFile())
+ t << u.path() << '\n';
+ else
+ t << u.url() << '\n';
+ }
+ }
+ else
+ {
+ while ((i=writeItem()))
+ {
+ KURL u(i.property("url"));
+ if (u.isLocalFile())
+ t << u.path() << '\n';
+ else
+ t << u.url() << '\n';
+ }
+ }
+
+ saver.close();
+ KIO::NetAccess::upload(local, file, 0L);
+ saver.remove();
+ return true;
+}
+
+static QString findNoCase(const QMap<QString,QString> &map, const QString &key)
+{
+ for (QMap<QString,QString>::ConstIterator i=map.begin(); i!=map.end(); ++i)
+ {
+ if (i.key().lower() == key.lower())
+ return i.data();
+ }
+ return 0;
+}
+
+bool PlaylistSaver::loadPLS(const KURL &file, int /*opt*/)
+{
+ kdDebug(66666) << k_funcinfo << "file='" << file.path() << endl;
+
+ QString localFile;
+ if(!KIO::NetAccess::download(file, localFile, 0L))
+ return false;
+
+ QFile checkFile(localFile);
+ checkFile.open(IO_ReadOnly);
+ QTextStream t(&checkFile);
+ QString firstLine = t.readLine();
+ if(firstLine.lower() != "[playlist]")
+ {
+ kdDebug(66666) << k_funcinfo << "PLS didn't start with '[playlist]', aborting" << endl;
+ return false;
+ }
+
+
+ KSimpleConfig list(localFile, true);
+ //list.setGroup("playlist");
+
+ // some stupid Windows lusers like to be case insensitive
+ QStringList groups = list.groupList().grep(QRegExp("^playlist$", false));
+ /*
+ if (!groups.count()) // didn't find "[playlist]", it's not a .pls file
+ return false;
+ */
+
+ QMap<QString,QString> group = list.entryMap(groups[0]);
+
+ QString numOfEntries = findNoCase(group, "numberofentries");
+ if(numOfEntries.isEmpty())
+ return false;
+
+ reset();
+
+ unsigned int nEntries = numOfEntries.toInt();
+ for(unsigned int entry = 1; entry <= nEntries; ++entry )
+ {
+ QString str;
+ str.sprintf("file%d", entry);
+ QString cast = findNoCase(group, str.utf8());
+ str.sprintf("title%d", entry);
+ QString title = findNoCase(group, str.utf8());
+
+ // assume that everything in a pls is a streamable file
+ QMap<QString,QString> map;
+
+ KURL url(cast);
+ if (!url.hasPath())
+ url.setPath("/");
+
+ map["playObject"]="SplayPlayObject";
+ if (title.isEmpty())
+ map["title"] = i18n("Stream from %1 (port: %2)").arg( url.host() ).arg( url.port() );
+ else
+ map["title"] = i18n("Stream from %1, (ip: %2, port: %3)").arg( title ).arg( url.host() ).arg(url.port() );
+
+ map["url"] = map["stream_"]= url.url();
+
+ readItem(map);
+ }
+ return true;
+}
+
+bool PlaylistSaver::savePLS(const KURL &, int)
+{
+ return false;
+}
+
+void PlaylistSaver::setGroup(const QString &)
+{
+}
+
+
diff --git a/noatun/library/plugin.cpp b/noatun/library/plugin.cpp
new file mode 100644
index 00000000..e9090609
--- /dev/null
+++ b/noatun/library/plugin.cpp
@@ -0,0 +1,578 @@
+#include <qtimer.h>
+#include <qfile.h>
+#include <artsflow.h>
+#include <vector>
+#include <artsflow.h>
+#include <soundserver.h>
+#include <noatunarts.h>
+#include <dcopclient.h>
+#include <dispatcher.h>
+#include <kdebug.h>
+#include <kstandarddirs.h>
+
+#include <cmath>
+
+#include "plugin_deps.h"
+#include "player.h"
+#include "plugin.h"
+#include "pluginloader.h"
+#include "app.h"
+#include "engine.h"
+
+using std::vector;
+
+Arts::Dispatcher *Visualization::mDispatcher=0;
+bool Visualization::internalVis=false;
+
+Plugin::Plugin()
+{
+}
+
+Plugin::~Plugin()
+{
+}
+
+void Plugin::init()
+{
+}
+
+bool Plugin::unload()
+{
+ return napp->libraryLoader()->remove(this);
+}
+
+TimerThingy::TimerThingy(Visualization *vis)
+ : mVis(vis), id(-1)
+{
+}
+
+void TimerThingy::timerEvent(QTimerEvent *)
+{
+ mVis->timeout();
+}
+
+void TimerThingy::setInterval(int msec)
+{
+ ms=msec;
+ if (id!=-1)
+ {
+ killTimer(id);
+ id=startTimer(msec);
+ }
+}
+
+void TimerThingy::start()
+{
+ if (id==-1)
+ id=startTimer(ms);
+}
+
+void TimerThingy::stop()
+{
+ if (id==-1)
+ {
+ killTimer(id);
+ id=-1;
+ }
+}
+
+
+Visualization::Visualization(int timeout, int pid)
+{
+ mTimerThingy=new TimerThingy(this);
+ setInterval(timeout);
+
+ // if this is a fork, do a cutesy arts thingy to get a remote
+ // stack, otherwise, get it from localhost :)
+ {
+ int parent=pid ? pid : getppid();
+
+ if (getenv("NOATUN_PID"))
+ parent = QString::fromLatin1(getenv("NOATUN_PID")).toInt();
+
+ DCOPClient c;
+ c.attach();
+
+ QCString appids[2];
+ appids[0]=QString("noatun-%1").arg(parent).local8Bit();
+ appids[1]="noatun";
+ QCString &appid=appids[0];
+
+ if (!internalVis && c.isApplicationRegistered(appids[0]))
+ {
+ appid=appids[0];
+ }
+ else if (!internalVis && c.isApplicationRegistered(appids[1]))
+ {
+ appid=appids[1];
+ }
+ else
+ {
+ kdDebug(66666) << "Internal Vis" <<endl;
+ mVisualizationStack=napp->player()->engine()->visualizationStack()->toString().c_str();
+ mServer=new Arts::SoundServerV2(*(napp->player()->engine()->server()));
+ return;
+ }
+
+ {
+ QByteArray replyData;
+ QCString replyType;
+
+ if (!c.call(appid, "Noatun", "visStack()", QByteArray(), replyType, replyData))
+ {
+ kdDebug(66666) << "Error communicating to parent noatun" << endl;
+ }
+ else
+ {
+ initDispatcher();
+ mServer=new Arts::SoundServerV2;
+ *mServer = Arts::Reference("global:Arts_SoundServerV2");
+ QDataStream reply(replyData, IO_ReadOnly);
+ QCString result;
+ reply >> result;
+ mVisualizationStack=result;
+ }
+ }
+ }
+}
+
+Visualization::~Visualization()
+{
+// napp->player()->engine()->visualizationStack()->remove(mId);
+
+ delete mServer;
+ delete mTimerThingy;
+}
+
+void Visualization::start()
+{
+ mTimerThingy->start();
+}
+
+void Visualization::stop()
+{
+ mTimerThingy->stop();
+}
+
+void Visualization::setInterval(int msec)
+{
+ mTimeout=msec;
+ if (!msec)
+ stop();
+ mTimerThingy->setInterval(msec);
+}
+
+int Visualization::interval() const
+{
+ return mTimeout;
+}
+
+Noatun::StereoEffectStack Visualization::visualizationStack()
+{
+ return Arts::Reference(mVisualizationStack);
+}
+
+Arts::SoundServerV2 *Visualization::server()
+{
+ return mServer;
+}
+
+int Visualization::noatunPid()
+{
+ DCOPClient dcop;
+ dcop.attach();
+ QCStringList apps = dcop.registeredApplications();
+ for (QCStringList::Iterator i = apps.begin(); i != apps.end(); ++i )
+ if ((*i).left(9) != "anonymous" )
+ {
+ if ((*i).left(6) != "noatun")
+ continue;
+ int pid=(*i).mid((*i).find('-')+1).toInt();
+ return pid;
+ }
+ return -1;
+}
+
+bool Visualization::connected()
+{
+ server()->_interfaceName(); // makes error() work
+ return !(server()->error() || server()->isNull());
+}
+
+void Visualization::initDispatcher()
+{
+ if (!mDispatcher)
+ {
+ mDispatcher=new Arts::Dispatcher;
+ }
+}
+
+FFTScope::FFTScope(int interval, int pid) : Visualization(interval, pid)
+{
+}
+
+float FFTScope::magic(int bands)
+{
+/* QString path=locate("data", "noatun/magictable");
+ QFile magic(path);
+ if (!magic.open(IO_ReadOnly | IO_Raw))
+ return 0;
+ if (!magic.at(bands*sizeof(float)))
+ return 0;
+
+ float value;
+ if (magic.readBlock((char*)&value, sizeof(float))==-1)
+ value=0;
+*/
+ bands += 20-1; // index-1 from FFTScopes.cpp
+ float value = std::log(std::pow(2046.0, 1.0/bands));
+
+ return value;
+}
+
+
+StereoFFTScope::StereoFFTScope(int timeout, int pid) : FFTScope(timeout, pid)
+{
+ mScope=new Noatun::FFTScopeStereo;
+ *mScope=Arts::DynamicCast(server()->createObject("Noatun::FFTScopeStereo"));
+
+ if ((*mScope).isNull())
+ {
+ delete mScope;
+ mScope=0;
+ }
+ else
+ {
+ mScope->start();
+ mId=visualizationStack().insertBottom(*mScope, "Noatun FFT");
+ }
+}
+
+StereoFFTScope::~StereoFFTScope()
+{
+ if (mScope)
+ {
+ if (connected())
+ visualizationStack().remove(mId);
+ mScope->stop();
+ delete mScope;
+ }
+}
+
+void StereoFFTScope::scopeData(vector<float> *&left, vector<float> *&right)
+{
+ left=mScope->scopeLeft();
+ right=mScope->scopeRight();
+}
+
+void StereoFFTScope::timeout()
+{
+ vector<float> *left, *right;
+ scopeData(left, right);
+
+ if (left->size())
+ scopeEvent(&left->front(), &right->front(), left->size());
+ delete left;
+ delete right;
+
+}
+
+int StereoFFTScope::bands() const
+{
+ vector<float> *d=mScope->scopeLeft();
+ int size=d->size();
+ delete d;
+ return size;
+}
+
+void StereoFFTScope::setBands(float f)
+{
+ mScope->bandResolution(f);
+}
+
+
+
+MonoFFTScope::MonoFFTScope(int timeout, int pid) : FFTScope(timeout, pid)
+{
+ mScope=new Noatun::FFTScope;
+ *mScope=Arts::DynamicCast(server()->createObject("Noatun::FFTScope"));
+
+ if ((*mScope).isNull())
+ {
+ delete mScope;
+ mScope=0;
+ }
+ else
+ {
+ mScope->start();
+ mId=visualizationStack().insertBottom(*mScope, "Noatun FFT");
+ }
+}
+
+MonoFFTScope::~MonoFFTScope()
+{
+ if (mScope)
+ {
+ if (connected())
+ visualizationStack().remove(mId);
+ mScope->stop();
+ delete mScope;
+ }
+}
+
+void MonoFFTScope::scopeData(vector<float> *&data)
+{
+ data=mScope->scope();
+}
+
+void MonoFFTScope::timeout()
+{
+ vector<float> *data(mScope->scope());
+
+ float *f=&data->front();
+ if (data->size()) scopeEvent(f, data->size());
+ delete data;
+}
+
+int MonoFFTScope::bands() const
+{
+ vector<float> *d=mScope->scope();
+ int size=d->size();
+ delete d;
+ return size;
+
+}
+
+void MonoFFTScope::setBands(float f)
+{
+ mScope->bandResolution(f);
+}
+
+
+
+
+
+
+Scope::Scope(int interval, int pid) : Visualization(interval, pid)
+{
+}
+
+
+
+MonoScope::MonoScope(int timeout, int pid) : Scope(timeout, pid)
+{
+ mScope=new Noatun::RawScope;
+ *mScope=Arts::DynamicCast(server()->createObject("Noatun::RawScope"));
+
+ if ((*mScope).isNull())
+ {
+ delete mScope;
+ mScope=0;
+ }
+ else
+ {
+ mScope->start();
+ mId=visualizationStack().insertBottom(*mScope, "Noatun Scope");
+ }
+}
+
+MonoScope::~MonoScope()
+{
+ if (mScope)
+ {
+ if (connected())
+ visualizationStack().remove(mId);
+ mScope->stop();
+ delete mScope;
+ }
+}
+
+void MonoScope::scopeData(vector<float> *&data)
+{
+ data=mScope->scope();
+}
+
+void MonoScope::timeout()
+{
+ vector<float> *data(mScope->scope());
+
+ float *f=&data->front();
+ if (data->size()) scopeEvent(f, data->size());
+ delete data;
+}
+
+int MonoScope::samples() const
+{
+ return (long)mScope->buffer();
+}
+
+void MonoScope::setSamples(int len)
+{
+ mScope->buffer((long)len);
+}
+
+
+
+StereoScope::StereoScope(int timeout, int pid) : Scope(timeout, pid)
+{
+ mScope=new Noatun::RawScopeStereo;
+ *mScope=Arts::DynamicCast(server()->createObject("Noatun::RawScopeStereo"));
+
+ if ((*mScope).isNull())
+ {
+ delete mScope;
+ mScope=0;
+ }
+ else
+ {
+ mScope->start();
+ mId=visualizationStack().insertBottom(*mScope, "Noatun Stereo Scope");
+ }
+}
+
+StereoScope::~StereoScope()
+{
+ if (mScope)
+ {
+ if (connected())
+ visualizationStack().remove(mId);
+ mScope->stop();
+ delete mScope;
+ }
+}
+
+void StereoScope::scopeData(vector<float> *&left, vector<float> *&right)
+{
+ left=mScope->scopeLeft();
+ right=mScope->scopeRight();
+}
+
+void StereoScope::timeout()
+{
+ vector<float> *left(mScope->scopeLeft());
+ vector<float> *right(mScope->scopeRight());
+
+ float *l=&left->front();
+ float *r=&right->front();
+
+ if (left->size()==right->size() && left->size())
+ scopeEvent(l, r, left->size());
+ delete left;
+ delete right;
+}
+
+int StereoScope::samples() const
+{
+ return (long)mScope->buffer();
+}
+
+void StereoScope::setSamples(int len)
+{
+ mScope->buffer((long)len);
+}
+
+
+
+NoatunListener::NoatunListener(QObject *parent) : QObject(parent)
+{ }
+
+NoatunListener::~NoatunListener()
+{ }
+
+void NoatunListener::message()
+{
+ emit event();
+}
+
+NoatunListenerNotif::NoatunListenerNotif(NoatunListener *l)
+{
+ mListener=l;
+}
+
+void NoatunListenerNotif::message()
+{
+ mListener->message();
+}
+
+
+ExitNotifier::ExitNotifier(int pid, QObject *parent) : NoatunListener(parent)
+{
+ mNotif=new NoatunListenerNotif(this);
+
+ DCOPClient c;
+ c.attach();
+
+
+ QCString appids[2];
+ appids[0]=QString("noatun-%1").arg(pid).local8Bit();
+ appids[1]="noatun";
+ appid=appids[0];
+
+ if (c.isApplicationRegistered(appids[0]))
+ {
+ appid=appids[0];
+ }
+ else if (c.isApplicationRegistered(appids[1]))
+ {
+ appid=appids[1];
+ }
+ else
+ {
+ return;
+ }
+
+ QByteArray replyData;
+ QCString replyType;
+
+ QCString sessionName;
+
+ if (!c.call(appid, "Noatun", "session()", QByteArray(), replyType, replyData))
+ {
+ kdDebug(66666) << "Error communicating to parent noatun" << endl;
+ }
+ else
+ {
+ QDataStream reply(replyData, IO_ReadOnly);
+ reply >> sessionName;
+ }
+
+ Visualization::initDispatcher();
+
+ Noatun::Session session=Arts::Reference(sessionName);
+ session.addListener(*mNotif);
+}
+
+ExitNotifier::~ExitNotifier()
+{
+ QByteArray replyData;
+ QCString replyType;
+
+ QCString sessionName;
+
+ DCOPClient c;
+ c.attach();
+
+ if (c.call(appid, "Noatun", "session()", QByteArray(), replyType, replyData))
+ {
+ QDataStream reply(replyData, IO_ReadOnly);
+ reply >> sessionName;
+
+ Noatun::Session session=Arts::Reference(sessionName);
+ session.removeListener(*mNotif);
+ }
+ delete mNotif;
+}
+
+BoolNotifier::BoolNotifier(bool *value, NoatunListener *listener, QObject *parent)
+ : QObject(parent)
+{
+ connect(listener, SIGNAL(event()), SLOT(event()));
+ mValue=value;
+}
+
+SessionManagement::SessionManagement() { }
+SessionManagement::~SessionManagement() { }
+void SessionManagement::restore() { }
+
+#include "plugin.moc"
+#include "plugin_deps.moc"
diff --git a/noatun/library/plugin_deps.h b/noatun/library/plugin_deps.h
new file mode 100644
index 00000000..a2f3f628
--- /dev/null
+++ b/noatun/library/plugin_deps.h
@@ -0,0 +1,44 @@
+#ifndef PLUGIN_DEPS_H
+#define PLUGIN_DEPS_H
+
+#include <qtimer.h>
+#include <qobject.h>
+#include <noatunarts.h>
+
+class Visualization;
+class NoatunListener;
+
+
+class TimerThingy : public QObject
+{
+Q_OBJECT
+public:
+ TimerThingy(Visualization*);
+
+ void setInterval(int msec);
+ void stop();
+ void start();
+
+public:
+ virtual void timerEvent(QTimerEvent *);
+
+private:
+ Visualization *mVis;
+ int id;
+ int ms;
+};
+
+class NoatunListenerNotif : public Noatun::Listener_skel
+{
+public:
+ NoatunListenerNotif(NoatunListener *);
+
+ virtual void message();
+
+ operator Noatun::Listener() { return Noatun::Listener::_from_base(_copy()); }
+private:
+ NoatunListener *mListener;
+};
+
+#endif
+
diff --git a/noatun/library/pluginloader.cpp b/noatun/library/pluginloader.cpp
new file mode 100644
index 00000000..c367228a
--- /dev/null
+++ b/noatun/library/pluginloader.cpp
@@ -0,0 +1,366 @@
+#include <qfile.h>
+#include <kglobal.h>
+#include <qdir.h>
+#include <ksimpleconfig.h>
+#include <kstandarddirs.h>
+#include <knotifyclient.h>
+#include <klocale.h>
+#include <kurl.h>
+#include <pluginloader.h>
+#include <plugin.h>
+#include <kdebug.h>
+
+bool operator ==(const NoatunLibraryInfo &a, const NoatunLibraryInfo &b)
+{
+ // Feels like cheating, doesn't it?
+ return a.specfile == b.specfile;
+}
+
+LibraryLoader::LibraryLoader() : mPlaylist(0)
+{
+}
+
+LibraryLoader::~LibraryLoader()
+{
+ QValueList<NoatunLibraryInfo> l;
+
+ l = loaded();
+ for(QValueList<NoatunLibraryInfo>::Iterator i = l.begin(); i != l.end(); ++i)
+ {
+ if((*i).type != "userinterface" && (*i).type != "playlist" && (*i).type != "systray")
+ {
+ removeNow((*i).specfile);
+ }
+ }
+
+ l = loaded();
+ for(QValueList<NoatunLibraryInfo>::Iterator i = l.begin(); i != l.end(); ++i)
+ {
+ if((*i).type == "userinterface")
+ {
+ removeNow((*i).specfile);
+ }
+ }
+
+ l = loaded();
+ for(QValueList<NoatunLibraryInfo>::Iterator i = l.begin(); i != l.end(); ++i)
+ {
+ removeNow((*i).specfile);
+ }
+}
+
+QValueList<NoatunLibraryInfo> LibraryLoader::available() const
+{
+ QValueList<NoatunLibraryInfo> items;
+ QStringList files=KGlobal::dirs()->findAllResources("appdata", "*.plugin", false, true);
+ for (QStringList::Iterator i=files.begin(); i!=files.end(); ++i)
+ items.append(getInfo(*i));
+
+ return items;
+}
+
+QPtrList<Plugin> LibraryLoader::plugins() const
+{
+ QPtrList<Plugin> list;
+ for (QDictIterator<LibraryLoader::PluginLibrary> i(mLibHash); i.current(); ++i)
+ list.append(i.current()->plugin);
+ return list;
+}
+
+bool LibraryLoader::loadAll()
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup("");
+ QStringList modules = config->readListEntry("Modules");
+ return loadAll(modules);
+}
+
+bool LibraryLoader::loadAll(const QStringList &modules)
+{
+ // Session management...
+ for(QStringList::ConstIterator i=modules.begin(); i!=modules.end(); ++i)
+ {
+ NoatunLibraryInfo info=getInfo(*i);
+ if (!info.type.contains("sm"))
+ continue;
+ loadSO(*i);
+ }
+
+ // load all the playlists in the first
+ for(QStringList::ConstIterator i=modules.begin(); i!=modules.end(); ++i)
+ {
+ NoatunLibraryInfo info=getInfo(*i);
+ if (!info.type.contains("playlist"))
+ continue;
+ loadSO(*i);
+ }
+
+ if (!mPlaylist)
+ {
+ kdWarning(66666) << "No playlist plugin loaded, defaulting to splitplaylist" << endl;
+ if (!loadSO("splitplaylist.plugin"))
+ return false;
+ }
+
+ // load all the user interfaces now
+ for(QStringList::ConstIterator i=modules.begin(); i!=modules.end(); ++i)
+ {
+ NoatunLibraryInfo info=getInfo(*i);
+ if (!info.type.contains("userinterface"))
+ continue;
+ loadSO(*i);
+ }
+
+ if (!loadedByType("userinterface").count())
+ {
+ kdWarning(66666) << "No userinterface plugin loaded, defaulting to excellent" << endl;
+ if (!loadSO("excellent.plugin"))
+ return false;
+ }
+
+ for(QStringList::ConstIterator i=modules.begin(); i!=modules.end(); ++i)
+ {
+ NoatunLibraryInfo info=getInfo(*i);
+ if((!info.type.contains("playlist"))
+ && (!info.type.contains("userinterface"))
+ && (!info.type.contains("sm")))
+ {
+ loadSO(*i);
+ }
+ }
+
+ return true;
+}
+
+NoatunLibraryInfo LibraryLoader::getInfo(const QString &spec) const
+{
+ NoatunLibraryInfo info;
+ QString specPath = (spec[0]=='/') ? spec : KGlobal::dirs()->findResource("appdata", spec);
+ if (!QFile::exists(specPath))
+ return info;
+ KSimpleConfig file(specPath);
+ if (spec.find('/')>=0)
+ info.specfile=KURL(spec).fileName();
+ else
+ info.specfile=spec;
+ info.filename=file.readPathEntry("Filename");
+ info.author=file.readEntry("Author");
+ info.site=file.readEntry("Site");
+ info.email=file.readEntry("Email");
+ info.type=file.readEntry("Type");
+ info.name=file.readEntry("Name");
+ info.comment=file.readEntry("Comment");
+ info.require=file.readListEntry("Require");
+ info.license=file.readEntry("License");
+ return info;
+}
+
+bool LibraryLoader::isLoaded(const QString &spec) const
+{
+ PluginLibrary *lib=mLibHash[spec];
+ if (!lib) return false;
+ return lib->plugin;
+}
+
+bool LibraryLoader::loadSO(const QString &spec)
+{
+ if(!isLoaded(spec))
+ {
+ NoatunLibraryInfo info = getInfo(spec);
+ if (info.specfile != spec)
+ return false;
+
+ for (QStringList::ConstIterator it = info.require.begin(); it != info.require.end(); ++it)
+ loadSO(*it);
+
+ // get the library loader instance
+ KLibLoader *loader = KLibLoader::self();
+
+ PluginLibrary *listitem=mLibHash[spec];
+
+ if (!listitem)
+ {
+ QString filename = KGlobal::dirs()->findResource("module", info.filename);
+ KLibrary *lib = loader->library(QFile::encodeName(filename));
+ if (!lib)
+ return false;
+ listitem=new PluginLibrary;
+ listitem->library=lib;
+ mLibHash.insert(spec, listitem);
+ }
+
+ void *create=listitem->library->symbol("create_plugin");
+ if (!create)
+ return false;
+
+ Plugin* (*plugInStart)();
+ plugInStart=(Plugin* (*)()) create;
+ listitem->plugin=plugInStart();
+
+ if (info.type.contains("playlist"))
+ {
+ //kdDebug(66666) << k_funcinfo << "Assigning mPlaylist to " << info.name << endl;
+ mPlaylist=listitem->plugin->playlist();
+ }
+ listitem->plugin->init();
+
+ return true;
+ }
+ else
+ return false;
+}
+
+void LibraryLoader::add(const QString &spec)
+{
+ PluginLibrary *lib=mLibHash[spec];
+ if (lib)
+ if (lib->plugin) return;
+
+ loadSO(spec);
+}
+
+void LibraryLoader::setModules(const QStringList &mods)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(0);
+ config->writeEntry("Modules", mods);
+ config->sync();
+}
+
+bool LibraryLoader::remove(const QString& spec, bool terminateOnLastUI)
+{
+ bool SystrayPluginEnabled=false;
+
+ NoatunLibraryInfo info = getInfo(spec);
+ // exit if this is the last UI
+ if (info.type=="userinterface" && terminateOnLastUI)
+ {
+ QValueList<NoatunLibraryInfo> l=loaded();
+
+ // Iterate over other plugins
+ for (QValueList<NoatunLibraryInfo>::Iterator i=l.begin(); i!=l.end(); ++i)
+ {
+ // Is this a UI plugin?
+ if ((*i).specfile!=spec && (*i).type=="userinterface")
+ {
+ // Good, we don't have to exit
+ removeNow(spec);
+ return true;
+ }
+ else if((*i).type=="systray") // Is there a Systray plugin?
+ {
+ SystrayPluginEnabled=true;
+ }
+ }
+
+ // Don't terminate, when there is a systray plugin.
+ if(SystrayPluginEnabled)
+ {
+ napp->toggleInterfaces();
+ return true;
+ }
+ else
+ {
+ // No other UIs, terminate
+ kapp->exit();
+ }
+ }
+ else if (info.type=="playlist")
+ {
+ //kdWarning(66666) << "Unloading playlist, this might be dangerous" << endl;
+ mPlaylist=0;
+ }
+
+ removeNow(spec);
+ return true;
+}
+
+bool LibraryLoader::remove(const QString &spec)
+{
+ remove(spec, true);
+ return true;
+}
+
+bool LibraryLoader::remove(const PluginLibrary *pl)
+{
+ for (QDictIterator<PluginLibrary> i(mLibHash); i.current(); ++i)
+ {
+ if (i.current()==pl)
+ return remove(i.currentKey());
+ }
+ return false;
+}
+
+bool LibraryLoader::remove(const Plugin *plugin)
+{
+ for (QDictIterator<PluginLibrary> i(mLibHash); i.current(); ++i)
+ {
+ if (i.current()->plugin==plugin)
+ return remove(i.currentKey());
+ }
+ return false;
+
+}
+
+Playlist *LibraryLoader::playlist() const
+{
+ // playlist should never be NULL when this method is called
+ Q_ASSERT(mPlaylist);
+ return mPlaylist;
+}
+
+QValueList<NoatunLibraryInfo> LibraryLoader::loaded() const
+{
+ QValueList<NoatunLibraryInfo> items;
+
+ for (QDictIterator<PluginLibrary> i(mLibHash); i.current(); ++i)
+ if (isLoaded(i.currentKey()))
+ items.append(getInfo(i.currentKey()));
+
+ return items;
+}
+
+QValueList<NoatunLibraryInfo> LibraryLoader::loadedByType(const QString &type) const
+{
+ QValueList<NoatunLibraryInfo> items;
+
+ for (QDictIterator<PluginLibrary> i(mLibHash); i.current(); ++i)
+ {
+ if (isLoaded(i.currentKey()))
+ {
+ NoatunLibraryInfo info = getInfo(i.currentKey());
+ if (info.type.contains(type))
+ items.append(info);
+ }
+ }
+
+ return items;
+}
+
+void LibraryLoader::removeNow(const QString &spec)
+{
+ NoatunLibraryInfo info = getInfo(spec);
+ if (info.specfile == spec)
+ {
+ QValueList<NoatunLibraryInfo> l = loaded();
+ for (QValueList<NoatunLibraryInfo>::Iterator i = l.begin(); i != l.end(); ++i)
+ {
+ for (QStringList::ConstIterator it = (*i).require.begin(); it != (*i).require.end(); ++it)
+ {
+ if (*it == spec)
+ removeNow((*i).specfile);
+ }
+ }
+ }
+
+ PluginLibrary *lib=mLibHash[spec];
+
+ if (!lib) return;
+
+ delete lib->plugin;
+ lib->plugin=0;
+ mLibHash.remove(spec);
+ delete lib;
+}
+
+
diff --git a/noatun/library/pluginmodule.cpp b/noatun/library/pluginmodule.cpp
new file mode 100644
index 00000000..aec76e44
--- /dev/null
+++ b/noatun/library/pluginmodule.cpp
@@ -0,0 +1,406 @@
+// Copyright (c) 2000-2001 Charles Samuels <charles@kde.org>
+// Copyright (c) 2000-2001 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIAB\ILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include <noatun/app.h>
+
+#include <kdebug.h>
+#include <kdialog.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <qtabwidget.h>
+#include <qheader.h>
+#include <qlabel.h>
+#include <qlayout.h>
+
+#include "noatunlistview.h"
+#include "pluginmodule.h"
+
+#include <qwhatsthis.h>
+
+#include "common.h"
+
+PluginListItem::PluginListItem(const bool _exclusive, bool _checked, const NoatunLibraryInfo &_info, QListView *_parent)
+ : QCheckListItem(_parent, _info.name, CheckBox)
+ , mInfo(_info)
+ , silentStateChange(false)
+ , exclusive(_exclusive)
+{
+ setChecked(_checked);
+ if(_checked) static_cast<PluginListView *>(listView())->count++;
+}
+
+
+void PluginListItem::setChecked(bool b)
+{
+ silentStateChange = true;
+ setOn(b);
+ silentStateChange = false;
+}
+
+void PluginListItem::stateChange(bool b)
+{
+ if(!silentStateChange)
+ static_cast<PluginListView *>(listView())->stateChanged(this, b);
+}
+
+void PluginListItem::paintCell(QPainter *p, const QColorGroup &cg, int a, int b, int c)
+{
+ if(exclusive) myType = RadioButton;
+ QCheckListItem::paintCell(p, cg, a, b, c);
+ if(exclusive) myType = CheckBox;
+}
+
+PluginListView::PluginListView(unsigned _min, unsigned _max, QWidget *_parent, const char *_name)
+ : KListView(_parent, _name)
+ , hasMaximum(true)
+ , max(_max)
+ , min(_min <= _max ? _min : _max)
+ , count(0)
+{
+}
+
+PluginListView::PluginListView(unsigned _min, QWidget *_parent, const char *_name)
+ : KListView(_parent, _name)
+ , hasMaximum(false)
+ , min(_min)
+ , count(0)
+{
+}
+
+PluginListView::PluginListView(QWidget *_parent, const char *_name)
+ : KListView(_parent, _name)
+ , hasMaximum(false)
+ , min(0)
+ , count(0)
+{
+}
+
+void PluginListView::clear()
+{
+ count = 0;
+ KListView::clear();
+}
+
+void PluginListView::stateChanged(PluginListItem *item, bool b)
+{
+ if(b)
+ {
+ count++;
+ emit stateChange(item, b);
+
+ if(hasMaximum && count > max)
+ {
+ // Find a different one and turn it off
+
+ QListViewItem *cur = firstChild();
+ PluginListItem *curItem = dynamic_cast<PluginListItem *>(cur);
+
+ while(cur == item || !curItem || !curItem->isOn())
+ {
+ cur = cur->nextSibling();
+ curItem = dynamic_cast<PluginListItem *>(cur);
+ }
+
+ curItem->setOn(false);
+ }
+ }
+ else
+ {
+ if(count == min)
+ {
+ item->setChecked(true);
+ }
+ else
+ {
+ count--;
+ emit stateChange(item, b);
+ }
+ }
+}
+
+Plugins::Plugins(QObject *_parent)
+ : CModule(i18n("Plugins"), i18n("Select Your Plugins"), "gear", _parent)
+ , shown(false)
+{
+ (new QVBoxLayout(this))->setAutoAdd(true);
+ QTabWidget *tabControl = new QTabWidget(this,"tabControl");
+
+ QFrame *interfaceTab = new QFrame(tabControl);
+ (new QVBoxLayout(interfaceTab, KDialog::marginHint(), KDialog::spacingHint()))->setAutoAdd(true);
+ (void)new QLabel(i18n("<b>Select one or more interfaces to use:</b>"), interfaceTab);
+ // At least one interface is required
+ interfaceList = new PluginListView(1, interfaceTab);
+ interfaceList->addColumn(i18n("Name"));
+ interfaceList->addColumn(i18n("Description"));
+ interfaceList->addColumn(i18n("Author"));
+ interfaceList->addColumn(i18n("License"));
+ connect(interfaceList, SIGNAL(stateChange(PluginListItem *, bool)), this, SLOT(stateChange(PluginListItem *, bool)));
+ tabControl->addTab(interfaceTab, i18n("&Interfaces"));
+
+ QFrame *playlistTab = new QFrame(tabControl);
+ (new QVBoxLayout(playlistTab, KDialog::marginHint(), KDialog::spacingHint()))->setAutoAdd(true);
+ (void)new QLabel(i18n("<b>Select one playlist to use:</b>"), playlistTab);
+ // Exactly one playlist is required
+ playlistList = new PluginListView(1, 1, playlistTab);
+ playlistList->addColumn(i18n("Name"));
+ playlistList->addColumn(i18n("Description"));
+ playlistList->addColumn(i18n("Author"));
+ playlistList->addColumn(i18n("License"));
+ connect(playlistList, SIGNAL(stateChange(PluginListItem *, bool)), this, SLOT(stateChange(PluginListItem *, bool)));
+ tabControl->addTab(playlistTab, i18n("&Playlist"));
+
+ QFrame *visTab = new QFrame(tabControl);
+ (new QVBoxLayout(visTab, KDialog::marginHint(), KDialog::spacingHint()))->setAutoAdd(true);
+ (void)new QLabel(i18n("<b>Select any visualizations to use:</b>"), visTab);
+ visList = new PluginListView(0, visTab);
+ visList->addColumn(i18n("Name"));
+ visList->addColumn(i18n("Description"));
+ visList->addColumn(i18n("Author"));
+ visList->addColumn(i18n("License"));
+ connect(visList, SIGNAL(stateChange(PluginListItem *, bool)), this, SLOT(stateChange(PluginListItem *, bool)));
+ tabControl->addTab(visTab, i18n("&Visualizations"));
+
+ // Other plugins are not restricted
+ QFrame *otherTab = new QFrame(tabControl);
+ (new QVBoxLayout(otherTab, KDialog::marginHint(), KDialog::spacingHint()))->setAutoAdd(true);
+ (void)new QLabel(i18n("<b>Select any other plugins to use:</b>"), otherTab);
+ otherList = new PluginListView(0, otherTab);
+ otherList->addColumn(i18n("Name"));
+ otherList->addColumn(i18n("Description"));
+ otherList->addColumn(i18n("Author"));
+ otherList->addColumn(i18n("License"));
+ connect(otherList, SIGNAL(stateChange(PluginListItem *, bool)), this, SLOT(stateChange(PluginListItem *, bool)));
+ tabControl->addTab(otherTab, i18n("O&ther Plugins"));
+}
+
+void Plugins::reopen()
+{
+ playlistList->clear();
+ interfaceList->clear();
+ otherList->clear();
+ visList->clear();
+
+ QValueList<NoatunLibraryInfo> available = napp->libraryLoader()->available();
+ QValueList<NoatunLibraryInfo> loaded = napp->libraryLoader()->loaded();
+
+ for(QValueList<NoatunLibraryInfo>::Iterator i = available.begin(); i != available.end(); ++i)
+ {
+ PluginListView *parent;
+ bool exclusive = false;
+
+ if((*i).type == "userinterface")
+ {
+ parent = interfaceList;
+ }
+ else if((*i).type == "playlist")
+ {
+ parent = playlistList;
+ exclusive = true;
+ }
+ else if((*i).type == "sm" || (*i).type=="hidden")
+ {
+ parent = 0;
+ }
+ else if ((*i).type == "visualization")
+ {
+ parent = visList;
+ }
+ else
+ {
+ parent = otherList;
+ }
+
+ if(parent)
+ {
+ PluginListItem *item = new PluginListItem(exclusive, loaded.contains(*i), *i, parent);
+ item->setText(0, (*i).name);
+ item->setText(1, (*i).comment);
+ item->setText(2, (*i).author);
+ item->setText(3, (*i).license);
+ }
+ }
+}
+
+void Plugins::stateChange(PluginListItem *item, bool b)
+{
+ if(b)
+ addPlugin(item->info());
+ else
+ removePlugin(item->info());
+}
+
+void Plugins::addPlugin(const NoatunLibraryInfo &info)
+{
+ // Load any that this one depends upon
+ for(QStringList::ConstIterator i = info.require.begin(); i != info.require.end(); ++i)
+ {
+ NoatunLibraryInfo requiredInfo = napp->libraryLoader()->getInfo(*i);
+ PluginListItem *item = findItem(requiredInfo);
+ if(item) item->setOn(true);
+ }
+
+ if(mDeleted.contains(info.specfile))
+ mDeleted.remove(info.specfile);
+ else if(!mAdded.contains(info.specfile))
+ mAdded.append(info.specfile);
+}
+
+void Plugins::removePlugin(const NoatunLibraryInfo &info)
+{
+ LibraryLoader &loader = *(napp->libraryLoader());
+
+ // Here are the ones loaded
+ QValueList<NoatunLibraryInfo> loaded = napp->libraryLoader()->loaded();
+
+ // Add the ones marked for loading
+ for(QStringList::ConstIterator i = mAdded.begin(); i != mAdded.end(); ++i)
+ loaded.append(loader.getInfo(*i));
+
+ // Subtract the ones marked for removal
+ for(QStringList::ConstIterator i = mDeleted.begin(); i != mDeleted.end(); ++i)
+ loaded.remove(loader.getInfo(*i));
+
+ // If any depend on this plugin, mark them for removal (or remove them from mAdded)
+ for(QValueList<NoatunLibraryInfo>::Iterator i = loaded.begin(); i != loaded.end(); ++i)
+ {
+ for(QStringList::ConstIterator j = (*i).require.begin(); j != (*i).require.end(); ++j)
+ {
+ if(*j == info.specfile)
+ {
+ PluginListItem *item = findItem(*i);
+ if(item) item->setOn(false);
+ }
+ }
+ }
+
+ if (mAdded.contains(info.specfile))
+ mAdded.remove(info.specfile);
+ else if(!mDeleted.contains(info.specfile))
+ mDeleted.append(info.specfile);
+}
+
+PluginListItem *Plugins::findItem(const NoatunLibraryInfo &info) const
+{
+ for(QListViewItem *cur = otherList->firstChild(); cur != 0; cur = cur->itemBelow())
+ {
+ PluginListItem *item = dynamic_cast<PluginListItem *>(cur);
+ if(item && item->info() == info)
+ return item;
+ }
+
+ // visualizations
+ for(QListViewItem *cur = visList->firstChild(); cur != 0; cur = cur->itemBelow())
+ {
+ PluginListItem *item = dynamic_cast<PluginListItem *>(cur);
+ if(item && item->info() == info)
+ return item;
+ }
+
+ // If our only interface has a dependency removed, that's a double dose of trouble
+ // We may as well have this here for completeness, though
+ for(QListViewItem *cur = interfaceList->firstChild(); cur != 0; cur = cur->itemBelow())
+ {
+ PluginListItem *item = dynamic_cast<PluginListItem *>(cur);
+ if(item && item->info() == info)
+ return item;
+ }
+
+ // If a playlist is added or removed due to a dependency, we're doom-diddly-oomed
+ // We may as well have this here for completeness, though
+ for(QListViewItem *cur = playlistList->firstChild(); cur != 0; cur = cur->itemBelow())
+ {
+ PluginListItem *item = dynamic_cast<PluginListItem *>(cur);
+ if(item && item->info() == info)
+ return item;
+ }
+
+ return 0;
+}
+
+void Plugins::save()
+{
+ LibraryLoader &loader = *(napp->libraryLoader());
+
+ // Load the plugins the user added
+ //loader.loadAll(mAdded);
+
+ QString oldPlaylist, newPlaylist;
+
+ // first load all non playlist things
+ for (QStringList::Iterator i = mAdded.begin(); i != mAdded.end(); ++i)
+ {
+ NoatunLibraryInfo info = loader.getInfo(*i);
+ if(info.type != "playlist")
+ loader.loadAll(QStringList(*i));
+ else
+ newPlaylist = (*i);
+ }
+
+ // Remove the plugins the user removed
+ for (QStringList::Iterator i = mDeleted.begin(); i != mDeleted.end(); ++i)
+ {
+ NoatunLibraryInfo info = loader.getInfo(*i);
+ if(info.type != "playlist")
+ loader.remove(*i);
+ else
+ oldPlaylist = *i;
+ }
+
+ // Loading normal plugins works the other way round!
+ // If you unload a playlist it sets the global playlist pointer to NULL,
+ // that also means you cannot first load the new and then unload the old one.
+ if(!newPlaylist.isEmpty() && !oldPlaylist.isEmpty())
+ {
+ kdDebug(66666) << k_funcinfo << "Unloading " << oldPlaylist << endl;
+ loader.remove(oldPlaylist);
+ kdDebug(66666) << k_funcinfo << "Loading " << oldPlaylist << endl;
+ loader.loadAll(QStringList(newPlaylist));
+ }
+
+
+ // Round up the ones that weren't loaded right now, for saving in the configuration
+ QStringList specList(mAdded);
+
+ QValueList<NoatunLibraryInfo> loaded = loader.loaded();
+ for(QValueList<NoatunLibraryInfo>::Iterator i = loaded.begin(); i != loaded.end(); ++i)
+ {
+ if(!specList.contains((*i).specfile) && loader.isLoaded((*i).specfile))
+ specList += (*i).specfile;
+ }
+
+ // Now we actually save
+ loader.setModules(specList);
+
+ mDeleted.clear();
+ mAdded.clear();
+}
+
+void Plugins::showEvent(QShowEvent *e)
+{
+ if(!shown)
+ {
+ shown = true;
+ KMessageBox::information(this, i18n("<qt>Changing your playlist plugin will stop playback. Different playlists may use different methods of storing information, so after changing playlists you may have to recreate your playlist.</qt>"), QString::null, "Plugin warning");
+ }
+ CModule::showEvent(e);
+}
+
+#include "pluginmodule.moc"
diff --git a/noatun/library/pluginmodule.h b/noatun/library/pluginmodule.h
new file mode 100644
index 00000000..75d3e04d
--- /dev/null
+++ b/noatun/library/pluginmodule.h
@@ -0,0 +1,100 @@
+// Copyright (c) 2000-2001 Charles Samuels <charles@kde.org>
+// Copyright (c) 2000-2001 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIAB\ILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef PLUGINMODULE_H
+#define PLUGINMODULE_H
+
+#include "noatun/pref.h"
+#include "noatun/pluginloader.h"
+
+#include <klistview.h>
+#include <qframe.h>
+#include <qstringlist.h>
+
+class KTabCtl;
+
+class PluginListItem : public QCheckListItem
+{
+public:
+ PluginListItem(const bool _exclusive, bool _checked, const NoatunLibraryInfo &_info, QListView *_parent);
+ const NoatunLibraryInfo &info() const { return mInfo; }
+
+ // This will toggle the state without "emitting" the stateChange
+ void setChecked(bool);
+
+protected:
+ virtual void stateChange(bool);
+ virtual void paintCell(QPainter *, const QColorGroup &, int, int, int);
+private:
+ NoatunLibraryInfo mInfo;
+ bool silentStateChange;
+ bool exclusive;
+};
+
+class PluginListView : public KListView
+{
+Q_OBJECT
+
+friend class PluginListItem;
+
+public:
+ PluginListView(QWidget *_parent = 0, const char *_name = 0);
+ PluginListView(unsigned _min, QWidget *_parent = 0, const char *_name = 0);
+ PluginListView(unsigned _min, unsigned _max, QWidget *_parent = 0, const char *_name = 0);
+
+ virtual void clear();
+
+signals:
+ void stateChange(PluginListItem *, bool);
+
+private:
+ void stateChanged(PluginListItem *, bool);
+
+ bool hasMaximum;
+ unsigned max, min;
+ unsigned count;
+};
+
+class Plugins : public CModule
+{
+Q_OBJECT
+public:
+ Plugins(QObject *_parent = 0);
+ virtual void save();
+ virtual void reopen();
+
+protected:
+ virtual void showEvent(QShowEvent *);
+
+private slots:
+ void stateChange(PluginListItem *, bool);
+
+private:
+ void addPlugin(const NoatunLibraryInfo &);
+ void removePlugin(const NoatunLibraryInfo &);
+ PluginListItem *findItem(const NoatunLibraryInfo &) const;
+
+ QStringList mAdded, mDeleted;
+ PluginListView *interfaceList, *playlistList, *otherList, *visList;
+
+ bool shown;
+};
+
+#endif
diff --git a/noatun/library/pref.cpp b/noatun/library/pref.cpp
new file mode 100644
index 00000000..98e71645
--- /dev/null
+++ b/noatun/library/pref.cpp
@@ -0,0 +1,95 @@
+#include "pref.h"
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <qlayout.h>
+//#include <qlabel.h>
+#include "cmodule.h"
+
+NoatunPreferences::NoatunPreferences(QWidget *parent)
+ : KDialogBase(TreeList, i18n("Preferences - Noatun"),
+ Ok|Apply|Cancel|Help, Ok, parent, "NoatunPreferences", false, true)
+{
+ resize(640, 480); // KDE is required to support 800x600 min.
+ setShowIconsInTreeList(true);
+ setRootIsDecorated(false);
+}
+
+void NoatunPreferences::slotOk()
+{
+ slotApply();
+ hide();
+}
+
+void NoatunPreferences::show()
+{
+ for (CModule *i=mModules.first(); i != 0; i=mModules.next())
+ i->reopen();
+ KDialogBase::show();
+}
+
+void NoatunPreferences::show(CModule *page)
+{
+ int index = pageIndex( static_cast<QWidget *>(page->parent()) );
+ if (index != -1)
+ showPage(index);
+ show();
+}
+
+void NoatunPreferences::slotApply()
+{
+ for (CModule *i=mModules.first(); i != 0; i=mModules.next())
+ i->save();
+}
+
+void NoatunPreferences::add(CModule *page)
+{
+ mModules.append(page);
+}
+
+void NoatunPreferences::remove(CModule *page)
+{
+ mModules.removeRef(page);
+}
+
+CModule::CModule(const QString &name, const QString &description, const QString &icon, QObject *owner)
+ : QWidget(napp->preferencesBox()->addPage(name, description, KGlobal::iconLoader()->loadIcon(
+ icon, KIcon::Small,0, KIcon::DefaultState,0, true)))
+{
+ if (owner)
+ connect(owner, SIGNAL(destroyed()), SLOT(ownerDeleted()));
+
+ //kdDebug(66666) << k_funcinfo << "name = " << name << endl;
+
+ napp->preferencesBox()->add(this);
+
+ QFrame *page=static_cast<QFrame*>(parent());
+ (new QHBoxLayout(page))->addWidget(this);
+}
+
+CModule::~CModule()
+{
+ //kdDebug(66666) << k_funcinfo << endl;
+#if QT_VERSION < 0x030102 && KDE_VERSION < KDE_MAKE_VERSION( 3, 1, 90 )
+ // Due to a bug in Qt 3.1 and 3.1.1 no close events are sent to hidden
+ // widgets, causing the KJanusWidget to crash. This workaround is
+ // rather intrusive and should be used only in the affected versions
+ // to avoid hard to track bugs in the future. KDE HEAD (to become 3.2)
+ // has a workaround for this problem, and additionally it's fixed in
+ // Qt 3.1.2.
+ napp->sendPostedEvents();
+#endif
+
+ napp->preferencesBox()->remove(this);
+}
+
+void CModule::ownerDeleted()
+{
+ QObject *p=parent();
+ delete this;
+ p->deleteLater();
+}
+
+#include "pref.moc"
diff --git a/noatun/library/scrollinglabel.cpp b/noatun/library/scrollinglabel.cpp
new file mode 100644
index 00000000..d7bdd643
--- /dev/null
+++ b/noatun/library/scrollinglabel.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2000 Rik Hemsley (rikkus) <rik@kde.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <noatun/scrollinglabel.h>
+
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qtimer.h>
+#include <qtooltip.h>
+
+class ScrollingLabel::Private
+{
+ public:
+
+ Private()
+ : scrollSize (0),
+ pos (0),
+ add (false),
+ scroll (true)
+ {
+ }
+
+ // Order dependency.
+ QString text;
+ QString originalText;
+ QPixmap buf;
+ QTimer scrollTimer;
+ QTimer resetTimer;
+ int scrollSize;
+ int pos;
+ bool add;
+ bool scroll;
+ // End order dependency.
+};
+
+ScrollingLabel::ScrollingLabel
+(
+ const QString & initialText,
+ QWidget * parent,
+ const char * name
+)
+ : QWidget(parent, name)
+{
+ d = new Private;
+
+ connect(&d->scrollTimer, SIGNAL(timeout()), this, SLOT(scroll()));
+ connect(&d->resetTimer, SIGNAL(timeout()), this, SLOT(restoreText()));
+
+ setText(initialText);
+}
+
+ScrollingLabel::~ScrollingLabel()
+{
+ delete d;
+}
+
+ void
+ScrollingLabel::setText(const QString & t, int time)
+{
+ d->resetTimer.stop();
+
+ if (-1 != time)
+ {
+ restoreText();
+ d->originalText = d->text;
+ d->text = t;
+ d->resetTimer.start(time, true);
+ _update();
+ }
+ else
+ {
+ d->text = d->originalText = t;
+ _update();
+ }
+
+ QToolTip::remove(this);
+ QToolTip::add(this, d->text);
+}
+
+ void
+ScrollingLabel::restoreText()
+{
+ d->text = d->originalText;
+ _update();
+}
+
+ void
+ScrollingLabel::_update()
+{
+ d->scrollTimer.stop();
+
+ d->pos = 0;
+ d->add = false;
+
+ int w = fontMetrics().width(d->text);
+ int h = fontMetrics().height();
+
+ setFixedHeight(h);
+
+ d->scrollSize = QMAX(0, w - width());
+
+ d->buf.resize(w, h);
+ d->buf.fill(colorGroup().background());
+
+ QPainter p(&d->buf);
+ p.setFont(font());
+ p.drawText(0, fontMetrics().ascent(), d->text);
+
+ if (d->scroll && (d->scrollSize > 0))
+ d->scrollTimer.start(100, true);
+
+ repaint(false);
+}
+
+ void
+ScrollingLabel::paintEvent(QPaintEvent *)
+{
+ bitBlt
+ (this, 0, 0, &d->buf, d->pos, 0, d->pos + width(), height(), Qt::CopyROP);
+}
+
+ void
+ScrollingLabel::resizeEvent(QResizeEvent *)
+{
+ _update();
+}
+
+ void
+ScrollingLabel::scroll()
+{
+ d->scrollTimer.stop();
+
+ repaint(false);
+
+ int scrollTime = 100;
+
+ if (d->pos == d->scrollSize || d->pos == 0)
+ {
+ d->add = !d->add;
+ scrollTime = 800;
+ }
+
+ d->pos += (d->add ? 1 : -1);
+
+ if (d->scroll)
+ d->scrollTimer.start(scrollTime, true);
+}
+
+ QSize
+ScrollingLabel::sizeHint() const
+{
+ return fontMetrics().boundingRect(d->text).size();
+}
+
+ QSize
+ScrollingLabel::minimumSizeHint() const
+{
+ return QSize(0, fontMetrics().height());
+}
+
+ QString
+ScrollingLabel::text() const
+{
+ return d->text;
+}
+
+ void
+ScrollingLabel::setScroll(bool b)
+{
+ d->scroll = b;
+ _update();
+}
+
+#include "scrollinglabel.moc"
+
+// vim:ts=2:sw=2:tw=78:noet
+
diff --git a/noatun/library/spline.cpp b/noatun/library/spline.cpp
new file mode 100644
index 00000000..d09c12dc
--- /dev/null
+++ b/noatun/library/spline.cpp
@@ -0,0 +1,194 @@
+/*
+ Copyright (C) 1998 Jrgen Hochwald <juergen.hochwald@privat.kkf.net>
+ Copyright (C) 2003 Charles Samuels <charles@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this library; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+
+
+#include <stdio.h>
+#include "spline.h"
+
+const bool Spline::natural=false;
+
+Spline::Spline()
+{
+ yp1=0.0;
+ ypn=0.0;
+ mRecalc = true;
+}
+
+Spline::Spline(const Spline &copy)
+{
+ operator=(copy);
+}
+
+Spline &Spline::operator =(const Spline &copy)
+{
+ mPoints = copy.mPoints;
+ mRecalc = copy.mRecalc;
+ yp1 = copy.yp1;
+ ypn = copy.ypn;
+ return *this;
+}
+
+Spline::~Spline()
+{
+
+}
+
+void Spline::add(double x, double y)
+{
+ Group g = { x, y, 0.0 };
+ mPoints.push_back(g);
+ mRecalc=true;
+}
+
+std::vector<double> Spline::points(int count) const
+{
+ std::vector<double> points;
+ if (count == numPoints())
+ {
+ for (int i=0; i < count; ++i)
+ {
+ points.push_back(mPoints[i].y);
+ }
+ }
+ else
+ {
+ double min = mPoints[0].x;
+ double max = mPoints[numPoints()-1].x;
+ double dcount = double(count);
+
+ for (int i=0; i<count; ++i)
+ {
+ points.push_back(spline( (max-min)/dcount*i + min));
+ }
+ }
+
+ return points;
+}
+
+void Spline::calcSpline()
+{
+ const int np=numPoints();
+ double *u = new double[np];
+
+ if (natural)
+ {
+ mPoints[0].y2 = u[0] = 0.0;
+ }
+ else
+ {
+ mPoints[0].y2 = -0.5;
+ u[0] =
+ (3.0/(mPoints[1].x-mPoints[0].x))*((mPoints[1].y-mPoints[0].y)
+ / (mPoints[1].x-mPoints[0].x)-yp1);
+ }
+
+ double sig,p,qn,un;
+ for (int i=1; i<=np-2; i++)
+ {
+ sig=(mPoints[i].x-mPoints[i-1].x) / (mPoints[i+1].x-mPoints[i-1].x);
+ p = sig*mPoints[i-1].y2+2.0;
+ mPoints[i].y2 = (sig-1.0)/p;
+ u[i] =
+ (mPoints[i+1].y - mPoints[i].y) / (mPoints[i+1].x-mPoints[i].x)
+ - (mPoints[i].y - mPoints[i-1].y) / (mPoints[i].x - mPoints[i-1].x);
+ u[i] = (6.0*u[i] / (mPoints[i+1].x - mPoints[i-1].x) - sig *u[i-1])/p;
+ }
+
+ if (natural)
+ {
+ qn = un = 0.0;
+ }
+ else
+ {
+ qn = 0.5;
+ un =
+ (3.0 / (mPoints[np-1].x - mPoints[np-2].x))
+ * (ypn - (mPoints[np-1].y - mPoints[np-2].y)
+ / (mPoints[np-1].x - mPoints[np-2].x));
+ }
+ mPoints[np-1].y2 = (un - qn * u[np-2]) / (qn*mPoints[np-2].y2 +1.0 );
+
+ for (int i=np-2; i>=0; i--)
+ {
+ mPoints[i].y2 = mPoints[i].y2 * mPoints[i+1].y2+u[i];
+ }
+ mRecalc = false;
+ delete [] u;
+}
+
+double Spline::spline(double xarg) const
+{
+ if (numPoints()==0) return 0.0;
+ if (numPoints()==1) return mPoints[0].y;
+
+ if (mRecalc) calcSpline();
+
+ int klo=0;
+ int khi=numPoints()-1;
+ int k;
+ while (khi-klo > 1)
+ {
+ k = khi+klo;
+ if (k % 2)
+ k = (k+1) / 2;
+ else
+ k = k/2;
+
+ if (mPoints[k].x > xarg) khi=k;
+ else klo=k;
+ }
+
+ double h = mPoints[khi].x - mPoints[klo].x;
+ if (h==0)
+ {
+ // failed
+ return 0.0;
+ }
+
+ double a = (mPoints[khi].x - xarg) / h;
+ double b = (xarg - mPoints[klo].x) / h;
+ return
+ a * mPoints[klo].y + b*mPoints[khi].y
+ + ((a*a*a-a) * mPoints[klo].y2
+ + (b*b*b-b) * mPoints[khi].y2) * (h*h) / 6.0;
+}
+
+double Spline::x(int num) const
+{
+ if (numPoints()<num) return 0.0;
+ return mPoints[num].x;
+}
+
+double Spline::y(int num) const
+{
+ if (numPoints()<num) return 0.0;
+ return mPoints[num].y;
+}
+
+void Spline::clear()
+{
+ mPoints.resize(0);
+ ypn=0.0;
+ yp1=0.0;
+ mRecalc=true;
+}
+
+
diff --git a/noatun/library/spline.h b/noatun/library/spline.h
new file mode 100644
index 00000000..db1bb03b
--- /dev/null
+++ b/noatun/library/spline.h
@@ -0,0 +1,74 @@
+/*
+Copyright (C) 1998 Jrgen Hochwald <juergen.hochwald@privat.kkf.net>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+
+#ifndef SPLINE_H
+#define SPLINE_H
+
+#include <stdlib.h>
+#include <vector>
+
+class Spline
+{
+ struct Group
+ {
+ double x, y, y2;
+ };
+
+ std::vector<Spline::Group> mPoints;
+ bool mRecalc;
+ double yp1;
+ double ypn;
+
+ // stupid AIX[?] compiler won't let me give it a value here
+ static const bool natural;
+
+public:
+ Spline();
+ Spline(const Spline &copy);
+
+ Spline &operator =(const Spline &copy);
+ ~Spline();
+
+ /**
+ * if the curve had @p count points, return them
+ **/
+ std::vector<double> points(int count) const;
+
+ void add(double x, double y);
+ double spline(double xarg) const;
+ double operator[] (double xarg) const { return spline(xarg); }
+ int numPoints() const { return mPoints.size(); }
+ double x(int num) const;
+ double y(int num) const;
+
+ void clear();
+
+private:
+ void calcSpline() const
+ {
+ const_cast<Spline*>(this)->calcSpline();
+ }
+ void calcSpline();
+
+};
+
+#endif
+
diff --git a/noatun/library/stereobuttonaction.cpp b/noatun/library/stereobuttonaction.cpp
new file mode 100644
index 00000000..bfb688f7
--- /dev/null
+++ b/noatun/library/stereobuttonaction.cpp
@@ -0,0 +1,47 @@
+#include "stereobuttonaction.h"
+
+namespace NoatunStdAction
+{
+
+StereoButtonAction::StereoButtonAction(const QString& text, int accel, QObject* parent, const char* name )
+ : KAction(text, accel, parent, name)
+{}
+
+StereoButtonAction::StereoButtonAction(const QString& text, int accel, const QObject* receiver, const char* slot, QObject* parent, const char* name )
+ : KAction(text, accel, receiver, slot, parent, name)
+{}
+
+StereoButtonAction::StereoButtonAction(const QString& text, const QIconSet& pix, int accel, QObject* parent, const char* name )
+ : KAction(text, pix, accel, parent, name)
+{}
+
+StereoButtonAction::StereoButtonAction(const QString& text, const QString& pix, int accel, QObject* parent, const char* name )
+ : KAction(text, pix, accel, parent, name)
+{}
+
+StereoButtonAction::StereoButtonAction(const QString& text, const QIconSet& pix, int accel, const QObject* receiver, const char* slot, QObject* parent, const char* name )
+ : KAction(text, pix, accel, receiver, slot, parent, name)
+{}
+
+StereoButtonAction::StereoButtonAction(const QString& text, const QString& pix, int accel, const QObject* receiver, const char* slot, QObject* parent, const char* name )
+ : KAction(text, pix, accel, receiver, slot, parent, name)
+{}
+
+void StereoButtonAction::disable(void)
+{
+ setEnabled(false);
+}
+
+void StereoButtonAction::enable(void)
+{
+ setEnabled(true);
+}
+
+void StereoButtonAction::toggleEnabled(void)
+{
+ setEnabled(!isEnabled());
+}
+
+}
+
+#include "stereobuttonaction.moc"
diff --git a/noatun/library/titleproxy.cpp b/noatun/library/titleproxy.cpp
new file mode 100644
index 00000000..7515a35a
--- /dev/null
+++ b/noatun/library/titleproxy.cpp
@@ -0,0 +1,376 @@
+/***************************************************************************
+ Proxy.cpp - description
+ -------------------
+begin : Nov 20 14:35:18 CEST 2003
+copyright : (C) 2003 by Mark Kretschmann
+email : markey@web.de
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "titleproxy.h"
+
+#include <kdebug.h>
+#include <kprotocolmanager.h>
+#include <kmdcodec.h>
+
+#include <qstring.h>
+#include <qtimer.h>
+#include "noatun/app.h"
+
+using namespace TitleProxy;
+
+static const uint MIN_PROXYPORT = 6700;
+static const uint MAX_PROXYPORT = 7777;
+static const int BUFSIZE = 32768;
+
+Proxy::Proxy( KURL url )
+ : QObject()
+ , m_url( url )
+ , m_initSuccess( true )
+ , m_metaInt( 0 )
+ , m_byteCount( 0 )
+ , m_metaLen( 0 )
+ , m_usedPort( 0 )
+ , m_pBuf( 0 )
+{
+ kdDebug(66666) << k_funcinfo << endl;
+
+ m_pBuf = new char[ BUFSIZE ];
+ // Don't try to get metdata for ogg streams (different protocol)
+ m_icyMode = url.path().endsWith( ".ogg" ) ? false : true;
+ // If no port is specified, use default shoutcast port
+ if ( m_url.port() < 1 )
+ m_url.setPort( 80 );
+
+ connect( &m_sockRemote, SIGNAL( error( int ) ), this, SLOT( connectError() ) );
+ connect( &m_sockRemote, SIGNAL( connected() ), this, SLOT( sendRequest() ) );
+ connect( &m_sockRemote, SIGNAL( readyRead() ), this, SLOT( readRemote() ) );
+
+ uint i = 0;
+ Server* server = 0;
+ for ( i = MIN_PROXYPORT; i <= MAX_PROXYPORT; i++ )
+ {
+ server = new Server( i, this );
+ kdDebug(66666) << k_funcinfo <<
+ "Trying to bind to port: " << i << endl;
+ if ( server->ok() ) // found a free port
+ break;
+ delete server;
+ }
+
+ if ( i > MAX_PROXYPORT )
+ {
+ kdWarning(66666) << k_funcinfo <<
+ "Unable to find a free local port. Aborting." << endl;
+ m_initSuccess = false;
+ return;
+ }
+ m_usedPort = i;
+ connect( server, SIGNAL( connected( int ) ), this, SLOT( accept( int ) ) );
+}
+
+
+Proxy::~Proxy()
+{
+ kdDebug(66666) << k_funcinfo << endl;
+ delete[] m_pBuf;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC
+//////////////////////////////////////////////////////////////////////////////////////////
+
+KURL Proxy::proxyUrl()
+{
+ if ( m_initSuccess )
+ {
+ KURL url;
+ url.setPort( m_usedPort );
+ url.setHost( "localhost" );
+ url.setProtocol( "http" );
+ return url;
+ }
+ else
+ return m_url;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// PRIVATE SLOTS
+//////////////////////////////////////////////////////////////////////////////////////////
+
+void Proxy::accept( int socket ) //SLOT
+{
+ //kdDebug(66666) << "BEGIN " << k_funcinfo << endl;
+ m_sockProxy.setSocket( socket );
+ m_sockProxy.waitForMore( KProtocolManager::readTimeout() * 1000 );
+ connectToHost();
+ //kdDebug(66666) << "END " << k_funcinfo << endl;
+}
+
+
+void Proxy::connectToHost() //SLOT
+{
+ //kdDebug(66666) << "BEGIN " << k_funcinfo << endl;
+
+ { //initialisations
+ m_connectSuccess = false;
+ m_headerFinished = false;
+ m_headerStr = "";
+ }
+
+ { //connect to server
+ QTimer::singleShot( KProtocolManager::connectTimeout() * 1000,
+ this, SLOT( connectError() ) );
+
+ kdDebug(66666) << k_funcinfo << "Connecting to " <<
+ m_url.host() << ":" << m_url.port() << endl;
+
+ m_sockRemote.connectToHost( m_url.host(), m_url.port() );
+ }
+
+ //kdDebug(66666) << "END " << k_funcinfo << endl;
+}
+
+
+void Proxy::sendRequest() //SLOT
+{
+ //kdDebug(66666) << "BEGIN " << k_funcinfo << endl;
+
+ QCString username = m_url.user().utf8();
+ QCString password = m_url.pass().utf8();
+ QCString authString = KCodecs::base64Encode( username + ":" + password );
+ bool auth = !( username.isEmpty() && password.isEmpty() );
+
+ QString request = QString( "GET %1 HTTP/1.0\r\n"
+ "Host: %2\r\n"
+ "User-Agent: Noatun/%5\r\n"
+ "%3"
+ "%4"
+ "\r\n" )
+ .arg( m_url.path( -1 ).isEmpty() ? "/" : m_url.path( -1 ) )
+ .arg( m_url.host() )
+ .arg( m_icyMode ? QString( "Icy-MetaData:1\r\n" ) : QString::null )
+ .arg( auth ? QString( "Authorization: Basic " ).append( authString ) : QString::null )
+ .arg( NOATUN_VERSION );
+
+ m_sockRemote.writeBlock( request.latin1(), request.length() );
+
+ //kdDebug(66666) << "END " << k_funcinfo << endl;
+}
+
+
+void Proxy::readRemote() //SLOT
+{
+ m_connectSuccess = true;
+ Q_LONG index = 0;
+ Q_LONG bytesWrite = 0;
+ Q_LONG bytesRead = m_sockRemote.readBlock( m_pBuf, BUFSIZE );
+ if ( bytesRead == -1 )
+ {
+ kdDebug(66666) << k_funcinfo << "Could not read remote data from socket, aborting" << endl;
+ m_sockRemote.close();
+ emit proxyError();
+ return;
+ }
+
+ if ( !m_headerFinished )
+ {
+ if ( !processHeader( index, bytesRead ) )
+ return;
+ }
+
+ //This is the main loop which processes the stream data
+ while ( index < bytesRead )
+ {
+ if ( m_icyMode && m_metaInt && ( m_byteCount == m_metaInt ) )
+ {
+ m_byteCount = 0;
+ m_metaLen = m_pBuf[ index++ ] << 4;
+ }
+ else if ( m_icyMode && m_metaLen )
+ {
+ m_metaData.append( m_pBuf[ index++ ] );
+ --m_metaLen;
+
+ if ( !m_metaLen )
+ {
+ transmitData( m_metaData );
+ m_metaData = "";
+ }
+ }
+ else
+ {
+ bytesWrite = bytesRead - index;
+
+ if ( m_icyMode && bytesWrite > m_metaInt - m_byteCount )
+ bytesWrite = m_metaInt - m_byteCount;
+ bytesWrite = m_sockProxy.writeBlock( m_pBuf + index, bytesWrite );
+
+ if ( bytesWrite == -1 )
+ {
+ error();
+ return;
+ }
+
+ index += bytesWrite;
+ m_byteCount += bytesWrite;
+ }
+ }
+}
+
+
+void Proxy::connectError() //SLOT
+{
+ if ( !m_connectSuccess )
+ {
+ kdWarning(66666) <<
+ "TitleProxy error: Unable to connect to this stream " <<
+ "server. Can't play the stream!" << endl;
+
+ emit proxyError();
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// PRIVATE
+//////////////////////////////////////////////////////////////////////////////////////////
+
+bool Proxy::processHeader( Q_LONG &index, Q_LONG bytesRead )
+{
+ while ( index < bytesRead )
+ {
+ m_headerStr.append( m_pBuf[ index++ ] );
+ if ( m_headerStr.endsWith( "\r\n\r\n" ) )
+ {
+ /*kdDebug(66666) << k_funcinfo <<
+ "Got shoutcast header: '" << m_headerStr << "'" << endl;*/
+
+ // Handle redirection
+ QString loc( "Location: " );
+ int index = m_headerStr.find( loc );
+ if ( index >= 0 )
+ {
+ int start = index + loc.length();
+ int end = m_headerStr.find( "\n", index );
+ m_url = m_headerStr.mid( start, end - start - 1 );
+
+ kdDebug(66666) << k_funcinfo <<
+ "Stream redirected to: " << m_url << endl;
+
+ m_sockRemote.close();
+ connectToHost();
+ return false;
+ }
+
+
+ if (m_headerStr.startsWith("ICY"))
+ {
+ m_metaInt = m_headerStr.section( "icy-metaint:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 ).toInt();
+ m_bitRate = m_headerStr.section( "icy-br:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamName = m_headerStr.section( "icy-name:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamGenre = m_headerStr.section( "icy-genre:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamUrl = m_headerStr.section( "icy-url:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ }
+ else // not ShoutCast
+ {
+ QString serverName = m_headerStr.section( "Server:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ kdDebug(66666) << k_funcinfo << "Server name: " << serverName << endl;
+
+ if (serverName == "Icecast")
+ {
+ m_metaInt = 0;
+ m_streamName = m_headerStr.section( "ice-name:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamGenre = m_headerStr.section( "ice-genre:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamUrl = m_headerStr.section( "ice-url:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ }
+ else if (serverName.startsWith("icecast/1."))
+ {
+ m_metaInt = 0;
+ m_bitRate = m_headerStr.section( "x-audiocast-bitrate:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamName = m_headerStr.section( "x-audiocast-name:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamGenre = m_headerStr.section( "x-audiocast-genre:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamUrl = m_headerStr.section( "x-audiocast-url:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ }
+ }
+
+ if ( m_streamUrl.startsWith( "www.", true ) )
+ m_streamUrl.prepend( "http://" );
+
+ m_sockProxy.writeBlock( m_headerStr.latin1(), m_headerStr.length() );
+ m_headerFinished = true;
+
+ if ( m_icyMode && !m_metaInt )
+ {
+ error();
+ return false;
+ }
+
+ connect( &m_sockRemote, SIGNAL( connectionClosed() ),
+ this, SLOT( connectError() ) );
+ return true;
+ }
+ }
+ return false;
+}
+
+
+void Proxy::transmitData( const QString &data )
+{
+ /*kdDebug(66666) << k_funcinfo <<
+ " received new metadata: '" << data << "'" << endl;*/
+
+ //prevent metadata spam by ignoring repeated identical data
+ //(some servers repeat it every 10 seconds)
+ if ( data == m_lastMetadata )
+ return;
+
+ m_lastMetadata = data;
+
+ emit metaData(
+ m_streamName, m_streamGenre, m_streamUrl, m_bitRate,
+ extractStr(data, QString::fromLatin1("StreamTitle")),
+ extractStr(data, QString::fromLatin1("StreamUrl")));
+}
+
+
+void Proxy::error()
+{
+ kdDebug(66666) <<
+ "TitleProxy error: Stream does not support shoutcast metadata. " <<
+ "Restarting in non-metadata mode." << endl;
+
+ m_sockRemote.close();
+ m_icyMode = false;
+
+ //open stream again, but this time without metadata, please
+ connectToHost();
+}
+
+
+QString Proxy::extractStr( const QString &str, const QString &key )
+{
+ int index = str.find( key, 0, true );
+ if ( index == -1 )
+ {
+ return QString::null;
+ }
+ else
+ {
+ index = str.find( "'", index ) + 1;
+ int indexEnd = str.find( "'", index );
+ return str.mid( index, indexEnd - index );
+ }
+}
+
+#include "titleproxy.moc"
diff --git a/noatun/library/titleproxy.h b/noatun/library/titleproxy.h
new file mode 100644
index 00000000..058943d6
--- /dev/null
+++ b/noatun/library/titleproxy.h
@@ -0,0 +1,129 @@
+/***************************************************************************
+ titleproxy.h - description
+ -------------------
+begin : Nov 20 14:35:18 CEST 2003
+copyright : (C) 2003 by Mark Kretschmann
+email :
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 AMAROK_TITLEPROXY_H
+#define AMAROK_TITLEPROXY_H
+
+#include <kurl.h> //stack allocated
+
+#include <qobject.h>
+#include <qserversocket.h> //baseclass
+#include <qsocket.h> //stack allocated
+
+class QString;
+
+namespace TitleProxy
+{
+ /**
+ * Proxy Concept:
+ * 1. Connect to streamserver
+ * 2. Listen on localhost, let aRts connect to proxyserver
+ * 3. Write GET request to streamserver, containing Icy-MetaData:1 token
+ * 4. Read MetaInt token from streamserver (==metadata offset)
+ *
+ * 5. Read stream data (mp3 + metadata) from streamserver
+ * 6. Filter out metadata, send to app
+ * 7. Write mp3 data to proxyserver
+ * 8. Goto 5
+ *
+ * Some info on the shoutcast metadata protocol can be found at:
+ * @see http://www.smackfu.com/stuff/programming/shoutcast.html
+ *
+ * @short A proxy server for extracting metadata from Shoutcast streams.
+ */
+
+ class Proxy : public QObject
+ {
+ Q_OBJECT
+ public:
+ Proxy( KURL url );
+ ~Proxy();
+
+ bool initSuccess() { return m_initSuccess; }
+ KURL proxyUrl();
+
+ signals:
+ void metaData(
+ const QString &streamName,
+ const QString &streamGenre,
+ const QString &streamUrl,
+ const QString &streamBitrate,
+ const QString &trackTitle,
+ const QString &trackUrl);
+ void proxyError();
+
+ private slots:
+ void accept( int socket );
+ void connectToHost();
+ void sendRequest();
+ void readRemote();
+ void connectError();
+
+ private:
+ bool processHeader( Q_LONG &index, Q_LONG bytesRead );
+ void transmitData( const QString &data );
+ void error();
+ QString extractStr( const QString &str, const QString &key );
+
+ //ATTRIBUTES:
+ KURL m_url;
+ int m_streamingMode;
+ bool m_initSuccess;
+ bool m_connectSuccess;
+
+ int m_metaInt;
+ QString m_bitRate;
+ int m_byteCount;
+ uint m_metaLen;
+
+ QString m_metaData;
+ bool m_headerFinished;
+ QString m_headerStr;
+ int m_usedPort;
+ QString m_lastMetadata;
+ bool m_icyMode;
+
+ QString m_streamName;
+ QString m_streamGenre;
+ QString m_streamUrl;
+
+ char *m_pBuf;
+
+ QSocket m_sockRemote;
+ QSocket m_sockProxy;
+ };
+
+
+ class Server : public QServerSocket
+ {
+ Q_OBJECT
+
+ public:
+ Server( Q_UINT16 port, QObject* parent )
+ : QServerSocket( port, 1, parent, "TitleProxyServer" ) {};
+
+ signals:
+ void connected( int socket );
+
+ private:
+ void newConnection( int socket ) { emit connected( socket ); }
+ };
+
+} //namespace TitleProxy
+
+#endif /*AMAROK_TITLEPROXY_H*/
+
diff --git a/noatun/library/vequalizer.cpp b/noatun/library/vequalizer.cpp
new file mode 100644
index 00000000..4b131e8a
--- /dev/null
+++ b/noatun/library/vequalizer.cpp
@@ -0,0 +1,936 @@
+/*
+ * Copyright (c) 2003 Charles Samuels <charles@kde.org>
+ *
+ * This file is hereby licensed under the GNU General Public License version
+ * 2 or later at your option.
+ *
+ * This file is licensed under the Qt Public License version 1 with the
+ * condition that the licensed will be governed under the Laws of California
+ * (USA) instead of Norway. Disputes will be settled in Santa Clara county
+ * courts.
+ *
+ * This file is licensed under the following additional license. Be aware
+ * that it is identical to the BSD license, except for the added clause 3:
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. By integrating this software into any other software codebase, you waive
+ all rights to any patents associated with the stated codebase.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "vequalizer.h"
+#include "engine.h"
+#include "spline.h"
+#include "ksaver.h"
+
+#include <noatunarts.h>
+#include <app.h>
+#include <player.h>
+
+#include <common.h>
+#include <dynamicrequest.h>
+#include <artsflow.h>
+#include <soundserver.h>
+
+#include <ktempfile.h>
+#include <kio/netaccess.h>
+#include <kstandarddirs.h>
+#include <kconfig.h>
+#include <klocale.h>
+
+#include <qdom.h>
+#include <qtextstream.h>
+#include <qfile.h>
+
+#include <math.h>
+
+#define EQ (napp->vequalizer())
+#define EQBACK (napp->player()->engine()->equalizer())
+
+using std::vector;
+
+static const double splineTension = 4.0;
+
+VBandsInterface::VBandsInterface()
+{
+
+}
+
+VBandsInterface::~VBandsInterface()
+{
+
+}
+
+
+VBand VBandsInterface::operator [] (int num)
+{
+ return band(num);
+}
+
+struct VBand::Private
+{
+ int refs;
+
+ int index;
+ int start, end;
+ VBandsInterface *bands;
+};
+
+VBand::VBand(VBandsInterface *bands, int index, int start, int end)
+{
+ d = new Private;
+ d->refs=1;
+ d->index = index;
+ d->start = start;
+ d->end = end;
+ d->bands = bands;
+}
+
+VBand::~VBand()
+{
+ if (--d->refs == 0)
+ {
+ delete d;
+ }
+}
+
+VBand::VBand(const VBand &copy)
+{
+ d=0;
+ operator=(copy);
+}
+
+VBand & VBand::operator =(const VBand &copy)
+{
+ if (d && --d->refs == 0)
+ {
+ delete d;
+ }
+
+ d= copy.d;
+ d->refs++;
+ return *this;
+}
+
+int VBand::level() const
+{
+ return d->bands->level(d->index);
+}
+
+void VBand::setLevel(int l)
+{
+ d->bands->setLevel(d->index, l);
+}
+
+int VBand::start() const
+{
+ return d->start;
+}
+
+int VBand::end() const
+{
+ return d->end;
+}
+
+int VBand::center() const
+{
+ return (d->start + d->end)/2;
+}
+
+static QString formatFreq(int f, bool withHz)
+{
+ QString format;
+ if (f<991)
+ format=QString::number(f);
+ else
+ format=QString::number((int)((f+500)/1000.0))+"k";
+
+ if (withHz)
+ format+="Hz";
+
+ return format;
+}
+
+QString VBand::formatStart(bool withHz) const
+{
+ return formatFreq(d->start, withHz);
+}
+
+QString VBand::formatEnd(bool withHz) const
+{
+ return formatFreq(d->end, withHz);
+}
+
+QString VBand::format(bool withHz) const
+{
+ return formatFreq(center(), withHz);
+}
+
+
+
+
+struct VInterpolation::Private
+{
+ int bands;
+ Spline spline;
+
+};
+
+VInterpolation::VInterpolation(int bands)
+{
+ d = new Private;
+ d->bands = bands;
+}
+
+VInterpolation::~VInterpolation()
+{
+ delete d;
+}
+
+int VInterpolation::bands() const
+{
+ return d->bands;
+}
+
+void VInterpolation::getFrequencies(int num, int *low, int *high) const
+{
+ QValueList<int> fs = VEqualizer::frequencies(bands());
+
+ if (num == 0) *low = 1;
+ else *low = fs[num-1]+1;
+ *high=fs[num];
+}
+
+
+VBand VInterpolation::band(int num)
+{
+ int low, high;
+ getFrequencies(num, &low, &high);
+ return VBand(this, num, low, high);
+}
+
+int VInterpolation::level(int index) const
+{
+ const_cast<VInterpolation*>(this)->refresh();
+ double x = onSpline(index);
+
+ return int(d->spline[x*splineTension]);
+}
+
+void VInterpolation::setLevel(int index, int level)
+{
+ refresh();
+
+ double numbands = double(bands());
+ Spline spline;
+
+ for (int i=0; i < numbands; ++i)
+ {
+ VBand b = band(i);
+ spline.add(i*splineTension, double(index == i ? level : b.level()));
+ }
+
+ int realbands = EQ->bands();
+ QValueList<int> values;
+ for (int i=0; i < realbands; ++i)
+ {
+ // i
+ // realbands numbands
+ double x = double(i)*numbands/double(realbands)*splineTension;
+ double value = spline[x];
+ values.append(int(value));
+ }
+ EQ->setLevels(values);
+}
+
+double VInterpolation::onSpline(int bandNum) const
+{
+ double maxReal(EQ->bands()); // 2
+ double maxInter(bands()); // 4
+ double offset(bandNum); // 4
+
+ return maxReal/maxInter*offset;
+}
+
+void VInterpolation::refresh()
+{
+ d->spline.clear();
+
+ VEqualizer *eq = EQ;
+
+ for (int i=0; i < eq->bands(); ++i)
+ {
+ VBand band = eq->band(i);
+ d->spline.add(double(i*splineTension), double(band.level()));
+ }
+
+}
+
+struct VEqualizer::Private
+{
+ struct BandInfo
+ {
+ int level;
+ int start;
+ int end;
+ };
+
+ std::vector<BandInfo> bands;
+ int preamp;
+};
+
+/* rate 4
+27 54 0-108 108
+81 163 109-217 108
+243 514 218-810 269
+729 1621 811-2431 1620
+2187 4661 2432-7290 4858
+6561 13645 7291+ 12708
+*/
+VEqualizer::VEqualizer()
+{
+ d = new Private;
+ d->preamp=1;
+ setBands(6, false);
+}
+
+void VEqualizer::init()
+{
+ KURL url;
+ url.setPath(kapp->dirs()->localkdedir()+"/share/apps/noatun/equalizer");
+ if(!load(url))
+ {
+ setPreamp(0);
+ disable();
+ }
+ else
+ {
+ KConfig *config=kapp->config();
+ config->setGroup("Equalizer");
+ setEnabled(config->readBoolEntry("enabled", false));
+ }
+}
+
+
+VEqualizer::~VEqualizer()
+{
+ KURL url;
+ url.setPath(kapp->dirs()->localkdedir()+"/share/apps/noatun/equalizer");
+ save(url, "auto");
+
+ delete d;
+}
+
+int VEqualizer::start()
+{
+ return 20;
+}
+
+int VEqualizer::end()
+{
+ return 20000;
+}
+
+int VEqualizer::maxBands() const
+{
+ return 14;
+}
+
+int VEqualizer::minBands() const
+{
+ return 2;
+}
+
+
+QValueList<int> VEqualizer::frequencies(int _num)
+{
+#if 0
+ QValueList<int> fs;
+ fs += 108;
+ fs += 217;
+ fs += 810;
+ fs += 2431;
+ fs += 7290;
+ fs += 19999;
+ return fs;
+
+#else
+
+
+
+ // we're looking for
+ // ?^_num = end()-start()
+ // so log[?] (end()-start()) = _num
+ // log(end()-start()) / log(?) = _num
+ // log(end()-start()) = _num * log(?)
+ // log(end()-start()) / _num = log(?)
+ // ? = 10^(log(end()-start()) / _num)
+
+ double num = double(_num);
+ double vstart = double(start());
+ double vend = double(end());
+ const double base = ::pow(10.0, ::log10(vend-vstart)/num);
+
+ QValueList<int> fs;
+
+ for (double i=1.0; i <= num; i++)
+ {
+ double f = ::pow(base, i);
+ f += vstart;
+ fs.append(int(f));
+ }
+
+ return fs;
+#endif
+}
+
+void VEqualizer::setBands(int num, bool interpolate)
+{
+ if (interpolate)
+ {
+ setBands(num);
+ return;
+ }
+
+ if (num > maxBands())
+ num=maxBands();
+ else if (num < minBands()) num = minBands();
+
+ if (num == bands()) return;
+
+ QValueList<int> fs = VEqualizer::frequencies(num);
+ std::vector<Private::BandInfo> modified;
+
+ int bstart=0;
+ for (QValueList<int>::Iterator i(fs.begin()); i != fs.end(); ++i)
+ {
+ Private::BandInfo info;
+ info.start = bstart+1;
+ info.level = 0;
+ info.end = *i;
+ bstart = info.end;
+
+ modified.push_back(info);
+ }
+ d->bands = modified;
+ update(true);
+
+ emit changedBands();
+ emit changed();
+}
+
+void VEqualizer::setPreamp(int preamp)
+{
+ d->preamp = preamp;
+ EQBACK->preamp(pow(2,float(preamp)/100.0));
+ emit changed();
+ emit preampChanged();
+ emit preampChanged(preamp);
+}
+
+
+void VEqualizer::setBands(int num)
+{
+ if (num == bands()) return;
+ VInterpolation ip(num);
+
+ std::vector<Private::BandInfo> modified;
+
+ for (int i=0; i < num; ++i)
+ {
+ Private::BandInfo info;
+ VBand b = ip[i];
+ info.level = b.level();
+ info.start = b.start();
+ info.end = b.end();
+
+ modified.push_back(info);
+ }
+ d->bands = modified;
+ update(true);
+
+ emit changedBands();
+ emit changed();
+}
+
+VBand VEqualizer::band(int num)
+{
+ return VBand(this, num, d->bands[num].start, d->bands[num].end);
+}
+
+int VEqualizer::bands() const
+{
+ return d->bands.size();
+}
+
+bool VEqualizer::isEnabled() const
+{
+ return bool(EQBACK->enabled());
+}
+
+int VEqualizer::preamp() const
+{
+ return d->preamp;
+}
+
+void VEqualizer::enable()
+{
+ setEnabled(true);
+}
+
+void VEqualizer::disable()
+{
+ setEnabled(false);
+}
+
+int VEqualizer::level(int index) const
+{
+ return d->bands[index].level;
+}
+
+void VEqualizer::setLevel(int index, int level)
+{
+ d->bands[index].level = level;
+ update();
+ emit changed();
+ emit modified();
+}
+
+void VEqualizer::setLevels(const QValueList<int> &levels)
+{
+ int index=0;
+ for (
+ QValueList<int>::ConstIterator i(levels.begin());
+ i != levels.end(); ++i
+ )
+ {
+ d->bands[index].level = *i;
+ index++;
+ }
+ update();
+ emit changed();
+ emit modified();
+}
+
+
+void VEqualizer::setEnabled(bool e)
+{
+ update(true); // just in case
+ EQBACK->enabled((long)e);
+ KConfig *config=kapp->config();
+ config->setGroup("Equalizer");
+ config->writeEntry("enabled", e);
+ config->sync();
+
+ emit enabled(e);
+ if (e)
+ emit enabled();
+ else
+ emit disabled();
+}
+
+void VEqualizer::update(bool full)
+{
+ std::vector<float> levels;
+ std::vector<float> mids;
+ std::vector<float> widths;
+
+ for (unsigned int i=0; i < d->bands.size(); ++i)
+ {
+ Private::BandInfo &info = d->bands[i];
+ levels.push_back(::pow(2.0, float(info.level)/50.0));
+ if (full)
+ {
+ int mid = info.start + info.end;
+ mids.push_back(float(mid)*0.5);
+ widths.push_back(float(info.end - info.start));
+ }
+ }
+ if (full)
+ EQBACK->set(levels, mids, widths);
+ else
+ EQBACK->levels(levels);
+}
+
+
+
+bool VEqualizer::save(const KURL &filename, const QString &friendly) const
+{
+ Noatun::KSaver saver(filename);
+ if(!saver.open()) return false;
+
+ saver.textStream() << toString(friendly);
+ saver.close();
+
+ return saver.close();
+}
+
+bool VEqualizer::load(const KURL &filename)
+{
+ QString dest;
+ if(KIO::NetAccess::download(filename, dest, 0L))
+ {
+ QFile file(dest);
+ if (!file.open(IO_ReadOnly))
+ return false;
+
+ QTextStream t(&file);
+ QString str = t.read();
+ fromString(str);
+ return true;
+ }
+ return false;
+}
+
+QString VEqualizer::toString(const QString &name) const
+{
+ QDomDocument doc("noatunequalizer");
+ doc.setContent(QString("<!DOCTYPE NoatunEqualizer><noatunequalizer/>"));
+ QDomElement docElem = doc.documentElement();
+
+ {
+ docElem.setAttribute("level", preamp());
+ docElem.setAttribute("name", name);
+ docElem.setAttribute("version", napp->version());
+ }
+
+ int bandc = bands();
+ for (int i=0; i < bandc; ++i)
+ {
+ VBand band = const_cast<VEqualizer*>(this)->operator[](i);
+ QDomElement elem = doc.createElement("band");
+ elem.setAttribute("start", band.start());
+ elem.setAttribute("end", band.end());
+ elem.setAttribute("level", band.level());
+
+ docElem.appendChild(elem);
+ }
+
+ return doc.toString();
+}
+
+bool VEqualizer::fromString(const QString &str)
+{
+ QDomDocument doc("noatunequalizer");
+ if (!doc.setContent(str))
+ return false;
+
+ QDomElement docElem = doc.documentElement();
+ if (docElem.tagName()!="noatunequalizer")
+ return false;
+
+ setPreamp(docElem.attribute("level", "0").toInt());
+
+ std::vector<Private::BandInfo> modified;
+ for (QDomNode n = docElem.firstChild(); !n.isNull(); n = n.nextSibling())
+ {
+ QDomElement e = n.toElement();
+ if(e.isNull()) continue;
+ if (e.tagName().lower() != "band") continue;
+
+ Private::BandInfo data;
+
+ data.level = e.attribute("level", "0").toInt();
+ data.start = e.attribute("start", "1").toInt();
+ data.end = e.attribute("end", "19999").toInt();
+
+ modified.push_back(data);
+ }
+ d->bands = modified;
+ update(true);
+
+ emit changedBands();
+ emit changed();
+ return true;
+}
+
+static QString makePresetFile()
+{
+ QString basedir=kapp->dirs()->localkdedir()+"/share/apps/noatun/eq.preset/";
+ // now append a filename that doesn't exist
+ KStandardDirs::makeDir(basedir);
+ QString fullpath;
+ int num=0;
+ do
+ {
+ if (num)
+ fullpath=basedir+"preset."+QString::number(num);
+ else
+ fullpath=basedir+"preset";
+ num++;
+ }
+ while (QFile(fullpath).exists());
+ return fullpath;
+}
+
+VPreset VEqualizer::createPreset(const QString &name, bool smart)
+{
+ if (presetExists(name) && !smart) return VPreset();
+ QString nameReal=name;
+ {
+ int number=1;
+ while (presetExists(nameReal))
+ {
+ nameReal=name+" ("+QString::number(number)+')';
+ number++;
+ }
+ }
+
+ VPreset preset(makePresetFile());
+ preset.setName(nameReal);
+
+ save(preset.file(), nameReal);
+ KConfig *config=kapp->config();
+ config->setGroup("Equalizer");
+ QStringList list = config->readListEntry("presets");
+ list += preset.file();
+ config->writeEntry("presets", list);
+ config->sync();
+
+ emit created(preset);
+ return preset;
+}
+
+
+QValueList<VPreset> VEqualizer::presets() const
+{
+ KConfig *conf=KGlobal::config();
+ conf->setGroup("Equalizer");
+
+ QStringList list;
+ if (conf->hasKey("presets"))
+ {
+ list=conf->readListEntry("presets");
+ }
+ else
+ {
+ list=kapp->dirs()->findAllResources("data", "noatun/eq.preset/*");
+ conf->writeEntry("presets", list);
+ conf->sync();
+ }
+
+ QValueList<VPreset> presets;
+
+ for (QStringList::Iterator i = list.begin(); i!=list.end(); ++i)
+ {
+ QFile file(*i);
+ if (!file.open(IO_ReadOnly)) continue;
+
+ QDomDocument doc("noatunequalizer");
+ if (!doc.setContent(&file)) continue;
+
+ QDomElement docElem = doc.documentElement();
+ if (docElem.tagName()!="noatunequalizer") continue;
+
+ presets.append(VPreset(*i));
+ }
+ return presets;
+}
+
+VPreset VEqualizer::presetByName(const QString &name)
+{
+ QValueList<VPreset> pr = presets();
+ for (
+ QValueList<VPreset>::Iterator i(pr.begin());
+ i != pr.end(); ++i
+ )
+ {
+ if ((*i).name() == name)
+ return *i;
+ }
+ return VPreset();
+}
+
+VPreset VEqualizer::presetByFile(const QString &file)
+{
+ KConfig *conf=KGlobal::config();
+ conf->setGroup("Equalizer");
+ QStringList list=kapp->config()->readListEntry("presets");
+ if (list.contains(file))
+ return VPreset(file);
+ return VPreset();
+}
+
+bool VEqualizer::presetExists(const QString &name) const
+{
+ QValueList<VPreset> list=presets();
+ for (
+ QValueList<VPreset>::Iterator i(list.begin());
+ i != list.end(); ++i
+ )
+ {
+ if ((*i).name() == name)
+ return true;
+ }
+ return false;
+}
+
+
+
+struct VPreset::Private
+{
+ QString file;
+};
+
+
+
+VPreset::VPreset(const QString &file)
+{
+ d = new Private;
+ d->file = file;
+
+}
+
+
+VPreset::VPreset()
+{
+ d = new Private;
+}
+
+VPreset::VPreset(const VPreset &copy)
+{
+ d = new Private;
+ operator =(copy);
+}
+
+VPreset::~VPreset()
+{
+ delete d;
+}
+
+bool VPreset::operator ==(const VPreset &other) const
+{
+ return name() == other.name();
+}
+
+VPreset & VPreset::operator=(const VPreset &copy)
+{
+ d->file = copy.file();
+ return *this;
+}
+
+QString VPreset::name() const
+{
+ QFile file(d->file);
+ if (!file.open(IO_ReadOnly)) return 0;
+
+ QDomDocument doc("noatunequalizer");
+ if (!doc.setContent(&file)) return 0;
+
+ QDomElement docElem = doc.documentElement();
+ if (docElem.tagName()!="noatunequalizer") return 0;
+ bool standard=docElem.attribute("default", "0")=="0";
+ QString n=docElem.attribute("name", 0);
+
+ { // All the translations for the presets
+# ifdef I18N_STUFF
+ I18N_NOOP("Trance");
+ I18N_NOOP("Dance");
+ I18N_NOOP("Metal");
+ I18N_NOOP("Jazz");
+ I18N_NOOP("Zero");
+ I18N_NOOP("Eclectic Guitar");
+# endif
+ }
+
+ if (standard)
+ n=i18n(n.local8Bit());
+
+ return n;
+}
+
+bool VPreset::setName(const QString &name)
+{
+ QFile file(d->file);
+ if (!file.open(IO_ReadOnly)) return false;
+
+ QDomDocument doc("noatunequalizer");
+ if (!doc.setContent(&file)) return false;
+
+ QDomElement docElem = doc.documentElement();
+ if (docElem.tagName()!="noatunequalizer") return false;
+
+ if (docElem.attribute("name") == name) return true;
+ if (EQ->presetByName(name)) return false;
+
+ docElem.setAttribute("name", name);
+ file.close();
+
+ if (!file.open(IO_ReadWrite | IO_Truncate)) return false;
+
+ QTextStream s(&file);
+ s << doc.toString();
+ file.close();
+
+ emit EQ->renamed(*this);
+
+ return true;
+}
+
+bool VPreset::isValid() const
+{
+ return d->file.length();
+}
+
+void VPreset::save()
+{
+ KURL url;
+ url.setPath(d->file);
+ EQ->load(url);
+}
+
+void VPreset::load() const
+{
+ KURL url;
+ url.setPath(d->file);
+ EQ->load(url);
+}
+
+QString VPreset::file() const
+{
+ return d->file;
+}
+
+void VPreset::remove()
+{
+ KConfig *config=kapp->config();
+ config->setGroup("Equalizer");
+ QStringList items=config->readListEntry("presets");
+ items.remove(file());
+ config->writeEntry("presets", items);
+ config->sync();
+
+ emit EQ->removed(*this);
+
+ if (file().find(kapp->dirs()->localkdedir())==0)
+ {
+ QFile f(file());
+ f.remove();
+ }
+ d->file = "";
+}
+
+
+#undef EQ
+#undef EQBACK
+
+#include "vequalizer.moc"
+
diff --git a/noatun/library/video.cpp b/noatun/library/video.cpp
new file mode 100644
index 00000000..c259d4ba
--- /dev/null
+++ b/noatun/library/video.cpp
@@ -0,0 +1,162 @@
+/****************************************************************************
+ Copyright (C) 2002 Charles Samuels
+ this file is on the BSD license, sans advertisement clause
+ *****************************************************************************/
+
+#include <noatun/video.h>
+#include <noatun/app.h>
+#include <noatun/player.h>
+#include <noatun/engine.h>
+
+#include <qpopupmenu.h>
+#include <kaction.h>
+#include <klocale.h>
+
+#include "globalvideo.h"
+
+// sorry :)
+QPtrList<VideoFrame> VideoFrame::frames;
+
+VideoFrame *VideoFrame::whose=0;
+
+struct VideoFrame::Private
+{
+};
+
+
+VideoFrame::VideoFrame(KXMLGUIClient *clientParent, QWidget *parent, const char*name, WFlags f)
+ : KVideoWidget(clientParent, parent, name, f)
+{
+ d = new Private;
+ connect(napp->player(), SIGNAL(newSong()), SLOT(changed()));
+ connect(napp->player(), SIGNAL(stopped()), SLOT(stopped()));
+ frames.append(this);
+}
+
+VideoFrame::VideoFrame(QWidget *parent, const char *name, WFlags f)
+ : KVideoWidget(parent, name, f)
+{
+ d = new Private;
+ connect(napp->player(), SIGNAL(newSong()), SLOT(changed()));
+ connect(napp->player(), SIGNAL(stopped()), SLOT(stopped()));
+ frames.append(this);
+}
+
+VideoFrame::~VideoFrame()
+{
+ if (whose == this)
+ {
+ embed(Arts::VideoPlayObject::null());
+ whose=0;
+ }
+
+ frames.removeRef(this);
+ VideoFrame *l = frames.last();
+ if (l) l->give();
+ else whose=0;
+ delete d;
+}
+
+VideoFrame *VideoFrame::playing()
+{
+ return whose;
+}
+
+QPopupMenu *VideoFrame::popupMenu(QWidget *parent)
+{
+ QPopupMenu *view = new QPopupMenu(parent);
+ action( "half_size" )->plug( view );
+ action( "normal_size" )->plug( view );
+ action( "double_size" )->plug( view );
+ view->insertSeparator();
+ action( "fullscreen_mode" )->plug( view );
+ return view;
+}
+
+void VideoFrame::give()
+{
+ VideoFrame *old=whose;
+ whose = this;
+
+ if (whose != old && old != 0)
+ {
+ old->embed(Arts::VideoPlayObject::null());
+ emit old->lost();
+ }
+
+ Arts::PlayObject po = napp->player()->engine()->playObject();
+ if (po.isNull()) return;
+
+ Arts::VideoPlayObject vpo = Arts::DynamicCast(po);
+ if (!vpo.isNull())
+ {
+ embed(vpo);
+ emit acquired();
+ }
+}
+
+void VideoFrame::changed()
+{
+ if (whose==this)
+ give();
+}
+
+void VideoFrame::stopped()
+{
+ if (whose==this)
+ {
+ embed(Arts::VideoPlayObject::null());
+ emit lost();
+ }
+}
+
+#include <qlayout.h>
+
+
+GlobalVideo::GlobalVideo()
+ : QWidget( 0, 0, WType_TopLevel | WStyle_Customize | WStyle_DialogBorder | WStyle_Title )
+{
+ setCaption(i18n("Video - Noatun"));
+ (new QVBoxLayout(this))->setAutoAdd(true);
+ video = new VideoFrame(this);
+ menu = video->popupMenu(this);
+
+ // FIXME: How to obtain minimum size for top-level widgets?
+// video->setMinimumSize(minimumSizeHint());
+// video->setMinimumSize(101,35);
+ video->setMinimumSize(128,96);
+
+ connect(video, SIGNAL(acquired()), SLOT(appear()));
+ connect(video, SIGNAL(lost()), SLOT(hide()));
+ connect(video, SIGNAL(adaptSize(int,int)), this, SLOT(slotAdaptSize(int,int)));
+
+ video->setNormalSize();
+ video->give();
+}
+
+void GlobalVideo::slotAdaptSize(int w, int h)
+{
+ resize(w, h);
+}
+
+void GlobalVideo::appear()
+{
+ QWidget::show();
+}
+
+void GlobalVideo::hide()
+{
+ QWidget::hide();
+}
+
+void GlobalVideo::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (e->button() == RightButton)
+ {
+ menu->exec(mapToGlobal(e->pos()));
+ }
+}
+
+#include "globalvideo.moc"
+#include "video.moc"
+
diff --git a/noatun/modules/Makefile.am b/noatun/modules/Makefile.am
new file mode 100644
index 00000000..90c298d3
--- /dev/null
+++ b/noatun/modules/Makefile.am
@@ -0,0 +1,4 @@
+SUBDIRS = artseffects \
+ dcopiface excellent htmlexport infrared kaiman keyz kjofol-skin \
+ marquis metatag monoscope net noatunui splitplaylist systray \
+ voiceprint winskin simple
diff --git a/noatun/modules/artseffects/ExtraStereo.mcopclass b/noatun/modules/artseffects/ExtraStereo.mcopclass
new file mode 100644
index 00000000..7fb466ec
--- /dev/null
+++ b/noatun/modules/artseffects/ExtraStereo.mcopclass
@@ -0,0 +1,5 @@
+Buildable=true
+Interface=ExtraStereo,Arts::StereoEffect,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartseffects.la
+Use=directly \ No newline at end of file
diff --git a/noatun/modules/artseffects/ExtraStereoGuiFactory.mcopclass b/noatun/modules/artseffects/ExtraStereoGuiFactory.mcopclass
new file mode 100644
index 00000000..5b5521ae
--- /dev/null
+++ b/noatun/modules/artseffects/ExtraStereoGuiFactory.mcopclass
@@ -0,0 +1,5 @@
+Buildable=true
+Interface=ExtraStereoGuiFactory,Arts::GuiFactory,Arts::Object
+Library=libartseffects.la
+Language=C++
+CanCreate=ExtraStereo
diff --git a/noatun/modules/artseffects/Makefile.am b/noatun/modules/artseffects/Makefile.am
new file mode 100644
index 00000000..49a5bb99
--- /dev/null
+++ b/noatun/modules/artseffects/Makefile.am
@@ -0,0 +1,25 @@
+INCLUDES= -I$(top_builddir)/arts/gui/common -I$(top_srcdir)/arts/gui/common -I$(kde_includes)/arts $(all_includes)
+lib_LTLIBRARIES = libartseffects.la
+#libartseffectsui.la
+
+libartseffects_la_SOURCES = artseffects.cc extrastereo_impl.cc
+libartseffects_la_COMPILE_FIRST = artseffects.cc
+libartseffects_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+libartseffects_la_LIBADD = $(top_builddir)/arts/gui/common/libartsgui_idl.la -lkmedia2_idl -lsoundserver_idl -lartsflow
+
+
+artseffects.mcopclass: artseffects.h
+artseffects.mcoptype: artseffects.h
+artseffects.cc artseffects.h: $(srcdir)/artseffects.idl $(MCOPIDL)
+ $(MCOPIDL) -t -I$(top_builddir)/arts/gui/common -I$(top_srcdir)/arts/gui/common -I$(kde_includes)/arts $(srcdir)/artseffects.idl
+
+CLEANFILES = artsmidi.cc artsmidi.h artsmidi.mcoptype artsmidi.mcopclass
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = artseffects.mcoptype artseffects.mcopclass
+
+mcopclassdir = $(libdir)/mcop
+mcopclass_DATA = ExtraStereo.mcopclass VoiceRemoval.mcopclass RawWriter.mcopclass ExtraStereoGuiFactory.mcopclass
+
+artseffects.lo: artseffects.h ../../../arts/gui/common/artsgui.h
+extrastereo_impl.lo: ../../../arts/gui/common/artsgui.h artseffects.h
diff --git a/noatun/modules/artseffects/RawWriter.mcopclass b/noatun/modules/artseffects/RawWriter.mcopclass
new file mode 100644
index 00000000..535db20a
--- /dev/null
+++ b/noatun/modules/artseffects/RawWriter.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=RawWriter,Arts::StereoEffect,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartseffects.la
diff --git a/noatun/modules/artseffects/VoiceRemoval.mcopclass b/noatun/modules/artseffects/VoiceRemoval.mcopclass
new file mode 100644
index 00000000..661029bd
--- /dev/null
+++ b/noatun/modules/artseffects/VoiceRemoval.mcopclass
@@ -0,0 +1,5 @@
+Buildable=true
+Interface=VoiceRemoval,Arts::StereoEffect,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartseffects.la
+Use=directly
diff --git a/noatun/modules/artseffects/artseffects.idl b/noatun/modules/artseffects/artseffects.idl
new file mode 100644
index 00000000..86fdea16
--- /dev/null
+++ b/noatun/modules/artseffects/artseffects.idl
@@ -0,0 +1,17 @@
+#include <artsflow.idl>
+#include <artsgui.idl>
+
+interface ExtraStereo : Arts::StereoEffect {
+ attribute float intensity;
+};
+
+interface VoiceRemoval : Arts::StereoEffect {
+};
+
+interface RawWriter : Arts::StereoEffect
+{
+};
+
+interface ExtraStereoGuiFactory : Arts::GuiFactory {
+};
+
diff --git a/noatun/modules/artseffects/effect.cpp b/noatun/modules/artseffects/effect.cpp
new file mode 100644
index 00000000..8d1c8b69
--- /dev/null
+++ b/noatun/modules/artseffects/effect.cpp
@@ -0,0 +1 @@
+
diff --git a/noatun/modules/artseffects/extrastereo_impl.cc b/noatun/modules/artseffects/extrastereo_impl.cc
new file mode 100644
index 00000000..3dc92705
--- /dev/null
+++ b/noatun/modules/artseffects/extrastereo_impl.cc
@@ -0,0 +1,153 @@
+#include "artsgui.h"
+#include "artseffects.h"
+#include "stdsynthmodule.h"
+#include <dynamicrequest.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <convert.h>
+#include "debug.h"
+#include <connect.h>
+#include <cstdlib>
+
+using namespace Arts;
+
+class ExtraStereo_impl : virtual public ExtraStereo_skel,
+ virtual public StdSynthModule
+{
+ float _intensity;
+// StereoEffectGUI mStereoEffectGUI;
+public:
+ float intensity() { return _intensity; }
+ void intensity(float newValue) {
+ _intensity = newValue;
+// cout << " **** INTENSITY = " << newValue << endl;
+ }
+
+ ExtraStereo_impl() : _intensity(2.0)
+ {
+// mStereoEffectGUI = DynamicCast(server.createObject("Arts::ExtraStereo"));
+// DynamicRequest(mStereoEffectGUI).method("_set_effect").param(*this).invoke();
+
+ }
+ void calculateBlock(unsigned long samples)
+ {
+ unsigned long i;
+
+ for(i = 0; i < samples; i++)
+ {
+ float average = (inleft[i] + inright[i]) / 2.0;
+
+ float outleftnew = average + (inleft[i] - average) * _intensity;
+ if(outleftnew > 1.0) outleft[i] = 1.0;
+ else if(outleftnew < -1.0) outleft[i] = -1.0;
+ else outleft[i] = outleftnew;
+
+ float outrightnew = average + (inright[i] - average) * _intensity;
+ if(outrightnew > 1.0) outright[i] = 1.0;
+ else if(outrightnew < -1.0) outright[i] = -1.0;
+ else outright[i] = outrightnew;
+ }
+ }
+};
+
+
+class VoiceRemoval_impl : virtual public VoiceRemoval_skel,
+ virtual public StdSynthModule
+{
+public:
+
+ VoiceRemoval_impl()
+ {
+
+ }
+ // This is based on the work of Anders Carlsson <anders.carlsson@tordata.se>
+ void calculateBlock(unsigned long samples)
+ {
+ for (unsigned i = 0; i < samples; i++)
+ {
+ float outleftnew=inleft[i]-inright[i];
+ float outrightnew=inright[i]-inleft[i];
+
+ if (inleft[i] < -1.0) outleftnew = -1.0;
+ else if (inleft[i] > 1.0) outleftnew = 1.0;
+
+ if (inright[i] < -1.0) outrightnew = -1.0;
+ else if (inright[i] > 1.0) outrightnew = 1.0;
+ outleft[i] = outleftnew;
+ outright[i] = outrightnew;
+ }
+
+ }
+};
+
+class RawWriter_impl : virtual public RawWriter_skel,
+ virtual public StdSynthModule
+{
+ int mFd;
+public:
+ RawWriter_impl()
+ {
+ std::string file=getenv("HOME");
+ file.append("/arts-write.raw");
+ mFd=::open(file.c_str(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ }
+ ~RawWriter_impl()
+ {
+ ::close(mFd);
+ }
+ // This is based on the work of Anders Carlsson <anders.carlsson@tordata.se>
+ void calculateBlock(unsigned long samples)
+ {
+ for (unsigned i = 0; i < samples; i++)
+ {
+ outleft[i]=inleft[i];
+ outright[i]=inright[i];
+ }
+ unsigned char *buffer=new unsigned char[samples*4];
+ convert_stereo_2float_i16le(samples,
+ inleft, inright, buffer);
+ ::write(mFd, (const void*)buffer, samples*4);
+ delete [] buffer;
+ }
+};
+
+class ExtraStereoGuiFactory_impl : public ExtraStereoGuiFactory_skel
+{
+public:
+ Widget createGui(Object object)
+ {
+ arts_return_val_if_fail(!object.isNull(), Arts::Widget::null());
+
+ ExtraStereo e = DynamicCast(object);
+ arts_return_val_if_fail(!e.isNull(), Arts::Widget::null());
+
+ HBox hbox;
+ hbox.width(80);
+ hbox.height(80);
+ hbox.show();
+
+
+ Poti intense;
+ intense.caption("Intensity");
+ intense.color("red");
+ intense.min(0);
+ intense.max(5);
+ intense.value(e.intensity());
+ intense.parent(hbox);
+ intense.show();
+ connect(intense,"value_changed", e, "intensity");
+
+ hbox._addChild(intense,"intensityWidget");
+
+ return hbox;
+ }
+};
+
+
+REGISTER_IMPLEMENTATION(ExtraStereo_impl);
+REGISTER_IMPLEMENTATION(VoiceRemoval_impl);
+REGISTER_IMPLEMENTATION(RawWriter_impl);
+REGISTER_IMPLEMENTATION(ExtraStereoGuiFactory_impl);
+
+
diff --git a/noatun/modules/artseffects/extrastereogui_impl.cc b/noatun/modules/artseffects/extrastereogui_impl.cc
new file mode 100644
index 00000000..5952c80e
--- /dev/null
+++ b/noatun/modules/artseffects/extrastereogui_impl.cc
@@ -0,0 +1,28 @@
+#include <qlayout.h>
+#include <qslider.h>
+#include "extrastereogui_impl.h"
+
+namespace Arts {
+
+ExtraStereoGUI_impl::ExtraStereoGUI_impl() : QWidget(0)
+{
+ (new QHBoxLayout(this))->setAutoAdd(true);
+ mSlider=new QSlider(0,100,10, 0, Horizontal, this);
+ mSlider->show();
+ show();
+}
+
+void ExtraStereoGUI_impl::changeSlider(int v)
+{
+ effect.intensity((float)v/100.0);
+}
+
+void ExtraStereoGUI_impl::setEffect(StereoEffect newEffect)
+{
+ effect = DynamicCast(newEffect);
+}
+
+REGISTER_IMPLEMENTATION(ExtraStereoGUI_impl);
+
+};
+
diff --git a/noatun/modules/artseffects/extrastereogui_impl.h b/noatun/modules/artseffects/extrastereogui_impl.h
new file mode 100644
index 00000000..56104ae8
--- /dev/null
+++ b/noatun/modules/artseffects/extrastereogui_impl.h
@@ -0,0 +1,26 @@
+#include "artseffects.h"
+#include "stdsynthmodule.h"
+#include <qwidget.h>
+
+class QSlider;
+
+namespace Arts {
+
+class ExtraStereoGUI_impl : public QWidget, virtual public ExtraStereoGUI_skel
+{
+public:
+ ExtraStereo effect;
+
+ ExtraStereoGUI_impl();
+
+ void setEffect(StereoEffect newEffect);
+
+public slots:
+ void changeSlider(int);
+
+private:
+ QSlider *mSlider;
+};
+
+
+};
diff --git a/noatun/modules/dcopiface/Makefile.am b/noatun/modules/dcopiface/Makefile.am
new file mode 100644
index 00000000..6d55fc72
--- /dev/null
+++ b/noatun/modules/dcopiface/Makefile.am
@@ -0,0 +1,16 @@
+INCLUDES= -I$(top_builddir)/noatun/library -I$(top_srcdir)/noatun/library -I$(kde_includes)/arts $(all_includes)
+kde_module_LTLIBRARIES = noatun_dcopiface.la
+
+noatun_dcopiface_la_SOURCES = dcopiface.cpp dcopiface.skel
+
+noatun_dcopiface_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_dcopiface_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la
+
+noatun_dcopiface_la_METASOURCES = AUTO
+
+noinst_HEADERS = dcopiface.h
+
+noatun_modules_dcopiface_DATA = dcopiface.plugin
+noatun_modules_dcopifacedir = $(kde_datadir)/noatun
+
+dcopiface.lo: ../../library/noatunarts/noatunarts.h
diff --git a/noatun/modules/dcopiface/dcopiface.cpp b/noatun/modules/dcopiface/dcopiface.cpp
new file mode 100644
index 00000000..93ef6160
--- /dev/null
+++ b/noatun/modules/dcopiface/dcopiface.cpp
@@ -0,0 +1,250 @@
+#include "dcopiface.h"
+
+#include <noatun/player.h>
+#include <noatun/app.h>
+#include <noatunarts/noatunarts.h>
+#include <noatun/engine.h>
+
+#include <dcopclient.h>
+
+extern "C"
+{
+ KDE_EXPORT NIF *create_plugin()
+ {
+ return new NIF();
+ }
+}
+
+
+NIF::NIF() : Plugin(), DCOPObject("Noatun")
+{
+ mLastVolume = 0;
+// connect(napp->player(), SIGNAL(newSong()), SLOT(newSongPlaying()));
+}
+
+NIF::~NIF()
+{
+ kapp->dcopClient()->emitDCOPSignal("exiting()", QByteArray());
+}
+
+void NIF::toggleListView()
+{
+ napp->player()->toggleListView();
+}
+
+void NIF::handleButtons()
+{
+ napp->player()->handleButtons();
+}
+
+void NIF::removeCurrent()
+{
+ napp->player()->removeCurrent();
+}
+
+void NIF::back()
+{
+ napp->player()->back();
+}
+
+void NIF::stop()
+{
+ napp->player()->stop();
+}
+
+void NIF::play()
+{
+ napp->player()->play();
+}
+
+void NIF::playpause()
+{
+ napp->player()->playpause();
+}
+
+void NIF::forward()
+{
+ napp->player()->forward();
+}
+
+void NIF::skipTo(int msec)
+{
+ napp->player()->skipTo(msec);
+}
+
+void NIF::loop()
+{
+ napp->player()->loop();
+}
+
+void NIF::setVolume(int i)
+{
+ napp->player()->setVolume(i);
+}
+
+int NIF::volume()
+{
+ return napp->player()->volume();
+}
+
+void NIF::volumeUp()
+{
+ napp->player()->setVolume(napp->player()->volume() + 5);
+}
+
+void NIF::volumeDown()
+{
+ napp->player()->setVolume(napp->player()->volume() - 5);
+}
+
+void NIF::toggleMute()
+{
+ int currVol = napp->player()->volume();
+ if (currVol == 0)
+ {
+ napp->player()->setVolume(mLastVolume);
+ }
+ else
+ {
+ mLastVolume = currVol;
+ napp->player()->setVolume(0);
+ }
+}
+
+int NIF::length() // returns -1 if there's no playobject
+{
+ return napp->player()->getLength();
+}
+
+int NIF::position() // returns -1 if there's no playobject
+{
+ return napp->player()->getTime();
+}
+
+int NIF::state()
+{
+ if (napp->player()->isPlaying())
+ return 2;
+ if (napp->player()->isPaused())
+ return 1;
+
+ return 0; // default to stopped
+}
+
+QString NIF::lengthString()
+{
+ return napp->player()->current() ? napp->player()->current().lengthString() : "";
+}
+
+QString NIF::timeString()
+{
+ return napp->player()->lengthString();
+}
+
+QString NIF::title()
+{
+ return napp->player()->current() ? napp->player()->current().title() : "";
+}
+
+void NIF::setCurrentProperty(const QString &key, const QString &value)
+{
+ if (!napp->player()->current()) return;
+
+ napp->player()->current().setProperty(key, value);
+}
+
+QString NIF::currentProperty(const QString &key)
+{
+ if (!napp->player()->current()) return "";
+
+ return napp->player()->current().property(key);
+}
+
+void NIF::clearCurrentProperty(const QString &key)
+{
+ if (!napp->player()->current()) return;
+
+ return napp->player()->current().clearProperty(key);
+}
+
+
+QCString NIF::visStack()
+{
+ return napp->player()->engine()->visualizationStack()->toString().c_str();
+}
+
+QCString NIF::session()
+{
+ return napp->player()->engine()->session()->toString().c_str();
+}
+
+// adds one file to the playlist
+void NIF::addFile(const QString& f, bool autoplay)
+{
+ napp->player()->openFile(f, false, autoplay);
+}
+
+// Adds a bunch of files to the playlist
+void NIF::addFile(const QStringList &f, bool autoplay)
+{
+ for (QStringList::ConstIterator it = f.begin(); it != f.end(); ++it )
+ napp->player()->openFile(*it, false, autoplay);
+}
+
+void NIF::loadPlugin(const QString &spec)
+{
+ napp->libraryLoader()->add(spec);
+}
+
+QStringList NIF::availablePlugins() {
+ QStringList available_spec_files;
+ QValueList<NoatunLibraryInfo> available;
+
+ available = napp->libraryLoader()->available();
+
+ QValueList<NoatunLibraryInfo>::iterator it;
+ for (it = available.begin();it != available.end();it++) {
+ available_spec_files += (*it).specfile;
+ }
+
+ return available_spec_files;
+}
+
+QStringList NIF::loadedPlugins() {
+ QStringList loaded_spec_files;
+ QValueList<NoatunLibraryInfo> loaded;
+
+ loaded = napp->libraryLoader()->loaded();
+
+ QValueList<NoatunLibraryInfo>::iterator it;
+ for (it = loaded.begin();it != loaded.end();it++) {
+ loaded_spec_files += (*it).specfile;
+ }
+
+ return loaded_spec_files;
+}
+
+bool NIF::unloadPlugin(const QString &spec)
+{
+ return napp->libraryLoader()->remove(spec);
+}
+
+QStringList NIF::mimeTypes()
+{
+ return napp->mimeTypes();
+}
+
+QCString NIF::version()
+{
+ return napp->version();
+}
+
+void NIF::newSongPlaying()
+{
+ kapp->dcopClient()->emitDCOPSignal("newFile()", QByteArray());
+}
+
+void NIF::clear()
+{
+ napp->playlist()->clear();
+}
diff --git a/noatun/modules/dcopiface/dcopiface.h b/noatun/modules/dcopiface/dcopiface.h
new file mode 100644
index 00000000..4b9fe5e7
--- /dev/null
+++ b/noatun/modules/dcopiface/dcopiface.h
@@ -0,0 +1,104 @@
+#ifndef DCOPIFACE_H
+#define DCOPIFACE_H
+
+#include <noatun/player.h>
+#include <noatun/plugin.h>
+
+#include <dcopobject.h>
+#include <kdemacros.h>
+
+class KDE_EXPORT NIF : public Plugin, public DCOPObject
+{
+K_DCOP
+
+public:
+ NIF();
+ ~NIF();
+
+private slots:
+ void newSongPlaying();
+
+k_dcop:
+ void toggleListView();
+ void handleButtons();
+ void removeCurrent();
+
+ void back();
+ void stop();
+ void play();
+ void playpause();
+ void forward();
+ void skipTo(int);
+ void loop();
+
+ void setVolume(int);
+ int volume();
+ void volumeUp();
+ void volumeDown();
+ void toggleMute();
+
+ /**
+ * length in milliseconds
+ **/
+ int length();
+ /**
+ * position in milliseconds
+ **/
+ int position();
+
+ /**
+ * 0 stopped
+ * 1 paused
+ * 2 playing
+ **/
+ int state();
+
+ QString lengthString();
+ QString timeString();
+
+ QString title();
+
+ /**
+ * set a property for the current song
+ **/
+ void setCurrentProperty(const QString &key, const QString &value);
+ /**
+ * get a property from the current song
+ **/
+ QString currentProperty(const QString &key);
+ /**
+ * clear a property from the current song
+ **/
+ void clearCurrentProperty(const QString &key);
+
+ QCString visStack();
+ QCString session();
+
+ void addFile(const QString& f, bool autoplay);
+ void addFile(const QStringList &f, bool autoplay);
+
+ void loadPlugin(const QString &specFile);
+ QStringList availablePlugins();
+ QStringList loadedPlugins();
+ bool unloadPlugin(const QString &specFile);
+
+ QStringList mimeTypes();
+ QCString version();
+
+ /**
+ * clear the playlist
+ **/
+ void clear();
+private:
+ int mLastVolume; // remember volume for mute/unmute
+
+#ifdef DOCUMENTATION_BLEH_BLEH_DONT_TRY_COMPILING_THIS
+signals:
+ void exiting();
+ void newFile();
+
+#endif
+};
+
+#endif
+
diff --git a/noatun/modules/dcopiface/dcopiface.plugin b/noatun/modules/dcopiface/dcopiface.plugin
new file mode 100644
index 00000000..d98a661f
--- /dev/null
+++ b/noatun/modules/dcopiface/dcopiface.plugin
@@ -0,0 +1,137 @@
+Filename=noatun_dcopiface.la
+Author=Charles Samuels
+Site=http://www.derkarl.org/noatun
+Email=charles@kde.org
+Type=hidden
+License=Artistic
+Name=DCOP Interface
+Name[af]=Dcop Koppelvlak
+Name[ar]=واجهة DCOP
+Name[az]=DCOP Ara üzü
+Name[bn]=ডিকপ ইন্টারফেস
+Name[br]=Etrefas DCOP
+Name[ca]=Interfície DCOP
+Name[cs]=Rozhraní DCOP
+Name[cy]=Rhyngwyneb DCOP
+Name[da]=DCOP-grænseflade
+Name[de]=DCOP-Schnittstelle
+Name[el]=Διασύνδεση DCOP
+Name[eo]=DCOP-interfaco
+Name[es]=Interfaz de DCOP
+Name[et]=DCOP liides
+Name[eu]=DCOP interfazea
+Name[fa]=واسط DCOP
+Name[fi]=DCOP-rajapinta
+Name[fr]=Interface DCOP
+Name[ga]=Comhéadan DCOP
+Name[gl]=Interface DCOP
+Name[he]=ממשק DCOP
+Name[hi]= डीकॉप इंटरफेस
+Name[hr]=DCOP sučelje
+Name[hu]=DCOP-felület
+Name[is]=DCOP aðgangur
+Name[it]=Interfaccia DCOP
+Name[ja]=DCOP インターフェース
+Name[kk]=DCOP интерфейсі
+Name[km]=ចំណុច​ប្រទាក់ DCOP
+Name[ko]=DCOP 인터페이스
+Name[lt]=DCOP sąsaja
+Name[lv]=DCOP Starpseja
+Name[mk]=Интерфејс DCOP
+Name[ms]=Antaramuka DCOP
+Name[mt]=Interfaċċja DCOP
+Name[nb]=DCOP-grensesnitt
+Name[nds]=DCOP-Koppelsteed
+Name[ne]=DCOP इन्टरफेस
+Name[nl]=DCOP-interface
+Name[nn]=DCOP-grensesnitt
+Name[pa]=DCOP ਇੰਟਰਫੇਸ
+Name[pl]=Interfejs DCOP
+Name[pt]=Interface do DCOP
+Name[pt_BR]=Interface DCOP
+Name[ro]=Interfaţă DCOP
+Name[ru]=Интерфейс DCOP
+Name[se]=DCOP-lakta
+Name[sk]=Rozhranie DCOP
+Name[sl]=Vmesnik DCOP
+Name[sr]=DCOP интерфејс
+Name[sr@Latn]=DCOP interfejs
+Name[sv]=DCOP-gränssnitt
+Name[ta]=DCOP இடைமுகம்
+Name[tg]=Интерфейси DCOP
+Name[th]=ส่วนติดต่อ DCOP
+Name[tr]=DCOP Arayüzü
+Name[uk]=Інтерфейс DCOP
+Name[uz]=DCOP interfeysi
+Name[uz@cyrillic]=DCOP интерфейси
+Name[xh]=Ujongano lwe DCOP
+Name[zh_CN]=DCOP 接口
+Name[zh_HK]=DCOP 介面
+Name[zh_TW]=DCOP 介面
+Name[zu]=Uxhumano olubhekeneyo lwe DCOP
+Comment=DCOP Interface for Inter-Process Communication
+Comment[af]=Dcop Koppelvlak vir Inter-Process Kommunikasie
+Comment[ar]=واجهة DCOP لإستعراض تواصل عمليات النظام مع بعضها
+Comment[az]=IPC üçün DCOP Ara üzü
+Comment[bg]=Интерфейс за комуникация между процесите
+Comment[bn]=আন্তঃপ্রক্রিয়া যোগাযোগের জন্য ডিকপ ইন্টারফেস
+Comment[bs]=DCOP interfejs za međuprocesnu komunikaciju
+Comment[ca]=Interfície DCOP per la comunicació entre processos
+Comment[cs]=Rozhraní DCOP pro mezi procesovou komunikaci
+Comment[cy]=Rhyngwyneb DCOP ar gyfer Cyfathrebu Rhyngbrosesol
+Comment[da]=DCOP-grænseflade for interproceskommunikation
+Comment[de]=DCOP-Schnittstelle für die Kommunikation zwischen Prozessen
+Comment[el]=Διασύνδεση DCOP για επικοινωνία μεταξύ διεργασιών (IPC)
+Comment[eo]=DCOP-interfaco por interproceza komunikado
+Comment[es]=Interfaz de DCOP para comunicaciones entre procesos (IPC)
+Comment[et]=DCOP liides protsessidevahelise ühenduse loomiseks
+Comment[eu]=DCOP interfazea prozesuen arteko komunikaziorako
+Comment[fa]=واسط DCOP برای ارتباط درون فرایند
+Comment[fi]=DCOP-rajapinta sisäiseen kommunikointiin
+Comment[fr]=Interface DCOP pour les communications entre processus
+Comment[ga]=Comhéadan DCOP le haghaidh cumarsáide idirphróiseas
+Comment[gl]=Interface DCOP para Comuicación Entre Procesos
+Comment[he]=ממשק DCOP לתקשורת בין-תהליכית
+Comment[hi]=इंटर-प्रोसेस कम्यूनिकेशन के लिए डीकॉप इंटरफेस
+Comment[hr]=DCOP sučelje za komunikaciju među procesima
+Comment[hu]=DCOP-felület IPC-hívásokhoz
+Comment[is]=DCOP aðgangur svo ferlin geti talað saman
+Comment[it]=Interfaccia DCOP per le comunicazioni tra processi (IPC)
+Comment[ja]=プロセス間通信に使われる DCOP インターフェース
+Comment[kk]=Процесаралық қатынаудың DCOP интерфейсі
+Comment[km]=ចំណុច​ប្រទាក់ DCOP សម្រាប់​ការ​ទំនាក់​ទំនង​ខាង​ក្នុង​ដំណើរការ
+Comment[ko]=프로세스 사이에 통신을 주고 받을 수 있게 해주는 DCOP 인터페이스
+Comment[lt]=DCOP sąsaja tarpprocesiniam ryšiui
+Comment[lv]=DCOP Starpseja Iekšējo-Procesu komunikācijām
+Comment[mk]=Интерфејс DCOP за комуникација помеѓу процесите
+Comment[ms]=Antaramuka DCOP untuk Komunikasi Antara Proses
+Comment[mt]=Interfaċċja DCOP għal komunikazzjoni bejn proċessi (IPC)
+Comment[nb]=DCOP grensesnitt for interprosesskommunikasjon
+Comment[nds]=DCOP-Koppelsteed för Kommunikatschoon twischen Perzessen
+Comment[ne]=आन्तरिक-प्रक्रिया सञ्चारका लागि DCOP इन्टरफेस
+Comment[nl]=DCOP-interface voor interprocescommunicatie
+Comment[nn]=DCOP-grensesnitt for interprosesskommunikasjon
+Comment[pl]=Interfejs DCOP dla Komunikacji Międzyprocesowej (IPC)
+Comment[pt]=Interface do DCOP para a comunicação entre os processos
+Comment[pt_BR]=Interface DCOP para comunicações inter-processos
+Comment[ro]=Interfaţă DCOP pentru comunicaţie interprocese
+Comment[ru]=Интерфейс с DCOP для межпроцессного взаимодействия
+Comment[se]=DCOP-lakta proseassaidgaskkasaš gulahallama dihte
+Comment[sk]=Rozhranie DCOP pre komunikáciu medzi procesmi
+Comment[sl]=Vmesnik DCOP za medprocesno komunikacijo
+Comment[sr]=DCOP интерфејс за међупроцесну комуникацју
+Comment[sr@Latn]=DCOP interfejs za međuprocesnu komunikacju
+Comment[sv]=DCOP-gränssnitt för kommunikation mellan processer
+Comment[ta]=செயல்களுக்கிடைப்பட்ட தகவல் பரிவர்த்தனைக்கான ODBC இடைமுகம்
+Comment[tg]=Интерфейси DCOP барои Алоқаи Ҷараёни Интернет
+Comment[th]=ส่วนติดต่อ DCOP สำหรับการสื่อสารระหว่างโปรเซส
+Comment[tr]=IPC için DCOP Arayüzü
+Comment[uk]=Інтерфейс DCOP для взаємодії процесів (IPC)
+Comment[uz]=Jarayonlararo bogʻlanish uchun DCOP interfeysi
+Comment[uz@cyrillic]=Жараёнлараро боғланиш учун DCOP интерфейси
+Comment[ven]=DCOP Interface ya tshitenwa tsha vhudavhudzani
+Comment[xh]=Ujongano lwe DCOP-lothungelwano lwangaphakathi oluqhubekayo
+Comment[zh_CN]=进程间通信的 DCOP 接口
+Comment[zh_HK]=用於行程間通訊的 DCOP 介面
+Comment[zh_TW]=程序程式間通訊的 DCOP 介面
+Comment[zu]=Uxhumano olubhekene lwe DCOP-lothungelwano lwangaphakathi oluqhubekayo
diff --git a/noatun/modules/excellent/Makefile.am b/noatun/modules/excellent/Makefile.am
new file mode 100644
index 00000000..a2558676
--- /dev/null
+++ b/noatun/modules/excellent/Makefile.am
@@ -0,0 +1,18 @@
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_excellent.la
+
+noatun_excellent_la_SOURCES = noatunui.cpp\
+ userinterface.cpp
+
+noatun_excellent_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_excellent_la_LIBADD = $(LIB_KFILE) \
+ $(top_builddir)/noatun/library/libnoatun.la \
+ $(top_builddir)/noatun/library/libnoatuncontrols.la \
+ -lqtmcop -lkmedia2_idl -lsoundserver_idl
+
+noatun_excellent_la_METASOURCES = AUTO
+
+noinst_HEADERS = userinterface.h
+
+noatun_modules_excellent_DATA = excellent.plugin excellentui.rc
+noatun_modules_excellentdir = $(kde_datadir)/noatun
diff --git a/noatun/modules/excellent/excellent.plugin b/noatun/modules/excellent/excellent.plugin
new file mode 100644
index 00000000..89f747c0
--- /dev/null
+++ b/noatun/modules/excellent/excellent.plugin
@@ -0,0 +1,120 @@
+Filename=noatun_excellent.la
+Author=Neil Stevens
+Site=http://noatun.kde.org/plugins/excellent/
+Email=neil@qualityassistant.com
+Type=userinterface
+License=X11-like
+Name=Excellent
+Name[af]=Uitstekende
+Name[ar]=ممتاز
+Name[az]=Mükəmməl
+Name[ca]=Excel·lent
+Name[cs]=Vynikající
+Name[cy]=Ardderchog
+Name[de]=Hervorragend
+Name[el]=Έξοχο
+Name[eo]=Brila
+Name[es]=Excelente
+Name[et]=Suurepärane
+Name[eu]=Aparta
+Name[fa]=عالی
+Name[fi]=Erinomainen
+Name[ga]=Sármhaith
+Name[gl]=Excelente
+Name[he]=מעולה
+Name[hi]=एक्सेलेंट
+Name[hr]=Odlično
+Name[is]=Frábært
+Name[it]=Eccellente
+Name[ja]=エクセレント
+Name[km]=ល្អ​ឥត​ខ្ចោះ
+Name[lt]=Puikus
+Name[lv]=Lielisks
+Name[mk]=Одличен
+Name[mt]=Eċċellenti
+Name[nb]=Fantastisk
+Name[nds]=Goot
+Name[ne]=दक्ष
+Name[nn]=Fantastisk
+Name[pa]=ਸਰਵੋਤਮ
+Name[pl]=Doskonały
+Name[pt]=Excelente
+Name[pt_BR]=Excelente
+Name[ro]=Excelent
+Name[ru]=Превосходный
+Name[sk]=Fantastický
+Name[sv]=Utmärkt
+Name[ta]=அற்புதம்
+Name[tg]=Олиҷаноб
+Name[th]=ยอดเยี่ยม
+Name[tr]=Mükemmel
+Name[uk]=Чудовий
+Name[ven]=Zwavhudi
+Name[xh]=Hle kwaphela
+Name[zh_CN]=优秀的
+Name[zh_HK]=極佳的
+Name[zh_TW]=完美的
+Name[zu]=Kuhle kakhulu
+Comment=A very ordinary, and therefore very usable, interface
+Comment[af]='n baie gewone, en daarom baie bruikbaar, koppelvlak
+Comment[ar]=واجهة استخدام عادية ، و بالتالي ممتازة
+Comment[az]=Olduqca bəsit və istifadəli bir axtar üz
+Comment[bg]=Обикновен и поради това много полезен интерфейс
+Comment[bs]=Vrlo uobičajen i zato vrlo upotrebljiv interfejs
+Comment[ca]=Una interfície molt normal, i per tant, molt usable
+Comment[cs]=Velmi obvyklé, and proto velmi použitelné rozhraní
+Comment[cy]=Rhyngwyneb Cyffredin iawn, ac felly defnyddiol iawn
+Comment[da]=En meget ordinær, og derfor meget nyttig, grænseflade
+Comment[de]=Eine ganz normale und folglich sehr brauchbare Oberfläche
+Comment[el]=Ένα πολύ συνηθισμένο, και έτσι πολύ εύχρηστο περιβάλλον
+Comment[eo]=Ordinara kaj do tre uzebla interfaco
+Comment[es]=Una interfaz muy común y por tanto muy fácil de usar
+Comment[et]=Väga tavaline ja lihtsasti kasutatav kasutajaliides
+Comment[eu]=Interfaze arrunta eta oso erabilgarria
+Comment[fa]=یک واسط معمولی، و بنابراین بسیار مفید
+Comment[fi]=Hyvin tavallinen ja käyttökelpoinen käyttöliittymä
+Comment[fr]=Une interface très ordinaire et donc très utilisable
+Comment[ga]=Comhéadan an-choitianta, agus dá bharr sin, an-inúsáidte
+Comment[gl]=Unha interface moi usual e polo tanto moi usábel
+Comment[he]=ממשק מאוד פשוט, ועל כן מאוד שימושי
+Comment[hi]=एक बहुत साधारण, अतः बहुत उपयोगी इंटरफेस
+Comment[hr]=Vrlo obično, i prema tome vrlo upotrebljivo, sučelje
+Comment[hu]=Egy jól ismert, ezért jól használható felület
+Comment[is]=Mjög venjulegt og notanlegt viðmót
+Comment[it]=Un'interfaccia molto ordinaria e molto usabile
+Comment[ja]=非常にありふれているが、だからこそ使いやすいインターフェース
+Comment[kk]=Ең кәдімгі, сондықтан жиі қолданылатын интерфейс
+Comment[km]=ចំណុច​ប្រទាក់​សាមញ្ញ​បំផុត ហើយ​ក៏​ងាយស្រួល​ប្រើ​ដែរ
+Comment[ko]=아주 평범하고 쓸만한 인터페이스
+Comment[lt]=Labai paprasta ir dėlto labai naudinga sąsaja
+Comment[lv]=Ļoti vienkārša, un tādēļ ļoti izmantojama, starpseja
+Comment[mk]=Мошне обичен, и според тоа мошне корисен интерфејс
+Comment[ms]=Antaramuka yang sangat biasa tetapi berguna
+Comment[mt]=Interfaċċja ordijarja ħafna u għalhekk utli ħafna
+Comment[nb]=Et svært vanlig og brukervennlig grensesnitt
+Comment[nds]=En bannig normaal un dorüm bannig eenfach to bruken Böversiet
+Comment[ne]=साधारण भएकोले धेरै उपयोगि इन्टरफेस
+Comment[nl]=Een heel gewone, en daarom erg bruikbare, interface
+Comment[nn]=Eit svært normalt og difor lettbruka grensesnitt
+Comment[pl]=Bardzo zwyczajny, ale przez to bardzo użyteczny motyw
+Comment[pt]=Uma interface básica e, por isso, muito fácil de usar
+Comment[pt_BR]=Uma interface bem ordinária e portanto muito utilizável
+Comment[ro]=O interfaţă foarte simplă şi foarte utilă
+Comment[ru]=Самый обычный, а потому и очень полезный интерфейс
+Comment[se]=Hui dábálaš ja dannege geavahahtti lakta
+Comment[sk]=Veľmi obyčajné, a preto použiteľné, rozhranie
+Comment[sl]=Zelo običajen in zato zelo uporaben vmesnik
+Comment[sr]=Веома обичан, стога и веома лак за коришћење, интерфејс
+Comment[sr@Latn]=Veoma običan, stoga i veoma lak za korišćenje, interfejs
+Comment[sv]=Ett väldigt vanligt och därför väldigt användbart gränssnitt
+Comment[ta]=மிக எளிய, பயனுள்ள இடைமுகம்
+Comment[tg]=Интерфейси хеле содда ва бинобар бисёр фоиданок
+Comment[th]=ส่วนติดต่อแบบง่ายๆ และด้วยเหตุนั้นก็เลยมีประโยชน์อย่างมาก
+Comment[tr]=Oldukça basit ve kullanışlı bir arayüz
+Comment[uk]=Дуже звичний, і тому дуже зручний, інтерфейс
+Comment[ven]=Zwithu zwazwo, zwo ralo zwa vhuthogwa, interface
+Comment[xh]=Eqheleke kakhulu, neluncedo kakhulu, injongano
+Comment[zh_CN]=非常普通,因而也非常有用的接口
+Comment[zh_HK]=非常普通但是很實用的介面
+Comment[zh_TW]=非常普通但是很實用的介面
+Comment[zu]=Ejwayelekile ngakhoke esebenzisekayo,uxhumano olubhekeneyo
diff --git a/noatun/modules/excellent/excellentui.rc b/noatun/modules/excellent/excellentui.rc
new file mode 100644
index 00000000..f9c43c70
--- /dev/null
+++ b/noatun/modules/excellent/excellentui.rc
@@ -0,0 +1,45 @@
+<!DOCTYPE kpartgui>
+<!--
+vim: syntax=xml
+-->
+<kpartgui name="noatunexcellent" version="4">
+<MenuBar>
+ <Menu name="file" noMerge="1"><text>&amp;File</text>
+ <Action name="file_open"/>
+ <Separator lineSeparator="true"/>
+ <Action name="file_quit"/>
+ </Menu>
+ <Menu name="go_music" noMerge="1"><text>&amp;Go</text>
+ <Action name="back"/>
+ <Action name="stop"/>
+ <Action name="play"/>
+ <Action name="pause"/>
+ <Action name="forward"/>
+ </Menu>
+ <Menu name="settings" noMerge="1"><text>&amp;Settings</text>
+ <Merge name="StandardToolBarMenuHandler" />
+ <Action name="options_show_toolbar"/>
+ <Action name="options_show_menubar"/>
+ <Action name="show_playlist"/>
+ <Action name="show_volumecontrol"/>
+ <Separator lineSeparator="true"/>
+ <Action name="effects"/>
+ <Action name="equalizer"/>
+ <Action name="loop_style"/>
+ <Separator lineSeparator="true"/>
+ <Action name="options_configure"/>
+ </Menu>
+</MenuBar>
+<Toolbar name="main"><text>Main Toolbar</text>
+ <Action name="back"/>
+ <Action name="stop"/>
+ <Action name="play"/>
+ <Action name="pause"/>
+ <Action name="forward"/>
+ <Separator lineSeparator="true"/>
+ <Action name="file_open"/>
+ <Action name="show_playlist"/>
+ <Separator lineSeparator="true"/>
+ <Action name="loop_style"/>
+</Toolbar>
+</kpartgui>
diff --git a/noatun/modules/excellent/noatunui.cpp b/noatun/modules/excellent/noatunui.cpp
new file mode 100644
index 00000000..a103268d
--- /dev/null
+++ b/noatun/modules/excellent/noatunui.cpp
@@ -0,0 +1,32 @@
+// noatunui.cpp
+//
+// Copyright (C) 2000 Neil Stevens <multivac@fcmail.com>
+// Copyright (C) 1999 Charles Samuels <charles@kde.org>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#include "userinterface.h"
+
+extern "C" KDE_EXPORT Plugin *create_plugin()
+{
+ return new Excellent();
+}
diff --git a/noatun/modules/excellent/userinterface.cpp b/noatun/modules/excellent/userinterface.cpp
new file mode 100644
index 00000000..7f218e98
--- /dev/null
+++ b/noatun/modules/excellent/userinterface.cpp
@@ -0,0 +1,394 @@
+// userinterface.cpp
+//
+// Copyright (C) 2001 Neil Stevens <neil@qualityassistant.com>
+// Copyright (C) 1999 Charles Samuels <charles@kde.org>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <noatun/effects.h>
+#include <noatun/app.h>
+#include <noatun/controls.h>
+#include <noatun/pref.h>
+#include <noatun/player.h>
+
+#include "userinterface.h"
+
+#include <kbuttonbox.h>
+#include <kconfig.h>
+#include <kfiledialog.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmenubar.h>
+#include <kmessagebox.h>
+#include <kpixmapeffect.h>
+#include <kpopupmenu.h>
+#include <kstatusbar.h>
+#include <kstdaction.h>
+#include <kwin.h>
+#include <kurldrag.h>
+
+#include <qbitmap.h>
+#include <qdragobject.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qobjectlist.h>
+#include <qobjectdict.h>
+#include <qpushbutton.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+
+Excellent::Excellent()
+ : KMainWindow(0, "NoatunExcellent")
+ , UserInterface()
+{
+ setAcceptDrops(true);
+
+ KStdAction::quit(napp, SLOT(quit()), actionCollection());
+ KStdAction::open(napp, SLOT(fileOpen()), actionCollection());
+ setStandardToolBarMenuEnabled(true);
+
+ menubarAction = KStdAction::showMenubar(this, SLOT(showMenubar()), actionCollection());
+ KStdAction::preferences(napp, SLOT(preferences()), actionCollection());
+
+ // buttons
+ NoatunStdAction::back(actionCollection(), "back");
+ NoatunStdAction::stop(actionCollection(), "stop");
+ NoatunStdAction::play(actionCollection(), "play");
+ NoatunStdAction::pause(actionCollection(), "pause");
+ NoatunStdAction::forward(actionCollection(), "forward");
+ NoatunStdAction::playlist(actionCollection(), "show_playlist");
+
+ volumeAction = new KToggleAction(i18n("Show &Volume Control"), 0, this, SLOT(showVolumeControl()), actionCollection(), "show_volumecontrol");
+ volumeAction->setCheckedState(i18n("Hide &Volume Control"));
+ NoatunStdAction::effects(actionCollection(), "effects");
+ NoatunStdAction::equalizer(actionCollection(), "equalizer");
+
+ NoatunStdAction::loop(actionCollection(), "loop_style");
+
+ createGUI("excellentui.rc");
+
+ napp->pluginActionMenu()->plug(menuBar(),3);
+ toolBar("mainToolBar")->hide();
+
+ // Who needs Qt Designer?
+ mainFrame = new QHBox(this);
+ mainFrame->setSpacing(KDialog::spacingHint());
+ mainFrame->setMargin(0);
+ slider = new L33tSlider(0, 1000, 10, 0, L33tSlider::Horizontal, mainFrame);
+ slider->setTickmarks(QSlider::NoMarks);
+
+ elapsed = new QLabel(mainFrame);
+ QFont labelFont = elapsed->font();
+ labelFont.setPointSize(24);
+ labelFont.setBold(true);
+ QFontMetrics labelFontMetrics = labelFont;
+ elapsed->setFont(labelFont);
+ elapsed->setAlignment(AlignCenter | AlignVCenter | ExpandTabs);
+ elapsed->setText("--:--");
+ elapsed->setFixedHeight(labelFontMetrics.height());
+ elapsed->setMinimumWidth(elapsed->sizeHint().width());
+
+ // Doing this makes the slider the same heigh as the font. This is just plain wrong...
+ //slider->setFixedHeight(labelFontMetrics.height());
+
+ setCentralWidget(mainFrame);
+
+ total = new QLabel(statusBar());
+ labelFont = total->font();
+ labelFont.setBold(true);
+ total->setFont(labelFont);
+ total->setAlignment(AlignCenter | AlignVCenter | ExpandTabs);
+ total->setText("--:--");
+ total->setMinimumWidth(total->sizeHint().width());
+ total->setText("");
+
+ statusBar()->addWidget(total, 0, true);
+ statusBar()->show();
+
+ connect( napp, SIGNAL(hideYourself()), this, SLOT(hide()) );
+ connect( napp, SIGNAL(showYourself()), this, SLOT(show()) );
+
+ connect(napp->player(), SIGNAL(playing()), this, SLOT(slotPlaying()));
+ connect(napp->player(), SIGNAL(stopped()), this, SLOT(slotStopped()));
+ connect(napp->player(), SIGNAL(paused()), this, SLOT(slotPaused()));
+ napp->player()->handleButtons();
+
+ connect(napp->player(), SIGNAL(timeout()), this, SLOT(slotTimeout()));
+ connect(napp->player(), SIGNAL(loopTypeChange(int)), this, SLOT(slotLoopTypeChanged(int)));
+
+ /* This skipToWrapper is needed to pass milliseconds to Player() as everybody
+ * below the GUI is based on milliseconds instead of some unprecise thingy
+ * like seconds or mille */
+ connect(slider, SIGNAL(userChanged(int)), this, SLOT(skipToWrapper(int)));
+ connect(this, SIGNAL(skipTo(int)), napp->player(), SLOT(skipTo(int)));
+
+ connect(slider, SIGNAL(sliderMoved(int)), SLOT(sliderMoved(int)));
+
+ setCaption("Noatun");
+ setIcon(SmallIcon("noatun"));
+ //slotLoopTypeChanged(Player::None);
+ changeStatusbar();
+ handleLengthString("--:--/--:--");
+
+ setMinimumWidth(250);
+ resize(300, 75);
+
+ KConfig &config = *KGlobal::config();
+
+ toolBar("main")->applySettings(&config, "Excellent main");
+
+ config.setGroup("excellent");
+
+ volumeSlider = 0;
+ volumeAction->setChecked( config.readBoolEntry("volumeShown", false) );
+ showVolumeControl();
+
+ menubarAction->setChecked( config.readBoolEntry("menuShown", true) );
+ showMenubar();
+
+ applyMainWindowSettings(&config, "excellent");
+
+ switch((NET::MappingState)config.readNumEntry("mappingState", (int)NET::Visible))
+ {
+ case NET::Visible:
+ showNormal();
+ break;
+ case NET::Withdrawn:
+ if (napp->libraryLoader()->isLoaded("systray.plugin"))
+ hide();
+ break;
+ case NET::Iconic:
+ showMinimized();
+ break;
+ }
+
+ for (QPtrListIterator<QObject> i(*children()); i.current(); ++i)
+ (*i)->installEventFilter(this);
+}
+
+Excellent::~Excellent()
+{
+ KConfig &config = *KGlobal::config();
+ saveMainWindowSettings(&config, "excellent");
+ toolBar("main")->saveSettings(&config, "Excellent main");
+ config.setGroup("excellent");
+ config.writeEntry("volumeShown", volumeAction->isChecked());
+ config.writeEntry("menuShown", menubarAction->isChecked());
+ config.writeEntry("width", width());
+ config.writeEntry("height", height());
+ config.sync();
+}
+
+void Excellent::showEvent(QShowEvent *e)
+{
+ KConfig *config = KGlobal::config();
+ config->setGroup("excellent");
+ config->writeEntry("mappingState", NET::Visible);
+ config->sync();
+
+ KMainWindow::showEvent(e);
+}
+
+void Excellent::hideEvent(QHideEvent *e)
+{
+ KConfig *config = KGlobal::config();
+ config->setGroup("excellent");
+ config->writeEntry("mappingState", NET::Withdrawn);
+ config->sync();
+
+ KMainWindow::hideEvent(e);
+}
+
+void Excellent::closeEvent(QCloseEvent *)
+{
+ unload();
+}
+
+void Excellent::dragEnterEvent(QDragEnterEvent *event)
+{
+ // accept uri drops only
+ event->accept(KURLDrag::canDecode(event));
+}
+
+void Excellent::dropEvent(QDropEvent *event)
+{
+ KURL::List uri;
+ if (KURLDrag::decode(event, uri))
+ {
+ for (KURL::List::Iterator i = uri.begin(); i != uri.end(); ++i)
+ napp->player()->openFile(*i, false);
+ }
+}
+
+bool Excellent::eventFilter(QObject *o, QEvent *e)
+{
+ if (e->type() == QEvent::Wheel)
+ {
+ wheelEvent(static_cast<QWheelEvent*>(e));
+ return true;
+ }
+ return QWidget::eventFilter(o, e);
+}
+
+void Excellent::wheelEvent(QWheelEvent *e)
+{
+ int delta=e->delta();
+ napp->player()->setVolume(napp->player()->volume() + delta/120*2);
+}
+
+void Excellent::slotPlaying()
+{
+ slider->setEnabled(true);
+ changeStatusbar();
+}
+
+void Excellent::slotStopped()
+{
+ slider->setEnabled(false);
+ if(!napp->player()->current()) return;
+ changeStatusbar();
+ slider->setValue(0);
+ handleLengthString("--:--/--:--");
+}
+
+void Excellent::slotPaused()
+{
+ slider->setEnabled(true);
+ changeStatusbar();
+}
+
+void Excellent::slotTimeout()
+{
+ if(volumeSlider) volumeSlider->setValue(100 - napp->player()->volume());
+
+ if (!slider->currentlyPressed())
+ handleLengthString(napp->player()->lengthString());
+
+ if(!napp->player()->current()) return;
+ if(slider->currentlyPressed()) return;
+
+ slider->setRange(0, (int)napp->player()->getLength() / 1000 );
+ slider->setValue((int)napp->player()->getTime() / 1000 );
+
+ changeStatusbar();
+}
+
+void Excellent::sliderMoved(int seconds)
+{
+ if(napp->player()->current())
+ handleLengthString(napp->player()->lengthString(seconds * 1000));
+}
+
+void Excellent::skipToWrapper(int second) // wrap int to int _and_ seconds to mseconds
+{
+ emit skipTo((int)(second*1000));
+}
+
+void Excellent::slotLoopTypeChanged(int type)
+{
+ static const int time = 2000;
+ switch (type)
+ {
+ case Player::None:
+ statusBar()->message(i18n("No looping"), time);
+ break;
+ case Player::Song:
+ statusBar()->message(i18n("Song looping"), time);
+ break;
+ case Player::Playlist:
+ statusBar()->message(i18n("Playlist looping"), time);
+ break;
+ case Player::Random:
+ statusBar()->message(i18n("Random play"), time);
+ }
+}
+
+void Excellent::showMenubar(void)
+{
+ if(menubarAction->isChecked())
+ {
+ menuBar()->show();
+ }
+ else
+ {
+ KMessageBox::information(this, i18n("<qt>Press %1 to show the menubar.</qt>").arg(menubarAction->shortcut().toString()), QString::null, "Hide Menu warning");
+ menuBar()->hide();
+ }
+}
+
+void Excellent::showVolumeControl(void)
+{
+ if(volumeAction->isChecked())
+ growVolumeControl();
+ else
+ shrinkVolumeControl();
+}
+
+void Excellent::changeStatusbar(void)
+{
+ if(!napp->player()->current().isNull())
+ statusBar()->message(napp->player()->current().title());
+}
+
+void Excellent::handleLengthString(const QString &text)
+{
+ if(text.right(5) == "00:00" && text.left(5) == "00:00")
+ {
+ elapsed->setText("--:--");
+ total->setText("--:--");
+ }
+ else
+ {
+ // Split the length string in to "current" and "elapsed"
+ QStringList tokens = QStringList::split("/", text);
+
+ elapsed->setText(tokens[0]);
+ total->setText(tokens[1]);
+ }
+}
+
+void Excellent::growVolumeControl(void)
+{
+ volumeSlider = new L33tSlider(0, 100, 10, 0, Vertical, mainFrame);
+ volumeSlider->setValue(100 - napp->player()->volume());
+ volumeSlider->show();
+ connect(volumeSlider, SIGNAL(sliderMoved(int)), SLOT(changeVolume(int)));
+ connect(volumeSlider, SIGNAL(userChanged(int)), SLOT(changeVolume(int)));
+}
+
+void Excellent::shrinkVolumeControl(void)
+{
+ delete volumeSlider;
+ volumeSlider = 0;
+}
+
+void Excellent::changeVolume(int slider)
+{
+ napp->player()->setVolume(100 - slider);
+}
+
+#include "userinterface.moc"
diff --git a/noatun/modules/excellent/userinterface.h b/noatun/modules/excellent/userinterface.h
new file mode 100644
index 00000000..588db6e4
--- /dev/null
+++ b/noatun/modules/excellent/userinterface.h
@@ -0,0 +1,101 @@
+// userinterface.h
+//
+// Copyright (C) 2000, 2001 Neil Stevens <multivac@fcmail.com>
+// Copyright (C) 1999 Charles Samuels <charles@kde.org>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifndef USERINTERFACE_H
+#define USERINTERFACE_H
+
+#include <noatun/plugin.h>
+#include <noatun/app.h>
+#include <noatun/stdaction.h>
+#include <kmainwindow.h>
+
+class KAction;
+class KPopupMenu;
+class KStatusBar;
+class Player;
+class QHBox;
+class QLabel;
+class QSlider;
+class L33tSlider;
+class KToggleAction;
+
+/**
+ * @short Main window class
+ * @author Neil Stevens <multivac@fcmail.com>
+ * @author Charles Samuels <charles@kde.org>
+ */
+class Excellent : public KMainWindow, public UserInterface
+{
+Q_OBJECT
+
+public:
+ Excellent();
+ virtual ~Excellent();
+ void load(const QString& url);
+
+protected:
+ virtual void showEvent(QShowEvent *);
+ virtual void hideEvent(QHideEvent *);
+ virtual void closeEvent(QCloseEvent *);
+ virtual void dragEnterEvent(QDragEnterEvent *);
+ virtual void dropEvent(QDropEvent *);
+ void wheelEvent(QWheelEvent *e);
+ bool eventFilter(QObject *o, QEvent *e);
+
+public slots:
+ void slotPlaying();
+ void slotStopped();
+ void slotPaused();
+
+ void slotTimeout();
+ void sliderMoved(int seconds);
+ void slotLoopTypeChanged(int type);
+ void skipToWrapper(int second);
+
+signals:
+ // emitted by skipToWrapper()
+ void skipTo(int);
+
+private slots:
+ void showVolumeControl(void);
+ void showMenubar(void);
+ void changeStatusbar(void);
+ void changeVolume(int);
+
+ void handleLengthString(const QString &text);
+
+private:
+ void growVolumeControl(void);
+ void shrinkVolumeControl(void);
+
+ QHBox *mainFrame;
+
+ KToggleAction *volumeAction, *menubarAction;
+ L33tSlider *volumeSlider, *slider;
+ QLabel *elapsed, *total;
+};
+
+#endif
diff --git a/noatun/modules/htmlexport/Makefile.am b/noatun/modules/htmlexport/Makefile.am
new file mode 100644
index 00000000..bbea16ab
--- /dev/null
+++ b/noatun/modules/htmlexport/Makefile.am
@@ -0,0 +1,16 @@
+
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_htmlexport.la
+
+noatun_htmlexport_la_SOURCES = htmlexport.cpp
+noinst_HEADERS = htmlexport.h
+
+noatun_htmlexport_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_htmlexport_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la \
+ -lqtmcop -lkmedia2_idl -lsoundserver_idl
+
+noatun_htmlexport_la_METASOURCES = AUTO
+
+
+noatun_modules_htmlexport_DATA = htmlexport.plugin
+noatun_modules_htmlexportdir = $(kde_datadir)/noatun
diff --git a/noatun/modules/htmlexport/TODO b/noatun/modules/htmlexport/TODO
new file mode 100644
index 00000000..6f97ad78
--- /dev/null
+++ b/noatun/modules/htmlexport/TODO
@@ -0,0 +1,3 @@
+Most important TODO items:
+* Read tags before writing the file
+* Clean up the configure dialog and fix remaining issues
diff --git a/noatun/modules/htmlexport/htmlexport.cpp b/noatun/modules/htmlexport/htmlexport.cpp
new file mode 100644
index 00000000..dc48fd8f
--- /dev/null
+++ b/noatun/modules/htmlexport/htmlexport.cpp
@@ -0,0 +1,308 @@
+#include <klocale.h>
+#include <qregexp.h>
+#include <qtextcodec.h>
+#include <kaction.h>
+#include <noatun/stdaction.h>
+#include "htmlexport.h"
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new HTMLExport();
+ }
+}
+
+HTMLExport::HTMLExport(): QObject(0, "HTMLExport"), Plugin()
+{
+ NOATUNPLUGINC(HTMLExport);
+
+ mAction = new KAction(i18n("&Export Playlist..."), "filesaveas", 0,
+ this, SLOT(slotExport()), this, "exportlist");
+ napp->pluginActionMenu()->insert(mAction);
+
+ new Prefs(this);
+ config = KGlobal::config();
+}
+
+HTMLExport::~HTMLExport()
+{
+ napp->pluginActionMenu()->remove(mAction);
+}
+
+void HTMLExport::slotExport()
+{
+ // init readConfig
+ config->setGroup("HTMLExport");
+
+ // get output target
+ KURL url = KFileDialog::getSaveURL(QString::null,
+ "text/html",
+ 0,
+ i18n("Export Playlist"));
+
+ // write into tempfile
+ KTempFile temp;
+ temp.setAutoDelete(true);
+ QFile file(temp.name());
+ file.open(IO_WriteOnly);
+ QTextStream str(&file);
+ str.setCodec(QTextCodec::codecForName("utf8"));
+
+ str << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
+ str << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">" << endl;
+ str << "<!-- Generated by Noatun " << NOATUN_MAJOR << "." << NOATUN_MINOR << "." << NOATUN_PATCHLEVEL << " -->" << endl;
+
+ str << endl;
+
+ str << "<html>" << endl;
+ str << "<head>" << endl;
+ str << "\t<title>" << i18n("Noatun Playlist") << "</title>" << endl;
+
+ str << "\t<style type=\"text/css\">" << endl;
+ str << "\t\t<!--" << endl;
+ // Set the heading color
+ str << "\t\th1 { color:#"<< getColorByEntry("headColor")<<"; }" << endl;
+
+ // Optionally set the background image
+ if (!config->readPathEntry( "bgImgPath" ).stripWhiteSpace().isEmpty()) {
+ // Image
+ str << "\t\tp,li { color:#"<< getColorByEntry("txtColor") << "; }" << endl;
+ str << "\t\tbody { background-image: url(\"" << config->readPathEntry( "bgImgPath" ) << "\"); }" << endl;
+ }
+ else {
+ // Color
+ str << "\t\tp,li,body { color:#"<< getColorByEntry("txtColor");
+ str << "; background-color:#" << getColorByEntry( "bgColor" ) << "; }" << endl;
+ }
+
+ // Links are text colored, too
+ str << "\t\ta { color:#" << getColorByEntry("txtColor") << "; }" << endl;
+ if (getColorByEntry("hoverColor") != getColorByEntry("txtColor"))
+ str << "\t\ta:hover { color:#"<< getColorByEntry("hoverColor")<<"; }"<< endl;
+
+ str << "\t\t-->" << endl;
+ str << "\t</style>" << endl;
+
+ str << "</head>" << endl;
+ str << endl;
+ str << "<body>" << endl;
+ str << "\t<h1>" << i18n("Noatun Playlist") << "</h1>" << endl;
+
+ // Cache the config settings used in the big loop
+ bool link_entries = config->readBoolEntry( "linkEntries" );
+ bool number_entries = config->readBoolEntry( "numberEntries" );
+
+ if (number_entries)
+ str << "\t\t<ol>" << endl;
+ else
+ str << "\t\t<p>" << endl;
+
+
+ for (PlaylistItem item = napp->playlist()->getFirst();item;item = napp->playlist()->getAfter(item)) {
+ str << "\t\t\t";
+
+ if (number_entries)
+ str << "<li>";
+
+ if (link_entries)
+ str << "<a href=\"" << htmlEscape(item.file()) << "\">";
+
+ str << htmlEscape(item.title());
+
+ if (link_entries)
+ str << "</a>";
+
+ if (number_entries)
+ str << "</li>" << endl;
+ else
+ str << "<br />" << endl;
+ }
+
+ if (number_entries)
+ str << "\t\t</ol>" << endl;
+ else
+ str << "\t\t</p>" << endl;
+
+ str << "\t</body>" << endl;
+ str << "</html>" << endl;
+
+ file.close();
+ // tempfile -> userdefined file
+ KIO::NetAccess::upload(temp.name(), url, 0);
+}
+
+QString HTMLExport::htmlEscape(const QString &source) {
+ // Escape characters that need to be escaped
+ QString temp = source;
+
+ temp.replace( QRegExp("&"), "&amp;" );
+ temp.replace( QRegExp("<"), "&lt;" );
+ temp.replace( QRegExp(">"), "&gt;" );
+
+ return temp;
+}
+
+QString HTMLExport::getColorByEntry(QString s)
+{
+ QString res;
+ QString tmp;
+ QColor c;
+
+ // init readConfig
+
+ config->setGroup("HTMLExport");
+
+ c = config->readColorEntry( s );
+ tmp = QString::number( c.red(), 16);
+ if (tmp.length()==1) tmp="0"+tmp;
+ res = tmp;
+
+ tmp = QString::number( c.green(), 16);
+ if (tmp.length()==1) tmp="0"+tmp;
+ res += tmp;
+
+ tmp = QString::number( c.blue(), 16);
+ if (tmp.length()==1) tmp="0"+tmp;
+ res += tmp;
+
+ return res;
+
+}
+//////////////////////////////////// Settings ////////////////////////////////////
+
+Prefs::Prefs(QObject *parent)
+ : CModule(i18n("Playlist Export"), i18n("Colors & Settings for HTML Export"), "html", parent)
+{
+
+ // Init Config
+ KConfig *config = KGlobal::config();
+ config->setGroup("HTMLExport");
+
+ // Set default values
+ if ( !config->hasKey( "headColor" ) )
+ config->writeEntry( "headColor", QColor( black ) ) ;
+
+ if ( !config->hasKey( "hoverColor" ) )
+ config->writeEntry( "hoverColor", QColor( black ) );
+
+ if ( !config->hasKey( "bgColor" ) )
+ config->writeEntry( "bgColor", QColor( white ) ) ;
+
+ if ( !config->hasKey( "txtColor" ) )
+ config->writeEntry( "txtColor", QColor( black ) );
+
+ config->sync();
+
+ // Draw Stuff and insert Settings
+ QVBoxLayout *top = new QVBoxLayout(this, KDialog::marginHint(),
+ KDialog::spacingHint() );
+
+ colorBox = new QGroupBox( i18n( "HTML Color Settings" ), this, "colorBox" );
+
+ bgcolorLabel = new QGridLayout( colorBox, 2, 5,
+ KDialog::marginHint(), KDialog::spacingHint() );
+
+ headColorSelect = new KColorButton( colorBox, "headColorSelect" );
+
+ hoverColorSelect = new KColorButton( colorBox, "hoverColorSelect" );
+
+ bgColorSelect = new KColorButton( colorBox, "bgColorSelect" );
+
+ txtColorSelect = new KColorButton( colorBox, "txtColorSelect" );
+
+ txtColorLabel = new QLabel( colorBox, "txtColorLabel" );
+ txtColorLabel->setText( i18n( "Text:" ) );
+ txtColorLabel->setAlignment( int( QLabel::AlignVCenter | QLabel::AlignRight ) );
+
+ bgColorLabel = new QLabel( colorBox, "bgColorLabel" );
+ bgColorLabel->setText( i18n( "Background:" ) );
+ bgColorLabel->setAlignment( int( QLabel::AlignVCenter | QLabel::AlignRight ) );
+
+ headColorLabel = new QLabel( colorBox, "headColorLabel" );
+ headColorLabel->setText( i18n( "Heading:" ) );
+ headColorLabel->setAlignment( int( QLabel::AlignVCenter | QLabel::AlignRight ) );
+
+ hoverColorLabel = new QLabel( colorBox, "hoverColorLabel" );
+ hoverColorLabel->setText( i18n( "Link hover:" ) );
+ hoverColorLabel->setAlignment( int( QLabel::AlignVCenter | QLabel::AlignRight ) );
+
+ bgcolorLabel->setRowStretch(0, 1);
+
+ // Makes the spacing nice
+ bgcolorLabel->setColStretch(1, 2);
+ bgcolorLabel->setColStretch(2, 1);
+ bgcolorLabel->setColStretch(4, 2);
+
+ bgcolorLabel->addWidget( txtColorLabel, 0, 0 );
+ bgcolorLabel->addWidget( txtColorSelect, 0, 1 );
+ bgcolorLabel->addWidget( headColorLabel, 1, 0 );
+ bgcolorLabel->addWidget( headColorSelect, 1, 1 );
+ bgcolorLabel->addWidget( bgColorLabel, 0, 3 );
+ bgcolorLabel->addWidget( bgColorSelect, 0, 4 );
+ bgcolorLabel->addWidget( hoverColorLabel, 1, 3 );
+ bgcolorLabel->addWidget( hoverColorSelect, 1, 4 );
+
+
+ // Set up the Background Image frame
+ bgPicBox = new QHGroupBox( i18n( "Background Image"), this, "bgPicBox" );
+
+ // Set up the URL requestor
+ bgPicPath = new KURLRequester ( bgPicBox, "bgPicPath" );
+ bgPicPath->setShowLocalProtocol(true);
+
+ // Set up the URL requestor's file dialog
+ bgPicPath->setMode(KFile::File | KFile::ExistingOnly);
+ bgPicPath->setFilter("image/gif image/jpeg image/gif image/png image/svg+xml image/tiff");
+
+ linkEntries = new QCheckBox( this, "linkEntries" );
+ linkEntries->setText( i18n( "Hyper&link playlist entries to their URL" ) );
+ linkEntries->setTristate( false );
+
+ numberEntries = new QCheckBox( this, "numberEntries" );
+ numberEntries->setText( i18n( "&Number playlist entries" ) );
+ numberEntries->setTristate( false );
+
+ top->addWidget( colorBox );
+ top->addWidget( bgPicBox );
+ top->addWidget( linkEntries );
+ top->addWidget( numberEntries );
+
+ top->addStretch();
+
+ reopen();
+}
+
+void Prefs::save()
+{
+ KConfig *config = KGlobal::config();
+
+ QString bgRealURL = bgPicPath->url();
+
+ if (bgRealURL[0] == '/')
+ bgRealURL = "file:" + bgRealURL;
+
+ config->setGroup( "HTMLExport" );
+ config->writeEntry( "bgColor", bgColorSelect->color() );
+ config->writeEntry( "txtColor", txtColorSelect->color() );
+ config->writeEntry( "headColor", headColorSelect->color() );
+ config->writeEntry( "hoverColor", hoverColorSelect->color() );
+ config->writePathEntry( "bgImgPath", bgRealURL );
+ config->writeEntry( "linkEntries", linkEntries->isChecked() );
+ config->writeEntry( "numberEntries", numberEntries->isChecked() );
+ config->sync();
+}
+
+void Prefs::reopen()
+{
+ KConfig *config = KGlobal::config();
+ headColorSelect->setColor(config->readColorEntry( "headColor" ) );
+ hoverColorSelect->setColor( config->readColorEntry( "hoverColor" ) );
+ bgColorSelect->setColor( config->readColorEntry( "bgColor" ) );
+ txtColorSelect->setColor( config->readColorEntry( "txtColor" ) );
+ bgPicPath->setURL( config->readPathEntry( "bgImgPath" ) );
+ numberEntries->setChecked( config->readBoolEntry( "numberEntries" ) );
+ linkEntries->setChecked( config->readBoolEntry( "linkEntries" ) );
+}
+#include "htmlexport.moc"
+
diff --git a/noatun/modules/htmlexport/htmlexport.h b/noatun/modules/htmlexport/htmlexport.h
new file mode 100644
index 00000000..a909a5cb
--- /dev/null
+++ b/noatun/modules/htmlexport/htmlexport.h
@@ -0,0 +1,89 @@
+
+#ifndef _HTMLEXPORT_H_
+#define _HTMLEXPORT_H_
+
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qlabel.h>
+#include <qhgroupbox.h>
+#include <qlineedit.h>
+#include <qcheckbox.h>
+#include <qpushbutton.h>
+#include <qlayout.h>
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kfiledialog.h>
+#include <ktempfile.h>
+#include <kcolorbutton.h>
+#include <kio/netaccess.h>
+#include <kconfig.h>
+#include <kurlrequester.h>
+
+//#include <kdebug.h>
+
+#include <noatun/app.h>
+#include <noatun/playlist.h>
+#include <noatun/pref.h>
+#include <noatun/plugin.h>
+
+class KAction;
+
+class HTMLExport : public QObject, public Plugin
+{
+Q_OBJECT
+NOATUNPLUGIND
+public:
+ HTMLExport();
+ ~HTMLExport();
+
+
+private:
+ QString htmlEscape(const QString &source);
+ QString getColorByEntry(QString s);
+ KConfig *config;
+ KAction *mAction;
+
+private slots:
+ void slotExport();
+
+};
+
+class Prefs : public CModule
+{
+Q_OBJECT
+public:
+ Prefs(QObject *parent);
+ virtual void save();
+ virtual void reopen();
+
+ QGroupBox* colorBox;
+
+ KColorButton* headColorSelect;
+ KColorButton* hoverColorSelect;
+ KColorButton* bgColorSelect;
+ KColorButton* txtColorSelect;
+
+ QLabel* bgColorLabel;
+ QLabel* txtColorLabel;
+ QLabel* headColorLabel;
+ QLabel* hoverColorLabel;
+
+ QCheckBox* linkEntries;
+ QCheckBox* numberEntries;
+
+ QGroupBox* bgPicBox;
+ KURLRequester* bgPicPath;
+
+protected:
+ QGridLayout* bgcolorLabel;
+
+
+signals:
+ void saved();
+};
+
+#endif
+
diff --git a/noatun/modules/htmlexport/htmlexport.plugin b/noatun/modules/htmlexport/htmlexport.plugin
new file mode 100644
index 00000000..97a10fb9
--- /dev/null
+++ b/noatun/modules/htmlexport/htmlexport.plugin
@@ -0,0 +1,124 @@
+Filename=noatun_htmlexport.la
+Author=Malte Starostik
+Email=malte@kde.org
+Type=other
+License=Artistic
+Name=HTML Playlist Export
+Name[bn]=এইচ-টি-এম-এল সঙ্গীত-তালিকা রপ্তানি
+Name[br]=Ezporzh ar roll tonioù e HTML
+Name[bs]=Izvoz liste numera u HTML
+Name[ca]=Exportació del repertori en HTML
+Name[cs]=Export seznamu skladeb do HTML
+Name[cy]=Allforio Rhestr Chwarae HTML
+Name[da]=HTML-spilleliste-eksport
+Name[de]=Export von Wiedergabelisten in HTML
+Name[el]=Εξαγωγή λίστας αναπαραγωγής HTML
+Name[eo]=HTML-ludlisteksporto
+Name[es]=Exportador de listas de reproducción HTML
+Name[et]=Esitusnimekirja eksport HTML-i
+Name[eu]=HTML erreprodukzio-zerrendaren esportazioa
+Name[fa]=صادرات فهرست پخش زنگام
+Name[fi]=HTML-soittolistan vienti
+Name[fr]=Export en HTML des listes de lecture
+Name[ga]=Easpórtáil Seinmliosta mar HTML
+Name[gl]=Exportación de Lista de Reprodución a HTML
+Name[he]=ייצוא לרשימת ניגון בתבנית HTML
+Name[hu]=Lejátszási lista exportálása HTML-fájlba
+Name[is]=Útflutningur á HTML lagalista
+Name[it]=Esportazione HTML delle playlist
+Name[ja]=HTML プレイリストのエクスポート
+Name[kk]=Орындау тізімін HTML-экспорттау
+Name[km]=នាំចេញ​បញ្ជី​ចាក់ HTML
+Name[ko]=HTML 재생 목록 내보내기
+Name[lt]=HTML gaidaraščio eksportas
+Name[mk]=Испраќање на листа со нумери како HTML
+Name[nb]=Eksport av HTML-spilleliste
+Name[nds]=Afspeellist-Export na HTML
+Name[ne]=HTML बजाउने सूची निर्यात
+Name[nl]=HTML-afspeellijst-export
+Name[nn]=Eksport av HTML-speleliste
+Name[pa]=HTML ਸੰਗੀਤ-ਸੂਚੀ ਨਿਰਯਾਤ
+Name[pl]=Eksport listy utworów do formatu HTML
+Name[pt]=Exportação de Listas de Músicas em HTML
+Name[pt_BR]=Exportação de listas de reprodução em HTML
+Name[ro]=Exportare HTML listă de redare
+Name[ru]=Экспорт списка песен в HTML
+Name[sk]=Export playlistu do HTML
+Name[sl]=Izvoz predvajalnega seznama v HTML
+Name[sr]=Извожење листе нумера у HTML
+Name[sr@Latn]=Izvoženje liste numera u HTML
+Name[sv]=HTML-spellistexport
+Name[ta]=HTML பாடல் பட்டியல் ஏற்றுமதி
+Name[th]=ส่งออกรายการเล่นเป็น HTML
+Name[tr]=HTML Çalma Listesi Dışarı Aktarma
+Name[uk]=Експорт списку композицій до HTML
+Name[zh_CN]=HTML 播放列表导出
+Name[zh_HK]=HTML 播放清單匯出
+Name[zh_TW]=HTML 播放清單匯出
+Comment=Creates a HTML file from the playlist
+Comment[af]=Skep 'n Html lêer van die liedjielys
+Comment[ar]=ينشئ ملف HTML من لائحة التشغيل
+Comment[az]=Çalma siyahısından bir HTML faylı yarat
+Comment[bg]=Експортиране на списък за изпълнение във файл от тип HTML
+Comment[bn]=সঙ্গীত-তালিকা থেকে একটি এইচ-টি-এম-এল ফাইল তৈরি করে
+Comment[br]=Krouiñ ur restr HTML eus ar roll tonioù
+Comment[bs]=Pravi HTML dokument od liste numera
+Comment[ca]=Crea un fitxer HTML d'una selecció de peces
+Comment[cs]=Vytváří ze seznamu skladeb HTML soubor
+Comment[cy]=Creu ffeil HTML o'r rhestr chwarae
+Comment[da]=Opretter en HTML-fil fra spillelisten
+Comment[de]=Erstellt eine HTML-Datei aus einer Wiedergabeliste
+Comment[el]=Δημιουργεί ένα αρχείο HTML από τη λίστα αναπαραγωγής
+Comment[en_GB]=Creates an HTML file from the playlist
+Comment[eo]=Kreas HTML-dosieron el la ludlisto
+Comment[es]=Crea un archivo HTML a partir de la lista de reproducción
+Comment[et]=HTML-faili loomine nimekirjast
+Comment[eu]=HTML fitxategia erreprodukzio-zerrendatik sortzen du
+Comment[fa]=از فهرست پخش یک پروندۀ زنگام ایجاد می‌‌کند
+Comment[fi]=Luo HTML-tiedoston soittolistasta
+Comment[fr]=Crée un fichier HTML à partir de la liste de lecture
+Comment[ga]=Cruthaigh comhad HTML ón seinmliosta
+Comment[gl]=Crea un ficheiro HTML a partires da lista de reprodución
+Comment[he]=יצירת קובץ HTML מתוך רשימת הניגון
+Comment[hi]=प्लेलिस्ट से एचटीएमएल फ़ाइल बनाता है
+Comment[hr]=Stvara od liste pjesama HTML datoteku
+Comment[hu]=Lejátszási lista exportálása HTML-ben
+Comment[is]=Býr til HTML skrá úr lagalistanum
+Comment[it]=Crea un file HTML dalla playlist
+Comment[ja]=プレイリストから HTML を作成
+Comment[kk]=Орындау тізімін HTML-пішімді файлға жазу
+Comment[km]=បង្កើត​បញ្ជី​ឯកសារ HTML ពី​បញ្ជី​ចាក់
+Comment[ko]=재생 목록에서 HTML 파일 만들기
+Comment[lt]=Sukuria HTML failą iš gaidaraščio
+Comment[lv]=Izveido HTML failu no plejlista
+Comment[mk]=Креира датотека HTML од листата со нумери
+Comment[ms]=Bina fail HTML untuk senarai main
+Comment[mt]=Joħloq fajl HTML mill-playlist
+Comment[nb]=Lager en HTML-fil fra spillelisten
+Comment[nds]=Stellt ut de Afspeellist en HTML-Datei op
+Comment[ne]=बजाउने सूचीबाट HTML फाइल सिर्जना गर्दछ
+Comment[nl]=Maakt een HTML-bestand aan op basis van de speellijst
+Comment[nn]=Lagar ei HTML-fil frå spelelista
+Comment[pa]=ਸੰਗੀਤ-ਸੂਚੀ ਤੋਂ ਇੱਕ HTML ਫਾਇਲ ਬਣਾਓ
+Comment[pl]=Tworzy plik HTML z listy nagrań
+Comment[pt]=Cria um ficheiro HTML a partir de uma lista de músicas
+Comment[pt_BR]=Cria um arquivo HTML a partir da lista de reprodução
+Comment[ro]=Creează un fişier HTML dintr-o listă de redare
+Comment[ru]=Создание HTML-файла из списка песен
+Comment[se]=Ráhkada HTML-fiilla čuojahanlisttus
+Comment[sk]=Vytvorí HTML súbor z playlistu
+Comment[sl]=Ustvari datoteko HTML iz predvajalnega seznama
+Comment[sr]=Прави HTML фајл на основу листе нумера
+Comment[sr@Latn]=Pravi HTML fajl na osnovu liste numera
+Comment[sv]=Skapar en HTML-fil av en spellista
+Comment[ta]=பாடல் பட்டியலிலிருந்து HTML ஆவணமொன்றை உருவாக்கின்றது
+Comment[tg]=Аз рӯйхати бозикуниҳо файли HTML-ро офарид
+Comment[th]=สร้างแฟ้ม HTML สำหรับรายการเล่น
+Comment[tr]=Çalma listesinden bir HTML dosyası oluşturur
+Comment[uk]=Створює файл HTML зі списку композицій
+Comment[ven]=Itani faela ya HTML u bva kha mutevhe wa tshitambi
+Comment[xh]=Yenza ifayile ye HTML kuluhlu lomdlalo
+Comment[zh_CN]=从播放列表创建 HTML 文件
+Comment[zh_HK]=從播放清單建立 HTML 檔案
+Comment[zh_TW]=從播放清單建立 HTML 文件檔案
+Comment[zu]=Yenza ifayela ye HTML kuluhlu lomdlalo
diff --git a/noatun/modules/infrared/Makefile.am b/noatun/modules/infrared/Makefile.am
new file mode 100644
index 00000000..bf8a7212
--- /dev/null
+++ b/noatun/modules/infrared/Makefile.am
@@ -0,0 +1,16 @@
+
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_infrared.la
+
+noatun_infrared_la_SOURCES = infrared.cpp lirc.cpp irprefs.cpp
+noinst_HEADERS = infrared.h lirc.h irprefs.h
+
+noatun_infrared_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_infrared_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la \
+ -lqtmcop -lkmedia2_idl -lsoundserver_idl
+
+noatun_infrared_la_METASOURCES = AUTO
+
+
+noatun_modules_infrared_DATA = infrared.plugin
+noatun_modules_infrareddir = $(kde_datadir)/noatun
diff --git a/noatun/modules/infrared/README b/noatun/modules/infrared/README
new file mode 100644
index 00000000..b3fda334
--- /dev/null
+++ b/noatun/modules/infrared/README
@@ -0,0 +1,3 @@
+This plugin enables Noatun to be controlled with an IR remote control.
+You'll need lirc (http://fsinfo.cs.uni-sb.de/~columbus/lirc/) and
+an IR receiver supported by lirc.
diff --git a/noatun/modules/infrared/infrared.cpp b/noatun/modules/infrared/infrared.cpp
new file mode 100644
index 00000000..60ea145c
--- /dev/null
+++ b/noatun/modules/infrared/infrared.cpp
@@ -0,0 +1,120 @@
+
+#include <stdio.h>
+
+#include <unistd.h>
+#include <noatun/player.h>
+#include <noatun/app.h>
+
+#include <klocale.h>
+#include <qtimer.h>
+
+#include "infrared.h"
+#include "lirc.h"
+#include "irprefs.h"
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new InfraRed();
+ }
+}
+
+
+InfraRed::InfraRed()
+ : QObject(),
+ Plugin()
+{
+ NOATUNPLUGINC(InfraRed);
+ m_lirc = new Lirc(this);
+ connect(m_lirc,
+ SIGNAL(commandReceived(const QString &, const QString &, int)),
+ SLOT(slotCommand(const QString &, const QString &, int)));
+
+ IRPrefs::s_lirc = m_lirc;
+ volume=0;
+ QTimer::singleShot(0, this, SLOT(start()));
+}
+
+InfraRed::~InfraRed()
+{
+}
+
+void InfraRed::start()
+{
+ new IRPrefs(this);
+}
+
+void InfraRed::slotCommand(const QString &remote, const QString &button, int repeat)
+{
+ switch (IRPrefs::actionFor(remote, button, repeat))
+ {
+ case IRPrefs::None:
+ break;
+
+ case IRPrefs::Play:
+ napp->player()->play();
+ break;
+
+ case IRPrefs::Stop:
+ napp->player()->stop();
+ break;
+
+ case IRPrefs::Pause:
+ napp->player()->playpause();
+ break;
+
+ case IRPrefs::Mute:
+ if (napp->player()->volume())
+ {
+ volume=napp->player()->volume();
+ napp->player()->setVolume(0);
+ }
+ else
+ {
+ napp->player()->setVolume(volume);
+ }
+ break;
+
+ case IRPrefs::Previous:
+ napp->player()->back();
+ break;
+
+ case IRPrefs::Next:
+ napp->player()->forward();
+ break;
+
+ case IRPrefs::VolumeDown:
+ napp->player()->setVolume(napp->player()->volume() - 4);
+ break;
+
+ case IRPrefs::VolumeUp:
+ napp->player()->setVolume(napp->player()->volume() + 4);
+ break;
+
+ case IRPrefs::SeekBackward: // - 3 seconds
+ napp->player()->skipTo( QMAX(0, napp->player()->getTime() - 3000) );
+ break;
+
+ case IRPrefs::SeekForward: // + 3 seconds
+ napp->player()->skipTo( QMIN(napp->player()->getLength(),
+ napp->player()->getTime() + 3000) );
+ break;
+ case IRPrefs::ShowPlaylist:
+ napp->player()->toggleListView();
+ break;
+ case IRPrefs::NextSection:
+ // This and the next case theoretically shouldn't bypass player()
+ // but I'm making this change as inobtrusive as possible. That
+ // means restricting the change to infrared and not messing around
+ // in libnoatun -- Neil
+ napp->playlist()->nextSection();
+ break;
+ case IRPrefs::PreviousSection:
+ napp->playlist()->previousSection();
+ break;
+ }
+}
+
+#include "infrared.moc"
+
diff --git a/noatun/modules/infrared/infrared.h b/noatun/modules/infrared/infrared.h
new file mode 100644
index 00000000..37e97735
--- /dev/null
+++ b/noatun/modules/infrared/infrared.h
@@ -0,0 +1,30 @@
+
+#ifndef _INFRARED_H_
+#define _INFRARED_H_
+
+#include <noatun/player.h>
+#include <noatun/plugin.h>
+
+class NoatunPreferences;
+class Lirc;
+
+class InfraRed : public QObject, public Plugin
+{
+Q_OBJECT
+NOATUNPLUGIND
+public:
+ InfraRed();
+ ~InfraRed();
+
+private slots:
+ void slotCommand(const QString &, const QString &, int);
+
+ void start();
+
+private:
+ Lirc *m_lirc;
+ int volume;
+};
+
+#endif
+
diff --git a/noatun/modules/infrared/infrared.plugin b/noatun/modules/infrared/infrared.plugin
new file mode 100644
index 00000000..dab20149
--- /dev/null
+++ b/noatun/modules/infrared/infrared.plugin
@@ -0,0 +1,120 @@
+Filename=noatun_infrared.la
+Author=Malte Starostik
+Email=malte@kde.org
+Type=other
+License=Artistic
+Name=Infrared Control
+Name[af]=Infrared Kontrole
+Name[ar]=تحكم بالأشعة تحت الحمراء
+Name[az]=İnfra Qırmızı İdarə
+Name[bn]=ইনফ্রা-রেড নিয়ন্ত্রণ
+Name[bs]=Infrared kontrola
+Name[ca]=Control per infraroigs
+Name[cs]=Dálkové ovládání
+Name[cy]=Rheolaeth Isgoch
+Name[da]=Infrarød kontrol
+Name[de]=Infrarot-Fernbedienung
+Name[el]=Έλεγχος μέσω υπέρυθρων
+Name[eo]=INfraruĝstirilo
+Name[es]=Control por infrarrojos
+Name[et]=Infrapunaliidese kaudu juhtimine
+Name[eu]=Kontrol infragorria
+Name[fa]= کنترل مادون قرمز
+Name[fi]=Infrapunahallinta
+Name[fr]=Contrôleur infrarouge
+Name[ga]=Rialú Infridhearg
+Name[gl]=Control por Infravermellos
+Name[he]=שליטה בעזרת אינפרא־אדום
+Name[hi]=इन्फ्रारेड नियंत्रण
+Name[hr]=Infra-crvena kontrola
+Name[hu]=Távirányító
+Name[id]=Kontrol infra merah
+Name[is]=Innrauð stýring
+Name[it]=Controllo a infrarossi
+Name[ja]=赤外線コントロール
+Name[kk]=ИҚ тұтқасымен басқару
+Name[km]=ឧបករណ៍ Infrared
+Name[ko]=적외선 제어
+Name[lt]=Infraraudonųjų spindulių valdymas
+Name[lv]=Infrasarkanā Vadība
+Name[mk]=Инфрацрвена контрола
+Name[mt]=Kontroll Infrared
+Name[nb]=Infrarød kontroll
+Name[nds]=Infraroot-Feernbedenen
+Name[ne]=इन्फ्रारेड नियन्त्रण
+Name[nl]=Infraroodbediening
+Name[nn]=Infraraud kontroll
+Name[pa]=ਇੰਫਰਾਰੈੱਡ ਕੰਟਰੋਲ
+Name[pl]=Sterowanie przez podczerwień
+Name[pt]=Controlo de Infra-Vermelhos
+Name[pt_BR]=Controle de Infravermelho
+Name[ro]=Control în infraroşu
+Name[ru]=ИК пульт
+Name[se]=Infrarukses stivrran
+Name[sk]=Diaľkové ovládanie
+Name[sl]=Infrardeči nadzor
+Name[sr]=Инфрацрвена контрола
+Name[sr@Latn]=Infracrvena kontrola
+Name[sv]=Infraröd fjärrkontroll
+Name[ta]=செங்கீழ்க்கதிர்க் கட்டுப்பாடு
+Name[tg]=Идоракунии Инфрасурх
+Name[th]=ตัวควบคุมแบบอินฟราเรด
+Name[tr]=Kızılötesi Kontrol
+Name[uk]=Керуванням інфрачервоним зв'язком
+Name[ven]=Vhulanguli ha Infrared
+Name[xh]=Ulawulo olwakhelwe ngezantsi
+Name[zh_CN]=红外线控制
+Name[zh_HK]=紅外線控制
+Name[zh_TW]=紅外線控制
+Name[zu]=Ulawulo Olunokubomvu
+Comment=Control Noatun with your IR remote
+Comment[bg]=Управление на Noatun с помощта на инфрачервено, дистанционно управление
+Comment[bn]=আপনার ইনফ্রা-রেড রিমোট ব্যবহার করে নোটান নিয়ন্ত্রণ করুন
+Comment[bs]=Kontrolišite Noatun vašim infracrvenim daljinskim upravljačem
+Comment[ca]=Controla Noatun amb el comandament a distància
+Comment[cs]=Ovládejte Noatun svým dálkovým ovladačem
+Comment[cy]=Rheoli Noatun efo'ch Rheolydd Isgoch
+Comment[da]=Kontrollér Noatun med din fjernbetjening
+Comment[de]=Kontrollieren Sie Noatun mit Ihrer Infrarot-Fernbedienung
+Comment[el]=Έλεγχος του Noatun με το IR τηλεκοντρόλ σας
+Comment[eo]=Stiru Noatun per via infraruĝstirilo
+Comment[es]=Controle Noatun con su mando a distancia
+Comment[et]=Noatun juhtimine IR puldi abil
+Comment[eu]=Kontrolatu Noatun urruneko infragorriekin
+Comment[fa]=کنترل Noatun با IR دور شما
+Comment[fi]=Hallitse Noatunia infrapunakauko-ohjaimella
+Comment[fr]=Contrôlez Noatun avec votre télécommande infrarouge
+Comment[ga]=Rialaigh Noatun le do cianrialtán infridhearg
+Comment[gl]=Controlar Noatun co seu IR remoto
+Comment[he]=שליטה ב-Noatun באמצעות שלט אינפרא־אדום
+Comment[hu]=A Noatun kezelése távirányítóval
+Comment[is]=Stjórnaðu Nóatún með fjarstýringunni þinni
+Comment[it]=Controlla Noatun con il tuo telecomando
+Comment[ja]=赤外線リモコンで Noatun をコントロール
+Comment[kk]=Noatun-ды ИҚ тұтқасымен басқару
+Comment[km]=បញ្ជា Noatun ដោយ​ប្រើ IR របស់​អ្នក​ពី​ចម្ងាយ
+Comment[ko]=적외선 리모콘으로 Noatun 조정하기
+Comment[lt]=Valdykite Noatun pasitelkdami nuotolinį pultą
+Comment[mk]=Го контролира Noatun со вашиот инфрацрвен далечински управувач
+Comment[nb]=Styr Noatun med en infrerød fjernkontroll
+Comment[nds]=Noatun mit Dien Infraroot-Feernbedenen stüern
+Comment[ne]=तपाईँको IR टाढाकोसँग नियन्त्रण नोवटुन
+Comment[nl]=Bedien Noatun met uw infrarood afstandbediening
+Comment[nn]=Styr Noatun med ein infraraud fjernkontroll
+Comment[pl]=Sterowanie Noatun za pomocą pilota podczerwieni
+Comment[pt]=Controle o Noatun com um comando à distância
+Comment[pt_BR]=Controlar o Noatun com seu controle-remoto infravermelho
+Comment[ro]=Controlează Noatun cu telecomanda în infraroşu
+Comment[ru]=Управление Noatun с помощью инфракрасного пульта
+Comment[sk]=Ovládajte Noatun vaším diaľkovým ovládačom
+Comment[sl]=Nadzorujte Noatun z vašim infrardečim daljinskim upravljalcem
+Comment[sr]=Контролишите Noatun вашим ИЦ даљинским управљачем
+Comment[sr@Latn]=Kontrolišite Noatun vašim IC daljinskim upravljačem
+Comment[sv]=Styr Noatun med en infraröd fjärrkontroll
+Comment[ta]=உங்கள் IR கருவியால் Noatun இனைக் கட்டுப்படுத்தவும்
+Comment[th]=ควบคุม Noatun ด้วยตัวรีโมทอินฟราเรดของคุณ
+Comment[tr]=Kızılötesi Uzaktan Kumanda ile Noatun'u Kontrol Edin
+Comment[uk]=Керує Noatun з віддаленого інфрачервоного пульта керування
+Comment[zh_CN]=使用红外线遥控 Noatun
+Comment[zh_HK]=使用紅外線遙控控制 Noatun
+Comment[zh_TW]=使用 Noatun 透過紅外線遙控
diff --git a/noatun/modules/infrared/irprefs.cpp b/noatun/modules/infrared/irprefs.cpp
new file mode 100644
index 00000000..409fa94e
--- /dev/null
+++ b/noatun/modules/infrared/irprefs.cpp
@@ -0,0 +1,311 @@
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <noatun/app.h>
+
+#include <kdialog.h>
+#include <klocale.h>
+#include <kconfig.h>
+#include <klistview.h>
+#include <kcombobox.h>
+#include <knuminput.h>
+
+#include "irprefs.h"
+#include "lirc.h"
+
+class CommandItem : public QListViewItem
+{
+public:
+ CommandItem(QListViewItem *remote, const QString &name,
+ IRPrefs::Action action, int interval)
+ : QListViewItem(remote, name, IRPrefs::actionName(action),
+ interval ? QString().setNum(interval) : QString::null),
+ m_name(remote->text(0) + "::" + name),
+ m_action(action),
+ m_interval(interval)
+ {
+ }
+
+ const QString &name() const { return m_name; }
+ IRPrefs::Action action() const { return m_action; }
+ int interval() const { return m_interval; }
+ void setAction(IRPrefs::Action action)
+ {
+ setText(1, IRPrefs::actionName(action));
+ m_action = action;
+ }
+ void setInterval(int interval)
+ {
+ setText(2, interval ? QString().setNum(interval) : QString::null);
+ m_interval = interval;
+ }
+
+private:
+ QString m_name;
+ IRPrefs::Action m_action;
+ int m_interval;
+};
+
+Lirc *IRPrefs::s_lirc = 0;
+bool IRPrefs::s_configRead = false;
+QMap<QString, IRPrefs::Command> IRPrefs::s_commands;
+
+IRPrefs::IRPrefs(QObject *parent)
+ : CModule(i18n("Infrared Control"), i18n("Configure Infrared Commands"), "remote", parent)
+{
+ QGridLayout *layout = new QGridLayout(this, 3, 5, KDialog::marginHint(), KDialog::spacingHint());
+ layout->setColStretch(1, 1);
+
+ QLabel *label = new QLabel(i18n("Remote control &commands:"), this);
+ layout->addMultiCellWidget(label, 0, 0, 0, 4);
+
+ label->setBuddy(m_commands = new KListView(this));
+ layout->addMultiCellWidget(m_commands, 1, 1, 0, 4);
+
+ label = new QLabel(i18n("&Action:"), this);
+ layout->addWidget(label, 2, 0);
+
+ label->setBuddy(m_action = new KComboBox(this));
+ m_action->setEnabled(false);
+ layout->addWidget(m_action, 2, 1);
+
+ m_repeat = new QCheckBox(i18n("&Repeat"), this);
+ m_repeat->setEnabled(false);
+ layout->addWidget(m_repeat, 2, 2);
+
+ label = new QLabel(i18n("&Interval:"), this);
+ layout->addWidget(label, 2, 3);
+
+ label->setBuddy(m_interval = new KIntSpinBox(this));
+ m_interval->setMinValue(1);
+ m_interval->setMaxValue(0xff);
+ m_interval->setValue(10);
+ m_interval->setEnabled(false);
+ layout->addWidget(m_interval, 2, 4);
+
+ connect(s_lirc, SIGNAL(remotesRead()), SLOT(reopen()));
+ connect(m_commands,
+ SIGNAL(selectionChanged(QListViewItem *)),
+ SLOT(slotCommandSelected(QListViewItem *)));
+ connect(m_action,
+ SIGNAL(activated(int)),
+ SLOT(slotActionActivated(int)));
+ connect(m_repeat,
+ SIGNAL(toggled(bool)),
+ SLOT(slotRepeatToggled(bool)));
+ connect(m_interval,
+ SIGNAL(valueChanged(int)),
+ SLOT(slotIntervalChanged(int)));
+
+ reopen();
+}
+
+void IRPrefs::save()
+{
+ KConfig *c = kapp->config();
+ KConfigGroupSaver groupSaver(c, "Infrared");
+ c->writeEntry("CommandCount", s_commands.count());
+ int i = 1;
+ for (QMap<QString, Command>::ConstIterator it = s_commands.begin(); it != s_commands.end(); ++it)
+ {
+ c->writePathEntry(QString("Command_%1").arg(i), it.key());
+ c->writeEntry(QString("Action_%1").arg(i), (int)((*it).action));
+ c->writeEntry(QString("Interval_%1").arg(i), (*it).interval);
+ ++i;
+ }
+}
+
+void IRPrefs::reopen()
+{
+ readConfig();
+
+ QStringList remotes = s_lirc->remotes();
+ m_commands->clear();
+ while (m_commands->columns()) m_commands->removeColumn(0);
+ if (!remotes.count())
+ {
+ m_commands->addColumn(i18n("Sorry"));
+ m_commands->setSorting(-1);
+ if (s_lirc->isConnected())
+ {
+ new QListViewItem(m_commands, i18n("You do not have any remote control configured."));
+ new QListViewItem(m_commands, i18n("Please make sure lirc is setup correctly."));
+ }
+ else
+ {
+ new QListViewItem(m_commands, i18n("Connection could not be established."));
+ new QListViewItem(m_commands, i18n("Please make sure lirc is setup correctly and lircd is running."));
+ }
+ m_commands->setEnabled(false);
+ return;
+ }
+ m_commands->setEnabled(true);
+ m_commands->addColumn(i18n("Button"));
+ m_commands->addColumn(i18n("Action"));
+ m_commands->addColumn(i18n("Interval"));
+ m_commands->setSorting(0);
+ for (QStringList::ConstIterator it = remotes.begin(); it != remotes.end(); ++it)
+ {
+ QListViewItem *remote = new QListViewItem(m_commands, *it);
+ const QStringList &buttons = s_lirc->buttons(*it);
+ for (QStringList::ConstIterator btn = buttons.begin(); btn != buttons.end(); ++btn)
+ {
+ QString key = *it + "::" + *btn;
+ if (s_commands.contains(key))
+ new CommandItem(remote, *btn, s_commands[key].action, s_commands[key].interval);
+ else
+ new CommandItem(remote, *btn, None, 0);
+ }
+ remote->setOpen(true);
+ }
+
+ m_action->clear();
+ for (int i = 0; ; ++i)
+ {
+ QString action = actionName((Action)i);
+ if (action.isNull())
+ break;
+ if (action.isEmpty())
+ m_action->insertItem(i18n("None"));
+ else
+ m_action->insertItem(action);
+ }
+
+
+}
+
+void IRPrefs::slotCommandSelected(QListViewItem *item)
+{
+ CommandItem *cmd = dynamic_cast<CommandItem *>(item);
+ if (cmd)
+ {
+ m_action->setCurrentItem((int)(cmd->action()));
+ m_repeat->setChecked(cmd->interval());
+ if (cmd->interval())
+ m_interval->setValue(cmd->interval());
+ else
+ {
+ m_interval->setValue(10);
+ cmd->setInterval(0); // HACKHACKHACK
+ }
+ m_action->setEnabled(true);
+ m_repeat->setEnabled(cmd->action() != None);
+ m_interval->setEnabled(cmd->interval());
+ }
+ else
+ {
+ m_action->setEnabled(false);
+ m_repeat->setEnabled(false);
+ m_interval->setEnabled(false);
+ }
+}
+
+void IRPrefs::slotActionActivated(int action)
+{
+ CommandItem *cmd = dynamic_cast<CommandItem *>(m_commands->currentItem());
+ if (!cmd)
+ return; // Shouldn't happen
+ cmd->setAction((Action)action);
+ if (cmd->action() == None)
+ {
+ cmd->setInterval(0);
+ m_repeat->setChecked(false);
+ m_repeat->setEnabled(false);
+ m_interval->setEnabled(false);
+ }
+ else
+ {
+ m_repeat->setEnabled(true);
+ m_interval->setEnabled(cmd->interval());
+ }
+ s_commands[cmd->name()].action = cmd->action();
+ s_commands[cmd->name()].interval = 0;
+}
+
+void IRPrefs::slotRepeatToggled(bool value)
+{
+ CommandItem *cmd = dynamic_cast<CommandItem *>(m_commands->currentItem());
+ if (!cmd)
+ return; // Shouldn't happen
+ cmd->setInterval(value ? 10 : 0);
+ s_commands[cmd->name()].interval = cmd->interval();
+ m_interval->setEnabled(value);
+}
+
+void IRPrefs::slotIntervalChanged(int value)
+{
+ CommandItem *cmd = dynamic_cast<CommandItem *>(m_commands->currentItem());
+ if (!cmd)
+ return; // Shouldn't happen
+ cmd->setInterval(value);
+ s_commands[cmd->name()].interval = cmd->interval();
+}
+
+const QString IRPrefs::actionName(Action action)
+{
+ switch (action)
+ {
+ case None:
+ return QString("");
+ case Play:
+ return i18n("Play");
+ case Stop:
+ return i18n("Stop");
+ case Previous:
+ return i18n("Back");
+ case Next:
+ return i18n("Next");
+ case VolumeDown:
+ return i18n("Volume Down");
+ case VolumeUp:
+ return i18n("Volume Up");
+ case Mute:
+ return i18n("Mute");
+ case Pause:
+ return i18n("Pause");
+ case SeekBackward:
+ return i18n("Seek Backward");
+ case SeekForward:
+ return i18n("Seek Forward");
+ case ShowPlaylist:
+ return i18n("Show Playlist");
+ case NextSection:
+ return i18n("Next Section");
+ case PreviousSection:
+ return i18n("Previous Section");
+ }
+ return QString::null;
+}
+
+void IRPrefs::readConfig()
+{
+ if (s_configRead)
+ return;
+ KConfig *c = kapp->config();
+ KConfigGroupSaver groupSaver(c, "Infrared");
+ int count = c->readNumEntry("CommandCount");
+ for (int i = 1; i <= count; ++i)
+ {
+ Command cmd;
+ cmd.action = (Action)(c->readNumEntry(QString("Action_%1").arg(i)));
+ cmd.interval = c->readNumEntry(QString("Interval_%1").arg(i));
+ s_commands.insert(c->readPathEntry(QString("Command_%1").arg(i)), cmd);
+ }
+ s_configRead = true;
+}
+
+IRPrefs::Action IRPrefs::actionFor(const QString &remote, const QString &button, int repeat)
+{
+ readConfig();
+ Command cmd = s_commands[remote + "::" + button];
+ if ((cmd.interval == 0 && repeat == 0)
+ || (cmd.interval != 0 && repeat % cmd.interval == 0))
+ return cmd.action;
+ else
+ return None;
+}
+
+#include "irprefs.moc"
+
+
diff --git a/noatun/modules/infrared/irprefs.h b/noatun/modules/infrared/irprefs.h
new file mode 100644
index 00000000..7f813ac8
--- /dev/null
+++ b/noatun/modules/infrared/irprefs.h
@@ -0,0 +1,62 @@
+
+#ifndef _IRPREFS_H_
+#define _IRPREFS_H_
+
+#include <qmap.h>
+#include <noatun/pref.h>
+
+class QCheckBox;
+class QListViewItem;
+class KListView;
+class KComboBox;
+class KIntSpinBox;
+class Lirc;
+
+class IRPrefs : public CModule
+{
+Q_OBJECT
+public:
+ enum Action
+ {
+ None, Play, Stop, Pause, Mute,
+ Previous, Next, VolumeDown, VolumeUp,
+ SeekBackward, SeekForward, ShowPlaylist,
+ NextSection, PreviousSection
+ };
+
+ IRPrefs(QObject *parent);
+
+ virtual void save();
+
+ static const QString actionName(Action);
+ static Action actionFor(const QString &, const QString &, int);
+
+public slots:
+ static Lirc *s_lirc;
+
+private slots:
+ virtual void reopen();
+ void slotCommandSelected(QListViewItem *);
+ void slotActionActivated(int);
+ void slotRepeatToggled(bool);
+ void slotIntervalChanged(int);
+
+private:
+ static void readConfig();
+
+ KListView *m_commands;
+ KComboBox *m_action;
+ QCheckBox *m_repeat;
+ KIntSpinBox *m_interval;
+
+ struct Command
+ {
+ Action action;
+ int interval;
+ };
+ static bool s_configRead;
+ static QMap<QString, Command> s_commands;
+};
+
+#endif
+
diff --git a/noatun/modules/infrared/lirc.cpp b/noatun/modules/infrared/lirc.cpp
new file mode 100644
index 00000000..857c07e2
--- /dev/null
+++ b/noatun/modules/infrared/lirc.cpp
@@ -0,0 +1,173 @@
+
+#include <unistd.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+#include <qsocket.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include "lirc.h"
+
+Lirc::Lirc(QObject *parent)
+ : QObject(parent),
+ m_socket(0)
+{
+ int sock = ::socket(PF_UNIX, SOCK_STREAM, 0);
+ if (sock == -1)
+ {
+ KMessageBox::sorry(0, i18n("Could not create a socket to receive infrared signals. The error is:\n") + strerror(errno));
+ return;
+ }
+ sockaddr_un addr;
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, "/dev/lircd");
+ if (::connect(sock, (struct sockaddr *)(&addr), sizeof(addr)) == -1)
+ {
+ KMessageBox::sorry(0, i18n("Could not establish a connection to receive infrared signals. The error is:\n") + strerror(errno));
+ ::close(sock);
+ return;
+ }
+
+ m_socket = new QSocket;
+ m_socket->setSocket(sock);
+ connect(m_socket, SIGNAL(readyRead()), SLOT(slotRead()));
+ update();
+}
+
+Lirc::~Lirc()
+{
+ delete m_socket;
+}
+
+const QStringList Lirc::remotes() const
+{
+ QStringList result;
+ for (Remotes::ConstIterator it = m_remotes.begin(); it != m_remotes.end(); ++it)
+ result.append(it.key());
+ result.sort();
+ return result;
+}
+
+void Lirc::slotRead()
+{
+ while (m_socket->bytesAvailable())
+ {
+ QString line = readLine();
+ if (line == "BEGIN")
+ {
+ // BEGIN
+ // <command>
+ // [SUCCESS|ERROR]
+ // [DATA
+ // n
+ // n lines of data]
+ // END
+ line = readLine();
+ if (line == "SIGHUP")
+ {
+ // Configuration changed
+ do line = readLine();
+ while (!line.isEmpty() && line != "END");
+ update();
+ return;
+ }
+ else if (line == "LIST")
+ {
+ // remote control list
+ if (readLine() != "SUCCESS" || readLine() != "DATA")
+ {
+ do line = readLine();
+ while (!line.isEmpty() && line != "END");
+ return;
+ }
+ QStringList remotes;
+ int count = readLine().toInt();
+ for (int i = 0; i < count; ++i)
+ remotes.append(readLine());
+ do line = readLine();
+ while (!line.isEmpty() && line != "END");
+ if (line.isEmpty())
+ return; // abort on corrupt data
+ for (QStringList::ConstIterator it = remotes.begin(); it != remotes.end(); ++it)
+ sendCommand("LIST " + *it);
+ return;
+ }
+ else if (line.left(4) == "LIST")
+ {
+ // button list
+ if (readLine() != "SUCCESS" || readLine() != "DATA")
+ {
+ do line = readLine();
+ while (!line.isEmpty() && line != "END");
+ return;
+ }
+ QString remote = line.mid(5);
+ QStringList buttons;
+ int count = readLine().toInt();
+ for (int i = 0; i < count; ++i)
+ {
+ // <code> <name>
+ QString btn = readLine();
+ buttons.append(btn.mid(17));
+ }
+ m_remotes.insert(remote, buttons);
+ }
+ do line = readLine();
+ while (!line.isEmpty() && line != "END");
+ emit remotesRead();
+ }
+ else
+ {
+ // <code> <repeat> <button name> <remote control name>
+ line.remove(0, 17); // strip code
+ int pos = line.find(' ');
+ if (pos < 0)
+ return;
+ bool ok;
+ int repeat = line.left(pos).toInt(&ok, 16);
+ if (!ok)
+ return;
+ line.remove(0, pos + 1);
+
+ pos = line.find(' ');
+ if (pos < 0)
+ return;
+ QString btn = line.left(pos);
+ line.remove(0, pos + 1);
+
+ emit commandReceived(line, btn, repeat);
+ }
+ }
+}
+
+void Lirc::update()
+{
+ m_remotes.clear();
+ sendCommand("LIST");
+}
+
+const QString Lirc::readLine()
+{
+ if (!m_socket->bytesAvailable())
+ return QString::null;
+
+ QString line = m_socket->readLine();
+ if (line.isEmpty())
+ return QString::null;
+
+ line.remove(line.length() - 1, 1);
+ return line;
+}
+
+void Lirc::sendCommand(const QString &command)
+{
+ QString cmd = command + "\n";
+ m_socket->writeBlock(cmd.latin1(), cmd.length());
+}
+
+#include "lirc.moc"
+
diff --git a/noatun/modules/infrared/lirc.h b/noatun/modules/infrared/lirc.h
new file mode 100644
index 00000000..e5380c5c
--- /dev/null
+++ b/noatun/modules/infrared/lirc.h
@@ -0,0 +1,75 @@
+
+#ifndef _LIRC_H_
+#define _LIRC_H_
+
+#include <qobject.h>
+#include <qstringlist.h>
+#include <qmap.h>
+
+class QSocket;
+
+typedef QMap<QString, QStringList> Remotes;
+
+class Lirc : public QObject
+{
+Q_OBJECT
+public:
+ /**
+ * Constructor
+ */
+ Lirc(QObject *parent);
+ /**
+ * Destructor
+ */
+ virtual ~Lirc();
+
+ /**
+ * Returns true if the connection to lircd is operational
+ */
+ bool isConnected() const { return m_socket; }
+ /**
+ * The names of the remote configured controls
+ */
+ const QStringList remotes() const;
+ /**
+ * The names of the buttons for the specified
+ * remote control
+ */
+ const QStringList buttons(const QString &remote) const
+ {
+ return m_remotes[remote];
+ }
+
+signals:
+ /**
+ * Emitted when the list of controls / buttons was cmpletely read
+ */
+ void remotesRead();
+ /**
+ * Emitted when a IR command was received
+ *
+ * The arguments are the name of the remote control used,
+ * the name of the button pressed and the repeat counter.
+ *
+ * The signal is emitted repeatedly as long as the button
+ * on the remote control remains pressed.
+ * The repeat counter starts with 0 and increases
+ * every time this signal is emitted.
+ */
+ void commandReceived(const QString &, const QString &, int);
+
+private slots:
+ void slotRead();
+
+private:
+ void update();
+ const QString readLine();
+ void sendCommand(const QString &);
+
+private:
+ QSocket *m_socket;
+ Remotes m_remotes;
+};
+
+#endif
+
diff --git a/noatun/modules/kaiman/Makefile.am b/noatun/modules/kaiman/Makefile.am
new file mode 100644
index 00000000..a8205a59
--- /dev/null
+++ b/noatun/modules/kaiman/Makefile.am
@@ -0,0 +1,23 @@
+SUBDIRS = skins
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_kaiman.la
+
+noatun_kaiman_la_SOURCES = \
+ noatunui.cpp \
+ style.cpp \
+ userinterface.cpp \
+ pref.cpp
+
+noatun_kaiman_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_kaiman_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la \
+ -lqtmcop -lkmedia2_idl -lsoundserver_idl
+
+noatun_kaiman_la_METASOURCES = AUTO
+
+noinst_HEADERS = \
+ userinterface.h \
+ style.h \
+ pref.h
+
+noatun_modules_kaiman_DATA = kaiman.plugin kaimanui.rc
+noatun_modules_kaimandir = $(kde_datadir)/noatun
diff --git a/noatun/modules/kaiman/README b/noatun/modules/kaiman/README
new file mode 100644
index 00000000..4c24a3ae
--- /dev/null
+++ b/noatun/modules/kaiman/README
@@ -0,0 +1,3 @@
+kaiman - Media player for KDE2.0
+
+
diff --git a/noatun/modules/kaiman/SKIN-SPECS b/noatun/modules/kaiman/SKIN-SPECS
new file mode 100644
index 00000000..bc3533ad
--- /dev/null
+++ b/noatun/modules/kaiman/SKIN-SPECS
@@ -0,0 +1,518 @@
+ ############ GQmpeg skin specifications file. ############
+
+ (A quick reference of what is required in the image files
+ for each widget type is located at the end of this document )
+
+Skins are simply a directory which contain image files and
+a skindata file (named skindata).
+
+All skin features are configured in the skindata file.
+
+Note: when using an alternate skin, it's specs go into a file named
+skindata_alt, it uses the same format as the skindata file. Pressing
+the Alt_Skin_Button button switches between the two skins.
+(each skin must contain an Alt_Skin_Button if you want the alternate
+skin feature to work)
+
+Any line can be made into a comment by prefacing it with a '#' symbol.
+
+All image files can be any size, GQmpeg will calculate the drawing data
+for you. Skins can have any size buttons, display items, digits,
+fonts, etc. The files can be of any type supported by gdk-pixbuf (xpm, png,
+jpeg, gif, etc.) The recommended file format is png.
+
+Prelights are optional on all items that support them. A prelight is an
+alternate image that is displayed when the mouse is over a pressable
+item (button, slider, dial). For example, the default skin includes prelights
+for all buttons, notice the buttons 'brighten' when the mouse moves over
+them.
+
+Every image within a file must have the same width and height, for example
+if the play button was 30 by 20, the resulting image file would be
+180 by 20. (6 button states total, including status lights and prelights)
+
+ Addendum: If the above button was specified with the status light and
+ prelight options as false, the resulting image file would be
+ 60 by 20. (2 button states total, 1 for normal, 1 for pressed)
+
+Note that images for buttons and numbers contain the items horizontally,
+the images for items contain the items vertically, the image for a font
+contains 3 rows of 32 items, and the slider contains the background and
+handle horizontally or vertically (depending if a slider is horizontal
+or verticle, respectivley).
+
+Only the background image is required, all other elements are optional
+(although it would be nice to always have a play button :)
+If you do not want an item displayed, comment out the line with
+a '#' symbol.
+
+IMPORTANT:
+Slots enclosed in "[]" are optional, but are so only to retain backward
+compatibility of skins. Please specify all options for each type as in the
+future the options enclosed in "[]" may no longer be "optional". Please
+separate each option with a single space, and do not add extra characters
+at the end of the line, as the extra info may be mistaken for expanded
+options in the event that options are added to the skin spec in the future.
+
+PROPER TRANSPARENCY:
+The main background image uses a threshold of 1 (out of 256 levels) for the
+window shape, the rest is used for partially overwiting the background when
+the Transparency option is true.
+
+Portions of items, buttons, sliders, numbers, and text that never change and
+are the same as the background image should be set transparent so that the
+'force transparent' option works properly.
+
+ ############# skindata file format #################
+
+Note: For an example skindata file see the file skindata-template.
+
+x and y are always the position in the window (use the -skinhelp command line
+option to have GQmpeg print out the mouse coordinates as the mouse moves)
+
+And finally, to what is available:
+
+==========================
+ Main options
+==========================
+
+Background: filename
+
+ filename
+ The background image file, the window will be the same size as this image.
+ Add transparency to this image for shaped skins, the cutoff threshold for
+ transparency is 1 on images with muli-level alpha (like in png).
+
+Transparency: flag
+
+ flag
+ True or False, this specifies if the background image (above) has a multiple
+ level alpha channel (as in png files) to apply when overwriting the root
+ window's background.
+
+Mask: filename
+
+(this is DEPRECATED!, for transparent skins just add transparency to the
+ Background image, Mask remains merely for backwards compatibility)
+ filename
+ The mask image file, only needed for skins which are shaped windows (not
+ rectangular). Contains a transparency mask for the main window.
+
+==========================
+ Text display
+==========================
+
+Title/Album/Artist/Genre: filename length [extended] x y
+
+ filename
+ Should contain a fixed font. With 3 or 6 lines of 32 characters each,
+ these are the characters, they are listed in three rows so you can copy
+ and past them into you graphics program. (first character in the top line
+ is a space)
+
+ !"#$%&'()*+,-./0123456789:;<=>?
+ @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
+ `abcdefghijklmnopqrstuvwxyz{|}~
+
+ When extended is TRUE these are the 3 addition lines of international chars
+
+ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ XXXXXXX FIXME! TO DO! XXXXXXXXXX
+ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+ length
+ Maximum number of characters to display in window.
+
+ extended
+ (optional, absence defaults to FALSE)
+ When TRUE, the image contains 3 additional lines for internation
+ characters. The result is 6 lines of 32 characters each.
+
+==========================
+ Buttons (that optionally include an 'active' mode)
+==========================
+
+Play/Pause/Stop/Shuffle/Repeat/
+ Time_Remaining/Time_Total_Button: filename prelight status_light x y [clip_filename]
+
+ filename
+ Image file. Contains the buttons horizontally in this order:
+ normal, normal pressed, normal active, normal active pressed, prelit, prelit active
+ (the actual number of images may vary, see next two options)
+
+ prelight
+ TRUE or FALSE, specifies whether or not prelight buttons are available, If
+ FALSE, do not include the last two prelight buttons in the image file.
+
+ status_light
+ TRUE or FALSE, specifies whether or not button lights are available. If FALSE,
+ do not include the 'lit up', 'lit down', and 'prelight lit up' buttons in the
+ image file.
+
+ clip_filename (optional)
+ When specified, defines an image with transparency to be used as the button's
+ draw clip mask. The clip mask is placed at location x, y The transparency is used to
+ indicate the portions of the button that should be visible (drawn) and respond to
+ mouse clicks. If the image contains a multiple level alpha channel, the mask is reduced
+ such that levels above 50% are visible and those below 50% are not visible.
+
+==========================
+ Buttons (standard)
+==========================
+
+Next/Prev/FF/RW/Playlist/Config/Iconify/Mixer/Exit/Alt_Skin/Volume_Up/Volume_Down/
+ Balance_Left/Balance_Right_Button: filename prelight x y [clip_filename]
+Preset_1_/.../Preset_10_Button: filename prelight x y [clip_filename]
+
+ filename
+ Image file. Contains the buttons horizontally in this order:
+ normal, normal pressed, prelit
+ (the actual number of images may vary, see next option)
+
+ prelight
+ TRUE or FALSE, specifies whether or not prelight buttons are available. If
+ FALSE, do not include the last prelight button in the image file.
+
+ clip_filename (optional)
+ When specified, defines an image with transparency to be used as the button's
+ draw clip mask. The clip mask is placed at location x, y The transparency is used to
+ indicate the portions of the button that should be visible (drawn) and respond to
+ mouse clicks. If the image contains a multiple level alpha channel, the mask is reduced
+ such that levels above 50% are visible and those below 50% are not visible.
+
+
+==========================
+ Items (with fixed number sections)
+==========================
+
+Stereo/Shuffle/Repeat/Mpegversion/Mpeglayer/Mpegmode/Status/
+ Minus/Total_Item: filename x y
+
+ filename
+ Image file. Contains the items vertically in the order below:
+
+ Stereo_Item: blank, mono , stereo
+ Shuffle_Item: off, on
+ Repeat_Item: off, on
+ Mpegversion_Item: blank, 1, 2
+ Mpeglayer_Item: blank, 1, 2, 3
+ Mpegmode_Item: blank, stereo, joint-stereo, dual-channel, single-channel
+ Status_Item: stop, pause, play
+ Minus_Item: time counts up, time counts down
+ Total_Item: time refers to current song only, to total playlist, to live
+
+==========================
+ Items (animation oriented)
+==========================
+
+Load_Item: filename sections x y
+
+ filename
+ Image file. Contains animations for the following items:
+
+ Load_Item: Animation for loading playlist in background.
+
+ sections
+ The total number of sections in the image file. The first section is always
+ blank (animation is off), the subsequent images are cycled through to create
+ the animation. This number is a total count, so it will be 1 (first is always
+ blank) plus the number of animation frames.
+
+==========================
+ Items (value oriented)
+==========================
+
+Position/Volume/Balance_Item: filename sections x y
+
+ filename
+ Image file. Contains images vertically in the order representing the lowest to
+ highest values.
+
+ sections
+ The number of images within the file, recommended number of images is 16 to 32.
+ The most possible usable images is 101 for volume and blance (from volume=0%
+ to volume = 100%).
+
+ Note:
+ These items must be listed before their respective sliders:
+ (see Position/Volume/Balance_Slider).
+
+==========================
+ Digit placeholder (for convenience, less memory usage with many similar numbers)
+==========================
+
+Digit_Large/Digit_Small_Default: filename
+
+ filename
+ Image file. Contains digits horizontally from 0 to 9, and a blank space.
+
+ These two digits are a convenience function, if you want to use a digit more than
+ once it is quicker to load it into on of these two slots. Then when using the digit
+ in the number item type below, use the words 'Large' or 'Small' in place of the
+ filename.
+
+==========================
+ Numbers
+==========================
+
+Hour/Minute/Second/Song/Total/In_Rate/In_Hz/Out_Bits/Out_Hz/
+ Song_Minute/Song_Second/Frame/Frame_Total/CPU/
+ Hour_Total/Minute_Total/Second_Total_Number: filename [length center] x y
+
+ filename
+ Image file for the number's digit, or the word 'Large' or 'Small' (see above).
+ If a filename is specified, the image should contain the digits horizontally
+ from 0 to 9, and a blank space.
+
+ length (optional)
+ The number of digits to display, if not present the default is assumed.
+
+ center (optional, but if specified length is required too)
+ TRUE or FALSE, specify to center the number.
+
+==========================
+ Sliders
+==========================
+
+Position/Volume/Balance_Slider: filename prelight [verticle reversed] length x y
+
+ filename
+ Image file. Contains images horizontally in this order:
+ slider background, handle normal, handle pressed, handle prelit
+ (handles must have the same dimensions)
+
+ prelight
+ TRUE or FALSE, specifies whether or not a prelight handle is available, if FALSE,
+ do not include a 'handle prelit' in the image file.
+
+ verticle
+ TRUE or FALSE. If false the slider is horizontal, if true, verticle.
+
+ reversed
+ TRUE or FALSE. If true, the slider works opposite than normal. For example
+ when false the slider moves from left to right, when true the slider moves
+ from right to left. On a verticle slider and reversed is false, the slider
+ moves from top to bottom.
+
+ length
+ The width of the slider's background, this is the complete width the slider will
+ be in the window, and must match the length of the 'slider background' in the
+ image file.
+
+==========================
+ Dials (AKA knobs)
+==========================
+
+Position/Volume/Balance_Dial: filename has_press_image has_prelight_image reversed
+ angle_start angle_end handle_offset_x handle_offset_y center_x center_y
+ x y w h [clip_filename]
+
+ filename
+ Image file. Contains images for the dial's handle vertically in this order:
+ normal
+ pressed (being dragged with mouse, optional)
+ prelit (mouse over highlight, optional)
+
+ has_press_image
+ TRUE or FALSE, specifies whether or not handle has a pressed image (above)
+
+ has_prelight_image
+ TRUE or FALSE, specifies whether or not handle has a pressed image (above)
+
+ reversed
+ TRUE of FALSE, normally a dial works clockwise with angle_start being the
+ lowest (zero) value and angle_end being the highest value. When TRUE the dial
+ works counter-clockwise with angle_end being the lowest (zero) value to
+ angle_start being the highest position.
+
+ angle_start
+ angle_end
+ The start and end angles define the end points of the dial's rotation in integer
+ degrees, the degrees count from 0 located right of center axis increasing clockwise
+ to a value of 359. (360 is equivelent to 0, but the only accepted numbers are 0 - 359.
+ This (admittedly poor) figure might help:
+
+
+ 270 ____ center axis
+ _|_ /
+ / /
+ / / \
+ 180 -| + |- 0 (360)
+ \ /
+ \_ _/
+ |
+ 90
+
+ handle_offset_x
+ handle_offset_y
+ The x and y coordinates into the handle image that represents the handle center
+ of rotation (pivot point), this does not have to actually be within the image size.
+
+ center_x
+ center_y
+ The x and y coordinates on the skin image for the handle center of rotation.
+
+ x, y, width, height:
+ Marks the clipping region to draw the dial, basically the handle is not drawn
+ outside this region. (width and height will be ignored if a clip mask image
+ is specified (see next option).
+
+ clip_filename (optional)
+ When specified, defines an image with transparency to be used as the dial's
+ draw clip mask. The clip mask is placed at x, y (above) and the image's dimensions
+ are used in place of width, height (above). The transparency is used to indicate the
+ portions of the dial that should be visible (drawn) and respond to mouse clicks.
+ If the image contains a multiple level alpha channel, the mask is reduced such that
+ levels above 50% are visible and those below 50% are not visible.
+
+==============================================================================
+ ************ Quick reference tables **************
+==============================================================================
+
+Note: All example values below set (*)coordinates x=1 and y=1, and filename to fn.png.
+ (*) Except Dials.
+
+--------------------------
+ Buttons (all button images contained horizontally)
+--------------------------
+
+Play/Pause/Stop/Shuffle/Repeat_Button: filename prelight status_light x y
+Time_Remaining/Time_Total_Button: filename prelight status_light x y
+
+Option line: | # images | Normal | Pressed | Lit | Lit | Prelit | Prelit |
+ | total | | | Normal | Pressed | Normal | Lit Normal |
+-----------------------+----------+--------+---------+--------+---------+--------+------------+
+ fn.png TRUE TRUE 1 1 | 6 | X | X | X | X | X | X |
+ fn.png TRUE FALSE 1 1 | 3 | X | X | | | X | |
+ fn.png FALSE TRUE 1 1 | 4 | X | X | X | X | | |
+ fn.png FALSE FALSE 1 1| 2 | X | X | | | | |
+
+Next/Prev/FF/RW/Playlist/Config/Iconify/Mixer/Exit/Alt_Skin_Button: filename prelight x y
+Volume_Up/Volume_Down/Balance_Left/Balance_Right_Button: filename prelight x y
+
+Option line: | # images | Normal | Pressed | Prelit |
+ | total | | | Normal |
+-----------------------+----------+--------+---------+--------+
+ fn.png TRUE 1 1 | 3 | X | X | X |
+ fn.png FALSE 1 1 | 2 | X | X | |
+
+
+--------------------------
+ Items (all item images contained vertically)
+--------------------------
+
+Stereo/Shuffle/Repeat/Mpegversion/Mpeglayer/Mpegmode/Status/Minus/Total_Item: filename x y
+
+Item: | # images | Image 1 | Image 2 | Image 3 | Image 4 | Image 5 |
+ | total | | | | | |
+-----------------------+----------+---------+---------+---------+---------+---------+
+ Stereo_Item | 3 | blank | mono | stereo | | |
+ Shuffle_Item | 2 | off | on | | | |
+ Repeat_Item | 2 | off | on | | | |
+ Mpegversion_Item | 3 | blank | ver 1 | ver 2 | | |
+ Mpeglayer_Item | 4 | blank | layer 1 | layer 2 | layer 3 | |
+ Mpegmode_Item | 5 | blank | stereo | j-stereo| dual-ch |single-ch|
+ Status_Item | 3 | stop | pause | play | | |
+ Minus_Item | 2 | normal |remaining| | | |
+ Total_Item | 2 | normal | total | live | | |
+
+
+Load_Item: filename section_count x y
+
+(These are special Animation items, any number of sections can be included)
+
+Option line: | # images | Image 1 | Image 2 | ....... | Last Image |
+ | total | | | | |
+-------------------+----------+---------+---------+---------+------------+
+ fn.png 8 1 1 | 8 | blank | Frame 1 | F2...F6 | Frame 7 |
+ fn.png 4 1 1 | 4 | blank | Frame 1 | Frame 2 | Frame 3 |
+
+
+Position/Volume/Balance_Item: filename sections x y
+
+Option Line: (*) | # images | 1st Image | middle Image | Last Image |
+ | total | | | |
+----------------------------+----------+-----------+--------------+---------------+
+ Volume_Item fn.png 17 1 1 | 17 | 1 - 0% vol| 9 - 50 % vol | 17 - 100% vol |
+ Volume_Item fn.png 31 1 1 | 31 | 1 - 0% vol| 16 - 50 % vol| 31 - 100% vol |
+ Balance_Item fn.png 17 1 1 | 17 | 1 - Left | 9 - middle | 17 - Right |
+ Balance_Item fn.png 13 1 1 | 13 | 1 - Left | 6 - middle | 13 - Right |
+
+ (*)note: The values (17, 31, 17, 13) above are only examples, any number of images
+ can be specified.
+
+
+--------------------------
+ Sliders (all slider images contained horizontally)
+--------------------------
+
+Position/Volume/Balance_Slider: filename prelight [verticle reversed] length x y
+
+Option line: | # images | Background | Normal | Pressed | Prelit |
+ | total | width | | | Normal |
+-----------------------------------+----------+-------------+--------+---------+--------+
+ fn.png TRUE FALSE FALSE 32 1 1 | 4 | 32 pixels | X | X | X |
+ fn.png FALSE FALSE FALSE 250 1 1 | 3 | 250 pixels | X | X | |
+
+
+--------------------------
+ Dials (can be confusing, see detailed description, above)
+--------------------------
+
+Position/Volume/Balance_Dial: filename has_press_image has_prelight_image reversed
+ angle_start angle_end handle_offset_x handle_offset_y center_x center_y
+ x y w h [clip_filename]
+
+Option line: | # images | Normal | Pressed | Prelit |
+ | total | | | Normal |
+----------------------------------------------------------+----------+--------+---------+--------+
+ fn.png TRUE TRUE TRUE 0 180 16 16 200 100 140 40 80 80 | 3 | X | X | X |
+ fn.png FALSE TRUE TRUE 0 180 16 16 200 100 140 40 80 80 | 2 | X | | X |
+ fn.png FALSE FALSE TRUE 0 180 16 16 200 100 140 40 80 80 | 1 | X | | |
+
+--------------------------
+ Numbers (all number images contained horizontally)
+--------------------------
+
+*_Number: filename [length centered] x y
+
+Option line: | # images | Images in order (left to right) |
+ | total | |
+---------------------+----------+------------------------------------------------+
+ fn.png 1 1 | 11 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " "(blank space) |
+ fn.png 3 FALSE 1 1 | 11 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " "(blank space) |
+
+
+--------------------------
+ Fonts (all font images contained in 3 or 6 rows of 32 columns)
+--------------------------
+
+Title/Album/Artist/Genre: filename length [extended] x y
+
+Option line: | # images | Images in order |
+ | total | |
+------------------------+----------+--------------------+
+ fn.png 16 1 1 | 96 | see grid 1 (below) |
+ fn.png 16 FALSE 1 1 | 96 | see grid 1 (below) |
+ fn.png 16 TRUE 1 1 | 192 | see grid 2 (below) |
+
+ +------------------------------------+
+character grid 1: | |
+(standard) | !"#$%&'()*+,-./0123456789:;<=>? |
+ | @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ |
+ | `abcdefghijklmnopqrstuvwxyz{|}~ |
+ | |
+ +------------------------------------+
+
+ +------------------------------------+
+character grid 2: | |
+(international | !"#$%&'()*+,-./0123456789:;<=>? |
+ extended) | @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ |
+ | `abcdefghijklmnopqrstuvwxyz{|}~ |
+ | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
+ | XXXXXXX FIXME! TO DO! XXXXXXXXXX |
+ | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
+ | |
+ +------------------------------------+
+
+#################################
diff --git a/noatun/modules/kaiman/kaiman.plugin b/noatun/modules/kaiman/kaiman.plugin
new file mode 100644
index 00000000..1b345de3
--- /dev/null
+++ b/noatun/modules/kaiman/kaiman.plugin
@@ -0,0 +1,132 @@
+Filename=noatun_kaiman.la
+Author=Stefan Schimanski
+Site=http://www.derkarl.org/noatun
+Email=schimmi@kde.org
+Type=userinterface
+License=GPL
+Name=Kaiman Interface
+Name[af]=Kaiman Koppelvlak
+Name[ar]=واجهة Kaiman
+Name[az]=Kaiman Ara üzü
+Name[bn]=কাইমান ইন্টারফেস
+Name[br]=Etrefas Kaiman
+Name[ca]=Interfície Kaiman
+Name[cs]=Rozhraní Kaimana
+Name[cy]=Rhyngwyneb Kaiman
+Name[da]=Kaiman-grænseflade
+Name[de]=Kaiman-Oberfläche
+Name[el]=Περιβάλλον Kaiman
+Name[eo]=Kajmaninterfaco
+Name[es]=Interfaz de Kaiman
+Name[et]=Kaiman kasutajaliides
+Name[eu]=Kaiman interfazea
+Name[fa]=واسط Kaiman
+Name[fi]=Kaiman-käyttöliittymä
+Name[fr]=Interface de Kaiman
+Name[ga]=Comhéadan Kaiman
+Name[gl]=Interface Kayman
+Name[he]=ממשק Kaiman
+Name[hi]= काईमेन इंटरफेस
+Name[hr]=Kaiman sučelje
+Name[hu]=Kaiman felület
+Name[is]=Kaiman aðgangur
+Name[it]=Interfaccia Kaiman
+Name[ja]=Kaiman インターフェース
+Name[kk]=Kaiman интерфейсі
+Name[km]=ចំណុច​ប្រទាក់ Kaiman
+Name[ko]=Kaiman 인터페이스
+Name[lt]=Kaiman sąsaja
+Name[lv]=Kaiman Starpseja
+Name[mk]=Интерфејс Kaiman
+Name[mt]=Interfaċċja Kaiman
+Name[nb]=Kaiman grensesnitt
+Name[nds]=Kaiman-Böversiet
+Name[ne]=काइम्यान इन्टरफेस
+Name[nl]=Kaiman interface
+Name[nn]=Kaiman-grensesnitt
+Name[pl]=Motyw Kaimana
+Name[pt]=Interface do Kaiman
+Name[pt_BR]=Interface do Kaiman
+Name[ro]=Interfaţă Kaiman
+Name[ru]=Интерфейс Кайман
+Name[se]=Kaiman-lakta
+Name[sk]=Rozhranie Kaimana
+Name[sl]=Vmesnik Kaiman
+Name[sr]=Kaiman интерфејс
+Name[sr@Latn]=Kaiman interfejs
+Name[sv]=Kaiman-gränssnitt
+Name[ta]=Kaiman இடைமுகம்
+Name[tg]=Интерфейси Kaiman
+Name[th]=ส่วนติดต่อ Kaiman
+Name[tr]=Kaiman Arayüzü
+Name[uk]=Інтерфейс Kaiman
+Name[uz]=Kaiman interfeysi
+Name[uz@cyrillic]=Kaiman интерфейси
+Name[ven]=Interface ya Kaiman
+Name[xh]=Ujongano lwe Kaiman
+Name[zh_CN]=Kaiman 接口
+Name[zh_HK]=Kaiman 介面
+Name[zh_TW]=Kaiman 介面
+Name[zu]=Uxhumano olubhekeneyo lwe Kaiman
+Comment=A GQMpeg skin interface ported from Kaiman
+Comment[af]='n Gqmpeg vel koppelvlak oorgedra van Kaiman
+Comment[ar]=واجهة GQMpeg مأخوذة من Kaiman
+Comment[az]=Kaiman'dan alınan GQMpeg dekorsiya axtar üzü
+Comment[bg]=Интерфейс за GQMpeg прехвърлен за Kaiman
+Comment[bs]=GQMpeg skin interface prebačen sa Kaiman-a
+Comment[ca]=Una aparença d'interfície GQMpeg portada de Kaiman
+Comment[cs]=Motiv rozhraní GQMpegu přenesený z Kaimana
+Comment[cy]=Rhyngwyneb croen GQMpeg wedi'i droi o Kaiman
+Comment[da]=En GQMpeg-forsidegrænseflade overført fra Kaiman
+Comment[de]=Eine Schnittstelle zur GQMpeg-Optik, aus Kaiman übernommen
+Comment[el]=Μια διασύνδεση βασισμένη στο θέμα GQMpeg προσαρμοσμένο από το Kaiman
+Comment[eo]=GQMpeg-etosinterfaco portita de Kajmano
+Comment[es]=Una interfaz de pieles GQMpeg portado de Kaiman
+Comment[et]=Kaimanist porditud GQMpeg skinnide toetus
+Comment[eu]=GQMpeg azal interfazea Kaiman-etik ekarria
+Comment[fa]=یک واسط GQMpeg skin که از Kaiman آورده شده است
+Comment[fi]=GQMpeg-käyttöliittymärajapinta Kaimanille
+Comment[fr]=Un revêtement à la GQMpeg importé de Kaiman
+Comment[gl]=Unha pel para a interface GQMPeg importada de Kaiman
+Comment[he]=ממשק Skin של GQMpeg שיובא מתוך Kaiman
+Comment[hi]=काईमेन से पोर्टेड जीक्यू-एमपीईजी इंटरफेस
+Comment[hr]=GQMpeg sučelje za kože uvezeno iz Kaiman-a
+Comment[hu]=A Kaimanban használt GQMpeg kinézet átültetett változata
+Comment[is]=GQMpeg skinn frá Kaiman
+Comment[it]=Una skin per GQMpeg convertita da Kaiman
+Comment[ja]=Kaiman から移植した GQMpeg スキンインターフェース
+Comment[kk]=Kaiman-нан аударылған GQMpeg тыстарының интерфейсі
+Comment[km]=ចំណុច​ប្រទាក់​ស្បែក GQMpeg ដែល​បាន​បញ្ចូល​ពី Kaiman
+Comment[ko]=Kaiman에서 이식된 GQMpeg 스킨
+Comment[lt]=GQMpeg pavidalų sąsaja, pritaikyta nuo Kaiman
+Comment[lv]=GQMpeg ādu starpseja pārcelta no Kaimana
+Comment[mk]=Интерфејс GQMpeg за маски пренесен од Kaiman
+Comment[ms]=Kulit antaramuka GQMpeg dari Kaiman
+Comment[mt]=Interfaċċja għal faċċati GQMpeg portata minn Kaiman
+Comment[nb]=Et GQMpeg ham-grensesnitt tatt fra Kaiman
+Comment[nds]=En GQMpeg-Böversiet, vun Kaiman överdragen
+Comment[ne]=काइम्यानबाट परिमार्जन गरिएको GQMpeg स्किन इन्टरफेस
+Comment[nl]=Een GQMpeg-skin-interface, overgedragen van Kaiman
+Comment[nn]=Eit GQMpeg-skalgrensesnitt porta frå Kaiman
+Comment[pl]=Motyw skór GQMpeg przeniesiony z Kaimana
+Comment[pt]=A interface de aspectos do GQMpeg transposta para o Kaiman
+Comment[pt_BR]=Uma interface de aparência (skin) GQMpeg portada do Kaiman
+Comment[ro]=O interfaţă GQMpeg portată de la Kaiman
+Comment[ru]=Интерфейс образов GQMpeg, перенесенный из Каймана
+Comment[se]=GQMpeg-náhkkelakta portejuvvon Kaimanas
+Comment[sk]=Téma rozhrania GQMpeq prenesená z Kaimana
+Comment[sl]=Vmesnik preobleke GQMpeg, prenesen iz Kaimana
+Comment[sr]=GQMpeg интерфејс скинова пренесен са из Kaiman-а
+Comment[sr@Latn]=GQMpeg interfejs skinova prenesen sa iz Kaiman-a
+Comment[sv]=Gqmpeg-skalgränssnitt överfört från Kaiman
+Comment[ta]=GQMpeg தோல் இடைமுகம் காய்மானில் இருந்து இறக்கப்பட்டது
+Comment[tg]=Намуди интерфейси GQMpeg, ки аз Kaiman даргоҳбандӣ шудааст
+Comment[th]=ส่วนติดต่อหน้ากาก GQMpeg ที่ข้ามระบบมาให้ใช้กับ Kaiman
+Comment[tr]=Kaiman'dan alınan GQMpeg dekor arayüzü
+Comment[uk]=Інтерфейс жупанів GQMpeg, перенесено з Kaiman
+Comment[ven]=Lukanda lwa GQMpeg lu vhonwaho kha Kaiman
+Comment[xh]=GQMpeg wojongano nolusu olunezibuko olusuka kwi Kaiman
+Comment[zh_CN]=从 Kaiman 移植的 GQMpeg 外观
+Comment[zh_HK]=從 Kaiman 移植的 GQMpeg 外貌主題
+Comment[zh_TW]=從 Kaiman 移植的 GQMpeg 外表
+Comment[zu]=A GQMpeg uxhumano lwesikhumba ported from Kaiman
diff --git a/noatun/modules/kaiman/kaimanui.rc b/noatun/modules/kaiman/kaimanui.rc
new file mode 100644
index 00000000..fb2696ff
--- /dev/null
+++ b/noatun/modules/kaiman/kaimanui.rc
@@ -0,0 +1,45 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="noatunkaiman" version="1">
+<ActionProperties>
+ <Action name="play" icon="noatunplay"/>
+ <Action name="stop" icon="noatunstop"/>
+ <Action name="back" icon="noatunback"/>
+ <Action name="forward" icon="noatunforward"/>
+ <Action name="show_playlist" icon="noatunplaylist"/>
+</ActionProperties>
+<MenuBar>
+ <Menu name="file" noMerge="1"><text>&amp;File</text>
+ <Action name="file_open"/>
+ <Separator lineSeparator="true"/>
+ <Action name="file_quit"/>
+ </Menu>
+ <Menu name="go_music" noMerge="1"><text>&amp;Go</text>
+ <Action name="back"/>
+ <Action name="stop"/>
+ <Action name="play"/>
+ <Action name="forward"/>
+ </Menu>
+ <Menu name="settings" noMerge="1"><text>&amp;Settings</text>
+ <Action name="options_show_toolbar"/>
+ <Action name="show_playlist"/>
+ <Separator lineSeparator="true"/>
+ <Action name="options_configure"/>
+ <Action name="effects"/>
+ <Separator lineSeparator="true"/>
+ <Action name="loop_style"/>
+ </Menu>
+</MenuBar>
+<Toolbar name="main"><text>Main Toolbar</text>
+ <Action name="file_quit"/>
+ <Separator lineSeparator="true"/>
+ <Action name="back"/>
+ <Action name="stop"/>
+ <Action name="play"/>
+ <Action name="forward"/>
+ <Separator lineSeparator="true"/>
+ <Action name="file_open"/>
+ <Action name="show_playlist"/>
+ <Separator lineSeparator="true"/>
+ <Action name="loop_style"/>
+</Toolbar>
+</kpartgui>
diff --git a/noatun/modules/kaiman/noatunui.cpp b/noatun/modules/kaiman/noatunui.cpp
new file mode 100644
index 00000000..bc1bceb0
--- /dev/null
+++ b/noatun/modules/kaiman/noatunui.cpp
@@ -0,0 +1,9 @@
+#include "userinterface.h"
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new Kaiman();
+ }
+}
diff --git a/noatun/modules/kaiman/pref.cpp b/noatun/modules/kaiman/pref.cpp
new file mode 100644
index 00000000..892435fc
--- /dev/null
+++ b/noatun/modules/kaiman/pref.cpp
@@ -0,0 +1,122 @@
+/*
+ Copyright (c) 1999-2000 Stefan Schimanski <1Stein@gmx.de>
+
+ 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, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <klocale.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <klistbox.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <kglobal.h>
+#include <kdebug.h>
+#include <kstandarddirs.h>
+#include <kconfig.h>
+
+#include "pref.h"
+#include "userinterface.h"
+
+
+KaimanPrefDlg::KaimanPrefDlg(QObject *parent )
+ : CModule(i18n("Kaiman Skins"), i18n("Skin Selection for the Kaiman Plugin"), "style", parent)
+{
+ // create widgets
+ QVBoxLayout *topLayout = new QVBoxLayout( this, 6, 11 );
+ QLabel *label = new QLabel( i18n("Kaiman Skins"), this, "label" );
+ topLayout->addWidget( label );
+
+ _skinList = new KListBox( this, "skinList" );
+ topLayout->addWidget( _skinList, 1 );
+ reopen();
+}
+
+
+KaimanPrefDlg::~KaimanPrefDlg()
+{
+}
+
+
+void KaimanPrefDlg::save()
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup("Kaiman");
+ config->writeEntry("SkinResource", skin() );
+ config->sync();
+
+ Kaiman *l=Kaiman::kaiman;
+ if ( l ) {
+ l->changeStyle( skin() );
+ }
+}
+
+void KaimanPrefDlg::reopen()
+{
+ _skinList->clear();
+ // fill with available skins
+ KGlobal::dirs()->addResourceType("skins", KStandardDirs::kde_default("data") + "noatun/skins/kaiman/");
+ QStringList list = KGlobal::dirs()->resourceDirs("skins");
+ for (QStringList::ConstIterator it = list.begin(); it != list.end(); it++)
+ readSkinDir(*it);
+
+ // load current config
+ KConfig *config=KGlobal::config();
+ config->setGroup("Kaiman");
+ QString skin = config->readEntry( "SkinResource", Kaiman::DEFAULT_SKIN );
+ QListBoxItem *item = _skinList->findItem( skin );
+ if ( item )
+ _skinList->setCurrentItem( item );
+ else
+ _skinList->setCurrentItem( 0 );
+}
+
+
+void KaimanPrefDlg::setSkin( QString skin )
+{
+ _skinList->setCurrentItem( _skinList->findItem( skin ) );
+}
+
+
+QString KaimanPrefDlg::skin()
+{
+ return _skinList->currentText();
+}
+
+
+void KaimanPrefDlg::readSkinDir( const QString &dir )
+{
+ kdDebug() << "readSkinDir " << dir << endl;
+
+ QDir directory( dir );
+ if (!directory.exists())
+ return;
+
+ const QFileInfoList *list = directory.entryInfoList();
+ QFileInfoListIterator it(*list);
+
+ while ( it.current() ) {
+ kdDebug() << it.current()->absFilePath() << endl;
+ QFileInfo skindata( it.current()->absFilePath()+"/skindata" );
+
+ if ( skindata.exists() ) {
+ _skinList->insertItem( it.current()->baseName() );
+ }
+
+ ++it;
+ }
+}
+
+#include "pref.moc"
diff --git a/noatun/modules/kaiman/pref.h b/noatun/modules/kaiman/pref.h
new file mode 100644
index 00000000..551dd16a
--- /dev/null
+++ b/noatun/modules/kaiman/pref.h
@@ -0,0 +1,48 @@
+/*
+ Copyright (c) 1999-2000 Stefan Schimanski <1Stein@gmx.de>
+
+ 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, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef PREF_H_INCLUDED
+#define PREF_H_INCLUDED
+
+#include <noatun/pref.h>
+
+class KListBox;
+class QLabel;
+
+class KaimanPrefDlg : public CModule
+{
+ Q_OBJECT
+ public:
+ KaimanPrefDlg( QObject *parent );
+ virtual ~KaimanPrefDlg();
+
+ virtual void save();
+ virtual void reopen();
+
+ public slots:
+ void setSkin( QString skin );
+ QString skin();
+
+ private:
+ void readSkinDir( const QString &dir );
+
+ KListBox *_skinList;
+};
+
+#endif
+
diff --git a/noatun/modules/kaiman/skins/Makefile.am b/noatun/modules/kaiman/skins/Makefile.am
new file mode 100644
index 00000000..0d3d6687
--- /dev/null
+++ b/noatun/modules/kaiman/skins/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = k9 car-preset circle
+
+skinsdir = $(kde_datadir)/Skins
diff --git a/noatun/modules/kaiman/skins/car-preset/Makefile.am b/noatun/modules/kaiman/skins/car-preset/Makefile.am
new file mode 100644
index 00000000..646898d0
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/Makefile.am
@@ -0,0 +1,10 @@
+skin_DATA = btn_p1.png btn_play.png digbig.png random.png \
+ btn_p2.png btn_prev.png digmed.png repeat.png \
+btn_exit.png btn_p3.png btn_sml.png letters.png skindata \
+btn_iconify.png btn_p4.png btn_stop.png main.png status.png \
+btn_list.png btn_p5.png btn_voldn.png monoster.png volume.png \
+btn_next.png btn_p6.png btn_volup.png posbar.png
+
+skindir = $(kde_datadir)/noatun/skins/kaiman/car-preset
+
+EXTRA_DIST = $(skin_DATA)
diff --git a/noatun/modules/kaiman/skins/car-preset/README b/noatun/modules/kaiman/skins/car-preset/README
new file mode 100644
index 00000000..fedefaaa
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/README
@@ -0,0 +1,22 @@
+GQmpeg skin directory: car-preset
+Author: Johne Ellis <gqview@geocities.ocm>
+Released: October 25, 1998
+Version: 1.0
+URL:
+Comment: Skin similar to an automotive stereo, complete
+ with presets.
+Note: For the presets and volume controls to work, GQmpeg
+ 0.3.6 is required. Previous versions will work,
+ except for these functions.
+
+To use this skin with GQmpeg, use the command line:
+
+ gqmpeg -skin:car-preset
+
+Or when editing skin data, point GQmpeg to this skin with:
+
+ gqmpeg -skin:../car-preset
+
+or to allow the skin to be the default skin copy this directory
+to 'HOME/.gqmpeg/skins/car-preset' and specify 'car-preset' as the
+skin on the skin tab of the config dialog.
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_exit.png b/noatun/modules/kaiman/skins/car-preset/btn_exit.png
new file mode 100644
index 00000000..5bac9d23
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_exit.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_iconify.png b/noatun/modules/kaiman/skins/car-preset/btn_iconify.png
new file mode 100644
index 00000000..81b2859a
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_iconify.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_list.png b/noatun/modules/kaiman/skins/car-preset/btn_list.png
new file mode 100644
index 00000000..1bea110e
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_list.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_next.png b/noatun/modules/kaiman/skins/car-preset/btn_next.png
new file mode 100644
index 00000000..67a3db2a
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_next.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_p1.png b/noatun/modules/kaiman/skins/car-preset/btn_p1.png
new file mode 100644
index 00000000..4877b86e
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_p1.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_p2.png b/noatun/modules/kaiman/skins/car-preset/btn_p2.png
new file mode 100644
index 00000000..1f6b1f41
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_p2.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_p3.png b/noatun/modules/kaiman/skins/car-preset/btn_p3.png
new file mode 100644
index 00000000..d3ec7ab5
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_p3.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_p4.png b/noatun/modules/kaiman/skins/car-preset/btn_p4.png
new file mode 100644
index 00000000..16b57b7c
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_p4.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_p5.png b/noatun/modules/kaiman/skins/car-preset/btn_p5.png
new file mode 100644
index 00000000..18d65172
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_p5.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_p6.png b/noatun/modules/kaiman/skins/car-preset/btn_p6.png
new file mode 100644
index 00000000..2b0eba96
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_p6.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_play.png b/noatun/modules/kaiman/skins/car-preset/btn_play.png
new file mode 100644
index 00000000..814cbbf9
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_play.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_prev.png b/noatun/modules/kaiman/skins/car-preset/btn_prev.png
new file mode 100644
index 00000000..ffdc59a5
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_prev.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_sml.png b/noatun/modules/kaiman/skins/car-preset/btn_sml.png
new file mode 100644
index 00000000..a2acff28
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_sml.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_stop.png b/noatun/modules/kaiman/skins/car-preset/btn_stop.png
new file mode 100644
index 00000000..faca588f
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_stop.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_voldn.png b/noatun/modules/kaiman/skins/car-preset/btn_voldn.png
new file mode 100644
index 00000000..15e40697
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_voldn.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_volup.png b/noatun/modules/kaiman/skins/car-preset/btn_volup.png
new file mode 100644
index 00000000..9e6c8964
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_volup.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/digbig.png b/noatun/modules/kaiman/skins/car-preset/digbig.png
new file mode 100644
index 00000000..44ea6a9d
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/digbig.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/digmed.png b/noatun/modules/kaiman/skins/car-preset/digmed.png
new file mode 100644
index 00000000..55a43732
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/digmed.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/letters.png b/noatun/modules/kaiman/skins/car-preset/letters.png
new file mode 100644
index 00000000..7f59af69
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/letters.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/main.png b/noatun/modules/kaiman/skins/car-preset/main.png
new file mode 100644
index 00000000..2601e03b
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/main.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/monoster.png b/noatun/modules/kaiman/skins/car-preset/monoster.png
new file mode 100644
index 00000000..fe8129b7
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/monoster.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/posbar.png b/noatun/modules/kaiman/skins/car-preset/posbar.png
new file mode 100644
index 00000000..3d6eb8e8
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/posbar.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/random.png b/noatun/modules/kaiman/skins/car-preset/random.png
new file mode 100644
index 00000000..30b99726
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/random.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/repeat.png b/noatun/modules/kaiman/skins/car-preset/repeat.png
new file mode 100644
index 00000000..022648fd
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/repeat.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/skindata b/noatun/modules/kaiman/skins/car-preset/skindata
new file mode 100644
index 00000000..7a9651e0
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/skindata
@@ -0,0 +1,71 @@
+#GQmpeg skin data file
+
+#Title: Car faceplate with presets
+#Version: 1
+#Released: October 25, 1998
+#Author: John Ellis <gqview@geocities.com>
+#URL:
+#Comments: Skin similar to an automotive stereo, complete
+# with presets.
+
+#run 'gqmpeg -skinhelp' for help with coordinates.
+#simply comment out items you do not want to display
+#only Background is required.
+Background: main.png
+
+#Title: filename length x y
+Title: letters.png 16 60 18
+
+#Play/Pause/Stop/Shuffle/Repeat_Button: filename prelight status_light x y
+Play_Button: btn_play.png FALSE FALSE 25 32
+Stop_Button: btn_stop.png FALSE FALSE 4 32
+Shuffle_Button: btn_sml.png FALSE FALSE 265 41
+Repeat_Button: btn_sml.png FALSE FALSE 283 41
+
+#Next/Prev/FF/RW/Playlist/Config/
+# Iconify/Mixer/Exit/Alt_Skin_Button: prelight x y
+Next_Button: btn_next.png FALSE 7 18
+Prev_Button: btn_prev.png FALSE 7 51
+Playlist_Button: btn_list.png FALSE 11 66
+Config_Button: btn_sml.png FALSE 283 23
+Iconify_Button: btn_iconify.png FALSE 263 4
+Exit_Button: btn_exit.png FALSE 280 4
+
+#Stereo/Shuffle/Repeat/Mpegver/Mpeglayer/Mpegmode/Status_Item: filename x y
+Stereo_Item: monoster.png 161 49
+Shuffle_Item: random.png 161 38
+Repeat_Item: repeat.png 205 38
+Status_Item: status.png 57 39
+
+#you can define one or both of these first:
+Digit_Large_Default: digbig.png
+Digit_Small_Default: digmed.png
+#then use Large or Small as the filename in *_Number below (convenience feature)
+
+#Minute/Second/Song/Total/In_Rate/In_Hz/Out_Bits/Out_Hz/Frame/CPU_Number: filename x y
+Minute_Number: Large 65 38
+Second_Number: Large 97 38
+Song_Number: Small 129 44
+CPU_Number: Small 236 44
+
+#Volume/Balance_Item: filename sections x y
+# (these 2 items must be before their respective Volume/Balance_Sliders)
+Volume_Item: volume.png 17 181 48
+
+#Volume_Up/Volume_Down/Balance_Left/Balance_Right_Button: filename prelight x y
+Volume_Up_Button: btn_volup.png FALSE 261 60
+Volume_Down_Button: btn_voldn.png FALSE 279 60
+
+#Position/Volume/Balance_Slider: filename prelight length x y
+Position_Slider: posbar.png FALSE 195 58 5
+
+#Preset_1_ ... Preset_10_Button: filename prelight x y
+Preset_1_Button: btn_p1.png FALSE 58 66
+Preset_2_Button: btn_p2.png FALSE 91 66
+Preset_3_Button: btn_p3.png FALSE 124 66
+Preset_4_Button: btn_p4.png FALSE 157 66
+Preset_5_Button: btn_p5.png FALSE 190 66
+Preset_6_Button: btn_p6.png FALSE 223 66
+
+# end
+
diff --git a/noatun/modules/kaiman/skins/car-preset/status.png b/noatun/modules/kaiman/skins/car-preset/status.png
new file mode 100644
index 00000000..05ceed86
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/status.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/volume.png b/noatun/modules/kaiman/skins/car-preset/volume.png
new file mode 100644
index 00000000..f690e79d
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/volume.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/Makefile.am b/noatun/modules/kaiman/skins/circle/Makefile.am
new file mode 100644
index 00000000..6c8ae5f2
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/Makefile.am
@@ -0,0 +1,13 @@
+skin_DATA = btn_exit.png btn_repeat.png btn_sm_stop.png \
+ btn_iconify.png btn_shuffle.png btn_stop.png \
+back.png btn_list.png btn_sm_exit.png dig.png \
+back_mask.png btn_mode.png btn_sm_iconify.png digsml.png \
+back_sm.png btn_next.png btn_sm_mode.png letters.png \
+back_sm_mask.png btn_play.png btn_sm_next.png skindata \
+bar_pos.png btn_pref.png btn_sm_play.png \
+bar_vol.png btn_prev.png btn_sm_prev.png status.png
+
+
+skindir = $(kde_datadir)/noatun/skins/kaiman/circle
+
+EXTRA_DIST = $(skin_DATA)
diff --git a/noatun/modules/kaiman/skins/circle/README b/noatun/modules/kaiman/skins/circle/README
new file mode 100644
index 00000000..d80082af
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/README
@@ -0,0 +1,22 @@
+GQmpeg skin directory: circle
+Author: Johne Ellis <gqview@geocities.ocm>
+Released: November 25, 1998
+Version: 1.0
+URL: http://www.geocities.com/SiliconValley/Haven/5235
+Comments: Skin with a doughnut shape to test shaped windows.
+ (skins with a shape mask)
+
+Note: For transparency to work, GQmpeg 0.4.2 is required.
+ Previous versions will work, but will be _ugly_.
+
+To use this skin with GQmpeg, use the command line:
+
+ gqmpeg -skin:circle
+
+Or when editing skin data, point GQmpeg to this skin with:
+
+ gqmpeg -skin:../circle
+
+or to allow the skin to be the default skin copy this directory
+to 'HOME/.gqmpeg/skins/circle' and specify 'circle' as the
+skin on the skin tab of the config dialog.
diff --git a/noatun/modules/kaiman/skins/circle/back.png b/noatun/modules/kaiman/skins/circle/back.png
new file mode 100644
index 00000000..83758a14
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/back.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/back_mask.png b/noatun/modules/kaiman/skins/circle/back_mask.png
new file mode 100644
index 00000000..de54ddef
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/back_mask.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/back_sm.png b/noatun/modules/kaiman/skins/circle/back_sm.png
new file mode 100644
index 00000000..3263c2db
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/back_sm.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/back_sm_mask.png b/noatun/modules/kaiman/skins/circle/back_sm_mask.png
new file mode 100644
index 00000000..315d88bf
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/back_sm_mask.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/bar_pos.png b/noatun/modules/kaiman/skins/circle/bar_pos.png
new file mode 100644
index 00000000..e6ac85a3
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/bar_pos.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/bar_vol.png b/noatun/modules/kaiman/skins/circle/bar_vol.png
new file mode 100644
index 00000000..88f88651
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/bar_vol.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_exit.png b/noatun/modules/kaiman/skins/circle/btn_exit.png
new file mode 100644
index 00000000..cd36d2fd
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_exit.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_iconify.png b/noatun/modules/kaiman/skins/circle/btn_iconify.png
new file mode 100644
index 00000000..20647819
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_iconify.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_list.png b/noatun/modules/kaiman/skins/circle/btn_list.png
new file mode 100644
index 00000000..f51e322b
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_list.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_mode.png b/noatun/modules/kaiman/skins/circle/btn_mode.png
new file mode 100644
index 00000000..4c6db0e1
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_mode.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_next.png b/noatun/modules/kaiman/skins/circle/btn_next.png
new file mode 100644
index 00000000..4ae8ee71
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_next.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_play.png b/noatun/modules/kaiman/skins/circle/btn_play.png
new file mode 100644
index 00000000..f21d287e
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_play.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_pref.png b/noatun/modules/kaiman/skins/circle/btn_pref.png
new file mode 100644
index 00000000..9337e704
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_pref.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_prev.png b/noatun/modules/kaiman/skins/circle/btn_prev.png
new file mode 100644
index 00000000..c65a9298
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_prev.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_repeat.png b/noatun/modules/kaiman/skins/circle/btn_repeat.png
new file mode 100644
index 00000000..48b12a90
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_repeat.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_shuffle.png b/noatun/modules/kaiman/skins/circle/btn_shuffle.png
new file mode 100644
index 00000000..75ad39f9
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_shuffle.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_sm_exit.png b/noatun/modules/kaiman/skins/circle/btn_sm_exit.png
new file mode 100644
index 00000000..f14752f8
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_sm_exit.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_sm_iconify.png b/noatun/modules/kaiman/skins/circle/btn_sm_iconify.png
new file mode 100644
index 00000000..69b9332c
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_sm_iconify.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_sm_mode.png b/noatun/modules/kaiman/skins/circle/btn_sm_mode.png
new file mode 100644
index 00000000..79b4c5a0
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_sm_mode.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_sm_next.png b/noatun/modules/kaiman/skins/circle/btn_sm_next.png
new file mode 100644
index 00000000..66c01ddb
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_sm_next.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_sm_play.png b/noatun/modules/kaiman/skins/circle/btn_sm_play.png
new file mode 100644
index 00000000..191551eb
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_sm_play.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_sm_prev.png b/noatun/modules/kaiman/skins/circle/btn_sm_prev.png
new file mode 100644
index 00000000..d70dbb53
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_sm_prev.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_sm_stop.png b/noatun/modules/kaiman/skins/circle/btn_sm_stop.png
new file mode 100644
index 00000000..0d0841d7
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_sm_stop.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_stop.png b/noatun/modules/kaiman/skins/circle/btn_stop.png
new file mode 100644
index 00000000..2563dbc0
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_stop.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/dig.png b/noatun/modules/kaiman/skins/circle/dig.png
new file mode 100644
index 00000000..c417de15
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/dig.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/digsml.png b/noatun/modules/kaiman/skins/circle/digsml.png
new file mode 100644
index 00000000..29f2ed43
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/digsml.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/letters.png b/noatun/modules/kaiman/skins/circle/letters.png
new file mode 100644
index 00000000..b4322d62
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/letters.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/skindata b/noatun/modules/kaiman/skins/circle/skindata
new file mode 100644
index 00000000..25268d22
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/skindata
@@ -0,0 +1,58 @@
+#GQmpeg skin data file
+#tested on version 0.4.2
+
+#Title: circle
+#Version: 1
+#Released: November 25, 1998
+#Author: John Ellis <gqview@geocities.com>
+#URL: http://www.geocities.com/SiliconValley/Haven/5235/
+#Comments: Skin with a doughnut shape to test shaped windows.
+# (skins with a shape mask)
+
+#run 'gqmpeg -skinhelp' for help with coordinates.
+#simply comment out items you do not want to display
+#only Background is required.
+Background: back.png
+
+#Mask is an image with transparency used to define a shaped window
+Mask: back_mask.png
+
+#Title: filename length x y
+Title: letters.png 23 32 86
+
+#Play/Pause/Stop/Shuffle/Repeat_Button: filename prelight status_light x y
+Play_Button: btn_play.png TRUE TRUE 125 160
+Stop_Button: btn_stop.png TRUE TRUE 44 160
+Shuffle_Button: btn_shuffle.png TRUE TRUE 151 29
+Repeat_Button: btn_repeat.png TRUE TRUE 164 49
+
+#Next/Prev/FF/RW/Playlist/Config/
+# Iconify/Mixer/Exit/Alt_Skin_Button: prelight x y
+Next_Button: btn_next.png TRUE 125 10
+Prev_Button: btn_prev.png TRUE 44 10
+Playlist_Button: btn_list.png TRUE 158 130
+Config_Button: btn_pref.png TRUE 13 130
+Iconify_Button: btn_iconify.png TRUE 3 69
+Exit_Button: btn_exit.png TRUE 13 40
+Alt_Skin_Button: btn_mode.png TRUE 3 103
+
+#Stereo/Shuffle/Repeat/Mpegver/Mpeglayer/Mpegmode/Status_Item: filename x y
+#Stereo_Item: stereo.png 280 26
+Status_Item: status.png 82 20
+
+#you can define one or both of these first:
+Digit_Large_Default: dig.png
+#then use Large or Small as the filename in *_Number below (convenience feature)
+
+#Minute/Second/Song/Total/In_Rate/In_Hz/Out_Bits/Out_Hz/Frame/CPU_Number: filename x y
+Minute_Number: Large 79 176
+Second_Number: Large 102 176
+Song_Number: Large 91 7
+CPU_Number: digsml.png 78 8
+
+#Position/Volume/Balance_Slider: filename prelight verticle reversed length x y
+Position_Slider: bar_pos.png TRUE FALSE FALSE 142 30 103
+Volume_Slider: bar_vol.png TRUE TRUE TRUE 46 175 77
+
+# end
+
diff --git a/noatun/modules/kaiman/skins/circle/skindata_alt b/noatun/modules/kaiman/skins/circle/skindata_alt
new file mode 100644
index 00000000..fb3e97f5
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/skindata_alt
@@ -0,0 +1,44 @@
+#GQmpeg skin data file
+#tested on version 0.4.2
+
+#Title: circle
+#Version: 1
+#Released: November 25, 1998
+#Author: John Ellis <gqview@geocities.com>
+#URL: http://www.geocities.com/SiliconValley/Haven/5235/
+#Comments: Skin with a doughnut shape to test shaped windows.
+# (skins with a shape mask)
+
+#run 'gqmpeg -skinhelp' for help with coordinates.
+#simply comment out items you do not want to display
+#only Background is required.
+Background: back_sm.png
+
+#Mask is an image with transparency used to define a shaped window
+Mask: back_sm_mask.png
+
+#Title: filename length x y
+Title: letters.png 21 8 4
+
+#Play/Pause/Stop/Shuffle/Repeat_Button: filename prelight status_light x y
+Play_Button: btn_sm_play.png TRUE TRUE 21 21
+Stop_Button: btn_sm_stop.png TRUE TRUE 4 21
+
+#Next/Prev/FF/RW/Playlist/Config/
+# Iconify/Mixer/Exit/Alt_Skin_Button: prelight x y
+Next_Button: btn_sm_next.png TRUE 87 21
+Prev_Button: btn_sm_prev.png TRUE 70 21
+Iconify_Button: btn_sm_iconify.png TRUE 120 29
+Exit_Button: btn_sm_exit.png TRUE 120 21
+Alt_Skin_Button: btn_sm_mode.png TRUE 104 21
+
+#you can define one or both of these first:
+Digit_Small_Default: digsml.png
+#then use Large or Small as the filename in *_Number below (convenience feature)
+
+#Minute/Second/Song/Total/In_Rate/In_Hz/Out_Bits/Out_Hz/Frame/CPU_Number: filename x y
+Minute_Number: Small 41 24
+Second_Number: Small 55 24
+
+# end
+
diff --git a/noatun/modules/kaiman/skins/circle/status.png b/noatun/modules/kaiman/skins/circle/status.png
new file mode 100644
index 00000000..b29e075c
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/status.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/Makefile.am b/noatun/modules/kaiman/skins/k9/Makefile.am
new file mode 100644
index 00000000..bac3bd84
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/Makefile.am
@@ -0,0 +1,11 @@
+skin_DATA = README skindata conf.jpg conf.png eject.jpg icon.jpg icon.png \
+ kill.jpg kill.png knine-nfont.jpg knine-nfont.png knine-nfont2.jpg \
+ knine-nfont2.png knine-normal2.jpg knine-normal2.png knine-vfont.jpg \
+ knine-vfont.png long2.jpg mask.png newtext.jpg newtext.png next.jpg \
+ pause.jpg play.jpg pos_item.jpg repeat.jpg repeat.png reverse.jpg \
+ shuffle.jpg shuffle.png small-k.jpg small-k.png square.jpg square.png \
+ status.jpg status.png stop.jpg trans-pos.png trans-slide.png
+
+skindir = $(kde_datadir)/noatun/skins/kaiman/k9
+
+EXTRA_DIST = $(skin_DATA)
diff --git a/noatun/modules/kaiman/skins/k9/README b/noatun/modules/kaiman/skins/k9/README
new file mode 100644
index 00000000..8425d0c3
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/README
@@ -0,0 +1,24 @@
+
+This skin is totally (c) 1999 to Morgan aka."Splif" Thomas /
+the Aegis Corporation. Please don't rip, sell, use in public
+without the author's acceptation. Thanks. But you are
+totally free to distribute it all around the universe.
+_____________________________________________________________
+Ported by Me, 'cuz "A skin like this belongs under
+Enlightenment, not windows."
+ DNAspark99
+Contact: jedeye_one@hotmail.com
+check out: http://www3.bc.sympatico.ca/desperados
+
+Notes: I reversed the iris, so it "closes" as volume increases.
+If ya wanna try it the default way, edit the "Volume_Item" line in
+the skindata file and change it to "long" instead of "long2"
+
+ & Too bad GQmpeg doesn't have fancy playlist customization like those
+"other" mp3 players......this skin has a "kill" one of those too...
+----------------------------------------------------- --- --
+origional artist:
+Contact: Splif@Aegis-Corp.org
+Home: http://www.Aegis-Corp.org/Splif/
+
+____________________________________________________________
diff --git a/noatun/modules/kaiman/skins/k9/conf.jpg b/noatun/modules/kaiman/skins/k9/conf.jpg
new file mode 100644
index 00000000..91be54d1
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/conf.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/conf.png b/noatun/modules/kaiman/skins/k9/conf.png
new file mode 100644
index 00000000..56860e00
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/conf.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/eject.jpg b/noatun/modules/kaiman/skins/k9/eject.jpg
new file mode 100644
index 00000000..f1d00f09
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/eject.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/icon.jpg b/noatun/modules/kaiman/skins/k9/icon.jpg
new file mode 100644
index 00000000..3740549b
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/icon.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/icon.png b/noatun/modules/kaiman/skins/k9/icon.png
new file mode 100644
index 00000000..e9057671
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/icon.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/kill.jpg b/noatun/modules/kaiman/skins/k9/kill.jpg
new file mode 100644
index 00000000..8d52aa86
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/kill.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/kill.png b/noatun/modules/kaiman/skins/k9/kill.png
new file mode 100644
index 00000000..8ec851dd
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/kill.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/knine-nfont.jpg b/noatun/modules/kaiman/skins/k9/knine-nfont.jpg
new file mode 100644
index 00000000..5010b881
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/knine-nfont.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/knine-nfont.png b/noatun/modules/kaiman/skins/k9/knine-nfont.png
new file mode 100644
index 00000000..2e166ee1
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/knine-nfont.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/knine-nfont2.jpg b/noatun/modules/kaiman/skins/k9/knine-nfont2.jpg
new file mode 100644
index 00000000..7511230a
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/knine-nfont2.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/knine-nfont2.png b/noatun/modules/kaiman/skins/k9/knine-nfont2.png
new file mode 100644
index 00000000..63e45974
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/knine-nfont2.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/knine-normal2.jpg b/noatun/modules/kaiman/skins/k9/knine-normal2.jpg
new file mode 100644
index 00000000..b744ac82
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/knine-normal2.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/knine-normal2.png b/noatun/modules/kaiman/skins/k9/knine-normal2.png
new file mode 100644
index 00000000..e0d95dc8
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/knine-normal2.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/knine-vfont.jpg b/noatun/modules/kaiman/skins/k9/knine-vfont.jpg
new file mode 100644
index 00000000..a7c27bbf
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/knine-vfont.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/knine-vfont.png b/noatun/modules/kaiman/skins/k9/knine-vfont.png
new file mode 100644
index 00000000..e8692e80
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/knine-vfont.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/long2.jpg b/noatun/modules/kaiman/skins/k9/long2.jpg
new file mode 100644
index 00000000..321c121a
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/long2.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/mask.png b/noatun/modules/kaiman/skins/k9/mask.png
new file mode 100644
index 00000000..9ea57d2b
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/mask.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/newtext.jpg b/noatun/modules/kaiman/skins/k9/newtext.jpg
new file mode 100644
index 00000000..69c437f2
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/newtext.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/newtext.png b/noatun/modules/kaiman/skins/k9/newtext.png
new file mode 100644
index 00000000..e8f25356
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/newtext.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/next.jpg b/noatun/modules/kaiman/skins/k9/next.jpg
new file mode 100644
index 00000000..0a0e5267
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/next.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/pause.jpg b/noatun/modules/kaiman/skins/k9/pause.jpg
new file mode 100644
index 00000000..1144e70d
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/pause.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/play.jpg b/noatun/modules/kaiman/skins/k9/play.jpg
new file mode 100644
index 00000000..dbfe446f
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/play.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/pos_item.jpg b/noatun/modules/kaiman/skins/k9/pos_item.jpg
new file mode 100644
index 00000000..0ba2f333
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/pos_item.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/repeat.jpg b/noatun/modules/kaiman/skins/k9/repeat.jpg
new file mode 100644
index 00000000..774e0804
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/repeat.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/repeat.png b/noatun/modules/kaiman/skins/k9/repeat.png
new file mode 100644
index 00000000..84810e5b
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/repeat.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/reverse.jpg b/noatun/modules/kaiman/skins/k9/reverse.jpg
new file mode 100644
index 00000000..a9b74366
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/reverse.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/shuffle.jpg b/noatun/modules/kaiman/skins/k9/shuffle.jpg
new file mode 100644
index 00000000..1d52ac9b
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/shuffle.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/shuffle.png b/noatun/modules/kaiman/skins/k9/shuffle.png
new file mode 100644
index 00000000..a3e22250
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/shuffle.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/skindata b/noatun/modules/kaiman/skins/k9/skindata
new file mode 100644
index 00000000..1fdc4320
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/skindata
@@ -0,0 +1,73 @@
+#
+# port of K-Nine skin for K-Jofol
+# original art done by Morgan aka. "Splif" Thomas / the Aegis Corp.
+#
+# "'cuz a skin like this belongs under Enlightenment"
+#
+# DNAspark99
+#
+#
+######################################################
+
+Background: knine-normal2.jpg
+Mask: mask.png
+
+Play_Button: play.jpg FALSE FALSE 55 133 89 166
+Stop_Button: stop.jpg FALSE FALSE 28 124 52 145
+Pause_Button: pause.jpg FALSE 91 148 116 173
+Prev_Button: reverse.jpg FALSE 13 108 32 127
+Next_Button: next.jpg FALSE 122 154 141 173
+Repeat_Button: repeat.jpg FALSE TRUE 50 114 63 128
+Shuffle_Button: shuffle.jpg FALSE TRUE 102 134 118 147
+Playlist_Button: eject.jpg FALSE 78 106 104
+Mixer_Button: square.jpg FALSE 294 110 310 126
+Exit_Button: kill.jpg FALSE 282 57 295 69
+Iconify_Button: icon.jpg FALSE 292 85 304 96
+
+Config_Button: conf.jpg FALSE 222 163 242 181
+
+Alt_Skin_Button: small-k.jpg FALSE 287 138 299 153
+
+
+Minute_Number: knine-nfont2.jpg 69 64
+Second_Number: knine-nfont2.jpg 91 64
+
+In_Rate_Number: knine-vfont.jpg 123 59
+In_Hz_Number: knine-vfont.jpg 132 50
+
+Song_Number: knine-vfont.jpg 113 50
+
+Status_Item: status.jpg 113 68
+
+CPU_Number: knine-vfont.jpg 80 50
+
+Digit_Large: knine-vfont.jpg 113 40
+Digit_Small_Default: knine-vfont.jpg
+
+Title: newtext.jpg 26 19 87
+
+#RW_Button: back.jpg FALSE 35 192
+#FF_Button: ff.jpg FALSE 51 214
+
+#Preset_1_Button: list_1.jpg TRUE 70 70
+#Preset_2_Button: list_2.jpg TRUE 77 70
+#Preset_3_Button: list_3.jpg TRUE 84 70
+#Preset_4_Button: list_4.jpg TRUE 91 70
+#Preset_5_Button: list_5.jpg TRUE 98 70
+#Preset_6_Button: list_6.jpg TRUE 70 80
+#Preset_7_Button: list_7.jpg TRUE 77 80
+#Preset_8_Button: list_8.jpg TRUE 84 80
+#Preset_9_Button: list_9.jpg TRUE 91 80
+#Preset_10_Button: list_10.jpg TRUE 98 80
+
+
+Volume_Item: long2.jpg 27 199 76
+Volume_Slider: trans-slide.png FALSE FALSE FALSE 71 205 76 210
+
+Position_Item: pos_item.jpg 32 128 2 30
+Position_Slider: trans-pos.png FALSE FALSE FALSE 140 128 2 70
+
+#Balance_Item: balance.jpg FALSE FALSE FALSE 10 156 95
+#Balance_Slider: knine-pitchbtn.jpg 57 156 95
+
+# END
diff --git a/noatun/modules/kaiman/skins/k9/small-k.jpg b/noatun/modules/kaiman/skins/k9/small-k.jpg
new file mode 100644
index 00000000..219ebb05
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/small-k.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/small-k.png b/noatun/modules/kaiman/skins/k9/small-k.png
new file mode 100644
index 00000000..2a43a4a8
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/small-k.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/square.jpg b/noatun/modules/kaiman/skins/k9/square.jpg
new file mode 100644
index 00000000..c13b1644
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/square.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/square.png b/noatun/modules/kaiman/skins/k9/square.png
new file mode 100644
index 00000000..d77de4ef
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/square.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/status.jpg b/noatun/modules/kaiman/skins/k9/status.jpg
new file mode 100644
index 00000000..db6c61a1
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/status.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/status.png b/noatun/modules/kaiman/skins/k9/status.png
new file mode 100644
index 00000000..682d01b4
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/status.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/stop.jpg b/noatun/modules/kaiman/skins/k9/stop.jpg
new file mode 100644
index 00000000..0ca261e3
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/stop.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/trans-pos.png b/noatun/modules/kaiman/skins/k9/trans-pos.png
new file mode 100644
index 00000000..0eecf3bb
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/trans-pos.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/trans-slide.png b/noatun/modules/kaiman/skins/k9/trans-slide.png
new file mode 100644
index 00000000..76aa00d7
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/trans-slide.png
Binary files differ
diff --git a/noatun/modules/kaiman/style.cpp b/noatun/modules/kaiman/style.cpp
new file mode 100644
index 00000000..d42ed45f
--- /dev/null
+++ b/noatun/modules/kaiman/style.cpp
@@ -0,0 +1,1504 @@
+/*
+ Copyright (c) 2000 Stefan Schimanski (1Stein@gmx.de)
+ 1999-2000 Christian Esken (esken@kde.org)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+#include <kapplication.h>
+#include <kstandarddirs.h>
+#include <kmessagebox.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qpainter.h>
+#include <qdropsite.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kurldrag.h>
+#include <qtimer.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "style.h"
+#include "userinterface.h"
+#include <noatun/app.h>
+#include <noatun/stdaction.h>
+
+const bool KaimanStyleSlider::optionVertical = 1;
+const bool KaimanStyleSlider::optionReversed = 2;
+const bool KaimanStyleText::optionExtended = 1;
+
+KaimanStyleElement::KaimanStyleElement(QWidget *parent, const char *name)
+ : QWidget(parent, name)
+{
+ // Initialize everything to default values
+ filename = "";
+ element = "";
+ upperLeft.setX(0);
+ upperLeft.setY(0);
+ dimension.setWidth(0);
+ dimension.setHeight(0);
+ optionPrelight = optionStatuslight = false;
+ options[0] = options[1] = options[2] = false;
+ _currentPixmap = 0;
+ digits = -1;
+ pixmapLines = 1;
+ pixmapColumns = 1;
+
+ setAcceptDrops(true);
+ pixmaps.setAutoDelete(true);
+
+ setBackgroundMode( NoBackground );
+}
+
+
+KaimanStyleElement::~KaimanStyleElement()
+{
+}
+
+
+void KaimanStyleElement::loadPixmaps(QString &val_s_filename)
+{
+ QPixmap pixmap;
+
+ bool i_b_ret = pixmap.load(val_s_filename);
+ pixmapNum = pixmapLines*pixmapColumns;
+
+ pixmaps.resize(pixmapNum);
+
+ if ( i_b_ret ) {
+ if(pixmapNum) {
+ int firstWidth, firstHeight, width, height, sourcex = 0, sourcey = 0;
+
+ // first bitmap may be with different size
+ if ( dimension.width()!=0 )
+ {
+ firstWidth = dimension.width();
+ if ( pixmapColumns>1 )
+ width = (pixmap.width()-firstWidth) / (pixmapColumns-1);
+ else
+ width = 0;
+ } else
+ firstWidth = width = pixmap.width() / pixmapColumns;
+
+ if ( dimension.height()!=0 )
+ {
+ firstHeight = dimension.height();
+ if ( pixmapLines>1 )
+ height = (pixmap.height()-firstHeight) / (pixmapLines-1);
+ else
+ height = 0;
+ } else
+ firstHeight = height = pixmap.height() / pixmapLines;
+
+ // create single pixmaps
+ int i=0;
+ sourcey = 0;
+ for( int y=0; y<pixmapLines; y++ )
+ {
+ int h = (y==0) ? firstHeight : height;
+ sourcex = 0;
+
+ for( int x=0; x<pixmapColumns; x++ )
+ {
+ int w = (x==0) ? firstWidth : width;
+
+ QPixmap *part = new QPixmap(w,h,pixmap.depth());
+ part->fill(Qt::black);
+ bitBlt(part,0,0,&pixmap,sourcex,sourcey,w,h);
+ pixmaps.insert(i,part);
+
+ if(pixmap.mask())
+ {
+ QBitmap maskpart(w,h);
+ bitBlt(&maskpart,0,0,pixmap.mask(),sourcex,sourcey,w,h);
+ part->setMask(maskpart);
+ }
+
+ i++;
+ sourcex += w;
+ }
+
+ sourcey += h;
+ }
+ }
+ } else {
+ kdDebug() << "Cannot load pixmap " << val_s_filename << endl;
+
+ for ( int i=0; i<pixmapNum; i++ )
+ {
+ QPixmap *pm = new QPixmap(10, 10);
+ pm->fill(Qt::black);
+ pixmaps.insert( i, pm );
+ }
+ }
+
+ if ( dimension.width()==0 ) dimension.setWidth( pixmaps[0]->width() );
+ if ( dimension.height()==0 ) dimension.setHeight( pixmaps[0]->height() );
+ setGeometry( QRect(upperLeft, dimension) );
+}
+
+void KaimanStyleElement::setPixmap( int num )
+{
+ if ( num!=_currentPixmap )
+ {
+ if ( num>pixmapNum-1 ) num = pixmapNum-1;
+ if ( num<0 ) num = 0;
+
+ _currentPixmap = num;
+ repaint( FALSE );
+ }
+}
+
+void KaimanStyleElement::paintEvent ( QPaintEvent */*qpe*/ )
+{
+ QPixmap *pm = pixmaps[_currentPixmap];
+ if ( pm )
+ bitBlt(this, 0, 0, pm );
+ else
+ kdDebug() << "Invalid pixmap" << endl;
+
+/* QPainter p( this );
+ p.setBrush( NoBrush );
+ p.setPen( QColor(255,255,255) );
+ p.drawRect( 0, 0, width(), height() );
+ p.drawText( 2, 16, name() ); */
+}
+
+void KaimanStyleElement::dragEnterEvent( QDragEnterEvent *event )
+{
+ event->accept( KURLDrag::canDecode(event) );
+}
+
+void KaimanStyleElement::dropEvent( QDropEvent *event )
+{
+ ((Kaiman*)(parentWidget()->parentWidget()))->doDropEvent(event);
+}
+
+/***************************************************************************/
+
+
+KaimanStyleButton::KaimanStyleButton(QWidget *parent, const char *name)
+ : KaimanStyleMasked(parent, name)
+{
+ i_b_lit = i_b_prelit = i_b_down = false;
+ i_i_currentState = NormalUp;
+
+ I_pmIndex.resize( StateListEND );
+
+ for (int i=0; i<StateListEND; i++) {
+ // Set pixmap index of all states to 0 (the default pixmap)
+ I_pmIndex.insert( i, new int(0));
+ }
+}
+
+KaimanStyleButton::~KaimanStyleButton()
+{
+}
+
+void KaimanStyleButton::mousePressEvent(QMouseEvent *qme)
+{
+ // We deactivate prelight, because the user presses the button.
+ // So it is now down, but there is no PrelitDown icon (BTW: would
+ // make no real sense anyhow).
+ setPrelight(false);
+ setDown(true);
+ grabMouse();
+
+ KaimanStyleMasked::mousePressEvent( qme );
+}
+
+void KaimanStyleButton::mouseReleaseEvent(QMouseEvent *qme)
+{
+ releaseMouse ();
+
+ if (down())
+ {
+ setDown(false);
+ emit clicked();
+ }
+
+ KaimanStyleMasked::mouseReleaseEvent( qme );
+}
+
+
+/* paint prelight */
+void KaimanStyleButton::enterEvent ( QEvent * e )
+{
+ if ( !down() )
+ setPrelight(true);
+
+ KaimanStyleMasked::enterEvent( e );
+}
+
+/* unpaint prelight */
+void KaimanStyleButton::leaveEvent ( QEvent * e )
+{
+ if (!down())
+ setPrelight(false);
+
+ KaimanStyleMasked::leaveEvent( e );
+}
+
+bool KaimanStyleButton::lit()
+{
+ return i_b_lit;
+}
+
+void KaimanStyleButton::setLit(bool val_b_lit)
+{
+ i_b_lit = val_b_lit;
+ updateButtonState();
+}
+
+bool KaimanStyleButton::prelit()
+{
+ return i_b_prelit;
+}
+
+void KaimanStyleButton::setPrelight(bool val_b_prelit)
+{
+ i_b_prelit = val_b_prelit;
+ updateButtonState();
+}
+
+bool KaimanStyleButton::down()
+{
+ return i_b_down;
+}
+
+void KaimanStyleButton::setDown(bool val_b_down)
+{
+ i_b_down = val_b_down;
+ updateButtonState();
+}
+
+void KaimanStyleButton::updateButtonState() {
+
+ if ( i_b_prelit ) {
+ if ( i_b_lit ) {
+ // Prelit and Lit
+ i_i_currentState = PrelightLitUp;
+ }
+ else {
+ // Prelit and not Lit
+ i_i_currentState = PrelightUp;
+ }
+ }
+
+ else if ( i_b_lit ) {
+ if ( i_b_down ) {
+ // Lit and Down
+ i_i_currentState = LitDown;
+ } else {
+ // Lit and not Down
+ i_i_currentState = LitUp;
+ }
+ }
+ else {
+ if ( i_b_down ) {
+ // Normal and Down
+ i_i_currentState = NormalDown;
+ }
+ else {
+ // Normal and not Down
+ i_i_currentState = NormalUp;
+ }
+ }
+
+ setPixmap( *I_pmIndex[i_i_currentState] );
+ repaint();
+}
+
+
+/***********************************************************************/
+
+KaimanStyleSlider::KaimanStyleSlider(int min, int max, QWidget *parent, const char *name)
+ : KaimanStyleMasked( parent, name )
+{
+ _min = min;
+ _max = max;
+ _down = false;
+ _lit = false;
+
+ setValue( _min );
+}
+
+
+KaimanStyleSlider::~KaimanStyleSlider()
+{
+}
+
+
+void KaimanStyleSlider::setValue( int value )
+{
+ if (value>_max) value=_max;
+ if (value<_min) value=_min;
+ _value = value;
+ repaint();
+}
+
+
+void KaimanStyleSlider::setValue( int value, int min, int max )
+{
+ if ( value!=_value || min!=_min || max!=_max ) {
+ _min = min;
+ _max = max;
+ setValue( value );
+ repaint();
+ }
+}
+
+int KaimanStyleSlider::pos2value( int x, int y )
+{
+ int p;
+ int v;
+ if ( options[optionVertical] ) {
+ p = y;
+ v = p*(_max-_min)/height();
+ } else {
+ p = x;
+ v = p*(_max-_min)/width();
+ }
+
+ if ( options[optionReversed] ) v = (_max-_min) - v;
+ return _min + v;
+}
+
+
+void KaimanStyleSlider::mouseMoveEvent(QMouseEvent *qme)
+{
+ KaimanStyleMasked::mouseMoveEvent( qme );
+
+ if ( _down )
+ {
+ setValue( pos2value(qme->x(), qme->y()) );
+ emit newValue( value() );
+ }
+}
+
+void KaimanStyleSlider::mousePressEvent(QMouseEvent *qme)
+{
+ if ( !_down )
+ {
+ grabMouse();
+ _down = true;
+
+ setValue( pos2value(qme->x(), qme->y()) );
+ emit newValueDrag( value() );
+ emit newValue( value() );
+ }
+
+ KaimanStyleMasked::mousePressEvent( qme );
+}
+
+
+void KaimanStyleSlider::mouseReleaseEvent(QMouseEvent *qme)
+{
+ if ( _down )
+ {
+ _down = false;
+ releaseMouse();
+ repaint();
+
+ setValue( pos2value(qme->x(), qme->y()) );
+ emit newValue( value() );
+ emit newValueDrop( value() );
+ }
+
+ KaimanStyleMasked::mouseReleaseEvent( qme );
+}
+
+
+void KaimanStyleSlider::paintEvent(QPaintEvent */*qpe*/)
+{
+ // draw background
+ bitBlt( this, 0, 0, pixmaps[0] );
+
+ // draw optional handle
+ QPixmap *handle;
+ if ( _down )
+ handle = pixmaps[2];
+ else
+ {
+ if ( _lit && optionPrelight )
+ handle = pixmaps[3];
+ else
+ handle = pixmaps[1];
+ }
+
+ if ( handle && handle->width() )
+ {
+ int x = 0;
+ int y = 0;
+
+ if ( _max-_min ) {
+ int v = _value-_min;
+ if ( options[optionReversed] ) v = (_max-_min) - v;
+
+ if ( options[optionVertical] )
+ y = ( height()-handle->height() ) * v / (_max-_min);
+ else
+ x = ( width()-handle->width() ) * v / (_max-_min);
+ }
+
+ bitBlt( this, x, y, handle );
+ }
+}
+
+void KaimanStyleSlider::enterEvent ( QEvent * e )
+{
+ if ( !_lit && optionPrelight )
+ {
+ _lit = true;
+ repaint();
+ }
+
+ KaimanStyleMasked::enterEvent( e );
+}
+
+void KaimanStyleSlider::leaveEvent ( QEvent * e )
+{
+ if ( _lit )
+ {
+ _lit = false;
+ repaint();
+ }
+
+ KaimanStyleMasked::leaveEvent( e );
+}
+
+
+/***********************************************************************/
+
+
+KaimanStyleBackground::KaimanStyleBackground(QWidget *parent, const char *name)
+ : KaimanStyleMasked( parent, name )
+{
+ i_b_move = false;
+}
+
+KaimanStyleBackground::~KaimanStyleBackground()
+{
+}
+
+void KaimanStyleBackground::mouseReleaseEvent(QMouseEvent *qme)
+{
+ i_b_move = false;
+ KaimanStyleMasked::mouseReleaseEvent( qme );
+}
+
+void KaimanStyleBackground::mouseMoveEvent(QMouseEvent *qme)
+{
+ QPoint diff = qme->globalPos() - i_point_lastPos;
+ if ( abs(diff.x()) > 10 || abs(diff.y()) > 10) {
+ // Moving starts only, when passing a drag border
+ i_b_move = true;
+ }
+
+ if ( i_b_move ) {
+ QWidget *p = parentWidget()->parentWidget();
+ if ( !p ) p = parentWidget();
+
+ p->move( qme->globalPos() - i_point_dragStart );
+ }
+
+ KaimanStyleMasked::mouseMoveEvent( qme );
+}
+
+void KaimanStyleBackground::mousePressEvent(QMouseEvent *qme)
+{
+ // On the background we move the shaped toplevel around
+ if (!i_b_move) {
+ i_point_dragStart = qme->pos();
+ i_point_lastPos = qme->globalPos();
+ }
+
+ KaimanStyleMasked::mousePressEvent( qme );
+}
+
+/***********************************************************************/
+
+KaimanStyleValue::KaimanStyleValue(int min, int max, QWidget *parent, const char *name)
+ : KaimanStyleMasked( parent, name )
+{
+ _min = min;
+ _max = max;
+ _value = _min;
+
+ setPixmap( 0 );
+}
+
+KaimanStyleValue::~KaimanStyleValue()
+{
+}
+
+void KaimanStyleValue::setValue( int value )
+{
+ if (value>_max) value=_max;
+ if (value<_min) value=_min;
+ _value = value;
+
+ int len = _max-_min;
+ if ( len )
+ setPixmap( (_value-_min)*pixmapNum/len );
+ else
+ setPixmap( 0 );
+}
+
+void KaimanStyleValue::setValue( int value, int min, int max )
+{
+ _min = min;
+ _max = max;
+
+ setValue( value );
+}
+
+
+/***********************************************************************/
+
+KaimanStyleNumber::KaimanStyleNumber(QWidget *parent, const char *name)
+ : KaimanStyleElement( parent, name )
+{
+ //kdDebug(66666) << k_funcinfo << "name = '" << name << "'" << endl;
+ _value = 0;
+ if (QCString(name) == "In_Rate_Number")
+ digits = 3;
+ else
+ digits = 2;
+}
+
+KaimanStyleNumber::~KaimanStyleNumber()
+{
+}
+
+
+void KaimanStyleNumber::loadPixmaps(QString &val_s_filename)
+{
+ KaimanStyleElement::loadPixmaps( val_s_filename );
+ resize( digits*pixmaps[0]->width(),pixmaps[0]->height() );
+}
+
+
+void KaimanStyleNumber::setValue( int value )
+{
+ if ( _value!=value )
+ {
+ _value = value;
+ repaint();
+ }
+}
+
+void KaimanStyleNumber::paintEvent(QPaintEvent */*qpe*/)
+{
+ // check for overflow
+ int v = _value;
+ for ( int i=digits; i>0 && v>0; i-- )
+ v /= 10;
+
+ if ( v!=0 )
+ v = 999999999; // overflow
+ else
+ v = _value;
+
+ // draw number
+ int x = width();
+ do {
+ x -= pixmaps[0]->width();
+ bitBlt(this, x, 0, pixmaps[v%10] );
+
+ v /= 10;
+ } while ( v>0 );
+
+ // draw right free space
+ while ( x>0 )
+ {
+ x -= pixmaps[0]->width();
+ bitBlt(this, x, 0, pixmaps[0] );
+ }
+/*
+ QPainter p( this );
+ p.setBrush( NoBrush );
+ p.setPen( QColor(255,255,255) );
+ p.drawRect( 0, 0, width(), height() );
+ p.drawText( 2, 16, name() );*/
+}
+
+/***********************************************************************/
+
+KaimanStyleText::KaimanStyleText(QWidget *parent, const char *name)
+ : KaimanStyleElement( parent, name )
+{
+ _pos = 0;
+ _timer = new QTimer( this );
+ _delay = 500;
+ connect( _timer, SIGNAL(timeout()), this, SLOT(timeout()) );
+}
+
+KaimanStyleText::~KaimanStyleText()
+{
+}
+
+
+void KaimanStyleText::loadPixmaps(QString &val_s_filename)
+{
+ KaimanStyleElement::loadPixmaps( val_s_filename );
+ resize( digits*pixmaps[0]->width(), pixmaps[0]->height() );
+}
+
+
+void KaimanStyleText::setValue( QString value )
+{
+ if ( value!=_value ) {
+ _pos = 0;
+ _direction = 1;
+ _value = value;
+ repaint();
+ }
+}
+
+
+void KaimanStyleText::startAnimation( int delay )
+{
+ _pos = 0;
+ _direction = 1;
+ _delay = delay;
+ _timer->start( _delay, TRUE );
+}
+
+
+void KaimanStyleText::stopAnimation()
+{
+ _pos = 0;
+ _timer->stop();
+}
+
+
+void KaimanStyleText::timeout()
+{
+ // reflect
+ if ( _pos+_direction<0 || (int)_value.length()-(_pos+_direction)<digits ) {
+ _direction = -_direction;
+ _timer->start( _delay*5, TRUE );
+ } else {
+ // check new position
+ if ( _pos+_direction>=0 && (int)_value.length()-(_pos+_direction)>=digits ) {
+ _pos += _direction;
+ repaint();
+ }
+
+ _timer->start( _delay, TRUE );
+ }
+
+
+}
+
+
+void KaimanStyleText::paintEvent(QPaintEvent */*qpe*/)
+{
+ // draw number
+ int p;
+ for (p=0; p<digits && p<(int)_value.length()-_pos; p++ )
+ {
+ int pmNum = _value[p+_pos].latin1() - ' ' ;
+ if ( pmNum>=96 ) pmNum = '?' - ' ';
+ if ( pmNum<0 ) pmNum = '?' - ' ';
+
+ QPixmap *pm = pixmaps[pmNum];
+ if ( pm ) bitBlt(this, p*pixmaps[0]->width(), 0, pm );
+ }
+
+ QPixmap *pm = pixmaps[0];
+ for ( ; p<digits; p++ )
+ bitBlt(this, p*pixmaps[0]->width(), 0, pm );
+
+/* QPainter pnt( this );
+ pnt.setBrush( NoBrush );
+ pnt.setPen( QColor(255,255,255) );
+ pnt.drawRect( 0, 0, width(), height() );
+ pnt.drawText( 2, 16, name() );*/
+}
+
+/***********************************************************************/
+
+KaimanStyleAnimation::KaimanStyleAnimation(int delay, QWidget *parent, const char *name)
+ : KaimanStyleMasked( parent, name )
+{
+ _delay = delay;
+ _frame = 0;
+ _timer = new QTimer( this );
+ connect( _timer, SIGNAL(timeout()), this, SLOT(timeout()) );
+}
+
+KaimanStyleAnimation::~KaimanStyleAnimation()
+{
+}
+
+void KaimanStyleAnimation::start()
+{
+ _timer->start( _delay, FALSE );
+}
+
+void KaimanStyleAnimation::pause()
+{
+ _timer->stop();
+}
+
+void KaimanStyleAnimation::stop()
+{
+ _timer->stop();
+ _frame = 0;
+ setPixmap( _frame );
+}
+
+void KaimanStyleAnimation::timeout()
+{
+ _frame++;
+ if ( _frame>=pixmapNum ) _frame = 1;
+ setPixmap( _frame );
+}
+
+/***********************************************************************/
+
+KaimanStyleState::KaimanStyleState(QWidget *parent, const char *name)
+ : KaimanStyleMasked( parent, name )
+{
+ _value = 0;
+}
+
+KaimanStyleState::~KaimanStyleState()
+{
+}
+
+void KaimanStyleState::setValue( int value )
+{
+ _value = value;
+ setPixmap( _value );
+}
+
+void KaimanStyleState::mousePressEvent(QMouseEvent *qme)
+{
+ emit clicked();
+ KaimanStyleMasked::mouseReleaseEvent( qme );
+}
+
+/***********************************************************************/
+
+
+KaimanStyle::KaimanStyle( QWidget *parent, const char *name )
+ : QWidget( parent, name )
+{
+ i_qw_parent = parent;
+ i_eventSemaphore = false;
+}
+
+
+KaimanStyle::~KaimanStyle()
+{
+}
+
+
+int KaimanStyle::parseStyleFile(QString &l_s_tmpName)
+{
+ int l_i_ret = false;
+
+ QStringList l_s_tokens;
+
+ QFile l_fd(l_s_tmpName);
+ if ( l_fd.open(IO_ReadOnly) ) {
+ // file opened successfully
+ QTextStream l_ts_line( &l_fd );
+ QString l_s_textLine, l_s_token;
+ while ( !l_ts_line.eof() ) {
+ // Clear list of tokens (we are going to fill them now)
+ l_s_tokens.clear();
+
+ // Read a line
+ l_s_textLine = l_ts_line.readLine();
+ l_s_textLine = l_s_textLine.simplifyWhiteSpace();
+
+ if ( l_s_textLine.left(1) != "#" ) {
+ // OK, this is not a comment line
+ if ( l_s_textLine.isNull())
+ l_s_textLine = "";
+ while ( !l_s_textLine.isEmpty() ) {
+ l_s_token = getToken(l_s_textLine, ' ');
+ if ( ! l_s_token.isEmpty() ) {
+ // OK. There is a useful token. It is not NULL
+ if ( l_s_token.right(1) == ":" )
+ l_s_tokens.append(l_s_token.left(l_s_token.length() -1 ));
+ else
+ l_s_tokens.append(l_s_token);
+ } // -<- if it is a not-empty token
+ } // -<- while there are tokens available
+
+ interpretTokens(l_s_tokens);
+
+ } // -<- if is not comment line
+ } // -<- While not EOF on file
+
+ l_i_ret = 0;
+ } // -<- if file could be opened
+
+ else {
+ l_i_ret = KaimanStyle::FileNotFound;
+ }
+ return l_i_ret;
+}
+
+
+
+/*
+ This function gets a list of tokens and inserts a new
+ KaimanStyleElement in I_styleElem. */
+void KaimanStyle::interpretTokens(QStringList& ref_s_tokens)
+{
+ if ( ref_s_tokens.count() < 1 ) {
+ // A list with less than 1 item is useless to us
+ return;
+ }
+
+ QString l_s_tokenTypes;
+ const QString &l_s_elem = ref_s_tokens.first();
+ bool l_vertPixmaps = false;
+ int l_i_pmIndex[KaimanStyleButton::StateListEND];
+ for (int i=0; i<KaimanStyleButton::StateListEND; i++) l_i_pmIndex[i]=0;
+
+ enum { UnknownElement, BackgroundElement, MaskElement, ButtonElement, SliderElement,
+ ValueElement, AnimationElement, StateElement, DigitElement, NumberElement,
+ TextElement } l_elementType = UnknownElement;
+
+ // Now determine the meaning of the following tokens
+ // l_s_tokenTypes stores the meaning (e.g. x-Position, filename, ...)
+ if ( l_s_elem == "Background" ) {
+ l_s_tokenTypes = "f";
+ l_elementType = BackgroundElement;
+ }
+ // ---
+ else if ( l_s_elem == "Mask" ) {
+ l_s_tokenTypes = "f";
+ l_elementType = MaskElement;
+ } else if ( l_s_elem=="Digit_Small" || l_s_elem=="Digit_Small_Default" ) {
+ i_smallFont = ref_s_tokens[1];
+ return;
+ } else if ( l_s_elem=="Digit_Large" || l_s_elem=="Digit_Large_Default" ) {
+ i_largeFont = ref_s_tokens[1];
+ return;
+ }
+ // ---
+ else if ( l_s_elem == "Title" ||
+ l_s_elem == "Album" ||
+ l_s_elem == "Artist" ||
+ l_s_elem == "Genre" ) {
+ // You can have an OPTIONAL argument, so lets see if it is there.
+ if (ref_s_tokens.count() == 6 )
+ l_s_tokenTypes = "fd1xy";
+ else
+ l_s_tokenTypes = "fdxy";
+ l_elementType = TextElement;
+ }
+ // ---
+ else if ( l_s_elem == "Play_Button" ||
+ l_s_elem == "Pause_Button" ||
+ l_s_elem == "Stop_Button" ||
+ l_s_elem == "Shuffle_Button" ||
+ l_s_elem == "Repeat_Button" ) {
+ l_s_tokenTypes = "fPSxy";
+ l_i_pmIndex[KaimanStyleButton::NormalUp] = 0;
+ l_i_pmIndex[KaimanStyleButton::NormalDown] = 1;
+ l_i_pmIndex[KaimanStyleButton::LitUp] = 2;
+ l_i_pmIndex[KaimanStyleButton::LitDown] = 3;
+ l_i_pmIndex[KaimanStyleButton::PrelightUp] = 4;
+ l_i_pmIndex[KaimanStyleButton::PrelightLitUp] = 5;
+ l_elementType = ButtonElement;
+ }
+ // ---
+ else if ( l_s_elem == "Next_Button" ||
+ l_s_elem == "Prev_Button" ||
+ l_s_elem == "FF" ||
+ l_s_elem == "RW" ||
+ l_s_elem == "Playlist_Button" ||
+ l_s_elem == "Config_Button" ||
+ l_s_elem == "Iconify_Button" ||
+ l_s_elem == "Mixer_Button" ||
+ l_s_elem == "Exit_Button" ||
+ l_s_elem == "Alt_Skin_Button" ||
+ l_s_elem == "Volume_Up_Button" ||
+ l_s_elem == "Volume_Down_Button" ||
+ l_s_elem == "Balance_Left_Button" ||
+ l_s_elem == "Balance_Right_Button" ) {
+ l_i_pmIndex[KaimanStyleButton::NormalUp] = 0;
+ l_i_pmIndex[KaimanStyleButton::NormalDown] = 1;
+ l_i_pmIndex[KaimanStyleButton::PrelightUp] = 2;
+ l_s_tokenTypes = "fPxy";
+ l_elementType = ButtonElement;
+ }
+ // ---
+ else if ( l_s_elem == "Stereo_Item" ||
+ l_s_elem == "Shuffle_Item" ||
+ l_s_elem == "Repeat_Item" ||
+ l_s_elem == "Mpegversion_Item" ||
+ l_s_elem == "Mpeglayer_Item" ||
+ l_s_elem == "Mpegmode_Item" ||
+ l_s_elem == "Status_Item" ) {
+ l_s_tokenTypes = "Vfxy";
+ l_elementType = StateElement;
+ }
+ // ---
+ else if ( l_s_elem == "Hour_Number" ||
+ l_s_elem == "Minute_Number" ||
+ l_s_elem == "Second_Number" ||
+ l_s_elem == "Song_Number" ||
+ l_s_elem == "Total_Number" ||
+ l_s_elem == "In_Rate_Number" ||
+ l_s_elem == "In_Hz_Number" ||
+ l_s_elem == "Out_Bits" ||
+ l_s_elem == "Out_Hz" ||
+ l_s_elem == "Song_Minute" ||
+ l_s_elem == "Song_Second" ||
+ l_s_elem == "Frame" ||
+ l_s_elem == "Frame_Total" ||
+ l_s_elem == "CPU_Number" ||
+ l_s_elem == "Hour_Total" ||
+ l_s_elem == "Minute_Total" ||
+ l_s_elem == "Second_Total_Number" ) {
+ // You can have an OPTIONAL argument, so lets see if it is there.
+ if (ref_s_tokens.count() == 6 )
+ l_s_tokenTypes = "fd1xy";
+ else if (ref_s_tokens.count() == 5 )
+ l_s_tokenTypes = "fdxy";
+ else
+ l_s_tokenTypes = "fxy";
+
+ l_elementType = NumberElement;
+ }
+ // ---
+ else if ( l_s_elem == "Position_Item" ||
+ l_s_elem == "Volume_Item" ||
+ l_s_elem == "Balance_Item" ) {
+ l_s_tokenTypes = "Vflxy";
+ l_elementType = ValueElement;
+ }
+ // ---
+ else if ( l_s_elem == "Load_Item" ) {
+ l_s_tokenTypes = "Vflxy";
+ l_elementType = AnimationElement;
+ }
+ // ---
+ else if ( l_s_elem == "Position_Slider" ||
+ l_s_elem == "Volume_Slider" ||
+ l_s_elem == "Balance_Slider" ) {
+
+ if ( ref_s_tokens.count()==10 )
+ l_s_tokenTypes = "VfP12wxyh";
+ else
+ l_s_tokenTypes = "VfP12sxy";
+ l_elementType = SliderElement;
+ }
+ else {
+ kdDebug() << l_s_elem << " not handled yet." << endl;
+ l_s_tokenTypes = "f";
+ }
+
+ /* The above lines decode the meanings of the tokens. The rules for
+ this are the SKIN-SPECS. So the decoder implements a syntactic
+ analyser (parser).
+
+ I now do know the type of each tokens, whether it represents a
+ filename, the prelight parameter, the x position or what else. This
+ information resides in l_s_tokenTypes, and will help in creating
+ the kaiman style elements.
+
+ I will now do two things:
+ 1) Create a new KaimanStyle*, that is inserted into I_styleElem.
+ 2) Fill the KaimanStyleElement structure, by interpreting the tokens.
+ */
+ QStringList::Iterator li_s_tokens = ref_s_tokens.begin();
+ ++ li_s_tokens; // Skip the name of the element
+
+ // 1) Create a new KaimanStyleElement, that is inserted into I_styleElem.
+ KaimanStyleElement *l_kse_elem = 0;
+ KaimanStyleButton *but = 0;
+
+ switch ( l_elementType )
+ {
+ case BackgroundElement:
+ l_kse_elem = new KaimanStyleBackground(this, l_s_elem.latin1());
+ l_kse_elem->show();
+ break;
+
+ case MaskElement:
+ l_kse_elem = new KaimanStyleElement(this, l_s_elem.latin1());
+ l_kse_elem->hide();
+ break;
+
+ case ButtonElement:
+ l_kse_elem = new KaimanStyleButton(this, l_s_elem.latin1());
+ but = static_cast<KaimanStyleButton*>(l_kse_elem);
+ l_kse_elem->show();
+ break;
+
+ case SliderElement:
+ l_kse_elem = new KaimanStyleSlider(0, 100, this, l_s_elem.latin1());
+ i_sliders.append(l_kse_elem);
+ l_kse_elem->show();
+ break;
+
+ case ValueElement:
+ l_kse_elem = new KaimanStyleValue(0, 100, this, l_s_elem.latin1());
+ break;
+
+ case AnimationElement:
+ l_kse_elem = new KaimanStyleAnimation(30, this, l_s_elem.latin1());
+ break;
+
+ case StateElement:
+ l_kse_elem = new KaimanStyleState(this, l_s_elem.latin1());
+ break;
+
+ case NumberElement:
+ l_kse_elem = new KaimanStyleNumber(this, l_s_elem.latin1());
+ break;
+
+ case TextElement:
+ l_kse_elem = new KaimanStyleText(this, l_s_elem.latin1());
+ break;
+
+ default:
+ break;
+ }
+
+ if ( !l_kse_elem )
+ {
+ kdDebug() << "Ignoring style element " << l_s_elem << endl;
+ return;
+ }
+
+ // insert element into element list
+ uint l_i_size = I_styleElem.size();
+ I_styleElem.resize(l_i_size + 1);
+ I_styleElem.insert(l_i_size, l_kse_elem);
+ l_kse_elem->installEventFilter( this );
+
+ // initialize element parameters
+ l_kse_elem->element = l_s_elem;
+
+ if ( l_s_tokenTypes.left(1) == "V" ) {
+ // Vertical flag
+ l_vertPixmaps = true;
+ l_s_tokenTypes = l_s_tokenTypes.mid(1);
+ }
+
+ // initialize button parameters
+ if ( but )
+ {
+ for (int i=0; i<KaimanStyleButton::StateListEND; i++)
+ but->I_pmIndex.insert(i, new int(l_i_pmIndex[i]) );
+ }
+
+ // 2) Fill the KaimanStyleElement structure, by interpreting the tokens.
+ while ( l_s_tokenTypes.length() != 0 ) {
+ /* The skindata format allows omitting arguments if the parser
+ can reconstruct without problems what you mean. This is taken
+ into account when writing the l_s_tokenTypes sting.
+
+ Unfortunately, several skins do ship with a broken skindata file.
+ Most common problem is that width and height is also given.
+
+ Even worse examples leave out specified parametes and add others.
+ For instance, the pause line is specified as "fPSxy". But in k9
+
+
+ the line looks like
+
+ Pause_Button: pause.jpg FALSE 91 148 116 173
+
+ So the actual parameters are fPxywh. The parser has to be pretty
+ smart now. It should "see" that S (status light) is not present,
+ since that should be either TRUE or FALSE, and distribute the
+ others accordingly. */
+
+ bool skipOne;
+
+ do {
+ skipOne = false;
+
+ // Take the first item from the l_s_tokenTypes;
+ char l_c_type = (l_s_tokenTypes[0]).latin1();
+ l_s_tokenTypes = l_s_tokenTypes.mid(1);
+ QString l_s_token = *li_s_tokens;
+ switch(l_c_type) {
+ case 'f':
+ // filename
+ l_kse_elem->filename = l_s_token;
+ break;
+ case 'P':
+ // Prelight
+ if ( l_s_token.upper() == "TRUE" )
+ l_kse_elem->optionPrelight = true;
+ else
+ {
+ l_kse_elem->optionPrelight = false;
+
+ // was that token really there?
+ skipOne = ( l_s_token.upper() != "FALSE" );
+ }
+ break;
+ case 'S':
+ // Statuslight
+ if ( l_s_token.upper() == "TRUE" )
+ l_kse_elem->optionStatuslight = true;
+ else
+ {
+ l_kse_elem->optionStatuslight = false;
+
+ // was that token really there?
+ skipOne = ( l_s_token.upper() != "FALSE" );
+ }
+ break;
+ case '1':
+ // parameter 1
+ if ( l_s_token.upper() == "TRUE" )
+ l_kse_elem->options[0] = true;
+ else
+ {
+ l_kse_elem->options[0] = false;
+
+ // was that token really there?
+ skipOne = ( l_s_token.upper() != "FALSE" );
+ }
+ break;
+ case '2':
+ // parameter 2
+ if ( l_s_token.upper() == "TRUE" )
+ l_kse_elem->options[1] = true;
+ else
+ {
+ l_kse_elem->options[1] = false;
+
+ // was that token really there?
+ skipOne = ( l_s_token.upper() != "FALSE" );
+ }
+ break;
+ case '3':
+ // parameter 3
+ if ( l_s_token.upper() == "TRUE" )
+ l_kse_elem->options[2] = true;
+ else
+ {
+ l_kse_elem->options[2] = false;
+
+ // was that token really there?
+ skipOne = ( l_s_token.upper() != "FALSE" );
+ }
+ break;
+ case 'l':
+ // length
+ if ( l_vertPixmaps )
+ l_kse_elem->pixmapLines = l_s_token.toInt();
+ else
+ l_kse_elem->pixmapColumns = l_s_token.toInt();
+ break;
+ case 'x':
+ // x Position
+ l_kse_elem->upperLeft.setX(l_s_token.toInt());
+ break;
+ case 'y':
+ // y Position
+ l_kse_elem->upperLeft.setY(l_s_token.toInt());
+ break;
+ case 's':
+ // dimension
+ if ( l_kse_elem->options[KaimanStyleSlider::optionVertical] )
+ l_kse_elem->dimension.setHeight(l_s_token.toInt());
+ else
+ l_kse_elem->dimension.setWidth(l_s_token.toInt());
+ break;
+ case 'w':
+ // width
+ l_kse_elem->dimension.setWidth(l_s_token.toInt());
+ break;
+ case 'h':
+ // height
+ l_kse_elem->dimension.setHeight(l_s_token.toInt());
+ break;
+ case 'd':
+ // number of digits
+ l_kse_elem->digits = l_s_token.toInt();
+ break;
+
+ default:
+ kdDebug() << "Element type '" << l_c_type << "' unknown" << endl;
+ }
+
+ if(skipOne) {
+ kdDebug() << "Skipped one element '" << l_c_type << "'" << endl;
+ }
+ } while(skipOne && l_s_tokenTypes.length() != 0);
+
+ // Next token.
+ ++li_s_tokens;
+ if (li_s_tokens == ref_s_tokens.end() ) {
+ // End of token list
+ break;
+ }
+ }
+
+ /* Do some post-processing */
+
+ if( l_elementType==ButtonElement ) {
+ // <Normal button>
+ if(but->optionPrelight) {
+ // --- Has Prelight ---
+ if(but->optionStatuslight)
+ but->pixmapColumns = 6;
+ else {
+ but->pixmapColumns = 3;
+ but->I_pmIndex.insert( KaimanStyleButton::LitUp,
+ new int( *(but->I_pmIndex[KaimanStyleButton::NormalUp])));
+ but->I_pmIndex.insert( KaimanStyleButton::LitDown,
+ new int( *(but->I_pmIndex[KaimanStyleButton::NormalDown])));
+ }
+ } else {
+ // --- Has No Prelight ---
+ but->I_pmIndex.insert( KaimanStyleButton::PrelightUp,
+ new int( *(but->I_pmIndex[KaimanStyleButton::NormalUp])));
+ but->I_pmIndex.insert( KaimanStyleButton::PrelightLitUp,
+ new int( *(but->I_pmIndex[KaimanStyleButton::LitUp])));
+ if(l_kse_elem->optionStatuslight)
+ but->pixmapColumns = 4;
+ else {
+ but->pixmapColumns = 2;
+ but->I_pmIndex.insert( KaimanStyleButton::LitUp,
+ new int( *(but->I_pmIndex[KaimanStyleButton::NormalUp])));
+ but->I_pmIndex.insert( KaimanStyleButton::LitDown,
+ new int( *(but->I_pmIndex[KaimanStyleButton::NormalDown])));
+ but->I_pmIndex.insert( KaimanStyleButton::PrelightLitUp,
+ new int( *(but->I_pmIndex[KaimanStyleButton::NormalUp])));
+ }
+ }
+ } else if( l_elementType==NumberElement ) {
+ // number items
+ l_kse_elem->pixmapColumns = 11;
+ if ( l_kse_elem->filename=="Small" ) l_kse_elem->filename = i_smallFont;
+ else if ( l_kse_elem->filename=="Large" ) l_kse_elem->filename = i_largeFont;
+ } else if( l_elementType==SliderElement ) {
+ // slider items
+ if ( l_kse_elem->options[KaimanStyleSlider::optionVertical] )
+ l_kse_elem->pixmapLines = l_kse_elem->optionPrelight ? 4 : 3;
+ else
+ l_kse_elem->pixmapColumns = l_kse_elem->optionPrelight ? 4 : 3;
+ } else if( l_elementType==TextElement ) {
+ // text items
+ l_kse_elem->pixmapColumns = 32;
+ l_kse_elem->pixmapLines = l_kse_elem->options[KaimanStyleText::optionExtended] ? 6 : 3;
+ } else {
+ // <Not standard element>
+ if(l_s_elem == "Stereo_Item")
+ l_kse_elem->pixmapLines = 3;
+ else if(l_s_elem == "Shuffle_Item")
+ l_kse_elem->pixmapLines = 2;
+ else if(l_s_elem == "Repeat_Item")
+ l_kse_elem->pixmapLines = 2;
+ else if(l_s_elem == "Mpegversion_Item")
+ l_kse_elem->pixmapLines = 3;
+ else if(l_s_elem == "Mpegversion_Item")
+ l_kse_elem->pixmapLines = 4;
+ else if(l_s_elem == "Mpegmode_Item")
+ l_kse_elem->pixmapLines = 5;
+ else if(l_s_elem == "Status_Item")
+ l_kse_elem->pixmapLines = 3;
+ } // </Not normal button>
+}
+
+
+QString KaimanStyle::getToken(QString &val_s_string, char val_c_separator)
+{
+ int l_i_pos;
+ QString l_s_token;
+
+ // Find the first occurrence of the separator
+ l_i_pos = val_s_string.find(val_c_separator, 0, false);
+ if ( l_i_pos == -1 ) {
+ // No sparator! Then the whole string is the token
+ l_s_token = val_s_string;
+ val_s_string = "";
+ }
+ else {
+ // Separator found: Split the string at the separator position
+ l_s_token = val_s_string.left(l_i_pos);
+ val_s_string.remove(0,l_i_pos);
+ }
+ val_s_string = val_s_string.simplifyWhiteSpace();
+
+ // Return the first token
+ return l_s_token;
+}
+
+
+
+bool KaimanStyle::loadStyle(const QString &styleName, const QString &descFile)
+{
+ bool l_b_ret = true;
+ int l_i_ret = 0;
+ QString l_s_tmpName;
+
+ i_skinName = styleName;
+ this->i_s_styleName = styleName;
+ i_s_styleBase = QString("skins/kaiman/") + i_s_styleName + QString("/");
+
+ l_s_tmpName = locate("appdata", i_s_styleBase + descFile );
+ if ( l_s_tmpName.isNull() ) {
+ l_b_ret = false;
+ }
+
+ if ( l_b_ret) {
+ // Skin description found. Now parse StyleFile.
+ l_i_ret = parseStyleFile(l_s_tmpName);
+ if (l_i_ret == 0) {
+ // If parsiing OK, load the referenced pixmaps
+ l_b_ret = loadPixmaps();
+
+ kdDebug(66666) << "Found " << I_styleElem.count() << " elements." << endl;
+ }
+ else {
+ if ( l_i_ret == KaimanStyle::FileNotFound ) {
+ // File not found
+ KMessageBox::error( 0, i18n("Cannot load style. Style not installed.") );
+ }
+ else {
+ // Parsing not OK: Notify user
+ KMessageBox::error( 0, i18n("Cannot load style. Unsupported or faulty style description.") );
+ }
+ }
+ }
+
+ return l_b_ret;
+}
+
+bool KaimanStyle::eventFilter( QObject *o, QEvent *e )
+{
+ /* HACK! HACK! HACK! */
+ if ( !i_eventSemaphore )
+ if ( e->type()==QEvent::MouseMove || e->type()==QEvent::MouseButtonPress ||
+ e->type()==QEvent::MouseButtonRelease )
+ {
+ QMouseEvent *m = (QMouseEvent*)e;
+
+ // handle noatun context menu
+ if (m->button()==RightButton) {
+ NoatunStdAction::ContextMenu::showContextMenu();
+ return true;
+ }
+
+ QPoint mousePos( m->x()+static_cast<QWidget *>(o)->x(),
+ m->y()+static_cast<QWidget *>(o)->y() );
+ QWidget *slider = 0;
+
+ /* find slider that is under the mouse position */
+ for ( QWidget *s = i_sliders.first(); s!=0; s=i_sliders.next() )
+ {
+ QRect sliderRect( s->pos(), s->size() );
+ if ( sliderRect.contains(mousePos) ) slider = s;
+ }
+
+ /* the slider the mouse events instead of the visible widget */
+ if ( slider )
+ {
+ QMouseEvent newMouseEvent( m->type(), mousePos-slider->pos(),
+ m->globalPos(), m->button(), m->state() );
+
+ i_eventSemaphore = true;
+ bool ret = QApplication::sendEvent( slider, &newMouseEvent );
+ i_eventSemaphore = false;
+ return ret;
+ }
+ }
+
+ return QWidget::eventFilter( o, e ); // standard event processing
+}
+
+bool KaimanStyle::loadPixmaps()
+{
+ QString l_s_tmpName;
+ QPixmap *l_pixmap_Background = 0, *l_pixmap_Mask = 0;
+ KaimanStyleElement *l_kse_elem;
+
+ for ( uint l_i_elem = 0; l_i_elem<I_styleElem.count(); l_i_elem++) {
+ l_kse_elem = I_styleElem[l_i_elem];
+
+ l_s_tmpName = locate("appdata", i_s_styleBase + l_kse_elem->filename );
+ l_kse_elem->loadPixmaps(l_s_tmpName);
+ }
+
+ l_kse_elem = this->find("Background");
+ if ( l_kse_elem != 0 ) {
+ l_pixmap_Background = l_kse_elem->pixmaps[0];
+ }
+ l_kse_elem = this->find("Mask");
+ if ( l_kse_elem != 0 ) {
+ l_pixmap_Mask = l_kse_elem->pixmaps[0];
+ }
+
+ if ( (l_pixmap_Background != 0) && (l_pixmap_Mask != 0) ) {
+
+ // OK, background and mask are defined. So now I can calculate the shape
+ int l_i_width_Mask = l_pixmap_Mask->width();
+ int l_i_height_Mask = l_pixmap_Mask->height();
+
+ QImage l_image_MaskOrig = l_pixmap_Mask->convertToImage();
+
+ QImage l_image_Mask(l_i_width_Mask,l_i_height_Mask, 1, 2, QImage::LittleEndian);
+ l_image_Mask.setColor( 0, 0x00ffffff );
+ l_image_Mask.setColor( 1, 0 );
+ l_image_Mask.fill( 0xff );
+
+ uchar *l_c_pixel;
+ uint l_qcol_white = qRgb(255,255,255);
+
+ for (int l_i_x=0; l_i_x<l_i_width_Mask; l_i_x++) {
+ for (int l_i_y=0; l_i_y < l_i_height_Mask; l_i_y++) {
+ if ( ((*((QRgb*) l_image_MaskOrig.scanLine(l_i_y)+l_i_x) & 0x00ffffff)) != (l_qcol_white & 0x00ffffff) ) {
+ l_c_pixel = (uchar *)l_image_Mask.scanLine(l_i_y);
+ *(l_c_pixel + (l_i_x>>3) ) &= ~(1 << (l_i_x & 7));
+ }
+ }
+ }
+
+ i_bitmap_Mask.convertFromImage(l_image_Mask);
+ // l_pixmap_Background.setMask(i_bitmap_Mask);
+ }
+
+ return true;
+}
+
+
+QBitmap* KaimanStyle::Mask()
+{
+ return &i_bitmap_Mask;
+}
+
+KaimanStyleElement* KaimanStyle::find(const char *val_s_elemName)
+{
+ for (uint i=0; i< I_styleElem.count(); i++) {
+ if ( I_styleElem[i]->element == QString(val_s_elemName)) {
+ return I_styleElem[i];
+ }
+ }
+ return 0;
+}
+#include "style.moc"
diff --git a/noatun/modules/kaiman/style.h b/noatun/modules/kaiman/style.h
new file mode 100644
index 00000000..0c1a6d81
--- /dev/null
+++ b/noatun/modules/kaiman/style.h
@@ -0,0 +1,356 @@
+// -*- C++ -*-
+/*
+ Copyright (c) 2000 Stefan Schimanski (1Stein@gmx.de)
+ 1999-2000 Christian Esken (esken@kde.org)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KaimanStyle_H
+#define KaimanStyle_H
+
+#include <qstring.h>
+#include <qwidget.h>
+#include <qpixmap.h>
+#include <qbitmap.h>
+#include <qimage.h>
+#include <qevent.h>
+#include <qptrvector.h>
+
+class KaimanStyleElement : public QWidget
+{
+ Q_OBJECT
+public:
+ KaimanStyleElement(QWidget *parent, const char *name=0);
+ ~KaimanStyleElement();
+
+ virtual void loadPixmaps(QString &val_s_filename);
+
+ QString element;
+ QString filename;
+ QPoint upperLeft;
+ QSize dimension;
+
+ bool options[3];
+ int digits;
+
+ bool optionPrelight;
+ bool optionStatuslight;
+
+ int pixmapLines;
+ int pixmapColumns;
+
+ QPtrVector<QPixmap> pixmaps;
+
+public slots:
+ void setPixmap( int num );
+
+protected:
+ void paintEvent(QPaintEvent *qpe);
+ void dropEvent( QDropEvent *event );
+ void dragEnterEvent( QDragEnterEvent *event );
+
+ int pixmapNum;
+
+private:
+ int _currentPixmap;
+};
+
+
+class KaimanStyleMasked : public KaimanStyleElement
+{
+ Q_OBJECT
+public:
+ KaimanStyleMasked(QWidget *parent, const char *name=0)
+ : KaimanStyleElement( parent, name ) {};
+
+ virtual void loadPixmaps(QString &val_s_filename)
+ {
+ KaimanStyleElement::loadPixmaps( val_s_filename );
+ if(pixmaps[0]->mask())
+ setMask(*pixmaps[0]->mask());
+ };
+};
+
+
+class KaimanStyleButton : public KaimanStyleMasked
+{
+ Q_OBJECT
+public:
+ KaimanStyleButton(QWidget *parent, const char *name=0);
+ ~KaimanStyleButton();
+
+ // Button states.
+ enum { NormalUp=0, NormalDown, LitUp, LitDown, PrelightUp, PrelightLitUp, StateListEND };
+
+ QPtrVector<int> I_pmIndex;
+
+ void setLit(bool);
+ void setPrelight(bool);
+ void setDown(bool);
+ bool lit();
+ bool prelit();
+ bool down();
+ void updateButtonState();
+
+signals:
+ void clicked();
+
+protected:
+ void mousePressEvent(QMouseEvent *qme);
+ void mouseReleaseEvent(QMouseEvent *qme);
+ void enterEvent(QEvent * );
+ void leaveEvent ( QEvent * );
+
+private:
+ int i_i_currentState;
+ bool i_b_lit;
+ bool i_b_prelit;
+ bool i_b_down;
+};
+
+
+class KaimanStyleSlider : public KaimanStyleMasked
+{
+ Q_OBJECT
+public:
+ KaimanStyleSlider(int min, int max, QWidget *parent, const char *name=0);
+ ~KaimanStyleSlider();
+
+ int value() { return _value; };
+
+ static const bool optionVertical;
+ static const bool optionReversed;
+
+public slots:
+ void setValue( int value );
+ void setValue( int value, int min, int max );
+
+signals:
+ void newValue( int value );
+ void newValueDrag( int value );
+ void newValueDrop( int value );
+
+protected:
+ void mouseMoveEvent(QMouseEvent *qme);
+ void mousePressEvent(QMouseEvent *qme);
+ void mouseReleaseEvent(QMouseEvent *qme);
+ void paintEvent(QPaintEvent *qpe);
+ void enterEvent(QEvent * );
+ void leaveEvent ( QEvent * );
+
+ int pos2value( int x, int y );
+
+ bool _down;
+ bool _lit;
+ int _value;
+ int _min, _max;
+};
+
+
+class KaimanStyleBackground : public KaimanStyleMasked
+{
+ Q_OBJECT
+public:
+ KaimanStyleBackground(QWidget *parent, const char *name=0);
+ ~KaimanStyleBackground();
+
+protected:
+ void mousePressEvent(QMouseEvent *qme);
+ void mouseReleaseEvent(QMouseEvent *qme);
+ void mouseMoveEvent(QMouseEvent *qme);
+
+private:
+ bool i_b_move;
+ QPoint i_point_dragStart;
+ QPoint i_point_lastPos;
+};
+
+
+class KaimanStyleValue : public KaimanStyleMasked
+{
+ Q_OBJECT
+public:
+ KaimanStyleValue(int min, int max, QWidget *parent, const char *name=0);
+ ~KaimanStyleValue();
+
+ int value() { return _value; };
+
+public slots:
+ void setValue( int value );
+ void setValue( int value, int min, int max );
+
+private:
+ int _min, _max, _value;
+};
+
+
+class KaimanStyleState : public KaimanStyleMasked
+{
+ Q_OBJECT
+public:
+ KaimanStyleState(QWidget *parent, const char *name=0);
+ ~KaimanStyleState();
+
+ int value() { return _value; };
+
+public slots:
+ void setValue( int value );
+
+signals:
+ void clicked();
+
+protected:
+ void mousePressEvent(QMouseEvent *qme);
+
+private:
+ int _value;
+};
+
+
+class KaimanStyleNumber : public KaimanStyleElement
+{
+ Q_OBJECT
+public:
+ KaimanStyleNumber(QWidget *parent, const char *name=0);
+ ~KaimanStyleNumber();
+
+ virtual void loadPixmaps(QString &val_s_filename);
+
+ static const bool optionCentered = 1;
+
+ int value() { return _value; };
+
+public slots:
+ void setValue( int value );
+
+protected:
+ void paintEvent(QPaintEvent *qpe);
+
+private:
+ int _value;
+};
+
+
+class KaimanStyleText : public KaimanStyleElement
+{
+ Q_OBJECT
+public:
+ KaimanStyleText(QWidget *parent, const char *name=0);
+ ~KaimanStyleText();
+
+ virtual void loadPixmaps(QString &val_s_filename);
+
+ static const bool optionExtended;
+
+ QString value() { return _value; };
+
+ void startAnimation( int delay );
+ void stopAnimation();
+
+public slots:
+ void setValue( QString value );
+
+protected:
+ void paintEvent(QPaintEvent *qpe);
+
+protected slots:
+ void timeout();
+
+private:
+ QString _value;
+ int _pos;
+ int _direction;
+ int _delay;
+ QTimer *_timer;
+};
+
+
+class KaimanStyleAnimation : public KaimanStyleMasked
+{
+ Q_OBJECT
+public:
+ KaimanStyleAnimation(int delay, QWidget *parent, const char *name=0);
+ ~KaimanStyleAnimation();
+
+public slots:
+ void start();
+ void pause();
+ void stop();
+
+protected:
+ void timeout();
+
+private:
+ int _delay,_frame;
+ QTimer *_timer;
+};
+
+class KaimanStyle : public QWidget
+{
+ Q_OBJECT
+public:
+ KaimanStyle(QWidget *parent, const char *name=0);
+ ~KaimanStyle();
+
+ enum { background, mask, play_Button, stop_Button, pause_Button, prev_Button, next_Button, repeat_Button, shuffle_Button, playlist_Button, mixer_Button, exit_Button, Iconify_Button, Config_Button, Alt_Skin_Button, Minute_Number, Second_Number, in_Rate_Number, in_Hz_Number, song_Number, status_Item, cPU_Number, digit_Large, digit_Small_Default, title, volume_Item, volume_Slider, position_Item, position_Slider };
+
+ enum { ParsingError=1, FileNotFound };
+
+ /// Finds a style element, according to it's name. Returns 0 when element is not available.
+ KaimanStyleElement* find(const char* val_s_elemName);
+
+ /// Tries to load the given style and returns success (true) or failure (false)
+ bool loadStyle(const QString &styleName, const QString &descFile="skindata" );
+ QString skinName() { return i_skinName; };
+
+ /// Returns the mask
+ QBitmap* Mask();
+
+ virtual bool eventFilter( QObject *o, QEvent *e );
+
+private:
+ // Parses the "skindata" file and returns success (true) or failure (false)
+ int parseStyleFile(QString &l_s_tmpName);
+ QString getToken(QString &val_s_string, char val_c_separator);
+ void interpretTokens(QStringList& ref_s_tokens);
+ bool loadPixmaps();
+
+ /// The name of the style, e.g. "k9"
+ QString i_s_styleName;
+ /// The base directory, where the style is found. For example
+ /// "/opt/kde/share/apps/kaiman/Skins/k9/" or "/opt/kde/share/apps/kaiman/Skins/k9.tgz"
+ QString i_s_styleBase;
+
+
+ // The mask of the complete style. Used for doing shaped windows
+ QBitmap i_bitmap_Mask;
+
+ /// All style elements are stored here.
+ QPtrVector<KaimanStyleElement> I_styleElem;
+
+ // The parent window. In other words: The container that holds all the KaimanStyleElement's
+ QWidget* i_qw_parent;
+
+ QPtrList<QWidget> i_sliders;
+ bool i_eventSemaphore;
+
+ QString i_smallFont;
+ QString i_largeFont;
+ QString i_skinName;
+};
+
+
+#endif
diff --git a/noatun/modules/kaiman/userinterface.cpp b/noatun/modules/kaiman/userinterface.cpp
new file mode 100644
index 00000000..31db0e09
--- /dev/null
+++ b/noatun/modules/kaiman/userinterface.cpp
@@ -0,0 +1,562 @@
+/*
+ Copyright (c) 2000 Stefan Schimanski <1Stein@gmx.de>
+
+ 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, 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
+ aint with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <noatun/effects.h>
+#include <noatun/app.h>
+#include <noatun/player.h>
+#include <noatun/pref.h>
+#include "userinterface.h"
+#include "pref.h"
+
+#include <qcursor.h>
+#include <qpixmap.h>
+#include <qbitmap.h>
+#include <qimage.h>
+#include <qdropsite.h>
+#include <qdragobject.h>
+#include <qtimer.h>
+
+#include <kfiledialog.h>
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kwin.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kconfig.h>
+#include <kpopupmenu.h>
+#include <kstandarddirs.h>
+#include <kmessagebox.h>
+#include <kio/netaccess.h>
+#include <kurldrag.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+#include <X11/X.h>
+#include <X11/Xos.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/shape.h>
+
+Kaiman* Kaiman::kaiman=0;
+const char Kaiman::DEFAULT_SKIN[]="car-preset";
+
+Kaiman::Kaiman()
+ : KMainWindow(0, "NoatunKaiman"), UserInterface()
+{
+ NOATUNPLUGINC(Kaiman);
+ kaiman=this;
+
+ //setCaption( i18n("Kaiman") );
+ KWin::setType( this->winId(), NET::Override );
+ setBackgroundMode( NoBackground );
+ setAcceptDrops(true);
+
+ _style = 0;
+ _seeking = false;
+ _altSkin = false;
+
+ // init config
+ KConfig *config=KGlobal::config();
+ config->setGroup("Kaiman");
+
+ // load skin
+ QString skinName = config->readEntry( "SkinResource", DEFAULT_SKIN );
+
+ if ( !changeStyle(skinName, "skindata") )
+ {
+ KMessageBox::sorry( this, i18n("Cannot load skin %1. Switching to default skin.").arg(skinName) );
+ if ( !changeStyle( DEFAULT_SKIN, "skindata" ) )
+ {
+ KMessageBox::error( this, i18n("Cannot load default skin %1.").arg(DEFAULT_SKIN) );
+ QTimer::singleShot( 0, this, SLOT(close()) );
+ return;
+ }
+ }
+
+ // global connects
+ connect( napp, SIGNAL(hideYourself()), this, SLOT(hide()) );
+ connect( napp, SIGNAL(showYourself()), this, SLOT(show()) );
+
+ connect( napp->player(), SIGNAL(playing()), this, SLOT(updateMode()));
+ connect( napp->player(), SIGNAL(stopped()), this, SLOT(updateMode()));
+ connect( napp->player(), SIGNAL(paused()), this, SLOT(updateMode()));
+ connect( napp->player(), SIGNAL(timeout()), this, SLOT(timeout()));
+ connect( napp->player(), SIGNAL(loopTypeChange(int)), this, SLOT(loopTypeChange(int)));
+ connect( napp->player(), SIGNAL(newSongLen(int,int)), this, SLOT(newSongLen(int,int)));
+ connect( napp->player(), SIGNAL(newSong()), this, SLOT(newSong()));
+
+ if( napp->player()->isPlaying() )
+ newSong();
+
+ new KaimanPrefDlg(this);
+
+ show();
+}
+
+
+Kaiman::~Kaiman()
+{
+}
+
+
+bool Kaiman::changeStyle( const QString &style, const QString &desc )
+{
+ QString ldesc = desc;
+ if ( ldesc.isEmpty() )
+ ldesc = _altSkin ? "alt_skindata" : "skindata";
+
+ bool vis = isVisible();
+ if ( vis )
+ hide();
+
+ bool ret = loadStyle( style, ldesc );
+
+ newSongLen(0,0);
+ timeout();
+ loopTypeChange(0);
+ updateMode();
+
+ if ( vis )
+ show();
+ return ret;
+}
+
+
+bool Kaiman::loadStyle( const QString &style, const QString &desc )
+{
+ if ( _style ) delete _style;
+ _style = new KaimanStyle(this);
+ if ( !_style->loadStyle( style, desc ) )
+ {
+ delete _style;
+ _style = 0;
+ return false;
+ }
+
+ if ( _style->Mask() != 0 )
+ {
+ // Set the shaped window form
+ XShapeCombineMask( qt_xdisplay(), winId(), ShapeBounding, 0,0,
+ _style->Mask()->handle(), ShapeSet );
+ }
+
+ KaimanStyleElement* item = _style->find("Background");
+ setBackgroundMode(QWidget::NoBackground);
+ if ( item!=0 )
+ {
+ _style->resize( item->width(), item->height());
+ resize( item->width(), item->height());
+ setFixedSize( item->width(), item->height());
+ }
+
+ item = _style->find("Playlist_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), napp->player(), SLOT(toggleListView()) );
+
+ item = _style->find("Play_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), napp->player(), SLOT(playpause()) );
+
+ item = _style->find("Pause_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), napp->player(), SLOT(playpause()) );
+
+ item = _style->find("Stop_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), napp->player(), SLOT(stop()) );
+
+ item = _style->find("Next_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), napp->player(), SLOT(forward()) );
+
+ item = _style->find("Prev_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), napp->player(), SLOT(back()) );
+
+ item = _style->find("Exit_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), this, SLOT(close()) );
+
+ item = _style->find("Mixer_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), this, SLOT(execMixer()) );
+
+ item = _style->find("Iconify_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), this, SLOT(showMinimized()) );
+
+ item = _style->find("Alt_Skin_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), this, SLOT(toggleSkin()) );
+
+ item = _style->find("Repeat_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), this, SLOT(toggleLoop()) );
+
+ item = _style->find("Shuffle_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), this, SLOT(toggleShuffle()) );
+
+ item = _style->find("Config_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), napp, SLOT(preferences()) );
+
+ item = _style->find("Volume_Up_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), this,SLOT(volumeUp()));
+
+ item = _style->find("Volume_Down_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), this,SLOT(volumeDown()));
+
+ KaimanStyleSlider* slider =
+ static_cast<KaimanStyleSlider*>(_style->find("Position_Slider"));
+ if( slider!=0 )
+ {
+ connect( slider, SIGNAL(newValueDrag(int)), this, SLOT(seekStart(int)) );
+ connect( slider, SIGNAL(newValue(int)), this, SLOT(seekDrag(int)) );
+ connect( slider, SIGNAL(newValueDrop(int)), this, SLOT(seekStop(int)) );
+ slider->setValue( 0, 0, 1000 );
+ }
+
+ slider = static_cast<KaimanStyleSlider*>(_style->find("Volume_Slider"));
+ if ( slider!=0 )
+ {
+ connect(slider, SIGNAL(newValue(int)), this, SLOT(setVolume(int)));
+ slider->setValue( napp->player()->volume(), 0, 100 );
+ }
+
+ KaimanStyleValue* volItem = static_cast<KaimanStyleValue*>(_style->find("Volume_Item"));
+ if ( volItem )
+ volItem->setValue( napp->player()->volume(), 0, 100 );
+
+ KaimanStyleText* titleItem = static_cast<KaimanStyleText*>(_style->find("Title"));
+ if ( titleItem )
+ titleItem->startAnimation( 300 );
+
+ return true;
+}
+
+
+void Kaiman::closeEvent(QCloseEvent*)
+{
+ unload();
+}
+
+
+void Kaiman::dragEnterEvent( QDragEnterEvent *event )
+{
+ event->accept( KURLDrag::canDecode(event) );
+}
+
+
+void Kaiman::dropEvent( QDropEvent *event )
+{
+ doDropEvent(event);
+}
+
+
+void Kaiman::doDropEvent(QDropEvent *event)
+{
+ KURL::List uri;
+ if (KURLDrag::decode(event, uri))
+ {
+ for (KURL::List::Iterator i = uri.begin(); i != uri.end(); ++i)
+ napp->player()->openFile(*i, false);
+ }
+}
+
+
+void Kaiman::seekStart( int )
+{
+ _seeking = true;
+}
+
+
+void Kaiman::seekDrag( int value )
+{
+ int length = napp->player()->getLength()/1000;
+ if ( length < 0)
+ length = 0;
+
+ if ( !_style ) return;
+
+ KaimanStyleValue* posItem =
+ static_cast<KaimanStyleValue*>(_style->find("Position_Item"));
+ if ( posItem )
+ posItem->setValue( value, 0, length );
+
+ KaimanStyleSlider* posSlider =
+ static_cast<KaimanStyleSlider*>(_style->find("Position_Slider"));
+ if ( posSlider )
+ posSlider->setValue( value, 0, length );
+
+ // update time
+
+ KaimanStyleNumber* numItem =
+ static_cast<KaimanStyleNumber*>(_style->find("Minute_Number"));
+ if ( numItem )
+ numItem->setValue( value/60%60 );
+
+ numItem = static_cast<KaimanStyleNumber*>(_style->find("Second_Number"));
+ if ( numItem )
+ numItem->setValue( value % 60 );
+}
+
+
+void Kaiman::seekStop( int value )
+{
+ seek( value );
+ _seeking = false;
+}
+
+
+void Kaiman::seek( int value )
+{
+ napp->player()->skipTo( value*1000 ); // skipTo() takes milliseconds
+}
+
+
+void Kaiman::toggleSkin()
+{
+ _altSkin = !_altSkin;
+
+ QString skinName = _style->skinName();
+
+ QString oldDesc, newDesc;
+ if ( _altSkin )
+ {
+ oldDesc = QString::fromLatin1("skindata");
+ newDesc = QString::fromLatin1("alt_skindata");
+ }
+ else
+ {
+ newDesc = QString::fromLatin1("skindata");
+ oldDesc = QString::fromLatin1("alt_skindata");
+ }
+
+ if ( !changeStyle(skinName, newDesc) )
+ changeStyle(skinName, oldDesc);
+}
+
+
+void Kaiman::setVolume( int vol )
+{
+ if ( vol<0 ) vol=0;
+ if ( vol>=100 ) vol=100;
+
+ napp->player()->setVolume( vol );
+}
+
+
+void Kaiman::volumeUp()
+{
+ setVolume( napp->player()->volume()+10 );
+}
+
+
+void Kaiman::volumeDown()
+{
+ setVolume( napp->player()->volume()-10 );
+}
+
+
+void Kaiman::execMixer()
+{
+ kapp->startServiceByDesktopName ( QString::fromLatin1("kmix"), QString::null );
+}
+
+
+void Kaiman::timeout()
+{
+ if ( !_style ) return;
+
+ if (!napp->player()->current())
+ return;
+
+ // update volume
+ KaimanStyleSlider* l_elem_volslider = static_cast<KaimanStyleSlider*>(_style->find("Volume_Slider"));
+ KaimanStyleValue* l_elem_volitem = static_cast<KaimanStyleValue*>(_style->find("Volume_Item"));
+ if ( l_elem_volslider!=0 )
+ l_elem_volslider->setValue( napp->player()->volume(), 0, 100 );
+ if ( l_elem_volitem!=0 )
+ l_elem_volitem->setValue( napp->player()->volume(), 0, 100 );
+
+ // update position
+ if ( !_seeking )
+ {
+ int sec = napp->player()->getTime()/1000;
+ if ( sec < 0 )
+ sec = 0;
+
+ KaimanStyleValue* posItem =
+ static_cast<KaimanStyleValue*>(_style->find("Position_Item"));
+ if ( posItem ) posItem->setValue( sec, 0, napp->player()->getLength()/1000 );
+
+ KaimanStyleSlider* posSlider =
+ static_cast<KaimanStyleSlider*>(_style->find("Position_Slider"));
+ if ( posSlider ) posSlider->setValue( sec, 0, napp->player()->getLength()/1000 );
+
+ // update time
+ KaimanStyleNumber* numItem =
+ static_cast<KaimanStyleNumber*>(_style->find("Minute_Number"));
+ if ( numItem )
+ numItem->setValue( sec/60%60 );
+
+ numItem = static_cast<KaimanStyleNumber*>(_style->find("Second_Number"));
+ if ( numItem )
+ numItem->setValue( sec%60 );
+ }
+
+ const PlaylistItem &item = napp->playlist()->current();
+ KaimanStyleNumber* numItem = 0;
+
+ numItem = static_cast<KaimanStyleNumber*>(_style->find("In_Rate_Number"));
+ if ( numItem )
+ numItem->setValue(item.property("bitrate").toInt());
+
+ QString hzString = item.property("samplerate");
+ hzString.truncate(2);
+
+ numItem = static_cast<KaimanStyleNumber*>(_style->find("In_Hz_Number"));
+ if ( numItem )
+ numItem->setValue(hzString.toInt());
+}
+
+
+void Kaiman::updateMode()
+{
+ if ( !_style ) return;
+
+ KaimanStyleButton* pause =
+ static_cast<KaimanStyleButton*>(_style->find("Pause_Button"));
+ KaimanStyleButton* play =
+ static_cast<KaimanStyleButton*>(_style->find("Play_Button"));
+ KaimanStyleState* status =
+ static_cast<KaimanStyleState*>(_style->find("Status_Item"));
+
+ if (napp->player()->isStopped() )
+ {
+ if ( pause ) pause->setLit( false );
+ if ( play ) play->setLit( false );
+ if ( status ) status->setValue( 0 );
+ }
+ else if ( napp->player()->isPlaying() )
+ {
+ if ( pause ) pause->setLit( false );
+ if ( play ) play->setLit( true );
+ if ( status ) status->setValue( 2 );
+ }
+ else if ( napp->player()->isPaused() )
+ {
+ if ( pause ) pause->setLit( true );
+ if ( play ) play->setLit( false );
+ if ( status ) status->setValue( 1 );
+ }
+}
+
+
+void Kaiman::loopTypeChange( int )
+{
+ if ( !_style ) return;
+
+ KaimanStyleState* rep = static_cast<KaimanStyleState*>(_style->find("Repeat_Item"));
+ if ( rep )
+ rep->setValue( napp->player()->loopStyle() );
+}
+
+
+void Kaiman::newSongLen( int, int )
+{
+ if ( !_style )
+ return;
+
+ int len = napp->player()->getLength()/1000; // convert milliseconds -> seconds
+ if ( len < 0 ) // getLength returns -1 if there's no Playobject
+ len = 0;
+
+ // update time
+ KaimanStyleNumber* numItem =
+ static_cast<KaimanStyleNumber*>(_style->find("Minute_Total_Number"));
+ if ( numItem )
+ numItem->setValue( len/60%60 );
+
+ numItem = static_cast<KaimanStyleNumber*>(_style->find("Second_Total_Number"));
+ if ( numItem )
+ numItem->setValue( len%60 );
+
+ numItem = static_cast<KaimanStyleNumber*>(_style->find("Song_Minute_Number"));
+ if ( numItem )
+ numItem->setValue( len/60%60 );
+
+ numItem = static_cast<KaimanStyleNumber*>(_style->find("Second_Minute_Number"));
+ if ( numItem )
+ numItem->setValue( len%60 );
+
+ newSong();
+}
+
+
+void Kaiman::newSong()
+{
+ if ( !_style ) return;
+
+ KaimanStyleText* titleItem = static_cast<KaimanStyleText*>(_style->find("Title"));
+ if ( titleItem )
+ {
+ QString title = i18n("Noatun");
+ if ( napp->player()->current() )
+ {
+ title = napp->player()->current().title();
+ if ( title.isEmpty() )
+ title = napp->player()->current().file();
+
+ title = i18n("TITLE (LENGTH)", "%1 (%2)").arg(title,
+ napp->player()->current().lengthString());
+ }
+ titleItem->setValue( title );
+ }
+}
+
+
+#undef None
+void Kaiman::toggleLoop()
+{
+ KPopupMenu *loopMenu = new KPopupMenu(this, "loopMenu");
+ int selectedItem = 0;
+
+ loopMenu->setCheckable(true);
+ loopMenu->insertTitle(i18n("Loop Style"));
+ loopMenu->insertItem(i18n("&None"),
+ (int)Player::None);
+ loopMenu->insertItem(i18n("&Song"),
+ (int)Player::Song);
+ loopMenu->insertItem(i18n("&Playlist"),
+ (int)(Player::Playlist));
+ loopMenu->insertItem(i18n("&Random"),
+ (int)(Player::Random));
+
+ loopMenu->setItemChecked((int)napp->player()->loopStyle(), true); // select current loopstyle in menu
+ selectedItem = loopMenu->exec(QCursor::pos());
+ if (selectedItem != -1)
+ napp->player()->loop(selectedItem); // set new loopstyle
+
+ delete loopMenu;
+}
+
+
+void Kaiman::toggleShuffle()
+{
+ napp->player()->loop(Player::Random); // set loopstyle to be random
+}
+
+#include "userinterface.moc"
diff --git a/noatun/modules/kaiman/userinterface.h b/noatun/modules/kaiman/userinterface.h
new file mode 100644
index 00000000..2fb46184
--- /dev/null
+++ b/noatun/modules/kaiman/userinterface.h
@@ -0,0 +1,85 @@
+/*
+ Copyright (c) 2000 Stefan Schimanski <1Stein@gmx.de>
+
+ 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, 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
+ aint with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef USERINTERFACE_H
+#define USERINTERFACE_H
+
+#include <noatun/plugin.h>
+#include <noatun/app.h>
+#include <kmainwindow.h>
+#include "style.h"
+
+
+class Player;
+
+/**
+ * @short Main window class
+ * @author Stefan Schimanski <1Stein@gmx.de>
+ * @version 0.1
+ */
+class Kaiman : public KMainWindow, public UserInterface
+{
+Q_OBJECT
+ public:
+ Kaiman();
+ virtual ~Kaiman();
+
+ bool changeStyle( const QString &style, const QString &desc=QString::null );
+ public slots:
+ void dropEvent( QDropEvent * );
+ void doDropEvent( QDropEvent * );
+ void dragEnterEvent( QDragEnterEvent * );
+ void closeEvent(QCloseEvent*);
+
+ protected slots:
+ void seekStart( int );
+ void seekDrag( int );
+ void seekStop( int );
+ void seek( int );
+ void toggleSkin();
+
+ void setVolume( int vol );
+ void volumeUp();
+ void volumeDown();
+
+ void execMixer();
+
+
+ void timeout();
+ void loopTypeChange( int t );
+ void newSongLen( int mins, int sec );
+ void newSong();
+ void updateMode();
+ void toggleLoop();
+ void toggleShuffle();
+
+ public:
+ static const char DEFAULT_SKIN[];
+ static Kaiman *kaiman;
+
+ protected:
+ bool loadStyle( const QString &style, const QString &desc );
+
+
+ bool _seeking;
+ bool _altSkin;
+ KaimanStyle *_style;
+};
+
+
+#endif
diff --git a/noatun/modules/keyz/Makefile.am b/noatun/modules/keyz/Makefile.am
new file mode 100644
index 00000000..61c9f1bd
--- /dev/null
+++ b/noatun/modules/keyz/Makefile.am
@@ -0,0 +1,14 @@
+INCLUDES= -I$(top_srcdir)/noatun/library -I$(kde_includes)/arts $(all_includes)
+kde_module_LTLIBRARIES = noatun_keyz.la
+
+noatun_keyz_la_SOURCES = keyz.cpp
+
+noatun_keyz_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_keyz_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la
+
+noatun_keyz_la_METASOURCES = AUTO
+
+noinst_HEADERS = keyz.h
+
+noatun_modules_keyz_DATA = keyz.plugin
+noatun_modules_keyzdir = $(kde_datadir)/noatun
diff --git a/noatun/modules/keyz/keyz.cpp b/noatun/modules/keyz/keyz.cpp
new file mode 100644
index 00000000..7b32cf98
--- /dev/null
+++ b/noatun/modules/keyz/keyz.cpp
@@ -0,0 +1,189 @@
+#include <noatun/engine.h>
+#include <noatun/player.h>
+#include <noatun/app.h>
+#include <noatun/playlist.h>
+
+#include <qlayout.h>
+
+#include <kglobalaccel.h>
+#include <kkeydialog.h>
+#include <klocale.h>
+#include <qclipboard.h>
+
+#include "keyz.h"
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new Keyz();
+ }
+}
+
+KGlobalAccel * Keyz::s_accel = 0L;
+
+Keyz::Keyz() : QObject( 0L, "Keyz" ), Plugin(), preMuteVol(0)
+{
+ NOATUNPLUGINC(Keyz);
+ Player *player = napp->player();
+
+ if ( !s_accel )
+ {
+ s_accel = new KGlobalAccel( this, "noatunglobalaccel" );
+ s_accel->insert( "PlayPause", i18n("Play/Pause"), QString::null,
+ CTRL+ALT+Key_P, KKey::QtWIN+CTRL+Key_P,
+ player, SLOT( playpause() ));
+ s_accel->insert( "Stop", i18n("Stop Playing"), QString::null,
+ CTRL+ALT+Key_S, KKey::QtWIN+CTRL+Key_S,
+ player, SLOT( stop() ));
+ s_accel->insert( "Back", i18n("Back"), QString::null,
+ CTRL+ALT+Key_Left, KKey::QtWIN+CTRL+Key_Left,
+ player, SLOT( back() ));
+ s_accel->insert( "Forward", i18n("Forward"), QString::null,
+ CTRL+ALT+Key_Right, KKey::QtWIN+CTRL+Key_Right,
+ player, SLOT( forward() ));
+ s_accel->insert( "Playlist", i18n("Show/Hide Playlist"), QString::null,
+ CTRL+ALT+Key_L, KKey::QtWIN+CTRL+Key_L,
+ player, SLOT( toggleListView() ));
+ s_accel->insert( "OpenFile", i18n("Open File to Play"), QString::null,
+ CTRL+ALT+Key_O, KKey::QtWIN+CTRL+Key_O,
+ napp, SLOT( fileOpen() ));
+ s_accel->insert( "Effects", i18n("Effects Configuration"), QString::null,
+ CTRL+ALT+Key_E, KKey::QtWIN+CTRL+Key_E,
+ napp, SLOT( effectView() ));
+ s_accel->insert( "Preferences", i18n("Preferences"), QString::null,
+ CTRL+ALT+Key_F, KKey::QtWIN+CTRL+Key_F,
+ napp, SLOT( preferences() ));
+ s_accel->insert( "VolumeUp", i18n("Volume Up"), QString::null,
+ CTRL+ALT+SHIFT+Key_Up, KKey::QtWIN+CTRL+SHIFT+Key_Up,
+ this, SLOT( slotVolumeUp() ));
+ s_accel->insert( "VolumeDown", i18n("Volume Down"), QString::null,
+ CTRL+ALT+SHIFT+Key_Down, KKey::QtWIN+CTRL+SHIFT+Key_Down,
+ this, SLOT( slotVolumeDown() ));
+ s_accel->insert( "Mute", i18n("Mute"), QString::null,
+ CTRL+ALT+Key_M, KKey::QtWIN+CTRL+Key_M,
+ this, SLOT( slotMute() ));
+ s_accel->insert( "SeekForward", i18n("Seek Forward"), QString::null,
+ CTRL+ALT+SHIFT+Key_Right, KKey::QtWIN+CTRL+SHIFT+Key_Right,
+ this, SLOT( slotForward() ));
+ s_accel->insert( "SeekBackward", i18n("Seek Backward"), QString::null,
+ CTRL+ALT+SHIFT+Key_Left, KKey::QtWIN+CTRL+SHIFT+Key_Left,
+ this, SLOT( slotBackward() ));
+ s_accel->insert( "NextSection", i18n("Next Section"), QString::null,
+ 0, 0,
+ this, SLOT( slotNextSection() ));
+ s_accel->insert( "PrevSection", i18n("Previous Section"), QString::null,
+ 0, 0,
+ this, SLOT( slotPrevSection() ));
+ s_accel->insert( "CopyTitle", i18n("Copy Song Title to Clipboard"), QString::null,
+ CTRL+ALT+Key_C, KKey::QtWIN+CTRL+Key_C,
+ this, SLOT( slotCopyTitle() ));
+
+ s_accel->insert( "ToggleGUI", i18n("Show/Hide Main Window"), QString::null,
+ CTRL+ALT+Key_W, KKey::QtWIN+CTRL+Key_W,
+ napp, SLOT( toggleInterfaces() ));
+
+ s_accel->readSettings();
+ s_accel->updateConnections();
+ }
+
+ new KeyzPrefs(this);
+}
+
+Keyz::~Keyz()
+{
+ delete s_accel;
+ s_accel = 0L;
+}
+
+void Keyz::slotVolumeUp()
+{
+ int vol = napp->player()->volume();
+ if ( vol >= 100 )
+ return;
+
+ napp->player()->setVolume( vol + 4 );
+}
+
+void Keyz::slotVolumeDown()
+{
+ int vol = napp->player()->volume();
+ if ( vol <= 0 )
+ return;
+
+ napp->player()->setVolume( vol - 4 );
+}
+
+void Keyz::slotForward()
+{
+ napp->player()->skipTo( QMIN(napp->player()->getLength(), napp->player()->getTime() + 3000) ); // + 3 seconds
+}
+
+void Keyz::slotBackward()
+{
+ napp->player()->skipTo( QMAX( 0, napp->player()->getTime() - 3000 )); // - 3 seconds
+}
+
+void Keyz::slotNextSection()
+{
+ Playlist *list = napp->playlist();
+ if ( list ) {
+ PlaylistItem item = list->nextSection();
+ if ( !item.isNull() )
+ napp->player()->play( item );
+ }
+}
+
+void Keyz::slotPrevSection()
+{
+ Playlist *list = napp->playlist();
+ if ( list ) {
+ PlaylistItem item = list->previousSection();
+ if ( !item.isNull() )
+ napp->player()->play( item );
+ }
+}
+
+void Keyz::slotCopyTitle()
+{
+ if (napp->player()->current())
+ KApplication::kApplication()->clipboard()->setText(napp->player()->current().title());
+}
+
+
+void Keyz::slotMute()
+{
+ int vol = napp->player()->volume();
+
+ if ( vol <= 0 ) // already muted
+ {
+ vol = preMuteVol;
+ }
+ else // we should mute
+ {
+ preMuteVol = vol;
+ vol = 0;
+ }
+
+ napp->player()->setVolume( vol );
+}
+
+///////////////////////////////////////////////////////////////////
+
+KeyzPrefs::KeyzPrefs( QObject *parent ) :
+ CModule( i18n("Keyz"), i18n("Shortcut Configuration"), "key_bindings",
+ parent )
+{
+ QVBoxLayout *layout = new QVBoxLayout( this );
+ m_chooser = new KKeyChooser( Keyz::accel(), this );
+ layout->addWidget( m_chooser );
+}
+
+void KeyzPrefs::save()
+{
+ m_chooser->commitChanges();
+ Keyz::accel()->updateConnections();
+ Keyz::accel()->writeSettings();
+}
+
+#include "keyz.moc"
diff --git a/noatun/modules/keyz/keyz.h b/noatun/modules/keyz/keyz.h
new file mode 100644
index 00000000..929e6c67
--- /dev/null
+++ b/noatun/modules/keyz/keyz.h
@@ -0,0 +1,48 @@
+#ifndef KEYZ_H
+#define KEYZ_H
+
+#include <noatun/pref.h>
+#include <noatun/plugin.h>
+
+class Keyz : public QObject, public Plugin
+{
+ Q_OBJECT
+ NOATUNPLUGIND
+
+public:
+ Keyz();
+ ~Keyz();
+
+ static KGlobalAccel *accel() { return s_accel; }
+
+public slots:
+void slotVolumeUp();
+ void slotVolumeDown();
+ void slotMute();
+
+ void slotForward();
+ void slotBackward();
+ void slotNextSection();
+ void slotPrevSection();
+ void slotCopyTitle();
+
+private:
+ static KGlobalAccel *s_accel;
+ int preMuteVol;
+};
+
+
+class KeyzPrefs : public CModule
+{
+ Q_OBJECT
+
+public:
+ KeyzPrefs( QObject *parent );
+ virtual void save();
+
+private:
+ class KKeyChooser* m_chooser;
+
+};
+
+#endif // KEYZ_H
diff --git a/noatun/modules/keyz/keyz.plugin b/noatun/modules/keyz/keyz.plugin
new file mode 100644
index 00000000..41560e86
--- /dev/null
+++ b/noatun/modules/keyz/keyz.plugin
@@ -0,0 +1,69 @@
+Filename=noatun_keyz.la
+Author=Carsten Pfeiffer
+Site=http://noatun.kde.org/
+Email=pfeiffer@kde.org
+Type=other
+License=Artistic
+Name=Keyz
+Name[af]=Keys
+Name[cs]=Klávesy
+Name[de]=Tasten
+Name[eo]=Klavkombinoj
+Name[he]=מקשים
+Name[hi]=के-ईवायज़ेड
+Name[nds]=Tasten
+Name[ru]=Клавишки
+Name[sv]=Tangentisar
+Name[ta]=விசைகள்
+Name[xh]=Izitshixo
+Name[zu]=Izikhiye
+Comment=Global shortcuts for most operations
+Comment[bg]=Управление чрез бързи клавиши на повечето операции
+Comment[bs]=Globalne kratice za većinu operacija
+Comment[ca]=Dreceres globals per a la majoria d'operacions
+Comment[cs]=Globální zkratky pro většinu operací
+Comment[cy]=Byrlwybrau Eang i'r rhan fwyaf o weithredoedd
+Comment[da]=Globale genveje til de fleste operationer
+Comment[de]=Globale Kurzbefehle für die meisten Vorgänge
+Comment[el]=Καθολικές συντομεύσεις για τις περισσότερες λειτουργίες
+Comment[eo]=Mallokaj klavkombinoj por multaj operacioj
+Comment[es]=Accesos directos globales para la mayoría de las operaciones
+Comment[et]=Enamiku tegevuste globaalsed kiirklahvid
+Comment[eu]=Laisterbide globalak eragiketa gehienetarako
+Comment[fa]=میان‌برهای سراسری برای اغلب عملیاتها
+Comment[fi]=Yleiset pikavalinnat lähes kaikille toiminnoille
+Comment[fr]=Raccourcis pour l'essentiel des opérations
+Comment[ga]=Aicearraí comhchoiteanna d'fhormhór na n-oibríochtaí
+Comment[gl]=Atallos globais para a maioría das operacións
+Comment[he]=קיצורי דרך גלובליים לרוב הפעולות
+Comment[hu]=Globális billentyűparancsok a legtöbb művelethez
+Comment[is]=Altækir flýtilyklar fyrir flest allt
+Comment[it]=Scorciatoie globali per le operazioni comuni
+Comment[ja]=ほとんどの操作に使われるグローバルショートカット
+Comment[kk]=Көп операциялардың жалпы-жүйелік перне тірсесімдері
+Comment[km]=ផ្លូវកាត់​សកល​សម្រាប់​ប្រតិបត្តិការ​ភាគ​ច្រើន
+Comment[ko]=대부분 작업에 대한 전역 단축키
+Comment[lt]=Bendri spartieji klavišai daugumai operacijų
+Comment[mk]=Глобални кратенки за повеќето операции
+Comment[nb]=Globale snarveier for de fleste operasjoner
+Comment[nds]=Globaal Tastkombinatschonen för de mehrsten Akschonen
+Comment[ne]=धेरै सञ्चालनका लागि विश्वव्यापी सर्टकट
+Comment[nl]=Globale snelkoppelingen voor de meeste handelingen
+Comment[nn]=Globale snøggtastar for dei fleste operasjonar
+Comment[pl]=Globalne skróty dla większości operacji
+Comment[pt]=Atalhos globais para a maioria das operações
+Comment[pt_BR]=Atalhos globais para a maioria das operações
+Comment[ro]=Acceleratori globali pentru operaţii uzuale
+Comment[ru]=Привязки клавиш для большинства действий
+Comment[sk]=Globálne skratky pre väčšinu operácií
+Comment[sl]=Globalne bližnjice za večino operacij
+Comment[sr]=Глобалне пречице за већину операција
+Comment[sr@Latn]=Globalne prečice za većinu operacija
+Comment[sv]=Globala snabbtangenter för de flesta operationerna
+Comment[ta]=பல செயலுகளுக்கான உலகளாவிய குறுக்குவழிகள்
+Comment[th]=ปุ่มพิมพ์ลัดส่วนกลางสำหรับปฏิบัติการส่วนใหญ่
+Comment[tr]=Pek çok İşlemler için Genel Kısayollar
+Comment[uk]=Глобальні скорочення для більшості дій
+Comment[zh_CN]=大多数操作的全局快捷键
+Comment[zh_HK]=大部分操作的通用捷徑
+Comment[zh_TW]=給一般大部分操作使用的全域快速鍵
diff --git a/noatun/modules/kjofol-skin/ChangeLog b/noatun/modules/kjofol-skin/ChangeLog
new file mode 100644
index 00000000..537de012
--- /dev/null
+++ b/noatun/modules/kjofol-skin/ChangeLog
@@ -0,0 +1,111 @@
+ChangeLog (only lists MY changes)
+-----------------------------------------------------------------------------------
+
+2003-01-09 Stefan Gehn <sgehn@gmx.net>
+* Allow use of system fonts instead of skin-supplied pixmap-fonts, user
+ has to take care for choosing an appropriate font and font-color
+
+2003-01-19 Stefan Gehn <sgehn@gmx.net>
+* Equalizer support
+* Ignore unsupported buttons for spectrum/oscilloscope, won't work with default skin anyway
+* Display about-lines in skinselector
+
+2002-04-04 Stefan Gehn <sgehn@gmx.net>
+* Started work on config-dialog, this will unhide most things you can now only
+ set by clicking on widgets (i.e. time counting mode)
+
+2002-01-21 Stefan Gehn <sgehn@gmx.net>
+* Actually read counting-mode of time-display from config
+
+2002-01-20 Stefan Gehn <sgehn@gmx.net>
+* Fix ugly crash on Startup related to Time-Display
+* less debug
+
+2002-01-06 Stefan Gehn <sgehn@gmx.net>
+* Made Playlist-button show if playlist-window is open even after loading a new skin
+* Added Tooltips for text-displays
+* usual removal of old debug-messages and adding new ones :)
+
+2002-01-04 Stefan Gehn <sgehn@gmx.net>
+* changed scrolling-text behaviour (scrolls 1/2 char every 400ms now)
+* playlist-button is a state-button, i.e. shows if playlist-window is open or not
+* commented or removed some more unused debug-messages
+
+2001-12-14 Stefan Gehn <sgehn@gmx.net>
+* slightly changed skin-installer (creates dirs without archive extension)
+* code cleanups, removed debug-messages
+
+2001-12-12 Stefan Gehn <sgehn@gmx.net>
+* finally made font-spacing between characters working (no garbage inside space)
+* textfields with more space than needed for the string to display now get centered text
+ (take a look at volumetext or pitchtext to see the centere-effect)
+* Noatun is not shown in taskbar if in dockmode as it's no normal window in that mode
+* take care if Playlist is shown/hidden and update the playlistbutton
+* using kdDebug(66666) for debugmessages instead of stderr-output
+
+2001-10-04 Stefan Gehn <sgehn@gmx.net>
+* Still fighting with analyzer and osci visualizations,
+ now both are buffered and flicker-free
+* fixed stupid bug in isGray(qRgb), now RGB(0,0,0) is treated as gray again :)
+* commented out (hopefully) unneded debug-messages
+* added splash-screen for skins supporting it (only skin I know of: K-Nine)
+
+2001-09-30 Stefan Gehn <sgehn@gmx.net>
+* ignore alpha-channel of loaded files, they break applying masks to QPixmaps,
+ result of this is text without transparency
+ (this happened for two skins: adagio and xbs)
+
+2001-09-28 Stefan Gehn <sgehn@gmx.net>
+* analyzer-like scope is now drawn using a gradient instead of exactly one boring color
+* removed KJLoader::fixSkin() as Njaard deleted the script doing all the work, I assume
+ that this is not needed anymore (guess: maybe QT3 now handles
+ most fileformats in a proper way?)
+
+2001-09-22 Stefan Gehn <sgehn@gmx.net>
+* isGray(qRgb) is more tolerant, colors like rgb(162,163,162) are treated as gray too
+ This fixes some volume/pitch-sliders and makes using them a lot easier
+* made titletext move again (works with and without transparent text)
+
+2001-09-16 Stefan Gehn <sgehn@gmx.net>
+* Made it compile with QT3 and stopped using Njaard's hack to load PNGs
+
+2001-09-04 Stefan Gehn <sgehn@gmx.net>
+* added support for transparent fonts (a skin named steelforged still got problems though)
+ for the moment this disables moving of titletext,
+ I have to find a new way to move it (bitblt on a masked QPixmap is tricky)
+
+2001-09-03 Stefan Gehn <sgehn@gmx.net>
+* added support for pitch-slider and its textlabel
+ kjfol now depends on libartsmodules because of pitch-support
+* finished splitting up kjloader.cpp into several files.
+ Now almost every class has its own file
+ also took care that compiling with --enable-final works
+
+2001-09-02 Stefan Gehn <sgehn@gmx.net>
+* started splitting up kjloader.cpp into several files
+
+Before 2001-09-02 Stefan Gehn <sgehn@gmx.net>
+
+I should have started a ChangeLog earlier :/
+Never thought I would change THAT much
+(it all started with small noatun-bugfixes)
+
+I'll now try to list the tons of changes done before (totally unordered)
+
+* fixed seeker to work with long files
+ (I've got a 330min mp3, seeking did not work behind about 120min)
+* made the repeat-button work, it switches between no-looping and song-looping
+* made the forward/backward-buttons work, they will skip 10 seconds
+* added dockmode-support, in this mode kjfol will dock to the currently active window
+ the window's behaviour looks like this mode needs some more work
+* added support for textlabels showing current volume
+* added a skininstaller, it still needs proper widgets showing
+ the actions going on (unpacking, moving files, deleting files on uninstall)
+* tweaked builtin visualizations, both now try to fit into any possible skin
+ last activated vis is remembered on restart
+* vis honors "AnalyzerColor" and defaults to white if that key is missing in the skin.rc
+* support for buttons with "DARKEN"-flag (normally "BMPsomenumber" is used)
+* made kjofol parse the skin.rc in lowercase only, now it's fully case-insensitve
+* many many tests for existence of keys, 99% of former crashes were caused by not making
+ sure a certain key really exists. Many places now assume "default"-values if keys are
+ missing, this leads to MUCH better compatibility
diff --git a/noatun/modules/kjofol-skin/Makefile.am b/noatun/modules/kjofol-skin/Makefile.am
new file mode 100644
index 00000000..17ff1162
--- /dev/null
+++ b/noatun/modules/kjofol-skin/Makefile.am
@@ -0,0 +1,41 @@
+INCLUDES = -I$(top_srcdir)/noatun/library \
+ -I$(top_builddir)/noatun/library \
+ -I$(top_builddir)/arts/modules \
+ -I$(top_builddir)/arts/midi \
+ -I$(top_builddir)/arts/modules/synth \
+ -I$(top_builddir)/arts/modules/common \
+ -I$(top_builddir)/arts/modules/effects \
+ -I$(top_builddir)/arts/modules/mixers \
+ -I$(top_builddir)/arts/gui/common \
+ -I$(kde_includes)/kio \
+ -I$(kde_includes)/arts \
+ $(all_includes)
+
+kde_module_LTLIBRARIES = noatun_kjofol.la
+SUBDIRS= . skins
+METASOURCES = AUTO
+
+noatun_kjofol_la_SOURCES = kjloader.cpp kjwidget.cpp kjbutton.cpp kjseeker.cpp \
+ kjsliders.cpp kjfont.cpp kjtextdisplay.cpp \
+ kjvis.cpp kjequalizer.cpp kjbackground.cpp \
+ noatunui.cpp parser.cpp \
+ kjprefs.cpp \
+ kjskinselectorwidget.ui kjguisettingswidget.ui
+
+noatun_kjofol_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_kjofol_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la \
+ $(LIB_KIO) -lm \
+ $(top_builddir)/arts/modules/libartsmodules.la
+
+noinst_HEADERS = kjloader.h kjwidget.h kjbutton.h kjseeker.h \
+ kjsliders.h kjfont.h kjtextdisplay.h \
+ kjvis.h kjequalizer.h kjbackground.h \
+ parser.h kjprefs.h \
+ kjskinselectorwidget.h kjguisettingswidget.h
+
+noatun_modules_kjofol_DATA = kjofolui.plugin
+noatun_modules_kjofoldir = $(kde_datadir)/noatun
+
+kjloader.lo: ../../../arts/modules/artsmodules.h ../../../arts/midi/artsmidi.h ../../../arts/gui/common/artsgui.h ../../../arts/modules/common/artsmodulescommon.h ../../../arts/modules/synth/artsmodulessynth.h ../../../arts/modules/effects/artsmoduleseffects.h ../../../arts/modules/mixers/artsmodulesmixers.h ../../library/noatunarts/noatunarts.h
+kjsliders.lo: ../../../arts/modules/artsmodules.h ../../../arts/midi/artsmidi.h ../../../arts/gui/common/artsgui.h ../../../arts/modules/common/artsmodulescommon.h ../../../arts/modules/synth/artsmodulessynth.h ../../../arts/modules/effects/artsmoduleseffects.h ../../../arts/modules/mixers/artsmodulesmixers.h
+kjtextdisplay.lo: ../../../arts/modules/artsmodules.h ../../../arts/midi/artsmidi.h ../../../arts/gui/common/artsgui.h ../../../arts/modules/common/artsmodulescommon.h ../../../arts/modules/synth/artsmodulessynth.h ../../../arts/modules/effects/artsmoduleseffects.h ../../../arts/modules/mixers/artsmodulesmixers.h
diff --git a/noatun/modules/kjofol-skin/helpers.cpp b/noatun/modules/kjofol-skin/helpers.cpp
new file mode 100644
index 00000000..b38822a6
--- /dev/null
+++ b/noatun/modules/kjofol-skin/helpers.cpp
@@ -0,0 +1,64 @@
+/***************************************************************************
+ helpers.cpp
+ Just a few functions needed in several Kjofol-classes
+ ---------------------------------------------
+ Maintainer: Charles Samuels <charles@kde.org>
+
+ ***************************************************************************/
+
+#ifndef KJHELPERS_CPP
+#define KJHELPERS_CPP
+
+static int grayRgb(QRgb r)
+{
+ return qGray(qRed(r), qGreen(r), qBlue(r));
+}
+
+static int isGray(QRgb r)
+{
+// this is more tolerant than the old version
+// i.e. RGB 162 163 162 is treated as gray too
+// too many broken skins around having such colors
+
+// cerr << "r("<<qRed(r)<<","<<qGreen(r)<<","<<qBlue(r)<<")"<<endl;
+
+ if ( (qRed(r)==qGreen(r)) || (qRed(r)+1==qGreen(r)) || (qRed(r)-1==qGreen(r)))
+ {
+ if ( (qRed(r)==qBlue(r)) || (qRed(r)+1==qBlue(r)) || (qRed(r)-1==qBlue(r)))
+ {
+ // looks a bit like gray, so return true
+ return (1);
+ }
+ }
+ // well, it's not gray
+ return(0);
+
+/*
+ // mETz: wrong braces in the code below ??
+ return (qRed(r)==qGreen(r)) && (qRed(r) == qBlue(r));
+*/
+}
+
+// only works little endian
+// UPDATE: should work on both little and big endian now (haven't tested that!)
+// this code is taken from the QT-docu and I hope that this example
+// is one of the working ones ;)
+inline void setPixel1BPP(QImage &image, int x, int y, bool value)
+{
+ if ( image.bitOrder() == QImage::LittleEndian )
+ {
+ if (value)
+ *(image.scanLine(y) + (x >> 3)) |= 1 << (x & 7);
+ else
+ *(image.scanLine(y) + (x >> 3)) &= ~(1 << (x & 7));
+ }
+ else
+ {
+ if (value)
+ *(image.scanLine(y) + (x >> 3)) |= 1 << (7-(x & 7));
+ else
+ *(image.scanLine(y) + (x >> 3)) &= ~(1 << (7-(x & 7)));
+ }
+}
+
+#endif
diff --git a/noatun/modules/kjofol-skin/kjbackground.cpp b/noatun/modules/kjofol-skin/kjbackground.cpp
new file mode 100644
index 00000000..83c19ace
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjbackground.cpp
@@ -0,0 +1,29 @@
+/***************************************************************************
+ kjbackground.cpp
+ --------------------------------------
+ Just draws the main-pixmap of a KJfol-Skin
+ --------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+#include "kjbackground.h"
+
+KJBackground::KJBackground(KJLoader *parent)
+ : KJWidget(parent)
+{
+ QImage ibackground;
+
+ mBackground = parent->pixmap(parser()["backgroundimage"][1]);
+ ibackground = parent->image(parser()["backgroundimage"][1]);
+
+ parent->setMask( getMask(ibackground) );
+ parent->setFixedSize ( QSize(mBackground.width(), mBackground.height()) );
+
+ setRect(0,0,parent->width(),parent->height());
+}
+
+void KJBackground::paint(QPainter *painter, const QRect &rect)
+{
+ bitBlt(painter->device(), rect.topLeft(), &mBackground, rect, Qt::CopyROP);
+}
diff --git a/noatun/modules/kjofol-skin/kjbackground.h b/noatun/modules/kjofol-skin/kjbackground.h
new file mode 100644
index 00000000..502611c5
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjbackground.h
@@ -0,0 +1,21 @@
+#ifndef KJBACKGROUND_H
+#define KJBACKGROUND_H
+
+#include "kjwidget.h"
+//#include "kjloader.h"
+class KJLoader;
+
+#include <qpainter.h>
+
+class KJBackground : public KJWidget
+{
+public:
+ KJBackground(KJLoader *);
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &) {return false;}
+ virtual void mouseRelease(const QPoint &, bool) {}
+
+private:
+ QPixmap mBackground;
+};
+#endif
diff --git a/noatun/modules/kjofol-skin/kjbutton.cpp b/noatun/modules/kjofol-skin/kjbutton.cpp
new file mode 100644
index 00000000..403ea61e
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjbutton.cpp
@@ -0,0 +1,301 @@
+/***************************************************************************
+ kjbutton.cpp
+ --------------------------------------
+ Handles all ordinary Buttons like stop, play, pause, etc.
+ --------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+#include "kjbutton.h"
+#include "kjbutton.moc"
+#include "kjloader.h"
+#include "kjprefs.h"
+
+#include <noatun/pref.h>
+#include <noatun/player.h>
+#include <noatun/vequalizer.h>
+
+#include <qcursor.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kpixmap.h>
+#include <kpixmapeffect.h>
+#include <kurl.h>
+#include <kfiledialog.h>
+#include <khelpmenu.h>
+#include <kpopupmenu.h>
+
+/*******************************************
+ * KJButton
+ *******************************************/
+
+KJButton::KJButton(const QStringList &i, KJLoader *parent)
+ : QObject(0), KJWidget(parent), mTitle(i[0]), mShowPressed(false)
+{
+// kdDebug(66666) << k_funcinfo << "new button: " << i[0].latin1() << endl;
+ mPushedPixmap = (i.count() >= 7);
+
+ // get the rectangle
+ int x, y, xs, ys;
+ x=i[1].toInt();
+ y=i[2].toInt();
+ xs=i[3].toInt()-x; // width
+ ys=i[4].toInt()-y; // height
+ setRect ( x, y, xs, ys );
+
+ QStringList temp = i;
+
+ // search for selected button-type
+ // can be either BMPx where x is a number representing one
+ // of the background-images
+ // or darken which means just darken the button on click
+ bool gotBack = false; // in case any of these keys is duplicated
+ for(QStringList::Iterator it = temp.begin(); it != temp.end(); ++it)
+ {
+ if((*it).contains("bmp"))
+ {
+ QString pressedTmp = backgroundPressed((*it));
+ if(!pressedTmp.isEmpty())
+ {
+ mPressed = parent->pixmap(pressedTmp);
+ gotBack = true;
+ }
+ }
+ else if((*it) == "darken")
+ {
+ // take background and darken the buttons rectangle
+ // FIXME: what KPixmapEffect causes the desired effect?
+ // intensity is the wrong one
+ KPixmap temp = parent->pixmap(parser()["backgroundimage"][1]);
+ mPressed = (QPixmap)KPixmapEffect::intensity ( temp, 1.2f );
+ gotBack = true;
+ }
+ if(gotBack)
+ break;
+ }
+
+ if(!gotBack)
+ {
+ kdDebug(66666) << k_funcinfo << "Couldn't find valid background for button '" <<
+ mTitle << "', dafulting to backgroundimage" << endl;
+ mPressed = parent->pixmap(parser()["backgroundimage"][1]);
+ }
+
+ // playlistbutton has to show if playlistwindow is open
+ // right after the button appears on screen
+ if (mTitle=="playlistbutton")
+ {
+ mShowPressed = napp->playlist()->listVisible();
+ connect( napp->player(), SIGNAL(playlistShown()), this, SLOT(slotPlaylistShown()) );
+ connect( napp->player(), SIGNAL(playlistHidden()), this, SLOT(slotPlaylistHidden()) );
+ }
+ else if ( mTitle=="equalizeroffbutton") // same goes for EQ buttons
+ {
+ mShowPressed = (!napp->vequalizer()->isEnabled());
+ connect( napp->vequalizer(), SIGNAL(enabled(bool)), SLOT(slotEqEnabled(bool)));
+ }
+ else if (mTitle=="equalizeronbutton")
+ {
+ mShowPressed = napp->vequalizer()->isEnabled();
+ connect( napp->vequalizer(), SIGNAL(enabled(bool)), SLOT(slotEqEnabled(bool)));
+ }
+}
+
+QString KJButton::tip()
+{
+ QString str;
+ if (mTitle=="closebutton")
+ str=i18n("Close");
+ else if (mTitle=="minimizebutton")
+ str=i18n("Minimize");
+ else if (mTitle=="aboutbutton")
+ str=i18n("About");
+ else if (mTitle=="stopbutton")
+ str=i18n("Stop");
+ else if (mTitle=="playbutton")
+ str=i18n("Play");
+ else if (mTitle=="pausebutton")
+ str=i18n("Pause");
+ else if (mTitle=="openfilebutton")
+ str=i18n("Open");
+ else if (mTitle=="playlistbutton")
+ str=i18n("Playlist");
+ else if (mTitle=="repeatbutton")
+ str=i18n("Loop");
+ else if (mTitle=="equalizerbutton")
+ str=i18n("Show Equalizer Window");
+ else if (mTitle=="equalizeronbutton")
+ str=i18n("Turn on Equalizer");
+ else if (mTitle=="equalizeroffbutton")
+ str=i18n("Turn off Equalizer");
+ else if (mTitle=="equalizerresetbutton")
+ str=i18n("Reset Equalizer");
+ else if (mTitle=="nextsongbutton")
+ str=i18n("Next");
+ else if (mTitle=="previoussongbutton")
+ str=i18n("Previous");
+ else if (mTitle=="forwardbutton")
+ str=i18n("Forward");
+ else if (mTitle=="rewindbutton")
+ str=i18n("Rewind");
+ else if (mTitle=="preferencesbutton")
+ str=i18n("K-Jöfol Preferences");
+ else if (mTitle=="dockmodebutton")
+ str=i18n("Switch to dockmode");
+ else if (mTitle=="undockmodebutton")
+ str=i18n("Return from dockmode");
+
+ return str;
+}
+
+void KJButton::paint(QPainter *, const QRect &)
+{
+ if (mShowPressed)
+ bitBlt(KJWidget::parent(), rect().topLeft(), &mPressed, rect(), Qt::CopyROP);
+}
+
+bool KJButton::mousePress(const QPoint &)
+{
+ bitBlt(KJWidget::parent(), rect().topLeft(), &mPressed, rect(), Qt::CopyROP);
+ return true;
+}
+
+void KJButton::showPressed(bool b)
+{
+ mShowPressed = b;
+ if ( mShowPressed )
+ repaint(true); // repaint with selected image
+ else
+ repaint(false); // repaint with default image (player-background)
+}
+
+void KJButton::slotPlaylistShown(void)
+{
+// kdDebug(66666) << "KJButton::slotPlaylistShown()" << endl;
+ showPressed(true);
+}
+
+void KJButton::slotPlaylistHidden(void)
+{
+// kdDebug(66666) << "KJButton::slotPlaylistHidden()" << endl;
+ showPressed(false);
+}
+
+void KJButton::slotEqEnabled(bool on)
+{
+// kdDebug(66666) << "KJButton::slotEqEnabled(" << on << ") for " << mTitle << endl;
+ if (mTitle=="equalizeronbutton")
+ showPressed(on);
+ else if (mTitle=="equalizeroffbutton")
+ showPressed(!on);
+}
+
+void KJButton::mouseRelease(const QPoint &, bool in)
+{
+ // repaint with default image (player-background)
+ repaint(false);
+
+ if (!in) // only do something if users is still inside the button
+ return;
+
+ // now, find what widget I am and do the proper action
+ if (mTitle=="closebutton")
+ KJWidget::parent()->close();
+ else if (mTitle=="minimizebutton")
+ KJWidget::parent()->minimize();
+ else if (mTitle=="aboutbutton")
+ KJWidget::parent()->helpMenu()->aboutApplication();
+ else if (mTitle=="stopbutton")
+ napp->player()->stop();
+ else if (mTitle=="playbutton")
+ napp->player()->play();
+ else if (mTitle=="pausebutton")
+ napp->player()->playpause();
+ else if (mTitle=="openfilebutton")
+ {
+ KURL file(KFileDialog::getOpenURL(0, napp->mimeTypes(), KJWidget::parent(), i18n("Select File to Play")));
+ if (file.isValid())
+ napp->player()->openFile(file);
+ }
+ else if (mTitle=="playlistbutton")
+ napp->player()->toggleListView();
+ else if (mTitle=="repeatbutton")
+ {
+ KPopupMenu *loopMenu = new KPopupMenu(KJWidget::parent(),"loopMenu");
+ int selectedItem = 0;
+
+ loopMenu->setCheckable(true);
+ loopMenu->insertTitle(i18n("Loop Style"));
+ loopMenu->insertItem(i18n("&None"), static_cast<int>(Player::None));
+ loopMenu->insertItem(i18n("&Song"), static_cast<int>(Player::Song));
+ loopMenu->insertItem(i18n("&Playlist"), static_cast<int>(Player::Playlist));
+ loopMenu->insertItem(i18n("&Random"), static_cast<int>(Player::Random));
+
+ loopMenu->setItemChecked(static_cast<int>(napp->player()->loopStyle()), true); // select current loopstyle in menu
+ selectedItem = loopMenu->exec(QCursor::pos());
+ if (selectedItem != -1)
+ napp->player()->loop(selectedItem); // set new loopstyle
+
+ delete loopMenu;
+
+/*
+ if ( napp->player()->loopStyle() == 1)
+ {
+// kdDebug(66666) << "loop song is OFF" << endl;
+
+// bah, xlib.h already defined None
+#undef None
+ napp->player()->loop( Player::None );
+ showPressed( false );
+ }
+ else
+ {
+// kdDebug(66666) << "loop song is ON" << endl;
+ napp->player()->loop( Player::Song );
+ showPressed ( true );
+ }
+*/
+ }
+ else if (mTitle=="equalizerbutton")
+ {
+ napp->equalizerView();
+ }
+ else if (mTitle=="equalizeronbutton")
+ {
+ if (!napp->vequalizer()->isEnabled())
+ napp->vequalizer()->enable();
+ }
+ else if (mTitle=="equalizeroffbutton")
+ {
+ if (napp->vequalizer()->isEnabled())
+ napp->vequalizer()->disable();
+ }
+ else if (mTitle=="equalizerresetbutton")
+ {
+ for (int band=0; band<napp->vequalizer()->bands(); band++)
+ napp->vequalizer()->band(band).setLevel(0);
+ /*
+ // That preset resets to 6 bands, that's not what we want
+ VPreset set = napp->vequalizer()->presetByName("Zero");
+ if (set) // tests if that preset is valid
+ set.load();
+ */
+ }
+ else if (mTitle=="nextsongbutton")
+ napp->player()->forward();
+ else if (mTitle=="previoussongbutton")
+ napp->player()->back();
+ else if (mTitle=="forwardbutton")
+ napp->player()->skipTo(napp->player()->getTime()+10000); // TODO: make +- 10 secs configurable
+ else if (mTitle=="rewindbutton")
+ napp->player()->skipTo(napp->player()->getTime()-10000);
+ else if (mTitle=="preferencesbutton")
+ napp->preferencesBox()->show(static_cast<CModule *>(KJWidget::parent()->prefs()));
+ else if (mTitle=="dockmodebutton")
+ KJWidget::parent()->switchToDockmode();
+ else if (mTitle=="undockmodebutton")
+ KJWidget::parent()->returnFromDockmode();
+ else
+ kdDebug(66666) << "unknown buttontype: " << mTitle.latin1() << endl;
+}
diff --git a/noatun/modules/kjofol-skin/kjbutton.h b/noatun/modules/kjofol-skin/kjbutton.h
new file mode 100644
index 00000000..4af4fcb0
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjbutton.h
@@ -0,0 +1,34 @@
+#ifndef KJBUTTON_H
+#define KJBUTTON_H
+
+#include "kjwidget.h"
+#include <qobject.h>
+class KJLoader;
+
+class KJButton : public QObject, public KJWidget
+{
+Q_OBJECT
+public:
+ KJButton(const QStringList&, KJLoader *);
+
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &pos);
+ virtual void mouseRelease(const QPoint &pos, bool);
+ virtual void showPressed(bool b=true);
+
+ virtual QString tip();
+
+private slots:
+ void slotPlaylistShown(void);
+ void slotPlaylistHidden(void);
+ void slotEqEnabled(bool on);
+
+private:
+ QPixmap mBackground;
+ bool mPushedPixmap;
+ QPixmap mPressed;
+ QString mTitle;
+ bool mShowPressed;
+};
+
+#endif
diff --git a/noatun/modules/kjofol-skin/kjequalizer.cpp b/noatun/modules/kjofol-skin/kjequalizer.cpp
new file mode 100644
index 00000000..3f0716e4
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjequalizer.cpp
@@ -0,0 +1,129 @@
+/***************************************************************************
+ kjequalizer.cpp - links noatun VEqualizer and KJofol
+ --------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+#include "kjequalizer.h"
+#include "kjequalizer.moc"
+
+#include <qpainter.h>
+#include <qtimer.h>
+
+#include <kdebug.h>
+#include <kpixmap.h>
+
+#include <noatun/vequalizer.h>
+
+KJEqualizer::KJEqualizer(const QStringList &l, KJLoader *p)
+ : QObject(0), KJWidget(p), mBack(0), mView(0), mInterpEq(0)
+{
+ int x=l[1].toInt();
+ int y=l[2].toInt();
+ int xs=l[3].toInt()-x;
+ int ys=l[4].toInt()-y;
+ setRect(x,y,xs,ys);
+
+ mBars = p->pixmap(parser()["equalizerbmp"][3]);
+
+ mBands = l[6].toInt();
+ mXSpace = l[7].toInt();
+
+ // background under equalizer
+ // needed to only blit onto screen ONCE and not for every band
+ QPixmap tmp = p->pixmap(p->item("backgroundimage")[1]);
+ mBack = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mBack, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ // buffer for view
+ mView = new QPixmap ( xs, ys );
+
+ mBandWidth=parser()["EqualizerBmp"][1].toInt();
+ mBandHalfHeight=parser()["EqualizerBmp"][2].toInt();
+
+ kdDebug(66666) << "[KJEqualizer] mBands=" << mBands << ", mXSpace=" << mXSpace << ", mBandWidth=" << mBandWidth << ", mBandHalfHeight=" << mBandHalfHeight << "." << endl;
+
+ kdDebug(66666) << "[KJEqualizer] creating VInterpolation for " << mBands << " bands..." << endl;
+ mInterpEq = new VInterpolation(mBands);
+// napp->vequalizer()->setBands(mBands); // FIXME: hack because spline sucks :P
+ connect(napp->vequalizer(), SIGNAL(changed()), this, SLOT(slotUpdateBuffer()));
+
+ slotUpdateBuffer(); // fill mView pixmap with valid data
+}
+
+KJEqualizer::~KJEqualizer(void)
+{
+ delete mInterpEq;
+ delete mView;
+ delete mBack;
+}
+
+int KJEqualizer::barNum(const QPoint &pos) const
+{
+ int x = pos.x();
+ x = x / mXSpace;
+ return mInterpEq->bands() * x / mBands;
+}
+
+int KJEqualizer::level(const QPoint &pos) const
+{
+ int y = ((-pos.y()) + mBandHalfHeight+1) * (200/mBandHalfHeight);
+ return y;
+}
+
+void KJEqualizer::paint(QPainter *p, const QRect &)
+{
+ QPixmap temp(rect().width(), rect().height());
+ // draw background into buffer
+ bitBlt ( &temp, 0, 0, mBack, 0, 0, -1, -1, Qt::CopyROP );
+ // draw band sliders into buffer
+ bitBlt( &temp, 0, 0, mView, 0, 0, rect().width(), rect().height(), Qt::CopyROP);
+ // and draw it on screen
+ bitBlt(p->device(), rect().topLeft(), &temp, QRect(0,0,-1,-1), Qt::CopyROP);
+}
+
+void KJEqualizer::slotUpdateBuffer()
+{
+// kdDebug(66666) << "[KJEqualizer] slotUpdateBuffer() called." << endl;
+
+ QBitmap regionMask( rect().width(), rect().height(), true); // fully transparent mask
+ QPainter mask( &regionMask );
+
+ QPoint destX = QPoint(0, 0);
+
+ for (int band=0; band<mBands; band++)
+ {
+ int level = mInterpEq->level(band);
+ if (level>200) level=200;
+ if (level<-200) level=-200;
+ int picNum = ((int)(level+200)*(mBandHalfHeight-1) / 400) + 1;
+ int xPos = (picNum * mBandWidth) - mBandWidth;
+
+// kdDebug(66666) << "[KJEqualizer] band=" << band << ", level=" << level << ", picNum=" << picNum << " @ xpos=" << xPos << "." << endl;
+
+ bitBlt(mView, destX, &mBars, QRect(xPos,0,mBandWidth,rect().height()), Qt::CopyROP);
+ // make slider opaque in mask so you see something on screen
+ mask.fillRect ( destX.x(), 0, mBandWidth, rect().height(), Qt::color1 );
+ destX += QPoint(mXSpace,0);
+
+ } // for()
+ // whole thingy has been drawn, now set the mask
+ mView->setMask( regionMask );
+ repaint();
+}
+
+void KJEqualizer::mouseMove(const QPoint &p, bool in)
+{
+ if (!in) return;
+ mousePress(p);
+}
+
+bool KJEqualizer::mousePress(const QPoint &p)
+{
+ kdDebug(66666) << "[KJEqualizer] setting band " << mBands << "/" << barNum(p)+1 << " to level " << level(p) << endl;
+ VBand b = mInterpEq->band( barNum(p) );
+ b.setLevel( level(p) );
+// mouseMove(p, true);
+ return true;
+}
diff --git a/noatun/modules/kjofol-skin/kjequalizer.h b/noatun/modules/kjofol-skin/kjequalizer.h
new file mode 100644
index 00000000..f3f13bd5
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjequalizer.h
@@ -0,0 +1,39 @@
+#ifndef KJEQUALIZER_H
+#define KJEQUALIZER_H
+
+#include "kjwidget.h"
+//#include "kjloader.h"
+class KJLoader;
+class VInterpolation;
+
+#include <qobject.h>
+
+class KJEqualizer : public QObject, public KJWidget
+{
+Q_OBJECT
+public:
+ KJEqualizer(const QStringList &, KJLoader *parent);
+ ~KJEqualizer(void);
+
+ virtual void mouseMove(const QPoint &pos, bool);
+ virtual bool mousePress(const QPoint&);
+ virtual void paint(QPainter *p, const QRect &rect);
+ int barNum(const QPoint &pos) const;
+ int level(const QPoint &pos) const;
+
+public slots:
+ void slotUpdateBuffer();
+
+private:
+ int mBands;
+ int mXSpace;
+
+ int mBandWidth;
+ int mBandHalfHeight;
+ QPixmap mBars; // holds all slider images
+ QPixmap *mBack; // holds background of EQ for easy repaint
+ QPixmap *mView; // holds prepared img of all sliders
+ VInterpolation *mInterpEq;
+};
+
+#endif
diff --git a/noatun/modules/kjofol-skin/kjfont.cpp b/noatun/modules/kjofol-skin/kjfont.cpp
new file mode 100644
index 00000000..df2abed3
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjfont.cpp
@@ -0,0 +1,290 @@
+/***************************************************************************
+ kjfont.cpp
+ --------------------------------------
+ Font-Handling of KJfol
+ Creates pixmaps of strings using the supplied font-pixmap
+ --------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+#include "kjfont.h"
+#include "kjloader.h"
+#include "kjwidget.h"
+#include "kjprefs.h"
+
+#include <kdebug.h>
+#include <kglobalsettings.h>
+
+#include <qimage.h>
+#include <qpainter.h>
+
+/*******************************************
+ * KJFont
+ *******************************************/
+
+KJFont::KJFont(const QString &prefix, KJLoader *parent) : mTextMask(0), mTransparentRGB(0)
+{
+// kdDebug(66666) << "KJFont::KJFont(const QString &prefix, KJLoader *parent)" << prefix.latin1() << endl;
+
+ if (prefix=="timefont")
+ {
+ mString[0]="0123456789: ";
+ mString[1]=mString[2]="";
+ mNullChar=' ';
+ }
+ else if ( (prefix=="volumefont") || (prefix=="pitchfont") )
+ {
+ mString[0]="0123456789% ";
+ mString[1]=mString[2]="";
+ mNullChar=' ';
+ }
+ else
+ {
+ mString[0]="abcdefghijklmnopqrstuvwxyz\"@";
+ mString[1]="0123456789;_:()-'!_+\\/[]*&%.=$#";
+ mString[2]="?*, ";
+ mNullChar=' ';
+ }
+
+ mText = parent->pixmap(parent->item(prefix+"image")[1]);
+
+ if ( parent->exist(prefix+"size") )
+ {
+ mWidth = parent->item(prefix+"size")[1].toInt();
+ mHeight = parent->item(prefix+"size")[2].toInt();
+ }
+ else // try to load the font even we are missing important settings
+ { // this still can cause crashes!
+ kdDebug(66666) << "font height/width missing, this skin might crash noatun!" << endl;
+
+ mWidth = mText.width() / strlen(mString[0]);
+
+ if ( (prefix=="timefont") || (prefix=="volumefont") || (prefix=="pitchfont") )
+ mHeight = mText.height();
+ else
+ mHeight = mText.height()/3;
+ }
+
+// kdDebug(66666) << prefix << " h: " << mHeight << " w: " << mWidth << endl;
+
+ // fix for wrong numbers in rc (a skin named steelforged needs that)
+ if ( mHeight > mText.height() )
+ mHeight = mText.height();
+
+ // Stupid Skin authors tend to forget keys :/
+ if ( parent->exist(prefix+"spacing") )
+ mSpacing = parent->item(prefix+"spacing")[1].toInt();
+ else
+ mSpacing = 0; // FIXME: What's default for this in kjfol???
+
+ if ( parent->exist(prefix+"transparent") )
+ mTransparent = (bool)parent->item(prefix+"transparent")[1].toInt();
+ else
+ mTransparent = true; // transparency seems to be default in kjfol
+
+ // define color in font that will be transparent later on
+ if ( mTransparent )
+ {
+ QImage ibackground = mText.convertToImage();
+ mTransparentRGB = ibackground.pixel( ibackground.width()-1, ibackground.height()-1 );
+// kdDebug(66666) << "color (" << qRed(mTransparentRGB) << "," << qGreen(mTransparentRGB) << "," << qBlue(mTransparentRGB) << ") will be transparent for " << prefix.latin1() << endl;
+ mTextMask = KJWidget::getMask(ibackground,mTransparentRGB);
+ }
+
+ mUseSysFont = KJLoader::kjofol->prefs()->useSysFont();
+ sysFontMetrics = 0L;
+ if (mUseSysFont)
+ recalcSysFont();
+}
+
+void KJFont::recalcSysFont(void)
+{
+// kdDebug(66666) << k_funcinfo << "called." << endl;
+
+ mUseSysFont = KJLoader::kjofol->prefs()->useSysFont();
+ if (!mUseSysFont)
+ return;
+ sysFont = QFont(KJLoader::kjofol->prefs()->sysFont());
+ sysFont.setStyleStrategy( QFont::NoAntialias );
+ if ( sysFontMetrics )
+ delete sysFontMetrics;
+ sysFontColor = KJLoader::kjofol->prefs()->sysFontColor();
+
+// kdDebug(66666) << "mHeight=" << mHeight << endl;
+
+ int fSize;
+ for(fSize = mHeight; fSize>=4; fSize--)
+ {
+ sysFont.setPixelSize ( fSize );
+ sysFontMetrics = new QFontMetrics(sysFont);
+// kdDebug(66666) << "wanted fSize=" << fSize << ", metric h=" << sysFontMetrics->height() << endl;
+ // either found a small enough font or found no proper font at all
+ if ( sysFontMetrics->height() <= mHeight || fSize==4 )
+ {
+// kdDebug(66666) << "stopping @ fSize=" << fSize << ", metric h=" << sysFontMetrics->height() << endl;
+ return;
+ }
+ delete sysFontMetrics;
+ }
+}
+
+QPixmap KJFont::draw(const QCString &str, int wide, const QPoint &pos) const
+{
+ if ( mUseSysFont )
+ return drawSysFont(str,wide,pos);
+ else
+ return drawPixmapFont(str,wide,pos);
+}
+
+QPixmap KJFont::drawSysFont(const QCString &s, int wide, const QPoint &pos) const
+{
+// kdDebug(66666) << k_funcinfo << "BEGIN, s='" << s << "'" << endl;
+ QPoint to(pos);
+ QString string(s);
+
+ int stringWidth = sysFontMetrics->width( string );
+// kdDebug(66666) << "final metrics; w=" << stringWidth << ", h=" << sysFontMetrics->height() << endl;
+
+ QPixmap region(
+ (stringWidth > wide ? stringWidth : wide),
+ mHeight);
+ QPainter rp(&region); // region painter
+
+ QBitmap regionMask(
+ (stringWidth > wide ? stringWidth : wide),
+ mHeight, true); // fully transparent mask
+ QPainter mp(&regionMask); // mask painter
+
+// kdDebug(66666) << "region; w=" << region.width() << ", h=" << region.height() << endl;
+
+ int freeSpace=0;
+ // center string into pixmap if its chars won't fill the whole pixmap
+ if ( stringWidth < wide )
+ {
+ freeSpace = wide - stringWidth;
+ mp.fillRect ( to.x(), 0, (freeSpace/2), mHeight, Qt::color0 );
+ to += QPoint ( (freeSpace/2), 0 );
+// kdDebug(66666) << "centering text, freeSpace=" << freeSpace << endl;
+ }
+
+ rp.setFont(sysFont);
+ rp.setPen(sysFontColor);
+ rp.drawText(to.x(), to.y(), region.width()-freeSpace, mHeight, Qt::AlignLeft|Qt::AlignTop, string);
+
+ mp.setFont(sysFont);
+ mp.setPen(Qt::color1);
+ mp.drawText(to.x(), to.y(), region.width()-freeSpace, mHeight, Qt::AlignLeft|Qt::AlignTop, string);
+
+ to += QPoint(region.width()-freeSpace,0);
+// kdDebug(66666) << "text width=" << region.width()-freeSpace << endl;
+
+ if (freeSpace > 0)
+ {
+ mp.fillRect ( to.x(), 0, (freeSpace/2), mHeight, Qt::color0 );
+ to += QPoint ( (freeSpace/2), 0 );
+// kdDebug(66666) << "centering text, freeSpace=" << freeSpace << endl;
+ }
+
+ region.setMask( regionMask );
+// kdDebug(66666) << "width: " << wide << "|end after drawing: " << to.x() << endl;
+// kdDebug(66666) << k_funcinfo << "END" << endl << endl;
+ return region;
+}
+
+QPixmap KJFont::drawPixmapFont(const QCString &str, int wide, const QPoint &pos) const
+{
+// kdDebug(66666) << k_funcinfo << "BEGIN" << endl;
+ QPoint to(pos);
+
+ QCString string = str.lower();
+ QPixmap region(
+ (string.length()*mWidth+string.length()*mSpacing > (unsigned int)wide
+ ? string.length()*mWidth+string.length()*mSpacing : wide),
+ mHeight);
+
+ QBitmap regionMask(
+ (string.length()*mWidth+string.length()*mSpacing > (unsigned int)wide
+ ? string.length()*mWidth+string.length()*mSpacing : wide),
+ mHeight, true); // fully transparent mask
+ QPainter mask( &regionMask );
+
+// kdDebug(66666) << "draw: {" << str << "}" << endl;
+
+ int freeSpace=0;
+ // center string into pixmap if its chars won't fill the whole pixmap
+ if ( string.length()*mWidth+string.length()*mSpacing < (unsigned int)wide )
+ {
+ freeSpace = wide - string.length()*mWidth+string.length()*mSpacing;
+ mask.fillRect ( to.x(), 0, (freeSpace/2), mHeight, Qt::color0 );
+ to += QPoint ( (freeSpace/2), 0 );
+ }
+
+// kdDebug(66666) << k_funcinfo << "pixmap width=" << region.width() << endl;
+
+ // draw every char and add spacing in between these chars if defined
+ unsigned int stringLength(string.length());
+ for ( unsigned int charPos=0; charPos < stringLength; charPos++ )
+ {
+ char c = string[charPos]; // the character to be drawn next
+
+ drawCharacter(&region, &regionMask, to, c);
+ to += QPoint(mWidth, 0);
+
+ // draw according to "spacing"
+ if ( (charPos < string.length()-1) && mSpacing > 0 )
+ { // make the spacing-area transparent
+ mask.fillRect ( to.x(), 0, mSpacing, mHeight, Qt::color0 );
+ to += QPoint ( mSpacing, 0 );
+ }
+ }
+
+ if (freeSpace > 0)
+ {
+ mask.fillRect ( to.x(), 0, (freeSpace/2), mHeight, Qt::color0 );
+ to += QPoint ( (freeSpace/2), 0 );
+ }
+
+ region.setMask( regionMask );
+// kdDebug(66666) << "width: " << wide << "|end after drawing: " << to.x() << endl;
+ return region;
+}
+
+void KJFont::drawCharacter(QPixmap *dev, QBitmap *devMask, const QPoint &to, char c) const
+{
+ QPoint src=charSource(c);
+ int x=src.x();
+ int y=src.y();
+ int xs=mWidth;
+ int ys=mHeight;
+
+ bitBlt(dev, to, &mText, QRect(x,y,xs,ys), Qt::CopyROP);
+
+ // bitBlt mask for transparency
+ if ( mTransparent )
+ {
+ bitBlt(devMask, to, &mTextMask, QRect(x,y,xs,ys), Qt::OrROP);
+ }
+ else // fill mask
+ {
+ QPainter tempPainter (devMask);
+ tempPainter.fillRect ( to.x(), 0, xs,ys, Qt::color1 );
+ }
+}
+
+// needed for strchr
+#include <string.h>
+
+// searches for top/left coordinate of a given character inside the font-pixmap
+QPoint KJFont::charSource(char c) const
+{
+ for (int i=0; i<3; i++)
+ {
+ const char *pos = strchr(mString[i], c);
+
+ if (!pos) continue;
+ return QPoint(mWidth*((int)(pos-mString[i])), mHeight*i);
+ }
+
+ return charSource(mNullChar);
+}
diff --git a/noatun/modules/kjofol-skin/kjfont.h b/noatun/modules/kjofol-skin/kjfont.h
new file mode 100644
index 00000000..4ea5319b
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjfont.h
@@ -0,0 +1,50 @@
+#ifndef KJFONT_H
+#define KJFONT_H
+
+#include <qstring.h>
+#include <qpixmap.h>
+#include <qbitmap.h>
+#include <qfont.h>
+
+class KJLoader;
+
+class KJFont
+{
+public:
+ KJFont(const QString &prefix, KJLoader *parent);
+ // draw the string str to dev at position pos, within rectangle limit in relation to pos
+ QPixmap draw(const QCString &str, int wide, const QPoint &pt=QPoint(0,0)) const;
+ QPixmap draw(const QString &str, int wide, const QPoint &pt=QPoint(0,0)) const
+ { return draw(QCString(str.latin1()), wide, pt); }
+
+ int fontHeight() const {return mHeight;}
+ int fontWidth() const {return mWidth;}
+ int fontSpacing() const {return mSpacing;}
+ bool isTransparent() const {return mTransparent;}
+
+ // !!! Call if you changed the systemfont !!!
+ void recalcSysFont(void);
+
+protected:
+ QPixmap drawSysFont(const QCString &s, int wide, const QPoint &pos=QPoint(0,0)) const;
+ QPixmap drawPixmapFont(const QCString &, int, const QPoint &pos=QPoint(0,0)) const;
+
+ void drawCharacter(QPixmap *dev, QBitmap *devMask, const QPoint &to, char c) const;
+ QPoint charSource(char c) const;
+
+private:
+ QPixmap mText;
+ QBitmap mTextMask;
+ QRgb mTransparentRGB; // this color will be transparent
+ int mSpacing;
+ int mWidth, mHeight;
+ bool mTransparent; // indicates wether there's transparency
+ const char *mString[3];
+ char mNullChar;
+ QFontMetrics *sysFontMetrics;
+ QFont sysFont;
+ QColor sysFontColor;
+ bool mUseSysFont;
+};
+
+#endif
diff --git a/noatun/modules/kjofol-skin/kjguisettingswidget.ui b/noatun/modules/kjofol-skin/kjguisettingswidget.ui
new file mode 100644
index 00000000..e2b4f784
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjguisettingswidget.ui
@@ -0,0 +1,465 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>KJGuiSettings</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KJGuiSettings</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>690</width>
+ <height>454</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>Layout7</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>ButtonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>Visualization</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>visScope</cstring>
+ </property>
+ <property name="text">
+ <string>Oscillo&amp;scope</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>visAnalyzer</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Analyzer</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>visNone</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;None</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1_3</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>U&amp;pdate every:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>visTimerValue</cstring>
+ </property>
+ </widget>
+ <widget class="KIntNumInput">
+ <property name="name">
+ <cstring>visTimerValue</cstring>
+ </property>
+ <property name="value">
+ <number>30</number>
+ </property>
+ <property name="minValue">
+ <number>20</number>
+ </property>
+ <property name="maxValue">
+ <number>1000</number>
+ </property>
+ <property name="suffix">
+ <string>ms</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>GroupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Pitch</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Lower limit:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>minPitch</cstring>
+ </property>
+ </widget>
+ <widget class="KIntNumInput">
+ <property name="name">
+ <cstring>minPitch</cstring>
+ </property>
+ <property name="value">
+ <number>50</number>
+ </property>
+ <property name="minValue">
+ <number>50</number>
+ </property>
+ <property name="maxValue">
+ <number>98</number>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1_2_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Upper limit:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>maxPitch</cstring>
+ </property>
+ </widget>
+ <widget class="KIntNumInput">
+ <property name="name">
+ <cstring>maxPitch</cstring>
+ </property>
+ <property name="value">
+ <number>200</number>
+ </property>
+ <property name="minValue">
+ <number>102</number>
+ </property>
+ <property name="maxValue">
+ <number>200</number>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>displayTooltips</cstring>
+ </property>
+ <property name="text">
+ <string>Display &amp;tooltips</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>displaySplash</cstring>
+ </property>
+ <property name="text">
+ <string>Display splash sc&amp;reen</string>
+ </property>
+ </widget>
+ <spacer row="5" column="0">
+ <property name="name">
+ <cstring>Spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>60</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget" row="4" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>Layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>T&amp;itle display scrolling speed:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>titleScrollSpeed</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Slow</string>
+ </property>
+ </widget>
+ <widget class="QSlider">
+ <property name="name">
+ <cstring>titleScrollSpeed</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="maxValue">
+ <number>3</number>
+ </property>
+ <property name="pageStep">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>2</number>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="tickmarks">
+ <enum>NoMarks</enum>
+ </property>
+ <property name="tickInterval">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Fast</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox" row="3" column="0">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>System Font</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KColorCombo" row="2" column="1">
+ <property name="name">
+ <cstring>cmbSysFontColor</cstring>
+ </property>
+ <property name="color">
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>txtFontColor</cstring>
+ </property>
+ <property name="text">
+ <string>Color:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>txtFont</cstring>
+ </property>
+ <property name="text">
+ <string>Font:</string>
+ </property>
+ </widget>
+ <widget class="KFontCombo" row="1" column="1">
+ <property name="name">
+ <cstring>cmbSysFont</cstring>
+ </property>
+ <property name="urlDropsEnabled" stdset="0">
+ <bool>false</bool>
+ </property>
+ <property name="editable" stdset="0">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>useSysFont</cstring>
+ </property>
+ <property name="text">
+ <string>Use system font</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer row="3" column="1">
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>140</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<tabstops>
+ <tabstop>visScope</tabstop>
+ <tabstop>visAnalyzer</tabstop>
+ <tabstop>visNone</tabstop>
+ <tabstop>visTimerValue</tabstop>
+ <tabstop>minPitch</tabstop>
+ <tabstop>maxPitch</tabstop>
+ <tabstop>displayTooltips</tabstop>
+ <tabstop>displaySplash</tabstop>
+ <tabstop>useSysFont</tabstop>
+ <tabstop>cmbSysFont</tabstop>
+ <tabstop>cmbSysFontColor</tabstop>
+ <tabstop>titleScrollSpeed</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>kcolorcombo.h</includehint>
+ <includehint>kfontcombo.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/noatun/modules/kjofol-skin/kjloader.cpp b/noatun/modules/kjofol-skin/kjloader.cpp
new file mode 100644
index 00000000..11f96b7a
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjloader.cpp
@@ -0,0 +1,832 @@
+/***************************************************************************
+ kjloader.cpp - The KJfol-GUI itself
+ --------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+// local includes
+#include "kjloader.h"
+#include "kjloader.moc"
+#include "kjwidget.h"
+#include "kjbackground.h"
+#include "kjbutton.h"
+#include "kjfont.h"
+#include "kjseeker.h"
+#include "kjsliders.h"
+#include "kjtextdisplay.h"
+#include "kjvis.h"
+#include "kjprefs.h"
+#include "kjequalizer.h"
+
+#include "helpers.cpp"
+
+// arts-includes, needed for pitch
+#include <artsmodules.h>
+#include <reference.h>
+#include <soundserver.h>
+#include <kmedia2.h>
+
+// noatun-specific includes
+#include <noatun/engine.h>
+#include <noatunarts/noatunarts.h>
+#include <noatun/stdaction.h>
+#include <noatun/app.h>
+#include <noatun/player.h>
+#include <noatun/vequalizer.h>
+
+// system includes
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <qdragobject.h>
+#include <qimage.h>
+#include <qbitmap.h>
+#include <qpixmap.h>
+#include <qcursor.h>
+#include <qpainter.h>
+#include <qtooltip.h>
+#include <qptrvector.h>
+#include <qvbox.h>
+#include <qlabel.h>
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <kfiledialog.h>
+#include <khelpmenu.h>
+#include <kstdaction.h>
+#include <kpopupmenu.h>
+#include <klocale.h>
+#include <kglobalsettings.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <knotifyclient.h>
+#include <kpixmapeffect.h>
+#include <kurldrag.h>
+
+#include <kwin.h>
+#include <kiconloader.h>
+
+class KJToolTip : public QToolTip
+{
+public:
+ KJToolTip(KJLoader *parent)
+ : QToolTip(parent), mParent(parent)
+ {}
+
+protected:
+ virtual void maybeTip(const QPoint &p)
+ {
+ if ( !mParent->prefs()->displayTooltips() )
+ return;
+
+ QPtrList<KJWidget> things=mParent->widgetsAt(p);
+ for (KJWidget *i=things.first(); i!=0; i=things.next())
+ {
+ QString string=i->tip();
+ if (string.length())
+ {
+ tip(i->rect(), string);
+ return;
+ }
+ }
+ }
+
+private:
+ KJLoader *mParent;
+};
+
+
+KJLoader *KJLoader::kjofol=0;
+
+
+KJLoader::KJLoader()
+ : QWidget(0, "NoatunKJLoader",
+ WType_TopLevel | WStyle_NoBorder | WRepaintNoErase ),
+ UserInterface(),
+ moving(false),
+ mClickedIn(0),
+ mText(0),
+ mNumbers(0),
+ mVolumeFont(0),
+ mPitchFont(0),
+ splashScreen(0)
+{
+ kjofol = this;
+
+ mTooltips = new KJToolTip(this);
+
+ // Windowname and Icon
+ setCaption(i18n("Noatun"));
+ setIcon(SmallIcon("noatun"));
+ setAcceptDrops(true);
+
+ // We're going to draw over everything, there is no point in drawing the grey background first
+ setBackgroundMode(NoBackground);
+
+ // used for dockmode
+ mWin = new KWinModule();
+
+ subwidgets.setAutoDelete(true);
+
+ mPrefs = new KJPrefs(this);
+ connect ( mPrefs, SIGNAL(configChanged()), this, SLOT(readConfig()) );
+
+ QString skin = mPrefs->skin();
+ if ( QFile(skin).exists() )
+ {
+ loadSkin(skin);
+ }
+ else
+ {
+ KNotifyClient::event(winId(), "warning",
+ i18n("There was trouble loading skin %1. Please select another skin file.").arg(skin));
+ napp->preferences();
+ }
+
+ mHelpMenu = new KHelpMenu(this, kapp->aboutData());
+ connect(napp->player(), SIGNAL(timeout()), SLOT(timeUpdate()));
+ connect(napp->player(), SIGNAL(stopped()), SLOT(timeUpdate()));
+ connect(napp->player(), SIGNAL(newSong()), SLOT(newSong()));
+
+ connect(napp, SIGNAL(hideYourself()), SLOT(hide()));
+ connect(napp, SIGNAL(showYourself()), SLOT(show()));
+// KStdAction::quit(napp, SLOT(quit()), actionCollection());
+
+ QApplication::restoreOverrideCursor();
+// newSong();
+}
+
+QPtrList<KJWidget> KJLoader::widgetsAt(const QPoint &pt) const
+{
+ QPtrList<KJWidget> things;
+ for ( QPtrListIterator<KJWidget> i(subwidgets); i.current(); ++i )
+ if ( (*i)->rect().contains(pt) )
+ things.append((*i));
+
+ return things;
+}
+
+void KJLoader::removeChild(KJWidget *c)
+{
+ if ( mClickedIn == c )
+ mClickedIn = 0;
+ if (subwidgets.findRef(c) != -1)
+ subwidgets.take();
+}
+
+void KJLoader::addChild(KJWidget *c)
+{
+ subwidgets.append(c);
+}
+
+// The BIG one ;)
+// this methode does all the hard work on loading these weird skins
+void KJLoader::loadSkin(const QString &file)
+{
+// kdDebug(66666) << "<KJLoader::loadSkin(const QString &file)>" << endl;
+// kdDebug(66666) << " file = " << file.latin1() << endl;
+
+ if ( file == mCurrentSkin ) // we don't load the same skin again
+ return;
+
+ mCurrentSkin = file;
+
+ // don't overwrite path to *.rc when we are loading dock- or winshade-mode
+ if ( (file != mCurrentWinshadeModeSkin) && (file != mCurrentDockModeSkin) )
+ {
+ mCurrentDefaultSkin = file;
+// kdDebug(66666) << " setting mCurrentDefaultSkin: '" << file.latin1() << "'" << endl;
+ }
+ unloadSkin();
+
+ Parser::open( filenameNoCase(file) );
+
+ KJPitchText *pitchText=0;
+ KJVolumeText *volumeText=0;
+ mText = 0;
+ mNumbers = 0;
+ mVolumeFont = 0;
+ mPitchFont = 0;
+
+ if ( exist("splashscreen") && mPrefs->displaySplash() )
+ showSplash();
+
+ if ( (file != mCurrentWinshadeModeSkin) && (file != mCurrentDockModeSkin) )
+ {
+ if ( exist("dockmodercfile") )
+ {
+ // set path to dockmode rc-file (its not always skinname.dck)
+ mCurrentDockModeSkin = file.left(file.findRev("/")+1) + (item("dockmodercfile")[1]);
+ mDockPosition = item("dockmodeposition")[1].toInt();
+ mDockPositionX = item("dockmodepositionxy")[1].toInt();
+ mDockPositionY = item("dockmodepositionxy")[2].toInt();
+ }
+ else // NO DockMode
+ mCurrentDockModeSkin="";
+
+ if ( exist("winshademodercfile") )
+ mCurrentWinshadeModeSkin = file.left(file.findRev("/")+1) + (item("winshademodercfile")[1]);
+ else // no WinshadeMode
+ mCurrentWinshadeModeSkin="";
+ }
+
+ // Font-loading
+ if ( exist("fontimage") )
+ mText = new KJFont("font", this);
+
+ if ( exist("timefontimage") )
+ mNumbers = new KJFont("timefont", this);
+
+ if (exist("volumefontimage"))
+ mVolumeFont = new KJFont("volumefont", this);
+
+ // our skin-background, There has to be one so no check with exist()
+ subwidgets.append( new KJBackground(this) );
+
+ if ( exist("pitchtext") )
+ {
+ if (exist("pitchfontimage"))
+ {
+ mPitchFont = new KJFont("pitchfont", this);
+ }
+ else
+ {
+ mPitchFont = mNumbers;
+ kdDebug(66666) << "no pitchfont but pitchtext!" << endl;
+ kdDebug(66666) << "replacing pitchfont with timefont" << endl;
+ }
+ subwidgets.append( pitchText=new KJPitchText(item("pitchtext"), this) );
+ }
+
+ if (exist("volumetext"))
+ subwidgets.append(volumeText=new KJVolumeText(item("volumetext"), this));
+
+ if ( exist("volumecontroltype") )
+ {
+ if ( item("volumecontroltype")[1] == "bmp" )
+ {
+ KJVolumeBMP *b;
+ subwidgets.append(b=new KJVolumeBMP(item("volumecontrolbutton"), this));
+ b->setText(volumeText);
+ }
+ else if ( item("volumecontroltype")[1] == "bar" )
+ {
+ KJVolumeBar *b;
+ subwidgets.append(b=new KJVolumeBar(item("volumecontrolbutton"), this));
+ b->setText(volumeText);
+ }
+/* else
+ {
+ kdDebug(66666) << "unknown volumecontrol: " << item("volumecontroltype")[1].latin1() << endl;
+ } */
+ }
+ else
+ {
+ kdDebug(66666) << "guessing type of volumecontrol" << endl;
+ if (exist("volumecontrolbutton") &&
+ exist("volumecontrolimage") &&
+ exist("volumecontrolimagexsize") &&
+ exist("volumecontrolimageposition") &&
+ exist("volumecontrolimagenb") )
+ {
+ KJVolumeBMP *b;
+ subwidgets.append(b=new KJVolumeBMP(item("volumecontrolbutton"), this));
+ b->setText(volumeText);
+ }
+ else if (exist("volumecontrolimage") &&
+ exist("volumecontrolbutton") )
+ {
+ KJVolumeBar *b;
+ subwidgets.append(b=new KJVolumeBar(item("volumecontrolbutton"), this));
+ b->setText(volumeText);
+ }
+/* else
+ {
+ kdDebug(66666) << " no volumecontrol" << endl;
+ } */
+ }
+
+ if (exist("pitchcontrolbutton") &&
+ exist("pitchcontrolimage") &&
+ exist("pitchcontrolimagexsize") &&
+ exist("pitchcontrolimageposition") &&
+ exist("pitchcontrolimagenb") )
+ {
+// kdDebug(66666) << "added KJPitchBMP" << endl;
+ KJPitchBMP *b;
+ subwidgets.append(b=new KJPitchBMP(item("pitchcontrolbutton"), this));
+ b->setText(pitchText);
+ }
+ else
+ {
+ // make sure we reset speed to 100% as the user won't be able
+ // to reset it without a pitchcontrol
+ Arts::PlayObject playobject = napp->player()->engine()->playObject();
+ Arts::PitchablePlayObject pitchable = Arts::DynamicCast(playobject);
+
+ if ( !pitchable.isNull() )
+ {
+ if ( pitchable.speed() > 1.0f )
+ pitchable.speed(1.0f);
+ }
+ }
+
+ if (exist("filenamewindow"))
+ subwidgets.append(new KJFilename(item("filenamewindow"), this));
+
+ if (exist("mp3timewindow"))
+ subwidgets.append(new KJTime(item("mp3timewindow"), this));
+
+ if (exist("mp3kbpswindow"))
+ subwidgets.append(new KJFileInfo(item("mp3kbpswindow"), this));
+
+ if (exist("mp3khzwindow"))
+ subwidgets.append(new KJFileInfo(item("mp3khzwindow"), this));
+
+ if (exist("analyzerwindow"))
+ {
+ int vistype = mPrefs->visType();
+ switch ( vistype )
+ {
+ case KJVisScope::Null:
+ subwidgets.append(new KJNullScope(item("analyzerwindow"), this));
+ break;
+ case KJVisScope::FFT:
+ subwidgets.append(new KJFFT(item("analyzerwindow"), this));
+ break;
+ case KJVisScope::StereoFFT:
+ subwidgets.append(new KJStereoFFT(item("analyzerwindow"), this));
+ break;
+ case KJVisScope::Mono:
+ subwidgets.append(new KJScope(item("analyzerwindow"), this));
+ break;
+ }
+ }
+
+ if (exist("EqualizerWindow"))
+ subwidgets.append(new KJEqualizer(item("EqualizerWindow"), this));
+
+ // I cant believe it, there are skins without a seeker, now THATS stupid :)
+ if (exist("seekregion"))
+ QTimer::singleShot(0, this, SLOT(loadSeeker()));
+
+ // all the regular buttons
+ for (QDictIterator<QStringList> i(*this); i.current(); ++i)
+ {
+ QString d=i.currentKey();
+ if(d.contains("button") &&
+ !d.startsWith("playlistwindow") && // don't add buttons that belong to the playlistwindow
+ d != "pitchcontrolbutton" && // both already handled above as they aren't buttons but sliders
+ d != "volumecontrolbutton" &&
+ d != "spectrumanalyzerbutton" && // FIXME: unsupported button
+ d != "oscilloscopebutton" && // FIXME: unsupported button
+ i.count() >= 7 )
+ {
+ subwidgets.append(new KJButton(*(*i), this));
+ }
+ }
+
+ show();
+ conserveMemory();
+
+ repaint();
+
+ // update displays if we are already playing
+ // This happens while changing skins
+ if (napp->player()->isPlaying())
+ newSong();
+
+// kdDebug(66666) << "</KJLoader::loadSkin(const QString &file)>" << endl;
+}
+
+void KJLoader::loadSeeker()
+{
+ subwidgets.append(new KJSeeker(item("seekregion"), this));
+}
+
+void KJLoader::unloadSkin()
+{
+// kdDebug(66666) << "<KJLoader::unloadSkin()>" << endl;
+
+ KWin::clearState(winId(), NET::SkipTaskbar);
+
+// kdDebug(66666) << " freeing subwidgets" << endl;
+ subwidgets.clear();
+
+ // This is special because mPitchfont can also point to mNumbers
+ // as some skins use the NumberFont for pitchtext
+ if ( mPitchFont && mPitchFont != mNumbers )
+ {
+// kdDebug(66666) << " freeing mPitchFont" << endl;
+ delete mPitchFont;
+ }
+
+ if ( mText )
+ {
+// kdDebug(66666) << " freeing mText" << endl;
+ delete mText;
+ }
+
+ if ( mNumbers )
+ {
+// kdDebug(66666) << " freeing mNumbers" << endl;
+ delete mNumbers;
+ }
+
+ if ( mVolumeFont )
+ {
+// kdDebug(66666) << " freeing mVolumeFont" << endl;
+ delete mVolumeFont;
+ }
+
+// kdDebug(66666) << "</KJLoader::unloadSkin()>" << endl;
+}
+
+void KJLoader::minimize()
+{
+// kdDebug(66666) << "KJLoader::minimize()" << endl;
+ showMinimized();
+}
+
+void KJLoader::closeEvent(QCloseEvent*)
+{
+// kdDebug(66666) << "KJLoader::closeEvent(QCloseEvent*)" << endl;
+ unload();
+}
+
+void KJLoader::dragEnterEvent(QDragEnterEvent *event)
+{
+ // accept uri drops only
+ event->accept(KURLDrag::canDecode(event));
+}
+
+void KJLoader::dropEvent(QDropEvent *event)
+{
+ KURL::List urls;
+ if ( KURLDrag::decode(event,urls) )
+ {
+ for ( KURL::List::iterator it = urls.begin(); it != urls.end(); ++it )
+ napp->player()->openFile((*it), false);
+ }
+}
+
+void KJLoader::wheelEvent(QWheelEvent *e)
+{ // from QT-Docu: delta() is 120 for one step
+ if (e->state() & ControlButton)
+ napp->player()->setVolume ( napp->player()->volume() + (e->delta()/8) ); // 15% volumechange
+ else
+ napp->player()->setVolume ( napp->player()->volume() + (e->delta()/24) ); // 5% volumechange
+}
+
+// now for some dockmode stuff
+void KJLoader::switchToDockmode()
+{
+// kdDebug(66666) << "KJLoader::switchToDockmode()" << endl;
+ loadSkin( mCurrentDockModeSkin );
+
+ connect(mWin, SIGNAL(activeWindowChanged(WId)), this, SLOT(slotWindowActivate(WId)));
+ connect(mWin, SIGNAL(windowRemoved(WId)), this, SLOT(slotWindowRemove(WId)));
+ connect(mWin, SIGNAL(stackingOrderChanged()), this, SLOT(slotStackingChanged()));
+ connect(mWin, SIGNAL(windowChanged(WId)), this, SLOT(slotWindowChange(WId)));
+ connect(mWin, SIGNAL(currentDesktopChanged(int)), this, SLOT(slotDesktopChange(int)));
+
+ WId activeWin = mWin->activeWindow();
+ if (activeWin && (activeWin != winId()))
+ {
+ KWin::WindowInfo winInf = KWin::windowInfo(activeWin, NET::WMKDEFrameStrut);
+ if(winInf.valid())
+ {
+ mDockToWin = activeWin;
+ mDockWindowRect = winInf.frameGeometry();
+ slotWindowActivate(mDockToWin);
+ hide();
+ restack();
+ }
+ }
+}
+
+void KJLoader::returnFromDockmode()
+{
+// kdDebug(66666) << "KJLoader::returnFromDockmode()" << endl;
+ mWin->disconnect();
+ loadSkin(mCurrentDefaultSkin);
+}
+
+void KJLoader::slotWindowActivate(WId win)
+{
+ if(mCurrentSkin != mCurrentDockModeSkin)
+ return;
+
+ KWin::WindowInfo winInf = KWin::windowInfo(
+ win, NET::WMWindowType);
+ if((win != winId()) && winInf.valid())
+ {
+ // ensure we dock to the active window _except_ our own
+ // and stick to the last window if the NEW current one is a desktop
+ NET::WindowType winType = winInf.windowType(
+ NET::NormalMask|NET::DesktopMask|NET::DockMask|
+ NET::ToolbarMask|NET::MenuMask|NET::DialogMask|
+ NET::OverrideMask|NET::TopMenuMask|NET::UtilityMask|
+ NET::SplashMask);
+
+ if(winType == NET::Unknown || winType == NET::Normal || winType == NET::Dialog)
+ {
+ //kdDebug(66666) << k_funcinfo << "Now docking to window: " << win << endl;
+ mDockToWin = win;
+ }
+
+ }
+
+ if(mDockToWin != 0)
+ {
+ mDockWindowRect = KWin::windowInfo(mDockToWin, NET::WMKDEFrameStrut).frameGeometry();
+ /*kdDebug(66666) << k_funcinfo << "winrect: " << mDockWindowRect.x() << ", " <<
+ mDockWindowRect.y() << endl;*/
+ switch ( mDockPosition )
+ {
+ case 0:
+ move( mDockWindowRect.x() + mDockPositionX, mDockWindowRect.y() + mDockPositionY );
+ break;
+ case 2:
+ move( mDockWindowRect.x() + mDockPositionX, mDockWindowRect.y() + mDockWindowRect.height() + mDockPositionY );
+ break;
+ }
+
+ if(!isVisible())
+ {
+ show();
+ KWin::setState(winId(), NET::SkipTaskbar);
+ }
+ restack();
+ }
+ else
+ {
+ // We don't want to do anything until a window comes into
+ // focus.
+ //kdDebug(66666) << "No window having focus, hiding" << endl;
+ hide();
+ }
+
+// kdDebug(66666) << "END slotWindowActivate()" << endl;
+}
+
+void KJLoader::slotWindowRemove(WId win)
+{
+// kdDebug(66666) << "START slotWindowRemove()" << endl;
+ if ( mCurrentSkin != mCurrentDockModeSkin )
+ return;
+
+ if (win == mDockToWin)
+ {
+// kdDebug(66666) << "our window removed: " << win << endl;
+ hide();
+ mDockToWin = 0;
+ }
+// kdDebug(66666) << "END slotWindowRemove()" << endl;
+}
+
+void KJLoader::slotWindowChange(WId win)
+{
+// kdDebug(66666) << "START slotWindowChange()" << endl;
+ if ( mCurrentSkin != mCurrentDockModeSkin )
+ return;
+
+ if ( win == mDockToWin )
+ {
+// kdDebug(66666) << "changed our window:" << win << endl;
+ KWin::WindowInfo winInf = KWin::windowInfo(
+ mDockToWin, NET::WMKDEFrameStrut|NET::WMWindowType|
+ NET::WMState|NET::XAWMState|NET::WMDesktop);
+
+ if(!winInf.valid())
+ {
+ /*kdDebug(66666) << k_funcinfo <<
+ "No valid WindowInfo for tracked window: " << win << endl;*/
+ hide();
+ mDockToWin = 0;
+ return;
+ }
+
+ NET::WindowType winType = winInf.windowType(
+ NET::NormalMask|NET::DesktopMask|NET::DockMask|
+ NET::ToolbarMask|NET::MenuMask|NET::DialogMask|
+ NET::OverrideMask|NET::TopMenuMask|NET::UtilityMask|
+ NET::SplashMask);
+
+ if (
+ (winInf.state() & NET::Hidden) ||
+ (winInf.state() & NET::FullScreen) ||
+ (winType != NET::Unknown && winType != NET::Normal && winType != NET::Dialog)
+ )
+ {
+ /*kdDebug(66666) << k_funcinfo <<
+ "Our window changed: " << win <<
+ ". Either iconified or special window" << endl;*/
+ // target-window has been iconified or window is desktop
+ hide();
+ mDockToWin = 0;
+ return;
+ }
+
+ // Size or position of target-window changed.
+ mDockWindowRect = winInf.frameGeometry();
+ /*kdDebug(66666) << k_funcinfo << "winrect: " << mDockWindowRect.x() << ", " <<
+ mDockWindowRect.y() << endl;*/
+ // Ensure we are still on the window.
+ switch(mDockPosition)
+ {
+ case 0:
+ {
+ move(
+ mDockWindowRect.x() + mDockPositionX,
+ mDockWindowRect.y() + mDockPositionY);
+ break;
+ }
+ case 2:
+ {
+ move(
+ mDockWindowRect.x() + mDockPositionX,
+ mDockWindowRect.y() + mDockWindowRect.height() + mDockPositionY);
+ break;
+ }
+ }
+ restack();
+ }
+}
+
+void KJLoader::slotDesktopChange(int)
+{
+// kdDebug(66666) << "START slotDesktopChange()" << endl;
+ if ( mCurrentSkin != mCurrentDockModeSkin )
+ return;
+
+ hide();
+ mDockToWin = 0L;
+// kdDebug(66666) << "END slotDesktopChange()" << endl;
+}
+
+void KJLoader::slotStackingChanged()
+{
+// kdDebug(66666) << "START slotStackingChanged()" << endl;
+ if ( mCurrentSkin != mCurrentDockModeSkin )
+ return;
+
+ // We seem to get this signal before the window has been restacked,
+ // so we just schedule a restack.
+ QTimer::singleShot ( 10, this, SLOT(restack()) );
+
+// kdDebug(66666) << "END slotStackingChanged()" << endl;
+}
+
+// Set the animation's stacking order to be just above the target window's
+// window decoration, or on top.
+void KJLoader::restack()
+{
+// kdDebug(66666) << "START restack()" << endl;
+
+ if ( !mDockToWin )
+ {
+// kdDebug(66666) << "No window to dock to, no restacking" << endl;
+ hide();
+ return;
+ }
+
+ // simply raise ourselves to the top
+ raise();
+ // and then ensure our target-window gets focus
+// NET::setActiveWindow (mDockToWin);
+
+// kdDebug(66666) << "END restack()" << endl;
+}
+
+KJLoader::~KJLoader()
+{
+// kdDebug(66666) << "KJLoader::~KJLoader()" << endl;
+ delete mWin;
+}
+
+void KJLoader::paintEvent(QPaintEvent *e)
+{
+ QPainter p(this);
+ for (KJWidget* i=subwidgets.first(); i!=0; i=subwidgets.next())
+ if (i->rect().intersects(e->rect()))
+ i->paint(&p, e->rect().intersect(i->rect()));
+// QWidget::paintEvent(e);
+}
+
+void KJLoader::mouseMoveEvent(QMouseEvent *e)
+{
+ if (moving)
+ {
+ move ( QCursor::pos()-mMousePoint );
+ return;
+ }
+
+
+// QWidget::mouseMoveEvent(e);
+ // not on background but on a widget: pass event to subwidget
+ if ( !moving && mClickedIn && subwidgets.findRef(mClickedIn) != -1 )
+ {
+ mClickedIn->mouseMove (
+ e->pos()-mClickedIn->rect().topLeft(),
+ mClickedIn->rect().contains(mapFromGlobal(QCursor::pos())) );
+ }
+}
+
+void KJLoader::mousePressEvent(QMouseEvent *e)
+{
+// kdDebug(66666) << "KJLoader::mousePressEvent(QMouseEvent *e)" << endl;
+
+// QWidget::mousePressEvent(e);
+
+ if ( e->button()==RightButton )
+ NoatunStdAction::ContextMenu::showContextMenu();
+ else /* if ( e->button()==LeftButton ) */
+ {
+ mMousePoint = mapFromGlobal(QCursor::pos());
+ // try to find a KJWidget that is here
+ for (KJWidget* i=subwidgets.first(); i!=0; i=subwidgets.next())
+ if (i->rect().contains(mMousePoint))
+ {
+ if (i->mousePress(mMousePoint-i->rect().topLeft()))
+ {
+ mClickedIn=i;
+ return;
+ }
+ }
+ // can't find a widget, so move the window
+ if ( mCurrentSkin != mCurrentDockModeSkin)
+ moving = true;
+ }
+}
+
+void KJLoader::mouseReleaseEvent(QMouseEvent */*e*/)
+{
+// kdDebug(66666) << "KJLoader::mouseReleaseEvent(QMouseEvent *e)" << endl;
+
+// QWidget::mouseReleaseEvent(e);
+
+ if (!moving && mClickedIn && subwidgets.findRef(mClickedIn)!=-1)
+ {
+ mClickedIn->mouseRelease(mapFromGlobal(QCursor::pos())-
+ mClickedIn->rect().topLeft(),
+ mClickedIn->rect().contains(
+ mapFromGlobal(QCursor::pos())));
+ mClickedIn=0;
+ }
+
+ moving = false;
+}
+
+void KJLoader::timeUpdate()
+{
+ for (KJWidget* widget=subwidgets.first(); widget; widget=subwidgets.next())
+ widget->timeUpdate(napp->player()->getTime()/1000); // pass seconds to all Widgets
+}
+
+void KJLoader::newSong()
+{
+ if (!napp->player()->current())
+ return;
+ for ( KJWidget* i=subwidgets.first(); i!=0; i=subwidgets.next() )
+ i->newFile();
+}
+
+void KJLoader::readConfig()
+{
+// kdDebug(66666) << "KJLoader::readConfig()" << endl;
+ for (KJWidget* i=subwidgets.first(); i!=0; i=subwidgets.next())
+ i->readConfig();
+}
+
+void KJLoader::showSplash()
+{
+ splashScreen = new QLabel( 0L, "SplashScreen",
+ WType_TopLevel | WStyle_NoBorder | WRepaintNoErase | WX11BypassWM );
+
+ QPixmap splashPix = pixmap(item("splashscreen")[1]);
+ splashScreen->setPixmap( splashPix );
+ splashScreen->setBackgroundMode ( NoBackground );
+ splashScreen->setMask( KJWidget::getMask(image(item("splashscreen")[1])) );
+
+ QSize sh = splashScreen->sizeHint();
+
+ QRect desk = KGlobalSettings::splashScreenDesktopGeometry();
+ splashScreen->move (desk.x() + (desk.width() - sh.width())/2,
+ desk.y() + (desk.height() - sh.height())/2 );
+
+ splashScreen->setFixedSize(sh);
+ splashScreen->show();
+ napp->processEvents(); // we want this one time to get the splash actually displayed ASAP
+
+ QTimer::singleShot(3000, this, SLOT(hideSplash()) );
+}
+
+void KJLoader::hideSplash()
+{
+ splashScreen->hide();
+ delete splashScreen;
+}
diff --git a/noatun/modules/kjofol-skin/kjloader.h b/noatun/modules/kjofol-skin/kjloader.h
new file mode 100644
index 00000000..44b507d6
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjloader.h
@@ -0,0 +1,129 @@
+#ifndef KJLOADER_H
+#define KJLOADER_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+// local includes
+#include "parser.h"
+
+// noatun-specific includes
+#include <noatun/plugin.h>
+#include <noatun/app.h>
+
+// system includes
+#include <qwidget.h>
+#include <qbitmap.h>
+#include <qptrlist.h>
+#include <qcstring.h>
+
+#include <kwinmodule.h>
+
+class QLabel;
+class KJWidget;
+class KHelpMenu;
+class KJSeeker;
+class NoatunPreferences;
+class KJToolTip;
+class KJFont;
+class KJPrefs;
+
+
+class KJLoader : public QWidget, public UserInterface, public Parser
+{
+Q_OBJECT
+NOATUNPLUGIND
+
+ friend class KJWidget;
+public:
+ KJLoader();
+ ~KJLoader();
+
+public:
+ void minimize();
+ KHelpMenu *helpMenu() const { return mHelpMenu; }
+ QStringList &item(const QString &key) { return *Parser::find(key); }
+
+ // returns path to currently loaded configfile
+ // can be either a newly loaded one or one of the three below
+ QString currentSkin() { return mCurrentSkin; }
+
+ // returns path to mainskin-configfile
+ QString currentDefaultSkin() { return mCurrentDefaultSkin; }
+
+ //returns path to dockmode-configfile if present
+ QString currentDockModeSkin() { return mCurrentDockModeSkin; }
+
+ //returns path to winshademode-configfile if present (not supported yet)
+ QString currentWinshadeModeSkin() { return mCurrentWinshadeModeSkin; }
+
+ KJPrefs *prefs() const { return mPrefs; }
+
+ QPtrList<KJWidget> widgetsAt(const QPoint &pt) const;
+
+ void removeChild(KJWidget *c);
+ void addChild(KJWidget *c);
+
+public slots:
+ void loadSkin(const QString &file);
+ void readConfig();
+ void switchToDockmode();
+ void returnFromDockmode();
+
+protected:
+ void unloadSkin();
+ void showSplash();
+
+public slots:
+ void timeUpdate();
+ void newSong();
+
+private slots:
+ void loadSeeker();
+ void slotWindowActivate(WId win);
+ void slotWindowRemove(WId win);
+ void slotWindowChange(WId win);
+ void slotDesktopChange(int);
+ void slotStackingChanged();
+ void restack();
+ void hideSplash();
+
+protected:
+ virtual void mouseMoveEvent(QMouseEvent *e);
+ virtual void mousePressEvent(QMouseEvent *e);
+ virtual void mouseReleaseEvent(QMouseEvent *e);
+ virtual void paintEvent(QPaintEvent *e);
+ virtual void closeEvent(QCloseEvent*e);
+ virtual void wheelEvent(QWheelEvent *e);
+
+ virtual void dragEnterEvent(QDragEnterEvent *event);
+ virtual void dropEvent(QDropEvent *event);
+
+public:
+ static KJLoader* kjofol;
+
+private:
+ // ==== docking stuff ====
+ KWinModule *mWin;
+ WId mDockToWin;
+ int mDockPositionX, mDockPositionY, mDockPosition;
+ QRect mDockWindowRect;
+ // ==== end of docking stuff ====
+ bool moving;
+ QPoint mMousePoint;
+ QPtrList<KJWidget> subwidgets;
+ KJWidget *mClickedIn;
+ KHelpMenu *mHelpMenu;
+ KJFont *mText, *mNumbers, *mVolumeFont, *mPitchFont;
+ QLabel *splashScreen;
+ KJToolTip *mTooltips;
+ QString mCurrentSkin;
+ QString mCurrentDefaultSkin;
+ QString mCurrentDockModeSkin;
+ QString mCurrentWinshadeModeSkin;
+
+ KJPrefs *mPrefs;
+};
+
+#endif // KJLOADER_H
diff --git a/noatun/modules/kjofol-skin/kjofolui.plugin b/noatun/modules/kjofol-skin/kjofolui.plugin
new file mode 100644
index 00000000..f6037cdb
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjofolui.plugin
@@ -0,0 +1,64 @@
+Filename=noatun_kjofol.la
+Author=Charles Samuels, Stefan Gehn
+Site=http://noatun.kde.org/
+Email=charles@kde.org
+Type=userinterface
+License=Artistic
+Name=K-Jöfol
+Name[eo]=K-Jofol
+Name[hi]=के-जॉफ़ॉल
+Name[is]=K-Jofol
+Name[ne]=के-जोफोल
+Name[pt_BR]=K-Jofol
+Name[ta]=K-Jofol
+Name[xh]=K-Jofol
+Comment=Skin loader for K-Jofol skins
+Comment[bg]=Зареждане на теми за K-Jofol
+Comment[br]=Ur c'harger a groc'hen evit K-Jofol
+Comment[bs]=Skin Loader za K-Jofol skinove
+Comment[ca]=Carregador d'aparences per les aparences K-Jofol
+Comment[cs]=Nahrávání motivů pro motivy K-Jofolu
+Comment[cy]=Llwythydd croen ar gyfer crwyn K-Jofol
+Comment[da]=Forsideindlæser for K-Jöfol-forsider
+Comment[de]=Importprogramm für K-Jöfol-Designs
+Comment[el]=Φόρτωση θέματος για θέματα K-Jofol
+Comment[eo]=Ŝargilo por K-Jofol-etosoj
+Comment[es]=Cargador de pieles de K-Jofol
+Comment[et]=K-Jöfoli rüüde laadija
+Comment[eu]=Azal kargatzailea K-Jöfol azalentzat
+Comment[fa]=بارکننده Skin برای K-Jofol skins
+Comment[fi]=K-Jofol-nahkojen latausohjelma
+Comment[fr]=Chargeur de revêtements K-Jöfol
+Comment[gl]=Cargador de peles para as peles de K-Jofol
+Comment[he]=טוען Skins של K-Jofol
+Comment[hu]=Betöltőprogram a K-Jofol-os kinézetekhez
+Comment[is]=Hleður inn K-Jofol skinn
+Comment[it]=Caricatore di skin per K-Jöfol
+Comment[ja]=K-Jöfol スキンのローダ
+Comment[kk]=K-Jofol тыстарының жүктегіші
+Comment[km]=កម្មវិធី​ផ្ទុក​ស្បែក K-Jofol
+Comment[ko]=K-Jofol 스킨 로더
+Comment[lt]=K-Jofol apvalkalų pakrovėjas
+Comment[mk]=Вчитувач на маски за маски K-Jofol
+Comment[nb]=Drakthenter for K-Jöfol-drakt
+Comment[nds]=Böversietlader för "K-Jöfol"-Böversieden
+Comment[ne]=के-जोफोल स्किनका लागि स्किन लोडर
+Comment[nl]=Skinlader voor K-Jofol-skins
+Comment[nn]=Skal-lastar for K-Jofol-skal
+Comment[pl]=Ładowarka skór dla skór K-Jofol
+Comment[pt]=Leitor de aspectos do K-Jofol
+Comment[pt_BR]=Carregador de aparências (skins) para o K-Jofol
+Comment[ro]=Încărcător de interfeţe pentru tematici K-Jofol
+Comment[ru]=Загрузчик образов K-Jofol
+Comment[sk]=Nahrávanie tém K-Jofol
+Comment[sl]=Nalagalnik za preobleke K-Jofol
+Comment[sr]=Учитавач кошуљица за К-Jofol кошуљице
+Comment[sr@Latn]=Učitavač košuljica za K-Jofol košuljice
+Comment[sv]=Skalladdare för K-Jofol-skal
+Comment[ta]=கே-ஜோபோல் அலங்கார அமைப்புக்கான ஏற்றி
+Comment[th]=ตัวโหลดหน้ากากสำหรับ K-Jofol
+Comment[tr]=K-Jofol arayüzleri için yükleyici
+Comment[uk]=Завантажувач жупанів для K-Jofol
+Comment[zh_CN]=K-Jofol 外观载入器
+Comment[zh_HK]=用於 K-Jofol 外貌的外貌載入器
+Comment[zh_TW]= K-Jofol 面板載入器
diff --git a/noatun/modules/kjofol-skin/kjprefs.cpp b/noatun/modules/kjofol-skin/kjprefs.cpp
new file mode 100644
index 00000000..0cadc5ac
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjprefs.cpp
@@ -0,0 +1,658 @@
+/***************************************************************************
+ kjprefs.cpp - Preferences-Dialog for KJ�ol-Skinloader
+ --------------------------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+// local includes
+#include "kjprefs.h"
+#include "kjprefs.moc"
+#include "kjloader.h"
+#include "kjwidget.h"
+#include "kjvis.h"
+#include "parser.h"
+
+// system includes
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qradiobutton.h>
+#include <qslider.h>
+#include <qpixmap.h>
+#include <qtabwidget.h>
+#include <qtextbrowser.h>
+#include <qfileinfo.h>
+#include <qstringlist.h>
+#include <qregexp.h>
+
+#include <knuminput.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kio/job.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kmimemagic.h>
+#include <knotifyclient.h>
+#include <kprocess.h>
+#include <kstandarddirs.h>
+#include <kglobalsettings.h>
+#include <kfontcombo.h>
+#include <kcolorcombo.h>
+
+static QString expand(QString s);
+
+KJPrefs::KJPrefs(QObject* parent)
+ : CModule(i18n("K-Jöfol Skins"), i18n("Skin Selection For the K-Jöfol Plugin"), "style", parent)
+{
+ cfg = KGlobal::config();
+
+ QVBoxLayout *vbox = new QVBoxLayout(this);
+ vbox->setAutoAdd(true);
+ vbox->setSpacing( 0 );
+ vbox->setMargin( 0 );
+
+ mTabWidget = new QTabWidget( this, "mTabWidget" );
+
+ mSkinselectorWidget = new KJSkinselector ( mTabWidget, "mSkinselectorWidget" );
+ mGuiSettingsWidget = new KJGuiSettings ( mTabWidget, "mGuiSettingsWidget" );
+
+ mTabWidget->insertTab( mSkinselectorWidget, i18n("&Skin Selector") );
+ mTabWidget->insertTab( mGuiSettingsWidget, i18n("O&ther Settings") );
+
+ connect ( mSkinselectorWidget->mSkins, SIGNAL(activated(const QString&)), SLOT(showPreview(const QString&)) );
+ connect ( mSkinselectorWidget->installButton, SIGNAL(clicked()), this, SLOT(installNewSkin()) );
+ connect ( mSkinselectorWidget->mRemoveButton, SIGNAL(clicked()), this, SLOT(removeSelectedSkin()) );
+
+ reopen(); // fill the skinlist and draw a preview
+}
+
+
+void KJPrefs::reopen() // reload config and set stuff in dialog
+{
+// kdDebug(66666) << "[KJPrefs] reopen()" << endl;
+
+ cfg->setGroup("KJofol-Skins");
+
+// mGuiSettingsWidget->timeCountdown->setChecked( cfg->readBoolEntry("TimeCountMode", false) );
+ mGuiSettingsWidget->displayTooltips->setChecked( cfg->readBoolEntry("DisplayTooltips", true) );
+ mGuiSettingsWidget->displaySplash->setChecked( cfg->readBoolEntry("DisplaySplashScreen", true) );
+
+ mGuiSettingsWidget->minPitch->setValue( cfg->readNumEntry("minimumPitch", 50) );
+ mGuiSettingsWidget->maxPitch->setValue( cfg->readNumEntry("maximumPitch", 200) );
+ mGuiSettingsWidget->visTimerValue->setValue( cfg->readNumEntry("VisualizationSpeed", 30) );
+
+ mGuiSettingsWidget->useSysFont->setChecked( cfg->readBoolEntry("Use SysFont", false) );
+ mGuiSettingsWidget->cmbSysFont->setCurrentFont(
+ cfg->readEntry("SysFont Family", KGlobalSettings::generalFont().family()) );
+ QColor tmpColor = QColor(255,255,255);
+ mGuiSettingsWidget->cmbSysFontColor->setColor(
+ cfg->readColorEntry("SysFont Color", &tmpColor));
+
+ // TODO somehow honor both config-entries, I want a custom mode
+ switch ( cfg->readNumEntry("TitleScrollSpeed", 400 ) )
+ {
+ case 800:
+ mGuiSettingsWidget->titleScrollSpeed->setValue(1);
+ break;
+ case 400:
+ mGuiSettingsWidget->titleScrollSpeed->setValue(2);
+ break;
+ case 200:
+ mGuiSettingsWidget->titleScrollSpeed->setValue(3);
+ break;
+ }
+
+ switch ( cfg->readNumEntry("AnalyzerType", KJVisScope::FFT ) )
+ {
+ case KJVisScope::Null:
+ mGuiSettingsWidget->visNone->setChecked(true);
+ mGuiSettingsWidget->visScope->setChecked(false);
+ mGuiSettingsWidget->visAnalyzer->setChecked(false);
+ break;
+
+ case KJVisScope::FFT:
+ mGuiSettingsWidget->visNone->setChecked(false);
+ mGuiSettingsWidget->visScope->setChecked(false);
+ mGuiSettingsWidget->visAnalyzer->setChecked(true);
+ break;
+
+ case KJVisScope::Mono:
+ mGuiSettingsWidget->visNone->setChecked(false);
+ mGuiSettingsWidget->visScope->setChecked(true);
+ mGuiSettingsWidget->visAnalyzer->setChecked(false);
+ break;
+ }
+
+ QStringList skins;
+ QStringList skinLocations = KGlobal::dirs()->findDirs("data", "noatun/skins/kjofol");
+ // iterate through all paths where Noatun is searching for kjofol-skins
+ for (uint i = 0; i < skinLocations.count(); ++i )
+ {
+ QStringList skinDirs = QDir(skinLocations[i]).entryList();
+ // iterate trough all dirs (normally, users can fsck every dir-struct *g*) containing a skin
+ for (uint k = 2; k < skinDirs.count(); ++k )
+ {
+ QDir skinDirCnt = QDir ( skinLocations[i]+skinDirs[k], "*.rc", QDir::Name|QDir::IgnoreCase, QDir::Files );
+ // make a list of all .rc-files in a skindir
+ QStringList rcFiles = skinDirCnt.entryList();
+ // iterate trough all those rc.-files in a skindir
+ for (uint j = 0; j < rcFiles.count(); j++ )
+ {
+// kdDebug(66666) << "found: " << rcFiles[j].latin1() << endl;
+ skins += ( rcFiles[j] );
+ }
+ }
+ }
+
+ skins.sort();
+
+ QString loaded = cfg->readEntry("SkinResource", locate("data", "noatun/skins/kjofol/kjofol/kjofol.rc") );
+ loaded = loaded.mid(loaded.findRev("/")+1); // remove path
+ loaded = loaded.left( loaded.length() - 3 ); // remove ".rc"
+
+ mSkinselectorWidget->mSkins->clear();
+
+ int index = 0;
+ for (QStringList::Iterator i=skins.begin(); i!=skins.end(); ++i)
+ {
+ *i = (*i).left( (*i).length() - 3 );
+ mSkinselectorWidget->mSkins->insertItem(*i);
+ if ( (*i) == loaded )
+ index = mSkinselectorWidget->mSkins->count()-1; // save index no. to set active item later on
+ }
+
+ mSkinselectorWidget->mSkins->setCurrentItem(index);
+
+ showPreview( mSkinselectorWidget->mSkins->currentText() );
+}
+
+
+void KJPrefs::save()
+{
+// kdDebug(66666) << k_funcinfo << "called." << endl;
+ QString skin=::expand ( mSkinselectorWidget->mSkins->currentText() );
+
+ // first load skin and then save config to prevent
+ // reloading a broken skin after a crash
+ KJLoader *l=KJLoader::kjofol;
+ if (l)
+ l->loadSkin(skin);
+
+ cfg->setGroup("KJofol-Skins");
+
+ cfg->writeEntry("SkinResource", skin);
+// cfg->writeEntry("TimeCountMode", timeCountMode() );
+ cfg->writeEntry("DisplayTooltips", displayTooltips() );
+ cfg->writeEntry("DisplaySplashScreen", displaySplash() );
+
+ cfg->writeEntry("TitleScrollSpeed", titleMovingUpdates() );
+ cfg->writeEntry("TitleScrollAmount", titleMovingDistance() );
+ cfg->writeEntry("AnalyzerType", (int)visType() );
+ cfg->writeEntry("minimumPitch", minimumPitch() );
+ cfg->writeEntry("maximumPitch", maximumPitch() );
+ cfg->writeEntry("VisualizationSpeed", visTimerValue() );
+
+ cfg->writeEntry("Use SysFont", mGuiSettingsWidget->useSysFont->isChecked());
+ cfg->writeEntry("SysFont Family", mGuiSettingsWidget->cmbSysFont->currentFont());
+// kdDebug(66666) << k_funcinfo << "currentfont=" << mGuiSettingsWidget->cmbSysFont->currentFont() << endl;
+ cfg->writeEntry("SysFont Color", mGuiSettingsWidget->cmbSysFontColor->color());
+
+ cfg->sync();
+
+ emit configChanged();
+}
+
+QString KJPrefs::skin( void ) const
+{
+ // return full path to currently loaded skin
+ return ::expand( mSkinselectorWidget->mSkins->currentText() );
+}
+
+int KJPrefs::minimumPitch( void ) const
+{
+ return mGuiSettingsWidget->minPitch->value();
+}
+
+int KJPrefs::maximumPitch( void ) const
+{
+ return mGuiSettingsWidget->maxPitch->value();
+}
+
+int KJPrefs::visTimerValue ( void ) const
+{
+ return mGuiSettingsWidget->visTimerValue->value();
+}
+
+int KJPrefs::titleMovingUpdates ( void ) const
+{
+ switch ( mGuiSettingsWidget->titleScrollSpeed->value() )
+ {
+ case 1:
+ return 800;
+ case 2:
+ return 400;
+ case 3:
+ return 200;
+ default:
+ return 400; // emergency exit :)
+ }
+}
+
+float KJPrefs::titleMovingDistance ( void ) const
+{
+ switch ( mGuiSettingsWidget->titleScrollSpeed->value() )
+ {
+ case 1:
+ return 0.2f;
+ case 2:
+ return 0.5f;
+ case 3:
+ return 1.0f;
+ default:
+ return 0.5f; // emergency exit :)
+ }
+}
+
+int KJPrefs::visType ( void ) const
+{
+ if ( mGuiSettingsWidget->visNone->isChecked() ) // No Vis
+ return KJVisScope::Null;
+ else if ( mGuiSettingsWidget->visScope->isChecked() ) // MonoScope
+ return KJVisScope::Mono;
+ else if ( mGuiSettingsWidget->visAnalyzer->isChecked() ) // FFT Analyzer
+ return KJVisScope::FFT;
+ else
+ return KJVisScope::StereoFFT; //Null; // emergency exit :)
+}
+
+void KJPrefs::setVisType ( int vis )
+{
+ switch ( vis )
+ {
+ case KJVisScope::Null:
+ mGuiSettingsWidget->visNone->setChecked(true);
+ mGuiSettingsWidget->visScope->setChecked(false);
+ mGuiSettingsWidget->visAnalyzer->setChecked(false);
+ break;
+
+ case KJVisScope::FFT:
+ mGuiSettingsWidget->visNone->setChecked(false);
+ mGuiSettingsWidget->visScope->setChecked(false);
+ mGuiSettingsWidget->visAnalyzer->setChecked(true);
+ break;
+
+ case KJVisScope::StereoFFT:
+ mGuiSettingsWidget->visNone->setChecked(false);
+ mGuiSettingsWidget->visScope->setChecked(false);
+ mGuiSettingsWidget->visAnalyzer->setChecked(false);
+ break;
+
+ case KJVisScope::Mono:
+ mGuiSettingsWidget->visNone->setChecked(false);
+ mGuiSettingsWidget->visScope->setChecked(true);
+ mGuiSettingsWidget->visAnalyzer->setChecked(false);
+ break;
+ }
+ save(); // not sure if that's a good idea or doing saving by hand in here
+}
+
+
+bool KJPrefs::useSysFont( void ) const
+{
+ return mGuiSettingsWidget->useSysFont->isChecked();
+}
+
+void KJPrefs::setUseSysFont( bool mode )
+{
+ mGuiSettingsWidget->useSysFont->setChecked( mode );
+ save(); // not sure if that's a good idea or doing saving by hand in here
+}
+
+QFont KJPrefs::sysFont(void) const
+{
+ QString family = mGuiSettingsWidget->cmbSysFont->currentFont();
+// kdDebug(66666) << k_funcinfo << "family=" << family << endl;
+ return QFont( family );
+}
+
+void KJPrefs::setSysFont(QFont &fnt)
+{
+ mGuiSettingsWidget->cmbSysFont->setCurrentFont( fnt.family() );
+}
+
+QColor KJPrefs::sysFontColor(void) const
+{
+ return mGuiSettingsWidget->cmbSysFontColor->color();
+}
+
+void KJPrefs::sysFontColor(QColor &c)
+{
+ mGuiSettingsWidget->cmbSysFontColor->setColor( c );
+}
+
+bool KJPrefs::displayTooltips( void ) const
+{
+ return mGuiSettingsWidget->displayTooltips->isChecked();
+}
+
+bool KJPrefs::displaySplash( void ) const
+{
+ return mGuiSettingsWidget->displaySplash->isChecked();
+}
+
+
+void KJPrefs::showPreview(const QString &_skin)
+{
+ Parser p;
+ p.open( ::expand(_skin) );
+
+ QImage image = p.image(p["BackgroundImage"][1]);
+ if (!image.isNull())
+ {
+ mPixmap.convertFromImage(image);
+ mPixmap.setMask( KJWidget::getMask(image) );
+ }
+ else
+ mPixmap=QPixmap();
+
+ mSkinselectorWidget->mPreview->setPixmap(mPixmap);
+ mSkinselectorWidget->mAboutText->setText(p.about());
+ mSkinselectorWidget->updateGeometry();
+}
+
+
+/* =================================================================================== */
+
+
+void KJPrefs::installNewSkin( void )
+{
+ bool skinInstalled = false; // flag showing wether a skindir got installed
+ KURL src, dst; // sourcedir and destinationdir for skin-installation
+
+ KURL srcFile ( mSkinselectorWidget->mSkinRequester->url() );
+
+ //kdDebug(66666) << "file to work on: " << srcFile.path().latin1() << endl;
+
+ if ( !srcFile.isValid() || srcFile.isEmpty() ) // stop working on broken URLs
+ {
+ kdDebug(66666) << "srcFile is malformed or empty !!!" << endl;
+ return;
+ }
+
+ if ( !srcFile.isLocalFile() ) // TODO: Download file into tmp dir + unpack afterwards
+ {
+ KMessageBox::sorry ( this, i18n("Non-Local files are not supported yet") );
+ return;
+ }
+
+ // Determine file-format trough mimetype (no stupid .ext test)
+ KMimeMagicResult * result = KMimeMagic::self()->findFileType( srcFile.path() );
+
+ if ( !result->isValid() )
+ {
+ kdDebug(66666) << "Could not determine filetype of srcFile !!!" << endl;
+ return;
+ }
+
+ if ( result->mimeType() != "application/x-zip" )
+ {
+ KMessageBox::error ( this, i18n("The selected file does not appear to be a valid zip-archive") );
+ return;
+ }
+
+ // create a dir with name of the skinarchive
+ // path to unpack to: pathToTmp/filename.ext/
+ QString tmpUnpackPath = locateLocal("tmp", srcFile.fileName()+"/" );
+ kdDebug(66666) << "tmpUnpackPath: " << tmpUnpackPath.latin1() << endl;
+
+ // Our extract-process, TODO: wanna have kio_(un)zip instead :)
+ KShellProcess proc;
+
+ // "unzip -d whereToUnpack whatToUnpack"
+ proc << "unzip -d " << proc.quote(tmpUnpackPath) << " " << proc.quote(srcFile.path());
+ kdDebug(66666) << "unzip -d " << tmpUnpackPath.latin1() << " " << srcFile.path().latin1() << endl;
+
+ proc.start( KProcess::Block, KProcess::NoCommunication );
+
+ // "unzip" spits out errorcodes > 0 only, 0 on success
+ if ( proc.exitStatus() != 0 )
+ {
+ KMessageBox::error ( this, i18n("Extracting skin-archive failed") );
+ // FIXME: Do I have to wait for the job to finish?
+ // I'd say no because I don't care about the temp-dir
+ // anyway after leaving this method :)
+ KIO::del( tmpUnpackPath );
+ return;
+ }
+
+ QDir tmpCnt = QDir ( tmpUnpackPath );
+ tmpCnt.setFilter ( QDir::Dirs );
+
+ QStringList dirList = tmpCnt.entryList();
+ // Iterate trough all subdirs of tmpUnpackPath (including "."!)
+ for ( unsigned int i = 0; i < dirList.count(); i++ )
+ {
+ // FIXME: is the following portable?
+ if ( dirList[i] == ".." )
+ continue;
+
+ QDir tmpSubCnt = QDir( tmpUnpackPath + dirList[i], "*.rc;*.RC;*.Rc;*.rC", QDir::Name|QDir::IgnoreCase, QDir::Files );
+ kdDebug(66666) << "Searching for *.rc in " << QString(tmpUnpackPath+dirList[i]).latin1() << endl;
+
+ // oh, no .rc file in current dir, let's go to next dir in list
+ if ( tmpSubCnt.count() == 0 )
+ continue;
+
+ src = KURL::encode_string(tmpUnpackPath+dirList[i]);
+ dst = KURL::encode_string(locateLocal("data","noatun/skins/kjofol/")); // destination to copy skindir into
+
+ if ( dirList[i] == "." ) // zip did not contain a subdir, we have to create one
+ {
+ // skindir is named like the archive without extension (FIXME: extension is not stripped from name)
+
+ int dotPos = srcFile.fileName().findRev('.');
+ if ( dotPos > 0 ) // found a dot -> (hopefully) strip the extension
+ {
+ dst.addPath( srcFile.fileName().left(dotPos) );
+ }
+ else // we don't seem to have any extension, just append the archivename
+ {
+ dst.addPath( srcFile.fileName() );
+ }
+
+ kdDebug(66666) << "want to create: " << dst.path().latin1() << endl;
+
+ if ( !dst.isValid() )
+ {
+ KMessageBox::error ( this,
+ i18n("Installing new skin failed: Destination path is invalid.\n"
+ "Please report a bug to the K-Jöfol maintainer") );
+ KIO::del( tmpUnpackPath );
+ return;
+ }
+ KIO::mkdir( dst );
+ }
+
+ if ( !src.isValid() || !dst.isValid() )
+ {
+ KMessageBox::error ( this,
+ i18n("Installing new skin failed: Either source or destination path is invalid.\n"
+ "Please report a bug to the K-Jöfol maintainer") );
+ }
+ else
+ {
+ kdDebug(66666) << "src: " << src.path().latin1() << endl;
+ kdDebug(66666) << "dst: " << dst.path().latin1() << endl;
+ KIO::Job *job = KIO::copy(src,dst);
+ connect ( job, SIGNAL(result(KIO::Job*)), this, SLOT(slotResult(KIO::Job*)) );
+ skinInstalled = true;
+ }
+ } // END iterate trough dirList
+
+ if ( !skinInstalled )
+ {
+ KMessageBox::sorry ( this, i18n("No new skin has been installed.\nMake sure the archive contains a valid K-Jöfol skin") );
+ }
+ else
+ {
+ KMessageBox::information ( this, i18n("The new skin has been successfully installed") );
+ }
+
+ KIO::del( tmpUnpackPath );
+}
+
+
+void KJPrefs::removeSelectedSkin( void )
+{
+ QString question = i18n("Are you sure you want to remove %1?\n"
+ "This will delete the files installed by this skin ").
+ arg ( mSkinselectorWidget->mSkins->currentText() );
+
+ cfg->setGroup("KJofol-Skins");
+ QString loadedSkin = cfg->readEntry("SkinResource", "kjofol");
+// kdDebug(66666) << "loaded Skin Name: " << QFileInfo(loadedSkin).baseName().latin1() << endl;
+
+ int r = KMessageBox::warningContinueCancel ( this, question, i18n("Confirmation"), KStdGuiItem::del() );
+ if ( r != KMessageBox::Continue )
+ return;
+
+ bool deletingCurrentSkin = ( mSkinselectorWidget->mSkins->currentText() == QFileInfo(loadedSkin).baseName() );
+
+ // Now find the dir to delete !!!
+
+ QString dirToDelete = QString ("");
+ QStringList skinLocations = KGlobal::dirs()->findDirs("data", "noatun/skins/kjofol");
+
+ // iterate through all paths where Noatun is searching for kjofol-skins
+ for (uint i = 0; i < skinLocations.count(); ++i )
+ {
+ QStringList skinDirs = QDir(skinLocations[i]).entryList();
+
+ // iterate trough all dirs containing a skin
+ for (uint k = 0; k < skinDirs.count(); ++k )
+ {
+ QDir skinDirCnt = QDir ( skinLocations[i]+skinDirs[k], "*.rc", QDir::Name|QDir::IgnoreCase, QDir::Files );
+ // make a list of all .rc-files in a skindir
+ QStringList rcFiles = skinDirCnt.entryList();
+
+ // iterate trough all those rc.-files in a skindir
+ for (uint j = 0; j < rcFiles.count(); j++ )
+ {
+ if ( rcFiles[j].left(rcFiles[j].length()-3) == mSkinselectorWidget->mSkins->currentText() ) // found skinname.rc :)
+ {
+ dirToDelete = QString ( skinLocations[i]+skinDirs[k] );
+ kdDebug(66666) << "FOUND SKIN @ " << dirToDelete.latin1() << endl;
+ }
+ }
+ }
+ }
+
+ if ( dirToDelete.length() != 0 )
+ {
+ kdDebug(66666) << "Deleting Skindir: " << dirToDelete.latin1() << endl;
+ KIO::Job *job = KIO::del( dirToDelete, false, true );
+ connect ( job, SIGNAL(result(KIO::Job*)), this, SLOT(slotResult(KIO::Job*)) );
+ }
+
+ int item = -1;
+ // Fallback to kjofol-skin (the default one) if we've deleted the current skin
+ if ( deletingCurrentSkin )
+ {
+ for ( int i = 0; i < mSkinselectorWidget->mSkins->count(); i++ )
+ { // FIXME: no check wether "kjofol" is ever found, well, it HAS to be in the list
+ if ( mSkinselectorWidget->mSkins->text(i) == "kjofol" )
+ item = i;
+ }
+ }
+ else
+ item = mSkinselectorWidget->mSkins->currentItem();
+
+ if ( item != -1 )
+ mSkinselectorWidget->mSkins->setCurrentItem( item );
+
+ // update configuration
+ if ( deletingCurrentSkin )
+ save();
+}
+
+void KJPrefs::slotResult(KIO::Job *job )
+{
+ if ( job->error() )
+ {
+ job->showErrorDialog(this);
+ }
+ else
+ {
+ // Reload Skinlist
+ reopen();
+ }
+}
+
+
+/* =================================================================================== */
+
+
+// takes name of rc-file without .rc at the end and returns full path to rc-file
+static QString expand(QString s)
+{
+// kdDebug(66666) << "expand( "<< s.latin1() << " )" << endl;
+
+ QStringList skinLocations = KGlobal::dirs()->findDirs("data", "noatun/skins/kjofol");
+
+ // iterate through all paths where Noatun is searching for kjofol-skins
+ for (uint i = 0; i < skinLocations.count(); ++i )
+ {
+ QStringList skinDirs = QDir(skinLocations[i]).entryList();
+
+ // iterate trough all dirs containing a skin
+ for (uint k = 0; k < skinDirs.count(); ++k )
+ {
+ QDir skinDirCnt = QDir ( skinLocations[i]+skinDirs[k], "*.rc", QDir::Name|QDir::IgnoreCase, QDir::Files );
+ // make a list of all .rc-files in a skindir
+ QStringList rcFiles = skinDirCnt.entryList();
+
+ // iterate trough all those rc.-files in a skindir
+ for (uint j = 0; j < rcFiles.count(); j++ )
+ {
+ if ( rcFiles[j].left(rcFiles[j].length()-3) == s ) // found $s.rc :)
+ {
+// kdDebug(66666) << "expand() found: " << QString(skinLocations[i]+skinDirs[k]+"/"+rcFiles[j]).latin1() << endl;
+ return (skinLocations[i]+skinDirs[k]+"/"+rcFiles[j]);
+ }
+ }
+ }
+ }
+ return QString();
+}
+
+QString filenameNoCase(const QString &filename, int badNodes)
+{
+ QStringList names=QStringList::split('/', filename);
+ QString full;
+ int number=(int)names.count();
+ for (QStringList::Iterator i=names.begin(); i!=names.end(); ++i)
+ {
+ full+="/";
+ if (number<=badNodes)
+ {
+ QDir d(full);
+ QStringList files=d.entryList();
+ files=files.grep(QRegExp("^"+ (*i) + "$", false));
+ if (!files.count())
+ return "";
+ *i=files.grep(*i, false)[0];
+ }
+
+ full+=*i;
+
+ number--;
+ }
+
+ if (filename.right(1)=="/")
+ full+="/";
+ return full;
+}
diff --git a/noatun/modules/kjofol-skin/kjprefs.h b/noatun/modules/kjofol-skin/kjprefs.h
new file mode 100644
index 00000000..ce1725d5
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjprefs.h
@@ -0,0 +1,96 @@
+#ifndef KJPREFS_H
+#define KJPREFS_H
+
+//#include "kjprefswidget.h"
+#include "kjskinselectorwidget.h"
+#include "kjguisettingswidget.h"
+
+// system includes
+#include <qwidget.h>
+#include <noatun/pref.h>
+
+#include <kio/job.h>
+#include <kurlrequester.h>
+
+class QVBoxLayout;
+class QHBoxLayout;
+class QGridLayout;
+class QComboBox;
+class QLabel;
+class QPushButton;
+class QTabWidget;
+class KConfig;
+class KJLoader;
+
+class KJPrefs : public CModule
+{
+Q_OBJECT
+public:
+ KJPrefs(QObject* parent);
+
+ // Save which Skin is currently selected
+ virtual void save();
+
+ // Rebuild the Skinlist
+ virtual void reopen();
+
+ QString skin( void ) const;
+
+ int minimumPitch( void ) const;
+ int maximumPitch( void ) const;
+
+ int visTimerValue ( void ) const;
+
+ int titleMovingUpdates ( void ) const;
+ float titleMovingDistance ( void ) const;
+
+ int visType ( void ) const;
+ void setVisType ( int vis );
+
+ bool useSysFont( void ) const;
+ void setUseSysFont( bool );
+
+ QFont sysFont(void) const;
+ void setSysFont(QFont&);
+
+ QColor sysFontColor(void) const;
+ void sysFontColor(QColor &);
+
+ bool displayTooltips( void ) const;
+ bool displaySplash( void ) const;
+
+public slots:
+ // Installs a skin defined by the URL in mSkinRequester
+ void installNewSkin( void );
+
+ // Delete the currently selected Skin (does not work for systemwide skins!)
+ void removeSelectedSkin ( void );
+
+ // Show a preview of "skin" in mPixmap
+ void showPreview(const QString &skin);
+
+ // gets called after a KIO-action has finished
+ // KIO is used for installing/removing skins
+ void slotResult(KIO::Job *job);
+
+signals:
+ void configChanged();
+
+private:
+ QPixmap mPixmap; // preview Pixmap
+ KConfig *cfg;
+
+ // Dialog-Widgets
+ QTabWidget *mTabWidget;
+ KJSkinselector *mSkinselectorWidget;
+ KJGuiSettings *mGuiSettingsWidget;
+};
+
+/**
+ * resolve a filename to its correct case.
+ * badNodes is the amount of directories/files (at the end)
+ * that aren't known)
+ **/
+QString filenameNoCase(const QString &filename, int badNodes=1);
+
+#endif // KJPREFS_H
diff --git a/noatun/modules/kjofol-skin/kjseeker.cpp b/noatun/modules/kjofol-skin/kjseeker.cpp
new file mode 100644
index 00000000..41e4db13
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjseeker.cpp
@@ -0,0 +1,210 @@
+/***************************************************************************
+ kjseeker.cpp
+ ---------------------------------------------
+ slider that lets the user jump inside the currently played file
+ ---------------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+#include "kjseeker.h"
+#include "kjloader.h"
+
+#include "helpers.cpp"
+#include <noatun/player.h>
+
+#include <kdebug.h>
+
+KJSeeker::KJSeeker(const QStringList &i, KJLoader *l) : KJWidget(l), g(0)
+{
+ QString activeBg = backgroundPressed("bmp1");
+ if(activeBg.isEmpty())
+ {
+ kdDebug(66666) << k_funcinfo << "No pressed background found for seeker," <<
+ " using default background!" << endl;
+ parent()->image(parser()["backgroundimage"][1]);
+ }
+ else
+ mActive = parent()->image(activeBg);
+
+ mScale = parent()->image(parser()["seekimage"][1]);
+ QImage pixmapNoPress = parent()->image(parser()["backgroundimage"][1]);
+
+ // generate transparent mask
+ int x, y, xs, ys;
+ x=i[1].toInt();
+ y=i[2].toInt();
+ xs=i[3].toInt()-x;
+ ys=i[4].toInt()-y;
+ setRect(x,y,xs,ys);
+ QImage transmask(xs, ys, 1, 2, QImage::LittleEndian);
+#if QT_VERSION < 0x030300
+ transmask.setColor(0, qRgb(0,0,0));
+ transmask.setColor(1, qRgb(255,255,255));
+#else
+ transmask.setColor(1, qRgb(0,0,0));
+ transmask.setColor(0, qRgb(255,255,255));
+#endif
+
+ // clear the pointers
+ memset(barmodeImages, 0, 256*sizeof(QImage*));
+ memset(barmode, 0, 256*sizeof(QPixmap*));
+
+ // Now do the pixel fking
+// kdDebug(66666) << "creating Pixmaps for Seeker" << endl;
+ for (int iy=y;iy<y+ys; iy++)
+ {
+ for (int ix=x;ix<x+xs; ix++)
+ {
+ QRgb checkmScale = mScale.pixel(ix, iy);
+ // am I transparent?
+ if (!isGray(checkmScale))
+ {
+ setPixel1BPP(transmask, ix-x, iy-y, 0);
+ continue;
+ }
+ setPixel1BPP(transmask, ix-x, iy-y, 1);
+
+ // what is the level
+ int level=grayRgb(checkmScale)+1;
+ if (level>255) level=255;
+ // allocate the pixmap of the level proper
+ // copy the color to the surface proper
+ QRgb activeColor=mActive.pixel(ix,iy);
+ QRgb inactiveColor=pixmapNoPress.pixel(ix,iy);
+ // set this pixel and everything before it
+ for(int i=0; i<level; i++)
+ {
+ if (!barmodeImages[i])
+ barmodeImages[i]=new QImage(xs,ys, 32);
+ QRgb *l=(QRgb*)barmodeImages[i]->scanLine(iy-y);
+ l[ix-x]=inactiveColor;
+ }
+
+ do
+ {
+ if (!barmodeImages[level])
+ barmodeImages[level]=new QImage(xs,ys, 32);
+ QRgb *l=(QRgb*)barmodeImages[level]->scanLine(iy-y);
+ l[ix-x]=activeColor;
+ } while (level++<255);
+ }
+ }
+// kdDebug(66666) << "finished creating Pixmaps" << endl;
+
+ // create the blank one
+ barmode[0]=new QPixmap(xs, ys);
+ QPixmap px=parent()->pixmap(parser()["backgroundimage"][1]);
+ bitBlt(barmode[0], 0, 0, &px, x, y, xs, ys, Qt::CopyROP);
+ px.convertFromImage(transmask);
+ barModeMask=px;
+
+// kdDebug(66666) << "END KJSeeker constructor" << endl;
+}
+
+QPixmap *KJSeeker::toPixmap(int n)
+{
+ if (!barmodeImages[n]) return barmode[n];
+
+ barmode[n]=new QPixmap(
+ barmodeImages[n]->width(),
+ barmodeImages[n]->height()
+ );
+ barmode[n]->convertFromImage(*barmodeImages[n]);
+
+ delete barmodeImages[n];
+ barmodeImages[n]=0;
+ return barmode[n];
+}
+
+
+KJSeeker::~KJSeeker()
+{
+ for (uint i=0; i<256; i++)
+ {
+ if (barmode[i])
+ delete barmode[i];
+ if (barmodeImages[i])
+ delete barmodeImages[i];
+ }
+}
+
+void KJSeeker::paint(QPainter *p, const QRect &)
+{
+ closest();
+ QPixmap *pixmap = toPixmap(g);
+ pixmap->setMask(barModeMask);
+ bitBlt(p->device(), rect().topLeft().x(), rect().topLeft().y(),
+ pixmap, 0, 0, rect().width(), rect().height(), Qt::CopyROP);
+}
+
+bool KJSeeker::mousePress(const QPoint &pos)
+{
+ return (isGray(mScale.pixel(rect().topLeft().x()+pos.x(), rect().topLeft().y()+pos.y())));
+}
+
+void KJSeeker::mouseRelease(const QPoint &pos, bool in)
+{
+ int x = rect().topLeft().x()+pos.x();
+ int y = rect().topLeft().y()+pos.y();
+
+ if(napp->player()->isStopped())
+ return;
+
+ if(!mScale.valid(x, y))
+ return;
+
+ QRgb color=mScale.pixel(x, y);
+
+ // user released mousebutton outside of the seeker-area (which is gray)
+ if ( (!isGray(color)) || (!in) )
+ return;
+
+ g = grayRgb(color);
+ repaint();
+
+// kdDebug(66666) << "length : " << napp->player()->getLength() << endl;
+// kdDebug(66666) << "skip to: " << ((long long)g*(long long)napp->player()->getLength())/255 << endl;
+
+ // g * titlelength can get REALLY HUGE, that's why I used (long long)
+ napp->player()->skipTo( ((long long)g*(long long)napp->player()->getLength())/255 );
+
+ return;
+}
+
+void KJSeeker::timeUpdate(int sec)
+{
+ int length = napp->player()->getLength() / 1000;
+ if (length<1)
+ length=1;
+
+ if (sec > length)
+ sec = length;
+ else if ( sec < 0 )
+ sec=0;
+
+ g = sec * 255 / length;
+ //kdDebug(66666) << "sec: " << sec << " len: " << length << " g: " << g << endl;
+ QPainter p(parent());
+ paint(&p, rect());
+}
+
+void KJSeeker::closest()
+{
+ int south=g, north=g;
+ bool southtried=false, northtried=false;
+ while (
+ !barmode[south] && !barmodeImages[south]
+ && !barmode[north] && !barmodeImages[north])
+ {
+ if (southtried && northtried) { g=0; return; }
+ south--;
+ north++;
+ if (north>255) {northtried=true; north=g;}
+ if (south<0) {southtried=true; south=g;}
+ }
+ if (barmode[south] || barmodeImages[south])
+ g=south;
+ else if (barmode[north] || barmodeImages[north])
+ g=north;
+}
diff --git a/noatun/modules/kjofol-skin/kjseeker.h b/noatun/modules/kjofol-skin/kjseeker.h
new file mode 100644
index 00000000..78fea6fb
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjseeker.h
@@ -0,0 +1,37 @@
+#ifndef KJSEEKER_H
+#define KJSEEKER_H
+
+#include "kjwidget.h"
+//#include "kjloader.h"
+class KJLoader;
+
+#include <qpainter.h>
+
+class KJSeeker : public KJWidget
+{
+public:
+ KJSeeker(const QStringList &i, KJLoader *);
+ ~KJSeeker();
+
+
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &pos);
+ virtual void mouseRelease(const QPoint &pos, bool);
+
+ void timeUpdate(int mille);
+
+ void closest();
+
+private:
+ QPixmap *toPixmap(int n);
+
+private:
+ QImage mScale;
+ QImage mActive;
+ QPixmap *barmode[256];
+ QImage *barmodeImages[256];
+ QBitmap barModeMask;
+ int g;
+};
+
+#endif
diff --git a/noatun/modules/kjofol-skin/kjskinselectorwidget.ui b/noatun/modules/kjofol-skin/kjskinselectorwidget.ui
new file mode 100644
index 00000000..1540ad5e
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjskinselectorwidget.ui
@@ -0,0 +1,227 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>KJSkinselector</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KJSkinselector</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>461</width>
+ <height>345</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>mSkins</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>previewGroup</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Preview</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer row="0" column="1">
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>31</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="2" column="2">
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>51</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="3" column="1">
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>41</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>31</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="1" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>mPreview</cstring>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ <property name="hAlign" stdset="0">
+ </property>
+ <property name="vAlign" stdset="0">
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>About skin:</string>
+ </property>
+ </widget>
+ <widget class="QTextBrowser">
+ <property name="name">
+ <cstring>mAboutText</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Here you will see all the comments people wrote about their skins.
+It can be several lines and usually does not contain anything interesting but still this will be shown.</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>mSkinRequester</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>installButton</cstring>
+ </property>
+ <property name="text">
+ <string>Install Skin</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>mRemoveButton</cstring>
+ </property>
+ <property name="text">
+ <string>Remove Skin</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/noatun/modules/kjofol-skin/kjsliders.cpp b/noatun/modules/kjofol-skin/kjsliders.cpp
new file mode 100644
index 00000000..8cadd04f
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjsliders.cpp
@@ -0,0 +1,336 @@
+/***************************************************************************
+ kjsliders.cpp
+ ---------------------------------------------
+ Sliders for Volume and Pitch
+ ---------------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+// local includes
+#include "kjsliders.h"
+#include "kjtextdisplay.h"
+#include "kjprefs.h"
+
+#include "helpers.cpp"
+
+// kde includes
+#include <klocale.h>
+#include <kdebug.h>
+
+// arts-includes, needed for pitch
+#include <artsmodules.h>
+#include <reference.h>
+#include <soundserver.h>
+#include <kmedia2.h>
+
+// noatun includes
+#include <noatun/player.h>
+#include <noatun/engine.h>
+
+/*******************************************
+ * KJVolumeBar
+ *******************************************/
+
+KJVolumeBar::KJVolumeBar(const QStringList &i, KJLoader *p)
+ : KJWidget(p), mVolume(0), mText(0)
+{
+ int x, y, xs, ys;
+ x=i[1].toInt();
+ y=i[2].toInt();
+ xs=i[3].toInt()-x;
+ ys=i[4].toInt()-y;
+ setRect ( x, y, xs, ys );
+
+// kdDebug(66666) << "x: " << x << " y: " << y << " w: " << xs << " h: " << ys << endl;
+
+ mBack = parent()->pixmap(parser()["backgroundimage"][1]);
+ mSlider = parent()->pixmap(parser()["volumecontrolimage"][1]);
+}
+
+QString KJVolumeBar::tip()
+{
+ return i18n("Volume");
+}
+
+void KJVolumeBar::paint(QPainter *p, const QRect &)
+{
+// kdDebug(66666) << "x: " << rect().x() << " y: " << rect().y() << endl;
+// kdDebug(66666) << "vol x: " << rect().x()+(mVolume*rect().width())/100 << endl;
+
+ // center of that slider-pixmap
+// QPoint hotSpot = QPoint( mSlider.width()/2, mSlider.height()/2 );
+
+ // draw our background
+ bitBlt(
+ p->device(),
+ rect().x() /*- hotSpot.x()*/,
+ rect().y() /*- hotSpot.y()*/,
+ &mBack,
+ rect().x() /*- hotSpot.x()*/,
+ rect().y() /*- hotSpot.y()*/,
+ rect().width() /*+ (2*hotSpot.x())*/,
+ rect().height() /*+ (2*hotSpot.y())*/,
+ Qt::CopyROP);
+
+ // draw our slider
+ bitBlt(
+ p->device(),
+ rect().x() + ((mVolume*rect().width())/100) /*- hotSpot.x()*/,
+ rect().y() /*- hotSpot.y()*/,
+ &mSlider,
+ 0,
+ 0,
+ mSlider.width(),
+ mSlider.height(),
+ Qt::CopyROP);
+
+ if (mText)
+ mText->repaint();
+}
+
+bool KJVolumeBar::mousePress(const QPoint &pos)
+{
+ mVolume = (pos.x()*100) / rect().width();
+// kdDebug(66666) << "volume: " << mVolume << endl;
+ repaint();
+ napp->player()->setVolume(mVolume);
+ return true;
+}
+
+void KJVolumeBar::mouseRelease(const QPoint &, bool)
+{
+}
+
+void KJVolumeBar::mouseMove(const QPoint &pos, bool in)
+{
+ if (!in)
+ return;
+ mousePress(pos);
+}
+
+void KJVolumeBar::timeUpdate(int)
+{
+ mVolume = napp->player()->volume();
+ repaint();
+}
+
+
+/*******************************************
+ * KJVolumeBMP
+ *******************************************/
+
+KJVolumeBMP::KJVolumeBMP(const QStringList &i, KJLoader *p)
+ : KJWidget(p), mVolume(0), mOldVolume(0), mText(0)
+{
+ int x, y, xs, ys;
+ x=i[1].toInt();
+ y=i[2].toInt();
+ xs=i[3].toInt()-x;
+ ys=i[4].toInt()-y;
+ setRect ( x, y, xs, ys );
+
+ mWidth = parser()["volumecontrolimagexsize"][1].toInt();
+ mCount = parser()["volumecontrolimagenb"][1].toInt()-1;
+
+ mImages = parent()->pixmap(parser()["volumecontrolimage"][1]);
+ mPos = parent()->image(parser()["volumecontrolimageposition"][1]);
+ timeUpdate(0);
+}
+
+QString KJVolumeBMP::tip()
+{
+ return i18n("Volume");
+}
+
+void KJVolumeBMP::paint(QPainter *p, const QRect &)
+{
+ QRect from(mVolume*mCount/100*mWidth, 0, mWidth, mImages.height());
+ bitBlt(p->device(), rect().topLeft(), &mImages, from, Qt::CopyROP);
+ if (mText)
+ mText->repaint();
+}
+
+bool KJVolumeBMP::mousePress(const QPoint &pos)
+{
+ QRgb color = mPos.pixel ( rect().topLeft().x()+pos.x(), rect().topLeft().y()+pos.y() );
+
+ if (!isGray(color))
+ return false;
+
+ mVolume = grayRgb(color)*100/255;
+// kdDebug(66666) << "gray : " << grayRgb(color) << endl;
+// kdDebug(66666) << "volume: " << mVolume << endl;
+
+ repaint();
+
+ napp->player()->setVolume(mVolume);
+
+ return true;
+}
+
+void KJVolumeBMP::mouseRelease(const QPoint &, bool)
+{}
+
+void KJVolumeBMP::mouseMove(const QPoint &pos, bool in)
+{
+ if (!in) return;
+ mousePress(pos);
+}
+
+void KJVolumeBMP::timeUpdate(int)
+{
+ mVolume = napp->player()->volume();
+
+ if ( mVolume == mOldVolume ) // dont redraw if nothing changed
+ return;
+
+ mOldVolume = mVolume;
+
+ repaint();
+}
+
+
+/*******************************************
+ * KJPitchBMP
+ *******************************************/
+
+KJPitchBMP::KJPitchBMP(const QStringList &i, KJLoader *p)
+ : KJWidget(p), mText(0)
+{
+ int x = i[1].toInt();
+ int y = i[2].toInt();
+ int xs = i[3].toInt() - x;
+ int ys = i[4].toInt() - y;
+
+ setRect ( x, y, xs, ys );
+
+ mWidth = parser()["pitchcontrolimagexsize"][1].toInt();
+ mCount = parser()["pitchcontrolimagenb"][1].toInt()-1;
+
+ mImages = parent()->pixmap(parser()["pitchcontrolimage"][1]);
+ mPos = parent()->image(parser()["pitchcontrolimageposition"][1]);
+
+ // makes all pixels with rgb(255,0,255) transparent
+ QImage ibackground;
+ ibackground = parent()->image(parser()["pitchcontrolimage"][1]);
+ mImages.setMask( getMask(ibackground) );
+
+ Arts::PlayObject playobject = napp->player()->engine()->playObject();
+ Arts::PitchablePlayObject pitchable = Arts::DynamicCast(playobject);
+
+ if ( pitchable.isNull() )
+ mCurrentPitch = 1.0;
+ else
+ mCurrentPitch = pitchable.speed();
+
+// kdDebug() << "[KJPitchBMP] starting with pitch: " << mCurrentPitch << endl;
+/*
+ mMinPitch = 0.5;
+ mMaxPitch = 2.0;
+
+ mMinPitch = 0.8;
+ mMaxPitch = 1.2;
+*/
+
+ readConfig();
+
+ if (mText)
+ mText->repaint();
+}
+
+QString KJPitchBMP::tip()
+{
+ return i18n("Pitch");
+}
+
+void KJPitchBMP::paint(QPainter *p, const QRect &)
+{
+ float xPos = (int)((mCurrentPitch-mMinPitch)*100.0) * mCount / (int)((mMaxPitch-mMinPitch)*100.0) * mWidth;
+
+ QRect from( (int)xPos, 0, mWidth, mImages.height());
+
+ bitBlt(p->device(), rect().topLeft(), &mImages, from, Qt::CopyROP);
+
+ if (mText)
+ mText->repaint();
+}
+
+bool KJPitchBMP::mousePress(const QPoint &pos)
+{
+ QRgb color = mPos.pixel ( rect().topLeft().x()+pos.x(), rect().topLeft().y()+pos.y() );
+
+ if (!isGray(color))
+ return false;
+
+ mCurrentPitch = mMinPitch + ( (grayRgb(color)*(mMaxPitch-mMinPitch)) / 255 );
+// kdDebug(66666) << "[KJPitchBMP] mousePress() mCurrentPitch: " << mCurrentPitch << endl;
+
+ repaint();
+
+ newFile(); // wrong naming, in fact it just sets pitch
+
+ return true;
+}
+
+void KJPitchBMP::mouseRelease(const QPoint &, bool)
+{}
+
+void KJPitchBMP::mouseMove(const QPoint &pos, bool in)
+{
+ if (!in) return;
+ mousePress(pos);
+}
+
+void KJPitchBMP::timeUpdate(int)
+{
+// kdDebug(66666) << "[KJPitchBMP] :timeUpdate(int)" << endl;
+
+ Arts::PlayObject playobject = napp->player()->engine()->playObject();
+ Arts::PitchablePlayObject pitchable = Arts::DynamicCast(playobject);
+
+ if ( !pitchable.isNull() )
+ {
+ mCurrentPitch = pitchable.speed();
+// kdDebug(66666) << "[KJPitchBMP] mCurrentPitch: " << mCurrentPitch << endl;
+ }
+
+ if ( mCurrentPitch == mOldPitch ) // dont redraw if nothing changed
+ return;
+
+ mOldPitch = mCurrentPitch;
+
+ repaint();
+}
+
+void KJPitchBMP::newFile()
+{
+// kdDebug(66666) << "[KJPitchBMP] newFile()" << endl;
+
+ Arts::PlayObject playobject = napp->player()->engine()->playObject();
+ Arts::PitchablePlayObject pitchable = Arts::DynamicCast(playobject);
+
+ if (!pitchable.isNull())
+ {
+// kdDebug(66666) << "[KJPitchBMP] new speed: " << mCurrentPitch << endl;
+ pitchable.speed( mCurrentPitch );
+ }
+}
+
+void KJPitchBMP::readConfig()
+{
+// kdDebug(66666) << "KJPitchBMP::readConfig()" << endl;
+
+ mMinPitch = KJLoader::kjofol->prefs()->minimumPitch() / 100.0;
+ mMaxPitch = KJLoader::kjofol->prefs()->maximumPitch() / 100.0;
+
+ // Now comes the range checking if the user changed the setting :)
+ if ( mCurrentPitch < mMinPitch || mCurrentPitch > mMaxPitch )
+ {
+ if ( mCurrentPitch < mMinPitch )
+ mCurrentPitch = mMinPitch;
+ if ( mCurrentPitch > mMaxPitch )
+ mCurrentPitch = mMaxPitch;
+ newFile(); // wrong naming, in fact it just sets pitch
+ }
+}
diff --git a/noatun/modules/kjofol-skin/kjsliders.h b/noatun/modules/kjofol-skin/kjsliders.h
new file mode 100644
index 00000000..94f10934
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjsliders.h
@@ -0,0 +1,88 @@
+#ifndef KJSLIDERS_H
+#define KJSLIDERS_H
+
+#include "kjwidget.h"
+#include <qpainter.h>
+
+class KJLoader;
+class KJPitchText;
+class KJVolumeText;
+
+
+class KJVolumeBMP : public KJWidget
+{
+public:
+ KJVolumeBMP(const QStringList &, KJLoader *parent);
+
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &pos);
+ virtual void mouseRelease(const QPoint &pos, bool);
+ virtual void timeUpdate(int);
+ virtual void mouseMove(const QPoint &pos, bool);
+
+ virtual QString tip();
+
+ void setText(KJVolumeText *t) { mText=t; }
+
+private:
+ QPixmap mImages;
+ QImage mPos;
+ int mVolume, mOldVolume;
+ int mWidth, mCount;
+ KJVolumeText *mText;
+};
+
+
+class KJVolumeBar : public KJWidget
+{
+public:
+ KJVolumeBar(const QStringList &, KJLoader *parent);
+
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &pos);
+ virtual void mouseRelease(const QPoint &pos, bool);
+ virtual void timeUpdate(int);
+ virtual void mouseMove(const QPoint &pos, bool);
+
+ virtual QString tip();
+
+ void setText(KJVolumeText *t) { mText=t; }
+
+private:
+ QPixmap mSlider;
+ QPixmap mBack;
+ int mVolume;
+ KJVolumeText *mText;
+};
+
+
+class KJPitchBMP : public KJWidget
+{
+public:
+ KJPitchBMP(const QStringList &, KJLoader *parent);
+
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &pos);
+ virtual void mouseRelease(const QPoint &pos, bool);
+ virtual void timeUpdate(int);
+ virtual void newFile();
+ virtual void mouseMove(const QPoint &pos, bool);
+ virtual void readConfig();
+
+ virtual QString tip();
+
+ void setText(KJPitchText *t) { mText=t; }
+
+private:
+ QPixmap mImages;
+ QImage mPos;
+ int mWidth, mCount;
+ float mCurrentPitch;
+ float mOldPitch;
+ float mMinPitch;
+ float mMaxPitch;
+
+ KJPitchText *mText;
+};
+
+#endif
diff --git a/noatun/modules/kjofol-skin/kjtextdisplay.cpp b/noatun/modules/kjofol-skin/kjtextdisplay.cpp
new file mode 100644
index 00000000..89f92526
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjtextdisplay.cpp
@@ -0,0 +1,650 @@
+/***************************************************************************
+ kjtexdisplay.cpp
+ ---------------------------------------------
+ Displays for time and other things
+ using fonts provided by KJFont
+ ---------------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+// local includes
+#include "kjtextdisplay.h"
+#include "kjfont.h"
+#include "kjprefs.h"
+
+// kde includes
+#include <klocale.h>
+#include <kdebug.h>
+#include <kpixmap.h>
+#include <kurl.h>
+#include <krun.h>
+#include <kmimemagic.h>
+
+// arts-includes, needed for pitch
+#include <artsmodules.h>
+#include <arts/reference.h>
+#include <arts/soundserver.h>
+#include <arts/kmedia2.h>
+
+// noatun includes
+#include <noatun/player.h>
+#include <noatun/engine.h>
+
+/*******************************************
+ * KJFilename
+ *******************************************/
+
+KJFilename::KJFilename(const QStringList &l, KJLoader *p)
+ : QObject(0), KJWidget(p), mBack(0)
+{
+ int x = l[1].toInt();
+ int y = l[2].toInt();
+ int xs = l[3].toInt() - x;
+ int ys = l[4].toInt() - y;
+
+ // fix for all those weird skins where the filenamewindow has more
+ // height than needed for the font
+ // ( ... usually resulting in garbage on-screen )
+ if ( ys > (textFont().fontHeight()) )
+ ys = textFont().fontHeight();
+
+ // background under filename-scroller
+ QPixmap tmp = p->pixmap(p->item("backgroundimage")[1]);
+ mBack = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mBack, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ setRect(x,y,xs,ys);
+
+ // how far it moves per cycle
+ // TODO: make that configurable for the user
+
+ //mDistance = 1;
+// mDistance = (int)(textFont().fontWidth()/2);
+ readConfig();
+
+ prepareString(i18n("Welcome to Noatun").local8Bit());
+ killTimers();
+}
+
+KJFilename::~KJFilename()
+{
+ delete mBack;
+}
+
+void KJFilename::paint(QPainter *p, const QRect &)
+{
+ QPixmap temp ( rect().width(), rect().height() );
+
+ // draw background into buffer
+ bitBlt ( &temp, 0, 0, mBack, 0, 0, -1, -1, Qt::CopyROP );
+ // draw font into buffer
+ bitBlt( &temp, 0, 0, &mView, 0, 0, rect().width(), rect().height(), Qt::CopyROP);
+ // and draw it on screen
+ bitBlt(p->device(), rect().topLeft(), &temp,
+ QRect(0,0,-1,-1), Qt::CopyROP);
+}
+
+void KJFilename::timerEvent(QTimerEvent *)
+{
+ int height = mView.height();
+ int width = mView.width();
+
+ QBitmap cycleMask ( mDistance, height ); // temporary-space for moving parts of the mask
+ QPixmap cycle ( mDistance, height ); // temporary-space for moving parts of the pixmap
+ QBitmap newMask ( *mView.mask() ); // save old mask
+
+ // copy mask like the same way we're doing it with the pixmap
+ // a mask does not get copied on a bitblt automatically, we have to do
+ // it "by hand"
+ bitBlt(&cycleMask, 0,0, &newMask, 0,0, mDistance, height, Qt::CopyROP);
+ bitBlt(&newMask, 0,0, &newMask, mDistance, 0, width-mDistance, height, Qt::CopyROP);
+ bitBlt(&newMask, width-mDistance, 0, &cycleMask, 0,0, mDistance, height, Qt::CopyROP);
+
+ bitBlt(&cycle, 0,0, &mView, 0,0, mDistance, height, Qt::CopyROP);
+ bitBlt(&mView, 0,0, &mView, mDistance, 0, width-mDistance, height, Qt::CopyROP);
+ bitBlt(&mView, width-mDistance, 0, &cycle, 0,0, mDistance, height, Qt::CopyROP);
+
+ // apply the newly created mask
+ mView.setMask(newMask);
+
+ repaint();
+}
+
+bool KJFilename::mousePress(const QPoint &)
+{
+ return true;
+}
+
+void KJFilename::mouseRelease(const QPoint &, bool in)
+{
+ if (!in) // only do something if users is still inside the button
+ return;
+
+ if ( !napp->player()->current() )
+ return;
+
+ KURL dirURL = napp->player()->current().url().upURL();
+
+ KMimeMagicResult *result = KMimeMagic::self()->findFileType( dirURL.path() );
+
+ // TODO: Maybe test for protocol type?
+// if ( napp->player()->current().url().protocol() == "file" )
+ if ( result->isValid() )
+ KRun::runURL ( dirURL, result->mimeType() );
+}
+
+void KJFilename::readConfig()
+{
+ kdDebug(66666) << "KJFilename::readConfig()" << endl;
+ mDistance = (int)( textFont().fontWidth() * KJLoader::kjofol->prefs()->titleMovingDistance() );
+ if ( mDistance <= 0 )
+ mDistance = 1;
+ mTimerUpdates = KJLoader::kjofol->prefs()->titleMovingUpdates();
+ textFont().recalcSysFont();
+ mLastTitle=""; // invalidate title so it gets repainted on next timeUpdate()
+}
+
+void KJFilename::prepareString(const QCString &str)
+{
+ killTimers(); // i.e. stop timers
+
+ mView = textFont().draw(str, rect().width());
+
+ startTimer(mTimerUpdates);
+}
+
+void KJFilename::timeUpdate(int)
+{
+ if ( !napp->player()->current() ) // just for safety
+ return;
+
+ QCString title = QCString( napp->player()->current().title().local8Bit() );
+
+ if ( title == mLastTitle )
+ return;
+
+ mLastTitle = title;
+
+ QCString timestring = napp->player()->lengthString().local8Bit();
+ timestring = timestring.mid(timestring.find('/')+1);
+ prepareString ( title + " (" + timestring + ") ");
+}
+
+QString KJFilename::tip()
+{
+ if ( !napp->player()->current() ) // just for safety
+ return i18n("Filename");
+ else
+ return napp->player()->current().url().prettyURL();
+}
+
+
+/*******************************************
+ * KJTime
+ *******************************************/
+
+KJTime::KJTime(const QStringList &l, KJLoader *p)
+ : KJWidget(p), mBack(0)
+{
+ int x = l[1].toInt();
+ int y = l[2].toInt();
+ int xs = l[3].toInt() - x;
+ int ys = l[4].toInt() - y;
+
+ // fix for all those weird skins where the timewindow
+ // has more space than needed for the font
+ int maxNeededHeight = timeFont().fontHeight();
+ if ( ys > maxNeededHeight )
+ ys = maxNeededHeight;
+
+ // five digits + spacing between them
+ int maxNeededWidth = ( 5 *timeFont().fontWidth() ) + ( 4 * timeFont().fontSpacing() );
+ if ( xs > maxNeededWidth )
+ xs = maxNeededWidth;
+
+ // background under time-display
+ QPixmap tmp = p->pixmap(p->item("backgroundimage")[1]);
+ mBack = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mBack, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ setRect(x,y,xs,ys);
+
+ readConfig();
+
+ prepareString("00:00");
+}
+
+KJTime::~KJTime()
+{
+ delete mBack;
+}
+
+void KJTime::paint(QPainter *p, const QRect &)
+{
+// kdDebug(66666) << "KJTime::paint(QPainter *p, const QRect &)" << endl;
+ QPixmap temp ( rect().width(), rect().height() );
+
+ // draw background into buffer
+ bitBlt ( &temp, 0, 0, mBack, 0, 0, -1, -1, Qt::CopyROP );
+ // draw time-display into buffer (that's a pixmap with a mask applied)
+ bitBlt( &temp, 0, 0, &mTime, 0, 0, rect().width(), rect().height(), Qt::CopyROP);
+
+ // and draw it on screen
+ bitBlt(p->device(), rect().topLeft(), &temp,
+ QRect(0,0, rect().width(), rect().height()), Qt::CopyROP);
+}
+
+bool KJTime::mousePress(const QPoint &)
+{
+ return true;
+}
+
+void KJTime::mouseRelease(const QPoint &, bool in)
+{
+ if (!in) // only do something if users is still inside the button
+ return;
+
+ countDown = !countDown;
+ napp->setDisplayRemaining( countDown );
+// KJLoader::kjofol->prefs()->setTimeCountMode( countDown );
+}
+
+void KJTime::readConfig()
+{
+// kdDebug(66666) << "KJTime::readConfig()" << endl;
+ countDown = napp->displayRemaining();
+ timeFont().recalcSysFont();
+ mLastTime=""; // invalidate time so it gets repainted on next timeUpdate()
+}
+
+QString KJTime::lengthString ( void )
+{
+ int pos = 0;
+ QString posString;
+ int secs = 0,
+ seconds = 0,
+ minutes = 0,
+ hours = 0;
+
+ if ( countDown )
+ { // current remaining time
+ pos = napp->player()->getLength() - napp->player()->getTime();
+ }
+ else
+ { // current time
+ pos = napp->player()->getTime();
+ }
+
+ if ( pos < 0 )
+ {
+ posString = "00:00";
+ }
+ else
+ { // get the position
+ secs = pos / 1000; // convert milliseconds -> seconds
+
+ seconds = secs % 60;
+ minutes = (secs - seconds) / 60;
+ hours = minutes / 60;
+ minutes %= 60; // remove the hours from minutes ;)
+
+// cerr << " " << hours << ":" << minutes << ":" << seconds << endl;
+
+// if ( hours > 0 ) // looks ugly :)
+ if ( (napp->player()->getLength()/1000) >= 3600 ) // displays hh:mm if file is long
+ {
+ posString.sprintf("%d:%.2d", hours, minutes);
+ }
+ else // displays mm:ss
+ {
+ posString.sprintf("%.2d:%.2d", minutes, seconds);
+ }
+ }
+
+ return posString;
+}
+
+void KJTime::timeUpdate(int)
+{
+// kdDebug(66666) << "START KJTime::timeUpdate(int)" << endl;
+ if (!napp->player()->current())
+ return;
+
+ prepareString( (lengthString()).latin1() );
+
+// kdDebug(66666) << "END KJTime::timeUpdate(int)" << endl;
+}
+
+void KJTime::prepareString(const QCString &str)
+{
+// kdDebug(66666) << "START KJTime::prepareString(const QCString &str)" << endl;
+ if ( str == mLastTime )
+ return;
+
+ mLastTime = str;
+ mTime = timeFont().draw(str, rect().width());
+
+ repaint();
+// kdDebug(66666) << "END KJTime::prepareString(const QCString &str)" << endl;
+}
+
+QString KJTime::tip()
+{
+ if ( countDown )
+ return i18n("Play time left");
+ else
+ return i18n("Current play time");
+}
+
+
+/*******************************************
+ * KJVolumeText
+ *******************************************/
+
+KJVolumeText::KJVolumeText(const QStringList &l, KJLoader *p)
+ : KJWidget(p), mBack(0)
+{
+ int x=l[1].toInt();
+ int y=l[2].toInt();
+ int xs=l[3].toInt()-x;
+ int ys=l[4].toInt()-y;
+
+ // fix for all those weird skins where the timewindow has more space than needed for the font
+ if ( ys > (volumeFont().fontHeight()) )
+ ys = volumeFont().fontHeight();
+
+ // 3 digits for volume (1-100)
+ // + spaces according to spacing
+ // + percentage letter (seems to be 1px wider than a normal char)
+ int tempWidth = (3*volumeFont().fontWidth()) + (2*volumeFont().fontSpacing()) + ((volumeFont().fontWidth()+1));
+ if ( xs > ( tempWidth ) )
+ xs = tempWidth;
+
+ // background under volumetext-display
+ QPixmap tmp = p->pixmap(p->item("backgroundimage")[1]);
+ mBack = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mBack, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ setRect(x,y,xs,ys);
+
+ prepareString("100%");
+}
+
+KJVolumeText::~KJVolumeText()
+{
+ delete mBack;
+}
+
+void KJVolumeText::paint(QPainter *p, const QRect &)
+{
+ QPixmap temp ( rect().width(), rect().height() );
+
+ // draw background into buffer
+ bitBlt ( &temp, 0, 0, mBack, 0, 0, -1, -1, Qt::CopyROP );
+ // draw time-display into buffer
+ bitBlt( &temp, 0, 0, &mVolume, 0, 0, rect().width(), rect().height(), Qt::CopyROP);
+
+ // and draw it on screen
+ bitBlt(p->device(), rect().topLeft(), &temp,
+ QRect(0,0,-1,-1), Qt::CopyROP);
+}
+
+bool KJVolumeText::mousePress(const QPoint &)
+{
+ return false;
+}
+
+void KJVolumeText::readConfig()
+{
+ volumeFont().recalcSysFont();
+ mLastVolume=""; // invalidate value so it gets repainted on next timeUpdate()
+}
+
+void KJVolumeText::timeUpdate(int)
+{
+ QCString volume;
+
+ if (!napp->player()->current())
+ return;
+
+ volume.sprintf("%d%%", napp->player()->volume() ); // FIXME: is sprintf safe to use?
+
+ prepareString(volume);
+}
+
+void KJVolumeText::prepareString(const QCString &str)
+{
+ if ( str == mLastVolume )
+ return;
+
+ mLastVolume = str;
+ mVolume = volumeFont().draw(str, rect().width());
+
+ repaint();
+}
+
+QString KJVolumeText::tip()
+{
+ return i18n("Volume");
+}
+
+
+/*******************************************
+ * KJPitchText
+ *******************************************/
+
+KJPitchText::KJPitchText(const QStringList &l, KJLoader *p)
+ : KJWidget(p), mBack(0)
+{
+ int x = l[1].toInt();
+ int y = l[2].toInt();
+ int xs = l[3].toInt() - x;
+ int ys = l[4].toInt() - y;
+
+ // fix for all those weird skins where the timewindow has more space than needed for the font
+ if ( ys > (pitchFont().fontHeight()) )
+ ys = pitchFont().fontHeight();
+
+ // 3 digits for volume (1-100), spaces according to spacing and percentage letter
+ int tempWidth = (3*pitchFont().fontWidth()) + (2*pitchFont().fontSpacing());
+ if ( xs > tempWidth )
+ xs = tempWidth;
+
+ // background under time-display
+ QPixmap tmp = p->pixmap(p->item("backgroundimage")[1]);
+ mBack = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mBack, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ setRect(x,y,xs,ys);
+
+ prepareString("100");
+}
+
+KJPitchText::~KJPitchText()
+{
+ delete mBack;
+}
+
+
+void KJPitchText::paint(QPainter *p, const QRect &)
+{
+ QPixmap temp ( rect().width(), rect().height() );
+
+ // draw background into buffer
+ bitBlt ( &temp, 0, 0, mBack, 0, 0, -1, -1, Qt::CopyROP );
+ // draw time-display into buffer
+ bitBlt( &temp, 0, 0, &mSpeed, 0, 0, rect().width(), rect().height(), Qt::CopyROP);
+ // and draw it on screen
+ bitBlt(p->device(), rect().topLeft(), &temp, QRect(0,0,-1,-1), Qt::CopyROP);
+}
+
+bool KJPitchText::mousePress(const QPoint &)
+{
+ return true;
+}
+
+void KJPitchText::mouseRelease(const QPoint &, bool in)
+{
+ if (!in)
+ return;
+
+ Arts::PlayObject playobject = napp->player()->engine()->playObject();
+ Arts::PitchablePlayObject pitchable = Arts::DynamicCast(playobject);
+
+ if (pitchable.isNull())
+ return;
+
+ pitchable.speed( 1.00f ); // reset pitch
+}
+
+void KJPitchText::readConfig()
+{
+ pitchFont().recalcSysFont();
+ mLastPitch=""; // invalidate value so it gets repainted on next timeUpdate()
+}
+
+void KJPitchText::timeUpdate(int)
+{
+ QCString speed;
+
+ if (!napp->player()->current())
+ return;
+
+ Arts::PlayObject playobject = napp->player()->engine()->playObject();
+ Arts::PitchablePlayObject pitchable = Arts::DynamicCast(playobject);
+
+ if (pitchable.isNull())
+ return;
+
+ speed.setNum ( (int) ((float)pitchable.speed()*(float)100) );
+ prepareString ( speed );
+}
+
+void KJPitchText::prepareString(const QCString &str)
+{
+ if ( str == mLastPitch )
+ return;
+
+ mLastPitch = str;
+ mSpeed = pitchFont().draw(str, rect().width());
+
+ repaint();
+}
+
+QString KJPitchText::tip()
+{
+ return i18n("Pitch");
+}
+
+
+/*******************************************
+ * KJFileInfo
+ *******************************************/
+
+KJFileInfo::KJFileInfo(const QStringList &l, KJLoader *p)
+ : KJWidget(p), mBack(0)
+{
+ mInfoType = l[0]; // type of info-display
+
+ int x = l[1].toInt();
+ int y = l[2].toInt();
+ int xs = l[3].toInt() - x;
+ int ys = l[4].toInt() - y;
+
+ // fix for all those weird skins where the timewindow
+ // has more space than needed for the font
+ int maxNeededHeight = timeFont().fontHeight();
+ if ( ys > maxNeededHeight )
+ ys = maxNeededHeight;
+
+ // five digits + spacing between them
+ int maxNeededWidth = ( 3 *timeFont().fontWidth() ) + ( 2 * timeFont().fontSpacing() );
+ if ( xs > maxNeededWidth )
+ xs = maxNeededWidth;
+
+ // background under info-display
+ QPixmap tmp = p->pixmap(p->item("backgroundimage")[1]);
+ mBack = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mBack, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ setRect(x,y,xs,ys);
+
+ prepareString("");
+}
+
+KJFileInfo::~KJFileInfo()
+{
+ delete mBack;
+}
+
+void KJFileInfo::paint(QPainter *p, const QRect &)
+{
+ QPixmap temp ( rect().width(), rect().height() );
+
+ // draw background into buffer
+ bitBlt ( &temp, 0, 0, mBack, 0, 0, -1, -1, Qt::CopyROP );
+ // draw time-display into buffer (that's a pixmap with a mask applied)
+ bitBlt( &temp, 0, 0, &mTime, 0, 0, rect().width(), rect().height(), Qt::CopyROP);
+
+ // and draw it on screen
+ bitBlt(p->device(), rect().topLeft(), &temp,
+ QRect(0,0, rect().width(), rect().height()), Qt::CopyROP);
+}
+
+bool KJFileInfo::mousePress(const QPoint &)
+{
+ return false;
+}
+
+void KJFileInfo::readConfig()
+{
+ textFont().recalcSysFont();
+ mLastTime=""; // invalidate value so it gets repainted on next timeUpdate()
+}
+
+void KJFileInfo::timeUpdate(int)
+{
+ if (!napp->player()->current())
+ return;
+
+ const PlaylistItem &item = napp->player()->current();
+ QString prop;
+
+ if ( mInfoType == "mp3khzwindow" )
+ {
+ prop = item.property("samplerate");
+ prop.truncate(2); // we just want 44 instead of 44100
+ }
+ else if ( mInfoType == "mp3kbpswindow" )
+ {
+ prop = item.property("bitrate");
+ }
+ else // for safety: no infoType we know of
+ return;
+
+ if (prop.isNull())
+ prop="";
+ prepareString( prop.latin1() );
+}
+
+void KJFileInfo::prepareString(const QCString &str)
+{
+ if ( str == mLastTime )
+ return;
+ mLastTime = str;
+ mTime = textFont().draw(str, rect().width());
+ repaint();
+}
+
+QString KJFileInfo::tip()
+{
+ if ( mInfoType == "mp3khzwindow" )
+ return i18n("Sample rate in kHz");
+ else if ( mInfoType == "mp3kbpswindow" )
+ return i18n("Bitrate in kbps");
+
+ return QString();
+}
+
+#include "kjtextdisplay.moc"
diff --git a/noatun/modules/kjofol-skin/kjtextdisplay.h b/noatun/modules/kjofol-skin/kjtextdisplay.h
new file mode 100644
index 00000000..11098b0c
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjtextdisplay.h
@@ -0,0 +1,139 @@
+#ifndef KJTEXTDISPLAY_H
+#define KJTEXTDISPLAY_H
+
+#include "kjwidget.h"
+class KJLoader;
+class KPixmap;
+//#include "kjloader.h"
+
+#include <qobject.h>
+#include <qpainter.h>
+
+class KJFilename : public QObject, public KJWidget
+{
+Q_OBJECT
+public:
+ KJFilename(const QStringList &, KJLoader *parent);
+ ~KJFilename();
+
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &pos);
+ virtual void mouseRelease(const QPoint &, bool in);
+// virtual void newFile();
+ virtual void timeUpdate(int);
+ virtual void readConfig();
+
+ void prepareString(const QCString &str);
+ virtual QString tip();
+
+ virtual void timerEvent(QTimerEvent *);
+
+private:
+ QCString mLastTitle;
+ int mDistance;
+ int mTimerUpdates;
+ int mWidth;
+ int mTickerPos;
+ QPixmap mView;
+ KPixmap *mBack;
+};
+
+
+class KJTime : public KJWidget
+{
+public:
+ KJTime(const QStringList &, KJLoader *parent);
+ ~KJTime();
+
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &pos);
+ virtual void mouseRelease(const QPoint &, bool in);
+ virtual void timeUpdate(int);
+ virtual void readConfig();
+
+ void prepareString(const QCString &time);
+ virtual QString tip();
+
+// enum countModes { Up=0, Down };
+
+private:
+ QCString mLastTime;
+ int mWidth;
+ bool countDown;
+ QPixmap mTime;
+ KPixmap *mBack;
+
+private:
+ QString lengthString ( void );
+
+};
+
+
+class KJVolumeText : public KJWidget
+{
+public:
+ KJVolumeText(const QStringList &, KJLoader *parent);
+ ~KJVolumeText();
+
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &pos);
+ virtual void timeUpdate(int);
+ virtual void readConfig();
+
+ void prepareString(const QCString &time);
+ virtual QString tip();
+
+private:
+ QCString mLastVolume;
+ int mWidth;
+ QPixmap mVolume;
+ KPixmap *mBack;
+};
+
+
+class KJPitchText : public KJWidget
+{
+public:
+ KJPitchText(const QStringList &, KJLoader *parent);
+ ~KJPitchText();
+
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &pos);
+ virtual void mouseRelease(const QPoint &, bool in);
+ virtual void timeUpdate(int);
+ virtual void readConfig();
+
+ void prepareString(const QCString &time);
+ virtual QString tip();
+
+private:
+ QCString mLastPitch;
+ int mWidth;
+ QPixmap mSpeed;
+ KPixmap *mBack;
+};
+
+
+class KJFileInfo : public KJWidget
+{
+public:
+ KJFileInfo(const QStringList &, KJLoader *parent);
+ ~KJFileInfo();
+
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &pos);
+ virtual void timeUpdate(int);
+ virtual void readConfig();
+
+ void prepareString(const QCString &time);
+ virtual QString tip();
+
+private:
+ QCString mLastTime;
+ QString mInfoType;
+ int mWidth;
+ QPixmap mTime;
+ KPixmap *mBack;
+};
+
+#endif
diff --git a/noatun/modules/kjofol-skin/kjvis.cpp b/noatun/modules/kjofol-skin/kjvis.cpp
new file mode 100644
index 00000000..71246089
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjvis.cpp
@@ -0,0 +1,538 @@
+/***************************************************************************
+ kjvis.cpp - Visualizations used in the KJfol-GUI
+ --------------------------------------
+ Maintainer: Stefan Gehn <metz AT gehn.net>
+
+ ***************************************************************************/
+
+// local includes
+#include "kjvis.h"
+#include "kjprefs.h"
+
+// system includes
+#include <math.h>
+
+//qt includes
+#include <qpainter.h>
+#include <qsize.h>
+
+//kde includes
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kconfig.h>
+#include <kpixmapeffect.h>
+#include <kpixmap.h>
+
+// noatun includes
+#include <noatun/player.h>
+
+#define _KJ_GRADIENT_DIFF 130
+
+/*******************************************
+ * KJFFTScope
+ *******************************************/
+
+void KJVisScope::swapScope(Visuals newOne)
+{
+ //kdDebug(66666) << k_funcinfo << endl;
+ QStringList line = parent()->item("analyzerwindow");
+ KJLoader *p=parent();
+ p->removeChild(this);
+ delete this;
+
+ KJLoader::kjofol->prefs()->setVisType ( newOne );
+
+ KJWidget *w = 0;
+ switch (newOne)
+ {
+ case Null:
+ w = new KJNullScope(line, p);
+ break;
+ case FFT:
+ w = new KJFFT(line, p);
+ break;
+ case StereoFFT:
+ w = new KJStereoFFT(line, p);
+ break;
+ case Mono:
+ w = new KJScope(line, p);
+ break;
+ };
+
+ p->addChild(w);
+}
+
+/*******************************************
+ * KJNullScope
+ *******************************************/
+
+KJNullScope::KJNullScope(const QStringList &l, KJLoader *parent)
+ : KJVisScope(parent)
+{
+ int x = l[1].toInt();
+ int y = l[2].toInt();
+ int xs = l[3].toInt() - x;
+ int ys = l[4].toInt() - y;
+
+ // background under vis
+ QPixmap tmp = parent->pixmap(parent->item("backgroundimage")[1]);
+ mBack = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mBack, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+ setRect ( x, y, xs, ys );
+ repaint();
+}
+
+void KJNullScope::paint(QPainter *p, const QRect &)
+{
+ // just redraw the background
+ bitBlt ( p->device(), rect().topLeft(), mBack, QRect(0,0,-1,-1), Qt::CopyROP );
+}
+
+bool KJNullScope::mousePress(const QPoint &)
+{
+ return true;
+}
+
+void KJNullScope::mouseRelease(const QPoint &, bool in)
+{
+ if (!in) // only do something if users is still inside the button
+ return;
+
+ parent()->repaint(rect(), false);
+ swapScope(FFT);
+}
+
+void KJNullScope::readConfig()
+{
+// kdDebug(66666) << "[KJNullScope] readConfig()" << endl;
+ Visuals v = (Visuals) KJLoader::kjofol->prefs()->visType();
+ if ( v != Null )
+ {
+ parent()->repaint(rect(), false);
+ swapScope ( v );
+ }
+}
+
+
+/*************************************************
+ * KJFFT - Analyzer like visualization, mono
+ *************************************************/
+
+KJFFT::KJFFT(const QStringList &l, KJLoader *parent)
+ : KJVisScope(parent), MonoFFTScope(50), mGradient(0)
+{
+ int x = l[1].toInt();
+ int y = l[2].toInt();
+ int xs = l[3].toInt()-x;
+ int ys = l[4].toInt()-y;
+
+ // each bar will be 1px wide
+ mMultiples=1;
+
+ if ( parent->exist("analyzercolor") )
+ {
+ QStringList &col = parser()["analyzercolor"];
+ mColor.setRgb ( col[1].toInt(), col[2].toInt(), col[3].toInt() );
+ }
+ else // TODO: what should be default colors for Vis?
+ {
+ mColor.setRgb ( 255, 255, 255 ); // white is default
+ }
+
+ // background under vis
+ QPixmap tmp = parent->pixmap(parent->item("backgroundimage")[1]);
+ mBack = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mBack, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ mAnalyzer = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mAnalyzer, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ // create a gradient for the bars going from 30% lighter to 30% darker than mColor
+ mGradient = new KPixmap ( QSize(xs,ys) );
+ KPixmapEffect::gradient ( *mGradient, mColor.light(_KJ_GRADIENT_DIFF),
+ mColor.dark(_KJ_GRADIENT_DIFF), KPixmapEffect::VerticalGradient );
+
+ setRect (x,y,xs,ys);
+ setBands(magic(xs/mMultiples));
+ readConfig(); // read our config settings
+ start();
+}
+
+void KJFFT::scopeEvent(float *d, int size)
+{
+ if ( !napp->player()->isPlaying() ) // don't draw if we aren't playing (either paused or stopped)
+ {
+ if ( napp->player()->isStopped() ) // clear vis-window if playing has been stopped
+ parent()->repaint(rect(), false);
+ return;
+ }
+
+ int x = 0;
+ int h = rect().height();
+
+ QBitmap mGradientMask ( rect().width(), h, true );
+ QPainter mask( &mGradientMask );
+
+ float *start = d ;
+ float *end = d + size /*- 1*/;
+
+ // loop creating the mask for vis-gradient
+ for ( ; start < end; ++start )
+ {
+ // 5 has been 8 before and I have no idea how this scaling works :/
+ // FIXME: somebody please make it scale to 100% of height,
+ // I guess that would be nicer to look at
+// float n = log((*start)+1) * (float)h * 5;
+ float n = log((*start)+1) * (float)h * 5;
+ int amp=(int)n;
+
+ // range check
+ if ( amp < 0 ) amp = 0;
+ else if ( amp > h ) amp = h;
+
+ // make a part of the analyzer-gradient visible
+ mask.fillRect ( x, (h-amp), mMultiples, amp, Qt::color1 );
+ x += mMultiples;
+ }
+ // done creating our mask
+
+ // draw background of vis into it
+ bitBlt ( mAnalyzer, 0, 0, mBack, 0, 0, -1, -1, Qt::CopyROP );
+
+ // draw the analyzer
+ mGradient->setMask(mGradientMask);
+ bitBlt ( mAnalyzer, 0, 0, mGradient, 0, 0, -1, -1, Qt::CopyROP );
+
+ repaint();
+}
+
+void KJFFT::paint(QPainter *p, const QRect &)
+{
+ // put that thing on screen
+ if ( !napp->player()->isStopped() )
+ bitBlt ( p->device(), rect().topLeft(), mAnalyzer, QRect(0,0,-1,-1), Qt::CopyROP );
+}
+
+
+bool KJFFT::mousePress(const QPoint &)
+{
+ return true;
+}
+
+void KJFFT::mouseRelease(const QPoint &, bool in)
+{
+ if (!in) // only do something if users is still inside the button
+ return;
+
+ stop();
+ parent()->repaint(rect(), false);
+ swapScope(Mono);
+}
+
+void KJFFT::readConfig()
+{
+// kdDebug(66666) << "[KJFFT] readConfig()" << endl;
+ Visuals v = (Visuals) KJLoader::kjofol->prefs()->visType();
+ if ( v != FFT )
+ {
+ stop();
+ parent()->repaint(rect(), false);
+ swapScope ( v );
+ return;
+ }
+
+ mTimerValue = KJLoader::kjofol->prefs()->visTimerValue();
+ setInterval( mTimerValue );
+}
+
+
+/*************************************************
+ * KJStereoFFT - Analyzer like visualization, stereo
+ *************************************************/
+
+KJStereoFFT::KJStereoFFT(const QStringList &l, KJLoader *parent)
+ : KJVisScope(parent), StereoFFTScope(50), mGradient(0)
+{
+ //kdDebug(66666) << k_funcinfo << endl;
+
+ int x = l[1].toInt();
+ int y = l[2].toInt();
+ int xs = l[3].toInt()-x;
+ int ys = l[4].toInt()-y;
+
+ // each bar will be 1px wide
+ mMultiples=1;
+
+ if ( parent->exist("analyzercolor") )
+ {
+ QStringList &col = parser()["analyzercolor"];
+ mColor.setRgb ( col[1].toInt(), col[2].toInt(), col[3].toInt() );
+ }
+ else // TODO: what should be default colors for Vis?
+ {
+ mColor.setRgb ( 255, 255, 255 ); // white is default
+ }
+
+ // background under vis
+ QPixmap tmp = parent->pixmap(parent->item("backgroundimage")[1]);
+ mBack = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mBack, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ mAnalyzer = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mAnalyzer, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ // create a gradient for the bars going from 30% lighter to 30% darker than mColor
+ mGradient = new KPixmap ( QSize(xs,ys) );
+ KPixmapEffect::gradient ( *mGradient, mColor.light(_KJ_GRADIENT_DIFF),
+ mColor.dark(_KJ_GRADIENT_DIFF), KPixmapEffect::VerticalGradient );
+
+ setRect (x,y,xs,ys);
+ setBands(magic(xs/mMultiples));
+ readConfig(); // read our config settings
+ start();
+}
+
+void KJStereoFFT::scopeEvent(float *left, float *right, int len)
+{
+ if ( !napp->player()->isPlaying() ) // don't draw if we aren't playing (either paused or stopped)
+ {
+ if ( napp->player()->isStopped() ) // clear vis-window if playing has been stopped
+ parent()->repaint(rect(), false);
+ return;
+ }
+
+ unsigned int h = rect().height();
+ int hh = (int)(rect().height()/2);
+
+ QBitmap mGradientMask ( rect().width(), h, true );
+ QPainter mask( &mGradientMask );
+
+ float *start = left;
+ float *end = left + len;
+ float n = 0.0;
+ int amp = 0;
+ int x = 0;
+
+ // loop creating the mask for vis-gradient
+ for ( ; start < end; ++start )
+ {
+ n = log((*start)+1) * (float)hh * 5;
+ amp = (int)n;
+
+ // range check
+ if ( amp < 0 ) amp = 0;
+ else if ( amp > hh ) amp = hh;
+
+ // make a part of the analyzer-gradient visible
+ mask.fillRect ( x, (h-amp), mMultiples, amp, Qt::color1 );
+ x += mMultiples;
+ }
+ // done creating our mask
+
+
+ start = right;
+ end = right + len;
+ x = 0;
+ // loop creating the mask for vis-gradient
+ for ( ; start < end; ++start )
+ {
+ n = log((*start)+1) * (float)hh * 5;
+ amp = (int)n;
+
+ // range check
+ if ( amp < 0 ) amp = 0;
+ else if ( amp > hh ) amp = hh;
+
+ // make a part of the analyzer-gradient visible
+ mask.fillRect ( x, 0, mMultiples, amp, Qt::color1 );
+ x += mMultiples;
+ }
+
+
+ // draw background of vis into it
+ bitBlt ( mAnalyzer, 0, 0, mBack, 0, 0, -1, -1, Qt::CopyROP );
+
+ // draw the analyzer
+ mGradient->setMask(mGradientMask);
+ bitBlt ( mAnalyzer, 0, 0, mGradient, 0, 0, -1, -1, Qt::CopyROP );
+
+ repaint();
+}
+
+void KJStereoFFT::paint(QPainter *p, const QRect &)
+{
+ // put that thing on screen
+ if ( !napp->player()->isStopped() )
+ bitBlt ( p->device(), rect().topLeft(), mAnalyzer, QRect(0,0,-1,-1), Qt::CopyROP );
+}
+
+bool KJStereoFFT::mousePress(const QPoint &)
+{
+ return true;
+}
+
+void KJStereoFFT::mouseRelease(const QPoint &, bool in)
+{
+ if (!in) // only do something if users is still inside the button
+ return;
+ stop();
+ parent()->repaint(rect(), false);
+ swapScope(Null);
+}
+
+void KJStereoFFT::readConfig()
+{
+ //kdDebug(66666) << k_funcinfo << endl;
+ Visuals v = (Visuals) KJLoader::kjofol->prefs()->visType();
+ if ( v != StereoFFT )
+ {
+ stop();
+ parent()->repaint(rect(), false);
+ swapScope ( v );
+ return;
+ }
+ setInterval(KJLoader::kjofol->prefs()->visTimerValue());
+}
+
+
+/*************************************************
+ * KJScope - oscilloscope like visualization
+ *************************************************/
+
+KJScope::KJScope(const QStringList &l, KJLoader *parent)
+ : KJVisScope(parent), MonoScope(50)/*, blurnum(0), mOsci(0)*/
+{
+ int x=l[1].toInt();
+ int y=l[2].toInt();
+ int xs = mWidth = l[3].toInt()-x;
+ int ys = mHeight = l[4].toInt()-y;
+
+ blurnum = 0;
+
+// kdDebug(66666) << "Analyzer Window " << x << "," << y << " " << mWidth << "," << mHeight << endl;
+
+ if ( parent->exist("analyzercolor") )
+ {
+ QStringList &col = parser()["analyzercolor"];
+ mColor.setRgb ( col[1].toInt(), col[2].toInt(), col[3].toInt() );
+ }
+ else // FIXME: what should be default colors for Vis?
+ mColor.setRgb ( 255, 255, 255 );
+
+ // background under vis
+ QPixmap tmp = parent->pixmap(parent->item("backgroundimage")[1]);
+ mBack = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mBack, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ mOsci = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mOsci, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ // create a gradient
+ mGradient = new KPixmap ( QSize(xs,ys) );
+ KPixmapEffect::gradient ( *mGradient, mColor.light(_KJ_GRADIENT_DIFF),
+ mColor.dark(_KJ_GRADIENT_DIFF), KPixmapEffect::VerticalGradient );
+
+ setRect ( x, y, xs, ys );
+
+ // set the samplewidth to the largest integer divisible by mWidth
+ setSamples ( xs );
+
+ readConfig();
+ start();
+}
+
+void KJScope::scopeEvent(float *d, int size)
+{
+ if ( !napp->player()->isPlaying() )
+ {
+ if ( napp->player()->isStopped() )
+ {
+ bitBlt ( mOsci, 0, 0, mBack, 0, 0, -1, -1, Qt::CopyROP );
+ repaint();
+ }
+ return;
+ }
+
+ float *start = d;
+ float *end = d + size;
+
+ int heightHalf = rect().height()/2 /* -1 */;
+ int x = 0;
+
+ QPainter tempP( mOsci );
+
+ if ( blurnum == 3 )
+ { // clear whole Vis
+ bitBlt ( mOsci, 0, 0, mBack, 0, 0, -1, -1, Qt::CopyROP );
+ tempP.setPen( mColor.light(110) ); // 10% lighter than mColor
+ blurnum=0;
+ }
+ else
+ {
+ blurnum++;
+ // reduce color for blur-effect
+ tempP.setPen( mColor.dark(90+(10*blurnum)) ); // darken color
+ }
+
+ for ( ; start < end; ++start )
+ {
+ float n = (*start) * (float)heightHalf;
+ int amp = (int)n;
+
+ // range check
+ if ( amp > heightHalf ) amp = heightHalf;
+ else if ( amp < -heightHalf) amp = -heightHalf;
+
+ // draw
+// tempP.drawLine(x, heightHalf, x, heightHalf+amp);
+ if ( amp > 0 )
+ {
+ bitBlt ( tempP.device(), QPoint(x,heightHalf), mGradient, QRect(x,heightHalf,1,amp), Qt::CopyROP );
+ }
+ else
+ {
+ amp = -amp;
+ bitBlt ( tempP.device(), QPoint(x,heightHalf-amp), mGradient, QRect(x,(heightHalf-amp),1,amp), Qt::CopyROP );
+ }
+ x++;
+ }
+
+ repaint();
+}
+
+void KJScope::paint(QPainter *p, const QRect &)
+{
+ // put that thing on screen
+ bitBlt ( p->device(), rect().topLeft(), mOsci, QRect(0,0,-1,-1), Qt::CopyROP );
+}
+
+bool KJScope::mousePress(const QPoint &)
+{
+ return true;
+}
+
+void KJScope::mouseRelease(const QPoint &, bool in)
+{
+ if (!in) // only do something if users is still inside the button
+ return;
+
+ stop();
+ parent()->repaint(rect(), false);
+ swapScope(/*Null*/ StereoFFT);
+}
+
+void KJScope::readConfig()
+{
+// kdDebug(66666) << "[KJScope] readConfig()" << endl;
+ Visuals v = (Visuals) KJLoader::kjofol->prefs()->visType();
+ if ( v != Mono )
+ {
+ stop();
+ parent()->repaint(rect(), false);
+ swapScope ( v );
+ return;
+ }
+
+ mTimerValue = KJLoader::kjofol->prefs()->visTimerValue();
+ setInterval( mTimerValue );
+}
diff --git a/noatun/modules/kjofol-skin/kjvis.h b/noatun/modules/kjofol-skin/kjvis.h
new file mode 100644
index 00000000..d2a43700
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjvis.h
@@ -0,0 +1,102 @@
+#ifndef KJVIS_H
+#define KJVIS_H
+
+#include "kjwidget.h"
+class KJLoader;
+class KPixmap;
+
+class KJVisScope : public KJWidget
+{
+public:
+ KJVisScope(KJLoader *parent) : KJWidget(parent) {};
+ enum Visuals { Null=0, FFT, Mono, StereoFFT };
+ void swapScope(Visuals newOne);
+// virtual void readConfig();
+};
+
+
+// dummy-scope displaying nothing
+class KJNullScope : public KJVisScope
+{
+public:
+ KJNullScope(const QStringList &, KJLoader *parent);
+ virtual void paint(QPainter *p, const QRect &);
+ virtual bool mousePress(const QPoint&);
+ virtual void mouseRelease(const QPoint &, bool in);
+ virtual void readConfig(void);
+
+private:
+ KPixmap *mBack;
+
+};
+
+
+// analyzer-like scope
+class KJFFT : public KJVisScope, public MonoFFTScope
+{
+public:
+ KJFFT(const QStringList &, KJLoader *parent);
+ virtual void paint(QPainter *p, const QRect &);
+ virtual void scopeEvent(float *d, int size);
+
+ virtual bool mousePress(const QPoint&);
+ virtual void mouseRelease(const QPoint &, bool in);
+ virtual void readConfig(void);
+
+private:
+ QColor mColor;
+ KPixmap *mGradient;
+ KPixmap *mBack;
+ KPixmap *mAnalyzer;
+ int mMultiples;
+ int mTimerValue;
+};
+
+
+// analyzer-like scope, stereo version
+class KJStereoFFT : public KJVisScope, public StereoFFTScope
+{
+public:
+ KJStereoFFT(const QStringList &, KJLoader *parent);
+ virtual void paint(QPainter *p, const QRect &);
+ virtual void scopeEvent(float *left, float *right, int len);
+
+ virtual bool mousePress(const QPoint&);
+ virtual void mouseRelease(const QPoint &, bool in);
+ virtual void readConfig(void);
+
+private:
+ QColor mColor;
+ KPixmap *mGradient;
+ KPixmap *mBack;
+ KPixmap *mAnalyzer;
+ int mMultiples;
+ int mTimerValue;
+};
+
+
+// oscilloscope showing waveform
+class KJScope : public KJVisScope, public MonoScope
+{
+public:
+ KJScope ( const QStringList &, KJLoader *parent);
+ virtual void paint(QPainter *p, const QRect &);
+ virtual void scopeEvent(float *d, int size);
+
+ virtual bool mousePress(const QPoint&);
+ virtual void mouseRelease(const QPoint &, bool in);
+ virtual void readConfig(void);
+
+private:
+ QColor mColor;
+ KPixmap *mGradient;
+ KPixmap *mBack;
+ KPixmap *mOsci;
+ int mMultiples;
+ int mWidth;
+ int mHeight;
+ unsigned int blurnum;
+ int mTimerValue;
+};
+
+#endif
diff --git a/noatun/modules/kjofol-skin/kjwidget.cpp b/noatun/modules/kjofol-skin/kjwidget.cpp
new file mode 100644
index 00000000..e7b6a4d1
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjwidget.cpp
@@ -0,0 +1,70 @@
+/***************************************************************************
+ kjwidget.cpp - Base Class for all widgets
+ --------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+// local includes
+#include "kjwidget.h"
+//#include <kdebug.h>
+
+// ugly static functions and stuff
+#include "helpers.cpp"
+
+#include <qpainter.h>
+
+KJWidget::KJWidget(KJLoader *p) : mParent(p)
+{
+}
+
+QBitmap KJWidget::getMask(const QImage &_rect, register QRgb transparent)
+{
+ QImage result(_rect.width(), _rect.height(), 1,2, QImage::LittleEndian);
+#if QT_VERSION < 0x030300
+ result.setColor(0, qRgb(0,0,0)); //TODO: maybe use Qt::color0 and Qt::color1
+ result.setColor(1, qRgb(255,255,255));
+#else
+ result.setColor(1, qRgb(0,0,0));
+ result.setColor(0, qRgb(255,255,255));
+#endif
+
+ for(int height=0;height<_rect.height(); height++)
+ {
+ for(int width=0; width<_rect.width(); width++)
+ setPixel1BPP(result, width, height, _rect.pixel(width, height)!=transparent);
+ }
+ QBitmap bm;
+ bm.convertFromImage(result);
+ return bm;
+}
+
+void KJWidget::repaint(bool me, const QRect &r, bool clear)
+{
+ QPainter p(parent());
+ if (me)
+ paint(&p, r.isValid() ? r : rect());
+ else
+ parent()->repaint(r.isValid() ? r : rect(), clear);
+}
+
+const QString &KJWidget::backgroundPressed(const QString &bmp) const
+{
+ if(bmp.isEmpty()) // play safe
+ {
+// kdDebug(66666) << k_funcinfo << "empty argument 'bmp', returning QString::null!" << endl;
+ return QString::null;
+ }
+
+// kdDebug(66666) << k_funcinfo << "Returning pressed pixmap for '" << bmp.latin1() << "'" << endl;
+
+ // make absolutely sure the wanted backgroundimagepressedX line is there
+ QStringList item = parser()["backgroundimagepressed"+QString::number(bmp.mid(3).toInt())];
+ if(item.count() < 2)
+ {
+// kdDebug(66666) << k_funcinfo << "backgroundimagepressed doesn't have enough keys in its line!" << endl;
+ return QString::null;
+ }
+ else
+ return item[1];
+}
diff --git a/noatun/modules/kjofol-skin/kjwidget.h b/noatun/modules/kjofol-skin/kjwidget.h
new file mode 100644
index 00000000..cc7ddf53
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjwidget.h
@@ -0,0 +1,53 @@
+#ifndef KJWIDGET_H
+#define KJWIDGET_H
+
+#include "kjloader.h"
+
+class KJWidget
+{
+public:
+ KJWidget(KJLoader *);
+ virtual ~KJWidget() {};
+ // called when the widget should paint
+ virtual void paint(QPainter *, const QRect &) {};
+ // called to receive the rect this widget is in
+ virtual QRect rect() const { return mRect; }
+ // called when pressed in this widget
+ virtual bool mousePress(const QPoint &) {return false; }
+ // called when the mouse is released after clicked in this widget
+ virtual void mouseRelease(const QPoint &, bool){}
+ virtual void mouseMove(const QPoint &, bool) {}
+ // called with the current time (mille)
+ virtual void timeUpdate(int) {}
+ // called when a new song is playing, player() is ready with it too
+ virtual void newFile() {}
+ // called when config-entries have to be read, is a TODO for most widgets
+ virtual void readConfig() {}
+
+ // called when the mouse is moved while clicked in this widget
+ // repaint myself
+ virtual void repaint(bool me=true, const QRect &rect=QRect(), bool clear=false);
+
+ virtual QString tip() { return 0; }
+
+public:
+ static QBitmap getMask(const QImage &color, register QRgb=qRgb(255,0,255));
+
+protected:
+ const QString &backgroundPressed(const QString &bmp) const;
+ KJLoader *parent() const {return mParent;}
+ KJLoader &parser() const {return *mParent;}
+
+ KJFont &textFont() const {return *mParent->mText;}
+ KJFont &timeFont() const {return *mParent->mNumbers;}
+ KJFont &volumeFont() const {return *mParent->mVolumeFont;}
+ KJFont &pitchFont() const {return *mParent->mPitchFont;}
+
+ void setRect(const QRect& rect) {mRect=rect;}
+ void setRect(int x, int y, int xs, int ys) {mRect=QRect(x,y,xs,ys);}
+private:
+ KJLoader *mParent;
+ QRect mRect;
+};
+
+#endif
diff --git a/noatun/modules/kjofol-skin/noatunui.cpp b/noatun/modules/kjofol-skin/noatunui.cpp
new file mode 100644
index 00000000..b5be87fd
--- /dev/null
+++ b/noatun/modules/kjofol-skin/noatunui.cpp
@@ -0,0 +1,9 @@
+#include "kjloader.h"
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new KJLoader();
+ }
+}
diff --git a/noatun/modules/kjofol-skin/parser.cpp b/noatun/modules/kjofol-skin/parser.cpp
new file mode 100644
index 00000000..df5fdc40
--- /dev/null
+++ b/noatun/modules/kjofol-skin/parser.cpp
@@ -0,0 +1,132 @@
+/***************************************************************************
+ parser.cpp - Reads *.rc files in kjfol-config-format into a QDict
+ --------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+// local includes
+#include "parser.h"
+#include "kjprefs.h"
+
+// system includes
+#include <qtextstream.h>
+#include <qimage.h>
+#include <qfile.h>
+#include <kdebug.h>
+#include <kmimemagic.h>
+#include <kurl.h>
+
+Parser::Parser() : QDict<QStringList>(17,false)
+{
+ mSkinAbout="";
+ mImageCache.setAutoDelete(true);
+ setAutoDelete(true);
+}
+
+void Parser::conserveMemory()
+{
+ mImageCache.clear();
+}
+
+void Parser::open(const QString &file)
+{
+ clear();
+ mImageCache.clear();
+ mSkinAbout="";
+ mDir=KURL(file).directory();
+ QFile f(file);
+ if ( !f.exists() )
+ return;
+ f.open(IO_ReadOnly);
+
+ f.at(0);
+ QTextStream stream(&f);
+ while (!stream.eof())
+ {
+ QString line=stream.readLine();
+ line=line.simplifyWhiteSpace();
+ if ((!line.length()) || line[0]=='#')
+ continue;
+ QStringList *l=new QStringList(QStringList::split(" ", (line.lower())));
+ QString first=l->first();
+
+ // special handling for about-texts as the key "about" can appear multiple
+ // times and thus does not fit into qdict.
+ if (first=="about")
+ {
+ if (!mSkinAbout.isEmpty())
+ mSkinAbout+="\n";
+
+ mSkinAbout += line.mid(6);
+// kdDebug(66666) << "found About-line, mSkinAbout is now '" << mSkinAbout << "'" << endl;
+ delete l; // don't need the stringlist anymore
+ }
+ else
+ insert(first, l);
+ }
+}
+
+QString Parser::fileItem(const QString &i) const
+{
+ return dir()+'/'+i;
+}
+
+QString Parser::dir() const
+{
+ return mDir;
+}
+
+Parser::ImagePixmap* Parser::getPair(const QString &filenameOld) const
+{
+ // is it in there?
+ ImagePixmap *pair;
+ {
+ pair=mImageCache.find(filenameOld);
+ if (pair)
+ return pair;
+ }
+
+ QString filename=fileItem(filenameOld);
+
+ QImage image;
+
+ // Determine file-format trough mimetype (no stupid .ext test)
+ KMimeMagicResult * result = KMimeMagic::self()->findFileType( filename );
+
+ if ( result->mimeType() == "image/png" )
+ {
+// image = NoatunApp::readPNG(filenameNoCase(filename));
+ QImageIO iio;
+ iio.setFileName( filenameNoCase(filename) );
+ // forget about gamma-value, fix for broken PNGs
+ iio.setGamma( 0.00000001 );
+ if ( iio.read() )
+ {
+ image = iio.image();
+ image.setAlphaBuffer(false); // we don't want/support alpha-channels
+ }
+ else
+ {
+ kdDebug(66666) << "Could not load file: " << filename.latin1() << endl;
+ }
+ }
+ else
+ {
+ image = QImage(filenameNoCase(filename));
+ }
+
+ //add to the cache
+ QPixmap pixmap;
+ pixmap.convertFromImage(image, QPixmap::AutoColor|QPixmap::ThresholdDither|QPixmap::AvoidDither);
+ pair = new Parser::ImagePixmap;
+ pair->mImage = image;
+ pair->mPixmap = pixmap;
+ mImageCache.insert(filenameOld, pair);
+ return pair;
+}
+
+bool Parser::exist(const QString &i) const
+{
+ return (bool)find(i);
+}
diff --git a/noatun/modules/kjofol-skin/parser.h b/noatun/modules/kjofol-skin/parser.h
new file mode 100644
index 00000000..97e20d99
--- /dev/null
+++ b/noatun/modules/kjofol-skin/parser.h
@@ -0,0 +1,49 @@
+#ifndef PARSER_H
+#define PARSER_H
+
+// system includes
+#include <qstringlist.h>
+#include <qpixmap.h>
+#include <qimage.h>
+#include <qdict.h>
+
+class Parser : public QDict<QStringList>
+{
+ class ImagePixmap
+ {
+ public:
+ ImagePixmap() : mImage(0), mPixmap(0) {}
+ ~ImagePixmap() {}
+ QImage mImage;
+ QPixmap mPixmap;
+ };
+
+ public:
+ Parser();
+
+ void conserveMemory();
+ void open(const QString &file);
+
+ QString dir() const;
+ QPixmap pixmap(const QString &pixmap) const
+ { return getPair(pixmap)->mPixmap; }
+ QImage image(const QString &image) const
+ { return getPair(image)->mImage; }
+ QString about() const { return mSkinAbout; };
+
+ QString fileItem(const QString &file) const;
+
+ bool exist(const QString &i) const;
+
+ public:
+ QStringList& operator[](const QString &l) { return *find(l);}
+
+ private:
+ ImagePixmap *getPair(const QString &i) const;
+
+ private:
+ mutable QDict<ImagePixmap> mImageCache;
+ QString mDir;
+ QString mSkinAbout;
+};
+#endif // PARSER_H
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/HexoBronx.rc b/noatun/modules/kjofol-skin/skins/HexoBronx/HexoBronx.rc
new file mode 100644
index 00000000..76e9d10c
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/HexoBronx.rc
@@ -0,0 +1,77 @@
+About .: HexoBronx :.
+
+BackgroundImage inactive.png
+BackgroundImagePressed1 active.png
+BackgroundImageInactive inactive.png
+
+SplashScreen splash.png
+
+
+FontImage font.png
+FontSize 5 7
+FontSpacing 0
+FontTransparent 1
+
+TimeFontImage eckig_font.png
+TimeFontSize 7 7
+TimeFontSpacing 0
+TimeFontTransparent 1
+
+PitchFontImage volume_pitch_font.png
+PitchFontSize 5 7
+PitchFontSpacing 0
+PitchFontTransparent 1
+
+VolumeFontImage volume_pitch_font.png
+VolumeFontSize 5 7
+VolumeFontSpacing 0
+VolumeFontTransparent 1
+
+
+SeekRegion 100 138 222 178
+SeekImage mask.png
+
+VolumeControlType BMP
+VolumeControlImage volume.png
+VolumeControlImagePosition mask.png
+VolumeControlImageXSize 33
+VolumeControlImageNb 39
+VolumeControlButton 16 108 48 146 vol
+
+PitchControlImage pitch.png
+PitchControlImagePosition mask.png
+PitchControlImageXSize 33
+PitchControlImageNb 39
+PitchControlButton 272 108 304 146 pitch
+
+CloseButton 233 31 245 42 close BMP1
+MinimizeButton 77 32 89 41 min BMP1
+
+PlayButton 124 21 160 59 play BMP1
+PauseButton 161 21 197 59 pause BMP1
+
+RewindButton 52 62 111 99 rwd BMP1
+ForwardButton 211 62 271 99 fwd BMP1
+
+AboutButton 144 76 176 86 about BMP1
+PlaylistButton 137 89 185 102 pl BMP1
+
+RepeatButton 85 119 103 136 repeat BMP1
+PreferencesButton 220 118 236 136 pref BMP1
+
+PreviousSongButton 52 156 109 192 prevsong BMP1
+NextSongButton 212 156 271 192 nextsong BMP1
+
+StopButton 123 197 197 233 stop BMP1
+
+
+MP3TimeWindow 144 104 179 111
+
+VolumeText 118 115 138 122
+
+PitchText 190 115 205 122
+
+FilenameWindow 108 125 215 132
+
+AnalyzerWindow 130 134 192 153 analyzer
+AnalyzerColor 136 217 144
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/Makefile.am b/noatun/modules/kjofol-skin/skins/HexoBronx/Makefile.am
new file mode 100644
index 00000000..5be0735d
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/Makefile.am
@@ -0,0 +1,7 @@
+
+skin_DATA = HexoBronx.rc README.txt active.png eckig_font.png \
+ font.png inactive.png mask.png pitch.png splash.png time_font.png volume.png \
+ volume_pitch_font.png
+
+skindir = $(kde_datadir)/noatun/skins/kjofol/HexoBronx
+EXTRA_DIST = $(skin_DATA)
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/README.txt b/noatun/modules/kjofol-skin/skins/HexoBronx/README.txt
new file mode 100644
index 00000000..640ca023
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/README.txt
@@ -0,0 +1,79 @@
+HeXoBronX - Desktop Media Control Interface
+ for Noatun's K-Jfol Skin Loader
+
+ ...brought to you by mETz, Crix and Sush
+---------------------------------------------------------------
+
+We would like to thank you for downloading/installing this little piece
+of art. This is our first try with K-Jfol skinning and we hope you
+enjoy the result.
+
+Features
+--------
+The skin features the following buttons/functions
+
+ - play, pause, stop
+ - fastforward, rewind
+ - previous and next track
+ - volume and pitch
+ - seeker
+ - playlist button
+ - repeat
+ - preferences
+ - about
+ - window controls: minimize, close
+ - works with Noatun and XMMS
+
+What you could possibly miss
+----------------------------
+Things this skin doesn't support:
+
+ - There is no skinned playlist because playlists suck with K-Jfol skins.
+
+ - There is no skinned dockmode.
+
+ - It lacks support for original K-Jfol player. Sounds odd but it's true.
+ This skin does not work with that player. During design we forgot to
+ make sizes divisible by four which seems to be a quite important skin
+ spec. When we actually realized that it was already too late to make
+ the appropriate changes :/
+ However it works very well with Noatun's K-Jfol skin loader.
+ If there is anyone out there who want's to use this skin for
+ the original K-Jfol player he might drop us a line and we'll see.
+
+
+Credits
+-------
+ - Design concept, scripting and idea:
+ Stefan "mETz" Gehn, <sgehn@gmx.net>
+
+ - Executive artist:
+ Christian "Crix" Hoffmann
+ be sure to visit http://www.crixensgfxcorner.de.vu
+
+ - Assistance and suggestions:
+ Sascha "Sush" Hoffmann
+
+Special thanks to
+-----------------
+ - Lars "die.viper" Kluge for improving the first scribbles of this skin
+ be sure to visit http://www.die-viper.de
+
+ - You for reading this file and trying this skin
+
+Licensing stuff
+---------------
+This skin is copyright 2002 by the people mentioned in the Credits above
+and distributed under the Clarified Artistic Licence.
+
+The most important things to mention:
+
+ - you are allowed to redistribute it
+
+ - you are allowed to alter it but please give credits and give it a new name
+
+
+Hey! Why are you wasting your time with this readme? Just go ahead and try the skin!
+Have fun!
+
+ -mETz, Crix and Sush, 01/25/2002
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/active.png b/noatun/modules/kjofol-skin/skins/HexoBronx/active.png
new file mode 100644
index 00000000..f82388e8
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/active.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/eckig_font.png b/noatun/modules/kjofol-skin/skins/HexoBronx/eckig_font.png
new file mode 100644
index 00000000..ad137023
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/eckig_font.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/font.png b/noatun/modules/kjofol-skin/skins/HexoBronx/font.png
new file mode 100644
index 00000000..ba1c2f7e
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/font.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/inactive.png b/noatun/modules/kjofol-skin/skins/HexoBronx/inactive.png
new file mode 100644
index 00000000..2c19d06a
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/inactive.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/mask.png b/noatun/modules/kjofol-skin/skins/HexoBronx/mask.png
new file mode 100644
index 00000000..d9e376c1
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/mask.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/pitch.png b/noatun/modules/kjofol-skin/skins/HexoBronx/pitch.png
new file mode 100644
index 00000000..97c9c464
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/pitch.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/splash.png b/noatun/modules/kjofol-skin/skins/HexoBronx/splash.png
new file mode 100644
index 00000000..d7433a5f
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/splash.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/time_font.png b/noatun/modules/kjofol-skin/skins/HexoBronx/time_font.png
new file mode 100644
index 00000000..bd409d7f
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/time_font.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/volume.png b/noatun/modules/kjofol-skin/skins/HexoBronx/volume.png
new file mode 100644
index 00000000..1db32364
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/volume.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/volume_pitch_font.png b/noatun/modules/kjofol-skin/skins/HexoBronx/volume_pitch_font.png
new file mode 100644
index 00000000..df0160f9
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/volume_pitch_font.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/Makefile.am b/noatun/modules/kjofol-skin/skins/Makefile.am
new file mode 100644
index 00000000..47e5721d
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/Makefile.am
@@ -0,0 +1,3 @@
+
+SUBDIRS=kjofol phong vibrocentric HexoBronx
+
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/Makefile.am b/noatun/modules/kjofol-skin/skins/kjofol/Makefile.am
new file mode 100644
index 00000000..f549bccc
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/Makefile.am
@@ -0,0 +1,11 @@
+
+skin_DATA = kjofol.dck kjofol.pl kjofol.rc kjofol.wsh sgdock2.png \
+ sgdock.png sgdocksk.png sgdockvp.png sgeq.png sg_num.png \
+ sgpitch.png sgpitchp.png sgplist2.png sgplist.png sg.png \
+ sgpres1.png sgpres2.png sgpres3.png sg_seek.bmp sg_seek.png \
+ sg_text.png sgvolnum.png sgvol.png sgvolpos.png sgwshad2.png \
+ sgwshad.png sgwshdsk.png sgwshvol.png sgwshvp.png
+
+skindir = $(kde_datadir)/noatun/skins/kjofol/kjofol
+EXTRA_DIST = $(skin_DATA)
+
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/kjofol.dck b/noatun/modules/kjofol-skin/skins/kjofol/kjofol.dck
new file mode 100644
index 00000000..7af3b666
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/kjofol.dck
@@ -0,0 +1,62 @@
+# Kjofol Default resource file - DOCKING MODE
+
+# Command : BackgroundImage <Name of .BMP file>
+# Desc. : This is the back image.
+BackgroundImage sgdock.png
+
+# Command : BackgroundImageInactive <Name of .BMP file>
+# Desc. : This is the back image when the window is not selected.
+# : If you do not want this feature, just put the same name as
+# : BackgroundImage
+BackgroundImageInactive sgdock.png
+
+# Command : BackgroundImagePressed[1-3] <Name of .BMP file>
+# Desc. : This is the back images when all the buttons are pressed
+# : Used if you use the BMP option in the buttons options.
+# : You can have 3 backimages so you can do nifty things for the
+# : buttons =)
+BackgroundImagePressed1 sgdock2.png
+
+FontImage sg_text.png
+FontSize 5 9
+FontSpacing 0
+FontTransparent 0
+
+# Command : VolumeControlType <BAR/BMP>
+# Desc. : Put BAR if you want a bar style volume control, BMP if you want
+# : to customize it by a BMP animation file ...
+VolumeControlType BMP
+VolumeControlImage sgwshvol.png
+VolumeControlImagePosition sgdockvp.png
+VolumeControlImageXSize 30
+VolumeControlImageNb 28
+VolumeControlButton 100 17 129 23 vOLUME
+
+# Command : [Option]Button <Position X> <Position Y> <End X> <End Y> <Tooltip Text> <DARKEN/BMP[?]>
+# The DARKEN option just dark the button when the user click on it.
+# The BMP[?] option use the BackgroundImagePressed[?] defined above.
+# Be sure to define a BackgroundImagePressed if you use the BMP option !!
+AboutButton 10 12 27 32 About BMP1
+OpenFileButton 14 73 32 91 Open BMP1
+StopButton 14 56 32 73 Stop BMP1
+PlayButton 14 37 32 55 Play BMP1
+PreviousSongButton 14 92 33 109 PreviousSong BMP1
+NextSongButton 14 110 33 127 NextSong BMP1
+UnDockModeButton 14 127 33 140 UnDock BMP1
+
+# Command : FilenameWindow
+# Desc. : This is the window where the file name appears
+FilenameWindow 65 23 141 32
+
+SeekRegion 56 18 84 21
+SeekImage sgdocksk.png
+
+# Command : AnalyzerWindow <X> <Y> <MaxX> <MaxY> <TipTool>
+# Desc. : Spectrum Analyzer area. If you doesn't want one, just comment the
+# line ...
+AnalyzerWindow 41 23 62 32 Analyzer
+# Command : AnalyzerColor <Red> <Green> <Blue>
+# Desc. : Spectrum Analyzer color. Colors range are 0-255.
+AnalyzerColor 81 94 81
+
+IncludeRCFile kjofol.pl \ No newline at end of file
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/kjofol.pl b/noatun/modules/kjofol-skin/skins/kjofol/kjofol.pl
new file mode 100644
index 00000000..e7eaf45a
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/kjofol.pl
@@ -0,0 +1,39 @@
+# Playlist screen section
+PlaylistBmp sgplist.png
+PlaylistBmpPressed sgplist2.png
+
+# Command : PlaylistWindowText <X> <Y> <MaxX> <MaxY>
+# Desc. : Where all the playlist files will be placed on the playlist
+# screen
+PlaylistWindowText 43 42 170 211
+PlaylistWindowFontName Arial
+PlaylistWindowFontSize 12
+PlaylistWindowFontYSpacing 10
+PlaylistWindowNbSelectedTrack 79 215 93 225
+PlaylistWindowNbTotalTracks 79 230 93 240
+
+PlaylistWindowLinkButton 180 33 194 48
+PlaylistWindowCloseButton 174 21 187 35
+PlaylistWindowUpButton 146 226 179 242
+PlaylistWindowDownButton 146 243 179 259
+PlaylistWindowShuffleButton 189 175 233 185
+PlaylistWindowSortButton 188 144 234 155
+PlaylistWindowSortInverseButton 187 159 234 170
+PlaylistWindowMinimizeButton 184 50 193 59
+PlaylistWindowAddButton 188 74 234 84
+PlaylistWindowDelButton 189 87 234 99
+PlaylistWindowResetButton 192 102 232 113
+PlaylistWindowLoadPlaylistButton 191 129 231 141
+PlaylistWindowSavePlaylistButton 189 115 232 127
+PlaylistWindowSelectionUpButton 27 42 41 55
+PlaylistWindowSelectionDownButton 27 198 41 211
+PlaylistWindowAboutButton 194 29 217 59
+PlaylistWindowPlayButton 113 285 147 311
+PlaylistWindowPreviousButton 76 285 109 310
+PlaylistWindowNextButton 153 283 185 308
+PlaylistWindowPauseButton 187 266 217 290
+PlaylistWindowStopButton 198 239 234 262
+PlaylistWindowOpenButton 202 210 236 233
+PlaylistWindowColor 0 0 0
+PlaylistWindowCurrentTrackColor 0 0 136
+PlaylistWindowCurrentSelectionColor 153 207 181
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/kjofol.rc b/noatun/modules/kjofol-skin/skins/kjofol/kjofol.rc
new file mode 100644
index 00000000..daf99638
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/kjofol.rc
@@ -0,0 +1,150 @@
+# If you want to know all secrets about K-Jofol's resources files,
+# Just check out the great tutorial at : http://www.angelfire.com/mo/nequiem/
+
+# Command : About <text>
+# Desc. : This text will appear when the users selects "About ..."
+# : You have 5 lines of text ...
+About
+About Made by Steve Gedikian for K-Jofol.
+
+# Command : BackgroundImage <Name of .BMP file>
+# Desc. : This is the back image.
+BackgroundImage sg.png
+
+# Command : BackgroundImageInactive <Name of .BMP file>
+# Desc. : This is the back image when the window is not selected.
+# : If you do not want this feature, just put the same name as
+# : BackgroundImage
+BackgroundImageInactive sg.png
+
+# Command : BackgroundImagePressed[1-3] <Name of .BMP file>
+# Desc. : This is the back images when all the buttons are pressed
+# : Used if you use the BMP option in the buttons options.
+# : You can have 3 backimages so you can do nifty things for the
+# : buttons =)
+BackgroundImagePressed1 sgpres1.png
+BackgroundImagePressed2 sgpres2.png
+BackgroundImagePressed3 sgpres3.png
+
+FontImage sg_text.png
+FontSize 5 9
+FontSpacing 0
+FontTransparent 0
+TimeFontImage sg_num.png
+TimeFontSize 8 9
+TimeFontSpacing 0
+TimeFontTransparent 0
+
+# Command : VolumeControlType <BAR/BMP>
+# Desc. : Put BAR if you want a bar style volume control, BMP if you want
+# : to customize it by a BMP animation file ...
+# VolumeControlType BAR
+# VolumeControlImage btn_vol.bmp
+# VolumeControlButton 235 166 275 174 vOLUME
+VolumeControlType BMP
+VolumeControlImage sgvol.png
+VolumeControlImagePosition sgvolpos.png
+VolumeControlImageXSize 86
+VolumeControlImageNb 83
+VolumeControlButton 211 133 296 209 vOLUME
+
+SeekImage sg_seek.png
+SeekRegion 91 22 300 127
+
+# Pitch control definition
+PitchText 243 93 260 102
+PitchControlImage sgpitch.png
+PitchControlImageXSize 58
+PitchControlImageNb 36
+PitchControlButton 222 68 280 125 pITCH
+PitchControlImagePosition sgpitchp.png
+
+PitchFontImage sgvolnum.png
+PitchFontSize 5 10
+PitchFontSpacing 0
+PitchFontTransparent 0
+
+# Command : [Option]Button <Position X> <Position Y> <End X> <End Y> <Tooltip Text> <DARKEN/BMP[?]>
+# The DARKEN option just dark the button when the user click on it.
+# The BMP[?] option use the BackgroundImagePressed[?] defined above.
+# Be sure to define a BackgroundImagePressed if you use the BMP option !!
+CloseButton 310 116 322 128 Close BMP1
+MinimizeButton 306 132 319 140 Minimize BMP1
+AboutButton 306 83 334 116 About BMP1
+OpenFileButton 25 8 61 26 Open BMP1
+StopButton 54 16 81 51 Stop BMP2
+PlayButton 28 27 57 56 Play BMP3
+#RewindButton 11 49 43 78 FastRewind BMP1
+#ForwardButton 45 49 73 77 FastForward BMP1
+PreviousSongButton 11 49 43 78 PreviousSong BMP1
+NextSongButton 45 49 73 77 NextSong BMP1
+PauseButton 9 18 31 51 Pause BMP2
+PreferencesButton 267 43 300 51 Options BMP1
+EqualizerButton 125 152 135 159 Equalizer BMP1
+EqualizerResetButton 175 104 200 112 Reset BMP1
+EqualizerOnButton 141 150 152 157 On BMP1
+EqualizerOffButton 156 150 167 157 Off BMP1
+RepeatButton 278 51 308 59 Repeat BMP1
+PlaylistButton 292 67 325 77 Playlist BMP1
+
+# Command : EqualizerWindow <X> <Y> <MaxX> <MaxY> <TipTool> <# of bands> <X-Space between bands>
+EqualizerWindow 111 115 180 148 Equalizer 12 6
+# Command : EqualizerBmp <X-Size> <Nb Equalizer> <BMP File>
+EqualizerBmp 4 17 sgeq.png
+
+# Inactive Zone
+# This put a button that does NOTHING :)
+# In fact, this option is very useful for designing the "Spectrum Analyzer" and
+# the "Oscilliscope" buttons because they are not rectangulars.
+# Inactive zones for the Spectrum Analyzer button
+InactiveZone 91 124 159 136
+InactiveZone 99 136 159 152
+InactiveZone 113 153 163 166
+# Inactive zones for the Oscilloscope butoon
+InactiveZone 159 123 198 137
+InactiveZone 159 138 190 149
+InactiveZone 163 151 177 160
+# Remember to put first your inactive zones BEFORE the buttons
+
+# Now that we have put our inactive zones, we could safely put the buttons
+SpectrumAnalyzerButton 68 123 167 185 SpectrumAnalyzer
+OscilloscopeButton 165 122 211 172 Oscilloscope
+
+# Dock Mode
+DockModeButton 243 27 268 36 DockMode BMP1
+DockModeRCFile kjofol.dck
+# DockModePosition : 0 - Upper Left 1 - Upper Right
+# 2 - Bottom Left 3 - Bottom Right
+DockModePosition 0
+DockModePositionXY -33 -38
+WinshadeModeRCFile kjofol.wsh
+WinshadeModePosition 1
+WinshadeModePositionXY -405 -9
+
+# Command : FilenameWindow
+# Desc. : This is the window where the file name appears
+FilenameWindow 96 80 200 89
+
+MP3KbpsWindow 93 90 110 97
+MP3KbpsString
+MP3KhzWindow 135 90 146 97
+Mp3KhzString
+
+MP3TimeWindow 124 50 165 59
+CurrentTrackWindow 191 90 204 97
+
+# Command : AnalyzerWindow <X> <Y> <MaxX> <MaxY> <TipTool>
+# Desc. : Spectrum Analyzer area. If you doesn't want one, just comment the
+# line ...
+AnalyzerWindow 106 61 184 78 Analyzer
+# Command : AnalyzerColor <Red> <Green> <Blue>
+# Desc. : Spectrum Analyzer color. Colors range are 0-255.
+AnalyzerColor 81 94 81
+
+VolumeFontImage sgvolnum.png
+VolumeFontSize 5 10
+VolumeFontSpacing 0
+VolumeFontTransparent 0
+VolumeText 247 168 265 176
+
+IncludeRCFile kjofol.pl
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/kjofol.wsh b/noatun/modules/kjofol-skin/skins/kjofol/kjofol.wsh
new file mode 100644
index 00000000..2910f636
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/kjofol.wsh
@@ -0,0 +1,63 @@
+# Kjofol Default resource file - WINDOW SHADE MODE
+
+# Command : BackgroundImage <Name of .BMP file>
+# Desc. : This is the back image.
+BackgroundImage sgwshad.png
+
+# Command : BackgroundImageInactive <Name of .BMP file>
+# Desc. : This is the back image when the window is not selected.
+# : If you do not want this feature, just put the same name as
+# : BackgroundImage
+BackgroundImageInactive sgwshad.png
+
+# Command : BackgroundImagePressed[1-3] <Name of .BMP file>
+# Desc. : This is the back images when all the buttons are pressed
+# : Used if you use the BMP option in the buttons options.
+# : You can have 3 backimages so you can do nifty things for the
+# : buttons =)
+BackgroundImagePressed1 sgwshad2.png
+
+FontImage sg_text.png
+FontSize 5 9
+FontSpacing 0
+FontTransparent 0
+
+# Command : VolumeControlType <BAR/BMP>
+# Desc. : Put BAR if you want a bar style volume control, BMP if you want
+# : to customize it by a BMP animation file ...
+VolumeControlType BMP
+VolumeControlImage sgwshvol.png
+VolumeControlImagePosition sgwshvp.png
+VolumeControlImageXSize 30
+VolumeControlImageNb 28
+VolumeControlButton 192 18 221 22 vOLUME
+
+
+# Command : [Option]Button <Position X> <Position Y> <End X> <End Y> <Tooltip Text> <DARKEN/BMP[?]>
+# The DARKEN option just dark the button when the user click on it.
+# The BMP[?] option use the BackgroundImagePressed[?] defined above.
+# Be sure to define a BackgroundImagePressed if you use the BMP option !!
+AboutButton 10 13 26 29 About BMP1
+PlayButton 230 13 247 29 Play BMP1
+StopButton 249 13 265 30 Stop BMP1
+OpenFileButton 267 14 284 28 Open BMP1
+PreviousSongButton 285 14 302 30 PreviousSong BMP1
+NextSongButton 303 13 319 29 NextSong BMP1
+UnDockModeButton 322 14 336 28 UnDock BMP1
+
+# Command : FilenameWindow
+# Desc. : This is the window where the file name appears
+FilenameWindow 38 16 114 25
+
+SeekRegion 148 19 176 22
+SeekImage sgwshdsk.png
+
+# Command : AnalyzerWindow <X> <Y> <MaxX> <MaxY> <TipTool>
+# Desc. : Spectrum Analyzer area. If you doesn't want one, just comment the
+# line ...
+AnalyzerWindow 117 16 130 25 Analyzer
+# Command : AnalyzerColor <Red> <Green> <Blue>
+# Desc. : Spectrum Analyzer color. Colors range are 0-255.
+AnalyzerColor 81 94 81
+
+IncludeRCFile kjofol.pl \ No newline at end of file
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sg.png b/noatun/modules/kjofol-skin/skins/kjofol/sg.png
new file mode 100644
index 00000000..6195fbe2
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sg.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sg_num.png b/noatun/modules/kjofol-skin/skins/kjofol/sg_num.png
new file mode 100644
index 00000000..ec1b6df8
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sg_num.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sg_seek.bmp b/noatun/modules/kjofol-skin/skins/kjofol/sg_seek.bmp
new file mode 100644
index 00000000..209014fa
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sg_seek.bmp
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sg_seek.png b/noatun/modules/kjofol-skin/skins/kjofol/sg_seek.png
new file mode 100644
index 00000000..92145805
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sg_seek.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sg_text.png b/noatun/modules/kjofol-skin/skins/kjofol/sg_text.png
new file mode 100644
index 00000000..a867da1b
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sg_text.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgdock.png b/noatun/modules/kjofol-skin/skins/kjofol/sgdock.png
new file mode 100644
index 00000000..49663245
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgdock.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgdock2.png b/noatun/modules/kjofol-skin/skins/kjofol/sgdock2.png
new file mode 100644
index 00000000..c932db01
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgdock2.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgdocksk.png b/noatun/modules/kjofol-skin/skins/kjofol/sgdocksk.png
new file mode 100644
index 00000000..9c6e5209
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgdocksk.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgdockvp.png b/noatun/modules/kjofol-skin/skins/kjofol/sgdockvp.png
new file mode 100644
index 00000000..056f29e4
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgdockvp.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgeq.png b/noatun/modules/kjofol-skin/skins/kjofol/sgeq.png
new file mode 100644
index 00000000..5d2ffeb6
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgeq.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgpitch.png b/noatun/modules/kjofol-skin/skins/kjofol/sgpitch.png
new file mode 100644
index 00000000..e0fff521
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgpitch.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgpitchp.png b/noatun/modules/kjofol-skin/skins/kjofol/sgpitchp.png
new file mode 100644
index 00000000..fa9b3121
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgpitchp.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgplist.png b/noatun/modules/kjofol-skin/skins/kjofol/sgplist.png
new file mode 100644
index 00000000..de165370
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgplist.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgplist2.png b/noatun/modules/kjofol-skin/skins/kjofol/sgplist2.png
new file mode 100644
index 00000000..2516a029
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgplist2.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgpres1.png b/noatun/modules/kjofol-skin/skins/kjofol/sgpres1.png
new file mode 100644
index 00000000..555ffa90
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgpres1.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgpres2.png b/noatun/modules/kjofol-skin/skins/kjofol/sgpres2.png
new file mode 100644
index 00000000..70bae784
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgpres2.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgpres3.png b/noatun/modules/kjofol-skin/skins/kjofol/sgpres3.png
new file mode 100644
index 00000000..f440f739
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgpres3.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgvol.png b/noatun/modules/kjofol-skin/skins/kjofol/sgvol.png
new file mode 100644
index 00000000..85d2a5dd
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgvol.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgvolnum.png b/noatun/modules/kjofol-skin/skins/kjofol/sgvolnum.png
new file mode 100644
index 00000000..ecb5aa1c
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgvolnum.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgvolpos.png b/noatun/modules/kjofol-skin/skins/kjofol/sgvolpos.png
new file mode 100644
index 00000000..b6e08a9f
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgvolpos.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgwshad.png b/noatun/modules/kjofol-skin/skins/kjofol/sgwshad.png
new file mode 100644
index 00000000..ab603003
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgwshad.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgwshad2.png b/noatun/modules/kjofol-skin/skins/kjofol/sgwshad2.png
new file mode 100644
index 00000000..0a456cea
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgwshad2.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgwshdsk.png b/noatun/modules/kjofol-skin/skins/kjofol/sgwshdsk.png
new file mode 100644
index 00000000..b63f09e2
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgwshdsk.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgwshvol.png b/noatun/modules/kjofol-skin/skins/kjofol/sgwshvol.png
new file mode 100644
index 00000000..394134fd
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgwshvol.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgwshvp.png b/noatun/modules/kjofol-skin/skins/kjofol/sgwshvp.png
new file mode 100644
index 00000000..e072545a
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgwshvp.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/Makefile.am b/noatun/modules/kjofol-skin/skins/phong/Makefile.am
new file mode 100644
index 00000000..fb299a01
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/Makefile.am
@@ -0,0 +1,8 @@
+
+skin_DATA = p_eq.png p_numbers.png p_propos.png p_volpos.png phong.wsh \
+ p_main.png p_playback.png p_text.png phong.dck phong_readme.txt \
+ p_mainback.png p_playlist.png p_volbar.png phong.rc
+
+skindir = $(kde_datadir)/noatun/skins/kjofol/phong
+EXTRA_DIST = $(skin_DATA)
+
diff --git a/noatun/modules/kjofol-skin/skins/phong/p_eq.png b/noatun/modules/kjofol-skin/skins/phong/p_eq.png
new file mode 100644
index 00000000..7b5390a0
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/p_eq.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/p_main.png b/noatun/modules/kjofol-skin/skins/phong/p_main.png
new file mode 100644
index 00000000..1e1b5110
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/p_main.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/p_mainback.png b/noatun/modules/kjofol-skin/skins/phong/p_mainback.png
new file mode 100644
index 00000000..1f12b323
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/p_mainback.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/p_numbers.png b/noatun/modules/kjofol-skin/skins/phong/p_numbers.png
new file mode 100644
index 00000000..51865529
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/p_numbers.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/p_playback.png b/noatun/modules/kjofol-skin/skins/phong/p_playback.png
new file mode 100644
index 00000000..233b681d
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/p_playback.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/p_playlist.png b/noatun/modules/kjofol-skin/skins/phong/p_playlist.png
new file mode 100644
index 00000000..b98b0a9b
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/p_playlist.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/p_propos.png b/noatun/modules/kjofol-skin/skins/phong/p_propos.png
new file mode 100644
index 00000000..e5ed17e3
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/p_propos.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/p_text.png b/noatun/modules/kjofol-skin/skins/phong/p_text.png
new file mode 100644
index 00000000..8254b6d2
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/p_text.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/p_volbar.png b/noatun/modules/kjofol-skin/skins/phong/p_volbar.png
new file mode 100644
index 00000000..61c4ea0e
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/p_volbar.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/p_volpos.png b/noatun/modules/kjofol-skin/skins/phong/p_volpos.png
new file mode 100644
index 00000000..0052097f
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/p_volpos.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/phong.dck b/noatun/modules/kjofol-skin/skins/phong/phong.dck
new file mode 100644
index 00000000..f4fadf52
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/phong.dck
@@ -0,0 +1,26 @@
+BackgroundImage ../kjofol/sgdock.png
+BackgroundImageInactive ../kjofol/sgdock.png
+BackgroundImagePressed1 ../kjofol/sgdock2.png
+FontImage ../kjofol/sg_text.png
+FontSize 5 9
+FontSpacing 0
+FontTransparent 0
+VolumeControlType BMP
+VolumeControlImage ../kjofol/sgwshvol.png
+VolumeControlImagePosition ../kjofol/sgdockvp.png
+VolumeControlImageXSize 30
+VolumeControlImageNb 28
+VolumeControlButton 100 17 129 23 vOLUME
+AboutButton 10 12 27 32 About BMP1
+OpenFileButton 14 73 32 91 Open BMP1
+StopButton 14 56 32 73 Stop BMP1
+PlayButton 14 37 32 55 Play BMP1
+PreviousSongButton 14 92 33 109 PreviousSong BMP1
+NextSongButton 14 110 33 127 NextSong BMP1
+UnDockModeButton 14 127 33 140 UnDock BMP1
+FilenameWindow 65 23 141 32
+SeekRegion 56 18 84 21
+SeekImage ../kjofol/sgdocksk.png
+AnalyzerWindow 41 23 62 32 Analyzer
+AnalyzerColor 81 94 81
+IncludeRCFile noirotic.pl \ No newline at end of file
diff --git a/noatun/modules/kjofol-skin/skins/phong/phong.rc b/noatun/modules/kjofol-skin/skins/phong/phong.rc
new file mode 100644
index 00000000..ee90005c
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/phong.rc
@@ -0,0 +1,104 @@
+About : : : phong : : :
+About by misery in motion 1999
+About misery_in_motion@hotmail.com
+About http://misery.mp3s.com.au
+
+BackgroundImage p_main.png
+BackgroundImageInactive p_main.png
+BackgroundImagePressed1 p_mainback.png
+
+
+FontImage p_text.png
+FontSize 5 9
+FontSpacing 1
+FontTransparent 0
+TimeFontImage p_numbers.png
+TimeFontSize 8 11
+TimeFontSpacing 1
+TimeFontTransparent 0
+
+VolumeControlType BMP
+VolumeControlImage p_volbar.png
+VolumeControlImagePosition p_volpos.png
+VolumeControlImageXSize 37
+VolumeControlImageNb 20
+VolumeControlButton 190 53 227 181 vOLUME
+
+SeekRegion 68 55 193 173
+SeekImage p_propos.png
+
+CloseButton 185 41 196 51 Close BMP1
+MinimizeButton 174 32 183 42 Minimize BMP1
+AboutButton 107 194 147 229 About BMP1
+OpenFileButton 237 95 257 130 Open BMP1
+StopButton 28 137 53 171 Stop BMP1
+PlayButton 24 96 45 130 Play BMP1
+#RewindButton 16 35 32 48 FastRewind BMP1
+#ForwardButton 33 11 49 25 FastForward BMP1
+PreviousSongButton 49 31 76 56 PreviousSong BMP1
+NextSongButton 49 172 77 202 NextSong BMP1
+PauseButton 32 60 53 91 Pause BMP1
+PreferencesButton 198 2 205 11 Options BMP1
+#EqualizerButton 76 103 82 107 Equalizer BMP1
+EqualizerResetButton 121 150 153 161 Reset BMP1
+EqualizerOnButton 98 130 107 142 On BMP1
+EqualizerOffButton 146 130 159 141 Off BMP1
+RepeatButton 221 66 253 92 Repeat BMP1
+PlaylistButton 227 131 252 162 Playlist BMP1
+
+FilenameWindow 86 119 168 129
+MP3KbpsWindow 150 108 170 116
+MP3KbpsString
+MP3KhzWindow 88 108 103 116
+Mp3KhzString
+MP3TimeWindow 105 106 153 122
+
+AnalyzerWindow 106 89 147 104 Analyzer
+AnalyzerColor 88 88 88
+EqualizerWindow 109 131 144 145 Equalizer 12 3
+EqualizerBmp 2 12 p_eq.png
+
+DockModeButton 158 23 171 36 DockMode BMP1
+DockModeRCFile phong.dck
+DockModePosition 0
+DockModePositionXY -33 -38
+WinshadeModeRCFile phong.wsh
+WinshadeModePosition 1
+WinshadeModePositionXY -405 -9
+
+PlaylistBmp p_playlist.png
+PlaylistBmpPressed p_playback.png
+PlaylistWindowText 45 88 177 198
+PlaylistWindowFontName Arial
+PlaylistWindowFontSize 12
+PlaylistWindowFontYSpacing 10
+PlaylistWindowNbSelectedTrack 58 211 68 221
+PlaylistWindowNbTotalTracks 83 211 93 221
+
+PlaylistWindowLinkButton 148 15 160 25
+PlaylistWindowCloseButton 174 31 184 41
+PlaylistWindowUpButton 98 67 122 81
+PlaylistWindowDownButton 98 207 122 220
+PlaylistWindowShuffleButton 157 232 174 248
+PlaylistWindowSortButton 115 232 132 248
+PlaylistWindowSortInverseButton 137 232 153 248
+PlaylistWindowMinimizeButton 161 21 172 32
+PlaylistWindowAddButton 48 232 64 248
+PlaylistWindowDelButton 70 232 86 248
+PlaylistWindowResetButton 91 232 107 248
+PlaylistWindowLoadPlaylistButton 41 71 74 78
+PlaylistWindowSavePlaylistButton 127 71 164 78
+PlaylistWindowSelectionUpButton 185 83 195 90
+PlaylistWindowSelectionDownButton 185 199 195 206
+PlaylistWindowAboutButton 140 210 193 220
+PlaylistWindowPlayButton 84 37 100 53
+PlaylistWindowPreviousButton 66 37 82 53
+PlaylistWindowNextButton 138 37 153 53
+PlaylistWindowPauseButton 101 37 118 53
+PlaylistWindowStopButton 120 37 136 53
+PlaylistWindowOpenButton 202 210 236 233
+PlaylistWindowColor 97 97 97
+PlaylistWindowCurrentTrackColor 60 60 60
+PlaylistWindowCurrentSelectionColor 150 150 150
+
+
diff --git a/noatun/modules/kjofol-skin/skins/phong/phong.wsh b/noatun/modules/kjofol-skin/skins/phong/phong.wsh
new file mode 100644
index 00000000..0fc338e7
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/phong.wsh
@@ -0,0 +1,26 @@
+BackgroundImage ../kjofol/sgwshad.png
+BackgroundImageInactive ../kjofol/sgwshad.png
+BackgroundImagePressed1 ../kjofol/sgwshad2.png
+FontImage ../kjofol/sg_text.png
+FontSize 5 9
+FontSpacing 0
+FontTransparent 0
+VolumeControlType BMP
+VolumeControlImage ../kjofol/sgwshvol.png
+VolumeControlImagePosition ../kjofol/sgwshvp.png
+VolumeControlImageXSize 30
+VolumeControlImageNb 28
+VolumeControlButton 192 18 221 22 vOLUME
+AboutButton 10 13 26 29 About BMP1
+PlayButton 230 13 247 29 Play BMP1
+StopButton 249 13 265 30 Stop BMP1
+OpenFileButton 267 14 284 28 Open BMP1
+PreviousSongButton 285 14 302 30 PreviousSong BMP1
+NextSongButton 303 13 319 29 NextSong BMP1
+UnDockModeButton 322 14 336 28 UnDock BMP1
+FilenameWindow 38 16 114 25
+SeekRegion 148 19 176 22
+SeekImage ../kjofol/sgwshdsk.png
+AnalyzerWindow 117 16 130 25 Analyzer
+AnalyzerColor 81 94 81
+IncludeRCFile noirotic.pl \ No newline at end of file
diff --git a/noatun/modules/kjofol-skin/skins/phong/phong_readme.txt b/noatun/modules/kjofol-skin/skins/phong/phong_readme.txt
new file mode 100644
index 00000000..a6030dc9
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/phong_readme.txt
@@ -0,0 +1,62 @@
+This is the original phong skin by misery
+
+Included is his little readme
+
+
+p h o n g 1 . 5
+-------------------------------------------------
+
+well, err... yes, its phong... created in
+photoshop 4 & 5 during the last two months
+
+does not include an individual dock/shade mode,
+but the wsh, rc and dk are set properly so you
+can use the default.
+
+i've also included a rather decent phong wallpaper
+(phong_wallpaper.jpg)
+its in 1024 x 768, but you can use it in other
+resolutions too. for 800x600 just be sure that
+you do not stretch the file, for bigger resolutions
+than 1024 x 768 make also sure to pick a 50% grey
+as background color.
+
+
+other skins by misery
+--------------------------------------------------
+
+winamp 1.x/2.x
+
+miseryamp 1.0 (-)
+phreak 0.667
+xtended 1.0
+stainless 2.0 (*)
+metropolis 2.01 (*)
+pandemonium 1.0
+coldbringer 2.01 (*)
+boost VIII 2.0 (*)
+schubduese 2.0 (*)
+asmith.net 2.0 (*)
+crystal bastard 2.0 (*)
+zorg 2.0 (*)
+
+k-jofol
+
+wintermute 1.0
+wintermute 2.0
+phong 1.5
+
+* winamp 2.0x support
+- shitty skin, removed
+
+distibute this skin freely, but don't delete the
+readme. manipulators and modifiers of this skin
+will be eaten by myself personally :-0.
+
+-------------------------------------------------
+misery, 08.04.99
+
+web: http://misery.mp3s.com.au
+mail: misery_in_motion@hotmail.com
+
+(c) m i s e r y i n m o t i o n ( 9 9 )
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/Makefile.am b/noatun/modules/kjofol-skin/skins/vibrocentric/Makefile.am
new file mode 100644
index 00000000..37461998
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/Makefile.am
@@ -0,0 +1,8 @@
+
+skin_DATA = i_base2.png i_pl.png i_text.png vibrocentric.dck vibrocentric_readme.txt \
+ i_eq.png i_pl2.png i_vol.png vibrocentric.rc i_base.png \
+ i_font.png i_pro.png i_volpos.png vibrocentric.wsh
+
+skindir = $(kde_datadir)/noatun/skins/kjofol/vibrocentric
+EXTRA_DIST = $(skin_DATA)
+
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/i_base.png b/noatun/modules/kjofol-skin/skins/vibrocentric/i_base.png
new file mode 100644
index 00000000..331dc169
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/i_base.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/i_base2.png b/noatun/modules/kjofol-skin/skins/vibrocentric/i_base2.png
new file mode 100644
index 00000000..d32f5460
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/i_base2.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/i_eq.png b/noatun/modules/kjofol-skin/skins/vibrocentric/i_eq.png
new file mode 100644
index 00000000..0174afa7
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/i_eq.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/i_font.png b/noatun/modules/kjofol-skin/skins/vibrocentric/i_font.png
new file mode 100644
index 00000000..7b37dc22
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/i_font.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/i_pl.png b/noatun/modules/kjofol-skin/skins/vibrocentric/i_pl.png
new file mode 100644
index 00000000..1e203bd9
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/i_pl.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/i_pl2.png b/noatun/modules/kjofol-skin/skins/vibrocentric/i_pl2.png
new file mode 100644
index 00000000..748d55cf
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/i_pl2.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/i_pro.png b/noatun/modules/kjofol-skin/skins/vibrocentric/i_pro.png
new file mode 100644
index 00000000..37e30816
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/i_pro.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/i_text.png b/noatun/modules/kjofol-skin/skins/vibrocentric/i_text.png
new file mode 100644
index 00000000..28ddfd39
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/i_text.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/i_vol.png b/noatun/modules/kjofol-skin/skins/vibrocentric/i_vol.png
new file mode 100644
index 00000000..53170e92
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/i_vol.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/i_volpos.png b/noatun/modules/kjofol-skin/skins/vibrocentric/i_volpos.png
new file mode 100644
index 00000000..b40ec693
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/i_volpos.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.dck b/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.dck
new file mode 100644
index 00000000..f4fadf52
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.dck
@@ -0,0 +1,26 @@
+BackgroundImage ../kjofol/sgdock.png
+BackgroundImageInactive ../kjofol/sgdock.png
+BackgroundImagePressed1 ../kjofol/sgdock2.png
+FontImage ../kjofol/sg_text.png
+FontSize 5 9
+FontSpacing 0
+FontTransparent 0
+VolumeControlType BMP
+VolumeControlImage ../kjofol/sgwshvol.png
+VolumeControlImagePosition ../kjofol/sgdockvp.png
+VolumeControlImageXSize 30
+VolumeControlImageNb 28
+VolumeControlButton 100 17 129 23 vOLUME
+AboutButton 10 12 27 32 About BMP1
+OpenFileButton 14 73 32 91 Open BMP1
+StopButton 14 56 32 73 Stop BMP1
+PlayButton 14 37 32 55 Play BMP1
+PreviousSongButton 14 92 33 109 PreviousSong BMP1
+NextSongButton 14 110 33 127 NextSong BMP1
+UnDockModeButton 14 127 33 140 UnDock BMP1
+FilenameWindow 65 23 141 32
+SeekRegion 56 18 84 21
+SeekImage ../kjofol/sgdocksk.png
+AnalyzerWindow 41 23 62 32 Analyzer
+AnalyzerColor 81 94 81
+IncludeRCFile noirotic.pl \ No newline at end of file
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.rc b/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.rc
new file mode 100644
index 00000000..cd2840d2
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.rc
@@ -0,0 +1,105 @@
+
+
+About idoru xt 1 : vibrocentric
+About part of the idoru litestep theme
+About by misery in motion 1999
+About misery_in_motion@mindless.com
+About http://misery.subnet.at
+
+BackgroundImage i_base.png
+BackgroundImageInactive i_base.png
+BackgroundImagePressed1 i_base2.png
+
+
+FontImage i_text.png
+FontSize 5 6
+FontSpacing 0
+FontTransparent 0
+TimeFontImage i_font.png
+TimeFontSize 9 8
+TimeFontSpacing 1
+TimeFontTransparent 0
+
+VolumeControlType BMP
+VolumeControlImage i_vol.png
+VolumeControlImagePosition i_volpos.png
+VolumeControlImageXSize 51
+VolumeControlImageNb 16
+VolumeControlButton 41 19 91 69 vOLUME
+
+SeekRegion 58 65 180 186
+SeekImage i_pro.png
+
+CloseButton 123 49 136 61 Close BMP1
+MinimizeButton 101 191 114 204 Minimize BMP1
+AboutButton 32 79 50 97 About BMP1
+OpenFileButton 144 51 161 69 Open BMP1
+StopButton 183 88 201 105 Stop BMP1
+PlayButton 159 181 186 206 Play BMP1
+#RewindButton 16 35 32 48 FastRewind BMP1
+#ForwardButton 33 11 49 25 FastForward BMP1
+PreviousSongButton 166 63 185 80 PreviousSong BMP1
+NextSongButton 185 149 204 166 NextSong BMP1
+PauseButton 190 117 208 135 Pause BMP1
+PreferencesButton 29 113 47 131 Options BMP1
+#EqualizerButton 76 103 82 107 Equalizer BMP1
+EqualizerResetButton 116 166 123 172 Reset BMP1
+EqualizerOnButton 105 166 114 172 On BMP1
+EqualizerOffButton 125 166 137 172 Off BMP1
+RepeatButton 35 144 54 160 Repeat BMP1
+PlaylistButton 75 185 93 201 Playlist BMP1
+
+FilenameWindow 72 131 169 137
+MP3KbpsWindow 74 117 90 122
+#MP3KbpsString
+MP3KhzWindow 79 123 90 129
+#Mp3KhzString
+MP3TimeWindow 111 118 166 128
+
+AnalyzerWindow 90 90 149 112 Analyzer
+AnalyzerColor 206 208 210
+EqualizerWindow 92 139 150 162 Equalizer 12 5
+EqualizerBmp 4 14 i_eq.png
+
+DockModeButton 53 168 71 186 DockMode BMP1
+DockModeRCFile vibrocentric.dck
+DockModePosition 0
+DockModePositionXY -33 -38
+WinshadeModeRCFile vibrocentric.wsh
+WinshadeModePosition 1
+WinshadeModePositionXY -405 -9
+
+PlaylistBmp i_pl.png
+PlaylistBmpPressed i_pl2.png
+PlaylistWindowText 60 108 176 181
+PlaylistWindowFontName Arial
+PlaylistWindowFontSize 12
+PlaylistWindowFontYSpacing 8
+PlaylistWindowNbSelectedTrack 106 186 120 195
+PlaylistWindowNbTotalTracks 106 198 120 207
+
+PlaylistWindowLinkButton 148 46 161 59
+PlaylistWindowCloseButton 189 82 202 95
+PlaylistWindowUpButton 148 86 158 93
+PlaylistWindowDownButton 148 96 158 103
+PlaylistWindowShuffleButton 136 186 146 196
+PlaylistWindowSortButton 124 186 134 196
+PlaylistWindowSortInverseButton 148 186 159 196
+PlaylistWindowMinimizeButton 124 42 136 55
+PlaylistWindowAddButton 124 198 134 208
+PlaylistWindowDelButton 136 198 146 208
+PlaylistWindowResetButton 148 198 158 208
+PlaylistWindowLoadPlaylistButton 88 210 116 219
+PlaylistWindowSavePlaylistButton 118 210 147 219
+PlaylistWindowSelectionUpButton 44 19 84 38
+PlaylistWindowSelectionDownButton 42 39 84 57
+PlaylistWindowAboutButton 169 59 183 73
+PlaylistWindowPlayButton 157 234 182 257
+PlaylistWindowPreviousButton 31 196 44 209
+PlaylistWindowNextButton 100 238 112 252
+PlaylistWindowPauseButton 75 234 88 247
+PlaylistWindowStopButton 50 221 63 234
+PlaylistWindowOpenButton 21 165 35 178
+PlaylistWindowColor 130 135 139
+PlaylistWindowCurrentTrackColor 164 168 171
+PlaylistWindowCurrentSelectionColor 81 89 94
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.wsh b/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.wsh
new file mode 100644
index 00000000..0fc338e7
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.wsh
@@ -0,0 +1,26 @@
+BackgroundImage ../kjofol/sgwshad.png
+BackgroundImageInactive ../kjofol/sgwshad.png
+BackgroundImagePressed1 ../kjofol/sgwshad2.png
+FontImage ../kjofol/sg_text.png
+FontSize 5 9
+FontSpacing 0
+FontTransparent 0
+VolumeControlType BMP
+VolumeControlImage ../kjofol/sgwshvol.png
+VolumeControlImagePosition ../kjofol/sgwshvp.png
+VolumeControlImageXSize 30
+VolumeControlImageNb 28
+VolumeControlButton 192 18 221 22 vOLUME
+AboutButton 10 13 26 29 About BMP1
+PlayButton 230 13 247 29 Play BMP1
+StopButton 249 13 265 30 Stop BMP1
+OpenFileButton 267 14 284 28 Open BMP1
+PreviousSongButton 285 14 302 30 PreviousSong BMP1
+NextSongButton 303 13 319 29 NextSong BMP1
+UnDockModeButton 322 14 336 28 UnDock BMP1
+FilenameWindow 38 16 114 25
+SeekRegion 148 19 176 22
+SeekImage ../kjofol/sgwshdsk.png
+AnalyzerWindow 117 16 130 25 Analyzer
+AnalyzerColor 81 94 81
+IncludeRCFile noirotic.pl \ No newline at end of file
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric_readme.txt b/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric_readme.txt
new file mode 100644
index 00000000..c2180678
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric_readme.txt
@@ -0,0 +1,19 @@
+This is the original vibrocentric skin by misery
+
+I couldn't find any readmes....
+but i hope this sentence from the phong skin
+is also okay for this skin
+
+<<
+distibute this skin freely, but don't delete the
+readme. manipulators and modifiers of this skin
+will be eaten by myself personally :-0.
+
+-------------------------------------------------
+misery, 08.04.99
+
+web: http://misery.mp3s.com.au
+mail: misery_in_motion@hotmail.com
+
+(c) m i s e r y i n m o t i o n ( 9 9 )
+>> \ No newline at end of file
diff --git a/noatun/modules/making_plugins b/noatun/modules/making_plugins
new file mode 100644
index 00000000..cb921cc2
--- /dev/null
+++ b/noatun/modules/making_plugins
@@ -0,0 +1,23 @@
+Welcome to the wonderful world of noatun plugin development!
+
+This is just a "notes" file. It won't show you _how_ to do it.
+
+You're free to use the net plugin as a base, I've licensed it under the
+public domain, so you can relicense it however you wish (preferably not
+GPL, though :)
+
+Remember that QObject must derive first.
+
+Do a "return this;" in the PlayList *playlist() const; function, if your class
+is a playlist, otherwise, don't even override that function.
+
+If your playlist can't seem to get activated, you might have left out the
+"const".
+
+For your create_plugin class, it should return Plugin*, not ClassName *:
+Plugin *create_plugin() {...} // good
+MyClass *create_plugin() {...{ // bad
+(this is important)
+
+A lot should be available to you with the 'napp' variable. It returns
+the global NoatunApp* type.
diff --git a/noatun/modules/marquis/Makefile.am b/noatun/modules/marquis/Makefile.am
new file mode 100644
index 00000000..a2fc3e3f
--- /dev/null
+++ b/noatun/modules/marquis/Makefile.am
@@ -0,0 +1,15 @@
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_marquis.la
+
+noatun_marquis_la_SOURCES = marquis.cpp plugin.cpp
+
+noatun_marquis_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_marquis_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la \
+ -lqtmcop -lkmedia2_idl -lsoundserver_idl
+
+noatun_marquis_la_METASOURCES = AUTO
+
+noinst_HEADERS = marquis.h
+
+noatun_modules_marquis_DATA = marquis.plugin
+noatun_modules_marquisdir = $(kde_datadir)/noatun
diff --git a/noatun/modules/marquis/marquis.cpp b/noatun/modules/marquis/marquis.cpp
new file mode 100644
index 00000000..29ca7364
--- /dev/null
+++ b/noatun/modules/marquis/marquis.cpp
@@ -0,0 +1,186 @@
+// marquis.cpp
+//
+// Copyright (C) 2000 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <noatun/pluginloader.h>
+#include "marquis.h"
+#include <noatun/player.h>
+#include <noatun/engine.h>
+#include <kaction.h>
+
+static int getPlayStatus( Player *player )
+{
+ if ( player->isPlaying() )
+ return 1;
+ if ( player->isPaused() )
+ return 2;
+ return 0;
+}
+
+static void setPlayStatus( Player *player, int status )
+{
+ switch( status )
+ {
+ default:
+ case 0:
+ player->stop();
+ break;
+ case 1:
+ player->play();
+ break;
+ case 2:
+ if ( !player->isPaused() )
+ {
+ player->play();
+ player->playpause();
+ }
+ break;
+ }
+}
+
+
+Marquis::Marquis()
+ : KMainWindow(0, "Marquis")
+ , SessionManagement()
+{
+ hide();
+ kdDebug(66666) << k_funcinfo << "number of members == " << memberList->count() << endl;
+
+// for testing: uncomment this and use
+// dcop `dcop | grep noatun` Marquis activateAction dynamicRestore
+// and dynamicSave accordingly.
+// (void) new KAction("Restore", 0, this, SLOT( dynamicRestore() ), actionCollection(), "dynamicRestore" );
+// (void) new KAction("Save", 0, this, SLOT( dynamicSave() ), actionCollection(), "dynamicSave" );
+ connect( napp, SIGNAL( saveYourself() ), SLOT( dynamicSave() ));
+}
+
+Marquis::~Marquis()
+{
+}
+
+void Marquis::restore(void)
+{
+ kdDebug(66666) << k_funcinfo << endl;
+ dynamicRestore();
+}
+
+// unload every window, and save the config as QStringList of those loaded
+void Marquis::saveSessionConfig(KConfig *c)
+{
+ kdDebug(66666) << k_funcinfo << endl;
+
+ Player *player = napp->player();
+ c->writeEntry("Volume", player->volume());
+ c->writeEntry("Loop Style", player->loopStyle());
+ if ( napp->playlist() )
+ c->writeEntry("Playlist Visible", napp->playlist()->listVisible());
+
+ if ( !player->current().isNull() )
+ {
+ KURL songURL = player->current().url();
+ songURL.setPass( QString::null ); // don't save passwords
+ c->writePathEntry("Current Song", songURL.url());
+ }
+ else
+ c->writePathEntry("Current Song", QString::null);
+
+ c->writeEntry("Current Position", player->getTime());
+ c->writeEntry("PlayStatus", getPlayStatus( player ));
+
+ // borrowed from Plugin config dialog
+ QStringList specList;
+
+ QValueList<NoatunLibraryInfo> loaded = napp->libraryLoader()->loaded();
+ for( QValueList<NoatunLibraryInfo>::Iterator i = loaded.begin(); i != loaded.end(); ++i)
+ {
+ if(!specList.contains((*i).specfile)
+ && napp->libraryLoader()->isLoaded((*i).specfile)
+ && (*i).specfile != "marquis.plugin")
+ {
+ specList += (*i).specfile;
+ napp->libraryLoader()->remove((*i).specfile, false);
+ }
+ }
+
+ c->writeEntry("Loaded Plugins", specList);
+}
+
+// get the list of loaded plugins from the config, and load them
+void Marquis::readSessionConfig(KConfig *c)
+{
+ Player *player = napp->player();
+
+ c->setGroup(QString::null);
+
+ // First set volume, then load modules, some module could start
+ // playback and that would be with 100% volume!
+ player->setVolume( c->readNumEntry("Volume", 100) );
+ //player->setVolume( 0 );
+ player->loop( c->readNumEntry("Loop Style", (int) Player::None) );
+
+ QStringList list = c->readListEntry("Loaded Plugins");
+ /*
+ kdDebug(66666) << "Marquis::readSessionConfig()" << endl;
+ for(QStringList::ConstIterator i=list.begin(); i!=list.end(); ++i)
+ kdDebug(66666) << *i << endl;
+ kdDebug(66666) << "Marquis::readSessionConfig() there we go" << endl;
+ */
+ napp->libraryLoader()->loadAll(list);
+
+ if (!napp->playlist())
+ {
+ KMessageBox::error(0,i18n("No playlist plugin was found. " \
+ "Please make sure that Noatun was installed correctly."));
+ napp->quit();
+ return;
+ }
+
+ if ( c->readBoolEntry( "Playlist Visible", false ))
+ napp->playlist()->showList();
+
+ napp->player()->engine()->setInitialized();
+ // TODO: restore the playback state (also save it ;)
+}
+
+void Marquis::dynamicSave()
+{
+ KConfig config( "marquisrc" );
+ saveSessionConfig( &config );
+}
+
+void Marquis::dynamicRestore()
+{
+ KConfig config( "marquisrc" );
+ readSessionConfig( &config );
+}
+
+#include "marquis.moc"
diff --git a/noatun/modules/marquis/marquis.h b/noatun/modules/marquis/marquis.h
new file mode 100644
index 00000000..5b166d57
--- /dev/null
+++ b/noatun/modules/marquis/marquis.h
@@ -0,0 +1,52 @@
+// marquis.h
+//
+// Copyright (C) 2000 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifndef MARQUIS_H
+#define MARQUIS_H
+
+#include <noatun/plugin.h>
+#include <noatun/app.h>
+#include <kmainwindow.h>
+
+class Player;
+
+class Marquis : public KMainWindow, public SessionManagement
+{
+Q_OBJECT
+NOATUNPLUGIND
+public:
+ Marquis();
+ virtual ~Marquis();
+
+ virtual void restore(void);
+private:
+ void saveSessionConfig(KConfig *);
+ void readSessionConfig(KConfig *);
+private slots:
+ void dynamicSave();
+ void dynamicRestore();
+};
+
+#endif
diff --git a/noatun/modules/marquis/marquis.plugin b/noatun/modules/marquis/marquis.plugin
new file mode 100644
index 00000000..7987d7b2
--- /dev/null
+++ b/noatun/modules/marquis/marquis.plugin
@@ -0,0 +1,83 @@
+Filename=noatun_marquis.la
+Author=Neil Stevens
+Site=http://www.derkarl.org/noatun
+Email=neil@qualityassistant.com
+Type=sm
+License=X11-like
+Name=Marquis
+Name[af]=Markies
+Name[ar]=ماركيس
+Name[cs]=Markýz
+Name[eo]=Markizo
+Name[es]=Marqués
+Name[hi]=मरक्यू
+Name[lt]=Markizas
+Name[lv]=Marķīzs
+Name[mk]=Маркиз
+Name[mt]=Markis
+Name[ne]=मार्किज
+Name[ru]=Маркиз
+Name[zh_CN]=侯爵
+Name[zh_HK]=侯爵
+Name[zh_TW]=侯爵
+Comment=Plugin to interact with the Session Manager
+Comment[af]=Inplak na interaksie het met die Sessie Bestuurder
+Comment[az]=İclas idarəcisi ilə interaksiya əlavəsi
+Comment[bg]=Взаимодействие с мениджъра на сесии
+Comment[bs]=Dodatak za interakciju sa upraviteljem sesija
+Comment[ca]=Connector per interactuar amb el gestor de sessió
+Comment[cs]=Modul pro spolupráci se správcem relace
+Comment[cy]=Atodyn i ryngweithredu efo'r Rheolwr Sesiynau
+Comment[da]=Plugin til at fungere sammen med sessionshåndteringen
+Comment[de]=Modul zur Kommunikation mit dem Sitzungsmanager
+Comment[el]=Πρόσθετο για αλληλεπίδραση με το διαχειριστή συνεδρίας
+Comment[eo]=Kromaĵo por interago kun la seancokonservilo
+Comment[es]=Plugin para interactuar con el Administrador de sesiones
+Comment[et]=Seansihalduriga suhtlemise plugin
+Comment[eu]=Sesio kudeatzailearekin komunikatzeko plugin-a
+Comment[fa]=وصله، برای کنش با مدیر نشست
+Comment[fi]=Sovelma istunnonhallinnan ohjaamiseen
+Comment[fr]=Un module pour interagir avec le gestionnaire de session
+Comment[gl]=Extensión para interaccionar co Xestor de Sesións
+Comment[he]=תוסף לתקשורת עם מנהל ההפעלה
+Comment[hi]=सत्र प्रबंधक से इंटरेक्ट करने का प्लगइन
+Comment[hr]=Dodatak za interakciju sa upravljačem seansa
+Comment[hu]=Beépülő modul a munkafolyamat-kezelő beállításához
+Comment[is]=Viðbót til að hafa samband við setustjórann
+Comment[it]=Plugin per interagire con la gestione delle sessioni
+Comment[ja]=セッションマネージャと情報をやり取りするためのプラグイン
+Comment[kk]=Сеанс менеджерімен әрекеттесу плагин модулі
+Comment[km]=កម្មវិធី​ជំនួយ​ដើម្បី​ធ្វើ​អន្តរកម្ម​ជាមួយ​កម្មវិធី​គ្រប់គ្រង​សម័យ
+Comment[ko]=세션 관리자와 작동하는 플러그인
+Comment[lt]=Sąveikos su sesijos tvarkykle priemonė
+Comment[lv]=Iespraudnis sadarbībai ar Sesiju Menedžeri
+Comment[mk]=Приклучок за содејство со менаџерот на сесијата
+Comment[ms]=Plug masuk untuk berinteraksi dengan Pengurus Sesi
+Comment[mt]=Plugin biex jaqbad mal-Manager tas-Sessjoni
+Comment[nb]=Programtillegg for å kommunisere med øktbehandleren
+Comment[nds]=Moduul för't Tosamenwerken mit den Törnpleger
+Comment[ne]=सत्र प्रबन्धकसँग अन्तरक्रिया गर्न प्लगइन
+Comment[nl]=Plugin om te communiceren met de sessiebeheerder
+Comment[nn]=Tilleggsmodul for å kommunisera med økthandsamaren
+Comment[pl]=Wtyczka do współpracy z menedżerem sesji
+Comment[pt]=Um 'plugin' para interagir com o gestor de sessões
+Comment[pt_BR]=Plug-in para interagir com o gerenciador de sessão
+Comment[ro]=Modul de interacţiune cu managerul de sesiune
+Comment[ru]=Модуль взаимодействия с менеджером сеанса
+Comment[se]=Lassemoduvla mainna gulahalla bargovuorrugieđahalliin
+Comment[sk]=Modul pre spoluprácu so Správcom sedenia
+Comment[sl]=Vstavek za delovanje z Upravljalnikom sej
+Comment[sr]=Прикључак за интеракцију са менаџером сесије
+Comment[sr@Latn]=Priključak za interakciju sa menadžerom sesije
+Comment[sv]=Insticksprogram för att samverka med sessionshanteraren
+Comment[ta]=அமர்வு மேலாளருடன் தொடர்பாடும் சொருகுப்பொருள்
+Comment[tg]=Модул барои амали мутақобила бо Мудири Сеанс
+Comment[th]=ปลั๊กอินสำหรับโต้ตอบกับตัวจัดการโปรเซส
+Comment[tr]=Oturum yöneticisi ile etkileşim eklentisi
+Comment[uk]=Втулок для взаємодії з Менеджером сеансів
+Comment[ven]=U plaga u itela u tangana na mulanguli wa tshitenwa
+Comment[xh]=Faka iplagi ukusebenza ngaphakathi noMphathi weNtlanganiso
+Comment[zh_CN]=和会话管理器交互的插件
+Comment[zh_HK]=用於和工作階段管理員互動的插件
+Comment[zh_TW]=用來給工作階段管理程式使用的外掛程式
+Comment[zu]=Faka iplagi ukusebenza ngaphakathi noMphathi weNhlanganiso
diff --git a/noatun/modules/marquis/plugin.cpp b/noatun/modules/marquis/plugin.cpp
new file mode 100644
index 00000000..146b0979
--- /dev/null
+++ b/noatun/modules/marquis/plugin.cpp
@@ -0,0 +1,9 @@
+#include "marquis.h"
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new Marquis();
+ }
+}
diff --git a/noatun/modules/metatag/HANDLED_ITEMS b/noatun/modules/metatag/HANDLED_ITEMS
new file mode 100644
index 00000000..3c369a9e
--- /dev/null
+++ b/noatun/modules/metatag/HANDLED_ITEMS
@@ -0,0 +1,21 @@
+ Here are the mappings between the items returned from KFileMetaInfo, and the
+properties Metatag sets based on those items. These properties may or may be
+set depending on the file's type and contents, and will not be set for
+non-local files or streams.
+
+|=============================================================================|
+| KFileMetaInfo Key | Noatun Property | Description |
+|===================|=================|=======================================|
+| Artist | author | Author |
+| Album | album | Album from which the file originates |
+| Bitrate | bitrate | Average bitrate (kilobits per second) |
+| Channels | channels | Number of audio channels |
+| Comment | comment | Free-form text comment |
+| Date | date | Date the file was released |
+| Genre | genre | Genre of file |
+| Location | location | Location of recording |
+| Organization | organization | |
+| Sample Rate | samplerate | File's audio sample rate (hertz) |
+| Title | title | Title of file |
+| Tracknumber | track | Track number of file on it's album |
+|=============================================================================|
diff --git a/noatun/modules/metatag/Makefile.am b/noatun/modules/metatag/Makefile.am
new file mode 100644
index 00000000..330e9f34
--- /dev/null
+++ b/noatun/modules/metatag/Makefile.am
@@ -0,0 +1,15 @@
+INCLUDES= -I$(top_srcdir)/noatun/library -I$(kde_includes)/arts $(all_includes)
+kde_module_LTLIBRARIES = noatun_metatag.la
+
+noatun_metatag_la_SOURCES = metatag.cpp edit.cpp
+
+noatun_metatag_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_metatag_la_LIBADD = $(LIB_KIO) $(top_builddir)/noatun/library/libnoatun.la \
+ $(top_builddir)/noatun/library/noatuntags/libnoatuntags.la
+
+noatun_metatag_la_METASOURCES = AUTO
+
+noinst_HEADERS = metatag.h edit.h
+
+noatun_modules_metatag_DATA = metatag.plugin
+noatun_modules_metatagdir = $(kde_datadir)/noatun
diff --git a/noatun/modules/metatag/edit.cpp b/noatun/modules/metatag/edit.cpp
new file mode 100644
index 00000000..d5ccb0c4
--- /dev/null
+++ b/noatun/modules/metatag/edit.cpp
@@ -0,0 +1,312 @@
+#include <klocale.h>
+#include <qlayout.h>
+#include <klineedit.h>
+#include <qlabel.h>
+#include <qptrlist.h>
+
+#include <kmimetype.h>
+#include <kicontheme.h>
+#include <qtooltip.h>
+#include <kstringvalidator.h>
+#include <qspinbox.h>
+#include <qhbox.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qcombobox.h>
+#include <qpushbutton.h>
+#include <kdialogbase.h>
+#include <kdebug.h>
+#include <kseparator.h>
+#include <kfilemetainfo.h>
+#include <qvalidator.h>
+
+#include "metatag.h"
+#include "edit.h"
+
+Editor::Editor()
+: KDialogBase((QWidget*)0L, 0, false, i18n("Tag Editor"), Ok | Cancel)
+{
+ mMainWidget = makeMainWidget();
+// mMainWidget->setMinimumWidth(325);
+
+ mGrid = new QGridLayout(mMainWidget, 1, 1, 0, spacingHint(), "Editor::mGrid");
+
+ mGrid->setColStretch(1, 1);
+ mGrid->setColStretch(2, 1);
+
+ QHBox *heading = new QHBox(mMainWidget, "Editor::heading");
+ heading->setSpacing(4);
+ mFileIcon = new QLabel(heading);
+ mFileIcon->setAlignment(AlignVCenter | AlignLeft);
+ mFile = new QLabel(heading);
+ mFile->setAlignment(AlignVCenter | AlignLeft);
+ heading->setStretchFactor(mFile, 2);
+ mGrid->addMultiCellWidget(heading, 0, 0, 0, 2);
+
+ KSeparator *sep = new KSeparator(KSeparator::HLine, mMainWidget);
+ mGrid->addMultiCellWidget(sep, 1, 1, 0, 2);
+
+ mControls.setAutoDelete(true);
+ mNextRow = 2;
+
+ connect(this, SIGNAL(closeClicked()), SLOT(delayedDestruct()));
+ connect(this, SIGNAL(okClicked()), SLOT(save()));
+
+ enableButtonSeparator(true);
+ setFixedHeight(sizeHint().height());
+}
+
+void Editor::open(const PlaylistItem & file)
+{
+ KFileMetaInfo file_meta_info(file.file(), file.mimetype());
+ KFileMetaInfoItem info_item;
+
+ item = file;
+ mDirty = false;
+
+ mFile->setText("<nobr><b>" + file.url().fileName(false) + "</b></nobr>");
+ QToolTip::add(mFile, file.url().prettyURL());
+ mFileIcon->
+ setPixmap(KMimeType::pixmapForURL(file.url(), 0, KIcon::Small));
+
+ if (file.url().isLocalFile()) {
+ QFileInfo file_info(file.file());
+ mFileWritable = file_info.isWritable();
+ }
+ else {
+ // KFileMetaInfo doesn't work on remote files
+ mFileWritable = false;
+ }
+
+ if ( !file_meta_info.isValid() ) // go ahead people, nothing to see here
+ return;
+
+ mControls.append(createControl(file_meta_info, i18n("&Title"), "Title", QVariant::String, false, mMainWidget));
+ mControls.append(createControl(file_meta_info, i18n("&Artist"), "Artist", QVariant::String, false, mMainWidget));
+ mControls.append(createControl(file_meta_info, i18n("A&lbum"), "Album", QVariant::String, false, mMainWidget));
+ mControls.append(createControl(file_meta_info, i18n("&Date"), "Date", QVariant::String, false, mMainWidget));
+ mControls.append(createControl(file_meta_info, i18n("T&rack"), "Tracknumber", QVariant::UInt, false, mMainWidget));
+ mControls.append(createControl(file_meta_info, i18n("&Genre"), "Genre", QVariant::String, false, mMainWidget));
+ mControls.append(createControl(file_meta_info, i18n("Co&mment"), "Comment", QVariant::String, false, mMainWidget));
+}
+
+void Editor::save()
+{
+ // Only write to disk if something actually changed
+ if (!mDirty)
+ {
+ delayedDestruct();
+ return;
+ }
+
+ KFileMetaInfo meta_info(item.file(), item.mimetype());
+ if ( !meta_info.isValid() )
+ {
+ delayedDestruct();
+ return;
+ }
+
+ for (MetaWidget *meta_widget = mControls.first(); meta_widget; meta_widget = mControls.next() )
+ saveControl(meta_info, *meta_widget);
+
+ meta_info.applyChanges();
+
+ emit(saved(item));
+ delayedDestruct();
+}
+
+void Editor::saveControl(KFileMetaInfo& meta_info, const MetaWidget &meta_widget) {
+ QVariant value;
+ const KFileMimeTypeInfo *info = KFileMetaInfoProvider::self()->mimeTypeInfo( meta_info.mimeType() );
+
+ if (!meta_widget.widget->isEnabled())
+ return;
+
+ if (meta_widget.widget->inherits("QSpinBox"))
+ value = static_cast<QSpinBox *>(meta_widget.widget)->value();
+ else if (meta_widget.widget->inherits("QComboBox"))
+ value = static_cast<QComboBox *>(meta_widget.widget)->currentText();
+ else if (meta_widget.widget->inherits("QLineEdit"))
+ value = static_cast<QLineEdit *>(meta_widget.widget)->text();
+
+ QString group = keyGroup(meta_info, meta_widget.key);
+
+ if (group.isNull()) {
+ kdWarning() << "Cannot find group for " << meta_widget.key << endl;
+ return;
+ }
+
+ if (info->groupInfo(group)->itemInfo(meta_widget.key)) {
+ if (info->groupInfo(group)->attributes() & KFileMimeTypeInfo::Addable) {
+ kdDebug(66666) << "Adding group " << group << endl;
+ if (!meta_info.addGroup(group))
+ kdWarning() << "Adding group \"" << group << "\" failed!" << endl;
+ }
+
+ if (info->groupInfo(group)->itemInfo(meta_widget.key)->attributes() & KFileMimeTypeInfo::Addable) {
+ kdDebug(66666) << "Adding key " << meta_widget.key << endl;
+ if (!meta_info.group(group).addItem(meta_widget.key).isValid())
+ kdWarning() << "Adding key \"" << meta_widget.key << "\" failed!" << endl;
+ }
+ }
+
+ if (value.cast(meta_info.item(meta_widget.key).type())) {
+ if (!meta_info.item(meta_widget.key).setValue(value))
+ kdWarning() << "setValue() failed on " << group << "/" << meta_widget.key << endl;
+ }
+ else
+ kdWarning() << "Cannot save " << meta_widget.key << " as required type." << endl;
+}
+
+MetaWidget* Editor::createControl(KFileMetaInfo& meta_info, const QString &label, const QString &key, QVariant::Type default_type, bool optional, QWidget *parent) {
+ QLabel *tmp_label = 0L;
+ KFileMetaInfoItem info_item = meta_info.item(key);
+ QVariant::Type type;
+ MetaWidget *meta_widget = 0L;
+ QValidator *validator = 0L;
+ QString groupName = keyGroup(meta_info, key);
+ bool known_key = ((!groupName.isNull()) && meta_info.group(groupName).contains(key));
+ bool addable = keyAddable(meta_info, key);
+ const KFileMimeTypeInfo *info = KFileMetaInfoProvider::self()->mimeTypeInfo( meta_info.mimeType() );
+
+ // This key isn't a real thing, and we can't even create it
+ if ((!info_item.isEditable()) && (!addable) && optional)
+ return 0;
+
+ if (!groupName.isNull()) {
+ type = info->groupInfo(groupName)->itemInfo(key)->type();
+ }
+ else {
+ type = default_type;
+ }
+
+ // Get the correct validator
+ if ( info && !groupName.isNull() )
+ validator = info->createValidator( groupName, key, parent );
+
+ // meta_widget is used for book-keeping internally
+ meta_widget = new MetaWidget;
+ meta_widget->key = key;
+
+ if ((type == QVariant::Int) || (type == QVariant::UInt)) {
+ // We're an int, make a spin box
+ QSpinBox *box = new QSpinBox(parent);
+
+ // Well, normally metatag doesn't care that much about suffixes
+ // and prefixes, but this is just too easy.
+ box->setPrefix(info_item.prefix());
+ box->setSuffix(info_item.suffix());
+ // Kinda a hack... display " " instead of "0" (think track numbers)
+ box->setSpecialValueText(" ");
+
+ // Do we have a validator?
+ if (validator) {
+ box->setValidator(validator);
+
+ // Is it an integer validator
+ if (validator->inherits("QIntValidator")) {
+ QIntValidator *int_validator = static_cast<QIntValidator *>(validator);
+
+ // FIXME: Why the -hell- doesn't QSpinBox::setValidator() do this??
+ box->setMinValue(int_validator->bottom());
+ box->setMaxValue(int_validator->top());
+ }
+ }
+
+ box->setValue(info_item.value().toInt());
+
+ connect(box, SIGNAL(valueChanged(int)), this, SLOT(modified()));
+ meta_widget->widget = box;
+ }
+ else {
+ // We're not an int, make a KLineEdit/QComboBox, depending on our validator
+ bool combo_box = false;
+
+ if (validator)
+ combo_box = validator->isA("KStringListValidator");
+
+ if (combo_box) {
+ QComboBox *combo = new QComboBox(parent);
+
+ combo->clear();
+ combo->insertStringList(static_cast<
+ KStringListValidator *
+ >(validator)->stringList());
+
+ combo->setCurrentText(info_item.value().toString());
+ connect(combo, SIGNAL(activated(int)), this, SLOT(modified()));
+
+ meta_widget->widget = combo;
+ }
+ else {
+ KLineEdit *edit;
+
+ edit = new KLineEdit(parent);
+ edit->setText(info_item.value().toString());
+ edit->setValidator(validator);
+ connect(edit, SIGNAL(textChanged(const QString &)), this, SLOT(modified()));
+
+ meta_widget->widget = edit;
+ }
+ }
+
+ if (known_key)
+ meta_widget->widget->setEnabled(info_item.isEditable() && mFileWritable);
+ else
+ meta_widget->widget->setEnabled(addable && mFileWritable);
+
+ mGrid->addMultiCellWidget(meta_widget->widget, mNextRow, mNextRow, 1, 2);
+
+ // Add our label. This is the easy part
+ tmp_label = new QLabel(meta_widget->widget, label + ":", parent);
+ mGrid->addWidget(tmp_label, mNextRow, 0);
+
+
+ mNextRow++;
+
+ return meta_widget;
+}
+
+QString Editor::keyGroup(const KFileMetaInfo& i, QString key) {
+ const KFileMimeTypeInfo *info = KFileMetaInfoProvider::self()->mimeTypeInfo( i.mimeType() );
+ QStringList groups = info->supportedGroups();
+
+ for (QStringList::Iterator it = groups.begin();it != groups.end();++it) {
+ if (info->groupInfo(*it)->itemInfo(key)) {
+ return *it;
+ }
+ }
+
+ return QString::null;
+}
+
+bool Editor::keyAddable(const KFileMetaInfo &i, QString key) {
+ const KFileMimeTypeInfo *info = KFileMetaInfoProvider::self()->mimeTypeInfo( i.mimeType() );
+ QStringList groups = info->supportedGroups();
+
+ kdDebug(66666) << "Testing if " << key << " is addable" << endl;
+ for (QStringList::Iterator it = groups.begin();it != groups.end();++it) {
+ if (info->groupInfo(*it)->supportsVariableKeys()) {
+ kdDebug(66666) << "Group " << *it << " supports variable keys" << endl;
+ return true;
+ }
+
+ if (info->groupInfo(*it)->itemInfo(key)) {
+ if (info->groupInfo(*it)->attributes() & KFileMimeTypeInfo::Addable) {
+ kdDebug(66666) << "Group " << *it << " is addable" << endl;
+ return true;
+ }
+
+ if (info->groupInfo(*it)->itemInfo(key)->attributes() & KFileMimeTypeInfo::Addable)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Editor::modified() {
+ mDirty = true;
+}
+
+#include "edit.moc"
diff --git a/noatun/modules/metatag/edit.h b/noatun/modules/metatag/edit.h
new file mode 100644
index 00000000..d6b0ab9c
--- /dev/null
+++ b/noatun/modules/metatag/edit.h
@@ -0,0 +1,55 @@
+#ifndef EDITOR_H
+#define EDITOR_H
+
+#include <kdialogbase.h>
+#include <noatun/playlist.h>
+#include <qptrlist.h>
+#include <qvariant.h>
+
+class KFileMetaInfo;
+class KFileMetaInfoItem;
+class QGridLayout;
+
+struct MetaWidget {
+ QWidget *widget;
+ QString key;
+};
+
+class Editor:public KDialogBase {
+ Q_OBJECT
+ public:
+ Editor();
+
+ signals:
+ void saved(PlaylistItem &);
+
+ public slots:
+ void open(const PlaylistItem & i);
+
+ protected slots:
+ void save();
+ void modified();
+
+ protected:
+ bool keyAddable(const KFileMetaInfo &, QString);
+ QString keyGroup(const KFileMetaInfo &, QString);
+
+ void saveControl(KFileMetaInfo& meta_info, const MetaWidget&);
+ MetaWidget *createControl(KFileMetaInfo& meta_info, const QString &label, const QString &key, QVariant::Type default_type, bool optional, QWidget *parent);
+
+ QPtrList<MetaWidget> mControls;
+
+ QWidget *mMainWidget;
+ QGridLayout *mGrid;
+ int mNextRow;
+
+ bool mFileWritable;
+ bool mDirty;
+
+ QLabel *mFile;
+ QLabel *mFileIcon;
+ const char *filename;
+ PlaylistItem item;
+};
+
+#endif
diff --git a/noatun/modules/metatag/metatag.cpp b/noatun/modules/metatag/metatag.cpp
new file mode 100644
index 00000000..d0531092
--- /dev/null
+++ b/noatun/modules/metatag/metatag.cpp
@@ -0,0 +1,124 @@
+
+#include "metatag.h"
+#include "edit.h"
+
+#include <string.h>
+
+#include <noatun/app.h>
+#include <noatun/stdaction.h>
+
+#include <qfile.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qregexp.h>
+
+#include <klocale.h>
+#include <kaction.h>
+#include <kglobal.h>
+#include <klineedit.h>
+#include <kconfig.h>
+#include <kaction.h>
+#include <kiconloader.h>
+#include <kpopupmenu.h>
+#include <kfilemetainfo.h>
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new MetaTagLoader;
+ }
+}
+
+MetaTagLoader::MetaTagLoader():Plugin()
+{
+ mAction = new KAction(i18n("&Tag Editor..."), "edit", 0, this, SLOT(editTag()), this, "edittag");
+ napp->pluginActionMenu()->insert(mAction);
+}
+
+MetaTagLoader::~MetaTagLoader()
+{
+ napp->pluginActionMenu()->remove(mAction);
+}
+
+void MetaTagLoader::editTag()
+{
+ PlaylistItem i = napp->player()->current();
+
+ if(!i)
+ return;
+
+ Editor *e = new Editor();
+ e->open(i);
+ e->show();
+
+ connect(e, SIGNAL(saved(PlaylistItem &)),
+ SLOT(update(PlaylistItem &)));
+}
+
+bool MetaTagLoader::update(PlaylistItem & item)
+{
+ KFileMetaInfo file_info(item.file(),item.mimetype());
+
+ // Ack, no file info :(
+ if ( !file_info.isValid() )
+ return false;
+
+ if(item.length() == -1) // no value set, set almost correct id3tag time
+ {
+ KFileMetaInfoItem length_item = file_info.item("Length");
+ if(length_item.isValid())
+ {
+ int numVal = length_item.value().toInt();
+ if (numVal != 0)
+ item.setLength(numVal * 1000);
+ }
+ }
+
+ // Yes, this is icky. It maps KFileMetaInfo property names to Noatun's
+ setProperty(file_info, item, "Title", "title");
+ setProperty(file_info, item, "Artist", "author");
+ setProperty(file_info, item, "Album", "album");
+ setProperty(file_info, item, "Genre", "genre");
+ setProperty(file_info, item, "Tracknumber", "track");
+ setProperty(file_info, item, "Date", "date");
+ setProperty(file_info, item, "Comment", "comment");
+ setProperty(file_info, item, "Location", "location");
+ setProperty(file_info, item, "Organization", "organization");
+
+ // Now map the audio properties over
+ setProperty(file_info, item, "Bitrate", "bitrate");
+ setProperty(file_info, item, "Sample Rate", "samplerate");
+ setProperty(file_info, item, "Channels", "channels");
+
+ return true;
+}
+
+bool MetaTagLoader::setProperty(KFileMetaInfo &info, PlaylistItem &item, const QString &key, const QString &property)
+{
+ KFileMetaInfoItem info_item = info.item(key);
+
+ if ( info_item.isValid() )
+ {
+ if (!info_item.value().toString().stripWhiteSpace().isEmpty())
+ {
+ // The item is valid and non-empty, add it
+ item.setProperty(property, info_item.value().toString());
+ }
+ else
+ {
+ // If the info_item is valid, but empty.
+ // This means we know for a fact that this
+ // property has no value. Blow it away.
+ item.clearProperty(property);
+ }
+ return true;
+ }
+
+ // The item isn't valid, so we don't know that it has
+ // no value. Don't remove the property, so we can work
+ // well with other tag readers, like Lucky
+ return false;
+}
+
+#include "metatag.moc"
diff --git a/noatun/modules/metatag/metatag.h b/noatun/modules/metatag/metatag.h
new file mode 100644
index 00000000..ff5c277f
--- /dev/null
+++ b/noatun/modules/metatag/metatag.h
@@ -0,0 +1,30 @@
+#ifndef NID3_H
+#define NID3_H
+
+#include <noatun/player.h>
+#include <noatun/plugin.h>
+#include <noatun/pref.h>
+#include <noatuntags/tags.h>
+
+#include <qobject.h>
+
+class KFileMetaInfo;
+class KAction;
+
+class MetaTagLoader:public QObject, public Tags, public Plugin {
+ Q_OBJECT
+ public:
+ MetaTagLoader();
+ ~MetaTagLoader();
+
+ public slots:
+ bool update(PlaylistItem & item);
+ void editTag();
+
+ private:
+ bool setProperty(KFileMetaInfo &info, PlaylistItem &item, const QString &key, const QString &property);
+ int menuID;
+ KAction *mAction;
+};
+
+#endif
diff --git a/noatun/modules/metatag/metatag.plugin b/noatun/modules/metatag/metatag.plugin
new file mode 100644
index 00000000..0e359350
--- /dev/null
+++ b/noatun/modules/metatag/metatag.plugin
@@ -0,0 +1,125 @@
+Filename=noatun_metatag.la
+Author=Ryan Cumming
+Site=http://noatun.derkarl.org/
+Email=bodnar42@phalynx.dhs.org
+Type=other
+License=Artistic
+Name=Tag Reader
+Name[af]=Etiket Leser
+Name[ar]=قارئ معلومات الرقعة
+Name[br]=Lenner al liketennoù
+Name[bs]=Čitač tagova
+Name[ca]=Lector de marques
+Name[cs]=Čtení tagů
+Name[cy]=Darllenydd Tag
+Name[da]=Mærkelæser
+Name[de]=Meta-Info-Leser
+Name[el]=Αναγνώστης ετικετών
+Name[eo]=Indiklegilo
+Name[es]=Lector de etiquetas
+Name[et]=Tagide lugeja
+Name[eu]=Etiketa irakurlea
+Name[fa]=خوانندۀ برچسب
+Name[fi]=Tagilukija
+Name[fr]=Lecteur de balise
+Name[ga]=Léitheoir Clibe
+Name[gl]=Lector de Etiquetas
+Name[he]=קורא תגיות
+Name[hi]=टैग रीडर
+Name[hr]=Čitač tag-ova
+Name[hu]=Azonosító-olvasó
+Name[is]=Tagabirtir
+Name[it]=Lettore tag
+Name[ja]=タグリーダー
+Name[kk]=Тегтерді оқу
+Name[km]=កម្មវិធី​អាន​ស្លាក
+Name[ko]=태그 리더
+Name[lt]=Etikečių skaityklė
+Name[lv]=Tagu Lasītājs
+Name[mk]=Читач на ознаки
+Name[nb]=Taggleser
+Name[nds]=Beteker-Kieker
+Name[ne]=ट्याग रिडर
+Name[nl]=Tag-lezer
+Name[nn]=Tagglesar
+Name[pa]=ਟੈਗ ਰੀਡਰ
+Name[pl]=Czytnik znaczników
+Name[pt]=Leitor de Marcas
+Name[pt_BR]=Leitor de símbolos
+Name[ro]=Cititor taguri
+Name[ru]=Чтение тегов
+Name[se]=Sárggislohkki
+Name[sk]=Čítačka tagov
+Name[sl]=Bralnik oznak
+Name[sr]=Читач ознака
+Name[sr@Latn]=Čitač oznaka
+Name[sv]=Taggläsare
+Name[ta]=ஒட்டு வாசிப்பாளர்
+Name[tg]=Хонандаи Борчасп
+Name[th]=โปรแกรมอ่านแท็ก
+Name[tr]=İm Okuyucu
+Name[uk]=Читач етикеток
+Name[ven]=Muvhali wa Tag
+Name[zh_CN]=标签读取程序
+Name[zh_HK]=標籤閱讀器
+Name[zh_TW]=標籤閱讀器
+Name[zu]=Umfundi we Tag
+Comment=Support for reading and writing to tags in media files
+Comment[af]=Ondersteun vir lees en om te skryf na etiket in media lêers
+Comment[ar]=دعم قراءة و كتابة الرقع في ملفات الوسائط
+Comment[bg]=Поддръжка на четене и запис на информацията в мултимедийни файловете
+Comment[bs]=Podrška za čitanje i pisanje tagova u multimedijalnim datotekama
+Comment[ca]=Suport per llegir i escriure etiquetes a fitxers multimèdia
+Comment[cs]=Podpora čtení a zápisu tagů v multimediálních souborech
+Comment[cy]=Cynhaliaeth ar gyfer darllen ac ysgrifennu i dagiau mewn ffeiliau cyfryngau
+Comment[da]=Understøttelse af læsning fra og skrivning til mærker i mediefiler
+Comment[de]=Lesen und Schreiben von Meta-Infos (Tags) in Media-Dateien
+Comment[el]=Υποστήριξη για ανάγνωση και εγγραφή στις ετικέτες των αρχείων μέσων
+Comment[eo]=Subteno por legado kaj skribado de indikoj en sonordosieroj
+Comment[es]=Soporte para lectura y escritura de etiquetas en archivos multimedia
+Comment[et]=Multimeediafailide tagide lugemise ja kirjutamise toetus
+Comment[eu]=Euskarri fitxategien etiketak irakurtzeko eta idazteko euskarria
+Comment[fa]=پشتیبانی برای خواندن و نوشتن در برچسبهای پرونده‌های رسانه
+Comment[fi]=Tuki mediatiedostojen tagien lukuun ja kirjoittamiseen
+Comment[fr]=Lecture et écriture des balises ID3 dans les fichiers multimedia
+Comment[gl]=Soporte para a lectura e escritura de etiquetas en ficheiros multimedia
+Comment[he]=תמיכה בקריאה וכתיבה של תגיות בקבצי מדיה
+Comment[hi]=मीडिया फ़ाइलों के टैग पढ़ने तथा लिखने का समर्थन
+Comment[hr]=Podrška za čitanje i pisanje dodatnih informacija (tag-ova) u medijske datoteke
+Comment[hu]=Támogatás médiafájlok azonosítóinak olvasásához és írásához
+Comment[is]=Stuðningur til að lesa og skrifa ID3 tög í MP3 skrám
+Comment[it]=Supporto per la lettura e la scrittura dei tag nei file multimediali
+Comment[ja]=メディアファイル内のタグの読み書きをサポート
+Comment[kk]=Медиа файлдарының тегтерін оқу-жазу құралы
+Comment[km]=ការ​គាំទ្រ​ដើម្បី​អាន និង​សរសេរ​ស្លាក​ក្នុង​ឯកសារ​មេឌៀ
+Comment[ko]=미디어 파일의 태그 읽고 쓰기 지원
+Comment[lt]=Palaiko etikečių skaitymą ir rašymą media bylose
+Comment[lv]=Tagu lasīšanas un rakstīšanas uzturēšana mēdiju failos
+Comment[mk]=Поддршка за читање и запишување на ознаките од мултимедијални датотеки
+Comment[ms]=Sokongan untuk baca dan tulis tag pada fail media
+Comment[nb]=Støtte for lesing og skriving til merkelapper i mediafiler
+Comment[nds]=Ünnerstütten för't Lesen un Schrieven vun Betekers binnen Mediendateien
+Comment[ne]=मिडिया फाइलको ट्यागमा लेख्न र पढ्नका लागि समर्थन
+Comment[nl]=Ondersteuning voor het lezen van en schrijven naar tags in mediabestanden
+Comment[nn]=Støtte for lesing og skriving av taggar i mediefiler
+Comment[pl]=Obsługa odczytu i zapisu znaczników w plikach medialnych
+Comment[pt]=O suporte de leitura/escrita de marcas em ficheiros multimédia
+Comment[pt_BR]=Suporte a leitura e gravação de símbolos em arquivos de mídia
+Comment[ro]=Suport pentru citirea şi scrierea tagurilor în fişiere multimedia
+Comment[ru]=Поддержка чтения и записи тегов в медиафайлах
+Comment[se]=Doarju sárggislohkama ja -čállima mediefiillain
+Comment[sk]=Podpora pre čítanie a zápis do tagov v multimediálnych súboroch
+Comment[sl]=Podpora branju in pisanju oznak za večpredstavnostne datoteke
+Comment[sr]=Подршка за читање и писање ID3 ознака у MP3 фајловима
+Comment[sr@Latn]=Podrška za čitanje i pisanje ID3 oznaka u MP3 fajlovima
+Comment[sv]=Stöd för att läsa och skriva taggar i mediafiler
+Comment[ta]=ஊடகக் கோப்புகளில் குறியொட்டுக்களை எழுத வாசிக்கப்பதற்கு ஆதரவு
+Comment[tg]=Пуштибонии хондан ва навиштани борчаспҳо дар файлҳои расона
+Comment[th]=รองรับการอ่านและเขียนแท็กในแฟ้มสื่อ
+Comment[tr]=Medya dosyalarındaki imleri okumak ve yazmak için destek
+Comment[uk]=Підтримка для читання та запису етикеток в медіафайлах
+Comment[ven]=Thikhedzo ya u vhala na u nwala u itela u tag kha dzifaela dza media
+Comment[zh_CN]=支持对媒体文件的标签进行读取和写入
+Comment[zh_HK]=讀取或寫入媒體檔案標籤的支援
+Comment[zh_TW]=支援讀取或寫入媒體檔案的標籤
+Comment[zu]=Inxaso yokufunda nokubhala amathegi kumafayela ezezindaba
diff --git a/noatun/modules/monoscope/Makefile.am b/noatun/modules/monoscope/Makefile.am
new file mode 100644
index 00000000..55c58a05
--- /dev/null
+++ b/noatun/modules/monoscope/Makefile.am
@@ -0,0 +1,16 @@
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_monoscope.la
+
+noatun_monoscope_la_SOURCES = monoscope.cpp
+#prefs.cpp
+
+noatun_monoscope_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_monoscope_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la -lm
+
+noatun_monoscope_la_METASOURCES = AUTO
+
+noinst_HEADERS = monoscope.h
+#prefs.h
+
+noatun_modules_monoscope_DATA = monoscope.plugin
+noatun_modules_monoscopedir = $(kde_datadir)/noatun
diff --git a/noatun/modules/monoscope/monoscope.cpp b/noatun/modules/monoscope/monoscope.cpp
new file mode 100644
index 00000000..5ab63ecc
--- /dev/null
+++ b/noatun/modules/monoscope/monoscope.cpp
@@ -0,0 +1,112 @@
+#include "monoscope.h"
+#include <noatun/player.h>
+#include <noatun/app.h>
+#include <math.h>
+#include <qpainter.h>
+#include <kactionclasses.h>
+#include <noatun/stdaction.h>
+#include <klocale.h>
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new Monoscope();
+ }
+}
+
+Monoscope::Monoscope() : QWidget(0,0,WRepaintNoErase), MonoScope(30), Plugin()
+{
+ NOATUNPLUGINC(Monoscope);
+
+ mAction=0L;
+ mLowColor=qRgb(0,0,0);
+ mHighColor=qRgb(238,238,238);
+ resize(320, 240);
+ MonoScope::start();
+ setCaption(i18n("Monoscope"));
+ show();
+ resizeEvent(0);
+ repaint(0,0, QWidget::width(), height(), false);
+ resizeEvent(0);
+ setBackgroundColor(mLowColor);
+}
+
+Monoscope::~Monoscope()
+{
+ if(mAction)
+ napp->pluginActionMenu()->remove(mAction);
+}
+
+void Monoscope::init()
+{
+ mAction = new KToggleAction(i18n("Toggle Monoscope"), 0, 0,
+ this, SLOT(toggle()), this, "togglemonoscope");
+ mAction->setChecked(!isHidden());
+ napp->pluginActionMenu()->insert(mAction);
+}
+
+void Monoscope::toggle(void)
+{
+ if(isHidden())
+ show();
+ else
+ hide();
+}
+
+void Monoscope::closeEvent(QCloseEvent *)
+{
+ hide();
+}
+
+void Monoscope::resizeEvent(QResizeEvent *)
+{
+ setSamples(width());
+}
+
+void Monoscope::scopeEvent(float *d, int size)
+{
+ // save cpu
+ if(isHidden()) return;
+
+ const bool line=false;
+
+ int viewWidth =width();
+ int viewHeight=height();
+
+ float *end=d+size;
+ int x=0;
+ int heightHalf=viewHeight/4;
+ int y=viewHeight/2;
+ // reduce flicker
+ QPixmap buffer(viewWidth, viewHeight, -1, QPixmap::BestOptim);
+ buffer.fill(mLowColor);
+ QPainter p(&buffer);
+ p.setPen(mHighColor);
+ repaint(rect());
+
+ if (line)
+ p.moveTo(0, y);
+
+ while (d<=end)
+ {
+ float &n=*d;
+
+ n *= heightHalf;
+ int amp=(int)n;
+
+ if (line) // line
+ p.lineTo(x, y+amp);
+ else // fill
+ p.drawLine(x, y, x, y+amp);
+ d++;
+ x++;
+
+ }
+ if (line)
+ p.drawLine(0, y, size, y);
+ bitBlt(this, 0, 0, &buffer, 0, 0, viewWidth, viewHeight, Qt::CopyROP);
+}
+
+#include "monoscope.moc"
+
diff --git a/noatun/modules/monoscope/monoscope.h b/noatun/modules/monoscope/monoscope.h
new file mode 100644
index 00000000..37031a4b
--- /dev/null
+++ b/noatun/modules/monoscope/monoscope.h
@@ -0,0 +1,34 @@
+#ifndef MONOSCOPE_H
+#define MONOSCOPE_H
+
+#include <noatun/plugin.h>
+
+class KToggleAction;
+
+class Monoscope : public QWidget, public MonoScope, public Plugin
+{
+Q_OBJECT
+NOATUNPLUGIND
+
+public:
+ Monoscope();
+ virtual ~Monoscope();
+
+ void init();
+
+public slots:
+ void toggle(void);
+
+protected:
+ virtual void closeEvent(QCloseEvent *);
+ virtual void scopeEvent(float *data, int bands);
+
+ virtual void resizeEvent(QResizeEvent *);
+
+private:
+ QRgb mHighColor, mLowColor;
+ KToggleAction *mAction;
+};
+
+#endif
+
diff --git a/noatun/modules/monoscope/monoscope.plugin b/noatun/modules/monoscope/monoscope.plugin
new file mode 100644
index 00000000..b4533c0f
--- /dev/null
+++ b/noatun/modules/monoscope/monoscope.plugin
@@ -0,0 +1,99 @@
+Filename=noatun_monoscope.la
+Author=Charles Samuels
+Site=http://noatun.kde.org/
+Email=charles@kde.org
+Type=visualization
+License=BSD
+Name=Monoscope
+Name[af]=Monoskoop
+Name[ar]=مونوسكوب
+Name[az]=Monoskop
+Name[ca]=Monoscopi
+Name[cs]=Monoskop
+Name[da]=Monoskop
+Name[eo]=Monoskopo
+Name[es]=Monoscopio
+Name[fa]=تک دامنه‌ای
+Name[gl]=Monoscopio
+Name[hi]=मोनोस्कोप
+Name[hu]=Monoszkóp
+Name[it]=Monoscopio
+Name[ja]=モノスコープ
+Name[kk]=Осцилограф
+Name[lv]=Monoskops
+Name[mt]=Monoskopju
+Name[ne]=मोमोस्कोप
+Name[pa]=ਮੋਨੋਸਕੋਪ
+Name[pl]=Monoskop
+Name[ro]=Monoscop
+Name[ru]=Моноскоп
+Name[sk]=Monoskop
+Name[sv]=Monoskop
+Name[ta]=ஓரெல்லைப் பரப்பு
+Name[tr]=Monoskop
+Name[uk]=Моноскоп
+Name[xh]=Isithuba esinye
+Name[zh_CN]=单像管
+Name[zh_HK]=單像管
+Name[zh_TW]=單像管
+Name[zu]=Isithuba esinye
+Comment=A neat waveform scope analyzer
+Comment[af]='n netjiese golfvorm skoop analiseerder
+Comment[az]=Dalğa şəklində analiz proqramı
+Comment[bg]=Изящен осцилограф
+Comment[bs]=Zgodan analizator talasne forme
+Comment[ca]=Un analitzador pulcre de camps d'ones
+Comment[cs]=Vkusný analyzátor vln
+Comment[cy]=Dadansoddydd Twt ar gyfer Cwmpas Tonffurf
+Comment[da]=En smart bølgeformsscope-analysator
+Comment[de]=Ein Programm zur "Waveform/Scope"-Analyse
+Comment[el]=Ένας θαυμάσιος αναλυτής φάσματος κυματομορφών
+Comment[en_GB]=A neat waveform scope analyser
+Comment[eo]=Uzinda ondo-analizilo
+Comment[es]=Un analizador elegante de formas de onda
+Comment[et]=Nunnu signaalikuju analüsaator
+Comment[eu]=uhinforma esparruko analizatzailea
+Comment[fa]=تحلیل‌کنندۀ دامنۀ موجی شکل ساده
+Comment[fi]=Nätti oskilloskooppi
+Comment[fr]=Un bel analyseur de spectre
+Comment[gl]=Analizador suave da forma da onda
+Comment[he]=מאבחן תחומי גל
+Comment[hi]=एक साफ वेवफार्म स्कोप एनॉलाइजर
+Comment[hr]=Simpatičan analizator zvuka
+Comment[hu]=Egy kellemes hullámforma-analizáló program
+Comment[is]=Flottur bylgjugreinir
+Comment[it]=Un puro analizzatore di forme d'onda
+Comment[ja]=かっこいい波形スコープアナライザ
+Comment[kk]=Керемет бір осциллограф секілді аспап
+Comment[km]=កម្មវិធី​វិភាគ​វិសាលភាព​ទម្រង់ wave ហ្មត់ចត់
+Comment[ko]=파형을 관찰하고 분석하는 도구
+Comment[lt]=Gražus bangų formos vaizdo analizatorius
+Comment[lv]=Vienkāršs viļņaformas analizators
+Comment[mk]=Симпатичен осцилоскоп за анализа на бранови форми
+Comment[ms]=Penganalisis skop bentuk gelombang
+Comment[nb]=Et nett lite program for analysering av «waveform scope»
+Comment[nds]=En smuck Bülgenformrebeetkieker
+Comment[ne]=क्षेत्र विश्लेषकबाट निट तरङ
+Comment[nl]=Een goed waveform scope-analyseprogramma
+Comment[nn]=Analysering av «waveform scope»
+Comment[pl]=Analizator fal dźwiękowych
+Comment[pt]=Um analisador de formas de onda bonito
+Comment[pt_BR]=Um analisador de formas de onda
+Comment[ro]=Un analizor de formă de undă foarte bun
+Comment[ru]=Симпатичный осциллограф
+Comment[sk]=Milý analyzátor vĺn
+Comment[sl]=Lep analizator zvoka
+Comment[sr]=Уредан анализатор облика таласа
+Comment[sr@Latn]=Uredan analizator oblika talasa
+Comment[sv]=Elegant oscilloskop
+Comment[ta]=ஒரு சீரான அலைவடிவ ஆய்வாளர்
+Comment[tg]=Таҳлилгари намудсози шакли мавҷии шабакавӣ
+Comment[th]=โปรแกรมวิเคราะห์รูปแบบคลื่นที่ปราณีต
+Comment[tr]=Dalga formu analiz programı
+Comment[uk]=Гарний аналізатор хвильових меж
+Comment[ven]=Mugudi neat waveform scope
+Comment[xh]=Umhlahleli wesithuba wendlela ecocekileyo yamaza
+Comment[zh_CN]=一个小巧的波形范围分析器
+Comment[zh_HK]=小巧的波形範圍分析器
+Comment[zh_TW]=一個小巧的波形分析器
+Comment[zu]=Umhaziyi wesithuba wendlela ecehlanzekile
diff --git a/noatun/modules/net/Makefile.am b/noatun/modules/net/Makefile.am
new file mode 100644
index 00000000..9398a602
--- /dev/null
+++ b/noatun/modules/net/Makefile.am
@@ -0,0 +1,14 @@
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_net.la
+
+noatun_net_la_SOURCES = net.cpp
+
+noatun_net_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_net_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la
+
+noatun_net_la_METASOURCES = AUTO
+
+noinst_HEADERS = net.h
+
+noatun_modules_net_DATA = net.plugin
+noatun_modules_netdir = $(kde_datadir)/noatun
diff --git a/noatun/modules/net/net.cpp b/noatun/modules/net/net.cpp
new file mode 100644
index 00000000..5e3ba707
--- /dev/null
+++ b/noatun/modules/net/net.cpp
@@ -0,0 +1,57 @@
+#include "net.h"
+#include <noatun/player.h>
+#include <noatun/app.h>
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new Net();
+ }
+}
+
+
+Net::Net() : QServerSocket(7539, 10), Plugin()
+{
+ mFDs.setAutoDelete(true);
+ connect(napp->player(), SIGNAL(newSong()), SLOT(newSong()));
+}
+
+Net::~Net()
+{
+}
+
+
+void Net::newConnection(int fd)
+{
+ QSocket *s=new QSocket;
+ s->setSocket(fd);
+ mFDs.append(s);
+}
+
+void Net::newSong()
+{
+ if (!napp->player()->current())
+ return;
+
+ for (QSocket *i=mFDs.first(); i!=0; i=mFDs.next())
+ {
+ QCString line;
+ line=napp->player()->current().title().latin1();
+ line+='\n';
+ ::write(i->socket(), (const void*)line.data(), line.length());
+ }
+}
+
+void Net::closed()
+{
+ for (QSocket *i=mFDs.first(); i!=0; i=mFDs.next())
+ {
+ if (sender()==i)
+ mFDs.removeRef(i);
+ }
+}
+
+
+
+#include "net.moc"
diff --git a/noatun/modules/net/net.h b/noatun/modules/net/net.h
new file mode 100644
index 00000000..7d0fcb11
--- /dev/null
+++ b/noatun/modules/net/net.h
@@ -0,0 +1,31 @@
+#ifndef DCOPIFACE_H
+#define DCOPIFACE_H
+
+#include <noatun/player.h>
+#include <noatun/plugin.h>
+#include <qserversocket.h>
+#include <qsocket.h>
+#include <unistd.h>
+
+class Net : public QServerSocket, public Plugin
+{
+Q_OBJECT
+
+public:
+ Net();
+ ~Net();
+
+public slots:
+ void newSong();
+
+private slots:
+ void closed();
+protected:
+ virtual void newConnection(int socket);
+
+private:
+ QPtrList<QSocket> mFDs;
+};
+
+#endif
+
diff --git a/noatun/modules/net/net.plugin b/noatun/modules/net/net.plugin
new file mode 100644
index 00000000..21683b5d
--- /dev/null
+++ b/noatun/modules/net/net.plugin
@@ -0,0 +1,136 @@
+Filename=noatun_net.la
+Author=Charles Samuels
+Site=http://noatun.derkarl.org/
+Email=charles@kde.org
+Type=other
+License=Public Domain
+Name=Network Interface
+Name[af]=Netwerk Koppelvlak
+Name[ar]=واجهة الشبكة
+Name[az]=Şəbəkə Ara üzü
+Name[bn]=নেটওয়ার্ক ইন্টারফেস
+Name[br]=Etrefas Rouedad
+Name[bs]=Mrežni interfejs
+Name[ca]=Interfície de xarxa
+Name[cs]=Síťové rozhraní
+Name[cy]=Rhyngwyneb Rhwydwaith
+Name[da]=Netværksgrænseflade
+Name[de]=Netzwerk-Schnittstelle
+Name[el]=Διασύνδεση δικτύου
+Name[eo]=Retinterfaco
+Name[es]=Interfaz de red
+Name[et]=Võrguliides
+Name[eu]=Sareko interfazea
+Name[fa]=واسط شبکه
+Name[fi]=Verkkokäyttöliittymä
+Name[fr]=Interface réseau
+Name[ga]=Comhéadan Gréasáin
+Name[gl]=Interface de rede
+Name[he]=ממשק רשת
+Name[hi]= नेटवर्क इंटरफेस
+Name[hr]=Mrežno sučelje
+Name[hu]=Hálózati felület
+Name[id]=Interface jaringan
+Name[is]=Nettengi
+Name[it]=Interfaccia di rete
+Name[ja]=ネットワークインターフェース
+Name[kk]=Желілік интерфейсі
+Name[km]=ចំណុច​ប្រទាក់​បណ្ដាញ
+Name[ko]=네트워크 인터페이스
+Name[lt]=Tinklo sąsaja
+Name[lv]=Tīkla Starpseja
+Name[mk]=Мрежен интерфејс
+Name[mt]=Interfaċċja tan-Network
+Name[nb]=Nettverksgrensesnitt
+Name[nds]=Nettwark-Koppelsteed
+Name[ne]=सञ्जाल इन्टरफेस
+Name[nl]=Netwerk-interface
+Name[nn]=Nettverksgrensesnitt
+Name[pa]=ਨੈਟਵਰਕ ਇੰਟਰਫੇਸ
+Name[pl]=Interfejs sieciowy
+Name[pt]=Interface de Rede
+Name[pt_BR]=Interface de Rede
+Name[ro]=Interfată reţea
+Name[ru]=Сетевой интерфейс
+Name[se]=Fierpmádatlakta
+Name[sk]=Sieťové rozhranie
+Name[sl]=Omrežni vmesnik
+Name[sr]=Мрежни интерфејс
+Name[sr@Latn]=Mrežni interfejs
+Name[sv]=Nätverksgränssnitt
+Name[ta]=வலைப்பின்னல் இடைமுகம்
+Name[tg]=Интерфейси Шабакавӣ
+Name[th]=แผงวงจรเครือข่าย
+Name[tr]=Ağ Arayüzü
+Name[uk]=Інтерфейс мережі
+Name[uz]=Tarmoq interfeysi
+Name[uz@cyrillic]=Тармоқ интерфейси
+Name[ven]=Vhukwamani
+Name[xh]=Ujongano nomsebenzi womnatha
+Name[zh_CN]=网络接口
+Name[zh_HK]=網絡介面
+Name[zh_TW]=網路介面
+Name[zu]=Uxhumano Olubhekene loxhumaniso
+Comment=A very simple read-only network interface on port 7539
+Comment[af]='n baie eenvoudige lees-alleen netwerk koppelvlak op poort 7539
+Comment[ar]=واجهة بسيطة للشبكة قابلة للقراءة فقط على المنفذ رقم 7539
+Comment[az]=Çox bəsit bir sırf-oxuma şəbəkə axtar üzü (7539.port)
+Comment[bg]=Елементарен мрежов интерфейс само за четене на порт 7539
+Comment[bs]=Veoma jednostavan read-only mrežni interfejs na portu 7539
+Comment[ca]=Una interfície de xarxa molt simple de només lectura al port 7539
+Comment[cs]=Velmi jednoduché síťové rozhraní určené jen pro čtení na portu 7539
+Comment[cy]=Rhyngwyneb rhwydwaith syml darllen-yn-unig ar borth 7539
+Comment[da]=En meget simpel skrivebeskyttet netværksgrænseflade på port 7539
+Comment[de]=Einfache Netzwerkschnittstelle an Port 7539
+Comment[el]=Μια πολύ απλή διασύνδεση δικτύου, μόνο-ανάγνωσης, στη θύρα 7539
+Comment[eo]=Simpla nurlega retinterfaco por a pordo 7539
+Comment[es]=Una interfaz de red simple de sólo lectura en el puerto 7539
+Comment[et]=Väga lihtne pordil 7539 töötav võrguliides (ainult lugemiseks)
+Comment[eu]=Irakurketarako bakarrik den 7539 atakako sare interfaze oso sinplea
+Comment[fa]=یک واسط شبکه فقط خواندنی بسیار ساده روی درگاه ۷۵۳۹
+Comment[fi]=Yksinkertainen vain-luku-verkkokäyttöliittymä portissa 7539
+Comment[fr]=Une interface réseau très simple sur le port 7539
+Comment[ga]=Comhéadan an-simplí líonra ar phort 7539
+Comment[gl]=Unha interface de rede moi sinxela de só lectura no porto 7539
+Comment[he]=ממשק רשת פשוט מאוד לקריאה בלבד ביציאה 7539
+Comment[hi]=एक अत्यंत साधारण सिर्फ पढ़ने लायक नेटवर्क इंटरफेल पोर्ट 7539 पर
+Comment[hr]=Vrlo jednostavno mrežno sučelje za čitanje s porta 7539
+Comment[hu]=Egy nagyon egyszerű, csak olvasható hálózati felület a 7539-es porton
+Comment[is]=Mjög einfalt ritvarið nettengi á porti 7539
+Comment[it]=Un'interfaccia su porta 7539 di sola lettura molto semplice
+Comment[ja]=ポート 7539 を使う非常にシンプルな読み込み専用のネットワークインターフェース
+Comment[kk]=Өте қарапайым, 7539 портынан тек оқу үшін желілік интерфейсі
+Comment[km]=ចំណុច​ប្រទាក់​បណ្ដាញ​សាមញ្ញ​បំផុត ហើយ​បាន​តែ​អាន ដែល​រត់​លើ​ច្រក 7539
+Comment[ko]=7539 포트에서 작동하는 간단한 읽기 전용 네트워크 인터페이스
+Comment[lt]=Labai paprasta tik skaitoma tinklo sąsaja 7539'am portui
+Comment[lv]=Ļoti vienkārša tikai-lasīšana tīkla starpseja uz porta 7539
+Comment[mk]=Многу едноставен мрежен интерфејс, само за читање, на порта 7539
+Comment[ms]=Antaramuka jaringan baca sahaja yang ringkas untuk liang 7539
+Comment[mt]=Interfaċċja sempiliċi ħafna tinqara biss fuq port 7539
+Comment[nb]=Et veldig enkelt skrivebeskyttet nettverksgrensesnitt på port 7539
+Comment[nds]=En bannig eenfach "Bloots-lesen"-Nettwarkkoppelsteed op Port 7539
+Comment[ne]=पोर्ट ७५३९ मा धेरै साधारण पढ्ने-मात्र सञ्जाल
+Comment[nl]=Een heel eenvoudige alleen-lezen netwerkinterface op poort 7539
+Comment[nn]=Eit svært enkelt nettverksgrensesnitt for lesing på port 7539
+Comment[pl]=Bardzo prosty interfejs sieciowy tylko do odczytu, na porcie 7539
+Comment[pt]=Uma interface de rede muito simples no porto 7539
+Comment[pt_BR]=Uma interface muito simples de rede, porta 7539, somente leitura
+Comment[ro]=O interfaţă de reţea foarte simplă pe portul 7539
+Comment[ru]=Простейший сетевой интерфейс на 7539 порту только для чтения
+Comment[se]=Hui oktageardanis fierpmádatlakta mii lohká dieđuid 7539 verráhis
+Comment[sk]=Veľmi jednoduché sieťové rozhranie iba pre čítanie na porte 7539
+Comment[sl]=Zelo preprost bralni omrežni vmesnik na vratih 7539
+Comment[sr]=Једноставан, само-за-читање мрежни интерфејс на порту 7539
+Comment[sr@Latn]=Jednostavan, samo-za-čitanje mrežni interfejs na portu 7539
+Comment[sv]=Väldigt enkelt skrivskyddat nätverksgränssnitt på port 7539
+Comment[ta]=துறை 7539இலுள்ள மிக எளிய, வாசிக்க-மட்டும் வலைத்தள இடைமுடம்
+Comment[tg]=Интерфейси хеле содда ва танҳо барои хониши шабакавӣ дар даргоҳи 7539
+Comment[th]=แผงวงจรเครือข่ายบนพอร์ต 7539
+Comment[tr]=Çok basit bir salt-oku ağ arayüzü (7539.port)
+Comment[uk]=Дуже простий інтерфейс мережі для зчитування на порту 7539
+Comment[ven]=Vhukwamani ho leluwaho hau vhala kha port ya 7539
+Comment[xh]=Ufundo olulula kakhulu-lujongano lomsebenzi womnatha kuphela kwizibuko 7539
+Comment[zh_CN]=简单的位于 7539 端口的只读网络接口
+Comment[zh_HK]=工作於 7539 連接埠的唯讀簡易型網絡介面
+Comment[zh_TW]=一個位於 7539 Port 上唯讀存取的簡易型網路介面
+Comment[zu]=Okulula ukufunda kuphela kuxhumaniso olubhekeneyo kwi port 7539
diff --git a/noatun/modules/noatunui/Makefile.am b/noatun/modules/noatunui/Makefile.am
new file mode 100644
index 00000000..443afd6c
--- /dev/null
+++ b/noatun/modules/noatunui/Makefile.am
@@ -0,0 +1,16 @@
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_ui.la
+
+noatun_ui_la_SOURCES = noatunui.cpp userinterface.cpp
+
+noatun_ui_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_ui_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la \
+ $(top_builddir)/noatun/library/libnoatuncontrols.la \
+ -lqtmcop -lkmedia2_idl -lsoundserver_idl
+
+noatun_ui_la_METASOURCES = AUTO
+
+noinst_HEADERS = userinterface.h
+
+noatun_modules_ui_DATA = noatunui.plugin
+noatun_modules_uidir = $(kde_datadir)/noatun
diff --git a/noatun/modules/noatunui/noatunui.cpp b/noatun/modules/noatunui/noatunui.cpp
new file mode 100644
index 00000000..adb9534b
--- /dev/null
+++ b/noatun/modules/noatunui/noatunui.cpp
@@ -0,0 +1,9 @@
+#include "userinterface.h"
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new MilkChocolate;
+ }
+}
diff --git a/noatun/modules/noatunui/noatunui.plugin b/noatun/modules/noatunui/noatunui.plugin
new file mode 100644
index 00000000..e8413cd8
--- /dev/null
+++ b/noatun/modules/noatunui/noatunui.plugin
@@ -0,0 +1,120 @@
+Filename=noatun_ui.la
+Author=Charles Samuels
+Site=http://noatun.kde.org/plugins/milkchocolate/
+Email=charles@kde.org
+Type=userinterface
+License=Artistic
+Name=Milk-Chocolate
+Name[ar]=حليب-شوكولا
+Name[az]=Şokaladlı Süd
+Name[br]=Chokolad leizh
+Name[bs]=Mlijeko-čokolada
+Name[ca]=Xocolata amb llet
+Name[cs]=Noatun mléčná čokoláda
+Name[cy]=Sioclat Llefrith
+Name[da]=Mælkechokolade
+Name[de]=Milchschokolade
+Name[eo]=Laktoĉokolado
+Name[es]=Chocolate con leche
+Name[fi]=Maitosuklaa
+Name[fr]=Chocolat au lait
+Name[gl]=Leite-Chocolate
+Name[hi]=मिल्क-चॉकलेट
+Name[hr]=Mliječna čokolada
+Name[hu]=Tejcsoki
+Name[is]=Mjólkursúkkulaði
+Name[it]=Caffellatte
+Name[km]=ទឹកដោះគោ-សូកូឡា
+Name[ko]=밀크 초콜릿
+Name[lt]=Pieniškas šokoladas
+Name[lv]=Piena-Šokolāde
+Name[mk]=Млечна чоколада
+Name[mt]=Ċikkulata
+Name[nb]=Melkesjokolade
+Name[nds]=Melkschokolaad
+Name[ne]=दुध-मिठाइ
+Name[nl]=Melkchocolade
+Name[nn]=Mjølkesjokolade
+Name[pl]=Czekolada mleczna
+Name[pt]=Leite com Chocolate
+Name[pt_BR]=Noatun Leite-Chocolate
+Name[ro]=Ciocolată cu lapte
+Name[ru]=Молочный шоколад
+Name[se]=Mielkešokolada
+Name[sk]=Mliečna čokoláda
+Name[sl]=Mlečna čokolada
+Name[sv]=Mjölkchoklad
+Name[ta]=மில்க்-சாக்லேட்
+Name[tg]=Шоколади Ширӣ
+Name[tr]=Çikolatalı Süt
+Name[uk]=Молочний шоколад
+Name[ven]=Tshokholeiti ya mafhi
+Name[xh]=Ubisi-ichocolate
+Name[zh_CN]=牛奶巧克力
+Name[zh_HK]=牛奶巧克力
+Name[zh_TW]=牛奶巧克力
+Name[zu]=Ushokolade Wobisi
+Comment=Noatun's simple GUI
+Comment[ar]=واجهة بسيطة جدا لNoatun
+Comment[az]=Noatun bəsit axtar üzü
+Comment[bg]=Семпъл графичен интерфейс за Noatun
+Comment[bs]=Jednostavan Noatunov GUI
+Comment[ca]=IGU senzill pel Noatun
+Comment[cs]=Jednoduché rozhraní pro Noatun
+Comment[cy]=GUI syml Noatun
+Comment[da]=Noatuns enkle GUI
+Comment[de]=Noatuns einfache Oberfläche
+Comment[el]=Το απλό περιβάλλον χρήσης του Noatun
+Comment[eo]=La simpla grafika etoso de Noatun
+Comment[es]=Interfaz simple de Noatun
+Comment[et]=Lihtne Noatuni kasutajaliides
+Comment[eu]=Noatun-en GUI simplea
+Comment[fa]=ونک ساده Noatun
+Comment[fi]=Noatunin yksinkertainen käyttöliittymä
+Comment[fr]=Interface graphique simple de Noatun
+Comment[ga]=Comhéadan simplí grafach ar Noatun
+Comment[gl]=GUI sinxela de Noatun
+Comment[he]=ממשק המשתמש הגרפי הפשוט של Noatun
+Comment[hi]=नोआट्यून का साधारण जीयूआई
+Comment[hr]=Noatunovo jednostavno sučelje
+Comment[hu]=A Noatun egyszerű grafikus kezelői felülete
+Comment[id]=Contoh Noatun'GUI
+Comment[is]=Hið einfalda viðmót Nóatúns forritsins
+Comment[it]=Interfaccia grafica semplice di Noatun
+Comment[ja]=Noatun のシンプルな GUI
+Comment[kk]=Noatun-ның қарапайым графикалық интерфейсі
+Comment[km]=ចំណុច​ប្រទាក់​ក្រាហ្វិក​សាមញ្ញ​របស់ Noatun
+Comment[ko]=Noatun의 간단한 GUI
+Comment[lt]=Noatun paprastas GUI
+Comment[lv]=Vienkāršs Noatuna GUI
+Comment[mk]=Едноставен графички интерфејс за Noatun
+Comment[ms]=GUI Noatun yang ringkas
+Comment[mt]=GUI sempliċi għal Noatun
+Comment[nb]=Noatuns enkle GUI
+Comment[nds]=Eenfach Böversiet vun Noatun
+Comment[ne]=नोवटुनको साधारण GUI
+Comment[nl]=Noatun's eenvoudige GUI
+Comment[nn]=Det enkle Noatun-GUI-et
+Comment[pa]=ਨੋਟੌਮ ਦਾ ਸਧਾਰਨ GUI
+Comment[pl]=Prosty motyw Noatun
+Comment[pt]=Uma interface simples do Noatun
+Comment[pt_BR]=Interface simplificada do Noatun
+Comment[ro]=Interfaţă grafică Noatun simplă
+Comment[ru]=Простой графический интерфейс Noatun
+Comment[se]=Noatuna oktageardanis grafihkkalaš lakta
+Comment[sk]=Jednoduché rozhranie pre Noatun
+Comment[sl]=Noatunov preprost uporabniški vmesnik
+Comment[sr]=Noatun-ов једноставан GUI
+Comment[sr@Latn]=Noatun-ov jednostavan GUI
+Comment[sv]=Noatuns enkla gränssnitt
+Comment[ta]=Noatun'இன் எளிய முகப்பொன்று
+Comment[tg]=Интерфейси Графикии соддаи Noatun
+Comment[th]=ส่วนติดต่อผู้ใช้แบบกราฟิกแบบเรียบง่ายของ Noatun
+Comment[tr]=Noatun basit arayüzü
+Comment[uk]=Простий графічний інтерфейс Noatun
+Comment[ven]=Noatun yo leluwaho ya GUI
+Comment[xh]=iGUI elula ye Noatun
+Comment[zh_CN]=Noatun 的简单界面
+Comment[zh_HK]=Noatun 的簡單圖形界面
+Comment[zh_TW]=Noatun 簡單圖形界面
+Comment[zu]=I GUI elula ye Noatun
diff --git a/noatun/modules/noatunui/userinterface.cpp b/noatun/modules/noatunui/userinterface.cpp
new file mode 100644
index 00000000..c13d4d6d
--- /dev/null
+++ b/noatun/modules/noatunui/userinterface.cpp
@@ -0,0 +1,315 @@
+/*
+ * noatun.cpp
+ *
+ * Copyright (C) 1999 Charles Samuels <charles@kde.org>
+ */
+
+#include "userinterface.h"
+#include <noatun/playlist.h>
+#include <noatun/stdaction.h>
+#include <noatun/app.h>
+#include <noatun/player.h>
+#include <noatun/controls.h>
+#include <noatun/effects.h>
+
+#include <qpushbutton.h>
+#include <qdragobject.h>
+#include <qlayout.h>
+#include <qtooltip.h>
+#include <qobjectlist.h>
+#include <qobjectdict.h>
+
+#include <kpopupmenu.h>
+#include <kstatusbar.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kurldrag.h>
+#include <kfiledialog.h>
+#include <kconfig.h>
+
+MilkChocolate::MilkChocolate() : QWidget(0,"NoatunUI"), UserInterface()
+{
+ setAcceptDrops(true);
+ static const int buttonSize=32;
+
+ { // The buttons
+ mBack=new QPushButton(this);
+ mBack->setFixedSize(buttonSize,buttonSize);
+ mBack->setPixmap(BarIcon("noatunback"));
+ connect(mBack, SIGNAL(clicked()), napp->player(), SLOT(back()));
+ QToolTip::add(mBack,i18n("Back"));
+
+ mStop=new QPushButton(this);
+ mStop->setFixedSize(buttonSize,buttonSize);
+ mStop->setPixmap(BarIcon("noatunstop"));
+ connect(mStop, SIGNAL(clicked()), napp->player(), SLOT(stop()));
+ QToolTip::add(mStop, i18n("Stop"));
+
+ mPlay=new QPushButton(this);
+ mPlay->setToggleButton(true);
+ mPlay->setFixedSize(buttonSize,buttonSize);
+ mPlay->setPixmap(BarIcon("noatunplay"));
+ connect(mPlay, SIGNAL(clicked()), napp->player(), SLOT(playpause()));
+ QToolTip::add(mPlay, i18n("Play"));
+
+ mForward=new QPushButton(this);
+ mForward->setFixedSize(buttonSize,buttonSize);
+ mForward->setPixmap(BarIcon("noatunforward"));
+ connect(mForward, SIGNAL(clicked()), napp->player(), SLOT(forward()));
+ QToolTip::add(mForward, i18n("Forward"));
+
+ mPlaylist=new QPushButton(this);
+ mPlaylist->setToggleButton(true);
+ mPlaylist->setFixedSize(buttonSize,buttonSize);
+ mPlaylist->setPixmap(BarIcon("noatunplaylist"));
+ connect(mPlaylist, SIGNAL(clicked()), napp->player(), SLOT(toggleListView()));
+ QToolTip::add(mPlaylist, i18n("Playlist"));
+
+ mLoop=new QPushButton(this);
+ mLoop->setFixedSize(buttonSize,buttonSize);
+ mLoop->setPixmap(BarIcon("noatunloopnone"));
+ connect(mLoop, SIGNAL(clicked()), napp->player(), SLOT(loop()));
+ QToolTip::add(mLoop, i18n("Change loop style"));
+
+ mPopup=new QPushButton(this);
+ mPopup->setFixedSize(buttonSize,buttonSize);
+ mPopup->setPixmap(BarIcon("noatun"));
+ connect(mPopup, SIGNAL(clicked()), SLOT(popup()));
+// QToolTip::add(mRemoveCurrent, i18n("Remove current file from playlist"));
+
+ }
+
+ mVolume=new L33tSlider(0,100,10,0, Horizontal, this);
+ mVolume->setValue(napp->player()->volume());
+ mSeeker=new L33tSlider(0,1000,10,0, Horizontal, this);
+
+ mStatusBar=new KStatusBar(this);
+
+
+ QGridLayout *l=new QGridLayout(this);
+ l->addWidget(mBack,0,0);
+ l->addWidget(mStop,0,1);
+ l->addWidget(mPlay,0,2);
+ l->addWidget(mForward,0,3);
+ l->addWidget(mPlaylist,0,4, Qt::AlignLeft);
+ l->addWidget(mLoop,0,5);
+ l->addWidget(mPopup,0,6);
+ l->addColSpacing(4, buttonSize+8);
+
+ l->addMultiCellWidget(mVolume,1,1,0,6);
+ l->addMultiCellWidget(mSeeker,2,2,0,6);
+ l->addMultiCellWidget(mStatusBar,3,3,0,6);
+
+ statusBar()->message(i18n("No File Loaded"));
+ statusBar()->insertItem("--:--/--:--", 1, 0, true);
+
+ connect(napp, SIGNAL(hideYourself()), this, SLOT(hide()) );
+ connect(napp, SIGNAL(showYourself()), this, SLOT(show()) );
+
+ connect(napp->player(), SIGNAL(playing()), this, SLOT(slotPlaying()));
+ connect(napp->player(), SIGNAL(stopped()), this, SLOT(slotStopped()));
+ connect(napp->player(), SIGNAL(paused()), this, SLOT(slotPaused()));
+ napp->player()->handleButtons();
+
+ connect(napp->player(), SIGNAL(timeout()), this, SLOT(slotTimeout()));
+ connect(napp->player(), SIGNAL(loopTypeChange(int)), this, SLOT(changeLoopType(int)));
+
+// if(seeker())
+ {
+ /* This skipToWrapper is needed to pass milliseconds to Player() as everybody
+ * below the GUI is based on milliseconds instead of some unprecise thingy
+ * like seconds or mille */
+ connect(seeker(), SIGNAL(userChanged(int)), this, SLOT(skipToWrapper(int)));
+ connect(this, SIGNAL(skipTo(int)), napp->player(), SLOT(skipTo(int)));
+ connect(seeker(), SIGNAL(sliderMoved(int)), SLOT(sliderMoved(int)));
+ }
+ connect(mVolume, SIGNAL(sliderMoved(int)), napp->player(), SLOT(setVolume(int)));
+ connect(mVolume, SIGNAL(userChanged(int)), napp->player(), SLOT(setVolume(int)));
+
+
+ connect(napp->player(), SIGNAL(playlistShown()), SLOT(playlistShown()));
+ connect(napp->player(), SIGNAL(playlistHidden()), SLOT(playlistHidden()));
+
+ // Event Filter for the RMB
+ for (QPtrListIterator<QObject> i(*children()); i.current(); ++i)
+ (*i)->installEventFilter(this);
+
+ setCaption("Noatun");
+ setIcon(BarIcon("noatun"));
+ show();
+
+ // What it is now, stay, stay.. roll over, good boy!
+ setFixedSize(minimumSize());
+}
+
+MilkChocolate::~MilkChocolate()
+{
+ // If cfg dialog is still open, delete it so it saves it's position
+// if(prefDlgExist)
+// delete prefDlg;
+}
+
+void MilkChocolate::closeEvent(QCloseEvent*)
+{
+ unload();
+}
+
+void MilkChocolate::showEvent(QShowEvent*e)
+{
+ QWidget::showEvent(e);
+}
+
+void MilkChocolate::dragEnterEvent(QDragEnterEvent *event)
+{
+ // accept uri drops only
+ event->accept(KURLDrag::canDecode(event));
+}
+
+void MilkChocolate::dropEvent(QDropEvent *event)
+{
+ KURL::List uri;
+ if (KURLDrag::decode(event, uri))
+ {
+ for (KURL::List::Iterator i = uri.begin(); i != uri.end(); ++i)
+ napp->player()->openFile(*i, false);
+ }
+}
+
+void MilkChocolate::mouseReleaseEvent(QMouseEvent *e)
+{
+ QWidget::mouseReleaseEvent(e);
+ if (e->button()!=RightButton) return;
+ NoatunStdAction::ContextMenu::showContextMenu();
+}
+
+void MilkChocolate::changeStatusbar(const QString& text, const QString &text2)
+{
+ if (!text2.isNull())
+ statusBar()->changeItem(text2, 1);
+
+ statusBar()->message(!text.isNull() ? text : napp->player()->current().title());
+}
+
+void MilkChocolate::changeCaption(const QString& text)
+{
+ setCaption(text);
+}
+
+void MilkChocolate::popup()
+{
+ NoatunStdAction::ContextMenu::showContextMenu(
+ mapToGlobal(mPopup->geometry().bottomLeft())
+ );
+}
+
+void MilkChocolate::slotPlaying()
+{
+// connect(kwinmodule, SIGNAL(windowAdded(WId)), view, SLOT(attemptReparent(WId)));
+ changeStatusbar(napp->player()->current().title(), napp->player()->lengthString());
+ mPlay->setOn(true);
+ mStop->setEnabled(true);
+ mPlay->setPixmap(BarIcon("noatunpause"));
+}
+
+void MilkChocolate::slotStopped()
+{
+ if (!napp->player()->current()) return;
+ changeStatusbar(i18n("No File Loaded"), napp->player()->lengthString());
+ mStop->setEnabled(false);
+ mPlay->setOn(false);
+ seeker()->setValue(0);
+ mPlay->setPixmap(BarIcon("noatunplay"));
+}
+
+void MilkChocolate::slotPaused()
+{
+ mStop->setEnabled(true);
+ mPlay->setOn(false);
+ mPlay->setPixmap(BarIcon("noatunplay"));
+}
+
+void MilkChocolate::slotTimeout()
+{
+ mVolume->setValue(napp->player()->volume());
+
+ if (!napp->player()->current()) return;
+ if (static_cast<L33tSlider*>(seeker())->currentlyPressed()) return;
+ if (seeker())
+ {
+ seeker()->setRange ( 0, (int)napp->player()->getLength()/1000 );
+ seeker()->setValue ( (int)napp->player()->getTime()/1000 );
+ }
+ changeStatusbar(0, napp->player()->lengthString());
+}
+
+void MilkChocolate::sliderMoved(int seconds)
+{
+ if (napp->player()->current())
+ changeStatusbar(0, napp->player()->lengthString(seconds*1000));
+}
+
+void MilkChocolate::skipToWrapper(int second)
+{
+ emit skipTo((long)(second*1000));
+}
+
+void MilkChocolate::changeLoopType(int t)
+{
+ static const int time=2000;
+ switch (t)
+ {
+ case(Player::None):
+ statusBar()->message(i18n("No looping"), time);
+ mLoop->setPixmap(BarIcon("noatunloopnone"));
+ break;
+ case(Player::Song):
+ statusBar()->message(i18n("Song looping"), time);
+ mLoop->setPixmap(BarIcon("noatunloopsong"));
+ break;
+ case(Player::Playlist):
+ statusBar()->message(i18n("Playlist looping"), time);
+ mLoop->setPixmap(BarIcon("noatunloopplaylist"));
+ break;
+ case(Player::Random):
+ statusBar()->message(i18n("Random play"), time);
+ mLoop->setPixmap(BarIcon("noatunlooprandom"));
+ }
+
+}
+
+bool MilkChocolate::eventFilter(QObject *o, QEvent *e)
+{
+ if ((e->type() == QEvent::MouseButtonRelease)
+ && ((static_cast<QMouseEvent*>(e))->button()==RightButton))
+ {
+ mouseReleaseEvent(static_cast<QMouseEvent*>(e));
+ return true;
+ }
+
+ if (e->type() == QEvent::Wheel)
+ {
+ wheelEvent(static_cast<QWheelEvent*>(e));
+ return true;
+ }
+ return QWidget::eventFilter(o, e);
+}
+
+void MilkChocolate::playlistShown()
+{
+ mPlaylist->setOn(true);
+}
+
+void MilkChocolate::playlistHidden()
+{
+ mPlaylist->setOn(false);
+}
+
+void MilkChocolate::wheelEvent(QWheelEvent *e)
+{
+ int delta=e->delta();
+ mVolume->setValue(mVolume->value()+(delta/120));
+ napp->player()->setVolume(mVolume->value()+(delta/120));
+}
+
+#include "userinterface.moc"
diff --git a/noatun/modules/noatunui/userinterface.h b/noatun/modules/noatunui/userinterface.h
new file mode 100644
index 00000000..94dc9c44
--- /dev/null
+++ b/noatun/modules/noatunui/userinterface.h
@@ -0,0 +1,72 @@
+#ifndef USERINTERFACE_H
+#define USERINTERFACE_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <noatun/plugin.h>
+#include <noatun/app.h>
+
+// Pref dialog pointer global now for position saving
+#include <noatun/pref.h>
+
+class Player;
+class QSlider;
+class QPushButton;
+class KStatusBar;
+
+/**
+ * @short Main window class
+ * @author Charles Samuels <charles@kde.org>
+ * @version 0.1
+ */
+class MilkChocolate : public QWidget, public UserInterface
+{
+Q_OBJECT
+public:
+ MilkChocolate();
+ virtual ~MilkChocolate();
+ void load(const QString& url);
+
+protected:
+ virtual void dragEnterEvent(QDragEnterEvent *event);
+ virtual void dropEvent(QDropEvent *event);
+ virtual void closeEvent(QCloseEvent*);
+ virtual void showEvent(QShowEvent*e);
+ virtual void mouseReleaseEvent(QMouseEvent *);
+ virtual bool eventFilter(QObject*, QEvent*);
+ virtual void wheelEvent(QWheelEvent *e);
+
+protected:
+ QSlider *seeker() const { return mSeeker; }
+ KStatusBar *statusBar() const { return mStatusBar; }
+
+public slots:
+ void slotPlaying();
+ void slotStopped();
+ void slotPaused();
+
+ void playlistShown();
+ void playlistHidden();
+
+ void slotTimeout();
+ void sliderMoved(int seconds);
+ void changeLoopType(int t);
+ void skipToWrapper(int second);
+
+signals:
+ void skipTo( int ); // emitted by skipToWrapper()
+
+private slots:
+ void changeStatusbar(const QString& text, const QString &text2=0);
+ void changeCaption(const QString& text);
+ void popup();
+
+private:
+ QPushButton *mBack, *mStop, *mPlay, *mForward, *mPlaylist, *mPopup, *mLoop;
+ QSlider *mSeeker, *mVolume;
+ KStatusBar *mStatusBar;
+};
+
+#endif
diff --git a/noatun/modules/simple/Makefile.am b/noatun/modules/simple/Makefile.am
new file mode 100644
index 00000000..de5a7aa8
--- /dev/null
+++ b/noatun/modules/simple/Makefile.am
@@ -0,0 +1,16 @@
+INCLUDES= -I$(top_srcdir)/noatun/library -I$(kde_includes)/arts $(all_includes)
+
+kde_module_LTLIBRARIES = noatunsimple.la
+
+noatunsimple_la_SOURCES = propertiesdialog.ui noatunui.cpp userinterface.cpp
+noatunsimple_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module
+noatunsimple_la_LIBADD = $(top_builddir)/noatun/library/libnoatun.la \
+ $(top_builddir)/noatun/library/libnoatuncontrols.la \
+ -lkmedia2_idl -lsoundserver_idl -lartskde
+
+noatunsimple_la_METASOURCES = AUTO
+
+noinst_HEADERS = propertiesdialog.h userinterface.h
+
+noatun_modules_simple_DATA = simple.plugin simpleui.rc
+noatun_modules_simpledir = $(kde_datadir)/noatun
diff --git a/noatun/modules/simple/back.xpm b/noatun/modules/simple/back.xpm
new file mode 100644
index 00000000..bd433e5e
--- /dev/null
+++ b/noatun/modules/simple/back.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+const char * back_xpm[] = {
+"16 16 2 1",
+" c None",
+". c #000000",
+" ",
+" ",
+" ",
+" .... .. ",
+" .... ... ",
+" .... .... ",
+" .... ..... ",
+" .... ...... ",
+" .... ...... ",
+" .... ..... ",
+" .... .... ",
+" .... ... ",
+" .... .. ",
+" ",
+" ",
+" "};
diff --git a/noatun/modules/simple/forward.xpm b/noatun/modules/simple/forward.xpm
new file mode 100644
index 00000000..c4e95ccc
--- /dev/null
+++ b/noatun/modules/simple/forward.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+const char * forward_xpm[] = {
+"16 16 2 1",
+" c None",
+". c #000000",
+" ",
+" ",
+" ",
+" .. .... ",
+" ... .... ",
+" .... .... ",
+" ..... .... ",
+" ...... .... ",
+" ...... .... ",
+" ..... .... ",
+" .... .... ",
+" ... .... ",
+" .. .... ",
+" ",
+" ",
+" "};
diff --git a/noatun/modules/simple/noatunui.cpp b/noatun/modules/simple/noatunui.cpp
new file mode 100644
index 00000000..3b647e42
--- /dev/null
+++ b/noatun/modules/simple/noatunui.cpp
@@ -0,0 +1,17 @@
+/*
+ This file is part of KDE/aRts (Noatun) - xine integration
+ Copyright (C) 2002 Ewald Snel <ewald@rambo.its.tudelft.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+*/
+
+#include "userinterface.h"
+
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin() { return new SimpleUI(); }
+}
diff --git a/noatun/modules/simple/pause.xpm b/noatun/modules/simple/pause.xpm
new file mode 100644
index 00000000..48d37568
--- /dev/null
+++ b/noatun/modules/simple/pause.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+const char * pause_xpm[] = {
+"16 16 2 1",
+" c None",
+". c #000000",
+" ",
+" ",
+" ",
+" .... .... ",
+" .... .... ",
+" .... .... ",
+" .... .... ",
+" .... .... ",
+" .... .... ",
+" .... .... ",
+" .... .... ",
+" .... .... ",
+" .... .... ",
+" ",
+" ",
+" "};
diff --git a/noatun/modules/simple/play.xpm b/noatun/modules/simple/play.xpm
new file mode 100644
index 00000000..7cb55a88
--- /dev/null
+++ b/noatun/modules/simple/play.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+const char * play_xpm[] = {
+"16 16 2 1",
+" c None",
+". c #000000",
+" ",
+" ",
+" ",
+" .. ",
+" ... ",
+" .... ",
+" ..... ",
+" ...... ",
+" ...... ",
+" ..... ",
+" .... ",
+" ... ",
+" .. ",
+" ",
+" ",
+" "};
diff --git a/noatun/modules/simple/playlist.xpm b/noatun/modules/simple/playlist.xpm
new file mode 100644
index 00000000..80b622ac
--- /dev/null
+++ b/noatun/modules/simple/playlist.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+const char * playlist_xpm[] = {
+"16 16 2 1",
+" c None",
+". c #000000",
+" ",
+" ",
+" .. ",
+" .... ",
+" ...... ",
+" ........ ",
+" .......... ",
+" .......... ",
+" ",
+" ",
+" .......... ",
+" .......... ",
+" .......... ",
+" .......... ",
+" ",
+" "};
diff --git a/noatun/modules/simple/propertiesdialog.ui b/noatun/modules/simple/propertiesdialog.ui
new file mode 100644
index 00000000..08f4f71c
--- /dev/null
+++ b/noatun/modules/simple/propertiesdialog.ui
@@ -0,0 +1,348 @@
+<!DOCTYPE UI><UI version="3.0" stdsetdef="1">
+<class>PropertiesDialog</class>
+<author>Ewald Snel</author>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>PropertiesDialog</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>320</width>
+ <height>261</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Properties</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>TabWidget2</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>details</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Details</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>16</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLineEdit" row="0" column="2">
+ <property name="name">
+ <cstring>nameField</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>180</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="Line" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>Line1</cstring>
+ </property>
+ <property name="frameShape" stdset="0">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>iconLabel</cstring>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignLeft</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="2">
+ <property name="name">
+ <cstring>typeLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <spacer row="7" column="2">
+ <property name="name" stdset="0">
+ <cstring>Spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="Line" row="6" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>Line1_2</cstring>
+ </property>
+ <property name="frameShape" stdset="0">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel7_2</cstring>
+ </property>
+ <property name="text">
+ <string>Type:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="1" rowspan="4" colspan="1">
+ <property name="name">
+ <cstring>TextLabel2_2</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>25</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>25</width>
+ <height>32767</height>
+ </size>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Length:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="2">
+ <property name="name">
+ <cstring>lengthLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>TextLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>Audio:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="2">
+ <property name="name">
+ <cstring>audioLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>TextLabel7</cstring>
+ </property>
+ <property name="text">
+ <string>Video:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="2">
+ <property name="name">
+ <cstring>videoLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>playobject</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;PlayObject</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>16</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel2</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>80</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Description:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>descriptionLabel</cstring>
+ </property>
+ </widget>
+ <widget class="QListView" row="1" column="0" rowspan="1" colspan="2">
+ <column>
+ <property name="text">
+ <string>Capabilities</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizeable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>capsList</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>buttonLayout</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer>
+ <property name="name" stdset="0">
+ <cstring>Spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>okButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;OK</string>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>okButton</sender>
+ <signal>clicked()</signal>
+ <receiver>PropertiesDialog</receiver>
+ <slot>accept()</slot>
+ </connection>
+</connections>
+<includes>
+ <include location="global" impldecl="in declaration">noatun/playlist.h</include>
+ <include location="global" impldecl="in declaration">kmedia2.h</include>
+ <include location="global" impldecl="in declaration">kmimetype.h</include>
+ <include location="local" impldecl="in implementation">propertiesdialog.ui.h</include>
+</includes>
+<slots>
+ <slot specifier="non virtual">setPlayObject( PlaylistItem pi, Arts::PlayObject po )</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/noatun/modules/simple/propertiesdialog.ui.h b/noatun/modules/simple/propertiesdialog.ui.h
new file mode 100644
index 00000000..a712d776
--- /dev/null
+++ b/noatun/modules/simple/propertiesdialog.ui.h
@@ -0,0 +1,58 @@
+/*
+ This file is part of KDE/aRts (Noatun) - xine integration
+ Copyright (C) 2002 Ewald Snel <ewald@rambo.its.tudelft.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+*/
+
+#include <klocale.h>
+
+void PropertiesDialog::setPlayObject( PlaylistItem pi, Arts::PlayObject po )
+{
+ // Arts::PlayObject properties
+ if (!po.isNull())
+ {
+ Arts::poCapabilities ncaps = po.capabilities();
+ QCheckListItem *item;
+
+ descriptionLabel->setText( po.description().c_str() );
+
+ // Determine capabilities
+ if (!(item = (QCheckListItem *)capsList->findItem( "capSeek", 0 )))
+ {
+ item = new QCheckListItem( capsList, "capSeek",
+ QCheckListItem::CheckBox );
+ }
+ item->setOn( (ncaps & Arts::capSeek) );
+
+ if (!(item = (QCheckListItem *)capsList->findItem( "capPause", 0 )))
+ {
+ item = new QCheckListItem( capsList, "capPause",
+ QCheckListItem::CheckBox );
+ }
+ item->setOn( (ncaps & Arts::capPause) );
+
+ // Defaults
+ nameField->setText( i18n("unknown") );
+ typeLabel->setText( i18n("unknown") );
+ lengthLabel->setText( i18n("unknown") );
+ audioLabel->setText( i18n("unknown") );
+ videoLabel->setText( i18n("unknown") );
+ }
+
+ // PlaylistItem properties (name and mimetype)
+ if (!pi.isNull())
+ {
+ setCaption( i18n("Properties for %1").arg(pi.url().fileName()) );
+
+ KSharedPtr<KMimeType> mime = KMimeType::mimeType( pi.mimetype() );
+ iconLabel->setPixmap( mime->pixmap( KIcon::Desktop, KIcon::SizeMedium ) );
+
+ nameField->setText( pi.url().fileName() );
+ typeLabel->setText( pi.mimetype() );
+ lengthLabel->setText( pi.lengthString() );
+ }
+}
diff --git a/noatun/modules/simple/simple.plugin b/noatun/modules/simple/simple.plugin
new file mode 100644
index 00000000..9d7e4beb
--- /dev/null
+++ b/noatun/modules/simple/simple.plugin
@@ -0,0 +1,126 @@
+Filename=noatunsimple.la
+Author=Ewald Snel
+Site=http://rambo.its.tudelft.nl/xine/
+Email=ewald@rambo.its.tudelft.nl
+Type=userinterface
+License=LGPL
+Name=Simple
+Name[af]=Eenvoudige
+Name[ar]=بسيط
+Name[az]=Bəsit
+Name[bn]=সরল
+Name[br]=Eeun
+Name[bs]=Jednostavno
+Name[ca]=Senzill
+Name[cs]=Jednoduchý
+Name[cy]=Syml
+Name[da]=Simpel
+Name[de]=Einfach
+Name[el]=Απλό
+Name[eo]=Simpla
+Name[et]=Lihtne
+Name[eu]=Sinplea
+Name[fa]=ساده
+Name[fi]=Yksinkertainen
+Name[ga]=Simplí
+Name[he]=פשוט
+Name[hi]=साधारण
+Name[hr]=Jednostavno
+Name[hu]=Egyszerű
+Name[id]=Sederhana
+Name[is]=Einfalt
+Name[it]=Semplice
+Name[km]=សាមញ្ញ
+Name[ko]=간단
+Name[lt]=Paprastas
+Name[lv]=Vienkāršs
+Name[mk]=Едноставен
+Name[mt]=Sempliċi
+Name[nb]=Enkel
+Name[nds]=Eenfach
+Name[ne]=साधारण
+Name[nl]=Eenvoudig
+Name[nn]=Enkel
+Name[pa]=ਸਧਾਰਨ
+Name[pl]=Prosty
+Name[pt]=Simples
+Name[pt_BR]=Simples
+Name[ro]=Simplu
+Name[ru]=Простой
+Name[se]=Oktageardanis
+Name[sk]=Jednoduchý
+Name[sl]=Preprosto
+Name[sr]=Једноставан
+Name[sr@Latn]=Jednostavan
+Name[sv]=Enkel
+Name[ta]=எளிய
+Name[tg]=Содда
+Name[th]=เรียบง่าย
+Name[tr]=Basit
+Name[uk]=Простий
+Name[uz]=Oddiy
+Name[uz@cyrillic]=Оддий
+Name[ven]=Zwoleluwa
+Name[wa]=Simpe
+Name[xh]=Lula
+Name[zh_CN]=简单
+Name[zh_HK]=簡單
+Name[zh_TW]=簡單
+Name[zu]=Okulula
+Comment=Simple GUI (embedded video)
+Comment[af]=Eenvoudige Gui (ingebedde video)
+Comment[bg]=Семпъл графичен интерфейс за Noatun
+Comment[bs]=Jednostavan GUI (uključen video)
+Comment[ca]=IGU senzill (vídeo incrustat)
+Comment[cs]=Jednoduché GUI (vnořené video)
+Comment[cy]=GUI Syml (fideo mewnol)
+Comment[da]=Simpel GUI (indlejret video)
+Comment[de]=Einfache Oberfläche (eingebettetes Video)
+Comment[el]=Απλό περιβάλλον χρήσης (ενσωματωμένο βίντεο)
+Comment[es]=Interfaz gráfica simple (vídeo empotrado)
+Comment[et]=Lihtne kasutajaliides (põimitud video)
+Comment[eu]=GUI sinplea (bideo kapsulatua)
+Comment[fa]=ونک ساده )ویدیوی نهفته(
+Comment[fi]=Yksinkertainen käyttöliittymä (upotettu video)
+Comment[fr]=Interface graphique simple (video incorporée)
+Comment[ga]=Comhéadan simplí (fís leabaithe)
+Comment[gl]=GUI Sinxela (video incrustado)
+Comment[he]=ממשק משתמש גרפי פשוט (וידאו מוטבע)
+Comment[hi]=साधारण जीयूआई (एम्बेडेड वीडियो)
+Comment[hu]=Egyszerű kezelőfelület (beágyazott videó)
+Comment[is]=Einföld myndræn skil (innbyggt)
+Comment[it]=Semplice GUI (video integrato)
+Comment[ja]=シンプルな GUI (埋め込みビデオ)
+Comment[kk]=Қарапайым инерфейсі (ендірілген бейне)
+Comment[km]=ចំណុច​ប្រទាក់​ក្រាហ្វិក​សាមញ្ញ (វីដេអូ​បង្កប់)
+Comment[ko]=간단한 GUI (내장된 비디오)
+Comment[lt]=Paprastas GUI (įdedamas video)
+Comment[mk]=Едноставен графички интерфејс (вградено видео)
+Comment[ms]=GUI Ringkas (video diselitkan)
+Comment[nb]=Enkel GUI (innebygget video)
+Comment[nds]=Eenfach Böversiet (inbett Video)
+Comment[ne]=साधारण GUI (सम्मिलित भिडियो)
+Comment[nl]=Eenvoudige GUI (inbedbare video)
+Comment[nn]=Enkelt grensesnitt (innebygd video)
+Comment[pa]=ਸਧਾਰਨ GUI (ਸ਼ਾਮਲ ਵੀਡਿਓ)
+Comment[pl]=Zwykły motyw (osadzone wideo)
+Comment[pt]=Interface Simples (vídeo embebido)
+Comment[pt_BR]=Interface Simples (vídeo integrado)
+Comment[ro]=Interfaţă grafică simplă (video integrat)
+Comment[ru]=Простой интерфейс (встроенное видео)
+Comment[se]=Oktageardánis lakta (vuojuhuvvon video)
+Comment[sk]=Jednoduché rozhranie (vložené video)
+Comment[sl]=Preprost uporabniški vmesnik (vključen video)
+Comment[sr]=Једноставан GUI (уграђен видео)
+Comment[sr@Latn]=Jednostavan GUI (ugrađen video)
+Comment[sv]=Enkelt grafiskt gränssnitt (inbäddad video)
+Comment[ta]=எளிய GUI (உட்பொதிந்த தாரை)
+Comment[tg]=Интерфейси Графикии содда (видеои дарунгузошта)
+Comment[th]=ส่วนติดต่อผู้ใช้แบบกราฟิกแบบเรียบง่าย (วิดีโอแบบฝัง)
+Comment[tr]=Basit arayüz (gömülü video)
+Comment[uk]=Простий інтерфейс (вмонтоване відео)
+Comment[xh]=GUI Elula (video ebekiweyo)
+Comment[zh_CN]=简单图形用户界面 (嵌入式视频)
+Comment[zh_HK]=簡單圖形用戶(嵌入式視訊)
+Comment[zh_TW]=簡單圖形使用者界面(嵌入式視訊)
+Comment[zu]=I-GUI Elula (ividiyo ehlanganiselwe)
diff --git a/noatun/modules/simple/simpleui.rc b/noatun/modules/simple/simpleui.rc
new file mode 100644
index 00000000..6e5fb86a
--- /dev/null
+++ b/noatun/modules/simple/simpleui.rc
@@ -0,0 +1,37 @@
+<!DOCTYPE kpartgui>
+<!--
+vim: syntax=xml
+-->
+<kpartgui name="simpleui" version="1">
+<MenuBar>
+ <Menu name="file"><text>&amp;File</text>
+ <Action name="_file_open"/>
+ <Separator lineSeparator="true"/>
+ <Action name="_file_properties"/>
+ <Separator lineSeparator="true"/>
+ <Action name="_file_quit"/>
+ </Menu>
+ <Menu name="view">
+ <Action name="view_playlist"/>
+ <Separator lineSeparator="true"/>
+ <Action name="half_size" />
+ <Action name="normal_size" />
+ <Action name="double_size" />
+ <Separator lineSeparator="true"/>
+ <Action name="fullscreen_mode" />
+ </Menu>
+ <Menu name="settings" noMerge="1"><text>&amp;Settings</text>
+ <Action name="options_show_menubar"/>
+ <Action name="options_show_statusbar"/>
+ <Separator lineSeparator="true"/>
+ <Action name="effects"/>
+ <Action name="equalizer"/>
+ <Action name="loop_style"/>
+ <Separator lineSeparator="true"/>
+ <Action name="options_configure"/>
+ </Menu>
+
+ <Action name="menu_actions" />
+</MenuBar>
+
+</kpartgui>
diff --git a/noatun/modules/simple/stop.xpm b/noatun/modules/simple/stop.xpm
new file mode 100644
index 00000000..6b080e94
--- /dev/null
+++ b/noatun/modules/simple/stop.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+const char * stop_xpm[] = {
+"16 16 2 1",
+" c None",
+". c #000000",
+" ",
+" ",
+" ",
+" .......... ",
+" .......... ",
+" .......... ",
+" .......... ",
+" .......... ",
+" .......... ",
+" .......... ",
+" .......... ",
+" .......... ",
+" .......... ",
+" ",
+" ",
+" "};
diff --git a/noatun/modules/simple/userinterface.cpp b/noatun/modules/simple/userinterface.cpp
new file mode 100644
index 00000000..6e7b486f
--- /dev/null
+++ b/noatun/modules/simple/userinterface.cpp
@@ -0,0 +1,379 @@
+/*
+ This file is part of KDE/aRts (Noatun) - xine integration
+ Copyright (C) 2002 Ewald Snel <ewald@rambo.its.tudelft.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <noatun/app.h>
+#include <noatun/engine.h>
+#include <noatun/player.h>
+#include <noatun/stdaction.h>
+
+#include <kconfig.h>
+#include <kglobal.h>
+#include <kglobalsettings.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kurldrag.h>
+#include <kmenubar.h>
+#include <kpopupmenu.h>
+#include <kstatusbar.h>
+#include <kstdaction.h>
+#include <kstdguiitem.h>
+#include <kurl.h>
+
+#include <qaccel.h>
+#include <qdragobject.h>
+#include <qhbox.h>
+#include <qimage.h>
+#include <qpixmap.h>
+#include <qstrlist.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+
+#include "userinterface.h"
+
+#include "back.xpm"
+#include "forward.xpm"
+#include "pause.xpm"
+#include "play.xpm"
+#include "playlist.xpm"
+#include "stop.xpm"
+#include "volume.xpm"
+
+SimpleUI::SimpleUI()
+ : KMainWindow(0, "NoatunSimpleUI"), UserInterface()
+{
+ setAcceptDrops( true );
+ setCaption( i18n("Noatun") );
+ setIcon( SmallIcon( "noatun" ) );
+
+ setupCentralWidget();
+ setupActions();
+
+ contextMenu = video->popupMenu( this );
+
+ setupGUI( StatusBar|Create, "simpleui.rc" );
+
+ connect( napp->player(), SIGNAL(playing()), SLOT(slotPlaying()) );
+ connect( napp->player(), SIGNAL(stopped()), SLOT(slotStopped()) );
+ connect( napp->player(), SIGNAL(paused()), SLOT(slotPaused()) );
+ connect( napp->player(), SIGNAL(timeout()), SLOT(slotTimeout()) );
+ connect( napp->player(), SIGNAL(newSong()), SLOT(slotChanged()) );
+ connect( napp->player(), SIGNAL(volumeChanged(int)), SLOT(slotVolumeChanged(int)) );
+ connect( napp, SIGNAL(hideYourself()), SLOT(hide()) );
+ connect( napp, SIGNAL(showYourself()), SLOT(show()) );
+
+ napp->player()->handleButtons();
+
+ resize( minimumSize() );
+
+ // Show UI and calculate video widget frame
+ show();
+
+ extra_width = (width() - video->width());
+ extra_height = (height() - video->height());
+
+ // Load configuration
+ KConfig &config = *KGlobal::config();
+ config.setGroup( "Simple" );
+ QString str = config.readEntry( "View", "NormalSize" );
+
+ if (str == "HalfSize")
+ video->setHalfSize();
+ else if (str == "NormalSize")
+ video->setNormalSize();
+ else if (str == "DoubleSize")
+ video->setDoubleSize();
+ else
+ applyMainWindowSettings( &config, "Simple" );
+
+ // PlayObject could be running, update video widget
+ slotChanged();
+
+ video->give();
+}
+
+SimpleUI::~SimpleUI()
+{
+ KConfig &config = *KGlobal::config();
+ saveMainWindowSettings( &config, "Simple" );
+ config.setGroup( "Simple" );
+ QString str;
+
+ if (video->isHalfSize())
+ str = "HalfSize";
+ else if (video->isNormalSize())
+ str = "NormalSize";
+ else if (video->isDoubleSize())
+ str = "DoubleSize";
+ else
+ str = "CustomSize";
+
+ config.writeEntry( "View", str );
+ config.sync();
+}
+
+
+
+void SimpleUI::setupActions()
+{
+ KStdAction::open( napp, SLOT(fileOpen()), actionCollection(), "_file_open" );
+ new KAction( i18n("&Properties"), 0, propertiesDialog, SLOT(show()),
+ actionCollection(), "_file_properties" );
+ KStdAction::quit( napp, SLOT(quit()), actionCollection(), "_file_quit");
+
+ NoatunStdAction::playlist( actionCollection(), "view_playlist" );
+ actionCollection()->insert(video->action( "half_size" ));
+ actionCollection()->insert(video->action( "normal_size" ));
+ actionCollection()->insert(video->action( "double_size" ));
+ actionCollection()->insert(video->action( "fullscreen_mode" ));
+
+ actionCollection()->insert(napp->pluginActionMenu());
+
+ menubarAction = KStdAction::showMenubar(this, SLOT(showMenubar()),
+ actionCollection());
+ statusbarAction = KStdAction::showStatusbar(this, SLOT(showStatusbar()),
+ actionCollection());
+ NoatunStdAction::effects( actionCollection(), "effects" );
+ NoatunStdAction::equalizer( actionCollection(), "equalizer" );
+ NoatunStdAction::loop( actionCollection(), "loop_style" );
+ KStdAction::preferences( napp, SLOT(preferences()), actionCollection() );
+}
+
+void SimpleUI::showMenubar()
+{
+ if(menubarAction->isChecked())
+ menuBar()->show();
+ else
+ menuBar()->hide();
+}
+
+void SimpleUI::showStatusbar()
+{
+ if(statusbarAction->isChecked())
+ statusBar()->show();
+ else
+ statusBar()->hide();
+}
+
+
+void SimpleUI::setupCentralWidget()
+{
+ QVBox *npWidget = new QVBox( this );
+ npWidget->setMargin( 0 );
+ npWidget->setSpacing( 0 );
+
+ positionLabel = new QLabel( statusBar() );
+ positionLabel->setAlignment( AlignVCenter | AlignCenter );
+ positionLabel->setFixedSize( fontMetrics().size( 0, " 00:00/00:00 " ) );
+ statusBar()->addWidget( positionLabel, 0, true );
+
+ video = new VideoFrame( npWidget );
+ connect( video, SIGNAL(adaptSize(int,int)),
+ SLOT(slotAdaptSize(int,int)) );
+ connect( video, SIGNAL(rightButtonPressed(const QPoint &)),
+ SLOT(slotContextMenu(const QPoint &)) );
+
+ QHBox *ctlFrame = new QHBox( npWidget );
+ ctlFrame->setFixedHeight( 38 );
+ ctlFrame->setFrameShape( QFrame::StyledPanel );
+ ctlFrame->setFrameShadow( QFrame::Raised );
+ ctlFrame->setMargin( 6 );
+ ctlFrame->setSpacing( 6 );
+
+ QPushButton *backButton = new QPushButton( ctlFrame );
+ backButton->setFixedSize( 24, 24 );
+ backButton->setPixmap( QPixmap( back_xpm ) );
+ QToolTip::add( backButton, i18n("Back") );
+ connect( backButton, SIGNAL(clicked()), napp->player(), SLOT(back()) );
+
+ stopButton = new QPushButton( ctlFrame );
+ stopButton->setFixedSize( 24, 24 );
+ stopButton->setPixmap( QPixmap( stop_xpm ) );
+ QToolTip::add( stopButton, i18n("Stop") );
+ connect( stopButton, SIGNAL(clicked()), napp->player(), SLOT(stop()) );
+
+ playButton = new QPushButton( ctlFrame );
+ playButton->setFixedSize( 24, 24 );
+ playButton->setPixmap( QPixmap( play_xpm ) );
+ QToolTip::add( playButton, i18n("Play / Pause") );
+ connect( playButton, SIGNAL(clicked()), napp->player(), SLOT(playpause()) );
+
+ QPushButton *forwButton = new QPushButton( ctlFrame );
+ forwButton->setFixedSize( 24, 24 );
+ forwButton->setPixmap( QPixmap( forward_xpm ) );
+ QToolTip::add( forwButton, i18n("Forward") );
+ connect( forwButton, SIGNAL(clicked()), napp->player(), SLOT(forward()) );
+
+ slider = new L33tSlider( 0, 1000, 10, 0, L33tSlider::Horizontal, ctlFrame );
+ slider->setFixedHeight( 24 );
+ slider->setMinimumWidth( 100 );
+ slider->setTickmarks( QSlider::NoMarks );
+ connect( slider, SIGNAL(userChanged(int)), SLOT(slotSkipTo(int)) );
+ connect( slider, SIGNAL(sliderMoved(int)), SLOT(slotSliderMoved(int)) );
+
+ QPushButton *playlistButton = new QPushButton( ctlFrame );
+ playlistButton->setFixedSize( 24, 24 );
+ playlistButton->setPixmap( QPixmap( playlist_xpm ) );
+ QToolTip::add( playlistButton, i18n("Playlist") );
+ connect( playlistButton, SIGNAL(clicked()), napp->player(), SLOT(toggleListView()) );
+
+ volumeButton = new QPushButton( ctlFrame );
+ volumeButton->setFixedSize( 24, 24 );
+ volumeButton->setPixmap( QPixmap( volume_xpm ) );
+ QToolTip::add( volumeButton, i18n("Volume") );
+
+ volumeFrame = new QVBox( this, "Volume", WStyle_Customize | WType_Popup );
+ volumeFrame->setFrameStyle( QFrame::PopupPanel );
+ volumeFrame->setMargin( 4 );
+
+ volumeLabel = new QLabel( volumeFrame );
+ volumeLabel->setText( "100%" );
+ volumeLabel->setAlignment( AlignCenter );
+ volumeLabel->setFixedSize( volumeLabel->sizeHint() );
+
+ QHBox *volumeSubFrame = new QHBox( volumeFrame );
+ volumeSlider = new L33tSlider( 0, 100, 10, 0, Vertical, volumeSubFrame );
+ volumeSlider->setValue( 100 - napp->player()->volume() );
+ volumeSlider->setFixedSize( volumeSlider->sizeHint() );
+
+ volumeFrame->resize( volumeFrame->sizeHint() );
+
+ connect( volumeSlider, SIGNAL(sliderMoved(int)), SLOT(slotVolumeSliderMoved(int)) );
+ connect( volumeSlider, SIGNAL(userChanged(int)), SLOT(slotVolumeSliderMoved(int)) );
+ connect( volumeButton, SIGNAL(clicked()), SLOT(slotVolumeFrame()) );
+
+ setCentralWidget( npWidget );
+
+ video->setMinimumSize( minimumSizeHint().width(), 1 );
+
+ // Create properties dialog
+ propertiesDialog = new PropertiesDialog( this );
+ propertiesDialog->resize( 375, 285 );
+}
+
+void SimpleUI::closeEvent( QCloseEvent * )
+{
+ unload();
+}
+
+void SimpleUI::dragEnterEvent( QDragEnterEvent *event )
+{
+ event->accept( KURLDrag::canDecode( event ) );
+}
+
+void SimpleUI::dropEvent( QDropEvent *event )
+{
+ KURL::List uri;
+ if (KURLDrag::decode( event, uri ))
+ napp->player()->openFile( uri, false );
+}
+
+void SimpleUI::slotAdaptSize( int width, int height )
+{
+ resize( width + extra_width, height + extra_height );
+}
+
+void SimpleUI::slotPlaying()
+{
+ playButton->setPixmap( QPixmap( pause_xpm ) );
+ stopButton->setEnabled( true );
+ slider->setEnabled( true );
+
+ if (napp->player()->current())
+ statusBar()->message( napp->player()->current().title() );
+}
+
+void SimpleUI::slotStopped()
+{
+ playButton->setPixmap( QPixmap( play_xpm ) );
+ stopButton->setEnabled( false );
+ slider->setEnabled( false );
+ slider->setValue( 0 );
+ positionLabel->setText( "" );
+ statusBar()->message( "" );
+}
+
+void SimpleUI::slotPaused()
+{
+ playButton->setPixmap( QPixmap( play_xpm ) );
+ slider->setEnabled( true );
+}
+
+void SimpleUI::slotTimeout()
+{
+ if (napp->player()->current() && !slider->currentlyPressed())
+ {
+ positionLabel->setText( napp->player()->lengthString() );
+ slider->setRange( 0, (int)napp->player()->getLength() / 1000 );
+ slider->setValue( (int)napp->player()->getTime() / 1000 );
+ }
+}
+
+void SimpleUI::slotSkipTo( int sec )
+{
+ napp->player()->skipTo( sec * 1000 );
+}
+
+void SimpleUI::slotChanged()
+{
+ propertiesDialog->setPlayObject( napp->player()->current(),
+ napp->player()->engine()->playObject() );
+}
+
+void SimpleUI::slotContextMenu( const QPoint &pos )
+{
+ contextMenu->exec( pos );
+}
+
+void SimpleUI::slotSliderMoved( int sec )
+{
+ if (napp->player()->current())
+ positionLabel->setText( napp->player()->lengthString( sec * 1000 ) );
+}
+
+void SimpleUI::slotVolumeChanged( int volume )
+{
+ volumeLabel->setText( QString::number( volume ) + "%" );
+ volumeSlider->setValue( 100 - volume );
+}
+
+void SimpleUI::slotVolumeFrame()
+{
+ if (volumeFrame->isVisible())
+ {
+ volumeFrame->hide();
+ }
+ else
+ {
+ int x = (volumeButton->width() - volumeFrame->width()) / 2;
+ int y = -(volumeFrame->height() + 5);
+
+ QPoint point( volumeButton->mapToGlobal( QPoint( x, y ) ) );
+ QRect deskRect = KGlobalSettings::desktopGeometry( point );
+
+ bool bottom = (point.y() + volumeFrame->height()) > (deskRect.y() + deskRect.height());
+ bool right = (point.x() + volumeFrame->width()) > (deskRect.x() + deskRect.width());
+
+ volumeFrame->move(
+ right ? (deskRect.x() + deskRect.width()) - volumeFrame->width() : ( point.x() < 0 ? 0 : point.x() ),
+ bottom ? (deskRect.y() + deskRect.height()) - volumeFrame->height() : ( point.y() < 0 ? 0 : point.y() ) );
+ volumeFrame->show();
+ }
+}
+
+void SimpleUI::slotVolumeSliderMoved( int slider )
+{
+ napp->player()->setVolume( 100 - slider );
+}
+
+#include "userinterface.moc"
diff --git a/noatun/modules/simple/userinterface.h b/noatun/modules/simple/userinterface.h
new file mode 100644
index 00000000..c545fd2c
--- /dev/null
+++ b/noatun/modules/simple/userinterface.h
@@ -0,0 +1,80 @@
+/*
+ This file is part of KDE/aRts (Noatun) - xine integration
+ Copyright (C) 2002 Ewald Snel <ewald@rambo.its.tudelft.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+*/
+
+#ifndef __USERINTERFACE_H
+#define __USERINTERFACE_H
+
+#include <noatun/controls.h>
+#include <noatun/plugin.h>
+#include <noatun/video.h>
+#include <kaction.h>
+#include <kmainwindow.h>
+#include <kmedia2.h>
+#include <qevent.h>
+#include <qlabel.h>
+#include <qpopupmenu.h>
+#include <qpushbutton.h>
+#include <qvbox.h>
+#include "propertiesdialog.h"
+
+
+class SimpleUI : public KMainWindow, public UserInterface
+{
+ Q_OBJECT
+
+public:
+ SimpleUI();
+ ~SimpleUI();
+
+protected:
+ void setupActions();
+ void setupCentralWidget();
+
+ virtual void closeEvent( QCloseEvent * );
+ virtual void dragEnterEvent( QDragEnterEvent *event );
+ virtual void dropEvent( QDropEvent *event );
+
+public slots:
+ void slotAdaptSize( int width, int height );
+ void slotPlaying();
+ void slotStopped();
+ void slotPaused();
+ void slotTimeout();
+ void slotSkipTo( int sec );
+ void slotChanged();
+ void slotContextMenu( const QPoint &pos );
+
+private slots:
+ void slotSliderMoved( int sec );
+ void slotVolumeSliderMoved( int volume );
+ void slotVolumeFrame();
+ void slotVolumeChanged( int volume );
+ void showMenubar();
+ void showStatusbar();
+
+private:
+ PropertiesDialog *propertiesDialog;
+ QPopupMenu *contextMenu;
+ QPushButton *stopButton;
+ QPushButton *playButton;
+ QPushButton *volumeButton;
+ QVBox *volumeFrame;
+ QLabel *volumeLabel;
+ QLabel *positionLabel;
+ VideoFrame *video;
+ L33tSlider *volumeSlider;
+ L33tSlider *slider;
+ int extra_width;
+ int extra_height;
+ KToggleAction *menubarAction;
+ KToggleAction *statusbarAction;
+};
+
+#endif
diff --git a/noatun/modules/simple/volume.xpm b/noatun/modules/simple/volume.xpm
new file mode 100644
index 00000000..fa240d42
--- /dev/null
+++ b/noatun/modules/simple/volume.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+const char * volume_xpm[] = {
+"16 16 2 1",
+" c None",
+". c #000000",
+" ",
+" ",
+" . .. ",
+" .. . . ",
+" .... . . ",
+" .... . . ",
+" .. .. . ",
+" . ... . ",
+" .... . ",
+" ..... . ",
+" ...... . ",
+" ....... . ",
+" ........ . ",
+" ............ ",
+" ",
+" "};
diff --git a/noatun/modules/splitplaylist/LICENSE b/noatun/modules/splitplaylist/LICENSE
new file mode 100644
index 00000000..8f9bdefc
--- /dev/null
+++ b/noatun/modules/splitplaylist/LICENSE
@@ -0,0 +1,124 @@
+The "Artistic License"
+
+ Preamble
+
+ The intent of this document is to state the conditions under which a
+ Package may be copied, such that the Copyright Holder maintains some
+ semblance of artistic control over the development of the package,
+ while giving the users of the package the right to use and distribute
+ the Package in a more-or-less customary fashion, plus the right to
+ make reasonable modifications.
+
+ Definitions
+
+ "Package" refers to the collection of files distributed by the
+ Copyright Holder, and derivatives of that collection of files
+ created through textual modification.
+
+ "Standard Version" refers to such a Package if it has not been
+ modified, or has been modified in accordance with the wishes of the
+ Copyright Holder as specified below.
+
+ "Copyright Holder" is whoever is named in the copyright or
+ copyrights for the package.
+
+ "You" is you, if you're thinking about copying or distributing this
+ Package.
+
+ "Reasonable copying fee" is whatever you can justify on the basis
+ of media cost, duplication charges, time of people involved, and so
+ on. (You will not be required to justify it to the Copyright
+ Holder, but only to the computing community at large as a market
+ that must bear the fee.)
+
+ "Freely Available" means that no fee is charged for the item
+ itself, though there may be fees involved in handling the item. It
+ also means that recipients of the item may redistribute it under
+ the same conditions they received it.
+
+ 1. You may make and give away verbatim copies of the source form of
+ the Standard Version of this Package without restriction, provided
+ that you duplicate all of the original copyright notices and
+ associated disclaimers.
+ 2. You may apply bug fixes, portability fixes and other modifications
+ derived from the Public Domain or from the Copyright Holder. A
+ Package modified in such a way shall still be considered the
+ Standard Version.
+ 3. You may otherwise modify your copy of this Package in any way,
+ provided that you insert a prominent notice in each changed file
+ stating how and when you changed that file, and provided that you
+ do at least ONE of the following:
+
+ a. place your modifications in the Public Domain or otherwise make
+ them Freely Available, such as by posting said modifications to
+ Usenet or an equivalent medium, or placing the modifications on a
+ major archive site such as uunet.uu.net, or by allowing the
+ Copyright Holder to include your modifications in the Standard
+ Version of the Package.
+ b. use the modified Package only within your corporation or
+ organization.
+ c. rename any non-standard executables so the names do not conflict
+ with standard executables, which must also be provided, and
+ provide a separate manual page for each non-standard executable
+ that clearly documents how it differs from the Standard Version.
+ d. make other distribution arrangements with the Copyright Holder.
+
+ You may distribute the programs of this Package in object code or
+ executable form, provided that you do at least ONE of the following:
+
+ a. distribute a Standard Version of the executables and library
+ files, together with instructions (in the manual page or
+ equivalent) on where to get the Standard Version.
+ b. accompany the distribution with the machine-readable source of the
+ Package with your modifications.
+ c. give non-standard executables non-standard names, and clearly
+ document the differences in manual pages (or equivalent), together
+ with instructions on where to get the Standard Version.
+ d. make other distribution arrangements with the Copyright Holder.
+
+ You may charge a reasonable copying fee for any distribution of this
+ Package. You may charge any fee you choose for support of this
+ Package. You may not charge a fee for this Package itself. However,
+ you may distribute this Package in aggregate with other (possibly
+ commercial) programs as part of a larger (possibly commercial)
+ software distribution provided that you do not advertise this Package
+ as a product of your own. You may embed this Package's interpreter
+ within an executable of yours (by linking); this shall be construed as
+ a mere form of aggregation, provided that the complete Standard
+ Version of the interpreter is so embedded.
+
+ The scripts and library files supplied as input to or produced as
+ output from the programs of this Package do not automatically fall
+ under the copyright of this Package, but belong to whomever generated
+ them, and may be sold commercially, and may be aggregated with this
+ Package. If such scripts or library files are aggregated with this
+ Package via the so-called "undump" or "unexec" methods of producing a
+ binary executable image, then distribution of such an image shall
+ neither be construed as a distribution of this Package nor shall it
+ fall under the restrictions of Paragraphs 3 and 4, provided that you
+ do not represent such an executable image as a Standard Version of
+ this Package.
+
+ C subroutines (or comparably compiled subroutines in other
+ languages) supplied by you and linked into this Package in order to
+ emulate subroutines and variables of the language defined by this
+ Package shall not be considered part of this Package, but are the
+ equivalent of input as in Paragraph 6, provided these subroutines do
+ not change the language in any way that would cause it to fail the
+ regression tests for the language.
+
+ Aggregation of this Package with a commercial distribution is always
+ permitted provided that the use of this Package is embedded; that is,
+ when no overt attempt is made to make this Package's interfaces
+ visible to the end user of the commercial distribution. Such use shall
+ not be construed as a distribution of this Package.
+
+ The name of the Copyright Holder may not be used to endorse or
+ promote products derived from this software without specific prior
+ written permission.
+
+ THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+ The End
diff --git a/noatun/modules/splitplaylist/Makefile.am b/noatun/modules/splitplaylist/Makefile.am
new file mode 100644
index 00000000..4ea511ac
--- /dev/null
+++ b/noatun/modules/splitplaylist/Makefile.am
@@ -0,0 +1,17 @@
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_splitplaylist.la
+
+noatun_splitplaylist_la_SOURCES = splitplaylist.cpp playlist.cpp view.cpp find.cpp
+
+noatun_splitplaylist_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_splitplaylist_la_LIBADD = $(LIB_KIO) $(top_builddir)/noatun/library/libnoatun.la
+
+noatun_splitplaylist_la_METASOURCES = AUTO
+
+noinst_HEADERS = playlist.h view.h find.h
+
+noatun_modules_splitplaylist_DATA = splitplaylist.plugin
+noatun_modules_splitplaylistdir = $(kde_datadir)/noatun
+
+rc_DATA = splui.rc
+rcdir = $(kde_datadir)/noatun
diff --git a/noatun/modules/splitplaylist/find.cpp b/noatun/modules/splitplaylist/find.cpp
new file mode 100644
index 00000000..b6e196d9
--- /dev/null
+++ b/noatun/modules/splitplaylist/find.cpp
@@ -0,0 +1,63 @@
+#include "find.h"
+#include <qlayout.h>
+#include <kcombobox.h>
+#include <qpushbutton.h>
+#include <qcheckbox.h>
+#include <klocale.h>
+
+Finder::Finder(QWidget *parent) : KDialogBase(parent, 0, false, i18n("Find"), Close | User1, User1, false, KGuiItem(i18n("&Find"),"find"))
+{
+ QWidget *mainWidget = new QWidget(this);
+ mainWidget->setMinimumWidth(320);
+ setMainWidget(mainWidget);
+
+ QGridLayout *layout=new QGridLayout(mainWidget);
+ layout->setSpacing(KDialog::spacingHint());
+
+ mText=new KHistoryCombo(mainWidget);
+ mText->setMaxCount(10);
+
+ mText->setFocus();
+
+ mRegexp=new QCheckBox(i18n("&Regular expression"), mainWidget);
+ mBackwards=new QCheckBox(i18n("Find &backwards"), mainWidget);
+
+ layout->addMultiCellWidget(mText, 0, 0, 0, 1);
+ layout->addWidget(mRegexp, 1, 0);
+ layout->addWidget(mBackwards, 1, 1);
+
+ connect(this, SIGNAL(user1Clicked()), SLOT(clicked()));
+
+ connect(mText, SIGNAL(activated(int)), SLOT(clicked()));
+ connect(mText, SIGNAL(textChanged(const QString &)), SLOT(textChanged(const QString &)));
+
+ enableButton(User1, false);
+}
+
+void Finder::textChanged(const QString &text) {
+ enableButton(User1, !text.isEmpty());
+}
+
+bool Finder::regexp() const
+{
+ return mRegexp->isChecked();
+}
+
+bool Finder::isForward() const
+{
+ return !mBackwards->isChecked();
+}
+
+void Finder::clicked()
+{
+ mText->addToHistory( mText->currentText() );
+ emit search(this);
+}
+
+QString Finder::string() const
+{
+ return mText->currentText();
+}
+
+
+#include "find.moc"
diff --git a/noatun/modules/splitplaylist/find.h b/noatun/modules/splitplaylist/find.h
new file mode 100644
index 00000000..a4791339
--- /dev/null
+++ b/noatun/modules/splitplaylist/find.h
@@ -0,0 +1,33 @@
+#ifndef FIND_H
+#define FIND_H
+
+#include <kdialogbase.h>
+
+class KHistoryCombo;
+class QCheckBox;
+class QPushButton;
+
+class Finder : public KDialogBase
+{
+Q_OBJECT
+public:
+ Finder(QWidget *parent);
+
+ bool regexp() const;
+ bool isForward() const;
+
+ QString string() const;
+signals:
+ void search(Finder *);
+
+public slots:
+ void textChanged(const QString &);
+ void clicked();
+
+private:
+ KHistoryCombo *mText;
+ QCheckBox *mRegexp, *mBackwards;
+};
+
+#endif
+
diff --git a/noatun/modules/splitplaylist/playlist.cpp b/noatun/modules/splitplaylist/playlist.cpp
new file mode 100644
index 00000000..57d6fb48
--- /dev/null
+++ b/noatun/modules/splitplaylist/playlist.cpp
@@ -0,0 +1,281 @@
+#include "playlist.h"
+#include "view.h"
+#include <noatun/player.h>
+
+#include <kapplication.h>
+#include <krandomsequence.h>
+#include <kdebug.h>
+#include <kwin.h>
+
+#include <kiconloader.h>
+
+SplitPlaylist *SplitPlaylist::Self=0;
+
+SplitPlaylist::SplitPlaylist()
+ : Playlist(0, "SplitPlaylist"), Plugin(), mExiting(false)
+{
+ Self=this;
+}
+
+void SplitPlaylist::init()
+{
+ view=new View(this); // 195
+ connect(view->listView(), SIGNAL(executed(QListViewItem*)), SLOT(listItemSelected(QListViewItem*)));
+ connect(view, SIGNAL(shown()), SIGNAL(listShown()));
+ connect(view, SIGNAL(hidden()), SIGNAL(listHidden()));
+
+ view->init(); // 1000
+}
+
+SplitPlaylist::~SplitPlaylist()
+{
+ mExiting=true;
+ delete view;
+}
+
+void SplitPlaylist::reset()
+{
+ SafeListViewItem *i;
+ setCurrent(i=static_cast<SafeListViewItem*>(view->listView()->firstChild()), false);
+ if (i && !i->isOn())
+ next(false);
+}
+
+PlaylistItem SplitPlaylist::next()
+{
+ return next(true);
+}
+
+PlaylistItem SplitPlaylist::next(bool play)
+{
+ PlaylistItem nextItem;
+
+ if (napp->player()->loopStyle() == Player::Random)
+ {
+ // Ignore all this order stuff and select a random item
+ List *lview = view->listView();
+
+ if (lview->childCount())
+ {
+ SafeListViewItem *slvi = static_cast<SafeListViewItem*>(
+ lview->itemAtIndex(KApplication::random() % lview->childCount())
+ );
+ nextItem = PlaylistItem(slvi);
+ }
+ else
+ {
+ nextItem = 0;
+ }
+ }
+ else
+ {
+ if(!current())
+ {
+ nextItem = static_cast<SafeListViewItem*>(static_cast<SafeListViewItem*>(getFirst().data()));
+ }
+ else
+ {
+ nextItem = static_cast<SafeListViewItem*>(
+ static_cast<SafeListViewItem*>(current().data())->itemBelow());
+ }
+ }
+
+ if (!nextItem) // don't set a null-item as current item
+ {
+ return 0;
+// nextItem = static_cast<SafeListViewItem*>(static_cast<SafeListViewItem*>(getFirst().data()));
+ }
+
+ PlaylistItem oldCurrent = currentItem;
+ setCurrent(nextItem, play);
+
+ // Hack for back button on randomized play
+ if (oldCurrent)
+ randomPrevious = oldCurrent;
+
+ if (currentItem)
+ if (!static_cast<SafeListViewItem*>(currentItem.data())->isOn())
+ return next(play);
+
+ return currentItem;
+}
+
+PlaylistItem SplitPlaylist::current()
+{
+ return currentItem;
+}
+
+PlaylistItem SplitPlaylist::previous()
+{
+ if (napp->player()->loopStyle() == Player::Random && randomPrevious)
+ {
+ List *list = view->listView();
+ // check if the item still exists (hackitude: 50%)
+ bool found=false;
+ for (QListViewItem *i = list->firstChild(); i; i = i->nextSibling())
+ {
+ if (i == static_cast<SafeListViewItem*>(randomPrevious.data()))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (found)
+ {
+ // setCurrent modified randomPrevious, and setCurrent is pass-by-reference
+ PlaylistItem prev = randomPrevious;
+
+ setCurrent(prev);
+ return currentItem;
+ }
+ }
+ // there's a possibility that I will fall out to here
+ // from the above branch
+
+ PlaylistItem nextItem;
+ if(!current())
+ {
+ nextItem = static_cast<SafeListViewItem*>(static_cast<SafeListViewItem*>(getFirst().data()));
+ }
+ else
+ {
+ nextItem = static_cast<SafeListViewItem*>(
+ static_cast<SafeListViewItem*>(current().data())->itemAbove());
+ }
+ if (!nextItem) // don't set a null-item as current item
+ return 0;
+
+ setCurrent(nextItem);
+
+ if (currentItem)
+ if (!static_cast<SafeListViewItem*>(currentItem.data())->isOn())
+ return previous();
+
+ return currentItem;
+}
+
+PlaylistItem SplitPlaylist::getFirst() const
+{
+ return static_cast<SafeListViewItem*>(view->listView()->firstChild());
+}
+
+PlaylistItem SplitPlaylist::getAfter(const PlaylistItem &item) const
+{
+ if (item)
+ return static_cast<SafeListViewItem*>(static_cast<const SafeListViewItem*>(item.data())->nextSibling());
+ return 0;
+}
+
+bool SplitPlaylist::listVisible() const
+{
+ KWin::WindowInfo info = KWin::windowInfo(view->winId());
+ return !(info.hasState(NET::Shaded) || info.hasState(NET::Hidden) || !info.valid() || !info.isOnCurrentDesktop());
+}
+
+void SplitPlaylist::showList()
+{
+ KWin::setOnDesktop(view->winId(), KWin::currentDesktop());
+ view->show();
+ if (view->isMinimized())
+ view->showNormal();
+ view->raise();
+}
+
+void SplitPlaylist::hideList()
+{
+ view->hide();
+}
+
+void SplitPlaylist::clear()
+{
+ view->listView()->clear();
+}
+
+void SplitPlaylist::addFile(const KURL &file, bool play)
+{
+ view->addFile(file, play);
+}
+
+void SplitPlaylist::setCurrent(const PlaylistItem &i)
+{
+ setCurrent(i, true);
+}
+
+void SplitPlaylist::setCurrent(const PlaylistItem &i, bool emitC)
+{
+ randomPrevious = PlaylistItem();
+ emitC = emitC && currentItem;
+ if (!i)
+ {
+ currentItem=0;
+ }
+ else
+ {
+ // remove the old icon
+ SafeListViewItem *now=static_cast<SafeListViewItem*>(current().data());
+ if (now)
+ now->setPixmap(0, QPixmap());
+
+ QRect rect(view->listView()->itemRect(static_cast<SafeListViewItem*>(current().data())));
+ rect.setWidth(view->listView()->viewport()->width());
+ currentItem=i;
+ view->listView()->viewport()->repaint(rect,true);
+
+ view->listView()->ensureItemVisible(static_cast<SafeListViewItem*>(current().data()));
+ QRect currentRect= view->listView()->itemRect(static_cast<SafeListViewItem*>(current().data()));
+ view->listView()->viewport()->repaint(currentRect);
+
+ now=static_cast<SafeListViewItem*>(current().data());
+ if(now)
+ now->setPixmap(0, ::SmallIcon("noatunplay"));
+ }
+
+ if (emitC && !exiting())
+ emit playCurrent();
+}
+
+void SplitPlaylist::remove(const PlaylistItem &)
+{
+// delete i;
+}
+
+void SplitPlaylist::listItemSelected(QListViewItem *i)
+{
+ setCurrent(PlaylistItem(static_cast<SafeListViewItem*>(i)), false);
+ emit playCurrent();
+}
+
+void SplitPlaylist::randomize()
+{
+ // turning off sorting is necessary
+ // otherwise, the list will get randomized and promptly sorted again
+ view->setSorting(false);
+ List *lview = view->listView();
+ // eeeeevil :)
+ QPtrList<void> list;
+ QPtrList<QListViewItem> items;
+ for(int i = 0; i < lview->childCount(); i++)
+ {
+ list.append( (void*) i );
+ items.append( lview->itemAtIndex( i ) );
+ }
+
+ KRandomSequence seq;
+ seq.randomize( &list );
+
+ for(int i = 0; i < lview->childCount(); i++)
+ {
+ items.take()->moveItem(lview->itemAtIndex((long) list.take()));
+ }
+
+ setCurrent(currentItem, false);
+}
+
+void SplitPlaylist::sort()
+{
+ view->setSorting(true);
+ setCurrent(currentItem, false);
+}
+
+#include "playlist.moc"
diff --git a/noatun/modules/splitplaylist/playlist.h b/noatun/modules/splitplaylist/playlist.h
new file mode 100644
index 00000000..04cb648d
--- /dev/null
+++ b/noatun/modules/splitplaylist/playlist.h
@@ -0,0 +1,98 @@
+#ifndef PLAYLIST_H
+#define PLAYLIST_H
+
+#include <noatun/playlist.h>
+#include <noatun/plugin.h>
+
+/*
+class PlaylistItem
+{
+ PlaylistItem(const KURL &u=0);
+ virtual ~PlaylistItem();
+
+ QString title() const;
+ virtual void setTitle(const QString &t);
+
+ KURL url() const;
+ virtual void setUrl(const KURL &u);
+
+ int length() const;
+ virtual void setLength(int l);
+};
+*/
+class SafeListViewItem;
+class View;
+class List;
+class QListViewItem;
+
+class SplitPlaylist : public Playlist, public Plugin
+{
+Q_OBJECT
+friend class SafeListViewItem;
+friend class List;
+public:
+ SplitPlaylist();
+ ~SplitPlaylist();
+
+ /**
+ * go to the front
+ **/
+ virtual void reset();
+
+ virtual void clear();
+ virtual void addFile(const KURL&, bool play=false);
+ /**
+ * Cycle everthing through forward
+ **/
+ virtual PlaylistItem next();
+ PlaylistItem next(bool play);
+ /**
+ * return the one that might/should be playing now
+ **/
+ virtual PlaylistItem current();
+ /**
+ * Cycle through backwards
+ **/
+ virtual PlaylistItem previous();
+
+ virtual PlaylistItem getFirst() const;
+ virtual PlaylistItem getAfter(const PlaylistItem &item) const;
+
+ virtual bool listVisible() const;
+ virtual void init();
+
+ virtual Playlist *playlist()
+ { return this; }
+
+ static SplitPlaylist *SPL() { return Self; }
+ inline bool exiting() const { return mExiting; }
+public slots:
+ virtual void showList();
+ virtual void hideList();
+ virtual void remove(const PlaylistItem&);
+ virtual void sort();
+
+
+public slots:
+ void setCurrent(const PlaylistItem &, bool emitC);
+ void setCurrent(const PlaylistItem &);
+
+ void listItemSelected(QListViewItem*);
+
+ void randomize();
+
+private:
+ PlaylistItem currentItem, randomPrevious;
+
+signals:
+ void play(PlaylistItem*);
+
+private:
+ bool mExiting; // HACK HACK HACK HACK!!!
+ View *view;
+// QRect currentRect;
+ static SplitPlaylist *Self;
+};
+
+
+#endif
diff --git a/noatun/modules/splitplaylist/splitplaylist.cpp b/noatun/modules/splitplaylist/splitplaylist.cpp
new file mode 100644
index 00000000..e86a3921
--- /dev/null
+++ b/noatun/modules/splitplaylist/splitplaylist.cpp
@@ -0,0 +1,13 @@
+#include <kcmodule.h>
+
+#include "playlist.h"
+
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new SplitPlaylist();
+ }
+}
+
diff --git a/noatun/modules/splitplaylist/splitplaylist.plugin b/noatun/modules/splitplaylist/splitplaylist.plugin
new file mode 100644
index 00000000..3474dbed
--- /dev/null
+++ b/noatun/modules/splitplaylist/splitplaylist.plugin
@@ -0,0 +1,118 @@
+Filename=noatun_splitplaylist.la
+Author=Charles Samuels
+Site=http://www.derkarl.org/noatun
+Email=charles@kde.org
+Type=playlist
+License=Artistic
+Name=Split Playlist
+Name[af]=Skei Liedjielys
+Name[az]=Çalma Siyahısını Ayır
+Name[bn]=বিভাজিত সঙ্গীত-তালিকা
+Name[br]=Didrochañ ar roll tonioù
+Name[bs]=Podijeli playlistu
+Name[ca]=Dividir la selecció de peces
+Name[cs]=Oddělovací seznam skladeb
+Name[cy]=Hollti Rhestr Chwarae
+Name[da]=Opdelt spilleliste
+Name[de]=Aufgeteilte Wiedergabeliste
+Name[el]=Split λίστα αναπαραγωγής
+Name[eo]=Dividu ludliston
+Name[es]=Lista de reproducción dividida
+Name[et]=Poolitatud nimekiri
+Name[eu]=Erreprodukzio-zerrenda zatitu
+Name[fa]=شکافتن فهرست پخش
+Name[fi]=Jaettu soittolista
+Name[fr]=Liste de lecture découpée
+Name[ga]=Roinn Seinmliosta
+Name[gl]=Dividir Lista de Reproducións
+Name[hi]=स्प्लिट प्लेलिस्ट
+Name[hr]=Razdvoji listu pjesama
+Name[hu]=Osztott lejátszási lista
+Name[is]=Tvískiptur lagalisti
+Name[it]=Split playlist
+Name[km]=ពុះ​បញ្ជី​ចាក់
+Name[ko]=재생 목록 나누기
+Name[lt]=Suskaidytas gaidaraštis
+Name[lv]=Dalīt Plejlistu
+Name[mk]=Раздели листа со нумери
+Name[mt]=Playlist Maqsum
+Name[nb]=Splittet spilleliste
+Name[nds]=Opdeelt Afspeellist
+Name[ne]=बजाउने सूची विभाजन गर्नुहोस्
+Name[nl]=Gesplitste speellijst
+Name[nn]=Delt speleliste
+Name[pa]=ਸੰਗੀਤ-ਸੂਚੀ ਵੰਡੋ
+Name[pl]=Zwykła lista odtwarzania
+Name[pt]=Lista de Músicas Split
+Name[pt_BR]=A lista de reprodução dividida
+Name[ro]=Împarte lista de redare
+Name[ru]=Разбить список произведений
+Name[se]=Juhkkojuvvon čuojahanlistu
+Name[sk]=Rozdeľovací playlist
+Name[sl]=Razdeli predvajalni seznam
+Name[sr]=Split листа нумера
+Name[sr@Latn]=Split lista numera
+Name[sv]=Delad spellista
+Name[ta]=பாடல் பட்டியலை பிரி
+Name[tg]=Рӯйхати бозикуниҳои Пора
+Name[th]=แยกรายการเล่น
+Name[tr]=Ayrılmış Parça Listesi
+Name[uk]=Розбитий список композицій
+Name[ven]=Mutevhe wa tshitambi tshau phadalala
+Name[xh]=Chaka uluhlu lomdlalo
+Name[zh_CN]=分割播放列表
+Name[zh_HK]=分割播放清單
+Name[zh_TW]=分割播放清單
+Name[zu]=Qhekeza uluhlu lomdlalo
+Comment=The inaccurately titled playlist
+Comment[bg]=Неподреден списък за поддръжка на файлове за изпълнение
+Comment[bs]=Neispravno naslovljena playlista
+Comment[ca]=Llista de reproducció titulada inexactament
+Comment[cs]=Nesprávně pojmenovaný seznam skladeb
+Comment[cy]=Y rhestr chwarae efo'r teitl gwallus
+Comment[da]=Den upræcist benævnte spilleliste
+Comment[de]=Die ungenaue Wiedergabeliste
+Comment[el]=Η ανακριβώς ονομαζόμενη λίστα αναπαραγωγής
+Comment[eo]=La neĝuste titolita ludlisto
+Comment[es]=La lista de reproducción con título inexacto
+Comment[et]=Ebaadekvaatselt nimetatud nimekiri
+Comment[eu]=Zehatza ez den izenburudun erreprodukzio-zerrenda
+Comment[fa]=فهرست پخش که با بی‌دقتی عنوان‌بندی شده است
+Comment[fi]=Epätarkasti nimetty soittolista
+Comment[fr]=La liste de lecture mal nommée
+Comment[ga]=An seinmliosta le teideal neamhchruinn
+Comment[gl]=A lista de reprodución con títulos inexactos
+Comment[he]=רשימת הניגון עם השם הלא מדוייק
+Comment[hi]=गलत शीर्षक युक्त गीत-सूची
+Comment[hu]=Nem pontosan feliratozott lejátszási lista
+Comment[is]=Lagalistinn með ónákvæma nafnið
+Comment[it]=Playlist con i titoli non accurati
+Comment[ja]=不正確なタイトルのプレイリスト (実際に分割はしません)
+Comment[kk]=Дұрыс аталмаған орындау тізімі
+Comment[km]=បញ្ជី​ចាក់​ដែល​មាន​ចំណងជើង​មិន​សុក្រឹត
+Comment[ko]=정확하지 않게 제목이 붙은 재생 목록
+Comment[lt]=Netiksliai pavadintas gaidaraštis
+Comment[mk]=Неточно насловена листа со нумери
+Comment[nb]=Den unøyaktig navngitte spillelisten
+Comment[nds]=De nich nau nöömte Afspeellist
+Comment[ne]=अशुद्ध तरिकाले शीर्षक दिइएको बजाउने सूची
+Comment[nl]=De inaccuraat getitelde afspeellijst
+Comment[nn]=Spelelista med unøyaktig namn
+Comment[pl]=Zwykła, prosta lista odtwarzania
+Comment[pt]=A lista mal intitulada
+Comment[pt_BR]=A lista de reprodução erroneamente intitulada
+Comment[ro]=Listă de redare incorect denumită
+Comment[ru]=Список песен с неточными названиями
+Comment[sk]=Nesprávne pomenovaný playlist
+Comment[sl]=Nenatančno naslovljen predvajalni seznam
+Comment[sr]=Непрецизно названа листа нумера
+Comment[sr@Latn]=Neprecizno nazvana lista numera
+Comment[sv]=Den felaktigt benämnda spellistan
+Comment[ta]=பிழையாக தலைப்பிட்ட பாடல் பட்டியல்
+Comment[tg]=Рӯйхати бозикуниҳои бетартибона номгузошташуда
+Comment[th]=รายการเล่นที่มีชื่อไม่ถูกต้อง
+Comment[tr]=Düzensiz başlıklı çalma listesi
+Comment[uk]=Неохайно підписаний список композицій
+Comment[zh_CN]=命名不确切的播放列表
+Comment[zh_HK]=未有正確命名的播放清單
+Comment[zh_TW]=未精確命名的撥放清單
diff --git a/noatun/modules/splitplaylist/splui.rc b/noatun/modules/splitplaylist/splui.rc
new file mode 100644
index 00000000..2b0b0a32
--- /dev/null
+++ b/noatun/modules/splitplaylist/splui.rc
@@ -0,0 +1,34 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui name="SPL" version="1">
+
+<MenuBar>
+
+<Menu name="file">
+ <Action name="add_files" />
+ <Action name="add_dir" />
+</Menu>
+
+<Menu name="edit">
+ <Action name="delete" />
+ <Action name="clear" />
+ <Separator />
+ <Action name="shuffle" />
+</Menu>
+
+</MenuBar>
+
+<ToolBar noMerge="1" name="mainToolBar">
+ <text>Main Toolbar</text>
+ <Action name="add_files" />
+ <Action name="add_dir" />
+ <Separator />
+ <Action name="delete" />
+ <Separator />
+ <Action name="file_open" />
+ <Action name="file_save" />
+ <Action name="file_save_as" />
+ <Separator />
+ <Action name="edit_find" />
+</ToolBar>
+
+</kpartgui>
diff --git a/noatun/modules/splitplaylist/view.cpp b/noatun/modules/splitplaylist/view.cpp
new file mode 100644
index 00000000..7f5584f9
--- /dev/null
+++ b/noatun/modules/splitplaylist/view.cpp
@@ -0,0 +1,1009 @@
+/**
+ * Copyright (c) 2000-2004 Charles Samuels <charles@kde.org>
+ * 2000-2001 Neil Stevens <neil@qualityassistant.com>
+ *
+ * Copyright (c) from the patches of:
+ * 2001 Klas Kalass <klas.kalass@gmx.de>
+ * 2001 Anno v. Heimburg <doktor.dos@gmx.de>
+ **/
+
+
+// Abandon All Hope, Ye Who Enter Here
+
+#include <qdragobject.h>
+#include <qheader.h>
+#include <qlayout.h>
+#include <qmap.h>
+#include <qregexp.h>
+#include <qtextstream.h>
+#include <qpainter.h>
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <kfiledialog.h>
+#include <kfileitem.h>
+#include <kio/job.h>
+#include <kio/netaccess.h>
+#include <klocale.h>
+#include <kmenubar.h>
+#include <ksimpleconfig.h>
+#include <kstandarddirs.h>
+#include <kstdaction.h>
+#include <kedittoolbar.h>
+#include <kurldrag.h>
+#include <kmessagebox.h>
+
+#include <noatun/app.h>
+#include <noatun/player.h>
+#include <noatun/playlistsaver.h>
+
+#include "playlist.h"
+#include "view.h"
+#include "find.h"
+
+#define SPL SplitPlaylist::SPL()
+
+SafeListViewItem::SafeListViewItem(QListView *parent, QListViewItem *after, const KURL &text)
+ : QCheckListItem(parent,0, QCheckListItem::CheckBox), PlaylistItemData(), removed(false)
+{
+ addRef();
+ setUrl(text);
+
+ static_cast<KListView*>(parent)->moveItem(this, 0, after);
+ setOn(true);
+
+ // is this really needed, it makes the listview too wide for me :(
+// setText(0,text.filename());
+
+ // if (!isDownloaded()) setText(1, "0%");
+
+// mProperties.setAutoDelete(true);
+
+ if (!streamable() && enqueue(url()))
+ setUrl(KURL(localFilename()));
+
+ PlaylistItemData::added();
+}
+
+SafeListViewItem::SafeListViewItem(QListView *parent, QListViewItem *after, const QMap<QString,QString> &props)
+ : QCheckListItem(parent, 0, QCheckListItem::CheckBox), removed(false)
+{
+ addRef();
+
+ setOn(true);
+
+ // A version of setProperty that assumes a key is unique,
+ // and doesn't call modified for every new key.
+ // Ugly, but this function is a very hot path on playlist loading
+ for (QMap<QString,QString>::ConstIterator i=props.begin(); i!=props.end(); ++i )
+ {
+ QString n = i.key();
+ QString val = i.data();
+
+ if (n=="enabled")
+ {
+ setOn(val!="false" && val!="0");
+ }
+ else
+ {
+ Property p={n,val};
+ mProperties += p;
+ }
+ }
+
+ static_cast<KListView*>(parent)->moveItem(this, 0, after);
+ modified();
+
+ if (!streamable() && enqueue(url()))
+ {
+ KURL u;
+ u.setPath(localFilename());
+
+ setUrl(u);
+ }
+ PlaylistItemData::added();
+}
+
+SafeListViewItem::~SafeListViewItem()
+{
+ remove();
+}
+
+QString SafeListViewItem::file() const
+{
+ return localFilename();
+}
+
+static void pad(QString &str)
+{
+ int len=str.length();
+ int at = 0;
+ int blocklen=0;
+
+ static const int paddingsize=12;
+
+ // not static for reason
+ const QChar chars[paddingsize] =
+ {
+ QChar('0'), QChar('0'), QChar('0'), QChar('0'),
+ QChar('0'), QChar('0'), QChar('0'), QChar('0'),
+ QChar('0'), QChar('0'), QChar('0'), QChar('0')
+ };
+
+ for (int i=0; i < len; i++)
+ {
+ if (str[i].isNumber())
+ {
+ if (!blocklen)
+ at = i;
+ blocklen++;
+ }
+ else if (blocklen)
+ {
+ int pads=paddingsize;
+ pads -= blocklen;
+ str.insert(at, chars, pads);
+ i += pads;
+ blocklen = 0;
+ }
+ }
+ if (blocklen)
+ {
+ int pads=paddingsize;
+ pads -= blocklen;
+ str.insert(at, chars, pads);
+ }
+}
+
+int SafeListViewItem::compare(QListViewItem * i, int col, bool) const
+{
+ QString text1 = text(col);
+ QString text2 = i->text(col);
+
+ pad(text1);
+ pad(text2);
+ return text1.compare(text2);
+}
+
+QString SafeListViewItem::property(const QString &n, const QString &def) const
+{
+ for (QValueList<Property>::ConstIterator i=mProperties.begin(); i != mProperties.end(); ++i)
+ {
+ if ((*i).key==n)
+ return (*i).value;
+ }
+ if (n=="enabled")
+ {
+ if (isOn()) return "true";
+ else return "false";
+ }
+ return def;
+}
+
+void SafeListViewItem::setProperty(const QString &n, const QString &val)
+{
+ if (n=="enabled")
+ {
+ setOn(val!="false" && val!="0");
+ }
+ else
+ {
+ if ( property(n,"") == val )
+ {
+// kdDebug(66666) << "SafeListViewItem::setProperty(), property unchanged!" << endl;
+ return;
+ }
+
+ clearProperty(n);
+ Property p={n,val};
+ mProperties += p;
+ }
+ modified();
+}
+
+void SafeListViewItem::clearProperty(const QString &n)
+{
+ if (n=="enabled")
+ {
+ setOn(true);
+ modified();
+ return;
+ }
+
+ for (QValueList<Property>::Iterator i=mProperties.begin(); i != mProperties.end(); ++i)
+ {
+ if ((*i).key==n)
+ {
+ mProperties.remove(i);
+ modified();
+ break;
+ }
+ }
+}
+
+QStringList SafeListViewItem::properties() const
+{
+ QStringList list;
+ for (QValueList<Property>::ConstIterator i=mProperties.begin(); i != mProperties.end(); ++i)
+ list += (*i).key;
+ list += "enabled";
+ return list;
+}
+
+bool SafeListViewItem::isProperty(const QString &n) const
+{
+ for (QValueList<Property>::ConstIterator i=mProperties.begin(); i != mProperties.end(); ++i)
+ {
+ if ((*i).key==n)
+ return true;
+ }
+ return n=="enabled";
+}
+
+void SafeListViewItem::downloaded(int percent)
+{
+ if (!removed)
+ setText(1, QString::number(percent)+'%');
+}
+
+void SafeListViewItem::downloadTimeout()
+{
+ if (!removed)
+ setText(1, "-");
+}
+
+void SafeListViewItem::downloadFinished()
+{
+ if (!removed)
+ setText(1, "");
+}
+
+void SafeListViewItem::modified()
+{
+ bool widthChangeNeeded = false;
+
+ if (text(0)!=title())
+ {
+ setText(0, title());
+ widthChangeNeeded = true;
+ }
+
+ if (isDownloaded() && length()!=-1 && text(1)!=lengthString())
+ {
+ setText(1, lengthString());
+ widthChangeNeeded = true;
+ }
+
+ if (widthChangeNeeded)
+ widthChanged(-1);
+
+ PlaylistItemData::modified();
+}
+
+void SafeListViewItem::stateChange(bool s)
+{
+ // if you uncheck this, uncheck thet others that
+ // are selected too
+
+ QPtrList<QListViewItem> list=SPL->view->listView()->selectedItems();
+
+ // but not if I'm not selected
+ if (list.containsRef(this))
+ for (QListViewItem *i=list.first(); i != 0; i=list.next())
+ static_cast<QCheckListItem*>(i)->setOn(s);
+ else
+ QCheckListItem::stateChange(s);
+}
+
+void SafeListViewItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align)
+{
+ QCheckListItem::paintCell(p, cg, column, width, align);
+
+ if (SPL->current() == this)
+ {
+ p->save();
+ p->setRasterOp(XorROP);
+ p->fillRect(0, 0, width, height(), QColor(255,255,255));
+
+ p->restore();
+ }
+}
+
+void SafeListViewItem::remove()
+{
+ removed=true;
+ if (napp->player()->current()==this && !itemAbove() && !itemBelow())
+ {
+ napp->player()->stop();
+ SPL->setCurrent(0);
+ }
+ else if (napp->player()->current()==this)
+ {
+// SPL->setCurrent(0);
+// napp->player()->playCurrent();
+
+ if (napp->player()->isPlaying() && !SPL->exiting())
+ napp->player()->forward();
+ else
+ SPL->setCurrent(0);
+ }
+
+ if (listView())
+ {
+ if (SPL->currentItem==this) // just optimizing for least unreadably
+ SPL->setCurrent(static_cast<SafeListViewItem*>(itemBelow()));
+
+ listView()->takeItem(this);
+ }
+ else if (SPL->currentItem==this)
+ {
+ SPL->setCurrent(0);
+ }
+
+ dequeue();
+ PlaylistItemData::removed();
+}
+
+List::List(View *parent)
+ : KListView(parent), recursiveAddAfter(0), listJob(0)
+{
+ addColumn(i18n("File"));
+ addColumn(i18n("Time"));
+ setAcceptDrops(true);
+ setSorting(-1);
+ setDropVisualizer(true);
+ setDragEnabled(true);
+ setItemsMovable(true);
+ setSelectionMode(QListView::Extended);
+ connect(this, SIGNAL(dropped(QDropEvent*, QListViewItem*)), SLOT(dropEvent(QDropEvent*, QListViewItem*)));
+ connect(this, SIGNAL(moved()), SLOT(move()));
+ connect(this, SIGNAL(aboutToMove()), parent, SLOT(setNoSorting()));
+ connect(this, SIGNAL(deleteCurrentItem()), parent, SLOT(deleteSelected()));
+}
+
+List::~List()
+{
+}
+
+void List::move()
+{
+ emit modified();
+}
+
+bool List::acceptDrag(QDropEvent *event) const
+{
+ return KURLDrag::canDecode(event) || KListView::acceptDrag(event);
+}
+
+void List::dropEvent(QDropEvent *event, QListViewItem *after)
+{
+ static_cast<View*>(parent())->setNoSorting();
+ KURL::List textlist;
+ if (!KURLDrag::decode(event, textlist)) return;
+ event->acceptAction();
+
+ for (KURL::List::Iterator i=textlist.begin(); i != textlist.end(); ++i)
+ {
+ after= addFile(*i, false, after);
+ }
+
+ emit modified();
+}
+
+void List::keyPressEvent(QKeyEvent *e)
+{
+ if (e->key()==Key_Enter || e->key()==Key_Return)
+ {
+ if (currentItem())
+ {
+ emit KListView::executed(currentItem());
+ }
+
+ return;
+ }
+
+ if (e->key()==Key_Delete)
+ {
+ if (currentItem())
+ {
+ emit deleteCurrentItem();
+ }
+
+ return;
+ }
+
+ KListView::keyPressEvent(e);
+
+
+}
+
+/**
+ * use this only once!!!
+ **/
+class NoatunSaver : public PlaylistSaver
+{
+ List *mList;
+ SafeListViewItem *after, *mFirst;
+public:
+ NoatunSaver(List *l, QListViewItem *after=0)
+ : mList(l)
+ {
+ this->after = static_cast<SafeListViewItem*>(after);
+ mFirst = 0;
+ }
+
+ QListViewItem *getAfter() { return after; }
+ QListViewItem *getFirst() { return mFirst; }
+
+protected:
+ virtual void readItem(const QMap<QString,QString> &properties)
+ {
+ after = new SafeListViewItem(mList, after, properties);
+ if (mFirst==0)
+ mFirst = after;
+ }
+
+ virtual PlaylistItem writeItem()
+ {
+ if (!after)
+ {
+ after=static_cast<SafeListViewItem*>(mList->firstChild());
+ }
+ else
+ {
+ after=static_cast<SafeListViewItem*>(static_cast<SafeListViewItem*>(after)->nextSibling());
+ }
+ return PlaylistItem(after);
+ }
+};
+
+
+bool View::saveToURL(const KURL &url)
+{
+ NoatunSaver saver(list);
+ if(saver.save(url))
+ {
+ return true;
+ }
+ else
+ {
+ KMessageBox::error( this, i18n("Could not write to %1.").arg(url.prettyURL()) );
+ return false;
+ }
+}
+
+void View::exportTo(const KURL &url)
+{
+ QString local(napp->tempSaveName(url.path()));
+ QFile saver(local);
+ saver.open(IO_ReadWrite | IO_Truncate);
+ QTextStream t(&saver);
+ // navigate the list
+ for (SafeListViewItem *i=static_cast<SafeListViewItem*>(listView()->firstChild());
+ i != 0; i=static_cast<SafeListViewItem*>(i->itemBelow()))
+ {
+ KURL u=i->url();
+ if (u.isLocalFile())
+ t<< u.path() << '\n';
+ else
+ t << u.url() << '\n';
+ }
+ saver.close();
+
+ KIO::NetAccess::upload(local, url, this);
+
+ saver.remove();
+}
+
+QListViewItem *List::openGlobal(const KURL &u, QListViewItem *after)
+{
+ clear();
+ NoatunSaver saver(this, after);
+ saver.metalist(u);
+
+ return saver.getAfter();
+}
+
+// for m3u files
+QListViewItem *List::importGlobal(const KURL &u, QListViewItem *after)
+{
+ NoatunSaver saver(this, after);
+ if (!saver.metalist(u))
+ {
+ after=new SafeListViewItem(this, after, u);
+// SPL->listItemSelected(after);
+ return after;
+ }
+
+ // return the first item added from this playlist
+ // that way noatun can start playing the first item
+ if (saver.getFirst())
+ return saver.getFirst();
+
+ // failsafe in case nothing was added, getFirst() may return 0
+ return saver.getAfter();
+}
+
+QListViewItem *List::addFile(const KURL& url, bool play, QListViewItem *after)
+{
+ // when a new item is added, we don't want to sort anymore
+ SPL->view->setNoSorting();
+
+ if (
+ url.path().right(4).lower()==".m3u"
+ || url.path().right(4).lower()==".pls"
+ || url.protocol().lower()=="http"
+ )
+ {
+ // a playlist is requested
+ QListViewItem *i = importGlobal(url, after);
+ if (play)
+ SPL->listItemSelected(i);
+ return i;
+ }
+ else
+ {
+ if (!after) after=lastItem();
+ KFileItem fileItem(KFileItem::Unknown,KFileItem::Unknown,url);
+ if (fileItem.isDir())
+ {
+ addDirectoryRecursive(url, after);
+ return after; // don't (and can't) know better!?
+ }
+ else
+ {
+ QListViewItem *i = new SafeListViewItem(this, after, url);
+ if (play)
+ SPL->listItemSelected(i);
+ return i;
+ }
+ }
+}
+
+// starts a new listJob if there is no active but work to do
+void List::addNextPendingDirectory()
+{
+ KURL::List::Iterator pendingIt= pendingAddDirectories.begin();
+ if (!listJob && (pendingIt!= pendingAddDirectories.end()))
+ {
+ currentJobURL= *pendingIt;
+ listJob= KIO::listRecursive(currentJobURL, false,false);
+ connect(
+ listJob, SIGNAL(entries(KIO::Job*, const KIO::UDSEntryList&)),
+ SLOT(slotEntries(KIO::Job*, const KIO::UDSEntryList&))
+ );
+ connect(
+ listJob, SIGNAL(result(KIO::Job *)),
+ SLOT(slotResult(KIO::Job *))
+ );
+ connect(
+ listJob, SIGNAL(redirection(KIO::Job *, const KURL &)),
+ SLOT(slotRedirection(KIO::Job *, const KURL &))
+ );
+ pendingAddDirectories.remove(pendingIt);
+ }
+}
+
+void List::addDirectoryRecursive(const KURL &dir, QListViewItem *after)
+{
+ if (!after) after=lastItem();
+ recursiveAddAfter= after;
+ pendingAddDirectories.append(dir);
+ addNextPendingDirectory();
+}
+
+void List::slotResult(KIO::Job *job)
+{
+ listJob= 0;
+ if (job && job->error())
+ job->showErrorDialog();
+ addNextPendingDirectory();
+}
+
+void List::slotEntries(KIO::Job *, const KIO::UDSEntryList &entries)
+{
+ QMap<QString,KURL> __list; // temp list to sort entries
+
+ KIO::UDSEntryListConstIterator it = entries.begin();
+ KIO::UDSEntryListConstIterator end = entries.end();
+
+ for (; it != end; ++it)
+ {
+ KFileItem file(*it, currentJobURL, false /* no mimetype detection */, true);
+ // "prudhomm:
+ // insert the path + url in the map to sort automatically by path
+ // note also that you use audiocd to rip your CDs then it will be sorted the right way
+ // now it is an easy fix to have a nice sort BUT it is not the best
+ // we should sort based on the tracknumber"
+ // - copied over from old kdirlister hack <hans_meine@gmx.de>
+ if (!file.isDir())
+ __list.insert(file.url().path(), file.url());
+ }
+ QMap<QString,KURL>::Iterator __it;
+ for( __it = __list.begin(); __it != __list.end(); ++__it )
+ {
+ recursiveAddAfter= addFile(__it.data(), false, recursiveAddAfter);
+ }
+}
+
+void List::slotRedirection(KIO::Job *, const KURL & url)
+{
+ currentJobURL= url;
+}
+
+/////////////////////////////////
+
+View::View(SplitPlaylist *)
+ : KMainWindow(0, "NoatunSplitplaylistView")
+{
+ list=new List(this);
+ setCentralWidget(list);
+ connect(list, SIGNAL(modified(void)), this, SLOT(setModified(void)) );
+ // connect the click on the header with sorting
+ connect(list->header(),SIGNAL(clicked(int)),this,SLOT(headerClicked(int)) );
+
+ mOpen=new KAction(i18n("Add &Files..."), "queue", 0, this, SLOT(addFiles()), actionCollection(), "add_files");
+ (void) new KAction(i18n("Add Fol&ders..."), "folder", 0, this, SLOT(addDirectory()), actionCollection(), "add_dir");
+ mDelete=new KAction(i18n("Delete"), "editdelete", Key_Delete, this, SLOT(deleteSelected()), actionCollection(), "delete");
+
+ mClose=KStdAction::close(this, SLOT(close()), actionCollection());
+ mFind=KStdAction::find(this, SLOT(find()), actionCollection());
+
+ (void) KStdAction::configureToolbars(this, SLOT(configureToolBars()), actionCollection());
+ mOpenNew=KStdAction::openNew(this, SLOT(openNew()), actionCollection());
+ mOpenpl=KStdAction::open(this, SLOT(open()), actionCollection());
+ mSave=KStdAction::save(this, SLOT(save()), actionCollection());
+ mSaveAs=KStdAction::saveAs(this, SLOT(saveAs()), actionCollection());
+
+ (void) new KAction(i18n("Shuffle"), "misc", 0, SPL, SLOT( randomize() ), actionCollection(), "shuffle");
+ (void) new KAction(i18n("Clear"), "editclear", 0, list, SLOT( clear() ), actionCollection(), "clear");
+
+ createGUI("splui.rc");
+
+ mFinder = new Finder(this);
+
+ applyMainWindowSettings(KGlobal::config(), "SPL Window");
+ list->setFocus();
+}
+
+void View::find()
+{
+ mFinder->show();
+ connect(mFinder, SIGNAL(search(Finder*)), SLOT(findIt(Finder*)));
+}
+
+static bool testWord(QListViewItem *i, const QString &finder)
+{
+ PlaylistItemData *item=static_cast<SafeListViewItem*>(i);
+ if (item->title().find(finder, 0, false) >=0)
+ return true;
+ if (item->file().find(finder, 0, false) >=0)
+ return true;
+ if (item->url().path().find(finder.local8Bit(), 0, false) >=0)
+ return true;
+ if (item->lengthString().find(finder, 0, false) >=0)
+ return true;
+ if (item->mimetype().find(finder.local8Bit(), 0, false) >=0)
+ return true;
+ return false;
+}
+
+static bool testWord(QListViewItem *i, const QRegExp &finder)
+{
+ PlaylistItemData *item=static_cast<SafeListViewItem*>(i);
+ if (item->title().find(finder) >=0)
+ return true;
+ if (item->file().find(finder) >=0)
+ return true;
+ if (item->url().path().find(finder) >=0)
+ return true;
+ if (item->lengthString().find(finder) >=0)
+ return true;
+ if (item->mimetype().find(finder) >=0)
+ return true;
+ return false;
+}
+
+void View::findIt(Finder *f)
+{
+ QListViewItem *search=list->currentItem();
+
+ if (list->currentItem())
+ {
+ if (f->isForward())
+ search=list->currentItem()->itemBelow();
+ else
+ search=list->currentItem()->itemAbove();
+ }
+ else
+ {
+ if (f->isForward())
+ search=list->firstChild();
+ else
+ search=list->lastChild();
+ }
+
+
+ while (search)
+ {
+ if (f->regexp())
+ {
+ if (testWord(search, QRegExp(f->string(), false)))
+ break;
+ }
+ else
+ {
+ if (testWord(search, f->string()))
+ break;
+ }
+
+ if (f->isForward())
+ search=search->itemBelow();
+ else
+ search=search->itemAbove();
+
+ if (!search)
+ {
+ if (f->isForward())
+ {
+ if (KMessageBox::questionYesNo(this, i18n("End of playlist reached. Continue searching from beginning?"),QString::null,KStdGuiItem::cont(),KStdGuiItem::cancel()) == KMessageBox::Yes)
+ search=list->firstChild();
+ }
+ else
+ {
+ if (KMessageBox::questionYesNo(this, i18n("Beginning of playlist reached. Continue searching from end?"),QString::null,KStdGuiItem::cont(),KStdGuiItem::cancel()) == KMessageBox::Yes)
+ search=list->lastChild();
+ }
+ }
+ }
+
+ if (search)
+ {
+ { // select none
+ QPtrList<QListViewItem> sel=list->selectedItems();
+ for (QListViewItem *i=sel.first(); i!=0; i=sel.next())
+ list->setSelected(i, false);
+ }
+ list->setSelected(search, true);
+ list->setCurrentItem(search);
+ list->ensureItemVisible(search);
+ }
+}
+
+View::~View()
+{
+ napp->player()->stop();
+ hide();
+ saveState();
+ delete list;
+}
+
+void View::init()
+{
+ // see if we are importing an old-style list
+ bool importing= ! QFile(napp->dirs()->saveLocation("data", "noatun/") + "splitplaylist.xml").exists();
+
+ if (importing)
+ {
+ KURL internalURL;
+ internalURL.setPath(napp->dirs()->saveLocation("data", "noatun/") + "splitplaylistdata");
+ NoatunSaver saver(list, 0);
+ saver.load(internalURL, PlaylistSaver::M3U);
+ }
+ else
+ {
+ KURL internalURL;
+ internalURL.setPath(napp->dirs()->saveLocation("data", "noatun/") + "splitplaylist.xml");
+ list->openGlobal(internalURL);
+ }
+
+ KConfig &config = *KGlobal::config();
+ config.setGroup("splitplaylist");
+
+ // this has to come after openGlobal, since openGlobal emits modified()
+ setModified(config.readBoolEntry("modified", false));
+ QString path = config.readPathEntry("file");
+ // don't call setPath with an empty path, that would make the url "valid"
+ if ( !path.isEmpty() )
+ mPlaylistFile.setPath(path);
+
+ SPL->reset();
+ int saved = config.readNumEntry("current", 0);
+
+ PlaylistItem item=SPL->getFirst();
+ for(int i = 0 ; i < saved ; i++)
+ {
+ item=SPL->getAfter(item);
+ }
+ if (item)
+ SPL->setCurrent(item);
+}
+
+void View::save()
+{
+ if(mPlaylistFile.isEmpty() || !mPlaylistFile.isValid())
+ {
+ saveAs();
+ return;
+ }
+
+ if(saveToURL(mPlaylistFile))
+ setModified(false);
+}
+
+void View::saveAs()
+{
+ KURL u=KFileDialog::getSaveURL(0, "*.xml splitplaylistdata *.pls *.m3u\n*", this, i18n("Save Playlist"));
+ if(!u.isValid())
+ return;
+ mPlaylistFile = u;
+ save();
+}
+
+void View::open()
+{
+ KURL u=KFileDialog::getOpenURL(0, "*.xml splitplaylistdata *.pls *.m3u\n*", this, i18n("Open Playlist"));
+ if(!u.isValid())
+ return;
+ mPlaylistFile = u;
+ list->openGlobal(u);
+ setModified(false);
+}
+
+void View::openNew()
+{
+ mPlaylistFile = "";
+ listView()->clear();
+}
+
+void List::clear()
+{
+ SPL->setCurrent(0);
+
+ QListView::clear();
+}
+
+void View::deleteSelected()
+{
+ QPtrList<QListViewItem> items(list->selectedItems());
+
+ bool stopped=false;
+ // noatun shouldn't play files for now
+ QListViewItem *afterLast=0;
+
+ for (QPtrListIterator<QListViewItem> it(items); it.current(); ++it)
+ {
+ SafeListViewItem *i = static_cast<SafeListViewItem*>(*it);
+ if (!stopped && SPL->current() == i)
+ {
+ napp->player()->stop();
+ SPL->setCurrent(0);
+ stopped = true;
+ }
+ i->remove();
+
+ afterLast = i->itemBelow();
+ }
+
+ if (stopped)
+ SPL->setCurrent(static_cast<SafeListViewItem*>(afterLast));
+
+ setModified(true);
+}
+
+void View::addFiles()
+{
+ KURL::List files=KFileDialog::getOpenURLs(":mediadir", napp->mimeTypes(), this, i18n("Select File to Play"));
+
+ QListViewItem *last = list->lastItem();
+ for(KURL::List::Iterator it=files.begin(); it!=files.end(); ++it)
+ last = addFile(KURL(*it), false);
+
+ setModified(true);
+}
+
+void View::addDirectory()
+{
+ QString file=KFileDialog::getExistingDirectory(0, this, i18n("Select Folder"));
+
+ if (!file) return;
+ KURL url;
+ url.setPath(file);
+ list->addDirectoryRecursive(url);
+
+ setModified(true);
+}
+
+void View::closeEvent(QCloseEvent*)
+{
+ hide();
+}
+
+void View::showEvent(QShowEvent *)
+{
+ emit shown();
+}
+
+void View::hideEvent(QHideEvent *)
+{
+ emit hidden();
+}
+
+void View::setModified(bool b)
+{
+ modified = b;
+ setCaption(i18n("Playlist"), modified);
+}
+
+void View::setModified(void)
+{
+ setModified(true);
+}
+
+void View::saveState(void)
+{
+ KConfig &config = *KGlobal::config();
+ config.setGroup("splitplaylist");
+
+ config.writeEntry("modified", modified);
+ config.writePathEntry("file", mPlaylistFile.path());
+ saveToURL(napp->dirs()->saveLocation("data", "noatun/") + "splitplaylist.xml");
+
+ unsigned int i;
+ PlaylistItem item=SPL->getFirst();
+ for(i = 0; item && item != SPL->current(); )
+ item=SPL->getAfter(item), i++;
+
+ config.writeEntry("current", i);
+ saveMainWindowSettings(KGlobal::config(), "SPL Window");
+
+ config.sync();
+}
+
+void View::configureToolBars()
+{
+ saveMainWindowSettings(KGlobal::config(), "SPL Window");
+ KEditToolbar dlg(actionCollection(), "splui.rc");
+ connect(&dlg, SIGNAL(newToolbarConfig()), SLOT(newToolBarConfig()));
+ dlg.exec();
+}
+
+void View::newToolBarConfig()
+{
+ createGUI("splui.rc");
+ applyMainWindowSettings(KGlobal::config(), "SPL Window");
+}
+
+// turns the sorting on or off
+void View::setSorting(bool on, int column)
+{
+ if (on)
+ {
+ list->setSorting(column,true);
+ list->setShowSortIndicator(true);
+ }
+ else
+ {
+ list->setShowSortIndicator(false);
+ list->setSorting(-1);
+ }
+}
+
+void View::headerClicked(int column)
+{
+ // this is to avoid that if we already have it sorted,
+ // we sort it again ascendingly this way, clicking on
+ // the header a second time will correctly toggle
+ // ascending/descending sort
+ if (list->showSortIndicator())
+ {
+ return;
+ }
+ else
+ {
+ setSorting(true,column);
+ }
+}
+
+#include "view.moc"
+
diff --git a/noatun/modules/splitplaylist/view.h b/noatun/modules/splitplaylist/view.h
new file mode 100644
index 00000000..18dc917a
--- /dev/null
+++ b/noatun/modules/splitplaylist/view.h
@@ -0,0 +1,165 @@
+#ifndef VIEW_H
+#define VIEW_H
+
+#include <qevent.h>
+#include <qptrlist.h>
+#include <klistview.h>
+#include <kmainwindow.h>
+#include <qrect.h>
+#include <qdict.h>
+#include <kio/global.h>
+#include <noatun/downloader.h>
+
+class Finder;
+class View;
+namespace KIO { class ListJob; }
+
+
+class SafeListViewItem
+ : public QCheckListItem
+ , public PlaylistItemData
+ , public DownloadItem
+{
+public:
+ SafeListViewItem(QListView *parent, QListViewItem *after, const KURL &text);
+ SafeListViewItem(QListView *parent, QListViewItem *after, const QMap<QString,QString> &properties);
+ virtual ~SafeListViewItem();
+
+ virtual QString property(const QString &, const QString & = 0) const;
+ virtual void setProperty(const QString &, const QString &);
+ virtual void clearProperty(const QString &);
+ virtual QStringList properties() const;
+ virtual bool isProperty(const QString &) const;
+
+ virtual QString file() const;
+
+ int compare(QListViewItem * i, int col, bool ascending) const;
+ virtual void remove();
+
+protected:
+ virtual void downloaded(int percent);
+ virtual void downloadTimeout();
+ virtual void downloadFinished();
+ virtual void modified();
+ virtual void stateChange(bool s);
+
+ void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align);
+
+private:
+ struct Property
+ {
+ QString key;
+ QString value;
+ };
+ QValueList<Property> mProperties;
+ bool removed;
+};
+
+class List : public KListView
+{
+Q_OBJECT
+friend class View;
+public:
+ List(View *parent);
+ virtual ~List();
+ QListViewItem *openGlobal(const KURL&, QListViewItem * =0);
+ QListViewItem *importGlobal(const KURL&, QListViewItem * =0);
+ QListViewItem *addFile(const KURL&, bool play=false, QListViewItem * =0);
+ void addDirectoryRecursive(const KURL &dir, QListViewItem *after= 0);
+
+public slots:
+ virtual void clear();
+
+signals:
+ void modified(void);
+ void deleteCurrentItem();
+
+protected:
+ virtual bool acceptDrag(QDropEvent *event) const;
+ virtual void keyPressEvent(QKeyEvent *e);
+
+protected slots:
+ virtual void dropEvent(QDropEvent *event, QListViewItem *after);
+ void move();
+
+protected:
+ QListViewItem *recursiveAddAfter;
+
+protected slots:
+ // used when adding directories via KIO::listRecursive
+ void slotResult(KIO::Job *job);
+ void slotEntries(KIO::Job *job, const KIO::UDSEntryList &entries);
+ void slotRedirection(KIO::Job *, const KURL & url);
+
+protected:
+ void addNextPendingDirectory();
+ KURL::List pendingAddDirectories;
+ KIO::ListJob *listJob;
+ KURL currentJobURL;
+};
+
+class KFileDialog;
+class KToggleAction;
+class KToolBar;
+
+class View : public KMainWindow
+{
+Q_OBJECT
+public:
+ View(SplitPlaylist *mother);
+ // load the SM playlist
+ void init();
+ virtual ~View();
+ List *listView() const { return list; }
+ QListViewItem *addFile(const KURL &u, bool play=false)
+ { return list->addFile(u, play, list->lastItem()); }
+
+
+public slots:
+ void deleteSelected();
+ void addFiles();
+ void addDirectory();
+ void save();
+ void saveAs();
+ void open();
+ void openNew();
+ void setSorting(bool on, int column = 0);
+ void setNoSorting() { setSorting(false); }
+ void headerClicked(int column);void find();
+ void findIt(Finder *);
+
+
+private slots:
+ void setModified();
+ void saveState();
+
+ void configureToolBars();
+ void newToolBarConfig();
+
+protected:
+ void setupActions();
+
+ bool saveToURL(const KURL &);
+ void exportTo(const KURL &);
+
+ void setModified(bool);
+ virtual void closeEvent(QCloseEvent*e);
+ virtual void showEvent(QShowEvent *);
+ virtual void hideEvent(QHideEvent *);
+
+signals:
+ void hidden();
+ void shown();
+
+private:
+ List *list;
+ KAction *mOpen, *mDelete, *mSave, *mSaveAs, *mOpenpl, *mOpenNew;
+ KAction *mClose;
+ KAction *mFind;
+ Finder *mFinder;
+
+ KURL mPlaylistFile;
+ bool modified;
+};
+
+#endif
diff --git a/noatun/modules/systray/Makefile.am b/noatun/modules/systray/Makefile.am
new file mode 100644
index 00000000..1dcfdd25
--- /dev/null
+++ b/noatun/modules/systray/Makefile.am
@@ -0,0 +1,16 @@
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_systray.la
+
+noatun_systray_la_SOURCES = systray.cpp noatunui.cpp kitsystemtray.cpp cmodule.cpp \
+ yhconfig.kcfgc yhconfigwidget.ui
+
+
+noatun_systray_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_systray_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la
+
+noatun_systray_la_METASOURCES = AUTO
+
+noinst_HEADERS = systray.h kitsystemtray.h cmodule.h
+
+noatun_modules_systray_DATA = systray.plugin systrayui.rc yhconfig.kcfg
+noatun_modules_systraydir = $(kde_datadir)/noatun
diff --git a/noatun/modules/systray/cmodule.cpp b/noatun/modules/systray/cmodule.cpp
new file mode 100644
index 00000000..4adf7e44
--- /dev/null
+++ b/noatun/modules/systray/cmodule.cpp
@@ -0,0 +1,192 @@
+// cmodule.cpp
+//
+// Copyright (C) 2001 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#include "cmodule.h"
+#include "yhconfig.h"
+#include "yhconfigwidget.h"
+
+#include <kdebug.h>
+//#include <kglobal.h>
+#include <klocale.h>
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qradiobutton.h>
+#include <qspinbox.h>
+
+#include <noatun/app.h>
+#include <noatun/pluginloader.h>
+
+#include <fixx11h.h>
+
+YHModule::YHModule(QObject *_parent)
+ : CModule(i18n("System Tray Icon"), i18n("Configure System Tray Icon"),
+ "bottom", _parent)
+{
+ QVBoxLayout *top = new QVBoxLayout(this);
+ mWidget = new YHConfigWidget(this);
+ top->addWidget(mWidget);
+
+ mWidget->cmbModifier->insertItem(i18n("None"), YHConfig::None);
+ mWidget->cmbModifier->insertItem(i18n("Shift"), YHConfig::Shift);
+ mWidget->cmbModifier->insertItem(i18n("Alt"), YHConfig::Alt);
+ mWidget->cmbModifier->insertItem(i18n("Ctrl"), YHConfig::Ctrl);
+ mWidget->cmbModifier->setCurrentItem(YHConfig::None);
+
+ connect(mWidget->chkUsePopup, SIGNAL(toggled(bool)), this, SLOT(slotUsePopupToggled(bool)));
+ connect(mWidget->cmbModifier, SIGNAL(activated(int)), this, SLOT(slotModifierActivated(int)));
+ connect(mWidget->grpMwheel, SIGNAL(clicked(int)), this, SLOT(slotMwheelClicked(int)));
+
+ reopen();
+}
+
+
+void YHModule::reopen()
+{
+ kdDebug(66666) << k_funcinfo << endl;
+ YHConfig *c = YHConfig::self();
+
+ /** General TAB **/
+
+ mWidget->chkUseTooltip->setChecked(c->tip());
+ mWidget->chkUseCovers->setChecked(c->passivePopupCovers());
+
+ mWidget->chkUsePopup->setChecked(c->passivePopup());
+ mWidget->spinPopupTimeout->setValue(c->passivePopupTimeout());
+ mWidget->chkPopupButtons->setChecked(c->passivePopupButtons());
+
+ switch(c->stateIconDisplay())
+ {
+ case (YHConfig::Animation):
+ mWidget->rbStateAnim->setChecked(true);
+ break;
+ case (YHConfig::FlashingIcon):
+ mWidget->rbStateFlashing->setChecked(true);
+ break;
+ case (YHConfig::StaticIcon):
+ mWidget->rbStateStatic->setChecked(true);
+ break;
+ case (YHConfig::NoIcon):
+ mWidget->rbStateNone->setChecked(true);
+ break;
+ }
+
+ /** Advanced TAB **/
+
+ if (c->middleMouseAction() == YHConfig::PlayPause)
+ mWidget->rbPlayPause->setChecked(true);
+ else
+ mWidget->rbHideShowPlaylist->setChecked(true);
+
+ mActionMap[YHConfig::None] = YHConfig::self()->mouseWheelAction(YHConfig::None);
+ mActionMap[YHConfig::Shift] = YHConfig::self()->mouseWheelAction(YHConfig::Shift);
+ mActionMap[YHConfig::Alt] = YHConfig::self()->mouseWheelAction(YHConfig::Alt);
+ mActionMap[YHConfig::Ctrl] = YHConfig::self()->mouseWheelAction(YHConfig::Ctrl);
+
+ slotModifierActivated(mWidget->cmbModifier->currentItem());
+}
+
+
+void YHModule::save()
+{
+ kdDebug(66666) << k_funcinfo << endl;
+
+ YHConfig *c = YHConfig::self();
+
+ /** General TAB **/
+
+ c->setTip(mWidget->chkUseTooltip->isChecked());
+ c->setPassivePopupCovers(mWidget->chkUseCovers->isChecked());
+
+ c->setPassivePopup(mWidget->chkUsePopup->isChecked());
+ c->setPassivePopupTimeout(mWidget->spinPopupTimeout->value());
+ c->setPassivePopupButtons(mWidget->chkPopupButtons->isChecked());
+
+ if (mWidget->rbStateAnim->isChecked())
+ c->setStateIconDisplay(YHConfig::Animation);
+ else if (mWidget->rbStateFlashing->isChecked())
+ c->setStateIconDisplay(YHConfig::FlashingIcon);
+ else if (mWidget->rbStateStatic->isChecked())
+ c->setStateIconDisplay(YHConfig::StaticIcon);
+ else
+ c->setStateIconDisplay(YHConfig::NoIcon);
+
+ /** Advanced TAB **/
+
+ if (mWidget->rbPlayPause->isChecked())
+ c->setMiddleMouseAction(YHConfig::PlayPause);
+ else
+ c->setMiddleMouseAction(YHConfig::HideShowPlaylist);
+
+ c->setMouseWheelAction(YHConfig::None, mActionMap[YHConfig::None]);
+ c->setMouseWheelAction(YHConfig::Shift, mActionMap[YHConfig::Shift]);
+ c->setMouseWheelAction(YHConfig::Alt, mActionMap[YHConfig::Alt]);
+ c->setMouseWheelAction(YHConfig::Ctrl, mActionMap[YHConfig::Ctrl]);
+
+ c->writeConfig();
+ emit saved();
+}
+
+
+void YHModule::slotUsePopupToggled(bool on)
+{
+ mWidget->lblPopupTimeout->setEnabled(on);
+ mWidget->spinPopupTimeout->setEnabled(on);
+ mWidget->chkPopupButtons->setEnabled(on);
+}
+
+
+void YHModule::slotModifierActivated(int index)
+{
+ kdDebug(66666) << k_funcinfo << endl;
+ switch(mActionMap[index])
+ {
+ case (YHConfig::Nothing):
+ mWidget->rbActNothing->setChecked(true);
+ break;
+ case (YHConfig::ChangeVolume):
+ mWidget->rbActVolume->setChecked(true);
+ break;
+ case (YHConfig::ChangeTrack):
+ mWidget->rbActTrack->setChecked(true);
+ break;
+ }
+}
+
+void YHModule::slotMwheelClicked(int index)
+{
+ kdDebug(66666) << k_funcinfo << endl;
+ if (index == 0)
+ mActionMap[mWidget->cmbModifier->currentItem()] = YHConfig::Nothing;
+ else if (index == 1)
+ mActionMap[mWidget->cmbModifier->currentItem()] = YHConfig::ChangeVolume;
+ else
+ mActionMap[mWidget->cmbModifier->currentItem()] = YHConfig::ChangeTrack;
+}
+
+#include "cmodule.moc"
diff --git a/noatun/modules/systray/cmodule.h b/noatun/modules/systray/cmodule.h
new file mode 100644
index 00000000..fecc70e3
--- /dev/null
+++ b/noatun/modules/systray/cmodule.h
@@ -0,0 +1,55 @@
+// cmodule.h
+//
+// Copyright (C) 2001 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifndef CMODULE_H
+#define CMODULE_H
+
+#include <qmap.h>
+#include <noatun/pref.h>
+
+class YHConfigWidget;
+
+class YHModule : public CModule
+{
+Q_OBJECT
+ public:
+ YHModule(QObject *_parent);
+ virtual void save();
+ virtual void reopen();
+
+ signals:
+ void saved();
+
+ private:
+ YHConfigWidget *mWidget;
+ QMap<int, int> mActionMap;
+
+ private slots:
+ void slotUsePopupToggled(bool on);
+ void slotModifierActivated(int index);
+ void slotMwheelClicked(int index);
+};
+
+#endif
diff --git a/noatun/modules/systray/kitsystemtray.cpp b/noatun/modules/systray/kitsystemtray.cpp
new file mode 100644
index 00000000..5847d7da
--- /dev/null
+++ b/noatun/modules/systray/kitsystemtray.cpp
@@ -0,0 +1,131 @@
+// $Id$
+//
+// Kit
+//
+// Copyright (C) 1999 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "yhconfig.h"
+
+#include <noatun/app.h>
+#include <noatun/player.h>
+#include <qdragobject.h>
+
+#include "kitsystemtray.h"
+#include <kmainwindow.h>
+#include <kpopupmenu.h>
+#include <kxmlguifactory.h>
+#include <kiconloader.h>
+#include <kurldrag.h>
+
+#include <fixx11h.h>
+
+KitSystemTray::KitSystemTray(const QString &contextMenu, KMainWindow *parent, const char *name)
+ : KSystemTray(parent, name)
+{
+ setAlignment(AlignHCenter | AlignVCenter);
+ menu = (KPopupMenu *)parent->guiFactory()->container(contextMenu, parent);
+ menu->insertTitle(SmallIcon("noatun"), QString::null, 0, 0);
+ setAcceptDrops(true);
+}
+
+void KitSystemTray::changeTitle(const QPixmap &pixmap, const QString &title)
+{
+ menu->changeTitle(0, pixmap, title);
+}
+
+void KitSystemTray::showEvent(QShowEvent *)
+{
+ // empty
+}
+
+void KitSystemTray::mousePressEvent(QMouseEvent *event)
+{
+ switch(event->button())
+ {
+ case LeftButton:
+ napp->toggleInterfaces();
+ break;
+ case MidButton:
+ if (YHConfig::self()->middleMouseAction() == YHConfig::HideShowPlaylist)
+ napp->playlist()->toggleList();
+ else // play or pause
+ napp->player()->playpause();
+ break;
+ default:
+ menu->popup(event->globalPos());
+ break;
+ }
+}
+
+void KitSystemTray::dragEnterEvent(QDragEnterEvent * event)
+{
+ event->accept(KURLDrag::canDecode(event)); // accept uri drops only
+}
+
+void KitSystemTray::dropEvent(QDropEvent * event)
+{
+ KURL::List uris;
+ if (KURLDrag::decode(event, uris))
+ {
+ KURL::List::ConstIterator it;
+ for (it = uris.begin(); it != uris.end(); ++it)
+ napp->player()->openFile(*it, false);
+ }
+}
+
+void KitSystemTray::wheelEvent(QWheelEvent *event)
+{
+ YHConfig *c = YHConfig::self();
+
+ int action = 0;
+ if (event->state() & Qt::ShiftButton)
+ action = c->mouseWheelAction(YHConfig::Shift);
+ else if (event->state() & Qt::ControlButton)
+ action = c->mouseWheelAction(YHConfig::Ctrl);
+ else if (event->state() & Qt::AltButton)
+ action = c->mouseWheelAction(YHConfig::Alt);
+ else
+ action = c->mouseWheelAction(YHConfig::None);
+
+ switch(action)
+ {
+ case (YHConfig::ChangeVolume):
+ napp->player()->setVolume(napp->player()->volume()+event->delta()/24);
+ break;
+ case (YHConfig::ChangeTrack):
+ if (event->delta() > 0)
+ napp->player()->forward(true);
+ else
+ napp->player()->back();
+ break;
+ default:
+ break;
+ }
+}
+
+#include "kitsystemtray.moc"
diff --git a/noatun/modules/systray/kitsystemtray.h b/noatun/modules/systray/kitsystemtray.h
new file mode 100644
index 00000000..2f640adb
--- /dev/null
+++ b/noatun/modules/systray/kitsystemtray.h
@@ -0,0 +1,54 @@
+// $Id$
+//
+// Kit
+//
+// Copyright (C) 1999 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifndef KITSYSTEMTRAY_H
+#define KITSYSTEMTRAY_H
+
+#include <ksystemtray.h>
+
+class KPopupMenu;
+class KMainWindow;
+class QPixmap;
+
+class KitSystemTray : public KSystemTray
+{
+Q_OBJECT
+
+public:
+ KitSystemTray(const QString &contextMenu, KMainWindow *parent, const char *name = 0);
+ void changeTitle(const QPixmap &, const QString &);
+protected:
+ virtual void showEvent(QShowEvent *);
+ virtual void mousePressEvent(QMouseEvent *);
+ virtual void dragEnterEvent(QDragEnterEvent *);
+ virtual void dropEvent(QDropEvent *);
+ virtual void wheelEvent(QWheelEvent *e);
+
+ KPopupMenu *menu;
+};
+
+#endif
diff --git a/noatun/modules/systray/noatunui.cpp b/noatun/modules/systray/noatunui.cpp
new file mode 100644
index 00000000..76f9af25
--- /dev/null
+++ b/noatun/modules/systray/noatunui.cpp
@@ -0,0 +1,9 @@
+#include "systray.h"
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new NoatunSystray();
+ }
+}
diff --git a/noatun/modules/systray/systray.cpp b/noatun/modules/systray/systray.cpp
new file mode 100644
index 00000000..c93080ca
--- /dev/null
+++ b/noatun/modules/systray/systray.cpp
@@ -0,0 +1,467 @@
+// systray.h
+//
+// Copyright (C) 2000 Neil Stevens <multivac@fcmail.com>
+// Copyright (C) 1999 Charles Samuels <charles@kde.org>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "systray.h"
+#include "kitsystemtray.h"
+#include "cmodule.h"
+#include "yhconfig.h"
+
+#include <noatun/app.h>
+#include <noatun/pref.h>
+#include <noatun/player.h>
+#include <noatun/stdaction.h>
+
+#include <kaction.h>
+#include <kconfig.h>
+#include <qfile.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kpassivepopup.h>
+#include <kpixmapeffect.h>
+#include <kstdaction.h>
+#include <qbitmap.h>
+#include <qhbox.h>
+#include <qpainter.h>
+#include <qpushbutton.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+
+#include <qimage.h>
+#include <kurl.h>
+#include <kio/netaccess.h>
+#include <kdebug.h>
+#include <kstandarddirs.h>
+
+#include <netwm.h>
+#include <kglobalsettings.h>
+
+// TODO: Maybe make this value configurable?
+const int COVER_MAXW = 128;
+const int COVER_MAXH = 128;
+#define BASEICON "noatun"
+
+// From JuK
+class PassivePopup : public KPassivePopup
+{
+public:
+ PassivePopup(QWidget *parent = 0, const char *name = 0) : KPassivePopup(parent, name) {}
+
+protected:
+ virtual void enterEvent(QEvent *)
+ {
+ setTimeout(3000000); // Make timeout damn near infinite
+ }
+
+ virtual void leaveEvent(QEvent *)
+ {
+ setTimeout(250); // Close quickly
+ }
+};
+
+
+//NoatunSystray *NoatunSystray::self = 0;
+
+
+NoatunSystray::NoatunSystray() : KMainWindow(0, "NoatunSystray"), Plugin(),
+ mTray(0), trayStatus(0), trayBase(0), mPassivePopup(0L)
+{
+ //self = this;
+ hide();
+
+ tmpCoverPath = locateLocal("tmp", "youngHickoryCover.png");
+
+ removeCover(); // make sure any old temp cover is gone
+
+ KStdAction::quit(napp, SLOT(quit()), actionCollection());
+ KStdAction::open(napp, SLOT(fileOpen()), actionCollection());
+ KStdAction::preferences(napp, SLOT(preferences()), actionCollection());
+ NoatunStdAction::back(actionCollection(), "back");
+ NoatunStdAction::stop(actionCollection(), "stop");
+ NoatunStdAction::playpause(actionCollection(), "play");
+ NoatunStdAction::forward(actionCollection(), "forward");
+ NoatunStdAction::playlist(actionCollection(), "show_playlist");
+ NoatunStdAction::loop(actionCollection(), "loop_style");
+ NoatunStdAction::effects(actionCollection(), "effects");
+ NoatunStdAction::equalizer(actionCollection(), "equalizer");
+
+ createGUI("systrayui.rc");
+
+ mTray = new KitSystemTray("tray", this);
+ mTray->show();
+
+ trayBase = renderIcon(BASEICON, QString::null);
+ trayStatus = renderIcon(BASEICON, "player_stop");
+
+ mTray->changeTitle(*trayBase, i18n("Noatun"));
+ showingTrayStatus = false;
+
+ mBlinkTimer = new QTimer(this);
+ connect(mBlinkTimer, SIGNAL(timeout()), this, SLOT(slotBlinkTimer()));
+
+ connect(napp->player(), SIGNAL(playing()), this, SLOT(slotPlayPause()));
+ connect(napp->player(), SIGNAL(paused()), this, SLOT(slotPlayPause()));
+ connect(napp->player(), SIGNAL(stopped()), this, SLOT(slotStopped()));
+ //napp->player()->handleButtons();
+}
+
+
+NoatunSystray::~NoatunSystray()
+{
+ //kdDebug(66666) << k_funcinfo << "Called." << endl;
+ removeCover();
+ delete trayBase;
+ delete trayStatus;
+ napp->showInterfaces();
+}
+
+
+void NoatunSystray::init()
+{
+ YHModule *cmod = new YHModule(this);
+ connect(cmod, SIGNAL(saved()), this, SLOT(slotLoadSettings()));
+ slotLoadSettings();
+}
+
+
+void NoatunSystray::slotLoadSettings()
+{
+ kdDebug(66666) << k_funcinfo << endl;
+
+ YHConfig *c = YHConfig::self();
+
+ if(c->stateIconDisplay() == YHConfig::FlashingIcon)
+ mBlinkTimer->start(1000);
+ else
+ mBlinkTimer->stop();
+ slotBlinkTimer();
+
+
+ if(c->tip())
+ QToolTip::add(mTray, tipText);
+ else
+ QToolTip::remove(mTray);
+
+ if (!c->passivePopupCovers())
+ removeCover();
+
+ if(c->passivePopup())
+ {
+ mPassivePopup = new PassivePopup(mTray, "NoatunPassivePopup");
+ }
+ else
+ {
+ delete mPassivePopup;
+ mPassivePopup = 0L;
+ }
+}
+
+
+void NoatunSystray::closeEvent(QCloseEvent*)
+{
+ //kdDebug(66666) << k_funcinfo << "Called." << endl;
+ disconnect(napp->player(), 0, 0, 0);
+ unload();
+}
+
+
+void NoatunSystray::slotPlayPause()
+{
+ QString status;
+
+ if(napp->player()->isPaused())
+ {
+ changeTray("player_pause");
+ status = i18n("Noatun - Paused");
+ }
+ else
+ {
+ changeTray("player_play");
+ status = i18n("Noatun - Playing");
+ }
+
+ const PlaylistItem item = napp->player()->current();
+ QString s;
+
+ if(!item.isProperty("title"))
+ {
+ // No metadata
+ s = QString("<nobr>%1</nobr>").arg(item.title());
+ }
+ else
+ {
+ s = QString("<h2><nobr>%1</nobr></h2>").arg(item.property("title"));
+
+ if(item.isProperty("author"))
+ s += QString("<nobr>%1</nobr><br>").arg(item.property("author"));
+
+ if(item.isProperty("album"))
+ {
+ if(item.isProperty("date"))
+ s += QString("<nobr>%1 (%2)</nobr><br>").arg(item.property("album")).arg(item.property("date"));
+ else
+ s += QString("<nobr>%1</nobr><br>").arg(item.property("album"));
+ }
+ }
+
+ // prepare cover image for display
+ if (YHConfig::self()->passivePopupCovers())
+ updateCover();
+
+ if(YHConfig::self()->passivePopupCovers() && QFile::exists(tmpCoverPath))
+ {
+ // QT always adds an empty line after the table so we add en empty line before the
+ // table to get equal spacing on top and bottom
+ setTipText(QString("<qt><br><table cellspacing=0 cellpadding=0><tr>" \
+ "<td align=center valign=center><h4><nobr>%1</nobr></h4>%2</td>" \
+ "<td valign=center><img src='%3'></td>" \
+ "</qt></tr></table>").arg(status).arg(s).arg(tmpCoverPath));
+ }
+ else
+ {
+ setTipText(QString("<qt><center><h4><nobr>%1</nobr></h4>%2</center></qt>").arg(status).arg(s));
+ }
+}
+
+
+void NoatunSystray::slotStopped()
+{
+ if(!napp->player()->current())
+ return;
+ changeTray("player_stop");
+ setTipText(QString("<qt><nobr><h4>%1</h4></nobr></qt>").arg(i18n("Noatun - Stopped")));
+}
+
+
+
+void NoatunSystray::changeTray(const QString &pm)
+{
+ delete trayStatus;
+ trayStatus = renderIcon(BASEICON, pm);
+ if(showingTrayStatus)
+ slotBlinkTimer();
+}
+
+
+void NoatunSystray::slotBlinkTimer()
+{
+ switch(YHConfig::self()->stateIconDisplay())
+ {
+ case (YHConfig::FlashingIcon):
+ showingTrayStatus ^= true;
+ break;
+ case (YHConfig::StaticIcon):
+ showingTrayStatus = true;
+ break;
+ case (YHConfig::NoIcon):
+ showingTrayStatus = false;
+ break;
+ }
+
+ if(showingTrayStatus)
+ mTray->setPixmap(*trayStatus);
+ else
+ mTray->setPixmap(*trayBase);
+}
+
+
+// taken from patched karamba xmmssensor
+// modified heavily to work in this place
+void NoatunSystray::updateCover()
+{
+ //kdDebug(66666) << k_funcinfo << endl;
+ QString dir = napp->player()->current().url().directory();
+ QString cover;
+
+ // TODO: Maybe make these filenames configurable?
+ if(QFile::exists(dir + "/folder.png"))
+ cover = dir + "/folder.png";
+ else if(QFile::exists(dir + "/.folder.png"))
+ cover = dir + "/.folder.png";
+ else if(QFile::exists(dir + "/cover.png"))
+ cover = dir + "/cover.png";
+ else if(QFile::exists(dir + "/cover.jpg"))
+ cover = dir + "/cover.jpg";
+ else if(QFile::exists(dir + "/cover.jpeg"))
+ cover = dir + "/cover.jpeg";
+ else // no cover
+ {
+ //kdDebug(66666) << k_funcinfo << "NO COVER" << endl;
+ removeCover();
+ return;
+ }
+
+ QString title = napp->player()->current().title();
+
+ QImage previmg;
+ previmg.load(tmpCoverPath);
+
+ if(previmg.text("Title") != title)
+ { //Verify song change to limit CPU usage
+ /*kdDebug(66666) << k_funcinfo << "Creating new temp cover for '" <<
+ cover << "'" << endl;*/
+
+ QImage src;
+ QImage tmpimg;
+
+ if(src.load(cover))
+ {
+ if(src.width() >= COVER_MAXW || src.height() >= COVER_MAXH)
+ tmpimg = src.scale(COVER_MAXW, COVER_MAXH, QImage::ScaleMin);
+ else
+ tmpimg = src;
+
+ tmpimg.setText("Title", 0, title); //add Title in the image text for cache usage
+ tmpimg.save(tmpCoverPath, "PNG", 0);
+ }
+ else
+ {
+ removeCover();
+ }
+ }
+}
+
+
+void NoatunSystray::removeCover()
+{
+ if(QFile::exists(tmpCoverPath))
+ KIO::NetAccess::del(KURL(tmpCoverPath), this);
+}
+
+
+void NoatunSystray::setTipText(const QString& text)
+{
+ if(text == tipText) // save the planet, save cpu cycles ;)
+ return;
+ tipText = text;
+
+ YHConfig *c = YHConfig::self();
+ if(c->passivePopup())
+ QTimer::singleShot(0, this, SLOT(showPassivePopup()));
+
+ if(c->tip())
+ QToolTip::add(mTray, tipText);
+}
+
+
+void NoatunSystray::showPassivePopup()
+{
+ if (!mPassivePopup)
+ {
+ kdDebug(66666) << k_funcinfo << "Called but no KPassivePopup created yet!" << endl;
+ return;
+ }
+
+ mPassivePopup->reparent(0L, QPoint(0,0));
+
+ if (YHConfig::self()->passivePopupButtons() && !napp->player()->isStopped())
+ {
+ QVBox *widget = mPassivePopup->standardView(QString::null, tipText, QPixmap());
+ QHBox *box = new QHBox(mPassivePopup, "popupbox");
+
+ box->setSpacing(8);
+
+ // Algorithm for determining popup location from kpassivepopup.cpp via JuK
+ NETWinInfo ni(qt_xdisplay(), mTray->winId(), qt_xrootwin(),
+ NET::WMIconGeometry | NET::WMKDESystemTrayWinFor);
+ NETRect frame, win;
+ ni.kdeGeometry(frame, win);
+
+ QRect bounds = KGlobalSettings::desktopGeometry(QPoint(win.pos.x, win.pos.y));
+
+ if(win.pos.x < bounds.center().x())
+ {
+ // Buttons to the left
+
+ QVBox *buttonBox = new QVBox(box);
+ buttonBox->setSpacing(3);
+
+ QPushButton *forwardButton = new QPushButton(action("forward")->iconSet(), 0, buttonBox, "popup_forward");
+ forwardButton->setFlat(true);
+ connect(forwardButton, SIGNAL(clicked()), action("forward"), SLOT(activate()));
+
+ QPushButton *backButton = new QPushButton(action("back")->iconSet(), 0, buttonBox, "popup_back");
+ backButton->setFlat(true);
+ connect(backButton, SIGNAL(clicked()), action("back"), SLOT(activate()));
+
+ QFrame *line = new QFrame(box);
+ line->setFrameShape(QFrame::VLine);
+
+ widget->reparent(box, QPoint(0, 0));
+ }
+ else
+ {
+ // Buttons to the right
+ widget->reparent(box, QPoint(0, 0));
+
+ QFrame *line = new QFrame(box);
+ line->setFrameShape(QFrame::VLine);
+
+ QVBox *buttonBox = new QVBox(box);
+ buttonBox->setSpacing(3);
+
+ QPushButton *forwardButton = new QPushButton(action("forward")->iconSet(), 0, buttonBox, "popup_forward");
+ forwardButton->setFlat(true);
+ connect(forwardButton, SIGNAL(clicked()), action("forward"), SLOT(activate()));
+
+ QPushButton *backButton = new QPushButton(action("back")->iconSet(), 0, buttonBox, "popup_back");
+ backButton->setFlat(true);
+ connect(backButton, SIGNAL(clicked()), action("back"), SLOT(activate()));
+ }
+ mPassivePopup->setView(box);
+ }
+ else
+ {
+ mPassivePopup->setView(QString::null, tipText);
+ }
+
+ mPassivePopup->setTimeout(YHConfig::self()->passivePopupTimeout()*1000);
+ mPassivePopup->show();
+}
+
+
+QPixmap *NoatunSystray::renderIcon(const QString& baseIcon, const QString &overlayIcon) const
+{
+ QPixmap *base = new QPixmap(KSystemTray::loadIcon(baseIcon));
+
+ if(!(overlayIcon.isNull())) // otherwise leave the base as-is
+ {
+ QPixmap overlay = KSystemTray::loadIcon(overlayIcon);
+ if(!overlay.isNull())
+ {
+ // draw the overlay on top of it
+ QPainter p(base);
+ p.drawPixmap(0, 0, overlay);
+ }
+ }
+ return base;
+}
+
+#include "systray.moc"
diff --git a/noatun/modules/systray/systray.h b/noatun/modules/systray/systray.h
new file mode 100644
index 00000000..f602c4e3
--- /dev/null
+++ b/noatun/modules/systray/systray.h
@@ -0,0 +1,80 @@
+// systray.h
+//
+// Copyright (C) 2000 Neil Stevens <multivac@fcmail.com>
+// Copyright (C) 1999 Charles Samuels <charles@kde.org>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifndef SYSTRAY_H
+#define SYSTRAY_H
+
+#include <noatun/plugin.h>
+#include <noatun/app.h>
+
+#include <qtimer.h>
+
+#include <kmainwindow.h>
+
+class KitSystemTray;
+class QTimer;
+class PassivePopup;
+
+class NoatunSystray : public KMainWindow, public Plugin
+{
+Q_OBJECT
+public:
+ NoatunSystray();
+ virtual ~NoatunSystray();
+ virtual void init();
+
+protected:
+ virtual void closeEvent(QCloseEvent *);
+
+public slots:
+ void slotPlayPause();
+ void slotStopped();
+
+private slots:
+ void changeTray(const QString &);
+ void slotLoadSettings();
+ void slotBlinkTimer();
+ void showPassivePopup();
+ QPixmap *renderIcon(const QString &, const QString &) const;
+
+private:
+ void setTipText(const QString&);
+ void updateCover();
+ void removeCover();
+
+private:
+ KitSystemTray *mTray;
+ QTimer *mBlinkTimer;
+ QPixmap *trayStatus;
+ QPixmap *trayBase;
+ PassivePopup *mPassivePopup;
+
+ bool showingTrayStatus;
+ QString tipText;
+ QString tmpCoverPath;
+};
+
+#endif
diff --git a/noatun/modules/systray/systray.plugin b/noatun/modules/systray/systray.plugin
new file mode 100644
index 00000000..24af57f5
--- /dev/null
+++ b/noatun/modules/systray/systray.plugin
@@ -0,0 +1,100 @@
+Filename=noatun_systray.la
+Author=Neil Stevens
+Site=http://noatun.kde.org/plugins/yh/
+Email=neil@qualityassistant.com
+Type=systray
+License=X11-like
+Name=Young Hickory
+Name[af]=Jong Hickory
+Name[ar]=شجرة الجوز الصغيرة
+Name[az]=Gənc Hickory
+Name[cs]=Mladý ořech
+Name[eo]=Juna hikorio
+Name[es]=Joven Nogal
+Name[et]=Noor hikkor
+Name[fa]=یانگ هیکوری
+Name[fi]=Nuori Hickory
+Name[fr]=Jeune Hickory
+Name[hi]=यंग हिकॉरी
+Name[is]=Ungi Hickory
+Name[it]=Giovane nocciolina
+Name[ko]=젊은 히코리
+Name[lt]=Jaunas Hickory
+Name[lv]=Jaunais Hickorijs
+Name[ne]=युवा हिक्कोरी
+Name[pl]=Tacka systemowa
+Name[ru]=Ветка гикори
+Name[sk]=Mladý Hickory
+Name[sl]=Mladi hikori
+Name[sv]=Ung valnöt
+Name[ta]=சின்ன ஒருவகை மரம்
+Name[tr]=Genç Hickory
+Name[ven]=Hickory mutuku
+Name[xh]=iHickory encinane
+Name[zh_CN]=小胡桃木
+Name[zh_HK]=小山胡桃
+Name[zh_TW]=小山胡桃
+Comment=A system tray interface
+Comment[af]='n stelsel laai koppelvlak
+Comment[ar]=واجهة للوحة النظام
+Comment[az]=Sistem rəf paneli
+Comment[bg]=Икона за управление от системния панел
+Comment[bn]=একটি সিস্টেম ট্রে ইন্টারফেস
+Comment[bs]=Interfejs za sistemski tray
+Comment[ca]=Una interfície de la safata del sistema
+Comment[cs]=Rozhraní systémové části panelu
+Comment[cy]=Rhyngwyneb hambwrdd cysawd
+Comment[da]=En statusfelt-grænseflade
+Comment[de]=Oberfläche für den Systembereich der Kontrollleiste
+Comment[el]=Μια διασύνδεση για το πλαίσιο συστήματος
+Comment[eo]=Interfaco por la dokejo
+Comment[es]=Una interfaz para la bandeja del sistema
+Comment[et]=Süsteemse doki liides
+Comment[eu]=Interfazea sistemaren bandejarentzat
+Comment[fa]=یک واسط سینی سیستم
+Comment[fi]=Järjestelmäikkunan rajapinta
+Comment[fr]=Une interface intégrée dans la boîte à miniatures
+Comment[gl]=Unha interface para a bandexa do sistema
+Comment[he]=ממשק מגש מערכת
+Comment[hi]=एक तंत्र तश्तरी इंटरफेस
+Comment[hr]=Sučelje za sustavsku ladicu
+Comment[hu]=A rendszertálca kezelői felülete
+Comment[id]=interface tray sistem
+Comment[is]=Aðgangur að kerfisbakka
+Comment[it]=Un'interfaccia per il vassoio di sistema
+Comment[ja]=システムトレイインターフェース
+Comment[kk]=Жүйелік сөре интерфейсі
+Comment[km]=ចំណុច​ប្រទាក់​ថាស​ប្រព័ន្ធ
+Comment[ko]=시스템 트레이 인터페이스
+Comment[lt]=Sistemos dėklo sąsaja
+Comment[lv]=Sistēmas teknes starpseja
+Comment[mk]=Интерфејс за системската лента
+Comment[ms]=Antaramuka dulang sistem
+Comment[mt]=Interfaċċja għat-tray tas-sistema
+Comment[nb]=Systemkurv grensesnitt
+Comment[nds]=Systeemafsnitt-Koppelsteed
+Comment[ne]=प्रणाली ट्रे इन्टरफेस
+Comment[nl]=Een systeemvakinterface
+Comment[nn]=Systemtrau-grensesnitt
+Comment[pa]=ਇੱਕ ਸਿਸਟਮ ਟਰੇ ਇੰਟਰਫੇਸ
+Comment[pl]=Interfejs tacki systemowej
+Comment[pt]=Uma interface para a bandeja do sistema
+Comment[pt_BR]=Uma interface para os ícones de sistema
+Comment[ro]=O interfaţă pentru tava de sistem
+Comment[ru]=Интерфейс панели системного лотка
+Comment[sk]=Rozhranie pre systémovú lištu
+Comment[sl]=Vmesnik za sistemsko vrstico v pultu
+Comment[sr]=Интерфејс за системску касету
+Comment[sr@Latn]=Interfejs za sistemsku kasetu
+Comment[sv]=Gränssnitt för aktivitetsfältet
+Comment[ta]=அமைப்பு தொகுதித் தட்டு இடைமுகம்
+Comment[tg]=Интерфейси сабади системавӣ
+Comment[th]=ส่วนติดต่อกับถาดระบบ
+Comment[tr]=Sistem çekmecesi arayüzü
+Comment[uk]=Інтерфейс системного лотка
+Comment[ven]=Thirei ya sisitemu
+Comment[xh]=Ujongano lwendlela yetreyi
+Comment[zh_CN]=一个系统托盘界面
+Comment[zh_HK]=系統匣介面
+Comment[zh_TW]=System Tray的介面
+Comment[zu]=Isistimu yetreyi loxhumano olubhekene
diff --git a/noatun/modules/systray/systrayui.rc b/noatun/modules/systray/systrayui.rc
new file mode 100644
index 00000000..2b50654b
--- /dev/null
+++ b/noatun/modules/systray/systrayui.rc
@@ -0,0 +1,20 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="noatunsystray" version="12">
+<Menu name="tray">
+ <Action name="file_open"/>
+ <Action name="show_playlist"/>
+ <Separator lineSeparator="true"/>
+ <Action name="back"/>
+ <Action name="stop"/>
+ <Action name="play"/>
+ <Action name="forward"/>
+ <Separator lineSeparator="true"/>
+ <Action name="loop_style"/>
+ <Action name="effects"/>
+ <Action name="equalizer"/>
+ <Separator lineSeparator="true"/>
+ <Action name="options_configure"/>
+ <Separator lineSeparator="true"/>
+ <Action name="file_quit"/>
+</Menu>
+</kpartgui>
diff --git a/noatun/modules/systray/yhconfig.kcfg b/noatun/modules/systray/yhconfig.kcfg
new file mode 100644
index 00000000..9b017aec
--- /dev/null
+++ b/noatun/modules/systray/yhconfig.kcfg
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+ <kcfgfile name="noatunrc"/>
+ <group name="Young Hickory">
+
+ <entry key="State Icon Display" type="Enum">
+ <label>State Icon Display</label>
+ <choices>
+ <choice name="Animation"/>
+ <choice name="FlashingIcon"/>
+ <choice name="StaticIcon"/>
+ <choice name="NoIcon"/>
+ </choices>
+ <default>FlashingIcon</default>
+ </entry>
+
+ <entry name="icon" type="Int">
+ <label>Icon</label>
+ <default>1</default>
+ </entry>
+
+ <entry name="tip" type="Bool">
+ <label>Show a tooltip for the current track</label>
+ <default>true</default>
+ </entry>
+
+ <entry name="passivePopup" type="Bool">
+ <label>Announce tracks with a popup window</label>
+ <default>true</default>
+ </entry>
+
+ <entry name="passivePopupTimeout" type="Int">
+ <label>Display popup window for x seconds</label>
+ <default>5</default>
+ </entry>
+
+ <entry name="passivePopupCovers" type="Bool">
+ <label>Show covers in popup window and tooltip</label>
+ <default>true</default>
+ </entry>
+
+ <entry name="passivePopupButtons" type="Bool">
+ <label>Show buttons in popup window</label>
+ <default>true</default>
+ </entry>
+
+
+ <entry key="MiddleMouse Action" type="Enum">
+ <label>Mode</label>
+ <choices>
+ <choice name="PlayPause"/>
+ <choice name="HideShowPlaylist"/>
+ </choices>
+ <default>HideShowPlaylist</default>
+ </entry>
+
+ <entry name="MouseWheelAction$(Modifier)" type="Enum" key="mousewheelaction_$(Modifier)">
+ <parameter name="Modifier" type="Enum">
+ <values>
+ <value>None</value>
+ <value>Shift</value>
+ <value>Alt</value>
+ <value>Ctrl</value>
+ </values>
+ </parameter>
+ <choices>
+ <choice name="Nothing"/>
+ <choice name="ChangeVolume"/>
+ <choice name="ChangeTrack"/>
+ </choices>
+ <default param="None">ChangeVolume</default>
+ <default param="Shift">Nothing</default>
+ <default param="Alt">Nothing</default>
+ <default param="Ctrl">ChangeTrack</default>
+ </entry>
+
+ </group>
+</kcfg>
diff --git a/noatun/modules/systray/yhconfig.kcfgc b/noatun/modules/systray/yhconfig.kcfgc
new file mode 100644
index 00000000..5b4f99e4
--- /dev/null
+++ b/noatun/modules/systray/yhconfig.kcfgc
@@ -0,0 +1,7 @@
+# Code generation options for kconfig_compiler
+File=yhconfig.kcfg
+ClassName=YHConfig
+Singleton=true
+Mutators=true
+MemberVariables=private
+GlobalEnums=true
diff --git a/noatun/modules/systray/yhconfigwidget.ui b/noatun/modules/systray/yhconfigwidget.ui
new file mode 100644
index 00000000..48267c28
--- /dev/null
+++ b/noatun/modules/systray/yhconfigwidget.ui
@@ -0,0 +1,333 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>YHConfigWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>YHConfigWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>371</width>
+ <height>379</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>tabWidget2</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;General</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>chkUseTooltip</cstring>
+ </property>
+ <property name="text">
+ <string>Show a &amp;tooltip for the current track</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>chkUseCovers</cstring>
+ </property>
+ <property name="text">
+ <string>Show &amp;covers in popup window and tooltip</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Popup Window</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>lblPopupTimeout</cstring>
+ </property>
+ <property name="text">
+ <string>Display popup window t&amp;ime:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>spinPopupTimeout</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>chkPopupButtons</cstring>
+ </property>
+ <property name="text">
+ <string>Show &amp;buttons in popup window</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="1" column="1">
+ <property name="name">
+ <cstring>spinPopupTimeout</cstring>
+ </property>
+ <property name="suffix">
+ <string>s</string>
+ <comment>Seconds</comment>
+ </property>
+ <property name="maxValue">
+ <number>600</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>5</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>chkUsePopup</cstring>
+ </property>
+ <property name="text">
+ <string>Announce tracks with a &amp;popup window</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>State Icon Display</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton" row="0" column="0">
+ <property name="name">
+ <cstring>rbStateAnim</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Animated</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="0" column="1">
+ <property name="name">
+ <cstring>rbStateFlashing</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Flashing</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="1" column="0">
+ <property name="name">
+ <cstring>rbStateStatic</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Static</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="1" column="1">
+ <property name="name">
+ <cstring>rbStateNone</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;None</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>81</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Ad&amp;vanced</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>grpMiddleMouse</cstring>
+ </property>
+ <property name="title">
+ <string>Middle Mouse Button Action</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>rbHideShowPlaylist</cstring>
+ </property>
+ <property name="text">
+ <string>Hide / Show play&amp;list</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>rbPlayPause</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Play / Pause</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>grpMwheel</cstring>
+ </property>
+ <property name="title">
+ <string>Mouse &amp;Wheel</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblModifier</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Keyboard modifier:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>cmbModifier</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>cmbModifier</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblAction</cstring>
+ </property>
+ <property name="text">
+ <string>Action:</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>rbActNothing</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Nothing</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>rbActVolume</cstring>
+ </property>
+ <property name="text">
+ <string>Change v&amp;olume</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>rbActTrack</cstring>
+ </property>
+ <property name="text">
+ <string>Switch &amp;track</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </widget>
+ </vbox>
+</widget>
+<tabstops>
+ <tabstop>tabWidget2</tabstop>
+ <tabstop>chkUseTooltip</tabstop>
+ <tabstop>chkUseCovers</tabstop>
+ <tabstop>chkUsePopup</tabstop>
+ <tabstop>spinPopupTimeout</tabstop>
+ <tabstop>chkPopupButtons</tabstop>
+ <tabstop>rbStateAnim</tabstop>
+ <tabstop>rbStateFlashing</tabstop>
+ <tabstop>rbStateStatic</tabstop>
+ <tabstop>rbStateNone</tabstop>
+ <tabstop>rbHideShowPlaylist</tabstop>
+ <tabstop>rbPlayPause</tabstop>
+ <tabstop>cmbModifier</tabstop>
+ <tabstop>rbActNothing</tabstop>
+ <tabstop>rbActVolume</tabstop>
+ <tabstop>rbActTrack</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/noatun/modules/voiceprint/Makefile.am b/noatun/modules/voiceprint/Makefile.am
new file mode 100644
index 00000000..22c3f65c
--- /dev/null
+++ b/noatun/modules/voiceprint/Makefile.am
@@ -0,0 +1,14 @@
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_voiceprint.la
+
+noatun_voiceprint_la_SOURCES = voiceprint.cpp prefs.cpp
+
+noatun_voiceprint_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_voiceprint_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la -lm
+
+noatun_voiceprint_la_METASOURCES = AUTO
+
+noinst_HEADERS = voiceprint.h prefs.h
+
+noatun_modules_voiceprint_DATA = voiceprint.plugin
+noatun_modules_voiceprintdir = $(kde_datadir)/noatun
diff --git a/noatun/modules/voiceprint/prefs.cpp b/noatun/modules/voiceprint/prefs.cpp
new file mode 100644
index 00000000..48998680
--- /dev/null
+++ b/noatun/modules/voiceprint/prefs.cpp
@@ -0,0 +1,67 @@
+#include "prefs.h"
+#include "voiceprint.h"
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <kcolorbutton.h>
+#include <kconfig.h>
+
+Prefs::Prefs(QObject* parent)
+ : CModule(i18n("Voiceprint"), i18n("Options for the Voiceprint Visualization"), "xapp", parent)
+{
+ QVBoxLayout *king=new QVBoxLayout(this);
+ QHBoxLayout *minor;
+
+ QLabel *label;
+ mForeground=new KColorButton(this);
+ label=new QLabel(mForeground, i18n("&Foreground color:"), this);
+ minor=new QHBoxLayout(king);
+ minor->addWidget(label);
+ minor->addWidget(mForeground);
+
+ mBackground=new KColorButton(this);
+ label=new QLabel(mBackground, i18n("&Background color:"), this);
+ minor=new QHBoxLayout(king);
+ minor->addWidget(label);
+ minor->addWidget(mBackground);
+
+ mLine=new KColorButton(this);
+ label=new QLabel(mForeground, i18n("&Sweep color:"), this);
+ minor=new QHBoxLayout(king);
+ minor->addWidget(label);
+ minor->addWidget(mLine);
+
+ king->addStretch();
+}
+
+void Prefs::reopen()
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup("VoicePrint");
+ QColor black(0, 0, 0);
+ QColor blue(0, 0, 222);
+ mBackground->setColor(config->readColorEntry("Background", &black));
+ mForeground->setColor(config->readColorEntry("Foreground", &blue));
+ mLine->setColor(config->readColorEntry("Line", &black));
+}
+
+void Prefs::save()
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup("VoicePrint");
+ config->writeEntry("Background", mBackground->color());
+ config->writeEntry("Foreground", mForeground->color());
+ config->writeEntry("Line", mLine->color());
+
+ config->sync();
+
+ VoicePrint *l=VoicePrint::voicePrint;
+ if (l)
+ l->setColors(mBackground->color(), mForeground->color(), mLine->color());
+
+}
+
+#include "prefs.moc"
+
diff --git a/noatun/modules/voiceprint/prefs.h b/noatun/modules/voiceprint/prefs.h
new file mode 100644
index 00000000..6541b4de
--- /dev/null
+++ b/noatun/modules/voiceprint/prefs.h
@@ -0,0 +1,22 @@
+#ifndef PREFS_H
+#define PREFS_H
+
+#include <qwidget.h>
+#include <noatun/pref.h>
+
+class KColorButton;
+
+class Prefs : public CModule
+{
+Q_OBJECT
+public:
+ Prefs(QObject* parent);
+ virtual void save();
+ virtual void reopen();
+
+private:
+ KColorButton *mForeground, *mBackground, *mLine;
+};
+
+#endif
+
diff --git a/noatun/modules/voiceprint/voiceprint.cpp b/noatun/modules/voiceprint/voiceprint.cpp
new file mode 100644
index 00000000..4f6c7aea
--- /dev/null
+++ b/noatun/modules/voiceprint/voiceprint.cpp
@@ -0,0 +1,126 @@
+#include "voiceprint.h"
+#include <noatun/player.h>
+#include <noatun/app.h>
+#include <math.h>
+#include <qpainter.h>
+#include "prefs.h"
+#include <klocale.h>
+#include <stdio.h>
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new VoicePrint();
+ }
+}
+
+VoicePrint *VoicePrint::voicePrint=0;
+
+VoicePrint::VoicePrint() : QWidget(0,0,WRepaintNoErase), MonoFFTScope(50), Plugin()
+{
+ voicePrint=this;
+ mOffset=0;
+ mSegmentWidth=2;
+ setCaption(i18n("Voiceprint"));
+ resize(320, 240);
+ MonoFFTScope::start();
+ show();
+ setMaximumHeight(1024);
+}
+
+VoicePrint::~VoicePrint()
+{
+}
+
+void VoicePrint::init()
+{
+ Prefs *p=new Prefs(this);
+ p->reopen();
+ p->save();
+ resizeEvent(0);
+}
+
+void VoicePrint::setColors(const QColor &bg, const QColor &fg, const QColor &l)
+{
+ mProgress=l;
+ mLowColor=bg.rgb();
+ mHighColor=fg.rgb();
+ setBackgroundColor(mLowColor);
+}
+
+void VoicePrint::closeEvent(QCloseEvent *)
+{
+ unload();
+}
+
+void VoicePrint::resizeEvent(QResizeEvent *)
+{
+ mOffset=0;
+ mBuffer.resize(size());
+ QPainter paint(&mBuffer);
+ paint.fillRect(QRect(0,0, QWidget::width(), height()), QColor(mLowColor));
+ setBands(magic(height()/mSegmentWidth));
+}
+
+#define COLOR(color, bgcolor, fgcolor, foctet) \
+ (int)( color(bgcolor) + (foctet) * (color(fgcolor) - color(bgcolor)) )
+
+inline static QRgb averageByIntensity(QRgb bgcolor, QRgb fgcolor, int octet)
+{
+ float foctet = octet / 255.0;
+
+ return qRgb(COLOR(qRed, bgcolor, fgcolor, foctet),
+ COLOR(qGreen, bgcolor, fgcolor, foctet),
+ COLOR(qBlue, bgcolor, fgcolor, foctet)
+ );
+}
+
+#undef COLOR
+
+void VoicePrint::paintEvent(QPaintEvent *e)
+{
+ bitBlt(this, e->rect().topLeft(), &mBuffer, e->rect(), Qt::CopyROP);
+}
+
+void VoicePrint::scopeEvent(float *data, int bands)
+{
+ // save cpu
+ if(isHidden()) return;
+
+ QPainter paint(&mBuffer);
+ // each square has a width of mSegmentWidth
+ float brightness = float(bands * mSegmentWidth);
+ for (int i=0; i<bands ; i++)
+ {
+ float b=data[bands-i-1]+1.0;
+ // the more bands there are, the dimmer each becomes
+ b=log10(b)/log(2) * 16 * brightness;
+ int band=int(b);
+ if (band>255) band=255;
+ else if (band<0) band=0;
+
+ QColor area(averageByIntensity(mLowColor, mHighColor, band));
+
+ int bandTop=i*height()/bands, bandBottom=(i+1)*height()/bands;
+ paint.fillRect(mOffset, bandTop, mSegmentWidth,bandBottom-bandTop,area);
+ }
+
+ int newOffset = mOffset+mSegmentWidth;
+ if (newOffset>QWidget::width()) newOffset=0;
+ paint.fillRect(newOffset, 0, mSegmentWidth, height(), mProgress);
+
+ // redraw changes with the minimum amount of work
+ if(newOffset != 0)
+ {
+ repaint(mOffset,0,mSegmentWidth*2,height(),false);
+ }
+ else
+ {
+ repaint(mOffset,0,mSegmentWidth,height(),false);
+ repaint(newOffset,0,mSegmentWidth,height(),false);
+ }
+ mOffset = newOffset;
+}
+
+#include "voiceprint.moc"
diff --git a/noatun/modules/voiceprint/voiceprint.h b/noatun/modules/voiceprint/voiceprint.h
new file mode 100644
index 00000000..ab5af69d
--- /dev/null
+++ b/noatun/modules/voiceprint/voiceprint.h
@@ -0,0 +1,33 @@
+#ifndef VOICEPRINT_H
+#define VOICEPRINT_H
+
+#include <noatun/plugin.h>
+
+class VoicePrint : public QWidget, public MonoFFTScope, public Plugin
+{
+Q_OBJECT
+
+public:
+ VoicePrint();
+ virtual ~VoicePrint();
+
+ void setColors(const QColor &bg, const QColor &fg, const QColor &l);
+ void init();
+
+protected:
+ virtual void closeEvent(QCloseEvent *);
+ virtual void scopeEvent(float *data, int bands);
+ virtual void resizeEvent(QResizeEvent *);
+ virtual void paintEvent(QPaintEvent *);
+
+public:
+ static VoicePrint* voicePrint;
+
+private:
+ QColor mProgress;
+ QPixmap mBuffer;
+ QRgb mLowColor, mHighColor;
+ int mOffset, mSegmentWidth;
+};
+#endif
+
diff --git a/noatun/modules/voiceprint/voiceprint.plugin b/noatun/modules/voiceprint/voiceprint.plugin
new file mode 100644
index 00000000..b7bca4f4
--- /dev/null
+++ b/noatun/modules/voiceprint/voiceprint.plugin
@@ -0,0 +1,92 @@
+Filename=noatun_voiceprint.la
+Author=Charles Samuels
+Site=http://noatun.derkarl.org/
+Email=charles@kde.org
+Type=visualization
+License=Artistic
+Name=Voiceprint
+Name[af]=Stem afdruk
+Name[az]=Səs gözləyici
+Name[ca]=Empremta de veu
+Name[cy]=Argraff Lais
+Name[el]=Φωνητικό αποτύπωμα
+Name[eo]=Voĉvidigilo
+Name[es]=Huella de voz
+Name[et]=Visuaalne signaal
+Name[fi]=Äänikuva
+Name[ga]=Guthlorg
+Name[hi]=वाइसप्रिंट
+Name[hr]=Otisak glasa
+Name[hu]=Hanglenyomat
+Name[is]=Raddrit
+Name[it]=Impronta vocale
+Name[ja]=ボイスプリント
+Name[km]=បង្ហាញ​សំឡេង
+Name[ko]=성문
+Name[lt]=Balso antspaudas
+Name[lv]=Balssdruka
+Name[mt]=Stampavuċi
+Name[ne]=आवाज मुद्रण
+Name[nl]=Stemafdruk
+Name[pl]=Zapis głosu
+Name[pt]=Impressão Vocal
+Name[ro]=Amprentă vocală
+Name[sl]=Galsovni zapis
+Name[sv]=Röstavtryck
+Name[ta]=குரல் அச்சு
+Name[tr]=Ses Gözlemcisi
+Name[ven]=U phirintha ha Mukosi
+Name[xh]=Ushicilelo lelizwi
+Name[zh_CN]=声波纹
+Name[zh_HK]=聲波紋
+Name[zh_TW]=聲波紋
+Name[zu]=Ushicilelo lelizwi
+Comment=A voiceprint visualizer
+Comment[bg]=Визуализатор Voiceprint
+Comment[bs]=Vizualizacija sa ispisom glasa
+Comment[ca]=Un visualitzador d'empremta de veu
+Comment[cs]=Vizualizátor hlasu
+Comment[cy]=Gweledydd argraff lais
+Comment[da]=En voiceprint-visualisering
+Comment[de]=Klangvisualisierung
+Comment[el]=Μια αναπαράσταση αποτυπώματος φωνής
+Comment[en_GB]=A voiceprint visualiser
+Comment[eo]=Voĉo-vidigilo
+Comment[es]=Un visualizador de huellas de voz
+Comment[et]=Signaali visualiseerimise plugin
+Comment[eu]=Voiceprint ikustailea
+Comment[fa]=یک تجسم‌کننده voiceprint
+Comment[fi]=Äänikuvavisualisoija
+Comment[fr]=Un afficheur Voiceprint
+Comment[gl]=Visualizador voiceprint
+Comment[he]=ממחיש טביעות קול
+Comment[hu]=Hanglenyomat-analizáló
+Comment[is]=Raddritsskjár
+Comment[it]=Visualizzatore di impronta vocale
+Comment[ja]=声紋の視覚効果
+Comment[kk]=Дыбыс таңба бейнекөрінісі
+Comment[km]=ម៉ាកែត​សម្រាប់​បង្ហាញ​សំឡេង
+Comment[ko]=성문 비주얼라이저
+Comment[lt]=Balso antspaudo vaizdavimo priemonė
+Comment[nb]=Voiceprint-fremvisar
+Comment[nds]=Stimmafdruck-Filmmaker
+Comment[ne]=आवाज मुद्रण भिजुलाइजर
+Comment[nl]=Stemafdruk visualisatie
+Comment[nn]=Voiceprint-framvisar
+Comment[pl]=Wizualizacja śladu głosu
+Comment[pt]=Um visualizador do comportamento vocal
+Comment[pt_BR]=Um visualizador voiceprint
+Comment[ro]=Vizualizor de amprentă vocală
+Comment[ru]=визуализация voiceprint
+Comment[sk]=Vizualizátor voiceprint
+Comment[sl]=Predstavitelj glasovnega zapisa
+Comment[sr]=Визуализатор гласовног отиска
+Comment[sr@Latn]=Vizualizator glasovnog otiska
+Comment[sv]=En röstavtrycksvisare
+Comment[ta]=குரல் அச்சு படக்காட்சி
+Comment[th]=โปรแกรมสร้างวิฌวลไลเซอร์ voiceprint
+Comment[tr]=Ses Dökümü İzleme Programı
+Comment[uk]=Візуаліатор voiseprint
+Comment[zh_CN]=声波纹视觉效果
+Comment[zh_HK]=聲波紋顯示程式
+Comment[zh_TW]=聲波紋顯示程式
diff --git a/noatun/modules/winskin/Makefile.am b/noatun/modules/winskin/Makefile.am
new file mode 100644
index 00000000..5c0423e6
--- /dev/null
+++ b/noatun/modules/winskin/Makefile.am
@@ -0,0 +1,51 @@
+SUBDIRS = vis skins mimetypes
+
+noatun_modules_winskin_DATA = winskin.plugin
+noatun_modules_winskindir = $(kde_datadir)/noatun
+
+INCLUDES = -I$(top_srcdir)/noatun/library \
+ -I$(top_builddir)/noatun/library \
+ -I$(kde_includes)/arts \
+ $(all_includes)
+
+kde_module_LTLIBRARIES = noatun_winskin.la
+
+noatun_winskin_la_SOURCES = fileInfo.cpp \
+ guiSpectrumAnalyser.cpp \
+ plugin.cpp \
+ waBalanceSlider.cpp \
+ waButton.cpp \
+ waClutterbar.cpp \
+ waColor.cpp \
+ waDigit.cpp \
+ waInfo.cpp \
+ waIndicator.cpp \
+ waJumpSlider.cpp \
+ waLabel.cpp \
+ waMain.cpp \
+ waRegion.cpp \
+ waSkin.cpp \
+ waSkinModel.cpp \
+ waSlider.cpp \
+ waStatus.cpp \
+ waTitleBar.cpp \
+ waVolumeSlider.cpp \
+ waWidget.cpp \
+ winSkinConfig.cpp \
+ winSkinVis.cpp \
+ waSkinManager.cpp \
+ waSkinManager.skel
+
+noatun_winskin_la_LDFLAGS = $(all_libraries) \
+ -module -avoid-version -no-undefined
+
+noatun_winskin_la_LIBADD = $(top_builddir)/noatun/library/libnoatun.la \
+ $(top_builddir)/noatun/modules/winskin/vis/libwinskinvis.la
+
+noatun_winskin_la_METASOURCES = AUTO
+
+
+
+waSkin.lo: ../../library/noatunarts/noatunarts.h vis/winskinvis.h
+winSkinVis.lo: ../../library/noatunarts/noatunarts.h vis/winskinvis.h
+guiSpectrumAnalyser.lo: ../../library/noatunarts/noatunarts.h vis/winskinvis.h
diff --git a/noatun/modules/winskin/fileInfo.cpp b/noatun/modules/winskin/fileInfo.cpp
new file mode 100644
index 00000000..69f93215
--- /dev/null
+++ b/noatun/modules/winskin/fileInfo.cpp
@@ -0,0 +1,50 @@
+#include <noatun/app.h>
+#include <noatun/playlist.h>
+
+#include <qstring.h>
+#include <kfilemetainfo.h>
+
+#include "fileInfo.h"
+
+fileInfo::fileInfo(const PlaylistItem &item)
+{
+ QString prop;
+
+ prop = item.property("bitrate");
+ if (prop.isNull())
+ _bps = 0;
+ else
+ _bps = prop.toInt();
+
+ prop = item.property("samplerate");
+ if (prop.isNull())
+ _KHz = 44100;
+ else
+ _KHz = prop.toInt();
+
+ prop = item.property("channels");
+ if (prop.isNull())
+ _channelCount = 2;
+ else
+ _channelCount = prop.toInt();
+}
+
+fileInfo::~fileInfo()
+{
+}
+
+unsigned int fileInfo::bps()
+{
+ return _bps;
+}
+
+unsigned int fileInfo::KHz()
+{
+ return _KHz;
+}
+
+unsigned int fileInfo::channelCount()
+{
+ return _channelCount;
+}
+
diff --git a/noatun/modules/winskin/fileInfo.h b/noatun/modules/winskin/fileInfo.h
new file mode 100644
index 00000000..203af087
--- /dev/null
+++ b/noatun/modules/winskin/fileInfo.h
@@ -0,0 +1,21 @@
+#ifndef _FILEINFO_H
+#define _FILEINFO_H
+
+#include <noatun/playlist.h>
+
+class fileInfo {
+ public:
+ fileInfo(const PlaylistItem &);
+ ~fileInfo();
+
+ unsigned int bps();
+ unsigned int KHz();
+ unsigned int channelCount();
+
+ private:
+ int _KHz;
+ int _bps;
+ int _channelCount;
+};
+
+#endif
diff --git a/noatun/modules/winskin/guiSpectrumAnalyser.cpp b/noatun/modules/winskin/guiSpectrumAnalyser.cpp
new file mode 100644
index 00000000..d015e5da
--- /dev/null
+++ b/noatun/modules/winskin/guiSpectrumAnalyser.cpp
@@ -0,0 +1,224 @@
+/*
+ winamp visualisation plugin.
+ Copyright (C) 2001 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include <klocale.h>
+#include <qcolor.h>
+#include <qpopupmenu.h>
+#include <qpainter.h>
+#include <kconfig.h>
+
+#include "waColor.h"
+#include "waSkinModel.h"
+
+#include "guiSpectrumAnalyser.h"
+#include "vis/winskinvis.h"
+
+#define __BANDS 75
+
+GuiSpectrumAnalyser::GuiSpectrumAnalyser()
+ : WaWidget(_WA_MAPPING_ANALYSER)
+{
+ connect(WaSkinModel::instance(), SIGNAL(skinChanged()), this, SLOT(pixmapChange()));
+
+ contextMenu = new QPopupMenu(this);
+ visualizationMenu = new QPopupMenu();
+ analyserMenu = new QPopupMenu();
+
+ contextMenu->insertItem(i18n("Visualization Mode"), visualizationMenu);
+ contextMenu->insertItem(i18n("Analyzer Mode"), analyserMenu);
+
+ visualizationMenu->insertItem(i18n("Analyzer"), (int)MODE_ANALYSER);
+ visualizationMenu->insertItem(i18n("Disabled"), (int)MODE_DISABLED);
+ visualizationMenu->setCheckable(true);
+ connect(visualizationMenu, SIGNAL(activated(int)), this, SLOT(setVisualizationMode(int)));
+
+ analyserMenu->insertItem(i18n("Normal"), (int)MODE_NORMAL);
+ analyserMenu->insertItem(i18n("Fire"), (int)MODE_FIRE);
+ analyserMenu->insertItem(i18n("Vertical Lines"), (int)MODE_VERTICAL_LINES);
+ analyserMenu->setCheckable(true);
+ connect(analyserMenu, SIGNAL(activated(int)), this, SLOT(setAnalyserMode(int)));
+
+ analyserCache = NULL;
+ winSkinVis = NULL;
+
+ KConfig *config = KGlobal::config();
+ config->setGroup("Winskin");
+
+ setVisualizationMode(config->readNumEntry("visualizationMode", MODE_ANALYSER));
+ setAnalyserMode(config->readNumEntry("analyserMode", MODE_NORMAL));
+}
+
+
+GuiSpectrumAnalyser::~GuiSpectrumAnalyser()
+{
+ KConfig *config = KGlobal::config();
+ config->setGroup("Winskin");
+
+ config->writeEntry("visualizationMode", visualization_mode);
+ config->writeEntry("analyserMode", analyser_mode);
+
+ delete analyserCache;
+}
+
+void GuiSpectrumAnalyser::mousePressEvent ( QMouseEvent *e )
+{
+ if (e->button() == LeftButton) {
+ if (visualization_mode == MODE_DISABLED)
+ setVisualizationMode(MODE_ANALYSER);
+ else
+ setVisualizationMode(MODE_DISABLED);
+ }
+ else if (e->button() == RightButton) {
+ contextMenu->popup(mapToGlobal(QPoint(e->x(), e->y())));
+ }
+}
+
+void GuiSpectrumAnalyser::setAnalyserMode(int mode)
+{
+ analyser_mode = mode;
+
+ analyserMenu->setItemChecked(MODE_NORMAL, (mode == MODE_NORMAL));
+ analyserMenu->setItemChecked(MODE_FIRE, (mode == MODE_FIRE));
+ analyserMenu->setItemChecked(MODE_VERTICAL_LINES, (mode == MODE_VERTICAL_LINES));
+
+ delete analyserCache;
+ analyserCache = NULL;
+}
+
+void GuiSpectrumAnalyser::pauseVisualization()
+{
+ hide();
+}
+
+void GuiSpectrumAnalyser::resumeVisualization()
+{
+ show();
+}
+
+void GuiSpectrumAnalyser::updatePeaks()
+{
+ if ((visualization_mode == MODE_DISABLED) || (!isVisible()))
+ return;
+
+ float* currentPeaks = winSkinVis->currentPeaks();
+
+ if (!analyserCache)
+ freshenAnalyserCache();
+
+ for (int x = 0;x < __BANDS;x++) {
+ int amp = int(currentPeaks[x]);
+
+ if (amp < 0)
+ amp = 0;
+ else if (amp > 16)
+ amp = 16;
+
+ bitBlt(this, x, 0, analyserCache, (amp * 2) + (x % 2), 0, 1, 16);
+ }
+}
+
+void GuiSpectrumAnalyser::setVisualizationMode(int mode)
+{
+ visualization_mode = mode;
+
+ visualizationMenu->setItemChecked(MODE_ANALYSER, (mode == MODE_ANALYSER));
+ visualizationMenu->setItemChecked(MODE_DISABLED, (mode == MODE_DISABLED));
+
+ if (mode == MODE_ANALYSER)
+ {
+ if (!winSkinVis)
+ {
+ winSkinVis=new WinSkinVis(this,"WinSkinVis");
+ connect(winSkinVis,SIGNAL(doRepaint()),this,SLOT(updatePeaks()));
+ }
+ }
+ else
+ {
+ delete winSkinVis;
+ winSkinVis = NULL;
+ }
+
+ update();
+}
+
+
+void GuiSpectrumAnalyser::freshenAnalyserCache()
+{
+ // We need a color scheme
+ if (!colorScheme)
+ return;
+
+ // The analyser cache is a 34x16 pixmap containing all the bits needed
+ // to quickly draw the spectrum analyser
+ analyserCache = new QPixmap(34, 16);
+ QPainter p(analyserCache);
+
+ for (unsigned int x = 0;x < 17;x++) {
+ if (x != 16) {
+ p.setPen(QPen(colorScheme->skinColors[INDEX_BACKGROUND_COLOR]));
+ p.drawLine(x * 2, 0, x * 2, 16 - x - 1);
+ }
+
+ for (unsigned int y = 0; y < (16 - x);y++) {
+ if (y % 2)
+ p.setPen(QPen(colorScheme->skinColors[INDEX_GRID_COLOR]));
+ else
+ p.setPen(QPen(colorScheme->skinColors[INDEX_BACKGROUND_COLOR]));
+
+ p.drawPoint((x * 2) + 1, y);
+ }
+
+ if (!x)
+ continue;
+
+ switch (analyser_mode) {
+ case MODE_FIRE:
+ for (unsigned int y = (16 - x); y < 16; y++) {
+ p.setPen(QPen(colorScheme->skinColors[INDEX_SPEC_BASE + (y - (16 - x))]));
+ p.drawPoint((x * 2), y);
+ p.drawPoint((x * 2) + 1, y);
+ }
+ break;
+ case MODE_VERTICAL_LINES:
+ p.setPen(QPen(colorScheme->skinColors[INDEX_SPEC_BASE + (16 - x)]));
+ p.drawLine((x * 2), (15 - x), (x * 2), 15);
+ p.drawLine((x * 2) + 1, (15 - x), (x * 2) + 1, 15);
+ break;
+ case MODE_NORMAL:
+ // Fall through
+ default:
+ for (unsigned int y = (16 - x); y < 16; y++) {
+ p.setPen(QPen(colorScheme->skinColors[INDEX_SPEC_BASE + y]));
+ p.drawPoint((x * 2), y);
+ p.drawPoint((x * 2) + 1, y);
+ }
+ break;
+ }
+ }
+}
+
+void GuiSpectrumAnalyser::paintEvent (QPaintEvent *)
+{
+ if (visualization_mode == MODE_DISABLED)
+ paintBackground();
+}
+
+void GuiSpectrumAnalyser::pixmapChange()
+{
+ delete analyserCache;
+ analyserCache = NULL;
+}
+
+
+#include "guiSpectrumAnalyser.moc"
+
diff --git a/noatun/modules/winskin/guiSpectrumAnalyser.h b/noatun/modules/winskin/guiSpectrumAnalyser.h
new file mode 100644
index 00000000..ecef8d37
--- /dev/null
+++ b/noatun/modules/winskin/guiSpectrumAnalyser.h
@@ -0,0 +1,66 @@
+/*
+ a GUI for a spectrum analyser
+ Copyright (C) 1998 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef _GUISPECTRUMANALYSER_H
+#define _GUISPECTRUMANALYSER_H
+
+#include <math.h>
+
+#include "winSkinVis.h"
+#include "waWidget.h"
+#include "waColor.h"
+
+
+#define MAX_MODE 1
+
+enum visualizationMode {MODE_DISABLED = 0, MODE_ANALYSER = 1};
+enum analyserMode {MODE_NORMAL = 0, MODE_FIRE = 1, MODE_VERTICAL_LINES = 2};
+
+class GuiSpectrumAnalyser : public WaWidget {
+ Q_OBJECT
+
+ public:
+ GuiSpectrumAnalyser();
+ ~GuiSpectrumAnalyser();
+
+ void mousePressEvent(QMouseEvent * mouseEvent);
+
+ public slots:
+ void pauseVisualization();
+ void resumeVisualization();
+
+ private slots:
+ void pixmapChange();
+ void updatePeaks();
+
+ void setVisualizationMode(int);
+ void setAnalyserMode(int);
+
+ private:
+ void paintEvent(QPaintEvent *);
+
+ QPopupMenu *contextMenu;
+ QPopupMenu *visualizationMenu;
+ QPopupMenu *analyserMenu;
+
+ void freshenAnalyserCache();
+
+ int visualization_mode;
+ int analyser_mode;
+
+ QPixmap *analyserCache;
+ WinSkinVis *winSkinVis;
+};
+#endif
+
diff --git a/noatun/modules/winskin/mimetypes/Makefile.am b/noatun/modules/winskin/mimetypes/Makefile.am
new file mode 100644
index 00000000..c28d0e8e
--- /dev/null
+++ b/noatun/modules/winskin/mimetypes/Makefile.am
@@ -0,0 +1,2 @@
+
+SUBDIRS = interface
diff --git a/noatun/modules/winskin/mimetypes/interface/Makefile.am b/noatun/modules/winskin/mimetypes/interface/Makefile.am
new file mode 100644
index 00000000..9ca52284
--- /dev/null
+++ b/noatun/modules/winskin/mimetypes/interface/Makefile.am
@@ -0,0 +1,6 @@
+noatun_modules_winskin_mimetypes_interface_DATA = x-winamp-skin.desktop
+
+noatun_modules_winskin_mimetypes_interfacedir = $(kde_mimedir)/interface
+
+EXTRA_DIST = $(noatun_modules_winskin_mimetypes_interface_DATA)
+
diff --git a/noatun/modules/winskin/mimetypes/interface/x-winamp-skin.desktop b/noatun/modules/winskin/mimetypes/interface/x-winamp-skin.desktop
new file mode 100644
index 00000000..34686e98
--- /dev/null
+++ b/noatun/modules/winskin/mimetypes/interface/x-winamp-skin.desktop
@@ -0,0 +1,58 @@
+[Desktop Entry]
+Type=MimeType
+MimeType=interface/x-winamp-skin
+Icon=colorscm
+Patterns=*.wsz;*.WSZ
+Comment=Compressed Winamp Skin
+Comment[bg]=Компресирана тема за Winamp
+Comment[bn]=কম্প্রেস করা উইন-অ্যাম্প স্কিন
+Comment[br]=Kroc'hen Winamp gwasket
+Comment[bs]=Komprimirani Winamp skin
+Comment[ca]=Aparença Winamp comprimida
+Comment[cs]=Komprimovaný Winamp skin
+Comment[cy]=Croen Winamp cywasgedig
+Comment[da]=Komprimeret Winamp-forside
+Comment[de]=Komprimierte Winamp-Oberfläche
+Comment[el]=Συμπιεσμένο θέμα Winamp
+Comment[eo]=Kunpremita Winamp-etoso
+Comment[es]=Piel comprimida de Winamp
+Comment[et]=Winampi pakitud kest (skin)
+Comment[eu]=Winamp azal konprimitua
+Comment[fa]=Winamp Skin فشرده
+Comment[fi]=Pakattu Winamp-nahka
+Comment[fr]=Revêtement Winamp compacté
+Comment[gl]=Pel de Winamp Comprimida
+Comment[he]=Winamp דחוס של Skin
+Comment[hi]=संपीडित विनएम्प स्किन
+Comment[hu]=Tömörített Winamp-kinézet
+Comment[is]=Þjappað Winamp-skin
+Comment[it]=Skin di Winamp compressa
+Comment[ja]=圧縮された Winamp のスキン
+Comment[kk]=Сығылған Winamp тысы
+Comment[km]=ស្បែក Winamp បាន​បង្ហាប់
+Comment[ko]=압축된 Winamp 스킨
+Comment[lt]=Suglaudintas Winamp pavidalas
+Comment[mk]=Компресирана маска Winamp
+Comment[nb]=Komprimert Winamp-ham
+Comment[nds]=Komprimeert Winamp-Böversiet
+Comment[ne]=सङ्कुचित विन्याप स्किन
+Comment[nl]=Gecomprimeerde Winamp-skin
+Comment[nn]=Komprimert Winamp-drakt
+Comment[pl]=Skompresowana skóra Winampa
+Comment[pt]=Aspecto Comprimido do Winamp
+Comment[pt_BR]=Skin do Winamp comprimido
+Comment[ro]=Interfaţă Winamp comprimată
+Comment[ru]=Сжатая тема Winamp
+Comment[sk]=Komprimované rozhranie pre Winamp
+Comment[sl]=Stisnjena preobleka za Winamp
+Comment[sr]=Компресован Winamp-ов скин
+Comment[sr@Latn]=Kompresovan Winamp-ov skin
+Comment[sv]=Komprimerat Winamp-skal
+Comment[ta]=அழுத்தப்பட்ட வின் ஆம்ப் அலங்கார அமைப்பு
+Comment[tg]=Намуди Фишурдашудаи Winamp
+Comment[th]=หน้ากากวินแอมป์บีบอัด Compress
+Comment[tr]=Sıkıştırılmış Winamp Teması
+Comment[uk]=Стиснутий жупан Winamp
+Comment[zh_CN]=压缩的 Winamp 皮肤
+Comment[zh_HK]=已壓縮的 Winamp skin
+Comment[zh_TW]=壓縮的 Winamp 面板
diff --git a/noatun/modules/winskin/plugin.cpp b/noatun/modules/winskin/plugin.cpp
new file mode 100644
index 00000000..8890c515
--- /dev/null
+++ b/noatun/modules/winskin/plugin.cpp
@@ -0,0 +1,13 @@
+#include <kglobal.h>
+#include <klocale.h>
+
+#include "winSkinConfig.h"
+#include "waSkin.h"
+
+extern "C" {
+ KDE_EXPORT Plugin *create_plugin() {
+ WaSkin *new_skin = new WaSkin();
+ new WinSkinConfig(new_skin, new_skin->skinManager());
+ return new_skin;
+ }
+}
diff --git a/noatun/modules/winskin/skinMap.h b/noatun/modules/winskin/skinMap.h
new file mode 100644
index 00000000..f67f1557
--- /dev/null
+++ b/noatun/modules/winskin/skinMap.h
@@ -0,0 +1,38 @@
+/*
+ generic type for describing skins.
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef _SKINMAP_H
+#define _SKINMAP_H
+
+#include <qpixmap.h>
+
+typedef struct {
+ int x;
+ int y;
+ int width;
+ int height;
+} SkinMap;
+
+typedef struct {
+ int fileId;
+ int x;
+ int y;
+ int width;
+ int height;
+} SkinDesc;
+
+
+
+#endif
diff --git a/noatun/modules/winskin/skins/Makefile.am b/noatun/modules/winskin/skins/Makefile.am
new file mode 100644
index 00000000..8f8e17ab
--- /dev/null
+++ b/noatun/modules/winskin/skins/Makefile.am
@@ -0,0 +1,11 @@
+# widgetlib - Makefile.am
+
+SUBDIRS = winamp
+
+
+
+
+
+
+
+
diff --git a/noatun/modules/winskin/skins/winamp/BALANCE.BMP b/noatun/modules/winskin/skins/winamp/BALANCE.BMP
new file mode 100644
index 00000000..3ce036e9
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/BALANCE.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/CBUTTONS.BMP b/noatun/modules/winskin/skins/winamp/CBUTTONS.BMP
new file mode 100644
index 00000000..8b335e55
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/CBUTTONS.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/FONT.BMP b/noatun/modules/winskin/skins/winamp/FONT.BMP
new file mode 100644
index 00000000..57caa947
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/FONT.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/MAIN.BMP b/noatun/modules/winskin/skins/winamp/MAIN.BMP
new file mode 100644
index 00000000..5e8a2cec
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/MAIN.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/MONOSTER.BMP b/noatun/modules/winskin/skins/winamp/MONOSTER.BMP
new file mode 100644
index 00000000..35fee70c
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/MONOSTER.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/Makefile.am b/noatun/modules/winskin/skins/winamp/Makefile.am
new file mode 100644
index 00000000..169e1bc5
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/Makefile.am
@@ -0,0 +1,8 @@
+
+EXTRA_DIST = BALANCE.BMP CBUTTONS.BMP FONT.BMP MAIN.BMP \
+ MONOSTER.BMP NUMS_EX.BMP PLAYPAUS.BMP \
+ POSBAR.BMP SHUFREP.BMP SPEC.BMP \
+ TEXT.BMP TITLEBAR.BMP VISCOLOR.TXT VOLUME.BMP
+
+skin_DATA = $(EXTRA_DIST)
+skindir = $(kde_datadir)/noatun/skins/winamp/Winamp
diff --git a/noatun/modules/winskin/skins/winamp/NUMS_EX.BMP b/noatun/modules/winskin/skins/winamp/NUMS_EX.BMP
new file mode 100644
index 00000000..72c3215d
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/NUMS_EX.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/PLAYPAUS.BMP b/noatun/modules/winskin/skins/winamp/PLAYPAUS.BMP
new file mode 100644
index 00000000..90319a0e
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/PLAYPAUS.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/POSBAR.BMP b/noatun/modules/winskin/skins/winamp/POSBAR.BMP
new file mode 100644
index 00000000..be6636ca
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/POSBAR.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/SHUFREP.BMP b/noatun/modules/winskin/skins/winamp/SHUFREP.BMP
new file mode 100644
index 00000000..12f5e406
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/SHUFREP.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/SPEC.BMP b/noatun/modules/winskin/skins/winamp/SPEC.BMP
new file mode 100644
index 00000000..38df7813
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/SPEC.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/TEXT.BMP b/noatun/modules/winskin/skins/winamp/TEXT.BMP
new file mode 100644
index 00000000..5b0b3a77
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/TEXT.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/TITLEBAR.BMP b/noatun/modules/winskin/skins/winamp/TITLEBAR.BMP
new file mode 100644
index 00000000..b3109235
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/TITLEBAR.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/VISCOLOR.TXT b/noatun/modules/winskin/skins/winamp/VISCOLOR.TXT
new file mode 100644
index 00000000..5871ad89
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/VISCOLOR.TXT
@@ -0,0 +1,24 @@
+0,0,0, // color 0 = black
+24,33,41, // color 1 = grey for dots
+239,49,16, // color 2 = top of spec
+206,41,16, // 3
+214,90,0, // 4
+214,102,0, // 5
+214,115,0, // 6
+198,123,8, // 7
+222,165,24, // 8
+214,181,33, // 9
+189,222,41, // 10
+148,222,33, // 11
+41,206,16, // 12
+50,190,16, // 13
+57,181,16, // 14
+49,156,8, // 15
+41,148,0, // 16
+24,132,8, // 17 = bottom of spec
+255,255,255, // 18 = osc 1
+214,214,222, // 19 = osc 2 (slightly dimmer)
+181,189,189, // 20 = osc 3
+160,170,175, // 21 = osc 4
+148,156,165, // 22 = osc 4
+150, 150, 150, // 23 = analyzer peak dots
diff --git a/noatun/modules/winskin/skins/winamp/VOLUME.BMP b/noatun/modules/winskin/skins/winamp/VOLUME.BMP
new file mode 100644
index 00000000..deb7e924
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/VOLUME.BMP
Binary files differ
diff --git a/noatun/modules/winskin/vis/Makefile.am b/noatun/modules/winskin/vis/Makefile.am
new file mode 100644
index 00000000..6935d860
--- /dev/null
+++ b/noatun/modules/winskin/vis/Makefile.am
@@ -0,0 +1,39 @@
+INCLUDES= -I$(kde_includes)/arts $(all_includes)
+KDE_OPTIONS = nofinal
+
+DISTCLEANFILES = winskinvis.h winskinvis.cc
+
+winskinvis.mcopclass: winskinvis.h
+winskinvis.mcoptype: winskinvis.h
+winskinvis.cc winskinvis.h : $(srcdir)/winskinvis.idl
+ $(MCOPIDL) -t -I$(kde_includes)/arts $(srcdir)/winskinvis.idl
+
+lib_LTLIBRARIES = libwinskinvis.la
+libwinskinvis_la_SOURCES= winskinvis.cc \
+ winSkinFFT_impl.cpp realFFT.cpp \
+ realFFTFilter.cpp visQueue.cpp
+
+
+libwinskinvis_la_LDFLAGS= $(all_libraries) -avoid-version \
+ -no-undefined
+
+libwinskinvis_la_LIBADD = -lkmedia2_idl -lsoundserver_idl -lartsflow
+libwinskinvis_la_COMPILE_FIRST = winskinvis.cc
+libwinskinvis_la_METASOURCES = AUTO
+
+
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = winskinvis.mcoptype winskinvis.mcopclass
+
+mcopclassdir = $(libdir)/mcop/Noatun
+mcopclass_DATA = WinSkinFFT.mcopclass
+
+noatuninclude_HEADERS = winskinvis.h
+
+noatunincludedir = $(includedir)/noatun
+
+
+winSkinFFT_impl.lo: winskinvis.h
+winskinvis.lo: winskinvis.h
+
diff --git a/noatun/modules/winskin/vis/WinSkinFFT.mcopclass b/noatun/modules/winskin/vis/WinSkinFFT.mcopclass
new file mode 100644
index 00000000..90d21e61
--- /dev/null
+++ b/noatun/modules/winskin/vis/WinSkinFFT.mcopclass
@@ -0,0 +1,5 @@
+Interface=Noatun::WinSkinFFT,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libwinskinvis.la
+
+
diff --git a/noatun/modules/winskin/vis/realFFT.cpp b/noatun/modules/winskin/vis/realFFT.cpp
new file mode 100644
index 00000000..330280ea
--- /dev/null
+++ b/noatun/modules/winskin/vis/realFFT.cpp
@@ -0,0 +1,156 @@
+/*
+ a FFT class
+ Copyright (C) 1998 Martin Vogt;Philip VanBaren
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "realFFT.h"
+
+/*
+ * Initialize the Sine table and Twiddle pointers (bit-reversed pointers)
+ * for the FFT routine.
+ */
+RealFFT::RealFFT(int fftlen) {
+ int i;
+ int temp;
+ int mask;
+
+ /*
+ * FFT size is only half the number of data points
+ * The full FFT output can be reconstructed from this FFT's output.
+ * (This optimization can be made since the data is real.)
+ */
+ Points = fftlen;
+
+ if((SinTable=(short *)malloc(Points*sizeof(short)))==NULL)
+ {
+ puts("Error allocating memory for Sine table.");
+ exit(1);
+ }
+ if((BitReversed=(int *)malloc(Points/2*sizeof(int)))==NULL)
+ {
+ puts("Error allocating memory for BitReversed.");
+ exit(1);
+ }
+
+ for(i=0;i<Points/2;i++)
+ {
+ temp=0;
+ for(mask=Points/4;mask>0;mask >>= 1)
+ temp=(temp >> 1) + (i&mask ? Points/2 : 0);
+
+ BitReversed[i]=temp;
+ }
+
+ for(i=0;i<Points/2;i++)
+ {
+ register double s,c;
+ s=floor(-32768.0*sin(2*M_PI*i/(Points))+0.5);
+ c=floor(-32768.0*cos(2*M_PI*i/(Points))+0.5);
+ if(s>32767.5) s=32767;
+ if(c>32767.5) c=32767;
+ SinTable[BitReversed[i] ]=(short)s;
+ SinTable[BitReversed[i]+1]=(short)c;
+ }
+
+}
+
+/*
+ * Free up the memory allotted for Sin table and Twiddle Pointers
+ */
+RealFFT::~RealFFT() {
+ free(BitReversed);
+ free(SinTable);
+ Points=0;
+}
+
+
+/*
+ * Actual FFT routine. Must call InitializeFFT(fftlen) first!
+ * This routine has another parameter list than the other fft's
+ * But because we want a fast fft on pcm data this routine
+ * is better than the other two.
+ * The other two can be useful for inverse FFT.
+ * The format is an array of floats. (only real parts the img
+ * part does not exists)
+ */
+void RealFFT::fft(short* buffer) {
+ int ButterfliesPerGroup=Points/4;
+
+ endptr1=buffer+Points;
+
+ /*
+ * Butterfly:
+ * Ain-----Aout
+ * \ /
+ * / \
+ * Bin-----Bout
+ */
+
+ while(ButterfliesPerGroup>0)
+ {
+ A=buffer;
+ B=buffer+ButterfliesPerGroup*2;
+ sptr=SinTable;
+
+ while(A<endptr1)
+ {
+ register short sin=*sptr;
+ register short cos=*(sptr+1);
+ endptr2=B;
+ while(A<endptr2)
+ {
+ long v1=((long)*B*cos + (long)*(B+1)*sin) >> 15;
+ long v2=((long)*B*sin - (long)*(B+1)*cos) >> 15;
+ *B=(*A+v1)>>1;
+ *(A++)=*(B++)-v1;
+ *B=(*A-v2)>>1;
+ *(A++)=*(B++)+v2;
+ }
+ A=B;
+ B+=ButterfliesPerGroup*2;
+ sptr+=2;
+ }
+ ButterfliesPerGroup >>= 1;
+ }
+ /*
+ * Massage output to get the output for a real input sequence.
+ */
+ br1=BitReversed+1;
+ br2=BitReversed+Points/2-1;
+
+ while(br1<=br2)
+ {
+ register long temp1,temp2;
+ short sin=SinTable[*br1];
+ short cos=SinTable[*br1+1];
+ A=buffer+*br1;
+ B=buffer+*br2;
+ HRplus = (HRminus = *A - *B ) + (*B << 1);
+ HIplus = (HIminus = *(A+1) - *(B+1)) + (*(B+1) << 1);
+ temp1 = ((long)sin*HRminus - (long)cos*HIplus) >> 15;
+ temp2 = ((long)cos*HRminus + (long)sin*HIplus) >> 15;
+ *B = (*A = (HRplus + temp1) >> 1) - temp1;
+ *(B+1) = (*(A+1) = (HIminus + temp2) >> 1) - HIminus;
+
+ br1++;
+ br2--;
+ }
+ /*
+ * Handle DC bin separately
+ */
+ buffer[0]+=buffer[1];
+ buffer[1]=0;
+}
+
+
+int* RealFFT::getBitReversed() {
+ return BitReversed;
+}
diff --git a/noatun/modules/winskin/vis/realFFT.h b/noatun/modules/winskin/vis/realFFT.h
new file mode 100644
index 00000000..39c6dbfd
--- /dev/null
+++ b/noatun/modules/winskin/vis/realFFT.h
@@ -0,0 +1,69 @@
+/*
+ a FFT class
+ Copyright (C) 1998 Martin Vogt;Philip VanBaren
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __REALFFT_H
+#define __REALFFT_H
+
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/**
+ <pre>
+ * Program: REALFFTF.C
+ * Author: Philip VanBaren
+ * Date: 2 September 1993
+ *
+ * Description: These routines perform an FFT on real data.
+ * On a 486/33 compiled using Borland C++ 3.1 with full
+ * speed optimization and a small memory model, a 1024 point
+ * FFT takes about 16ms.
+ * This code is for floating point data.
+ *
+ * Note: Output is BIT-REVERSED! so you must use the BitReversed to
+ * get legible output, (i.e. Real_i = buffer[ BitReversed[i] ]
+ * Imag_i = buffer[ BitReversed[i]+1 ] )
+ * Input is in normal order.
+ </pre>
+ */
+
+
+
+class RealFFT {
+
+ int* BitReversed;
+ short* SinTable;
+ int Points;
+
+ public:
+ RealFFT(int fftlen);
+ ~RealFFT();
+
+ void fft(short* buffer);
+ int* getBitReversed();
+
+ private:
+
+ short *A,*B;
+ short *sptr;
+ short *endptr1,*endptr2;
+ int *br1,*br2;
+ long HRplus,HRminus,HIplus,HIminus;
+
+
+};
+
+
+#endif
diff --git a/noatun/modules/winskin/vis/realFFTFilter.cpp b/noatun/modules/winskin/vis/realFFTFilter.cpp
new file mode 100644
index 00000000..13343bce
--- /dev/null
+++ b/noatun/modules/winskin/vis/realFFTFilter.cpp
@@ -0,0 +1,88 @@
+/*
+ a FFT filter
+ Copyright (C) 1998 Martin Vogt;Philip VanBaren, 2 September 1993
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "realFFTFilter.h"
+
+RealFFTFilter::RealFFTFilter(int fftPoints) {
+ this->fftPoints = fftPoints;
+ data=new short[fftPoints*4];
+ realFFT= new RealFFT(fftPoints*2);
+}
+
+
+RealFFTFilter::~RealFFTFilter() {
+ delete data;
+ delete realFFT;
+}
+
+
+int RealFFTFilter::getPoints() {
+ return fftPoints;
+}
+
+
+short* RealFFTFilter::getPointPtr() {
+ return data;
+}
+
+
+/*
+ the array is expected to be a PCM stream (real data)
+*/
+int RealFFTFilter::fft16(float* left,float* right,int len) {
+ int i;
+
+ len=len/4;
+
+
+ int mixTmp;
+
+ // take care for no array overflows:
+ int n=min(len,fftPoints);
+
+ // copy things into fftArray.
+
+ for (i = 0 ; i < n; i++) {
+ mixTmp=(int) (16384.0*(left[i]+right[i]));
+
+ if (mixTmp < SHRT_MIN) {
+ data[i]= SHRT_MIN;
+ } else {
+ if (mixTmp > SHRT_MAX) {
+ data[i] = SHRT_MAX;
+ } else {
+ data[i]=(short)mixTmp;
+ }
+ }
+ }
+
+ realFFT->fft(data);
+ return true;
+}
+
+
+
+int* RealFFTFilter::getBitReversed() {
+ return realFFT->getBitReversed();
+}
+
+
+
+int RealFFTFilter::min(int x1,int x2) {
+ if (x1 < x2) {
+ return x1;
+ }
+ return x2;
+}
+
+
diff --git a/noatun/modules/winskin/vis/realFFTFilter.h b/noatun/modules/winskin/vis/realFFTFilter.h
new file mode 100644
index 00000000..255e5191
--- /dev/null
+++ b/noatun/modules/winskin/vis/realFFTFilter.h
@@ -0,0 +1,49 @@
+/*
+ a FFT filter
+ Copyright (C) 1998 Martin Vogt;Philip VanBaren, 2 September 1993
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __REALFFTFILTER_H
+#define __REALFFTFILTER_H
+
+
+
+#include "realFFT.h"
+#include <limits.h>
+
+
+
+class RealFFTFilter {
+
+ int fftPoints;
+ RealFFT* realFFT;
+
+ short* data;
+
+
+ public:
+ RealFFTFilter(int points);
+ ~RealFFTFilter();
+ int fft16(float* left,float* right,int len);
+
+ int* getBitReversed();
+ int getPoints();
+ short* getPointPtr();
+
+ private:
+ int min(int x1,int x2);
+
+};
+
+
+#endif
diff --git a/noatun/modules/winskin/vis/visQueue.cpp b/noatun/modules/winskin/vis/visQueue.cpp
new file mode 100644
index 00000000..370930d2
--- /dev/null
+++ b/noatun/modules/winskin/vis/visQueue.cpp
@@ -0,0 +1,43 @@
+/*
+ queue fft samples
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "visQueue.h"
+
+
+VISQueue::VISQueue(int elements) {
+ this->elements=elements;
+
+ visArrayQueue = new std::vector<float>*[elements];
+ for(int i=0;i<elements;i++)
+ {
+ visArrayQueue[i]=new std::vector<float>;
+ }
+
+}
+
+VISQueue::~VISQueue() {
+ for(int i=0;i<elements;i++) {
+ delete visArrayQueue[i];
+ }
+ delete [] visArrayQueue;
+}
+
+std::vector<float>* VISQueue::getElement(int i)
+{
+ if ( (i < 0) || (i>elements) ) {
+ return visArrayQueue[0];
+ }
+ return visArrayQueue[i];
+}
+
+
diff --git a/noatun/modules/winskin/vis/visQueue.h b/noatun/modules/winskin/vis/visQueue.h
new file mode 100644
index 00000000..2f737fd1
--- /dev/null
+++ b/noatun/modules/winskin/vis/visQueue.h
@@ -0,0 +1,32 @@
+/*
+ queue fft samples
+ Copyright (C) 2001 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __VISQUEUE_H
+#define __VISQUEUE_H
+
+
+#include <vector>
+
+class VISQueue {
+
+ int elements;
+ std::vector<float> **visArrayQueue;
+
+ public:
+ VISQueue(int elements);
+ ~VISQueue();
+
+ std::vector<float>* getElement(int i);
+
+};
+#endif
diff --git a/noatun/modules/winskin/vis/winSkinFFT_impl.cpp b/noatun/modules/winskin/vis/winSkinFFT_impl.cpp
new file mode 100644
index 00000000..5396ac3c
--- /dev/null
+++ b/noatun/modules/winskin/vis/winSkinFFT_impl.cpp
@@ -0,0 +1,148 @@
+/*
+ implementation for winskin fft
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "winSkinFFT_impl.h"
+#include <audiosubsys.h>
+#include <cstring>
+
+#define __BANDS 75
+
+namespace Noatun {
+
+WinSkinFFT_impl::WinSkinFFT_impl() {
+ fftBands_short=256;
+ realFFTFilter= new RealFFTFilter(fftBands_short);
+ fftArray=new int[fftBands_short];
+ bands=0;
+
+ fragCnt=(int)(AudioSubSystem::the()->fragmentCount());
+ visQueue=new VISQueue(fragCnt);
+ writePos=0;
+
+}
+
+WinSkinFFT_impl::~WinSkinFFT_impl(){
+ delete realFFTFilter;
+ delete fftArray;
+ delete visQueue;
+}
+
+void WinSkinFFT_impl::streamInit() {
+}
+
+
+void WinSkinFFT_impl::streamStart() {
+}
+
+
+void WinSkinFFT_impl::calculateBlock(unsigned long samples) {
+
+
+ unsigned long i;
+
+ // monitoring only tasks can't be done with that StereoEffect
+ // interface nicely - copy input to output until there is
+ // something better
+ // (when?)
+ int n=sizeof(float)*samples;
+ memcpy(outleft,inleft,n);
+ memcpy(outright,inright,n);
+
+
+ if (realFFTFilter->fft16(inleft,inright,samples) == false) {
+ return;
+ }
+
+
+
+ //
+ // The following modifications have nothing to do
+ // with an fft, they only make the output look nice.
+ // (mostly scaling)
+
+ short* fftPtr;
+ int* bitReversed;
+
+ fftPtr=realFFTFilter->getPointPtr();
+ bitReversed=realFFTFilter->getBitReversed();
+
+ int pos=0;
+ int step=realFFTFilter->getPoints()/__BANDS;
+
+
+ int re;
+ int im;
+ int tmp;
+
+ float max=0.0;
+ float avg=0.0;
+
+
+
+ for (i=0;i<__BANDS;i++) {
+ re=(int)fftPtr[bitReversed[pos]];
+ im=(int)fftPtr[bitReversed[pos]+1];
+
+ tmp=re*re+im*im;
+ // Here I check a new idea. We remove all low values
+ // and all values over xyz to xyz.
+ fftArray[pos]=(int)(::sqrt(::sqrt(tmp)));
+
+ if (fftArray[pos]<=15) {
+ max+=fftArray[pos];
+ } else {
+ max+=15+fftArray[pos]/2;
+ }
+ pos=pos+step;
+ }
+ avg=0.65*max/(float)__BANDS;
+
+ pos=0;
+ vector<float>* visAnalyserArray=visQueue->getElement(writePos);
+ visAnalyserArray->clear();
+ visAnalyserArray->reserve(__BANDS);
+ for (i=0;i<__BANDS;i++) {
+ float val=(float)(fftArray[pos]-avg);
+ visAnalyserArray->push_back(val);
+ pos=pos+step;
+ }
+ writePos++;
+ if (writePos >= fragCnt) writePos=0;
+
+}
+
+
+void WinSkinFFT_impl::bandResolution(float res) {
+ bands=(int)res;
+}
+
+float WinSkinFFT_impl::bandResolution() {
+ return (float)bands;
+}
+
+
+vector<float>* WinSkinFFT_impl::scope() {
+ int delay=writePos+1;
+ if (delay >= fragCnt) delay=0;
+
+
+ vector<float>* visAnalyserArray=visQueue->getElement(delay);
+
+ return new vector<float>(*visAnalyserArray);
+}
+
+
+REGISTER_IMPLEMENTATION(WinSkinFFT_impl);
+
+}
diff --git a/noatun/modules/winskin/vis/winSkinFFT_impl.h b/noatun/modules/winskin/vis/winSkinFFT_impl.h
new file mode 100644
index 00000000..c1a77e45
--- /dev/null
+++ b/noatun/modules/winskin/vis/winSkinFFT_impl.h
@@ -0,0 +1,62 @@
+/*
+ implementation for winskin fft
+ Copyright (C) 2000 Martin Vogt
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __WINSKINFFT_IMPL_H
+#define __WINSKINFFT_IMPL_H
+
+#include <artsflow.h>
+#include <stdsynthmodule.h>
+#include "winskinvis.h"
+#include "realFFTFilter.h"
+#include "visQueue.h"
+
+
+using namespace std;
+using namespace Arts;
+
+namespace Noatun {
+
+class WinSkinFFT_impl : public WinSkinFFT_skel, public StdSynthModule {
+
+ public:
+
+ WinSkinFFT_impl();
+ ~WinSkinFFT_impl();
+
+ void streamInit();
+ void streamStart();
+
+ // in: audio stream inleft, inright;
+ // out: audio stream outleft, outright;
+ void calculateBlock(unsigned long samples);
+
+ void bandResolution(float res);
+ float bandResolution();
+ vector<float> *scope();
+
+ private:
+ RealFFTFilter* realFFTFilter;
+ int fftBands_short;
+ int* fftArray;
+ VISQueue* visQueue;
+ int bands;
+
+ int fragCnt;
+ int writePos;
+ int readPos;
+};
+
+
+}
+
+#endif
diff --git a/noatun/modules/winskin/vis/winskinvis.idl b/noatun/modules/winskin/vis/winskinvis.idl
new file mode 100644
index 00000000..9b6564f4
--- /dev/null
+++ b/noatun/modules/winskin/vis/winskinvis.idl
@@ -0,0 +1,12 @@
+#include <artsflow.idl>
+
+module Noatun
+{
+
+interface WinSkinFFT : Arts::StereoEffect
+{
+ attribute float bandResolution;
+ sequence<float> scope();
+};
+
+}; \ No newline at end of file
diff --git a/noatun/modules/winskin/waBalanceSlider.cpp b/noatun/modules/winskin/waBalanceSlider.cpp
new file mode 100644
index 00000000..1ac1b562
--- /dev/null
+++ b/noatun/modules/winskin/waBalanceSlider.cpp
@@ -0,0 +1,56 @@
+/*
+ balanceslider for winamp skins
+ Copyright (C) 1998 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include <waBalanceSlider.h>
+
+
+WaBalanceSlider::WaBalanceSlider() : WaWidget(_WA_MAPPING_BALANCE_BAR)
+{
+}
+
+
+WaBalanceSlider::~WaBalanceSlider()
+{
+}
+
+
+void WaBalanceSlider::buildGui()
+{
+ ws = new WaSlider(_WA_MAPPING_BALANCE_BAR,
+ _WA_MAPPING_BALANCE_SLIDER, true);
+
+ ws->setRange(-100, 100);
+
+
+ ws->setPixmapSliderButtonUp(_WA_SKIN_BALANCE_SLIDER_NORM);
+ ws->setPixmapSliderButtonDown(_WA_SKIN_BALANCE_SLIDER_PRES);
+ ws->setPixmapSliderBar(_WA_SKIN_BALANCE_BAR);
+
+ ws->setValue(0);
+
+ connect(ws, SIGNAL(valueChanged(int)), this,
+ SIGNAL(balanceSetValue(int)));
+ connect(ws, SIGNAL(sliderPressed()), SIGNAL(sliderPressed()));
+ connect(ws, SIGNAL(sliderReleased()), SIGNAL(sliderReleased()));
+}
+
+
+void WaBalanceSlider::setBalanceValue(int val)
+{
+ int currVal = ws->value();
+ if (currVal != val) {
+ ws->setValue(val);
+ }
+}
+
+#include "waBalanceSlider.moc"
diff --git a/noatun/modules/winskin/waBalanceSlider.h b/noatun/modules/winskin/waBalanceSlider.h
new file mode 100644
index 00000000..d120bcc7
--- /dev/null
+++ b/noatun/modules/winskin/waBalanceSlider.h
@@ -0,0 +1,42 @@
+/*
+ balanceslider for winamp skins
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2001 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __WABALANCESLIDER_H
+#define __WABALANCESLIDER_H
+
+#include <waSlider.h>
+#include "waWidget.h"
+
+class WaBalanceSlider : public WaWidget {
+ Q_OBJECT
+
+ public:
+ WaBalanceSlider();
+ ~WaBalanceSlider();
+ void buildGui();
+
+ void setBalanceValue(int val);
+
+ private:
+ WaSlider *ws;
+
+ signals:
+ void balanceSetValue(int val);
+ void sliderPressed();
+ void sliderReleased();
+};
+
+
+#endif
diff --git a/noatun/modules/winskin/waButton.cpp b/noatun/modules/winskin/waButton.cpp
new file mode 100644
index 00000000..cac0275a
--- /dev/null
+++ b/noatun/modules/winskin/waButton.cpp
@@ -0,0 +1,101 @@
+/*
+ standard Button fo winamp Skin
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include <qbitmap.h>
+
+#include "waButton.h"
+
+WaButton::WaButton(int mapId) : WaWidget(mapId)
+{
+ _togglable = false;
+ _toggled = false;
+ pressed = false;
+}
+
+
+WaButton::~WaButton() {
+}
+
+void WaButton::setPixmapUp(int pixId) {
+ nUpId=pixId;
+}
+
+
+void WaButton::setPixmapDown(int pixId) {
+ nDownId=pixId;
+}
+
+
+void WaButton::setPixmapUpSelected(int pixId) {
+ nUpIdSelected=pixId;
+}
+
+
+void WaButton::setPixmapDownSelected(int pixId) {
+ nDownIdSelected=pixId;
+}
+
+void WaButton::paintEvent(QPaintEvent *) {
+ paintPixmap(getPixmapId());
+}
+
+
+void WaButton::mousePressEvent(QMouseEvent* e) {
+ if (e->button() != LeftButton) {
+ // We can't deal with it, but maybe the widget can do something clever
+ WaWidget::mousePressEvent(e);
+ }
+ else {
+ pressed = true;
+ update();
+ }
+}
+
+void WaButton::mouseReleaseEvent(QMouseEvent* e) {
+ if (!pressed) {
+ // We're not pressed, so just pass along the mouse event, it's not ours
+ WaWidget::mouseReleaseEvent(e);
+ }
+ else {
+ pressed = false;
+
+ if (this->rect().contains(e->pos())){
+ if (_togglable) {
+ _toggled = !_toggled;
+ emit(toggleEvent(_toggled));
+ }
+
+ emit(clicked());
+ }
+ }
+
+ update();
+}
+
+int WaButton::getPixmapId() {
+ if (_toggled == true) {
+ if (pressed)
+ return nDownIdSelected;
+ else
+ return nUpIdSelected;
+ }
+ else {
+ if (pressed)
+ return nDownId;
+ else
+ return nUpId;
+ }
+
+ return -1;
+}
+
+#include "waButton.moc"
diff --git a/noatun/modules/winskin/waButton.h b/noatun/modules/winskin/waButton.h
new file mode 100644
index 00000000..50947216
--- /dev/null
+++ b/noatun/modules/winskin/waButton.h
@@ -0,0 +1,62 @@
+/*
+ standard Button for winamp Skin
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __WABUTTON_H
+#define __WABUTTON_H
+
+#include <qpainter.h>
+#include "waWidget.h"
+
+class WaButton : public WaWidget {
+ Q_OBJECT
+ public:
+ WaButton(int mapId);
+ ~WaButton();
+
+ void setPixmapDown(int pixId);
+ void setPixmapUp(int pixId);
+ void setPixmapUpSelected(int pixId);
+ void setPixmapDownSelected(int pixId);
+
+ void setToggled(bool toggled_flag) { _toggled = toggled_flag; update(); }
+ bool toggled() const { return _toggled; }
+
+ void setTogglable(bool togglable_flag) { _togglable = togglable_flag; update(); }
+ bool togglable() const { return _togglable; }
+
+ int getPixmapId();
+ void paintEvent(QPaintEvent*);
+
+ private:
+ void mousePressEvent (QMouseEvent* e);
+ void mouseReleaseEvent (QMouseEvent* e);
+
+ int nUpId;
+ int nDownId;
+ int nDownIdSelected;
+ int nUpIdSelected;
+
+ QPoint currentLocation;
+
+ bool _toggled;
+ bool _togglable;
+
+ bool pressed;
+
+ signals:
+ void toggleEvent(bool val);
+ void clicked();
+};
+#endif
diff --git a/noatun/modules/winskin/waClutterbar.cpp b/noatun/modules/winskin/waClutterbar.cpp
new file mode 100644
index 00000000..6ceb7d04
--- /dev/null
+++ b/noatun/modules/winskin/waClutterbar.cpp
@@ -0,0 +1,11 @@
+#include "waClutterbar.h"
+#include "waClutterbar.moc"
+WaClutterbar::WaClutterbar() : WaWidget(_WA_MAPPING_CLUTTERBAR) {
+}
+
+WaClutterbar::~WaClutterbar() {
+}
+
+void WaClutterbar::paintEvent(QPaintEvent *) {
+ paintPixmap(_WA_SKIN_CLUTTERBAR_DISABLED);
+}
diff --git a/noatun/modules/winskin/waClutterbar.h b/noatun/modules/winskin/waClutterbar.h
new file mode 100644
index 00000000..69339c37
--- /dev/null
+++ b/noatun/modules/winskin/waClutterbar.h
@@ -0,0 +1,18 @@
+#ifndef __WACLUTTERBAR_H
+#define __WACLUTTERBAR_H
+
+#include <qpainter.h>
+#include "waWidget.h"
+
+class WaClutterbar : public WaWidget {
+ Q_OBJECT
+
+ public:
+ WaClutterbar();
+ ~WaClutterbar();
+
+ public slots:
+ void paintEvent(QPaintEvent *);
+};
+
+#endif
diff --git a/noatun/modules/winskin/waColor.cpp b/noatun/modules/winskin/waColor.cpp
new file mode 100644
index 00000000..e06b38bc
--- /dev/null
+++ b/noatun/modules/winskin/waColor.cpp
@@ -0,0 +1,73 @@
+#include <fstream>
+#include <qfile.h>
+
+#include "waColor.h"
+
+WaColor *colorScheme = NULL;
+
+WaColor::WaColor(QString filename) {
+ int r, g, b;
+ char comma;
+
+ skinColors[0].setRgb(0, 0, 0);
+ skinColors[1].setRgb(24, 33, 41);
+ skinColors[2].setRgb(239, 49, 16);
+ skinColors[3].setRgb(206, 41, 16);
+ skinColors[4].setRgb(214, 90, 0);
+ skinColors[5].setRgb(214, 102, 0);
+ skinColors[6].setRgb(214, 115, 0);
+ skinColors[7].setRgb(198, 123, 8);
+ skinColors[8].setRgb(222, 165, 24);
+ skinColors[9].setRgb(214, 181, 33);
+ skinColors[10].setRgb(189, 222, 41);
+ skinColors[11].setRgb(148, 222, 33);
+ skinColors[12].setRgb(41, 206, 16);
+ skinColors[13].setRgb(50, 190, 16);
+ skinColors[14].setRgb(57, 181, 16);
+ skinColors[15].setRgb(49, 156, 8);
+ skinColors[16].setRgb(41, 148, 0);
+ skinColors[17].setRgb(24, 132, 8);
+ skinColors[18].setRgb(255, 255, 255);
+ skinColors[19].setRgb(214, 214, 222);
+ skinColors[20].setRgb(181, 189, 189);
+ skinColors[21].setRgb(160, 170, 175);
+ skinColors[22].setRgb(148, 156, 165);
+ skinColors[23].setRgb(150, 150, 150);
+
+ if (filename.isEmpty()) {
+ return;
+ }
+
+ std::ifstream viscolor(QFile::encodeName(filename));
+
+ if (!viscolor)
+ return;
+
+ for (int index = 0;index < 24;index++) {
+ viscolor >> r;
+ viscolor >> std::ws;
+ viscolor >> comma;
+ viscolor >> std::ws;
+ viscolor >> g;
+ viscolor >> std::ws;
+ viscolor >> comma;
+ viscolor >> std::ws;
+ viscolor >> b;
+
+ while(1) {
+ char c;
+
+ if (!viscolor.get(c))
+ return;
+
+ if (c == '\n')
+ break;
+ }
+
+ skinColors[index].setRgb(r, g, b);
+ }
+
+}
+
+WaColor::~WaColor() {
+}
diff --git a/noatun/modules/winskin/waColor.h b/noatun/modules/winskin/waColor.h
new file mode 100644
index 00000000..a0e18484
--- /dev/null
+++ b/noatun/modules/winskin/waColor.h
@@ -0,0 +1,26 @@
+
+
+#ifndef WACOLOR_H
+#define WACOLOR_H
+
+#include <qcolor.h>
+#include <qstring.h>
+
+#define INDEX_BACKGROUND_COLOR 0
+#define INDEX_GRID_COLOR 1
+#define INDEX_SPEC_BASE 2
+#define INDEX_OSC_BASE 18
+#define INDEX_PEAKS 23
+
+class WaColor {
+public:
+ WaColor(QString filename);
+ ~WaColor();
+
+ QColor skinColors[24];
+};
+
+extern WaColor *colorScheme;
+
+#endif
+
diff --git a/noatun/modules/winskin/waDigit.cpp b/noatun/modules/winskin/waDigit.cpp
new file mode 100644
index 00000000..b775d7cf
--- /dev/null
+++ b/noatun/modules/winskin/waDigit.cpp
@@ -0,0 +1,89 @@
+/*
+ The digit for the time
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2002 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "waDigit.h"
+#include "waSkinModel.h"
+
+#include <kconfig.h>
+#include <kglobal.h>
+
+WaDigit::WaDigit() : WaWidget(_WA_MAPPING_DIGITS)
+{
+ KConfig *config = KGlobal::config();
+ config->setGroup("Winskin");
+
+ reverse_time = config->readNumEntry("timeReversed", false);
+}
+
+
+WaDigit::~WaDigit()
+{
+ KConfig *config = KGlobal::config();
+ config->setGroup("Winskin");
+ config->writeEntry("timeReversed", reverse_time);
+}
+
+void WaDigit::paintEvent(QPaintEvent *)
+{
+ paintBackground();
+
+ const char *time = timeString.latin1();
+ int len = strlen(time);
+ if (len == 0)
+ return;
+
+ // Declare all these variables after we check for zero-length
+ WaSkinModel *waSkinModel = WaSkinModel::instance();
+
+ int x = waSkinModel->getMapGeometry(mapping).x();
+ int y = waSkinModel->getMapGeometry(mapping).y();
+
+ QRect mapRect;
+
+ // We expect strings either in the form "xx:yy", or "-xx:yy"
+ // If the string length is 6, we have it in the form of "-xx:yy"
+ // Remove the -, move the string forward one character, and
+ // continue parsing
+ mapRect = waSkinModel->getMapGeometry(_WA_MAPPING_MINUS);
+ if (len == 6) {
+ waSkinModel->getDigit('-', this, mapRect.x() - x, mapRect.y() - y);
+ time++;
+ }
+ else {
+ waSkinModel->getDigit(' ', this, mapRect.x() - x, mapRect.y() - y);
+ }
+
+ mapRect = waSkinModel->getMapGeometry(_WA_MAPPING_DIGIT_1);
+ waSkinModel->getDigit(time[0], this, mapRect.x() - x, mapRect.y() - y);
+
+ mapRect = waSkinModel->getMapGeometry(_WA_MAPPING_DIGIT_2);
+ waSkinModel->getDigit(time[1], this, mapRect.x() - x, mapRect.y() - y);
+
+ mapRect = waSkinModel->getMapGeometry(_WA_MAPPING_DIGIT_3);
+ waSkinModel->getDigit(time[3], this, mapRect.x() - x, mapRect.y() - y);
+
+ mapRect = waSkinModel->getMapGeometry(_WA_MAPPING_DIGIT_4);
+ waSkinModel->getDigit(time[4], this, mapRect.x() - x, mapRect.y() - y);
+}
+
+void WaDigit::mousePressEvent(QMouseEvent* e) {
+ if (e->button() == LeftButton) {
+ reverse_time = !reverse_time;
+ emit digitsClicked();
+ }
+ else
+ WaWidget::mousePressEvent(e);
+}
+
+#include "waDigit.moc"
diff --git a/noatun/modules/winskin/waDigit.h b/noatun/modules/winskin/waDigit.h
new file mode 100644
index 00000000..cefbfeb3
--- /dev/null
+++ b/noatun/modules/winskin/waDigit.h
@@ -0,0 +1,48 @@
+/*
+ The digit for the time
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __WADIGIT_H
+#define __WADIGIT_H
+
+#include <qpainter.h>
+
+#include "waWidget.h"
+
+class WaDigit : public WaWidget {
+ Q_OBJECT
+
+ public:
+ WaDigit();
+ ~WaDigit();
+
+ void setTime(QString time) { timeString = time; update(); }
+ QString time() const { return timeString; }
+
+ bool timeReversed() const { return reverse_time; }
+
+ public slots:
+ void paintEvent(QPaintEvent * paintEvent);
+
+ private:
+ void mousePressEvent(QMouseEvent* e);
+ bool reverse_time;
+
+ WaSkinModel *waSkinModel;
+ QString timeString;
+
+ signals:
+ void digitsClicked();
+};
+#endif
diff --git a/noatun/modules/winskin/waIndicator.cpp b/noatun/modules/winskin/waIndicator.cpp
new file mode 100644
index 00000000..9c5efa84
--- /dev/null
+++ b/noatun/modules/winskin/waIndicator.cpp
@@ -0,0 +1,34 @@
+/*
+ State indicator for Winamp Skin
+ Copyright (C) 2002 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include <waIndicator.h>
+
+WaIndicator::WaIndicator(int widget_mapping, int enabled_mapping, int disabled_mapping)
+ : WaWidget(widget_mapping)
+{
+ _state = false;
+ _enabled_mapping = enabled_mapping;
+ _disabled_mapping = disabled_mapping;
+}
+
+WaIndicator::~WaIndicator()
+{
+}
+
+void WaIndicator::paintEvent(QPaintEvent *)
+{
+ paintPixmap( _state ? _enabled_mapping : _disabled_mapping );
+}
+
+
+#include "waIndicator.moc"
diff --git a/noatun/modules/winskin/waIndicator.h b/noatun/modules/winskin/waIndicator.h
new file mode 100644
index 00000000..f367fe84
--- /dev/null
+++ b/noatun/modules/winskin/waIndicator.h
@@ -0,0 +1,41 @@
+/*
+ Standard state indicator for Winamp skin
+ Copyright (C) 2002 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __WAINDICATOR_H
+#define __WAINDICATOR_H
+
+#include <qpainter.h>
+
+#include "waWidget.h"
+
+class WaIndicator : public WaWidget {
+ Q_OBJECT
+
+ public:
+ WaIndicator(int widget_mapping, int enabled_mapping, int disabled_mapping);
+ ~WaIndicator();
+
+ void setState(bool state) { _state = state; update(); }
+ bool state() const { return _state; }
+
+ public slots:
+ void paintEvent(QPaintEvent *);
+
+ private:
+ int _enabled_mapping;
+ int _disabled_mapping;
+ bool _state;
+};
+#endif
diff --git a/noatun/modules/winskin/waInfo.cpp b/noatun/modules/winskin/waInfo.cpp
new file mode 100644
index 00000000..c735a8e4
--- /dev/null
+++ b/noatun/modules/winskin/waInfo.cpp
@@ -0,0 +1,173 @@
+/*
+ Scrolling song title for winamp Skin
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2001 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include <qpainter.h>
+#include <qpixmap.h>
+
+#include <stdlib.h>
+
+#include <kconfig.h>
+#include <kglobal.h>
+
+#include "waInfo.h"
+#include "waSkinModel.h"
+
+WaInfo::WaInfo() : WaWidget(_WA_MAPPING_INFO)
+{
+ connect(WaSkinModel::instance(), SIGNAL(skinChanged()),
+ this, SLOT(pixmapChange()));
+
+ completePixmap = new QPixmap();
+
+ QSize size = sizeHint();
+ completePixmap->resize(size.width(), size.height());
+
+ xGrabbedPos = -1;
+
+ timer = new QTimer(this);
+ connect(timer, SIGNAL(timeout()), this, SLOT(timeEvent()));
+}
+
+WaInfo::~WaInfo()
+{
+ delete completePixmap;
+}
+
+
+void WaInfo::timeEvent()
+{
+ if ((xGrabbedPos == -1) && (xScrollDirection)) {
+ xScrollPos += xScrollDirection;
+
+ if (abs(xScrollPos) > completePixmap->width()) {
+ xScrollPos = 0;
+ }
+
+ if (isVisible())
+ repaint(false);
+ }
+}
+
+
+void WaInfo::scrollerSetup()
+{
+ xScrollPos = 0;
+ xScrollDirection = 0;
+ timer->stop();
+ QSize size = sizeHint();
+ if (completePixmap->width() > size.width()) {
+ xScrollDirection = 1;
+
+ KConfig *config=KGlobal::config();
+ config->setGroup("Winskin");
+ int s = config->readNumEntry("ScrollDelay", 15);
+ if (s!=0)
+ timer->start(50-s);
+ }
+}
+
+void WaInfo::paintEvent(QPaintEvent *)
+{
+ QSize size = sizeHint();
+
+ if (completePixmap->width() <= size.width()) {
+ bitBlt(this, 0, 0, completePixmap);
+ return;
+ }
+
+ // pixmap widther than window:
+ int xDrawWidth;
+ int xRestWidth;
+
+ xDrawWidth = completePixmap->width() - xScrollPos;
+ if (xDrawWidth > size.width()) {
+ xDrawWidth = size.width();
+ }
+
+
+ bitBlt(this, 0, 0, completePixmap, xScrollPos, 0, xDrawWidth);
+
+ if (xDrawWidth < size.width()) {
+ xRestWidth = size.width() - xDrawWidth;
+ bitBlt(this, xDrawWidth, 0, completePixmap, 0, 0, xRestWidth);
+ }
+}
+
+
+void WaInfo::setText(QString song)
+{
+ if (_text != song) {
+ _text = song;
+ pixmapChange();
+ }
+}
+
+QString WaInfo::text() const
+{
+ return _text;
+}
+
+
+void WaInfo::pixmapChange()
+{
+ int i;
+ const char *infoString = _text.latin1();
+
+ int x = 0;
+ int n=infoString ? strlen(infoString) : 0;
+
+ QSize size = sizeHint();
+
+ completePixmap->resize(QMAX(n * _WA_TEXT_WIDTH, size.width()), _WA_TEXT_HEIGHT);
+
+ for (i = 0; i < n; i++) {
+ WaSkinModel::instance()->getText(infoString[i], completePixmap, x, 0);
+ x += _WA_TEXT_WIDTH;
+ }
+
+ // if the size is now smaller than the with of this widget, we
+ // fill the pixmap with spaces
+ if (x < size.width()) {
+ while (x < size.width()) {
+ WaSkinModel::instance()->getText(' ', completePixmap, x, 0);
+ x += _WA_TEXT_WIDTH;
+ }
+ }
+
+ scrollerSetup();
+ update();
+}
+
+void WaInfo::mousePressEvent (QMouseEvent *e) {
+ if (e->button() == LeftButton)
+ xGrabbedPos = (e->x() + xScrollPos) % completePixmap->width();
+}
+
+void WaInfo::mouseMoveEvent (QMouseEvent * e) {
+ xScrollPos = -e->x() + xGrabbedPos;
+
+ if (xScrollPos < 0)
+ xScrollPos = completePixmap->width() - (-xScrollPos % completePixmap->width());
+ else
+ xScrollPos %= completePixmap->width();
+
+ update();
+}
+
+void WaInfo::mouseReleaseEvent (QMouseEvent *) {
+ xGrabbedPos = -1;
+}
+
+
+#include "waInfo.moc"
diff --git a/noatun/modules/winskin/waInfo.h b/noatun/modules/winskin/waInfo.h
new file mode 100644
index 00000000..513ff3cc
--- /dev/null
+++ b/noatun/modules/winskin/waInfo.h
@@ -0,0 +1,53 @@
+/*
+ standard Button for winamp Skin
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __WAINFO_H
+#define __WAINFO_H
+
+#include <qpainter.h>
+#include <qtimer.h>
+
+#include "waWidget.h"
+
+class WaInfo : public WaWidget {
+ Q_OBJECT
+
+ public:
+ WaInfo();
+ ~WaInfo();
+
+ void setText(QString song);
+ QString text() const;
+ void scrollerSetup();
+
+ protected:
+ void paintEvent(QPaintEvent * paintEvent);
+
+ void mousePressEvent (QMouseEvent * e);
+ void mouseMoveEvent (QMouseEvent * e);
+ void mouseReleaseEvent (QMouseEvent * e);
+
+ QPixmap *completePixmap;
+ QString _text;
+ QTimer *timer;
+ int xScrollPos;
+ int xScrollDirection;
+ int xGrabbedPos;
+
+ protected slots:
+ void pixmapChange();
+ void timeEvent();
+};
+#endif
diff --git a/noatun/modules/winskin/waJumpSlider.cpp b/noatun/modules/winskin/waJumpSlider.cpp
new file mode 100644
index 00000000..51633300
--- /dev/null
+++ b/noatun/modules/winskin/waJumpSlider.cpp
@@ -0,0 +1,78 @@
+/*
+ jumpslider for winamp skins
+ Copyright (C) 1998 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "waJumpSlider.h"
+
+WaJumpSlider::WaJumpSlider() : WaWidget(_WA_MAPPING_POS_BAR)
+{
+}
+
+WaJumpSlider::~WaJumpSlider()
+{
+}
+
+void WaJumpSlider::buildGui()
+{
+ ws = new WaSlider(_WA_MAPPING_POS_BAR, _WA_MAPPING_POS_BAR_SLIDER);
+ ws->setPixmapSliderButtonUp(_WA_SKIN_POS_BAR_SLIDER_NORM);
+ ws->setPixmapSliderButtonDown(_WA_SKIN_POS_BAR_SLIDER_PRES);
+ ws->setPixmapSliderBar(_WA_SKIN_POS_BAR);
+ ws->setRange(0, 100);
+ ws->setValue(0);
+
+ connect(ws, SIGNAL(sliderPressed()), this, SIGNAL(sliderPressed()));
+ connect(ws, SIGNAL(sliderReleased()), this, SLOT(releasedSlider()));
+ connect(ws, SIGNAL(valueChanged(int)), this, SIGNAL(valueChanged(int)));
+}
+
+void WaJumpSlider::setJumpRange(int val)
+{
+ if (val == -1)
+ ws->hideButton();
+ else {
+ ws->showButton();
+ ws->setRange(0, val);
+ }
+}
+
+int WaJumpSlider::jumpValue() {
+ return ws->value();
+}
+
+void WaJumpSlider::setJumpValue(int val)
+{
+ int currVal = ws->value();
+
+ if (currVal != val) {
+ ws->setValue(val);
+ }
+}
+
+void WaJumpSlider::releasedSlider() {
+ emit(jump(ws->value()));
+ emit(sliderReleased());
+}
+
+void WaJumpSlider::showEvent (QShowEvent *) {
+ ws->show();
+}
+
+void WaJumpSlider::hideEvent (QHideEvent *) {
+ ws->hide();
+}
+
+void WaJumpSlider::cancelDrag() {
+ ws->cancelDrag();
+}
+
+#include "waJumpSlider.moc"
diff --git a/noatun/modules/winskin/waJumpSlider.h b/noatun/modules/winskin/waJumpSlider.h
new file mode 100644
index 00000000..12c07808
--- /dev/null
+++ b/noatun/modules/winskin/waJumpSlider.h
@@ -0,0 +1,52 @@
+/*
+ jumpslider for winamp skins
+ Copyright (C) 1998 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __WAJUMPSLIDER_H
+#define __WAJUMPSLIDER_H
+
+#include "waSlider.h"
+#include "waWidget.h"
+
+class WaJumpSlider : public WaWidget {
+ Q_OBJECT
+
+ public:
+ WaJumpSlider();
+ ~WaJumpSlider();
+ void buildGui();
+
+ void setJumpRange(int val);
+
+ void setJumpValue(int val);
+ int jumpValue();
+
+ void cancelDrag();
+
+ protected:
+ WaSlider *ws;
+ void showEvent (QShowEvent *);
+ void hideEvent (QHideEvent *);
+
+ private slots:
+ void releasedSlider();
+
+ signals:
+ void jump(int seconds);
+ void sliderPressed();
+ void sliderReleased();
+ void valueChanged(int);
+};
+
+
+#endif
diff --git a/noatun/modules/winskin/waLabel.cpp b/noatun/modules/winskin/waLabel.cpp
new file mode 100644
index 00000000..8f3ddb60
--- /dev/null
+++ b/noatun/modules/winskin/waLabel.cpp
@@ -0,0 +1,65 @@
+/*
+ Standard label for Winskin
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2001 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include <qpainter.h>
+#include <qpixmap.h>
+
+#include "waLabel.h"
+#include "waSkinModel.h"
+
+WaLabel::WaLabel(int mapping) : WaWidget(mapping)
+{
+ connect(WaSkinModel::instance(), SIGNAL(skinChanged()),
+ this, SLOT(pixmapChange()));
+
+ completePixmap = new QPixmap();
+
+ QSize size = sizeHint();
+
+ completePixmap->resize(size.width(), size.height());
+}
+
+WaLabel::~WaLabel()
+{
+ delete completePixmap;
+}
+
+void WaLabel::paintEvent(QPaintEvent *)
+{
+ bitBlt(this, 0, 0, completePixmap);
+}
+
+void WaLabel::setText(const QString &new_text)
+{
+ int width = WaSkinModel::instance()->getMapGeometry(mapping).width();
+
+ // Fit the text to the widget
+ // This should always be three characters, but we generate its value anyway
+ _text = new_text.rightJustify(width / _WA_TEXT_WIDTH, ' ');
+
+ pixmapChange();
+
+ update();
+}
+
+void WaLabel::pixmapChange()
+{
+ const char *label_text = _text.latin1();
+ int n = label_text ? strlen(label_text) : 0;
+
+ for (int i = 0; i < n; i++)
+ WaSkinModel::instance()->getText(label_text[i], completePixmap,
+ i * _WA_TEXT_WIDTH, 0);
+}
+
+#include "waLabel.moc"
diff --git a/noatun/modules/winskin/waLabel.h b/noatun/modules/winskin/waLabel.h
new file mode 100644
index 00000000..f1470902
--- /dev/null
+++ b/noatun/modules/winskin/waLabel.h
@@ -0,0 +1,39 @@
+/*
+ standard Button for winamp Skin
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#ifndef __WABPS_H
+#define __WABPS_H
+
+#include <qpainter.h>
+#include "waWidget.h"
+
+class WaLabel : public WaWidget {
+ Q_OBJECT
+
+ public:
+ WaLabel(int mapping);
+ ~WaLabel();
+
+ void setText(const QString &text);
+ QString text() const { return _text; }
+
+ private:
+ void paintEvent(QPaintEvent *);
+
+ QPixmap *completePixmap;
+ QString _text;
+
+
+ private slots:
+ void pixmapChange();
+};
+#endif
diff --git a/noatun/modules/winskin/waMain.cpp b/noatun/modules/winskin/waMain.cpp
new file mode 100644
index 00000000..a5bb7fdc
--- /dev/null
+++ b/noatun/modules/winskin/waMain.cpp
@@ -0,0 +1,13 @@
+#include "waMain.h"
+
+WaMain::WaMain() : WaWidget(_WA_MAPPING_MAIN) {
+}
+
+WaMain::~WaMain() {
+}
+
+void WaMain::paintEvent(QPaintEvent *) {
+ paintPixmap(_WA_SKIN_MAIN);
+}
+
+#include "waMain.moc"
diff --git a/noatun/modules/winskin/waMain.h b/noatun/modules/winskin/waMain.h
new file mode 100644
index 00000000..06bcfe5d
--- /dev/null
+++ b/noatun/modules/winskin/waMain.h
@@ -0,0 +1,16 @@
+#ifndef __WAMAIN_H
+#define __WAMAIN_H
+
+#include "waWidget.h"
+
+class WaMain : WaWidget {
+ Q_OBJECT
+public:
+ WaMain();
+ ~WaMain();
+
+protected:
+ void paintEvent(QPaintEvent *);
+};
+
+#endif
diff --git a/noatun/modules/winskin/waRegion.cpp b/noatun/modules/winskin/waRegion.cpp
new file mode 100644
index 00000000..ab4d2e57
--- /dev/null
+++ b/noatun/modules/winskin/waRegion.cpp
@@ -0,0 +1,126 @@
+/*
+ Winamp Skin
+ Copyright (C) 2002 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include <ksimpleconfig.h>
+#include <qbitmap.h>
+#include <qpainter.h>
+#include <qregexp.h>
+
+#include "waSkinModel.h"
+#include "waSkins.h"
+#include "waRegion.h"
+
+WaRegion *windowRegion = NULL;
+
+// Hack around case-insensitivity in Winamp INI files by searching for common capitializations
+// Needs to be replaced with an all-custom loader after 3.0
+const char *pointListNames[] = {"PointList", "pointlist", "Pointlist", "pointList", "POINTLIST", 0};
+const char *numPointsNames[] = {"NumPoints", "numpoints", "Numpoints", "numPoints", "NUMPOINTS", 0};
+
+WaRegion::WaRegion(QString filename) {
+ // Load the region file, which happens to be in KConfig format
+ KSimpleConfig regionFile(filename, true);
+
+ // Clear our variables by default
+ window_mask = 0;
+ shade_mask = 0;
+
+ // Make the new bitmaps, default window size
+ window_mask = new QBitmap(WaSkinModel::instance()->getMapGeometry(_WA_MAPPING_MAIN).size(), true);
+ shade_mask = new QBitmap(WaSkinModel::instance()->getMapGeometry(_WA_MAPPING_TITLE).size(), true);
+
+ // Load the normal window mask data
+ regionFile.setGroup("Normal");
+
+ QValueList<int> num_points;
+ for (int x = 0;numPointsNames[x];x++) {
+ if (regionFile.hasKey(numPointsNames[x]))
+ num_points = parseList(regionFile.readEntry(numPointsNames[x]));
+ }
+
+ QValueList<int> point_list;
+ for (int x = 0;pointListNames[x];x++) {
+ if (regionFile.hasKey(pointListNames[x]))
+ point_list = parseList(regionFile.readEntry(pointListNames[x]));
+ }
+
+ // Now build the mask
+ buildPixmap(num_points, point_list, window_mask);
+
+ // Load the windowshade mask data
+ regionFile.setGroup("WindowShade");
+
+ num_points = parseList(regionFile.readEntry("NumPoints"));
+ point_list = parseList(regionFile.readEntry("PointList"));
+
+ // Now build the mask
+ buildPixmap(num_points, point_list, shade_mask);
+}
+
+WaRegion::~WaRegion() {
+ delete window_mask;
+ delete shade_mask;
+}
+
+void WaRegion::buildPixmap(const QValueList<int> &num_points_list, const QValueList<int> &points_list, QBitmap *dest) {
+ if (!num_points_list.count()) {
+ dest->fill(Qt::color1);
+ return;
+ }
+
+ QValueList<int>::const_iterator points = points_list.begin();
+
+ QPainter p(dest);
+
+ // Coordinates in REGION.TXT can go one pixel beyond the window size
+ QBitmap bm(dest->width()+1,dest->height()+1,true);
+ QPainter bmp(&bm);
+
+ bmp.setBrush(Qt::color1);
+ bmp.setPen(Qt::NoPen); // The polygon border itself should not be part of the visible window
+
+ // Go over each "region" in the file
+ for (QValueList<int>::const_iterator num_points = num_points_list.begin();num_points != num_points_list.end();num_points++) {
+ // Make a new point array
+ QPointArray point_array(*num_points);
+
+ // Populate it
+ for (int i = 0;i < *num_points;i++) {
+ int x = (*points++);
+ int y = (*points++);
+
+ point_array.setPoint(i, x, y);
+ }
+
+ // Now draw it as a filled polygon on the mask
+ bmp.drawPolygon(point_array);
+ }
+
+ p.drawPixmap(0,0,bm,0,0,dest->width(),dest->height());
+}
+
+
+// The winamp list format is absolutely insane, it will accept either
+// commas or whitespace as the delimiter. This function deals with
+// that.
+QValueList<int> WaRegion::parseList(const QString &list) const {
+ QValueList<int> temp_list;
+
+ if (list.isEmpty())
+ return temp_list;
+
+ QStringList open=QStringList::split(QRegExp("[,\\s]+"), list);
+ for (QStringList::Iterator i=open.begin(); i != open.end(); ++i)
+ temp_list.append((*i).toInt());
+
+ return temp_list;
+}
diff --git a/noatun/modules/winskin/waRegion.h b/noatun/modules/winskin/waRegion.h
new file mode 100644
index 00000000..853909c1
--- /dev/null
+++ b/noatun/modules/winskin/waRegion.h
@@ -0,0 +1,26 @@
+#ifndef WAREGION_H
+#define WAREGION_H
+
+#include <qcolor.h>
+#include <qstring.h>
+
+class WaRegion {
+public:
+ WaRegion(QString filename);
+ ~WaRegion();
+
+ const QBitmap *mainWindowMask() const { return window_mask; }
+ const QBitmap *mainWindowShadeMask() const { return shade_mask; }
+
+private:
+ QValueList<int> parseList(const QString &list) const;
+ void buildPixmap(const QValueList<int> &num_points, const QValueList<int> &point_list, QBitmap *dest);
+
+ QBitmap *window_mask;
+ QBitmap *shade_mask;
+};
+
+extern WaRegion *windowRegion;
+
+#endif
+
diff --git a/noatun/modules/winskin/waShadeMapping.h b/noatun/modules/winskin/waShadeMapping.h
new file mode 100644
index 00000000..d28d4ca8
--- /dev/null
+++ b/noatun/modules/winskin/waShadeMapping.h
@@ -0,0 +1,148 @@
+/*
+ mapping from file and id to pixmap, and global player map
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2002 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __WASHADEMAPPING_H
+#define __WASHADEMAPPING_H
+
+
+/**
+ This file is not a header file in the normal sense.
+ It define _directly_ variables, which are used in the
+ WaSkinLoader class.
+ Its a bit black voodoo magic.
+*/
+
+/**
+ The Skin file format was downloaded from the web.
+ http://www.algonet.et.se/~daniel7
+
+ Author: unknown
+*/
+
+#include "skinMap.h"
+#include "waSkins.h"
+
+static const SkinMap shadeMapToGui[] = {
+ {0, 0, 275, 14}, // _WA_MAPPING_MAIN
+ {168, 3, 9, 9}, // _WA_MAPPING_CBUTTONS_PREV
+ {177, 3, 9, 9}, // _WA_MAPPING_CBUTTONS_PLAY
+ {186, 3, 9, 9}, // _WA_MAPPING_CBUTTONS_PAUSE
+ {195, 3, 9, 9}, // _WA_MAPPING_CBUTTONS_STOP
+ {204, 3, 9, 9}, // _WA_MAPPING_CBUTTONS_NEXT
+ {215, 3, 9, 9}, // _WA_MAPPING_CBUTTONS_EJECT
+ {239, 41, 29, 12}, // _WA_MAPPING_MONOSTER_STEREO
+ {212, 41, 27, 12}, // _WA_MAPPING_MONOSTER_MONO
+ {210, 89, 28, 15}, // _WA_MAPPING_REPEAT
+ {164, 89, 46, 15}, // _WA_MAPPING_SHUFFLE
+ {242, 58, 23, 12}, // _WA_MAPPING_PLAYLIST
+ {219, 58, 23, 12}, // _WA_MAPPING_EQ
+ {107, 57, 68, 13}, // _WA_MAPPING_VOLUME_BAR
+ {0, 0, 13, 11}, // _WA_MAPPING_VOLUME_SLIDER
+ {177, 57, 38, 13}, // _WA_MAPPING_BALANCE_BAR
+ {0, 0, 13, 11}, // _WA_MAPPING_BALANCE_SLIDER
+ {24, 28, 11, 9}, // _WA_MAPPING_PLAYPAUS
+ {16, 72, 248, 10}, // _WA_MAPPING_POS_BAR
+ {0, 0, 29, 10}, // _WA_MAPPING_POS_BAR_SLIDER
+ {130, 4, 27, 6}, // _WA_MAPPING_DIGITS
+ {130, 4, 5, 6}, // _WA_MAPPING_MINUS
+ {135, 4, 5, 6}, // _WA_MAPPING_DIGIT_1
+ {140, 4, 5, 6}, // _WA_MAPPING_DIGIT_2
+ {147, 4, 5, 6}, // _WA_MAPPING_DIGIT_3
+ {152, 4, 5, 6}, // _WA_MAPPING_DIGIT_4
+ {24, 43, 75, 16}, // _WA_MAPPING_ANALYSER
+ {111, 43, 15, 6}, // _WA_MAPPING_BPS
+ {156, 43, 10, 6}, // _WA_MAPPING_FREQ
+ {112, 27, 152, 6}, // _WA_MAPPING_INFO
+ {0, 0, 274, 14}, // _WA_MAPPING_TITLE
+ {6, 3, 9, 9}, // _WA_MAPPING_TITLE_MENU
+ {244, 3, 9, 9}, // _WA_MAPPING_TITLE_MIN
+ {254, 3, 9, 9}, // _WA_MAPPING_TITLE_SHADE
+ {264, 3, 9, 9}, // _WA_MAPPING_TITLE_CLOSE
+ {10, 22, 8, 43} // _WA_MAPPING_CLUTTERBAR
+};
+
+
+static const SkinDesc shadeMapFromFile[] = {
+ {_WA_FILE_TITLEBAR, 27, 29, 275, 14}, // _WA_SKIN_MAIN
+ {_WA_FILE_TITLEBAR, 195, 32, 9, 9}, // _WA_SKIN_CBUTTONS_PREV_NORM
+ {_WA_FILE_TITLEBAR, 195, 32, 9, 9}, // _WA_SKIN_CBUTTONS_PREV_PRES
+ {_WA_FILE_TITLEBAR, 204, 32, 9, 9}, //_WA_SKIN_CBUTTONS_PLAY_NORM,
+ {_WA_FILE_TITLEBAR, 204, 32, 9, 9}, // _WA_SKIN_CBUTTONS_PLAY_PRES
+ {_WA_FILE_TITLEBAR, 213, 32, 9, 9}, // _WA_SKIN_CBUTTONS_PAUSE_NORM
+ {_WA_FILE_TITLEBAR, 213, 32, 9, 9}, // _WA_SKIN_CBUTTONS_PAUSE_PRES
+ {_WA_FILE_TITLEBAR, 222, 32, 9, 9}, // _WA_SKIN_CBUTTONS_STOP_NORM
+ {_WA_FILE_TITLEBAR, 222, 32, 9, 9}, // _WA_SKIN_CBUTTONS_STOP_PRES
+ {_WA_FILE_TITLEBAR, 231, 32, 9, 9}, // _WA_SKIN_CBUTTONS_NEXT_NORM
+ {_WA_FILE_TITLEBAR, 231, 32, 9, 9}, // _WA_SKIN_CBUTTONS_NEXT_PRES
+ {_WA_FILE_TITLEBAR, 242, 32, 9, 9}, // _WA_SKIN_CBUTTONS_EJECT_NORM
+ {_WA_FILE_TITLEBAR, 242, 32, 9, 9}, // _WA_SKIN_CBUTTONS_EJECT_PRESS
+ {_WA_FILE_MONOSTER, 0, 0, 29, 12}, // _WA_SKIN_MONOSTER_STEREO_TRUE
+ {_WA_FILE_MONOSTER, 0, 12, 29, 12}, // _WA_SKIN_MONOSTER_STEREO_FALSE
+ {_WA_FILE_MONOSTER, 29, 0, 27, 12}, // _WA_SKIN_MONOSTER_MONO_TRUE
+ {_WA_FILE_MONOSTER, 29, 12, 27, 12}, // _WA_SKIN_MONOSTER_MONO_FALSE
+ {_WA_FILE_TEXT, 0, 6, 50, 6}, // _WA_SKIN_NUMBERS
+ {_WA_FILE_TEXT, 75, 6, 5, 6}, // _WA_SKIN_NUMBERS_MINUS
+ {_WA_FILE_TEXT, 50, 12, 5, 6}, // _WA_SKIN_NUMBERS_BLANK
+ {_WA_FILE_SHUFREP, 0, 0, 28, 15}, // _WA_SKIN_SHUFREP_REPEAT_NOT_SET_NORM
+ {_WA_FILE_SHUFREP, 0, 15, 28, 15}, // _WA_SKIN_SHUFREP_REPEAT_NOT_SET_PRES
+ {_WA_FILE_SHUFREP, 0, 30, 28, 15}, // _WA_SKIN_SHUFREP_REPEAT_SET_NORM
+ {_WA_FILE_SHUFREP, 0, 45, 28, 15}, // _WA_SKIN_SHUFREP_REPEAT_SET_PRES
+ {_WA_FILE_SHUFREP, 28, 0, 47, 15}, // _WA_SKIN_SHUFREP_SHUFFLE_NOT_SET_NORM
+ {_WA_FILE_SHUFREP, 28, 15, 47, 15}, // _WA_SKIN_SHUFREP_SHUFFLE_NOT_SET_PRES
+ {_WA_FILE_SHUFREP, 28, 30, 47, 15}, // _WA_SKIN_SHUFREP_SHUFFLE_SET_NORM
+ {_WA_FILE_SHUFREP, 28, 45, 47, 15}, // _WA_SKIN_SHUFREP_SHUFFLE_SET_PRES
+ {_WA_FILE_SHUFREP, 23, 61, 23, 12}, // _WA_SKIN_SHUFREP_PLAYLIST_NOT_SET_NORM
+ {_WA_FILE_SHUFREP, 69, 61, 23, 12}, // _WA_SKIN_SHUFREP_PLAYLIST_NOT_SET_PRES
+ {_WA_FILE_SHUFREP, 23, 73, 23, 12}, // _WA_SKIN_SHUFREP_PLAYLIST_SET_NORM
+ {_WA_FILE_SHUFREP, 73, 23, 12}, // _WA_SKIN_SHUFREP_PLAYLIST_SET_PRES
+ {_WA_FILE_SHUFREP, 0, 61, 23, 12}, // _WA_SKIN_SHUFREP_EQ_NOT_SET_NORM
+ {_WA_FILE_SHUFREP, 46, 61, 23, 12}, // _WA_SKIN_SHUFREP_EQ_NOT_SET_PRES
+ {_WA_FILE_SHUFREP, 0, 73, 23, 12}, // _WA_SKIN_SHUFREP_EQ_SET_NORM
+ {_WA_FILE_SHUFREP, 46, 73, 23, 12}, // _WA_SKIN_SHUFREP_EQ_SET_PRES
+ {_WA_FILE_TEXT, 0, 0, 155, 18}, // _WA_SKIN_TEXT
+ {_WA_FILE_VOLUME, 0, 0, 68, 421}, // _WA_SKIN_VOLUME_BAR_ALL_BARS
+ {_WA_FILE_VOLUME, 0, 0, 68, 13}, // _WA_SKIN_VOLUME_BAR
+ {_WA_FILE_VOLUME, 0, 422, 14, 11}, // _WA_SKIN_VOLUME_SLIDER_NORM
+ {_WA_FILE_VOLUME, 15, 422, 14, 11}, // _WA_SKIN_VOLUME_SLIDER_PRES
+ {_WA_FILE_BALANCE, 9, 0, 38, 421}, // _WA_SKIN_BALANCE_BAR_ALL_BARS
+ {_WA_FILE_BALANCE, 9, 0, 38, 13}, // _WA_SKIN_BALANCE_BAR
+ {_WA_FILE_BALANCE, 0, 422, 14, 11}, // _WA_SKIN_BALANCE_SLIDER_NORM
+ {_WA_FILE_BALANCE, 15, 422, 14, 11}, // _WA_SKIN_BALANCE_SLIDER_PRES
+ {_WA_FILE_POSBAR, 0, 0, 248, 10}, // _WA_SKIN_POS_BAR
+ {_WA_FILE_POSBAR, 278, 0, 29, 10}, // _WA_SKIN_POS_BAR_SLIDER_NORM
+ {_WA_FILE_POSBAR, 248, 0, 29, 10}, // _WA_SKIN_POS_BAR_SLIDER_PRES
+ {_WA_FILE_PLAYPAUS, 1, 0, 8, 9}, // _WA_SKIN_PLAYPAUS_PLAY
+ {_WA_FILE_PLAYPAUS, 9, 0, 9, 9}, // _WA_SKIN_PLAYPAUS_PAUSE
+ {_WA_FILE_PLAYPAUS, 18, 0, 9, 9}, // _WA_SKIN_PLAYPAUS_STOP
+ {_WA_FILE_PLAYPAUS, 27, 0, 2, 9}, // _WA_SKIN_PLAYPAUS_FILLER
+ {_WA_FILE_PLAYPAUS, 36, 0, 3, 9}, // _WA_SKIN_PLAYPAUS_WORK_INDICATOR
+ {_WA_FILE_TITLEBAR, 27, 29, 275, 14}, // _WA_SKIN_TITLE_ACTIVE
+ {_WA_FILE_TITLEBAR, 27, 42, 275, 14}, // _WA_SKIN_TITLE_INACTIVE
+ {_WA_FILE_TITLEBAR, 0, 9, 9, 9}, // _WA_SKIN_TITLE_MENU_PRES
+ {_WA_FILE_TITLEBAR, 33, 32, 9, 9}, // _WA_SKIN_TITLE_MENU_NORM
+ {_WA_FILE_TITLEBAR, 33, 45, 9, 9}, // _WA_SKIN_TITLE_MENU_INACTIVE
+ {_WA_FILE_TITLEBAR, 9, 9, 9, 9}, // _WA_SKIN_TITLE_MIN_PRES
+ {_WA_FILE_TITLEBAR, 271, 32, 9, 9}, // _WA_SKIN_TITLE_MIN_NORM
+ {_WA_FILE_TITLEBAR, 271, 45, 9, 9}, // _WA_SKIN_TITLE_MIN_INACTIVE
+ {_WA_FILE_TITLEBAR, 9, 27, 9, 9}, // _WA_SKIN_TITLE_SHADE_PRES
+ {_WA_FILE_TITLEBAR, 281, 32, 9, 9}, // _WA_SKIN_TITLE_SHADE_NORM
+ {_WA_FILE_TITLEBAR, 281, 45, 9, 9}, // _WA_SKIN_TITLE_SHADE_INACTIVE
+ {_WA_FILE_TITLEBAR, 18, 9, 9, 9}, // _WA_SKIN_TITLE_CLOSE_PRES
+ {_WA_FILE_TITLEBAR, 291, 32, 9, 9}, // _WA_SKIN_TITLE_CLOSE_NORM
+ {_WA_FILE_TITLEBAR, 291, 45, 9, 9}, // _WA_SKIN_TITLE_CLOSE_INACTIVE
+ {_WA_FILE_TITLEBAR, 304, 0, 8, 43} // _WA_SKIN_CLUTTERBAR_DISABLED
+};
+
+#endif
diff --git a/noatun/modules/winskin/waSkin.cpp b/noatun/modules/winskin/waSkin.cpp
new file mode 100644
index 00000000..7d95047d
--- /dev/null
+++ b/noatun/modules/winskin/waSkin.cpp
@@ -0,0 +1,800 @@
+/*
+ Winamp Skin
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2001-2002 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include <kconfig.h>
+#include <qstringlist.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <khelpmenu.h>
+#include <kpopupmenu.h>
+#include <kurldrag.h>
+#include <kstdaction.h>
+#include <kaction.h>
+#include <qdragobject.h>
+#include <kstandarddirs.h>
+#include <kwin.h>
+#include <time.h>
+
+#include <noatun/playlist.h>
+#include <noatun/app.h>
+#include <noatun/stdaction.h>
+
+#include "waSkin.h"
+
+#include "waRegion.h"
+
+#include "waMain.h"
+#include "waClutterbar.h"
+#include "waDigit.h"
+#include "waInfo.h"
+#include "waButton.h"
+#include "waLabel.h"
+#include "waIndicator.h"
+#include "waTitleBar.h"
+
+#include "waJumpSlider.h"
+#include "waBalanceSlider.h"
+#include "waVolumeSlider.h"
+
+#include "waStatus.h"
+
+#include "waSkinManager.h"
+#include "waSkinModel.h"
+
+#include "guiSpectrumAnalyser.h"
+#include "fileInfo.h"
+
+WaSkin *_waskin_instance = NULL;
+
+WaSkin::WaSkin() : QWidget(0, "NoatunWinampSkin"), UserInterface()
+{
+ mJumpPressed = false;
+ mVolumePressed = false;
+ mBalancePressed = false;
+
+ _waskin_instance = this;
+
+ // Our really low-level render-y sort of stuff
+ waSkinModel = new WaSkinModel();
+ // Skin management stuff
+ waSkinManager = new WaSkinManager();
+
+ createHighLevelElements();
+ createButtons();
+ setMinimumSize(sizeHint());
+ setMaximumSize(sizeHint());
+
+ KWin::setType(this->winId(), NET::Override);
+ setBackgroundMode(NoBackground);
+
+ setAcceptDrops(true);
+
+ title_shaded = false;
+
+ // These slots make Young Hickory love us
+ connect( napp, SIGNAL(hideYourself()), this, SLOT(hide()) );
+ connect( napp, SIGNAL(showYourself()), this, SLOT(show()) );
+
+ connect(napp->player(), SIGNAL(playlistShown()), this,
+ SLOT(playlistShown()));
+ connect(napp->player(), SIGNAL(playlistHidden()), this,
+ SLOT(playlistHidden()));
+ connect(napp->player(), SIGNAL(loopTypeChange(int)), this,
+ SLOT(loopChange(int)));
+ connect(napp->player(), SIGNAL(newSong()), this, SLOT(newSong()));
+
+ connect(napp->player(), SIGNAL(timeout()), this, SLOT(timetick()));
+ KConfig *config=KGlobal::config();
+
+ config->setGroup("Winskin");
+ QString skin = config->readEntry("CurrentSkin", WaSkin::defaultSkin());
+
+ loadSkin(skin);
+
+ setCaption(i18n("Noatun"));
+ setIcon(SmallIcon("noatun"));
+
+
+ QObject::connect(waTitleBar, SIGNAL(shaded()), this, SLOT(shadeEvent()));
+ // connect to players signals - so we can update our display if someone else
+ // changes settings...
+
+ connect(napp->player(), SIGNAL(stopped()), this, SLOT(slotStopped()));
+ connect(napp->player(), SIGNAL(playing()), this, SLOT(slotPlaying()));
+ connect(napp->player(), SIGNAL(paused()), this, SLOT(slotPaused()));
+
+ napp->player()->handleButtons();
+
+ playlist->setToggled(napp->playlist()->listVisible());
+
+ shuffle->setToggled(false);
+ repeat->setToggled(false);
+ waBalanceSlider->setBalanceValue(0);
+ waVolumeSlider->setVolumeValue(napp->player()->volume());
+
+ newSong();
+
+ // HACK: We won't get focus events otherwise
+ setFocusPolicy(QWidget::ClickFocus);
+
+ show();
+}
+
+
+WaSkin::~WaSkin()
+{
+ delete waSkinManager;
+ waSkinManager = 0L;
+}
+
+void WaSkin::loadSkin(QString newSkinDir)
+{
+ waSkinManager->loadSkin(newSkinDir);
+
+ setMinimumSize(sizeHint());
+
+ if (title_shaded) {
+ // waSkinModel::load() resets our skin model :(
+ waSkinModel->setSkinModel(WA_MODEL_WINDOWSHADE);
+ setMask(*windowRegion->mainWindowShadeMask());
+ }
+ else {
+ setMask(*windowRegion->mainWindowMask());
+ }
+}
+
+QSize WaSkin::sizeHint() const
+{
+ QRect temp_rect;
+
+ temp_rect = waSkinModel->getGeometry(_WA_SKIN_MAIN);
+
+ return temp_rect.size();
+}
+
+
+void WaSkin::createButtons()
+{
+ prev = new WaButton(_WA_MAPPING_CBUTTONS_PREV);
+ play = new WaButton(_WA_MAPPING_CBUTTONS_PLAY);
+ pause = new WaButton(_WA_MAPPING_CBUTTONS_PAUSE);
+ stop = new WaButton(_WA_MAPPING_CBUTTONS_STOP);
+ next = new WaButton(_WA_MAPPING_CBUTTONS_NEXT);
+ eject = new WaButton(_WA_MAPPING_CBUTTONS_EJECT);
+ shuffle = new WaButton(_WA_MAPPING_SHUFFLE);
+ repeat = new WaButton(_WA_MAPPING_REPEAT);
+ playlist = new WaButton(_WA_MAPPING_PLAYLIST);
+ eq = new WaButton(_WA_MAPPING_EQ);
+
+ menu = new WaButton(_WA_MAPPING_TITLE_MENU);
+ menu->setPixmapUp(_WA_SKIN_TITLE_MENU_NORM);
+ menu->setPixmapDown(_WA_SKIN_TITLE_MENU_PRES);
+ connect(menu, SIGNAL(clicked()), this, SLOT(menuEvent()));
+
+ minimize = new WaButton(_WA_MAPPING_TITLE_MIN);
+ minimize->setPixmapUp(_WA_SKIN_TITLE_MIN_NORM);
+ minimize->setPixmapDown(_WA_SKIN_TITLE_MIN_PRES);
+ connect(minimize, SIGNAL(clicked()), this, SLOT(minimizeEvent()));
+
+ titleshade = new WaButton(_WA_MAPPING_TITLE_SHADE);
+ titleshade->setPixmapUp(_WA_SKIN_TITLE_SHADE_NORM);
+ titleshade->setPixmapDown(_WA_SKIN_TITLE_SHADE_PRES);
+ connect(titleshade, SIGNAL(clicked()), this, SLOT(shadeEvent()));
+
+ close = new WaButton(_WA_MAPPING_TITLE_CLOSE);
+ close->setPixmapUp(_WA_SKIN_TITLE_CLOSE_NORM);
+ close->setPixmapDown(_WA_SKIN_TITLE_CLOSE_PRES);
+ connect(close, SIGNAL(clicked()), this, SLOT(doClose()));
+
+ shuffle->setTogglable(true);
+ shuffle->show();
+
+ repeat->setTogglable(true);
+ playlist->setTogglable(true);
+
+ connect(shuffle, SIGNAL(toggleEvent(bool)),
+ this, SLOT(shuffleClickedEvent(bool)));
+
+ connect(repeat, SIGNAL(toggleEvent(bool)),
+ this, SLOT(repeatClickedEvent(bool)));
+
+ connect(playlist, SIGNAL(toggleEvent(bool)),
+ this, SLOT(playlistClickedEvent(bool)));
+
+ connect(eq, SIGNAL(clicked()),
+ this, SLOT(eqClickedEvent()));
+
+ prev->setPixmapUp(_WA_SKIN_CBUTTONS_PREV_NORM);
+ prev->setPixmapDown(_WA_SKIN_CBUTTONS_PREV_PRES);
+ connect(prev, SIGNAL(clicked()), napp->player(), SLOT(back()));
+
+ play->setPixmapUp(_WA_SKIN_CBUTTONS_PLAY_NORM);
+ play->setPixmapDown(_WA_SKIN_CBUTTONS_PLAY_PRES);
+ connect(play, SIGNAL(clicked()), this, SLOT(playCurrentEvent()));
+
+ pause->setPixmapUp(_WA_SKIN_CBUTTONS_PAUSE_NORM);
+ pause->setPixmapDown(_WA_SKIN_CBUTTONS_PAUSE_PRES);
+ connect(pause, SIGNAL(clicked()), this, SLOT(playPauseEvent()));
+
+
+ stop->setPixmapUp(_WA_SKIN_CBUTTONS_STOP_NORM);
+ stop->setPixmapDown(_WA_SKIN_CBUTTONS_STOP_PRES);
+ connect(stop, SIGNAL(clicked()), napp->player(), SLOT(stop()));
+
+
+ next->setPixmapUp(_WA_SKIN_CBUTTONS_NEXT_NORM);
+ next->setPixmapDown(_WA_SKIN_CBUTTONS_NEXT_PRES);
+ connect(next, SIGNAL(clicked()), napp->player(), SLOT(forward()));
+
+
+ eject->setPixmapUp(_WA_SKIN_CBUTTONS_EJECT_NORM);
+ eject->setPixmapDown(_WA_SKIN_CBUTTONS_EJECT_PRESS);
+ connect(eject, SIGNAL(clicked()), napp, SLOT(fileOpen()));
+
+ shuffle->setPixmapUp(_WA_SKIN_SHUFREP_SHUFFLE_NOT_SET_NORM);
+ shuffle->setPixmapDown(_WA_SKIN_SHUFREP_SHUFFLE_NOT_SET_PRES);
+ shuffle->setPixmapUpSelected(_WA_SKIN_SHUFREP_SHUFFLE_SET_NORM);
+ shuffle->setPixmapDownSelected(_WA_SKIN_SHUFREP_SHUFFLE_SET_PRES);
+ shuffle->setToggled(true);
+
+ repeat->setPixmapUp(_WA_SKIN_SHUFREP_REPEAT_NOT_SET_NORM);
+ repeat->setPixmapDown(_WA_SKIN_SHUFREP_REPEAT_NOT_SET_PRES);
+ repeat->setPixmapUpSelected(_WA_SKIN_SHUFREP_REPEAT_SET_NORM);
+ repeat->setPixmapDownSelected(_WA_SKIN_SHUFREP_REPEAT_SET_PRES);
+
+
+ eq->setPixmapUp(_WA_SKIN_SHUFREP_EQ_NOT_SET_NORM);
+ eq->setPixmapDown(_WA_SKIN_SHUFREP_EQ_NOT_SET_PRES);
+
+ playlist->setPixmapUp(_WA_SKIN_SHUFREP_PLAYLIST_NOT_SET_NORM);
+ playlist->setPixmapDown( _WA_SKIN_SHUFREP_PLAYLIST_NOT_SET_PRES);
+
+ playlist->setPixmapUpSelected(_WA_SKIN_SHUFREP_PLAYLIST_SET_NORM);
+ playlist->setPixmapDownSelected(_WA_SKIN_SHUFREP_PLAYLIST_SET_PRES);
+
+ waClutterbar = new WaClutterbar();
+}
+
+void WaSkin::createHighLevelElements()
+{
+ // Two most top-level elements
+ main = new WaMain();
+ waTitleBar = new WaTitleBar();
+
+ guiSpectrumAnalyser = new GuiSpectrumAnalyser();
+
+ waJumpSlider = new WaJumpSlider();
+ waJumpSlider->buildGui();
+
+ waVolumeSlider = new WaVolumeSlider();
+ waVolumeSlider->buildGui();
+
+ connect(waVolumeSlider, SIGNAL(volumeSetValue(int)),
+ this, SLOT(volumeSetValue(int)));
+ connect(waVolumeSlider, SIGNAL(sliderPressed()),
+ this, SLOT(volumeSliderPressed()));
+ connect(waVolumeSlider, SIGNAL(sliderReleased()),
+ this, SLOT(volumeSliderReleased()));
+
+ waBalanceSlider = new WaBalanceSlider();
+ waBalanceSlider->buildGui();
+
+ connect(waBalanceSlider, SIGNAL(balanceSetValue(int)),
+ this, SLOT(balanceSetValue(int)));
+ connect(waBalanceSlider, SIGNAL(sliderPressed()),
+ this, SLOT(balanceSliderPressed()));
+ connect(waBalanceSlider, SIGNAL(sliderReleased()),
+ this, SLOT(balanceSliderReleased()));
+
+ waDigit = new WaDigit();
+ connect(waDigit, SIGNAL(digitsClicked()), this, SLOT(digitsClicked()));
+
+ waBPS = new WaLabel(_WA_MAPPING_BPS);
+ waFreq = new WaLabel(_WA_MAPPING_FREQ);
+
+ waInfo = new WaInfo();
+
+ waStatus = new WaStatus();
+
+ waStereo = new WaIndicator(_WA_MAPPING_MONOSTER_STEREO, _WA_SKIN_MONOSTER_STEREO_TRUE, _WA_SKIN_MONOSTER_STEREO_FALSE);
+ waMono = new WaIndicator(_WA_MAPPING_MONOSTER_MONO, _WA_SKIN_MONOSTER_MONO_TRUE, _WA_SKIN_MONOSTER_MONO_FALSE);
+
+ connect(waJumpSlider, SIGNAL(jump(int)), this, SLOT(jump(int)));
+ connect(waJumpSlider, SIGNAL(sliderPressed()),
+ this, SLOT(jumpSliderPressed()));
+ connect(waJumpSlider, SIGNAL(sliderReleased()),
+ this, SLOT(jumpSliderReleased()));
+ connect(waJumpSlider, SIGNAL(valueChanged(int)),
+ this, SLOT(jumpValueChanged(int)));
+}
+
+
+void WaSkin::setChannels(int val)
+{
+ if (val <= 0) {
+ waStereo->setState(false);
+ waMono->setState(false);
+ } else if (val == 1) {
+ waStereo->setState(false);
+ waMono->setState(true);
+ } else {
+ waStereo->setState(true);
+ waMono->setState(false);
+ }
+}
+
+void WaSkin::shade() {
+ waSkinModel->setSkinModel(WA_MODEL_WINDOWSHADE);
+
+ setMinimumSize(sizeHint());
+ setMask(*windowRegion->mainWindowShadeMask());
+
+ title_shaded = true;
+}
+
+void WaSkin::unshade() {
+ waSkinModel->setSkinModel(WA_MODEL_NORMAL);
+
+ setMinimumSize(sizeHint());
+ setMask(*windowRegion->mainWindowMask());
+
+ title_shaded = false;
+}
+
+void WaSkin::focusOutEvent( QFocusEvent * ) {
+ menu->setPixmapUp(_WA_SKIN_TITLE_MENU_INACTIVE);
+ menu->update();
+
+ minimize->setPixmapUp(_WA_SKIN_TITLE_MIN_INACTIVE);
+ minimize->update();
+
+ titleshade->setPixmapUp(_WA_SKIN_TITLE_SHADE_INACTIVE);
+ titleshade->update();
+
+ close->setPixmapUp(_WA_SKIN_TITLE_CLOSE_INACTIVE);
+ close->update();
+
+ waTitleBar->setState(false);
+}
+
+void WaSkin::focusInEvent( QFocusEvent * ) {
+ menu->setPixmapUp(_WA_SKIN_TITLE_MENU_NORM);
+ menu->update();
+
+ minimize->setPixmapUp(_WA_SKIN_TITLE_MIN_NORM);
+ minimize->update();
+
+ titleshade->setPixmapUp(_WA_SKIN_TITLE_SHADE_NORM);
+ titleshade->update();
+
+ close->setPixmapUp(_WA_SKIN_TITLE_CLOSE_NORM);
+ close->update();
+
+ waTitleBar->setState(true);
+}
+
+void WaSkin::wheelEvent(QWheelEvent *e) {
+ // Get the current volume
+ int newVolume = napp->player()->volume();
+
+ // Wheel events return needlessly large "deltas", normalize it a bit
+ newVolume += e->delta() / 24;
+ napp->player()->setVolume(newVolume);
+}
+
+void WaSkin::repeatClickedEvent(bool)
+{
+ updateLoopStyle();
+}
+
+void WaSkin::shuffleClickedEvent(bool)
+{
+ updateLoopStyle();
+}
+
+void WaSkin::updateLoopStyle() {
+ if (shuffle->toggled()) {
+ napp->player()->loop(Player::Random);
+ }
+ else {
+ int loopVal = repeat->toggled() ? Player::Playlist : Player::None;
+ napp->player()->loop(loopVal);
+ }
+}
+
+void WaSkin::playlistClickedEvent(bool)
+{
+ napp->playlist()->toggleList();
+}
+
+void WaSkin::eqClickedEvent()
+{
+ napp->equalizerView();
+}
+
+void WaSkin::jump(int val)
+{
+ if (napp->player()->isStopped()) {
+ waJumpSlider->setJumpValue(0);
+ } else {
+ napp->player()->skipTo((int) (val * 1000));
+ }
+}
+
+void WaSkin::jumpSliderPressed()
+{
+ mJumpPressed = true;
+ jumpValueChanged(waJumpSlider->jumpValue());
+}
+
+
+void WaSkin::jumpSliderReleased()
+{
+ mJumpPressed = false;
+ waInfo->setText(getTitleString());
+}
+
+void WaSkin::jumpValueChanged(int val)
+{
+ if (mJumpPressed && !napp->player()->isStopped()) {
+ QString timeStr = i18n("Seek to: %1/%2 (%3%)").
+ arg(getTimeString(val * 1000)).
+ arg(getTimeString(napp->player()->getLength())).
+ arg((val * 1000 * 100) / napp->player()->getLength());
+ waInfo->setText(timeStr);
+ }
+}
+
+QString WaSkin::getTitleString() {
+ int length;
+ QString title = "";
+
+ if (!napp->playlist()->current()) {
+ title = "Noatun ";
+ title += QString::number(NOATUN_MAJOR) + ".";
+ title += QString::number(NOATUN_MINOR) + ".";
+ title += QString::number(NOATUN_PATCHLEVEL);
+ }
+ else {
+ length = napp->playlist()->current().length();
+
+ title = napp->playlist()->current().title();
+
+ if (length >= 0)
+ title += " (" + getTimeString(length) + ")";
+
+ if (title.length() > 30) {
+ // It's scrolling; provide the nice, friendly seperator.
+ title += " *** ";
+ }
+ }
+
+ return title;
+}
+
+QString WaSkin::getTimeString(int milliseconds, bool truncate) {
+ int seconds = abs(milliseconds / 1000);
+ QString ret = "";
+
+ // Do we need to convert to hours:minutes instead of minutes:seconds?
+ if (truncate && (abs(seconds) >= (100 * 60)))
+ seconds /= 60;
+
+ // Print the optional minus sign, hours/minutes, a colon, and then minutes/seconds.
+ ret.sprintf("%s%.2d:%.2d", ((milliseconds < 0) ? "-" : ""), seconds / 60, seconds % 60);
+
+ return ret;
+}
+
+void WaSkin::menuEvent() {
+ NoatunStdAction::ContextMenu::showContextMenu(mapToGlobal(QPoint(0, 14)));
+}
+
+void WaSkin::minimizeEvent() {
+ showMinimized();
+}
+
+void WaSkin::shadeEvent()
+{
+ if (!title_shaded)
+ shade();
+ else
+ unshade();
+}
+
+void WaSkin::doUnload() {
+ unload();
+}
+
+void WaSkin::doClose() {
+ QTimer::singleShot(0, this, SLOT(doUnload()));
+}
+
+void WaSkin::dragEnterEvent(QDragEnterEvent * event)
+{
+ // accept uri drops only
+ event->accept(KURLDrag::canDecode(event));
+}
+
+void WaSkin::dropEvent(QDropEvent * event)
+{
+ KURL::List uri;
+ if (KURLDrag::decode(event, uri))
+ {
+ for (KURL::List::Iterator i = uri.begin(); i != uri.end(); ++i)
+ napp->player()->openFile(*i, false);
+ }
+}
+
+void WaSkin::balanceSliderPressed()
+{
+ mBalancePressed = true;
+ balanceSetValue(0);
+}
+
+void WaSkin::balanceSliderReleased()
+{
+
+ mBalancePressed = false;
+ waBalanceSlider->setBalanceValue(0);
+
+ waInfo->setText(getTitleString());
+}
+
+void WaSkin::balanceSetValue(int val)
+{
+ if (val == 0) {
+ waInfo->setText(i18n("Balance: Center"));
+ }
+ else if (val < 0) {
+ waInfo->setText(i18n("Balance: %1% Left").arg(-val));
+ } else {
+ waInfo->setText(i18n("Balance: %1% Right").arg(val));
+ }
+}
+
+void WaSkin::playCurrentEvent()
+{
+ if (napp->player()->isPaused())
+ napp->player()->playpause();
+ else
+ napp->player()->playCurrent();
+}
+
+void WaSkin::playPauseEvent()
+{
+ if (!napp->player()->isStopped())
+ napp->player()->playpause();
+}
+
+
+void WaSkin::loopChange(int loopType)
+{
+ shuffle->setToggled(loopType == Player::Random);
+
+ if (loopType != Player::Random)
+ repeat->setToggled(loopType != Player::None);
+}
+
+void WaSkin::playlistShown()
+{
+ playlist->setToggled(true);
+}
+
+void WaSkin::playlistHidden()
+{
+ playlist->setToggled(false);
+}
+
+void WaSkin::newSong()
+{
+ if (napp->player()->getLength() == -1)
+ waJumpSlider->hide();
+ else
+ waJumpSlider->show();
+
+ mJumpPressed = false;
+ waJumpSlider->cancelDrag();
+
+ timetick();
+}
+
+void WaSkin::timetick()
+{
+ int mLength;
+
+ if (!mVolumePressed && !mBalancePressed && !mJumpPressed)
+ waInfo->setText(getTitleString());
+
+ if (!napp->player()->current())
+ return;
+
+ mLength = (int) napp->player()->getLength() / 1000;
+ if (mLength < 0)
+ mLength = 0;
+
+ waJumpSlider->setJumpRange(mLength);
+
+ digitsClicked();
+
+ int time = 0;
+ if (napp->player()->current())
+ time = (int) napp->player()->getTime() / 1000;
+
+ if (!mJumpPressed)
+ waJumpSlider->setJumpValue(time);
+
+ waVolumeSlider->setVolumeValue(napp->player()->volume());
+}
+
+void WaSkin::digitsClicked() {
+ if (!waDigit->timeReversed() || (napp->player()->getLength() == -1)) {
+ // Setting truncate=true means we want setTime to return
+ // no more than 6 digits (-xx:yy)
+
+ if (napp->player()->getTime() != -1)
+ waDigit->setTime(getTimeString(napp->player()->getTime(), true));
+ else
+ waDigit->setTime(getTimeString(0, true));
+ }
+ else {
+ int rem_time = napp->player()->getTime() - napp->player()->getLength();
+
+ // Setting truncate=true means we want setTime to return
+ // no more than 6 digits (-xx:yy)
+ waDigit->setTime(getTimeString(rem_time, true));
+ }
+}
+
+void WaSkin::volumeSliderPressed()
+{
+ mVolumePressed = true;
+ volumeSetValue(napp->player()->volume());
+}
+
+void WaSkin::volumeSliderReleased()
+{
+ mVolumePressed = false;
+ waInfo->setText(getTitleString());
+}
+
+void WaSkin::volumeSetValue(int val)
+{
+ if (mVolumePressed)
+ waInfo->setText(i18n("Volume: %1%").arg(val));
+
+ napp->player()->setVolume(val);
+}
+
+void WaSkin::slotPlaying()
+{
+ waStatus->setStatus(STATUS_PLAYING);
+
+ if (!napp->playlist()->current()) {
+ return;
+ }
+
+ fileInfo info(napp->playlist()->current());
+
+ if (!info.bps())
+ waBPS->setText("");
+ else
+ waBPS->setText(QString::number(info.bps()));
+
+ if (!info.KHz())
+ waFreq->setText("");
+ else
+ waFreq->setText(QString::number(info.KHz() / 1000));
+
+ setChannels(info.channelCount());
+ guiSpectrumAnalyser->resumeVisualization();
+
+ if (napp->player()->getLength() == -1)
+ waJumpSlider->hide();
+ else
+ waJumpSlider->show();
+
+ timetick();
+}
+
+void WaSkin::slotStopped()
+{
+ waStatus->setStatus(STATUS_STOPPED);
+
+ waDigit->setTime("");
+
+ waBPS->setText("");
+ waFreq->setText("");
+ setChannels(0);
+
+ waJumpSlider->setJumpValue(0);
+ // -1 == disable jump bar
+ waJumpSlider->setJumpRange(-1);
+
+
+ mJumpPressed = false;
+ waJumpSlider->cancelDrag();
+
+ waJumpSlider->hide();
+
+ guiSpectrumAnalyser->pauseVisualization();
+}
+
+void WaSkin::slotPaused()
+{
+ waStatus->setStatus(STATUS_PAUSED);
+}
+
+void WaSkin::keyPressEvent(QKeyEvent *e) {
+ switch(e->key()) {
+ case Key_Up:
+ napp->player()->setVolume(napp->player()->volume() + 5);
+ break;
+ case Key_Down:
+ napp->player()->setVolume(napp->player()->volume() - 5);
+ break;
+ case Key_Left:
+ if (napp->player()->current())
+ napp->player()->skipTo(napp->player()->getTime() - 5000);
+
+ break;
+ case Key_Right:
+ if (napp->player()->current())
+ napp->player()->skipTo(napp->player()->getTime() + 5000);
+
+ break;
+
+ case Key_Z:
+ napp->player()->back();
+ break;
+
+ case Key_X:
+ if (napp->player()->isPaused())
+ napp->player()->playpause();
+ else
+ napp->player()->playCurrent();
+
+ break;
+
+ case Key_C:
+ if (!napp->player()->isStopped())
+ napp->player()->playpause();
+
+ break;
+
+ case Key_V:
+ napp->player()->stop();
+ break;
+
+ case Key_B:
+ napp->player()->forward();
+ break;
+
+ case Key_R:
+ repeat->setToggled(!repeat->toggled());
+ updateLoopStyle();
+ break;
+
+ case Key_S:
+ shuffle->setToggled(!shuffle->toggled());
+ updateLoopStyle();
+ break;
+ }
+}
+
+QString WaSkin::defaultSkin() {
+ return "Winamp";
+}
+
+#include "waSkin.moc"
diff --git a/noatun/modules/winskin/waSkin.h b/noatun/modules/winskin/waSkin.h
new file mode 100644
index 00000000..7a2db9e1
--- /dev/null
+++ b/noatun/modules/winskin/waSkin.h
@@ -0,0 +1,177 @@
+/*
+ Winamp Skin
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2001 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __WASKIN_H
+#define __WASKIN_H
+
+#include <noatun/plugin.h>
+#include <noatun/app.h>
+#include <noatun/player.h>
+#include <qwidget.h>
+
+// Use forward declarations so we compile in a finite time
+class WaSkinModel;
+class WaSkinManager;
+
+class WaButton;
+class WaDigit;
+class WaLabel;
+class WaInfo;
+class WaMain;
+class WaIndicator;
+class WaTitleBar;
+class WaClutterbar;
+class WaStatus;
+
+class WaJumpSlider;
+class WaVolumeSlider;
+class WaBalanceSlider;
+
+class GuiSpectrumAnalyser;
+
+class WaSkin;
+extern WaSkin *_waskin_instance;
+
+class WaSkin : public QWidget, public UserInterface {
+ Q_OBJECT
+ NOATUNPLUGIND
+ WaSkinModel *waSkinModel;
+ WaSkinManager *waSkinManager;
+
+ public:
+ WaSkin();
+ ~WaSkin();
+
+ int getSkinId();
+
+ void loadSkin(QString skinDir);
+ void setChannels(int val);
+
+ QSize sizeHint() const;
+
+ static QString defaultSkin();
+ static WaSkin *instance() { return _waskin_instance; }
+
+ public slots:
+ void repeatClickedEvent(bool);
+ void shuffleClickedEvent(bool);
+ void playlistClickedEvent(bool);
+ void eqClickedEvent();
+
+ // seek bar
+ void jump(int second);
+ void jumpSliderPressed();
+ void jumpSliderReleased();
+ void jumpValueChanged(int);
+
+ void menuEvent();
+ void minimizeEvent();
+ void shadeEvent();
+ void doUnload();
+ void doClose();
+
+ // balance
+ void balanceSliderPressed();
+ void balanceSetValue(int val);
+ void balanceSliderReleased();
+
+ // volume
+ void volumeSliderPressed();
+ void volumeSetValue(int val);
+ void volumeSliderReleased();
+
+ void playCurrentEvent();
+ void playPauseEvent();
+
+ void loopChange(int loopType);
+ void playlistShown();
+ void playlistHidden();
+ void newSong();
+
+ void timetick();
+ void digitsClicked();
+
+ void slotPlaying();
+ void slotStopped();
+ void slotPaused();
+
+ WaSkinModel *skinModel() { return waSkinModel; }
+ WaSkinManager *skinManager() { return waSkinManager; }
+ WaInfo *skinInfo() { return waInfo; }
+
+ protected:
+ void updateLoopStyle();
+
+ void createButtons();
+ void createHighLevelElements();
+
+ void keyPressEvent(QKeyEvent *);
+
+ void shade();
+ void unshade();
+
+ QString getTitleString();
+ QString getTimeString(int milliseconds, bool truncate = false);
+
+ void focusOutEvent ( QFocusEvent * );
+ void focusInEvent ( QFocusEvent * );
+
+ void dragEnterEvent(QDragEnterEvent * event);
+ void dropEvent(QDropEvent * event);
+
+ void wheelEvent(QWheelEvent *e);
+
+ WaButton *prev;
+ WaButton *play;
+ WaButton *pause;
+ WaButton *stop;
+ WaButton *next;
+ WaButton *eject;
+ WaButton *shuffle;
+ WaButton *repeat;
+ WaButton *playlist;
+ WaButton *eq;
+
+ WaButton *menu;
+ WaButton *minimize;
+ WaButton *titleshade;
+ WaButton *close;
+
+ WaJumpSlider *waJumpSlider;
+ WaVolumeSlider *waVolumeSlider;
+ WaBalanceSlider *waBalanceSlider;
+ WaDigit *waDigit;
+
+ WaLabel *waBPS;
+ WaLabel *waFreq;
+
+ WaInfo *waInfo;
+ WaStatus *waStatus;
+ WaIndicator *waStereo;
+ WaIndicator *waMono;
+
+ WaMain *main;
+ WaTitleBar *waTitleBar;
+ WaClutterbar *waClutterbar;
+
+ GuiSpectrumAnalyser *guiSpectrumAnalyser;
+
+ bool title_shaded;
+
+ bool mJumpPressed;
+ bool mBalancePressed;
+ bool mVolumePressed;
+};
+#endif
diff --git a/noatun/modules/winskin/waSkinManager.cpp b/noatun/modules/winskin/waSkinManager.cpp
new file mode 100644
index 00000000..5cde117b
--- /dev/null
+++ b/noatun/modules/winskin/waSkinManager.cpp
@@ -0,0 +1,127 @@
+#include <kglobal.h>
+#include <qfileinfo.h>
+#include <qregexp.h>
+#include <kstandarddirs.h>
+#include <qdir.h>
+#include <kdebug.h>
+#include <kmimetype.h>
+#include <kio/job.h>
+#include <kurl.h>
+#include <kio/netaccess.h>
+#include <kzip.h>
+
+#include "waSkinManager.h"
+#include "waSkinModel.h"
+
+WaSkinManager::WaSkinManager() : DCOPObject("WaSkinManager") {
+}
+
+WaSkinManager::~WaSkinManager() {
+}
+
+QStringList WaSkinManager::availableSkins() {
+ QStringList skinDirs = KGlobal::dirs()->findDirs("data", "noatun/skins/winamp");
+ QStringList skin_list;
+
+ // This loop adds them all to our skin list
+ for(unsigned int x = 0;x < skinDirs.count();x++) {
+ QDir skinQDir(skinDirs[x]);
+
+ // We only want directories, although there shouldn't be anything else
+ skinQDir.setFilter( QDir::Dirs );
+ // I guess name is as good as any
+ skinQDir.setSorting( QDir::Name );
+
+ for (unsigned int y = 0;y < skinQDir.count();y++) {
+ QStringList skins = skinQDir.entryList(QDir::Dirs, QDir::Name);
+
+ // We really don't care for '.' and '..'
+ if (skinQDir[y][0] != (char)'.') {
+ // Add ourselves to the list, using our directory name
+ skin_list += skinQDir[y];
+ }
+ }
+ }
+
+ return skin_list;
+}
+
+QString WaSkinManager::currentSkin() {
+ return mCurrentSkin;
+}
+
+QString WaSkinManager::defaultSkin() {
+ return "Winamp";
+}
+
+bool WaSkinManager::loadSkin(QString skinName) {
+ QStringList skins = KGlobal::dirs()->findDirs("data", "noatun/skins/winamp/" + skinName);
+
+ if (!skins.count())
+ mCurrentSkin = defaultSkin();
+ else
+ mCurrentSkin = skinName;
+
+ return _waskinmodel_instance->load(skins[0]);
+}
+
+bool WaSkinManager::installSkin(QString _url) {
+ QString location = KGlobal::dirs()->saveLocation("data", "noatun/skins/winamp");
+ KURL url(_url);
+ QString mimetype = KMimeType::findByURL(_url)->name();
+
+ if (mimetype == "inode/directory")
+ {
+ KIO::Job *job = KIO::copy(url, location, !url.isLocalFile());
+ connect(job, SIGNAL(result(KIO::Job *)), this, SIGNAL(updateSkinList()));
+ return true;
+ }
+ else if ((mimetype == "interface/x-winamp-skin") || (mimetype == "application/x-zip"))
+ {
+ if (!url.isLocalFile())
+ return false;
+
+ QString base_path;
+ base_path = location + "/" + QFileInfo(url.path()).baseName().replace(QRegExp("_"), " ");
+ KIO::Job *job = KIO::copy("zip:" + url.path(), base_path);
+ connect(job, SIGNAL(result(KIO::Job *)), this, SIGNAL(updateSkinList()));
+
+ return true;
+ }
+
+ return false;
+}
+
+bool WaSkinManager::removeSkin(QString skinName) {
+ if (!skinRemovable(skinName))
+ return false;
+
+ QStringList skins = KGlobal::dirs()->findDirs("data", "noatun/skins/winamp/" + skinName);
+
+ KIO::Job *job = KIO::del(KURL(skins[0]), false, false);
+ connect(job, SIGNAL(result(KIO::Job *)), this, SIGNAL(updateSkinList()));
+
+ return true;
+}
+
+bool WaSkinManager::skinRemovable(QString skinName) {
+ QStringList skins = KGlobal::dirs()->findDirs("data", "noatun/skins/winamp/" + skinName);
+
+ if (!skins.count())
+ return false;
+
+ QFileInfo info(skins[0]);
+ return info.isWritable();
+}
+
+QStringList WaSkinManager::skinMimeTypes() {
+ QStringList temp;
+
+ temp.append("interface/x-winamp-skin");
+ temp.append("application/x-zip");
+ temp.append("inode/directory");
+
+ return temp;
+}
+
+#include "waSkinManager.moc"
diff --git a/noatun/modules/winskin/waSkinManager.h b/noatun/modules/winskin/waSkinManager.h
new file mode 100644
index 00000000..17b21daf
--- /dev/null
+++ b/noatun/modules/winskin/waSkinManager.h
@@ -0,0 +1,39 @@
+#ifndef _WASKINMANAGER_H
+#define _WASKINMANAGER_H
+
+#include <dcopobject.h>
+#include <qobject.h>
+#include <qstringlist.h>
+#include <qstring.h>
+#include <qmap.h>
+
+class WaSkinManager : public QObject, public DCOPObject {
+Q_OBJECT
+K_DCOP
+
+public:
+ WaSkinManager();
+ ~WaSkinManager();
+
+k_dcop:
+ QStringList availableSkins();
+ QString currentSkin();
+ bool loadSkin(QString skinName);
+
+ QString defaultSkin();
+
+ bool installSkin(QString url);
+
+ bool skinRemovable(QString skinName);
+ bool removeSkin(QString skinName);
+
+ QStringList skinMimeTypes();
+
+signals:
+ void updateSkinList();
+
+private:
+ QString mCurrentSkin;
+};
+
+#endif
diff --git a/noatun/modules/winskin/waSkinMapping.h b/noatun/modules/winskin/waSkinMapping.h
new file mode 100644
index 00000000..a3dfc9c5
--- /dev/null
+++ b/noatun/modules/winskin/waSkinMapping.h
@@ -0,0 +1,148 @@
+/*
+ mapping from file and id to pixmap, and global player map
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2002 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __WASKINMAPPING_H
+#define __WASKINMAPPING_H
+
+
+/**
+ This file is not a header file in the normal sense.
+ It define _directly_ variables, which are used in the
+ WaSkinLoader class.
+ Its a bit black voodoo magic.
+*/
+
+/**
+ The Skin file format was downloaded from the web.
+ http://www.algonet.et.se/~daniel7
+
+ Author: unknown
+*/
+
+#include "skinMap.h"
+#include "waSkins.h"
+
+static const SkinMap normalMapToGui[] = {
+ {0, 0, 275, 116}, // _WA_MAPPING_MAIN
+ {16, 88, 23, 18}, // _WA_MAPPING_CBUTTONS_PREV
+ {16 + 1 * 23, 88, 23, 18}, // _WA_MAPPING_CBUTTONS_PLAY
+ {16 + 2 * 23, 88, 23, 18}, // _WA_MAPPING_CBUTTONS_PAUSE
+ {16 + 3 * 23, 88, 23, 18}, // _WA_MAPPING_CBUTTONS_STOP
+ {16 + 4 * 23, 88, 22, 18}, // _WA_MAPPING_CBUTTONS_NEXT
+ {136, 89, 22, 16}, // _WA_MAPPING_CBUTTONS_EJECT
+ {239, 41, 29, 12}, // _WA_MAPPING_MONOSTER_STEREO
+ {212, 41, 27, 12}, // _WA_MAPPING_MONOSTER_MONO
+ {210, 89, 28, 15}, // _WA_MAPPING_REPEAT
+ {164, 89, 46, 15}, // _WA_MAPPING_SHUFFLE
+ {242, 58, 23, 12}, // _WA_MAPPING_PLAYLIST
+ {219, 58, 23, 12}, // _WA_MAPPING_EQ
+ {107, 57, 68, 13}, // _WA_MAPPING_VOLUME_BAR
+ {0, 0, 14, 11}, // _WA_MAPPING_VOLUME_SLIDER
+ {177, 57, 38, 13}, // _WA_MAPPING_BALANCE_BAR
+ {0, 0, 14, 11}, // _WA_MAPPING_BALANCE_SLIDER
+ {24, 28, 11, 9}, // _WA_MAPPING_PLAYPAUS
+ {16, 72, 248, 10}, // _WA_MAPPING_POS_BAR
+ {0, 0, 29, 10}, // _WA_MAPPING_POS_BAR_SLIDER
+ {40, 26, 63, 13}, // _WA_MAPPING_DIGITS
+ {40, 32, 5, 1}, // _WA_MAPPING_MINUS
+ {48, 26, 9, 13}, // _WA_MAPPING_DIGIT_1
+ {60, 26, 9, 13}, // _WA_MAPPING_DIGIT_2
+ {78, 26, 9, 13}, // _WA_MAPPING_DIGIT_3
+ {90, 26, 9, 13}, // _WA_MAPPING_DIGIT_4
+ {24, 43, 75, 16}, // _WA_MAPPING_ANALYSER
+ {111, 43, 15, 6}, // _WA_MAPPING_BPS
+ {156, 43, 10, 6}, // _WA_MAPPING_FREQ
+ {112, 27, 152, 6}, // _WA_MAPPING_INFO
+ {0, 0, 274, 14}, // _WA_MAPPING_TITLE
+ {6, 3, 9, 9}, // _WA_MAPPING_TITLE_MENU
+ {244, 3, 9, 9}, // _WA_MAPPING_TITLE_MIN
+ {254, 3, 9, 9}, // _WA_MAPPING_TITLE_SHADE
+ {264, 3, 9, 9}, // _WA_MAPPING_TITLE_CLOSE
+ {10, 22, 8, 43} // _WA_MAPPING_CLUTTERBAR
+};
+
+
+static const SkinDesc normalMapFromFile[] = {
+ {_WA_FILE_MAIN, 0, 0, 275, 116}, // _WA_SKIN_MAIN
+ {_WA_FILE_CBUTTONS, 0, 0, 23, 18}, // _WA_SKIN_CBUTTONS_PREV_NORM
+ {_WA_FILE_CBUTTONS, 0, 18, 23, 18}, // _WA_SKIN_CBUTTONS_PREV_PRES
+ {_WA_FILE_CBUTTONS, 23, 0, 23, 18}, //_WA_SKIN_CBUTTONS_PLAY_NORM,
+ {_WA_FILE_CBUTTONS, 23, 18, 23, 18}, // _WA_SKIN_CBUTTONS_PLAY_PRES
+ {_WA_FILE_CBUTTONS, 46, 0, 23, 18}, // _WA_SKIN_CBUTTONS_PAUSE_NORM
+ {_WA_FILE_CBUTTONS, 46, 18, 23, 18}, // _WA_SKIN_CBUTTONS_PAUSE_PRES
+ {_WA_FILE_CBUTTONS, 69, 0, 23, 18}, // _WA_SKIN_CBUTTONS_STOP_NORM
+ {_WA_FILE_CBUTTONS, 69, 18, 23, 18}, // _WA_SKIN_CBUTTONS_STOP_PRES
+ {_WA_FILE_CBUTTONS, 92, 0, 22, 18}, // _WA_SKIN_CBUTTONS_NEXT_NORM
+ {_WA_FILE_CBUTTONS, 92, 18, 22, 18}, // _WA_SKIN_CBUTTONS_NEXT_PRES
+ {_WA_FILE_CBUTTONS, 114, 0, 22, 16}, // _WA_SKIN_CBUTTONS_EJECT_NORM
+ {_WA_FILE_CBUTTONS, 114, 16, 22, 16}, // _WA_SKIN_CBUTTONS_EJECT_PRESS
+ {_WA_FILE_MONOSTER, 0, 0, 29, 12}, // _WA_SKIN_MONOSTER_STEREO_TRUE
+ {_WA_FILE_MONOSTER, 0, 12, 29, 12}, // _WA_SKIN_MONOSTER_STEREO_FALSE
+ {_WA_FILE_MONOSTER, 29, 0, 27, 12}, // _WA_SKIN_MONOSTER_MONO_TRUE
+ {_WA_FILE_MONOSTER, 29, 12, 27, 12}, // _WA_SKIN_MONOSTER_MONO_FALSE
+ {_WA_FILE_NUMBERS, 0, 0, 99, 13}, // _WA_SKIN_NUMBERS
+ {_WA_FILE_NUMBERS, 20, 6, 5, 1}, // _WA_SKIN_NUMBERS_MINUS
+ {_WA_FILE_NUMBERS, 90, 6, 5, 1}, // _WA_SKIN_NUMBERS_BLANK
+ {_WA_FILE_SHUFREP, 0, 0, 28, 15}, // _WA_SKIN_SHUFREP_REPEAT_NOT_SET_NORM
+ {_WA_FILE_SHUFREP, 0, 15, 28, 15}, // _WA_SKIN_SHUFREP_REPEAT_NOT_SET_PRES
+ {_WA_FILE_SHUFREP, 0, 30, 28, 15}, // _WA_SKIN_SHUFREP_REPEAT_SET_NORM
+ {_WA_FILE_SHUFREP, 0, 45, 28, 15}, // _WA_SKIN_SHUFREP_REPEAT_SET_PRES
+ {_WA_FILE_SHUFREP, 28, 0, 47, 15}, // _WA_SKIN_SHUFREP_SHUFFLE_NOT_SET_NORM
+ {_WA_FILE_SHUFREP, 28, 15, 47, 15}, // _WA_SKIN_SHUFREP_SHUFFLE_NOT_SET_PRES
+ {_WA_FILE_SHUFREP, 28, 30, 47, 15}, // _WA_SKIN_SHUFREP_SHUFFLE_SET_NORM
+ {_WA_FILE_SHUFREP, 28, 45, 47, 15}, // _WA_SKIN_SHUFREP_SHUFFLE_SET_PRES
+ {_WA_FILE_SHUFREP, 23, 61, 23, 12}, // _WA_SKIN_SHUFREP_PLAYLIST_NOT_SET_NORM
+ {_WA_FILE_SHUFREP, 69, 61, 23, 12}, // _WA_SKIN_SHUFREP_PLAYLIST_NOT_SET_PRES
+ {_WA_FILE_SHUFREP, 23, 73, 23, 12}, // _WA_SKIN_SHUFREP_PLAYLIST_SET_NORM
+ {_WA_FILE_SHUFREP, 73, 23, 12}, // _WA_SKIN_SHUFREP_PLAYLIST_SET_PRES
+ {_WA_FILE_SHUFREP, 0, 61, 23, 12}, // _WA_SKIN_SHUFREP_EQ_NOT_SET_NORM
+ {_WA_FILE_SHUFREP, 46, 61, 23, 12}, // _WA_SKIN_SHUFREP_EQ_NOT_SET_PRES
+ {_WA_FILE_SHUFREP, 0, 73, 23, 12}, // _WA_SKIN_SHUFREP_EQ_SET_NORM
+ {_WA_FILE_SHUFREP, 46, 73, 23, 12}, // _WA_SKIN_SHUFREP_EQ_SET_PRES
+ {_WA_FILE_TEXT, 0, 0, 155, 18}, // _WA_SKIN_TEXT
+ {_WA_FILE_VOLUME, 0, 0, 68, 421}, // _WA_SKIN_VOLUME_BAR_ALL_BARS
+ {_WA_FILE_VOLUME, 0, 0, 68, 13}, // _WA_SKIN_VOLUME_BAR
+ {_WA_FILE_VOLUME, 0, 422, 14, 11}, // _WA_SKIN_VOLUME_SLIDER_NORM
+ {_WA_FILE_VOLUME, 15, 422, 14, 11}, // _WA_SKIN_VOLUME_SLIDER_PRES
+ {_WA_FILE_BALANCE, 9, 0, 38, 421}, // _WA_SKIN_BALANCE_BAR_ALL_BARS
+ {_WA_FILE_BALANCE, 9, 0, 38, 13}, // _WA_SKIN_BALANCE_BAR
+ {_WA_FILE_BALANCE, 0, 422, 14, 11}, // _WA_SKIN_BALANCE_SLIDER_NORM
+ {_WA_FILE_BALANCE, 15, 422, 14, 11}, // _WA_SKIN_BALANCE_SLIDER_PRES
+ {_WA_FILE_POSBAR, 0, 0, 248, 10}, // _WA_SKIN_POS_BAR
+ {_WA_FILE_POSBAR, 278, 0, 29, 10}, // _WA_SKIN_POS_BAR_SLIDER_NORM
+ {_WA_FILE_POSBAR, 248, 0, 29, 10}, // _WA_SKIN_POS_BAR_SLIDER_PRES
+ {_WA_FILE_PLAYPAUS, 1, 0, 8, 9}, // _WA_SKIN_PLAYPAUS_PLAY
+ {_WA_FILE_PLAYPAUS, 9, 0, 9, 9}, // _WA_SKIN_PLAYPAUS_PAUSE
+ {_WA_FILE_PLAYPAUS, 18, 0, 9, 9}, // _WA_SKIN_PLAYPAUS_STOP
+ {_WA_FILE_PLAYPAUS, 27, 0, 2, 9}, // _WA_SKIN_PLAYPAUS_FILLER
+ {_WA_FILE_PLAYPAUS, 36, 0, 3, 9}, // _WA_SKIN_PLAYPAUS_WORK_INDICATOR
+ {_WA_FILE_TITLEBAR, 27, 0, 275, 14}, // _WA_SKIN_TITLE_ACTIVE
+ {_WA_FILE_TITLEBAR, 27, 15, 275, 14}, // _WA_SKIN_TITLE_INACTIVE
+ {_WA_FILE_TITLEBAR, 0, 9, 9, 9}, // _WA_SKIN_TITLE_MENU_PRES
+ {_WA_FILE_TITLEBAR, 0, 0, 9, 9}, // _WA_SKIN_TITLE_MENU_NORM
+ {_WA_FILE_TITLEBAR, 33, 18, 9, 9}, // _WA_SKIN_TITLE_MENU_INACTIVE
+ {_WA_FILE_TITLEBAR, 9, 9, 9, 9}, // _WA_SKIN_TITLE_MIN_PRES
+ {_WA_FILE_TITLEBAR, 9, 0, 9, 9}, // _WA_SKIN_TITLE_MIN_NORM
+ {_WA_FILE_TITLEBAR, 271, 18, 9, 9}, // _WA_SKIN_TITLE_MIN_INACTIVE
+ {_WA_FILE_TITLEBAR, 9, 18, 9, 9}, // _WA_SKIN_TITLE_SHADE_PRES
+ {_WA_FILE_TITLEBAR, 0, 18, 9, 9}, // _WA_SKIN_TITLE_SHADE_NORM
+ {_WA_FILE_TITLEBAR, 281, 18, 9, 9}, // _WA_SKIN_TITLE_SHADE_INACTIVE
+ {_WA_FILE_TITLEBAR, 18, 9, 9, 9}, // _WA_SKIN_TITLE_CLOSE_PRES
+ {_WA_FILE_TITLEBAR, 18, 0, 9, 9}, // _WA_SKIN_TITLE_CLOSE_NORM
+ {_WA_FILE_TITLEBAR, 291, 18, 9, 9}, // _WA_SKIN_TITLE_CLOSE_INACTIVE
+ {_WA_FILE_TITLEBAR, 312, 0, 8, 43} // _WA_SKIN_CLUTTERBAR_DISABLED
+};
+
+#endif
diff --git a/noatun/modules/winskin/waSkinModel.cpp b/noatun/modules/winskin/waSkinModel.cpp
new file mode 100644
index 00000000..ff53b4e6
--- /dev/null
+++ b/noatun/modules/winskin/waSkinModel.cpp
@@ -0,0 +1,458 @@
+/*
+ operations with skinset.
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2001 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include <config.h>
+#include <qdir.h>
+#include <qstringlist.h>
+#include <qbitmap.h>
+#include <kdebug.h>
+#include <kstandarddirs.h>
+
+#include "waSkinMapping.h"
+#include "waShadeMapping.h"
+
+#include "waSkinModel.h"
+#include "waColor.h"
+#include "waRegion.h"
+#include "waSkin.h"
+
+WaSkinModel *_waskinmodel_instance = NULL;
+
+// Our current skin map
+// Can switch between normal and windowshade maps
+const SkinMap *mapToGui;
+const SkinDesc *mapFromFile;
+int digit_width;
+int digit_height;
+
+
+struct WaPixmapEntry {
+ const char *filename;
+ QPixmap *pixmap;
+};
+
+WaPixmapEntry waPixmapEntries[11] = {
+ {"main.bmp", NULL},
+ {"cbuttons.bmp", NULL},
+ {"monoster.bmp", NULL},
+ {"numbers.bmp", NULL},
+ {"shufrep.bmp", NULL},
+ {"text.bmp", NULL},
+ {"volume.bmp", NULL},
+ {"balance.bmp", NULL},
+ {"posbar.bmp", NULL},
+ {"playpaus.bmp", NULL},
+ {"titlebar.bmp", NULL}
+ };
+
+WaSkinModel::WaSkinModel()
+{
+ for (int x = 0;x < 11;x++)
+ waPixmapEntries[x].pixmap = new QPixmap;
+
+ resetSkinModel();
+ _waskinmodel_instance = this;
+}
+
+WaSkinModel::~WaSkinModel()
+{
+ for (int x = 0;x < 11;x++)
+ delete waPixmapEntries[x].pixmap;
+
+ delete windowRegion;
+ delete colorScheme;
+}
+
+bool WaSkinModel::load(QString skinDir)
+{
+ bool success = true;
+
+ QDir dir(skinDir);
+
+ if (findFile(dir, "main.bmp").isEmpty()) {
+ // Ack, our skin dir doesn't exist, fall back to the default
+ dir = QDir(KGlobal::dirs()->findDirs("data", "noatun/skins/winamp/" + WaSkin::defaultSkin())[0]);
+ success = false;
+ }
+
+ for (int x = 0;x < 11;x++) {
+ getPixmap(dir, waPixmapEntries[x].filename,
+ waPixmapEntries[x].pixmap);
+ }
+
+ resetSkinModel();
+
+ loadColors(dir);
+ loadRegion(dir);
+
+ emit(skinChanged());
+
+ return success;
+}
+
+// Does a case-insenstive file search (like DOS/Windows)
+// Filename -must- be lowercase, which is an nice optimization, since
+// this is a private API, and all our filenames are internally lowercase
+// anyway
+QString WaSkinModel::findFile(const QDir &dir, const QString &filename) {
+ QFileInfo fileInfo;
+ QString ret = "";
+
+ QStringList strList = dir.entryList();
+
+ for (QStringList::iterator file = strList.begin(); file != strList.end(); file++) {
+ QFileInfo fileInfo(*file);
+
+ if (fileInfo.isDir())
+ continue;
+
+ if (fileInfo.filePath().lower() == filename)
+ return dir.absPath() + "/" + QString(fileInfo.filePath());
+ }
+
+ return "";
+}
+
+void WaSkinModel::loadColors(const QDir &dir) {
+ QString colorFile = findFile(dir, "viscolor.txt");
+
+ if (colorScheme) {
+ delete colorScheme;
+ }
+
+ colorScheme = new WaColor(colorFile);
+}
+
+void WaSkinModel::loadRegion(const QDir &dir) {
+ QString regionFile = findFile(dir, "region.txt");
+
+ if (windowRegion) {
+ delete windowRegion;
+ windowRegion = 0;
+ }
+
+ windowRegion = new WaRegion(regionFile);
+}
+
+int WaSkinModel::getPixmap(const QDir &dir, QString fname,
+ QPixmap *target)
+{
+ QFileInfo fileInfo;
+ QStringList strList = dir.entryList();
+ QString abspath;
+
+ abspath = findFile(dir, fname);
+
+ if (!abspath.isEmpty()) {
+ target->load(abspath);
+ return true;
+ }
+
+ // now the filename mapping 1.8x -> 2.0
+ if (fname == "volume.bmp")
+ return WaSkinModel::getPixmap(dir, QString("volbar.bmp"), target);
+
+ if (fname == "numbers.bmp")
+ return WaSkinModel::getPixmap(dir, QString("nums_ex.bmp"), target);
+
+ // Even 2.x themes can omit BALANCE, in which case we use VOLUME
+ if (fname == "balance.bmp")
+ return WaSkinModel::getPixmap(dir, QString("volume.bmp"), target);
+
+ return false;
+}
+
+
+QRect WaSkinModel::getGeometry(int id) {
+ if ( (id < 0) || (id >= _WA_SKIN_ENTRIES) ) {
+ kdDebug() << "Array index out of range. WaSkinModel::getGeometry"<<endl;
+ exit(-1);
+ }
+ return QRect(mapFromFile[id].x, mapFromFile[id].y,
+ mapFromFile[id].width, mapFromFile[id].height);
+}
+
+QRect WaSkinModel::getMapGeometry(int id) {
+ if ( (id < 0) || (id >= _WA_MAPPING_ENTRIES) ) {
+ kdDebug() << "Array index out of range. WaSkinModel::getMapGeometry"<<endl;
+ exit(-1);
+ }
+ return QRect(mapToGui[id].x, mapToGui[id].y,
+ mapToGui[id].width, mapToGui[id].height);
+}
+
+void WaSkinModel::bltTo(int id, QPaintDevice *dest, int x, int y) {
+ bitBlt(dest, x, y, waPixmapEntries[mapFromFile[id].fileId].pixmap,
+ mapFromFile[id].x, mapFromFile[id].y,
+ mapFromFile[id].width, mapFromFile[id].height);
+}
+
+void WaSkinModel::bltTo(int id, QPaintDevice *dest, int x, int y, int argument) {
+ if (id == _WA_SKIN_VOLUME_BAR) {
+ QPixmap *pix = waPixmapEntries[_WA_FILE_VOLUME].pixmap;
+
+ int nBar = int((float)argument * 27.0 / 100.0);
+ bitBlt(dest, x, y, pix, 0, 15 * nBar, 68, 13);
+
+ return;
+ }
+
+ if (id == _WA_SKIN_BALANCE_BAR) {
+ QPixmap *pix = waPixmapEntries[_WA_FILE_BALANCE].pixmap;
+
+ argument = abs(argument);
+
+ int nBar = int((float)argument * 27.0 / 100.0);
+ bitBlt(dest, x, y, pix, 9, 15 * nBar, 38, 13);
+
+ return;
+ }
+
+ bltTo(id, dest, x, y);
+}
+
+void WaSkinModel::getDigit(char number, QPaintDevice *dest, int x, int y) {
+ if (number=='-') {
+ bltTo(_WA_SKIN_NUMBERS_MINUS, dest, x, y);
+ return;
+ }
+
+ // empty number ?
+ if (number == ' ') {
+ bltTo(_WA_SKIN_NUMBERS_BLANK, dest, x, y);
+ return;
+ }
+
+ // number
+ QPixmap *pix = waPixmapEntries[mapFromFile[_WA_SKIN_NUMBERS].fileId].pixmap;
+
+ // ordinary number:
+ int index = number - '0';
+ if ((index < 0) || (index > 9))
+ return;
+
+ bitBlt(dest, x, y, pix , (index * digit_width) + mapFromFile[_WA_SKIN_NUMBERS].x, mapFromFile[_WA_SKIN_NUMBERS].y, digit_width, digit_height);
+
+ return;
+}
+
+void WaSkinModel::getText(char text, QPaintDevice * dest, int x, int y) {
+ QPixmap *pix = waPixmapEntries[_WA_FILE_TEXT].pixmap;
+
+ text = deaccent(text);
+
+ if (('A' <= text) && (text <= 'Z')) {
+ bitBlt(dest, x, y,pix,(text-'A')*5,0,5,6);
+ return;
+ }
+ if (('a' <= text) && (text <= 'z')) {
+ bitBlt(dest, x, y,pix,(text-'a')*5,0,5,6);
+ return;
+ }
+ if (('0' <= text) && (text <= '9')) {
+ bitBlt(dest, x, y,pix,(text-'0')*5,6,5,6);
+ return;
+ }
+ if ('"' == text) {
+ bitBlt(dest, x, y,pix,27*5,0,5,6);
+ return;
+ }
+ if ('@' == text) {
+ bitBlt(dest, x, y,pix,28*5,0,5,6);
+ return;
+ }
+
+
+ if ('.' == text) {
+ bitBlt(dest, x, y,pix,11*5,6,5,6);
+ return;
+ }
+ if (':' == text) {
+ bitBlt(dest, x, y,pix,12*5,6,5,6);
+ return;
+ }
+ if (('(' == text) || ('<' == text) || ('{' == text)) {
+ bitBlt(dest, x, y,pix,13*5,6,5,6);
+ return;
+ }
+ if ((')' == text) || ('>' == text) || ('}' == text)) {
+ bitBlt(dest, x, y,pix,14*5,6,5,6);
+ return;
+ }
+ if ('-' == text) {
+ bitBlt(dest, x, y,pix,15*5,6,5,6);
+ return;
+ }
+ if (('`' == text) || ('\'' == text)) {
+ bitBlt(dest, x, y,pix,16*5,6,5,6);
+ return;
+ }
+ if ('!' == text) {
+ bitBlt(dest, x, y,pix,17*5,6,5,6);
+ return;
+ }
+ if ('_' == text) {
+ bitBlt(dest, x, y,pix,18*5,6,5,6);
+ return;
+ }
+ if ('+' == text) {
+ bitBlt(dest, x, y,pix,19*5,6,5,6);
+ return;
+ }
+ if ('\\' == text) {
+ bitBlt(dest, x, y,pix,20*5,6,5,6);
+ return;
+ }
+ if ('/' == text) {
+ bitBlt(dest, x, y,pix,21*5,6,5,6);
+ return;
+ }
+ if ('[' == text) {
+ bitBlt(dest, x, y,pix,22*5,6,5,6);
+ return;
+ }
+ if (']' == text) {
+ bitBlt(dest, x, y,pix,23*5,6,5,6);
+ return;
+ }
+ if ('^' == text) {
+ bitBlt(dest, x, y,pix,24*5,6,5,6);
+ return;
+ }
+ if ('&' == text) {
+ bitBlt(dest, x, y,pix,25*5,6,5,6);
+ return;
+ }
+ if ('%' == text) {
+ bitBlt(dest, x, y,pix,26*5,6,5,6);
+ return;
+ }
+ if (',' == text) {
+ bitBlt(dest, x, y,pix,27*5,6,5,6);
+ return;
+ }
+ if ('=' == text) {
+ bitBlt(dest, x, y,pix,28*5,6,5,6);
+ return;
+ }
+ if ('$' == text) {
+ bitBlt(dest, x, y,pix,29*5,6,5,6);
+ return;
+ }
+ if ('#' == text) {
+ bitBlt(dest, x, y,pix,30*5,6,5,6);
+ return;
+ }
+ if (('' == text) || ('' == text)) {
+ bitBlt(dest, x, y,pix,0*5,12,5,6);
+ return;
+ }
+ if (('' == text) || ('' == text)) {
+ bitBlt(dest, x, y,pix,1*5,12,5,6);
+ return;
+ }
+ if (('' == text) || ('' == text)) {
+ bitBlt(dest, x, y,pix,2*5,12,5,6);
+ return;
+ }
+ if ('?' == text) {
+ bitBlt(dest, x, y,pix,3*5,12,5,6);
+ return;
+ }
+ if ('*' == text) {
+ bitBlt(dest, x, y,pix,4*5,12,5,6);
+ return;
+ }
+ // default back is space char
+ bitBlt(dest, x, y,pix,(10*5),12,5,6);
+}
+
+void WaSkinModel::paintBackgroundTo(int mapping, QPaintDevice *dest, int x, int y)
+{
+ QPixmap *pix = waPixmapEntries[mapFromFile[_WA_SKIN_MAIN].fileId].pixmap;
+ QRect main_rect = getGeometry(_WA_SKIN_MAIN);
+ QRect dest_rect = getMapGeometry(mapping);
+
+ int source_x = main_rect.x() + dest_rect.x() + x;
+ int source_y = main_rect.y() + dest_rect.y() + y;
+
+ int width = dest_rect.width() - x;
+ int height = dest_rect.height() - y;
+
+ bitBlt(dest, x, y, pix, source_x, source_y, width, height);
+}
+
+void WaSkinModel::setSkinModel(skin_models new_model) {
+ if (new_model == WA_MODEL_NORMAL) {
+ mapToGui = normalMapToGui;
+ mapFromFile = normalMapFromFile;
+ digit_width = 9;
+ digit_height = 13;
+ }
+ else if (new_model == WA_MODEL_WINDOWSHADE) {
+ mapToGui = shadeMapToGui;
+ mapFromFile = shadeMapFromFile;
+ digit_width = 5;
+ digit_height = 6;
+ }
+
+ emit(skinChanged());
+}
+
+void WaSkinModel::resetSkinModel() {
+ mapToGui = normalMapToGui;
+ mapFromFile = normalMapFromFile;
+ digit_width = 9;
+ digit_height = 13;
+}
+
+
+QChar WaSkinModel::deaccent(QChar input) {
+ if (QString("").contains(input))
+ return 'A';
+
+ if (QString("").contains(input))
+ return 'E';
+
+ if (QString("").contains(input))
+ return 'I';
+
+ if (QString("").contains(input))
+ return 'O';
+
+ if (QString("").contains(input))
+ return 'U';
+
+ if (input == '')
+ return 'Y';
+
+ if (QString("").contains(input))
+ return 'a';
+
+ if (QString("").contains(input))
+ return 'e';
+
+ if (QString("").contains(input))
+ return 'i';
+
+ if (QString("").contains(input))
+ return 'o';
+
+ if (QString("").contains(input))
+ return 'u';
+
+ return input;
+}
+
+#include "waSkinModel.moc"
diff --git a/noatun/modules/winskin/waSkinModel.h b/noatun/modules/winskin/waSkinModel.h
new file mode 100644
index 00000000..9447f8ae
--- /dev/null
+++ b/noatun/modules/winskin/waSkinModel.h
@@ -0,0 +1,64 @@
+/*
+ Model for winamp skins
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#ifndef __WASKINMODEL_H
+#define __WASKINMODEL_H
+
+#include <qstring.h>
+#include <qobject.h>
+#include <qdir.h>
+
+#include "waSkins.h"
+
+enum skin_models { WA_MODEL_NORMAL, WA_MODEL_WINDOWSHADE };
+
+class QChar;
+class WaSkinModel;
+extern WaSkinModel *_waskinmodel_instance;
+
+class WaSkinModel : public QObject {
+ Q_OBJECT
+
+ public:
+ WaSkinModel();
+ ~WaSkinModel();
+
+ void getDigit(char number, QPaintDevice *dest, int x, int y);
+ void getText(char text, QPaintDevice *dest, int x, int y);
+
+ void bltTo(int id, QPaintDevice *dest, int x, int y);
+ void bltTo(int id, QPaintDevice *dest, int x, int y, int argument);
+
+ void paintBackgroundTo(int mapping, QPaintDevice *dest, int x, int y);
+
+ QRect getGeometry(int id);
+ QRect getMapGeometry(int id);
+
+ bool load(QString skinDir);
+
+ void setSkinModel(skin_models new_model);
+ static WaSkinModel *instance() { return _waskinmodel_instance; }
+
+ private:
+ void resetSkinModel();
+
+ QString findFile(const QDir &dir, const QString &filename);
+ QChar deaccent(QChar input);
+
+ int getPixmap(const QDir &dir, QString fname, QPixmap * target);
+ void loadColors(const QDir &dir);
+ void loadRegion(const QDir &dir);
+
+ signals:
+ void skinChanged();
+};
+#endif
diff --git a/noatun/modules/winskin/waSkins.h b/noatun/modules/winskin/waSkins.h
new file mode 100644
index 00000000..f8d5f623
--- /dev/null
+++ b/noatun/modules/winskin/waSkins.h
@@ -0,0 +1,161 @@
+/*
+ names for the different skins in winamp
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef _WASKINS_H
+#define _WASKINS_H
+
+
+
+/**
+ The Skin file format was downloaded from the web.
+ http://www.algonet.et.se/~daniel7
+
+ Author: unknown
+*/
+
+
+#define _WA_SKIN_MAIN 0
+#define _WA_SKIN_CBUTTONS_PREV_NORM 1
+#define _WA_SKIN_CBUTTONS_PREV_PRES 2
+#define _WA_SKIN_CBUTTONS_PLAY_NORM 3
+#define _WA_SKIN_CBUTTONS_PLAY_PRES 4
+#define _WA_SKIN_CBUTTONS_PAUSE_NORM 5
+#define _WA_SKIN_CBUTTONS_PAUSE_PRES 6
+#define _WA_SKIN_CBUTTONS_STOP_NORM 7
+#define _WA_SKIN_CBUTTONS_STOP_PRES 8
+#define _WA_SKIN_CBUTTONS_NEXT_NORM 9
+#define _WA_SKIN_CBUTTONS_NEXT_PRES 10
+#define _WA_SKIN_CBUTTONS_EJECT_NORM 11
+#define _WA_SKIN_CBUTTONS_EJECT_PRESS 12
+#define _WA_SKIN_MONOSTER_STEREO_TRUE 13
+#define _WA_SKIN_MONOSTER_STEREO_FALSE 14
+#define _WA_SKIN_MONOSTER_MONO_TRUE 15
+#define _WA_SKIN_MONOSTER_MONO_FALSE 16
+
+#define _WA_SKIN_NUMBERS 17
+#define _WA_SKIN_NUMBERS_MINUS 18
+#define _WA_SKIN_NUMBERS_BLANK 19
+
+#define _WA_SKIN_SHUFREP_REPEAT_NOT_SET_NORM 20
+#define _WA_SKIN_SHUFREP_REPEAT_NOT_SET_PRES 21
+#define _WA_SKIN_SHUFREP_REPEAT_SET_NORM 22
+#define _WA_SKIN_SHUFREP_REPEAT_SET_PRES 23
+#define _WA_SKIN_SHUFREP_SHUFFLE_NOT_SET_NORM 24
+#define _WA_SKIN_SHUFREP_SHUFFLE_NOT_SET_PRES 25
+#define _WA_SKIN_SHUFREP_SHUFFLE_SET_NORM 26
+#define _WA_SKIN_SHUFREP_SHUFFLE_SET_PRES 27
+#define _WA_SKIN_SHUFREP_PLAYLIST_NOT_SET_NORM 28
+#define _WA_SKIN_SHUFREP_PLAYLIST_NOT_SET_PRES 29
+#define _WA_SKIN_SHUFREP_PLAYLIST_SET_NORM 30
+#define _WA_SKIN_SHUFREP_PLAYLIST_SET_PRES 31
+#define _WA_SKIN_SHUFREP_EQ_NOT_SET_NORM 32
+#define _WA_SKIN_SHUFREP_EQ_NOT_SET_PRES 33
+#define _WA_SKIN_SHUFREP_EQ_SET_NORM 34
+#define _WA_SKIN_SHUFREP_EQ_SET_PRES 35
+
+#define _WA_SKIN_TEXT 36
+
+#define _WA_SKIN_VOLUME_BAR_ALL_BARS 37
+#define _WA_SKIN_VOLUME_BAR 38
+#define _WA_SKIN_VOLUME_SLIDER_NORM 39
+#define _WA_SKIN_VOLUME_SLIDER_PRES 40
+
+#define _WA_SKIN_BALANCE_BAR_ALL_BARS 41
+#define _WA_SKIN_BALANCE_BAR 42
+#define _WA_SKIN_BALANCE_SLIDER_NORM 43
+#define _WA_SKIN_BALANCE_SLIDER_PRES 44
+
+#define _WA_SKIN_POS_BAR 45
+#define _WA_SKIN_POS_BAR_SLIDER_NORM 46
+#define _WA_SKIN_POS_BAR_SLIDER_PRES 47
+
+#define _WA_SKIN_PLAYPAUS_PLAY 48
+#define _WA_SKIN_PLAYPAUS_PAUSE 49
+#define _WA_SKIN_PLAYPAUS_STOP 50
+#define _WA_SKIN_PLAYPAUS_FILLER 51
+#define _WA_SKIN_PLAYPAUS_WORK_INDICATOR 52
+
+#define _WA_SKIN_TITLE_ACTIVE 53
+#define _WA_SKIN_TITLE_INACTIVE 54
+
+#define _WA_SKIN_TITLE_MENU_PRES 55
+#define _WA_SKIN_TITLE_MENU_NORM 56
+#define _WA_SKIN_TITLE_MENU_INACTIVE 57
+#define _WA_SKIN_TITLE_MIN_PRES 58
+#define _WA_SKIN_TITLE_MIN_NORM 59
+#define _WA_SKIN_TITLE_MIN_INACTIVE 60
+#define _WA_SKIN_TITLE_SHADE_PRES 61
+#define _WA_SKIN_TITLE_SHADE_NORM 62
+#define _WA_SKIN_TITLE_SHADE_INACTIVE 63
+#define _WA_SKIN_TITLE_CLOSE_PRES 64
+#define _WA_SKIN_TITLE_CLOSE_NORM 65
+#define _WA_SKIN_TITLE_CLOSE_INACTIVE 66
+
+#define _WA_SKIN_CLUTTERBAR_DISABLED 67
+
+#define _WA_MAPPING_MAIN 0
+#define _WA_MAPPING_CBUTTONS_PREV 1
+#define _WA_MAPPING_CBUTTONS_PLAY 2
+#define _WA_MAPPING_CBUTTONS_PAUSE 3
+#define _WA_MAPPING_CBUTTONS_STOP 4
+#define _WA_MAPPING_CBUTTONS_NEXT 5
+#define _WA_MAPPING_CBUTTONS_EJECT 6
+#define _WA_MAPPING_MONOSTER_STEREO 7
+#define _WA_MAPPING_MONOSTER_MONO 8
+#define _WA_MAPPING_REPEAT 9
+#define _WA_MAPPING_SHUFFLE 10
+#define _WA_MAPPING_PLAYLIST 11
+#define _WA_MAPPING_EQ 12
+#define _WA_MAPPING_VOLUME_BAR 13
+#define _WA_MAPPING_VOLUME_SLIDER 14
+#define _WA_MAPPING_BALANCE_BAR 15
+#define _WA_MAPPING_BALANCE_SLIDER 16
+#define _WA_MAPPING_PLAYPAUS 17
+#define _WA_MAPPING_POS_BAR 18
+#define _WA_MAPPING_POS_BAR_SLIDER 19
+#define _WA_MAPPING_DIGITS 20
+#define _WA_MAPPING_MINUS 21
+#define _WA_MAPPING_DIGIT_1 22
+#define _WA_MAPPING_DIGIT_2 23
+#define _WA_MAPPING_DIGIT_3 24
+#define _WA_MAPPING_DIGIT_4 25
+#define _WA_MAPPING_ANALYSER 26
+#define _WA_MAPPING_BPS 27
+#define _WA_MAPPING_FREQ 28
+#define _WA_MAPPING_INFO 29
+#define _WA_MAPPING_TITLE 30
+#define _WA_MAPPING_TITLE_MENU 31
+#define _WA_MAPPING_TITLE_MIN 32
+#define _WA_MAPPING_TITLE_SHADE 33
+#define _WA_MAPPING_TITLE_CLOSE 34
+#define _WA_MAPPING_CLUTTERBAR 35
+
+#define _WA_FILE_MAIN 0
+#define _WA_FILE_CBUTTONS 1
+#define _WA_FILE_MONOSTER 2
+#define _WA_FILE_NUMBERS 3
+#define _WA_FILE_SHUFREP 4
+#define _WA_FILE_TEXT 5
+#define _WA_FILE_VOLUME 6
+#define _WA_FILE_BALANCE 7
+#define _WA_FILE_POSBAR 8
+#define _WA_FILE_PLAYPAUS 9
+#define _WA_FILE_TITLEBAR 10
+
+#define _WA_MAPPING_ENTRIES 36
+#define _WA_SKIN_ENTRIES 68
+
+#define _WA_TEXT_WIDTH 5
+#define _WA_TEXT_HEIGHT 6
+
+#endif
diff --git a/noatun/modules/winskin/waSlider.cpp b/noatun/modules/winskin/waSlider.cpp
new file mode 100644
index 00000000..4fff1b0d
--- /dev/null
+++ b/noatun/modules/winskin/waSlider.cpp
@@ -0,0 +1,209 @@
+/*
+ Standard slider for Winskin
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2002 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include <stdlib.h>
+
+#include "waSlider.h"
+#include "waSkinModel.h"
+
+WaSlider::WaSlider(int sliderBarMapId, int sliderId, bool is_volume) : WaWidget(sliderBarMapId)
+{
+ this->sliderBarMapId = sliderBarMapId;
+
+ lDragging = false;
+
+ currentValue = 0;
+ setRange(0, 100);
+
+ slider_y = is_volume ? 1 : 0;
+ slider_x = 0;
+
+ slider_width = WaSkinModel::instance()->getMapGeometry(sliderId).width();
+
+ slider_visible = true;
+}
+
+
+WaSlider::~WaSlider()
+{
+}
+
+void WaSlider::setPixmapSliderBar(int pixId)
+{
+ this->sliderBarId = pixId;
+ update();
+}
+
+void WaSlider::paintEvent(QPaintEvent *)
+{
+ // POSBAR.BMP does not have full height in all Winamp skins
+ // Paint background before painting slider to be on the safe side
+ if(sliderBarId == _WA_SKIN_POS_BAR)
+ paintPixmap(-1);
+
+ paintPixmap(sliderBarId, value());
+
+ if (slider_visible)
+ paintPixmap(lDragging ? up_pixmap : down_pixmap, slider_x, slider_y);
+}
+
+
+void WaSlider::mouseMoveEvent(QMouseEvent * e)
+{
+ if (lDragging == false) {
+ WaWidget::mouseMoveEvent(e);
+ return;
+ }
+
+ int newX = e->x() - pressPoint.x();
+
+ if (newX < 0)
+ newX = 0;
+
+ QSize size = sizeHint();
+
+ int maxX = size.width() - slider_width;
+
+ if(mapping == _WA_MAPPING_VOLUME_BAR)
+ maxX -= 3;
+
+ if (newX > maxX)
+ newX = maxX;
+
+ int value = pixel2Value(newX);
+
+ setValue(value);
+}
+
+
+void WaSlider::updateSliderPos(int value)
+{
+ if (value > maxValue) {
+ value = maxValue;
+ }
+ if (value < minValue) {
+ value = minValue;
+ }
+
+ int pixelPos = value2Pixel(value);
+ slider_x = (pixelPos);
+
+ update();
+}
+
+void WaSlider::mousePressEvent(QMouseEvent *e) {
+ if (e->button() != LeftButton && e->button() != MidButton) {
+ WaWidget::mousePressEvent(e);
+ return;
+ }
+
+ int maxX = slider_x - slider_width;
+
+ if(mapping == _WA_MAPPING_VOLUME_BAR)
+ maxX -= 3;
+
+ if ((e->x() < slider_x) || (e->x() > (maxX))) {
+ int newX = e->x();
+ newX -= (slider_width / 2);
+ setValue(pixel2Value(newX));
+ }
+
+
+ pressPoint.setX(e->x() - slider_x);
+ lDragging = true;
+
+ update();
+
+ emit(sliderPressed());
+}
+
+void WaSlider::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (!lDragging) {
+ WaWidget::mouseReleaseEvent(e);
+ return;
+ }
+
+ lDragging = false;
+ update();
+
+ emit(sliderReleased());
+}
+
+
+int WaSlider::pixel2Value(int xpos)
+{
+ QSize size = sizeHint();
+ int min = abs(minValue);
+ int max = abs(maxValue);
+
+ int valuerange = min + max;
+ int pixelrange = size.width() - slider_width;
+
+ if(mapping == _WA_MAPPING_VOLUME_BAR)
+ pixelrange -= 3;
+
+ return ((xpos * valuerange) / pixelrange) + minValue;
+}
+
+int WaSlider::value2Pixel(int value)
+{
+ QSize size = sizeHint();
+ float min = (float) minValue;
+ float max = (float) maxValue;
+ float fmin = min;
+ float fmax = max;
+ if (min < 0) {
+ fmin = -1 * fmin;
+ }
+ if (max < 0) {
+ fmax = -1 * fmax;
+ }
+ float valuerange = fmin + fmax;
+ float verhaeltnis = fmin / valuerange;
+ float pixelrange = (float) (size.width() - slider_width);
+
+ if(mapping == _WA_MAPPING_VOLUME_BAR)
+ pixelrange -= 3;
+
+ float zeropoint = verhaeltnis * pixelrange;
+ float anstieg = pixelrange / valuerange;
+
+ float pixel = (float) value * anstieg + zeropoint;
+ return (int) (pixel+0.5); // add 0.5 to round upwards if larger than x.5
+}
+
+void WaSlider::setRange(int min, int max) {
+ minValue = min;
+ maxValue = max;
+
+ if (currentValue < min)
+ currentValue = min;
+
+ if (currentValue > max)
+ currentValue = max;
+}
+
+void WaSlider::setValue(int value) {
+ currentValue = value;
+
+ updateSliderPos(currentValue);
+ emit(valueChanged(value));
+}
+
+void WaSlider::cancelDrag() {
+ lDragging = false;
+ update();
+}
+
+#include "waSlider.moc"
diff --git a/noatun/modules/winskin/waSlider.h b/noatun/modules/winskin/waSlider.h
new file mode 100644
index 00000000..0003afd2
--- /dev/null
+++ b/noatun/modules/winskin/waSlider.h
@@ -0,0 +1,82 @@
+/*
+ standard Slider for winamp Skin
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __WASLIDER_H
+#define __WASLIDER_H
+
+#include <qpainter.h>
+
+#include "waWidget.h"
+#include "waButton.h"
+
+class WaSlider : public WaWidget {
+ Q_OBJECT
+
+ public:
+ WaSlider(int sliderBarMapId, int sliderId, bool is_volume = false);
+ ~WaSlider();
+
+ void setRange(int min, int max);
+
+ void setValue(int value);
+ int value() const { return currentValue; }
+
+ void hideButton() { slider_visible = false; }
+ void showButton() { slider_visible = true; }
+
+ void cancelDrag();
+
+ public slots:
+ void setPixmapSliderButtonDown(int pixId) { down_pixmap = pixId; }
+ void setPixmapSliderButtonUp(int pixId) { up_pixmap = pixId; }
+ void setPixmapSliderBar(int pixId);
+
+ private:
+ void paintEvent(QPaintEvent *);
+ int pixel2Value(int xpos);
+ int value2Pixel(int value);
+
+ int slider_x;
+ int slider_y;
+ int slider_width;
+ bool slider_visible;
+
+ int up_pixmap;
+ int down_pixmap;
+
+ int sliderBarId;
+ int sliderBarMapId;
+
+ bool lDragging;
+ QPoint pressPoint;
+
+ int currentValue;
+
+ int minValue;
+ int maxValue;
+
+ void mousePressEvent(QMouseEvent *e);
+ void mouseMoveEvent(QMouseEvent *e);
+ void mouseReleaseEvent(QMouseEvent *e);
+
+ private slots:
+ void updateSliderPos(int value);
+
+ signals:
+ void sliderPressed();
+ void sliderReleased();
+ void valueChanged(int);
+};
+#endif
diff --git a/noatun/modules/winskin/waStatus.cpp b/noatun/modules/winskin/waStatus.cpp
new file mode 100644
index 00000000..e028b7f0
--- /dev/null
+++ b/noatun/modules/winskin/waStatus.cpp
@@ -0,0 +1,42 @@
+/*
+ standard Button for winamp Skin
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "waStatus.h"
+#include "waSkinModel.h"
+
+WaStatus::WaStatus() : WaWidget(_WA_MAPPING_PLAYPAUS)
+{
+ _status = STATUS_STOPPED;
+}
+
+WaStatus::~WaStatus()
+{
+}
+
+void WaStatus::paintEvent(QPaintEvent *)
+{
+ if (_status == STATUS_PLAYING) {
+ paintPixmap(_WA_SKIN_PLAYPAUS_WORK_INDICATOR);
+ paintPixmap(_WA_SKIN_PLAYPAUS_PLAY, 3, 0);
+ }
+ else if (_status == STATUS_STOPPED) {
+ paintPixmap(_WA_SKIN_PLAYPAUS_FILLER);
+ paintPixmap(_WA_SKIN_PLAYPAUS_STOP, 2 ,0);
+ }
+ else if (_status == STATUS_PAUSED) {
+ paintPixmap(_WA_SKIN_PLAYPAUS_FILLER);
+ paintPixmap(_WA_SKIN_PLAYPAUS_PAUSE, 2, 0);
+ }
+}
+
+#include "waStatus.moc"
diff --git a/noatun/modules/winskin/waStatus.h b/noatun/modules/winskin/waStatus.h
new file mode 100644
index 00000000..a3fe1bc8
--- /dev/null
+++ b/noatun/modules/winskin/waStatus.h
@@ -0,0 +1,40 @@
+/*
+ standard Button for winamp Skin
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __WASTATUS_H
+#define __WASTATUS_H
+
+#include <qpainter.h>
+
+#include "waWidget.h"
+
+enum status_enum {STATUS_PLAYING, STATUS_STOPPED, STATUS_PAUSED};
+
+class WaStatus : public WaWidget {
+ Q_OBJECT
+
+ public:
+ WaStatus();
+ ~WaStatus();
+
+ void setStatus(status_enum status) { _status = status; update(); }
+ status_enum status() const { return _status; }
+
+ private:
+ void paintEvent(QPaintEvent * paintEvent);
+
+ status_enum _status;
+};
+#endif
diff --git a/noatun/modules/winskin/waTitleBar.cpp b/noatun/modules/winskin/waTitleBar.cpp
new file mode 100644
index 00000000..82b603f5
--- /dev/null
+++ b/noatun/modules/winskin/waTitleBar.cpp
@@ -0,0 +1,79 @@
+/*
+ Titlebar for winamp Skin
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2001 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include <qwidget.h>
+#include <qpixmap.h>
+
+#include "waTitleBar.h"
+#include "waSkinModel.h"
+
+#include <stdlib.h>
+
+#include <iostream>
+
+WaTitleBar::WaTitleBar() : WaIndicator(_WA_MAPPING_TITLE, _WA_SKIN_TITLE_ACTIVE, _WA_SKIN_TITLE_INACTIVE)
+{
+ moving = false;
+ setState(parentWidget()->isActiveWindow());
+}
+
+WaTitleBar::~WaTitleBar()
+{
+}
+
+void WaTitleBar::mousePressEvent(QMouseEvent * e)
+{
+ if (e->button() != RightButton) {
+ if (!moving) {
+ moving = true;
+ mDragStart = e->pos();
+ mLastPos = e->globalPos();
+ }
+
+ setState(true);
+ update();
+ return;
+ }
+ else
+ WaWidget::mousePressEvent(e);
+}
+
+void WaTitleBar::mouseDoubleClickEvent(QMouseEvent *) {
+ emit(shaded());
+}
+
+void WaTitleBar::mouseReleaseEvent(QMouseEvent * e)
+{
+ if (e->button() != RightButton) {
+ moving = false;
+ update();
+ return;
+ }
+ else
+ WaWidget::mouseReleaseEvent(e);
+}
+
+void WaTitleBar::mouseMoveEvent(QMouseEvent * e)
+{
+ QPoint diff = e->globalPos() - mLastPos;
+ if (abs(diff.x()) > 10 || abs(diff.y()) > 10) {
+ // Moving starts only, when passing a drag border
+ moving = true;
+ }
+
+ if (moving)
+ parentWidget()->move(e->globalPos() - mDragStart);
+}
+
+#include <waTitleBar.moc>
diff --git a/noatun/modules/winskin/waTitleBar.h b/noatun/modules/winskin/waTitleBar.h
new file mode 100644
index 00000000..4e001394
--- /dev/null
+++ b/noatun/modules/winskin/waTitleBar.h
@@ -0,0 +1,51 @@
+/*
+ standard Button for winamp Skin
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __WATITLEBAR_H
+#define __WATITLEBAR_H
+
+#include <qpainter.h>
+
+#include "waIndicator.h"
+
+
+/**
+ A titlebar is similar to a button only that we move
+ the whole widget, when pressed
+*/
+
+
+class WaTitleBar : public WaIndicator {
+ Q_OBJECT
+
+ public:
+ WaTitleBar();
+ ~WaTitleBar();
+
+ private:
+ void mouseDoubleClickEvent (QMouseEvent * e);
+ void mousePressEvent(QMouseEvent * e);
+ void mouseReleaseEvent(QMouseEvent * e);
+ void mouseMoveEvent(QMouseEvent * e);
+
+ bool moving;
+ QPoint mLastPos;
+ QPoint mDragStart;
+
+ signals:
+ void shaded();
+
+};
+#endif
diff --git a/noatun/modules/winskin/waVolumeSlider.cpp b/noatun/modules/winskin/waVolumeSlider.cpp
new file mode 100644
index 00000000..d0a934d7
--- /dev/null
+++ b/noatun/modules/winskin/waVolumeSlider.cpp
@@ -0,0 +1,51 @@
+/*
+ jumpslider for winamp skins
+ Copyright (C) 1998 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include <waVolumeSlider.h>
+
+
+WaVolumeSlider::WaVolumeSlider() : WaWidget(_WA_MAPPING_VOLUME_BAR)
+{
+
+}
+
+
+WaVolumeSlider::~WaVolumeSlider()
+{
+}
+
+
+void WaVolumeSlider::buildGui()
+{
+ ws = new WaSlider(_WA_MAPPING_VOLUME_BAR, _WA_MAPPING_VOLUME_SLIDER, true);
+
+ ws->setPixmapSliderButtonUp(_WA_SKIN_VOLUME_SLIDER_NORM);
+ ws->setPixmapSliderButtonDown(_WA_SKIN_VOLUME_SLIDER_PRES);
+ ws->setPixmapSliderBar(_WA_SKIN_VOLUME_BAR);
+
+ connect(ws, SIGNAL(valueChanged(int)), this,
+ SIGNAL(volumeSetValue(int)));
+ connect(ws, SIGNAL(sliderPressed()), SIGNAL(sliderPressed()));
+ connect(ws, SIGNAL(sliderReleased()), SIGNAL(sliderReleased()));
+}
+
+void WaVolumeSlider::setVolumeValue(int val)
+{
+ int currVal = ws->value();
+ if (currVal != val) {
+ ws->setValue(val);
+ }
+}
+
+
+#include "waVolumeSlider.moc"
diff --git a/noatun/modules/winskin/waVolumeSlider.h b/noatun/modules/winskin/waVolumeSlider.h
new file mode 100644
index 00000000..29030c65
--- /dev/null
+++ b/noatun/modules/winskin/waVolumeSlider.h
@@ -0,0 +1,41 @@
+/*
+ jumpslider for winamp skins
+ Copyright (C) 1998 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __WAVOLUMESLIDER_H
+#define __WAVOLUMESLIDER_H
+
+#include "waSlider.h"
+#include "waWidget.h"
+
+class WaVolumeSlider:public WaWidget {
+ Q_OBJECT
+
+ public:
+ WaVolumeSlider();
+ ~WaVolumeSlider();
+ void buildGui();
+
+ void setVolumeValue(int val);
+
+ private:
+ WaSlider *ws;
+
+ signals:
+ void volumeSetValue(int val);
+ void sliderPressed();
+ void sliderReleased();
+};
+
+
+#endif
diff --git a/noatun/modules/winskin/waWidget.cpp b/noatun/modules/winskin/waWidget.cpp
new file mode 100644
index 00000000..ffe2d3b4
--- /dev/null
+++ b/noatun/modules/winskin/waWidget.cpp
@@ -0,0 +1,58 @@
+#include "waWidget.h"
+#include "waSkinModel.h"
+#include "noatun/stdaction.h"
+#include "waSkin.h"
+
+WaWidget::WaWidget(int _mapping) : QWidget(WaSkin::instance()) {
+ mapping = _mapping;
+ setBackgroundMode(NoBackground);
+ connect (WaSkinModel::instance(), SIGNAL(skinChanged()), this, SLOT(skinChanged()));
+}
+
+WaWidget::~WaWidget() {
+}
+
+void WaWidget::paintPixmap(int pixmap_mapping) {
+ if (pixmap_mapping != -1)
+ WaSkinModel::instance()->bltTo(pixmap_mapping, this, 0, 0);
+ else
+ WaSkinModel::instance()->paintBackgroundTo(mapping, this, 0, 0);
+}
+
+void WaWidget::paintPixmap(int pixmap_mapping, int x, int y) {
+ if (pixmap_mapping != -1)
+ WaSkinModel::instance()->bltTo(pixmap_mapping, this, x, y);
+ else
+ WaSkinModel::instance()->paintBackgroundTo(mapping, this, x, y);
+}
+
+void WaWidget::paintPixmap(int pixmap_mapping, int argument) {
+ if (pixmap_mapping != -1)
+ WaSkinModel::instance()->bltTo(pixmap_mapping, this, 0, 0, argument);
+ else
+ WaSkinModel::instance()->paintBackgroundTo(mapping, this, 0, 0);
+
+}
+
+void WaWidget::paintPixmap(int pixmap_mapping, int argument, int x, int y) {
+ if (pixmap_mapping != -1)
+ WaSkinModel::instance()->bltTo(pixmap_mapping, this, x, y, argument);
+ else
+ WaSkinModel::instance()->paintBackgroundTo(mapping, this, x, y);
+}
+
+QSize WaWidget::sizeHint() {
+ return WaSkinModel::instance()->getMapGeometry(mapping).size();
+}
+
+void WaWidget::mousePressEvent(QMouseEvent *e) {
+ if (e->button() == RightButton)
+ NoatunStdAction::ContextMenu::showContextMenu();
+}
+
+void WaWidget::skinChanged() {
+ setGeometry(WaSkinModel::instance()->getMapGeometry(mapping));
+ update();
+}
+
+#include <waWidget.moc>
diff --git a/noatun/modules/winskin/waWidget.h b/noatun/modules/winskin/waWidget.h
new file mode 100644
index 00000000..f2a41c82
--- /dev/null
+++ b/noatun/modules/winskin/waWidget.h
@@ -0,0 +1,31 @@
+#ifndef _WAWIDGET_H
+#define _WAWIDGET_H
+
+#include <qwidget.h>
+#include "waSkinModel.h"
+
+class WaWidget : public QWidget {
+ Q_OBJECT
+public:
+ WaWidget(int mapping);
+ virtual ~WaWidget();
+
+ QSize sizeHint();
+
+ void paintPixmap(int wa_mapping);
+ void paintPixmap(int wa_mapping, int number);
+
+ void paintPixmap(int wa_mapping, int x, int y);
+ void paintPixmap(int wa_mapping, int number, int x, int y);
+
+ void paintBackground() { WaSkinModel::instance()->paintBackgroundTo(mapping, this, 0, 0); }
+
+protected:
+ void mousePressEvent(QMouseEvent *);
+ int mapping;
+
+private slots:
+ void skinChanged();
+};
+
+#endif
diff --git a/noatun/modules/winskin/winSkinConfig.cpp b/noatun/modules/winskin/winSkinConfig.cpp
new file mode 100644
index 00000000..649fd1fe
--- /dev/null
+++ b/noatun/modules/winskin/winSkinConfig.cpp
@@ -0,0 +1,174 @@
+#include <noatun/pref.h>
+
+#include <klocale.h>
+#include <qpushbutton.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qpixmap.h>
+#include <kglobal.h>
+#include <kconfig.h>
+#include <qslider.h>
+#include <qframe.h>
+#include <qstringlist.h>
+#include <kfile.h>
+#include <kfiledialog.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <kurlrequester.h>
+#include <kurlrequesterdlg.h>
+#include <qdir.h>
+
+#include "waSkin.h"
+#include "waInfo.h"
+#include "waSkinManager.h"
+#include "winSkinConfig.h"
+
+WinSkinConfig::WinSkinConfig(QWidget * parent, WaSkinManager *waSkinManager) :
+ CModule(i18n("Winskin"),
+ i18n("Skin Selection for the Winskin Plugin"),
+ "style",
+ parent)
+{
+ // Make a token horizontal layout box
+ vbox = new QVBoxLayout(this);
+ vbox->setSpacing( 6 );
+ vbox->setMargin( 0 );
+
+ // Add a simple list of skins, populated in WinSkinConfig::reopen()
+ skin_list = new QListBox(this, "skin_list");
+ vbox->addWidget(skin_list);
+
+ QHBoxLayout* hbox = new QHBoxLayout( 0, 6, 6 );
+
+ QPushButton* buttonInstall = new QPushButton( i18n("&Install New Skin..."), this );
+ hbox->addWidget(buttonInstall);
+
+ buttonRemove = new QPushButton( i18n("&Remove Skin"), this );
+ buttonRemove->setEnabled(false);
+ hbox->addWidget(buttonRemove);
+ vbox->addLayout(hbox);
+
+ connect( skin_list, SIGNAL(highlighted(const QString &)), this, SLOT(selected()));
+ connect( buttonInstall, SIGNAL(clicked()), this, SLOT(install()));
+ connect( buttonRemove, SIGNAL(clicked()), this, SLOT(remove()));
+ connect(waSkinManager, SIGNAL(updateSkinList()), this, SLOT(reopen()));
+
+ mWaSkinManager = waSkinManager;
+
+ QGroupBox *settingsBox = new QGroupBox( 1, Vertical, i18n("Settings"), this );
+ vbox->addWidget(settingsBox);
+
+ QHBox *box = new QHBox(settingsBox);
+ QLabel *label = new QLabel(i18n("T&itle scrolling speed:"), box);
+ new QLabel(i18n("None"), box);
+
+ scrollSpeed = new QSlider(box);
+ label->setBuddy(scrollSpeed);
+ scrollSpeed->setMinimumSize( QSize( 80, 0 ) );
+ scrollSpeed->setMinValue( 0 );
+ scrollSpeed->setMaxValue( 50 );
+ scrollSpeed->setPageStep( 1 );
+ scrollSpeed->setOrientation( QSlider::Horizontal );
+ scrollSpeed->setTickmarks( QSlider::NoMarks );
+
+ label = new QLabel(i18n("Fast"), box);
+
+ reopen();
+}
+
+void WinSkinConfig::save()
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup("Winskin");
+ config->writeEntry("CurrentSkin", skin_list->currentText());
+ config->writeEntry("ScrollDelay", scrollSpeed->value());
+ config->sync();
+
+ if (skin_list->currentText() != orig_skin) {
+ _waskin_instance->loadSkin(skin_list->currentText());
+ orig_skin = skin_list->currentText();
+ }
+ else
+ {
+ _waskin_instance->skinInfo()->scrollerSetup();
+ }
+}
+
+void WinSkinConfig::reopen() {
+ // Wipe out the old list
+ skin_list->clear();
+
+ // Get a list of skins
+ QStringList skins = mWaSkinManager->availableSkins();
+
+ // This loop adds them all to our skin list
+ for(unsigned int x = 0;x < skins.count();x++) {
+ // Add ourselves to the list
+ skin_list->insertItem(skins[x]);
+ }
+
+ // Figure out our current skin
+ QString orig_skin = mWaSkinManager->currentSkin();
+
+ // Where is that skin in our big-list-o-skins?
+ QListBoxItem *item = skin_list->findItem(orig_skin);
+
+ if (item) {
+ // Aha, found it... make it the currently selected skin
+ skin_list->setCurrentItem( item );
+ }
+ else {
+ // Er, it's not there... select the current item
+ // Maybe this should emit a warning? Oh well, it's not harmful
+ skin_list->setCurrentItem( 0 );
+ }
+
+ KConfig *config=KGlobal::config();
+ config->setGroup("Winskin");
+ scrollSpeed->setValue(config->readNumEntry("ScrollDelay", 15));
+}
+
+void WinSkinConfig::selected()
+{
+ buttonRemove->setEnabled(mWaSkinManager->skinRemovable( skin_list->currentText() ));
+}
+
+void WinSkinConfig::install()
+{
+ QString url;
+
+ // Ask the user for directory containing a skin
+ KURLRequesterDlg* udlg = new KURLRequesterDlg( QString::null, this, "udlg", true );
+ udlg->urlRequester()->setFilter(mWaSkinManager->skinMimeTypes().join(" "));
+ udlg->urlRequester()->setMode( KFile::File | KFile::Directory | KFile::ExistingOnly );
+
+ if( udlg->exec() == QDialog::Accepted ) {
+ url = udlg->urlRequester()->url();
+ mWaSkinManager->installSkin( url );
+ }
+}
+
+void WinSkinConfig::remove()
+{
+ // Is there any item selected ??
+ if( skin_list->currentText().isEmpty() )
+ return;
+
+ // We can't remove every skin
+ if( !mWaSkinManager->skinRemovable( skin_list->currentText() ) ) {
+ KMessageBox::information( this, i18n("You cannot remove this skin.") );
+ // Reload skin list, perhaps the skin is already removed!
+ return;
+ }
+
+ // Ask the user first
+ if( KMessageBox::warningContinueCancel( this,
+ i18n("<qt>Are you sure you want to remove the <b>%1</b> skin?</qt>").arg( skin_list->currentText() ), QString::null, KStdGuiItem::del() )
+ == KMessageBox::Continue ) {
+
+ mWaSkinManager->removeSkin( skin_list->currentText() );
+ reopen();
+ }
+}
+
+#include <winSkinConfig.moc>
diff --git a/noatun/modules/winskin/winSkinConfig.h b/noatun/modules/winskin/winSkinConfig.h
new file mode 100644
index 00000000..22dca884
--- /dev/null
+++ b/noatun/modules/winskin/winSkinConfig.h
@@ -0,0 +1,35 @@
+#ifndef __WINSKINCONFIG_H
+#define __WINSKINCONFIG_H
+
+#include <noatun/pref.h>
+#include <qwidget.h>
+
+class QVBoxLayout;
+class WaSkinManager;
+class QSlider;
+
+class WinSkinConfig:public CModule {
+ Q_OBJECT
+ public:
+ WinSkinConfig(QWidget * parent, WaSkinManager *waManager);
+
+ void save();
+
+ public slots:
+ void reopen();
+
+ private slots:
+ void selected();
+ void install();
+ void remove();
+
+ private:
+ WaSkinManager *mWaSkinManager;
+ QListBox *skin_list;
+ QString orig_skin;
+ QVBoxLayout *vbox;
+ QPushButton *buttonRemove;
+ QSlider *scrollSpeed;
+};
+
+#endif
diff --git a/noatun/modules/winskin/winSkinVis.cpp b/noatun/modules/winskin/winSkinVis.cpp
new file mode 100644
index 00000000..f9937981
--- /dev/null
+++ b/noatun/modules/winskin/winSkinVis.cpp
@@ -0,0 +1,107 @@
+/*
+ noatun visualisation interface for winskin
+ Copyright (C) 2001 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "winSkinVis.h"
+
+#define __BANDS 75
+#define __SPAHEIGHT 15
+
+WinSkinVis::WinSkinVis(QObject *parent, const char *name):
+ QObject(parent,name),Visualization(50) {
+
+ m_currentPeaks=new float[__BANDS];
+
+ for(unsigned int i=0;i<__BANDS;i++)
+ m_currentPeaks[i]=0.0;
+
+ // If we can create our server-side object, visualize away
+ if (initServerObject())
+ {
+ start();
+ }
+}
+
+
+WinSkinVis::~WinSkinVis()
+{
+ if (m_winSkinFFT != NULL) {
+ if (connected())
+ {
+ visualizationStack().remove(m_id);
+ m_winSkinFFT->stop();
+ delete m_winSkinFFT;
+ }
+ }
+
+ delete[] m_currentPeaks;
+}
+
+
+bool WinSkinVis::initServerObject()
+{
+ // Create FFT on server
+ m_winSkinFFT = new Noatun::WinSkinFFT();
+ *m_winSkinFFT = Arts::DynamicCast(server()->createObject("Noatun::WinSkinFFT"));
+
+ if ( (*m_winSkinFFT).isNull() ) {
+ delete m_winSkinFFT;
+ m_winSkinFFT=NULL;
+ }
+ else
+ {
+ m_winSkinFFT->bandResolution(__BANDS);
+ m_winSkinFFT->start();
+ m_id=visualizationStack().insertBottom(*m_winSkinFFT, "WinSkin FFT");
+ }
+
+ return (m_winSkinFFT != NULL);
+}
+
+void WinSkinVis::timeout()
+{
+ std::vector<float> *data(m_winSkinFFT->scope());
+
+ float *f=&data->front();
+ if (data->size())
+ scopeEvent(f, data->size());
+
+ delete data;
+}
+
+float* WinSkinVis::currentPeaks()
+{
+ return m_currentPeaks;
+}
+
+void WinSkinVis::scopeEvent(float* bandPtr, unsigned int bands)
+{
+ for (unsigned int i = 0;i < bands;i++) {
+ float value=bandPtr[i];
+ // if the peak is less we prefer the higher one
+ if (m_currentPeaks[i] < value)
+ m_currentPeaks[i] = value;
+ else
+ m_currentPeaks[i] = m_currentPeaks[i]-1.3;
+
+ if (m_currentPeaks[i] < 0.0)
+ m_currentPeaks[i] = 0.0;
+
+ if (m_currentPeaks[i] > __SPAHEIGHT)
+ m_currentPeaks[i]=__SPAHEIGHT;
+ }
+ emit(doRepaint());
+}
+
+
+#include "winSkinVis.moc"
diff --git a/noatun/modules/winskin/winSkinVis.h b/noatun/modules/winskin/winSkinVis.h
new file mode 100644
index 00000000..7d139d0c
--- /dev/null
+++ b/noatun/modules/winskin/winSkinVis.h
@@ -0,0 +1,54 @@
+/*
+ noatun visualisation interface for winskin
+ Copyright (C) 2001 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __WINSKINVIS_H
+#define __WINSKINVIS_H
+
+#include <qobject.h>
+
+// These are needed for the Visualisation class
+#include <soundserver.h>
+#include "noatunarts/noatunarts.h"
+#include "noatun/plugin.h"
+
+#include "vis/winskinvis.h"
+
+class WinSkinVis : public QObject, public Visualization {
+ Q_OBJECT
+
+ public:
+ WinSkinVis(QObject* parent,const char* name);
+ ~WinSkinVis();
+
+ /**
+ * reimplemented from class Visualization, you
+ * should never need to reimplement this yourself
+ **/
+ void timeout();
+ float* currentPeaks();
+
+ signals:
+ void doRepaint();
+
+ private:
+ bool initServerObject();
+ void scopeEvent(float* spectrum, unsigned int size);
+
+ Noatun::WinSkinFFT* m_winSkinFFT;
+ long m_id;
+ float* m_currentPeaks;
+
+
+};
+
+#endif
diff --git a/noatun/modules/winskin/winskin.plugin b/noatun/modules/winskin/winskin.plugin
new file mode 100644
index 00000000..0f8b1074
--- /dev/null
+++ b/noatun/modules/winskin/winskin.plugin
@@ -0,0 +1,122 @@
+Filename=noatun_winskin.la
+Author=Ryan Cumming, Martin Vogt
+Site=http://mpeglib.sf.net
+Email=bodnar42@phalynx.dhs.org
+Type=userinterface
+License=LGPL
+Name=WinAmp Interface
+Name[af]=Winamp Koppelvlak
+Name[ar]=واجهة WinAmp
+Name[az]=WinAMP Ara üzü
+Name[bn]=উইন-অ্যাম্প ইন্টারফেস
+Name[br]=Talbenn evit WinAmp
+Name[ca]=Interfície WinAmp
+Name[cs]=Rozhraní WinAmpu
+Name[cy]=Rhyngwyneb Winamp
+Name[da]=Winamp-grænseflade
+Name[de]=Winamp-Oberfläche
+Name[el]=Περιβάλλον WinAmp
+Name[eo]=WinAmp-interfaco
+Name[es]=Interfaz de Winamp
+Name[et]=WinAmp liides
+Name[eu]=WinAmp interfazea
+Name[fa]=واسط WinAmp
+Name[fi]=WinAmp-käyttöliittymä
+Name[fr]=Interface Winamp
+Name[ga]=Comhéadan WinAmp
+Name[gl]=Interface de WinAmp
+Name[he]=ממשק WinAmp
+Name[hi]= विनएम्प इंटरफेस
+Name[hr]=WinAmp sučelje
+Name[hu]=Winamp-felület
+Name[id]=Interface winamp
+Name[is]=WinAmp andlit
+Name[it]=Interfaccia WinAmp
+Name[ja]=WinAmp インターフェース
+Name[kk]=WinAmp интерфейсі
+Name[km]=ចំណុច​ប្រទាក់ WinAmp
+Name[ko]=WinAmp 인터페이스
+Name[lt]=WinAmp sąsaja
+Name[lv]=WinAmp Starpseja
+Name[mk]=Интерфејс WinAmp
+Name[ms]=Antaramuka WinAmp
+Name[mt]=Interfaċċja WinAmp
+Name[nb]=WinAmp-grensesnitt
+Name[nds]=Winamp-Böversiet
+Name[ne]=WinAmp इन्टरफेस
+Name[nl]=WinAmp-interface
+Name[nn]=WinAmp-grensesnitt
+Name[pa]=WinAmp ਇੰਟਰਫੇਸ
+Name[pl]=Motyw WinAmpa
+Name[pt]=Interface do WinAmp
+Name[pt_BR]=Interface WinAmp
+Name[ro]=Interfaţă WinAmp
+Name[ru]=Интерфейс WinAmp
+Name[sk]=Rozhranie WinAmp
+Name[sl]=Vmesnik WinAmp
+Name[sr]=WinAmp интерфејс
+Name[sr@Latn]=WinAmp interfejs
+Name[sv]=Winamp-gränssnitt
+Name[ta]=வின் ஆம்ப் முகப்பு
+Name[tg]=Интерфейси WinAmp
+Name[th]=ส่วนติดต่อแบบ WinAmp
+Name[tr]=WinAmp Arayüzü
+Name[uk]=Інтерфейс WinAmp
+Name[ven]=Interface ya WinAmp
+Name[wa]=Eterface po WinAmp
+Name[xh]=Ujongano lwe WinAmp
+Name[zh_CN]=WinAmp 界面
+Name[zh_HK]=WinAmp 界面
+Name[zh_TW]=WinAmp 界面
+Name[zu]=Uxhumano olubhekeneyo lwe WinAmp
+Comment=A Winamp skin loader
+Comment[bg]=Зареждане на теми за Winamp
+Comment[bn]=একটি উইন-অ্যাম্প স্কিন লোডার
+Comment[br]=Ur c'harger a groc'hen Winamp
+Comment[bs]=Učitavanje Winamp skinova
+Comment[ca]=Carregador d'aparences de Winamp
+Comment[cs]=Zavaděč motivů WinAmpu
+Comment[cy]=Llwythydd Crwyn Winamp
+Comment[da]=En Winamp-forsideindlæser
+Comment[de]=Import von Winamp-Designs
+Comment[el]=Φόρτωση θεμάτων Winamp
+Comment[eo]=WinAmp-etosŝargilo
+Comment[es]=Un cargador de pieles de Winamp
+Comment[et]=Winampi rüüde laadija
+Comment[eu]=Winamp azal kargatzailea
+Comment[fa]=یک بارکنندۀ Winamp skin
+Comment[fi]=Winamp-nahkojen lataaja
+Comment[fr]=Un chargeur de revêtement Winamp
+Comment[gl]=Un cargador de peles de Winamp
+Comment[he]=טוען Skins של Winamp
+Comment[hu]=Betöltőprogram Winamp-kinézetekhez
+Comment[is]=Les skinn fyrir WinAmp
+Comment[it]=Caricatore di skin di Winamp
+Comment[ja]=Winamp のスキンローダ
+Comment[kk]=Winamp тысының жүктегіші
+Comment[km]=កម្មវិធី​ផ្ទុក​ស្បែក Winamp
+Comment[ko]=Winamp 스킨 로더
+Comment[lt]=Winamp pavidalų įkėliklis
+Comment[mk]=Вчитувач на маски за Winamp
+Comment[nb]=Laster WinAmp-drakt
+Comment[nds]=En Winamp-Böversietlader
+Comment[ne]=विन्याप स्किन लोडर
+Comment[nl]=Een Winamp-skinlader
+Comment[nn]=Lastar WinAmp-skal
+Comment[pl]=Ładowarka skór Winampa
+Comment[pt]=Um leitor de aspectos do WinAmp
+Comment[pt_BR]=Um carregador de aparências (skins) do Winamp
+Comment[ro]=Un încărcător de tematici WinAmp
+Comment[ru]=Загрузчик тем Winamp
+Comment[sk]=Načítavač tém WinAmp
+Comment[sl]=Nalagalnik preoblek Winamp
+Comment[sr]=Учитавач Winamp-ових кошуљица
+Comment[sr@Latn]=Učitavač Winamp-ovih košuljica
+Comment[sv]=Skalladdare för Winamp
+Comment[ta]=வின் ஆம்ப் அலங்கார அமைப்பு ஏற்றி
+Comment[th]=ตัวโหลดหน้ากากแบบวินแอมป์
+Comment[tr]=Winamp arayüz yükleyici
+Comment[uk]=Завантажувач жупанів Winamp
+Comment[zh_CN]=Winamp 外观载入器
+Comment[zh_HK]=Winamp Skin 載入器
+Comment[zh_TW]=Winamp 面板載入器
diff --git a/noatun/noatun.api b/noatun/noatun.api
new file mode 100644
index 00000000..abf3bf87
--- /dev/null
+++ b/noatun/noatun.api
@@ -0,0 +1,169 @@
+# Doxygen configuration generated by Doxywizard version 0.1
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME = Noatun
+PROJECT_NUMBER = 2.7.0
+OUTPUT_DIRECTORY = apidocs
+OUTPUT_LANGUAGE = English
+EXTRACT_ALL = NO
+EXTRACT_PRIVATE = NO
+EXTRACT_STATIC = YES
+EXTRACT_LOCAL_CLASSES = YES
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+HIDE_FRIEND_COMPOUNDS = YES
+HIDE_IN_BODY_DOCS = NO
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ALWAYS_DETAILED_SEC = YES
+FULL_PATH_NAMES = NO
+STRIP_FROM_PATH =
+INTERNAL_DOCS = NO
+SOURCE_BROWSER = YES
+INLINE_SOURCES = NO
+STRIP_CODE_COMMENTS = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION = YES
+CASE_SENSE_NAMES = YES
+HIDE_SCOPE_NAMES = NO
+VERBATIM_HEADERS = YES
+SHOW_INCLUDE_FILES = YES
+JAVADOC_AUTOBRIEF = NO
+INHERIT_DOCS = YES
+INLINE_INFO = YES
+SORT_MEMBER_DOCS = YES
+DISTRIBUTE_GROUP_DOC = NO
+TAB_SIZE = 8
+ENABLED_SECTIONS =
+GENERATE_TODOLIST = NO
+GENERATE_TESTLIST = NO
+GENERATE_BUGLIST = NO
+GENERATE_DEPRECATEDLIST= YES
+ALIASES =
+MAX_INITIALIZER_LINES = 30
+OPTIMIZE_OUTPUT_FOR_C = NO
+SHOW_USED_FILES = YES
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET = YES
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = YES
+WARN_IF_DOC_ERROR = YES
+WARN_FORMAT =
+WARN_LOGFILE =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT = library/noatun
+FILE_PATTERNS = *.h
+RECURSIVE = NO
+EXCLUDE_PATTERNS = *.moc.* \
+ moc* \
+ *.all_cpp.* \
+ *unload.* \
+EXAMPLE_PATH =
+EXAMPLE_PATTERNS =
+EXAMPLE_RECURSIVE = NO
+IMAGE_PATH =
+INPUT_FILTER =
+FILTER_SOURCE_FILES = NO
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX = YES
+COLS_IN_ALPHA_INDEX = 3
+IGNORE_PREFIX =
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML = YES
+HTML_OUTPUT = libnoatun/html
+HTML_HEADER = apidocs/common/header.html
+HTML_FOOTER = apidocs/common/footer.html
+HTML_STYLESHEET = apidocs/common/doxygen.css
+HTML_ALIGN_MEMBERS = YES
+GENERATE_HTMLHELP = NO
+CHM_FILE =
+HHC_LOCATION =
+GENERATE_CHI = NO
+BINARY_TOC = NO
+TOC_EXPAND = NO
+DISABLE_INDEX = YES
+ENUM_VALUES_PER_LINE = 4
+GENERATE_TREEVIEW = NO
+TREEVIEW_WIDTH = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX = NO
+LATEX_OUTPUT = latex
+COMPACT_LATEX = NO
+PAPER_TYPE = a4wide
+EXTRA_PACKAGES =
+LATEX_HEADER =
+PDF_HYPERLINKS = NO
+USE_PDFLATEX = NO
+LATEX_BATCHMODE = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF = NO
+RTF_OUTPUT = rtf
+COMPACT_RTF = NO
+RTF_HYPERLINKS = NO
+RTF_STYLESHEET_FILE =
+RTF_EXTENSIONS_FILE =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN = NO
+MAN_OUTPUT = man
+MAN_EXTENSION = .3
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = NO
+EXPAND_ONLY_PREDEF = NO
+SEARCH_INCLUDES = YES
+INCLUDE_PATH =
+INCLUDE_FILE_PATTERNS =
+PREDEFINED =
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = YES
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references
+#---------------------------------------------------------------------------
+TAGFILES =
+GENERATE_TAGFILE = noatun.tag
+ALLEXTERNALS = NO
+EXTERNAL_GROUPS = NO
+PERL_PATH = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS = YES
+HIDE_UNDOC_RELATIONS = NO
+HAVE_DOT = YES
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = NO
+UML_LOOK = NO
+TEMPLATE_RELATIONS = YES
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+CALL_GRAPH = NO
+GRAPHICAL_HIERARCHY = YES
+DOT_IMAGE_FORMAT = png
+DOT_PATH =
+DOTFILE_DIRS =
+MAX_DOT_GRAPH_WIDTH = 1024
+MAX_DOT_GRAPH_HEIGHT = 1024
+MAX_DOT_GRAPH_DEPTH = 0
+GENERATE_LEGEND = YES
+DOT_CLEANUP = YES
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE = NO
diff --git a/noatun/noatun.desktop b/noatun/noatun.desktop
new file mode 100644
index 00000000..2643d967
--- /dev/null
+++ b/noatun/noatun.desktop
@@ -0,0 +1,83 @@
+[Desktop Entry]
+Name=Noatun
+Name[bn]=নোটান
+Name[eo]=Sonorludilo
+Name[hi]=नोआट्यून
+Name[is]=Nóatún
+Name[ne]=नोवटुन
+Name[pa]=ਨੌਟੋਮ
+Name[ta]=நோடன்
+Exec=noatun %i %m -caption "%c" %U
+Icon=noatun
+Type=Application
+DocPath=noatun/index.html
+MimeType=audio/x-mp3;audio/x-mp2;audio/mpeg;audio/vorbis;application/ogg;audio/x-mpegurl;audio/x-wav;audio/x-mod;video/mpeg;video/x-msvideo;video/quicktime;video/mp4;video/x-flic;video/x-theora;video/x-ogm;audio/basic;audio/mp4;audio/ac3;audio/aac;audio/x-flac;audio/x-oggflac;audio/x-speex;audio/x-musepack;audio/x-ms-wma;
+GenericName=Media Player
+GenericName[af]=Media Speler
+GenericName[ar]=مشغل وسائط
+GenericName[bg]=Мултимедиен плеър
+GenericName[bn]=মিডিয়া প্লেয়ার
+GenericName[br]=Soner liesvedia
+GenericName[ca]=Reproductor multimèdia
+GenericName[cs]=Přehrávač médií
+GenericName[cy]=Chwaraeydd Cyfryngau
+GenericName[da]=Medieafspiller
+GenericName[de]=Multimedia-Wiedergabe
+GenericName[el]=Αναπαραγωγέας μέσων
+GenericName[eo]=Ludilo por sonor-dosieroj
+GenericName[es]=Reproductor multimedia
+GenericName[et]=Multimeedia mängija
+GenericName[eu]=Euskarri erreproduzigailua
+GenericName[fa]=پخش‌کنندۀ رسانه
+GenericName[fi]=Mediasoitin
+GenericName[fr]=Lecteur multimédia
+GenericName[ga]=Seinnteoir Meán
+GenericName[gl]=Reproductor Multimedia
+GenericName[he]=נגן מדיה
+GenericName[hi]=मीडिया प्लेयर
+GenericName[hu]=Médialejátszó
+GenericName[is]=Margmiðlunarforrit
+GenericName[it]=Lettore multimediale
+GenericName[ja]=メディアプレーヤ
+GenericName[kk]=Media ойнатқышы
+GenericName[km]=កម្មវិធី​ចាក់​មេ​ឌៀ
+GenericName[ko]=미디어 재생기
+GenericName[lt]=Media grotuvas
+GenericName[lv]=Mēdiju Atskaņotājs
+GenericName[mk]=Медијaплеер
+GenericName[nb]=Mediaspiller
+GenericName[nds]=Medienafspeler
+GenericName[ne]=मिडिया प्लेयर
+GenericName[nl]=Mediaspeler
+GenericName[nn]=Mediespelar
+GenericName[pa]=ਸੰਗੀਤ ਵਾਜਾ
+GenericName[pl]=Odtwarzacz multimedialny
+GenericName[pt]=Leitor Multimédia
+GenericName[pt_BR]=Reprodutor de Mídia
+GenericName[ro]=Program de redare multimedia
+GenericName[ru]=Медиаплеер
+GenericName[se]=Mediačuojaheaddji
+GenericName[sk]=Prehrávač médií
+GenericName[sl]=Večpredstavnostni predvajalnik
+GenericName[sr]=Медија плејер
+GenericName[sr@Latn]=Medija plejer
+GenericName[sv]=Mediaspelare
+GenericName[ta]=ஊடக இயக்கி
+GenericName[tg]=Бозингари Расона
+GenericName[th]=โปรแกรมเล่นแฟ้มสื่อ
+GenericName[tr]=Medya Yürütücüsü
+GenericName[uk]=Програвач медіа-матеріалів
+GenericName[uz]=Media pleyer
+GenericName[uz@cyrillic]=Медиа плейер
+GenericName[ven]=Tshitambi tsha Media
+GenericName[wa]=Djouweu multimedia
+GenericName[xh]=Umdlali we Midia
+GenericName[zh_CN]=媒体播放器
+GenericName[zh_HK]=媒體播放器
+GenericName[zh_TW]=媒體播放器
+GenericName[zu]=Umdlali Womculo
+Terminal=false
+InitialPreference=6
+X-KDE-StartupNotify=true
+X-DCOP-ServiceType=Unique
+Categories=Qt;KDE;AudioVideo;
diff --git a/noatun/noatun.upd b/noatun/noatun.upd
new file mode 100644
index 00000000..c58d08cd
--- /dev/null
+++ b/noatun/noatun.upd
@@ -0,0 +1,5 @@
+Id=noatun20
+File=noatunrc
+Script=noatun20update
+Group=KDE
+RemoveKey=MultipleInstances
diff --git a/noatun/noatun20update.cpp b/noatun/noatun20update.cpp
new file mode 100644
index 00000000..e6a611f0
--- /dev/null
+++ b/noatun/noatun20update.cpp
@@ -0,0 +1,41 @@
+// Convert Noatun 1.2 plugins that no longer exist to their closest 2.0
+// counterparts.
+
+#include <qfile.h>
+#include <qregexp.h>
+
+#ifndef stdin
+extern "C" FILE *stdin;
+#endif
+
+#ifndef stdout
+extern "C" FILE *stdout;
+#endif
+
+int main(int, char **)
+{
+ QFile qin, qout;
+ qin.open(IO_ReadOnly, stdin);
+ qout.open(IO_WriteOnly, stdout);
+
+ QString text = qin.readAll();
+
+ // tag loaders
+ bool tagloaders = text.contains("id3tag.plugin") ||
+ text.contains("oggtag.plugin") ||
+ text.contains("luckytag.plugin");
+
+ text.replace(QRegExp("id3tag\\.plugin"), "");
+ text.replace(QRegExp("oggtag\\.plugin"), "");
+ text.replace(QRegExp("luckytag\\.plugin"), "");
+
+ if(tagloaders) text.replace(QRegExp("Modules="), "Modules=metatag.plugin,");
+
+ // playlists
+ text.replace(QRegExp("tron\\.plugin"), "splitplaylist.plugin");
+ text.replace(QRegExp("liszt\\.plugin"), "splitplaylist.plugin");
+
+ qout.writeBlock(text.local8Bit());
+
+ return 0;
+}
diff --git a/noatun/pics/Makefile.am b/noatun/pics/Makefile.am
new file mode 100644
index 00000000..07e86dbf
--- /dev/null
+++ b/noatun/pics/Makefile.am
@@ -0,0 +1,2 @@
+noatuniconsdir = $(kde_datadir)/noatun/icons
+noatunicons_ICON = AUTO
diff --git a/noatun/pics/cr128-action-equalizer.png b/noatun/pics/cr128-action-equalizer.png
new file mode 100644
index 00000000..b526abef
--- /dev/null
+++ b/noatun/pics/cr128-action-equalizer.png
Binary files differ
diff --git a/noatun/pics/cr128-action-playlist.png b/noatun/pics/cr128-action-playlist.png
new file mode 100644
index 00000000..f825459c
--- /dev/null
+++ b/noatun/pics/cr128-action-playlist.png
Binary files differ
diff --git a/noatun/pics/cr16-action-effect.png b/noatun/pics/cr16-action-effect.png
new file mode 100644
index 00000000..c388b4ae
--- /dev/null
+++ b/noatun/pics/cr16-action-effect.png
Binary files differ
diff --git a/noatun/pics/cr16-action-equalizer.png b/noatun/pics/cr16-action-equalizer.png
new file mode 100644
index 00000000..001826c4
--- /dev/null
+++ b/noatun/pics/cr16-action-equalizer.png
Binary files differ
diff --git a/noatun/pics/cr16-action-noatunback.png b/noatun/pics/cr16-action-noatunback.png
new file mode 100644
index 00000000..13db3289
--- /dev/null
+++ b/noatun/pics/cr16-action-noatunback.png
Binary files differ
diff --git a/noatun/pics/cr16-action-noatunforward.png b/noatun/pics/cr16-action-noatunforward.png
new file mode 100644
index 00000000..0ff89c3e
--- /dev/null
+++ b/noatun/pics/cr16-action-noatunforward.png
Binary files differ
diff --git a/noatun/pics/cr16-action-noatunpause.png b/noatun/pics/cr16-action-noatunpause.png
new file mode 100644
index 00000000..1b88b6fe
--- /dev/null
+++ b/noatun/pics/cr16-action-noatunpause.png
Binary files differ
diff --git a/noatun/pics/cr16-action-noatunplay.png b/noatun/pics/cr16-action-noatunplay.png
new file mode 100644
index 00000000..abd8f18a
--- /dev/null
+++ b/noatun/pics/cr16-action-noatunplay.png
Binary files differ
diff --git a/noatun/pics/cr16-action-noatunplaylist.png b/noatun/pics/cr16-action-noatunplaylist.png
new file mode 100644
index 00000000..77e0e9b9
--- /dev/null
+++ b/noatun/pics/cr16-action-noatunplaylist.png
Binary files differ
diff --git a/noatun/pics/cr16-action-noatunstop.png b/noatun/pics/cr16-action-noatunstop.png
new file mode 100644
index 00000000..734184ec
--- /dev/null
+++ b/noatun/pics/cr16-action-noatunstop.png
Binary files differ
diff --git a/noatun/pics/cr16-action-playlist.png b/noatun/pics/cr16-action-playlist.png
new file mode 100644
index 00000000..ecaa54be
--- /dev/null
+++ b/noatun/pics/cr16-action-playlist.png
Binary files differ
diff --git a/noatun/pics/cr22-action-equalizer.png b/noatun/pics/cr22-action-equalizer.png
new file mode 100644
index 00000000..09d698e4
--- /dev/null
+++ b/noatun/pics/cr22-action-equalizer.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunback.png b/noatun/pics/cr22-action-noatunback.png
new file mode 100644
index 00000000..698f22df
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunback.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunfback.png b/noatun/pics/cr22-action-noatunfback.png
new file mode 100644
index 00000000..4634a633
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunfback.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunfforward.png b/noatun/pics/cr22-action-noatunfforward.png
new file mode 100644
index 00000000..2df91709
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunfforward.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunforward.png b/noatun/pics/cr22-action-noatunforward.png
new file mode 100644
index 00000000..d6eed5f0
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunforward.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunloopnone.png b/noatun/pics/cr22-action-noatunloopnone.png
new file mode 100644
index 00000000..d44109c3
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunloopnone.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunloopplaylist.png b/noatun/pics/cr22-action-noatunloopplaylist.png
new file mode 100644
index 00000000..142bfe5d
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunloopplaylist.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunlooprandom.png b/noatun/pics/cr22-action-noatunlooprandom.png
new file mode 100644
index 00000000..59916d74
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunlooprandom.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunloopsong.png b/noatun/pics/cr22-action-noatunloopsong.png
new file mode 100644
index 00000000..d18f40a5
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunloopsong.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunpause.png b/noatun/pics/cr22-action-noatunpause.png
new file mode 100644
index 00000000..1dde8554
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunpause.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunplay.png b/noatun/pics/cr22-action-noatunplay.png
new file mode 100644
index 00000000..d4fbdae4
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunplay.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunplaylist.png b/noatun/pics/cr22-action-noatunplaylist.png
new file mode 100644
index 00000000..008197fe
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunplaylist.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunstop.png b/noatun/pics/cr22-action-noatunstop.png
new file mode 100644
index 00000000..5881503a
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunstop.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatuntiny.png b/noatun/pics/cr22-action-noatuntiny.png
new file mode 100644
index 00000000..74bc5e94
--- /dev/null
+++ b/noatun/pics/cr22-action-noatuntiny.png
Binary files differ
diff --git a/noatun/pics/cr22-action-playlist.png b/noatun/pics/cr22-action-playlist.png
new file mode 100644
index 00000000..ec3006cf
--- /dev/null
+++ b/noatun/pics/cr22-action-playlist.png
Binary files differ
diff --git a/noatun/pics/cr32-action-effect.png b/noatun/pics/cr32-action-effect.png
new file mode 100644
index 00000000..e983d952
--- /dev/null
+++ b/noatun/pics/cr32-action-effect.png
Binary files differ
diff --git a/noatun/pics/cr32-action-equalizer.png b/noatun/pics/cr32-action-equalizer.png
new file mode 100644
index 00000000..383f7ce5
--- /dev/null
+++ b/noatun/pics/cr32-action-equalizer.png
Binary files differ
diff --git a/noatun/pics/cr32-action-playlist.png b/noatun/pics/cr32-action-playlist.png
new file mode 100644
index 00000000..a0d02f56
--- /dev/null
+++ b/noatun/pics/cr32-action-playlist.png
Binary files differ
diff --git a/noatun/pics/cr48-action-effect.png b/noatun/pics/cr48-action-effect.png
new file mode 100644
index 00000000..1394861e
--- /dev/null
+++ b/noatun/pics/cr48-action-effect.png
Binary files differ
diff --git a/noatun/pics/cr48-action-equalizer.png b/noatun/pics/cr48-action-equalizer.png
new file mode 100644
index 00000000..c0d75a68
--- /dev/null
+++ b/noatun/pics/cr48-action-equalizer.png
Binary files differ
diff --git a/noatun/pics/cr48-action-playlist.png b/noatun/pics/cr48-action-playlist.png
new file mode 100644
index 00000000..e9b51c89
--- /dev/null
+++ b/noatun/pics/cr48-action-playlist.png
Binary files differ
diff --git a/noatun/pics/cr64-action-equalizer.png b/noatun/pics/cr64-action-equalizer.png
new file mode 100644
index 00000000..39999c34
--- /dev/null
+++ b/noatun/pics/cr64-action-equalizer.png
Binary files differ
diff --git a/noatun/pics/cr64-action-playlist.png b/noatun/pics/cr64-action-playlist.png
new file mode 100644
index 00000000..4277fdaa
--- /dev/null
+++ b/noatun/pics/cr64-action-playlist.png
Binary files differ
diff --git a/noatun/preset.dance b/noatun/preset.dance
new file mode 100644
index 00000000..89ba2557
--- /dev/null
+++ b/noatun/preset.dance
@@ -0,0 +1,8 @@
+<!DOCTYPE NoatunEqualizer ><noatunequalizer version="1.1.354" name="Dance" level="-100" default="1" >
+ <band end="108" level="149" start="0" />
+ <band end="217" level="122" start="109" />
+ <band end="810" level="105" start="218" />
+ <band end="2431" level="109" start="811" />
+ <band end="7290" level="135" start="2432" />
+ <band end="19999" level="164" start="7291" />
+</noatunequalizer>
diff --git a/noatun/preset.eclecticguitar b/noatun/preset.eclecticguitar
new file mode 100644
index 00000000..bc41ce8d
--- /dev/null
+++ b/noatun/preset.eclecticguitar
@@ -0,0 +1,9 @@
+<!DOCTYPE NoatunEqualizer>
+<noatunequalizer version="1.1.354" level="-150" name="Eclectic Guitar" default="1">
+ <band end="108" start="0" level="-66" />
+ <band end="217" start="109" level="-56" />
+ <band end="810" start="218" level="38" />
+ <band end="2431" start="811" level="65" />
+ <band end="7290" start="2432" level="84" />
+ <band end="19999" start="7291" level="70" />
+</noatunequalizer>
diff --git a/noatun/preset.jazz b/noatun/preset.jazz
new file mode 100644
index 00000000..a20ed11e
--- /dev/null
+++ b/noatun/preset.jazz
@@ -0,0 +1,8 @@
+<!DOCTYPE NoatunEqualizer ><noatunequalizer version="1.1.354" name="Jazz" level="0" default="1" >
+ <band end="108" start="0" level="181" />
+ <band end="217" start="109" level="154" />
+ <band end="810" start="218" level="93" />
+ <band end="2431" start="811" level="130" />
+ <band end="7290" start="2432" level="160" />
+ <band end="19999" start="7291" level="177" />
+</noatunequalizer>
diff --git a/noatun/preset.metal b/noatun/preset.metal
new file mode 100644
index 00000000..747fe9eb
--- /dev/null
+++ b/noatun/preset.metal
@@ -0,0 +1,8 @@
+<!DOCTYPE NoatunEqualizer ><noatunequalizer version="1.1.354" name="Metal" level="-100" default="1" >
+ <band end="108" start="0" level="162" />
+ <band end="217" start="109" level="135" />
+ <band end="810" start="218" level="110" />
+ <band end="2431" start="811" level="124" />
+ <band end="7290" start="2432" level="149" />
+ <band end="19999" start="7291" level="175" />
+</noatunequalizer>
diff --git a/noatun/preset.trance b/noatun/preset.trance
new file mode 100644
index 00000000..78a38221
--- /dev/null
+++ b/noatun/preset.trance
@@ -0,0 +1,8 @@
+<!DOCTYPE NoatunEqualizer ><noatunequalizer version="1.1.354" name="Trance" level="-100" default="1" >
+ <band end="108" start="0" level="162" />
+ <band end="217" start="109" level="103" />
+ <band end="810" start="218" level="162" />
+ <band end="2431" start="811" level="112" />
+ <band end="7290" start="2432" level="177" />
+ <band end="19999" start="7291" level="112" />
+</noatunequalizer>
diff --git a/noatun/preset.zero b/noatun/preset.zero
new file mode 100644
index 00000000..82380e9a
--- /dev/null
+++ b/noatun/preset.zero
@@ -0,0 +1,9 @@
+<!DOCTYPE NoatunEqualizer>
+<noatunequalizer version="2.4.0" name="Zero" level="0" default="1" >
+ <band end="5" start="1" level="0" />
+ <band end="27" start="6" level="0" />
+ <band end="141" start="28" level="0" />
+ <band end="736" start="142" level="0" />
+ <band end="3835" start="737" level="0" />
+ <band end="19979" start="3836" level="0" />
+</noatunequalizer>
diff --git a/oggvorbis_artsplugin/Makefile.am b/oggvorbis_artsplugin/Makefile.am
new file mode 100644
index 00000000..aeec6205
--- /dev/null
+++ b/oggvorbis_artsplugin/Makefile.am
@@ -0,0 +1,23 @@
+# $Id$
+
+INCLUDES= -I$(kde_includes)/arts $(all_includes)
+
+noinst_HEADERS = oggPlayObject_impl.h
+
+lib_LTLIBRARIES = liboggarts.la
+liboggarts_la_COMPILE_FIRST = oggarts.h
+liboggarts_la_SOURCES = oggarts.cc oggPlayObject_impl.cpp
+liboggarts_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+liboggarts_la_LIBADD = -lkmedia2_idl -lsoundserver_idl -lartsflow -lvorbisfile -lvorbis -logg
+liboggarts_la_METASOURCES = AUTO
+
+oggarts.mcopclass: oggarts.h
+oggarts.mcoptype: oggarts.h
+oggarts.cc oggarts.h: $(srcdir)/oggarts.idl $(MCOPIDL)
+ $(MCOPIDL) -t -I$(kde_includes)/arts $(srcdir)/oggarts.idl
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = oggarts.mcoptype oggarts.mcopclass
+
+mcopclassdir = $(libdir)/mcop/Arts
+mcopclass_DATA = oggPlayObject.mcopclass
diff --git a/oggvorbis_artsplugin/configure.in.in b/oggvorbis_artsplugin/configure.in.in
new file mode 100644
index 00000000..9efe4fe9
--- /dev/null
+++ b/oggvorbis_artsplugin/configure.in.in
@@ -0,0 +1,5 @@
+if test "$kde_mpeglib_compiles" = "yes"
+then
+DO_NOT_COMPILE="$DO_NOT_COMPILE oggvorbis_artsplugin"
+fi
+
diff --git a/oggvorbis_artsplugin/oggPlayObject.mcopclass b/oggvorbis_artsplugin/oggPlayObject.mcopclass
new file mode 100644
index 00000000..8128ea64
--- /dev/null
+++ b/oggvorbis_artsplugin/oggPlayObject.mcopclass
@@ -0,0 +1,6 @@
+Interface=Arts::oggPlayObject,Arts::PlayObject,Arts::SynthModule,Arts::Object
+Library=liboggarts.la
+Author=Alex Zepeda <zipzippy@sonic.net>
+Extension=ogg
+MimeType=audio/x-vorbis,application/ogg
+Language=C++
diff --git a/oggvorbis_artsplugin/oggPlayObject_impl.cpp b/oggvorbis_artsplugin/oggPlayObject_impl.cpp
new file mode 100644
index 00000000..80fd058e
--- /dev/null
+++ b/oggvorbis_artsplugin/oggPlayObject_impl.cpp
@@ -0,0 +1,313 @@
+/* $Id$ */
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/stat.h>
+#include <sys/shm.h>
+#include <sys/wait.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <vorbis/codec.h>
+#include <vorbis/vorbisfile.h>
+
+/////////////////////////////////////////////////////////////
+// aRts interface
+
+#include <stdsynthmodule.h>
+#include "oggarts.h"
+#include <convert.h>
+#include <debug.h>
+
+
+#include "oggPlayObject_impl.h"
+
+using namespace Arts;
+
+oggPlayObject_impl::oggPlayObject_impl()
+{
+ struct shmid_ds bleh;
+ shm_id = shmget(IPC_PRIVATE, sizeof(*shm_buf), 0600);
+ shm_buf = (struct buf_t *)shmat(shm_id, 0, 0);
+
+ // mark it to be destroyed after the last detach
+ shmctl(shm_id, IPC_RMID, &bleh);
+
+ // sem0 has base
+ // sem1 remaining space
+ // sem2 seekTo
+ buflen_sem = semget(IPC_PRIVATE, 4, 0600);
+ child_pid = 0;
+}
+
+oggPlayObject_impl::~oggPlayObject_impl()
+{
+ halt();
+ union semun foo;
+ arts_debug("oggvorbis: removing IPC resources");
+ semctl(buflen_sem, 0, IPC_RMID, foo);
+}
+
+bool oggPlayObject_impl::loadMedia(const std::string &filename)
+{
+ halt(); // stop playing any previous stream
+ currentFile = filename;
+
+ //throw off a process to handle the decoding
+ //fill the buffers first...
+
+ short decode_buf[BACKBUFSIZ];
+
+ struct sembuf semoper;
+ semoper.sem_flg = 0;
+
+ buf_pos = 0;
+ // setup the initial semaphore joy
+
+ union semun foo;
+
+ foo.val = 0;
+ if (semctl(buflen_sem, 0, SETVAL, foo))
+ arts_debug("oggvorbis: couldn't clear queue %d, %s", errno, strerror(errno)); // no data in the queue
+
+ foo.val = 0;
+ // seekTo is -1 (ie, no seek)
+ if (semctl(buflen_sem, 2, SETVAL, foo))
+ arts_debug("oggvorbis: couldn't clear seekTo, %s",strerror(errno));
+
+ semctl(buflen_sem, 3, SETVAL, foo);
+ arts_debug("oggvorbis: seekTo is %d", semctl(buflen_sem, 2, GETVAL, foo));
+
+ // setup the starting free space in the buffer
+ foo.val = BACKBUFSIZ;
+ if (semctl(buflen_sem, 1, SETVAL, foo))
+ arts_debug("oggvorbis: couldn't mark buffer empty");
+
+ FILE *ogg_fp;
+ ogg_fp = fopen(filename.c_str(), "r");
+ int current_section=0;
+
+ ov_open(ogg_fp, &vf, NULL, 0);
+ if (!(child_pid = fork())) {
+ arts_debug("oggvorbis: child process");
+ do {
+ // get more data
+
+ int seekTo = semctl(buflen_sem, 2, GETVAL, foo);
+ if (seekTo) {
+ arts_debug("oggvorbis: seeking to %d", seekTo);
+ // we have a seek command
+ int ret = ov_time_seek(&vf, (double)seekTo-1);
+ arts_debug("oggvorbis: ov_time_seek returned: %d\n", ret);
+ foo.val=0;
+ semctl(buflen_sem, 2, SETVAL, foo); // we've done it
+ }
+ foo.val=(long)ov_time_tell(&vf);
+ if (foo.val == -1) foo.val=0;
+ semctl(buflen_sem, 3, SETVAL, foo);
+
+ int thisPass = ov_read(&vf, (char *)decode_buf, BACKBUFSIZ*sizeof(short), 0, 2, 1, &current_section);
+ if (thisPass == 0) {
+ // we're done, or we errored (in which case we're done)
+ break;
+ }
+ thisPass = (thisPass / 4);
+
+
+ semoper.sem_num = 1;
+ semoper.sem_op = -thisPass;
+ semop(buflen_sem, &semoper, 1);
+
+ // block until there's enough space to stick in this frame
+ int roomFor = semctl(buflen_sem, 1, GETVAL, foo);
+ if (roomFor > BACKBUFSIZ) {
+ arts_debug("oggvorbis: exit requested, bye!");
+ // this can never go above BACKBUFSIZ in normal operation,
+ // the consumer wants us to exit
+ break;
+ }
+
+ for (int i=0 ; i <thisPass ; ++i, buf_pos = ((buf_pos + 1) % BACKBUFSIZ)) {
+ shm_buf->left[buf_pos] = conv_16le_float(decode_buf[2*i]);
+ shm_buf->right[buf_pos] = conv_16le_float(decode_buf[2*i+1]);
+ }
+
+ //arts_debug("oggvorbis: enqueued them");
+ semoper.sem_num = 0;
+ semoper.sem_op = thisPass;
+ semop(buflen_sem, &semoper, 1); // mark the additional data now available
+
+ //arts_debug("oggvorbis: calculated %d more samples",shm_buf->back$
+ } while(1);
+
+ //signal completion
+ foo.val = 0;
+ // no more data available
+ semctl(buflen_sem, 0, SETVAL, foo);
+ // and no room either (ie, none coming)
+ semctl(buflen_sem, 1, SETVAL, foo);
+
+ arts_debug("oggvorbis: decoder process exiting");
+ exit(0);
+ }
+ return true;
+}
+
+std::string oggPlayObject_impl::description()
+{
+ return "ogg/vorbis artsplugin - this is my w00t!";
+}
+
+poTime oggPlayObject_impl::currentTime()
+{
+ poTime time;
+ union semun foo;
+ foo.val=0;
+ time.seconds = semctl(buflen_sem, 3, GETVAL, foo);
+ if (time.seconds == -1)
+ time.seconds = 0; // Eek infinite loop time.
+ time.ms=0;
+ //arts_debug("oggvorbis: time is now %d\n", time.seconds);
+ return time;
+}
+
+poTime oggPlayObject_impl::overallTime() {
+ poTime time;
+ time.seconds=(long)ov_time_total(&vf, -1);
+ time.ms=0;
+ return time;
+}
+
+poCapabilities oggPlayObject_impl::capabilities() {
+ return static_cast<poCapabilities>(capPause|capSeek);
+}
+
+std::string oggPlayObject_impl::mediaName() {
+ return currentFile;
+}
+
+poState oggPlayObject_impl::state() {
+ return mState;
+}
+
+void oggPlayObject_impl::play() {
+ arts_debug("oggvorbis: play");
+ mState = posPlaying;
+}
+
+void oggPlayObject_impl::halt()
+{
+ mState = posIdle;
+ union semun foo;
+
+ if (child_pid) {
+ arts_debug("oggvorbis: killing decoder process");
+ foo.val = 2*BACKBUFSIZ;
+ semctl(buflen_sem, 1, SETVAL, foo);
+ waitpid(child_pid, NULL, 0);
+ child_pid = 0;
+ }
+ // tell the producer to exit (would also result in us halting, if we weren't already)
+
+ // mainly this is to ensure that the decoder wakes up to notice
+}
+
+void oggPlayObject_impl::seek(const class poTime &t)
+{
+ union semun foo;
+
+ // this index is one-based so 0 can represent no seek
+ foo.val = static_cast<int>(t.seconds);
+ arts_debug("requesting seek to %d", foo.val);
+ semctl(buflen_sem, 2, SETVAL, foo); // we've done it
+}
+
+void oggPlayObject_impl::pause()
+{
+ mState = posPaused;
+}
+
+void oggPlayObject_impl::streamInit()
+{
+ arts_debug("oggvorbis: streamInit");
+}
+
+void oggPlayObject_impl::streamStart()
+{
+ arts_debug("ogg_vorbis: streamStart");
+}
+
+void oggPlayObject_impl::calculateBlock(unsigned long samples)
+{
+ int samplesAvailable = 0;
+
+ //arts_debug("oggvorbis: calculateBlock");
+
+ if (mState == posPlaying) {
+ //arts_debug("oggvorbis: calculateBlock, %d(%d) of %d samples in buffer",
+ //shm_buf->buflen - bufpos, shm_buf->backbuflen,samples);
+
+ struct sembuf bleh;
+
+ bleh.sem_num = 0;
+ bleh.sem_flg = IPC_NOWAIT;
+ //arts_debug("oggvorbis: %d samples wanted", samplesAvailable);
+ bleh.sem_op = -samples; // does the buffer have sufficient samples?
+ if (semop(buflen_sem, &bleh, 1) == -1) {
+ if (errno == EAGAIN) {
+ union semun foo;
+ arts_debug("oggvorbis: buffer underrun");
+ samplesAvailable = semctl(buflen_sem, 0, GETVAL, foo);
+ // no samples AND no room is the decoder's way of signalling completion
+ if (semctl(buflen_sem, 1, GETVAL, foo) == 0) {
+ halt();
+ samplesAvailable = 0;
+ }
+ } else {
+ // something awful has happened
+ halt();
+ samplesAvailable = 0;
+ }
+ } else {
+ samplesAvailable = samples; // number of samples we pushed from buffers
+ // used to calculate the number we should zero out for an underrun
+ }
+ bleh.sem_flg = 0; // back to normal now
+
+ //arts_debug("oggvorbis: %d samples available",samplesAvailable);
+ for (int i = 0; i < samplesAvailable;
+ ++i, buf_pos = ((buf_pos + 1) % BACKBUFSIZ)) {
+
+ left[i] = shm_buf->left[buf_pos];
+ right[i] = shm_buf->right[buf_pos];
+ }
+
+ bleh.sem_num = 1;
+ bleh.sem_op = samplesAvailable;
+ semop(buflen_sem, &bleh, 1); // mark the now-free space
+ }
+ // zero out any samples we didn't have enough to complete
+ while(static_cast<unsigned long>(samplesAvailable) < samples) {
+ left[samplesAvailable] = 0.0;
+ right[samplesAvailable] = 0.0;
+ samplesAvailable++;
+ }
+}
+
+void oggPlayObject_impl::streamEnd()
+{
+ arts_debug("oggvorbis: streamEnd");
+}
+
+REGISTER_IMPLEMENTATION(oggPlayObject_impl);
diff --git a/oggvorbis_artsplugin/oggPlayObject_impl.h b/oggvorbis_artsplugin/oggPlayObject_impl.h
new file mode 100644
index 00000000..2f1b0c0c
--- /dev/null
+++ b/oggvorbis_artsplugin/oggPlayObject_impl.h
@@ -0,0 +1,62 @@
+#ifndef OGGPLAYER_IMPL_H
+#define OGGPLAYER_IMPL_H "$Id$"
+
+#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) || defined(__DragonFly__)
+/* union semun is defined by including <sys/sem.h> */
+#else
+/* according to X/OPEN we have to define it ourselves */
+union semun {
+ int val; /* value for SETVAL */
+ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
+ unsigned short int *array; /* array for GETALL, SETALL */
+ struct seminfo *__buf; /* buffer for IPC_INFO */
+};
+#endif
+
+int buf_pos;
+
+namespace Arts {
+
+class oggPlayObject_impl
+ : public oggPlayObject_skel, public StdSynthModule
+{
+ public:
+ oggPlayObject_impl();
+ ~oggPlayObject_impl();
+ bool loadMedia(const std::string &filename);
+ std::string description();
+ poTime currentTime();
+ poTime overallTime();
+ poCapabilities capabilities();
+ std::string mediaName();
+ poState state();
+ void play();
+ void halt();
+ void seek(const class poTime &t);
+ void pause();
+ void streamInit();
+ void streamStart();
+ void calculateBlock(unsigned long samples);
+ void streamEnd();
+
+ protected:
+
+ static const int BACKBUFSIZ=4096;
+ OggVorbis_File vf;
+ std::string currentFile;
+
+ inline float conv_16le_float(short x)
+ { return static_cast<float>(x) / 32768.0; }
+
+ poState mState;
+ struct buf_t{
+ float left[BACKBUFSIZ];
+ float right[BACKBUFSIZ];
+ } *shm_buf;
+ int shm_id, child_pid;
+ int buflen_sem;
+};
+
+};
+
+#endif
diff --git a/oggvorbis_artsplugin/oggarts.idl b/oggvorbis_artsplugin/oggarts.idl
new file mode 100644
index 00000000..c56deac9
--- /dev/null
+++ b/oggvorbis_artsplugin/oggarts.idl
@@ -0,0 +1,12 @@
+#include <kmedia2.idl>
+#include <soundserver.idl>
+
+module Arts
+{
+
+interface oggPlayObject : PlayObject, SynthModule
+{
+ out audio stream left,right;
+};
+
+};
diff --git a/xine_artsplugin/Makefile.am b/xine_artsplugin/Makefile.am
new file mode 100644
index 00000000..799bff29
--- /dev/null
+++ b/xine_artsplugin/Makefile.am
@@ -0,0 +1,25 @@
+INCLUDES= -I$(kde_includes)/arts $(all_includes) $(XINE_CFLAGS)
+
+AM_CFLAGS = -U__STRICT_ANSI__
+
+lib_LTLIBRARIES = libarts_xine.la
+
+libarts_xine_la_SOURCES = xinePlayObject.cc \
+ xinePlayObject_impl.cpp \
+ audio_fifo_out.c
+libarts_xine_la_LDFLAGS = $(all_libraries) -module -no-undefined -pthread
+libarts_xine_la_LIBADD = $(XINE_LIBS) $(LIB_X11) $(LIB_XEXT) \
+ -lkmedia2_idl -lsoundserver_idl -lartsflow
+
+libarts_xine_la_METASOURCES = AUTO
+
+$(srcdir)/xinePlayObject_impl.cpp: xinePlayObject.h
+xinePlayObject.cc xinePlayObject.h: $(srcdir)/xinePlayObject.idl
+ $(MCOPIDL) -I$(kde_includes)/arts $(srcdir)/xinePlayObject.idl
+
+noinst_HEADERS = xinePlayObject_impl.h audio_fifo_out.h
+
+mcopclassdir = $(libdir)/mcop
+mcopclass_DATA = xineAudioPlayObject.mcopclass xineVideoPlayObject.mcopclass
+
+SUBDIRS = tools
diff --git a/xine_artsplugin/audio_fifo_out.c b/xine_artsplugin/audio_fifo_out.c
new file mode 100644
index 00000000..eb5e24bc
--- /dev/null
+++ b/xine_artsplugin/audio_fifo_out.c
@@ -0,0 +1,407 @@
+/*
+ This file is part of KDE/aRts (Noatun) - xine integration
+ Copyright (C) 2002-2003 Ewald Snel <ewald@rambo.its.tudelft.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <time.h>
+#include <sys/time.h>
+#include <xine/audio_out.h>
+
+#include "audio_fifo_out.h"
+
+#define GAP_TOLERANCE 5000
+
+typedef struct fifo_driver_s
+{
+ ao_driver_t ao_driver;
+
+ xine_arts_audio *audio;
+
+ int capabilities;
+ int mode;
+ pthread_mutex_t read_mutex;
+ pthread_mutex_t write_mutex;
+ pthread_cond_t cond;
+
+ uint32_t bytes_per_frame;
+ uint8_t *fifo;
+ int fifo_size;
+ int fifo_read_ptr;
+ int fifo_write_ptr;
+ int fifo_flush;
+ int fifo_delay;
+} fifo_driver_t;
+
+/*
+ * open the audio device for writing to
+ */
+static int ao_fifo_open( ao_driver_t *this_gen, uint32_t bits, uint32_t rate, int mode )
+{
+ fifo_driver_t *ao = (fifo_driver_t *)this_gen;
+
+ if ((mode & ao->capabilities) == 0)
+ {
+ fprintf( stderr, "fifo_audio_out: unsupported mode %08x\n", mode);
+ return 0;
+ }
+
+ /* lock read buffer */
+ pthread_mutex_lock( &ao->read_mutex );
+
+ ao->mode = mode;
+ ao->audio->sample_rate = rate;
+ ao->audio->bits_per_sample = bits;
+
+ switch (mode)
+ {
+ case AO_CAP_MODE_MONO:
+ ao->audio->num_channels = 1;
+ break;
+ case AO_CAP_MODE_STEREO:
+ ao->audio->num_channels = 2;
+ break;
+ }
+
+ ao->bytes_per_frame = (ao->audio->bits_per_sample * ao->audio->num_channels) / 8;
+ ao->fifo_size = ao->audio->sample_rate * ao->bytes_per_frame;
+ ao->fifo = malloc( 2*ao->fifo_size );
+ ao->fifo_read_ptr = 0;
+ ao->fifo_write_ptr = 0;
+ ao->fifo_flush = 0;
+ ao->fifo_delay = 0;
+
+ /* unlock and enable read buffer for aRts sound server */
+ pthread_mutex_unlock( &ao->read_mutex );
+
+ return ao->audio->sample_rate;
+}
+
+static int ao_fifo_num_channels( ao_driver_t *this_gen )
+{
+ return ((fifo_driver_t *)this_gen)->audio->num_channels;
+}
+
+static int ao_fifo_bytes_per_frame( ao_driver_t *this_gen )
+{
+ return ((fifo_driver_t *)this_gen)->bytes_per_frame;
+}
+
+static int ao_fifo_get_gap_tolerance( ao_driver_t *this_gen )
+{
+ return GAP_TOLERANCE;
+}
+
+static int ao_fifo_bytes_in_buffer( fifo_driver_t *ao )
+{
+ int bytes_in_buffer = (ao->fifo_write_ptr - ao->fifo_read_ptr);
+
+ if (bytes_in_buffer < 0)
+ {
+ bytes_in_buffer += ao->fifo_size;
+ }
+ return bytes_in_buffer;
+}
+
+static int ao_fifo_write( ao_driver_t *this_gen, int16_t *data, uint32_t num_frames )
+{
+ fifo_driver_t *ao = (fifo_driver_t *)this_gen;
+ uint8_t *src = (uint8_t *)data;
+ int bytes_in_buffer, bytes_to_write, written;
+
+ bytes_to_write = (num_frames * ao->bytes_per_frame);
+
+ pthread_mutex_lock( &ao->write_mutex );
+
+ while (!ao->fifo_flush && bytes_to_write > 0)
+ {
+ bytes_in_buffer = ao_fifo_bytes_in_buffer( ao );
+ written = bytes_to_write;
+
+ if ((bytes_in_buffer + written) >= ao->fifo_size)
+ {
+ written = (ao->fifo_size - bytes_in_buffer - 1);
+ written -= (written % ao->bytes_per_frame);
+
+ if (written == 0)
+ {
+ struct timespec ts;
+ struct timeval tv;
+ int delay;
+
+ gettimeofday( &tv, 0 );
+
+ delay = ao_fifo_arts_delay();
+ delay += (1000 * num_frames) / ao->audio->sample_rate;
+ delay = (delay < 20) ? 20 : ((delay >= 250) ? 250 : delay + 1);
+
+ ts.tv_sec = tv.tv_sec + (delay / 1000);
+ ts.tv_nsec = (1000 * tv.tv_usec) + (1000000 * (delay % 1000));
+
+ if (ts.tv_nsec >= 1000000000)
+ {
+ ts.tv_sec++;
+ ts.tv_nsec -= 1000000000;
+ }
+ if (pthread_cond_timedwait( &ao->cond, &ao->write_mutex, &ts ) != 0)
+ {
+ fprintf( stderr, "fifo_audio_out: blocked for more than %d ms,\n", delay);
+ fprintf( stderr, "fifo_audio_out: %d sample(s) discarded.\n", num_frames);
+ pthread_mutex_unlock( &ao->write_mutex );
+ return 0;
+ }
+ }
+ }
+
+ if (!ao->fifo_flush && written > 0)
+ {
+ int new_write_ptr = (ao->fifo_write_ptr + written);
+
+ if (new_write_ptr >= ao->fifo_size)
+ {
+ new_write_ptr -= ao->fifo_size;
+
+ memcpy( &ao->fifo[ao->fifo_write_ptr], src, (written - new_write_ptr) );
+ memcpy( ao->fifo, &src[written - new_write_ptr], new_write_ptr );
+ }
+ else
+ {
+ memcpy( &ao->fifo[ao->fifo_write_ptr], src, written );
+ }
+
+ /* update audio buffer pointer */
+ ao->fifo_write_ptr = new_write_ptr;
+ bytes_to_write -= written;
+ src += written;
+ }
+ }
+
+ /* audio has stopped */
+ ao->fifo_delay += bytes_to_write;
+
+ pthread_mutex_unlock( &ao->write_mutex );
+
+ return 1;
+}
+
+static int ao_fifo_delay( ao_driver_t *this_gen )
+{
+ fifo_driver_t *ao = (fifo_driver_t *)this_gen;
+ return (ao_fifo_arts_delay() * ao->audio->sample_rate / 1000)
+ + ((ao_fifo_bytes_in_buffer( ao ) + ao->fifo_delay) / ao->bytes_per_frame);
+}
+
+static void ao_fifo_close( ao_driver_t *this_gen )
+{
+ fifo_driver_t *ao = (fifo_driver_t *)this_gen;
+
+ /* lock read buffer */
+ pthread_mutex_lock( &ao->read_mutex );
+
+ /* disable audio driver */
+ ao->fifo_flush = 2;
+ ao->fifo_delay = 0;
+
+ /* free audio FIFO */
+ if (ao->fifo)
+ {
+ free( ao->fifo );
+ ao->fifo = NULL;
+ }
+ pthread_mutex_unlock( &ao->read_mutex );
+}
+
+static uint32_t ao_fifo_get_capabilities( ao_driver_t *this_gen )
+{
+ return ((fifo_driver_t *)this_gen)->capabilities;
+}
+
+static void ao_fifo_exit( ao_driver_t *this_gen )
+{
+ fifo_driver_t *ao = (fifo_driver_t *)this_gen;
+
+ ao_fifo_close( this_gen );
+
+ pthread_cond_destroy( &ao->cond );
+ pthread_mutex_destroy( &ao->read_mutex );
+ pthread_mutex_destroy( &ao->write_mutex );
+
+ free( ao );
+}
+
+static int ao_fifo_get_property( ao_driver_t *this_gen, int property )
+{
+ return 0;
+}
+
+static int ao_fifo_set_property( ao_driver_t *this_gen, int property, int value )
+{
+ return ~value;
+}
+
+static int ao_fifo_control( ao_driver_t *this_gen, int cmd, ... )
+{
+ fifo_driver_t *ao = (fifo_driver_t *)this_gen;
+
+ switch (cmd)
+ {
+ case AO_CTRL_FLUSH_BUFFERS:
+ case AO_CTRL_PLAY_PAUSE:
+ /* flush audio FIFO */
+ pthread_mutex_lock( &ao->read_mutex );
+
+ ao->fifo_read_ptr = ao->fifo_write_ptr;
+
+ if (ao->fifo_flush == 1)
+ {
+ ao->fifo_flush = 0;
+ ao->fifo_delay = 0;
+ }
+ pthread_mutex_unlock( &ao->read_mutex );
+
+ break;
+
+ case AO_CTRL_PLAY_RESUME:
+ break;
+ }
+ return 0;
+}
+
+xine_audio_port_t *init_audio_out_plugin( xine_t *xine, xine_arts_audio *audio,
+ void **ao_driver )
+{
+ fifo_driver_t *ao = (fifo_driver_t *)malloc( sizeof(fifo_driver_t) );
+
+ ao->audio = audio;
+ ao->fifo = NULL;
+ ao->fifo_read_ptr = 0;
+ ao->fifo_write_ptr = 0;
+ ao->fifo_flush = 2;
+ ao->fifo_delay = 0;
+ ao->capabilities = (AO_CAP_MODE_MONO | AO_CAP_MODE_STEREO);
+
+ ao->ao_driver.get_capabilities = ao_fifo_get_capabilities;
+ ao->ao_driver.get_property = ao_fifo_get_property;
+ ao->ao_driver.set_property = ao_fifo_set_property;
+ ao->ao_driver.open = ao_fifo_open;
+ ao->ao_driver.num_channels = ao_fifo_num_channels;
+ ao->ao_driver.bytes_per_frame = ao_fifo_bytes_per_frame;
+ ao->ao_driver.delay = ao_fifo_delay;
+ ao->ao_driver.write = ao_fifo_write;
+ ao->ao_driver.close = ao_fifo_close;
+ ao->ao_driver.exit = ao_fifo_exit;
+ ao->ao_driver.get_gap_tolerance = ao_fifo_get_gap_tolerance;
+ ao->ao_driver.control = ao_fifo_control;
+
+ pthread_cond_init( &ao->cond, NULL );
+ pthread_mutex_init( &ao->read_mutex, NULL );
+ pthread_mutex_init( &ao->write_mutex, NULL );
+
+ *ao_driver = (void *)ao;
+
+ return _x_ao_new_port( xine, (ao_driver_t *)ao, 0 );
+}
+
+unsigned long ao_fifo_read( void *ao_driver, unsigned char **buffer,
+ unsigned long samples )
+{
+ fifo_driver_t *ao = (fifo_driver_t *)ao_driver;
+ int bytes_in_buffer, bytes_to_read;
+
+ /* lock read buffer */
+ pthread_mutex_lock( &ao->read_mutex );
+
+ bytes_in_buffer = ao_fifo_bytes_in_buffer( ao );
+ bytes_to_read = (samples * ao->bytes_per_frame);
+
+ if (ao->fifo_flush || bytes_in_buffer == 0)
+ {
+ /* unlock read buffer */
+ pthread_mutex_unlock( &ao->read_mutex );
+
+ /* signal blocked writes */
+ pthread_mutex_lock( &ao->write_mutex );
+ pthread_cond_signal( &ao->cond );
+ pthread_mutex_unlock( &ao->write_mutex );
+
+ /* audio FIFO empty or disabled, return */
+ return 0;
+ }
+
+ if (bytes_to_read > bytes_in_buffer)
+ {
+ fprintf( stderr, "fifo_audio_out: audio buffer underflow!\n" );
+ bytes_to_read = bytes_in_buffer - (bytes_in_buffer % ao->bytes_per_frame);
+ }
+ if ((ao->fifo_read_ptr + bytes_to_read) > ao->fifo_size)
+ {
+ /* copy samples from front to end of buffer */
+ memcpy( &ao->fifo[ao->fifo_size], ao->fifo,
+ ((ao->fifo_read_ptr + bytes_to_read) - ao->fifo_size) );
+ }
+
+ /* return pointer to audio samples */
+ *buffer = &ao->fifo[ao->fifo_read_ptr];
+
+ return bytes_to_read;
+}
+
+void ao_fifo_flush( void *ao_driver, unsigned long samples )
+{
+ fifo_driver_t *ao = (fifo_driver_t *)ao_driver;
+ int bytes_in_buffer, bytes_to_flush;
+
+ /* flush audio data */
+ bytes_in_buffer = ao_fifo_bytes_in_buffer( ao );
+ bytes_to_flush = (samples * ao->bytes_per_frame);
+
+ if (bytes_to_flush <= bytes_in_buffer)
+ {
+ int new_read_ptr = (ao->fifo_read_ptr + bytes_to_flush);
+
+ if (new_read_ptr >= ao->fifo_size)
+ {
+ new_read_ptr -= ao->fifo_size;
+ }
+ ao->fifo_read_ptr = new_read_ptr;
+ }
+
+ /* unlock read buffer */
+ pthread_mutex_unlock( &ao->read_mutex );
+
+ /* signal blocked writes */
+ pthread_mutex_lock( &ao->write_mutex );
+ pthread_cond_signal( &ao->cond );
+ pthread_mutex_unlock( &ao->write_mutex );
+}
+
+void ao_fifo_clear( void *ao_driver, int clear )
+{
+ fifo_driver_t *ao = (fifo_driver_t *)ao_driver;
+
+ pthread_mutex_lock( &ao->write_mutex );
+
+ /* enable/disable audio driver */
+ ao->fifo_flush = clear;
+ ao->fifo_delay = 0;
+
+ if (clear)
+ {
+ /* signal blocked writes */
+ pthread_cond_signal( &ao->cond );
+ }
+ pthread_mutex_unlock( &ao->write_mutex );
+}
diff --git a/xine_artsplugin/audio_fifo_out.h b/xine_artsplugin/audio_fifo_out.h
new file mode 100644
index 00000000..b6629001
--- /dev/null
+++ b/xine_artsplugin/audio_fifo_out.h
@@ -0,0 +1,44 @@
+/*
+ This file is part of KDE/aRts (Noatun) - xine integration
+ Copyright (C) 2002-2003 Ewald Snel <ewald@rambo.its.tudelft.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+*/
+
+#ifndef __AUDIO_FIFO_OUT_H
+#define __AUDIO_FIFO_OUT_H
+
+#include <xine.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct
+{
+ int32_t sample_rate;
+ uint32_t num_channels;
+ uint32_t bits_per_sample;
+} xine_arts_audio;
+
+
+xine_audio_port_t *init_audio_out_plugin( xine_t *xine, xine_arts_audio *audio,
+ void **ao_driver );
+
+int ao_fifo_arts_delay();
+
+unsigned long ao_fifo_read( void *ao_driver, unsigned char **buffer,
+ unsigned long samples );
+
+void ao_fifo_flush( void *ao_driver, unsigned long samples );
+
+void ao_fifo_clear( void *ao_driver, int clear );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/xine_artsplugin/configure.in.in b/xine_artsplugin/configure.in.in
new file mode 100644
index 00000000..a79269b1
--- /dev/null
+++ b/xine_artsplugin/configure.in.in
@@ -0,0 +1,257 @@
+dnl Configure paths for XINE
+dnl
+dnl Copyright (C) 2001 Daniel Caujolle-Bert <segfault@club-internet.fr>
+dnl
+dnl This program is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; either version 2 of the License, or
+dnl (at your option) any later version.
+dnl
+dnl This program is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+dnl GNU General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with this program; if not, write to the Free Software
+dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+dnl
+dnl
+dnl As a special exception to the GNU General Public License, if you
+dnl distribute this file as part of a program that contains a configuration
+dnl script generated by Autoconf, you may include it under the same
+dnl distribution terms that you use for the rest of that program.
+dnl
+if test "x$build_arts" = "xno"; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE xine_artsplugin"
+fi
+
+
+dnl AC_PATH_XINE([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]])
+dnl Test for XINE, and define XINE_CFLAGS and XINE_LIBS
+dnl
+AC_DEFUN([AC_PATH_XINE],
+[dnl
+dnl Get the cflags and libraries from the xine-config script
+dnl
+AC_ARG_WITH(xine-prefix,
+ [ --with-xine-prefix=PFX Prefix where XINE is installed (optional)],
+ xine_config_prefix="$withval", xine_config_prefix="")
+AC_ARG_WITH(xine-exec-prefix,
+ [ --with-xine-exec-prefix=PFX Exec prefix where XINE is installed (optional)],
+ xine_config_exec_prefix="$withval", xine_config_exec_prefix="")
+AC_ARG_ENABLE(xinetest,
+ [ --disable-xinetest Do not try to compile and run a test XINE program],, enable_xinetest=yes)
+
+ if test x$xine_config_exec_prefix != x ; then
+ xine_config_args="$xine_config_args --exec-prefix=$xine_config_exec_prefix"
+ if test x${XINE_CONFIG+set} != xset ; then
+ XINE_CONFIG=$xine_config_exec_prefix/bin/xine-config
+ fi
+ fi
+ if test x$xine_config_prefix != x ; then
+ xine_config_args="$xine_config_args --prefix=$xine_config_prefix"
+ if test x${XINE_CONFIG+set} != xset ; then
+ XINE_CONFIG=$xine_config_prefix/bin/xine-config
+ fi
+ fi
+
+ min_xine_version=ifelse([$1], ,0.5.0,$1)
+ if test "x$enable_xinetest" != "xyes" ; then
+ AC_MSG_CHECKING([for XINE-LIB version >= $min_xine_version])
+ else
+ AC_PATH_PROG(XINE_CONFIG, xine-config, no)
+ AC_MSG_CHECKING([for XINE-LIB version >= $min_xine_version])
+ no_xine=""
+ if test "$XINE_CONFIG" = "no" ; then
+ no_xine=yes
+ else
+ XINE_CFLAGS=`$XINE_CONFIG $xine_config_args --cflags`
+ XINE_LIBS=`$XINE_CONFIG $xine_config_args --libs`
+ xine_config_major_version=`$XINE_CONFIG $xine_config_args --version | \
+ sed -n 's/^\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*$/\1/p'`
+ xine_config_minor_version=`$XINE_CONFIG $xine_config_args --version | \
+ sed -n 's/^\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*$/\2/p'`
+ xine_config_sub_version=`$XINE_CONFIG $xine_config_args --version | \
+ sed -n 's/^\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*$/\3/p'`
+ xine_script_dir=`$XINE_CONFIG $xine_config_args --scriptdir`
+ xine_plugin_dir=`$XINE_CONFIG $xine_config_args --plugindir`
+ xine_locale_dir=`$XINE_CONFIG $xine_config_args --localedir`
+ dnl if test "x$enable_xinetest" = "xyes" ; then
+ ac_save_CFLAGS="$CFLAGS"
+ ac_save_LIBS="$LIBS"
+ CFLAGS="$CFLAGS $XINE_CFLAGS $all_includes"
+ LIBS="$XINE_LIBS $LIBS $all_libraries"
+dnl
+dnl Now check if the installed XINE is sufficiently new. (Also sanity
+dnl checks the results of xine-config to some extent
+dnl
+ AC_LANG_SAVE()
+ AC_LANG_C()
+ rm -f conf.xinetest
+ AC_TRY_RUN([
+#include <xine.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int
+main ()
+{
+ int major, minor, sub;
+ char *tmp_version;
+
+ system ("touch conf.xinetest");
+
+ /* HP/UX 9 (%@#!) writes to sscanf strings */
+ tmp_version = (char *) strdup("$min_xine_version");
+ if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &sub) != 3) {
+ printf("%s, bad version string\n", "$min_xine_version");
+ exit(1);
+ }
+
+ if ((XINE_MAJOR_VERSION != $xine_config_major_version) ||
+ (XINE_MINOR_VERSION != $xine_config_minor_version) ||
+ (XINE_SUB_VERSION != $xine_config_sub_version))
+ {
+ printf("\n*** 'xine-config --version' returned %d.%d.%d, but XINE (%d.%d.%d)\n",
+ $xine_config_major_version, $xine_config_minor_version, $xine_config_sub_version,
+ XINE_MAJOR_VERSION, XINE_MINOR_VERSION, XINE_SUB_VERSION);
+ printf ("*** was found! If xine-config was correct, then it is best\n");
+ printf ("*** to remove the old version of XINE. You may also be able to fix the error\n");
+ printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
+ printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
+ printf("*** required on your system.\n");
+ printf("*** If xine-config was wrong, set the environment variable XINE_CONFIG\n");
+ printf("*** to point to the correct copy of xine-config, and remove the file config.cache\n");
+ printf("*** before re-running configure\n");
+ }
+ else
+ {
+ if ((XINE_MAJOR_VERSION > major) ||
+ ((XINE_MAJOR_VERSION == major) && (XINE_MINOR_VERSION > minor)) ||
+ ((XINE_MAJOR_VERSION == major) && (XINE_MINOR_VERSION == minor) && (XINE_SUB_VERSION >= sub)))
+ {
+ return 0;
+ }
+ else
+ {
+ printf("\n*** An old version of XINE (%d.%d.%d) was found.\n",
+ XINE_MAJOR_VERSION, XINE_MINOR_VERSION, XINE_SUB_VERSION);
+ printf("*** You need a version of XINE newer than %d.%d.%d. The latest version of\n",
+ major, minor, sub);
+ printf("*** XINE is always available from:\n");
+ printf("*** http://xine.sourceforge.net\n");
+ printf("***\n");
+ printf("*** If you have already installed a sufficiently new version, this error\n");
+ printf("*** probably means that the wrong copy of the xine-config shell script is\n");
+ printf("*** being found. The easiest way to fix this is to remove the old version\n");
+ printf("*** of XINE, but you can also set the XINE_CONFIG environment to point to the\n");
+ printf("*** correct copy of xine-config. (In this case, you will have to\n");
+ printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
+ printf("*** so that the correct libraries are found at run-time))\n");
+ }
+ }
+ return 1;
+}
+],, no_xine=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+ CFLAGS="$ac_save_CFLAGS"
+ LIBS="$ac_save_LIBS"
+ fi
+ fi
+ if test "x$no_xine" = x ; then
+ AC_MSG_RESULT(yes)
+ ifelse([$2], , :, [$2])
+ else
+ AC_MSG_RESULT(no)
+ if test "$XINE_CONFIG" = "no" ; then
+ echo "*** The xine-config script installed by XINE could not be found"
+ echo "*** If XINE was installed in PREFIX, make sure PREFIX/bin is in"
+ echo "*** your path, or set the XINE_CONFIG environment variable to the"
+ echo "*** full path to xine-config."
+ else
+ if test -f conf.xinetest ; then
+ :
+ else
+ echo "*** Could not run XINE test program, checking why..."
+ CFLAGS="$CFLAGS $XINE_CFLAGS"
+ LIBS="$LIBS $XINE_LIBS"
+ AC_TRY_LINK([
+#include <xine.h>
+#include <stdio.h>
+], [ return ((XINE_MAJOR_VERSION) || (XINE_MINOR_VERSION) || (XINE_SUB_VERSION)); ],
+ [ echo "*** The test program compiled, but did not run. This usually means"
+ echo "*** that the run-time linker is not finding XINE or finding the wrong"
+ echo "*** version of XINE. If it is not finding XINE, you'll need to set your"
+ echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+ echo "*** to the installed location Also, make sure you have run ldconfig if that"
+ echo "*** is required on your system"
+ echo "***"
+ echo "*** If you have an old version installed, it is best to remove it, although"
+ echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"
+ echo "***"],
+ [ echo "*** The test program failed to compile or link. See the file config.log for the"
+ echo "*** exact error that occured. This usually means XINE was incorrectly installed"
+ echo "*** or that you have moved XINE since it was installed. In the latter case, you"
+ echo "*** may want to edit the xine-config script: $XINE_CONFIG" ])
+ CFLAGS="$ac_save_CFLAGS"
+ LIBS="$ac_save_LIBS"
+ fi
+ fi
+ XINE_CFLAGS=""
+ XINE_LIBS=""
+ ifelse([$3], , :, [$3])
+ fi
+ AC_SUBST(XINE_CFLAGS)
+ AC_SUBST(XINE_LIBS)
+ AC_LANG_RESTORE()
+ rm -f conf.xinetest
+
+dnl Make sure HAVE_STRSEP, HAVE_SETENV and HAVE_STRPBRK are defined as
+dnl necessary.
+ AC_CHECK_FUNCS([strsep strpbrk setenv])
+])
+
+dnl Check for XShmGetEventBase
+AC_MSG_CHECKING([for XShmGetEventBase])
+AC_LANG_SAVE()
+AC_LANG_CPLUSPLUS()
+AC_TRY_COMPILE([
+#include <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+ ],[
+static Display *display = NULL;
+int shmCompletionType = XShmGetEventBase( display );
+ ],[
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_XSHMGETEVENTBASE, 1, [Define if you have XShmGetEventBase in <X11/extensions/XShm.h])
+ ],[
+ AC_MSG_RESULT(no)
+ ]
+)
+AC_LANG_RESTORE()
+
+dnl Check for new internal xine symbol names
+KDE_CHECK_LIB(xine, _x_ao_new_port, :,
+[
+ AC_DEFINE(_x_ao_new_port, ao_new_port, [Compatibility with older version of xine])
+])
+AC_CHECK_FUNC([ao_new_port])
+
+AC_ARG_WITH([xine],
+ [AC_HELP_STRING([--with-xine],
+ [Enable support for Xine @<:@default=check@:>@])],
+ [], with_xine=check)
+
+have_xine=no
+if test "x$with_xine" != xno; then
+ AC_PATH_XINE(1.0.0, have_xine=yes)
+
+ if test "x$with_xine" != xcheck && test "x$have_xine" != xyes; then
+ AC_MSG_ERROR([--with-xine was given, but test for Xine failed])
+ fi
+fi
+
+if test "x$have_xine" != xyes; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE xine_artsplugin"
+fi
diff --git a/xine_artsplugin/tools/Makefile.am b/xine_artsplugin/tools/Makefile.am
new file mode 100644
index 00000000..1ed9efda
--- /dev/null
+++ b/xine_artsplugin/tools/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS=thumbnail
diff --git a/xine_artsplugin/tools/thumbnail/Makefile.am b/xine_artsplugin/tools/thumbnail/Makefile.am
new file mode 100644
index 00000000..40358fda
--- /dev/null
+++ b/xine_artsplugin/tools/thumbnail/Makefile.am
@@ -0,0 +1,24 @@
+## $Id$
+## Makefile.am of kdemultimedia/tools/thumbnail
+
+INCLUDES = -I$(kde_includes)/arts $(all_includes) $(XINE_CFLAGS)
+
+AM_CFLAGS = -U__STRICT_ANSI__
+
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = videothumbnail.la
+
+videothumbnail_la_SOURCES = videocreator.cpp videoscaler.cpp
+videothumbnail_la_LIBADD = $(XINE_LIBS) $(LIB_KDECORE)
+videothumbnail_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) -pthread
+
+noinst_HEADERS = videocreator.h
+
+xineartsplugin_tools_videothumbnail_DATA = sprocket-small.png sprocket-medium.png sprocket-large.png
+
+xineartsplugin_tools_videothumbnaildir = $(kde_datadir)/videothumbnail
+
+services_DATA = videothumbnail.desktop
+
+servicesdir = $(kde_servicesdir)
diff --git a/xine_artsplugin/tools/thumbnail/sprocket-large.png b/xine_artsplugin/tools/thumbnail/sprocket-large.png
new file mode 100644
index 00000000..6c0e65a9
--- /dev/null
+++ b/xine_artsplugin/tools/thumbnail/sprocket-large.png
Binary files differ
diff --git a/xine_artsplugin/tools/thumbnail/sprocket-medium.png b/xine_artsplugin/tools/thumbnail/sprocket-medium.png
new file mode 100644
index 00000000..8868e660
--- /dev/null
+++ b/xine_artsplugin/tools/thumbnail/sprocket-medium.png
Binary files differ
diff --git a/xine_artsplugin/tools/thumbnail/sprocket-small.png b/xine_artsplugin/tools/thumbnail/sprocket-small.png
new file mode 100644
index 00000000..62fa3fd7
--- /dev/null
+++ b/xine_artsplugin/tools/thumbnail/sprocket-small.png
Binary files differ
diff --git a/xine_artsplugin/tools/thumbnail/videocreator.cpp b/xine_artsplugin/tools/thumbnail/videocreator.cpp
new file mode 100644
index 00000000..94e87b48
--- /dev/null
+++ b/xine_artsplugin/tools/thumbnail/videocreator.cpp
@@ -0,0 +1,376 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002 Simon MacMullen
+ Copyright (C) 2003 Ewald Snel <ewald@rambo.its.tudelft.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+// $Id$
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <pthread.h>
+#include <sys/time.h>
+
+#include <qpixmap.h>
+#include <qdialog.h>
+#include <qfile.h>
+#include <qimage.h>
+#include <qpainter.h>
+#include <qpaintdevice.h>
+
+#include <iostream>
+
+#include <kstandarddirs.h>
+#include <kapplication.h>
+
+#define XINE_ENABLE_EXPERIMENTAL_FEATURES 1
+
+#include <xine.h>
+
+#include "videocreator.h"
+#include "videoscaler.h"
+
+#define TIMEOUT 15 // 15 seconds
+#define MAX_ATTEMPTS 25
+
+
+// Global xine pointer
+static xine_t *xine_shared = NULL;
+static pthread_mutex_t xine_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t xine_cond = PTHREAD_COND_INITIALIZER;
+static int xineRefCount = 0;
+
+static void xine_init_routine()
+{
+ char cfgFileName[272];
+
+ xine_shared = (xine_t *)xine_new();
+
+ snprintf( cfgFileName, 272, "%s/.xine/config", getenv( "HOME" ) );
+
+ xine_config_load( xine_shared, (const char *)cfgFileName );
+
+ xine_init( xine_shared );
+}
+
+static void *xine_timeout_routine( void * )
+{
+ pthread_mutex_lock( &xine_mutex );
+
+ while (xine_shared != 0)
+ {
+ if (xineRefCount == 0)
+ {
+ struct timespec ts;
+ struct timeval tv;
+
+ gettimeofday( &tv, 0 );
+
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+ ts.tv_sec += TIMEOUT;
+
+ if (pthread_cond_timedwait( &xine_cond, &xine_mutex, &ts ) != 0 &&
+ xineRefCount == 0)
+ {
+ xine_exit( xine_shared );
+ xine_shared = NULL;
+ break;
+ }
+ }
+ else
+ {
+ pthread_cond_wait( &xine_cond, &xine_mutex );
+ }
+ }
+ pthread_mutex_unlock( &xine_mutex );
+
+ return NULL;
+}
+
+static xine_t *xine_shared_init()
+{
+ pthread_mutex_lock( &xine_mutex );
+
+ ++xineRefCount;
+
+ if (xine_shared == 0)
+ {
+ pthread_t thread;
+
+ xine_init_routine();
+
+ if (pthread_create( &thread, NULL, xine_timeout_routine, NULL ) == 0)
+ {
+ pthread_detach( thread );
+ }
+ }
+ else
+ {
+ pthread_cond_signal( &xine_cond );
+ }
+ pthread_mutex_unlock( &xine_mutex );
+
+ return xine_shared;
+}
+
+static void xine_shared_exit( xine_t * )
+{
+ pthread_mutex_lock( &xine_mutex );
+
+ if (--xineRefCount == 0)
+ {
+ pthread_cond_signal( &xine_cond );
+ }
+ pthread_mutex_unlock( &xine_mutex );
+}
+
+static QImage createThumbnail( xine_video_frame_t *frame, int width, int height )
+{
+ unsigned char *base[3];
+ unsigned int pitches[3];
+
+ if ((frame->aspect_ratio * height) > width)
+ height = (int)(.5 + (width / frame->aspect_ratio));
+ else
+ width = (int)(.5 + (height * frame->aspect_ratio));
+
+ QImage image( width, height, 32 );
+
+ if (frame->colorspace == XINE_IMGFMT_YV12)
+ {
+ int y_size, uv_size;
+
+ pitches[0] = (frame->width + 7) & ~0x7;
+ pitches[1] = (((frame->width + 1) / 2) + 7) & ~0x7;
+ pitches[2] = pitches[1];
+
+ y_size = pitches[0] * frame->height;
+ uv_size = pitches[1] * ((frame->height + 1) / 2);
+
+ base[0] = frame->data;
+ base[1] = base[0] + y_size + uv_size;
+ base[2] = base[0] + y_size;
+
+ scaleYuvToRgb32( frame->width, frame->height, base, pitches,
+ width, height, (unsigned int *)image.bits(),
+ image.bytesPerLine() );
+ }
+ else if (frame->colorspace == XINE_IMGFMT_YUY2)
+ {
+ pitches[0] = 2*((frame->width + 3) & ~0x3);
+ base[0] = frame->data;
+
+ scaleYuy2ToRgb32( frame->width, frame->height, base[0], pitches[0],
+ width, height, (unsigned int *)image.bits(),
+ image.bytesPerLine() );
+ }
+ return image;
+}
+
+// Return the variance of the brightness of the pixels
+static double imageVariance( unsigned char *pixels, int pitch,
+ int width, int height, int step )
+{
+ double sigmaX = 0;
+ double sigmaXSquared = 0;
+
+ for (int y=0; y < height ; y++)
+ {
+ unsigned int uSigmaX = 0;
+ unsigned int uSigmaXSquared = 0;
+
+ for (int x=0, n=(width * step); x < n ; x+=step)
+ {
+ int gray = pixels[x];
+
+ uSigmaX += gray;
+ uSigmaXSquared += gray * gray;
+ }
+
+ sigmaX += uSigmaX;
+ sigmaXSquared += uSigmaXSquared;
+
+ pixels += pitch;
+ }
+
+ unsigned int total = height * width;
+
+ return sqrt( sigmaXSquared / total - (sigmaX / total) * (sigmaX / total) );
+}
+
+static bool findBestFrame( xine_video_port_t *vo_port, xine_video_frame_t *frame )
+{
+ xine_video_frame_t frames[2], *bestFrame = NULL;
+ double variance, bestVariance = 0;
+
+ for (int i=0, n=0; i < MAX_ATTEMPTS; i++)
+ {
+ xine_video_frame_t *cFrame = &frames[n];
+
+ // Try to read next frame
+ if (!xine_get_next_video_frame( vo_port, cFrame ))
+ {
+ break;
+ }
+
+ variance = imageVariance( cFrame->data, ((cFrame->width + 7) & ~0x7),
+ cFrame->width, cFrame->height,
+ (cFrame->colorspace == XINE_IMGFMT_YV12) ? 1 : 2 );
+
+ // Compare current frame to best frame
+ if (bestFrame == NULL || variance > bestVariance)
+ {
+ if (bestFrame != NULL)
+ {
+ xine_free_video_frame( vo_port, bestFrame );
+ }
+
+ bestFrame = cFrame;
+ bestVariance = variance;
+
+ n = (1 - n);
+ }
+ else
+ {
+ xine_free_video_frame( vo_port, cFrame );
+ }
+
+ // Stop searching if current frame is interesting enough
+ if (variance > 40.0)
+ {
+ break;
+ }
+ }
+
+ // This should be the best frame to create a thumbnail from
+ if (bestFrame != NULL)
+ {
+ *frame = *bestFrame;
+ }
+ return (bestFrame != NULL);
+}
+
+
+extern "C"
+{
+ ThumbCreator *new_creator()
+ {
+ return new VideoCreator;
+ }
+}
+
+VideoCreator::VideoCreator()
+{
+}
+
+VideoCreator::~VideoCreator()
+{
+}
+
+bool VideoCreator::create(const QString &path, int width, int height, QImage &img)
+{
+ if (m_sprocketSmall.isNull())
+ {
+ QString pixmap = locate( "data", "videothumbnail/sprocket-small.png" );
+ m_sprocketSmall = QPixmap(pixmap);
+ pixmap = locate( "data", "videothumbnail/sprocket-medium.png" );
+ m_sprocketMedium = QPixmap(pixmap);
+ pixmap = locate( "data", "videothumbnail/sprocket-large.png" );
+ m_sprocketLarge = QPixmap(pixmap);
+ }
+
+ // The long term plan is to seek to frame 1, create thumbnail, see if is is
+ // interesting enough, if not seek to frame 2, then 4, then 8, etc.
+ // "Interesting enough" means the variance of the pixel brightness is high. This
+ // is because many videos fade up from black and a black rectangle is boring.
+ //
+ // But for the time being we can't seek so we just let it play for one second
+ // then take whatever we find.
+
+ xine_t *xine = xine_shared_init();
+ xine_audio_port_t *ao_port = xine_new_framegrab_audio_port( xine );
+ xine_video_port_t *vo_port = xine_new_framegrab_video_port( xine );
+ xine_stream_t *stream = xine_stream_new( xine, ao_port, vo_port );
+ bool success = false;
+
+ if (xine_open( stream, QFile::encodeName ( path ).data() ))
+ {
+ xine_video_frame_t frame;
+ int length;
+
+ // Find 'best' (or at least any) frame
+ if (!xine_get_pos_length( stream, NULL, NULL, &length ) || length > 5000)
+ {
+ if (xine_play( stream, 0, 4000 ))
+ {
+ success = findBestFrame( vo_port, &frame );
+ }
+ }
+ if (!success)
+ {
+ // Some codecs can't seek to start, but close/open works
+ xine_close( stream );
+ xine_open( stream, path.ascii() );
+
+ if (xine_play( stream, 0, 0 ))
+ {
+ success = findBestFrame( vo_port, &frame );
+ }
+ }
+
+ // Create thumbnail image
+ if (success)
+ {
+ QPixmap pix( createThumbnail( &frame, width, height ) );
+ QPainter painter( &pix );
+ QPixmap sprocket;
+
+ if (pix.height() < 60)
+ sprocket = m_sprocketSmall;
+ else if (pix.height() < 90)
+ sprocket = m_sprocketMedium;
+ else
+ sprocket = m_sprocketLarge;
+
+ for (int y = 0; y < pix.height() + sprocket.height(); y += sprocket.height()) {
+ painter.drawPixmap( 0, y, sprocket );
+ }
+
+ img = pix.convertToImage();
+
+ xine_free_video_frame( vo_port, &frame );
+ }
+
+ xine_stop( stream );
+ }
+
+ xine_dispose( stream );
+ xine_close_audio_driver( xine, ao_port );
+ xine_close_video_driver( xine, vo_port );
+ xine_shared_exit( xine );
+
+ return (success);
+}
+
+ThumbCreator::Flags VideoCreator::flags() const
+{
+ return (ThumbCreator::Flags) (DrawFrame);
+}
+
+#include "videocreator.moc"
diff --git a/xine_artsplugin/tools/thumbnail/videocreator.h b/xine_artsplugin/tools/thumbnail/videocreator.h
new file mode 100644
index 00000000..bd16e6e2
--- /dev/null
+++ b/xine_artsplugin/tools/thumbnail/videocreator.h
@@ -0,0 +1,40 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002 Simon MacMullen
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _VIDEOCREATOR_H_
+#define _VIDEOCREATOR_H_ "$Id$"
+
+#include <kio/thumbcreator.h>
+
+class VideoCreator : public QObject, public ThumbCreator
+{
+ Q_OBJECT
+public:
+ VideoCreator();
+ virtual ~VideoCreator();
+ virtual bool create(const QString &path, int width, int height, QImage &img);
+ virtual Flags flags() const;
+
+private:
+ QPixmap m_sprocketSmall;
+ QPixmap m_sprocketMedium;
+ QPixmap m_sprocketLarge;
+};
+
+#endif
diff --git a/xine_artsplugin/tools/thumbnail/videoscaler.cpp b/xine_artsplugin/tools/thumbnail/videoscaler.cpp
new file mode 100644
index 00000000..2b4b49ad
--- /dev/null
+++ b/xine_artsplugin/tools/thumbnail/videoscaler.cpp
@@ -0,0 +1,258 @@
+/*
+ This file is part of KDE/aRts - xine integration
+ Copyright (C) 2003 Ewald Snel <ewald@rambo.its.tudelft.nl>
+
+ 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.
+
+ inspired by code from the xine project
+
+ Copyright (C) 2003 the xine project
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+#include <config.h>
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
+#include "videoscaler.h"
+
+#define THUMBNAIL_BRIGHTNESS +32
+#define THUMBNAIL_CONTRAST 128
+#define THUMBNAIL_SATURATION 128
+
+
+// Colorspace conversion tables
+static int tableLY[256];
+static int tableRV[256], tableBU[256], tableGU[256], tableGV[256];
+static int clipR[2240], clipG[2240], clipB[2240];
+
+static pthread_once_t once_control = PTHREAD_ONCE_INIT;
+
+
+static void init_once_routine()
+{
+ int cly = ( 76309 * THUMBNAIL_CONTRAST + 64) / 128;
+ int crv = (104597 * THUMBNAIL_SATURATION + 64) / 128;
+ int cbu = (132201 * THUMBNAIL_SATURATION + 64) / 128;
+ int cgu = ( 25675 * THUMBNAIL_SATURATION + 64) / 128;
+ int cgv = ( 53279 * THUMBNAIL_SATURATION + 64) / 128;
+ int i;
+
+ for (i=0; i < 256; i++)
+ {
+ tableLY[i] = cly * (i + THUMBNAIL_BRIGHTNESS - 16) + (864 << 16) + 32768;
+ tableRV[i] = crv * (i - 128);
+ tableBU[i] = cbu * (i - 128);
+ tableGU[i] = cgu * (i - 128);
+ tableGV[i] = cgv * (i - 128);
+ }
+ for (i=0; i < 2240; i++)
+ {
+ int c = (i < 864) ? 0 : ((i > 1119) ? 255 : (i - 864));
+
+ clipR[i] = c << 16;
+ clipG[i] = c << 8;
+ clipB[i] = c;
+ }
+}
+
+static void yuvToRgb32( unsigned char *bufy, unsigned char *bufu, unsigned char *bufv,
+ unsigned int *pixels, int width )
+{
+ for (int i=0; i < width; i++)
+ {
+ int l = tableLY[bufy[i]];
+
+ pixels[i] = clipR[(l + tableRV[bufv[i]]) >> 16] |
+ clipG[(l - tableGU[bufu[i]] - tableGV[bufv[i]]) >> 16] |
+ clipB[(l + tableBU[bufu[i]]) >> 16];
+ }
+}
+
+static inline void scaleLine( unsigned char *src[2], int width,
+ unsigned char *dst, int scaledWidth,
+ int scale, int weight, int step, int offset )
+{
+ int a, b, c, d;
+ int x = (scale / 2) - 32768;
+ unsigned char *p0 = (src[0] + offset);
+ unsigned char *p1 = (src[1] + offset);
+
+ weight >>= 8;
+
+ if (scaledWidth > width)
+ {
+ /* Trailing pixels, no horizontal filtering */
+ c = scaledWidth - (((width << 16) - 32768 - (scale / 2)) / scale);
+ a = p0[(step * width) - step];
+ b = p1[(step * width) - step];
+ a += (128 + (b - a) * weight) >> 8;
+ scaledWidth -= c;
+ memset( &dst[scaledWidth], a, c );
+
+ /* Leading pixels, no horizontal filtering */
+ c = (32767 + (scale / 2)) / scale;
+ a = p0[0];
+ b = p1[0];
+ a += (128 + (b - a) * weight) >> 8;
+ scaledWidth -= c;
+ memset( dst, a, c );
+
+ /* Adjust source and destination */
+ dst += c;
+ x += (c * scale);
+ }
+
+ for (int i=0; i < scaledWidth; i++)
+ {
+ int xhi = (step == 1) ? (x >> 16)
+ : ((step == 2) ? (x >> 15) & ~0x1
+ : (x >> 14) & ~0x3);
+ int xlo = (x & 0xFFFF) >> 8;
+
+ /* Four nearest points for bilinear filtering */
+ a = p0[xhi];
+ b = p0[xhi + step];
+ c = p1[xhi];\
+ d = p1[xhi + step];
+
+ /* Interpolate horizontally */
+ a = (256 * a) + (b - a) * xlo;
+ c = (256 * c) + (d - c) * xlo;
+
+ /* Interpolate vertically and store bilinear filtered sample */
+ *(dst++) = ((256 * a) + (c - a) * weight + 32768) >> 16;
+
+ x += scale;
+ }
+}
+
+void scaleYuvToRgb32( int width, int height,
+ unsigned char *base[3], unsigned int pitches[3],
+ int scaledWidth, int scaledHeight,
+ unsigned int *pixels, unsigned int bytesPerLine )
+{
+ int chromaWidth = (width + 1) / 2;
+ int chromaHeight = (height + 1) / 2;
+ int scaleX = (width << 16) / scaledWidth;
+ int scaleY = (height << 16) / scaledHeight;
+ int scaleCX = (scaleX / 2);
+ int y = (scaleY / 2) - 32768;
+
+ // Temporary line buffers (stack)
+ unsigned char *bufy = (unsigned char *)alloca( scaledWidth );
+ unsigned char *bufu = (unsigned char *)alloca( scaledWidth );
+ unsigned char *bufv = (unsigned char *)alloca( scaledWidth );
+
+ pthread_once( &once_control, init_once_routine );
+
+ for (int i=0; i < scaledHeight; i++)
+ {
+ unsigned char *twoy[2], *twou[2], *twov[2];
+ int y2 = (y / 2) - 32768;
+
+ // Calculate luminance scanlines for bilinear filtered scaling
+ if (y < 0)
+ {
+ twoy[0] = twoy[1] = base[0];
+ }
+ else if (y >= ((height - 1) << 16))
+ {
+ twoy[0] = twoy[1] = base[0] + (height - 1) * pitches[0];
+ }
+ else
+ {
+ twoy[0] = base[0] + (y >> 16) * pitches[0];
+ twoy[1] = twoy[0] + pitches[0];
+ }
+
+ // Calculate chrominance scanlines for bilinear filtered scaling
+ if (y2 < 0)
+ {
+ twou[0] = twou[1] = base[1];
+ twov[0] = twov[1] = base[2];
+ }
+ else if (y2 >= ((chromaHeight - 1) << 16))
+ {
+ twou[0] = twou[1] = base[1] + (chromaHeight - 1) * pitches[1];
+ twov[0] = twov[1] = base[2] + (chromaHeight - 1) * pitches[2];
+ }
+ else
+ {
+ twou[0] = base[1] + (y2 >> 16) * pitches[1];
+ twov[0] = base[2] + (y2 >> 16) * pitches[2];
+ twou[1] = twou[0] + pitches[1];
+ twov[1] = twov[0] + pitches[2];
+ }
+
+ // Bilinear filtered scaling
+ scaleLine( twoy, width, bufy, scaledWidth, scaleX, (y & 0xFFFF), 1, 0 );
+ scaleLine( twou, chromaWidth, bufu, scaledWidth, scaleCX, (y2 & 0xFFFF), 1, 0 );
+ scaleLine( twov, chromaWidth, bufv, scaledWidth, scaleCX, (y2 & 0xFFFF), 1, 0 );
+
+ // YUV to RGB32 comnversion
+ yuvToRgb32( bufy, bufu, bufv, pixels, scaledWidth );
+
+ pixels = (unsigned int *)(((char *)pixels) + bytesPerLine);
+ y += scaleY;
+ }
+}
+
+void scaleYuy2ToRgb32( int width, int height,
+ unsigned char *base, unsigned int pitch,
+ int scaledWidth, int scaledHeight,
+ unsigned int *pixels, unsigned int bytesPerLine )
+{
+ int chromaWidth = (width + 1) / 2;
+ int scaleX = (width << 16) / scaledWidth;
+ int scaleY = (height << 16) / scaledHeight;
+ int scaleCX = (scaleX / 2);
+ int y = (scaleY / 2) - 32768;
+
+ // Temporary line buffers (stack)
+ unsigned char *bufy = (unsigned char *)alloca( scaledWidth );
+ unsigned char *bufu = (unsigned char *)alloca( scaledWidth );
+ unsigned char *bufv = (unsigned char *)alloca( scaledWidth );
+
+ pthread_once( &once_control, init_once_routine );
+
+ for (int i=0; i < scaledHeight; i++)
+ {
+ unsigned char *two[2];
+
+ // Calculate scanlines for bilinear filtered scaling
+ if (y < 0)
+ {
+ two[0] = two[1] = base;
+ }
+ else if (y >= ((height - 1) << 16))
+ {
+ two[0] = two[1] = base + (height - 1) * pitch;
+ }
+ else
+ {
+ two[0] = base + (y >> 16) * pitch;
+ two[1] = two[0] + pitch;
+ }
+
+ // Bilinear filtered scaling
+ scaleLine( two, width, bufy, scaledWidth, scaleX, (y & 0xFFFF), 2, 0 );
+ scaleLine( two, chromaWidth, bufu, scaledWidth, scaleCX, (y & 0xFFFF), 4, 1 );
+ scaleLine( two, chromaWidth, bufv, scaledWidth, scaleCX, (y & 0xFFFF), 4, 3 );
+
+ // YUV to RGB32 comnversion
+ yuvToRgb32( bufy, bufu, bufv, pixels, scaledWidth );
+
+ pixels = (unsigned int *)(((char *)pixels) + bytesPerLine);
+ y += scaleY;
+ }
+}
diff --git a/xine_artsplugin/tools/thumbnail/videoscaler.h b/xine_artsplugin/tools/thumbnail/videoscaler.h
new file mode 100644
index 00000000..fd4d51db
--- /dev/null
+++ b/xine_artsplugin/tools/thumbnail/videoscaler.h
@@ -0,0 +1,24 @@
+/*
+ This file is part of KDE/aRts - xine integration
+ Copyright (C) 2003 Ewald Snel <ewald@rambo.its.tudelft.nl>
+
+ 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 __VIDEOSCALER_H
+#define __VIDEOSCALER_H
+
+void scaleYuvToRgb32( int width, int height,
+ unsigned char *base[3], unsigned int pitches[3],
+ int scaledWidth, int scaledHeight,
+ unsigned int *pixels, unsigned int bytesPerLine );
+
+void scaleYuy2ToRgb32( int width, int height,
+ unsigned char *base, unsigned int pitch,
+ int scaledWidth, int scaledHeight,
+ unsigned int *pixels, unsigned int bytesPerLine );
+
+#endif
diff --git a/xine_artsplugin/tools/thumbnail/videothumbnail.desktop b/xine_artsplugin/tools/thumbnail/videothumbnail.desktop
new file mode 100644
index 00000000..e437b2a3
--- /dev/null
+++ b/xine_artsplugin/tools/thumbnail/videothumbnail.desktop
@@ -0,0 +1,71 @@
+[Desktop Entry]
+Type=Service
+Name=Video Files
+Name[af]=Video Lêers
+Name[ar]=ملفات مرئيات
+Name[bg]=Видео файлове
+Name[bn]=ভিডিও ফাইল
+Name[br]=Restroù Video
+Name[bs]=Video datoteke
+Name[ca]=Fitxers de vídeo
+Name[cs]=Video soubory
+Name[cy]=Ffeiliau Fideo
+Name[da]=Videofiler
+Name[de]=Video-Dateien
+Name[el]=Αρχεία βίντεο
+Name[eo]=Vidaj dosieroj
+Name[es]=Archivos de vídeo
+Name[et]=Videofailid
+Name[eu]=Bideo fitxategiak
+Name[fa]=پرونده‌های ویدیویی
+Name[fi]=Videotiedostot
+Name[fr]=Fichiers vidéo
+Name[ga]=Comhaid Fhíse
+Name[gl]=Ficheiros de Video
+Name[he]=קבצי וידאו
+Name[hi]=वीडियो फ़ाइलें
+Name[hr]=Video datoteke
+Name[hu]=Videófájlok
+Name[is]=Kvikmyndaskrár
+Name[it]=File Video
+Name[ja]=ビデオファイル
+Name[kk]=Бейне файлдар
+Name[km]=ឯកសារ​វីដេអូ
+Name[ko]=비디오 파일
+Name[lt]=Video bylos
+Name[mk]=Видео датотеки
+Name[nb]=Videofiler
+Name[nds]=Videodateien
+Name[ne]=भिडियो फाइल
+Name[nl]=Videobestanden
+Name[nn]=Videofiler
+Name[pa]=ਵੀਡਿਓ ਫਾਇਲਾਂ
+Name[pl]=Pliki wideo
+Name[pt]=Ficheiros de Vídeo
+Name[pt_BR]=Arquivos de vídeo
+Name[ro]=Fişiere video
+Name[ru]=Видеофайлы
+Name[sk]=Video súbory
+Name[sl]=Video datoteke
+Name[sr]=Видео фајлови
+Name[sr@Latn]=Video fajlovi
+Name[sv]=Videofiler
+Name[ta]=படக்காட்சி கோப்புகள்
+Name[tg]=Файлҳои Видео
+Name[th]=แฟ้มวิดีโอ
+Name[tr]=Video Dosyaları
+Name[uk]=Відеофайли
+Name[uz]=Video fayllar
+Name[uz@cyrillic]=Видео файллар
+Name[ven]=Dzifaela dza Video
+Name[wa]=Fitchîs videyo
+Name[xh]=Iifayile ze Video
+Name[zh_CN]=视频文件
+Name[zh_HK]=視訊檔案
+Name[zh_TW]=視訊檔案
+Name[zu]=Amafayela Evidiyo
+ServiceTypes=ThumbCreator
+MimeTypes=video/*,application/vnd.ms-asf,application/vnd.rn-realmedia
+X-KDE-Library=videothumbnail
+CacheThumbnail=true
+IgnoreMaximumSize=true
diff --git a/xine_artsplugin/xineAudioPlayObject.mcopclass b/xine_artsplugin/xineAudioPlayObject.mcopclass
new file mode 100644
index 00000000..5a2f2225
--- /dev/null
+++ b/xine_artsplugin/xineAudioPlayObject.mcopclass
@@ -0,0 +1,7 @@
+Interface=xinePlayObject,Arts::PlayObject,Arts::SynthModule
+Author=Ewald Snel <ewald@rambo.its.tudelft.nl>
+Extension=flac,m4a,spx,ac3,aac,ogg,mp1,mp2,mp3,ra,wma,mpc,mp+
+MimeType=audio/vnd.rn-realaudio,audio/x-pn-realaudio,audio/x-flac,audio/x-oggflac,audio/x-speex,audio/mp4,audio/ac3,audio/aac,audio/vorbis,audio/x-mp3,audio/x-mp1,audio/x-mp2,audio/mpeg,audio/x-ms-wma,audio/x-musepack
+Language=C++
+Library=libarts_xine.la
+Preference=4
diff --git a/xine_artsplugin/xinePlayObject.idl b/xine_artsplugin/xinePlayObject.idl
new file mode 100644
index 00000000..7ba429d6
--- /dev/null
+++ b/xine_artsplugin/xinePlayObject.idl
@@ -0,0 +1,13 @@
+#include <kmedia2.idl>
+#include <soundserver.idl>
+
+interface xinePlayObject : Arts::PlayObject, Arts::SynthModule
+{
+ out audio stream left, right;
+};
+
+interface xineAudioPlayObject : xinePlayObject, Arts::PlayObject, Arts::SynthModule
+{};
+
+interface xineVideoPlayObject : xinePlayObject, Arts::PlayObject, Arts::VideoPlayObject, Arts::SynthModule
+{};
diff --git a/xine_artsplugin/xinePlayObject.mcopclass b/xine_artsplugin/xinePlayObject.mcopclass
new file mode 100644
index 00000000..21536a78
--- /dev/null
+++ b/xine_artsplugin/xinePlayObject.mcopclass
@@ -0,0 +1,7 @@
+Interface=xinePlayObject,Arts::PlayObject,Arts::VideoPlayObject,Arts::SynthModule,Arts::Object
+Author=Ewald Snel <ewald@rambo.its.tudelft.nl>
+Extension=vob,mpg,mpeg,m1v,m2v,m1s,m2s,m2p,avi,asf,asx,wmv,qt,mov,moov,mp4,rv,ra,ram,rm,smi,flac,m4v,m4a,spx,ac3,aac
+MimeType=video/x-mpg,video/x-dat,video/x-mpeg,video/mpeg,video/x-msvideo,video/x-ms-asf,video/x-ms-wmv,video/quicktime,video/x-theora,video/mp4,video/x-ogm,application/vnd.ms-asf,application/vnd.rn-realmedia,video/vnd.rn-realvideo,audio/vnd.rn-realaudio,audio/x-pn-realaudio,audio/x-flac,audio/x-speex,audio/mp4,audio/ac3,audio/aac,audio/vorbis,audio/x-mp3,audio/x-mp1,audio/x-mp2,audio/mpeg
+Language=C++
+Library=libarts_xine.la
+Preference=4
diff --git a/xine_artsplugin/xinePlayObject_impl.cpp b/xine_artsplugin/xinePlayObject_impl.cpp
new file mode 100644
index 00000000..21e3dff5
--- /dev/null
+++ b/xine_artsplugin/xinePlayObject_impl.cpp
@@ -0,0 +1,784 @@
+/*
+ This file is part of KDE/aRts (Noatun) - xine integration
+ Copyright (C) 2002-2003 Ewald Snel <ewald@rambo.its.tudelft.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <math.h>
+#include <sys/time.h>
+#include <audiosubsys.h>
+#include <convert.h>
+#include <debug.h>
+
+#include "xinePlayObject_impl.h"
+
+#ifndef HAVE_XSHMGETEVENTBASE
+extern "C" {
+extern int XShmGetEventBase( Display* );
+};
+#endif
+
+#define TIMEOUT 15 // 15 seconds
+
+using namespace Arts;
+
+
+// Global xine pointer
+static xine_t *xine_shared = NULL;
+static pthread_mutex_t xine_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t xine_cond = PTHREAD_COND_INITIALIZER;
+static int xineRefCount = 0;
+static bool xineForceXShm = false;
+
+static void xine_init_routine()
+{
+ const char *id;
+ char cfgFileName[272];
+
+ xine_shared = (xine_t *)xine_new();
+
+ snprintf( cfgFileName, 272, "%s/.xine/config", getenv( "HOME" ) );
+
+ xine_config_load( xine_shared, (const char *)cfgFileName );
+
+ // Check default video output driver
+ id = xine_config_register_string (xine_shared, "video.driver",
+ "auto", "video driver to use",
+ NULL, 10, NULL, NULL);
+
+ xineForceXShm = (id && !strcasecmp( id, "XShm" ));
+
+ xine_init( xine_shared );
+}
+
+static void *xine_timeout_routine( void * )
+{
+ pthread_mutex_lock( &xine_mutex );
+
+ while (xine_shared != 0)
+ {
+ if (xineRefCount == 0)
+ {
+ struct timespec ts;
+ struct timeval tv;
+
+ gettimeofday( &tv, 0 );
+
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+ ts.tv_sec += TIMEOUT;
+
+ if (pthread_cond_timedwait( &xine_cond, &xine_mutex, &ts ) != 0 &&
+ xineRefCount == 0)
+ {
+ xine_exit( xine_shared );
+ xine_shared = NULL;
+ break;
+ }
+ }
+ else
+ {
+ pthread_cond_wait( &xine_cond, &xine_mutex );
+ }
+ }
+ pthread_mutex_unlock( &xine_mutex );
+
+ return NULL;
+}
+
+static xine_t *xine_shared_init()
+{
+ pthread_mutex_lock( &xine_mutex );
+
+ ++xineRefCount;
+
+ if (xine_shared == 0)
+ {
+ pthread_t thread;
+
+ xine_init_routine();
+
+ if (pthread_create( &thread, NULL, xine_timeout_routine, NULL ) == 0)
+ {
+ pthread_detach( thread );
+ }
+ }
+ else
+ {
+ pthread_cond_signal( &xine_cond );
+ }
+ pthread_mutex_unlock( &xine_mutex );
+
+ return xine_shared;
+}
+
+static void xine_shared_exit( xine_t * )
+{
+ pthread_mutex_lock( &xine_mutex );
+
+ if (--xineRefCount == 0)
+ {
+ pthread_cond_signal( &xine_cond );
+ }
+ pthread_mutex_unlock( &xine_mutex );
+}
+
+int ao_fifo_arts_delay()
+{
+ return (int)(1000 * Arts::AudioSubSystem::the()->outputDelay());
+}
+
+xinePlayObject_impl::xinePlayObject_impl(bool audioOnly)
+ : mrl( "" ), xine( 0 ), stream( 0 ), queue( 0 ), ao_port( 0 ), vo_port( 0 ), audioOnly(audioOnly)
+{
+
+ if (!audioOnly)
+ {
+ XInitThreads();
+
+ if (!(display = XOpenDisplay( NULL )))
+ {
+ arts_fatal( "could not open X11 display" );
+ }
+
+ XFlush( display );
+
+ // Create a special window for uninterrupted X11 communication
+ xcomWindow = XCreateSimpleWindow( display, DefaultRootWindow( display ),
+ 0, 0, 1, 1, 0, 0, 0 );
+
+ XSelectInput( display, xcomWindow, ExposureMask );
+ }
+ pthread_mutex_init( &mutex, 0 );
+
+ if (!audioOnly)
+ {
+ // Initialize X11 properties
+ xcomAtomQuit = XInternAtom( display, "VPO_INTERNAL_EVENT", False );
+ xcomAtomResize = XInternAtom( display, "VPO_RESIZE_NOTIFY", False );
+ screen = DefaultScreen( display );
+ shmCompletionType = (XShmQueryExtension( display ) == True)
+ ? XShmGetEventBase( display ) + ShmCompletion : -1;
+
+ width = 0;
+ height = 0;
+ dscbTimeOut = 0;
+
+ // Initialize xine visual structure
+ visual.display = display;
+ visual.screen = screen;
+ visual.d = xcomWindow;
+ visual.dest_size_cb = &dest_size_cb;
+ visual.frame_output_cb = &frame_output_cb;
+ visual.user_data = this;
+ }
+
+ // Initialize audio and video details
+ Arts::SoundServerV2 server = Arts::Reference( "global:Arts_SoundServerV2" );
+ audio.sample_rate = 0;
+ audio.num_channels = 0;
+ audio.bits_per_sample = 0;
+
+ flpos = 0.0;
+ if (!audioOnly)
+ if (pthread_create( &thread, 0, pthread_start_routine, this ))
+ {
+ arts_fatal( "could not create thread" );
+ }
+}
+
+xinePlayObject_impl::~xinePlayObject_impl()
+{
+ XEvent event;
+
+ halt();
+
+ // Send stop event to thread (X11 client message)
+ memset( &event, 0, sizeof(event) );
+
+ event.type = ClientMessage;
+ event.xclient.window = xcomWindow;
+ event.xclient.message_type = xcomAtomQuit;
+ event.xclient.format = 32;
+
+ if (!audioOnly)
+ {
+
+ XSendEvent( display, xcomWindow, True, 0, &event );
+
+ XFlush( display );
+
+ // Wait for the thread to die
+ pthread_join( thread, 0 );
+
+ }
+
+ // Destroy stream, xine and related resources
+ if (stream != 0)
+ {
+ halt();
+
+ xine_event_dispose_queue( queue );
+ xine_dispose( stream );
+ xine_close_audio_driver( xine, ao_port );
+ xine_close_video_driver( xine, vo_port );
+ }
+ if (xine != 0)
+ {
+ xine_shared_exit( xine );
+ }
+
+ pthread_mutex_destroy( &mutex );
+
+ if (!audioOnly)
+ {
+ XSync( display, False );
+ XDestroyWindow( display, xcomWindow );
+ XCloseDisplay( display );
+ }
+}
+
+bool xinePlayObject_impl::loadMedia( const string &url )
+{
+ bool result = false;
+
+ pthread_mutex_lock( &mutex );
+
+ mrl = "";
+
+ if (stream == 0)
+ {
+ if (xine == 0)
+ {
+ xine = xine_shared_init();
+ }
+
+ ao_port = init_audio_out_plugin( xine, &audio, &ao_driver );
+
+ if (xineForceXShm && !audioOnly)
+ {
+ vo_port = xine_open_video_driver( xine, "XShm",
+ XINE_VISUAL_TYPE_X11,
+ (void *)&visual );
+ }
+ if (vo_port == 0 && !audioOnly)
+ {
+ vo_port = xine_open_video_driver( xine, "Xv",
+ XINE_VISUAL_TYPE_X11,
+ (void *)&visual );
+ }
+ if (vo_port == 0 && !audioOnly)
+ {
+ vo_port = xine_open_video_driver( xine, "XShm",
+ XINE_VISUAL_TYPE_X11,
+ (void *)&visual );
+ }
+ if (vo_port == 0 && !audioOnly)
+ {
+ vo_port = xine_open_video_driver( xine, "OpenGL",
+ XINE_VISUAL_TYPE_X11,
+ (void *)&visual );
+ }
+ if (vo_port == 0)
+ {
+ vo_port = xine_open_video_driver( xine, 0,
+ XINE_VISUAL_TYPE_NONE, 0 );
+ }
+
+ if (ao_port != 0 && vo_port != 0 )
+ {
+ stream = xine_stream_new( xine, ao_port, vo_port );
+
+ if (stream != 0)
+ {
+ xine_set_param( stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL, 0 );
+ xine_set_param( stream, XINE_PARAM_SPU_CHANNEL, -1 );
+
+ queue = xine_event_new_queue( stream );
+ xine_event_create_listener_thread( queue, xine_handle_event, this );
+ }
+ }
+ if (stream == 0)
+ {
+ if (ao_port != 0)
+ {
+ xine_close_audio_driver( xine, ao_port );
+ ao_port = 0;
+ }
+ if (vo_port != 0)
+ {
+ xine_close_video_driver( xine, vo_port );
+ vo_port = 0;
+ }
+ }
+ }
+
+ if (stream != 0)
+ {
+ if (xine_get_status( stream ) == XINE_STATUS_PLAY)
+ {
+ ao_fifo_clear( ao_driver, 2 );
+
+ xine_stop( stream );
+
+ clearWindow();
+ }
+ if ((result = xine_open( stream, url.c_str() )))
+ {
+ mrl = url;
+ }
+
+ streamLength = 0;
+ streamPosition = 0;
+
+ width = 0;
+ height = 0;
+ }
+
+ pthread_mutex_unlock( &mutex );
+
+ return result;
+}
+
+string xinePlayObject_impl::description()
+{
+ return "xine aRts plugin";
+}
+
+poTime xinePlayObject_impl::currentTime()
+{
+ poTime time;
+ int pos_time;
+
+ pthread_mutex_lock( &mutex );
+
+ if (stream != 0 && !mrl.empty())
+ {
+ if (xine_get_pos_length( stream, 0, &pos_time, 0 ))
+ {
+ streamPosition = pos_time;
+ }
+ else
+ {
+ pos_time = streamPosition;
+ }
+
+ time.seconds = pos_time / 1000;
+ time.ms = pos_time % 1000;
+ }
+ else
+ {
+ time.seconds = 0;
+ time.ms = 0;
+ }
+ pthread_mutex_unlock( &mutex );
+
+ return time;
+}
+
+poTime xinePlayObject_impl::overallTime()
+{
+ poTime time;
+ int length_time;
+
+ pthread_mutex_lock( &mutex );
+
+ if (stream != 0 && !mrl.empty())
+ {
+ if (xine_get_pos_length( stream, 0, 0, &length_time ))
+ {
+ streamLength = length_time;
+ }
+ else
+ {
+ length_time = streamLength;
+ }
+
+ if (length_time <= 0)
+ {
+ length_time = 1;
+ }
+
+ time.seconds = length_time / 1000;
+ time.ms = length_time % 1000;
+ }
+ else
+ {
+ time.seconds = 0;
+ time.ms = 1;
+ }
+ pthread_mutex_unlock( &mutex );
+
+ return time;
+}
+
+poCapabilities xinePlayObject_impl::capabilities()
+{
+ int n;
+
+ pthread_mutex_lock( &mutex );
+
+ n = (stream == 0) ? 0 : xine_get_stream_info( stream, XINE_STREAM_INFO_SEEKABLE );
+
+ pthread_mutex_unlock( &mutex );
+
+ return static_cast<poCapabilities>( capPause | ((n == 0) ? 0 : capSeek) );
+}
+
+string xinePlayObject_impl::mediaName()
+{
+ return mrl;
+}
+
+poState xinePlayObject_impl::state()
+{
+ poState state;
+
+ pthread_mutex_lock( &mutex );
+
+ if (stream == 0 || xine_get_status( stream ) != XINE_STATUS_PLAY)
+ state = posIdle;
+ else if (xine_get_param( stream, XINE_PARAM_SPEED ) == XINE_SPEED_PAUSE)
+ state = posPaused;
+ else
+ state = posPlaying;
+
+ pthread_mutex_unlock( &mutex );
+
+ return state;
+}
+
+void xinePlayObject_impl::play()
+{
+ pthread_mutex_lock( &mutex );
+
+ if (stream != 0)
+ {
+ if (xine_get_status( stream ) == XINE_STATUS_PLAY)
+ {
+ if (xine_get_param( stream, XINE_PARAM_SPEED ) == XINE_SPEED_PAUSE)
+ {
+ xine_set_param( stream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL );
+ }
+ }
+ else if (!mrl.empty())
+ {
+ xine_play( stream, 0, 0 );
+ }
+ }
+ pthread_mutex_unlock( &mutex );
+}
+
+void xinePlayObject_impl::halt()
+{
+ pthread_mutex_lock( &mutex );
+
+ if (stream != 0 && xine_get_status( stream ) == XINE_STATUS_PLAY)
+ {
+ ao_fifo_clear( ao_driver, 2 );
+
+ xine_stop( stream );
+
+ clearWindow();
+
+ streamLength = 0;
+ streamPosition = 0;
+ }
+ pthread_mutex_unlock( &mutex );
+}
+
+void xinePlayObject_impl::seek( const class poTime &t )
+{
+ pthread_mutex_lock( &mutex );
+
+ if (stream != 0 && xine_get_status( stream ) == XINE_STATUS_PLAY)
+ {
+ int seekPosition = (1000 * t.seconds) + t.ms;
+ int paused = (xine_get_param( stream, XINE_PARAM_SPEED ) == XINE_SPEED_PAUSE);
+
+ ao_fifo_clear( ao_driver, 1 );
+
+ if (xine_play( stream, 0, seekPosition ))
+ {
+ if (seekPosition >= 0 && seekPosition <= streamLength)
+ {
+ streamPosition = seekPosition;
+ }
+ }
+
+ if (paused)
+ {
+ xine_set_param( stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE );
+ }
+
+ ao_fifo_clear( ao_driver, 0 );
+ }
+ pthread_mutex_unlock( &mutex );
+}
+
+void xinePlayObject_impl::pause()
+{
+ pthread_mutex_lock( &mutex );
+
+ if (stream != 0 && xine_get_status( stream ) == XINE_STATUS_PLAY)
+ {
+ ao_fifo_clear( ao_driver, 1 );
+
+ xine_set_param( stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE );
+ }
+ pthread_mutex_unlock( &mutex );
+}
+
+void xinePlayObject_impl::calculateBlock( unsigned long samples )
+{
+ unsigned int skip, received = 0, converted = 0, xSamples = 0;
+ unsigned char *buffer;
+ double speed = 1.0;
+
+ pthread_mutex_lock( &mutex );
+
+ if (stream != 0)
+ {
+ // Calculate resampling parameters
+ speed = (double)audio.sample_rate / samplingRateFloat;
+ xSamples = (unsigned int)((double)samples * speed + 8.0);
+ received = ao_fifo_read( ao_driver, &buffer, xSamples );
+ }
+
+ pthread_mutex_unlock( &mutex );
+
+ // Convert samples and fill gaps with zeroes
+ if (received)
+ {
+ converted = uni_convert_stereo_2float( samples, buffer, received,
+ audio.num_channels,
+ audio.bits_per_sample,
+ left, right, speed, flpos );
+ flpos += (double)converted * speed;
+ skip = (int)floor( flpos );
+ skip = (received < (xSamples - 8)) ? (xSamples - 8) : skip;
+ flpos = flpos - floor( flpos );
+ ao_fifo_flush( ao_driver, skip );
+ }
+ for (unsigned long i=converted; i < samples; i++)
+ {
+ left[i] = 0;
+ right[i] = 0;
+ }
+}
+
+void xinePlayObject_impl::xineEvent( const xine_event_t &event )
+{
+ if (event.type == XINE_EVENT_UI_PLAYBACK_FINISHED)
+ {
+ clearWindow();
+ }
+}
+
+void xinePlayObject_impl::clearWindow()
+{
+ if (audioOnly) return;
+
+ Window root;
+ unsigned int u, w, h;
+ int x, y, screen;
+
+ XLockDisplay( display );
+
+ screen = DefaultScreen( display );
+
+ XGetGeometry( display, visual.d, &root, &x, &y, &w, &h, &u, &u );
+
+ XSetForeground( display, DefaultGC( display, screen ),
+ BlackPixel( display, screen ) );
+ XFillRectangle( display, visual.d,
+ DefaultGC( display, screen ), x, y, w, h );
+
+ XUnlockDisplay( display );
+}
+
+void xinePlayObject_impl::frameOutput( int &x, int &y,
+ int &width, int &height, double &ratio,
+ int displayWidth, int displayHeight,
+ double displayPixelAspect, bool dscb )
+{
+ if (audioOnly) return;
+
+ Window child, root;
+ unsigned int u;
+ int n;
+
+ XLockDisplay( display );
+
+ XGetGeometry( display, visual.d, &root, &n, &n,
+ (unsigned int *)&width, (unsigned int *)&height, &u, &u );
+
+ if (!dscb)
+ {
+ XTranslateCoordinates( display, visual.d, root, 0, 0, &x, &y, &child );
+ }
+
+ // Most displays use (nearly) square pixels
+ ratio = 1.0;
+
+ // Correct for display pixel aspect
+ if (displayPixelAspect < 1.0)
+ {
+ displayHeight = (int)((displayHeight / displayPixelAspect) + .5);
+ }
+ else
+ {
+ displayWidth = (int)((displayWidth * displayPixelAspect) + .5);
+ }
+
+ if (dscb || dscbTimeOut == 0 || --dscbTimeOut == 0)
+ {
+ // Notify client of new display size
+ if (displayWidth != this->width || displayHeight != this->height)
+ {
+ this->width = displayWidth;
+ this->height = displayHeight;
+
+ resizeNotify();
+ }
+
+ // Reset 'seen dest_size_cb' time out
+ if (dscb)
+ {
+ dscbTimeOut = 25;
+ }
+ }
+ XUnlockDisplay( display );
+}
+
+void xinePlayObject_impl::resizeNotify()
+{
+ if (audioOnly) return;
+ XEvent event;
+
+ // Resize notify signal for front-ends
+ memset( &event, 0, sizeof(event) );
+
+ event.type = ClientMessage;
+ event.xclient.window = visual.d;
+ event.xclient.message_type = xcomAtomResize;
+ event.xclient.format = 32;
+ event.xclient.data.l[0] = width;
+ event.xclient.data.l[1] = height;
+
+ XSendEvent( display, visual.d, True, 0, &event );
+
+ XFlush( display );
+}
+
+void xinePlayObject_impl::eventLoop()
+{
+ XEvent event;
+
+ do
+ {
+ XNextEvent( display, &event );
+
+ if (event.type == Expose && event.xexpose.count == 0 &&
+ event.xexpose.window == visual.d)
+ {
+ pthread_mutex_lock( &mutex );
+
+ if (stream != 0)
+ {
+ xine_gui_send_vo_data( stream,
+ XINE_GUI_SEND_EXPOSE_EVENT,
+ &event );
+ }
+ else
+ {
+ clearWindow();
+ }
+ pthread_mutex_unlock( &mutex );
+ }
+ else if (event.type == shmCompletionType)
+ {
+ pthread_mutex_lock( &mutex );
+
+ if (stream != 0)
+ {
+ xine_gui_send_vo_data( stream,
+ XINE_GUI_SEND_COMPLETION_EVENT,
+ &event );
+ }
+ pthread_mutex_unlock( &mutex );
+ }
+ }
+ while (event.type != ClientMessage ||
+ event.xclient.message_type != xcomAtomQuit ||
+ event.xclient.window != xcomWindow);
+}
+
+void xineVideoPlayObject_impl::x11WindowId( long window )
+{
+ pthread_mutex_lock( &mutex );
+
+ if (window == -1)
+ {
+ window = xcomWindow;
+ }
+
+ if ((Window)window != visual.d)
+ {
+ XLockDisplay( display );
+
+ // Change window and set event mask of new window
+ visual.d = window;
+
+ XSelectInput( display, window, ExposureMask );
+
+ if (stream != 0)
+ {
+ resizeNotify();
+
+ xine_gui_send_vo_data( stream,
+ XINE_GUI_SEND_DRAWABLE_CHANGED,
+ (void *)window );
+ }
+
+ XUnlockDisplay( display );
+ }
+ pthread_mutex_unlock( &mutex );
+}
+
+long xineVideoPlayObject_impl::x11WindowId()
+{
+ return (visual.d == xcomWindow) ? (long)-1 : visual.d;
+}
+
+long xineVideoPlayObject_impl::x11Snapshot()
+{
+ long pixmap = -1;
+
+ pthread_mutex_lock( &mutex );
+
+ if (stream != 0 && xine_get_status( stream ) == XINE_STATUS_PLAY)
+ {
+ // FIXME: snapshot...
+ pixmap = (long)-1;
+ }
+ pthread_mutex_unlock( &mutex );
+
+ return pixmap;
+}
+
+REGISTER_IMPLEMENTATION(xinePlayObject_impl);
+REGISTER_IMPLEMENTATION(xineAudioPlayObject_impl);
+REGISTER_IMPLEMENTATION(xineVideoPlayObject_impl);
diff --git a/xine_artsplugin/xinePlayObject_impl.h b/xine_artsplugin/xinePlayObject_impl.h
new file mode 100644
index 00000000..94482dac
--- /dev/null
+++ b/xine_artsplugin/xinePlayObject_impl.h
@@ -0,0 +1,158 @@
+/*
+ This file is part of KDE/aRts (Noatun) - xine integration
+ Copyright (C) 2002-2003 Ewald Snel <ewald@rambo.its.tudelft.nl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+*/
+
+#ifndef __XINEPLAYOBJECT_IMPL_H
+#define __XINEPLAYOBJECT_IMPL_H
+
+#include <string>
+#include <pthread.h>
+#include <stdsynthmodule.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/XShm.h>
+#include <xine.h>
+
+#include "audio_fifo_out.h"
+#include "xinePlayObject.h"
+
+
+using namespace std;
+using Arts::poState;
+using Arts::poTime;
+using Arts::poCapabilities;
+
+class xinePlayObject_impl : virtual public xinePlayObject_skel, public Arts::StdSynthModule
+{
+public:
+ xinePlayObject_impl(bool audioOnly=false);
+ virtual ~xinePlayObject_impl();
+
+ bool loadMedia( const string &url );
+ string description();
+ poTime currentTime();
+ poTime overallTime();
+ poCapabilities capabilities();
+ string mediaName();
+ poState state();
+ void play();
+ void halt();
+ void seek( const class poTime &t );
+ void pause();
+ void calculateBlock( unsigned long samples );
+
+protected:
+ void xineEvent( const xine_event_t &event );
+ void clearWindow();
+ void frameOutput( int &x, int &y,
+ int &width, int &height, double &ratio,
+ int displayWidth, int displayHeight,
+ double displayPixelAspect, bool dscb );
+ void resizeNotify();
+ void eventLoop();
+
+ // C -> C++ wrapper for pthread API
+ static inline void *pthread_start_routine( void *obj )
+ {
+ ((xinePlayObject_impl *)obj)->eventLoop();
+ pthread_exit( 0 );
+ }
+
+ // C -> C++ wrapper for xine API
+ static inline void xine_handle_event( void *obj, const xine_event_t *event )
+ {
+ ((xinePlayObject_impl *)obj)->xineEvent( *event );
+ }
+
+ // C -> C++ wrapper for xine API
+ static inline void frame_output_cb( void *obj,
+ int video_width, int video_height,
+ double video_pixel_aspect,
+ int *dest_x, int *dest_y,
+ int *dest_width, int *dest_height,
+ double *dest_pixel_aspect,
+ int *win_x, int *win_y )
+ {
+ ((xinePlayObject_impl *)obj)->frameOutput( *win_x, *win_y,
+ *dest_width, *dest_height,
+ *dest_pixel_aspect,
+ video_width, video_height,
+ video_pixel_aspect, false );
+
+ *dest_x = 0;
+ *dest_y = 0;
+ }
+
+ // C -> C++ wrapper for xine API
+ static inline void dest_size_cb( void *obj,
+ int video_width, int video_height,
+ double video_pixel_aspect,
+ int *dest_width, int *dest_height,
+ double *dest_pixel_aspect )
+ {
+ int win_x, win_y;
+
+ ((xinePlayObject_impl *)obj)->frameOutput( win_x, win_y,
+ *dest_width, *dest_height,
+ *dest_pixel_aspect,
+ video_width, video_height,
+ video_pixel_aspect, true );
+ }
+
+private:
+ double flpos;
+ string mrl;
+
+protected:
+ pthread_mutex_t mutex;
+ pthread_t thread;
+
+ // xine data
+ xine_t *xine;
+ xine_stream_t *stream;
+ xine_event_queue_t *queue;
+ xine_audio_port_t *ao_port;
+ xine_video_port_t *vo_port;
+ void *ao_driver;
+ x11_visual_t visual;
+ xine_arts_audio audio;
+
+ Display *display;
+ Window xcomWindow;
+ Atom xcomAtomQuit;
+ Atom xcomAtomResize;
+ int screen;
+ int width;
+ int height;
+ int dscbTimeOut;
+ int shmCompletionType;
+
+private:
+ int streamLength;
+ int streamPosition;
+ bool audioOnly;
+};
+
+class xineAudioPlayObject_impl : virtual public xineAudioPlayObject_skel, public xinePlayObject_impl
+{
+public:
+ xineAudioPlayObject_impl() : xinePlayObject_impl(true) {};
+};
+
+class xineVideoPlayObject_impl : virtual public xineVideoPlayObject_skel, public xinePlayObject_impl
+{
+public:
+ xineVideoPlayObject_impl() : xinePlayObject_impl(false) {};
+ long x11Snapshot();
+ long x11WindowId();
+ void x11WindowId( long window );
+};
+
+
+#endif
diff --git a/xine_artsplugin/xineVideoPlayObject.mcopclass b/xine_artsplugin/xineVideoPlayObject.mcopclass
new file mode 100644
index 00000000..5863fcf1
--- /dev/null
+++ b/xine_artsplugin/xineVideoPlayObject.mcopclass
@@ -0,0 +1,7 @@
+Interface=xinePlayObject,Arts::PlayObject,Arts::VideoPlayObject,Arts::SynthModule
+Author=Ewald Snel <ewald@rambo.its.tudelft.nl>
+Extension=vob,mpg,mpeg,m1v,m2v,m4v,m1s,m2s,m2p,avi,asf,asx,wmv,qt,mov,moov,mp4,rv,ram,rm,smi,ogm
+MimeType=video/x-mpg,video/x-dat,video/x-mpeg,video/mpeg,video/x-msvideo,video/x-ms-asf,video/x-ms-wmv,video/quicktime,video/x-theora,video/mp4,video/x-ogm,application/vnd.ms-asf,application/vnd.rn-realmedia,video/vnd.rn-realvideo,application/ogg
+Language=C++
+Library=libarts_xine.la
+Preference=4